aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore15
-rw-r--r--BUGS36
-rw-r--r--COPYING339
-rw-r--r--ChangeLog57
-rw-r--r--INSTALL209
-rw-r--r--INSTALL-cross335
-rw-r--r--Makeconf651
-rw-r--r--Makefile268
-rw-r--r--NEWS270
-rw-r--r--README52
-rw-r--r--TODO284
-rw-r--r--aclocal.m4241
-rw-r--r--auth/Makefile30
-rw-r--r--auth/auth.c533
-rw-r--r--auth/auth.h28
-rw-r--r--auth/mig-decls.h42
-rw-r--r--auth/mig-mutate.h24
-rw-r--r--benchmarks/Makefile25
-rw-r--r--benchmarks/forks.c64
-rw-r--r--boot/Makefile50
-rw-r--r--boot/boot.c1969
-rw-r--r--boot/boot_script.c791
-rw-r--r--boot/boot_script.h116
-rw-r--r--boot/frank1.ld94
-rw-r--r--boot/frankemul.ld107
-rw-r--r--boot/mach-crt0.c158
-rw-r--r--boot/sigvec.S23
-rw-r--r--boot/syscall.S35
-rw-r--r--boot/userland-boot.c108
-rw-r--r--boot/ux.c303
-rw-r--r--boot/ux.h114
-rw-r--r--build.mk.in9
-rw-r--r--build.mkcf.in3
-rwxr-xr-xconfig.guess1534
-rw-r--r--config.make.in104
-rwxr-xr-xconfig.sub1788
-rw-r--r--config/Makefile53
-rw-r--r--config/login-.bash_login2
-rw-r--r--config/login-.bashrc11
-rw-r--r--config/login-.hushlogin0
-rw-r--r--config/login-.profile3
-rw-r--r--config/login-README10
-rw-r--r--config/motd1
-rw-r--r--config/ttys14
-rw-r--r--configure.ac344
-rw-r--r--console-client/Makefile133
-rw-r--r--console-client/bdf.c990
-rw-r--r--console-client/bdf.h272
-rw-r--r--console-client/bell.h56
-rw-r--r--console-client/console.c757
-rw-r--r--console-client/current-vcs.c224
-rw-r--r--console-client/display.h154
-rw-r--r--console-client/driver.c361
-rw-r--r--console-client/driver.h290
-rw-r--r--console-client/generic-speaker.c552
-rw-r--r--console-client/input.h99
-rw-r--r--console-client/kbd-repeat.c262
-rw-r--r--console-client/mach-inputdev.h138
-rw-r--r--console-client/ncursesw.c753
-rw-r--r--console-client/pc-kbd.c1472
-rw-r--r--console-client/pc-mouse.c523
-rw-r--r--console-client/timer.c219
-rw-r--r--console-client/timer.h72
-rw-r--r--console-client/trans.c931
-rw-r--r--console-client/trans.h82
-rw-r--r--console-client/unicode.h349
-rw-r--r--console-client/vga-dynacolor.c322
-rw-r--r--console-client/vga-dynacolor.h108
-rw-r--r--console-client/vga-dynafont.c1083
-rw-r--r--console-client/vga-dynafont.h83
-rw-r--r--console-client/vga-hw.h144
-rw-r--r--console-client/vga-support.c479
-rw-r--r--console-client/vga-support.h89
-rw-r--r--console-client/vga.c850
-rw-r--r--console-client/xkb/MISSING-FEATURES32
-rw-r--r--console-client/xkb/README178
-rw-r--r--console-client/xkb/TODO8
-rw-r--r--console-client/xkb/compose.c592
-rw-r--r--console-client/xkb/kstoucs.c51
-rw-r--r--console-client/xkb/kstoucs_map.sh20
-rw-r--r--console-client/xkb/lex.l386
-rw-r--r--console-client/xkb/parser.y1605
-rw-r--r--console-client/xkb/xkb-data/keymap/hurd660
-rw-r--r--console-client/xkb/xkb-data/symbols/hurd125
-rw-r--r--console-client/xkb/xkb-data/types/hurd17
-rw-r--r--console-client/xkb/xkb.c1381
-rw-r--r--console-client/xkb/xkb.h431
-rw-r--r--console-client/xkb/xkbdata.c464
-rw-r--r--console-client/xkb/xkbtimer.c231
-rw-r--r--console/Makefile43
-rw-r--r--console/README.UTF8143
-rw-r--r--console/console.c2098
-rw-r--r--console/display.c2152
-rw-r--r--console/display.h84
-rw-r--r--console/hurd.ti162
-rw-r--r--console/input.c277
-rw-r--r--console/input.h58
-rw-r--r--console/motd.UTF84
-rw-r--r--console/mutations.h33
-rw-r--r--console/pager.c239
-rw-r--r--console/pager.h47
-rw-r--r--daemons/Makefile44
-rw-r--r--daemons/console-run.c234
-rw-r--r--daemons/getty.c213
-rw-r--r--daemons/lmail.c542
-rw-r--r--daemons/rc.sh123
-rw-r--r--daemons/runsystem.sh155
-rw-r--r--daemons/runttys.c502
-rw-r--r--defpager/Makefile33
-rw-r--r--defpager/backing.c130
-rw-r--r--defpager/defpager.c136
-rw-r--r--defpager/wiring.c111
-rw-r--r--doc/.gitignore3
-rw-r--r--doc/Makefile55
-rw-r--r--doc/gpl.texinfo395
-rw-r--r--doc/hurd.texi4634
-rw-r--r--doc/navigating53
-rw-r--r--exec/Makefile37
-rw-r--r--exec/elfcore.c566
-rw-r--r--exec/exec.c1523
-rw-r--r--exec/execmutations.h18
-rw-r--r--exec/hashexec.c456
-rw-r--r--exec/hostarch.c89
-rw-r--r--exec/main.c268
-rw-r--r--exec/mig-decls.h40
-rw-r--r--exec/priv.h155
-rw-r--r--ext2fs/Makefile31
-rw-r--r--ext2fs/balloc.c512
-rw-r--r--ext2fs/bitmap.c108
-rw-r--r--ext2fs/dir.c1092
-rw-r--r--ext2fs/ext2_fs.h651
-rw-r--r--ext2fs/ext2_fs_i.h42
-rw-r--r--ext2fs/ext2fs.c218
-rw-r--r--ext2fs/ext2fs.h574
-rw-r--r--ext2fs/getblk.c314
-rw-r--r--ext2fs/hyper.c224
-rw-r--r--ext2fs/ialloc.c429
-rw-r--r--ext2fs/inode.c849
-rw-r--r--ext2fs/msg.c89
-rw-r--r--ext2fs/pager.c1510
-rw-r--r--ext2fs/pokel.c197
-rw-r--r--ext2fs/sblock.words6
-rw-r--r--ext2fs/storeinfo.c131
-rw-r--r--ext2fs/truncate.c374
-rw-r--r--ext2fs/xinl.c2
-rw-r--r--fatfs/Makefile30
-rw-r--r--fatfs/dir.c1003
-rw-r--r--fatfs/fat.c761
-rw-r--r--fatfs/fat.h399
-rw-r--r--fatfs/fatfs.h127
-rw-r--r--fatfs/inode.c788
-rw-r--r--fatfs/main.c282
-rw-r--r--fatfs/node-create.c204
-rw-r--r--fatfs/pager.c1065
-rw-r--r--fatfs/virt-inode.c229
-rw-r--r--fatfs/virt-inode.h69
-rw-r--r--fstests/Makefile29
-rw-r--r--fstests/fdtests.c96
-rw-r--r--fstests/fstests.c95
-rw-r--r--fstests/opendisk.c122
-rw-r--r--fstests/timertest.c64
-rw-r--r--ftpfs/Makefile30
-rw-r--r--ftpfs/ccache.c293
-rw-r--r--ftpfs/ccache.h73
-rw-r--r--ftpfs/conn.c106
-rw-r--r--ftpfs/dir.c885
-rw-r--r--ftpfs/fs.c86
-rw-r--r--ftpfs/ftpfs.c430
-rw-r--r--ftpfs/ftpfs.h250
-rw-r--r--ftpfs/host.c154
-rw-r--r--ftpfs/ncache.c87
-rw-r--r--ftpfs/netfs.c463
-rw-r--r--ftpfs/node.c114
-rwxr-xr-xgitlog-to-changelog432
-rw-r--r--hostmux/Makefile30
-rw-r--r--hostmux/hostmux-xinl.c22
-rw-r--r--hostmux/hostmux.c174
-rw-r--r--hostmux/hostmux.h99
-rw-r--r--hostmux/leaf.c124
-rw-r--r--hostmux/mux.c462
-rw-r--r--hostmux/node.c127
-rw-r--r--hostmux/stubs.c145
-rw-r--r--hurd.boot13
-rw-r--r--hurd/=pending-changes24
-rw-r--r--hurd/Makefile87
-rw-r--r--hurd/auth.defs79
-rw-r--r--hurd/auth_reply.defs43
-rw-r--r--hurd/auth_request.defs36
-rw-r--r--hurd/console.h309
-rw-r--r--hurd/crash.defs45
-rw-r--r--hurd/crash_reply.defs30
-rw-r--r--hurd/default_pager.defs99
-rw-r--r--hurd/default_pager_reply.defs17
-rw-r--r--hurd/default_pager_types.h28
-rw-r--r--hurd/exec.defs57
-rw-r--r--hurd/exec_startup.defs50
-rw-r--r--hurd/fs.defs373
-rw-r--r--hurd/fs_notify.defs55
-rw-r--r--hurd/fsys.defs132
-rw-r--r--hurd/fsys_reply.defs88
-rw-r--r--hurd/gensym.awk77
-rw-r--r--hurd/hurd_types.defs268
-rw-r--r--hurd/hurd_types.h374
-rw-r--r--hurd/ifsock.defs34
-rw-r--r--hurd/iioctl.defs183
-rw-r--r--hurd/interrupt.defs36
-rw-r--r--hurd/io.defs333
-rw-r--r--hurd/io_reply.defs182
-rw-r--r--hurd/io_request.defs180
-rw-r--r--hurd/ioctl-decode.h15
-rw-r--r--hurd/ioctl-tmpl.sym13
-rw-r--r--hurd/ioctl.awk127
-rw-r--r--hurd/ioctl_types.h33
-rw-r--r--hurd/ioctls.defs48
-rw-r--r--hurd/kdioctl.defs39
-rw-r--r--hurd/kernel_boot.defs29
-rw-r--r--hurd/login.defs58
-rw-r--r--hurd/msg.defs202
-rw-r--r--hurd/msg_reply.defs55
-rw-r--r--hurd/msg_request.defs61
-rw-r--r--hurd/newterm.defs38
-rw-r--r--hurd/password.defs50
-rw-r--r--hurd/paths.h56
-rw-r--r--hurd/pfinet.defs39
-rw-r--r--hurd/process.defs402
-rw-r--r--hurd/process_reply.defs194
-rw-r--r--hurd/process_request.defs404
-rw-r--r--hurd/shared.h158
-rw-r--r--hurd/socket.defs136
-rw-r--r--hurd/startup.defs80
-rw-r--r--hurd/startup_notify.defs35
-rw-r--r--hurd/startup_reply.defs52
-rw-r--r--hurd/subsystems45
-rw-r--r--hurd/term.defs122
-rw-r--r--hurd/tioctl.defs258
-rw-r--r--hurd/version.h6
-rw-r--r--include/Makefile32
-rw-r--r--include/pids.h29
-rw-r--r--include/refcount.h263
-rw-r--r--include/sys/procfs.h120
-rw-r--r--init/Makefile31
-rw-r--r--init/init.c1593
-rw-r--r--init/stubs.c139
-rw-r--r--install-sh519
-rw-r--r--isofs/EXTENSIONS15
-rw-r--r--isofs/Makefile29
-rw-r--r--isofs/ext.c58
-rw-r--r--isofs/inode.c635
-rw-r--r--isofs/iso9660.h125
-rw-r--r--isofs/isofs.h94
-rw-r--r--isofs/lookup.c457
-rw-r--r--isofs/main.c170
-rw-r--r--isofs/pager.c363
-rw-r--r--isofs/rr.c649
-rw-r--r--isofs/rr.h266
-rw-r--r--libcons/Makefile38
-rw-r--r--libcons/cons-lookup.c107
-rw-r--r--libcons/cons-switch.c89
-rw-r--r--libcons/cons.h331
-rw-r--r--libcons/demuxer.c29
-rw-r--r--libcons/dir-changed.c129
-rw-r--r--libcons/extra-version.c24
-rw-r--r--libcons/file-changed.c367
-rw-r--r--libcons/init-init.c98
-rw-r--r--libcons/init-loop.c31
-rw-r--r--libcons/mutations.h26
-rw-r--r--libcons/opts-std-startup.c229
-rw-r--r--libcons/opts-version.c44
-rw-r--r--libcons/priv.h93
-rw-r--r--libcons/vcons-add.c30
-rw-r--r--libcons/vcons-close.c45
-rw-r--r--libcons/vcons-destroy.c52
-rw-r--r--libcons/vcons-event.c31
-rw-r--r--libcons/vcons-input.c65
-rw-r--r--libcons/vcons-move-mouse.c105
-rw-r--r--libcons/vcons-open.c176
-rw-r--r--libcons/vcons-refresh.c76
-rw-r--r--libcons/vcons-remove.c31
-rw-r--r--libcons/vcons-scrollback.c163
-rw-r--r--libdirmgt/Makefile23
-rw-r--r--libdirmgt/dirmgt.h51
-rw-r--r--libdiskfs/Makefile74
-rw-r--r--libdiskfs/boot-start.c650
-rw-r--r--libdiskfs/conch-fetch.c71
-rw-r--r--libdiskfs/conch-set.c49
-rw-r--r--libdiskfs/console.c71
-rw-r--r--libdiskfs/dead-name.c47
-rw-r--r--libdiskfs/demuxer.c49
-rw-r--r--libdiskfs/dir-chg.c82
-rw-r--r--libdiskfs/dir-clear.c70
-rw-r--r--libdiskfs/dir-init.c68
-rw-r--r--libdiskfs/dir-link.c133
-rw-r--r--libdiskfs/dir-lookup.c561
-rw-r--r--libdiskfs/dir-mkdir.c70
-rw-r--r--libdiskfs/dir-mkfile.c91
-rw-r--r--libdiskfs/dir-readdir.c61
-rw-r--r--libdiskfs/dir-rename.c239
-rw-r--r--libdiskfs/dir-renamed.c241
-rw-r--r--libdiskfs/dir-rmdir.c94
-rw-r--r--libdiskfs/dir-unlink.c98
-rw-r--r--libdiskfs/direnter.c48
-rw-r--r--libdiskfs/dirremove.c45
-rw-r--r--libdiskfs/dirrewrite.c50
-rw-r--r--libdiskfs/disk-pager.c131
-rw-r--r--libdiskfs/diskfs-pager.h73
-rw-r--r--libdiskfs/diskfs.h1076
-rw-r--r--libdiskfs/extern-inline.c20
-rw-r--r--libdiskfs/extra-version.c24
-rw-r--r--libdiskfs/fhandle.h36
-rw-r--r--libdiskfs/file-access.c44
-rw-r--r--libdiskfs/file-chauthor.c42
-rw-r--r--libdiskfs/file-chflags.c40
-rw-r--r--libdiskfs/file-chg.c71
-rw-r--r--libdiskfs/file-chmod.c57
-rw-r--r--libdiskfs/file-chown.c64
-rw-r--r--libdiskfs/file-exec.c212
-rw-r--r--libdiskfs/file-get-children.c95
-rw-r--r--libdiskfs/file-get-fs-opts.c54
-rw-r--r--libdiskfs/file-get-source.c37
-rw-r--r--libdiskfs/file-get-trans.c138
-rw-r--r--libdiskfs/file-get-transcntl.c48
-rw-r--r--libdiskfs/file-getcontrol.c51
-rw-r--r--libdiskfs/file-getfh.c60
-rw-r--r--libdiskfs/file-getlinknode.c39
-rw-r--r--libdiskfs/file-lock-stat.c38
-rw-r--r--libdiskfs/file-lock.c36
-rw-r--r--libdiskfs/file-reparent.c70
-rw-r--r--libdiskfs/file-set-size.c55
-rw-r--r--libdiskfs/file-set-trans.c215
-rw-r--r--libdiskfs/file-statfs.c50
-rw-r--r--libdiskfs/file-sync.c42
-rw-r--r--libdiskfs/file-syncfs.c61
-rw-r--r--libdiskfs/file-utimes.c57
-rw-r--r--libdiskfs/fsmutations.h39
-rw-r--r--libdiskfs/fsys-forward.c37
-rw-r--r--libdiskfs/fsys-getfile.c104
-rw-r--r--libdiskfs/fsys-getroot.c207
-rw-r--r--libdiskfs/fsys-goaway.c51
-rw-r--r--libdiskfs/fsys-options.c112
-rw-r--r--libdiskfs/fsys-startup.c36
-rw-r--r--libdiskfs/fsys-syncfs.c70
-rw-r--r--libdiskfs/get-source.c33
-rw-r--r--libdiskfs/ifsock.c131
-rw-r--r--libdiskfs/init-first.c65
-rw-r--r--libdiskfs/init-init.c111
-rw-r--r--libdiskfs/init-main.c78
-rw-r--r--libdiskfs/init-startup.c224
-rw-r--r--libdiskfs/io-async-icky.c33
-rw-r--r--libdiskfs/io-async.c30
-rw-r--r--libdiskfs/io-duplicate.c46
-rw-r--r--libdiskfs/io-get-conch.c44
-rw-r--r--libdiskfs/io-identity.c67
-rw-r--r--libdiskfs/io-map-cntl.c53
-rw-r--r--libdiskfs/io-map.c72
-rw-r--r--libdiskfs/io-modes-get.c33
-rw-r--r--libdiskfs/io-modes-off.c35
-rw-r--r--libdiskfs/io-modes-on.c35
-rw-r--r--libdiskfs/io-modes-set.c36
-rw-r--r--libdiskfs/io-owner-get.c36
-rw-r--r--libdiskfs/io-owner-mod.c36
-rw-r--r--libdiskfs/io-pathconf.c80
-rw-r--r--libdiskfs/io-prenotify.c72
-rw-r--r--libdiskfs/io-read.c109
-rw-r--r--libdiskfs/io-readable.c46
-rw-r--r--libdiskfs/io-reauthenticate.c66
-rw-r--r--libdiskfs/io-rel-conch.c44
-rw-r--r--libdiskfs/io-restrict-auth.c55
-rw-r--r--libdiskfs/io-revoke.c59
-rw-r--r--libdiskfs/io-seek.c61
-rw-r--r--libdiskfs/io-select.c40
-rw-r--r--libdiskfs/io-sigio.c36
-rw-r--r--libdiskfs/io-stat.c51
-rw-r--r--libdiskfs/io-stubs.c62
-rw-r--r--libdiskfs/io-version.c42
-rw-r--r--libdiskfs/io-write.c93
-rw-r--r--libdiskfs/lithp.h31
-rw-r--r--libdiskfs/lookup.c234
-rw-r--r--libdiskfs/name-cache.c376
-rw-r--r--libdiskfs/node-create.c163
-rw-r--r--libdiskfs/node-drop.c98
-rw-r--r--libdiskfs/node-make.c75
-rw-r--r--libdiskfs/node-nput.c81
-rw-r--r--libdiskfs/node-nputl.c38
-rw-r--r--libdiskfs/node-nref.c40
-rw-r--r--libdiskfs/node-nrefl.c30
-rw-r--r--libdiskfs/node-nrele.c65
-rw-r--r--libdiskfs/node-nrelel.c39
-rw-r--r--libdiskfs/node-rdwr.c77
-rw-r--r--libdiskfs/node-times.c74
-rw-r--r--libdiskfs/node-update.c32
-rw-r--r--libdiskfs/opts-append-std.c66
-rw-r--r--libdiskfs/opts-common.c59
-rw-r--r--libdiskfs/opts-get.c28
-rw-r--r--libdiskfs/opts-runtime.c24
-rw-r--r--libdiskfs/opts-set.c34
-rw-r--r--libdiskfs/opts-std-runtime.c161
-rw-r--r--libdiskfs/opts-std-startup.c178
-rw-r--r--libdiskfs/opts-version.c47
-rw-r--r--libdiskfs/peropen-make.c72
-rw-r--r--libdiskfs/peropen-rele.c49
-rw-r--r--libdiskfs/priv.h141
-rw-r--r--libdiskfs/protid-make.c68
-rw-r--r--libdiskfs/protid-rele.c35
-rw-r--r--libdiskfs/rdwr-internal.c74
-rw-r--r--libdiskfs/readonly-changed.c31
-rw-r--r--libdiskfs/readonly.c111
-rw-r--r--libdiskfs/remount.c47
-rw-r--r--libdiskfs/shutdown.c102
-rw-r--r--libdiskfs/sync-default.c23
-rw-r--r--libdiskfs/sync-interval.c136
-rw-r--r--libdiskfs/trans-callback.c91
-rw-r--r--libdiskfs/validate-author.c27
-rw-r--r--libdiskfs/validate-flags.c27
-rw-r--r--libdiskfs/validate-group.c27
-rw-r--r--libdiskfs/validate-mode.c27
-rw-r--r--libdiskfs/validate-owner.c27
-rw-r--r--libdiskfs/validate-rdev.c27
-rw-r--r--libfshelp/Makefile40
-rw-r--r--libfshelp/delegate.c64
-rw-r--r--libfshelp/drop-transbox.c28
-rw-r--r--libfshelp/exec-reauth.c152
-rw-r--r--libfshelp/fetch-control.c31
-rw-r--r--libfshelp/fetch-root.c195
-rw-r--r--libfshelp/fshelp.h308
-rw-r--r--libfshelp/get-identity.c92
-rw-r--r--libfshelp/lock-acquire.c131
-rw-r--r--libfshelp/lock-init.c32
-rw-r--r--libfshelp/locks.h28
-rw-r--r--libfshelp/perms-access.c43
-rw-r--r--libfshelp/perms-checkdirmod.c44
-rw-r--r--libfshelp/perms-iscontroller.c38
-rw-r--r--libfshelp/perms-isowner.c39
-rw-r--r--libfshelp/set-active.c62
-rw-r--r--libfshelp/set-options.c47
-rw-r--r--libfshelp/start-translator-long.c328
-rw-r--r--libfshelp/start-translator.c63
-rw-r--r--libfshelp/touch.c49
-rw-r--r--libfshelp/trans.h32
-rw-r--r--libfshelp/transbox-init.c34
-rw-r--r--libfshelp/translated.c28
-rw-r--r--libfshelp/translator-list.c202
-rw-r--r--libftpconn/Makefile33
-rw-r--r--libftpconn/addr.c79
-rw-r--r--libftpconn/cmd.c169
-rw-r--r--libftpconn/create.c87
-rw-r--r--libftpconn/cwd.c113
-rw-r--r--libftpconn/errs.c32
-rw-r--r--libftpconn/fname.c78
-rw-r--r--libftpconn/ftpconn.h394
-rw-r--r--libftpconn/names.c237
-rw-r--r--libftpconn/open.c252
-rw-r--r--libftpconn/priv.h98
-rw-r--r--libftpconn/reply.c241
-rw-r--r--libftpconn/rmt.c94
-rw-r--r--libftpconn/set-type.c60
-rw-r--r--libftpconn/stats.c80
-rw-r--r--libftpconn/unix.c772
-rw-r--r--libftpconn/xfer.c280
-rw-r--r--libftpconn/xinl.c24
-rw-r--r--libhurdbugaddr/Makefile28
-rw-r--r--libhurdbugaddr/bugaddr.c23
-rw-r--r--libihash/Makefile28
-rw-r--r--libihash/ihash.c346
-rw-r--r--libihash/ihash.h278
-rw-r--r--libiohelp/Makefile31
-rw-r--r--libiohelp/get_conch.c57
-rw-r--r--libiohelp/handle_io_get_conch.c50
-rw-r--r--libiohelp/handle_io_release_conch.c43
-rw-r--r--libiohelp/initialize_conch.c29
-rw-r--r--libiohelp/iohelp.h133
-rw-r--r--libiohelp/iouser-create.c112
-rw-r--r--libiohelp/iouser-dup.c58
-rw-r--r--libiohelp/iouser-free.c28
-rw-r--r--libiohelp/iouser-reauth.c107
-rw-r--r--libiohelp/iouser-restrict.c83
-rw-r--r--libiohelp/return-buffer.c55
-rw-r--r--libiohelp/shared.c35
-rw-r--r--libiohelp/verify_user_conch.c40
-rw-r--r--libnetfs/Makefile75
-rw-r--r--libnetfs/append-args.c31
-rw-r--r--libnetfs/append-std-options.c30
-rw-r--r--libnetfs/callbacks.h25
-rw-r--r--libnetfs/dead-name.c47
-rw-r--r--libnetfs/demuxer.c47
-rw-r--r--libnetfs/dir-link.c42
-rw-r--r--libnetfs/dir-lookup.c463
-rw-r--r--libnetfs/dir-mkdir.c39
-rw-r--r--libnetfs/dir-mkfile.c63
-rw-r--r--libnetfs/dir-notice-changes.c30
-rw-r--r--libnetfs/dir-readdir.c58
-rw-r--r--libnetfs/dir-rename.c42
-rw-r--r--libnetfs/dir-rmdir.c36
-rw-r--r--libnetfs/dir-unlink.c33
-rw-r--r--libnetfs/drop-node.c29
-rw-r--r--libnetfs/execserver.h23
-rw-r--r--libnetfs/file-chauthor.c37
-rw-r--r--libnetfs/file-check-access.c37
-rw-r--r--libnetfs/file-chflags.c37
-rw-r--r--libnetfs/file-chmod.c39
-rw-r--r--libnetfs/file-chown.c39
-rw-r--r--libnetfs/file-exec.c167
-rw-r--r--libnetfs/file-get-children.c109
-rw-r--r--libnetfs/file-get-fs-options.c62
-rw-r--r--libnetfs/file-get-source.c37
-rw-r--r--libnetfs/file-get-storage-info-default.c55
-rw-r--r--libnetfs/file-get-storage-info.c47
-rw-r--r--libnetfs/file-get-transcntl.c52
-rw-r--r--libnetfs/file-get-translator.c132
-rw-r--r--libnetfs/file-getcontrol.c51
-rw-r--r--libnetfs/file-getlinknode.c38
-rw-r--r--libnetfs/file-lock-stat.c37
-rw-r--r--libnetfs/file-lock.c36
-rw-r--r--libnetfs/file-reparent.c74
-rw-r--r--libnetfs/file-set-size.c39
-rw-r--r--libnetfs/file-set-translator.c186
-rw-r--r--libnetfs/file-statfs.c37
-rw-r--r--libnetfs/file-sync.c38
-rw-r--r--libnetfs/file-syncfs.c40
-rw-r--r--libnetfs/file-utimes.c53
-rw-r--r--libnetfs/fsstubs.c45
-rw-r--r--libnetfs/fsys-get-options.c65
-rw-r--r--libnetfs/fsys-getroot.c147
-rw-r--r--libnetfs/fsys-goaway.c48
-rw-r--r--libnetfs/fsys-set-options.c88
-rw-r--r--libnetfs/fsys-syncfs.c40
-rw-r--r--libnetfs/fsysstubs.c77
-rw-r--r--libnetfs/get-source.c28
-rw-r--r--libnetfs/init-init.c46
-rw-r--r--libnetfs/init-loop.c43
-rw-r--r--libnetfs/init-startup.c66
-rw-r--r--libnetfs/io-async.c30
-rw-r--r--libnetfs/io-clear-some-openmodes.c35
-rw-r--r--libnetfs/io-duplicate.c44
-rw-r--r--libnetfs/io-get-icky-async-id.c29
-rw-r--r--libnetfs/io-get-openmodes.c34
-rw-r--r--libnetfs/io-get-owner.c34
-rw-r--r--libnetfs/io-identity.c61
-rw-r--r--libnetfs/io-mod-owner.c34
-rw-r--r--libnetfs/io-pathconf.c70
-rw-r--r--libnetfs/io-read.c109
-rw-r--r--libnetfs/io-readable.c49
-rw-r--r--libnetfs/io-reauthenticate.c53
-rw-r--r--libnetfs/io-restrict-auth.c62
-rw-r--r--libnetfs/io-revoke.c60
-rw-r--r--libnetfs/io-seek.c67
-rw-r--r--libnetfs/io-select.c46
-rw-r--r--libnetfs/io-set-all-openmodes.c36
-rw-r--r--libnetfs/io-set-some-openmodes.c35
-rw-r--r--libnetfs/io-stat.c53
-rw-r--r--libnetfs/io-version.c40
-rw-r--r--libnetfs/io-write.c68
-rw-r--r--libnetfs/iostubs.c89
-rw-r--r--libnetfs/make-node.c59
-rw-r--r--libnetfs/make-peropen.c68
-rw-r--r--libnetfs/make-protid.c45
-rw-r--r--libnetfs/misc.h24
-rw-r--r--libnetfs/modes.h22
-rw-r--r--libnetfs/mutations.h37
-rw-r--r--libnetfs/netfs.h475
-rw-r--r--libnetfs/nput.c37
-rw-r--r--libnetfs/nref.c29
-rw-r--r--libnetfs/nrele.c37
-rw-r--r--libnetfs/priv.h54
-rw-r--r--libnetfs/release-peropen.c52
-rw-r--r--libnetfs/release-protid.c35
-rw-r--r--libnetfs/runtime-argp.c24
-rw-r--r--libnetfs/set-get-trans.c47
-rw-r--r--libnetfs/set-options.c30
-rw-r--r--libnetfs/shutdown.c106
-rw-r--r--libnetfs/startup-argp.c49
-rw-r--r--libnetfs/std-runtime-argp.c24
-rw-r--r--libnetfs/std-startup-argp.c27
-rw-r--r--libnetfs/trans-callback.c86
-rw-r--r--libpager/Makefile37
-rw-r--r--libpager/chg-compl.c53
-rw-r--r--libpager/clean.c50
-rw-r--r--libpager/data-request.c145
-rw-r--r--libpager/data-return.c272
-rw-r--r--libpager/data-unlock.c87
-rw-r--r--libpager/demuxer.c40
-rw-r--r--libpager/dropweak.c29
-rw-r--r--libpager/get-upi.c27
-rw-r--r--libpager/inhibit-term.c35
-rw-r--r--libpager/lock-completed.c66
-rw-r--r--libpager/lock-object.c107
-rw-r--r--libpager/mark-error.c122
-rw-r--r--libpager/mig-decls.h42
-rw-r--r--libpager/mig-mutate.h29
-rw-r--r--libpager/no-senders.c36
-rw-r--r--libpager/notify-stubs.c76
-rw-r--r--libpager/object-init.c76
-rw-r--r--libpager/object-terminate.c130
-rw-r--r--libpager/offer-page.c51
-rw-r--r--libpager/pagemap.c47
-rw-r--r--libpager/pager-attr.c101
-rw-r--r--libpager/pager-create.c64
-rw-r--r--libpager/pager-flush.c44
-rw-r--r--libpager/pager-memcpy.c218
-rw-r--r--libpager/pager-port.c26
-rw-r--r--libpager/pager-return.c43
-rw-r--r--libpager/pager-shutdown.c32
-rw-r--r--libpager/pager-sync.c44
-rw-r--r--libpager/pager.h210
-rw-r--r--libpager/priv.h153
-rw-r--r--libpager/seqnos.c79
-rw-r--r--libpager/stubs.c67
-rw-r--r--libpipe/Makefile31
-rw-r--r--libpipe/addr.c29
-rw-r--r--libpipe/dgram.c59
-rw-r--r--libpipe/pipe-funcs.c2
-rw-r--r--libpipe/pipe.c486
-rw-r--r--libpipe/pipe.h444
-rw-r--r--libpipe/pq-funcs.c2
-rw-r--r--libpipe/pq.c462
-rw-r--r--libpipe/pq.h306
-rw-r--r--libpipe/seqpack.c64
-rw-r--r--libpipe/stream.c77
-rw-r--r--libports/Makefile50
-rw-r--r--libports/begin-rpc.c108
-rw-r--r--libports/bucket-iterate.c93
-rw-r--r--libports/claim-right.c52
-rw-r--r--libports/class-iterate.c35
-rw-r--r--libports/complete-deallocate.c51
-rw-r--r--libports/count-bucket.c34
-rw-r--r--libports/count-class.c33
-rw-r--r--libports/create-bucket.c58
-rw-r--r--libports/create-class.c47
-rw-r--r--libports/create-internal.c120
-rw-r--r--libports/create-port-noinstall.c32
-rw-r--r--libports/create-port.c32
-rw-r--r--libports/dead-name.c28
-rw-r--r--libports/default-uninhibitable-rpcs.c27
-rw-r--r--libports/destroy-right.c50
-rw-r--r--libports/enable-bucket.c34
-rw-r--r--libports/enable-class.c34
-rw-r--r--libports/end-rpc.c55
-rw-r--r--libports/get-right.c58
-rw-r--r--libports/get-send-right.c38
-rw-r--r--libports/import-port.c115
-rw-r--r--libports/inhibit-all-rpcs.c77
-rw-r--r--libports/inhibit-bucket-rpcs.c73
-rw-r--r--libports/inhibit-class-rpcs.c68
-rw-r--r--libports/inhibit-port-rpcs.c69
-rw-r--r--libports/init.c27
-rw-r--r--libports/interrupt-notified-rpcs.c116
-rw-r--r--libports/interrupt-on-notify.c184
-rw-r--r--libports/interrupt-operation.c38
-rw-r--r--libports/interrupt-rpcs.c39
-rw-r--r--libports/interrupted.c75
-rw-r--r--libports/lookup-port.c52
-rw-r--r--libports/manage-multithread.c246
-rw-r--r--libports/manage-one-thread.c100
-rw-r--r--libports/mig-decls.h40
-rw-r--r--libports/mig-mutate.h32
-rw-r--r--libports/no-senders.c65
-rw-r--r--libports/notify-dead-name.c36
-rw-r--r--libports/notify-msg-accepted.c29
-rw-r--r--libports/notify-no-senders.c32
-rw-r--r--libports/notify-port-deleted.c29
-rw-r--r--libports/notify-port-destroyed.c29
-rw-r--r--libports/notify-send-once.c28
-rw-r--r--libports/port-deref-weak.c36
-rw-r--r--libports/port-deref.c50
-rw-r--r--libports/port-ref-weak.c33
-rw-r--r--libports/port-ref.c33
-rw-r--r--libports/ports.h415
-rw-r--r--libports/reallocate-from-external.c83
-rw-r--r--libports/reallocate-port.c61
-rw-r--r--libports/resume-all-rpcs.c36
-rw-r--r--libports/resume-bucket-rpcs.c36
-rw-r--r--libports/resume-class-rpcs.c36
-rw-r--r--libports/resume-port-rpcs.c39
-rw-r--r--libports/stubs.c40
-rw-r--r--libports/transfer-right.c98
-rw-r--r--libps/Makefile40
-rw-r--r--libps/common.h44
-rw-r--r--libps/context.c151
-rw-r--r--libps/filters.c91
-rw-r--r--libps/fmt.c575
-rw-r--r--libps/host.c114
-rw-r--r--libps/proclist.c695
-rw-r--r--libps/procstat.c1147
-rw-r--r--libps/ps.h1056
-rw-r--r--libps/pshost.h53
-rw-r--r--libps/spec.c1171
-rw-r--r--libps/tty.c155
-rw-r--r--libps/user.c163
-rw-r--r--libps/write.c264
-rw-r--r--libshouldbeinlibc/Makefile37
-rw-r--r--libshouldbeinlibc/cacheq.c143
-rw-r--r--libshouldbeinlibc/cacheq.h87
-rw-r--r--libshouldbeinlibc/canon-host.c57
-rw-r--r--libshouldbeinlibc/exec-reauth.c108
-rw-r--r--libshouldbeinlibc/fsysops.c96
-rw-r--r--libshouldbeinlibc/idvec-auth.c85
-rw-r--r--libshouldbeinlibc/idvec-funcs.c2
-rw-r--r--libshouldbeinlibc/idvec-impgids.c115
-rw-r--r--libshouldbeinlibc/idvec-rep.c164
-rw-r--r--libshouldbeinlibc/idvec-verify.c362
-rw-r--r--libshouldbeinlibc/idvec.c339
-rw-r--r--libshouldbeinlibc/idvec.h236
-rw-r--r--libshouldbeinlibc/lcm.c46
-rw-r--r--libshouldbeinlibc/localhost.c75
-rw-r--r--libshouldbeinlibc/maptime-funcs.c5
-rw-r--r--libshouldbeinlibc/maptime.c84
-rw-r--r--libshouldbeinlibc/maptime.h61
-rw-r--r--libshouldbeinlibc/nullauth.c45
-rw-r--r--libshouldbeinlibc/nullauth.h31
-rw-r--r--libshouldbeinlibc/portinfo.c158
-rw-r--r--libshouldbeinlibc/portinfo.h58
-rw-r--r--libshouldbeinlibc/portxlate.c179
-rw-r--r--libshouldbeinlibc/portxlate.h67
-rw-r--r--libshouldbeinlibc/shared-dom.c54
-rw-r--r--libshouldbeinlibc/termsize.c54
-rw-r--r--libshouldbeinlibc/timefmt.c359
-rw-r--r--libshouldbeinlibc/timefmt.h58
-rw-r--r--libshouldbeinlibc/ugids-argp.c174
-rw-r--r--libshouldbeinlibc/ugids-auth.c53
-rw-r--r--libshouldbeinlibc/ugids-imply.c36
-rw-r--r--libshouldbeinlibc/ugids-merge.c110
-rw-r--r--libshouldbeinlibc/ugids-posix.c95
-rw-r--r--libshouldbeinlibc/ugids-rep.c118
-rw-r--r--libshouldbeinlibc/ugids-subtract.c130
-rw-r--r--libshouldbeinlibc/ugids-verify-auth.c185
-rw-r--r--libshouldbeinlibc/ugids-verify.c65
-rw-r--r--libshouldbeinlibc/ugids-xinl.c23
-rw-r--r--libshouldbeinlibc/ugids.c98
-rw-r--r--libshouldbeinlibc/ugids.h231
-rw-r--r--libshouldbeinlibc/wire.c186
-rw-r--r--libshouldbeinlibc/wire.h26
-rw-r--r--libshouldbeinlibc/xportinfo.c69
-rw-r--r--libstore/Makefile89
-rw-r--r--libstore/argp.c394
-rw-r--r--libstore/bunzip2.c13
-rw-r--r--libstore/clone.c91
-rw-r--r--libstore/copy.c267
-rw-r--r--libstore/create.c79
-rw-r--r--libstore/crypt.h12
-rw-r--r--libstore/decode.c203
-rw-r--r--libstore/derive.c87
-rw-r--r--libstore/device.c342
-rw-r--r--libstore/do-bunzip2.c87
-rw-r--r--libstore/do-gunzip.c80
-rw-r--r--libstore/enc.c98
-rw-r--r--libstore/encode.c178
-rw-r--r--libstore/file.c303
-rw-r--r--libstore/flags.c66
-rw-r--r--libstore/gunzip.c33
-rw-r--r--libstore/kids.c312
-rw-r--r--libstore/make.c100
-rw-r--r--libstore/map.c79
-rw-r--r--libstore/memobj.c197
-rw-r--r--libstore/module.c178
-rw-r--r--libstore/mvol.c158
-rw-r--r--libstore/nbd.c529
-rw-r--r--libstore/open.c65
-rw-r--r--libstore/part.c209
-rw-r--r--libstore/rdwr.c298
-rw-r--r--libstore/remap.c345
-rw-r--r--libstore/set.c78
-rw-r--r--libstore/std.c44
-rw-r--r--libstore/store.h800
-rw-r--r--libstore/stripe.c293
-rw-r--r--libstore/task.c205
-rw-r--r--libstore/typed.c177
-rw-r--r--libstore/unknown.c231
-rw-r--r--libstore/unzipstore.c267
-rw-r--r--libstore/url.c92
-rw-r--r--libstore/util.c19
-rw-r--r--libstore/xinl.c2
-rw-r--r--libstore/zero.c196
-rw-r--r--libthreads/GNUmakefile.old38
-rw-r--r--libthreads/Makefile48
-rw-r--r--libthreads/Makefile.CMU93
-rw-r--r--libthreads/Makefile.GNU33
-rw-r--r--libthreads/Makefile.GNU233
-rw-r--r--libthreads/alpha/csw.S200
-rw-r--r--libthreads/alpha/cthreads.h61
-rw-r--r--libthreads/alpha/lock.S83
-rw-r--r--libthreads/alpha/thread.c100
-rw-r--r--libthreads/call.c79
-rw-r--r--libthreads/cancel-cond.c116
-rw-r--r--libthreads/cprocs.c1214
-rw-r--r--libthreads/cthread_data.c195
-rw-r--r--libthreads/cthread_internals.h324
-rw-r--r--libthreads/cthreads.c488
-rw-r--r--libthreads/cthreads.h710
-rw-r--r--libthreads/i386/csw.S194
-rw-r--r--libthreads/i386/cthread_inline.awk86
-rw-r--r--libthreads/i386/cthreads.h117
-rw-r--r--libthreads/i386/lock.s74
-rw-r--r--libthreads/i386/thread.c135
-rw-r--r--libthreads/libthreads.map27
-rw-r--r--libthreads/lockfile.c65
-rw-r--r--libthreads/mig_support.c234
-rw-r--r--libthreads/options.h104
-rw-r--r--libthreads/rwlock.c2
-rw-r--r--libthreads/rwlock.h128
-rw-r--r--libthreads/stack.c424
-rw-r--r--libthreads/sync.c86
-rw-r--r--libtreefs/Makefile38
-rw-r--r--libtreefs/defhooks.c80
-rw-r--r--libtreefs/dir-hooks.c136
-rw-r--r--libtreefs/dir-lookup.c306
-rw-r--r--libtreefs/fs-mutate.h30
-rw-r--r--libtreefs/fsys-getroot.c144
-rw-r--r--libtreefs/fsys-hooks.c91
-rw-r--r--libtreefs/fsys-startup.c36
-rw-r--r--libtreefs/fsys.c127
-rw-r--r--libtreefs/hooks.c59
-rw-r--r--libtreefs/mdir.c92
-rw-r--r--libtreefs/mig-decls.h46
-rw-r--r--libtreefs/nlist.c70
-rw-r--r--libtreefs/node-hooks.c176
-rw-r--r--libtreefs/rights.c96
-rw-r--r--libtreefs/s-dir.c112
-rw-r--r--libtreefs/s-file.c235
-rw-r--r--libtreefs/s-fsys.c76
-rw-r--r--libtreefs/s-io.c284
-rw-r--r--libtreefs/trans-help.c129
-rw-r--r--libtreefs/trans-start.c66
-rw-r--r--libtreefs/treefs-hooks.h401
-rw-r--r--libtreefs/treefs-s-hooks.h231
-rw-r--r--libtreefs/treefs.h493
-rw-r--r--libtreefs/xinl.c3
-rw-r--r--libtrivfs/Makefile62
-rw-r--r--libtrivfs/append-args.c30
-rw-r--r--libtrivfs/cntl-classes.c22
-rw-r--r--libtrivfs/cntl-clean.c35
-rw-r--r--libtrivfs/cntl-create.c100
-rw-r--r--libtrivfs/demuxer.c46
-rw-r--r--libtrivfs/dir-chg.c26
-rw-r--r--libtrivfs/dir-link.c30
-rw-r--r--libtrivfs/dir-lookup.c89
-rw-r--r--libtrivfs/dir-mkdir.c26
-rw-r--r--libtrivfs/dir-mkfile.c29
-rw-r--r--libtrivfs/dir-readdir.c32
-rw-r--r--libtrivfs/dir-rename.c31
-rw-r--r--libtrivfs/dir-rmdir.c26
-rw-r--r--libtrivfs/dir-unlink.c26
-rw-r--r--libtrivfs/dyn-classes.c270
-rw-r--r--libtrivfs/file-access.c36
-rw-r--r--libtrivfs/file-chauthor.c27
-rw-r--r--libtrivfs/file-chflags.c27
-rw-r--r--libtrivfs/file-chg.c27
-rw-r--r--libtrivfs/file-chmod.c28
-rw-r--r--libtrivfs/file-chown.c27
-rw-r--r--libtrivfs/file-exec.c43
-rw-r--r--libtrivfs/file-get-children.c36
-rw-r--r--libtrivfs/file-get-fs-options.c52
-rw-r--r--libtrivfs/file-get-source.c34
-rw-r--r--libtrivfs/file-get-storage-info.c34
-rw-r--r--libtrivfs/file-get-trans.c28
-rw-r--r--libtrivfs/file-get-transcntl.c28
-rw-r--r--libtrivfs/file-getcontrol.c34
-rw-r--r--libtrivfs/file-getfh.c27
-rw-r--r--libtrivfs/file-getlinknode.c32
-rw-r--r--libtrivfs/file-lock.c35
-rw-r--r--libtrivfs/file-reparent.c36
-rw-r--r--libtrivfs/file-set-size.c29
-rw-r--r--libtrivfs/file-set-trans.c33
-rw-r--r--libtrivfs/file-statfs.c36
-rw-r--r--libtrivfs/file-sync.c27
-rw-r--r--libtrivfs/file-syncfs.c28
-rw-r--r--libtrivfs/file-utimes.c27
-rw-r--r--libtrivfs/fsys-forward.c39
-rw-r--r--libtrivfs/fsys-get-options.c51
-rw-r--r--libtrivfs/fsys-getroot.c120
-rw-r--r--libtrivfs/fsys-goaway.c35
-rw-r--r--libtrivfs/fsys-set-options.c36
-rw-r--r--libtrivfs/fsys-stubs.c72
-rw-r--r--libtrivfs/fsys-syncfs.c32
-rw-r--r--libtrivfs/get-source.c28
-rw-r--r--libtrivfs/io-async-icky.c34
-rw-r--r--libtrivfs/io-async.c35
-rw-r--r--libtrivfs/io-duplicate.c48
-rw-r--r--libtrivfs/io-identity.c51
-rw-r--r--libtrivfs/io-map.c36
-rw-r--r--libtrivfs/io-modes-get.c39
-rw-r--r--libtrivfs/io-modes-off.c34
-rw-r--r--libtrivfs/io-modes-on.c34
-rw-r--r--libtrivfs/io-modes-set.c34
-rw-r--r--libtrivfs/io-owner-get.c33
-rw-r--r--libtrivfs/io-owner-mod.c33
-rw-r--r--libtrivfs/io-pathconf.c30
-rw-r--r--libtrivfs/io-read.c33
-rw-r--r--libtrivfs/io-readable.c30
-rw-r--r--libtrivfs/io-reauthenticate.c94
-rw-r--r--libtrivfs/io-restrict-auth.c144
-rw-r--r--libtrivfs/io-revoke.c33
-rw-r--r--libtrivfs/io-seek.c32
-rw-r--r--libtrivfs/io-select.c49
-rw-r--r--libtrivfs/io-stat.c51
-rw-r--r--libtrivfs/io-stubs.c101
-rw-r--r--libtrivfs/io-version.c31
-rw-r--r--libtrivfs/io-write.c37
-rw-r--r--libtrivfs/mig-decls.h105
-rw-r--r--libtrivfs/mig-mutate.h32
-rw-r--r--libtrivfs/open.c87
-rw-r--r--libtrivfs/priv.h26
-rw-r--r--libtrivfs/protid-classes.c22
-rw-r--r--libtrivfs/protid-clean.c59
-rw-r--r--libtrivfs/protid-dup.c69
-rw-r--r--libtrivfs/runtime-argp.c23
-rw-r--r--libtrivfs/set-options.c33
-rw-r--r--libtrivfs/startup.c88
-rw-r--r--libtrivfs/times.c48
-rw-r--r--libtrivfs/trivfs.h265
-rw-r--r--login/Makefile26
-rw-r--r--login/utmp.c424
-rw-r--r--mach-defpager/Makefile38
-rw-r--r--mach-defpager/default_pager.c3774
-rw-r--r--mach-defpager/default_pager.h43
-rw-r--r--mach-defpager/file_io.h69
-rw-r--r--mach-defpager/kalloc.c297
-rw-r--r--mach-defpager/kalloc.h30
-rw-r--r--mach-defpager/main.c185
-rw-r--r--mach-defpager/mig-decls.h34
-rw-r--r--mach-defpager/mig-mutate.h22
-rw-r--r--mach-defpager/priv.h200
-rw-r--r--mach-defpager/queue.h316
-rw-r--r--mach-defpager/setup.c307
-rw-r--r--mach-defpager/wiring.c176
-rw-r--r--mach-defpager/wiring.h35
-rwxr-xr-xmkinstalldirs40
-rw-r--r--mount/mount.defs109
-rw-r--r--mount/mount.h72
-rw-r--r--mount/mount_types.h61
-rwxr-xr-xmove-if-change17
-rw-r--r--nfs/Makefile32
-rw-r--r--nfs/cache.c211
-rw-r--r--nfs/consts.c25
-rw-r--r--nfs/main.c438
-rw-r--r--nfs/mount.c277
-rw-r--r--nfs/mount.h40
-rw-r--r--nfs/name-cache.c305
-rw-r--r--nfs/nfs-spec.h168
-rw-r--r--nfs/nfs.c681
-rw-r--r--nfs/nfs.h204
-rw-r--r--nfs/ops.c1925
-rw-r--r--nfs/rpc.c425
-rw-r--r--nfs/storage-info.c104
-rw-r--r--nfsd/Makefile32
-rw-r--r--nfsd/cache.c575
-rw-r--r--nfsd/fsys.c209
-rw-r--r--nfsd/loop.c224
-rw-r--r--nfsd/main.c105
-rw-r--r--nfsd/nfsd.h129
-rw-r--r--nfsd/ops.c755
-rw-r--r--nfsd/xdr.c218
-rw-r--r--pfinet/Makefile148
-rw-r--r--pfinet/README55
-rw-r--r--pfinet/config.h40
-rw-r--r--pfinet/dummy.c134
-rw-r--r--pfinet/ethernet.c383
-rw-r--r--pfinet/glue-include/asm/atomic.h27
-rw-r--r--pfinet/glue-include/asm/bitops.h37
-rw-r--r--pfinet/glue-include/asm/byteorder.h155
-rw-r--r--pfinet/glue-include/asm/delay.h1
-rw-r--r--pfinet/glue-include/asm/errno.h3
-rw-r--r--pfinet/glue-include/asm/hardirq.h1
-rw-r--r--pfinet/glue-include/asm/init.h3
-rw-r--r--pfinet/glue-include/asm/segment.h0
-rw-r--r--pfinet/glue-include/asm/spinlock.h73
-rw-r--r--pfinet/glue-include/asm/system.h20
-rw-r--r--pfinet/glue-include/asm/types.h1
-rw-r--r--pfinet/glue-include/asm/uaccess.h53
-rw-r--r--pfinet/glue-include/linux/autoconf.h0
-rw-r--r--pfinet/glue-include/linux/binfmts.h1
-rw-r--r--pfinet/glue-include/linux/config.h1
-rw-r--r--pfinet/glue-include/linux/errno.h13
-rw-r--r--pfinet/glue-include/linux/fcntl.h1
-rw-r--r--pfinet/glue-include/linux/fs.h21
-rw-r--r--pfinet/glue-include/linux/if.h3
-rw-r--r--pfinet/glue-include/linux/in.h44
-rw-r--r--pfinet/glue-include/linux/in6.h110
-rw-r--r--pfinet/glue-include/linux/interrupt.h46
-rw-r--r--pfinet/glue-include/linux/ioctl.h1
-rw-r--r--pfinet/glue-include/linux/ipv6.h126
-rw-r--r--pfinet/glue-include/linux/kernel.h78
-rw-r--r--pfinet/glue-include/linux/limits.h8
-rw-r--r--pfinet/glue-include/linux/major.h0
-rw-r--r--pfinet/glue-include/linux/malloc.h27
-rw-r--r--pfinet/glue-include/linux/mm.h38
-rw-r--r--pfinet/glue-include/linux/param.h1
-rw-r--r--pfinet/glue-include/linux/personality.h1
-rw-r--r--pfinet/glue-include/linux/poll.h24
-rw-r--r--pfinet/glue-include/linux/proc_fs.h0
-rw-r--r--pfinet/glue-include/linux/sched.h207
-rw-r--r--pfinet/glue-include/linux/slab.h0
-rw-r--r--pfinet/glue-include/linux/socket.h158
-rw-r--r--pfinet/glue-include/linux/sockios.h0
-rw-r--r--pfinet/glue-include/linux/stat.h1
-rw-r--r--pfinet/glue-include/linux/string.h1
-rw-r--r--pfinet/glue-include/linux/termios.h1
-rw-r--r--pfinet/glue-include/linux/time.h10
-rw-r--r--pfinet/glue-include/linux/timer.h36
-rw-r--r--pfinet/glue-include/linux/timex.h0
-rw-r--r--pfinet/glue-include/linux/types.h31
-rw-r--r--pfinet/glue-include/linux/un.h0
-rw-r--r--pfinet/glue-include/linux/version.h3
-rw-r--r--pfinet/glue-include/linux/wait.h32
-rw-r--r--pfinet/iioctl-ops.c388
-rw-r--r--pfinet/io-ops.c648
-rw-r--r--pfinet/kmem_cache.c88
-rw-r--r--pfinet/linux-src/arch/alpha/lib/checksum.c169
-rw-r--r--pfinet/linux-src/arch/alpha/lib/csum_partial_copy.c384
-rw-r--r--pfinet/linux-src/arch/arm/lib/checksum.S730
-rw-r--r--pfinet/linux-src/arch/i386/lib/checksum.S447
-rw-r--r--pfinet/linux-src/arch/i386/lib/old-checksum.c17
-rw-r--r--pfinet/linux-src/arch/m68k/lib/checksum.c420
-rw-r--r--pfinet/linux-src/arch/ppc/lib/checksum.S194
-rw-r--r--pfinet/linux-src/arch/s390/lib/checksum.c55
-rw-r--r--pfinet/linux-src/arch/sparc/lib/checksum.S581
-rw-r--r--pfinet/linux-src/arch/sparc64/lib/checksum.S277
-rw-r--r--pfinet/linux-src/include/asm-alpha/checksum.h90
-rw-r--r--pfinet/linux-src/include/asm-arm/checksum.h162
-rw-r--r--pfinet/linux-src/include/asm-i386/checksum.h211
-rw-r--r--pfinet/linux-src/include/asm-m68k/checksum.h154
-rw-r--r--pfinet/linux-src/include/asm-mips/checksum.h255
-rw-r--r--pfinet/linux-src/include/asm-ppc/checksum.h113
-rw-r--r--pfinet/linux-src/include/asm-s390/checksum.h186
-rw-r--r--pfinet/linux-src/include/asm-sparc/checksum.h252
-rw-r--r--pfinet/linux-src/include/asm-sparc64/checksum.h207
-rw-r--r--pfinet/linux-src/include/linux/a.out.h268
-rw-r--r--pfinet/linux-src/include/linux/acct.h88
-rw-r--r--pfinet/linux-src/include/linux/adfs_fs.h175
-rw-r--r--pfinet/linux-src/include/linux/adfs_fs_i.h17
-rw-r--r--pfinet/linux-src/include/linux/adfs_fs_sb.h33
-rw-r--r--pfinet/linux-src/include/linux/affs_fs.h115
-rw-r--r--pfinet/linux-src/include/linux/affs_fs_i.h47
-rw-r--r--pfinet/linux-src/include/linux/affs_fs_sb.h74
-rw-r--r--pfinet/linux-src/include/linux/affs_hardblocks.h66
-rw-r--r--pfinet/linux-src/include/linux/amifd.h61
-rw-r--r--pfinet/linux-src/include/linux/amifdreg.h81
-rw-r--r--pfinet/linux-src/include/linux/amigaffs.h228
-rw-r--r--pfinet/linux-src/include/linux/apm_bios.h144
-rw-r--r--pfinet/linux-src/include/linux/arcdevice.h354
-rw-r--r--pfinet/linux-src/include/linux/atalk.h180
-rw-r--r--pfinet/linux-src/include/linux/atari_rootsec.h34
-rw-r--r--pfinet/linux-src/include/linux/auto_fs.h83
-rw-r--r--pfinet/linux-src/include/linux/awe_voice.h524
-rw-r--r--pfinet/linux-src/include/linux/ax25.h98
-rw-r--r--pfinet/linux-src/include/linux/b1lli.h136
-rw-r--r--pfinet/linux-src/include/linux/b1pcmcia.h36
-rw-r--r--pfinet/linux-src/include/linux/baycom.h39
-rw-r--r--pfinet/linux-src/include/linux/binfmts.h77
-rw-r--r--pfinet/linux-src/include/linux/bios32.h34
-rw-r--r--pfinet/linux-src/include/linux/bitops.h72
-rw-r--r--pfinet/linux-src/include/linux/blk.h489
-rw-r--r--pfinet/linux-src/include/linux/blkdev.h94
-rw-r--r--pfinet/linux-src/include/linux/bpqether.h41
-rw-r--r--pfinet/linux-src/include/linux/busmouse.h103
-rw-r--r--pfinet/linux-src/include/linux/capability.h337
-rw-r--r--pfinet/linux-src/include/linux/capi.h127
-rw-r--r--pfinet/linux-src/include/linux/cd1400.h292
-rw-r--r--pfinet/linux-src/include/linux/cdk.h486
-rw-r--r--pfinet/linux-src/include/linux/cdrom.h433
-rw-r--r--pfinet/linux-src/include/linux/coda.h799
-rw-r--r--pfinet/linux-src/include/linux/coda_cache.h77
-rw-r--r--pfinet/linux-src/include/linux/coda_fs_i.h56
-rw-r--r--pfinet/linux-src/include/linux/coda_linux.h143
-rw-r--r--pfinet/linux-src/include/linux/coda_opstats.h94
-rw-r--r--pfinet/linux-src/include/linux/coda_proc.h145
-rw-r--r--pfinet/linux-src/include/linux/coda_psdev.h131
-rw-r--r--pfinet/linux-src/include/linux/coff.h351
-rw-r--r--pfinet/linux-src/include/linux/comstats.h119
-rw-r--r--pfinet/linux-src/include/linux/concap.h109
-rw-r--r--pfinet/linux-src/include/linux/config.h6
-rw-r--r--pfinet/linux-src/include/linux/console.h115
-rw-r--r--pfinet/linux-src/include/linux/console_struct.h109
-rw-r--r--pfinet/linux-src/include/linux/consolemap.h15
-rw-r--r--pfinet/linux-src/include/linux/ctype.h54
-rw-r--r--pfinet/linux-src/include/linux/cyclades.h814
-rw-r--r--pfinet/linux-src/include/linux/dcache.h196
-rw-r--r--pfinet/linux-src/include/linux/delay.h37
-rw-r--r--pfinet/linux-src/include/linux/devpts_fs.h74
-rw-r--r--pfinet/linux-src/include/linux/digi1.h100
-rw-r--r--pfinet/linux-src/include/linux/digiFep1.h136
-rw-r--r--pfinet/linux-src/include/linux/digiPCI.h37
-rw-r--r--pfinet/linux-src/include/linux/dio.h204
-rw-r--r--pfinet/linux-src/include/linux/dirent.h11
-rw-r--r--pfinet/linux-src/include/linux/dlists.h108
-rw-r--r--pfinet/linux-src/include/linux/dmascc.h42
-rw-r--r--pfinet/linux-src/include/linux/dtlk.h104
-rw-r--r--pfinet/linux-src/include/linux/efs_dir.h41
-rw-r--r--pfinet/linux-src/include/linux/efs_fs.h66
-rw-r--r--pfinet/linux-src/include/linux/efs_fs_i.h67
-rw-r--r--pfinet/linux-src/include/linux/efs_fs_sb.h62
-rw-r--r--pfinet/linux-src/include/linux/efs_vh.h69
-rw-r--r--pfinet/linux-src/include/linux/elf.h601
-rw-r--r--pfinet/linux-src/include/linux/elfcore.h88
-rw-r--r--pfinet/linux-src/include/linux/epca.h169
-rw-r--r--pfinet/linux-src/include/linux/epcaconfig.h8
-rw-r--r--pfinet/linux-src/include/linux/errno.h16
-rw-r--r--pfinet/linux-src/include/linux/errqueue.h44
-rw-r--r--pfinet/linux-src/include/linux/etherdevice.h56
-rw-r--r--pfinet/linux-src/include/linux/ext2_fs.h624
-rw-r--r--pfinet/linux-src/include/linux/ext2_fs_i.h42
-rw-r--r--pfinet/linux-src/include/linux/ext2_fs_sb.h66
-rw-r--r--pfinet/linux-src/include/linux/fat_cvf.h49
-rw-r--r--pfinet/linux-src/include/linux/fb.h494
-rw-r--r--pfinet/linux-src/include/linux/fcdevice.h40
-rw-r--r--pfinet/linux-src/include/linux/fcntl.h6
-rw-r--r--pfinet/linux-src/include/linux/fd.h378
-rw-r--r--pfinet/linux-src/include/linux/fddidevice.h39
-rw-r--r--pfinet/linux-src/include/linux/fdreg.h143
-rw-r--r--pfinet/linux-src/include/linux/file.h71
-rw-r--r--pfinet/linux-src/include/linux/filter.h140
-rw-r--r--pfinet/linux-src/include/linux/firewall.h61
-rw-r--r--pfinet/linux-src/include/linux/fs.h910
-rw-r--r--pfinet/linux-src/include/linux/ftape-header-segment.h122
-rw-r--r--pfinet/linux-src/include/linux/ftape-vendors.h137
-rw-r--r--pfinet/linux-src/include/linux/ftape.h212
-rw-r--r--pfinet/linux-src/include/linux/genhd.h267
-rw-r--r--pfinet/linux-src/include/linux/ghash.h218
-rw-r--r--pfinet/linux-src/include/linux/hayesesp.h126
-rw-r--r--pfinet/linux-src/include/linux/hdlcdrv.h383
-rw-r--r--pfinet/linux-src/include/linux/hdreg.h282
-rw-r--r--pfinet/linux-src/include/linux/hfmodem.h256
-rw-r--r--pfinet/linux-src/include/linux/hfs_fs.h337
-rw-r--r--pfinet/linux-src/include/linux/hfs_fs_i.h43
-rw-r--r--pfinet/linux-src/include/linux/hfs_fs_sb.h53
-rw-r--r--pfinet/linux-src/include/linux/hfs_sysdep.h245
-rw-r--r--pfinet/linux-src/include/linux/hippidevice.h58
-rw-r--r--pfinet/linux-src/include/linux/hpfs_fs.h13
-rw-r--r--pfinet/linux-src/include/linux/hpfs_fs_i.h24
-rw-r--r--pfinet/linux-src/include/linux/hpfs_fs_sb.h32
-rw-r--r--pfinet/linux-src/include/linux/i2c.h190
-rw-r--r--pfinet/linux-src/include/linux/icmp.h102
-rw-r--r--pfinet/linux-src/include/linux/icmpv6.h149
-rw-r--r--pfinet/linux-src/include/linux/if.h138
-rw-r--r--pfinet/linux-src/include/linux/if_arcnet.h63
-rw-r--r--pfinet/linux-src/include/linux/if_arp.h133
-rw-r--r--pfinet/linux-src/include/linux/if_cablemodem.h22
-rw-r--r--pfinet/linux-src/include/linux/if_ec.h47
-rw-r--r--pfinet/linux-src/include/linux/if_eql.h81
-rw-r--r--pfinet/linux-src/include/linux/if_ether.h98
-rw-r--r--pfinet/linux-src/include/linux/if_fc.h50
-rw-r--r--pfinet/linux-src/include/linux/if_fddi.h223
-rw-r--r--pfinet/linux-src/include/linux/if_frad.h201
-rw-r--r--pfinet/linux-src/include/linux/if_hippi.h157
-rw-r--r--pfinet/linux-src/include/linux/if_ltalk.h12
-rw-r--r--pfinet/linux-src/include/linux/if_packet.h50
-rw-r--r--pfinet/linux-src/include/linux/if_plip.h28
-rw-r--r--pfinet/linux-src/include/linux/if_ppp.h143
-rw-r--r--pfinet/linux-src/include/linux/if_pppvar.h135
-rw-r--r--pfinet/linux-src/include/linux/if_shaper.h64
-rw-r--r--pfinet/linux-src/include/linux/if_slip.h30
-rw-r--r--pfinet/linux-src/include/linux/if_strip.h25
-rw-r--r--pfinet/linux-src/include/linux/if_tr.h100
-rw-r--r--pfinet/linux-src/include/linux/if_tunnel.h29
-rw-r--r--pfinet/linux-src/include/linux/igmp.h129
-rw-r--r--pfinet/linux-src/include/linux/in.h189
-rw-r--r--pfinet/linux-src/include/linux/in6.h194
-rw-r--r--pfinet/linux-src/include/linux/in_route.h32
-rw-r--r--pfinet/linux-src/include/linux/in_systm.h32
-rw-r--r--pfinet/linux-src/include/linux/inet.h52
-rw-r--r--pfinet/linux-src/include/linux/inetdevice.h128
-rw-r--r--pfinet/linux-src/include/linux/init.h68
-rw-r--r--pfinet/linux-src/include/linux/interrupt.h84
-rw-r--r--pfinet/linux-src/include/linux/ioctl.h6
-rw-r--r--pfinet/linux-src/include/linux/ioport.h35
-rw-r--r--pfinet/linux-src/include/linux/ip.h138
-rw-r--r--pfinet/linux-src/include/linux/ip_fw.h193
-rw-r--r--pfinet/linux-src/include/linux/ip_masq.h140
-rw-r--r--pfinet/linux-src/include/linux/ipc.h47
-rw-r--r--pfinet/linux-src/include/linux/ipsec.h69
-rw-r--r--pfinet/linux-src/include/linux/ipv6.h132
-rw-r--r--pfinet/linux-src/include/linux/ipv6_route.h56
-rw-r--r--pfinet/linux-src/include/linux/ipx.h89
-rw-r--r--pfinet/linux-src/include/linux/irda.h116
-rw-r--r--pfinet/linux-src/include/linux/isdn.h936
-rw-r--r--pfinet/linux-src/include/linux/isdn_divertif.h62
-rw-r--r--pfinet/linux-src/include/linux/isdn_ppp.h236
-rw-r--r--pfinet/linux-src/include/linux/isdnif.h631
-rw-r--r--pfinet/linux-src/include/linux/isicom.h309
-rw-r--r--pfinet/linux-src/include/linux/iso_fs.h230
-rw-r--r--pfinet/linux-src/include/linux/iso_fs_i.h14
-rw-r--r--pfinet/linux-src/include/linux/iso_fs_sb.h31
-rw-r--r--pfinet/linux-src/include/linux/istallion.h134
-rw-r--r--pfinet/linux-src/include/linux/ixjuser.h629
-rw-r--r--pfinet/linux-src/include/linux/joystick.h280
-rw-r--r--pfinet/linux-src/include/linux/kbd_diacr.h8
-rw-r--r--pfinet/linux-src/include/linux/kbd_kern.h164
-rw-r--r--pfinet/linux-src/include/linux/kbd_ll.h12
-rw-r--r--pfinet/linux-src/include/linux/kd.h180
-rw-r--r--pfinet/linux-src/include/linux/kdev_t.h114
-rw-r--r--pfinet/linux-src/include/linux/kernel.h96
-rw-r--r--pfinet/linux-src/include/linux/kernel_stat.h50
-rw-r--r--pfinet/linux-src/include/linux/kernelcapi.h140
-rw-r--r--pfinet/linux-src/include/linux/keyboard.h430
-rw-r--r--pfinet/linux-src/include/linux/kmod.h11
-rw-r--r--pfinet/linux-src/include/linux/lapb.h56
-rw-r--r--pfinet/linux-src/include/linux/limits.h19
-rw-r--r--pfinet/linux-src/include/linux/linkage.h54
-rw-r--r--pfinet/linux-src/include/linux/linux_logo.h1445
-rw-r--r--pfinet/linux-src/include/linux/list.h99
-rw-r--r--pfinet/linux-src/include/linux/lists.h62
-rw-r--r--pfinet/linux-src/include/linux/locks.h61
-rw-r--r--pfinet/linux-src/include/linux/loop.h130
-rw-r--r--pfinet/linux-src/include/linux/lp.h188
-rw-r--r--pfinet/linux-src/include/linux/lp_intern.h21
-rw-r--r--pfinet/linux-src/include/linux/lp_m68k.h135
-rw-r--r--pfinet/linux-src/include/linux/lp_mfc.h13
-rw-r--r--pfinet/linux-src/include/linux/major.h135
-rw-r--r--pfinet/linux-src/include/linux/malloc.h5
-rw-r--r--pfinet/linux-src/include/linux/mc146818rtc.h149
-rw-r--r--pfinet/linux-src/include/linux/mca.h100
-rw-r--r--pfinet/linux-src/include/linux/md.h300
-rw-r--r--pfinet/linux-src/include/linux/minix_fs.h127
-rw-r--r--pfinet/linux-src/include/linux/minix_fs_i.h14
-rw-r--r--pfinet/linux-src/include/linux/minix_fs_sb.h26
-rw-r--r--pfinet/linux-src/include/linux/miscdevice.h43
-rw-r--r--pfinet/linux-src/include/linux/mm.h392
-rw-r--r--pfinet/linux-src/include/linux/mman.h8
-rw-r--r--pfinet/linux-src/include/linux/modsetver.h10
-rw-r--r--pfinet/linux-src/include/linux/module.h287
-rw-r--r--pfinet/linux-src/include/linux/mount.h47
-rw-r--r--pfinet/linux-src/include/linux/mpp.h18
-rw-r--r--pfinet/linux-src/include/linux/mroute.h223
-rw-r--r--pfinet/linux-src/include/linux/msdos_fs.h336
-rw-r--r--pfinet/linux-src/include/linux/msdos_fs_i.h39
-rw-r--r--pfinet/linux-src/include/linux/msdos_fs_sb.h59
-rw-r--r--pfinet/linux-src/include/linux/msg.h81
-rw-r--r--pfinet/linux-src/include/linux/mtio.h373
-rw-r--r--pfinet/linux-src/include/linux/nbd.h85
-rw-r--r--pfinet/linux-src/include/linux/ncp.h204
-rw-r--r--pfinet/linux-src/include/linux/ncp_fs.h331
-rw-r--r--pfinet/linux-src/include/linux/ncp_fs_i.h36
-rw-r--r--pfinet/linux-src/include/linux/ncp_fs_sb.h95
-rw-r--r--pfinet/linux-src/include/linux/ncp_mount.h45
-rw-r--r--pfinet/linux-src/include/linux/net.h152
-rw-r--r--pfinet/linux-src/include/linux/netbeui.h16
-rw-r--r--pfinet/linux-src/include/linux/netdevice.h468
-rw-r--r--pfinet/linux-src/include/linux/netlink.h158
-rw-r--r--pfinet/linux-src/include/linux/netrom.h34
-rw-r--r--pfinet/linux-src/include/linux/nfs.h226
-rw-r--r--pfinet/linux-src/include/linux/nfs3.h252
-rw-r--r--pfinet/linux-src/include/linux/nfs_fs.h284
-rw-r--r--pfinet/linux-src/include/linux/nfs_fs_i.h71
-rw-r--r--pfinet/linux-src/include/linux/nfs_fs_sb.h31
-rw-r--r--pfinet/linux-src/include/linux/nfs_mount.h53
-rw-r--r--pfinet/linux-src/include/linux/nfsiod.h52
-rw-r--r--pfinet/linux-src/include/linux/nls.h56
-rw-r--r--pfinet/linux-src/include/linux/notifier.h115
-rw-r--r--pfinet/linux-src/include/linux/ntfs_fs.h6
-rw-r--r--pfinet/linux-src/include/linux/ntfs_fs_i.h83
-rw-r--r--pfinet/linux-src/include/linux/ntfs_fs_sb.h44
-rw-r--r--pfinet/linux-src/include/linux/nubus.h95
-rw-r--r--pfinet/linux-src/include/linux/nvram.h18
-rw-r--r--pfinet/linux-src/include/linux/openpic.h362
-rw-r--r--pfinet/linux-src/include/linux/pagemap.h156
-rw-r--r--pfinet/linux-src/include/linux/param.h6
-rw-r--r--pfinet/linux-src/include/linux/parport.h396
-rw-r--r--pfinet/linux-src/include/linux/parport_pc.h152
-rw-r--r--pfinet/linux-src/include/linux/pc_keyb.h130
-rw-r--r--pfinet/linux-src/include/linux/pci.h1359
-rw-r--r--pfinet/linux-src/include/linux/personality.h57
-rw-r--r--pfinet/linux-src/include/linux/pg.h63
-rw-r--r--pfinet/linux-src/include/linux/phonedev.h26
-rw-r--r--pfinet/linux-src/include/linux/pipe_fs_i.h34
-rw-r--r--pfinet/linux-src/include/linux/pkt_cls.h146
-rw-r--r--pfinet/linux-src/include/linux/pkt_sched.h277
-rw-r--r--pfinet/linux-src/include/linux/poll.h107
-rw-r--r--pfinet/linux-src/include/linux/posix_types.h48
-rw-r--r--pfinet/linux-src/include/linux/ppp-comp.h198
-rw-r--r--pfinet/linux-src/include/linux/ppp.h4
-rw-r--r--pfinet/linux-src/include/linux/ppp_defs.h182
-rw-r--r--pfinet/linux-src/include/linux/prctl.h9
-rw-r--r--pfinet/linux-src/include/linux/proc_fs.h472
-rw-r--r--pfinet/linux-src/include/linux/ps2esdi.h98
-rw-r--r--pfinet/linux-src/include/linux/ptrace.h26
-rw-r--r--pfinet/linux-src/include/linux/qic117.h290
-rw-r--r--pfinet/linux-src/include/linux/qnx4_fs.h123
-rw-r--r--pfinet/linux-src/include/linux/qnx4_fs_i.h38
-rw-r--r--pfinet/linux-src/include/linux/qnx4_fs_sb.h27
-rw-r--r--pfinet/linux-src/include/linux/qnxtypes.h28
-rw-r--r--pfinet/linux-src/include/linux/quota.h209
-rw-r--r--pfinet/linux-src/include/linux/quotaops.h134
-rw-r--r--pfinet/linux-src/include/linux/raid0.h27
-rw-r--r--pfinet/linux-src/include/linux/raid1.h49
-rw-r--r--pfinet/linux-src/include/linux/raid5.h110
-rw-r--r--pfinet/linux-src/include/linux/random.h73
-rw-r--r--pfinet/linux-src/include/linux/reboot.h52
-rw-r--r--pfinet/linux-src/include/linux/resource.h60
-rw-r--r--pfinet/linux-src/include/linux/rocket.h55
-rw-r--r--pfinet/linux-src/include/linux/romfs_fs.h62
-rw-r--r--pfinet/linux-src/include/linux/romfs_fs_i.h11
-rw-r--r--pfinet/linux-src/include/linux/romfs_fs_sb.h10
-rw-r--r--pfinet/linux-src/include/linux/rose.h87
-rw-r--r--pfinet/linux-src/include/linux/route.h69
-rw-r--r--pfinet/linux-src/include/linux/rpcsock.h121
-rw-r--r--pfinet/linux-src/include/linux/rtnetlink.h673
-rw-r--r--pfinet/linux-src/include/linux/sc26198.h533
-rw-r--r--pfinet/linux-src/include/linux/scc.h258
-rw-r--r--pfinet/linux-src/include/linux/sched.h813
-rw-r--r--pfinet/linux-src/include/linux/sdla.h339
-rw-r--r--pfinet/linux-src/include/linux/sdla_chdlc.h808
-rw-r--r--pfinet/linux-src/include/linux/sdla_fr.h637
-rw-r--r--pfinet/linux-src/include/linux/sdla_ppp.h573
-rw-r--r--pfinet/linux-src/include/linux/sdla_x25.h625
-rw-r--r--pfinet/linux-src/include/linux/sdladrv.h77
-rw-r--r--pfinet/linux-src/include/linux/sdlapci.h67
-rw-r--r--pfinet/linux-src/include/linux/sdlasfm.h103
-rw-r--r--pfinet/linux-src/include/linux/securebits.h30
-rw-r--r--pfinet/linux-src/include/linux/selection.h44
-rw-r--r--pfinet/linux-src/include/linux/sem.h114
-rw-r--r--pfinet/linux-src/include/linux/serial.h142
-rw-r--r--pfinet/linux-src/include/linux/serial167.h175
-rw-r--r--pfinet/linux-src/include/linux/serialP.h119
-rw-r--r--pfinet/linux-src/include/linux/serial_reg.h143
-rw-r--r--pfinet/linux-src/include/linux/shm.h79
-rw-r--r--pfinet/linux-src/include/linux/signal.h212
-rw-r--r--pfinet/linux-src/include/linux/skbuff.h584
-rw-r--r--pfinet/linux-src/include/linux/slab.h71
-rw-r--r--pfinet/linux-src/include/linux/smb.h123
-rw-r--r--pfinet/linux-src/include/linux/smb_fs.h240
-rw-r--r--pfinet/linux-src/include/linux/smb_fs_i.h35
-rw-r--r--pfinet/linux-src/include/linux/smb_fs_sb.h50
-rw-r--r--pfinet/linux-src/include/linux/smb_mount.h25
-rw-r--r--pfinet/linux-src/include/linux/smbno.h278
-rw-r--r--pfinet/linux-src/include/linux/smp.h86
-rw-r--r--pfinet/linux-src/include/linux/smp_lock.h17
-rw-r--r--pfinet/linux-src/include/linux/socket.h280
-rw-r--r--pfinet/linux-src/include/linux/sockios.h109
-rw-r--r--pfinet/linux-src/include/linux/sound.h15
-rw-r--r--pfinet/linux-src/include/linux/soundcard.h1270
-rw-r--r--pfinet/linux-src/include/linux/soundmodem.h90
-rw-r--r--pfinet/linux-src/include/linux/stallion.h156
-rw-r--r--pfinet/linux-src/include/linux/stat.h57
-rw-r--r--pfinet/linux-src/include/linux/stddef.h14
-rw-r--r--pfinet/linux-src/include/linux/string.h43
-rw-r--r--pfinet/linux-src/include/linux/swap.h175
-rw-r--r--pfinet/linux-src/include/linux/swapctl.h35
-rw-r--r--pfinet/linux-src/include/linux/synclink.h255
-rw-r--r--pfinet/linux-src/include/linux/sys.h30
-rw-r--r--pfinet/linux-src/include/linux/sysctl.h560
-rw-r--r--pfinet/linux-src/include/linux/sysrq.h40
-rw-r--r--pfinet/linux-src/include/linux/sysv_fs.h413
-rw-r--r--pfinet/linux-src/include/linux/sysv_fs_i.h15
-rw-r--r--pfinet/linux-src/include/linux/sysv_fs_sb.h121
-rw-r--r--pfinet/linux-src/include/linux/tasks.h25
-rw-r--r--pfinet/linux-src/include/linux/tcp.h90
-rw-r--r--pfinet/linux-src/include/linux/telephony.h200
-rw-r--r--pfinet/linux-src/include/linux/termios.h7
-rw-r--r--pfinet/linux-src/include/linux/time.h92
-rw-r--r--pfinet/linux-src/include/linux/timer.h96
-rw-r--r--pfinet/linux-src/include/linux/times.h11
-rw-r--r--pfinet/linux-src/include/linux/timex.h277
-rw-r--r--pfinet/linux-src/include/linux/tpqic02.h738
-rw-r--r--pfinet/linux-src/include/linux/tqueue.h124
-rw-r--r--pfinet/linux-src/include/linux/trdevice.h40
-rw-r--r--pfinet/linux-src/include/linux/tty.h417
-rw-r--r--pfinet/linux-src/include/linux/tty_driver.h227
-rw-r--r--pfinet/linux-src/include/linux/tty_flip.h28
-rw-r--r--pfinet/linux-src/include/linux/tty_ldisc.h138
-rw-r--r--pfinet/linux-src/include/linux/types.h108
-rw-r--r--pfinet/linux-src/include/linux/udp.h29
-rw-r--r--pfinet/linux-src/include/linux/ufs_fs.h571
-rw-r--r--pfinet/linux-src/include/linux/ufs_fs_i.h32
-rw-r--r--pfinet/linux-src/include/linux/ufs_fs_sb.h245
-rw-r--r--pfinet/linux-src/include/linux/uio.h37
-rw-r--r--pfinet/linux-src/include/linux/ultrasound.h103
-rw-r--r--pfinet/linux-src/include/linux/umsdos_fs.h188
-rw-r--r--pfinet/linux-src/include/linux/umsdos_fs.p118
-rw-r--r--pfinet/linux-src/include/linux/umsdos_fs_i.h75
-rw-r--r--pfinet/linux-src/include/linux/un.h11
-rw-r--r--pfinet/linux-src/include/linux/unistd.h11
-rw-r--r--pfinet/linux-src/include/linux/user.h1
-rw-r--r--pfinet/linux-src/include/linux/utime.h9
-rw-r--r--pfinet/linux-src/include/linux/uts.h23
-rw-r--r--pfinet/linux-src/include/linux/utsname.h36
-rw-r--r--pfinet/linux-src/include/linux/vfs.h6
-rw-r--r--pfinet/linux-src/include/linux/video_decoder.h37
-rw-r--r--pfinet/linux-src/include/linux/video_encoder.h21
-rw-r--r--pfinet/linux-src/include/linux/videodev.h293
-rw-r--r--pfinet/linux-src/include/linux/videotext.h144
-rw-r--r--pfinet/linux-src/include/linux/vmalloc.h23
-rw-r--r--pfinet/linux-src/include/linux/vt.h54
-rw-r--r--pfinet/linux-src/include/linux/vt_buffer.h83
-rw-r--r--pfinet/linux-src/include/linux/vt_kern.h94
-rw-r--r--pfinet/linux-src/include/linux/wait.h33
-rw-r--r--pfinet/linux-src/include/linux/wanpipe.h371
-rw-r--r--pfinet/linux-src/include/linux/wanrouter.h476
-rw-r--r--pfinet/linux-src/include/linux/watchdog.h40
-rw-r--r--pfinet/linux-src/include/linux/wavefront.h675
-rw-r--r--pfinet/linux-src/include/linux/wireless.h448
-rw-r--r--pfinet/linux-src/include/linux/wrapper.h40
-rw-r--r--pfinet/linux-src/include/linux/x25.h93
-rw-r--r--pfinet/linux-src/include/linux/yam.h82
-rw-r--r--pfinet/linux-src/include/linux/zftape.h87
-rw-r--r--pfinet/linux-src/include/linux/zorro.h736
-rw-r--r--pfinet/linux-src/include/net/addrconf.h160
-rw-r--r--pfinet/linux-src/include/net/af_unix.h35
-rw-r--r--pfinet/linux-src/include/net/arp.h24
-rw-r--r--pfinet/linux-src/include/net/atalkcall.h2
-rw-r--r--pfinet/linux-src/include/net/ax25.h349
-rw-r--r--pfinet/linux-src/include/net/ax25call.h2
-rw-r--r--pfinet/linux-src/include/net/br.h328
-rw-r--r--pfinet/linux-src/include/net/checksum.h110
-rw-r--r--pfinet/linux-src/include/net/datalink.h16
-rw-r--r--pfinet/linux-src/include/net/dst.h178
-rw-r--r--pfinet/linux-src/include/net/flow.h101
-rw-r--r--pfinet/linux-src/include/net/icmp.h42
-rw-r--r--pfinet/linux-src/include/net/if_inet6.h134
-rw-r--r--pfinet/linux-src/include/net/inet_common.h43
-rw-r--r--pfinet/linux-src/include/net/ip.h267
-rw-r--r--pfinet/linux-src/include/net/ip6_fib.h177
-rw-r--r--pfinet/linux-src/include/net/ip6_fw.h54
-rw-r--r--pfinet/linux-src/include/net/ip6_route.h111
-rw-r--r--pfinet/linux-src/include/net/ip_fib.h257
-rw-r--r--pfinet/linux-src/include/net/ip_masq.h362
-rw-r--r--pfinet/linux-src/include/net/ip_masq_mod.h86
-rw-r--r--pfinet/linux-src/include/net/ipconfig.h21
-rw-r--r--pfinet/linux-src/include/net/ipip.h33
-rw-r--r--pfinet/linux-src/include/net/ipv6.h325
-rw-r--r--pfinet/linux-src/include/net/ipx.h82
-rw-r--r--pfinet/linux-src/include/net/ipxcall.h2
-rw-r--r--pfinet/linux-src/include/net/lapb.h150
-rw-r--r--pfinet/linux-src/include/net/lapbcall.h2
-rw-r--r--pfinet/linux-src/include/net/llc.h134
-rw-r--r--pfinet/linux-src/include/net/llc_frame.h98
-rw-r--r--pfinet/linux-src/include/net/llc_name.h6
-rw-r--r--pfinet/linux-src/include/net/llc_state.h4
-rw-r--r--pfinet/linux-src/include/net/llccall.h2
-rw-r--r--pfinet/linux-src/include/net/ndisc.h123
-rw-r--r--pfinet/linux-src/include/net/neighbour.h270
-rw-r--r--pfinet/linux-src/include/net/netbeuicall.h2
-rw-r--r--pfinet/linux-src/include/net/netrom.h181
-rw-r--r--pfinet/linux-src/include/net/nrcall.h2
-rw-r--r--pfinet/linux-src/include/net/p8022.h7
-rw-r--r--pfinet/linux-src/include/net/p8022call.h2
-rw-r--r--pfinet/linux-src/include/net/pkt_cls.h93
-rw-r--r--pfinet/linux-src/include/net/pkt_sched.h407
-rw-r--r--pfinet/linux-src/include/net/profile.h311
-rw-r--r--pfinet/linux-src/include/net/protocol.h82
-rw-r--r--pfinet/linux-src/include/net/psnap.h7
-rw-r--r--pfinet/linux-src/include/net/psnapcall.h2
-rw-r--r--pfinet/linux-src/include/net/rarp.h11
-rw-r--r--pfinet/linux-src/include/net/raw.h38
-rw-r--r--pfinet/linux-src/include/net/rawv6.h27
-rw-r--r--pfinet/linux-src/include/net/rose.h241
-rw-r--r--pfinet/linux-src/include/net/rosecall.h2
-rw-r--r--pfinet/linux-src/include/net/route.h153
-rw-r--r--pfinet/linux-src/include/net/scm.h66
-rw-r--r--pfinet/linux-src/include/net/slhc.h6
-rw-r--r--pfinet/linux-src/include/net/slhc_vj.h187
-rw-r--r--pfinet/linux-src/include/net/snmp.h183
-rw-r--r--pfinet/linux-src/include/net/sock.h951
-rw-r--r--pfinet/linux-src/include/net/spx.h93
-rw-r--r--pfinet/linux-src/include/net/spxcall.h2
-rw-r--r--pfinet/linux-src/include/net/tcp.h1097
-rw-r--r--pfinet/linux-src/include/net/transp_v6.h46
-rw-r--r--pfinet/linux-src/include/net/udp.h66
-rw-r--r--pfinet/linux-src/include/net/x25.h221
-rw-r--r--pfinet/linux-src/include/net/x25call.h2
-rw-r--r--pfinet/linux-src/net/core/Makefile41
-rw-r--r--pfinet/linux-src/net/core/datagram.c249
-rw-r--r--pfinet/linux-src/net/core/dev.c2074
-rw-r--r--pfinet/linux-src/net/core/dev_mcast.c251
-rw-r--r--pfinet/linux-src/net/core/dst.c145
-rw-r--r--pfinet/linux-src/net/core/filter.c454
-rw-r--r--pfinet/linux-src/net/core/firewall.c160
-rw-r--r--pfinet/linux-src/net/core/iovec.c278
-rw-r--r--pfinet/linux-src/net/core/neighbour.c1394
-rw-r--r--pfinet/linux-src/net/core/profile.c305
-rw-r--r--pfinet/linux-src/net/core/rtnetlink.c512
-rw-r--r--pfinet/linux-src/net/core/scm.c280
-rw-r--r--pfinet/linux-src/net/core/skbuff.c385
-rw-r--r--pfinet/linux-src/net/core/sock.c1051
-rw-r--r--pfinet/linux-src/net/core/sysctl_net_core.c61
-rw-r--r--pfinet/linux-src/net/core/utils.c66
-rw-r--r--pfinet/linux-src/net/ethernet/Makefile33
-rw-r--r--pfinet/linux-src/net/ethernet/eth.c298
-rw-r--r--pfinet/linux-src/net/ethernet/pe2.c38
-rw-r--r--pfinet/linux-src/net/ethernet/sysctl_net_ether.c13
-rw-r--r--pfinet/linux-src/net/ipv4/Config.in84
-rw-r--r--pfinet/linux-src/net/ipv4/Makefile116
-rw-r--r--pfinet/linux-src/net/ipv4/af_inet.c1167
-rw-r--r--pfinet/linux-src/net/ipv4/arp.c1191
-rw-r--r--pfinet/linux-src/net/ipv4/devinet.c1121
-rw-r--r--pfinet/linux-src/net/ipv4/fib_frontend.c627
-rw-r--r--pfinet/linux-src/net/ipv4/fib_hash.c885
-rw-r--r--pfinet/linux-src/net/ipv4/fib_rules.c419
-rw-r--r--pfinet/linux-src/net/ipv4/fib_semantics.c991
-rw-r--r--pfinet/linux-src/net/ipv4/icmp.c1156
-rw-r--r--pfinet/linux-src/net/ipv4/igmp.c697
-rw-r--r--pfinet/linux-src/net/ipv4/ip_forward.c292
-rw-r--r--pfinet/linux-src/net/ipv4/ip_fragment.c593
-rw-r--r--pfinet/linux-src/net/ipv4/ip_fw.c1773
-rw-r--r--pfinet/linux-src/net/ipv4/ip_gre.c1223
-rw-r--r--pfinet/linux-src/net/ipv4/ip_input.c550
-rw-r--r--pfinet/linux-src/net/ipv4/ip_masq.c2570
-rw-r--r--pfinet/linux-src/net/ipv4/ip_masq_app.c603
-rw-r--r--pfinet/linux-src/net/ipv4/ip_masq_autofw.c448
-rw-r--r--pfinet/linux-src/net/ipv4/ip_masq_cuseeme.c264
-rw-r--r--pfinet/linux-src/net/ipv4/ip_masq_ftp.c393
-rw-r--r--pfinet/linux-src/net/ipv4/ip_masq_irc.c345
-rw-r--r--pfinet/linux-src/net/ipv4/ip_masq_mfw.c770
-rw-r--r--pfinet/linux-src/net/ipv4/ip_masq_mod.c322
-rw-r--r--pfinet/linux-src/net/ipv4/ip_masq_portfw.c509
-rw-r--r--pfinet/linux-src/net/ipv4/ip_masq_quake.c320
-rw-r--r--pfinet/linux-src/net/ipv4/ip_masq_raudio.c578
-rw-r--r--pfinet/linux-src/net/ipv4/ip_masq_user.c473
-rw-r--r--pfinet/linux-src/net/ipv4/ip_masq_vdolive.c294
-rw-r--r--pfinet/linux-src/net/ipv4/ip_nat_dumb.c158
-rw-r--r--pfinet/linux-src/net/ipv4/ip_options.c620
-rw-r--r--pfinet/linux-src/net/ipv4/ip_output.c991
-rw-r--r--pfinet/linux-src/net/ipv4/ip_sockglue.c739
-rw-r--r--pfinet/linux-src/net/ipv4/ipconfig.c970
-rw-r--r--pfinet/linux-src/net/ipv4/ipip.c870
-rw-r--r--pfinet/linux-src/net/ipv4/ipmr.c1609
-rw-r--r--pfinet/linux-src/net/ipv4/proc.c387
-rw-r--r--pfinet/linux-src/net/ipv4/protocol.c211
-rw-r--r--pfinet/linux-src/net/ipv4/rarp.c606
-rw-r--r--pfinet/linux-src/net/ipv4/raw.c573
-rw-r--r--pfinet/linux-src/net/ipv4/route.c2050
-rw-r--r--pfinet/linux-src/net/ipv4/syncookies.c203
-rw-r--r--pfinet/linux-src/net/ipv4/sysctl_net_ipv4.c210
-rw-r--r--pfinet/linux-src/net/ipv4/tcp.c1886
-rw-r--r--pfinet/linux-src/net/ipv4/tcp_input.c2432
-rw-r--r--pfinet/linux-src/net/ipv4/tcp_ipv4.c2062
-rw-r--r--pfinet/linux-src/net/ipv4/tcp_output.c1143
-rw-r--r--pfinet/linux-src/net/ipv4/tcp_timer.c595
-rw-r--r--pfinet/linux-src/net/ipv4/timer.c126
-rw-r--r--pfinet/linux-src/net/ipv4/udp.c1207
-rw-r--r--pfinet/linux-src/net/ipv4/utils.c90
-rw-r--r--pfinet/linux-src/net/ipv6/addrconf.c1964
-rw-r--r--pfinet/linux-src/net/ipv6/af_inet6.c646
-rw-r--r--pfinet/linux-src/net/ipv6/datagram_ipv6.c434
-rw-r--r--pfinet/linux-src/net/ipv6/exthdrs.c770
-rw-r--r--pfinet/linux-src/net/ipv6/icmpv6.c676
-rw-r--r--pfinet/linux-src/net/ipv6/ip6_fib.c1203
-rw-r--r--pfinet/linux-src/net/ipv6/ip6_flowlabel.c627
-rw-r--r--pfinet/linux-src/net/ipv6/ip6_input.c284
-rw-r--r--pfinet/linux-src/net/ipv6/ip6_output.c720
-rw-r--r--pfinet/linux-src/net/ipv6/ipv6_sockglue.c452
-rw-r--r--pfinet/linux-src/net/ipv6/mcast.c711
-rw-r--r--pfinet/linux-src/net/ipv6/ndisc.c1217
-rw-r--r--pfinet/linux-src/net/ipv6/protocol_ipv6.c117
-rw-r--r--pfinet/linux-src/net/ipv6/raw_ipv6.c691
-rw-r--r--pfinet/linux-src/net/ipv6/reassembly.c492
-rw-r--r--pfinet/linux-src/net/ipv6/route_ipv6.c1974
-rw-r--r--pfinet/linux-src/net/ipv6/tcp_ipv6.c1803
-rw-r--r--pfinet/linux-src/net/ipv6/udp_ipv6.c1014
-rw-r--r--pfinet/loopback.c137
-rw-r--r--pfinet/main.c495
-rw-r--r--pfinet/mapped-time.h30
-rw-r--r--pfinet/mig-decls.h55
-rw-r--r--pfinet/mig-mutate.h40
-rw-r--r--pfinet/misc.c69
-rw-r--r--pfinet/options.c570
-rw-r--r--pfinet/pfinet-ops.c93
-rw-r--r--pfinet/pfinet.h107
-rw-r--r--pfinet/sched.c76
-rw-r--r--pfinet/socket-ops.c544
-rw-r--r--pfinet/socket.c126
-rw-r--r--pfinet/stubs.c73
-rw-r--r--pfinet/time.c27
-rw-r--r--pfinet/timer-emul.c182
-rw-r--r--pfinet/tunnel.c683
-rw-r--r--pflocal/Makefile33
-rw-r--r--pflocal/connq.c327
-rw-r--r--pflocal/connq.h63
-rw-r--r--pflocal/io.c657
-rw-r--r--pflocal/mig-decls.h59
-rw-r--r--pflocal/mig-mutate.h33
-rw-r--r--pflocal/pf.c140
-rw-r--r--pflocal/pflocal.c139
-rw-r--r--pflocal/sock.c502
-rw-r--r--pflocal/sock.h167
-rw-r--r--pflocal/socket.c475
-rw-r--r--pflocal/sserver.c105
-rw-r--r--pflocal/sserver.h33
-rw-r--r--proc/=proc_excrepl.defs37
-rw-r--r--proc/Makefile38
-rw-r--r--proc/cpu-types.c163
-rw-r--r--proc/hash.c156
-rw-r--r--proc/host.c481
-rw-r--r--proc/info.c801
-rw-r--r--proc/main.c146
-rw-r--r--proc/mgt.c947
-rw-r--r--proc/mig-decls.h60
-rw-r--r--proc/mig-mutate.h33
-rw-r--r--proc/msg.c163
-rw-r--r--proc/notify.c104
-rw-r--r--proc/ourmsg.defs12
-rw-r--r--proc/pgrp.c463
-rw-r--r--proc/proc.h205
-rw-r--r--proc/proc_exc.defs51
-rw-r--r--proc/stubs.c178
-rw-r--r--proc/wait.c318
-rw-r--r--release/=announce-0.0111
-rw-r--r--release/=announce-0.170
-rw-r--r--release/=announce-0.262
-rw-r--r--release/COPYING.LIB481
-rw-r--r--release/INSTALL-binary472
-rw-r--r--release/Makefile73
-rw-r--r--release/README11
-rw-r--r--release/SETUP49
-rw-r--r--release/SOURCES.0.0111
-rw-r--r--release/SOURCES.0.2108
-rw-r--r--release/bfloppy-special.copy1
-rw-r--r--release/bfloppy.boot14
-rw-r--r--release/bfloppy.copy11
-rw-r--r--release/bfloppy1-special.copy1
-rw-r--r--release/bfloppy1.copy7
-rw-r--r--release/bfloppy1.grub29
-rw-r--r--release/bfloppy2-special.copy2
-rw-r--r--release/bfloppy2.boot14
-rw-r--r--release/bfloppy2.copy4
-rw-r--r--release/checklist61
-rw-r--r--release/dist-README36
-rwxr-xr-xrelease/fstab-to-settrans1
-rwxr-xr-xrelease/install-stripped184
-rw-r--r--release/menu.lst49
-rwxr-xr-xrelease/mkemptyso.sh5
-rw-r--r--release/mkfsimage.sh412
-rwxr-xr-xrelease/mksmallso.sh48
-rw-r--r--release/release-steps8
-rw-r--r--release/rfloppy-special.copy3
-rw-r--r--release/rfloppy.copy175
-rw-r--r--release/rfloppy.group1
-rw-r--r--release/rfloppy.nss19
-rw-r--r--release/rfloppy.passwd1
-rw-r--r--release/servers.boot14
-rw-r--r--release/tool-Makefile93
-rw-r--r--storeio/Makefile29
-rw-r--r--storeio/dev.c473
-rw-r--r--storeio/dev.h127
-rw-r--r--storeio/io.c378
-rw-r--r--storeio/open.c127
-rw-r--r--storeio/open.h68
-rw-r--r--storeio/pager.c286
-rw-r--r--storeio/storeio.c426
-rw-r--r--sutils/MAKEDEV.sh231
-rw-r--r--sutils/Makefile43
-rw-r--r--sutils/clookup.c155
-rwxr-xr-xsutils/e2os.sh153
-rw-r--r--sutils/fsck.c568
-rw-r--r--sutils/fstab.c943
-rw-r--r--sutils/fstab.h178
-rw-r--r--sutils/halt.c40
-rw-r--r--sutils/losetup.sh66
-rw-r--r--sutils/reboot.c40
-rw-r--r--sutils/swapoff.c2
-rw-r--r--sutils/swapon.c551
-rw-r--r--sutils/update.c51
-rw-r--r--tasks161
-rw-r--r--term/Makefile37
-rw-r--r--term/devio.c804
-rw-r--r--term/hurdio.c652
-rw-r--r--term/main.c451
-rw-r--r--term/mig-mutate.h25
-rw-r--r--term/munge.c770
-rw-r--r--term/ourmsg.defs14
-rw-r--r--term/ptyio.c643
-rw-r--r--term/term.h393
-rw-r--r--term/users.c2250
-rw-r--r--term/xinl.c2
-rw-r--r--tmpfs/Makefile29
-rw-r--r--tmpfs/dir.c315
-rw-r--r--tmpfs/node.c594
-rw-r--r--tmpfs/pager-stubs.c96
-rw-r--r--tmpfs/tmpfs.c447
-rw-r--r--tmpfs/tmpfs.h85
-rw-r--r--trans/Makefile67
-rw-r--r--trans/bogus-fifo.c160
-rw-r--r--trans/crash.c800
-rw-r--r--trans/fakeroot.c1041
-rw-r--r--trans/fifo.c628
-rw-r--r--trans/firmlink.c288
-rw-r--r--trans/fwd.c51
-rw-r--r--trans/hello-mt.c332
-rw-r--r--trans/hello.c293
-rw-r--r--trans/ifsock.c152
-rw-r--r--trans/magic.c567
-rw-r--r--trans/mtab.c882
-rw-r--r--trans/new-fifo.c857
-rw-r--r--trans/null.c340
-rw-r--r--trans/password.c230
-rw-r--r--trans/proxy-defpager.c279
-rw-r--r--trans/remap.c152
-rw-r--r--trans/streamio.c1189
-rw-r--r--trans/symlink.c236
-rw-r--r--usermux/Makefile30
-rw-r--r--usermux/leaf.c152
-rw-r--r--usermux/mux.c502
-rw-r--r--usermux/node.c129
-rw-r--r--usermux/stubs.c143
-rw-r--r--usermux/usermux-xinl.c24
-rw-r--r--usermux/usermux.c156
-rw-r--r--usermux/usermux.h99
-rw-r--r--utils/Makefile85
-rw-r--r--utils/addauth.c100
-rw-r--r--utils/devprobe.c112
-rw-r--r--utils/fakeauth.c443
-rw-r--r--utils/fakeroot.sh65
-rw-r--r--utils/frobauth-mod.c162
-rw-r--r--utils/frobauth.c282
-rw-r--r--utils/frobauth.doc83
-rw-r--r--utils/frobauth.h71
-rw-r--r--utils/fsysopts.c128
-rw-r--r--utils/ftpcp.c397
-rw-r--r--utils/ftpdir.c329
-rw-r--r--utils/gcore.c107
-rw-r--r--utils/ids.c196
-rw-r--r--utils/login.c895
-rw-r--r--utils/loginpr.sh20
-rw-r--r--utils/match-options.c68
-rw-r--r--utils/match-options.h33
-rw-r--r--utils/mount.c677
-rw-r--r--utils/msgport.c661
-rw-r--r--utils/nonsugid.c63
-rw-r--r--utils/nullauth.c90
-rw-r--r--utils/parse.c185
-rw-r--r--utils/parse.h63
-rw-r--r--utils/pids.c231
-rw-r--r--utils/pids.h47
-rw-r--r--utils/portinfo.c350
-rw-r--r--utils/ps.c448
-rw-r--r--utils/psout.c144
-rw-r--r--utils/psout.h34
-rw-r--r--utils/remap.sh67
-rw-r--r--utils/rmauth.c121
-rw-r--r--utils/rpctrace.c1968
-rw-r--r--utils/setauth.c134
-rw-r--r--utils/settrans.c331
-rw-r--r--utils/shd.c389
-rw-r--r--utils/showtrans.c144
-rw-r--r--utils/storecat.c73
-rw-r--r--utils/storeinfo.c267
-rw-r--r--utils/storeread.c130
-rw-r--r--utils/sush.sh90
-rw-r--r--utils/syncfs.c80
-rw-r--r--utils/umount.c357
-rw-r--r--utils/unsu.c90
-rw-r--r--utils/uptime.sh62
-rw-r--r--utils/vminfo.c241
-rw-r--r--utils/vmstat.c660
-rw-r--r--utils/w.c520
-rw-r--r--utils/x.c247
-rw-r--r--version.h.in29
1744 files changed, 342236 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..1719508f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+*~
+
+*.d
+*.o
+*.a
+*.so
+*.so.*
+TAGS
+
+autom4te.cache/
+/config.log
+/config.make
+/config.status
+/configure
+/version.h
diff --git a/BUGS b/BUGS
new file mode 100644
index 00000000..dd1307ef
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,36 @@
+This file is for bugs in Mach. Bugs in the Hurd go into TODO.
+
+Do not ever delete a bug from this list; new Mach releases need to be
+checked to make sure fixed bugs stay fixed, and so this list must
+never get truncated. -mib
+
+--Reported, verified fixed--
+
+--Reported, claimed fixed--
+MiG user stubs need to destroy reply port on any message transmission
+error, not just a limited set.
+
+--Reported--
+Bug in vm_fault: when vm_fault on a shadow page:
+First page from anonymous copy; returns unavaliable. Then fault on
+the actual page and block. This block is interruptible, but a lock is
+being held on the copy object (because it's FIRST_M). If the sleep is
+interrupted, the lock gets released, but unfortunately, it could also
+be suspended, and that's a lose.
+
+pager flush of wired pages can block forever uninterruptibly.
+
+unwiring of pages should call PAGE_WAKEUP
+
+permission arg of vm_wire is not passed to vm_fault, so that vm_fault
+overeagerly wires the current protection, which might be more than the
+permission arg of vm_wire.
+
+When wiring, vm_fault uses current permission instead of max
+permission; this will cause a failure of vm_wire if the current
+permission is later promoted.
+
+Patch for MiG dealing when sreplyport and mach_port_poly_t are used together.
+
+Install Linux and NetBSD physical CPU reset code in place of current
+Mach version.
diff --git a/COPYING b/COPYING
new file mode 100644
index 00000000..a43ea212
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 00000000..8ecce54b
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,57 @@
+2772f5c6a6a51cf946fd95bf6ffe254273157a21 is the last commit imported from CVS.
+All commits after that one have valid author and committer information.
+
+Use this to examine the change log for earlier changes:
+
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:auth/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:benchmarks/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:boot/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:config/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:console-client/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:console/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:daemons/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:defpager/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:doc/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:exec/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:ext2fs/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:fatfs/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:fstests/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:ftpfs/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:hostmux/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:hurd/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:include/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:init/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:isofs/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libcons/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libdirmgt/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libdiskfs/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libfshelp/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libftpconn/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libhurdbugaddr/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libihash/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libiohelp/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libnetfs/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libpager/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libpipe/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libports/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libps/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libshouldbeinlibc/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libstore/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libthreads/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:libtrivfs/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:login/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:mach-defpager/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:nfs/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:nfsd/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:pfinet/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:pflocal/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:proc/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:release/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:storeio/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:sutils/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:term/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:tmpfs/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:trans/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:usermux/ChangeLog
+ $ git show 2772f5c6a6a51cf946fd95bf6ffe254273157a21:utils/ChangeLog
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 00000000..b45322e0
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,209 @@
+This is the GNU Hurd, <http://www.gnu.org/software/hurd/>. Welcome.
+
+The following text has not been updated in a long time. Beware.
+
+This file contains instructions for compiling and installing the Hurd
+from your existing Hurd system.
+
+If you are running any other kind of system whatsoever, these
+instructions will *NOT* be sufficient. The file INSTALL-cross
+contains some past instructions for doing so, but it's too much
+trouble to maintain them and make them easier. Your best bet is to
+start with a running Hurd system already.
+
+The Hurd and the GNU C Library each need each other in order to
+compile. If you are installing both, please follow the directions
+"Building the Hurd and libc together". If the C library version you
+want to use is already installed, and you know both it and this
+version of the Hurd will interoperate together, then see the
+instructions "Bulding the Hurd by itself" below.
+
+The Hurd version 0.2 has been verified to work with versions 2.0.3 and
+2.0.4 of the GNU C library. (But note that version 2.0.3 has some
+easily-fixed bugs in compilation for the i386-gnu target.)
+
+Bug reports for the GNU Hurd should be sent to the mailing list
+`bug-hurd@prep.ai.mit.edu'. Please do not send requests for
+assistance in installing or using the software to that address.
+Instead, send requests for assistance to the mailing list
+`help-hurd@prep.ai.mit.edu'. You can join these lists by sending a
+request to `bug-hurd-request@prep.ai.mit.edu' or
+`help-hurd-request@prep.ai.mit.edu' respectively.
+
+
+Configuring the Hurd
+====================
+
+The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a
+file `config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+If you need to do unusual things to compile the package, please try to
+figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+The file `configure.ac' is used to create `configure' by a program
+called `autoconf'. You only need `configure.ac' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+
+Building the Hurd, GNUmach, and libc together
+===========================================
+
+1. Configure GNUmach.
+
+3. Do `make install' in <mach4-build>/mig. ONLY.
+
+4. Do `make install' in <mach4-build>/include. ONLY.
+ This may have installed an include file called mach_init.h. If so,
+ delete it; you want to use the one that libc will install, and if
+ this file exists, libc might not install its version.
+
+5. Configure the Hurd with `configure'.
+
+6. In the Hurd directory, type `make install-headers no_deps=t'.
+
+7. Configure libc.
+
+8. `make install' libc.
+
+9. `make' and `make install' Hurd.
+
+10. Do `make -r kernel' in mach4/kernel.
+
+11. Copy mach4/kernel to /boot/kernel.
+
+
+Building the Hurd and libc together
+===================================
+
+1. `cd' to the directory containing the Hurd's source code and type
+ `./configure' to configure the Hurd.
+
+2. Type `make install-headers no_deps=t' to install the Hurd's header files.
+
+3. Follow the instructions in the GNU C Library for configuring and
+ installing GNU libc.
+
+4. Return to the directory containing the Hurd's source code and type
+ `make' to compile the Hurd.
+
+5. Type `make install' to install the Hurd.
+
+
+Building the Hurd by itself
+===========================
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. `cd' to the directory where you want the object files
+and executables to go and run the `configure' script. `configure'
+automatically checks for the source code in the directory that
+`configure' is in and in `..'.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in `/bin',
+`/man', etc. You can specify an installation prefix by giving
+`configure' the option `--prefix=PATH'.
+
+You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If
+you give `configure' the option `--exec-prefix=PATH', the package will
+use PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for
+particular kinds of files. Run `configure --help' for a list of the
+directories you can set and what kinds of files go in them.
+
+If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure'
+the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Please note, however, that the Hurd knows where it is located in the
+filesystem. If you have installed it in an unusual location, the
+system might not work properly, or at all. The chief utility of these
+options for the Hurd is to allow you to "install" in some alternate
+location, and then copy these to the actual root filesystem later.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+ Use and save the results of the tests in FILE instead of
+ `./config.cache'. Set FILE to `/dev/null' to disable caching, for
+ debugging `configure'.
+
+`--help'
+ Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made.
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--version'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
diff --git a/INSTALL-cross b/INSTALL-cross
new file mode 100644
index 00000000..05f8b8f9
--- /dev/null
+++ b/INSTALL-cross
@@ -0,0 +1,335 @@
+Cross-compiling the GNU Hurd -*- Outline -*-
+
+* READ HERE!
+
+The procedure described below -- albeit not completely obsolete, of course --
+is outdated a bit. If you attempt to build a cross compiler, have a look at
+<http://www.gnu.org/software/hurd/toolchain/cross-gnu.html> for now.
+
+Last update 1998-06-01 Gordon Matzigkeit <gord@gnu.org>.
+Previous update 1996-04-11 Thomas Bushnell, n/BSG <thomas@gnu.org>.
+
+* Introduction
+These are instructions for building the Hurd as in cross-development
+environment. Unless you are building the Hurd on an already running
+Hurd system, you will need these directions to build the Hurd from any
+other kind of system. Even if you are using a Mach system (Lites,
+say) you will *STILL* need to follow these directions; this is still
+cross-compilation.
+
+IMPORTANT: If you are not prepared to fix bugs without begging other
+people for help, or you lack patience, then this process is *not for
+you*! Go get a binary distribution... downloading 80MB of files over
+a 9600 baud PPP connection takes far less time and effort than
+cross-compiling, especially when you consider that the size of the
+source code for all the packages in the Hurd binary distribution is
+larger than the distribution itself.
+
+You will need *at least* 500 MB of free disk space for the build
+process. More is always better.
+
+NOTE: On the other hand, do not be sloppy with any of these
+instructions, until you have tried following them exactly, and they
+didn't work for you the first time. There are all sorts of pitfalls
+and untested paths, so it is better not to be creative with things
+(like trying to install the cross-compiler somewhere other than
+/usr/local). Expect random problems if you deviate from these
+instructions. You have been warned.
+
+In other words, follow these instructions closely, but not blindly.
+
+You may wish to create build logs, so that you can trace any problems
+after you let a compilation run unattended. To do this, background
+and redirect all make output to a file (i.e. `nohup make -k >&
+make.out &'). If you wish to monitor the build, you can use a command
+like `tail -f make.out'. After the build completes, simply search the
+log for `***' (i.e. `grep '\*\*\*' make.out'), and you'll find any
+errors.
+
+* Cross-compiler tools
+
+First you must install a cross-GCC and binutils. Currently, GCC 2.8.1
+and binutils version 2.9.1 are the minimum suggested versions.
+
+Even if you are building the Hurd on a 586, even if it's running a
+different Mach-based OS, you are still cross-compiling. Don't attempt
+to use tools that have been configured for something other than
+i586-gnu (unless, of course, you are trying to port the Hurd to a
+different processor).
+
+** Binutils
+
+The cross-binutils installation is quite straightforward. Just
+configure, build and install them with commands like:
+
+$ [cd to top of binutils source tree]
+$ mkdir obj
+$ cd obj
+$ ../configure --prefix=/usr/local --target=i586-gnu
+[...]
+$ make
+[...]
+$ su
+Password:
+# make install
+[...]
+#
+
+The above instructions show you how to create a new build
+subdirectory, `obj', and then configure and build the package there.
+You should get used to this process, because some of the Hurd packages
+will not build properly in any other way.
+
+This installation should be smooth. If you see any strange errors
+during the build process, you should investigate them, since they are
+probably bugs.
+
+** GCC (attempt #1)
+
+Bootstrapping a GCC cross-compiler is only slightly more difficult
+than installing a cross-binutils. GCC has a few interdependencies
+with the Hurd libc, such as the creation of the `libgcc2.a' library.
+
+Therefore, the first time you try compiling and installing GCC, you
+will need to kludge things so that you can get a mostly-working
+compiler, then come back and redo them to get a fully-working
+compiler.
+
+There is a bug when creating a cross-compiler on certain platforms.
+In order to avoid this bug, you should copy a working `float.h' header
+file from a known working GCC installation for the same processor. On
+a GNU/Linux machine using gcc-2.7.2.1, `float.h' can be found in
+`/usr/lib/gcc-lib/i386-linux/2.7.2.1/include/float.h'. In general,
+you can find it in
+`$(prefix)/lib/gcc-lib/$(host)/$(VERSION)/include/float.h'. You will
+need to copy this file into the `include' subdirectory of the unpacked
+GCC distribution, overwriting the old `include/float.h' if it exists.
+
+The build process should look something like the following. Be sure
+to use the `-k' and `LANGUAGES=c' make arguments this time around, or
+else the build will fail in random places:
+
+$ [cd to top of gcc source tree]
+$ cp /usr/lib/gcc-lib/i386-linux/2.7.2.1/include/float.h include/float.h
+$ mkdir obj
+$ cd obj
+$ ../configure --prefix=/usr/local --target=i586-gnu --with-gnu-as --with-gnu-ld
+[...]
+$ make -k CFLAGS="-g -O2" LANGUAGES=c
+[... fails while trying to build libgcc2.a]
+$ su
+Password:
+# make -k install LANGUAGES=c
+[again, fails on libgcc2.a]
+#
+
+If the build fails anywhere except on libgcc2, you have a problem.
+Investigate it. Otherwise, you should now have a mostly-working
+cross-compiler suite in /usr/local.
+
+NOTE: Do not delete the GCC compile tree. You will need it later in
+order to finish the installation. If you choose to delete it, you
+will have to repeat this step in its entirety.
+
+* Set up the Hurd root
+
+You need space to "install" the compiled Hurd, its libraries, include
+files, and binaries that will run on the Hurd. In these instructions,
+we will refer to this as the "installation staging area". This will,
+more or less, be a suitable image to use as a Hurd root image when you
+are finished.
+
+This space needs to be accessible to the machine doing the
+cross-compilation, because the libc and include files that go in the
+Hurd's root filesystem are the same ones that are needed during
+cross-compilation.
+
+We strongly advise that this directory not be put in /usr/local. This
+will tend to cause confusion. It would be a good idea to create a new
+filesystem, and mount it on your cross-compilation machine as /hurd.
+
+If you followed the directions above, then in /usr/local/i586-gnu you
+already have a number of cross-development tools (ar, ranlib, ld, as
+and so forth). This is the place where the compiler looks for
+cross-development stuff. So now make two symlinks, named
+/usr/local/i586-gnu/include and /usr/local/i586-gnu/lib, and point
+them at /hurd/include and /hurd/lib, where `/hurd' is the name of your
+Hurd installation staging area.
+
+If /usr/local/i586-gnu/include or /usr/local/i586-gnu/lib already
+exists, you should move their contents to your Hurd installation
+staging area before creating the symlinks.
+
+If you don't do these steps, you will lose. Do them now.
+
+* Install Mach
+
+Get the latest gnumach distribution, and configure it to cross
+compile. You should read the README in order to determine which
+device driver options you should use. You should also specify your
+current build platform... in the following example, we are
+cross-compiling from an i586-linux-gnu machine:
+
+$ [cd to top of gnumach source tree]
+$ mkdir obj
+$ cd obj
+$ CC=i586-gnu-gcc ../configure --build=i586-linux-gnu --host=i586-gnu \
+ --enable-com --enable-floppy --enable-ide --enable-aha152x
+[...]
+$ make
+[...]
+$ su
+Password:
+# make install prefix=/hurd
+[...]
+#
+
+Besides building the Mach kernel, this step installs several Mach
+headers and interface files into the staging area. These files are
+required for cross-compilation.
+
+* Install a cross-MiG
+
+This process can be confusing, because there are so many different
+varieties of cross-compilers:
+
+1) When you were building Mach, above, the build process needed a MiG
+which can could run on the build machine, but create code for GNU.
+This is called `local-mig' in the Mach Makefiles.
+
+2) Then, when you installed Mach, you also installed a MiG which can
+run on GNU and create code for GNU. This is called `cross-mig' in the
+Makefiles.
+
+3) Now, you need to install a version of MiG like #1, so that you can
+use it to build the C library and the Hurd. Unfortunately, the shell
+script wrapper used in #1 is not appropriate for installation, so you
+need to generate Yet-Another-MiG:
+
+# make install-local-mig prefix=/usr/local
+[...]
+#
+
+Be sure to set the `prefix' variable as indicated, or you will
+accidentally clobber the MiG you installed in the previous step.
+
+* Build the GNU C library
+
+In order to build the GNU C library for the Hurd, you will need recent
+versions of several tools, including gawk. See the glibc INSTALL file
+for more details.
+
+** Install Hurd headers
+
+The Hurd interface definitions and include files need to be visible to
+the cross-compiler so that the C library can use them.
+
+$ [cd to top of hurd source tree]
+$ mkdir obj
+$ cd obj
+$ ../configure --build=i586-linux-gnu --host=i586-gnu \
+ --prefix=/hurd --disable-profile
+[...]
+$ su
+Password:
+# make install-headers no_deps=t
+[...]
+#
+
+This step may spout a few warning messages, but you don't need to
+worry about them, because you aren't interested in compiling any of
+the Hurd (yet).
+
+
+** Build and install C library
+
+Configure, compile, and install the core GNU C library (note that we
+reset the `install_root' variable instead of `prefix'):
+
+$ [cd to top of glibc source tree]
+$ mkdir obj
+$ cd obj
+$ ../configure --build=i586-linux-gnu --host=i586-gnu \
+ --prefix= --enable-add-ons=crypt --disable-profile
+[...]
+$ make -k
+[... fails when trying to link programs]
+$ su
+Password:
+# make -k install install_root=/hurd
+[... again, fails]
+#
+
+This process fails because the cross-GCC you installed is missing
+libgcc2.a, which is required to link working programs. Do not delete
+the libc source files... you will need them again very soon.
+
+** Finish GCC install
+
+If you were silly, and deleted the GCC source tree, you need to go
+back to the first GCC build step and follow those instructions again
+(which should successfully build and install the entire C
+cross-compiler).
+
+Otherwise, there are now enough headers to finish installing the GCC
+cross-compiler, so do it:
+
+$ [cd to top of gcc source tree]
+$ cd obj
+$ make -k
+[...]
+$ su
+Password:
+# make -k install
+[...]
+#
+
+This time there should be no failures.
+
+** Finish libc install
+
+Now there is a fully-working GCC, so the libc programs can be built.
+Continue the build process:
+
+$ [cd to top of glibc source tree]
+$ cd obj
+$ make -k
+[...]
+$ su
+Password:
+# make -k install install_root=/hurd
+[...]
+#
+
+There should be no errors or warnings from this step.
+
+
+* Install the Hurd
+
+Since you already configured the Hurd in a previous step, you can now
+build and install it:
+
+$ [cd to top of hurd source tree]
+$ cd obj
+$ make -k
+[...]
+$ su
+Password:
+# make -k install prefix=/hurd
+[...]
+#
+
+This step should complete with no problems.
+
+
+* Final details
+
+Now in your Hurd staging area are the complete binaries for the Hurd
+and its programs, and the C library and its associated programs. You
+will want binaries for other programs too, of course--for example, you
+have no shell yet. In general, you can build most GNU packages
+without too much hassle using your cross compilers. In this way you
+can build up as much of a binary distribution as you like.
+
+See the file `INSTALL-binary' for instructions on bootstrapping and
+running your new binaries.
diff --git a/Makeconf b/Makeconf
new file mode 100644
index 00000000..5cf995d3
--- /dev/null
+++ b/Makeconf
@@ -0,0 +1,651 @@
+# Generic configuration for Hurd compilation -*- makefile-gmake -*-
+
+# Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2001, 2002, 2003,
+# 2006, 2007, 2008, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Directory makefiles should set the variable makemode to either
+# `server' if they compile and install a single program for /hurd
+# `utility' if they compile and install a single program for /bin
+# `servers' if they compile and install multiple programs for /hurd
+# `utilities' if they compile and install multiple programs for /bin
+# `library' if they compile and install a library
+# `misc' if they do none of those
+
+# Every makefile should define
+# SRCS (all actual source code)
+# OBJS (all .o files used to produce some target).
+# HURDLIBS (all Hurd libraries used; with no directory name or `lib' attached)
+# For types `server' and `utility' an automatic dependency will be
+# written for these, and type `library' will include a .so dependency.
+# Types `servers' and `utilities'; you have to do it yourself.
+# DISTFILES (any built files to be included in distributions).
+
+# Types `server' and `utility' should define
+# target (the name of the program built)
+# OTHERLIBS (all libraries used)
+
+# Types `servers' and `utilities' should define
+# targets (the names of all the programs built)
+# special-targets (targets which should not be built the normal way
+# and have their own rules)
+
+# Type `library' should define
+# libname (the name of the library, without .a.)
+# installhdrs (header files that should be installed in /include)
+# installhdrsubdir (the subdirectory they should go in, default `hurd')
+# and must not define $(targets).
+
+# Put this first so it's the default
+all:
+
+# Figure out how to locate the parent directory from here.
+ifeq (.,$(dir))
+.. =
+else
+.. = ../
+endif
+
+ifndef srcdir
+# We are building in the source directory itself.
+srcdir = .
+ifeq (.,$(dir))
+top_srcdir = .
+else
+top_srcdir = ..
+endif
+endif
+
+# Generic compiler options, appended to whatever the particular makefile set.
+# The config.make file will append the values chosed by configure.
+INCLUDES = -I. $(srcdirinc)
+ifneq (.,$(dir))
+INCLUDES += -I.. $(top_srcdirinc)
+endif
+INCLUDES += -I$(..)include -I$(top_srcdir)/include
+CPPFLAGS += $(INCLUDES) \
+ -D_GNU_SOURCE -D_IO_MTSAFE_IO -D_FILE_OFFSET_BITS=64 \
+ $($*-CPPFLAGS)
+CFLAGS += -std=gnu99 $(gnu89-inline-CFLAGS) -Wall -g -O3 \
+ $($*-CFLAGS)
+
+# Include the configure-generated file of parameters.
+# This sets up variables for build tools and installation directories.
+ifneq (,$(wildcard $(..)config.make))
+include $(..)config.make
+# Set a flag for the makefiles to indicated config.make was included.
+configured = yes
+endif
+
+# If we're not configured, don't do deps; then `make TAGS' and such can work.
+ifndef configured
+no_deps = t
+endif
+
+# Test build options set by configure.
+ifeq (no,$(build-profiled))
+no_prof = t
+endif
+
+
+# Flags for compilation.
+# It is important to have this inclusion first; that picks up our
+# library header files locally rather than from installed copies.
+# Append to any value set by the specific Makefile or by configure.
+ifeq ($(srcdir),.)
+srcdirinc=
+else
+srcdirinc=-I$(srcdir)
+endif
+ifeq ($(top_srcdir),..)
+top_srcdirinc=
+else
+top_srcdirinc=-I$(top_srcdir)
+endif
+
+# More useful version of HURDLIBS
+library_deps=$(foreach lib,$(HURDLIBS),$(..)lib$(lib)/lib$(lib).so)
+
+# Local programs:
+MKINSTALLDIRS = $(top_srcdir)/mkinstalldirs
+move-if-change = $(SHELL) $(top_srcdir)/move-if-change
+
+# Decode makemode.
+# After this section, $(targets) and $(progtarg) will be defined,
+# and everything else should use only those and not $(target).
+# targets will have all the (one or more) targets that should be installed;
+# progtarg will have all the (one or more) programs that should be linked;
+# linktarg will have the complete set of linked targets, including both
+# .static versions of $(progtarg) and/or shared library object targets.
+
+ifeq ($(makemode),server)
+ doinst := one
+ makemode-instdir := hurd
+ clean := yes
+ targets = $(target)
+ progtarg = $(targets)
+endif
+
+ifeq ($(makemode),utility)
+ doinst := one
+ makemode-instdir := bin
+ clean := yes
+ targets = $(target)
+endif
+
+ifeq ($(makemode),servers)
+ doinst := many
+ makemode-instdir := hurd
+ clean := yes
+ progtarg := $(targets)
+endif
+
+ifeq ($(makemode),utilities)
+ doinst := many
+ makemode-instdir := bin
+ clean := yes
+ progtarg := $(targets)
+endif
+
+ifeq ($(makemode),library)
+
+ linktarg := $(libname).so.$(hurd-version)
+
+ clean := yes
+ cleantarg := $(linktarg) $(addprefix $(libname),.a _p.a _pic.a \
+ .so .so.$(hurd-version))
+
+ targets := $(libname).a $(libname).so
+ ifneq ($(no_pic),t)
+ targets += $(libname)_pic.a
+ endif
+ ifneq ($(no_prof),t)
+ targets += $(libname)_p.a
+ endif
+
+ ifndef installhdrsubdir
+ installhdrsubdir = hurd
+ endif
+
+else
+
+ ifeq ($(makemode),misc)
+ ifndef doinst
+ doinst := many
+ endif
+ ifeq ($(doinst),one)
+ targets = $(target)
+ endif
+ ifeq (,$(installationdir))
+ ifneq (,$(targets))
+ ?Error subdir Makefile must define installationdir
+ else
+ makemode-instdir := NOINSTALL
+ endif
+ endif
+ else # server/utility modes
+ progtarg := $(filter-out $(special-targets),$(targets))
+ linktarg := $(progtarg) $(progtarg:=.static)
+ endif
+
+endif
+
+ifndef installationdir
+installationdir := $($(makemode-instdir)dir)
+endif
+
+ifeq ($(cleantarg),)
+ cleantarg := $(linktarg)
+endif
+
+
+# This is a hack to give all hurd utilities a default bug-reporting
+# address (defined in libhurdbugaddr/bugaddr.c).
+BUGADDR = $(..)libhurdbugaddr/libhurdbugaddr.a
+BUGADDR_REF = -uargp_program_bug_address
+
+# Standard targets
+
+.PHONY: all install libs relink dist-hook clean objs
+
+# Just build all the object files.
+objs: $(OBJS)
+ifneq ($(no_prof),t)
+objs: $(OBJS:%.o=%_p.o)
+endif
+ifeq ($(makemode),library)
+ifneq ($(no_pic),t)
+objs: $(OBJS:%.o=%_pic.o)
+endif
+endif
+
+# Installation
+ifneq ($(makemode),library)
+
+# not library
+installable := $(sort $(linktarg) $(targets))
+install-targets := $(targets) $(filter $(build-static:=.static),$(linktarg))
+all: $(install-targets)
+install: $(installationdir) $(addprefix $(installationdir)/,$(install-targets))
+$(addprefix $(installationdir)/,$(installable)): $(installationdir)/%: %
+ $(INSTALL_PROGRAM) $(INSTALL-$<-ops) $< $@
+else
+
+# library (several parts, library itself, headers, etc.)
+
+all: libs
+install libs: add-to-librecord
+add-to-librecord: $(targets)
+install: $(libdir) $(includedir)/$(installhdrsubdir) $(libdir)/$(libname).so.$(hurd-version) $(addprefix $(libdir)/,$(targets)) $(addprefix $(includedir)/$(installhdrsubdir)/,$(installhdrs))
+
+install-headers: $(includedir)/$(installhdrsubdir) $(addprefix $(includedir)/$(installhdrsubdir)/,$(installhdrs))
+
+$(includedir)/$(installhdrsubdir): $(includedir)
+ @$(MKINSTALLDIRS) $@
+
+# Arrange to have the headers installed locally anytime we build the library.
+# Not quite perfect, but at least it does end up getting done; and once done
+# it never needs to be repeated for a particular header.
+local-installhdrsubdir = include/$(installhdrsubdir)
+INSTALLED_LOCAL_HEADERS := $(installhdrs:%=../$(local-installhdrsubdir)/%)
+$(INSTALLED_LOCAL_HEADERS): ../$(local-installhdrsubdir)/%: Makefile
+ @rm -f $@
+ @test -d $(@D)/ || $(MKINSTALLDIRS) $(@D)
+ echo '#include "../$(dir)/$*"' > $@
+libs: $(INSTALLED_LOCAL_HEADERS)
+# Make sure we make those before compiling, since -MG will be unhelpful.
+$(patsubst %.o,%.d,$(filter %.o,$(OBJS))): $(INSTALLED_LOCAL_HEADERS)
+
+# The installed local headers referring to our own files will use
+# relative names with ../$(dir) and make won't notice that's us.
+../$(dir)/%: % ;
+
+$(addprefix $(libdir)/$(libname),_p.a .a _pic.a): $(libdir)/%: %
+ $(INSTALL_DATA) $< $@
+ $(RANLIB) $@
+
+$(libdir)/$(libname).so.$(hurd-version): $(libname).so.$(hurd-version)
+ $(INSTALL_DATA) $< $@
+
+$(libdir)/$(libname).so: $(libdir)/$(libname).so.$(hurd-version)
+ ln -f -s $(<F) $@
+
+$(addprefix $(includedir)/$(installhdrsubdir)/,$(installhdrs)): $(includedir)/$(installhdrsubdir)/%: %
+ $(INSTALL_DATA) $< $@
+
+# Arrange to have the shared libraries available locally in one single
+# directory. This is not used by the build system itself, but is just for easy
+# testing.
+local-libdir = lib
+../$(local-libdir)/$(libname).so.$(hurd-version): $(libname).so.$(hurd-version)
+ @test -d $(@D)/ || $(MKINSTALLDIRS) $(@D)
+ ln -sf ../$(dir)/$< $@
+libs: ../$(local-libdir)/$(libname).so.$(hurd-version)
+
+endif
+
+# Provide default.
+install:
+install-headers:
+
+# Making installation directories
+$(installationdirlist): %:
+ @$(MKINSTALLDIRS) $@
+
+# Building the target
+ifneq ($(makemode),misc)
+
+ifeq ($(doinst),one)
+$(linktarg): $(OBJS) $(OTHERLIBS) $(library_deps)
+endif
+
+# Determine which sort of library we should link against from whether -static
+# is used in LDFLAGS.
+__libext=.so
+__libext-static=.a
+_libext=$(__libext$(findstring -static,$(LDFLAGS) $($*-LDFLAGS)))
+
+libsubst=$(basename ${lib})$(_libext)
+libsubst-override=${$(notdir $(basename ${lib}))-libsubst}
+_libsubst=${libsubst$(patsubst %,-override,${libsubst-override})}
+
+# Direct the linker where to find shared objects specified in the
+# dependencies of other shared objects it encounters.
+rpath := -Wl,-rpath-link=.:$(subst $. ,:,$(dir $(wildcard ../lib*/lib*.so)))
+
+# Main rule to link executables
+#
+# (prof-depend is a special kind of run not normally used; see the rules
+# below for %.prof_d which uses it.)
+ifeq ($(prof-depend),)
+
+define link-executable
+$(CC) $(rpath) $(CFLAGS) $($*-CFLAGS) $(LDFLAGS) $($*-LDFLAGS) \
+ $(BUGADDR_REF) \
+ -o $@
+endef
+$(progtarg): %$(target-suffix): $(BUGADDR)
+ $(link-executable) \
+ $(filter %.o,$^) \
+ '-Wl,-(' $(foreach lib,$(filter-out %.o,$^),${_libsubst}) \
+ $($*-LDLIBS) $(LDLIBS) \
+ '-Wl,-)'
+
+$(addsuffix .static,$(progtarg)): %$(target-suffix).static: $(BUGADDR)
+ $(link-executable) -static \
+ '-Wl,-(' $(patsubst %.so,%.a,$^) $($*-LDLIBS) $(LDLIBS) \
+ '-Wl,-)' \
+ $(and $(filter %/libstore_part.a,$^), $(PARTED_LIBS))
+endif
+
+# Just like above, but tell how to make .prof versions of programs.
+$(addsuffix .prof,$(progtarg)): %$(target-suffix).prof: $(BUGADDR)
+ $(CC) -pg $(CFLAGS) $($*-CFLAGS) $(LDFLAGS) $($*-LDFLAGS) \
+ $(BUGADDR_REF) -static \
+ -o $@ \
+ '-Wl,-(' $^ $($*-LDLIBS) $(LDLIBS) \
+ '-Wl,-)'
+
+ifeq ($(makemode),library)
+$(libname).a: $(OBJS)
+ rm -f $(libname).a
+ $(AR) r $@ $^
+ $(RANLIB) $@
+
+$(libname)_p.a: $(patsubst %.o,%_p.o,$(OBJS))
+ rm -f $(libname)_p.a
+ $(AR) r $@ $^
+ $(RANLIB) $@
+
+$(libname)_pic.a: $(patsubst %.o,%_pic.o,$(OBJS))
+ rm -f $(libname)_pic.a
+ $(AR) r $@ $^
+ $(RANLIB) $@
+
+# The shared object needs to be findable in the build directory as
+# libfoo.so.VERSION (i.e. its soname) so that ld finds it when looking
+# for dependencies of other shared libraries.
+# But we also need the libfoo.so name that -lfoo looks for, so
+# we make that a symlink.
+$(libname).so.$(hurd-version): $(patsubst %.o,%_pic.o,$(OBJS)) $(library_deps)
+ $(CC) -shared -Wl,-soname=$@ -o $@ \
+ $(rpath) $(CFLAGS) $(LDFLAGS) $($(libname).so-LDFLAGS) \
+ '-Wl,-(' $(filter-out %.map,$^) \
+ $($(libname).so-LDLIBS) $(LDLIBS) \
+ '-Wl,-)' $(filter %.map,$^)
+
+$(libname).so: $(libname).so.$(hurd-version)
+ ln -f -s $< $@
+endif
+
+# Providing directory dependencies
+ifneq ($(makemode),library)
+hurd-bug-addr-dir-dep = libhurdbugaddr
+endif
+
+endif # makemode != misc
+
+directory-depend: $(..)$(dir).d
+$(..)$(dir).d: $(srcdir)/Makefile
+ rm -f $@
+ echo $(dir): $(hurd-bug-addr-dir-dep) $(addprefix lib,$(HURDLIBS)) > $@
+
+# TAGS files
+ifneq ($(dir),.)
+ifdef configured
+ifneq ($(OBJS:.o=.d),)
+DEP_SRCS = sed -e 's/^.*://' -e 's/ \\$$//' | tr ' ' '\012'| \
+ sed -n -e 's@^$(srcdir)@&@p' -e 's@^[^/]@&@p' | sort -ur
+TAGSFILES=$(OBJS:.o=.d) $(OTHERTAGS)
+else
+TAGSFILES=$(OTHERTAGS)
+endif
+else
+TAGSFILES=$(SRCS) $(OTHERTAGS)
+endif
+
+TAGS: $(TAGSFILES)
+ifeq ($(strip ($(TAGSFILES))),)
+# no tags, but parent will include this file, so make empty one.
+ > $@
+else
+ifdef DEP_SRCS
+ cat $(OBJS:.o=.d) | $(DEP_SRCS) | etags -o $@ - $(OTHERTAGS)
+else
+ etags -o $@ $^
+endif
+endif
+endif
+
+.PHONY: dist-hook
+ifdef DISTFILES
+dist-hook: dist.tar
+else
+# Don't bother creating an empty tarball.
+dist-hook:
+endif
+
+# FORCE is needed as $(dist-version) can change between two invocations.
+dist.tar: $(DISTFILES) FORCE
+ @[ x$(dist-version) != x ] || \
+ { echo >&2 Can\''t make $@ without dist-version set.' && \
+ false; }
+ tar -c -f $@ --files-from=/dev/null
+# Every file from $(DISTFILES) can exist either in the build directory or in
+# the source directory, but that must not affect the name it gets in dist.tar.
+ for f in $(DISTFILES); do \
+ if test -e "$$f"; then d=.; else d=$(srcdir); fi && \
+ if test "$(dir)" = "."; then subdir=""; else subdir="$(dir)/"; fi && \
+ tar --append -f $@ --owner=0 --group=0 \
+ --transform="s%^%$(dist-version)/$$subdir%" -C "$$d" "$$f" \
+ || exit $$?; \
+ done
+
+# Cleaning
+clean:
+ rm -f dist.tar
+ifeq ($(clean),yes)
+ rm -f *.d *.o *Server.c *User.c *_S.h *_U.h *.[su]defsi \
+ $(cleantarg)
+endif
+
+relink:
+ifeq ($(clean),yes)
+ rm -f $(linktarg)
+endif
+
+# Subdependencies
+
+# We record which libraries have been built in this run in the file
+# $(librecord). That file contains a series of lines like
+# `../libfoo/libfoo.a ../libfoo/libfoo.so: ; /bin/true'
+# that serve to inhibit the pattern rule which follows from doing anything.
+# Above, when we make `libs' in library directories, we always append
+# to $(librecord), so that future make invocations don't bother repeating
+# the effort.
+
+# if this is the first level, then set librecord. Otherwise, read it in.
+#ifeq ($(MAKELEVEL),0)
+#librecord:=/tmp/hurd-make-$(shell echo $$$$)
+#export librecord
+#else
+#include $(librecord)
+#endif
+
+# How to create it.
+#$(librecord):
+# touch $(librecord)
+
+# `libs' target depends on this.
+#add-to-librecord:
+# echo $(addprefix ../$(dir)/,$(targets)) : \; /bin/true >> $(librecord)
+
+# Building libraries from other directories. We force both libraries to be
+# built if either is, because it will use the appropriate one even if the other
+# is specified in someone's dependency list.
+#../%.a ../%.so: FORCE
+# $(MAKE) -C $(dir $@) libs
+
+# Tell make where to find other -l libraries that we use
+vpath libutil.% $(libdir)/
+
+# The libstore_%.a files fetch symbols from libstore.so
+ifneq ($(dir),libstore)
+$(boot-store-types:%=../libstore/libstore_%.a): ../libstore/libstore.so
+endif
+
+# Default rules to build PIC object files.
+%_pic.o: %.c
+ $(COMPILE.c) $< -DPIC -fPIC -o $@
+
+%_pic.o: %.S
+ $(COMPILE.S) $< -DPIC -o $@
+
+# Default rules to build profiled object files.
+%_p.o: %.c
+ $(COMPILE.c) $< -DPROF -pg -o $@
+
+%_p.o: %.S
+ $(COMPILE.S) $< -DPROF -o $@
+
+# How to build RPC stubs
+
+# We always need this setting, because libc does not include the bogus names.
+MIGCOMFLAGS := -subrprefix __
+
+# User settable variables:
+# mig-sheader-prefix prepend to foo_S.h for name of foo.defs stub header
+# MIGSFLAGS flags to CPP when building server stubs and headers
+# foo-MIGSFLAGS same, but only for interface `foo'
+# MIGCOMSFLAGS flags to MiG when building server stubs and headers
+# foo-MIGCOMSFLAGS same, but only for interface `foo'
+# MIGUFLAGS flags to CPP when building user stubs and headers
+# foo-MIGUFLAGS same, but only for interface `foo'
+# MIGCOMUFLAGS flags to MiG when building user stubs and headers
+# foo-MIGCOMUFLAGS same, but only for interface `foo'
+# CPPFLAGS flags to CPP
+
+# Implicit rules for building server and user stubs from mig .defs files.
+
+# These chained rules could be (and used to be) single rules using pipes.
+# But it's convenient to be able to explicitly make the intermediate
+# files when you want to deal with a problem in the MiG stub generator.
+
+%.sdefsi %.sdefs.d: %.defs
+ $(CPP) $(CPPFLAGS) $(MIGSFLAGS) $($*-MIGSFLAGS) -DSERVERPREFIX=S_ \
+ -MD -MF $*.sdefs.d.new \
+ $< -o $*.sdefsi
+ sed -e 's/[^:]*:/$*Server.c $(mig-sheader-prefix)$*_S.h:/' \
+ < $*.sdefs.d.new > $*.sdefs.d
+ rm $*.sdefs.d.new
+
+$(mig-sheader-prefix)%_S.h %Server.c: %.sdefsi
+ $(MIGCOM) $(MIGCOMFLAGS) $(MIGCOMSFLAGS) $($*-MIGCOMSFLAGS) \
+ -sheader $(mig-sheader-prefix)$*_S.h -server $*Server.c \
+ -user /dev/null -header /dev/null < $<
+
+%.udefsi %.udefs.d: %.defs
+ $(CPP) $(CPPFLAGS) $(MIGUFLAGS) $($*-MIGUFLAGS) \
+ -MD -MF $*.udefs.d.new \
+ $< -o $*.udefsi
+ sed -e 's/[^:]*:/$*User.c $*_U.h:/' \
+ < $*.udefs.d.new > $*.udefs.d
+ rm $*.udefs.d.new
+
+%_U.h %User.c: %.udefsi
+ $(MIGCOM) $(MIGCOMFLAGS) $(MIGCOMUFLAGS) $($*-MIGCOMUFLAGS) < $< \
+ -user $*User.c -server /dev/null -header $*_U.h
+
+# Where to find .defs files.
+vpath %.defs $(top_srcdir)/hurd
+
+# These we want to find in the libc include directory...
+mach_defs_names = bootstrap exc mach mach4 \
+ mach_host mach_port mach_timer_reply memory_object \
+ memory_object_default notify
+device_defs_names = dev_forward device device_reply device_request
+
+mach_defs = $(addsuffix .defs,$(mach_defs_names))
+device_defs = $(addsuffix .defs,$(device_defs_names))
+
+$(mach_defs): %.defs:
+ echo '#include <mach/$@>' > $@
+$(device_defs): %.defs:
+ echo '#include <device/$@>' > $@
+
+
+FORCE:
+
+
+# How to build automatic dependencies
+
+# Don't include dependencies if $(no_deps) is set; the master makefile
+# does this for clean and other such targets that don't need
+# dependencies. That then avoids rebuilding dependencies.
+
+ifneq ($(no_deps),t)
+
+# The MIG stubs depend on their definition files.
+# These lines assume that every Makefile that uses a foo_S.h or foo_U.h file
+# also mentions the associated fooServer.o or fooUser.o file.
+-include $(subst Server.o,.sdefs.d,$(filter %Server.o,$(OBJS))) /dev/null
+-include $(subst User.o,.udefs.d,$(filter %User.o,$(OBJS))) /dev/null
+
+ifneq ($(prof-depend),t)
+ifneq ($(no_prof),t)
+-include $(addsuffix .prof_d,$(progtarg)) /dev/null
+endif
+endif
+
+# For each .o file we need a .d file.
+-include $(subst .o,.d,$(filter %.o,$(OBJS))) /dev/null
+
+endif
+
+# Here is how to build those dependency files
+
+%.prof_d: $(srcdir)/Makefile
+ $(MAKE) $* prof-depend=t
+
+ifeq ($(prof-depend),t)
+$(progtarg): %: FORCE
+ rm -f $@.prof_d
+ echo $@.prof: $(subst .so,_p.a,$(subst .o,_p.o,$(filter-out FORCE,$+))) > $@.prof_d
+endif
+
+define make-deps
+set -e; $(CC) $(CFLAGS) $(CPPFLAGS) -M -MG $< | \
+sed > $@.new -e 's%$*\.o:%$*.o $*_pic.o $*_p.o $@: $($*-DEPS)%' \
+ -e 's% [^ ]*/gcc-lib/[^ ]*\.h%%g'
+mv -f $@.new $@
+endef
+
+# Here is how to make .d files from .c files
+%.d: %.c; $(make-deps)
+# Here is how to make .d files from .S files
+%.d: %.S; $(make-deps)
+
+# .s files don't go through the preprocessor, so we do this
+# This rule must come *after* the genuine ones above, so that
+# make doesn't build a .s file and then make an empty dependency
+# list.
+%.d: %.s
+ echo '$*.o: $<' > $@
+
+# Rule to make executable shell scripts from .sh files.
+%: %.sh $(..)config.make
+ sed -e 's/STANDARD_HURD_VERSION_\(.[^_]*\)_/\1 (GNU Hurd) $(package-version)/' < $< > $@
+ chmod +x $@
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..106f2f60
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,268 @@
+#
+# Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2001, 2002, 2004,
+# 2006, 2009, 2011, 2012, 2013 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := .
+makemode := misc
+
+DISTFILES := configure
+
+include ./Makeconf
+
+## Subdirectories of this directory should all be mentioned here
+
+# Hurd libraries
+lib-subdirs = libshouldbeinlibc libihash libiohelp libports libthreads \
+ libpager libfshelp libdiskfs libtrivfs libps \
+ libnetfs libpipe libstore libhurdbugaddr libftpconn libcons
+
+# Hurd programs
+prog-subdirs = auth proc exec init term \
+ ext2fs isofs tmpfs fatfs \
+ storeio pflocal pfinet defpager mach-defpager \
+ login daemons boot console \
+ hostmux usermux ftpfs trans \
+ console-client utils sutils \
+ benchmarks fstests
+
+ifeq ($(HAVE_SUN_RPC),yes)
+prog-subdirs += nfs nfsd
+endif
+
+# Other directories
+other-subdirs = hurd doc config release include
+
+# All the subdirectories together
+subdirs = $(lib-subdirs) $(prog-subdirs) $(other-subdirs)
+
+# This allows the creation of a file BROKEN in any of the prog-subdirs;
+# that will prevent this top level Makefile from attempting to make it.
+working-prog-subdirs := $(filter-out \
+ $(patsubst %/,%,\
+ $(dir $(wildcard $(prog-subdirs:=/BROKEN)))),\
+ $(prog-subdirs))
+
+
+$(subdirs): version.h
+
+## GNU Coding Standards targets (not all are here yet), and some other
+## similar sorts of things
+
+all: $(lib-subdirs) $(working-prog-subdirs)
+
+# Create a distribution tar file.
+
+git_describe := git describe --match '*release*'
+dist-version := $(shell cd $(top_srcdir)/ && $(git_describe))
+
+.PHONY: dist
+ifdef configured
+dist: $(foreach Z,bz2 gz,$(dist-version).tar.$(Z))
+else
+dist:
+ @echo >&2 'Cannot build a distribution from an unconfigured tree.'
+ false
+endif
+
+HEAD.tar: FORCE
+ cd $(top_srcdir)/ && git status --short \
+ | $(AWK) '{ print; rc=1 } END { exit rc }' \
+ || { echo >&2 \
+ 'Refusing to build a distribution from dirty sources.' && \
+ false; }
+ (cd $(top_srcdir)/ && git archive --prefix=$(dist-version)/ HEAD) > $@
+
+ChangeLog.tar: gen-ChangeLog
+ tar -c -f $@ --owner=0 --group=0 \
+ --transform='s%^%$(dist-version)/%' $(ChangeLog_files)
+
+gen_start_commit = 2772f5c6a6a51cf946fd95bf6ffe254273157a21
+ChangeLog_files = \
+ ChangeLog \
+ auth/ChangeLog \
+ benchmarks/ChangeLog \
+ boot/ChangeLog \
+ config/ChangeLog \
+ console-client/ChangeLog \
+ console/ChangeLog \
+ daemons/ChangeLog \
+ defpager/ChangeLog \
+ doc/ChangeLog \
+ exec/ChangeLog \
+ ext2fs/ChangeLog \
+ fatfs/ChangeLog \
+ fstests/ChangeLog \
+ ftpfs/ChangeLog \
+ hostmux/ChangeLog \
+ hurd/ChangeLog \
+ include/ChangeLog \
+ init/ChangeLog \
+ isofs/ChangeLog \
+ libcons/ChangeLog \
+ libdirmgt/ChangeLog \
+ libdiskfs/ChangeLog \
+ libfshelp/ChangeLog \
+ libftpconn/ChangeLog \
+ libhurdbugaddr/ChangeLog \
+ libihash/ChangeLog \
+ libiohelp/ChangeLog \
+ libnetfs/ChangeLog \
+ libpager/ChangeLog \
+ libpipe/ChangeLog \
+ libports/ChangeLog \
+ libps/ChangeLog \
+ libshouldbeinlibc/ChangeLog \
+ libstore/ChangeLog \
+ libthreads/ChangeLog \
+ libtrivfs/ChangeLog \
+ login/ChangeLog \
+ mach-defpager/ChangeLog \
+ nfs/ChangeLog \
+ nfsd/ChangeLog \
+ pfinet/ChangeLog \
+ pflocal/ChangeLog \
+ proc/ChangeLog \
+ release/ChangeLog \
+ storeio/ChangeLog \
+ sutils/ChangeLog \
+ term/ChangeLog \
+ tmpfs/ChangeLog \
+ trans/ChangeLog \
+ usermux/ChangeLog \
+ utils/ChangeLog
+distdir = .
+.PHONY: gen-ChangeLog
+gen-ChangeLog:
+ $(AM_V_GEN)if test -d $(top_srcdir)/.git; then \
+ (cd $(top_srcdir)/ && \
+ ./gitlog-to-changelog --strip-tab \
+ $(gen_start_commit).. && \
+ echo) >> $(distdir)/cl-t && \
+ for f in $(ChangeLog_files); do \
+ (cd $(top_srcdir)/ && \
+ git show $(gen_start_commit):$$f) >> $(distdir)/cl-t && \
+ rm -f $(distdir)/$$f && \
+ mv $(distdir)/cl-t $(distdir)/$$f \
+ || exit $$?; \
+ done; \
+ fi
+
+$(dist-version).tar: HEAD.tar $(addsuffix /dist-hook,hurd/.. $(subdirs)) ChangeLog.tar
+ tar -c -f $@ --files-from=/dev/null
+# Concatenate the tar files. Have
+# to do it one by one: <http://savannah.gnu.org/patch/?7757>.
+ for f in HEAD.tar dist.tar */dist.tar ChangeLog.tar; do \
+ tar -v --concatenate -f $@ "$$f"; \
+ done
+
+clean: $(addsuffix -clean,$(subdirs)) clean-misc
+
+relink: $(addsuffix -relink,$(lib-subdirs) $(prog-subdirs))
+
+objs: $(addsuffix -objs,$(lib-subdirs) $(prog-subdirs))
+
+install: $(addsuffix -install,$(lib-subdirs) $(working-prog-subdirs) \
+ $(other-subdirs))
+
+install-headers: $(addsuffix -install-headers,$(lib-subdirs) \
+ $(working-prog-subdirs)\
+ $(other-subdirs))
+
+TAGS: $(addsuffix -TAGS,$(working-prog-subdirs) $(lib-subdirs))
+ etags -o $@ $(patsubst %-TAGS,-i %/TAGS,$^)
+
+%.bz2: %
+ bzip2 -9 < $< > $@
+
+%.gz: %
+ gzip -9n < $< > $@
+
+## Targets used by the main targets above.
+$(prog-subdirs) $(lib-subdirs): FORCE
+ $(MAKE) -C $@ all
+FORCE:
+
+%-clean:
+ $(MAKE) -C $* clean no_deps=t
+
+%-relink:
+ $(MAKE) -C $* relink no_deps=t
+
+%-objs:
+ $(MAKE) -C $* objs
+
+%-install:
+ $(MAKE) -C $* install
+
+%-install-headers:
+ $(MAKE) -C $* install-headers
+
+%-TAGS:
+ $(MAKE) -C $* TAGS no_deps=t
+
+.PHONY: %/dist-hook
+%/dist-hook:
+ $(MAKE) -C $* dist-hook no_deps=t dist-version=$(dist-version)
+
+.PHONY: clean-misc distclean
+clean-misc:
+ rm -f HEAD.tar ChangeLog.tar $(ChangeLog_files)
+
+distclean: clean
+ rm -f config.make config.log config.status config.cache
+ifneq (.,${srcdir})
+ rm -f Makefile
+endif
+
+
+## Directory dependencies
+#
+# Some directories depend on others, so we need to find out exactly
+# what they are. This does that for us.
+
+ifneq ($(no_deps),t)
+-include $(addsuffix .d,$(subdirs))
+endif
+
+# How to build them
+$(addsuffix .d,$(subdirs)): %.d: $(top_srcdir)/%/Makefile
+ $(MAKE) -C $* directory-depend no_deps=t
+
+
+## Build system
+
+AUTOCONF = autoconf
+AUTOCONF_FLAGS = -I $(top_srcdir)
+
+$(top_srcdir)/configure: $(top_srcdir)/configure.ac $(top_srcdir)/aclocal.m4
+ $(AUTOCONF) $(AUTOCONF_FLAGS) $< > $@
+ chmod +x $@
+
+config.status: $(top_srcdir)/configure
+ $(SHELL) $@ --recheck
+
+config.make: config.status $(top_srcdir)/config.make.in
+# No stamp file is used here, as config.make's timestamp changing will not have
+# any far-reaching consequences.
+ $(SHELL) $< --file=$@
+
+version.h: stamp-version; @:
+stamp-version: version.h.in config.make
+ sed -e 's/MASTER_HURD_VERSION/\"$(package-version)\"/' \
+ < $< > version.h.new
+ $(move-if-change) version.h.new version.h
+ touch $@
diff --git a/NEWS b/NEWS
new file mode 100644
index 00000000..72a2e2d5
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,270 @@
+2013-09-27
+Version 0.5
+
+Really too many changes have been applied to list them individually. Some
+highlights include:
+
+Numerous bug fixes, compatibility fixes (for compliance to standards such as
+POSIX), and stability fixes.
+
+Thread-local storage (TLS) support in the threading libraries. Conversion from
+cthreads to POSIX Threads (pthreads).
+
+Large store support for ext2fs.
+
+A mtab translator.
+
+IPv6 support in pfinet, based on Linux 2.2.14.
+
+A XKB parser and wide character support for the Hurd console.
+
+The new remap translator can be used to create an environment in which
+some paths are redirected, for instance the TCP/IP stack, or /bin/sh.
+
+The new nullauth utility drops all authentication credentials before running a
+program. This is also useful to drop privileges on behalf of translators that
+do not need any credentials.
+
+Several improvements to rpctrace, such as the -E option to add/change/unset
+environment variables among the ones inherited by the process.
+
+Improvements to the default pager and tmpfs virtual filesystem generally, and
+for the latter also add the possibility to specify the size with the --size
+parameter.
+
+The UFS filesystem translator and supporting utilities have been removed:
+unused/untested/unmaintained for a decade.
+
+Follows a list assembled in 2002, more than ten years ago:
+
+Many, many, many bugs have been fixed, including some resource
+consumption problems and race conditions. The system can compile
+itself as well as most external applications. The amount of stress
+the system can tolerate has increased a lot.
+
+The new ftpfs translator can be used to transparently access FTP
+servers through the filesystem. Together with the new hostmux
+translator, which can invoke host-specific translators like ftpfs,
+this leads to a very comfortable way to use FTP archives on the Hurd.
+
+A new translator usermux can invoke user-specific translators. It can
+be used to provide a symlink to all users home directories in a single
+place (similar to amd/automount).
+
+The tmpfs translator can be used to store files and directories in RAM
+without any overhead of a real filesystem. The amount of memory
+allocated equals the amount of space occupied, and an upper limit can
+be defined.
+
+Shadow passwords are now supported. If the password field in
+/etc/passwd contains the string "x", the crypted password is looked up
+in /etc/shadow instead. A new password server distributes auth ports
+after password verification.
+
+The pfinet server uses the Linux 2.2.12 IP stack now. It supports the
+new interface types dummy and tun which provide a bottomless sink and
+IP tunnel interface respectively. It also supports all common network
+ioctls now.
+
+The Hurd can now access CD-ROMs containing an ISO9660 file format with
+the new iso9660fs translator. Rock Ridge extensions are supported, as
+well as some GNU extensions to store GNU-specific filesystem attributes.
+
+Two new translators, hello and hello-mt, illustrate how to implement
+simple trivfs based filesystems in the Hurd.
+
+A new utility `rpctrace' aids in debugging the Hurd by listing all
+invoked RPCs with message id and port right information.
+
+The Hurd does no longer contain the utility `su', which is included in
+the GNU shellutils package. `setauth --save' can also be used as a
+replacement.
+
+The init script mechanism became more flexible, as it was splitted
+into several files. This allows to plug in different styles of init
+procedures by just hooking them into /libexec/runsystem.
+
+The ext2fs server understands the sparse_super filesystem option, and
+can deal with the filetype option (but won't make use of this itself).
+
+Several utilities have new options. All store based filesystems can
+be configured with --no-suid, --no-exec, --bootflags, --boot-command
+and --no-cache. The null translator also can be full now (--full).
+The magic translator can provide an empty directory.
+
+The crash server supports several modes of operation, it can suspend
+or kill a crashing task. Core dumping is planned for but not
+implemented yet. Users can set their own crash server using an
+environment variable.
+
+The new fakeroot translator appears to give transparent access to the
+underlying directory node. However, all accesses are made using the
+credentials of the translator regardless of the client and the
+translator fakes success for chown and chmod operations, reporting the
+faked IDs and modes in later stat calls.
+
+The new fakeauth program runs a command with a fake authentication
+handle that claims to be root or any arbitrary identity derived from
+that handle, but in fact is always just a proxy for your real
+authentication handle.
+
+The features provided by the fakeroot translator and fakeauth are
+combined in the new fakeroot program.
+
+The proc server can show the start time of processes.
+
+* sync -> syncfs, takes args
+
+* Default pager (serverboot) understands Linux (mkswap) signature pages.
+ New boot script function $(add-raw-paging-file) inhibits looking for the
+ signature page. New function $(add-linux-paging-file) checks for the
+ page and refuses to use the swap partition or paging file if there is no
+ valid Linux signature page. A stand-alone default pager `mach-defpager'
+ is implemented which supports the same features. With
+ "die $(serverboot)"
+ in the boot script, serverboot doesn't become the default pager.
+
+* libstore uses off64_t internally, allowing large stores to be
+ handled properly. The zero store supports suffix b, k/K, m/M, g/G
+ for blocks, kilo-, mega- and gigabytes resp. when specifying the
+ store size. The remap store understands N+ as the end of the store
+ starting from block N. A new store type `bunzip2' uncompresses
+ stores in the bzip2 format into memory.
+
+* msgport, ping
+
+The Hurd interfaces have been changed to support large files.
+
+The new proxy-defpager translator grants unprivileged users access to
+the default pager.
+
+1997-06-09
+Version 0.2
+
+Summary of important externally visible changes since version 0.1:
+
+Many, many bugs have been fixed, including some resource consumption
+problems. The system can now compile itself, and stay running the
+whole time. It is quite reliable, and is used here for regular
+computing tasks now.
+
+New programs addauth, rmauth, unsu, su, and setauth modify the uid
+sets of running programs. Using addauth you can add root to your
+emacs, write a file, and then use rmauth to take the uid back. (Of
+course, passwords are required when necessary.) New program `ids'
+will tell you what all the user ids are that a program has. Note that
+in the Hurd a program can have several user ids all at once, just like
+Unix supports having several group ids. Now that you can dynamically
+change the ids of running programs, system administration (among other
+things) becomes much easier.
+
+Two new programs, ftpcp and ftpdir, can be used to fetch files using
+FTP with a simple command line interface.
+
+We have moved the `serverboot' bootstrapper and default pager from
+Mach into the Hurd source distribution, so that Mach can avoid
+distributing user programs. We hope to phase out use of serverboot
+entirely at some point. Along with this, we made serverboot much
+quieter.
+
+We now build profiling versions of the Hurd libraries, and the
+makefiles have targets to build profiling executables as desired.
+
+We have added `lmail', a /bin/mail replacement for use with sendmail
+to do local mail delivery.
+
+The idle threads of the kernel no longer count in `ps' towards the
+total run time of the kernel process. This means that the kernel will
+now sort into the right location for `u' format output.
+
+Several oddities in NFS client behavior have been fixed to produce
+more sensible results.
+
+The "loopback" device is now supported (automatically) inside pfinet.
+It should now be possible to use pfinet with no ethernet at all.
+
+Filesystems now use `store' library; the contents of the filesystem
+can be nearly anywhere (including another file somewhere).
+
+Some annoying bugs in tty behavior have been fixed. Some still
+remain, unfortunately.
+
+New translator `firmlink' is like a cross between a symlink and a hard
+link.
+
+
+Programmer visible changes:
+
+Filesystems support a new "reparenting" feature. This makes `..' from
+the "reparented" node return whatever the user wishes, for a given
+`open' regime. This enables the "firmlink" translator to work, and
+will also be important in implementing shadow filesystems.
+
+The fshelp library now managed UID sets itself, and contains functions
+to do permission checks in the usual way. Now it's much easier to be
+sure every filesystem implements the same permission checks.
+
+The trivfs library has had its static initializations replaced with
+spiffy dynamic ones.
+
+New library `libftpconn' is used to manage FTP connections. We are
+already using this library for some local system administration tasks,
+and are making it the basis of the (as yet unfinished) ftpfs
+filesystem for the Hurd.
+
+1996-09-06
+Version 0.1.
+
+Summary of important externally visible changes since version 0.0:
+
+Many miscellaneous bugs have been fixed.
+
+Missing source files in 0.0 have been correctly added to the
+distribution.
+
+Configuration now knows that various 386 equivalents use the same
+assembly files.
+
+There is now an nfsd; it has not be well-tested. It would be nice if
+some people would run it and try things out. It does not yet support
+Hurd-specific features.
+
+Exec now compiles correctly even when you don't have BFD installed.
+
+Ext2fs format interpretation problems have been fixed. This means it
+can be suitably used to boot with.
+
+init now properly understands SIGHUP and SIGTERM, and does the right
+thing correctly.
+
+Much code has been written in nfs towards supporting version 3 of the
+protocol, but it's not done yet. Don't try and turn it on.
+
+Version information in uname is now calculated differently.
+
+Some improvements have been made in the SETUP script.
+
+sync, reboot, and halt now do argument parsing and understand --help
+and --version.
+
+The new `e2os' program has been added to change the "creator OS" field
+on an ext2fs filesystem.
+
+Bugs in term and libtrivfs have been fixed, allowing emacs shell mode
+to work cleanly. Other minor bugs have also been fixed.
+
+ext2fs now does directory search rotoring to speed repeated and
+sequential directory lookups.
+
+A new program `vminfo' prints the virtual memory map of task.
+
+All disk filesystems (ext2fs) now do directory name caching of
+`..' which was not previously done. In addition, you can now set the
+cache size to be large, and then do experiments and see what the cache
+hit rate would have been for various smaller sizes. This should help
+in optimizing the size of the cache.
+
+1996-08-06
+Version 0.0
+
+Initial release.
diff --git a/README b/README
new file mode 100644
index 00000000..72955e31
--- /dev/null
+++ b/README
@@ -0,0 +1,52 @@
+This is the GNU Hurd, <http://www.gnu.org/software/hurd/>. Welcome.
+
+GNU Hurd runs on 32-bit x86 machines. A version running on 64-bit x86
+(x86_64) machines is in progress. Volunteers interested in ports to
+other architectures are sought; please contact us (see below) if you'd
+like to help.
+
+To compile the Hurd, you need a toolchain configured to target i?86-gnu;
+you cannot use a toolchain targeting GNU/Linux. Also note that you
+cannot run the Hurd "in isolation": you'll need to add further components
+such as the GNU C Library (glibc), to turn it into a runnable system.
+
+Recent versions of Mach, MIG, glibc, and GCC are required. Optionally, a Sun
+RPC implementation is needed to build the NFS translator and daemon:
+
+glibc Configured with --enable-obsolete-rpc.
+TI-RPC Currently fails to build on GNU, see
+ <http://lists.debian.org/debian-hurd/2010/12/msg00007.html>.
+
+Obviously, you also need somewhat recent versions of binutils, make,
+bash and some other tools. No hard requirements are currently known
+for these, though.
+
+For instructions on compiling and installing the GNU Hurd from an
+already running Hurd system, see the file `INSTALL'.
+
+It is possible to cross-build the Hurd; the file INSTALL-cross
+contains some past instructions for doing so, but it's too much
+trouble to maintain these instructions and keep them up to date. Your
+best bet is to start with a running Hurd system already. If you do
+decide to cross compile, you will need to examine the instructions in
+INSTALL for building Mach, libc, and the Hurd together, and follow
+them. The instructions in INSTALL-cross are quite out-of-date, but
+they contain some useful hints buried amongst the errors, so we have
+left the file for those who find it useful.
+
+Please note that this directory also contains a fair amount of
+not-yet-working code. By default, the makefiles build only the
+working code.
+
+
+The GNU Hurd is free software. All of it, including the libraries in
+this distribution, is covered by the GNU General Public License, found
+in the file COPYING.
+
+
+Please read the FAQ at <http://www.gnu.org/software/hurd/faq.html>.
+Bug reports should be sent to <bug-hurd@gnu.org> or filed on
+<http://savannah.gnu.org/bugs/?group=hurd>. Requests for assistance
+should be sent to <help-hurd@gnu.org> or filed on
+<http://savannah.gnu.org/support/?group=hurd>. You can also find us on
+the Freenode IRC network in the #hurd channel.
diff --git a/TODO b/TODO
new file mode 100644
index 00000000..d2500dc3
--- /dev/null
+++ b/TODO
@@ -0,0 +1,284 @@
+-*- Mode: Outline -*-
+
+Before working on anything in this file, it's very important that you
+make contact with the core Hurd developers. Things herein might be
+slightly out of date or otherwise not easy to understand at first
+glance. So write to <bug-hurd@gnu.org> first.
+
+Priorities:
+ Check the end of this file for a task list referring to the next
+ public release.
+
+ Reported bugs generally have top priority.
+ Non-reported and non-encountered bugs (things we know don't work,
+ but don't really impede things) have lower priority.
+ Things in this file are ranked with one to three !; the more, the
+ higher priority.
+
+See `tasks', the exported task list.
+
+* Contents of =pending-changes.
+* Make sure all the pieces of the Hurd have adequate version stuff. !
+* fix root dependencies in filesystem, network, etc. !
+* Profile things !!
+* Write coding standards suggestions for Hurd
+* Implement file_fetch_dir
+* Conform to coding standards (esp. CFLAGS setting)
+* Internationalization !
+* Use hostid in /etc/rc and mention it in installation instructions,
+ when suitable sh-utils is released.
+* "user-friendly" naming scheme for /dev
+
+* Fix emacs/src/unexelf.c to deal with occasional lack of mmap !
+
+* Currently libshouldbeinlibc (idvec, etc) and password.defs and login
+ all think there is one user per-uid, and that ain't necessarily so.
+ Needs fixed. !
+
+* Libraries
+
+** general:
+*** implement all the pathconf things from Posix.1 right.
+*** implement an soversion and symbol-versioning scheme similar to libc's.
+
+** libc:
+*** !!!! Do these for libc 2.3 (ask Roland for details):
+**** nix hurdmalloc, use special arena with new _int_malloc
+**** TLS support
+**** avoid sbrk altogether in malloc (don't link it in at all for static)?
+**** maybe hack libio to use _hurd_fd_read et al directly as stdio used to
+*** (once vm_remap exists) implement mremap
+*** when a session leader _exits, it should SIGHUP, TIOCDRAIN, and revoke. !!!
+ [it is still under discussion exactly where to address this,
+ so don't do it in libc yet]
+*** Version of tmpfile that takes a directory
+*** flink
+*** make sure profiling works !!
+*** many functions that are syscalls in unix can potentially leak send rights
+ if a signal handler longjmp's out past them. They should probably all
+ be using HURD_CRITICAL_SECTION to avoid this. !!
+** items relocated from libc/hurd/STATUS:
+*** We are not sure about possible races between setpgrp (A, pgrp) from
+ process B vs process A receiving proc_newids.
+*** does sigaltstack/sigstack really work? -- NO
+*** hurdmalloc kludge
+*** think more about environment variable use viz security
+*** SIGINFO handler?
+*** itimers could be implemented better
+*** mmap cannot do MAP_NOEXTEND.
+*** unimplemented calls(?)
+ acct
+ getfh
+ getfsstat
+ madvise
+ mincore
+ mount
+ msync
+ sstk
+ swapon
+ unmount
+
+
+** libports:
+*** Get rid of general `uninibited-rpcs' mechanism in libports, & just
+ special-case interrupt_operation. !
+
+** libstore:
+*** Add lock protocol for mutable stores.
+*** Creating/opening stores with STORE_INACTIVE should work (and then the hack
+ in storeio/dev.c should be removed).
+
+** libfsserver/libnetfs:
+*** convert libnetfs to libfsserver
+
+** libnetfs
+*** Support --readonly, --writable, and --update !
+*** Implement dir_reparent.
+
+** libpager:
+*** Put user-defined fns into a callback struct passed to pager_create. !
+*** Make libpager paging interface able to read/write multiple pages at once.
+*** Remove pagers from portset if there are too many incoming requests to
+ avoid forking too many threads. !
+*** flush functions don't actually force pending delayed copies. (and in
+ fact, they seem to block if a delayed copy is wired down) !
+
+** libshouldbeinlibc:
+*** Merge contents of libshouldbeinlibc that belong there into libc.
+ Rename the rest to libhurdutil or somesuch.
+
+** libdiskfs
+*** file_chflags does not do proper permission checking (non-root isn't
+ supposed to be able to change the low bits)
+*** Add the short-circuited-but-not-builtin translator startup code from
+ dir-lookup to fsys_getroot. Compare and match carefully these two
+ routines and then share common code.
+*** Implement Posix O_SYNC, O_DSYNC, and O_RSYNC under the right
+ names; change libc header to match current Posix.
+*** Handle dead name notifications on execserver ports. !
+*** Deal correctly with setting the translators on /servers/exec.
+*** Implement file_notice_changes.
+*** Implement async I/O. !
+*** Think of a way to have when-to-sync-nodes be more flexible so it can
+ be done right for each format.
+*** Provide for MNT_NODEV, etc. !
+*** Implement io_restrict_auth correctly. !
+*** Use off_t correctly. !!
+*** Add a consistent message printing facility for filesystems to use
+ (syslog, but takes special care when the root file system?). !!
+*** Some of diskfs_readonly_changed in ext2fs should be in generic
+ routines. !
+
+** libfshelp
+*** Put functions here to deal with directory and file change notifications !!!
+*** Translator startup should provide a more useful stdin/stdout than nothing !
+
+** libtrivfs
+*** Allow for read/write/exec to be passed down.
+*** Implement file_exec when appropriate. !!
+*** Provide for the visible owner, etc., to be held in command-line args
+ instead of the underlying node, when it's important. !!
+
+** libps
+*** Add option to print values times with a delay,
+ and to print values relative to last printing.
+*** Whizzier columnation (autosizing?)
+*** Make getters more robust.
+
+* Servers
+** finish default pager in defpager !!
+** Implement goaway in all the servers that don't already have it. !!
+** (init) sleep on spinning gettys !
+** Add calls to various servers to return interesting statistical information.
+** Test new-fifo & make it fifo. !
+** Login/utmp?
+** fifos are flaky. ?? Details ??
+** implement io_revoke for things in trans/ that need it. !
+
+** pflocal
+*** make peer addresses work?
+*** implement io_revoke on sockets !
+
+** term:
+*** find a way to reduce duplicate signals from repeated VINTR input
+*** when a session leader exits, netbsd SIGHUP's the terminal, drains output,
+ and then revokes it. (See Posix.1-1996, p. 184, l. 64-68.)
+*** when a session leader sets the ospeed to 0, netbsd SIGHUP's the termianl.
+*** enforce session leader / controlling terminal uniqueness rules. !
+ (1: if a terminal is matched to a session, then another session
+ can't make it its controlling terminal;
+ 2: tcsetpgrp to a pgrp that's not in the right sessions must
+ fail;
+ 3: when a session leader exits, the association has to be torn
+ down; bsd does SIGHUP + drain + revoke.)
+
+** proc:
+*** Add a version of proc_wait usable by non-parent processes, and use it in
+ gdb; maybe just a flag WNOREAP to proc_wait that doesn't reap and allows
+ anyone to use it. !!
+*** Add proc_get_tty() [returns tty opened with no flags], so that ps can be
+ non-suid. !
+*** add timeouts to all the msg_* calls proc makes. !
+
+** pfinet
+*** Allow multiple pfinets to arp on the same ethernet interface for different
+ IP addresses. !!
+*** Diagnose why shutdown doesn't close TCP channels properly or reliably. !
+*** select for read on a UDP socket seems never to return. !!!
+*** Undefined functions at the end of pfinet/io-ops.c. !!
+*** Implement io_revoke on sockets. !
+*** Implement SO_LINGER correctly (close() should return EWOULDBLOCK if not all
+ packets could be delivered). !
+
+** nfs
+*** Implement async I/O !!
+*** Finish work to turn on paging. !!
+*** Implement TCP nfs. !
+*** Implement V3 nfs.
+*** Implement nqnfs. !
+*** Add Hurd-specific calls. !!
+*** errors in mount_root should get reflected more usefully to users. !
+
+** storeio:
+*** Make a server (/servers/storeio?) to share multiple storeio nodes
+*** Serverify, ala new-fifo.
+*** implement io_revoke !
+
+** ext2fs
+*** Try to write directories with 512-byte record boundaries. !!
+*** Maybe file_pager_write_page should be able to accurately reproduce holes
+*** Add byte-swapping. !!
+*** If the target of a symlink is the empty string, stat seems to spin forever !!!
+
+** arla -- port it
+
+
+* Utilities
+** Make id, et. al. work with no/multiple uids. !!!
+** Make things work with the `nobody' mode bits: chmod, ls, ... !
+** Make things work with filesystem extensions (author, etc.): ls!!!
+** Make things work better with translators, e.g., tar... !!!
+** Fix bash to turn on interrupts around syscalls more generally,
+ especially chdir. !!
+** talk doesn't work !!
+** login: Make --retry work correctly when invoked via a suid shell
+ script by other than root (it doesn't now if the script specifies
+ --retry="$0" because the exec server will use /dev/fd/N for name,
+ and child_lookup() doesn't supply more than fds 0-2). !!
+** Make w use utmp's tty instead of the process's
+
+** fsck:
+*** fsck should use fsys_get_options returned device instead of /etc/fstab !!
+
+** settrans:
+*** needs an option to make the active go away without using goaway. !
+
+** ps:
+*** ps should timeout quickly (one second?) on non-responsive message ports. !
+*** help displays for: stat letters, format specs.
+*** --match option?
+*** fetch and print task (and thread?) startup times.
+
+** gdb:
+*** Add various mach convenience features (vminfo, &c). !!
+*** Be even more vigilant about noticing new threads. In particular:
+**** For mach-indep thread commands, before validating against
+ internal thread list. !!!
+*** read core files !!
+*** Gdb doesn't work right if it doesn't have permission to frob the
+ inferior process (for instance, if it's a set[ug]id program), but
+ doesn't give a meaningful error message. Try `break main' in /bin/login.
+
+** nfsd
+*** Implement TCP nfs. !
+*** Implement V3 nfs. !
+*** Implement nqnfs. !
+*** Add Hurd-specific calls. !!
+*** writes don't seem to work properly !!!
+*** Report statfs correctly. !!
+
+* Mach:
+** Have the at-close actions on devices be more directly controllable
+ by users by deleting the current at-close actions and replacing
+ them with suitable device_set_status calls.
+** Specify and implement vm_remap.
+
+======
+
+???
+ [I'm not sure of the preconditions for this; it usually seems to happen to
+ new login shells (but not often enough to really get a handle on it).
+ If the command is repeated, then it usually works correctly.]
+ login> ps aux|head
+ bash: /bin/head: (ipc/send) invalid msg-header
+ Broken pipe
+ [bash only seems to print error messages in this particular format if an
+ execve fails]
+
+ I've also had it happen in shells that have been around a while. -thomas
+
+ I just saw it happen in make output; there the particular error
+ happens only when execvp fails. -thomas
+???
+
+* Next release:
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 00000000..8823289c
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,241 @@
+dnl These modifications are to allow for an empty cross compiler tree.
+dnl In the situation that cross-linking is impossible, the variable
+dnl `cross_linkable' will be substituted with "yes".
+
+dnl
+AC_DEFUN([hurd_MIG_RETCODE], [dnl
+# See if mig groks `retcode'.
+AC_CACHE_CHECK(whether $MIG supports the retcode keyword, hurd_cv_mig_retcode,
+[cat > conftest.defs <<\EOF
+#include <mach/std_types.defs>
+#include <mach/mach_types.defs>
+subsystem foobar 1000;
+type reply_port_t = polymorphic | MACH_MSG_TYPE_PORT_SEND_ONCE
+ ctype: mach_port_t;
+simpleroutine foobar_reply (
+ reply_port: reply_port_t;
+ err: kern_return_t, RetCode);
+EOF
+if AC_TRY_COMMAND([CC="${CC}" ${MIG-false} -n conftest.defs 1>&AS_MESSAGE_LOG_FD()]); then
+ hurd_cv_mig_retcode=yes
+else
+ hurd_cv_mig_retcode=no
+fi
+rm -f conftest*])
+if test $hurd_cv_mig_retcode = yes; then
+ AC_DEFINE(HAVE_MIG_RETCODE)
+fi])
+
+dnl The following check is based on a similar check in GNU inetutils 1.4.0.
+dnl
+dnl hurd_LIB_NCURSESW -- check for, and configure, ncursesw
+dnl
+dnl If libncursesw is found to exist on this system and the --disable-ncursesw
+dnl flag wasn't specified, defines LIBNCURSESW with the appropriate linker
+dnl specification, and possibly defines NCURSESW_INCLUDE with the appropriate
+dnl -I flag to get access to ncursesw include files.
+dnl
+AC_DEFUN([hurd_LIB_NCURSESW], [
+ AC_ARG_ENABLE(ncursesw, [ --disable-ncursesw Do not use ncursesw],
+ , enable_ncursesw=yes)
+ if test "$enable_ncursesw" = yes; then
+ AC_CHECK_LIB(ncursesw, initscr, LIBNCURSESW="-lncursesw")
+ if test "$LIBNCURSESW"; then
+ AC_ARG_WITH(ncursesw-include-dir,
+[ --with-ncursesw-include-dir=DIR
+ Set directory containing the include files for
+ use with -lncursesw, when it isn't installed as
+ the default curses library. If DIR is "none",
+ then no special ncursesw include files are used.
+ --without-ncursesw-include-dir
+ Equivalent to --with-ncursesw-include-dir=none])dnl
+ if test "${with_ncursesw_include_dir+set}" = set; then
+ AC_MSG_CHECKING(for ncursesw include dir)
+ case "$with_ncursesw_include_dir" in
+ no|none)
+ hurd_cv_includedir_ncursesw=none;;
+ *)
+ hurd_cv_includedir_ncursesw="$with_ncursesw_include_dir";;
+ esac
+ AC_MSG_RESULT($hurd_cv_includedir_ncursesw)
+ else
+ AC_CACHE_CHECK(for ncursesw include dir,
+ hurd_cv_includedir_ncursesw,
+ for D in $includedir $prefix/include /local/include /usr/local/include /include /usr/include; do
+ if test -d $D/ncursesw; then
+ hurd_cv_includedir_ncursesw="$D/ncursesw"
+ break
+ fi
+ test "$hurd_cv_includedir_ncursesw" \
+ || hurd_cv_includedir_ncursesw=none
+ done)
+ fi
+ if test "$hurd_cv_includedir_ncursesw" = none; then
+ NCURSESW_INCLUDE=""
+ else
+ NCURSESW_INCLUDE="-I$hurd_cv_includedir_ncursesw"
+ fi
+ fi
+ fi
+ AC_SUBST(NCURSESW_INCLUDE)
+ AC_SUBST(LIBNCURSESW)])dnl
+
+# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
+# serial 1 (pkg-config-0.24)
+#
+# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# PKG_PROG_PKG_CONFIG([MIN-VERSION])
+# ----------------------------------
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
+m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
+AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
+AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+ AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+ _pkg_min_version=m4_default([$1], [0.9.0])
+ AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+ if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ PKG_CONFIG=""
+ fi
+fi[]dnl
+])# PKG_PROG_PKG_CONFIG
+
+# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# Check to see whether a particular set of modules exists. Similar
+# to PKG_CHECK_MODULES(), but does not set variables or print errors.
+#
+# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+# only at the first occurence in configure.ac, so if the first place
+# it's called might be skipped (such as if it is within an "if", you
+# have to call PKG_CHECK_EXISTS manually
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+ m4_default([$2], [:])
+m4_ifvaln([$3], [else
+ $3])dnl
+fi])
+
+# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+# ---------------------------------------------
+m4_define([_PKG_CONFIG],
+[if test -n "$$1"; then
+ pkg_cv_[]$1="$$1"
+ elif test -n "$PKG_CONFIG"; then
+ PKG_CHECK_EXISTS([$3],
+ [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes ],
+ [pkg_failed=yes])
+ else
+ pkg_failed=untried
+fi[]dnl
+])# _PKG_CONFIG
+
+# _PKG_SHORT_ERRORS_SUPPORTED
+# -----------------------------
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi[]dnl
+])# _PKG_SHORT_ERRORS_SUPPORTED
+
+
+# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+# [ACTION-IF-NOT-FOUND])
+#
+#
+# Note that if there is a possibility the first call to
+# PKG_CHECK_MODULES might not happen, you should be sure to include an
+# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+#
+#
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+ AC_MSG_RESULT([no])
+ _PKG_SHORT_ERRORS_SUPPORTED
+ if test $_pkg_short_errors_supported = yes; then
+ $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
+ else
+ $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+ m4_default([$4], [AC_MSG_ERROR(
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT])[]dnl
+ ])
+elif test $pkg_failed = untried; then
+ AC_MSG_RESULT([no])
+ m4_default([$4], [AC_MSG_FAILURE(
+[The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
+ ])
+else
+ $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+ $1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+ AC_MSG_RESULT([yes])
+ $3
+fi[]dnl
+])# PKG_CHECK_MODULES
diff --git a/auth/Makefile b/auth/Makefile
new file mode 100644
index 00000000..b9eedda5
--- /dev/null
+++ b/auth/Makefile
@@ -0,0 +1,30 @@
+# Copyright (C) 1991, 93, 94, 95, 96, 2000, 2012 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with the GNU Hurd; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := auth
+makemode := server
+
+SRCS = auth.c
+OBJS = auth.o authServer.o auth_replyUser.o
+target = auth
+HURDLIBS = ports ihash shouldbeinlibc
+OTHERLIBS = -lpthread
+
+MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+
+include ../Makeconf
diff --git a/auth/auth.c b/auth/auth.c
new file mode 100644
index 00000000..7d35bd37
--- /dev/null
+++ b/auth/auth.c
@@ -0,0 +1,533 @@
+/* Authentication server.
+ Copyright (C) 1996,97,98,99,2002 Free Software Foundation, Inc.
+ Written by Roland McGrath.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mach.h>
+#include <pthread.h>
+#include <hurd.h>
+#include <hurd/startup.h>
+#include <hurd/ports.h>
+#include <hurd/ihash.h>
+#include <idvec.h>
+#include <assert.h>
+#include <argp.h>
+#include <error.h>
+#include <version.h>
+#include "auth_S.h"
+#include "auth_reply_U.h"
+
+#include "auth.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION(auth);
+
+
+/* Auth handles are server ports with sets of ids. */
+struct authhandle
+ {
+ struct port_info pi;
+ struct idvec euids, egids, auids, agids;
+ };
+
+struct port_bucket *auth_bucket;
+struct port_class *authhandle_portclass;
+
+
+/* Create a new auth port. */
+
+static error_t
+create_authhandle (struct authhandle **new)
+{
+ error_t err = ports_create_port (authhandle_portclass, auth_bucket,
+ sizeof **new, new);
+ if (! err)
+ bzero (&(*new)->euids, (void *) &(*new)[1] - (void *) &(*new)->euids);
+ return err;
+}
+
+/* Clean up a dead auth port. */
+
+static void
+destroy_authhandle (void *p)
+{
+ struct authhandle *h = p;
+ idvec_free_contents (&h->euids);
+ idvec_free_contents (&h->egids);
+ idvec_free_contents (&h->auids);
+ idvec_free_contents (&h->agids);
+}
+
+/* id management. */
+
+static inline void
+idvec_copyout (struct idvec *idvec, uid_t **ids, size_t *nids)
+{
+ if (idvec->num > *nids)
+ *ids = idvec->ids;
+ else
+ memcpy (*ids, idvec->ids, idvec->num * sizeof *ids);
+ *nids = idvec->num;
+}
+
+#define C(auth, ids) idvec_copyout (&auth->ids, ids, n##ids)
+#define OUTIDS(auth) (C (auth, euids), C (auth, egids), \
+ C (auth, auids), C (auth, agids))
+
+/* Implement auth_getids as described in <hurd/auth.defs>. */
+kern_return_t
+S_auth_getids (struct authhandle *auth,
+ uid_t **euids,
+ size_t *neuids,
+ uid_t **auids,
+ size_t *nauids,
+ uid_t **egids,
+ size_t *negids,
+ uid_t **agids,
+ size_t *nagids)
+{
+ if (! auth)
+ return EOPNOTSUPP;
+
+ OUTIDS (auth);
+
+ return 0;
+}
+
+/* Implement auth_makeauth as described in <hurd/auth.defs>. */
+kern_return_t
+S_auth_makeauth (struct authhandle *auth,
+ mach_port_t *authpts, size_t nauths,
+ uid_t *euids, size_t neuids,
+ uid_t *auids, size_t nauids,
+ uid_t *egids, size_t negids,
+ uid_t *agids, size_t nagids,
+ mach_port_t *newhandle)
+{
+ struct authhandle *newauth, *auths[1 + nauths];
+ int hasroot = 0;
+ error_t err;
+ size_t i, j;
+
+ if (!auth)
+ return EOPNOTSUPP;
+
+ auths[0] = auth;
+
+ /* Fetch the auth structures for all the ports passed in. */
+ for (i = 0; i < nauths; i++)
+ auths[i + 1] = auth_port_to_handle (authpts[i]);
+
+ ++nauths;
+
+ /* Verify that the union of the handles passed in either contains euid 0
+ (root), or contains all the requested ids. */
+
+#define isuid(uid, auth) \
+ (idvec_contains (&(auth)->euids, uid) \
+ || idvec_contains (&(auth)->auids, uid))
+#define groupmember(gid, auth) \
+ (idvec_contains (&(auth)->egids, gid) \
+ || idvec_contains (&(auth)->agids, gid))
+#define isroot(auth) isuid (0, auth)
+
+ for (i = 0; i < nauths; i++)
+ if (auths[i] && isroot (auths[i]))
+ {
+ hasroot = 1;
+ break;
+ }
+
+ if (!hasroot)
+ {
+ int has_it;
+
+ for (i = 0; i < neuids; i++)
+ {
+ has_it = 0;
+ for (j = 0; j < nauths; j++)
+ if (auths[j] && isuid (euids[i], auths[j]))
+ {
+ has_it = 1;
+ break;
+ }
+ if (!has_it)
+ goto eperm;
+ }
+
+ for (i = 0; i < nauids; i++)
+ {
+ has_it = 0;
+ for (j = 0; j < nauths; j++)
+ if (auths[j] && isuid (auids[i], auths[j]))
+ {
+ has_it = 1;
+ break;
+ }
+ if (!has_it)
+ goto eperm;
+ }
+
+ for (i = 0; i < negids; i++)
+ {
+ has_it = 0;
+ for (j = 0; j < nauths; j++)
+ if (auths[j] && groupmember (egids[i], auths[j]))
+ {
+ has_it = 1;
+ break;
+ }
+ if (!has_it)
+ goto eperm;
+ }
+
+ for (i = 0; i < nagids; i++)
+ {
+ has_it = 0;
+ for (j = 0; j < nauths; j++)
+ if (auths[j] && groupmember (agids[i], auths[j]))
+ {
+ has_it = 1;
+ break;
+ }
+ if (!has_it)
+ goto eperm;
+ }
+ }
+
+ err = create_authhandle (&newauth);
+
+ /* Create a new handle with the specified ids. */
+
+#define MERGE S (euids); S (egids); S (auids); S (agids);
+#define S(uids) if (!err) err = idvec_merge_ids (&newauth->uids, uids, n##uids)
+
+ MERGE;
+
+#undef S
+
+ if (! err)
+ {
+ for (j = 1; j < nauths; ++j)
+ mach_port_deallocate (mach_task_self (), authpts[j - 1]);
+ *newhandle = ports_get_right (newauth);
+ ports_port_deref (newauth);
+ }
+
+ for (j = 1; j < nauths; j++)
+ if (auths[j])
+ ports_port_deref (auths[j]);
+ return err;
+
+ eperm:
+ for (j = 1; j < nauths; j++)
+ if (auths[j])
+ ports_port_deref (auths[j]);
+ return EPERM;
+}
+
+/* Transaction handling. */
+
+/* Since the user is responsible for freeing the rendezvous port, it has to
+ * wait for the server to have finished transmitting uids.
+ *
+ * The server thus waits for the user to give it uids (unless it was already
+ * there), transmits them and provides the passthrough port.
+ *
+ * The user gives the uids and waits for the passthrough port from the server.
+ *
+ * If the user is early, it has to tell the server it arrived.
+ */
+
+/* A pending user. */
+struct pending_user
+ {
+ hurd_ihash_locp_t locp; /* Position in the pending_users ihash table. */
+ pthread_cond_t wakeup; /* The reader is blocked on this condition. */
+
+ /* The user's auth handle. */
+ struct authhandle *user;
+
+ /* The port to pass back to the user. */
+ mach_port_t passthrough;
+ };
+
+/* A pending server. */
+struct pending_server
+ {
+ hurd_ihash_locp_t locp; /* Position in the pending_servers ihash table. */
+ pthread_cond_t wakeup; /* The server is blocked on this condition. */
+ };
+
+/* Table of pending transactions keyed on RENDEZVOUS. */
+struct hurd_ihash pending_users
+ = HURD_IHASH_INITIALIZER (offsetof (struct pending_user, locp));
+struct hurd_ihash pending_servers
+ = HURD_IHASH_INITIALIZER (offsetof (struct pending_server, locp));
+pthread_mutex_t pending_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Implement auth_user_authenticate as described in <hurd/auth.defs>. */
+kern_return_t
+S_auth_user_authenticate (struct authhandle *userauth,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mach_port_t rendezvous,
+ mach_port_t *newport,
+ mach_msg_type_name_t *newporttype)
+{
+ struct pending_server *s;
+ struct pending_user u;
+ error_t err;
+
+ if (! userauth)
+ return EOPNOTSUPP;
+
+ if (rendezvous == MACH_PORT_NULL || rendezvous == MACH_PORT_DEAD)
+ return EINVAL;
+
+ u.user = userauth;
+ pthread_cond_init (&u.wakeup, NULL);
+
+ pthread_mutex_lock (&pending_lock);
+
+ err = hurd_ihash_add (&pending_users, rendezvous, &u);
+ if (err) {
+ pthread_mutex_unlock (&pending_lock);
+ return err;
+ }
+
+ /* Give the server the auth port.
+ We need to add a ref in case the port dies. */
+ ports_port_ref (userauth);
+
+ /* Look for this rendezvous in the server list. */
+ s = hurd_ihash_find (&pending_servers, rendezvous);
+ if (s) {
+ /* Found it! */
+
+ /* Remove it from the pending list. */
+ hurd_ihash_locp_remove (&pending_servers, s->locp);
+
+ /* Tell it we eventually arrived. */
+ pthread_cond_signal (&s->wakeup);
+ }
+
+ ports_interrupt_self_on_port_death (userauth, rendezvous);
+ /* Wait for server answer. */
+ if (pthread_hurd_cond_wait_np (&u.wakeup, &pending_lock) &&
+ hurd_ihash_find (&pending_users, rendezvous))
+ /* We were interrupted; remove our record. */
+ {
+ hurd_ihash_locp_remove (&pending_users, u.locp);
+
+ /* Was it a normal interruption or did RENDEZVOUS die? */
+ mach_port_type_t type;
+ mach_port_type (mach_task_self (), rendezvous, &type);
+ err = type & MACH_PORT_TYPE_DEAD_NAME ? EINVAL : EINTR;
+ }
+
+ pthread_mutex_unlock (&pending_lock);
+
+ if (! err)
+ {
+ /* Extract the port. */
+ *newport = u.passthrough;
+ *newporttype = MACH_MSG_TYPE_MOVE_SEND;
+ mach_port_deallocate (mach_task_self (), rendezvous);
+ }
+
+ return err;
+}
+
+/* Implement auth_server_authenticate as described in <hurd/auth.defs>. */
+kern_return_t
+S_auth_server_authenticate (struct authhandle *serverauth,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mach_port_t rendezvous,
+ mach_port_t newport,
+ mach_msg_type_name_t newport_type,
+ uid_t **euids,
+ size_t *neuids,
+ uid_t **auids,
+ size_t *nauids,
+ uid_t **egids,
+ size_t *negids,
+ uid_t **agids,
+ size_t *nagids)
+{
+ struct pending_user *u;
+ struct authhandle *user;
+ error_t err = 0;
+
+ if (! serverauth)
+ return EOPNOTSUPP;
+
+ if (rendezvous == MACH_PORT_NULL || rendezvous == MACH_PORT_DEAD)
+ return EINVAL;
+
+ pthread_mutex_lock (&pending_lock);
+
+ /* Look for this rendezvous in the user list. */
+ u = hurd_ihash_find (&pending_users, rendezvous);
+ if (! u)
+ {
+ /* User not here yet, have to wait for it. */
+ struct pending_server s;
+ pthread_cond_init (&s.wakeup, NULL);
+ err = hurd_ihash_add (&pending_servers, rendezvous, &s);
+ if (! err)
+ {
+ ports_interrupt_self_on_port_death (serverauth, rendezvous);
+ if (pthread_hurd_cond_wait_np (&s.wakeup, &pending_lock) &&
+ hurd_ihash_find (&pending_servers, rendezvous))
+ /* We were interrupted; remove our record. */
+ {
+ hurd_ihash_locp_remove (&pending_servers, s.locp);
+
+ /* Was it a normal interruption or did RENDEZVOUS die? */
+ mach_port_type_t type;
+ mach_port_type (mach_task_self (), rendezvous, &type);
+ err = type & MACH_PORT_TYPE_DEAD_NAME ? EINVAL : EINTR;
+ }
+ else
+ {
+ u = hurd_ihash_find (&pending_users, rendezvous);
+ if (! u)
+ /* User still not here, odd! */
+ err = EINTR;
+ }
+ }
+ }
+
+ if (u)
+ {
+ error_t err2;
+
+ /* Remove it from the pending list. */
+ hurd_ihash_locp_remove (&pending_users, u->locp);
+
+ /* Found it! */
+ user = u->user;
+
+ pthread_mutex_unlock (&pending_lock);
+
+ /* Tell third party. */
+ err2 = auth_server_authenticate_reply (reply, reply_type, 0,
+ user->euids.ids, user->euids.num,
+ user->auids.ids, user->auids.num,
+ user->egids.ids, user->egids.num,
+ user->agids.ids, user->agids.num);
+
+ if (err2)
+ mach_port_deallocate (mach_task_self (), reply);
+
+ pthread_mutex_lock (&pending_lock);
+
+ /* Give the user the new port and wake the RPC up. */
+ u->passthrough = newport;
+
+ pthread_cond_signal (&u->wakeup);
+ }
+
+ pthread_mutex_unlock (&pending_lock);
+
+ if (err)
+ return err;
+
+ ports_port_deref (user);
+ mach_port_deallocate (mach_task_self (), rendezvous);
+ return MIG_NO_REPLY;
+}
+
+
+#include "../libports/notify_S.h"
+#include "../libports/interrupt_S.h"
+
+static int
+auth_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ mig_routine_t routine;
+ if ((routine = auth_server_routine (inp)) ||
+ (routine = ports_interrupt_server_routine (inp)) ||
+ (routine = ports_notify_server_routine (inp)))
+ {
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t boot;
+ process_t proc;
+ mach_port_t hostpriv, masterdev;
+ struct authhandle *firstauth;
+ struct argp argp = { 0, 0, 0, "Hurd standard authentication server." };
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ auth_bucket = ports_create_bucket ();
+ authhandle_portclass = ports_create_class (&destroy_authhandle, 0);
+
+ /* Create the initial root auth handle. */
+ err = create_authhandle (&firstauth);
+ assert_perror (err);
+ idvec_add (&firstauth->euids, 0);
+ idvec_add (&firstauth->auids, 0);
+ idvec_add (&firstauth->auids, 0);
+ idvec_merge (&firstauth->egids, &firstauth->euids);
+ idvec_merge (&firstauth->agids, &firstauth->auids);
+
+ /* Fetch our bootstrap port and contact the bootstrap filesystem. */
+ err = task_get_bootstrap_port (mach_task_self (), &boot);
+ assert_perror (err);
+ if (boot == MACH_PORT_NULL)
+ error (2, 0, "auth server can only be run by init during boot");
+ err = startup_authinit (boot, ports_get_right (firstauth),
+ MACH_MSG_TYPE_MAKE_SEND, &proc);
+ if (err)
+ error (2, err, "cannot contact init for bootstrap");
+
+ /* Register ourselves with the proc server and then start signals. */
+ proc_getprivports (proc, &hostpriv, &masterdev);
+ proc_register_version (proc, hostpriv, "auth", "", HURD_VERSION);
+ mach_port_deallocate (mach_task_self (), masterdev);
+ _hurd_port_set (&_hurd_ports[INIT_PORT_PROC], proc);
+ _hurd_proc_init (argv, NULL, 0);
+
+ /* Init knows intimately that we will be ready for messages
+ as soon as this returns. */
+ startup_essential_task (boot, mach_task_self (), MACH_PORT_NULL, "auth",
+ hostpriv);
+ mach_port_deallocate (mach_task_self (), boot);
+ mach_port_deallocate (mach_task_self (), hostpriv);
+
+ /* Be a server. */
+ while (1)
+ ports_manage_port_operations_multithread (auth_bucket,
+ auth_demuxer,
+ 30 * 1000, 0, 0);
+}
diff --git a/auth/auth.h b/auth/auth.h
new file mode 100644
index 00000000..0bc341d3
--- /dev/null
+++ b/auth/auth.h
@@ -0,0 +1,28 @@
+/*
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Justus Winter.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __AUTH_H__
+#define __AUTH_H__
+
+#include <hurd/ports.h>
+
+extern struct port_bucket *auth_bucket;
+extern struct port_class *authhandle_portclass;
+
+#endif /* __AUTH_H__ */
diff --git a/auth/mig-decls.h b/auth/mig-decls.h
new file mode 100644
index 00000000..09c7c70a
--- /dev/null
+++ b/auth/mig-decls.h
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Justus Winter.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __AUTH_MIG_DECLS_H__
+#define __AUTH_MIG_DECLS_H__
+
+#include "auth.h"
+
+typedef struct authhandle *authhandle_t;
+
+/* Called by server stub functions. */
+
+static inline struct authhandle * __attribute__ ((unused))
+auth_port_to_handle (mach_port_t auth)
+{
+ return ports_lookup_port (auth_bucket, auth, authhandle_portclass);
+}
+
+static inline void __attribute__ ((unused))
+end_using_authhandle (struct authhandle *auth)
+{
+ if (auth)
+ ports_port_deref (auth);
+}
+
+#endif /* __AUTH_MIG_DECLS_H__ */
diff --git a/auth/mig-mutate.h b/auth/mig-mutate.h
new file mode 100644
index 00000000..ea40c707
--- /dev/null
+++ b/auth/mig-mutate.h
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 1991,93,94,2014 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+/* CPP definitions for MiG processing of auth.defs for auth server. */
+
+#define AUTH_INTRAN authhandle_t auth_port_to_handle (auth_t)
+#define AUTH_DESTRUCTOR end_using_authhandle (authhandle_t)
+#define AUTH_IMPORTS import "mig-decls.h";
diff --git a/benchmarks/Makefile b/benchmarks/Makefile
new file mode 100644
index 00000000..416315ef
--- /dev/null
+++ b/benchmarks/Makefile
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 1994, 1995 Free Software Foundation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := benchmarks
+makemode := utility
+
+SRCS = forks.c
+OBJS = forks.o
+target = forks
+
+include ../Makeconf
diff --git a/benchmarks/forks.c b/benchmarks/forks.c
new file mode 100644
index 00000000..ed8e53e1
--- /dev/null
+++ b/benchmarks/forks.c
@@ -0,0 +1,64 @@
+/* From 4.4 BSD sys/tests/benchmarks/forks.c. */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/wait.h>
+
+/*
+ * Benchmark program to calculate fork+wait
+ * overhead (approximately). Process
+ * forks and exits while parent waits.
+ * The time to run this program is used
+ * in calculating exec overhead.
+ */
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int nforks, i;
+ char *cp;
+ int pid, child, status, brksize;
+ time_t starttime, endtime;
+
+ if (argc < 3) {
+ printf("usage: %s number-of-forks sbrk-size\n", argv[0]);
+ exit(1);
+ }
+ nforks = atoi(argv[1]);
+ if (nforks < 0) {
+ printf("%s: bad number of forks\n", argv[1]);
+ exit(2);
+ }
+ brksize = atoi(argv[2]);
+ if (brksize < 0) {
+ printf("%s: bad size to sbrk\n", argv[2]);
+ exit(3);
+ }
+
+ time (&starttime);
+ cp = (char *)sbrk(brksize);
+ if (cp == (void *)-1) {
+ perror("sbrk");
+ exit(4);
+ }
+ for (i = 0; i < brksize; i += 1024)
+ cp[i] = i;
+ while (nforks-- > 0) {
+ child = fork();
+ if (child == -1) {
+ perror("fork");
+ exit(-1);
+ }
+ if (child == 0)
+ _exit(-1);
+ while ((pid = wait(&status)) != -1 && pid != child)
+ ;
+ }
+ time (&endtime);
+ printf ("Time: %d seconds.\n", (int) (endtime - starttime));
+ exit(0);
+}
diff --git a/boot/Makefile b/boot/Makefile
new file mode 100644
index 00000000..2d52f3ff
--- /dev/null
+++ b/boot/Makefile
@@ -0,0 +1,50 @@
+# Copyright (C) 1993,94,95,96,97,2001,2012 Free Software Foundation, Inc.
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with the GNU Hurd; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := boot
+makemode := utility
+
+SRCS = mach-crt0.c boot.c ux.c sigvec.S syscall.S \
+ boot_script.c userland-boot.c
+COMMON-OBJS = notifyServer.o deviceServer.o \
+ ioServer.o io_replyUser.o device_replyUser.o \
+ termServer.o bootstrapServer.o boot_script.o userland-boot.o
+OBJS = boot.o $(COMMON-OBJS)
+UX-OBJS = mach-crt0.o uxboot.o sigvec.o syscall.o ux.o $(COMMON-OBJS)
+target = boot
+io-MIGSFLAGS=-DREPLY_PORTS
+HURDLIBS = store shouldbeinlibc
+OTHERLIBS = -lpthread
+
+include ../Makeconf
+
+#install: /usr/local/bin/uxboot
+#
+#/usr/local/bin/uxboot: uxboot
+# cp $< $@
+
+all: boot # uxboot
+
+uxboot.o: boot.c
+ $(COMPILE.c) -DUX $< -o $@
+
+uxboot.0: $(UX-OBJS)
+ $(LINK.o) -o $@ -static -nostartfiles -Wl,-T -Wl,$(srcdir)/frank1.ld $^
+uxboot.1: frankemul.ld uxboot.0
+ $(LD) -o $@ -T $^
+uxboot: uxboot.1
+ -$(OBJCOPY) -S --remove-section=.comment -O a.out-mach3 $< $@
diff --git a/boot/boot.c b/boot/boot.c
new file mode 100644
index 00000000..ed290148
--- /dev/null
+++ b/boot/boot.c
@@ -0,0 +1,1969 @@
+/* Load a task using the single server, and then run it
+ as if we were the kernel.
+ Copyright (C) 1993,94,95,96,97,98,99,2000,01,02,2006
+ Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include <mach.h>
+#include <mach/notify.h>
+#include <device/device.h>
+#include <a.out.h>
+#include <mach/message.h>
+#include <mach/mig_errors.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <elf.h>
+#include <mach/mig_support.h>
+#include <mach/default_pager.h>
+#include <mach/machine/vm_param.h> /* For VM_XXX_ADDRESS */
+#include <argp.h>
+#include <hurd/store.h>
+#include <sys/mman.h>
+#include <version.h>
+
+#include "notify_S.h"
+#include "device_S.h"
+#include "io_S.h"
+#include "device_reply_U.h"
+#include "io_reply_U.h"
+#include "term_S.h"
+#include "bootstrap_S.h"
+/* #include "tioctl_S.h" */
+
+#include "boot_script.h"
+
+#include <hurd/auth.h>
+
+#ifdef UX
+#undef STORE /* We can't use libstore when under UX. */
+#else
+#define STORE
+#endif
+
+#ifdef UX
+
+#include "ux.h"
+
+#else /* !UX */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <error.h>
+#include <hurd.h>
+#include <assert.h>
+
+static struct termios orig_tty_state;
+static int isig;
+static char *kernel_command_line;
+
+static void
+init_termstate ()
+{
+ struct termios tty_state;
+
+ if (tcgetattr (0, &tty_state) < 0)
+ error (10, errno, "tcgetattr");
+
+ orig_tty_state = tty_state;
+ cfmakeraw (&tty_state);
+ if (isig)
+ tty_state.c_lflag |= ISIG;
+
+ if (tcsetattr (0, 0, &tty_state) < 0)
+ error (11, errno, "tcsetattr");
+}
+
+static void
+restore_termstate ()
+{
+ tcsetattr (0, 0, &orig_tty_state);
+}
+
+#define host_fstat fstat
+typedef struct stat host_stat_t;
+#define host_exit exit
+
+#endif /* UX */
+
+mach_port_t privileged_host_port, master_device_port, defpager;
+mach_port_t pseudo_master_device_port;
+mach_port_t receive_set;
+mach_port_t pseudo_console, pseudo_root;
+auth_t authserver;
+
+struct store *root_store;
+
+pthread_spinlock_t queuelock = PTHREAD_SPINLOCK_INITIALIZER;
+pthread_spinlock_t readlock = PTHREAD_SPINLOCK_INITIALIZER;
+
+mach_port_t php_child_name, psmdp_child_name, taskname;
+
+task_t child_task;
+mach_port_t bootport;
+
+int console_mscount;
+
+vm_address_t fs_stack_base;
+vm_size_t fs_stack_size;
+
+void init_termstate ();
+void restore_termstate ();
+
+char *fsname;
+
+char bootstrap_args[100] = "-";
+char *bootdevice = 0;
+char *bootscript = 0;
+
+
+void safe_gets (char *buf, int buf_len)
+{
+ fgets (buf, buf_len, stdin);
+}
+
+char *useropen_dir;
+
+int
+useropen (const char *name, int flags, int mode)
+{
+ if (useropen_dir)
+ {
+ static int dlen;
+ if (!dlen) dlen = strlen (useropen_dir);
+ {
+ int len = strlen (name);
+ char try[dlen + 1 + len + 1];
+ int fd;
+ memcpy (try, useropen_dir, dlen);
+ try[dlen] = '/';
+ memcpy (&try[dlen + 1], name, len + 1);
+ fd = open (try, flags, mode);
+ if (fd >= 0)
+ return fd;
+ }
+ }
+ return open (name, flags, mode);
+}
+
+int
+request_server (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ extern int io_server (mach_msg_header_t *, mach_msg_header_t *);
+ extern int device_server (mach_msg_header_t *, mach_msg_header_t *);
+ extern int notify_server (mach_msg_header_t *, mach_msg_header_t *);
+ extern int term_server (mach_msg_header_t *, mach_msg_header_t *);
+/* extern int tioctl_server (mach_msg_header_t *, mach_msg_header_t *); */
+ extern int bootstrap_server (mach_msg_header_t *, mach_msg_header_t *);
+ extern void bootstrap_compat ();
+
+#if 0
+ if (inp->msgh_local_port == bootport && boot_like_cmudef)
+ {
+ if (inp->msgh_id == 999999)
+ {
+ bootstrap_compat (inp, outp);
+ return 1;
+ }
+ else
+ return bootstrap_server (inp, outp);
+ }
+ else
+#endif
+ return (io_server (inp, outp)
+ || device_server (inp, outp)
+ || notify_server (inp, outp)
+ || term_server (inp, outp)
+ /* || tioctl_server (inp, outp) */);
+}
+
+vm_address_t
+load_image (task_t t,
+ char *file)
+{
+ int fd;
+ union
+ {
+ struct exec a;
+ Elf32_Ehdr e;
+ } hdr;
+ char msg[] = ": cannot open bootstrap file\n";
+
+ fd = useropen (file, O_RDONLY, 0);
+
+ if (fd == -1)
+ {
+ write (2, file, strlen (file));
+ write (2, msg, sizeof msg - 1);
+ task_terminate (t);
+ host_exit (1);
+ }
+
+ read (fd, &hdr, sizeof hdr);
+ if (*(Elf32_Word *) hdr.e.e_ident == *(Elf32_Word *) "\177ELF")
+ {
+ Elf32_Phdr phdrs[hdr.e.e_phnum], *ph;
+ lseek (fd, hdr.e.e_phoff, SEEK_SET);
+ read (fd, phdrs, sizeof phdrs);
+ for (ph = phdrs; ph < &phdrs[sizeof phdrs/sizeof phdrs[0]]; ++ph)
+ if (ph->p_type == PT_LOAD)
+ {
+ vm_address_t buf;
+ vm_size_t offs = ph->p_offset & (ph->p_align - 1);
+ vm_size_t bufsz = round_page (ph->p_filesz + offs);
+
+ buf = (vm_address_t) mmap (0, bufsz,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+
+ lseek (fd, ph->p_offset, SEEK_SET);
+ read (fd, (void *)(buf + offs), ph->p_filesz);
+
+ ph->p_memsz = ((ph->p_vaddr + ph->p_memsz + ph->p_align - 1)
+ & ~(ph->p_align - 1));
+ ph->p_vaddr &= ~(ph->p_align - 1);
+ ph->p_memsz -= ph->p_vaddr;
+
+ vm_allocate (t, (vm_address_t*)&ph->p_vaddr, ph->p_memsz, 0);
+ vm_write (t, ph->p_vaddr, buf, bufsz);
+ munmap ((caddr_t) buf, bufsz);
+ vm_protect (t, ph->p_vaddr, ph->p_memsz, 0,
+ ((ph->p_flags & PF_R) ? VM_PROT_READ : 0) |
+ ((ph->p_flags & PF_W) ? VM_PROT_WRITE : 0) |
+ ((ph->p_flags & PF_X) ? VM_PROT_EXECUTE : 0));
+ }
+ return hdr.e.e_entry;
+ }
+ else
+ {
+ /* a.out */
+ int magic = N_MAGIC (hdr.a);
+ int headercruft;
+ vm_address_t base = 0x10000;
+ int rndamount, amount;
+ vm_address_t bsspagestart, bssstart;
+ char *buf;
+
+ headercruft = sizeof (struct exec) * (magic == ZMAGIC);
+
+ amount = headercruft + hdr.a.a_text + hdr.a.a_data;
+ rndamount = round_page (amount);
+ buf = mmap (0, rndamount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ lseek (fd, sizeof hdr.a - headercruft, SEEK_SET);
+ read (fd, buf, amount);
+ vm_allocate (t, &base, rndamount, 0);
+ vm_write (t, base, (vm_address_t) buf, rndamount);
+ if (magic != OMAGIC)
+ vm_protect (t, base, trunc_page (headercruft + hdr.a.a_text),
+ 0, VM_PROT_READ | VM_PROT_EXECUTE);
+ munmap ((caddr_t) buf, rndamount);
+
+ bssstart = base + hdr.a.a_text + hdr.a.a_data + headercruft;
+ bsspagestart = round_page (bssstart);
+ vm_allocate (t, &bsspagestart,
+ hdr.a.a_bss - (bsspagestart - bssstart), 0);
+
+ return hdr.a.a_entry;
+ }
+}
+
+
+void read_reply ();
+void * msg_thread (void *);
+
+/* Callbacks for boot_script.c; see boot_script.h. */
+
+mach_port_t
+boot_script_read_file (const char *filename)
+{
+ static const char msg[] = ": cannot open\n";
+ int fd = useropen (filename, O_RDONLY, 0);
+ host_stat_t st;
+ error_t err;
+ mach_port_t memobj;
+ vm_address_t region;
+
+ write (2, filename, strlen (filename));
+ if (fd < 0)
+ {
+ write (2, msg, sizeof msg - 1);
+ host_exit (1);
+ }
+ else
+ write (2, msg + sizeof msg - 2, 1);
+
+ host_fstat (fd, &st);
+
+ err = default_pager_object_create (defpager, &memobj,
+ round_page (st.st_size));
+ if (err)
+ {
+ static const char msg[] = "cannot create default-pager object\n";
+ write (2, msg, sizeof msg - 1);
+ host_exit (1);
+ }
+
+ region = 0;
+ vm_map (mach_task_self (), &region, round_page (st.st_size),
+ 0, 1, memobj, 0, 0, VM_PROT_ALL, VM_PROT_ALL, VM_INHERIT_NONE);
+ read (fd, (char *) region, st.st_size);
+ munmap ((caddr_t) region, round_page (st.st_size));
+
+ close (fd);
+ return memobj;
+}
+
+int
+boot_script_exec_cmd (void *hook,
+ mach_port_t task, char *path, int argc,
+ char **argv, char *strings, int stringlen)
+{
+ char *args, *p;
+ int arg_len, i;
+ size_t reg_size;
+ void *arg_pos;
+ vm_offset_t stack_start, stack_end;
+ vm_address_t startpc, str_start;
+ thread_t thread;
+
+ write (2, path, strlen (path));
+ for (i = 1; i < argc; ++i)
+ {
+ write (2, " ", 1);
+ write (2, argv[i], strlen (argv[i]));
+ }
+ write (2, "\r\n", 2);
+
+ startpc = load_image (task, path);
+ arg_len = stringlen + (argc + 2) * sizeof (char *) + sizeof (integer_t);
+ arg_len += 5 * sizeof (int);
+ stack_end = VM_MAX_ADDRESS;
+ stack_start = VM_MAX_ADDRESS - 16 * 1024 * 1024;
+ vm_allocate (task, &stack_start, stack_end - stack_start, FALSE);
+ arg_pos = (void *) ((stack_end - arg_len) & ~(sizeof (natural_t) - 1));
+ args = mmap (0, stack_end - trunc_page ((vm_offset_t) arg_pos),
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ str_start = ((vm_address_t) arg_pos
+ + (argc + 2) * sizeof (char *) + sizeof (integer_t));
+ p = args + ((vm_address_t) arg_pos & (vm_page_size - 1));
+ *(int *) p = argc;
+ p = (void *) p + sizeof (int);
+ for (i = 0; i < argc; i++)
+ {
+ *(char **) p = argv[i] - strings + (char *) str_start;
+ p = (void *) p + sizeof (char *);
+ }
+ *(char **) p = 0;
+ p = (void *) p + sizeof (char *);
+ *(char **) p = 0;
+ p = (void *) p + sizeof (char *);
+ memcpy (p, strings, stringlen);
+ bzero (args, (vm_offset_t) arg_pos & (vm_page_size - 1));
+ vm_write (task, trunc_page ((vm_offset_t) arg_pos), (vm_address_t) args,
+ stack_end - trunc_page ((vm_offset_t) arg_pos));
+ munmap ((caddr_t) args,
+ stack_end - trunc_page ((vm_offset_t) arg_pos));
+
+ thread_create (task, &thread);
+#ifdef i386_THREAD_STATE_COUNT
+ {
+ struct i386_thread_state regs;
+ reg_size = i386_THREAD_STATE_COUNT;
+ thread_get_state (thread, i386_THREAD_STATE,
+ (thread_state_t) &regs, &reg_size);
+ regs.eip = (int) startpc;
+ regs.uesp = (int) arg_pos;
+ thread_set_state (thread, i386_THREAD_STATE,
+ (thread_state_t) &regs, reg_size);
+ }
+#elif defined(ALPHA_THREAD_STATE_COUNT)
+ {
+ struct alpha_thread_state regs;
+ reg_size = ALPHA_THREAD_STATE_COUNT;
+ thread_get_state (thread, ALPHA_THREAD_STATE,
+ (thread_state_t) &regs, &reg_size);
+ regs.r30 = (natural_t) arg_pos;
+ regs.pc = (natural_t) startpc;
+ thread_set_state (thread, ALPHA_THREAD_STATE,
+ (thread_state_t) &regs, reg_size);
+ }
+#else
+# error needs to be ported
+#endif
+
+ thread_resume (thread);
+ mach_port_deallocate (mach_task_self (), thread);
+ return 0;
+}
+
+const char *argp_program_version = STANDARD_HURD_VERSION (boot);
+
+static struct argp_option options[] =
+{
+ { "boot-root", 'D', "DIR", 0,
+ "Root of a directory tree in which to find files specified in BOOT-SCRIPT" },
+ { "single-user", 's', 0, 0,
+ "Boot in single user mode" },
+ { "kernel-command-line", 'c', "COMMAND LINE", 0,
+ "Simulated multiboot command line to supply" },
+ { "pause" , 'd', 0, 0,
+ "Pause for user confirmation at various times during booting" },
+ { "isig", 'I', 0, 0,
+ "Do not disable terminal signals, so you can suspend and interrupt boot."},
+ { "device", 'f', "device_name=device_file", 0,
+ "Specify a device file used by subhurd and its virtual name."},
+ { 0 }
+};
+static char args_doc[] = "BOOT-SCRIPT";
+static char doc[] = "Boot a second hurd";
+
+struct dev_map
+{
+ char *name;
+ mach_port_t port;
+ struct dev_map *next;
+};
+
+static struct dev_map *dev_map_head;
+
+static struct dev_map *add_dev_map (char *dev_name, char *dev_file)
+{
+ struct dev_map *map = malloc (sizeof (*map));
+
+ assert (map);
+ map->name = dev_name;
+ map->port = file_name_lookup (dev_file, 0, 0);
+ if (map->port == MACH_PORT_NULL)
+ error (1, errno, "file_name_lookup: %s", dev_file);
+ map->next = dev_map_head;
+ dev_map_head = map;
+ return map;
+}
+
+static struct dev_map *lookup_dev (char *dev_name)
+{
+ struct dev_map *map;
+
+ for (map = dev_map_head; map; map = map->next)
+ {
+ if (strcmp (map->name, dev_name) == 0)
+ return map;
+ }
+ return NULL;
+}
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ char *dev_file;
+
+ switch (key)
+ {
+ size_t len;
+
+ case 'c': kernel_command_line = arg; break;
+
+ case 'D': useropen_dir = arg; break;
+
+ case 'I': isig = 1; break;
+
+ case 's': case 'd':
+ len = strlen (bootstrap_args);
+ if (len >= sizeof bootstrap_args - 1)
+ argp_error (state, "Too many bootstrap args");
+ bootstrap_args[len++] = key;
+ bootstrap_args[len] = '\0';
+ break;
+
+ case 'f':
+ dev_file = strchr (arg, '=');
+ if (dev_file == NULL)
+ return ARGP_ERR_UNKNOWN;
+ *dev_file = 0;
+ add_dev_map (arg, dev_file+1);
+ break;
+
+ case ARGP_KEY_ARG:
+ if (state->arg_num == 0)
+ bootscript = arg;
+ else
+ return ARGP_ERR_UNKNOWN;
+ break;
+
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = state->input; break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+int
+main (int argc, char **argv, char **envp)
+{
+ error_t err;
+ mach_port_t foo;
+ char *buf = 0;
+ int i, len;
+ pthread_t pthread_id;
+ char *root_store_name;
+ const struct argp_child kids[] = { { &store_argp }, { 0 }};
+ struct argp argp = { options, parse_opt, args_doc, doc, kids };
+ struct store_argp_params store_argp_params = { 0 };
+
+ argp_parse (&argp, argc, argv, 0, 0, &store_argp_params);
+ err = store_parsed_name (store_argp_params.result, &root_store_name);
+ if (err)
+ error (2, err, "store_parsed_name");
+
+ err = store_parsed_open (store_argp_params.result, 0, &root_store);
+ if (err)
+ error (4, err, "%s", root_store_name);
+
+ get_privileged_ports (&privileged_host_port, &master_device_port);
+
+ defpager = MACH_PORT_NULL;
+ vm_set_default_memory_manager (privileged_host_port, &defpager);
+
+ strcat (bootstrap_args, "f");
+
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_PORT_SET,
+ &receive_set);
+
+ if (root_store->class == &store_device_class && root_store->name
+ && (root_store->flags & STORE_ENFORCED)
+ && root_store->num_runs == 1 && root_store->runs[0].start == 0)
+ /* Let known device nodes pass through directly. */
+ bootdevice = root_store->name;
+ else
+ /* Pass a magic value that we can use to do I/O to ROOT_STORE. */
+ {
+ bootdevice = "pseudo-root";
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &pseudo_root);
+ mach_port_move_member (mach_task_self (), pseudo_root, receive_set);
+ }
+
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &pseudo_master_device_port);
+ mach_port_insert_right (mach_task_self (),
+ pseudo_master_device_port,
+ pseudo_master_device_port,
+ MACH_MSG_TYPE_MAKE_SEND);
+ mach_port_move_member (mach_task_self (), pseudo_master_device_port,
+ receive_set);
+ mach_port_request_notification (mach_task_self (), pseudo_master_device_port,
+ MACH_NOTIFY_NO_SENDERS, 1,
+ pseudo_master_device_port,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &foo);
+ if (foo != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), foo);
+
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &pseudo_console);
+ mach_port_move_member (mach_task_self (), pseudo_console, receive_set);
+ mach_port_request_notification (mach_task_self (), pseudo_console,
+ MACH_NOTIFY_NO_SENDERS, 1, pseudo_console,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &foo);
+ if (foo != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), foo);
+
+ if (kernel_command_line == 0)
+ asprintf (&kernel_command_line, "%s %s root=%s",
+ argv[0], bootstrap_args, bootdevice);
+
+ /* Initialize boot script variables. */
+ if (boot_script_set_variable ("host-port", VAL_PORT,
+ (int) privileged_host_port)
+ || boot_script_set_variable ("device-port", VAL_PORT,
+ (integer_t) pseudo_master_device_port)
+ || boot_script_set_variable ("kernel-command-line", VAL_STR,
+ (integer_t) kernel_command_line)
+ || boot_script_set_variable ("root-device",
+ VAL_STR, (integer_t) bootdevice)
+ || boot_script_set_variable ("boot-args",
+ VAL_STR, (integer_t) bootstrap_args))
+ {
+ static const char msg[] = "error setting variable";
+
+ write (2, msg, strlen (msg));
+ host_exit (1);
+ }
+
+ /* Turn each `FOO=BAR' word in the command line into a boot script
+ variable ${FOO} with value BAR. */
+ {
+ int len = strlen (kernel_command_line) + 1;
+ char *s = memcpy (alloca (len), kernel_command_line, len);
+ char *word;
+
+ while ((word = strsep (&s, " \t")) != 0)
+ {
+ char *eq = strchr (word, '=');
+ if (eq == 0)
+ continue;
+ *eq++ = '\0';
+ err = boot_script_set_variable (word, VAL_STR, (integer_t) eq);
+ if (err)
+ {
+ char *msg;
+ asprintf (&msg, "cannot set boot-script variable %s: %s\n",
+ word, boot_script_error_string (err));
+ assert (msg);
+ write (2, msg, strlen (msg));
+ free (msg);
+ host_exit (1);
+ }
+ }
+ }
+
+ /* Parse the boot script. */
+ {
+ char *p, *line;
+ static const char filemsg[] = "Can't open boot script\n";
+ static const char memmsg[] = "Not enough memory\n";
+ int amt, fd, err;
+
+ fd = open (bootscript, O_RDONLY, 0);
+ if (fd < 0)
+ {
+ write (2, filemsg, sizeof (filemsg));
+ host_exit (1);
+ }
+ p = buf = malloc (500);
+ if (!buf)
+ {
+ write (2, memmsg, sizeof (memmsg));
+ host_exit (1);
+ }
+ len = 500;
+ amt = 0;
+ while (1)
+ {
+ i = read (fd, p, len - (p - buf));
+ if (i <= 0)
+ break;
+ p += i;
+ amt += i;
+ if (p == buf + len)
+ {
+ char *newbuf;
+
+ len += 500;
+ newbuf = realloc (buf, len);
+ if (!newbuf)
+ {
+ write (2, memmsg, sizeof (memmsg));
+ host_exit (1);
+ }
+ p = newbuf + (p - buf);
+ buf = newbuf;
+ }
+ }
+ line = p = buf;
+ while (1)
+ {
+ while (p < buf + amt && *p != '\n')
+ p++;
+ *p = '\0';
+ err = boot_script_parse_line (0, line);
+ if (err)
+ {
+ char *str;
+ int i;
+
+ str = boot_script_error_string (err);
+ i = strlen (str);
+ write (2, str, i);
+ write (2, " in `", 5);
+ write (2, line, strlen (line));
+ write (2, "'\n", 2);
+ host_exit (1);
+ }
+ if (p == buf + amt)
+ break;
+ line = ++p;
+ }
+ }
+
+ if (index (bootstrap_args, 'd'))
+ {
+ static const char msg[] = "Pausing. . .";
+ char c;
+ write (2, msg, sizeof (msg) - 1);
+ read (0, &c, 1);
+ }
+
+ init_termstate ();
+
+ /* The boot script has now been parsed into internal data structures.
+ Now execute its directives. */
+ {
+ int err;
+
+ err = boot_script_exec ();
+ if (err)
+ {
+ char *str = boot_script_error_string (err);
+ int i = strlen (str);
+
+ write (2, str, i);
+ write (2, "\n", 1);
+ host_exit (1);
+ }
+ free (buf);
+ }
+
+ mach_port_deallocate (mach_task_self (), pseudo_master_device_port);
+
+ err = pthread_create (&pthread_id, NULL, msg_thread, NULL);
+ if (!err)
+ pthread_detach (pthread_id);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+
+ for (;;)
+ {
+ fd_set rmask;
+ FD_ZERO (&rmask);
+ FD_SET (0, &rmask);
+ if (select (1, &rmask, 0, 0, 0) == 1)
+ read_reply ();
+ else /* We hosed */
+ error (5, errno, "select");
+ }
+
+/* mach_msg_server (request_server, __vm_page_size * 2, receive_set); */
+}
+
+void *
+msg_thread (void *arg)
+{
+ while (1)
+ mach_msg_server (request_server, 0, receive_set);
+}
+
+
+enum read_type
+{
+ DEV_READ,
+ DEV_READI,
+ IO_READ,
+};
+struct qr
+{
+ enum read_type type;
+ mach_port_t reply_port;
+ mach_msg_type_name_t reply_type;
+ int amount;
+ struct qr *next;
+};
+struct qr *qrhead, *qrtail;
+
+/* Queue a read for later reply. */
+kern_return_t
+queue_read (enum read_type type,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ int amount)
+{
+ struct qr *qr;
+
+ qr = malloc (sizeof (struct qr));
+ if (!qr)
+ return D_NO_MEMORY;
+
+ pthread_spin_lock (&queuelock);
+
+ qr->type = type;
+ qr->reply_port = reply_port;
+ qr->reply_type = reply_type;
+ qr->amount = amount;
+ qr->next = 0;
+ if (qrtail)
+ qrtail->next = qr;
+ else
+ qrhead = qrtail = qr;
+
+ pthread_spin_unlock (&queuelock);
+ return D_SUCCESS;
+}
+
+/* TRUE if there's data available on stdin, which should be used to satisfy
+ console read requests. */
+static int should_read = 0;
+
+/* Reply to a queued read. */
+void
+read_reply ()
+{
+ int avail;
+ struct qr *qr;
+ char * buf;
+ int amtread;
+
+ /* By forcing SHOULD_READ to true before trying the lock, we ensure that
+ either we get the lock ourselves or that whoever currently holds the
+ lock will service this read when he unlocks it. */
+ should_read = 1;
+ if (pthread_spin_trylock (&readlock))
+ return;
+
+ /* Since we're committed to servicing the read, no one else need do so. */
+ should_read = 0;
+
+ ioctl (0, FIONREAD, &avail);
+ if (!avail)
+ {
+ pthread_spin_unlock (&readlock);
+ return;
+ }
+
+ pthread_spin_lock (&queuelock);
+
+ if (!qrhead)
+ {
+ pthread_spin_unlock (&queuelock);
+ pthread_spin_unlock (&readlock);
+ return;
+ }
+
+ qr = qrhead;
+ qrhead = qr->next;
+ if (qr == qrtail)
+ qrtail = 0;
+
+ pthread_spin_unlock (&queuelock);
+
+ if (qr->type == DEV_READ)
+ buf = mmap (0, qr->amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ else
+ buf = alloca (qr->amount);
+ amtread = read (0, buf, qr->amount);
+
+ pthread_spin_unlock (&readlock);
+
+ switch (qr->type)
+ {
+ case DEV_READ:
+ if (amtread >= 0)
+ ds_device_read_reply (qr->reply_port, qr->reply_type, 0,
+ (io_buf_ptr_t) buf, amtread);
+ else
+ ds_device_read_reply (qr->reply_port, qr->reply_type, errno, 0, 0);
+ break;
+
+ case DEV_READI:
+ if (amtread >= 0)
+ ds_device_read_reply_inband (qr->reply_port, qr->reply_type, 0,
+ buf, amtread);
+ else
+ ds_device_read_reply_inband (qr->reply_port, qr->reply_type, errno,
+ 0, 0);
+ break;
+
+ case IO_READ:
+ if (amtread >= 0)
+ io_read_reply (qr->reply_port, qr->reply_type, 0,
+ buf, amtread);
+ else
+ io_read_reply (qr->reply_port, qr->reply_type, errno, 0, 0);
+ break;
+ }
+
+ free (qr);
+}
+
+/* Unlock READLOCK, and also service any new read requests that it was
+ blocking. */
+static void
+unlock_readlock ()
+{
+ pthread_spin_unlock (&readlock);
+ while (should_read)
+ read_reply ();
+}
+
+/*
+ * Handle bootstrap requests.
+ */
+/* These two functions from .../mk/bootstrap/default_pager.c. */
+
+kern_return_t
+do_bootstrap_privileged_ports(bootstrap, hostp, devicep)
+ mach_port_t bootstrap;
+ mach_port_t *hostp, *devicep;
+{
+ *hostp = privileged_host_port;
+ *devicep = pseudo_master_device_port;
+ return KERN_SUCCESS;
+}
+
+void
+bootstrap_compat(in, out)
+ mach_msg_header_t *in, *out;
+{
+ mig_reply_header_t *reply = (mig_reply_header_t *) out;
+ mach_msg_return_t mr;
+
+ struct imsg {
+ mach_msg_header_t hdr;
+ mach_msg_type_t port_desc_1;
+ mach_port_t port_1;
+ mach_msg_type_t port_desc_2;
+ mach_port_t port_2;
+ } imsg;
+
+ /*
+ * Send back the host and device ports.
+ */
+
+ imsg.hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX |
+ MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(in->msgh_bits), 0);
+ /* msgh_size doesn't need to be initialized */
+ imsg.hdr.msgh_remote_port = in->msgh_remote_port;
+ imsg.hdr.msgh_local_port = MACH_PORT_NULL;
+ /* msgh_seqno doesn't need to be initialized */
+ imsg.hdr.msgh_id = in->msgh_id + 100; /* this is a reply msg */
+
+ imsg.port_desc_1.msgt_name = MACH_MSG_TYPE_COPY_SEND;
+ imsg.port_desc_1.msgt_size = (sizeof(mach_port_t) * 8);
+ imsg.port_desc_1.msgt_number = 1;
+ imsg.port_desc_1.msgt_inline = TRUE;
+ imsg.port_desc_1.msgt_longform = FALSE;
+ imsg.port_desc_1.msgt_deallocate = FALSE;
+ imsg.port_desc_1.msgt_unused = 0;
+
+ imsg.port_1 = privileged_host_port;
+
+ imsg.port_desc_2 = imsg.port_desc_1;
+
+ imsg.port_desc_2.msgt_name = MACH_MSG_TYPE_MAKE_SEND;
+ imsg.port_2 = pseudo_master_device_port;
+
+ /*
+ * Send the reply message.
+ * (mach_msg_server can not do this, because the reply
+ * is not in standard format.)
+ */
+
+ mr = mach_msg(&imsg.hdr, MACH_SEND_MSG,
+ sizeof imsg, 0, MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if (mr != MACH_MSG_SUCCESS)
+ (void) mach_port_deallocate(mach_task_self (),
+ imsg.hdr.msgh_remote_port);
+
+ /*
+ * Tell mach_msg_server to do nothing.
+ */
+
+ reply->RetCode = MIG_NO_REPLY;
+}
+
+/* Implementation of device interface */
+
+kern_return_t
+ds_device_open (mach_port_t master_port,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ dev_mode_t mode,
+ dev_name_t name,
+ mach_port_t *device,
+ mach_msg_type_name_t *devicetype)
+{
+ struct dev_map *map;
+
+ if (master_port != pseudo_master_device_port)
+ return D_INVALID_OPERATION;
+
+ if (!strcmp (name, "console"))
+ {
+#if 0
+ mach_port_insert_right (mach_task_self (), pseudo_console,
+ pseudo_console, MACH_MSG_TYPE_MAKE_SEND);
+ console_send_rights++;
+#endif
+ console_mscount++;
+ *device = pseudo_console;
+ *devicetype = MACH_MSG_TYPE_MAKE_SEND;
+ return 0;
+ }
+ else if (strcmp (name, "pseudo-root") == 0)
+ /* Magic root device. */
+ {
+ *device = pseudo_root;
+ *devicetype = MACH_MSG_TYPE_MAKE_SEND;
+ return 0;
+ }
+
+ map = lookup_dev (name);
+ if (map)
+ {
+ *devicetype = MACH_MSG_TYPE_MOVE_SEND;
+ return device_open (map->port, mode, "", device);
+ }
+
+ *devicetype = MACH_MSG_TYPE_MOVE_SEND;
+ return device_open (master_device_port, mode, name, device);
+}
+
+kern_return_t
+ds_device_close (device_t device)
+{
+ if (device != pseudo_console && device != pseudo_root)
+ return D_NO_SUCH_DEVICE;
+ return 0;
+}
+
+kern_return_t
+ds_device_write (device_t device,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ dev_mode_t mode,
+ recnum_t recnum,
+ io_buf_ptr_t data,
+ size_t datalen,
+ int *bytes_written)
+{
+ if (device == pseudo_console)
+ {
+#if 0
+ if (console_send_rights)
+ {
+ mach_port_mod_refs (mach_task_self (), pseudo_console,
+ MACH_PORT_TYPE_SEND, -console_send_rights);
+ console_send_rights = 0;
+ }
+#endif
+
+ *bytes_written = write (1, data, datalen);
+
+ return (*bytes_written == -1 ? D_IO_ERROR : D_SUCCESS);
+ }
+ else if (device == pseudo_root)
+ {
+ size_t wrote;
+ if (store_write (root_store, recnum, data, datalen, &wrote) != 0)
+ return D_IO_ERROR;
+ *bytes_written = wrote;
+ return D_SUCCESS;
+ }
+ else
+ return D_NO_SUCH_DEVICE;
+}
+
+kern_return_t
+ds_device_write_inband (device_t device,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ dev_mode_t mode,
+ recnum_t recnum,
+ io_buf_ptr_inband_t data,
+ size_t datalen,
+ int *bytes_written)
+{
+ if (device == pseudo_console)
+ {
+#if 0
+ if (console_send_rights)
+ {
+ mach_port_mod_refs (mach_task_self (), pseudo_console,
+ MACH_PORT_TYPE_SEND, -console_send_rights);
+ console_send_rights = 0;
+ }
+#endif
+
+ *bytes_written = write (1, data, datalen);
+
+ return (*bytes_written == -1 ? D_IO_ERROR : D_SUCCESS);
+ }
+ else if (device == pseudo_root)
+ {
+ size_t wrote;
+ if (store_write (root_store, recnum, data, datalen, &wrote) != 0)
+ return D_IO_ERROR;
+ *bytes_written = wrote;
+ return D_SUCCESS;
+ }
+ else
+ return D_NO_SUCH_DEVICE;
+}
+
+kern_return_t
+ds_device_read (device_t device,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ dev_mode_t mode,
+ recnum_t recnum,
+ int bytes_wanted,
+ io_buf_ptr_t *data,
+ size_t *datalen)
+{
+ if (device == pseudo_console)
+ {
+ int avail;
+
+#if 0
+ if (console_send_rights)
+ {
+ mach_port_mod_refs (mach_task_self (), pseudo_console,
+ MACH_PORT_TYPE_SEND, -console_send_rights);
+ console_send_rights = 0;
+ }
+#endif
+
+ pthread_spin_lock (&readlock);
+ ioctl (0, FIONREAD, &avail);
+ if (avail)
+ {
+ *data = mmap (0, bytes_wanted, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ *datalen = read (0, *data, bytes_wanted);
+ unlock_readlock ();
+ return (*datalen == -1 ? D_IO_ERROR : D_SUCCESS);
+ }
+ else
+ {
+ kern_return_t err;
+
+ unlock_readlock ();
+ err = queue_read (DEV_READ, reply_port, reply_type, bytes_wanted);
+ if (err)
+ return err;
+ return MIG_NO_REPLY;
+ }
+ }
+ else if (device == pseudo_root)
+ {
+ *datalen = 0;
+ return
+ (store_read (root_store, recnum, bytes_wanted, (void **)data, datalen) == 0
+ ? D_SUCCESS
+ : D_IO_ERROR);
+ }
+ else
+ return D_NO_SUCH_DEVICE;
+}
+
+kern_return_t
+ds_device_read_inband (device_t device,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ dev_mode_t mode,
+ recnum_t recnum,
+ int bytes_wanted,
+ io_buf_ptr_inband_t data,
+ size_t *datalen)
+{
+ if (device == pseudo_console)
+ {
+ int avail;
+
+#if 0
+ if (console_send_rights)
+ {
+ mach_port_mod_refs (mach_task_self (), pseudo_console,
+ MACH_PORT_TYPE_SEND, -console_send_rights);
+ console_send_rights = 0;
+ }
+#endif
+
+ pthread_spin_lock (&readlock);
+ ioctl (0, FIONREAD, &avail);
+ if (avail)
+ {
+ *datalen = read (0, data, bytes_wanted);
+ unlock_readlock ();
+ return (*datalen == -1 ? D_IO_ERROR : D_SUCCESS);
+ }
+ else
+ {
+ kern_return_t err;
+
+ unlock_readlock ();
+ err = queue_read (DEV_READI, reply_port, reply_type, bytes_wanted);
+ if (err)
+ return err;
+ return MIG_NO_REPLY;
+ }
+ }
+ else if (device == pseudo_root)
+ {
+ error_t err;
+ void *returned = data;
+
+ *datalen = bytes_wanted;
+ err =
+ store_read (root_store, recnum, bytes_wanted, (void **)&returned, datalen);
+
+ if (! err)
+ {
+ if (returned != data)
+ {
+ bcopy (returned, (void *)data, *datalen);
+ munmap ((caddr_t) returned, *datalen);
+ }
+ return D_SUCCESS;
+ }
+ else
+ return D_IO_ERROR;
+ }
+ else
+ return D_NO_SUCH_DEVICE;
+}
+
+kern_return_t
+ds_xxx_device_set_status (device_t device,
+ dev_flavor_t flavor,
+ dev_status_t status,
+ size_t statu_cnt)
+{
+ if (device != pseudo_console)
+ return D_NO_SUCH_DEVICE;
+ return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_xxx_device_get_status (device_t device,
+ dev_flavor_t flavor,
+ dev_status_t status,
+ size_t *statuscnt)
+{
+ if (device != pseudo_console && device != pseudo_root)
+ return D_NO_SUCH_DEVICE;
+ return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_xxx_device_set_filter (device_t device,
+ mach_port_t rec,
+ int pri,
+ filter_array_t filt,
+ size_t len)
+{
+ if (device != pseudo_console && device != pseudo_root)
+ return D_NO_SUCH_DEVICE;
+ return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_device_map (device_t device,
+ vm_prot_t prot,
+ vm_offset_t offset,
+ vm_size_t size,
+ memory_object_t *pager,
+ int unmap)
+{
+ if (device != pseudo_console && device != pseudo_root)
+ return D_NO_SUCH_DEVICE;
+ return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_device_set_status (device_t device,
+ dev_flavor_t flavor,
+ dev_status_t status,
+ size_t statuslen)
+{
+ if (device != pseudo_console && device != pseudo_root)
+ return D_NO_SUCH_DEVICE;
+ return D_INVALID_OPERATION;
+}
+
+kern_return_t
+ds_device_get_status (device_t device,
+ dev_flavor_t flavor,
+ dev_status_t status,
+ size_t *statuslen)
+{
+ if (device == pseudo_console)
+ return D_INVALID_OPERATION;
+ else if (device == pseudo_root)
+ if (flavor == DEV_GET_SIZE)
+ if (*statuslen < DEV_GET_SIZE_COUNT)
+ return D_INVALID_SIZE;
+ else
+ {
+ status[DEV_GET_SIZE_DEVICE_SIZE] = root_store->size;
+ status[DEV_GET_SIZE_RECORD_SIZE] = root_store->block_size;
+ *statuslen = DEV_GET_SIZE_COUNT;
+ return D_SUCCESS;
+ }
+ else
+ return D_INVALID_OPERATION;
+ else
+ return D_NO_SUCH_DEVICE;
+}
+
+kern_return_t
+ds_device_set_filter (device_t device,
+ mach_port_t receive_port,
+ int priority,
+ filter_array_t filter,
+ size_t filterlen)
+{
+ if (device != pseudo_console && device != pseudo_root)
+ return D_NO_SUCH_DEVICE;
+ return D_INVALID_OPERATION;
+}
+
+
+/* Implementation of notify interface */
+kern_return_t
+do_mach_notify_port_deleted (mach_port_t notify,
+ mach_port_t name)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_msg_accepted (mach_port_t notify,
+ mach_port_t name)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_port_destroyed (mach_port_t notify,
+ mach_port_t port)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_no_senders (mach_port_t notify,
+ mach_port_mscount_t mscount)
+{
+ static int no_console;
+ mach_port_t foo;
+ if (notify == pseudo_master_device_port)
+ {
+ if (no_console)
+ goto bye;
+ pseudo_master_device_port = MACH_PORT_NULL;
+ return 0;
+ }
+ if (notify == pseudo_console)
+ {
+ if (mscount == console_mscount &&
+ pseudo_master_device_port == MACH_PORT_NULL)
+ {
+ bye:
+ restore_termstate ();
+ write (2, "bye\n", 4);
+ host_exit (0);
+ }
+ else
+ {
+ no_console = (mscount == console_mscount);
+
+ mach_port_request_notification (mach_task_self (), pseudo_console,
+ MACH_NOTIFY_NO_SENDERS,
+ console_mscount == mscount
+ ? mscount + 1
+ : console_mscount,
+ pseudo_console,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &foo);
+ if (foo != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), foo);
+ }
+ }
+
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_send_once (mach_port_t notify)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_dead_name (mach_port_t notify,
+ mach_port_t name)
+{
+#if 0
+ if (name == child_task && notify == bootport)
+ host_exit (0);
+#endif
+ return EOPNOTSUPP;
+}
+
+
+/* Implementation of the Hurd I/O interface, which
+ we support for the console port only. */
+
+kern_return_t
+S_io_write (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ char *data,
+ mach_msg_type_number_t datalen,
+ off_t offset,
+ mach_msg_type_number_t *amtwritten)
+{
+ if (object != pseudo_console)
+ return EOPNOTSUPP;
+
+#if 0
+ if (console_send_rights)
+ {
+ mach_port_mod_refs (mach_task_self (), pseudo_console,
+ MACH_PORT_TYPE_SEND, -console_send_rights);
+ console_send_rights = 0;
+ }
+#endif
+
+ *amtwritten = write (1, data, datalen);
+ return *amtwritten == -1 ? errno : 0;
+}
+
+kern_return_t
+S_io_read (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ char **data,
+ mach_msg_type_number_t *datalen,
+ off_t offset,
+ mach_msg_type_number_t amount)
+{
+ mach_msg_type_number_t avail;
+
+ if (object != pseudo_console)
+ return EOPNOTSUPP;
+
+#if 0
+ if (console_send_rights)
+ {
+ mach_port_mod_refs (mach_task_self (), pseudo_console,
+ MACH_PORT_TYPE_SEND, -console_send_rights);
+ console_send_rights = 0;
+ }
+#endif
+
+ pthread_spin_lock (&readlock);
+ ioctl (0, FIONREAD, &avail);
+ if (avail)
+ {
+ if (amount > *datalen)
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ *datalen = read (0, *data, amount);
+ unlock_readlock ();
+ return *datalen == -1 ? errno : 0;
+ }
+ else
+ {
+ kern_return_t err;
+ unlock_readlock ();
+ err = queue_read (IO_READ, reply_port, reply_type, amount);
+ if (err)
+ return err;
+ return MIG_NO_REPLY;
+ }
+}
+
+kern_return_t
+S_io_seek (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ off_t offset,
+ int whence,
+ off_t *newp)
+{
+ return object == pseudo_console ? ESPIPE : EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_readable (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ mach_msg_type_number_t *amt)
+{
+ if (object != pseudo_console)
+ return EOPNOTSUPP;
+ ioctl (0, FIONREAD, amt);
+ return 0;
+}
+
+kern_return_t
+S_io_set_all_openmodes (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_get_openmodes (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ int *modes)
+{
+ *modes = O_READ | O_WRITE;
+ return object == pseudo_console ? 0 : EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_set_some_openmodes (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_clear_some_openmodes (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_async (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ mach_port_t notify,
+ mach_port_t *id,
+ mach_msg_type_name_t *idtype)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_mod_owner (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ pid_t owner)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_get_owner (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ pid_t *owner)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_get_icky_async_id (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ mach_port_t *id,
+ mach_msg_type_name_t *idtype)
+{
+ return EOPNOTSUPP;
+}
+
+static kern_return_t
+io_select_common (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ struct timespec *tsp, int *type)
+{
+ struct timeval tv, *tvp;
+ fd_set r, w, x;
+ int n;
+
+ if (object != pseudo_console)
+ return EOPNOTSUPP;
+
+ FD_ZERO (&r);
+ FD_ZERO (&w);
+ FD_ZERO (&x);
+ FD_SET (0, &r);
+ FD_SET (0, &w);
+ FD_SET (0, &x);
+
+ if (tsp == NULL)
+ tvp = NULL;
+ else
+ {
+ tv.tv_sec = tsp->tv_sec;
+ tv.tv_usec = tsp->tv_nsec / 1000;
+ tvp = &tv;
+ }
+
+ n = select (1,
+ (*type & SELECT_READ) ? &r : 0,
+ (*type & SELECT_WRITE) ? &w : 0,
+ (*type & SELECT_URG) ? &x : 0,
+ tvp);
+ if (n < 0)
+ return errno;
+
+ if (! FD_ISSET (0, &r))
+ *type &= ~SELECT_READ;
+ if (! FD_ISSET (0, &w))
+ *type &= ~SELECT_WRITE;
+ if (! FD_ISSET (0, &x))
+ *type &= ~SELECT_URG;
+
+ return 0;
+}
+
+kern_return_t
+S_io_select (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ int *type)
+{
+ return io_select_common (object, reply_port, reply_type, NULL, type);
+}
+
+kern_return_t
+S_io_select_timeout (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ struct timespec ts,
+ int *type)
+{
+ return io_select_common (object, reply_port, reply_type, &ts, type);
+}
+
+kern_return_t
+S_io_stat (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ struct stat *st)
+{
+ if (object != pseudo_console)
+ return EOPNOTSUPP;
+
+ bzero (st, sizeof (struct stat));
+ st->st_blksize = 1024;
+ return 0;
+}
+
+kern_return_t
+S_io_reauthenticate (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ mach_port_t rend)
+{
+ uid_t *gu, *au;
+ gid_t *gg, *ag;
+ size_t gulen = 0, aulen = 0, gglen = 0, aglen = 0;
+ error_t err;
+
+ err = mach_port_insert_right (mach_task_self (), object, object,
+ MACH_MSG_TYPE_MAKE_SEND);
+ assert_perror (err);
+
+ if (! auth_server_authenticate (authserver,
+ rend, MACH_MSG_TYPE_COPY_SEND,
+ object, MACH_MSG_TYPE_COPY_SEND,
+ &gu, &gulen,
+ &au, &aulen,
+ &gg, &gglen,
+ &ag, &aglen))
+ {
+ mig_deallocate ((vm_address_t) gu, gulen * sizeof *gu);
+ mig_deallocate ((vm_address_t) au, aulen * sizeof *gu);
+ mig_deallocate ((vm_address_t) gg, gglen * sizeof *gu);
+ mig_deallocate ((vm_address_t) au, aulen * sizeof *gu);
+ }
+ mach_port_deallocate (mach_task_self (), rend);
+ mach_port_deallocate (mach_task_self (), object);
+
+ return 0;
+}
+
+kern_return_t
+S_io_restrict_auth (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ mach_port_t *newobject,
+ mach_msg_type_name_t *newobjtype,
+ uid_t *uids,
+ size_t nuids,
+ uid_t *gids,
+ size_t ngids)
+{
+ if (object != pseudo_console)
+ return EOPNOTSUPP;
+ *newobject = pseudo_console;
+ *newobjtype = MACH_MSG_TYPE_MAKE_SEND;
+ console_mscount++;
+ return 0;
+}
+
+kern_return_t
+S_io_duplicate (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ mach_port_t *newobj,
+ mach_msg_type_name_t *newobjtype)
+{
+ if (object != pseudo_console)
+ return EOPNOTSUPP;
+ *newobj = pseudo_console;
+ *newobjtype = MACH_MSG_TYPE_MAKE_SEND;
+ console_mscount++;
+ return 0;
+}
+
+kern_return_t
+S_io_server_version (mach_port_t object,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ char *name,
+ int *maj,
+ int *min,
+ int *edit)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_map (mach_port_t obj,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ mach_port_t *rd,
+ mach_msg_type_name_t *rdtype,
+ mach_port_t *wr,
+ mach_msg_type_name_t *wrtype)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_map_cntl (mach_port_t obj,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ mach_port_t *mem,
+ mach_msg_type_name_t *memtype)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_get_conch (mach_port_t obj,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_release_conch (mach_port_t obj,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_eofnotify (mach_port_t obj,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type)
+
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_prenotify (mach_port_t obj,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ vm_offset_t start,
+ vm_offset_t end)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_postnotify (mach_port_t obj,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ vm_offset_t start,
+ vm_offset_t end)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_readsleep (mach_port_t obj,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_readnotify (mach_port_t obj,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type)
+{
+ return EOPNOTSUPP;
+}
+
+
+kern_return_t
+S_io_sigio (mach_port_t obj,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type)
+{
+ return EOPNOTSUPP;
+}
+
+
+kern_return_t
+S_io_pathconf (mach_port_t obj,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ int name, int *value)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_identity (mach_port_t obj,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t *id,
+ mach_msg_type_name_t *idtype,
+ mach_port_t *fsid,
+ mach_msg_type_name_t *fsidtype,
+ ino_t *fileno)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_io_revoke (mach_port_t obj,
+ mach_port_t reply, mach_msg_type_name_t replyPoly)
+{
+ return EOPNOTSUPP;
+}
+
+
+
+/* Implementation of the Hurd terminal driver interface, which we only
+ support on the console device. */
+
+kern_return_t
+S_termctty_open_terminal (mach_port_t object,
+ int flags,
+ mach_port_t *result,
+ mach_msg_type_name_t *restype)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_term_getctty (mach_port_t object,
+ mach_port_t *cttyid, mach_msg_type_name_t *cttyPoly)
+{
+ static mach_port_t id = MACH_PORT_NULL;
+
+ if (object != pseudo_console)
+ return EOPNOTSUPP;
+
+ if (id == MACH_PORT_NULL)
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_DEAD_NAME, &id);
+
+ *cttyid = id;
+ *cttyPoly = MACH_MSG_TYPE_COPY_SEND;
+ return 0;
+}
+
+
+kern_return_t S_term_open_ctty
+(
+ io_t terminal,
+ pid_t pid,
+ pid_t pgrp,
+ mach_port_t *newtty,
+ mach_msg_type_name_t *newttytype
+)
+{ return EOPNOTSUPP; }
+
+kern_return_t S_term_set_nodename
+(
+ io_t terminal,
+ string_t name
+)
+{ return EOPNOTSUPP; }
+
+kern_return_t S_term_get_nodename
+(
+ io_t terminal,
+ string_t name
+)
+{ return EOPNOTSUPP; }
+
+kern_return_t S_term_get_peername
+(
+ io_t terminal,
+ string_t name
+)
+{ return EOPNOTSUPP; }
+
+kern_return_t S_term_set_filenode
+(
+ io_t terminal,
+ file_t filenode
+)
+{ return EOPNOTSUPP; }
+
+kern_return_t S_term_get_bottom_type
+(
+ io_t terminal,
+ int *ttype
+)
+{ return EOPNOTSUPP; }
+
+kern_return_t S_term_on_machdev
+(
+ io_t terminal,
+ mach_port_t machdev
+)
+{ return EOPNOTSUPP; }
+
+kern_return_t S_term_on_hurddev
+(
+ io_t terminal,
+ io_t hurddev
+)
+{ return EOPNOTSUPP; }
+
+kern_return_t S_term_on_pty
+(
+ io_t terminal,
+ io_t *ptymaster
+)
+{ return EOPNOTSUPP; }
diff --git a/boot/boot_script.c b/boot/boot_script.c
new file mode 100644
index 00000000..6fd449b2
--- /dev/null
+++ b/boot/boot_script.c
@@ -0,0 +1,791 @@
+/* Boot script parser for Mach. */
+
+/* Written by Shantanu Goel (goel@cs.columbia.edu). */
+
+#include <mach/mach_types.h>
+#if !KERNEL || OSKIT_MACH
+#include <string.h>
+#endif
+#include "boot_script.h"
+
+
+/* This structure describes a symbol. */
+struct sym
+{
+ /* Symbol name. */
+ const char *name;
+
+ /* Type of value returned by function. */
+ int type;
+
+ /* Symbol value. */
+ integer_t val;
+
+ /* For function symbols; type of value returned by function. */
+ int ret_type;
+
+ /* For function symbols; if set, execute function at the time
+ of command execution, not during parsing. A function with
+ this field set must also have `no_arg' set. Also, the function's
+ `val' argument will always be NULL. */
+ int run_on_exec;
+};
+
+/* Additional values symbols can take.
+ These are only used internally. */
+#define VAL_SYM 10 /* symbol table entry */
+#define VAL_FUNC 11 /* function pointer */
+
+/* This structure describes an argument. */
+struct arg
+{
+ /* Argument text copied verbatim. 0 if none. */
+ char *text;
+
+ /* Type of value assigned. 0 if none. */
+ int type;
+
+ /* Argument value. */
+ integer_t val;
+};
+
+/* List of commands. */
+static struct cmd **cmds = 0;
+
+/* Amount allocated for `cmds'. */
+static int cmds_alloc = 0;
+
+/* Next available slot in `cmds'. */
+static int cmds_index = 0;
+
+/* Symbol table. */
+static struct sym **symtab = 0;
+
+/* Amount allocated for `symtab'. */
+static int symtab_alloc = 0;
+
+/* Next available slot in `symtab'. */
+static int symtab_index = 0;
+
+/* Create a task and suspend it. */
+static int
+create_task (struct cmd *cmd, int *val)
+{
+ int err = boot_script_task_create (cmd);
+ *val = (int) cmd->task;
+ return err;
+}
+
+/* Resume a task. */
+static int
+resume_task (struct cmd *cmd, int *val)
+{
+ return boot_script_task_resume (cmd);
+}
+
+/* Resume a task when the user hits return. */
+static int
+prompt_resume_task (struct cmd *cmd, int *val)
+{
+ return boot_script_prompt_task_resume (cmd);
+}
+
+/* List of builtin symbols. */
+static struct sym builtin_symbols[] =
+{
+ { "task-create", VAL_FUNC, (integer_t) create_task, VAL_TASK, 0 },
+ { "task-resume", VAL_FUNC, (integer_t) resume_task, VAL_NONE, 1 },
+ { "prompt-task-resume",
+ VAL_FUNC, (integer_t) prompt_resume_task, VAL_NONE, 1 },
+};
+#define NUM_BUILTIN (sizeof (builtin_symbols) / sizeof (builtin_symbols[0]))
+
+/* Free CMD and all storage associated with it.
+ If ABORTING is set, terminate the task associated with CMD,
+ otherwise just deallocate the send right. */
+static void
+free_cmd (struct cmd *cmd, int aborting)
+{
+ if (cmd->task)
+ boot_script_free_task (cmd->task, aborting);
+ if (cmd->args)
+ {
+ int i;
+ for (i = 0; i < cmd->args_index; i++)
+ boot_script_free (cmd->args[i], sizeof *cmd->args[i]);
+ boot_script_free (cmd->args, sizeof cmd->args[0] * cmd->args_alloc);
+ }
+ if (cmd->exec_funcs)
+ boot_script_free (cmd->exec_funcs,
+ sizeof cmd->exec_funcs[0] * cmd->exec_funcs_alloc);
+ boot_script_free (cmd, sizeof *cmd);
+}
+
+/* Free all storage allocated by the parser.
+ If ABORTING is set, terminate all tasks. */
+static void
+cleanup (int aborting)
+{
+ int i;
+
+ for (i = 0; i < cmds_index; i++)
+ free_cmd (cmds[i], aborting);
+ boot_script_free (cmds, sizeof cmds[0] * cmds_alloc);
+ cmds = 0;
+ cmds_index = cmds_alloc = 0;
+
+ for (i = 0; i < symtab_index; i++)
+ boot_script_free (symtab[i], sizeof *symtab[i]);
+ boot_script_free (symtab, sizeof symtab[0] * symtab_alloc);
+ symtab = 0;
+ symtab_index = symtab_alloc = 0;
+}
+
+/* Add PTR to the list of pointers PTR_LIST, which
+ currently has ALLOC amount of space allocated to it, and
+ whose next available slot is INDEX. If more space
+ needs to to allocated, INCR is the amount by which
+ to increase it. Return 0 on success, non-zero otherwise. */
+static int
+add_list (void *ptr, void ***ptr_list, int *alloc, int *index, int incr)
+{
+ if (*index == *alloc)
+ {
+ void **p;
+
+ *alloc += incr;
+ p = boot_script_malloc (*alloc * sizeof (void *));
+ if (! p)
+ {
+ *alloc -= incr;
+ return 1;
+ }
+ if (*ptr_list)
+ {
+ memcpy (p, *ptr_list, *index * sizeof (void *));
+ boot_script_free (*ptr_list, (*alloc - incr) * sizeof (void *));
+ }
+ *ptr_list = p;
+ }
+ *(*ptr_list + *index) = ptr;
+ *index += 1;
+ return 0;
+}
+
+/* Create an argument with TEXT, value type TYPE, and value VAL.
+ Add the argument to the argument list of CMD. */
+static struct arg *
+add_arg (struct cmd *cmd, const char *text, int textlen, int type, int val)
+{
+ struct arg *arg;
+
+ arg = boot_script_malloc (sizeof (struct arg) + textlen);
+ if (arg)
+ {
+ arg->text = text == 0 ? 0 : memcpy (arg + 1, text, textlen);
+ arg->type = type;
+ arg->val = val;
+ if (add_list (arg, (void ***) &cmd->args,
+ &cmd->args_alloc, &cmd->args_index, 5))
+ {
+ boot_script_free (arg, sizeof *arg);
+ return 0;
+ }
+ }
+ return arg;
+}
+
+/* Search for the symbol NAME in the symbol table. */
+static struct sym *
+sym_lookup (const char *name)
+{
+ int i;
+
+ for (i = 0; i < symtab_index; i++)
+ if (! strcmp (name, symtab[i]->name))
+ return symtab[i];
+ return 0;
+}
+
+/* Create an entry for symbol NAME in the symbol table. */
+static struct sym *
+sym_enter (const char *name)
+{
+ struct sym *sym;
+
+ sym = boot_script_malloc (sizeof (struct sym));
+ if (sym)
+ {
+ memset (sym, 0, sizeof (struct sym));
+ sym->name = name;
+ if (add_list (sym, (void ***) &symtab, &symtab_alloc, &symtab_index, 20))
+ {
+ boot_script_free (sym, sizeof *sym);
+ return 0;
+ }
+ }
+ return sym;
+}
+
+/* Parse the command line CMDLINE. */
+int
+boot_script_parse_line (void *hook, char *cmdline)
+{
+ char *p, *q;
+ int error;
+ struct cmd *cmd;
+ struct arg *arg;
+
+ /* Extract command name. Ignore line if it lacks a command. */
+ for (p = cmdline; *p == ' ' || *p == '\t'; p++)
+ ;
+ if (*p == '#')
+ /* Ignore comment line. */
+ return 0;
+
+#if 0
+ if (*p && *p != ' ' && *p != '\t' && *p != '\n')
+ {
+ printf ("(bootstrap): %s\n", cmdline);
+ }
+#endif
+
+ for (q = p; *q && *q != ' ' && *q != '\t' && *q != '\n'; q++)
+ ;
+ if (p == q)
+ return 0;
+
+ *q++ = '\0';
+
+ /* Allocate a command structure. */
+ cmd = boot_script_malloc (sizeof (struct cmd) + (q - p));
+ if (! cmd)
+ return BOOT_SCRIPT_NOMEM;
+ memset (cmd, 0, sizeof (struct cmd));
+ cmd->hook = hook;
+ cmd->path = memcpy (cmd + 1, p, q - p);
+ p = q;
+
+ for (arg = 0;;)
+ {
+ if (! arg)
+ {
+ /* Skip whitespace. */
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ /* End of command line. */
+ if (! *p || *p == '\n')
+ {
+ /* Add command to list. */
+ if (add_list (cmd, (void ***) &cmds,
+ &cmds_alloc, &cmds_index, 10))
+ {
+ error = BOOT_SCRIPT_NOMEM;
+ goto bad;
+ }
+ return 0;
+ }
+ }
+
+ /* Look for a symbol. */
+ if (arg || (*p == '$' && (*(p + 1) == '{' || *(p + 1) == '(')))
+ {
+ char end_char = (*(p + 1) == '{') ? '}' : ')';
+ struct sym *sym = 0;
+
+ for (p += 2;;)
+ {
+ char c;
+ int i, type;
+ integer_t val;
+ struct sym *s;
+
+ /* Parse symbol name. */
+ for (q = p; *q && *q != '\n' && *q != end_char && *q != '='; q++)
+ ;
+ if (p == q || ! *q || *q == '\n'
+ || (end_char == '}' && *q != '}'))
+ {
+ error = BOOT_SCRIPT_SYNTAX_ERROR;
+ goto bad;
+ }
+ c = *q;
+ *q = '\0';
+
+ /* See if this is a builtin symbol. */
+ for (i = 0; i < NUM_BUILTIN; i++)
+ if (! strcmp (p, builtin_symbols[i].name))
+ break;
+
+ if (i < NUM_BUILTIN)
+ s = &builtin_symbols[i];
+ else
+ {
+ /* Look up symbol in symbol table.
+ If no entry exists, create one. */
+ s = sym_lookup (p);
+ if (! s)
+ {
+ s = sym_enter (p);
+ if (! s)
+ {
+ error = BOOT_SCRIPT_NOMEM;
+ goto bad;
+ }
+ }
+ }
+
+ /* Only values are allowed in ${...} constructs. */
+ if (end_char == '}' && s->type == VAL_FUNC)
+ return BOOT_SCRIPT_INVALID_SYM;
+
+ /* Check that assignment is valid. */
+ if (c == '=' && s->type == VAL_FUNC)
+ {
+ error = BOOT_SCRIPT_INVALID_ASG;
+ goto bad;
+ }
+
+ /* For function symbols, execute the function. */
+ if (s->type == VAL_FUNC)
+ {
+ if (! s->run_on_exec)
+ {
+ (error
+ = ((*((int (*) (struct cmd *, integer_t *)) s->val))
+ (cmd, &val)));
+ if (error)
+ goto bad;
+ type = s->ret_type;
+ }
+ else
+ {
+ if (add_list (s, (void ***) &cmd->exec_funcs,
+ &cmd->exec_funcs_alloc,
+ &cmd->exec_funcs_index, 5))
+ {
+ error = BOOT_SCRIPT_NOMEM;
+ goto bad;
+ }
+ type = VAL_NONE;
+ goto out;
+ }
+ }
+ else if (s->type == VAL_NONE)
+ {
+ type = VAL_SYM;
+ val = (integer_t) s;
+ }
+ else
+ {
+ type = s->type;
+ val = s->val;
+ }
+
+ if (sym)
+ {
+ sym->type = type;
+ sym->val = val;
+ }
+ else if (arg)
+ {
+ arg->type = type;
+ arg->val = val;
+ }
+
+ out:
+ p = q + 1;
+ if (c == end_char)
+ {
+ /* Create an argument if necessary.
+ We create an argument if the symbol appears
+ in the expression by itself.
+
+ NOTE: This is temporary till the boot filesystem
+ servers support arguments. When that happens,
+ symbol values will only be printed if they're
+ associated with an argument. */
+ if (! arg && end_char == '}')
+ {
+ if (! add_arg (cmd, 0, 0, type, val))
+ {
+ error = BOOT_SCRIPT_NOMEM;
+ goto bad;
+ }
+ }
+ arg = 0;
+ break;
+ }
+ if (s->type != VAL_FUNC)
+ sym = s;
+ }
+ }
+ else
+ {
+ char c;
+
+ /* Command argument; just copy the text. */
+ for (q = p;; q++)
+ {
+ if (! *q || *q == ' ' || *q == '\t' || *q == '\n')
+ break;
+ if (*q == '$' && *(q + 1) == '{')
+ break;
+ }
+ c = *q;
+ *q = '\0';
+
+ /* Add argument to list. */
+ arg = add_arg (cmd, p, q + 1 - p, VAL_NONE, 0);
+ if (! arg)
+ {
+ error = BOOT_SCRIPT_NOMEM;
+ goto bad;
+ }
+ if (c == '$')
+ p = q;
+ else
+ {
+ if (c)
+ p = q + 1;
+ else
+ p = q;
+ arg = 0;
+ }
+ }
+ }
+
+
+ bad:
+ free_cmd (cmd, 1);
+ cleanup (1);
+ return error;
+}
+
+/* Ensure that the command line buffer can accommodate LEN bytes of space. */
+#define CHECK_CMDLINE_LEN(len) \
+{ \
+ if (cmdline_alloc - cmdline_index < len) \
+ { \
+ char *ptr; \
+ int alloc, i; \
+ alloc = cmdline_alloc + len - (cmdline_alloc - cmdline_index) + 100; \
+ ptr = boot_script_malloc (alloc); \
+ if (! ptr) \
+ { \
+ error = BOOT_SCRIPT_NOMEM; \
+ goto done; \
+ } \
+ memcpy (ptr, cmdline, cmdline_index); \
+ for (i = 0; i < argc; ++i) \
+ argv[i] = ptr + (argv[i] - cmdline); \
+ boot_script_free (cmdline, cmdline_alloc); \
+ cmdline = ptr; \
+ cmdline_alloc = alloc; \
+ } \
+}
+
+/* Execute commands previously parsed. */
+int
+boot_script_exec ()
+{
+ int cmd_index;
+
+ for (cmd_index = 0; cmd_index < cmds_index; cmd_index++)
+ {
+ char **argv, *cmdline;
+ int i, argc, cmdline_alloc;
+ int cmdline_index, error, arg_index;
+ struct cmd *cmd = cmds[cmd_index];
+
+ /* Skip command if it doesn't have an associated task. */
+ if (cmd->task == 0)
+ continue;
+
+ /* Allocate a command line and copy command name. */
+ cmdline_index = strlen (cmd->path) + 1;
+ cmdline_alloc = cmdline_index + 100;
+ cmdline = boot_script_malloc (cmdline_alloc);
+ if (! cmdline)
+ {
+ cleanup (1);
+ return BOOT_SCRIPT_NOMEM;
+ }
+ memcpy (cmdline, cmd->path, cmdline_index);
+
+ /* Allocate argument vector. */
+ argv = boot_script_malloc (sizeof (char *) * (cmd->args_index + 2));
+ if (! argv)
+ {
+ boot_script_free (cmdline, cmdline_alloc);
+ cleanup (1);
+ return BOOT_SCRIPT_NOMEM;
+ }
+ argv[0] = cmdline;
+ argc = 1;
+
+ /* Build arguments. */
+ for (arg_index = 0; arg_index < cmd->args_index; arg_index++)
+ {
+ struct arg *arg = cmd->args[arg_index];
+
+ /* Copy argument text. */
+ if (arg->text)
+ {
+ int len = strlen (arg->text);
+
+ if (arg->type == VAL_NONE)
+ len++;
+ CHECK_CMDLINE_LEN (len);
+ memcpy (cmdline + cmdline_index, arg->text, len);
+ argv[argc++] = &cmdline[cmdline_index];
+ cmdline_index += len;
+ }
+
+ /* Add value of any symbol associated with this argument. */
+ if (arg->type != VAL_NONE)
+ {
+ char *p, buf[50];
+ int len;
+ mach_port_t name;
+
+ if (arg->type == VAL_SYM)
+ {
+ struct sym *sym = (struct sym *) arg->val;
+
+ /* Resolve symbol value. */
+ while (sym->type == VAL_SYM)
+ sym = (struct sym *) sym->val;
+ if (sym->type == VAL_NONE)
+ {
+ error = BOOT_SCRIPT_UNDEF_SYM;
+ goto done;
+ }
+ arg->type = sym->type;
+ arg->val = sym->val;
+ }
+
+ /* Print argument value. */
+ switch (arg->type)
+ {
+ case VAL_STR:
+ p = (char *) arg->val;
+ len = strlen (p);
+ break;
+
+ case VAL_TASK:
+ case VAL_PORT:
+ if (arg->type == VAL_TASK)
+ /* Insert send right to task port. */
+ error = boot_script_insert_task_port
+ (cmd, (task_t) arg->val, &name);
+ else
+ /* Insert send right. */
+ error = boot_script_insert_right (cmd,
+ (mach_port_t) arg->val,
+ &name);
+ if (error)
+ goto done;
+
+ i = name;
+ p = buf + sizeof (buf);
+ len = 0;
+ do
+ {
+ *--p = i % 10 + '0';
+ len++;
+ }
+ while (i /= 10);
+ break;
+
+ default:
+ error = BOOT_SCRIPT_BAD_TYPE;
+ goto done;
+ }
+ len++;
+ CHECK_CMDLINE_LEN (len);
+ memcpy (cmdline + cmdline_index, p, len - 1);
+ *(cmdline + cmdline_index + len - 1) = '\0';
+ if (! arg->text)
+ argv[argc++] = &cmdline[cmdline_index];
+ cmdline_index += len;
+ }
+ }
+
+ /* Terminate argument vector. */
+ argv[argc] = 0;
+
+ /* Execute the command. */
+ if (boot_script_exec_cmd (cmd->hook, cmd->task, cmd->path,
+ argc, argv, cmdline, cmdline_index))
+ {
+ error = BOOT_SCRIPT_EXEC_ERROR;
+ goto done;
+ }
+
+ error = 0;
+
+ done:
+ boot_script_free (cmdline, cmdline_alloc);
+ boot_script_free (argv, sizeof (char *) * (cmd->args_index + 2));
+ if (error)
+ {
+ cleanup (1);
+ return error;
+ }
+ }
+
+ for (cmd_index = 0; cmd_index < cmds_index; cmd_index++)
+ {
+ int i;
+ struct cmd *cmd = cmds[cmd_index];
+
+ /* Execute functions that want to be run on exec. */
+ for (i = 0; i < cmd->exec_funcs_index; i++)
+ {
+ struct sym *sym = cmd->exec_funcs[i];
+ int error = ((*((int (*) (struct cmd *, integer_t *)) sym->val))
+ (cmd, 0));
+ if (error)
+ {
+ cleanup (1);
+ return error;
+ }
+ }
+ }
+
+ cleanup (0);
+ return 0;
+}
+
+/* Create an entry for the variable NAME with TYPE and value VAL,
+ in the symbol table. */
+int
+boot_script_set_variable (const char *name, int type, integer_t val)
+{
+ struct sym *sym = sym_enter (name);
+
+ if (sym)
+ {
+ sym->type = type;
+ sym->val = val;
+ }
+ return sym ? 0 : 1;
+}
+
+
+/* Define the function NAME, which will return type RET_TYPE. */
+int
+boot_script_define_function (const char *name, int ret_type,
+ int (*func) (const struct cmd *cmd,
+ integer_t *val))
+{
+ struct sym *sym = sym_enter (name);
+
+ if (sym)
+ {
+ sym->type = VAL_FUNC;
+ sym->val = (integer_t) func;
+ sym->ret_type = ret_type;
+ sym->run_on_exec = ret_type == VAL_NONE;
+ }
+ return sym ? 0 : 1;
+}
+
+
+/* Return a string describing ERR. */
+char *
+boot_script_error_string (int err)
+{
+ switch (err)
+ {
+ case BOOT_SCRIPT_NOMEM:
+ return "no memory";
+
+ case BOOT_SCRIPT_SYNTAX_ERROR:
+ return "syntax error";
+
+ case BOOT_SCRIPT_INVALID_ASG:
+ return "invalid variable in assignment";
+
+ case BOOT_SCRIPT_MACH_ERROR:
+ return "mach error";
+
+ case BOOT_SCRIPT_UNDEF_SYM:
+ return "undefined symbol";
+
+ case BOOT_SCRIPT_EXEC_ERROR:
+ return "exec error";
+
+ case BOOT_SCRIPT_INVALID_SYM:
+ return "invalid variable in expression";
+
+ case BOOT_SCRIPT_BAD_TYPE:
+ return "invalid value type";
+ }
+ return 0;
+}
+
+#ifdef BOOT_SCRIPT_TEST
+#include <stdio.h>
+
+int
+boot_script_exec_cmd (void *hook,
+ mach_port_t task, char *path, int argc,
+ char **argv, char *strings, int stringlen)
+{
+ int i;
+
+ printf ("port = %d: ", (int) task);
+ for (i = 0; i < argc; i++)
+ printf ("%s ", argv[i]);
+ printf ("\n");
+ return 0;
+}
+
+void
+main (int argc, char **argv)
+{
+ char buf[500], *p;
+ int len;
+ FILE *fp;
+ mach_port_t host_port, device_port;
+
+ if (argc < 2)
+ {
+ fprintf (stderr, "Usage: %s <script>\n", argv[0]);
+ exit (1);
+ }
+ fp = fopen (argv[1], "r");
+ if (! fp)
+ {
+ fprintf (stderr, "Can't open %s\n", argv[1]);
+ exit (1);
+ }
+ host_port = 1;
+ device_port = 2;
+ boot_script_set_variable ("host-port", VAL_PORT, (int) host_port);
+ boot_script_set_variable ("device-port", VAL_PORT, (int) device_port);
+ boot_script_set_variable ("root-device", VAL_STR, (int) "hd0a");
+ boot_script_set_variable ("boot-args", VAL_STR, (int) "-ad");
+ p = buf;
+ len = sizeof (buf);
+ while (fgets (p, len, fp))
+ {
+ int i, err;
+
+ i = strlen (p) + 1;
+ err = boot_script_parse_line (0, p);
+ if (err)
+ {
+ fprintf (stderr, "error %s\n", boot_script_error_string (err));
+ exit (1);
+ }
+ p += i;
+ len -= i;
+ }
+ boot_script_exec ();
+ exit (0);
+}
+#endif /* BOOT_SCRIPT_TEST */
diff --git a/boot/boot_script.h b/boot/boot_script.h
new file mode 100644
index 00000000..62458693
--- /dev/null
+++ b/boot/boot_script.h
@@ -0,0 +1,116 @@
+/* Definitions for boot script parser for Mach. */
+
+#ifndef _boot_script_h
+#define _boot_script_h
+
+/* Written by Shantanu Goel (goel@cs.columbia.edu). */
+
+/* Error codes returned by boot_script_parse_line()
+ and boot_script_exec_cmd(). */
+#define BOOT_SCRIPT_NOMEM 1
+#define BOOT_SCRIPT_SYNTAX_ERROR 2
+#define BOOT_SCRIPT_INVALID_ASG 3
+#define BOOT_SCRIPT_MACH_ERROR 4
+#define BOOT_SCRIPT_UNDEF_SYM 5
+#define BOOT_SCRIPT_EXEC_ERROR 6
+#define BOOT_SCRIPT_INVALID_SYM 7
+#define BOOT_SCRIPT_BAD_TYPE 8
+
+/* Legal values for argument `type' to function
+ boot_script_set_variable and boot_script_define_function. */
+#define VAL_NONE 0 /* none -- function runs at exec time */
+#define VAL_STR 1 /* string */
+#define VAL_PORT 2 /* port */
+#define VAL_TASK 3 /* task port */
+
+/* This structure describes a command. */
+struct cmd
+{
+ /* Cookie passed in to boot_script_parse_line. */
+ void *hook;
+
+ /* Path of executable. */
+ char *path;
+
+ /* Task port. */
+ task_t task;
+
+ /* Argument list. */
+ struct arg **args;
+
+ /* Amount allocated for `args'. */
+ int args_alloc;
+
+ /* Next available slot in `args'. */
+ int args_index;
+
+ /* List of functions that want to be run on command execution. */
+ struct sym **exec_funcs;
+
+ /* Amount allocated for `exec_funcs'. */
+ int exec_funcs_alloc;
+
+ /* Next available slot in `exec_funcs'. */
+ int exec_funcs_index;
+};
+
+
+/* The user must define these functions, we work like malloc and free. */
+void *boot_script_malloc (unsigned int);
+void boot_script_free (void *, unsigned int);
+
+/* The user must define this function. Load the image of the
+ executable specified by PATH in TASK. Create a thread
+ in TASK and point it at the executable's entry point. Initialize
+ TASK's stack with argument vector ARGV of length ARGC whose
+ strings are STRINGS. STRINGS has length STRINGLEN.
+ Return 0 for success, non-zero otherwise. */
+int boot_script_exec_cmd (void *hook,
+ task_t task, char *path, int argc,
+ char **argv, char *strings, int stringlen);
+
+/* The user must define this function. Load the contents of FILE
+ into a fresh anonymous memory object and return the memory object port. */
+mach_port_t boot_script_read_file (const char *file);
+
+/* The user must define this functions to perform the corresponding
+ Mach task manipulations. */
+int boot_script_task_create (struct cmd *); /* task_create + task_suspend */
+int boot_script_task_resume (struct cmd *);
+int boot_script_prompt_task_resume (struct cmd *);
+int boot_script_insert_right (struct cmd *, mach_port_t, mach_port_t *namep);
+int boot_script_insert_task_port (struct cmd *, task_t, mach_port_t *namep);
+
+/* The user must define this function to clean up the `task_t'
+ returned by boot_script_task_create. */
+void boot_script_free_task (task_t task, int aborting);
+
+
+/* Parse the command line LINE. This causes the command line to be
+ converted into an internal format. Returns 0 for success, non-zero
+ otherwise.
+
+ NOTE: The parser writes into the line so it must not be a string constant.
+ It is also the responsibility of the caller not to deallocate the line
+ across calls to the parser. */
+int boot_script_parse_line (void *hook, char *cmdline);
+
+/* Execute the command lines prevously parsed.
+ Returns 0 for success, non-zero otherwise. */
+int boot_script_exec (void);
+
+/* Create an entry in the symbol table for variable NAME,
+ whose type is TYPE and value is VAL. Returns 0 on success,
+ non-zero otherwise. */
+int boot_script_set_variable (const char *name, int type, integer_t val);
+
+/* Define the function NAME, which will return type RET_TYPE. */
+int boot_script_define_function (const char *name, int ret_type,
+ int (*func) (const struct cmd *cmd,
+ integer_t *val));
+
+/* Returns a string describing the error ERR. */
+char *boot_script_error_string (int err);
+
+
+#endif /* _boot_script_h */
diff --git a/boot/frank1.ld b/boot/frank1.ld
new file mode 100644
index 00000000..9de827ae
--- /dev/null
+++ b/boot/frank1.ld
@@ -0,0 +1,94 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386",
+ "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+ SEARCH_DIR(/usr/local/i386-gnuelf/lib);
+/* Do we need any of these for elf?
+ __DYNAMIC = 0; */
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x10020;
+ .text :
+ {
+ *(.text)
+ *(.interp)
+ *(.hash)
+ *(.dynsym)
+ *(.dynstr)
+ *(.rel.text)
+ *(.rela.text)
+ *(.rel.data)
+ *(.rela.data)
+ *(.rel.rodata)
+ *(.rela.rodata)
+ *(.rel.got)
+ *(.rela.got)
+ *(.rel.ctors)
+ *(.rela.ctors)
+ *(.rel.dtors)
+ *(.rela.dtors)
+ *(.rel.init)
+ *(.rela.init)
+ *(.rel.fini)
+ *(.rela.fini)
+ *(.rel.bss)
+ *(.rela.bss)
+ *(.rel.plt)
+ *(.rela.plt)
+ *(.init)
+ *(.plt)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.fini)
+ *(.rodata)
+ *(.rodata1)
+ _etext = .;
+ PROVIDE (etext = .);
+ . = ALIGN(0x1000);
+ } =0x9090
+ . = ALIGN(0x1000);
+ .data :
+ {
+ *(.data)
+ CONSTRUCTORS
+
+ *(.data1)
+ *(.ctors)
+ *(.dtors)
+ *(.got.plt) *(.got)
+ *(.dynamic)
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ *(.sdata)
+ _edata = .;
+ PROVIDE (edata = .);
+ . = ALIGN(0x10);
+}
+ __bss_start = .;
+ .bss :
+ {
+ *(.sbss) *(.scommon)
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ _end = ALIGN(4) ;
+ PROVIDE (end = ALIGN(4));
+ }
+ /* These are needed for ELF backends which have not yet been
+ converted to the new style linker. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ /* DWARF debug sections.
+ Symbols in the .debug DWARF section are relative to the beginning of the
+ section so we begin .debug at 0. It's not clear yet what needs to happen
+ for the others. */
+ .debug 0 : { *(.debug) }
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ .line 0 : { *(.line) }
+ /* These must appear regardless of . */
+}
diff --git a/boot/frankemul.ld b/boot/frankemul.ld
new file mode 100644
index 00000000..413953ef
--- /dev/null
+++ b/boot/frankemul.ld
@@ -0,0 +1,107 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386",
+ "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+ SEARCH_DIR(/usr/local/i386-gnuelf/lib);
+/* Do we need any of these for elf?
+ __DYNAMIC = 0; */
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x10020;
+ .text :
+ {
+ *(.text)
+ *(.interp)
+ *(.hash)
+ *(.dynsym)
+ *(.dynstr)
+ *(.rel.text)
+ *(.rela.text)
+ *(.rel.data)
+ *(.rela.data)
+ *(.rel.rodata)
+ *(.rela.rodata)
+ *(.rel.got)
+ *(.rela.got)
+ *(.rel.ctors)
+ *(.rela.ctors)
+ *(.rel.dtors)
+ *(.rela.dtors)
+ *(.rel.init)
+ *(.rela.init)
+ *(.rel.fini)
+ *(.rela.fini)
+ *(.rel.bss)
+ *(.rela.bss)
+ *(.rel.plt)
+ *(.rela.plt)
+ *(.init)
+ *(.plt)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.fini)
+ *(.rodata)
+ *(.rodata1)
+*(_hurd_ioctl_handler_lists)
+*(_hurd_pgrp_changed_hook)
+*(_hurd_fork_locks)
+*(_hurd_subinit)
+*(__libc_atexit)
+*(_hurd_fd_subinit)
+*(_hurd_preinit_hook)
+*(_hurd_fork_child_hook)
+*(_hurd_fork_parent_hook)
+*(_hurd_fork_prepare_hook)
+*(_hurd_reauth_hook)
+*(_hurd_proc_subinit)
+*(__libc_subinit)
+ _etext = .;
+ PROVIDE (etext = .);
+ . = ALIGN(0x1000);
+ } =0x9090
+ . = ALIGN(0x1000);
+ .data :
+ {
+ *(.data)
+ CONSTRUCTORS
+
+ *(.data1)
+ *(.ctors)
+ *(.dtors)
+ *(.got.plt) *(.got)
+ *(.dynamic)
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ *(.sdata)
+ _edata = .;
+ PROVIDE (edata = .);
+ . = ALIGN(0x10);
+}
+ __bss_start = .;
+ .bss :
+ {
+ *(.sbss) *(.scommon)
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ _end = ALIGN(4) ;
+ PROVIDE (end = ALIGN(4));
+ }
+ /* These are needed for ELF backends which have not yet been
+ converted to the new style linker. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ /* DWARF debug sections.
+ Symbols in the .debug DWARF section are relative to the beginning of the
+ section so we begin .debug at 0. It's not clear yet what needs to happen
+ for the others. */
+ .debug 0 : { *(.debug) }
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ .line 0 : { *(.line) }
+ /* These must appear regardless of . */
+}
diff --git a/boot/mach-crt0.c b/boot/mach-crt0.c
new file mode 100644
index 00000000..0469424e
--- /dev/null
+++ b/boot/mach-crt0.c
@@ -0,0 +1,158 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement: ``This product includes software
+ * developed by the University of California, Berkeley and its contributors''
+ * in the documentation or other materials provided with the distribution
+ * and in all advertising materials mentioning features or use of this
+ * software. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)crt0.c 5.2 (Berkeley) 5/14/90";
+#endif /* not lint */
+
+/*
+ * C start up routine.
+ * Robert Henry, UCB, 20 Oct 81
+ *
+ * We make the following (true) assumptions:
+ * 1) when the kernel calls start, it does a jump to location 2,
+ * and thus avoids the register save mask. We are NOT called
+ * with a calls! see sys1.c:setregs().
+ * 2) The only register variable that we can trust is sp,
+ * which points to the base of the kernel calling frame.
+ * Do NOT believe the documentation in exec(2) regarding the
+ * values of fp and ap.
+ * 3) We can allocate as many register variables as we want,
+ * and don't have to save them for anybody.
+ * 4) Because of the ways that asm's work, we can't have
+ * any automatic variables allocated on the stack, because
+ * we must catch the value of sp before any automatics are
+ * allocated.
+ */
+
+#include <mach/machine/asm.h>
+
+int __data_start = 0;
+char **environ = (char **)0;
+#ifdef paranoid
+static int fd;
+#endif paranoid
+
+int (*mach_init_routine)();
+int (*_cthread_init_routine)();
+int (*_cthread_exit_routine)();
+int (*_monstartup_routine)();
+int (*_StrongBox_init_routine)();
+int errno = 0;
+int exit();
+
+extern int main();
+
+extern unsigned char etext;
+int _start()
+{
+ __label__ eprol;
+ struct kframe {
+ int kargc;
+ char *kargv[1]; /* size depends on kargc */
+ char kargstr[1]; /* size varies */
+ char kenvstr[1]; /* size varies */
+ };
+ /*
+ * ALL REGISTER VARIABLES!!!
+ */
+ register struct kframe *kfp; /* r10 */
+ register char **targv;
+ register char **argv;
+
+#ifdef lint
+ kfp = 0;
+ initcode = initcode = 0;
+#else not lint
+#define Entry_sp() \
+({ int _spl__, _tmp1__; \
+ asm volatile("leal 4(%%ebp), %0" : "=r" (_spl__) : "r" (_tmp1__)); \
+ _spl__; })
+
+ kfp = (struct kframe *)Entry_sp();
+#endif not lint
+ for (argv = targv = &kfp->kargv[0]; *targv++; /* void */)
+ /* void */ ;
+ if (targv >= (char **)(*argv))
+ --targv;
+ environ = targv;
+ if (mach_init_routine)
+ (void) mach_init_routine();
+
+ eprol:
+#ifdef paranoid
+ /*
+ * The standard I/O library assumes that file descriptors 0, 1, and 2
+ * are open. If one of these descriptors is closed prior to the start
+ * of the process, I/O gets very confused. To avoid this problem, we
+ * insure that the first three file descriptors are open before calling
+ * main(). Normally this is undefined, as it adds two unnecessary
+ * system calls.
+ */
+ do {
+ fd = open("/dev/null", 2);
+ } while (fd >= 0 && fd < 3);
+ close(fd);
+#endif paranoid
+
+
+ if (_cthread_init_routine) {
+ int new_sp;
+ new_sp = (*_cthread_init_routine)();
+ if (new_sp) {
+ asm volatile("movl %0, %%esp" : : "g" (new_sp) );
+ }
+ }
+ if (_StrongBox_init_routine) (*_StrongBox_init_routine)();
+
+ if (_monstartup_routine) {
+ _monstartup_routine(&&eprol, &etext);
+ }
+
+ (* (_cthread_exit_routine ? _cthread_exit_routine : exit))
+ (main(kfp->kargc, argv, targv));
+}
diff --git a/boot/sigvec.S b/boot/sigvec.S
new file mode 100644
index 00000000..cc7bb94e
--- /dev/null
+++ b/boot/sigvec.S
@@ -0,0 +1,23 @@
+#include <i386/asm.h>
+
+.text
+ENTRY(sigreturn)
+ movl $0x67,%eax
+ lcall $0x7,$0x0
+ jb error
+ ret
+ENTRY(_sigreturn)
+ addl $0xc,%esp
+ call EXT(sigreturn)
+ ret
+ENTRY(sigvec)
+ movl $0x6c,%eax
+ movl $EXT(_sigreturn),%edx
+ orl $0x80000000,%edx
+ lcall $0x7,$0x0
+ jb error
+ ret
+error:
+ movl %eax,EXT(errno)
+ movl $-1,%eax
+ ret
diff --git a/boot/syscall.S b/boot/syscall.S
new file mode 100644
index 00000000..a04ab28d
--- /dev/null
+++ b/boot/syscall.S
@@ -0,0 +1,35 @@
+/* Temporary....
+ Copyright (C) 1993, 1995 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <i386/asm.h>
+
+/* .globl EXT(errno)*/
+.text
+ENTRY(syscall)
+ pop %ecx
+ pop %eax
+ push %ecx
+ lcall $7, $0
+ push %ecx /* Restore stack position. */
+ jb error
+ ret
+error:
+ movl %eax,EXT(errno)
+ movl $-1,%eax
+ ret
diff --git a/boot/userland-boot.c b/boot/userland-boot.c
new file mode 100644
index 00000000..d048c00e
--- /dev/null
+++ b/boot/userland-boot.c
@@ -0,0 +1,108 @@
+/* boot_script.c support functions for running in a Mach user task.
+ Copyright (C) 2001 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <mach.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <error.h>
+#include "boot_script.h"
+
+void *
+boot_script_malloc (unsigned int size)
+{
+ return malloc (size);
+}
+
+void
+boot_script_free (void *ptr, unsigned int size)
+{
+ free (ptr);
+}
+
+
+int
+boot_script_task_create (struct cmd *cmd)
+{
+ error_t err = task_create (mach_task_self (), 0, &cmd->task);
+ if (err)
+ {
+ error (0, err, "%s: task_create", cmd->path);
+ return BOOT_SCRIPT_MACH_ERROR;
+ }
+ err = task_suspend (cmd->task);
+ if (err)
+ {
+ error (0, err, "%s: task_resume", cmd->path);
+ return BOOT_SCRIPT_MACH_ERROR;
+ }
+ return 0;
+}
+
+int
+boot_script_task_resume (struct cmd *cmd)
+{
+ error_t err = task_resume (cmd->task);
+ if (err)
+ {
+ error (0, err, "%s: task_resume", cmd->path);
+ return BOOT_SCRIPT_MACH_ERROR;
+ }
+ return 0;
+}
+
+int
+boot_script_prompt_task_resume (struct cmd *cmd)
+{
+ char xx[5];
+
+ printf ("Hit return to resume %s...", cmd->path);
+ fgets (xx, sizeof xx, stdin);
+
+ return boot_script_task_resume (cmd);
+}
+
+void
+boot_script_free_task (task_t task, int aborting)
+{
+ if (aborting)
+ task_terminate (task);
+ else
+ mach_port_deallocate (mach_task_self (), task);
+}
+
+int
+boot_script_insert_right (struct cmd *cmd, mach_port_t port, mach_port_t *name)
+{
+ error_t err = mach_port_insert_right (cmd->task,
+ port, port, MACH_MSG_TYPE_COPY_SEND);
+ if (err)
+ {
+ error (0, err, "%s: mach_port_insert_right", cmd->path);
+ return BOOT_SCRIPT_MACH_ERROR;
+ }
+ *name = port;
+ return 0;
+}
+
+int
+boot_script_insert_task_port (struct cmd *cmd, task_t task, mach_port_t *name)
+{
+ return boot_script_insert_right (cmd, task, name);
+}
diff --git a/boot/ux.c b/boot/ux.c
new file mode 100644
index 00000000..7239762c
--- /dev/null
+++ b/boot/ux.c
@@ -0,0 +1,303 @@
+/* Hacks to make boot work under UX
+
+ Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <mach.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "ux.h"
+
+#if 0
+static int (* const _sc)(int, ...) = &syscall;
+int _sc_print = 1;
+
+#define syscall(num, args...) \
+ ({ int _rv, _num = (num), _pr = _sc_print; \
+ _sc_print = 0; \
+ if (_pr) printf ("syscall (%d) start\r\n", _num); \
+ _rv = (*_sc) (_num , ##args); \
+ if (_pr) printf ("syscall (%d) end\r\n", _num); \
+ _sc_print = _pr; \
+ _rv; \
+ })
+#endif
+
+extern void __mach_init ();
+void (*mach_init_routine)() = __mach_init;
+
+/* These will prevent the Hurd-ish versions from being used */
+
+struct free_reply_port
+{
+ mach_port_t port;
+ struct free_reply_port *next;
+};
+static struct free_reply_port *free_reply_ports = NULL;
+static pthread_spinlock_t free_reply_ports_lock = PTHREAD_SPINLOCK_INITIALIZER;
+
+mach_port_t __mig_get_reply_port ()
+{
+ pthread_spin_lock (&free_reply_ports_lock);
+ if (free_reply_ports == NULL)
+ {
+ pthread_spin_unlock (&free_reply_ports_lock);
+ return __mach_reply_port ();
+ }
+ else
+ {
+ struct free_reply_port *frp = free_reply_ports;
+ mach_port_t reply_port = frp->port;
+ free_reply_ports = free_reply_ports->next;
+ pthread_spin_unlock (&free_reply_ports_lock);
+ free (frp);
+ return reply_port;
+ }
+}
+mach_port_t mig_get_reply_port ()
+{
+ return __mig_get_reply_port ();
+}
+void __mig_put_reply_port (mach_port_t port)
+{
+ struct free_reply_port *frp = malloc (sizeof (struct free_reply_port));
+ frp->port = port;
+ pthread_spin_lock (&free_reply_ports_lock);
+ frp->next = free_reply_ports;
+ free_reply_ports = frp;
+ pthread_spin_unlock (&free_reply_ports_lock);
+}
+void mig_put_reply_port (mach_port_t port)
+{
+ __mig_put_reply_port (port);
+}
+void __mig_dealloc_reply_port (mach_port_t port)
+{
+ mach_port_mod_refs (__mach_task_self (), port,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+}
+void mig_dealloc_reply_port (mach_port_t port)
+{
+ __mig_dealloc_reply_port (port);
+}
+void __mig_init (void *stack) {}
+void mig_init (void *stack) {}
+
+int
+task_by_pid (int pid)
+{
+ return syscall (-33, pid);
+}
+
+int
+write (int fd,
+ const void *buf,
+ int buflen)
+{
+ return syscall (4, fd, buf, buflen);
+}
+
+int
+read (int fd,
+ void *buf,
+ int buflen)
+{
+ return syscall (3, fd, buf, buflen);
+}
+
+int
+open (const char *name,
+ int flags,
+ int mode)
+{
+ return syscall (5, name, flags, mode);
+}
+
+int
+uxfstat (int fd, struct uxstat *buf)
+{
+ return syscall (62, fd, buf);
+}
+
+int
+close (int fd)
+{
+ return syscall (6, fd);
+}
+
+int
+lseek (int fd,
+ int off,
+ int whence)
+{
+ return syscall (19, fd, off, whence);
+}
+
+int
+uxexit (int code)
+{
+ return syscall (1, code);
+}
+
+int
+getpid ()
+{
+ return syscall (20);
+}
+
+int
+ioctl (int fd, int code, void *buf)
+{
+ return syscall (54, fd, code, buf);
+}
+
+int
+sigblock (int mask)
+{
+ return syscall (109, mask);
+}
+
+int
+sigsetmask (int mask)
+{
+ return syscall (110, mask);
+}
+
+int
+sigpause (int mask)
+{
+ return syscall (111, mask);
+}
+
+
+#if 0
+void
+sigreturn ()
+{
+ asm volatile ("movl $0x67,%eax\n"
+ "lcall $0x7, $0x0\n"
+ "ret");
+}
+
+void
+_sigreturn ()
+{
+ asm volatile ("addl $0xc, %%esp\n"
+ "call %0\n"
+ "ret"::"m" (sigreturn));
+}
+
+int
+sigvec (int sig, struct sigvec *vec, struct sigvec *ovec)
+{
+ asm volatile ("movl $0x6c,%%eax\n"
+ "movl %0, %%edx\n"
+ "orl $0x80000000, %%edx\n"
+ "lcall $0x7,$0x0\n"
+ "ret"::"g" (_sigreturn));
+}
+#else
+int sigvec ();
+#endif
+
+void get_privileged_ports (mach_port_t *host_port, mach_port_t *device_port)
+{
+ *host_port = task_by_pid (-1);
+ *device_port = task_by_pid (-2);
+}
+
+/* A *really* stupid printf that only understands %s & %d. */
+int
+printf (const char *fmt, ...)
+{
+ va_list ap;
+ const char *p = fmt, *q = p;
+
+ void flush (const char *new)
+ {
+ if (p > q)
+ write (1, q, p - q);
+ q = p = new;
+ }
+
+ va_start (ap, fmt);
+ while (*p)
+ if (*p == '%' && p[1] == 's')
+ {
+ char *str = va_arg (ap, char *);
+ flush (p + 2);
+ write (1, str, strlen (str));
+ }
+ else if (*p == '%' && p[1] == 'd')
+ {
+ int i = va_arg (ap, int);
+ char rbuf[20], *e = rbuf + sizeof (rbuf), *b = e;
+
+ if (i == 0)
+ *--b = '0';
+ else
+ while (i)
+ {
+ *--b = i % 10 + '0';
+ i /= 10;
+ }
+
+ flush (p + 2);
+ write (1, b, e - b);
+ }
+ else
+ p++;
+ va_end (ap);
+
+ flush (0);
+
+ return 0;
+}
+
+static struct sgttyb term_sgb;
+static int localbits;
+
+void
+init_termstate ()
+{
+ struct sgttyb sgb;
+ int bits;
+ ioctl (0, TIOCGETP, &term_sgb);
+ ioctl (0, TIOCLGET, &localbits);
+ /* Enter raw made. Rather than try and interpret these bits,
+ we just do what emacs does in .../emacs/src/sysdep.c for
+ an old style terminal driver. */
+ bits = localbits | LDECCTQ | LLITOUT | LPASS8 | LNOFLSH;
+ ioctl (0, TIOCLSET, &bits);
+ sgb = term_sgb;
+ sgb.sg_flags &= ~ECHO;
+ sgb.sg_flags |= RAW | ANYP;
+ ioctl (0, TIOCSETN, &sgb);
+}
+
+void
+restore_termstate ()
+{
+ ioctl (0, TIOCLSET, &localbits);
+ ioctl (0, TIOCSETN, &term_sgb);
+}
diff --git a/boot/ux.h b/boot/ux.h
new file mode 100644
index 00000000..d3787c54
--- /dev/null
+++ b/boot/ux.h
@@ -0,0 +1,114 @@
+/* Hacks to make boot work under UX
+
+ Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define sigmask(m) (1 << ((m)-1))
+
+#define IOCPARM_MASK 0x7f
+#define IOC_OUT 0x40000000
+#define IOC_IN 0x80000000
+#define _IOR(x,y,t) (IOC_OUT|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y)
+#define _IOW(x,y,t) (IOC_IN|((sizeof(t)&IOCPARM_MASK)<<16)|(x<<8)|y)
+#define FIONREAD _IOR('f', 127, int)
+#define FIOASYNC _IOW('f', 125, int)
+#define TIOCGETP _IOR('t', 8, struct sgttyb)
+#define TIOCLGET _IOR('t', 124, int)
+#define TIOCLSET _IOW('t', 125, int)
+#define TIOCSETN _IOW('t', 10, struct sgttyb)
+#define LDECCTQ 0x4000
+#define LLITOUT 0x0020
+#define LPASS8 0x0800
+#define LNOFLSH 0x8000
+#define RAW 0x0020
+#define ANYP 0x00c0
+#define ECHO 8
+
+
+struct sgttyb
+{
+ char unused[4];
+ short sg_flags;
+};
+
+#define SIGIO 23
+
+struct sigvec
+{
+ void (*sv_handler)();
+ int sv_mask;
+ int sv_flags;
+};
+
+struct uxstat
+ {
+ short int st_dev; /* Device containing the file. */
+ __ino_t st_ino; /* File serial number. */
+ unsigned short int st_mode; /* File mode. */
+ __nlink_t st_nlink; /* Link count. */
+ unsigned short int st_uid; /* User ID of the file's owner. */
+ unsigned short int st_gid; /* Group ID of the file's group.*/
+ short int st_rdev; /* Device number, if device. */
+ __off_t st_size; /* Size of file, in bytes. */
+ __time_t st_atime; /* Time of last access. */
+ unsigned long int st_atime_usec;
+ __time_t st_mtime; /* Time of last modification. */
+ unsigned long int st_mtime_usec;
+ __time_t st_ctime; /* Time of last status change. */
+ unsigned long int st_ctime_usec;
+ unsigned long int st_blksize; /* Optimal block size for I/O. */
+ unsigned long int st_blocks; /* Number of 512-byte blocks allocated. */
+ long int st_spare[2];
+ };
+
+void get_privileged_ports (mach_port_t *host_port, mach_port_t *device_port);
+
+/* We can't include <unistd.h> for this, because that will fight witho
+ our definitions of syscalls below. */
+int syscall (int, ...);
+
+int open (const char *name, int flags, int mode);
+int write (int fd, const void *buf, int len);
+int read (int fd, void *buf, int len);
+int uxfstat (int fd, struct uxstat *buf);
+int close (int fd);
+int lseek (int fd, int off, int whence);
+int uxexit (int code);
+int getpid ();
+int ioctl (int fd, int code, void *buf);
+int sigblock (int mask);
+int sigsetmask (int mask);
+int sigpause (int mask);
+int sigvec (int sig, struct sigvec *vec, struct sigvec *ovec);
+
+#undef O_RDONLY
+#undef O_WRONLY
+#undef O_RDWR
+#define O_RDONLY 0
+#define O_WRONLY 1
+#define O_RDWR 2
+
+#define host_exit(c) uxexit(c)
+
+typedef struct uxstat host_stat_t;
+#define host_fstat(fd, st) uxfstat (fd, st)
+
+void init_stdio ();
+
+#undef errno
+int errno;
diff --git a/build.mk.in b/build.mk.in
new file mode 100644
index 00000000..cfc4c3dc
--- /dev/null
+++ b/build.mk.in
@@ -0,0 +1,9 @@
+# @configure_input@
+# Generic subdirectory makefile for Hurd configured outside ${srcdir}.
+
+top_srcdir = @top_srcdir@
+srcdir = @srcdir@
+
+VPATH = $(srcdir)
+
+include $(srcdir)/Makefile
diff --git a/build.mkcf.in b/build.mkcf.in
new file mode 100644
index 00000000..17452771
--- /dev/null
+++ b/build.mkcf.in
@@ -0,0 +1,3 @@
+
+# Get the make rules from the source directory.
+include $(top_srcdir)/Makeconf
diff --git a/config.guess b/config.guess
new file mode 100755
index 00000000..f475ceb4
--- /dev/null
+++ b/config.guess
@@ -0,0 +1,1534 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright 1992-2013 Free Software Foundation, Inc.
+
+timestamp='2013-02-12'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner.
+#
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+#
+# Please send patches with a ChangeLog entry to config-patches@gnu.org.
+
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright 1992-2013 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ sh5el) machine=sh5le-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ELF__
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit ;;
+ *:Bitrig:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
+ exit ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ *:ekkoBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+ exit ;;
+ *:SolidBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+ exit ;;
+ macppc:MirBSD:*:*)
+ echo powerpc-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ *:MirBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ exitcode=$?
+ trap '' 0
+ exit $exitcode ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit ;;
+ *:z/VM:*:*)
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit ;;
+ arm*:riscos:*:*|arm*:RISCOS:*:*)
+ echo arm-unknown-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7; exit ;;
+ esac ;;
+ s390x:SunOS:*:*)
+ echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux${UNAME_RELEASE}
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ eval $set_cc_for_build
+ SUN_ARCH="i386"
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH="x86_64"
+ fi
+ fi
+ echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten${UNAME_RELEASE}
+ exit ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c &&
+ dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`$dummy $dummyarg` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+ then
+ echo "$SYSTEM_NAME"
+ else
+ echo rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+ *:AIX:*:[4567])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ eval $set_cc_for_build
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep -q __LP64__
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo unknown-hitachi-hiuxwe2
+ exit ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:FreeBSD:*:*)
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ case ${UNAME_PROCESSOR} in
+ amd64)
+ echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ *)
+ echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ esac
+ exit ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit ;;
+ *:MINGW64*:*)
+ echo ${UNAME_MACHINE}-pc-mingw64
+ exit ;;
+ *:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit ;;
+ i*:MSYS*:*)
+ echo ${UNAME_MACHINE}-pc-msys
+ exit ;;
+ i*:windows32*:*)
+ # uname -m includes "-pc" on this system.
+ echo ${UNAME_MACHINE}-mingw32
+ exit ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit ;;
+ *:Interix*:*)
+ case ${UNAME_MACHINE} in
+ x86)
+ echo i586-pc-interix${UNAME_RELEASE}
+ exit ;;
+ authenticamd | genuineintel | EM64T)
+ echo x86_64-unknown-interix${UNAME_RELEASE}
+ exit ;;
+ IA64)
+ echo ia64-unknown-interix${UNAME_RELEASE}
+ exit ;;
+ esac ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit ;;
+ 8664:Windows_NT:*)
+ echo x86_64-pc-mks
+ exit ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit ;;
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ echo x86_64-unknown-cygwin
+ exit ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit ;;
+ aarch64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ aarch64_be:Linux:*:*)
+ UNAME_MACHINE=aarch64_be
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep -q ld.so.1
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit ;;
+ arm*:Linux:*:*)
+ eval $set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ else
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+ else
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabihf
+ fi
+ fi
+ exit ;;
+ avr32*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ cris:Linux:*:*)
+ echo ${UNAME_MACHINE}-axis-linux-gnu
+ exit ;;
+ crisv32:Linux:*:*)
+ echo ${UNAME_MACHINE}-axis-linux-gnu
+ exit ;;
+ frv:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ hexagon:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ i*86:Linux:*:*)
+ LIBC=gnu
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+ exit ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m32r*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ mips:Linux:*:* | mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef ${UNAME_MACHINE}
+ #undef ${UNAME_MACHINE}el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=${UNAME_MACHINE}el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=${UNAME_MACHINE}
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ ;;
+ or1k:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ or32:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ padre:Linux:*:*)
+ echo sparc-unknown-linux-gnu
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ tile*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ vax:Linux:*:*)
+ echo ${UNAME_MACHINE}-dec-linux-gnu
+ exit ;;
+ x86_64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ xtensa*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit ;;
+ i*86:syllable:*:*)
+ echo ${UNAME_MACHINE}-pc-syllable
+ exit ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configury will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo ${UNAME_MACHINE}-stratus-vos
+ exit ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
+ x86_64:Haiku:*:*)
+ echo x86_64-unknown-haiku
+ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-7:SUPER-UX:*:*)
+ echo sx7-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-8:SUPER-UX:*:*)
+ echo sx8-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-8R:SUPER-UX:*:*)
+ echo sx8r-nec-superux${UNAME_RELEASE}
+ exit ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+ case $UNAME_PROCESSOR in
+ i386)
+ eval $set_cc_for_build
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ UNAME_PROCESSOR="x86_64"
+ fi
+ fi ;;
+ unknown) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+ NEO-?:NONSTOP_KERNEL:*:*)
+ echo neo-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSE-*:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit ;;
+ *:DragonFly:*:*)
+ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "${UNAME_MACHINE}" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+ V*) echo vax-dec-vms ; exit ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ echo i386-pc-xenix
+ exit ;;
+ i*86:skyos:*:*)
+ echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+ exit ;;
+ i*86:rdos:*:*)
+ echo ${UNAME_MACHINE}-pc-rdos
+ exit ;;
+ i*86:AROS:*:*)
+ echo ${UNAME_MACHINE}-pc-aros
+ exit ;;
+ x86_64:VMkernel:*:*)
+ echo ${UNAME_MACHINE}-unknown-esx
+ exit ;;
+esac
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ c34*)
+ echo c34-convex-bsd
+ exit ;;
+ c38*)
+ echo c38-convex-bsd
+ exit ;;
+ c4*)
+ echo c4-convex-bsd
+ exit ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+and
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config.make.in b/config.make.in
new file mode 100644
index 00000000..0f1390a7
--- /dev/null
+++ b/config.make.in
@@ -0,0 +1,104 @@
+# @configure_input@
+
+package-version := @PACKAGE_VERSION@
+# What version of the Hurd is this? For compatibility (libraries' SONAMEs),
+# hard-code this to 0.3 instead of coupling with PACKAGE_VERSION.
+hurd-version := 0.3
+
+# Machine architecture.
+machine = @host_cpu@
+asm_syntax = @asm_syntax@
+
+# Build options.
+build-profiled = @enable_profile@
+build-static = @enable_static_progs@
+boot-store-types = @boot_store_types@
+
+# Prefix prepended to names of machine-independent installed files.
+prefix = @prefix@
+# Prefix prepended to names of machine-dependent installed files.
+exec_prefix = @exec_prefix@
+
+# Directories where things get installed.
+hurddir = ${exec_prefix}/hurd
+libdir = @libdir@
+bindir = @bindir@
+sbindir = @sbindir@
+includedir = @includedir@
+libexecdir = @libexecdir@
+bootdir = ${exec_prefix}/boot
+infodir = @infodir@
+sysconfdir = @sysconfdir@
+localstatedir = @localstatedir@
+sharedstatedir = @sharedstatedir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+
+# All of those directories together:
+installationdirlist = $(hurddir) $(libdir) $(bindir) $(sbindir) \
+ $(includedir) $(libexecdir) $(bootdir) $(infodir) $(sysconfdir) \
+ $(localstatedir) $(sharedstatedir)
+
+
+# How to run compilation tools.
+CC = @CC@
+CPP = $(CC) -E -x c # We need this option when input file names are not *.c.
+LD = @LD@
+OBJCOPY = @OBJCOPY@
+AR = @AR@
+RANLIB = @RANLIB@
+MIG = @MIG@
+MIGCOM = $(MIG) -cc cat - /dev/null
+AWK = @AWK@
+SED = @SED@
+LEX = @LEX@
+YACC = @YACC@
+
+# Compilation flags. Append these to the definitions already made by
+# the specific Makefile.
+CPPFLAGS += @CPPFLAGS@ @DEFS@
+CFLAGS += @CFLAGS@
+LDFLAGS += @LDFLAGS@
+
+gnu89-inline-CFLAGS = @libc_cv_gnu89_inline@
+
+# `yes' or `no' to indicate if ld --version-script is available.
+VERSIONING = @VERSIONING@
+
+# How to link against Parted libraries, if at all.
+PARTED_LIBS = @PARTED_LIBS@
+
+# How to compile and link against ncursesw.
+LIBNCURSESW = @LIBNCURSESW@
+NCURSESW_INCLUDE = @NCURSESW_INCLUDE@
+
+# How to compile and link against X11.
+HAVE_X11 = @have_x11@
+X11_CFLAGS = @X11_CFLAGS@
+X11_LIBS = @X11_LIBS@
+XKB_BASE = @XKB_BASE@
+X11_KEYSYMDEF_H = @X11_KEYSYMDEF_H@
+
+# How to compile and link against libdaemon.
+HAVE_DAEMON = @HAVE_DAEMON@
+libdaemon_CFLAGS = @libdaemon_CFLAGS@
+libdaemon_LIBS = @libdaemon_LIBS@
+
+# How to compile and link against libbz2.
+HAVE_LIBBZ2 = @HAVE_LIBBZ2@
+
+# How to compile and link against libz.
+HAVE_LIBZ = @HAVE_LIBZ@
+
+# How to compile and link against libblkid.
+HAVE_BLKID = @HAVE_BLKID@
+libblkid_CFLAGS = @libblkid_CFLAGS@
+libblkid_LIBS = @libblkid_LIBS@
+
+# Whether Sun RPC support is available.
+HAVE_SUN_RPC = @HAVE_SUN_RPC@
+
+# Installation tools.
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
diff --git a/config.sub b/config.sub
new file mode 100755
index 00000000..872199ac
--- /dev/null
+++ b/config.sub
@@ -0,0 +1,1788 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright 1992-2013 Free Software Foundation, Inc.
+
+timestamp='2013-02-12'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+
+
+# Please send patches with a ChangeLog entry to config-patches@gnu.org.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright 1992-2013 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit ;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
+ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
+ knetbsd*-gnu* | netbsd*-gnu* | \
+ kopensolaris*-gnu* | \
+ storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ android-linux)
+ os=-linux-android
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis | -knuth | -cray | -microblaze*)
+ os=
+ basic_machine=$1
+ ;;
+ -bluegene*)
+ os=-cnk
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco6)
+ os=-sco5v6
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco5v6*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*178)
+ os=-lynxos178
+ ;;
+ -lynx*5)
+ os=-lynxos5
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | aarch64 | aarch64_be \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | am33_2.0 \
+ | arc \
+ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
+ | avr | avr32 \
+ | be32 | be64 \
+ | bfin \
+ | c4x | clipper \
+ | d10v | d30v | dlx | dsp16xx \
+ | epiphany \
+ | fido | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | hexagon \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
+ | le32 | le64 \
+ | lm32 \
+ | m32c | m32r | m32rle | m68000 | m68k | m88k \
+ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64octeon | mips64octeonel \
+ | mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipsr5900 | mipsr5900el \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | moxie \
+ | mt \
+ | msp430 \
+ | nds32 | nds32le | nds32be \
+ | nios | nios2 | nios2eb | nios2el \
+ | ns16k | ns32k \
+ | open8 \
+ | or1k | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle \
+ | pyramid \
+ | rl78 | rx \
+ | score \
+ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+ | spu \
+ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
+ | ubicom32 \
+ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
+ | we32k \
+ | x86 | xc16x | xstormy16 | xtensa \
+ | z8k | z80)
+ basic_machine=$basic_machine-unknown
+ ;;
+ c54x)
+ basic_machine=tic54x-unknown
+ ;;
+ c55x)
+ basic_machine=tic55x-unknown
+ ;;
+ c6x)
+ basic_machine=tic6x-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+ ms1)
+ basic_machine=mt-unknown
+ ;;
+
+ strongarm | thumb | xscale)
+ basic_machine=arm-unknown
+ ;;
+ xgate)
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ xscaleeb)
+ basic_machine=armeb-unknown
+ ;;
+
+ xscaleel)
+ basic_machine=armel-unknown
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | aarch64-* | aarch64_be-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* | avr32-* \
+ | be32-* | be64-* \
+ | bfin-* | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* \
+ | clipper-* | craynv-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | hexagon-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
+ | le32-* | le64-* \
+ | lm32-* \
+ | m32c-* | m32r-* | m32rle-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
+ | microblaze-* | microblazeel-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64octeon-* | mips64octeonel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64r5900-* | mips64r5900el-* \
+ | mips64vr-* | mips64vrel-* \
+ | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* \
+ | mips64vr5000-* | mips64vr5000el-* \
+ | mips64vr5900-* | mips64vr5900el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipsr5900-* | mipsr5900el-* \
+ | mipstx39-* | mipstx39el-* \
+ | mmix-* \
+ | mt-* \
+ | msp430-* \
+ | nds32-* | nds32le-* | nds32be-* \
+ | nios-* | nios2-* | nios2eb-* | nios2el-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | open8-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
+ | pyramid-* \
+ | rl78-* | romp-* | rs6000-* | rx-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+ | sparclite-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
+ | tahoe-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tile*-* \
+ | tron-* \
+ | ubicom32-* \
+ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
+ | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xc16x-* | xps100-* \
+ | xstormy16-* | xtensa*-* \
+ | ymp-* \
+ | z8k-* | z80-*)
+ ;;
+ # Recognize the basic CPU types without company name, with glob match.
+ xtensa*)
+ basic_machine=$basic_machine-unknown
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ abacus)
+ basic_machine=abacus-unknown
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amd64)
+ basic_machine=x86_64-pc
+ ;;
+ amd64-*)
+ basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aros)
+ basic_machine=i386-pc
+ os=-aros
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ blackfin)
+ basic_machine=bfin-unknown
+ os=-linux
+ ;;
+ blackfin-*)
+ basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ bluegene*)
+ basic_machine=powerpc-ibm
+ os=-cnk
+ ;;
+ c54x-*)
+ basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c55x-*)
+ basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c6x-*)
+ basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ cegcc)
+ basic_machine=arm-unknown
+ os=-cegcc
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | j90)
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
+ craynv)
+ basic_machine=craynv-cray
+ os=-unicosmp
+ ;;
+ cr16 | cr16-*)
+ basic_machine=cr16-unknown
+ os=-elf
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ crisv32 | crisv32-* | etraxfs*)
+ basic_machine=crisv32-axis
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ crx)
+ basic_machine=crx-unknown
+ os=-elf
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ decsystem10* | dec10*)
+ basic_machine=pdp10-dec
+ os=-tops10
+ ;;
+ decsystem20* | dec20*)
+ basic_machine=pdp10-dec
+ os=-tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dicos)
+ basic_machine=i686-pc
+ os=-dicos
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ os=-msdosdjgpp
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ os=-linux
+ ;;
+ m68knommu-*)
+ basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ microblaze*)
+ basic_machine=microblaze-xilinx
+ ;;
+ mingw64)
+ basic_machine=x86_64-pc
+ os=-mingw64
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ mingw32ce)
+ basic_machine=arm-unknown
+ os=-mingw32ce
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ ms1-*)
+ basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+ ;;
+ msys)
+ basic_machine=i386-pc
+ os=-msys
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ nacl)
+ basic_machine=le32-unknown
+ os=-nacl
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ neo-tandem)
+ basic_machine=neo-tandem
+ ;;
+ nse-tandem)
+ basic_machine=nse-tandem
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ openrisc | openrisc-*)
+ basic_machine=or32-unknown
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=-os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ parisc)
+ basic_machine=hppa-unknown
+ os=-linux
+ ;;
+ parisc-*)
+ basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pc98)
+ basic_machine=i386-pc
+ ;;
+ pc98-*)
+ basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium | p5 | k5 | k6 | nexgen | viac3)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon | athlon_*)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2 | pentiumiii | pentium3)
+ basic_machine=i686-pc
+ ;;
+ pentium4)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium4-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc | ppcbe) basic_machine=powerpc-unknown
+ ;;
+ ppc-* | ppcbe-*)
+ basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rdos | rdos64)
+ basic_machine=x86_64-pc
+ os=-rdos
+ ;;
+ rdos32)
+ basic_machine=i386-pc
+ os=-rdos
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sb1)
+ basic_machine=mipsisa64sb1-unknown
+ ;;
+ sb1el)
+ basic_machine=mipsisa64sb1el-unknown
+ ;;
+ sde)
+ basic_machine=mipsisa32-sde
+ os=-elf
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=-seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sh5el)
+ basic_machine=sh5le-unknown
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparclite-wrs | simso-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ strongarm-* | thumb-*)
+ basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=-unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=-unicos
+ ;;
+ tile*)
+ basic_machine=$basic_machine-unknown
+ os=-linux-gnu
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=-tops20
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=-tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ os=-mingw32
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ xscale-* | xscalee[bl]-*)
+ basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ z80-*-coff)
+ basic_machine=z80-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ mmix)
+ basic_machine=mmix-knuth
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
+ basic_machine=sh-unknown
+ ;;
+ sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -auroraux)
+ os=-auroraux
+ ;;
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+ | -sym* | -kopensolaris* | -plan9* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* | -aros* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+ | -bitrig* | -openbsd* | -solidbsd* \
+ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* | -cegcc* \
+ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
+ | -linux-newlib* | -linux-musl* | -linux-uclibc* \
+ | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto-qnx*)
+ ;;
+ -nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux-dietlibc)
+ os=-linux-dietlibc
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -os400*)
+ os=-os400
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -syllable*)
+ os=-syllable
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -nova*)
+ os=-rtmk-nova
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -tpf*)
+ os=-tpf
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -aros*)
+ os=-aros
+ ;;
+ -zvmoe)
+ os=-zvmoe
+ ;;
+ -dicos*)
+ os=-dicos
+ ;;
+ -nacl*)
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ score-*)
+ os=-elf
+ ;;
+ spu-*)
+ os=-elf
+ ;;
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ hexagon-*)
+ os=-elf
+ ;;
+ tic54x-*)
+ os=-coff
+ ;;
+ tic55x-*)
+ os=-coff
+ ;;
+ tic6x-*)
+ os=-coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mep-*)
+ os=-elf
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ or1k-*)
+ os=-elf
+ ;;
+ or32-*)
+ os=-coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-haiku)
+ os=-haiku
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-knuth)
+ os=-mmixware
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -cnk*|-aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -os400*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -tpf*)
+ vendor=ibm
+ ;;
+ -vxsim* | -vxworks* | -windiss*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config/Makefile b/config/Makefile
new file mode 100644
index 00000000..00501598
--- /dev/null
+++ b/config/Makefile
@@ -0,0 +1,53 @@
+# Copyright (C) 1996, 1997, 1999, 2004, 2012 Free Software Foundation, Inc.
+# Written by Michael I. Bushnell, p/BSG.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+
+dir := config
+makemode := misc
+
+# Files that are copied verbatim to $(sysconfdir). But we never want
+# to overwrite an existing file. Print a warning for such files.
+# If override_conf is set to `t' then install even on top of existing
+# files.
+SYSCONFFILES = motd ttys
+
+installed_conf = $(addprefix $(sysconfdir)/,$(SYSCONFFILES))
+
+LOGINDOTS = .bash_login .bashrc .hushlogin .profile README
+installed_logins = $(addprefix $(sysconfdir)/login/,$(LOGINDOTS))
+
+FORCE:
+
+ifeq ($(override_conf),t)
+$(installed_conf): FORCE
+$(installed_logins): FORCE
+endif
+
+include ../Makeconf
+
+install: $(sysconfdir) $(sysconfdir)/login \
+ $(installed_conf) $(installed_logins)
+
+$(sysconfdir)/login: %:
+ mkdir -p $@
+
+$(installed_logins): $(sysconfdir)/login/%: login-%
+ $(INSTALL_DATA) $< $(sysconfdir)/login/$*
+
+$(installed_conf): $(sysconfdir)/%: %
+ $(INSTALL_DATA) $< $(sysconfdir)/$*
diff --git a/config/login-.bash_login b/config/login-.bash_login
new file mode 100644
index 00000000..eecad52a
--- /dev/null
+++ b/config/login-.bash_login
@@ -0,0 +1,2 @@
+. ~/.bashrc
+. ~/.profile
diff --git a/config/login-.bashrc b/config/login-.bashrc
new file mode 100644
index 00000000..cc2447fb
--- /dev/null
+++ b/config/login-.bashrc
@@ -0,0 +1,11 @@
+# login -- a normal login
+alias login='exec login -p -R-p -R-aHOME -R-aMOTD -R-e_LOGIN_RETRY=yes'
+alias logon=login
+alias l=login
+alias su=login
+
+# quick login -- don't act like a login shell, but do cd to $HOME
+alias ql='exec login -pSL -aMOTD -R-p -R-aHOME -R-aMOTD -R-e_LOGIN_RETRY=yes'
+
+alias help='cat $HOME/README'
+alias '?'=help
diff --git a/config/login-.hushlogin b/config/login-.hushlogin
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/config/login-.hushlogin
diff --git a/config/login-.profile b/config/login-.profile
new file mode 100644
index 00000000..92a5e309
--- /dev/null
+++ b/config/login-.profile
@@ -0,0 +1,3 @@
+PS1='login> '
+test "$_LOGIN_RETRY" || echo "Use \`login USER' to login, or \`help' for more information."
+unset _LOGIN_RETRY
diff --git a/config/login-README b/config/login-README
new file mode 100644
index 00000000..9855ecf5
--- /dev/null
+++ b/config/login-README
@@ -0,0 +1,10 @@
+This is a hurd login shell; it is a normal user shell, but has no user
+privileges. To login as a user with a userid of USER, use the command:
+
+ login USER
+
+other useful commands:
+
+ ql USER # quick login -- just start a shell in USER's home directory
+ su USER # set the id of the current (login) shell to USER
+ # (use the `unsu' command to undo this, or just exit the shell)
diff --git a/config/motd b/config/motd
new file mode 100644
index 00000000..03e10be9
--- /dev/null
+++ b/config/motd
@@ -0,0 +1 @@
+This is the Hurd. Welcome.
diff --git a/config/ttys b/config/ttys
new file mode 100644
index 00000000..2b3464d1
--- /dev/null
+++ b/config/ttys
@@ -0,0 +1,14 @@
+# Programs to be maintained on terminal lines. init runs these programs,
+# and restartsthem when they die. Note that in GNU, unlike in BSD, there
+# is no need to list pseudo-ttys here.
+
+# name program type status comments
+
+console "/libexec/getty 9600" mach-gnu-color on secure trusted console
+tty1 "/libexec/getty 38400" hurd on secure trusted console
+tty2 "/libexec/getty 38400" hurd on secure trusted console
+tty3 "/libexec/getty 38400" hurd on secure trusted console
+tty4 "/libexec/getty 38400" hurd on secure trusted console
+tty5 "/libexec/getty 38400" hurd on secure trusted console
+tty6 "/libexec/getty 38400" hurd on secure trusted console
+#com0 "/libexec/getty 9600" dialup on secure
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 00000000..f8856dbe
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,344 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_PREREQ(2.54) dnl Minimum Autoconf version required.
+AC_INIT([GNU Hurd], [0.5], [bug-hurd@gnu.org])
+AC_CONFIG_SRCDIR([hurd/hurd_types.h]) dnl File to look for in srcdir.
+
+AC_PREFIX_DEFAULT() dnl Default to empty prefix, not /usr/local.
+
+AC_CANONICAL_HOST
+case "$host_os" in
+gnu*) ;;
+none) AC_MSG_ERROR([
+*** You must specify a host of $host_cpu-gnu or $host_cpu-$host_vendor-gnu
+*** to configure; you will need to use the same host specification
+*** to configure other packages for the GNU/Hurd system.]) ;;
+*) AC_MSG_ERROR([this is the gnu os, host cannot be $host_os
+*** Host configuration must be \`MACHINE-gnu' or \`MACHINE-VENDOR-gnu'.
+*** To cross-compile, you must specify both --host and --build;
+*** for example \`--build=$host --host=$host_cpu-gnu'.
+*** Run $0 --help for more information.]) ;;
+esac
+
+case "$host_cpu" in
+alpha*)
+ asm_syntax=alpha
+ ;;
+arm*)
+ asm_syntax=arm
+ ;;
+m68k | m680?0)
+ asm_syntax=m68k
+ ;;
+mips*)
+ asm_syntax=mips
+ ;;
+i?86)
+ asm_syntax=i386
+ ;;
+powerpc*)
+ asm_syntax=ppc
+ ;;
+sparc64* | ultrasparc*)
+ asm_syntax=sparc64
+ ;;
+sparc*)
+ asm_syntax=sparc
+ ;;
+*)
+ asm_syntax="$host_cpu"
+ ;;
+esac
+AC_SUBST(asm_syntax)
+
+test -r "$srcdir/libthreads/$asm_syntax/cthreads.h" || {
+ AC_MSG_WARN([unsupported CPU type $host_cpu])
+}
+
+AC_ARG_ENABLE(profile,
+[ --disable-profile do not build profiled libraries and programs])
+AC_SUBST(enable_profile)
+
+define([default_static],['ext2fs'])dnl
+AC_ARG_ENABLE(static-progs,
+[ --enable-static-progs=PROGRAMS...
+ build statically-linked PROGRAM.static versions
+ of (only) the listed programs ]dnl
+changequote(',')[default_static]changequote([,]))
+case "$enable_static_progs" in
+'no') enable_static_progs= ;; # we got --disable-static
+'') enable_static_progs=default_static ;;
+esac
+# Convert comma/space-separated list into space-separated list.
+enable_static_progs=`echo "$enable_static_progs" | sed 's/[[, ]][[, ]]*/ /g'`
+AC_SUBST(enable_static_progs)
+
+[# Don't needlessly overwrite files that whose contents haven't changed. This
+# helps for avoinding unneccessary recompilation cycles when keeping
+# cross-compilation toolchains up-to-date. Thus, unconditionally use the
+# supplied `install-sh', as the GNU Coreutils one doesn't provide this
+# functionality yet (TODO: change that). TODO: $ac_abs_top_builddir et al. are
+# not yet available here, that's why we use `readlink' (but only if available).
+INSTALL="$SHELL $(readlink -f "$ac_install_sh")"\ -C || unset INSTALL]
+AC_PROG_INSTALL
+AC_PROG_AWK
+AC_PROG_SED
+
+if test "x$cross_compiling" = "xyes"; then
+ # It may be that we don't have a working libc yet, for instance
+ # because we're bootstrapping the cross-compilation tool chain.
+ # Thus, use this undocumented Autoconf macro designed for this.
+ AC_NO_EXECUTABLES
+ AC_MSG_WARN("cross-compiling, disabling linking")
+fi
+AC_PROG_CC
+# Require GCC.
+if test x$GCC != xyes; then
+ AC_MSG_ERROR([this code uses GNU C extensions, you must compile with GCC])
+fi
+
+AC_CHECK_TOOL(LD, ld)
+AC_CHECK_TOOL(OBJCOPY, objcopy)
+AC_CHECK_TOOL(AR, ar)
+AC_CHECK_TOOL(RANLIB, ranlib)
+AC_CHECK_TOOL(MIG, mig)
+# Require MiG.
+if test x${MIG} = x; then
+ AC_MSG_ERROR([
+*** You need GNU MiG to compile the GNU Hurd, please see
+*** http://www.gnu.org/software/hurd/mig.html for further details, or
+*** download it directly from the main GNU server (ftp.gnu.org) or any
+*** GNU mirror.])
+fi
+
+dnl Let these propagate from the environment.
+AC_SUBST(CFLAGS) AC_SUBST(CPPFLAGS) AC_SUBST(LDFLAGS)
+
+hurd_MIG_RETCODE
+
+# See if --version-script is available.
+AC_CACHE_CHECK(for ld --version-script, hurd_cv_ld_version_script_option, [dnl
+cat > conftest.c <<\EOF
+void foobar() {}
+EOF
+cat > conftest.map <<\EOF
+VERS_1 {
+ global: sym;
+};
+
+VERS_2 {
+ global: sym;
+} VERS_1;
+EOF
+
+if AC_TRY_COMMAND([eval $ac_compile 1>&AS_MESSAGE_LOG_FD()]) &&
+ AC_TRY_COMMAND([${CC-cc} $CFLAGS -shared -o conftest.so conftest.o
+ -nostartfiles -nostdlib
+ -Wl,--version-script,conftest.map
+ 1>&AS_MESSAGE_LOG_FD()]); then
+ hurd_cv_ld_version_script_option=yes
+else
+ hurd_cv_ld_version_script_option=no
+fi
+rm -f conftest*])
+
+# See if libc was built with --enable-libio.
+AC_CACHE_CHECK([for libio],
+ hurd_cv_libio,
+ AC_TRY_COMPILE([#include <stdio.h>
+#ifndef _STDIO_USES_IOSTREAM
+# error No libio found.
+#endif],,
+ hurd_cv_libio=yes,
+ hurd_cv_libio=no))
+
+# The versions of the symbols in libthreads have to match those in
+# libc.so. Since the symbols in a libc that includes libio will be
+# versioned differently from the ones in a libc that uses stdio, this
+# isn't easy to accomplish. Instead we leave things unversioned if
+# libio isn't found.
+if test $hurd_cv_libio = yes; then
+ VERSIONING=$hurd_cv_ld_version_script_option
+else
+ VERSIONING=no
+fi
+AC_SUBST(VERSIONING)
+
+
+# From glibc HEAD, 2007-11-07.
+AC_CACHE_CHECK(for -fgnu89-inline, libc_cv_gnu89_inline, [dnl
+cat > conftest.c <<EOF
+int foo;
+#ifdef __GNUC_GNU_INLINE__
+main () { return 0;}
+#else
+#error
+#endif
+EOF
+if AC_TRY_COMMAND([${CC-cc} $CFLAGS $CPPFLAGS -S -std=gnu99 -fgnu89-inline
+ -o conftest.s conftest.c 1>&AS_MESSAGE_LOG_FD])
+then
+ libc_cv_gnu89_inline=yes
+else
+ libc_cv_gnu89_inline=no
+fi
+rm -f conftest*])
+if test $libc_cv_gnu89_inline = yes; then
+ libc_cv_gnu89_inline=-fgnu89-inline
+else
+ libc_cv_gnu89_inline=
+fi
+AC_SUBST(libc_cv_gnu89_inline)
+
+
+# Insist on libparted unless the user declines explicitely
+AC_ARG_WITH([parted],
+ [AS_HELP_STRING([--without-parted], [disable user-space partition stores])],
+ [],
+ [with_parted=yes])
+
+PARTED_LIBS=
+AC_DEFUN([PARTED_FAIL], [
+ AC_MSG_FAILURE([Please install required libraries or use --without-parted.])
+])
+AS_IF([test "x$with_parted" != xno], [
+ AC_CHECK_HEADER([parted/parted.h],
+ [AC_DEFINE(HAVE_PARTED_PARTED_H)],
+ [PARTED_FAIL])
+ AC_CHECK_LIB([parted], [ped_device_read], [], [PARTED_FAIL])
+ AC_CHECK_LIB([uuid], [uuid_generate], [], [PARTED_FAIL])
+ AC_CHECK_LIB([dl], [dlopen], [], [PARTED_FAIL])
+ PARTED_LIBS="-lparted -luuid -ldl"
+])
+AC_SUBST([PARTED_LIBS])
+
+AC_ARG_WITH([libbz2],
+ [AS_HELP_STRING([--without-libbz2], [disable libbz2])], , [with_libbz2=yes])
+
+AS_IF([test "x$with_libbz2" != xno], [
+ AC_CHECK_LIB(bz2, BZ2_bzCompress, [HAVE_LIBBZ2=1], [true])
+])
+AC_SUBST([HAVE_LIBBZ2])
+
+AC_ARG_WITH([libz],
+ [AS_HELP_STRING([--without-libz], [disable libz])], , [with_libz=yes])
+
+AS_IF([test "x$with_libz" != xno], [
+ AC_CHECK_LIB(z, deflate, [HAVE_LIBZ=1], [true])
+])
+AC_SUBST([HAVE_LIBZ])
+
+AC_ARG_ENABLE(boot-store-types,
+[ --enable-boot-store-types=TYPES...
+ list of store types included in statically
+ linked filesystems used for booting])dnl
+if test -z "$enable_boot_store_types"; then
+ boot_store_types='device remap'
+ test -z "$PARTED_LIBS" || boot_store_types="$boot_store_types part"
+ test -z "$HAVE_LIBBZ2" || boot_store_types="$boot_store_types bunzip2"
+ test -z "$HAVE_LIBZ" || boot_store_types="$boot_store_types gunzip"
+elif test "x$enable_boot_store_types" = xno; then
+ AC_MSG_WARN([you probably wanted --disable-static-progs])
+else
+ boot_store_types="$enable_boot_store_types"
+fi
+AC_SUBST(boot_store_types)dnl
+AC_MSG_CHECKING(boot store types)
+AC_MSG_RESULT($boot_store_types)
+
+# Check for ncursesw, which is needed for the console-curses client.
+hurd_LIB_NCURSESW
+
+AC_PROG_LEX
+AC_PROG_YACC
+AS_IF([test "$LEX" = ":" -o "$YACC" = ":"], [
+have_x11=no
+AC_MSG_WARN([lex or yacc is mising, XKB will be disabled.])
+],[
+# Check for those Xorg modules needed for keyboard mappings.
+PKG_CHECK_MODULES([X11], [x11 xproto],
+ [ have_x11=yes
+ pkg_failed=no
+ AC_MSG_CHECKING([for xkb base])
+ _PKG_CONFIG([XKB_BASE], [variable=xkb_base], [xkeyboard-config])
+ AS_IF([test $pkg_failed = no],
+ [XKB_BASE="$pkg_cv_XKB_BASE"
+ AC_MSG_RESULT([$XKB_BASE])],
+ [XKB_BASE="$datadir/X11/xkb"
+ AC_MSG_RESULT([(default) $XKB_BASE])])
+ pkg_failed=no
+ AC_MSG_CHECKING([for X11 prefix])
+ _PKG_CONFIG([X11_PREFIX], [variable=prefix], [x11])
+ AS_IF([test $pkg_failed = no],
+ [X11_PREFIX="$pkg_cv_X11_PREFIX"
+ AC_MSG_RESULT([$X11_PREFIX])],
+ [X11_PREFIX="$prefix"
+ AC_MSG_RESULT([(default) $X11_PREFIX])])
+
+ have_keysymdef_h=no
+ AC_CHECK_HEADER([X11/keysymdef.h],
+ [AC_MSG_CHECKING([for X11/keysymdef.h absolute location])
+ AC_PREPROC_IFELSE([AC_LANG_SOURCE([[#include <X11/keysymdef.h>]])],
+ [[X11_KEYSYMDEF_H=`$SED -n 's%^[^"]*"\([^"]*X11/keysymdef.h\)".*$%\1%p' conftest.i`]
+ # did the sed magic above work?
+ AS_IF([test -f "$X11_KEYSYMDEF_H"],
+ [have_keysymdef_h=yes],
+ [X11_KEYSYMDEF_H=not-found])
+ ],
+ [X11_KEYSYMDEF_H=not-found])
+ AC_MSG_RESULT([$X11_KEYSYMDEF_H])
+ ])
+ AS_IF([test $have_keysymdef_h = no],
+ [AC_MSG_WARN([X11/keysymdef.h was not found, XKB will be disabled.])
+ have_x11=no])
+ ], [have_x11=no])
+])
+AC_SUBST([have_x11])
+AC_SUBST([XKB_BASE])
+AC_DEFINE_UNQUOTED([X11_PREFIX], "$X11_PREFIX")
+AC_SUBST([X11_KEYSYMDEF_H])
+
+# Check for Sun RPC headers and library.
+AC_CHECK_HEADER([rpc/types.h], [HAVE_SUN_RPC=yes], [HAVE_SUN_RPC=no])
+AC_SEARCH_LIBS([clnt_create], [], [:], [HAVE_SUN_RPC=no])
+AC_SUBST([HAVE_SUN_RPC])
+
+if test -f ./$ac_unique_file; then
+ # Configuring in source directory; don't create any Makefiles.
+ makefiles=
+else
+ # We are configuring in a separate build tree.
+ # Create a Makefile in the top-level build directory and
+ # one for each subdirectory Makefile in the source.
+ makefiles="Makeconf:build.mkcf.in \
+ `cd $srcdir; for file in Makefile */Makefile; do \
+ echo ${file}:build.mk.in; done`"
+fi
+
+AC_ARG_WITH([libdaemon],
+ [AS_HELP_STRING([--without-libdaemon], [disable libdaemon use in console client])],
+ [],
+ [with_libdaemon=yes])
+
+AS_IF([test "x$with_libdaemon" != xno], [
+ PKG_CHECK_MODULES([libdaemon], [libdaemon],
+ [AC_DEFINE([HAVE_DAEMON], [1], [Use libdaemon])],
+ [true])
+])
+AC_SUBST([libdaemon_LIBS])
+AC_SUBST([libdaemon_CFLAGS])
+
+PKG_CHECK_MODULES([libblkid], [blkid],
+ [AC_DEFINE([HAVE_BLKID], [1], [Use libblkid])],
+ [true])
+AC_SUBST([libblkid_LIBS])
+AC_SUBST([libblkid_CFLAGS])
+
+AC_CONFIG_FILES([config.make ${makefiles}])
+AC_OUTPUT
+
+dnl Local Variables:
+dnl comment-start: "dnl "
+dnl comment-end: ""
+dnl comment-start-skip: "\\bdnl\\b\\s *"
+dnl compile-command: "autoconf"
+dnl End:
diff --git a/console-client/Makefile b/console-client/Makefile
new file mode 100644
index 00000000..f576cbeb
--- /dev/null
+++ b/console-client/Makefile
@@ -0,0 +1,133 @@
+#
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2004,
+# 2005, 2008, 2010 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := console-client
+makemode := utilities
+
+targets = console
+CONSOLE_SRCS = console.c timer.c driver.c trans.c
+VGA_SO_SRCS = bdf.c vga-dynafont.c vga-dynacolor.c vga-support.c vga.c
+PC_KBD_SO_SRCS = pc-kbd.c kbd-repeat.c
+PC_MOUSE_SO_SRCS = pc-mouse.c
+GENERIC_SPEAKER_SO_SRCS = generic-speaker.c
+CURRENT_VCS_SO_SRCS = current-vcs.c
+ifneq ($(LIBNCURSESW),)
+NCURSESW_SO_SRCS = ncursesw.c
+endif
+SRCS = $(CONSOLE_SRCS) \
+ $(VGA_SO_SRCS) $(PC_KBD_SO_SRCS) $(PC_MOUSE_SO_SRCS) \
+ $(GENERIC_SPEAKER_SO_SRCS) $(CURRENT_VCS_SO_SRCS) $(NCURSESW_SO_SRCS) \
+ $(XKB_SRCS)
+
+VPATH += $(srcdir)/xkb
+OBJS = $(addsuffix .o,$(basename $(notdir $(SRCS)))) kdioctlServer.o
+HURDLIBS = cons ports netfs fshelp iohelp ihash shouldbeinlibc
+LDLIBS = -ldl -lpthread $(libdaemon_LIBS)
+module-dir = $(libdir)/hurd/console
+console-LDFLAGS = -Wl,-E
+
+CPPFLAGS += -I$(CURDIR)/xkb -I$(srcdir)/xkb $(libdaemon_CFLAGS)
+LFLAGS = -i
+YFLAGS = -by
+XKB_DATA_FILES = keymap/hurd types/hurd symbols/hurd
+
+# In seeking, thou shalt find it!
+CPPFLAGS += -DQUAERENDO_INVENIETIS
+
+include ../Makeconf
+
+driver-CPPFLAGS = -D'CONSOLE_DEFPATH="$(module-dir)\0"' \
+ -D'CONSOLE_SONAME_SUFFIX=".so.$(hurd-version)"'
+driver-DEPS = $(..)config.make
+
+console: $(CONSOLE_SRCS:.c=.o) \
+ ../libnetfs/libnetfs.a ../libfshelp/libfshelp.a \
+ ../libcons/libcons.a ../libports/libports.a \
+ ../libshouldbeinlibc/libshouldbeinlibc.a
+
+modules = vga pc_kbd generic_speaker pc_mouse current_vcs
+
+vga-CPPFLAGS = -DDEFAULT_VGA_FONT_DIR=\"${datadir}/hurd/\"
+vga.so.$(hurd-version): $(patsubst %.c,%_pic.o,$(VGA_SO_SRCS))
+pc_kbd.so.$(hurd-version): $(patsubst %.c,%_pic.o,$(PC_KBD_SO_SRCS)) \
+ kdioctlServer_pic.o
+pc_mouse.so.$(hurd-version): $(patsubst %.c,%_pic.o,$(PC_MOUSE_SO_SRCS))
+generic_speaker.so.$(hurd-version): $(patsubst %.c,%_pic.o,$(GENERIC_SPEAKER_SO_SRCS))
+current_vcs.so.$(hurd-version): $(patsubst %.c,%_pic.o,$(CURRENT_VCS_SO_SRCS))
+
+ifneq ($(LIBNCURSESW),)
+modules += ncursesw
+ncursesw.so.$(hurd-version): $(patsubst %.c,%_pic.o,$(NCURSESW_SO_SRCS))
+ncursesw-CPPFLAGS = $(NCURSESW_INCLUDE)
+ncursesw-LDLIBS = $(LIBNCURSESW)
+endif
+
+all: $(addsuffix .so.$(hurd-version), $(modules))
+
+cleantarg += $(addsuffix .so.$(hurd-version), $(modules))
+
+install: $(module-dir) $(addprefix $(module-dir)/,$(addsuffix .so.$(hurd-version),$(modules)))
+
+$(module-dir):
+ @$(MKINSTALLDIRS) $@
+
+$(module-dir)/%: %
+ $(INSTALL_DATA) $< $@
+
+# You can use this rule to make a dynamically-loadable version of any
+# of the modules.
+%.so.$(hurd-version):
+ $(CC) -shared -Wl,-soname=$@ -o $@ $(rpath) \
+ $(CFLAGS) $($*-CFLAGS) $(LDFLAGS) \
+ '-Wl,-(' $($*-LDLIBS) '-Wl,-)' $^
+
+lex.c: lex.l parser.tab.h
+parser.tab.h: parser.y
+ if $(YACC) $(YFLAGS) -d $<; then \
+ mv y.tab.h $@; \
+ rm y.tab.c; \
+ fi
+
+XKB_SRCS = xkb/compose.c xkb/kstoucs.c xkb/parser.y xkb/lex.l \
+ xkb/xkb.c xkb/xkbdata.c xkb/xkbtimer.c
+ifeq ($(HAVE_X11),yes)
+XKB_UNITS = $(basename $(notdir $(XKB_SRCS)))
+pc_kbd.so.$(hurd-version): $(addsuffix _pic.o,$(XKB_UNITS))
+pc-kbd-CFLAGS = -DXKB_SUPPORT -DXKB_DATA_DIR=\"$(XKB_BASE)\" $(X11_CFLAGS)
+$(foreach XKB_UNIT, $(XKB_UNITS), $(eval $(XKB_UNIT)-CFLAGS = $(X11_CFLAGS)))
+compose-CFLAGS += -DDATADIR=\"$(datadir)\"
+pc_kbd-LDLIBS = $(X11_LIBS)
+install: $(XKB_BASE) $(addprefix $(XKB_BASE)/, $(XKB_DATA_FILES))
+
+$(XKB_BASE):
+ @$(MKINSTALLDIRS) $@
+
+$(XKB_BASE)/%: xkb/xkb-data/%
+ $(INSTALL_DATA) $< $@
+
+kstoucs.o: xkb/kstoucs_map.c
+kstoucs_pic.o: xkb/kstoucs_map.c
+xkb/kstoucs_map.c: $(X11_KEYSYMDEF_H) $(srcdir)/xkb/kstoucs_map.sh
+ mkdir -p xkb ; \
+ SED=$(SED) \
+ AWK=$(AWK) \
+ sh $(srcdir)/xkb/kstoucs_map.sh \
+ < $< \
+ > map.tmp && \
+ mv map.tmp $@
+endif
diff --git a/console-client/bdf.c b/console-client/bdf.c
new file mode 100644
index 00000000..f62a2473
--- /dev/null
+++ b/console-client/bdf.c
@@ -0,0 +1,990 @@
+/* bdf.c - Parser for the Adobe Glyph Bitmap Distribution Format (BDF).
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann <marcus@gnu.org>.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <malloc.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <search.h>
+
+#include "bdf.h"
+
+
+/* Return a statically allocated string describing the BDF error value
+ ERR. */
+const char *
+bdf_strerror (bdf_error_t err)
+{
+ switch (err)
+ {
+ case BDF_NO_ERROR:
+ return "Success";
+ case BDF_SYSTEM_ERROR:
+ return "System error";
+ case BDF_SYNTAX_ERROR:
+ return "Syntax error";
+ case BDF_INVALID_ARGUMENT:
+ return "Invalid Argument";
+ case BDF_COUNT_MISMATCH:
+ return "Count mismatch";
+ default:
+ return "Unknown error";
+ }
+}
+
+
+/* Copy the string starting from ARG and return the pointer to it in
+ STRING. If QUOTED is true, outer double quotes are stripped, and
+ two consecutive double quotes within the string are replaced by one
+ douple quotes. */
+static bdf_error_t
+parse_string (char *arg, char **string, int quoted)
+{
+ if (quoted)
+ {
+ char *value = ++arg;
+ do
+ {
+ value = strchr (value, '"');
+ if (!value)
+ return BDF_INVALID_ARGUMENT;
+ else if (*(value + 1) == '"')
+ {
+ char *copyp = value++;
+ while (*(++copyp))
+ *(copyp - 1) = *copyp;
+ *(copyp - 1) = 0;
+ }
+ }
+ while (*value != '"' || *(value + 1) == '"');
+ *value = 0;
+ }
+
+ *string = strdup (arg);
+ if (!*string)
+ {
+ errno = ENOMEM;
+ return BDF_SYSTEM_ERROR;
+ }
+ return 0;
+}
+
+
+/* Parse the string STR for format TEMPLATE, and require an exact
+ match. Set err if a parsing error occurs. TEMPLATE must be a
+ string constant. */
+#define parse_template(str, template, rest...) \
+ do \
+ { \
+ int parse_template_count = -1; \
+ sscanf (str, template " %n", rest, &parse_template_count); \
+ if (parse_template_count == -1 || *(str + parse_template_count)) \
+ err = BDF_SYNTAX_ERROR; \
+ } \
+ while (0)
+
+
+#define hex2nr(c) (((c) >= '0' && (c) <= '9') ? (c) - '0' \
+ : (((c) >= 'a' && (c) <= 'f') ? (c) - 'a' + 10 \
+ : (((c) >= 'A' && (c) <= 'F') ? (c) - 'A' + 10 : 0)))
+
+/* Convert a two-digit hex number starting from LINE to a char and
+ return the result in BYTE. */
+static bdf_error_t
+parse_hexbyte (char *line, unsigned char *byte)
+{
+ if (!isxdigit (*line) || !isxdigit(*(line + 1)))
+ return BDF_SYNTAX_ERROR;
+ else
+ *byte = (hex2nr (*line) << 4) + hex2nr (*(line + 1));
+ return 0;
+}
+
+
+/* Like getline(), but keeps track of line count in COUNT, skips
+ COMMENT lines, and removes whitespace at the beginning and end of a
+ line. */
+static int
+next_line (char **line, int *size, FILE *file, int *count)
+{
+ int len;
+
+ do
+ {
+ len = getline (line, size, file);
+ if (len >= 0)
+ {
+ char *cline = *line;
+ if (count)
+ (*count)++;
+ if (!strncmp (cline, "COMMENT", 7))
+ len = 0;
+ else
+ while (len > 0 && (cline[len - 1] == '\n' || cline[len - 1] == '\r'
+ || cline[len - 1] == ' '
+ || cline[len - 1] == '\t'))
+ cline[--len] = 0;
+ }
+ }
+ while (len <= 0 && !feof (file) && !ferror (file));
+ return len;
+}
+
+
+/* Isolate the next white-space separated argument from the current
+ line, and set ARGP to the beginning of the next argument. It is an
+ error if there is no further argument. */
+static bdf_error_t
+find_arg (char **argp)
+{
+ char *arg = *argp;
+
+ arg = strchr (arg, ' ');
+ if (arg)
+ {
+ *(arg++) = 0;
+ while (*arg == ' ' || *arg == '\t')
+ arg++;
+ }
+ if (!arg || !*arg)
+ return BDF_SYNTAX_ERROR;
+ *argp = arg;
+ return 0;
+}
+
+
+/* Read the font from stream FILE, and return it in FONT. If
+ LINECOUNT is not zero, it will contain the number of lines in the
+ file at success, and the line an error occurred at failure. */
+bdf_error_t
+bdf_read (FILE *filep, bdf_font_t *font, int *linecount)
+{
+ bdf_error_t err = 0;
+ char *line = 0;
+ int line_size = 0;
+ int len;
+ int done = 0;
+ bdf_font_t bdf;
+ struct
+ {
+ /* Current line. */
+ enum { START, FONT, PROPERTIES, GLYPHS, GLYPH, BITMAP } location;
+ /* The number of properties parsed so far. */
+ int properties;
+ /* The number of glyphs parsed so far. */
+ int glyphs;
+
+ /* True if we have seen a SIZE keyword so far. */
+ unsigned int has_size : 1;
+ /* True if we have seen a FONTBOUNDINGBOX keyword so far. */
+ unsigned int has_fbbx : 1;
+ /* True if we have seen a METRICSSET keyword so far. */
+ unsigned int has_metricsset : 1;
+
+ /* Current glyph. */
+ struct bdf_glyph *glyph;
+ /* True if we have seen an ENCODING keyword for the glyph. */
+ unsigned int glyph_has_encoding : 1;
+ /* True if we have seen an BBX keyword for the glyph. */
+ unsigned int glyph_has_bbx : 1;
+ /* Width of the glyph in bytes. */
+ unsigned int glyph_bwidth;
+ /* Height of the glyph in pixel. */
+ unsigned int glyph_bheight;
+ /* How many bitmap lines have been parsed already. */
+ unsigned int glyph_blines;
+ } parser = { location: START, properties: 0, glyphs: 0,
+ has_size: 0, has_fbbx: 0 };
+
+ bdf = calloc (1, sizeof *bdf);
+ if (!bdf)
+ {
+ errno = ENOMEM;
+ return BDF_SYSTEM_ERROR;
+ }
+
+ if (linecount)
+ *linecount = 0;
+
+ while (!err && (len = next_line (&line, &line_size, filep, linecount)) >= 0)
+ {
+ switch (parser.location)
+ {
+ case START:
+ {
+ /* This is the start of the file, only comments are allowed
+ until STARTFONT is encountered. */
+ char *arg = line;
+ err = find_arg (&arg);
+
+ if (err)
+ continue;
+ if (!strcmp (line, "STARTFONT"))
+ {
+ char *minor = strchr (arg, '.');
+ if (minor)
+ *(minor++) = '\0';
+ parser.location = FONT;
+ parse_template (arg, "%i", &bdf->version_maj);
+ if (minor)
+ parse_template (minor, "%i", &bdf->version_min);
+ else
+ bdf->version_min = 0;
+ }
+ else
+ err = BDF_SYNTAX_ERROR;
+ }
+ break;
+
+ case FONT:
+ {
+ /* This is the global header before the CHARS. */
+ char *arg = line;
+ err = find_arg (&arg);
+
+ if (err)
+ continue;
+ else if (!bdf->has_content_version
+ && !strcmp (line, "CONTENTVERSION"))
+ {
+ bdf->has_content_version = 1;
+ parse_template (arg, "%i", &bdf->content_version);
+ }
+ else if (!bdf->name && !strcmp (line, "FONT"))
+ err = parse_string (arg, &bdf->name, 0);
+ else if (!parser.has_size
+ && !strcmp (line, "SIZE"))
+ {
+ parser.has_size = 1;
+ parse_template (arg, "%i%i%i", &bdf->point_size,
+ &bdf->res_x, &bdf->res_y);
+ }
+ else if (!parser.has_fbbx && !strcmp (line, "FONTBOUNDINGBOX"))
+ {
+ parser.has_fbbx = 1;
+ parse_template (arg, "%i%i%i%i", &bdf->bbox.width,
+ &bdf->bbox.height, &bdf->bbox.offx,
+ &bdf->bbox.offy);
+ }
+ else if (!parser.has_metricsset && !strcmp (line, "METRICSSET"))
+ {
+ parser.has_metricsset = 1;
+ parse_template (arg, "%i", &bdf->metricsset);
+ if (!err && (bdf->metricsset < 0
+ || bdf->metricsset > 2))
+ err = BDF_INVALID_ARGUMENT;
+ }
+ else if (!bdf->properties && !strcmp (line, "STARTPROPERTIES"))
+ {
+ parser.location = PROPERTIES;
+ parse_template (arg, "%i", &bdf->properties_count);
+ if (!err && (bdf->properties_count <= 0))
+ err = BDF_INVALID_ARGUMENT;
+ if (err)
+ goto leave;
+ bdf->__properties_allocated = bdf->properties_count;
+ bdf->properties = calloc (bdf->properties_count,
+ sizeof (struct bdf_property));
+ if (!bdf->properties)
+ {
+ errno = ENOMEM;
+ err = BDF_SYSTEM_ERROR;
+ }
+ }
+ else if (!strcmp (line, "CHARS"))
+ {
+ /* This marks the end of the first section, so check
+ for mandatory global options. */
+ if (!bdf->name || !parser.has_size || !parser.has_fbbx)
+ err = BDF_SYNTAX_ERROR;
+ else
+ {
+ parser.location = GLYPHS;
+ parse_template (arg, "%i", &bdf->glyphs_count);
+ if (!err && (bdf->glyphs_count < 0))
+ err = BDF_INVALID_ARGUMENT;
+ if (!err)
+ {
+ bdf->__glyphs_allocated = bdf->glyphs_count;
+ bdf->glyphs = calloc (bdf->glyphs_count,
+ sizeof (struct bdf_glyph));
+ if (!bdf->glyphs)
+ {
+ errno = ENOMEM;
+ err = BDF_SYSTEM_ERROR;
+ }
+ }
+ }
+ }
+ else if (!bdf->has_swidth && !strcmp (line, "SWIDTH"))
+ {
+ bdf->has_swidth = 1;
+ parse_template (arg, "%i%i", &bdf->swidth.x, &bdf->swidth.y);
+ }
+ else if (!bdf->has_dwidth && !strcmp (line, "DWIDTH"))
+ {
+ bdf->has_dwidth = 1;
+ parse_template (arg, "%i%i", &bdf->dwidth.x, &bdf->dwidth.y);
+ }
+ else if (!bdf->has_swidth1 && !strcmp (line, "SWIDTH1"))
+ {
+ bdf->has_swidth1 = 1;
+ parse_template (arg, "%i%i", &bdf->swidth1.x, &bdf->swidth1.y);
+ }
+ else if (!bdf->has_dwidth1 && !strcmp (line, "DWIDTH1"))
+ {
+ bdf->has_dwidth1 = 1;
+ parse_template (arg, "%i%i", &bdf->dwidth1.x, &bdf->dwidth1.y);
+ }
+ else if (!bdf->has_vvector && !strcmp (line, "VVECTOR"))
+ {
+ bdf->has_vvector = 1;
+ parse_template (arg, "%i%i", &bdf->vvector.x, &bdf->vvector.y);
+ }
+ else
+ err = BDF_SYNTAX_ERROR;
+ }
+ break;
+
+ case PROPERTIES:
+ /* This is the property list in the global header, between
+ STARTPROPERTIES and ENDPROPERTIES. */
+ if (!strcmp (line, "ENDPROPERTIES"))
+ {
+ parser.location = FONT;
+ if (parser.properties != bdf->properties_count)
+ err = BDF_COUNT_MISMATCH;
+ }
+ else
+ {
+ if (parser.properties == bdf->properties_count)
+ err = BDF_COUNT_MISMATCH;
+ else
+ {
+ struct bdf_property *prop
+ = &bdf->properties[parser.properties++];
+ char *arg = line;
+
+ err = find_arg (&arg);
+ if (err)
+ continue;
+
+ err = parse_string (line, &prop->name, 0);
+ if (!err)
+ {
+ if (*arg == '"')
+ {
+ prop->type = BDF_PROPERTY_STRING;
+ err = parse_string (arg, &prop->value.string, 1);
+ }
+ else
+ {
+ prop->type = BDF_PROPERTY_NUMBER;
+ parse_template (arg, "%i", &prop->value.number);
+ }
+ }
+ }
+ }
+ break;
+
+ case GLYPHS:
+ /* This is the second section of the file, containing the
+ glyphs. */
+ if (!strcmp (line, "ENDFONT"))
+ {
+ if (parser.glyphs != bdf->glyphs_count)
+ err = BDF_COUNT_MISMATCH;
+ done = 1;
+ }
+ else
+ {
+ char *arg = line;
+
+ err = find_arg (&arg);
+ if (err)
+ continue;
+ else if (!strcmp (line, "STARTCHAR"))
+ {
+ if (parser.glyphs == bdf->glyphs_count)
+ err = BDF_COUNT_MISMATCH;
+
+ parser.location = GLYPH;
+ parser.glyph = &bdf->glyphs[parser.glyphs++];
+ parser.glyph_has_encoding = 0;
+ parser.glyph_has_bbx = 0;
+ parser.glyph_blines = 0;
+ err = parse_string (arg, &(parser.glyph->name), 0);
+ }
+ else
+ err = BDF_SYNTAX_ERROR;
+ }
+ break;
+
+ case GLYPH:
+ /* This is a glyph, but not its bitmap yet. */
+ if (!strcmp (line, "BITMAP"))
+ {
+ if (!parser.glyph_has_encoding
+ || !parser.glyph_has_bbx
+
+ /* In writing mode 0, SWIDTH and DWIDTH are mandatory. */
+ || (bdf->metricsset != 1
+ && (!(parser.glyph->has_swidth || bdf->has_swidth)
+ || !(parser.glyph->has_dwidth || bdf->has_dwidth)))
+
+ /* In writing mode 1, SWIDTH1, DWIDTH1 and VVECTOR
+ are mandatory. */
+ || (bdf->metricsset != 0
+ && (!(parser.glyph->has_swidth1 || bdf->has_swidth1)
+ || !(parser.glyph->has_dwidth1 || bdf->has_dwidth1)
+ || !(parser.glyph->has_vvector
+ || bdf->has_vvector))))
+ err = BDF_SYNTAX_ERROR;
+
+ parser.location = BITMAP;
+ parser.glyph->bitmap = malloc (parser.glyph_bwidth
+ * parser.glyph_bheight);
+ if (!parser.glyph->bitmap)
+ {
+ errno = ENOMEM;
+ err = BDF_SYSTEM_ERROR;
+ }
+ }
+ else
+ {
+ char *arg = line;
+
+ err = find_arg (&arg);
+ if (err)
+ continue;
+ else if (!parser.glyph_has_encoding
+ && !strcmp (line, "ENCODING"))
+ {
+ parser.glyph_has_encoding = 1;
+ parse_template (arg, "%i", &parser.glyph->encoding);
+ if (err == BDF_SYNTAX_ERROR)
+ {
+ err = 0;
+ parse_template (arg, "%i%i", &parser.glyph->encoding,
+ &parser.glyph->internal_encoding);
+ if (!err && parser.glyph->encoding != -1)
+ err = BDF_SYNTAX_ERROR;
+ }
+ }
+ else if (!parser.glyph_has_bbx && !strcmp (line, "BBX"))
+ {
+ parser.glyph_has_bbx = 1;
+ parse_template (arg, "%i%i%i%i", &parser.glyph->bbox.width,
+ &parser.glyph->bbox.height,
+ &parser.glyph->bbox.offx,
+ &parser.glyph->bbox.offy);
+ if (!err)
+ {
+ parser.glyph_bwidth = (parser.glyph->bbox.width + 7) / 8;
+ parser.glyph_bheight = parser.glyph->bbox.height;
+ }
+ }
+ else if (!parser.glyph->has_swidth && !strcmp (line, "SWIDTH"))
+ {
+ parser.glyph->has_swidth = 1;
+ parse_template (arg, "%i%i", &parser.glyph->swidth.x,
+ &parser.glyph->swidth.y);
+ }
+ else if (!parser.glyph->has_dwidth && !strcmp (line, "DWIDTH"))
+ {
+ parser.glyph->has_dwidth = 1;
+ parse_template (arg, "%i%i", &parser.glyph->dwidth.x,
+ &parser.glyph->dwidth.y);
+ }
+ else if (!parser.glyph->has_swidth1 && !strcmp (line, "SWIDTH1"))
+ {
+ parser.glyph->has_swidth1 = 1;
+ parse_template (arg, "%i%i", &parser.glyph->swidth1.x,
+ &parser.glyph->swidth1.y);
+ }
+ else if (!parser.glyph->has_dwidth1 && !strcmp (line, "DWIDTH1"))
+ {
+ parser.glyph->has_dwidth1 = 1;
+ parse_template (arg, "%i%i", &parser.glyph->dwidth1.x,
+ &parser.glyph->dwidth1.y);
+ }
+ else if (!parser.glyph->has_vvector && !strcmp (line, "VVECTOR"))
+ {
+ parser.glyph->has_vvector = 1;
+ parse_template (arg, "%i%i", &parser.glyph->vvector.x,
+ &parser.glyph->vvector.y);
+ }
+ else
+ err = BDF_SYNTAX_ERROR;
+ }
+ break;
+
+ case BITMAP:
+ /* This is the bitmap of a glyph. */
+ if (!strcmp (line, "ENDCHAR"))
+ {
+ if (parser.glyph_blines != parser.glyph_bheight)
+ err = BDF_COUNT_MISMATCH;
+ parser.location = GLYPHS;
+ parser.glyph = 0;
+ }
+ else
+ {
+ if (strlen (line) != 2 * parser.glyph_bwidth)
+ err = BDF_SYNTAX_ERROR;
+ else if (parser.glyph_blines == parser.glyph_bheight)
+ err = BDF_COUNT_MISMATCH;
+ else
+ {
+ char *number = line;
+ unsigned char *bline = parser.glyph->bitmap
+ + parser.glyph_bwidth * parser.glyph_blines++;
+
+ do
+ {
+ err = parse_hexbyte (number, bline);
+ number += 2;
+ bline++;
+ }
+ while (!err && *number);
+ }
+ }
+ break;
+ }
+ }
+ while (!err && !done && !feof (filep) && !ferror (filep));
+
+ leave:
+ if (ferror (filep))
+ err = ferror (filep);
+ if (err)
+ free (bdf);
+ else
+ *font = bdf;
+ return err;
+}
+
+
+/* Destroy the BDF font object and release all associated
+ resources. */
+void
+bdf_destroy (bdf_font_t font)
+{
+ int i;
+ for (i = 0; i < font->glyphs_count; i++)
+ free (font->glyphs[i].name);
+ free (font->glyphs);
+ for (i = 0; i < font->properties_count; i++)
+ {
+ free (font->properties[i].name);
+ if (font->properties[i].type == BDF_PROPERTY_STRING)
+ free (font->properties[i].value.string);
+ }
+ free (font->properties);
+ free (font->name);
+}
+
+
+bdf_error_t
+bdf_new (bdf_font_t *font, int version_maj, int version_min,
+ const char *name, int point_size, int res_x, int res_y,
+ int bbox_width, int bbox_height, int bbox_offx, int bbox_offy,
+ int metricsset)
+{
+ bdf_font_t bdf;
+
+ if (!font
+ || (version_maj != 2)
+ || (version_min != 1 && version_min != 2)
+ || !name)
+ return BDF_INVALID_ARGUMENT;
+
+ bdf = calloc (1, sizeof *bdf);
+ if (!bdf)
+ {
+ errno = ENOMEM;
+ return BDF_SYSTEM_ERROR;
+ }
+
+ bdf->version_maj = version_maj;
+ bdf->version_min = version_min;
+ bdf->name = strdup (name);
+ if (!name)
+ {
+ free (bdf);
+ errno = ENOMEM;
+ return BDF_SYSTEM_ERROR;
+ }
+
+ bdf->point_size = point_size;
+ bdf->res_x = res_x;
+ bdf->res_y = res_y;
+ bdf->bbox.width = bbox_width;
+ bdf->bbox.height = bbox_height;
+ bdf->bbox.offx = bbox_offx;
+ bdf->bbox.offy = bbox_offy;
+ bdf->metricsset = metricsset;
+ *font = bdf;
+ return 0;
+}
+
+
+#define bdf_set_something(what) \
+bdf_error_t \
+bdf_set_##what (bdf_font_t font, int glyph, int x, int y) \
+{ \
+ if (x < 0 || y < 0 || glyph > font->glyphs_count - 1) \
+ return BDF_INVALID_ARGUMENT; \
+ if (glyph < 0) \
+ { \
+ font->has_##what = 1; \
+ font->what.x = x; \
+ font->what.y = y; \
+ } \
+ else \
+ { \
+ font->glyphs[glyph].has_##what = 1; \
+ font->glyphs[glyph].what.x = x; \
+ font->glyphs[glyph].what.y = y; \
+ } \
+ return 0; \
+}
+
+bdf_set_something (swidth)
+bdf_set_something (dwidth)
+bdf_set_something (swidth1)
+bdf_set_something (dwidth1)
+bdf_set_something (vvector)
+
+
+static bdf_error_t
+expand_properties (bdf_font_t font, int count)
+{
+ if (font->__properties_allocated == font->properties_count)
+ {
+ struct bdf_property *new;
+ new = realloc (font->properties,
+ (font->__properties_allocated + count)
+ * sizeof (struct bdf_property));
+ if (!new)
+ {
+ errno = ENOMEM;
+ return BDF_SYSTEM_ERROR;
+ }
+ font->__properties_allocated += count;
+ font->properties = new;
+ }
+ return 0;
+}
+
+
+/* Add a new string property to the font FONT. */
+bdf_error_t
+bdf_add_string_property (bdf_font_t font, const char *name, const char *value)
+{
+ bdf_error_t err;
+ struct bdf_property *prop;
+
+ err = expand_properties (font, 16);
+ if (err)
+ {
+ err = expand_properties (font, 1);
+ if (err)
+ return err;
+ }
+
+ prop = &font->properties[font->properties_count];
+ prop->type = BDF_PROPERTY_STRING;
+ prop->name = strdup (name);
+ if (prop->name)
+ prop->value.string = strdup (value);
+ if (!prop->name || !prop->value.string)
+ {
+ errno = ENOMEM;
+ return BDF_SYSTEM_ERROR;
+ }
+ font->properties_count++;
+ return 0;
+}
+
+
+/* Add a new number property to the font FONT. */
+bdf_error_t
+bdf_add_number_property (bdf_font_t font, const char *name, int value)
+{
+ bdf_error_t err;
+ struct bdf_property *prop;
+
+ err = expand_properties (font, 16);
+ if (err)
+ {
+ err = expand_properties (font, 1);
+ if (err)
+ return err;
+ }
+
+ prop = &font->properties[font->properties_count];
+ prop->type = BDF_PROPERTY_NUMBER;
+ prop->name = strdup (name);
+ if (!prop->name)
+ {
+ errno = ENOMEM;
+ return BDF_SYSTEM_ERROR;
+ }
+ prop->value.number = value;
+ font->properties_count++;
+ return 0;
+}
+
+
+static bdf_error_t
+expand_glyphs (bdf_font_t font, int count)
+{
+ if (font->__glyphs_allocated == font->glyphs_count)
+ {
+ struct bdf_glyph *new;
+ new = realloc (font->glyphs,
+ (font->__glyphs_allocated + count)
+ * sizeof (struct bdf_glyph));
+ if (!new)
+ {
+ errno = ENOMEM;
+ return BDF_SYSTEM_ERROR;
+ }
+ font->__glyphs_allocated += count;
+ font->glyphs = new;
+ }
+ return 0;
+}
+
+
+/* Add a new glyph with the specified paramters to the font FONT. If
+ encoding is -1, internal_encoding specifies the internal
+ encoding. All other parameters are mandatory. */
+bdf_error_t
+bdf_add_glyph (bdf_font_t font, const char *name, int encoding,
+ int internal_encoding, int bbox_width, int bbox_height,
+ int bbox_offx, int bbox_offy, const unsigned char *bitmap)
+{
+ bdf_error_t err;
+ struct bdf_glyph *glyph;
+ int bsize;
+
+ err = expand_glyphs (font, 64);
+ if (err)
+ {
+ err = expand_glyphs (font, 1);
+ if (err)
+ return err;
+ }
+
+ glyph = &font->glyphs[font->glyphs_count];
+ memset (glyph, 0, sizeof (*glyph));
+
+ glyph->name = strdup (name);
+ if (!glyph->name)
+ {
+ errno = ENOMEM;
+ return BDF_SYSTEM_ERROR;
+ }
+ glyph->encoding = encoding;
+ if (encoding == -1)
+ glyph->internal_encoding = internal_encoding;
+ glyph->bbox.width = bbox_width;
+ glyph->bbox.height = bbox_height;
+ glyph->bbox.offx = bbox_offx;
+ glyph->bbox.offy = bbox_offy;
+ bsize = ((bbox_width + 7) / 8) * bbox_height;
+ glyph->bitmap = malloc (bsize);
+ if (!glyph->bitmap)
+ {
+ free (glyph->name);
+ errno = ENOMEM;
+ return BDF_SYSTEM_ERROR;
+ }
+ memcpy (glyph->bitmap, bitmap, bsize);
+ font->glyphs_count++;
+ return 0;
+}
+
+
+/* Write the font FONT in BDF format to stream FILEP. */
+bdf_error_t
+bdf_write (FILE *filep, bdf_font_t font)
+{
+ int index;
+
+ if (font->version_maj != 2
+ || (font->version_min != 1 && font->version_min != 2))
+ return BDF_INVALID_ARGUMENT;
+ fprintf (filep, "STARTFONT %i.%i\n", font->version_maj, font->version_min);
+
+ if (!font->name)
+ return BDF_INVALID_ARGUMENT;
+ fprintf (filep, "FONT %s\n", font->name);
+ fprintf (filep, "SIZE %i %i %i\n", font->point_size,
+ font->res_x, font->res_y);
+ fprintf (filep, "FONTBOUNDINGBOX %i %i %i %i\n",
+ font->bbox.width, font->bbox.height,
+ font->bbox.offx, font->bbox.offy);
+ if (font->has_swidth)
+ fprintf (filep, "SWIDTH %i %i\n", font->swidth.x, font->swidth.y);
+ if (font->has_dwidth)
+ fprintf (filep, "DWIDTH %i %i\n", font->dwidth.x, font->dwidth.y);
+ if (font->has_swidth1)
+ fprintf (filep, "SWIDTH1 %i %i\n", font->swidth1.x, font->swidth1.y);
+ if (font->has_dwidth1)
+ fprintf (filep, "DWIDTH1 %i %i\n", font->dwidth1.x, font->dwidth1.y);
+ if (font->has_vvector)
+ fprintf (filep, "VVECTOR %i %i\n", font->vvector.x, font->vvector.y);
+ if (font->properties_count < 0)
+ return BDF_INVALID_ARGUMENT;
+ /* XXX We always print out a properties block for xmbdfed's sake. */
+ fprintf (filep, "STARTPROPERTIES %i\n", font->properties_count);
+ if (font->properties_count > 0)
+ {
+ fprintf (filep, "STARTPROPERTIES %i\n", font->properties_count);
+ for (index = 0; index < font->properties_count; index++)
+ {
+ struct bdf_property *prop = &font->properties[index];
+
+ if (prop->type == BDF_PROPERTY_NUMBER)
+ fprintf (filep, "%s %i\n", prop->name, prop->value.number);
+ else
+ {
+ char *val = prop->value.string;
+
+ fprintf (filep, "%s \"", prop->name);
+ while (*val)
+ {
+ fputc (*val, filep);
+ if (*(val++) == '"')
+ fputc ('"', filep);
+ }
+ fprintf (filep, "\"\n");
+ }
+ }
+ }
+ fprintf (filep, "ENDPROPERTIES\n");
+ if (font->glyphs_count <= 0)
+ return BDF_INVALID_ARGUMENT;
+ fprintf (filep, "CHARS %i\n", font->glyphs_count);
+
+ for (index = 0; index < font->glyphs_count; index++)
+ {
+ struct bdf_glyph *glyph = &font->glyphs[index];
+ unsigned char *bitmap;
+ int row, col;
+
+ fprintf (filep, "STARTCHAR %s\n", glyph->name);
+ if (glyph->encoding != -1)
+ fprintf (filep, "ENCODING %i\n", glyph->encoding);
+ else
+ fprintf (filep, "ENCODING %i %i\n", glyph->encoding,
+ glyph->internal_encoding);
+ if (glyph->has_swidth)
+ fprintf (filep, "SWIDTH %i %i\n", glyph->swidth.x, glyph->swidth.y);
+ if (glyph->has_dwidth)
+ fprintf (filep, "DWIDTH %i %i\n", glyph->dwidth.x, glyph->dwidth.y);
+ if (glyph->has_swidth1)
+ fprintf (filep, "SWIDTH1 %i %i\n", glyph->swidth1.x, glyph->swidth1.y);
+ if (glyph->has_dwidth1)
+ fprintf (filep, "DWIDTH1 %i %i\n", glyph->dwidth1.x, glyph->dwidth1.y);
+ if (glyph->has_vvector)
+ fprintf (filep, "VVECTOR %i %i\n", glyph->vvector.x, glyph->vvector.y);
+ fprintf (filep, "BBX %i %i %i %i\n", glyph->bbox.width,
+ glyph->bbox.height, glyph->bbox.offx, glyph->bbox.offy);
+ fprintf (filep, "BITMAP\n");
+ bitmap = glyph->bitmap;
+ for (row = 0; row < glyph->bbox.height; row++)
+ {
+ for (col = 0; col < (glyph->bbox.width + 7) / 8; col++)
+ fprintf (filep, "%02X", *(bitmap++));
+ fputc ('\n', filep);
+ }
+ fprintf (filep, "ENDCHAR\n");
+ }
+ fprintf (filep, "ENDFONT\n");
+ if (ferror (filep))
+ {
+ errno = ferror (filep);
+ return BDF_SYSTEM_ERROR;
+ }
+ return 0;
+}
+
+
+/* The function returns -1 if the encoding of glyph A is lower than
+ the encoding of glyph B, 1 if it is the other way round, and 0 if
+ the encoding of both glyphs is the same. */
+int
+bdf_compare_glyphs (const void *a, const void *b)
+{
+ struct bdf_glyph *first = (struct bdf_glyph *) a;
+ struct bdf_glyph *second = (struct bdf_glyph *) b;
+
+ if (first->encoding < second->encoding)
+ return -1;
+ else if (first->encoding > second->encoding)
+ return 1;
+ else
+ {
+ if (first->encoding == -1)
+ {
+ if (first->internal_encoding < second->internal_encoding)
+ return -1;
+ else if (first->internal_encoding > second->internal_encoding)
+ return 1;
+ else
+ return 0;
+ }
+ else
+ return 0;
+ }
+}
+
+
+/* Sort the glyphs in the font FONT. This must be called before using
+ bdf_find_glyphs, after the font has been created or after new
+ glyphs have been added to the font. */
+void
+bdf_sort_glyphs (bdf_font_t font)
+{
+ qsort (font->glyphs, font->glyphs_count, sizeof (struct bdf_glyph),
+ bdf_compare_glyphs);
+}
+
+
+/* Find the glyph with the encoding ENC (and INTERNAL_ENC, if ENC is
+ -1) in the font FONT. Requires that the glyphs in the font are
+ sorted. */
+struct bdf_glyph *
+bdf_find_glyph (bdf_font_t font, int enc, int internal_enc)
+{
+ struct bdf_glyph key = { encoding: enc, internal_encoding: internal_enc };
+ return bsearch (&key, font->glyphs, font->glyphs_count,
+ sizeof (struct bdf_glyph), bdf_compare_glyphs);
+}
diff --git a/console-client/bdf.h b/console-client/bdf.h
new file mode 100644
index 00000000..17cd0235
--- /dev/null
+++ b/console-client/bdf.h
@@ -0,0 +1,272 @@
+/* bdf.h - Parser for the Adobe Glyph Bitmap Distribution Format (BDF).
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann <marcus@gnu.org>.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef _BDF_H_
+#define _BDF_H_
+
+#include <stdio.h>
+
+/* Version 2.2 of the Glyph Bitmap Distribution Format Specification
+ was released by Adobe 22 March 1993 and is a superset of version 2.1.
+
+ The format of a BDF file is a human-readable ASCII text file.
+ Every line consists of a keyword, which may be followed by one or
+ more arguments. The specification is vague about the exact data
+ types of the arguments, so we treat a string as an 8-bit string
+ which must not contain a binary null, and a number like an integer
+ as an int. Leading and trailing white space are removed, multiple
+ spaces that separate arguments are replaced by a single white
+ space, and empty lines are ignored. */
+
+
+/* Possible error values returned by the BDF functions. */
+typedef enum
+{
+ /* No error occurred. This is guaranteed to be zero. */
+ BDF_NO_ERROR = 0,
+
+ /* A system error occurred. The caller should consult errno. */
+ BDF_SYSTEM_ERROR,
+
+ /* All following errors indicate that the file is not a valid BDF
+ file. */
+ BDF_SYNTAX_ERROR,
+
+ /* An argument is out of range or of an invalid type. */
+ BDF_INVALID_ARGUMENT,
+
+ /* The number of properties, glyphs or bitmap lines is
+ incorrect. */
+ BDF_COUNT_MISMATCH
+} bdf_error_t;
+
+/* Return a statically allocated string describing the error value ERR. */
+const char *bdf_strerror (bdf_error_t err);
+
+
+/* A property in the BDF font file is an unspecified KEYWORD VALUE
+ pair in a line after STARTPROPERTIES and before ENDPROPERTIES.
+ VALUE can be an integer or a string in double quotes (two
+ consecutives double quotes can be used to include a double quote in
+ the string. */
+struct bdf_property
+{
+ /* The property name. */
+ char *name;
+
+ /* The type indicates which member of the union is valid. */
+ enum { BDF_PROPERTY_NUMBER, BDF_PROPERTY_STRING } type;
+ union
+ {
+ int number;
+ char *string;
+ } value;
+};
+typedef struct bdf_property *bdf_property_t;
+
+/* The bounding box of a font or a glyph within. */
+struct bdf_bbox
+{
+ int width;
+ int height;
+ int offx;
+ int offy;
+};
+
+/* A vector, used for displacement and resolution. */
+struct bdf_vector
+{
+ int x;
+ int y;
+};
+
+/* A single glyph in the font. */
+struct bdf_glyph
+{
+ /* The name of the glyph. */
+ char *name;
+
+ /* The Adode Standard Encoding of the glyph, or -1 if not
+ available. */
+ int encoding;
+
+ /* If encoding is -1, internal_encoding contains the internal
+ encoding of the glyph. The internal encoding is private to the
+ font and the application. */
+ int internal_encoding;
+
+ /* The bounding box of the glyph. The width of the bounding box,
+ divided by 8 (rounding up), specifies how many bytes are used in
+ the bitmap for each row of the glyph. The height specifies the
+ number of rows. */
+ struct bdf_bbox bbox;
+
+ /* The bitmap of the glyph, row-by-row from top to bottom, and
+ byte-by-byte within a row from left to right. Within a byte, the
+ most significant bit is left in the glyph. */
+ unsigned char *bitmap;
+
+ /* If the writing direction is horizontal, SWIDTH and DWIDTH are
+ relevant. If the writing direction is vertical, SWIDTH1, DWIDTH1
+ and VVECTOR are relevant. Relevant values have to be specified
+ here, or font-wide in the global section. A global value can be
+ overridden for individual glyphs. */
+ int has_swidth : 1;
+ int has_dwidth : 1;
+ int has_swidth1 : 1;
+ int has_dwidth1 : 1;
+ int has_vvector : 1;
+ struct bdf_vector swidth;
+ struct bdf_vector dwidth;
+ struct bdf_vector swidth1;
+ struct bdf_vector dwidth1;
+ struct bdf_vector vvector;
+};
+
+/* A font is a set of glyphs with some font-wide attributes. */
+struct bdf_font
+{
+ /* The version of the font format. Should be 2.1 or 2.2. It is
+ split up into major and minor component to make precise
+ comparison possible. */
+ int version_maj;
+ int version_min;
+
+ /* The font name. */
+ char *name;
+
+ /* The content version, if available. The content version indicates
+ the revision of the appearance of the glyphs within the font. */
+ int has_content_version : 1;
+ int content_version;
+
+ /* The point size of the font in device pixels. */
+ int point_size;
+
+ /* The resolution of the display the font is intended for (dpi).
+ This is needed if you want to scale the glyphs for a different
+ device than they were intended for. */
+ int res_x;
+ int res_y;
+
+ /* The global bounding box parameters. */
+ struct bdf_bbox bbox;
+
+ int __properties_allocated;
+ int properties_count;
+ struct bdf_property *properties;
+ int __glyphs_allocated;
+ int glyphs_count;
+ struct bdf_glyph *glyphs;
+
+ /* The metricsset. If 0, the font has a horizontal writing
+ direction. If 1, the font has a vertical writing direction. If
+ 2, the font has a mixed writing direction. */
+ int metricsset;
+
+ /* The following have the same meaning as the corresponding members
+ in the glyph structure and specify a font wide default which must
+ be used if the glyph does not provide its own values. */
+ int has_swidth : 1;
+ int has_dwidth : 1;
+ int has_swidth1 : 1;
+ int has_dwidth1 : 1;
+ int has_vvector : 1;
+ struct bdf_vector swidth;
+ struct bdf_vector dwidth;
+ struct bdf_vector swidth1;
+ struct bdf_vector dwidth1;
+ struct bdf_vector vvector;
+};
+typedef struct bdf_font *bdf_font_t;
+
+
+/* Read the font from stream FILE, and return it in FONT. If
+ LINECOUNT is not zero, it will contain the number of lines in the
+ file at success, and the current line an error occurred at
+ failure. */
+bdf_error_t bdf_read (FILE *file, bdf_font_t *font, int *linecount);
+
+/* Destroy the BDF font object and release all associated
+ resources. */
+void bdf_destroy (bdf_font_t font);
+
+/* Create a new font object with the specified mandatory
+ parameters. */
+bdf_error_t bdf_new (bdf_font_t *font, int version_maj, int version_min,
+ const char *name, int point_size, int res_x, int res_y,
+ int bbox_width, int bbox_height, int bbox_offx,
+ int bbox_offy, int metricsset);
+
+/* Set the SWIDTH of the glyph GLYPH in font FONT to X and Y. If
+ glyph is negativ, the default for the font will be set. */
+bdf_error_t bdf_set_swidth (bdf_font_t font, int glyph, int x, int y);
+
+/* Set the DWIDTH of the glyph GLYPH in font FONT to X and Y. If
+ glyph is negativ, the default for the font will be set. */
+bdf_error_t bdf_set_dwidth (bdf_font_t font, int glyph, int x, int y);
+
+/* Set the SWIDTH1 of the glyph GLYPH in font FONT to X and Y. If
+ glyph is negativ, the default for the font will be set. */
+bdf_error_t bdf_set_swidth1 (bdf_font_t font, int glyph, int x, int y);
+
+/* Set the DWIDTH1 of the glyph GLYPH in font FONT to X and Y. If
+ glyph is negativ, the default for the font will be set. */
+bdf_error_t bdf_set_dwidth1 (bdf_font_t font, int glyph, int x, int y);
+
+/* Set the VVECTOR of the glyph GLYPH in font FONT to X and Y. If
+ glyph is negativ, the default for the font will be set. */
+bdf_error_t bdf_set_vvector (bdf_font_t font, int glyph, int x, int y);
+
+/* Add a new string property to the font FONT. */
+bdf_error_t bdf_add_string_property (bdf_font_t font, const char *name,
+ const char *value);
+
+/* Add a new number property to the font FONT. */
+bdf_error_t bdf_add_number_property (bdf_font_t font, const char *name,
+ int value);
+
+/* Add a new glyph with the specified paramters to the font FONT. If
+ encoding is -1, internal_encoding specifies the internal
+ encoding. All other parameters are mandatory. */
+bdf_error_t bdf_add_glyph (bdf_font_t font, const char *name, int encoding,
+ int internal_encoding, int bbox_width,
+ int bbox_height, int bbox_offx, int bbox_offy,
+ const unsigned char *bitmap);
+
+/* Write the font FONT in BDF format to stream FILEP. */
+bdf_error_t bdf_write (FILE *filep, bdf_font_t font);
+
+/* The function returns -1 if the encoding of glyph A is lower than
+ the encoding of glyph B, 1 if it is the other way round, and 0 if
+ the encoding of both glyphs is the same. */
+int bdf_compare_glyphs (const void *a, const void *b);
+
+/* Sort the glyphs in the font FONT. This must be called before using
+ bdf_find_glyphs, after the font has been created or after new
+ glyphs have been added to the font. */
+void bdf_sort_glyphs (bdf_font_t font);
+
+/* Find the glyph with the encoding ENC (and INTERNAL_ENC, if ENC is
+ -1) in the font FONT. Requires that the glyphs in the font are
+ sorted. */
+struct bdf_glyph *bdf_find_glyph (bdf_font_t font, int enc, int internal_enc);
+
+#endif /* _BDF_H_ */
diff --git a/console-client/bell.h b/console-client/bell.h
new file mode 100644
index 00000000..8235cca1
--- /dev/null
+++ b/console-client/bell.h
@@ -0,0 +1,56 @@
+/* bell.h - The interface to and for a bell driver.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef _BELL_H_
+#define _BELL_H_ 1
+
+#include <errno.h>
+
+
+/* The bell drivers are set up by the driver's initialization routine
+ and added to the console client with driver_add_bell. All
+ subsequent operations on the display are fully synchronized by the
+ caller. The driver deinitialization routine should call
+ driver_remove_bell. */
+
+/* Forward declaration. */
+struct bell_ops;
+typedef struct bell_ops *bell_ops_t;
+
+/* Add the bell HANDLE with the operations OPS to the console client.
+ As soon as this is called, operations on this bell may be
+ performed, even before the function returns. */
+error_t driver_add_bell (bell_ops_t ops, void *handle);
+
+/* Remove the bell HANDLE with the operations OPS from the console
+ client. As soon as this function returns, no operations will be
+ performed on the bell anymore. */
+error_t driver_remove_bell (bell_ops_t ops, void *handle);
+
+struct bell_ops
+{
+ /* Beep! the bell HANDLE. */
+ error_t (*beep) (void *handle);
+
+ /* Do not use, do not remove. */
+ void (*deprecated) (void *handle, unsigned int key);
+};
+
+#endif /* _BELL_H_ */
diff --git a/console-client/console.c b/console-client/console.c
new file mode 100644
index 00000000..2fa05339
--- /dev/null
+++ b/console-client/console.c
@@ -0,0 +1,757 @@
+/* console.c -- A pluggable console client.
+ Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <argp.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <wchar.h>
+#include <error.h>
+#include <assert.h>
+
+#include <pthread.h>
+#if HAVE_DAEMON
+#include <libdaemon/daemon.h>
+#endif
+
+#include <hurd/console.h>
+#include <hurd/cons.h>
+
+#include <version.h>
+
+#include "driver.h"
+#include "timer.h"
+#include "trans.h"
+
+const char *cons_client_name = "console";
+const char *cons_client_version = HURD_VERSION;
+
+/* The default node on which the console node is started. */
+#define DEFAULT_CONSOLE_NODE "/dev/cons"
+
+
+/* The global lock protects the active_vcons variable, and thus all
+ operations on the virtual console that is currently active. */
+static pthread_mutex_t global_lock;
+
+/* The active virtual console. This is the one currently
+ displayed. */
+static vcons_t active_vcons = NULL;
+
+/* Contains the VT id when switched away. */
+static int saved_id = 0;
+
+/* The console, used to switch back. */
+static cons_t saved_cons;
+
+/* The file name of the node on which the console translator is
+ set. */
+static char *console_node;
+
+/* If set, the client will daemonize. */
+static int daemonize;
+
+/* Callbacks for input source drivers. */
+
+/* Returns current console ID. */
+error_t
+console_current_id (int *cur)
+{
+ vcons_t vcons;
+
+ pthread_mutex_lock (&global_lock);
+ vcons = active_vcons;
+ if (!vcons)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return ENODEV;
+ }
+ *cur = vcons->id;
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+/* Switch the active console to console ID or DELTA (relative to the
+ active console). */
+error_t
+console_switch (int id, int delta)
+{
+ error_t err = 0;
+ vcons_t vcons;
+ vcons_t new_vcons;
+
+ /* We must give up our global lock before we can call back into
+ libcons. This is because cons_switch will lock CONS, and as
+ other functions in libcons lock CONS while calling back into our
+ functions which take the global lock (like cons_vcons_add), we
+ would deadlock. So we acquire a reference for VCONS to make sure
+ it isn't deallocated while we are outside of the global lock. We
+ also know that */
+
+ pthread_mutex_lock (&global_lock);
+ vcons = active_vcons;
+ if (!vcons)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EINVAL;
+ }
+ ports_port_ref (vcons);
+ pthread_mutex_unlock (&global_lock);
+
+ err = cons_switch (vcons, id, delta, &new_vcons);
+ if (!err)
+ {
+ pthread_mutex_lock (&global_lock);
+ if (active_vcons != new_vcons)
+ {
+ cons_vcons_close (active_vcons);
+ active_vcons = new_vcons;
+ }
+ pthread_mutex_unlock (&new_vcons->lock);
+ ports_port_deref (vcons);
+ pthread_mutex_unlock (&global_lock);
+ }
+ return err;
+}
+
+
+/* Enter SIZE bytes from the buffer BUF into the currently active
+ console. This can be called by the input driver at any time. */
+error_t
+console_input (char *buf, size_t size)
+{
+ error_t err = 0;
+ vcons_t vcons;
+
+ pthread_mutex_lock (&global_lock);
+ vcons = active_vcons;
+ if (!vcons)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EINVAL;
+ }
+ ports_port_ref (vcons);
+ pthread_mutex_unlock (&global_lock);
+
+ if (vcons)
+ {
+ err = cons_vcons_input (vcons, buf, size);
+ ports_port_deref (vcons);
+ }
+ return err;
+}
+
+
+/* Report the mouse event EV to the currently active console. This
+ can be called by the input driver at any time. */
+error_t
+console_move_mouse (mouse_event_t ev)
+{
+ error_t err;
+ vcons_t vcons;
+
+ pthread_mutex_lock (&global_lock);
+
+ vcons = active_vcons;
+ if (!vcons)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EINVAL;
+ }
+ ports_port_ref (vcons);
+ pthread_mutex_unlock (&global_lock);
+
+ if (vcons)
+ {
+ err = cons_vcons_move_mouse (vcons, ev);
+ ports_port_deref (vcons);
+ return err;
+ }
+
+ return 0;
+}
+
+
+/* Scroll the active console by TYPE and VALUE as specified by
+ cons_vcons_scrollback. */
+int
+console_scrollback (cons_scroll_t type, float value)
+{
+ int nr = 0;
+ vcons_t vcons;
+
+ pthread_mutex_lock (&global_lock);
+ vcons = active_vcons;
+ if (!vcons)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EINVAL;
+ }
+ ports_port_ref (vcons);
+ pthread_mutex_unlock (&global_lock);
+
+ if (vcons)
+ {
+ nr = cons_vcons_scrollback (vcons, type, value);
+ ports_port_deref (vcons);
+ }
+ return nr;
+}
+
+
+/* Switch away from the console an external use of the console like
+ XFree. */
+void
+console_switch_away (void)
+{
+ pthread_mutex_lock (&global_lock);
+
+ driver_iterate
+ if (driver->ops->save_status)
+ driver->ops->save_status (driver->handle);
+
+ if (active_vcons)
+ {
+ saved_id = active_vcons->id;
+ saved_cons = active_vcons->cons;
+ cons_vcons_close (active_vcons);
+ active_vcons = NULL;
+ }
+ else
+ {
+ saved_cons = NULL;
+ }
+ pthread_mutex_unlock (&global_lock);
+}
+
+/* Switch back to the console client from an external user of the
+ console like XFree. */
+void
+console_switch_back (void)
+{
+ vcons_list_t conslist;
+ pthread_mutex_lock (&global_lock);
+
+ driver_iterate
+ if (driver->ops->restore_status)
+ driver->ops->restore_status (driver->handle);
+
+ if (saved_cons)
+ {
+ error_t err;
+
+ err = cons_lookup (saved_cons, saved_id, 1, &conslist);
+ if (err)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return;
+ }
+
+ err = cons_vcons_open (saved_cons, conslist, &active_vcons);
+ if (err)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return;
+ }
+
+ conslist->vcons = active_vcons;
+ saved_cons = NULL;
+ pthread_mutex_unlock (&active_vcons->lock);
+ }
+ pthread_mutex_unlock (&global_lock);
+}
+
+
+/* Exit the console client. Does not return. */
+void
+console_exit (void)
+{
+ driver_fini ();
+#if HAVE_DAEMON
+ if (daemonize)
+ daemon_pid_file_remove ();
+#endif /* HAVE_DAEMON */
+ exit (0);
+}
+
+/* Signal an error to the user. */
+void console_error (const wchar_t *const err_msg)
+{
+ pthread_mutex_lock (&global_lock);
+ bell_iterate
+ if (bell->ops->beep)
+ bell->ops->beep (bell->handle);
+ pthread_mutex_unlock (&global_lock);
+}
+
+#if QUAERENDO_INVENIETIS
+void
+console_deprecated (int key)
+{
+ pthread_mutex_lock (&global_lock);
+ input_iterate
+ if (input->ops->deprecated)
+ (*input->ops->deprecated) (input->handle, key);
+ display_iterate
+ if (display->ops->deprecated)
+ (*display->ops->deprecated) (display->handle, key);
+ bell_iterate
+ if (bell->ops->deprecated)
+ (*bell->ops->deprecated) (bell->handle, key);
+ pthread_mutex_unlock (&global_lock);
+}
+#endif /* QUAERENDO_INVENIETIS */
+
+
+/* Callbacks for libcons. */
+
+/* The user may define this function. It is called after a
+ virtual console entry was added. CONS is locked. */
+void
+cons_vcons_add (cons_t cons, vcons_list_t vcons_entry)
+{
+ error_t err = 0;
+ pthread_mutex_lock (&global_lock);
+ if (!active_vcons)
+ {
+ vcons_t vcons;
+
+ /* The first virtual console added to the list is automatically
+ opened after startup. */
+ err = cons_vcons_open (cons, vcons_entry, &vcons);
+ if (!err)
+ {
+ vcons_entry->vcons = vcons;
+ active_vcons = vcons;
+ pthread_mutex_unlock (&vcons->lock);
+ }
+ }
+ pthread_mutex_unlock (&global_lock);
+}
+
+
+/* The user may define this function. Make the changes from
+ cons_vcons_write, cons_vcons_set_cursor_pos,
+ cons_vcons_set_cursor_status and cons_vcons_scroll active. VCONS
+ is locked and will have been continuously locked from the first
+ change since the last update on. This is the latest possible point
+ the user must make the changes visible from. The user can always
+ make the changes visible at a more convenient, earlier time. */
+void
+cons_vcons_update (vcons_t vcons)
+{
+ pthread_mutex_lock (&global_lock);
+ if (vcons == active_vcons)
+ display_iterate
+ if (display->ops->update)
+ display->ops->update (display->handle);
+ pthread_mutex_unlock (&global_lock);
+}
+
+
+/* The user must define this function. Set the cursor on virtual
+ console VCONS, which is locked, to position COL and ROW. */
+void
+cons_vcons_set_cursor_pos (vcons_t vcons, uint32_t col, uint32_t row)
+{
+ pthread_mutex_lock (&global_lock);
+ if (vcons == active_vcons)
+ display_iterate
+ if (display->ops->set_cursor_pos)
+ display->ops->set_cursor_pos (display->handle, col, row);
+ pthread_mutex_unlock (&global_lock);
+}
+
+
+/* The user must define this function. Set the cursor status of
+ virtual console VCONS, which is locked, to STATUS. */
+void
+cons_vcons_set_cursor_status (vcons_t vcons, uint32_t status)
+{
+ pthread_mutex_lock (&global_lock);
+ if (vcons == active_vcons)
+ display_iterate
+ if (display->ops->set_cursor_status)
+ display->ops->set_cursor_status (display->handle, status);
+ pthread_mutex_unlock (&global_lock);
+}
+
+
+/* The user must define this function. Scroll the content of virtual
+ console VCONS, which is locked, up by DELTA if DELTA is positive or
+ down by -DELTA if DELTA is negative. DELTA will never be zero, and
+ the absolute value if DELTA will be smaller than or equal to the
+ height of the screen matrix.
+
+ See <hurd/cons.h> for more information about this function. */
+void
+cons_vcons_scroll (vcons_t vcons, int delta)
+{
+ pthread_mutex_lock (&global_lock);
+ if (vcons == active_vcons)
+ display_iterate
+ if (display->ops->scroll)
+ display->ops->scroll (display->handle, delta);
+ pthread_mutex_unlock (&global_lock);
+}
+
+
+/* The user must define this function. Deallocate the scarce
+ resources (like font glyph slots, colors etc) in the LENGTH entries
+ of the screen matrix starting from position COL and ROW. This call
+ is immediately followed by a subsequent cons_vcons_write call with
+ the same LENGTH, COL and ROW arguments, and should help to make the
+ write successful. If there are no scarce resources, the caller
+ might do nothing. */
+void cons_vcons_clear (vcons_t vcons, size_t length,
+ uint32_t col, uint32_t row)
+{
+ pthread_mutex_lock (&global_lock);
+ if (vcons == active_vcons)
+ display_iterate
+ if (display->ops->clear)
+ display->ops->clear (display->handle, length, col, row);
+ pthread_mutex_unlock (&global_lock);
+}
+
+
+/* The user must define this function. Write LENGTH characters
+ starting from STR on the virtual console VCONS, which is locked,
+ starting from position COL and ROW. */
+void
+cons_vcons_write (vcons_t vcons, conchar_t *str, size_t length,
+ uint32_t col, uint32_t row)
+{
+ pthread_mutex_lock (&global_lock);
+ if (vcons == active_vcons)
+ display_iterate
+ if (display->ops->write)
+ display->ops->write (display->handle, str, length, col, row);
+ pthread_mutex_unlock (&global_lock);
+}
+
+
+/* The user must define this function. Make the virtual console
+ VCONS, which is locked, beep audibly. */
+void
+cons_vcons_beep (vcons_t vcons)
+{
+ pthread_mutex_lock (&global_lock);
+ if (vcons == active_vcons)
+ bell_iterate
+ if (bell->ops->beep)
+ bell->ops->beep (bell->handle);
+ pthread_mutex_unlock (&global_lock);
+}
+
+
+/* The user must define this function. Make the virtual console
+ VCONS, which is locked, flash visibly. */
+void
+cons_vcons_flash (vcons_t vcons)
+{
+ pthread_mutex_lock (&global_lock);
+ if (vcons == active_vcons)
+ display_iterate
+ if (display->ops->flash)
+ display->ops->flash (display->handle);
+ pthread_mutex_unlock (&global_lock);
+}
+
+
+/* The user must define this function. Notice the current status of
+ the scroll lock flag. */
+void
+cons_vcons_set_scroll_lock (vcons_t vcons, int onoff)
+{
+ pthread_mutex_lock (&global_lock);
+ if (vcons == active_vcons)
+ input_iterate
+ if (input->ops->set_scroll_lock_status)
+ input->ops->set_scroll_lock_status (input->handle, onoff);
+ pthread_mutex_unlock (&global_lock);
+}
+
+
+/* The user must define this function. Clear the existing screen
+ matrix and set the size of the screen matrix to the dimension COL x
+ ROW. This call will be immediately followed by a call to
+ cons_vcons_write that covers the whole new screen matrix. */
+error_t
+cons_vcons_set_dimension (vcons_t vcons, uint32_t col, uint32_t row)
+{
+ pthread_mutex_lock (&global_lock);
+ if (vcons == active_vcons)
+ display_iterate
+ if (display->ops->set_dimension)
+ display->ops->set_dimension (display->handle, col, row);
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+
+error_t
+cons_vcons_set_mousecursor_pos (vcons_t vcons, float x, float y)
+{
+ pthread_mutex_lock (&global_lock);
+ if (vcons == active_vcons)
+ display_iterate
+ if (display->ops->set_mousecursor_pos)
+ display->ops->set_mousecursor_pos (display->handle, x, y);
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+
+error_t
+cons_vcons_set_mousecursor_status (vcons_t vcons, int status)
+{
+ pthread_mutex_lock (&global_lock);
+ if (vcons == active_vcons)
+ display_iterate
+ if (display->ops->set_mousecursor_status)
+ display->ops->set_mousecursor_status (display->handle, status);
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+
+}
+
+
+#define DAEMONIZE_KEY 0x80 /* !isascii (DAEMONIZE_KEY), so no short option. */
+
+/* Console-specific options. */
+static const struct argp_option
+options[] =
+ {
+ {"driver-path", 'D', "PATH", 0, "Specify search path for driver modules" },
+ {"driver", 'd', "NAME", 0, "Add driver NAME to the console" },
+ {"console-node", 'c', "FILE", OPTION_ARG_OPTIONAL,
+ "Set a translator on the node FILE (default: " DEFAULT_CONSOLE_NODE ")" },
+#if HAVE_DAEMON
+ {"daemonize", DAEMONIZE_KEY, NULL, 0, "daemonize the console client"},
+#endif
+ {0}
+ };
+
+/* Parse a command line option. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ static int devcount = 0;
+ error_t err;
+
+ switch (key)
+ {
+ case 'D':
+ {
+ char *s;
+ char *d;
+
+ if (driver_path)
+ free (driver_path);
+ driver_path = malloc (strlen (arg) + 2);
+ if (!driver_path)
+ {
+ argp_failure (state, 1, ENOMEM, "adding driver path failed");
+ return EINVAL;
+ }
+ s = arg;
+ d = driver_path;
+ while (*s)
+ {
+ *(d++) = (*s == ':') ? '\0' : *s;
+ s++;
+ }
+ *(d++) = '\0';
+ *d = '\0';
+ }
+ break;
+
+ case 'd':
+ err = driver_add (arg /* XXX */, arg,
+ state->argc, state->argv, &state->next, 0);
+ if (err)
+ {
+ argp_failure (state, 1, err, "loading driver `%s' failed", arg);
+ return EINVAL;
+ }
+ devcount++;
+ break;
+
+ case 'c':
+ console_node = arg ? arg : DEFAULT_CONSOLE_NODE;
+ if (!console_node)
+ return ENOMEM;
+ break;
+
+ case DAEMONIZE_KEY:
+ daemonize = 1;
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ if (!devcount)
+ {
+ argp_error (state, "at least one --driver argument required");
+ return EINVAL;
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Add our startup arguments to the standard cons set. */
+static const struct argp_child startup_children[] =
+ { { &cons_startup_argp }, { 0 } };
+static struct argp startup_argp = {options, parse_opt, 0,
+ 0, startup_children};
+#if HAVE_DAEMON
+#define daemon_error(status, errnum, format, args...) \
+ do \
+ { \
+ if (daemonize) \
+ { \
+ if (errnum) \
+ daemon_log (LOG_ERR, format ": %s", ##args, \
+ strerror(errnum)); \
+ else \
+ daemon_log (LOG_ERR, format, ##args); \
+ if (status) \
+ { \
+ /* Signal parent. */ \
+ daemon_retval_send (status); \
+ daemon_pid_file_remove (); \
+ return 0; \
+ } \
+ } \
+ else \
+ error (status, errnum, format, ##args); \
+ } \
+ while (0);
+#else
+#define daemon_error error
+#endif
+
+int
+main (int argc, char *argv[])
+{
+ error_t err;
+ char *errname;
+
+ driver_init ();
+
+ /* Parse our command line. This shouldn't ever return an error. */
+ argp_parse (&startup_argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+#if HAVE_DAEMON
+ if (daemonize)
+ {
+ /* Reset signal handlers. */
+ if (daemon_reset_sigs (-1) < 0)
+ error (1, errno, "Failed to reset all signal handlers");
+
+ /* Unblock signals. */
+ if (daemon_unblock_sigs (-1) < 0)
+ error (1, errno, "Failed to unblock all signals");
+
+ /* Set indetification string for the daemon for both syslog and
+ PID file. */
+ daemon_pid_file_ident = daemon_log_ident = \
+ daemon_ident_from_argv0 (argv[0]);
+
+ /* Check that the daemon is not run twice at the same time. */
+ pid_t pid;
+ if ((pid = daemon_pid_file_is_running ()) >= 0)
+ error (1, errno, "Daemon already running on PID file %u", pid);
+
+ /* Prepare for return value passing from the initialization
+ procedure of the daemon process. */
+ if (daemon_retval_init () < 0)
+ error (1, errno, "Failed to create pipe.");
+
+ /* Do the fork. */
+ if ((pid = daemon_fork ()) < 0)
+ {
+ /* Exit on error. */
+ daemon_retval_done ();
+ error (1, errno, "Failed to fork");
+ }
+ else if (pid)
+ {
+ /* The parent. */
+ int ret;
+
+ /* Wait for 20 seconds for the return value passed from the
+ daemon process. . */
+ if ((ret = daemon_retval_wait (20)) < 0)
+ error (1, errno,
+ "Could not receive return value from daemon process");
+
+ return ret;
+ }
+ else
+ {
+ /* The daemon. */
+ /* Close FDs. */
+ if (daemon_close_all (-1) < 0)
+ daemon_error (1, errno, "Failed to close all file descriptors");
+
+ /* Create the PID file. */
+ if (daemon_pid_file_create () < 0)
+ daemon_error (2, errno, "Could not create PID file");
+ }
+ }
+#endif /* HAVE_DAEMON */
+
+ err = driver_start (&errname);
+ if (err)
+ daemon_error (1, err, "Starting driver %s failed", errname);
+
+ pthread_mutex_init (&global_lock, NULL);
+
+ err = cons_init ();
+ if (err)
+ {
+ driver_fini ();
+ daemon_error (1, err, "Console library initialization failed");
+ }
+
+ err = timer_init ();
+ if (err)
+ {
+ driver_fini ();
+ daemon_error (1, err, "Timer thread initialization failed");
+ }
+
+ if (console_node)
+ console_setup_node (console_node);
+
+#if HAVE_DAEMON
+ if (daemonize)
+ /* Signal parent that all went well. */
+ daemon_retval_send (0);
+#endif
+
+ cons_server_loop ();
+
+ /* Never reached. */
+ console_exit ();
+}
diff --git a/console-client/current-vcs.c b/console-client/current-vcs.c
new file mode 100644
index 00000000..fb053799
--- /dev/null
+++ b/console-client/current-vcs.c
@@ -0,0 +1,224 @@
+/* current-vcs.c -- Add a "current vcs" symlink to the cons node.
+ Copyright (C) 2005 Free Software Foundation, Inc.
+ Written by Samuel Thibault.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <argp.h>
+#include <driver.h>
+#include <input.h>
+#include <stdio.h>
+#include <error.h>
+#include <sys/mman.h>
+
+#include "trans.h"
+
+
+/* We use the same infrastructure as the kbd and mouse repeaters. */
+
+/* The default name of the node of the repeater. */
+#define DEFAULT_REPEATER_NODE "vcs"
+
+/* The name of the repeater node. */
+static char *repeater_node = DEFAULT_REPEATER_NODE;
+
+/* The repeater node. */
+static consnode_t vcs_node;
+
+
+/* Callbacks for vcs console node. */
+
+/* Reading the link to get current vcs, returns length of path (trailing \0
+ excluded). */
+static error_t
+vcs_readlink (struct iouser *user, struct node *np, char *buf)
+{
+ int cur;
+ error_t ret = 0;
+
+ ret = console_current_id (&cur);
+ if (!ret)
+ {
+ if (!buf)
+ ret = snprintf (NULL, 0, "%s/%d", cons_file, cur);
+ else
+ ret = sprintf (buf, "%s/%d", cons_file, cur);
+
+ if (ret < 0)
+ ret = -errno;
+ }
+ else
+ ret = -ret;
+ return ret;
+}
+
+static error_t
+vcs_read (struct protid *user, char **data,
+ mach_msg_type_number_t * datalen, off_t offset,
+ mach_msg_type_number_t amount)
+{
+ int err;
+ int size;
+ char *buf;
+
+ if (amount > 0)
+ {
+ size = vcs_readlink (user->user, NULL, NULL);
+ if (size < 0)
+ return -size;
+
+ buf = alloca (size);
+
+ err = vcs_readlink (user->user, NULL, buf);
+
+ if (err < 0)
+ return -err;
+
+ if (offset + amount > size)
+ amount = size - offset;
+ if (amount < 0)
+ amount = 0;
+
+ if (*datalen < amount)
+ {
+ *data = mmap (0, amount, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ return ENOMEM;
+ }
+
+ memcpy (*data, buf + offset, amount);
+ *datalen = amount;
+ }
+ return 0;
+}
+
+/* Making a link to set current vcs.
+ Relative values perform relative switches. */
+static error_t
+vcs_mksymlink (struct iouser *user, struct node *np, char *name)
+{
+ char *c, *d;
+ int vt, delta = 0;
+
+ c = strrchr (name, '/');
+ if (!c)
+ c = name;
+ else
+ c++;
+ if (!*c)
+ return EINVAL;
+
+ if (*c == '-' || *c == '+')
+ delta = 1;
+
+ vt = strtol (c, &d, 10);
+ if (*d)
+ /* Bad number. */
+ return EINVAL;
+
+ if (!vt)
+ return 0;
+ return console_switch (delta ? 0 : vt, delta ? vt : 0);
+}
+
+
+static const char doc[] = "Current VCS Driver";
+
+static const struct argp_option options[] =
+ {
+ { "repeater", 'r', "NODE", OPTION_ARG_OPTIONAL,
+ "Set a current vcs translator on NODE (default: " DEFAULT_REPEATER_NODE ")"},
+ { 0 }
+ };
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ int *pos = (int *) state->input;
+
+ switch (key)
+ {
+ case 'r':
+ repeater_node = arg ? arg: DEFAULT_REPEATER_NODE;
+ break;
+
+ case ARGP_KEY_END:
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ *pos = state->next;
+ return 0;
+}
+
+static struct argp argp = {options, parse_opt, 0, doc};
+
+/* Initialize the current VCS driver. */
+static error_t
+current_vcs_init (void **handle, int no_exit, int argc, char *argv[], int *next)
+{
+ error_t err;
+ int pos = 1;
+
+ /* Parse the arguments. */
+ err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER | ARGP_NO_EXIT
+ | ARGP_SILENT, 0, &pos);
+ *next += pos - 1;
+
+ if (err && err != EINVAL)
+ return err;
+
+ return 0;
+}
+
+static error_t
+current_vcs_start (void *handle)
+{
+ error_t err;
+
+ err = console_create_consnode (repeater_node, &vcs_node);
+ if (err)
+ return err;
+
+ vcs_node->read = vcs_read;
+ vcs_node->write = NULL;
+ vcs_node->select = NULL;
+ vcs_node->open = NULL;
+ vcs_node->close = NULL;
+ vcs_node->demuxer = NULL;
+ vcs_node->readlink = vcs_readlink;
+ vcs_node->mksymlink = vcs_mksymlink;
+ console_register_consnode (vcs_node);
+
+ return 0;
+}
+
+static error_t
+current_vcs_fini (void *handle, int force)
+{
+ console_unregister_consnode (vcs_node);
+ console_destroy_consnode (vcs_node);
+ return 0;
+}
+
+
+struct driver_ops driver_current_vcs_ops =
+ {
+ current_vcs_init,
+ current_vcs_start,
+ current_vcs_fini
+ };
diff --git a/console-client/display.h b/console-client/display.h
new file mode 100644
index 00000000..1e73bdee
--- /dev/null
+++ b/console-client/display.h
@@ -0,0 +1,154 @@
+/* display.h - The interface to and for a display driver.
+ Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef _DISPLAY_H_
+#define _DISPLAY_H_ 1
+
+#include <errno.h>
+#include <stdint.h>
+
+#include <hurd/console.h>
+
+
+/* The display drivers are set up by the driver's initialization
+ routine and added to the console client with driver_add_display.
+ All subsequent operations on the display are fully synchronized by
+ the caller. The driver deinitialization routine should call
+ driver_remove_display. */
+
+/* Forward declaration. */
+struct display_ops;
+typedef struct display_ops *display_ops_t;
+
+/* Add the display HANDLE with the operations OPS to the console
+ client. As soon as this is called, operations on this display may
+ be performed, even before the function returns. */
+error_t driver_add_display (display_ops_t ops, void *handle);
+
+/* Remove the display HANDLE with the operations OPS from the console
+ client. As soon as this function returns, no operations will be
+ performed on the display anymore. */
+error_t driver_remove_display (display_ops_t ops, void *handle);
+
+
+struct display_ops
+{
+ /* Set the cursor's position on display HANDLE to column COL and row
+ ROW. The home position (COL and ROW both being 0) is the upper
+ left corner of the screen matrix. COL will be smaller than the
+ width and ROW will be smaller than the height of the display.
+
+ The driver is allowed to delay the effect of this operation until
+ the UPDATE function is called. */
+ error_t (*set_cursor_pos) (void *handle, uint32_t col, uint32_t row);
+
+
+ /* Set the cursor's state to STATE on display HANDLE. The possible
+ values for STATE are defined by the CONS_CURSOR_* symbols in
+ <hurd/console.h>.
+
+ The driver is allowed to delay the effect of this operation until
+ the UPDATE function is called. */
+ error_t (*set_cursor_status) (void *handle, uint32_t state);
+
+
+ /* Scroll the display by DELTA lines up if DELTA is positive, and by
+ -DELTA lines down if DELTA is negative. DELTA will never be
+ zero, and the absolute value if DELTA will be smaller than or
+ equal to the height of the screen matrix.
+
+ The area that becomes free will be filled in a subsequent write
+ call. The purpose of the function is two-fold: It is called with
+ an absolute value of DELTA smaller than the screen height to
+ perform scrolling. It is called with an absolute value of DELTA
+ equal to the screen height to prepare a full refresh of the
+ screen. In the latter case the driver should not really perform
+ any scrolling. Instead it might deallocate limited resources
+ (like display glyph slots and palette colors) if that helps to
+ perform the subsequent write. It goes without saying that the
+ same deallocation, if any, should be performed on the area that
+ will be filled with the scrolled in content.
+
+ XXX Possibly need a function to invalidate scrollback buffer, or
+ in general to signal a switch of the console so state can be
+ reset. Only do this if we make guarantees about validity of
+ scrollback buffer, of course.
+
+ The driver is allowed to delay the effect of this operation until
+ the UPDATE function is called. */
+ error_t (*scroll) (void *handle, int delta);
+
+ /* Deallocate the scarce resources (like font glyph slots, colors
+ etc) in the LENGTH entries of the screen matrix starting from
+ position COL and ROW. This call is immediately followed by calls
+ to write which cover the same area. If there are no scarce
+ resources, the caller might do nothing. */
+ error_t (*clear) (void *handle, size_t length, uint32_t col, uint32_t row);
+
+ /* Write the text STR with LENGTH characters to column COL and row
+ ROW on display HANDLE. LENGTH can be longer than the width of
+ the screen matrix minus COL. In this case the driver should
+ automatically wrap around the edge. However, LENGTH will never
+ be so huge that the whole text starting from COL and ROW will go
+ beyond the lower right corner of the screen matrix.
+
+ The driver is allowed to delay the effect of this operation until
+ the UPDATE function is called. */
+ error_t (*write) (void *handle, conchar_t *str, size_t length,
+ uint32_t col, uint32_t row);
+
+ /* Flush all the past changes on display HANDLE that have not been
+ flushed yet. This should make all past changes visible to the
+ user. The driver is free to make them visible earlier, but it
+ must happen before returning from this call.
+
+ The purpose of this function is to group several related change
+ operations together, but also several change operations which
+ occur in rapid succession. The first grouping is essential to
+ make it possible for a driver to implement double buffering. The
+ second grouping is important to keep up performance if there is a
+ lot of activity on the screen matrix. */
+ error_t (*update) (void *handle);
+
+ /* Flash the display HANDLE once. This should be done
+ immediately. */
+ error_t (*flash) (void *handle);
+
+ /* Do not use, do not remove. */
+ void (*deprecated) (void *handle, int key);
+
+ /* Change the dimension of the physical screen to one that can
+ display the vcons with the size of WIDTH * HEIGHT and clear the
+ old screen. If the physical screen already has the right
+ resolution do nothing. This function is always followed by a
+ write that covers the whole new screen. */
+ error_t (*set_dimension) (void *handle, unsigned int width,
+ unsigned int height);
+
+ /* Move the mouse cursor to the position X, Y. If the mouse cursor
+ is visible, update its position. */
+ error_t (*set_mousecursor_pos) (void *handle, float x, float y);
+
+ /* If STATUS is set to 0, hide the mouse cursor, otherwise show
+ it. */
+ error_t (*set_mousecursor_status) (void *handle, int status);
+};
+
+#endif /* _DISPLAY_H_ */
diff --git a/console-client/driver.c b/console-client/driver.c
new file mode 100644
index 00000000..64078241
--- /dev/null
+++ b/console-client/driver.c
@@ -0,0 +1,361 @@
+/* driver.c - The console client driver code.
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <dlfcn.h>
+
+#include <pthread.h>
+
+#include "driver.h"
+
+
+/* The number of entries by which we grow a driver or component list
+ if we need more space. */
+#define LIST_GROW 4
+
+/* The path where we search for drivers, in addition to the default
+ path. The directories are separated by '\0' and terminated with an
+ empty string. XXX Should use argz or something. XXX Should get a
+ protective lock. */
+char *driver_path;
+
+/* The driver list lock, the list itself, its current length and the
+ total number of entries in the list. */
+pthread_mutex_t driver_list_lock;
+driver_t driver_list;
+size_t driver_list_len;
+size_t driver_list_alloc;
+
+
+/* Initialize the driver framework. */
+error_t
+driver_init (void)
+{
+ pthread_mutex_init (&driver_list_lock, NULL);
+ pthread_mutex_init (&display_list_lock, NULL);
+ pthread_mutex_init (&input_list_lock, NULL);
+ pthread_mutex_init (&bell_list_lock, NULL);
+ return 0;
+}
+
+
+/* Deinitialize and unload all loaded drivers and deinitialize the
+ driver framework. */
+error_t
+driver_fini (void)
+{
+ unsigned int i;
+
+ pthread_mutex_lock (&driver_list_lock);
+ for (i = 0; i < driver_list_len; i++)
+ {
+ driver_list[i].ops->fini (driver_list[i].handle, 1);
+ dlclose (driver_list[i].module);
+ free (driver_list[i].name);
+ free (driver_list[i].driver);
+ }
+ driver_list_len = 0;
+ pthread_mutex_unlock (&driver_list_lock);
+ return 0;
+}
+
+
+/* Load, intialize and (if START is non-zero) start the driver DRIVER
+ under the given NAME (which must be unique among all loaded
+ drivers) with arguments ARGZ with length ARGZ_LEN. This function
+ will grab the driver list lock. The driver itself might try to
+ grab the display, input source and bell list locks as well. */
+error_t driver_add (const char *const name, const char *const driver,
+ int argc, char *argv[], int *next, int start)
+{
+ error_t err;
+ static char cons_defpath[] = CONSOLE_DEFPATH;
+ driver_ops_t ops;
+ char *filename = NULL;
+ char *modname;
+ void *shobj = NULL;
+ driver_t drv;
+ unsigned int i;
+ char *dir = driver_path;
+ int defpath = 0;
+ char *opt_backup;
+
+ pthread_mutex_lock (&driver_list_lock);
+ for (i = 0; i < driver_list_len; i++)
+ if (driver_list[i].name && !strcmp (driver_list[i].name, name))
+ {
+ pthread_mutex_unlock (&driver_list_lock);
+ return EEXIST;
+ }
+
+ if (!dir || !*dir)
+ {
+ dir = cons_defpath;
+ defpath = 1;
+ }
+
+ while (dir)
+ {
+ if (filename)
+ free (filename);
+ if (asprintf (&filename,
+ "%s/%s%s", dir, driver, CONSOLE_SONAME_SUFFIX) < 0)
+ {
+ pthread_mutex_unlock (&driver_list_lock);
+ return ENOMEM;
+ }
+
+ errno = 0;
+ shobj = dlopen (filename, RTLD_LAZY);
+ if (!shobj)
+ {
+ const char *errstring = dlerror (); /* Must always call or it leaks! */
+ if (errno != ENOENT)
+ {
+ free (filename);
+ pthread_mutex_unlock (&driver_list_lock);
+ return errno ?: EGRATUITOUS;
+ }
+ }
+ else
+ break;
+
+ dir += strlen (dir) + 1;
+ if (!*dir)
+ {
+ if (defpath)
+ break;
+ else
+ {
+ dir = cons_defpath;
+ defpath = 1;
+ }
+ }
+ }
+
+ if (!shobj)
+ {
+ if (filename)
+ free (filename);
+ pthread_mutex_unlock (&driver_list_lock);
+ return ENOENT;
+ }
+
+ if (asprintf (&modname, "driver_%s_ops", driver) < 0)
+ {
+ dlclose (shobj);
+ free (filename);
+ pthread_mutex_unlock (&driver_list_lock);
+ return ENOMEM;
+ }
+
+ ops = dlsym (shobj, modname);
+ free (modname);
+ if (!ops || !ops->init)
+ {
+ dlclose (shobj);
+ free (filename);
+ pthread_mutex_unlock (&driver_list_lock);
+ return EGRATUITOUS;
+ }
+
+ if (driver_list_len == driver_list_alloc)
+ {
+ size_t new_alloc = driver_list_alloc + LIST_GROW;
+ driver_t new_list = realloc (driver_list,
+ new_alloc * sizeof (*driver_list));
+ if (!new_list)
+ {
+ dlclose (shobj);
+ free (filename);
+ pthread_mutex_unlock (&driver_list_lock);
+ return errno;
+ }
+ driver_list = new_list;
+ driver_list_alloc = new_alloc;
+ }
+ drv = &driver_list[driver_list_len];
+
+ drv->name = strdup (name);
+ drv->driver = strdup (driver);
+ drv->filename = filename;
+ drv->ops = ops;
+ drv->module = shobj;
+ if (!drv->name || !drv->driver)
+ {
+ if (drv->name)
+ free (drv->name);
+ if (drv->driver)
+ free (drv->driver);
+ dlclose (shobj);
+ free (filename);
+ pthread_mutex_unlock (&driver_list_lock);
+ return ENOMEM;
+ }
+
+ opt_backup = argv[*next - 1];
+ argv[*next - 1] = (char *) name;
+ /* If we will start the driver, the init function must not exit. */
+ err = (*drv->ops->init) (&drv->handle, start, argc - (*next - 1),
+ argv + *next - 1, next);
+ argv[*next - 1] = opt_backup;
+
+ if (!err && start && drv->ops->start)
+ err = (*drv->ops->start) (drv->handle);
+ if (err)
+ {
+ free (drv->name);
+ free (drv->driver);
+ dlclose (shobj);
+ free (filename);
+ pthread_mutex_unlock (&driver_list_lock);
+ return err;
+ }
+
+ driver_list_len++;
+ pthread_mutex_unlock (&driver_list_lock);
+ return 0;
+}
+
+
+/* Start all drivers. Only used once at start up, after all the
+ option parsing and driver initialization.
+
+ Returns 0 on success, and the name of a driver if it initializing
+ that driver fails. */
+error_t
+driver_start (char **name)
+{
+ error_t err = 0;
+ int i;
+
+ pthread_mutex_lock (&driver_list_lock);
+ for (i = 0; i < driver_list_len; i++)
+ {
+ if (driver_list[i].ops->start)
+ err = (*driver_list[i].ops->start) (driver_list[i].handle);
+ if (err)
+ {
+ *name = driver_list[i].name;
+ while (i > 0)
+ {
+ i--;
+ (*driver_list[i].ops->fini) (driver_list[i].handle, 1);
+ }
+ break;
+ }
+ }
+ pthread_mutex_unlock (&driver_list_lock);
+ return err;
+}
+
+
+/* Deinitialize and unload the driver with the name NAME. This
+ function will grab the driver list lock. The driver might try to
+ grab the display, input source and bell list locks as well. */
+error_t driver_remove (const char *const name)
+{
+ error_t err;
+ unsigned int i;
+
+ pthread_mutex_lock (&driver_list_lock);
+ for (i = 0; i < driver_list_len; i++)
+ if (driver_list[i].name && !strcmp (driver_list[i].name, name))
+ {
+ err = driver_list[i].ops->fini (driver_list[i].handle, 0);
+ if (!err)
+ {
+ dlclose (driver_list[i].module);
+ free (driver_list[i].name);
+ free (driver_list[i].driver);
+ free (driver_list[i].filename);
+ while (i + 1 < driver_list_len)
+ {
+ driver_list[i] = driver_list[i + 1];
+ i++;
+ }
+ driver_list_len--;
+ }
+ pthread_mutex_unlock (&driver_list_lock);
+ return err;
+ }
+ pthread_mutex_unlock (&driver_list_lock);
+ return ESRCH;
+}
+
+#define ADD_REMOVE_COMPONENT(component) \
+pthread_mutex_t component##_list_lock; \
+component##_t component##_list; \
+size_t component##_list_len; \
+size_t component##_list_alloc; \
+ \
+error_t \
+driver_add_##component (component##_ops_t ops, void *handle) \
+{ \
+ pthread_mutex_lock (&component##_list_lock); \
+ if (component##_list_len == component##_list_alloc) \
+ { \
+ size_t new_alloc = component##_list_alloc + LIST_GROW; \
+ component##_t new_list = realloc (component##_list, \
+ new_alloc \
+ * sizeof (*component##_list)); \
+ if (!new_list) \
+ { \
+ pthread_mutex_unlock (&component##_list_lock); \
+ return errno; \
+ } \
+ component##_list = new_list; \
+ component##_list_alloc = new_alloc; \
+ } \
+ component##_list[component##_list_len].ops = ops; \
+ component##_list[component##_list_len].handle = handle; \
+ component##_list_len++; \
+ pthread_mutex_unlock (&component##_list_lock); \
+ return 0; \
+} \
+ \
+error_t \
+driver_remove_##component (component##_ops_t ops, void *handle) \
+{ \
+ unsigned int i; \
+ \
+ pthread_mutex_lock (&component##_list_lock); \
+ for (i = 0; i < component##_list_len; i++) \
+ if (component##_list[i].ops == ops \
+ && component##_list[i].handle == handle) \
+ { \
+ while (i + 1 < component##_list_len) \
+ { \
+ component##_list[i] = component##_list[i + 1]; \
+ i++; \
+ } \
+ component##_list_len--; \
+ } \
+ pthread_mutex_unlock (&component##_list_lock); \
+ return 0; \
+}
+
+ADD_REMOVE_COMPONENT (display)
+ADD_REMOVE_COMPONENT (input)
+ADD_REMOVE_COMPONENT (bell)
diff --git a/console-client/driver.h b/console-client/driver.h
new file mode 100644
index 00000000..c1b24b07
--- /dev/null
+++ b/console-client/driver.h
@@ -0,0 +1,290 @@
+/* driver.h - The interface to and for a console client driver.
+ Copyright (C) 2002, 2005 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef _CONSOLE_DRIVER_H_
+#define _CONSOLE_DRIVER_H_ 1
+
+#include <errno.h>
+#include <stddef.h>
+#include <pthread.h>
+
+#include "display.h"
+#include "input.h"
+#include "bell.h"
+
+
+/* The path where we search for drivers, in addition to the default
+ path. The directories are separated by '\0' and terminated with an
+ empty string. XXX Should use argz or something. XXX Should get a
+ protective lock. */
+extern char *driver_path;
+
+
+/* The driver framework allows loading and unloading of new drivers.
+ It also provides some operations on the loaded drivers. The device
+ framework module does its own locking, so all operations can be run
+ at any time by any thread. */
+
+/* Initialize the driver framework. */
+error_t driver_init (void);
+
+/* Deinitialize and unload all loaded drivers and deinitialize the
+ driver framework. */
+error_t driver_fini (void);
+
+/* Forward declaration. */
+struct driver_ops;
+typedef struct driver_ops *driver_ops_t;
+
+/* Load, initialize and (if START is non-zero) start the driver DRIVER
+ under the given NAME (which must be unique among all loaded
+ drivers) with arguments ARGC, ARGV and NEXT (see
+ parse_startup_args). This function will grab the driver list lock.
+ The driver itself might try to grab the display, input source and
+ bell list locks as well. */
+error_t driver_add (const char *const name, const char *const driver,
+ int argc, char *argv[], int *next, int start);
+
+/* Start all drivers. Only used once at start up, after all the
+ option parsing and driver initialization.
+
+ Returns 0 on success, and the error if it initializing that driver
+ fails (NAME points to the driver name then). */
+error_t driver_start (char **name);
+
+/* Deinitialize and unload the driver with the name NAME. This
+ function will grab the driver list lock. The driver might try to
+ grab the display, input source and bell list locks as well. */
+error_t driver_remove (const char *const name);
+
+/* Iterate over all loaded drivers. This macro will grab the driver
+ list lock. You use it with a block:
+
+ driver_iterate
+ {
+ printf ("%s\n", driver->ops->name);
+ }
+
+ Or even just:
+
+ driver_iterate printf ("%s\n", driver->ops->name);
+
+ The variable DRIVER is provided by the macro. */
+#define driver_iterate \
+ for (driver_t driver = (pthread_mutex_lock (&driver_list_lock), \
+ &driver_list[0]); \
+ driver < &driver_list[driver_list_len] \
+ || (pthread_mutex_unlock (&driver_list_lock), 0); \
+ driver++)
+
+
+struct driver_ops
+{
+ /* Initialize an instance of the driver and return a handle for it
+ in HANDLE. The options in ARGC, ARGV and NEXT should be
+ processed and validated.
+
+ If NO_EXIT is zero, the function might exit on fatal errors or
+ invalid arguments. The drawback is that it must NOT allocate any
+ resources that need to be freed or deallocated explicitely before
+ exiting the program either, because other driver instances are
+ also allowed to exit without prior notice at some later time.
+ Allocation and initialization of such resources (like the video
+ card) must be delayed until the start() function is called (see
+ below).
+
+ If NO_EXIT is non-zero, the function must not exit, but report
+ all errors back to the caller. In this case, it is guaranteed
+ that the START function is called immediately after this function
+ returns, and that the driver is properly unloaded with fini() at
+ some later time.
+
+ The above behaviour, and the split into an init() and a start()
+ function, was carefully designed to allow the init() function the
+ optimal use of argp at startup and at run time to parse options.
+
+ ARGV[*NEXT] is the next argument to be parsed. ARGC is the
+ number of total arguments in ARGV. The function should increment
+ *NEXT for each argument parsed. The function should not reorder
+ arguments, nor should it parse non-option arguments. It should
+ also not parse over any single "--" argument.
+
+ Every driver must implement this function.
+
+ If NO_EXIT is zero, the function should return zero on success
+ and otherwise either terminate or return an appropriate error
+ value. If it returns, either the program terminates because of
+ other errors, or the function start() is called.
+
+ If NO_EXIT is non-zero, the function should return zero on
+ success and an appropriate error value otherwise. If it returns
+ success, the function start() will be called next, otherwise
+ nothing happens. */
+ error_t (*init) (void **handle, int no_exit,
+ int argc, char *argv[], int *next);
+
+ /* Activate the driver instance. This function should load all the
+ display, input and bell river components for this driver
+ instance.
+
+ If successful, the function should return zero. In this case it
+ is guaranteed that fini() will be called before the program
+ terminates. If not successful, the function should free all
+ resources associated with HANDLE and return non-zero. */
+ error_t (*start) (void *handle);
+
+ /* Deinitialize the driver. This should remove all the individual
+ drivers installed by init() and release all resources. It should
+ also reset all hardware devices into the state they had before
+ calling init(), as far as applicable. HANDLE is provided as
+ returned by init().
+
+ The function is allowed to fail if FORCE is 0. If FORCE is not
+ 0, the driver should remove itself no matter what. */
+ error_t (*fini) (void *handle, int force);
+
+
+ /* Save the status of the hardware. */
+ void (*save_status) (void *handle);
+
+ /* Restore the status of the hardware. */
+ void (*restore_status) (void *handle);
+};
+
+
+/* The driver structure. */
+struct driver
+{
+ /* The unique name of the driver. */
+ char *name;
+
+ /* The plugin name. */
+ char *driver;
+
+ /* The filename that was identified as providing the plugin. */
+ char *filename;
+
+ driver_ops_t ops;
+ void *handle;
+
+ /* The following members are private to the driver support code. Do
+ not use. */
+
+ /* The shared object handle as returned by dlopen(). */
+ void *module;
+};
+typedef struct driver *driver_t;
+
+
+/* Forward declarations needed by the macro above. Don't use these
+ variables directly. */
+extern pthread_mutex_t driver_list_lock;
+extern driver_t driver_list;
+extern size_t driver_list_len;
+
+
+/* Iterate over all loaded displays. This macro will grab the display
+ list lock. You use it with a block, just like driver_iterate.
+
+ display_iterate display->ops->flash (display->handle);
+
+ The variable DISPLAY is provided by the macro. */
+#define display_iterate \
+ for (display_t display = (pthread_mutex_lock (&display_list_lock), \
+ &display_list[0]); \
+ display < &display_list[display_list_len] \
+ || (pthread_mutex_unlock (&display_list_lock), 0); \
+ display++)
+
+
+/* The display structure. */
+struct display
+{
+ display_ops_t ops;
+ void *handle;
+};
+typedef struct display *display_t;
+
+
+/* Forward declarations needed by the macro above. Don't use these
+ variables directly. */
+extern pthread_mutex_t display_list_lock;
+extern display_t display_list;
+extern size_t display_list_len;
+
+
+/* Iterate over all loaded inputs. This macro will grab the input
+ list lock. You use it with a block, just like driver_iterate.
+
+ input_iterate input->ops->set_scroll_lock_status (input->handle, 0);
+
+ The variable INPUT is provided by the macro. */
+#define input_iterate \
+ for (input_t input = (pthread_mutex_lock (&input_list_lock), &input_list[0]); \
+ input < &input_list[input_list_len] \
+ || (pthread_mutex_unlock (&input_list_lock), 0); \
+ input++)
+
+
+/* The input structure. */
+struct input
+{
+ input_ops_t ops;
+ void *handle;
+};
+typedef struct input *input_t;
+
+
+/* Forward declarations needed by the macro above. Don't use these
+ variables directly. */
+extern pthread_mutex_t input_list_lock;
+extern input_t input_list;
+extern size_t input_list_len;
+
+
+/* Iterate over all loaded bells. This macro will grab the bell list
+ lock. You use it with a block, just like driver_iterate.
+
+ bell_iterate bell->ops->beep (bell->handle);
+
+ The variable BELL is provided by the macro. */
+#define bell_iterate \
+ for (bell_t bell = (pthread_mutex_lock (&bell_list_lock), &bell_list[0]); \
+ bell < &bell_list[bell_list_len] \
+ || (pthread_mutex_unlock (&bell_list_lock), 0); \
+ bell++)
+
+
+/* The bell structure, needed by the macro above. Don't use it
+ directly. */
+struct bell
+{
+ bell_ops_t ops;
+ void *handle;
+};
+typedef struct bell *bell_t;
+
+/* Forward declarations needed by the macro above. Don't use these
+ variables directly. */
+extern pthread_mutex_t bell_list_lock;
+extern bell_t bell_list;
+extern size_t bell_list_len;
+
+#endif /* _CONSOLE_DRIVER_H_ */
diff --git a/console-client/generic-speaker.c b/console-client/generic-speaker.c
new file mode 100644
index 00000000..c03cc47e
--- /dev/null
+++ b/console-client/generic-speaker.c
@@ -0,0 +1,552 @@
+/* generic-speaker.c - The simple speaker bell driver.
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+#include <sys/io.h>
+#include <string.h>
+#include <argp.h>
+
+#include "driver.h"
+#include "timer.h"
+
+/* This driver should work on IA32, IA64, Alpha, PowerPC, MIPS and ARM
+ architectures. It requires a Programmable Interrupt Timer (PIT) at
+ I/O ports 0x40 - 0x43 and a speaker that can be connected to timer
+ 2 at I/O port 0x61. */
+
+/* The beep timer. */
+static struct timer_list generic_speaker_timer;
+
+/* Forward declaration. */
+static struct bell_ops generic_speaker_ops;
+
+
+/* The speaker port. */
+#define SPEAKER 0x61
+
+/* If 0, follow state of SPEAKER_DATA bit, otherwise enable output
+ from timer 2. */
+#define SPEAKER_TMR2 0x01
+
+/* If SPEAKER_TMR2 is not set, this provides direct input into the
+ speaker. Otherwise, this enables or disables the output from the
+ timer. */
+#define SPEAKER_DATA 0x02
+
+
+/* The PIT channel value ports. You can write to and read from them.
+ Do not mess with timer 0 or 1. */
+#define PIT_COUNTER_0 0x40
+#define PIT_COUNTER_1 0x41
+#define PIT_COUNTER_2 0x42
+
+/* The frequency of the PIT clock. */
+#define PIT_FREQUENCY 0x1234dd
+
+/* The PIT control port. You can only write to it. Do not mess with
+ timer 0 or 1. */
+#define PIT_CTRL 0x43
+#define PIT_CTRL_SELECT_MASK 0xc0
+#define PIT_CTRL_SELECT_0 0x00
+#define PIT_CTRL_SELECT_1 0x40
+#define PIT_CTRL_SELECT_2 0x80
+
+/* Read and load control. */
+#define PIT_CTRL_READLOAD_MASK 0x30
+#define PIT_CTRL_COUNTER_LATCH 0x00 /* Hold timer value until read. */
+#define PIT_CTRL_READLOAD_LSB 0x10 /* Read/load the LSB. */
+#define PIT_CTRL_READLOAD_MSB 0x20 /* Read/load the MSB. */
+#define PIT_CTRL_READLOAD_WORD 0x30 /* Read/load the LSB then the MSB. */
+
+/* Mode control. */
+#define PIT_CTRL_MODE_MASK 0x0e
+
+/* Interrupt on terminal count. Setting the mode sets output to low.
+ When counter is set and terminated, output is set to high. */
+#define PIT_CTRL_INTR_ON_TERM 0x00
+
+/* Programmable one-shot. When loading counter, output is set to
+ high. When counter terminated, output is set to low. Can be
+ triggered again from that point on by setting the gate pin to
+ high. */
+#define PIT_CTRL_PROGR_ONE_SHOT 0x02
+
+/* Rate generator. Output is low for one period of the counter, and
+ high for the other. */
+#define PIT_CTRL_RATE_GEN 0x04
+
+/* Square wave generator. Output is low for one half of the period,
+ and high for the other half. */
+#define PIT_CTRL_SQUAREWAVE_GEN 0x06
+
+/* Software triggered strobe. Setting the mode sets output to high.
+ When counter is set and terminated, output is set to low. */
+#define PIT_CTRL_SOFTSTROBE 0x08
+
+/* Hardware triggered strobe. Like software triggered strobe, but
+ only starts the counter when the gate pin is set to high. */
+#define PIT_CTRL_HARDSTROBE 0x0a
+
+
+/* Count mode. */
+#define PIT_CTRL_COUNT_MASK 0x01
+#define PIT_CTRL_COUNT_BINARY 0x00 /* 16-bit binary counter. */
+#define PIT_CTRL_COUNT_BCD 0x01 /* 4-decade BCD counter. */
+
+
+/* Let's be loud! */
+
+/* The 12th root of 2. Sort of. */
+#define T_TEMP_SCALE 1.0594630943592952645
+#define T_DOWN_ONE_HALF(x) ((short) (x / T_TEMP_SCALE))
+#define T_UP_ONE_HALF(x) ((short) (x * T_TEMP_SCALE))
+#define T_DOWN_ONE_OCTAVE(x) ((short) (x / 2))
+#define T_UP_ONE_OCTAVE(x) ((short) (x * 2))
+#define T_REST ((short) 0)
+#define T_FINE ((short) (-1))
+
+#define T_b_3 T_UP_ONE_OCTAVE (T_b_2)
+#define T_b_F_3 T_UP_ONE_OCTAVE (T_b_F_2)
+#define T_a_S_3 T_b_F_3
+#define T_a_3 T_UP_ONE_OCTAVE (T_a_2)
+#define T_a_F_3 T_UP_ONE_OCTAVE (T_a_F_2)
+#define T_g_S_3 T_a_F_3
+#define T_g_3 T_UP_ONE_OCTAVE (T_g_2)
+#define T_g_F_3 T_f_S_3
+#define T_f_S_3 T_UP_ONE_OCTAVE (T_f_S_2)
+#define T_f_3 T_UP_ONE_OCTAVE (T_f_2)
+#define T_e_3 T_UP_ONE_OCTAVE (T_e_2)
+#define T_e_F_3 T_UP_ONE_OCTAVE (T_e_F_2)
+#define T_d_S_3 T_e_F_3
+#define T_d_3 T_UP_ONE_OCTAVE (T_d_2)
+#define T_d_F_3 T_c_S_3
+#define T_c_S_3 T_UP_ONE_OCTAVE (T_c_S_2)
+#define T_c_3 T_UP_ONE_OCTAVE (T_c_2)
+
+#define T_b_2 T_UP_ONE_OCTAVE (T_b_1)
+#define T_b_F_2 T_UP_ONE_OCTAVE (T_b_F_1)
+#define T_a_S_2 T_b_F_2
+#define T_a_2 T_UP_ONE_OCTAVE (T_a_1)
+#define T_a_F_2 T_UP_ONE_OCTAVE (T_a_F_1)
+#define T_g_S_2 T_a_F_2
+#define T_g_2 T_UP_ONE_OCTAVE (T_g_1)
+#define T_g_F_2 T_f_S_2
+#define T_f_S_2 T_UP_ONE_OCTAVE (T_f_S_1)
+#define T_f_2 T_UP_ONE_OCTAVE (T_f_1)
+#define T_e_2 T_UP_ONE_OCTAVE (T_e_1)
+#define T_e_F_2 T_UP_ONE_OCTAVE (T_e_F_1)
+#define T_d_S_2 T_e_F_2
+#define T_d_2 T_UP_ONE_OCTAVE (T_d_1)
+#define T_d_F_2 T_c_S_2
+#define T_c_S_2 T_UP_ONE_OCTAVE (T_c_S_1)
+#define T_c_2 T_UP_ONE_OCTAVE (T_c_1)
+
+#define T_b_1 T_UP_ONE_HALF (T_b_F_1)
+#define T_b_F_1 T_UP_ONE_HALF (T_a_1)
+#define T_a_S_1 T_b_F_1
+#define T_a_1 ((short) (440))
+#define T_a_F_1 T_DOWN_ONE_HALF (T_a_1)
+#define T_g_S_1 T_a_F_1
+#define T_g_1 T_DOWN_ONE_HALF (T_a_F_1)
+#define T_g_F_1 T_f_S_1
+#define T_f_S_1 T_DOWN_ONE_HALF (T_g_1)
+#define T_f_1 T_DOWN_ONE_HALF (T_f_S_1)
+#define T_e_1 T_DOWN_ONE_HALF (T_f_1)
+#define T_e_F_1 T_DOWN_ONE_HALF (T_e_1)
+#define T_d_S_1 T_e_F_1
+#define T_d_1 T_DOWN_ONE_HALF (T_e_F_1)
+#define T_d_F_1 T_c_S_1
+#define T_c_S_1 T_DOWN_ONE_HALF (T_d_1)
+#define T_c_1 T_DOWN_ONE_HALF (T_c_S_1)
+
+#define T_b T_DOWN_ONE_OCTAVE (T_b_1)
+#define T_b_F T_DOWN_ONE_OCTAVE (T_b_F_1)
+#define T_a_S T_b_F
+#define T_a T_DOWN_ONE_OCTAVE (T_a_1)
+#define T_a_F T_DOWN_ONE_OCTAVE (T_a_F_1)
+#define T_g_S T_a_F
+#define T_g T_DOWN_ONE_OCTAVE (T_g_1)
+#define T_g_F T_f_S
+#define T_f_S T_DOWN_ONE_OCTAVE (T_f_S_1)
+#define T_f T_DOWN_ONE_OCTAVE (T_f_1)
+#define T_e T_DOWN_ONE_OCTAVE (T_e_1)
+#define T_e_F T_DOWN_ONE_OCTAVE (T_e_F_1)
+#define T_d_S T_e_F
+#define T_d T_DOWN_ONE_OCTAVE (T_d_1)
+#define T_d_F T_c_S
+#define T_c_S T_DOWN_ONE_OCTAVE (T_c_S_1)
+#define T_c T_DOWN_ONE_OCTAVE (T_c_1)
+
+struct note
+{
+ short pitch;
+ short duration;
+};
+
+struct melody
+{
+ char *name;
+ int measure;
+ struct note *next;
+ struct note note[];
+};
+
+#define BELL_CLASSIC "classic"
+#define BELL_LINUX "linux"
+#define BELL_ALARM "alarm"
+#define BELL_CMAJOR "cmajor"
+
+static struct melody beep1 =
+ { BELL_CLASSIC, 160, NULL, {
+ /* The classical bell. */
+ { T_a_1, 4 }, { T_FINE, 0 }
+ } };
+
+static struct melody beep2 =
+ { BELL_LINUX, 60, NULL, {
+ /* The Linux bell. */
+ { 750, 1 }, { T_FINE, 0 }
+ } };
+
+static struct melody beep3 =
+ { BELL_ALARM, 160, NULL, {
+ /* The tritonus. Quite alarming. */
+ { T_f_2, 2 }, { T_b_1, 4 }, { T_FINE, 0 }
+ } };
+
+static struct melody beep4 =
+ { BELL_CMAJOR, 160, NULL, {
+ /* C-Major chord. A bit playful. */
+ { T_c_2, 2 }, { T_e_2, 2 }, { T_g_2, 2 },
+ { T_FINE, 0 }
+ } };
+
+static struct melody *beep[] = { &beep1, &beep2, &beep3, &beep4 };
+static int active_beep;
+
+#if QUAERENDO_INVENIETIS
+struct melody tune1 =
+ { "FSF Song", 160, NULL, {
+ /* The Free Software Song. Measure: 7/4. */
+ { T_d_2, 16 }, { T_c_2, 8 }, { T_b_1, 16 }, { T_a_1, 16 },
+ { T_b_1, 16 }, { T_c_2, 8 }, { T_b_1, 8 }, { T_a_1, 8 }, { T_g_1, 16 },
+ { T_g_1, 24 }, { T_a_1, 24 }, { T_b_1, 8 },
+ { T_c_2, 24 }, { T_b_1, 14 }, { T_REST, 2 }, { T_b_1, 8 }, { T_d_2, 8 },
+ { T_a_1, 22 }, { T_REST, 2 }, { T_a_1, 32 },
+ { T_c_2, 16 }, { T_d_2, 8 }, { T_c_2, 16 }, { T_b_1, 24 },
+ { T_d_2, 16 }, { T_c_2, 8 }, { T_b_1, 16 }, { T_a_1, 16 },
+ { T_b_1, 16 }, { T_c_2, 8 }, { T_b_1, 8 }, { T_a_1, 8 }, { T_g_1, 16 },
+ { T_g_1, 24 }, { T_a_1, 24 }, { T_b_1, 8 },
+ { T_c_2, 24 }, { T_b_1, 14 }, { T_REST, 2 }, { T_b_1, 8 }, { T_d_2, 8 },
+ { T_a_1, 22 }, { T_REST, 2 }, { T_a_1, 30 }, { T_REST, 2 },
+ { T_a_1, 56 }, { T_FINE, 0 }
+ } };
+
+struct melody tune2 =
+ { "I Feel Pretty", 160, NULL, {
+ /* I feel pretty. Measure: 3/4. By Leonard Bernstein. */
+ { T_c_1, 8 }, { T_e_1, 8 },
+ { T_f_1, 4 }, { T_a_1, 20 },
+ { T_REST, 8 }, { T_c_1, 8 }, { T_e_1, 8 },
+ { T_f_1, 4 }, { T_a_1, 20 },
+ { T_REST, 8 }, { T_c_1, 8 }, { T_e_1, 8 },
+ { T_f_1, 4 }, { T_a_1, 12 }, { T_e_1, 8 },
+ { T_f_1, 4 }, { T_a_1, 12 }, { T_f_1, 8 },
+ { T_c_2, 32 },
+ { T_b_F_1,8 }, { T_a_1, 8 },
+ { T_g_1, 4 }, { T_f_1, 20 },
+ { T_REST, 8 }, { T_g_1, 8 }, { T_a_1, 8 },
+ { T_b_F_1,12}, { T_a_1, 4 }, { T_g_1, 4 }, { T_f_1, 4 },
+ { T_e_1, 16 }, { T_d_1, 3 }, { T_e_1, 2 }, { T_d_1, 3 },
+ { T_c_1, 56 }, { T_FINE, 0 }
+ } };
+
+struct melody tune3 =
+ { "Summertime", 120, NULL, {
+ /* Summertime. Measure: 4/4. By George & Ira Gershwin. */
+ { T_b_1, 8 }, { T_g_1, 8 },
+ { T_b_1, 36 }, { T_REST, 4 }, { T_a_1, 6 }, { T_g_1, 2 },
+ { T_a_1, 6 }, { T_b_1, 2 }, { T_g_1, 8 },
+ { T_e_1, 16 }, { T_b, 24 }, { T_REST, 8 },
+ { T_b_1, 8 }, { T_g_1, 8 },
+ { T_a_1, 3 }, { T_REST, 1 }, { T_a_1, 28 },
+ { T_REST, 8 }, { T_g_1, 6 }, { T_e_1, 2 },
+ { T_g_1, 6 }, { T_e_1, 2 }, { T_g_1, 8 },
+ { T_f_S_1,48}, { T_REST, 4 }, { T_b_1, 8 }, { T_g_1, 4 },
+ { T_b_1, 3 }, { T_REST, 1 }, { T_b_1, 7 }, { T_REST, 1 }, { T_b_1, 20 },
+ { T_REST, 8 }, { T_a_1, 6 }, { T_g_1, 2 },
+ { T_a_1, 6 }, { T_b_1, 2 }, { T_g_1, 8 },
+ { T_e_1, 16 }, { T_b, 32 }, { T_REST, 8 },
+ { T_b, 8 }, { T_d_1, 8 }, { T_b, 4 },
+ { T_d_1, 4 }, { T_e_1, 8 }, { T_g_1, 8 },
+ /* Keen attempt at a glissando. */
+ { T_b_1, 2 }, { T_b_1 + (T_b_1 - T_a_1) / 3, 1 },
+ { T_b_1 + 2 * (T_b_1 - T_a_1) / 3, 1 },
+ { T_a_1, 12 }, { T_g_1, 15 }, { T_REST, 1 },
+ { T_g_1, 4 }, { T_e_1, 68 },
+ { T_FINE, 0 }
+ } };
+
+struct melody tune4 =
+ { "Indiana Jones Theme", 250, NULL, {
+ /* Indiana Jones Theme. Measure: 4/4. By John Williams. */
+ { T_e_1, 4 }, { T_REST, 8 }, { T_f_1, 4 },
+ { T_g_1, 4 }, { T_REST, 4 }, { T_c_2, 24 },
+ { T_REST, 16 }, { T_d_1, 4 }, { T_REST, 8 }, { T_e_1, 4 },
+ { T_f_1, 32 },
+ { T_REST, 16 }, { T_g_1, 4 }, { T_REST, 8 }, { T_a_1, 4 },
+ { T_b_1, 4 }, { T_REST, 4 }, { T_f_2, 24 },
+ { T_REST, 16 }, { T_a_1, 4 }, { T_REST, 8 }, { T_b_1, 4 },
+ { T_c_2, 8 }, { T_REST, 8 }, { T_d_2, 12 }, { T_REST, 4 },
+ { T_e_2, 8 }, { T_REST, 8 },
+ { T_e_1, 4 }, { T_REST, 8 }, { T_f_1, 4 },
+ { T_g_1, 4 }, { T_REST, 4 }, { T_c_2, 24 },
+ { T_REST, 16 }, { T_d_2, 4 }, { T_REST, 8 }, { T_e_2, 4 },
+ { T_f_2, 32 },
+ { T_REST, 16 }, { T_g_1, 4 }, { T_REST, 8 }, { T_g_1, 4 },
+ { T_e_2, 16 }, { T_d_2, 4 }, { T_REST, 8 }, { T_g_1, 4 },
+ { T_e_2, 16 }, { T_d_2, 4 }, { T_REST, 8 }, { T_g_1, 4 },
+ { T_f_2, 16 }, { T_e_2, 4 }, { T_REST, 8 }, { T_d_2, 4 },
+ { T_c_2, 32 }, { T_FINE, 0 }
+ } };
+
+struct melody *tune[] = { &tune1, &tune2, &tune3, &tune4 };
+#endif /* QUAERENDO_INVENIETIS */
+
+
+static void
+beep_off (void)
+{
+ unsigned char status;
+
+ status = inb (SPEAKER) & ~(SPEAKER_TMR2 | SPEAKER_DATA);
+ outb (status, SPEAKER);
+}
+
+static void
+beep_on (short pitch)
+{
+ unsigned char status;
+ unsigned int counter;
+
+ if (pitch < 20)
+ pitch = 20;
+ else if (pitch > 20000)
+ pitch = 20000;
+
+ counter = PIT_FREQUENCY / pitch;
+
+ /* Program timer 2. */
+ outb (PIT_CTRL_SELECT_2 | PIT_CTRL_READLOAD_WORD
+ | PIT_CTRL_SQUAREWAVE_GEN | PIT_CTRL_COUNT_BINARY, PIT_CTRL);
+ outb (counter & 0xff, PIT_COUNTER_2); /* LSB */
+ outb ((counter >> 8) & 0xff, PIT_COUNTER_2); /* MSB */
+
+ /* Start speaker. */
+ status = inb (SPEAKER) | SPEAKER_TMR2 | SPEAKER_DATA;
+ outb (status, SPEAKER);
+}
+
+
+static int
+next_note (void *handle)
+{
+ struct melody *melody = handle;
+
+ switch (melody->next->pitch)
+ {
+ case T_FINE:
+ beep_off ();
+ return 0;
+
+ case T_REST:
+ beep_off ();
+ break;
+
+ default:
+ beep_on (melody->next->pitch);
+ break;
+ }
+
+ generic_speaker_timer.expires
+ += (60 * HZ * melody->next->duration / (8 * melody->measure));
+ melody->next++;
+ return 1;
+}
+
+
+static const char doc[] = "Generic speaker driver";
+
+static const struct argp_option options[] =
+ {
+ {"bell-style", 'b', "BELL", 0, "Use one of the bells: "
+ BELL_CLASSIC ", " BELL_LINUX ", " BELL_ALARM
+ " or " BELL_CMAJOR},
+ { 0 }
+ };
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ int *pos = (int *) state->input;
+
+ switch (key)
+ {
+ case 'b':
+ {
+ unsigned int i;
+ int found = 0;
+
+ for (i = 0; i < sizeof (*beep); i++)
+ {
+ if (! strcasecmp (beep[i]->name, arg))
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ if (! found)
+ argp_usage (state);
+
+ active_beep = i;
+ break;
+ }
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ /* Save which option comes after the last accepted option. */
+ *pos = state->next;
+ return 0;
+}
+
+static struct argp argp = {options, parse_opt, 0, doc};
+
+/* Initialization of the generic speaker driver. */
+static error_t
+generic_speaker_init (void **handle, int no_exit,
+ int argc, char *argv[], int *next)
+{
+ error_t err;
+ int pos = 1;
+
+ /* Parse the arguments. */
+ err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER | ARGP_NO_EXIT
+ | ARGP_SILENT, 0 , &pos);
+ *next += pos - 1;
+
+ if (err && err != EINVAL)
+ return err;
+
+ timer_clear (&generic_speaker_timer);
+ generic_speaker_timer.fnc = &next_note;
+
+ return 0;
+}
+
+
+/* Start the driver. */
+static error_t
+generic_speaker_start (void *handle)
+{
+ error_t err;
+
+ if (ioperm (SPEAKER, 1, 1) < 0)
+ return errno;
+ if (ioperm (PIT_COUNTER_2, PIT_CTRL - PIT_COUNTER_2 + 1, 1) < 0)
+ return errno;
+
+ beep_off ();
+
+ err = driver_add_bell (&generic_speaker_ops, NULL);
+ /* XXX We can not disable the I/O ports on error as there might be
+ concurrent users of it. */
+ return err;
+}
+
+
+/* Deinitialization of the generic speaker driver. */
+static error_t
+generic_speaker_fini (void *handle, int force)
+{
+ driver_remove_bell (&generic_speaker_ops, NULL);
+
+ /* XXX We can not disable the I/O ports as there might be concurrent
+ users of it. */
+ if (timer_remove (&generic_speaker_timer))
+ beep_off ();
+ return 0;
+}
+
+
+/* Beep! */
+static error_t
+generic_speaker_beep (void *handle)
+{
+ if (timer_remove (&generic_speaker_timer))
+ beep_off ();
+ generic_speaker_timer.fnc_data = beep[active_beep];
+ beep[active_beep]->next = &beep[active_beep]->note[1];
+ beep_on (beep[active_beep]->note[0].pitch);
+ generic_speaker_timer.expires = fetch_jiffies ()
+ + (60 * HZ * beep[active_beep]->note[0].duration
+ / (8 * beep[active_beep]->measure));
+ timer_add (&generic_speaker_timer);
+ return 0;
+}
+
+#if QUAERENDO_INVENIETIS
+static void
+generic_speaker_play_melody (void *handle, unsigned int key)
+{
+ if (key < 0 || key >= sizeof (tune) / sizeof (tune[0]))
+ return;
+
+ if (timer_remove (&generic_speaker_timer))
+ beep_off ();
+ generic_speaker_timer.fnc_data = tune[key];
+ tune[key]->next = &tune[key]->note[1];
+ beep_on (tune[key]->note[0].pitch);
+ generic_speaker_timer.expires = fetch_jiffies ()
+ + (60 * HZ * tune[key]->note[0].duration / (8 * tune[key]->measure));
+ timer_add (&generic_speaker_timer);
+ return;
+}
+#endif /* QUAERENDO_INVENIETIS */
+
+
+struct driver_ops driver_generic_speaker_ops =
+ {
+ generic_speaker_init,
+ generic_speaker_start,
+ generic_speaker_fini
+ };
+
+static struct bell_ops generic_speaker_ops =
+ {
+ generic_speaker_beep,
+#if QUAERENDO_INVENIETIS
+ generic_speaker_play_melody
+#else
+ NULL
+#endif
+ };
diff --git a/console-client/input.h b/console-client/input.h
new file mode 100644
index 00000000..61deadc0
--- /dev/null
+++ b/console-client/input.h
@@ -0,0 +1,99 @@
+/* input.h - The interface to and for an input driver.
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef _INPUT_H_
+#define _INPUT_H_ 1
+
+#include <errno.h>
+#include <stddef.h>
+
+#include <hurd/cons.h>
+
+
+/* The input drivers are set up by the driver's initialization routine
+ and added to the console client with driver_add_input. All
+ subsequent operations on the display are fully synchronized by the
+ caller. The driver deinitialization routine should call
+ driver_remove_input. */
+
+/* Forward declaration. */
+struct input_ops;
+typedef struct input_ops *input_ops_t;
+
+/* Add the input source HANDLE with the operations OPS to the console
+ client. As soon as this is called, operations on this input source
+ may be performed, even before the function returns. */
+error_t driver_add_input (input_ops_t ops, void *handle);
+
+/* Remove the input HANDLE with the operations OPS from the console
+ client. As soon as this function returns, no operations will be
+ performed on the input source anymore. */
+error_t driver_remove_input (input_ops_t ops, void *handle);
+
+/* Enter SIZE bytes from the buffer BUF into the currently active
+ console. This can be called by the input driver at any time. */
+error_t console_input (char *buf, size_t size);
+
+/* Scroll the active console by TYPE and VALUE as specified by
+ cons_vcons_scrollback. */
+int console_scrollback (cons_scroll_t type, float value);
+
+/* Returns current console ID. */
+error_t console_current_id (int *cur);
+
+/* Switch the active console to console ID or DELTA (relative to the
+ active console). */
+error_t console_switch (int id, int delta);
+
+/* Signal an error to the user. */
+void console_error (const wchar_t *const err_msg);
+
+/* Exit the console client. Does not return. */
+void console_exit (void) __attribute__ ((noreturn));
+
+/* Switch away from the console an external use of the console like
+ XFree. */
+void console_switch_away (void);
+
+/* Switch back to the console client from an external user of the
+ console like XFree. */
+void console_switch_back (void);
+
+/* Report the mouse event EV to the currently active console. This
+ can be called by the input driver at any time. */
+error_t console_move_mouse (mouse_event_t ev);
+
+
+#if QUAERENDO_INVENIETIS
+/* Do not use, do not remove. */
+void console_deprecated (int key);
+#endif
+
+
+struct input_ops
+{
+ /* Set the status of the scroll lock indication. */
+ error_t (*set_scroll_lock_status) (void *handle, int onoff);
+
+ /* Do not use, do not remove. */
+ void (*deprecated) (void *handle, int key);
+};
+
+#endif /* _INPUT_H_ */
diff --git a/console-client/kbd-repeat.c b/console-client/kbd-repeat.c
new file mode 100644
index 00000000..d3a39d07
--- /dev/null
+++ b/console-client/kbd-repeat.c
@@ -0,0 +1,262 @@
+/* kbd-repeat.c - Keyboard repeater.
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+ Written by Marco Gerards.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <hurd/netfs.h>
+#include <stdlib.h>
+#include <error.h>
+#include <string.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/mman.h>
+
+#include "kdioctl_S.h"
+#include "mach-inputdev.h"
+#include "input.h"
+
+/* The amount of keyboard events that can be stored in the keyboard buffer. */
+#define KBDEVTBUFSZ 20
+
+/* The size of the keyboard buffer in bytes. */
+#define KBDBUFSZ (KBDEVTBUFSZ * sizeof (kd_event))
+
+/* Return the position of X in the buffer. */
+#define KBDBUF_POS(x) ((x) % KBDBUFSZ)
+
+/* The keyboard buffer. */
+static struct kbdbuf
+{
+ char keybuffer[KBDBUFSZ];
+ int pos;
+ size_t size;
+ pthread_cond_t readcond;
+ pthread_cond_t writecond;
+} kbdbuf;
+
+/* Wakeup for select */
+static pthread_cond_t select_alert;
+
+/* The global lock */
+static pthread_mutex_t global_lock;
+
+/* Amount of times the device was opened. Normally this translator
+ should be only opened once. */
+int kbd_repeater_opened;
+
+
+/* Place the keyboard event KEY in the keyboard buffer. */
+void
+kbd_repeat_key (kd_event *key)
+{
+ kd_event *ev;
+
+ pthread_mutex_lock (&global_lock);
+ while (kbdbuf.size + sizeof (kd_event) > KBDBUFSZ)
+ {
+ /* The input buffer is full, wait until there is some space. If this call
+ is interrupted, silently continue. */
+ (void) pthread_hurd_cond_wait_np (&kbdbuf.writecond, &global_lock);
+ }
+ ev = (kd_event *) &kbdbuf.keybuffer[KBDBUF_POS (kbdbuf.pos
+ + kbdbuf.size)];
+ kbdbuf.size += sizeof (kd_event);
+ memcpy (ev, key, sizeof (kd_event));
+
+ pthread_cond_broadcast (&kbdbuf.readcond);
+ pthread_cond_broadcast (&select_alert);
+ pthread_mutex_unlock (&global_lock);
+}
+
+
+static error_t
+repeater_select (struct protid *cred, mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ struct timespec *tsp, int *type)
+{
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (*type & ~SELECT_READ)
+ return EINVAL;
+
+ if (*type == 0)
+ return 0;
+
+ pthread_mutex_lock (&global_lock);
+ while (1)
+ {
+ if (kbdbuf.size > 0)
+ {
+ *type = SELECT_READ;
+ pthread_mutex_unlock (&global_lock);
+
+ return 0;
+ }
+
+ ports_interrupt_self_on_port_death (cred, reply);
+ err = pthread_hurd_cond_timedwait_np (&select_alert, &global_lock, tsp);
+ if (err)
+ {
+ *type = 0;
+ pthread_mutex_unlock (&global_lock);
+
+ if (err == ETIMEDOUT)
+ err = 0;
+
+ return err;
+ }
+ }
+}
+
+
+static error_t
+repeater_read (struct protid *cred, char **data,
+ mach_msg_type_number_t *datalen, off_t offset,
+ mach_msg_type_number_t amount)
+{
+ /* Deny access if they have bad credentials. */
+ if (! cred)
+ return EOPNOTSUPP;
+ else if (! (cred->po->openstat & O_READ))
+ return EBADF;
+
+ pthread_mutex_lock (&global_lock);
+ while (amount > kbdbuf.size)
+ {
+ if (cred->po->openstat & O_NONBLOCK && amount > kbdbuf.size)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EWOULDBLOCK;
+ }
+
+ if (pthread_hurd_cond_wait_np (&kbdbuf.readcond, &global_lock))
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EINTR;
+ }
+ }
+
+ if (amount > 0)
+ {
+ char *keydata;
+ unsigned int i = 0;
+
+ /* Allocate a buffer when this is required. */
+ if (*datalen < amount)
+ {
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return ENOMEM;
+ }
+ }
+
+ /* Copy the bytes to the user's buffer and remove them from the
+ keyboard buffer. */
+ keydata = *data;
+ while (i != amount)
+ {
+ keydata[i++] = kbdbuf.keybuffer[kbdbuf.pos++];
+ kbdbuf.pos = KBDBUF_POS (kbdbuf.pos);
+ }
+ kbdbuf.size -= amount;
+ pthread_cond_broadcast (&kbdbuf.writecond);
+ }
+
+ *datalen = amount;
+ pthread_mutex_unlock (&global_lock);
+
+ return 0;
+}
+
+
+static void
+repeater_open (void)
+{
+ /* Make sure the console does not access the hardware anymore. */
+ if (! kbd_repeater_opened)
+ console_switch_away ();
+ kbd_repeater_opened++;
+}
+
+
+static void
+repeater_close (void)
+{
+ kbd_repeater_opened--;
+
+ /* Allow the console to access the hardware again. */
+ if (! kbd_repeater_opened)
+ {
+ console_switch_back ();
+ kbdbuf.pos = 0;
+ kbdbuf.size = 0;
+ }
+}
+
+
+/* Set the repeater translator. The node will be named NODENAME and
+ NODE will be filled with information about this node. */
+error_t
+kbd_setrepeater (const char *nodename, consnode_t *cn)
+{
+ extern int kdioctl_server (mach_msg_header_t *inp, mach_msg_header_t *outp);
+ error_t err;
+
+ err = console_create_consnode (nodename, cn);
+ if (err)
+ return err;
+
+ (*cn)->read = repeater_read;
+ (*cn)->write = 0;
+ (*cn)->select = repeater_select;
+ (*cn)->open = repeater_open;
+ (*cn)->close = repeater_close;
+ (*cn)->demuxer = kdioctl_server;
+
+ pthread_mutex_init (&global_lock, NULL);
+
+ pthread_cond_init (&kbdbuf.readcond, NULL);
+ pthread_cond_init (&kbdbuf.writecond, NULL);
+ pthread_cond_init (&select_alert, NULL);
+
+ console_register_consnode (*cn);
+
+ return 0;
+}
+
+
+/* Some RPC calls for controlling the keyboard. These calls are just
+ ignored and just exist to make XFree happy. */
+
+kern_return_t
+S_kdioctl_kdskbdmode (io_t port, int mode)
+{
+ return 0;
+}
+
+
+kern_return_t
+S_kdioctl_kdgkbdmode (io_t port, int *mode)
+{
+ return 0;
+}
diff --git a/console-client/mach-inputdev.h b/console-client/mach-inputdev.h
new file mode 100644
index 00000000..985e1e1d
--- /dev/null
+++ b/console-client/mach-inputdev.h
@@ -0,0 +1,138 @@
+/* mach-inputdev.h - Interfaces for the PC pc-kbd and mouse input drivers.
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+ Written by Marco Gerards.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* This gross stuff is cut & pasted from Mach sources, as Mach doesn't
+ export the interface we are using here. */
+
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#ifndef _INPUTDEV_H_
+#define _INPUTDEV_H_ 1
+
+#include <trans.h>
+
+typedef u_short kev_type; /* kd event type */
+
+/* (used for event records) */
+struct mouse_motion {
+ short mm_deltaX; /* units? */
+ short mm_deltaY;
+};
+typedef u_char Scancode;
+
+typedef struct {
+ kev_type type; /* see below */
+ struct timeval time; /* timestamp */
+ union { /* value associated with event */
+ boolean_t up; /* MOUSE_LEFT .. MOUSE_RIGHT */
+ Scancode sc; /* KEYBD_EVENT */
+ struct mouse_motion mmotion; /* MOUSE_MOTION */
+ } value;
+} kd_event;
+#define m_deltaX mmotion.mm_deltaX
+#define m_deltaY mmotion.mm_deltaY
+
+/*
+ * kd_event ID's.
+ */
+#define MOUSE_LEFT 1 /* mouse left button up/down */
+#define MOUSE_MIDDLE 2
+#define MOUSE_RIGHT 3
+#define MOUSE_MOTION 4 /* mouse motion */
+#define KEYBD_EVENT 5 /* key up/down */
+
+
+#define IOCPARM_MASK 0x1fff /* parameter length, at most 13 bits */
+#define IOC_OUT 0x40000000 /* copy out parameters */
+#define IOC_IN 0x80000000U /* copy in parameters */
+
+#ifndef _IOC
+#define _IOC(inout,group,num,len) \
+ (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num))
+#endif
+#ifndef _IOR
+#define _IOR(g,n,t) _IOC(IOC_OUT, (g), (n), sizeof(t))
+#endif
+#ifndef _IOW
+#define _IOW(g,n,t) _IOC(IOC_IN, (g), (n), sizeof(t))
+#endif
+
+#define KDSKBDMODE _IOW('K', 1, int) /* set keyboard mode */
+#define KB_EVENT 1
+#define KB_ASCII 2
+
+#define KDGKBDTYPE _IOR('K', 2, int) /* get keyboard type */
+#define KB_VANILLAKB 0
+
+#define KDSETLEDS _IOW('K', 5, int) /* set keyboard leds */
+
+/*
+ * Low 3 bits of minor are the com port #.
+ * The high 5 bits of minor are the mouse type
+ */
+#define MOUSE_SYSTEM_MOUSE 0
+#define MICROSOFT_MOUSE 1
+#define IBM_MOUSE 2
+#define NO_MOUSE 3
+#define LOGITECH_TRACKMAN 4
+#define MICROSOFT_MOUSE7 5
+
+#define DEV_COM0 "com0"
+#define DEV_COM1 "com1"
+
+/* End of Mach code. */
+
+
+/* Amount of times the device was opened. Normally this translator
+ should be only opened once. */
+extern int kbd_repeater_opened;
+
+/* Place the keyboard event KEY in the keyboard buffer. */
+void kbd_repeat_key (kd_event *key);
+
+/* Set the repeater translator. The node will be named NODENAME and
+ NODE will be filled with information about this node. */
+error_t kbd_setrepeater (const char *nodename, consnode_t *node);
+
+#endif /* _INPUTDEV_H_ */
diff --git a/console-client/ncursesw.c b/console-client/ncursesw.c
new file mode 100644
index 00000000..881acad7
--- /dev/null
+++ b/console-client/ncursesw.c
@@ -0,0 +1,753 @@
+/* ncursesw.c - The ncursesw console driver.
+ Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <wchar.h>
+
+/* The makefiles make sure that this program is compiled with
+ -I${prefix}/ncursesw. */
+#include <curses.h>
+
+#include <pthread.h>
+#include <hurd/console.h>
+
+#include "driver.h"
+
+
+/* ncurses is not thread-safe. This lock protects all calls into the
+ ncurses library. */
+static pthread_mutex_t ncurses_lock;
+
+/* The current width and height the ncursesw driver is using. */
+static unsigned int current_width;
+static unsigned int current_height;
+
+/* The window on which the console is shown. */
+static WINDOW *conspad;
+
+/* The upper left corner shown in the pad. */
+static unsigned int padx;
+static unsigned int pady;
+
+/* Autoscroll is on or off. Autoscroll makes scrolling dependent on
+ the cursor position. */
+static int autoscroll;
+
+/* Forward declaration. */
+static struct display_ops ncursesw_display_ops;
+static struct input_ops ncursesw_input_ops;
+static struct bell_ops ncursesw_bell_ops;
+
+struct curses_kc_to_cons_kc
+{
+ int curses;
+ char *cons;
+};
+
+static struct curses_kc_to_cons_kc keycodes[] =
+ {
+ { KEY_BREAK, NULL }, /* XXX */
+ { KEY_DOWN, CONS_KEY_DOWN },
+ { KEY_UP, CONS_KEY_UP },
+ { KEY_RIGHT, CONS_KEY_RIGHT },
+ { KEY_LEFT, CONS_KEY_LEFT },
+ { KEY_HOME, CONS_KEY_HOME },
+ { KEY_BACKSPACE, CONS_KEY_BACKSPACE },
+ { KEY_F(1), CONS_KEY_F1 },
+ { KEY_F(2), CONS_KEY_F2 },
+ { KEY_F(3), CONS_KEY_F3 },
+ { KEY_F(4), CONS_KEY_F4 },
+ { KEY_F(5), CONS_KEY_F5 },
+ { KEY_F(6), CONS_KEY_F6 },
+ { KEY_F(7), CONS_KEY_F7 },
+ { KEY_F(8), CONS_KEY_F8 },
+ { KEY_F(9), CONS_KEY_F9 },
+ { KEY_F(10), CONS_KEY_F10 },
+ { KEY_DL, NULL }, /* XXX Delete line. */
+ { KEY_IL, NULL }, /* XXX Insert line. */
+ { KEY_DC, CONS_KEY_DC },
+ { KEY_IC, CONS_KEY_IC },
+ { KEY_EIC, NULL }, /* XXX Exit insert mode. */
+ { KEY_CLEAR, NULL }, /* XXX Clear screen. */
+ { KEY_EOS, NULL }, /* XXX Clear to end of screen. */
+ { KEY_EOL, NULL }, /* XXX Clear to end of line. */
+ { KEY_SF, NULL }, /* XXX Scroll one line forward. */
+ { KEY_SR, NULL }, /* XXX Scroll one line backward. */
+ { KEY_NPAGE, CONS_KEY_NPAGE },
+ { KEY_PPAGE, CONS_KEY_PPAGE },
+ { KEY_STAB, NULL }, /* XXX Set tab. */
+ { KEY_CTAB, NULL }, /* XXX Clear tab. */
+ { KEY_CATAB, NULL }, /* XXX Clear all tabs. */
+ { KEY_ENTER, NULL }, /* XXX Enter or send. */
+ { KEY_SRESET, NULL }, /* XXX Soft (partial) reset. */
+ { KEY_RESET, NULL }, /* XXX Reset or hard reset. */
+ { KEY_PRINT, NULL }, /* XXX Print or copy. */
+ { KEY_LL, NULL }, /* XXX Home down or bottom (lower left). */
+ { KEY_A1, NULL }, /* XXX Upper left of keypad. */
+ { KEY_A3, NULL }, /* XXX Upper right of keypad. */
+ { KEY_B2, NULL }, /* XXX Center of keypad. */
+ { KEY_C1, NULL }, /* XXX Lower left of keypad. */
+ { KEY_C3, NULL }, /* XXX Lower right of keypad. */
+ { KEY_BTAB, CONS_KEY_BTAB },
+ { KEY_BEG, NULL }, /* XXX Beg(inning) key. */
+ { KEY_CANCEL, NULL }, /* XXX Cancel key. */
+ { KEY_CLOSE, NULL }, /* XXX Close key. */
+ { KEY_COMMAND, NULL }, /* XXX Cmd (command) key. */
+ { KEY_COPY, NULL }, /* XXX Copy key. */
+ { KEY_CREATE, NULL }, /* XXX Create key. */
+ { KEY_END, CONS_KEY_END },
+ { KEY_EXIT, NULL }, /* XXX Exit key. */
+ { KEY_FIND, NULL }, /* XXX Find key. */
+ { KEY_HELP, NULL }, /* XXX Help key. */
+ { KEY_MARK, NULL }, /* XXX Mark key. */
+ { KEY_MESSAGE, NULL }, /* XXX Message key. */
+ { KEY_MOUSE, NULL }, /* XXX Mouse event read. */
+ { KEY_MOVE, NULL }, /* XXX Move key. */
+ { KEY_NEXT, NULL }, /* XXX Next object key. */
+ { KEY_OPEN, NULL }, /* XXX Open key. */
+ { KEY_OPTIONS, NULL }, /* XXX Options key. */
+ { KEY_PREVIOUS, NULL }, /* XXX Previous object key. */
+ { KEY_REDO, NULL }, /* XXX Redo key. */
+ { KEY_REFERENCE, NULL }, /* XXX Ref(erence) key. */
+ { KEY_REFRESH, NULL }, /* XXX Refresh key. */
+ { KEY_REPLACE, NULL }, /* XXX Replace key. */
+ { KEY_RESIZE, NULL }, /* XXX Screen resized. */
+ { KEY_RESTART, NULL }, /* XXX Restart key. */
+ { KEY_RESUME, NULL }, /* XXX Resume key. */
+ { KEY_SAVE, NULL }, /* XXX Save key. */
+ { KEY_SBEG, NULL }, /* XXX Shifted beginning key. */
+ { KEY_SCANCEL, NULL }, /* XXX Shifted cancel key. */
+ { KEY_SCOMMAND, NULL }, /* XXX Shifted command key. */
+ { KEY_SCOPY, NULL }, /* XXX Shifted copy key. */
+ { KEY_SCREATE, NULL }, /* XXX Shifted create key. */
+ { KEY_SDC, NULL }, /* XXX Shifted delete char key. */
+ { KEY_SDL, NULL }, /* XXX Shifted delete line key. */
+ { KEY_SELECT, NULL }, /* XXX Select key. */
+ { KEY_SEND, NULL }, /* XXX Shifted end key. */
+ { KEY_SEOL, NULL }, /* XXX Shifted clear line key. */
+ { KEY_SEXIT, NULL }, /* XXX Shifted exit key. */
+ { KEY_SFIND, NULL }, /* XXX Shifted find key. */
+ { KEY_SHELP, NULL }, /* XXX Shifted help key. */
+ { KEY_SHOME, NULL }, /* XXX Shifted home key. */
+ { KEY_SIC, NULL }, /* XXX Shifted input key. */
+ { KEY_SLEFT, NULL }, /* XXX Shifted left arrow key. */
+ { KEY_SMESSAGE, NULL }, /* XXX Shifted message key. */
+ { KEY_SMOVE, NULL }, /* XXX Shifted move key. */
+ { KEY_SNEXT, NULL }, /* XXX Shifted next key. */
+ { KEY_SOPTIONS, NULL }, /* XXX Shifted options key. */
+ { KEY_SPREVIOUS, NULL }, /* XXX Shifted prev key. */
+ { KEY_SPRINT, NULL }, /* XXX Shifted print key. */
+ { KEY_SREDO, NULL }, /* XXX Shifted redo key. */
+ { KEY_SREPLACE, NULL }, /* XXX Shifted replace key. */
+ { KEY_SRIGHT, NULL }, /* XXX Shifted right arrow. */
+ { KEY_SRSUME, NULL }, /* XXX Shifted resume key. */
+ { KEY_SSAVE, NULL }, /* XXX Shifted save key. */
+ { KEY_SSUSPEND, NULL }, /* XXX Shifted suspend key. */
+ { KEY_SUNDO, NULL }, /* XXX Shifted undo key. */
+ { KEY_SUSPEND, NULL }, /* XXX Suspend key. */
+ { KEY_UNDO, NULL } /* XXX Undo key. */
+ };
+
+static int
+ucs4_to_altchar (wchar_t chr, chtype *achr)
+{
+ switch (chr)
+ {
+ case CONS_CHAR_RARROW:
+ *achr = ACS_RARROW;
+ break;
+ case CONS_CHAR_LARROW:
+ *achr = ACS_LARROW;
+ break;
+ case CONS_CHAR_UARROW:
+ *achr = ACS_UARROW;
+ break;
+ case CONS_CHAR_DARROW:
+ *achr = ACS_DARROW;
+ break;
+ case CONS_CHAR_BLOCK:
+ *achr = ACS_BLOCK;
+ break;
+ case CONS_CHAR_LANTERN:
+ *achr = ACS_LANTERN;
+ break;
+ case CONS_CHAR_DIAMOND:
+ *achr = ACS_DIAMOND;
+ break;
+ case CONS_CHAR_CKBOARD:
+ *achr = ACS_CKBOARD;
+ break;
+ case CONS_CHAR_DEGREE:
+ *achr = ACS_DEGREE;
+ break;
+ case CONS_CHAR_PLMINUS:
+ *achr = ACS_PLMINUS;
+ break;
+ case CONS_CHAR_BOARD:
+ *achr = ACS_BOARD;
+ break;
+ case CONS_CHAR_LRCORNER:
+ *achr = ACS_LRCORNER;
+ break;
+ case CONS_CHAR_URCORNER:
+ *achr = ACS_URCORNER;
+ break;
+ case CONS_CHAR_ULCORNER:
+ *achr = ACS_ULCORNER;
+ break;
+ case CONS_CHAR_LLCORNER:
+ *achr = ACS_LLCORNER;
+ break;
+ case CONS_CHAR_PLUS:
+ *achr = ACS_PLUS;
+ break;
+ case CONS_CHAR_S1:
+ *achr = ACS_S1;
+ break;
+ case CONS_CHAR_S3:
+ *achr = ACS_S3;
+ break;
+ case CONS_CHAR_HLINE:
+ *achr = ACS_HLINE;
+ break;
+ case CONS_CHAR_S7:
+ *achr = ACS_S7;
+ break;
+ case CONS_CHAR_S9:
+ *achr = ACS_S9;
+ break;
+ case CONS_CHAR_LTEE:
+ *achr = ACS_LTEE;
+ break;
+ case CONS_CHAR_RTEE:
+ *achr = ACS_RTEE;
+ break;
+ case CONS_CHAR_BTEE:
+ *achr = ACS_BTEE;
+ break;
+ case CONS_CHAR_TTEE:
+ *achr = ACS_TTEE;
+ break;
+ case CONS_CHAR_VLINE:
+ *achr = ACS_VLINE;
+ break;
+ case CONS_CHAR_LEQUAL:
+ *achr = ACS_LEQUAL;
+ break;
+ case CONS_CHAR_GEQUAL:
+ *achr = ACS_GEQUAL;
+ break;
+ case CONS_CHAR_PI:
+ *achr = ACS_PI;
+ break;
+ case CONS_CHAR_NEQUAL:
+ *achr = ACS_NEQUAL;
+ break;
+ case CONS_CHAR_STERLING:
+ *achr = ACS_STERLING;
+ break;
+ case CONS_CHAR_BULLET:
+ *achr = ACS_BULLET;
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+static error_t
+refresh_screen (void)
+{
+ /* It is possible */
+ if (!current_width && !current_height)
+ return 0;
+ return prefresh (conspad, pady, padx, 0, 0,
+ (current_height <= (unsigned int) LINES
+ ? current_height : (unsigned int) LINES) - 1,
+ (current_width <= (unsigned int) COLS
+ ? current_width : (unsigned int) COLS) - 1);
+}
+
+static void *
+input_loop (void *unused)
+{
+ int fd = 0;
+ fd_set rfds;
+ int w_escaped = 0;
+
+ FD_ZERO (&rfds);
+ FD_SET (fd, &rfds);
+
+ while (1)
+ {
+ int ret;
+
+ FD_SET (fd, &rfds);
+
+ ret = select (fd + 1, &rfds, 0, 0, 0);
+ if (ret == 1)
+ {
+ char buffer[100];
+ char *buf = buffer;
+ size_t size = 0;
+
+ pthread_mutex_lock (&ncurses_lock);
+ while ((ret = wgetch (conspad)) != ERR)
+ {
+ unsigned int i;
+ int found;
+
+ if (w_escaped)
+ {
+ switch (ret)
+ {
+ case 'x':
+ pthread_mutex_unlock (&ncurses_lock);
+ console_exit ();
+ break;
+ case 23: /* ^W */
+ assert (size < 100);
+ buf[size++] = ret;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ /* Avoid a dead lock. */
+ pthread_mutex_unlock (&ncurses_lock);
+ console_switch (1 + (ret - '1'), 0);
+ pthread_mutex_lock (&ncurses_lock);
+ break;
+ case 'j':
+ /* Scroll pad to left. */
+ if (padx > 0)
+ {
+ padx--;
+ refresh_screen ();
+ }
+ break;
+ case 'k':
+ /* Scroll pad down. */
+ if (pady < current_height - LINES)
+ {
+ pady++;
+ refresh_screen ();
+ }
+ break;
+ case 'l':
+ /* Scroll pad to right. */
+ if (padx < current_width - COLS)
+ {
+ padx++;
+ refresh_screen ();
+ }
+ break;
+ case 'i':
+ /* Scroll pad up. */
+ if (pady > 0)
+ {
+ pady--;
+ refresh_screen ();
+ }
+ break;
+ case 'a':
+ /* Switch autoscroll on/off. */
+ autoscroll = !autoscroll;
+ break;
+ default:
+ break;
+ }
+ w_escaped = 0;
+ }
+ else
+ switch (ret)
+ {
+ case 23: /* ^W */
+ w_escaped = 1;
+ break;
+ default:
+ found = 0;
+ for (i = 0; i < sizeof (keycodes) / sizeof (keycodes[0]);
+ i++)
+ {
+ if (keycodes[i].curses == ret)
+ {
+ if (keycodes[i].cons)
+ {
+ assert (size
+ < 101 - strlen (keycodes[i].cons));
+ strcpy (&buf[size], keycodes[i].cons);
+ size += strlen (keycodes[i].cons);
+ }
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ {
+ assert (size < 100);
+ buf[size++] = ret;
+ }
+ break;
+ }
+ }
+ pthread_mutex_unlock (&ncurses_lock);
+ if (size)
+ console_input (buf, size);
+ }
+ }
+}
+
+static inline attr_t
+conchar_attr_to_attr (conchar_attr_t attr)
+{
+ return ((attr.intensity == CONS_ATTR_INTENSITY_BOLD
+ ? A_BOLD : (attr.intensity == CONS_ATTR_INTENSITY_DIM
+ ? A_DIM : A_NORMAL))
+ | (attr.underlined ? A_UNDERLINE : 0)
+ | (attr.reversed ? A_REVERSE : 0)
+ | (attr.blinking ? A_BLINK: 0)
+ | (attr.concealed ? A_INVIS : 0));
+}
+
+static inline short
+conchar_attr_to_color_pair (conchar_attr_t attr)
+{
+ return COLOR_PAIR (attr.bgcol << 3 | attr.fgcol);
+}
+
+static void
+mvwputsn (conchar_t *str, size_t len, off_t x, off_t y)
+{
+ cchar_t chr;
+ wchar_t wch[2] = { L'\0', L'\0' };
+ uint32_t last_attr = * (uint32_t *) &str->attr;
+ attr_t attr = conchar_attr_to_attr (str->attr);
+ short color_pair = conchar_attr_to_color_pair (str->attr);
+
+ wmove (conspad, y, x);
+ while (len)
+ {
+ int ret;
+ chtype ac;
+
+ if (last_attr != *(uint32_t *) &str->attr)
+ {
+ last_attr = * (uint32_t *) &str->attr;
+ attr = conchar_attr_to_attr (str->attr);
+ color_pair = conchar_attr_to_color_pair (str->attr);
+ }
+
+ if (ucs4_to_altchar (str->chr, &ac))
+ waddch (conspad, ac | attr | color_pair);
+ else
+ {
+ wch[0] = str->chr;
+ ret = setcchar (&chr, wch, attr, color_pair, NULL);
+#if 0
+ if (ret == ERR)
+ {
+ printf ("setcchar failed: %s\n", strerror (errno));
+ printf ("[%lc]\n", wch[0]);
+ assert (!"Do something if setcchar fails.");
+ }
+#endif
+ ret = wadd_wch (conspad, &chr);
+#if 0
+ if (ret == ERR)
+ {
+ printf ("add_wch failed: %i, %s\n", ret, strerror (errno));
+ printf ("[%lc]\n", wch[0]);
+ assert (!"Do something if add_wchr fails.");
+ }
+#endif
+ }
+ len--;
+ str++;
+ }
+}
+
+
+static error_t
+ncursesw_update (void *handle)
+{
+ pthread_mutex_lock (&ncurses_lock);
+ refresh_screen ();
+ pthread_mutex_unlock (&ncurses_lock);
+ return 0;
+}
+
+
+static error_t
+ncursesw_set_cursor_pos (void *handle, uint32_t col, uint32_t row)
+{
+ pthread_mutex_lock (&ncurses_lock);
+ assert (current_width && current_height);
+ if (autoscroll)
+ {
+ /* Autoscroll to the right. */
+ if (col > COLS + padx)
+ {
+ padx += COLS / 2;
+ if (padx > COLS + current_width)
+ padx = current_width - COLS;
+ refresh_screen ();
+ }
+ /* Autoscroll to the left. */
+ else if (col < padx)
+ {
+ padx -= COLS / 2;
+ if (padx < 0)
+ padx = 0;
+ refresh_screen ();
+ }
+ /* Autoscroll down. */
+ if (row > LINES + pady)
+ {
+ pady += LINES / 2;
+ if (pady > LINES + current_height)
+ pady = current_height - LINES;
+ refresh_screen ();
+ }
+ /* Autoscroll up. */
+ else if (row < pady)
+ {
+ pady -= LINES / 2;
+ if (pady < 0)
+ pady = 0;
+ refresh_screen ();
+ }
+ }
+
+ wmove (conspad, row, col);
+
+ pthread_mutex_unlock (&ncurses_lock);
+ return 0;
+}
+
+
+static error_t
+ncursesw_set_cursor_status (void *handle, uint32_t status)
+{
+ pthread_mutex_lock (&ncurses_lock);
+
+ /* If the cursor is invisible and switching to one visible state is
+ impossible, switch to the other visible state or else the cursor
+ will not be shown at all. */
+ if (curs_set (status) == -1 && status)
+ curs_set (status == 1 ? 2 : 1);
+
+ pthread_mutex_unlock (&ncurses_lock);
+ return 0;
+}
+
+
+static error_t
+ncursesw_scroll (void *handle, int delta)
+{
+ /* XXX We don't support scrollback for now. */
+ assert (delta >= 0);
+
+ pthread_mutex_lock (&ncurses_lock);
+ idlok (conspad, TRUE);
+ scrollok (conspad, TRUE);
+ wscrl (conspad, delta);
+ idlok (conspad, FALSE);
+ scrollok (conspad, FALSE);
+ pthread_mutex_unlock (&ncurses_lock);
+ return 0;
+}
+
+
+static error_t
+ncursesw_write (void *handle, conchar_t *str, size_t length,
+ uint32_t col, uint32_t row)
+{
+ int x;
+ int y;
+
+ pthread_mutex_lock (&ncurses_lock);
+ getyx (conspad, y, x);
+ mvwputsn (str, length, col, row);
+ wmove (conspad, y, x);
+ pthread_mutex_unlock (&ncurses_lock);
+ return 0;
+}
+
+
+static error_t
+ncursesw_flash (void *handle)
+{
+ pthread_mutex_lock (&ncurses_lock);
+ flash ();
+ pthread_mutex_unlock (&ncurses_lock);
+ return 0;
+}
+
+
+/* Bell driver operations. */
+error_t
+ncursesw_beep (void *handle)
+{
+ pthread_mutex_lock (&ncurses_lock);
+ beep ();
+ pthread_mutex_unlock (&ncurses_lock);
+ return 0;
+}
+
+
+
+static error_t
+ncursesw_driver_init (void **handle, int no_exit,
+ int argc, char *argv[], int *next)
+{
+ pthread_mutex_init (&ncurses_lock, NULL);
+ return 0;
+}
+
+static error_t
+ncursesw_driver_start (void *handle)
+{
+ pthread_t thread;
+ error_t err;
+ int i;
+
+ initscr ();
+ start_color ();
+ for (i = 0; i < 64; i++)
+ init_pair (i, i & 7, i >> 3);
+ raw ();
+ noecho ();
+ nonl ();
+
+ /* Create a new pad with the size minimal size. This pad will be
+ resized by ncursesw_set_dimension. */
+ conspad = newpad (1, 1);
+ if (!conspad)
+ return errno;
+
+ intrflush (conspad, FALSE);
+ nodelay (conspad, TRUE);
+ wtimeout (conspad, 1);
+ keypad (conspad, TRUE);
+
+ err = driver_add_display (&ncursesw_display_ops, NULL);
+ if (err)
+ {
+ endwin ();
+ return err;
+ }
+ err = driver_add_input (&ncursesw_input_ops, NULL);
+ if (err)
+ {
+ err = driver_remove_display (&ncursesw_display_ops, NULL);
+ endwin ();
+ return err;
+ }
+ err = driver_add_bell (&ncursesw_bell_ops, NULL);
+ if (err)
+ {
+ err = driver_remove_input (&ncursesw_input_ops, NULL);
+ err = driver_remove_display (&ncursesw_display_ops, NULL);
+ endwin ();
+ return err;
+ }
+
+ err = pthread_create (&thread, NULL, input_loop, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+
+ return 0;
+}
+
+/* Destroy the display HANDLE. */
+static error_t
+ncursesw_driver_fini (void *handle, int force)
+{
+ /* XXX Cancel the input thread. */
+ pthread_mutex_lock (&ncurses_lock);
+ driver_remove_display (&ncursesw_display_ops, NULL);
+ driver_remove_input (&ncursesw_input_ops, NULL);
+ driver_remove_bell (&ncursesw_bell_ops, NULL);
+ pthread_mutex_unlock (&ncurses_lock);
+
+ endwin ();
+ return 0;
+}
+
+static error_t
+ncursesw_set_dimension (void *handle, unsigned int width, unsigned int height)
+{
+ pthread_mutex_lock (&ncurses_lock);
+ if (width != current_width || height != current_height)
+ {
+ wresize (conspad, height, width);
+ padx = 0;
+ pady = 0;
+ }
+ current_width = width;
+ current_height = height;
+ pthread_mutex_unlock(&ncurses_lock);
+ return 0;
+}
+
+
+struct driver_ops driver_ncursesw_ops =
+ {
+ ncursesw_driver_init,
+ ncursesw_driver_start,
+ ncursesw_driver_fini,
+ };
+
+static struct display_ops ncursesw_display_ops =
+ {
+ ncursesw_set_cursor_pos,
+ ncursesw_set_cursor_status,
+ ncursesw_scroll,
+ NULL,
+ ncursesw_write,
+ ncursesw_update,
+ ncursesw_flash,
+ NULL,
+ ncursesw_set_dimension
+ };
+
+static struct input_ops ncursesw_input_ops =
+ {
+ NULL,
+ NULL
+ };
+
+static struct bell_ops ncursesw_bell_ops =
+ {
+ ncursesw_beep,
+ NULL
+ };
diff --git a/console-client/pc-kbd.c b/console-client/pc-kbd.c
new file mode 100644
index 00000000..21c09876
--- /dev/null
+++ b/console-client/pc-kbd.c
@@ -0,0 +1,1472 @@
+/* pc-kbd.c - The PC Keyboard input driver.
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <iconv.h>
+#include <sys/mman.h>
+#include <argp.h>
+
+#include <device/device.h>
+#include <pthread.h>
+
+#include <hurd/console.h>
+#include <hurd/cons.h>
+
+#include "driver.h"
+#include "mach-inputdev.h"
+
+#ifdef XKB_SUPPORT
+#include "xkb/xkb.h"
+#endif
+
+
+/* The default name of the node of the repeater. */
+#define DEFAULT_REPEATER_NODE "kbd"
+
+/* The keyboard device in the kernel. */
+static device_t kbd_dev;
+
+/* The converter.
+ XXX: Here it is used by the fixed US layout code. It's not static because
+ xkb.c also needs a converter. This variable and it's initialization should
+ be moved there once XKB is supported in OSKit. */
+iconv_t cd;
+
+/* The status of various LEDs. */
+struct {
+ int scroll_lock : 1;
+ int num_lock : 1;
+ int caps_lock : 1;
+} led_state;
+
+/* True if we are in the GNU Mach v1 compatibility mode. */
+int gnumach_v1_compat;
+
+/* Forward declaration. */
+static struct input_ops pc_kbd_ops;
+
+/* The name of the repeater node. */
+static char *repeater_node;
+
+/* The repeater node. */
+static consnode_t cnode;
+
+#ifdef XKB_SUPPORT
+/* CTRL + Alt + Backspace will terminate the console client by
+ default, this hardcoded behaviour can be disabled. */
+static int ctrlaltbs;
+
+/* The number of jiffies until repetition starts. */
+static int xkb_repeat_delay;
+
+/* The number of jiffies until the next repetition is generated. */
+static int xkb_repeat_interval;
+#endif
+
+/* A list of scan codes generated by the keyboard, in the set 2 encoding. */
+enum scancode
+ {
+ SC_F9 = 0x01,
+ SC_F5 = 0x03,
+ SC_F3 = 0x04,
+ SC_F1 = 0x05,
+ SC_F2 = 0x06,
+ SC_F12 = 0x07,
+ SC_F10 = 0x09,
+ SC_F8 = 0x0A,
+ SC_F6 = 0x0B,
+ SC_F4 = 0x0C,
+ SC_TAB = 0x0D,
+ SC_BACKQUOTE = 0x0E, /* ` */
+ SC_LEFT_ALT = 0x11,
+ SC_LEFT_SHIFT = 0x12,
+ SC_LEFT_CTRL = 0x14,
+ SC_Q = 0x15,
+ SC_1 = 0x16,
+ SC_Z = 0x1A,
+ SC_S = 0x1B,
+ SC_A = 0x1C,
+ SC_W = 0x1D,
+ SC_2 = 0x1E,
+ SC_C = 0x21,
+ SC_X = 0x22,
+ SC_D = 0x23,
+ SC_E = 0x24,
+ SC_4 = 0x25,
+ SC_3 = 0x26,
+ SC_SPACE = 0x29,
+ SC_V = 0x2A,
+ SC_F = 0x2B,
+ SC_T = 0x2C,
+ SC_R = 0x2D,
+ SC_5 = 0x2E,
+ SC_N = 0x31,
+ SC_B = 0x32,
+ SC_H = 0x33,
+ SC_G = 0x34,
+ SC_Y = 0x35,
+ SC_6 = 0x36,
+ SC_M = 0x3A,
+ SC_J = 0x3B,
+ SC_U = 0x3C,
+ SC_7 = 0x3D,
+ SC_8 = 0x3E,
+ SC_COMMA = 0x41, /* , */
+ SC_K = 0x42,
+ SC_I = 0x43,
+ SC_O = 0x44,
+ SC_0 = 0x45,
+ SC_9 = 0x46,
+ SC_PERIOD = 0x49, /* . */
+ SC_SLASH = 0x4A, /* / */
+ SC_L = 0x4B,
+ SC_SEMICOLON = 0x4C, /* ; */
+ SC_P = 0x4D,
+ SC_MINUS = 0x4E, /* - */
+ SC_APOSTROPHE = 0x52, /* ' */
+ SC_LEFT_BRACKET = 0x54, /* [ */
+ SC_EQUAL = 0x55, /* = */
+ SC_CAPSLOCK = 0x58,
+ SC_RIGHT_SHIFT = 0x59,
+ SC_ENTER = 0x5A,
+ SC_RIGHT_BRACKET = 0x5B, /* ] */
+ SC_BACKSLASH = 0x5D, /* \ */
+ SC_BACKSPACE = 0x66,
+ SC_PAD_1 = 0x69,
+ SC_PAD_4 = 0x6B,
+ SC_PAD_7 = 0x6C,
+ SC_PAD_0 = 0x70,
+ SC_PAD_DECIMAL = 0x71,
+ SC_PAD_2 = 0x72,
+ SC_PAD_5 = 0x73,
+ SC_PAD_6 = 0x74,
+ SC_PAD_8 = 0x75,
+ SC_ESC = 0x76,
+ SC_NUMLOCK = 0x77,
+ SC_F11 = 0x78,
+ SC_PAD_PLUS = 0x79,
+ SC_PAD_3 = 0x7A,
+ SC_PAD_MINUS = 0x7B,
+ SC_PAD_ASTERISK = 0x7C,
+ SC_PAD_9 = 0x7D,
+ SC_SCROLLLOCK = 0x7E,
+ SC_F7 = 0x83,
+ SC_EXTENDED1 = 0xE0, /* One code follows. */
+ SC_EXTENDED2 = 0xE1, /* Two codes follow (only used for Pause). */
+ SC_ERROR = 0xFF, /* Too many keys held down. */
+ SC_FLAG_UP = 0xF000 /* ORed to basic scancode. */
+ };
+
+/* In set 2 function keys don't have a logical order. This macro can
+ determine if a function key was pressed. */
+#define IS_FUNC_KEY(c) ((sc >= SC_F9 && sc <= SC_F4) || \
+ sc == SC_F7 || sc == SC_F11)
+
+/* Codes which can follow SC_EXTENDED1. */
+enum scancode_x1
+ {
+ SC_X1_RIGHT_ALT = 0x11,
+ SC_X1_PRTSC = 0x12,
+/* SC_X1_PRTSC = 0x7C, */
+ SC_X1_RIGHT_CTRL = 0x14,
+ SC_X1_LEFT_GUI = 0x1F,
+ SC_X1_RIGHT_GUI = 0x27,
+ SC_X1_APPS = 0x2F,
+ SC_X1_POWER = 0x37,
+ SC_X1_SLEEP = 0x3F,
+ SC_X1_PAD_SLASH = 0x4A,
+ SC_X1_PAD_ENTER = 0x5A,
+ SC_X1_WAKEUP = 0x5E,
+ SC_X1_END = 0x69,
+ SC_X1_LEFT = 0x6B,
+ SC_X1_HOME = 0x6C,
+ SC_X1_INS = 0x70,
+ SC_X1_DEL = 0x71,
+ SC_X1_DOWN = 0x72,
+ SC_X1_RIGHT = 0x74,
+ SC_X1_UP = 0x75,
+ SC_X1_PGDN = 0x7A,
+ SC_X1_PGUP = 0x7D
+ };
+
+/* Codes which can follow SC_EXTENDED2. */
+enum scancode_x2
+ {
+ SC_X2_BREAK = 0x1477,
+ };
+
+
+/* Scancode to Unicode mapping. The empty string stands for the NULL
+ character. */
+char *sc_to_kc[][7] =
+ {
+ /*None, Shift, Ctrl, LAlt, S+LAlt, C+LAlt, RAlt */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_F9, 0, 0, 0, 0, 0, 0 }, /* SC_F9. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_F5, CONS_KEY_F17, 0, 0, 0, 0, 0 }, /* SC_F5. */
+ { CONS_KEY_F3, CONS_KEY_F15, 0, 0, 0, 0, 0 }, /* SC_F3. */
+ { CONS_KEY_F1, CONS_KEY_F13, 0, 0, 0, 0, 0 }, /* SC_F1. */
+ { CONS_KEY_F2, CONS_KEY_F14, 0, 0, 0, 0, 0 }, /* SC_F2. */
+ { CONS_KEY_F12, 0, 0, 0, 0, 0, 0 }, /* SC_F12. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_F10, 0, 0, 0, 0, 0, 0 }, /* SC_F10. */
+ { CONS_KEY_F8, CONS_KEY_F20, 0, 0, 0, 0, 0 }, /* SC_F8. */
+ { CONS_KEY_F6, CONS_KEY_F18, 0, 0, 0, 0, 0 }, /* SC_F6. */
+ { CONS_KEY_F4, CONS_KEY_F16, 0, 0, 0, 0, 0 }, /* SC_F4. */
+ { "\t", "\t", "\t", "\e\t", "\e\t", "\e\t", "\t" }, /* SC_TAB. */
+ { "`", "~", 0, "\e`", "\e~", 0, 0 }, /* SC_BACKQUOTE. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_LEFT_ALT. XXX */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_LEFT_SHIFT. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_LEFT_CTRL. XXX */
+ { "q", "Q", "\x11", "\eq", "\eQ", "\e\x11", "q" }, /* SC_Q. */
+ { "1", "!", 0, "\e1", "\e!", 0, "1" }, /* SC_1. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { "z", "Z", "\x1a", "\ez", "\eZ", "\e\x1a", "z" }, /* SC_Z. */
+ { "s", "S", "\x13", "\es", "\eS", "\e\x13", "s" }, /* SC_S. */
+ { "a", "A", "\x01", "\ea", "\eA", "\e\x01", "a" }, /* SC_A. */
+ { "w", "W", "\x17", "\ew", "\eW", "\e\x17", "w" }, /* SC_W. */
+ { "2", "@", "", "\e2", "\e@", 0, "2" }, /* SC_2. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { "c", "C", "\x03", "\ec", "\eC", "\e\x03", "\xc2\xa2" }, /* SC_C. */
+ { "x", "X", "\x18", "\ex", "\eX", "\e\x18", "x" }, /* SC_X. */
+ { "d", "D", "\x04", "\ed", "\eD", "\e\x04", "d" }, /* SC_D. */
+ { "e", "E", "\x05", "\ee", "\eE", "\e\x05","\xe2\x82\xac" }, /* SC_E. */
+ { "4", "$", "\x1c", "\e4", "\e$", "\e\x1c", "4" }, /* SC_4. */
+ { "3", "#", "\e", "\e3", "\e#", 0, "3" }, /* SC_3. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { " ", " ", "", "\e ", "\e ", /*XXX*/0, " " }, /* SC_SPACE. */
+ { "v", "V", "\x16", "\ev", "\eV", "\e\x16", "v" }, /* SC_V. */
+ { "f", "F", "\x06", "\ef", "\eF", "\e\x06", "f" }, /* SC_F. */
+ { "t", "T", "\x14", "\et", "\eT", "\e\x14", "t" }, /* SC_T. */
+ { "r", "R", "\x12", "\er", "\eR", "\e\x12", "r" }, /* SC_R. */
+ { "5", "%", "\x1d", "\e5", "\e%", 0, "5" }, /* SC_5. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { "n", "N", "\x0e", "\en", "\eN", "\e\x0e", "n" }, /* SC_N. */
+ { "b", "B", "\x02", "\eb", "\eB", "\e\x02", "b" }, /* SC_B. */
+ { "h", "H", "\x08", "\eh", "\eH", "\e\x08", "h" }, /* SC_H. */
+ { "g", "G", "\x07", "\eg", "\eG", "\e\x07", "g" }, /* SC_G. */
+ { "y", "Y", "\x19", "\ey", "\eY", "\e\x19", "y" }, /* SC_Y. */
+ { "6", "^", "\x1e", "\e6", "\e^", 0, "6" }, /* SC_6. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { "m", "M", "\x0d", "\em", "\eM", "\e\x0d", "m" }, /* SC_M. */
+ { "j", "J", "\x0a", "\ej", "\eJ", "\e\x0a", "j" }, /* SC_J. */
+ { "u", "U", "\x15", "\eu", "\eU", "\e\x15", "u" }, /* SC_U. */
+ { "7", "&", "\x1f", "\e7", "\e&", "\e\x1f", "7" }, /* SC_7. */
+ { "8", "*", "\x7f", "\e8", "\e*", 0, "8" }, /* SC_8. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { ",", "<", 0, "\e,", "\e<", 0, 0 }, /* SC_COMMA. */
+ { "k", "K", "\x0b", "\ek", "\eK", "\e\x0b", "k" }, /* SC_K. */
+ { "i", "I", "\x09", "\ei", "\eI", "\e\x09", "i" }, /* SC_I. */
+ { "o", "O", "\x0f", "\eo", "\eO", "\e\x0f", "o" }, /* SC_O. */
+ { "0", ")", 0, "\e0", "\e)", 0, "0" }, /* SC_0. */
+ { "9", "(", 0, "\e9", "\e(", 0, "9" }, /* SC_9. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { ".", ">", 0, "\e.", "\e>", 0, 0 }, /* SC_PERIOD. */
+ { "/", "?", "\x7f", "\e/", "\e?", 0, 0 }, /* SC_SLASH. */
+ { "l", "L", "\x0c", "\el", "\eL", "\e\x0c", "l" }, /* SC_L. */
+ { ";", ":", 0, "\e;", "\e:", 0, 0 }, /* SC_SEMICOLON. */
+ { "p", "P", "\x10", "\ep", "\eP", "\e\x10", "p" }, /* SC_P. */
+ { "-", "_", "\x1f", "\e-", "\e_", "\e\x1f", "-" }, /* SC_MINUS. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { "'", "\"", "\x07", "\e'", "\e\"", 0, 0 }, /* SC_APOSTROPHE. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { "[", "{", "\e", "\e[", "\e{", 0, 0 }, /* SC_LEFT_BRACKET. */
+ { "=", "+", 0, "\e=", "\e+", 0, "=" }, /* SC_EQUAL. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_CAPSLOCK. */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_RIGHT_SHIFT. */
+ {"\x0d","\x0d", "\x0d","\e\x0d","\e\x0d","\e\x0d","\x0d" }, /* SC_ENTER. */
+ { "]", "}", "\x1d", "\e]", "\e}", "\e\x1d", "~" }, /* SC_RIGHT_BRACKET. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { "\\", "|", "\x1c", "\e\\", "\e|", 0, 0 }, /* SC_BACKSLASH. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_BACKSPACE, CONS_KEY_BACKSPACE, CONS_KEY_BACKSPACE,
+ "\e" CONS_KEY_BACKSPACE, "\e" CONS_KEY_BACKSPACE,
+ "\e" CONS_KEY_BACKSPACE, CONS_KEY_BACKSPACE }, /* SC_BACKSPACE. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_END, CONS_KEY_END, CONS_KEY_END, 0, 0, 0, 0 }, /* SC_PAD_1. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT, 0, 0, 0, 0 }, /* SC_PAD_4. */
+ { CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME, 0, 0, 0, 0 }, /* SC_PAD_7. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_IC, CONS_KEY_IC, CONS_KEY_IC, 0, 0, 0, 0 }, /* SC_PAD_0. */
+ { CONS_KEY_DC, CONS_KEY_DC, CONS_KEY_DC, 0, 0, 0, 0 }, /* SC_PAD_DECIMAL. */
+ { CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN, 0, 0, 0, 0 }, /* SC_PAD_2. */
+ {/* XXX */ "\e[G", "\e[G", "\e[G", 0, 0, 0, 0 }, /* SC_PAD_5. */
+ { CONS_KEY_RIGHT, CONS_KEY_RIGHT, CONS_KEY_RIGHT,0, 0, 0, 0 }, /* SC_PAD_6. */
+ { CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP, 0, 0, 0, 0 }, /* SC_PAD_8. */
+ { "\e", "\e", "\e", "\e\e", "\e\e", "\e\e", "\e" }, /* SC_ESC. */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_NUMLOCK. */
+ { CONS_KEY_F11, 0, 0, 0, 0, 0, 0 }, /* SC_F11. */
+ { "+", "+", "+", "+", "+", "+", "+" }, /* SC_PAD_PLUS. */
+ { CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE,0, 0, 0, 0 }, /* SC_PAD_3. */
+ { "-", "-", "-", "-", "-", "-", "-" }, /* SC_PAD_MINUS. */
+ { "*", "*", "*", "*", "*", "*", "*" }, /* SC_PAD_ASTERISK. XXX */
+ { CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE,0, 0, 0, 0 }, /* SC_PAD_9. */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_SCROLLLOCK. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_F7, CONS_KEY_F19, 0, 0, 0, 0, 0 } /* SC_F7. */
+ };
+
+char *sc_x1_to_kc[][7] =
+ {
+ /* None, Shift, Ctrl, LAlt, S+LAlt, C+LAlt, RAlt */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_RIGHT_ALT. */
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_PRTSC. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_RIGHT_CTRL. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_LEFT_GUI. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_RIGHT_GUI. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_APPS. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_POWER. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_SLEEP. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { "/", "/", "/", "/", "/", "/", 0 }, /* SC_X1_PAD_SLASH. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { "\n", "\n", "\n", "\n", "\n", "\n", 0 }, /* SC_X1_PAD_ENTER. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_WAKEUP. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_END, CONS_KEY_END, CONS_KEY_END, CONS_KEY_END,
+ CONS_KEY_END, CONS_KEY_END, CONS_KEY_END }, /* SC_X1_END. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT,
+ CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT }, /* SC_X1_LEFT. */
+ { CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME,
+ CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME }, /* SC_X1_HOME. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_IC, CONS_KEY_IC, CONS_KEY_IC, CONS_KEY_IC,
+ CONS_KEY_IC, CONS_KEY_IC, CONS_KEY_IC }, /* SC_X1_INS. */
+ { CONS_KEY_DC, CONS_KEY_DC, CONS_KEY_DC, CONS_KEY_DC,
+ CONS_KEY_DC, CONS_KEY_DC, CONS_KEY_DC }, /* SC_X1_DEL. */
+ { CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN,
+ CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN }, /* SC_X1_DOWN. */
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_RIGHT, CONS_KEY_RIGHT, CONS_KEY_RIGHT, CONS_KEY_RIGHT,
+ CONS_KEY_RIGHT, CONS_KEY_RIGHT, CONS_KEY_RIGHT }, /* SC_X1_RIGHT. */
+ { CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP,
+ CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP }, /* SC_X1_UP. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE,
+ CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE }, /* SC_X1_PGDN. */
+ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 },
+ { CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE,
+ CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE } /* SC_X1_PGUP. */
+ };
+
+char *sc_x2_to_kc[][7] =
+ {
+ /* We don't add all those zero entries here. It's just one key,
+ so it's special cased. */
+ { "\e[P", "\e[P", "\e[P", "\e[P", "\e[P", "\e[P","\e[P" }, /* SC_X1_BREAK. */
+ };
+
+
+/* GNU Mach v1 compatibility code. */
+
+/* This is a conversion table from i8042 scancode set 1 to set 2. */
+enum scancode sc_set1_to_set2[] =
+ {
+ 0x00,
+ SC_ESC,
+ SC_1,
+ SC_2,
+ SC_3,
+ SC_4,
+ SC_5,
+ SC_6,
+ SC_7,
+ SC_8,
+ SC_9,
+ SC_0,
+ SC_MINUS,
+ SC_EQUAL,
+ SC_BACKSPACE,
+ SC_TAB,
+ SC_Q,
+ SC_W,
+ SC_E,
+ SC_R,
+ SC_T,
+ SC_Y,
+ SC_U,
+ SC_I,
+ SC_O,
+ SC_P,
+ SC_LEFT_BRACKET,
+ SC_RIGHT_BRACKET,
+ SC_ENTER,
+ SC_LEFT_CTRL,
+ SC_A,
+ SC_S,
+ SC_D,
+ SC_F,
+ SC_G,
+ SC_H,
+ SC_J,
+ SC_K,
+ SC_L,
+ SC_SEMICOLON,
+ SC_APOSTROPHE,
+ SC_BACKQUOTE,
+ SC_LEFT_SHIFT,
+ SC_BACKSLASH,
+ SC_Z,
+ SC_X,
+ SC_C,
+ SC_V,
+ SC_B,
+ SC_N,
+ SC_M,
+ SC_COMMA,
+ SC_PERIOD,
+ SC_SLASH,
+ SC_RIGHT_SHIFT,
+ SC_PAD_ASTERISK,
+ SC_LEFT_ALT,
+ SC_SPACE,
+ SC_CAPSLOCK,
+ SC_F1,
+ SC_F2,
+ SC_F3,
+ SC_F4,
+ SC_F5,
+ SC_F6,
+ SC_F7,
+ SC_F8,
+ SC_F9,
+ SC_F10,
+ SC_NUMLOCK,
+ SC_SCROLLLOCK,
+ SC_PAD_7,
+ SC_PAD_8,
+ SC_PAD_9,
+ SC_PAD_MINUS,
+ SC_PAD_4,
+ SC_PAD_5,
+ SC_PAD_6,
+ SC_PAD_PLUS,
+ SC_PAD_1,
+ SC_PAD_2,
+ SC_PAD_3,
+ SC_PAD_0,
+ SC_PAD_DECIMAL,
+ 0x00, /* XXX SYSREQ */
+ 0x00,
+ 0x00,
+ SC_F11,
+ SC_F12,
+ };
+
+/* Conversion table for codes which can follow SC_EXTENDED1. */
+enum scancode sc_set1_to_set2_x1[] =
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ SC_X1_PAD_ENTER,
+ SC_X1_RIGHT_CTRL,
+ 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ SC_X1_PAD_SLASH,
+ 0x00,
+ SC_X1_PRTSC,
+ SC_X1_RIGHT_ALT,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, /* XXX SC_X1_BREAK */
+ SC_X1_HOME,
+ SC_X1_UP,
+ SC_X1_PGUP,
+ 0x00,
+ SC_X1_LEFT,
+ 0x00,
+ SC_X1_RIGHT,
+ 0x00,
+ SC_X1_END,
+ SC_X1_DOWN,
+ SC_X1_PGDN,
+ SC_X1_INS,
+ SC_X1_DEL
+ };
+
+static enum scancode
+gnumach_v1_input_next ()
+{
+ kd_event data_buf;
+#ifndef XKB_SUPPORT
+ int up;
+ enum scancode sc;
+#endif /* not XKB_SUPPORT */
+
+ do
+ {
+ /* io_buf_ptr_t is (char *), not (void *). So I have a few
+ casts to quiet warnings. */
+ mach_msg_type_number_t data_cnt = sizeof (data_buf);
+ error_t err = device_read_inband (kbd_dev, 0, -1, sizeof (kd_event),
+ (void *) &data_buf, &data_cnt);
+
+ /* XXX The error occurred likely because KBD_DEV was closed, so
+ terminate. */
+ if (err)
+ return 0;
+
+ if (kbd_repeater_opened && data_buf.type == KEYBD_EVENT)
+ {
+ kbd_repeat_key (&data_buf);
+ data_buf.type = 0;
+ continue;
+ }
+ }
+ while (data_buf.type != KEYBD_EVENT);
+
+#ifdef XKB_SUPPORT
+ /* XKB code work with set1 scancodes. */
+ return data_buf.value.sc;
+#else /* not XKB_SUPPORT */
+ /* Some fixed codes which are the same in set 1 and set 2, and have
+ the UP flag set. */
+ if (data_buf.value.sc == SC_EXTENDED1
+ || data_buf.value.sc == SC_EXTENDED2
+ || data_buf.value.sc == SC_ERROR)
+ return data_buf.value.sc;
+
+#define SC_SET1_FLAG_UP 0x80
+ up = data_buf.value.sc & SC_SET1_FLAG_UP;
+ sc = sc_set1_to_set2[data_buf.value.sc &~ SC_SET1_FLAG_UP];
+
+ return sc | (up ? SC_FLAG_UP : 0);
+#endif /* not XKB_SUPPORT */
+}
+
+
+static void
+update_leds (void)
+{
+ error_t err;
+
+ if (gnumach_v1_compat)
+ {
+ int led = (led_state.scroll_lock ? 1 : 0)
+ | (led_state.num_lock ? 2 : 0)
+ | (led_state.caps_lock ? 4 : 0);
+
+ err = device_set_status (kbd_dev, KDSETLEDS, &led, 1);
+ /* Just ignore the error, GNUMach 1.3 and older cannot set the
+ keyboard LEDs. */
+ }
+ else
+ {
+ char leds[2];
+ mach_msg_type_number_t data_cnt = 2;
+
+ leds[0] = '\xed';
+ leds[1] = (led_state.scroll_lock ? 1 : 0)
+ | (led_state.num_lock ? 2 : 0)
+ | (led_state.caps_lock ? 4 : 0);
+
+ err = device_write_inband (kbd_dev, 0, -1, (void *) leds, 2, &data_cnt);
+ if (!err && data_cnt == 1)
+ err = device_write_inband (kbd_dev, 0, -1, (void *) &leds[1], 1,
+ &data_cnt);
+ }
+}
+
+static enum scancode
+input_next ()
+{
+ enum scancode sc = 0;
+ unsigned char next;
+
+ /* GNU Mach v1 does provide keyboard input in a different format. */
+ if (gnumach_v1_compat)
+ return gnumach_v1_input_next ();
+
+ /* XXX This should read several characters at once. */
+ do
+ {
+ mach_msg_type_number_t data_cnt = 1;
+ error_t err = device_read_inband (kbd_dev, 0, -1, 1,
+ (void *) &next, &data_cnt);
+
+ /* XXX The error occurred likely because KBD_DEV was closed, so
+ terminate. */
+ if (err)
+ return 0;
+
+ if (next == 0xF0)
+ /* XXX Magic constant. */
+ sc |= SC_FLAG_UP;
+ }
+ while (next == 0xF0); /* XXX Magic constant. */
+
+ sc |= next;
+ return sc;
+}
+
+#ifdef XKB_SUPPORT
+/* Read a keycode using the input_next routine. The translation from
+ scancodes is hardcoded. A configuration file should be used in the
+ near future because this is an UGLY HACK. */
+keycode_t
+read_keycode (void)
+{
+ scancode_t sc = input_next ();
+ int release = 0;
+
+ /* The keypress generated two keycodes. */
+ if (sc == 0xE0)
+ {
+ sc = input_next ();
+
+ release = sc & 0x80;
+ sc &= ~0x80;
+
+ switch (sc)
+ {
+ case 0x1D: /* RCTRL. */
+ sc = 101;
+ break;
+ case 0x38: /* RALT. */
+ sc = 105;
+ break;
+ /* LRGUI MENU. */
+ case 0x5B: /* LGUI. */
+ sc = 107;
+ break;
+ case 0x5C: /* RGUI. */
+ sc = 108;
+ break;
+ case 0x5D: /* MENU. */
+ sc = 109;
+ break;
+ case 0x52: /* Insert. */
+ sc = 98;
+ break;
+ case 0x47: /* Home. */
+ sc = 89;
+ break;
+ case 0x49: /* Pg Up. */
+ sc = 91;
+ break;
+ case 0x53: /* Delete. */
+ sc = 99;
+ break;
+ case 0x4F: /* End. */
+ sc = 95;
+ break;
+ case 0x51: /* Pg Down. */
+ sc = 97;
+ break;
+ case 0x48: /* Arrow up. */
+ sc = 90;
+ break;
+ case 0x50: /* Arrow down. */
+ sc = 96;
+ break;
+ case 0x4b: /* Arrow left. */
+ sc = 92;
+ break;
+ case 0x4d: /* Arrow right. */
+ sc = 94;
+ break;
+ case 0x35: /* '/' */
+ sc = 104;
+ break;
+ case 0x1C: /* KP_Enter. */
+ sc = 100;
+ break;
+ default:
+ sc += 0x78;
+ }
+
+ sc |= release;
+ }
+ else
+ release = sc & 0x80;
+
+ return sc;
+}
+#endif /* XKB_SUPPORT */
+
+/* The input loop. */
+static void *
+input_loop (void *unused)
+{
+#ifdef XKB_SUPPORT
+ /* XXX: until conversion from scancode to keycode is properly implemented
+ XKB won't work on anything but scancode set 1.
+ In the meanwhile, a fixed US layout implementation is used for OSKit
+ Mach. **/
+ if (gnumach_v1_compat)
+ {
+ /* The previous keypress. */
+ keycode_t prevkey = 0;
+
+ while (1)
+ {
+ keypress_t key;
+
+ key.keycode = read_keycode () + min_keys;
+ key.rel = key.keycode & 0x80;
+ key.redir = 0;
+
+ if (!key.rel && key.keycode == prevkey)
+ key.repeat = 1;
+ else
+ key.repeat = 0;
+
+ if (key.repeat)
+ continue;
+
+ /* The keycombination CTRL+Alt+Backspace terminates the console
+ client. Keycodes instead of modifiers+symbols are used to
+ make it able to exit the client, even when the keymaps are
+ faulty. */
+ if ((keystate[64].keypressed || keystate[113].keypressed) /* Alt */
+ && (keystate[37].keypressed || keystate[109].keypressed) /* CTRL*/
+ && keystate[22].keypressed && ctrlaltbs) /* Backspace. */
+ console_exit ();
+
+ if (!key.repeat)
+ xkb_input_key (key.keycode);
+ prevkey = key.keycode;
+ }
+
+ return 0;
+ }
+#endif /* XKB_SUPPORT */
+
+ while (1)
+ {
+ enum scancode fsc = input_next ();
+ enum scancode sc = fsc & ~SC_FLAG_UP;
+ int down = !(fsc & SC_FLAG_UP);
+ char buf[100];
+ size_t size = 0;
+ int modifier = -1;
+
+ static struct {
+ wchar_t direct;
+ unsigned int extended : 2;
+ unsigned int left_shift : 1;
+ unsigned int right_shift : 1;
+ unsigned int caps_lock : 1;
+ unsigned int caps_lock_pressed : 1;
+ unsigned int left_ctrl : 1;
+ unsigned int right_ctrl : 1;
+ unsigned int left_alt : 1;
+ unsigned int right_alt : 1;
+ unsigned int num_lock : 1;
+ unsigned int num_lock_pressed : 1;
+ } state;
+
+ if (!state.left_alt && !state.right_alt)
+ {
+ if (state.left_ctrl || state.right_ctrl)
+ modifier = 2;
+ else if (state.left_shift || state.right_shift)
+ modifier = 1;
+ else
+ modifier = 0;
+ }
+ else if (state.left_alt)
+ {
+ if (state.left_ctrl || state.right_ctrl)
+ modifier = 5;
+ if (state.left_shift || state.right_shift)
+ modifier = 4;
+ else
+ modifier = 3;
+ }
+ else if (state.right_alt)
+ {
+ if (!state.left_ctrl && !state.right_ctrl
+ && !state.left_shift && !state.right_shift)
+ modifier = 6;
+ }
+
+ if (!state.extended)
+ {
+ if (fsc == SC_EXTENDED1)
+ state.extended = 1;
+ else if (fsc == SC_EXTENDED2)
+ state.extended = 2;
+ else if (sc == SC_LEFT_SHIFT)
+ state.left_shift = down;
+ else if (sc == SC_RIGHT_SHIFT)
+ state.right_shift = down;
+ else if (sc == SC_CAPSLOCK)
+ {
+ if (down && !state.caps_lock_pressed)
+ {
+ state.caps_lock = !state.caps_lock;
+ state.caps_lock_pressed = 1;
+ led_state.caps_lock = state.caps_lock;
+ update_leds ();
+ }
+ else if (!down)
+ state.caps_lock_pressed = 0;
+ }
+ else if (sc == SC_LEFT_CTRL)
+ state.left_ctrl = down;
+ else if (sc == SC_LEFT_ALT)
+ state.left_alt = down;
+ else if (state.left_alt && down && IS_FUNC_KEY (sc))
+ {
+ /* The virtual console to switch to. */
+ int vc = 0;
+
+ /* Check if a function key was pressed.
+ Choose the virtual console corresponding to that key. */
+ switch (sc)
+ {
+ case SC_F1:
+ vc = 1;
+ break;
+ case SC_F2:
+ vc = 2;
+ break;
+ case SC_F3:
+ vc = 3;
+ break;
+ case SC_F4:
+ vc = 4;
+ break;
+ case SC_F5:
+ vc = 5;
+ break;
+ case SC_F6:
+ vc = 6;
+ break;
+ case SC_F7:
+ vc = 7;
+ break;
+ case SC_F8:
+ vc = 8;
+ break;
+ case SC_F9:
+ vc = 9;
+ break;
+ case SC_F10:
+ vc = 10;
+ break;
+ case SC_F11:
+ vc = 11;
+ break;
+ case SC_F12:
+ vc = 12;
+ break;
+ /* No function key was pressed, don't
+ switch to another vc. */
+ default:
+ vc = 0;
+ }
+
+ if (vc)
+ console_switch (vc, 0);
+ }
+ else if (state.left_alt && state.left_ctrl && down && sc == SC_BACKSPACE)
+ console_exit ();
+ else if (state.right_alt && down && sc == SC_PAD_0) /* XXX */
+ state.direct = (state.direct << 4) | 0x0;
+ else if (state.right_alt && down && sc == SC_PAD_1) /* XXX */
+ state.direct = (state.direct << 4) | 0x1;
+ else if (state.right_alt && down && sc == SC_PAD_2) /* XXX */
+ state.direct = (state.direct << 4) | 0x2;
+ else if (state.right_alt && down && sc == SC_PAD_3) /* XXX */
+ state.direct = (state.direct << 4) | 0x3;
+ else if (state.right_alt && down && sc == SC_PAD_4) /* XXX */
+ state.direct = (state.direct << 4) | 0x4;
+ else if (state.right_alt && down && sc == SC_PAD_5) /* XXX */
+ state.direct = (state.direct << 4) | 0x5;
+ else if (state.right_alt && down && sc == SC_PAD_6) /* XXX */
+ state.direct = (state.direct << 4) | 0x6;
+ else if (state.right_alt && down && sc == SC_PAD_7) /* XXX */
+ state.direct = (state.direct << 4) | 0x7;
+ else if (state.right_alt && down && sc == SC_PAD_8) /* XXX */
+ state.direct = (state.direct << 4) | 0x8;
+ else if (state.right_alt && down && sc == SC_PAD_9) /* XXX */
+ state.direct = (state.direct << 4) | 0x9;
+ else if (state.right_alt && down && sc == SC_NUMLOCK) /* XXX */
+ state.direct = (state.direct << 4) | 0xa;
+ else if (state.right_alt && down && sc == SC_PAD_ASTERISK) /* XXX */
+ state.direct = (state.direct << 4) | 0xc;
+ else if (state.right_alt && down && sc == SC_PAD_MINUS) /* XXX */
+ state.direct = (state.direct << 4) | 0xd;
+ else if (state.right_alt && down && sc == SC_PAD_PLUS) /* XXX */
+ state.direct = (state.direct << 4) | 0xe;
+ else if (sc == SC_NUMLOCK)
+ {
+ if (down && !state.num_lock_pressed)
+ {
+ state.num_lock = !state.num_lock;
+ state.num_lock_pressed = 1;
+ led_state.num_lock = state.num_lock;
+ update_leds ();
+ }
+ else if (!down)
+ state.num_lock_pressed = 0;
+ }
+ else if (down && sc < sizeof (sc_to_kc)/sizeof (sc_to_kc[0]))
+ {
+#if QUAERENDO_INVENIETIS
+ if (state.left_alt && state.right_alt
+ && sc_to_kc[sc][0][0] >= '0' && sc_to_kc[sc][0][0] <= '9'
+ && sc_to_kc[sc][0][1] == '\0')
+ console_deprecated (sc_to_kc[sc][0][0] - '0');
+ else
+#endif
+ {
+ /* Special rule for caps lock. */
+ if (modifier == 0 && state.caps_lock
+ && sc_to_kc[sc][modifier]
+ && sc_to_kc[sc][modifier][0] >= 'a'
+ && sc_to_kc[sc][modifier][0] <= 'z'
+ && sc_to_kc[sc][modifier][1] == '\0')
+ modifier = 1;
+ else if (state.num_lock && sc == SC_PAD_0)
+ {
+ modifier = 0;
+ sc = SC_0;
+ }
+ else if (state.num_lock && sc == SC_PAD_1)
+ {
+ modifier = 0;
+ sc = SC_1;
+ }
+ else if (state.num_lock && sc == SC_PAD_2)
+ {
+ modifier = 0;
+ sc = SC_2;
+ }
+ else if (state.num_lock && sc == SC_PAD_3)
+ {
+ modifier = 0;
+ sc = SC_3;
+ }
+ else if (state.num_lock && sc == SC_PAD_4)
+ {
+ modifier = 0;
+ sc = SC_4;
+ }
+ else if (state.num_lock && sc == SC_PAD_5)
+ {
+ modifier = 0;
+ sc = SC_5;
+ }
+ else if (state.num_lock && sc == SC_PAD_6)
+ {
+ modifier = 0;
+ sc = SC_6;
+ }
+ else if (state.num_lock && sc == SC_PAD_7)
+ {
+ modifier = 0;
+ sc = SC_7;
+ }
+ else if (state.num_lock && sc == SC_PAD_8)
+ {
+ modifier = 0;
+ sc = SC_8;
+ }
+ else if (state.num_lock && sc == SC_PAD_9)
+ {
+ modifier = 0;
+ sc = SC_9;
+ }
+ else if (state.num_lock && sc == SC_PAD_DECIMAL)
+ {
+ modifier = 0;
+ sc = SC_PERIOD;
+ }
+
+ if (modifier >= 0 && sc_to_kc[sc][modifier])
+ {
+ if (!sc_to_kc[sc][modifier][0])
+ {
+ /* Special meaning, emit NUL. */
+ assert (size < 100);
+ buf[size++] = '\0';
+ }
+ else
+ {
+ assert (size
+ < 101 - strlen(sc_to_kc[sc][modifier]));
+ strcpy (&buf[size], sc_to_kc[sc][modifier]);
+ size += strlen (sc_to_kc[sc][modifier]);
+ }
+ }
+ }
+ }
+ }
+ else if (state.extended == 1)
+ {
+ state.extended = 0;
+ if (sc == SC_X1_RIGHT_CTRL)
+ state.right_ctrl = down;
+ else if (sc == SC_X1_RIGHT_ALT)
+ {
+ state.right_alt = down;
+
+ /* Handle the AltGR+Keypad direct input. */
+ if (down)
+ state.direct = (wchar_t) 0;
+ else
+ {
+ if (state.direct != (wchar_t) 0)
+ {
+ char *buffer = &buf[size];
+ size_t left = sizeof (buf) - size;
+ char *inbuf = (char *) &state.direct;
+ size_t inbufsize = sizeof (wchar_t);
+ size_t nr;
+
+ nr = iconv (cd, &inbuf, &inbufsize, &buffer, &left);
+ if (nr == (size_t) -1)
+ {
+ if (errno == E2BIG)
+ console_error (L"Input buffer overflow");
+ else if (errno == EILSEQ)
+ console_error
+ (L"Input contained invalid byte sequence");
+ else if (errno == EINVAL)
+ console_error
+ (L"Input contained incomplete byte sequence");
+ else
+ console_error
+ (L"Input caused unexpected error");
+ }
+ size = sizeof (buf) - left;
+ }
+ }
+ }
+ else if (state.right_alt && down && sc == SC_X1_PAD_SLASH) /* XXX */
+ state.direct = (state.direct << 4) | 0xb;
+ else if (state.right_alt && down && sc == SC_X1_PAD_ENTER) /* XXX */
+ state.direct = (state.direct << 4) | 0xf;
+ else if (state.left_alt && down && sc == SC_X1_RIGHT) /* XXX */
+ console_switch (0, 1);
+ else if (state.left_alt && down && sc == SC_X1_LEFT) /* XXX */
+ console_switch (0, -1);
+ else if (state.left_alt && down && sc == SC_X1_UP) /* XXX */
+ console_scrollback (CONS_SCROLL_DELTA_LINES, 1);
+ else if (state.left_alt && down && sc == SC_X1_DOWN) /* XXX */
+ console_scrollback (CONS_SCROLL_DELTA_LINES, -1);
+ else if ((state.right_shift || state.left_shift)
+ && down && sc == SC_X1_PGUP) /* XXX */
+ console_scrollback (CONS_SCROLL_DELTA_SCREENS, 0.5);
+ else if ((state.right_shift || state.left_shift)
+ && down && sc == SC_X1_PGDN) /* XXX */
+ console_scrollback (CONS_SCROLL_DELTA_SCREENS, -0.5);
+ else if (down && sc < sizeof (sc_x1_to_kc)/sizeof (sc_x1_to_kc[0]))
+ {
+ if (modifier >= 0 && sc_x1_to_kc[sc][modifier])
+ {
+ assert (size < 101 - strlen(sc_x1_to_kc[sc][modifier]));
+ strcpy (&buf[size], sc_x1_to_kc[sc][modifier]);
+ size += strlen (sc_x1_to_kc[sc][modifier]);
+ }
+ }
+ }
+ else if (state.extended == 2)
+ state.extended = 3;
+ else if (state.extended == 3)
+ state.extended = 0;
+
+ if (size)
+ console_input (buf, size);
+ }
+ return 0;
+}
+
+
+
+
+static const char doc[] = "PC Keyboard Driver";
+
+struct arguments
+{
+ int pos;
+#ifdef XKB_SUPPORT
+ char *xkbdir;
+ char *keymapfile;
+ char *keymap;
+ char *composefile;
+ int ctrlaltbs;
+ int repeat_delay;
+ int repeat_interval;
+#endif
+};
+
+static const struct argp_option options[] =
+ {
+#ifdef XKB_SUPPORT
+/* Some random ids for options available only in long form. */
+#define REPEAT_DELAY_ID 25425
+#define REPEAT_INTERVAL_ID 5322
+ {"xkbdir", 'x', "DIR", 0,
+ "Directory containing the XKB configuration files" },
+ {"keymapfile", 'f', "FILE", 0,
+ "File containing the keymap" },
+ {"keymap", 'k', "SECTIONNAME" , 0,
+ "Choose keymap"},
+ {"compose", 'o', "COMPOSEFILE", 0,
+ "Compose file to load (default none)"},
+ {"ctrlaltbs", 'c', 0 , 0,
+ "CTRL + Alt + Backspace will exit the console client (default)."},
+ {"no-ctrlaltbs", 'n', 0 , 0,
+ "CTRL + Alt + Backspace will not exit the console client."},
+ {"repeat-delay", REPEAT_DELAY_ID, "DELAY", 0,
+ "Delay before pressed key starts repeating (measured in jiffies)"},
+ {"repeat-interval", REPEAT_INTERVAL_ID, "INTERVAL", 0,
+ "Time elapsed between repeated keys (measured in jiffies)"},
+#endif /* XKB_SUPPORT */
+ {"repeat", 'r', "NODE", OPTION_ARG_OPTIONAL,
+ "Set a repeater translator on NODE (default: " DEFAULT_REPEATER_NODE ")"},
+ { 0 }
+ };
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct arguments *arguments = state->input;
+
+ switch (key)
+ {
+#ifdef XKB_SUPPORT
+ case 'x':
+ arguments->xkbdir = arg;
+ break;
+
+ case 'f':
+ arguments->keymapfile = arg;
+ break;
+
+ case 'k':
+ arguments->keymap = arg;
+ break;
+
+ case 'o':
+ arguments->composefile = arg;
+ break;
+
+ case 'c':
+ arguments->ctrlaltbs = 1;
+ break;
+
+ case 'n':
+ arguments->ctrlaltbs = 0;
+ break;
+
+ case REPEAT_DELAY_ID:
+ arguments->repeat_delay = atoi(arg);
+ break;
+
+ case REPEAT_INTERVAL_ID:
+ arguments->repeat_interval = atoi(arg);
+ break;
+#endif /* XKB_SUPPORT */
+ case 'r':
+ repeater_node = arg ? arg: DEFAULT_REPEATER_NODE;
+ break;
+
+ case ARGP_KEY_END:
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ arguments->pos = state->next;
+ return 0;
+}
+
+static struct argp argp = {options, parse_opt, 0, doc};
+
+/* Initialize the PC keyboard driver. */
+static error_t
+pc_kbd_init (void **handle, int no_exit, int argc, char *argv[], int *next)
+{
+ error_t err;
+ struct arguments arguments =
+ {
+ pos: 1
+#ifdef XKB_SUPPORT
+ , xkbdir: 0
+ , keymapfile: 0
+ , keymap: 0
+ , composefile: 0
+ , ctrlaltbs: 1
+ , repeat_delay: -1
+ , repeat_interval: -1
+#endif
+ };
+
+ /* Parse the arguments. */
+ err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER | ARGP_NO_EXIT
+ | ARGP_SILENT, 0 , &arguments);
+ *next += arguments.pos - 1;
+
+ if (err && err != EINVAL)
+ return err;
+
+#ifdef XKB_SUPPORT
+ if (!arguments.xkbdir)
+ {
+ arguments.xkbdir = XKB_DATA_DIR;
+ }
+ if (!arguments.keymapfile)
+ {
+ arguments.keymapfile = "keymap/hurd";
+ }
+ if (arguments.repeat_delay <= 0)
+ {
+ arguments.repeat_delay = 50;
+ }
+ if (arguments.repeat_interval <= 0)
+ {
+ arguments.repeat_interval = 10;
+ }
+
+ ctrlaltbs = arguments.ctrlaltbs;
+ xkb_repeat_delay = arguments.repeat_delay;
+ xkb_repeat_interval = arguments.repeat_interval;
+
+ err = read_composefile (arguments.composefile);
+ if (err)
+ return err;
+
+ xkb_data_init ();
+ err = xkb_load_layout (arguments.xkbdir, arguments.keymapfile,
+ arguments.keymap);
+
+ if (err)
+ return err;
+#endif /* XKB_SUPPORT */
+
+ return 0;
+}
+
+
+/* Start the PC keyboard driver. */
+static error_t
+pc_kbd_start (void *handle)
+{
+ error_t err;
+ pthread_t thread;
+ device_t device_master;
+
+ cd = iconv_open ("UTF-8", "WCHAR_T");
+ if (cd == (iconv_t) -1)
+ return errno;
+
+ err = get_privileged_ports (0, &device_master);
+ if (err)
+ {
+ iconv_close (cd);
+ return err;
+ }
+
+ err = device_open (device_master, D_READ | D_WRITE, "@>=kbd", &kbd_dev);
+ if (err == D_NO_SUCH_DEVICE)
+ {
+ /* GNU Mach v1 has a different device. */
+ gnumach_v1_compat = 1;
+ err = device_open (device_master, D_READ, "kbd", &kbd_dev);
+ }
+
+ mach_port_deallocate (mach_task_self (), device_master);
+ if (err)
+ {
+ iconv_close (cd);
+ return err;
+ }
+
+ if (gnumach_v1_compat)
+ {
+ int data = KB_EVENT;
+ err = device_set_status (kbd_dev, KDSKBDMODE, &data, 1);
+ if (err)
+ {
+ device_close (kbd_dev);
+ mach_port_deallocate (mach_task_self (), kbd_dev);
+ iconv_close (cd);
+ return err;
+ }
+#ifdef XKB_SUPPORT
+ xkb_init_repeat (xkb_repeat_delay, xkb_repeat_interval);
+#endif
+ }
+ update_leds ();
+
+ err = driver_add_input (&pc_kbd_ops, NULL);
+ if (err)
+ {
+ if (gnumach_v1_compat)
+ {
+ int data = KB_ASCII;
+ device_set_status (kbd_dev, KDSKBDMODE, &data, 1);
+ }
+ device_close (kbd_dev);
+ mach_port_deallocate (mach_task_self (), kbd_dev);
+ iconv_close (cd);
+ return err;
+ }
+
+ if (repeater_node)
+ kbd_setrepeater (repeater_node, &cnode);
+
+ err = pthread_create (&thread, NULL, input_loop, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+
+ return 0;
+}
+
+/* Deinitialize the PC keyboard driver. */
+static error_t
+pc_kbd_fini (void *handle, int force)
+{
+ driver_remove_input (&pc_kbd_ops, NULL);
+ if (gnumach_v1_compat)
+ {
+ int data = KB_ASCII;
+ device_set_status (kbd_dev, KDSKBDMODE, &data, 1);
+ }
+ device_close (kbd_dev);
+ mach_port_deallocate (mach_task_self (), kbd_dev);
+ iconv_close (cd);
+
+ console_unregister_consnode (cnode);
+ console_destroy_consnode (cnode);
+
+ return 0;
+}
+
+
+/* Set the scroll lock status indication (Scroll LED) to ONOFF. */
+static error_t
+pc_kbd_set_scroll_lock_status (void *handle, int onoff)
+{
+ led_state.scroll_lock = onoff;
+ update_leds ();
+ return 0;
+}
+
+
+struct driver_ops driver_pc_kbd_ops =
+ {
+ pc_kbd_init,
+ pc_kbd_start,
+ pc_kbd_fini
+ };
+
+static struct input_ops pc_kbd_ops =
+ {
+ pc_kbd_set_scroll_lock_status,
+ NULL
+ };
diff --git a/console-client/pc-mouse.c b/console-client/pc-mouse.c
new file mode 100644
index 00000000..abdb602b
--- /dev/null
+++ b/console-client/pc-mouse.c
@@ -0,0 +1,523 @@
+/* pc-mouse.c - Mouse driver.
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+ Written by Marco Gerards.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <argp.h>
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <device/device.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include "driver.h"
+#include "mach-inputdev.h"
+
+static struct input_ops pc_mouse_ops;
+
+/* Default to the protocol I use :). */
+static int majordev = IBM_MOUSE;
+static int minordev = 0;
+
+static device_t mousedev;
+
+
+/* The default name of the node of the repeater. */
+#define DEFAULT_REPEATER_NODE "mouse"
+
+/* The amount of mouse events that can be stored in the event buffer. */
+#define MOUSEDEVTBUFSZ 256
+
+/* The size of the event buffer in bytes. */
+#define MOUSEBUFSZ (MOUSEDEVTBUFSZ * sizeof (kd_event))
+
+/* Return the position of X in the buffer. */
+#define MOUSEBUF_POS(x) ((x) % MOUSEBUFSZ)
+
+/* The mouse sensitivity. */
+#define STRINGIFY(x) STRINGIFY_1(x)
+#define STRINGIFY_1(x) #x
+#define DEFAULT_MOUSE_SENS 1.0
+#define DEFAULT_MOUSE_SENS_STRING STRINGIFY(DEFAULT_MOUSE_SENS)
+
+/* The mouse event buffer. */
+static struct mousebuf
+{
+ char evtbuffer[MOUSEBUFSZ];
+ int pos;
+ size_t size;
+ pthread_cond_t readcond;
+ pthread_cond_t writecond;
+} mousebuf;
+
+/* Wakeup for select */
+static pthread_cond_t select_alert;
+
+/* The global lock */
+static pthread_mutex_t global_lock;
+
+/* Amount of times the device was opened. Normally this translator
+ should be only opened once. */
+static int mouse_repeater_opened;
+
+/* The name of the repeater node. */
+static char *repeater_node;
+
+/* The repeater node. */
+static consnode_t cnode;
+
+/* The mouse sensitivity. */
+float mouse_sens = DEFAULT_MOUSE_SENS;
+
+/* Place the mouse event EVNT in the mouse event buffer. */
+static void
+repeat_event (kd_event *evt)
+{
+ kd_event *ev;
+
+ pthread_mutex_lock (&global_lock);
+ while (mousebuf.size + sizeof (kd_event) > MOUSEBUFSZ)
+ {
+ /* The input buffer is full, wait until there is some space. If this call
+ * is interrupted, silently continue */
+ (void) pthread_hurd_cond_wait_np (&mousebuf.writecond, &global_lock);
+ }
+ ev = (kd_event *) &mousebuf.evtbuffer[MOUSEBUF_POS (mousebuf.pos
+ + mousebuf.size)];
+ mousebuf.size += sizeof (kd_event);
+ memcpy (ev, evt, sizeof (kd_event));
+
+ pthread_cond_broadcast (&mousebuf.readcond);
+ pthread_cond_broadcast (&select_alert);
+ pthread_mutex_unlock (&global_lock);
+}
+
+
+static error_t
+repeater_select (struct protid *cred, mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ struct timespec *tsp, int *type)
+{
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (*type & ~SELECT_READ)
+ return EINVAL;
+
+ if (*type == 0)
+ return 0;
+
+ pthread_mutex_lock (&global_lock);
+ while (1)
+ {
+ if (mousebuf.size > 0)
+ {
+ *type = SELECT_READ;
+ pthread_mutex_unlock (&global_lock);
+
+ return 0;
+ }
+
+ ports_interrupt_self_on_port_death (cred, reply);
+ err = pthread_hurd_cond_timedwait_np (&select_alert, &global_lock, tsp);
+ if (err)
+ {
+ *type = 0;
+ pthread_mutex_unlock (&global_lock);
+
+ if (err == ETIMEDOUT)
+ err = 0;
+
+ return err;
+ }
+ }
+}
+
+
+static void
+repeater_open (void)
+{
+ mouse_repeater_opened++;
+}
+
+
+static void
+repeater_close (void)
+{
+ mouse_repeater_opened--;
+ if (!mouse_repeater_opened)
+ {
+ mousebuf.pos = 0;
+ mousebuf.size = 0;
+ }
+}
+
+
+static error_t
+repeater_read (struct protid *cred, char **data,
+ mach_msg_type_number_t *datalen, off_t offset,
+ mach_msg_type_number_t amount)
+{
+ /* Deny access if they have bad credentials. */
+ if (! cred)
+ return EOPNOTSUPP;
+ else if (! (cred->po->openstat & O_READ))
+ return EBADF;
+
+ pthread_mutex_lock (&global_lock);
+ while (!mousebuf.size)
+ {
+ if (cred->po->openstat & O_NONBLOCK && mousebuf.size == 0)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EWOULDBLOCK;
+ }
+
+ if (pthread_hurd_cond_wait_np (&mousebuf.readcond, &global_lock))
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EINTR;
+ }
+ }
+
+ amount = (amount / sizeof (kd_event) - 1) * sizeof (kd_event);
+ if (amount > mousebuf.size)
+ amount = mousebuf.size;
+
+ if (amount > 0)
+ {
+ char *mousedata;
+ unsigned int i = 0;
+
+ /* Allocate a buffer when this is required. */
+ if (*datalen < amount)
+ {
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return ENOMEM;
+ }
+ }
+
+ /* Copy the bytes to the user's buffer and remove them from the
+ mouse events buffer. */
+ mousedata = *data;
+ while (i != amount)
+ {
+ mousedata[i++] = mousebuf.evtbuffer[mousebuf.pos++];
+ mousebuf.pos = MOUSEBUF_POS (mousebuf.pos);
+ }
+ mousebuf.size -= amount;
+ pthread_cond_broadcast (&mousebuf.writecond);
+ }
+
+ *datalen = amount;
+ pthread_mutex_unlock (&global_lock);
+
+ return 0;
+}
+
+
+
+static void *
+input_loop (void *unused)
+{
+ kd_event *ev;
+ vm_offset_t buf;
+ mach_msg_type_number_t buf_size;
+
+ while (1)
+ {
+ struct mouse_event evt = { 0 };
+ device_read (mousedev, 0, 0, sizeof (kd_event),
+ (char **) &buf, &buf_size);
+ ev = (kd_event *) buf;
+
+ /* The repeater is set, send the event to the repeater. */
+ if (mouse_repeater_opened)
+ {
+ repeat_event (ev);
+ vm_deallocate (mach_task_self(), buf, buf_size);
+ continue;
+ }
+
+ evt.mouse_movement = CONS_VCONS_MOUSE_MOVE_REL;
+
+ switch (ev->type)
+ {
+ case MOUSE_LEFT:
+ evt.button = CONS_MOUSE_BUTTON1;
+ break;
+ case MOUSE_MIDDLE:
+ evt.button = CONS_MOUSE_BUTTON2;
+ break;
+ case MOUSE_RIGHT:
+ evt.button = CONS_MOUSE_BUTTON3;
+ break;
+
+ case MOUSE_MOTION:
+ evt.x = ev->value.mmotion.mm_deltaX * mouse_sens;
+ evt.y = -ev->value.mmotion.mm_deltaY * mouse_sens;
+ break;
+ }
+
+ if (ev->type > 0 && ev->type <= 3)
+ {
+ if (ev->value.up)
+ evt.mouse_button = CONS_VCONS_MOUSE_BUTTON_RELEASED;
+ else
+ evt.mouse_button = CONS_VCONS_MOUSE_BUTTON_PRESSED;
+ }
+
+ /* Generate a mouse movement event. */
+ console_move_mouse (&evt);
+ vm_deallocate (mach_task_self(), buf, buf_size);
+ }
+
+ return NULL;
+}
+
+
+#define PROTO_MOUSESYSTEM "mousesystem"
+#define PROTO_MICROSOFT "microsoft"
+#define PROTO_PS2 "ps/2"
+#define PROTO_NOMOUSE "nomouse"
+#define PROTO_LOGITECH "logitech"
+#define PROTO_MOUSE7 "mouse7"
+
+/* The supported mouse protocols. Be careful with adding more, the
+ protocols are carefully ordered so the index is the major device
+ number. */
+static char *mouse_protocols[] =
+ {
+ PROTO_MOUSESYSTEM,
+ PROTO_MICROSOFT,
+ PROTO_PS2,
+ PROTO_NOMOUSE,
+ PROTO_LOGITECH,
+ PROTO_MOUSE7
+ };
+
+static const char doc[] = "Mouse Driver";
+
+static const struct argp_option options[] =
+ {
+ { "protocol", 'p', "PROTOCOL", 0, "One of the protocols: "
+ PROTO_MOUSESYSTEM ", " PROTO_MICROSOFT ", " PROTO_PS2 ", "
+ PROTO_NOMOUSE ", " PROTO_LOGITECH ", " PROTO_MOUSE7 },
+ { "device", 'e', "DEVICE" , 0,
+ "One of the devices: " DEV_COM0 ", " DEV_COM1 },
+ { "sensitivity", 's', "SENSITIVITY", 0, "The mouse"
+ " sensitivity (default " DEFAULT_MOUSE_SENS_STRING "). A lower value"
+ " means more sensitive" },
+ { "repeat", 'r', "NODE", OPTION_ARG_OPTIONAL,
+ "Set a repeater translator on NODE (default: " DEFAULT_REPEATER_NODE ")"},
+ { 0 }
+ };
+
+static error_t setrepeater (const char *nodename);
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ int *pos = (int *) state->input;
+
+ switch (key)
+ {
+ case 'p':
+ {
+ unsigned int i;
+
+ for (i = 0; i < (sizeof (mouse_protocols) / sizeof (char *)); i++)
+ {
+ if (!strcasecmp (arg, mouse_protocols[i]))
+ {
+ majordev = i;
+ *pos = state->next;
+ return 0;
+ }
+ }
+ fprintf (stderr, "Unknown protocol `%s'\n", arg);
+ argp_usage (state);
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ case 'e':
+ {
+ if (!strcasecmp (DEV_COM0, arg))
+ minordev = 0;
+ else if (!strcasecmp (DEV_COM1, arg))
+ minordev = 1;
+ else
+ {
+ fprintf (stderr, "Unknown device `%s'\n", arg);
+ argp_usage (state);
+ return ARGP_ERR_UNKNOWN;
+ }
+ break;
+ }
+
+ case 'r':
+ repeater_node = arg ? arg : DEFAULT_REPEATER_NODE;
+ break;
+
+ case 's':
+ {
+ char *tail;
+
+ errno = 0;
+ mouse_sens = strtod (arg, &tail);
+ if (tail == NULL || tail == arg || *tail != '\0')
+ argp_error (state, "SENSITIVITY is not a number: %s", arg);
+ if (errno)
+ argp_error (state, "Overflow in argument SENSITIVITY %s", arg);
+ break;
+ }
+
+ case ARGP_KEY_END:
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ *pos = state->next;
+ return 0;
+}
+
+
+static struct argp argp = {options, parse_opt, 0, doc};
+
+static error_t
+pc_mouse_init (void **handle, int no_exit, int argc, char *argv[], int *next)
+{
+ error_t err;
+ int pos = 1;
+
+ /* Parse the arguments. */
+ err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER | ARGP_NO_EXIT
+ | ARGP_SILENT, 0, &pos);
+ *next += pos - 1;
+ if (err && err != EINVAL)
+ return err;
+
+ return 0;
+}
+
+
+static error_t
+pc_mouse_start (void *handle)
+{
+ error_t err;
+ pthread_t thread;
+ char device_name[9];
+ int devnum = majordev << 3 | minordev;
+ device_t device_master;
+
+ sprintf (device_name, "mouse%d", devnum);
+ err = get_privileged_ports (0, &device_master);
+ if (err)
+ return err;
+
+ err = device_open (device_master, D_READ, device_name, &mousedev);
+ mach_port_deallocate (mach_task_self (), device_master);
+ if (err)
+ return ENODEV;
+
+ err = driver_add_input (&pc_mouse_ops, NULL);
+ if (err)
+ {
+ device_close (mousedev);
+ mach_port_deallocate (mach_task_self (), mousedev);
+
+ return err;
+ }
+
+ err = pthread_create (&thread, NULL, input_loop, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+
+ if (repeater_node)
+ setrepeater (repeater_node);
+
+ return 0;
+}
+
+
+static error_t
+pc_mouse_fini (void *handle, int force)
+{
+ device_close (mousedev);
+ mach_port_deallocate (mach_task_self (), mousedev);
+ console_unregister_consnode (cnode);
+ console_destroy_consnode (cnode);
+
+ return 0;
+}
+
+
+
+struct driver_ops driver_pc_mouse_ops =
+ {
+ pc_mouse_init,
+ pc_mouse_start,
+ pc_mouse_fini
+ };
+
+static struct input_ops pc_mouse_ops =
+ {
+ NULL,
+ NULL
+ };
+
+
+/* Set make repeater translator node named NODENAME. */
+static error_t
+setrepeater (const char *nodename)
+{
+ error_t err;
+
+ err = console_create_consnode (nodename, &cnode);
+ if (err)
+ return err;
+
+ cnode->read = repeater_read;
+ cnode->write = 0;
+ cnode->select = repeater_select;
+ cnode->open = repeater_open;
+ cnode->close = repeater_close;
+ cnode->demuxer = 0;
+
+ pthread_mutex_init (&global_lock, NULL);
+
+ pthread_cond_init (&mousebuf.readcond, NULL);
+ pthread_cond_init (&mousebuf.writecond, NULL);
+ pthread_cond_init (&select_alert, NULL);
+
+ console_register_consnode (cnode);
+
+ return 0;
+}
diff --git a/console-client/timer.c b/console-client/timer.c
new file mode 100644
index 00000000..69cc643d
--- /dev/null
+++ b/console-client/timer.c
@@ -0,0 +1,219 @@
+/* timer.c - A timer module for Mach.
+ Copyright (C) 1995,96,2000,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG and Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+#include <string.h>
+#include <maptime.h>
+#include <mach.h>
+#include <pthread.h>
+#include <stdio.h>
+
+#include "timer.h"
+
+/* The value of fetch_jiffies() at startup. */
+long long timer_root_jiffies;
+
+/* The mapped time. */
+volatile struct mapped_time_value *timer_mapped_time;
+
+
+/* The timer thread. */
+static thread_t timer_thread;
+
+/* The lock protects the timer list TIMERS. */
+static pthread_mutex_t timer_lock;
+
+/* A list of all active timers. */
+static struct timer_list *timers;
+
+
+static inline void
+timer_add_internal (struct timer_list *timer)
+{
+ struct timer_list **tp;
+
+ for (tp = &timers; *tp; tp = &(*tp)->next)
+ if ((*tp)->expires > timer->expires)
+ {
+ timer->next = *tp;
+ timer->next->prev = &timer->next;
+ timer->prev = tp;
+ *tp = timer;
+ break;
+ }
+ if (!*tp)
+ {
+ timer->next = 0;
+ timer->prev = tp;
+ *tp = timer;
+ }
+}
+
+
+/* Make the timer thread aware of new timers at the beginning of the
+ list. */
+static inline void
+kick_timer_thread (void)
+{
+ /* XXX This is a whacky notion. */
+ while (!timer_thread)
+ swtch_pri (0);
+
+ if (timer_thread != mach_thread_self ())
+ {
+ thread_suspend (timer_thread);
+ thread_abort (timer_thread);
+ thread_resume (timer_thread);
+ }
+}
+
+/* The timer thread. */
+static void *
+timer_function (void *this_is_a_pointless_variable_with_a_rather_long_name)
+{
+ mach_port_t recv = mach_reply_port ();
+ int wait = 0;
+
+ timer_thread = mach_thread_self ();
+
+ pthread_mutex_lock (&timer_lock);
+ while (1)
+ {
+ int jiff = fetch_jiffies ();
+
+ if (!timers)
+ wait = -1;
+ else if (timers->expires < jiff)
+ wait = 0;
+ else
+ wait = ((timers->expires - jiff) * 1000) / HZ;
+
+ pthread_mutex_unlock (&timer_lock);
+ mach_msg (NULL, (MACH_RCV_MSG | MACH_RCV_INTERRUPT
+ | (wait == -1 ? 0 : MACH_RCV_TIMEOUT)),
+ 0, 0, recv, wait, MACH_PORT_NULL);
+ pthread_mutex_lock (&timer_lock);
+
+ while (timers && timers->expires < fetch_jiffies ())
+ {
+ struct timer_list *tp;
+
+ tp = timers;
+
+ timers = timers->next;
+ if (timers)
+ timers->prev = &timers;
+
+ tp->next = 0;
+ tp->prev = 0;
+
+ if ((*tp->fnc) (tp->fnc_data))
+ timer_add_internal (tp);
+ }
+ }
+
+ return NULL;
+}
+
+
+/* Initialize the timer component. Must be called once at startup. */
+error_t
+timer_init (void)
+{
+ error_t err;
+ struct timeval tp;
+ pthread_t thread;
+
+ pthread_mutex_init (&timer_lock, NULL);
+
+ err = maptime_map (0, 0, &timer_mapped_time);
+ if (err)
+ return err;
+
+ maptime_read (timer_mapped_time, &tp);
+
+ timer_root_jiffies = (long long) tp.tv_sec * HZ
+ + ((long long) tp.tv_usec * HZ) / 1000000;
+
+ err = pthread_create (&thread, NULL, timer_function, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+
+ return 0;
+}
+
+
+/* Initialize the timer TIMER. */
+void
+timer_clear (struct timer_list *timer)
+{
+ memset (timer, 0, sizeof (struct timer_list));
+}
+
+/* Add the timer TIMER to the list. */
+void
+timer_add (struct timer_list *timer)
+{
+ pthread_mutex_lock (&timer_lock);
+ timer_add_internal (timer);
+
+ if (timers == timer)
+ kick_timer_thread ();
+
+ pthread_mutex_unlock (&timer_lock);
+}
+
+/* Remove the timer TIMER from the list. */
+int
+timer_remove (struct timer_list *timer)
+{
+ pthread_mutex_lock (&timer_lock);
+ if (timer->prev)
+ {
+ *timer->prev = timer->next;
+ if (timer->next)
+ timer->next->prev = timer->prev;
+
+ timer->next = 0;
+ timer->prev = 0;
+ pthread_mutex_unlock (&timer_lock);
+ return 1;
+ }
+ else
+ {
+ pthread_mutex_unlock (&timer_lock);
+ return 0;
+ }
+}
+
+/* Change the expiration time of the timer TIMER to EXPIRES. */
+void
+timer_change (struct timer_list *timer, long long expires)
+{
+ /* XXX Should optimize this. */
+ timer_remove (timer);
+ timer->expires = expires;
+ timer_add (timer);
+}
diff --git a/console-client/timer.h b/console-client/timer.h
new file mode 100644
index 00000000..4204192e
--- /dev/null
+++ b/console-client/timer.h
@@ -0,0 +1,72 @@
+/* timer.h - Interface to a timer module for Mach.
+ Copyright (C) 1995,96,2000,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG and Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef _TIMER_H_
+#define _TIMER_H_
+
+#include <errno.h>
+#include <maptime.h>
+
+
+/* Initialize the timer component. Must be called once at startup. */
+error_t timer_init (void);
+
+/* The data structure of a timer. A user can set the values EXPIRES,
+ DATA and FUNCTION, and should leave the other fields alone. */
+struct timer_list
+{
+ struct timer_list *next, **prev; /* things like to test "T->prev != NULL" */
+ long long expires;
+
+ /* The function to be called when the timer expires. If the
+ function returns a non-zero value, the timer is put back on the
+ list. */
+ int (*fnc) (void *);
+ void *fnc_data;
+};
+
+/* Initialize the timer TIMER. */
+void timer_clear (struct timer_list *timer);
+
+/* Add the timer TIMER to the list. */
+void timer_add (struct timer_list *timer);
+
+/* Remove the timer TIMER from the list. */
+int timer_remove (struct timer_list *timer);
+
+/* Change the expiration time of the timer TIMER to EXPIRES. */
+void timer_change (struct timer_list *timer, long long expires);
+
+extern inline long long
+fetch_jiffies ()
+{
+ extern volatile struct mapped_time_value *timer_mapped_time;
+ extern long long timer_root_jiffies;
+ struct timeval tv;
+ long long j;
+
+ maptime_read (timer_mapped_time, &tv);
+
+#define HZ 100
+ j = (long long) tv.tv_sec * HZ + ((long long) tv.tv_usec * HZ) / 1000000;
+ return j - timer_root_jiffies;
+}
+
+#endif /* _TIMER_H_ */
diff --git a/console-client/trans.c b/console-client/trans.c
new file mode 100644
index 00000000..67cd149d
--- /dev/null
+++ b/console-client/trans.c
@@ -0,0 +1,931 @@
+/* trans.c -- Control a translator node for the repeaters.
+
+ Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+
+ Written by Marco Gerards.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <fcntl.h>
+#include <maptime.h>
+#include <stddef.h>
+#include <dirent.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <hurd/hurd_types.h>
+#include <error.h>
+#include <version.h>
+#include <stdio.h>
+
+#include "trans.h"
+#include "libnetfs/io_S.h"
+
+
+char *netfs_server_name = "console";
+char *netfs_server_version = HURD_VERSION;
+int netfs_maxsymlinks = 0;
+
+/* Handy source of time. */
+static volatile struct mapped_time_value *console_maptime;
+
+static consnode_t node_list = 0;
+
+struct netnode
+{
+ consnode_t node;
+ char *symlink_path;
+};
+
+typedef mach_msg_header_t request_t;
+
+
+int
+console_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ int ret;
+ struct protid *user = (struct protid *) inp;
+ request_t *inop = (request_t *) inp;
+
+ ret = netfs_demuxer (inp, outp);
+ if (ret)
+ return ret;
+
+ user = ports_lookup_port (netfs_port_bucket, inop->msgh_local_port, netfs_protid_class);
+ if (!user)
+ return ret;
+
+ /* Don't do anything for the root node. */
+ if (user->po->np == netfs_root_node)
+ {
+ ports_port_deref (user);
+ return 0;
+ }
+
+ if (!ret && user->po->np->nn->node && user->po->np->nn->node->demuxer)
+ ret = user->po->np->nn->node->demuxer (inp, outp);
+
+ ports_port_deref (user);
+ return ret;
+}
+
+
+
+/* Make sure that NP->nn_stat is filled with current information. CRED
+ identifies the user responsible for the operation. */
+error_t
+netfs_validate_stat (struct node *np, struct iouser *cred)
+{
+ return 0;
+}
+
+
+/* This should attempt a chmod call for the user specified by CRED on node
+ NODE, to change the owner to UID and the group to GID. */
+error_t
+netfs_attempt_chown (struct iouser *cred, struct node *np,
+ uid_t uid, uid_t gid)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* This should attempt a chauthor call for the user specified by CRED on node
+ NODE, to change the author to AUTHOR. */
+error_t
+netfs_attempt_chauthor (struct iouser *cred, struct node *np,
+ uid_t author)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* This should attempt a chmod call for the user specified by CRED on node
+ NODE, to change the mode to MODE. Unlike the normal Unix and Hurd meaning
+ of chmod, this function is also used to attempt to change files into other
+ types. If such a transition is attempted which is impossible, then return
+ EOPNOTSUPP. */
+error_t
+netfs_attempt_chmod (struct iouser *cred, struct node *np,
+ mode_t mode)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* Attempt to turn NODE (user CRED) into a symlink with target NAME. */
+error_t
+netfs_attempt_mksymlink (struct iouser *cred, struct node *np,
+ char *name)
+{
+ if (!np->nn->node)
+ {
+ if (np->nn->symlink_path)
+ free (np->nn->symlink_path);
+ np->nn->symlink_path = strdup (name);
+ return 0;
+ }
+ else if (np->nn->node->mksymlink)
+ return np->nn->node->mksymlink (cred, np, name);
+ return EOPNOTSUPP;
+}
+
+
+/* Attempt to turn NODE (user CRED) into a device. TYPE is either S_IFBLK or
+ S_IFCHR. */
+error_t
+netfs_attempt_mkdev (struct iouser *cred, struct node *np,
+ mode_t type, dev_t indexes)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* This should attempt a chflags call for the user specified by CRED on node
+ NODE, to change the flags to FLAGS. */
+error_t
+netfs_attempt_chflags (struct iouser *cred, struct node *np,
+ int flags)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* This should attempt a utimes call for the user specified by CRED on
+ locked node NP, to change the atime to ATIME and the mtime to
+ MTIME. If ATIME or MTIME is null, then set to the current
+ time. */
+error_t
+netfs_attempt_utimes (struct iouser *cred, struct node *np,
+ struct timespec *atime, struct timespec *mtime)
+{
+ error_t err = fshelp_isowner (&np->nn_stat, cred);
+ int flags = TOUCH_CTIME;
+
+ if (! err)
+ {
+ if (mtime)
+ np->nn_stat.st_mtim = *mtime;
+ else
+ flags |= TOUCH_MTIME;
+
+ if (atime)
+ np->nn_stat.st_atim = *atime;
+ else
+ flags |= TOUCH_ATIME;
+
+ fshelp_touch (&np->nn_stat, flags, console_maptime);
+ }
+ return err;
+
+}
+
+
+/* This should attempt to set the size of the locked file NP (for user
+ CRED) to SIZE bytes long. */
+error_t
+netfs_attempt_set_size (struct iouser *cred, struct node *np,
+ loff_t size)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should attempt to fetch filesystem status information for the
+ remote filesystem, for the user CRED. NP is locked. */
+error_t
+netfs_attempt_statfs (struct iouser *cred, struct node *np,
+ fsys_statfsbuf_t *st)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* This should sync the locked file NP completely to disk, for the
+ user CRED. If WAIT is set, return only after the sync is
+ completely finished. */
+error_t
+netfs_attempt_sync (struct iouser *cred, struct node *np,
+ int wait)
+{
+ return 0;
+}
+
+
+/* This should sync the entire remote filesystem. If WAIT is set,
+ return only after the sync is completely finished. */
+error_t
+netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+ return 0;
+}
+
+
+/* Lookup NAME in DIR (which is locked) for USER; set *NP to the found
+ name upon return. If the name was not found, then return ENOENT.
+ On any error, clear *NP. (*NP, if found, should be locked and a
+ reference to it generated. This call should unlock DIR no matter
+ what.) */
+error_t
+netfs_attempt_lookup (struct iouser *user, struct node *dir,
+ char *name, struct node **node)
+{
+ error_t err;
+ consnode_t cn;
+
+ *node = 0;
+ err = fshelp_access (&dir->nn_stat, S_IEXEC, user);
+ if (err)
+ goto out;
+
+ if (strcmp (name, ".") == 0)
+ {
+ /* Current directory -- just add an additional reference to DIR
+ and return it. */
+ netfs_nref (dir);
+ *node = dir;
+ goto out;
+ }
+
+ if (strcmp (name, "..") == 0)
+ {
+ err = EAGAIN;
+ goto out;
+ }
+
+ for (cn = node_list; cn; cn = cn->next)
+ if (!strcmp (name, cn->name))
+ {
+ if (cn->node == NULL)
+ {
+ struct netnode *nn;
+ ssize_t size = 0;
+
+ if (cn->readlink)
+ {
+ size = cn->readlink (user, NULL, NULL);
+ if (size < 0)
+ {
+ err = -size;
+ goto out;
+ }
+ }
+
+ nn = calloc (1, sizeof *nn);
+ if (nn == NULL)
+ {
+ err = ENOMEM;
+ goto out;
+ }
+
+ *node = netfs_make_node (nn);
+
+ nn->node = cn;
+ (*node)->nn_stat = netfs_root_node->nn_stat;
+ (*node)->nn_stat.st_mode = (netfs_root_node->nn_stat.st_mode & ~S_IFMT & ~S_ITRANS);
+ (*node)->nn_stat.st_ino = 5;
+ if (cn->readlink)
+ (*node)->nn_stat.st_mode |= S_IFLNK;
+ else
+ (*node)->nn_stat.st_mode |= S_IFCHR;
+ (*node)->nn_stat.st_size = size;
+ cn->node = *node;
+ goto out;
+ }
+ else
+ {
+ *node = cn->node;
+
+ netfs_nref (*node);
+ goto out;
+ }
+ }
+
+ err = ENOENT;
+
+ out:
+ pthread_mutex_unlock (&dir->lock);
+ if (err)
+ *node = 0;
+ else
+ pthread_mutex_lock (&(*node)->lock);
+
+ if (!err && *node != dir && (*node)->nn->node->open)
+ (*node)->nn->node->open ();
+
+ return err;
+}
+
+
+error_t
+netfs_S_io_seek (struct protid *user, off_t offset,
+ int whence, off_t *newoffset)
+{
+ /* XXX: Will all nodes be device nodes? */
+ if (!user)
+ return EOPNOTSUPP;
+ else
+ return ESPIPE;
+}
+
+
+static error_t
+io_select_common (struct protid *user, mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ struct timespec *tsp, int *type)
+{
+ struct node *np;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ np = user->po->np;
+
+ if (np->nn->node && np->nn->node->select)
+ return np->nn->node->select (user, reply, replytype, tsp, type);
+ return EOPNOTSUPP;
+}
+
+
+error_t
+netfs_S_io_select (struct protid *user, mach_port_t reply,
+ mach_msg_type_name_t replytype, int *type)
+{
+ return io_select_common (user, reply, replytype, NULL, type);
+}
+
+
+error_t
+netfs_S_io_select_timeout (struct protid *user, mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ struct timespec ts, int *type)
+{
+ return io_select_common (user, reply, replytype, &ts, type);
+}
+
+
+/* Delete NAME in DIR (which is locked) for USER. */
+error_t
+netfs_attempt_unlink (struct iouser *user, struct node *dir,
+ char *name)
+{
+ error_t err;
+ consnode_t cn;
+
+ err = fshelp_access (&dir->nn_stat, S_IWRITE, user);
+ if (err)
+ return err;
+
+ for (cn = node_list; cn; cn = cn->next)
+ if (!strcmp (name, cn->name))
+ {
+ if (cn->mksymlink)
+ return 0;
+ else
+ break;
+ }
+ return EOPNOTSUPP;
+}
+
+
+/* Attempt to rename the directory FROMDIR to TODIR. Note that neither
+ of the specific nodes are locked. */
+error_t
+netfs_attempt_rename (struct iouser *user, struct node *fromdir,
+ char *fromname, struct node *todir,
+ char *toname, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* Attempt to create a new directory named NAME in DIR (which is
+ locked) for USER with mode MODE. */
+error_t
+netfs_attempt_mkdir (struct iouser *user, struct node *dir,
+ char *name, mode_t mode)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* Attempt to remove directory named NAME in DIR (which is locked) for
+ USER. */
+error_t
+netfs_attempt_rmdir (struct iouser *user,
+ struct node *dir, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* Create a link in DIR with name NAME to FILE for USER. Note that
+ neither DIR nor FILE are locked. If EXCL is set, do not delete the
+ target. Return EEXIST if NAME is already found in DIR. */
+error_t
+netfs_attempt_link (struct iouser *user, struct node *dir,
+ struct node *file, char *name, int excl)
+{
+ error_t err;
+ consnode_t cn;
+
+ err = fshelp_access (&dir->nn_stat, S_IWRITE, user);
+ if (err)
+ return err;
+
+ if (!file->nn->node && file->nn->symlink_path)
+ {
+ for (cn = node_list; cn; cn = cn->next)
+ if (!strcmp (name, cn->name))
+ {
+ if (cn->mksymlink)
+ {
+ file->nn->node = cn;
+ cn->mksymlink (user, file, file->nn->symlink_path);
+ free (file->nn->symlink_path);
+ file->nn->symlink_path = NULL;
+ return 0;
+ }
+ else
+ break;
+ }
+ }
+ return EOPNOTSUPP;
+}
+
+
+/* Attempt to create an anonymous file related to DIR (which is
+ locked) for USER with MODE. Set *NP to the returned file upon
+ success. No matter what, unlock DIR. */
+error_t
+netfs_attempt_mkfile (struct iouser *user, struct node *dir,
+ mode_t mode, struct node **np)
+{
+ error_t err;
+ struct netnode *nn;
+
+ err = fshelp_access (&dir->nn_stat, S_IWRITE, user);
+ if (err)
+ {
+ *np = 0;
+ return err;
+ }
+
+ pthread_mutex_unlock (&dir->lock);
+
+ nn = calloc (1, sizeof (*nn));
+ if (!nn)
+ return ENOMEM;
+
+ *np = netfs_make_node (nn);
+ pthread_mutex_lock (&(*np)->lock);
+
+ return 0;
+}
+
+
+/* Attempt to create a file named NAME in DIR (which is locked) for
+ USER with MODE. Set *NP to the new node upon return. On any
+ error, clear *NP. *NP should be locked on success; no matter what,
+ unlock DIR before returning. */
+error_t
+netfs_attempt_create_file (struct iouser *user, struct node *dir,
+ char *name, mode_t mode, struct node **np)
+{
+ *np = 0;
+ pthread_mutex_unlock (&dir->lock);
+ return EOPNOTSUPP;
+}
+
+
+/* Read the contents of locked node NP (a symlink), for USER, into
+ BUF. */
+error_t
+netfs_attempt_readlink (struct iouser *user, struct node *np,
+ char *buf)
+{
+ if (np->nn->node && np->nn->node->readlink)
+ {
+ error_t err = np->nn->node->readlink (user, np, buf);
+ if (err < 0)
+ return -err;
+ else
+ return 0;
+ }
+ return EOPNOTSUPP;
+}
+
+
+/* Locked node NP is being opened by USER, with FLAGS. NEWNODE is
+ nonzero if we just created this node. Return an error if we should
+ not permit the open to complete because of a permission
+ restriction. */
+error_t
+netfs_check_open_permissions (struct iouser *user, struct node *np,
+ int flags, int newnode)
+{
+ error_t err = 0;
+
+ if (flags & O_READ)
+ err = fshelp_access (&np->nn_stat, S_IREAD, user);
+ if (!err && (flags & O_WRITE))
+ err = fshelp_access (&np->nn_stat, S_IWRITE, user);
+ if (!err && (flags & O_EXEC))
+ err = fshelp_access (&np->nn_stat, S_IEXEC, user);
+ return err;
+
+}
+
+
+/* This function will never be called. It is only used when a node is
+ a symlink or by io_read, which is overridden. */
+error_t
+netfs_attempt_read (struct iouser *cred, struct node *np,
+ loff_t offset, size_t *len, void *data)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* This function will never be called. It is only called from
+ io_write, which is overridden. */
+error_t
+netfs_attempt_write (struct iouser *cred, struct node *np,
+ loff_t offset, size_t *len, void *data)
+{
+ return EOPNOTSUPP;
+}
+
+
+error_t
+netfs_S_io_read (struct protid *user,
+ char **data,
+ mach_msg_type_number_t *datalen,
+ off_t offset,
+ mach_msg_type_number_t amount)
+{
+ struct node *np;
+
+ if (!user)
+ return EOPNOTSUPP;
+ np = user->po->np;
+
+ if (np->nn->node && np->nn->node->read)
+ return np->nn->node->read (user, data, datalen, offset, amount);
+ return EOPNOTSUPP;
+}
+
+
+error_t
+netfs_S_io_write (struct protid *user,
+ char *data,
+ mach_msg_type_number_t datalen,
+ off_t offset,
+ mach_msg_type_number_t *amount)
+{
+ struct node *np;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ np = user->po->np;
+ if (np->nn->node && np->nn->node->write)
+ return np->nn->node->write (user, data, datalen, offset, amount);
+ return EOPNOTSUPP;
+}
+
+
+/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and
+ O_EXEC) in *TYPES for locked file NP and user CRED. */
+error_t
+netfs_report_access (struct iouser *cred, struct node *np,
+ int *types)
+{
+ *types = 0;
+ if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0)
+ *types |= O_READ;
+ if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0)
+ *types |= O_WRITE;
+ if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0)
+ *types |= O_EXEC;
+ return 0;
+
+}
+
+/* Node NP has no more references; free all its associated storage. */
+void netfs_node_norefs (struct node *np)
+
+{
+ if (np->nn->node)
+ {
+ if (np->nn->node->close)
+ np->nn->node->close ();
+ np->nn->node->node = 0;
+ }
+
+ if (np->nn->symlink_path)
+ free (np->nn->symlink_path);
+
+ free (np->nn);
+ free (np);
+}
+
+
+/* Returned directory entries are aligned to blocks this many bytes long.
+ Must be a power of two. */
+#define DIRENT_ALIGN 4
+#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name)
+/* Length is structure before the name + the name + '\0', all
+ padded to a four-byte alignment. */
+#define DIRENT_LEN(name_len) \
+ ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \
+ & ~(DIRENT_ALIGN - 1))
+
+/* Fill the array *DATA of size BUFSIZE with up to NENTRIES dirents
+ from DIR (which is locked) starting with entry ENTRY for user CRED.
+ The number of entries in the array is stored in *AMT and the number
+ of bytes in *DATACNT. If the supplied buffer is not large enough
+ to hold the data, it should be grown. */
+error_t
+netfs_get_dirents (struct iouser *cred, struct node *dir,
+ int first_entry, int num_entries, char **data,
+ mach_msg_type_number_t *data_len,
+ vm_size_t max_data_len, int *data_entries)
+{
+ error_t err;
+ int count = 0;
+ size_t size = 0; /* Total size of our return block. */
+ consnode_t cn = node_list;
+ consnode_t first_node;
+
+
+ /* Add the length of a directory entry for NAME to SIZE and return true,
+ unless it would overflow MAX_DATA_LEN or NUM_ENTRIES, in which case
+ return false. */
+ int bump_size (const char *name)
+ {
+ if (num_entries == -1 || count < num_entries)
+ {
+ size_t new_size = size + DIRENT_LEN (strlen (name));
+ if (max_data_len > 0 && new_size > max_data_len)
+ return 0;
+ size = new_size;
+ count++;
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ if (dir != netfs_root_node)
+ return ENOTDIR;
+
+ for (first_node = node_list, count = 2;
+ first_node && first_entry > count;
+ first_node = first_node->next);
+ count++;
+
+ count = 0;
+
+ /* Make space for the `.' and `..' entries. */
+ if (first_entry == 0)
+ bump_size (".");
+ if (first_entry <= 1)
+ bump_size ("..");
+
+ for (cn = first_node; cn; cn = cn->next)
+ bump_size (cn->name);
+
+
+ /* Allocate it. */
+ *data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ err = ((void *) *data == (void *) -1) ? errno : 0;
+
+ if (! err)
+ /* Copy out the result. */
+ {
+ char *p = *data;
+
+ int add_dir_entry (const char *name, ino_t fileno, int type)
+ {
+ if (num_entries == -1 || count < num_entries)
+ {
+ struct dirent hdr;
+ size_t name_len = strlen (name);
+ size_t sz = DIRENT_LEN (name_len);
+
+ if (sz > size)
+ return 0;
+ else
+ size -= sz;
+
+ hdr.d_fileno = fileno;
+ hdr.d_reclen = sz;
+ hdr.d_type = type;
+ hdr.d_namlen = name_len;
+
+ memcpy (p, &hdr, DIRENT_NAME_OFFS);
+ strcpy (p + DIRENT_NAME_OFFS, name);
+ p += sz;
+
+ count++;
+
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ *data_len = size;
+ *data_entries = count;
+
+ count = 0;
+
+ /* Add `.' and `..' entries. */
+ if (first_entry == 0)
+ add_dir_entry (".", 2, DT_DIR);
+ if (first_entry <= 1)
+ add_dir_entry ("..", 2, DT_DIR);
+
+ /* Fill in the real directory entries. */
+ for (cn = first_node; cn; cn = cn->next)
+ if (!add_dir_entry (cn->name, cn->id, cn->readlink ? DT_LNK : DT_CHR))
+ break;
+ }
+
+ fshelp_touch (&dir->nn_stat, TOUCH_ATIME, console_maptime);
+ return err;
+}
+
+
+
+static void *
+console_client_translator (void *unused)
+{
+ error_t err;
+
+ do
+ {
+ ports_manage_port_operations_multithread (netfs_port_bucket,
+ console_demuxer,
+ 1000 * 60 * 2,
+ 1000 * 60 * 10,
+ 0);
+ err = netfs_shutdown (0);
+ }
+ while (err);
+ return 0;
+}
+
+
+/* Create a node with the name NAME and return it in *CN. */
+error_t
+console_create_consnode (const char *name, consnode_t *cn)
+{
+ *cn = malloc (sizeof (struct consnode));
+ if (!*cn)
+ return ENOMEM;
+
+ (*cn)->name = strdup (name);
+ if (!(*cn)->name)
+ {
+ free (cn);
+ return ENOMEM;
+ }
+
+ (*cn)->readlink = NULL;
+ (*cn)->mksymlink = NULL;
+
+ return 0;
+}
+
+
+/* Destroy the node CN. */
+void
+console_destroy_consnode (consnode_t cn)
+{
+ if (!cn)
+ return;
+ free (cn->name);
+ free (cn);
+}
+
+
+/* Register the node CN with the translator. */
+void
+console_register_consnode (consnode_t cn)
+{
+ cn->node = 0;
+ cn->next = node_list;
+ node_list = cn;
+}
+
+
+/* Unregister the node CN from the translator. */
+void
+console_unregister_consnode (consnode_t cn)
+{
+ if (!cn)
+ return;
+
+ if (node_list == cn)
+ node_list = cn->next;
+ else
+ {
+ consnode_t prev = node_list;
+
+ for (prev = node_list; prev->next != cn; prev = prev->next)
+ ;
+
+ prev->next = cn->next;
+ }
+}
+
+
+error_t
+console_setup_node (char *path)
+{
+ mach_port_t bootstrap;
+ error_t err;
+ struct stat ul_stat;
+ file_t node;
+ struct port_info *newpi;
+ mach_port_t right;
+ pthread_t thread;
+
+ node = file_name_lookup (path, O_CREAT|O_NOTRANS, 0664);
+ if (node == MACH_PORT_NULL)
+ return errno;
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ netfs_init ();
+
+ /* Create the root node (some attributes initialized below). */
+ netfs_root_node = netfs_make_node (0);
+ if (! netfs_root_node)
+ error (1, ENOMEM, "Cannot create root node");
+
+ err = maptime_map (0, 0, &console_maptime);
+ if (err)
+ error (1, err, "Cannot map time");
+
+ err = ports_create_port (netfs_control_class, netfs_port_bucket, sizeof (struct port_info), &newpi);
+ right = ports_get_send_right (newpi);
+ err = file_set_translator (node, 0, FS_TRANS_EXCL | FS_TRANS_SET, 0, 0, 0,
+ right, MACH_MSG_TYPE_COPY_SEND);
+ mach_port_deallocate (mach_task_self (), right);
+
+ err = io_stat (node, &ul_stat);
+ if (err)
+ error (1, err, "Cannot stat underlying node");
+
+ netfs_root_node->nn_stat.st_ino = 2;
+ netfs_root_node->nn_stat.st_uid = ul_stat.st_uid;
+ netfs_root_node->nn_stat.st_gid = ul_stat.st_gid;
+ netfs_root_node->nn_stat.st_author = ul_stat.st_author;
+ netfs_root_node->nn_stat.st_mode = S_IFDIR | (ul_stat.st_mode & ~S_IFMT & ~S_ITRANS);
+ netfs_root_node->nn_stat.st_fsid = getpid ();
+ netfs_root_node->nn_stat.st_nlink = 1;
+ netfs_root_node->nn_stat.st_size = 0;
+ netfs_root_node->nn_stat.st_blocks = 0;
+ netfs_root_node->nn_stat.st_fstype = FSTYPE_MISC;
+ netfs_root_node->nn_translated = 0;
+
+ /* If the underlying node isn't a directory, propagate read permission to
+ execute permission since we need that for lookups. */
+ if (! S_ISDIR (ul_stat.st_mode))
+ {
+ if (ul_stat.st_mode & S_IRUSR)
+ netfs_root_node->nn_stat.st_mode |= S_IXUSR;
+ if (ul_stat.st_mode & S_IRGRP)
+ netfs_root_node->nn_stat.st_mode |= S_IXGRP;
+ if (ul_stat.st_mode & S_IROTH)
+ netfs_root_node->nn_stat.st_mode |= S_IXOTH;
+ }
+
+ fshelp_touch (&netfs_root_node->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
+ console_maptime);
+
+ err = pthread_create (&thread, NULL, console_client_translator, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+
+ return 0;
+}
diff --git a/console-client/trans.h b/console-client/trans.h
new file mode 100644
index 00000000..9891cf3f
--- /dev/null
+++ b/console-client/trans.h
@@ -0,0 +1,82 @@
+/* trans.h -- Control a translator node for the repeaters.
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+ Written by Marco Gerards.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <hurd/netfs.h>
+
+struct consnode
+{
+ /* The filename of the node. */
+ char *name;
+
+ /* The id of the node. */
+ int id;
+
+ /* Cached if the node is already opened. */
+ struct node *node;
+
+ /* Read data from a node. This is exactly the same as io_read
+ does. */
+ error_t (*read) (struct protid *user, char **data,
+ mach_msg_type_number_t *datalen, off_t offset,
+ mach_msg_type_number_t amount);
+
+ /* Read data to a node. This is exactly the same as io_write
+ does. */
+ error_t (*write) (struct protid *user, char *data,
+ mach_msg_type_number_t datalen, off_t offset,
+ mach_msg_type_number_t *amount);
+
+ /* This is exactly the same as io_select{,_timeout} do. */
+ error_t (*select) (struct protid *user, mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ struct timespec *tsp, int *type);
+
+ /* Called when the node is opened. */
+ void (*open) (void);
+
+ /* Called when the node is closed. */
+ void (*close) (void);
+
+ /* The demuxer used for this node. */
+ int (*demuxer) (mach_msg_header_t *inp, mach_msg_header_t *outp);
+
+ /* Called when the symlink is read */
+ error_t (*readlink) (struct iouser *user, struct node *np, char *buf);
+
+ /* Called when the symlink is written */
+ error_t (*mksymlink) (struct iouser *cred, struct node *np, char *name);
+
+ struct consnode *next;
+};
+
+typedef struct consnode *consnode_t;
+
+/* Register the node CN with the translator. */
+void console_register_consnode (consnode_t cn);
+
+/* Unregister the node CN from the translator. */
+void console_unregister_consnode (consnode_t cn);
+
+/* Create a node with the name NAME and return it in *CN. */
+error_t console_create_consnode (const char *name, consnode_t *cn);
+
+/* Destroy the node CN. */
+void console_destroy_consnode (consnode_t cn);
+
+/* Setup the translator for console client nodes on PATH. */
+error_t console_setup_node (char *path);
diff --git a/console-client/unicode.h b/console-client/unicode.h
new file mode 100644
index 00000000..a3991748
--- /dev/null
+++ b/console-client/unicode.h
@@ -0,0 +1,349 @@
+/* unicode.h - A list of useful Unicode characters.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef _UNICODE_H_
+#define _UNICODE_H_
+
+#define UNICODE_NO_BREAK_SPACE ((wchar_t) 0x00a0)
+#define UNICODE_INVERTED_EXCLAMATION_MARK ((wchar_t) 0x00a1)
+#define UNICODE_CENT_SIGN ((wchar_t) 0x00a2)
+#define UNICODE_POUND_SIGN ((wchar_t) 0x00a3)
+#define UNICODE_CURRENCY_SIGN ((wchar_t) 0x00a4)
+#define UNICODE_YEN_SIGN ((wchar_t) 0x00a5)
+#define UNICODE_BROKEN_BAR ((wchar_t) 0x00a6)
+#define UNICODE_BROKEN_VERTICAL_BAR UNICODE_BROKEN_BAR
+#define UNICODE_SECTION_SIGN ((wchar_t) 0x00a7)
+#define UNICODE_DIARESIS ((wchar_t) 0x00a8)
+#define UNICODE_COPYRIGHT_SIGN ((wchar_t) 0x00a9)
+#define UNICODE_FEMININE_ORDINAL_INDICATOR ((wchar_t) 0x00aa)
+#define UNICODE_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK ((wchar_t) 0x00ab)
+#define UNICODE_LEFT_POINTING_GUILLEMET \
+ UNICODE_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK
+#define UNICODE_NOT_SIGN ((wchar_t) 0x00ac)
+#define UNICODE_SOFT_HYPHEN ((wchar_t) 0x00ad)
+#define UNICODE_REGISTERED_SIGN ((wchar_t) 0x00ae)
+#define UNICODE_REGISTERED_TRADE_MARK_SIGN UNICODE_REGISTERED_SIGN
+#define UNICODE_MACRON ((wchar_t) 0x00af)
+#define UNICODE_DEGREE_SIGN ((wchar_t) 0x00b0)
+#define UNICODE_PLUS_MINUS_SIGN ((wchar_t) 0x00b1)
+#define UNICODE_SUPERSCRIPT_TWO ((wchar_t) 0x00b2)
+#define UNICODE_SUPERSCRIPT_THREE ((wchar_t) 0x00b3)
+#define UNICODE_ACUTE_ACCENT ((wchar_t) 0x00b4)
+#define UNICODE_MICRO_SIGN ((wchar_t) 0x00b5)
+#define UNICODE_PILCROW_SIGN ((wchar_t) 0x00b6)
+#define UNICODE_PARAGRAPH_SIGN UNICODE_PILCROW_SIGN
+#define UNICODE_MIDDLE_DOT ((wchar_t) 0x00b7)
+#define UNICODE_CEDILLA ((wchar_t) 0x00b8)
+#define UNICODE_SUPERSCRIPT_ONE ((wchar_t) 0x00b9)
+#define UNICODE_MASCULINE_ORDINAL_INDICATOR ((wchar_t) 0x00ba)
+#define UNICODE_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK ((wchar_t) 0x00bb)
+#define UNICODE_RIGHT_POINTING_GUILLEMET \
+ UNICODE_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK
+#define UNICODE_VULGAR_FRACTION_ONE_QUARTER ((wchar_t) 0x00bc)
+#define UNICODE_VULGAR_FRACTION_ONE_HALF ((wchar_t) 0x00bd)
+#define UNICODE_VULGAR_FRACTION_THREE_QUARTERS ((wchar_t) 0x00be)
+#define UNICODE_INVERTED_QUESTION_MARK ((wchar_t) 0x00bf)
+#define UNICODE_LATIN_CAPITAL_LETTER_A_WITH_GRAVE ((wchar_t) 0x00c0)
+#define UNICODE_LATIN_CAPITAL_LETTER_A_WITH_ACUTE ((wchar_t) 0x00c1)
+#define UNICODE_LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX ((wchar_t) 0x00c2)
+#define UNICODE_LATIN_CAPITAL_LETTER_A_WITH_TILDE ((wchar_t) 0x00c3)
+#define UNICODE_LATIN_CAPITAL_LETTER_A_WITH_DIARESIS ((wchar_t) 0x00c4)
+#define UNICODE_LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE ((wchar_t) 0x00c5)
+#define UNICODE_LATIN_CAPITAL_LETTER_AE ((wchar_t) 0x00c6)
+#define UNICODE_LATIN_CAPITAL_LETTER_C_WITH_CEDILLA ((wchar_t) 0x00c7)
+#define UNICODE_LATIN_CAPITAL_LIGATURE_AE UNICODE_LATIN_CAPITAL_LETTER_AE
+#define UNICODE_LATIN_CAPITAL_LETTER_E_WITH_GRAVE ((wchar_t) 0x00c8)
+#define UNICODE_LATIN_CAPITAL_LETTER_E_WITH_ACUTE ((wchar_t) 0x00c9)
+#define UNICODE_LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX ((wchar_t) 0x00ca)
+#define UNICODE_LATIN_CAPITAL_LETTER_E_WITH_DIARESIS ((wchar_t) 0x00cb)
+#define UNICODE_LATIN_CAPITAL_LETTER_I_WITH_GRAVE ((wchar_t) 0x00cc)
+#define UNICODE_LATIN_CAPITAL_LETTER_I_WITH_ACUTE ((wchar_t) 0x00cd)
+#define UNICODE_LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX ((wchar_t) 0x00ce)
+#define UNICODE_LATIN_CAPITAL_LETTER_I_WITH_DIARESIS ((wchar_t) 0x00cf)
+#define UNICODE_LATIN_CAPITAL_LETTER_ETH ((wchar_t) 0x00d0)
+#define UNICODE_LATIN_CAPITAL_LETTER_N_WITH_TILDE ((wchar_t) 0x00d1)
+#define UNICODE_LATIN_CAPITAL_LETTER_O_WITH_GRAVE ((wchar_t) 0x00d2)
+#define UNICODE_LATIN_CAPITAL_LETTER_O_WITH_ACUTE ((wchar_t) 0x00d3)
+#define UNICODE_LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX ((wchar_t) 0x00d4)
+#define UNICODE_LATIN_CAPITAL_LETTER_O_WITH_TILDE ((wchar_t) 0x00d5)
+#define UNICODE_LATIN_CAPITAL_LETTER_O_WITH_DIARESIS ((wchar_t) 0x00d6)
+#define UNICODE_MULTIPLICATION_SIGN ((wchar_t) 0x00d7)
+#define UNICODE_CAPITAL_LETTER_O_WITH_STROKE ((wchar_t) 0x00d8)
+#define UNICODE_LATIN_CAPITAL_LETTER_U_WITH_GRAVE ((wchar_t) 0x00d9)
+#define UNICODE_LATIN_CAPITAL_LETTER_U_WITH_ACUTE ((wchar_t) 0x00da)
+#define UNICODE_LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX ((wchar_t) 0x00db)
+#define UNICODE_LATIN_CAPITAL_LETTER_U_WITH_DIARESIS ((wchar_t) 0x00dc)
+#define UNICODE_LATIN_CAPITAL_LETTER_Y_WITH_ACUTE ((wchar_t) 0x00dd)
+#define UNICODE_LATIN_CAPITAL_LETTER_THORN ((wchar_t) 0x00de)
+#define UNICODE_LATIN_SMALL_LETTER_SHARP_S ((wchar_t) 0x00df)
+#define UNICODE_LATIN_SMALL_LETTER_A_WITH_GRAVE ((wchar_t) 0x00e0)
+#define UNICODE_LATIN_SMALL_LETTER_A_WITH_ACUTE ((wchar_t) 0x00e1)
+#define UNICODE_LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX ((wchar_t) 0x00e2)
+#define UNICODE_LATIN_SMALL_LETTER_A_WITH_DIARESIS ((wchar_t) 0x00e4)
+#define UNICODE_LATIN_SMALL_LETTER_A_WITH_RING_ABOVE ((wchar_t) 0x00e5)
+#define UNICODE_LATIN_SMALL_LETTER_AE ((wchar_t) 0x00e6)
+#define UNICODE_LATIN_SMALL_LIGATURE_AE UNICODE_LATIN_SMALL_LETTER_AE
+#define UNICODE_LATIN_SMALL_LETTER_C_WITH_CEDILLA ((wchar_t) 0x00e7)
+#define UNICODE_LATIN_SMALL_LETTER_E_WITH_GRAVE ((wchar_t) 0x00e8)
+#define UNICODE_LATIN_SMALL_LETTER_E_WITH_ACUTE ((wchar_t) 0x00e9)
+#define UNICODE_LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX ((wchar_t) 0x00ea)
+#define UNICODE_LATIN_SMALL_LETTER_E_WITH_DIARESIS ((wchar_t) 0x00eb)
+#define UNICODE_LATIN_SMALL_LETTER_I_WITH_GRAVE ((wchar_t) 0x00ec)
+#define UNICODE_LATIN_SMALL_LETTER_I_WITH_ACUTE ((wchar_t) 0x00ed)
+#define UNICODE_LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX ((wchar_t) 0x00ee)
+#define UNICODE_LATIN_SMALL_LETTER_I_WITH_DIARESIS ((wchar_t) 0x00ef)
+#define UNICODE_LATIN_SMALL_LETTER_ETH ((wchar_t) 0x00f0)
+#define UNICODE_LATIN_SMALL_LETTER_N_WITH_TILDE ((wchar_t) 0x00f1)
+#define UNICODE_LATIN_SMALL_LETTER_O_WITH_GRAVE ((wchar_t) 0x00f2)
+#define UNICODE_LATIN_SMALL_LETTER_O_WITH_ACUTE ((wchar_t) 0x00f3)
+#define UNICODE_LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX ((wchar_t) 0x00f4)
+#define UNICODE_LATIN_SMALL_LETTER_O_WITH_DIARESIS ((wchar_t) 0x00f6)
+#define UNICODE_DIVISION_SIGN ((wchar_t) 0x00f7)
+#define UNICODE_SMALL_LETTER_O_WITH_STROKE ((wchar_t) 0x00f8)
+#define UNICODE_LATIN_SMALL_LETTER_U_WITH_GRAVE ((wchar_t) 0x00f9)
+#define UNICODE_LATIN_SMALL_LETTER_U_WITH_ACUTE ((wchar_t) 0x00fa)
+#define UNICODE_LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX ((wchar_t) 0x00fb)
+#define UNICODE_LATIN_SMALL_LETTER_U_WITH_DIARESIS ((wchar_t) 0x00fc)
+#define UNICODE_LATIN_SMALL_LETTER_Y_WITH_ACUTE ((wchar_t) 0x00fd)
+#define UNICODE_LATIN_SMALL_LETTER_THORN ((wchar_t) 0x00fe)
+#define UNICODE_LATIN_SMALL_LETTER_Y_WITH_DIARESIS ((wchar_t) 0x00ff)
+
+#define UNICODE_LATIN_SMALL_LETTER_F_WITH_HOOK ((wchar_t) 0x0192)
+#define UNICODE_LATIN_SMALL_LETTER_SCRIPT_F \
+ UNICODE_LATIN_SMALL_LETTER_F_WITH_HOOK
+#define UNICODE_GREEK_CAPITAL_LETTER_GAMMA ((wchar_t) 0x0393)
+#define UNICODE_GREEK_CAPITAL_LETTER_OMICRON ((wchar_t) 0x039f)
+#define UNICODE_GREEK_CAPITAL_LETTER_SIGMA ((wchar_t) 0x03a3)
+#define UNICODE_GREEK_CAPITAL_LETTER_PHI ((wchar_t) 0x03a6)
+#define UNICODE_GREEK_CAPITAL_LETTER_OMEGA ((wchar_t) 0x03a9)
+#define UNICODE_GREEK_SMALL_LETTER_ALPHA ((wchar_t) 0x03b1)
+#define UNICODE_GREEK_SMALL_LETTER_BETA ((wchar_t) 0x03b2)
+#define UNICODE_GREEK_SMALL_LETTER_DELTA ((wchar_t) 0x03b4)
+#define UNICODE_GREEK_SMALL_LETTER_EPSILON ((wchar_t) 0x03b5)
+#define UNICODE_GREEK_SMALL_LETTER_MU ((wchar_t) 0x03bc)
+#define UNICODE_GREEK_SMALL_LETTER_PI ((wchar_t) 0x03c0)
+#define UNICODE_GREEK_SMALL_LETTER_SIGMA ((wchar_t) 0x03c3)
+#define UNICODE_GREEK_SMALL_LETTER_TAU ((wchar_t) 0x03c4)
+#define UNICODE_GREEK_SMALL_LETTER_PHI ((wchar_t) 0x03c6)
+
+#define UNICODE_BULLET ((wchar_t) 0x2022)
+#define UNICODE_DOUBLE_EXCLAMATION_MARK ((wchar_t) 0x203c)
+#define UNICODE_SUPERSCRIPT_LATIN_SMALL_LETTER ((wchar_t) 0x207f)
+#define UNICODE_PESETA_SIGN ((wchar_t) 0x20a7)
+
+#define UNICODE_LEFTWARDS_ARROW ((wchar_t) 0x2190)
+#define UNICODE_UPWARDS_ARROW ((wchar_t) 0x2191)
+#define UNICODE_RIGHTWARDS_ARROW ((wchar_t) 0x2192)
+#define UNICODE_DOWNWARDS_ARROW ((wchar_t) 0x2193)
+#define UNICODE_LEFT_RIGHT_ARROW ((wchar_t) 0x2194)
+#define UNICODE_UP_DOWN_ARROW ((wchar_t) 0x2195)
+#define UNICODE_UP_DOWN_ARROW_WITH_BASE ((wchar_t) 0x21a8)
+
+#define UNICODE_BULLET_OPERATOR ((wchar_t) 0x2219)
+#define UNICODE_SQUARE_ROOT ((wchar_t) 0x221a)
+#define UNICODE_INFINITY ((wchar_t) 0x221e)
+#define UNICODE_RIGHT_ANGLE ((wchar_t) 0x221f)
+#define UNICODE_INTERSECTION ((wchar_t) 0x2229)
+#define UNICODE_ALMOST_EQUAL_TO ((wchar_t) 0x2248)
+#define UNICODE_NOT_EQUAL_TO ((wchar_t) 0x2260)
+#define UNICODE_IDENTICAL_TO ((wchar_t) 0x2261)
+#define UNICODE_LESS_THAN_OR_EQUAL_TO ((wchar_t) 0x2264)
+#define UNICODE_GREATER_THAN_OR_EQUAL_TO ((wchar_t) 0x2265)
+
+#define UNICODE_HOUSE ((wchar_t) 0x2302)
+#define UNICODE_REVERSED_NOT_SIGN ((wchar_t) 0x2310)
+#define UNICODE_TOP_HALF_INTEGRAL ((wchar_t) 0x2320)
+#define UNICODE_BOTTOM_HALF_INTEGRAL ((wchar_t) 0x2321)
+
+#define UNICODE_BOX_DRAWINGS_LIGHT_HORIZONTAL ((wchar_t) 0x2500)
+#define UNICODE_BOX_DRAWINGS_HEAVY_HORIZONTAL ((wchar_t) 0x2501)
+#define UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL ((wchar_t) 0x2502)
+#define UNICODE_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT ((wchar_t) 0x250c)
+#define UNICODE_BOX_DRAWINGS_DOWN_LIGHT_AND_RIGHT_HEAVY ((wchar_t) 0x250d)
+#define UNICODE_BOX_DRAWINGS_DOWN_HEAVY_AND_RIGHT_LIGHT ((wchar_t) 0x250e)
+#define UNICODE_BOX_DRAWINGS_HEAVY_DOWN_AND_RIGHT ((wchar_t) 0x250f)
+#define UNICODE_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT ((wchar_t) 0x2510)
+#define UNICODE_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT ((wchar_t) 0x2514)
+#define UNICODE_BOX_DRAWINGS_UP_LIGHT_AND_RIGHT_HEAVY ((wchar_t) 0x2515)
+#define UNICODE_BOX_DRAWINGS_UP_HEAVY_AND_RIGHT_LIGHT ((wchar_t) 0x2516)
+#define UNICODE_BOX_DRAWINGS_HEAVY_UP_AND_RIGHT ((wchar_t) 0x2517)
+#define UNICODE_BOX_DRAWINGS_LIGHT_UP_AND_LEFT ((wchar_t) 0x2518)
+#define UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_RIGHT ((wchar_t) 0x251c)
+#define UNICODE_BOX_DRAWINGS_VERTICAL_LIGHT_AND_RIGHT_HEAVY ((wchar_t) 0x251d)
+#define UNICODE_BOX_DRAWINGS_UP_HEAVY_AND_RIGHT_UP_LIGHT ((wchar_t) 0x251e)
+#define UNICODE_BOX_DRAWINGS_DOWN_HEAVY_AND_RIGHT_UP_LIGHT ((wchar_t) 0x251f)
+#define UNICODE_BOX_DRAWINGS_VERTICAL_HEAVY_AND_RIGHT_LIGHT ((wchar_t) 0x2520)
+#define UNICODE_BOX_DRAWINGS_DOWN_LIGHT_AND_RIGHT_UP_HEAVY ((wchar_t) 0x2521)
+#define UNICODE_BOX_DRAWINGS_UP_LIGHT_AND_RIGHT_DOWN_HEAVY ((wchar_t) 0x2522)
+#define UNICODE_BOX_DRAWINGS_HEAVY_VERTICAL_AND_RIGHT ((wchar_t) 0x2523)
+#define UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_LEFT ((wchar_t) 0x2524)
+#define UNICODE_BOX_DRAWINGS_LIGHT_DOWN_AND_HORIZONTAL ((wchar_t) 0x252c)
+#define UNICODE_BOX_DRAWINGS_LEFT_HEAVY_AND_RIGHT_DOWN_LIGHT ((wchar_t) 0x252d)
+#define UNICODE_BOX_DRAWINGS_RIGHT_HEAVY_AND_LEFT_DOWN_LIGHT ((wchar_t) 0x252e)
+#define UNICODE_BOX_DRAWINGS_DOWN_LIGHT_AND_HORIZONTAL_HEAVY ((wchar_t) 0x252f)
+#define UNICODE_BOX_DRAWINGS_DOWN_HEAVY_AND_HORIZONTAL_LIGHT ((wchar_t) 0x2530)
+#define UNICODE_BOX_DRAWINGS_RIGHT_LIGHT_AND_LEFT_DOWN_HEAVY ((wchar_t) 0x2531)
+#define UNICODE_BOX_DRAWINGS_LEFT_LIGHT_AND_RIGHT_DOWN_HEAVY ((wchar_t) 0x2532)
+#define UNICODE_BOX_DRAWINGS_HEAVY_DOWN_AND_HORIZONTAL ((wchar_t) 0x2533)
+#define UNICODE_BOX_DRAWINGS_LIGHT_UP_AND_HORIZONTAL ((wchar_t) 0x2534)
+#define UNICODE_BOX_DRAWINGS_LEFT_HEAVY_AND_RIGHT_UP_LIGHT ((wchar_t) 0x2535)
+#define UNICODE_BOX_DRAWINGS_RIGHT_HEAVY_AND_LEFT_UP_LIGHT ((wchar_t) 0x2536)
+#define UNICODE_BOX_DRAWINGS_UP_LIGHT_AND_HORIZONTAL_HEAVY ((wchar_t) 0x2537)
+#define UNICODE_BOX_DRAWINGS_UP_HEAVY_AND_HORIZONTAL_LIGHT ((wchar_t) 0x2538)
+#define UNICODE_BOX_DRAWINGS_RIGHT_LIGHT_AND_LEFT_UP_HEAVY ((wchar_t) 0x2539)
+#define UNICODE_BOX_DRAWINGS_LEFT_LIGHT_AND_RIGHT_UP_HEAVY ((wchar_t) 0x253a)
+#define UNICODE_BOX_DRAWINGS_HEAVY_UP_AND_HORIZONTAL ((wchar_t) 0x253b)
+#define UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_HORIZONTAL ((wchar_t) 0x253c)
+#define UNICODE_BOX_DRAWINGS_LEFT_HEAVY_AND_RIGHT_VERTICAL_LIGHT \
+ ((wchar_t) 0x253d)
+#define UNICODE_BOX_DRAWINGS_RIGHT_HEAVY_AND_LEFT_VERTICAL_LIGHT \
+ ((wchar_t) 0x253e)
+#define UNICODE_BOX_DRAWINGS_VERTICAL_LIGHT_AND_HORIZONTAL_HEAVY \
+ ((wchar_t) 0x253f)
+#define UNICODE_BOX_DRAWINGS_UP_HEAVY_AND_DOWN_HORIZONTAL_LIGHT \
+ ((wchar_t) 0x2540)
+#define UNICODE_BOX_DRAWINGS_DOWN_HEAVY_AND_UP_HORIZONTAL_LIGHT \
+ ((wchar_t) 0x2541)
+#define UNICODE_BOX_DRAWINGS_VERTICAL_HEAVY_AND_HORIZONTAL_LIGHT \
+ ((wchar_t) 0x2542)
+#define UNICODE_BOX_DRAWINGS_LEFT_UP_HEAVY_AND_RIGHT_DOWN_LIGHT \
+ ((wchar_t) 0x2543)
+#define UNICODE_BOX_DRAWINGS_RIGHT_UP_HEAVY_AND_LEFT_DOWN_LIGHT \
+ ((wchar_t) 0x2544)
+#define UNICODE_BOX_DRAWINGS_LEFT_DOWN_HEAVY_AND_RIGHT_UP_LIGHT \
+ ((wchar_t) 0x2545)
+#define UNICODE_BOX_DRAWINGS_RIGHT_DOWN_HEAVY_AND_LEFT_UP_LIGHT \
+ ((wchar_t) 0x2546)
+#define UNICODE_BOX_DRAWINGS_DOWN_LIGHT_AND_UP_HORIZONTAL_HEAVY \
+ ((wchar_t) 0x2547)
+#define UNICODE_BOX_DRAWINGS_UP_LIGHT_AND_DOWN_HORIZONTAL_HEAVY \
+ ((wchar_t) 0x2548)
+#define UNICODE_BOX_DRAWINGS_RIGHT_LIGHT_AND_LEFT_VERTICAL_HEAVY \
+ ((wchar_t) 0x2549)
+#define UNICODE_BOX_DRAWINGS_LEFT_LIGHT_AND_RIGHT_VERTICAL_HEAVY \
+ ((wchar_t) 0x254a)
+#define UNICODE_BOX_DRAWINGS_HEAVY_VERTICAL_AND_HORIZONTAL \
+ ((wchar_t) 0x254b)
+#define UNICODE_BOX_DRAWINGS_DOUBLE_HORIZONTAL ((wchar_t) 0x2550)
+#define UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL ((wchar_t) 0x2551)
+#define UNICODE_BOX_DRAWINGS_DOWN_SINGLE_AND_RIGHT_DOUBLE ((wchar_t) 0x2552)
+#define UNICODE_BOX_DRAWINGS_DOWN_DOUBLE_AND_RIGHT_SINGLE ((wchar_t) 0x2553)
+#define UNICODE_BOX_DRAWINGS_DOUBLE_DOWN_AND_RIGHT ((wchar_t) 0x2554)
+#define UNICODE_BOX_DRAWINGS_DOWN_SINGLE_AND_LEFT_DOUBLE ((wchar_t) 0x2555)
+#define UNICODE_BOX_DRAWINGS_DOWN_DOUBLE_AND_LEFT_SINGLE ((wchar_t) 0x2556)
+#define UNICODE_BOX_DRAWINGS_DOUBLE_DOWN_AND_LEFT ((wchar_t) 0x2557)
+#define UNICODE_BOX_DRAWINGS_UP_SINGLE_AND_RIGHT_DOUBLE ((wchar_t) 0x2558)
+#define UNICODE_BOX_DRAWINGS_UP_DOUBLE_AND_RIGHT_SINGLE ((wchar_t) 0x2559)
+#define UNICODE_BOX_DRAWINGS_DOUBLE_UP_AND_RIGHT ((wchar_t) 0x255a)
+#define UNICODE_BOX_DRAWINGS_UP_SINGLE_AND_LEFT_DOUBLE ((wchar_t) 0x255b)
+#define UNICODE_BOX_DRAWINGS_UP_DOUBLE_AND_LEFT_SINGLE ((wchar_t) 0x255c)
+#define UNICODE_BOX_DRAWINGS_DOUBLE_UP_AND_LEFT ((wchar_t) 0x255d)
+#define UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_RIGHT_DOUBLE \
+ ((wchar_t) 0x255e)
+#define UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_RIGHT_SINGLE \
+ ((wchar_t) 0x255f)
+#define UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_RIGHT ((wchar_t) 0x2560)
+#define UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_LEFT_DOUBLE ((wchar_t) 0x2561)
+#define UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_LEFT_SINGLE ((wchar_t) 0x2562)
+#define UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_LEFT ((wchar_t) 0x2563)
+#define UNICODE_BOX_DRAWINGS_DOWN_SINGLE_AND_HORIZONTAL_DOUBLE \
+ ((wchar_t) 0x2564)
+#define UNICODE_BOX_DRAWINGS_DOWN_DOUBLE_AND_HORIZONTAL_SINGLE \
+ ((wchar_t) 0x2565)
+#define UNICODE_BOX_DRAWINGS_DOUBLE_DOWN_AND_HORIZONTAL ((wchar_t) 0x2566)
+#define UNICODE_BOX_DRAWINGS_UP_SINGLE_AND_HORIZONTAL_DOUBLE ((wchar_t) 0x2567)
+#define UNICODE_BOX_DRAWINGS_UP_DOUBLE_AND_HORIZONTAL_SINGLE ((wchar_t) 0x2568)
+#define UNICODE_BOX_DRAWINGS_DOUBLE_UP_AND_HORIZONTAL ((wchar_t) 0x2569)
+#define UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_HORIZONTAL_DOUBLE \
+ ((wchar_t) 0x256a)
+#define UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_HORIZONTAL_SINGLE \
+ ((wchar_t) 0x256b)
+#define UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_HORIZONTAL ((wchar_t) 0x256c)
+#define UNICODE_BOX_DRAWINGS_LIGHT_ARC_DOWN_AND_RIGHT ((wchar_t) 0x256d)
+#define UNICODE_BOX_DRAWINGS_LIGHT_ARC_UP_AND_RIGHT ((wchar_t) 0x2570)
+#define UNICODE_BOX_DRAWINGS_LIGHT_DIAGONAL_UPPER_RIGHT_TO_LOWER_LEFT \
+ ((wchar_t) 0x2571)
+#define UNICODE_BOX_DRAWINGS_LIGHT_DIAGONAL_UPPER_LEFT_TO_LOWER_RIGHT \
+ ((wchar_t) 0x2572)
+#define UNICODE_BOX_DRAWINGS_LIGHT_DIAGONAL_CROSS ((wchar_t) 0x2573)
+#define UNICODE_BOX_DRAWINGS_LIGHT_RIGHT ((wchar_t) 0x2576)
+#define UNICODE_BOX_DRAWINGS_HEAVY_RIGHT ((wchar_t) 0x257a)
+#define UNICODE_BOX_DRAWINGS_LIGHT_LEFT_AND_HEAVY_RIGHT ((wchar_t) 0x257c)
+#define UNICODE_BOX_DRAWINGS_HEAVY_LEFT_AND_LIGHT_RIGHT ((wchar_t) 0x257e)
+
+#define UNICODE_UPPER_HALF_BLOCK ((wchar_t) 0x2580)
+#define UNICODE_LOWER_ONE_EIGHTH_BLOCK ((wchar_t) 0x2581)
+#define UNICODE_LOWER_ONE_QUARTER_BLOCK ((wchar_t) 0x2582)
+#define UNICODE_LOWER_THREE_EIGHTHS_BLOCK ((wchar_t) 0x2583)
+#define UNICODE_LOWER_HALF_BLOCK ((wchar_t) 0x2584)
+#define UNICODE_LOWER_FIVE_EIGHTHS_BLOCK ((wchar_t) 0x2585)
+#define UNICODE_LOWER_THREE_QUARTERS_BLOCK ((wchar_t) 0x2586)
+#define UNICODE_LOWER_SEVEN_EIGHTHS_BLOCK ((wchar_t) 0x2587)
+#define UNICODE_FULL_BLOCK ((wchar_t) 0x2588)
+#define UNICODE_LEFT_HALF_BLOCK ((wchar_t) 0x258c)
+#define UNICODE_RIGHT_HALF_BLOCK ((wchar_t) 0x2590)
+#define UNICODE_LIGHT_SHADE ((wchar_t) 0x2591)
+#define UNICODE_MEDIUM_SHADE ((wchar_t) 0x2592)
+#define UNICODE_DARK_SHADE ((wchar_t) 0x2593)
+#define UNICODE_UPPER_ONE_EIGHTH_BLOCK ((wchar_t) 0x2594)
+#define UNICODE_RIGHT_ONE_EIGHTH_BLOCK ((wchar_t) 0x2595)
+#define UNICODE_QUADRANT_LOWER_LEFT ((wchar_t) 0x2596)
+#define UNICODE_QUADRANT_LOWER_RIGHT ((wchar_t) 0x2597)
+#define UNICODE_QUADRANT_UPPER_LEFT ((wchar_t) 0x2598)
+#define UNICODE_QUADRANT_UPPER_LEFT_AND_LOWER_LEFT_AND_LOWER_RIGHT \
+ ((wchar_t) 0x2599)
+#define UNICODE_QUADRANT_UPPER_LEFT_AND_LOWER_RIGHT ((wchar_t) 0x259a)
+#define UNICODE_QUADRANT_UPPER_LEFT_AND_UPPER_RIGHT_AND_LOWER_LEFT \
+ ((wchar_t) 0x259b)
+#define UNICODE_QUADRANT_UPPER_LEFT_AND_UPPER_RIGHT_AND_LOWER_RIGHT \
+ ((wchar_t) 0x259c)
+#define UNICODE_QUADRANT_UPPER_RIGHT ((wchar_t) 0x259d)
+#define UNICODE_QUADRANT_UPPER_RIGHT_AND_LOWER_LEFT ((wchar_t) 0x259e)
+#define UNICODE_QUADRANT_UPPER_RIGHT_AND_LOWER_LEFT_AND_LOWER_RIGHT \
+ ((wchar_t) 0x259f)
+
+#define UNICODE_BLACK_SQUARE ((wchar_t) 0x25a0)
+#define UNICODE_BLACK_RECTANGLE ((wchar_t) 0x25ac)
+#define UNICODE_BLACK_UP_POINTING_TRIANGLE ((wchar_t) 0x25b2)
+#define UNICODE_BLACK_RIGHT_POINTING_TRIANGLE ((wchar_t) 0x25b6)
+#define UNICODE_BLACK_DOWN_POINTING_TRIANGLE ((wchar_t) 0x25bc)
+#define UNICODE_BLACK_LEFT_POINTING_TRIANGLE ((wchar_t) 0x25c0)
+#define UNICODE_WHITE_CIRCLE ((wchar_t) 0x25cb)
+#define UNICODE_INVERSE_BULLET ((wchar_t) 0x25d8)
+#define UNICODE_INVERSE_WHITE_CIRCLE ((wchar_t) 0x25d9)
+
+#define UNICODE_WHITE_SMILING_FACE ((wchar_t) 0x263a)
+#define UNICODE_BLACK_SMILING_FACE ((wchar_t) 0x263b)
+#define UNICODE_WHITE_SUN_WITH_RAYS ((wchar_t) 0x263c)
+#define UNICODE_FEMALE_SIGN ((wchar_t) 0x2640)
+#define UNICODE_MALE_SIGN ((wchar_t) 0x2642)
+#define UNICODE_BLACK_SPADE_SUIT ((wchar_t) 0x2660)
+#define UNICODE_BLACK_CLUB_SUIT ((wchar_t) 0x2663)
+#define UNICODE_BLACK_HEART_SUIT ((wchar_t) 0x2665)
+#define UNICODE_BLACK_DIAMOND_SUIT ((wchar_t) 0x2666)
+#define UNICODE_EIGHTH_NOTE ((wchar_t) 0x266a)
+#define UNICODE_BEAMED_EIGHTH_NOTES ((wchar_t) 0x266b)
+
+#define UNICODE_PRIVATE_USE_AREA ((wchar_t) 0xe000)
+#define UNICODE_PRIVATE_USE_AREA_LAST ((wchar_t) 0xf8ff)
+
+#define UNICODE_REPLACEMENT_CHARACTER ((wchar_t) 0xfffd)
+
+#endif /* _UNICODE_H_ */
diff --git a/console-client/vga-dynacolor.c b/console-client/vga-dynacolor.c
new file mode 100644
index 00000000..9289e1eb
--- /dev/null
+++ b/console-client/vga-dynacolor.c
@@ -0,0 +1,322 @@
+/* vga-dynacolor.c - Dynamic color handling for VGA cards.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <assert.h>
+
+#include <hurd/console.h>
+
+#include "vga-hw.h"
+#include "vga-support.h"
+#include "vga-dynacolor.h"
+
+
+dynacolor_t dynacolor_init_8 = DYNACOLOR_INIT_8;
+dynacolor_t dynacolor_init_16 = DYNACOLOR_INIT_16;
+
+static const unsigned char std_palette[16][DYNACOLOR_COMPONENTS] =
+ {
+ { 0, 0, 0 }, /* Black. */
+ { 42, 0, 0 }, /* Red. */
+ { 0, 42, 0 }, /* Green. */
+ { 42, 21, 0 }, /* Brown. */
+ { 0, 0, 42 }, /* Blue. */
+ { 42, 0, 42 }, /* Magenta. */
+ { 0, 42, 42 }, /* Cyan. */
+ { 42, 42, 42 }, /* White. */
+ { 21, 21, 21 }, /* Bright Black. */
+ { 63, 21, 21 }, /* Bright Red. */
+ { 21, 63, 21 }, /* Bright Green. */
+ { 63, 63, 21 }, /* Bright Yellow. */
+ { 21, 21, 63 }, /* Bright Blue. */
+ { 63, 21, 63 }, /* Bright Magenta. */
+ { 21, 63, 63 }, /* Bright Cyan. */
+ { 63, 63, 63 } /* Bright White. */
+ };
+
+/* The currently active (visible) dynafont. */
+static dynacolor_t *active_dynacolor;
+
+static unsigned char saved_palette[16][DYNACOLOR_COMPONENTS];
+
+/* We initialize this to the desired mapping for
+ vga_exchange_palette_attributes. */
+/* Palette index 0 is left as it is, for the border color. */
+static unsigned char saved_palette_attr[16] =
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+
+
+/* Initialize the dynacolor subsystem. */
+void
+dynacolor_init (void)
+{
+ /* Palette index 0 is left as it is, as it is also used for the
+ border color by default. */
+ vga_exchange_palette_attributes (0, saved_palette_attr, 16);
+ vga_read_palette (0, saved_palette[0], 16);
+
+ vga_write_palette (0, std_palette[0], 16);
+}
+
+
+/* Restore the original palette. */
+void
+dynacolor_fini (void)
+{
+ vga_write_palette (0, saved_palette[0], 16);
+ vga_exchange_palette_attributes (0, saved_palette_attr, 16);
+}
+
+
+/* Activate the dynamic color palette DC. */
+void
+dynacolor_activate (dynacolor_t *dc)
+{
+ if (dc == active_dynacolor)
+ return;
+
+ if (dc->ref[0] < 0 && (!active_dynacolor || active_dynacolor->ref[0] >= 0))
+ {
+ /* Switching from dynamic to static palette. */
+ vga_write_palette (0, std_palette[0], 16);
+ }
+ else if (dc->ref[0] >= 0
+ && (!active_dynacolor || active_dynacolor->ref[0] < 0))
+ {
+ /* Switching from static to dynamic palette. */
+ int i;
+ for (i = 0; i < 16; i++)
+ if (dc->col[i] >= 0)
+ {
+ vga_write_palette (dc->col[i], std_palette[i], 1);
+ vga_write_palette (8 + dc->col[i], std_palette[i], 1);
+ }
+ }
+ active_dynacolor = dc;
+}
+
+
+/* Try to allocate a slot for the color COL in the dynamic color
+ palette DC. Return the allocated slot number or -1 if no slot is
+ available. */
+signed char
+dynacolor_allocate (dynacolor_t *dc, unsigned char col)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ if (dc->ref[i] == 0)
+ {
+ /* We want to reuse slot i. Clear the old user. */
+ int j;
+
+ for (j = 0; j < 16; j++)
+ if (dc->col[j] == i)
+ {
+ dc->col[j] = -1;
+ break;
+ }
+
+ dc->ref[i] = 1;
+ dc->col[col] = i;
+ if (active_dynacolor == dc)
+ {
+ vga_write_palette (0 + i, std_palette[col], 1);
+ vga_write_palette (8 + i, std_palette[col], 1);
+ }
+ return i;
+ }
+ return -1;
+}
+
+
+/* This is a convenience function that looks up a replacement color
+ pair if the original colors are not available. The function always
+ succeeds. DC is the dynacolor to use for allocation, FGCOL and
+ BGCOL are the desired colors, and R_FGCOL and R_BGCOL are the
+ resulting colors. The function assumes that the caller already
+ tried to look up the desired colors before trying to replace them,
+ and that the result of the lookup is contained in R_FGCOL and
+ R_BGCOL.
+
+ Example:
+
+ res_bgcol = dynacolor_lookup (&dc, bgcol);
+ res_fgcol = dynacolor_lookup (&dc, fgcol);
+ if (res_bgcol == -1 || res_fgcol == -1)
+ dynacolor_replace_colors (&dc, fgcol, bgcol, &res_fgcol, &res_bgcol);
+
+ After the above code, res_fgcol and res_bgcol contain valid color
+ values. */
+void
+dynacolor_replace_colors (dynacolor_t *dc,
+ signed char fgcol, signed char bgcol,
+ signed char *r_fgcol, signed char *r_bgcol)
+{
+ /* Replacement colors. As we have 8 colors in our palette, and
+ one was already tried, we only need to try out 8 possible
+ replacements. Only the first seven can fail. But one is
+ possibly taken by the fore-/background color, so we actually
+ have to try up to nine. XXX Maybe we should have a table
+ based on pairs, but that increases the number of cases a
+ lot. */
+ /* Note that no color must occur twice in one replacement list,
+ and that the color to be replaced must not occur either. */
+ static signed char pref[16][9] =
+ {
+ /* Replacements for CONS_COLOR_BLACK. */
+ { CONS_COLOR_BLACK | (1 << 3), CONS_COLOR_BLUE,
+ CONS_COLOR_YELLOW, CONS_COLOR_RED, CONS_COLOR_MAGENTA,
+ CONS_COLOR_GREEN, CONS_COLOR_CYAN, CONS_COLOR_WHITE,
+ CONS_COLOR_BLUE | (1 << 3) },
+ /* Replacements for CONS_COLOR_RED. */
+ { CONS_COLOR_RED | (1 << 3), CONS_COLOR_YELLOW,
+ CONS_COLOR_MAGENTA, CONS_COLOR_BLUE, CONS_COLOR_CYAN,
+ CONS_COLOR_GREEN, CONS_COLOR_WHITE, CONS_COLOR_BLACK,
+ CONS_COLOR_BLACK | (1 << 3) },
+ /* Replacements for CONS_COLOR_GREEN. */
+ { CONS_COLOR_GREEN | (1 << 3), CONS_COLOR_CYAN,
+ CONS_COLOR_YELLOW, CONS_COLOR_BLUE, CONS_COLOR_RED,
+ CONS_COLOR_MAGENTA, CONS_COLOR_WHITE, CONS_COLOR_BLACK,
+ CONS_COLOR_BLACK | (1 << 3) },
+ /* Replacements for CONS_COLOR_YELLOW. */
+ { CONS_COLOR_YELLOW | (1 << 3), CONS_COLOR_RED,
+ CONS_COLOR_GREEN, CONS_COLOR_MAGENTA, CONS_COLOR_BLUE,
+ CONS_COLOR_CYAN, CONS_COLOR_WHITE, CONS_COLOR_BLACK,
+ CONS_COLOR_BLACK | (1 << 3) },
+ /* Replacements for CONS_COLOR_BLUE. */
+ { CONS_COLOR_BLUE | (1 << 3), CONS_COLOR_CYAN,
+ CONS_COLOR_MAGENTA, CONS_COLOR_RED, CONS_COLOR_GREEN,
+ CONS_COLOR_YELLOW, CONS_COLOR_WHITE, CONS_COLOR_BLACK,
+ CONS_COLOR_BLACK | (1 << 3) },
+ /* Replacements for CONS_COLOR_MAGENTA. */
+ { CONS_COLOR_MAGENTA | (1 << 3), CONS_COLOR_RED,
+ CONS_COLOR_BLUE, CONS_COLOR_YELLOW, CONS_COLOR_CYAN,
+ CONS_COLOR_BLUE, CONS_COLOR_WHITE, CONS_COLOR_BLACK,
+ CONS_COLOR_BLACK | (1 << 3) },
+ /* Replacements for CONS_COLOR_CYAN. */
+ { CONS_COLOR_CYAN | (1 << 3), CONS_COLOR_BLUE,
+ CONS_COLOR_MAGENTA, CONS_COLOR_GREEN, CONS_COLOR_RED,
+ CONS_COLOR_YELLOW, CONS_COLOR_WHITE, CONS_COLOR_BLACK,
+ CONS_COLOR_BLACK | (1 << 3) },
+ /* Replacements for CONS_COLOR_WHITE. */
+ { CONS_COLOR_WHITE | (1 << 3), CONS_COLOR_CYAN,
+ CONS_COLOR_GREEN, CONS_COLOR_YELLOW, CONS_COLOR_MAGENTA,
+ CONS_COLOR_RED, CONS_COLOR_BLUE, CONS_COLOR_CYAN | (1 << 3),
+ CONS_COLOR_BLACK | (1 << 3) },
+ /* Replacements for CONS_COLOR_BLACK | (1 << 3). */
+ { CONS_COLOR_BLACK, CONS_COLOR_BLUE | (1 << 3),
+ CONS_COLOR_YELLOW | (1 << 3), CONS_COLOR_RED | (1 << 3),
+ CONS_COLOR_MAGENTA | (1 << 3), CONS_COLOR_GREEN | (1 << 3),
+ CONS_COLOR_CYAN | (1 << 3), CONS_COLOR_WHITE | (1 << 3),
+ CONS_COLOR_WHITE },
+ /* Replacements for CONS_COLOR_RED | (1 << 3). */
+ { CONS_COLOR_RED, CONS_COLOR_YELLOW | (1 << 3),
+ CONS_COLOR_MAGENTA | (1 << 3), CONS_COLOR_BLUE | (1 << 3),
+ CONS_COLOR_CYAN | (1 << 3), CONS_COLOR_GREEN | (1 << 3),
+ CONS_COLOR_WHITE | (1 << 3), CONS_COLOR_WHITE,
+ CONS_COLOR_BLACK | (1 << 3) },
+ /* Replacements for CONS_COLOR_GREEN | (1 << 3). */
+ { CONS_COLOR_GREEN, CONS_COLOR_CYAN | (1 << 3),
+ CONS_COLOR_YELLOW | (1 << 3), CONS_COLOR_BLUE | (1 << 3),
+ CONS_COLOR_RED | (1 << 3), CONS_COLOR_MAGENTA | (1 << 3),
+ CONS_COLOR_WHITE | (1 << 3), CONS_COLOR_WHITE,
+ CONS_COLOR_BLACK | (1 << 3) },
+ /* Replacements for CONS_COLOR_YELLOW | (1 << 3). */
+ { CONS_COLOR_YELLOW, CONS_COLOR_RED | (1 << 3),
+ CONS_COLOR_GREEN | (1 << 3), CONS_COLOR_MAGENTA | (1 << 3),
+ CONS_COLOR_BLUE | (1 << 3), CONS_COLOR_CYAN | (1 << 3),
+ CONS_COLOR_WHITE | (1 << 3), CONS_COLOR_WHITE,
+ CONS_COLOR_BLACK | (1 << 3) },
+ /* Replacements for CONS_COLOR_BLUE | (1 << 3). */
+ { CONS_COLOR_BLUE, CONS_COLOR_CYAN | (1 << 3),
+ CONS_COLOR_MAGENTA | (1 << 3), CONS_COLOR_RED | (1 << 3),
+ CONS_COLOR_GREEN | (1 << 3), CONS_COLOR_YELLOW | (1 << 3),
+ CONS_COLOR_WHITE | (1 << 3), CONS_COLOR_WHITE,
+ CONS_COLOR_BLACK | (1 << 3) },
+ /* Replacements for CONS_COLOR_MAGENTA | (1 << 3). */
+ { CONS_COLOR_MAGENTA, CONS_COLOR_RED | (1 << 3),
+ CONS_COLOR_BLUE | (1 << 3), CONS_COLOR_YELLOW | (1 << 3),
+ CONS_COLOR_CYAN | (1 << 3), CONS_COLOR_GREEN | (1 << 3),
+ CONS_COLOR_WHITE | (1 << 3), CONS_COLOR_WHITE,
+ CONS_COLOR_BLACK | (1 << 3) },
+ /* Replacements for CONS_COLOR_CYAN | (1 << 3). */
+ { CONS_COLOR_CYAN, CONS_COLOR_BLUE | (1 << 3),
+ CONS_COLOR_MAGENTA | (1 << 3), CONS_COLOR_GREEN | (1 << 3),
+ CONS_COLOR_RED | (1 << 3), CONS_COLOR_YELLOW | (1 << 3),
+ CONS_COLOR_WHITE | (1 << 3), CONS_COLOR_WHITE,
+ CONS_COLOR_BLACK | (1 << 3) },
+ /* Replacements for CONS_COLOR_WHITE | (1 << 3). */
+ { CONS_COLOR_WHITE, CONS_COLOR_CYAN | (1 << 3),
+ CONS_COLOR_GREEN | (1 << 3), CONS_COLOR_YELLOW | (1 << 3),
+ CONS_COLOR_MAGENTA | (1 << 3), CONS_COLOR_RED | (1 << 3),
+ CONS_COLOR_BLUE | (1 << 3), CONS_COLOR_CYAN,
+ CONS_COLOR_BLACK | (1 << 3) },
+ };
+
+ signed char res_fgcol = *r_fgcol;
+ signed char res_bgcol = *r_bgcol;
+ signed char new_bgcol = bgcol;
+ int i;
+
+ /* First get a background color. */
+ if (res_bgcol == -1)
+ {
+ for (i = 0; i < 9; i++)
+ {
+ /* If the foreground color is found, make sure to not
+ set the background to it. */
+ if (res_fgcol == -1 || pref[bgcol][i] != fgcol)
+ {
+ res_bgcol = dynacolor_lookup (*dc, pref[bgcol][i]);
+ if (res_bgcol >= 0)
+ break;
+ }
+ }
+
+ assert (res_bgcol >= 0);
+ new_bgcol = pref[bgcol][i];
+ }
+
+ if (fgcol == bgcol)
+ {
+ assert (res_fgcol == -1);
+ /* Acquire another reference. */
+ res_fgcol = dynacolor_lookup (*dc, new_bgcol);
+ }
+ else
+ assert (res_fgcol != res_bgcol);
+
+ if (res_fgcol == -1)
+ {
+ /* Now find a foreground color. */
+ for (i = 0; i < 9; i++)
+ {
+ if (pref[fgcol][i] != new_bgcol)
+ {
+ res_fgcol = dynacolor_lookup (*dc, pref[fgcol][i]);
+ if (res_fgcol >= 0)
+ break;
+ }
+ }
+ assert (res_fgcol >= 0);
+ }
+ *r_fgcol = res_fgcol;
+ *r_bgcol = res_bgcol;
+}
diff --git a/console-client/vga-dynacolor.h b/console-client/vga-dynacolor.h
new file mode 100644
index 00000000..304bcc1b
--- /dev/null
+++ b/console-client/vga-dynacolor.h
@@ -0,0 +1,108 @@
+/* vga-dynacolor.h - Interface to dynamic color handling for VGA cards.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef _VGA_DYNACOLOR_H_
+#define _VGA_DYNACOLOR_H_ 1
+
+/* Every component can have up to 6 bits. */
+#define DYNACOLOR_COMPONENT_MAX 0x63
+
+/* The components are, in that order, red, green, blue. */
+#define DYNACOLOR_COMPONENTS 3
+
+/* The maximum number of colors. */
+#define DYNACOLOR_COLORS 8
+
+struct dynacolor
+{
+ int ref[8];
+ /* Reverse lookup, -1 denotes an unmapped color. */
+ signed char col[16];
+};
+
+typedef struct dynacolor dynacolor_t;
+
+/* We start with one reference for the black color so that it is
+ always available. It is also put in front so that it can be shared
+ as the border color. The importance of this is that for
+ non-standard font heights, we have some black-filled normal text
+ rows below the screen matrix, for which we must allocate a real
+ color slot :( */
+#define DYNACOLOR_INIT_8 { { 1, 0, 0, 0, 0, 0, 0, 0 }, \
+ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } }
+#define DYNACOLOR_INIT_16 { { -1 } }
+
+extern dynacolor_t dynacolor_init_8;
+extern dynacolor_t dynacolor_init_16;
+
+
+/* Initialize the dynacolor subsystem. */
+void dynacolor_init (void);
+
+/* Restore the original palette. */
+void dynacolor_fini (void);
+
+/* Activate the dynamic color palette DC. */
+void dynacolor_activate (dynacolor_t *dc);
+
+/* Try to allocate a slot for the color COL in the dynamic color
+ palette DC. Return the allocated slot number (with one reference)
+ or -1 if no slot is available. */
+signed char dynacolor_allocate (dynacolor_t *dc, unsigned char col);
+
+/* Get the slot number for color C in the dynamic color palette DC, or
+ -1 if we ran out of color slots. If successful, this allocates a
+ reference for the color. */
+#define dynacolor_lookup(dc,c) \
+ ((dc).ref[0] < 0 ? (c) : \
+ ((dc).col[(c)] >= 0 ? (dc).ref[(dc).col[(c)]]++, (dc).col[(c)] : \
+ dynacolor_allocate (&(dc), (c))))
+
+/* Add a reference to palette entry P in the dynamic font DC. */
+#define dynacolor_add_ref(dc,p) \
+ ((dc).ref[0] >= 0 && (dc).ref[p]++)
+
+/* Deallocate a reference for palette entry P in the dynamic font DC. */
+#define dynacolor_release(dc,p) \
+ ((dc).ref[0] >= 0 && (dc).ref[p]--)
+
+/* This is a convenience function that looks up a replacement color
+ pair if the original colors are not available. The function always
+ succeeds. DC is the dynacolor to use for allocation, FGCOL and
+ BGCOL are the desired colors, and R_FGCOL and R_BGCOL are the
+ resulting colors. The function assumes that the caller already
+ tried to look up the desired colors before trying to replace them,
+ and that the result of the lookup is contained in R_FGCOL and
+ R_BGCOL.
+
+ Example:
+
+ res_bgcol = dynacolor_lookup (&dc, bgcol);
+ res_fgcol = dynacolor_lookup (&dc, fgcol);
+ if (res_bgcol == -1 || res_fgcol == -1)
+ dynacolor_replace_colors (&dc, fgcol, bgcol, &res_fgcol, &res_bgcol);
+
+ After the above code, res_fgcol and res_bgcol contain valid color
+ values. */
+void dynacolor_replace_colors (dynacolor_t *dc,
+ signed char fgcol, signed char bgcol,
+ signed char *r_fgcol, signed char *r_bgcol);
+
+#endif /* _VGA_DYNACOLOR_H_ */
diff --git a/console-client/vga-dynafont.c b/console-client/vga-dynafont.c
new file mode 100644
index 00000000..573d63b4
--- /dev/null
+++ b/console-client/vga-dynafont.c
@@ -0,0 +1,1083 @@
+/* vga-dynafont.c - Dynamic font handling for VGA cards.
+ Copyright (C) 2002, 2010 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stddef.h>
+#include <assert.h>
+#include <malloc.h>
+#include <wchar.h>
+#include <stdlib.h>
+#include <hurd/ihash.h>
+#include <string.h>
+
+#include <hurd/console.h>
+#include "vga-hw.h"
+#include "vga-support.h"
+#include "bdf.h"
+#include "vga-dynafont.h"
+#include "unicode.h"
+
+
+/* The currently active (visible) dynafont. The original idea was to
+ use some sort of caching for multiple dynafonts using the 8 font
+ buffers on the graphic card, but the new idea is to share a common
+ state among all virtual consoles and do a full refresh on
+ switching. However, bits and pieces of the old idea are still
+ present, in case they become useful. */
+static dynafont_t active_dynafont;
+
+
+/* One glyph in a VGA font is 8 pixels wide and 32 pixels high. Only
+ the first N lines are visible, and N depends on the VGA register
+ settings. */
+typedef char vga_font_glyph[VGA_FONT_HEIGHT];
+
+
+/* For each glyph in the VGA font, one instance of this structure is
+ held in the dynafont. */
+struct mapped_character
+{
+ /* Reference count of the mapped character. If this drops zero, we
+ can remove the mapping at any time. */
+ int refs;
+
+ /* Remember the character this glyph belongs to, so the glyph can be
+ reloaded when the font is changed. This is actually a wchar_t
+ with some text attributes mixed into the high bits. */
+#define WCHAR_BOLD ((wchar_t) 0x20000000)
+#define WCHAR_ITALIC ((wchar_t) 0x10000000)
+#define WCHAR_MASK CONS_WCHAR_MASK
+ wchar_t character;
+
+ /* Used by libihash for fast removal of elements. */
+ hurd_ihash_locp_t locp;
+};
+
+
+struct dynafont
+{
+ /* The sorted font to take the glyphs from. */
+ bdf_font_t font;
+
+ /* The sorted font to take the italic glyphs from. */
+ bdf_font_t font_italic;
+
+ /* The sorted font to take the bold glyphs from. */
+ bdf_font_t font_bold;
+
+ /* The sorted font to take the bold and italic glyphs from. */
+ bdf_font_t font_bold_italic;
+
+ /* The size of the VGA font (256 or 512). Must be a power of 2! */
+ int size;
+
+ /* The width of the VGA font (8 or 9). */
+ int width;
+
+ /* A hash containing a pointer to a struct mapped_character for each
+ UCS-4 character we map to a VGA font index. */
+ struct hurd_ihash charmap;
+
+ /* The struct mapped_characters are preallocated for all vga font
+ index values. This points to an array of SIZE such elements. */
+ struct mapped_character *charmap_data;
+
+ /* The last vga font index that had been free (or could be
+ reused). */
+ int vga_font_last_free_index;
+
+ /* The number of free slots in the VGA font. */
+ int vga_font_free_indices;
+
+ int use_lgc;
+
+ /* The last vga font index that had been free (or could be reused)
+ for horizontal line graphic characters. */
+ int vga_font_last_free_index_lgc;
+
+ /* The number of free slots in the VGA font for horizontal line
+ graphic charactes. */
+ int vga_font_free_indices_lgc;
+
+ /* The font memory as stored on the card. */
+ vga_font_glyph *vga_font;
+
+ /* The cursor size, standout or normal. */
+ int cursor_standout;
+};
+
+
+/* The VGA standard font follows IBM code page 437. The following
+ table maps this to unicode. For simplicity, a 1:1 mapping is
+ assumed. Ambiguities are special cased in create_system_font. */
+static
+wchar_t ibm437_to_unicode[VGA_FONT_SIZE] = {
+ 0, /* 0x00 */
+ UNICODE_WHITE_SMILING_FACE, /* 0x01 */
+ UNICODE_BLACK_SMILING_FACE, /* 0x02 */
+ UNICODE_BLACK_HEART_SUIT, /* 0x03 */
+ UNICODE_BLACK_DIAMOND_SUIT, /* 0x04 */
+ UNICODE_BLACK_CLUB_SUIT, /* 0x05 */
+ UNICODE_BLACK_SPADE_SUIT, /* 0x06 */
+ UNICODE_BULLET, /* 0x07 */
+ UNICODE_INVERSE_BULLET, /* 0x08 */
+ UNICODE_WHITE_CIRCLE, /* 0x09 */
+ UNICODE_INVERSE_WHITE_CIRCLE, /* 0x0a */
+ UNICODE_MALE_SIGN, /* 0x0b */
+ UNICODE_FEMALE_SIGN, /* 0x0c */
+ UNICODE_EIGHTH_NOTE, /* 0x0d */
+ UNICODE_BEAMED_EIGHTH_NOTES, /* 0x0e */
+ UNICODE_WHITE_SUN_WITH_RAYS, /* 0x0f */
+ UNICODE_BLACK_RIGHT_POINTING_TRIANGLE, /* 0x10 */
+ UNICODE_BLACK_LEFT_POINTING_TRIANGLE, /* 0x11 */
+ UNICODE_UP_DOWN_ARROW, /* 0x12 */
+ UNICODE_DOUBLE_EXCLAMATION_MARK, /* 0x13 */
+ UNICODE_PILCROW_SIGN, /* 0x14 */
+ UNICODE_SECTION_SIGN, /* 0x15 */
+ UNICODE_BLACK_RECTANGLE, /* 0x16 */
+ UNICODE_UP_DOWN_ARROW_WITH_BASE, /* 0x17 */
+ UNICODE_UPWARDS_ARROW, /* 0x18 */
+ UNICODE_DOWNWARDS_ARROW, /* 0x19 */
+ UNICODE_RIGHTWARDS_ARROW, /* 0x1a */
+ UNICODE_LEFTWARDS_ARROW, /* 0x1b */
+ UNICODE_RIGHT_ANGLE, /* 0x1c */
+ UNICODE_LEFT_RIGHT_ARROW, /* 0x1d */
+ UNICODE_BLACK_UP_POINTING_TRIANGLE, /* 0x1e */
+ UNICODE_BLACK_DOWN_POINTING_TRIANGLE, /* 0x1f */
+ ' ', '!', '"', '#', '$', '%', '&', '\'', /* 0x20 - 0x27 */
+ '(', ')', '*', '+', ',', '-', '.', '/', /* 0x28 - 0x2f */
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', /* 0x30 - 0x37 */
+ ':', ';', '<', '=', '>', '?', /* 0x38 - 0x3f */
+ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 0x40 - 0x47 */
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* 0x48 - 0x4f */
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 0x50 - 0x57 */
+ 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', /* 0x58 - 0x5f */
+ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 0x60 - 0x67 */
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* 0x68 - 0x6f */
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 0x70 - 0x77 */
+ 'x', 'y', 'z', '{', '|', '}', '~', UNICODE_HOUSE, /* 0x78 - 0x7f */
+ UNICODE_LATIN_CAPITAL_LETTER_C_WITH_CEDILLA, /* 0x80 */
+ UNICODE_LATIN_SMALL_LETTER_U_WITH_DIARESIS, /* 0x81 */
+ UNICODE_LATIN_SMALL_LETTER_E_WITH_ACUTE, /* 0x82 */
+ UNICODE_LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX, /* 0x83 */
+ UNICODE_LATIN_SMALL_LETTER_A_WITH_DIARESIS, /* 0x84 */
+ UNICODE_LATIN_SMALL_LETTER_A_WITH_GRAVE, /* 0x85 */
+ UNICODE_LATIN_SMALL_LETTER_A_WITH_RING_ABOVE, /* 0x86 */
+ UNICODE_LATIN_SMALL_LETTER_C_WITH_CEDILLA, /* 0x87 */
+ UNICODE_LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX, /* 0x88 */
+ UNICODE_LATIN_SMALL_LETTER_E_WITH_DIARESIS, /* 0x89 */
+ UNICODE_LATIN_SMALL_LETTER_E_WITH_GRAVE, /* 0x8a */
+ UNICODE_LATIN_SMALL_LETTER_I_WITH_DIARESIS, /* 0x8b */
+ UNICODE_LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX, /* 0x8c */
+ UNICODE_LATIN_SMALL_LETTER_I_WITH_GRAVE, /* 0x8d */
+ UNICODE_LATIN_CAPITAL_LETTER_A_WITH_DIARESIS, /* 0x8e */
+ UNICODE_LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE, /* 0x8f */
+ UNICODE_LATIN_CAPITAL_LETTER_E_WITH_ACUTE, /* 0x90 */
+ UNICODE_LATIN_SMALL_LETTER_AE, /* 0x91 */
+ UNICODE_LATIN_CAPITAL_LETTER_AE, /* 0x92 */
+ UNICODE_LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX, /* 0x93 */
+ UNICODE_LATIN_SMALL_LETTER_O_WITH_DIARESIS, /* 0x94 */
+ UNICODE_LATIN_SMALL_LETTER_O_WITH_GRAVE, /* 0x95 */
+ UNICODE_LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX, /* 0x96 */
+ UNICODE_LATIN_SMALL_LETTER_U_WITH_GRAVE, /* 0x97 */
+ UNICODE_LATIN_SMALL_LETTER_Y_WITH_DIARESIS, /* 0x98 */
+ UNICODE_LATIN_CAPITAL_LETTER_O_WITH_DIARESIS, /* 0x99 */
+ UNICODE_LATIN_CAPITAL_LETTER_U_WITH_DIARESIS, /* 0x9a */
+ UNICODE_CENT_SIGN, /* 0x9b */
+ UNICODE_POUND_SIGN, /* 0x9c */
+ UNICODE_YEN_SIGN, /* 0x9d */
+ UNICODE_PESETA_SIGN, /* 0x9e */
+ UNICODE_LATIN_SMALL_LETTER_F_WITH_HOOK, /* 0x9f */
+ UNICODE_LATIN_SMALL_LETTER_A_WITH_ACUTE, /* 0xa0 */
+ UNICODE_LATIN_SMALL_LETTER_I_WITH_ACUTE, /* 0xa1 */
+ UNICODE_LATIN_SMALL_LETTER_O_WITH_ACUTE, /* 0xa2 */
+ UNICODE_LATIN_SMALL_LETTER_U_WITH_ACUTE, /* 0xa3 */
+ UNICODE_LATIN_SMALL_LETTER_N_WITH_TILDE, /* 0xa4 */
+ UNICODE_LATIN_CAPITAL_LETTER_N_WITH_TILDE, /* 0xa5 */
+ UNICODE_FEMININE_ORDINAL_INDICATOR, /* 0xa6 */
+ UNICODE_MASCULINE_ORDINAL_INDICATOR, /* 0xa7 */
+ UNICODE_INVERTED_QUESTION_MARK, /* 0xa8 */
+ UNICODE_REVERSED_NOT_SIGN, /* 0xa9 */
+ UNICODE_NOT_SIGN, /* 0xaa */
+ UNICODE_VULGAR_FRACTION_ONE_HALF, /* 0xab */
+ UNICODE_VULGAR_FRACTION_ONE_QUARTER, /* 0xac */
+ UNICODE_INVERTED_EXCLAMATION_MARK, /* 0xad */
+ UNICODE_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK, /* 0xae */
+ UNICODE_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK, /* 0xaf */
+ UNICODE_LIGHT_SHADE, /* 0xb0 */
+ UNICODE_MEDIUM_SHADE, /* 0xb1 */
+ UNICODE_DARK_SHADE, /* 0xb2 */
+ UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL, /* 0xb3 */
+ UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_LEFT, /* 0xb4 */
+ UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_LEFT_DOUBLE, /* 0xb5 */
+ UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_LEFT_SINGLE, /* 0xb6 */
+ UNICODE_BOX_DRAWINGS_DOWN_DOUBLE_AND_LEFT_SINGLE, /* 0xb7 */
+ UNICODE_BOX_DRAWINGS_DOWN_SINGLE_AND_LEFT_DOUBLE, /* 0xb8 */
+ UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_LEFT, /* 0xb9 */
+ UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL, /* 0xba */
+ UNICODE_BOX_DRAWINGS_DOUBLE_DOWN_AND_LEFT, /* 0xbb */
+ UNICODE_BOX_DRAWINGS_DOUBLE_UP_AND_LEFT, /* 0xbc */
+ UNICODE_BOX_DRAWINGS_UP_DOUBLE_AND_LEFT_SINGLE, /* 0xbd */
+ UNICODE_BOX_DRAWINGS_UP_SINGLE_AND_LEFT_DOUBLE, /* 0xbe */
+ UNICODE_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT, /* 0xbf */
+ UNICODE_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT, /* 0xc0 */
+ UNICODE_BOX_DRAWINGS_LIGHT_UP_AND_HORIZONTAL, /* 0xc1 */
+ UNICODE_BOX_DRAWINGS_LIGHT_DOWN_AND_HORIZONTAL, /* 0xc2 */
+ UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_RIGHT, /* 0xc3 */
+ UNICODE_BOX_DRAWINGS_LIGHT_HORIZONTAL, /* 0xc4 */
+ UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_HORIZONTAL, /* 0xc5 */
+ UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_RIGHT_DOUBLE, /* 0xc6 */
+ UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_RIGHT_SINGLE, /* 0xc7 */
+ UNICODE_BOX_DRAWINGS_DOUBLE_UP_AND_RIGHT, /* 0xc8 */
+ UNICODE_BOX_DRAWINGS_DOUBLE_DOWN_AND_RIGHT, /* 0xc9 */
+ UNICODE_BOX_DRAWINGS_DOUBLE_UP_AND_HORIZONTAL, /* 0xca */
+ UNICODE_BOX_DRAWINGS_DOUBLE_DOWN_AND_HORIZONTAL, /* 0xcb */
+ UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_RIGHT, /* 0xcc */
+ UNICODE_BOX_DRAWINGS_DOUBLE_HORIZONTAL, /* 0xcd */
+ UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_HORIZONTAL,/* 0xce */
+ UNICODE_BOX_DRAWINGS_UP_SINGLE_AND_HORIZONTAL_DOUBLE, /* 0xcf */
+ UNICODE_BOX_DRAWINGS_UP_DOUBLE_AND_HORIZONTAL_SINGLE, /* 0xd0 */
+ UNICODE_BOX_DRAWINGS_DOWN_SINGLE_AND_HORIZONTAL_DOUBLE, /* 0xd1 */
+ UNICODE_BOX_DRAWINGS_DOWN_DOUBLE_AND_HORIZONTAL_SINGLE, /* 0xd2 */
+ UNICODE_BOX_DRAWINGS_UP_DOUBLE_AND_RIGHT_SINGLE, /* 0xd3 */
+ UNICODE_BOX_DRAWINGS_UP_SINGLE_AND_RIGHT_DOUBLE, /* 0xd4 */
+ UNICODE_BOX_DRAWINGS_DOWN_SINGLE_AND_RIGHT_DOUBLE, /* 0xd5 */
+ UNICODE_BOX_DRAWINGS_DOWN_DOUBLE_AND_RIGHT_SINGLE, /* 0xd6 */
+ UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_HORIZONTAL_SINGLE, /* 0xd7 */
+ UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_HORIZONTAL_DOUBLE, /* 0xd8 */
+ UNICODE_BOX_DRAWINGS_LIGHT_UP_AND_LEFT, /* 0xd9 */
+ UNICODE_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT, /* 0xda */
+ UNICODE_FULL_BLOCK, /* 0xdb */
+ UNICODE_LOWER_HALF_BLOCK, /* 0xdc */
+ UNICODE_LEFT_HALF_BLOCK, /* 0xdd */
+ UNICODE_RIGHT_HALF_BLOCK, /* 0xde */
+ UNICODE_UPPER_HALF_BLOCK, /* 0xdf */
+ UNICODE_GREEK_SMALL_LETTER_ALPHA, /* 0xe0 */
+ /* The next one: Also sz-ligature. */
+ UNICODE_GREEK_SMALL_LETTER_BETA, /* 0xe1 */
+ UNICODE_GREEK_CAPITAL_LETTER_GAMMA, /* 0xe2 */
+ UNICODE_GREEK_SMALL_LETTER_PI, /* 0xe3 */
+ UNICODE_GREEK_CAPITAL_LETTER_SIGMA, /* 0xe4 */
+ UNICODE_GREEK_SMALL_LETTER_SIGMA, /* 0xe5 */
+ /* The next one: Also micro. */
+ UNICODE_GREEK_SMALL_LETTER_MU, /* 0xe6 */
+ UNICODE_GREEK_SMALL_LETTER_TAU, /* 0xe7 */
+ UNICODE_GREEK_CAPITAL_LETTER_PHI, /* 0xe8 */
+ UNICODE_GREEK_CAPITAL_LETTER_OMICRON, /* 0xe9 */
+ UNICODE_GREEK_CAPITAL_LETTER_OMEGA, /* 0xea */
+ UNICODE_GREEK_SMALL_LETTER_DELTA, /* 0xeb */
+ UNICODE_INFINITY, /* 0xec */
+ UNICODE_GREEK_SMALL_LETTER_PHI, /* 0xed */
+ UNICODE_GREEK_SMALL_LETTER_EPSILON, /* 0xee */
+ UNICODE_INTERSECTION, /* 0xef */
+ UNICODE_IDENTICAL_TO, /* 0xf0 */
+ UNICODE_PLUS_MINUS_SIGN, /* 0xf1 */
+ UNICODE_GREATER_THAN_OR_EQUAL_TO, /* 0xf2 */
+ UNICODE_LESS_THAN_OR_EQUAL_TO, /* 0xf3 */
+ UNICODE_TOP_HALF_INTEGRAL, /* 0xf4 */
+ UNICODE_BOTTOM_HALF_INTEGRAL, /* 0xf5 */
+ UNICODE_DIVISION_SIGN, /* 0xf6 */
+ UNICODE_ALMOST_EQUAL_TO, /* 0xf7 */
+ UNICODE_DEGREE_SIGN, /* 0xf8 */
+ UNICODE_BULLET_OPERATOR, /* 0xf9 */
+ UNICODE_MIDDLE_DOT, /* 0xfa */
+ UNICODE_SQUARE_ROOT, /* 0xfb */
+ UNICODE_SUPERSCRIPT_LATIN_SMALL_LETTER, /* 0xfc */
+ UNICODE_SUPERSCRIPT_TWO, /* 0xfd */
+ UNICODE_BLACK_SQUARE, /* 0xfe */
+ UNICODE_NO_BREAK_SPACE /* 0xff */
+ };
+
+/* Read the VGA card's memory as IBM 437 font and create a Unicode BDF
+ font from it. If an error occurs, errno is set and NULL is
+ returned. */
+static bdf_font_t
+create_system_font (void)
+{
+ bdf_error_t bdferr;
+ bdf_font_t font;
+ unsigned char bitmap[VGA_FONT_SIZE][VGA_FONT_HEIGHT]; /* 8kB on stack. */
+ int width = vga_get_font_width ();
+ int i;
+
+ /* Add the glyph at position POS to the font for character
+ ENCODING. */
+ void vga_add_glyph (int pos, int encoding)
+ {
+ char name[16];
+ snprintf (name, sizeof (name), "VGA %i", pos);
+
+ if (width == 8)
+ bdferr = bdf_add_glyph (font, name, encoding,
+ 0, 8, 16, 0, 0, bitmap[pos]);
+ else
+ {
+ int i;
+ char glyph_bitmap[32];
+
+ for (i = 0; i < 16; i++)
+ {
+ glyph_bitmap[i * 2] = bitmap[pos][i];
+ if (pos >= VGA_FONT_LGC_BEGIN
+ && pos < VGA_FONT_LGC_BEGIN + VGA_FONT_LGC_COUNT)
+ glyph_bitmap[i * 2 + 1]
+ = (bitmap[pos][i] & 1) ? 0x80 : 0;
+ else
+ glyph_bitmap[i * 2 + 1] = 0;
+ }
+ bdferr = bdf_add_glyph (font, name, encoding,
+ 0, 9, 16, 0, 0, glyph_bitmap);
+ }
+ }
+
+ /* The point size and resolution is arbitrary. */
+ bdferr = bdf_new (&font, 2, 2, "vga-system", 10, 100, 100,
+ width, 16, 0, 0, 0);
+ if (bdferr)
+ {
+ if (bdferr != BDF_SYSTEM_ERROR)
+ errno = EGRATUITOUS;
+ return NULL;
+ }
+
+ vga_read_font_buffer (0, 0, (unsigned char *) bitmap,
+ VGA_FONT_SIZE * VGA_FONT_HEIGHT);
+
+ for (i = 0; i < VGA_FONT_SIZE; i++)
+ if (ibm437_to_unicode[i])
+ {
+ vga_add_glyph (i, ibm437_to_unicode[i]);
+ if (bdferr)
+ break;
+
+ /* Some glyphs are ambivalent. */
+ if (ibm437_to_unicode[i] == UNICODE_GREEK_SMALL_LETTER_BETA)
+ vga_add_glyph (i, UNICODE_LATIN_SMALL_LETTER_SHARP_S);
+ else if (ibm437_to_unicode[i] == UNICODE_GREEK_SMALL_LETTER_MU)
+ vga_add_glyph (i, UNICODE_MICRO_SIGN);
+ if (bdferr)
+ break;
+ }
+
+ if (bdferr)
+ {
+ bdf_destroy (font);
+ if (bdferr != BDF_SYSTEM_ERROR)
+ errno = EGRATUITOUS;
+ return NULL;
+ }
+
+ return font;
+}
+
+
+#if QUAERENDO_INVENIETIS
+#define GNU_HEAD_BEGIN (UNICODE_PRIVATE_USE_AREA + 0x0f00)
+
+static void
+add_gnu_head (bdf_font_t font)
+{
+#define GNU_HEAD_WIDTH 6
+ static unsigned char gnu_head[][GNU_HEAD_WIDTH] =
+ {
+ { 255, 255, 255, 255, 255, 255 }, { 255, 0, 127, 255, 252, 31 },
+ { 252, 0, 31, 255, 224, 7 }, { 248, 0, 7, 255, 0, 3 },
+ { 240, 0, 15, 255, 128, 3 }, { 240, 31, 255, 255, 252, 1 },
+ { 224, 63, 241, 255, 255, 1 }, { 192, 127, 128, 96, 255, 129 },
+ { 192, 255, 0, 0, 63, 193 }, { 192, 254, 0, 0, 31, 193 },
+ { 192, 252, 0, 0, 15, 193 }, { 192, 248, 0, 0, 15, 193 },
+ { 192, 248, 0, 0, 7, 129 }, { 192, 96, 63, 131, 192, 1 },
+ { 192, 1, 227, 98, 112, 1 }, { 192, 3, 195, 244, 176, 3 },
+ { 224, 7, 221, 125, 248, 3 }, { 240, 15, 184, 124, 120, 7 },
+ { 240, 15, 248, 124, 60, 15 }, { 248, 15, 220, 254, 124, 127 },
+ { 252, 31, 223, 255, 254, 127 }, { 255, 159, 159, 255, 31, 191 },
+ { 255, 223, 159, 255, 35, 63 }, { 255, 191, 127, 195, 152, 127 },
+ { 255, 188, 255, 156, 199, 255 }, { 255, 96, 253, 134, 115, 255 },
+ { 254, 134, 251, 227, 251, 255 }, { 254, 46, 254, 249, 251, 255 },
+ { 255, 238, 126, 127, 231, 255 }, { 255, 239, 127, 127, 207, 255 },
+ { 255, 239, 63, 63, 231, 255 }, { 255, 247, 159, 158, 15, 255 },
+ { 255, 247, 207, 193, 159, 255 }, { 255, 247, 223, 255, 223, 255 },
+ { 255, 243, 199, 252, 31, 255 }, { 255, 251, 227, 224, 63, 255 },
+ { 255, 253, 241, 240, 255, 255 }, { 255, 252, 244, 126, 255, 255 },
+ { 255, 254, 121, 122, 255, 255 }, { 255, 255, 252, 48, 255, 255 },
+ { 255, 255, 252, 35, 255, 255 }, { 255, 255, 249, 1, 127, 255 },
+ { 255, 255, 251, 0, 127, 255 }, { 255, 255, 255, 128, 255, 255 },
+ { 255, 255, 255, 255, 255, 255 }
+ };
+ /* Height of a single glyph, truncated to fit VGA glyph slot. */
+ int height = (font->bbox.height > 32) ? 32 : font->bbox.height;
+ /* Width of a single glyph in byte. */
+ int width = (font->bbox.width + 7) / 8;
+ /* Number of rows in bitmap. */
+ int rows = sizeof (gnu_head) / sizeof (gnu_head[0]);
+ /* Number of rows of glyphs necessary. */
+ int nr = (rows + height - 1) / height;
+ int row, col;
+
+ /* Check that all those glyphs are free in the private area. */
+ if (nr * GNU_HEAD_WIDTH > GNU_HEAD_BEGIN - UNICODE_PRIVATE_USE_AREA + 1)
+ return;
+ for (int i = 0; i < nr * GNU_HEAD_WIDTH; i++)
+ if (bdf_find_glyph (font, (int) GNU_HEAD_BEGIN + i, 0)
+ || bdf_find_glyph (font, -1, (int) GNU_HEAD_BEGIN + i))
+ return;
+
+ for (row = 0; row < nr; row++)
+ for (col = 0; col < GNU_HEAD_WIDTH; col++)
+ {
+ char bitmap[font->bbox.height][width];
+ char name[] = "GNU Head ..........";
+
+ /* strlen ("GNU Head ") == 9. */
+ sprintf (&name[9], "%i", row * GNU_HEAD_WIDTH + col);
+
+ memset (bitmap, 0, sizeof (bitmap));
+ for (int j = 0; j < height && row * height + j < rows; j++)
+ bitmap[j][0] = gnu_head[row * height + j][col];
+
+ if (bdf_add_glyph (font, name,
+ GNU_HEAD_BEGIN + row * GNU_HEAD_WIDTH + col,
+ 0, font->bbox.width, font->bbox.height,
+ 0, 0, (unsigned char *) bitmap))
+ return;
+ }
+}
+#endif
+
+/* Create a new dynafont object, which uses glyphs from the font FONT
+ (which should be 8 or 9 pixels wide and 13 to 16 pixels high).
+ SIZE is either 256 or 512, and specifies the number of available
+ glyphs in the font cache. The object is returned in DYNAFONT.
+
+ The font is consumed. If FONT is the null pointer, the VGA card's
+ memory is converted to a font according to the VGA default map (IBM
+ Code Page 437). */
+error_t
+dynafont_new (bdf_font_t font, bdf_font_t font_italic, bdf_font_t font_bold,
+ bdf_font_t font_bold_italic, int size, int width,
+ dynafont_t *dynafont)
+{
+ dynafont_t df;
+ struct bdf_glyph *glyph = NULL;
+
+ if (!font)
+ font = create_system_font ();
+ if (!font || !font->bbox.height)
+ return errno;
+ if (!width)
+ width = font->bbox.width;
+ if ((width % 8) == 0)
+ width = 8;
+ if (width != 8 && width != 9)
+ return EINVAL;
+
+ df = malloc (sizeof *df);
+ if (!df)
+ return ENOMEM;
+
+#if QUAERENDO_INVENIETIS
+ add_gnu_head (font);
+#endif
+ bdf_sort_glyphs (font);
+ df->font = font;
+ df->font_italic = font_italic;
+ df->font_bold = font_bold;
+ df->font_bold_italic = font_bold_italic;
+ df->size = size;
+ df->width = width;
+ df->cursor_standout = 0;
+
+ df->charmap_data = calloc (size, sizeof (struct mapped_character));
+ if (!df->charmap_data)
+ {
+ free (df);
+ return ENOMEM;
+ }
+
+ df->vga_font = malloc (sizeof (vga_font_glyph) * size);
+ if (!df->vga_font)
+ {
+ free (df->charmap_data);
+ free (df);
+ return ENOMEM;
+ }
+
+ hurd_ihash_init (&df->charmap, offsetof (struct mapped_character, locp));
+
+ if (width == 9)
+ {
+ /* 32 from 256 font slots are for horizontal line graphic
+ characters. */
+ df->use_lgc = 1;
+ df->vga_font_free_indices = df->size
+ - (df->size / 256) * VGA_FONT_LGC_COUNT;
+ df->vga_font_last_free_index = 0;
+ df->vga_font_free_indices_lgc = (df->size / 256) * VGA_FONT_LGC_COUNT;
+ df->vga_font_last_free_index_lgc = VGA_FONT_LGC_BEGIN;
+ }
+ else
+ {
+ df->use_lgc = 0;
+ df->vga_font_free_indices = df->size;
+ df->vga_font_last_free_index = 0;
+ df->vga_font_free_indices_lgc = 0;
+ df->vga_font_last_free_index_lgc = 0;
+ }
+
+ /* Ensure that ASCII is always available 1-to-1, for kernel messages. */
+ for (int c = ' '; c <= '~'; c++)
+ {
+ glyph = bdf_find_glyph (df->font, c, 0);
+ if (!glyph)
+ glyph = bdf_find_glyph (df->font, -1, c);
+ if (glyph)
+ {
+ struct mapped_character *chr = &df->charmap_data[c];
+ df->vga_font_free_indices--;
+ chr->refs = 1;
+
+ for (int i = 0; i < ((glyph->bbox.height > 32)
+ ? 32 : glyph->bbox.height); i++)
+ df->vga_font[c][i]
+ = glyph->bitmap[i * ((glyph->bbox.width + 7) / 8)];
+ if (glyph->bbox.height < 32)
+ memset (((char *) df->vga_font[c])
+ + glyph->bbox.height, 0, 32 - glyph->bbox.height);
+
+ /* Update the hash table. */
+ hurd_ihash_add (&df->charmap, c, chr);
+ }
+ }
+
+ /* Ensure that we always have the replacement character
+ available. */
+ {
+ struct mapped_character *chr = &df->charmap_data[FONT_INDEX_UNKNOWN];
+ df->vga_font_free_indices--;
+ chr->refs = 1;
+
+ glyph = bdf_find_glyph (df->font, UNICODE_REPLACEMENT_CHARACTER, 0);
+ if (!glyph)
+ glyph = bdf_find_glyph (df->font, -1, UNICODE_REPLACEMENT_CHARACTER);
+ if (glyph)
+ {
+ for (int i = 0; i < ((glyph->bbox.height > 32)
+ ? 32 : glyph->bbox.height); i++)
+ df->vga_font[FONT_INDEX_UNKNOWN][i]
+ = glyph->bitmap[i * ((glyph->bbox.width + 7) / 8)];
+ if (glyph->bbox.height < 32)
+ memset (((char *) df->vga_font[FONT_INDEX_UNKNOWN])
+ + glyph->bbox.height, 0, 32 - glyph->bbox.height);
+
+ /* Update the hash table. */
+ hurd_ihash_add (&df->charmap, UNICODE_REPLACEMENT_CHARACTER, chr);
+ }
+ else
+ {
+ int i;
+ char *gl = df->vga_font[FONT_INDEX_UNKNOWN];
+ /* XXX Take font height into account. */
+ gl[0] = 0x7E; /* ****** */
+ gl[1] = 0xC3; /* ** ** */
+ gl[2] = 0x99; /* * ** * */
+ gl[3] = 0x99; /* * ** * */
+ gl[4] = 0xF9; /* ***** * */
+ gl[5] = 0xF3; /* **** ** */
+ gl[6] = 0xF3; /* *** *** */
+ gl[7] = 0xE7; /* *** *** */
+ gl[8] = 0xFF; /* ******** */
+ gl[9] = 0xE7; /* *** *** */
+ gl[10] = 0xE7; /* *** *** */
+ gl[11] = 0x7E; /* ****** */
+ for (i = 12; i < 32; i++)
+ gl[i] = 0;
+ }
+ }
+
+ *dynafont = df;
+ return 0;
+}
+
+
+/* Release a dynafont object and its associated resources. */
+void
+dynafont_free (dynafont_t df)
+{
+ if (active_dynafont == df)
+ active_dynafont = NULL;
+
+ bdf_destroy (df->font);
+ if (df->font_italic)
+ bdf_destroy (df->font_italic);
+ if (df->font_bold)
+ bdf_destroy (df->font_bold);
+ if (df->font_bold_italic)
+ bdf_destroy (df->font_bold_italic);
+ hurd_ihash_destroy (&df->charmap);
+ free (df->charmap_data);
+ free (df->vga_font);
+ free (df);
+}
+
+
+/* Determine if CHR is a character that needs to be continuous in the
+ horizontal direction by repeating the last (eighth) column in
+ 9-pixel-width mode. */
+static inline int
+is_lgc (wchar_t chr)
+{
+ /* This list must be sorted for bsearch! */
+ static wchar_t horiz_glyphs[] =
+ {
+ UNICODE_BOX_DRAWINGS_LIGHT_HORIZONTAL, /* 0x2500 */
+ UNICODE_BOX_DRAWINGS_HEAVY_HORIZONTAL, /* 0x2501 */
+ UNICODE_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT, /* 0x250c */
+ UNICODE_BOX_DRAWINGS_DOWN_LIGHT_AND_RIGHT_HEAVY, /* 0x250d */
+ UNICODE_BOX_DRAWINGS_DOWN_HEAVY_AND_RIGHT_LIGHT, /* 0x250e */
+ UNICODE_BOX_DRAWINGS_HEAVY_DOWN_AND_RIGHT, /* 0x250f */
+ UNICODE_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT, /* 0x2514 */
+ UNICODE_BOX_DRAWINGS_UP_LIGHT_AND_RIGHT_HEAVY, /* 0x2515 */
+ UNICODE_BOX_DRAWINGS_UP_HEAVY_AND_RIGHT_LIGHT, /* 0x2516 */
+ UNICODE_BOX_DRAWINGS_HEAVY_UP_AND_RIGHT, /* 0x2517 */
+ UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_RIGHT, /* 0x251c */
+ UNICODE_BOX_DRAWINGS_VERTICAL_LIGHT_AND_RIGHT_HEAVY, /* 0x251d */
+ UNICODE_BOX_DRAWINGS_UP_HEAVY_AND_RIGHT_UP_LIGHT, /* 0x251e */
+ UNICODE_BOX_DRAWINGS_DOWN_HEAVY_AND_RIGHT_UP_LIGHT, /* 0x251f */
+ UNICODE_BOX_DRAWINGS_VERTICAL_HEAVY_AND_RIGHT_LIGHT, /* 0x2520 */
+ UNICODE_BOX_DRAWINGS_DOWN_LIGHT_AND_RIGHT_UP_HEAVY, /* 0x2521 */
+ UNICODE_BOX_DRAWINGS_UP_LIGHT_AND_RIGHT_DOWN_HEAVY, /* 0x2522 */
+ UNICODE_BOX_DRAWINGS_HEAVY_VERTICAL_AND_RIGHT, /* 0x2523 */
+ UNICODE_BOX_DRAWINGS_LIGHT_DOWN_AND_HORIZONTAL, /* 0x252c */
+ UNICODE_BOX_DRAWINGS_LEFT_HEAVY_AND_RIGHT_DOWN_LIGHT, /* 0x252d */
+ UNICODE_BOX_DRAWINGS_RIGHT_HEAVY_AND_LEFT_DOWN_LIGHT, /* 0x252e */
+ UNICODE_BOX_DRAWINGS_DOWN_LIGHT_AND_HORIZONTAL_HEAVY, /* 0x252f */
+ UNICODE_BOX_DRAWINGS_DOWN_HEAVY_AND_HORIZONTAL_LIGHT, /* 0x2530 */
+ UNICODE_BOX_DRAWINGS_RIGHT_LIGHT_AND_LEFT_DOWN_HEAVY, /* 0x2531 */
+ UNICODE_BOX_DRAWINGS_LEFT_LIGHT_AND_RIGHT_DOWN_HEAVY, /* 0x2532 */
+ UNICODE_BOX_DRAWINGS_HEAVY_DOWN_AND_HORIZONTAL, /* 0x2533 */
+ UNICODE_BOX_DRAWINGS_LIGHT_UP_AND_HORIZONTAL, /* 0x2534 */
+ UNICODE_BOX_DRAWINGS_LEFT_HEAVY_AND_RIGHT_UP_LIGHT, /* 0x2535 */
+ UNICODE_BOX_DRAWINGS_RIGHT_HEAVY_AND_LEFT_UP_LIGHT, /* 0x2536 */
+ UNICODE_BOX_DRAWINGS_UP_LIGHT_AND_HORIZONTAL_HEAVY, /* 0x2537 */
+ UNICODE_BOX_DRAWINGS_UP_HEAVY_AND_HORIZONTAL_LIGHT, /* 0x2538 */
+ UNICODE_BOX_DRAWINGS_RIGHT_LIGHT_AND_LEFT_UP_HEAVY, /* 0x2539 */
+ UNICODE_BOX_DRAWINGS_LEFT_LIGHT_AND_RIGHT_UP_HEAVY, /* 0x253a */
+ UNICODE_BOX_DRAWINGS_HEAVY_UP_AND_HORIZONTAL, /* 0x253b */
+ UNICODE_BOX_DRAWINGS_LIGHT_VERTICAL_AND_HORIZONTAL, /* 0x253c */
+ UNICODE_BOX_DRAWINGS_LEFT_HEAVY_AND_RIGHT_VERTICAL_LIGHT, /* 0x253d */
+ UNICODE_BOX_DRAWINGS_RIGHT_HEAVY_AND_LEFT_VERTICAL_LIGHT, /* 0x253e */
+ UNICODE_BOX_DRAWINGS_VERTICAL_LIGHT_AND_HORIZONTAL_HEAVY, /* 0x253f */
+ UNICODE_BOX_DRAWINGS_UP_HEAVY_AND_DOWN_HORIZONTAL_LIGHT, /* 0x2540 */
+ UNICODE_BOX_DRAWINGS_DOWN_HEAVY_AND_UP_HORIZONTAL_LIGHT, /* 0x2541 */
+ UNICODE_BOX_DRAWINGS_VERTICAL_HEAVY_AND_HORIZONTAL_LIGHT, /* 0x2542 */
+ UNICODE_BOX_DRAWINGS_LEFT_UP_HEAVY_AND_RIGHT_DOWN_LIGHT, /* 0x2543 */
+ UNICODE_BOX_DRAWINGS_RIGHT_UP_HEAVY_AND_LEFT_DOWN_LIGHT, /* 0x2544 */
+ UNICODE_BOX_DRAWINGS_LEFT_DOWN_HEAVY_AND_RIGHT_UP_LIGHT, /* 0x2545 */
+ UNICODE_BOX_DRAWINGS_RIGHT_DOWN_HEAVY_AND_LEFT_UP_LIGHT, /* 0x2546 */
+ UNICODE_BOX_DRAWINGS_DOWN_LIGHT_AND_UP_HORIZONTAL_HEAVY, /* 0x2547 */
+ UNICODE_BOX_DRAWINGS_UP_LIGHT_AND_DOWN_HORIZONTAL_HEAVY, /* 0x2548 */
+ UNICODE_BOX_DRAWINGS_RIGHT_LIGHT_AND_LEFT_VERTICAL_HEAVY, /* 0x2549 */
+ UNICODE_BOX_DRAWINGS_LEFT_LIGHT_AND_RIGHT_VERTICAL_HEAVY, /* 0x254a */
+ UNICODE_BOX_DRAWINGS_HEAVY_VERTICAL_AND_HORIZONTAL, /* 0x254b */
+ UNICODE_BOX_DRAWINGS_DOUBLE_HORIZONTAL, /* 0x2550 */
+ UNICODE_BOX_DRAWINGS_DOWN_SINGLE_AND_RIGHT_DOUBLE, /* 0x2552 */
+ UNICODE_BOX_DRAWINGS_DOWN_DOUBLE_AND_RIGHT_SINGLE, /* 0x2553 */
+ UNICODE_BOX_DRAWINGS_DOUBLE_DOWN_AND_RIGHT, /* 0x2554 */
+ UNICODE_BOX_DRAWINGS_UP_SINGLE_AND_RIGHT_DOUBLE, /* 0x2558 */
+ UNICODE_BOX_DRAWINGS_UP_DOUBLE_AND_RIGHT_SINGLE, /* 0x2559 */
+ UNICODE_BOX_DRAWINGS_DOUBLE_UP_AND_RIGHT, /* 0x255a */
+ UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_RIGHT_DOUBLE, /* 0x255e */
+ UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_RIGHT_SINGLE, /* 0x255f */
+ UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_RIGHT, /* 0x2560 */
+ UNICODE_BOX_DRAWINGS_DOWN_SINGLE_AND_HORIZONTAL_DOUBLE, /* 0x2564 */
+ UNICODE_BOX_DRAWINGS_DOWN_DOUBLE_AND_HORIZONTAL_SINGLE, /* 0x2565 */
+ UNICODE_BOX_DRAWINGS_DOUBLE_DOWN_AND_HORIZONTAL, /* 0x2566 */
+ UNICODE_BOX_DRAWINGS_UP_SINGLE_AND_HORIZONTAL_DOUBLE, /* 0x2567 */
+ UNICODE_BOX_DRAWINGS_UP_DOUBLE_AND_HORIZONTAL_SINGLE, /* 0x2568 */
+ UNICODE_BOX_DRAWINGS_DOUBLE_UP_AND_HORIZONTAL, /* 0x2569 */
+ UNICODE_BOX_DRAWINGS_VERTICAL_SINGLE_AND_HORIZONTAL_DOUBLE, /* 0x256a */
+ UNICODE_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_HORIZONTAL_SINGLE, /* 0x256b */
+ UNICODE_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_HORIZONTAL, /* 0x256c */
+ UNICODE_BOX_DRAWINGS_LIGHT_ARC_DOWN_AND_RIGHT, /* 0x256d */
+ UNICODE_BOX_DRAWINGS_LIGHT_ARC_UP_AND_RIGHT, /* 0x2570 */
+#if 0
+ /* The diagonal lines don't look much better with or without the
+ ninth column. */
+ /* 0x2571 */
+ UNICODE_BOX_DRAWINGS_LIGHT_DIAGONAL_UPPER_RIGHT_TO_LOWER_LEFT,
+ /* 0x2572 */
+ UNICODE_BOX_DRAWINGS_LIGHT_DIAGONAL_UPPER_LEFT_TO_LOWER_RIGHT,
+ UNICODE_BOX_DRAWINGS_LIGHT_DIAGONAL_CROSS, /* 0x2573 */
+#endif
+ UNICODE_BOX_DRAWINGS_LIGHT_RIGHT, /* 0x2576 */
+ UNICODE_BOX_DRAWINGS_HEAVY_RIGHT, /* 0x257a */
+ UNICODE_BOX_DRAWINGS_LIGHT_LEFT_AND_HEAVY_RIGHT, /* 0x257c */
+ UNICODE_BOX_DRAWINGS_HEAVY_LEFT_AND_LIGHT_RIGHT, /* 0x257e */
+ UNICODE_UPPER_HALF_BLOCK, /* 0x2580 */
+ UNICODE_LOWER_ONE_EIGHTH_BLOCK, /* 0x2581 */
+ UNICODE_LOWER_ONE_QUARTER_BLOCK, /* 0x2582 */
+ UNICODE_LOWER_THREE_EIGHTHS_BLOCK, /* 0x2583 */
+ UNICODE_LOWER_HALF_BLOCK, /* 0x2584 */
+ UNICODE_LOWER_FIVE_EIGHTHS_BLOCK, /* 0x2585 */
+ UNICODE_LOWER_THREE_QUARTERS_BLOCK, /* 0x2586 */
+ UNICODE_LOWER_SEVEN_EIGHTHS_BLOCK, /* 0x2587 */
+ UNICODE_FULL_BLOCK, /* 0x2588 */
+ UNICODE_RIGHT_HALF_BLOCK, /* 0x2590 */
+#if 0
+ /* The shades don't look much better with or without the ninth
+ column. */
+ UNICODE_LIGHT_SHADE, /* 0x2591 */
+ UNICODE_MEDIUM_SHADE, /* 0x2592 */
+ UNICODE_DARK_SHADE, /* 0x2593 */
+#endif
+ UNICODE_BLACK_SQUARE, /* 0x25a0 */
+ UNICODE_UPPER_ONE_EIGHTH_BLOCK, /* 0x2594 */
+ UNICODE_RIGHT_ONE_EIGHTH_BLOCK, /* 0x2595 */
+ UNICODE_QUADRANT_LOWER_RIGHT, /* 0x2597 */
+ UNICODE_QUADRANT_UPPER_LEFT_AND_LOWER_LEFT_AND_LOWER_RIGHT, /* 0x2599 */
+ UNICODE_QUADRANT_UPPER_LEFT_AND_LOWER_RIGHT, /* 0x259a */
+ UNICODE_QUADRANT_UPPER_LEFT_AND_UPPER_RIGHT_AND_LOWER_LEFT, /* 0x259b */
+ UNICODE_QUADRANT_UPPER_LEFT_AND_UPPER_RIGHT_AND_LOWER_RIGHT, /* 0x259c */
+ UNICODE_QUADRANT_UPPER_RIGHT, /* 0x259d */
+ UNICODE_QUADRANT_UPPER_RIGHT_AND_LOWER_LEFT, /* 0x259e */
+ UNICODE_QUADRANT_UPPER_RIGHT_AND_LOWER_LEFT_AND_LOWER_RIGHT, /* 0x259f */
+ };
+
+ int cmp_wchar (const void *a, const void *b)
+ {
+ const wchar_t *wa = (const wchar_t *) a;
+ const wchar_t *wb = (const wchar_t *) b;
+ return (*wa > *wb) - (*wa < *wb);
+ }
+
+#if QUAERENDO_INVENIETIS
+ /* XXX The (maximum) size is hard coded. */
+ if (chr >= GNU_HEAD_BEGIN && chr <= GNU_HEAD_BEGIN + 50)
+ return 1;
+#endif
+
+ return bsearch (&chr, horiz_glyphs,
+ sizeof (horiz_glyphs) / sizeof (horiz_glyphs[0]),
+ sizeof (horiz_glyphs[0]), cmp_wchar) ? 1 : 0;
+}
+
+
+/* Try to look up glyph CHR in font FONT of dynafont DF with attribute
+ ATTR. This is a helper function for dynafont_lookup. Returns 1 on
+ success, 0 otherwise. */
+static int
+dynafont_lookup_internal (dynafont_t df, bdf_font_t font,
+ wchar_t wide_chr, wchar_t attr, int *rpos)
+{
+ /* When hashing the character, we mix in the font attribute. */
+ struct mapped_character *chr = hurd_ihash_find (&df->charmap,
+ (int) (wide_chr | attr));
+ int lgc;
+ struct bdf_glyph *glyph;
+ int pos;
+ int found = 0;
+
+ lgc = df->use_lgc ? is_lgc (wide_chr) : 0;
+
+ if (chr)
+ {
+ if (!chr->refs++)
+ {
+ if (lgc)
+ df->vga_font_free_indices_lgc--;
+ else
+ df->vga_font_free_indices--;
+ }
+ /* Return the index of CHR. */
+ *rpos = chr - df->charmap_data;
+ return 1;
+ }
+
+ /* The character is not currently mapped. Look for an empty slot
+ and add it. */
+ if ((lgc && !df->vga_font_free_indices_lgc)
+ || (!lgc && !df->vga_font_free_indices))
+ return 0;
+
+ glyph = bdf_find_glyph (font, (int) (wide_chr & ~CONS_WCHAR_CONTINUED), 0);
+ if (!glyph)
+ glyph = bdf_find_glyph (font, -1, (int) (wide_chr & ~CONS_WCHAR_CONTINUED));
+ if (!glyph)
+ return 0;
+
+ if (lgc)
+ {
+ int start_pos = df->vga_font_last_free_index_lgc + 1;
+ if ((start_pos % VGA_FONT_SIZE)
+ == VGA_FONT_LGC_BEGIN + VGA_FONT_LGC_COUNT)
+ {
+ start_pos += VGA_FONT_SIZE - VGA_FONT_LGC_COUNT;
+ start_pos %= df->size;
+ }
+ pos = start_pos;
+
+ do
+ {
+ if (df->charmap_data[pos].refs == 0)
+ {
+ found = 1;
+ break;
+ }
+ pos++;
+ if ((pos % VGA_FONT_SIZE) == VGA_FONT_LGC_BEGIN + VGA_FONT_LGC_COUNT)
+ {
+ pos += VGA_FONT_SIZE - VGA_FONT_LGC_COUNT;
+ pos %= df->size;
+ }
+ }
+ while (pos != start_pos);
+
+ assert (found);
+ df->vga_font_free_indices_lgc--;
+ df->vga_font_last_free_index_lgc = pos;
+ }
+ else
+ {
+ int start_pos = (df->vga_font_last_free_index + 1) % df->size;
+ if (df->use_lgc && (start_pos % VGA_FONT_SIZE) == VGA_FONT_LGC_BEGIN)
+ start_pos += VGA_FONT_LGC_COUNT;
+ pos = start_pos;
+
+ do
+ {
+ if (df->charmap_data[pos].refs == 0)
+ {
+ found = 1;
+ break;
+ }
+ pos = (pos + 1) % df->size;
+ if (df->use_lgc && (pos % VGA_FONT_SIZE) == VGA_FONT_LGC_BEGIN)
+ pos += VGA_FONT_LGC_COUNT;
+ }
+ while (pos != start_pos);
+
+ assert (found);
+ df->vga_font_free_indices--;
+ df->vga_font_last_free_index = pos;
+ }
+
+ /* Ok, we found a new entry, use it. */
+ chr = &df->charmap_data[pos];
+ chr->refs = 1;
+ chr->character = (wide_chr | attr);
+
+ /* Copy the glyph bitmap, taking into account double-width characters. */
+ {
+ int height = (glyph->bbox.height > 32) ? 32 : glyph->bbox.height;
+ int bwidth = (glyph->bbox.width + 7) / 8;
+ int ofs = (bwidth >= 2) && (wide_chr & CONS_WCHAR_CONTINUED);
+
+ for (int i = 0; i < height; i++)
+ df->vga_font[pos][i] = glyph->bitmap[i * bwidth + ofs];
+ if (height < 32)
+ memset (&df->vga_font[pos][height], 0, 32 - height);
+ }
+
+ if (active_dynafont == df)
+ vga_write_font_buffer (0, pos, df->vga_font[pos],
+ VGA_FONT_HEIGHT);
+ /* Update the hash table. */
+ if (chr->locp)
+ hurd_ihash_locp_remove (&df->charmap, chr->locp);
+ hurd_ihash_add (&df->charmap, (int) (wide_chr | attr), chr);
+ *rpos = pos;
+ return 1;
+}
+
+/* Look up the vga font index for an UCS-4 character. If not already
+ mapped, try to find space for a new entry and add the mapping.
+ Acquires an additional reference to the character. Might return
+ the glyph for the unrepresentable character if the glyph is ot
+ available for this character or no free slot is available right
+ now. In the former case, some information gets lost (to do
+ otherwise, one would have to either assign one of the scarce font
+ indices to each undisplayable character value on screen, or to
+ store the whole scrollback buffer as wide chars as well to recover
+ the lost info from that copy of the original text. */
+int
+dynafont_lookup (dynafont_t df, conchar_t *conchr)
+{
+ wchar_t attr = (conchr->attr.italic ? WCHAR_ITALIC : 0)
+ | (conchr->attr.bold ? WCHAR_BOLD : 0);
+ int found = 0;
+ int pos = FONT_INDEX_UNKNOWN;
+
+ if (attr == (WCHAR_BOLD | WCHAR_ITALIC) && df->font_bold_italic)
+ found = dynafont_lookup_internal (df, df->font_bold_italic,
+ conchr->chr, WCHAR_BOLD | WCHAR_ITALIC,
+ &pos);
+ /* This order will prefer bold over italic if both are requested but
+ are not available at the same time. The other order is just as
+ good. XXX. */
+ if (!found && (attr & WCHAR_BOLD) && df->font_bold)
+ found = dynafont_lookup_internal (df, df->font_bold,
+ conchr->chr, WCHAR_BOLD, &pos);
+ if (!found && (attr & WCHAR_ITALIC) && df->font_italic)
+ found = dynafont_lookup_internal (df, df->font_italic,
+ conchr->chr, WCHAR_ITALIC, &pos);
+ if (!found)
+ found = dynafont_lookup_internal (df, df->font, conchr->chr, 0, &pos);
+ if (!found)
+ {
+ df->charmap_data[FONT_INDEX_UNKNOWN].refs++;
+ pos = FONT_INDEX_UNKNOWN;
+ }
+ return pos;
+}
+
+
+/* Release a reference to the glyph VGA_FONT_INDEX in dynafont DF. */
+void
+dynafont_release (dynafont_t df, int vga_font_index)
+{
+ if (! --df->charmap_data[vga_font_index].refs)
+ {
+ if (df->use_lgc
+ && is_lgc (df->charmap_data[vga_font_index].character & WCHAR_MASK))
+ df->vga_font_free_indices_lgc++;
+ else
+ df->vga_font_free_indices++;
+ }
+}
+
+
+
+/* Set the cursor to normal if STANDOUT is zero, or to a block cursor
+ otherwise. */
+void
+dynafont_set_cursor (dynafont_t df, int standout)
+{
+ int height = (df->font->bbox.height > 32) ? 32 : df->font->bbox.height;
+
+ df->cursor_standout = standout;
+
+ if (df == active_dynafont)
+ {
+ if (standout)
+ vga_set_cursor_size (1, height - 1);
+ else
+ vga_set_cursor_size ((height >= 2) ? height - 2 : 0, height - 1);
+ }
+}
+
+
+/* Load the VGA font to the card and make it active. */
+void
+dynafont_activate (dynafont_t df)
+{
+ int height = (df->font->bbox.height > 32) ? 32 : df->font->bbox.height;
+
+ vga_write_font_buffer (0, 0, (char *) df->vga_font,
+ df->size * VGA_FONT_HEIGHT);
+ vga_select_font_buffer (0, (df->size == 512) ? 1 : 0);
+
+ /* XXX Changing the font height below 13 or above 16 will cause
+ display problems for the user if we don't also program the video
+ mode timings. The standard font height for 80x25 is 16. */
+ vga_set_font_height (height);
+ vga_set_font_width (df->width);
+
+ active_dynafont = df;
+ dynafont_set_cursor (df, df->cursor_standout);
+}
+
+
+#if 0
+/* XXX This code is deactivated because it needs to be changed to
+ allow the italic and bold code, too. */
+
+/* Change the font used by dynafont DF to FONT. This transformation
+ is initially loss-less, if the new font can't display some
+ characters on the screen, you can always go back to a font that
+ does and get the glyphs for those characters back. (However, the
+ comments in dynafont_lookup hold for glyphs looked up after the font
+ change.) */
+void
+dynafont_change_font (dynafont_t df, bdf_font_t font)
+{
+ int i;
+
+ df->font = font;
+ for (i = 0; i < df->size; i++)
+ {
+ /* If we don't derive the unknown or space glyph from the font,
+ we don't update it. */
+#ifndef ENCODING_UNKNOWN
+ if (i == FONT_INDEX_UNKNOWN)
+ continue;
+#endif
+ if (! df->charmap_data[i].refs)
+ {
+ /* The glyph is not used. If it is mapped, we need to
+ remove the mapping to invalidate the glyph. */
+ if (df->charmap_data[i].locp)
+ {
+
+ hurd_ihash_locp_remove (&df->charmap, df->charmap_data[i].locp);
+ df->charmap_data[i].locp = NULL;
+ }
+ }
+
+ else
+ {
+ /* The glyph is mapped and in use. We will not destroy the
+ mapping, but just override the glyph in the VGA font.
+ This way the user can recover from loading a bad font by
+ going back to a better one. */
+ struct bdf_glyph *glyph;
+ glyph = bdf_find_glyph (df->font,
+ (int) df->charmap_data[i].character, 0);
+ if (!glyph)
+ glyph = bdf_find_glyph (df->font, -1,
+ (int) df->charmap_data[i].character);
+ if (!glyph)
+ {
+#ifdef ENCODING_UNKNOWN
+ /* The new font doesn't have a representation for the
+ unknown glyph at the desired place, so keep the old
+ one. XXX Should load the built-in one here. */
+ if (i == FONT_INDEX_UNKNOWN)
+ continue;
+#endif
+ memcpy (df->vga_font[i], df->vga_font[FONT_INDEX_UNKNOWN], 32);
+ }
+ else
+ {
+ /* XXX Take font size and glyph size into account. */
+ for (int j = 0; j < ((glyph->bbox.height > 32)
+ ? 32 : glyph->bbox.height); j++)
+ df->vga_font[i][j]
+ = glyph->bitmap[j * ((glyph->bbox.width + 7) / 8)];
+ if (glyph->bbox.height < 32)
+ memset (((char *) df->vga_font[i]) + glyph->bbox.height,
+ 0, 32 - glyph->bbox.height);
+ }
+ }
+ }
+
+ if (active_dynafont == df)
+ /* Refresh the card info. */
+ dynafont_activate (df);
+}
+#endif
diff --git a/console-client/vga-dynafont.h b/console-client/vga-dynafont.h
new file mode 100644
index 00000000..0e80e047
--- /dev/null
+++ b/console-client/vga-dynafont.h
@@ -0,0 +1,83 @@
+/* vga-dynafont.h - Interface to the dynamic font handling for VGA cards.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef _VGA_DYNAFONT_H_
+#define _VGA_DYNAFONT_H_ 1
+
+#include <wchar.h>
+
+#include "bdf.h"
+
+
+/* The dynafont interface does not do locking on its own, for maximum
+ efficiency it relies on locking by the caller (because usually the
+ caller has some other data structures to lock along with the
+ dynafont. However, it is safe to call two functions on different
+ dynafonts asynchronously. */
+typedef struct dynafont *dynafont_t;
+
+/* The representation for the unknown glyph is always at the same
+ location. */
+#define FONT_INDEX_UNKNOWN 0
+
+/* Create a new dynafont object, which uses glyphs from the font FONT
+ (which must be 8 pixels wide and up to 32 pixels heigh). SIZE is
+ either 256 or 512, and specifies the number of available glyphs in
+ the font cache. The object is returned in DYNAFONT. The caller
+ must ensure the integrity of FONT until the object is destroyed or
+ the font is changed with dynafont_change_font. */
+error_t dynafont_new (bdf_font_t font, bdf_font_t font_italic,
+ bdf_font_t font_bold, bdf_font_t font_bold_italic,
+ int size, int width, dynafont_t *dynafont);
+
+/* Release a dynafont object and its associated resources. */
+void dynafont_free (dynafont_t df);
+
+/* Look up the vga font index for an UCS-4 character. If not already
+ mapped, try to find space for a new entry and add the mapping.
+ Acquires an additional reference to the character. Might return
+ the glyph for the unrepresentable character if the glyph is ot
+ available for this character or no free slot is available right
+ now. In the former case, some information gets lost (to do
+ otherwise, one would have to either assign one of the scarce font
+ indices to each undisplayable character value on screen, or to
+ store the whole scrollback buffer as wide chars as well to recover
+ the lost info from that copy of the original text. */
+int dynafont_lookup (dynafont_t df, conchar_t *chr);
+
+/* Release a reference to the glyph VGA_FONT_INDEX in dynafont DF. */
+void dynafont_release (dynafont_t df, int vga_font_index);
+
+/* Load the VGA font to the card and make it active. */
+void dynafont_activate (dynafont_t df);
+
+/* Set the cursor to normal if STANDOUT is zero, or to a block cursor
+ otherwise. */
+void dynafont_set_cursor (dynafont_t df, int standout);
+
+/* Change the font used by dynafont DF to FONT. This transformation
+ is initially loss-less, if the new font can't display some
+ characters on the screen, you can always go back to a font that
+ does and get the glyphs for those characters back. (However, the
+ comments in dynafont_lookup hold for glyphs looked up after the font
+ change.) */
+void dynafont_change_font (dynafont_t df, bdf_font_t font);
+
+#endif /* _VGA_DYNAFONT_H_ */
diff --git a/console-client/vga-hw.h b/console-client/vga-hw.h
new file mode 100644
index 00000000..b4212e62
--- /dev/null
+++ b/console-client/vga-hw.h
@@ -0,0 +1,144 @@
+/* vga-hw.h - Definitions for the VGA hardware.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef _VGA_HW_H_
+#define _VGA_HW_H_ 1
+
+#define VGA_VIDEO_MEM_BASE_ADDR 0x0b8000
+#define VGA_VIDEO_MEM_LENGTH 0x004000
+
+#define VGA_FONT_BUFFER 8
+#define VGA_FONT_SIZE 256
+#define VGA_FONT_HEIGHT 32
+#define VGA_FONT_LGC_BEGIN 0xc0
+#define VGA_FONT_LGC_COUNT 32
+
+#define VGA_MIN_REG 0x3c0
+#define VGA_MAX_REG 0x3df
+
+/* The sequencer address register selects the sub-register of the
+ sequencer that is accessed through the sequencer data register. */
+#define VGA_SEQ_ADDR_REG 0x3c4
+#define VGA_SEQ_DATA_REG 0x3c5
+
+/* The reset subregister can be used to asynchronously or
+ synchronously halt or clear the sequencer. */
+#define VGA_SEQ_RESET_ADDR 0x00
+#define VGA_SEQ_RESET_ASYNC 0x10 /* Can cause loss of video data. */
+#define VGA_SEQ_RESET_SYNC 0x01
+#define VGA_SEQ_RESET_CLEAR 0x11 /* Sequencer can operate. */
+
+/* The clocking mode subregister. */
+#define VGA_SEQ_CLOCK_MODE_ADDR 0x01
+#define VGA_SEQ_CLOCK_MODE_8 0x01 /* 8-pixel width for fonts. */
+
+/* The map subregister specifies which planes are written to. */
+#define VGA_SEQ_MAP_ADDR 0x02
+#define VGA_SEQ_MAP_PLANE0 0x01
+#define VGA_SEQ_MAP_PLANE1 0x02
+#define VGA_SEQ_MAP_PLANE2 0x04
+#define VGA_SEQ_MAP_PLANE3 0x08
+
+/* The font subregister. */
+#define VGA_SEQ_FONT_ADDR 0x03
+
+/* The memory mode subregister specifies the way that memory is
+ accessed. */
+#define VGA_SEQ_MODE_ADDR 0x04
+#define VGA_SEQ_MODE_EXT 0x02 /* Access 265kB rather than 64kB. */
+#define VGA_SEQ_MODE_SEQUENTIAL 0x04 /* Sequential, not odd/even addr. */
+#define VGA_SEQ_MODE_CHAIN4 0x08 /* Chain 4 addressing. */
+
+
+/* The graphics address register selects the sub-register that is
+ accessed through the graphics data register. */
+#define VGA_GFX_ADDR_REG 0x3ce
+#define VGA_GFX_DATA_REG 0x3cf
+
+/* The map subregister selects the plane to read from. */
+#define VGA_GFX_MAP_ADDR 0x04
+#define VGA_GFX_MAP_PLANE0 0x00
+#define VGA_GFX_MAP_PLANE1 0x01
+#define VGA_GFX_MAP_PLANE2 0x02
+#define VGA_GFX_MAP_PLANE3 0x03
+
+/* The mode subregister selects the memory access mode. */
+#define VGA_GFX_MODE_ADDR 0x05
+#define VGA_GFX_MODE_SHIFT256 0x40
+#define VGA_GFX_MODE_SHIFT 0x20
+#define VGA_GFX_MODE_HOSTOE 0x10
+#define VGA_GFX_MODE_READ0 0x00
+#define VGA_GFX_MODE_READ1 0x08
+#define VGA_GFX_MODE_WRITE0 0x00
+#define VGA_GFX_MODE_WRITE1 0x01
+#define VGA_GFX_MODE_WRITE2 0x02
+#define VGA_GFX_MODE_WRITE3 0x03
+
+/* The miscellaneous subregister. */
+#define VGA_GFX_MISC_ADDR 0x06
+#define VGA_GFX_MISC_GFX 0x01 /* Switch on graphics mode. */
+#define VGA_GFX_MISC_CHAINOE 0x02
+#define VGA_GFX_MISC_A0TOBF 0x00
+#define VGA_GFX_MISC_A0TOAF 0x04
+#define VGA_GFX_MISC_B0TOB7 0x08
+#define VGA_GFX_MISC_B8TOBF 0x0c
+
+
+/* The CRTC Registers. XXX Depends on the I/O Address Select field.
+ However, the only need to use the other values is for compatibility
+ with monochrome adapters. */
+#define VGA_CRT_ADDR_REG 0x3d4
+#define VGA_CRT_DATA_REG 0x3d5
+
+/* The maximum scan line subregister. */
+#define VGA_CRT_MAX_SCAN_LINE 0x09
+
+/* The cursor start subregister. */
+#define VGA_CRT_CURSOR_START 0x0a
+#define VGA_CRT_CURSOR_DISABLE 0x20
+
+/* The cursor end subregister. */
+#define VGA_CRT_CURSOR_END 0x0b
+
+/* The cursor position subregisters. */
+#define VGA_CRT_CURSOR_HIGH 0x0e
+#define VGA_CRT_CURSOR_LOW 0x0f
+
+
+/* The DAC Registers. */
+#define VGA_DAC_WRITE_ADDR_REG 0x3c8
+#define VGA_DAC_READ_ADDR_REG 0x3c7
+#define VGA_DAC_DATA_REG 0x3c9
+
+
+/* The Attribute Registers. */
+#define VGA_ATTR_ADDR_DATA_REG 0x3c0
+#define VGA_ATTR_DATA_READ_REG 0x3c1
+
+/* The Attribute Mode Control subregister. */
+#define VGA_ATTR_MODE_ADDR 0x10
+#define VGA_ATTR_MODE_LGE 0x04
+
+#define VGA_ATTR_ENABLE_ADDR 0x20
+
+/* Other junk. */
+#define VGA_INPUT_STATUS_1_REG 0x3da
+
+#endif /* _VGA_HW_H_ */
diff --git a/console-client/vga-support.c b/console-client/vga-support.c
new file mode 100644
index 00000000..e8497f82
--- /dev/null
+++ b/console-client/vga-support.c
@@ -0,0 +1,479 @@
+/* vga-support.c - VGA hardware access.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/io.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "vga-hw.h"
+#include "vga-support.h"
+
+
+/* The base of the video memory mapping. */
+char *vga_videomem;
+
+/* The saved state of the VGA card. */
+struct vga_state
+{
+ unsigned char seq_clock_mode;
+ unsigned char seq_map;
+ unsigned char seq_font;
+ unsigned char seq_mode;
+
+ unsigned char gfx_map;
+ unsigned char gfx_mode;
+ unsigned char gfx_misc;
+
+ unsigned char crt_max_scan_line;
+ unsigned char crt_cursor_start;
+ unsigned char crt_cursor_end;
+ unsigned char crt_cursor_high;
+ unsigned char crt_cursor_low;
+
+ unsigned char attr_mode;
+
+ /* Alignment is required by some "hardware", and optimizes transfers. */
+ char videomem[2 * 80 * 25]
+ __attribute__ ((aligned (__BIGGEST_ALIGNMENT__)));
+ unsigned char fontmem[2 * VGA_FONT_SIZE * VGA_FONT_HEIGHT]
+ __attribute__ ((aligned (__BIGGEST_ALIGNMENT__)));
+};
+
+static struct vga_state *vga_state;
+
+
+error_t
+vga_init (void)
+{
+ error_t err;
+ int fd;
+
+ /* Acquire I/O port access. */
+ if (ioperm (VGA_MIN_REG, VGA_MAX_REG - VGA_MIN_REG + 1, 1) < 0)
+ {
+ /* GNU Mach v1 is broken in that it doesn't implement an I/O
+ perm interface and just allows all tasks to access any I/O
+ port. */
+ if (errno != EMIG_BAD_ID && errno != ENOSYS)
+ {
+ free (vga_state);
+ return errno;
+ }
+ }
+
+ fd = open ("/dev/mem", O_RDWR);
+ if (fd >= 0)
+ {
+ vga_videomem = mmap (0, VGA_VIDEO_MEM_LENGTH, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, VGA_VIDEO_MEM_BASE_ADDR);
+ err = errno;
+ close (fd);
+ if (vga_videomem == (void *) -1)
+ return err;
+ }
+ else
+ return errno;
+
+ /* Save the current state. */
+ vga_state = malloc (sizeof (*vga_state));
+ if (!vga_state)
+ return errno;
+
+ outb (VGA_SEQ_CLOCK_MODE_ADDR, VGA_SEQ_ADDR_REG);
+ vga_state->seq_clock_mode = inb (VGA_SEQ_DATA_REG);
+ outb (VGA_SEQ_MAP_ADDR, VGA_SEQ_ADDR_REG);
+ vga_state->seq_map = inb (VGA_SEQ_DATA_REG);
+ outb (VGA_SEQ_FONT_ADDR, VGA_SEQ_ADDR_REG);
+ vga_state->seq_font = inb (VGA_SEQ_DATA_REG);
+ outb (VGA_SEQ_MODE_ADDR, VGA_SEQ_ADDR_REG);
+ vga_state->seq_mode = inb (VGA_SEQ_DATA_REG);
+
+ outb (VGA_GFX_MAP_ADDR, VGA_GFX_ADDR_REG);
+ vga_state->gfx_map = inb (VGA_GFX_DATA_REG);
+ outb (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG);
+ vga_state->gfx_mode = inb (VGA_GFX_DATA_REG);
+ outb (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG);
+ vga_state->gfx_misc = inb (VGA_GFX_DATA_REG);
+
+ outb (VGA_CRT_MAX_SCAN_LINE, VGA_CRT_ADDR_REG);
+ vga_state->crt_max_scan_line = inb (VGA_CRT_DATA_REG);
+ outb (VGA_CRT_CURSOR_START, VGA_CRT_ADDR_REG);
+ vga_state->crt_cursor_start = inb (VGA_CRT_DATA_REG);
+ outb (VGA_CRT_CURSOR_END, VGA_CRT_ADDR_REG);
+ vga_state->crt_cursor_end = inb (VGA_CRT_DATA_REG);
+ outb (VGA_CRT_CURSOR_HIGH, VGA_CRT_ADDR_REG);
+ vga_state->crt_cursor_high = inb (VGA_CRT_DATA_REG);
+ outb (VGA_CRT_CURSOR_LOW, VGA_CRT_ADDR_REG);
+ vga_state->crt_cursor_low = inb (VGA_CRT_DATA_REG);
+
+ /* Side effect of reading the input status #1 register is to
+ reset the attribute mixed address/data register so that the
+ next write it expects is the address, not the data. */
+ inb (VGA_INPUT_STATUS_1_REG);
+ outb (VGA_ATTR_MODE_ADDR, VGA_ATTR_ADDR_DATA_REG);
+ vga_state->attr_mode = inb (VGA_ATTR_DATA_READ_REG);
+
+ /* Re-enable the screen. */
+ inb (VGA_INPUT_STATUS_1_REG);
+ outb (VGA_ATTR_ENABLE_ADDR, VGA_ATTR_ADDR_DATA_REG);
+ outb (0x00, VGA_ATTR_ADDR_DATA_REG);
+
+ /* Read/write in interleaved mode. */
+ outb (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG);
+ outb (VGA_GFX_MISC_CHAINOE | VGA_GFX_MISC_B8TOBF, VGA_GFX_DATA_REG);
+ outb (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG);
+ outb (VGA_GFX_MODE_HOSTOE, VGA_GFX_DATA_REG);
+
+ memcpy (vga_state->videomem, vga_videomem, 2 * 80 * 25);
+ vga_read_font_buffer (0, 0, vga_state->fontmem,
+ 2 * VGA_FONT_SIZE * VGA_FONT_HEIGHT);
+
+ /* 80 cols, 25 rows, two bytes per cell and twice because with lower
+ max scan line we get more lines on the screen. */
+ memset (vga_videomem, 0, 80 * 25 * 2 * 2);
+
+ return 0;
+}
+
+
+/* Release the resources and privileges associated with the VGA
+ hardware access. */
+void
+vga_fini (void)
+{
+ /* Recover the saved state. */
+ vga_write_font_buffer (0, 0, vga_state->fontmem,
+ 2 * VGA_FONT_SIZE * VGA_FONT_HEIGHT);
+ memcpy (vga_videomem, vga_state->videomem, 2 * 80 * 25);
+
+ /* Restore the registers. */
+ outb (VGA_SEQ_CLOCK_MODE_ADDR, VGA_SEQ_ADDR_REG);
+ outb (vga_state->seq_clock_mode, VGA_SEQ_DATA_REG);
+ outb (VGA_SEQ_MAP_ADDR, VGA_SEQ_ADDR_REG);
+ outb (vga_state->seq_map, VGA_SEQ_DATA_REG);
+ outb (VGA_SEQ_FONT_ADDR, VGA_SEQ_ADDR_REG);
+ outb (vga_state->seq_font, VGA_SEQ_DATA_REG);
+ outb (VGA_SEQ_MODE_ADDR, VGA_SEQ_ADDR_REG);
+ outb (vga_state->seq_mode, VGA_SEQ_DATA_REG);
+
+ outb (VGA_GFX_MAP_ADDR, VGA_GFX_ADDR_REG);
+ outb (vga_state->gfx_map, VGA_GFX_DATA_REG);
+ outb (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG);
+ outb (vga_state->gfx_mode, VGA_GFX_DATA_REG);
+ outb (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG);
+ outb (vga_state->gfx_misc, VGA_GFX_DATA_REG);
+
+ outb (VGA_CRT_MAX_SCAN_LINE, VGA_CRT_ADDR_REG);
+ outb (vga_state->crt_max_scan_line, VGA_CRT_DATA_REG);
+ outb (VGA_CRT_CURSOR_START, VGA_CRT_ADDR_REG);
+ outb (vga_state->crt_cursor_start, VGA_CRT_DATA_REG);
+ outb (VGA_CRT_CURSOR_END, VGA_CRT_ADDR_REG);
+ outb (vga_state->crt_cursor_end, VGA_CRT_DATA_REG);
+ outb (VGA_CRT_CURSOR_HIGH, VGA_CRT_ADDR_REG);
+ outb (vga_state->crt_cursor_high, VGA_CRT_DATA_REG);
+ outb (VGA_CRT_CURSOR_LOW, VGA_CRT_ADDR_REG);
+ outb (vga_state->crt_cursor_low, VGA_CRT_DATA_REG);
+
+ inb (VGA_INPUT_STATUS_1_REG);
+ outb (VGA_ATTR_MODE_ADDR, VGA_ATTR_ADDR_DATA_REG);
+ outb (vga_state->attr_mode, VGA_ATTR_DATA_READ_REG);
+
+ /* Re-enable the screen. */
+ inb (VGA_INPUT_STATUS_1_REG);
+ outb (VGA_ATTR_ENABLE_ADDR, VGA_ATTR_ADDR_DATA_REG);
+ outb (0x00, VGA_ATTR_ADDR_DATA_REG);
+
+ ioperm (VGA_MIN_REG, VGA_MAX_REG - VGA_MIN_REG + 1, 0);
+ munmap (vga_videomem, VGA_VIDEO_MEM_LENGTH);
+}
+
+
+/* Access the font buffer BUFFER, starting from glyph INDEX, and
+ either read DATALEN bytes into DATA (if WRITE is 0) or write
+ DATALEN bytes from DATA (if WRITE is not 0). */
+static void
+vga_read_write_font_buffer (int write, int buffer, int index,
+ char *data, size_t datalen)
+{
+ char saved_seq_map;
+ char saved_seq_mode;
+ char saved_gfx_map;
+ char saved_gfx_mode;
+ char saved_gfx_misc;
+
+ int offset = buffer * VGA_FONT_SIZE + index * VGA_FONT_HEIGHT;
+ assert (offset >= 0 && offset + datalen <= VGA_VIDEO_MEM_LENGTH);
+
+ /* Select plane 2 for sequential writing. You might think it is not
+ necessary for reading, but it is. Likewise for read settings
+ when writing. Joy. */
+ outb (VGA_SEQ_MAP_ADDR, VGA_SEQ_ADDR_REG);
+ saved_seq_map = inb (VGA_SEQ_DATA_REG);
+ outb (VGA_SEQ_MAP_PLANE2, VGA_SEQ_DATA_REG);
+ outb (VGA_SEQ_MODE_ADDR, VGA_SEQ_ADDR_REG);
+ saved_seq_mode = inb (VGA_SEQ_DATA_REG);
+ outb (VGA_SEQ_MODE_SEQUENTIAL | VGA_SEQ_MODE_EXT, VGA_SEQ_DATA_REG);
+
+ /* Read sequentially from plane 2. */
+ outb (VGA_GFX_MAP_ADDR, VGA_GFX_ADDR_REG);
+ saved_gfx_map = inb (VGA_GFX_DATA_REG);
+ outb (VGA_GFX_MAP_PLANE2, VGA_GFX_DATA_REG);
+ outb (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG);
+ saved_gfx_mode = inb (VGA_GFX_DATA_REG);
+ outb (VGA_GFX_MODE_READ0, VGA_GFX_DATA_REG);
+ outb (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG);
+ saved_gfx_misc = inb (VGA_GFX_DATA_REG);
+ outb (VGA_GFX_MISC_B8TOBF, VGA_GFX_DATA_REG);
+
+ if (write)
+ memcpy (vga_videomem + offset, data, datalen);
+ else
+ memcpy (data, vga_videomem + offset, datalen);
+
+ /* Restore sequencer and graphic register values. */
+ outb (VGA_SEQ_MAP_ADDR, VGA_SEQ_ADDR_REG);
+ outb (saved_seq_map, VGA_SEQ_DATA_REG);
+ outb (VGA_SEQ_MODE_ADDR, VGA_SEQ_ADDR_REG);
+ outb (saved_seq_mode, VGA_SEQ_DATA_REG);
+
+ outb (VGA_GFX_MAP_ADDR, VGA_GFX_ADDR_REG);
+ outb (saved_gfx_map, VGA_GFX_DATA_REG);
+ outb (VGA_GFX_MODE_ADDR, VGA_GFX_ADDR_REG);
+ outb (saved_gfx_mode, VGA_GFX_DATA_REG);
+ outb (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG);
+ outb (saved_gfx_misc, VGA_GFX_DATA_REG);
+}
+
+
+/* Write DATALEN bytes from DATA to the font buffer BUFFER, starting
+ from glyph INDEX. */
+void
+vga_write_font_buffer (int buffer, int index, char *data, size_t datalen)
+{
+ vga_read_write_font_buffer (1, buffer, index, data, datalen);
+}
+
+/* Read DATALEN bytes into DATA from the font buffer BUFFER, starting
+ from glyph INDEX. */
+void
+vga_read_font_buffer (int buffer, int index, char *data, size_t datalen)
+{
+ vga_read_write_font_buffer (0, buffer, index, data, datalen);
+}
+
+
+/* Set FONT_BUFFER_SUPP to FONT_BUFFER if the font is small. */
+void
+vga_select_font_buffer (int font_buffer, int font_buffer_supp)
+{
+ char font = ((font_buffer & 6) >> 1) | ((font_buffer & 1) << 4)
+ | ((font_buffer_supp & 6) << 1) | ((font_buffer_supp & 1) << 5);
+
+ outb (VGA_SEQ_FONT_ADDR, VGA_SEQ_ADDR_REG);
+ outb (font, VGA_SEQ_DATA_REG);
+}
+
+/* Set the font height in pixel. */
+void
+vga_set_font_height (int height)
+{
+ char saved;
+
+ outb (VGA_CRT_MAX_SCAN_LINE, VGA_CRT_ADDR_REG);
+ saved = inb (VGA_CRT_DATA_REG);
+ saved &= ~31;
+ saved |= (height - 1) & 31;
+ outb (saved, VGA_CRT_DATA_REG);
+}
+
+
+/* Get the font width in pixel. Can be 8 or 9. */
+int
+vga_get_font_width (void)
+{
+ outb (VGA_SEQ_CLOCK_MODE_ADDR, VGA_SEQ_ADDR_REG);
+ return (inb (VGA_SEQ_DATA_REG) & VGA_SEQ_CLOCK_MODE_8) ? 8 : 9;
+}
+
+/* Set the font width in pixel. WIDTH can be 8 or 9. */
+void
+vga_set_font_width (int width)
+{
+ char saved;
+
+ if (width != 8 && width != 9)
+ return;
+
+ outb (VGA_SEQ_CLOCK_MODE_ADDR, VGA_SEQ_ADDR_REG);
+ saved = inb (VGA_SEQ_DATA_REG);
+ if (width == 8)
+ saved |= VGA_SEQ_CLOCK_MODE_8;
+ else
+ saved &= ~VGA_SEQ_CLOCK_MODE_8;
+ outb (saved, VGA_SEQ_DATA_REG);
+
+ inb (VGA_INPUT_STATUS_1_REG);
+ outb (VGA_ATTR_MODE_ADDR, VGA_ATTR_ADDR_DATA_REG);
+ saved = inb (VGA_ATTR_DATA_READ_REG);
+ if (width == 8)
+ saved &= ~VGA_ATTR_MODE_LGE;
+ else
+ saved |= VGA_ATTR_MODE_LGE;
+ outb (saved, VGA_ATTR_ADDR_DATA_REG);
+
+ /* Re-enable the screen. */
+ inb (VGA_INPUT_STATUS_1_REG);
+ outb (VGA_ATTR_ENABLE_ADDR, VGA_ATTR_ADDR_DATA_REG);
+ outb (0x00, VGA_ATTR_ADDR_DATA_REG);
+}
+
+
+/* Enable (if ON is true) or disable (otherwise) the cursor. Expects
+ the VGA hardware to be locked. */
+void
+vga_display_cursor (int on)
+{
+ char crs_start;
+
+ outb (VGA_CRT_CURSOR_START, VGA_CRT_ADDR_REG);
+ crs_start = inb (VGA_CRT_DATA_REG);
+ if (on)
+ crs_start &= ~VGA_CRT_CURSOR_DISABLE;
+ else
+ crs_start |= VGA_CRT_CURSOR_DISABLE;
+ outb (crs_start, VGA_CRT_DATA_REG);
+}
+
+
+/* Set cursor size from START to END (set to -1 to not set one of the
+ values). */
+void
+vga_set_cursor_size (int start, int end)
+{
+ char saved;
+
+ if (start >= 0)
+ {
+ outb (VGA_CRT_CURSOR_START, VGA_CRT_ADDR_REG);
+ saved = inb (VGA_CRT_DATA_REG);
+ saved &= ~31;
+ saved |= start & 31;
+ outb (saved, VGA_CRT_DATA_REG);
+ }
+ if (end >= 0)
+ {
+ outb (VGA_CRT_CURSOR_END, VGA_CRT_ADDR_REG);
+ saved = inb (VGA_CRT_DATA_REG);
+ saved &= ~31;
+ saved |= end & 31;
+ outb (saved, VGA_CRT_DATA_REG);
+ }
+}
+
+
+/* Set the cursor position to POS, which is (x_pos + y_pos * width). */
+void
+vga_set_cursor_pos (unsigned int pos)
+{
+ outb (VGA_CRT_CURSOR_HIGH, VGA_CRT_ADDR_REG);
+ outb (pos >> 8, VGA_CRT_DATA_REG);
+ outb (VGA_CRT_CURSOR_LOW, VGA_CRT_ADDR_REG);
+ outb (pos & 0xff, VGA_CRT_DATA_REG);
+}
+
+
+/* Read NR entries from the color palette, starting from INDEX. DATA
+ must be able to hold at least 3 * NR bytes and will contain the
+ desired colors in RGB form. Only the lower six bits of each
+ component are significant. */
+void
+vga_read_palette (unsigned char index, unsigned char *data, int nr)
+{
+ /* Every color has three components. */
+ nr *= 3;
+
+ outb (index, VGA_DAC_READ_ADDR_REG);
+ while (nr--)
+ *(data++) = inb (VGA_DAC_DATA_REG);
+}
+
+
+/* Write NR entries to the color palette, starting from INDEX. DATA
+ must be at least 3 * NR of bytes long and contains the desired
+ colors in RGB form. Only the lower six bits for each component are
+ significant. */
+void
+vga_write_palette (unsigned char index, const unsigned char *data, int nr)
+{
+ /* Every color has three components. */
+ nr *= 3;
+
+ outb (index, VGA_DAC_WRITE_ADDR_REG);
+ while (nr--)
+ outb (*(data++), VGA_DAC_DATA_REG);
+}
+
+
+/* Exchange NR entries in the internal palette with the values in
+ PALETTE_ATTR, starting from the internal palette entry INDEX (which
+ can be betweern 0 and 15). Only the lower six bits of each entry
+ in PALETTE_ATTR is significant.
+
+ The internal palette entry is used to look up a color in the
+ palette for the color attribute in text mode with the corresponding
+ value.
+
+ Example: The attribute byte specifies 3 as the foreground color.
+ This means that the character with this attribute gets the color of
+ the palette entry specified by the internal palette entry 3 (the
+ fourth one). */
+void
+vga_exchange_palette_attributes (unsigned char index,
+ unsigned char *palette_attr,
+ int nr)
+{
+ if (!nr)
+ return;
+
+ /* We want to read and change the palette attribute register. */
+ while (nr--)
+ {
+ unsigned char attr;
+
+ /* Set the address. */
+ inb (VGA_INPUT_STATUS_1_REG);
+ outb (index++, VGA_ATTR_ADDR_DATA_REG);
+ attr = inb (VGA_ATTR_DATA_READ_REG);
+ outb ((attr & ~077) | (*palette_attr & 077), VGA_ATTR_ADDR_DATA_REG);
+ *(palette_attr++) = attr & 077;
+ }
+
+ /* Re-enable the screen, which was blanked during the palette
+ operation. */
+ inb (VGA_INPUT_STATUS_1_REG);
+ outb (VGA_ATTR_ENABLE_ADDR, VGA_ATTR_ADDR_DATA_REG);
+ outb (0x00, VGA_ATTR_ADDR_DATA_REG);
+}
diff --git a/console-client/vga-support.h b/console-client/vga-support.h
new file mode 100644
index 00000000..38c6248f
--- /dev/null
+++ b/console-client/vga-support.h
@@ -0,0 +1,89 @@
+/* vga-support.h - Interface for VGA hardware access.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef _VGA_SUPPORT_H_
+#define _VGA_SUPPORT_H_ 1
+
+#include <errno.h>
+#include <sys/types.h>
+
+
+/* The VGA interface does not do locking on its own, for maximum
+ efficiency it relies on locking by the caller (because usually the
+ caller has some other data structures to lock along with the
+ VGA hardware. */
+
+/* The mapped video memory. */
+extern char *vga_videomem;
+
+/* Initialize the VGA hardware and set up the permissions and memory
+ mappings. */
+error_t vga_init (void);
+
+/* Release the resources and privileges associated with the VGA
+ hardware access. */
+void vga_fini (void);
+
+/* Write DATALEN bytes from DATA to the font buffer BUFFER, starting
+ from glyph index. */
+void vga_write_font_buffer (int buffer, int index,
+ char *data, size_t datalen);
+
+/* Read DATALEN bytes into DATA from the font buffer BUFFER, starting
+ from glyph INDEX. */
+void vga_read_font_buffer (int buffer, int index,
+ char *data, size_t datalen);
+
+/* Set FONT_BUFFER_SUPP to FONT_BUFFER if the font is small. */
+void vga_select_font_buffer (int font_buffer, int font_buffer_supp);
+
+/* Set the font height in pixel. */
+void vga_set_font_height (int height);
+
+/* Get the font height in pixel. Can be 8 or 9. */
+int vga_get_font_width (void);
+
+/* Set the font height in pixel. WIDTH can be 8 or 9. */
+void vga_set_font_width (int width);
+
+/* Enable (if ON is true) or disable (otherwise) the cursor. Expects
+ the VGA hardware to be locked. */
+void vga_display_cursor (int on);
+
+/* Set cursor size from START to END (set to -1 to not set one of the
+ values). */
+void vga_set_cursor_size (int start, int end);
+
+/* Set the cursor position to POS, which is (x_pos + y_pos *
+ width). */
+void vga_set_cursor_pos (unsigned int pos);
+
+/* Read NR entries from the color palette, starting from INDEX. */
+void vga_read_palette (unsigned char index, unsigned char *data, int nr);
+
+/* Write NR entries to the color palette, starting from INDEX. */
+void vga_write_palette (unsigned char index,
+ const unsigned char *data, int nr);
+
+void vga_exchange_palette_attributes (unsigned char index,
+ unsigned char *saved_palette_attr,
+ int nr);
+
+#endif /* _VGA_SUPPORT_H_ */
diff --git a/console-client/vga.c b/console-client/vga.c
new file mode 100644
index 00000000..cf6c8c35
--- /dev/null
+++ b/console-client/vga.c
@@ -0,0 +1,850 @@
+/* vga.c - The VGA device display driver.
+ Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <iconv.h>
+#include <argp.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <sys/io.h>
+#include <sys/mman.h>
+#include <sys/fcntl.h>
+#include <pthread.h>
+#include <hurd/console.h>
+
+#include "driver.h"
+#include "timer.h"
+
+#include "vga-hw.h"
+#include "vga-support.h"
+#include "bdf.h"
+#include "vga-dynafont.h"
+#include "vga-dynacolor.h"
+#include "unicode.h"
+
+
+#define VGA_DISP_WIDTH 80
+#define VGA_DISP_HEIGHT 25
+
+/* The font file. */
+#define DEFAULT_VGA_FONT DEFAULT_VGA_FONT_DIR "vga-system.bdf"
+static char *vga_display_font;
+
+#define DEFAULT_VGA_FONT_ITALIC DEFAULT_VGA_FONT_DIR "vga-system-italic.bdf"
+static char *vga_display_font_italic;
+
+#define DEFAULT_VGA_FONT_BOLD DEFAULT_VGA_FONT_DIR "vga-system-bold.bdf"
+static char *vga_display_font_bold;
+
+#define DEFAULT_VGA_FONT_BOLD_ITALIC \
+ DEFAULT_VGA_FONT_DIR "vga-system-bold-italic.bdf"
+static char *vga_display_font_bold_italic;
+
+/* If false use all colors, else use double font slots. */
+static int vga_display_max_glyphs;
+
+/* width of glyphs. */
+static int vga_display_font_width;
+
+/* The timer used for flashing the screen. */
+static struct timer_list vga_display_timer;
+
+/* The lock that protects the color palette manipulation. */
+static pthread_mutex_t vga_display_lock;
+
+/* Forward declaration. */
+static struct display_ops vga_display_ops;
+
+/* The current width and height the ncursesw driver is using. */
+static unsigned int current_width;
+static unsigned int current_height;
+
+/* The cursor state to restore the state to. */
+static int cursor_state;
+
+/* Is set to 1 if the cursor moved out of the physical screen and the
+ cursor state should be hidden. */
+static int cursor_hidden;
+
+struct refchr
+{
+ unsigned int used : 1;
+ unsigned int chr : 9;
+ unsigned int attr : 8;
+};
+
+
+typedef struct vga_mousecursor
+{
+ float posx;
+ float posy;
+ char oldcolor;
+ int visible;
+ int enabled;
+} vga_mousecursor_t;
+
+struct vga_display
+{
+ /* The VGA font for this display. */
+ dynafont_t df;
+ int df_size;
+ int df_width;
+
+ /* The color palette. */
+ dynacolor_t dc;
+
+ unsigned int width;
+ unsigned int height;
+
+ /* Current attribute. */
+ int cur_conchar_attr_init;
+ conchar_attr_t cur_conchar_attr;
+ char cur_attr;
+
+ /* The state of the mouse cursor. */
+ vga_mousecursor_t mousecursor;
+
+ /* Remember for each cell on the display the glyph written to it and
+ the colors (in the upper byte) assigned. 0 means unassigned. */
+
+ struct refchr refmatrix[VGA_DISP_HEIGHT][VGA_DISP_WIDTH];
+};
+
+
+static void
+vga_display_invert_border (void)
+{
+ unsigned char col[3];
+
+ pthread_mutex_lock (&vga_display_lock);
+ vga_read_palette (0, col, 1);
+ col[0] = 0xff - col[0];
+ col[1] = 0xff - col[1];
+ col[2] = 0xff - col[2];
+ vga_write_palette (0, col, 1);
+ pthread_mutex_unlock (&vga_display_lock);
+}
+
+
+static int
+vga_display_flash_off (void *dummy)
+{
+ vga_display_invert_border ();
+ return 0;
+}
+
+
+static error_t
+vga_display_flash (void *handle)
+{
+ if (timer_remove (&vga_display_timer))
+ vga_display_invert_border ();
+ vga_display_invert_border ();
+ vga_display_timer.expires = fetch_jiffies () + 10;
+ timer_add (&vga_display_timer);
+ return 0;
+}
+
+
+static void
+hide_mousecursor (struct vga_display *disp)
+{
+ char *oldpos = vga_videomem + 2 * ((int) disp->mousecursor.posy * disp->width
+ + (int) disp->mousecursor.posx) + 1;
+
+ if (!disp->mousecursor.visible)
+ return;
+
+ /* First remove the old cursor. */
+ *oldpos = disp->mousecursor.oldcolor;
+ disp->mousecursor.visible = 0;
+}
+
+
+static void
+draw_mousecursor (struct vga_display *disp)
+{
+ char *newpos = vga_videomem + 2 * ((int) disp->mousecursor.posy * disp->width
+ + (int) disp->mousecursor.posx) + 1;
+
+ if (disp->mousecursor.visible)
+ return;
+
+ /* Draw the new cursor. */
+ disp->mousecursor.oldcolor = *newpos;
+ *newpos = (127) ^ *newpos;
+
+ disp->mousecursor.visible = 1;
+}
+
+
+static const char doc[] = "VGA Driver";
+
+static const struct argp_option options[] =
+ {
+ {"font", 'f', "FONT", 0, "Use FONT for normal text"},
+ {"font-italic", 'i', "FONT", 0, "Use FONT for italic text"},
+ {"font-bold", 'b', "FONT", 0, "Use FONT for bold text"},
+ {"font-bold-italic",'a', "FONT", 0,
+ "Use FONT for text that is both bold and italic"},
+ {"max-colors", 'm', 0 , 0,
+ "Prefer a lot of colors above a lot of glyphs"},
+ {"max-glyphs", 'g', 0 , 0,
+ "Prefer a lot of glyphs above a lot of colors"},
+ {"font-width", 'w', "NUM" , 0, "Force using NUM pixel-wide glyphs"},
+ { 0 }
+ };
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ int *pos = (int *) state->input;
+
+ switch (key)
+ {
+ case 'f':
+ vga_display_font = strdup (arg);
+ if (! vga_display_font)
+ return 0;
+ break;
+
+ case 'i':
+ vga_display_font_italic = strdup (arg);
+ if (! vga_display_font_italic)
+ return 0;
+ break;
+
+ case 'b':
+ vga_display_font_bold = strdup (arg);
+ if (! vga_display_font_bold)
+ return 0;
+ break;
+
+ case 'a':
+ vga_display_font_bold_italic = strdup (arg);
+ if (! vga_display_font_bold_italic)
+ return 0;
+ break;
+
+ case 'm':
+ vga_display_max_glyphs = 0;
+ break;
+
+ case 'g':
+ vga_display_max_glyphs = 1;
+ break;
+
+ case 'w':
+ vga_display_font_width = atoi (arg);
+ break;
+
+ case ARGP_KEY_END:
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ *pos = state->next;
+ return 0;
+}
+
+static struct argp argp = {options, parse_opt, 0, doc};
+
+/* Initialize the subsystem. */
+static error_t
+vga_display_init (void **handle, int no_exit, int argc, char *argv[],
+ int *next)
+{
+ error_t err;
+ struct vga_display *disp;
+ int pos = 1;
+
+ /* XXX Assert that we are called only once. */
+ pthread_mutex_init (&vga_display_lock, NULL);
+ timer_clear (&vga_display_timer);
+ vga_display_timer.fnc = &vga_display_flash_off;
+
+ /* Parse the arguments. */
+ err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER | ARGP_NO_EXIT
+ | ARGP_SILENT, 0, &pos);
+ *next += pos - 1;
+ if (err && err != EINVAL)
+ return err;
+
+ /* Create and initialize the display structure as much as
+ possible. */
+ disp = calloc (1, sizeof *disp);
+ if (!disp)
+ return ENOMEM;
+
+ disp->df_size = vga_display_max_glyphs ? 512 : 256;
+ disp->df_width = vga_display_font_width;
+ disp->width = VGA_DISP_WIDTH;
+ disp->height = VGA_DISP_HEIGHT;
+
+ *handle = disp;
+ return 0;
+}
+
+
+/* Start the driver. */
+static error_t
+vga_display_start (void *handle)
+{
+ error_t err;
+ struct vga_display *disp = handle;
+ bdf_font_t font = NULL;
+ bdf_font_t font_italic = NULL;
+ bdf_font_t font_bold = NULL;
+ bdf_font_t font_bold_italic = NULL;
+ FILE *font_file;
+
+ err = vga_init ();
+ if (err)
+ return err;
+
+ dynacolor_init ();
+
+#define LOAD_FONT(x,y) \
+ do { \
+ font_file = fopen (vga_display_##x ?: DEFAULT_VGA_##y, "r"); \
+ if (font_file) \
+ { \
+ bdf_error_t bdferr = bdf_read (font_file, &x, NULL); \
+ if (bdferr) \
+ x = NULL; \
+ else \
+ bdf_sort_glyphs (x); \
+ fclose (font_file); \
+ } \
+ } while (0)
+
+ LOAD_FONT (font, FONT);
+ LOAD_FONT (font_italic, FONT_ITALIC);
+ LOAD_FONT (font_bold, FONT_BOLD);
+ LOAD_FONT (font_bold_italic, FONT_BOLD_ITALIC);
+
+ err = dynafont_new (font, font_italic, font_bold, font_bold_italic,
+ disp->df_size, disp->df_width, &disp->df);
+ if (err)
+ {
+ free (disp);
+ vga_fini ();
+ return err;
+ }
+ dynafont_activate (disp->df);
+
+ disp->dc = (disp->df_size == 512) ? dynacolor_init_8 : dynacolor_init_16;
+ dynacolor_activate (&disp->dc);
+
+ err = driver_add_display (&vga_display_ops, disp);
+ if (err)
+ {
+ dynafont_free (disp->df);
+ dynacolor_fini ();
+ vga_fini ();
+ free (disp);
+ }
+ return err;
+}
+
+
+/* Destroy the display HANDLE. */
+static error_t
+vga_display_fini (void *handle, int force)
+{
+ struct vga_display *disp = handle;
+ driver_remove_display (&vga_display_ops, disp);
+ if (timer_remove (&vga_display_timer))
+ vga_display_flash_off (0);
+
+ dynafont_free (disp->df);
+ free (disp);
+ dynacolor_fini ();
+ vga_fini ();
+ if (vga_display_font)
+ free (vga_display_font);
+ if (vga_display_font_italic)
+ free (vga_display_font_italic);
+ if (vga_display_font_bold)
+ free (vga_display_font_bold);
+ if (vga_display_font_bold_italic)
+ free (vga_display_font_bold_italic);
+
+ return 0;
+}
+
+
+static void
+vga_display_restore_status (void *handle)
+{
+ /* Read/write in interleaved mode. This is not preserved by the
+ XFree VESA driver. */
+ outb (VGA_GFX_MISC_ADDR, VGA_GFX_ADDR_REG);
+ outb (VGA_GFX_MISC_CHAINOE | VGA_GFX_MISC_B8TOBF, VGA_GFX_DATA_REG);
+}
+
+
+/* Set the cursor's state to STATE on display HANDLE. */
+static error_t
+vga_display_set_cursor_status (void *handle, uint32_t state)
+{
+ struct vga_display *disp = handle;
+
+ /* Don't display the cursor if its location is not within the
+ physical screen. */
+ if (!cursor_hidden)
+ {
+ if (state != CONS_CURSOR_INVISIBLE)
+ dynafont_set_cursor (disp->df,
+ state == CONS_CURSOR_VERY_VISIBLE ? 1 : 0);
+
+ vga_display_cursor (state == CONS_CURSOR_INVISIBLE ? 0 : 1);
+ }
+
+ cursor_state = state;
+
+ return 0;
+}
+
+
+/* Set the cursor's position on display HANDLE to column COL and row
+ ROW. */
+static error_t
+vga_display_set_cursor_pos (void *handle, uint32_t col, uint32_t row)
+{
+ struct vga_display *disp = handle;
+ unsigned int pos = row * disp->width + col;
+
+ /* Make sure the cursor can only be moved to a position on te
+ physical screen. */
+ if (col < disp->width && row < disp->height)
+ {
+ vga_set_cursor_pos (pos);
+ if (cursor_hidden)
+ {
+ /* Restore the cursor. */
+ cursor_hidden = 0;
+ vga_display_set_cursor_status (handle, cursor_state);
+ }
+ }
+ else if (!cursor_hidden)
+ {
+ /* Hide the cursor. */
+ cursor_hidden = 1;
+ vga_display_cursor (CONS_CURSOR_INVISIBLE);
+ }
+
+ return 0;
+}
+
+
+/* Scroll the display by the desired amount. The area that becomes
+ free will be filled in a subsequent write call. */
+static error_t
+vga_display_scroll (void *handle, int delta)
+{
+ struct vga_display *disp = handle;
+ int count = abs(delta) * disp->width;
+ int i;
+ struct refchr *refpos;
+
+ hide_mousecursor (disp);
+
+ /* XXX: If the virtual console is bigger than the physical console it is
+ impossible to scroll because the data to scroll is not in memory. */
+ if (current_height > disp->height)
+ return ENOTSUP;
+
+ if (delta > 0)
+ {
+ memmove (vga_videomem, vga_videomem + 2 * count,
+ 2 * disp->width * (disp->height - delta));
+ refpos = &disp->refmatrix[0][0];
+ }
+ else
+ {
+ memmove (vga_videomem + 2 * count, vga_videomem,
+ 2 * disp->width * (disp->height + delta));
+ refpos = &disp->refmatrix[disp->height + delta][0];
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ if (refpos->used)
+ {
+ dynafont_release (disp->df, refpos->chr);
+ /* We intimately know that reference counting is only done
+ for the up to 8 colors mode. */
+ dynacolor_release (disp->dc, refpos->attr & 7);
+ dynacolor_release (disp->dc, (refpos->attr >> 4) & 7);
+ }
+ refpos++;
+ }
+
+ if (delta > 0)
+ {
+ memmove (&disp->refmatrix[0][0], &disp->refmatrix[0][0] + count,
+ sizeof (struct refchr) * disp->width * (disp->height - delta));
+ refpos = &disp->refmatrix[disp->height - delta][0];
+ }
+ else
+ {
+ memmove (&disp->refmatrix[0][0] + count, &disp->refmatrix[0][0],
+ sizeof (struct refchr) * disp->width * (disp->height + delta));
+ refpos = &disp->refmatrix[0][0];
+ }
+
+ for (i = 0; i < count; i++)
+ (refpos++)->used = 0;
+
+ return 0;
+}
+
+#if 0
+/* Change the font on the console CONSOLE to font. The old font will
+ not be accessed by the vga console subsystem anymore after this
+ call completed. */
+static void
+vga_display_change_font (void *handle, bdf_font_t font)
+{
+ struct vga_display *disp = handle;
+
+ dynafont_change_font (disp->df, font);
+}
+#endif
+
+
+static inline char
+vga_display_recalculate_attr (dynacolor_t *dc, conchar_attr_t attr)
+{
+ char vga_attr;
+ signed char res_fgcol;
+ signed char res_bgcol;
+ signed char fgcol;
+ signed char bgcol;
+
+ /* VGA has way too few bits for this stuff. The highest background
+ color bit is also the blinking bit if blinking is enabled. The
+ highest foreground color bit is the font selector bit,
+ unfortunately. Underlining is enabled if foreground is ?001 and
+ background ?000. */
+
+ /* Reversed means colors are reversed. Note that this does not
+ reverse the intensity. */
+ if (attr.reversed)
+ {
+ fgcol = attr.bgcol;
+ bgcol = attr.fgcol;
+ }
+ else
+ {
+ fgcol = attr.fgcol;
+ bgcol = attr.bgcol;
+ }
+
+ /* Set the foreground color. */
+ if (attr.concealed)
+ fgcol = bgcol;
+ else
+ {
+ /* Intensity bold and dim also affect the font selection bit. */
+ switch (attr.intensity)
+ {
+ case CONS_ATTR_INTENSITY_BOLD:
+ fgcol |= 1 << 3;
+ break;
+ case CONS_ATTR_INTENSITY_DIM:
+ fgcol = CONS_COLOR_BLACK | 1 << 3;
+ break;
+ case CONS_ATTR_INTENSITY_NORMAL:
+ break;
+ }
+ }
+
+ /* Try to get the colors as desired. This might change the palette,
+ so we need to take the lock (in case a flash operation times
+ out). */
+ pthread_mutex_lock (&vga_display_lock);
+ res_bgcol = dynacolor_lookup (*dc, bgcol);
+ res_fgcol = dynacolor_lookup (*dc, fgcol);
+ pthread_mutex_unlock (&vga_display_lock);
+ if (res_bgcol == -1 || res_fgcol == -1)
+ dynacolor_replace_colors (dc, fgcol, bgcol, &res_fgcol, &res_bgcol);
+ vga_attr = res_bgcol << 4 | res_fgcol;
+
+ vga_attr |= attr.blinking << 7;
+
+ /* XXX We can support underlined in a monochrome mode. */
+ return vga_attr;
+}
+
+
+/* Deallocate any scarce resources occupied by the LENGTH characters
+ from column COL and row ROW. */
+static error_t
+vga_display_clear (void *handle, size_t length, uint32_t col, uint32_t row)
+{
+ struct vga_display *disp = handle;
+ struct refchr *refpos = &disp->refmatrix[row][0];
+ int cols;
+
+ /* The column can be outside the physical screen, in that case
+ adjust the position. */
+ if (col >= disp->width)
+ {
+ col = disp->width - col;
+ row++;
+ }
+ refpos += col;
+
+ /* The first row is not in the physical screen, nothing has to be
+ done. */
+ if (row >= disp->height)
+ return 0;
+
+ /* The length cannot be used. Recalculate it to wrap the lines. */
+ cols = length / current_width;
+ length = (length % current_width) + cols * disp->width ;
+
+ /* Make sure the end of length is still in the physical screen. */
+ if (length > (disp->width * disp->height - (row * disp->width + col)) - col)
+ length = disp->width * disp->height - (row * disp->width + col) - col;
+
+ while (length > 0)
+ {
+ if (refpos->used)
+ {
+ dynafont_release (disp->df, refpos->chr);
+ /* We intimately know that reference counting is only done
+ for the up to 8 colors mode. */
+ dynacolor_release (disp->dc, refpos->attr & 7);
+ dynacolor_release (disp->dc, (refpos->attr >> 4) & 7);
+ refpos->used = 0;
+ }
+ refpos++;
+ length--;
+ }
+ return 0;
+}
+
+/* Write the text STR with LENGTH characters to column COL and row
+ ROW. */
+static error_t
+vga_display_write (void *handle, conchar_t *str, size_t length,
+ uint32_t col, uint32_t row)
+{
+ struct vga_display *disp = handle;
+ char *pos;
+ struct refchr *refpos = &disp->refmatrix[row][col];
+ char *mouse_cursor_pos;
+
+ /* The starting column is outside the physical screen. */
+ if (disp->width < current_width && col >= disp->width)
+ {
+ size_t skip = current_width - disp->width;
+ str += skip;
+ length -= skip;
+ col = 0;
+ row++;
+ }
+
+ pos = vga_videomem + 2 * (row * disp->width + col);
+ mouse_cursor_pos = (vga_videomem + 2
+ * ((int) disp->mousecursor.posy
+ * disp->width + (int) disp->mousecursor.posx) + 1);
+
+ /* Although all references to the current fgcol or bgcol could have
+ been released here, for example due to a scroll operation, we
+ know that the color slots have not been reused yet, as no routine
+ but ours does color allocation. This ensures that cur_attr is
+ still valid. XXX consider recalculating the attribute for more
+ authentic (but less homogenous) colors anyway. */
+
+ while (length--)
+ {
+ int charval = dynafont_lookup (disp->df, str);
+ col++;
+
+ /* The virtual console is smaller than the physical screen. */
+ if (col > current_width)
+ {
+ size_t skip = disp->width - current_width;
+ pos += skip * 2;
+ refpos += skip;
+ col = 1;
+ row++;
+ }
+ /* The virtual console is bigger than the physical console. */
+ else if (disp->width < current_width && col == disp->width)
+ {
+ size_t skip = current_width - disp->width;
+ str += skip;
+ length -= skip;
+ col = 1;
+ row++;
+ }
+
+ /* The screen is filled until the bottom of the screen. */
+ if (row >= disp->height)
+ return 0;
+
+ if (!disp->cur_conchar_attr_init
+ || *(uint32_t *) &disp->cur_conchar_attr != *(uint32_t *) &str->attr)
+ {
+ if (!disp->cur_conchar_attr_init)
+ disp->cur_conchar_attr_init = 1;
+ disp->cur_conchar_attr = str->attr;
+ disp->cur_attr = vga_display_recalculate_attr (&disp->dc, str->attr);
+ }
+ else
+ {
+ /* Add two references to the colors. See comment above for
+ why we can assume that this will succeed. */
+ /* We intimately know that reference counting is only done
+ for the up to 8 colors mode. */
+ dynacolor_add_ref (disp->dc, disp->cur_attr & 7);
+ dynacolor_add_ref (disp->dc, (disp->cur_attr >> 4) & 7);
+ }
+
+ *(pos++) = charval & 0xff;
+
+ if (pos == mouse_cursor_pos)
+ disp->mousecursor.visible = 0;
+
+ *(pos++) = disp->cur_attr
+ | (disp->df_size == 512 ? (charval >> 5) & 0x8 : 0);
+
+ /* Perform reference counting. */
+ if (refpos->used)
+ {
+ dynafont_release (disp->df, refpos->chr);
+ /* We intimately know that reference counting is only done
+ for the up to 8 colors mode. */
+ dynacolor_release (disp->dc, refpos->attr & 7);
+ dynacolor_release (disp->dc, (refpos->attr >> 4) & 7);
+ }
+ refpos->used = 1;
+ refpos->chr = charval;
+ refpos->attr = disp->cur_attr;
+ refpos++;
+
+ /* Wrap around displayed area. */
+ str++;
+ }
+ return 0;
+}
+
+static error_t
+vga_set_dimension (void *handle, unsigned int width, unsigned int height)
+{
+ if (current_width && current_height)
+ vga_display_clear (handle, current_width * current_height, 0, 0);
+
+ current_width = width;
+ current_height = height;
+
+ /* FIXME: Should support greater dimensions by changing the video
+ mode. */
+
+ return 0;
+}
+
+
+static error_t
+vga_display_update (void *handle)
+{
+ struct vga_display *disp = handle;
+
+ if (disp->mousecursor.enabled)
+ draw_mousecursor (disp);
+
+ return 0;
+}
+
+
+static error_t
+vga_set_mousecursor_pos (void *handle, float x, float y)
+{
+ struct vga_display *disp = handle;
+
+ /* If the mouse did not move from the character position, don't
+ bother about updating the cursor position. */
+ if (disp->mousecursor.visible && x == (int) disp->mousecursor.posx
+ && y == (int) disp->mousecursor.posy)
+ return 0;
+
+ hide_mousecursor (disp);
+
+ disp->mousecursor.posx = x;
+ disp->mousecursor.posy = y;
+
+ if (disp->mousecursor.enabled)
+ draw_mousecursor (disp);
+
+ return 0;
+}
+
+
+static error_t
+vga_set_mousecursor_status (void *handle, int status)
+{
+ struct vga_display *disp = handle;
+
+ disp->mousecursor.enabled = status;
+ if (!status)
+ hide_mousecursor (disp);
+ else
+ draw_mousecursor (disp);
+
+ return 0;
+}
+
+
+
+struct driver_ops driver_vga_ops =
+ {
+ vga_display_init,
+ vga_display_start,
+ vga_display_fini,
+ NULL,
+ vga_display_restore_status
+ };
+
+static struct display_ops vga_display_ops =
+ {
+ vga_display_set_cursor_pos,
+ vga_display_set_cursor_status,
+ vga_display_scroll,
+ vga_display_clear,
+ vga_display_write,
+ vga_display_update,
+ vga_display_flash,
+ NULL,
+ vga_set_dimension,
+ vga_set_mousecursor_pos,
+ vga_set_mousecursor_status
+ };
diff --git a/console-client/xkb/MISSING-FEATURES b/console-client/xkb/MISSING-FEATURES
new file mode 100644
index 00000000..237c13d7
--- /dev/null
+++ b/console-client/xkb/MISSING-FEATURES
@@ -0,0 +1,32 @@
+Required for 100% compatibility with XKB:
+
+- Jukebox (possibility that this won't ever be done)
+- Loading the keydatabase
+- Proper indicator support
+- ISOLock
+- key lock
+- radio groups
+- overlays
+- Latching (properly, mostly works)
+- Enabling/disabling controls
+- The following controls are not implemented at all:
+ - SlowKeys
+ - BounceKeys
+ - StickyKeys
+ - MouseKeysAccel
+ - AccesXKeys
+ - AccesXTimeout
+ - AccessXFreedback (jukebox required)
+ - Overlay1 + Overlay2
+ - AudibleBell
+ - IgnoreGroupLock
+ - EnabledControls
+
+Hurd features:
+
+- Binding a string to a key (requires new action)
+ (It should be possible to insert this on other vcs too)
+- Binding executable to a key (requires new action)
+
+I'm sure I forgot some missing features, please report them:
+metgerards@student.han.nl
diff --git a/console-client/xkb/README b/console-client/xkb/README
new file mode 100644
index 00000000..de781143
--- /dev/null
+++ b/console-client/xkb/README
@@ -0,0 +1,178 @@
+-- Some random notes about the XKB module. --
+
+This XKB module used by the pc_kbd input driver can load XKB configuration
+files. XKB configuration files, the keymap files currently used in XFree,
+are very versatile.
+
+One of the biggest advantages of using XKB configurations is that both
+the Hurd and XFree work similarly. Another advantage is that it has many
+features:
+
+* Human readable configuration files with mechanisms like include
+ files, replacing keys, etc.
+
+* Many shift levels, all configurable per key type.
+
+* Support for four groups. These groups can be used for things like
+ additional alphabets.
+
+* Accessibility features like SlowKeys, StickyKeys, etc.
+
+* Dynamic behaviour; the user can define how all keys and indicators operate.
+
+
+Most support for basic operation have been implemented. Still there
+are many things not implemented. If some of these things are really
+important to you, please tell me!
+
+List of not implemented XKB features:
+
+* Accessibility and software repeat.
+* Jukebox support (user configurable audible messages)
+* Key database; loading new keynames from the X key database.
+* Error handling; partially done.
+* Indicators (Keyboard LEDs, etc.) not working, partially implemented.
+* ISOLock not implemented.
+* No support for non UTF-8 locales.
+* No support for rarely used features (Many of they don't even work in
+ X AFAIK).
+* Many other stuff doesn't work (lock, radio group, overlays, etc.).
+ If I forgot something important, please report it :)
+
+* The scanner and parser are still ugly and far from optimal!
+
+---
+
+Installation:
+
+This XKB module is an optional feature of pc-kbd input driver. Whether
+it is compiled in or not is defined by the precense of XKB_SUPPORT
+preprocessor symbol.
+
+In addition to the usual Hurd dependencies, you need yacc and lex
+programs to generate the scanner and parser.
+
+--
+
+Using it:
+
+To use it you need XKB configuration files. These are included with
+XFree and will be placed in /share/X11/xkb on most systems.
+
+This module adds the following set of options to pc-kbd for overriding
+the default behaviour:
+
+--xkbdir : The root directory of the xkb configuration, by default
+ this is /share/X11/xkb.
+
+--keymapfile : The file that hold the descriptions of the default
+ keymaps file. This file holds the description of all keymaps. This
+ path should be relative to the path set by `xkbdir'. By default
+ "keymap/xfree86" is used.
+
+--keymap : The keymap to use. By default en_US is used. Examples of
+ some other keymaps are: fr, us, de, dvorak. Example: --keymap=de
+
+--ctrlaltbs : CTRL+Alt+Backspace will exit the console client.
+--no-ctrlaltbs : CTRL+Alt+Backspace will not exit the console client.
+
+--repeat-delay : The number of jiffies the driver should wait before
+ starting to repeat keys. By default this is 50 jiffies.
+
+--repeat-interval : The number of jiffies the driver should wait
+ between each repeated key. By default this is 10 jiffies.
+
+I wrote some XKB extensions and configuration files to use these
+extensions. You can find these files in the xkb-data directory.
+
+The build system will copy these files to where your XKB
+configuration files are (usually /share/X11/xkb).
+
+for example: xkb-data/keymap/hurd will be copied to /share/X11/xkb/keymap.
+
+To use these extensions you have to use the hurd keymaps file. This
+holds all keymaps extended with Hurd features. Use "--keymapfile
+keymap/hurd" to select this file.
+
+After that you can use these keybindings:
+
+Alt + F1...F12: Switch VC.
+
+Alt + Left, Right: Switch to the console left or right of the current
+ console.
+
+Alt + Up, Down: Scroll the console by one line up or down.
+Ctrl + PgUp, PgDown: Scroll the console up or down with 1/2 screen.
+
+Alt + Home: Scroll the console to the 0% of the scrollback buffer.
+Ctrl + Home: Scroll the console to the 25% of the scrollback buffer.
+Alt + End: Scroll the console to the 100% of the scrollback buffer.
+Alt + End: Scroll the console to the 75% of the scrollback buffer.
+
+--
+
+Hurd features:
+
+If you want to use the Hurd features yourself make sure you put the
+changes in files that are not used by Xfree because it cannot parse
+these options. See how I did it for the configuration described in the
+previous paragraph.
+
+Function list:
+
+--
+ConsScroll: scroll the console x lines or to a fixed position.
+
+parameters:
+Screen = pos : Scroll to screen #pos.
+Screen += i : Scroll i screens up.
+Screen -= i : Scroll i screens down.
+line = pos : Scroll to line #pos.
+line += i : Scroll i lines up.
+line -= i : Scroll i lines down.
+percent = percentage : Scroll to PERCENTAGE % of the scrollback
+ buffer.
+
+Example:
+ConsScroll (screen += 0.5) --> Scroll down a 1/2 screen.
+
+--
+SwitchScreen: Switch to a VC.
+
+paramaters:
+screen = i : Switch to VC #i.
+screen += i : Switch to #i consoles to the right.
+screen -= i : Switch to #i consoles to the left.
+
+sameserver is a flag that will be added. I want to use it to switch to
+external consoles like X servers and SVGALib screens.
+
+example:
+SwitchScreen (screen += 1) --> Switch to the console right of the
+ current console.
+
+
+--
+More functions will be added. One to send keysyms to other VCs (can be
+usefull to control a mixer, ogg vorbis player, etc.). It should also
+be capable of inserting often used strings like "apfelkorn" :).
+
+A function to call the configuration dialog should be added.
+
+Functions to execute things should be added. Think about binding the
+reboot command to Ctrl+Alt+Delete for example. Wolfgang also asked me
+for scheme support, but I don't know enough about scheme to know what
+cool stuff is possible with this (I don't know scheme... Hey! Not
+everyone can be perfect ;)).
+
+Perhaps functions can be added to control the console client in other
+ways.
+
+More ideas would be interesting to hear :)
+
+--
+
+Enjoy this stuff and please send me many bug reports :)
+
+Marco Gerards
+(metgerards@student.han.nl)
diff --git a/console-client/xkb/TODO b/console-client/xkb/TODO
new file mode 100644
index 00000000..120dd486
--- /dev/null
+++ b/console-client/xkb/TODO
@@ -0,0 +1,8 @@
+For release:
+
+Use rules instead of keymaps
+Indicators.
+Use libihash.
+Better memory handling.
+Better debug output
+CLEAN UP!
diff --git a/console-client/xkb/compose.c b/console-client/xkb/compose.c
new file mode 100644
index 00000000..fb3f07ca
--- /dev/null
+++ b/console-client/xkb/compose.c
@@ -0,0 +1,592 @@
+/* compose.c -- Keysym composing
+
+ Copyright (C) 2003 Marco Gerards
+
+ Written by Marco Gerards <metgerards@student.han.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <X11/keysymdef.h>
+#include "xkb.h"
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <locale.h>
+#include <assert.h>
+
+/* Tokens that can be recognised by the scanner. */
+enum tokentype
+ {
+ UNKNOWN,
+ EOL,
+ REQKS,
+ SEMICOL,
+ STR,
+ PRODKS,
+ END
+ };
+
+/* The current token. */
+struct token
+{
+ enum tokentype toktype;
+ char value[50];
+} tok;
+
+/* The linenumber of the line currently parsed, used for returning
+ errors and warnings. */
+static int linenum;
+
+/* Read a token from the file CF. */
+static void
+read_token (FILE *cf)
+{
+ int c = fgetc (cf);
+ int pos = 0;
+
+ /* Remove whitespaces. */
+ while (c == ' ' || c == '\t')
+ c = fgetc (cf);
+
+ /* Comment, remove until end of line and return a EOL token. */
+ if (c == '#')
+ {
+ while (c != '\n')
+ c = fgetc (cf);
+ tok.toktype = EOL;
+ linenum++;
+ return;
+ }
+
+ /* End of file. */
+ if (c == EOF)
+ {
+ tok.toktype = END;
+ return;
+ }
+
+ /* Semicolon. */
+ if (c == ':')
+ {
+ tok.toktype = SEMICOL;
+ return;
+ }
+
+ /* End of line. */
+ if (c == '\n')
+ {
+ linenum++;
+ tok.toktype = EOL;
+ return;
+ }
+
+
+ /* Required keysym. */
+ if (c == '<')
+ {
+ while ((c = fgetc (cf)) != '>')
+ tok.value[pos++] = c;
+ tok.value[pos] = '\0';
+ tok.toktype = REQKS;
+ return;
+ }
+
+ /* Character string. */
+ if (c == '"')
+ {
+ while ((c = fgetc (cf)) != '"')
+ {
+ if (c == '\\')
+ c = fgetc (cf);
+
+ tok.value[pos++] = c;
+ }
+ tok.value[pos] = '\0';
+ tok.toktype = STR;
+ return;
+ }
+
+ /* Produced keysym. */
+ if (isalpha (c))
+ {
+ tok.value[pos++] = c;
+ while (isgraph (c = fgetc (cf)))
+ tok.value[pos++] = c;
+ tok.value[pos] = '\0';
+ tok.toktype = PRODKS;
+ ungetc (c, cf);
+ return;
+ }
+
+ /* Unknown token. */
+ tok.toktype = UNKNOWN;
+ return;
+}
+
+/* Compose sequence. */
+struct compose
+{
+ struct compose *left;
+ struct compose *right;
+ symbol *expected;
+ symbol produced;
+} *compose_tree;
+
+
+/* Compare symbol sequence s1 to symbol sequence s1. This function
+ works just like the strcmp function. */
+static int
+symbolscmp (symbol *s1, symbol *s2)
+{
+ while (*s1 && *s2 && (*s1 == *s2))
+ {
+ s1++;s2++;
+ }
+ if (*s1 < *s2)
+ return -1;
+ if (*s1 > *s2)
+ return 1;
+ return 0;
+}
+
+/* Compare symbol sequence s1 to symbol sequence s1, compare a maximum
+ of N symbols. This function works just like the strcmp function.
+ */
+static int
+symbolsncmp (symbol *s1, symbol *s2, int n)
+{
+ int cnt = 0;
+ while (*s1 && *s2 && (*s1 == *s2))
+ {
+ if (++cnt == n)
+ break;
+ s1++;s2++;
+ }
+
+ if (*s1 < *s2)
+ return -1;
+ if (*s1 > *s2)
+ return 1;
+ return 0;
+}
+
+
+/* Add the compose sequence EXP to the binary tree, store RESULT as
+ the keysym produced by EXP. */
+static struct compose *
+composetree_add (struct compose *tree, symbol *exp, symbol result)
+{
+ int cmp;
+
+ if (tree == NULL)
+ {
+ tree = malloc (sizeof (struct compose));
+ tree->expected = exp;
+ tree->produced = result;
+ tree->left = tree->right = NULL;
+
+ return tree;
+ }
+
+ cmp = symbolscmp (exp, tree->expected);
+ if (cmp == 0)
+ {
+ printf ("Warning: line %d: Double sequence.\n", linenum);
+ free (exp);
+ }
+ else if (cmp < 0)
+ tree->left = composetree_add (tree->left, exp, result);
+ else
+ tree->right = composetree_add (tree->right, exp, result);
+ return tree;
+}
+
+/* Parse the composefile CF and put all sequences in the binary tree
+ COMPOSE_TREE. This function may never fail because of syntactical or
+ lexalical errors, generate a warning instead. */
+static error_t
+parse_composefile (FILE *cf)
+{
+ void skip_line (void)
+ {
+ while (tok.toktype != EOL && tok.toktype != END)
+ read_token (cf);
+ }
+
+ for (;;)
+ {
+ /* Expected keysyms. */
+ symbol exp[50];
+ symbol *exps;
+ size_t expcnt = 0;
+ symbol sym;
+
+ read_token (cf);
+ /* Blank line. */
+ if (tok.toktype == EOL)
+ continue;
+
+ /* End of file, done parsing. */
+ if (tok.toktype == END)
+ return 0;
+
+ if (tok.toktype != REQKS)
+ {
+ printf ("Warning: line %d: Keysym expected on beginning of line.\n",
+ linenum);
+ skip_line ();
+ continue;
+ }
+
+ /* Keysym was recognised, add it. */
+ sym = XStringToKeysym (tok.value);
+ if (!sym)
+ {
+ printf ("Warning: line %d: Unknown keysym \"%s\".\n", linenum,
+ tok.value);
+ skip_line ();
+ continue;
+ }
+ exp[expcnt++] = sym;
+
+ do
+ {
+ read_token (cf);
+ /* If another required keysym is recognised, add it. */
+ if (tok.toktype == REQKS)
+ {
+ sym = XStringToKeysym (tok.value);
+ if (!sym)
+ {
+ printf ("Warning: line %d: Unknown keysym \"%s\".\n",
+ linenum, tok.value);
+ skip_line ();
+ continue;
+ }
+ exp[expcnt++] = sym;
+ }
+ } while (tok.toktype == REQKS);
+
+ if (tok.toktype != SEMICOL)
+ {
+ printf ("Warning: line %d: Semicolon expected.\n", linenum);
+ skip_line ();
+ continue;
+ }
+
+ read_token (cf);
+ /* Force token and ignore it. */
+ if (tok.toktype != STR)
+ {
+ printf ("Warning: line %d: string expected.\n", linenum);
+ skip_line ();
+ continue;
+ }
+
+ read_token (cf);
+ if (tok.toktype != PRODKS)
+ {
+ printf ("Warning: line %d: keysym expected.\n", linenum);
+ skip_line ();
+ continue;
+ }
+ sym = XStringToKeysym (tok.value);
+ if (!sym)
+ {
+ printf ("Warning: line %d: Unknown keysym \"%s\".\n", linenum,
+ tok.value);
+ skip_line ();
+ continue;
+ }
+
+ read_token (cf);
+ if (tok.toktype != EOL && tok.toktype != END)
+ {
+ printf ("Warning: line %d: end of line or end of file expected.\n",
+ linenum);
+ skip_line ();
+ continue;
+ }
+
+ /* Add the production rule. */
+ exp[expcnt++] = 0;
+ exps = malloc (sizeof (symbol) * expcnt);
+ memcpy (exps, exp, sizeof (symbol) * expcnt);
+ compose_tree = composetree_add (compose_tree, exps, sym);
+ }
+ return 0;
+}
+
+/* Read keysyms passed to this function by S until a keysym can be
+ composed. If the first keysym cannot start a compose sequence return
+ the keysym. */
+symbol
+compose_symbols (symbol s)
+{
+ /* Current position in the compose tree. */
+ static struct compose *treepos = NULL;
+ /* Current compose sequence. */
+ static symbol syms[100];
+ /* Current position in the compose sequence. */
+ static int pos = 0;
+ int cmp;
+
+ if (!treepos)
+ treepos = compose_tree;
+
+ /* Maximum sequence length reached. Some idiot typed this many
+ symbols and now we throw it all away, wheee!!! */
+ if (pos == 99)
+ {
+ treepos = compose_tree;
+ pos = 0;
+ }
+
+ /* Put the keysym in the compose sequence array. */
+ syms[pos++] = s;
+ syms[pos] = 0;
+
+ /* Search the tree for a keysym sequence that can match the current one. */
+ while (treepos)
+ {
+ cmp = symbolsncmp (syms, treepos->expected, pos);
+ if (cmp == 0)
+ {
+ /* The keysym sequence was partially recognised, check if it
+ can completely match. */
+ if (!symbolscmp (syms, treepos->expected))
+ {
+ symbol ret = treepos->produced;
+ treepos = compose_tree;
+ pos = 0;
+ return ret;
+ }
+
+ /* The sequence was partially recognised. */
+ return -1;
+ }
+
+ if (cmp < 0)
+ treepos = treepos->left;
+ else
+ treepos = treepos->right;
+ }
+
+ /* Nothing can be found. */
+ treepos = compose_tree;
+
+ /* This ks should've started a sequence but couldn't be found,
+ just return it. */
+ if (pos == 1)
+ {
+ pos = 0;
+ return s;
+ }
+
+ debug_printf ("Invalid\n");
+ /* Invalid keysym sequence. */
+ pos = 0;
+ return -1;
+}
+
+struct map_entry
+{
+ const char *left;
+ const char *right;
+};
+
+enum callback_result
+ {
+ NEXT,
+ DONE
+ };
+
+typedef enum callback_result (*map_callback) (void *context, struct map_entry *entry);
+
+static error_t
+map_iterate(const char *map_path, map_callback action, void *context)
+{
+ FILE *map;
+ char *buffer = NULL;
+ size_t buffer_size = 0;
+ size_t line_length = 0;
+
+ assert (map_path != NULL);
+ assert (action != NULL);
+
+ map = fopen (map_path, "r");
+
+ if (map == NULL)
+ return errno;
+
+ while ( (line_length = getline (&buffer, &buffer_size, map)) != -1)
+ {
+ /* skips empty lines and comments */
+ if (line_length < 1 || buffer[0] == '#')
+ continue;
+ else
+ {
+ struct map_entry entry = {NULL, NULL};
+ char *end = buffer + line_length;
+ char *p = buffer;
+
+ while (p != end && isspace(*p)) p++;
+
+ if (p == end)
+ continue;
+
+ entry.left = p;
+
+ while (p != end && !isspace(*p)) p++;
+
+ if (p != end)
+ {
+ *(p++) = 0;
+ while (p != end && isspace(*p)) p++;
+
+ if (p != end)
+ {
+ entry.right = p;
+ while (p != end && !isspace(*p)) p++;
+ if (p != end)
+ *p = 0;
+ }
+ }
+
+ if (action (context, &entry) == DONE)
+ break;
+ }
+ }
+ free (buffer);
+ fclose (map);
+ return 0;
+}
+
+struct matcher_context
+{
+ char *value;
+ char *result;
+};
+
+static enum callback_result
+match_left_set_right (void *context, struct map_entry *entry)
+{
+ struct matcher_context *ctx = (struct matcher_context *) context;
+
+ if (strcmp (ctx->value, entry->left) == 0)
+ {
+ ctx->result = strdup (entry->right);
+ return DONE;
+ }
+ return NEXT;
+}
+
+static enum callback_result
+match_right_set_left (void *context, struct map_entry *entry)
+{
+ struct matcher_context *ctx = (struct matcher_context *) context;
+
+ if (strcmp (ctx->value, entry->right) == 0)
+ {
+ ctx->result = strdup (entry->left);
+ return DONE;
+ }
+ return NEXT;
+}
+
+/* Search for a compose file.
+
+ According to Compose(5) man page the compose file searched in the
+ following locations:
+ - XCOMPOSEFILE variable.
+ - .XCompose at $HOME.
+ - System wide compose file for the current locale. */
+static char *
+get_compose_file_for_locale()
+{
+ struct matcher_context context = { NULL };
+ char *xcomposefile;
+ char *to_be_freed;
+ char *home;
+ int err;
+
+ xcomposefile = getenv ("XCOMPOSEFILE");
+ if (xcomposefile != NULL)
+ return strdup (xcomposefile);
+
+ home = getenv ("HOME");
+ if (home != NULL)
+ {
+ err = asprintf (&xcomposefile, "%s/.XCompose", home);
+ if (err != -1)
+ {
+ if (faccessat(AT_FDCWD, xcomposefile, R_OK, AT_EACCESS) == 0)
+ return xcomposefile;
+ else
+ {
+ free (xcomposefile);
+ /* TODO: check and report whether the compose file doesn't exist or
+ read permission was not granted to us. */
+ }
+ }
+ }
+
+ context.value = setlocale (LC_ALL, NULL);
+ map_iterate (X11_PREFIX "/share/X11/locale/locale.alias", match_left_set_right, &context);
+ to_be_freed = context.result;
+
+ if (context.result != NULL)
+ {
+ /* Current locale is an alias. Use the real name to index the database. */
+ context.value = context.result;
+ }
+ context.result = NULL;
+ map_iterate (X11_PREFIX "/share/X11/locale/compose.dir", match_right_set_left, &context);
+ free (to_be_freed);
+
+ /* compose.dir contains relative paths to compose files. */
+ to_be_freed = context.result;
+ err = asprintf (&context.result, X11_PREFIX "/share/X11/locale/%s", context.result);
+ if (err == -1)
+ context.result = NULL;
+
+ free (to_be_freed);
+ return context.result;
+}
+
+/* Read a Compose file. */
+error_t
+read_composefile (char *composefn)
+{
+ FILE *cf;
+
+ error_t err;
+
+ if (composefn == NULL)
+ composefn = get_compose_file_for_locale ();
+
+ cf = fopen (composefn, "r");
+ if (cf == NULL)
+ return errno;
+
+ err = parse_composefile (cf);
+ fclose (cf);
+
+ return err;
+}
diff --git a/console-client/xkb/kstoucs.c b/console-client/xkb/kstoucs.c
new file mode 100644
index 00000000..0211e9e3
--- /dev/null
+++ b/console-client/xkb/kstoucs.c
@@ -0,0 +1,51 @@
+struct ksmap {
+ int keysym;
+ unsigned int ucs;
+};
+
+#include "kstoucs_map.c"
+
+unsigned int
+KeySymToUcs4 (int keysym)
+{
+#ifdef XKB_DEBUG
+ char *XKeysymToString(int keysym);
+ printf ("KeySymToUcs4: %s (%d) -> ", XKeysymToString (keysym), keysym);
+unsigned int doit (int keysym)
+{
+#endif
+
+ /* Control characters not covered by keysym map. */
+ if (keysym > 0 && keysym < 32)
+ return keysym;
+
+ /* 'Unicode keysym' */
+ if ((keysym & 0xff000000) == 0x01000000)
+ return (keysym & 0x00ffffff);
+
+ unsigned int
+ find_ucs (int keysym, struct ksmap *first, struct ksmap *last)
+ {
+ struct ksmap *middle = first + (last - first) / 2;
+
+ if (middle->keysym == keysym)
+ return middle->ucs; /* base case: needle found. */
+ else if (middle == first && middle == last)
+ return 0; /* base case: empty search space. */
+ /* recursive cases: halve search space. */
+ else if (middle->keysym < keysym)
+ return find_ucs (keysym, middle+1, last);
+ else if (middle->keysym > keysym)
+ return find_ucs (keysym, first, middle-1);
+ return 0;
+ }
+
+ #define NUM_KEYSYMS (sizeof kstoucs_map / sizeof(struct ksmap))
+ return find_ucs(keysym, &kstoucs_map[0], &kstoucs_map[NUM_KEYSYMS - 1]);
+#ifdef XKB_DEBUG
+}
+ unsigned int ret = doit (keysym);
+ printf ("%d\n", ret);
+ return ret;
+#endif
+}
diff --git a/console-client/xkb/kstoucs_map.sh b/console-client/xkb/kstoucs_map.sh
new file mode 100644
index 00000000..624d3518
--- /dev/null
+++ b/console-client/xkb/kstoucs_map.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+SED=${SED:-sed}
+AWK=${AWK:-awk}
+
+$SED -n -e 's/#define XK_[a-zA-Z0-9_]\+\s\+\(0x[0-9a-fA-F]\+\)\s*\/\*\s*U+\([0-9a-fA-F]\+\).*\*\//\1\t0x\2/p' \
+ | $AWK '{ print strtonum($1) "\t" $2; }' \
+ | sort -n -k1 \
+ | $AWK '
+ BEGIN { print "struct ksmap kstoucs_map[] = {" }
+ {
+ if (NR == 1)
+ separator =" ";
+ else
+ separator =" , ";
+
+ if (strtonum($1) < 0x1000000)
+ print separator "{ " $1 ", " $2 " }"
+ }
+ END { print "};" }'
diff --git a/console-client/xkb/lex.l b/console-client/xkb/lex.l
new file mode 100644
index 00000000..d9198981
--- /dev/null
+++ b/console-client/xkb/lex.l
@@ -0,0 +1,386 @@
+/* XKB scanner.
+
+ Copyright (C) 2002, 03 Marco Gerards
+
+ Written by Marco Gerards <marco@student.han.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details. */
+
+
+ #include "xkb.h"
+ #include "parser.tab.h"
+ #include <string.h>
+ #include <unistd.h>
+ #include <stdlib.h>
+ #include <stdio.h>
+ #include <error.h>
+
+ int close_include (void);
+ int lineno = 1;
+ char *filename = "foo";
+
+%option nodebug
+
+%option UNPUT
+KEYCODE "<"[A-Z][-+A-Z0-9]*">"
+DIGIT [0-9]
+NUM {DIGIT}{DIGIT}*
+FLOAT {DIGIT}{DIGIT}*\.{DIGIT}{DIGIT}*
+HEX 0x[A-Za-z0-9]*
+IDENTIFIER [a-zA-Z][a-zA-Z0-9_]*
+CPPCOMMENT "//".*"\n"
+%%
+ /* When the end of a file is reached, close the
+ current one and pop the next file to process
+ of the stack. */
+
+ /* Filter out comment. */
+{CPPCOMMENT} { lineno++; }
+ "/*" {
+ int c;
+
+ while((c = input()) != 0)
+ {
+ if(c == '\n')
+ lineno++;
+
+ else if(c == '*')
+ {
+ if((c = input()) == '/')
+ break;
+ else
+ unput(c);
+ }
+ }
+ }
+
+\n { lineno++; }
+
+ /* Beginning of sections. */
+xkb_keymap { return XKBKEYMAP; }
+xkb_symbols { return XKBSYMBOLS; }
+xkb_keycodes { return XKBKEYCODES; }
+xkb_types { return XKBTYPES; }
+xkb_compatibility { return XKBCOMPAT; }
+xkb_geometry { return XKBGEOMETRY; }
+
+
+ /* Real modifiers. */
+shift { return SHIFT; }
+lock { return LOCK; }
+control { return CONTROL; }
+ctrl { return CONTROL; }
+mod1 { return MOD1; }
+mod2 { return MOD2; }
+mod3 { return MOD3; }
+mod4 { return MOD4; }
+mod5 { return MOD5; }
+
+ /* Levels. */
+anylevel { yylval.val = 0; return LEVEL; }
+levelone { yylval.val = 1; return LEVEL; }
+level1 { yylval.val = 1; return LEVEL; }
+level2 { yylval.val = 2; return LEVEL; }
+level3 { yylval.val = 3; return LEVEL; }
+level4 { yylval.val = 4; return LEVEL; }
+level[1-9][0-9]* { yylval.val = atoi(yytext + 5); return LEVEL; }
+
+ /* Groups. */
+group1 { yylval.val = 1; return GROUPNUM; }
+group2 { yylval.val = 2; return GROUPNUM; }
+group3 { yylval.val = 3; return GROUPNUM; }
+group4 { yylval.val = 4; return GROUPNUM; }
+
+ /* Booleans */
+true { yylval.val = 1; return BOOLEAN; }
+false { yylval.val = 0; return BOOLEAN; }
+
+ /* Interpretation */
+interpret { return INTERPRET; }
+usemodmap { return USEMODMAP; }
+usemodmapmods { return USEMODMAP; }
+modmapmods { return USEMODMAP; }
+repeat { return REPEAT; }
+locking { return LOCKING; }
+locks { return LOCKING; }
+virtualmodifier { return VIRTUALMODIFIER; }
+virtualmod { return VIRTUALMODIFIER; }
+
+ /* Interpretation match. */
+noneof { yylval.val = 0; return INTERPMATCH ;}
+anyofornone { yylval.val = 1; return INTERPMATCH ;}
+anyof { yylval.val = 2; return INTERPMATCH ;}
+allof { yylval.val = 3; return INTERPMATCH ;}
+exactly { yylval.val = 4; return INTERPMATCH ;}
+
+ /* SetMods action. */
+clearlocks { return CLEARLOCKS; }
+mods { return MODS; }
+
+unlock { return UNLOCK; }
+both { return BOTH; }
+neither { return NEITHER; }
+
+ /* Actions. */
+setmods { return SETMODS; }
+latchmods { return LATCHMODS; }
+lockmods { return LOCKMODS; }
+setgroup { return SETGROUP; }
+latchgroup { return LATCHGROUP; }
+lockgroup { return LOCKGROUP; }
+setptrdflt { return PTRDFLT; }
+setpointerdefault { return PTRDFLT; }
+lockptrbtn { return LOCKPTRBTN; }
+lockpointerbutton { return LOCKPTRBTN; }
+lockptrbutton { return LOCKPTRBTN; }
+lockpointerbtn { return LOCKPTRBTN; }
+setcontrols { return SETCONTROLS; }
+lockcontrols { return LOCKCONTROLS; }
+terminateserver { return TERMINATE; }
+terminate { return TERMINATE; }
+switchscreen { return SWITCHSCREEN; }
+consscroll { return CONSSCROLL; }
+consolescroll { return CONSSCROLL; }
+moveptr { return MOVEPTR; }
+private { return PRIVATE; }
+ /* Action parameters. */
+latchtolock { return LATCHTOLOCK; }
+group { return GROUP; }
+groups { return GROUPS; }
+accel { return ACCEL; }
+accelerate { return ACCEL; }
+default { return DEFAULT; }
+count { return COUNT; }
+controls { return CONTROLS; }
+same { return SAMESERVER; }
+sameserver { return SAMESERVER; }
+screen { return SCREEN; }
+line { return LINE; }
+percentage { return PERCENT; }
+ptrbtn { return PTRBTN ; }
+pointerbutton { return PTRBTN ; }
+
+action { return ACTION; }
+actions { return ACTIONS; }
+
+whichmodstate { return WHICHMODSTATE; }
+whichmodifierstate { return WHICHMODSTATE; }
+whichgroupstate { return WHICHGROUPSTATE; }
+
+ /* Match state for indicator. */
+base { yylval.val = 0; return WHICHSTATE; }
+latched { yylval.val = 1; return WHICHSTATE; }
+locked { yylval.val = 4; return WHICHSTATE; }
+effective { yylval.val = 8; return WHICHSTATE; }
+
+ /* Bits for binary controls. */
+repeatkeys { yylval.val = 0; return CONTROLFLAG; }
+autorepeat { yylval.val = 0; return CONTROLFLAG; }
+accessxkeys { yylval.val = 0; return CONTROLFLAG; }
+slowkeys { yylval.val = 0; return CONTROLFLAG; }
+bouncekeys { yylval.val = 0; return CONTROLFLAG; }
+stickykeys { yylval.val = 0; return CONTROLFLAG; }
+accessxtimeout { yylval.val = 0; return CONTROLFLAG; }
+accessxfeedback { yylval.val = 0; return CONTROLFLAG; }
+mousekeys { yylval.val = 0; return CONTROLFLAG; }
+mousekeysaccel { yylval.val = 0; return CONTROLFLAG; }
+audiblebell { yylval.val = 0; return CONTROLFLAG; }
+ignoregrouplock { yylval.val = 0; return CONTROLFLAG; }
+
+index { return INDEX; }
+name { return NAME; }
+symbols { return SYMBOLS; }
+key { return KEY; }
+
+ /* Mouse buttons. */
+button1 { yylval.val = 1; return BUTTONNUM; }
+button2 { yylval.val = 2; return BUTTONNUM; }
+button3 { yylval.val = 3; return BUTTONNUM; }
+button4 { yylval.val = 4; return BUTTONNUM; }
+button5 { yylval.val = 5; return BUTTONNUM; }
+button { return BUTTON; }
+
+ /* Fuzzyness. */
+any { return ANY; }
+all { return ALL; }
+none { return NONE; }
+
+defaultbutton { return DEFAULTBTN; }
+affect { return AFFECT; }
+
+allowexplicit { return ALLOWEXPLICIT; }
+
+ /* Feedback to the keyboard. */
+driveskeyboard { return DRIVESKBD; }
+driveskbd { return DRIVESKBD; }
+ledsdrivekbd { return DRIVESKBD; }
+ledsdrivekeyboard { return DRIVESKBD; }
+indicatordriveskbd { return DRIVESKBD; }
+indicatordriveskeyboard { return DRIVESKBD; }
+
+
+modifier_map { return MODMAP; }
+minimum { return MINIMUM; }
+maximum { return MAXIMUM; }
+virtual { return VIRTUAL; }
+alias { return ALIAS; }
+indicator { return INDICATOR; }
+virtual_modifiers { return VMODS; }
+virtualmods { return VMODS; }
+vmods { return VMODS; }
+type { return TYPE; }
+data { return DATA; }
+modifiers { return MODS; }
+map { return MAP; }
+level_name { return LEVEL_NAME; }
+preserve { return PRESERVE; }
+
+ /* Section flags. */
+partial { yylval.val = 1; return FLAGS; }
+complete { yylval.val = 2; return FLAGS; }
+fc { yylval.val = 4; return FLAGS; }
+fd { yylval.val = 8; return FLAGS; }
+cp { return XKBCOMPAT; }
+
+ /* Section merge modes. */
+include { yylval.mergemode = defaultmm; return INCLUDE; }
+augment { yylval.mergemode = augment; return AUGMENT; }
+replace { yylval.mergemode = replace; return REPLACE; }
+override { yylval.mergemode = override; return OVERRIDE; }
+
+isolock { return ISOLOCK; }
+pointers { return POINTERS;}
+ptr { return POINTERS;}
+noaction { return NOACTION;}
+groupswrap { return GROUPSWRAP; }
+wrapgroups { return GROUPSWRAP; }
+clampgroups { return GROUPSCLAMP; }
+groupsredirect { return GROUPSREDIRECT; }
+overlay1 { yylval.val = 1; return OVERLAY; }
+overlay2 { yylval.val = 2; return OVERLAY; }
+
+ /* String. */
+\"([^"]|\\\")*\" {
+ yytext[strlen (yytext) - 1] = '\0';
+ yylval.str = strdup (yytext + 1);
+ return STR;
+ }
+ /* Ignore whitespace. */
+[ \t]*
+ /* A keycode. */
+{KEYCODE} { yylval.str = strdup (yytext) + 1; yylval.str[strlen (yylval.str) - 1] = 0; return KEYCODE; }
+ /* A float vlaue. */
+{FLOAT} { yylval.dbl = atof (yytext); return FLOAT; }
+ /* An integer. */
+{NUM} { yylval.val = atoi (yytext); return NUM; }
+ /* A hexadecimal value. */
+{HEX} { sscanf (yytext, "0x%X", &yylval.val); return HEX; }
+ /* An identifier. */
+{IDENTIFIER} { yylval.str = strdup (yytext); return IDENTIFIER; }
+ /* All unrecognized characters. */
+. { return yytext[0]; }
+%%
+ /* Stupid hack, the current version of flex is fucked. */
+ #define yytext_ptr yytext
+
+ void
+ scanner_unput (int c)
+ {
+ unput (c);
+ }
+
+ int
+ yywrap (void)
+ {
+ if (close_include () == 0)
+ return 0;
+ else
+ return 1;
+ }
+
+ #define MAX_INCLUDE_DEPTH 50
+
+ /* An include file on the stack. */
+ struct include_stack
+ {
+ YY_BUFFER_STATE buffer;
+ mergemode merge_mode;
+ int currline;
+ char *filename;
+ } include_stack[MAX_INCLUDE_DEPTH];
+ int include_stack_ptr = 0;
+
+ /* Add an file to the stack. */
+ void
+ include_file (FILE *file, mergemode new_mm, char *fname)
+ {
+ YY_BUFFER_STATE buffer;
+
+ debug_printf ("including file %s\n.", fname);
+
+ if (include_stack_ptr >= MAX_INCLUDE_DEPTH)
+ {
+ fprintf (stderr, "Includes nested too deeply\n");
+ exit (EXIT_FAILURE);
+ }
+
+ include_stack[include_stack_ptr].filename = filename;
+ include_stack[include_stack_ptr].currline = lineno;
+ include_stack[include_stack_ptr].merge_mode = merge_mode;
+ include_stack[include_stack_ptr++].buffer = YY_CURRENT_BUFFER;
+ filename = fname;
+ lineno = 1;
+ merge_mode = new_mm;
+
+ buffer = yy_create_buffer (file, YY_BUF_SIZE);
+ yy_switch_to_buffer (buffer);
+ }
+
+ /* Close an includefile. returns 0 on success */
+ int
+ close_include (void)
+ {
+ if ( --include_stack_ptr < 0 )
+ {
+ fprintf (stderr, "Unexpected end of file at %s:%d.\n", filename, lineno);
+ return (1);
+ }
+ else
+ {
+ fclose (yyin);
+ yy_delete_buffer (YY_CURRENT_BUFFER);
+ merge_mode = include_stack[include_stack_ptr].merge_mode;
+ lineno = include_stack[include_stack_ptr].currline;
+ filename = include_stack[include_stack_ptr].filename;
+ yy_switch_to_buffer (include_stack[include_stack_ptr].buffer);
+ debug_printf ("closing file. going back to %s.\n", filename);
+ return (0);
+ }
+ }
+
+ void
+ yyerror (char *s)
+ {
+ fprintf (stderr, "%s:%d: %s\n", filename, lineno, s);
+ // error_at_line (0, 1, filename, lineno, "foo %d\n", 2);
+ }
+
+ int
+ scanner_get_current_location()
+ {
+ return lineno;
+ }
+
+ const char*
+ scanner_get_current_file()
+ {
+ return filename;
+ }
diff --git a/console-client/xkb/parser.y b/console-client/xkb/parser.y
new file mode 100644
index 00000000..27b62145
--- /dev/null
+++ b/console-client/xkb/parser.y
@@ -0,0 +1,1605 @@
+/* parser.y -- XKB parser.
+
+ Copyright (C) 2003, 2004 Marco Gerards
+
+ Written by Marco Gerards <marco@student.han.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details. */
+
+%{
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "xkb.h"
+
+void yyerror(char *);
+int yylex (void);
+static error_t include_section (char *incl, int sectionsymbol, char *dirname,
+ mergemode);
+static error_t include_sections (char *incl, int sectionsymbol, char *dirname,
+ mergemode);
+void close_include ();
+static void skipsection (void);
+static error_t set_default_action (struct xkb_action *, struct xkb_action **);
+static void key_set_keysym (struct key *key, group_t group, int level,
+ symbol ks);
+static void key_new (char *keyname);
+static void key_delete (char *keyname);
+void scanner_unput (int c);
+static void remove_symbols (struct key *key, group_t group);
+
+struct xkb_interpret *current_interpretation;
+struct xkb_action *current_action;
+struct xkb_indicator indi;
+struct xkb_indicator *current_indicator = &indi;
+struct key defkey;
+struct key *default_key = &defkey;
+
+/* The default settings for actions. */
+struct xkb_action default_setmods = { SA_SetMods };
+struct xkb_action default_lockmods = { SA_LockMods };
+struct xkb_action default_latchmods = { SA_LatchMods };
+struct xkb_action default_setgroup = { SA_SetGroup };
+struct xkb_action default_latchgroup = { SA_LatchGroup };
+struct xkb_action default_lockgroup = { SA_LockGroup };
+struct xkb_action default_moveptr = { SA_MovePtr };
+struct xkb_action default_ptrbtn = { SA_PtrBtn };
+struct xkb_action default_lockptrbtn = { SA_LockPtrBtn };
+struct xkb_action default_ptrdflt = { SA_SetPtrDflt };
+struct xkb_action default_setcontrols = { SA_SetControls };
+struct xkb_action default_lockcontrols = { SA_LockControls };
+struct xkb_action default_isolock = { SA_ISOLock };
+struct xkb_action default_switchscrn = { SA_SwitchScreen };
+struct xkb_action default_consscroll = { SA_ConsScroll };
+
+static struct key *current_key;
+
+/* The dummy gets used when the original may not be overwritten. */
+static struct key dummy_key;
+
+/* The current parsed group. */
+static int current_group;
+static int current_rmod = 0;
+
+/* The current symbol in the currently parsed key. */
+static int symbolcnt;
+static int actioncnt;
+
+mergemode merge_mode = override;
+
+//#define YYDEBUG 1
+
+#ifndef YY_NULL
+#define YY_NULL 0
+#endif
+
+static struct keytype *current_keytype;
+%}
+
+%union {
+ int val;
+ char *str;
+ modmap_t modmap;
+ struct xkb_action *action;
+ double dbl;
+ mergemode mergemode;
+}
+
+%token XKBKEYMAP "xkb_keymap"
+%token XKBKEYCODES "xkb_keycodes"
+%token XKBCOMPAT "xkb_compatibility"
+%token XKBGEOMETRY "xkb_geometry"
+%token XKBTYPES "xkb_types"
+%token XKBSYMBOLS "xkb_symbols"
+%token STR
+%token HEX
+%token FLAGS
+%token KEYCODE
+%token NUM
+%token MINIMUM "minimum"
+%token MAXIMUM "maximum"
+%token VIRTUAL "virtual"
+%token INDICATOR "indicator"
+%token ALIAS "alias"
+%token IDENTIFIER
+%token VMODS "virtualmods"
+%token TYPE "type"
+%token DATA "data"
+%token MAP "map"
+%token LEVEL_NAME "level_name"
+%token PRESERVE "preserve"
+%token LEVEL
+%token USEMODMAP "usemodmap"
+%token REPEAT "repeat"
+%token LOCKING "locking"
+%token VIRTUALMODIFIER "virtualmod"
+%token BOOLEAN
+%token INTERPRET "interpret"
+%token INTERPMATCH
+%token CLEARLOCKS "clearlocks"
+%token MODS "mods"
+%token SETMODS "setmods"
+%token LATCHMODS "latchmods"
+%token LOCKMODS "lockmods"
+%token ACTION "action"
+%token LATCHTOLOCK "latchtolock"
+%token GROUP "group"
+%token GROUPS "groups"
+%token SETGROUP "setgroup"
+%token LATCHGROUP "latchgroup"
+%token LOCKGROUP "lockgroup"
+%token ACCEL "accel"
+%token MOVEPTR "moveptr"
+%token PRIVATE "private"
+%token BUTTON "button"
+%token BUTTONNUM
+%token DEFAULT "default"
+%token COUNT "count"
+%token PTRBTN "ptrbtn"
+%token DEFAULTBTN "defaultbutton"
+%token ALL "all"
+%token NONE "none"
+%token ANY "any"
+%token CONTROLFLAG
+%token AFFECT "affect"
+%token PTRDFLT "setptrdflt"
+%token LOCKPTRBTN "lockptrbtn"
+%token SETCONTROLS "setcontrols"
+%token LOCKCONTROLS "lockcontrols"
+%token CONTROLS "controls"
+%token TERMINATE "terminate"
+%token WHICHMODSTATE "whichmodstate"
+%token WHICHGROUPSTATE "whichgroupstate"
+%token WHICHSTATE "whichstate"
+%token INDEX "index"
+%token ALLOWEXPLICIT "allowexplicit"
+%token DRIVESKBD "driveskbd"
+//%token AFFECTBTNLOCK
+%token SYMBOLS "symbols"
+%token NAME "name"
+%token GROUPNUM
+%token ACTIONS "actions"
+%token KEY "key"
+%token MODMAP "modifier_map"
+%token SHIFT "shift"
+%token LOCK "lock"
+%token CONTROL "control"
+%token MOD1 "mod1"
+%token MOD2 "mod2"
+%token MOD3 "mod3"
+%token MOD4 "mod4"
+%token MOD5 "mod5"
+%token UNLOCK "unlock"
+%token BOTH "both"
+%token NEITHER "neither"
+
+%token INCLUDE "include"
+%token AUGMENT "augment"
+%token OVERRIDE "override"
+%token REPLACE "replace"
+
+
+%token ISOLOCK "isolock"
+%token POINTERS "pointers"
+%token NOACTION "noaction"
+%token GROUPSWRAP "groupswrap"
+%token GROUPSCLAMP "groupsclamp"
+%token GROUPSREDIRECT "groupsredirect"
+%token OVERLAY "overlay"
+%token SWITCHSCREEN "switchscreen"
+%token SAMESERVER "sameserver"
+%token SCREEN "screen"
+%token LINE "line"
+%token PERCENT "percent"
+%token CONSSCROLL "consscroll"
+%token FLOAT "float"
+%type <str> STR KEYCODE IDENTIFIER
+%type <val> FLAGS NUM HEX vmod level LEVEL rmod BOOLEAN symbol INTERPMATCH
+%type <val> clearlocks usemodmap latchtolock noaccel button BUTTONNUM
+%type <val> ctrlflags allowexplicit driveskbd
+%type <val> DRIVESKBD GROUPNUM group symbolname groups whichstate WHICHSTATE
+/* Booleans */
+%type <val> locking repeat groupswrap groupsclamp sameserver
+%type <modmap> mods
+%type <action> action
+%type <action> ctrlparams
+%type <mergemode> include
+%type <dbl> FLOAT
+//%debug
+
+%%
+/* A XKB keymap. */
+xkbkeymap:
+ "xkb_keymap" '{' keymap '}' ';' { YYACCEPT; }
+| "xkb_keymap" STR '{' keymap '}' ';' { YYACCEPT; }
+| '{' keymap '}' { YYACCEPT; } ';'
+;
+
+/* A XKB configuration has many sections. */
+keymap:
+ /* empty */
+| keymap types
+| keymap keycodes
+| keymap compat
+| keymap symbols
+| keymap geometry
+;
+
+include:
+ "include" { $$ = defaultmm; }
+| "augment" { $$ = augment; }
+| "replace" { $$ = replace; }
+| "override" { $$= override; }
+;
+
+/* All flags assigned to a section. */
+flags:
+ /* empty */
+| flags FLAGS
+;
+
+/* The header of a keycode section. */
+keycodes:
+ flags "xkb_keycodes" '{' keycodesect '}' ';'
+| flags "xkb_keycodes" STR '{' keycodesect '}' ';'
+;
+
+/* Process the includes on the stack. */
+keycodesinclude:
+ '{' keycodesect '}' { close_include (); }
+| keycodesinclude '{' keycodesect '}' { close_include (); }
+;
+
+/* The first lines of a keycode section. The amount of keycodes are
+ declared here. */
+keycodesect:
+/* empty */
+| "minimum" '=' NUM ';' keycodesect
+ {
+ min_keys = $3;
+ debug_printf ("working on key: %d\n", $3);
+ current_key = &keys[$3];
+ }
+| MAXIMUM '=' NUM ';' keycodesect
+ {
+ max_keys = $3;
+ keys = calloc ($3, sizeof (struct key));
+ }
+| KEYCODE '=' NUM ';'
+ { keyname_add ($1, $3); }
+ keycodesect
+| "replace" KEYCODE '=' NUM ';'
+ { keyname_add ($2, $4); }
+ keycodesect
+| "indicator" NUM '=' STR ';' keycodesect { }
+| "virtual" INDICATOR NUM '=' STR ';' keycodesect
+| "alias" KEYCODE '=' KEYCODE ';'
+ {
+ keycode_t key = keyname_find ($4);
+ if (key)
+ keyname_add ($2, key);
+ else
+ {
+ key = keyname_find ($2);
+ if (key)
+ keyname_add ($4, key);
+ }
+ }
+ keycodesect
+| include STR
+ { include_sections ($2, XKBKEYCODES, "keycodes", $1); }
+ keycodesinclude keycodesect
+;
+
+/* The header of a keytypes section. */
+types:
+ flags "xkb_types" '{' typessect '}' ';'
+| flags "xkb_types" STR '{' typessect '}' ';'
+;
+
+/* A list of virtual modifier declarations (see vmods_def), seperated
+ by commas. */
+vmodslist:
+ IDENTIFIER { vmod_add ($1); }
+| vmodslist ',' IDENTIFIER { vmod_add ($3); }
+;
+
+/* Virtual modifiers must be declared before they can be used. */
+vmods_def:
+ "virtualmods" vmodslist ';'
+;
+
+/* Return the number of the virtual modifier. */
+vmod:
+ IDENTIFIER
+ { if (($$ = vmod_find ($1)) != 0)
+ $$ = 1 << ($$ - 1);
+ else
+ fprintf(stderr, "warning: %s virtual modifier is not defined.", $1);
+ }
+;
+
+/* A single realmodifier. */
+rmod:
+ "shift" { $$ = RMOD_SHIFT; }
+| "lock" { $$ = RMOD_LOCK; }
+| "control" { $$ = RMOD_CTRL; }
+| "mod1" { $$ = RMOD_MOD1; }
+| "mod2" { $$ = RMOD_MOD2; }
+| "mod3" { $$ = RMOD_MOD3; }
+| "mod4" { $$ = RMOD_MOD4; }
+| "mod5" { $$ = RMOD_MOD5; }
+;
+
+/* One of more modifiers, seperated by '+'. A modmap_t will return all real
+ and virtual modifiers specified. */
+mods:
+ mods '+' rmod { $$.rmods = $1.rmods | $3; }
+| mods '+' vmod { $$.vmods = $1.vmods | $3; }
+ /* Use a mid-rule action to start with no modifiers. */
+| { $<modmap>$.rmods = 0; $<modmap>$.vmods = 0; } rmod { $$.rmods = $2; }
+| { $<modmap>$.rmods = 0; $<modmap>$.vmods = 0; } vmod { $$.vmods = $2; }
+| "all" { $$.rmods = 0xFF; $$.vmods = 0xFFFF; }
+| "none" { $$.rmods = 0; $$.vmods = 0; }
+;
+
+/* The numeric level starts with 0. Level1-Level4 returns 0-3, also
+ numeric values can be used to describe a level. */
+level:
+ LEVEL { $$ = $1 - 1; }
+| "any" { $$ = 0; }
+| NUM { $$ = $1 - 1; }
+;
+
+/* A single keytype. */
+type:
+ /* Empty */
+| type MODS '=' mods ';'
+ { current_keytype->modmask = $4; }
+| type MAP '[' mods ']' '=' level ';'
+ { keytype_mapadd (current_keytype, $4, $7); }
+| type "level_name" '[' level ']' '=' STR ';'
+| type "preserve" '[' mods ']' '=' mods ';'
+ { keytype_preserve_add (current_keytype, $4, $7); }
+;
+
+/* Process the includes on the stack. */
+typesinclude:
+ '{' typessect '}' { close_include (); }
+| typesinclude '{' typessect '}' { close_include (); }
+;
+
+/* A keytype section contains keytypes and virtual modifier declarations. */
+typessect:
+ /* Empty */
+| typessect vmods_def
+| typessect TYPE STR { keytype_new ($3, &current_keytype); }'{' type '}' ';' { }
+| typessect include STR
+ { include_sections ($3, XKBTYPES, "types", $2); }
+ typesinclude
+;
+
+/* The header of a compatibility section. */
+compat:
+ flags "xkb_compatibility" '{' compatsect '}' ';'
+| flags "xkb_compatibility" STR '{' compatsect '}' ';'
+;
+
+/* XXX: A symbol can be more than just an identifier (hex). */
+symbol:
+ IDENTIFIER { $$ = (int) XStringToKeysym ( $1) ? : -1; }
+| ANY { $$ = 0; }
+| error { yyerror ("Invalid symbol."); }
+;
+
+/* Which kinds of modifiers (like base, locked, etc.) can affect the
+ indicator. */
+whichstate:
+ WHICHSTATE { $$ = $1; }
+| "none" { $$ = 0; }
+| "any" { $$ = 0xff; } /* XXX */
+;
+
+/* The groups that can affect a indicator. */
+groups:
+ groups '+' group
+| groups '-' group
+| group {}
+| "all" { $$ = 0xff; } /* XXX */
+;
+
+indicators:
+/* empty */
+| indicators indicator
+;
+
+/* An indicator desciption. */
+indicator:
+ "mods" '=' mods ';' { current_indicator->modmap = $3; }
+| "groups" '=' groups ';' { current_indicator->groups = $3; }
+| "controls" '=' ctrls ';'
+| "whichmodstate" '=' whichstate ';' { current_indicator->which_mods = $3; }
+| "whichgroupstate" '=' whichstate ';' { current_indicator->which_mods = $3; }
+| allowexplicit ';' {} /* Ignored for now. */
+| driveskbd ';' {}
+| "index" '=' NUM ';' {}
+;
+
+/* Boolean for allowexplicit. */
+allowexplicit:
+ '~' "allowexplicit" { $$ = 0; }
+| '!' "allowexplicit" { $$ = 0; }
+| "allowexplicit" { $$ = 1; }
+| "allowexplicit" '=' BOOLEAN { $$ = $3; }
+;
+
+/* Boolean for driveskbd. */
+driveskbd:
+ '~' "driveskbd" { $$ = 0; }
+| '!' "driveskbd" { $$ = 0; }
+| "driveskbd" { $$ = 1; }
+| "driveskbd" '=' BOOLEAN { $$ = $3; }
+;
+
+interprets:
+ /* Empty */
+| interprets interpret
+;
+
+/* A single interpretation. */
+interpret:
+ "usemodmap" '=' level ';'
+ { current_interpretation->match &= 0x7F | ($3 << 7); } /* XXX */
+| repeat ';'
+ {
+ current_interpretation->flags &= ~(KEYREPEAT | KEYNOREPEAT);
+ current_interpretation->flags |= $1;
+ }
+| locking ';' {}
+| "virtualmod" '=' vmod ';' { current_interpretation->vmod = $3; }
+| "action" '=' action ';'
+ {
+ memcpy (&current_interpretation->action, $3, sizeof (xkb_action_t));
+ free ($3);
+ }
+;
+
+/* Process the includes on the stack. */
+compatinclude:
+ '{' compatsect '}' { close_include (); }
+| compatinclude '{' compatsect '}' { close_include (); }
+;
+
+/* The body of a compatibility section. */
+compatsect:
+ /* Empty */
+| compatsect vmods_def
+| compatsect "interpret" '.'
+ { current_interpretation = &default_interpretation; }
+ interpret
+| compatsect "interpret" symbol
+ {
+ if ($3 != -1)
+ {
+ interpret_new (&current_interpretation, $3);
+ current_interpretation->match |= 1;
+ }
+ }
+ '{' interprets '}' ';'
+| compatsect "interpret" symbol '+' rmod
+ {
+ if ($3 != -1)
+ {
+ interpret_new (&current_interpretation, $3);
+ current_interpretation->rmods = $5;
+ current_interpretation->match |= 4;
+ }
+ }
+ '{' interprets '}' ';'
+| compatsect "interpret" symbol '+' "any"
+ {
+ if ($3 != -1)
+ {
+ interpret_new (&current_interpretation, $3);
+ current_interpretation->rmods = 255;
+ current_interpretation->match |= 2;
+ }
+ }
+ '{' interprets '}' ';'
+| compatsect "interpret" symbol '+' INTERPMATCH '(' mods ')'
+ {
+ if ($3 != -1)
+ {
+ interpret_new (&current_interpretation, $3);
+ current_interpretation->rmods = $7.rmods;
+ current_interpretation->match |= $5;
+ }
+ }
+ '{' interprets '}' ';'
+| compatsect GROUP NUM '=' mods ';'
+| compatsect "indicator" STR '{' indicators '}' ';'
+| compatsect include STR
+ { include_sections ($3, XKBCOMPAT, "compat", $2); }
+ compatinclude
+| compatsect actiondef
+| compatsect "indicator" '.' indicator
+;
+
+
+ /* Booleans */
+/* Boolean for clearlocks. */
+clearlocks:
+ '~' "clearlocks" { $$ = 0; }
+| '!' "clearlocks" { $$ = 0; }
+| "clearlocks" { $$ = 1; }
+| "clearlocks" '=' BOOLEAN { $$ = $3; }
+;
+
+/* Boolean for latchtolock. */
+latchtolock:
+ '~' "latchtolock" { $$ = 0; }
+| '!' "latchtolock" { $$ = 0; }
+| "latchtolock" { $$ = 1; }
+| "latchtolock" '=' BOOLEAN { $$ = $3; }
+;
+
+/* Boolean for useModMap. */
+usemodmap:
+ '~' "usemodmap" { $$ = 0; }
+| '!' "usemodmap" { $$ = 0; }
+| "usemodmap" { $$ = 1; }
+| "usemodmap" '=' BOOLEAN { $$ = $3; }
+;
+
+/* Boolean for locking. */
+locking:
+ '~' "locking" { $$ = 0; }
+| '!' "locking" { $$ = 0; }
+| "locking" { $$ = 1; }
+| "locking" '=' BOOLEAN { $$ = $3; }
+;
+
+/* Boolean for repeat. */
+repeat:
+ '~' "repeat" { $$ = KEYNOREPEAT; }
+| '!' "repeat" { $$ = KEYNOREPEAT; }
+| "repeat" { $$ = KEYREPEAT; }
+| "repeat" '=' BOOLEAN
+ {
+ if ($3)
+ $$ = KEYREPEAT;
+ else
+ $$ = KEYNOREPEAT;
+ }
+;
+
+/* Boolean for groupswrap. */
+groupswrap:
+ '~' "groupswrap" { $$ = 0; }
+| '!' "groupswrap" { $$ = 0; }
+| "groupswrap" { $$ = 1; }
+| "groupswrap" '=' BOOLEAN { $$ = $3; }
+;
+
+/* Boolean for groupsclamp. */
+groupsclamp:
+ '~' "groupsclamp" { $$ = 0; }
+| '!' "groupsclamp" { $$ = 0; }
+| "groupsclamp" { $$ = 1; }
+| "groupsclamp" '=' BOOLEAN { $$ = $3; }
+;
+
+/* Boolean for noaccel. */
+noaccel:
+ '~' "accel" { $$ = 0; }
+| '!' "accel" { $$ = 0; }
+| "accel" { $$ = 1; }
+| "accel" '=' BOOLEAN { $$ = $3; }
+;
+
+
+sameserver:
+ '~' "sameserver" { $$ = 0; }
+| '!' "sameserver" { $$ = 0; }
+| "sameserver" { $$ = 1; }
+| "sameserver" '=' BOOLEAN { $$ = $3; }
+;
+
+setmodsparams:
+ /* empty */
+| setmodsparam
+| setmodsparams ',' setmodsparam
+;
+
+
+/* Parameter for the (Set|Lock|Latch)Mods action. */
+setmodsparam:
+ "mods" '=' mods
+ {
+ ((action_setmods_t *) current_action)->modmap = $3;
+ }
+| "mods" '=' "usemodmap"
+ { ((action_setmods_t *) current_action)->flags |= useModMap;
+ }
+| clearlocks
+ {
+ ((action_setmods_t *) current_action)->flags &= ~clearLocks;
+ ((action_setmods_t *) current_action)->flags |= $1;
+ }
+| usemodmap
+ {
+ ((action_setmods_t *) current_action)->flags &= ~useModMap;
+ ((action_setmods_t *) current_action)->flags |= $1;
+ }
+| latchtolock
+ {
+ ((action_setmods_t *) current_action)->flags &= ~latchToLock;
+ ((action_setmods_t *) current_action)->flags |= $1;
+ }
+;
+
+setgroupparams:
+/* empty */
+| setgroupparam
+| setgroupparams ',' setgroupparam
+;
+
+/* Parameter for the (Set|Lock|Latch)Group action. */
+setgroupparam:
+ "group" '=' NUM
+ {
+ ((action_setgroup_t *) current_action)->group = $3;
+ ((action_setgroup_t *) current_action)->flags |= groupAbsolute;
+ }
+| "group" '=' '+' NUM
+ {
+ ((action_setgroup_t *) current_action)->group = $4;
+ }
+| "group" '=' '-' NUM
+ {
+ ((action_setgroup_t *) current_action)->group = -$4;
+ }
+| clearlocks
+ {
+ ((action_setgroup_t *) current_action)->flags |= $1;
+ }
+| latchtolock
+ {
+ ((action_setgroup_t *) current_action)->flags |= $1;
+ }
+;
+
+moveptrparams:
+/* empty */
+| moveptrparam
+| moveptrparams ',' moveptrparam
+;
+
+/* Parameters for the MovePtr action. */
+moveptrparam:
+ IDENTIFIER '=' NUM
+ {
+ ((action_moveptr_t *) current_action)->x = $3;
+ ((action_setgroup_t *) current_action)->flags |= MoveAbsoluteX;
+ }
+| IDENTIFIER '=' '+' NUM
+ {
+ ((action_moveptr_t *) current_action)->x = $4;
+ }
+| IDENTIFIER '=' '-' NUM
+ {
+ ((action_moveptr_t *) current_action)->x = -$4;
+ }
+| noaccel
+ {
+ ((action_moveptr_t *) current_action)->flags |= NoAcceleration;
+ }
+;
+
+/* A mouse button. */
+button:
+ NUM { $$ = $1; }
+| BUTTONNUM { $$ = $1; }
+| "default" { $$ = 0; }
+;
+
+affectbtnlock:
+ "lock"
+| "unlock"
+| "both"
+| "neither"
+;
+
+ptrbtnparams:
+ /* empty */
+| ptrbtnparam
+| ptrbtnparams ',' ptrbtnparam
+;
+
+/* Parameters for the (Set|Lock|Latch)PtrBtn action. */
+ptrbtnparam:
+ "button" '=' button
+ { ((action_ptrbtn_t *) current_action)->button = $3; }
+| "count" '=' NUM
+ { ((action_ptrbtn_t *) current_action)->count = $3; }
+| "affect" '=' affectbtnlock
+ {
+ // ((action_ptrbtn_t *) $$)->a = $3;
+ }
+;
+
+/* XXX: This should be extended. */
+affectbtns:
+ "defaultbutton"
+| "button"
+| "all"
+;
+
+ptrdfltparams:
+/* empty */
+| ptrdfltparam
+| ptrdfltparams ',' ptrdfltparam
+;
+
+/* Parameters for the SetPtrDflt action. */
+ptrdfltparam:
+ "button" '=' button { }
+| "button" '=' '+' button { }
+| "button" '=' '-' button { }
+| "affect" '=' affectbtns { }
+;
+
+/* A list of controlflags. */
+ctrls:
+ ctrls '+' CONTROLFLAG// { $$ |= $3; }
+| CONTROLFLAG// { $$ = $1; }
+| OVERLAY
+;
+
+/* Modified controlflags. */
+ctrlflags:
+ ctrls { /*$$ = $1;*/ }
+| "all" { $$ = 0xFFFF; }
+| "none" { $$ = 0; }
+;
+
+/* The parameters of a (Set|Lock|Latch)Ctrls Action. */
+ctrlparams:
+ "controls" '=' ctrlflags
+ { /* ((action_setcontrols_t *) $$)->controls = $3; */ }
+;
+
+isoaffect:
+ "mods"
+| "groups"
+| "controls"
+| "pointers"
+| "all"
+| "none"
+;
+
+isolockparams:
+/* empty */
+| isolockparam
+| isolockparams ',' isolockparam
+;
+
+/* Parameters for the ISOLock action. */
+isolockparam:
+ "mods" '=' mods
+| "mods" '=' USEMODMAP
+| "group" '=' group
+| "controls" '=' ctrlflags
+| "affect" '=' isoaffect
+;
+
+switchscrnparams:
+ switchscrnparam
+ | switchscrnparams ',' switchscrnparam
+;
+
+/* Parameters for the SwitchScreen action. */
+switchscrnparam:
+ "screen" '=' NUM
+ {
+ ((action_switchscrn_t *) current_action)->screen = $3;
+ ((action_switchscrn_t *) current_action)->flags |= screenAbs;
+ }
+| "screen" '+' '=' NUM
+ {
+ ((action_switchscrn_t *) current_action)->screen = $4;
+ }
+| "screen" '-' '=' NUM
+ {
+ ((action_switchscrn_t *) current_action)->screen = -$4;
+ }
+| sameserver
+ {
+ /* XXX: Implement this. */
+/* ((action_switchscrn_t *) current_action)->flags &= ~0; */
+/* ((action_switchscrn_t *) current_action)->flags |= $1; */
+ }
+;
+
+consscrollparams:
+ consscrollparam
+ | consscrollparams ',' consscrollparam
+;
+
+/* Parameters for the ConsScroll action. */
+consscrollparam:
+ "screen" '+' '=' FLOAT
+ {
+ ((action_consscroll_t *) current_action)->screen = $4;
+ }
+| "screen" '-' '=' FLOAT
+ {
+ ((action_consscroll_t *) current_action)->screen = -$4;
+ }
+| "line" '=' NUM
+ {
+ ((action_consscroll_t *) current_action)->line = $3;
+ ((action_consscroll_t *) current_action)->flags |= lineAbs;
+ }
+| "line" '+' '=' NUM
+ {
+ ((action_consscroll_t *) current_action)->line = $4;
+ }
+| "line" '-' '=' NUM
+ {
+ ((action_consscroll_t *) current_action)->line = -$4;
+ }
+| "percent" '=' NUM
+ {
+ ((action_consscroll_t *) current_action)->percent = $3;
+ ((action_consscroll_t *) current_action)->flags |= usePercentage;
+ }
+;
+
+privateparams:
+ /* empty */
+ | privateparam
+ | privateparams ',' privateparam
+;
+
+privateparam:
+ "type" '=' HEX
+ {
+ }
+| "data" '=' STR
+ {
+ }
+;
+
+/* An action definition. */
+action:
+ "setmods"
+ {
+ if (set_default_action (&default_setmods, &current_action))
+ YYABORT;
+ }
+ '(' setmodsparams ')' { $$ = current_action; }
+| "latchmods"
+ {
+ if (set_default_action (&default_latchmods, &current_action))
+ YYABORT;
+ }
+ '(' setmodsparams ')' { $$ = current_action; }
+| "lockmods"
+ {
+ if (set_default_action (&default_lockmods, &current_action))
+ YYABORT;
+ }
+ '(' setmodsparams ')' { $$ = current_action; }
+| "setgroup"
+ {
+ if (set_default_action (&default_setgroup, &current_action))
+ YYABORT;
+ }
+ '(' setgroupparams ')' { $$ = current_action; }
+| "latchgroup"
+ {
+ if (set_default_action (&default_latchgroup, &current_action))
+ YYABORT;
+ }
+ '(' setgroupparams ')' { $$ = current_action; }
+| "lockgroup"
+ {
+ if (set_default_action (&default_lockgroup, &current_action))
+ YYABORT;
+ }
+ '(' setgroupparams ')' { $$ = current_action; }
+| "moveptr"
+ {
+ if (set_default_action (&default_moveptr, &current_action))
+ YYABORT;
+ }
+ '(' moveptrparams ')' { $$ = current_action; }
+| "ptrbtn"
+ {
+ if (set_default_action (&default_ptrbtn, &current_action))
+ YYABORT;
+ }
+ '(' ptrbtnparams ')' { $$ = current_action; }
+| "lockptrbtn"
+ {
+ if (set_default_action (&default_lockptrbtn, &current_action))
+ YYABORT;
+ }
+ '(' ptrbtnparams ')' { $$ = current_action; }
+| "setptrdflt"
+ {
+ if (set_default_action (&default_ptrdflt, &current_action))
+ YYABORT;
+ }
+ '(' ptrdfltparams ')' { $$ = current_action; }
+| "setcontrols"
+ {
+ if (set_default_action (&default_setcontrols, &current_action))
+ YYABORT;
+ }
+ '(' ctrlparams ')' { $$ = current_action; }
+| "lockcontrols"
+ {
+ if (set_default_action (&default_lockcontrols, &current_action))
+ YYABORT;
+ }
+ '(' ctrlparams ')' { $$ = current_action; }
+| "terminate" '(' ')'
+ { $$ = calloc (1, sizeof (xkb_action_t)); $$->type = SA_TerminateServer; }
+| "switchscreen"
+ {
+ if (set_default_action (&default_switchscrn, &current_action))
+ YYABORT;
+ }
+'(' switchscrnparams ')' { $$ = current_action; }
+| "consscroll"
+ {
+ if (set_default_action (&default_consscroll, &current_action))
+ YYABORT;
+ }
+ '(' consscrollparams ')' { $$ = current_action; }
+| "isolock"
+ {
+ if (set_default_action (&default_isolock, &current_action))
+ YYABORT;
+ }
+ '(' isolockparams ')' { $$ = current_action; }
+| "private" '(' privateparams ')'
+ { $$ = calloc (1, sizeof (xkb_action_t)); $$->type = SA_NoAction; }
+| "noaction" '(' ')'
+ { $$ = calloc (1, sizeof (xkb_action_t)); $$->type = SA_NoAction; }
+| error ')' { yyerror ("Invalid action\n"); }
+;
+
+/* Define values for default actions. */
+actiondef:
+ "setmods" '.' { current_action = &default_setmods; } setmodsparam ';'
+| "latchmods" '.' { current_action = &default_latchmods; } setmodsparam ';'
+| "lockmods" '.' { current_action = &default_lockmods; } setmodsparam ';'
+| "setgroup" '.' { current_action = &default_setgroup; } setgroupparam ';'
+| "latchgroup" '.' { current_action = &default_latchgroup; } setgroupparam ';'
+| "lockgroup" '.' { current_action = &default_lockgroup; } setgroupparam ';'
+| "moveptr" '.' { current_action = &default_moveptr; } moveptrparam ';'
+| "ptrbtn" '.' { current_action = &default_ptrbtn; } ptrbtnparam ';'
+| "lockptrbtn" '.' { current_action = &default_lockptrbtn; } ptrbtnparam ';'
+| "setptrdflt" '.' { current_action = &default_ptrdflt; } ptrdfltparam ';'
+| "setcontrols" '.' { current_action = &default_setcontrols; } ctrlparams ';'
+| "lockcontrols" '.' { current_action = &default_lockcontrols; } ctrlparams ';'
+| "isolock" '.' { current_action = &default_isolock; } isolockparam ';'
+| "switchscreen" '.' { current_action = &default_switchscrn; } switchscrnparam ';'
+;
+
+/* The header of a symbols section. */
+symbols:
+ flags "xkb_symbols" '{' symbolssect '}' ';'
+| flags "xkb_symbols" STR '{' symbolssect '}' ';'
+;
+
+/* A group. */
+group:
+ GROUPNUM { $$ = $1 - 1; }
+| NUM { $$ = $1 - 1; }
+| HEX { $$ = $1 - 1; }
+;
+
+/* A list of keysyms and keycodes bound to a realmodifier. */
+key_list:
+ key_list ',' KEYCODE { set_rmod_keycode ($3, current_rmod); }
+| key_list ',' symbolname { ksrm_add ($3, current_rmod); }
+| KEYCODE { set_rmod_keycode ($1, current_rmod); }
+| symbolname { ksrm_add ($1, current_rmod); }
+;
+
+/* Process the includes on the stack. */
+symbolinclude:
+ '{' symbolssect '}' { close_include (); }
+| symbolinclude '{' symbolssect '}' { close_include (); }
+;
+
+/* A XKB symbol section. It is used to bind keysymbols, actions and
+ realmodifiers to keycodes. */
+symbolssect:
+ /* Empty */
+| symbolssect vmods_def
+| symbolssect NAME '[' group ']' '=' STR ';'
+| symbolssect "key" KEYCODE
+ {
+ key_new ($3);
+ current_group = 0;
+ } '{' keydescs '}' ';'
+| symbolssect "replace" "key" KEYCODE
+ {
+ key_delete ($4);
+ key_new ($4);
+ current_group = 0;
+ } '{' keydescs '}' ';'
+| symbolssect "override" "key" KEYCODE
+ {
+ key_delete ($4);
+ key_new ($4);
+ current_group = 0;
+ } '{' keydescs '}' ';'
+| symbolssect "modifier_map" rmod { current_rmod = $3; } '{' key_list '}' ';'
+| symbolssect include STR
+ { include_sections ($3, XKBSYMBOLS, "symbols", $2); }
+ symbolinclude
+| symbolssect actiondef
+| symbolssect "key" '.' {debug_printf ("working on default key.\n"); current_key = default_key; } keydesc ';'
+| symbolssect error ';' { yyerror ("Error in symbol section\n"); }
+;
+
+/* Returns a keysymbols, the numberic representation. */
+symbolname:
+ IDENTIFIER { $$ = XStringToKeysym ($1); }
+| NUM
+ {
+ if (($1 >= 0) && ($1 < 10))
+ $$ = $1 + '0';
+ else
+ $$ = $1;
+ }
+| HEX { $$ = $1; }
+;
+
+/* None or more keysyms, assigned to a single group of the current
+ key. */
+groupsyms:
+ /* empty */
+| groupsyms ',' symbolname
+ { key_set_keysym (current_key, current_group, symbolcnt++, $3); }
+| { symbolcnt = 0; } symbolname
+ {
+ symbolcnt = 0;
+ key_set_keysym (current_key, current_group, symbolcnt++, $2);
+ }
+;
+
+/* A list of actions. */
+actions:
+ actions ',' action
+ { key_set_action (current_key, current_group, actioncnt++, $3); }
+| { actioncnt = 0; } action
+ { key_set_action ( current_key, current_group, actioncnt++, $2); }
+;
+
+keydescs:
+ keydesc
+| keydescs ',' keydesc
+;
+
+/* A single key and everything assigned to it. */
+keydesc:
+ "type" '[' group ']' '=' STR
+ {
+ current_key->groups[$3].keytype = keytype_find ($6);
+ }
+| "type" '[' error ']' { yyerror ("Invalid group.\n"); }
+| "type" '=' STR
+ { current_key->groups[current_group].keytype = keytype_find ($3); }
+| { symbolcnt = 0; } "symbols" '[' group ']' { current_group = $4; } '=' '[' groupsyms ']'
+ {
+ current_key->numgroups = ($4 + 1) > current_key->numgroups ?
+ ($4 + 1) : current_key->numgroups;
+ }
+| {actioncnt = 0; } "actions" '[' group ']' { current_group = $4; } '=' '[' actions ']'
+ {
+ current_key->numgroups = ($4 + 1) > current_key->numgroups ?
+ ($4 + 1) : current_key->numgroups;
+ }
+| "virtualmods" '=' mods
+ { current_key->mods.vmods = $3.vmods; }
+| '[' groupsyms ']'
+ {
+ current_group++;
+ current_key->numgroups = current_group > current_key->numgroups ?
+ current_group : current_key->numgroups;
+ }
+| '[' actions ']'
+ {
+ current_group++;
+ current_key->numgroups = current_group > current_key->numgroups ?
+ current_group : current_key->numgroups;
+ }
+| locking {}/* This is not implemented - YET. */
+/* XXX: There 3 features are described in Ivan Pascals docs about XKB,
+ but aren't used in the standard keymaps and cannot be used because it
+ cannot be stored in the XKM dataformat. */
+| groupswrap {}
+| groupsclamp {}
+| "groupsredirect" '=' NUM
+| "overlay" '=' KEYCODE /* If you _REALLY_ need overlays, mail me!!!! */
+| repeat
+ {
+ current_key->flags &= ~(KEYREPEAT | KEYNOREPEAT);
+ current_key->flags |= $1;
+ }
+;
+
+/* The geometry map is ignored. */
+
+/* The header of a geometry section. */
+geometry:
+ flags "xkb_geometry" '{' { skipsection (); } '}' ';'
+| flags "xkb_geometry" STR '{' { skipsection (); } '}' ';'
+;
+
+%%
+/* Skip all tokens until a section of the type SECTIONSYMBOL with the
+ name SECTIONNAME is found. */
+static int
+skip_to_sectionname (char *sectionname, int sectionsymbol)
+{
+ int symbol;
+
+ do
+ {
+ do
+ {
+ symbol = yylex ();
+ } while ((symbol != YY_NULL) && (symbol != sectionsymbol));
+
+ if (symbol != YY_NULL)
+ symbol = yylex ();
+
+ if (symbol == YY_NULL) {
+ return 1;
+ } else if (symbol != STR)
+ continue;
+
+ } while (strcmp (yylval.str, sectionname));
+ return 0;
+}
+
+/* Skip all tokens until the first section of the type SECTIONSYMBOL
+ is found. */
+static int
+skip_to_firstsection (int sectionsymbol)
+{
+ int symbol;
+
+ do
+ {
+ do
+ {
+ symbol = yylex ();
+ } while ((symbol != YY_NULL) && (symbol != sectionsymbol));
+
+ if (symbol != YY_NULL)
+ symbol = yylex ();
+
+ if (symbol == YY_NULL)
+ return 1;
+ } while (symbol != STR);
+ return 0;
+}
+
+/* Skip all tokens until the default section is found. */
+static int
+skip_to_defaultsection (void)
+{
+ int symbol;
+
+ /* Search the default section. */
+ do
+ {
+ if ((symbol = yylex ()) == YY_NULL)
+ return 1;
+ } while (symbol != DEFAULT);
+
+ do
+ {
+ if ((symbol = yylex ()) == YY_NULL)
+ return 1;
+ } while (symbol != '{');
+ scanner_unput ('{');
+ return 0;
+}
+
+/* Include a single file. INCL is the filename. SECTIONSYMBOL is the
+ token that marks the beginning of the section. DIRNAME is the name
+ of the directory from where the includefiles must be loaded. NEW_MM
+ is the mergemode that should be used. */
+static error_t
+include_section (char *incl, int sectionsymbol, char *dirname,
+ mergemode new_mm)
+{
+ void include_file (FILE *, mergemode, char *);
+ int scanner_get_current_location ();
+ const char* scanner_get_current_file ();
+
+ char *filename;
+ char *sectionname = NULL;
+ FILE *includefile;
+
+ int current_location = scanner_get_current_location ();
+ char* current_file = strdup (scanner_get_current_file ());
+
+ sectionname = strchr (incl, '(');
+ if (sectionname)
+ {
+ int snlen;
+
+ snlen = strlen (sectionname);
+ if (sectionname[snlen-1] != ')')
+ {
+ free(current_file);
+ return 0;
+ }
+ sectionname[snlen-1] = '\0';
+ sectionname[0] = '\0';
+ sectionname++;
+
+ if (asprintf (&filename, "%s/%s", dirname, incl) < 0)
+ {
+ free (current_file);
+ return ENOMEM;
+ }
+ }
+ else
+ {
+ if (asprintf (&filename, "%s/%s", dirname, incl) < 0)
+ {
+ free (current_file);
+ return ENOMEM;
+ }
+ }
+
+ includefile = fopen (filename, "r");
+
+ if (includefile == NULL)
+ {
+ fprintf (stderr, "Couldn't open include file \"%s\"\n", filename);
+ free (current_file);
+ free (filename);
+ exit (EXIT_FAILURE);
+ }
+
+ include_file (includefile, new_mm, strdup (filename));
+ debug_printf ("skipping to section %s\n", (sectionname ? sectionname : "default"));
+ /* If there is a sectionname not the entire file should be included,
+ the scanner should be positioned at the required section. */
+ int err;
+ if (sectionname)
+ err = skip_to_sectionname (sectionname, sectionsymbol);
+ else
+ if ((err = skip_to_defaultsection ()) != 0)
+ {
+ /* XXX: after skip_to_defaultsection failed the whole file was
+ consumed and it is required to include it here, too. */
+ include_file (includefile, new_mm, strdup (filename));
+ err = skip_to_firstsection (sectionsymbol);
+ }
+ if (err != 0) {
+ char* tmpbuf = malloc (sizeof(char)*1024);
+ if (tmpbuf) {
+ snprintf (tmpbuf, 1023, "cannot find section %s in file %s included from %s:%d.\n"
+ , (sectionname ? sectionname : "DEFAULT")
+ , filename, current_file, current_location);
+ yyerror (tmpbuf);
+ free (tmpbuf);
+ }
+ free (current_file);
+ free (filename);
+ exit (err);
+ }
+ free (current_file);
+ free (filename);
+ return 0;
+}
+
+/* Include multiple file sections, seperated by '+'. INCL is the
+ include string. SECTIONSYMBOL is the token that marks the beginning
+ of the section. DIRNAME is the name of the directory from where the
+ includefiles must be loaded. NEW_MM is the mergemode that should be
+ used. */
+static error_t
+include_sections (char *incl, int sectionsymbol, char *dirname,
+ mergemode new_mm)
+{
+ char *curstr;
+ char *s;
+
+ if (new_mm == defaultmm)
+ new_mm = merge_mode;
+
+/* printf ("dir: %s - include: %s: %d\n", dirname, incl, new_mm); */
+ /* Cut of all includes, starting with the first. The includes are
+ pushed on the stack in reversed order. */
+ do {
+ curstr = strrchr (incl, '+');
+ if (curstr)
+ {
+ curstr[0] = '\0';
+ curstr++;
+
+ s = strdup (curstr);
+ if (s == NULL)
+ return ENOMEM;
+
+ include_section (s, sectionsymbol, dirname, new_mm);
+ free (s);
+ }
+ } while (curstr);
+
+ s = strdup (incl);
+ if (s == NULL)
+ return ENOMEM;
+
+ include_section (s, sectionsymbol, dirname, new_mm);
+ free (s);
+
+ return 0;
+}
+
+/* Skip all tokens until the end of the section is reached. */
+static void
+skipsection (void)
+{
+ /* Pathesensis counter. */
+ int p = 0;
+ while (p >= 0)
+ {
+ int symbol = yylex ();
+ if (symbol == '{')
+ p++;
+ if (symbol == '}')
+ p--;
+ }
+ scanner_unput ('}');
+}
+
+/* Initialize the default action with the default DEF. */
+static error_t
+set_default_action (struct xkb_action *def,
+ struct xkb_action **newact)
+{
+ struct xkb_action *newaction;
+ newaction = malloc (sizeof (struct xkb_action));
+ if (newaction == NULL)
+ return ENOMEM;
+ memcpy (newaction, def, sizeof (struct xkb_action));
+
+ *newact = newaction;
+
+ return 0;
+}
+
+/* Remove all keysyms bound to the group GROUP or the key KEY. */
+static void
+remove_symbols (struct key *key, group_t group)
+{
+ // printf ("rem: group: %d\n", group);
+ if (key->groups[group].symbols)
+ {
+ free (key->groups[group].symbols);
+ key->groups[group].symbols = NULL;
+ key->groups[group].width = 0;
+ }
+}
+
+/* Set the keysym KS for key KEY on group GROUP and level LEVEL. */
+static void
+key_set_keysym (struct key *key, group_t group, int level, symbol ks)
+{
+ symbol *keysyms = key->groups[group].symbols;
+
+ if ((level + 1) > key->groups[group].width)
+ {
+ keysyms = realloc (keysyms, (level + 1)*sizeof(symbol));
+
+ if (!keysyms)
+ {
+ fprintf (stderr, "No mem\n");
+ exit (EXIT_FAILURE);
+ }
+
+ key->groups[group].symbols = keysyms;
+ key->groups[group].width++;
+ }
+ else
+ /* For NoSymbol leave the old symbol intact. */
+ if (!ks) {
+ debug_printf ("symbol %d was not added to key.\n", ks);
+ return;
+ }
+
+ debug_printf ("symbol '%c'(%d) added to key for group %d and level %d.\n", ks, ks, group, level);
+ keysyms[level++] = ks;
+}
+
+/* Set the action ACTION for key KEY on group GROUP and level LEVEL. */
+void
+key_set_action (struct key *key, group_t group, int level, xkb_action_t *action)
+{
+ xkb_action_t **actions = key->groups[group].actions;
+ size_t width = key->groups[group].actionwidth;
+
+ if ((size_t) (level + 1) > width)
+ {
+ actions = realloc (actions, (level + 1)*sizeof(xkb_action_t *));
+ /* Levels between 'width' and 'level' have no actions defined. */
+ memset (&actions[width], 0, (level - width)*sizeof(xkb_action_t *));
+
+ if (!actions)
+ {
+ fprintf (stderr, "No mem\n");
+ exit (EXIT_FAILURE);
+ }
+
+ key->groups[group].actions = actions;
+ key->groups[group].actionwidth += level - width + 1;
+ }
+
+ actions[level++] = action;
+}
+
+/* Delete keycode to keysym mapping. */
+void
+key_delete (char *keyname)
+{
+ group_t group;
+ keycode_t kc = keyname_find (keyname);
+
+ current_key = &keys[kc];
+ for (group = 0; group < current_key->numgroups; group++)
+ remove_symbols (current_key, group);
+ memset (current_key, 0, sizeof (struct key));
+
+}
+
+/* Create a new keycode to keysym mapping, check if the old one should
+ be removed or preserved. */
+static void
+key_new (char *keyname)
+{
+ group_t group;
+
+ int isempty (char *mem, int size)
+ {
+ int i;
+ for (i = 0; i < size; i++)
+ if (mem[i])
+ return 0;
+ return 1;
+ }
+
+ keycode_t kc = keyname_find (keyname);
+
+ if (merge_mode == augment)
+ {
+ if (!isempty ((char *) &keys[kc], sizeof (struct key)))
+ {
+ current_key = &dummy_key;
+ debug_printf ("working on dummy key due to merge mode.\n");
+ return;
+ }
+ else
+ current_key = &keys[kc];
+ }
+
+ if (merge_mode == override)
+ current_key = &keys[kc];
+
+ if (merge_mode == replace)
+ {
+ key_delete (keyname);
+ current_key = &keys[kc];
+ }
+
+ debug_printf ("working on key %s(%d)", keyname, kc);
+
+ if (current_key->numgroups == 0 || merge_mode == replace)
+ {
+ debug_printf (" cloned default key");
+ /* Clone the default key. */
+ memcpy (current_key, default_key, sizeof (struct key));
+ for (group = 0; group < 3; group++)
+ {
+ current_key->groups[group].symbols = NULL;
+ current_key->groups[group].actions = NULL;
+ current_key->groups[group].actionwidth = 0;
+ current_key->groups[group].width = 0;
+ }
+ }
+ debug_printf ("\n");
+}
+
+/* Load the XKB configuration from the section XKBKEYMAP in the
+ keymapfile XKBKEYMAPFILE. Use XKBDIR as root directory for relative
+ pathnames. */
+error_t
+parse_xkbconfig (char *xkbdir, char *xkbkeymapfile, char *xkbkeymap)
+{
+ error_t err;
+ char *cwd = getcwd (NULL, 0);
+ extern FILE *yyin;
+ extern char *filename;
+
+ debug_printf ("Dir: %s, file: %s sect: %s\n", xkbdir, xkbkeymapfile, xkbkeymap);
+
+ if (xkbkeymapfile)
+ {
+ filename = xkbkeymapfile;
+
+ if (chdir (xkbdir) == -1)
+ {
+ fprintf (stderr, "Could not set \"%s\" as the active directory\n",
+ xkbdir);
+ free (cwd);
+ return errno;
+ }
+
+ yyin = fopen (xkbkeymapfile, "r");
+ if (yyin == NULL)
+ {
+ fprintf (stderr, "Couldn't open keymap file\n");
+ free (cwd);
+ return errno;
+ }
+
+ if (xkbkeymap)
+ skip_to_sectionname (xkbkeymap, XKBKEYMAP);
+ else
+ skip_to_defaultsection();
+ }
+ else
+ {
+ free (cwd);
+ return EINVAL;
+ }
+ err = yyparse ();
+ fclose (yyin);
+
+ if (err || yynerrs > 0)
+ {
+ free (cwd);
+ return EINVAL;
+ }
+
+ if (xkbkeymapfile)
+ {
+ if (chdir (cwd) == -1)
+ {
+ fprintf (stderr,
+ "Could not set \"%s\" as the active directory\n", cwd);
+ free (cwd);
+ return errno;
+ }
+ }
+
+ /* Apply keysym to realmodifier mappings. */
+ ksrm_apply ();
+
+ free (cwd);
+ return 0;
+}
diff --git a/console-client/xkb/xkb-data/keymap/hurd b/console-client/xkb/xkb-data/keymap/hurd
new file mode 100644
index 00000000..b123f2b4
--- /dev/null
+++ b/console-client/xkb/xkb-data/keymap/hurd
@@ -0,0 +1,660 @@
+// $XFree86: xc/programs/xkbcomp/keymap/xfree86,v 3.30 2003/04/03 16:34:49 dawes Exp $
+
+
+xkb_keymap "Hurd" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+us+hurd+group(win_switch)+compose(menu)" };
+ xkb_geometry { include "pc" };
+};
+
+default xkb_keymap "us" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+us+hurd" };
+ xkb_geometry { include "pc" };
+};
+
+// "ar" addition by Arabeyes Team, <support@arabeyes.org>
+xkb_keymap "ar" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ara+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "be" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+be+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "bg" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+bg+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+// us_intl and br by Ricardo Y. Igarashi (iga@that.com.br)
+xkb_keymap "br" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+br+hurd" };
+ xkb_geometry { include "pc(abnt2)" };
+};
+// cz and sk keymaps by Kamil Toman (ktoman@email.cz)
+// are designed to replace old czechoslovakian and czsk keyboards
+// and their prog variants. Those are now obsolete and should not be used anymore.
+xkb_keymap "cz" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+cz+hurd" };
+ xkb_geometry { include "pc" };
+};
+xkb_keymap "de" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+de+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "ch_de" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ch(de)+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "ch_fr" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ch(fr)+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "dk" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+dk+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "dvorak" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+us(dvorak)+hurd"};
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "en_US" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+us+hurd" };
+ xkb_geometry { include "pc" };
+};
+xkb_keymap "es" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+es+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "fr" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+fr+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "fr-latin9" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+fr-latin9+hurd" };
+ xkb_geometry { include "pc" };
+};
+xkb_keymap "fr_CA" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ca+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "gb" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+gb+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "hr" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+hr+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "it" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+it+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "jp106" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "jp(jp106)+hurd" };
+ xkb_geometry { include "pc(jp106)" };
+};
+xkb_keymap "lt" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+lt+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "lt_std" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+lt(std)+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "lv" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+lv+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "mk" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+mk+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "mt" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+mt+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "neo" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default+caps(caps_lock)+misc(assign_shift_left_action)+level5(level5_lock)" };
+ xkb_symbols { include "pc(pc105)+de(neo)+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "no" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+no+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "pl" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+pl+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "pt" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+pt+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+// ro: additions by Cristian Gafton, <gafton@redhat.com>
+xkb_keymap "ro" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ro(pc101)+hurd" };
+ xkb_geometry { include "pc(pc101)" };
+};
+xkb_keymap "ro_microsoft" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ro(pc105)+hurd" };
+ xkb_geometry { include "pc(pc105)" };
+};
+xkb_keymap "ru" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ru+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "se_FI" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+fi+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "se_SE" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+se+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "sl" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+si+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "sl_SI" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+si+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+// cz and sk keymaps by Kamil Toman (ktoman@email.cz)
+// are designed to replace old czechoslovakian and czsk keyboards
+// and their prog variants. Those are now obsolete and should not be used anymore.
+xkb_keymap "sk" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+sk+hurd" };
+ xkb_geometry { include "pc" };
+};
+// Additions by Emil Soleyman-Zomalan, <emil@nishra.com>
+xkb_keymap "syr" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+syr+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "th" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+th+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "th_tis" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+th(tis)+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "th_pat" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+th(pat)+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "tr" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+tr+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "uk" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+uk)+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "us_flexpro" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "us(pc105)+hurd" };
+ xkb_geometry { include "keytronic(FlexPro)" };
+};
+// us_intl and br by Ricardo Y. Igarashi (iga@that.com.br)
+// us_intl means standard us keyboard plus dead_keys symbols
+// these keyboards are very popular in Brazil
+xkb_keymap "us_intl" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "us(pc101)+us(intl)+hurd" };
+ xkb_geometry { include "pc" };
+};
+xkb_keymap "us_microsoft" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "us(pc105)+hurd" };
+ xkb_geometry { include "microsoft" };
+};
+
+xkb_keymap "uz" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+uz+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+
+# svu: compatibility keymaps, based on variants
+xkb_keymap "cz_qwerty" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+cz(qwerty)+hurd" };
+ xkb_geometry { include "pc" };
+};
+xkb_keymap "de_CH" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ch(de)+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "fr_CH" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ch(fr)+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "mt_us" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+mt(us)+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "sk_qwerty" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+sk(qwerty)+hurd" };
+ xkb_geometry { include "pc" };
+};
+
+/* Additional keymaps used by the Debian installer */
+xkb_keymap "al" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+al+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "ara" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ara+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "bd" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+bd+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "by" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+by+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "in" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+in+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "ba" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ba+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "ca" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ca+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "cn" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+cn+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "nl" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+nl+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "bt" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+bt+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "epo" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+epo+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "ee" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ee+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "et" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+et+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "fi" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+fi+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "ge" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ge+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "gr" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+gr+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "il" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+il+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "hu" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+hu+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "is" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+is+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "ie" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ie+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "jp" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+jp+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "kz" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+kz+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "kh" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+kh+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "kg" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+kg+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "kr" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+kr+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "la" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+la+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "latam" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+latam+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "np" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+np+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "ir" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ir+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "rs" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+rs+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "pk" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+pk+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "lk" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+lk+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "si" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+si+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "se" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+se+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "ch" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ch+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "tr" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+tr+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "ua" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+ua+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
+xkb_keymap "vn" {
+ xkb_keycodes { include "xfree86" };
+ xkb_types { include "default+hurd" };
+ xkb_compatibility { include "default" };
+ xkb_symbols { include "pc(pc105)+vn+hurd" };
+ xkb_geometry { include "pc(pc102)" };
+};
diff --git a/console-client/xkb/xkb-data/symbols/hurd b/console-client/xkb/xkb-data/symbols/hurd
new file mode 100644
index 00000000..39edf2ae
--- /dev/null
+++ b/console-client/xkb/xkb-data/symbols/hurd
@@ -0,0 +1,125 @@
+// -*- Mode: C -*-
+default
+xkb_symbols "hurd" {
+ /* Switch to local consoles by default. */
+ // SwitchScreen.SameServer;
+
+ /* Make F1 - F10 switch virtual consoles when Alt is held down. */
+ key <FK01>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction () , SwitchScreen (screen = 1),
+ NoAction () ]
+ };
+ key <FK02>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction () , SwitchScreen (screen = 2),
+ NoAction () ]
+ };
+ key <FK03>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction () , SwitchScreen (screen = 3),
+ NoAction () ]
+ };
+ key <FK04>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction () , SwitchScreen (screen = 4),
+ NoAction () ]
+ };
+ key <FK05>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction () , SwitchScreen (screen = 5),
+ NoAction () ]
+ };
+ key <FK06>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction () , SwitchScreen (screen = 6),
+ NoAction () ]
+ };
+ key <FK07>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction () , SwitchScreen (screen = 7),
+ NoAction () ]
+ };
+ key <FK08>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction () , SwitchScreen (screen = 8),
+ NoAction () ]
+ };
+ key <FK09>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction () , SwitchScreen (screen = 9),
+ NoAction () ]
+ };
+ key <FK10>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction () , SwitchScreen (screen = 10),
+ NoAction () ]
+ };
+
+ // Make the left and right cursor keys switch virtual consoles when
+ // Alt is held down.
+ key <LEFT>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction (), SwitchScreen (screen -= 1),
+ NoAction () ]
+ };
+ key <RGHT>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction (), SwitchScreen (screen += 1),
+ NoAction () ]
+ };
+
+ // Scroll the console up or down (one line).
+ key <UP>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction (), ConsScroll (line -= 1) ]
+ };
+ key <DOWN>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction (), ConsScroll (line += 1) ]
+ };
+
+ // Scroll the console up or down (1/2 screen).
+ key <PGUP>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction (), NoAction (),
+ ConsScroll (screen -= 0.5) ]
+ };
+ key <PGDN>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction (), NoAction (),
+ ConsScroll (screen += 0.5) ]
+ };
+
+ // Scroll the console to 0%, 25%, 75% or 100%.
+ key <HOME>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction (),
+ ConsScroll (percentage = 0),
+ ConsScroll (percentage = 25) ]
+ };
+ key <END>
+ {
+ type[Group1] = "HURD",
+ actions[Group1] = [ NoAction (),
+ ConsScroll (percentage = 100),
+ ConsScroll (percentage = 75) ]
+ };
+};
diff --git a/console-client/xkb/xkb-data/types/hurd b/console-client/xkb/xkb-data/types/hurd
new file mode 100644
index 00000000..11ad5647
--- /dev/null
+++ b/console-client/xkb/xkb-data/types/hurd
@@ -0,0 +1,17 @@
+// -*- Mode: C -*-
+
+default xkb_types "hurd" {
+ virtual_modifiers Alt;
+
+ type "HURD"
+ {
+ modifiers = Shift + Alt + Control;
+ map[Alt] = Level2;
+ map[Shift] = Level3;
+ map[Control] = Level4;
+ level_name[Level1] = "Base";
+ level_name[Level2] = "Hurd console";
+ level_name[Level3] = "Hurd console2";
+ level_name[Level4] = "Hurd console3";
+ };
+};
diff --git a/console-client/xkb/xkb.c b/console-client/xkb/xkb.c
new file mode 100644
index 00000000..0039714b
--- /dev/null
+++ b/console-client/xkb/xkb.c
@@ -0,0 +1,1381 @@
+/* xkb.c -- Main XKB routines.
+
+ Copyright (C) 2002, 2003, 2004 Marco Gerards
+
+ Written by Marco Gerards <metgerards@student.han.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <iconv.h>
+#include <locale.h>
+#include <error.h>
+#include <device/device.h>
+#include <mach/mach_port.h>
+
+#include "xkb.h"
+#include <hurd/console.h>
+#define XK_XKB_KEYS
+#define XK_MISCELLANY
+#include <X11/keysymdef.h>
+#include <driver.h>
+#include <mach-inputdev.h>
+#include <wctype.h>
+
+
+
+/* The converter. */
+extern iconv_t cd;
+
+/* All interpretations for compatibility. (Translation from keysymbol
+ to actions). */
+xkb_interpret_t *interpretations;
+
+/* All keysymbols and how they are handled by XKB. */
+struct key *keys = NULL;
+int min_keys;
+int max_keys;
+
+/* The current set of modifiers. */
+static modmap_t bmods;
+/* Temporary set of modifiers. This is a copy of mods, so mods won't
+ be consumed (Effective modifiers). */
+static modmap_t emods;
+
+/* Effective group. */
+static group_t egroup;
+/* Base group. */
+static group_t bgroup;
+/* Locked group. */
+static group_t lgroup;
+/* Latched group. */
+static group_t latchedgroup;
+
+static boolctrls bboolctrls;
+
+/* A counter to count how often the modifier was set. This is used
+ when two seperate actions set the same modifier. (example: Left
+ Shift and Right Shift.). */
+modcount_t modsc;
+
+keystate_t keystate[255];
+
+/* The locked modifiers. Lock simply works an an invertion. */
+static modmap_t lmods = {0, 0};
+
+/* When the modifier is used the modifier will be consumed. */
+static modmap_t latchedmods = {0, 0};
+
+/* Not setting GroupsWrap uses modulus to keep the value into the
+ range. */
+static int GroupsWrap = 0;
+
+/* MouseKeys, default: off. */
+static int MouseKeys = 0;
+/* Default mousebutton. */
+static int default_button = 0;
+
+static xkb_indicator_t *indicators;
+static int indicator_count;
+static int indicator_map = 0;
+
+/* unused
+static int stickykeys_active = 1;
+*/
+
+int
+debug_printf (const char *f, ...)
+{
+ va_list ap;
+ int ret = 0;
+
+ va_start (ap, f);
+#ifdef XKB_DEBUG
+ ret = vfprintf (stderr, f, ap);
+#endif
+ va_end (ap);
+
+ return ret;
+}
+
+
+static void
+interpret_kc (keycode_t kc)
+{
+ int cursym;
+ int rmods = keys[kc].mods.rmods;
+ struct xkb_interpret *interp;
+
+ for (interp = interpretations; interp; interp = interp->next)
+ {
+ group_t group;
+
+ for (group = 0; group < keys[kc].numgroups; group++)
+ {
+ int width = keys[kc].groups[group].width;
+
+ for (cursym = 0; cursym < width; cursym++)
+ {
+ int symbol = keys[kc].groups[group].symbols[cursym];
+
+ /* Check if a keysymbol requirement exists or if it
+ matches. */
+ if (interp->symbol == 0 ||
+ (symbol && (interp->symbol == symbol)))
+ {
+ int flags = interp->match & 0x7f;
+
+ /* XXX: use enum. */
+ if ((flags == 0 && (!(interp->rmods & rmods))) ||
+ (flags == 1) ||
+ (flags == 2 && (interp->rmods & rmods)) ||
+ (flags == 3 && ((interp->rmods & rmods) ==
+ interp->rmods)) ||
+ (flags == 4 && interp->rmods == rmods))
+ {
+ xkb_action_t *action;
+
+ if (keys[kc].groups[group].actionwidth > cursym &&
+ keys[kc].groups[group].actions[cursym] &&
+ keys[kc].groups[group].actions[cursym]->type !=
+ SA_NoAction)
+ continue;
+
+ action = malloc (sizeof (xkb_action_t));
+ memcpy (action, &interp->action, sizeof (xkb_action_t));
+
+ key_set_action (&keys[kc], group, cursym, action);
+
+ keys[kc].flags = interp->flags | KEYHASACTION;
+ if (!keys[kc].mods.vmods)
+ keys[kc].mods.vmods = interp->vmod;
+ }
+ }
+ }
+ }
+ }
+
+}
+
+
+/* Test if c is an uppercase letter. */
+static int islatin_upper (int c)
+{
+ return (c >= 'A' && c <= 'Z');
+}
+
+/* Test if c is an lowercase letter. */
+static int islatin_lower (int c)
+{
+ return (c >= 'a' && c <= 'z');
+}
+
+/* A key is of the keytype KEYPAD when one of the symbols that can be produced
+ by this key is in the KEYPAD symbol range. */
+static int
+iskeypad (int width, int *sym)
+{
+ int i;
+
+ for (i = 0; i < width; i++, sym++)
+ {
+ /* Numlock is in the keypad range but shouldn't be of the type
+ keypad because it will depend on itself in that case. */
+ if (*sym == XK_Num_Lock)
+ return 0;
+ if (*sym >= KEYPAD_FIRST_KEY && *sym <= KEYPAD_LAST_KEY)
+ return 1;
+ }
+ return 0;
+}
+
+/* Get the keytype (the keytype determines which modifiers are used
+ for shifting.
+
+ For reference, see FindAutomaticType@xkbcomp/symbols.c.
+
+ These rules are used:
+
+ * If the width is 1 the keytype is ONE_LEVEL.
+ * If the first symbol is lowercase and the second is uppercase
+ (latin alphabeth) the keytype is ALPHABETHIC.
+ * If one of the symbols is in the keypad range the keytype is KEYPAD.
+ * If width is 4 the type is either FOUR_LEVEL, FOUR_LEVEL_ALPHABETIC,
+ FOUR_LEVEL_SEMI_ALPHABETIC (first sym pair is alphabetic) or
+ FOUR_LEVEL_KEYPAD.
+ * Else the keytype is TWO_LEVEL. */
+static struct keytype *
+get_keytype (int width, symbol *sym)
+{
+ struct keytype *ktfound = NULL;
+
+ if (!sym)
+ ktfound = keytype_find ("TWO_LEVEL");
+ else if ((width == 1) || (width == 0))
+ ktfound = keytype_find ("ONE_LEVEL");
+ else if (width == 2) {
+ if (islatin_lower (sym[0]) && islatin_upper (sym[1]))
+ ktfound = keytype_find ("ALPHABETIC");
+ else if (iskeypad (width, sym))
+ ktfound = keytype_find ("KEYPAD");
+ else
+ ktfound = keytype_find ("TWO_LEVEL");
+ }
+ else if (width <= 4) {
+ if (islatin_lower (sym[0]) && islatin_upper (sym[1]))
+ if (islatin_lower(sym[2]) && islatin_upper(sym[3]))
+ ktfound = keytype_find ("FOUR_LEVEL_ALPHABETIC");
+ else
+ ktfound = keytype_find ("FOUR_LEVEL_SEMIALPHABETIC");
+ else if (iskeypad (2, sym))
+ ktfound = keytype_find ("FOUR_LEVEL_KEYPAD");
+ else
+ ktfound = keytype_find ("FOUR_LEVEL");
+ }
+
+ if (!ktfound)
+ ktfound = keytype_find ("TWO_LEVEL");
+ if (!ktfound)
+ {
+ console_error (L"Default keytypes have not been defined!\n");
+ exit (1);
+ }
+
+ return ktfound;
+}
+
+/* Create XKB style actions for every action described by keysymbols. */
+static void
+interpret_all (void)
+{
+ keycode_t curkc;
+
+ /* Check every key. */
+ for (curkc = 0; curkc < max_keys; curkc++)
+ interpret_kc (curkc);
+}
+
+static void
+determine_keytypes (void)
+{
+ keycode_t curkc;
+
+ /* Check every key. */
+ for (curkc = 0; curkc < max_keys; curkc++)
+ {
+ group_t group;
+ for (group = 0; group < 4; group++)
+ {
+ struct keygroup *kg = &keys[curkc].groups[group];
+
+ if (!kg->keytype)
+ kg->keytype = get_keytype (kg->width, kg->symbols);
+ }
+ }
+}
+
+/* Wrap the group GROUP into a valid group range. The method to use is
+ defined by the GroupsWrap control. */
+static int
+wrapgroup (group_t group, int maxgroup)
+{
+ /* If the group is in an invalid range, fix it. */
+ if (group < 0 || group >= maxgroup)
+ {
+ /* If RedirectIntoRange should be used use the 4 LSbs of
+ the GroupsWrap control instead of the current group. */
+ if (GroupsWrap & RedirectIntoRange)
+ group = GroupsWrap & 0x0F;
+ /* Select the closest valid group. */
+ else if (GroupsWrap & ClampIntoRange)
+ {
+ if (group < 0)
+ group = 0;
+ else
+ group = maxgroup - 1;
+ }
+ else /* Default: use modulus to wrap the group. */
+ group %= maxgroup;
+ }
+
+ return group;
+}
+
+
+
+/* This function must be called after a modifier, group or control has
+ been changed. The indicator map will be regenerated and the hardwre
+ representation of this map will be updated. */
+static void
+set_indicator_mods (void)
+{
+ int i;
+
+ /* Calculate the effective modmap. */
+ emods = bmods;
+ emods.rmods |= lmods.rmods;
+ emods.vmods |= lmods.vmods;
+ emods.rmods |= latchedmods.rmods;
+ emods.vmods |= latchedmods.vmods;
+
+ for (i = 0; i < indicator_count; i++)
+ {
+ if (!(indicators[i].flags & IM_NoAutomatic))
+ {
+ if (indicators[i].which_mods & IM_UseBase)
+ {
+ if (((indicators[i].modmap.rmods & bmods.rmods) ==
+ indicators[i].modmap.rmods) &&
+ ((indicators[i].modmap.vmods & bmods.vmods) ==
+ indicators[i].modmap.vmods))
+ {
+ indicator_map |= (1 << i);
+ continue;
+ }
+ }
+ if (indicators[i].which_mods & IM_UseLatched)
+ {
+ if (((indicators[i].modmap.rmods & latchedmods.rmods) ==
+ indicators[i].modmap.rmods) &&
+ ((indicators[i].modmap.vmods & latchedmods.vmods) ==
+ indicators[i].modmap.vmods))
+ {
+ indicator_map |= (1 << i);
+ continue;
+ }
+ }
+ if (indicators[i].which_mods & IM_UseLocked)
+ {
+ if (((indicators[i].modmap.rmods & lmods.rmods) ==
+ indicators[i].modmap.rmods) &&
+ ((indicators[i].modmap.vmods & lmods.vmods) ==
+ indicators[i].modmap.vmods))
+ {
+ indicator_map |= (1 << i);
+ continue;
+ }
+ }
+ if (indicators[i].which_mods & IM_UseEffective)
+ {
+ if (((indicators[i].modmap.rmods & emods.rmods) ==
+ indicators[i].modmap.rmods) &&
+ ((indicators[i].modmap.vmods & emods.vmods) ==
+ indicators[i].modmap.vmods))
+ {
+ indicator_map |= (1 << i);
+ continue;
+ }
+ }
+ /* The indicator shouldn't be on anymore so turn it off. */
+ indicator_map &= ~(1 << i);
+ }
+ }
+ debug_printf ("INDICATOR: %d\n", indicator_map);
+}
+
+/* Set base modifiers. A counter exists for every modifier. When a
+ modifier is set this counter will be incremented with one. */
+static void
+setmods (modmap_t smods, keypress_t key)
+{
+ /* Higher the modcount for every set modifier. */
+ void set_xmods (int xmods, int modsc[])
+ {
+ int i = 0;
+
+ while (xmods)
+ {
+ if (xmods & 1)
+ modsc[i]++;
+
+ xmods >>= 1;
+ i++;
+ }
+ }
+
+ bmods.rmods |= smods.rmods;
+ bmods.vmods |= smods.vmods;
+
+ set_xmods (smods.rmods, modsc.rmods);
+ set_xmods (smods.vmods, modsc.vmods);
+
+ set_indicator_mods ();
+}
+
+/* Clear base modifiers. A counter exists for every modifier. When a
+ key release wants to clear a modifier this counter will be
+ decreased with one. When the counter becomes 0 the modifier will be
+ cleared and unlocked if the clearLocks flag is set. */
+static void
+clearmods (modmap_t cmods, keypress_t key, int flags)
+{
+#define CLEAR_XMOD(MODTYPE) \
+ { \
+ int i = 0; \
+ int mmods = cmods.MODTYPE; \
+ \
+ while (mmods) \
+ { \
+ if (mmods & 1) \
+ if (!(--modsc.MODTYPE[i])) \
+ { \
+ bmods.MODTYPE &= ~(1 << i); \
+ if (flags & clearLocks) \
+ lmods.MODTYPE &= ~(1 << i); \
+ } \
+ mmods >>= 1; \
+ i++; \
+ } \
+ }
+
+ CLEAR_XMOD(rmods);
+ CLEAR_XMOD(vmods);
+
+ set_indicator_mods ();
+}
+
+/* Set modifiers in smods and also lock them if the flag noLock is
+ not set. */
+static void
+setlocks (modmap_t smods, keypress_t key, int flags)
+{
+ if (!key.rel)
+ {
+ modmap_t cmods;
+ cmods.rmods = lmods.rmods & smods.rmods;
+ cmods.vmods = lmods.vmods & smods.vmods;
+
+ /* Locking also sets the base modifiers. */
+ setmods (smods, key);
+
+ if (!(flags & noUnlock))
+ {
+ lmods.rmods &= ~cmods.rmods;
+ lmods.vmods &= ~cmods.vmods;
+ }
+
+ if (!(flags & noLock))
+ {
+ lmods.rmods |= (~cmods.rmods & smods.rmods);
+ lmods.vmods |= (~cmods.vmods & smods.vmods);
+ }
+ }
+ else
+ clearmods (smods, key, flags);
+}
+
+/* Latch the modifiers smods for key KEY. When another key is operated while
+ KEY is pressed the release of KEY will just clear the base
+ modifiers, otherwise latch the modifiers like is described in the
+ protocol specification. */
+static void
+latchmods (modmap_t smods, keypress_t key, int flags)
+{
+ if (!key.rel)
+ setmods (smods, key);
+ else
+ {
+ modmap_t oldlmods;
+ oldlmods = lmods;
+ clearmods (smods, key, flags);
+ /* Modifier that have been unlocked don't have effect anymore. */
+ smods.rmods &= ~(lmods.rmods & oldlmods.rmods);
+ smods.vmods &= ~(lmods.vmods & oldlmods.vmods);
+
+
+ /* If another key has been pressed while this modifier key was
+ pressed don't latch, just behave as SetMods. */
+ if (key.keycode == key.prevkc)
+ {
+ if (flags & latchToLock)
+ {
+ /* When a modifier exists as a locked modifier, consume
+ and unlock. */
+ lmods.rmods |= latchedmods.rmods & smods.rmods;
+ lmods.vmods |= latchedmods.vmods & smods.vmods;
+
+ smods.rmods &= ~(latchedmods.rmods & smods.rmods);
+ smods.vmods &= ~(latchedmods.vmods & smods.vmods);
+ }
+
+ /* Use the remaining modifiers for latching. */
+ latchedmods.rmods |= smods.rmods;
+ latchedmods.vmods |= smods.vmods;
+ }
+ }
+}
+
+static void
+setgroup (keypress_t key, group_t group, int flags)
+{
+ debug_printf ("Setgroup ()\n");
+ if (!key.rel)
+ {
+ if (flags & groupAbsolute)
+ {
+ bgroup = group;
+ keystate[key.keycode].oldgroup = bgroup;
+ }
+ else
+ bgroup += group;
+ }
+ else
+ {
+ if ((key.keycode == key.prevkc) && (flags & clearLocks))
+ lgroup = 0;
+ if (flags & groupAbsolute)
+ bgroup = keystate[key.keycode].oldgroup;
+ else
+ /* XXX: Maybe oldgroup should be restored for !groupAbsolute
+ too, because wrapgroup might have affected the calculation
+ and substracting will not undo the set operation. However
+ this way of handeling relative groups is better because the
+ order of releasing keys when multiple relative setgroup keys
+ are pressed doesn't matter. */
+ bgroup -= group;
+ }
+}
+
+static void
+latchgroup (keypress_t key, group_t sgroup, int flags)
+{
+ group_t oldlgroup = sgroup;
+ setgroup (key, sgroup, flags);
+ debug_printf ("Latchgroup ()\n");
+
+ if (key.keycode == key.prevkc && oldlgroup == lgroup)
+ {
+ if ((flags & latchToLock) && latchedgroup)
+ {
+ lgroup += sgroup;
+ latchedgroup -= sgroup;
+ }
+ else
+ latchedgroup += sgroup;
+ }
+}
+
+static void
+lockgroup (keypress_t key, group_t group, int flags)
+{
+ debug_printf (">L: %d, g: %d\n", lgroup, group);
+
+ keystate[key.keycode].oldgroup = lgroup;
+ if (flags & groupAbsolute)
+ lgroup = group;
+ else
+ lgroup += group;
+
+ lgroup = wrapgroup (lgroup, 4);
+
+ debug_printf ("<L: %d, g: %d\n", lgroup, group);
+
+}
+
+static void
+setcontrols (keypress_t key, boolctrls ctrls, int flags)
+{
+ keystate[key.keycode].bool = ctrls & ~bboolctrls;
+ bboolctrls |= ctrls;
+}
+
+static void
+clearcontrols (keypress_t key, boolctrls ctrls, int flags)
+{
+ bboolctrls &= ~keystate[key.keycode].bool;
+}
+
+static void
+lockcontrols (keypress_t key, boolctrls ctrls, int flags)
+{
+ /* XXX this needs a closer look. */
+ if (!key.rel)
+ {
+ //setcontrols (key, boolctrls, flags);
+ //if (!(flags & noLock))
+ // lboolctrls |= boolctrls;
+ }
+ else
+ {
+ // clearcontrols (key, boolctrls, flags);
+ /* This unlock behaviour doesnt work and sucks, just like the protocol
+ specification where it was documented. */
+ // if (!(flags & noUnlock))
+ // lboolctrls &= ~keystate[key.keycode].bool;
+ }
+
+}
+
+/* Not properly implemented, not very high priority for me. */
+/* static void */
+/* isolock (keypress_t key, group group, modmap_t mods, int flags) */
+/* { */
+/* if (!key.rel) */
+/* { */
+/* struct isolock *newiso; */
+
+/* if (flags & dfltIsGroup) */
+/* setgroup (key, group, flags & groupAbsolute); */
+/* else */
+/* setmods (key, mods, 0); */
+
+/* newiso = malloc (sizeof struct isolock); */
+/* if (!newiso) */
+/* ;// return error */
+/* active_isolocks.anchor */
+/* } */
+/* else */
+/* { */
+/* if (flags & dfltIsGroup) */
+/* { */
+/* cleargroup (key, group, flags & groupAbsolute); */
+/* if (bla) */
+/* lockgroup (key, group, flags & groupAbsolute); */
+/* } */
+/* else */
+/* { */
+/* clearmods (key, mods, 0); */
+/* if (bla) */
+/* { */
+/* lmods.rmods |= mods.rmods; */
+/* lmods.vmods |= mods.vmods; */
+/* } */
+/* } */
+/* } */
+/* } */
+
+/* Move the mousepointer relational to its current position. */
+static void
+mouse_x_move (int xdelta)
+{
+ /* XXX: Ofcouse this function has to do *something* :). */
+}
+
+/* Change the horizontal position of the mousepointer. */
+static void
+mouse_x_move_to (int x)
+{
+ /* XXX: Ofcouse this function has to do *something* :). */
+}
+
+/* Move the mousepointer relational to its current position. */
+static void
+mouse_y_move (int ydelta)
+{
+ /* XXX: Ofcouse this function has to do *something* :). */
+}
+
+/* Change the vertical position of the mousepointer. */
+static void
+mouse_y_move_to (int y)
+{
+ /* XXX: Ofcouse this function has to do *something* :). */
+}
+
+/* Simulate a mouse button press for button BUTTON. */
+static void
+mouse_button_press (int button)
+{
+ /* XXX: Ofcouse this function has to do *something* :). */
+}
+
+/* Simulate a mouse button press for button BUTTON. */
+static void
+mouse_button_release (int button)
+{
+ /* XXX: Ofcouse this function has to do *something* :). */
+}
+
+
+
+/* Forward declaration for redirected keys. */
+static symbol handle_key (keypress_t);
+
+
+/* Execute an action bound to a key. When the action isn't supported
+ or when the action doesn't consume the key return true, otherwise
+ return false. */
+static int
+action_exec (xkb_action_t *action, keypress_t key)
+{
+ if (!action)
+ return KEYNOTCONSUMED;
+
+ debug_printf ("EXEC: %d\n", action->type);
+
+ switch (action->type)
+ {
+ /* LockMods: Lock/Unlock modifiers when the key is pressed. */
+ case SA_LockMods:
+ {
+ action_setmods_t *setmodmap = (action_setmods_t *) action;
+ modmap_t modm = setmodmap->modmap;
+
+ /* UseModMap */
+ if (setmodmap->flags & useModMap)
+ {
+ modm.rmods |= keys[key.keycode].mods.rmods;
+ modm.vmods |= keys[key.keycode].mods.vmods;
+ }
+
+ setlocks (modm, key, setmodmap->flags);
+ }
+ break;
+ /* SetMods: Set/Unset modifiers. Those modifier will be set as
+ long the key is pressed. Keys like shift, alt and control are
+ used here often. */
+ case SA_SetMods:
+ {
+ action_setmods_t *setmodmap = (action_setmods_t *) action;
+ modmap_t modm = setmodmap->modmap;
+
+ /* UseModMapMods means: also use the real modifiers specified
+ in the key's modmap. */
+ if (setmodmap->flags & useModMap)
+ {
+ debug_printf ("Apply modmaps\n");
+ modm.rmods |= keys[key.keycode].mods.rmods;
+ modm.vmods |= keys[key.keycode].mods.vmods;
+ }
+
+ /* When the key is pressed set the modifiers. */
+ if (!key.rel)
+ setmods (modm, key);
+ else /* When the key is released clear the modifiers. */
+ clearmods (modm, key, setmodmap->flags);
+
+ break;
+ }
+ /* Set the basegroup. When groupAbsolute isn't used add it
+ to the basegroup. */
+ case SA_LatchMods:
+ {
+ action_setmods_t *setmodmap = (action_setmods_t *) action;
+
+ modmap_t modm = setmodmap->modmap;
+
+ /* UseModMapMods means: also use the real modifiers specified
+ in the key's modmap. */
+ if (setmodmap->flags & useModMap)
+ {
+ modm.rmods |= keys[key.keycode].mods.rmods;
+ modm.vmods |= keys[key.keycode].mods.vmods;
+ }
+
+ latchmods (modm, key, setmodmap->flags);
+
+ break;
+ }
+ case SA_SetGroup:
+ {
+ action_setgroup_t *setgroupac = (action_setgroup_t *) action;
+
+ setgroup (key, setgroupac->group, setgroupac->flags);
+ break;
+ }
+ case SA_LockGroup:
+ {
+ action_setgroup_t *setgroupac = (action_setgroup_t *) action;
+
+ if (!key.rel)
+ lockgroup (key, setgroupac->group, setgroupac->flags);
+ break;
+ }
+ case SA_LatchGroup:
+ {
+ action_setgroup_t *setgroupac = (action_setgroup_t *) action;
+
+ latchgroup (key, setgroupac->group, setgroupac->flags);
+ break;
+ }
+
+ case SA_PtrBtn:
+ {
+ action_ptrbtn_t *ptrbtnac = (action_ptrbtn_t *) action;
+ int i;
+ int button;
+
+ if (!MouseKeys)
+ return KEYNOTCONSUMED;
+
+ if (ptrbtnac->flags & useDfltBtn)
+ button = default_button;
+ else
+ button = ptrbtnac->button;
+
+ if (ptrbtnac->count)
+ for (i = 0; i < ptrbtnac->count; i++)
+ {
+ /* XXX: Should there be a delay? */
+ mouse_button_press (button);
+ mouse_button_release (button);
+ }
+ else if (!key.rel)
+ mouse_button_press (button);
+ else
+ mouse_button_release (button);
+ break;
+ }
+ case SA_LockPtrBtn:
+ {
+ action_ptrbtn_t *ptrbtnac = (action_ptrbtn_t *) action;
+
+ int button;
+
+ if (!MouseKeys)
+ return KEYNOTCONSUMED;
+
+ if (ptrbtnac->flags & useDfltBtn)
+ button = default_button;
+ else
+ button = ptrbtnac->button;
+
+ /* XXX: Do stuff. */
+
+ break;
+ }
+ case SA_SetPtrDflt:
+ {
+ action_ptr_dflt_t *ptrdfltac = (action_ptr_dflt_t *) action;
+
+ if (!MouseKeys)
+ return KEYNOTCONSUMED;
+
+ if (!key.rel)
+ {
+ if (ptrdfltac->flags & DfltBtnAbsolute)
+ default_button = ptrdfltac->value;
+ else
+ default_button += ptrdfltac->value;
+ }
+
+ if (default_button < 0)
+ default_button = 0;
+
+ if (default_button > 5)
+ default_button = 5;
+
+ break;
+ }
+ case SA_TerminateServer:
+ /* Zap! */
+ console_exit ();
+ break;
+ case SA_SwitchScreen:
+ {
+ action_switchscrn_t *switchscrnac = (action_switchscrn_t *) action;
+
+ if (key.rel)
+ break;
+
+ if (switchscrnac->flags & screenAbs)
+ /* Switch to screen. */
+ console_switch ((char) switchscrnac->screen, 0);
+ else
+ /* Move to next/prev. screen. */
+ console_switch (0, (char) switchscrnac->screen);
+ break;
+ }
+ case SA_RedirectKey:
+ {
+ action_redirkey_t *redirkeyac = (action_redirkey_t *) action;
+
+ key.keycode = redirkeyac->newkey & (key.rel ? 0x80:0);
+
+ /* For the redirected key other modifiers should be used. */
+ emods.rmods = bmods.rmods | lmods.rmods | latchedmods.rmods;
+ emods.vmods = bmods.vmods | lmods.vmods | latchedmods.vmods;
+
+ emods.rmods &= ~redirkeyac->rmodsmask;
+ emods.rmods |= redirkeyac->rmods;
+ emods.vmods &= ~redirkeyac->vmods;
+ emods.vmods |= redirkeyac->vmodsmask;
+
+ /* XXX: calc group etc. */
+
+ handle_key (key);
+ break;
+ }
+ case SA_ConsScroll:
+ {
+ action_consscroll_t *scrollac = (action_consscroll_t *) action;
+
+ if (key.rel)
+ break;
+
+ if (scrollac->flags & usePercentage)
+ console_scrollback (CONS_SCROLL_ABSOLUTE_PERCENTAGE,
+ 100 - scrollac->percent);
+
+ if (scrollac->screen)
+ console_scrollback (CONS_SCROLL_DELTA_SCREENS, -scrollac->screen);
+
+ if (scrollac->line)
+ {
+ int type = (scrollac->flags & lineAbs) ?
+ CONS_SCROLL_ABSOLUTE_LINE : CONS_SCROLL_DELTA_LINES;
+ console_scrollback (type, -scrollac->line);
+ }
+ break;
+ }
+ case SA_ActionMessage:
+ case SA_DeviceBtn:
+ case SA_LockDeviceBtn:
+ case SA_DeviceValuator:
+ return KEYNOTCONSUMED;
+ case SA_MovePtr:
+ {
+ action_moveptr_t *moveptrac = (action_moveptr_t *) action;
+
+ if (!MouseKeys)
+ return KEYNOTCONSUMED;
+
+ if (moveptrac->flags & MoveAbsoluteX)
+ mouse_x_move_to (moveptrac->x);
+ else
+ mouse_x_move (moveptrac->x);
+
+ if (moveptrac->flags & MoveAbsoluteY)
+ mouse_y_move_to (moveptrac->y);
+ else
+ mouse_y_move (moveptrac->y);
+ break;
+ }
+ case SA_SetControls:
+ {
+ action_setcontrols_t *controlsac = (action_setcontrols_t *) action;
+ if (key.rel)
+ clearcontrols (key, controlsac->controls, 0);
+ else
+ setcontrols (key, controlsac->controls, 0);
+ break;
+ }
+ case SA_LockControls:
+ {
+ action_setcontrols_t *controlsac = (action_setcontrols_t *) action;
+ lockcontrols (key, controlsac->controls, 0);
+ break;
+ }
+ default:
+ /* Preserve the keycode. */
+ return KEYNOTCONSUMED;
+ break;
+ }
+
+ /* Don't preserve the keycode because it was consumed. */
+ return KEYCONSUMED;
+}
+
+
+
+/* Calculate the shift level for a specific key. */
+static int
+calc_shift (keycode_t key)
+{
+ /* The keytype for this key. */
+ struct keytype *keytype = keys[key].groups[egroup].keytype;
+ struct typemap *map;
+
+ /* XXX: Shouldn't happen, another way to fix this? */
+ if (!keytype)
+ return 0;
+
+ /* Scan though all modifier to level maps of this keytype to search
+ the level. */
+ for (map = keytype->maps; map; map = map->next)
+ /* Does this map meet our requirements? */
+ if (map->mods.rmods == (emods.rmods & keytype->modmask.rmods) &&
+ map->mods.vmods == (emods.vmods & keytype->modmask.vmods))
+ {
+ /* Preserve all modifiers specified in preserve for this map. */
+ emods.rmods &= ~(map->mods.rmods & (~map->preserve.rmods));
+ emods.vmods &= ~(map->mods.vmods & (~map->preserve.vmods));
+ return map->level;
+ }
+
+ /* When no map is found use the default shift level and consume all
+ modifiers. */
+ emods.vmods &= ~keytype->modmask.vmods;
+ emods.rmods &= ~keytype->modmask.rmods;
+
+ return 0;
+}
+
+static symbol
+symtoctrlsym (symbol c)
+{
+ c = toupper (c);
+
+ switch (c)
+ {
+ case 'A' ... 'Z':
+ c = c - 'A' + 1;
+ break;
+ case '[': case '3':
+ c = '\e';
+ break;
+ case '\\': case '4':
+ c = '';
+ break;
+ case ']': case '5':
+ c = '';
+ break;
+ case '^': case '6':
+ c = '';
+ break;
+ case '/':
+ c = '/';
+ break;
+ case ' ':
+ c = '\0';
+ break;
+ case '_': case '7':
+ c= '';
+ break;
+ case '8':
+ c = '\x7f';
+ break;
+ }
+
+ return c;
+}
+
+
+/* Handle all actions, etc. bound to the key KEYCODE and return a XKB
+ symbol if one is generated by this key. If redirected_key contains
+ 1 this is keypress generated by the action SA_RedirectKey, don't
+ change the effective modifiers because they exist and have been
+ changed by SA_RedirectKey. */
+static symbol
+handle_key (keypress_t key)
+{
+ int actioncompl = 0;
+
+ modmap_t oldmods;
+ group_t oldgroup = 0;
+
+ /* The level for this key. */
+ int level;
+
+ /* The symbol this keypress generated. */
+ symbol sym = 0;
+
+ debug_printf ("groups\n");
+ /* If the key does not have a group there is nothing to do. */
+ if (keys[key.keycode].numgroups == 0)
+ return -1;
+
+ /* The effective group is the current group, but it can't be
+ out of range. */
+ egroup = wrapgroup (bgroup + lgroup,
+ keys[key.keycode].numgroups);
+
+ if (keys[key.keycode].groups[egroup].actions)
+ {
+ if (key.rel)
+ {
+ debug_printf ("action\n");
+ if (!keystate[key.keycode].prevstate)
+ /* Executing the inverse action of a never executed
+ action... Stop! */
+ return -1;
+
+ keystate[key.keycode].prevstate = 0;
+ emods = keystate[key.keycode].prevmods;
+ egroup = wrapgroup (keystate[key.keycode].prevgroup,
+ keys[key.keycode].numgroups);
+ }
+ else /* This is a keypress event. */
+ {
+ /* Calculate the effective modmap. */
+ emods = bmods;
+ emods.rmods |= lmods.rmods;
+ emods.vmods |= lmods.vmods;
+ emods.rmods |= latchedmods.rmods;
+ emods.vmods |= latchedmods.vmods;
+ }
+
+ oldmods = emods;
+ oldgroup = egroup;
+
+ level = calc_shift (key.keycode);// %
+
+ if (keys[key.keycode].groups[egroup].actionwidth >= level + 1
+ && keys[key.keycode].groups[egroup].actions[level])
+ {
+ actioncompl = action_exec
+ (keys[key.keycode].groups[egroup].actions[level], key);
+ }
+ }
+
+ if (actioncompl == KEYCONSUMED && !key.rel)
+ {
+ /* An action was executed. Store the effective modifier this key
+ so the reverse action can be called on key release. */
+ keystate[key.keycode].prevstate = 1;
+ keystate[key.keycode].prevmods = oldmods;
+ keystate[key.keycode].prevgroup = oldgroup;
+ }
+
+ debug_printf ("consumed: %d - %d -%d\n", actioncompl, key.rel,
+ !keys[key.keycode].groups[egroup].width);
+ /* If the action comsumed the keycode, this is a key release event
+ or if the key doesn't have any symbols bound to it there is no
+ symbol returned. */
+ if (actioncompl == KEYCONSUMED || key.rel ||
+ !keys[key.keycode].groups[egroup].width)
+ return -1;
+
+ /* Calculate the effective modmap. */
+ emods = bmods;
+ emods.rmods |= lmods.rmods;
+ emods.vmods |= lmods.vmods;
+ emods.rmods |= latchedmods.rmods;
+ emods.vmods |= latchedmods.vmods;
+
+ level = calc_shift (key.keycode) % keys[key.keycode].groups[egroup].width;
+
+ /* The latched modifier is used for a symbol, clear it. */
+ latchedmods.rmods = latchedmods.vmods = 0;
+
+ /* Search the symbol for this key in the keytable. Make sure the
+ group and shift level exists. */
+ sym = keys[key.keycode].groups[egroup].symbols[level];
+
+ /* Convert keypad symbols to symbols. XXX: Is this the right place
+ to do this? */
+ if ((sym >= XK_KP_Multiply && sym <= XK_KP_Equal) || sym == XK_KP_Enter)
+ sym &= ~0xFF80;
+
+ /* Check if this keypress was a part of a compose sequence. */
+ sym = compose_symbols (sym);
+
+ return sym;
+}
+
+void
+xkb_input (keypress_t key)
+{
+ char buf[100];
+ size_t size = 0;
+ wchar_t input;
+
+ debug_printf ("input: %d, rel: %d, rep: %d\n", key.keycode, key.rel, key.repeat);
+
+ if (key.rel)
+ keystate[key.keycode].lmods = lmods;
+ input = handle_key (key);
+
+ debug_printf ("handle: %d\n", input);
+ if (input == -1)
+ return;
+
+ /* If the realmodifier MOD1 (AKA Alt) is set generate an ESC
+ symbol. */
+ if (emods.rmods & RMOD_MOD1)
+ buf[size++] = '\e';
+
+ buf[size] = '\0';
+
+ if (!input)
+ return;
+
+ /* Special key, generate escape sequence. */
+ char *escseq = NULL;
+
+ switch (input)
+ {
+ case XK_Up: case XK_KP_Up:
+ escseq = CONS_KEY_UP;
+ break;
+ case XK_Down: case XK_KP_Down:
+ escseq = CONS_KEY_DOWN;
+ break;
+ case XK_Left: case XK_KP_Left:
+ escseq = CONS_KEY_LEFT;
+ break;
+ case XK_Right: case XK_KP_Right:
+ escseq = CONS_KEY_RIGHT;
+ break;
+ case XK_BackSpace:
+ escseq = CONS_KEY_BACKSPACE;
+ break;
+ case XK_F1: case XK_KP_F1:
+ escseq = CONS_KEY_F1;
+ break;
+ case XK_F2: case XK_KP_F2:
+ escseq = CONS_KEY_F2;
+ break;
+ case XK_F3: case XK_KP_F3:
+ escseq = CONS_KEY_F3;
+ break;
+ case XK_F4: case XK_KP_F4:
+ escseq = CONS_KEY_F4;
+ break;
+ case XK_F5:
+ escseq = CONS_KEY_F5;
+ break;
+ case XK_F6:
+ escseq = CONS_KEY_F6;
+ break;
+ case XK_F7:
+ escseq = CONS_KEY_F7;
+ break;
+ case XK_F8:
+ escseq = CONS_KEY_F8;
+ break;
+ case XK_F9:
+ escseq = CONS_KEY_F9;
+ break;
+ case XK_F10:
+ escseq = CONS_KEY_F10;
+ break;
+ case XK_F11:
+ escseq = CONS_KEY_F11;
+ break;
+ case XK_F12:
+ escseq = CONS_KEY_F12;
+ break;
+ case XK_F13:
+ escseq = CONS_KEY_F13;
+ break;
+ case XK_F14:
+ escseq = CONS_KEY_F14;
+ break;
+ case XK_F15:
+ escseq = CONS_KEY_F15;
+ break;
+ case XK_F16:
+ escseq = CONS_KEY_F16;
+ break;
+ case XK_F17:
+ escseq = CONS_KEY_F17;
+ break;
+ case XK_F18:
+ escseq = CONS_KEY_F18;
+ break;
+ case XK_F19:
+ escseq = CONS_KEY_F19;
+ break;
+ case XK_F20:
+ escseq = CONS_KEY_F20;
+ break;
+ case XK_Home: case XK_KP_Home:
+ escseq = CONS_KEY_HOME;
+ break;
+ case XK_Insert: case XK_KP_Insert:
+ escseq = CONS_KEY_IC;
+ break;
+ case XK_Delete: case XK_KP_Delete:
+ escseq = CONS_KEY_DC;
+ break;
+ case XK_End: case XK_KP_End:
+ escseq = CONS_KEY_END;
+ break;
+ case XK_Page_Up: case XK_KP_Page_Up:
+ escseq = CONS_KEY_PPAGE;
+ break;
+ case XK_Page_Down: case XK_KP_Page_Down:
+ escseq = CONS_KEY_NPAGE;
+ break;
+ case XK_KP_Begin:
+ escseq = CONS_KEY_B2;
+ break;
+ case XK_ISO_Left_Tab:
+ escseq = CONS_KEY_BTAB;
+ break;
+ case XK_Return: case XK_KP_Enter:
+ escseq = "\x0d";
+ break;
+ case XK_Tab: case XK_KP_Tab:
+ escseq = "\t";
+ break;
+ case XK_Escape:
+ escseq = "\e";
+ break;
+ }
+
+ if (escseq != NULL)
+ {
+ strcat (buf + size, escseq);
+ size += strlen (escseq);
+ }
+ else
+ {
+ char *buffer = &buf[size];
+ size_t left = sizeof (buf) - size;
+ char *inbuf = (char *) &input;
+ size_t inbufsize = sizeof (wchar_t);
+ size_t nr;
+
+ /* Control key behaviour. */
+ if (bmods.rmods & RMOD_CTRL)
+ input = symtoctrlsym (input);
+
+ /* Convert the Keysym to a UCS4 characted. */
+ input = KeySymToUcs4 (input);
+ /* if (!input) */
+ /* continue; */
+
+ debug_printf ("UCS4: %d -- %c\n", (int) input, input);
+
+ /* If CAPSLOCK is active capitalize the symbol. */
+ if (emods.rmods & 2)
+ input = towupper (input);
+
+ nr = iconv (cd, &inbuf, &inbufsize, &buffer, &left);
+ if (nr == (size_t) -1)
+ {
+ if (errno == E2BIG)
+ console_error (L"Input buffer overflow");
+ else if (errno == EILSEQ)
+ console_error
+ (L"Input contained invalid byte sequence");
+ else if (errno == EINVAL)
+ console_error
+ (L"Input contained incomplete byte sequence");
+ else
+ console_error
+ (L"Input caused unexpected error");
+ }
+ size = sizeof (buf) - left;
+ }
+
+ if (size)
+ console_input (buf, size);
+ size = 0;
+}
+
+error_t parse_xkbconfig (char *xkbdir, char *xkbkeymapfile, char *xkbkeymap);
+
+error_t
+xkb_load_layout (char *xkbdir, char *xkbkeymapfile, char *xkbkeymap)
+{
+ error_t err;
+
+ err = parse_xkbconfig (xkbdir, xkbkeymapfile, xkbkeymap);
+ if (err)
+ return err;
+
+ determine_keytypes ();
+ interpret_all ();
+ return 0;
+}
diff --git a/console-client/xkb/xkb.h b/console-client/xkb/xkb.h
new file mode 100644
index 00000000..07694930
--- /dev/null
+++ b/console-client/xkb/xkb.h
@@ -0,0 +1,431 @@
+/* Keyboard plugin for the Hurd console using XKB keymaps.
+
+ Copyright (C) 2002,03 Marco Gerards
+
+ Written by Marco Gerards <marco@student.han.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details. */
+
+#include <errno.h>
+#include <argp.h>
+#include <X11/Xlib.h>
+
+typedef int keycode_t;
+typedef unsigned int scancode_t;
+typedef int symbol;
+typedef int group_t;
+typedef unsigned int boolctrls;
+
+#define KEYCONSUMED 1
+#define KEYNOTCONSUMED 0
+
+#define RedirectIntoRange 1
+#define ClampIntoRange 2
+#define WrapIntoRange 0
+
+typedef enum mergemode
+ {
+ augment,
+ override,
+ replace,
+ alternate,
+ defaultmm
+ } mergemode;
+
+extern mergemode merge_mode;
+
+typedef unsigned long KeySym;
+
+/* Real modifiers. */
+#define RMOD_SHIFT 1 << 0
+#define RMOD_LOCK 1 << 1
+#define RMOD_CTRL 1 << 2
+#define RMOD_MOD1 1 << 3
+#define RMOD_MOD2 1 << 4
+#define RMOD_MOD3 1 << 5
+#define RMOD_MOD4 1 << 6
+#define RMOD_MOD5 1 << 7
+
+/* If set the key has action(s). */
+#define KEYHASACTION (1<<4)
+/* Normally the keytype will be calculated, but some keys like SYSRQ
+ can't be calculated. For these keys the name for the keytypes will
+ be given for every group fro whch the bit is set. */
+#define KEYHASTYPES 0xf
+#define KEYHASBEHAVIOUR (1<<5)
+/* Will the key be repeated when held down, or not. */
+#define KEYREPEAT (1<<6)
+#define KEYNOREPEAT (1<<7)
+
+/* The complete set of modifiers. */
+typedef struct modmap
+{
+ /* Real modifiers. */
+ int rmods;
+ /* Virtual modifiers. */
+ int vmods;
+} modmap_t;
+
+/* Modifier counter. */
+typedef struct modcount
+{
+ int rmods[8];
+ int vmods[16];
+} modcount_t;
+
+/* Map modifiers to a shift level. */
+typedef struct typemap
+{
+ /* Shift level used when required modifiers match the active modifiers. */
+ int level;
+ modmap_t mods;
+ modmap_t preserve;
+ struct typemap *next;
+} typemap_t;
+
+/* The keypad symbol range. */
+#define KEYPAD_FIRST_KEY 0xFF80
+#define KEYPAD_LAST_KEY 0xFFB9
+#define KEYPAD_MASK 0xFF80
+
+/* Convert a keypad symbol to a ASCII symbol. */
+#define keypad_to_ascii(c) c = (c & (~KEYPAD_MASK))
+
+/* The default keytypes. These can be calculated. */
+#define KT_ONE_LEVEL 0
+#define KT_TWO_LEVEL 1
+#define KT_ALPHA 2
+#define KT_KEYPAD 3
+
+typedef struct keytype
+{
+ /* Mask that determines which modifiers should be checked. */
+ modmap_t modmask;
+ /* Amount of levels. */
+ int levels;
+ /* The required set of modifiers for one specific level. */
+ struct typemap *maps;
+
+ char *name;
+ struct keytype *hnext;
+ struct keytype **prevp;
+} keytype_t;
+
+extern struct keytype *keytypes;
+extern int keytype_count;
+
+/* All Actions as described in the protocol specification. */
+typedef enum actiontype
+ {
+ SA_NoAction,
+ SA_SetMods,
+ SA_LatchMods,
+ SA_LockMods,
+ SA_SetGroup,
+ SA_LatchGroup,
+ SA_LockGroup,
+ SA_MovePtr,
+ SA_PtrBtn,
+ SA_LockPtrBtn,
+ SA_SetPtrDflt,
+ SA_ISOLock,
+ SA_TerminateServer,
+ SA_SwitchScreen,
+ SA_SetControls,
+ SA_LockControls,
+ SA_ActionMessage,
+ SA_RedirectKey,
+ SA_DeviceBtn,
+ SA_LockDeviceBtn,
+ SA_DeviceValuator,
+ SA_ConsScroll
+ } actiontype_t;
+
+typedef struct xkb_action
+{
+ actiontype_t type;
+ int data[15];
+} xkb_action_t;
+
+#define useModMap 4
+#define clearLocks 1
+#define latchToLock 2
+#define noLock 1
+#define noUnlock 2
+#define groupAbsolute 4
+#define NoAcceleration 1
+#define MoveAbsoluteX 2
+#define MoveAbsoluteY 4
+/* XXX: This flag is documentated and I've implemented it. Weird
+ stuff. */
+#define useDfltBtn 0
+#define DfltBtnAbsolute 2
+#define AffectDfltBtn 1
+#define switchApp 1
+#define screenAbs 4
+#define lineAbs 2
+#define usePercentage 8
+
+/* Defines how symbols and rmods are interpreted. This is used to
+ bound an action to a key that doesn't have an action bound to it,
+ only a modifier or action describing symbol. */
+typedef struct xkb_interpret
+{
+ symbol symbol;
+ int rmods;
+ int match;
+ int vmod; /* XXX: Why does this field have a size of only 8 bits? */
+ int flags;
+ struct xkb_action action;
+ struct xkb_interpret *next;
+} xkb_interpret_t;
+
+extern xkb_interpret_t *interpretations;
+extern int interpret_count;
+
+/* These are the parameter names that are used by the actions that
+ control modifiers. (this is stored in the data field of
+ xkb_action)*/
+typedef struct action_setmods
+{
+ actiontype_t type;
+ /* The flags configure the behaviour of the action. */
+ int flags;
+ /* XXX: The real modifiers that can be controlled by this action. */
+ int modmask;
+ /* The modifiers that are will be set/unset by this action. */
+ modmap_t modmap;
+} action_setmods_t;
+
+typedef struct action_setgroup
+{
+ actiontype_t type;
+ int flags;
+ int group;
+} action_setgroup_t;
+
+typedef struct action_moveptr
+{
+ actiontype_t type;
+ int flags;
+ int x;
+ int y;
+} action_moveptr_t;
+
+typedef struct action_ptrbtn
+{
+ actiontype_t type;
+ int flags;
+ int count; /* Isn't used for LockPtrBtn. */
+ int button;
+} action_ptrbtn_t;
+
+typedef struct action_ptr_dflt
+{
+ actiontype_t type;
+ int flags;
+ int affect;
+ int value;
+} action_ptr_dflt_t;
+
+typedef struct action_switchscrn
+{
+ actiontype_t type;
+ int flags;
+ int screen;
+} action_switchscrn_t;
+
+typedef struct action_consscroll
+{
+ actiontype_t type;
+ int flags;
+ double screen;
+ int line;
+ int percent;
+} action_consscroll_t;
+
+typedef struct action_redirkey
+{
+ actiontype_t type;
+ int newkey;
+ int rmodsmask;
+ int rmods;
+ int vmodsmask;
+ int vmods;
+} action_redirkey_t;
+
+typedef struct action_setcontrols
+{
+ actiontype_t type;
+ int controls;
+} action_setcontrols_t;
+
+/* Every key can have 4 groups, this is the information stored per
+ group. */
+struct keygroup
+{
+ /* All symbols for every available shift level and group. */
+ symbol *symbols;
+ /* All actions for every available shift level and group. */
+ struct xkb_action **actions;
+ /* The keytype of this key. The keytype determines the available
+ shift levels and which modiers are used to set the shift level.
+ */
+ struct keytype *keytype;
+ /* Amount of symbols held in this group of this key. */
+ int width;
+ int actionwidth;
+};
+
+/* A single key scancode stored in memory. */
+typedef struct key
+{
+ /* The flags that can be set for this key (To change the behaviour
+ of this key). */
+ int flags;
+ /* Every key has a maximum of 4 groups. (XXX: According to Ivan
+ Pascal's documentation... I'm not really sure if that is true.) */
+ struct keygroup groups[4];
+ int numgroups;
+ struct modmap mods;
+} keyinf_t;
+
+extern struct key *keys;
+extern int min_keys;
+extern int max_keys;
+
+/* The current state of every key. */
+typedef struct keystate
+{
+ /* Key is pressed. */
+ unsigned short keypressed:1;
+ unsigned short prevstate:1;
+ /* The key was disabled for bouncekeys. */
+ unsigned short disabled:1;
+ /* Information about locked modifiers at the time of the keypress,
+ this information is required for unlocking when the key is released. */
+ modmap_t lmods;
+ /* The modifiers and group that were active at keypress, make them
+ active again on keyrelease so the action will be undone. */
+ modmap_t prevmods;
+ boolctrls bool;
+ group_t prevgroup;
+ group_t oldgroup;
+} keystate_t;
+
+extern struct keystate keystate[255];
+
+typedef struct keypress
+{
+ keycode_t keycode;
+ keycode_t prevkc;
+ unsigned short repeat:1; /* It this a real keypress?. */
+ unsigned short redir:1; /* This is not a real keypress. */
+ unsigned short rel; /* Key release. */
+} keypress_t;
+
+/* Flags for indicators. */
+#define IM_NoExplicit 0x80
+#define IM_NoAutomatic 0x40
+#define IM_LEDDrivesKB 0x20
+
+#define IM_UseCompat 0x10
+#define IM_UseEffective 0x08
+#define IM_UseLocked 0x04
+#define IM_UseLatched 0x02
+#define IM_UseBase 0x01
+
+
+typedef struct xkb_indicator
+{
+ int flags;
+ int which_mods;
+ modmap_t modmap;
+ int which_groups;
+ int groups;
+ unsigned int ctrls;
+} xkb_indicator_t;
+
+unsigned int KeySymToUcs4(int keysym);
+symbol compose_symbols (symbol symbol);
+error_t read_composefile (char *);
+struct keytype *keytype_find (char *name);
+
+void key_set_action (struct key *key, group_t group, int level,
+ xkb_action_t *action);
+
+
+/* Interfaces for xkbdata.c: */
+extern struct xkb_interpret default_interpretation;
+
+
+/* Assign the name KEYNAME to the keycode KEYCODE. */
+error_t keyname_add (char *keyname, int keycode);
+
+/* Find the numberic representation of the keycode with the name
+ KEYNAME. */
+int keyname_find (char *keyname);
+
+/* Search the keytype with the name NAME. */
+struct keytype *keytype_find (char *name);
+
+/* Remove the keytype KT. */
+void keytype_delete (struct keytype *kt);
+
+/* Create a new keytype with the name NAME. */
+error_t keytype_new (char *name, struct keytype **new_kt);
+
+/* Add a level (LEVEL) to modifiers (MODS) mapping to the current
+ keytype. */
+error_t keytype_mapadd (struct keytype *kt, modmap_t mods, int level);
+
+/* For the current keytype the modifiers PRESERVE should be preserved
+ when the modifiers MODS are pressed. */
+error_t keytype_preserve_add (struct keytype *kt, modmap_t mods,
+ modmap_t preserve);
+
+/* Add a new interpretation. */
+error_t interpret_new (xkb_interpret_t **new_interpret, symbol ks);
+
+/* Get the number assigned to the virtualmodifier with the name
+ VMODNAME. */
+int vmod_find (char *vmodname);
+
+/* Give the virtualmodifier VMODNAME a number and add it to the
+ hashtable. */
+error_t vmod_add (char *vmodname);
+
+/* Initialize the list for keysyms to realmodifiers mappings. */
+void ksrm_init ();
+
+/* Add keysym to realmodifier mapping. */
+error_t ksrm_add (symbol ks, int rmod);
+
+/* Apply the rkms (realmods to keysyms) table to all keysyms. */
+void ksrm_apply (void);
+
+/* Set the current rmod for the key with keyname KEYNAME. */
+/* XXX: It shouldn't be applied immediatly because the key can be
+ replaced. */
+void set_rmod_keycode (char *keyname, int rmod);
+
+/* Initialize XKB data structures. */
+error_t xkb_data_init (void);
+
+error_t xkb_input_key (int key);
+
+error_t xkb_init_repeat (int delay, int repeat);
+
+void xkb_input (keypress_t key);
+
+int debug_printf (const char *f, ...);
+
+error_t xkb_load_layout (char *xkbdir, char *xkbkeymapfile, char *xkbkeymap);
diff --git a/console-client/xkb/xkbdata.c b/console-client/xkb/xkbdata.c
new file mode 100644
index 00000000..767bf38a
--- /dev/null
+++ b/console-client/xkb/xkbdata.c
@@ -0,0 +1,464 @@
+/* xkbdata.c -- Manage XKB datastructures.
+
+ Copyright (C) 2003 Marco Gerards
+
+ Written by Marco Gerards <marco@student.han.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details. */
+
+/* Generate a key for the string S. XXX: The are many more effecient
+ algoritms, this one should be replaced by one of those. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <hurd/ihash.h>
+#include "xkb.h"
+
+static int
+name_hash (char *s)
+{
+ int i = 0;
+ while (*s)
+ i += *(s++);
+ return i;
+}
+
+
+/* A keyname with a keycode and realmodifier bound to it. */
+struct keyname
+{
+ int keycode;
+ int rmods;
+};
+
+static struct hurd_ihash kn_mapping;
+
+/* Initialize the keyname hashtable. */
+static void
+keyname_init ()
+{
+ hurd_ihash_init (&kn_mapping, HURD_IHASH_NO_LOCP);
+ debug_printf ("Kn_mapping init");
+ /* XXX: error. */
+}
+
+static inline int
+keyname_hash(char *keyname)
+{
+ char tmp[4] = { 0 };
+ strncpy(tmp, keyname, sizeof tmp);
+ return tmp[0] + (tmp[1] << 8) + (tmp[2] << 16) + (tmp[3] << 24);
+}
+
+/* Assign the name KEYNAME to the keycode KEYCODE. */
+error_t
+keyname_add (char *keyname, int keycode)
+{
+ struct keyname *kn;
+ int kn_int;
+
+ kn = malloc (sizeof (struct keyname));
+ if (!kn)
+ return ENOMEM;
+
+ /* XXX: 4 characters can be mapped into a int, it is safe to assume
+ this will not be changed. */
+ if (strlen (keyname) > 4)
+ {
+ debug_printf ("The keyname `%s' consist of more than 4 characters;"
+ " 4 characters is the maximum.\n", keyname);
+ /* XXX: Abort? */
+ return 0;
+ }
+
+ kn->keycode = keycode;
+ kn->rmods = 0;
+
+ kn_int = keyname_hash(keyname);
+ debug_printf ("add key %s(%d) hash: %d\n", keyname, keycode, kn_int);
+ hurd_ihash_add (&kn_mapping, kn_int, kn);
+
+ return 0;
+}
+
+/* Find the numberic representation of the keycode with the name
+ KEYNAME. */
+int
+keyname_find (char *keyname)
+{
+ struct keyname *kn;
+ int kn_int;
+
+ /* XXX: 4 characters can be mapped into a int, it is safe to assume
+ this will not be changed. */
+ if (strlen (keyname) > 4)
+ {
+ debug_printf ("The keyname `%s' consist of more than 4 characters;"
+ " 4 characters is the maximum.\n", keyname);
+ /* XXX: Abort? */
+ return 0;
+ }
+ kn_int = keyname_hash(keyname);
+
+ kn = hurd_ihash_find (&kn_mapping, kn_int);
+ if (kn)
+ return kn->keycode;
+/* int h = name_hash (keyname); */
+/* struct keyname *kn; */
+/* for (kn = knhash[KNHASH(h)]; kn; kn = kn->hnext) */
+/* { */
+/* if (strcmp (kn->keyname, keyname)) */
+/* continue; */
+
+/* return kn->keycode; */
+/* } */
+
+ /* XXX: Is 0 an invalid keycode? */
+ return 0;
+}
+
+
+/* Keytypes and keytype maps. */
+
+/* The dummy gets used when the original may not be overwritten. */
+static struct keytype dummy_keytype;
+
+#define KTHSZ 16
+#if ((KTHSZ&(KTHSZ-1)) == 0)
+#define KTHASH(ktttl) ((ktttl)&(KTHSZ-1))
+#else
+#define KTHASH(ktttl) (((unsigned)(kt))%KTHSZ)
+#endif
+
+/* All keytypes. */
+struct keytype *kthash[KTHSZ];
+
+/* Initialize the keytypes hashtable. */
+static void
+keytype_init ()
+{
+ int n;
+ for (n = 0; n < KTHSZ; n++)
+ kthash[n] = 0;
+}
+
+/* Search the keytype with the name NAME. */
+struct keytype *
+keytype_find (char *name)
+{
+ int nhash = name_hash (name);
+ struct keytype *kt;
+
+ for (kt = kthash[KTHASH(nhash)]; kt; kt = kt->hnext)
+ if (!strcmp (name, kt->name))
+ return kt;
+ return NULL;
+}
+
+/* Remove the keytype KT. */
+void
+keytype_delete (struct keytype *kt)
+{
+ struct typemap *map;
+
+
+ *kt->prevp = kt->hnext;
+ if (kt->hnext)
+ kt->hnext->prevp = kt->prevp;
+
+ map = kt->maps;
+ while (map)
+ {
+ struct typemap *nextmap = map->next;
+ free (map);
+ map = nextmap;
+ }
+
+}
+
+/* Create a new keytype with the name NAME. */
+error_t
+keytype_new (char *name, struct keytype **new_kt)
+{
+ struct keytype *kt;
+ struct keytype *ktlist;
+ int nhash;
+
+ nhash = name_hash (name);
+ debug_printf ("New: %s\n", name);
+
+ kt = keytype_find (name);
+
+ if (kt)
+ {
+ /* If the merge mode is augement don't replace it. */
+ if (merge_mode == augment)
+ {
+ *new_kt = &dummy_keytype;
+ return 0;
+ }
+ else /* This keytype should replace the old one, remove the old one. */
+ keytype_delete (kt);
+ }
+
+ ktlist = kthash[KTHASH(nhash)];
+ kt = calloc (1, sizeof (struct keytype));
+ if (kt == NULL)
+ return ENOMEM;
+
+ kt->hnext = ktlist;
+ kt->name = strdup (name);
+ kt->prevp = &kthash[KTHASH(nhash)];
+ kt->maps = NULL;
+ if (kthash[KTHASH(nhash)])
+ kthash[KTHASH(nhash)]->prevp = &(kt->hnext);
+ kthash[KTHASH(nhash)] = kt;
+
+ *new_kt = kt;
+ return 0;
+}
+
+/* Add a level (LEVEL) to modifiers (MODS) mapping to the current
+ keytype. */
+error_t
+keytype_mapadd (struct keytype *kt, modmap_t mods, int level)
+{
+ struct typemap *map;
+ modmap_t nulmap = {0, 0};
+
+ map = malloc (sizeof (struct typemap));
+ if (!map)
+ return ENOMEM;
+
+ map->level = level;
+ map->mods = mods;
+ map->preserve = nulmap;
+ /* By default modifiers shouldn't be preserved. */
+ map->next = kt->maps;
+
+ kt->maps = map;
+
+ return 0;
+}
+
+/* For the current keytype the modifiers PRESERVE should be preserved
+ when the modifiers MODS are pressed. */
+error_t
+keytype_preserve_add (struct keytype *kt, modmap_t mods, modmap_t preserve)
+{
+ error_t err;
+ struct typemap *map;
+
+ map = kt->maps;
+ while (map)
+ {
+ if (mods.rmods == map->mods.rmods && mods.vmods == map->mods.vmods)
+ {
+ map->preserve = preserve;
+ return 0;
+ }
+ map = map->next;
+ }
+
+ /* No map has been found, add the default map. */
+ err = keytype_mapadd (kt, mods, 0);
+ if (err)
+ return err;
+
+ keytype_preserve_add (kt, mods, preserve);
+
+ return 0;
+}
+
+
+/* Interpretations. */
+
+struct xkb_interpret *last_interp;
+struct xkb_interpret default_interpretation;
+
+
+/* Add a new interpretation. */
+error_t
+interpret_new (xkb_interpret_t **new_interpret, symbol ks)
+{
+ struct xkb_interpret *new_interp;
+
+ new_interp = malloc (sizeof (struct xkb_interpret));
+ if (!new_interp)
+ return ENOMEM;
+
+ memcpy (new_interp, &default_interpretation, sizeof (struct xkb_interpret));
+ new_interp->symbol = ks;
+
+ if (ks)
+ {
+ new_interp->next = interpretations;
+ interpretations = new_interp;
+
+ if (!last_interp)
+ last_interp = new_interp;
+ }
+ else
+ {
+ if (last_interp)
+ last_interp->next = new_interp;
+
+ last_interp = new_interp;
+
+ if (!interpretations)
+ interpretations = new_interp;
+ }
+
+ *new_interpret = new_interp;
+
+ return 0;
+}
+
+
+/* XXX: Dead code!? */
+/* Virtual modifiers name to number mapping. */
+/* Last number assigned to a virtual modifier. */
+static int lastvmod = 0;
+
+/* One virtual modifiername -> vmod number mapping. */
+struct vmodname
+{
+ char *name;
+ struct vmodname *next;
+};
+
+/* A list of virtualmodifier names and its numberic representation. */
+static struct vmodname *vmodnamel;
+
+/* Get the number assigned to the virtualmodifier with the name
+ VMODNAME. */
+int
+vmod_find (char *vmodname)
+{
+ int i = 0;
+ struct vmodname *vmn = vmodnamel;
+
+ while (vmn)
+ {
+ if (!strcmp (vmn->name, vmodname))
+ return (lastvmod - i);
+ vmn = vmn->next;
+ i++;
+ }
+
+ return 0;
+}
+
+/* Give the virtualmodifier VMODNAME a number and add it to the
+ hashtable. */
+error_t
+vmod_add (char *vmodname)
+{
+ struct vmodname *vmn;
+
+ if (vmod_find (vmodname))
+ return 0;
+
+ vmn = malloc (sizeof (struct vmodname));
+ if (vmn == NULL)
+ return ENOMEM;
+
+ vmn->name = vmodname;
+ vmn->next = vmodnamel;
+ vmodnamel = vmn;
+
+ lastvmod++;
+ if (lastvmod > 16)
+ debug_printf("warning: only sixteen virtual modifiers are supported, %s will not be functional.\n", vmodname);
+
+ return 0;
+}
+
+
+/* XXX: Use this, no pointers. */
+struct ksrm
+{
+ symbol ks;
+
+ int rmods;
+};
+static struct hurd_ihash ksrm_mapping;
+
+/* Initialize the list for keysyms to realmodifiers mappings. */
+void
+ksrm_init ()
+{
+ hurd_ihash_init (&ksrm_mapping, HURD_IHASH_NO_LOCP);
+ debug_printf ("KSRM MAP IHASH CREATED \n");
+}
+
+/* Add keysym to realmodifier mapping. */
+error_t
+ksrm_add (symbol ks, int rmod)
+{
+ hurd_ihash_add (&ksrm_mapping, ks, (void *) rmod);
+
+ return 0;
+}
+
+/* Apply the rkms (realmods to keysyms) table to all keysyms. */
+void
+ksrm_apply (void)
+{
+ keycode_t kc;
+ for (kc = 0; kc < max_keys; kc++)
+ {
+ int group;
+ for (group = 0; group < 4; group++)
+ {
+ int cursym;
+ for (cursym = 0; cursym < keys[kc].groups[group].width; cursym++)
+ {
+ symbol ks = keys[kc].groups[group].symbols[cursym];
+ int rmods = (int) hurd_ihash_find (&ksrm_mapping, ks);
+
+ if (rmods)
+ {
+ keys[kc].mods.rmods = rmods;
+ }
+ }
+ }
+ }
+}
+
+
+/* void */
+/* indicator_new (xkb_indicator_t **, */
+
+
+/* Keycode to realmodifier mapping. */
+
+/* Set the current rmod for the key with keyname KEYNAME. */
+/* XXX: It shouldn't be applied immediatly because the key can be
+ replaced. */
+void
+set_rmod_keycode (char *keyname, int rmod)
+{
+ keycode_t kc = keyname_find (keyname);
+ keys[kc].mods.rmods = rmod;
+ debug_printf ("%s (kc %d) rmod: %d\n", keyname, kc, rmod);
+}
+
+/* Initialize XKB data structures. */
+error_t
+xkb_data_init (void)
+{
+ keyname_init ();
+ keytype_init ();
+ ksrm_init ();
+
+ return 0;
+}
diff --git a/console-client/xkb/xkbtimer.c b/console-client/xkb/xkbtimer.c
new file mode 100644
index 00000000..7621af72
--- /dev/null
+++ b/console-client/xkb/xkbtimer.c
@@ -0,0 +1,231 @@
+/* xkbtimer.c -- Manage XKB timers.
+
+ Copyright (C) 2003, 2004 Marco Gerards
+
+ Written by Marco Gerards <marco@student.han.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details. */
+
+/* XKB requires a timer for key repeat and accessibility. Carefully
+ stack all compatibility features. */
+
+#include <mach.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "xkb.h"
+#include <timer.h>
+
+/* For key repeat. */
+static int key_delay = 0;
+static int key_repeat = 0;
+
+/* SlowKeys. */
+static int slowkeys_delay = 50;
+static int slowkeys_active = 0;
+
+/* BounceKeys. */
+static int bouncekeys_delay = 100;
+static int bouncekeys_active = 0;
+
+/* The current status of the timer. */
+enum timer_status
+ {
+ timer_stopped,
+ timer_slowkeys,
+ timer_repeat_delay,
+ timer_repeating
+ };
+
+/* Timer used to time key controls. */
+static struct per_key_timer
+{
+ /* Used for slowkeys and repeat. */
+ struct timer_list enable_timer;
+
+ /* Status of the enable timer. */
+ enum timer_status enable_status;
+
+ /* Used for bouncekeys. */
+ struct timer_list disable_timer;
+} per_key_timers[255];
+
+/* The last pressed key. Only this key may generate keyrepeat events. */
+static int lastkey = 0;
+
+error_t
+xkb_handle_key (keycode_t kc)
+{
+ static keycode_t prevkc = 0;
+ keypress_t key;
+
+ key.keycode = kc & 127;
+ key.prevkc = prevkc;
+ key.repeat = (prevkc == kc);
+ key.redir = 0;
+ key.rel = kc & 128;
+ keystate[key.keycode & 127].keypressed = key.rel ? 0 : 1;
+ debug_printf ("PRESSED: %d\n", !(key.rel));
+ xkb_input (key);
+ prevkc = key.keycode;
+ return 0;
+}
+
+error_t
+xkb_init_repeat (int delay, int repeat)
+{
+ error_t err;
+ err = timer_init ();
+ if (err)
+ return err;
+
+ key_delay = delay;
+ key_repeat = repeat;
+
+ return 0;
+}
+
+static int
+key_enable (void *handle)
+{
+ int key = (int) handle;
+
+ /* Enable the key. */
+ keystate[key].disabled = 0;
+
+ return 0;
+}
+
+/* Called by key timer. The global variable timer_status determines
+ the current control. */
+static int
+key_timing (void *handle)
+{
+ int current_key = (int) handle;
+
+ xkb_handle_key (current_key);
+
+ /* Another key was pressed after this key, stop repeating. */
+ if (lastkey != current_key)
+ {
+ per_key_timers[current_key].enable_status = timer_stopped;
+ return 0;
+ }
+
+ switch (per_key_timers[current_key].enable_status)
+ {
+ case timer_stopped:
+ assert ("Stopped timer triggered timer event\n");
+ break;
+ case timer_slowkeys:
+ per_key_timers[current_key].enable_timer.expires
+ = fetch_jiffies () + key_delay;
+ lastkey = current_key;
+
+ if (keys[current_key].flags & KEYNOREPEAT)
+ {
+ per_key_timers[current_key].enable_status = timer_stopped;
+ /* Stop the timer. */
+ return 0;
+ }
+ else
+ {
+ per_key_timers[current_key].enable_status = timer_repeat_delay;
+ }
+ break;
+ case timer_repeat_delay:
+ per_key_timers[current_key].enable_status = timer_repeating;
+ /* Fall through. */
+ case timer_repeating:
+ per_key_timers[current_key].enable_timer.expires
+ = fetch_jiffies () + key_repeat;
+ break;
+ }
+ return 1;
+}
+
+error_t
+xkb_input_key (int key)
+{
+ int pressed = !(key & 128);
+ int keyc = key & 127;
+
+ debug_printf ("KEYIN: %d\n", key);
+
+ /* Filter out any double or disabled keys. */
+ if (key == lastkey || keystate[keyc].disabled)
+ return 0;
+
+ /* Always handle keyrelease events. */
+ if (!pressed)
+ {
+ /* Stop the timer for this released key. */
+ if (per_key_timers[keyc].enable_status != timer_stopped)
+ {
+ timer_remove (&per_key_timers[keyc].enable_timer);
+ per_key_timers[keyc].enable_status = timer_stopped;
+ }
+
+ /* No more last key; it was released. */
+ if (keyc == lastkey)
+ lastkey = 0;
+
+ /* Make sure the key was pressed before releasing it, it might
+ not have been accepted. */
+ if (keystate[key & 127].keypressed)
+ xkb_handle_key (key);
+
+ /* If bouncekeys is active, disable the key. */
+ if (bouncekeys_active)
+ {
+ keystate[keyc].disabled = 1;
+
+ /* Setup a timer to enable the key. */
+ timer_clear (&per_key_timers[keyc].disable_timer);
+ per_key_timers[keyc].disable_timer.fnc = key_enable;
+ per_key_timers[keyc].disable_timer.fnc_data = (void *) keyc;
+ per_key_timers[keyc].disable_timer.expires
+ = fetch_jiffies () + bouncekeys_delay;
+ timer_add (&per_key_timers[keyc].disable_timer);
+ }
+
+ return 0;
+ }
+
+ /* Setup the timer for slowkeys. */
+ timer_clear (&per_key_timers[keyc].enable_timer);
+ lastkey = keyc;
+ per_key_timers[keyc].enable_timer.fnc = key_timing;
+ per_key_timers[keyc].enable_timer.fnc_data = (void *) keyc;
+
+ if (slowkeys_active)
+ {
+ per_key_timers[keyc].enable_status = timer_slowkeys;
+ per_key_timers[keyc].enable_timer.expires
+ = fetch_jiffies () + slowkeys_delay;
+ }
+ else
+ {
+ /* Immediatly report the keypress. */
+ xkb_handle_key (keyc);
+
+ /* Check if this repeat is allowed for this keycode. */
+ if (keys[keyc].flags & KEYNOREPEAT)
+ return 0; /* Nope. */
+
+ per_key_timers[keyc].enable_status = timer_repeat_delay;
+ per_key_timers[keyc].enable_timer.expires
+ = fetch_jiffies () + key_delay;
+ }
+ timer_add (&per_key_timers[keyc].enable_timer);
+
+ return 0;
+}
diff --git a/console/Makefile b/console/Makefile
new file mode 100644
index 00000000..c5ab543e
--- /dev/null
+++ b/console/Makefile
@@ -0,0 +1,43 @@
+# Copyright (C) 2002, 2012 Free Software Foundation, Inc.
+# Written by Marcus Brinkmann.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+
+dir := console
+makemode := server
+
+target = console
+
+SRCS = console.c display.c pager.c input.c
+
+MIGSTUBS = notifyServer.o tioctlServer.o fs_notifyUser.o
+
+HURDLIBS = netfs fshelp iohelp pager ports ihash shouldbeinlibc
+OTHERLIBS = -lpthread
+OBJS = $(sort $(SRCS:.c=.o) $(MIGSTUBS))
+
+MIGSFLAGS += -imacros $(srcdir)/mutations.h
+
+# This is the old monolithic version of the console server.
+#SRCS = main.c vcons.c focus.c vga-display.c vga.c dynafont.c bdf.c
+#
+#HURDLIBS = trivfs fshelp iohelp threads ports ihash shouldbeinlibc
+#OBJS = $(subst .c,.o,$(SRCS)) tioctlServer.o
+#
+#MIGSFLAGS += -imacros $(srcdir)/mutations.h
+
+include ../Makeconf
diff --git a/console/README.UTF8 b/console/README.UTF8
new file mode 100644
index 00000000..91901e1b
--- /dev/null
+++ b/console/README.UTF8
@@ -0,0 +1,143 @@
+
+The console server supports any encoding supported by iconv, but uses
+Unicode internally. The default encoding is ISO8859-1, another useful
+variant is UTF-8. To configure the console server to use UTF-8 you
+have to use the `--encoding' argument:
+
+# settrans -fg /dev/vcs /hurd/console --encoding=UTF-8
+
+If you actually try this, you will notice two problems:
+
+1. You can not enter the letters in your locale, because the keyboard
+doesn't have the right layout. Keyboard maps come later. For now,
+you have to help yourself with the direct input with RightAlt. Maybe
+I will put a simple compose key feature in the pc_kbd driver, so that
+some western locales can be used more easily.
+
+2. If you bother to look up the unicode hex code and enter it with
+AltGr, the font can not display it! If you are using the ncursesw
+driver, do you use it while you are logged in from a working UTF-8
+terminal? If not, then this is your problem. An ncurses driver for
+non-UTF-8 terminals is on the TODO list. But if you use the VGA
+driver, you need to load a different font.
+
+This is because by default, the vga driver just reads the VGA card
+memory and takes the font that is stored there. This font has a
+limited characterset (256 characters, many graphical symbols among
+that), so you won't get more than a few western characters with that.
+
+Unicode support
+===============
+
+But you want it all. You want to read Middle Old English. You want
+to read Thai. Your Korean spam. Georgian script. Hebrew. And you
+can have it.
+
+You need a Unicode font. There are good ones provided by Markus Kuhn,
+the UCS fonts. Get them here:
+
+http://www.cl.cam.ac.uk/~mgk25/download/ucs-fonts.tar.gz
+
+(See also the web page at http://www.cl.cam.ac.uk/~mgk25/ucs-fonts.html).
+
+Now, load the font by providing it with the --font option to the vga
+driver. I suggest only the 8x13 and the 9x15 fonts, but feel free to
+try others, too. Note that the VGA text mode can not really display 9
+pixel wide characters. But as most characters have the ninth column
+empty, and the VGA text mode can display an empty column between two
+adjacent character cells, this trick allows us to display most of the
+9x15 font correctly. So you won't notice a difference until you come
+to very broad characters or special symbols, where you will see that
+the last column is cut off. (BTW, I wrote the dynafont code carefully
+to still support horizontal line graphic characters properly in 9
+pixel wide fonts. This is done by exploiting some special modes in
+the VGA hardware. This is why in 512 (256) glyph mode and 9 pixel
+wide fonts, you are limited to 448 (224) normal characters: 64 (32)
+slots are reserved for the horizontal line graphic characters so they
+are drawn continuously.)
+
+So, try the following:
+
+# console -d vga --font 8x13.bdf -d pc_kbd -d generic_speaker /dev/vcs
+
+or
+
+# console -d vga --font 9x15.bdf -d pc_kbd -d generic_speaker /dev/vcs
+
+If you are satisfied, copy your default font to
+/lib/hurd/fonts/vga-system.bdf, where it will be picked up
+automatically in favor to the graphic card's font.
+
+More about fonts
+================
+
+While we are talking about fonts, try also the 8x13O font with
+--font-italic and 8x13B or 9x15B font with --font-bold. You can save
+them in /lib/hurd/fonts/vga-system-bold.bdf and
+/lib/hurd/fonts/vga-system-italic.bdf, too.
+
+To activate those fonts on your virtual console, try the following:
+
+# echo `tput sitm`Hello slanted world.`tput ritm`
+
+and
+
+# echo `tput gsbom`Hello bold world.`tput grbom`
+
+I hope you like what you see. Imagine this in emacs font-lock mode.
+
+
+Unicode, finally
+================
+
+There are a few more steps necessary to make your Unicode environment
+ready:
+
+Add a Unicode locale to /etc/locale.gen, and generate the locale
+information for that! For example, I am living in Germany, and
+normally use de_DE with the encoding ISO8859-1. My Unicode locale is
+de_DE.UTF-8, so I am adding that to /etc/locale.gen:
+
+de_DE.UTF-8 UTF-8
+
+and rerun locale-gen:
+
+# locale-gen
+
+See also /share/i18n/SUPPORTED. You can also do this more conveniently with
+
+# dpkg-reconfigure locales
+
+Once you generated this, make it your default locale:
+
+# export LANG=de_DE.UTF-8
+
+If you have also loaded the unicode font above, you are set up. Try
+for example to view the examples/ files in the ucs-fonts package with
+less.
+
+# less fonts/examples/UTF_8-demo.txt
+
+You should see most of that file with the 9x15 font (a bit less with
+the 8x13 font).
+
+You should be able to do the above process with other encodings than
+UTF-8. But you should _always_ use a Unicode font, because the
+console client uses Unicode internally for everything.
+
+Application specific notes
+==========================
+
+If you enter unicode characters at the shell, libreadline loses track
+of the number of characters displayed (it is not aware of multi-byte
+encodings like UTF-8). This is fixed in readline 4.3 (which is not
+yet in Debian).
+
+If you use mutt, install mutt-utf8. For lynx, edit /etc/lynx.cfg,
+making sure that CHARACTER_SET is set to utf-8.
+
+If you use other applications, try to search with google for
+"application-name utf8" or "application-name unicode". Often you find
+what you need. The issues are the same for the GNU/Hurd and GNU/Linux
+systems, so most of the information can be shared, except how to setup
+the system console to support Unicode, of course.
diff --git a/console/console.c b/console/console.c
new file mode 100644
index 00000000..57ae8133
--- /dev/null
+++ b/console/console.c
@@ -0,0 +1,2098 @@
+/* console.c -- A console server.
+
+ Copyright (C) 1997, 1999, 2002, 2003, 2007, 2008, 2010
+ Free Software Foundation, Inc.
+
+ Written by Miles Bader and Marcus Brinkmann.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <locale.h>
+
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <string.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <maptime.h>
+#include <pthread.h>
+
+#include <version.h>
+
+#include <mach.h>
+#include <hurd/netfs.h>
+#include <hurd/ioctl_types.h>
+/* We include console.h for the color numbers. */
+#include <hurd/console.h>
+
+#include "display.h"
+#include "input.h"
+
+#include "fs_notify_U.h"
+#include "libnetfs/fs_S.h"
+#include "libnetfs/io_S.h"
+#include "tioctl_S.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (console);
+
+char *netfs_server_name = "console";
+char *netfs_server_version = HURD_VERSION;
+int netfs_maxsymlinks = 16; /* Arbitrary. */
+
+/* Handy source of time. */
+volatile struct mapped_time_value *console_maptime;
+
+#define DEFAULT_ENCODING "UTF-8"
+#define DEFAULT_INTENSITY CONS_ATTR_INTENSITY_NORMAL
+#define DEFAULT_UNDERLINED 0
+#define DEFAULT_BLINKING 0
+#define DEFAULT_REVERSED 0
+#define DEFAULT_CONCEALED 0
+#define DEFAULT_WIDTH 80
+#define DEFAULT_HEIGHT 25
+#define DEFAULT_LINES 50
+/* Stringification of a macro. */
+#define STRX(s) #s
+#define STR(s) STRX(s)
+
+/* For the help output. */
+#define DEFAULT_ATTRIBUTE_NAME "normal"
+#define DEFAULT_FOREGROUND CONS_COLOR_WHITE
+/* For the help output. */
+#define DEFAULT_FOREGROUND_NAME "white"
+#define DEFAULT_BACKGROUND CONS_COLOR_BLACK
+/* For the help output. */
+#define DEFAULT_BACKGROUND_NAME "black"
+
+
+/* A handle for a console device. */
+typedef struct cons *cons_t;
+
+/* A handle for a virtual console device. */
+typedef struct vcons *vcons_t;
+
+struct vcons
+{
+ /* Protected by cons->lock. */
+ vcons_t next;
+ vcons_t prev;
+ /* We acquire one reference per netnode. */
+ int refcnt;
+
+ /* The following members remain constant over the lifetime of the
+ object and accesses don't need to be locked. */
+ int id;
+ char *name;
+ cons_t cons;
+ display_t display;
+ input_t input;
+
+ pthread_mutex_t lock;
+ /* Nodes in the filesystem referring to this virtual console. */
+ struct node *dir_node;
+ struct node *cons_node;
+ struct node *disp_node;
+ struct node *inpt_node;
+};
+
+/* Pending directory modification requests. */
+struct modreq
+{
+ mach_port_t port;
+ struct modreq *next;
+};
+
+struct cons
+{
+ /* The lock protects the console, all virtual consoles contained in
+ it and the reference counters. It also locks the configuration
+ parameters. */
+ pthread_mutex_t lock;
+ vcons_t vcons_list;
+ /* The encoding. */
+ char *encoding;
+ /* Default attributes. */
+ conchar_attr_t attribute;
+
+ /* Requester of directory modification notifications. */
+ struct modreq *dirmod_reqs;
+ unsigned int dirmod_tick;
+
+ struct node *node;
+ mach_port_t underlying;
+ /* A template for the stat information of all nodes. */
+ struct stat stat_template;
+
+ /* The amount of lines, width and height. */
+ unsigned int lines;
+ unsigned int width;
+ unsigned int height;
+};
+
+
+/* Requires CONS to be locked. */
+static void
+cons_notice_dirchange (cons_t cons, dir_changed_type_t type, char *name)
+{
+ error_t err;
+ struct modreq **preq = &cons->dirmod_reqs;
+
+ cons->dirmod_tick++;
+ while (*preq)
+ {
+ struct modreq *req = *preq;
+
+ err = dir_changed (req->port, cons->dirmod_tick, type, name);
+ if (err && err != MACH_SEND_TIMEOUT)
+ {
+ /* Remove notify port. */
+ *preq = req->next;
+ mach_port_deallocate (mach_task_self (), req->port);
+ free (req);
+ }
+ else
+ preq = &req->next;
+ }
+}
+
+
+/* Lookup the virtual console with number ID in the console CONS,
+ acquire a reference for it, and return it in R_VCONS. If CREATE is
+ true, the virtual console will be created if it doesn't exist yet.
+ If CREATE is true, and ID 0, the first free virtual console id is
+ used. */
+error_t
+vcons_lookup (cons_t cons, int id, int create, vcons_t *r_vcons)
+{
+ error_t err;
+ vcons_t previous_vcons = 0;
+ vcons_t vcons;
+
+ if (!id && !create)
+ return EINVAL;
+
+ pthread_mutex_lock (&cons->lock);
+ if (id)
+ {
+ if (cons->vcons_list && cons->vcons_list->id <= id)
+ {
+ previous_vcons = cons->vcons_list;
+ while (previous_vcons->next && previous_vcons->next->id <= id)
+ previous_vcons = previous_vcons->next;
+ if (previous_vcons->id == id)
+ {
+ previous_vcons->refcnt++;
+ pthread_mutex_unlock (&cons->lock);
+ *r_vcons = previous_vcons;
+ return 0;
+ }
+ }
+ else if (!create)
+ {
+ pthread_mutex_unlock (&cons->lock);
+ return ESRCH;
+ }
+ }
+ else
+ {
+ id = 1;
+ if (cons->vcons_list && cons->vcons_list->id == 1)
+ {
+ previous_vcons = cons->vcons_list;
+ while (previous_vcons && previous_vcons->id == id)
+ {
+ id++;
+ previous_vcons = previous_vcons->next;
+ }
+ }
+ }
+
+ vcons = calloc (1, sizeof (struct vcons));
+ if (!vcons)
+ {
+ pthread_mutex_unlock (&cons->lock);
+ return ENOMEM;
+ }
+ vcons->cons = cons;
+ vcons->refcnt = 1;
+ vcons->id = id;
+ asprintf (&vcons->name, "%i", id);
+ /* XXX Error checking. */
+
+ pthread_mutex_init (&vcons->lock, NULL);
+ err = display_create (&vcons->display, cons->encoding ?: DEFAULT_ENCODING,
+ cons->attribute, cons->lines, cons->width,
+ cons->height);
+ if (err)
+ {
+ free (vcons->name);
+ free (vcons);
+ pthread_mutex_unlock (&cons->lock);
+ return err;
+ }
+
+ err = input_create (&vcons->input, cons->encoding ?: DEFAULT_ENCODING);
+ if (err)
+ {
+ display_destroy (vcons->display);
+ free (vcons->name);
+ free (vcons);
+ pthread_mutex_unlock (&cons->lock);
+ return err;
+ }
+
+ /* Insert the virtual console into the doubly linked list. */
+ if (previous_vcons)
+ {
+ vcons->prev = previous_vcons;
+ if (previous_vcons->next)
+ {
+ previous_vcons->next->prev = vcons;
+ vcons->next = previous_vcons->next;
+ }
+ previous_vcons->next = vcons;
+ }
+ else
+ {
+ if (cons->vcons_list)
+ {
+ cons->vcons_list->prev = vcons;
+ vcons->next = cons->vcons_list;
+ }
+ cons->vcons_list = vcons;
+ }
+ cons_notice_dirchange (cons, DIR_CHANGED_NEW, vcons->name);
+
+ pthread_mutex_unlock (&cons->lock);
+ *r_vcons = vcons;
+ return 0;
+}
+
+/* Acquire an additional reference to the virtual console VCONS. */
+void
+vcons_ref (vcons_t vcons)
+{
+ cons_t cons = vcons->cons;
+
+ pthread_mutex_lock (&cons->lock);
+ vcons->refcnt++;
+ pthread_mutex_unlock (&cons->lock);
+}
+
+/* Release a reference to the virtual console VCONS. If this was the
+ last reference the virtual console is destroyed. */
+void
+vcons_release (vcons_t vcons)
+{
+ cons_t cons = vcons->cons;
+
+ pthread_mutex_lock (&cons->lock);
+ if (!--vcons->refcnt)
+ {
+ /* As we keep a reference for all input focus groups pointing to
+ the virtual console, and a reference for the active console,
+ we know that without references, this virtual console is
+ neither active nor used by any input group. */
+
+ if (vcons->prev)
+ vcons->prev->next = vcons->next;
+ else
+ cons->vcons_list = vcons->next;
+ if (vcons->next)
+ vcons->next->prev = vcons->prev;
+
+ cons_notice_dirchange (cons, DIR_CHANGED_UNLINK, vcons->name);
+
+ /* XXX Destroy the state. */
+ display_destroy (vcons->display);
+ input_destroy (vcons->input);
+ free (vcons->name);
+ free (vcons);
+ }
+ pthread_mutex_unlock (&cons->lock);
+}
+
+struct netnode
+{
+ /* The root node points to the console object. */
+ cons_t cons;
+
+ /* All other nodes point to the virtual console object. */
+ vcons_t vcons;
+};
+
+typedef enum
+ {
+ VCONS_NODE_DIR = 0,
+ VCONS_NODE_CONSOLE,
+ VCONS_NODE_DISPLAY,
+ VCONS_NODE_INPUT
+ } vcons_node_type;
+
+/* Make a new virtual node. Always consumes the ports. */
+static error_t
+new_node (struct node **np, vcons_t vcons, vcons_node_type type)
+{
+ struct netnode *nn = calloc (1, sizeof *nn);
+ if (nn == 0)
+ return ENOMEM;
+
+ nn->vcons = vcons;
+ *np = netfs_make_node (nn);
+ if (*np == 0)
+ {
+ free (nn);
+ return ENOMEM;
+ }
+ (*np)->nn_stat = vcons->cons->stat_template;
+ (*np)->nn_translated = 0;
+
+ switch (type)
+ {
+ case VCONS_NODE_DIR:
+ (*np)->nn_stat.st_ino = vcons->id << 2;
+ (*np)->nn_stat.st_mode |= S_IFDIR;
+ (*np)->nn_stat.st_size = 0;
+ break;
+ case VCONS_NODE_CONSOLE:
+ (*np)->nn_stat.st_ino = (vcons->id << 2) + 1;
+ (*np)->nn_stat.st_mode |= S_IFCHR; /* Don't set nn_translated! */
+ (*np)->nn_stat.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
+ (*np)->nn_stat.st_size = 0;
+ break;
+ case VCONS_NODE_DISPLAY:
+ (*np)->nn_stat.st_ino = (vcons->id << 2) + 2;
+ (*np)->nn_stat.st_mode |= S_IFREG;
+ (*np)->nn_stat.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
+ (*np)->nn_stat.st_size = display_get_size (vcons->display);
+ break;
+ case VCONS_NODE_INPUT:
+ (*np)->nn_stat.st_ino = (vcons->id << 2) + 3;
+ (*np)->nn_stat.st_mode |= S_IFIFO;
+ (*np)->nn_stat.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
+ (*np)->nn_stat.st_size = 0;
+ break;
+ }
+
+ /* If the underlying node isn't a directory, propagate read permission to
+ execute permission since we need that for lookups. */
+ if (! S_ISDIR (vcons->cons->stat_template.st_mode)
+ && S_ISDIR ((*np)->nn_stat.st_mode))
+ {
+ if (vcons->cons->stat_template.st_mode & S_IRUSR)
+ (*np)->nn_stat.st_mode |= S_IXUSR;
+ if (vcons->cons->stat_template.st_mode & S_IRGRP)
+ (*np)->nn_stat.st_mode |= S_IXGRP;
+ if (vcons->cons->stat_template.st_mode & S_IROTH)
+ (*np)->nn_stat.st_mode |= S_IXOTH;
+ }
+
+ fshelp_touch (&(*np)->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
+ console_maptime);
+
+ return 0;
+}
+
+
+/* Node management. */
+
+/* Node NP has no more references; free all its associated
+ storage. */
+void
+netfs_node_norefs (struct node *np)
+{
+ vcons_t vcons = np->nn->vcons;
+
+ /* The root node does never go away. */
+ assert (!np->nn->cons && np->nn->vcons);
+
+ /* Avoid deadlock. */
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+
+ /* Find the back reference to ourself in the virtual console
+ structure, and delete it. */
+ pthread_mutex_lock (&vcons->lock);
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ if (np->references)
+ {
+ /* Someone else got a reference while we were attempting to go
+ away. This can happen in netfs_attempt_lookup. In this
+ case, just unlock the node and do nothing else. */
+ pthread_mutex_unlock (&vcons->lock);
+ pthread_mutex_unlock (&np->lock);
+ return;
+ }
+ if (np == vcons->dir_node)
+ vcons->dir_node = 0;
+ else if (np == vcons->cons_node)
+ vcons->cons_node = 0;
+ else if (np == vcons->disp_node)
+ vcons->disp_node = 0;
+ else
+ {
+ assert (np == vcons->inpt_node);
+ vcons->inpt_node = 0;
+ }
+ pthread_mutex_unlock (&vcons->lock);
+
+ /* Release our reference. */
+ vcons_release (vcons);
+
+ free (np->nn);
+ free (np);
+}
+
+/* Attempt to create a file named NAME in DIR for USER with MODE. Set
+ *NODE to the new node upon return. On any error, clear *NODE.
+ *NODE should be locked on success; no matter what, unlock DIR
+ before returning. */
+error_t
+netfs_attempt_create_file (struct iouser *user, struct node *dir,
+ char *name, mode_t mode, struct node **np)
+{
+ /* We create virtual consoles dynamically on the fly, so there is no
+ need for an explicit create operation. */
+ *np = 0;
+ pthread_mutex_unlock (&dir->lock);
+ return EOPNOTSUPP;
+}
+
+/* Node NODE is being opened by USER, with FLAGS. NEWNODE is nonzero
+ if we just created this node. Return an error if we should not
+ permit the open to complete because of a permission
+ restriction. */
+error_t
+netfs_check_open_permissions (struct iouser *user, struct node *node,
+ int flags, int newnode)
+{
+ error_t err = 0;
+ if (flags & O_READ)
+ err = fshelp_access (&node->nn_stat, S_IREAD, user);
+ if (!err && (flags & O_WRITE))
+ err = fshelp_access (&node->nn_stat, S_IWRITE, user);
+ if (!err && (flags & O_EXEC))
+ err = fshelp_access (&node->nn_stat, S_IEXEC, user);
+ return err;
+}
+
+/* This should attempt a utimes call for the user specified by CRED on node
+ NODE, to change the atime to ATIME and the mtime to MTIME. */
+error_t
+netfs_attempt_utimes (struct iouser *cred, struct node *node,
+ struct timespec *atime, struct timespec *mtime)
+{
+ error_t err = fshelp_isowner (&node->nn_stat, cred);
+ int flags = TOUCH_CTIME;
+
+ if (! err)
+ {
+ if (mtime)
+ node->nn_stat.st_mtim = *mtime;
+ else
+ flags |= TOUCH_MTIME;
+
+ if (atime)
+ node->nn_stat.st_atim = *atime;
+ else
+ flags |= TOUCH_ATIME;
+
+ fshelp_touch (&node->nn_stat, flags, console_maptime);
+ }
+ return err;
+}
+
+/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and O_EXEC)
+ in *TYPES for file NODE and user CRED. */
+error_t
+netfs_report_access (struct iouser *cred, struct node *node, int *types)
+{
+ *types = 0;
+ if (fshelp_access (&node->nn_stat, S_IREAD, cred) == 0)
+ *types |= O_READ;
+ if (fshelp_access (&node->nn_stat, S_IWRITE, cred) == 0)
+ *types |= O_WRITE;
+ if (fshelp_access (&node->nn_stat, S_IEXEC, cred) == 0)
+ *types |= O_EXEC;
+ return 0;
+}
+
+/* Make sure that NP->nn_stat is filled with the most current
+ information. CRED identifies the user responsible for the
+ operation. NP is locked. */
+error_t
+netfs_validate_stat (struct node *np, struct iouser *cred)
+{
+ /* We are always uptodate. */
+ return 0;
+}
+
+/* This should sync the file NODE completely to disk, for the user
+ CRED. If WAIT is set, return only after sync is completely
+ finished. */
+error_t
+netfs_attempt_sync (struct iouser *cred, struct node *np, int wait)
+{
+ return 0;
+}
+
+
+/* Directory management. */
+
+/* Lookup NAME in DIR for USER; set *NODE to the found name upon
+ return. If the name was not found, then return ENOENT. On any
+ error, clear *NODE. (*NODE, if found, should be locked, this call
+ should unlock DIR no matter what.) */
+error_t
+netfs_attempt_lookup (struct iouser *user, struct node *dir,
+ char *name, struct node **node)
+{
+ error_t err;
+
+ *node = 0;
+ err = fshelp_access (&dir->nn_stat, S_IEXEC, user);
+ if (err)
+ goto out;
+
+ if (strcmp (name, ".") == 0)
+ {
+ /* Current directory -- just add an additional reference to DIR
+ and return it. */
+ netfs_nref (dir);
+ *node = dir;
+ goto out;
+ }
+
+ if (strcmp (name, "..") == 0)
+ {
+ /* Parent directory -- if this is the root directory, return
+ EAGAIN. Otherwise return the root node, because we know
+ that our hierarchy is only one level deep. */
+
+ if (dir->nn->cons)
+ err = EAGAIN;
+ else
+ {
+ netfs_nref (netfs_root_node);
+ *node = netfs_root_node;
+ }
+ goto out;
+ }
+
+ if (dir->nn->cons)
+ {
+ /* This is the root directory. Look up the desired virtual
+ console, creating it on the fly if necessary. */
+ vcons_t vcons;
+ int release_vcons = 0;
+ char *tail = NULL;
+ int id;
+ errno = 0;
+ id = strtol (name, &tail, 0);
+ if ((tail && *tail) || errno)
+ {
+ err = ENOENT;
+ goto out;
+ }
+ err = vcons_lookup (dir->nn->cons, id, 1, &vcons);
+ if (err == ESRCH || err == EINVAL)
+ err = ENOENT;
+ if (err)
+ goto out;
+
+ pthread_mutex_lock (&vcons->lock);
+ if (vcons->dir_node)
+ {
+ /* We already have a directory node for this virtual
+ console. Use that, acquire a reference for it, and drop
+ our extra reference to the virtual console. */
+ *node = vcons->dir_node;
+ netfs_nref (*node);
+ release_vcons = 1;
+ }
+ else
+ {
+ /* Create a new directory node, connsuming the reference to
+ the virtual console. */
+ err = new_node (node, vcons, VCONS_NODE_DIR);
+ if (!err)
+ vcons->dir_node = *node;
+ else
+ release_vcons = 1;
+ }
+ pthread_mutex_unlock (&vcons->lock);
+ if (release_vcons)
+ vcons_release (vcons);
+ }
+ else
+ {
+ /* This is a virtual console directory node. */
+ vcons_t vcons = dir->nn->vcons;
+ int ref_vcons = 0;
+ assert (dir == vcons->dir_node);
+
+ if (!strcmp (name, "console"))
+ {
+ pthread_mutex_lock (&vcons->lock);
+ if (vcons->cons_node)
+ {
+ *node = vcons->cons_node;
+ netfs_nref (*node);
+ }
+ else
+ {
+ err = new_node (node, vcons, VCONS_NODE_CONSOLE);
+ if (!err)
+ {
+ vcons->cons_node = *node;
+ ref_vcons = 1;
+ }
+ }
+ pthread_mutex_unlock (&vcons->lock);
+ }
+ else if (!strcmp (name, "display"))
+ {
+ pthread_mutex_lock (&vcons->lock);
+ if (vcons->disp_node)
+ {
+ *node = vcons->disp_node;
+ netfs_nref (*node);
+ }
+ else
+ {
+ err = new_node (node, vcons, VCONS_NODE_DISPLAY);
+ if (!err)
+ {
+ vcons->disp_node = *node;
+ ref_vcons = 1;
+ }
+ }
+ pthread_mutex_unlock (&vcons->lock);
+ }
+ else if (!strcmp (name, "input"))
+ {
+ pthread_mutex_lock (&vcons->lock);
+ if (vcons->inpt_node)
+ {
+ *node = vcons->inpt_node;
+ netfs_nref (*node);
+ }
+ else
+ {
+ err = new_node (node, vcons, VCONS_NODE_INPUT);
+ if (!err)
+ {
+ vcons->inpt_node = *node;
+ ref_vcons = 1;
+ }
+ }
+ pthread_mutex_unlock (&vcons->lock);
+ }
+ else
+ err = ENOENT;
+
+ if (ref_vcons)
+ vcons_ref (vcons);
+ }
+
+ if (!err)
+ fshelp_touch (&dir->nn_stat, TOUCH_ATIME, console_maptime);
+
+ out:
+ pthread_mutex_unlock (&dir->lock);
+ if (err)
+ *node = 0;
+ else
+ pthread_mutex_lock (&(*node)->lock);
+
+ return err;
+}
+
+/* Returned directory entries are aligned to blocks this many bytes long.
+ Must be a power of two. */
+#define DIRENT_ALIGN 4
+#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name)
+/* Length is structure before the name + the name + '\0', all
+ padded to a four-byte alignment. */
+#define DIRENT_LEN(name_len) \
+ ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \
+ & ~(DIRENT_ALIGN - 1))
+
+/* Implement the netfs_get_dirents callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_get_dirents (struct iouser *cred, struct node *dir,
+ int first_entry, int num_entries, char **data,
+ mach_msg_type_number_t *data_len,
+ vm_size_t max_data_len, int *data_entries)
+{
+ error_t err;
+ int count = 0;
+ size_t size = 0; /* Total size of our return block. */
+ struct vcons *first_vcons = NULL;
+ struct vcons *vcons;
+
+ /* Add the length of a directory entry for NAME to SIZE and return true,
+ unless it would overflow MAX_DATA_LEN or NUM_ENTRIES, in which case
+ return false. */
+ int bump_size (const char *name)
+ {
+ if (num_entries == -1 || count < num_entries)
+ {
+ size_t new_size = size + DIRENT_LEN (strlen (name));
+ if (max_data_len > 0 && new_size > max_data_len)
+ return 0;
+ size = new_size;
+ count++;
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ if (!dir->nn->cons && !(dir == dir->nn->vcons->dir_node))
+ return ENOTDIR;
+
+ if (dir->nn->cons)
+ {
+ pthread_mutex_lock (&dir->nn->cons->lock);
+
+ /* Find the first entry. */
+ for (first_vcons = dir->nn->cons->vcons_list, count = 2;
+ first_vcons && first_entry > count;
+ first_vcons = first_vcons->next)
+ count++;
+
+ count = 0;
+ }
+
+ /* Make space for the `.' and `..' entries. */
+ if (first_entry == 0)
+ bump_size (".");
+ if (first_entry <= 1)
+ bump_size ("..");
+
+ if (dir->nn->cons)
+ {
+ /* See how much space we need for the result. */
+ for (vcons = first_vcons; vcons; vcons = vcons->next)
+ if (!bump_size (vcons->name))
+ break;
+ }
+ else
+ {
+ if (first_entry <= 2)
+ bump_size ("console");
+ if (first_entry <= 3)
+ bump_size ("display");
+ if (first_entry <= 4)
+ bump_size ("input");
+ }
+
+ /* Allocate it. */
+ *data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ err = ((void *) *data == (void *) -1) ? errno : 0;
+
+ if (! err)
+ /* Copy out the result. */
+ {
+ char *p = *data;
+
+ int add_dir_entry (const char *name, ino_t fileno, int type)
+ {
+ if (num_entries == -1 || count < num_entries)
+ {
+ struct dirent hdr;
+ size_t name_len = strlen (name);
+ size_t sz = DIRENT_LEN (name_len);
+
+ if (sz > size)
+ return 0;
+ else
+ size -= sz;
+
+ hdr.d_fileno = fileno;
+ hdr.d_reclen = sz;
+ hdr.d_type = type;
+ hdr.d_namlen = name_len;
+
+ memcpy (p, &hdr, DIRENT_NAME_OFFS);
+ strcpy (p + DIRENT_NAME_OFFS, name);
+ p += sz;
+
+ count++;
+
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ *data_len = size;
+ *data_entries = count;
+
+ count = 0;
+
+ if (dir->nn->cons)
+ {
+ /* Add `.' and `..' entries. */
+ if (first_entry == 0)
+ add_dir_entry (".", 2, DT_DIR);
+ if (first_entry <= 1)
+ add_dir_entry ("..", 2, DT_DIR);
+
+ /* Fill in the real directory entries. */
+ for (vcons = first_vcons; vcons; vcons = vcons->next)
+ if (!add_dir_entry (vcons->name,
+ vcons->id << 2, DT_DIR))
+ break;
+ }
+ else
+ {
+ /* Add `.' and `..' entries. */
+ if (first_entry == 0)
+ add_dir_entry (".", dir->nn_stat.st_ino, DT_DIR);
+ if (first_entry <= 1)
+ add_dir_entry ("..", 2, DT_DIR);
+
+ if (first_entry <= 2)
+ add_dir_entry ("console", (dir->nn->vcons->id << 2) + 1, DT_REG);
+ if (first_entry <= 3)
+ add_dir_entry ("display", (dir->nn->vcons->id << 2) + 2, DT_REG);
+ if (first_entry <= 4)
+ add_dir_entry ("input", (dir->nn->vcons->id << 3) + 2, DT_FIFO);
+ }
+ }
+
+ if (dir->nn->cons)
+ pthread_mutex_unlock(&dir->nn->cons->lock);
+
+ fshelp_touch (&dir->nn_stat, TOUCH_ATIME, console_maptime);
+ return err;
+}
+
+/* This should sync the entire remote filesystem. If WAIT is set, return
+ only after sync is completely finished. */
+error_t
+netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+ return 0;
+}
+
+/* This should attempt a chmod call for the user specified by CRED on node
+ NODE, to change the owner to UID and the group to GID. */
+error_t
+netfs_attempt_chown (struct iouser *cred, struct node *node,
+ uid_t uid, uid_t gid)
+{
+ cons_t cons = node->nn->cons;
+ vcons_t vcons;
+ error_t err;
+
+ if (!cons)
+ return EOPNOTSUPP;
+
+ err = file_chown (cons->underlying, uid, gid);
+ if (err)
+ return err;
+
+ /* Change NODE's owner. */
+ node->nn_stat.st_uid = uid;
+ node->nn_stat.st_gid = gid;
+
+ pthread_mutex_lock (&cons->lock);
+ cons->stat_template.st_uid = uid;
+ cons->stat_template.st_gid = gid;
+
+ /* Change the owner of each leaf node. */
+ for (vcons = cons->vcons_list; vcons; vcons = vcons->next)
+ {
+ if (vcons->dir_node)
+ {
+ vcons->dir_node->nn_stat.st_uid = uid;
+ vcons->dir_node->nn_stat.st_gid = gid;
+ }
+ if (vcons->cons_node)
+ {
+ vcons->cons_node->nn_stat.st_uid = uid;
+ vcons->cons_node->nn_stat.st_gid = gid;
+ }
+ if (vcons->disp_node)
+ {
+ vcons->disp_node->nn_stat.st_uid = uid;
+ vcons->disp_node->nn_stat.st_gid = gid;
+ }
+ if (vcons->inpt_node)
+ {
+ vcons->inpt_node->nn_stat.st_uid = uid;
+ vcons->inpt_node->nn_stat.st_gid = gid;
+ }
+ }
+ pthread_mutex_unlock (&cons->lock);
+ fshelp_touch (&node->nn_stat, TOUCH_CTIME, console_maptime);
+ return err;
+}
+
+/* This should attempt a chauthor call for the user specified by CRED on node
+ NODE, to change the author to AUTHOR. */
+error_t
+netfs_attempt_chauthor (struct iouser *cred, struct node *node, uid_t author)
+{
+ cons_t cons = node->nn->cons;
+ vcons_t vcons;
+ error_t err;
+
+ if (!cons)
+ return EOPNOTSUPP;
+
+ err = file_chauthor (cons->underlying, author);
+ if (err)
+ return err;
+
+ /* Change NODE's author. */
+ node->nn_stat.st_author = author;
+
+ pthread_mutex_lock (&cons->lock);
+ cons->stat_template.st_author = author;
+
+ /* Change the author of each leaf node. */
+ for (vcons = cons->vcons_list; vcons; vcons = vcons->next)
+ {
+ if (vcons->dir_node)
+ vcons->dir_node->nn_stat.st_author = author;
+ if (vcons->cons_node)
+ vcons->cons_node->nn_stat.st_author = author;
+ if (vcons->disp_node)
+ vcons->disp_node->nn_stat.st_author = author;
+ if (vcons->inpt_node)
+ vcons->inpt_node->nn_stat.st_author = author;
+ }
+ pthread_mutex_unlock (&cons->lock);
+ fshelp_touch (&node->nn_stat, TOUCH_CTIME, console_maptime);
+ return err;
+}
+
+/* This should attempt a chmod call for the user specified by CRED on node
+ NODE, to change the mode to MODE. Unlike the normal Unix and Hurd meaning
+ of chmod, this function is also used to attempt to change files into other
+ types. If such a transition is attempted which is impossible, then return
+ EOPNOTSUPP. */
+error_t
+netfs_attempt_chmod (struct iouser *cred, struct node *node, mode_t mode)
+{
+ error_t err;
+
+ mode &= ~S_ITRANS;
+ if ((mode & S_IFMT) == 0)
+ mode |= (node->nn_stat.st_mode & S_IFMT);
+
+ if (!node->nn->cons || ((mode & S_IFMT) != (node->nn_stat.st_mode & S_IFMT)))
+ return EOPNOTSUPP;
+
+ err = file_chmod (node->nn->cons->underlying, mode & ~S_IFMT);
+ if (err)
+ return err;
+
+ node->nn_stat.st_mode = mode;
+ fshelp_touch (&node->nn_stat, TOUCH_CTIME, console_maptime);
+ return err;
+}
+
+
+/* The user must define this function. Attempt to turn locked node NP
+ (user CRED) into a symlink with target NAME. */
+error_t
+netfs_attempt_mksymlink (struct iouser *cred, struct node *np, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_mkdev (struct iouser *cred, struct node *np,
+ mode_t type, dev_t indexes)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_chflags (struct iouser *cred, struct node *np, int flags)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_set_size (struct iouser *cred, struct node *np, off_t size)
+{
+ vcons_t vcons = np->nn->vcons;
+
+ if (!vcons || np == vcons->dir_node
+ || np == vcons->disp_node)
+ return EOPNOTSUPP;
+
+ assert (np == vcons->cons_node || np == vcons->inpt_node);
+ return 0;
+}
+
+error_t
+netfs_attempt_statfs (struct iouser *cred, struct node *np, struct statfs *st)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_mkdir (struct iouser *user, struct node *dir,
+ char *name, mode_t mode)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_unlink (struct iouser *user, struct node *dir, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_rename (struct iouser *user, struct node *fromdir,
+ char *fromname, struct node *todir,
+ char *toname, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_rmdir (struct iouser *user,
+ struct node *dir, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_link (struct iouser *user, struct node *dir,
+ struct node *file, char *name, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_mkfile (struct iouser *user, struct node *dir,
+ mode_t mode, struct node **np)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_readlink (struct iouser *user, struct node *np, char *buf)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_read (struct iouser *cred, struct node *np,
+ off_t offset, size_t *len, void *data)
+{
+ error_t err = 0;
+ vcons_t vcons = np->nn->vcons;
+ if (!vcons || np == vcons->dir_node
+ || np == vcons->inpt_node)
+ return EOPNOTSUPP;
+
+ pthread_mutex_unlock (&np->lock);
+ if (np == vcons->cons_node)
+ {
+ ssize_t amt = input_dequeue (vcons->input,
+ /* cred->po->openstat & O_NONBLOCK */ 0,
+ data, *len);
+ if (amt == -1)
+ err = errno;
+ else
+ *len = amt;
+ }
+ else
+ {
+ /* Pass display content to caller. */
+ ssize_t amt = *len;
+ assert (np == vcons->disp_node);
+
+ if (offset + amt > np->nn_stat.st_size)
+ amt = np->nn_stat.st_size - offset;
+ if (amt < 0)
+ amt = 0;
+ else
+ amt = display_read (vcons->display,
+ /* cred->po->openstat & O_NONBLOCK */ 0,
+ offset, data, amt);
+ if (amt == -1)
+ err = errno;
+ else
+ *len = amt;
+ }
+ pthread_mutex_lock (&np->lock);
+ return err;
+}
+
+error_t
+netfs_attempt_write (struct iouser *cred, struct node *np,
+ off_t offset, size_t *len, void *data)
+{
+ error_t err = 0;
+ vcons_t vcons = np->nn->vcons;
+ if (!vcons || np == vcons->dir_node
+ || np == vcons->disp_node)
+ return EOPNOTSUPP;
+
+ pthread_mutex_unlock (&np->lock);
+ if (np == vcons->cons_node)
+ {
+ /* The term server is writing to the console device. Feed the
+ data into the screen matrix display. */
+ int amt = display_output (vcons->display,
+ /* cred->po->openstat & O_NONBLOCK */ 0,
+ data, *len);
+ if (amt == -1)
+ err = errno;
+ else
+ *len = amt;
+ }
+ else
+ {
+ int amt;
+ /* The input driver is writing to the input device. Feed the
+ data into the input queue. */
+ assert (np == vcons->inpt_node);
+
+ amt = input_enqueue (vcons->input,
+ /* cred->po->openstat & O_NONBLOCK */ 1,
+ data, *len);
+ if (amt == -1)
+ err = errno;
+ else
+ *len = amt;
+ }
+ pthread_mutex_lock (&np->lock);
+ return err;
+}
+
+
+/* Implement io_map as described in <hurd/io.defs>. */
+kern_return_t
+netfs_S_io_map (struct protid *cred,
+ memory_object_t *rdobj,
+ mach_msg_type_name_t *rdtype,
+ memory_object_t *wrobj,
+ mach_msg_type_name_t *wrtype)
+{
+ int flags;
+ struct node *np;
+ vcons_t vcons;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+ vcons = np->nn->vcons;
+ if (!vcons || np != vcons->disp_node)
+ return EOPNOTSUPP;
+
+ *wrobj = *rdobj = MACH_PORT_NULL;
+
+ flags = cred->po->openstat & (O_READ | O_WRITE);
+
+ pthread_mutex_lock (&np->lock);
+ switch (flags)
+ {
+ case O_READ | O_WRITE:
+ *wrobj = *rdobj = display_get_filemap (vcons->display,
+ VM_PROT_READ | VM_PROT_WRITE);
+ if (*wrobj == MACH_PORT_NULL)
+ goto error;
+ mach_port_mod_refs (mach_task_self (), *rdobj, MACH_PORT_RIGHT_SEND, 1);
+ break;
+ case O_READ:
+ *rdobj = display_get_filemap (vcons->display, VM_PROT_READ);
+ if (*rdobj == MACH_PORT_NULL)
+ goto error;
+ break;
+ case O_WRITE:
+ *wrobj = display_get_filemap (vcons->display, VM_PROT_WRITE);
+ if (*wrobj == MACH_PORT_NULL)
+ goto error;
+ break;
+ }
+ pthread_mutex_unlock (&np->lock);
+
+ *rdtype = MACH_MSG_TYPE_MOVE_SEND;
+ *wrtype = MACH_MSG_TYPE_MOVE_SEND;
+
+ return 0;
+
+ error:
+ pthread_mutex_unlock (&np->lock);
+ return errno;
+}
+
+
+kern_return_t
+netfs_S_dir_notice_changes (struct protid *cred, mach_port_t notify)
+{
+ error_t err;
+ cons_t cons;
+ struct modreq **preq;
+ struct modreq *req;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ cons = cred->po->np->nn->cons;
+ if (!cons)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&cons->lock);
+ /* We have to prevent that we accumulate dead-names in the
+ notification list. They are cleaned up in cons_notice_dirchange,
+ but that is not called often enough, so we also clean them up
+ here. This way, the maximum of dead-names will never exceed the
+ prior maximum of active clients. The better way would be to
+ request dead-name notifications, XXX. */
+ preq = &cons->dirmod_reqs;
+
+ while (*preq)
+ {
+ mach_port_type_t type;
+ req = *preq;
+
+ err = mach_port_type (mach_task_self (), req->port, &type);
+ if (!err && type == MACH_PORT_TYPE_DEAD_NAME)
+ {
+ /* Remove notify port. */
+ *preq = req->next;
+ mach_port_deallocate (mach_task_self (), req->port);
+ free (req);
+ }
+ else
+ preq = &req->next;
+ }
+
+ err = dir_changed (notify, cons->dirmod_tick, DIR_CHANGED_NULL, "");
+ if (err)
+ {
+ pthread_mutex_unlock (&cons->lock);
+ return err;
+ }
+ req = malloc (sizeof (struct modreq));
+ if (!req)
+ {
+ pthread_mutex_unlock (&cons->lock);
+ return errno;
+ }
+ req->port = notify;
+ req->next = cons->dirmod_reqs;
+ cons->dirmod_reqs = req;
+ pthread_mutex_unlock (&cons->lock);
+ return 0;
+}
+
+kern_return_t
+netfs_S_file_notice_changes (struct protid *cred, mach_port_t notify)
+{
+ struct node *np;
+ vcons_t vcons;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+ vcons = np->nn->vcons;
+ if (!vcons || np != vcons->disp_node)
+ return EOPNOTSUPP;
+
+ return display_notice_changes (vcons->display, notify);
+}
+
+
+static const char *color_names[CONS_COLOR_MAX + 1] =
+ {
+ [CONS_COLOR_BLACK] = "black",
+ [CONS_COLOR_RED] = "red",
+ [CONS_COLOR_GREEN] = "green",
+ [CONS_COLOR_YELLOW] = "yellow",
+ [CONS_COLOR_BLUE] = "blue",
+ [CONS_COLOR_MAGENTA] = "magenta",
+ [CONS_COLOR_CYAN] = "cyan",
+ [CONS_COLOR_WHITE] = "white"
+ };
+
+static const struct argp_option options[] =
+{
+ { "foreground", 'f', "COLOR", 0, "Set foreground color to"
+ " COLOR (default `" DEFAULT_FOREGROUND_NAME "')" },
+ { "background", 'b', "COLOR", 0, "Set background color to"
+ " COLOR (default `" DEFAULT_BACKGROUND_NAME "')" },
+ { "attribute", 'a', "ATTR[,...]", 0, "Set further default attributes"
+ " (default `" DEFAULT_ATTRIBUTE_NAME "')" },
+ { "encoding", 'e', "NAME", 0, "Set encoding of virtual consoles to"
+ " NAME (default `" DEFAULT_ENCODING "')" },
+ { "width", 'w', "WIDTH", 0, "Set width to WIDTH (default `"
+ STR(DEFAULT_WIDTH) "')" },
+ { "height", 'h', "HEIGHT", 0, "Set height to HEIGHT (default `"
+ STR(DEFAULT_HEIGHT) "')" },
+ { "lines", 'l', "LINES", 0, "Set amount of scrollback lines to LINES "
+ "(default `" STR(DEFAULT_LINES) "')" },
+ {0}
+};
+
+static error_t
+parse_color (const char *name, int *number)
+{
+ if (isdigit (*name))
+ {
+ long int nr;
+ char *tail;
+
+ errno = 0;
+
+ nr = strtol (name, &tail, 0);
+ if (errno || *tail || nr < 0 || nr > CONS_COLOR_MAX)
+ return EINVAL;
+ *number = nr;
+ return 0;
+ }
+ else
+ {
+ int i;
+ for (i = 0; i <= CONS_COLOR_MAX; i++)
+ if (!strcmp (color_names[i], name))
+ {
+ *number = i;
+ return 0;
+ }
+ return EINVAL;
+ }
+}
+
+static error_t
+parse_attributes (const char *name, conchar_attr_t *attr)
+{
+ while (*name)
+ {
+ int value = 1;
+
+ if (!strncmp (name, "not-", 4))
+ {
+ value = 0;
+ name += 4;
+ }
+
+ if (!strncmp (name, "normal", 6))
+ {
+ name += 6;
+ if (value != 1)
+ return EINVAL;
+ attr->intensity = CONS_ATTR_INTENSITY_NORMAL;
+ }
+ else if (!strncmp (name, "bright", 6))
+ {
+ name += 6;
+ if (value != 1)
+ return EINVAL;
+ attr->intensity = CONS_ATTR_INTENSITY_BOLD;
+ }
+ else if (!strncmp (name, "dim", 3))
+ {
+ name += 3;
+ if (value != 1)
+ return EINVAL;
+ attr->intensity = CONS_ATTR_INTENSITY_DIM;
+ }
+ else if (!strncmp (name, "underlined", 10))
+ {
+ name += 10;
+ attr->underlined = value;
+ }
+ else if (!strncmp (name, "blinking", 8))
+ {
+ name += 8;
+ attr->blinking = value;
+ }
+ else if (!strncmp (name, "concealed", 9))
+ {
+ name += 9;
+ attr->concealed = value;
+ }
+ else if (!strncmp (name, "italic", 6))
+ {
+ name += 6;
+ attr->italic = value;
+ }
+ else if (!strncmp (name, "bold", 4))
+ {
+ name += 4;
+ attr->bold = value;
+ }
+ else
+ return EINVAL;
+
+ if (name[0] == ',')
+ name++;
+ else if (name[0] != '\0')
+ return EINVAL;
+ }
+ return 0;
+}
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ cons_t cons = state->input ?: netfs_root_node->nn->cons;
+ error_t err;
+ int color = 0;
+ char *tail;
+
+ switch (opt)
+ {
+ default:
+ return ARGP_ERR_UNKNOWN;
+ case ARGP_KEY_SUCCESS:
+ case ARGP_KEY_ERROR:
+ break;
+
+ case ARGP_KEY_INIT:
+ pthread_mutex_lock (&cons->lock);
+ break;
+
+ case ARGP_KEY_FINI:
+ pthread_mutex_unlock (&cons->lock);
+ break;
+
+ case 'f':
+ err = parse_color (arg, &color);
+ cons->attribute.fgcol = color;
+ if (err)
+ argp_error (state, "Invalid color name: %s", arg);
+ break;
+
+ case 'b':
+ err = parse_color (arg, &color);
+ cons->attribute.bgcol = color;
+ if (err)
+ argp_error (state, "Invalid color name: %s", arg);
+ break;
+
+ case 'a':
+ err = parse_attributes (arg, &cons->attribute);
+ if (err)
+ argp_error (state, "Invalid attribute specifier: %s", arg);
+ break;
+
+ case 'l':
+ errno = 0;
+ cons->lines = strtoul (arg, &tail, 0);
+ if (tail == NULL || tail == arg || *tail != '\0')
+ argp_error (state, "LINES is not a number: %s", arg);
+ if (errno)
+ argp_error (state, "Overflow in argument LINES %s", arg);
+ break;
+
+ case 'w':
+ errno = 0;
+ cons->width = strtoul (arg, &tail, 0);
+ if (tail == NULL || tail == arg || *tail != '\0')
+ argp_error (state, "WIDTH is not a number: %s", arg);
+ if (errno)
+ argp_error (state, "Overflow in argument WIDTH %s", arg);
+ break;
+
+ case 'h':
+ errno = 0;
+ cons->height = strtoul (arg, &tail, 0);
+ if (tail == NULL || tail == arg || *tail != '\0')
+ argp_error (state, "HEIGHT is not a number: %s", arg);
+ if (errno)
+ argp_error (state, "Overflow in argument HEIGHT %s", arg);
+ break;
+
+ case 'e':
+ /* XXX Check validity of encoding. Can we perform all necessary
+ conversions? */
+ {
+ char *new = strdup (arg);
+ if (!new)
+ return ENOMEM;
+ if (cons->encoding)
+ free (cons->encoding);
+ cons->encoding = new;
+ }
+ break;
+ }
+ return 0;
+}
+
+/* Return an argz string describing the current options. Fill *ARGZ
+ with a pointer to newly malloced storage holding the list and *LEN
+ to the length of that storage. */
+error_t
+netfs_append_args (char **argz, size_t *argz_len)
+{
+ error_t err = 0;
+ cons_t cons = netfs_root_node->nn->cons;
+ /* The longest possible is 61 characters long:
+ "normal,not-underlined,not-blinking,not-reversed,not-concealed". */
+ char attr_str[80] = "--attribute=";
+ char *attr = &attr_str[12];
+ char *attrp = attr;
+
+ if (cons->encoding && strcmp (cons->encoding, DEFAULT_ENCODING))
+ {
+ char *buf;
+ if (asprintf (&buf, "--encoding=%s", cons->encoding) < 0)
+ err = ENOMEM;
+ else
+ err = argz_add (argz, argz_len, buf);
+
+ }
+ if (!err && cons->attribute.fgcol != DEFAULT_FOREGROUND)
+ {
+ char *buf;
+ if (asprintf (&buf, "--foreground=%s",
+ color_names[cons->attribute.fgcol]) < 0)
+ err = ENOMEM;
+ else
+ err = argz_add (argz, argz_len, buf);
+
+ }
+ if (!err && cons->attribute.bgcol != DEFAULT_BACKGROUND)
+ {
+ char *buf;
+ if (asprintf (&buf, "--background=%s",
+ color_names[cons->attribute.bgcol]) < 0)
+ err = ENOMEM;
+ else
+ err = argz_add (argz, argz_len, buf);
+ }
+ if (!err && cons->lines != DEFAULT_LINES)
+ {
+ char *buf;
+ if (asprintf (&buf, "--lines=%d", cons->lines) < 0)
+ err = ENOMEM;
+ else
+ err = argz_add (argz, argz_len, buf);
+ }
+ if (!err && cons->width != DEFAULT_WIDTH)
+ {
+ char *buf;
+ if (asprintf (&buf, "--width=%d", cons->lines) < 0)
+ err = ENOMEM;
+ else
+ err = argz_add (argz, argz_len, buf);
+ }
+ if (!err && cons->height != DEFAULT_HEIGHT)
+ {
+ char *buf;
+ if (asprintf (&buf, "--height=%d", cons->height) < 0)
+ err = ENOMEM;
+ else
+ err = argz_add (argz, argz_len, buf);
+ }
+ if (!err && cons->attribute.intensity != DEFAULT_INTENSITY)
+ {
+ if (attrp != attr)
+ *(attrp++) = ',';
+ switch (cons->attribute.intensity)
+ {
+ case CONS_ATTR_INTENSITY_NORMAL:
+ attrp = stpcpy (attrp, "normal");
+ break;
+ case CONS_ATTR_INTENSITY_BOLD:
+ attrp = stpcpy (attrp, "bold");
+ break;
+ case CONS_ATTR_INTENSITY_DIM:
+ attrp = stpcpy (attrp, "dim");
+ break;
+ }
+ }
+ if (!err && cons->attribute.underlined != DEFAULT_UNDERLINED)
+ {
+ if (attrp != attr)
+ *(attrp++) = ',';
+ if (!cons->attribute.underlined)
+ attrp = stpcpy (attrp, "not-");
+ attrp = stpcpy (attrp, "underlined");
+ }
+ if (!err && cons->attribute.blinking != DEFAULT_BLINKING)
+ {
+ if (attrp != attr)
+ *(attrp++) = ',';
+ if (!cons->attribute.blinking)
+ attrp = stpcpy (attrp, "not-");
+ attrp = stpcpy (attrp, "blinking");
+ }
+ if (!err && cons->attribute.reversed != DEFAULT_REVERSED)
+ {
+ if (attrp != attr)
+ *(attrp++) = ',';
+ if (!cons->attribute.reversed)
+ attrp = stpcpy (attrp, "not-");
+ attrp = stpcpy (attrp, "reversed");
+ }
+ if (!err && cons->attribute.concealed != DEFAULT_CONCEALED)
+ {
+ if (attrp != attr)
+ *(attrp++) = ',';
+ if (!cons->attribute.concealed)
+ attrp = stpcpy (attrp, "not-");
+ attrp = stpcpy (attrp, "concealed");
+ }
+ if (!err && attrp != attr)
+ err = argz_add (argz, argz_len, attr_str);
+
+ return err;
+}
+
+
+kern_return_t
+S_tioctl_tiocflush (struct protid *cred, int queue_selector)
+{
+ struct node *np;
+ vcons_t vcons;
+
+ if (!cred)
+ return EOPNOTSUPP;
+ if (!(cred->po->openstat & (O_READ | O_WRITE)))
+ return EBADF;
+
+ np = cred->po->np;
+ vcons = np->nn->vcons;
+ if (!vcons || np != vcons->cons_node)
+ return EOPNOTSUPP;
+
+ if (!queue_selector)
+ queue_selector = O_READ | O_WRITE;
+
+ if (queue_selector & O_READ)
+ input_flush (vcons->input);
+ if (queue_selector & O_WRITE)
+ display_discard_output (vcons->display);
+
+ return 0;
+}
+
+kern_return_t
+S_tioctl_tiocgwinsz (struct protid *cred, struct winsize *size)
+{
+ struct node *np;
+ vcons_t vcons;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+ vcons = np->nn->vcons;
+ if (!vcons || np != vcons->cons_node)
+ return EOPNOTSUPP;
+
+ display_getsize (vcons->display, size);
+ return 0;
+}
+
+kern_return_t
+S_tioctl_tiocstart (struct protid *cred)
+{
+ struct node *np;
+ vcons_t vcons;
+
+ if (!cred)
+ return EOPNOTSUPP;
+ if (!(cred->po->openstat & (O_READ | O_WRITE)))
+ return EBADF;
+
+ np = cred->po->np;
+ vcons = np->nn->vcons;
+ if (!vcons || np != vcons->cons_node)
+ return EOPNOTSUPP;
+
+ display_start_output (vcons->display);
+ return 0;
+}
+
+kern_return_t
+S_tioctl_tiocstop (struct protid *cred)
+{
+ struct node *np;
+ vcons_t vcons;
+
+ if (!cred)
+ return EOPNOTSUPP;
+ if (!(cred->po->openstat & (O_READ | O_WRITE)))
+ return EBADF;
+
+ np = cred->po->np;
+ vcons = np->nn->vcons;
+ if (!vcons || np != vcons->cons_node)
+ return EOPNOTSUPP;
+
+ display_stop_output (vcons->display);
+ return 0;
+}
+
+
+kern_return_t
+S_tioctl_tiocoutq (struct protid *cred, int *queue_size)
+{
+ struct node *np;
+ vcons_t vcons;
+
+ if (!cred)
+ return EOPNOTSUPP;
+ if (!(cred->po->openstat & (O_READ | O_WRITE)))
+ return EBADF;
+
+ np = cred->po->np;
+ vcons = np->nn->vcons;
+ if (!vcons || np != vcons->cons_node)
+ return EOPNOTSUPP;
+
+ *queue_size = display_pending_output (vcons->display);
+ return 0;
+}
+
+kern_return_t
+S_tioctl_tiocspgrp (struct protid *cred, int pgrp)
+{
+ struct node *np;
+ vcons_t vcons;
+
+ if (!cred)
+ return EOPNOTSUPP;
+ if (!(cred->po->openstat & (O_READ | O_WRITE)))
+ return EBADF;
+
+ np = cred->po->np;
+ vcons = np->nn->vcons;
+ if (!vcons || np != vcons->cons_node)
+ return EOPNOTSUPP;
+
+ display_set_owner (vcons->display, -pgrp);
+ return 0;
+}
+
+kern_return_t
+S_tioctl_tiocgpgrp (struct protid *cred, int *pgrp)
+{
+ error_t err;
+ struct node *np;
+ vcons_t vcons;
+
+ if (!cred)
+ return EOPNOTSUPP;
+ if (!(cred->po->openstat & (O_READ | O_WRITE)))
+ return EBADF;
+
+ np = cred->po->np;
+ vcons = np->nn->vcons;
+ if (!vcons || np != vcons->cons_node)
+ return EOPNOTSUPP;
+
+ err = display_get_owner (vcons->display, pgrp);
+ if (!err)
+ *pgrp = -*pgrp;
+
+ return err;
+}
+
+kern_return_t
+S_tioctl_tiocmodg (struct protid *cred, int *state)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocmods (struct protid *cred, int state)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocexcl (struct protid *cred)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocnxcl (struct protid *cred)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocgeta (struct protid *cred, tcflag_t *modes, cc_t *ccs,
+ speed_t *speeds)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocseta (struct protid *cred, tcflag_t *modes, cc_t *ccs,
+ speed_t *speeds)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocsetaw (struct protid *cred, tcflag_t *modes, cc_t *ccs,
+ speed_t *speeds)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocsetaf (struct protid *cred, tcflag_t *modes, cc_t *ccs,
+ speed_t *speeds)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocgetd (struct protid *cred, int *disc)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocsetd (struct protid *cred, int disc)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocdrain (struct protid *cred)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocmget (struct protid *cred, int *bits)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocmset (struct protid *cred, int bits)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocsig (struct protid *cred, int sig)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocext (struct protid *cred, int mode)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocucntl (struct protid *cred, int mode)
+
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocswinsz (struct protid *cred, struct winsize size)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocremote (struct protid *cred, int how)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocmbic (struct protid *cred, int bits)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocmbis (struct protid *cred, int bits)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocpkt (struct protid *cred, int mode)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocsti (struct protid *cred, char c)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tioccdtr (struct protid *cred)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocsdtr (struct protid *cred)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tioccbrk (struct protid *cred)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_tioctl_tiocsbrk (struct protid *cred)
+{
+ return EOPNOTSUPP;
+}
+
+
+int
+console_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ extern int netfs_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp);
+ extern int tioctl_server (mach_msg_header_t *inp, mach_msg_header_t *outp);
+
+ return (netfs_demuxer (inp, outp)
+ || tioctl_server (inp, outp));
+}
+
+const struct argp netfs_std_runtime_argp =
+ { options, parse_opt, NULL,
+ "A translator that provides virtual consoles." };
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct stat ul_stat;
+ cons_t cons;
+ struct netnode root_nn = { vcons: 0 };
+
+ cons = malloc (sizeof (struct cons));
+ if (!cons)
+ error (1, ENOMEM, "Cannot create console structure");
+ pthread_mutex_init (&cons->lock, NULL);
+ cons->encoding = NULL;
+ cons->width = DEFAULT_WIDTH;
+ cons->height = DEFAULT_HEIGHT;
+ cons->lines = DEFAULT_LINES;
+ cons->attribute.intensity = DEFAULT_INTENSITY;
+ cons->attribute.underlined = DEFAULT_UNDERLINED;
+ cons->attribute.blinking = DEFAULT_BLINKING;
+ cons->attribute.reversed = DEFAULT_REVERSED;
+ cons->attribute.concealed = DEFAULT_CONCEALED;
+ cons->attribute.fgcol = DEFAULT_FOREGROUND;
+ cons->attribute.bgcol = DEFAULT_BACKGROUND;
+ cons->vcons_list = NULL;
+ cons->dirmod_reqs = NULL;
+ cons->dirmod_tick = 0;
+ root_nn.cons = cons;
+
+ /* Parse our command line arguments. */
+ argp_parse (&netfs_std_runtime_argp, argc, argv, 0, 0, cons);
+
+ setlocale (LC_CTYPE, "C.UTF-8");
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+
+ netfs_init ();
+
+ display_init ();
+
+ /* Create the root node (some attributes initialized below). */
+ netfs_root_node = netfs_make_node (&root_nn);
+ if (! netfs_root_node)
+ error (2, ENOMEM, "Cannot create root node");
+
+ err = maptime_map (0, 0, &console_maptime);
+ if (err)
+ error (3, err, "Cannot map time");
+
+ cons->node = netfs_root_node;
+ cons->underlying = netfs_startup (bootstrap, O_READ);
+ if (cons->underlying == MACH_PORT_NULL)
+ error (4, 0, "Cannot get underlying node");
+
+ err = io_stat (cons->underlying, &ul_stat);
+ if (err)
+ error (5, err, "Cannot stat underlying node");
+
+ /* CONS.stat_template contains some fields that are inherited by all
+ nodes we create. */
+ cons->stat_template.st_uid = ul_stat.st_uid;
+ cons->stat_template.st_gid = ul_stat.st_gid;
+ cons->stat_template.st_author = ul_stat.st_author;
+ cons->stat_template.st_mode = (ul_stat.st_mode & ~S_IFMT & ~S_ITRANS);
+ cons->stat_template.st_fsid = getpid ();
+ cons->stat_template.st_nlink = 1;
+ cons->stat_template.st_fstype = FSTYPE_MISC;
+
+ /* Initialize the root node's stat information. */
+ netfs_root_node->nn_stat = cons->stat_template;
+ netfs_root_node->nn_stat.st_ino = 2;
+ netfs_root_node->nn_stat.st_mode |= S_IFDIR;
+ netfs_root_node->nn_translated = 0;
+
+ /* If the underlying node isn't a directory, propagate read permission to
+ execute permission since we need that for lookups. */
+ if (! S_ISDIR (ul_stat.st_mode))
+ {
+ if (ul_stat.st_mode & S_IRUSR)
+ netfs_root_node->nn_stat.st_mode |= S_IXUSR;
+ if (ul_stat.st_mode & S_IRGRP)
+ netfs_root_node->nn_stat.st_mode |= S_IXGRP;
+ if (ul_stat.st_mode & S_IROTH)
+ netfs_root_node->nn_stat.st_mode |= S_IXOTH;
+ }
+
+ fshelp_touch (&netfs_root_node->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
+ console_maptime);
+
+ do
+ {
+ ports_manage_port_operations_multithread (netfs_port_bucket,
+ console_demuxer,
+ 1000 * 60 * 2,
+ 1000 * 60 * 10,
+ 0);
+ err = netfs_shutdown (0);
+ }
+ while (err);
+
+ exit (err);
+}
diff --git a/console/display.c b/console/display.c
new file mode 100644
index 00000000..09add5c6
--- /dev/null
+++ b/console/display.c
@@ -0,0 +1,2152 @@
+/* display.c - The display component of a virtual console.
+ Copyright (C) 1999 Kalle Olavi Niemitalo (emu.c from colortext 0.3).
+ Copyright (C) 2002, 2003, 2010 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann and Kalle Olavi Niemitalo.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stddef.h>
+#include <errno.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <iconv.h>
+#include <argp.h>
+#include <string.h>
+#include <assert.h>
+#include <error.h>
+
+#include <pthread.h>
+
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/console.h>
+
+#ifndef __STDC_ISO_10646__
+#error It is required that wchar_t is UCS-4.
+#endif
+
+#include "display.h"
+#include "pager.h"
+
+#include "notify_S.h"
+
+struct changes
+{
+ uint32_t flags;
+ struct
+ {
+ uint32_t col;
+ uint32_t row;
+ uint32_t status;
+ } cursor;
+ struct
+ {
+ uint32_t cur_line;
+ uint32_t scr_lines;
+ } screen;
+
+ uint32_t bell_audible;
+ uint32_t bell_visible;
+
+ off_t start;
+ off_t end;
+
+#define DISPLAY_CHANGE_CURSOR_POS 0x0001
+#define DISPLAY_CHANGE_CURSOR_STATUS 0x0002
+#define DISPLAY_CHANGE_SCREEN_CUR_LINE 0x0004
+#define DISPLAY_CHANGE_SCREEN_SCR_LINES 0x0008
+#define DISPLAY_CHANGE_BELL_AUDIBLE 0x0010
+#define DISPLAY_CHANGE_BELL_VISIBLE 0x0020
+#define DISPLAY_CHANGE_FLAGS 0x0030
+#define DISPLAY_CHANGE_MATRIX 0x0040
+ unsigned int which;
+};
+
+struct cursor
+{
+ uint32_t saved_x;
+ uint32_t saved_y;
+};
+typedef struct cursor *cursor_t;
+
+struct scrolling_region
+{
+ uint32_t top;
+ uint32_t bottom;
+};
+
+struct parse
+{
+ /* The parsing state of output characters, needed to handle escape
+ character sequences. */
+ enum
+ {
+ STATE_NORMAL = 0,
+ /* An escape character has just been parsed. */
+ STATE_ESC,
+ STATE_ESC_BRACKET_INIT,
+ STATE_ESC_BRACKET,
+ STATE_ESC_BRACKET_QUESTION,
+ STATE_ESC_BRACKET_RIGHT_ANGLE
+ } state;
+
+ /* How many parameters an escape sequence may have. */
+#define PARSE_MAX_PARAMS 10
+ int params[PARSE_MAX_PARAMS];
+ int nparams;
+};
+typedef struct parse *parse_t;
+
+struct output
+{
+ /* The state of the conversion of output characters. */
+ iconv_t cd;
+ /* The output queue holds the characters that are to be outputted.
+ The conversion routine might refuse to handle some incomplete
+ multi-byte or composed character at the end of the buffer, so we
+ have to keep them around. */
+ int stopped;
+ pthread_cond_t resumed;
+ char *buffer;
+ size_t allocated;
+ size_t size;
+
+ /* The parsing state of output characters. */
+ struct parse parse;
+};
+typedef struct output *output_t;
+
+struct attr
+{
+ conchar_attr_t attr_def;
+ conchar_attr_t current;
+ /* True if in alternate character set (ASCII graphic) mode. */
+ unsigned int altchar;
+};
+typedef struct attr *attr_t;
+
+/* Pending directory and file modification requests. */
+struct modreq
+{
+ mach_port_t port;
+ struct modreq *next;
+ /* If the port should have been notified, but it was blocking, we
+ set this. */
+ int pending;
+};
+
+/* For each display, a notification port is created to which the
+ kernel sends message accepted notifications. */
+struct notify
+{
+ struct port_info pi;
+ struct display *display;
+};
+
+struct display
+{
+ /* The lock for the virtual console display structure. */
+ pthread_mutex_t lock;
+
+ /* Indicates if OWNER_ID is initialized. */
+ int has_owner;
+ /* Specifies the ID of the process that should receive the WINCH
+ signal for this virtual console. */
+ int owner_id;
+
+ /* The pending changes. */
+ struct changes changes;
+
+ /* The state of the virtual console. */
+ /* The saved cursor position. */
+ struct cursor cursor;
+ /* The output queue and parser state. */
+ struct output output;
+ /* The current video attributes. */
+ struct attr attr;
+ /* Non-zero if we are in insert mode. */
+ int insert_mode;
+ /* Scrolling region. */
+ struct scrolling_region csr;
+
+ struct cons_display *user;
+
+ /* The pager for the USER member. */
+ struct user_pager user_pager;
+
+ /* A list of ports to send file change notifications to. */
+ struct modreq *filemod_reqs;
+ /* Those ports which currently have a pending notification. */
+ struct modreq *filemod_reqs_pending;
+ /* The notify port. */
+ struct notify *notify_port;
+};
+
+
+/* The bucket and class for notification messages. */
+static struct port_bucket *notify_bucket;
+static struct port_class *notify_class;
+
+#define msgh_request_port msgh_remote_port
+#define msgh_reply_port msgh_local_port
+
+/* SimpleRoutine file_changed */
+kern_return_t
+nowait_file_changed (mach_port_t notify_port, natural_t tickno,
+ file_changed_type_t change,
+ off_t start, off_t end, mach_port_t notify)
+{
+ typedef struct
+ {
+ mach_msg_header_t Head;
+ mach_msg_type_t ticknoType;
+ natural_t tickno;
+ mach_msg_type_t changeType;
+ file_changed_type_t change;
+ mach_msg_type_t startType;
+ loff_t start;
+ mach_msg_type_t endType;
+ loff_t end;
+ } Request;
+ union
+ {
+ Request In;
+ } Mess;
+ register Request *InP = &Mess.In;
+
+ static const mach_msg_type_t ticknoType = {
+ /* msgt_name = */ 2,
+ /* msgt_size = */ 32,
+ /* msgt_number = */ 1,
+ /* msgt_inline = */ TRUE,
+ /* msgt_longform = */ FALSE,
+ /* msgt_deallocate = */ FALSE,
+ /* msgt_unused = */ 0
+ };
+
+ static const mach_msg_type_t changeType = {
+ /* msgt_name = */ 2,
+ /* msgt_size = */ 32,
+ /* msgt_number = */ 1,
+ /* msgt_inline = */ TRUE,
+ /* msgt_longform = */ FALSE,
+ /* msgt_deallocate = */ FALSE,
+ /* msgt_unused = */ 0
+ };
+
+ static const mach_msg_type_t startType = {
+ /* msgt_name = */ 11,
+ /* msgt_size = */ 64,
+ /* msgt_number = */ 1,
+ /* msgt_inline = */ TRUE,
+ /* msgt_longform = */ FALSE,
+ /* msgt_deallocate = */ FALSE,
+ /* msgt_unused = */ 0
+ };
+
+ static const mach_msg_type_t endType = {
+ /* msgt_name = */ 11,
+ /* msgt_size = */ 64,
+ /* msgt_number = */ 1,
+ /* msgt_inline = */ TRUE,
+ /* msgt_longform = */ FALSE,
+ /* msgt_deallocate = */ FALSE,
+ /* msgt_unused = */ 0
+ };
+
+ InP->ticknoType = ticknoType;
+ InP->tickno = tickno;
+ InP->changeType = changeType;
+ InP->change = change;
+ InP->startType = startType;
+ InP->start = start;
+ InP->endType = endType;
+ InP->end = end;
+
+ InP->Head.msgh_bits = MACH_MSGH_BITS(19, 0);
+ /* msgh_size passed as argument. */
+ InP->Head.msgh_request_port = notify_port;
+ InP->Head.msgh_reply_port = MACH_PORT_NULL;
+ InP->Head.msgh_seqno = 0;
+ InP->Head.msgh_id = 20501;
+
+ if (notify == MACH_PORT_NULL)
+ return mach_msg (&InP->Head, MACH_SEND_MSG | MACH_MSG_OPTION_NONE,
+ 64, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ else
+ return mach_msg (&InP->Head, MACH_SEND_MSG | MACH_SEND_NOTIFY,
+ 64, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
+ notify);
+}
+
+/* Free the list of modification requests MR */
+static void
+free_modreqs (struct modreq *mr)
+{
+ struct modreq *tmp;
+ for (; mr; mr = tmp)
+ {
+ mach_port_t old;
+ /* Cancel the dead-name notification. */
+ mach_port_request_notification (mach_task_self (), mr->port,
+ MACH_NOTIFY_DEAD_NAME, 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &old);
+ mach_port_deallocate (mach_task_self (), old);
+
+ /* Deallocate the user's port. */
+ mach_port_deallocate (mach_task_self (), mr->port);
+ tmp = mr->next;
+ free (mr);
+ }
+}
+
+/* A port deleted notification is generated when we deallocate the
+ user's notify port before it is dead. */
+error_t
+do_mach_notify_port_deleted (struct port_info *pi, mach_port_t name)
+{
+ /* As we cancel the dead-name notification before deallocating the
+ port, this should not happen. */
+ assert (0);
+}
+
+/* We request dead name notifications for the user ports. */
+error_t
+do_mach_notify_dead_name (struct port_info *pi, mach_port_t dead_name)
+{
+ struct notify *notify_port = (struct notify *) pi;
+ struct display *display;
+ struct modreq **preq;
+ struct modreq *req;
+
+ if (!notify_port
+ || notify_port->pi.bucket != notify_bucket
+ || notify_port->pi.class != notify_class)
+ return EOPNOTSUPP;
+
+ display = notify_port->display;
+ pthread_mutex_lock (&display->lock);
+
+ /* Find request in pending queue. */
+ preq = &display->filemod_reqs_pending;
+ while (*preq && (*preq)->port != dead_name)
+ preq = &(*preq)->next;
+ if (! *preq)
+ {
+ /* Find request in queue. */
+ preq = &display->filemod_reqs;
+ while (*preq && (*preq)->port != dead_name)
+ preq = &(*preq)->next;
+ }
+
+ if (*preq)
+ {
+ req = *preq;
+ *preq = req->next;
+
+ mach_port_deallocate (mach_task_self (), req->port);
+ free (req);
+ }
+ pthread_mutex_unlock (&display->lock);
+
+ /* Drop gratuitous extra reference that the notification creates. */
+ mach_port_deallocate (mach_task_self (), dead_name);
+
+ return 0;
+}
+
+error_t
+do_mach_notify_port_destroyed (struct port_info *pi, mach_port_t rights)
+{
+ assert (0);
+}
+
+error_t
+do_mach_notify_no_senders (struct port_info *pi, mach_port_mscount_t count)
+{
+ return ports_do_mach_notify_no_senders (pi, count);
+}
+
+kern_return_t
+do_mach_notify_send_once (struct port_info *pi)
+{
+ return 0;
+}
+
+kern_return_t
+do_mach_notify_msg_accepted (struct port_info *pi, mach_port_t send)
+{
+ struct notify *notify_port = (struct notify *) pi;
+ struct display *display;
+ struct modreq **preq;
+ struct modreq *req;
+
+ if (!notify_port
+ || notify_port->pi.bucket != notify_bucket
+ || notify_port->pi.class != notify_class)
+ return EOPNOTSUPP;
+
+ /* If we deallocated the send right in display_destroy before the
+ notification was created. We have nothing to do in this
+ case. */
+ if (!send)
+ {
+ assert(0);
+ return 0;
+ }
+
+ display = notify_port->display;
+ pthread_mutex_lock (&display->lock);
+ /* Find request in pending queue. */
+ preq = &display->filemod_reqs_pending;
+ while (*preq && (*preq)->port != send)
+ preq = &(*preq)->next;
+ /* If we don't find the request, it was destroyed in
+ display_destroy. In this case, there is nothing left to do
+ here. */
+ if (! *preq)
+ {
+ assert(0);
+ pthread_mutex_unlock (&display->lock);
+ return 0;
+ }
+ req = *preq;
+
+ if (req->pending)
+ {
+ error_t err;
+ /* A request was desired while we were blocking. Send it now
+ and stay in pending queue. */
+ req->pending = 0;
+ err = nowait_file_changed (req->port, 0, FILE_CHANGED_WRITE, -1, -1,
+ notify_port->pi.port_right);
+ if (err && err != MACH_SEND_WILL_NOTIFY)
+ {
+ mach_port_t old;
+ *preq = req->next;
+ pthread_mutex_unlock (&display->lock);
+
+ /* Cancel the dead-name notification. */
+ mach_port_request_notification (mach_task_self (), req->port,
+ MACH_NOTIFY_DEAD_NAME, 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &old);
+ mach_port_deallocate (mach_task_self (), old);
+
+ mach_port_deallocate (mach_task_self (), req->port);
+ free (req);
+ return err;
+ }
+ if (err == MACH_SEND_WILL_NOTIFY)
+ {
+ pthread_mutex_unlock (&display->lock);
+ return 0;
+ }
+ /* The message was successfully queued, fall through. */
+ }
+ /* Remove request from pending queue. */
+ *preq = req->next;
+ /* Insert request into active queue. */
+ req->next = display->filemod_reqs;
+ display->filemod_reqs = req;
+ pthread_mutex_unlock (&display->lock);
+ return 0;
+}
+
+/* A top-level function for the notification thread that just services
+ notification messages. */
+static void *
+service_notifications (void *arg)
+{
+ struct port_bucket *notify_bucket = arg;
+ extern int notify_server (mach_msg_header_t *inp, mach_msg_header_t *outp);
+
+ for (;;)
+ ports_manage_port_operations_one_thread (notify_bucket,
+ notify_server,
+ 1000 * 60 * 10);
+ return NULL;
+}
+
+error_t
+display_notice_changes (display_t display, mach_port_t notify)
+{
+ error_t err;
+ struct modreq *req;
+ mach_port_t notify_port;
+ mach_port_t old;
+
+ pthread_mutex_lock (&display->lock);
+ err = nowait_file_changed (notify, 0, FILE_CHANGED_NULL, 0, 0,
+ MACH_PORT_NULL);
+ if (err)
+ {
+ pthread_mutex_unlock (&display->lock);
+ return err;
+ }
+
+ req = malloc (sizeof (struct modreq));
+ if (!req)
+ {
+ pthread_mutex_unlock (&display->lock);
+ return errno;
+ }
+
+ notify_port = ports_get_right (display->notify_port);
+
+ /* Request dead-name notification for the user's port. */
+ err = mach_port_request_notification (mach_task_self (), notify,
+ MACH_NOTIFY_DEAD_NAME, 0,
+ notify_port,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &old);
+ if (err)
+ {
+ free (req);
+ pthread_mutex_unlock (&display->lock);
+ return err;
+ }
+ assert (old == MACH_PORT_NULL);
+
+ req->port = notify;
+ req->pending = 0;
+ req->next = display->filemod_reqs;
+ display->filemod_reqs = req;
+ pthread_mutex_unlock (&display->lock);
+ return 0;
+}
+
+/* Requires DISPLAY to be locked. */
+static void
+display_notice_filechange (display_t display)
+{
+ error_t err;
+ struct modreq *req = display->filemod_reqs_pending;
+ struct modreq **preq = &display->filemod_reqs;
+ mach_port_t notify_port = ports_get_right (display->notify_port);
+
+ while (req)
+ {
+ req->pending = 1;
+ req = req->next;
+ }
+
+ while (*preq)
+ {
+ req = *preq;
+
+ err = nowait_file_changed (req->port, 0, FILE_CHANGED_WRITE, -1, -1,
+ notify_port);
+ if (err)
+ {
+ /* Remove notify port. */
+ *preq = req->next;
+
+ if (err == MACH_SEND_WILL_NOTIFY)
+ {
+ req->next = display->filemod_reqs_pending;
+ display->filemod_reqs_pending = req;
+ }
+ else
+ {
+ mach_port_t old;
+
+ /* Cancel the dead-name notification. */
+ mach_port_request_notification (mach_task_self (), req->port,
+ MACH_NOTIFY_DEAD_NAME, 0,
+ MACH_PORT_NULL, 0, &old);
+ mach_port_deallocate (mach_task_self (), old);
+ mach_port_deallocate (mach_task_self (), req->port);
+ free (req);
+ }
+ }
+ else
+ preq = &req->next;
+ }
+}
+
+static void
+display_flush_filechange (display_t display, unsigned int type)
+{
+ struct cons_display *user = display->user;
+ cons_change_t *next = &user->changes._buffer[user->changes.written
+ % _CONS_CHANGES_LENGTH];
+ int notify = 0;
+ int bump_written = 0;
+
+ if (type & DISPLAY_CHANGE_MATRIX
+ && display->changes.which & DISPLAY_CHANGE_MATRIX)
+ {
+ notify = 1;
+ next->matrix.start = display->changes.start;
+ next->matrix.end = display->changes.end;
+ user->changes.written++;
+ next = &user->changes._buffer[user->changes.written
+ % _CONS_CHANGES_LENGTH];
+ display->changes.which &= ~DISPLAY_CHANGE_MATRIX;
+ }
+
+ memset (next, 0, sizeof (cons_change_t));
+ next->what.not_matrix = 1;
+
+ if (type & DISPLAY_CHANGE_CURSOR_POS
+ && display->changes.which & DISPLAY_CHANGE_CURSOR_POS
+ && (display->changes.cursor.col != user->cursor.col
+ || display->changes.cursor.row != user->cursor.row))
+ {
+ notify = 1;
+ next->what.cursor_pos = 1;
+ bump_written = 1;
+ display->changes.which &= ~DISPLAY_CHANGE_CURSOR_POS;
+ }
+
+ if (type & DISPLAY_CHANGE_CURSOR_STATUS
+ && display->changes.which & DISPLAY_CHANGE_CURSOR_STATUS
+ && display->changes.cursor.status != user->cursor.status)
+ {
+ notify = 1;
+ next->what.cursor_status = 1;
+ bump_written = 1;
+ display->changes.which &= ~DISPLAY_CHANGE_CURSOR_STATUS;
+ }
+
+ if (type & DISPLAY_CHANGE_SCREEN_CUR_LINE
+ && display->changes.which & DISPLAY_CHANGE_SCREEN_CUR_LINE
+ && display->changes.screen.cur_line != user->screen.cur_line)
+ {
+ notify = 1;
+ next->what.screen_cur_line = 1;
+ bump_written = 1;
+ display->changes.which &= ~DISPLAY_CHANGE_SCREEN_CUR_LINE;
+ }
+
+ if (type & DISPLAY_CHANGE_SCREEN_SCR_LINES
+ && display->changes.which & DISPLAY_CHANGE_SCREEN_SCR_LINES
+ && display->changes.screen.scr_lines != user->screen.scr_lines)
+ {
+ notify = 1;
+ next->what.screen_scr_lines = 1;
+ bump_written = 1;
+ display->changes.which &= ~DISPLAY_CHANGE_SCREEN_SCR_LINES;
+ }
+
+ if (type & DISPLAY_CHANGE_BELL_AUDIBLE
+ && display->changes.which & DISPLAY_CHANGE_BELL_AUDIBLE
+ && display->changes.bell_audible != user->bell.audible)
+ {
+ notify = 1;
+ next->what.bell_audible = 1;
+ bump_written = 1;
+ display->changes.which &= ~DISPLAY_CHANGE_BELL_AUDIBLE;
+ }
+
+ if (type & DISPLAY_CHANGE_BELL_VISIBLE
+ && display->changes.which & DISPLAY_CHANGE_BELL_VISIBLE
+ && display->changes.bell_visible != user->bell.visible)
+ {
+ notify = 1;
+ next->what.bell_visible = 1;
+ bump_written = 1;
+ display->changes.which &= ~DISPLAY_CHANGE_BELL_VISIBLE;
+ }
+
+ if (type & DISPLAY_CHANGE_FLAGS
+ && display->changes.which & DISPLAY_CHANGE_FLAGS
+ && display->changes.flags != user->flags)
+ {
+ notify = 1;
+ next->what.flags = 1;
+ bump_written = 1;
+ display->changes.which &= ~DISPLAY_CHANGE_FLAGS;
+ }
+
+ if (bump_written)
+ user->changes.written++;
+ if (notify)
+ display_notice_filechange (display);
+}
+
+/* Record a change in the matrix ringbuffer. */
+static void
+display_record_filechange (display_t display, off_t start, off_t end)
+{
+ if (!(display->changes.which & DISPLAY_CHANGE_MATRIX))
+ {
+ display->changes.start = start;
+ display->changes.end = end;
+ display->changes.which |= DISPLAY_CHANGE_MATRIX;
+ }
+ else
+ {
+ off_t size = display->user->screen.width * display->user->screen.lines;
+ off_t rotate = display->changes.start;
+ off_t old_end = display->changes.end;
+ int disjunct = 0;
+
+ /* First rotate the buffer to reduce the number of cases. */
+ old_end -= rotate;
+ if (old_end < 0)
+ old_end += size;
+ start -= rotate;
+ if (start < 0)
+ start += size;
+ end -= rotate;
+ if (end < 0)
+ end += size;
+
+ /* Now the old region starts at 0 and ends at OLD_END. Try to
+ merge in the new region if it overlaps or touches the old
+ one. */
+ if (start <= end)
+ {
+ if (start <= old_end + 1)
+ {
+ start = 0;
+ if (old_end > end)
+ end = old_end;
+ }
+ else
+ {
+ if (end == size - 1)
+ end = old_end;
+ else
+ disjunct = 1;
+ }
+ }
+ else
+ {
+ if (start <= old_end + 1)
+ {
+ start = 0;
+ end = size - 1;
+ }
+ else
+ {
+ if (old_end > end)
+ end = old_end;
+ }
+ }
+ /* Now reverse the rotation. */
+ start += rotate;
+ if (start >= size)
+ start -= size;
+ end += rotate;
+ if (end >= size)
+ end -= size;
+
+ if (disjunct)
+ {
+ /* The regions are disjunct, so we have to flush the old
+ changes. */
+ display_flush_filechange (display, DISPLAY_CHANGE_MATRIX);
+ display->changes.which |= DISPLAY_CHANGE_MATRIX;
+ }
+ display->changes.start = start;
+ display->changes.end = end;
+ }
+}
+
+
+static void
+conchar_memset (conchar_t *conchar, wchar_t chr, conchar_attr_t attr,
+ size_t size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ {
+ conchar->chr = chr;
+ conchar->attr = attr;
+ conchar++;
+ }
+}
+
+
+static error_t
+user_create (display_t display, uint32_t width, uint32_t height,
+ uint32_t lines, wchar_t chr, conchar_attr_t attr)
+{
+ error_t err;
+ struct cons_display *user;
+ int npages = (round_page (sizeof (struct cons_display) + sizeof (conchar_t)
+ * width * lines)) / vm_page_size;
+
+ err = user_pager_create (&display->user_pager, npages, &display->user);
+ if (err)
+ return err;
+
+ user = display->user;
+ user->magic = CONS_MAGIC;
+ user->version = CONS_VERSION_MAJ << CONS_VERSION_MAJ_SHIFT | CONS_VERSION_AGE;
+ user->changes.buffer = offsetof (struct cons_display, changes._buffer)
+ / sizeof (uint32_t);
+ user->changes.length = _CONS_CHANGES_LENGTH;
+ user->screen.width = width;
+ user->screen.height = height;
+ user->screen.lines = lines;
+ user->screen.cur_line = 0;
+ user->screen.scr_lines = 0;
+ user->screen.matrix = sizeof (struct cons_display) / sizeof (uint32_t);
+ user->cursor.col = 0;
+ user->cursor.row = 0;
+ user->cursor.status = CONS_CURSOR_NORMAL;
+ conchar_memset (user->_matrix, chr, attr,
+ user->screen.width * user->screen.lines);
+ return 0;
+}
+
+static void
+user_destroy (display_t display)
+{
+ user_pager_destroy (&display->user_pager, display->user);
+}
+
+
+static void
+screen_fill (display_t display, size_t col1, size_t row1, size_t col2,
+ size_t row2, wchar_t chr, conchar_attr_t attr)
+{
+ struct cons_display *user = display->user;
+ off_t start = ((user->screen.cur_line % user->screen.lines) + row1)
+ * user->screen.width + col1;
+ off_t end = ((user->screen.cur_line % user->screen.lines) + row2)
+ * user->screen.width + col2;
+ off_t size = user->screen.width * user->screen.lines;
+
+ if (start >= size && end >= size)
+ {
+ start -= size;
+ end -= size;
+ }
+
+ if (end < size)
+ {
+ conchar_memset (user->_matrix + start, chr, attr, end - start + 1);
+ display_record_filechange (display, start, end);
+ }
+ else
+ {
+ conchar_memset (user->_matrix + start, chr, attr, size - start);
+ conchar_memset (user->_matrix, chr, attr, end - size + 1);
+ display_record_filechange (display, start, end - size);
+ }
+}
+
+static void
+screen_shift_left (display_t display, size_t col1, size_t row1, size_t col2,
+ size_t row2, size_t shift, wchar_t chr, conchar_attr_t attr)
+{
+ struct cons_display *user = display->user;
+ off_t start = ((user->screen.cur_line % user->screen.lines) + row1)
+ * user->screen.width + col1;
+ off_t end = ((user->screen.cur_line % user->screen.lines) + row2)
+ * user->screen.width + col2;
+ off_t size = user->screen.width * user->screen.lines;
+
+ if (start >= size && end >= size)
+ {
+ start -= size;
+ end -= size;
+ }
+
+ if (start + shift <= end)
+ {
+ /* Use a loop to copy the data. Using wmemmove and wmemset on
+ the chunks is tiresome, as there are many cases. */
+ off_t src = start + shift;
+ off_t dst = start;
+
+ while (src <= end)
+ user->_matrix[dst++ % size] = user->_matrix[src++ % size];
+ while (dst <= end)
+ {
+ user->_matrix[dst % size].chr = chr;
+ user->_matrix[dst++ % size].attr = attr;
+ }
+
+ display_record_filechange (display, start, end);
+ }
+ else
+ screen_fill (display, col1, row1, col2, row2, chr, attr);
+}
+
+static void
+screen_shift_right (display_t display, size_t col1, size_t row1, size_t col2,
+ size_t row2, size_t shift,
+ wchar_t chr, conchar_attr_t attr)
+{
+ struct cons_display *user = display->user;
+ off_t start = ((user->screen.cur_line % user->screen.lines) + row1)
+ * user->screen.width + col1;
+ off_t end = ((user->screen.cur_line % user->screen.lines) + row2)
+ * user->screen.width + col2;
+ off_t size = user->screen.width * user->screen.lines;
+
+ if (start >= size && end >= size)
+ {
+ start -= size;
+ end -= size;
+ }
+
+ if (start + shift <= end)
+ {
+ /* Use a loop to copy the data. Using wmemmove and wmemset on
+ the chunks is tiresome, as there are many cases. */
+ off_t src = end - shift;
+ off_t dst = end;
+
+ while (src >= start)
+ user->_matrix[dst-- % size] = user->_matrix[src-- % size];
+ while (dst >= start)
+ {
+ user->_matrix[dst % size].chr = chr;
+ user->_matrix[dst-- % size].attr = attr;
+ }
+
+ display_record_filechange (display, start, end);
+ }
+ else
+ screen_fill (display, col1, row1, col2, row2, chr, attr);
+}
+
+
+static error_t
+output_init (output_t output, const char *encoding)
+{
+ pthread_cond_init (&output->resumed, NULL);
+ output->stopped = 0;
+ output->buffer = NULL;
+ output->allocated = 0;
+ output->size = 0;
+
+ /* WCHAR_T happens to be UCS-4 on the GNU system. */
+ output->cd = iconv_open ("WCHAR_T", encoding);
+ if (output->cd == (iconv_t) -1)
+ return errno;
+ return 0;
+}
+
+static void
+output_deinit (output_t output)
+{
+ iconv_close (output->cd);
+}
+
+
+static void
+handle_esc_bracket_hl (display_t display, int code, int flag)
+{
+ switch (code)
+ {
+ case 4: /* ECMA-48 <SMIR>, <RMIR>. */
+ /* Insert mode: <smir>, <rmir>. */
+ display->insert_mode = flag ? 1 : 0;
+ break;
+ case 34:
+ /* Cursor standout: <cnorm>, <cvvis>. */
+ if (flag)
+ display->user->cursor.status = CONS_CURSOR_NORMAL;
+ else
+ display->user->cursor.status = CONS_CURSOR_VERY_VISIBLE;
+ /* XXX Flag cursor status change. */
+ break;
+ }
+}
+
+static void
+handle_esc_bracket_m (attr_t attr, int code)
+{
+ switch (code)
+ {
+ case 0:
+ /* All attributes off: <sgr0>. */
+ attr->current = attr->attr_def;
+ attr->altchar = 0;
+ break;
+ case 1:
+ /* Bold on: <bold>. */
+ attr->current.intensity = CONS_ATTR_INTENSITY_BOLD;
+ break;
+ case 2:
+ /* Dim on: <dim>. */
+ attr->current.intensity = CONS_ATTR_INTENSITY_DIM;
+ break;
+ case 3:
+ /* Italic on: <sitm>. */
+ attr->current.italic = 1;
+ break;
+ case 4:
+ /* Underline on: <smul>. */
+ attr->current.underlined = 1;
+ break;
+ case 5:
+ /* (Slow) blink on: <blink>. */
+ attr->current.blinking = 1;
+ break;
+ case 7:
+ /* Reverse video on: <rev>, <smso>. */
+ attr->current.reversed = 1;
+ break;
+ case 8:
+ /* Concealed on: <invis>. */
+ attr->current.concealed = 1;
+ break;
+ case 10:
+ /* Alternate character set mode off: <rmacs>. */
+ attr->altchar = 0;
+ break;
+ case 11:
+ /* Alternate character set mode on: <smacs>. */
+ attr->altchar = 1;
+ break;
+ case 21:
+ /* Normal intensity (switch off bright). */
+ attr->current.intensity = CONS_ATTR_INTENSITY_NORMAL;
+ break;
+ case 22:
+ /* Normal intensity (switch off dim). */
+ attr->current.intensity = CONS_ATTR_INTENSITY_NORMAL;
+ break;
+ case 23:
+ /* Italic off: <ritm>. */
+ attr->current.italic = 0;
+ break;
+ case 24:
+ /* Underline off: <rmul>. */
+ attr->current.underlined = 0;
+ break;
+ case 25:
+ /* Blink off. */
+ attr->current.blinking = 0;
+ break;
+ case 27:
+ /* Reverse video off: <rmso>. */
+ attr->current.reversed = 0;
+ break;
+ case 28:
+ /* Concealed off. */
+ attr->current.concealed = 0;
+ break;
+ case 30 ... 37:
+ /* Set foreground color: <setaf>. */
+ attr->current.fgcol = code - 30;
+ break;
+ case 39:
+ /* Default foreground color; ANSI?. */
+ attr->current.fgcol = attr->attr_def.fgcol;
+ break;
+ case 40 ... 47:
+ /* Set background color: <setab>. */
+ attr->current.bgcol = code - 40;
+ break;
+ case 49:
+ /* Default background color; ANSI?. */
+ attr->current.bgcol = attr->attr_def.bgcol;
+ break;
+ }
+}
+
+static
+void limit_cursor (display_t display)
+{
+ struct cons_display *user = display->user;
+
+ if (user->cursor.col >= user->screen.width)
+ user->cursor.col = user->screen.width - 1;
+ else if (user->cursor.col < 0)
+ user->cursor.col = 0;
+
+ if (user->cursor.row >= user->screen.height)
+ user->cursor.row = user->screen.height - 1;
+ else if (user->cursor.row < 0)
+ user->cursor.row = 0;
+
+ /* XXX Flag cursor change. */
+}
+
+
+static void
+linefeed (display_t display)
+{
+ struct cons_display *user = display->user;
+
+ if (display->csr.top == 0 && display->csr.bottom >= user->screen.height - 1)
+ {
+ /* No scrolling region active, do the normal scrolling activity. */
+ if (user->cursor.row < user->screen.height - 1)
+ {
+ user->cursor.row++;
+ /* XXX Flag cursor update. */
+ }
+ else
+ {
+ user->screen.cur_line++;
+
+ screen_fill (display, 0, user->screen.height - 1,
+ user->screen.width - 1, user->screen.height - 1,
+ L' ', display->attr.current);
+ if (user->screen.scr_lines <
+ user->screen.lines - user->screen.height)
+ user->screen.scr_lines++;
+ /* XXX Flag current line change. */
+ /* XXX Possibly flag change of length of scroll back buffer. */
+ }
+ }
+ else
+ {
+ /* With an active scrolling region, never actually scroll. Just
+ shift the scrolling region if necessary. */
+ if (user->cursor.row != display->csr.bottom
+ && user->cursor.row < user->screen.height - 1)
+ {
+ user->cursor.row++;
+ /* XXX Flag cursor update. */
+ }
+ else if (user->cursor.row == display->csr.bottom)
+ screen_shift_left (display, 0, display->csr.top,
+ user->screen.width - 1, display->csr.bottom,
+ user->screen.width,
+ L' ', display->attr.current);
+ }
+}
+
+static void
+horizontal_tab (display_t display)
+{
+ struct cons_display *user = display->user;
+
+ user->cursor.col = (user->cursor.col | 7) + 1;
+ if (user->cursor.col >= user->screen.width)
+ {
+ user->cursor.col = 0;
+ linefeed (display);
+ }
+ /* XXX Flag cursor update. */
+}
+
+static void
+handle_esc_bracket (display_t display, char op)
+{
+ struct cons_display *user = display->user;
+ parse_t parse = &display->output.parse;
+ int i;
+
+ switch (op)
+ {
+ case 'H': /* ECMA-48 <CUP>. */
+ case 'f': /* ECMA-48 <HVP>. */
+ /* Cursor position: <cup>. */
+ user->cursor.col = (parse->params[1] ?: 1) - 1;
+ user->cursor.row = (parse->params[0] ?: 1) - 1;
+ limit_cursor (display);
+ break;
+ case 'G': /* ECMA-48 <CHA>. */
+ case '`': /* ECMA-48 <HPA>. */
+ case '\'': /* VT100. */
+ /* Horizontal cursor position: <hpa>. */
+ user->cursor.col = (parse->params[0] ?: 1) - 1;
+ limit_cursor (display);
+ break;
+ case 'a': /* ECMA-48 <HPR>. */
+ /* Horizontal cursor position relative. */
+ user->cursor.col += (parse->params[1] ?: 1) - 1;
+ limit_cursor (display);
+ break;
+ case 'd': /* ECMA-48 <VPA>. */
+ /* Vertical cursor position: <vpa>. */
+ user->cursor.row = (parse->params[0] ?: 1) - 1;
+ limit_cursor (display);
+ break;
+ case 'F': /* ECMA-48 <CPL>. */
+ /* Beginning of previous line. */
+ user->cursor.col = 0;
+ /* Fall through. */
+ case 'A': /* ECMA-48 <CUU>. */
+ case 'k': /* ECMA-48 <VPB>. */
+ /* Cursor up: <cuu>, <cuu1>. */
+ user->cursor.row -= (parse->params[0] ?: 1);
+ limit_cursor (display);
+ break;
+ case 'E': /* ECMA-48 <CNL>. */
+ /* Beginning of next line. */
+ user->cursor.col = 0;
+ /* Fall through. */
+ case 'B': /* ECMA-48 <CUD>. */
+ case 'e': /* ECMA-48 <VPR>. */
+ /* Cursor down: <cud1>, <cud>. */
+ /* Most implementations scroll the screen. */
+ for (i = 0; i < (parse->params[0] ?: 1); i++)
+ linefeed (display);
+ break;
+ case 'C': /* ECMA-48 <CUF>. */
+ /* Cursor right: <cuf1>, <cuf>. */
+ user->cursor.col += (parse->params[0] ?: 1);
+ limit_cursor (display);
+ break;
+ case 'D': /* ECMA-48 <CUB>. */
+ /* Cursor left: <cub>, <cub1>. */
+ user->cursor.col -= (parse->params[0] ?: 1);
+ limit_cursor (display);
+ break;
+ case 'l':
+ /* Reset mode. */
+ for (i = 0; i < parse->nparams; i++)
+ handle_esc_bracket_hl (display, parse->params[i], 0);
+ break;
+ case 'h':
+ /* Set mode. */
+ for (i = 0; i < parse->nparams; i++)
+ handle_esc_bracket_hl (display, parse->params[i], 1);
+ break;
+ case 'm': /* ECMA-48 <SGR>. */
+ for (i = 0; i < parse->nparams; i++)
+ handle_esc_bracket_m (&display->attr, parse->params[i]);
+ break;
+ case 'J': /* ECMA-48 <ED>. */
+ switch (parse->params[0])
+ {
+ case 0:
+ /* Clear to end of screen: <ed>. */
+ screen_fill (display, user->cursor.col, user->cursor.row,
+ user->screen.width - 1, user->screen.height - 1,
+ L' ', display->attr.current);
+ break;
+ case 1:
+ /* Clear to beginning of screen. */
+ screen_fill (display, 0, 0,
+ user->cursor.col, user->cursor.row,
+ L' ', display->attr.current);
+ break;
+ case 2:
+ /* Clear entire screen. */
+ screen_fill (display, 0, 0,
+ user->screen.width - 1, user->screen.height - 1,
+ L' ', display->attr.current);
+ break;
+ }
+ break;
+ case 'K': /* ECMA-48 <EL>. */
+ switch (parse->params[0])
+ {
+ case 0:
+ /* Clear to end of line: <el>. */
+ screen_fill (display, user->cursor.col, user->cursor.row,
+ user->screen.width - 1, user->cursor.row,
+ L' ', display->attr.current);
+ break;
+ case 1:
+ /* Clear to beginning of line: <el1>. */
+ screen_fill (display, 0, user->cursor.row,
+ user->cursor.col, user->cursor.row,
+ L' ', display->attr.current);
+ break;
+ case 2:
+ /* Clear entire line. */
+ screen_fill (display, 0, user->cursor.row,
+ user->screen.width - 1, user->cursor.row,
+ L' ', display->attr.current);
+ break;
+ }
+ break;
+ case 'L': /* ECMA-48 <IL>. */
+ /* Insert line(s): <il1>, <il>. */
+ screen_shift_right (display, 0, user->cursor.row,
+ user->screen.width - 1,
+ (user->cursor.row <= display->csr.bottom)
+ ? display->csr.bottom : user->screen.height - 1,
+ (parse->params[0] ?: 1) * user->screen.width,
+ L' ', display->attr.current);
+ break;
+ case 'M': /* ECMA-48 <DL>. */
+ /* Delete line(s): <dl1>, <dl>. */
+ screen_shift_left (display, 0, user->cursor.row,
+ user->screen.width - 1,
+ (user->cursor.row <= display->csr.bottom)
+ ? display->csr.bottom : user->screen.height - 1,
+ (parse->params[0] ?: 1) * user->screen.width,
+ L' ', display->attr.current);
+ break;
+ case '@': /* ECMA-48 <ICH>. */
+ /* Insert character(s): <ich1>, <ich>. */
+ screen_shift_right (display, user->cursor.col, user->cursor.row,
+ user->screen.width - 1, user->cursor.row,
+ parse->params[0] ?: 1,
+ L' ', display->attr.current);
+ break;
+ case 'P': /* ECMA-48 <DCH>. */
+ /* Delete character(s): <dch1>, <dch>. */
+ screen_shift_left (display, user->cursor.col, user->cursor.row,
+ user->screen.width - 1, user->cursor.row,
+ parse->params[0] ?: 1,
+ L' ', display->attr.current);
+ break;
+ case 'r': /* VT100: Set scrolling region. */
+ if (!parse->params[1])
+ {
+ display->csr.top = 0;
+ display->csr.bottom = user->screen.height - 1;
+ }
+ else
+ {
+ if (parse->params[1] <= user->screen.height
+ && parse->params[0] < parse->params[1])
+ {
+ display->csr.top = parse->params[0] ? parse->params[0] - 1 : 0;
+ display->csr.bottom = parse->params[1] - 1;
+ user->cursor.col = 0;
+ user->cursor.row = 0;
+ /* XXX Flag cursor change. */
+ }
+ }
+ break;
+ case 'S': /* ECMA-48 <SU>. */
+ /* Scroll up: <ind>, <indn>. */
+ screen_shift_left (display, 0, display->csr.top,
+ user->screen.width - 1, display->csr.bottom,
+ (parse->params[0] ?: 1) * user->screen.width,
+ L' ', display->attr.current);
+ break;
+ case 'T': /* ECMA-48 <SD>. */
+ /* Scroll down: <ri>, <rin>. */
+ screen_shift_right (display, 0, display->csr.top,
+ user->screen.width - 1, display->csr.bottom,
+ (parse->params[0] ?: 1) * user->screen.width,
+ L' ', display->attr.current);
+ break;
+ case 'X': /* ECMA-48 <ECH>. */
+ /* Erase character(s): <ech>. */
+ {
+ int col = user->cursor.col;
+ if (parse->params[0] - 1 > 0)
+ col += parse->params[0] - 1;
+ if (col > user->screen.width - 1)
+ col = user->screen.width - 1;
+
+ screen_fill (display, user->cursor.col, user->cursor.row,
+ col, user->cursor.row, L' ', display->attr.current);
+ }
+ break;
+ case 'I': /* ECMA-48 <CHT>. */
+ /* Horizontal tab. */
+ if (!parse->params[0])
+ parse->params[0] = 1;
+ while (parse->params[0]--)
+ horizontal_tab (display);
+ break;
+ case 'Z': /* ECMA-48 <CBT>. */
+ /* Cursor backward tabulation: <cbt>. */
+ if (parse->params[0] > user->screen.height * (user->screen.width / 8))
+ {
+ user->cursor.col = 0;
+ user->cursor.row = 0;
+ }
+ else
+ {
+ int i = parse->params[0] ?: 1;
+
+ while (i--)
+ {
+ if (user->cursor.col == 0)
+ {
+ if (user->cursor.row == 0)
+ break;
+ else
+ {
+ user->cursor.col = user->screen.width - 1;
+ user->cursor.row--;
+ }
+ }
+ else
+ user->cursor.col--;
+ user->cursor.col &= ~7;
+ }
+ }
+ }
+}
+
+
+static void
+handle_esc_bracket_question_hl (display_t display, int code, int flag)
+{
+ switch (code)
+ {
+ case 25:
+ /* Cursor invisibility: <civis>, <cnorm>. */
+ if (flag)
+ display->user->cursor.status = CONS_CURSOR_NORMAL;
+ else
+ display->user->cursor.status = CONS_CURSOR_INVISIBLE;
+ /* XXX Flag cursor status change. */
+ break;
+ case 1000:
+ /* XTerm mouse tracking. */
+ if (flag)
+ display->user->flags |= CONS_FLAGS_TRACK_MOUSE;
+ else
+ display->user->flags &= ~CONS_FLAGS_TRACK_MOUSE;
+ /* XXX Flag flags change. */
+ break;
+ }
+}
+
+
+static void
+handle_esc_bracket_question (display_t display, char op)
+{
+ parse_t parse = &display->output.parse;
+
+ int i;
+ switch (op)
+ {
+ case 'l':
+ /* Reset mode. */
+ for (i = 0; i < parse->nparams; ++i)
+ handle_esc_bracket_question_hl (display, parse->params[i], 0);
+ break;
+ case 'h':
+ /* Set mode. */
+ for (i = 0; i < parse->nparams; ++i)
+ handle_esc_bracket_question_hl (display, parse->params[i], 1);
+ break;
+ }
+}
+
+
+static void
+handle_esc_bracket_right_angle_hl (display_t display, int code, int flag)
+{
+ switch (code)
+ {
+ case 1:
+ /* Bold: <gsbom>, <grbom>. This is a GNU extension. */
+ if (flag)
+ display->attr.current.bold = 1;
+ else
+ display->attr.current.bold = 0;
+ break;
+ }
+}
+
+
+static void
+handle_esc_bracket_right_angle (display_t display, char op)
+{
+ parse_t parse = &display->output.parse;
+
+ int i;
+ switch (op)
+ {
+ case 'l':
+ /* Reset mode. */
+ for (i = 0; i < parse->nparams; ++i)
+ handle_esc_bracket_right_angle_hl (display, parse->params[i], 0);
+ break;
+ case 'h':
+ /* Set mode. */
+ for (i = 0; i < parse->nparams; ++i)
+ handle_esc_bracket_right_angle_hl (display, parse->params[i], 1);
+ break;
+ }
+}
+
+
+static wchar_t
+altchar_to_ucs4 (wchar_t chr)
+{
+ /* Alternative character set frobbing. */
+ switch (chr)
+ {
+ case L'+':
+ return CONS_CHAR_RARROW;
+ case L',':
+ return CONS_CHAR_LARROW;
+ case L'-':
+ return CONS_CHAR_UARROW;
+ case L'.':
+ return CONS_CHAR_DARROW;
+ case L'0':
+ return CONS_CHAR_BLOCK;
+ case L'I':
+ return CONS_CHAR_LANTERN;
+ case L'`':
+ return CONS_CHAR_DIAMOND;
+ case L'a':
+ return CONS_CHAR_CKBOARD;
+ case L'f':
+ return CONS_CHAR_DEGREE;
+ case L'g':
+ return CONS_CHAR_PLMINUS;
+ case L'h':
+ return CONS_CHAR_BOARD;
+ case L'j':
+ return CONS_CHAR_LRCORNER;
+ case L'k':
+ return CONS_CHAR_URCORNER;
+ case L'l':
+ return CONS_CHAR_ULCORNER;
+ case L'm':
+ return CONS_CHAR_LLCORNER;
+ case L'n':
+ return CONS_CHAR_PLUS;
+ case L'o':
+ return CONS_CHAR_S1;
+ case L'p':
+ return CONS_CHAR_S3;
+ case L'q':
+ return CONS_CHAR_HLINE;
+ case L'r':
+ return CONS_CHAR_S7;
+ case L's':
+ return CONS_CHAR_S9;
+ case L't':
+ return CONS_CHAR_LTEE;
+ case L'u':
+ return CONS_CHAR_RTEE;
+ case L'v':
+ return CONS_CHAR_BTEE;
+ case L'w':
+ return CONS_CHAR_TTEE;
+ case L'x':
+ return CONS_CHAR_VLINE;
+ case L'y':
+ return CONS_CHAR_LEQUAL;
+ case L'z':
+ return CONS_CHAR_GEQUAL;
+ case L'{':
+ return CONS_CHAR_PI;
+ case L'|':
+ return CONS_CHAR_NEQUAL;
+ case L'}':
+ return CONS_CHAR_STERLING;
+ case L'~':
+ return CONS_CHAR_BULLET;
+ default:
+ return chr;
+ }
+}
+
+/* Display must be locked. */
+static void
+display_output_one (display_t display, wchar_t chr)
+{
+ struct cons_display *user = display->user;
+ parse_t parse = &display->output.parse;
+
+ switch (parse->state)
+ {
+ case STATE_NORMAL:
+ switch (chr)
+ {
+ case L'\r':
+ /* Carriage return: <cr>. */
+ if (user->cursor.col)
+ {
+ user->cursor.col = 0;
+ /* XXX Flag cursor update. */
+ }
+ break;
+ case L'\n':
+ /* Line feed. */
+ linefeed (display);
+ break;
+ case L'\b':
+ /* Backspace. */
+ if (user->cursor.col > 0 || user->cursor.row > 0)
+ {
+ if (user->cursor.col > 0)
+ user->cursor.col--;
+ else
+ {
+ /* This implements the <bw> functionality. */
+ user->cursor.col = user->screen.width - 1;
+ user->cursor.row--;
+ }
+ /* XXX Flag cursor update. */
+ }
+ break;
+ case L'\t':
+ /* Horizontal tab: <ht> */
+ horizontal_tab (display);
+ break;
+ case L'\033':
+ parse->state = STATE_ESC;
+ break;
+ case L'\0':
+ /* Padding character: <pad>. */
+ break;
+ case L'\a':
+ /* Audible bell. */
+ user->bell.audible++;
+ break;
+ default:
+ {
+ int line;
+ int idx;
+
+ if (user->cursor.col >= user->screen.width)
+ {
+ user->cursor.col = 0;
+ linefeed (display);
+ }
+
+ line = (user->screen.cur_line + user->cursor.row)
+ % user->screen.lines;
+ idx = line * user->screen.width + user->cursor.col;
+ int width, i;
+
+ width = wcwidth (chr);
+ if (width < 0)
+ width = 1;
+
+ if (display->insert_mode
+ && user->cursor.col < user->screen.width - width)
+ {
+ /* If in insert mode, do the same as <ich1>. */
+ screen_shift_right (display, user->cursor.col,
+ user->cursor.row,
+ user->screen.width - 1, user->cursor.row,
+ width, L' ', display->attr.current);
+ }
+
+ if (display->attr.altchar)
+ chr = altchar_to_ucs4 (chr);
+
+ for (i = 0; i < width; i++)
+ {
+ if (user->cursor.col >= user->screen.width)
+ break;
+ user->_matrix[idx+i].chr = chr;
+ user->_matrix[idx+i].attr = display->attr.current;
+ user->cursor.col++;
+ chr |= CONS_WCHAR_CONTINUED;
+ }
+
+ if (i > 0)
+ display_record_filechange (display, idx, idx + i - 1);
+
+ if (user->cursor.col > user->screen.width)
+ {
+ user->cursor.col = 0;
+ linefeed (display);
+ }
+ }
+ break;
+ }
+ break;
+
+ case STATE_ESC:
+ parse->state = STATE_NORMAL;
+ switch (chr)
+ {
+ case L'[':
+ parse->state = STATE_ESC_BRACKET_INIT;
+ break;
+ case L'M': /* ECMA-48 <RIS>. */
+ /* Reset: <rs2>. */
+ display->attr.current = display->attr.attr_def;
+ display->attr.altchar = 0;
+ display->insert_mode = 0;
+ display->csr.top = 0;
+ display->csr.bottom = user->screen.height - 1;
+ user->cursor.status = CONS_CURSOR_NORMAL;
+ /* Fall through. */
+ case L'c':
+ /* Clear screen and home cursor: <clear>. */
+ screen_fill (display, 0, 0,
+ user->screen.width - 1, user->screen.height - 1,
+ L' ', display->attr.current);
+ user->cursor.col = user->cursor.row = 0;
+ /* XXX Flag cursor change. */
+ break;
+ case L'E': /* ECMA-48 <NEL>. */
+ /* Newline. */
+ user->cursor.col = 0;
+ linefeed (display);
+ break;
+ case L'7': /* VT100: Save cursor and attributes. */
+ /* Save cursor position: <sc>. */
+ display->cursor.saved_x = user->cursor.col;
+ display->cursor.saved_y = user->cursor.row;
+ break;
+ case L'8': /* VT100: Restore cursor and attributes. */
+ /* Restore cursor position: <rc>. */
+ user->cursor.col = display->cursor.saved_x;
+ user->cursor.row = display->cursor.saved_y;
+ /* In case the screen was larger before: */
+ limit_cursor (display);
+ break;
+ case L'g':
+ /* Visible bell. */
+ user->bell.visible++;
+ break;
+ default:
+ /* Unsupported escape sequence. */
+ break;
+ }
+ break;
+
+ case STATE_ESC_BRACKET_INIT:
+ memset (&parse->params, 0, sizeof parse->params);
+ parse->nparams = 0;
+ if (chr == '?')
+ {
+ parse->state = STATE_ESC_BRACKET_QUESTION;
+ break; /* Consume the question mark. */
+ }
+ else if (chr == '>')
+ {
+ parse->state = STATE_ESC_BRACKET_RIGHT_ANGLE;
+ break; /* Consume the right angle. */
+ }
+ else
+ parse->state = STATE_ESC_BRACKET;
+ /* Fall through. */
+ case STATE_ESC_BRACKET:
+ case STATE_ESC_BRACKET_QUESTION:
+ case STATE_ESC_BRACKET_RIGHT_ANGLE:
+ if (chr >= '0' && chr <= '9')
+ parse->params[parse->nparams]
+ = parse->params[parse->nparams]*10 + chr - '0';
+ else if (chr == ';')
+ {
+ if (++(parse->nparams) >= PARSE_MAX_PARAMS)
+ parse->state = STATE_NORMAL; /* too many */
+ }
+ else
+ {
+ parse->nparams++;
+ if (parse->state == STATE_ESC_BRACKET)
+ handle_esc_bracket (display, chr);
+ else if (parse->state == STATE_ESC_BRACKET_RIGHT_ANGLE)
+ handle_esc_bracket_right_angle (display, chr);
+ else
+ handle_esc_bracket_question (display, chr);
+ parse->state = STATE_NORMAL;
+ }
+ break;
+ default:
+ abort ();
+ }
+}
+
+/* Output LENGTH bytes starting from BUFFER in the system encoding.
+ Set BUFFER and LENGTH to the new values. The exact semantics are
+ just as in the iconv interface. */
+static error_t
+display_output_some (display_t display, char **buffer, size_t *length)
+{
+#define CONV_OUTBUF_SIZE 256
+ error_t err = 0;
+
+ display->changes.cursor.col = display->user->cursor.col;
+ display->changes.cursor.row = display->user->cursor.row;
+ display->changes.cursor.status = display->user->cursor.status;
+ display->changes.screen.cur_line = display->user->screen.cur_line;
+ display->changes.screen.scr_lines = display->user->screen.scr_lines;
+ display->changes.bell_audible = display->user->bell.audible;
+ display->changes.bell_visible = display->user->bell.visible;
+ display->changes.flags = display->user->flags;
+ display->changes.which = ~DISPLAY_CHANGE_MATRIX;
+
+ while (!err && *length > 0)
+ {
+ size_t nconv;
+ wchar_t outbuf[CONV_OUTBUF_SIZE];
+ char *outptr = (char *) outbuf;
+ size_t outsize = CONV_OUTBUF_SIZE * sizeof (wchar_t);
+ error_t saved_err;
+ int i;
+
+ nconv = iconv (display->output.cd, buffer, length, &outptr, &outsize);
+ saved_err = errno;
+
+ /* First process all successfully converted characters. */
+ for (i = 0; i < CONV_OUTBUF_SIZE - outsize / sizeof (wchar_t); i++)
+ display_output_one (display, outbuf[i]);
+
+ if (nconv == (size_t) -1)
+ {
+ /* Conversion is not completed, look for recoverable
+ errors. */
+#define UNICODE_REPLACEMENT_CHARACTER ((wchar_t) 0xfffd)
+ if (saved_err == EILSEQ)
+ {
+ assert (*length);
+ (*length)--;
+ (*buffer)++;
+ display_output_one (display, UNICODE_REPLACEMENT_CHARACTER);
+ }
+ else if (saved_err == EINVAL)
+ /* This is only an unfinished byte sequence at the end of
+ the input buffer. */
+ break;
+ else if (saved_err != E2BIG)
+ err = saved_err;
+ }
+ }
+
+ display_flush_filechange (display, ~0);
+ return err;
+}
+
+
+/* Forward declaration. */
+void display_destroy_complete (void *pi);
+
+void
+display_init (void)
+{
+ pthread_t thread;
+ error_t err;
+
+ user_pager_init ();
+
+ /* Create the notify bucket, and start to serve notifications. */
+ notify_bucket = ports_create_bucket ();
+ if (! notify_bucket)
+ error (5, errno, "Cannot create notify bucket");
+ notify_class = ports_create_class (display_destroy_complete, NULL);
+ if (! notify_class)
+ error (5, errno, "Cannot create notify class");
+
+ err = pthread_create (&thread, NULL, service_notifications, notify_bucket);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+}
+
+
+/* Create a new virtual console display, with the system encoding
+ being ENCODING. */
+error_t
+display_create (display_t *r_display, const char *encoding,
+ conchar_attr_t def_attr, unsigned int lines,
+ unsigned int width, unsigned int height)
+{
+ error_t err = 0;
+ display_t display;
+
+ *r_display = NULL;
+ display = calloc (1, sizeof *display);
+ if (!display)
+ return ENOMEM;
+
+ err = ports_create_port (notify_class, notify_bucket, sizeof (struct notify),
+ &display->notify_port);
+ if (err)
+ {
+ free (display);
+ return err;
+ }
+ display->notify_port->display = display;
+
+ pthread_mutex_init (&display->lock, NULL);
+ display->attr.attr_def = def_attr;
+ display->attr.current = display->attr.attr_def;
+ display->csr.bottom = height - 1;
+
+ err = user_create (display, width, height, lines, L' ',
+ display->attr.current);
+ if (err)
+ {
+ ports_destroy_right (display->notify_port);
+ free (display);
+ return err;
+ }
+
+ err = output_init (&display->output, encoding);
+ if (err)
+ {
+ user_destroy (display);
+ ports_destroy_right (display->notify_port);
+ free (display);
+ }
+ *r_display = display;
+ return err;
+}
+
+
+/* Destroy the display DISPLAY. */
+void
+display_destroy (display_t display)
+{
+ pthread_mutex_lock (&display->lock);
+ if (display->filemod_reqs_pending)
+ {
+ free_modreqs (display->filemod_reqs_pending);
+ display->filemod_reqs_pending = NULL;
+ }
+ if (display->filemod_reqs)
+ {
+ free_modreqs (display->filemod_reqs);
+ display->filemod_reqs = NULL;
+ }
+ ports_destroy_right (display->notify_port);
+ output_deinit (&display->output);
+ user_destroy (display);
+ pthread_mutex_unlock (&display->lock);
+
+ /* We can not free the display structure here, because it might
+ still be needed by pending modification requests when msg
+ accepted notifications are handled. So we have to wait until all
+ notifications have arrived and the notify port is completely
+ deallocated, which will invoke display_destroy_complete
+ below. */
+}
+
+
+/* Complete destruction of the display DISPLAY. */
+void
+display_destroy_complete (void *pi)
+{
+ struct display *display = ((struct notify *) pi)->display;
+ free (display);
+}
+
+
+/* Return the dimension of the display in bytes. */
+off_t
+display_get_size (display_t display)
+{
+ return sizeof (struct cons_display)
+ + (sizeof (conchar_t) * display->user->screen.width
+ * display->user->screen.lines);
+}
+
+
+/* Return the dimensions of the display DISPLAY in *WINSIZE. */
+void
+display_getsize (display_t display, struct winsize *winsize)
+{
+ pthread_mutex_lock (&display->lock);
+ winsize->ws_row = display->user->screen.height;
+ winsize->ws_col = display->user->screen.width;
+ winsize->ws_xpixel = 0;
+ winsize->ws_ypixel = 0;
+ pthread_mutex_unlock (&display->lock);
+}
+
+
+/* Set the owner of the display DISPLAY to PID. The owner receives
+ the SIGWINCH signal when the terminal size changes. */
+error_t
+display_set_owner (display_t display, pid_t pid)
+{
+ pthread_mutex_lock (&display->lock);
+ display->has_owner = 1;
+ display->owner_id = pid;
+ pthread_mutex_unlock (&display->lock);
+ return 0;
+}
+
+
+/* Return the owner of the display DISPLAY in PID. If there is no
+ owner, return ENOTTY. */
+error_t
+display_get_owner (display_t display, pid_t *pid)
+{
+ error_t err = 0;
+ pthread_mutex_lock (&display->lock);
+ if (!display->has_owner)
+ err = ENOTTY;
+ else
+ *pid = display->owner_id;
+ pthread_mutex_unlock (&display->lock);
+ return err;
+}
+
+/* Output DATALEN characters from the buffer DATA on display DISPLAY.
+ The DATA must be supplied in the system encoding configured for
+ DISPLAY. The function returns the amount of bytes written (might
+ be smaller than DATALEN) or -1 and the error number in errno. If
+ NONBLOCK is not zero, return with -1 and set errno to EWOULDBLOCK
+ if operation would block for a long time. */
+ssize_t
+display_output (display_t display, int nonblock, char *data, size_t datalen)
+{
+ output_t output = &display->output;
+ error_t err;
+ char *buffer;
+ size_t buffer_size;
+ ssize_t amount;
+
+ error_t ensure_output_buffer_size (size_t new_size)
+ {
+ /* Must be a power of two. */
+#define OUTPUT_ALLOCSIZE 32
+
+ if (output->allocated < new_size)
+ {
+ char *new_buffer;
+ new_size = (new_size + OUTPUT_ALLOCSIZE - 1)
+ & ~(OUTPUT_ALLOCSIZE - 1);
+ new_buffer = realloc (output->buffer, new_size);
+ if (!new_buffer)
+ return ENOMEM;
+ output->buffer = new_buffer;
+ output->allocated = new_size;
+ }
+ return 0;
+ }
+
+ pthread_mutex_lock (&display->lock);
+ while (output->stopped)
+ {
+ if (nonblock)
+ {
+ pthread_mutex_unlock (&display->lock);
+ errno = EWOULDBLOCK;
+ return -1;
+ }
+ if (pthread_hurd_cond_wait_np (&output->resumed, &display->lock))
+ {
+ pthread_mutex_unlock (&display->lock);
+ errno = EINTR;
+ return -1;
+ }
+ }
+
+ if (output->size)
+ {
+ err = ensure_output_buffer_size (output->size + datalen);
+ if (err)
+ {
+ pthread_mutex_unlock (&display->lock);
+ errno = ENOMEM;
+ return -1;
+ }
+ buffer = output->buffer;
+ buffer_size = output->size;
+ memcpy (buffer + buffer_size, data, datalen);
+ buffer_size += datalen;
+ }
+ else
+ {
+ buffer = data;
+ buffer_size = datalen;
+ }
+ amount = buffer_size;
+ err = display_output_some (display, &buffer, &buffer_size);
+ amount -= buffer_size;
+
+ if (err && !amount)
+ {
+ pthread_mutex_unlock (&display->lock);
+ errno = err;
+ return err;
+ }
+ if (buffer_size)
+ {
+ /* If we used the caller's buffer DATA, the remaining bytes
+ might not fit in our internal output buffer. In this case we
+ can reallocate the buffer in VCONS without needing to update
+ OUTPUT (as it points into DATA). */
+ err = ensure_output_buffer_size (buffer_size);
+ if (err)
+ {
+ pthread_mutex_unlock (&display->lock);
+ return err;
+ }
+ memmove (output->buffer, buffer, buffer_size);
+ }
+ output->size = buffer_size;
+ amount += buffer_size;
+
+ pthread_mutex_unlock (&display->lock);
+ return amount;
+}
+
+
+ssize_t
+display_read (display_t display, int nonblock, off_t off,
+ char *data, size_t len)
+{
+ pthread_mutex_lock (&display->lock);
+ memcpy (data, ((char *) display->user) + off, len);
+ pthread_mutex_unlock (&display->lock);
+ return len;
+}
+
+
+/* Resume the output on the display DISPLAY. */
+void
+display_start_output (display_t display)
+{
+ pthread_mutex_lock (&display->lock);
+ if (display->output.stopped)
+ {
+ display->output.stopped = 0;
+ pthread_cond_broadcast (&display->output.resumed);
+ }
+ display->changes.flags = display->user->flags;
+ display->changes.which = DISPLAY_CHANGE_FLAGS;
+ display->user->flags &= ~CONS_FLAGS_SCROLL_LOCK;
+ display_flush_filechange (display, DISPLAY_CHANGE_FLAGS);
+ pthread_mutex_unlock (&display->lock);
+}
+
+
+/* Stop all output on the display DISPLAY. */
+void
+display_stop_output (display_t display)
+{
+ pthread_mutex_lock (&display->lock);
+ display->output.stopped = 1;
+ display->changes.flags = display->user->flags;
+ display->changes.which = DISPLAY_CHANGE_FLAGS;
+ display->user->flags |= CONS_FLAGS_SCROLL_LOCK;
+ display_flush_filechange (display, DISPLAY_CHANGE_FLAGS);
+ pthread_mutex_unlock (&display->lock);
+}
+
+
+/* Return the number of pending output bytes for DISPLAY. */
+size_t
+display_pending_output (display_t display)
+{
+ int output_size;
+ pthread_mutex_lock (&display->lock);
+ output_size = display->output.size;
+ pthread_mutex_unlock (&display->lock);
+ return output_size;
+}
+
+
+/* Flush the output buffer, discarding all pending data. */
+void
+display_discard_output (display_t display)
+{
+ pthread_mutex_lock (&display->lock);
+ display->output.size = 0;
+ pthread_mutex_unlock (&display->lock);
+}
+
+
+mach_port_t
+display_get_filemap (display_t display, vm_prot_t prot)
+{
+ mach_port_t memobj;
+ pthread_mutex_lock (&display->lock);
+ memobj = user_pager_get_filemap (&display->user_pager, prot);
+ pthread_mutex_unlock (&display->lock);
+ return memobj;
+}
diff --git a/console/display.h b/console/display.h
new file mode 100644
index 00000000..d64cf69e
--- /dev/null
+++ b/console/display.h
@@ -0,0 +1,84 @@
+/* display.h - Interface to the display component of a virtual console.
+ Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef DISPLAY_H
+#define DISPLAY_H
+
+#include <sys/ioctl.h>
+
+struct display;
+typedef struct display *display_t;
+
+void display_init (void);
+
+/* Create a new virtual console display, with the system encoding
+ being ENCODING and the default colors being FOREGROUND and BACKGROUND. */
+error_t
+display_create (display_t *r_display, const char *encoding,
+ conchar_attr_t def_attr, unsigned int lines,
+ unsigned int width, unsigned int height);
+
+
+/* Destroy the display DISPLAY. */
+void display_destroy (display_t display);
+
+/* Return the dimension of the display in bytes. */
+off_t display_get_size (display_t display);
+
+/* Return the dimensions of the display DISPLAY in *WINSIZE. */
+void display_getsize (display_t display, struct winsize *winsize);
+
+/* Set the owner of the display DISPLAY to PID. The owner receives
+ the SIGWINCH signal when the terminal size changes. */
+error_t display_set_owner (display_t display, pid_t pid);
+
+/* Return the owner of the display DISPLAY in PID. If there is no
+ owner, return ENOTTY. */
+error_t display_get_owner (display_t display, pid_t *pid);
+
+/* Output DATALEN characters from the buffer DATA on display DISPLAY.
+ The DATA must be supplied in the system encoding configured for
+ DISPLAY. The function returns the amount of bytes written (might
+ be smaller than DATALEN) or -1 and the error number in errno. If
+ NONBLOCK is not zero, return with -1 and set errno to EWOULDBLOCK
+ if operation would block for a long time. */
+ssize_t display_output (display_t display, int nonblock, char *data,
+ size_t datalen);
+
+mach_port_t display_get_filemap (display_t display, vm_prot_t prot);
+
+ssize_t display_read (display_t display, int nonblock, off_t off,
+ char *data, size_t len);
+
+error_t display_notice_changes (display_t display, mach_port_t notify);
+
+/* Resume the output on the display DISPLAY. */
+void display_start_output (display_t display);
+
+/* Stop all output on the display DISPLAY. */
+void display_stop_output (display_t display);
+
+/* Return the number of pending output bytes for DISPLAY. */
+size_t display_pending_output (display_t display);
+
+/* Flush the output buffer, discarding all pending data. */
+void display_discard_output (display_t display);
+
+#endif /* DISPLAY_H */
diff --git a/console/hurd.ti b/console/hurd.ti
new file mode 100644
index 00000000..f5c86508
--- /dev/null
+++ b/console/hurd.ti
@@ -0,0 +1,162 @@
+hurd|The GNU Hurd console server,
+# Over-all properties.
+# We use 8-bit characters
+ km,
+# Although we don't do XON/XOFF, we don't want padding characters.
+ xon,
+# Hard reset.
+ rs1=\EM,
+
+# Cursor related capabilities.
+
+# Moving the cursor.
+# We have automatic margins.
+ am,
+# We wrap around the left edge.
+ bw,
+# We ignore \n at end of line
+ xenl,
+# Carriage return and newline.
+ cr=^M, nel=^M^J,
+# Move cursor to home position (to position P1, P2).
+ home=\E[H, cup=\E[%i%p1%d;%p2%dH,
+# Move cursor one character (P1 characters) backwards.
+# We use ^H instead \E[D for cub1, as only ^H implements <bw> and it
+# is one byte instead three.
+ cub1=^H, cub=\E[%p1%dD,
+# Move cursor one line (P1 lines) downwards.
+ cud1=\E[B, cud=\E[%p1%dB,
+# Move cursor one character (P1 characters) forwards.
+ cuf1=\E[C, cuf=\E[%p1%dC,
+# Move cursor one line (P1 lines) upwards.
+ cuu1=\E[A, cuu=\E[%p1%dA,
+# Set horizontal (vertical) cursor position to P1.
+ hpa=\E[%i%p1%dG, vpa=\E[%i%p1%dd,
+# Save (restore) cursor position.
+ sc=\E7, rc=\E8,
+# Set the scrolling region to lines P1 to P2.
+ csr=\E[%i%p1%d;%p2%dr,
+
+# Modifying cursor attributes.
+# Make cursor invisible, very visible or normal.
+ civis=\E[?25l, cvvis=\E[34l, cnorm=\E[?25h,
+
+# Tabulator stops.
+# We have tabulator stops every eight rows.
+ it#8,
+# Move cursor to next tabulator stop.
+ ht=^I,
+# Move cursor to previous tabulator stop.
+ cbt=\E[Z,
+# XXX When we implement this.
+# Set tab stop in the current column of every row.
+# hts=\EH,
+# Delete all tab stops.
+# tbc=\E[3g,
+
+
+# Screen editing capabilities.
+# Clear screen.
+ clear=\Ec,
+# Clear to end of screen.
+ ed=\E[J,
+# Clear to end (beginning) of line.
+ el=\E[K, el1=\E[1K,
+
+# Insert one character (P1 characters).
+# <ich1> not included because we have insert mode.
+# ich1=\E[@,
+ ich=\E[%p1%d@,
+# Enter (leave) insert mode.
+ smir=\E[4h, rmir=\E[4l,
+# It is save to move when in insert mode.
+ mir,
+# Delete one character (P1 characters).
+ dch1=\E[P, dch=\E[%p1%dP,
+# Erase the next N characters.
+ ech=\E[%p1%dX,
+# Insert one line (P1 lines).
+ il1=\E[L, il=\E[%p1%dL,
+# Delete one line (P1 lines).
+ dl1=\E[M, dl=\E[%p1%dM,
+# Scroll the whole screen one line (P1 lines) upwards. We don't use
+# ^J, because this could put things into the scrollback buffer.
+ ind=\E[S, indn=\E[%p1%dS,
+# Scroll the whole screen one line (P1 lines) downwards.
+ rin=\E[%p1%dT, ri=\E[T,
+
+
+# Bell capabilities.
+# Audible bell.
+ bel=^G,
+# Flash the screen (visible bell).
+ flash=\Eg,
+
+
+# Keycodes for special keys.
+# Backspace key.
+ kbs=\177,
+# Keycode for left, down, right and up arrow key.
+ kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA,
+# Keycodes for function keys.
+ kf1=\EOP, kf2=\EOQ, kf3=\EOR, kf4=\EOS, kf5=\E[15~,
+ kf6=\E[17~, kf7=\E[18~, kf8=\E[19~, kf9=\E[20~,
+ kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, kf13=\E[25~,
+ kf14=\E[26~, kf15=\E[28~, kf16=\E[29~, kf17=\E[31~,
+ kf18=\E[32~, kf19=\E[33~, kf20=\E[34~,
+# Keycode for backtab key.
+ kcbt=\E[Z,
+# Keycode for suspend key.
+ kspd=^Z,
+# Keycode for home (insert, delete, end) key.
+ khome=\E[1~, kich1=\E[2~, kdch1=\E[3~, kend=\E[4~,
+# Keycode for previous (next) page key.
+ kpp=\E[5~, knp=\E[6~,
+# Keycode for center of keypad area.
+ kb2=\E[G,
+# Mouse event has occurred.
+ kmous=\E[M,
+
+# Text attribute capabilities.
+ acsc=++\,\,--..00ii``aaffgghhjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
+
+# Color support.
+# We erase the screen with the current background color.
+ bce,
+# Number of colors and color pairs at the same time.
+ colors#8,
+ pairs#64,
+# Video attributes colliding with color.
+# ORed: A_STANDOUT 1, A_UNDERLINE 2, A_REVERSE 4, A_BLINK 8, A_DIM 16,
+# A_BOLD 32, A_INVIS 64
+# We don't define this as we do our own display optimization,
+# depending on the display driver. Alternatively, we could provide
+# different terminfo entries.
+# ncv#18,
+# Set background (foreground) color.
+ setab=\E[4%p1%dm, setaf=\E[3%p1%dm,
+# Set default color pair to its original value.
+ op=\E[39;49m,
+
+# Video attributes.
+# Overstrikes are erasable with a blank.
+ eo,
+# It is save to move when in standout mode.
+ msgr,
+# Enable dim (blinking, bold, invisible, reverse) attribute.
+ dim=\E[2m, blink=\E[5m, bold=\E[1m, invis=\E[8m, rev=\E[7m,
+# Enable (disable) standout mode.
+ smso=\E[7m, rmso=\E[27m,
+# Enable (disable) underline mode.
+ smul=\E[4m, rmul=\E[24m,
+# Enable (disable) italic mode.
+ sitm=\E[3m, ritm=\E[23m,
+# Enable (disable) real bold (not intensity bright) mode. This is a
+# GNU extension.
+ gsbom=\E[>1h, grbom=\E[>1l,
+# Enable (disable) alternative character set.
+ smacs=\E[11m, rmacs=\E[10m,
+# Set all attributes.
+ sgr=\E[0%?%p1%t;7%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;%?%p6%t;1%;%?%p7%t;8%;%?%p9%t;11%;m,
+# Reset all attributes.
+ sgr0=\E[0m,
diff --git a/console/input.c b/console/input.c
new file mode 100644
index 00000000..91f21c15
--- /dev/null
+++ b/console/input.c
@@ -0,0 +1,277 @@
+/* input.c - Input component of a virtual console.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <iconv.h>
+#include <error.h>
+#include <string.h>
+#include <errno.h>
+#include <malloc.h>
+#include <sys/types.h>
+
+#include <pthread.h>
+
+#include "input.h"
+
+struct input
+{
+ pthread_mutex_t lock;
+
+ pthread_cond_t data_available;
+ pthread_cond_t space_available;
+#define INPUT_QUEUE_SIZE 300
+ char buffer[INPUT_QUEUE_SIZE];
+ int full;
+ size_t size;
+
+ /* The state of the conversion of input characters. */
+ iconv_t cd;
+ /* The conversion routine might refuse to handle some incomplete
+ multi-byte or composed character at the end of the buffer, so we
+ have to keep them around. */
+ char *cd_buffer;
+ size_t cd_size;
+ size_t cd_allocated;
+};
+
+/* Create a new virtual console input queue, with the system encoding
+ being ENCODING. */
+error_t input_create (input_t *r_input, const char *encoding)
+{
+ input_t input = calloc (1, sizeof *input);
+ if (!input)
+ return ENOMEM;
+
+ pthread_mutex_init (&input->lock, NULL);
+ pthread_cond_init (&input->data_available, NULL);
+ pthread_cond_init (&input->space_available, NULL);
+
+ input->cd = iconv_open (encoding, "UTF-8");
+ if (input->cd == (iconv_t) -1)
+ {
+ free (input);
+ return errno;
+ }
+
+ *r_input = input;
+ return 0;
+}
+
+/* Destroy the input queue INPUT. */
+void input_destroy (input_t input)
+{
+ iconv_close (input->cd);
+ free (input);
+}
+
+/* Enter DATALEN characters from the buffer DATA into the input queue
+ INPUT. The DATA must be supplied in UTF-8 encoding (XXX UCS-4
+ would be nice, too, but it requires knowledge of endianess). The
+ function returns the amount of bytes written (might be smaller than
+ DATALEN) or -1 and the error number in errno. If NONBLOCK is not
+ zero, return with -1 and set errno to EWOULDBLOCK if operation
+ would block for a long time. */
+ssize_t input_enqueue (input_t input, int nonblock, char *data,
+ size_t datalen)
+{
+ error_t err = 0;
+ int was_empty;
+ int enqueued = 0;
+ char *buffer;
+ size_t buffer_size;
+ ssize_t amount;
+ size_t nconv;
+ char *outbuf;
+ size_t outbuf_size;
+
+ error_t ensure_cd_buffer_size (size_t new_size)
+ {
+ /* Must be a power of two. */
+#define CD_ALLOCSIZE 32
+
+ if (input->cd_allocated < new_size)
+ {
+ char *new_buffer;
+ new_size = (new_size + CD_ALLOCSIZE - 1)
+ & ~(CD_ALLOCSIZE - 1);
+ new_buffer = realloc (input->cd_buffer, new_size);
+ if (!new_buffer)
+ return ENOMEM;
+ input->cd_buffer = new_buffer;
+ input->cd_allocated = new_size;
+ }
+ return 0;
+ }
+
+ pthread_mutex_lock (&input->lock);
+ was_empty = !input->size;
+
+ while (datalen)
+ {
+ /* Make sure we are ready for writing (or at least can make a
+ honest attempt at it). */
+ while (input->full)
+ {
+ if (nonblock)
+ {
+ err = EWOULDBLOCK;
+ goto out;
+ }
+ if (pthread_hurd_cond_wait_np (&input->space_available, &input->lock))
+ {
+ err = EINTR;
+ goto out;
+ }
+ was_empty = !input->size;
+ }
+
+ /* Prepare the input buffer for iconv. */
+ if (input->cd_size)
+ {
+ err = ensure_cd_buffer_size (input->cd_size + datalen);
+ if (err)
+ goto out;
+ buffer = input->cd_buffer;
+ buffer_size = input->cd_size;
+ memcpy (buffer + buffer_size, data, datalen);
+ buffer_size += datalen;
+ }
+ else
+ {
+ buffer = data;
+ buffer_size = datalen;
+ }
+ /* Prepare output buffer for iconv. */
+ outbuf = &input->buffer[input->size];
+ outbuf_size = INPUT_QUEUE_SIZE - input->size;
+
+ amount = buffer_size;
+ nconv = iconv (input->cd, &buffer, &buffer_size, &outbuf, &outbuf_size);
+ amount -= buffer_size;
+
+ /* Calculate buffer progress. */
+ enqueued += amount;
+ data = buffer;
+ datalen = buffer_size;
+ input->size = INPUT_QUEUE_SIZE - outbuf_size;
+
+ if (nconv == (size_t) -1)
+ {
+ if (errno == E2BIG)
+ {
+ /* There is not enough space for more data in the outbuf
+ buffer. Mark the buffer as full, awake waiting
+ readers and go to sleep (above). */
+ input->full = 1;
+ if (was_empty)
+ pthread_cond_broadcast (&input->data_available);
+ /* Prevent calling pthread_cond_broadcast again if nonblock. */
+ was_empty = 0;
+ }
+ else
+ break;
+ }
+ }
+
+ /* XXX What should be done with invalid characters etc? */
+ if (errno == EINVAL && datalen)
+ {
+ /* The conversion stopped because of an incomplete byte sequence
+ at the end of the buffer. */
+ /* If we used the caller's buffer DATA, the remaining bytes
+ might not fit in our internal output buffer. In this case we
+ can reallocate the buffer in INPUT without needing to update
+ CD_BUFFER (as it points into DATA). */
+ err = ensure_cd_buffer_size (datalen);
+ if (err)
+ {
+ pthread_mutex_unlock (&input->lock);
+ errno = err;
+ return enqueued ?: -1;
+ }
+ memmove (input->cd_buffer, data, datalen);
+ }
+
+ out:
+ if (enqueued)
+ {
+ if (was_empty)
+ pthread_cond_broadcast (&input->data_available);
+ }
+ else
+ errno = err;
+ pthread_mutex_unlock (&input->lock);
+ return enqueued ?: -1;
+}
+
+/* Remove DATALEN characters from the input queue and put them in the
+ buffer DATA. The data will be supplied in the local encoding. The
+ function returns the amount of bytes removed (might be smaller than
+ DATALEN) or -1 and the error number in errno. If NONBLOCK is not
+ zero, return with -1 and set errno to EWOULDBLOCK if operation
+ would block for a long time. */
+ssize_t input_dequeue (input_t input, int nonblock, char *data,
+ size_t datalen)
+{
+ size_t amount = datalen;
+
+ pthread_mutex_lock (&input->lock);
+ while (!input->size)
+ {
+ if (nonblock)
+ {
+ pthread_mutex_unlock (&input->lock);
+ errno = EWOULDBLOCK;
+ return -1;
+ }
+ if (pthread_hurd_cond_wait_np (&input->data_available, &input->lock))
+ {
+ pthread_mutex_unlock (&input->lock);
+ errno = EINTR;
+ return -1;
+ }
+ }
+
+ if (amount > input->size)
+ amount = input->size;
+ memcpy (data, input->buffer, amount);
+ memmove (input->buffer, input->buffer + amount, input->size - amount);
+ input->size -= amount;
+ if (amount && input->full)
+ {
+ input->full = 0;
+ pthread_cond_broadcast (&input->space_available);
+ }
+ pthread_mutex_unlock (&input->lock);
+ return amount;
+}
+
+
+/* Flush the input buffer, discarding all pending data. */
+void input_flush (input_t input)
+{
+ pthread_mutex_lock (&input->lock);
+ input->size = 0;
+ if (input->full)
+ {
+ input->full = 0;
+ pthread_cond_broadcast (&input->space_available);
+ }
+ pthread_mutex_unlock (&input->lock);
+}
diff --git a/console/input.h b/console/input.h
new file mode 100644
index 00000000..47de35e2
--- /dev/null
+++ b/console/input.h
@@ -0,0 +1,58 @@
+/* input.h - Interface to the input component of a virtual console.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef INPUT_H
+#define INPUT_H
+
+#include <errno.h>
+
+struct input;
+typedef struct input *input_t;
+
+/* Create a new virtual console input queue, with the system encoding
+ being ENCODING. */
+error_t input_create (input_t *r_input, const char *encoding);
+
+/* Destroy the input queue INPUT. */
+void input_destroy (input_t input);
+
+/* Enter DATALEN characters from the buffer DATA into the input queue
+ INPUT. The DATA must be supplied in UTF-8 encoding (XXX UCS-4
+ would be nice, too, but it requires knowledge of endianess). The
+ function returns the amount of bytes written (might be smaller than
+ DATALEN) or -1 and the error number in errno. If NONBLOCK is not
+ zero, return with -1 and set errno to EWOULDBLOCK if operation
+ would block for a long time. */
+ssize_t input_enqueue (input_t input, int nonblock, char *data,
+ size_t datalen);
+
+/* Remove DATALEN characters from the input queue and put them in the
+ buffer DATA. The data will be supplied in the local encoding. The
+ function returns the amount of bytes removed (might be smaller than
+ DATALEN) or -1 and the error number in errno. If NONBLOCK is not
+ zero, return with -1 and set errno to EWOULDBLOCK if operation
+ would block for a long time. */
+ssize_t input_dequeue (input_t input, int nonblock, char *data,
+ size_t datalen);
+
+/* Flush the input buffer, discarding all pending data. */
+void input_flush (input_t input);
+
+#endif /* INPUT_H */
diff --git a/console/motd.UTF8 b/console/motd.UTF8
new file mode 100644
index 00000000..40a104be
--- /dev/null
+++ b/console/motd.UTF8
@@ -0,0 +1,4 @@
+î¼€î¼î¼‚
+
+î¼î¼Žî¼î¼î¼‘ This is the GNU Hurd. Welcome.
+
diff --git a/console/mutations.h b/console/mutations.h
new file mode 100644
index 00000000..4e1cc7e1
--- /dev/null
+++ b/console/mutations.h
@@ -0,0 +1,33 @@
+/* mutations.h - Automagic type transformation for MiG interfaces.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* Only CPP macro definitions should go in this file. */
+
+#define IO_INTRAN protid_t begin_using_protid_port (io_t)
+#define IO_DESTRUCTOR end_using_protid_port (protid_t)
+
+#define TIOCTL_IMPORTS import "libnetfs/priv.h";
+
+#define NOTIFY_INTRAN \
+ port_info_t begin_using_port_info_port (mach_port_t)
+#define NOTIFY_DESTRUCTOR \
+ end_using_port_info (port_info_t)
+#define NOTIFY_IMPORTS \
+ import "libports/mig-decls.h";
diff --git a/console/pager.c b/console/pager.c
new file mode 100644
index 00000000..87c36f07
--- /dev/null
+++ b/console/pager.c
@@ -0,0 +1,239 @@
+/* pager.c - The pager for the display component of a virtual console.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+#include <assert.h>
+#include <error.h>
+#include <stdio.h>
+
+#include <sys/mman.h>
+#include <pthread.h>
+
+#include <hurd.h>
+#include <hurd/pager.h>
+#include <hurd/console.h>
+
+#include "pager.h"
+
+
+struct user_pager_info
+{
+ size_t memobj_npages;
+ vm_address_t memobj_pages[0];
+};
+
+
+/* We need a separate bucket for the pager ports. */
+static struct port_bucket *pager_bucket;
+
+
+/* Implement the pager_clear_user_data callback from the pager library. */
+void
+pager_clear_user_data (struct user_pager_info *upi)
+{
+ int idx;
+
+ for (idx = 0; idx < upi->memobj_npages; idx++)
+ if (upi->memobj_pages[idx])
+ vm_deallocate (mach_task_self (), upi->memobj_pages[idx], vm_page_size);
+ free (upi);
+}
+
+
+error_t
+pager_read_page (struct user_pager_info *upi, vm_offset_t page,
+ vm_address_t *buf, int *writelock)
+{
+ /* XXX clients should get a read only object. */
+ *writelock = 0;
+
+ if (upi->memobj_pages[page / vm_page_size] != (vm_address_t) NULL)
+ {
+ *buf = upi->memobj_pages[page / vm_page_size];
+ upi->memobj_pages[page / vm_page_size] = (vm_address_t) NULL;
+ }
+ else
+ *buf = (vm_address_t) mmap (0, vm_page_size, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ return 0;
+}
+
+
+error_t
+pager_write_page (struct user_pager_info *upi, vm_offset_t page,
+ vm_address_t buf)
+{
+ assert (upi->memobj_pages[page / vm_page_size] == (vm_address_t) NULL);
+ upi->memobj_pages[page / vm_page_size] = buf;
+ return 0;
+}
+
+
+error_t
+pager_unlock_page (struct user_pager_info *pager,
+ vm_offset_t address)
+{
+ assert (!"unlocking requested on unlocked page");
+ return 0;
+}
+
+
+void
+pager_notify_evict (struct user_pager_info *pager,
+ vm_offset_t page)
+{
+ assert (!"unrequested notification on eviction");
+}
+
+
+/* Tell how big the file is. */
+error_t
+pager_report_extent (struct user_pager_info *upi,
+ vm_address_t *offset,
+ vm_size_t *size)
+{
+ *offset = 0;
+ *size = upi->memobj_npages * vm_page_size;
+ return 0;
+}
+
+
+void
+pager_dropweak (struct user_pager_info *upi)
+{
+}
+
+
+/* A top-level function for the paging thread that just services paging
+ requests. */
+static void *
+service_paging_requests (void *arg)
+{
+ struct port_bucket *pager_bucket = arg;
+ for (;;)
+ ports_manage_port_operations_multithread (pager_bucket,
+ pager_demuxer,
+ 1000 * 60 * 2,
+ 1000 * 60 * 10, 0);
+ return NULL;
+}
+
+
+/* Initialize the pager for the display component. */
+void
+user_pager_init (void)
+{
+ pthread_t thread;
+ error_t err;
+
+ /* Create the pager bucket, and start to serve paging requests. */
+ pager_bucket = ports_create_bucket ();
+ if (! pager_bucket)
+ error (5, errno, "Cannot create pager bucket");
+
+ /* Make a thread to service paging requests. */
+ err = pthread_create (&thread, NULL, service_paging_requests, pager_bucket);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+}
+
+
+/* Create a new pager in USER_PAGER with NPAGES pages, and return a
+ mapping to the memory in *USER. */
+error_t
+user_pager_create (struct user_pager *user_pager, unsigned int npages,
+ struct cons_display **user)
+{
+ error_t err;
+ struct user_pager_info *upi;
+
+ upi = calloc (1, sizeof (struct user_pager_info)
+ + sizeof (vm_address_t) * npages);
+ if (!upi)
+ return errno;
+
+ upi->memobj_npages = npages;
+
+ /* XXX Are the values 1 and MEMORY_OBJECT_COPY_DELAY correct? */
+ user_pager->pager = pager_create (upi, pager_bucket,
+ 1, MEMORY_OBJECT_COPY_DELAY, 0);
+ if (!user_pager->pager)
+ {
+ free (upi);
+ return errno;
+ }
+ user_pager->memobj = pager_get_port (user_pager->pager);
+ ports_port_deref (user_pager->pager);
+
+ mach_port_insert_right (mach_task_self (), user_pager->memobj,
+ user_pager->memobj, MACH_MSG_TYPE_MAKE_SEND);
+
+ err = vm_map (mach_task_self (),
+ (vm_address_t *) user,
+ (vm_size_t) npages * vm_page_size,
+ (vm_address_t) 0,
+ 1 /* ! (flags & MAP_FIXED) */,
+ user_pager->memobj, 0 /* (vm_offset_t) offset */,
+ 0 /* ! (flags & MAP_SHARED) */,
+ VM_PROT_READ | VM_PROT_WRITE,
+ VM_PROT_READ | VM_PROT_WRITE,
+ VM_INHERIT_NONE);
+ if (err)
+ {
+ /* UPI will be cleaned up by libpager. */
+ mach_port_deallocate (mach_task_self (), user_pager->memobj);
+ return err;
+ }
+
+ return 0;
+}
+
+
+/* Destroy the pager USER_PAGER and the mapping at USER. */
+void
+user_pager_destroy (struct user_pager *user_pager, struct cons_display *user)
+{
+ /* The pager will be deallocated by libpager. */
+ vm_deallocate (mach_task_self (), (vm_offset_t) user,
+ pager_get_upi (user_pager->pager)->memobj_npages
+ * vm_page_size);
+ mach_port_deallocate (mach_task_self (), user_pager->memobj);
+}
+
+
+/* Allocate a reference for the memory object backing the pager
+ USER_PAGER with protection PROT and return it. */
+mach_port_t
+user_pager_get_filemap (struct user_pager *user_pager, vm_prot_t prot)
+{
+ error_t err;
+
+ /* Add a reference for each call, the caller will deallocate it. */
+ err = mach_port_mod_refs (mach_task_self (), user_pager->memobj,
+ MACH_PORT_RIGHT_SEND, +1);
+ assert_perror (err);
+
+ return user_pager->memobj;
+}
diff --git a/console/pager.h b/console/pager.h
new file mode 100644
index 00000000..974db961
--- /dev/null
+++ b/console/pager.h
@@ -0,0 +1,47 @@
+/* pager.h - Interface to the pager for display component of a virtual console.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef PAGER_H
+#define PAGER_H
+
+struct user_pager
+{
+ struct pager *pager;
+ memory_object_t memobj;
+};
+
+/* Initialize the pager for the display component. */
+void user_pager_init (void);
+
+/* Create a new pager in USER_PAGER with NPAGES pages, and return a
+ mapping to the memory in *USER. */
+error_t user_pager_create (struct user_pager *user_pager, unsigned int npages,
+ struct cons_display **user);
+
+/* Destroy the pager USER_PAGER and the mapping at USER. */
+void user_pager_destroy (struct user_pager *user_pager,
+ struct cons_display *user);
+
+/* Allocate a reference for the memory object backing the pager
+ USER_PAGER with protection PROT and return it. */
+mach_port_t user_pager_get_filemap (struct user_pager *user_pager,
+ vm_prot_t prot);
+
+#endif /* PAGER_H_ */
diff --git a/daemons/Makefile b/daemons/Makefile
new file mode 100644
index 00000000..d16680ec
--- /dev/null
+++ b/daemons/Makefile
@@ -0,0 +1,44 @@
+# Makefile for daemons
+#
+# Copyright (C) 1996, 1999, 2008, 2010 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+dir := daemons
+makemode := utilities
+
+targets = rc getty mail.local console-run runttys runsystem
+special-targets = rc runsystem
+SRCS = rc.sh runsystem.sh getty.c lmail.c console-run.c runttys.c
+installationdir = $(libexecdir)
+
+HURDLIBS = fshelp ports shouldbeinlibc
+OBJS = $(SRCS:.c=.o)
+getty-LDLIBS = -lutil
+
+INSTALL-mail.local-ops = -o root -m 4755
+
+include ../Makeconf
+
+rc: rc.sh
+getty: getty.o ../libshouldbeinlibc/libshouldbeinlibc.a
+mail.local: lmail.o ../libshouldbeinlibc/libshouldbeinlibc.a
+console-run: console-run.o ../libfshelp/libfshelp.a ../libports/libports.a
+
+runttys: runttys.o
+runttys-LDLIBS = -lutil
+
+runsystem: runsystem.sh
diff --git a/daemons/console-run.c b/daemons/console-run.c
new file mode 100644
index 00000000..e1bfe642
--- /dev/null
+++ b/daemons/console-run.c
@@ -0,0 +1,234 @@
+/* Run a program on the console, trying hard to get the console open.
+ Copyright (C) 1999, 2001 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <error.h>
+#include <paths.h>
+#include <hurd.h>
+#include <hurd/fshelp.h>
+#include <device/device.h>
+
+static mach_port_t
+get_console ()
+{
+ mach_port_t device_master, console;
+ error_t err = get_privileged_ports (0, &device_master);
+
+ if (err)
+ return MACH_PORT_NULL;
+
+ err = device_open (device_master, D_WRITE | D_READ, "console", &console);
+ if (err)
+ return MACH_PORT_NULL;
+
+ return console;
+}
+
+static int open_console (char **namep);
+
+int
+main (int argc, char **argv)
+{
+ mach_port_t consdev = get_console ();
+ mach_port_t runsystem;
+ char *consname;
+
+ if (consdev == MACH_PORT_NULL)
+ _exit (127);
+
+ stdin = stdout = NULL;
+ stderr = mach_open_devstream (consdev, "w");
+ if (!stderr)
+ _exit (127);
+
+ if (argc < 2)
+ error (1, 0, "Usage: %s PROGRAM [ARG...]", program_invocation_short_name);
+
+ /* Check whether runsystem exists before opening a console for it. */
+ runsystem = file_name_lookup (argv[1], O_RDONLY, 0);
+ if (runsystem == MACH_PORT_NULL)
+ error (127, errno, "cannot open file `%s' for execution", argv[1]);
+ mach_port_deallocate (mach_task_self (), runsystem);
+
+ if (open_console (&consname))
+ setenv ("FALLBACK_CONSOLE", consname, 1);
+
+ execv (argv[1], &argv[1]);
+ error (5, errno, "cannot execute %s", argv[1]);
+ /* NOTREACHED */
+ return 127;
+}
+
+/* Open /dev/console. If it isn't there, or it isn't a terminal, then
+ create /dev/console and put the terminal on it. If we get EROFS,
+ in trying to create /dev/console then as a last resort, create
+ /tmp/console. If all fail, we exit.
+
+ Return nonzero if the vanilla open of /dev/console didn't work.
+ In any case, after the console has been opened, put it on fds 0, 1, 2. */
+static int
+open_console (char **namep)
+{
+#define TERMINAL_FIRST_TRY "/hurd/term\0/dev/console\0device\0console"
+#define TERMINAL_SECOND_TRY "/hurd/term\0/tmp/console\0device\0console"
+ mach_port_t term, proc;
+ static char *termname;
+ struct stat st;
+ error_t err = 0;
+ int fd;
+ int fallback;
+
+ termname = _PATH_CONSOLE;
+ term = file_name_lookup (termname, O_RDWR, 0);
+ if (term != MACH_PORT_NULL)
+ err = io_stat (term, &st);
+ else
+ err = errno;
+ if (err)
+ error (0, err, "%s", termname);
+ else if (st.st_fstype != FSTYPE_TERM)
+ error (0, 0, "%s: Not a terminal", termname);
+
+ fallback = (term == MACH_PORT_NULL || err || st.st_fstype != FSTYPE_TERM);
+ if (fallback)
+ /* Start the terminal server ourselves. */
+ {
+ size_t argz_len; /* Length of args passed to translator. */
+ char *terminal; /* Name of term translator. */
+ mach_port_t control; /* Control port for term translator. */
+ int try;
+
+ error_t open_node (int flags,
+ mach_port_t *underlying,
+ mach_msg_type_name_t *underlying_type,
+ task_t task, void *cookie)
+ {
+ term = file_name_lookup (termname, flags | O_CREAT|O_NOTRANS, 0666);
+ if (term == MACH_PORT_NULL)
+ {
+ error (0, errno, "%s", termname);
+ return errno;
+ }
+
+ *underlying = term;
+ *underlying_type = MACH_MSG_TYPE_COPY_SEND;
+
+ return 0;
+ }
+
+ terminal = TERMINAL_FIRST_TRY;
+ argz_len = sizeof TERMINAL_FIRST_TRY;
+ for (try = 1; try < 3; ++try)
+ {
+ if (try == 2)
+ {
+ terminal = TERMINAL_SECOND_TRY;
+ argz_len = sizeof TERMINAL_SECOND_TRY;
+ }
+
+ termname = terminal + strlen (terminal) + 1; /* first arg is name */
+
+ /* The callback to start_translator opens TERM as a side effect. */
+ err =
+ fshelp_start_translator (open_node, NULL, terminal, terminal,
+ argz_len, 3000, &control);
+ if (err)
+ {
+ error (0, err, "%s", terminal);
+ continue;
+ }
+
+ err = file_set_translator (term, 0, FS_TRANS_SET, 0, 0, 0,
+ control, MACH_MSG_TYPE_COPY_SEND);
+ mach_port_deallocate (mach_task_self (), control);
+ if (err)
+ {
+ error (0, err, "%s", termname);
+ continue;
+ }
+ mach_port_deallocate (mach_task_self (), term);
+
+ /* Now repeat the open. */
+ term = file_name_lookup (termname, O_RDWR, 0);
+ if (term == MACH_PORT_NULL)
+ {
+ error (0, errno, "%s", termname);
+ continue;
+ }
+ err = io_stat (term, &st);
+ if (err)
+ {
+ error (0, err, "%s", termname);
+ term = MACH_PORT_NULL;
+ continue;
+ }
+ if (st.st_fstype != FSTYPE_TERM)
+ {
+ error (0, 0, "%s: Not a terminal", termname);
+ term = MACH_PORT_NULL;
+ continue;
+ }
+
+ if (term != MACH_PORT_NULL)
+ {
+ if (try == 1)
+ /* We created /dev/console, started, and installed the
+ translator on it, so it really isn't a fallback
+ console. */
+ fallback = 0;
+ else
+ error (0, 0, "Using temporary console %s", termname);
+ break;
+ }
+ }
+
+ if (term == MACH_PORT_NULL)
+ error (2, 0, "Cannot start console terminal");
+ }
+
+ fd = openport (term, O_RDWR);
+ if (fd < 0)
+ error (3, errno, "Cannot open console");
+
+ if (fd != 0)
+ {
+ dup2 (fd, 0);
+ close (fd);
+ }
+ dup2 (0, 1);
+ dup2 (0, 2);
+
+ if (getsid (0) != getpid ())
+ if (setsid () == -1)
+ error (0, errno, "setsid");
+
+ /* Set the console to our pgrp. */
+ tcsetpgrp (0, getpid ());
+
+ /* Put us in a new login collection. */
+ proc = getproc ();
+ proc_make_login_coll (proc);
+ mach_port_deallocate (mach_task_self (), proc);
+
+ *namep = termname;
+ return fallback;
+}
diff --git a/daemons/getty.c b/daemons/getty.c
new file mode 100644
index 00000000..40ad4d73
--- /dev/null
+++ b/daemons/getty.c
@@ -0,0 +1,213 @@
+/* Stubby version of getty for Hurd
+
+ Copyright (C) 1996, 1998, 1999, 2007, 2014
+ Free Software Foundation, Inc.
+
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <syslog.h>
+#include <unistd.h>
+#include <ttyent.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <error.h>
+#include <sys/utsname.h>
+#include <stdlib.h>
+#include <string.h>
+#include <utmp.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+
+/* XXX */
+extern char *localhost ();
+
+#define _PATH_LOGIN "/bin/login"
+#define _PATH_ISSUE "/etc/issue"
+
+/* Parse the terminal speed. */
+static void
+set_speed (int tty, char *speedstr)
+{
+ error_t err;
+ struct termios ttystat;
+ speed_t speed;
+ char *tail;
+
+ errno = 0;
+ speed = strtoul (speedstr, &tail, 0);
+ if (errno || *tail)
+ return;
+
+ err = tcgetattr (tty, &ttystat);
+ if (!err && !cfsetspeed (&ttystat, speed))
+ tcsetattr (tty, TCSAFLUSH, &ttystat);
+}
+
+/* Load a banner from _PATH_ISSUE. If that fails, a built-in version
+ is provided. */
+static char *
+load_banner (void)
+{
+ char *buf = NULL, *p;
+ struct stat st;
+ int fd;
+ ssize_t remaining, count;
+
+ fd = open (_PATH_ISSUE, O_RDONLY);
+ if (fd == -1)
+ goto out;
+
+ if (fstat (fd, &st) == -1)
+ goto out;
+
+ buf = malloc (st.st_size + 1);
+ if (buf == NULL)
+ goto out;
+
+ remaining = st.st_size;
+ p = buf;
+ while (remaining > 0)
+ {
+ count = read (fd, p, remaining);
+ if (count == -1)
+ {
+ close (fd);
+ goto out;
+ }
+ p += count;
+ remaining -= count;
+ }
+
+ buf[st.st_size] = '\0';
+ close (fd);
+ return buf;
+
+ out:
+ free (buf);
+ return "\n\\s \\r (\\n) (\\l)\r\n\n";
+}
+
+/* Print a suitable welcome banner */
+static void
+print_banner (int fd, char *ttyname)
+{
+ char *s, *t, *expansion;
+ struct utsname u;
+
+ if (uname (&u))
+ u.sysname[0] = u.release[0] = '\0';
+
+ write (fd, "\r\n", 2);
+ for (s = load_banner (); *s; s++)
+ {
+ for (t = s; *t && *t != '\\'; t++) /* nomnomnom */;
+
+ write (fd, s, t - s);
+ if (! *t)
+ return;
+
+ switch (*(t + 1))
+ {
+ case '\\':
+ expansion = "\\";
+ break;
+ case 's':
+ expansion = u.sysname;
+ break;
+ case 'r':
+ expansion = u.release;
+ break;
+ case 'n':
+ expansion = localhost () ?: "?";
+ break;
+ case 'l':
+ expansion = basename (ttyname);
+ break;
+ default:
+ expansion = "?";
+ }
+ write (fd, expansion, strlen (expansion));
+
+ s = t + 1;
+ }
+}
+
+int
+main (int argc, char **argv)
+{
+ char *linespec, *ttyname;
+ int tty;
+ struct ttyent *tt;
+ char *arg;
+
+ openlog ("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
+
+ /* Nothing to do .... */
+ if (argc != 3)
+ {
+ syslog (LOG_ERR, "Bad syntax");
+ closelog ();
+ exit (1);
+ }
+
+ /* Don't do anything with this for now. */
+ linespec = argv[1];
+
+ tt = getttynam (argv[2]);
+ asprintf (&ttyname, "%s/%s", _PATH_DEV, argv[2]);
+
+ chown (ttyname, 0, 0);
+ chmod (ttyname, 0600);
+ revoke (ttyname);
+ sleep (2); /* leave DTR down for a bit */
+
+ do
+ {
+ tty = open (ttyname, O_RDWR);
+ if (tty == -1)
+ {
+ syslog (LOG_ERR, "%s: %m", ttyname);
+ closelog ();
+ sleep (60);
+ }
+ }
+ while (tty == -1);
+
+ set_speed (tty, linespec);
+
+ print_banner (tty, ttyname);
+
+ if (login_tty (tty) == -1)
+ syslog (LOG_ERR, "cannot set controlling terminal to %s: %m", ttyname);
+
+ asprintf (&arg, "TERM=%s", tt ? tt->ty_type : "unknown");
+
+ if (tt && strcmp (tt->ty_type, "dialup") == 0)
+ /* Dialup lines time out (which is login's default). */
+ execl (_PATH_LOGIN, "login", "-e", arg, NULL);
+ else
+ /* Hardwired lines don't. */
+ execl (_PATH_LOGIN, "login", "-e", arg, "-aNOAUTH_TIMEOUT", NULL);
+
+ syslog (LOG_ERR, "%s: %m", _PATH_LOGIN);
+
+ return 1;
+}
diff --git a/daemons/lmail.c b/daemons/lmail.c
new file mode 100644
index 00000000..23c7b497
--- /dev/null
+++ b/daemons/lmail.c
@@ -0,0 +1,542 @@
+/* Local mail delivery
+
+ Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <sysexits.h>
+#include <paths.h>
+#include <argp.h>
+#include <hurd.h>
+#include <hurd/fd.h>
+#include <version.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+
+#define OPT_FILE -5
+#define OPT_REMOVE -6
+
+const char *argp_program_version = STANDARD_HURD_VERSION (mail.local);
+
+static const struct argp_option
+options[] =
+{
+ {"from", 'f', "USER", 0, "Record sender as USER"},
+ {0, 'd', 0, OPTION_ALIAS|OPTION_HIDDEN},
+ {0, 'r', 0, OPTION_ALIAS|OPTION_HIDDEN},
+ {"file", OPT_FILE, "FILE", 0, "Deliver FILE instead of standard input"},
+ {"remove", OPT_REMOVE, 0, 0, "Remove FILE after successful delivery"},
+ {"mail-dir",'m', "DIR", 0, "Look for mailboxes in DIR"},
+ {"use-lock-file",'l', 0, OPTION_HIDDEN,
+ "Use a lock file instead of flock for mailboxes"},
+ {0}
+};
+static const char args_doc[] = "USER...";
+static const char doc[] = "Deliver mail to the local mailboxes of USER...";
+
+#define HDR_PFX "From " /* Header, at the beginning of a line,
+ starting each msg in a mailbox. */
+#define ESC_PFX ">" /* Used to escape occurrences of HDR_PFX in
+ the msg body. */
+
+#define BMAX (64*1024) /* Chunk size for I/O. */
+
+struct params
+{
+ char *from; /* Who the mail is from. */
+ char *mail_dir; /* Mailbox directory. */
+};
+
+/* Convert the system error code ERR to an appropriate exit value. This
+ function currently only returns three sorts: success, temporary failure,
+ or error, with exit codes 0, EX_TEMPFAIL, or EX_UNAVAILABLE. The table of
+ temporary failures is from the bsd original of this program. */
+static int
+err_to_ex (error_t err)
+{
+ switch (err)
+ {
+ case 0:
+ return 0; /* Success */
+ /* Temporary failures: */
+#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
+ case EWOULDBLOCK: /* Operation would block. */
+#endif
+ case EAGAIN: /* Resource temporarily unavailable */
+ case EDQUOT: /* Disc quota exceeded */
+ case EBUSY: /* Device busy */
+ case EPROCLIM: /* Too many processes */
+ case EUSERS: /* Too many users */
+ case ECONNABORTED: /* Software caused connection abort */
+ case ECONNREFUSED: /* Connection refused */
+ case ECONNRESET: /* Connection reset by peer */
+ case EDEADLK: /* Resource deadlock avoided */
+ case EFBIG: /* File too large */
+ case EHOSTDOWN: /* Host is down */
+ case EHOSTUNREACH: /* No route to host */
+ case EMFILE: /* Too many open files */
+ case ENETDOWN: /* Network is down */
+ case ENETRESET: /* Network dropped connection on reset */
+ case ENETUNREACH: /* Network is unreachable */
+ case ENFILE: /* Too many open files in system */
+ case ENOBUFS: /* No buffer space available */
+ case ENOMEM: /* Cannot allocate memory */
+ case ENOSPC: /* No space left on device */
+ case EROFS: /* Read-only file system */
+ case ESTALE: /* Stale NFS file handle */
+ case ETIMEDOUT: /* Connection timed out */
+ return EX_TEMPFAIL;
+ default:
+ return EX_UNAVAILABLE;
+ }
+}
+
+/* Print and syslog the given error message, with the system error string for
+ ERRNO appended. Return an appropriate exit code for ERRNO. */
+#define SYSERR(fmt, args...) \
+ ({ syslog (LOG_ERR, fmt ": %m" , ##args); err_to_ex (errno); })
+
+/* Print and syslog the given error message. Return the exit code
+ EX_UNAVAILABLE. */
+#define ERR(fmt, args...) \
+ ({ syslog (LOG_ERR, fmt , ##args); EX_UNAVAILABLE; })
+
+/* Print and syslog the given error message, with the system error string for
+ CODE appended. Return an appropriate exit code for CODE. */
+#define SYSERRX(code, fmt, args...) \
+ ({ error_t _code = (code); \
+ syslog (LOG_ERR, fmt ": %s" , ##args , strerror (_code)); \
+ err_to_ex (_code); })
+
+/* Block I/O functions. These are structured to allow the use of a read
+ method that avoids copying the data locally. */
+
+/* Free block allocated by bread. */
+static void
+bfree (char *blk, size_t blk_len)
+{
+ if (blk_len > 0)
+ munmap (blk, blk_len);
+}
+
+/* Read up to MAX chars from IN into BLK & BLK_LEN, which may be reused or
+ freed. */
+static int
+bread (int in, char *in_name, size_t max, char **blk, size_t *blk_len)
+{
+ char *orig_blk = *blk;
+ size_t orig_blk_len = *blk_len;
+ error_t err = HURD_DPORT_USE (in, io_read (port, blk, blk_len, -1, max));
+
+ if (err)
+ return SYSERRX (err, "%s", in_name);
+
+ if (*blk != orig_blk)
+ bfree (orig_blk, orig_blk_len);
+
+ return 0;
+}
+
+/* Write BLK & BLK_LEN to OUT. An exit status is returned. */
+static int
+bwrite (int out, char *out_name, const char *blk, size_t blk_len)
+{
+ while (blk_len > 0)
+ {
+ ssize_t wr = write (out, blk, blk_len);
+ if (wr < 0)
+ return SYSERR ("%s", out_name);
+ blk += wr;
+ blk_len -= wr;
+ }
+ return 0;
+}
+
+/* Copy from file descriptor IN to OUT. An exit status is returned. */
+static int
+copy (int in, char *in_name, int out, char *out_name)
+{
+ int ex = 0;
+ char *blk = 0;
+ size_t blk_len = 0;
+
+ do
+ {
+ ex = bread (in, in_name, BMAX, &blk, &blk_len);
+ if (! ex)
+ ex = bwrite (out, out_name, blk, blk_len);
+ }
+ while (blk_len > 0 && !ex);
+
+ bfree (blk, blk_len);
+
+ return ex;
+}
+
+static int
+write_header (int out, char *out_name, struct params *params)
+{
+ char *hdr;
+ size_t hdr_len;
+ struct timeval tv;
+ time_t time;
+ int ex = 0;
+
+ if (gettimeofday (&tv, 0) < 0)
+ return SYSERR ("gettimeofday");
+
+ /* Note that the string returned by ctime includes a terminating newline. */
+ time = tv.tv_sec;
+ hdr_len = asprintf (&hdr, "From %s %s", params->from, ctime (&time));
+ if (! hdr)
+ return SYSERRX (ENOMEM, "%s", out_name);
+
+ ex = bwrite (out, out_name, hdr, hdr_len);
+
+ free (hdr);
+
+ return ex;
+}
+
+/* Copy from file descriptor IN to OUT, making any changes needed to make the
+ contents a valid mailbox entry. These include:
+ (1) Prepending a `From ...' line, and appending a blank line.
+ (2) Replacing any occurrences of `From ' at the beginning of lines with
+ `>From '.
+ An exit status is returned. */
+static int
+process (int in, char *in_name, int out, char *out_name, struct params *params)
+{
+ /* The block currently being processed. */
+ char *blk = 0;
+ size_t blk_len = 0;
+ /* MATCH is the string we're looking for to escape, NL_MATCH is the same
+ string prefixed by a newline to ease searching (MATCH only matches at
+ the beginning of lines). */
+ const char *const nl_match = "\n" HDR_PFX, *const match = nl_match + 1;
+ /* The portion of MATCH that matched the end of the previous block. 0
+ means that there was at least a newline, so initializing MATCHED to 0
+ simulates a newline at the start of IN. */
+ ssize_t matched = 0;
+ int ex = write_header (out, out_name, params);
+
+#define match_len (sizeof HDR_PFX - 1)
+
+ if (ex)
+ return ex;
+
+#define BWRITE(p, p_len) \
+ ({ size_t _len = (p_len); \
+ if (_len > 0 && (ex = bwrite (out, out_name, p, _len))) \
+ break; })
+
+ do
+ {
+ char *start, *end;
+
+ ex = bread (in, in_name, BMAX, &blk, &blk_len);
+
+ if (matched >= 0)
+ /* The last block ended in a partial match, so see if we can complete
+ it in this block. */
+ {
+ if (blk_len >= match_len - matched
+ && memcmp (blk, match + matched, match_len - matched) == 0)
+ /* It matched; output the escape prefix before the match. */
+ BWRITE (ESC_PFX, sizeof ESC_PFX - 1);
+ /* Now we have to output the part of the preceding block that we
+ didn't do before because it was a partial match. */
+ BWRITE (match, matched);
+ /* Ok, we're all caught up. The portion of the match that's in
+ this block will be output as normal text. */
+ matched = -1;
+ }
+
+ /* Scan through the block looking for matches. */
+ for (start = end = blk; start < blk + blk_len; start = end)
+ {
+ /* Look for our match, prefixed by a newline. */
+ end = memmem (start, blk + blk_len - start, nl_match, match_len + 1);
+ if (end)
+ /* We found a match, output the escape prefix before it. */
+ {
+ end++; /* The newline should precede the escape. */
+ BWRITE (start, end - start); /* part of block before match. */
+ BWRITE (ESC_PFX, sizeof ESC_PFX - 1); /* escape prefix. */
+ }
+ else
+ {
+ end = blk + blk_len;
+ break;
+ }
+ }
+
+ /* Now see if there are any partial matches at the end. */
+ for (matched =
+ end - start < match_len + 1 ? end - start - 1 : match_len;
+ matched >= 0;
+ matched--)
+ if (memcmp (end - matched - 1, nl_match, matched + 1) == 0)
+ /* There's a partial match MATCHED characters long at the end of
+ this block, so delay outputting it until the next block can be
+ examined; we do output the preceding newline here, though. */
+ {
+ end -= matched;
+ break;
+ }
+
+ BWRITE (start, end - start);
+ }
+ while (blk_len > 0);
+
+ if (! ex)
+ ex = bwrite (out, out_name, "\n", 1); /* Append a blank line. */
+
+ bfree (blk, blk_len);
+
+ return ex;
+}
+
+/* Deliver flags. */
+#define D_PROCESS 0x1 /* Deliver */
+#define D_REWIND 0x2 /* Rewind MSG before using it. */
+
+/* Deliver the text from the file descriptor MSG to the mailbox of the user
+ RCPT in MAIL_DIR. FLAGS is from the set D_* above. An exit appropriate
+ exit code is returned. */
+static int
+deliver (int msg, char *msg_name, char *rcpt, int flags, struct params *params)
+{
+ char *mbox; /* Name of mailbox */
+ int fd; /* Opened mailbox */
+ struct stat stat;
+ int ex = 0; /* Exit status */
+ struct passwd *pw = getpwnam (rcpt); /* Details of recipient */
+
+ if (! pw)
+ return ERR ("%s: Unknown user", rcpt);
+
+ asprintf (&mbox, "%s/%s", params->mail_dir, rcpt);
+ if (! mbox)
+ return SYSERRX (ENOMEM, "%s", rcpt);
+
+ do
+ {
+ /* First try to open an existing mailbox. */
+ fd = open (mbox, O_WRONLY|O_APPEND|O_NOLINK|O_EXLOCK);
+ if (fd < 0 && errno == ENOENT)
+ /* There is none; try to create it. */
+ {
+ fd = open (mbox, O_WRONLY|O_APPEND|O_CREAT|O_EXCL|O_NOLINK|O_EXLOCK,
+ S_IRUSR|S_IWUSR);
+ if (fd >= 0)
+ /* Made a new mailbox! Set the owner and group appropiately. */
+ {
+ if (fchown (fd, pw->pw_uid, pw->pw_gid) < 0)
+ {
+ close (fd);
+ fd = -1; /* Propagate error. */
+ }
+ }
+ }
+ }
+ /* EEXIST can only be returned someone else created the mailbox between the
+ two opens above, so if we try again, the first open should work. */
+ while (fd < 0 && errno == EEXIST);
+
+ if (fd < 0 || fstat (fd, &stat) < 0)
+ ex = SYSERR ("%s", mbox);
+ else if (S_ISLNK (stat.st_mode) || stat.st_nlink != 1)
+ ex = ERR ("%s: Is linked", mbox);
+ else
+ {
+ if (flags & D_REWIND)
+ {
+ if (lseek (msg, 0L, SEEK_SET) < 0)
+ ex = SYSERR ("%s", msg_name);
+ }
+ if (! ex)
+ {
+ if (flags & D_PROCESS)
+ ex = process (msg, msg_name, fd, mbox, params);
+ else
+ ex = copy (msg, msg_name, fd, mbox);
+ }
+ }
+
+ if (fd >= 0)
+ {
+ if (fsync (fd) < 0 && !ex)
+ ex = SYSERR ("%s", mbox);
+ if (close (fd) < 0 && !ex)
+ ex = SYSERR ("%s", mbox);
+ }
+ free (mbox);
+
+ return ex;
+}
+
+/* Process from the file descriptor IN into a temporary file, which return a
+ descriptor to in CACHED; once *CACHED is closed, it will go away
+ permanently. The file pointer of *CACHED is at an undefined location. An
+ exit status is returned. */
+static int
+cache (int in, char *in_name, struct params *params, int *cached)
+{
+ int ex;
+ error_t err;
+ file_t file; /* Hurd port for temp file */
+ int fd; /* File descriptor for it */
+ file_t tmp_dir = file_name_lookup (_PATH_TMP, O_RDONLY, 0);
+
+ if (tmp_dir == MACH_PORT_NULL)
+ return SYSERR ("%s", _PATH_TMP);
+
+ /* Create FILE without actually putting it into TMP_DIR. */
+ err = dir_mkfile (tmp_dir, O_RDWR, 0600, &file);
+ if (err)
+ return SYSERRX (err, "%s", _PATH_TMP);
+
+ fd = _hurd_intern_fd (file, O_RDWR, 1);
+ if (fd < 0)
+ return SYSERR ("%s", _PATH_TMP);
+
+ ex = process (in, in_name, fd, _PATH_TMP, params);
+ if (! ex)
+ *cached = fd;
+ else
+ close (fd);
+
+ return ex;
+}
+
+int
+main (int argc, char **argv)
+{
+ int rcpt = 0; /* Index in ARGV of next recipient. */
+ char *file = 0; /* File containing message. */
+ int remove = 0; /* Remove file after successful delivery. */
+ int in = 0; /* Raw input file descriptor. */
+ int ex = 0; /* Exit status. */
+ struct params params = { from: 0, mail_dir: _PATH_MAILDIR };
+
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'd':
+ /* do nothing; compatibility */
+ break;
+ case 'f':
+ case 'r':
+ params.from = arg; break;
+ case OPT_FILE:
+ file = arg; break;
+ case OPT_REMOVE:
+ remove = 1; break;
+ case 'm':
+ params.mail_dir = arg; break;
+ case 'l':
+ argp_failure (state, EX_USAGE, EINVAL, "-l not supported");
+ case ARGP_KEY_NO_ARGS:
+ argp_error (state, "No recipients");
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ const struct argp argp = { options, parse_opt, args_doc, doc };
+
+ /* Parse arguments. */
+ argp_parse (&argp, argc, argv, 0, &rcpt, 0);
+
+ /* Use syslog to log errors. */
+ openlog ("mail.local", LOG_PERROR, LOG_MAIL);
+
+ if (! params.from)
+ /* No from address specified, use the current user. */
+ {
+ struct passwd *pw;
+ int uid = getuid ();
+
+ if (uid == -1)
+ exit (ERR ("No user id"));
+
+ pw = getpwuid (uid);
+ if (! pw)
+ exit (ERR ("%d: Unknown uid", uid));
+
+ params.from = strdup (pw->pw_name);
+ }
+
+ if (file)
+ /* Use FILE as the message contents. */
+ {
+ in = open (file, O_RDONLY);
+ if (in < 0)
+ exit (SYSERR ("%s", file));
+ }
+ else
+ /* Use standard input. */
+ in = 0;
+
+ if (rcpt == argc - 1)
+ /* Only a single recipient. */
+ ex = deliver (in, file ?: "-", argv[rcpt], D_PROCESS, &params);
+ else
+ /* Multiple recipients. */
+ {
+ int cached; /* Temporary processed input file. */
+
+ ex = cache (in, file ?: "-", &params, &cached);
+ if (! ex)
+ while (rcpt < argc)
+ {
+ /* Deliver to one recipient. */
+ int rex = deliver (cached, "message cache", argv[rcpt++],
+ D_REWIND, &params);
+
+ /* Merge the exit code for that recipient. Temporary failures
+ take precedence over hard failures and success, as
+ subsequently delivering duplicates (of the successful
+ messages) is preferable to not delivering the temporary
+ failures. */
+ if (ex != EX_TEMPFAIL)
+ {
+ if (rex == EX_TEMPFAIL)
+ ex = EX_TEMPFAIL;
+ else if (! ex)
+ ex = rex;
+ }
+ }
+ }
+
+ if (file && remove && !ex)
+ unlink (file);
+
+ exit (ex);
+}
diff --git a/daemons/rc.sh b/daemons/rc.sh
new file mode 100644
index 00000000..5cf44fa6
--- /dev/null
+++ b/daemons/rc.sh
@@ -0,0 +1,123 @@
+#!/bin/bash
+
+PATH=/bin:/sbin
+
+# Start the default pager. It will bail if there is already one running.
+/hurd/mach-defpager
+
+# Set up swap space. This will complain if no default pager is functioning.
+swapon -a
+
+# Check filesystems.
+if [ -r /fastboot ]
+then
+ # ... or don't.
+ rm -f /fastboot
+ echo Fast boot ... skipping disk checks
+elif [ $1x = autobootx ]
+then
+ echo Automatic boot in progress...
+ date
+
+ /sbin/fsck --preen --writable
+
+ case $? in
+ # Successful completion
+ 0)
+ ;;
+ # Filesystem modified (but ok now)
+ 1)
+ ;;
+ # Filesystem modified, filesystem should be restarted
+ # Ideally we would only restart the filesystem
+ 2 | 3)
+ /sbin/reboot
+ ;;
+ # Fsck couldn't fix it.
+ 4 | 5 | 8 | 9)
+ echo "Automatic boot failed... help!"
+ exit 1
+ ;;
+ # Signal that really interrupted something
+ 20 | 130 | 131)
+ echo "Boot interrupted"
+ exit 1
+ ;;
+ # Special `let fsck finish' interruption (SIGQUIT)
+ 12)
+ echo "Boot interrupted (filesystem checks complete)"
+ exit 1
+ ;;
+ # Oh dear.
+ *)
+ echo "Unknown error during fsck (exit status $?)"
+ exit 1
+ ;;
+ esac
+fi
+
+echo -n cleaning up left over files...
+rm -f /etc/nologin
+rm -f /var/lock/LCK.*
+if test -d /tmp; then
+
+ # Forcibly remove all translators in the directory.
+ # It is then safe to attempt to remove files and descend directories.
+ # All parameters must begin with "./".
+ function remove_translators() {
+ local f
+ for f; do
+ settrans -pagfS "$f"
+ if [ -L "$f" ] || [ ! -d "$f" ]; then
+ rm "$f"
+ else
+ remove_translators "$f"/* "$f"/.[!.] "$f"/.??*
+ rmdir "$f"
+ fi
+ done
+ }
+
+ (cd /tmp
+ shopt -s nullglob
+ for f in * .[!.] .??*; do
+ case "$f" in
+ 'lost+found'|'quotas') ;;
+ *) remove_translators "./$f"
+ esac
+ done)
+
+ unset -f remove_translators # because it relies on nullglob
+
+fi
+if test -d /var/run; then
+ (cd /var/run && { rm -rf -- *; cp /dev/null utmp; chmod 644 utmp; })
+fi
+echo done
+
+# This file must exist for e2fsck to work. XXX
+touch /var/run/mtab
+
+#echo -n restoring pty permissions...
+#chmod 666 /dev/tty[pqrs]*
+#echo done
+
+#echo -n updating /etc/motd...
+#echo GNU\'s Not Unix Version `uname --release` > /tmp/newmotd
+#egrep -v 'GNU|Version' /etc/motd >> /tmp/newmotd
+#mv /tmp/newmotd /etc/motd
+#echo done
+
+chmod 664 /etc/motd
+
+echo -n starting daemons:
+
+/sbin/syslogd && echo -n ' syslogd'
+/sbin/inetd && echo -n ' inetd'
+
+if test -x /sbin/sendmail -a -r /etc/sendmail.cf; then
+ /sbin/sendmail -bd -q30m && echo -n ' sendmail'
+fi
+
+echo .
+
+date
diff --git a/daemons/runsystem.sh b/daemons/runsystem.sh
new file mode 100644
index 00000000..f4f27711
--- /dev/null
+++ b/daemons/runsystem.sh
@@ -0,0 +1,155 @@
+#!/bin/bash
+#
+# This program is run by /hurd/init at boot time after the essential
+# servers are up, and is responsible for running the "userland" parts of a
+# normal system. This includes running the single-user shell as well as a
+# multi-user system. This program is expected never to exit.
+#
+
+
+###
+### Where to find programs, etc.
+###
+
+PATH=/bin:/sbin
+export PATH
+
+umask 022
+
+# If we lose badly, try to exec each of these in turn.
+fallback_shells='/bin/sh /bin/bash /bin/csh /bin/ash /bin/shd'
+
+# Shell used for normal single-user startup.
+SHELL=/bin/sh
+
+# Programs that do multi-user startup.
+RUNCOM=/libexec/rc
+RUNTTYS=/libexec/runttys
+# Signals that we should pass down to runttys.
+runttys_sigs='TERM INT HUP TSTP'
+
+###
+
+
+# If we get a SIGLOST, attempt to reopen the console in case
+# our console ports were revoked. This lets us print messages.
+function reopen_console ()
+{
+ exec 1>/dev/console 2>&1 || exit 3
+}
+trap 'reopen_console' SIGLOST
+
+
+# Call this when we are losing badly enough that we want to punt normal
+# startup entirely. We exec a single-user shell, so we will not come back
+# here. The only way to get to multi-user from that shell will be
+# explicitly exec this script or something like that.
+function singleuser ()
+{
+ test $# -eq 0 || echo "$0: $*"
+ for try in ${fallback_shells}; do
+ SHELL=${try}
+ exec ${SHELL}
+ done
+ exit 127
+}
+
+
+# See whether pflocal is set up already, and do so if not (install case)
+#
+# Normally this should be the case, but we better make sure since
+# without the pflocal server, pipe(2) does not work.
+if ! test -e /servers/socket/1 ; then
+ # The root filesystem should be read-only at this point.
+ if fsysopts / --update --writable ; then
+ settrans -c /servers/socket/1 /hurd/pflocal
+ else
+ singleuser "Failed to create /servers/socket/1."
+ fi
+fi
+
+# We expect to be started by console-run, which gives us no arguments and
+# puts FALLBACK_CONSOLE=file-name in the environment if our console is
+# other than a normal /dev/console.
+
+if [ "${FALLBACK_CONSOLE+set}" = set ]; then
+ singleuser "Running on fallback console ${FALLBACK_CONSOLE}"
+fi
+
+
+###
+### Normal startup procedures
+###
+
+# Parse the multiboot command line. We only pay attention to -s and -f.
+# The first argument is the kernel file name; skip that.
+shift
+flags=
+while [ $# -gt 0 ]; do
+ arg="$1"
+ shift
+ case "$arg" in
+ --*) ;;
+ *=*) ;;
+ -*)
+ flags="${flags}${arg#-}"
+ ;;
+ 'single'|'emergency') # Linux compat
+ flags="${flags}s"
+ ;;
+ 'fastboot')
+ flags="${flags}f"
+ ;;
+ esac
+done
+
+# Check boot flags.
+case "$flags" in
+*s*)
+ rc=false # force single-user
+ ;;
+*f*)
+ rc="${RUNCOM}" # fastboot
+ ;;
+*)
+ rc="${RUNCOM} autoboot" # multi-user default
+ ;;
+esac
+
+# Large infinite loop. If this script ever exits, init considers that
+# a serious bogosity and punts to a fallback single-user shell.
+# We handle here the normal transitions between single-user and multi-user.
+while : ; do
+
+ # Run the rc script. As long as it exits nonzero, punt to single-user.
+ # After the single-user shell exits, we will start over attempting to
+ # run rc; but later invocations strip the `autoboot' argument.
+ until $rc; do
+ rc=${RUNCOM}
+
+ # Run single-user shell and repeat as long as it dies with a signal.
+ until ${SHELL} || test $? -lt 128; do
+ :
+ done
+ done
+
+ # Now we are officially ready for normal multi-user operation.
+
+ # Trap certain signals and send them on to runttys. For this to work, we
+ # must run it asynchronously and wait for it with the `wait' built-in.
+ runttys_pid=0
+ for sig in $runttys_sigs; do
+ trap "kill -$sig \${runttys_pid}" $sig
+ done
+
+ # This program reads /etc/ttys and starts the programs it says to.
+ ${RUNTTYS} &
+ runttys_pid=$!
+
+ # Wait for runttys to die, meanwhile handling trapped signals.
+ wait
+
+ # Go back to the top of the infinite loop, as if booting single-user.
+ rc=false
+
+done
diff --git a/daemons/runttys.c b/daemons/runttys.c
new file mode 100644
index 00000000..7efb7b7a
--- /dev/null
+++ b/daemons/runttys.c
@@ -0,0 +1,502 @@
+/* /etc/ttys support for Hurd
+ Copyright (C) 1993,94,95,96,97,98,99,2001 Free Software Foundation, Inc.
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <argz.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <ttyent.h>
+#include <unistd.h>
+#include <utmp.h>
+
+
+/* How long to wait after starting window specs before starting getty */
+#define WINDOW_DELAY 3 /* seconds */
+
+#define _PATH_LOGIN "/bin/login"
+
+
+/* All the ttys in /etc/ttys. */
+struct terminal
+{
+ char *name; /* Name of the terminal device file. */
+
+ /* argv lists for getty and window spec.
+ The first element is always the malloc'd argz the rest point into. */
+ char **getty_argv, **window_argv;
+
+ int on; /* Nonzero iff the line is "on". */
+ pid_t pid; /* Child running on this line. */
+ int read; /* Used during reread_ttys. */
+};
+
+static struct terminal *ttys;
+/* Number of live elements in ttys */
+static int nttys;
+/* Total number of elements in ttys */
+static int ttyslen;
+
+
+static void
+free_argvs (struct terminal *t)
+{
+ if (t->getty_argv)
+ {
+ free (t->getty_argv[0]);
+ free (t->getty_argv);
+ }
+ if (t->window_argv)
+ {
+ free (t->window_argv[0]);
+ free (t->window_argv);
+ }
+}
+
+/* Set up the getty and window fields of terminal spec T corresponding
+ to line TT. */
+static void
+setup_terminal (struct terminal *t, struct ttyent *tt)
+{
+ free_argvs (t);
+
+ if ((tt->ty_status & TTY_ON) && tt->ty_getty)
+ {
+ char **make_args (const char *line)
+ {
+ int argc;
+ char *argz, **argv;
+ size_t len;
+ argz_create_sep (line, ' ', &argz, &len);
+ argc = argz_count (argz, len);
+ argv = malloc ((argc + 1) * sizeof (char *));
+ if (argv == 0)
+ error (0, ENOMEM,
+ "cannot allocate argument vector for %s", t->name);
+ else
+ argz_extract (argz, len, argv);
+ return argv;
+ }
+
+ char *line;
+ asprintf (&line, "%s %s", tt->ty_getty, tt->ty_name);
+ if (line == 0)
+ {
+ error (0, ENOMEM,
+ "cannot allocate arguments for %s", t->name);
+ t->getty_argv = 0;
+ }
+ else
+ {
+ t->getty_argv = make_args (line);
+ free (line);
+ }
+ t->window_argv = tt->ty_window ? make_args (tt->ty_window) : 0;
+ }
+ else
+ t->getty_argv = t->window_argv = 0;
+}
+
+
+/* Add a new terminal spec for TT and return it. */
+static struct terminal *
+add_terminal (struct ttyent *tt)
+{
+ struct terminal *t;
+
+ if (nttys >= ttyslen)
+ {
+ struct terminal *newttys = realloc (ttys,
+ (ttyslen * 2) * sizeof ttys[0]);
+ if (newttys == 0)
+ {
+ error (0, ENOMEM, "cannot expand terminals table past %d", ttyslen);
+ return 0;
+ }
+ else
+ {
+ ttys = newttys;
+ memset (&ttys[nttys], 0, ttyslen);
+ ttyslen *= 2;
+ }
+ }
+
+ t = &ttys[nttys];
+ t->name = strdup (tt->ty_name);
+ if (t->name == 0)
+ {
+ error (0, ENOMEM, "cannot allocate entry for %s", tt->ty_name);
+ return 0;
+ }
+
+ nttys++;
+ setup_terminal (t, tt);
+ if (t->getty_argv)
+ t->on = 1;
+
+ return t;
+}
+
+/* Read /etc/ttys and initialize ttys array. Return non-zero if we fail. */
+int
+init_ttys (void)
+{
+ struct ttyent *tt;
+
+ ttyslen = 10;
+ nttys = 0;
+
+ ttys = calloc (ttyslen, sizeof ttys[0]);
+ if (ttys == 0)
+ error (2, ENOMEM, "cannot allocate table");
+
+ if (!setttyent ())
+ {
+ error (0, errno, "%s", _PATH_TTYS);
+ return 1;
+ }
+ while ((tt = getttyent ()))
+ {
+ if (!tt->ty_name)
+ continue;
+
+ add_terminal (tt);
+ }
+
+ endttyent ();
+ return 0;
+}
+
+/* Free everything in the terminal array */
+void
+free_ttys (void)
+{
+ int i;
+
+ for (i = 0; i < nttys; i++)
+ {
+ free_argvs (&ttys[i]);
+ free (ttys[i].name);
+ }
+ free (ttys);
+}
+
+/* Start a child process. */
+static pid_t
+run (char **argv, int do_setsid)
+{
+ pid_t pid;
+
+ pid = fork ();
+ if (pid < 0)
+ {
+ error (0, errno, "fork");
+ return 0;
+ }
+
+ if (pid > 0)
+ return pid;
+ else
+ {
+ if (do_setsid && setsid () == -1)
+ error (0, errno, "setsid");
+
+ errno = 0;
+ execv (argv[0], argv);
+ error (127, errno, "%s", argv[0]);
+ }
+
+ /* NOTREACHED */
+ return -1;
+}
+
+
+/* Start line T. Return non-zero if we didn't actually start anything. */
+static int
+startup_terminal (struct terminal *t)
+{
+ pid_t pid;
+ assert (t->on);
+ assert (t->getty_argv);
+
+ if (t->window_argv)
+ {
+ pid = run (t->window_argv, 1);
+ if (!pid)
+ goto error;
+
+ sleep (WINDOW_DELAY);
+ }
+
+ pid = run (t->getty_argv, 0);
+ if (pid == 0)
+ {
+ error:
+ t->pid = 0;
+ t->on = 0;
+ return 1;
+ }
+ else
+ {
+ t->pid = pid;
+ return 0;
+ }
+}
+
+/* For each line in /etc/ttys, start up the specified program. Return
+ non-zero if we fail. */
+int
+startup_ttys (void)
+{
+ int i;
+ int didone, fail;
+
+ didone = 0;
+
+ for (i = 0; i < nttys; i++)
+ if (ttys[i].on)
+ {
+ fail = startup_terminal (&ttys[i]);
+ if (!fail)
+ didone = 1;
+ }
+ return !didone;
+}
+
+/* Find the terminal spec corresponding to line LINE. */
+static struct terminal *
+find_line (char *line)
+{
+ int i;
+
+ for (i = 0; i < nttys; i++)
+ if (!strcmp (ttys[i].name, line))
+ return &ttys[i];
+ return 0;
+}
+
+/* PID has just exited; restart the terminal it's on if necessary. */
+void
+restart_terminal (pid_t pid)
+{
+ int i;
+
+ for (i = 0; i < nttys; i++)
+ if (pid == ttys[i].pid)
+ {
+ if (logout (ttys[i].name))
+ logwtmp (ttys[i].name, "", "");
+ ttys[i].pid = 0;
+ if (ttys[i].on)
+ startup_terminal (&ttys[i]);
+ }
+}
+
+/* Shutdown the things running on terminal spec T. */
+static void
+shutdown_terminal (struct terminal *t)
+{
+ kill (t->pid, SIGHUP);
+ revoke (t->name);
+}
+
+/* Re-read /etc/ttys. If a line has turned off, kill what's there.
+ If a line has turned on, start it. */
+void
+reread_ttys (void)
+{
+ struct ttyent *tt;
+ struct terminal *t;
+ int on;
+ int i;
+
+ if (!setttyent ())
+ {
+ error (0, errno, "%s", _PATH_TTYS);
+ return;
+ }
+
+ while ((tt = getttyent ()))
+ {
+ if (!tt->ty_name)
+ continue;
+
+ t = find_line (tt->ty_name);
+ on = tt->ty_getty && (tt->ty_status & TTY_ON);
+
+ if (t)
+ {
+ if (t->on && !on)
+ {
+ t->on = 0;
+ shutdown_terminal (t);
+ }
+ else if (!t->on && on)
+ {
+ t->on = 1;
+ setup_terminal (t, tt);
+ startup_terminal (t);
+ }
+ }
+ else
+ {
+ t = add_terminal (tt);
+ if (t == 0)
+ continue;
+ if (on)
+ startup_terminal (t);
+ }
+
+ t->read = 1;
+ }
+ endttyent ();
+
+ /* Scan tty entries; any that were not found and were on, turn off. */
+ for (i = 0; i < nttys; i++)
+ {
+ if (!ttys[i].read && ttys[i].on)
+ {
+ ttys[i].on = 0;
+ shutdown_terminal (&ttys[i]);
+ }
+ ttys[i].read = 0; /* Clear flag for next time. */
+ }
+}
+
+
+
+/** Main program and signal handlers. **/
+
+static sig_atomic_t pending_hup;
+static void
+sighup (int signo)
+{
+ pending_hup = 1;
+}
+
+static sig_atomic_t pending_term;
+static void
+sigterm (int signo)
+{
+ pending_term = 1;
+}
+
+#ifdef SIGLOST
+static void
+reopen_console (int signo)
+{
+ int fd;
+
+ close (0);
+ close (1);
+ close (2);
+
+ fd = open (_PATH_CONSOLE, O_RDWR);
+ if (fd < 0)
+ _exit (2);
+ if (fd != 0)
+ {
+ dup2 (fd, 0);
+ close (fd);
+ }
+ dup2 (0, 1);
+ dup2 (0, 2);
+}
+#endif
+
+int
+main ()
+{
+ int fail;
+ struct sigaction sa;
+
+ fail = init_ttys ();
+ if (fail)
+ return fail;
+
+ if (setsid () == -1)
+ error (0, errno, "setsid");
+
+ sa.sa_handler = sighup;
+ sa.sa_flags = 0; /* No SA_RESTART! */
+ sigemptyset(&sa.sa_mask);
+ if (sigaction (SIGHUP, &sa, NULL))
+ error (2, errno, "cannot set SIGHUP handler");
+ sa.sa_handler = sigterm;
+ if (sigaction (SIGTERM, &sa, NULL))
+ error (2, errno, "cannot set SIGTERM handler");
+
+#ifdef SIGLOST
+ /* We may generate SIGLOST signal from trying to talk to the console
+ after our port has been revoked or the term server has died. In that
+ case, reopen the console and restart. (Unfortunately this won't
+ restart the offending RPC on the new console port.) */
+ if (signal (SIGLOST, reopen_console) == SIG_ERR)
+ error (2, errno, "cannot set SIGLOST handler");
+#endif
+
+ /* Start up tty lines. */
+ startup_ttys ();
+
+ /* We will spend the rest of our life waiting for children to die. */
+ while (1)
+ {
+ error_t waiterr;
+ pid_t pid = waitpid (WAIT_ANY, NULL, WUNTRACED);
+ waiterr = errno;
+
+ /* Elicit a SIGLOST now if the console (on our stderr, i.e. fd 2) has
+ died. That way, the next error message emitted will actually make
+ it out to the console if it can be made it work at all. */
+ write (2, "", 0);
+
+ /* If a SIGTERM or SIGHUP arrived recently, it set a flag
+ and broke us out of being blocked in waitpid. */
+
+ if (pending_term)
+ {
+ pending_term = 0;
+ error (3, 0, "Got SIGTERM");
+ }
+ if (pending_hup)
+ {
+ pending_hup = 0;
+ reread_ttys ();
+ }
+
+ if (pid < 0)
+ {
+ if (waiterr == EINTR) /* A signal woke us. */
+ continue;
+ error (1, waiterr, "waitpid");
+ }
+
+ assert (pid > 0);
+
+ /* We have reaped a dead child. Restart that tty line. */
+ restart_terminal (pid);
+ }
+}
diff --git a/defpager/Makefile b/defpager/Makefile
new file mode 100644
index 00000000..5b966c4e
--- /dev/null
+++ b/defpager/Makefile
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 1995, 1996 Free Software Foundation
+# Written by Michael I. Bushnell.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := defpager
+makemode := misc
+
+SRCS = defpager.c
+
+subst-functions=__syscall_vm_allocate syscall_vm_allocate \
+ __vm_allocate_rpc vm_allocate_rpc \
+ __syscall_vm_map syscall_vm_map \
+ __vm_map_rpc vm_map_rpc
+comma=,
+LDFLAGS=-Wl,$(subst :,$(comma),$(strip $(subst-functions)))
+
+include ../Makeconf
diff --git a/defpager/backing.c b/defpager/backing.c
new file mode 100644
index 00000000..7383c915
--- /dev/null
+++ b/defpager/backing.c
@@ -0,0 +1,130 @@
+/* Backing store management for GNU Hurd.
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <hurd/store.h>
+
+const struct store_class *const permitted_classes[] =
+{
+ &store_device_class, &store_ileave_class, &store_concat_class, 0
+};
+
+/* Allocation map, by PAGE. */
+/* If a bit is SET the corresponding PAGE is free. */
+char *bmap;
+
+/* Number of bytes in bmap */
+size_t bmap_len;
+
+/* Allocation rotor */
+char *bmap_rotor;
+
+pthread_mutex_t bmap_lock = PTHREAD_MUTEX_INITIALIZER;
+
+error_t
+init_backing (char *name)
+{
+ error_t err;
+ int i;
+
+ err = store_open (name, STORE_NO_FILEIO, &permitted_classes, &backing_store);
+ if (err)
+ return err;
+
+ bmap_len = backing_store->size / vm_page_size / NBBY;
+ bmap = malloc (bmap_len);
+ for (i = 0; i < bmap_len; i++)
+ bmap[i] = 0xff;
+ bmap_rotor = bmap;
+
+ /* Mark the very first page as occupied. This makes sure we never
+ return zero offsets from allocate_backing_page (which
+ conventionally means that there is no space left. It also makes
+ sure we don't tromp on the misfeature in Linux of using the first
+ page for permanent data. */
+ *bmap_rotor |= 1;
+}
+
+int
+allocate_backing_page ()
+{
+ int wrapped;
+ int bit;
+ int pfn;
+
+ pthread_mutex_lock (&bmap_lock);
+
+ wrapped = (bmap_rotor == bmap);
+
+ while (!wrapped || bmap_rotor < bmap + bmap_len)
+ {
+ if (bmap[bmap_rotor])
+ break;
+ bmap_rotor++;
+ if (bmap_rotor >= bmap + bmap_len)
+ wrapped++;
+ }
+
+ if (wrapped == 2)
+ {
+ /* Didn't find one... */
+ pthread_mutex_unlock (&bmap_lock);
+ printf ("WARNING: Out of paging space; pageout failing.");
+ return 0;
+ }
+
+ /* Find which bit */
+ bit = ffs (*bmap_rotor);
+ assert (bit);
+ bit--;
+
+ /* Mark it */
+ *bmap_rotor |= 1 << bit;
+
+ /* Return the correct offset */
+ pfn = (bmap_rotor - bmap) * 8 + bit;
+
+ pthread_mutex_unlock (&bmap_lock);
+
+ return pfn * (vm_page_size / store->block_size);
+}
+
+
+void
+return_backing_pages (off_t *map, int maplen)
+{
+ int i;
+
+ pthread_mutex_lock (&bmap_lock);
+ for (i = 0; i < maplen; i++)
+ {
+ int pfn;
+ char *b;
+ int bit;
+
+ pfn = map[i] / (vm_page_size / store->block_size);
+ b = bmap + pfn & ~7;
+ bit = pfn & 7;
+
+ assert ((*b & (1 << bit)) == 0);
+ *b |= 1 << bit;
+ }
+ pthread_mutex_unlock (&bmap_lock);
+}
diff --git a/defpager/defpager.c b/defpager/defpager.c
new file mode 100644
index 00000000..3a824cf1
--- /dev/null
+++ b/defpager/defpager.c
@@ -0,0 +1,136 @@
+/* Default pager for GNU Hurd.
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <hurd/pager.h>
+#include <hurd/store.h>
+
+struct user_pager_info
+{
+ /* Size of the object */
+ vm_size_t size;
+
+ /* One entry for each page of the object. */
+ off_t *map;
+};
+
+/* Expand the P->map as necessary to handle an incoming request of the
+ page at ADDR. */
+static inline void
+expand_map (struct user_pager_info *p, vm_offset_t addr)
+{
+ /* See if this is beyond the current extent */
+ if (page >= pager->size)
+ {
+ off_t *newmap;
+ vm_size_t newsize;
+
+ newsize = page + vm_page_size;
+ newmap = realloc (pager->map, size / vm_page_size * sizeof (off_t));
+
+ bzero (pager->map + pager->size / vm_page_size * sizeof (off_t),
+ (newsize - pager->size) / vm_page_size * sizeof (off_t));
+ pager->size = newsize;
+ pager->map = newmap;
+ }
+}
+
+error_t
+pager_read_page (struct user_pager_info *pager,
+ vm_offset_t page,
+ vm_address_t *buf,
+ int *write_lock)
+{
+ int pfn = page / vm_page_size;
+ size_t nread;
+
+ /* We never request write locks. */
+ *write_lock = 0;
+
+ expand_map (pager, page);
+
+ if (!pager->map[pfn])
+ vm_allocate (mach_task_self (), buf, vm_page_size, 1);
+ else
+ {
+ store_read (backing_store, pager->map[pfn], vm_page_size,
+ (void **)buf, &nread);
+ if (nread != vm_page_size)
+ {
+ munmap ((caddr_t) *buf, nread);
+ return EIO;
+ }
+ }
+ return 0;
+}
+
+
+error_t
+pager_write_page (struct user_pager_info *pager,
+ vm_offset_t page,
+ vm_address_t buf)
+{
+ int pfn = page / vm_page_size;
+ size_t nwritten;
+
+ expand_map (pager, page);
+
+ if (!pager->map[pfn])
+ pager->map[pfn] = allocate_backing_page ();
+
+ /* No more backing store. Oh dear. */
+ if (!pager->map[pfn])
+ return EIO;
+
+ err = store_write (backing_store, pager->map[pfn], (void *) buf,
+ vm_page_size, &nwritten);
+ if (!err && nwritten != vm_page_size)
+ err = EIO;
+ return err;
+}
+
+error_t
+pager_unlock_page (struct user_pager_info *pager,
+ vm_offset_t address)
+{
+ return 0;
+}
+
+error_t
+pager_report_extent (struct user_pager_info *pager,
+ vm_address_t *offset,
+ vm_size_t *size)
+{
+ *offset = 0;
+ *size = pager->size;
+ return 0;
+}
+
+void
+pager_clear_user_data (struct user_pager_info *pager)
+{
+ return_backing_pages (pager->map, pager->size / vm_page_size);
+ free (pager->map);
+}
+
+void
+pager_dropweak (struct user_pager_info *pager)
+{
+}
diff --git a/defpager/wiring.c b/defpager/wiring.c
new file mode 100644
index 00000000..dda5d9d5
--- /dev/null
+++ b/defpager/wiring.c
@@ -0,0 +1,111 @@
+/* Memory wiring functions for default pager
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file uses the "wrap" feature of GNU ld. See the GNU ld
+ documentation for more information on how this works.
+ The list of functions we wrap is specified in Makefile as
+ $(subst-functions). */
+
+
+error_t
+__wrap___syscall_vm_allocate (task_t target_task,
+ vm_address_t *address,
+ vm_size_t size,
+ boolean_t anywhere)
+{
+ error_t err;
+
+ err = __real___syscall_vm_allocate (target_task, address, size, anywhere);
+ if (!err && target_task == mach_task_self ())
+ wire_segment (*address, size);
+ return err;
+}
+
+error_t
+__wrap___vm_allocate_rpc (task_t target_task,
+ vm_address_t *address,
+ vm_size_t size,
+ boolean_t anywhere)
+{
+ error_t err;
+
+ err = __real___vm_allocate_rpc (target_task, address, size, anywhere);
+ if (!err && target_task == mach_task_self ())
+ wire_segment (*address, size);
+ return err;
+}
+
+error_t
+__wrap___syscall_vm_map (mach_port_t target_task,
+ vm_address_t *address,
+ vm_size_t size,
+ vm_address_t mask,
+ boolean_t anywhere,
+ mach_port_t memory_object,
+ vm_offset_t offset,
+ boolean_t copy,
+ vm_prot_t cur_protection,
+ vm_prot_t max_protection,
+ vm_inherit_t inheritance)
+{
+ error_t err;
+
+ err = __real___syscall_vm_map (target_task, address, size, mask, anywhere,
+ memory_object, offset, copy, cur_protection,
+ max_protection, inheritance);
+ if (!err && target_task == mach_task_self ())
+ wire_segment (*address, size);
+ return err;
+}
+
+
+error_t
+__wrap___vm_map_rpc (mach_port_t target_task,
+ vm_address_t *address,
+ vm_size_t size,
+ vm_address_t mask,
+ boolean_t anywhere,
+ mach_port_t memory_object,
+ vm_offset_t offset,
+ boolean_t copy,
+ vm_prot_t cur_protection,
+ vm_prot_t max_protection,
+ vm_inherit_t inheritance)
+{
+ error_t err;
+
+ err = __real___vm_map_rpc (target_task, address, size, mask, anywhere,
+ memory_object, offset, copy, cur_protection,
+ mak_protection, inheritance);
+ if (!err && target_task == mach_task_self ())
+ wire_segment (*address, size);
+ return err;
+}
+
+/* And the non-__ versions too. */
+
+#define weak_alias(name,aliasname) \
+ extern typeof (name) aliasname __attribute__ ((weak, alias (#name)));
+
+weak_alias (__wrap___vm_map_rpc, __wrap_vm_map_rpc)
+weak_alias (__wrap___syscall_vm_map, __wrap_syscall_vm_map)
+weak_alias (__wrap___vm_allocate_rpc, __wrap_vm_allocate_rpc)
+weak_alias (__wrap___syscall_vm_allocate, __wrap_syscall_vm_allocate)
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 00000000..5f3dadac
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1,3 @@
+/*.info
+/*.info-*
+/version.texi
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 00000000..2a75803b
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,55 @@
+# Copyright (C) 1994, 1998, 1999, 2003, 2012, 2013 Free Software Foundation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := doc
+makemode := misc
+
+targets = hurd.info $(wildcard hurd.info-*) version.texi
+installationdir = $(infodir)
+
+DISTFILES = $(targets)
+
+DVIPS = dvips
+
+include ../Makeconf
+
+# For each .info file we need a .d file.
+-include $(patsubst %.info,%.d,$(filter %.info,$(targets))) /dev/null
+
+# Build dependencies from included files.
+%.d: %.texi
+ set -e; (echo "$*.info $*.dvi: \\"; grep '^@include ' $< | \
+ sed -e 's/^[^ ]*[ ]*\([^ ]*\).*$$/ \1 \\/'; \
+ echo) > $@.new
+ mv -f $@.new $@
+
+%.info: %.texi
+ @rm -f $@ $@-[0-9] $@-[0-9][0-9]
+ $(MAKEINFO) -I $(@D) -I $(<D) $<
+
+.PRECIOUS: %.dvi
+%.dvi: %.texi
+ TEXINPUTS=$(srcdir):$$TEXINPUTS \
+ MAKEINFO='$(MAKEINFO) -I $(srcdir)' $(TEXI2DVI) $<
+
+%.ps: %.dvi
+ $(DVIPS) $< -o $@
+
+version.texi: stamp-version; @:
+stamp-version: $(..)config.make
+ echo '@set VERSION $(package-version)' > version.texi.new
+ $(move-if-change) version.texi.new version.texi
+ touch $@
diff --git a/doc/gpl.texinfo b/doc/gpl.texinfo
new file mode 100644
index 00000000..d29870cf
--- /dev/null
+++ b/doc/gpl.texinfo
@@ -0,0 +1,395 @@
+@node Copying
+@section GNU GENERAL PUBLIC LICENSE
+@center Version 2, June 1991
+
+@display
+Copyright @copyright{} 1989, 1991 Free Software Foundation, Inc.
+59 Temple Place -- Suite 330, Boston, MA 02111-1307, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+@end display
+
+@unnumberedsec Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software---to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+@iftex
+@unnumberedsec TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+@end iftex
+@ifinfo
+@center TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+@end ifinfo
+
+@enumerate
+@item
+This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The ``Program'', below,
+refers to any such program or work, and a ``work based on the Program''
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term ``modification''.) Each licensee is addressed as ``you''.
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+@item
+You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+@item
+You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+@enumerate a
+@item
+You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+@item
+You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+@item
+If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+@end enumerate
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+@item
+You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+@enumerate a
+@item
+Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+@item
+Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+@item
+Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+@end enumerate
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+@item
+You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+@item
+You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+@item
+Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+@item
+If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+@item
+If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+@item
+The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and ``any
+later version'', you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+@item
+If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+@iftex
+@heading NO WARRANTY
+@end iftex
+@ifinfo
+@center NO WARRANTY
+@end ifinfo
+
+@item
+BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM ``AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+@item
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+@end enumerate
+
+@iftex
+@heading END OF TERMS AND CONDITIONS
+@end iftex
+@ifinfo
+@center END OF TERMS AND CONDITIONS
+@end ifinfo
+
+@page
+@unnumberedsec How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the ``copyright'' line and a pointer to where the full notice is found.
+
+@smallexample
+@var{one line to give the program's name and an idea of what it does.}
+Copyright (C) 19@var{yy} @var{name of author}
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+@end smallexample
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+@smallexample
+Gnomovision version 69, Copyright (C) 19@var{yy} @var{name of author}
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
+type `show w'. This is free software, and you are welcome
+to redistribute it under certain conditions; type `show c'
+for details.
+@end smallexample
+
+The hypothetical commands @samp{show w} and @samp{show c} should show
+the appropriate parts of the General Public License. Of course, the
+commands you use may be called something other than @samp{show w} and
+@samp{show c}; they could even be mouse-clicks or menu items---whatever
+suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a ``copyright disclaimer'' for the program, if
+necessary. Here is a sample; alter the names:
+
+@smallexample
+@group
+Yoyodyne, Inc., hereby disclaims all copyright
+interest in the program `Gnomovision'
+(which makes passes at compilers) written
+by James Hacker.
+
+@var{signature of Ty Coon}, 1 April 1989
+Ty Coon, President of Vice
+@end group
+@end smallexample
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/doc/hurd.texi b/doc/hurd.texi
new file mode 100644
index 00000000..07ddfb46
--- /dev/null
+++ b/doc/hurd.texi
@@ -0,0 +1,4634 @@
+\input texinfo @c -*-texinfo-*-
+@setfilename hurd.info
+
+@c FIXME: might it be useful to have a glossary?
+@c tb: yes, indeed. very much so. If you provide a list of words,
+@c tb: then I'll write definitions.
+
+@c FIXME: Please use the active voice whenever possible. There are
+@c a lot of sentences here that use passive voice, and it's therefore
+@c hard for me to tell who is doing what to whom. I have not changed
+@c those sentences because I did not know what to make the subject and
+@c what the object.
+@c tb: can you mark the unclear sentences so I can fix them up?
+
+@c FIXME: Be consistent with mood -- that is, when writing the
+@c descriptions of functions, either write them all as declaratives (as
+@c in "This function twiddles the frobs.") or as imperatives (as in
+@c "Twiddle the frobs."). As this now stands, some function definitions
+@c are imperative (such as ports_reallocate_port: `Destroy the receive right'), and some
+@c are declarative (such as ihash_set_cleanup: `Sets @var{ht}'s element
+@c cleanup function'). This inconsistency is confusing. A particularly
+@c confusing example is the description of `pager_unlock_page': it reads
+@c `A page should be made writable.'
+@c tb: This is, I think, a consequence of many of the sections having
+@c tb: been written by cut-and-paste from the header files (which is a
+@c tb: decent way to start, but the results do need patching up).
+
+@c FIXME: Might we want to use some sort of highlighting when we
+@c refer to libraries by an abbreviated version of their name? For
+@c example, we often refer to `fshelp', by
+@c which we mean the library `libfshelp.a'. On those few occasions when
+@c we bother to spell out its full name, we use `@code', as we should;
+@c but when we abbreviate the name to `fshelp', we use no highlighting
+@c at all. These un-highlighted abbreviated names look odd to me.
+@c tb: Yes, perhaps so. We should consult a Texinfo god for advice.
+
+@c FIXME: I think we should say `zero' instead of `NULL' or `NUL'.
+@c Currently, this document uses all three, which is confusing.
+@c tb: I see no uses of "NULL". "null" is used, as an adjective,
+@c tb: which is synonymous with "zero" for pointers, but has different
+@c tb: connotations. "NUL" is an ASCII character, and is explicitly
+@c tb: used only as such.
+
+
+@c FIXME: This document sometimes says something MUST be
+@c such-and-such, and other times says something SHOULD be
+@c such-and-such. It's not clear if you're using `must' and `should'
+@c interchangeably, or if instead the really mean different things.
+@c (Similarly for `may', `do', and `does'.) Also, when we say something
+@c MUST be such-and-such, I for one always wonder `what happens if it
+@c isn't'? For example, the description of `diskfs_create_protid' says
+@c `The node @code{@var{po}->np} must be locked.' I wonder `what
+@c happens if the node isn't locked?' I imagine other programmers will
+@c wonder that too; in that case, perhaps the description should say
+@c `The node @code{@var{po}->np} must be locked; otherwise the function
+@c returns ENAUGHTY'. A particularly confusing example is the paragraph
+@c in subsection I/O Object Ports, which begins `The uid and gid sets
+@c associated with a port may not be visibly shared with other ports,
+@c nor may they ever change. The server must fix the identification of
+@c a set of uids and gids with a particular port at the moment of the
+@c port's creation.'
+@c tb: If the node is not locked on entry to diskfs_create_protid,
+@c tb: then the user (the program linking against libdiskfs) is
+@c tb: violating the interface, and the results are Undefined. The
+@c tb: resulting filesystem will experience difficult-to-trace and
+@c tb: apparently random crashes and data corruption.
+@c tb: We don't WANT such functions to have to check for and return
+@c tb: error codes any more than we want scanf to try and diagnose
+@c tb: stray pointers. But this does not mean that all things are as
+@c tb: clear as they should be, either. "must" means "if you don't do
+@c tb: this, then you are violating the interface". "should" often
+@c tb: means the same same thing, sometimes it's looser. The real
+@c tb: issue here is that we should define exactly what the
+@c tb: consequences of violating an interface are. In the case of
+@c tb: library interfaces, it means that the resulting program's
+@c tb: behavior is undefined. In the case of server interfaces, it
+@c tb: means that one has Broken The Rules and that other programs
+@c tb: will behave in correspondingly bad ways. In any case, some
+@c tb: careful auditing and editing of this kind of thing needs to
+@c tb: happen, but not until we have written more actual text.
+
+
+
+@c Get the Hurd version we are documenting.
+@include version.texi
+
+@c Unify all our little indices for now.
+@defcodeindex sc
+@syncodeindex sc cp
+@syncodeindex fn cp
+@syncodeindex vr cp
+@syncodeindex tp cp
+@syncodeindex pg cp
+
+@dircategory Kernel
+@direntry
+* Hurd: (hurd). Using and programming the Hurd kernel servers.
+@end direntry
+
+@copying
+This file documents the GNU Hurd kernel component. This edition of the
+documentation was last updated for version @value{VERSION} of the Hurd.
+
+Copyright @copyright{} 1994, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
+2004, 2005, 2007, 2008, 2009 Free Software Foundation, Inc.
+
+@quotation
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@ignore
+Permission is granted to process this file through TeX and print the
+results, provided the printed document carries a copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided also that
+the entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions.
+@end quotation
+@end copying
+
+@setchapternewpage off
+@settitle Hurd Reference Manual
+
+@titlepage
+@finalout
+@title The GNU Hurd Reference Manual
+@author Thomas Bushnell, BSG
+@author Gordon Matzigkeit
+@page
+
+@vskip 0pt plus 1filll
+@insertcopying
+@end titlepage
+
+@ifnottex
+@node Top
+@top The GNU Hurd
+
+@insertcopying
+@end ifnottex
+
+@menu
+* Introduction:: How to use this manual.
+* Bootstrap:: Turning a computer into a Hurd machine.
+* Foundations:: Basic features used throughout the Hurd.
+* Input and Output:: Reading and writing I/O channels.
+* Files:: Regular file and directory nodes.
+* Special Files:: Files with unusual Unix-compatible semantics.
+* Stores:: Generalized units of storage.
+* Stored Filesystems:: Filesystems for physical media.
+* Twisted Filesystems:: Providing new hierarchies for existing data.
+* Distributed Filesystems:: Sharing files between separate machines.
+* Networking:: Interconnecting with other machines.
+* Terminal Handling:: Helping people interact with the Hurd.
+* Running Programs:: Program execution and process management.
+* Authentication:: Verifying user and server privileges.
+* Index:: Guide to concepts, functions, and files.
+
+@detailmenu
+ --- The Detailed Node Listing ---
+
+Introduction
+
+* Audience:: The people for whom this manual is written.
+* Features:: Reasons to install and use the Hurd.
+* Overview:: Basic architecture of the Hurd.
+* History:: How the Hurd was born.
+* Copying:: The Hurd is free software.
+
+Bootstrap
+
+* Bootloader:: Starting the microkernel, or other OSes.
+* Server Bootstrap:: Waking up the Hurd.
+* Shutdown:: Letting the Hurd get some rest.
+
+Server Bootstrap
+
+* Recursive Bootstrap:: Running a Hurd under another Hurd.
+* Boot Scripts:: Describing server bootstrap relationships.
+* Invoking boot:: How to use the boot program.
+
+Foundations
+
+* Threads Library:: Every Hurd server and library is multithreaded.
+* Ports Library:: Managing server port receive rights.
+* Integer Hash Library:: Integer-keyed hash tables.
+* Misc Library:: Things that soon will be in the GNU C library.
+* Bug Address Library:: Where to report Hurd bugs.
+
+Ports Library
+
+* Buckets and Classes:: Basic units of port organization.
+* Port Rights:: Moving port rights to and from @code{libports}.
+* Port Metadata:: Managing port-related information.
+* Port References:: Guarding against leaks and lossage.
+* RPC Management:: Locking and interrupting RPC operations.
+
+Input and Output
+
+* Iohelp Library:: I/O authentication and lock management.
+* Pager Library:: Implementing multithreaded external pagers.
+* I/O Interface:: RPC-based input/output channels.
+
+Iohelp Library
+
+* I/O Users:: User authentication management.
+* Conch Management:: Deprecated shared I/O implementation.
+
+Pager Library
+
+* Pager Management:: High-level interface to external pagers.
+* Pager Callbacks:: Functions that the user must define.
+
+I/O Interface
+
+* I/O Object Ports:: How ports to I/O objects work.
+* Simple Operations:: Read, write, and seek.
+* Open Modes:: State bits that affect pieces of operation.
+* Asynchronous I/O:: How to be notified when I/O is possible.
+* Information Queries:: How to implement @code{io_stat} and
+ @code{io_server_version}.
+* Mapped Data:: Getting memory objects referring to the
+ data of an I/O object.
+
+Files
+
+* Translators:: Extending the Hurd filesystem hierarchy.
+* Trivfs Library:: Implementing single-file translators.
+* Fshelp Library:: Miscellaneous generic filesystem routines.
+* File Interface:: File ports implement the file interface.
+* Filesystem Interface:: Translator control interface.
+
+Translators
+
+* Invoking settrans:: Declaring how a node should be translated.
+* Invoking showtrans:: Displaying how nodes are translated.
+* Invoking mount:: Unix-compatible active filesystem translators.
+* Invoking fsysopts:: Modifying translation parameters at runtime.
+
+Trivfs Library
+
+* Trivfs Startup:: Writing a simple trivfs-based translator.
+* Trivfs Callbacks:: Mandatory user-defined trivfs functions.
+* Trivfs Options:: Optional user-defined trivfs functions.
+* Trivfs Ports:: Managing control and protid ports.
+
+Fshelp Library
+
+* Passive Translator Linkage:: Invoking passive translators.
+* Active Translator Linkage:: Managing active translators.
+* Fshelp Locking:: Implementing file locking.
+* Fshelp Permissions:: Standard file access permission policies.
+* Fshelp Misc:: Useful standalone routines.
+
+File Interface
+
+* File Overview:: Basic concepts for the file interface.
+* Changing Status:: Changing the owner (etc.) of a file.
+* Program Execution:: Executing files.
+* File Locking:: Implementing the @code{flock} call.
+* File Frobbing:: Other active calls on files.
+* Opening Files:: Looking up files in directories.
+* Modifying Directories:: Creating and deleting nodes.
+* Notifications:: File and directory change callbacks.
+* File Translators:: How to set and get translators.
+
+Stores
+
+* Store Library:: An abstract interface to storage systems.
+
+Store Library
+
+* Store Arguments:: Parsing store command-line arguments.
+* Store Management:: Creating and manipulating stores.
+* Store I/O:: Reading and writing data to stores.
+* Store Classes:: Ready-to-use storage backends.
+* Store RPC Encoding:: Transferring store descriptors via RPC.
+
+Stored Filesystems
+
+* Repairing Filesystems:: Recovering from minor filesystem crashes.
+* Linux Extended 2 FS:: The popular Linux filesystem format.
+* ISO-9660 CD-ROM FS:: Standard CD-ROM format.
+* Diskfs Library:: Implementing new filesystem servers.
+
+Diskfs Library
+
+* Diskfs Startup:: Initializing stored filesystems.
+* Diskfs Arguments:: Parsing command-line arguments.
+* Diskfs Globals:: Global behaviour modification.
+* Diskfs Node Management:: Allocation, reference counting, I/O,
+ caching, and other disk node routines.
+* Diskfs Callbacks:: Mandatory user-defined diskfs functions.
+* Diskfs Options:: Optional user-defined diskfs functions.
+* Diskfs Internals:: Reimplementing small pieces of diskfs.
+
+Distributed Filesystems
+
+* File Transfer Protocol:: A distributed filesystem based on FTP.
+* Network File System:: Sun's NFS: a lousy, but common filesystem.
+
+File Transfer Protocol
+
+* FTP Connection Library:: Managing remote FTP server connections.
+
+Networking
+
+* Socket Interface:: Network communication I/O protocol.
+
+Authentication
+
+* Auth Interface:: Auth ports implement the auth interface.
+
+Auth Interface
+
+* Auth Protocol:: Bidirectional authentication.
+
+@end detailmenu
+@end menu
+
+
+@node Introduction
+@chapter Introduction
+
+The GNU Hurd@footnote{The name @dfn{Hurd} stands for ``Hird of
+Unix-Replacing Daemons.'' The name @dfn{Hird} stands for ``Hurd of
+Interfaces Representing Depth.''} is the GNU Project's replacement for
+the Unix kernel. The Hurd is a collection of servers that run on the
+Mach microkernel to implement file systems, network protocols, file
+access control, and other features that are normally implemented by the
+Unix kernel or similar kernels (such as Linux).
+
+@c FIXME: Might we want to define `server' and `Mach' in a
+@c glossary, and refer to those definitions here?
+
+@menu
+* Audience:: The people for whom this manual is written.
+* Features:: Reasons to install and use the Hurd.
+* Overview:: Basic architecture of the Hurd.
+* History:: How the Hurd was born.
+* Copying:: The Hurd is free software.
+@end menu
+
+
+@node Audience
+@section Audience
+
+This manual is designed to be useful to everybody who is interested in
+using, administering, or programming the Hurd.
+
+If you are an end-user and you are looking for help on running the Hurd,
+the first few chapters of this manual describe the essential parts of
+installing, starting up, and shutting down a Hurd workstation. If you
+need help with a specific program, the best way to use this manual is to
+find the program's name in the index and go directly to the appropriate
+section. You may also wish to try running @kbd{@var{program} --help},
+which will display a brief usage message for @var{program}
+(@pxref{Foundations}).
+
+The rest of this manual is a technical discussion of the Hurd servers
+and their implementation, and would not be helpful until you want to
+learn how to modify the Hurd.
+
+This manual is organized according to the subsystems of the Hurd, and
+each chapter begins with descriptions of utilities and servers that are
+related to that subsystem. If you are a system administrator, and you
+want to learn more about, say, the Hurd networking subsystem, you can
+skip to the networking chapter (@pxref{Networking}), and read about the
+related utilities and servers.
+
+Programmers who are interested in learning how to modify Hurd servers, or
+write new ones, should begin by learning about a microkernel to which the
+Hurd has been ported (currently only GNU Mach) and reading
+@ref{Foundations}. You should then familiarize yourself with a
+subsystem that interests you by reading about existing servers and the
+libraries they use. At that point, you should be able to study the
+source code of existing Hurd servers and understand how they use the
+Hurd libraries.
+
+The final level of mastery is learning the about the RPC interfaces
+which the Hurd libraries implement. The last section of each chapter
+describes any Hurd interfaces used in that subsystem. Those sections
+assume that you are perusing the referenced interface definitions as you
+read. After you have understood a given interface, you will be in a
+good position to improve the Hurd libraries, design your own interfaces,
+and implement new subsystems.
+
+
+@node Features
+@section Features
+
+The Hurd is not the most advanced operating system known to the planet
+(yet), but it does have a number of enticing features:
+
+@table @asis
+@item it's free software
+Anybody can use, modify, and redistribute it under the terms of the GNU
+General Public License (@pxref{Copying}). The Hurd is part of the GNU
+system, which is a complete operating system licensed under the GPL.
+
+@item it's compatible
+The Hurd provides a familiar programming and user environment. For all
+intents and purposes, the Hurd is a modern Unix-like kernel. The Hurd
+uses the GNU C Library, whose development closely tracks standards such
+as ANSI/ISO, BSD, POSIX, Single Unix, SVID, and X/Open.
+
+@item it's built to survive
+Unlike other popular kernel software, the Hurd has an object-oriented
+structure that allows it to evolve without compromising its design.
+This structure will help the Hurd undergo major redesign and
+modifications without having to be entirely rewritten.
+
+@item it's scalable
+The Hurd implementation is aggressively multithreaded so that it runs
+efficiently on both single processors and symmetric multiprocessors.
+The Hurd interfaces are designed to allow transparent network clusters
+(@dfn{collectives}), although this feature has not yet been implemented.
+
+@item it's extensible
+The Hurd is an attractive platform for learning how to become a kernel
+hacker or for implementing new ideas in kernel technology. Every part
+of the system is designed to be modified and extended.
+
+@item it's stable
+It is possible to develop and test new Hurd kernel components without
+rebooting the machine (not even accidentally). Running your own kernel
+components doesn't interfere with other users, and so no special system
+privileges are required. The mechanism for kernel extensions is secure
+by design: it is impossible to impose your changes upon other users
+unless they authorize them or you are the system administrator.
+
+@item it exists
+The Hurd is real software that works Right Now. It is not a research
+project or a proposal. You don't have to wait at all before you can
+start using and developing it.
+@end table
+
+
+@node Overview
+@section Overview
+
+An operating system kernel provides a framework for programs to share a
+computer's hardware resources securely and efficiently. This framework
+includes mechanisms for programs to communicate safely, even if they do
+not trust one another (@pxref{Ports Library}).
+
+The GNU Hurd divides up the work of the traditional kernel, and
+implements it in separate programs, or @dfn{kernel servers}. The Hurd
+formally defines the communication protocols that each of the servers
+understands, so that it is possible for different servers to implement
+the same interface.
+
+The GNU C Library provides a POSIX environment on the Hurd, by
+translating standard POSIX system calls into interactions with the
+appropriate Hurd server.
+
+
+@node History
+@section History
+
+Richard Stallman (RMS) started GNU in 1983, as a project to create a
+complete free operating system. In the text of the GNU Manifesto, he
+mentioned that there is a primitive kernel. In the first GNUsletter,
+Feb. 1986, he says that GNU's kernel is TRIX, which was developed at the
+Massachusetts Institute of Technology.
+
+By December of 1986, the Free Software Foundation (FSF) had ``started
+working on the changes needed to TRIX'' [Gnusletter, Jan. 1987].
+Shortly thereafter, the FSF began ``negotiating with Professor Rashid of
+Carnegie-Mellon University about working with them on the development of
+the Mach kernel'' [Gnusletter, June, 1987]. The text implies that the
+FSF wanted to use someone else's work, rather than have to fix TRIX.
+
+In [Gnusletter, Feb. 1988], RMS was talking about taking Mach and
+putting the Berkeley Sprite filesystem on top of it, ``after the parts
+of Berkeley Unix@dots{} have been replaced.''
+
+Six months later, the FSF is saying that ``if we can't get Mach, we'll
+use TRIX or Berkeley's Sprite.'' Here, they present Sprite as a
+full-kernel option, rather than just a filesystem.
+
+In January, 1990, they say ``we aren't doing any kernel work. It does
+not make sense for us to start a kernel project now, when we still hope
+to use Mach'' [Gnusletter, Jan. 1990]. Nothing significant occurs until
+1991, when a more detailed plan is announced:
+
+@display
+``We are still interested in a multi-process kernel running on top of
+Mach. The CMU lawyers are currently deciding if they can release Mach
+with distribution conditions that will enable us to distribute it. If
+they decide to do so, then we will probably start work. CMU has
+available under the same terms as Mach a single-server partial Unix
+emulator named Poe; it is rather slow and provides minimal
+functionality. We would probably begin by extending Poe to provide full
+functionality. Later we hope to have a modular emulator divided into
+multiple processes.'' [Gnusletter, Jan. 1991].
+@end display
+
+RMS explains the relationship between the Hurd and Linux in
+@uref{http://www.gnu.org/software/hurd/hurd-and-linux.html}, where he
+mentions that the FSF started developing the Hurd in 1990. As of
+[Gnusletter, Nov. 1991], the Hurd (running on Mach) is GNU's official
+kernel.
+
+
+@include gpl.texinfo
+
+
+@node Bootstrap
+@chapter Bootstrap
+
+Bootstrapping@footnote{The term @dfn{bootstrapping} refers to a Dutch
+legend about a boy who was able to fly by pulling himself up by his
+bootstraps. In computers, this term refers to any process where a
+simple system activates a more complicated system.} is the procedure by
+which your machine loads the microkernel and transfers control to the
+Hurd servers.
+
+
+@menu
+* Bootloader:: Starting the microkernel, or other OSes.
+* Server Bootstrap:: Waking up the Hurd.
+* Shutdown:: Letting the Hurd get some rest.
+@end menu
+
+@node Bootloader
+@section Bootloader
+
+The @dfn{bootloader} is the first software that runs on your machine.
+Many hardware architectures have a very simple startup routine which
+reads a very simple bootloader from the beginning of the internal hard
+disk, then transfers control to it. Other architectures have startup
+routines which are able to understand more of the contents of the hard
+disk, and directly start a more advanced bootloader.
+
+@cindex GRUB
+@cindex GRand Unified Bootloader
+GNU @dfn{GRUB}@footnote{The GRand Unified Bootloader, available
+from @uref{http://www.gnu.org/software/grub/}.} is the GNU bootloader.
+GNU GRUB provides advanced functionality, and is capable of loading several
+different kernels (such as Linux, DOS, and the *BSD family).
+
+From the standpoint of the Hurd, the bootloader is just a mechanism to
+get the microkernel running and transfer control to the Hurd servers.
+You will need to refer to your bootloader and microkernel documentation
+for more information about the details of this process.
+
+
+@node Server Bootstrap
+@section Server Bootstrap
+
+As it can be seen from the GNU GRUB configuration, the Hurd is
+bootstrapped by starting the GNU Mach microkernel and two programs:
+the root filesystem and the exec server.
+
+The @option{--multiboot-command-line} option tells the file system server that
+it is a root filesystem, which triggers it to run @command{/hurd/init} as PID
+1. @command{/hurd/init} starts the @command{/hurd/proc} and
+@command{/hurd/auth} servers. After the servers are launched
+@command{/hurd/init} starts the @command{/libexec/runsystem.sh} script to
+finish booting.
+
+After the Hurd has been booted, other sets of core Hurd servers can be
+started in parallel. They're called sub-Hurds or neighbor Hurds
+(@pxref{Recursive Bootstrap}).
+
+@menu
+* Recursive Bootstrap:: Running a Hurd under another Hurd.
+* Boot Scripts:: Describing server bootstrap relationships.
+* Invoking boot:: How to use the boot program.
+@end menu
+
+
+@node Recursive Bootstrap
+@subsection Recursive Bootstrap
+
+The @command{boot} program can be used to start a set of core Hurd servers
+while another Hurd is already running. You will rarely need to do this,
+and it requires superuser privileges to control the new Hurd (or allow
+it to access certain devices), but it is interesting to note that it can
+be done.
+
+Usually, you would make changes to only one server, and simply tell your
+programs to use it in order to test out your changes. This process can
+be applied even to the core servers. However, some changes have
+far-reaching effects, and so it is nice to be able to test those effects
+without having to reboot the machine.
+
+@c FIXME: `boot' synopsis
+
+Here are the steps you can follow to test out a new set of servers:
+
+@enumerate 1
+@item
+Create a pseudo-root device. Usually, you would do this by creating a
+new partition under your old Hurd, and initializing it with your
+favorite filesystem format. @command{boot} understands the regular
+@code{libstore} options (FIXME xref), so you may use a file or other
+store instead of a partition.
+
+@example
+$ @kbd{dd if=/dev/zero of=my-partition bs=1024k count=400}
+400+0 records in
+400+0 records out
+$ @kbd{mke2fs ./my-partition}
+mke2fs 1.18, 11-Nov-1999 for EXT2 FS 0.5b, 95/08/09
+my-partition is not a block special device.
+Proceed anyway? (y,n) @kbd{y}
+Filesystem label=
+OS type: GNU/Hurd
+Block size=1024 (log=0)
+Fragment size=1024 (log=0)
+102400 inodes, 409600 blocks
+20480 blocks (5.00%) reserved for the super user
+First data block=1
+50 block groups
+8192 blocks per group, 8192 fragments per group
+2048 inodes per group
+Superblock backups stored on blocks:
+ 8193, 24577, 40961, 57345, 73729, 204801, 221185, 401409
+
+Writing inode tables: done
+Writing superblocks and filesystem accounting information: done
+$
+@end example
+
+@item
+Copy the core servers, C library, your modified programs, and anything
+else you need onto the pseudo-root.
+
+@example
+$ @kbd{settrans -c ./my-root /hurd/ext2fs -r `pwd`/my-partition}
+$ @kbd{fsysopts ./my-root --writable}
+$ @kbd{cd my-root}
+$ @kbd{tar -zxpf /pub/debian/FIXME/gnu-20000929.tar.gz}
+$ @kbd{cd ..}
+$ @kbd{fsysopts ./my-root --readonly}
+@end example
+
+@item
+Create a new boot script (FIXME xref).
+
+@item
+Run @command{boot}.
+
+@example
+$ @kbd{boot -D ./my-boot ./my-boot/boot/servers.boot ./my-partition}
+[...]
+@end example
+
+@item
+Here is an example using a hard drive that already has a GNU/Hurd
+system installed on an ext2 filesystem on @file{/dev/hd2s1}.
+
+@example
+$ @kbd{settrans /mnt /hurd/ex2fs --readonly /dev/hd2s1}
+$ @kbd{boot -d -D /mnt -I /mnt/boot/servers.boot /dev/hd2s1}
+@end example
+
+@item
+See @pxref{Invoking boot} for help with boot.
+@end enumerate
+
+Note that it is impossible to share microkernel devices between the two
+running Hurds, so don't get any funny ideas. When you're finished
+testing your new Hurd, then you can run the @command{halt} or @command{reboot}
+programs to return control to the parent Hurd.
+
+@c FIXME: the `don't get any funny ideas' comment is confusing. Am
+@c I genuinely in some sort of danger if I contemplate sharing
+@c microkernel devices between two running Hurds?
+@c tb: not if you know what you are doing. But there is no clever
+@c device mediation going on. Two hurds, with two filesystems writing
+@c the same partition, will wreak havoc. Two hurds reading from the
+@c same terminal device will not share nicely.
+
+If you're satisfied with your new Hurd, you can arrange for your
+bootloader to start it, and reboot your machine. Then, you'll be in a
+safe place to overwrite your old Hurd with the new one, and reboot back
+to your old configuration (with the new Hurd servers).
+
+
+@node Boot Scripts
+@subsection Boot Scripts
+@pindex /boot/servers.boot
+@pindex servers.boot
+
+Boot Scripts are used to boot further Hurd systems in parallel to the first,
+and are parsed by @command{boot} to boot a sub-Hurd.
+@c See @file{/boot/servers.boot} for an example of a Hurd boot script.
+
+
+In that script, the variables @var{host-port} and @var{device-port} are
+integers which represent the microkernel host and device ports, respectively
+(and are used to initialize the @code{$@{host-port@}} and
+@code{$@{device-port@}} boot script variables). If these ports are not
+specified, then it is assumed that the Hurd is already running, and the current
+ports will be fetched from the procserver.
+@c (FIXME xref).
+
+@var{root-name} is the name of the microkernel device that should be used as
+the Hurd bootstrap filesystem.
+@c @code{serverboot} uses this name
+@c to locate the boot script (described above), and to initialize the
+@c @code{$@{root-device@}} script variable.
+
+
+FIXME: finish
+
+
+@node Invoking boot
+@subsection Invoking boot
+
+Usage: boot [@var{option}@dots{}] @var{boot-script} @var{device}@dots{}
+
+@table @code
+@item --kernel-command-line=@var{command line}
+@itemx -c
+Simulated multiboot command line to supply.
+
+@item --pause
+@itemx -d
+Pause for user confirmation at various times during booting.
+
+@item --boot-root=@var{dir}
+@itemx -D
+Root of a directory tree in which to find the files specified in
+@var{boot-script}.
+
+@item --interleave=@var{blocks}
+Interleave in runs of length @var{blocks}.
+
+@item --isig
+@itemx -I
+Do not disable terminal signals, so you can suspend and interrupt the
+boot program itself, rather than the programs running in the booted
+system.
+
+@item --layer
+@itemx -L
+Layer multiple devices for redundancy.
+
+@item --single-user
+@itemx -s
+Boot into single user mode.
+
+@item --store-type=@var{type}
+@itemx -T
+Each @var{device} names a store of type @var{type}.
+@end table
+
+Mandatory or optional arguments to long options are also mandatory or optional
+for any corresponding short options.
+
+If neither @option{--interleave} or @option{--layer} is specified, multiple
+@var{device}s are concatenated.
+
+
+@node Shutdown
+@section Shutdown
+@scindex halt
+@scindex reboot
+
+FIXME: finish
+
+
+@node Foundations
+@chapter Foundations
+
+Every Hurd program accepts the following optional arguments:
+
+@table @samp
+@item --help
+Display a brief usage message, then exit. This message is not a
+substitute for reading program documentation; rather, it provides useful
+reminders about specific command-line options that a program
+understands.
+
+@item --version
+Output program version information and exit.
+@end table
+
+The rest of this chapter provides a programmer's introduction to the
+Hurd. If you are not a programmer, then this chapter will not make much
+sense to you@dots{} you should consider skipping to descriptions of
+specific Hurd programs (@pxref{Audience}).
+
+The Hurd distribution includes many libraries in order to provide a
+useful set of tools for writing Hurd utilities and servers. Several of
+these libraries are useful not only for the Hurd, but also for writing
+microkernel-based programs in general. These fundamental libraries are
+not difficult to understand, and they are a good starting point, because
+the rest of the Hurd relies upon them quite heavily.
+
+@menu
+* Threads Library:: Every Hurd server and library is multithreaded.
+* Ports Library:: Managing server port receive rights.
+* Integer Hash Library:: Integer-keyed hash tables.
+* Misc Library:: Things that soon will be in the GNU C library.
+* Bug Address Library:: Where to report Hurd bugs.
+@end menu
+
+@node Threads Library
+@section Threads Library
+@scindex libthreads
+@scindex cthreads.h
+
+All Hurd servers and libraries are aggressively multithreaded in order
+to take full advantage of any multiprocessing capabilities provided by
+the microkernel and the underlying hardware. The Hurd threads library,
+@code{libthreads}, contains the default Hurd thread implementation, which
+is declared in @code{<cthreads.h>}.
+
+Currently (April 1998), the Hurd uses cthreads, which have already been
+documented thoroughly by CMU. Eventually, it will be migrated to use
+POSIX pthreads, which are documented in a lot of places.
+@c Thomas, 26-03-1998
+
+@c FIXME: it would be nice if we referred specifically to some of
+@c the places in which POSIX pthreads are documented.
+@c tb: yes, but alas we are only allowed to refer to free
+@c documentation, and IEEE Posix ain't that... ;-(
+
+Every single library in the Hurd distribution (including the GNU C
+library) is completely thread-safe, and the Hurd servers themselves are
+aggressively multithreaded.
+
+
+@node Ports Library
+@section Ports Library
+@scindex libports
+@scindex ports.h
+
+Ports are communication channels that are held by the kernel.
+
+A port has separate send rights and receive rights, which may be
+transferred from task to task via the kernel. Port rights are similar
+to Unix file descriptors: they are per-task integers which are used to
+identify ports when making kernel calls. Send rights are required in
+order to send an RPC request down a port, and receive rights are
+required to serve the RPC request. Receive rights may be aggregated
+into a single @dfn{portset}, which serve as useful organizational units.
+
+In a single-threaded RPC client, managing and categorizing ports is not
+a difficult process. However, in a complex multithreaded server, it is
+useful to have a more abstract interface to managing portsets, as well
+as maintaining server metadata.
+
+The Hurd ports library, @code{libports}, fills that need. The
+@code{libports} functions are declared in @code{<hurd/ports.h>}.
+
+@menu
+* Buckets and Classes:: Basic units of port organization.
+* Port Rights:: Moving port rights to and from @code{libports}.
+* Port Metadata:: Managing port-related information.
+* Port References:: Guarding against leaks and lossage.
+* RPC Management:: Locking and interrupting RPC operations.
+@end menu
+
+@node Buckets and Classes
+@subsection Buckets and Classes
+
+The @code{libports} @dfn{bucket} is simply a port set, with some
+metadata and a lock. All of the @code{libports} functions operate on
+buckets.
+
+@deftypefun {struct port_bucket *} ports_create_bucket (void)
+Create and return a new, empty bucket.
+@end deftypefun
+
+A port @dfn{class} is a collection of individual ports, which can be
+manipulated conveniently, and have enforced deallocation routines.
+Buckets and classes are entirely orthogonal: there is no requirement
+that all the ports in a class be in the same bucket, nor is there a
+requirement that all the ports in a bucket be in the same class.
+
+@deftypefun {struct port_class} ports_create_class (@w{void (*@var{clean_routine}) (void *@var{port})}, @w{void (*@var{dropweak_routine}) (void *@var{port})})
+Create and return a new port class. If nonzero, @var{clean_routine}
+will be called for each allocated port object in this class when it is
+being destroyed. If nonzero, @var{dropweak_routine} will be called to
+request weak references to be dropped. (If @var{dropweak_routine} is
+null, then weak references and hard references will be identical for
+ports of this class.)
+@end deftypefun
+
+Once you have created at least one bucket and class, you may create new
+ports, and store them in those buckets. There are a few different
+functions for port creation, depending on your application's
+requirements:
+
+@deftypefun error_t ports_create_port (@w{struct port_class *@var{class}}, @w{struct port_bucket *@var{bucket}}, @w{size_t @var{size}}, @w{void *@var{result}})
+Create and return in @var{result} a new port in @var{class} and
+@var{bucket}; @var{size} bytes will be allocated to hold the port
+structure and whatever private data the user desires.
+@end deftypefun
+
+@deftypefun error_t ports_create_port_noinstall (@w{struct port_class *@var{class}}, @w{struct port_bucket *@var{bucket}}, @w{size_t @var{size}}, @w{void *@var{result}})
+Just like @code{ports_create_port}, except don't actually put the port
+into the portset underlying @var{bucket}. This is intended to be used
+for cases where the port right must be given out before the port is
+fully initialized; with this call you are guaranteed that no RPC service
+will occur on the port until you have finished initializing it and
+installed it into the portset yourself.
+@end deftypefun
+
+@deftypefun error_t ports_import_port (@w{struct port_class *@var{class}}, @w{struct port_bucket *@var{bucket}}, @w{mach_port_t @var{port}}, @w{size_t @var{size}}, @w{void *@var{result}})
+For an existing @emph{receive} right, create and return in @var{result}
+a new port structure; @var{bucket}, @var{size}, and @var{class} args are
+as for @code{ports_create_port}.
+@end deftypefun
+
+
+@node Port Rights
+@subsection Port Rights
+
+The following functions move port receive rights to and from the port
+structure:
+
+@deftypefun void ports_reallocate_port (@w{void *@var{port}})
+Destroy the receive right currently associated with @var{port} and
+allocate a new one.
+@end deftypefun
+
+@deftypefun void ports_reallocate_from_external (@w{void *@var{port}}, @w{mach_port_t @var{receive}})
+Destroy the receive right currently associated with @var{port} and
+designate @var{receive} as the new one.
+@end deftypefun
+
+@deftypefun void ports_destroy_right (@w{void *@var{port}})
+Destroy the receive right currently associated with @var{port}. After
+this call, @code{ports_reallocate_port} and
+@code{ports_reallocate_from_external} may not be used.
+@end deftypefun
+
+@deftypefun mach_port_t ports_claim_right (@w{void *@var{port}})
+Return the receive right currently associated with @var{port}. The
+effects on @var{port} are the same as in @code{ports_destroy_right},
+except that the receive right itself is not affected. Note that in
+multi-threaded servers, messages might already have been dequeued for
+this port before it gets removed from the portset; such messages will
+get @code{EOPNOTSUPP} errors.
+@end deftypefun
+
+@deftypefun error_t ports_transfer_right (@w{void *@var{topt}}, @w{void *@var{frompt}})
+Transfer the receive right from @var{frompt} to @var{topt}.
+@var{frompt} ends up with a destroyed right (as if
+@code{ports_destroy_right} were called) and @var{topt}'s old right is
+destroyed (as if @code{ports_reallocate_from_external} were called).
+@end deftypefun
+
+@deftypefun mach_port_t ports_get_right (@w{void *@var{port}})
+Return the name of the receive right associated with @var{port}. The
+user is responsible for creating an ordinary send right from this name.
+@end deftypefun
+
+
+@node Port Metadata
+@subsection Port Metadata
+
+It is important to point out that the @var{port} argument to each of
+the @code{libports} functions is a @code{void *} and not a @code{struct
+port_info *}. This is done so that you may add arbitrary
+meta-information to your @code{libports}-managed ports. Simply define
+your own structure whose first element is a @code{struct port_info}, and
+then you can use pointers to these structures as the @var{port} argument
+to any @code{libports} function.
+
+The following functions are useful for maintaining metadata that is
+stored in your own custom ports structure:
+
+@deftypefun {void *} ports_lookup_port (@w{struct port_bucket *@var{bucket}}, @w{mach_port_t @var{port}}, @w{struct port_class *@var{class}})
+Look up @var{port} and return the associated port structure, allocating
+a reference. If the call fails, return zero. If @var{bucket} is nonzero,
+then it specifies a bucket to search; otherwise all buckets will be
+searched. If @var{class} is nonzero, then the lookup will fail if
+@var{port} is not in @var{class}.
+@end deftypefun
+
+@deftypefun error_t ports_bucket_iterate (@w{struct port_bucket *@var{bucket}}, @w{error_t (*@var{fun}) (void *@var{port})})
+Call @var{fun} once for each port in @var{bucket}. No guarantee is made
+about the order of iteration, which might vary from call to call. If
+FUN returns an error, then no further calls to FUN are made for any
+remaining ports, and the return value of FUN is returned from
+ports_bucket_iterate.
+@end deftypefun
+
+@node Port References
+@subsection Port References
+
+These functions maintain references to ports so that the port
+information structures may be freed if and only if they are no longer
+needed. It is your responsibility to tell @code{libports} when
+references to ports change.
+
+@deftypefun void ports_port_ref (@w{void *@var{port}})
+Allocate a hard reference to @var{port}.
+@end deftypefun
+
+@deftypefun void ports_port_deref (@w{void *@var{port}})
+Drop a hard reference to @var{port}.
+@end deftypefun
+
+@deftypefun void ports_no_senders (@w{void *@var{port}}, @w{mach_port_mscount_t @var{mscount}})
+The user is responsible for listening for no senders notifications; when
+one arrives, call this routine for the @var{port} the message was sent
+to, providing the @var{mscount} from the notification.
+@end deftypefun
+
+@deftypefun int ports_count_class (@w{struct port_class *@var{class}})
+Block creation of new ports in @var{class}. Return the number of ports
+currently in @var{class}.
+@end deftypefun
+
+@deftypefun int ports_count_bucket (@w{struct port_bucket *@var{bucket}})
+Block creation of new ports in @var{bucket}. Return the number of ports
+currently in @var{bucket}.
+@end deftypefun
+
+@deftypefun void ports_enable_class (@w{struct port_class *@var{class}})
+Permit suspended port creation (blocked by @code{ports_count_class}) to
+continue.
+@end deftypefun
+
+@deftypefun void ports_enable_bucket (@w{struct port_bucket *@var{bucket}})
+Permit suspended port creation (blocked by @code{ports_count_bucket}) to
+continue.
+@end deftypefun
+
+Weak references are not often used, as they are the same as hard
+references for port classes where @var{dropweak_routine} is null.
+@xref{Buckets and Classes}.
+
+@deftypefun void ports_port_ref_weak (@w{void *@var{port}})
+Allocate a weak reference to @var{port}.
+@end deftypefun
+
+@deftypefun void ports_port_deref_weak (@w{void *@var{port}})
+Drop a weak reference to @var{port}.
+@end deftypefun
+
+
+@node RPC Management
+@subsection RPC Management
+
+The rest of the @code{libports} functions are dedicated to controlling
+RPC operations. These functions help you do all the locking and thread
+cancellations that are required in order to build robust servers.
+
+@deftypefn {Typedef} typedef int (*ports_demuxer_type) (@w{mach_msg_header_t *@var{inp}}, @w{mach_msg_header_t *@var{outp}})
+Type of MiG demuxer routines.
+@end deftypefn
+
+@c FIXME: Should I know what `MiG' means?
+@c tb: Yeah, it's the Mach Interface Generator.
+
+@deftypefun error_t ports_begin_rpc (@w{void *@var{port}}, @w{mach_msg_id_t @var{msg_id}}, @w{struct rpc_info *@var{info}})
+Call this when an RPC is beginning on @var{port}. @var{info} should be
+allocated by the caller and will be used to hold dynamic state. If this
+RPC should be abandoned, return @code{EDIED}; otherwise we return zero.
+@end deftypefun
+
+@deftypefun void ports_end_rpc (@w{void *@var{port}}, @w{struct rpc_info *@var{info}})
+Call this when an RPC is concluding. The arguments must match the ones
+passed to the paired call to @code{ports_begin_rpc}.
+@end deftypefun
+
+@deftypefun void ports_manage_port_operations_one_thread (@w{struct port_bucket *@var{bucket}}, @w{ports_demuxer_type @var{demuxer}}, @w{int @var{timeout}})
+Begin handling operations for the ports in @var{bucket}, calling
+@var{demuxer} for each incoming message. Return if @var{timeout} is
+nonzero and no messages have been received for @var{timeout}
+milliseconds. Use only one thread (the calling thread).
+@end deftypefun
+
+@deftypefun void ports_manage_port_operations_multithread (@w{struct port_bucket *@var{bucket}}, @w{ports_demuxer_type @var{demuxer}}, @w{int @var{thread_timeout}}, @w{int @var{global_timeout}}, @w{void (*@var{hook}) (void)})
+Begin handling operations for the ports in @var{bucket}, calling
+@var{demuxer} for each incoming message. Return if @var{global_timeout}
+is nonzero and no messages have been received for @var{global_timeout}
+milliseconds. Create threads as necessary to handle incoming messages
+so that no port is starved because of sluggishness on another port. If
+@var{thread_timeout} is nonzero, then individual threads will die off
+if they handle no incoming messages for @var{local_timeout}
+milliseconds. If non-null, @var{hook} will be called in each new thread
+immediately after it is created.
+@end deftypefun
+
+@deftypefun error_t ports_inhibit_port_rpcs (@w{void *@var{port}})
+Interrupt any pending RPC on @var{port}. Wait for all pending RPCs to
+finish, and then block any new RPCs starting on that port.
+@end deftypefun
+
+@deftypefun error_t ports_inhibit_class_rpcs (@w{struct port_class *@var{class}})
+Similar to @code{ports_inhibit_port_rpcs}, but affects all ports in
+@var{class}.
+@end deftypefun
+
+@deftypefun error_t ports_inhibit_bucket_rpcs (@w{struct port_bucket *@var{bucket}})
+Similar to @code{ports_inhibit_port_rpcs}, but affects all ports in
+@var{bucket}.
+@end deftypefun
+
+@deftypefun error_t ports_inhibit_all_rpcs (void)
+Similar to @code{ports_inhibit_port_rpcs}, but affects all ports
+whatsoever.
+@end deftypefun
+
+@deftypefun void ports_resume_port_rpcs (@w{void *@var{port}})
+Reverse the effect of a previous @code{ports_inhibit_port_rpcs} for this
+@var{port}, allowing blocked RPCs to continue.
+@end deftypefun
+
+@deftypefun void ports_resume_class_rpcs (@w{struct port_class *@var{class}})
+Reverse the effect of a previous @code{ports_inhibit_class_rpcs} for
+@var{class}.
+@end deftypefun
+
+@deftypefun void ports_resume_bucket_rpcs (@w{struct port_bucket *@var{bucket}})
+Reverse the effect of a previous @code{ports_inhibit_bucket_rpcs} for
+@var{bucket}.
+@end deftypefun
+
+@deftypefun void ports_resume_all_rpcs (void)
+Reverse the effect of a previous @code{ports_inhibit_all_rpcs}.
+@end deftypefun
+
+@deftypefun void ports_interrupt_rpcs (@w{void *@var{port}})
+Cancel (with @code{thread_cancel}) any RPCs in progress on @var{port}.
+@end deftypefun
+
+@deftypefun int ports_self_interrupted (void)
+If the current thread's RPC has been interrupted with
+@code{ports_interrupt_rpcs}, return nonzero and clear the interrupted
+flag.
+@end deftypefun
+
+@deftypefun error_t ports_interrupt_rpc_on_notification (@w{void *@var{object}}, @w{struct rpc_info *@var{rpc}}, @w{mach_port_t @var{port}}, @w{mach_msg_id_t @var{what}})
+Arrange for @code{hurd_cancel} to be called on @var{rpc}'s thread if
+@var{object} gets notified that any of the things in @var{what} have
+happened to @var{port}. @var{rpc} should be an RPC on @var{object}.
+@end deftypefun
+
+@deftypefun error_t ports_interrupt_self_on_notification (@w{void *@var{object}}, @w{mach_port_t @var{port}}, @w{mach_msg_id_t @var{what}})
+Arrange for @code{hurd_cancel} to be called on the current thread, which
+should be an RPC on @var{object}, if @var{port} gets notified with the
+condition @var{what}.
+@end deftypefun
+
+@deftypefun error_t ports_interrupt_self_on_port_death (@w{void *@var{object}}, @w{mach_port_t @var{port}})
+Same as calling @code{ports_interrupt_self_on_notification} with
+@var{what} set to @code{MACH_NOTIFY_DEAD_NAME}.
+@end deftypefun
+
+@deftypefun void ports_interrupt_notified_rpcs (@w{void *@var{object}}, @w{mach_port_t @var{port}}, @w{mach_msg_id_t @var{what}})
+Interrupt any RPCs on @var{object} that have requested such.
+@end deftypefun
+
+@deftypefun void ports_dead_name (@w{void *@var{object}}, @w{mach_port_t @var{port}})
+Same as calling @code{ports_interrupt_notified_rpcs} with @var{what} set
+to @code{MACH_NOTIFY_DEAD_NAME}.
+@end deftypefun
+
+
+@node Integer Hash Library
+@section Integer Hash Library
+@scindex libihash
+@scindex ihash.h
+
+@code{libihash} provides integer-keyed hash tables, for arbitrary
+element data types. Such hash tables are frequently used when
+implementing sparse arrays or buffer caches.
+
+The following functions are declared in @code{<hurd/ihash.h>}:
+
+@deftypefun error_t ihash_create (@w{ihash_t *@var{ht}})
+Create an integer hash table and return it in @var{ht}. If a memory
+allocation error occurs, @code{ENOMEM} is returned, otherwise zero.
+@end deftypefun
+
+@deftypefun void ihash_free (@w{ihash_t @var{ht}})
+Free @var{ht} and all resources it consumes.
+@end deftypefun
+
+@deftypefun void ihash_set_cleanup (@w{ihash_t @var{ht}}, @w{void (*@var{cleanup}) (void *@var{value}, void *@var{arg})}, @w{void *@var{arg}})
+Sets @var{ht}'s element cleanup function to @var{cleanup}, and its
+second argument to @var{arg}. @var{cleanup} will be called on every
+element @var{value} to be subsequently overwritten or deleted, with
+@var{arg} as the second argument.
+@end deftypefun
+
+@deftypefun error_t ihash_add (@w{ihash_t @var{ht}}, @w{int @var{id}}, @w{void *@var{item}}, @w{void ***@var{locp}})
+Add @var{item} to the hash table @var{ht} under the integer key
+@var{id}. @var{locp} is the address of a pointer located in @var{item};
+If non-null, @var{locp} should point to a variable of type @code{void
+**}, and will be filled with a pointer that may be used as an argument
+to @code{ihash_locp_remove}. The variable pointed to by @var{locp} may
+be overwritten sometime between this call and when the element is
+deleted, so you cannot stash its value elsewhere and hope to use the
+stashed value with @code{ihash_locp_remove}. If a memory allocation
+error occurs, @code{ENOMEM} is returned, otherwise zero.
+@end deftypefun
+
+@deftypefun {void *} ihash_find (@w{ihash_t @var{ht}}, @w{int @var{id}})
+Find and return the item in hash table @var{ht} with key @var{id}.
+Returns null if the specified item doesn't exist.
+@end deftypefun
+
+@deftypefun error_t ihash_iterate (@w{ihash_t @var{ht}}, @w{error_t (*@var{fun}) (void *@var{value})})
+Call function @var{fun} on every element of @var{ht}. @var{fun}'s only
+arg, @var{value}, is a pointer to the value stored in the hash table. If
+@var{fun} ever returns nonzero, then iteration stops and
+@code{ihash_iterate} returns that value, otherwise it (eventually)
+returns 0.
+@end deftypefun
+
+@deftypefun int ihash_remove (@w{ihash_t @var{ht}}, @w{int @var{id}})
+Remove the entry with a key of @var{id} from @var{ht}. If there was no
+such element, then return zero, otherwise nonzero.
+@end deftypefun
+
+@deftypefun void ihash_locp_remove (@w{ihash_t @var{ht}}, @w{void **@var{ht_locp}})
+Remove the entry at @var{locp} from the hashtable @var{ht}. @var{locp}
+is as returned from an earlier call to @code{ihash_add}. This call
+should be faster than @code{ihash_remove}. @var{ht} can be null, in
+which case the call still succeeds, but no cleanup is done.
+@end deftypefun
+
+
+@node Misc Library
+@section Misc Library
+@scindex libshouldbeinlibc
+
+The GNU C library is constantly developing to meet the needs of the
+Hurd. However, because the C library needs to be very stable, it is
+irresponsible to add new functions to it without carefully specifying
+their interface, and testing them thoroughly.
+
+The Hurd distribution includes a library called
+@code{libshouldbeinlibc}, which serves as a proving ground for additions
+to the GNU C library. This library is in flux, as some functions are
+added to it by the Hurd developers and others are moved to the official
+C library.
+
+These functions aren't currently documented (other than in their header
+files), but complete documentation will be added to
+@iftex
+@emph{The GNU C Library Reference Manual}
+@end iftex
+@ifinfo
+@ref{Top, The GNU C Library Reference Manual,, libc},
+@end ifinfo
+when these functions become part of the GNU C library.
+
+
+@node Bug Address Library
+@section Bug Address Library
+@scindex libhurdbugaddr
+
+@code{libhurdbugaddr} exists only to define a single variable:
+
+@deftypevar {char *} argp_program_bug_address
+@code{argp_program_bug_address} is the default Hurd bug-reporting e-mail
+address, @email{bug-hurd@@gnu.org}. This address is displayed to the
+user when any of the standard Hurd servers and utilities are invoked
+using the @samp{--help} option.
+@end deftypevar
+
+
+@node Input and Output
+@chapter Input and Output
+
+There are no specific programs or servers associated with the I/O
+subsystem, since it is used to interact with almost all servers in the
+GNU Hurd. It provides facilities for reading and writing I/O channels,
+which are the underlying implementation of file and socket descriptors
+in the GNU C library.
+
+@menu
+* Iohelp Library:: I/O authentication and lock management.
+* Pager Library:: Implementing multithreaded external pagers.
+* I/O Interface:: RPC-based input/output channels.
+@end menu
+
+@node Iohelp Library
+@section Iohelp Library
+@scindex libiohelp
+@scindex iohelp.h
+
+The @code{<hurd/iohelp.h>} file declares several functions which are
+useful for low-level I/O implementations. Most Hurd servers do not call
+these functions directly, but they are used by several of the Hurd
+filesystem and networking helper libraries. @code{libiohelp} requires
+@code{libthreads}.
+
+@menu
+* I/O Users:: User authentication management.
+* Conch Management:: Deprecated shared I/O implementation.
+@end menu
+
+@node I/O Users
+@subsection I/O Users
+
+Most I/O servers need to implement some kind of user authentication
+checking. In order to facilitate that process, @code{libiohelp} has
+some functions which encapsulate a set of idvecs (FIXME: xref to C
+library) in a single @code{struct iouser}.
+
+@deftypefun {struct iouser *} iohelp_create_iouser (@w{struct idvec *@var{uids}}, @w{struct idvec *@var{gids}})
+Create a new @var{iouser} for the specified @var{uids} and @var{gids}.
+@end deftypefun
+
+@deftypefun {struct iouser *} iohelp_dup_iouser (@w{struct iouser *@var{iouser}})
+Return a copy of @var{iouser}.
+@end deftypefun
+
+@deftypefun void iohelp_free_iouser (@w{struct iouser *@var{iouser}})
+Release a reference to @var{iouser}.
+@end deftypefun
+
+I/O reauthentication is a rather complex protocol involving the
+authserver as a trusted third party (@pxref{Auth Protocol}). In order
+to reduce the risk of flawed implementations, I/O reauthentication is
+encapsulated in the @code{iohelp_reauth} function:
+
+@deftypefun {struct iouser *} iohelp_reauth (@w{auth_t @var{authserver}}, @w{mach_port_t @var{rend_port}}, @w{mach_port_t @var{newright}}, @w{int @var{permit_failure}})
+Conduct a reauthentication transaction, and return a new @var{iouser}.
+@var{authserver} is the I/O server's auth port. The rendezvous port
+provided by the user is @var{rend_port}.
+
+If the transaction cannot be completed, return zero, unless
+@var{permit_failure} is nonzero. If @var{permit_failure} is nonzero,
+then should the transaction fail, return an @var{iouser} that has no
+ids. The new port to be sent to the user is @var{newright}.
+@end deftypefun
+
+
+@node Conch Management
+@subsection Conch Management
+
+@cindex conch
+@findex iohelp_initialize_conch
+@findex iohelp_handle_io_get_conch
+@findex iohelp_get_conch
+@findex iohelp_handle_io_release_conch
+@findex iohelp_verify_user_conch
+@findex iohelp_fetch_shared_data
+@findex iohelp_put_shared_data
+The @dfn{conch} is at the heart of the shared memory I/O system.
+Several Hurd libraries implement shared I/O, and so @code{libiohelp}
+contains functions to facilitate conch management.
+
+Everything about shared I/O is undocumented because it is not needed for
+adequate performance, and the RPC interface is simpler (@pxref{I/O
+Interface}). It is not useful for new libraries or servers to implement
+shared I/O.
+
+
+@node Pager Library
+@section Pager Library
+@scindex libpager
+@scindex pager.h
+
+@cindex XP (external pager)
+@cindex external pager (XP)
+The @dfn{external pager} (@dfn{XP}) microkernel interface allows
+applications to provide the backing store for a memory object, by
+converting hardware page faults into RPC requests. External pagers are
+required for memory-mapped I/O (@pxref{Mapped Data}) and stored
+filesystems (@pxref{Stored Filesystems}).
+
+The external pager interface is quite complex, so the Hurd pager library
+contains functions which aid in creating multithreaded external pagers.
+@code{libpager} is declared in @code{<hurd/pager.h>}, and requires only
+the threads and ports libraries.
+
+@menu
+* Pager Management:: High-level interface to external pagers.
+* Pager Callbacks:: Functions that the user must define.
+@end menu
+
+
+@node Pager Management
+@subsection Pager Management
+
+The pager library defines the @code{struct pager} data type in order to
+represent a multi-threaded pager. The general procedure for creating a
+pager is to define the functions listed in @ref{Pager Callbacks},
+allocate a @code{libports} bucket for the ports which will access the
+pager, and create at least one new @code{struct pager} with
+@code{pager_create}.
+
+@deftypefun {struct pager *} pager_create (@w{struct user_pager_info *@var{u_pager}}, @w{struct port_bucket *@var{bucket}}, @w{boolean_t @var{may_cache}}, @w{memory_object_copy_strategy_t @var{copy_strategy}})
+Create a new pager. The pager will have a port created for it (using
+@code{libports}, in @var{bucket}) and will be immediately ready to
+receive requests. @var{u_pager} will be provided to later calls to
+@code{pager_find_address}. The pager will have one user reference
+created. @var{may_cache} and @var{copy_strategy} are the original
+values of those attributes as for @code{memory_object_ready}. Users may
+create references to pagers by use of the relevant ports library
+functions. On errors, return null and set @code{errno}.
+@end deftypefun
+
+Once you are ready to turn over control to the pager library, you should
+call @code{ports_manage_port_operations_multithread} on the
+@var{bucket}, using @code{pager_demuxer} as the ports @var{demuxer}.
+This will handle all external pager RPCs, invoking your pager callbacks
+when necessary.
+
+@deftypefun int pager_demuxer (@w{mach_msg_header_t *@var{inp}}, @w{mach_msg_header_t *@var{outp}})
+Demultiplex incoming @code{libports} messages on pager ports.
+@end deftypefun
+
+The following functions are the body of the pager library, and provide a
+clean interface to pager functionality:
+
+@deftypefun void pager_sync (@w{struct pager *@var{pager}}, @w{int @var{wait}})
+@deftypefunx void pager_sync_some (@w{struct pager *@var{pager}}, @w{vm_address_t @var{start}}, @w{vm_size_t @var{len}}, @w{int @var{wait}})
+Write data from pager @var{pager} to its backing store. Wait for all
+the writes to complete if and only if @var{wait} is set.
+
+@code{pager_sync} writes all data; @code{pager_sync_some} only writes
+data starting at @var{start}, for @var{len} bytes.
+@end deftypefun
+
+@deftypefun void pager_flush (@w{struct pager *@var{pager}}, @w{int @var{wait}})
+@deftypefunx void pager_flush_some (@w{struct pager *@var{pager}}, @w{vm_address_t @var{start}}, @w{vm_size_t @var{len}}, @w{int @var{wait}})
+Flush data from the kernel for pager @var{pager} and force any pending
+delayed copies. Wait for all pages to be flushed if and only if
+@var{wait} is set.
+
+@code{pager_flush} flushes all data; @code{pager_flush_some} only
+flushes data starting at @var{start}, for @var{len} bytes.
+@end deftypefun
+
+@deftypefun void pager_return (@w{struct pager *@var{pager}}, @w{int @var{wait}})
+@deftypefunx void pager_return_some (@w{struct pager *@var{pager}}, @w{vm_address_t @var{start}}, @w{vm_size_t @var{len}}, @w{int @var{wait}})
+Flush data from the kernel for pager @var{pager} and force any pending
+delayed copies. Wait for all pages to be flushed if and only if
+@var{wait} is set. Have the kernel write back modifications.
+
+@code{pager_return} flushes and restores all data;
+@code{pager_return_some} only flushes and restores data starting at
+@var{start}, for @var{len} bytes.
+@end deftypefun
+
+@deftypefun void pager_offer_page (@w{struct pager *@var{pager}}, @w{int @var{precious}}, @w{int @var{writelock}}, @w{vm_offset_t @var{page}}, @w{vm_address_t @var{buf}})
+Offer a page of data to the kernel. If @var{precious} is set, then this
+page will be paged out at some future point, otherwise it might be
+dropped by the kernel. If the page is currently in core, the kernel
+might ignore this call.
+@end deftypefun
+
+@deftypefun void pager_change_attributes (@w{struct pager *@var{pager}}, @w{boolean_t @var{may_cache}}, @w{memory_object_copy_strategy_t @var{copy_strategy}}, @w{int @var{wait}})
+Change the attributes of the memory object underlying pager @var{pager}.
+The @var{may_cache} and @var{copy_strategy} arguments are as for
+@code{memory_object_change_}. Wait for the kernel to report
+completion if and only if @var{wait} is set.
+@end deftypefun
+
+@deftypefun void pager_shutdown (@w{struct pager *@var{pager}})
+Force termination of a pager. After this returns, no more paging
+requests on the pager will be honoured, and the pager will be
+deallocated. The actual deallocation might occur asynchronously if
+there are currently outstanding paging requests that will complete
+first.
+@end deftypefun
+
+@deftypefun error_t pager_get_error (@w{struct pager *@var{p}}, @w{vm_address_t @var{addr}})
+Return the error code of the last page error for pager @var{p} at
+address @var{addr}.@footnote{Note that this function will be deleted
+when the Mach pager interface is fixed to provide this information.}
+@end deftypefun
+
+@deftypefun error_t pager_memcpy (@w{struct pager *@var{pager}}, @w{memory_object_t @var{memobj}}, @w{vm_offset_t @var{offset}}, @w{void *@var{other}}, @w{size_t *@var{size}}, @w{vm_prot_t @var{prot}})
+Try to copy @code{*@var{size}} bytes between the region @var{other}
+points to and the region at @var{offset} in the pager indicated by
+@var{pager} and @var{memobj}. If @var{prot} is @code{VM_PROT_READ},
+copying is from the pager to @var{other}; if @var{prot} contains
+@code{VM_PROT_WRITE}, copying is from @var{other} into the pager.
+@code{*@var{size}} is always filled in with the actual number of bytes
+successfully copied. Returns an error code if the pager-backed memory
+faults; if there is no fault, returns zero and @code{*@var{size}} will
+be unchanged.
+@end deftypefun
+
+These functions allow you to recover the internal @code{struct pager}
+state, in case the @code{libpager} interface doesn't provide an
+operation you need:
+
+@deftypefun {struct user_pager_info *} pager_get_upi (@w{struct pager *@var{p}})
+Return the @code{struct user_pager_info} associated with a pager.
+@end deftypefun
+
+@deftypefun mach_port_t pager_get_port (@w{struct pager *@var{pager}})
+Return the port (receive right) for requests to the pager. It is
+absolutely necessary that a new send right be created from this receive
+right.
+@end deftypefun
+
+
+@node Pager Callbacks
+@subsection Pager Callbacks
+
+Like several other Hurd libraries, @code{libpager} depends on you to
+implement application-specific callback functions. You @emph{must}
+define the following functions:
+
+@deftypefun error_t pager_read_page (@w{struct user_pager_info *@var{pager}}, @w{vm_offset_t @var{page}}, @w{vm_address_t *@var{buf}}, @w{int *@var{write_lock}})
+For pager @var{pager}, read one page from offset @var{page}. Set
+@code{*@var{buf}} to be the address of the page, and set
+@code{*@var{write_lock}} if the page must be provided read-only. The
+only permissible error returns are @code{EIO}, @code{EDQUOT}, and
+@code{ENOSPC}.
+@end deftypefun
+
+@deftypefun error_t pager_write_page (@w{struct user_pager_info *@var{pager}}, @w{vm_offset_t @var{page}}, @w{vm_address_t @var{buf}})
+For pager @var{pager}, synchronously write one page from @var{buf} to
+offset @var{page}. In addition, @code{vm_deallocate} (or equivalent)
+@var{buf}. The only permissible error returns are @code{EIO},
+@code{EDQUOT}, and @code{ENOSPC}.
+@end deftypefun
+
+@deftypefun error_t pager_unlock_page (@w{struct user_pager_info *@var{pager}}, @w{vm_offset_t @var{address}})
+A page should be made writable.
+@end deftypefun
+
+@deftypefun error_t pager_report_extent (@w{struct user_pager_info *@var{pager}}, @w{vm_address_t *@var{offset}}, @w{vm_size_t *@var{size}})
+This function should report in @code{*@var{offset}} and
+@code{*@var{size}} the minimum valid address the pager will accept and
+the size of the object.
+@end deftypefun
+
+@deftypefun void pager_clear_user_data (@w{struct user_pager_info *@var{pager}})
+This is called when a pager is being deallocated after all extant send
+rights have been destroyed.
+@end deftypefun
+
+@deftypefun void pager_dropweak (@w{struct user_pager_info *@var{p}})
+This will be called when the ports library wants to drop weak
+references. The pager library creates no weak references itself, so if
+the user doesn't either, then it is all right for this function to do
+nothing.
+@end deftypefun
+
+
+@node I/O Interface
+@section I/O Interface
+@scindex io.defs
+
+The I/O interface facilities are described in @code{<hurd/io.defs>}.
+This section discusses only RPC-based I/O operations.@footnote{The
+latter portion of @code{<hurd/io.defs>} and all of
+@code{<hurd/shared.h>} describe how to implement shared-memory I/O
+operations. However, shared I/O has been deprecated. @xref{Conch
+Management}, for more details.}
+
+@menu
+* I/O Object Ports:: How ports to I/O objects work.
+* Simple Operations:: Read, write, and seek.
+* Open Modes:: State bits that affect pieces of operation.
+* Asynchronous I/O:: How to be notified when I/O is possible.
+* Information Queries:: How to implement @code{io_stat} and
+ @code{io_server_version}.
+* Mapped Data:: Getting memory objects referring to the
+ data of an I/O object.
+@end menu
+
+@node I/O Object Ports
+@subsection I/O Object Ports
+
+The I/O server must associate each I/O port with a particular set of
+uids and gids, identifying the user who is responsible for operations on
+the port. Every port to an I/O server should also support either the
+file protocol (@pxref{File Interface}) or the socket protocol
+(@pxref{Socket Interface}); naked I/O ports are not allowed.
+
+In addition, the server associates with each port a default file
+pointer, a set of open mode bits, a pid (called the ``owner''), and some
+underlying object which can absorb data (for write) or provide data (for
+read).
+
+The uid and gid sets associated with a port may not be visibly shared
+with other ports, nor may they ever change. The server must fix the
+identification of a set of uids and gids with a particular port at the
+moment of the port's creation. The other characteristics of an I/O port
+may be shared with other users. The I/O server interface does not
+generally specify the way in which servers may share these other
+characteristics (with the exception of the deprecated
+@code{O_ASYNC} interface); however, the file and socket interfaces make
+further requirements about what sharing is required and what sharing is prohibited.
+
+In general, users get send rights to I/O ports by some mechanism that is
+external to the I/O protocol. (For example, fileservers give out I/O
+ports in response to the @code{dir_lookup} and @code{fsys_getroot}
+calls. Socket servers give out ports in response to the
+@code{socket_create} and @code{socket_accept} calls.) However, the I/O
+protocol provides methods of obtaining new ports that refer to the same
+underlying object as another port. In response to all of these calls,
+all underlying state (including, but not limited to, the default file
+pointer, open mode bits, and underlying object) must be shared between
+the old and new ports. In the following descriptions of these calls,
+the term ``identical'' means this kind of sharing. All these calls must
+return send rights to a newly-constructed Mach port.
+
+@c FIXME: should be say `Mach' above, or should we say
+@c `microkernel'?
+@c tb: We say Mach. Other kernels might have different rules, and we
+@c should document what we have now.
+
+@findex io_duplicate
+The @code{io_duplicate} call simply returns another port which is
+identical to an existing port and has the same uid and gid set.
+
+@findex io_restrict_auth
+The @code{io_restrict_auth} call returns another port, identical to the
+provided port, but which has a smaller associated uid and gid set. The
+uid and gid sets of the new port are the intersection of the set on the
+existing port and the lists of uids and gids provided in the call.
+
+@findex io_reauthenticate
+Users use the @code{io_reauthenticate} call when they wish to have an
+entirely new set of uids or gids associated with a port. In response to
+the @code{io_reauthenticate} call, the server must create a new port,
+and then make the call @code{auth_server_authenticate} to the auth
+server. The rendezvous port for the @code{auth_server_authenticate}
+call is the I/O port to which was made the @code{io_reauthenticate}
+call. The server provides the @var{rend_int} parameter to the auth
+server as a copy from the corresponding parameter in the
+@code{io_reauthenticate} call. The I/O server also gives the auth
+server a new port; this must be a newly created port identical to the
+old port. The authserver will return the set of uids and gids
+associated with the user, and guarantees that the new port will go
+directly to the user that possessed the associated authentication port.
+The server then identifies the new port given out with the specified
+ID's.
+
+@node Simple Operations
+@subsection Simple Operations
+
+@findex io_write
+Users write to I/O ports by calling the @code{io_write} RPC. They
+specify an @var{offset} parameter; if the object supports writing at
+arbitrary offsets, the server should honour this parameter. If @math{-1}
+is passed as the offset, then the server should use the default file
+pointer. The server should return the amount of data which was
+successfully written. If the operation was interrupted after some but
+not all of the data was written, then it is considered to have succeeded
+and the server should return the amount written. If the port is not an
+I/O port at all, the server should reply with the error
+@code{EOPNOTSUPP}. If the port is an I/O port, but does not happen to
+support writing, then the correct error is @code{EBADF}.
+
+@findex io_read
+Users read from I/O ports by calling the @code{io_read} RPC. They
+specify the amount of data they wish to read, and the offset. The offset
+has the same meaning as for @code{io_write} above. The server should
+return the data that was read. If the call is interrupted after some
+data has been read (and the operation is not idempotent) then the server
+should return the amount read, even if it was less than the amount requested.
+The server should return as much data as possible, but never more than
+requested by the user. If there is no data, but there might be later,
+the call should block until data becomes available. The server indicates
+end-of-file by returning zero bytes. If the call is
+interrupted after some data has been read, but the call is idempotent,
+then the server may return @code{EINTR} rather than actually filling the
+buffer (taking care that any modifications of the default file pointer
+have been reversed). Preferably, however, servers should return data.
+
+There are two categories of objects: seekable and non-seekable.
+Seekable objects must accept arbitrary offset parameters in the
+@code{io_read} and @code{io_write} calls, and must implement the
+@code{io_seek} call. Non-seekable objects must ignore the offset
+parameters to @code{io_read} and @code{io_write}, and should return
+@code{ESPIPE} to the @code{io_seek} call.
+
+@c FIXME: should that last `should' be replaced with `must'?
+@c tb: maybe, but perhaps not. There might be a reason to implement a
+@c semi-seekable object which permits some but not all of these
+@c operations. In the case of the Hurd interfaces (as opposed to
+@c libraries) I like to be a little looser about this. The rule is "do
+@c what the interface says unless you really understand it and have a
+@c good reason to do something different".
+
+@findex io_seek
+On seekable objects, @code{io_seek} changes the default file pointer for
+reads and writes. (@xref{File Positioning, , , libc, The GNU C Library
+Reference Manual},
+for the interpretation of the @var{whence} and @var{offset} arguments.)
+It returns the new offset as modified by @code{io_seek}.
+
+@findex io_readable
+The @code{io_readable} interface returns the amount of data which can be
+immediately read. For the special technical meaning of ``immediately'',
+see @ref{Asynchronous I/O}.
+
+@node Open Modes
+@subsection Open Modes
+
+@findex io_set_all_openmodes
+@findex io_get_openmodes
+@findex io_set_some_openmodes
+@findex io_clear_some_openmodes
+The server associates each port with a set of bits that affect its
+operation. The @code{io_set_all_openmodes} call modifies these bits and
+the @code{io_get_openmodes} call returns them. In addition, the
+@code{io_set_some_openmodes} and @code{io_clear_some_openmodes} do an
+atomic read/modify/write of the openmodes.
+
+The @code{O_APPEND} bit, when set, changes the behaviour of
+@code{io_write} when it uses the default file pointer on seekable
+objects. When @code{io_write} is done on a port with the
+@code{O_APPEND} bit set, is must set the file pointer to the current
+file size before doing the write (which would then increment the file
+pointer as usual). The @dfn{current file size} is the smallest offset
+which returns end-of-file when provided to @code{io_read}. The server
+must atomically bind this update to the actual data write with respect
+to other users of @code{io_read}, @code{io_write}, and @code{io_seek}.
+
+The @code{O_FSYNC} bit, when set, guarantees that @code{io_write} will
+not return until data is fully written to the underlying medium.
+
+The @code{O_NONBLOCK} bit, when set, prevents read and write from
+blocking. They should copy such data as is immediately available. If
+no data is immediately available they should return @code{EWOULDBLOCK}.
+
+The definition of ``immediately'' is more or less server-dependent.
+Some servers, notably stored filesystem servers (@pxref{Stored
+Filesystems}), regard all data as immediately available. The one
+criterion is that something which must happen @dfn{immediately} may not
+wait for any user-synchronizable event.
+
+The @code{O_ASYNC} bit is deprecated; its use is documented in the
+following section. This bit must be shared between all users of the
+same underlying object.
+
+
+@node Asynchronous I/O
+@subsection Asynchronous I/O
+
+@findex io_async
+Users may wish to be notified when I/O can be done without blocking;
+they use the @code{io_async} call to indicate this to the server. In
+the @code{io_async} call the user provides a port on which will the
+server should send @code{sig_post} messages as I/O becomes possible.
+The server must return a port which will be the reference port in the
+@code{sig_post} messages. Each @code{io_async} call should generate a
+new reference port. (FIXME: xref the C library manual for information
+on how to send sig_post messages.)
+
+The server then sends one @code{SIGIO} signal to each registered async
+user every time I/O becomes possible. I/O is possible if at least one
+byte can be read or written immediately. The definition of
+``immediately'' must be the same as for the implementation of the
+@code{O_NONBLOCK} flag (@pxref{Open Modes}). In addition, every time a
+user calls @code{io_read} or @code{io_write} on a non-seekable object, or at the
+default file pointer on a seekable object, another signal should be sent
+to each user if I/O is still possible.
+
+Some objects may also define ``urgent'' conditions. Such servers should
+send the @code{SIGURG} signal to each registered async user anytime an
+urgent condition appears. After any RPC that has the possibility of
+clearing the urgent condition, the server should again send the signal
+to all registered users if the urgent condition is still present.
+
+@findex io_select
+A more fine-grained mechanism for doing async I/O is the
+@code{io_select} call. The user specifies the kind of access desired,
+and a send-once right. If I/O of the kind the user desires is
+immediately possible, then the server should return so indicating, and
+destroy the send-once right. If I/O is not immediately possible, the
+server should save the send-once right, and send a @code{select_done}
+message as soon as I/O becomes immediately possible. Again, the
+definition of ``immediately'' must be the same for @code{io_select},
+@code{io_async}, and @code{O_NONBLOCK} (@pxref{Open Modes}).
+
+@findex io_mod_owner
+@findex io_get_owner
+@findex io_get_icky_async_id
+For compatibility with 4.2 and 4.3 BSD, the I/O interface provides a
+deprecated feature (known as @dfn{icky async I/O}). The calls
+@code{io_mod_owner} and @code{io_get_owner} set the ``owner'' of the
+object, providing either a pid or a pgrp (if the value is negative).
+This implies that only one process at a time can do icky I/O on a given
+object. Whenever the I/O server is sending @code{sig_post} messages to
+all the @code{io_async} users, if the @code{O_ASYNC} bit is set, the
+server should also send a signal to the owning pid/pgrp. The ID port
+for this call should be different from all the @code{io_async} ID ports
+given to users. Users may find out what ID port the server uses for
+this by calling @code{io_get_icky_async_id}.
+
+@node Information Queries
+@subsection Information Queries
+
+@findex io_stat
+Users may call @code{io_stat} to find out information about the I/O
+object. Most of the fields of a @code{struct stat} are meaningful only
+for files. All objects, however, must support the fields
+@var{st_fstype}, @var{st_fsid}, @var{st_ino}, @var{st_atim},
+@var{st_mtim}, @var{st_ctim}, and @var{st_blksize}.
+
+@var{st_fstype}, @var{st_fsid}, and @var{st_ino} must be unique for
+the underlying object across the entire system.
+
+@var{st_atim} holds the timestamp of the system clock at the last time the
+object was read with @code{io_read}.
+
+@var{st_mtim} holds the timestamp of the system clock at the last time the
+object was written with @code{io_write}.
+
+Other appropriate operations may update the @var{atim} and the
+@var{mtim} as well; both the file and socket interfaces specify such
+operations.
+
+@var{st_ctim} holds the timestamp of the system clock at the last time
+permanent meta-data associated with the object was changed. The exact
+operations which cause such an update are server-dependent, but must
+include the creation of the object.
+
+The server is permitted to delay the actual update of these times until
+stat is called; before the server stores the times on permanent media
+(if it ever does so) it should update them if necessary.
+
+@var{st_blksize} gives the optimal I/O size in bytes for @code{io_read}
+and @code{io_write}; users should endeavor to read and write amounts
+which are multiples of the optimal size, and to use offsets which are
+multiples of the optimal size.
+
+In addition, objects which are seekable should set @var{st_size} to the
+current file size as in the description of the @code{O_APPEND} flag
+(@pxref{Open Modes}).
+
+The @var{st_uid} and @var{st_gid} fields are unrelated to the ``owner''
+as described above for icky async I/O.
+
+@findex io_server_version
+Users may find out the version of the server they are talking to by
+calling @code{io_server_version}; this should return strings and
+integers describing the version number of the server, as well as its
+name.
+
+@node Mapped Data
+@subsection Mapped Data
+
+@findex io_map
+Servers may optionally implement the @code{io_map} call. The ports
+returned by @code{io_map} must implement the external pager kernel
+interface (@pxref{Pager Library}) and be suitable as arguments to
+@code{vm_map}.
+
+Seekable objects must allow access from zero up to (but not including)
+the current file size as described for @code{O_APPEND} (@pxref{Open
+Modes}). Whether they provide access beyond such a point is
+server-dependent; in addition, the meaning of accessing a non-seekable
+object is server-dependent.
+
+
+@node Files
+@chapter Files
+
+A file is traditionally thought of as a quantity of disk storage. In
+the Hurd, files are an extension of the I/O interface, but they do not
+necessarily correspond to disk storage.
+
+Every file in the Hurd is represented by a port, which is connected to
+the server that manages the file. When a client wants to operate on a
+file, it makes RPC requests via a file port to its server process, which
+is commonly called a @dfn{translator}.
+
+@menu
+* Translators:: Extending the Hurd filesystem hierarchy.
+* Trivfs Library:: Implementing single-file translators.
+* Fshelp Library:: Miscellaneous generic filesystem routines.
+* File Interface:: File ports implement the file interface.
+* Filesystem Interface:: Translator control interface.
+@end menu
+
+
+@node Translators
+@section Translators
+
+The Hurd filesystem allows you to set translators on any file or
+directory that you own. A @dfn{translator} is any Hurd server which
+provides the basic filesystem interface. Translated nodes are somewhat
+like a cross between Unix symbolic links and mount points.
+
+Whenever a program tries to access the contents of a translated node,
+the filesystem server redirects the request to the appropriate
+translator (starting it if necessary). Then, the new translator
+services the client's request. The GNU C library makes this behaviour
+seamless from the client's perspective, so that standard Unix programs
+behave correctly under the Hurd.
+
+Translators run with the privileges of the translated node's
+@emph{owner}, so they cannot be used to compromise the security of the
+system. This also means that @emph{any} user can write their own
+translators, and provide other users with arbitrary
+filesystem-structured data, regardless of the data's actual source.
+Other chapters in this manual describe existing translators, and how you
+can modify them or write your own.
+
+The standard Hurd filesystem servers are constantly evolving to provide
+innovative features that users want. Here are a few examples of
+existing translators:
+
+@itemize @bullet
+@item
+Disk-based filesystem formats, such as @code{ext2fs} and
+@code{iso9660fs} (@pxref{Stored Filesystems}).
+
+@item
+Network filesystems, such as @code{nfs} and @code{ftpfs}
+(@pxref{Distributed Filesystems}).
+
+@item
+Single files with dynamic content, such as FIXME: we need a good
+example.
+
+@item
+@c FIXME: reword
+Hurd servers which translate rendezvous filesystem nodes in standard
+locations, so that other programs can easily find them and use
+server-specific interfaces. For example, @code{pflocal} implements the
+filesystem interfaces, but it also provides a special Unix-domain socket
+RPC interface (FIXME xref). Programs can fetch a port to this
+translator simply by calling @code{file_name_lookup} (FIXME xref) on
+@file{/servers/socket/1}@footnote{The number 1 corresponds to the
+@code{PF_LOCAL} C library socket domain constant.}, then use Unix
+socket-specific RPCs on that port, rather than adhering to the file
+protocol.
+@end itemize
+
+This section focuses on the generic programs that you need to understand
+in order to use existing translators. Many other parts of this manual
+describe how you can write your own translators.
+
+@menu
+* Invoking settrans:: Declaring how a node should be translated.
+* Invoking showtrans:: Displaying how nodes are translated.
+* Invoking mount:: Unix-compatible active filesystem translators.
+* Invoking fsysopts:: Modifying translation parameters at runtime.
+@end menu
+
+
+@node Invoking settrans
+@subsection Invoking @code{settrans}
+@pindex settrans
+
+The @code{settrans} program allows you to set a translator on a file or
+directory. By default, the passive translator is set (see the
+@samp{--passive} option).
+
+The @code{settrans} program has the following synopsis:
+
+@example
+settrans [@var{option}]@dots{} @var{node} [@var{translator} @var{arg}@dots{}]
+@end example
+
+@noindent
+where @var{translator} is the absolute filename of the new translator
+program. Each @var{arg} is passed to @var{translator} when it starts.
+If @var{translator} is not specified, then @code{settrans} clears the
+existing translator rather than setting a new one.
+
+@code{settrans} accepts the following options:
+
+@table @samp
+@item -a
+@itemx --active
+Set @var{node}'s active translator. @dfn{Active translators} are
+started immediately and are not persistent: if the system is rebooted
+then they are lost.
+
+@item -c
+@itemx --create
+Create @var{node} as a zero-length file if it doesn't already exist.
+
+@item -L
+@itemx --dereference
+If @var{node} is already translated, stack the new translator on top of
+it (rather than replacing the existing translator).
+
+@item --help
+Display a brief usage message, then exit.
+
+@item -p
+@itemx --passive
+Set @var{node}'s passive translator. @dfn{Passive translators} are only
+activated by the underlying filesystem when clients try to use the
+@var{node}, and they shut down automatically after they are no longer
+active in order to conserve system resources.
+
+Passive translators are stored on the underlying filesystem media, and
+so they persist between system reboots. Not all filesystems support
+passive translators, due to limitations in their underlying media.
+Consult the filesystem-specific documentation to see if they are
+supported.
+
+If you are setting the passive translator, and @var{node} already has an
+active translator, then the following options apply:
+
+@table @samp
+@item -g
+@itemx --goaway
+Tell the active translator to go away. In this case, the following
+additional options apply:
+
+@table @samp
+@item -f
+@itemx --force
+If the active translator doesn't go away, then force it.
+
+@item -S
+@itemx --nosync
+Don't flush its contents to disk before terminating.
+
+@item -R
+@itemx --recursive
+Shut down all of the active translator's children, too.
+@end table
+
+
+@item -k
+@itemx --keep-active
+Leave the existing active translator running. The new translator will
+not be started unless the active translator has stopped.
+@end table
+
+@item -P
+@itemx --pause
+When starting an active translator, prompt and wait for a newline on
+standard input before completing the startup handshake. This is useful
+when debugging a translator, as it gives you time to start the debugger.
+
+@item -t @var{sec}
+@itemx --timeout=@var{sec}
+If the translator does not start up in @var{sec} seconds (the default is
+60), then return an error; if @var{sec} is 0, then never timeout.
+
+@item --version
+Output program version information and exit.
+
+@item -x
+@itemx --exclusive
+Only set the translator if there is none already.
+@end table
+
+
+@node Invoking showtrans
+@subsection Invoking @code{showtrans}
+
+The @code{showtrans} program allows you to show the passive translator
+setting on a file system node.
+
+The @code{showtrans} program has the following synopsis:
+
+@example
+showtrans [@var{option}]@dots{} @var{file}@dots{}
+@end example
+
+@code{showtrans} accepts the following options:
+
+@table @code
+@item -p
+@itemx --prefix
+Always display @var{filename}: before translators.
+
+@item -P
+@itemx --no-prefix
+Never display @var{filename}: before translators.
+@item -s
+@itemx --silent
+No output; useful when checking error status.
+@item -t
+@itemx --translated
+Only display files that have translators.
+@end table
+
+
+@node Invoking mount
+@subsection Invoking @code{mount}
+
+
+@node Invoking fsysopts
+@subsection Invoking @code{fsysopts}
+
+The @code{fsysopts} program allows you to retrieve or set command line
+options for running translator @var{filesys}.
+
+The @code{fsysopts} program has the following synopsis:
+
+@example
+fsysopts [@var{option}@dots{}] @var{filesys} [@var{fs_option}@dots{}]
+@end example
+
+@code{fsysopts} accepts the following options:
+
+@table @code
+
+@item -L
+@itemx --dereference
+If @var{filesys} is a symbolic link, follow it.
+
+@item -R
+@itemx --recursive
+Pass these options to any child translators.
+@end table
+
+The legal values for @var{fs_option} depends on @var{filesys}, but
+some common ones are:
+
+@table @code
+@item --readonly
+@item --writable
+@item --remount
+@item --sync[=@var{interval}]
+@item --nosync
+@end table
+
+If no options are supplied, @var{filesys}' current options are
+printed.
+
+The options passed as @var{fs_option}s are meant to augment or change those
+which are already set, they're not meant to completely replace the existing
+command line options. For example, passing @code{--readonly} to a file system
+server will change the server from writable to read-only, but will not touch
+the used backing store. Passing @code{--address=new_IP} to
+@file{/servers/socket/2} will change the local IP address to @var{new_IP}, but
+will not touch the interface, netmask and gateway settings.
+
+@node Trivfs Library
+@section Trivfs Library
+@scindex libtrivfs
+@scindex trivfs.h
+
+Certain translators do not need to be very complex, because they
+represent a single file rather than an entire directory hierarchy. The
+trivfs library, which is declared in @code{<hurd/trivfs.h>}, does most of
+the work of implementing this kind of translator. This library requires
+the iohelp and ports libraries.
+
+@menu
+* Trivfs Startup:: Writing a simple trivfs-based translator.
+* Trivfs Callbacks:: Mandatory user-defined trivfs functions.
+* Trivfs Options:: Optional user-defined trivfs functions.
+* Trivfs Ports:: Managing control and protid ports.
+@end menu
+
+@node Trivfs Startup
+@subsection Trivfs Startup
+
+In order to use the trivfs library, you will need to define the
+appropriate callbacks (@pxref{Trivfs Callbacks}). As with all Hurd
+servers, your trivfs-based translator should first parse any
+command-line options, in case the user is just asking for help. Trivfs
+uses argp (@pxref{Argp, , , libc, The GNU C Library Reference Manual})
+for parsing command-line arguments.
+
+Your translator should redefine the following functions and variables as
+necessary, and then call @code{argp_parse} with the relevant arguments:
+
+@deftypevar {extern struct argp *} trivfs_runtime_argp
+If this is defined or set to an argp structure, it will be used by the
+default @code{trivfs_set_options} to handle runtime options parsing.
+Redefining this is the normal way to add option parsing to a trivfs
+program.
+@end deftypevar
+
+@deftypefun error_t trivfs_set_options (@w{struct trivfs_control *@var{fsys}}, @w{char *@var{argz}}, @w{size_t @var{argz_len}})
+Set runtime options for @var{fsys} to @var{argz} and @var{argz_len}.
+The default definition for this routine simply uses
+@var{trivfs_runtime_argp} (supplying @var{fsys} as the argp input
+field).
+@end deftypefun
+
+@deftypefun error_t trivfs_append_args (@w{struct trivfs_control *@var{fsys}}, @w{char **@var{argz}}, @w{size_t *@var{argz_len}})
+Append to the malloced string @code{*@var{argz}} of length
+@code{*@var{argz_len}} a NUL-separated list of the arguments to this
+translator.
+@end deftypefun
+
+@c FIXME: Shouldn't `NUL-separated', above, be changed to
+@c `NUL-terminated' (or, as I prefer, `zero-terminated')?
+@c tb: no, it's a NUL-separated list. Something like:
+@c "foo\0bar\0baz\0quux"
+
+After your translator parses its command-line arguments, it should fetch
+its bootstrap port by using @code{task_get_bootstrap_port}. If this
+port is @code{MACH_PORT_NULL}, then your program wasn't started as a
+translator. Otherwise, you can use the bootstrap port to create a new
+control structure (and advertise its port) with @code{trivfs_startup}:
+
+@deftypefun error_t trivfs_startup (@w{mach_port_t @var{bootstrap}}, @w{int @var{flags}}, @w{struct port_class *@var{control_class}}, @w{struct port_bucket *@var{control_bucket}}, @w{struct port_class *@var{protid_class}}, @w{struct port_bucket *@var{protid_bucket}}, @w{struct trivfs_control **@var{control}})
+@deftypefunx error_t trivfs_create_control (@w{mach_port_t @var{bootstrap}}, @w{struct port_class *@var{control_class}}, @w{struct port_bucket *@var{control_bucket}}, @w{struct port_class *@var{protid_class}}, @w{struct port_bucket *@var{protid_bucket}}, @w{struct trivfs_control **@var{control}})
+@code{trivfs_startup} creates a new trivfs control port, advertises it
+to the underlying node @var{bootstrap} with @code{fsys_startup},
+returning the results of this call, and places its control structure in
+@code{*@var{control}}. @code{trivfs_create_control} does the same
+thing, except it doesn't advertise the control port to the underlying
+node. @var{control_class} and @var{control_bucket} are passed to
+@code{libports} to create the control port, and @var{protid_class} and
+@var{protid_bucket} are used when creating ports representing opens of
+this node; any of these may be zero, in which case an appropriate port
+class/bucket is created. If @var{control} is non-null, the trivfs
+control port is returned in it. @var{flags} (a bitmask of the
+appropriate @code{O_*} constants) specifies how to open the underlying
+node.
+@end deftypefun
+
+If you did not supply zeros as the class and bucket arguments to
+@code{trivfs_startup}, you will probably need to use the trivfs port
+management functions (@pxref{Trivfs Ports}).
+
+Once you have successfully called @code{trivfs_startup}, and have a
+pointer to the control structure stored in, say, the @var{fsys}
+variable, you are ready to call one of the
+@code{ports_manage_port_operations_*} functions using
+@code{@var{fsys}->pi.bucket} and @code{trivfs_demuxer}. This will
+handle any incoming filesystem requests, invoking your callbacks when
+necessary.
+
+@deftypefun int trivfs_demuxer (@w{mach_msg_header_t *@var{inp}}, @w{mach_msg_header_t *@var{outp}})
+Demultiplex incoming @code{libports} messages on trivfs ports.
+@end deftypefun
+
+The following functions are not usually necessary, but they allow you to
+use the trivfs library even when it is not possible to turn
+message-handling over to @code{trivfs_demuxer} and @code{libports}:
+
+@deftypefun {struct trivfs_control *} trivfs_begin_using_control (@w{mach_port_t @var{port}})
+@deftypefunx {struct trivfs_protid *} trivfs_begin_using_protid (@w{mach_port_t @var{port}})
+These functions can be used as @code{intran} functions for a MiG port
+type to have the stubs called with either the control or protid pointer.
+@end deftypefun
+
+@c FIXME: `intran' needs to be explained, or else there needs to be
+@c a cross-reference there.
+@c tb: `intran' is a keyword in MiG.
+
+@deftypefun void trivfs_end_using_control (@w{struct trivfs_control *@var{port}})
+@deftypefunx void trivfs_end_using_protid (@w{struct trivfs_protid *@var{port}})
+These can be used as `destructor' functions for a MiG port type, to have
+the stubs called with the control or protid pointer.
+@end deftypefun
+
+@deftypefun error_t trivfs_open (@w{struct trivfs_control *@var{fsys}}, @w{struct iouser *@var{user}}, @w{unsigned @var{flags}}, @w{mach_port_t @var{realnode}}, @w{struct trivfs_protid **@var{cred}})
+Return a new protid (that is, a port representing an open of this node)
+pointing to a new peropen in @var{cred}, with @var{realnode} as the
+underlying node reference, with the given identity, and open flags in
+@var{flags}. @var{cntl} is the trivfs control object.
+@end deftypefun
+
+@deftypefun error_t trivfs_protid_dup (@w{struct trivfs_protid *@var{cred}}, @w{struct trivfs_protid **@var{dup}})
+Return a duplicate of @var{cred} in @var{dup}, sharing the same peropen
+and hook. A non-null protid @var{hook} indicates that
+@var{trivfs_peropen_create_hook} created this protid (@pxref{Trivfs
+Options}).
+@end deftypefun
+
+@deftypefun error_t trivfs_set_atime (@w{struct trivfs_control *@var{cntl}})
+@deftypefunx error_t trivfs_set_mtime (@w{struct trivfs_control *@var{cntl}})
+Call these to set atime or mtime for the node to the current time.
+@end deftypefun
+
+
+@node Trivfs Callbacks
+@subsection Trivfs Callbacks
+
+Like several other Hurd libraries, @code{libtrivfs} requires that you
+define a number of application-specific callback functions and
+configuration variables. You @emph{must} define the following variables
+and functions:
+
+@deftypevar {extern int} trivfs_fstype
+@deftypevarx {extern int} trivfs_fsid
+These variables are returned in the @var{st_fstype} and @var{st_fsid}
+fields of @code{struct stat}. @var{trivfs_fstype} should be chosen
+from the @code{FSTYPE_*} constants found in @code{<hurd/hurd_types.h>}.
+@end deftypevar
+
+@deftypevar {extern int} trivfs_allow_open
+Set this to some bitwise OR combination of @code{O_READ},
+@code{O_WRITE}, and @code{O_EXEC}; trivfs will only allow opens of the
+specified modes.
+@end deftypevar
+
+@deftypevar {extern int} trivfs_support_read
+@deftypevarx {extern int} trivfs_support_write
+@deftypevarx {extern int} trivfs_support_exec
+Set these to nonzero if trivfs should allow read, write, or execute of
+the file. These variables are necessary because @var{trivfs_allow_open}
+is used only to validate opens, not actual operations.
+@end deftypevar
+
+@deftypefun void trivfs_modify_stat (@w{struct trivfs_protid *@var{cred}}, @w{struct stat *@var{stbuf}})
+This should modify a @code{struct stat} (as returned from the underlying
+node) for presentation to callers of @code{io_stat}. It is permissible
+for this function to do nothing, but it must still be defined.
+@end deftypefun
+
+@deftypefun error_t trivfs_goaway (@w{struct trivfs_control *@var{cntl}}, @w{int @var{flags}})
+This function is called when someone wants the filesystem @var{cntl} to
+go away. @var{flags} are from the set @code{FSYS_GOAWAY_*} found in
+@code{<hurd/hurd_types.h>}.
+@end deftypefun
+
+
+@node Trivfs Options
+@subsection Trivfs Options
+
+The functions and variables described in this subsection already have
+default definitions in @code{libtrivfs}, so you are not forced to define
+them; rather, they may be redefined on a case-by-case basis.
+
+@deftypevar {extern struct port_class *} trivfs_protid_portclasses []
+@deftypevarx {extern int} trivfs_protid_nportclasses
+@deftypevarx {extern struct port_class *} trivfs_cntl_portclasses []
+@deftypevarx {extern int} trivfs_cntl_nportclasses
+If you define these, they should be vectors (and the associated sizes)
+of port classes that will be translated into control and protid pointers
+for passing to RPCs, in addition to those passed to or created by
+@code{trivfs_create_control} (or @code{trivfs_startup}), which will
+automatically be recognized.
+@end deftypevar
+
+@deftypefn {Variable} error_t {(*trivfs_check_open_hook)} (@w{struct trivfs_control *@var{cntl}}, @w{struct iouser *@var{user}}, @w{int @var{flags}})
+If this variable is non-zero, it will be called every time an open happens.
+@var{user} and @var{flags} are from the open; @var{cntl} identifies the
+node being opened. This call need not check permissions on the
+underlying node. This call can block as necessary, unless
+@code{O_NONBLOCK} is set in @var{flags}. Any desired error can be
+returned, which will be reflected to the user and will prevent the open from
+succeeding.
+@end deftypefn
+
+@deftypefn {Variable} error_t (*trivfs_protid_create_hook) (@w{struct trivfs_protid *@var{prot}})
+@deftypefnx {Variable} error_t (*trivfs_peropen_create_hook) (@w{struct trivfs_peropen *@var{perop}})
+If these variables are non-zero, they will be called every time a new protid or
+peropen structure is created and initialized.
+@end deftypefn
+
+@deftypefn {Variable} void (*trivfs_protid_destroy_hook) (@w{struct trivfs_protid *@var{prot}})
+@deftypefnx {Variable} void (*trivfs_peropen_destroy_hook) (@w{struct trivfs_peropen *@var{perop}})
+If these variables is non-zero, they will be called every time a protid or
+peropen structure is about to be destroyed.
+@end deftypefn
+
+@deftypefn {Variable} error_t (*trivfs_getroot_hook) (@w{struct trivfs_control *@var{cntl}}, @w{mach_port_t @var{reply_port}}, @w{mach_msg_type_name_t @var{reply_port_type}}, @w{mach_port_t @var{dotdot}}, @w{uid_t *@var{uids}}, @w{u_int @var{nuids}}, @w{uid_t *@var{gids}}, @w{u_int @var{ngids}}, @w{int @var{flags}}, @w{retry_type *@var{do_retry}}, @w{char *@var{retry_name}}, @w{mach_port_t *@var{node}}, @w{mach_msg_type_name_t *@var{node_type}})
+If this variable is set, it will be called by @code{trivfs_S_fsys_getroot}
+before any other processing takes place. If the return value is
+@code{EAGAIN}, normal trivfs getroot processing continues, otherwise the
+RPC returns with that return value.
+@end deftypefn
+
+
+@node Trivfs Ports
+@subsection Trivfs Ports
+
+If you choose to allocate your own trivfs port classes and buckets, the
+following functions may come in handy:
+
+@deftypefun error_t trivfs_add_port_bucket (@w{struct port_bucket **@var{bucket}})
+Add the port bucket @code{*@var{bucket}} to the list of dynamically-
+allocated port buckets; if @code{*@var{bucket}} is zero, an attempt is
+made to allocate a new port bucket, which is then stored in
+@code{*@var{bucket}}.
+@c FIXME: what if the allocation attempt fails?
+@c tb: then an appropriate error (ENOMEM in this case) is returned.
+@c tb: Users are not supposed to assume they know all the possible error
+@c tb: returns. All functions that return error_t are like this.
+@end deftypefun
+
+@deftypefun void trivfs_remove_port_bucket (@w{struct port_bucket *@var{bucket}})
+Remove the previously added dynamic port bucket @var{bucket}, freeing it
+if it was allocated by @code{trivfs_add_port_bucket}.
+@end deftypefun
+
+@deftypefun error_t trivfs_add_control_port_class (@w{struct port_class **@var{class}})
+@deftypefunx error_t trivfs_add_protid_port_class (@w{struct port_class **@var{class}})
+Add the port class @code{*@var{class}} to the list of control or protid port
+classes recognized by trivfs; if @code{*@var{class}} is zero, an attempt is
+made to allocate a new port class, which is stored in @code{*@var{class}}.
+@end deftypefun
+
+@deftypefun void trivfs_remove_control_port_class (@w{struct port_class *@var{class}})
+@deftypefunx void trivfs_remove_protid_port_class (@w{struct port_class *@var{class}})
+Remove the previously added dynamic control or protid port class
+@var{class}, freeing it if it was allocated by
+@code{trivfs_add_control_port_class} or
+@code{trivfs_add_protid_port_class}.
+@end deftypefun
+
+Even if you do not use the above allocation functions, you may still be
+able to use the default trivfs cleanroutines:
+
+@deftypefun void trivfs_clean_cntl (@w{void *@var{port}})
+@deftypefunx void trivfs_clean_protid (@w{void *@var{port}})
+These functions should be installed as @code{libports} cleanroutines for
+control port classes and protid port classes, respectively.
+@end deftypefun
+
+
+@node Fshelp Library
+@section Fshelp Library
+@scindex libfshelp
+@scindex fshelp.h
+
+The fshelp library implements various things that are useful to most
+implementors of the file protocol. It presumes that you are using the
+iohelp library as well. @code{libfshelp} is divided into separate
+facilities which may be used independently. These functions are
+declared in @code{<hurd/fshelp.h>}.
+@c FIXME: perhaps `useful to most implementors' should read `generic
+@c to most implementations'
+
+@menu
+* Passive Translator Linkage:: Invoking passive translators.
+* Active Translator Linkage:: Managing active translators.
+* Fshelp Locking:: Implementing file locking.
+* Fshelp Permissions:: Standard file access permission policies.
+* Fshelp Misc:: Useful standalone routines.
+@end menu
+
+@node Passive Translator Linkage
+@subsection Passive Translator Linkage
+
+These routines are self-contained and start passive translators,
+returning the control port. They do not require multithreading or the
+ports library.
+
+@deftypefn {Typedef} typedef error_t (*fshelp_open_fn_t) (@w{int @var{flags}}, @w{file_t *@var{node}}, @w{mach_msg_type_name_t *@var{node_type}})
+A callback used by the translator starting functions.
+Given some open flags, opens the appropriate file, and
+returns the node port.
+@end deftypefn
+
+@deftypefun error_t fshelp_start_translator_long (@w{fshelp_open_fn_t @var{underlying_open_fn}}, @w{char *@var{name}}, @w{char *@var{argz}}, @w{int @var{argz_len}}, @w{mach_port_t *@var{fds}}, @w{mach_msg_type_name_t @var{fds_type}}, @w{int @var{fds_len}}, @w{mach_port_t *@var{ports}}, @w{mach_msg_type_name_t @var{ports_type}}, @w{int @var{ports_len}}, @w{int *@var{ints}}, @w{int @var{ints_len}}, @w{int @var{timeout}}, @w{fsys_t *@var{control}})
+Start a passive translator @var{name} with arguments @var{argz} (length
+@var{argz_len}). Initialize the initports to @var{ports} (length
+@var{ports_len}), the initints to @var{ints} (length @var{ints_len}),
+and the file descriptor table to @var{fds} (length @var{fds_len}).
+Return the control port in @code{*@var{control}}. If the translator doesn't
+respond or die in @var{timeout} milliseconds (if @var{timeout} is
+greater than zero), return an appropriate error. If the translator dies
+before responding, return @code{EDIED}.
+@end deftypefun
+
+@deftypefun error_t fshelp_start_translator (@w{fshelp_open_fn_t @var{underlying_open_fn}}, @w{char *@var{name}}, @w{char *@var{argz}}, @w{int @var{argz_len}}, @w{int @var{timeout}}, @w{fsys_t *@var{control}})
+Same as @code{fshelp_start_translator_long}, except the initports and
+ints are copied from our own state, @var{fd}[2] is copied from our own
+stderr, and the other fds are cleared. For full-service filesystems, it
+is almost always wrong to use @code{fshelp_start_translator}, because
+the current working directory of the translator will not then be as
+normally expected. (Current working directories of passive translators
+should be the directory they were found in.) In fact, full-service
+filesystems should usually start passive translators as a side-effect of
+calling @code{fshelp_fetch_root} (@pxref{Active Translator Linkage}).
+@end deftypefun
+
+@node Active Translator Linkage
+@subsection Active Translator Linkage
+
+These routines implement the linkage to active translators needed
+by any filesystem which supports them. They require the threads
+library and use the passive translator routines above, but they don't
+require the ports library at all.
+
+This interface is complex, because creating the ports and state
+necessary for @code{start_translator_long} is expensive. The caller to
+@code{fshelp_fetch_root} should not need to create them on every call,
+since usually there will be an existing active translator.
+
+@deftypefun void fshelp_transbox_init (@w{struct transbox *@var{transbox}}, @w{struct mutex *@var{lock}}, @w{void *@var{cookie}})
+Initialize a transbox, which contains state information for active
+translators.
+@end deftypefun
+
+@deftypefn {Typedef} typedef error_t (*fshelp_fetch_root_callback1_t) (@w{void *@var{cookie1}}, @w{void *@var{cookie2}}, @w{uid_t *@var{uid}}, @w{gid_t *@var{gid}}, @w{char **@var{argz}}, @w{size_t *@var{argz_len}})
+This routine is called by @code{fshelp_fetch_root} to fetch more
+information. Return the owner and group of the underlying translated
+file in @code{*@var{uid}} and @code{*@var{gid}}; point
+@code{*@var{argz}} at the entire passive translator specification for
+the file (setting @code{*@var{argz_len}} to the length). If there is no
+passive translator, then return @code{ENOENT}. @var{cookie1} is the
+cookie passed in @code{fshelp_transbox_init}. @var{cookie2} is the
+cookie passed in the call to @code{fshelp_fetch_root}.
+@end deftypefn
+
+@deftypefn {Typedef} typedef error_t (*fshelp_fetch_root_callback2_t) (@w{void *@var{cookie1}}, @w{void *@var{cookie2}}, @w{int @var{flags}}, @w{mach_port_t *@var{underlying}}, @w{mach_msg_type_name_t *@var{underlying_type}})
+This routine is called by @code{fshelp_fetch_root} to fetch more
+information. Return an unauthenticated node for the file itself in
+@code{*@var{underlying}} and @code{*@var{underlying_type}} (opened with
+@var{flags}). @var{cookie1} is the cookie passed in
+@code{fshelp_transbox_init}. @var{cookie2} is the cookie passed in the
+call to @code{fshelp_fetch_root}.
+@end deftypefn
+
+@deftypefun error_t fshelp_fetch_root (@w{struct transbox *@var{transbox}}, @w{void *@var{cookie}}, @w{file_t @var{dotdot}}, @w{struct iouser *@var{user}}, @w{int @var{flags}}, @w{fshelp_fetch_root_callback1_t @var{callback1}}, @w{fshelp_fetch_root_callback2_t @var{callback2}}, @w{retry_type *@var{retry}}, @w{char *@var{retryname}}, @w{mach_port_t *@var{root}})
+Fetch the root from @var{transbox}. @var{dotdot} is an unauthenticated
+port for the directory in which we are looking; @var{user} specifies the
+ids of the user responsible for the call. @var{flags} are as for
+@code{dir_lookup} (but @code{O_CREAT} and @code{O_EXCL} are not
+meaningful and are ignored). The transbox lock (as set by
+@code{fshelp_transbox_init}) must be held before the call, and will be
+held upon return, but may be released during the operation of the call.
+@end deftypefun
+
+@deftypefun int fshelp_translated (@w{struct transbox *@var{box}})
+Return true if and only if there is an active translator on this box.
+@end deftypefun
+
+@deftypefun error_t fshelp_set_active (@w{struct transbox *@var{box}}, @w{fsys_t @var{newactive}}, @w{int @var{excl}})
+Atomically replace the existing active translator port for this box with
+@var{newactive}. If @var{excl} is non-zero then don't modify an
+existing active transbox; return @code{EBUSY} instead.
+@end deftypefun
+
+@deftypefun error_t fshelp_fetch_control (@w{struct transbox *@var{box}}, @w{mach_port_t *@var{control}})
+Fetch the control port to make a request on it. It's a bad idea to use
+@code{fsys_getroot} with the result; use @code{fshelp_fetch_root}
+instead.
+@end deftypefun
+
+@deftypefun void fshelp_drop_transbox (@w{struct transbox *@var{box}})
+Clean transbox state so that deallocation or reuse is possible.
+@end deftypefun
+
+
+@node Fshelp Locking
+@subsection Fshelp Locking
+
+The @code{flock} call is in flux, as the current Hurd interface (as of
+version @value{VERSION}) is not suitable for implementing the POSIX
+record-locking semantics.
+
+
+@node Fshelp Permissions
+@subsection Fshelp Permissions
+
+These functions are designed to aid with user permission checking. It
+is a good idea to use these routines rather than to roll your own, so
+that Hurd users see consistent handling of file and directory permission
+bits.
+
+@deftypefun error_t fshelp_isowner (@w{struct stat *@var{st}}, @w{struct iouser *@var{user}})
+Check to see whether @var{user} should be considered the owner of the
+file identified by @var{st}. If so, return zero; otherwise return an
+appropriate error code.
+@end deftypefun
+
+@deftypefun error_t fshelp_access (@w{struct stat *@var{st}}, @w{int @var{op}}, @w{struct iouser *@var{user}})
+Check to see whether the user @var{user} can operate on the file
+identified by @var{st}. @var{op} is one of @code{S_IREAD},
+@code{S_IWRITE}, and @code{S_IEXEC}. If the access is permitted, return
+zero; otherwise return an appropriate error code.
+@end deftypefun
+
+@deftypefun error_t fshelp_checkdirmod (@w{struct stat *@var{dir}}, @w{struct stat *@var{st}}, @w{struct iouser *@var{user}})
+Check to see whether @var{user} is allowed to modify @var{dir} with respect to
+existing file @var{st}. If there is no existing file, then @var{st}
+should be set to zero. If the access is permissible, return zero;
+otherwise return an appropriate error code.
+@c FIXME: what does it mean to modify a directory with respect to an
+@c existing file?
+@c tb: If you delete a file, say, then you are modifying the directory
+@c tb: (not the file) but with respect to that file. This is relevant
+@c tb: in implementing the directory sticky-bit permissions algorithm.
+@end deftypefun
+
+@node Fshelp Misc
+@subsection Fshelp Misc
+
+The following functions are completely standalone:
+
+@deftypefun error_t fshelp_delegate_translation (@w{char *@var{server_name}}, @w{mach_port_t @var{requestor}}, @w{char **@var{argv}})
+Try to hand off responsibility from a translator to the server located
+on the node @var{server_name}. @var{requestor} is the translator's
+bootstrap port, and @var{argv} is the command line. If
+@var{server_name} is null, then a name is concocted by prepending
+@code{_servers} to @code{argv[0]} .
+@end deftypefun
+
+@deftypefun error_t fshelp_exec_reauth (@w{int @var{suid}}, @w{uid_t @var{uid}}, @w{int @var{sgid}}, @w{gid_t @var{gid}}, @w{auth_t @var{auth}}, error_t (*@var{get_file_ids}) (@w{struct idvec *@var{uids}}, @w{struct idvec *@var{gids}}), @w{mach_port_t *@var{ports}}, @w{mach_msg_type_number_t @var{num_ports}}, @w{mach_port_t *@var{fds}}, @w{mach_msg_type_number_t @var{num_fds}}, @w{int *@var{secure}})
+If @var{suid} or @var{sgid} is true, adds @var{uid} and/or @var{gid}
+respectively to the authentication in
+@code{@var{ports}[INIT_PORT_AUTH]}, and replaces it with the result.
+All the other ports in @var{ports} and @var{fds} are then
+reauthenticated, using any privileges available through @var{auth}. If
+the auth port in @code{@var{ports}[INIT_PORT_AUTH]} is bogus, and
+@var{get_file_ids} is non-null, it is called to get a list
+of uids and gids from the file to use as a replacement. If @var{secure}
+is non-null and any added ids are new, then the variable it points to is
+set to nonzero, otherwise zero. If either the uid or gid case fails,
+then the other may still apply.
+@end deftypefun
+
+@deftypefun error_t fshelp_get_identity (@w{struct port_bucket *@var{bucket}}, @w{ino_t @var{fileno}}, @w{mach_port_t *@var{pt}})
+Return an identity port in @code{*@var{pt}} for the node numbered
+@var{fileno}, suitable for returning from @code{io_identity}; exactly
+one send right must be created from the returned value. @var{fileno}
+should be the same value returned as the @var{fileno} out-parameter in
+@code{io_identity}, and in the enclosing directory (except for mount
+points), and in the @code{st_ino} stat field. @var{bucket} should be a
+@code{libports} port bucket; fshelp requires the caller to make sure
+port operations (for no-senders notifications) are used.
+@end deftypefun
+
+@deftypefun error_t fshelp_return_malloced_buffer (@w{char *@var{buf}}, @w{size_t @var{len}}, @w{char **@var{rbuf}}, @w{mach_msg_type_number_t *@var{rlen}})
+Put data from the malloced buffer @var{buf}, @var{len} bytes long, into
+@var{rbuf} (which is @var{rlen} bytes long), suitable for returning from
+an RPC. If @var{len} is greater than zero, @var{buf} is freed,
+regardless of whether an error is returned or not.
+@end deftypefun
+
+@deftypefun error_t fshelp_set_options (@w{const struct argp *@var{argp}}, @w{int @var{flags}}, @w{char *@var{argz}}, @w{size_t @var{argz_len}}, @w{void *@var{input}})
+Invoke @code{argp_parse} in the standard way, with data from @var{argz}
+and @var{argz_len}.
+@end deftypefun
+
+@deftypefun void fshelp_touch (@w{struct stat *@var{st}}, @w{unsigned @var{what}}, @w{volatile struct mapped_time_value *@var{maptime}})
+Change the stat times of @var{node} as indicated by @var{what} to
+the current time. @var{what} is a bitmask of one or more of
+the @code{TOUCH_ATIME}, @code{TOUCH_MTIME}, and @code{TOUCH_CTIME}
+constants.
+@end deftypefun
+
+
+@node File Interface
+@section File Interface
+@scindex fs.defs
+
+This section documents the interface for operating on files.
+
+@menu
+* File Overview:: Basic concepts for the file interface.
+* Changing Status:: Changing the owner (etc.) of a file.
+* Program Execution:: Executing files.
+* File Locking:: Implementing the @code{flock} call.
+* File Frobbing:: Other active calls on files.
+* Opening Files:: Looking up files in directories.
+* Modifying Directories:: Creating and deleting nodes.
+* Notifications:: File and directory change callbacks.
+* File Translators:: How to set and get translators.
+@end menu
+
+@node File Overview
+@subsection File Overview
+
+The file interface is a superset of the I/O interface (@pxref{I/O
+Interface}). Servers which provide the file interface are required to
+support the I/O interface as well. All objects reachable in the
+filesystem are expected to provide the file interface, even if they do
+not contain data. (The @code{trivfs} library makes it easy to do so for
+ordinary sorts of cases. @xref{Trivfs Library}.)
+
+The interface definitions for the file interface are found in
+@code{<hurd/fs.defs>}.
+
+Files have various pieces of status information which are returned by
+@code{io_stat} (@pxref{Information Queries}). Most of this status
+information can be directly changed by various calls in the file
+interface; some of it should vary implicitly as the contents of the file
+change.
+
+Many of these calls have general rules associated with them describing
+how security and privilege should operate. The @code{diskfs} library
+(@pxref{Diskfs Library}) implements these rules for stored filesystems.
+These rules have also been implemented in the fshelp library
+(@pxref{Fshelp Library}). Trivfs-based servers generally have no need
+to implement these rules at all.
+
+In special cases, there may be a reason to implement a different
+security check from that specified here, or to implement a call to do
+something slightly different. But such cases must be carefully
+considered; make sure that you will not confuse innocent user programs
+through excessive cleverness.
+
+If some operation cannot be implemented (for example, @code{chauthor}
+over FTP), then the call should return @code{EOPNOTSUPP}. If it is
+merely difficult to implement a call, it is much better to figure out a
+way to implement it as a series of operations rather than to return
+errors to the user.
+
+@node Changing Status
+@subsection Changing Status
+
+There are several RPCs available for users to change much of the status
+information associated with a file. (The information is returned by the
+@code{io_stat} RPC; see @ref{Information Queries}.)
+
+All these operations are restricted to root and the owner of the file.
+When attempted by another user, they should return @code{EPERM}.
+
+@findex file_chown
+The @code{file_chown} RPC changes the owner and group of the file. Only
+root should be able to change the owner, and changing the group to a
+group the caller is not in should also be prohibited. Violating either
+of these conditions should return @code{EPERM}.
+
+@findex file_chauthor
+The @code{file_chauthor} RPC changes the author of the file. It should
+be legitimate to change the author to any value without restriction.
+
+@findex file_chmod
+The @code{file_chmod} RPC changes the file permission mode bits.
+
+@findex file_chflags
+The @code{file_chflags} RPC changes the flags of the file. It should be
+legitimate to change the flags to any value without restriction. No
+standard meanings have been assigned to the flags yet, but we intend to
+do so. Do not assume that the flags format we choose will map
+identically to that of some existing filesystem format.
+
+@findex file_utimes
+The @code{file_utimes} RPC changes the @var{atime} and @var{mtime} of
+the file. Making this call must cause the @var{ctime} to be updated as
+well, even if no actual change to either the @var{mtime} or the
+@var{atime} occurs.
+
+@findex file_set_size
+The @code{file_set_size} RPC is special; not only does it change the
+status word specifying the size of the file, but it also changes the
+actual contents of the file. If the file size is being reduced it
+should release secondary storage associated with the previous contents
+of the file. If the file is being extended, the new region added to the
+file must be zero-filled. Unlike the other RPCs in this section,
+@code{file_set_size} should be permitted to any user who is allowed to
+write the file.
+
+
+@node Program Execution
+@subsection Program Execution
+
+@findex file_exec
+Execution of programs on the Hurd is done through fileservers with the
+@code{file_exec} RPC. The fileserver is expected to verify that the
+user is allowed to execute the file, make whatever modifications to the
+ports are necessary for setuid execution, and then invoke the standard
+execserver found on @file{/servers/exec}.
+
+This section specifically addresses what fileservers are expected to do,
+with minimal attention to the other parts of the process. @xref{Running
+Programs}, for more general information.
+
+The file must be opened for execution; if it is not, @code{EBADF} should
+be returned. In addition, at least one of the execute bits must be on. A
+failure of this check should result in @code{EACCES}---not
+@code{ENOEXEC}. It is not proper for the fileserver ever to respond to
+the @code{file_exec} RPC with @code{ENOEXEC}.
+
+If either the setuid or setgid bits are set, the server needs to
+construct a new authentication handle with the additional new ID's.
+Then all the ports passed to @code{file_exec} need to be reauthenticated
+with the new handle. If the fileserver is unable to make the new
+authentication handle (for example, because it is not running as root)
+it is not acceptable to return an error; in such a case the server
+should simply silently fail to implement the setuid/setgid semantics.
+
+If the setuid/setgid transformation adds a new uid or gid to the user's
+authentication handle that was not previously present (as opposed to
+merely reordering them), then the @code{EXEC_SECURE} and
+@code{EXEC_NEWTASK} flags should both be added in the call to
+@code{exec_exec}.
+
+The server then needs to open a new port onto the executed file which
+will not share any file pointers with the port the user passed in,
+opened with @code{O_READ}. Finally, all the information (mutated
+appropriately for setuid/setgid) should be sent to the execserver with
+@code{exec_exec}. Whatever error code @code{exec_exec} returns should
+returned to the caller of @code{file_exec}.
+
+@node File Locking
+@subsection File Locking
+
+The @code{flock} call is in flux, as the current Hurd interface (as of
+version @value{VERSION}) is not suitable for implementing the POSIX
+record-locking semantics.
+
+@findex file_lock
+@findex file_lock_stat
+You should ignore the @code{file_lock} and @code{file_lock_stat} calls
+until the new record-locking interface is implemented.
+
+
+@node File Frobbing
+@subsection File Frobbing
+
+FIXME: Other active calls on files
+
+@code{file_sync}
+
+@code{file_getfh}
+
+@code{file_getlinknode}
+
+@code{file_check_access}
+
+These manipulate meta-information:
+
+@code{file_reparent}
+
+@code{file_statfs}
+
+@code{file_syncfs}
+
+@code{file_getcontrol}
+
+@code{file_get_storage_info}
+
+@code{file_get_fs_options}
+
+
+@node Opening Files
+@subsection Opening Files
+
+FIXME: Looking up files in directories
+
+@code{dir_lookup}
+
+@code{dir_readdir}
+
+@node Modifying Directories
+@subsection Modifying Directories
+
+@deftypefun kern_return_t dir_mkfile (@w{file_t @var{directory}}, @w{int @var{flags}}, @w{mode_t @var{mode}}, @w{mach_port_t *@var{newnode}})
+Create a new file in @var{directory} without linking it into the
+filesystem. You still must have write permission on the specified
+directory, even though it will not actually be written.
+
+The function returns a port to the new file in *@var{newnode}. Flags
+are the same as for @code{dir_lookup}, but @code{O_CREAT} and
+@code{O_TRUNC} are assumed even if not specified.
+@end deftypefun
+
+@deftypefun kern_return_t dir_mkdir (@w{file_t @var{directory}}, @w{char *@var{name}}, @w{mode_t @var{mode}})
+Create a new directory named @var{name} in @var{directory} with
+permission specified by @var{mode}.
+@end deftypefun
+
+@deftypefun kern_return_t dir_rmdir (@w{file_t @var{directory}}, @w{char *@var{name}})
+Remove the directory named @var{name} from @var{directory}.
+@end deftypefun
+
+@deftypefun kern_return_t dir_unlink (@w{file_t @var{directory}}, @w{char *@var{name}})
+Remove the non-directory node @var{name} from @var{directory}.
+@end deftypefun
+
+@deftypefun kern_return_t dir_link (@w{file_t @var{directory}}, @w{file_t @var{file}}, @w{char *@var{name}}, @w{int @var{excl}})
+Create a hard link in @var{directory}. If @var{excl} is set and
+@var{name} already exists in @var{directory}, then this function will
+fail. If @var{excl} is not set and @var{name} already exists the old
+file named @var{name} will be unlinked. If @var{directory} and
+@var{file} are not on the same filesystem, then @code{dir_link} might
+fail with @code{EXDEV}.
+@end deftypefun
+
+@deftypefun kern_return_t dir_rename (@w{file_t @var{olddirectory}}, @w{char *@var{oldname}}, @w{file_t @var{newdirectory}}, @w{char *@var{newname}}, @w{int @var{excl}})
+Move the node @var{oldname} in @var{olddirectory} to the node
+@var{newname} in @var{newdirectory}. If @var{excl} is set and
+@var{newname} already exists in @var{newdirectory}, then this function
+will fail. If @var{excl} is not set and @var{newname} already exists,
+the old file named @var{newname} will be unlinked. If
+@var{olddirectory} and @var{newdirectory} are not on the same
+filesystem, then @code{dir_rename} might fail with @code{EXDEV}.
+@end deftypefun
+
+@node Notifications
+@subsection Notifications
+
+FIXME: File and directory change callbacks
+
+File change notifications are not yet implemented, but directory
+notifications are.
+
+@code{file_notice_changes}
+
+@code{dir_notice_changes}
+
+@node File Translators
+@subsection File Translators
+
+FIXME: How to set and get translators
+
+@code{file_set_translator}
+
+@code{file_get_translator}
+
+@code{file_get_translator_cntl}
+
+
+@node Filesystem Interface
+@section Filesystem Interface
+@scindex fsys.defs
+
+The filesystem interface (described in @code{<hurd/fsys.defs>}) is
+supported by translator control ports.
+
+FIXME: finish
+
+
+@node Special Files
+@chapter Special Files
+
+In Unix, any file that does not act as a general-purpose unit of storage
+is called a @dfn{special file}. These are FIFOs, Unix-domain sockets,
+and device nodes. In the Hurd, there is no need for the ``special
+file'' distinction, since they are implemented by translators, just as
+regular files are.
+
+Nevertheless, the Hurd maintains this distinction, in order to provide
+backward compatibility for Unix programs (which do not know about
+translators). Studying the implementation of Hurd special files is a
+good way to introduce the idea of translators to people who are familiar
+with Unix.
+
+This chapter does not discuss @file{/dev/zero} or any of the
+microkernel-based devices, since these are translated by the generalized
+storeio server (FIXME xref).
+
+FIXME: finish
+
+@section fifo
+@section ifsock
+@section magic
+@section null
+
+
+FIXME: a chapter on libtreefs and libdirmgt will probably go here
+
+
+@node Stores
+@chapter Stores
+
+A @dfn{store} is a fixed-size block of storage, which can be read and
+perhaps written to. A store is more general than a file: it refers to
+any type of storage such as devices, files, memory, tasks, etc. Stores
+can also be representations of other stores, which may be combined and
+filtered in various ways.
+
+@menu
+* Store Library:: An abstract interface to storage systems.
+@end menu
+
+@section storeinfo, storecat, storeread
+@section storeio
+
+FIXME: finish
+
+@node Store Library
+@section Store Library
+@scindex libstore
+@scindex store.h
+
+The store library (which is declared in @code{<hurd/store.h>})
+implements many different backends which support the store abstraction.
+Hurd programs use @code{libstore} so that new storage types can be
+implemented with minimum impact.
+
+@menu
+* Store Arguments:: Parsing store command-line arguments.
+* Store Management:: Creating and manipulating stores.
+* Store I/O:: Reading and writing data to stores.
+* Store Classes:: Ready-to-use storage backends.
+* Store RPC Encoding:: Transferring store descriptors via RPC.
+@end menu
+
+
+@node Store Arguments
+@subsection Store Arguments
+
+FIXME: describe startup sequence
+
+@deftypevr {Structure} struct store_parsed
+The result of parsing a store, which should be enough information to
+open it, or return the arguments.
+@end deftypevr
+
+@deftypefn {Structure} struct store_argp_params @{ @w{struct store_parsed *@var{result}}; @w{const char *@var{default_type}}; @w{const struct store_class *const *@var{classes}}; @}
+This is the structure used to pass args back and forth from
+@var{store_argp}. @var{result} is the resulting parsed result. If
+@samp{--store-type} isn't specified, then @var{default_type} should be
+used as the store type; zero is equivalent to @code{"query"}.
+@var{classes} is set of classes used to validate store types and
+argument syntax.
+@end deftypefn
+
+@deftypevar {extern struct argp} store_argp
+This is an argument parser that may be used for parsing a simple command
+line specification for stores. The accompanying input parameter must be
+a pointer to a @code{struct store_argp_params}.
+@end deftypevar
+
+@deftypefun void store_parsed_free (@w{struct store_parsed *@var{parsed}})
+Free all resources used by @var{parsed}.
+@end deftypefun
+
+@deftypefun error_t store_parsed_open (@w{const struct store_parsed *@var{parsed}}, @w{int @var{flags}}, @w{struct store **@var{store}})
+Open the store specified by @var{parsed}, and return it in @var{store}.
+@end deftypefun
+
+@deftypefun error_t store_parsed_append_args (@w{const struct store_parsed *@var{parsed}}, @w{char **@var{argz}}, @w{size_t *@var{argz_len}})
+Add the arguments used to create @var{parsed} to @var{argz} and
+@var{argz_len}.
+@end deftypefun
+
+@deftypefun error_t store_parsed_name (@w{const struct store_parsed *@var{parsed}}, @w{char **@var{name}})
+Make an option string describing @var{parsed}, and return it in malloced
+storage in @var{name}.
+@end deftypefun
+
+
+@node Store Management
+@subsection Store Management
+
+The following functions provide basic management of stores:
+
+@deftypefun error_t store_create (@w{file_t @var{source}}, @w{int @var{flags}}, @w{const struct store_class *const *@var{classes}}, @w{struct store **@var{store}})
+Return a new store in @var{store}, which refers to the storage
+underlying @var{source}. @var{classes} is used to select classes
+specified by the provider; if zero, @var{store_std_classes} is used.
+@var{flags} is set with @code{store_set_flags}, with the exception of
+@code{STORE_INACTIVE}, which merely indicates that no attempt should be
+made to activate an inactive store; if @code{STORE_INACTIVE} is not
+specified, and the store returned for SOURCE is inactive, an attempt is
+made to activate it (failure of which causes an error to be returned).
+A reference to @var{source} is created (but may be destroyed with
+@code{store_close_source}).
+
+It is usually better to use a specific store open or create function
+such as @code{store_open} (@pxref{Store Classes}), since they are
+tailored to the needs of a specific store. Generally, you should only
+use @code{store_create} if you are defining your own store class, or you
+need options that are not provided by a more specific store creation
+function.
+@end deftypefun
+
+@deftypefun void store_close_source (@w{struct store *@var{store}})
+If @var{store} was created using @code{store_create}, remove the
+reference to the source from which it was created.
+@end deftypefun
+
+@deftypefun void store_free (@w{struct store *@var{store}})
+Clean up and deallocate @var{store}'s underlying stores.
+@end deftypefun
+
+@deftypefn {Structure} struct store_run @{ @w{store_offset_t @var{start}}, @var{length}; @}
+A @code{struct store_run} represents a contiguous region in a store's
+address range. These are used to designate active portions of a store.
+If @var{start} is -1, then the region is a @dfn{hole} (it is zero-filled
+and doesn't correspond to any real addresses).
+@end deftypefn
+
+@deftypefun error_t store_set_runs (@w{struct store *@var{store}}, @w{const struct store_run *@var{runs}}, @w{size_t @var{num_runs}})
+Set @var{store}'s current runs list to (a copy of) @var{runs} and
+@var{num_runs}.
+@end deftypefun
+
+@deftypefun error_t store_set_children (@w{struct store *@var{store}}, @w{struct store *const *@var{children}}, @w{size_t @var{num_children}})
+Set @var{store}'s current children to (a copy of) @var{children} and
+@var{num_children} (note that just the vector @var{children} is copied,
+not the actual children).
+@end deftypefun
+
+@deftypefun error_t store_children_name (@w{const struct store *@var{store}}, @w{char **@var{name}})
+Try to come up with a name for the children in @var{store}, combining
+the names of each child in a way that could be used to parse them with
+@code{store_open_children}. This is done heuristically, and so may not
+succeed. If a child doesn't have a name, @code{EINVAL} is returned.
+@end deftypefun
+
+@deftypefun error_t store_set_name (@w{struct store *@var{store}}, @w{const char *@var{name}})
+Sets the name associated with @var{store} to a copy of @var{name}.
+@end deftypefun
+
+@deftypefun error_t store_set_flags (@w{struct store *@var{store}}, @w{int @var{flags}})
+Add @var{flags} to @var{store}'s currently set flags.
+@end deftypefun
+
+@deftypefun error_t store_clear_flags (@w{struct store *@var{store}}, @w{int @var{flags}})
+Remove @var{flags} from @var{store}'s currently set flags.
+@end deftypefun
+
+@deftypefun error_t store_set_child_flags (@w{struct store *@var{store}}, @w{int @var{flags}})
+Set @var{flags} in all children of @var{store}, and if successful, add
+@var{flags} to @var{store}'s flags.
+@end deftypefun
+
+@deftypefun error_t store_clear_child_flags (@w{struct store *@var{store}}, @w{int @var{flags}})
+Clear @var{flags} in all children of @var{store}, and if successful,
+remove @var{flags} from @var{store}'s flags.
+@end deftypefun
+
+@deftypefun int store_is_securely_returnable (@w{struct store *@var{store}}, @w{int @var{open_flags}})
+Returns true if @var{store} can safely be returned to a user who has
+accessed it via a node using @var{open_flags}, without compromising
+security.
+@end deftypefun
+
+@deftypefun error_t store_clone (@w{struct store *@var{from}}, @w{struct store **@var{to}})
+Return a copy of @var{from} in @var{to}.
+@end deftypefun
+
+@deftypefun error_t store_remap (@w{struct store *@var{source}}, @w{const struct store_run *@var{runs}}, @w{size_t @var{num_runs}}, @w{struct store **@var{store}})
+Return a store in @var{store} that reflects the blocks in @var{runs} and
+@var{runs_len} from source; @var{source} is consumed, but not
+@var{runs}. Unlike the @code{store_remap_create} function, this may
+simply modify @var{source} and return it.
+@end deftypefun
+
+@c FIXME: what does `is consumed' mean?
+@c tb: gone; you can't use it any more. libstore has taken it over.
+
+@node Store I/O
+@subsection Store I/O
+
+The following functions allow you to read and modify the contents of a
+store:
+
+@deftypefun error_t store_map (@w{const struct store *@var{store}}, @w{vm_prot_t @var{prot}}, @w{mach_port_t *@var{memobj}})
+Return a memory object paging on @var{store}.
+@ignore @c FIXME: update if/when there are more pager-related functions
+If this call fails with @code{EOPNOTSUPP}, you can try calling some of
+the routines below to get a pager.
+@end ignore
+@end deftypefun
+
+@deftypefun error_t store_read (@w{struct store *@var{store}}, @w{store_offset_t @var{addr}}, @w{size_t @var{amount}}, @w{void **@var{buf}}, @w{size_t *@var{len}})
+Read @var{amount} bytes from @var{store} at @var{addr} into @var{buf}
+and @var{len} (which follows the usual Mach buffer-return semantics) to
+@var{store} at @var{addr}. @var{addr} is in @var{blocks} (as defined by
+@code{@var{store}->block_size}). Note that @var{len} is in bytes.
+@end deftypefun
+
+@c FIXME: should be say `Mach' above, or should we say
+@c `microkernel'?
+@c tb: nope, Mach-specific semantics.
+
+@deftypefun error_t store_write (@w{struct store *@var{store}}, @w{store_offset_t @var{addr}}, @w{void *@var{buf}}, @w{size_t @var{len}}, @w{size_t *@var{amount}})
+Write @var{len} bytes from @var{buf} to @var{store} at @var{addr}.
+Returns the amount written in @var{amount} (in bytes). @var{addr} is in
+@var{blocks} (as defined by @code{@var{store}->block_size}).
+@end deftypefun
+
+@deftypefun error_t store_set_size (@w{struct store *@var{store}}, @w{store_offset_t @var{newsize}})
+Set @var{store}'s size to @var{newsize} (in bytes).
+@end deftypefun
+
+@node Store Classes
+@subsection Store Classes
+
+The store library comes with a number of standard store class
+implementations:
+
+@deftypevar {extern const struct store_class *const} store_std_classes []
+This is a null-terminated vector of the standard store classes
+implemented by @code{libstore}.
+@end deftypevar
+
+If you are building your own class vectors, the following function may
+be useful:
+
+@deftypevar error_t store_concat_class_vectors (@w{struct store_class **@var{cv1}}, @w{struct store_class **@var{cv2}}, @w{struct store_class ***@var{concat}})
+Concatenate the store class vectors in @var{cv1} and @var{cv2}, and
+return a new (malloced) vector in @var{concat}.
+@end deftypevar
+
+@subsubsection @code{query} store
+@cindex @code{query} store
+
+@deftypevar {extern const struct store_class} store_query_class
+This store is a virtual store which queries a filesystem node, and
+delegates control to an appropriate store class.
+@end deftypevar
+
+@deftypefun error_t store_open (@w{const char *@var{name}}, @w{int @var{flags}}, @w{const struct store_class *const *@var{classes}}, @w{struct store **@var{store}})
+Open the file @var{name}, and return a new store in @var{store}, which
+refers to the storage underlying it. @var{classes} is used to select
+classes specified by the provider; if it is zero, then
+@var{store_std_classes} is used. @var{flags} is set with
+@code{store_set_flags}. A reference to the open file is created (but
+may be destroyed with @code{store_close_source}).
+@end deftypefun
+
+@subsubsection @code{typed_open} store
+@cindex @code{typed_open} store
+
+@deftypevar {extern const struct store_class} store_typed_open_class
+This store is special in that it doesn't correspond to any specific
+store functions, rather it provides a way to interpret character strings
+as specifications for other stores.
+@end deftypevar
+
+@deftypefun error_t store_typed_open (@w{const char *@var{name}}, @w{int @var{flags}}, @w{const struct store_class *const *@var{classes}}, @w{struct store **@var{store}})
+Open the store indicated by @var{name}, which should consist of a store
+type name followed by a @samp{:} and any type-specific name, returning the
+new store in @var{store}. @var{classes} is used to select classes
+specified by the type name; if it is zero, @var{store_std_classes} is
+used.
+@end deftypefun
+
+@deftypefun error_t store_open_children (@w{const char *@var{name}}, @w{int @var{flags}}, @w{const struct store_class *const *@var{classes}}, @w{struct store ***@var{stores}}, @w{size_t *@var{num_stores}})
+Parse multiple store names in @var{name}, and open each individually,
+returning all in the vector @var{stores}, and the number in
+@var{num_stores}. The syntax of @var{name} is a single non-alphanumeric
+separator character, followed by each child store name separated by the
+same separator; each child name is @samp{@var{type}:@var{name}} notation
+as parsed by @code{store_typed_open}. If every child uses the same
+@samp{@var{type}:} prefix, then it may be factored out and put before
+the child list instead (the two notations are differentiated by whether
+or not the first character of @var{name} is alphanumeric).
+@end deftypefun
+
+@subsubsection @code{device} store
+@cindex @code{device} store
+
+@cindex @code{device drivers}
+@deftypevar {extern const struct store_class} store_device_class
+This store is a simple wrapper for a microkernel device
+driver.@footnote{It is important to note that device drivers are not
+provided by the Hurd, but by the underlying microkernel. Hurd `devices'
+are just storeio-translated nodes which make the microkernel device
+drivers obey Hurd semantics. If you wish to implement a new device
+driver, you will need to consult the appropriate microkernel
+documentation.}
+@end deftypevar
+
+@deftypefun error_t store_device_open (@w{const char *@var{name}}, @w{int @var{flags}}, @w{struct store **@var{store}})
+Open the device named @var{name}, and return the corresponding store in
+@var{store}.
+@end deftypefun
+
+@deftypefun error_t store_device_create (@w{device_t @var{device}}, @w{int @var{flags}}, @w{struct store **@var{store}})
+Return a new store in @var{store} referring to the microkernel device
+@var{device}. Consumes the @var{device} send right.
+@end deftypefun
+
+@subsubsection @code{file} store
+@cindex @code{file} store
+
+@deftypevar {extern const struct store_class} store_file_class
+This store reads and writes the contents of a Hurd file.
+@end deftypevar
+
+@deftypefun error_t store_file_open (@w{const char *@var{name}}, @w{int @var{flags}}, @w{struct store **@var{store}})
+Open the file @var{name}, and return the corresponding store in @var{store}.
+@end deftypefun
+
+@deftypefun error_t store_file_create (@w{file_t @var{file}}, @w{int @var{flags}}, @w{struct store **@var{store}})
+Return a new store in @var{store} referring to the file @var{file}.
+Unlike @code{store_create}, this will always use file I/O, even it would
+be possible to be more direct. This may work in more cases, for instance
+if the file has holes. Consumes the @var{file} send right.
+@end deftypefun
+
+@subsubsection @code{task} store
+@cindex @code{task} store
+
+@deftypevar {extern const struct store_class} store_task_class
+This store provides access to the contents of a microkernel task.
+@end deftypevar
+
+@deftypevar error_t store_task_open (@w{const char *@var{name}}, @w{int @var{flags}}, @w{struct store **@var{store}})
+Open the task @var{name} (@var{name} should be the task's pid), and
+return the corresponding store in @var{store}.
+@end deftypevar
+
+@deftypevar {error_t} store_task_create (@w{task_t @var{task}}, @w{int @var{flags}}, @w{struct store **@var{store}})
+Return a new store in @var{store} referring to the task @var{task},
+consuming the @var{task} send right.
+@end deftypevar
+
+@subsubsection @code{zero} store
+@cindex @code{zero} store
+
+@deftypevar {extern const struct store_class} store_zero_class
+Reads to this store always return zero-filled buffers, no matter what
+has been written into it. This store corresponds to the Unix
+@file{/dev/zero} device node.
+@end deftypevar
+
+@deftypefun error_t store_zero_create (@w{store_offset_t @var{size}}, @w{int @var{flags}}, @w{struct store **@var{store}})
+Return a new zero store @var{size} bytes long in @var{store}.
+@end deftypefun
+
+@subsubsection @code{copy} store
+@cindex @code{copy} store
+
+@deftypevar {extern const struct store_class} store_copy_class
+This store provides a temporary copy of another store. This is useful
+if you want to provide writable data, but do not wish to modify the
+underlying store. All changes to a copy store are lost when it is
+closed.
+@end deftypevar
+
+@deftypefun error_t store_copy_open (@w{const char *@var{name}}, @w{int @var{flags}}, @w{const struct store_class *const *@var{classes}}, @w{struct store **@var{store}})
+Open the copy store @var{name} (which consists of another store class
+name, a @samp{:}, and a name for the store class to open) and return the
+corresponding store in @var{store}. @var{classes} is used to select
+classes specified by the type name; if it is zero,
+@var{store_std_classes} is used.
+@end deftypefun
+
+@deftypefun error_t store_copy_create (@w{struct store *@var{from}}, @w{int @var{flags}}, @w{struct store **@var{store}})
+Return a new store in @var{store} which contains a snapshot of the
+contents of the store @var{from}; @var{from} is consumed.
+@end deftypefun
+
+@deftypefun error_t store_buffer_create (@w{void *@var{buf}}, @w{size_t @var{buf_len}}, @w{int @var{flags}}, @w{struct store **@var{store}})
+Return a new store in @var{store} which contains the memory buffer
+@var{buf}, of length @var{buf_len}. @var{buf} must be allocated with
+@code{vm_allocate}, and will be consumed.
+@end deftypefun
+
+@subsubsection @code{gunzip} store
+@cindex @code{gunzip} store
+
+@deftypevar {extern const struct store_class} store_gunzip_class
+This store provides transparent GNU zip decompression of a substore.
+Unfortunately, this store is currently read-only.
+@end deftypevar
+
+@deftypevar error_t store_gunzip_open (@w{const char *@var{name}}, @w{int @var{flags}}, @w{const struct store_class *const *@var{classes}}, @w{struct store **@var{store}})
+Open the gunzip store @var{name} (which consists of another store class
+name, a @samp{:}, and a name for that store class to open), and return
+the corresponding store in @var{store}. @var{classes} is used to select
+classes specified by the type name; if it is zero,
+@var{store_std_classes} is used.
+@end deftypevar
+
+@deftypevar error_t store_gunzip_create (@w{struct store *@var{from}}, @w{int @var{flags}}, @w{struct store **@var{store}})
+Return a new store in @var{store} which contains a snapshot of the
+uncompressed contents of the store @var{from}; @var{from} is consumed.
+@var{block_size} is the desired block size of the result.
+@end deftypevar
+
+@subsubsection @code{concat} store
+@cindex @code{concat} store
+
+@cindex linear concatenation
+@cindex appending disks
+@cindex disks, appending
+@cindex disk concatenation
+@cindex concatenation, disk
+@deftypevar {extern const struct store_class} store_concat_class
+This class provides a linear concatenation storage mode. It creates a
+new virtual store which consists of several different substores appended
+to one another.
+
+This mode is designed to increase storage capacity, so that when one
+substore is filled, new data is transparently written to the next
+substore. Concatenation requires robust hardware, since a failure in
+any single substore will wipe out a large section of the data.
+@end deftypevar
+
+@deftypefun error_t store_concat_open (@w{const char *@var{name}}, @w{int @var{flags}}, @w{const struct store_class *const *@var{classes}}, @w{struct store **@var{store}})
+Return a new store that concatenates the stores created by opening all
+the individual stores described in @var{name}; for the syntax of
+@var{name}, see @code{store_open_children}.
+@end deftypefun
+
+@deftypefun error_t store_concat_create (@w{struct store * const *@var{stores}}, @w{size_t @var{num_stores}}, @w{int @var{flags}}, @w{struct store **@var{store}})
+Return a new store in @var{store} that concatenates all the stores in
+@var{stores} (@var{num_stores} of them). The stores in @var{stores} are
+consumed; that is, they will be freed when this store is freed. The
+@var{stores} @emph{array}, however, is copied, and so should be freed by
+the caller.
+@end deftypefun
+
+@subsubsection @code{ileave} store
+@cindex @code{ileave} store
+
+@cindex RAID-0
+@cindex striping, disk
+@cindex disk striping
+@cindex interleaving disks
+@cindex disks, interleaving
+@deftypevar {extern const struct store_class} store_ileave_class
+This class provides a RAID-0@footnote{``RAID'' stands for @dfn{Redundant Array of
+Independent Disks}: several disks used in
+parallel to achieve increased capacity, redundancy and/or
+performance.} storage mode (also called @dfn{disk striping}). It
+creates a new virtual store by interleaving the contents of several
+different substores.
+
+This RAID mode is designed to increase storage performance, since I/O
+will probably occur in parallel if the substores reside on different
+physical devices. Interleaving works best with evenly-yoked
+substores@dots{} if the stores are different sizes, some space will be
+not be used at the end of the larger stores; if the stores are different
+speeds, then I/O will have to wait for the slowest store; if some stores
+are not as reliable as others, failures will wipe out every @var{n}th
+storage block, where @var{n} is the number of substores.
+@end deftypevar
+
+@deftypefun error_t store_ileave_create (@w{struct store * const *@var{stripes}}, @w{size_t @w{num_stripes}}, @w{store_offset_t @var{interleave}}, @w{int @var{flags}}, @w{struct store **@var{store}})
+Return a new store in @var{store} that interleaves all the stores in
+@var{stripes} (@var{num_stripes} of them) every @var{interleave} bytes;
+@var{interleave} must be an integer multiple of each stripe's block
+size. The stores in @var{stripes} are consumed; that is, they will be
+freed when this store is freed. The @var{stripes} @emph{array},
+however, is copied, and so should be freed by the caller.
+@end deftypefun
+
+@subsubsection @code{mvol} store
+@cindex @code{mvol} store
+
+@deftypevar {extern const struct store_class} store_mvol_class
+This store provides access to multiple volumes using a single-volume
+device. One use of this store would be to provide a store which
+consists of multiple floppy disks when there is only a single disk
+drive. It works by remapping a single linear address range to multiple
+address ranges, and keeping track of the currently active range.
+Whenever a request maps to a range that is not active, a callback is
+made in order to switch to the new range.
+
+This class is not included in @var{store_std_classes}, because it
+requires an application-specific callback.
+@end deftypevar
+
+@deftypefun error_t store_mvol_create (@w{struct store *@var{phys}}, error_t (*@var{swap_vols}) (@w{struct store *@var{store}}, @w{size_t @var{new_vol}}, @w{ssize_t @var{old_vol}}), @w{int @var{flags}}, @w{struct store **@var{store}})
+Return a new store in @var{store} that multiplexes multiple physical
+volumes from @var{phys} as one larger virtual volume. @var{swap_vols}
+is a function that will be called whenever reads or writes refer to a
+block which is not addressable on the currently active volume.
+@var{phys} is consumed.
+@end deftypefun
+
+@subsubsection @code{remap} store
+@pindex @code{remap} store
+
+@deftypevar {extern const struct store_class} store_remap_class
+This store translates I/O requests into different addresses on a
+different store.
+@end deftypevar
+
+@deftypefun error_t store_remap_create (@w{struct store *@var{source}}, @w{const struct store_run *@var{runs}}, @w{size_t @var{num_runs}}, @w{int @var{flags}}, @w{struct store **@var{store}})
+Return a new store in @var{store} that reflects the blocks in @var{runs}
+and @var{runs_len} from @var{source}; @var{source} is consumed, but
+@var{runs} is not. Unlike the @code{store_remap} function, this
+function always operates by creating a new store of type @samp{remap}
+which has @var{source} as a child, and so may be less efficient than
+@code{store_remap} for some types of stores.
+@end deftypefun
+
+
+@node Store RPC Encoding
+@subsection Store RPC Encoding
+
+The store library also provides some functions which help transfer
+stores between tasks via RPC:
+
+@deftypevr {Structure} struct store_enc
+This structure is used to hold the various bits that make up the
+representation of a store for transmission via RPC. See
+@code{<hurd/hurd_types.h>} for an explanation of the encodings for the
+various storage types.
+@end deftypevr
+
+@deftypefun void store_enc_init (@w{struct store_enc *@var{enc}}, @w{mach_port_t *@var{ports}}, @w{mach_msg_type_number_t @var{num_ports}}, @w{int *@var{ints}}, @w{mach_msg_type_number_t @var{num_ints}}, @w{off_t *@var{offsets}}, @w{mach_msg_type_number_t @var{num_offsets}}, @w{char *@var{data}}, @w{mach_msg_type_number_t @var{data_len}})
+Initialize @var{enc}. The given vector and sizes will be used for the
+encoding if they are big enough (otherwise new ones will be
+automatically allocated).
+@end deftypefun
+
+@deftypefun void store_enc_dealloc (@w{struct store_enc *@var{enc}})
+Deallocate storage used by the fields in @var{enc} (but nothing is done
+with @var{enc} itself).
+@end deftypefun
+
+@deftypefun void store_enc_return (@w{struct store_enc *@var{enc}}, @w{mach_port_t **@var{ports}}, @w{mach_msg_type_number_t *@var{num_ports}}, @w{int **@var{ints}}, @w{mach_msg_type_number_t *@var{num_ints}}, @w{off_t **@var{offsets}}, @w{mach_msg_type_number_t *@var{num_offsets}}, @w{char **@var{data}}, @w{mach_msg_type_number_t *@var{data_len}})
+Copy out the parameters from @var{enc} into the given variables suitably
+for returning from a @code{file_get_storage_info} RPC, and deallocate
+@var{enc}.
+@end deftypefun
+
+@deftypefun error_t store_return (@w{const struct store *@var{store}}, @w{mach_port_t **@var{ports}}, @w{mach_msg_type_number_t *@var{num_ports}}, @w{int **@var{ints}}, @w{mach_msg_type_number_t *@var{num_ints}}, @w{off_t **@var{offsets}}, @w{mach_msg_type_number_t *@var{num_offsets}}, @w{char **@var{data}}, @w{mach_msg_type_number_t *@var{data_len}})
+Encode @var{store} into the given return variables, suitably for
+returning from a @code{file_get_storage_info} RPC.
+@end deftypefun
+
+@deftypefun error_t store_encode (@w{const struct store *@var{store}}, @w{struct store_enc *@var{enc}})
+Encode @var{store} into @var{enc}, which should have been prepared with
+@code{store_enc_init}, or return an error. The contents of @var{enc}
+may then be returned as the value of @code{file_get_storage_info}; if
+for some reason this can't be done, @code{store_enc_dealloc} may be used
+to deallocate the memory used by the unsent vectors.
+@end deftypefun
+
+@deftypefun error_t store_decode (@w{struct store_enc *@var{enc}}, @w{const struct store_class *const *@var{classes}}, @w{struct store **@var{store}})
+Decode @var{enc}, either returning a new store in @var{store}, or an
+error. @var{classes} is the mapping from Hurd storage class ids to store
+classes; if it is zero, @var{store_std_classes} is used. If nothing
+else is to be done with @var{enc}, its contents may then be freed using
+@code{store_enc_dealloc}.
+@end deftypefun
+
+@deftypefun error_t store_allocate_child_encodings (@w{const struct store *@var{store}}, @w{struct store_enc *@var{enc}})
+Calls the @code{allocate_encoding} method in each child store of
+@var{store}, propagating any errors. If any child does not have such a
+method, @code{EOPNOTSUPP} is returned.
+@end deftypefun
+
+@deftypefun error_t store_encode_children (@w{const struct store *@var{store}}, @w{struct store_enc *@var{enc}})
+Calls the encode method in each child store of @var{store}, propagating
+any errors. If any child does not have such a method, @code{EOPNOTSUPP}
+is returned.
+@end deftypefun
+
+@deftypefun error_t store_decode_children (@w{struct store_enc *@var{enc}}, @w{int @var{num_children}}, @w{const struct store_class *const *@var{classes}}, @w{struct store **@var{children}})
+Decodes @var{num_children} from @var{enc}, storing the results into
+successive positions in @var{children}.
+@end deftypefun
+
+@deftypefun error_t store_with_decoded_runs (@w{struct store_enc *@var{enc}}, @w{size_t @var{num_runs}}, error_t (*@var{fun}) (@w{const struct store_run *@var{runs}}, @w{size_t @var{num_runs}}))
+Call @var{fun} with the vector @var{runs} of length @var{num_runs}
+extracted from @var{enc}.
+@end deftypefun
+
+@deftypefun error_t store_std_leaf_allocate_encoding (@w{const struct store *@var{store}}, @w{struct store_enc *@var{enc}})
+@deftypefunx error_t store_std_leaf_encode (@w{const struct store *@var{store}}, @w{struct store_enc *@var{enc}})
+Standard encoding used for most data-providing (as opposed to filtering)
+store classes.
+@end deftypefun
+
+@deftypefn {Typedef} typedef error_t (*store_std_leaf_create_t) (@w{mach_port_t @var{port}}, @w{int @var{flags}}, @w{size_t @var{block_size}}, @w{const struct store_run *@var{runs}}, @w{size_t @var{num_runs}}, @w{struct store **@var{store}})
+Creation function used by @code{store_std_leaf_decode}.
+@end deftypefn
+
+@deftypefun error_t store_std_leaf_decode (@w{struct store_enc *@var{enc}}, @w{store_std_leaf_create_t @var{create}}, @w{struct store **@var{store}})
+Decodes the standard leaf encoding which is common to various builtin
+formats, and calls @var{create} to actually create the store.
+@end deftypefun
+
+
+@node Stored Filesystems
+@chapter Stored Filesystems
+@cindex disk-based filesystems
+@cindex filesystems, disk-based
+
+Stored filesystems allow users to save and load persistent data from any
+random-access storage media, such as hard disks, floppy diskettes, and
+CD-ROMs. Stored filesystems are required for bootstrapping standalone
+workstations, as well.
+
+@menu
+* Repairing Filesystems:: Recovering from minor filesystem crashes.
+* Linux Extended 2 FS:: The popular Linux filesystem format.
+* ISO-9660 CD-ROM FS:: Standard CD-ROM format.
+* Diskfs Library:: Implementing new filesystem servers.
+@end menu
+
+
+@node Repairing Filesystems
+@section Repairing Filesystems
+@pindex fsck
+
+FIXME: finish
+
+
+@node Linux Extended 2 FS
+@section Linux Extended 2 FS
+@pindex ext2fs
+
+FIXME: finish
+
+
+@node ISO-9660 CD-ROM FS
+@section ISO-9660 CD-ROM FS
+@pindex iso9660fs
+
+FIXME: finish
+
+
+@node Diskfs Library
+@section Diskfs Library
+@scindex libdiskfs
+@scindex diskfs.h
+
+The diskfs library is declared in @code{<hurd/diskfs.h>}, and does a lot
+of the work of implementing stored filesystems. @code{libdiskfs}
+requires the threads, ports, iohelp, fshelp, and store libraries. You
+should understand all these libraries before you attempt to use diskfs,
+and you should also be familiar with the pager library (@pxref{Pager
+Library}).
+
+@scindex libstorefs
+For historical reasons, the library for implementing stored filesystems
+is called @code{libdiskfs} instead of @code{libstorefs}. Keep in mind,
+however, that diskfs is useful for filesystems which are implemented on
+any block-addressed storage device, since it uses the store library to
+do I/O.
+
+Note that stored filesystems can be tricky to implement, since the
+diskfs callback interfaces are not trivial. It really is best if you
+examine the source code of a similar existing filesystem server, and
+follow its example rather than trying to write your own from scratch.
+
+@menu
+* Diskfs Startup:: Initializing stored filesystems.
+* Diskfs Arguments:: Parsing command-line arguments.
+* Diskfs Globals:: Global behaviour modification.
+* Diskfs Node Management:: Allocation, reference counting, I/O,
+ caching, and other disk node routines.
+* Diskfs Callbacks:: Mandatory user-defined diskfs functions.
+* Diskfs Options:: Optional user-defined diskfs functions.
+* Diskfs Internals:: Reimplementing small pieces of diskfs.
+@end menu
+
+
+@node Diskfs Startup
+@subsection Diskfs Startup
+
+This subsection gives an outline of the general steps involved in
+implementing a filesystem server, to help refresh your memory and to
+offer explanations rather than to serve as a tutorial.
+
+The first thing a filesystem server should do is parse its command-line
+arguments (@pxref{Diskfs Arguments}). Then, the standard output and
+error streams should be redirected to the console, so that error
+messages are not lost if this is the bootstrap filesystem:
+
+@deftypefun void diskfs_console_stdio (void)
+Redirect error messages to the console, so that they can be seen by
+users.
+@end deftypefun
+
+The following is a list of the relevant functions which would be called
+during the rest of the server initialization. Again, you should refer
+to the implementation of an already-working filesystem if you have any
+questions about how these functions should be used:
+
+@deftypefun error_t diskfs_init_diskfs (void)
+Call this function after arguments have been parsed to initialize the
+library. You must call this before calling any other diskfs functions,
+and after parsing diskfs options.
+@end deftypefun
+
+@deftypefun void diskfs_spawn_first_thread (void)
+Call this after all format-specific initialization is done (except for
+setting @code{diskfs_root_node}); at this point the pagers should be
+ready to go.
+@end deftypefun
+
+@deftypefun mach_port_t diskfs_startup_diskfs (@w{mach_port_t @var{bootstrap}}, @w{int @var{flags}})
+Call this once the filesystem is fully initialized, to advertise the new
+filesystem control port to our parent filesystem. If @var{bootstrap} is set,
+diskfs will call @code{fsys_startup} on that port as appropriate and return
+the @var{realnode} from that call; otherwise we call
+@code{diskfs_start_bootstrap} and return @code{MACH_PORT_NULL}.
+@var{flags} specifies how to open @var{realnode} (from the @code{O_*} set).
+@end deftypefun
+
+You should not need to call the following function directly, since
+@code{diskfs_startup_diskfs} will do it for you, when appropriate:
+
+@deftypefun void diskfs_start_bootstrap (void)
+Start the Hurd bootstrap sequence as if we were the bootstrap filesystem
+(that is, @code{diskfs_boot_flags} is nonzero). All filesystem
+initialization must be complete before you call this function.
+@end deftypefun
+
+
+@node Diskfs Arguments
+@subsection Diskfs Arguments
+
+The following functions implement standard diskfs command-line and
+runtime argument parsing, using argp (@pxref{Argp, , , libc, The GNU C
+Library Reference Manual}):
+
+@deftypefun error_t diskfs_set_options (@w{char *@var{argz}}, @w{size_t @var{argz_len}})
+Parse and execute the runtime options specified by @var{argz} and
+@var{argz_len}. @code{EINVAL} is returned if some option is
+unrecognized. The default definition of this routine will parse them
+using @code{diskfs_runtime_argp}.
+@end deftypefun
+
+@deftypefun error_t diskfs_append_args (@w{char **@var{argz}}, @w{unsigned *@var{argz_len}})
+Append to the malloced string @code{*@var{argz}} of length
+@code{*@var{argz_len}} a NUL-separated list of the arguments to this
+translator. The default definition of this routine simply calls
+@code{diskfs_append_std_options}.
+@end deftypefun
+
+@deftypefun error_t diskfs_append_std_options (@w{char **@var{argz}}, @w{unsigned *@var{argz_len}})
+@emph{Appends} NUL-separated options describing the standard diskfs
+option state to @var{argz} and increments @var{argz_len} appropriately.
+Note that unlike @code{diskfs_get_options}, @var{argz} and
+@var{argz_len} must already have sane values.
+@end deftypefun
+
+@deftypevar {struct argp *} diskfs_runtime_argp
+If this is defined or set to an argp structure, it will be used by the
+default @code{diskfs_set_options} to handle runtime option parsing. The
+default definition is initialized to a pointer to
+@code{diskfs_std_runtime_argp}.
+@end deftypevar
+
+@deftypevar {const struct argp} diskfs_std_runtime_argp
+An argp for the standard diskfs runtime options. The default definition
+of @code{diskfs_runtime_argp} points to this, although the user can
+redefine that to chain this onto his own argp.
+@end deftypevar
+
+@deftypevar {const struct argp} diskfs_startup_argp
+An argp structure for the standard diskfs command line arguments. The
+user may call @code{argp_parse} on this to parse the command line, chain
+it onto the end of his own argp structure, or ignore it completely.
+@end deftypevar
+
+@deftypevar {const struct argp} diskfs_store_startup_argp
+An argp structure for the standard diskfs command line arguments plus a
+store specification. The address of a location in which to return the
+resulting @code{struct store_parsed} structure should be passed as the
+input argument to @code{argp_parse}; FIXME xref the declaration for
+STORE_ARGP.
+@end deftypevar
+
+
+@node Diskfs Globals
+@subsection Diskfs Globals
+
+The following functions and variables control the overall behaviour of
+the library. Your callback functions may need to refer to these, but
+you should not need to modify or redefine them.
+
+@deftypevar mach_port_t diskfs_default_pager
+@deftypevarx mach_port_t diskfs_exec_ctl
+@deftypevarx mach_port_t diskfs_exec
+@deftypevarx auth_t diskfs_auth_server_port
+These are the respective send rights to the default pager, execserver
+control port, execserver itself, and authserver.
+@end deftypevar
+
+@deftypevar mach_port_t diskfs_fsys_identity
+The @code{io_identity} identity port for the filesystem.
+@end deftypevar
+
+@deftypevar {char **} diskfs_argv
+The command line with which diskfs was started, set by the default argument parser.
+If you don't use it, set this yourself. This is only used for bootstrap
+file systems, to give the procserver.
+@end deftypevar
+
+@deftypevar {char *} diskfs_boot_flags
+When this is a bootstrap filesystem, the command line options passed from
+the kernel. If not a bootstrap filesystem, it is zero, so it can be used to
+distinguish between the two cases.
+@end deftypevar
+
+@deftypevar {struct rwlock} diskfs_fsys_lock
+Hold this lock while doing filesystem-level operations. Innocuous users
+can just hold a reader lock, but operations that might corrupt other
+threads should hold a writer lock.
+@end deftypevar
+
+@deftypevar {volatile struct mapped_time_value *} diskfs_mtime
+The current system time, as used by the diskfs routines. This is
+converted into a @code{struct timeval} by the @code{maptime_read}
+C library function (FIXME xref).
+@end deftypevar
+
+@deftypevar int diskfs_synchronous
+True if and only if we should do every operation synchronously. It
+is the format-specific code's responsibility to keep allocation
+information permanently in sync if this is set; the rest will
+be done by format-independent code.
+@end deftypevar
+
+@deftypefun error_t diskfs_set_sync_interval (@w{int @var{interval}})
+Establish a thread to sync the filesystem every @var{interval} seconds,
+or never, if @var{interval} is zero. If an error occurs creating the
+thread, it is returned, otherwise zero. Subsequent calls will create a
+new thread and (eventually) get rid of the old one; the old thread won't
+do any more syncs, regardless.
+@end deftypefun
+
+@deftypevar spin_lock_t diskfs_node_refcnt_lock
+Pager reference count lock.
+@end deftypevar
+
+@deftypevar int diskfs_readonly
+Set to zero if the filesystem is currently writable.
+@end deftypevar
+
+@deftypefun error_t diskfs_set_readonly (@w{int @var{readonly}})
+Change an active filesystem between read-only and writable modes,
+setting the global variable @var{diskfs_readonly} to reflect the current
+mode. If an error is returned, nothing will have changed.
+@var{diskfs_fsys_lock} should be held while calling this routine.
+@end deftypefun
+
+@deftypefun int diskfs_check_readonly (void)
+Check if the filesystem is readonly before an operation that writes it.
+Return nonzero if readonly, otherwise zero.
+@end deftypefun
+
+@deftypefun error_t diskfs_remount (void)
+Reread all in-core data structures from disk. This function can only be
+successful if @var{diskfs_readonly} is true. @var{diskfs_fsys_lock}
+should be held while calling this routine.
+@end deftypefun
+
+@deftypefun error_t diskfs_shutdown (@w{int @var{flags}})
+Shutdown the filesystem; @var{flags} are as for @code{fsys_shutdown}.
+@end deftypefun
+
+
+@node Diskfs Node Management
+@subsection Diskfs Node Management
+
+Every file or directory is a diskfs @dfn{node}. The following functions
+help your diskfs callbacks manage nodes and their references:
+
+@deftypefun void diskfs_drop_node (@w{struct node *@var{np}})
+Node @var{np} now has no more references; clean all state. The
+@var{diskfs_node_refcnt_lock} must be held, and will be released upon
+return. @var{np} must be locked.
+@end deftypefun
+
+@deftypefun void diskfs_node_update (@w{struct node *@var{np}}, @w{int @var{wait}})
+Set disk fields from @code{@var{np}->dn_stat}; update ctime, atime, and mtime
+if necessary. If @var{wait} is true, then return only after the
+physical media has been completely updated.
+@end deftypefun
+
+@deftypefun void diskfs_nref (@w{struct node *@var{np}})
+Add a hard reference to node @var{np}. If there were no hard references
+previously, then the node cannot be locked (because you must hold a hard
+reference to hold the lock).
+@end deftypefun
+
+@deftypefun void diskfs_nput (@w{struct node *@var{np}})
+Unlock node @var{np} and release a hard reference; if this is the last
+hard reference and there are no links to the file then request light
+references to be dropped.
+@end deftypefun
+
+@deftypefun void diskfs_nrele (@w{struct node *@var{np}})
+Release a hard reference on @var{np}. If @var{np} is locked by anyone,
+then this cannot be the last hard reference (because you must hold a
+hard reference in order to hold the lock). If this is the last hard
+reference and there are no links, then request light references to be
+dropped.
+@end deftypefun
+
+@deftypefun void diskfs_nref_light (@w{struct node *@var{np}})
+Add a light reference to a node.
+@end deftypefun
+
+@deftypefun void diskfs_nput_light (@w{struct node *@var{np}})
+Unlock node @var{np} and release a light reference.
+@end deftypefun
+
+@deftypefun void diskfs_nrele_light (@w{struct node *@var{np}})
+Release a light reference on @var{np}. If @var{np} is locked by anyone,
+then this cannot be the last reference (because you must hold a hard
+reference in order to hold the lock).
+@end deftypefun
+
+@deftypefun error_t diskfs_node_rdwr (@w{struct node *@var{np}}, @w{char *@var{data}}, @w{off_t @var{off}}, @w{size_t @var{amt}}, @w{int @var{direction}}, @w{struct protid *@var{cred}}, @w{size_t *@var{amtread}})
+This is called by other filesystem routines to read or write files, and
+extends them automatically, if necessary. @var{np} is the node to be
+read or written, and must be locked. @var{data} will be written or
+filled. @var{off} identifies where in the file the I/O is to take place
+(negative values are not allowed). @var{amt} is the size of @var{data}
+and tells how much to copy. @var{dir} is zero for reading or nonzero
+for writing. @var{cred} is the user doing the access (only used to
+validate attempted file extension). For reads, @code{*@var{amtread}} is
+filled with the amount actually read.
+@end deftypefun
+
+@deftypefun void diskfs_notice_dirchange (@w{struct node *@var{dp}}, @w{enum dir_changed_type @var{type}}, @w{char *@var{name}})
+Send notifications to users who have requested them for directory
+@var{dp} with @code{dir_notice_changes}. The type of modification and
+affected name are @var{type} and @var{name} respectively. This should
+be called by @code{diskfs_direnter}, @code{diskfs_dirremove},
+@code{diskfs_dirrewrite}, and anything else that changes the directory,
+after the change is fully completed.
+@end deftypefun
+
+@deftypefun {struct node *} diskfs_make_node (@w{struct disknode *@var{dn}})
+Create a new node structure with @var{ds} as its physical disknode. The
+new node will have one hard reference and no light references.
+@end deftypefun
+
+@c FIXME: It's odd that `hard' and `light' seem to be opposites when
+@c we're talking about references. Or is `weak' the opposite of `hard'?
+@c These terms need to be explained.
+@c tb: hard is opposite to both light and weak, but we don't use both
+@c tb: light and weak in the same context, so it's ok.
+
+These next node manipulation functions are not generally useful, but may
+come in handy if you need to redefine any diskfs functions.
+
+@deftypefun error_t diskfs_create_node (@w{struct node *@var{dir}}, @w{char *@var{name}}, @w{mode_t @var{mode}}, @w{struct node **@var{newnode}}, @w{struct protid *@var{cred}}, @w{struct dirstat *@var{ds}})
+Create a new node. Give it @var{mode}: if @var{mode} includes
+@code{IFDIR}, also initialize @file{.} and @file{..} in the new
+directory. Return the node in @var{npp}. @var{cred} identifies the
+user responsible for the call. If @var{name} is nonzero, then link the
+new node into @var{dir} with name @var{name}; @var{ds} is the result of
+a prior @code{diskfs_lookup} for creation (and @var{dir} has been held
+locked since). @var{dir} must always be provided as at least a hint for
+disk allocation strategies.
+@end deftypefun
+
+@deftypefun void diskfs_set_node_atime (@w{struct node *@var{np}})
+If disk is not readonly and the noatime option is not enabled, set
+@code{@var{np}->dn_set_atime}.
+@end deftypefun
+
+@deftypefun void diskfs_set_node_times (@w{struct node *@var{np}})
+If @code{@var{np}->dn_set_ctime} is set, then modify
+@code{@var{np}->dn_stat.st_ctim} appropriately; do the analogous
+operations for @code{st_atim} and @code{st_mtim} as well.
+@end deftypefun
+
+@deftypefun {struct node *} diskfs_check_lookup_cache (@w{struct node *@var{dir}}, @w{char *@var{name}})
+Scan the cache looking for @var{name} inside @var{dir}. If we don't
+know any entries at all, then return zero. If the entry is confirmed to
+not exist, then return -1. Otherwise, return @var{np} for the entry,
+with a newly-allocated reference.
+@end deftypefun
+
+@deftypefun error_t diskfs_cached_lookup (@w{int @var{cache_id}}, @w{struct node **@var{npp}})
+Return the node corresponding to @var{cache_id} in @code{*@var{npp}}.
+@end deftypefun
+
+@deftypefun void diskfs_enter_lookup_cache (@w{struct node *@var{dir}}, @w{struct node *@var{np}}, @w{char *@var{name}})
+Node @var{np} has just been found in @var{dir} with @var{name}. If
+@var{np} is null, that means that this name has been confirmed as absent
+in the directory.
+@end deftypefun
+
+@deftypefun void diskfs_purge_lookup_cache (@w{struct node *@var{dp}}, @w{struct node *@var{np}})
+Purge all references in the cache to @var{np} as a node inside directory
+@var{dp}.
+@end deftypefun
+
+
+@node Diskfs Callbacks
+@subsection Diskfs Callbacks
+
+Like several other Hurd libraries, @code{libdiskfs} depends on you to
+implement application-specific callback functions. You @emph{must}
+define the following functions and variables, but you should also look
+at @ref{Diskfs Options}, as there are several defaults which should be
+modified to provide good filesystem support:
+
+@deftypevr {Structure} struct dirstat
+You must define this type, which will hold information between a call to
+@code{diskfs_lookup} and a call to one of @code{diskfs_direnter},
+@code{diskfs_dirremove}, or @code{diskfs_dirrewrite}. It must contain
+enough information so that those calls work as described below.
+@end deftypevr
+
+@deftypevar const size_t diskfs_dirstat_size
+This must be the size in bytes of a @code{struct dirstat}.
+@end deftypevar
+
+@deftypevar int diskfs_link_max
+This is the maximum number of links to any one file, which must be a
+positive integer. The implementation of @code{dir_rename} does not know
+how to succeed if this is only one allowed link; on such formats you
+need to reimplement @code{dir_rename} yourself.
+@end deftypevar
+
+@deftypevar int diskfs_maxsymlinks
+This variable is a positive integer which is the maximum number of
+symbolic links which can be traversed within a single call to
+@code{dir_lookup}. If this is exceeded, @code{dir_lookup} will
+return @code{ELOOP}.
+@end deftypevar
+
+@deftypevar {struct node *} diskfs_root_node
+Set this to be the node of the root of the filesystem.
+@end deftypevar
+
+@deftypevar {char *} diskfs_server_name
+Set this to the name of the filesystem server.
+@end deftypevar
+
+@deftypevar {char *} diskfs_server_version
+Set this to be the server version string.
+@end deftypevar
+
+@deftypevar {char *} diskfs_disk_name
+This should be a string that somehow identifies the particular disk this
+filesystem is interpreting. It is generally only used to print messages
+or to distinguish instances of the same filesystem type from one
+another. If this filesystem accesses no external media, then define
+this to be zero.
+@end deftypevar
+
+@deftypefun error_t diskfs_set_statfs (@w{fsys_statfsbuf_t *@var{statfsbuf}})
+Set @code{*@var{statfsbuf}} with appropriate values to reflect the
+current state of the filesystem.
+@end deftypefun
+
+@deftypefun error_t diskfs_lookup (@w{struct node *@var{dp}}, @w{const char *@var{name}}, @w{enum lookup_type @var{type}}, @w{struct node **@var{np}}, @w{struct dirstat *@var{ds}}, @w{struct protid *@var{cred}})
+@deftypefunx error_t diskfs_lookup_hard (@w{struct node *@var{dp}}, @w{const char *@var{name}}, @w{enum lookup_type @var{type}}, @w{struct node **@var{np}}, @w{struct dirstat *@var{ds}}, @w{struct protid *@var{cred}})
+You should not define @code{diskfs_lookup}, because it is simply a
+wrapper for @code{diskfs_lookup_hard}, and is already defined in
+@code{libdiskfs}.
+
+Lookup in directory @var{dp} (which is locked) the name @var{name}.
+@var{type} will either be @code{LOOKUP}, @code{CREATE}, @code{RENAME},
+or @code{REMOVE}. @var{cred} identifies the user making the call.
+
+If the name is found, return zero, and (if @var{np} is nonzero) set
+@code{*@var{np}} to point to the node for it, which should be locked.
+If the name is not found, return @code{ENOENT}, and (if @var{np} is
+nonzero) set @code{*@var{np}} to zero. If @var{np} is zero, then the
+node found must not be locked, not even transitorily. Lookups for
+@code{REMOVE} and @code{RENAME} (which must often check permissions on
+the node being found) will always set @var{np}.
+
+If @var{ds} is nonzero then the behaviour varies depending on the
+requested lookup @var{type}:
+
+@table @code
+@item LOOKUP
+Set @code{*@var{ds}} to be ignored by @code{diskfs_drop_dirstat}
+
+@item CREATE
+On success, set @code{*@var{ds}} to be ignored by
+@code{diskfs_drop_dirstat}. @*
+On failure, set @code{*@var{ds}} for a future call to
+@code{diskfs_direnter}.
+
+@item RENAME
+On success, set @code{*@var{ds}} for a future call to
+@code{diskfs_dirrewrite}. @*
+On failure, set @code{*@var{ds}} for a future call to
+@code{diskfs_direnter}.
+
+@item REMOVE
+On success, set @code{*@var{ds}} for a future call to
+@code{diskfs_dirremove}. @*
+On failure, set @code{*@var{ds}} to be ignored by
+@code{diskfs_drop_dirstat}.
+@end table
+
+The caller of this function guarantees that if @var{ds} is nonzero, then
+either the appropriate call listed above or @code{diskfs_drop_dirstat}
+will be called with @var{ds} before the directory @var{dp} is unlocked,
+and guarantees that no lookup calls will be made on this directory
+between this lookup and the use (or destruction) of *DS.
+
+If you use the library's versions of @code{diskfs_rename_dir},
+@code{diskfs_clear_directory}, and @code{diskfs_init_dir}, then lookups
+for @file{..} might have the flag @code{SPEC_DOTDOT} ORed in. This has a
+special meaning depending on the requested lookup @var{type}:
+
+@table @code
+@item LOOKUP
+@var{dp} should be unlocked and its reference dropped before returning.
+
+@item CREATE
+Ignore this case, because @code{SPEC_DOTDOT} is guaranteed not to be
+given.
+
+@item RENAME
+@itemx REMOVE
+In both of these cases, the node being found (@code{*@var{np}}) is
+already held locked, so don't lock it or add a reference to it.
+@end table
+
+Return @code{ENOENT} if @var{name} isn't in the directory. Return
+@code{EAGAIN} if @var{name} refers to the @file{..} of this filesystem's
+root. Return @code{EIO} if appropriate.
+@end deftypefun
+
+@deftypefun error_t diskfs_direnter (@w{struct node *@var{dp}}, @w{char *@var{name}}, @w{struct node *@var{np}}, @w{struct dirstat *@var{ds}}, @w{struct protid *@var{cred}})
+@deftypefunx error_t diskfs_direnter_hard (@w{struct node *@var{dp}}, @w{char *@var{name}}, @w{struct node *@var{np}}, @w{struct dirstat *@var{ds}}, @w{struct protid *@var{cred}})
+You should not define @code{diskfs_direnter}, because it is simply a
+wrapper for @code{diskfs_direnter_hard}, and is already defined in
+@code{libdiskfs}.
+
+Add @var{np} to directory @var{dp} under the name @var{name}. This will
+only be called after an unsuccessful call to @code{diskfs_lookup} of type
+@code{CREATE} or @code{RENAME}; @var{dp} has been locked continuously
+since that call and @var{ds} is as that call set it, @var{np} is locked.
+@var{cred} identifies the user responsible for the call (to be used only
+to validate directory growth).
+@end deftypefun
+
+@deftypefun error_t diskfs_dirrewrite (@w{struct node *@var{dp}}, @w{struct node *@var{oldnp}}, @w{struct node *@var{np}}, @w{char *@var{name}}, @w{struct dirstat *@var{ds}})
+@deftypefunx error_t diskfs_dirrewrite_hard (@w{struct node *@var{dp}}, @w{struct node *@var{np}}, @w{struct dirstat *@var{ds}})
+You should not define @code{diskfs_dirrewrite}, because it is simply a
+wrapper for @code{diskfs_dirrewrite_hard}, and is already defined in
+@code{libdiskfs}.
+
+This will only be called after a successful call to @code{diskfs_lookup}
+of type @code{RENAME}; this call should change the name found in
+directory @var{dp} to point to node @var{np} instead of its previous
+referent. @var{dp} has been locked continuously since the call to
+@code{diskfs_lookup} and @var{ds} is as that call set it; @var{np} is
+locked.
+
+@code{diskfs_dirrewrite} has some additional specifications: @var{name}
+is the name within @var{dp} which used to correspond to the previous
+referent, @var{oldnp}; it is this reference which is being rewritten.
+@code{diskfs_dirrewrite} also calls @code{diskfs_notice_dirchange} if
+@code{@var{dp}->dirmod_reqs} is nonzero.
+@end deftypefun
+
+@deftypefun error_t diskfs_dirremove (@w{struct node *@var{dp}}, @w{struct node *@var{np}}, @w{char *@var{name}}, @w{struct dirstat *@var{ds}})
+@deftypefunx error_t diskfs_dirremove_hard (@w{struct node *@var{dp}}, @w{struct dirstat *@var{ds}})
+You should not define @code{diskfs_dirremove}, because it is simply a
+wrapper for @code{diskfs_dirremove_hard}, and is already defined in
+@code{libdiskfs}.
+
+This will only be called after a successful call to @code{diskfs_lookup}
+of type @code{REMOVE}; this call should remove the name found from the
+directory @var{ds}. @var{dp} has been locked continuously since the
+call to @code{diskfs_lookup} and @var{ds} is as that call set it.
+
+@code{diskfs_dirremove} has some additional specifications: this routine
+should call @code{diskfs_notice_dirchange} if
+@code{@var{dp}->dirmod_reqs} is nonzero. The entry being removed has
+name @var{name} and refers to @var{np}.
+@end deftypefun
+
+@deftypefun error_t diskfs_drop_dirstat (@w{struct node *@var{dp}}, @w{struct dirstat *@var{ds}})
+@var{ds} has been set by a previous call to @code{diskfs_lookup} on
+directory @var{dp}; this function is guaranteed to be called if
+@code{diskfs_direnter}, @code{diskfs_dirrewrite}, and
+@code{diskfs_dirremove} have not been called, and should free any state
+retained by a @code{struct dirstat}. @var{dp} has been locked
+continuously since the call to @code{diskfs_lookup}.
+@end deftypefun
+
+@deftypefun void diskfs_null_dirstat (@w{struct dirstat *@var{ds}})
+Initialize @var{ds} such that @code{diskfs_drop_dirstat} will ignore it.
+@end deftypefun
+
+@deftypefun error_t diskfs_get_directs (@w{struct node *@var{dp}}, @w{int @var{entry}}, @w{int @var{n}}, @w{char **@var{data}}, @w{u_int *@var{datacnt}}, @w{vm_size_t @var{bufsiz}}, @w{int *@var{amt}})
+Return @var{n} directory entries starting at @var{entry} from locked
+directory node @var{dp}. Fill @code{*@var{data}} with the entries;
+which currently points to @code{*@var{datacnt}} bytes. If it isn't big
+enough, @code{vm_allocate} into @code{*@var{data}}. Set
+@code{*@var{datacnt}} with the total size used. Fill @var{amt} with the
+number of entries copied. Regardless, never copy more than @var{bufsiz}
+bytes. If @var{bufsiz} is zero, then there is no limit on
+@code{*@var{datacnt}}; if @var{n} is -1, then there is no limit on
+@var{amt}.
+@end deftypefun
+
+@deftypefun int diskfs_dirempty (@w{struct node *@var{dp}}, @w{struct protid *@var{cred}})
+Return nonzero if locked directory @var{dp} is empty. If the user has
+not redefined @code{diskfs_clear_directory} and
+@code{diskfs_init_directory}, then `empty' means `only possesses entries
+labelled @file{.} and @file{..}. @var{cred} identifies the user making
+the call@dots{} if this user cannot search the directory, then this
+routine should fail.
+@end deftypefun
+
+@deftypefun error_t diskfs_get_translator (@w{struct node *@var{np}}, @w{char **@var{namep}}, @w{u_int *@var{namelen}})
+For locked node @var{np} (for which @code{diskfs_node_translated} is
+true) look up the name of its translator. Store the name into newly
+malloced storage and set @code{*@var{namelen}} to the total length.
+@end deftypefun
+
+@deftypefun error_t diskfs_set_translator (@w{struct node *@var{np}}, @w{char *@var{name}}, @w{u_int @var{namelen}}, @w{struct protid *@var{cred}})
+For locked node @var{np}, set the name of the translating program to be
+@var{name}, which is @var{namelen} bytes long. @var{cred} identifies
+the user responsible for the call.
+@end deftypefun
+
+@deftypefun error_t diskfs_truncate (@w{struct node *@var{np}}, @w{off_t @var{size}})
+Truncate locked node @var{np} to be @var{size} bytes long. If @var{np}
+is already less than or equal to @var{size} bytes long, do nothing. If
+this is a symlink (and @code{diskfs_shortcut_symlink} is set) then this
+should clear the symlink, even if @code{diskfs_create_symlink_hook}
+stores the link target elsewhere.
+@end deftypefun
+
+@deftypefun error_t diskfs_grow (@w{struct node *@var{np}}, @w{off_t @var{size}}, @w{struct protid *@var{cred}})
+Grow the disk allocated to locked node @var{np} to be at least
+@var{size} bytes, and set @code{@var{np}->allocsize} to the actual
+allocated size. If the allocated size is already @var{size} bytes, do
+nothing. @var{cred} identifies the user responsible for the call.
+@end deftypefun
+
+@deftypefun error_t diskfs_node_reload (@w{struct node *@var{node}})
+This function must reread all data specific to @var{node} from disk,
+without writing anything. It is always called with
+@var{diskfs_readonly} set to true.
+@end deftypefun
+
+@deftypefun error_t diskfs_reload_global_state (void)
+This function must invalidate all cached global state, and reread it as
+necessary from disk, without writing anything. It is always called with
+@var{diskfs_readonly} set to true. @code{diskfs_node_reload} is
+subsequently called on all active nodes, so this call doesn't need to
+reread any node-specific data.
+@end deftypefun
+
+@deftypefun error_t diskfs_node_iterate (error_t (*@var{fun}) (@w{struct node *@var{np}}))
+For each active node @var{np}, call @var{fun}. The node is to be locked
+around the call to @var{fun}. If @var{fun} returns nonzero for any
+node, then stop immediately, and return that value.
+@end deftypefun
+
+@deftypefun error_t diskfs_alloc_node (@w{struct node *@var{dp}}, @w{mode_t @var{mode}}, @w{struct node **@var{np}})
+Allocate a new node to be of mode @var{mode} in locked directory
+@var{dp}, but don't actually set the mode or modify the directory, since
+that will be done by the caller. The user responsible for the request
+can be identified with @var{cred}. Set @code{*@var{np}} to be the newly
+allocated node.
+@end deftypefun
+
+@deftypefun void diskfs_free_node (@w{struct node *@var{np}}, @w{mode_t @var{mode}})
+Free node @var{np}; the on-disk copy has already been synchronized with
+@code{diskfs_node_update} (where @code{@var{np}->dn_stat.st_mode} was
+zero). @var{np}'s mode used to be @var{mode}.
+@end deftypefun
+
+@deftypefun void diskfs_lost_hardrefs (@w{struct node *@var{np}})
+Locked node @var{np} has some light references but has just lost its
+last hard reference.
+@end deftypefun
+
+@deftypefun void diskfs_new_hardrefs (@w{struct node *@var{np}})
+Locked node @var{np} has just acquired a hard reference where it had
+none previously. Therefore, it is okay again to have light references
+without real users.
+@end deftypefun
+
+@deftypefun void diskfs_try_dropping_softrefs (@w{struct node *@var{np}})
+Node @var{np} has some light references, but has just lost its last hard
+references. Take steps so that if any light references can be freed,
+they are. Both @var{diskfs_node_refcnt_lock} and @var{np} are locked.
+This function will be called after @code{diskfs_lost_hardrefs}.
+@end deftypefun
+
+@deftypefun void diskfs_node_norefs (@w{struct node *@var{np}})
+Node @var{np} has no more references; free local state, including
+@code{*@var{np}} if it shouldn't be retained.
+@var{diskfs_node_refcnt_lock} is held.
+@end deftypefun
+
+@deftypefun error_t diskfs_set_hypermetadata (@w{int @var{wait}}, @w{int @var{clean}})
+Write any non-paged metadata from format-specific buffers to disk,
+asynchronously unless @var{wait} is nonzero. If @var{clean} is nonzero,
+then after this is written the filesystem will be absolutely clean, and
+it must be possible for the non-paged metadata to indicate that fact.
+@end deftypefun
+
+@deftypefun void diskfs_write_disknode (@w{struct node *@var{np}}, @w{int @var{wait}})
+Write the information in @code{@var{np}->dn_stat} and any associated
+format-specific information to the disk. If @var{wait} is true, then
+return only after the physical media has been completely updated.
+@end deftypefun
+
+@deftypefun void diskfs_file_update (@w{struct node *@var{np}}, @w{int @var{wait}})
+Write the contents and all associated metadata of file NP to disk.
+Generally, this will involve calling @code{diskfs_node_update} for much
+of the metadata. If @var{wait} is true, then return only after the
+physical media has been completely updated.
+@end deftypefun
+
+@deftypefun mach_port_t diskfs_get_filemap (@w{struct node *@var{np}}, @w{vm_prot_t @var{prot}})
+Return a memory object port (send right) for the file contents of
+@var{np}. @var{prot} is the maximum allowable access. On errors,
+return @code{MACH_PORT_NULL} and set @code{errno}.
+@end deftypefun
+
+@deftypefun {struct pager *} diskfs_get_filemap_pager_struct (@w{struct node *@var{np}})
+Return a @code{struct pager *} that refers to the pager returned by
+diskfs_get_filemap for locked node NP, suitable for use as an argument
+to @code{pager_memcpy}.
+@end deftypefun
+
+@deftypefun vm_prot_t diskfs_max_user_pager_prot (void)
+Return the bitwise OR of the maximum @code{prot} parameter (the second
+argument to @code{diskfs_get_filemap}) for all active user pagers.
+@end deftypefun
+
+@deftypefun int diskfs_pager_users (void)
+Return nonzero if there are pager ports exported that might be in use by
+users. Further pager creation should be blocked before this function
+returns zero.
+@end deftypefun
+
+@deftypefun void diskfs_sync_everything (@w{int @var{wait}})
+Sync all the pagers and write any data belonging on disk except for the
+hypermetadata. If @var{wait} is true, then return only after the
+physical media has been completely updated.
+@end deftypefun
+
+@deftypefun void diskfs_shutdown_pager (void)
+Shut down all pagers. This is irreversible, and is done when the
+filesystem is exiting.
+@end deftypefun
+
+
+@node Diskfs Options
+@subsection Diskfs Options
+
+The functions and variables described in this subsection already have
+default definitions in @code{libdiskfs}, so you are not forced to define
+them; rather, they may be redefined on a case-by-case basis.
+
+You should set the values of any option variables as soon as your program
+starts (before you make any calls to diskfs, such as argument parsing).
+
+@deftypevar int diskfs_hard_readonly
+You should set this variable to nonzero if the filesystem media can
+never be made writable.
+@end deftypevar
+
+@deftypevar {char *} diskfs_extra_version
+Set this to be any additional version specification that should be
+printed for --version.
+@end deftypevar
+
+@deftypevar int diskfs_shortcut_symlink
+This should be nonzero if and only if the filesystem format supports
+shortcutting symbolic link translation. The library guarantees that
+users will not be able to read or write the contents of the node
+directly, and the library will only do so if the symlink hook functions
+(@code{diskfs_create_symlink_hook} and @code{diskfs_read_symlink_hook})
+return @code{EINVAL} or are not defined. The library knows that the
+@code{dn_stat.st_size} field is the length of the symlink, even if the
+hook functions are used.
+@end deftypevar
+
+@deftypevar int diskfs_shortcut_chrdev
+@deftypevarx int diskfs_shortcut_blkdev
+@deftypevarx int diskfs_shortcut_fifo
+@deftypevarx int diskfs_shortcut_ifsock
+These variables should be nonzero if and only if the filesystem format
+supports shortcutting character device node, block device node, FIFO, or
+Unix-domain socket translation, respectively.
+@end deftypevar
+
+@deftypevar int diskfs_default_sync_interval
+@code{diskfs_set_sync_interval} is called with this value when the first
+diskfs thread is started up (in @code{diskfs_spawn_first_thread}). This
+variable has a default default value of 30, which causes disk buffers to
+be flushed at least every 30 seconds.
+@end deftypevar
+
+@deftypefun error_t diskfs_validate_mode_change (@w{struct node *@var{np}}, @w{mode_t @var{mode}})
+@deftypefunx error_t diskfs_validate_owner_change (@w{struct node *@var{np}}, @w{uid_t @var{uid}})
+@deftypefunx error_t diskfs_validate_group_change (@w{struct node *@var{np}}, @w{gid_t @var{gid}})
+@deftypefunx error_t diskfs_validate_author_change (@w{struct node *@var{np}}, @w{uid_t @var{author}})
+@deftypefunx error_t diskfs_validate_flags_change (@w{struct node *@var{np}}, @w{int @var{flags}})
+@deftypefunx error_t diskfs_validate_rdev_change (@w{struct node *@var{np}}, @w{dev_t @var{rdev}})
+Return zero if for the node @var{np} can be changed as requested. That
+is, if @var{np}'s mode can be changed to @var{mode}, owner to @var{uid},
+group to @var{gid}, author to @var{author}, flags to @var{flags}, or raw
+device number to @var{rdev}, respectively. Otherwise, return an error
+code.
+
+It must always be possible to clear the mode or the flags; diskfs will
+not ask for permission before doing so.
+@end deftypefun
+
+@deftypefun void diskfs_readonly_changed (@w{int @var{readonly}})
+This is called when the disk has been changed from read-only to
+read-write mode or vice-versa. @var{readonly} is the new state (which
+is also reflected in @var{diskfs_readonly}). This function is also
+called during initial startup if the filesystem is to be writable.
+@end deftypefun
+
+@deftypefn {Variable} error_t (*diskfs_create_symlink_hook) (@w{struct node *@var{np}}, @w{char *@var{target}})
+If this function pointer is nonzero (and @code{diskfs_shortcut_symlink}
+is set) it is called to set a symlink. If it returns @code{EINVAL} or
+isn't set, then the normal method (writing the contents into the file
+data) is used. If it returns any other error, it is returned to the
+user.
+@end deftypefn
+
+@deftypefn {Variable} error_t (*diskfs_read_symlink_hook) (@w{struct node *@var{np}}, @w{char *@var{target}})
+If this function pointer is nonzero (and @code{diskfs_shortcut_symlink}
+is set) it is called to read the contents of a symlink. If it returns
+@code{EINVAL} or isn't set, then the normal method (reading from the
+file data) is used. If it returns any other error, it is returned to
+the user.
+@end deftypefn
+
+@deftypefun error_t diskfs_rename_dir (@w{struct node *@var{fdp}}, @w{struct node *@var{fnp}}, @w{char *@var{fromname}}, @w{struct node *@var{tdp}}, @w{char *@var{toname}}, @w{struct protid *@var{fromcred}}, @w{struct protid *@var{tocred}})
+Rename directory node @var{fnp} (whose parent is @var{fdp}, and which
+has name @var{fromname} in that directory) to have name @var{toname}
+inside directory @var{tdp}. None of these nodes are locked, and none
+should be locked upon return. This routine is serialized, so it doesn't
+have to be reentrant. Directories will never be renamed except by this
+routine. @var{fromcred} is the user responsible for @var{fdp} and
+@var{fnp}. @var{tocred} is the user responsible for @var{tdp}. This
+routine assumes the usual convention where @file{.} and @file{..} are
+represented by ordinary links; if that is not true for your format, you
+have to redefine this function.
+@end deftypefun
+
+@deftypefun error_t diskfs_clear_directory (@w{struct node *@var{dp}}, @w{struct node *@var{pdp}}, @w{struct protid *@var{cred}})
+Clear the @file{.} and @file{..} entries from directory @var{dp}. Its
+parent is @var{pdp}, and the user responsible for this is identified by
+@var{cred}. Both directories must be locked. This routine assumes the
+usual convention where @file{.} and @file{..} are represented by
+ordinary links; if that is not true for your format, you have to
+redefine this function.
+@end deftypefun
+
+@deftypefun error_t diskfs_init_dir (@w{struct node *@var{dp}}, @w{struct node *@var{pdp}}, @w{struct protid *@var{cred}})
+Locked node @var{dp} is a new directory; add whatever links are
+necessary to give it structure; its parent is the (locked) node
+@var{pdp}. This routine may not call @code{diskfs_lookup} on @var{pdp}.
+The new directory must be clear within the meaning of
+@code{diskfs_dirempty}. This routine assumes the usual convention where
+@file{.} and @file{..} are represented by ordinary links; if that is not
+true for your format, you have to redefine this function. @var{cred}
+identifies the user making the call.
+@end deftypefun
+
+
+@node Diskfs Internals
+@subsection Diskfs Internals
+
+The library also exports the following functions, but they are not
+generally useful unless you are redefining other functions the library
+provides.
+
+@deftypefun error_t diskfs_create_protid (@w{struct peropen *@var{po}}, @w{struct iouser *@var{user}}, @w{struct protid **@var{cred}})
+Create and return a protid for an existing peropen @var{po} in
+@var{cred}, referring to user @var{user}. The node @code{@var{po}->np}
+must be locked.
+@end deftypefun
+
+@deftypefun error_t diskfs_start_protid (@w{struct peropen *@var{po}}, @w{struct protid **@var{cred}})
+Build and return in @var{cred} a protid which has no user
+identification, for peropen @var{po}. The node @code{@var{po}->np} must
+be locked.
+@end deftypefun
+
+@deftypefun void diskfs_finish_protid (@w{struct protid *@var{cred}}, @w{struct iouser *@var{user}})
+Finish building protid @var{cred} started with @code{diskfs_start_protid};
+the user to install is @var{user}.
+@end deftypefun
+
+@deftypefun void diskfs_protid_rele (@w{void *@var{arg}})
+Called when a protid @var{cred} has no more references. Because
+references to protids are maintained by the port management library,
+this is installed in the clean routines list. The ports library will
+free the structure.
+@end deftypefun
+
+@deftypefun {struct peropen *} diskfs_make_peropen (@w{struct node *@var{np}}, @w{int @var{flags}}, @w{struct peropen *@var{context}})
+Create and return a new peropen structure on node @var{np} with open
+flags @var{flags}. The initial values for the @code{root_parent},
+@code{shadow_root}, and @code{shadow_root_parent} fields are copied from
+@var{context} if it is nonzero, otherwise each of these values are
+set to zero.
+@end deftypefun
+
+@deftypefun void diskfs_release_peropen (@w{struct peropen *@var{po}})
+Decrement the reference count on @var{po}.
+@end deftypefun
+
+@deftypefun error_t diskfs_execboot_fsys_startup (@w{mach_port_t @var{port}}, @w{int @var{flags}}, @w{mach_port_t @var{ctl}}, @w{mach_port_t *@var{real}}, @w{mach_msg_type_name_t *@var{realpoly}})
+This function is called by @code{S_fsys_startup} for execserver
+bootstrap. The execserver is able to function without a real node,
+hence this fraud. Arguments are as for @code{fsys_startup} in
+@code{<hurd/fsys.defs>}.
+@end deftypefun
+
+@deftypefun int diskfs_demuxer (@w{mach_msg_header_t *@var{inp}}, @w{mach_msg_header_t *@var{outp}})
+Demultiplex incoming @code{libports} messages on diskfs ports.
+@end deftypefun
+
+@findex diskfs_S_*
+The diskfs library also provides functions to demultiplex the fs, io,
+fsys, interrupt, and notify interfaces. All the server routines have
+the prefix @code{diskfs_S_}. For those routines, @code{in} arguments of
+type @code{file_t} or @code{io_t} appear as @code{struct protid *} to
+the stub.
+
+
+@node Twisted Filesystems
+@chapter Twisted Filesystems
+
+In the Hurd, translators are capable of redirecting filesystem requests
+to other translators, which makes it possible to implement alternative
+views of the same underlying data. The translators described in this
+chapter do not provide direct access to any data; rather, they are
+organizational tools to help you simplify an existing physical
+filesystem layout.
+
+Be prudent with these translators: you may accidentally injure people
+who want their filesystems to be rigidly tree-structured.@footnote{You
+are lost in a maze of twisty little filesystems, all alike@dots{}.}
+
+FIXME: finish
+
+@section symlink, firmlink
+@section hostmux, usermux
+@section shadowfs
+
+
+@node Distributed Filesystems
+@chapter Distributed Filesystems
+
+Distributed filesystems are designed to share files between separate
+machines via a network connection of some sort. Their design is
+significantly different than stored filesystems (@pxref{Stored
+Filesystems}): they need to deal with the problems of network delays and
+failures, and may require complex authentication and replication
+protocols involving multiple file servers.
+
+@menu
+* File Transfer Protocol:: A distributed filesystem based on FTP.
+* Network File System:: Sun's NFS: a lousy, but common filesystem.
+@end menu
+
+
+@node File Transfer Protocol
+@section File Transfer Protocol
+@cindex FTP
+
+FIXME: finish
+
+@menu
+* FTP Connection Library:: Managing remote FTP server connections.
+@end menu
+
+@subsection ftpcp, ftpdir
+@subsection ftpfs
+
+@node FTP Connection Library
+@subsection FTP Connection Library
+@scindex libftpconn
+@scindex ftpconn.h
+
+FIXME: finish
+
+
+@node Network File System
+@section Network File System
+@cindex NFS
+
+FIXME: finish
+
+@subsection nfsd
+@subsection nfs
+
+
+@node Networking
+@chapter Networking
+
+FIXME: this subsystem is in flux @c Thomas, 26-03-1998
+
+@menu
+* Socket Interface:: Network communication I/O protocol.
+@end menu
+
+
+@section pfinet
+@section pflocal
+@section libpipe
+
+@node Socket Interface
+@section Socket Interface
+@scindex socket.defs
+
+FIXME: net frobbing stuff may be added to socket.defs
+@c Thomas, 26-03-1998
+
+
+@node Terminal Handling
+@chapter Terminal Handling
+
+FIXME: finish
+
+@section term
+@section term.defs
+
+
+@node Running Programs
+@chapter Running Programs
+
+FIXME: finish
+
+@section ps, w
+@section libps
+@section exec
+@section proc
+@section crash
+
+
+@node Authentication
+@chapter Authentication
+
+FIXME: finish
+
+@menu
+* Auth Interface:: Auth ports implement the auth interface.
+@end menu
+
+@section addauth, rmauth, setauth
+@section su, sush, unsu
+@section login, loginpr
+@section auth
+
+@node Auth Interface
+@section Auth Interface
+@scindex auth.defs
+
+FIXME: finish
+
+@menu
+* Auth Protocol:: Bidirectional authentication.
+@end menu
+
+@node Auth Protocol
+@subsection Auth Protocol
+
+FIXME: finish
+
+
+@node Index
+@unnumbered Index
+
+@printindex cp
+
+@summarycontents
+@contents
+@bye
diff --git a/doc/navigating b/doc/navigating
new file mode 100644
index 00000000..b7e34dc8
--- /dev/null
+++ b/doc/navigating
@@ -0,0 +1,53 @@
+Some ideas on navigating and understanding the Hurd source code.
+
+First you must understand Mach IPC and MiG. Pay special attention to
+the way that various kinds of MiG calls manage memory; this is crucial
+to avoid leaks. The "Mach server writers' guide" explains all this.
+
+Most programs that make up the Hurd are built out of libraries; a
+solid understanding of the libraries that make up the source is
+essential. First start by reading the libports library specification
+(in libports/ports.h). This library manages the Mach ports that
+servers handle.
+
+Then start looking at some Hurd interfaces. A good place to start is
+to look at the proc server. There is only one proc server in a
+running system, but examine the way the interface (hurd/process.defs)
+is written and the way the proc server implements it.
+
+Then look at the auth server; make sure you understand how an auth
+transaction works. Again, by looking at the implementation, you can
+see a simple example.
+
+Filesystems are more complex; the interface specification is in
+hurd/io.defs and hurd/fs.defs. These interfaces are implemented by
+three different libraries: trivfs, diskfs, and netfs. trivfs
+implements single-node filesystems (that thus have no directories).
+Most trivfs filesystems don't even do any filesystem stuff at all.
+See, for example, the null translator (trans/null.c) for a simple
+example of using trivfs.
+
+diskfs is used for disk-based filesystems, with one in existence now:
+ext2fs. If you write another diskfs-based filesystem, you
+should DEFINITELY imitate the algorithms found in ext2fs; this
+is crucial to getting locking right.
+
+netfs is used for nfs and other such things: with directories, and all
+the actual filesystem operations being done by some other program (not
+necessarily over a network). The nfs implementation is fairly easy to
+understand.
+
+Examine some translators in the trans directory to see various simple
+examples.
+
+Also very important is to acquire familiarity with the Hurd and Mach
+calls provided in the GNU C library. Look at the header files in libc
+to see what they are, and read through them. Also examine parts of
+the libc implementation whenever you have any doubt about what an
+interface call should do: find where the C library uses that call, and
+that should help. It's worth, in fact, spending some time just
+exploring the implementation of various things in the hurd C library.
+
+You should take a look at all the libraries in the Hurd; spend time
+reading the code. Feel free to ask questions to help understand what
+you read.
diff --git a/exec/Makefile b/exec/Makefile
new file mode 100644
index 00000000..d332f360
--- /dev/null
+++ b/exec/Makefile
@@ -0,0 +1,37 @@
+# Copyright (C) 1993,94,95,96,98,99,2000,01,02,10,12 Free Software Foundation,
+# Inc.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with the GNU Hurd; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := exec
+makemode := server
+
+SRCS = exec.c main.c hashexec.c hostarch.c
+OBJS = main.o hostarch.o exec.o hashexec.o \
+ execServer.o exec_startupServer.o
+
+target = exec
+#targets = exec exec.static
+HURDLIBS = trivfs fshelp iohelp ports ihash shouldbeinlibc
+OTHERLIBS = -lpthread
+
+exec-MIGSFLAGS = -imacros $(srcdir)/execmutations.h
+exec_startup-MIGSFLAGS = -imacros $(srcdir)/execmutations.h
+
+include ../Makeconf
+
+exec.static exec: $(OBJS) $(library_deps)
diff --git a/exec/elfcore.c b/exec/elfcore.c
new file mode 100644
index 00000000..1f9238b6
--- /dev/null
+++ b/exec/elfcore.c
@@ -0,0 +1,566 @@
+/* Write ELF core dump files for GNU Hurd.
+ Copyright (C) 2002, 2004, 2008 Free Software Foundation, Inc.
+ Written by Roland McGrath.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <elf.h>
+#include <link.h>
+#include <string.h>
+#include <argz.h>
+#include <sys/mman.h>
+#include <sys/utsname.h>
+#include <sys/procfs.h>
+#include <stddef.h>
+
+#define ELF_CLASS PASTE (ELFCLASS, __ELF_NATIVE_CLASS)
+#define PASTE(a, b) PASTE_1 (a, b)
+#define PASTE_1(a, b) a##b
+
+#include <endian.h>
+#if BYTE_ORDER == BIG_ENDIAN
+#define ELF_DATA ELFDATA2MSB
+#elif BYTE_ORDER == LITTLE_ENDIAN
+#define ELF_DATA ELFDATA2LSB
+#endif
+
+#include <mach/thread_status.h>
+#include <assert.h>
+
+#ifdef i386_THREAD_STATE
+# define ELF_MACHINE EM_386
+
+/* The gregset_t format (compatible with Linux/x86) almost fits
+ the Mach i386_thread_state. */
+static inline void
+fetch_thread_regset (thread_t thread, prgregset_t *gregs)
+{
+ union
+ {
+ struct i386_thread_state state;
+ prgregset_t gregs;
+ } *u = (void *) gregs;
+ mach_msg_type_number_t count = i386_THREAD_STATE_COUNT;
+ assert (sizeof (struct i386_thread_state) < sizeof (prgregset_t));
+ assert (offsetof (struct i386_thread_state, gs) == REG_GS * 4);
+ assert (offsetof (struct i386_thread_state, eax) == REG_EAX * 4);
+
+ (void) thread_get_state (thread, i386_THREAD_STATE,
+ (thread_state_t) &u->state, &count);
+
+ u->gregs[REG_SS] = u->state.ss;
+ u->gregs[REG_UESP] = u->state.uesp;
+ u->gregs[REG_EFL] = u->state.efl;
+ u->gregs[REG_CS] = u->state.cs;
+ u->gregs[REG_EIP] = u->state.eip;
+
+ /* These are the extra words that don't exist in prgregset_t. */
+ u->gregs[REG_ERR] = u->gregs[REG_TRAPNO] = 0;
+}
+
+static inline void
+fetch_thread_fpregset (thread_t thread, prfpregset_t *fpregs)
+{
+ struct i386_float_state st;
+ mach_msg_type_number_t count = i386_FLOAT_STATE_COUNT;
+ error_t err = thread_get_state (thread, i386_FLOAT_STATE,
+ (thread_state_t) &st, &count);
+ if (err == 0 && st.initialized)
+ {
+ assert (sizeof *fpregs >= sizeof st.hw_state);
+ memcpy (fpregs, st.hw_state, sizeof st.hw_state);
+ }
+}
+
+#elif defined ALPHA_THREAD_STATE
+# define ELF_MACHINE EM_ALPHA
+
+/* The gregset_t format (compatible with Linux/Alpha) almost fits
+ the Mach alpha_thread_state. */
+static inline void
+fetch_thread_regset (thread_t thread, prgregset_t *gregs)
+{
+ mach_msg_type_number_t count = ALPHA_THREAD_STATE_COUNT;
+ assert (sizeof (struct alpha_thread_state) <= sizeof (prgregset_t));
+ (void) thread_get_state (thread, ALPHA_THREAD_STATE,
+ (thread_state_t) gregs, &count);
+ /* XXX
+ gregs[32] is process-status word: Mach doesn't return it!
+ It's already zero'd.
+ */
+}
+
+/* The FPU state matches exactly. */
+static inline void
+fetch_thread_fpregset (thread_t thread, prfpregset_t *fpregs)
+{
+ mach_msg_type_number_t count = ALPHA_FLOAT_STATE_COUNT;
+ assert (sizeof (struct alpha_float_state) == sizeof *fpregs);
+ (void) thread_get_state (thread, ALPHA_FLOAT_STATE,
+ (thread_state_t) fpregs, &count);
+}
+
+#else
+# warning "do not understand this machine flavor, no registers in dumps"
+# define ELF_MACHINE EM_NONE
+#endif
+
+
+#define TIME_VALUE_TO_TIMESPEC(tv, ts) { \
+ (ts)->tv_sec = (tv)->seconds; \
+ (ts)->tv_nsec = (tv)->microseconds * 1000; \
+}
+
+#define PAGES_TO_KB(x) ((x) * (vm_page_size / 1024))
+#define ENCODE_PCT(f) ((uint16_t) ((f) * 32768.0))
+
+extern process_t procserver; /* crash.c */
+
+error_t
+dump_core (task_t task, file_t file, off_t corelimit,
+ int signo, long int sigcode, int sigerror)
+{
+ static float host_memory_size = -1.0;
+ error_t err;
+ ElfW(Phdr) *phdrs, *ph;
+ ElfW(Ehdr) hdr = /* ELF header for the core file. */
+ {
+ e_ident:
+ {
+ [EI_MAG0] = ELFMAG0,
+ [EI_MAG1] = ELFMAG1,
+ [EI_MAG2] = ELFMAG2,
+ [EI_MAG3] = ELFMAG3,
+ [EI_CLASS] = ELF_CLASS,
+ [EI_DATA] = ELF_DATA,
+ [EI_VERSION] = EV_CURRENT,
+ [EI_OSABI] = ELFOSABI_SYSV,
+ [EI_ABIVERSION] = 0
+ },
+ e_type: ET_CORE,
+ e_version: EV_CURRENT,
+ e_machine: ELF_MACHINE,
+ e_ehsize: sizeof hdr,
+ e_phentsize: sizeof phdrs[0],
+ e_phoff: sizeof hdr, /* Fill in e_phnum later. */
+ };
+ off_t offset;
+ size_t wrote;
+
+ pid_t pid;
+ thread_t *threads;
+ size_t nthreads, i;
+ off_t notestart;
+
+ /* Helper macros for writing notes. */
+#define DEFINE_NOTE(typename) struct { struct note_header hdr; typename data; }
+#define WRITE_NOTE(type, var) ({ \
+ (var).hdr = NOTE_HEADER ((type), sizeof (var).data); \
+ write_note (&(var).hdr); \
+})
+ struct note_header
+ {
+ ElfW(Nhdr) note;
+ char name[(sizeof "CORE" + 3) &~ 3];
+ } __attribute__ ((packed));
+#define NOTE_HEADER(type, size) \
+ ((struct note_header) { { sizeof "CORE", (size), (type) }, "CORE" })
+ inline error_t write_note (struct note_header *hdr)
+ {
+ error_t err = 0;
+ char *data = (char *) hdr;
+ size_t size = sizeof *hdr + hdr->note.n_descsz;
+ if (corelimit >= 0 && offset + size > corelimit)
+ size = corelimit - offset;
+ while (size > 0)
+ {
+ err = io_write (file, data, size, offset, &wrote);
+ if (err)
+ return err;
+ if (wrote > size)
+ return EGRATUITOUS;
+ data += wrote;
+ size -= wrote;
+ }
+ offset = (offset + wrote + 3) &~ 3; /* Pad it to word alignment. */
+ return 0;
+ }
+
+ struct vm_region_list
+ {
+ struct vm_region_list *next;
+ vm_prot_t protection;
+ vm_address_t start;
+ vm_size_t length;
+ };
+ struct vm_region_list *regions = NULL, **tailp = &regions, *r;
+ unsigned int nregions = 0;
+
+ if (corelimit >= 0 && corelimit < sizeof hdr)
+ return EFBIG;
+
+ {
+ /* Examine the task and record the locations of contiguous memory
+ segments that we will dump into the core file. */
+
+ vm_address_t region_address, last_region_address, last_region_end;
+ vm_prot_t last_protection;
+#define RECORD_LAST_REGION do { \
+ if (last_region_end > last_region_address \
+ && last_protection != VM_PROT_NONE) \
+ record_last_region (alloca (sizeof (struct vm_region_list))); } while (0)
+ inline void record_last_region (struct vm_region_list *region)
+ {
+ *tailp = region;
+ tailp = &region->next;
+ region->next = NULL;
+ region->start = last_region_address;
+ region->length = last_region_end - last_region_address;
+ region->protection = last_protection;
+ ++nregions;
+ }
+
+ region_address = last_region_address = last_region_end = VM_MIN_ADDRESS;
+ last_protection = VM_PROT_NONE;
+ while (region_address < VM_MAX_ADDRESS)
+ {
+ vm_prot_t protection;
+ vm_prot_t max_protection;
+ vm_inherit_t inheritance;
+ boolean_t shared;
+ mach_port_t object_name;
+ vm_offset_t offset;
+ vm_size_t region_length = VM_MAX_ADDRESS - region_address;
+
+ err = vm_region (task,
+ &region_address,
+ &region_length,
+ &protection,
+ &max_protection,
+ &inheritance,
+ &shared,
+ &object_name,
+ &offset);
+ if (err == KERN_NO_SPACE)
+ break;
+ if (err != KERN_SUCCESS)
+ return err;
+
+ if (protection == last_protection && region_address == last_region_end)
+ /* This region is contiguous with and indistinguishable from
+ the previous one, so we just extend that one. */
+ last_region_end = region_address += region_length;
+ else
+ {
+ /* This region is distinct from the last one we saw,
+ so record that previous one. */
+ RECORD_LAST_REGION;
+ last_region_address = region_address;
+ last_region_end = region_address += region_length;
+ last_protection = protection;
+ }
+ }
+ /* Record the final region. */
+ RECORD_LAST_REGION;
+ }
+
+ /* Now we start laying out the file. */
+ offset = sizeof hdr + ((nregions + 1) * sizeof *phdrs);
+
+ /* Final check for tiny core limit. From now on, we will simply truncate
+ the file at CORELIMIT but not change the contents of what we write. */
+ if (corelimit >= 0 && corelimit < offset)
+ return EFBIG;
+
+ /* Now we can complete the file header and write it. */
+ hdr.e_phnum = nregions + 1;
+ err = io_write (file, (char *) &hdr, sizeof hdr, 0, &wrote);
+ if (err)
+ return err;
+ if (wrote < sizeof hdr)
+ return EGRATUITOUS; /* XXX */
+
+ /* Now we can write the various notes. */
+ notestart = offset;
+
+ /* First a dull note containing the results of `uname', a la Solaris. */
+ {
+ DEFINE_NOTE (struct utsname) note;
+ if (uname (&note.data) == 0) /* XXX Use proc_uname on task's proc port? */
+ err = WRITE_NOTE (NT_UTSNAME, note);
+ }
+ if (err || (corelimit >= 0 && corelimit <= offset))
+ return err;
+
+ err = proc_task2pid (procserver, task, &pid);
+ if (err)
+ return err;
+
+ /* Make sure we have the total RAM size of the host.
+ We only do this once, assuming that it won't change.
+ XXX this could use the task's host-self port instead. */
+ if (host_memory_size <= 0.0)
+ {
+ host_basic_info_data_t hostinfo;
+ mach_msg_type_number_t size = sizeof hostinfo;
+ error_t err = host_info (mach_host_self (), HOST_BASIC_INFO,
+ (host_info_t) &hostinfo, &size);
+ if (err == 0)
+ host_memory_size = hostinfo.memory_size;
+ }
+
+ /* The psinfo_t note contains some process-global info we should get from
+ the proc server, but no thread-specific info like register state. */
+ {
+ DEFINE_NOTE (psinfo_t) psinfo;
+ DEFINE_NOTE (pstatus_t) pstatus;
+ int flags = PI_FETCH_TASKINFO | PI_FETCH_THREADS | PI_FETCH_THREAD_BASIC;
+ char *waits = 0;
+ mach_msg_type_number_t num_waits = 0;
+ char pibuf[offsetof (struct procinfo, threadinfos[2])];
+ struct procinfo *pi = (void *) &pibuf;
+ mach_msg_type_number_t pi_size = sizeof pibuf;
+
+ memset (&pstatus.data, 0, sizeof pstatus.data);
+ memset (&psinfo.data, 0, sizeof psinfo.data);
+ pstatus.data.pr_pid = psinfo.data.pr_pid = pid;
+
+ err = proc_getprocinfo (procserver, pid, &flags,
+ (procinfo_t *) &pi, &pi_size,
+ &waits, &num_waits);
+ if (err == 0)
+ {
+ if (num_waits != 0)
+ munmap (waits, num_waits);
+
+ pstatus.data.pr_flags = psinfo.data.pr_flag = pi->state;
+ pstatus.data.pr_nlwp = psinfo.data.pr_nlwp = pi->nthreads;
+ pstatus.data.pr_ppid = psinfo.data.pr_ppid = pi->ppid;
+ pstatus.data.pr_pgid = psinfo.data.pr_pgid = pi->pgrp;
+ pstatus.data.pr_sid = psinfo.data.pr_sid = pi->session;
+
+ psinfo.data.pr_euid = pi->owner;
+ /* XXX struct procinfo should have these */
+ psinfo.data.pr_egid = psinfo.data.pr_gid = psinfo.data.pr_uid = -1;
+
+ psinfo.data.pr_size = PAGES_TO_KB (pi->taskinfo.virtual_size);
+ psinfo.data.pr_rssize = PAGES_TO_KB (pi->taskinfo.resident_size);
+
+ {
+ /* Sum all the threads' cpu_usage fields. */
+ integer_t cpu_usage = 0;
+ for (i = 0; i < pi->nthreads; ++i)
+ cpu_usage += pi->threadinfos[i].pis_bi.cpu_usage;
+ psinfo.data.pr_pctcpu = ENCODE_PCT ((float) cpu_usage
+ / (float) TH_USAGE_SCALE);
+ }
+ if (host_memory_size > 0.0)
+ psinfo.data.pr_pctmem
+ = ENCODE_PCT
+ ((float) pi->taskinfo.resident_size / host_memory_size);
+
+ TIME_VALUE_TO_TIMESPEC (&pi->taskinfo.creation_time,
+ &psinfo.data.pr_start);
+
+ TIME_VALUE_TO_TIMESPEC (&pi->taskinfo.user_time,
+ &pstatus.data.pr_utime);
+ TIME_VALUE_TO_TIMESPEC (&pi->taskinfo.system_time,
+ &pstatus.data.pr_stime);
+ /* Sum the user and system time for pr_time. */
+ pi->taskinfo.user_time.seconds += pi->taskinfo.system_time.seconds;
+ pi->taskinfo.user_time.microseconds += pi->taskinfo.system_time.microseconds;
+ if (pi->taskinfo.user_time.microseconds >= 1000000)
+ {
+ ++pi->taskinfo.user_time.seconds;
+ pi->taskinfo.user_time.microseconds -= 1000000;
+ }
+ TIME_VALUE_TO_TIMESPEC (&pi->taskinfo.user_time, &psinfo.data.pr_time);
+ /* XXX struct procinfo should have dead child info for pr_c[us]?time */
+
+ psinfo.data.pr_wstat = pi->exitstatus;
+
+ if ((void *) pi != &pibuf)
+ munmap (pi, pi_size);
+ }
+ if (err == 0)
+ {
+ /* We have to nab the process's own proc port to get the
+ proc server to tell us its registered arg locations. */
+ process_t proc;
+ err = proc_task2proc (procserver, task, &proc);
+ if (err == 0)
+ {
+ err = proc_get_arg_locations (proc,
+ &psinfo.data.pr_argv,
+ &psinfo.data.pr_envp);
+ mach_port_deallocate (mach_task_self (), proc);
+ }
+ {
+ /* Now fetch the arguments. We could do this directly from the
+ task given the locations we now have. But we are lazy and have
+ the proc server do it for us. */
+ char *data = psinfo.data.pr_psargs;
+ size_t datalen = sizeof psinfo.data.pr_psargs;
+ err = proc_getprocargs (procserver, pid, &data, &datalen);
+ if (err == 0)
+ {
+ psinfo.data.pr_argc = argz_count (data, datalen);
+ argz_stringify (data, datalen, ' ');
+ if (data != psinfo.data.pr_psargs)
+ {
+ memcpy (psinfo.data.pr_psargs, data,
+ sizeof psinfo.data.pr_psargs);
+ munmap (data, datalen);
+ }
+ }
+ }
+ err = WRITE_NOTE (NT_PSINFO, psinfo);
+ }
+
+ err = WRITE_NOTE (NT_PSTATUS, pstatus) ?: err;
+ }
+ if (err || (corelimit >= 0 && corelimit <= offset))
+ return err;
+
+ /* Now examine all the threads in the task.
+ For each thread we produce one or more notes. */
+ err = task_threads (task, &threads, &nthreads);
+ if (err)
+ return err;
+ for (i = 0; i < nthreads; ++i)
+ {
+ DEFINE_NOTE (lwpstatus_t) note;
+ memset (&note.data, 0, sizeof note.data);
+ note.data.pr_lwpid = i;
+
+ /* We have to write the death signal into every thread's record, even
+ though only one thread really took the signal. This is both because
+ we don't know which thread it was, and because GDB blindly uses the
+ value from each record to clobber the last (even if it's zero). */
+ note.data.pr_cursig = signo;
+ note.data.pr_info.si_signo = signo;
+ note.data.pr_info.si_code = sigcode;
+ note.data.pr_info.si_errno = sigerror;
+
+ fetch_thread_regset (threads[i], &note.data.pr_reg);
+ fetch_thread_fpregset (threads[i], &note.data.pr_fpreg);
+
+ err = WRITE_NOTE (NT_LWPSTATUS, note);
+ if (err || (corelimit >= 0 && corelimit <= offset))
+ break;
+
+ mach_port_deallocate (mach_task_self (), threads[i]);
+ }
+ /* If we broke out of the loop early, deallocate remaining thread ports. */
+ while (i < nthreads)
+ mach_port_deallocate (mach_task_self (), threads[i++]);
+ munmap (threads, nthreads * sizeof *threads);
+ if (err || (corelimit >= 0 && corelimit <= offset))
+ return err;
+
+ /* Make an array of program headers and fill them in.
+ The first one describes the note segment. */
+ ph = phdrs = alloca ((nregions + 1) * sizeof *phdrs);
+
+ memset (ph, 0, sizeof *ph);
+ ph->p_type = PT_NOTE;
+ ph->p_offset = notestart;
+ ph->p_filesz = offset - notestart;
+ ++ph;
+
+ /* Now make ELF program headers for each of the recorded memory regions.
+ Consistent with the Linux kernel, we create PT_LOAD headers with
+ p_filesz = 0 for the read-only segments that we are not dumping
+ into the file. */
+ for (r = regions; r != NULL; r = r->next)
+ {
+ memset (ph, 0, sizeof *ph);
+ ph->p_type = PT_LOAD;
+ ph->p_align = vm_page_size;
+ ph->p_flags = (((r->protection & VM_PROT_READ) ? PF_R : 0)
+ | ((r->protection & VM_PROT_WRITE) ? PF_W : 0)
+ | ((r->protection & VM_PROT_EXECUTE) ? PF_X : 0));
+ ph->p_vaddr = r->start;
+ ph->p_memsz = r->length;
+ ph->p_filesz = (r->protection & VM_PROT_WRITE) ? ph->p_memsz : 0;
+ ph->p_offset = round_page (offset);
+ offset += ph->p_filesz;
+ ++ph;
+ }
+
+ /* Now write the memory segment data. */
+ for (ph = &phdrs[1]; ph < &phdrs[nregions + 1]; ++ph)
+ if (ph->p_filesz > 0)
+ {
+ vm_address_t va = ph->p_vaddr;
+ vm_size_t sz = ph->p_memsz;
+ off_t ofs = ph->p_offset;
+ int wrote_any = 0;
+ do
+ {
+ pointer_t copied;
+ size_t copy_count;
+ err = vm_read (task, va, sz, &copied, &copy_count);
+ if (err == 0)
+ {
+ char *data = (void *) copied;
+ size_t left = copy_count, wrote;
+
+ va += copy_count;
+ sz -= copy_count;
+
+ do
+ {
+ if (corelimit >= 0 && ofs + left > corelimit)
+ left = corelimit - ofs;
+ err = io_write (file, data, left, ofs, &wrote);
+ if (err)
+ break;
+ ofs += wrote;
+ data += wrote;
+ left -= wrote;
+ if (ofs >= corelimit)
+ break;
+ } while (left > 0);
+
+ munmap ((void *) copied, copy_count);
+
+ if (left < copy_count)
+ wrote_any = 1;
+ }
+ else
+ {
+ /* Leave a hole in the file for pages we can't read. */
+ va += vm_page_size;
+ sz -= vm_page_size;
+ ofs += vm_page_size;
+ }
+ } while (sz > 0 && (corelimit < 0 || ofs < corelimit));
+
+ if (! wrote_any)
+ /* If we failed to write any contents at all,
+ don't claim the big hole as the contents. */
+ ph->p_filesz = 0;
+ }
+
+ /* Finally, we go back and write the program headers. */
+ err = io_write (file, (char *) phdrs, (nregions + 1) * sizeof phdrs[0],
+ sizeof hdr, &wrote);
+
+ return err;
+}
diff --git a/exec/exec.c b/exec/exec.c
new file mode 100644
index 00000000..2fc1e441
--- /dev/null
+++ b/exec/exec.c
@@ -0,0 +1,1523 @@
+/* GNU Hurd standard exec server.
+ Copyright (C) 1992,93,94,95,96,98,99,2000,01,02,04
+ Free Software Foundation, Inc.
+ Written by Roland McGrath.
+
+ Can exec ELF format directly.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+
+#include "priv.h"
+#include <mach/gnumach.h>
+#include <hurd.h>
+#include <hurd/exec.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+mach_port_t procserver; /* Our proc port. */
+
+/* Standard exec data for secure execs. */
+mach_port_t *std_ports;
+int *std_ints;
+size_t std_nports, std_nints;
+pthread_rwlock_t std_lock = PTHREAD_RWLOCK_INITIALIZER;
+
+
+#define b2he() a2he (errno)
+
+/* Zero the specified region but don't crash the server if it faults. */
+
+#include <hurd/sigpreempt.h>
+
+static error_t
+safe_bzero (void *ptr, size_t size)
+{
+ return hurd_safe_memset (ptr, 0, size);
+}
+
+
+/* Load or allocate a section. */
+static void
+load_section (void *section, struct execdata *u)
+{
+ vm_address_t addr = 0;
+ vm_offset_t filepos = 0;
+ vm_size_t filesz = 0, memsz = 0;
+ vm_prot_t vm_prot;
+ int anywhere;
+ vm_address_t mask = 0;
+ const ElfW(Phdr) *const ph = section;
+
+ if (u->error)
+ return;
+
+ vm_prot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
+
+ addr = ph->p_vaddr & ~(ph->p_align - 1);
+ memsz = ph->p_vaddr + ph->p_memsz - addr;
+ filepos = ph->p_offset & ~(ph->p_align - 1);
+ filesz = ph->p_offset + ph->p_filesz - filepos;
+ if ((ph->p_flags & PF_R) == 0)
+ vm_prot &= ~VM_PROT_READ;
+ if ((ph->p_flags & PF_W) == 0)
+ vm_prot &= ~VM_PROT_WRITE;
+ if ((ph->p_flags & PF_X) == 0)
+ vm_prot &= ~VM_PROT_EXECUTE;
+ anywhere = u->info.elf.anywhere;
+ if (! anywhere)
+ addr += u->info.elf.loadbase;
+ else
+#if 0
+ switch (elf_machine)
+ {
+ case EM_386:
+ case EM_486:
+ /* On the i386, programs normally load at 0x08000000, and
+ expect their data segment to be able to grow dynamically
+ upward from its start near that address. We need to make
+ sure that the dynamic linker is not mapped in a conflicting
+ address. */
+ /* mask = 0xf8000000UL; */ /* XXX */
+ break;
+ default:
+ break;
+ }
+#endif
+ if (anywhere && addr < vm_page_size)
+ addr = vm_page_size;
+
+ if (memsz == 0)
+ /* This section is empty; ignore it. */
+ return;
+
+ if (filesz != 0)
+ {
+ vm_address_t mapstart = round_page (addr);
+
+ /* Allocate space in the task and write CONTENTS into it. */
+ void write_to_task (vm_address_t mapstart, vm_size_t size,
+ vm_prot_t vm_prot, vm_address_t contents)
+ {
+ vm_size_t off = size % vm_page_size;
+ /* Allocate with vm_map to set max protections. */
+ u->error = vm_map (u->task,
+ &mapstart, size, mask, anywhere,
+ MACH_PORT_NULL, 0, 1,
+ vm_prot|VM_PROT_WRITE,
+ VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE,
+ VM_INHERIT_COPY);
+ if (! u->error && size >= vm_page_size)
+ u->error = vm_write (u->task, mapstart, contents, size - off);
+ if (! u->error && off != 0)
+ {
+ vm_address_t page = 0;
+ page = (vm_address_t) mmap (0, vm_page_size,
+ PROT_READ|PROT_WRITE, MAP_ANON,
+ 0, 0);
+ u->error = (page == -1) ? errno : 0;
+ if (! u->error)
+ {
+ u->error = hurd_safe_copyin ((void *) page, /* XXX/fault */
+ (void *) (contents + (size - off)),
+ off);
+ if (! u->error)
+ u->error = vm_write (u->task, mapstart + (size - off),
+ page, vm_page_size);
+ munmap ((caddr_t) page, vm_page_size);
+ }
+ }
+ /* Reset the current protections to the desired state. */
+ if (! u->error && (vm_prot & VM_PROT_WRITE) == 0)
+ u->error = vm_protect (u->task, mapstart, size, 0, vm_prot);
+ }
+
+ if (mapstart - addr < filesz)
+ {
+ /* MAPSTART is the first page that starts inside the section.
+ Map all the pages that start inside the section. */
+
+#define SECTION_IN_MEMORY_P (u->file_data != NULL)
+#define SECTION_CONTENTS (u->file_data + filepos)
+ if (SECTION_IN_MEMORY_P)
+ /* Data is already in memory; write it into the task. */
+ write_to_task (mapstart, filesz - (mapstart - addr), vm_prot,
+ (vm_address_t) SECTION_CONTENTS
+ + (mapstart - addr));
+ else if (u->filemap != MACH_PORT_NULL)
+ /* Map the data into the task directly from the file. */
+ u->error = vm_map (u->task,
+ &mapstart, filesz - (mapstart - addr),
+ mask, anywhere,
+ u->filemap, filepos + (mapstart - addr), 1,
+ vm_prot,
+ VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE,
+ VM_INHERIT_COPY);
+ else
+ {
+ /* Cannot map the data. Read it into a buffer and vm_write
+ it into the task. */
+ const vm_size_t size = filesz - (mapstart - addr);
+ void *buf = map (u, filepos + (mapstart - addr), size);
+ if (buf)
+ write_to_task (mapstart, size, vm_prot, (vm_address_t) buf);
+ }
+ if (u->error)
+ return;
+
+ if (anywhere)
+ {
+ /* We let the kernel choose the location of the mapping.
+ Now record where it ended up. Later sections cannot
+ be mapped anywhere, they must come after this one. */
+ u->info.elf.loadbase = mapstart;
+ addr = mapstart + (addr % vm_page_size);
+ anywhere = u->info.elf.anywhere = 0;
+ mask = 0;
+ }
+ }
+
+ /* If this segment is executable, adjust start_code and end_code
+ so that this mapping is within that range. */
+ if (vm_prot & VM_PROT_EXECUTE)
+ {
+ if (u->start_code == 0 || u->start_code > addr)
+ u->start_code = addr;
+
+ if (u->end_code < addr + memsz)
+ u->end_code = addr + memsz;
+ }
+
+ if (mapstart > addr)
+ {
+ /* We must read and copy in the space in the section before the
+ first page boundary. */
+ vm_address_t overlap_page = trunc_page (addr);
+ vm_address_t ourpage = 0;
+ vm_size_t size = 0;
+ void *readaddr;
+ size_t readsize;
+
+ u->error = vm_read (u->task, overlap_page, vm_page_size,
+ &ourpage, &size);
+ if (u->error)
+ {
+ if (u->error == KERN_INVALID_ADDRESS)
+ {
+ /* The space is unallocated. */
+ u->error = vm_allocate (u->task,
+ &overlap_page, vm_page_size, 0);
+ size = vm_page_size;
+ if (!u->error)
+ {
+ ourpage = (vm_address_t) mmap (0, vm_page_size,
+ PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ u->error = (ourpage == -1) ? errno : 0;
+ }
+ }
+ if (u->error)
+ {
+ maplose:
+ vm_deallocate (u->task, mapstart, filesz);
+ return;
+ }
+ }
+
+ readaddr = (void *) (ourpage + (addr - overlap_page));
+ readsize = size - (addr - overlap_page);
+ if (readsize > filesz)
+ readsize = filesz;
+
+ if (SECTION_IN_MEMORY_P)
+ memcpy (readaddr, SECTION_CONTENTS, readsize);
+ else
+ {
+ const void *contents = map (u, filepos, readsize);
+ if (!contents)
+ goto maplose;
+ u->error = hurd_safe_copyin (readaddr, contents,
+ readsize); /* XXX/fault */
+ if (u->error)
+ goto maplose;
+ }
+ u->error = vm_write (u->task, overlap_page, ourpage, size);
+ if (u->error == KERN_PROTECTION_FAILURE)
+ {
+ /* The overlap page is not writable; the section
+ that appears in preceding memory is read-only.
+ Change the page's protection so we can write it. */
+ u->error = vm_protect (u->task, overlap_page, size,
+ 0, vm_prot | VM_PROT_WRITE);
+ if (!u->error)
+ u->error = vm_write (u->task, overlap_page, ourpage, size);
+ /* If this section is not supposed to be writable either,
+ restore the page's protection to read-only. */
+ if (!u->error && !(vm_prot & VM_PROT_WRITE))
+ u->error = vm_protect (u->task, overlap_page, size,
+ 0, vm_prot);
+ }
+ munmap ((caddr_t) ourpage, size);
+ if (u->error)
+ goto maplose;
+ }
+
+ if (u->cntl)
+ u->cntl->accessed = 1;
+
+ /* Tell the code below to zero-fill the remaining area. */
+ addr += filesz;
+ memsz -= filesz;
+ }
+
+ if (memsz != 0)
+ {
+ /* SEC_ALLOC: Allocate zero-filled memory for the section. */
+
+ vm_address_t mapstart = round_page (addr);
+
+ if (mapstart - addr < memsz)
+ {
+ /* MAPSTART is the first page that starts inside the section.
+ Allocate all the pages that start inside the section. */
+ u->error = vm_map (u->task, &mapstart, memsz - (mapstart - addr),
+ mask, anywhere, MACH_PORT_NULL, 0, 1,
+ vm_prot, VM_PROT_ALL, VM_INHERIT_COPY);
+ if (u->error)
+ return;
+ }
+
+ if (anywhere)
+ {
+ /* We let the kernel choose the location of the zero space.
+ Now record where it ended up. Later sections cannot
+ be mapped anywhere, they must come after this one. */
+ u->info.elf.loadbase = mapstart;
+ addr = mapstart + (addr % vm_page_size);
+ anywhere = u->info.elf.anywhere = 0;
+ mask = 0;
+ }
+
+ if (mapstart > addr)
+ {
+ /* Zero space in the section before the first page boundary. */
+ vm_address_t overlap_page = trunc_page (addr);
+ vm_address_t ourpage = 0;
+ vm_size_t size = 0;
+ u->error = vm_read (u->task, overlap_page, vm_page_size,
+ &ourpage, &size);
+ if (u->error)
+ {
+ vm_deallocate (u->task, mapstart, memsz);
+ return;
+ }
+ u->error = safe_bzero ((void *) (ourpage + (addr - overlap_page)),
+ size - (addr - overlap_page));
+ if (! u->error && !(vm_prot & VM_PROT_WRITE))
+ u->error = vm_protect (u->task, overlap_page, size,
+ 0, VM_PROT_WRITE);
+ if (! u->error)
+ u->error = vm_write (u->task, overlap_page, ourpage, size);
+ if (! u->error && !(vm_prot & VM_PROT_WRITE))
+ u->error = vm_protect (u->task, overlap_page, size, 0, vm_prot);
+ munmap ((caddr_t) ourpage, size);
+ }
+ }
+}
+
+/* XXX all accesses of the mapped data need to use fault handling
+ to abort the RPC when mapped file data generates bad page faults.
+ I've marked some accesses with XXX/fault comments.
+ --roland */
+
+void *
+map (struct execdata *e, off_t posn, size_t len)
+{
+ const size_t size = e->file_size;
+ size_t offset;
+
+ if ((map_filepos (e) & ~(map_vsize (e) - 1)) == (posn & ~(map_vsize (e) - 1))
+ && posn + len - map_filepos (e) <= map_fsize (e))
+ /* The current mapping window covers it. */
+ offset = posn & (map_vsize (e) - 1);
+ else if (posn + len > size)
+ /* The requested data wouldn't fit in the file. */
+ return NULL;
+ else if (e->file_data != NULL) {
+ return e->file_data + posn;
+ } else if (e->filemap == MACH_PORT_NULL)
+ {
+ /* No mapping for the file. Read the data by RPC. */
+ char *buffer = map_buffer (e);
+ mach_msg_type_number_t nread = map_vsize (e);
+
+ assert (e->file_data == NULL); /* Must be first or second case. */
+
+ /* Read as much as we can get into the buffer right now. */
+ e->error = io_read (e->file, &buffer, &nread, posn, round_page (len));
+ if (e->error)
+ return NULL;
+ if (buffer != map_buffer (e))
+ {
+ /* The data was returned out of line. Discard the old buffer. */
+ if (map_vsize (e) != 0)
+ munmap (map_buffer (e), map_vsize (e));
+ map_buffer (e) = buffer;
+ map_vsize (e) = round_page (nread);
+ }
+
+ map_filepos (e) = posn;
+ map_set_fsize (e, nread);
+ offset = 0;
+ }
+ else
+ {
+ /* Deallocate the old mapping area. */
+ if (map_buffer (e) != NULL)
+ munmap (map_buffer (e), map_vsize (e));
+ map_buffer (e) = NULL;
+
+ /* Make sure our mapping is page-aligned in the file. */
+ offset = posn & (vm_page_size - 1);
+ map_filepos (e) = trunc_page (posn);
+ map_vsize (e) = round_page (posn + len) - map_filepos (e);
+
+ /* Map the data from the file. */
+ if (vm_map (mach_task_self (),
+ (vm_address_t *) &map_buffer (e), map_vsize (e), 0, 1,
+ e->filemap, map_filepos (e), 1, VM_PROT_READ, VM_PROT_READ,
+ VM_INHERIT_NONE))
+ {
+ e->error = EIO;
+ return NULL;
+ }
+
+ if (e->cntl)
+ e->cntl->accessed = 1;
+
+ map_set_fsize (e, MIN (map_vsize (e), size - map_filepos (e)));
+ }
+
+ return map_buffer (e) + offset;
+}
+
+/* We don't have a stdio stream, but we have a mapping window
+ we need to initialize. */
+static void
+prepare_stream (struct execdata *e)
+{
+ e->map_buffer = NULL;
+ e->map_vsize = e->map_fsize = 0;
+ e->map_filepos = 0;
+}
+
+/* Prepare to check and load FILE. */
+static void
+prepare (file_t file, struct execdata *e)
+{
+ memory_object_t rd, wr;
+
+ e->file = file;
+
+ e->file_data = NULL;
+ e->cntl = NULL;
+ e->filemap = MACH_PORT_NULL;
+ e->cntlmap = MACH_PORT_NULL;
+
+ e->interp.section = NULL;
+
+ e->start_code = 0;
+ e->end_code = 0;
+
+ /* Initialize E's stdio stream. */
+ prepare_stream (e);
+
+ /* Try to mmap FILE. */
+ e->error = io_map (file, &rd, &wr);
+ if (! e->error)
+ /* Mapping is O.K. */
+ {
+ if (wr != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), wr);
+ if (rd == MACH_PORT_NULL)
+ {
+ e->error = EBADF; /* ? XXX */
+ return;
+ }
+ e->filemap = rd;
+
+ e->error = /* io_map_cntl (file, &e->cntlmap) */ EOPNOTSUPP; /* XXX */
+ if (!e->error)
+ e->error = vm_map (mach_task_self (), (vm_address_t *) &e->cntl,
+ vm_page_size, 0, 1, e->cntlmap, 0, 0,
+ VM_PROT_READ|VM_PROT_WRITE,
+ VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_NONE);
+
+ if (e->cntl)
+ while (1)
+ {
+ pthread_spin_lock (&e->cntl->lock);
+ switch (e->cntl->conch_status)
+ {
+ case USER_COULD_HAVE_CONCH:
+ e->cntl->conch_status = USER_HAS_CONCH;
+ case USER_HAS_CONCH:
+ pthread_spin_unlock (&e->cntl->lock);
+ /* Break out of the loop. */
+ break;
+ case USER_RELEASE_CONCH:
+ case USER_HAS_NOT_CONCH:
+ default: /* Oops. */
+ pthread_spin_unlock (&e->cntl->lock);
+ e->error = io_get_conch (e->file);
+ if (e->error)
+ return;
+ /* Continue the loop. */
+ continue;
+ }
+
+ /* Get here if we are now IT. */
+ e->file_size = 0;
+ if (e->cntl->use_file_size)
+ e->file_size = e->cntl->file_size;
+ if (e->cntl->use_read_size && e->cntl->read_size > e->file_size)
+ e->file_size = e->cntl->read_size;
+ break;
+ }
+ }
+
+ if (!e->cntl && (!e->error || e->error == EOPNOTSUPP))
+ {
+ /* No shared page. Do a stat to find the file size. */
+ struct stat st;
+ e->error = io_stat (file, &st);
+ if (e->error)
+ return;
+ e->file_size = st.st_size;
+ e->optimal_block = st.st_blksize;
+ }
+}
+
+#include <endian.h>
+#if BYTE_ORDER == BIG_ENDIAN
+#define host_ELFDATA ELFDATA2MSB
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define host_ELFDATA ELFDATA2LSB
+#endif
+
+static void
+check_elf (struct execdata *e)
+{
+ ElfW(Ehdr) *ehdr = map (e, 0, sizeof (ElfW(Ehdr)));
+ ElfW(Phdr) *phdr;
+
+ if (! ehdr)
+ {
+ if (!e->error)
+ e->error = ENOEXEC;
+ return;
+ }
+
+ if (*(ElfW(Word) *) ehdr != ((union { ElfW(Word) word;
+ unsigned char string[SELFMAG]; })
+ { string: ELFMAG }).word)
+ {
+ e->error = ENOEXEC;
+ return;
+ }
+
+ if (ehdr->e_ident[EI_CLASS] != ELFCLASS32 ||
+ ehdr->e_ident[EI_DATA] != host_ELFDATA ||
+ ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
+ ehdr->e_version != EV_CURRENT ||
+ ehdr->e_ehsize < sizeof *ehdr ||
+ ehdr->e_phentsize != sizeof (ElfW(Phdr)))
+ {
+ e->error = ENOEXEC;
+ return;
+ }
+ e->error = elf_machine_matches_host (ehdr->e_machine);
+ if (e->error)
+ return;
+
+ /* Extract all this information now, while EHDR is mapped.
+ The `map' call below for the phdrs may reuse the mapping window. */
+ e->entry = ehdr->e_entry;
+ e->info.elf.anywhere = (ehdr->e_type == ET_DYN ||
+ ehdr->e_type == ET_REL);
+ e->info.elf.loadbase = 0;
+ e->info.elf.phnum = ehdr->e_phnum;
+
+ phdr = map (e, ehdr->e_phoff, ehdr->e_phnum * sizeof (ElfW(Phdr)));
+ if (! phdr)
+ {
+ if (!e->error)
+ e->error = ENOEXEC;
+ return;
+ }
+ e->info.elf.phdr = phdr;
+ e->info.elf.phdr_addr = ehdr->e_phoff;
+}
+
+/* Copy MAPPED_PHDR into E->info.elf.phdr, filling in E->interp.phdr
+ in the process. */
+static void
+check_elf_phdr (struct execdata *e, const ElfW(Phdr) *mapped_phdr)
+{
+ const ElfW(Phdr) *phdr;
+ bool seen_phdr = false;
+
+ memcpy (e->info.elf.phdr, mapped_phdr,
+ e->info.elf.phnum * sizeof (ElfW(Phdr)));
+
+ /* Default state if we do not see PT_GNU_STACK telling us what to do.
+ Executable stack is the compatible default.
+ (XXX should be machine-dependent??)
+ */
+ e->info.elf.execstack = 1;
+
+ for (phdr = e->info.elf.phdr;
+ phdr < &e->info.elf.phdr[e->info.elf.phnum];
+ ++phdr)
+ switch (phdr->p_type)
+ {
+ case PT_INTERP:
+ e->interp.phdr = phdr;
+ break;
+ case PT_LOAD:
+ if (e->file_size <= (off_t) (phdr->p_offset +
+ phdr->p_filesz))
+ {
+ e->error = ENOEXEC;
+ return;
+ }
+ /* Check if this is the segment that contains the phdr image. */
+ if (!seen_phdr
+ && (phdr->p_offset & -phdr->p_align) == 0 /* Sanity check. */
+ && phdr->p_offset <= e->info.elf.phdr_addr
+ && e->info.elf.phdr_addr - phdr->p_offset < phdr->p_filesz)
+ {
+ e->info.elf.phdr_addr += phdr->p_vaddr - phdr->p_offset;
+ seen_phdr = true;
+ }
+ break;
+ case PT_GNU_STACK:
+ e->info.elf.execstack = phdr->p_flags & PF_X;
+ break;
+ }
+
+ if (!seen_phdr)
+ e->info.elf.phdr_addr = 0;
+}
+
+
+static void
+check (struct execdata *e)
+{
+ check_elf (e); /* XXX/fault */
+}
+
+
+/* Release the conch and clean up mapping the file and control page. */
+static void
+finish_mapping (struct execdata *e)
+{
+ if (e->cntl != NULL)
+ {
+ pthread_spin_lock (&e->cntl->lock);
+ if (e->cntl->conch_status == USER_RELEASE_CONCH)
+ {
+ pthread_spin_unlock (&e->cntl->lock);
+ io_release_conch (e->file);
+ }
+ else
+ {
+ e->cntl->conch_status = USER_HAS_NOT_CONCH;
+ pthread_spin_unlock (&e->cntl->lock);
+ }
+ munmap (e->cntl, vm_page_size);
+ e->cntl = NULL;
+ }
+ if (e->filemap != MACH_PORT_NULL)
+ {
+ mach_port_deallocate (mach_task_self (), e->filemap);
+ e->filemap = MACH_PORT_NULL;
+ }
+ if (e->cntlmap != MACH_PORT_NULL)
+ {
+ mach_port_deallocate (mach_task_self (), e->cntlmap);
+ e->cntlmap = MACH_PORT_NULL;
+ }
+}
+
+/* Clean up after reading the file (need not be completed).
+ Note: this may be called several times for the E, so it must take care
+ of checking what was already freed. */
+void
+finish (struct execdata *e, int dealloc_file)
+{
+ finish_mapping (e);
+ {
+ if (e->file_data != NULL) {
+ free (e->file_data);
+ e->file_data = NULL;
+ } else if (map_buffer (e) != NULL) {
+ munmap (map_buffer (e), map_vsize (e));
+ map_buffer (e) = NULL;
+ }
+ }
+ if (dealloc_file && e->file != MACH_PORT_NULL)
+ {
+ mach_port_deallocate (mach_task_self (), e->file);
+ e->file = MACH_PORT_NULL;
+ }
+}
+
+
+/* Load the file. */
+static void
+load (task_t usertask, struct execdata *e)
+{
+ e->task = usertask;
+
+ if (! e->error)
+ {
+ ElfW(Word) i;
+ for (i = 0; i < e->info.elf.phnum; ++i)
+ if (e->info.elf.phdr[i].p_type == PT_LOAD)
+ load_section (&e->info.elf.phdr[i], e);
+
+ /* The entry point address is relative to wherever we loaded the
+ program text. */
+ e->entry += e->info.elf.loadbase;
+ }
+
+ /* Release the conch for the file. */
+ finish_mapping (e);
+}
+
+
+static inline void *
+servercopy (void *arg, mach_msg_type_number_t argsize, boolean_t argcopy,
+ error_t *errorp)
+{
+ if (! argcopy)
+ return arg;
+
+ /* ARG came in-line, so we must copy it. */
+ void *copy;
+ copy = mmap (0, argsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (copy == MAP_FAILED)
+ {
+ *errorp = errno;
+ return NULL;
+ }
+ memcpy (copy, arg, argsize);
+ return copy;
+}
+
+
+static error_t
+do_exec (file_t file,
+ task_t oldtask,
+ int flags,
+ char *argv, mach_msg_type_number_t argvlen, boolean_t argv_copy,
+ char *envp, mach_msg_type_number_t envplen, boolean_t envp_copy,
+ mach_port_t *dtable, mach_msg_type_number_t dtablesize,
+ boolean_t dtable_copy,
+ mach_port_t *portarray, mach_msg_type_number_t nports,
+ boolean_t portarray_copy,
+ int *intarray, mach_msg_type_number_t nints, boolean_t intarray_copy,
+ mach_port_t *deallocnames, mach_msg_type_number_t ndeallocnames,
+ mach_port_t *destroynames, mach_msg_type_number_t ndestroynames)
+{
+ struct execdata e, interp;
+ task_t newtask = MACH_PORT_NULL;
+ thread_t thread = MACH_PORT_NULL;
+ struct bootinfo *boot = 0;
+ int *ports_replaced;
+ int secure, defaults;
+ mach_msg_type_number_t i;
+ int intarray_dealloc = 0; /* Dealloc INTARRAY before returning? */
+ int oldtask_trashed = 0; /* Have we trashed the old task? */
+
+ /* Prime E for executing FILE and check its validity. This must be an
+ inline function because it stores pointers into alloca'd storage in E
+ for later use in `load'. */
+ void prepare_and_check (file_t file, struct execdata *e)
+ {
+ /* Prepare E to read the file. */
+ prepare (file, e);
+ if (e->error)
+ return;
+
+ /* Check the file for validity first. */
+ check (e);
+ }
+
+
+ /* Here is the main body of the function. */
+
+ interp.file = MACH_PORT_NULL;
+
+ /* Catch this error now, rather than later. */
+ /* XXX For EXEC_DEFAULTS, this is only an error if one of the user's
+ ports is null; if they are all provided, then EXEC_DEFAULTS would
+ have no effect, and the lack of installed standard ports should
+ not cause an error. -mib */
+ if ((!std_ports || !std_ints) && (flags & (EXEC_SECURE|EXEC_DEFAULTS)))
+ return EIEIO;
+
+ /* Suspend the existing task before frobnicating it. */
+ if (oldtask != MACH_PORT_NULL && (e.error = task_suspend (oldtask)))
+ return e.error;
+
+ /* Prime E for executing FILE and check its validity. */
+ prepare_and_check (file, &e);
+
+ if (e.error == ENOEXEC)
+ {
+ /* Check for a #! executable file. */
+ check_hashbang (&e,
+ file, oldtask, flags,
+ argv, argvlen, argv_copy,
+ envp, envplen, envp_copy,
+ dtable, dtablesize, dtable_copy,
+ portarray, nports, portarray_copy,
+ intarray, nints, intarray_copy,
+ deallocnames, ndeallocnames,
+ destroynames, ndestroynames);
+ if (! e.error)
+ /* The #! exec succeeded; nothing more to do. */
+ return 0;
+ }
+
+ if (e.error)
+ /* The file is not a valid executable. */
+ goto out;
+
+ const ElfW(Phdr) *phdr = e.info.elf.phdr;
+ e.info.elf.phdr = alloca (e.info.elf.phnum * sizeof (ElfW(Phdr)));
+ check_elf_phdr (&e, phdr);
+
+ if (oldtask == MACH_PORT_NULL)
+ flags |= EXEC_NEWTASK;
+
+ if (flags & (EXEC_NEWTASK|EXEC_SECURE))
+ {
+ /* Create the new task. If we are not being secure, then use OLDTASK
+ for the task_create RPC, in case it is something magical. */
+ e.error = task_create (((flags & EXEC_SECURE) ||
+ oldtask == MACH_PORT_NULL) ?
+ mach_task_self () : oldtask,
+#ifdef KERN_INVALID_LEDGER
+ NULL, 0, /* OSF Mach */
+#endif
+ 0, &newtask);
+ if (e.error)
+ goto out;
+ }
+ else
+ newtask = oldtask;
+
+
+ pthread_rwlock_rdlock (&std_lock);
+ {
+ /* Store the data that we will give in response
+ to the RPC on the new task's bootstrap port. */
+
+ /* Set boot->portarray[IDX] to NEW. If REAUTH is nonzero,
+ io_reauthenticate NEW and set it to the authenticated port.
+ If CONSUME is nonzero, a reference on NEW is consumed;
+ it is invalid to give nonzero values to both REAUTH and CONSUME. */
+#define use(idx, new, reauth, consume) \
+ do { use1 (idx, new, reauth, consume); \
+ if (e.error) goto stdout; } while (0)
+ void use1 (unsigned int idx, mach_port_t new,
+ int reauth, int consume)
+ {
+ if (new != MACH_PORT_NULL && reauth)
+ {
+ mach_port_t ref = mach_reply_port (), authed;
+ /* MAKE_SEND is safe here because we destroy REF ourselves. */
+ e.error = io_reauthenticate (new, ref, MACH_MSG_TYPE_MAKE_SEND);
+ if (! e.error)
+ e.error = auth_user_authenticate
+ (boot->portarray[INIT_PORT_AUTH],
+ ref, MACH_MSG_TYPE_MAKE_SEND, &authed);
+ mach_port_destroy (mach_task_self (), ref);
+ if (e.error)
+ return;
+ new = authed;
+ }
+ else
+ {
+ if (!consume && new != MACH_PORT_NULL)
+ mach_port_mod_refs (mach_task_self (),
+ new, MACH_PORT_RIGHT_SEND, 1);
+ }
+
+ boot->portarray[idx] = new;
+ ports_replaced[idx] = 1;
+ }
+
+ e.error = ports_create_port (execboot_portclass, port_bucket,
+ sizeof *boot, &boot);
+ if (boot == NULL)
+ {
+ stdout:
+ pthread_rwlock_unlock (&std_lock);
+ goto out;
+ }
+ bzero (&boot->pi + 1, (char *) &boot[1] - (char *) (&boot->pi + 1));
+
+ /* These flags say the information we pass through to the new program
+ may need to be modified. */
+ secure = (flags & EXEC_SECURE);
+ defaults = (flags & EXEC_DEFAULTS);
+
+ /* Now record the big blocks of data we shuffle around unchanged.
+ Whatever arrived inline, we must allocate space for so it can
+ survive after this RPC returns. */
+
+ boot->flags = flags;
+
+ argv = servercopy (argv, argvlen, argv_copy, &e.error);
+ if (e.error)
+ goto stdout;
+ boot->argv = argv;
+ boot->argvlen = argvlen;
+ envp = servercopy (envp, envplen, envp_copy, &e.error);
+ if (e.error)
+ goto stdout;
+ boot->envp = envp;
+ boot->envplen = envplen;
+ dtable = servercopy (dtable, dtablesize * sizeof (mach_port_t),
+ dtable_copy, &e.error);
+ if (e.error)
+ goto stdout;
+ boot->dtable = dtable;
+ boot->dtablesize = dtablesize;
+
+ if ((secure || defaults) && nints < INIT_INT_MAX)
+ {
+ /* Make sure the intarray is at least big enough. */
+ if (intarray_copy || (round_page (nints * sizeof (int)) <
+ round_page (INIT_INT_MAX * sizeof (int))))
+ {
+ /* Allocate a new vector that is big enough. */
+ boot->intarray = mmap (0, INIT_INT_MAX * sizeof (int),
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ memcpy (boot->intarray, intarray, nints * sizeof (int));
+ intarray_dealloc = !intarray_copy;
+ }
+ else
+ boot->intarray = intarray;
+ boot->nints = INIT_INT_MAX;
+ }
+ else
+ {
+ intarray = servercopy (intarray, nints * sizeof (int), intarray_copy,
+ &e.error);
+ if (e.error)
+ goto stdout;
+ boot->intarray = intarray;
+ boot->nints = nints;
+ }
+
+ if (secure)
+ boot->intarray[INIT_UMASK] = std_ints ? std_ints[INIT_UMASK] : CMASK;
+
+ /* Now choose the ports to give the new program. */
+
+ boot->nports = nports < INIT_PORT_MAX ? INIT_PORT_MAX : nports;
+ boot->portarray = mmap (0, boot->nports * sizeof (mach_port_t),
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ /* Start by copying the array as passed. */
+ for (i = 0; i < nports; ++i)
+ boot->portarray[i] = portarray[i];
+ if (MACH_PORT_NULL != 0)
+ for (; i < boot->nports; ++i)
+ boot->portarray[i] = MACH_PORT_NULL;
+ /* Keep track of which ports in BOOT->portarray come from the original
+ PORTARRAY, and which we replace. */
+ ports_replaced = alloca (boot->nports * sizeof *ports_replaced);
+ bzero (ports_replaced, boot->nports * sizeof *ports_replaced);
+
+ if (portarray[INIT_PORT_BOOTSTRAP] == MACH_PORT_NULL &&
+ oldtask != MACH_PORT_NULL)
+ {
+ if (! task_get_bootstrap_port (oldtask,
+ &boot->portarray[INIT_PORT_BOOTSTRAP]))
+ ports_replaced[INIT_PORT_BOOTSTRAP] = 1;
+ }
+
+ /* Note that the parentheses on this first test are different from the
+ others below it. */
+ if ((secure || defaults)
+ && boot->portarray[INIT_PORT_AUTH] == MACH_PORT_NULL)
+ /* Q: Doesn't this let anyone run a program and make it
+ get a root auth port?
+ A: No; the standard port for INIT_PORT_AUTH has no UID's at all.
+ See init.trim/init.c (init_stdarrays). */
+ use (INIT_PORT_AUTH, std_ports[INIT_PORT_AUTH], 0, 0);
+ if (secure || (defaults
+ && boot->portarray[INIT_PORT_PROC] == MACH_PORT_NULL))
+ {
+ /* Ask the proc server for the proc port for this task. */
+ mach_port_t new;
+
+ e.error = proc_task2proc (procserver, newtask, &new);
+ if (e.error)
+ goto stdout;
+ use (INIT_PORT_PROC, new, 0, 1);
+ }
+ else if (oldtask != newtask && oldtask != MACH_PORT_NULL
+ && boot->portarray[INIT_PORT_PROC] != MACH_PORT_NULL)
+ {
+ mach_port_t new;
+ /* This task port refers to the old task; use it to fetch a new
+ one for the new task. */
+ e.error = proc_task2proc (boot->portarray[INIT_PORT_PROC],
+ newtask, &new);
+ if (e.error)
+ goto stdout;
+ use (INIT_PORT_PROC, new, 0, 1);
+ }
+ if (secure || (defaults
+ && boot->portarray[INIT_PORT_CRDIR] == MACH_PORT_NULL))
+ use (INIT_PORT_CRDIR, std_ports[INIT_PORT_CRDIR], 1, 0);
+ if ((secure || defaults)
+ && boot->portarray[INIT_PORT_CWDIR] == MACH_PORT_NULL)
+ use (INIT_PORT_CWDIR, std_ports[INIT_PORT_CWDIR], 1, 0);
+ }
+ pthread_rwlock_unlock (&std_lock);
+
+
+ /* We have now concocted in BOOT the complete Hurd context (ports and
+ ints) that the new program image will run under. We will use these
+ ports for looking up the interpreter file if there is one. */
+
+ if (! e.error && e.interp.section)
+ {
+ /* There is an interpreter section specifying another file to load
+ along with this executable. Find the name of the file and open
+ it. */
+
+ char *name = map (&e, (e.interp.phdr->p_offset
+ & ~(e.interp.phdr->p_align - 1)),
+ e.interp.phdr->p_filesz);
+ if (! name && ! e.error)
+ e.error = ENOEXEC;
+
+ if (! name)
+ e.interp.section = NULL;
+ else
+ {
+ /* Open the named file using the appropriate directory ports for
+ the user. */
+ error_t user_port (int which, error_t (*operate) (mach_port_t))
+ {
+ return (*operate) (boot->nports > which ?
+ boot->portarray[which] :
+ MACH_PORT_NULL);
+ }
+ file_t user_fd (int fd)
+ {
+ if (fd < 0 || fd >= boot->dtablesize ||
+ boot->dtable[fd] == MACH_PORT_NULL)
+ {
+ errno = EBADF;
+ return MACH_PORT_NULL;
+ }
+ mach_port_mod_refs (mach_task_self (), boot->dtable[fd],
+ MACH_PORT_RIGHT_SEND, +1);
+ return boot->dtable[fd];
+ }
+ /* XXX/fault */
+ e.error = hurd_file_name_lookup (&user_port, &user_fd, 0,
+ name, O_READ, 0, &interp.file);
+ }
+ }
+
+ if (interp.file != MACH_PORT_NULL)
+ {
+ /* We opened an interpreter file. Prepare it for loading too. */
+ prepare_and_check (interp.file, &interp);
+ if (! interp.error)
+ {
+ const ElfW(Phdr) *phdr = interp.info.elf.phdr;
+ interp.info.elf.phdr = alloca (interp.info.elf.phnum *
+ sizeof (ElfW(Phdr)));
+ check_elf_phdr (&interp, phdr);
+ }
+ e.error = interp.error;
+ }
+
+ if (e.error)
+ goto out;
+
+
+ /* We are now committed to the exec. It "should not fail".
+ If it does fail now, the task will be hopelessly munged. */
+
+ if (newtask == oldtask)
+ {
+ thread_t *threads;
+ mach_msg_type_number_t nthreads, i;
+
+ /* Terminate all the threads of the old task. */
+
+ e.error = task_threads (oldtask, &threads, &nthreads);
+ if (e.error)
+ goto out;
+ for (i = 0; i < nthreads; ++i)
+ {
+ thread_terminate (threads[i]);
+ mach_port_deallocate (mach_task_self (), threads[i]);
+ }
+ munmap ((caddr_t) threads, nthreads * sizeof (thread_t));
+
+ /* Deallocate the entire virtual address space of the task. */
+
+ vm_deallocate (oldtask,
+ VM_MIN_ADDRESS, VM_MAX_ADDRESS - VM_MIN_ADDRESS);
+
+ /* Nothing is supposed to go wrong any more. If anything does, the
+ old task is now in a hopeless state and must be killed. */
+ oldtask_trashed = 1;
+
+ /* Deallocate and destroy the ports requested by the caller.
+ These are ports the task wants not to lose if the exec call
+ fails, but wants removed from the new program task. */
+
+ for (i = 0; i < ndeallocnames; ++i)
+ mach_port_deallocate (oldtask, deallocnames[i]);
+
+ for (i = 0; i < ndestroynames; ++i)
+ mach_port_destroy (oldtask, destroynames[i]);
+ }
+
+/* XXX this should be below
+ it is here to work around a vm_map kernel bug. */
+ if (interp.file != MACH_PORT_NULL)
+ {
+ /* Load the interpreter file. */
+ load (newtask, &interp);
+ if (interp.error)
+ {
+ e.error = interp.error;
+ goto out;
+ }
+ finish (&interp, 1);
+ }
+
+
+ /* Load the file into the task. */
+ load (newtask, &e);
+ if (e.error)
+ goto out;
+
+ /* XXX loading of interp belongs here */
+
+ /* Clean up. */
+ finish (&e, 0);
+
+ /* Now record some essential addresses from the image itself that the
+ program's startup code will need to know. We do this after loading
+ the image so that a load-anywhere image gets the adjusted addresses. */
+ if (e.info.elf.phdr_addr != 0)
+ {
+ e.info.elf.phdr_addr += e.info.elf.loadbase;
+ boot->phdr_addr = e.info.elf.phdr_addr;
+ boot->phdr_size = e.info.elf.phnum * sizeof (ElfW(Phdr));
+ }
+ boot->user_entry = e.entry; /* already adjusted in `load' */
+
+ /* /hurd/exec is used to start /hurd/proc, so at this point there is
+ no proc server, so we need to be careful here. */
+ if (boot->portarray[INIT_PORT_PROC] != MACH_PORT_NULL)
+ {
+ /* Set the start_code and end_code values for this process. */
+ e.error = proc_set_code (boot->portarray[INIT_PORT_PROC],
+ e.start_code, e.end_code);
+ if (e.error)
+ goto out;
+
+ pid_t pid;
+ e.error = proc_task2pid (boot->portarray[INIT_PORT_PROC],
+ newtask, &pid);
+ if (e.error)
+ goto out;
+
+ char *name;
+ int size = asprintf (&name, "%s(%d)", argv, pid);
+ if (size > 0)
+ {
+/* This is an internal implementational detail of the gnumach kernel. */
+#define TASK_NAME_SIZE 32
+ if (size < TASK_NAME_SIZE)
+ task_set_name (newtask, name);
+ else
+ {
+ char *abbr = name + size - TASK_NAME_SIZE + 1;
+ abbr[0] = abbr[1] = abbr[2] = '.';
+ task_set_name (newtask, abbr);
+ }
+#undef TASK_NAME_SIZE
+ free (name);
+ }
+ }
+
+ /* Create the initial thread. */
+ e.error = thread_create (newtask, &thread);
+ if (e.error)
+ goto out;
+
+ /* Start up the initial thread at the entry point. */
+ boot->stack_base = 0, boot->stack_size = 0; /* Don't care about values. */
+ e.error = mach_setup_thread (newtask, thread,
+ (void *) (e.interp.section ? interp.entry :
+ e.entry),
+ &boot->stack_base, &boot->stack_size);
+ if (e.error)
+ goto out;
+
+ /* It would probably be better to change mach_setup_thread so
+ it does a vm_map with the right permissions to start with. */
+ if (!e.info.elf.execstack)
+ e.error = vm_protect (newtask, boot->stack_base, boot->stack_size,
+ 0, VM_PROT_READ | VM_PROT_WRITE);
+
+ if (oldtask != newtask && oldtask != MACH_PORT_NULL)
+ {
+ /* The program is on its way. The old task can be nuked. */
+ process_t proc;
+ process_t psrv;
+
+ /* Use the canonical proc server if secure, or there is none other.
+ When not secure, it is nice to let processes associate with
+ whatever proc server turns them on, regardless of which exec
+ itself is using. */
+ if (secure
+ || boot->nports <= INIT_PORT_PROC
+ || boot->portarray[INIT_PORT_PROC] == MACH_PORT_NULL)
+ psrv = procserver;
+ else
+ psrv = boot->portarray[INIT_PORT_PROC];
+
+ /* XXX there is a race here for SIGKILLing the process. -roland
+ I don't think it matters. -mib */
+ if (! proc_task2proc (psrv, oldtask, &proc))
+ {
+ proc_reassign (proc, newtask);
+ mach_port_deallocate (mach_task_self (), proc);
+ }
+ }
+
+ /* Make sure the proc server has the right idea of our identity. */
+ if (secure)
+ {
+ uid_t euidbuf[10], egidbuf[10], auidbuf[10], agidbuf[10];
+ uid_t *euids, *egids, *auids, *agids;
+ size_t neuids, negids, nauids, nagids;
+ error_t err;
+
+ /* Find out what our UID is from the auth server. */
+ neuids = negids = nauids = nagids = 10;
+ euids = euidbuf, egids = egidbuf;
+ auids = auidbuf, agids = agidbuf;
+ err = auth_getids (boot->portarray[INIT_PORT_AUTH],
+ &euids, &neuids, &auids, &nauids,
+ &egids, &negids, &agids, &nagids);
+
+ if (!err)
+ {
+ /* Set the owner with the proc server */
+ /* Not much we can do about errors here; caller is responsible
+ for making sure that the provided proc port is correctly
+ authenticated anyhow. */
+ proc_setowner (boot->portarray[INIT_PORT_PROC],
+ neuids ? euids[0] : 0, !neuids);
+
+ /* Clean up */
+ if (euids != euidbuf)
+ munmap (euids, neuids * sizeof (uid_t));
+ if (egids != egidbuf)
+ munmap (egids, negids * sizeof (uid_t));
+ if (auids != auidbuf)
+ munmap (auids, nauids * sizeof (uid_t));
+ if (agids != agidbuf)
+ munmap (agids, nagids * sizeof (uid_t));
+ }
+ }
+
+ {
+ mach_port_t btport = ports_get_send_right (boot);
+ e.error = task_set_bootstrap_port (newtask, btport);
+ mach_port_deallocate (mach_task_self (), btport);
+ }
+
+ out:
+ if (interp.file != MACH_PORT_NULL)
+ finish (&interp, 1);
+ finish (&e, !e.error);
+
+ if (!e.error && (flags & EXEC_SIGTRAP)) /* XXX && !secure ? */
+ {
+ /* This is a "traced" exec, i.e. the new task is to be debugged. The
+ caller has requested that the new process stop with SIGTRAP before
+ it starts. Since the process has no signal thread yet to do its
+ own POSIX signal mechanics, we simulate it by notifying the proc
+ server of the signal and leaving the initial thread with a suspend
+ count of one, as it would be if the process were stopped by a
+ POSIX signal. */
+ mach_port_t proc;
+ if (boot->nports > INIT_PORT_PROC)
+ proc = boot->portarray[INIT_PORT_PROC];
+ else
+ /* Ask the proc server for the proc port for this task. */
+ e.error = proc_task2proc (procserver, newtask, &proc);
+ if (!e.error)
+ /* Tell the proc server that the process has stopped with the
+ SIGTRAP signal. Don't bother to check for errors from the RPC
+ here; for non-secure execs PROC may be the user's own proc
+ server its confusion shouldn't make the exec fail. */
+ proc_mark_stop (proc, SIGTRAP, 0);
+ }
+
+ if (boot)
+ {
+ /* Release the original reference. Now there is only one
+ reference, which will be released on no-senders notification.
+ If we are bailing out due to error before setting the task's
+ bootstrap port, this will be the last reference and BOOT
+ will get cleaned up here. */
+
+ if (e.error)
+ /* Kill the pointers to the argument information so the cleanup
+ of BOOT doesn't deallocate it. It will be deallocated my MiG
+ when we return the error. */
+ bzero (&boot->pi + 1, (char *) &boot[1] - (char *) (&boot->pi + 1));
+ else
+ /* Do this before we release the last reference. */
+ if (boot->nports > INIT_PORT_PROC)
+ proc_mark_exec (boot->portarray[INIT_PORT_PROC]);
+
+ ports_port_deref (boot);
+ }
+
+ if (thread != MACH_PORT_NULL)
+ {
+ if (!e.error && !(flags & EXEC_SIGTRAP))
+ thread_resume (thread);
+ mach_port_deallocate (mach_task_self (), thread);
+ }
+
+ if (e.error)
+ {
+ if (oldtask != newtask)
+ {
+ /* We created a new task but failed to set it up. Kill it. */
+ task_terminate (newtask);
+ mach_port_deallocate (mach_task_self (), newtask);
+ }
+ if (oldtask_trashed)
+ /* The old task is hopelessly trashed; there is no way it
+ can resume execution. Coup de grace. */
+ task_terminate (oldtask);
+ else
+ /* Resume the old task, which we suspended earlier. */
+ task_resume (oldtask);
+ }
+ else
+ {
+ if (oldtask != newtask)
+ {
+ /* We successfully set the new task up.
+ Terminate the old task and deallocate our right to it. */
+ task_terminate (oldtask);
+ mach_port_deallocate (mach_task_self (), oldtask);
+ }
+ else
+ /* Resume the task, it is ready to run the new program. */
+ task_resume (oldtask);
+ /* Deallocate the right to the new task we created. */
+ mach_port_deallocate (mach_task_self (), newtask);
+
+ for (i = 0; i < nports; ++i)
+ if (ports_replaced[i] && portarray[i] != MACH_PORT_NULL)
+ /* This port was replaced, so the reference that arrived in the
+ original portarray is not being saved in BOOT for transfer to
+ the user task. Deallocate it; we don't want it, and MiG will
+ leave it for us on successful return. */
+ mach_port_deallocate (mach_task_self (), portarray[i]);
+
+ /* If there is vm_allocate'd space for the original intarray and/or
+ portarray, and we are not saving those pointers in BOOT for later
+ transfer, deallocate the original space now. */
+ if (intarray_dealloc)
+ munmap (intarray, nints * sizeof intarray[0]);
+ if (!portarray_copy)
+ munmap (portarray, nports * sizeof portarray[0]);
+ }
+
+ return e.error;
+}
+
+kern_return_t
+S_exec_exec (struct trivfs_protid *protid,
+ file_t file,
+ task_t oldtask,
+ int flags,
+ char *argv, mach_msg_type_number_t argvlen, boolean_t argv_copy,
+ char *envp, mach_msg_type_number_t envplen, boolean_t envp_copy,
+ mach_port_t *dtable, mach_msg_type_number_t dtablesize,
+ boolean_t dtable_copy,
+ mach_port_t *portarray, mach_msg_type_number_t nports,
+ boolean_t portarray_copy,
+ int *intarray, mach_msg_type_number_t nints,
+ boolean_t intarray_copy,
+ mach_port_t *deallocnames, mach_msg_type_number_t ndeallocnames,
+ mach_port_t *destroynames, mach_msg_type_number_t ndestroynames)
+{
+ if (! protid)
+ return EOPNOTSUPP;
+
+ /* There were no user-specified exec servers,
+ or none of them could be found. */
+
+ return do_exec (file, oldtask, flags,
+ argv, argvlen, argv_copy,
+ envp, envplen, envp_copy,
+ dtable, dtablesize, dtable_copy,
+ portarray, nports, portarray_copy,
+ intarray, nints, intarray_copy,
+ deallocnames, ndeallocnames,
+ destroynames, ndestroynames);
+}
+
+kern_return_t
+S_exec_setexecdata (struct trivfs_protid *protid,
+ mach_port_t *ports, mach_msg_type_number_t nports, int ports_copy,
+ int *ints, mach_msg_type_number_t nints, int ints_copy)
+{
+ error_t err;
+
+ if (! protid || (protid->realnode != MACH_PORT_NULL && ! protid->isroot))
+ return EPERM;
+
+ if (nports < INIT_PORT_MAX || nints < INIT_INT_MAX)
+ return EINVAL; /* */
+
+ err = 0;
+ ports = servercopy (ports, nports * sizeof (mach_port_t), ports_copy, &err);
+ if (err)
+ return err;
+ ints = servercopy (ints, nints * sizeof (int), ints_copy, &err);
+ if (err)
+ {
+ munmap (ports, nports * sizeof (mach_port_t));
+ return err;
+ }
+
+ pthread_rwlock_wrlock (&std_lock);
+
+ if (std_ports)
+ {
+ mach_msg_type_number_t i;
+ for (i = 0; i < std_nports; ++i)
+ mach_port_deallocate (mach_task_self (), std_ports[i]);
+ munmap (std_ports, std_nports * sizeof (mach_port_t));
+ }
+
+ std_ports = ports;
+ std_nports = nports;
+
+ if (std_ints)
+ munmap (std_ints, std_nints * sizeof (int));
+
+ std_ints = ints;
+ std_nints = nints;
+
+ pthread_rwlock_unlock (&std_lock);
+
+ return 0;
+}
+
+
+#include "exec_startup_S.h"
+
+/* RPC sent on the bootstrap port. */
+
+kern_return_t
+S_exec_startup_get_info (struct bootinfo *boot,
+ vm_address_t *user_entry,
+ vm_address_t *phdr_data, vm_size_t *phdr_size,
+ vm_address_t *stack_base, vm_size_t *stack_size,
+ int *flags,
+ char **argvp, mach_msg_type_number_t *argvlen,
+ char **envpp, mach_msg_type_number_t *envplen,
+ mach_port_t **dtable,
+ mach_msg_type_name_t *dtablepoly,
+ mach_msg_type_number_t *dtablesize,
+ mach_port_t **portarray,
+ mach_msg_type_name_t *portpoly,
+ mach_msg_type_number_t *nports,
+ int **intarray, mach_msg_type_number_t *nints)
+{
+ if (! boot)
+ return EOPNOTSUPP;
+
+ /* Pass back all the information we are storing. */
+
+ *user_entry = boot->user_entry;
+ *phdr_data = boot->phdr_addr;
+ *phdr_size = boot->phdr_size;
+ *stack_base = boot->stack_base;
+ *stack_size = boot->stack_size;
+
+ *argvp = boot->argv;
+ *argvlen = boot->argvlen;
+ boot->argvlen = 0;
+
+ *envpp = boot->envp;
+ *envplen = boot->envplen;
+ boot->envplen = 0;
+
+ *dtable = boot->dtable;
+ *dtablesize = boot->dtablesize;
+ *dtablepoly = MACH_MSG_TYPE_MOVE_SEND;
+ boot->dtablesize = 0;
+
+ *intarray = boot->intarray;
+ *nints = boot->nints;
+ boot->nints = 0;
+
+ *portarray = boot->portarray;
+ *nports = boot->nports;
+ *portpoly = MACH_MSG_TYPE_MOVE_SEND;
+ boot->nports = 0;
+
+ *flags = boot->flags;
+
+ return 0;
+}
diff --git a/exec/execmutations.h b/exec/execmutations.h
new file mode 100644
index 00000000..2acca7a0
--- /dev/null
+++ b/exec/execmutations.h
@@ -0,0 +1,18 @@
+/* CPP definitions for MiG processing of exec.defs for exec server. */
+
+#define FILE_INTRAN trivfs_protid_t trivfs_begin_using_protid (file_t)
+#define FILE_DESTRUCTOR trivfs_end_using_protid (trivfs_protid_t)
+
+#define EXEC_IMPORTS \
+ import "priv.h"; \
+ import "../libtrivfs/mig-decls.h"; \
+
+#define EXEC_STARTUP_INTRAN \
+ bootinfo_t begin_using_bootinfo_port (exec_startup_t)
+#define EXEC_STARTUP_DESTRUCTOR \
+ end_using_bootinfo (bootinfo_t)
+#define EXEC_STARTUP_IMPORTS \
+ import "priv.h"; \
+ import "mig-decls.h";
+
+#define SERVERCOPY 1
diff --git a/exec/hashexec.c b/exec/hashexec.c
new file mode 100644
index 00000000..5641218c
--- /dev/null
+++ b/exec/hashexec.c
@@ -0,0 +1,456 @@
+/* GNU Hurd standard exec server, #! script execution support.
+ Copyright (C) 1995,96,97,98,99,2000,02 Free Software Foundation, Inc.
+ Written by Roland McGrath.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <hurd/sigpreempt.h>
+#include <unistd.h>
+#include <envz.h>
+#include <sys/param.h>
+
+/* This is called to check E for a #! interpreter specification. E has
+ already been prepared (successfully) and checked (unsuccessfully). If
+ we return success, our caller just returns success for the RPC; we must
+ handle all the RPC argument details ourselves. If we return ENOEXEC, we
+ should leave everything as it was. If we return failure other than
+ ENOEXEC, our caller will just fail the RPC. */
+void
+check_hashbang (struct execdata *e,
+ file_t file,
+ task_t oldtask,
+ int flags,
+ char *argv, u_int argvlen, boolean_t argv_copy,
+ char *envp, u_int envplen, boolean_t envp_copy,
+ mach_port_t *dtable, u_int dtablesize, boolean_t dtable_copy,
+ mach_port_t *portarray, u_int nports, boolean_t portarray_copy,
+ int *intarray, u_int nints, boolean_t intarray_copy,
+ mach_port_t *deallocnames, u_int ndeallocnames,
+ mach_port_t *destroynames, u_int ndestroynames)
+{
+ char *p;
+ char *interp, *arg; /* Interpreter file name, and first argument */
+ size_t interp_len, arg_len;
+ file_t interp_file; /* Port open on the interpreter file. */
+ char *new_argv;
+ size_t new_argvlen;
+ mach_port_t *new_dtable = NULL;
+ u_int new_dtablesize;
+
+ file_t user_fd (int fd)
+ {
+ if (fd >= 0 && fd < dtablesize)
+ {
+ const file_t dport = dtable[fd];
+ if (dport != MACH_PORT_NULL)
+ {
+ mach_port_mod_refs (mach_task_self (), dport,
+ MACH_PORT_RIGHT_SEND, +1);
+ return dport;
+ }
+ }
+ errno = EBADF;
+ return MACH_PORT_NULL;
+ }
+ file_t user_crdir, user_cwdir;
+ error_t user_port (int which, error_t (*operate) (mach_port_t))
+ {
+ error_t reauthenticate (file_t unauth, file_t *result)
+ {
+ error_t err;
+ mach_port_t ref;
+
+ /* MAKE_SEND is safe here because we destroy REF ourselves. */
+
+ error_t uauth (auth_t auth)
+ {
+ return auth_user_authenticate (auth,
+ ref, MACH_MSG_TYPE_MAKE_SEND,
+ result);
+ }
+ if (*result != MACH_PORT_NULL)
+ return 0;
+ ref = mach_reply_port ();
+ err = io_reauthenticate (unauth, ref, MACH_MSG_TYPE_MAKE_SEND);
+ if (!err)
+ err = user_port (INIT_PORT_AUTH, &uauth);
+ mach_port_destroy (mach_task_self (), ref);
+ return err;
+ }
+
+ /* Find the specified port, using defaults if so specified. */
+ mach_port_t port = ((which < nports &&
+ portarray[which] != MACH_PORT_NULL)
+ ? portarray[which] :
+ (flags & EXEC_DEFAULTS) ? std_ports[which]
+ : MACH_PORT_NULL);
+
+ /* Reauthenticate dir ports if they are the defaults. */
+ switch (which)
+ {
+ case INIT_PORT_CRDIR:
+ /* If secure, always use the default root. */
+ if ((flags & EXEC_SECURE) ||
+ port == std_ports[which])
+ return (reauthenticate (std_ports[which], &user_crdir) ?:
+ (*operate) (user_crdir));
+ break;
+ case INIT_PORT_CWDIR:
+ /* If secure, reauthenticate cwd whether default or given. */
+ if ((flags & EXEC_SECURE) || port == std_ports[which])
+ return (reauthenticate (port, &user_cwdir) ?:
+ (*operate) (user_cwdir));
+ break;
+ }
+
+ return (*operate) (port);
+ }
+ /* Look up NAME on behalf of the client. */
+ inline error_t lookup (const char *name, int flags, mach_port_t *result)
+ {
+ return hurd_file_name_lookup (&user_port, &user_fd, 0,
+ name, flags, 0, result);
+ }
+
+ const char *page;
+ char interp_buf[vm_page_size - 2 + 1];
+
+ e->error = 0;
+ page = map (e, 0, 2);
+
+ if (!page)
+ {
+ if (!e->error)
+ e->error = ENOEXEC;
+ return;
+ }
+
+ /* Check for our ``magic number''--"#!". */
+ if (page[0] != '#' || page[1] != '!')
+ {
+ /* These are not the droids we're looking for. */
+ e->error = ENOEXEC;
+ return;
+ }
+
+ /* Read the rest of the first line of the file.
+ We in fact impose an arbitrary limit of about a page on this. */
+
+ p = memccpy (interp_buf, page + 2, '\n',
+ MIN (map_fsize (e) - 2, sizeof interp_buf));
+ if (p == NULL)
+ {
+ /* The first line went on for more than sizeof INTERP_BUF! */
+ interp_len = sizeof interp_buf;
+ interp_buf[interp_len - 1] = '\0';
+ }
+ else
+ {
+ interp_len = p - interp_buf; /* Includes null terminator. */
+ *--p = '\0'; /* Kill the newline. */
+ }
+
+ /* We are now done reading the script file. */
+ finish (e, 0);
+
+
+ /* Find the name of the interpreter. */
+ interp = interp_buf + strspn (interp_buf, " \t");
+ p = strpbrk (interp, " \t");
+
+ if (p)
+ {
+ /* Terminate the interpreter name. */
+ *p++ = '\0';
+
+ /* Skip remaining blanks, and the rest of the line is the argument. */
+
+ arg = p + strspn (p, " \t");
+ arg_len = interp_len - 1 - (arg - interp_buf); /* without null here */
+ interp_len = p - interp; /* This one includes the null. */
+
+ if (arg_len == 0)
+ arg = NULL;
+ else
+ {
+ /* Trim trailing blanks after the argument. */
+ size_t i = arg_len - 1;
+ while (arg[i] == ' ' || arg[i] == '\t')
+ arg[i--] = '\0';
+ arg_len = i + 2; /* Include the terminating null. */
+ }
+ }
+ else
+ {
+ /* There is no argument. */
+ arg = NULL;
+ arg_len = 0;
+ interp_len -= interp - interp_buf; /* Account for blanks skipped. */
+ }
+
+ user_crdir = user_cwdir = MACH_PORT_NULL;
+
+ pthread_rwlock_rdlock (&std_lock);
+
+ /* Open a port on the interpreter file. */
+ e->error = lookup (interp, O_EXEC, &interp_file);
+
+ if (! e->error)
+ {
+ int free_file_name = 0; /* True if we should free FILE_NAME. */
+ jmp_buf args_faulted;
+ void fault_handler (int signo)
+ { longjmp (args_faulted, 1); }
+ error_t setup_args (struct hurd_signal_preemptor *preemptor)
+ {
+ size_t namelen;
+ char * volatile file_name = NULL;
+
+ if (setjmp (args_faulted))
+ file_name = NULL;
+ else if (! (flags & EXEC_SECURE))
+ {
+ /* Try to figure out the file's name. We guess that if ARGV[0]
+ contains a slash, it might be the name of the file; and that
+ if it contains no slash, looking for files named by ARGV[0] in
+ the `PATH' environment variable might find it. */
+
+ error_t error;
+ char *name;
+ int free_name = 0; /* True if we should free NAME. */
+ file_t name_file;
+ mach_port_t fileid, filefsid;
+ ino_t fileno;
+
+ /* Search $PATH for NAME, opening a port NAME_FILE on it.
+ This is encapsulated in a function so we can catch faults
+ reading the user's environment. */
+ error_t search_path (struct hurd_signal_preemptor *preemptor)
+ {
+ error_t err;
+ char *path = envz_get (envp, envplen, "PATH"), *pfxed_name;
+
+ if (! path)
+ {
+ const size_t len = confstr (_CS_PATH, NULL, 0);
+ path = alloca (len);
+ confstr (_CS_PATH, path, len);
+ }
+
+ err = hurd_file_name_path_lookup (user_port, user_fd, 0,
+ name, path, O_EXEC, 0,
+ &name_file, &pfxed_name);
+ if (!err && pfxed_name)
+ {
+ name = pfxed_name;
+ free_name = 1;
+ }
+
+ return err;
+ }
+
+ error = io_identity (file, &fileid, &filefsid, &fileno);
+ if (error)
+ goto out;
+ mach_port_deallocate (mach_task_self (), filefsid);
+
+ if (memchr (argv, '\0', argvlen) == NULL)
+ {
+ name = alloca (argvlen + 1);
+ bcopy (argv, name, argvlen);
+ name[argvlen] = '\0';
+ }
+ else
+ name = argv;
+
+ if (strchr (name, '/') != NULL)
+ error = lookup (name, 0, &name_file);
+ else if ((error = hurd_catch_signal
+ (sigmask (SIGBUS) | sigmask (SIGSEGV),
+ (vm_address_t) envp, (vm_address_t) envp + envplen,
+ &search_path, SIG_ERR)))
+ name_file = MACH_PORT_NULL;
+
+ if (!error && name_file != MACH_PORT_NULL)
+ {
+ mach_port_t id, fsid;
+ ino_t ino;
+ error = io_identity (name_file, &id, &fsid, &ino);
+ mach_port_deallocate (mach_task_self (), name_file);
+ if (!error)
+ {
+ mach_port_deallocate (mach_task_self (), fsid);
+ mach_port_deallocate (mach_task_self (), id);
+ }
+ if (!error && id == fileid)
+ {
+ file_name = name;
+ free_file_name = free_name;
+ }
+ else if (free_name)
+ free (name);
+ }
+
+ mach_port_deallocate (mach_task_self (), fileid);
+ }
+
+ if (file_name == NULL)
+ {
+ /* We can't easily find the file.
+ Put it in a file descriptor and pass /dev/fd/N. */
+ int fd;
+ out:
+
+ for (fd = 0; fd < dtablesize; ++fd)
+ if (dtable[fd] == MACH_PORT_NULL)
+ break;
+ if (fd == dtablesize)
+ {
+ /* Extend the descriptor table. */
+ new_dtable = alloca ((dtablesize + 1) * sizeof (file_t));
+ memcpy (new_dtable, dtable, dtablesize * sizeof (file_t));
+ new_dtablesize = dtablesize + 1;
+ new_dtable[fd] = file;
+ }
+ else
+ dtable[fd] = file;
+ mach_port_mod_refs (mach_task_self (), file,
+ MACH_PORT_RIGHT_SEND, +1);
+
+ file_name = alloca (100);
+ sprintf (file_name, "/dev/fd/%d", fd);
+ }
+
+ /* Prepare the arguments to pass to the interpreter from the original
+ arguments and the name of the script file. The args will look
+ like `INTERP {ARG} FILE_NAME ARGV[1..n]' (ARG might have been
+ omitted). */
+
+ namelen = strlen (file_name) + 1;
+
+ new_argvlen
+ = (argvlen - strlen (argv) - 1) /* existing args - old argv[0] */
+ + interp_len + arg_len + namelen; /* New args */
+
+ new_argv = mmap (0, new_argvlen, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ if (new_argv == (caddr_t) -1)
+ {
+ e->error = errno;
+ return e->error;
+ }
+ else
+ e->error = 0;
+
+ if (! setjmp (args_faulted))
+ {
+ char *other_args;
+
+ p = new_argv;
+
+ /* INTERP */
+ memcpy (p, interp, interp_len);
+ p += interp_len;
+
+ /* Maybe ARG */
+ if (arg)
+ {
+ memcpy (p, arg, arg_len);
+ p += arg_len;
+ }
+
+ /* FILE_NAME */
+ memcpy (p, file_name, namelen);
+ p += namelen;
+
+ /* Maybe remaining args */
+ other_args = argv + strlen (argv) + 1;
+ if (other_args - argv < argvlen)
+ memcpy (p, other_args, argvlen - (other_args - argv));
+ }
+ else
+ {
+ /* We got a fault reading ARGV. So don't use it. */
+ char *n = stpncpy (new_argv,
+ "**fault in exec server reading argv[0]**",
+ argvlen);
+ memcpy (memcpy (n, arg, arg_len) + arg_len, file_name, namelen);
+ }
+
+ if (free_file_name)
+ free (file_name);
+
+ return 0;
+ }
+
+ /* Set up the arguments. */
+ hurd_catch_signal (sigmask (SIGSEGV) | sigmask (SIGBUS),
+ (vm_address_t) argv, (vm_address_t) argv + argvlen,
+ &setup_args, &fault_handler);
+ }
+
+ pthread_rwlock_unlock (&std_lock);
+
+ if (user_crdir != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), user_crdir);
+ if (user_cwdir != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), user_cwdir);
+
+ if (e->error)
+ /* We cannot open the interpreter file to execute it. Lose! */
+ return;
+
+ /* Execute the interpreter program. */
+ e->error = file_exec (interp_file,
+ oldtask, flags,
+ new_argv, new_argvlen, envp, envplen,
+ new_dtable ?: dtable, MACH_MSG_TYPE_COPY_SEND,
+ new_dtable ? new_dtablesize : dtablesize,
+ portarray, MACH_MSG_TYPE_COPY_SEND, nports,
+ intarray, nints,
+ deallocnames, ndeallocnames,
+ destroynames, ndestroynames);
+ mach_port_deallocate (mach_task_self (), interp_file);
+ munmap (new_argv, new_argvlen);
+
+ if (! e->error)
+ {
+ /* The exec of the interpreter succeeded! Deallocate the resources
+ we passed to that exec. We don't need to save them in a bootinfo
+ structure; the exec of the interpreter takes care of that. */
+ u_int i;
+ mach_port_deallocate (mach_task_self (), file);
+ task_resume (oldtask); /* Our caller suspended it. */
+ mach_port_deallocate (mach_task_self (), oldtask);
+ if (! argv_copy)
+ munmap (argv, argvlen);
+ if (! envp_copy)
+ munmap (envp, envplen);
+ for (i = 0; i < dtablesize; ++i)
+ if (MACH_PORT_VALID (dtable[i]))
+ mach_port_deallocate (mach_task_self (), dtable[i]);
+ if (! dtable_copy)
+ munmap (dtable, dtablesize * sizeof *dtable);
+ for (i = 0; i < nports; ++i)
+ mach_port_deallocate (mach_task_self (), portarray[i]);
+ if (! portarray_copy)
+ munmap (portarray, nports * sizeof *portarray);
+ if (! intarray_copy)
+ munmap (intarray, nints * sizeof *intarray);
+ }
+}
diff --git a/exec/hostarch.c b/exec/hostarch.c
new file mode 100644
index 00000000..a3b93305
--- /dev/null
+++ b/exec/hostarch.c
@@ -0,0 +1,89 @@
+/* Determine the ELF architecture and machine flavor
+ from a Mach host port. Used by the exec and core servers.
+ Copyright (C) 1992,93,95,96,99,2000,02 Free Software Foundation, Inc.
+ Written by Roland McGrath.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <mach.h>
+#include <hurd/hurd_types.h>
+#include <errno.h>
+#include <elf.h>
+
+error_t
+elf_machine_matches_host (ElfW(Half) e_machine)
+{
+ static void *host_type; /* Cached entry into the switch below. */
+ struct host_basic_info hostinfo;
+
+ if (host_type)
+ goto *host_type;
+ else
+ {
+ error_t err;
+ mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
+
+ err = host_info (mach_host_self (), HOST_BASIC_INFO,
+ (host_info_t) &hostinfo, &hostinfocnt);
+ if (err)
+ return err;
+ assert (hostinfocnt == HOST_BASIC_INFO_COUNT);
+ }
+
+#define CACHE(test) ({ __label__ here; host_type = &&here; \
+ here: return (test) ? 0 : ENOEXEC; })
+ switch (hostinfo.cpu_type)
+ {
+ case CPU_TYPE_MC68020:
+ case CPU_TYPE_MC68030:
+ case CPU_TYPE_MC68040:
+ CACHE (e_machine == EM_68K);
+
+ case CPU_TYPE_I860:
+ CACHE (e_machine == EM_860);
+
+ case CPU_TYPE_MIPS:
+ CACHE (e_machine == EM_MIPS);
+
+ case CPU_TYPE_MC88000:
+ CACHE (e_machine == EM_88K);
+
+ case CPU_TYPE_SPARC:
+ CACHE (e_machine == EM_SPARC);
+
+ case CPU_TYPE_I386:
+ case CPU_TYPE_I486:
+ case CPU_TYPE_PENTIUM:
+ case CPU_TYPE_PENTIUMPRO:
+ CACHE (e_machine == EM_386);
+
+ case CPU_TYPE_POWERPC:
+ CACHE (e_machine == EM_PPC);
+
+ case CPU_TYPE_ALPHA:
+ CACHE (e_machine == EM_ALPHA);
+
+ case CPU_TYPE_HPPA:
+ CACHE (e_machine == EM_PARISC);
+
+ default:
+ return EGRATUITOUS; /* XXX */
+ }
+
+ return 0;
+}
diff --git a/exec/main.c b/exec/main.c
new file mode 100644
index 00000000..78faebd4
--- /dev/null
+++ b/exec/main.c
@@ -0,0 +1,268 @@
+/* GNU Hurd standard exec server, main program and server mechanics.
+
+ Copyright (C) 1992,93,94,95,96,97,98,99,2000,01,02,13
+ Free Software Foundation, Inc.
+ Written by Roland McGrath.
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <error.h>
+#include <device/device.h>
+#include <hurd/paths.h>
+#include <hurd/startup.h>
+#include <argp.h>
+#include <version.h>
+#include <pids.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (exec);
+
+/* Trivfs hooks. */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_allow_open = 0;
+
+struct port_class *trivfs_protid_portclasses[1];
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_protid_nportclasses = 1;
+int trivfs_cntl_nportclasses = 1;
+
+struct trivfs_control *fsys;
+
+char **save_argv;
+
+
+#include "exec_S.h"
+#include "exec_startup_S.h"
+
+static int
+exec_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ mig_routine_t routine;
+ if ((routine = exec_server_routine (inp)) ||
+ (routine = NULL, trivfs_demuxer (inp, outp)) ||
+ (routine = exec_startup_server_routine (inp)))
+ {
+ if (routine)
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+/* Clean up the storage in BOOT, which was never used. */
+
+void
+deadboot (void *p)
+{
+ struct bootinfo *boot = p;
+ size_t i;
+
+ munmap (boot->argv, boot->argvlen);
+ munmap (boot->envp, boot->envplen);
+
+ for (i = 0; i < boot->dtablesize; ++i)
+ mach_port_deallocate (mach_task_self (), boot->dtable[i]);
+ for (i = 0; i < boot->nports; ++i)
+ mach_port_deallocate (mach_task_self (), boot->portarray[i]);
+ munmap (boot->portarray, boot->nports * sizeof (mach_port_t));
+ munmap (boot->intarray, boot->nints * sizeof (int));
+
+ /* See if we are going away and this was the last thing keeping us up. */
+ if (ports_count_class (trivfs_cntl_portclasses[0]) == 0)
+ {
+ /* We have no fsys control port, so we are detached from the
+ parent filesystem. Maybe we have no users left either. */
+ if (ports_count_class (trivfs_protid_portclasses[0]) == 0)
+ {
+ /* We have no user ports left. Are we still listening for
+ exec_startup RPCs from any tasks we already started? */
+ if (ports_count_class (execboot_portclass) == 0)
+ /* Nobody talking. Time to die. */
+ exit (0);
+ ports_enable_class (execboot_portclass);
+ }
+ ports_enable_class (trivfs_protid_portclasses[0]);
+ }
+ ports_enable_class (trivfs_cntl_portclasses[0]);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct argp argp = { 0, 0, 0, "Hurd standard exec server." };
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ save_argv = argv;
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (2, 0, "Must be started as a translator");
+
+ /* Fetch our proc server port for easy use. If we are booting, it is not
+ set yet and `getproc' returns MACH_PORT_NULL; we reset PROCSERVER in
+ S_exec_init (below). */
+ procserver = getproc ();
+
+ port_bucket = ports_create_bucket ();
+ trivfs_cntl_portclasses[0] = ports_create_class (trivfs_clean_cntl, 0);
+ trivfs_protid_portclasses[0] = ports_create_class (trivfs_clean_protid, 0);
+ execboot_portclass = ports_create_class (deadboot, NULL);
+
+ /* Reply to our parent. */
+ err = trivfs_startup (bootstrap, 0,
+ trivfs_cntl_portclasses[0], port_bucket,
+ trivfs_protid_portclasses[0], port_bucket,
+ &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (3, err, "Contacting parent");
+
+ /* Launch. */
+ ports_manage_port_operations_multithread (port_bucket, exec_demuxer,
+ 2 * 60 * 1000, 0, 0);
+
+ return 0;
+}
+
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ st->st_fstype = FSTYPE_MISC;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ int count;
+
+ /* Stop new requests. */
+ ports_inhibit_class_rpcs (trivfs_cntl_portclasses[0]);
+ ports_inhibit_class_rpcs (trivfs_protid_portclasses[0]);
+
+ /* Are there any extant user ports for the /servers/exec file? */
+ count = ports_count_class (trivfs_protid_portclasses[0]);
+ if (count == 0 || (flags & FSYS_GOAWAY_FORCE))
+ {
+ /* No users. Disconnect from the filesystem. */
+ mach_port_deallocate (mach_task_self (), fsys->underlying);
+
+ /* Are there remaining exec_startup RPCs to answer? */
+ count = ports_count_class (execboot_portclass);
+ if (count == 0)
+ /* Nope. We got no reason to live. */
+ exit (0);
+
+ /* Continue servicing tasks starting up. */
+ ports_enable_class (execboot_portclass);
+
+ /* No more communication with the parent filesystem. */
+ ports_destroy_right (fsys);
+
+ return 0;
+ }
+ else
+ {
+ /* We won't go away, so start things going again... */
+ ports_enable_class (trivfs_protid_portclasses[0]);
+ ports_resume_class_rpcs (trivfs_cntl_portclasses[0]);
+ ports_resume_class_rpcs (trivfs_protid_portclasses[0]);
+
+ return EBUSY;
+ }
+}
+
+/* Sent by the bootstrap filesystem after the other essential
+ servers have been started up. */
+
+kern_return_t
+S_exec_init (struct trivfs_protid *protid,
+ auth_t auth, process_t proc)
+{
+ mach_port_t host_priv, device_master, startup;
+ error_t err;
+
+ if (! protid || ! protid->isroot)
+ return EPERM;
+
+ _hurd_port_set (&_hurd_ports[INIT_PORT_PROC], proc); /* Consume. */
+ _hurd_port_set (&_hurd_ports[INIT_PORT_AUTH], auth); /* Consume. */
+
+ /* Do initial setup with the proc server. */
+ _hurd_proc_init (save_argv, NULL, 0);
+
+ procserver = getproc ();
+
+ /* Have the proc server notify us when the canonical ints and ports
+ change. This will generate an immediate callback giving us the
+ initial boot-time canonical sets. */
+ {
+ struct iouser *user;
+ struct trivfs_protid *cred;
+ mach_port_t right;
+
+ err = iohelp_create_empty_iouser (&user);
+ assert_perror (err);
+ err = trivfs_open (fsys, user, 0, MACH_PORT_NULL, &cred);
+ assert_perror (err);
+
+ right = ports_get_send_right (cred);
+ proc_execdata_notify (procserver, right, MACH_MSG_TYPE_COPY_SEND);
+ mach_port_deallocate (mach_task_self (), right);
+ }
+
+ err = get_privileged_ports (&host_priv, &device_master);
+ assert_perror (err);
+
+ {
+ /* Get our stderr set up to print on the console, in case we have
+ to panic or something. */
+ mach_port_t cons;
+ error_t err;
+ err = device_open (device_master, D_READ|D_WRITE, "console", &cons);
+ assert_perror (err);
+ mach_port_deallocate (mach_task_self (), device_master);
+ stdin = mach_open_devstream (cons, "r");
+ stdout = stderr = mach_open_devstream (cons, "w");
+ mach_port_deallocate (mach_task_self (), cons);
+ }
+
+ proc_register_version (procserver, host_priv, "exec", "", HURD_VERSION);
+
+ err = proc_getmsgport (procserver, HURD_PID_STARTUP, &startup);
+ assert_perror (err);
+ mach_port_deallocate (mach_task_self (), procserver);
+
+ /* Call startup_essential task last; init assumes we are ready to
+ run once we call it. */
+ err = startup_essential_task (startup, mach_task_self (), MACH_PORT_NULL,
+ "exec", host_priv);
+ assert_perror (err);
+ mach_port_deallocate (mach_task_self (), startup);
+
+ mach_port_deallocate (mach_task_self (), host_priv);
+
+ return 0;
+}
diff --git a/exec/mig-decls.h b/exec/mig-decls.h
new file mode 100644
index 00000000..0437414f
--- /dev/null
+++ b/exec/mig-decls.h
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Justus Winter.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __EXEC_MIG_DECLS_H__
+#define __EXEC_MIG_DECLS_H__
+
+#include "priv.h"
+
+/* Called by server stub functions. */
+
+static inline struct bootinfo * __attribute__ ((unused))
+begin_using_bootinfo_port (mach_port_t port)
+{
+ return ports_lookup_port (port_bucket, port, execboot_portclass);
+}
+
+static inline void __attribute__ ((unused))
+end_using_bootinfo (struct bootinfo *b)
+{
+ if (b)
+ ports_port_deref (b);
+}
+
+#endif /* __EXEC_MIG_DECLS_H__ */
diff --git a/exec/priv.h b/exec/priv.h
new file mode 100644
index 00000000..733f35c0
--- /dev/null
+++ b/exec/priv.h
@@ -0,0 +1,155 @@
+/* GNU Hurd standard exec server, private declarations.
+ Copyright (C) 1992,93,94,95,96,99,2000,02, 04 Free Software Foundation, Inc.
+ Written by Roland McGrath.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <hurd/trivfs.h>
+#include <hurd/ports.h>
+#include <hurd/lookup.h>
+#include <pthread.h>
+
+#include <elf.h>
+#include <link.h> /* This gives us the ElfW macro. */
+#include <fcntl.h>
+#include "exec_S.h"
+
+
+#ifndef exec_priv_h
+#define exec_priv_h
+
+/* Information kept around to be given to a new task
+ in response to a message on the task's bootstrap port. */
+struct bootinfo
+ {
+ struct port_info pi;
+ vm_address_t stack_base;
+ vm_size_t stack_size;
+ int flags;
+ char *argv, *envp;
+ size_t argvlen, envplen, dtablesize, nports, nints;
+ mach_port_t *dtable, *portarray;
+ int *intarray;
+ vm_address_t phdr_addr, user_entry;
+ vm_size_t phdr_size;
+ };
+typedef struct bootinfo *bootinfo_t;
+
+
+/* Where to put the service ports. */
+struct port_bucket *port_bucket;
+struct port_class *execboot_portclass;
+
+extern mach_port_t procserver; /* Our proc port. */
+
+typedef void asection;
+
+/* Data shared between check, check_section,
+ load, load_section, and finish. */
+struct execdata
+ {
+ /* Passed out to caller. */
+ error_t error;
+
+ /* Set by check. */
+ vm_address_t entry;
+ file_t file;
+
+ /* Set by load_section. */
+ vm_address_t start_code;
+ vm_address_t end_code;
+
+ /* Note that if `file_data' (below) is set, then these just point
+ into that and should not be deallocated (file_data is malloc'd). */
+ char *map_buffer; /* Our mapping window or read buffer. */
+ size_t map_vsize; /* Page-aligned size allocated there. */
+ size_t map_fsize; /* Bytes from there to end of mapped data. */
+ off_t map_filepos; /* Position `map_buffer' maps to. */
+#define map_buffer(e) ((e)->map_buffer)
+#define map_fsize(e) ((e)->map_fsize)
+#define map_vsize(e) ((e)->map_vsize)
+#define map_filepos(e) ((e)->map_filepos)
+#define map_set_fsize(e, fsize) ((e)->map_fsize = (fsize))
+
+ union /* Interpreter section giving name of file. */
+ {
+ asection *section;
+ const ElfW(Phdr) *phdr;
+ } interp;
+ memory_object_t filemap, cntlmap;
+ struct shared_io *cntl;
+ char *file_data; /* File data if already copied in core. */
+ off_t file_size;
+ size_t optimal_block; /* Optimal size for io_read from file. */
+
+ /* Set by caller of load. */
+ task_t task;
+
+ union
+ {
+ struct
+ {
+ /* Program header table read from the executable.
+ After `check' this is a pointer into the mapping window.
+ By `load' it is local alloca'd storage. */
+ ElfW(Phdr) *phdr;
+ ElfW(Addr) phdr_addr;
+ ElfW(Word) phnum; /* Number of program header table elements. */
+ int anywhere; /* Nonzero if image can go anywhere. */
+ vm_address_t loadbase; /* Actual mapping location. */
+ int execstack; /* Zero if stack can be nonexecutable. */
+ } elf;
+ } info;
+ };
+
+error_t elf_machine_matches_host (ElfW(Half) e_machine);
+
+void finish (struct execdata *, int dealloc_file_port);
+
+/* Make sure our mapping window (or read buffer) covers
+ LEN bytes of the file starting at POSN, and return
+ a pointer into the window corresponding to POSN. */
+void *map (struct execdata *e, off_t posn, size_t len);
+
+
+void check_hashbang (struct execdata *e,
+ file_t file,
+ task_t oldtask,
+ int flags,
+ char *argv, u_int argvlen, boolean_t argv_copy,
+ char *envp, u_int envplen, boolean_t envp_copy,
+ mach_port_t *dtable, u_int dtablesize,
+ boolean_t dtable_copy,
+ mach_port_t *portarray, u_int nports,
+ boolean_t portarray_copy,
+ int *intarray, u_int nints, boolean_t intarray_copy,
+ mach_port_t *deallocnames, u_int ndeallocnames,
+ mach_port_t *destroynames, u_int ndestroynames);
+
+
+/* Standard exec data for secure execs. */
+extern mach_port_t *std_ports;
+extern int *std_ints;
+extern size_t std_nports, std_nints;
+extern pthread_rwlock_t std_lock;
+
+#endif /* exec_priv_h */
diff --git a/ext2fs/Makefile b/ext2fs/Makefile
new file mode 100644
index 00000000..8d2e68c7
--- /dev/null
+++ b/ext2fs/Makefile
@@ -0,0 +1,31 @@
+# Makefile for ext2fs
+#
+# Copyright (C) 1994,95,96,99,2000,02,12 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := ext2fs
+makemode := server
+
+target = ext2fs
+SRCS = balloc.c dir.c ext2fs.c getblk.c hyper.c ialloc.c \
+ inode.c pager.c pokel.c truncate.c storeinfo.c msg.c xinl.c
+OBJS = $(SRCS:.c=.o)
+HURDLIBS = diskfs pager iohelp fshelp store ports ihash shouldbeinlibc
+OTHERLIBS = -lpthread $(and $(HAVE_LIBBZ2),-lbz2) $(and $(HAVE_LIBZ),-lz)
+
+include ../Makeconf
+
+ext2fs.static: $(boot-store-types:%=../libstore/libstore_%.a)
diff --git a/ext2fs/balloc.c b/ext2fs/balloc.c
new file mode 100644
index 00000000..efef8ae4
--- /dev/null
+++ b/ext2fs/balloc.c
@@ -0,0 +1,512 @@
+/* Block allocation routines
+
+ Copyright (C) 1995,99,2000 Free Software Foundation, Inc.
+
+ Converted to work under the hurd by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * linux/fs/ext2/balloc.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * Enhanced block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
+ */
+
+/*
+ * The free blocks are managed by bitmaps. A file system contains several
+ * blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap
+ * block for inodes, N blocks for the inode table and data blocks.
+ *
+ * The file system contains group descriptors which are located after the
+ * super block. Each descriptor contains the number of the bitmap block and
+ * the free blocks count in the block. The descriptors are loaded in memory
+ * when a file system is mounted (see ext2_read_super).
+ */
+
+#include <string.h>
+#include "ext2fs.h"
+#include "bitmap.c"
+
+/* Returns a pointer to the first occurrence of CH in the buffer BUF of len
+ LEN, or BUF + LEN if CH doesn't occur. */
+static inline void *
+memscan (void *buf, unsigned char ch, size_t len)
+{
+ return memchr (buf, ch, len) ?: buf + len;
+}
+
+#define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1)
+
+void
+ext2_free_blocks (block_t block, unsigned long count)
+{
+ char *bh;
+ unsigned long block_group;
+ unsigned long bit;
+ unsigned long i;
+ struct ext2_group_desc *gdp;
+
+ pthread_spin_lock (&global_lock);
+
+ if (block < sblock->s_first_data_block ||
+ (block + count) > sblock->s_blocks_count)
+ {
+ ext2_error ("freeing blocks not in datazone - "
+ "block = %u, count = %lu", block, count);
+ pthread_spin_unlock (&global_lock);
+ return;
+ }
+
+ ext2_debug ("freeing block %u[%lu]", block, count);
+
+ do
+ {
+ unsigned long int gcount = count;
+
+ block_group = ((block - sblock->s_first_data_block)
+ / sblock->s_blocks_per_group);
+ bit = (block - sblock->s_first_data_block) % sblock->s_blocks_per_group;
+ if (bit + count > sblock->s_blocks_per_group)
+ {
+ unsigned long overflow = bit + count - sblock->s_blocks_per_group;
+ gcount -= overflow;
+ ext2_debug ("freeing blocks across group boundary - "
+ "block = %u, count = %lu",
+ block, count);
+ }
+ gdp = group_desc (block_group);
+ bh = disk_cache_block_ref (gdp->bg_block_bitmap);
+
+ if (in_range (gdp->bg_block_bitmap, block, gcount) ||
+ in_range (gdp->bg_inode_bitmap, block, gcount) ||
+ in_range (block, gdp->bg_inode_table, itb_per_group) ||
+ in_range (block + gcount - 1, gdp->bg_inode_table, itb_per_group))
+ ext2_panic ("freeing blocks in system zones - "
+ "block = %u, count = %lu",
+ block, count);
+
+ for (i = 0; i < gcount; i++)
+ {
+ if (!clear_bit (bit + i, bh))
+ ext2_warning ("bit already cleared for block %lu", block + i);
+ else
+ {
+ gdp->bg_free_blocks_count++;
+ sblock->s_free_blocks_count++;
+ }
+ }
+
+ record_global_poke (bh);
+ disk_cache_block_ref_ptr (gdp);
+ record_global_poke (gdp);
+
+ block += gcount;
+ count -= gcount;
+ } while (count > 0);
+
+ sblock_dirty = 1;
+
+ pthread_spin_unlock (&global_lock);
+
+ alloc_sync (0);
+}
+
+/*
+ * ext2_new_block uses a goal block to assist allocation. If the goal is
+ * free, or there is a free block within 32 blocks of the goal, that block
+ * is allocated. Otherwise a forward search is made for a free block; within
+ * each block group the search first looks for an entire free byte in the block
+ * bitmap, and then for any free bit if that fails.
+ */
+block_t
+ext2_new_block (block_t goal,
+ block_t prealloc_goal,
+ block_t *prealloc_count, block_t *prealloc_block)
+{
+ char *bh = NULL;
+ char *p, *r;
+ int i, j, k, tmp;
+ unsigned long lmap;
+ struct ext2_group_desc *gdp;
+
+#ifdef EXT2FS_DEBUG
+ static int goal_hits = 0, goal_attempts = 0;
+#endif
+
+ pthread_spin_lock (&global_lock);
+
+#ifdef XXX /* Auth check to use reserved blocks */
+ if (sblock->s_free_blocks_count <= sblock->s_r_blocks_count &&
+ (!fsuser () && (sb->u.ext2_sb.s_resuid != current->fsuid) &&
+ (sb->u.ext2_sb.s_resgid == 0 ||
+ !in_group_p (sb->u.ext2_sb.s_resgid))))
+ {
+ pthread_spin_unlock (&global_lock);
+ return 0;
+ }
+#endif
+
+ ext2_debug ("goal=%u", goal);
+
+repeat:
+ assert (bh == NULL);
+ /*
+ * First, test whether the goal block is free.
+ */
+ if (goal < sblock->s_first_data_block || goal >= sblock->s_blocks_count)
+ goal = sblock->s_first_data_block;
+ i = (goal - sblock->s_first_data_block) / sblock->s_blocks_per_group;
+ gdp = group_desc (i);
+ if (gdp->bg_free_blocks_count > 0)
+ {
+ j = ((goal - sblock->s_first_data_block) % sblock->s_blocks_per_group);
+#ifdef EXT2FS_DEBUG
+ if (j)
+ goal_attempts++;
+#endif
+ bh = disk_cache_block_ref (gdp->bg_block_bitmap);
+
+ ext2_debug ("goal is at %d:%d", i, j);
+
+ if (!test_bit (j, bh))
+ {
+#ifdef EXT2FS_DEBUG
+ goal_hits++;
+ ext2_debug ("goal bit allocated!");
+#endif
+ goto got_block;
+ }
+ if (j)
+ {
+ /*
+ * The goal was occupied; search forward for a free
+ * block within the next 32 blocks
+ */
+ if ((j & 31) == 31)
+ lmap = 0;
+ else
+ lmap = ((((unsigned long *) bh)[j >> 5]) >>
+ ((j & 31) + 1));
+ if (j < sblock->s_blocks_per_group - 32)
+ lmap |= (((unsigned long *) bh)[(j >> 5) + 1]) <<
+ (31 - (j & 31));
+ else
+ lmap |= 0xffffffff << (31 - (j & 31));
+ if (lmap != 0xffffffffl)
+ {
+ k = ffz (lmap) + 1;
+ if ((j + k) < sblock->s_blocks_per_group)
+ {
+ j += k;
+ goto got_block;
+ }
+ }
+ }
+
+ ext2_debug ("bit not found near goal");
+
+ /*
+ * There has been no free block found in the near vicinity
+ * of the goal: do a search forward through the block groups,
+ * searching in each group first for an entire free byte in
+ * the bitmap and then for any free bit.
+ *
+ * Search first in the remainder of the current group; then,
+ * cyclicly search through the rest of the groups.
+ */
+ p = ((char *) bh) + (j >> 3);
+ r = memscan (p, 0, (sblock->s_blocks_per_group - j + 7) >> 3);
+ k = (r - ((char *) bh)) << 3;
+ if (k < sblock->s_blocks_per_group)
+ {
+ j = k;
+ goto search_back;
+ }
+ k = find_next_zero_bit ((unsigned long *) bh,
+ sblock->s_blocks_per_group,
+ j);
+ if (k < sblock->s_blocks_per_group)
+ {
+ j = k;
+ goto got_block;
+ }
+
+ disk_cache_block_deref (bh);
+ bh = NULL;
+ }
+
+ ext2_debug ("bit not found in block group %d", i);
+
+ /*
+ * Now search the rest of the groups. We assume that
+ * i and gdp correctly point to the last group visited.
+ */
+ for (k = 0; k < groups_count; k++)
+ {
+ i++;
+ if (i >= groups_count)
+ i = 0;
+ gdp = group_desc (i);
+ if (gdp->bg_free_blocks_count > 0)
+ break;
+ }
+ if (k >= groups_count)
+ {
+ pthread_spin_unlock (&global_lock);
+ return 0;
+ }
+ assert (bh == NULL);
+ bh = disk_cache_block_ref (gdp->bg_block_bitmap);
+ r = memscan (bh, 0, sblock->s_blocks_per_group >> 3);
+ j = (r - bh) << 3;
+ if (j < sblock->s_blocks_per_group)
+ goto search_back;
+ else
+ j = find_first_zero_bit ((unsigned long *) bh,
+ sblock->s_blocks_per_group);
+ if (j >= sblock->s_blocks_per_group)
+ {
+ disk_cache_block_deref (bh);
+ bh = NULL;
+ ext2_error ("free blocks count corrupted for block group %d", i);
+ pthread_spin_unlock (&global_lock);
+ return 0;
+ }
+
+search_back:
+ assert (bh != NULL);
+ /*
+ * We have succeeded in finding a free byte in the block
+ * bitmap. Now search backwards up to 7 bits to find the
+ * start of this group of free blocks.
+ */
+ for (k = 0; k < 7 && j > 0 && !test_bit (j - 1, bh); k++, j--);
+
+got_block:
+ assert (bh != NULL);
+
+ ext2_debug ("using block group %d (%d)", i, gdp->bg_free_blocks_count);
+
+ tmp = j + i * sblock->s_blocks_per_group + sblock->s_first_data_block;
+
+ if (tmp == gdp->bg_block_bitmap ||
+ tmp == gdp->bg_inode_bitmap ||
+ in_range (tmp, gdp->bg_inode_table, itb_per_group))
+ ext2_panic ("allocating block in system zone; block = %u", tmp);
+
+ if (set_bit (j, bh))
+ {
+ ext2_warning ("bit already set for block %d", j);
+ disk_cache_block_deref (bh);
+ bh = NULL;
+ goto repeat;
+ }
+
+ /* Since due to bletcherousness block-modified bits are never turned off
+ when writing disk-pager pages, make sure they are here, in case this
+ block is being allocated to a file (see pager.c). */
+ if (modified_global_blocks)
+ {
+ pthread_spin_lock (&modified_global_blocks_lock);
+ clear_bit (tmp, modified_global_blocks);
+ pthread_spin_unlock (&modified_global_blocks_lock);
+ }
+
+ ext2_debug ("found bit %d", j);
+
+ /*
+ * Do block preallocation now if required.
+ */
+#ifdef EXT2_PREALLOCATE
+ if (prealloc_goal)
+ {
+ *prealloc_count = 0;
+ *prealloc_block = tmp + 1;
+ for (k = 1;
+ k < prealloc_goal && (j + k) < sblock->s_blocks_per_group; k++)
+ {
+ if (set_bit (j + k, bh))
+ break;
+ (*prealloc_count)++;
+
+ /* (See comment before the clear_bit above) */
+ if (modified_global_blocks)
+ {
+ pthread_spin_lock (&modified_global_blocks_lock);
+ clear_bit (tmp + k, modified_global_blocks);
+ pthread_spin_unlock (&modified_global_blocks_lock);
+ }
+ }
+ gdp->bg_free_blocks_count -= *prealloc_count;
+ sblock->s_free_blocks_count -= *prealloc_count;
+ ext2_debug ("preallocated a further %u bits", *prealloc_count);
+ }
+#endif
+
+ j = tmp;
+
+ record_global_poke (bh);
+ bh = NULL;
+
+ if (j >= sblock->s_blocks_count)
+ {
+ ext2_error ("block >= blocks count - block_group = %d, block=%d", i, j);
+ j = 0;
+ goto sync_out;
+ }
+
+ ext2_debug ("allocating block %d; goal hits %d of %d",
+ j, goal_hits, goal_attempts);
+
+ gdp->bg_free_blocks_count--;
+ disk_cache_block_ref_ptr (gdp);
+ record_global_poke (gdp);
+
+ sblock->s_free_blocks_count--;
+ sblock_dirty = 1;
+
+ sync_out:
+ assert (bh == NULL);
+ pthread_spin_unlock (&global_lock);
+ alloc_sync (0);
+
+ return j;
+}
+
+unsigned long
+ext2_count_free_blocks ()
+{
+#ifdef EXT2FS_DEBUG
+ unsigned long desc_count, bitmap_count, x;
+ struct ext2_group_desc *gdp;
+ int i;
+
+ pthread_spin_lock (&global_lock);
+
+ desc_count = 0;
+ bitmap_count = 0;
+ gdp = NULL;
+ for (i = 0; i < groups_count; i++)
+ {
+ void *bh;
+ gdp = group_desc (i);
+ desc_count += gdp->bg_free_blocks_count;
+ bh = disk_cache_block_ref (gdp->bg_block_bitmap);
+ x = count_free (bh, block_size);
+ disk_cache_block_deref (bh);
+ printf ("group %d: stored = %d, counted = %lu",
+ i, gdp->bg_free_blocks_count, x);
+ bitmap_count += x;
+ }
+ printf ("ext2_count_free_blocks: stored = %u, computed = %lu, %lu",
+ sblock->s_free_blocks_count, desc_count, bitmap_count);
+ pthread_spin_unlock (&global_lock);
+ return bitmap_count;
+#else
+ return sblock->s_free_blocks_count;
+#endif
+}
+
+static inline int
+block_in_use (block_t block, unsigned char *map)
+{
+ return test_bit ((block - sblock->s_first_data_block) %
+ sblock->s_blocks_per_group, map);
+}
+
+void
+ext2_check_blocks_bitmap ()
+{
+ char *bh;
+ unsigned long desc_count, bitmap_count, x;
+ unsigned long desc_blocks;
+ struct ext2_group_desc *gdp;
+ int i, j;
+
+ pthread_spin_lock (&global_lock);
+
+ desc_count = 0;
+ bitmap_count = 0;
+ gdp = NULL;
+
+ desc_blocks = (groups_count + desc_per_block - 1) / desc_per_block;
+
+ for (i = 0; i < groups_count; i++)
+ {
+ inline int test_root (int a, int b)
+ {
+ if (a == 0)
+ return 1;
+ while (1)
+ {
+ if (a == 1)
+ return 1;
+ if (a % b)
+ return 0;
+ a = a / b;
+ }
+ }
+ inline int ext2_group_sparse (int group)
+ {
+ return (test_root (group, 3) || test_root (group, 5)
+ || test_root (group, 7));
+ }
+
+ gdp = group_desc (i);
+ desc_count += gdp->bg_free_blocks_count;
+ bh = disk_cache_block_ref (gdp->bg_block_bitmap);
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE (sblock,
+ EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
+ || ext2_group_sparse (i))
+ {
+ if (!test_bit (0, bh))
+ ext2_error ("superblock in group %d is marked free", i);
+
+ for (j = 0; j < desc_blocks; j++)
+ if (!test_bit (j + 1, bh))
+ ext2_error ("descriptor block #%d in group %d is marked free",
+ j, i);
+ }
+
+ if (!block_in_use (gdp->bg_block_bitmap, bh))
+ ext2_error ("block bitmap for group %d is marked free", i);
+
+ if (!block_in_use (gdp->bg_inode_bitmap, bh))
+ ext2_error ("inode bitmap for group %d is marked free", i);
+
+ for (j = 0; j < itb_per_group; j++)
+ if (!block_in_use (gdp->bg_inode_table + j, bh))
+ ext2_error ("block #%d of the inode table in group %d is marked free", j, i);
+
+ x = count_free (bh, block_size);
+ disk_cache_block_deref (bh);
+ if (gdp->bg_free_blocks_count != x)
+ ext2_error ("wrong free blocks count for group %d,"
+ " stored = %d, counted = %lu",
+ i, gdp->bg_free_blocks_count, x);
+ bitmap_count += x;
+ }
+ if (sblock->s_free_blocks_count != bitmap_count)
+ ext2_error ("wrong free blocks count in super block,"
+ " stored = %lu, counted = %lu",
+ (unsigned long) sblock->s_free_blocks_count, bitmap_count);
+ pthread_spin_unlock (&global_lock);
+}
diff --git a/ext2fs/bitmap.c b/ext2fs/bitmap.c
new file mode 100644
index 00000000..92850232
--- /dev/null
+++ b/ext2fs/bitmap.c
@@ -0,0 +1,108 @@
+/* Bitmap perusing routines
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Converted to work under the hurd by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define ffz(word) (ffs (~(unsigned int) (word)) - 1)
+
+/*
+ * linux/fs/ext2/bitmap.c (&c)
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ */
+
+static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
+
+static inline
+unsigned long count_free (char * map, unsigned int numchars)
+{
+ unsigned int i;
+ unsigned long sum = 0;
+
+ if (!map)
+ return (0);
+ for (i = 0; i < numchars; i++)
+ sum += nibblemap[map[i] & 0xf] +
+ nibblemap[(map[i] >> 4) & 0xf];
+ return (sum);
+}
+
+/* ---------------------------------------------------------------- */
+
+/*
+ * Copyright 1994, David S. Miller (davem@caip.rutgers.edu).
+ */
+
+/* find_next_zero_bit() finds the first zero bit in a bit string of length
+ * 'size' bits, starting the search at bit 'offset'. This is largely based
+ * on Linus's ALPHA routines, which are pretty portable BTW.
+ */
+
+static inline unsigned long
+find_next_zero_bit(void *addr, unsigned long size, unsigned long offset)
+{
+ unsigned long *p = ((unsigned long *) addr) + (offset >> 5);
+ unsigned long result = offset & ~31UL;
+ unsigned long tmp;
+
+ if (offset >= size)
+ return size;
+ size -= result;
+ offset &= 31UL;
+ if (offset)
+ {
+ tmp = *(p++);
+ tmp |= ~0UL >> (32-offset);
+ if (size < 32)
+ goto found_first;
+ if (~tmp)
+ goto found_middle;
+ size -= 32;
+ result += 32;
+ }
+ while (size & ~31UL)
+ {
+ if (~(tmp = *(p++)))
+ goto found_middle;
+ result += 32;
+ size -= 32;
+ }
+ if (!size)
+ return result;
+ tmp = *p;
+
+found_first:
+ tmp |= ~0UL << size;
+ if (!~tmp)
+ return result + size;
+found_middle:
+ return result + ffz(tmp);
+}
+
+/* Linus sez that gcc can optimize the following correctly, we'll see if this
+ * holds on the Sparc as it does for the ALPHA.
+ */
+
+static inline int
+find_first_zero_bit(void *buf, unsigned len)
+{
+ return find_next_zero_bit(buf, len, 0);
+}
diff --git a/ext2fs/dir.c b/ext2fs/dir.c
new file mode 100644
index 00000000..337314c1
--- /dev/null
+++ b/ext2fs/dir.c
@@ -0,0 +1,1092 @@
+/* Directory management routines
+
+ Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2007
+ Free Software Foundation, Inc.
+
+ Converted for ext2fs by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ext2fs.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <stddef.h>
+
+/* This isn't quite right because a file system block may straddle several
+ device blocks, and so a write failure between writing two device blocks
+ may scramble things up a bit. But the linux doesn't do this. We could
+ try and make sure that we never wrote any modified directories with
+ entries that straddle device blocks (but read those that do)... */
+#define DIRBLKSIZ block_size
+
+enum slot_status
+{
+ /* This means we haven't yet found room for a new entry. */
+ LOOKING,
+
+ /* This means that the specified entry is free and should be used. */
+ TAKE,
+
+ /* This means that the specified entry has enough room at the end
+ to hold the new entry. */
+ SHRINK,
+
+ /* This means that there is enough space in the block, but not in
+ any one single entry, so they all have to be shifted to make
+ room. */
+ COMPRESS,
+
+ /* This means that the directory will have to be grown to hold the
+ entry. */
+ EXTEND,
+
+ /* For removal and rename, this means that this is the location
+ of the entry found. */
+ HERE_TIS,
+};
+
+struct dirstat
+{
+ /* Type of followp operation expected */
+ enum lookup_type type;
+
+ /* One of the statuses above */
+ enum slot_status stat;
+
+ /* Mapped address and length of directory */
+ vm_address_t mapbuf;
+ vm_size_t mapextent;
+
+ /* Index of this directory block. */
+ int idx;
+
+ /* For stat COMPRESS, this is the address (inside mapbuf)
+ of the first direct in the directory block to be compressed. */
+ /* For stat HERE_TIS, SHRINK, and TAKE, this is the entry referenced. */
+ struct ext2_dir_entry_2 *entry;
+
+ /* For stat HERE_TIS, type REMOVE, this is the address of the immediately
+ previous direct in this directory block, or zero if this is the first. */
+ struct ext2_dir_entry_2 *preventry;
+
+ /* For stat COMPRESS, this is the number of bytes needed to be copied
+ in order to undertake the compression. */
+ size_t nbytes;
+};
+
+const size_t diskfs_dirstat_size = sizeof (struct dirstat);
+
+/* Initialize DS such that diskfs_drop_dirstat will ignore it. */
+void
+diskfs_null_dirstat (struct dirstat *ds)
+{
+ ds->type = LOOKUP;
+}
+
+static error_t
+dirscanblock (vm_address_t blockoff, struct node *dp, int idx,
+ const char *name, size_t namelen, enum lookup_type type,
+ struct dirstat *ds, ino_t *inum);
+
+
+#if 0 /* XXX unused for now */
+static const unsigned char ext2_file_type[EXT2_FT_MAX] =
+{
+ [EXT2_FT_UNKNOWN] = DT_UNKNOWN,
+ [EXT2_FT_REG_FILE] = DT_REG,
+ [EXT2_FT_DIR] = DT_DIR,
+ [EXT2_FT_CHRDEV] = DT_CHR,
+ [EXT2_FT_BLKDEV] = DT_BLK,
+ [EXT2_FT_FIFO] = DT_FIFO,
+ [EXT2_FT_SOCK] = DT_SOCK,
+ [EXT2_FT_SYMLINK] = DT_LNK,
+};
+
+static const unsigned char file_type_ext2[] =
+{
+ [DT_UNKNOWN] = EXT2_FT_UNKNOWN,
+ [DT_REG] = EXT2_FT_REG_FILE,
+ [DT_DIR] = EXT2_FT_DIR,
+ [DT_CHR] = EXT2_FT_CHRDEV,
+ [DT_BLK] = EXT2_FT_BLKDEV,
+ [DT_FIFO] = EXT2_FT_FIFO,
+ [DT_SOCK] = EXT2_FT_SOCK,
+ [DT_LNK] = EXT2_FT_SYMLINK,
+};
+#endif
+
+/* Implement the diskfs_lookup from the diskfs library. See
+ <hurd/diskfs.h> for the interface specification. */
+error_t
+diskfs_lookup_hard (struct node *dp, const char *name, enum lookup_type type,
+ struct node **npp, struct dirstat *ds, struct protid *cred)
+{
+ error_t err;
+ ino_t inum;
+ size_t namelen;
+ int spec_dotdot;
+ struct node *np = 0;
+ ino_t retry_dotdot = 0;
+ vm_prot_t prot =
+ (type == LOOKUP) ? VM_PROT_READ : (VM_PROT_READ | VM_PROT_WRITE);
+ memory_object_t memobj;
+ vm_address_t buf = 0;
+ vm_size_t buflen = 0;
+ vm_address_t blockaddr;
+ int idx, lastidx;
+ int looped;
+
+ if ((type == REMOVE) || (type == RENAME))
+ assert (npp);
+
+ if (npp)
+ *npp = 0;
+
+ spec_dotdot = type & SPEC_DOTDOT;
+ type &= ~SPEC_DOTDOT;
+
+ namelen = strlen (name);
+
+ if (namelen > EXT2_NAME_LEN)
+ {
+ if (ds)
+ diskfs_null_dirstat (ds);
+ return ENAMETOOLONG;
+ }
+
+ try_again:
+ if (ds)
+ {
+ ds->type = LOOKUP;
+ ds->mapbuf = 0;
+ ds->mapextent = 0;
+ }
+ if (buf)
+ {
+ munmap ((caddr_t) buf, buflen);
+ buf = 0;
+ }
+ if (ds && (type == CREATE || type == RENAME))
+ ds->stat = LOOKING;
+
+ /* Map in the directory contents. */
+ memobj = diskfs_get_filemap (dp, prot);
+
+ if (memobj == MACH_PORT_NULL)
+ return errno;
+
+ buf = 0;
+ /* We allow extra space in case we have to do an EXTEND. */
+ buflen = round_page (dp->dn_stat.st_size + DIRBLKSIZ);
+ err = vm_map (mach_task_self (),
+ &buf, buflen, 0, 1, memobj, 0, 0, prot, prot, 0);
+ mach_port_deallocate (mach_task_self (), memobj);
+ if (err)
+ return err;
+
+ inum = 0;
+
+ diskfs_set_node_atime (dp);
+
+ /* Start the lookup at DP->dn->dir_idx. */
+ idx = dp->dn->dir_idx;
+ if (idx * DIRBLKSIZ > dp->dn_stat.st_size)
+ idx = 0; /* just in case */
+ blockaddr = buf + idx * DIRBLKSIZ;
+ looped = (idx == 0);
+ lastidx = idx;
+ if (lastidx == 0)
+ lastidx = dp->dn_stat.st_size / DIRBLKSIZ;
+
+ while (!looped || idx < lastidx)
+ {
+ err = dirscanblock (blockaddr, dp, idx, name, namelen, type, ds, &inum);
+ if (!err)
+ {
+ dp->dn->dir_idx = idx;
+ break;
+ }
+ if (err != ENOENT)
+ {
+ munmap ((caddr_t) buf, buflen);
+ return err;
+ }
+
+ blockaddr += DIRBLKSIZ;
+ idx++;
+ if (blockaddr - buf >= dp->dn_stat.st_size && !looped)
+ {
+ /* We've gotten to the end; start back at the beginning */
+ looped = 1;
+ blockaddr = buf;
+ idx = 0;
+ }
+ }
+
+ diskfs_set_node_atime (dp);
+ if (diskfs_synchronous)
+ diskfs_node_update (dp, 1);
+
+ /* If err is set here, it's ENOENT, and we don't want to
+ think about that as an error yet. */
+ err = 0;
+
+ if (inum && npp)
+ {
+ if (namelen != 2 || name[0] != '.' || name[1] != '.')
+ {
+ if (inum == dp->cache_id)
+ {
+ np = dp;
+ diskfs_nref (np);
+ }
+ else
+ {
+ err = diskfs_cached_lookup (inum, &np);
+ if (err)
+ goto out;
+ }
+ }
+
+ /* We are looking up .. */
+ /* Check to see if this is the root of the filesystem. */
+ else if (dp->cache_id == 2)
+ {
+ err = EAGAIN;
+ goto out;
+ }
+
+ /* We can't just do diskfs_cached_lookup, because we would then deadlock.
+ So we do this. Ick. */
+ else if (retry_dotdot)
+ {
+ /* Check to see that we got the same answer as last time. */
+ if (inum != retry_dotdot)
+ {
+ /* Drop what we *thought* was .. (but isn't any more) and
+ try *again*. */
+ diskfs_nput (np);
+ pthread_mutex_unlock (&dp->lock);
+ err = diskfs_cached_lookup (inum, &np);
+ pthread_mutex_lock (&dp->lock);
+ if (err)
+ goto out;
+ retry_dotdot = inum;
+ goto try_again;
+ }
+ /* Otherwise, we got it fine and np is already set properly. */
+ }
+ else if (!spec_dotdot)
+ {
+ /* Lock them in the proper order, and then
+ repeat the directory scan to see if this is still
+ right. */
+ pthread_mutex_unlock (&dp->lock);
+ err = diskfs_cached_lookup (inum, &np);
+ pthread_mutex_lock (&dp->lock);
+ if (err)
+ goto out;
+ retry_dotdot = inum;
+ goto try_again;
+ }
+
+ /* Here below are the spec dotdot cases. */
+ else if (type == RENAME || type == REMOVE)
+ np = ifind (inum);
+
+ else if (type == LOOKUP)
+ {
+ diskfs_nput (dp);
+ err = diskfs_cached_lookup (inum, &np);
+ if (err)
+ goto out;
+ }
+ else
+ assert (0);
+ }
+
+ if ((type == CREATE || type == RENAME) && !inum && ds && ds->stat == LOOKING)
+ {
+ /* We didn't find any room, so mark ds to extend the dir */
+ ds->type = CREATE;
+ ds->stat = EXTEND;
+ ds->idx = dp->dn_stat.st_size / DIRBLKSIZ;
+ }
+
+ /* Return to the user; if we can't, release the reference
+ (and lock) we acquired above. */
+ out:
+ /* Deallocate or save the mapping. */
+ if ((err && err != ENOENT)
+ || !ds
+ || ds->type == LOOKUP)
+ {
+ munmap ((caddr_t) buf, buflen);
+ if (ds)
+ ds->type = LOOKUP; /* set to be ignored by drop_dirstat */
+ }
+ else
+ {
+ ds->mapbuf = buf;
+ ds->mapextent = buflen;
+ }
+
+ if (np)
+ {
+ assert (npp);
+ if (err)
+ {
+ if (!spec_dotdot)
+ {
+ /* Normal case */
+ if (np == dp)
+ diskfs_nrele (np);
+ else
+ diskfs_nput (np);
+ }
+ else if (type == RENAME || type == REMOVE)
+ /* We just did ifind to get np; that allocates
+ no new references, so we don't have anything to do */
+ ;
+ else if (type == LOOKUP)
+ /* We did diskfs_cached_lookup */
+ diskfs_nput (np);
+ }
+ else
+ *npp = np;
+ }
+
+ return err ? : inum ? 0 : ENOENT;
+}
+
+/* Scan block at address BLKADDR (of node DP; block index IDX), for
+ name NAME of length NAMELEN. Args TYPE, DS are as for
+ diskfs_lookup. If found, set *INUM to the inode number, else
+ return ENOENT. */
+static error_t
+dirscanblock (vm_address_t blockaddr, struct node *dp, int idx,
+ const char *name, size_t namelen, enum lookup_type type,
+ struct dirstat *ds, ino_t *inum)
+{
+ size_t nfree = 0;
+ size_t needed = 0;
+ vm_address_t currentoff, prevoff;
+ struct ext2_dir_entry_2 *entry = 0;
+ int nentries = 0;
+ size_t nbytes = 0;
+ int looking = 0;
+ int countcopies = 0;
+ int consider_compress = 0;
+
+ if (ds && (ds->stat == LOOKING
+ || ds->stat == COMPRESS))
+ {
+ looking = 1;
+ countcopies = 1;
+ needed = EXT2_DIR_REC_LEN (namelen);
+ }
+
+ for (currentoff = blockaddr, prevoff = 0;
+ currentoff < blockaddr + DIRBLKSIZ;
+ prevoff = currentoff, currentoff += entry->rec_len)
+ {
+ entry = (struct ext2_dir_entry_2 *)currentoff;
+
+ if (!entry->rec_len
+ || entry->rec_len % EXT2_DIR_PAD
+ || entry->name_len > EXT2_NAME_LEN
+ || currentoff + entry->rec_len > blockaddr + DIRBLKSIZ
+ || EXT2_DIR_REC_LEN (entry->name_len) > entry->rec_len
+ || memchr (entry->name, '\0', entry->name_len))
+ {
+ ext2_warning ("bad directory entry: inode: %Ld offset: %zd",
+ dp->cache_id,
+ currentoff - blockaddr + idx * DIRBLKSIZ);
+ return ENOENT;
+ }
+
+ if (looking || countcopies)
+ {
+ size_t thisfree;
+
+ /* Count how much free space this entry has in it. */
+ if (entry->inode == 0)
+ thisfree = entry->rec_len;
+ else
+ thisfree = entry->rec_len - EXT2_DIR_REC_LEN (entry->name_len);
+
+ /* If this isn't at the front of the block, then it will
+ have to be copied if we do a compression; count the
+ number of bytes there too. */
+ if (countcopies && currentoff != blockaddr)
+ nbytes += EXT2_DIR_REC_LEN (entry->name_len);
+
+ if (ds->stat == COMPRESS && nbytes > ds->nbytes)
+ /* The previously found compress is better than
+ this one, so don't bother counting any more. */
+ countcopies = 0;
+
+ if (thisfree >= needed)
+ {
+ ds->type = CREATE;
+ ds->stat = entry->inode == 0 ? TAKE : SHRINK;
+ ds->entry = entry;
+ ds->idx = idx;
+ looking = countcopies = 0;
+ }
+ else
+ {
+ nfree += thisfree;
+ if (nfree >= needed)
+ consider_compress = 1;
+ }
+ }
+
+ if (entry->inode)
+ nentries++;
+
+ if (entry->name_len == namelen
+ && entry->name[0] == name[0]
+ && entry->inode
+ && !bcmp (entry->name, name, namelen))
+ break;
+ }
+
+ if (consider_compress
+ && (ds->stat == LOOKING
+ || (ds->stat == COMPRESS && ds->nbytes > nbytes)))
+ {
+ ds->type = CREATE;
+ ds->stat = COMPRESS;
+ ds->entry = (struct ext2_dir_entry_2 *) blockaddr;
+ ds->idx = idx;
+ ds->nbytes = nbytes;
+ }
+
+ if (currentoff >= blockaddr + DIRBLKSIZ)
+ {
+ int i;
+ /* The name is not in this block. */
+
+ /* Because we scanned the entire block, we should write
+ down how many entries there were. */
+ if (!dp->dn->dirents)
+ {
+ dp->dn->dirents = malloc ((dp->dn_stat.st_size / DIRBLKSIZ)
+ * sizeof (int));
+ for (i = 0; i < dp->dn_stat.st_size/DIRBLKSIZ; i++)
+ dp->dn->dirents[i] = -1;
+ }
+ /* Make sure the count is correct if there is one now. */
+ assert (dp->dn->dirents[idx] == -1
+ || dp->dn->dirents[idx] == nentries);
+ dp->dn->dirents[idx] = nentries;
+
+ return ENOENT;
+ }
+
+ /* We have found the required name. */
+
+ if (ds && type == CREATE)
+ ds->type = LOOKUP; /* it's invalid now */
+ else if (ds && (type == REMOVE || type == RENAME))
+ {
+ ds->type = type;
+ ds->stat = HERE_TIS;
+ ds->entry = entry;
+ ds->idx = idx;
+ ds->preventry = (struct ext2_dir_entry_2 *) prevoff;
+ }
+
+ *inum = entry->inode;
+ return 0;
+}
+
+/* Following a lookup call for CREATE, this adds a node to a directory.
+ DP is the directory to be modified; NAME is the name to be entered;
+ NP is the node being linked in; DS is the cached information returned
+ by lookup; CRED describes the user making the call. This call may
+ only be made if the directory has been held locked continuously since
+ the preceding lookup call, and only if that call returned ENOENT. */
+error_t
+diskfs_direnter_hard (struct node *dp, const char *name, struct node *np,
+ struct dirstat *ds, struct protid *cred)
+{
+ struct ext2_dir_entry_2 *new;
+ size_t namelen = strlen (name);
+ size_t needed = EXT2_DIR_REC_LEN (namelen);
+ size_t oldneeded;
+ vm_address_t fromoff, tooff;
+ size_t totfreed;
+ error_t err;
+ size_t oldsize = 0;
+
+ assert (ds->type == CREATE);
+
+ assert (!diskfs_readonly);
+
+ dp->dn_set_mtime = 1;
+
+ /* Select a location for the new directory entry. Each branch of this
+ switch is responsible for setting NEW to point to the on-disk
+ directory entry being written, and setting NEW->rec_len appropriately. */
+
+ switch (ds->stat)
+ {
+ case TAKE:
+ /* We are supposed to consume this slot. */
+ assert (ds->entry->inode == 0 && ds->entry->rec_len >= needed);
+
+ new = ds->entry;
+ break;
+
+ case SHRINK:
+ /* We are supposed to take the extra space at the end
+ of this slot. */
+ oldneeded = EXT2_DIR_REC_LEN (ds->entry->name_len);
+ assert (ds->entry->rec_len - oldneeded >= needed);
+
+ new = (struct ext2_dir_entry_2 *) ((vm_address_t) ds->entry + oldneeded);
+
+ new->rec_len = ds->entry->rec_len - oldneeded;
+ ds->entry->rec_len = oldneeded;
+ break;
+
+ case COMPRESS:
+ /* We are supposed to move all the entries to the
+ front of the block, giving each the minimum
+ necessary room. This should free up enough space
+ for the new entry. */
+ fromoff = tooff = (vm_address_t) ds->entry;
+
+ while (fromoff < (vm_address_t) ds->entry + DIRBLKSIZ)
+ {
+ struct ext2_dir_entry_2 *from = (struct ext2_dir_entry_2 *)fromoff;
+ struct ext2_dir_entry_2 *to = (struct ext2_dir_entry_2 *) tooff;
+ size_t fromreclen = from->rec_len;
+
+ if (from->inode != 0)
+ {
+ assert (fromoff >= tooff);
+
+ memmove (to, from, fromreclen);
+ to->rec_len = EXT2_DIR_REC_LEN (to->name_len);
+
+ tooff += to->rec_len;
+ }
+ fromoff += fromreclen;
+ }
+
+ totfreed = (vm_address_t) ds->entry + DIRBLKSIZ - tooff;
+ assert (totfreed >= needed);
+
+ new = (struct ext2_dir_entry_2 *) tooff;
+ new->rec_len = totfreed;
+ break;
+
+ case EXTEND:
+ /* Extend the file. */
+ assert (needed <= DIRBLKSIZ);
+
+ oldsize = dp->dn_stat.st_size;
+ if ((off_t)(oldsize + DIRBLKSIZ) != (dp->dn_stat.st_size + DIRBLKSIZ))
+ {
+ /* We can't possibly map the whole directory in. */
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+ return EOVERFLOW;
+ }
+ while (oldsize + DIRBLKSIZ > dp->allocsize)
+ {
+ err = diskfs_grow (dp, oldsize + DIRBLKSIZ, cred);
+ if (err)
+ {
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+ return err;
+ }
+ }
+
+ new = (struct ext2_dir_entry_2 *) (ds->mapbuf + oldsize);
+
+ dp->dn_stat.st_size = oldsize + DIRBLKSIZ;
+ dp->dn_set_ctime = 1;
+
+ new->rec_len = DIRBLKSIZ;
+ break;
+
+ default:
+ new = 0;
+ assert (! "impossible: bogus status field in dirstat");
+ }
+
+ /* NEW points to the directory entry being written, and its
+ rec_len field is already filled in. Now fill in the rest. */
+
+ new->inode = np->cache_id;
+#if 0
+ /* XXX We cannot enable this code because file types can change
+ (and conceivably quite often) with translator settings.
+ There is no way for the translator that determines the type of
+ the virtual node to cause all the directory entries linked to
+ its underlying inode to reflect the proper type. */
+ new->file_type = (EXT2_HAS_INCOMPAT_FEATURE (sblock,
+ EXT2_FEATURE_INCOMPAT_FILETYPE)
+ ? file_type_ext2[IFTODT (np->dn_stat.st_mode & S_IFMT)]
+ : 0);
+#else
+ new->file_type = 0;
+#endif
+ new->name_len = namelen;
+ memcpy (new->name, name, namelen);
+
+ /* Mark the directory inode has having been written. */
+ dp->dn->info.i_flags &= ~EXT2_BTREE_FL;
+ dp->dn_set_mtime = 1;
+
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+
+ if (ds->stat != EXTEND)
+ {
+ /* If we are keeping count of this block, then keep the count up
+ to date. */
+ if (dp->dn->dirents && dp->dn->dirents[ds->idx] != -1)
+ dp->dn->dirents[ds->idx]++;
+ }
+ else
+ {
+ int i;
+ /* It's cheap, so start a count here even if we aren't counting
+ anything at all. */
+ if (dp->dn->dirents)
+ {
+ dp->dn->dirents = realloc (dp->dn->dirents,
+ (dp->dn_stat.st_size / DIRBLKSIZ
+ * sizeof (int)));
+ for (i = oldsize / DIRBLKSIZ;
+ i < dp->dn_stat.st_size / DIRBLKSIZ;
+ i++)
+ dp->dn->dirents[i] = -1;
+
+ dp->dn->dirents[ds->idx] = 1;
+ }
+ else
+ {
+ dp->dn->dirents = malloc (dp->dn_stat.st_size / DIRBLKSIZ
+ * sizeof (int));
+ for (i = 0; i < dp->dn_stat.st_size / DIRBLKSIZ; i++)
+ dp->dn->dirents[i] = -1;
+ dp->dn->dirents[ds->idx] = 1;
+ }
+ }
+
+ diskfs_file_update (dp, diskfs_synchronous);
+
+ return 0;
+}
+
+/* Following a lookup call for REMOVE, this removes the link from the
+ directory. DP is the directory being changed and DS is the cached
+ information returned from lookup. This call is only valid if the
+ directory has been locked continuously since the call to lookup, and
+ only if that call succeeded. */
+error_t
+diskfs_dirremove_hard (struct node *dp, struct dirstat *ds)
+{
+ assert (ds->type == REMOVE);
+ assert (ds->stat == HERE_TIS);
+
+ assert (!diskfs_readonly);
+
+ if (ds->preventry == 0)
+ ds->entry->inode = 0;
+ else
+ {
+ assert ((vm_address_t) ds->entry - (vm_address_t) ds->preventry
+ == ds->preventry->rec_len);
+ ds->preventry->rec_len += ds->entry->rec_len;
+ }
+
+ dp->dn_set_mtime = 1;
+ dp->dn->info.i_flags &= ~EXT2_BTREE_FL;
+
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+
+ /* If we are keeping count of this block, then keep the count up
+ to date. */
+ if (dp->dn->dirents && dp->dn->dirents[ds->idx] != -1)
+ dp->dn->dirents[ds->idx]--;
+
+ diskfs_file_update (dp, diskfs_synchronous);
+
+ return 0;
+}
+
+
+/* Following a lookup call for RENAME, this changes the inode number
+ on a directory entry. DP is the directory being changed; NP is
+ the new node being linked in; DP is the cached information returned
+ by lookup. This call is only valid if the directory has been locked
+ continuously since the call to lookup, and only if that call
+ succeeded. */
+error_t
+diskfs_dirrewrite_hard (struct node *dp, struct node *np, struct dirstat *ds)
+{
+ assert (ds->type == RENAME);
+ assert (ds->stat == HERE_TIS);
+
+ assert (!diskfs_readonly);
+
+ ds->entry->inode = np->cache_id;
+ dp->dn_set_mtime = 1;
+ dp->dn->info.i_flags &= ~EXT2_BTREE_FL;
+
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+
+ diskfs_file_update (dp, diskfs_synchronous);
+
+ return 0;
+}
+
+/* Tell if DP is an empty directory (has only "." and ".." entries).
+ This routine must be called from inside a catch_exception (). */
+int
+diskfs_dirempty (struct node *dp, struct protid *cred)
+{
+ error_t err;
+ vm_address_t buf = 0, curoff;
+ struct ext2_dir_entry_2 *entry;
+ int hit = 0; /* Found something in the directory. */
+ memory_object_t memobj = diskfs_get_filemap (dp, VM_PROT_READ);
+
+ if (memobj == MACH_PORT_NULL)
+ /* XXX should reflect error properly. */
+ return 0;
+
+ err = vm_map (mach_task_self (), &buf, dp->dn_stat.st_size, 0,
+ 1, memobj, 0, 0, VM_PROT_READ, VM_PROT_READ, 0);
+ mach_port_deallocate (mach_task_self (), memobj);
+ assert (!err);
+
+ diskfs_set_node_atime (dp);
+
+ for (curoff = buf;
+ !hit && curoff < buf + dp->dn_stat.st_size;
+ curoff += entry->rec_len)
+ {
+ entry = (struct ext2_dir_entry_2 *) curoff;
+
+ if (entry->inode != 0
+ && (entry->name_len > 2
+ || entry->name[0] != '.'
+ || (entry->name[1] != '.'
+ && entry->name[1] != '\0')))
+ hit = 1;
+ }
+
+ diskfs_set_node_atime (dp);
+ if (diskfs_synchronous)
+ diskfs_node_update (dp, 1);
+
+ munmap ((caddr_t) buf, dp->dn_stat.st_size);
+
+ return !hit;
+}
+
+/* Make DS an invalid dirstat. */
+error_t
+diskfs_drop_dirstat (struct node *dp, struct dirstat *ds)
+{
+ if (ds->type != LOOKUP)
+ {
+ assert (ds->mapbuf);
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+ ds->type = LOOKUP;
+ }
+ return 0;
+}
+
+
+/* Count the entries in directory block NB for directory DP and
+ write the answer down in its dirents array. As a side affect
+ fill BUF with the block. */
+static error_t
+count_dirents (struct node *dp, int nb, char *buf)
+{
+ size_t amt;
+ char *offinblk;
+ struct ext2_dir_entry_2 *entry;
+ int count = 0;
+ error_t err;
+
+ assert (dp->dn->dirents);
+ assert ((nb + 1) * DIRBLKSIZ <= dp->dn_stat.st_size);
+
+ err = diskfs_node_rdwr (dp, buf, nb * DIRBLKSIZ, DIRBLKSIZ, 0, 0, &amt);
+ if (err)
+ return err;
+ assert (amt == DIRBLKSIZ);
+
+ for (offinblk = buf;
+ offinblk < buf + DIRBLKSIZ;
+ offinblk += entry->rec_len)
+ {
+ entry = (struct ext2_dir_entry_2 *) offinblk;
+ if (entry->inode)
+ count++;
+ }
+
+ assert (dp->dn->dirents[nb] == -1 || dp->dn->dirents[nb] == count);
+ dp->dn->dirents[nb] = count;
+ return 0;
+}
+
+/* Returned directory entries are aligned to blocks this many bytes long.
+ Must be a power of two. */
+#define DIRENT_ALIGN 4
+
+/* Implement the diskfs_get_directs callback as described in
+ <hurd/diskfs.h>. */
+error_t
+diskfs_get_directs (struct node *dp,
+ int entry,
+ int nentries,
+ char **data,
+ size_t *datacnt,
+ vm_size_t bufsiz,
+ int *amt)
+{
+ int blkno;
+ int nblks;
+ int curentry;
+ char buf[DIRBLKSIZ];
+ char *bufp;
+ int bufvalid;
+ error_t err;
+ int i;
+ char *datap;
+ struct ext2_dir_entry_2 *entryp;
+ int allocsize;
+ size_t checklen;
+ struct dirent *userp;
+
+ nblks = dp->dn_stat.st_size/DIRBLKSIZ;
+
+ if (!dp->dn->dirents)
+ {
+ dp->dn->dirents = malloc (nblks * sizeof (int));
+ for (i = 0; i < nblks; i++)
+ dp->dn->dirents[i] = -1;
+ }
+
+ /* Scan through the entries to find ENTRY. If we encounter
+ a -1 in the process then stop to fill it. When we run
+ off the end, ENTRY is too big. */
+ curentry = 0;
+ bufvalid = 0;
+ for (blkno = 0; blkno < nblks; blkno++)
+ {
+ if (dp->dn->dirents[blkno] == -1)
+ {
+ err = count_dirents (dp, blkno, buf);
+ if (err)
+ return err;
+ bufvalid = 1;
+ }
+
+ if (curentry + dp->dn->dirents[blkno] > entry)
+ /* ENTRY starts in this block. */
+ break;
+
+ curentry += dp->dn->dirents[blkno];
+
+ bufvalid = 0;
+ }
+
+ if (blkno == nblks)
+ {
+ /* We reached the end of the directory without seeing ENTRY.
+ This is treated as an EOF condition, meaning we return
+ success with empty results. */
+ *datacnt = 0;
+ *amt = 0;
+ return 0;
+ }
+
+ /* Allocate enough space to hold the maximum we might return */
+ if (!bufsiz || bufsiz > dp->dn_stat.st_size)
+ /* Allocate enough to return the entire directory. Since ext2's
+ directory format is different than the format used to return the
+ entries, we allocate enough to hold the on disk directory plus
+ whatever extra would be necessary in the worst-case. */
+ {
+ /* The minimum size of an ext2fs directory entry. */
+ size_t min_entry_size = EXT2_DIR_REC_LEN (0);
+ /* The minimum size of a returned dirent entry. The +1 is for '\0'. */
+ size_t min_dirent_size = offsetof (struct dirent, d_name) + 1;
+ /* The maximum possible number of ext2fs dir entries in this dir. */
+ size_t max_entries = dp->dn_stat.st_size / min_entry_size;
+ /* The maximum difference in size per directory entry. */
+ size_t entry_extra =
+ DIRENT_ALIGN
+ + (min_dirent_size > min_entry_size
+ ? min_dirent_size - min_entry_size : 0);
+
+ allocsize = round_page (dp->dn_stat.st_size + max_entries * entry_extra);
+ }
+ else
+ allocsize = round_page (bufsiz);
+
+ if (allocsize > *datacnt)
+ *data = mmap (0, allocsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+
+ /* Set bufp appropriately */
+ bufp = buf;
+ if (curentry != entry)
+ {
+ /* Look through the block to find out where to start,
+ setting bufp appropriately. */
+ if (!bufvalid)
+ {
+ err = diskfs_node_rdwr (dp, buf, blkno * DIRBLKSIZ, DIRBLKSIZ,
+ 0, 0, &checklen);
+ if (err)
+ return err;
+ assert (checklen == DIRBLKSIZ);
+ bufvalid = 1;
+ }
+ for (i = 0, bufp = buf;
+ i < entry - curentry && bufp - buf < DIRBLKSIZ;
+ bufp += ((struct ext2_dir_entry_2 *)bufp)->rec_len, i++)
+ ;
+ /* Make sure we didn't run off the end. */
+ assert (bufp - buf < DIRBLKSIZ);
+ }
+
+ i = 0;
+ datap = *data;
+
+ /* Copy the entries, one at a time. */
+ while (((nentries == -1) || (i < nentries))
+ && (!bufsiz || (datap - *data < bufsiz) )
+ && blkno < nblks)
+ {
+ if (!bufvalid)
+ {
+ err = diskfs_node_rdwr (dp, buf, blkno * DIRBLKSIZ, DIRBLKSIZ,
+ 0, 0, &checklen);
+ if (err)
+ return err;
+ assert (checklen == DIRBLKSIZ);
+ bufvalid = 1;
+ bufp = buf;
+ }
+
+ entryp = (struct ext2_dir_entry_2 *)bufp;
+
+ if (entryp->inode)
+ {
+ int rec_len;
+ int name_len = entryp->name_len;
+
+ userp = (struct dirent *) datap;
+
+ /* Length is structure before the name + the name + '\0', all
+ padded to a four-byte alignment. */
+ rec_len =
+ ((offsetof (struct dirent, d_name)
+ + name_len + 1
+ + (DIRENT_ALIGN - 1))
+ & ~(DIRENT_ALIGN - 1));
+
+ /* See if this record would run over the end of the return buffer. */
+ if (bufsiz == 0)
+ /* It shouldn't ever, as we calculated the worst case size. */
+ assert (datap + rec_len <= *data + allocsize);
+ else
+ /* It's ok if it does, just leave off returning this entry. */
+ if (datap + rec_len > *data + allocsize)
+ break;
+
+ userp->d_fileno = entryp->inode;
+ userp->d_reclen = rec_len;
+ userp->d_namlen = name_len;
+
+#if 0
+ /* We don't bother to check the EXT2_FEATURE_INCOMPAT_FILETYPE
+ flag in the superblock, because in old filesystems the
+ file_type field is the high byte of the length field and is
+ always zero because names cannot be that long. */
+ if (entryp->file_type < EXT2_FT_MAX)
+ userp->d_type = ext2_file_type[entryp->file_type];
+ else
+ {
+ ext2_warning ("bad type %d in directory entry: "
+ "inode: %d offset: %d",
+ entryp->file_type,
+ dp->cache_id,
+ blkno * DIRBLKSIZ + bufp - buf);
+ userp->d_type = DT_UNKNOWN;
+ }
+#else
+ /* XXX
+ For complex reasons it might not be correct to return
+ the filesystem's d_type value to the user. */
+ userp->d_type = DT_UNKNOWN;
+#endif
+ memcpy (userp->d_name, entryp->name, name_len);
+ userp->d_name[name_len] = '\0';
+
+ datap += rec_len;
+ i++;
+ }
+
+ if (entryp->rec_len == 0)
+ {
+ ext2_warning ("zero length directory entry: inode: %Ld offset: %zd",
+ dp->cache_id,
+ blkno * DIRBLKSIZ + bufp - buf);
+ return EIO;
+ }
+
+ bufp += entryp->rec_len;
+ if (bufp - buf == DIRBLKSIZ)
+ {
+ blkno++;
+ bufvalid = 0;
+ }
+ else if (bufp - buf > DIRBLKSIZ)
+ {
+ ext2_warning ("directory entry too long: inode: %Ld offset: %zd",
+ dp->cache_id,
+ blkno * DIRBLKSIZ + bufp - buf - entryp->rec_len);
+ return EIO;
+ }
+ }
+
+ /* We've copied all we can. If we allocated our own array
+ but didn't fill all of it, then free whatever memory we didn't use. */
+ if (allocsize > *datacnt)
+ {
+ if (round_page (datap - *data) < allocsize)
+ munmap ((caddr_t) (*data + round_page (datap - *data)),
+ allocsize - round_page (datap - *data));
+ }
+
+ /* Set variables for return */
+ *datacnt = datap - *data;
+ *amt = i;
+ return 0;
+}
diff --git a/ext2fs/ext2_fs.h b/ext2fs/ext2_fs.h
new file mode 100644
index 00000000..b1caeefa
--- /dev/null
+++ b/ext2fs/ext2_fs.h
@@ -0,0 +1,651 @@
+/*
+ * linux/include/linux/ext2_fs.h
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/include/linux/minix_fs.h
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#ifndef _LINUX_EXT2_FS_H
+#define _LINUX_EXT2_FS_H
+
+/* #include <linux/types.h> */
+
+/*
+ * The second extended filesystem constants/structures
+ */
+
+/*
+ * Define EXT2FS_DEBUG to produce debug messages
+ */
+#undef EXT2FS_DEBUG
+
+/*
+ * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files
+ */
+#define EXT2_PREALLOCATE
+#define EXT2_DEFAULT_PREALLOC_BLOCKS 8
+
+/*
+ * The second extended file system version
+ */
+#define EXT2FS_DATE "95/08/09"
+#define EXT2FS_VERSION "0.5b"
+
+/*
+ * Debug code
+ */
+#ifdef EXT2FS_DEBUG
+# define ext2_debug(f, a...) { \
+ printk ("EXT2-fs DEBUG (%s, %d): %s:", \
+ __FILE__, __LINE__, __FUNCTION__); \
+ printk (f, ## a); \
+ }
+#else
+# define ext2_debug(f, a...) /**/
+#endif
+
+/*
+ * Special inodes numbers
+ */
+#define EXT2_BAD_INO 1 /* Bad blocks inode */
+#define EXT2_ROOT_INO 2 /* Root inode */
+#define EXT2_ACL_IDX_INO 3 /* ACL inode */
+#define EXT2_ACL_DATA_INO 4 /* ACL inode */
+#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
+#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */
+
+/* First non-reserved inode for old ext2 filesystems */
+#define EXT2_GOOD_OLD_FIRST_INO 11
+
+/*
+ * The second extended file system magic number
+ */
+#define EXT2_SUPER_MAGIC 0xEF53
+
+/*
+ * Maximal count of links to a file
+ */
+#define EXT2_LINK_MAX 32000
+
+/*
+ * Macro-instructions used to manage several block sizes
+ */
+#define EXT2_MIN_BLOCK_SIZE 1024
+#define EXT2_MAX_BLOCK_SIZE 4096
+#define EXT2_MIN_BLOCK_LOG_SIZE 10
+#ifdef __KERNEL__
+# define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize)
+#else
+# define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
+#endif
+#define EXT2_ACLE_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_entry))
+#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32))
+#ifdef __KERNEL__
+# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits)
+#else
+# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10)
+#endif
+#ifdef __KERNEL__
+#define EXT2_ADDR_PER_BLOCK_BITS(s) ((s)->u.ext2_sb.s_addr_per_block_bits)
+#define EXT2_INODE_SIZE(s) ((s)->u.ext2_sb.s_inode_size)
+#define EXT2_FIRST_INO(s) ((s)->u.ext2_sb.s_first_ino)
+#else
+#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+ EXT2_GOOD_OLD_INODE_SIZE : \
+ (s)->s_inode_size)
+#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+ EXT2_GOOD_OLD_FIRST_INO : \
+ (s)->s_first_ino)
+#endif
+
+/*
+ * Macro-instructions used to manage fragments
+ */
+#define EXT2_MIN_FRAG_SIZE 1024
+#define EXT2_MAX_FRAG_SIZE 4096
+#define EXT2_MIN_FRAG_LOG_SIZE 10
+#ifdef __KERNEL__
+# define EXT2_FRAG_SIZE(s) ((s)->u.ext2_sb.s_frag_size)
+# define EXT2_FRAGS_PER_BLOCK(s) ((s)->u.ext2_sb.s_frags_per_block)
+#else
+# define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size)
+# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s))
+#endif
+
+/*
+ * ACL structures
+ */
+struct ext2_acl_header /* Header of Access Control Lists */
+{
+ __u32 aclh_size;
+ __u32 aclh_file_count;
+ __u32 aclh_acle_count;
+ __u32 aclh_first_acle;
+};
+
+struct ext2_acl_entry /* Access Control List Entry */
+{
+ __u32 acle_size;
+ __u16 acle_perms; /* Access permissions */
+ __u16 acle_type; /* Type of entry */
+ __u16 acle_tag; /* User or group identity */
+ __u16 acle_pad1;
+ __u32 acle_next; /* Pointer on next entry for the */
+ /* same inode or on next free entry */
+};
+
+/*
+ * Structure of a blocks group descriptor
+ */
+struct ext2_group_desc
+{
+ __u32 bg_block_bitmap; /* Blocks bitmap block */
+ __u32 bg_inode_bitmap; /* Inodes bitmap block */
+ __u32 bg_inode_table; /* Inodes table block */
+ __u16 bg_free_blocks_count; /* Free blocks count */
+ __u16 bg_free_inodes_count; /* Free inodes count */
+ __u16 bg_used_dirs_count; /* Directories count */
+ __u16 bg_pad;
+ __u32 bg_reserved[3];
+};
+
+/*
+ * Macro-instructions used to manage group descriptors
+ */
+#ifdef __KERNEL__
+# define EXT2_BLOCKS_PER_GROUP(s) ((s)->u.ext2_sb.s_blocks_per_group)
+# define EXT2_DESC_PER_BLOCK(s) ((s)->u.ext2_sb.s_desc_per_block)
+# define EXT2_INODES_PER_GROUP(s) ((s)->u.ext2_sb.s_inodes_per_group)
+# define EXT2_DESC_PER_BLOCK_BITS(s) ((s)->u.ext2_sb.s_desc_per_block_bits)
+#else
+# define EXT2_BLOCKS_PER_GROUP(s) ((s)->s_blocks_per_group)
+# define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc))
+# define EXT2_INODES_PER_GROUP(s) ((s)->s_inodes_per_group)
+#endif
+
+/*
+ * Constants relative to the data blocks
+ */
+#define EXT2_NDIR_BLOCKS 12
+#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
+#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
+#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
+#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
+
+/*
+ * Inode flags
+ */
+#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */
+#define EXT2_UNRM_FL 0x00000002 /* Undelete */
+#define EXT2_COMPR_FL 0x00000004 /* Compress file */
+#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */
+#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */
+#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */
+#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */
+#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */
+/* Reserved for compression usage... */
+#define EXT2_DIRTY_FL 0x00000100
+#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */
+#define EXT2_NOCOMP_FL 0x00000400 /* Don't compress */
+#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */
+/* End compression flags --- maybe not all used */
+#define EXT2_BTREE_FL 0x00001000 /* btree format dir */
+#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */
+
+#define EXT2_FL_USER_VISIBLE 0x00001FFF /* User visible flags */
+#define EXT2_FL_USER_MODIFIABLE 0x000000FF /* User modifiable flags */
+
+/* Flags that should be inherited by new inodes from their parent. */
+#define EXT2_FL_INHERITED (EXT2_SECRM_FL | EXT2_UNRM_FL | EXT2_COMPR_FL |\
+ EXT2_SYNC_FL | EXT2_NODUMP_FL |\
+ EXT2_NOATIME_FL | EXT2_COMPRBLK_FL |\
+ EXT2_NOCOMP_FL)
+
+/* Flags that are appropriate for regular files (all but dir-specific ones). */
+#define EXT2_REG_FLMASK (~(0))
+
+/* Flags that are appropriate for non-directories/regular files. */
+#define EXT2_OTHER_FLMASK (EXT2_NODUMP_FL | EXT2_NOATIME_FL)
+
+/* Mask out flags that are inappropriate for the given type of inode. */
+static __inline__ __u32 ext2_mask_flags(mode_t mode, __u32 flags)
+{
+ if (S_ISDIR(mode))
+ return flags;
+ else if (S_ISREG(mode))
+ return flags & EXT2_REG_FLMASK;
+ else
+ return flags & EXT2_OTHER_FLMASK;
+}
+
+/*
+ * ioctl commands
+ */
+#define EXT2_IOC_GETFLAGS _IOR('f', 1, long)
+#define EXT2_IOC_SETFLAGS _IOW('f', 2, long)
+#define EXT2_IOC_GETVERSION _IOR('v', 1, long)
+#define EXT2_IOC_SETVERSION _IOW('v', 2, long)
+
+/*
+ * Structure of an inode on the disk
+ */
+struct ext2_inode {
+ __u16 i_mode; /* File mode */
+ __u16 i_uid; /* Low 16 bits of Owner Uid */
+ __u32 i_size; /* Size in bytes */
+ __u32 i_atime; /* Access time */
+ __u32 i_ctime; /* Creation time */
+ __u32 i_mtime; /* Modification time */
+ __u32 i_dtime; /* Deletion Time */
+ __u16 i_gid; /* Low 16 bits of Group Id */
+ __u16 i_links_count; /* Links count */
+ __u32 i_blocks; /* Blocks count */
+ __u32 i_flags; /* File flags */
+ union {
+ struct {
+ __u32 l_i_reserved1;
+ } linux1;
+ struct {
+ __u32 h_i_translator;
+ } hurd1;
+ struct {
+ __u32 m_i_reserved1;
+ } masix1;
+ } osd1; /* OS dependent 1 */
+ __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+ __u32 i_generation; /* File version (for NFS) */
+ __u32 i_file_acl; /* File ACL */
+ __u32 i_dir_acl; /* Directory ACL */
+ __u32 i_faddr; /* Fragment address */
+ union {
+ struct {
+ __u8 l_i_frag; /* Fragment number */
+ __u8 l_i_fsize; /* Fragment size */
+ __u16 i_pad1;
+ __u16 l_i_uid_high; /* these 2 fields */
+ __u16 l_i_gid_high; /* were reserved2[0] */
+ __u32 l_i_reserved2;
+ } linux2;
+ struct {
+ __u8 h_i_frag; /* Fragment number */
+ __u8 h_i_fsize; /* Fragment size */
+ __u16 h_i_mode_high;
+ __u16 h_i_uid_high;
+ __u16 h_i_gid_high;
+ __u32 h_i_author;
+ } hurd2;
+ struct {
+ __u8 m_i_frag; /* Fragment number */
+ __u8 m_i_fsize; /* Fragment size */
+ __u16 m_pad1;
+ __u32 m_i_reserved2[2];
+ } masix2;
+ } osd2; /* OS dependent 2 */
+};
+
+#define i_size_high i_dir_acl
+
+#if defined(__KERNEL__) || defined(__linux__)
+#define i_reserved1 osd1.linux1.l_i_reserved1
+#define i_frag osd2.linux2.l_i_frag
+#define i_fsize osd2.linux2.l_i_fsize
+#define i_uid_low i_uid
+#define i_gid_low i_gid
+#define i_uid_high osd2.linux2.l_i_uid_high
+#define i_gid_high osd2.linux2.l_i_gid_high
+#define i_reserved2 osd2.linux2.l_i_reserved2
+#endif
+
+#ifdef __hurd__
+#define i_translator osd1.hurd1.h_i_translator
+#define i_frag osd2.hurd2.h_i_frag
+#define i_fsize osd2.hurd2.h_i_fsize
+#define i_uid_high osd2.hurd2.h_i_uid_high
+#define i_gid_high osd2.hurd2.h_i_gid_high
+#define i_author osd2.hurd2.h_i_author
+#endif
+
+#ifdef __masix__
+#define i_reserved1 osd1.masix1.m_i_reserved1
+#define i_frag osd2.masix2.m_i_frag
+#define i_fsize osd2.masix2.m_i_fsize
+#define i_reserved2 osd2.masix2.m_i_reserved2
+#endif
+
+/*
+ * File system states
+ */
+#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */
+#define EXT2_ERROR_FS 0x0002 /* Errors detected */
+
+/*
+ * Mount flags
+ */
+#define EXT2_MOUNT_CHECK_NORMAL 0x0001 /* Do some more checks */
+#define EXT2_MOUNT_CHECK_STRICT 0x0002 /* Do again more checks */
+#define EXT2_MOUNT_CHECK (EXT2_MOUNT_CHECK_NORMAL | \
+ EXT2_MOUNT_CHECK_STRICT)
+#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */
+#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */
+#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */
+#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */
+#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */
+#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */
+#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */
+
+#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt
+#define set_opt(o, opt) o |= EXT2_MOUNT_##opt
+#define test_opt(sb, opt) ((sb)->u.ext2_sb.s_mount_opt & \
+ EXT2_MOUNT_##opt)
+/*
+ * Maximal mount counts between two filesystem checks
+ */
+#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */
+#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */
+
+/*
+ * Behaviour when detecting errors
+ */
+#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */
+#define EXT2_ERRORS_RO 2 /* Remount fs read-only */
+#define EXT2_ERRORS_PANIC 3 /* Panic */
+#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE
+
+/*
+ * Structure of the super block
+ */
+struct ext2_super_block {
+ __u32 s_inodes_count; /* Inodes count */
+ __u32 s_blocks_count; /* Blocks count */
+ __u32 s_r_blocks_count; /* Reserved blocks count */
+ __u32 s_free_blocks_count; /* Free blocks count */
+ __u32 s_free_inodes_count; /* Free inodes count */
+ __u32 s_first_data_block; /* First Data Block */
+ __u32 s_log_block_size; /* Block size */
+ __s32 s_log_frag_size; /* Fragment size */
+ __u32 s_blocks_per_group; /* # Blocks per group */
+ __u32 s_frags_per_group; /* # Fragments per group */
+ __u32 s_inodes_per_group; /* # Inodes per group */
+ __u32 s_mtime; /* Mount time */
+ __u32 s_wtime; /* Write time */
+ __u16 s_mnt_count; /* Mount count */
+ __s16 s_max_mnt_count; /* Maximal mount count */
+ __u16 s_magic; /* Magic signature */
+ __u16 s_state; /* File system state */
+ __u16 s_errors; /* Behaviour when detecting errors */
+ __u16 s_minor_rev_level; /* minor revision level */
+ __u32 s_lastcheck; /* time of last check */
+ __u32 s_checkinterval; /* max. time between checks */
+ __u32 s_creator_os; /* OS */
+ __u32 s_rev_level; /* Revision level */
+ __u16 s_def_resuid; /* Default uid for reserved blocks */
+ __u16 s_def_resgid; /* Default gid for reserved blocks */
+ /*
+ * These fields are for EXT2_DYNAMIC_REV superblocks only.
+ *
+ * Note: the difference between the compatible feature set and
+ * the incompatible feature set is that if there is a bit set
+ * in the incompatible feature set that the kernel doesn't
+ * know about, it should refuse to mount the filesystem.
+ *
+ * e2fsck's requirements are more strict; if it doesn't know
+ * about a feature in either the compatible or incompatible
+ * feature set, it must abort and not try to meddle with
+ * things it doesn't understand...
+ */
+ __u32 s_first_ino; /* First non-reserved inode */
+ __u16 s_inode_size; /* size of inode structure */
+ __u16 s_block_group_nr; /* block group # of this superblock */
+ __u32 s_feature_compat; /* compatible feature set */
+ __u32 s_feature_incompat; /* incompatible feature set */
+ __u32 s_feature_ro_compat; /* readonly-compatible feature set */
+ __u8 s_uuid[16]; /* 128-bit uuid for volume */
+ char s_volume_name[16]; /* volume name */
+ char s_last_mounted[64]; /* directory where last mounted */
+ __u32 s_algorithm_usage_bitmap; /* For compression */
+ /*
+ * Performance hints. Directory preallocation should only
+ * happen if the EXT2_COMPAT_PREALLOC flag is on.
+ */
+ __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
+ __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
+ __u16 s_padding1;
+ __u32 s_reserved[204]; /* Padding to the end of the block */
+};
+
+#ifdef __KERNEL__
+#define EXT2_SB(sb) (&((sb)->u.ext2_sb))
+#else
+/* Assume that user mode programs are passing in an ext2fs superblock, not
+ * a kernel struct super_block. This will allow us to call the feature-test
+ * macros from user land. */
+#define EXT2_SB(sb) (sb)
+#endif
+
+/*
+ * Codes for operating systems
+ */
+#define EXT2_OS_LINUX 0
+#define EXT2_OS_HURD 1
+#define EXT2_OS_MASIX 2
+#define EXT2_OS_FREEBSD 3
+#define EXT2_OS_LITES 4
+
+/*
+ * Revision levels
+ */
+#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */
+#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */
+
+#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV
+#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV
+
+#define EXT2_GOOD_OLD_INODE_SIZE 128
+
+/*
+ * Feature set definitions
+ */
+
+#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \
+ ( EXT2_SB(sb)->s_feature_compat & (mask) )
+#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \
+ ( EXT2_SB(sb)->s_feature_ro_compat & (mask) )
+#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \
+ ( EXT2_SB(sb)->s_feature_incompat & (mask) )
+
+#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001
+
+#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
+#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
+#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004
+
+#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
+#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
+
+#define EXT2_FEATURE_COMPAT_SUPP 0
+#define EXT2_FEATURE_INCOMPAT_SUPP EXT2_FEATURE_INCOMPAT_FILETYPE
+#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+ EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+
+/*
+ * Default values for user and/or group using reserved blocks
+ */
+#define EXT2_DEF_RESUID 0
+#define EXT2_DEF_RESGID 0
+
+/*
+ * Structure of a directory entry
+ */
+#define EXT2_NAME_LEN 255
+
+struct ext2_dir_entry {
+ __u32 inode; /* Inode number */
+ __u16 rec_len; /* Directory entry length */
+ __u16 name_len; /* Name length */
+ char name[EXT2_NAME_LEN]; /* File name */
+};
+
+/*
+ * The new version of the directory entry. Since EXT2 structures are
+ * stored in intel byte order, and the name_len field could never be
+ * bigger than 255 chars, it's safe to reclaim the extra byte for the
+ * file_type field.
+ */
+struct ext2_dir_entry_2 {
+ __u32 inode; /* Inode number */
+ __u16 rec_len; /* Directory entry length */
+ __u8 name_len; /* Name length */
+ __u8 file_type;
+ char name[EXT2_NAME_LEN]; /* File name */
+};
+
+/*
+ * Ext2 directory file types. Only the low 3 bits are used. The
+ * other bits are reserved for now.
+ */
+#define EXT2_FT_UNKNOWN 0
+#define EXT2_FT_REG_FILE 1
+#define EXT2_FT_DIR 2
+#define EXT2_FT_CHRDEV 3
+#define EXT2_FT_BLKDEV 4
+#define EXT2_FT_FIFO 5
+#define EXT2_FT_SOCK 6
+#define EXT2_FT_SYMLINK 7
+
+#define EXT2_FT_MAX 8
+
+/*
+ * EXT2_DIR_PAD defines the directory entries boundaries
+ *
+ * NOTE: It must be a multiple of 4
+ */
+#define EXT2_DIR_PAD 4
+#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
+#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
+ ~EXT2_DIR_ROUND)
+
+#ifdef __KERNEL__
+/*
+ * Function prototypes
+ */
+
+/*
+ * Ok, these declarations are also in <linux/kernel.h> but none of the
+ * ext2 source programs needs to include it so they are duplicated here.
+ */
+# define NORET_TYPE /**/
+# define ATTRIB_NORET __attribute__((noreturn))
+# define NORET_AND noreturn,
+
+/* acl.c */
+extern int ext2_permission (struct inode *, int);
+
+/* balloc.c */
+extern int ext2_group_sparse(int group);
+extern int ext2_new_block (const struct inode *, unsigned long,
+ __u32 *, __u32 *, int *);
+extern void ext2_free_blocks (const struct inode *, unsigned long,
+ unsigned long);
+extern unsigned long ext2_count_free_blocks (struct super_block *);
+extern void ext2_check_blocks_bitmap (struct super_block *);
+extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
+ unsigned int block_group,
+ struct buffer_head ** bh);
+
+/* bitmap.c */
+extern unsigned long ext2_count_free (struct buffer_head *, unsigned);
+
+/* dir.c */
+extern int ext2_check_dir_entry (const char *, struct inode *,
+ struct ext2_dir_entry_2 *, struct buffer_head *,
+ unsigned long);
+
+/* file.c */
+extern int ext2_read (struct inode *, struct file *, char *, int);
+extern int ext2_write (struct inode *, struct file *, char *, int);
+
+/* fsync.c */
+extern int ext2_sync_file (struct file *, struct dentry *);
+
+/* ialloc.c */
+extern struct inode * ext2_new_inode (const struct inode *, int, int *);
+extern void ext2_free_inode (struct inode *);
+extern unsigned long ext2_count_free_inodes (struct super_block *);
+extern void ext2_check_inodes_bitmap (struct super_block *);
+
+/* inode.c */
+extern long ext2_bmap (struct inode *, long);
+extern int ext2_get_block (struct inode *, long, struct buffer_head *, int);
+
+extern struct buffer_head * ext2_getblk (struct inode *, long, int, int *);
+extern int ext2_getblk_block (struct inode *, long, int, int *, int *);
+extern struct buffer_head * ext2_bread (struct inode *, int, int, int *);
+
+extern int ext2_getcluster (struct inode * inode, long block);
+extern void ext2_read_inode (struct inode *);
+extern void ext2_write_inode (struct inode *);
+extern void ext2_put_inode (struct inode *);
+extern void ext2_delete_inode (struct inode *);
+extern int ext2_sync_inode (struct inode *);
+extern void ext2_discard_prealloc (struct inode *);
+
+/* ioctl.c */
+extern int ext2_ioctl (struct inode *, struct file *, unsigned int,
+ unsigned long);
+
+/* namei.c */
+extern void ext2_release (struct inode *, struct file *);
+extern struct dentry *ext2_lookup (struct inode *, struct dentry *);
+extern int ext2_create (struct inode *,struct dentry *,int);
+extern int ext2_mkdir (struct inode *,struct dentry *,int);
+extern int ext2_rmdir (struct inode *,struct dentry *);
+extern int ext2_unlink (struct inode *,struct dentry *);
+extern int ext2_symlink (struct inode *,struct dentry *,const char *);
+extern int ext2_link (struct dentry *, struct inode *, struct dentry *);
+extern int ext2_mknod (struct inode *, struct dentry *, int, int);
+extern int ext2_rename (struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
+
+/* super.c */
+extern void ext2_error (struct super_block *, const char *, const char *, ...)
+ __attribute__ ((format (printf, 3, 4)));
+extern NORET_TYPE void ext2_panic (struct super_block *, const char *,
+ const char *, ...)
+ __attribute__ ((NORET_AND format (printf, 3, 4)));
+extern void ext2_warning (struct super_block *, const char *, const char *, ...)
+ __attribute__ ((format (printf, 3, 4)));
+extern void ext2_put_super (struct super_block *);
+extern void ext2_write_super (struct super_block *);
+extern int ext2_remount (struct super_block *, int *, char *);
+extern struct super_block * ext2_read_super (struct super_block *,void *,int);
+extern int ext2_statfs (struct super_block *, struct statfs *, int);
+
+/* truncate.c */
+extern void ext2_truncate (struct inode *);
+
+/*
+ * Inodes and files operations
+ */
+
+/* dir.c */
+extern struct inode_operations ext2_dir_inode_operations;
+
+/* file.c */
+extern struct inode_operations ext2_file_inode_operations;
+
+/* symlink.c */
+extern struct inode_operations ext2_symlink_inode_operations;
+extern struct inode_operations ext2_fast_symlink_inode_operations;
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_EXT2_FS_H */
diff --git a/ext2fs/ext2_fs_i.h b/ext2fs/ext2_fs_i.h
new file mode 100644
index 00000000..72bcd5c0
--- /dev/null
+++ b/ext2fs/ext2_fs_i.h
@@ -0,0 +1,42 @@
+/*
+ * linux/include/linux/ext2_fs_i.h
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/include/linux/minix_fs_i.h
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#ifndef _LINUX_EXT2_FS_I
+#define _LINUX_EXT2_FS_I
+
+/*
+ * second extended file system inode data in memory
+ */
+struct ext2_inode_info {
+ __u32 i_data[15];
+ __u32 i_flags;
+ __u32 i_faddr;
+ __u8 i_frag_no;
+ __u8 i_frag_size;
+ __u16 i_osync;
+ __u32 i_file_acl;
+ __u32 i_dir_acl;
+ __u32 i_dtime;
+ __u32 not_used_1; /* FIX: not used/ 2.2 placeholder */
+ __u32 i_block_group;
+ __u32 i_next_alloc_block;
+ __u32 i_next_alloc_goal;
+ __u32 i_prealloc_block;
+ __u32 i_prealloc_count;
+ __u32 i_high_size;
+ int i_new_inode:1; /* Is a freshly allocated inode */
+};
+
+#endif /* _LINUX_EXT2_FS_I */
diff --git a/ext2fs/ext2fs.c b/ext2fs/ext2fs.c
new file mode 100644
index 00000000..128b6edd
--- /dev/null
+++ b/ext2fs/ext2fs.c
@@ -0,0 +1,218 @@
+/* Main entry point for the ext2 file system translator
+
+ Copyright (C) 1994,95,96,97,98,99,2002 Free Software Foundation, Inc.
+
+ Converted for ext2fs by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <device/device.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <error.h>
+#include <argz.h>
+#include <argp.h>
+#include <hurd/store.h>
+#include <version.h>
+#include "ext2fs.h"
+
+/* ---------------------------------------------------------------- */
+
+int diskfs_link_max = EXT2_LINK_MAX;
+int diskfs_name_max = EXT2_NAME_LEN;
+int diskfs_maxsymlinks = 8;
+int diskfs_shortcut_symlink = 1;
+int diskfs_shortcut_chrdev = 1;
+int diskfs_shortcut_blkdev = 1;
+int diskfs_shortcut_fifo = 1;
+int diskfs_shortcut_ifsock = 1;
+
+char *diskfs_server_name = "ext2fs";
+char *diskfs_server_version = HURD_VERSION;
+char *diskfs_extra_version = "GNU Hurd; ext2 " EXT2FS_VERSION;
+
+int diskfs_synchronous;
+
+struct node *diskfs_root_node;
+
+struct store *store;
+struct store_parsed *store_parsed;
+
+char *diskfs_disk_name;
+
+#ifdef EXT2FS_DEBUG
+int ext2_debug_flag;
+#endif
+
+/* Ext2fs-specific options. */
+static const struct argp_option
+options[] =
+{
+ {"debug", 'D', 0, 0, "Toggle debugging output"
+#ifndef EXT2FS_DEBUG
+ " (not compiled in)"
+#endif
+ },
+ {"sblock", 'S', "BLOCKNO", 0,
+ "Use alternate superblock location (1kb blocks)"},
+ {0}
+};
+
+/* Parse a command line option. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ /* We save our parsed values in this structure, hung off STATE->hook.
+ Only after parsing all options successfully will we use these values. */
+ struct
+ {
+ int debug_flag;
+ unsigned int sb_block;
+ } *values = state->hook;
+
+ switch (key)
+ {
+ case 'D':
+ values->debug_flag = 1;
+ break;
+ case 'S':
+ values->sb_block = strtoul (arg, &arg, 0);
+ if (!arg || *arg != '\0')
+ {
+ argp_error (state, "invalid number for --sblock");
+ return EINVAL;
+ }
+ break;
+
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = state->input;
+ values = malloc (sizeof *values);
+ if (values == 0)
+ return ENOMEM;
+ state->hook = values;
+ bzero (values, sizeof *values);
+ values->sb_block = SBLOCK_BLOCK;
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ /* All options parse successfully, so implement ours if possible. */
+ if (values->debug_flag)
+ {
+#ifdef EXT2FS_DEBUG
+ ext2_debug_flag = !ext2_debug_flag;
+#else
+ argp_failure (state, 2, 0, "debugging support not compiled in");
+ return EINVAL;
+#endif
+ }
+
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Override the standard diskfs routine so we can add our own output. */
+error_t
+diskfs_append_args (char **argz, size_t *argz_len)
+{
+ error_t err;
+
+ /* Get the standard things. */
+ err = diskfs_append_std_options (argz, argz_len);
+
+#ifdef EXT2FS_DEBUG
+ if (!err && ext2_debug_flag)
+ err = argz_add (argz, argz_len, "--debug");
+#endif
+ if (! err)
+ err = store_parsed_append_args (store_parsed, argz, argz_len);
+
+ return err;
+}
+
+/* Add our startup arguments to the standard diskfs set. */
+static const struct argp_child startup_children[] =
+ {{&diskfs_store_startup_argp}, {0}};
+static struct argp startup_argp = {options, parse_opt, 0, 0, startup_children};
+
+/* Similarly at runtime. */
+static const struct argp_child runtime_children[] =
+ {{&diskfs_std_runtime_argp}, {0}};
+static struct argp runtime_argp = {options, parse_opt, 0, 0, runtime_children};
+
+struct argp *diskfs_runtime_argp = (struct argp *)&runtime_argp;
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+
+ /* Initialize the diskfs library, parse arguments, and open the store.
+ This starts the first diskfs thread for us. */
+ store = diskfs_init_main (&startup_argp, argc, argv,
+ &store_parsed, &bootstrap);
+
+ if (store->size < SBLOCK_OFFS + SBLOCK_SIZE)
+ ext2_panic ("device too small for superblock (%Ld bytes)", store->size);
+ if (store->log2_blocks_per_page < 0)
+ ext2_panic ("device block size (%zu) greater than page size (%zd)",
+ store->block_size, vm_page_size);
+
+ /* Map the entire disk. */
+ create_disk_pager ();
+
+ pokel_init (&global_pokel, diskfs_disk_pager, disk_cache);
+
+ map_hypermetadata ();
+
+ inode_init ();
+
+ /* Set diskfs_root_node to the root inode. */
+ err = diskfs_cached_lookup (EXT2_ROOT_INO, &diskfs_root_node);
+ if (err)
+ ext2_panic ("can't get root: %s", strerror (err));
+ else if ((diskfs_root_node->dn_stat.st_mode & S_IFMT) == 0)
+ ext2_panic ("no root node!");
+ pthread_mutex_unlock (&diskfs_root_node->lock);
+
+ /* Now that we are all set up to handle requests, and diskfs_root_node is
+ set properly, it is safe to export our fsys control port to the
+ outside world. */
+ diskfs_startup_diskfs (bootstrap, 0);
+
+ /* and so we die, leaving others to do the real work. */
+ pthread_exit (NULL);
+ /* NOTREACHED */
+ return 0;
+}
+
+error_t
+diskfs_reload_global_state ()
+{
+ pokel_flush (&global_pokel);
+ pager_flush (diskfs_disk_pager, 1);
+ sblock = NULL;
+ get_hypermetadata ();
+ map_hypermetadata ();
+ return 0;
+}
diff --git a/ext2fs/ext2fs.h b/ext2fs/ext2fs.h
new file mode 100644
index 00000000..3422af2f
--- /dev/null
+++ b/ext2fs/ext2fs.h
@@ -0,0 +1,574 @@
+/* Common definitions for the ext2 filesystem translator
+
+ Copyright (C) 1995, 1996, 1999, 2002, 2004 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <mach.h>
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/pager.h>
+#include <hurd/fshelp.h>
+#include <hurd/iohelp.h>
+#include <hurd/store.h>
+#include <hurd/diskfs.h>
+#include <hurd/ihash.h>
+#include <assert.h>
+#include <pthread.h>
+#include <sys/mman.h>
+
+#define __hurd__ /* Enable some hurd-specific fields. */
+
+/* Types used by the ext2 header files. */
+typedef u_int32_t __u32;
+typedef int32_t __s32;
+typedef u_int16_t __u16;
+typedef int16_t __s16;
+typedef u_int8_t __u8;
+typedef int8_t __s8;
+
+#include "ext2_fs.h"
+#include "ext2_fs_i.h"
+
+#define i_mode_high osd2.hurd2.h_i_mode_high /* missing from ext2_fs.h */
+
+
+/* If ext2_fs.h defined a debug routine, undef it and use our own. */
+#undef ext2_debug
+
+#ifdef EXT2FS_DEBUG
+extern int ext2_debug_flag;
+#define ext2_debug(f, a...) \
+ do { if (ext2_debug_flag) printf ("ext2fs: (debug) %s: " f "\n", __FUNCTION__ , ## a); } while (0)
+#else
+#define ext2_debug(f, a...) (void)0
+#endif
+
+#undef __hurd__
+
+/* Define this if memory objects should not be cached by the kernel.
+ Normally, don't define it, but defining it causes a much greater rate
+ of paging requests, which may be helpful in catching bugs. */
+
+#undef DONT_CACHE_MEMORY_OBJECTS
+
+int printf (const char *fmt, ...);
+
+/* A block number. */
+typedef __u32 block_t;
+
+/* ---------------------------------------------------------------- */
+
+struct poke
+{
+ vm_offset_t offset;
+ vm_size_t length;
+ struct poke *next;
+};
+
+struct pokel
+{
+ struct poke *pokes, *free_pokes;
+ pthread_spinlock_t lock;
+ struct pager *pager;
+ void *image;
+};
+
+void pokel_init (struct pokel *pokel, struct pager *pager, void *image);
+/* Clean up any state associated with POKEL (but don't free POKEL). */
+void pokel_finalize (struct pokel *pokel);
+
+/* Remember that data here on the disk has been modified. */
+void pokel_add (struct pokel *pokel, void *loc, vm_size_t length);
+
+/* Sync all the modified pieces of disk */
+void pokel_sync (struct pokel *pokel, int wait);
+
+/* Flush (that is, drop on the ground) all pending pokes in POKEL. */
+void pokel_flush (struct pokel *pokel);
+
+/* Transfer all regions from FROM to POKEL, which must have the same pager. */
+void pokel_inherit (struct pokel *pokel, struct pokel *from);
+
+#include <features.h>
+#ifdef EXT2FS_DEFINE_EI
+#define EXT2FS_EI
+#else
+#define EXT2FS_EI __extern_inline
+#endif
+
+/* ---------------------------------------------------------------- */
+/* Bitmap routines. */
+
+#include <stdint.h>
+
+extern int test_bit (unsigned num, char *bitmap);
+
+extern int set_bit (unsigned num, char *bitmap);
+
+#if defined(__USE_EXTERN_INLINES) || defined(EXT2FS_DEFINE_EI)
+/* Returns TRUE if bit NUM is set in BITMAP. */
+EXT2FS_EI int
+test_bit (unsigned num, char *bitmap)
+{
+ const uint32_t *const bw = (uint32_t *) bitmap + (num >> 5);
+ const uint_fast32_t mask = 1 << (num & 31);
+ return *bw & mask;
+}
+
+/* Sets bit NUM in BITMAP, and returns the previous state of the bit. Unlike
+ the linux version, this function is NOT atomic! */
+EXT2FS_EI int
+set_bit (unsigned num, char *bitmap)
+{
+ uint32_t *const bw = (uint32_t *) bitmap + (num >> 5);
+ const uint_fast32_t mask = 1 << (num & 31);
+ return (*bw & mask) ?: (*bw |= mask, 0);
+}
+
+/* Clears bit NUM in BITMAP, and returns the previous state of the bit.
+ Unlike the linux version, this function is NOT atomic! */
+EXT2FS_EI int
+clear_bit (unsigned num, char *bitmap)
+{
+ uint32_t *const bw = (uint32_t *) bitmap + (num >> 5);
+ const uint_fast32_t mask = 1 << (num & 31);
+ return (*bw & mask) ? (*bw &= ~mask, mask) : 0;
+}
+#endif /* Use extern inlines. */
+
+/* ---------------------------------------------------------------- */
+
+/* ext2fs specific per-file data. */
+struct disknode
+{
+ /* For a directory, this array holds the number of directory entries in
+ each DIRBLKSIZE piece of the directory. */
+ int *dirents;
+
+ /* Links on hash list. */
+ struct node *hnext, **hprevp;
+
+ /* Lock to lock while fiddling with this inode's block allocation info. */
+ pthread_rwlock_t alloc_lock;
+
+ /* Where changes to our indirect blocks are added. */
+ struct pokel indir_pokel;
+
+ /* Random extra info used by the ext2 routines. */
+ struct ext2_inode_info info;
+ uint32_t info_i_translator; /* That struct from Linux source lacks this. */
+
+ /* This file's pager. */
+ struct pager *pager;
+
+ /* True if the last page of the file has been made writable, but is only
+ partially allocated. */
+ int last_page_partially_writable;
+
+ /* Index to start a directory lookup at. */
+ int dir_idx;
+};
+
+struct user_pager_info
+{
+ enum pager_type
+ {
+ DISK,
+ FILE_DATA,
+ } type;
+ struct node *node;
+ vm_prot_t max_prot;
+};
+
+/* ---------------------------------------------------------------- */
+/* pager.c */
+
+#define DISK_CACHE_BLOCKS 65536
+
+#include <hurd/diskfs-pager.h>
+
+/* Set up the disk pager. */
+void create_disk_pager (void);
+
+/* Call this when we should turn off caching so that unused memory object
+ ports get freed. */
+void drop_pager_softrefs (struct node *node);
+
+/* Call this when we should turn on caching because it's no longer
+ important for unused memory object ports to get freed. */
+void allow_pager_softrefs (struct node *node);
+
+/* Invalidate any pager data associated with NODE. */
+void flush_node_pager (struct node *node);
+
+/* ---------------------------------------------------------------- */
+
+/* The physical media. */
+extern struct store *store;
+/* What the user specified. */
+extern struct store_parsed *store_parsed;
+
+/* Mapped image of cached blocks of the disk. */
+extern void *disk_cache;
+extern store_offset_t disk_cache_size;
+extern int disk_cache_blocks;
+
+#define DC_INCORE 0x01 /* Not in core. */
+#define DC_UNTOUCHED 0x02 /* Not touched by disk_pager_read_paged
+ or disk_cache_block_ref. */
+#define DC_FIXED 0x04 /* Must not be re-associated. */
+
+/* Flags that forbid re-association of page. DC_UNTOUCHED is included
+ because this flag is used only when page is already to be
+ re-associated, so it's not good candidate for another
+ remapping. */
+#define DC_DONT_REUSE (DC_INCORE | DC_UNTOUCHED | DC_FIXED)
+
+#define DC_NO_BLOCK ((block_t) -1L)
+
+#ifndef NDEBUG
+#define DISK_CACHE_LAST_READ_XOR 0xDEADBEEF
+#endif
+
+/* Disk cache blocks' meta info. */
+struct disk_cache_info
+{
+ block_t block;
+ uint16_t flags;
+ uint16_t ref_count;
+#ifndef NDEBUG
+ block_t last_read, last_read_xor;
+#endif
+};
+
+/* block num --> pointer to in-memory block */
+extern hurd_ihash_t disk_cache_bptr;
+/* Metadata about cached block. */
+extern struct disk_cache_info *disk_cache_info;
+/* Lock for these mappings */
+extern pthread_mutex_t disk_cache_lock;
+/* Fired when a re-association is done. */
+extern pthread_cond_t disk_cache_reassociation;
+
+void *disk_cache_block_ref (block_t block);
+void disk_cache_block_ref_ptr (void *ptr);
+void disk_cache_block_deref (void *ptr);
+int disk_cache_block_is_ref (block_t block);
+
+/* Our in-core copy of the super-block (pointer into the disk_cache). */
+struct ext2_super_block *sblock;
+/* True if sblock has been modified. */
+int sblock_dirty;
+
+/* Where the super-block is located on disk (at min-block 1). */
+#define SBLOCK_BLOCK 1 /* Default location, second 1k block. */
+#define SBLOCK_SIZE (sizeof (struct ext2_super_block))
+extern unsigned int sblock_block; /* Specified location (in 1k blocks). */
+#define SBLOCK_OFFS (sblock_block << 10) /* Byte offset of superblock. */
+
+/* The filesystem block-size. */
+unsigned int block_size;
+/* The log base 2 of BLOCK_SIZE. */
+unsigned int log2_block_size;
+
+/* The number of bits to scale min-blocks to get filesystem blocks. */
+#define BLOCKSIZE_SCALE (sblock->s_log_block_size)
+
+/* log2 of the number of device blocks in a filesystem block. */
+unsigned log2_dev_blocks_per_fs_block;
+
+/* log2 of the number of stat blocks (512 bytes) in a filesystem block. */
+unsigned log2_stat_blocks_per_fs_block;
+
+/* A handy page of page-aligned zeros. */
+vm_address_t zeroblock;
+
+/* Get the superblock from the disk, point `sblock' to it, and setup
+ various global info from it. */
+void get_hypermetadata ();
+
+/* Map `group_desc_image' pointers to disk cache. Also, establish a
+ non-exported mapping to the superblock that will be used by
+ diskfs_set_hypermetadata to update the superblock from the cache
+ `sblock' points to. */
+void map_hypermetadata ();
+
+/* ---------------------------------------------------------------- */
+/* Random stuff calculated from the super block. */
+
+unsigned long frag_size; /* Size of a fragment in bytes */
+unsigned long frags_per_block; /* Number of fragments per block */
+unsigned long inodes_per_block; /* Number of inodes per block */
+
+unsigned long itb_per_group; /* Number of inode table blocks per group */
+unsigned long db_per_group; /* Number of descriptor blocks per group */
+unsigned long desc_per_block; /* Number of group descriptors per block */
+unsigned long addr_per_block; /* Number of disk addresses per block */
+
+unsigned long groups_count; /* Number of groups in the fs */
+
+/* ---------------------------------------------------------------- */
+
+pthread_spinlock_t node_to_page_lock;
+
+pthread_spinlock_t generation_lock;
+unsigned long next_generation;
+
+/* ---------------------------------------------------------------- */
+/* Functions for looking inside disk_cache */
+
+#define trunc_block(offs) \
+ ((off_t) ((offs) >> log2_block_size) << log2_block_size)
+#define round_block(offs) \
+ ((off_t) (((offs) + block_size - 1) >> log2_block_size) << log2_block_size)
+
+/* block num --> byte offset on disk */
+#define boffs(block) ((off_t) (block) << log2_block_size)
+/* byte offset on disk --> block num */
+#define boffs_block(offs) ((offs) >> log2_block_size)
+
+/* pointer to in-memory block -> index in disk_cache_info */
+#define bptr_index(ptr) (((char *)ptr - (char *)disk_cache) >> log2_block_size)
+
+/* byte offset on disk --> pointer to in-memory block */
+EXT2FS_EI char *
+boffs_ptr (off_t offset)
+{
+ block_t block = boffs_block (offset);
+ pthread_mutex_lock (&disk_cache_lock);
+ char *ptr = hurd_ihash_find (disk_cache_bptr, block);
+ pthread_mutex_unlock (&disk_cache_lock);
+ assert (ptr);
+ ptr += offset % block_size;
+ ext2_debug ("(%lld) = %p", offset, ptr);
+ return ptr;
+}
+
+/* pointer to in-memory block --> byte offset on disk */
+EXT2FS_EI off_t
+bptr_offs (void *ptr)
+{
+ vm_offset_t mem_offset = (char *)ptr - (char *)disk_cache;
+ off_t offset;
+ assert (mem_offset < disk_cache_size);
+ pthread_mutex_lock (&disk_cache_lock);
+ offset = (off_t) disk_cache_info[boffs_block (mem_offset)].block
+ << log2_block_size;
+ assert (offset || mem_offset < block_size);
+ offset += mem_offset % block_size;
+ pthread_mutex_unlock (&disk_cache_lock);
+ ext2_debug ("(%p) = %lld", ptr, offset);
+ return offset;
+}
+
+/* block num --> pointer to in-memory block */
+#define bptr(block) boffs_ptr(boffs(block))
+/* pointer to in-memory block --> block num */
+#define bptr_block(ptr) boffs_block(bptr_offs(ptr))
+
+/* Get the descriptor for block group NUM. The block group descriptors are
+ stored starting in the filesystem block following the super block.
+ We cache a pointer into the disk image for easy lookup. */
+#define group_desc(num) (&group_desc_image[num])
+struct ext2_group_desc *group_desc_image;
+
+#define inode_group_num(inum) (((inum) - 1) / sblock->s_inodes_per_group)
+
+extern struct ext2_inode *dino (ino_t inum);
+
+#if defined(__USE_EXTERN_INLINES) || defined(EXT2FS_DEFINE_EI)
+/* Convert an inode number to the dinode on disk. */
+EXT2FS_EI struct ext2_inode *
+dino_ref (ino_t inum)
+{
+ unsigned long inodes_per_group = sblock->s_inodes_per_group;
+ unsigned long bg_num = (inum - 1) / inodes_per_group;
+ unsigned long group_inum = (inum - 1) % inodes_per_group;
+ struct ext2_group_desc *bg = group_desc (bg_num);
+ block_t block = bg->bg_inode_table + (group_inum / inodes_per_block);
+ struct ext2_inode *inode = disk_cache_block_ref (block);
+ inode += group_inum % inodes_per_block;
+ ext2_debug ("(%llu) = %p", inum, inode);
+ return inode;
+}
+
+EXT2FS_EI void
+dino_deref (struct ext2_inode *inode)
+{
+ ext2_debug ("(%p)", inode);
+ disk_cache_block_deref (inode);
+}
+#endif /* Use extern inlines. */
+
+/* ---------------------------------------------------------------- */
+/* inode.c */
+
+/* Write all active disknodes into the inode pager. */
+void write_all_disknodes ();
+
+/* Lookup node INUM (which must have a reference already) and return it
+ without allocating any new references. */
+struct node *ifind (ino_t inum);
+
+void inode_init (void);
+
+/* ---------------------------------------------------------------- */
+
+/* What to lock if changing global data data (e.g., the superblock or block
+ group descriptors or bitmaps). */
+pthread_spinlock_t global_lock;
+
+/* Where to record such changes. */
+struct pokel global_pokel;
+
+/* If the block size is less than the page size, then this bitmap is used to
+ record which disk blocks are actually modified, so we don't stomp on parts
+ of the disk which are backed by file pagers. */
+char *modified_global_blocks;
+pthread_spinlock_t modified_global_blocks_lock;
+
+extern int global_block_modified (block_t block);
+extern void record_global_poke (void *ptr);
+extern void sync_global_ptr (void *bptr, int wait);
+extern void record_indir_poke (struct node *node, void *ptr);
+extern void sync_global (int wait);
+extern void alloc_sync (struct node *np);
+
+#if defined(__USE_EXTERN_INLINES) || defined(EXT2FS_DEFINE_EI)
+/* Marks the global block BLOCK as being modified, and returns true if we
+ think it may have been clean before (but we may not be sure). Note that
+ this isn't enough to cause the block to be synced; you must call
+ record_global_poke to do that. */
+EXT2FS_EI int
+global_block_modified (block_t block)
+{
+ if (modified_global_blocks)
+ {
+ int was_clean;
+ pthread_spin_lock (&modified_global_blocks_lock);
+ was_clean = !set_bit(block, modified_global_blocks);
+ pthread_spin_unlock (&modified_global_blocks_lock);
+ return was_clean;
+ }
+ else
+ return 1;
+}
+
+/* This records a modification to a non-file block. */
+EXT2FS_EI void
+record_global_poke (void *ptr)
+{
+ block_t block = boffs_block (bptr_offs (ptr));
+ void *block_ptr = bptr (block);
+ ext2_debug ("(%p = %p)", ptr, block_ptr);
+ assert (disk_cache_block_is_ref (block));
+ global_block_modified (block);
+ pokel_add (&global_pokel, block_ptr, block_size);
+}
+
+/* This syncs a modification to a non-file block. */
+EXT2FS_EI void
+sync_global_ptr (void *bptr, int wait)
+{
+ block_t block = boffs_block (bptr_offs (bptr));
+ void *block_ptr = bptr (block);
+ ext2_debug ("(%p -> %u)", bptr, block);
+ global_block_modified (block);
+ disk_cache_block_deref (block_ptr);
+ pager_sync_some (diskfs_disk_pager,
+ block_ptr - disk_cache, block_size, wait);
+
+}
+
+/* This records a modification to one of a file's indirect blocks. */
+EXT2FS_EI void
+record_indir_poke (struct node *node, void *ptr)
+{
+ block_t block = boffs_block (bptr_offs (ptr));
+ void *block_ptr = bptr (block);
+ ext2_debug ("(%llu, %p)", node->cache_id, ptr);
+ assert (disk_cache_block_is_ref (block));
+ global_block_modified (block);
+ pokel_add (&node->dn->indir_pokel, block_ptr, block_size);
+}
+
+/* ---------------------------------------------------------------- */
+
+EXT2FS_EI void
+sync_global (int wait)
+{
+ ext2_debug ("%d", wait);
+ pokel_sync (&global_pokel, wait);
+}
+
+/* Sync all allocation information and node NP if diskfs_synchronous. */
+EXT2FS_EI void
+alloc_sync (struct node *np)
+{
+ if (diskfs_synchronous)
+ {
+ if (np)
+ {
+ diskfs_node_update (np, 1);
+ pokel_sync (&np->dn->indir_pokel, 1);
+ }
+ diskfs_set_hypermetadata (1, 0);
+ }
+}
+#endif /* Use extern inlines. */
+
+/* ---------------------------------------------------------------- */
+/* getblk.c */
+
+void ext2_discard_prealloc (struct node *node);
+
+/* Returns in DISK_BLOCK the disk block corresponding to BLOCK in NODE.
+ If there is no such block yet, but CREATE is true, then it is created,
+ otherwise EINVAL is returned. */
+error_t ext2_getblk (struct node *node, block_t block, int create, block_t *disk_block);
+
+block_t ext2_new_block (block_t goal,
+ block_t prealloc_goal,
+ block_t *prealloc_count, block_t *prealloc_block);
+
+void ext2_free_blocks (block_t block, unsigned long count);
+
+/* ---------------------------------------------------------------- */
+
+/* Write disk block ADDR with DATA of LEN bytes, waiting for completion. */
+error_t dev_write_sync (block_t addr, vm_address_t data, long len);
+
+/* Write diskblock ADDR with DATA of LEN bytes; don't bother waiting
+ for completion. */
+error_t dev_write (block_t addr, vm_address_t data, long len);
+
+/* Read disk block ADDR; put the address of the data in DATA; read LEN
+ bytes. Always *DATA should be a full page no matter what. */
+error_t dev_read_sync (block_t addr, vm_address_t *data, long len);
+
+/* ---------------------------------------------------------------- */
+
+#define ext2_error(fmt, args...) _ext2_error (__FUNCTION__, fmt , ##args)
+extern void _ext2_error (const char *, const char *, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+#define ext2_panic(fmt, args...) _ext2_panic (__FUNCTION__, fmt , ##args)
+extern void _ext2_panic (const char *, const char *, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+extern void ext2_warning (const char *, ...)
+ __attribute__ ((format (printf, 1, 2)));
diff --git a/ext2fs/getblk.c b/ext2fs/getblk.c
new file mode 100644
index 00000000..bde66e1c
--- /dev/null
+++ b/ext2fs/getblk.c
@@ -0,0 +1,314 @@
+/* File block to disk block mapping routines
+
+ Copyright (C) 1995,96,99,2000,2004 Free Software Foundation, Inc.
+
+ Converted to work under the hurd by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * linux/fs/ext2/inode.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/inode.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Goal-directed block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
+ */
+
+#include <string.h>
+#include "ext2fs.h"
+
+/*
+ * ext2_discard_prealloc and ext2_alloc_block are atomic wrt. the
+ * superblock in the same manner as are ext2_free_blocks and
+ * ext2_new_block. We just wait on the super rather than locking it
+ * here, since ext2_new_block will do the necessary locking and we
+ * can't block until then.
+ */
+void
+ext2_discard_prealloc (struct node *node)
+{
+#ifdef EXT2_PREALLOCATE
+ if (node->dn->info.i_prealloc_count)
+ {
+ int i = node->dn->info.i_prealloc_count;
+ ext2_debug ("discarding %d prealloced blocks for inode %d",
+ i, node->cache_id);
+ node->dn->info.i_prealloc_count = 0;
+ ext2_free_blocks (node->dn->info.i_prealloc_block, i);
+ }
+#endif
+}
+
+/* Allocate a new block for the file NODE, as close to block GOAL as
+ possible, and return it, or 0 if none could be had. If ZERO is true, then
+ zero the block (and add it to NODE's list of modified indirect blocks). */
+static block_t
+ext2_alloc_block (struct node *node, block_t goal, int zero)
+{
+#ifdef EXT2FS_DEBUG
+ static unsigned long alloc_hits = 0, alloc_attempts = 0;
+#endif
+ block_t result;
+
+#ifdef EXT2_PREALLOCATE
+ if (node->dn->info.i_prealloc_count &&
+ (goal == node->dn->info.i_prealloc_block ||
+ goal + 1 == node->dn->info.i_prealloc_block))
+ {
+ result = node->dn->info.i_prealloc_block++;
+ node->dn->info.i_prealloc_count--;
+ ext2_debug ("preallocation hit (%lu/%lu) => %u",
+ ++alloc_hits, ++alloc_attempts, result);
+ }
+ else
+ {
+ ext2_debug ("preallocation miss (%lu/%lu)",
+ alloc_hits, ++alloc_attempts);
+ ext2_discard_prealloc (node);
+ result = ext2_new_block
+ (goal,
+ S_ISREG (node->dn_stat.st_mode)
+ ? (sblock->s_prealloc_blocks ?: EXT2_DEFAULT_PREALLOC_BLOCKS)
+ : (S_ISDIR (node->dn_stat.st_mode)
+ && EXT2_HAS_COMPAT_FEATURE(sblock,
+ EXT2_FEATURE_COMPAT_DIR_PREALLOC))
+ ? sblock->s_prealloc_dir_blocks
+ : 0,
+ &node->dn->info.i_prealloc_count,
+ &node->dn->info.i_prealloc_block);
+ }
+#else
+ result = ext2_new_block (goal, 0, 0);
+#endif
+
+ if (result && zero)
+ {
+ char *bh = disk_cache_block_ref (result);
+ bzero (bh, block_size);
+ record_indir_poke (node, bh);
+ }
+
+ return result;
+}
+
+static error_t
+inode_getblk (struct node *node, int nr, int create, int zero,
+ block_t new_block, block_t *result)
+{
+ int i;
+ block_t goal = 0;
+#ifdef EXT2FS_DEBUG
+ block_t hint;
+#endif
+
+ assert (0 <= nr && nr < EXT2_N_BLOCKS);
+
+ *result = node->dn->info.i_data[nr];
+ if (*result)
+ return 0;
+
+ if (!create)
+ return EINVAL;
+
+ if (node->dn->info.i_next_alloc_block == new_block)
+ goal = node->dn->info.i_next_alloc_goal;
+
+#ifdef EXT2FS_DEBUG
+ hint = goal;
+#endif
+
+ if (!goal)
+ {
+ for (i = nr - 1; i >= 0; i--)
+ {
+ if (node->dn->info.i_data[i])
+ {
+ goal = node->dn->info.i_data[i];
+ break;
+ }
+ }
+ if (!goal)
+ goal =
+ (node->dn->info.i_block_group * EXT2_BLOCKS_PER_GROUP (sblock))
+ + sblock->s_first_data_block;
+ }
+
+ *result = ext2_alloc_block (node, goal, zero);
+
+ ext2_debug ("%screate, hint = %u, goal = %u => %u",
+ create ? "" : "no", hint, goal, *result);
+
+ if (!*result)
+ return ENOSPC;
+
+ node->dn->info.i_data[nr] = *result;
+
+ node->dn->info.i_next_alloc_block = new_block;
+ node->dn->info.i_next_alloc_goal = *result;
+ node->dn_set_ctime = node->dn_set_mtime = 1;
+ node->dn_stat.st_blocks += 1 << log2_stat_blocks_per_fs_block;
+ node->dn_stat_dirty = 1;
+
+ if (diskfs_synchronous || node->dn->info.i_osync)
+ diskfs_node_update (node, 1);
+
+ return 0;
+}
+
+error_t
+block_getblk (struct node *node, block_t block, int nr, int create, int zero,
+ block_t new_block, block_t *result)
+{
+ int i;
+ block_t goal = 0;
+ block_t *bh = (block_t *)disk_cache_block_ref (block);
+
+ *result = bh[nr];
+ if (*result)
+ {
+ disk_cache_block_deref (bh);
+ return 0;
+ }
+
+ if (!create)
+ {
+ disk_cache_block_deref (bh);
+ return EINVAL;
+ }
+
+ if (node->dn->info.i_next_alloc_block == new_block)
+ goal = node->dn->info.i_next_alloc_goal;
+ if (!goal)
+ {
+ for (i = nr - 1; i >= 0; i--)
+ {
+ if (bh[i])
+ {
+ goal = bh[i];
+ break;
+ }
+ }
+ if (!goal)
+ goal = block;
+ }
+
+ *result = ext2_alloc_block (node, goal, zero);
+ if (!*result)
+ {
+ disk_cache_block_deref (bh);
+ return ENOSPC;
+ }
+
+ bh[nr] = *result;
+
+ if (diskfs_synchronous || node->dn->info.i_osync)
+ sync_global_ptr (bh, 1);
+ else
+ record_indir_poke (node, bh);
+
+ node->dn->info.i_next_alloc_block = new_block;
+ node->dn->info.i_next_alloc_goal = *result;
+ node->dn_set_ctime = node->dn_set_mtime = 1;
+ node->dn_stat.st_blocks += 1 << log2_stat_blocks_per_fs_block;
+ node->dn_stat_dirty = 1;
+
+ return 0;
+}
+
+/* Returns in DISK_BLOCK the disk block corresponding to BLOCK in NODE.
+ If there is no such block yet, but CREATE is true, then it is created,
+ otherwise EINVAL is returned. */
+error_t
+ext2_getblk (struct node *node, block_t block, int create, block_t *disk_block)
+{
+ error_t err;
+ block_t indir, b;
+ unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK (sblock);
+
+ if (block > EXT2_NDIR_BLOCKS + addr_per_block +
+ addr_per_block * addr_per_block +
+ addr_per_block * addr_per_block * addr_per_block)
+ {
+ ext2_warning ("block > big: %u", block);
+ return EIO;
+ }
+ /*
+ * If this is a sequential block allocation, set the next_alloc_block
+ * to this block now so that all the indblock and data block
+ * allocations use the same goal zone
+ */
+
+ ext2_debug ("block = %u, next = %u, goal = %u", block,
+ node->dn->info.i_next_alloc_block,
+ node->dn->info.i_next_alloc_goal);
+
+ if (block == node->dn->info.i_next_alloc_block + 1)
+ {
+ node->dn->info.i_next_alloc_block++;
+ node->dn->info.i_next_alloc_goal++;
+ }
+
+ b = block;
+
+ if (block < EXT2_NDIR_BLOCKS)
+ return inode_getblk (node, block, create, 0, b, disk_block);
+
+ block -= EXT2_NDIR_BLOCKS;
+ if (block < addr_per_block)
+ {
+ err = inode_getblk (node, EXT2_IND_BLOCK, create, 1, b, &indir);
+ if (!err)
+ err = block_getblk (node, indir, block, create, 0, b, disk_block);
+ return err;
+ }
+
+ block -= addr_per_block;
+ if (block < addr_per_block * addr_per_block)
+ {
+ err = inode_getblk (node, EXT2_DIND_BLOCK, create, 1, b, &indir);
+ if (!err)
+ err = block_getblk (node, indir, block / addr_per_block, create, 1,
+ b, &indir);
+ if (!err)
+ err = block_getblk (node, indir, block & (addr_per_block - 1),
+ create, 0, b, disk_block);
+ return err;
+ }
+
+ block -= addr_per_block * addr_per_block;
+ err = inode_getblk (node, EXT2_TIND_BLOCK, create, 1, b, &indir);
+ if (!err)
+ err = block_getblk (node, indir, block / (addr_per_block * addr_per_block),
+ create, 1, b, &indir);
+ if (!err)
+ err =
+ block_getblk (node, indir,
+ (block / addr_per_block) & (addr_per_block - 1),
+ create, 1, b, &indir);
+ if (!err)
+ err = block_getblk (node, indir, block & (addr_per_block - 1), create, 0,
+ b, disk_block);
+
+ return err;
+}
diff --git a/ext2fs/hyper.c b/ext2fs/hyper.c
new file mode 100644
index 00000000..5f288bf1
--- /dev/null
+++ b/ext2fs/hyper.c
@@ -0,0 +1,224 @@
+/* Fetching and storing the hypermetadata (superblock and bg summary info)
+
+ Copyright (C) 1994,95,96,99,2001,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h>
+#include <stdio.h>
+#include <error.h>
+#include <hurd/store.h>
+#include "ext2fs.h"
+
+vm_address_t zeroblock;
+char *modified_global_blocks;
+
+static void
+allocate_mod_map (void)
+{
+ static vm_size_t mod_map_size;
+
+ if (modified_global_blocks && mod_map_size)
+ /* Get rid of the old one. */
+ munmap (modified_global_blocks, mod_map_size);
+
+ if (!diskfs_readonly && block_size < vm_page_size)
+ /* If the block size is too small, we have to take extra care when
+ writing out pages from the global pager, to make sure we don't stomp
+ on any file pager blocks. In this case use a bitmap to record which
+ global blocks are actually modified so the pager can write only them. */
+ {
+ /* One bit per filesystem block. */
+ mod_map_size = sblock->s_blocks_count >> 3;
+ modified_global_blocks = mmap (0, mod_map_size, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ assert (modified_global_blocks != (void *) -1);
+ }
+ else
+ modified_global_blocks = 0;
+}
+
+unsigned int sblock_block = SBLOCK_BLOCK; /* in 1k blocks */
+
+static int ext2fs_clean; /* fs clean before we started writing? */
+
+void
+get_hypermetadata (void)
+{
+ error_t err;
+ size_t read = 0;
+
+ if (sblock != NULL)
+ munmap (sblock, SBLOCK_SIZE);
+
+ err = store_read (store, SBLOCK_OFFS >> store->log2_block_size,
+ SBLOCK_SIZE, (void **)&sblock, &read);
+ if (err || read != SBLOCK_SIZE)
+ ext2_panic ("Cannot read hypermetadata");
+
+ if (sblock->s_magic != EXT2_SUPER_MAGIC
+#ifdef EXT2FS_PRE_02B_COMPAT
+ && sblock->s_magic != EXT2_PRE_02B_MAGIC
+#endif
+ )
+ ext2_panic ("bad magic number %#x (should be %#x)",
+ sblock->s_magic, EXT2_SUPER_MAGIC);
+
+ log2_block_size = EXT2_MIN_BLOCK_LOG_SIZE + sblock->s_log_block_size;
+ block_size = 1 << log2_block_size;
+
+ if (block_size > EXT2_MAX_BLOCK_SIZE)
+ ext2_panic ("block size %d is too big (max is %d bytes)",
+ block_size, EXT2_MAX_BLOCK_SIZE);
+
+ log2_dev_blocks_per_fs_block = log2_block_size - store->log2_block_size;
+ if (log2_dev_blocks_per_fs_block < 0)
+ ext2_panic ("block size %d isn't a power-of-two multiple of the device"
+ " block size (%zd)!",
+ block_size, store->block_size);
+
+ log2_stat_blocks_per_fs_block = 0;
+ while ((512 << log2_stat_blocks_per_fs_block) < block_size)
+ log2_stat_blocks_per_fs_block++;
+ if ((512 << log2_stat_blocks_per_fs_block) != block_size)
+ ext2_panic ("block size %d isn't a power-of-two multiple of 512!",
+ block_size);
+
+ if ((store->size >> log2_block_size) < sblock->s_blocks_count)
+ ext2_panic ("disk size (%qd bytes) too small; superblock says we need %qd",
+ (long long int) store->size,
+ (long long int) sblock->s_blocks_count << log2_block_size);
+ if (log2_dev_blocks_per_fs_block != 0
+ && (store->size & ((1 << log2_dev_blocks_per_fs_block) - 1)) != 0)
+ ext2_warning ("%Ld (%zd byte) device blocks "
+ " unused after last filesystem (%d byte) block",
+ (store->size & ((1 << log2_dev_blocks_per_fs_block) - 1)),
+ store->block_size, block_size);
+
+ /* Set these handy variables. */
+ inodes_per_block = block_size / EXT2_INODE_SIZE (sblock);
+
+ frag_size = EXT2_MIN_FRAG_SIZE << sblock->s_log_frag_size;
+ if (frag_size)
+ frags_per_block = block_size / frag_size;
+ else
+ ext2_panic ("frag size is zero!");
+
+ if (sblock->s_rev_level > EXT2_GOOD_OLD_REV)
+ {
+ if (sblock->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPP)
+ ext2_panic ("could not mount because of unsupported optional features"
+ " (0x%x)",
+ sblock->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPP);
+ if (sblock->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPP)
+ {
+ ext2_warning ("mounted readonly because of"
+ " unsupported optional features (0x%x)",
+ sblock->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPP);
+ diskfs_readonly = 1;
+ }
+ if (sblock->s_inode_size != EXT2_GOOD_OLD_INODE_SIZE)
+ ext2_panic ("inode size %d isn't supported", sblock->s_inode_size);
+ }
+
+ groups_count =
+ ((sblock->s_blocks_count - sblock->s_first_data_block +
+ sblock->s_blocks_per_group - 1)
+ / sblock->s_blocks_per_group);
+
+ itb_per_group = sblock->s_inodes_per_group / inodes_per_block;
+ desc_per_block = block_size / sizeof (struct ext2_group_desc);
+ addr_per_block = block_size / sizeof (block_t);
+ db_per_group = (groups_count + desc_per_block - 1) / desc_per_block;
+
+ ext2fs_clean = sblock->s_state & EXT2_VALID_FS;
+ if (! ext2fs_clean)
+ {
+ ext2_warning ("FILESYSTEM NOT UNMOUNTED CLEANLY; PLEASE fsck");
+ if (! diskfs_readonly)
+ {
+ diskfs_readonly = 1;
+ ext2_warning ("MOUNTED READ-ONLY; MUST USE `fsysopts --writable'");
+ }
+ }
+
+ allocate_mod_map ();
+
+ /* A handy source of page-aligned zeros. */
+ if (zeroblock == 0)
+ {
+ zeroblock = (vm_address_t) mmap (0, block_size, PROT_READ, MAP_ANON, 0, 0);
+ assert (zeroblock != (vm_address_t) MAP_FAILED);
+ }
+}
+
+static struct ext2_super_block *mapped_sblock;
+
+void
+map_hypermetadata (void)
+{
+ mapped_sblock = (struct ext2_super_block *) boffs_ptr (SBLOCK_OFFS);
+
+ /* Cache a convenient pointer to the block group descriptors for allocation.
+ These are stored in the filesystem blocks following the superblock. */
+ group_desc_image =
+ (struct ext2_group_desc *) bptr (bptr_block (mapped_sblock) + 1);
+}
+
+error_t
+diskfs_set_hypermetadata (int wait, int clean)
+{
+ if (clean && ext2fs_clean && !(sblock->s_state & EXT2_VALID_FS))
+ /* The filesystem is clean, so we need to set the clean flag. */
+ {
+ sblock->s_state |= EXT2_VALID_FS;
+ sblock_dirty = 1;
+ }
+ else if (!clean && (sblock->s_state & EXT2_VALID_FS))
+ /* The filesystem just became dirty, so clear the clean flag. */
+ {
+ sblock->s_state &= ~EXT2_VALID_FS;
+ sblock_dirty = 1;
+ wait = 1;
+ }
+
+ if (sblock_dirty)
+ {
+ sblock_dirty = 0;
+ memcpy (mapped_sblock, sblock, SBLOCK_SIZE);
+ disk_cache_block_ref_ptr (mapped_sblock);
+ record_global_poke (mapped_sblock);
+ }
+
+ sync_global (wait);
+
+ /* Should check writability here and return EROFS if necessary. XXX */
+ return 0;
+}
+
+void
+diskfs_readonly_changed (int readonly)
+{
+ allocate_mod_map ();
+
+ (*(readonly ? store_set_flags : store_clear_flags)) (store, STORE_READONLY);
+
+ mprotect (disk_cache, disk_cache_size,
+ PROT_READ | (readonly ? 0 : PROT_WRITE));
+
+ if (!readonly && !(sblock->s_state & EXT2_VALID_FS))
+ ext2_warning ("UNCLEANED FILESYSTEM NOW WRITABLE");
+}
diff --git a/ext2fs/ialloc.c b/ext2fs/ialloc.c
new file mode 100644
index 00000000..52212d59
--- /dev/null
+++ b/ext2fs/ialloc.c
@@ -0,0 +1,429 @@
+/* Inode allocation routines.
+
+ Copyright (C) 1995,96,99,2000,02 Free Software Foundation, Inc.
+
+ Converted to work under the hurd by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * linux/fs/ext2/ialloc.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * BSD ufs-inspired inode and directory allocation by
+ * Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
+ */
+
+/*
+ * The free inodes are managed by bitmaps. A file system contains several
+ * blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap
+ * block for inodes, N blocks for the inode table and data blocks.
+ *
+ * The file system contains group descriptors which are located after the
+ * super block. Each descriptor contains the number of the bitmap block and
+ * the free blocks count in the block. The descriptors are loaded in memory
+ * when a file system is mounted (see ext2_read_super).
+ */
+
+#include "ext2fs.h"
+#include "bitmap.c"
+
+/* ---------------------------------------------------------------- */
+
+/* Free node NP; the on disk copy has already been synced with
+ diskfs_node_update (where NP->dn_stat.st_mode was 0). It's
+ mode used to be OLD_MODE. */
+void
+diskfs_free_node (struct node *np, mode_t old_mode)
+{
+ char *bh;
+ unsigned long block_group;
+ unsigned long bit;
+ struct ext2_group_desc *gdp;
+ ino_t inum = np->cache_id;
+
+ assert (!diskfs_readonly);
+
+ ext2_debug ("freeing inode %u", inum);
+
+ pthread_spin_lock (&global_lock);
+
+ if (inum < EXT2_FIRST_INO (sblock) || inum > sblock->s_inodes_count)
+ {
+ ext2_error ("reserved inode or nonexistent inode: %Ld", inum);
+ pthread_spin_unlock (&global_lock);
+ return;
+ }
+
+ block_group = (inum - 1) / sblock->s_inodes_per_group;
+ bit = (inum - 1) % sblock->s_inodes_per_group;
+
+ gdp = group_desc (block_group);
+ bh = disk_cache_block_ref (gdp->bg_inode_bitmap);
+
+ if (!clear_bit (bit, bh))
+ ext2_warning ("bit already cleared for inode %Ld", inum);
+ else
+ {
+ disk_cache_block_ref_ptr (bh);
+ record_global_poke (bh);
+
+ gdp->bg_free_inodes_count++;
+ if (S_ISDIR (old_mode))
+ gdp->bg_used_dirs_count--;
+ disk_cache_block_ref_ptr (gdp);
+ record_global_poke (gdp);
+
+ sblock->s_free_inodes_count++;
+ }
+
+ disk_cache_block_deref (bh);
+ sblock_dirty = 1;
+ pthread_spin_unlock (&global_lock);
+ alloc_sync(0);
+}
+
+/* ---------------------------------------------------------------- */
+
+/*
+ * There are two policies for allocating an inode. If the new inode is
+ * a directory, then a forward search is made for a block group with both
+ * free space and a low directory-to-inode ratio; if that fails, then of
+ * the groups with above-average free space, that group with the fewest
+ * directories already is chosen.
+ *
+ * For other inodes, search forward from the parent directory\'s block
+ * group to find a free inode.
+ */
+ino_t
+ext2_alloc_inode (ino_t dir_inum, mode_t mode)
+{
+ char *bh = NULL;
+ int i, j, avefreei;
+ ino_t inum;
+ struct ext2_group_desc *gdp;
+ struct ext2_group_desc *tmp;
+
+ pthread_spin_lock (&global_lock);
+
+repeat:
+ assert (bh == NULL);
+ gdp = NULL;
+ i = 0;
+
+ if (S_ISDIR (mode))
+ {
+ avefreei = sblock->s_free_inodes_count / groups_count;
+
+/* I am not yet convinced that this next bit is necessary.
+ i = inode_group_num(dir_inum);
+ for (j = 0; j < groups_count; j++)
+ {
+ tmp = group_desc (i);
+ if ((tmp->bg_used_dirs_count << 8) < tmp->bg_free_inodes_count)
+ {
+ gdp = tmp;
+ break;
+ }
+ else
+ i = ++i % groups_count;
+ }
+ */
+
+ if (!gdp)
+ {
+ for (j = 0; j < groups_count; j++)
+ {
+ tmp = group_desc (j);
+ if (tmp->bg_free_inodes_count
+ && tmp->bg_free_inodes_count >= avefreei)
+ {
+ if (!gdp ||
+ (tmp->bg_free_blocks_count > gdp->bg_free_blocks_count))
+ {
+ i = j;
+ gdp = tmp;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Try to place the inode in its parent directory
+ */
+ i = inode_group_num(dir_inum);
+ tmp = group_desc (i);
+ if (tmp->bg_free_inodes_count)
+ gdp = tmp;
+ else
+ {
+ /*
+ * Use a quadratic hash to find a group with a
+ * free inode
+ */
+ for (j = 1; j < groups_count; j <<= 1)
+ {
+ i += j;
+ if (i >= groups_count)
+ i -= groups_count;
+ tmp = group_desc (i);
+ if (tmp->bg_free_inodes_count)
+ {
+ gdp = tmp;
+ break;
+ }
+ }
+ }
+ if (!gdp)
+ {
+ /*
+ * That failed: try linear search for a free inode
+ */
+ i = inode_group_num(dir_inum) + 1;
+ for (j = 2; j < groups_count; j++)
+ {
+ if (++i >= groups_count)
+ i = 0;
+ tmp = group_desc (i);
+ if (tmp->bg_free_inodes_count)
+ {
+ gdp = tmp;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!gdp)
+ {
+ pthread_spin_unlock (&global_lock);
+ return 0;
+ }
+
+ bh = disk_cache_block_ref (gdp->bg_inode_bitmap);
+ if ((inum =
+ find_first_zero_bit ((unsigned long *) bh, sblock->s_inodes_per_group))
+ < sblock->s_inodes_per_group)
+ {
+ if (set_bit (inum, bh))
+ {
+ ext2_warning ("bit already set for inode %d", inum);
+ disk_cache_block_deref (bh);
+ bh = NULL;
+ goto repeat;
+ }
+ record_global_poke (bh);
+ bh = NULL;
+ }
+ else
+ {
+ disk_cache_block_deref (bh);
+ bh = NULL;
+ if (gdp->bg_free_inodes_count != 0)
+ {
+ ext2_error ("free inodes count corrupted in group %d", i);
+ inum = 0;
+ goto sync_out;
+ }
+ goto repeat;
+ }
+
+ inum += i * sblock->s_inodes_per_group + 1;
+ if (inum < EXT2_FIRST_INO (sblock) || inum > sblock->s_inodes_count)
+ {
+ ext2_error ("reserved inode or inode > inodes count - "
+ "block_group = %d,inode=%d", i, inum);
+ inum = 0;
+ goto sync_out;
+ }
+
+ gdp->bg_free_inodes_count--;
+ if (S_ISDIR (mode))
+ gdp->bg_used_dirs_count++;
+ disk_cache_block_ref_ptr (gdp);
+ record_global_poke (gdp);
+
+ sblock->s_free_inodes_count--;
+ sblock_dirty = 1;
+
+ sync_out:
+ assert (bh == NULL);
+ pthread_spin_unlock (&global_lock);
+ alloc_sync (0);
+
+ /* Make sure the coming read_node won't complain about bad
+ fields. */
+ {
+ struct ext2_inode *di = dino_ref (inum);
+ memset (di, 0, sizeof *di);
+ dino_deref (di);
+ }
+
+ return inum;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* The user must define this function. Allocate a new node to be of
+ mode MODE in locked directory DP (don't actually set the mode or
+ modify the dir, that will be done by the caller); the user
+ responsible for the request can be identified with CRED. Set *NP
+ to be the newly allocated node. */
+error_t
+diskfs_alloc_node (struct node *dir, mode_t mode, struct node **node)
+{
+ error_t err;
+ int sex, block;
+ struct node *np;
+ struct stat *st;
+ ino_t inum;
+
+ assert (!diskfs_readonly);
+
+ inum = ext2_alloc_inode (dir->cache_id, mode);
+
+ if (inum == 0)
+ return ENOSPC;
+
+ err = diskfs_cached_lookup (inum, &np);
+ if (err)
+ return err;
+
+ st = &np->dn_stat;
+
+ if (st->st_blocks)
+ {
+ st->st_blocks = 0;
+ np->dn_set_ctime = 1;
+ }
+ /* Zero out the block pointers in case there's some noise left on disk. */
+ for (block = 0; block < EXT2_N_BLOCKS; block++)
+ if (np->dn->info.i_data[block] != 0)
+ {
+ np->dn->info.i_data[block] = 0;
+ np->dn_set_ctime = 1;
+ }
+ if (np->dn->info_i_translator != 0)
+ {
+ np->dn->info_i_translator = 0;
+ np->dn_set_ctime = 1;
+ }
+ st->st_mode &= ~S_IPTRANS;
+ if (np->allocsize)
+ {
+ st->st_size = 0;
+ np->allocsize = 0;
+ np->dn_set_ctime = 1;
+ }
+
+ /* Propagate initial inode flags from the directory, as Linux does. */
+ np->dn->info.i_flags =
+ ext2_mask_flags(mode, dir->dn->info.i_flags & EXT2_FL_INHERITED);
+
+ st->st_flags = 0;
+
+ /*
+ * Set up a new generation number for this inode.
+ */
+ pthread_spin_lock (&generation_lock);
+ sex = diskfs_mtime->seconds;
+ if (++next_generation < (u_long)sex)
+ next_generation = sex;
+ st->st_gen = next_generation;
+ pthread_spin_unlock (&generation_lock);
+
+ alloc_sync (np);
+
+ *node = np;
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+unsigned long
+ext2_count_free_inodes ()
+{
+#ifdef EXT2FS_DEBUG
+ unsigned long desc_count, bitmap_count, x;
+ struct ext2_group_desc *gdp;
+ int i;
+
+ pthread_spin_lock (&global_lock);
+
+ desc_count = 0;
+ bitmap_count = 0;
+ gdp = NULL;
+ for (i = 0; i < groups_count; i++)
+ {
+ void *bh;
+ gdp = group_desc (i);
+ desc_count += gdp->bg_free_inodes_count;
+ bh = disk_cache_block_ref (gdp->bg_inode_bitmap);
+ x = count_free (bh, sblock->s_inodes_per_group / 8);
+ disk_cache_block_deref (bh);
+ ext2_debug ("group %d: stored = %d, counted = %lu",
+ i, gdp->bg_free_inodes_count, x);
+ bitmap_count += x;
+ }
+ ext2_debug ("stored = %u, computed = %lu, %lu",
+ sblock->s_free_inodes_count, desc_count, bitmap_count);
+ pthread_spin_unlock (&global_lock);
+ return desc_count;
+#else
+ return sblock->s_free_inodes_count;
+#endif
+}
+
+/* ---------------------------------------------------------------- */
+
+void
+ext2_check_inodes_bitmap ()
+{
+ int i;
+ struct ext2_group_desc *gdp;
+ unsigned long desc_count, bitmap_count, x;
+
+ pthread_spin_lock (&global_lock);
+
+ desc_count = 0;
+ bitmap_count = 0;
+ gdp = NULL;
+ for (i = 0; i < groups_count; i++)
+ {
+ void *bh;
+ gdp = group_desc (i);
+ desc_count += gdp->bg_free_inodes_count;
+ bh = disk_cache_block_ref (gdp->bg_inode_bitmap);
+ x = count_free (bh, sblock->s_inodes_per_group / 8);
+ disk_cache_block_deref (bh);
+ if (gdp->bg_free_inodes_count != x)
+ ext2_error ("wrong free inodes count in group %d, "
+ "stored = %d, counted = %lu",
+ i, gdp->bg_free_inodes_count, x);
+ bitmap_count += x;
+ }
+ if (sblock->s_free_inodes_count != bitmap_count)
+ ext2_error ("wrong free inodes count in super block, "
+ "stored = %lu, counted = %lu",
+ (unsigned long) sblock->s_free_inodes_count, bitmap_count);
+
+ pthread_spin_unlock (&global_lock);
+}
diff --git a/ext2fs/inode.c b/ext2fs/inode.c
new file mode 100644
index 00000000..ed782657
--- /dev/null
+++ b/ext2fs/inode.c
@@ -0,0 +1,849 @@
+/* Inode management routines
+
+ Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2007
+ Free Software Foundation, Inc.
+
+ Converted for ext2fs by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ext2fs.h"
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/statvfs.h>
+
+/* these flags aren't actually defined by a header file yet, so temporarily
+ disable them if necessary. */
+#ifndef UF_APPEND
+#define UF_APPEND 0
+#endif
+#ifndef UF_NODUMP
+#define UF_NODUMP 0
+#endif
+#ifndef UF_IMMUTABLE
+#define UF_IMMUTABLE 0
+#endif
+
+#define INOHSZ 512
+#if ((INOHSZ&(INOHSZ-1)) == 0)
+#define INOHASH(ino) ((ino)&(INOHSZ-1))
+#else
+#define INOHASH(ino) (((unsigned)(ino))%INOHSZ)
+#endif
+
+static struct node *nodehash[INOHSZ];
+static size_t nodehash_nr_items;
+
+static error_t read_node (struct node *np);
+
+pthread_spinlock_t generation_lock = PTHREAD_SPINLOCK_INITIALIZER;
+
+/* Initialize the inode hash table. */
+void
+inode_init ()
+{
+ int n;
+ for (n = 0; n < INOHSZ; n++)
+ nodehash[n] = 0;
+}
+
+/* Fetch inode INUM, set *NPP to the node structure;
+ gain one user reference and lock the node. */
+error_t
+diskfs_cached_lookup (ino_t inum, struct node **npp)
+{
+ error_t err;
+ struct node *np;
+ struct disknode *dn;
+
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
+ if (np->cache_id == inum)
+ {
+ np->references++;
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ pthread_mutex_lock (&np->lock);
+ *npp = np;
+ return 0;
+ }
+
+ /* Format specific data for the new node. */
+ dn = malloc (sizeof (struct disknode));
+ if (! dn)
+ {
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ return ENOMEM;
+ }
+ dn->dirents = 0;
+ dn->dir_idx = 0;
+ dn->pager = 0;
+ pthread_rwlock_init (&dn->alloc_lock, NULL);
+ pokel_init (&dn->indir_pokel, diskfs_disk_pager, disk_cache);
+
+ /* Create the new node. */
+ np = diskfs_make_node (dn);
+ np->cache_id = inum;
+
+ pthread_mutex_lock (&np->lock);
+
+ /* Put NP in NODEHASH. */
+ dn->hnext = nodehash[INOHASH(inum)];
+ if (dn->hnext)
+ dn->hnext->dn->hprevp = &dn->hnext;
+ dn->hprevp = &nodehash[INOHASH(inum)];
+ nodehash[INOHASH(inum)] = np;
+ nodehash_nr_items += 1;
+
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+
+ /* Get the contents of NP off disk. */
+ err = read_node (np);
+
+ if (!diskfs_check_readonly () && !np->dn_stat.st_gen)
+ {
+ pthread_spin_lock (&generation_lock);
+ if (++next_generation < diskfs_mtime->seconds)
+ next_generation = diskfs_mtime->seconds;
+ np->dn_stat.st_gen = next_generation;
+ pthread_spin_unlock (&generation_lock);
+ np->dn_set_ctime = 1;
+ }
+
+ if (err)
+ return err;
+ else
+ {
+ *npp = np;
+ return 0;
+ }
+}
+
+/* Lookup node INUM (which must have a reference already) and return it
+ without allocating any new references. */
+struct node *
+ifind (ino_t inum)
+{
+ struct node *np;
+
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
+ {
+ if (np->cache_id != inum)
+ continue;
+
+ assert (np->references);
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ return np;
+ }
+ assert (0);
+}
+
+/* The last reference to a node has gone away; drop
+ it from the hash table and clean all state in the dn structure. */
+void
+diskfs_node_norefs (struct node *np)
+{
+ *np->dn->hprevp = np->dn->hnext;
+ if (np->dn->hnext)
+ np->dn->hnext->dn->hprevp = np->dn->hprevp;
+ nodehash_nr_items -= 1;
+
+ if (np->dn->dirents)
+ free (np->dn->dirents);
+ assert (!np->dn->pager);
+
+ /* Move any pending writes of indirect blocks. */
+ pokel_inherit (&global_pokel, &np->dn->indir_pokel);
+ pokel_finalize (&np->dn->indir_pokel);
+
+ free (np->dn);
+ free (np);
+}
+
+/* The last hard reference to a node has gone away; arrange to have
+ all the weak references dropped that can be. */
+void
+diskfs_try_dropping_softrefs (struct node *np)
+{
+ drop_pager_softrefs (np);
+}
+
+/* The last hard reference to a node has gone away. */
+void
+diskfs_lost_hardrefs (struct node *np)
+{
+}
+
+/* A new hard reference to a node has been created; it's now OK to
+ have unused weak references. */
+void
+diskfs_new_hardrefs (struct node *np)
+{
+ allow_pager_softrefs (np);
+}
+
+/* Read stat information out of the ext2_inode. */
+static error_t
+read_node (struct node *np)
+{
+ error_t err;
+ struct stat *st = &np->dn_stat;
+ struct disknode *dn = np->dn;
+ struct ext2_inode *di;
+ struct ext2_inode_info *info = &dn->info;
+
+ ext2_debug ("(%llu)", np->cache_id);
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ di = dino_ref (np->cache_id);
+
+ st->st_fstype = FSTYPE_EXT2FS;
+ st->st_fsid = getpid (); /* This call is very cheap. */
+ st->st_ino = np->cache_id;
+ st->st_blksize = vm_page_size * 2;
+
+ st->st_nlink = di->i_links_count;
+ st->st_size = di->i_size;
+ st->st_gen = di->i_generation;
+
+ st->st_atim.tv_sec = di->i_atime;
+#ifdef not_yet
+ /* ``struct ext2_inode'' doesn't do better than sec. precision yet. */
+#else
+ st->st_atim.tv_nsec = 0;
+#endif
+ st->st_mtim.tv_sec = di->i_mtime;
+#ifdef not_yet
+ /* ``struct ext2_inode'' doesn't do better than sec. precision yet. */
+#else
+ st->st_mtim.tv_nsec = 0;
+#endif
+ st->st_ctim.tv_sec = di->i_ctime;
+#ifdef not_yet
+ /* ``struct ext2_inode'' doesn't do better than sec. precision yet. */
+#else
+ st->st_ctim.tv_nsec = 0;
+#endif
+
+ st->st_blocks = di->i_blocks;
+
+ st->st_flags = 0;
+ if (di->i_flags & EXT2_APPEND_FL)
+ st->st_flags |= UF_APPEND;
+ if (di->i_flags & EXT2_NODUMP_FL)
+ st->st_flags |= UF_NODUMP;
+ if (di->i_flags & EXT2_IMMUTABLE_FL)
+ st->st_flags |= UF_IMMUTABLE;
+
+ if (sblock->s_creator_os == EXT2_OS_HURD)
+ {
+ st->st_mode = di->i_mode | (di->i_mode_high << 16);
+ st->st_mode &= ~S_ITRANS;
+ if (di->i_translator)
+ st->st_mode |= S_IPTRANS;
+
+ st->st_uid = di->i_uid | (di->i_uid_high << 16);
+ st->st_gid = di->i_gid | (di->i_gid_high << 16);
+
+ st->st_author = di->i_author;
+ if (st->st_author == -1)
+ st->st_author = st->st_uid;
+ }
+ else
+ {
+ st->st_mode = di->i_mode & ~S_ITRANS;
+ st->st_uid = di->i_uid;
+ st->st_gid = di->i_gid;
+ st->st_author = st->st_uid;
+ np->author_tracks_uid = 1;
+ }
+
+ /* Setup the ext2fs auxiliary inode info. */
+ info->i_dtime = di->i_dtime;
+ info->i_flags = di->i_flags;
+ info->i_faddr = di->i_faddr;
+ info->i_frag_no = di->i_frag;
+ info->i_frag_size = di->i_fsize;
+ info->i_osync = 0;
+ info->i_file_acl = di->i_file_acl;
+ if (S_ISDIR (st->st_mode))
+ info->i_dir_acl = di->i_dir_acl;
+ else
+ {
+ info->i_dir_acl = 0;
+ info->i_high_size = di->i_size_high;
+ if (info->i_high_size) /* XXX */
+ {
+ dino_deref (di);
+ ext2_warning ("cannot handle large file inode %Ld", np->cache_id);
+ diskfs_end_catch_exception ();
+ return EFBIG;
+ }
+ }
+ info->i_block_group = inode_group_num (np->cache_id);
+ info->i_next_alloc_block = 0;
+ info->i_next_alloc_goal = 0;
+ info->i_prealloc_count = 0;
+
+ /* Set to a conservative value. */
+ dn->last_page_partially_writable = 0;
+
+ if (S_ISCHR (st->st_mode) || S_ISBLK (st->st_mode))
+ st->st_rdev = di->i_block[0];
+ else
+ {
+ memcpy (info->i_data, di->i_block,
+ EXT2_N_BLOCKS * sizeof info->i_data[0]);
+ st->st_rdev = 0;
+ }
+ dn->info_i_translator = di->i_translator;
+
+ dino_deref (di);
+ diskfs_end_catch_exception ();
+
+ if (S_ISREG (st->st_mode) || S_ISDIR (st->st_mode)
+ || (S_ISLNK (st->st_mode) && st->st_blocks))
+ {
+ unsigned offset;
+
+ np->allocsize = np->dn_stat.st_size;
+
+ /* Round up to a block multiple. */
+ offset = np->allocsize & ((1 << log2_block_size) - 1);
+ if (offset > 0)
+ np->allocsize += block_size - offset;
+ }
+ else
+ /* Allocsize should be zero for anything except directories, files, and
+ long symlinks. These are the only things allowed to have any blocks
+ allocated as well, although st_size may be zero for any type (cases
+ where st_blocks=0 and st_size>0 include fast symlinks, and, under
+ linux, some devices). */
+ np->allocsize = 0;
+
+ return 0;
+}
+
+/* Return EINVAL if this is not a hurd filesystem and any bits are set in L
+ except the low 16 bits, else 0. */
+static inline error_t
+check_high_bits (struct node *np, long l)
+{
+ if (sblock->s_creator_os == EXT2_OS_HURD)
+ return 0;
+
+ /* Linux 2.3.42 has a mount-time option (not a bit stored on disk)
+ NO_UID32 to ignore the high 16 bits of uid and gid, but by default
+ allows them. It also does this check for "interoperability with old
+ kernels". Note that our check refuses to change the values, while
+ Linux 2.3.42 just silently clears the high bits in an inode it updates,
+ even if it was updating it for an unrelated reason. */
+ if (np->dn->info.i_dtime != 0)
+ return 0;
+
+ return ((l & ~0xFFFF) == 0) ? 0 : EINVAL;
+}
+
+/* Return 0 if NP's owner can be changed to UID; otherwise return an error
+ code. */
+error_t
+diskfs_validate_owner_change (struct node *np, uid_t uid)
+{
+ return check_high_bits (np, uid);
+}
+
+/* Return 0 if NP's group can be changed to GID; otherwise return an error
+ code. */
+error_t
+diskfs_validate_group_change (struct node *np, gid_t gid)
+{
+ return check_high_bits (np, gid);
+}
+
+/* Return 0 if NP's mode can be changed to MODE; otherwise return an error
+ code. It must always be possible to clear the mode; diskfs will not ask
+ for permission before doing so. */
+error_t
+diskfs_validate_mode_change (struct node *np, mode_t mode)
+{
+ return check_high_bits (np, mode);
+}
+
+/* Return 0 if NP's author can be changed to AUTHOR; otherwise return an
+ error code. */
+error_t
+diskfs_validate_author_change (struct node *np, uid_t author)
+{
+ if (sblock->s_creator_os == EXT2_OS_HURD)
+ return 0;
+ else
+ /* For non-hurd filesystems, the author & owner are the same. */
+ return (author == np->dn_stat.st_uid) ? 0 : EINVAL;
+}
+
+/* The user may define this function. Return 0 if NP's flags can be
+ changed to FLAGS; otherwise return an error code. It must always
+ be possible to clear the flags. */
+error_t
+diskfs_validate_flags_change (struct node *np, int flags)
+{
+ if (flags & ~(UF_NODUMP | UF_IMMUTABLE | UF_APPEND))
+ return EINVAL;
+ else
+ return 0;
+}
+
+/* Writes everything from NP's inode to the disk image, and returns a pointer
+ to it, or NULL if nothing need be done. */
+static struct ext2_inode *
+write_node (struct node *np)
+{
+ error_t err;
+ struct stat *st = &np->dn_stat;
+ struct ext2_inode *di;
+
+ ext2_debug ("(%llu)", np->cache_id);
+
+ if (np->dn->info.i_prealloc_count)
+ ext2_discard_prealloc (np);
+
+ if (np->dn_stat_dirty)
+ {
+ struct ext2_inode_info *info = &np->dn->info;
+
+ assert (!diskfs_readonly);
+
+ ext2_debug ("writing inode %d to disk", np->cache_id);
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return NULL;
+
+ di = dino_ref (np->cache_id);
+
+ di->i_generation = st->st_gen;
+
+ /* We happen to know that the stat mode bits are the same
+ as the ext2fs mode bits. */
+ /* XXX? */
+
+ /* Only the low 16 bits of these fields are standard across all ext2
+ implementations. */
+ di->i_mode = st->st_mode & 0xFFFF & ~S_ITRANS;
+ di->i_uid = st->st_uid & 0xFFFF;
+ di->i_gid = st->st_gid & 0xFFFF;
+
+ if (sblock->s_creator_os == EXT2_OS_HURD)
+ /* If this is a hurd-compatible filesystem, write the high bits too. */
+ {
+ di->i_mode_high = (st->st_mode >> 16) & 0xffff & ~S_ITRANS;
+ di->i_uid_high = st->st_uid >> 16;
+ di->i_gid_high = st->st_gid >> 16;
+ di->i_author = st->st_author;
+ }
+ else
+ /* No hurd extensions should be turned on. */
+ {
+ assert ((st->st_uid & ~0xFFFF) == 0);
+ assert ((st->st_gid & ~0xFFFF) == 0);
+ assert ((st->st_mode & ~0xFFFF) == 0);
+ assert (np->author_tracks_uid && st->st_author == st->st_uid);
+ }
+
+ di->i_links_count = st->st_nlink;
+
+ di->i_atime = st->st_atim.tv_sec;
+#ifdef not_yet
+ /* ``struct ext2_inode'' doesn't do better than sec. precision yet. */
+ di->i_atime.tv_nsec = st->st_atim.tv_nsec;
+#endif
+ di->i_mtime = st->st_mtim.tv_sec;
+#ifdef not_yet
+ di->i_mtime.tv_nsec = st->st_mtim.tv_nsec;
+#endif
+ di->i_ctime = st->st_ctim.tv_sec;
+#ifdef not_yet
+ di->i_ctime.tv_nsec = st->st_ctim.tv_nsec;
+#endif
+
+ /* Convert generic flags in ST->st_flags to ext2-specific flags in DI
+ (but don't mess with ext2 flags we don't know about). The original
+ set was copied from DI into INFO by read_node, but might have been
+ modified for ext2fs-specific reasons; so we use INFO->i_flags
+ to start with, and then apply the flags in ST->st_flags. */
+ info->i_flags &= ~(EXT2_APPEND_FL | EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL);
+ if (st->st_flags & UF_APPEND)
+ info->i_flags |= EXT2_APPEND_FL;
+ if (st->st_flags & UF_NODUMP)
+ info->i_flags |= EXT2_NODUMP_FL;
+ if (st->st_flags & UF_IMMUTABLE)
+ info->i_flags |= EXT2_IMMUTABLE_FL;
+ di->i_flags = info->i_flags;
+
+ if (st->st_mode == 0)
+ /* Set dtime non-zero to indicate a deleted file.
+ We don't clear i_size, i_blocks, and i_translator in this case,
+ to give "undeletion" utilities a chance. */
+ di->i_dtime = di->i_mtime;
+ else
+ {
+ di->i_dtime = 0;
+ di->i_size = st->st_size;
+ di->i_blocks = st->st_blocks;
+ }
+
+ if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
+ di->i_block[0] = st->st_rdev;
+ else
+ memcpy (di->i_block, np->dn->info.i_data,
+ EXT2_N_BLOCKS * sizeof di->i_block[0]);
+
+ diskfs_end_catch_exception ();
+ np->dn_stat_dirty = 0;
+
+ /* Leave invoking dino_deref (di) to the caller. */
+ return di;
+ }
+ else
+ return NULL;
+}
+
+/* Reload all data specific to NODE from disk, without writing anything.
+ Always called with DISKFS_READONLY true. */
+error_t
+diskfs_node_reload (struct node *node)
+{
+ struct disknode *dn = node->dn;
+
+ if (dn->dirents)
+ {
+ free (dn->dirents);
+ dn->dirents = 0;
+ }
+ pokel_flush (&dn->indir_pokel);
+ flush_node_pager (node);
+ read_node (node);
+
+ return 0;
+}
+
+/* For each active node, call FUN. The node is to be locked around the call
+ to FUN. If FUN returns non-zero for any node, then immediately stop, and
+ return that value. */
+error_t
+diskfs_node_iterate (error_t (*fun)(struct node *))
+{
+ error_t err = 0;
+ int n;
+ size_t num_nodes;
+ struct node *node, **node_list, **p;
+
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+
+ /* We must copy everything from the hash table into another data structure
+ to avoid running into any problems with the hash-table being modified
+ during processing (normally we delegate access to hash-table with
+ diskfs_node_refcnt_lock, but we can't hold this while locking the
+ individual node locks). */
+ num_nodes = nodehash_nr_items;
+
+ /* TODO This method doesn't scale beyond a few dozen nodes and should be
+ replaced. */
+ node_list = malloc (num_nodes * sizeof (struct node *));
+ if (node_list == NULL)
+ {
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ ext2_debug ("unable to allocate temporary node table");
+ return ENOMEM;
+ }
+
+ p = node_list;
+ for (n = 0; n < INOHSZ; n++)
+ for (node = nodehash[n]; node; node = node->dn->hnext)
+ {
+ *p++ = node;
+ node->references++;
+ }
+
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+
+ p = node_list;
+ while (num_nodes-- > 0)
+ {
+ node = *p++;
+ if (!err)
+ {
+ pthread_mutex_lock (&node->lock);
+ err = (*fun)(node);
+ pthread_mutex_unlock (&node->lock);
+ }
+ diskfs_nrele (node);
+ }
+
+ free (node_list);
+ return err;
+}
+
+/* Write all active disknodes into the ext2_inode pager. */
+void
+write_all_disknodes ()
+{
+ error_t write_one_disknode (struct node *node)
+ {
+ struct ext2_inode *di;
+
+ /* Sync the indirect blocks here; they'll all be done before any
+ inodes. Waiting for them shouldn't be too bad. */
+ pokel_sync (&node->dn->indir_pokel, 1);
+
+ diskfs_set_node_times (node);
+
+ /* Update the inode image. */
+ di = write_node (node);
+ if (di)
+ record_global_poke (di);
+
+ return 0;
+ }
+
+ diskfs_node_iterate (write_one_disknode);
+}
+
+/* Sync the info in NP->dn_stat and any associated format-specific
+ information to disk. If WAIT is true, then return only after the
+ physicial media has been completely updated. */
+void
+diskfs_write_disknode (struct node *np, int wait)
+{
+ struct ext2_inode *di = write_node (np);
+ if (di)
+ {
+ if (wait)
+ sync_global_ptr (di, 1);
+ else
+ record_global_poke (di);
+ }
+}
+
+/* Set *ST with appropriate values to reflect the current state of the
+ filesystem. */
+error_t
+diskfs_set_statfs (struct statfs *st)
+{
+ st->f_type = FSTYPE_EXT2FS;
+ st->f_bsize = block_size;
+ st->f_blocks = sblock->s_blocks_count;
+ st->f_bfree = sblock->s_free_blocks_count;
+ st->f_bavail = st->f_bfree - sblock->s_r_blocks_count;
+ if (st->f_bfree < sblock->s_r_blocks_count)
+ st->f_bavail = 0;
+ st->f_files = sblock->s_inodes_count;
+ st->f_ffree = sblock->s_free_inodes_count;
+ st->f_fsid = getpid ();
+ st->f_namelen = 0;
+ st->f_favail = st->f_ffree;
+ st->f_frsize = frag_size;
+ return 0;
+}
+
+/* Implement the diskfs_set_translator callback from the diskfs
+ library; see <hurd/diskfs.h> for the interface description. */
+error_t
+diskfs_set_translator (struct node *np, const char *name, unsigned namelen,
+ struct protid *cred)
+{
+ daddr_t blkno;
+ error_t err;
+ char buf[block_size];
+ struct ext2_inode *di;
+
+ assert (!diskfs_readonly);
+
+ if (sblock->s_creator_os != EXT2_OS_HURD)
+ return EOPNOTSUPP;
+
+ if (namelen + 2 > block_size)
+ return ENAMETOOLONG;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ di = dino_ref (np->cache_id);
+ blkno = di->i_translator;
+
+ if (namelen && !blkno)
+ {
+ /* Allocate block for translator */
+ blkno =
+ ext2_new_block ((np->dn->info.i_block_group
+ * EXT2_BLOCKS_PER_GROUP (sblock))
+ + sblock->s_first_data_block,
+ 0, 0, 0);
+ if (blkno == 0)
+ {
+ dino_deref (di);
+ diskfs_end_catch_exception ();
+ return ENOSPC;
+ }
+
+ di->i_translator = blkno;
+ np->dn->info_i_translator = blkno;
+ record_global_poke (di);
+
+ np->dn_stat.st_blocks += 1 << log2_stat_blocks_per_fs_block;
+ np->dn_set_ctime = 1;
+ }
+ else if (!namelen && blkno)
+ {
+ /* Clear block for translator going away. */
+ di->i_translator = 0;
+ np->dn->info_i_translator = 0;
+ record_global_poke (di);
+ ext2_free_blocks (blkno, 1);
+
+ np->dn_stat.st_blocks -= 1 << log2_stat_blocks_per_fs_block;
+ np->dn_stat.st_mode &= ~S_IPTRANS;
+ np->dn_set_ctime = 1;
+ }
+ else
+ dino_deref (di);
+
+ if (namelen)
+ {
+ void *blkptr;
+
+ buf[0] = namelen & 0xFF;
+ buf[1] = (namelen >> 8) & 0xFF;
+ bcopy (name, buf + 2, namelen);
+
+ blkptr = disk_cache_block_ref (blkno);
+ memcpy (blkptr, buf, block_size);
+ record_global_poke (blkptr);
+
+ np->dn_stat.st_mode |= S_IPTRANS;
+ np->dn_set_ctime = 1;
+ }
+
+ diskfs_end_catch_exception ();
+ return err;
+}
+
+/* Implement the diskfs_get_translator callback from the diskfs library.
+ See <hurd/diskfs.h> for the interface description. */
+error_t
+diskfs_get_translator (struct node *np, char **namep, unsigned *namelen)
+{
+ error_t err = 0;
+ daddr_t blkno;
+ unsigned datalen;
+ void *transloc;
+ struct ext2_inode *di;
+
+ assert (sblock->s_creator_os == EXT2_OS_HURD);
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ di = dino_ref (np->cache_id);
+ blkno = di->i_translator;
+ dino_deref (di);
+ assert (blkno);
+ transloc = disk_cache_block_ref (blkno);
+
+ datalen =
+ ((unsigned char *)transloc)[0] + (((unsigned char *)transloc)[1] << 8);
+ if (datalen > block_size - 2)
+ err = EFTYPE; /* ? */
+ else
+ {
+ *namep = malloc (datalen);
+ if (!*namep)
+ err = ENOMEM;
+ else
+ memcpy (*namep, transloc + 2, datalen);
+ }
+
+ disk_cache_block_deref (transloc);
+ diskfs_end_catch_exception ();
+
+ *namelen = datalen;
+ return err;
+}
+
+/* The maximum size of a symlink store in the inode (including '\0'). */
+#define MAX_INODE_SYMLINK \
+ (EXT2_N_BLOCKS * sizeof (((struct ext2_inode *)0)->i_block[0]))
+
+/* Write an in-inode symlink, or return EINVAL if we can't. */
+static error_t
+write_symlink (struct node *node, const char *target)
+{
+ size_t len = strlen (target) + 1;
+
+ if (len > MAX_INODE_SYMLINK)
+ return EINVAL;
+
+ assert (node->dn_stat.st_blocks == 0);
+
+ bcopy (target, node->dn->info.i_data, len);
+ node->dn_stat.st_size = len - 1;
+ node->dn_set_ctime = 1;
+ node->dn_set_mtime = 1;
+
+ return 0;
+}
+
+/* Read an in-inode symlink, or return EINVAL if we can't. */
+static error_t
+read_symlink (struct node *node, char *target)
+{
+ if (node->dn_stat.st_blocks)
+ return EINVAL;
+
+ assert (node->dn_stat.st_size < MAX_INODE_SYMLINK);
+
+ bcopy (node->dn->info.i_data, target, node->dn_stat.st_size);
+ return 0;
+}
+
+/* If this function is nonzero (and diskfs_shortcut_symlink is set) it
+ is called to set a symlink. If it returns EINVAL or isn't set,
+ then the normal method (writing the contents into the file data) is
+ used. If it returns any other error, it is returned to the user. */
+error_t (*diskfs_create_symlink_hook)(struct node *np, const char *target) =
+ write_symlink;
+
+/* If this function is nonzero (and diskfs_shortcut_symlink is set) it
+ is called to read the contents of a symlink. If it returns EINVAL or
+ isn't set, then the normal method (reading from the file data) is
+ used. If it returns any other error, it is returned to the user. */
+error_t (*diskfs_read_symlink_hook)(struct node *np, char *target) =
+ read_symlink;
+
+/* Called when all hard ports have gone away. */
+void
+diskfs_shutdown_soft_ports ()
+{
+ /* Should initiate termination of internally held pager ports
+ (the only things that should be soft) XXX */
+}
diff --git a/ext2fs/msg.c b/ext2fs/msg.c
new file mode 100644
index 00000000..83939b06
--- /dev/null
+++ b/ext2fs/msg.c
@@ -0,0 +1,89 @@
+/* Message printing functions
+
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
+
+ Converted for ext2fs by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "ext2fs.h"
+
+pthread_mutex_t printf_lock = PTHREAD_MUTEX_INITIALIZER; /* XXX */
+
+
+int printf (const char *fmt, ...)
+{
+ va_list arg;
+ int done;
+ va_start (arg, fmt);
+ pthread_mutex_lock (&printf_lock);
+ done = vprintf (fmt, arg);
+ pthread_mutex_unlock (&printf_lock);
+ va_end (arg);
+ return done;
+}
+
+static char error_buf[1024];
+
+void _ext2_error (const char * function, const char * fmt, ...)
+{
+ va_list args;
+
+ pthread_mutex_lock (&printf_lock);
+
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+
+ fprintf (stderr, "ext2fs: %s: %s: %s\n", diskfs_disk_name, function, error_buf);
+
+ pthread_mutex_unlock (&printf_lock);
+}
+
+void _ext2_panic (const char * function, const char * fmt, ...)
+{
+ va_list args;
+
+ pthread_mutex_lock (&printf_lock);
+
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+
+ fprintf(stderr, "ext2fs: %s: panic: %s: %s\n",
+ diskfs_disk_name, function, error_buf);
+
+ pthread_mutex_unlock (&printf_lock);
+
+ exit (1);
+}
+
+void ext2_warning (const char * fmt, ...)
+{
+ va_list args;
+
+ pthread_mutex_lock (&printf_lock);
+
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+
+ fprintf (stderr, "ext2fs: %s: warning: %s\n", diskfs_disk_name, error_buf);
+
+ pthread_mutex_unlock (&printf_lock);
+}
diff --git a/ext2fs/pager.c b/ext2fs/pager.c
new file mode 100644
index 00000000..39cf1c73
--- /dev/null
+++ b/ext2fs/pager.c
@@ -0,0 +1,1510 @@
+/* Pager for ext2fs
+
+ Copyright (C) 1994,95,96,97,98,99,2000,02 Free Software Foundation, Inc.
+
+ Converted for ext2fs by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <error.h>
+#include <hurd/store.h>
+#include "ext2fs.h"
+
+/* XXX */
+#include "../libpager/priv.h"
+
+/* A ports bucket to hold disk pager ports. */
+struct port_bucket *disk_pager_bucket;
+
+/* A ports bucket to hold file pager ports. */
+struct port_bucket *file_pager_bucket;
+
+pthread_spinlock_t node_to_page_lock = PTHREAD_SPINLOCK_INITIALIZER;
+
+
+#ifdef DONT_CACHE_MEMORY_OBJECTS
+#define MAY_CACHE 0
+#else
+#define MAY_CACHE 1
+#endif
+
+#define STATS
+
+#ifdef STATS
+struct ext2fs_pager_stats
+{
+ pthread_spinlock_t lock;
+
+ unsigned long disk_pageins;
+ unsigned long disk_pageouts;
+
+ unsigned long file_pageins;
+ unsigned long file_pagein_reads; /* Device reads done by file pagein */
+ unsigned long file_pagein_freed_bufs; /* Discarded pages */
+ unsigned long file_pagein_alloced_bufs; /* Allocated pages */
+
+ unsigned long file_pageouts;
+
+ unsigned long file_page_unlocks;
+ unsigned long file_grows;
+};
+
+static struct ext2fs_pager_stats ext2s_pager_stats =
+ { .lock = PTHREAD_SPINLOCK_INITIALIZER };
+
+#define STAT_INC(field) \
+do { pthread_spin_lock (&ext2s_pager_stats.lock); \
+ ext2s_pager_stats.field++; \
+ pthread_spin_unlock (&ext2s_pager_stats.lock); } while (0)
+
+#else /* !STATS */
+#define STAT_INC(field) /* nop */0
+#endif /* STATS */
+
+#define FREE_PAGE_BUFS 24
+
+/* Returns a single page page-aligned buffer. */
+static void *
+get_page_buf ()
+{
+ static pthread_mutex_t free_page_bufs_lock = PTHREAD_MUTEX_INITIALIZER;
+ static void *free_page_bufs;
+ static int num_free_page_bufs;
+ void *buf;
+
+ pthread_mutex_lock (&free_page_bufs_lock);
+ if (num_free_page_bufs > 0)
+ {
+ buf = free_page_bufs;
+ num_free_page_bufs --;
+ if (num_free_page_bufs > 0)
+ free_page_bufs += vm_page_size;
+#ifndef NDEBUG
+ else
+ free_page_bufs = 0;
+#endif /* ! NDEBUG */
+ }
+ else
+ {
+ assert (free_page_bufs == 0);
+ buf = mmap (0, vm_page_size * FREE_PAGE_BUFS,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (buf == MAP_FAILED)
+ buf = 0;
+ else
+ {
+ free_page_bufs = buf + vm_page_size;
+ num_free_page_bufs = FREE_PAGE_BUFS - 1;
+ }
+ }
+
+ pthread_mutex_unlock (&free_page_bufs_lock);
+ return buf;
+}
+
+/* Frees a block returned by get_page_buf. */
+static inline void
+free_page_buf (void *buf)
+{
+ munmap (buf, vm_page_size);
+}
+
+/* Find the location on disk of page OFFSET in NODE. Return the disk block
+ in BLOCK (if unallocated, then return 0). If *LOCK is 0, then a reader
+ lock is acquired on NODE's ALLOC_LOCK before doing anything, and left
+ locked after the return -- even if an error is returned. 0 is returned
+ on success otherwise an error code. */
+static error_t
+find_block (struct node *node, vm_offset_t offset,
+ block_t *block, pthread_rwlock_t **lock)
+{
+ error_t err;
+
+ if (!*lock)
+ {
+ *lock = &node->dn->alloc_lock;
+ pthread_rwlock_rdlock (*lock);
+ }
+
+ if (offset + block_size > node->allocsize)
+ return EIO;
+
+ err = ext2_getblk (node, offset >> log2_block_size, 0, block);
+ if (err == EINVAL)
+ /* Don't barf yet if the node is unallocated. */
+ {
+ *block = 0;
+ err = 0;
+ }
+
+ return err;
+}
+
+/* Read one page for the pager backing NODE at offset PAGE, into BUF. This
+ may need to read several filesystem blocks to satisfy one page, and tries
+ to consolidate the i/o if possible. */
+static error_t
+file_pager_read_page (struct node *node, vm_offset_t page,
+ void **buf, int *writelock)
+{
+ error_t err;
+ int offs = 0;
+ int partial = 0; /* A page truncated by the EOF. */
+ pthread_rwlock_t *lock = NULL;
+ int left = vm_page_size;
+ block_t pending_blocks = 0;
+ int num_pending_blocks = 0;
+
+ ext2_debug ("reading inode %llu page %lu[%u]",
+ node->cache_id, page, vm_page_size);
+
+ /* Read the NUM_PENDING_BLOCKS blocks in PENDING_BLOCKS, into the buffer
+ pointed to by BUF (allocating it if necessary) at offset OFFS. OFFS in
+ adjusted by the amount read, and NUM_PENDING_BLOCKS is zeroed. Any read
+ error is returned. */
+ error_t do_pending_reads ()
+ {
+ if (num_pending_blocks > 0)
+ {
+ store_offset_t dev_block = (store_offset_t) pending_blocks
+ << log2_dev_blocks_per_fs_block;
+ size_t amount = num_pending_blocks << log2_block_size;
+ /* The buffer we try to read into; on the first read, we pass in a
+ size of zero, so that the read is guaranteed to allocate a new
+ buffer, otherwise, we try to read directly into the tail of the
+ buffer we've already got. */
+ void *new_buf = *buf + offs;
+ size_t new_len = offs == 0 ? 0 : vm_page_size - offs;
+
+ STAT_INC (file_pagein_reads);
+
+ err = store_read (store, dev_block, amount, &new_buf, &new_len);
+ if (err)
+ return err;
+ else if (amount != new_len)
+ return EIO;
+
+ if (new_buf != *buf + offs)
+ {
+ /* The read went into a different buffer than the one we
+ passed. */
+ if (offs == 0)
+ /* First read, make the returned page be our buffer. */
+ *buf = new_buf;
+ else
+ /* We've already got some buffer, so copy into it. */
+ {
+ bcopy (new_buf, *buf + offs, new_len);
+ free_page_buf (new_buf); /* Return NEW_BUF to our pool. */
+ STAT_INC (file_pagein_freed_bufs);
+ }
+ }
+
+ offs += new_len;
+ num_pending_blocks = 0;
+ }
+
+ return 0;
+ }
+
+ STAT_INC (file_pageins);
+
+ *writelock = 0;
+
+ if (page >= node->allocsize)
+ {
+ err = EIO;
+ left = 0;
+ }
+ else if (page + left > node->allocsize)
+ {
+ left = node->allocsize - page;
+ partial = 1;
+ }
+
+ while (left > 0)
+ {
+ block_t block;
+
+ err = find_block (node, page, &block, &lock);
+ if (err)
+ break;
+
+ if (block != pending_blocks + num_pending_blocks)
+ {
+ err = do_pending_reads ();
+ if (err)
+ break;
+ pending_blocks = block;
+ }
+
+ if (block == 0)
+ /* Reading unallocated block, just make a zero-filled one. */
+ {
+ *writelock = 1;
+ if (offs == 0)
+ /* No page allocated to read into yet. */
+ {
+ *buf = get_page_buf ();
+ if (! *buf)
+ break;
+ STAT_INC (file_pagein_alloced_bufs);
+ }
+ bzero (*buf + offs, block_size);
+ offs += block_size;
+ }
+ else
+ num_pending_blocks++;
+
+ page += block_size;
+ left -= block_size;
+ }
+
+ if (!err && num_pending_blocks > 0)
+ err = do_pending_reads();
+
+ if (!err && partial && !*writelock)
+ node->dn->last_page_partially_writable = 1;
+
+ if (lock)
+ pthread_rwlock_unlock (lock);
+
+ return err;
+}
+
+struct pending_blocks
+{
+ /* The block number of the first of the blocks. */
+ block_t block;
+ /* How many blocks we have. */
+ off_t num;
+ /* A (page-aligned) buffer pointing to the data we're dealing with. */
+ void *buf;
+ /* And an offset into BUF. */
+ int offs;
+};
+
+/* Write the any pending blocks in PB. */
+static error_t
+pending_blocks_write (struct pending_blocks *pb)
+{
+ if (pb->num > 0)
+ {
+ error_t err;
+ store_offset_t dev_block = (store_offset_t) pb->block
+ << log2_dev_blocks_per_fs_block;
+ size_t length = pb->num << log2_block_size, amount;
+
+ ext2_debug ("writing block %u[%ld]", pb->block, pb->num);
+
+ if (pb->offs > 0)
+ /* Put what we're going to write into a page-aligned buffer. */
+ {
+ void *page_buf = get_page_buf ();
+ bcopy (pb->buf + pb->offs, (void *)page_buf, length);
+ err = store_write (store, dev_block, page_buf, length, &amount);
+ free_page_buf (page_buf);
+ }
+ else
+ err = store_write (store, dev_block, pb->buf, length, &amount);
+ if (err)
+ return err;
+ else if (amount != length)
+ return EIO;
+
+ pb->offs += length;
+ pb->num = 0;
+ }
+
+ return 0;
+}
+
+static void
+pending_blocks_init (struct pending_blocks *pb, void *buf)
+{
+ pb->buf = buf;
+ pb->block = 0;
+ pb->num = 0;
+ pb->offs = 0;
+}
+
+/* Skip writing the next block in PB's buffer (writing out any previous
+ blocks if necessary). */
+static error_t
+pending_blocks_skip (struct pending_blocks *pb)
+{
+ error_t err = pending_blocks_write (pb);
+ pb->offs += block_size;
+ return err;
+}
+
+/* Add the disk block BLOCK to the list of destination disk blocks pending in
+ PB. */
+static error_t
+pending_blocks_add (struct pending_blocks *pb, block_t block)
+{
+ if (block != pb->block + pb->num)
+ {
+ error_t err = pending_blocks_write (pb);
+ if (err)
+ return err;
+ pb->block = block;
+ }
+ pb->num++;
+ return 0;
+}
+
+/* Write one page for the pager backing NODE, at OFFSET, into BUF. This
+ may need to write several filesystem blocks to satisfy one page, and tries
+ to consolidate the i/o if possible. */
+static error_t
+file_pager_write_page (struct node *node, vm_offset_t offset, void *buf)
+{
+ error_t err = 0;
+ struct pending_blocks pb;
+ pthread_rwlock_t *lock = &node->dn->alloc_lock;
+ block_t block;
+ int left = vm_page_size;
+
+ pending_blocks_init (&pb, buf);
+
+ /* Holding NODE->dn->alloc_lock effectively locks NODE->allocsize,
+ at least for the cases we care about: pager_unlock_page,
+ diskfs_grow and diskfs_truncate. */
+ pthread_rwlock_rdlock (&node->dn->alloc_lock);
+
+ if (offset >= node->allocsize)
+ left = 0;
+ else if (offset + left > node->allocsize)
+ left = node->allocsize - offset;
+
+ ext2_debug ("writing inode %d page %d[%d]", node->cache_id, offset, left);
+
+ STAT_INC (file_pageouts);
+
+ while (left > 0)
+ {
+ err = find_block (node, offset, &block, &lock);
+ if (err)
+ break;
+ assert (block);
+ pending_blocks_add (&pb, block);
+ offset += block_size;
+ left -= block_size;
+ }
+
+ if (!err)
+ pending_blocks_write (&pb);
+
+ pthread_rwlock_unlock (&node->dn->alloc_lock);
+
+ return err;
+}
+
+static error_t
+disk_pager_read_page (vm_offset_t page, void **buf, int *writelock)
+{
+ error_t err;
+ size_t length = vm_page_size, read = 0;
+ store_offset_t offset = page, dev_end = store->size;
+ int index = offset >> log2_block_size;
+
+ pthread_mutex_lock (&disk_cache_lock);
+ offset = ((store_offset_t) disk_cache_info[index].block << log2_block_size)
+ + offset % block_size;
+ disk_cache_info[index].flags |= DC_INCORE;
+ disk_cache_info[index].flags &=~ DC_UNTOUCHED;
+#ifndef NDEBUG
+ disk_cache_info[index].last_read = disk_cache_info[index].block;
+ disk_cache_info[index].last_read_xor
+ = disk_cache_info[index].block ^ DISK_CACHE_LAST_READ_XOR;
+#endif
+ pthread_mutex_unlock (&disk_cache_lock);
+
+ ext2_debug ("(%lld)", offset >> log2_block_size);
+
+ if (offset + vm_page_size > dev_end)
+ length = dev_end - offset;
+
+ err = store_read (store, offset >> store->log2_block_size, length,
+ buf, &read);
+ if (read != length)
+ return EIO;
+ if (!err && length != vm_page_size)
+ bzero ((void *)(*buf + length), vm_page_size - length);
+
+ *writelock = 0;
+
+ return err;
+}
+
+static error_t
+disk_pager_write_page (vm_offset_t page, void *buf)
+{
+ error_t err = 0;
+ size_t length = vm_page_size, amount;
+ store_offset_t offset = page, dev_end = store->size;
+ int index = offset >> log2_block_size;
+
+ pthread_mutex_lock (&disk_cache_lock);
+ assert (disk_cache_info[index].block != DC_NO_BLOCK);
+ offset = ((store_offset_t) disk_cache_info[index].block << log2_block_size)
+ + offset % block_size;
+#ifndef NDEBUG /* Not strictly needed. */
+ assert ((disk_cache_info[index].last_read ^ DISK_CACHE_LAST_READ_XOR)
+ == disk_cache_info[index].last_read_xor);
+ assert (disk_cache_info[index].last_read
+ == disk_cache_info[index].block);
+#endif
+ pthread_mutex_unlock (&disk_cache_lock);
+
+ if (offset + vm_page_size > dev_end)
+ length = dev_end - offset;
+
+ ext2_debug ("writing disk page %lld[%zu]", offset, length);
+
+ STAT_INC (disk_pageouts);
+
+ if (modified_global_blocks)
+ /* Be picky about which blocks in a page that we write. */
+ {
+ struct pending_blocks pb;
+
+ pending_blocks_init (&pb, buf);
+
+ while (length > 0 && !err)
+ {
+ block_t block = boffs_block (offset);
+
+ /* We don't clear the block modified bit here because this paging
+ write request may not be the same one that actually set the bit,
+ and our copy of the page may be out of date; we have to leave
+ the bit on in case a paging write request corresponding to the
+ modification comes along later. The bit is only actually ever
+ cleared if the block is allocated to a file, so this results in
+ excess writes of blocks from modified pages. Unfortunately I
+ know of no way to get arount this given the current external
+ paging interface. XXXX */
+ if (test_bit (block, modified_global_blocks))
+ /* This block may have been modified, so write it out. */
+ err = pending_blocks_add (&pb, block);
+ else
+ /* Otherwise just skip it. */
+ err = pending_blocks_skip (&pb);
+
+ offset += block_size;
+ length -= block_size;
+ }
+
+ if (!err)
+ err = pending_blocks_write (&pb);
+ }
+ else
+ {
+ err = store_write (store, offset >> store->log2_block_size,
+ buf, length, &amount);
+ if (!err && length != amount)
+ err = EIO;
+ }
+
+ return err;
+}
+
+static void
+disk_pager_notify_evict (vm_offset_t page)
+{
+ unsigned long index = page >> log2_block_size;
+
+ ext2_debug ("(block %lu)", index);
+
+ pthread_mutex_lock (&disk_cache_lock);
+ disk_cache_info[index].flags &= ~DC_INCORE;
+ pthread_mutex_unlock (&disk_cache_lock);
+}
+
+/* Satisfy a pager read request for either the disk pager or file pager
+ PAGER, to the page at offset PAGE into BUF. WRITELOCK should be set if
+ the pager should make the page writeable. */
+error_t
+pager_read_page (struct user_pager_info *pager, vm_offset_t page,
+ vm_address_t *buf, int *writelock)
+{
+ if (pager->type == DISK)
+ return disk_pager_read_page (page, (void **)buf, writelock);
+ else
+ return file_pager_read_page (pager->node, page, (void **)buf, writelock);
+}
+
+/* Satisfy a pager write request for either the disk pager or file pager
+ PAGER, from the page at offset PAGE from BUF. */
+error_t
+pager_write_page (struct user_pager_info *pager, vm_offset_t page,
+ vm_address_t buf)
+{
+ if (pager->type == DISK)
+ return disk_pager_write_page (page, (void *)buf);
+ else
+ return file_pager_write_page (pager->node, page, (void *)buf);
+}
+
+void
+pager_notify_evict (struct user_pager_info *pager, vm_offset_t page)
+{
+ if (pager->type == DISK)
+ disk_pager_notify_evict (page);
+}
+
+
+/* Make page PAGE writable, at least up to ALLOCSIZE. This function and
+ diskfs_grow are the only places that blocks are actually added to the
+ file. */
+error_t
+pager_unlock_page (struct user_pager_info *pager, vm_offset_t page)
+{
+ if (pager->type == DISK)
+ return 0;
+ else
+ {
+ error_t err;
+ volatile int partial_page;
+ struct node *node = pager->node;
+ struct disknode *dn = node->dn;
+
+ pthread_rwlock_wrlock (&dn->alloc_lock);
+
+ partial_page = (page + vm_page_size > node->allocsize);
+
+ err = diskfs_catch_exception ();
+ if (!err)
+ {
+ block_t block = page >> log2_block_size;
+ int left = (partial_page ? node->allocsize - page : vm_page_size);
+
+ while (left > 0)
+ {
+ block_t disk_block;
+ err = ext2_getblk (node, block++, 1, &disk_block);
+ if (err)
+ break;
+ left -= block_size;
+ }
+ }
+ diskfs_end_catch_exception ();
+
+ if (partial_page)
+ /* If an error occurred, this page still isn't writable; otherwise,
+ since it's at the end of the file, it's now partially writable. */
+ dn->last_page_partially_writable = !err;
+ else if (page + vm_page_size == node->allocsize)
+ /* This makes the last page writable, which ends exactly at the end
+ of the file. If any error occurred, the page still isn't
+ writable, and if not, then the whole thing is writable. */
+ dn->last_page_partially_writable = 0;
+
+#ifdef EXT2FS_DEBUG
+ if (dn->last_page_partially_writable)
+ ext2_debug ("made page %u[%lu] in inode %d partially writable",
+ page, node->allocsize - page, node->cache_id);
+ else
+ ext2_debug ("made page %u[%u] in inode %d writable",
+ page, vm_page_size, node->cache_id);
+#endif
+
+ STAT_INC (file_page_unlocks);
+
+ pthread_rwlock_unlock (&dn->alloc_lock);
+
+ if (err == ENOSPC)
+ ext2_warning ("This filesystem is out of space, and will now crash. Bye!");
+ else if (err)
+ ext2_warning ("inode=%Ld, page=0x%zx: %s",
+ node->cache_id, page, strerror (err));
+
+ return err;
+ }
+}
+
+/* Grow the disk allocated to locked node NODE to be at least SIZE bytes, and
+ set NODE->allocsize to the actual allocated size. (If the allocated size
+ is already SIZE bytes, do nothing.) CRED identifies the user responsible
+ for the call. */
+error_t
+diskfs_grow (struct node *node, off_t size, struct protid *cred)
+{
+ diskfs_check_readonly ();
+ assert (!diskfs_readonly);
+
+ if (size > node->allocsize)
+ {
+ error_t err = 0;
+ off_t old_size;
+ volatile off_t new_size;
+ volatile block_t end_block;
+ block_t new_end_block;
+ struct disknode *dn = node->dn;
+
+ pthread_rwlock_wrlock (&dn->alloc_lock);
+
+ old_size = node->allocsize;
+ new_size = round_block (size);
+
+ /* The first unallocated blocks after the old and new ends of the
+ file, respectively. */
+ end_block = old_size >> log2_block_size;
+ new_end_block = new_size >> log2_block_size;
+
+ if (new_end_block > end_block)
+ {
+ /* The first block of the first unallocate page after the old end
+ of the file. If LAST_PAGE_PARTIALLY_WRITABLE is true, any
+ blocks between this and END_BLOCK were unallocated, but are
+ considered `unlocked' -- that is pager_unlock_page has been
+ called on the page they're in. Since after this grow the pager
+ will expect them to be writable, we'd better allocate them. */
+ block_t old_page_end_block =
+ round_page (old_size) >> log2_block_size;
+
+ ext2_debug ("growing inode %d to %lu bytes (from %lu)", node->cache_id,
+ new_size, old_size);
+
+ if (dn->last_page_partially_writable
+ && old_page_end_block > end_block)
+ {
+ volatile block_t writable_end =
+ (old_page_end_block > new_end_block
+ ? new_end_block
+ : old_page_end_block);
+
+ ext2_debug ("extending writable page %u by %d blocks"
+ "; first new block = %u",
+ trunc_page (old_size),
+ writable_end - end_block,
+ end_block);
+
+ err = diskfs_catch_exception ();
+ while (!err && end_block < writable_end)
+ {
+ block_t disk_block;
+ err = ext2_getblk (node, end_block++, 1, &disk_block);
+ }
+ diskfs_end_catch_exception ();
+
+ if (! err)
+ /* Reflect how much we allocated successfully. */
+ new_size = end_block << log2_block_size;
+ else
+ /* See if it's still valid to say this. */
+ dn->last_page_partially_writable =
+ (old_page_end_block > end_block);
+ }
+ }
+
+ STAT_INC (file_grows);
+
+ ext2_debug ("new size: %ld%s.", new_size,
+ dn->last_page_partially_writable
+ ? " (last page writable)": "");
+ if (err)
+ ext2_warning ("inode=%Ld, target=%Ld: %s",
+ node->cache_id, new_size, strerror (err));
+
+ node->allocsize = new_size;
+
+ pthread_rwlock_unlock (&dn->alloc_lock);
+
+ return err;
+ }
+ else
+ return 0;
+}
+
+/* This syncs a single file (NODE) to disk. Wait for all I/O to complete
+ if WAIT is set. NODE->lock must be held. */
+void
+diskfs_file_update (struct node *node, int wait)
+{
+ struct pager *pager;
+
+ pthread_spin_lock (&node_to_page_lock);
+ pager = node->dn->pager;
+ if (pager)
+ ports_port_ref (pager);
+ pthread_spin_unlock (&node_to_page_lock);
+
+ if (pager)
+ {
+ pager_sync (pager, wait);
+ ports_port_deref (pager);
+ }
+
+ pokel_sync (&node->dn->indir_pokel, wait);
+
+ diskfs_node_update (node, wait);
+}
+
+/* Invalidate any pager data associated with NODE. */
+void
+flush_node_pager (struct node *node)
+{
+ struct pager *pager;
+ struct disknode *dn = node->dn;
+
+ pthread_spin_lock (&node_to_page_lock);
+ pager = dn->pager;
+ if (pager)
+ ports_port_ref (pager);
+ pthread_spin_unlock (&node_to_page_lock);
+
+ if (pager)
+ {
+ pager_flush (pager, 1);
+ ports_port_deref (pager);
+ }
+}
+
+
+/* Return in *OFFSET and *SIZE the minimum valid address the pager will
+ accept and the size of the object. */
+inline error_t
+pager_report_extent (struct user_pager_info *pager,
+ vm_address_t *offset, vm_size_t *size)
+{
+ assert (pager->type == DISK || pager->type == FILE_DATA);
+
+ *offset = 0;
+
+ if (pager->type == DISK)
+ *size = store->size;
+ else
+ *size = pager->node->allocsize;
+
+ return 0;
+}
+
+/* This is called when a pager is being deallocated after all extant send
+ rights have been destroyed. */
+void
+pager_clear_user_data (struct user_pager_info *upi)
+{
+ if (upi->type == FILE_DATA)
+ {
+ struct pager *pager;
+
+ pthread_spin_lock (&node_to_page_lock);
+ pager = upi->node->dn->pager;
+ if (pager && pager_get_upi (pager) == upi)
+ upi->node->dn->pager = 0;
+ pthread_spin_unlock (&node_to_page_lock);
+
+ diskfs_nrele_light (upi->node);
+ }
+
+ free (upi);
+}
+
+/* This will be called when the ports library wants to drop weak references.
+ The pager library creates no weak references itself. If the user doesn't
+ either, then it's OK for this function to do nothing. */
+void
+pager_dropweak (struct user_pager_info *p __attribute__ ((unused)))
+{
+}
+
+/* Cached blocks from disk. */
+void *disk_cache;
+
+/* DISK_CACHE size in bytes and blocks. */
+store_offset_t disk_cache_size;
+int disk_cache_blocks;
+
+/* block num --> pointer to in-memory block */
+hurd_ihash_t disk_cache_bptr;
+/* Cached blocks' info. */
+struct disk_cache_info *disk_cache_info;
+/* Hint index for which cache block to reuse next. */
+int disk_cache_hint;
+/* Lock for these structures. */
+pthread_mutex_t disk_cache_lock;
+/* Fired when a re-association is done. */
+pthread_cond_t disk_cache_reassociation;
+
+/* Finish mapping initialization. */
+static void
+disk_cache_init (void)
+{
+ if (block_size != vm_page_size)
+ ext2_panic ("Block size %u != vm_page_size %u",
+ block_size, vm_page_size);
+
+ pthread_mutex_init (&disk_cache_lock, NULL);
+ pthread_cond_init (&disk_cache_reassociation, NULL);
+
+ /* Allocate space for block num -> in-memory pointer mapping. */
+ if (hurd_ihash_create (&disk_cache_bptr, HURD_IHASH_NO_LOCP))
+ ext2_panic ("Can't allocate memory for disk_pager_bptr");
+
+ /* Allocate space for disk cache blocks' info. */
+ disk_cache_info = malloc ((sizeof *disk_cache_info) * disk_cache_blocks);
+ if (!disk_cache_info)
+ ext2_panic ("Cannot allocate space for disk cache info");
+
+ /* Initialize disk_cache_info. */
+ for (int i = 0; i < disk_cache_blocks; i++)
+ {
+ disk_cache_info[i].block = DC_NO_BLOCK;
+ disk_cache_info[i].flags = 0;
+ disk_cache_info[i].ref_count = 0;
+#ifndef NDEBUG
+ disk_cache_info[i].last_read = DC_NO_BLOCK;
+ disk_cache_info[i].last_read_xor
+ = DC_NO_BLOCK ^ DISK_CACHE_LAST_READ_XOR;
+#endif
+ }
+ disk_cache_hint = 0;
+
+ /* Map the superblock and the block group descriptors. */
+ block_t fixed_first = boffs_block (SBLOCK_OFFS);
+ block_t fixed_last = fixed_first
+ + (round_block ((sizeof *group_desc_image) * groups_count)
+ >> log2_block_size);
+ ext2_debug ("%u-%u\n", fixed_first, fixed_last);
+ assert (fixed_last - fixed_first + 1 <= (block_t)disk_cache_blocks + 3);
+ for (block_t i = fixed_first; i <= fixed_last; i++)
+ {
+ disk_cache_block_ref (i);
+ assert (disk_cache_info[i-fixed_first].block == i);
+ disk_cache_info[i-fixed_first].flags |= DC_FIXED;
+ }
+}
+
+static void
+disk_cache_return_unused (void)
+{
+ int index;
+
+ /* XXX: Touch all pages. It seems that sometimes GNU Mach "forgets"
+ to notify us about evicted pages. Disk cache must be
+ unlocked. */
+ for (vm_offset_t i = 0; i < disk_cache_size; i += vm_page_size)
+ *(volatile char *)(disk_cache + i);
+
+ /* Release some references to cached blocks. */
+ pokel_sync (&global_pokel, 1);
+
+ /* Return unused pages that are in core. */
+ int pending_begin = -1, pending_end = -1;
+ pthread_mutex_lock (&disk_cache_lock);
+ for (index = 0; index < disk_cache_blocks; index++)
+ if (! (disk_cache_info[index].flags & (DC_DONT_REUSE & ~DC_INCORE))
+ && ! disk_cache_info[index].ref_count)
+ {
+ ext2_debug ("return %u -> %d",
+ disk_cache_info[index].block, index);
+ if (index != pending_end)
+ {
+ /* Return previous region, if there is such, ... */
+ if (pending_end >= 0)
+ {
+ pthread_mutex_unlock (&disk_cache_lock);
+ pager_return_some (diskfs_disk_pager,
+ pending_begin * vm_page_size,
+ (pending_end - pending_begin)
+ * vm_page_size, 1);
+ pthread_mutex_lock (&disk_cache_lock);
+ }
+ /* ... and start new region. */
+ pending_begin = index;
+ }
+ pending_end = index + 1;
+ }
+
+ pthread_mutex_unlock (&disk_cache_lock);
+
+ /* Return last region, if there is such. */
+ if (pending_end >= 0)
+ pager_return_some (diskfs_disk_pager,
+ pending_begin * vm_page_size,
+ (pending_end - pending_begin) * vm_page_size,
+ 1);
+ else
+ {
+ printf ("ext2fs: disk cache is starving\n");
+
+ /* Give it some time. This should happen rarely. */
+ sleep (1);
+ }
+}
+
+/* Map block and return pointer to it. */
+void *
+disk_cache_block_ref (block_t block)
+{
+ int index;
+ void *bptr;
+
+ assert (block < store->size >> log2_block_size);
+
+ ext2_debug ("(%u)", block);
+
+retry_ref:
+ pthread_mutex_lock (&disk_cache_lock);
+
+ bptr = hurd_ihash_find (disk_cache_bptr, block);
+ if (bptr)
+ /* Already mapped. */
+ {
+ index = bptr_index (bptr);
+
+ /* In process of re-associating? */
+ if (disk_cache_info[index].flags & DC_UNTOUCHED)
+ {
+ /* Wait re-association to finish. */
+ pthread_cond_wait (&disk_cache_reassociation, &disk_cache_lock);
+ pthread_mutex_unlock (&disk_cache_lock);
+
+#if 0
+ printf ("Re-association -- wait finished.\n");
+#endif
+
+ goto retry_ref;
+ }
+
+ /* Just increment reference and return. */
+ assert (disk_cache_info[index].ref_count + 1
+ > disk_cache_info[index].ref_count);
+ disk_cache_info[index].ref_count++;
+
+ ext2_debug ("cached %u -> %d (ref_count = %hu, flags = %#hx, ptr = %p)",
+ disk_cache_info[index].block, index,
+ disk_cache_info[index].ref_count,
+ disk_cache_info[index].flags, bptr);
+
+ pthread_mutex_unlock (&disk_cache_lock);
+
+ return bptr;
+ }
+
+ /* Search for a block that is not in core and is not referenced. */
+ index = disk_cache_hint;
+ while ((disk_cache_info[index].flags & DC_DONT_REUSE)
+ || (disk_cache_info[index].ref_count))
+ {
+ ext2_debug ("reject %u -> %d (ref_count = %hu, flags = %#hx)",
+ disk_cache_info[index].block, index,
+ disk_cache_info[index].ref_count,
+ disk_cache_info[index].flags);
+
+ /* Just move to next block. */
+ index++;
+ if (index >= disk_cache_blocks)
+ index -= disk_cache_blocks;
+
+ /* If we return to where we started, than there is no suitable
+ block. */
+ if (index == disk_cache_hint)
+ break;
+ }
+
+ /* The next place in the disk cache becomes the current hint. */
+ disk_cache_hint = index + 1;
+ if (disk_cache_hint >= disk_cache_blocks)
+ disk_cache_hint -= disk_cache_blocks;
+
+ /* Is suitable place found? */
+ if ((disk_cache_info[index].flags & DC_DONT_REUSE)
+ || disk_cache_info[index].ref_count)
+ /* No place is found. Try to release some blocks and try
+ again. */
+ {
+ ext2_debug ("flush %u -> %d", disk_cache_info[index].block, index);
+
+ pthread_mutex_unlock (&disk_cache_lock);
+
+ disk_cache_return_unused ();
+
+ goto retry_ref;
+ }
+
+ /* Suitable place is found. */
+
+ /* Calculate pointer to data. */
+ bptr = (char *)disk_cache + (index << log2_block_size);
+ ext2_debug ("map %u -> %d (%p)", block, index, bptr);
+
+ /* This pager_return_some is used only to set PM_FORCEREAD for the
+ page. DC_UNTOUCHED is set so that we catch if someone has
+ referenced the block while we didn't hold disk_cache_lock. */
+ disk_cache_info[index].flags |= DC_UNTOUCHED;
+
+#if 0 /* XXX: Let's see if this is needed at all. */
+
+ pthread_mutex_unlock (&disk_cache_lock);
+ pager_return_some (diskfs_disk_pager, bptr - disk_cache, vm_page_size, 1);
+ pthread_mutex_lock (&disk_cache_lock);
+
+ /* Has someone used our bptr? Has someone mapped requested block
+ while we have unlocked disk_cache_lock? If so, environment has
+ changed and we have to restart operation. */
+ if ((! (disk_cache_info[index].flags & DC_UNTOUCHED))
+ || hurd_ihash_find (disk_cache_bptr, block))
+ {
+ pthread_mutex_unlock (&disk_cache_lock);
+ goto retry_ref;
+ }
+
+#elif 0
+
+ /* XXX: Use libpager internals. */
+
+ pthread_mutex_lock (&diskfs_disk_pager->interlock);
+ int page = (bptr - disk_cache) / vm_page_size;
+ assert (page >= 0);
+ int is_incore = (page < diskfs_disk_pager->pagemapsize
+ && (diskfs_disk_pager->pagemap[page] & PM_INCORE));
+ pthread_mutex_unlock (&diskfs_disk_pager->interlock);
+ if (is_incore)
+ {
+ pthread_mutex_unlock (&disk_cache_lock);
+ printf ("INCORE\n");
+ goto retry_ref;
+ }
+
+#endif
+
+ /* Re-associate. */
+ if (disk_cache_info[index].block != DC_NO_BLOCK)
+ /* Remove old association. */
+ hurd_ihash_remove (disk_cache_bptr, disk_cache_info[index].block);
+ /* New association. */
+ if (hurd_ihash_add (disk_cache_bptr, block, bptr))
+ ext2_panic ("Couldn't hurd_ihash_add new disk block");
+ assert (! (disk_cache_info[index].flags & DC_DONT_REUSE & ~DC_UNTOUCHED));
+ disk_cache_info[index].block = block;
+ assert (! disk_cache_info[index].ref_count);
+ disk_cache_info[index].ref_count = 1;
+
+ /* All data structures are set up. */
+ pthread_mutex_unlock (&disk_cache_lock);
+
+ /* Try to read page. */
+ *(volatile char *) bptr;
+
+ /* Check if it's actually read. */
+ pthread_mutex_lock (&disk_cache_lock);
+ if (disk_cache_info[index].flags & DC_UNTOUCHED)
+ /* It's not read. */
+ {
+ /* Remove newly created association. */
+ hurd_ihash_remove (disk_cache_bptr, block);
+ disk_cache_info[index].block = DC_NO_BLOCK;
+ disk_cache_info[index].flags &=~ DC_UNTOUCHED;
+ disk_cache_info[index].ref_count = 0;
+ pthread_mutex_unlock (&disk_cache_lock);
+
+ /* Prepare next time association of this page to succeed. */
+ pager_flush_some (diskfs_disk_pager, bptr - disk_cache,
+ vm_page_size, 0);
+
+#if 0
+ printf ("Re-association failed.\n");
+#endif
+
+ goto retry_ref;
+ }
+
+ /* Re-association was successful. */
+ pthread_cond_broadcast (&disk_cache_reassociation);
+
+ pthread_mutex_unlock (&disk_cache_lock);
+
+ ext2_debug ("(%u) = %p", block, bptr);
+ return bptr;
+}
+
+void
+disk_cache_block_ref_ptr (void *ptr)
+{
+ int index;
+
+ pthread_mutex_lock (&disk_cache_lock);
+ index = bptr_index (ptr);
+ assert (disk_cache_info[index].ref_count >= 1);
+ assert (disk_cache_info[index].ref_count + 1
+ > disk_cache_info[index].ref_count);
+ disk_cache_info[index].ref_count++;
+ assert (! (disk_cache_info[index].flags & DC_UNTOUCHED));
+ ext2_debug ("(%p) (ref_count = %hu, flags = %#hx)",
+ ptr,
+ disk_cache_info[index].ref_count,
+ disk_cache_info[index].flags);
+ pthread_mutex_unlock (&disk_cache_lock);
+}
+
+void
+disk_cache_block_deref (void *ptr)
+{
+ int index;
+
+ assert (disk_cache <= ptr && ptr <= disk_cache + disk_cache_size);
+
+ pthread_mutex_lock (&disk_cache_lock);
+ index = bptr_index (ptr);
+ ext2_debug ("(%p) (ref_count = %hu, flags = %#hx)",
+ ptr,
+ disk_cache_info[index].ref_count - 1,
+ disk_cache_info[index].flags);
+ assert (! (disk_cache_info[index].flags & DC_UNTOUCHED));
+ assert (disk_cache_info[index].ref_count >= 1);
+ disk_cache_info[index].ref_count--;
+ pthread_mutex_unlock (&disk_cache_lock);
+}
+
+/* Not used. */
+int
+disk_cache_block_is_ref (block_t block)
+{
+ int ref;
+ void *ptr;
+
+ pthread_mutex_lock (&disk_cache_lock);
+ ptr = hurd_ihash_find (disk_cache_bptr, block);
+ if (ptr == NULL)
+ ref = 0;
+ else /* XXX: Should check for DC_UNTOUCHED too. */
+ ref = disk_cache_info[bptr_index (ptr)].ref_count;
+ pthread_mutex_unlock (&disk_cache_lock);
+
+ return ref;
+}
+
+/* A top-level function for the paging thread that just services paging
+ requests. */
+static void *
+service_paging_requests (void *arg)
+{
+ struct port_bucket *pager_bucket = arg;
+ ports_manage_port_operations_multithread (pager_bucket,
+ pager_demuxer,
+ 1000,
+ 0,
+ NULL);
+ /* Not reached. */
+ return NULL;
+}
+
+/* Create the disk pager, and the file pager. */
+void
+create_disk_pager (void)
+{
+ pthread_t thread;
+ pthread_attr_t attr;
+ error_t err;
+
+ /* The disk pager. */
+ struct user_pager_info *upi = malloc (sizeof (struct user_pager_info));
+ if (!upi)
+ ext2_panic ("can't create disk pager: %s", strerror (errno));
+ upi->type = DISK;
+ disk_pager_bucket = ports_create_bucket ();
+ get_hypermetadata ();
+ disk_cache_blocks = DISK_CACHE_BLOCKS;
+ disk_cache_size = disk_cache_blocks << log2_block_size;
+ diskfs_start_disk_pager (upi, disk_pager_bucket, MAY_CACHE, 1,
+ disk_cache_size, &disk_cache);
+ disk_cache_init ();
+
+ /* The file pager. */
+ file_pager_bucket = ports_create_bucket ();
+
+#define STACK_SIZE (64 * 1024)
+ pthread_attr_init (&attr);
+ pthread_attr_setstacksize (&attr, STACK_SIZE);
+#undef STACK_SIZE
+
+ /* Make a thread to service file paging requests. */
+ err = pthread_create (&thread, &attr,
+ service_paging_requests, file_pager_bucket);
+ if (err)
+ error (2, err, "pthread_create");
+ pthread_detach (thread);
+}
+
+/* Call this to create a FILE_DATA pager and return a send right.
+ NODE must be locked. */
+mach_port_t
+diskfs_get_filemap (struct node *node, vm_prot_t prot)
+{
+ mach_port_t right;
+
+ assert (S_ISDIR (node->dn_stat.st_mode)
+ || S_ISREG (node->dn_stat.st_mode)
+ || (S_ISLNK (node->dn_stat.st_mode)));
+
+ pthread_spin_lock (&node_to_page_lock);
+ do
+ {
+ struct pager *pager = node->dn->pager;
+ if (pager)
+ {
+ /* Because PAGER is not a real reference,
+ this might be nearly deallocated. If that's so, then
+ the port right will be null. In that case, clear here
+ and loop. The deallocation will complete separately. */
+ right = pager_get_port (pager);
+ if (right == MACH_PORT_NULL)
+ node->dn->pager = 0;
+ else
+ pager_get_upi (pager)->max_prot |= prot;
+ }
+ else
+ {
+ struct user_pager_info *upi =
+ malloc (sizeof (struct user_pager_info));
+ upi->type = FILE_DATA;
+ upi->node = node;
+ upi->max_prot = prot;
+ diskfs_nref_light (node);
+ node->dn->pager =
+ pager_create (upi, file_pager_bucket, MAY_CACHE,
+ MEMORY_OBJECT_COPY_DELAY, 0);
+ if (node->dn->pager == 0)
+ {
+ diskfs_nrele_light (node);
+ free (upi);
+ pthread_spin_unlock (&node_to_page_lock);
+ return MACH_PORT_NULL;
+ }
+
+ right = pager_get_port (node->dn->pager);
+ ports_port_deref (node->dn->pager);
+ }
+ }
+ while (right == MACH_PORT_NULL);
+ pthread_spin_unlock (&node_to_page_lock);
+
+ mach_port_insert_right (mach_task_self (), right, right,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ return right;
+}
+
+/* Call this when we should turn off caching so that unused memory object
+ ports get freed. */
+void
+drop_pager_softrefs (struct node *node)
+{
+ struct pager *pager;
+
+ pthread_spin_lock (&node_to_page_lock);
+ pager = node->dn->pager;
+ if (pager)
+ ports_port_ref (pager);
+ pthread_spin_unlock (&node_to_page_lock);
+
+ if (MAY_CACHE && pager)
+ {
+ pager_sync (pager, 0);
+ pager_change_attributes (pager, 0, MEMORY_OBJECT_COPY_DELAY, 0);
+ }
+ if (pager)
+ ports_port_deref (pager);
+}
+
+/* Call this when we should turn on caching because it's no longer
+ important for unused memory object ports to get freed. */
+void
+allow_pager_softrefs (struct node *node)
+{
+ struct pager *pager;
+
+ pthread_spin_lock (&node_to_page_lock);
+ pager = node->dn->pager;
+ if (pager)
+ ports_port_ref (pager);
+ pthread_spin_unlock (&node_to_page_lock);
+
+ if (MAY_CACHE && pager)
+ pager_change_attributes (pager, 1, MEMORY_OBJECT_COPY_DELAY, 0);
+ if (pager)
+ ports_port_deref (pager);
+}
+
+/* Call this to find out the struct pager * corresponding to the
+ FILE_DATA pager of inode IP. This should be used *only* as a subsequent
+ argument to register_memory_fault_area, and will be deleted when
+ the kernel interface is fixed. NODE must be locked. */
+struct pager *
+diskfs_get_filemap_pager_struct (struct node *node)
+{
+ /* This is safe because pager can't be cleared; there must be
+ an active mapping for this to be called. */
+ return node->dn->pager;
+}
+
+/* Shutdown all the pagers (except the disk pager). */
+void
+diskfs_shutdown_pager ()
+{
+ error_t shutdown_one (void *v_p)
+ {
+ struct pager *p = v_p;
+ pager_shutdown (p);
+ return 0;
+ }
+
+ write_all_disknodes ();
+
+ ports_bucket_iterate (file_pager_bucket, shutdown_one);
+
+ /* Sync everything on the the disk pager. */
+ sync_global (1);
+
+ /* Despite the name of this function, we never actually shutdown the disk
+ pager, just make sure it's synced. */
+}
+
+/* Sync all the pagers. */
+void
+diskfs_sync_everything (int wait)
+{
+ error_t sync_one (void *v_p)
+ {
+ struct pager *p = v_p;
+ pager_sync (p, wait);
+ return 0;
+ }
+
+ write_all_disknodes ();
+ ports_bucket_iterate (file_pager_bucket, sync_one);
+
+ /* Do things on the the disk pager. */
+ sync_global (wait);
+}
+
+static void
+disable_caching ()
+{
+ error_t block_cache (void *arg)
+ {
+ struct pager *p = arg;
+
+ pager_change_attributes (p, 0, MEMORY_OBJECT_COPY_DELAY, 1);
+ return 0;
+ }
+
+ /* Loop through the pagers and turn off caching one by one,
+ synchronously. That should cause termination of each pager. */
+ ports_bucket_iterate (disk_pager_bucket, block_cache);
+ ports_bucket_iterate (file_pager_bucket, block_cache);
+}
+
+static void
+enable_caching ()
+{
+ error_t enable_cache (void *arg)
+ {
+ struct pager *p = arg;
+ struct user_pager_info *upi = pager_get_upi (p);
+
+ pager_change_attributes (p, 1, MEMORY_OBJECT_COPY_DELAY, 0);
+
+ /* It's possible that we didn't have caching on before, because
+ the user here is the only reference to the underlying node
+ (actually, that's quite likely inside this particular
+ routine), and if that node has no links. So dinkle the node
+ ref counting scheme here, which will cause caching to be
+ turned off, if that's really necessary. */
+ if (upi->type == FILE_DATA)
+ {
+ diskfs_nref (upi->node);
+ diskfs_nrele (upi->node);
+ }
+
+ return 0;
+ }
+
+ ports_bucket_iterate (disk_pager_bucket, enable_cache);
+ ports_bucket_iterate (file_pager_bucket, enable_cache);
+}
+
+/* Tell diskfs if there are pagers exported, and if none, then
+ prevent any new ones from showing up. */
+int
+diskfs_pager_users ()
+{
+ int npagers = ports_count_bucket (file_pager_bucket);
+
+ if (npagers == 0)
+ return 0;
+
+ if (MAY_CACHE)
+ {
+ disable_caching ();
+
+ /* Give it a second; the kernel doesn't actually shutdown
+ immediately. XXX */
+ sleep (1);
+
+ npagers = ports_count_bucket (file_pager_bucket);
+ if (npagers == 0)
+ return 0;
+
+ /* Darn, there are actual honest users. Turn caching back on,
+ and return failure. */
+ enable_caching ();
+ }
+
+ ports_enable_bucket (file_pager_bucket);
+
+ return 1;
+}
+
+/* Return the bitwise or of the maximum prot parameter (the second arg to
+ diskfs_get_filemap) for all active user pagers. */
+vm_prot_t
+diskfs_max_user_pager_prot ()
+{
+ vm_prot_t max_prot = 0;
+ int npagers = ports_count_bucket (file_pager_bucket);
+
+ if (npagers > 0)
+ {
+ error_t add_pager_max_prot (void *v_p)
+ {
+ struct pager *p = v_p;
+ struct user_pager_info *upi = pager_get_upi (p);
+ max_prot |= upi->max_prot;
+ /* Stop iterating if MAX_PROT is as filled as it's going to get. */
+ return max_prot == (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE);
+ }
+
+ disable_caching (); /* Make any silly pagers go away. */
+
+ /* Give it a second; the kernel doesn't actually shutdown
+ immediately. XXX */
+ sleep (1);
+
+ ports_bucket_iterate (file_pager_bucket, add_pager_max_prot);
+
+ enable_caching ();
+ }
+
+ ports_enable_bucket (file_pager_bucket);
+
+ return max_prot;
+}
diff --git a/ext2fs/pokel.c b/ext2fs/pokel.c
new file mode 100644
index 00000000..3afb32e4
--- /dev/null
+++ b/ext2fs/pokel.c
@@ -0,0 +1,197 @@
+/* A data structure to remember modifications to a memory region
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ext2fs.h"
+
+void
+pokel_init (struct pokel *pokel, struct pager *pager, void *image)
+{
+ pokel->lock = PTHREAD_SPINLOCK_INITIALIZER;
+ pokel->pokes = NULL;
+ pokel->free_pokes = NULL;
+ pokel->pager = pager;
+ pokel->image = image;
+}
+
+/* Clean up any state associated with POKEL (but don't free POKEL). */
+void
+pokel_finalize (struct pokel *pokel)
+{
+ struct poke *pl, *next;
+ for (pl = pokel->pokes; pl; pl = next)
+ {
+ next = pl->next;
+ free (pl);
+ }
+ for (pl = pokel->free_pokes; pl; pl = next)
+ {
+ next = pl->next;
+ free (pl);
+ }
+}
+
+/* Remember that data here on the disk has been modified. */
+void
+pokel_add (struct pokel *pokel, void *loc, vm_size_t length)
+{
+ struct poke *pl;
+ vm_offset_t offset = trunc_page (loc - pokel->image);
+ vm_offset_t end = round_page (loc + length - pokel->image);
+
+ ext2_debug ("adding %p[%ul] (range 0x%x to 0x%x)", loc, length, offset, end);
+
+ pthread_spin_lock (&pokel->lock);
+
+ pl = pokel->pokes;
+ while (pl != NULL)
+ {
+ vm_offset_t p_offs = pl->offset;
+ vm_size_t p_end = p_offs + pl->length;
+
+ if (p_offs <= offset && end <= p_end)
+ {
+ if (pokel->image == disk_cache)
+ for (vm_offset_t i = offset; i < end; i += block_size)
+ disk_cache_block_deref (disk_cache + i);
+
+ break;
+ }
+ else if (p_end >= offset && end >= p_offs)
+ {
+ pl->offset = offset < p_offs ? offset : p_offs;
+ pl->length = (end > p_end ? end : p_end) - pl->offset;
+
+ if (pokel->image == disk_cache)
+ {
+ vm_offset_t i_begin = p_offs > offset ? p_offs : offset;
+ vm_offset_t i_end = p_end < end ? p_end : end;
+ for (vm_offset_t i = i_begin; i < i_end; i += block_size)
+ disk_cache_block_deref (disk_cache + i);
+ }
+
+ ext2_debug ("extended 0x%x[%ul] to 0x%x[%ul]",
+ p_offs, p_end - p_offs, pl->offset, pl->length);
+ break;
+ }
+
+ pl = pl->next;
+ }
+
+ if (pl == NULL)
+ {
+ pl = pokel->free_pokes;
+ if (pl == NULL)
+ {
+ pl = malloc (sizeof (struct poke));
+ assert (pl);
+ }
+ else
+ pokel->free_pokes = pl->next;
+ pl->offset = offset;
+ pl->length = end - offset;
+ pl->next = pokel->pokes;
+ pokel->pokes = pl;
+ }
+
+ pthread_spin_unlock (&pokel->lock);
+}
+
+/* Move all pending pokes from POKEL into its free list. If SYNC is true,
+ otherwise do nothing. */
+void
+_pokel_exec (struct pokel *pokel, int sync, int wait)
+{
+ struct poke *pl, *pokes, *last = NULL;
+
+ pthread_spin_lock (&pokel->lock);
+ pokes = pokel->pokes;
+ pokel->pokes = NULL;
+ pthread_spin_unlock (&pokel->lock);
+
+ for (pl = pokes; pl; last = pl, pl = pl->next)
+ {
+ if (sync)
+ {
+ ext2_debug ("syncing 0x%lx[%ul]", pl->offset, pl->length);
+ pager_sync_some (pokel->pager, pl->offset, pl->length, wait);
+ }
+
+ if (pokel->image == disk_cache)
+ {
+ vm_offset_t begin = trunc_block (pl->offset);
+ vm_offset_t end = round_block (pl->offset + pl->length);
+ for (vm_offset_t i = begin; i != end; i += block_size)
+ disk_cache_block_deref (pokel->image + i);
+ }
+ }
+
+ if (last)
+ {
+ pthread_spin_lock (&pokel->lock);
+ last->next = pokel->free_pokes;
+ pokel->free_pokes = pokes;
+ pthread_spin_unlock (&pokel->lock);
+ }
+}
+
+/* Sync all the modified pieces of disk */
+void
+pokel_sync (struct pokel *pokel, int wait)
+{
+ _pokel_exec (pokel, 1, wait);
+}
+
+/* Flush (that is, drop on the ground) all pending pokes in POKEL. */
+void
+pokel_flush (struct pokel *pokel)
+{
+ _pokel_exec (pokel, 0, 0);
+}
+
+/* Transfer all regions from FROM to POKEL, which must have the same pager. */
+void
+pokel_inherit (struct pokel *pokel, struct pokel *from)
+{
+ struct poke *pokes, *last;
+
+ assert (pokel->pager == from->pager);
+ assert (pokel->image == from->image);
+
+ /* Take all pokes from FROM... */
+ pthread_spin_lock (&from->lock);
+ pokes = from->pokes;
+ from->pokes = NULL;
+ pthread_spin_unlock (&from->lock);
+
+ /* And put them in POKEL. */
+ pthread_spin_lock (&pokel->lock);
+ last = pokel->pokes;
+ if (last)
+ {
+ while (last->next)
+ last = last->next;
+ last->next = pokes;
+ }
+ else
+ pokel->pokes = pokes;
+ pthread_spin_unlock (&pokel->lock);
+}
diff --git a/ext2fs/sblock.words b/ext2fs/sblock.words
new file mode 100644
index 00000000..e17b8fa3
--- /dev/null
+++ b/ext2fs/sblock.words
@@ -0,0 +1,6 @@
+inodes blocks r_blocks free_blocks
+free_inodes first_dblock log_block_size log_frag_size
+blocks/group frags/group inodes/group mtime
+wtime mnt_cnt;max magic;state errors;pad
+lastcheck check_int creator_os rev_level
+res_uid;gid
diff --git a/ext2fs/storeinfo.c b/ext2fs/storeinfo.c
new file mode 100644
index 00000000..d9a2be81
--- /dev/null
+++ b/ext2fs/storeinfo.c
@@ -0,0 +1,131 @@
+/* Access to file layout information
+
+ Copyright (C) 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h>
+#include <hurd/store.h>
+
+#include "ext2fs.h"
+#include "libdiskfs/fs_S.h"
+
+error_t
+diskfs_S_file_get_storage_info (struct protid *cred,
+ mach_port_t **ports,
+ mach_msg_type_name_t *ports_type,
+ mach_msg_type_number_t *num_ports,
+ int **ints, mach_msg_type_number_t *num_ints,
+ off_t **offsets,
+ mach_msg_type_number_t *num_offsets,
+ char **data, mach_msg_type_number_t *data_len)
+{
+ error_t err = 0;
+ unsigned num_fs_blocks;
+ struct store *file_store;
+ struct store_run *runs, *run = 0;
+ block_t index = 0;
+ size_t num_runs = 0, runs_alloced = 10;
+ struct node *node = cred->po->np;
+
+ runs = malloc (runs_alloced * sizeof (struct store_run));
+ if (! runs)
+ return ENOMEM;
+
+ pthread_mutex_lock (&node->lock);
+
+ /* NUM_FS_BLOCKS counts down the blocks in the file that we've not
+ enumerated yet; when it hits zero, we can stop. */
+ if (node->dn_stat.st_size < node->dn_stat.st_blocks * 512)
+ /* The value indicated by st_blocks is too big (because it includes
+ indirect blocks), so use the size of the file. */
+ num_fs_blocks =
+ (node->dn_stat.st_size + block_size - 1) >> log2_block_size;
+ else
+ num_fs_blocks = node->dn_stat.st_blocks >> log2_stat_blocks_per_fs_block;
+
+ while (num_fs_blocks-- > 0)
+ {
+ block_t block;
+
+ err = ext2_getblk (node, index++, 0, &block);
+ if (err == EINVAL)
+ /* Either a hole, or past the end of the file.
+ A hole can't be mapped in runs since we don't know
+ where the blocks will be allocated, so we can't return the
+ underlying storage. */
+ err = EOPNOTSUPP;
+ if (err)
+ break;
+
+ block <<= log2_dev_blocks_per_fs_block;
+ if (num_runs == 0
+ || block != run->start + run->length) /* BLOCK doesn't follow RUN */
+ /* Add a new run. */
+ {
+ if (num_runs == runs_alloced)
+ /* Make some more space in RUNS. */
+ {
+ struct store_run *new;
+ runs_alloced *= 2;
+ new = realloc (runs, runs_alloced * sizeof (struct store_run));
+ if (! new)
+ {
+ err = ENOMEM;
+ break;
+ }
+ runs = new;
+ }
+
+ run = runs + num_runs++;
+ run->start = block;
+ /* The length will get extended just below. */
+ run->length = 0;
+ }
+
+ /* Increase the size of the current run by one filesystem block. */
+ run->length += 1 << log2_dev_blocks_per_fs_block;
+ }
+
+ pthread_mutex_unlock (&node->lock);
+
+ if (! err)
+ err = store_clone (store, &file_store);
+ if (! err)
+ {
+ err = store_remap (file_store, runs, num_runs, &file_store);
+ if (!err
+ && !idvec_contains (cred->user->uids, 0)
+ && !store_is_securely_returnable (file_store, cred->po->openstat))
+ {
+ err = store_set_flags (file_store, STORE_INACTIVE);
+ if (err == EINVAL)
+ err = EACCES;
+ }
+ if (! err)
+ {
+ *ports_type = MACH_MSG_TYPE_COPY_SEND;
+ err = store_return (file_store, ports, num_ports, ints, num_ints,
+ offsets, num_offsets, data, data_len);
+ }
+ store_free (file_store);
+ }
+
+ free (runs);
+
+ return err;
+}
diff --git a/ext2fs/truncate.c b/ext2fs/truncate.c
new file mode 100644
index 00000000..63d22955
--- /dev/null
+++ b/ext2fs/truncate.c
@@ -0,0 +1,374 @@
+/* File truncation
+
+ Copyright (C) 1995,96,97,99,2000 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ext2fs.h"
+
+#ifdef DONT_CACHE_MEMORY_OBJECTS
+#define MAY_CACHE 0
+#else
+#define MAY_CACHE 1
+#endif
+
+/* ---------------------------------------------------------------- */
+
+/* A sequence of blocks to be freed in NODE. */
+struct free_block_run
+{
+ block_t first_block;
+ unsigned long num_blocks;
+ struct node *node;
+};
+
+/* Initialize FBR, pointing to NODE. */
+static inline void
+free_block_run_init (struct free_block_run *fbr, struct node *node)
+{
+ fbr->num_blocks = 0;
+ fbr->node = node;
+}
+
+static inline void
+_free_block_run_flush (struct free_block_run *fbr, unsigned long count)
+{
+ fbr->node->dn_stat.st_blocks -= count << log2_stat_blocks_per_fs_block;
+ fbr->node->dn_stat_dirty = 1;
+ ext2_free_blocks (fbr->first_block, count);
+}
+
+/* Add BLOCK to the list of blocks to be freed in FBR. */
+static inline void
+free_block_run_add (struct free_block_run *fbr, block_t block)
+{
+ unsigned long count = fbr->num_blocks;
+ if (count == 0)
+ {
+ fbr->first_block = block;
+ fbr->num_blocks++;
+ }
+ else if (count > 0 && fbr->first_block == block - count)
+ fbr->num_blocks++;
+ else
+ {
+ _free_block_run_flush (fbr, count);
+ fbr->first_block = block;
+ fbr->num_blocks = 1;
+ }
+}
+
+/* If *P is non-zero, set it to zero, and add the block it pointed to the
+ list of blocks to be freed in FBR. */
+static inline void
+free_block_run_free_ptr (struct free_block_run *fbr, block_t *p)
+{
+ block_t block = *p;
+ if (block)
+ {
+ *p = 0;
+ free_block_run_add (fbr, block);
+ }
+}
+
+/* Free any blocks left in FBR, and cleanup any resources it's using. */
+static inline void
+free_block_run_finish (struct free_block_run *fbr)
+{
+ unsigned long count = fbr->num_blocks;
+ if (count > 0)
+ _free_block_run_flush (fbr, count);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Free any direct blocks starting with block END. */
+static void
+trunc_direct (struct node *node, block_t end, struct free_block_run *fbr)
+{
+ block_t *blocks = node->dn->info.i_data;
+
+ ext2_debug ("truncating direct blocks from %d", end);
+
+ while (end < EXT2_NDIR_BLOCKS)
+ free_block_run_free_ptr (fbr, blocks + end++);
+}
+
+/* Free any blocks in NODE greater than or equal to END that are rooted in
+ the indirect block *P; OFFSET should be the block position that *P
+ corresponds to. For each block pointer in *P that should be freed,
+ FREE_BLOCK is called with a pointer to the entry for that block, and the
+ index of the entry within *P. If every block in *P is freed, then *P is
+ set to 0, otherwise it is left alone. */
+static void
+trunc_indirect (struct node *node, block_t end,
+ block_t *p, block_t offset,
+ void (*free_block)(block_t *p, unsigned index),
+ struct free_block_run *fbr)
+{
+ if (*p)
+ {
+ unsigned index;
+ int modified = 0, all_freed = 1;
+ block_t *ind_bh = (block_t *) disk_cache_block_ref (*p);
+ unsigned first = end < offset ? 0 : end - offset;
+
+ for (index = first; index < addr_per_block; index++)
+ if (ind_bh[index])
+ {
+ (*free_block)(ind_bh + index, index);
+ if (ind_bh[index])
+ all_freed = 0; /* Some descendent hasn't been freed. */
+ else
+ modified = 1;
+ }
+
+ if (first == 0 && all_freed)
+ {
+ pager_flush_some (diskfs_disk_pager,
+ bptr_index (ind_bh) << log2_block_size,
+ block_size, 1);
+ free_block_run_free_ptr (fbr, p);
+ disk_cache_block_deref (ind_bh);
+ }
+ else if (modified)
+ record_indir_poke (node, ind_bh);
+ else
+ disk_cache_block_deref (ind_bh);
+ }
+}
+
+static void
+trunc_single_indirect (struct node *node, block_t end,
+ block_t *p, block_t offset,
+ struct free_block_run *fbr)
+{
+ void free_block (block_t *p, unsigned index)
+ {
+ free_block_run_free_ptr (fbr, p);
+ }
+ trunc_indirect (node, end, p, offset, free_block, fbr);
+}
+
+static void
+trunc_double_indirect (struct node *node, block_t end,
+ block_t *p, block_t offset,
+ struct free_block_run *fbr)
+{
+ void free_block (block_t *p, unsigned index)
+ {
+ block_t entry_offs = offset + (index * addr_per_block);
+ trunc_single_indirect (node, end, p, entry_offs, fbr);
+ }
+ trunc_indirect (node, end, p, offset, free_block, fbr);
+}
+
+static void
+trunc_triple_indirect (struct node *node, block_t end,
+ block_t *p, block_t offset,
+ struct free_block_run *fbr)
+{
+ void free_block (block_t *p, unsigned index)
+ {
+ block_t entry_offs = offset + (index * addr_per_block * addr_per_block);
+ trunc_double_indirect (node, end, p, entry_offs, fbr);
+ }
+ trunc_indirect (node, end, p, offset, free_block, fbr);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Write something to each page from START to END inclusive of memory
+ object OBJ, but make sure the data doesns't actually change. */
+static void
+poke_pages (memory_object_t obj, vm_offset_t start, vm_offset_t end)
+{
+ while (start < end)
+ {
+ error_t err;
+ vm_size_t len = 8 * vm_page_size;
+ vm_address_t addr = 0;
+
+ if (len > end - start)
+ len = end - start;
+
+ err = vm_map (mach_task_self (), &addr, len, 0, 1, obj, start, 0,
+ VM_PROT_WRITE|VM_PROT_READ, VM_PROT_READ|VM_PROT_WRITE, 0);
+ if (!err)
+ {
+ vm_address_t poke;
+ for (poke = addr; poke < addr + len; poke += vm_page_size)
+ *(volatile int *)poke = *(volatile int *)poke;
+ munmap ((caddr_t) addr, len);
+ }
+
+ start += len;
+ }
+}
+
+/* Flush all the data past the new size from the kernel. Also force any
+ delayed copies of this data to take place immediately. (We are implicitly
+ changing the data to zeros and doing it without the kernel's immediate
+ knowledge; accordingl we must help out the kernel thusly.) */
+static void
+force_delayed_copies (struct node *node, off_t length)
+{
+ struct pager *pager;
+
+ pthread_spin_lock (&node_to_page_lock);
+ pager = node->dn->pager;
+ if (pager)
+ ports_port_ref (pager);
+ pthread_spin_unlock (&node_to_page_lock);
+
+ if (pager)
+ {
+ mach_port_t obj;
+
+ pager_change_attributes (pager, MAY_CACHE, MEMORY_OBJECT_COPY_NONE, 1);
+ obj = diskfs_get_filemap (node, VM_PROT_READ);
+ if (obj != MACH_PORT_NULL)
+ {
+ /* XXX should cope with errors from diskfs_get_filemap */
+ poke_pages (obj, round_page (length), round_page (node->allocsize));
+ mach_port_deallocate (mach_task_self (), obj);
+ pager_flush_some (pager, round_page(length),
+ node->allocsize - length, 1);
+ }
+
+ ports_port_deref (pager);
+ }
+}
+
+static void
+enable_delayed_copies (struct node *node)
+{
+ struct pager *pager;
+
+ pthread_spin_lock (&node_to_page_lock);
+ pager = node->dn->pager;
+ if (pager)
+ ports_port_ref (pager);
+ pthread_spin_unlock (&node_to_page_lock);
+
+ if (pager)
+ {
+ pager_change_attributes (pager, MAY_CACHE, MEMORY_OBJECT_COPY_DELAY, 0);
+ ports_port_deref (pager);
+ }
+}
+
+/* ---------------------------------------------------------------- */
+
+/* The user must define this function. Truncate locked node NODE to be SIZE
+ bytes long. (If NODE is already less than or equal to SIZE bytes
+ long, do nothing.) If this is a symlink (and diskfs_shortcut_symlink
+ is set) then this should clear the symlink, even if
+ diskfs_create_symlink_hook stores the link target elsewhere. */
+error_t
+diskfs_truncate (struct node *node, off_t length)
+{
+ error_t err;
+ off_t offset;
+
+ diskfs_check_readonly ();
+ assert (!diskfs_readonly);
+
+ if (length >= node->dn_stat.st_size)
+ return 0;
+
+ if (! node->dn_stat.st_blocks)
+ /* There aren't really any blocks allocated, so just frob the size. This
+ is true for fast symlinks, and also apparently for some device nodes
+ in linux. */
+ {
+ node->dn_stat.st_size = length;
+ node->dn_set_mtime = 1;
+ node->dn_set_ctime = 1;
+ diskfs_node_update (node, diskfs_synchronous);
+ return 0;
+ }
+
+ /*
+ * If the file is not being truncated to a block boundary, the
+ * contents of the partial block following the end of the file must be
+ * zeroed in case it ever becomes accessible again because of
+ * subsequent file growth.
+ */
+ offset = length & (block_size - 1);
+ if (offset > 0)
+ {
+ diskfs_node_rdwr (node, (void *)zeroblock, length, block_size - offset,
+ 1, 0, 0);
+ /* Make sure that really happens to avoid leaks. */
+ diskfs_file_update (node, 1);
+ }
+
+ ext2_discard_prealloc (node);
+
+ force_delayed_copies (node, length);
+
+ pthread_rwlock_wrlock (&node->dn->alloc_lock);
+
+ /* Update the size on disk; fsck will finish freeing blocks if necessary
+ should we crash. */
+ node->dn_stat.st_size = length;
+ node->dn_set_mtime = 1;
+ node->dn_set_ctime = 1;
+ diskfs_node_update (node, diskfs_synchronous);
+
+ err = diskfs_catch_exception ();
+ if (!err)
+ {
+ block_t end = boffs_block (round_block (length)), offs;
+ block_t *bptrs = node->dn->info.i_data;
+ struct free_block_run fbr;
+
+ free_block_run_init (&fbr, node);
+
+ trunc_direct (node, end, &fbr);
+
+ offs = EXT2_NDIR_BLOCKS;
+ trunc_single_indirect (node, end, bptrs + EXT2_IND_BLOCK, offs, &fbr);
+ offs += addr_per_block;
+ trunc_double_indirect (node, end, bptrs + EXT2_DIND_BLOCK, offs, &fbr);
+ offs += addr_per_block * addr_per_block;
+ trunc_triple_indirect (node, end, bptrs + EXT2_TIND_BLOCK, offs, &fbr);
+
+ free_block_run_finish (&fbr);
+
+ node->allocsize = round_block (length);
+
+ /* Set our last_page_partially_writable to a pessimistic state -- it
+ won't hurt if is wrong. */
+ node->dn->last_page_partially_writable =
+ trunc_page (node->allocsize) != node->allocsize;
+
+ diskfs_end_catch_exception ();
+ }
+
+ node->dn_set_mtime = 1;
+ node->dn_set_ctime = 1;
+ node->dn_stat_dirty = 1;
+
+ /* Now we can permit delayed copies again. */
+ enable_delayed_copies (node);
+
+ pthread_rwlock_unlock (&node->dn->alloc_lock);
+
+ return err;
+}
diff --git a/ext2fs/xinl.c b/ext2fs/xinl.c
new file mode 100644
index 00000000..9f37e166
--- /dev/null
+++ b/ext2fs/xinl.c
@@ -0,0 +1,2 @@
+#define EXT2FS_DEFINE_EI
+#include "ext2fs.h"
diff --git a/fatfs/Makefile b/fatfs/Makefile
new file mode 100644
index 00000000..6224b644
--- /dev/null
+++ b/fatfs/Makefile
@@ -0,0 +1,30 @@
+# Copyright (C) 1997, 2003, 2007, 2012 Free Software Foundation
+# Modified by Marcus Brinkmann, 2000-05-05
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := fatfs
+makemode := server
+
+target = fatfs
+SRCS = inode.c main.c dir.c pager.c fat.c virt-inode.c node-create.c
+
+OBJS = $(SRCS:.c=.o)
+HURDLIBS = diskfs iohelp fshelp store pager ports ihash shouldbeinlibc
+OTHERLIBS = -lpthread $(and $(HAVE_LIBBZ2),-lbz2) $(and $(HAVE_LIBZ),-lz)
+
+include ../Makeconf
+
+fatfs.static: $(boot-store-types:%=../libstore/libstore_%.a)
diff --git a/fatfs/dir.c b/fatfs/dir.c
new file mode 100644
index 00000000..5a38c63a
--- /dev/null
+++ b/fatfs/dir.c
@@ -0,0 +1,1003 @@
+/* dir.c - FAT filesystem.
+
+ Copyright (C) 1997, 1998, 1999, 2002, 2003, 2007
+ Free Software Foundation, Inc.
+
+ Written by Thomas Bushnell, n/BSG and Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <ctype.h>
+#include <string.h>
+#include <dirent.h>
+#include <hurd/fsys.h>
+
+#include "fatfs.h"
+
+/* The size of a directory block is usually just the cluster size.
+ However, the root directory of FAT12/16 file systems is stored in
+ sectors in a special region, so we settle on the greatest common
+ divisor here. */
+#define DIRBLKSIZ bytes_per_sector
+#define LOG2_DIRBLKSIZ log2_bytes_per_sector
+
+enum slot_status
+{
+ /* This means we haven't yet found room for a new entry. */
+ LOOKING,
+
+ /* This means that the specified entry is free and should be used. */
+ TAKE,
+
+ /* This means that the specified entry has enough room at the end
+ to hold the new entry. */
+ SHRINK,
+
+ /* This means that there is enough space in the block, but not in
+ any one single entry, so they all have to be shifted to make
+ room. */
+ COMPRESS,
+
+ /* This means that the directory will have to be grown to hold the
+ entry. */
+ EXTEND,
+
+ /* For removal and rename, this means that this is the location
+ of the entry found. */
+ HERE_TIS,
+};
+
+struct dirstat
+{
+ /* Type of followp operation expected. */
+ enum lookup_type type;
+
+ /* One of the statuses above. */
+ enum slot_status stat;
+
+ /* Mapped address and length of directory. */
+ vm_address_t mapbuf;
+ vm_size_t mapextent;
+
+ /* Index of this directory block. */
+ int idx;
+
+ /* For stat COMPRESS, this is the address (inside mapbuf)
+ of the first direct in the directory block to be compressed. */
+ /* For stat HERE_TIS, SHRINK, and TAKE, this is the entry referenced. */
+ struct dirrect *entry;
+
+ /* For stat HERE_TIS, type REMOVE, this is the address of the immediately
+ previous direct in this directory block, or zero if this is the first. */
+ struct dirrect *preventry;
+
+ /* For stat COMPRESS, this is the number of bytes needed to be copied
+ in order to undertake the compression. */
+ size_t nbytes;
+};
+
+const size_t diskfs_dirstat_size = sizeof (struct dirstat);
+
+/* Initialize DS such that diskfs_drop_dirstat will ignore it. */
+void
+diskfs_null_dirstat (struct dirstat *ds)
+{
+ ds->type = LOOKUP;
+}
+
+/* Forward declaration. */
+static error_t
+dirscanblock (vm_address_t blockoff, struct node *dp, int idx,
+ const char *name, int namelen, enum lookup_type type,
+ struct dirstat *ds, ino_t *inum);
+
+static int
+fatnamematch (const char *dirname, const char *username, size_t unamelen)
+{
+ char *dn = strdup(dirname);
+ int dpos = 0;
+ int upos = 0;
+ int ext = 0;
+
+ /* Deleted files. */
+ if (dn[0] == FAT_DIR_NAME_DELETED || dn[0] == FAT_DIR_NAME_LAST)
+ return 0;
+ if (dn[0] == FAT_DIR_NAME_REPLACE_DELETED)
+ dn[0] = FAT_DIR_NAME_DELETED;
+
+ /* Special representations for `.' and `..'. */
+ if (!memcmp(dn, FAT_DIR_NAME_DOT, 11))
+ return unamelen == 1 && username[0] == '.';
+
+ if (!memcmp (dn, FAT_DIR_NAME_DOTDOT, 11))
+ return unamelen == 2 && username[0] == '.' && username[1] == '.';
+
+ if (unamelen > 12)
+ return 0;
+
+ do
+ {
+ /* First check if we have reached the extension without coming
+ across blanks. */
+ if (dpos == 8 && !ext)
+ {
+ if (username[upos] == '.')
+ {
+ upos++;
+ ext = 1;
+ }
+ else
+ break;
+ }
+ /* Second, skip blanks in base part. */
+ if (dn[dpos] == ' ')
+ {
+ if (ext)
+ break;
+ while (dpos < 8 && dn[++dpos] == ' ');
+ if (username[upos] == '.')
+ upos++;
+ ext = 1;
+ }
+ else
+ {
+ if (tolower(dn[dpos]) == tolower(username[upos]))
+ {
+ dpos++;
+ upos++;
+ }
+ else
+ break;
+ }
+ } while (upos < unamelen && dpos < 11);
+ while (dpos < 11 && dn[dpos] == ' ')
+ dpos++;
+ return (upos == unamelen && dpos == 11);
+}
+
+/* Implement the diskfs_lookup callback from the diskfs library. See
+ <hurd/diskfs.h> for the interface specification. */
+error_t
+diskfs_lookup_hard (struct node *dp, const char *name, enum lookup_type type,
+ struct node **npp, struct dirstat *ds, struct protid *cred)
+{
+ error_t err;
+ ino_t inum;
+ int namelen;
+ int spec_dotdot;
+ struct node *np = 0;
+ int retry_dotdot = 0;
+ vm_prot_t prot =
+ (type == LOOKUP) ? VM_PROT_READ : (VM_PROT_READ | VM_PROT_WRITE);
+ memory_object_t memobj;
+ vm_address_t buf = 0;
+ vm_size_t buflen = 0;
+ int blockaddr;
+ int idx, lastidx;
+ int looped;
+
+ if ((type == REMOVE) || (type == RENAME))
+ assert (npp);
+
+ if (npp)
+ *npp = 0;
+
+ spec_dotdot = type & SPEC_DOTDOT;
+ type &= ~SPEC_DOTDOT;
+
+ namelen = strlen (name);
+
+ if (namelen > FAT_NAME_MAX)
+ return ENAMETOOLONG;
+
+ try_again:
+ if (ds)
+ {
+ ds->type = LOOKUP;
+ ds->mapbuf = 0;
+ ds->mapextent = 0;
+ }
+ if (buf)
+ {
+ munmap ((caddr_t) buf, buflen);
+ buf = 0;
+ }
+ if (ds && (type == CREATE || type == RENAME))
+ ds->stat = LOOKING;
+
+ /* Map in the directory contents. */
+ memobj = diskfs_get_filemap (dp, prot);
+
+ if (memobj == MACH_PORT_NULL)
+ return errno;
+
+ buf = 0;
+ /* We allow extra space in case we have to do an EXTEND. */
+ buflen = round_page (dp->dn_stat.st_size + DIRBLKSIZ);
+ err = vm_map (mach_task_self (),
+ &buf, buflen, 0, 1, memobj, 0, 0, prot, prot, 0);
+ mach_port_deallocate (mach_task_self (), memobj);
+ if (err)
+ return err;
+
+ inum = 0;
+
+ diskfs_set_node_atime (dp);
+
+ /* Start the lookup at DP->dn->dir_idx. */
+ idx = dp->dn->dir_idx;
+ if (idx << LOG2_DIRBLKSIZ > dp->dn_stat.st_size)
+ idx = 0; /* just in case */
+ blockaddr = buf + (idx << LOG2_DIRBLKSIZ);
+ looped = (idx == 0);
+ lastidx = idx;
+ if (lastidx == 0)
+ lastidx = dp->dn_stat.st_size >> LOG2_DIRBLKSIZ;
+
+ while (!looped || idx < lastidx)
+ {
+ err = dirscanblock (blockaddr, dp, idx, name, namelen, type, ds, &inum);
+ if (!err)
+ {
+ dp->dn->dir_idx = idx;
+ break;
+ }
+ if (err != ENOENT)
+ {
+ munmap ((caddr_t) buf, buflen);
+ return err;
+ }
+
+ blockaddr += DIRBLKSIZ;
+ idx++;
+ if (blockaddr - buf >= dp->dn_stat.st_size && !looped)
+ {
+ /* We've gotten to the end; start back at the beginning. */
+ looped = 1;
+ blockaddr = buf;
+ idx = 0;
+ }
+ }
+
+ diskfs_set_node_atime (dp);
+ if (diskfs_synchronous)
+ diskfs_node_update (dp, 1);
+
+ /* If err is set here, it's ENOENT, and we don't want to
+ think about that as an error yet. */
+ err = 0;
+
+ if (inum && npp)
+ {
+ if (namelen != 2 || name[0] != '.' || name[1] != '.')
+ {
+ if (inum == dp->cache_id)
+ {
+ np = dp;
+ diskfs_nref (np);
+ }
+ else
+ {
+ err = diskfs_cached_lookup_in_dirbuf (inum, &np, buf);
+ if (err)
+ goto out;
+ }
+ }
+
+ /* We are looking up "..". */
+ /* Check to see if this is the root of the filesystem. */
+ else if (dp == diskfs_root_node)
+ {
+ err = EAGAIN;
+ goto out;
+ }
+
+ /* We can't just do diskfs_cached_lookup, because we would then
+ deadlock. So we do this. Ick. */
+ else if (retry_dotdot)
+ {
+ /* Check to see that we got the same answer as last time. */
+ if (inum != retry_dotdot)
+ {
+ /* Drop what we *thought* was .. (but isn't any more) and
+ try *again*. */
+ diskfs_nput (np);
+ pthread_mutex_unlock (&dp->lock);
+ err = diskfs_cached_lookup_in_dirbuf (inum, &np, buf);
+ pthread_mutex_lock (&dp->lock);
+ if (err)
+ goto out;
+ retry_dotdot = inum;
+ goto try_again;
+ }
+ /* Otherwise, we got it fine and np is already set properly. */
+ }
+ else if (!spec_dotdot)
+ {
+ /* Lock them in the proper order, and then
+ repeat the directory scan to see if this is still
+ right. */
+ pthread_mutex_unlock (&dp->lock);
+ err = diskfs_cached_lookup_in_dirbuf (inum, &np, buf);
+ pthread_mutex_lock (&dp->lock);
+ if (err)
+ goto out;
+ retry_dotdot = inum;
+ goto try_again;
+ }
+
+ /* Here below are the spec dotdot cases. */
+ else if (type == RENAME || type == REMOVE)
+ np = ifind (inum);
+
+ else if (type == LOOKUP)
+ {
+ diskfs_nput (dp);
+ err = diskfs_cached_lookup_in_dirbuf (inum, &np, buf);
+ if (err)
+ goto out;
+ }
+ else
+ assert (0);
+ }
+
+ if ((type == CREATE || type == RENAME) && !inum && ds && ds->stat == LOOKING)
+ {
+ /* We didn't find any room, so mark ds to extend the dir. */
+ ds->type = CREATE;
+ ds->stat = EXTEND;
+ ds->idx = dp->dn_stat.st_size >> LOG2_DIRBLKSIZ;
+ }
+
+ /* Return to the user; if we can't, release the reference
+ (and lock) we acquired above. */
+ out:
+ /* Deallocate or save the mapping. */
+ if ((err && err != ENOENT)
+ || !ds
+ || ds->type == LOOKUP)
+ {
+ munmap ((caddr_t) buf, buflen);
+ if (ds)
+ ds->type = LOOKUP; /* Set to be ignored by drop_dirstat. */
+ }
+ else
+ {
+ ds->mapbuf = buf;
+ ds->mapextent = buflen;
+ }
+
+ if (np)
+ {
+ assert (npp);
+ if (err)
+ {
+ if (!spec_dotdot)
+ {
+ /* Normal case. */
+ if (np == dp)
+ diskfs_nrele (np);
+ else
+ diskfs_nput (np);
+ }
+ else if (type == RENAME || type == REMOVE)
+ /* We just did ifind to get np; that allocates
+ no new references, so we don't have anything to do. */
+ ;
+ else if (type == LOOKUP)
+ /* We did diskfs_cached_lookup. */
+ diskfs_nput (np);
+ }
+ else
+ *npp = np;
+ }
+
+ return err ? : inum ? 0 : ENOENT;
+}
+
+/* Scan block at address BLKADDR (of node DP; block index IDX), for
+ name NAME of length NAMELEN. Args TYPE, DS are as for
+ diskfs_lookup. If found, set *INUM to the inode number, else
+ return ENOENT. */
+static error_t
+dirscanblock (vm_address_t blockaddr, struct node *dp, int idx,
+ const char *name, int namelen, enum lookup_type type,
+ struct dirstat *ds, ino_t *inum)
+{
+ int nfree = 0;
+ int needed = 0;
+ vm_address_t currentoff, prevoff = 0;
+ struct dirrect *entry = 0;
+ size_t nbytes = 0;
+ int looking = 0;
+ int countcopies = 0;
+ int consider_compress = 0;
+ inode_t inode;
+ vi_key_t entry_key = vi_zero_key;
+
+ /* FAT lacks the "." and ".." directory record in the root directory,
+ so we emulate them here. */
+ if (idx == 0 && dp == diskfs_root_node
+ && (fatnamematch (FAT_DIR_NAME_DOT, name, namelen)
+ || fatnamematch (FAT_DIR_NAME_DOTDOT, name, namelen)))
+ {
+ entry_key.dir_inode = diskfs_root_node->cache_id;
+ currentoff = blockaddr;
+ }
+ else
+ {
+ if (ds && (ds->stat == LOOKING
+ || ds->stat == COMPRESS))
+ {
+ looking = 1;
+ countcopies = 1;
+ needed = FAT_DIR_RECORDS (namelen);
+ }
+
+ for (currentoff = blockaddr, prevoff = 0;
+ currentoff < blockaddr + DIRBLKSIZ;
+ prevoff = currentoff, currentoff += FAT_DIR_REC_LEN)
+ {
+ entry = (struct dirrect *)currentoff;
+
+ if (looking || countcopies)
+ {
+ int thisfree;
+
+ /* Count how much free space this entry has in it. */
+ if ((char) entry->name[0] == FAT_DIR_NAME_LAST ||
+ (char) entry->name[0] == FAT_DIR_NAME_DELETED)
+ thisfree = FAT_DIR_REC_LEN;
+ else
+ thisfree = 0;
+
+ /* If this isn't at the front of the block, then it will
+ have to be copied if we do a compression; count the
+ number of bytes there too. */
+ if (countcopies && currentoff != blockaddr)
+ nbytes += FAT_DIR_REC_LEN;
+
+ if (ds->stat == COMPRESS && nbytes > ds->nbytes)
+ /* The previously found compress is better than this
+ one, so don't bother counting any more. */
+ countcopies = 0;
+
+ if (thisfree >= needed)
+ {
+ ds->type = CREATE;
+ ds->stat = TAKE;
+ ds->entry = entry;
+ ds->idx = idx;
+ looking = countcopies = 0;
+ }
+ else
+ {
+ nfree += thisfree;
+ if (nfree >= needed)
+ consider_compress = 1;
+ }
+ }
+
+ if (entry->attribute & FAT_DIR_ATTR_LABEL)
+ /* Either the volume label in root dir or a long filename
+ component. */
+ continue;
+
+ if (fatnamematch (entry->name, name, namelen))
+ break;
+ }
+
+ if (consider_compress
+ && (ds->type == LOOKING
+ || (ds->type == COMPRESS && ds->nbytes > nbytes)))
+ {
+ ds->type = CREATE;
+ ds->stat = COMPRESS;
+ ds->entry = (struct dirrect *) blockaddr;
+ ds->idx = idx;
+ ds->nbytes = nbytes;
+ }
+ }
+
+ if (currentoff >= blockaddr + DIRBLKSIZ)
+ {
+ /* The name is not in this block. */
+
+ return ENOENT;
+ }
+
+ /* We have found the required name. */
+
+ if (ds && type == CREATE)
+ ds->type = LOOKUP; /* It's invalid now. */
+ else if (ds && (type == REMOVE || type == RENAME))
+ {
+ ds->type = type;
+ ds->stat = HERE_TIS;
+ ds->entry = entry;
+ ds->idx = idx;
+ ds->preventry = (struct dirrect *) prevoff;
+ }
+
+ if (entry_key.dir_inode)
+ {
+ /* The required name is "." or ".." in the root dir. */
+ *inum = entry_key.dir_inode;
+ }
+ else if ((entry->attribute & FAT_DIR_ATTR_DIR)
+ && !memcmp (entry->name, FAT_DIR_NAME_DOT, 11))
+ {
+ /* "." and ".." have to be treated special. We don't want their
+ directory records, but the records of the directories they
+ point to. */
+
+ *inum = dp->cache_id;
+ }
+ else if ((entry->attribute & FAT_DIR_ATTR_DIR)
+ && !memcmp (entry->name, FAT_DIR_NAME_DOTDOT, 11))
+ {
+ if (entry->first_cluster_low[0] == 0
+ && entry->first_cluster_low[1] == 0
+ && entry->first_cluster_high[0] == 0
+ && entry->first_cluster_high[1] == 0)
+ {
+ *inum = diskfs_root_node->cache_id;
+ }
+ else
+ {
+ struct vi_key vk = vi_key (dp->dn->inode);
+ *inum = vk.dir_inode;
+ }
+ }
+ else
+ {
+ entry_key.dir_inode = dp->cache_id;
+ entry_key.dir_offset = (currentoff - blockaddr) + (idx << LOG2_DIRBLKSIZ);
+ return vi_rlookup(entry_key, inum, &inode, 1);
+ }
+ return 0;
+}
+
+/* Following a lookup call for CREATE, this adds a node to a
+ directory. DP is the directory to be modified; NAME is the name to
+ be entered; NP is the node being linked in; DS is the cached
+ information returned by lookup; CRED describes the user making the
+ call. This call may only be made if the directory has been held
+ locked continuously since the preceding lookup call, and only if
+ that call returned ENOENT. */
+error_t
+diskfs_direnter_hard (struct node *dp, const char *name, struct node *np,
+ struct dirstat *ds, struct protid *cred)
+{
+ struct dirrect *new;
+ int namelen = strlen (name);
+ int needed = FAT_DIR_RECORDS (namelen);
+ error_t err;
+ loff_t oldsize = 0;
+
+ assert (ds->type == CREATE);
+
+ assert (!diskfs_readonly);
+
+ dp->dn_set_mtime = 1;
+
+ /* Select a location for the new directory entry. Each branch of
+ this switch is responsible for setting NEW to point to the
+ on-disk directory entry being written. */
+
+ switch (ds->stat)
+ {
+ case TAKE:
+ /* We are supposed to consume this slot. */
+ assert ((char)ds->entry->name[0] == FAT_DIR_NAME_LAST
+ || (char)ds->entry->name[0] == FAT_DIR_NAME_DELETED);
+
+ new = ds->entry;
+ break;
+
+ case EXTEND:
+ /* Extend the file. */
+ assert (needed <= bytes_per_cluster);
+
+ oldsize = dp->dn_stat.st_size;
+ while (oldsize + bytes_per_cluster > dp->allocsize)
+ {
+ err = diskfs_grow (dp, oldsize + bytes_per_cluster, cred);
+ if (err)
+ {
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+ return err;
+ }
+ memset ((caddr_t) ds->mapbuf + oldsize, 0, bytes_per_cluster);
+ }
+
+ new = (struct dirrect *) ((char *) ds->mapbuf + oldsize);
+
+ dp->dn_stat.st_size = oldsize + bytes_per_cluster;
+ dp->dn_set_ctime = 1;
+
+ break;
+
+ case SHRINK:
+ case COMPRESS:
+ default:
+ assert(0);
+
+ /* COMPRESS will be used later, with long filenames, but shrink
+ does not make sense on fat, as all entries have fixed
+ size. */
+ }
+
+ /* NEW points to the directory entry being written. Now fill in the
+ data. */
+
+ memcpy (new->name, " ", 11);
+ memcpy (new->name, name, namelen % 11); /* XXX */
+
+ write_word (new->first_cluster_low, np->dn->start_cluster & 0xffff);
+ write_word (new->first_cluster_high, np->dn->start_cluster >> 16);
+ write_dword (new->file_size, np->dn_stat.st_size);
+
+ if (!(name[0] == '.' && (name[1] == '\0'
+ || (name[1] == '.' && name[2] =='\0'))))
+ {
+ vi_key_t entry_key;
+
+ entry_key.dir_inode = dp->cache_id;
+ entry_key.dir_offset = ((int) ds->entry) - ((int) ds->mapbuf);
+
+ /* Set the key for this inode now because it wasn't know when
+ the inode was initialized. */
+ vi_change (vi_lookup (np->cache_id), entry_key);
+
+ if (np->dn_stat.st_mode & S_IFDIR)
+ new->attribute = FAT_DIR_ATTR_DIR;
+ }
+ else
+ new->attribute = FAT_DIR_ATTR_DIR;
+
+ /* Mark the directory inode has having been written. */
+ dp->dn_set_mtime = 1;
+
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+
+ diskfs_file_update (dp, 1);
+
+ return 0;
+}
+
+/* Following a lookup call for REMOVE, this removes the link from the
+ directory. DP is the directory being changed and DS is the cached
+ information returned from lookup. This call is only valid if the
+ directory has been locked continuously since the call to lookup, and
+ only if that call succeeded. */
+error_t
+diskfs_dirremove_hard (struct node *dp, struct dirstat *ds)
+{
+ assert (ds->type == REMOVE);
+ assert (ds->stat == HERE_TIS);
+
+ assert (!diskfs_readonly);
+
+ dp->dn_set_mtime = 1;
+
+ ds->entry->name[0] = FAT_DIR_NAME_DELETED;
+
+ /* XXX Do something with dirrect? inode? */
+
+ dp->dn_set_mtime = 1;
+
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+
+ diskfs_file_update (dp, 1);
+
+ return 0;
+}
+
+/* Following a lookup call for RENAME, this changes the inode number
+ on a directory entry. DP is the directory being changed; NP is the
+ new node being linked in; DP is the cached information returned by
+ lookup. This call is only valid if the directory has been locked
+ continuously since the call to lookup, and only if that call
+ succeeded. */
+error_t
+diskfs_dirrewrite_hard (struct node *dp, struct node *np, struct dirstat *ds)
+{
+ error_t err;
+ vi_key_t entry_key;
+ mach_port_t control = MACH_PORT_NULL;
+ struct node *oldnp;
+ ino_t inode;
+ inode_t vinode;
+
+ /* We need the inode and vinode of the old node. */
+ entry_key.dir_inode = dp->cache_id;
+ entry_key.dir_offset = ((int) ds->entry) - ((int) ds->mapbuf);
+ err = vi_rlookup (entry_key, &inode, &vinode, 0);
+
+ assert (err != EINVAL);
+
+ /* Lookup the node, we already have a reference. */
+ oldnp = ifind (inode);
+
+ assert (ds->type == RENAME);
+ assert (ds->stat == HERE_TIS);
+
+ assert (!diskfs_readonly);
+
+ /* The link count must be 0 so the file will be removed and
+ the node will be dropped. */
+ oldnp->dn_stat.st_nlink--;
+ assert (!oldnp->dn_stat.st_nlink);
+
+ /* Close the file, free the referenced held by clients. */
+ fshelp_fetch_control (&oldnp->transbox, &control);
+
+ if (control)
+ {
+ fsys_goaway (control, FSYS_GOAWAY_UNLINK);
+ mach_port_deallocate (mach_task_self (), control);
+ }
+
+ /* Put the new key in the vinode. */
+ vi_change (vi_lookup (np->cache_id), entry_key);
+
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+
+ dp->dn_set_mtime = 1;
+ diskfs_file_update (dp, 1);
+
+ return 0;
+}
+
+/* Tell if DP is an empty directory (has only "." and ".." entries).
+ This routine must be called from inside a catch_exception (). */
+int
+diskfs_dirempty (struct node *dp, struct protid *cred)
+{
+ error_t err;
+ vm_address_t buf = 0, curoff;
+ struct dirrect *entry;
+ int hit = 0; /* Found something in the directory. */
+ memory_object_t memobj = diskfs_get_filemap (dp, VM_PROT_READ);
+
+ if (memobj == MACH_PORT_NULL)
+ /* XXX should reflect error properly. */
+ return 0;
+
+ err = vm_map (mach_task_self (), &buf, dp->dn_stat.st_size, 0,
+ 1, memobj, 0, 0, VM_PROT_READ, VM_PROT_READ, 0);
+ mach_port_deallocate (mach_task_self (), memobj);
+ assert (!err);
+
+ diskfs_set_node_atime (dp);
+
+ for (curoff = buf;
+ !hit && curoff < buf + dp->dn_stat.st_size;
+ curoff += FAT_DIR_REC_LEN)
+ {
+ entry = (struct dirrect *) curoff;
+
+ if (entry->name[0] == FAT_DIR_NAME_LAST)
+ break;
+ if ((char) entry->name[0] != FAT_DIR_NAME_DELETED
+ && memcmp (entry->name, FAT_DIR_NAME_DOT, 11)
+ && memcmp (entry->name, FAT_DIR_NAME_DOTDOT, 11))
+ hit = 1;
+ }
+
+ diskfs_set_node_atime (dp);
+ if (diskfs_synchronous)
+ diskfs_node_update (dp, 1);
+
+ munmap ((caddr_t) buf, dp->dn_stat.st_size);
+
+ return !hit;
+}
+
+/* Make DS an invalid dirstat. */
+error_t
+diskfs_drop_dirstat (struct node *dp, struct dirstat *ds)
+{
+ if (ds->type != LOOKUP)
+ {
+ assert (ds->mapbuf);
+ munmap ((caddr_t) ds->mapbuf, ds->mapextent);
+ ds->type = LOOKUP;
+ }
+ return 0;
+}
+
+
+/* Implement the diskfs_get_directs callback as described in
+ <hurd/diskfs.h>. */
+error_t
+diskfs_get_directs (struct node *dp,
+ int entry,
+ int nentries,
+ char **data,
+ u_int *datacnt,
+ vm_size_t bufsiz,
+ int *amt)
+{
+ volatile vm_size_t allocsize;
+ struct dirrect *ep;
+ struct dirent *userp;
+ int i;
+ char *datap;
+ volatile int ouralloc = 0;
+ error_t err;
+ vm_prot_t prot = VM_PROT_READ;
+ memory_object_t memobj;
+ vm_address_t buf = 0, bufp;
+ vm_size_t buflen = 0;
+
+ /* Allocate some space to hold the returned data. */
+ allocsize = bufsiz ? round_page (bufsiz) : vm_page_size * 4;
+ if (allocsize > *datacnt)
+ {
+ *data = mmap (0, allocsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ ouralloc = 1;
+ }
+
+ /* Map in the directory contents. */
+ memobj = diskfs_get_filemap (dp, prot);
+
+ if (memobj == MACH_PORT_NULL)
+ return errno;
+
+ /* We allow extra space in case we have to do an EXTEND. */
+ buflen = round_page (dp->dn_stat.st_size);
+ err = vm_map (mach_task_self (),
+ &buf, buflen, 0, 1, memobj, 0, 0, prot, prot, 0);
+ mach_port_deallocate (mach_task_self (), memobj);
+ if (err)
+ return err;
+
+ bufp = buf;
+ for (i = 0; i < entry; i ++)
+ {
+ /* The root directory in FAT file systems doesn't contain
+ entries for DOT and DOTDOT, they are special cased below. */
+ if (dp == diskfs_root_node && i < 2)
+ continue;
+
+ ep = (struct dirrect *) bufp;
+
+ if (bufp >= buf + buflen || (char)ep->name[0] == FAT_DIR_NAME_LAST)
+ {
+ /* Not that many entries in the directory; return nothing. */
+ if (allocsize > *datacnt)
+ munmap (data, allocsize);
+ munmap ((caddr_t) buf, buflen);
+ *datacnt = 0;
+ *amt = 0;
+ return 0;
+ }
+
+ /* Ignore and skip deleted and label entries (catches also long
+ filenames). */
+ if ((char)ep->name[0] == FAT_DIR_NAME_DELETED
+ || (ep->attribute & FAT_DIR_ATTR_LABEL))
+ i--;
+ bufp = bufp + FAT_DIR_REC_LEN;
+ }
+
+ /* Now copy entries one at a time. */
+ i = 0;
+ datap = *data;
+ while (((nentries == -1) || (i < nentries))
+ && (!bufsiz || datap - *data < bufsiz)
+ && bufp < buf + buflen)
+ {
+ char name[13];
+ size_t namlen, reclen;
+ struct dirrect dot = { FAT_DIR_NAME_DOT, FAT_DIR_ATTR_DIR };
+ struct dirrect dotdot = { FAT_DIR_NAME_DOTDOT, FAT_DIR_ATTR_DIR };
+
+ /* The root directory in FAT file systems doesn't contain
+ entries for DOT and DOTDOT, they are special cased below. */
+ if (dp == diskfs_root_node && (i + entry == 0))
+ ep = &dot;
+ else if (dp == diskfs_root_node && (i + entry == 1))
+ ep = &dotdot;
+ else
+ ep = (struct dirrect *) bufp;
+
+ if ((char)ep->name[0] == FAT_DIR_NAME_LAST)
+ {
+ /* Last entry. */
+ bufp = buf + buflen;
+ continue;
+ }
+
+ if ((char)ep->name[0] == FAT_DIR_NAME_DELETED || (ep->attribute & FAT_DIR_ATTR_LABEL))
+ {
+ bufp = bufp + FAT_DIR_REC_LEN;
+ continue;
+ }
+
+ /* See if there's room to hold this one. */
+
+ fat_to_unix_filename(ep->name, name);
+ namlen = strlen(name);
+
+ /* Perhaps downcase it? */
+
+ reclen = sizeof (struct dirent) + namlen;
+ reclen = (reclen + 3) & ~3;
+
+ /* Expand buffer if necessary. */
+ if (datap - *data + reclen > allocsize)
+ {
+ vm_address_t newdata;
+
+ vm_allocate (mach_task_self (), &newdata,
+ (ouralloc
+ ? (allocsize *= 2)
+ : (allocsize = vm_page_size * 2)), 1);
+ memcpy ((void *) newdata, (void *) *data, datap - *data);
+
+ if (ouralloc)
+ munmap (*data, allocsize / 2);
+
+ datap = (char *) newdata + (datap - *data);
+ *data = (char *) newdata;
+ ouralloc = 1;
+ }
+
+ userp = (struct dirent *) datap;
+
+ /* Fill in entry. */
+ {
+ ino_t inode;
+ inode_t v_inode;
+ vi_key_t entry_key;
+
+ entry_key.dir_inode = dp->cache_id;
+ entry_key.dir_offset = bufp - buf;
+
+ vi_rlookup (entry_key, &inode, &v_inode, 1);
+ userp->d_fileno = inode;
+ }
+ userp->d_type = DT_UNKNOWN;
+ userp->d_reclen = reclen;
+ userp->d_namlen = namlen;
+ memcpy (userp->d_name, name, namlen);
+ userp->d_name[namlen] = '\0';
+
+ /* And move along. */
+ datap = datap + reclen;
+ if (!(dp == diskfs_root_node && i + entry < 2))
+ bufp = bufp + FAT_DIR_REC_LEN;
+ i++;
+ }
+
+ /* If we didn't use all the pages of a buffer we allocated, free
+ the excess. */
+ if (ouralloc
+ && round_page (datap - *data) < round_page (allocsize))
+ munmap ((caddr_t) round_page (datap),
+ round_page (allocsize) - round_page (datap - *data));
+
+ munmap ((caddr_t) buf, buflen);
+
+ /* Return. */
+ *amt = i;
+ *datacnt = datap - *data;
+ return 0;
+}
diff --git a/fatfs/fat.c b/fatfs/fat.c
new file mode 100644
index 00000000..14926ff3
--- /dev/null
+++ b/fatfs/fat.c
@@ -0,0 +1,761 @@
+/* fat.c - Support for FAT filesystems.
+ Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+#include <error.h>
+#include <limits.h>
+#include <errno.h>
+#include <assert.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <hurd/store.h>
+#include <hurd/diskfs.h>
+
+#include "fatfs.h"
+
+/* Unprocessed superblock. */
+struct boot_sector *sblock;
+
+/* Processed sblock info. */
+fat_t fat_type;
+size_t bytes_per_sector;
+size_t log2_bytes_per_sector;
+size_t sectors_per_cluster;
+size_t bytes_per_cluster;
+unsigned int log2_bytes_per_cluster;
+size_t sectors_per_fat;
+size_t total_sectors;
+size_t nr_of_root_dir_sectors;
+size_t first_root_dir_byte;
+size_t first_data_sector;
+vm_offset_t first_data_byte;
+size_t first_fat_sector;
+cluster_t nr_of_clusters;
+
+/* Hold this lock while converting times using gmtime. */
+pthread_spinlock_t epoch_to_time_lock = PTHREAD_SPINLOCK_INITIALIZER;
+
+/* Hold this lock while allocating a new cluster in the FAT. */
+pthread_spinlock_t allocate_free_cluster_lock = PTHREAD_SPINLOCK_INITIALIZER;
+
+/* Where to look for the next free cluster. This is meant to avoid
+ searching through a nearly full file system from the beginning at
+ every request. It would be better to use the field of the same
+ name in the fs_info block. 2 is the first data cluster in any
+ FAT. */
+cluster_t next_free_cluster = 2;
+
+
+/* Read the superblock. */
+void
+fat_read_sblock (void)
+{
+ error_t err;
+ int read;
+
+ sblock = malloc (sizeof (struct boot_sector));
+ err = store_read (store, 0, sizeof (struct boot_sector),
+ (void **) &sblock, &read);
+ if (err)
+ error (1, err, "Could not read superblock");
+
+ if (read_word(sblock->id) != BOOT_SECTOR_ID)
+ error (1, 0, "Could not find valid superblock");
+
+ /* Parse some important bits of the superblock. */
+
+ bytes_per_sector = read_word (sblock->bytes_per_sector);
+ switch (bytes_per_sector)
+ {
+ case 512:
+ log2_bytes_per_sector = 9;
+ break;
+
+ case 1024:
+ log2_bytes_per_sector = 10;
+ break;
+
+ case 2048:
+ log2_bytes_per_sector = 11;
+ break;
+
+ case 4096:
+ log2_bytes_per_sector = 12;
+ break;
+
+ default:
+ error (1, 0, "Invalid number of bytes per sector");
+ };
+
+ sectors_per_cluster = sblock->sectors_per_cluster;
+ if (sectors_per_cluster != 1 && sectors_per_cluster != 2
+ && sectors_per_cluster != 4 && sectors_per_cluster != 8
+ && sectors_per_cluster != 16 && sectors_per_cluster != 32
+ && sectors_per_cluster != 64 && sectors_per_cluster != 128)
+ error (1, 0, "Invalid number of sectors per cluster");
+
+ bytes_per_cluster = sectors_per_cluster << log2_bytes_per_sector;
+ switch (bytes_per_cluster)
+ {
+ case 512:
+ log2_bytes_per_cluster = 9;
+ break;
+
+ case 1024:
+ log2_bytes_per_cluster = 10;
+ break;
+
+ case 2048:
+ log2_bytes_per_cluster = 11;
+ break;
+
+ case 4096:
+ log2_bytes_per_cluster = 12;
+ break;
+
+ case 8192:
+ log2_bytes_per_cluster = 13;
+ break;
+
+ case 16384:
+ log2_bytes_per_cluster = 14;
+ break;
+
+ case 32768:
+ log2_bytes_per_cluster = 15;
+ break;
+
+ default:
+ error (1, 0, "Invalid number of bytes per cluster");
+ };
+
+ total_sectors = read_word (sblock->total_sectors_16)
+ ?: read_word (sblock->total_sectors_32);
+ if (total_sectors * bytes_per_sector > store->size)
+ error (1, 0, "Store is smaller then implied by metadata");
+ if (total_sectors == 0)
+ error (1, 0, "Number of total sectors is zero");
+
+ if (bytes_per_sector & (store->block_size - 1))
+ error (1, 0, "Block size of filesystem is not"
+ " a multiple of the block size of the store");
+
+ if (read_word (sblock->reserved_sectors) == 0)
+ error (1, 0, "Number of reserved sectors is zero");
+ if (sblock->nr_of_fat_tables == 0)
+ error (1, 0, "Number of FATs is zero");
+
+ sectors_per_fat = read_word (sblock->sectors_per_fat_16)
+ ?: read_word (sblock->compat.fat32.sectors_per_fat_32);
+ if (sectors_per_fat == 0)
+ error (1, 0, "Number of sectors per fat is zero");
+
+ nr_of_root_dir_sectors = ((read_word (sblock->nr_of_root_dirents) *
+ FAT_DIR_REC_LEN) - 1) / bytes_per_sector + 1;
+
+ first_root_dir_byte = (read_word (sblock->reserved_sectors)
+ + (sblock->nr_of_fat_tables * sectors_per_fat)) << log2_bytes_per_sector;
+ first_data_sector = (first_root_dir_byte >> log2_bytes_per_sector)
+ + nr_of_root_dir_sectors;
+ first_data_byte = first_data_sector << log2_bytes_per_sector;
+
+ nr_of_clusters = (total_sectors - first_data_sector) / sectors_per_cluster;
+
+ if (nr_of_clusters < FAT12_MAX_NR_OF_CLUSTERS)
+ fat_type = FAT12;
+ else
+ {
+ if (nr_of_clusters < FAT16_MAX_NR_OF_CLUSTERS)
+ fat_type = FAT16;
+ else
+ fat_type = FAT32;
+ }
+
+ if (fat_type == FAT32 && read_word (sblock->compat.fat32.fs_version) != 0)
+ error (1, 0, "Incompatible file system version");
+
+ first_fat_sector = 0;
+ if (fat_type == FAT32 && read_word (sblock->compat.fat32.extension_flags) & 1<<7)
+ {
+ first_fat_sector = (read_word (sblock->compat.fat32.extension_flags) & 0x0f);
+ if (first_fat_sector > sblock->nr_of_fat_tables)
+ error (1, 0, "Active FAT table does not exist");
+ first_fat_sector *= sectors_per_fat;
+ }
+ first_fat_sector += read_word (sblock->reserved_sectors);
+}
+
+
+/* Write NEXT_CLUSTER in the FAT at position CLUSTER.
+ You must call this from inside diskfs_catch_exception.
+ Returns 0 (always succeeds). */
+error_t
+fat_write_next_cluster(cluster_t cluster, cluster_t next_cluster)
+{
+ loff_t fat_entry_offset;
+ cluster_t data;
+
+ /* First data cluster is cluster 2. */
+ assert (cluster >= 2 && cluster < nr_of_clusters + 2);
+
+ switch (fat_type)
+ {
+ case FAT12:
+ if (next_cluster == FAT_BAD_CLUSTER)
+ next_cluster = FAT12_BAD_CLUSTER;
+ else if (next_cluster == FAT_EOC)
+ next_cluster = FAT12_EOC;
+
+ fat_entry_offset = (cluster * 3) / 2;
+ data = read_word (fat_image + fat_entry_offset);
+ if (cluster & 1)
+ data = (data & 0xf) | ((next_cluster & 0xfff) << 4);
+ else
+ data = (data & 0xf000) | (next_cluster & 0xfff);
+
+ write_word (fat_image + fat_entry_offset, data);
+ break;
+
+ case FAT16:
+ if (next_cluster == FAT_BAD_CLUSTER)
+ next_cluster = FAT16_BAD_CLUSTER;
+ else if (next_cluster == FAT_EOC)
+ next_cluster = FAT16_EOC;
+
+ fat_entry_offset = cluster * 2;
+ write_word (fat_image + fat_entry_offset, next_cluster);
+ break;
+
+ case FAT32:
+ default: /* To silence gcc warning. */
+ if (next_cluster == FAT_BAD_CLUSTER)
+ next_cluster = FAT32_BAD_CLUSTER;
+ else if (next_cluster == FAT_EOC)
+ next_cluster = FAT32_EOC;
+
+ fat_entry_offset = cluster * 4;
+ write_dword (fat_image + fat_entry_offset, next_cluster & 0x0fffffff);
+ }
+
+ return 0;
+}
+
+/* Read the FAT entry at position CLUSTER into NEXT_CLUSTER.
+ You must call this from inside diskfs_catch_exception.
+ Returns 0 (always succeeds). */
+error_t
+fat_get_next_cluster(cluster_t cluster, cluster_t *next_cluster)
+{
+ loff_t fat_entry_offset;
+
+ /* First data cluster is cluster 2. */
+ assert (cluster >= 2 && cluster < nr_of_clusters + 2);
+
+ switch (fat_type)
+ {
+ case FAT12:
+ fat_entry_offset = (cluster * 3) / 2;
+ *next_cluster = read_word (fat_image + fat_entry_offset);
+ if (cluster & 1)
+ *next_cluster = *next_cluster >> 4;
+ else
+ *next_cluster &= 0xfff;
+
+ if (*next_cluster == FAT12_BAD_CLUSTER)
+ *next_cluster = FAT_BAD_CLUSTER;
+ else if (*next_cluster >= FAT12_EOC)
+ *next_cluster = FAT_EOC;
+ break;
+
+ case FAT16:
+ fat_entry_offset = cluster * 2;
+ *next_cluster = read_word (fat_image + fat_entry_offset);
+ if (*next_cluster == FAT16_BAD_CLUSTER)
+ *next_cluster = FAT_BAD_CLUSTER;
+ else if (*next_cluster >= FAT16_EOC)
+ *next_cluster = FAT_EOC;
+ break;
+
+ case FAT32:
+ default: /* To silence gcc warning. */
+ fat_entry_offset = cluster * 4;
+ *next_cluster = read_dword (fat_image + fat_entry_offset);
+ *next_cluster &= 0x0fffffff;
+ if (*next_cluster == FAT32_BAD_CLUSTER)
+ *next_cluster = FAT_BAD_CLUSTER;
+ else if (*next_cluster >= FAT32_EOC)
+ *next_cluster = FAT_EOC;
+ }
+
+ return 0;
+}
+
+/* Allocate a new cluster, write CONTENT into the FAT at this new
+ clusters position. At success, 0 is returned and CLUSTER contains
+ the cluster number allocated. Otherwise, ENOSPC is returned if the
+ filesystem is full.
+ You must call this from inside diskfs_catch_exception. */
+error_t
+fat_allocate_cluster (cluster_t content, cluster_t *cluster)
+{
+ error_t err = 0;
+ cluster_t old_next_free_cluster;
+ int wrapped = 0;
+ cluster_t found_cluster = FAT_FREE_CLUSTER;
+
+ assert (content != FAT_FREE_CLUSTER);
+
+ pthread_spin_lock (&allocate_free_cluster_lock);
+ old_next_free_cluster = next_free_cluster;
+
+ /* Loop over all clusters, starting from next_free_cluster and
+ wrapping if reaching the end of the FAT, until we either find an
+ unallocated cluster, or we have to give up because all clusters
+ are allocated. */
+ do
+ {
+ cluster_t next_free_content;
+
+ fat_get_next_cluster (next_free_cluster, &next_free_content);
+
+ if (next_free_content == FAT_FREE_CLUSTER)
+ found_cluster = next_free_cluster;
+
+ if (++next_free_cluster == nr_of_clusters + 2)
+ {
+ next_free_cluster = 2;
+ wrapped = 1;
+ }
+ }
+ while (found_cluster == FAT_FREE_CLUSTER
+ && !(wrapped && next_free_cluster == old_next_free_cluster));
+
+ if (found_cluster != FAT_FREE_CLUSTER)
+ {
+ *cluster = found_cluster;
+ fat_write_next_cluster(found_cluster, content);
+ }
+ else
+ err = ENOSPC;
+
+ pthread_spin_unlock (&allocate_free_cluster_lock);
+ return err;
+}
+
+/* Extend the cluster chain to maximum size or new_last_cluster,
+ whatever is less. If we reach the end of the file, and CREATE is
+ true, allocate new blocks until there is either no space on the
+ device or new_last_cluster are allocated. (new_last_cluster: 0 is
+ the first cluster of the file). */
+error_t
+fat_extend_chain (struct node *node, cluster_t new_last_cluster, int create)
+{
+ error_t err = 0;
+ struct disknode *dn = node->dn;
+ struct cluster_chain *table;
+ int offs;
+ cluster_t left, prev_cluster, cluster;
+
+ error_t allocate_new_table(struct cluster_chain **table)
+ {
+ struct cluster_chain *t;
+
+ t = *table;
+ *table = malloc (sizeof (struct cluster_chain));
+ if (!*table)
+ return ENOMEM;
+ (*table)->next = 0;
+ if (t)
+ dn->last = t->next = *table;
+ else
+ dn->last = dn->first = *table;
+ return 0;
+ }
+
+ pthread_spin_lock (&dn->chain_extension_lock);
+
+ /* If we already have what we need, or we have all clusters that are
+ available without allocating new ones, go out. */
+ if (new_last_cluster < dn->length_of_chain
+ || (!create && dn->chain_complete))
+ {
+ pthread_spin_unlock (&dn->chain_extension_lock);
+ return 0;
+ }
+
+ left = new_last_cluster + 1 - dn->length_of_chain;
+
+ table = dn->last;
+ if (table)
+ {
+ offs = (dn->length_of_chain - 1) & (CLUSTERS_PER_TABLE - 1);
+ prev_cluster = table->cluster[offs];
+ }
+ else
+ {
+ offs = CLUSTERS_PER_TABLE - 1;
+ prev_cluster = FAT_FREE_CLUSTER;
+ }
+
+ while (left)
+ {
+ if (dn->chain_complete)
+ {
+ err = fat_allocate_cluster(FAT_EOC, &cluster);
+ if (err)
+ break;
+ if (prev_cluster)
+ fat_write_next_cluster(prev_cluster, cluster);
+ else
+ /* XXX: Also write this to dirent structure! */
+ dn->start_cluster = cluster;
+ }
+ else
+ {
+ if (prev_cluster != FAT_FREE_CLUSTER)
+ err = fat_get_next_cluster(prev_cluster, &cluster);
+ else
+ cluster = dn->start_cluster;
+ if (cluster == FAT_EOC || cluster == FAT_FREE_CLUSTER)
+ {
+ dn->chain_complete = 1;
+ if (create)
+ continue;
+ else
+ break;
+ }
+ }
+ prev_cluster = cluster;
+ offs++;
+ if (offs == CLUSTERS_PER_TABLE)
+ {
+ offs = 0;
+ err = allocate_new_table(&table);
+ if (err)
+ break;
+ }
+ table->cluster[offs] = cluster;
+ dn->length_of_chain++;
+ left--;
+ }
+
+ if (dn->length_of_chain << log2_bytes_per_cluster > node->allocsize)
+ node->allocsize = dn->length_of_chain << log2_bytes_per_cluster;
+
+ pthread_spin_unlock (&dn->chain_extension_lock);
+ return err;
+}
+
+/* Returns in DISK_CLUSTER the disk cluster corresponding to cluster
+ CLUSTER in NODE. If there is no such cluster yet, but CREATE is
+ true, then it is created, otherwise EINVAL is returned. */
+error_t
+fat_getcluster (struct node *node, cluster_t cluster, int create,
+ cluster_t *disk_cluster)
+{
+ error_t err = 0;
+ cluster_t chains_to_go = cluster >> LOG2_CLUSTERS_PER_TABLE;
+ cluster_t offs = cluster & (CLUSTERS_PER_TABLE - 1);
+ struct cluster_chain *chain;
+
+ if (cluster >= node->dn->length_of_chain)
+ {
+ err = fat_extend_chain (node, cluster, create);
+ if (err)
+ return err;
+ if (cluster >= node->dn->length_of_chain)
+ {
+ assert (!create);
+ return EINVAL;
+ }
+ }
+ chain = node->dn->first;
+ while (chains_to_go--)
+ {
+ assert (chain);
+ chain = chain->next;
+ }
+ assert (chain);
+ *disk_cluster = chain->cluster[offs];
+ return 0;
+}
+
+void
+fat_truncate_node (struct node *node, cluster_t clusters_to_keep)
+{
+ struct cluster_chain *next;
+ cluster_t count;
+ cluster_t offs;
+ cluster_t pos;
+
+ /* The root dir of a FAT12/16 fs is of fixed size, while the root
+ dir of a FAT32 fs must never decease to exist. */
+ assert (! (((fat_type == FAT12 || fat_type == FAT16) && node == diskfs_root_node)
+ || (fat_type == FAT32 && node == diskfs_root_node && clusters_to_keep == 0)));
+
+ /* Expand the cluster chain, because we have to know the complete tail. */
+ fat_extend_chain (node, FAT_EOC, 0);
+ if (clusters_to_keep == node->dn->length_of_chain)
+ return;
+ assert (clusters_to_keep < node->dn->length_of_chain);
+
+ /* Truncation happens here. */
+ next = node->dn->first;
+ if (clusters_to_keep == 0)
+ {
+ /* Deallocate the complete file. */
+ node->dn->start_cluster = 0;
+ pos = count = offs = 0;
+ node->dn->last = 0;
+ }
+ else
+ {
+ count = (clusters_to_keep - 1) >> LOG2_CLUSTERS_PER_TABLE;
+ offs = (clusters_to_keep - 1) & (CLUSTERS_PER_TABLE - 1);
+ while (count-- > 0)
+ {
+ assert (next);
+
+ /* This cluster is now the last cluster in the chain. */
+ if (count == 0)
+ node->dn->last = next;
+
+ next = next->next;
+ }
+ assert (next);
+ fat_write_next_cluster (next->cluster[offs++], FAT_EOC);
+ pos = clusters_to_keep;
+ }
+
+ /* Purge dangling clusters. If we die here, scandisk will have to
+ clean up the remains. */
+ while (pos < node->dn->length_of_chain)
+ {
+ if (offs == CLUSTERS_PER_TABLE)
+ {
+ offs = 0;
+ next = next->next;
+ assert(next);
+ }
+ fat_write_next_cluster(next->cluster[offs++], 0);
+ pos++;
+ }
+
+ /* Free now unused tables. (Could be done in one run with the above.) */
+ next = node->dn->first;
+ if (clusters_to_keep != 0)
+ {
+ count = (clusters_to_keep - 1) >> LOG2_CLUSTERS_PER_TABLE;
+ offs = (clusters_to_keep - 1) & (CLUSTERS_PER_TABLE - 1);
+ while (count-- > 0)
+ {
+ assert (next);
+ next = next->next;
+ }
+ assert (next);
+ next = next->next;
+ }
+ while (next)
+ {
+ struct cluster_chain *next_next = next->next;
+ free (next);
+ next = next_next;
+ }
+
+ if (clusters_to_keep == 0)
+ node->dn->first = 0;
+
+ node->dn->length_of_chain = clusters_to_keep;
+}
+
+
+/* Count the number of free clusters in the FAT. */
+int
+fat_get_freespace (void)
+{
+ int free_clusters = 0;
+ cluster_t curr_cluster;
+ cluster_t next_cluster;
+ error_t err;
+
+ err = diskfs_catch_exception ();
+ if (!err)
+ {
+ /* First cluster is the 3rd entry in the FAT table. */
+ for (curr_cluster = 2; curr_cluster < nr_of_clusters + 2;
+ curr_cluster++)
+ {
+ fat_get_next_cluster (curr_cluster, &next_cluster);
+ if (next_cluster == FAT_FREE_CLUSTER)
+ free_clusters++;
+ }
+ }
+ diskfs_end_catch_exception ();
+
+ return free_clusters;
+}
+
+
+/* FILE must be a buffer with 13 characters. */
+void fat_to_unix_filename(const char *name, char *file)
+{
+ int npos;
+ int fpos = 0;
+ int ext = 0;
+
+ for (npos = 0; npos < 11; npos++)
+ {
+ if (name[npos] == ' ')
+ {
+ if (ext)
+ {
+ break;
+ }
+ else
+ {
+ file[fpos] = '.';
+ fpos++;
+ ext = 1;
+ while (npos < 7 && name[npos+1] == ' ') npos++;
+ }
+ }
+ else
+ {
+ file[fpos] = name[npos];
+ fpos++;
+ if (npos == 7)
+ {
+ file[fpos] = '.';
+ fpos++;
+ ext = 1;
+ }
+ }
+ }
+ if (ext && file[fpos-1] == '.')
+ file[fpos-1] = '\0';
+ else
+ file[fpos] = '\0';
+}
+
+void
+fat_from_unix_filename(char *fn, const char *un, int ul)
+{
+ int fp = 0;
+ int up = 0;
+ int ext = 0;
+
+ while (fp < 11)
+ {
+ if (up == ul)
+ {
+ /* We parsed the complete unix filename. */
+ while (fp < 11)
+ fn[fp++] = ' ';
+ }
+ else
+ {
+ if (!ext)
+ {
+ if (un[up] == '.')
+ {
+ while (fp < 8)
+ fn[fp++] = ' ';
+ ext = 1;
+ un++;
+ }
+ else if (fp == 8)
+ {
+ while (un[up++] != '.' && up < ul);
+ ext = 1;
+ }
+ else
+ fn[fp++] = toupper(un[ul++]);
+ }
+ else
+ {
+ if (un[up] == '.')
+ {
+ while (fp < 11)
+ fn[fp++] = ' ';
+ }
+ else
+ fn[fp++] = toupper(un[up++]);
+ }
+ }
+ }
+}
+
+
+/* Return Epoch-based time from a MSDOS time/date pair. */
+void
+fat_to_epoch (char *date, char *time, struct timespec *ts)
+{
+ struct tm tm;
+
+ /* Date format:
+ Bits 0-4: Day of month (1-31).
+ Bits 5-8: Month of year (1-12).
+ Bits 9-15: Count of years from 1980 (0-127).
+
+ Time format:
+ Bits 0-4: 2-second count (0-29).
+ Bits 5-10: Minutes (0-59).
+ Bits 11-15: Hours (0-23).
+ */
+
+ tm.tm_year = (read_word (date) >> 9) + 80;
+ tm.tm_mon = ((read_word (date) & 0x1ff) >> 5) - 1;
+ tm.tm_mday = read_word (date) & 0x1f;
+ tm.tm_hour = (read_word (time) >> 11);
+ tm.tm_min = (read_word (time) & 0x7ff) >> 5;
+ tm.tm_sec = read_word (time) & 0x1f;
+ tm.tm_isdst = 0;
+
+ ts->tv_sec = timegm (&tm);
+ ts->tv_nsec = 0;
+}
+
+/* Return MSDOS time/date pair from Epoch-based time. */
+void
+fat_from_epoch (char *date, char *time, time_t *tp)
+{
+ struct tm *tm;
+
+ pthread_spin_lock (&epoch_to_time_lock);
+ tm = gmtime (tp);
+
+ /* Date format:
+ Bits 0-4: Day of month (1-31).
+ Bits 5-8: Month of year (1-12).
+ Bits 9-15: Count of years from 1980 (0-127).
+
+ Time format:
+ Bits 0-4: 2-second count (0-29).
+ Bits 5-10: Minutes (0-59).
+ Bits 11-15: Hours (0-23).
+ */
+
+ write_word(date, tm->tm_mday | ((tm->tm_mon + 1) << 5)
+ | ((tm->tm_year - 80) << 9));
+ write_word(time, (tm->tm_hour << 11) | (tm->tm_min << 5)
+ | (tm->tm_sec >> 1));
+ pthread_spin_unlock (&epoch_to_time_lock);
+}
diff --git a/fatfs/fat.h b/fatfs/fat.h
new file mode 100644
index 00000000..58b45c63
--- /dev/null
+++ b/fatfs/fat.h
@@ -0,0 +1,399 @@
+/* fat.h - Support for FAT filesystems interfaces.
+ Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef FAT_H
+#define FAT_H
+
+/* Specification of the FAT12/16/32 filesystem format. */
+
+/* Overview
+ --------
+
+ Any FAT fs consists of several regions, which follow immediately
+ after each other.
+
+ Reserved
+
+ The reserved region consists of the boot sector, and with it the
+ BIOS Paramter Block, which contains all necessary data about the
+ filesystem like sector size, number of clusters etc. It also
+ holds the filesystem info block.
+
+ The reserved region of FAT32 filesystems also hold a backup copy
+ of the root sector at sector 6 (usually), followed by a backup
+ copy of the filesystem info sector.
+
+ The number of sectors occupied by the reserved region is stored
+ in the reserved region as well, in the word at offset 14
+ (reserved_sectors).
+
+ FAT
+
+ The FAT region contains the File Allocation Table, which is a
+ linked list of clusters occupied by each file or directory.
+ There might be multiple FAT tables in the FAT region, for
+ redundancy.
+
+ The number of FATs is stored in the reserved region, in the byte
+ at offset 16 (nr_of_fat_tables). The number of sectors per FAT is
+ stored in the word at offset 22 (sectors_per_fat_16) or, if this
+ is zero (as it is for FAT32), in the doubleword at offset 36
+ (sectors_per_fat_32).
+
+ Root Directory
+
+ In FAT12/16, the root directory entries allocate their own region
+ and are not accessed through the FAT.
+
+ The size of this region is determined by the word at offset 17
+ (nr_of_root_dirents). You have to multiply this with the nr of
+ bytes per entry, and divide through the number of bytes per
+ sector, rounding up. On FAT32 filesystems, this region does not
+ exist, and nr_of_root_dirents is zero. The FAT32 root directory
+ is accessed through the FAT as any other directory is.
+
+ Data
+
+ The data region occupies the rest of the filesystem and stores
+ the actual file and directory data. It is separated in clusters,
+ which are indexed in the FAT.
+
+ The size of the data region is stored in the word at offset 19
+ (total_sectors_16) or, if this is zero, in the doubleword at
+ offset 32 (total_sectors_32).
+
+
+ NOTE that all meta data in a FAT filesystem is stored in little endian
+ format.
+
+*/
+
+/* The supported FAT types. */
+
+enum fat { FAT12, FAT16, FAT32 };
+typedef enum fat fat_t;
+
+/* The FAT type is determined by the number of clusters in the data
+ region, and nothing else. The maximal number of clusters for a
+ FAT12 and FAT16 respectively is defined here.
+*/
+
+#define FAT12_MAX_NR_OF_CLUSTERS 4084
+#define FAT16_MAX_NR_OF_CLUSTERS 65524
+#define FAT32_MAX_NR_OF_CLUSTERS (FAT32_BAD_CLUSTER - 1)
+
+struct boot_sector
+{
+ /* Unused. */
+ unsigned char jump_to_boot_code[3]; /* 0, typ. 0xeb 0x?? 0x90 */
+ unsigned char oem_name[8]; /* 3, typ. "MSWIN4.1" */
+
+ /* Sector and Cluster size.
+ bytes_per_sector is usually 512, but 1024, 2048, 4096 are also allowed.
+ sectors_per_cluster is one of 1, 2, 4, 8, 16, 32, 64, 128.
+ Note that bytes per cluster (product of the two) must be <= 32768. */
+ unsigned char bytes_per_sector[2]; /* 11 */
+ unsigned char sectors_per_cluster; /* 13 */
+
+ /* Size of the various regions.
+ reserved_sectors must not be zero and is typically 1 on FAT12/16
+ filesystems and 32 on FAT32 filesystems.
+ nr_of_fat_tables must not be zero and is typically 2.
+ nr_of_root_dirents must be zero on FAT32 filesystems.
+ For FAT12/16, the value multiplied with DIR_ENTRY_SIZE (32)
+ should always be a multiple of bytes_per_sector to retain
+ compatibility. For FAT16, 512 should be used.
+ total_sectors_16 contains the complete number of sectors if not zero.
+ If zero, the number of sectors is stored in total_sectors_32. */
+ unsigned char reserved_sectors[2]; /* 14 */
+ unsigned char nr_of_fat_tables; /* 16 */
+ unsigned char nr_of_root_dirents[2]; /* 17 */
+ unsigned char total_sectors_16[2]; /* 19 */
+
+ /* Media descriptor.
+ Allowed are values between 0xf0 and 0xff.
+ 0xf8 is a fixed hardware (disk), 0xf0 denotes a removable media.
+ Must be the same as the first byte in the FAT (compatibility
+ with DOS 1.x). */
+ unsigned char media_descriptor; /* 21 */
+
+ /* Size of one FAT.
+ On FAT32 systems, this value must be zero and sectors_per_fat_32
+ used instead. */
+ unsigned char sectors_per_fat_16[2]; /* 22 */
+
+ /* Disk geometry. Unused. */
+ unsigned char sectors_per_track[2]; /* 24 */
+ unsigned char nr_of_heads[2]; /* 26 */
+ unsigned char nr_of_hidden_sectors[4]; /* 28 */
+
+ /* See total_sectors_16. */
+ unsigned char total_sectors_32[4]; /* 32 */
+
+ /* FAT specific information.
+ Starting with offset 36, FAT12/16 filesystems differ from FAT32
+ filesystems. */
+ union
+ {
+ struct
+ {
+ unsigned char drive; /* 36 */
+ unsigned char reserved; /* 37 */
+
+ /* Boot signature.
+ Value is 0x29.
+ Indicates that the following three fields
+ are present. */
+ unsigned char boot_signature; /* 38 */
+
+ /* Identifier.
+ serial is an unique identifier for removable media.
+ label is the filesystem label, which must match the label
+ stored in the root directory entry which has DIR_ATTR_LABEL
+ set. If no name is specified, the content is "NO NAME ".
+ fs_type: One of "FAT12 ", "FAT16 ", "FAT ".
+ Don't use. */
+ unsigned char serial[4]; /* 39 */
+ unsigned char label[11]; /* 43 */
+ unsigned char fs_type[8]; /* 54 */
+ } fat;
+ struct
+ {
+ /* See sectors_per_fat_16. */
+ unsigned char sectors_per_fat_32[4]; /* 36 */
+
+ /* Extension flags.
+ Bits 0-3: Zero based nr of active FAT.
+ Bit 7: If 0, all FATs are active and should be kept up to date.
+ If 1, only the active FAT (see bits 0-3) should be used.
+ The rest of the bits are reserved. */
+ unsigned char extension_flags[2]; /* 40 */
+
+ /* Filesystem version.
+ The high byte is the major number, the low byte the minor version.
+ Don't mount if either version number is higher than known versions. */
+ unsigned char fs_version[2]; /* 42 */
+
+ /* Root cluster.
+ The cluster where the root directory starts. */
+ unsigned char root_cluster[4]; /* 44 */
+
+ /* Filesystem Info sector.
+ The setor number of the filesystem info block in the
+ reserved area. */
+ unsigned char fs_info_sector[2]; /* 48 */
+
+ /* Backup boot sector.
+ The sector of the backup copy of the boot sector.
+ Should be 6, so it can be used even if this field is
+ corrupted. */
+ unsigned char backup_boot_sector[2]; /* 50 */
+ unsigned char reserved1[12]; /* 52 */
+
+ /* See fat structure above, with the following exception:
+ fs_type is "FAT32 ". */
+ unsigned char drive_number; /* 64 */
+ unsigned char reserved2; /* 65 */
+ unsigned char boot_signature; /* 66 */
+ unsigned char serial[4]; /* 67 */
+ unsigned char label[11]; /* 71 */
+ unsigned char fs_type[8]; /* 82 */
+ } fat32;
+ } compat;
+ unsigned char unused[420]; /* 90 */
+
+ /* Expected ID at offset 510.
+ */
+#define BOOT_SECTOR_ID 0xaa55
+
+ unsigned char id[2]; /* 510 */
+};
+
+/* File System Info Block. */
+
+#define FAT_FS_INFO_LEAD_SIGNATURE 0x41615252L
+#define FAT_FS_INFO_STRUCT_SIGNATURE 0x61417272L
+#define FAT_FS_INFO_TRAIL_SIGNAURE 0xaa550000L
+#define FAT_FS_NR_OF_FREE_CLUSTERS_UNKNOWN 0xffffffffL
+#define FAT_FS_NEXT_FREE_CLUSTER_UNKNOWN 0xffffffffL
+
+struct fat_fs_info
+{
+ unsigned char lead_signature[4];
+ unsigned char reserved1[480];
+ unsigned char struct_signature[4];
+ unsigned char nr_of_free_clusters[4];
+ unsigned char next_free_cluster[4];
+ unsigned char reserved2[12];
+ unsigned char trail_signature[4];
+};
+
+/* File Allocation Table, special entries. */
+
+#define FAT_FREE_CLUSTER 0
+
+#define FAT12_BAD_CLUSTER 0x0ff7
+#define FAT16_BAD_CLUSTER 0xfff7
+#define FAT32_BAD_CLUSTER 0x0ffffff7L
+#define FAT_BAD_CLUSTER FAT32_BAD_CLUSTER
+
+#define FAT12_EOC 0x0ff8
+#define FAT16_EOC 0xfff8
+#define FAT32_EOC 0x0ffffff8
+#define FAT_EOC FAT32_EOC
+
+/* Directories. */
+
+#define FAT_DIR_REC_LEN 32
+#define FAT_DIR_RECORDS(x) FAT_DIR_REC_LEN /* Something else for vfat. */
+
+#define FAT_DIR_ATTR_RDONLY 0x01
+#define FAT_DIR_ATTR_HIDDEN 0x02
+#define FAT_DIR_ATTR_SYSTEM 0x04
+#define FAT_DIR_ATTR_LABEL 0x08
+#define FAT_DIR_ATTR_DIR 0x10
+#define FAT_DIR_ATTR_ARCHIVE 0x20
+#define FAT_DIR_ATTR_LONGNAME (DIR_ATTR_RDONLY | DIR_ATTR_HIDDEN \
+ | DIR_ATTR_SYSTEM | DIR_ATTR_LABEL)
+
+#define FAT_DIR_NAME_LAST '\x00'
+#define FAT_DIR_NAME_DELETED '\xe5'
+
+/* If the first character is this, replace it with FAT_DIR_NAME_DELETED
+ after checking for it. */
+#define FAT_DIR_NAME_REPLACE_DELETED '\x05'
+
+#define FAT_DIR_NAME_DOT ". "
+#define FAT_DIR_NAME_DOTDOT ".. "
+
+struct dirrect
+{
+ unsigned char name[11];
+ unsigned char attribute;
+ unsigned char reserved;
+ unsigned char creation_time_centiseconds;
+ unsigned char creation_time[2];
+ unsigned char creation_date[2];
+ unsigned char last_access_date[2];
+ unsigned char first_cluster_high[2];
+ unsigned char write_time[2];
+ unsigned char write_date[2];
+ unsigned char first_cluster_low[2];
+ unsigned char file_size[4];
+};
+
+#define FAT_NAME_MAX 12 /* VFAT: 255 */
+
+extern vm_offset_t first_data_byte;
+extern size_t bytes_per_cluster;
+
+/* A cluster number. */
+typedef unsigned long cluster_t;
+
+#define LOG2_CLUSTERS_PER_TABLE 10
+#define CLUSTERS_PER_TABLE (1 << LOG2_CLUSTERS_PER_TABLE)
+
+struct cluster_chain
+{
+ struct cluster_chain *next;
+ cluster_t cluster[CLUSTERS_PER_TABLE];
+};
+
+/* Prototyping. */
+void fat_read_sblock (void);
+void fat_to_epoch (char *, char *, struct timespec *);
+void fat_from_epoch (char *, char *, time_t *);
+error_t fat_getcluster (struct node *, cluster_t, int, cluster_t *);
+void fat_truncate_node (struct node *, cluster_t);
+error_t fat_extend_chain (struct node *, cluster_t, int);
+int fat_get_freespace (void);
+
+/* Unprocessed superblock. */
+extern struct boot_sector *sblock;
+
+/* Processed sblock info. */
+extern fat_t fat_type;
+extern size_t bytes_per_sector;
+extern size_t log2_bytes_per_sector;
+extern size_t sectors_per_cluster;
+extern size_t bytes_per_cluster;
+extern unsigned int log2_bytes_per_cluster;
+extern size_t sectors_per_fat;
+extern size_t total_sectors;
+extern size_t nr_of_root_dir_sectors;
+extern size_t first_root_dir_byte;
+extern size_t first_data_sector;
+extern vm_offset_t first_data_byte;
+extern size_t first_fat_sector;
+extern cluster_t nr_of_clusters;
+
+/* Numeric conversions for these fields. */
+#include <endian.h>
+#include <byteswap.h>
+
+static inline unsigned int
+read_dword (unsigned char *addr)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return *(unsigned int *)addr;
+#elif BYTE_ORDER == BIG_ENDIAN
+ return bswap_32 (*(unsigned int *) addr);
+#else
+#error unknown byte order
+#endif
+}
+
+static inline unsigned int
+read_word (unsigned char *addr)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return *(unsigned short *)addr;
+#elif BYTE_ORDER == BIG_ENDIAN
+ return bswap_16 (*(unsigned int *) addr);
+#else
+#error unknown byte order
+#endif
+}
+
+static inline void
+write_dword (unsigned char *addr, unsigned int value)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ *(unsigned int *)addr = value;
+#elif BYTE_ORDER == BIG_ENDIAN
+ *(unsigned int *)addr = bswap_32 (value);
+#else
+#error unknown byte order
+#endif
+}
+
+static inline void
+write_word (unsigned char *addr, unsigned int value)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ *(unsigned short *)addr = value;
+#elif BYTE_ORDER == BIG_ENDIAN
+ *(unsigned int *)addr = bswap_16 (value);
+#else
+#error unknown byte order
+#endif
+}
+
+#endif /* FAT_H */
diff --git a/fatfs/fatfs.h b/fatfs/fatfs.h
new file mode 100644
index 00000000..9d385463
--- /dev/null
+++ b/fatfs/fatfs.h
@@ -0,0 +1,127 @@
+/* fatfs.h - Interface for fatfs.
+ Copyright (C) 1997, 1999, 2002, 2003 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG and Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <hurd/diskfs.h>
+#include <hurd/diskfs-pager.h>
+#include <hurd/store.h>
+
+#include "fat.h"
+#include "virt-inode.h"
+
+/* There is no such thing as an inode in this format, all such information
+ being recorded in the directory entry. So we report inode numbers as
+ the start cluster number of the file. When messing around with the
+ directory entry, hold the DIRENT_LOCK. */
+
+struct disknode
+{
+ cluster_t start_cluster;
+
+ /* Links on hash list. */
+ struct node *hnext, **hprevp;
+
+ /* The inode as returned by virtual inode management routines. */
+ inode_t inode;
+
+ /* The directory that hold this file, always hold a reference. */
+ struct node *dirnode;
+
+ pthread_rwlock_t dirent_lock;
+
+ char *link_target; /* For S_ISLNK. */
+
+ size_t translen;
+ char *translator;
+
+ /* Lock to hold while fiddling with this inode's block allocation
+ info. */
+ pthread_rwlock_t alloc_lock;
+ /* Lock to hold while extending this inode's block allocation info.
+ Hold only if you hold readers alloc_lock, then you don't need to
+ hold it if you hold writers alloc_lock already. */
+ pthread_spinlock_t chain_extension_lock;
+ struct cluster_chain *first;
+ struct cluster_chain *last;
+ cluster_t length_of_chain;
+ int chain_complete;
+
+ /* This file's pager. */
+ struct pager *pager;
+
+ /* Index to start a directory lookup at. */
+ int dir_idx;
+};
+
+struct user_pager_info
+{
+ struct node *node;
+ enum pager_type
+ {
+ FAT,
+ FILE_DATA,
+ } type;
+ vm_prot_t max_prot;
+};
+
+/* The physical media. */
+extern struct store *store;
+
+/* The UID and GID for all files in the filesystem. */
+extern uid_t fs_uid;
+extern gid_t fs_gid;
+
+/* Mapped image of the FAT. */
+extern void *fat_image;
+
+/* Handy source of zeroes. */
+extern vm_address_t zerocluster;
+
+extern struct dirrect dr_root_node;
+
+
+#define LOG2_BLOCKS_PER_CLUSTER \
+ (log2_bytes_per_cluster - store->log2_block_size)
+
+#define round_cluster(offs) \
+ ((((offs) + bytes_per_cluster - 1) \
+ >> log2_bytes_per_cluster) << log2_bytes_per_cluster)
+
+#define FAT_FIRST_CLUSTER_BLOCK(cluster) \
+ (((cluster - 2) << LOG2_BLOCKS_PER_CLUSTER) + \
+ (first_data_byte >> store->log2_block_size))
+
+void drop_pager_softrefs (struct node *);
+void allow_pager_softrefs (struct node *);
+void create_fat_pager (void);
+
+void flush_node_pager (struct node *node);
+
+void write_all_disknodes ();
+
+struct node *ifind (ino_t inum);
+
+error_t fat_get_next_cluster (cluster_t cluster, cluster_t *next_cluster);
+void fat_to_unix_filename (const char *, char *);
+
+error_t diskfs_cached_lookup_in_dirbuf (int cache_id, struct node **npp,
+ vm_address_t buf);
+void refresh_node_stats (void);
diff --git a/fatfs/inode.c b/fatfs/inode.c
new file mode 100644
index 00000000..ed6f3f08
--- /dev/null
+++ b/fatfs/inode.c
@@ -0,0 +1,788 @@
+/* inode.c - Inode management routines.
+
+ Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2002, 2003, 2007
+ Free Software Foundation, Inc.
+
+ Modified for fatfs by Marcus Brinkmann <marcus@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+#include "fatfs.h"
+#include "libdiskfs/fs_S.h"
+
+/* These flags aren't actually defined by a header file yet, so
+ temporarily disable them if necessary. */
+#ifndef UF_APPEND
+#define UF_APPEND 0
+#endif
+#ifndef UF_NODUMP
+#define UF_NODUMP 0
+#endif
+#ifndef UF_IMMUTABLE
+#define UF_IMMUTABLE 0
+#endif
+
+#define INOHSZ 512
+#if ((INOHSZ&(INOHSZ-1)) == 0)
+#define INOHASH(ino) ((ino)&(INOHSZ-1))
+#else
+#define INOHASH(ino) (((unsigned)(ino))%INOHSZ)
+#endif
+
+static struct node *nodehash[INOHSZ];
+static size_t nodehash_nr_items;
+
+static error_t read_node (struct node *np, vm_address_t buf);
+
+/* Initialize the inode hash table. */
+void
+inode_init ()
+{
+ int n;
+ for (n = 0; n < INOHSZ; n++)
+ nodehash[n] = 0;
+}
+
+/* Fetch inode INUM, set *NPP to the node structure; gain one user
+ reference and lock the node. */
+error_t
+diskfs_cached_lookup (ino64_t inum, struct node **npp)
+{
+ error_t err;
+ struct node *np;
+ struct disknode *dn;
+
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
+ if (np->cache_id == inum)
+ {
+ np->references++;
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ pthread_mutex_lock (&np->lock);
+ *npp = np;
+ return 0;
+ }
+
+ /* Format specific data for the new node. */
+ dn = malloc (sizeof (struct disknode));
+ if (! dn)
+ {
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ return ENOMEM;
+ }
+ dn->pager = 0;
+ dn->first = 0;
+ dn->last = 0;
+ dn->length_of_chain = 0;
+ dn->chain_complete = 0;
+ dn->chain_extension_lock = PTHREAD_SPINLOCK_INITIALIZER;
+ pthread_rwlock_init (&dn->alloc_lock, NULL);
+ pthread_rwlock_init (&dn->dirent_lock, NULL);
+
+ /* Create the new node. */
+ np = diskfs_make_node (dn);
+ np->cache_id = inum;
+ np->dn->inode = vi_lookup(inum);
+
+ pthread_mutex_lock (&np->lock);
+
+ /* Put NP in NODEHASH. */
+ dn->hnext = nodehash[INOHASH(inum)];
+ if (dn->hnext)
+ dn->hnext->dn->hprevp = &dn->hnext;
+ dn->hprevp = &nodehash[INOHASH(inum)];
+ nodehash[INOHASH(inum)] = np;
+ nodehash_nr_items += 1;
+
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+
+ /* Get the contents of NP off disk. */
+ err = read_node (np, 0);
+
+ if (err)
+ return err;
+ else
+ {
+ *npp = np;
+ return 0;
+ }
+}
+
+/* Fetch inode INUM, set *NPP to the node structure;
+ gain one user reference and lock the node.
+ On the way, use BUF as the directory file map. */
+error_t
+diskfs_cached_lookup_in_dirbuf (int inum, struct node **npp, vm_address_t buf)
+{
+ error_t err;
+ struct node *np;
+ struct disknode *dn;
+
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
+ if (np->cache_id == inum)
+ {
+ np->references++;
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ pthread_mutex_lock (&np->lock);
+ *npp = np;
+ return 0;
+ }
+
+ /* Format specific data for the new node. */
+ dn = malloc (sizeof (struct disknode));
+ if (! dn)
+ {
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ return ENOMEM;
+ }
+ dn->pager = 0;
+ dn->first = 0;
+ dn->last = 0;
+ dn->length_of_chain = 0;
+ dn->chain_complete = 0;
+ dn->chain_extension_lock = PTHREAD_SPINLOCK_INITIALIZER;
+ pthread_rwlock_init (&dn->alloc_lock, NULL);
+ pthread_rwlock_init (&dn->dirent_lock, NULL);
+
+ /* Create the new node. */
+ np = diskfs_make_node (dn);
+ np->cache_id = inum;
+ np->dn->inode = vi_lookup(inum);
+
+ pthread_mutex_lock (&np->lock);
+
+ /* Put NP in NODEHASH. */
+ dn->hnext = nodehash[INOHASH(inum)];
+ if (dn->hnext)
+ dn->hnext->dn->hprevp = &dn->hnext;
+ dn->hprevp = &nodehash[INOHASH(inum)];
+ nodehash[INOHASH(inum)] = np;
+ nodehash_nr_items += 1;
+
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+
+ /* Get the contents of NP off disk. */
+ err = read_node (np, buf);
+
+ if (err)
+ return err;
+ else
+ {
+ *npp = np;
+ return 0;
+ }
+}
+
+/* Lookup node INUM (which must have a reference already) and return
+ it without allocating any new references. */
+struct node *
+ifind (ino_t inum)
+{
+ struct node *np;
+
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
+ {
+ if (np->cache_id != inum)
+ continue;
+
+ assert (np->references);
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ return np;
+ }
+ assert (0);
+}
+
+/* The last reference to a node has gone away; drop it from the hash
+ table and clean all state in the dn structure. */
+void
+diskfs_node_norefs (struct node *np)
+{
+ struct cluster_chain *last = np->dn->first;
+
+ *np->dn->hprevp = np->dn->hnext;
+ if (np->dn->hnext)
+ np->dn->hnext->dn->hprevp = np->dn->hprevp;
+ nodehash_nr_items -= 1;
+
+ while (last)
+ {
+ struct cluster_chain *next = last->next;
+ free(last);
+ last = next;
+ }
+
+ if (np->dn->translator)
+ free (np->dn->translator);
+
+ /* It is safe to unlock diskfs_node_refcnt_lock here for a while because
+ all references to the node have been deleted. */
+ if (np->dn->dirnode)
+ {
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ diskfs_nrele (np->dn->dirnode);
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ }
+
+ assert (!np->dn->pager);
+
+ free (np->dn);
+ free (np);
+}
+
+/* The last hard reference to a node has gone away; arrange to have
+ all the weak references dropped that can be. */
+void
+diskfs_try_dropping_softrefs (struct node *np)
+{
+ drop_pager_softrefs (np);
+}
+
+/* The last hard reference to a node has gone away. */
+void
+diskfs_lost_hardrefs (struct node *np)
+{
+}
+
+/* A new hard reference to a node has been created; it's now OK to
+ have unused weak references. */
+void
+diskfs_new_hardrefs (struct node *np)
+{
+ allow_pager_softrefs (np);
+}
+
+/* Read stat information out of the directory entry. */
+static error_t
+read_node (struct node *np, vm_address_t buf)
+{
+ /* XXX This needs careful investigation. */
+ error_t err;
+ struct stat *st = &np->dn_stat;
+ struct disknode *dn = np->dn;
+ struct dirrect *dr;
+ struct node *dp = 0;
+ struct vi_key vk = vi_key(np->dn->inode);
+ vm_prot_t prot = VM_PROT_READ;
+ memory_object_t memobj;
+ vm_size_t buflen = 0;
+ int our_buf = 0;
+
+ st->st_fstype = FSTYPE_MSLOSS;
+ st->st_fsid = getpid ();
+ st->st_ino = np->cache_id;
+ st->st_gen = 0;
+ st->st_rdev = 0;
+
+ st->st_nlink = 1;
+ st->st_uid = fs_uid;
+ st->st_gid = fs_gid;
+
+ st->st_rdev = 0;
+
+ np->dn->translator = 0;
+ np->dn->translen = 0;
+
+ st->st_flags = 0;
+
+ /* FIXME: If we are called through diskfs_alloc_node for a newly
+ allocated node that has no directory entry yet, only set a
+ minimal amount of data until the dirent is created (and we get
+ called a second time?). */
+ if (vk.dir_inode == 0 && vk.dir_offset == (void *) 2)
+ return 0;
+
+ if (vk.dir_inode == 0)
+ dr = &dr_root_node;
+ else
+ {
+ if (buf == 0)
+ {
+ /* FIXME: We know intimately that the parent dir is locked
+ by libdiskfs. The only case it is not locked is for NFS
+ (fsys_getfile) and we disabled that. */
+ dp = ifind (vk.dir_inode);
+ assert (dp);
+
+ /* Map in the directory contents. */
+ memobj = diskfs_get_filemap (dp, prot);
+
+ if (memobj == MACH_PORT_NULL)
+ return errno;
+
+ buflen = round_page (dp->dn_stat.st_size);
+ err = vm_map (mach_task_self (),
+ &buf, buflen, 0, 1, memobj, 0, 0, prot, prot, 0);
+ mach_port_deallocate (mach_task_self (), memobj);
+ our_buf = 1;
+ }
+
+ dr = (struct dirrect *) (buf + vk.dir_offset);
+ }
+
+ /* Files in fatfs depend on the directory that hold the file. */
+ np->dn->dirnode = dp;
+ if (dp)
+ dp->references++;
+
+ pthread_rwlock_rdlock (&np->dn->dirent_lock);
+
+ dn->start_cluster = (read_word (dr->first_cluster_high) << 16)
+ + read_word (dr->first_cluster_low);
+
+ if (dr->attribute & FAT_DIR_ATTR_DIR)
+ {
+ st->st_mode = S_IFDIR | 0777;
+ /* When we read in the node the first time, diskfs_root_node is
+ zero. */
+ if ((diskfs_root_node == 0 || np == diskfs_root_node)
+ && (fat_type == FAT12 || fat_type == FAT16))
+ {
+ st->st_size = read_dword (dr->file_size);
+ np->allocsize = nr_of_root_dir_sectors << log2_bytes_per_sector;
+ }
+ else
+ {
+ np->allocsize = 0;
+ pthread_rwlock_rdlock (&dn->alloc_lock);
+ err = fat_extend_chain (np, FAT_EOC, 0);
+ pthread_rwlock_unlock (&dn->alloc_lock);
+ if (err)
+ {
+ if (our_buf && buf)
+ munmap ((caddr_t) buf, buflen);
+ return err;
+ }
+ st->st_size = np->allocsize;
+ }
+ }
+ else
+ {
+ unsigned offset;
+ st->st_mode = S_IFREG | 0666;
+ st->st_size = read_dword (dr->file_size);
+ np->allocsize = np->dn_stat.st_size;
+
+ /* Round up to a cluster multiple. */
+ offset = np->allocsize & (bytes_per_cluster - 1);
+ if (offset > 0)
+ np->allocsize += bytes_per_cluster - offset;
+ }
+ if (dr->attribute & FAT_DIR_ATTR_RDONLY)
+ st->st_mode &= ~0222;
+
+ {
+ struct timespec ts;
+ fat_to_epoch (dr->write_date, dr->write_time, &ts);
+ st->st_ctim = st->st_mtim = st->st_atim = ts;
+ }
+
+ st->st_blksize = bytes_per_sector;
+ st->st_blocks = (st->st_size - 1) / bytes_per_sector + 1;
+
+ pthread_rwlock_unlock (&np->dn->dirent_lock);
+
+ if (our_buf && buf)
+ munmap ((caddr_t) buf, buflen);
+ return 0;
+}
+
+/* Return 0 if NP's owner can be changed to UID; otherwise return an
+ error code. */
+error_t
+diskfs_validate_owner_change (struct node *np, uid_t uid)
+{
+ /* Allow configurable uid. */
+ if (uid != 0)
+ return EINVAL;
+ return 0;
+}
+
+/* Return 0 if NP's group can be changed to GID; otherwise return an
+ error code. */
+error_t
+diskfs_validate_group_change (struct node *np, gid_t gid)
+{
+ /* Allow configurable gid. */
+ if (gid != 0)
+ return EINVAL;
+ return 0;
+}
+
+/* Return 0 if NP's mode can be changed to MODE; otherwise return an
+ error code. It must always be possible to clear the mode; diskfs
+ will not ask for permission before doing so. */
+error_t
+diskfs_validate_mode_change (struct node *np, mode_t mode)
+{
+ /* XXX */
+ return 0;
+}
+
+/* Return 0 if NP's author can be changed to AUTHOR; otherwise return
+ an error code. */
+error_t
+diskfs_validate_author_change (struct node *np, uid_t author)
+{
+ return (author == np->dn_stat.st_uid) ? 0 : EINVAL;
+}
+
+/* The user may define this function. Return 0 if NP's flags can be
+ changed to FLAGS; otherwise return an error code. It must always
+ be possible to clear the flags. */
+error_t
+diskfs_validate_flags_change (struct node *np, int flags)
+{
+ if (flags & ~(UF_NODUMP | UF_IMMUTABLE | UF_APPEND))
+ return EINVAL;
+ else
+ return 0;
+}
+
+/* Writes everything from NP's inode to the disk image. */
+void
+write_node (struct node *np)
+{
+ error_t err;
+ struct stat *st = &np->dn_stat;
+ struct dirrect *dr;
+ struct node *dp;
+ struct vi_key vk = vi_key(np->dn->inode);
+ vm_prot_t prot = VM_PROT_READ | VM_PROT_WRITE;
+ memory_object_t memobj;
+ vm_address_t buf = 0;
+ vm_size_t buflen;
+
+ /* XXX: If we are called from node-create before direnter was
+ called, DR is zero and we can't update the node. Just return
+ here, and leave it to direnter to call us again when we are
+ ready.
+ If we are called for the root directory node, we can't do anything,
+ as FAT root dirs don't have a directory entry for themselve.
+ */
+ if (vk.dir_inode == 0 || np == diskfs_root_node)
+ return;
+
+ assert (!np->dn_set_ctime && !np->dn_set_atime && !np->dn_set_mtime);
+ if (np->dn_stat_dirty)
+ {
+ assert (!diskfs_readonly);
+
+ dp = np->dn->dirnode;
+ assert (dp);
+
+ pthread_mutex_lock (&dp->lock);
+
+ /* Map in the directory contents. */
+ memobj = diskfs_get_filemap (dp, prot);
+
+ if (memobj == MACH_PORT_NULL)
+ {
+ pthread_mutex_unlock (&dp->lock);
+ /* FIXME: We shouldn't ignore this error. */
+ return;
+ }
+
+ buflen = round_page (dp->dn_stat.st_size);
+ err = vm_map (mach_task_self (),
+ &buf, buflen, 0, 1, memobj, 0, 0, prot, prot, 0);
+ mach_port_deallocate (mach_task_self (), memobj);
+
+ dr = (struct dirrect *) (buf + vk.dir_offset);
+
+ pthread_rwlock_wrlock (&np->dn->dirent_lock);
+ write_word (dr->first_cluster_low, np->dn->start_cluster & 0xffff);
+ write_word (dr->first_cluster_high, np->dn->start_cluster >> 16);
+
+ write_dword (dr->file_size, st->st_size);
+
+ /* Write time. */
+ fat_from_epoch ((unsigned char *) &dr->write_date,
+ (unsigned char *) &dr->write_time, &st->st_mtime);
+
+ pthread_rwlock_unlock (&np->dn->dirent_lock);
+ np->dn_stat_dirty = 0;
+
+ munmap ((caddr_t) buf, buflen);
+ pthread_mutex_unlock (&dp->lock);
+ }
+}
+
+/* Reload all data specific to NODE from disk, without writing anything.
+ Always called with DISKFS_READONLY true. */
+error_t
+diskfs_node_reload (struct node *node)
+{
+ struct cluster_chain *last = node->dn->first;
+
+ while (last)
+ {
+ struct cluster_chain *next = last->next;
+ free(last);
+ last = next;
+ }
+ flush_node_pager (node);
+ read_node (node, 0);
+
+ return 0;
+}
+
+/* For each active node, call FUN. The node is to be locked around the call
+ to FUN. If FUN returns non-zero for any node, then immediately stop, and
+ return that value. */
+error_t
+diskfs_node_iterate (error_t (*fun)(struct node *))
+{
+ error_t err = 0;
+ int n;
+ size_t num_nodes;
+ struct node *node, **node_list, **p;
+
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+
+ /* We must copy everything from the hash table into another data structure
+ to avoid running into any problems with the hash-table being modified
+ during processing (normally we delegate access to hash-table with
+ diskfs_node_refcnt_lock, but we can't hold this while locking the
+ individual node locks). */
+
+ num_nodes = nodehash_nr_items;
+
+ node_list = alloca (num_nodes * sizeof (struct node *));
+ p = node_list;
+ for (n = 0; n < INOHSZ; n++)
+ for (node = nodehash[n]; node; node = node->dn->hnext)
+ {
+ *p++ = node;
+ node->references++;
+ }
+
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+
+ p = node_list;
+ while (num_nodes-- > 0)
+ {
+ node = *p++;
+ if (!err)
+ {
+ pthread_mutex_lock (&node->lock);
+ err = (*fun)(node);
+ pthread_mutex_unlock (&node->lock);
+ }
+ diskfs_nrele (node);
+ }
+
+ return err;
+}
+
+/* Write all active disknodes into the ext2_inode pager. */
+void
+write_all_disknodes ()
+{
+ error_t write_one_disknode (struct node *node)
+ {
+ diskfs_set_node_times (node);
+
+ /* Update the inode image. */
+ write_node (node);
+
+ return 0;
+ }
+
+ diskfs_node_iterate (write_one_disknode);
+}
+
+
+void
+refresh_node_stats ()
+{
+ error_t refresh_one_node_stat (struct node *node)
+ {
+ node->dn_stat.st_uid = fs_uid;
+ node->dn_stat.st_gid = fs_gid;
+ return 0;
+ }
+
+ diskfs_node_iterate (refresh_one_node_stat);
+}
+
+
+/* Sync the info in NP->dn_stat and any associated format-specific
+ information to disk. If WAIT is true, then return only after the
+ physicial media has been completely updated. */
+void
+diskfs_write_disknode (struct node *np, int wait)
+{
+ write_node (np);
+}
+
+/* Set *ST with appropriate values to reflect the current state of the
+ filesystem. */
+error_t
+diskfs_set_statfs (struct statfs *st)
+{
+ st->f_type = FSTYPE_MSLOSS;
+ st->f_bsize = bytes_per_sector;
+ st->f_blocks = total_sectors;
+ st->f_bfree = fat_get_freespace () * sectors_per_cluster;
+ st->f_bavail = st->f_bfree;
+ /* There is no easy way to determine the number of (free) files on a
+ FAT filesystem. */
+ st->f_files = 0;
+ st->f_ffree = 0;
+ st->f_fsid = getpid ();
+ st->f_namelen = 0;
+ st->f_favail = st->f_ffree;
+ st->f_frsize = bytes_per_cluster;
+ return 0;
+}
+
+error_t
+diskfs_set_translator (struct node *node,
+ const char *name, u_int namelen,
+ struct protid *cred)
+{
+ assert (!diskfs_readonly);
+ return EOPNOTSUPP;
+}
+
+error_t
+diskfs_get_translator (struct node *node, char **namep, u_int *namelen)
+{
+ assert(0);
+}
+
+void
+diskfs_shutdown_soft_ports ()
+{
+ /* Should initiate termination of internally held pager ports
+ (the only things that should be soft) XXX */
+}
+
+/* The user must define this function. Truncate locked node NODE to be SIZE
+ bytes long. (If NODE is already less than or equal to SIZE bytes
+ long, do nothing.) If this is a symlink (and diskfs_shortcut_symlink
+ is set) then this should clear the symlink, even if
+ diskfs_create_symlink_hook stores the link target elsewhere. */
+error_t
+diskfs_truncate (struct node *node, loff_t length)
+{
+ error_t err;
+ loff_t offset;
+
+ diskfs_check_readonly ();
+ assert (!diskfs_readonly);
+
+ if (length >= node->dn_stat.st_size)
+ return 0;
+
+ /* If the file is not being truncated to a cluster boundary, the
+ contents of the partial cluster following the end of the file
+ must be zeroed in case it ever becomes accessible again because
+ of subsequent file growth. */
+ offset = length & (bytes_per_cluster - 1);
+ if (offset > 0)
+ {
+ diskfs_node_rdwr (node, (void *)zerocluster, length, bytes_per_cluster - offset,
+ 1, 0, 0);
+ diskfs_file_update (node, 1);
+ }
+
+ pthread_rwlock_wrlock (&node->dn->alloc_lock);
+
+ /* Update the size on disk; if we crash, we'll loose. */
+ node->dn_stat.st_size = length;
+ node->dn_set_mtime = 1;
+ node->dn_set_ctime = 1;
+ diskfs_node_update (node, 1);
+
+ err = diskfs_catch_exception ();
+ if (!err)
+ {
+ fat_truncate_node(node, round_cluster(length) >> log2_bytes_per_cluster);
+ node->allocsize = round_cluster(length);
+ }
+ diskfs_end_catch_exception ();
+
+ node->dn_set_mtime = 1;
+ node->dn_set_ctime = 1;
+ node->dn_stat_dirty = 1;
+
+ pthread_rwlock_unlock (&node->dn->alloc_lock);
+
+ return err;
+}
+
+error_t
+diskfs_S_file_get_storage_info (struct protid *cred,
+ mach_port_t **ports,
+ mach_msg_type_name_t *ports_type,
+ mach_msg_type_number_t *num_ports,
+ int **ints, mach_msg_type_number_t *num_ints,
+ loff_t **offsets,
+ mach_msg_type_number_t *num_offsets,
+ char **data, mach_msg_type_number_t *data_len)
+{
+ /* XXX */
+ return EOPNOTSUPP;
+}
+
+/* Free node NP; the on disk copy has already been synced with
+ diskfs_node_update (where NP->dn_stat.st_mode was 0). It's
+ mode used to be OLD_MODE. */
+void
+diskfs_free_node (struct node *np, mode_t old_mode)
+{
+ assert (!diskfs_readonly);
+
+ vi_free(np->dn->inode);
+}
+
+/* The user must define this function. Allocate a new node to be of
+ mode MODE in locked directory DP (don't actually set the mode or
+ modify the dir, that will be done by the caller); the user
+ responsible for the request can be identified with CRED. Set *NP
+ to be the newly allocated node. */
+error_t
+diskfs_alloc_node (struct node *dir, mode_t mode, struct node **node)
+{
+ error_t err;
+ ino_t inum;
+ inode_t inode;
+ struct node *np;
+
+ assert (!diskfs_readonly);
+
+ /* FIXME: We use a magic key here that signals read_node that we are
+ not entered in any directory yet. */
+ err = vi_new((struct vi_key) {0,2}, &inum, &inode);
+ if (err)
+ return err;
+
+ err = diskfs_cached_lookup (inum, &np);
+ if (err)
+ return err;
+
+ /* FIXME: We know that readnode couldn't put this in. */
+ np->dn->dirnode = dir;
+ dir->references++;
+
+ *node = np;
+ return 0;
+}
diff --git a/fatfs/main.c b/fatfs/main.c
new file mode 100644
index 00000000..2bbcdfaa
--- /dev/null
+++ b/fatfs/main.c
@@ -0,0 +1,282 @@
+/* main.c - FAT filesystem.
+
+ Copyright (C) 1997, 1998, 1999, 2002, 2003, 2007
+ Free Software Foundation, Inc.
+
+ Written by Thomas Bushnell, n/BSG and Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+#include <error.h>
+#include <argp.h>
+#include <argz.h>
+#include <limits.h>
+
+#include <version.h>
+#include "fatfs.h"
+#include "libdiskfs/fsys_S.h"
+
+struct node *diskfs_root_node;
+
+struct store *store = 0;
+struct store_parsed *store_parsed = 0;
+char *diskfs_disk_name = 0;
+
+char *diskfs_server_name = "fatfs";
+char *diskfs_server_version = HURD_VERSION;
+char *diskfs_extra_version = "GNU Hurd";
+int diskfs_synchronous = 0;
+
+int diskfs_link_max = 1;
+int diskfs_name_max = FAT_NAME_MAX;
+int diskfs_maxsymlinks = 8; /* XXX */
+
+/* Handy source of zeroes. */
+vm_address_t zerocluster;
+
+struct dirrect dr_root_node;
+
+/* The UID and GID for all files in the filesystem. */
+uid_t default_fs_uid;
+gid_t default_fs_gid;
+uid_t fs_uid;
+gid_t fs_gid;
+
+/* fatfs specific options. */
+static const struct argp_option options[] =
+ {
+ { "uid", 'U', "uid", 0, "Default uid for files" },
+ { "gid", 'G', "gid", 0, "Default gid for files" },
+ { 0 }
+ };
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'U':
+ if (arg)
+ fs_uid = atoi (arg);
+ refresh_node_stats ();
+ break;
+ case 'G':
+ if (arg)
+ fs_gid = atoi (arg);
+ refresh_node_stats ();
+ break;
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = state->input;
+ break;
+ case ARGP_KEY_SUCCESS:
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+/* Add our startup arguments to the standard diskfs set. */
+static const struct argp_child startup_children[] =
+ { { &diskfs_store_startup_argp }, { 0 } };
+static struct argp startup_argp =
+ { options, parse_opt, 0, 0, startup_children };
+
+/* Similarly at runtime. */
+static const struct argp_child runtime_children[] =
+ { { &diskfs_std_runtime_argp }, { 0 } };
+static struct argp runtime_argp =
+ { options, parse_opt, 0, 0, runtime_children };
+
+struct argp *diskfs_runtime_argp = (struct argp *) &runtime_argp;
+
+
+/* Override the standard diskfs routine so we can add our own
+ output. */
+error_t
+diskfs_append_args (char **argz, unsigned *argz_len)
+{
+ error_t err;
+ char buf[100];
+
+ /* Get the standard things. */
+ err = diskfs_append_std_options (argz, argz_len);
+
+ if (!err && fs_uid != default_fs_uid)
+ {
+ snprintf (buf, sizeof buf, "--uid=%d", fs_uid);
+ err = argz_add (argz, argz_len, buf);
+ }
+
+ if (!err && fs_gid != default_fs_gid)
+ {
+ snprintf (buf, sizeof buf, "--gid=%d", fs_gid);
+ err = argz_add (argz, argz_len, buf);
+ }
+
+ if (! err)
+ err = store_parsed_append_args (store_parsed, argz, argz_len);
+
+ return err;
+}
+
+
+/* Fetch the root node. */
+static void
+fetch_root ()
+{
+ error_t err;
+ ino_t inum;
+ inode_t inode;
+
+ memset (&dr_root_node, 0, sizeof(struct dirrect));
+
+ /* Fill root directory entry. XXX Should partially be in fat.c */
+ dr_root_node.attribute = FAT_DIR_ATTR_DIR;
+ if (fat_type == FAT32)
+ {
+ /* FAT12/16: There is no such thing as a start cluster, because
+ the whole root dir is in a special region after the FAT. The
+ start cluster of the root node is undefined. */
+ dr_root_node.first_cluster_high[1]
+ = sblock->compat.fat32.root_cluster[3];
+ dr_root_node.first_cluster_high[0]
+ = sblock->compat.fat32.root_cluster[2];
+ dr_root_node.first_cluster_low[1] = sblock->compat.fat32.root_cluster[1];
+ dr_root_node.first_cluster_low[0] = sblock->compat.fat32.root_cluster[0];
+ }
+
+ /* Determine size of the directory (different for fat12/16 vs 32). */
+ switch (fat_type)
+ {
+ case FAT12:
+ case FAT16:
+ write_dword(dr_root_node.file_size, nr_of_root_dir_sectors
+ << log2_bytes_per_sector);
+ break;
+
+ case FAT32:
+ {
+ /* Extend the cluster chain of the root directory and calculate
+ file_size based on that. */
+ cluster_t rootdir;
+ int cs = 0;
+
+ rootdir = (cluster_t) *sblock->compat.fat32.root_cluster;
+ while (rootdir != FAT_EOC)
+ {
+ fat_get_next_cluster (rootdir, &rootdir);
+ cs++;
+ }
+ write_dword (dr_root_node.file_size, cs << log2_bytes_per_cluster);
+ }
+ break;
+
+ default:
+ assert(!"don't know how to set size of root dir");
+ };
+
+ /* The magic vi_key {0, 1} for the root directory is distinguished
+ from the vi_zero_key (in the dir_offset value) as well as all
+ normal virtual inode keys (in the dir_inode value). Enter the
+ disknode into the inode table. */
+ err = vi_new ((struct vi_key) {0, 1}, &inum, &inode);
+ assert_perror (err);
+
+ /* Allocate a node for the root directory disknode in
+ diskfs_root_node. */
+ if (!err)
+ err = diskfs_cached_lookup (inum, &diskfs_root_node);
+
+ assert_perror (err);
+
+ pthread_mutex_unlock (&diskfs_root_node->lock);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ mach_port_t bootstrap;
+
+ default_fs_uid = getuid ();
+ default_fs_gid = getgid ();
+ fs_uid = default_fs_uid;
+ fs_gid = default_fs_gid;
+
+ /* This filesystem is not capable of writing yet. */
+ diskfs_readonly = 1;
+ diskfs_hard_readonly = 1;
+
+ /* Initialize the diskfs library, parse arguments, and open the
+ store. This starts the first diskfs thread for us. */
+ store = diskfs_init_main (&startup_argp, argc, argv, &store_parsed,
+ &bootstrap);
+
+ fat_read_sblock ();
+
+ create_fat_pager ();
+
+ zerocluster = (vm_address_t) mmap (0, bytes_per_cluster, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+
+ fetch_root ();
+
+ diskfs_startup_diskfs (bootstrap, 0);
+
+ pthread_exit (NULL);
+
+ return 0;
+}
+
+
+/* Nothing to do for read-only medium. */
+error_t
+diskfs_reload_global_state ()
+{
+ return 0;
+}
+
+
+error_t
+diskfs_set_hypermetadata (int wait, int clean)
+{
+ return 0;
+}
+
+
+void
+diskfs_readonly_changed (int readonly)
+{
+ /* We should never get here because we set diskfs_hard_readonly above. */
+ abort ();
+}
+
+/* FIXME: libdiskfs doesn't lock the parent dir when looking up a node
+ for fsys_getfile, so we disable NFS. */
+error_t
+diskfs_S_fsys_getfile (struct diskfs_control *pt,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ uid_t *uids, mach_msg_type_number_t nuids,
+ gid_t *gids, mach_msg_type_number_t ngids,
+ char *handle, mach_msg_type_number_t handle_len,
+ mach_port_t *file, mach_msg_type_name_t *file_type)
+{
+ return EOPNOTSUPP;
+}
diff --git a/fatfs/node-create.c b/fatfs/node-create.c
new file mode 100644
index 00000000..d85508b3
--- /dev/null
+++ b/fatfs/node-create.c
@@ -0,0 +1,204 @@
+/* node-create.c - Making new files.
+ Copyright (C) 1992,93,94,96,98,2001, 2003 Free Software Foundation
+ Modified for fatfs by Marco Gerards.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* This file was copied from libdiskfs and changed for fatfs. The
+ libdiskfs version created the "." and ".." links. After creating
+ those the directory was created and the "." and ".." were linked
+ in. In normally it is sane to do this, but this is impossible for
+ fatfs because fatfs doesn't support hardlinks. For fatfs the "."
+ and".." directory entries are created after creating the
+ directory. */
+
+
+#include <hurd/diskfs.h>
+
+/* This enables SysV style group behaviour. New nodes inherit the GID
+ of the user creating them unless the SGID bit is set of the parent
+ directory. */
+int _diskfs_no_inherit_dir_group;
+
+/* Create a new node. Give it MODE; if that includes IFDIR, also
+ initialize `.' and `..' in the new directory. Return the node in NPP.
+ CRED identifies the user responsible for the call. If NAME is nonzero,
+ then link the new node into DIR with name NAME; DS is the result of a
+ prior diskfs_lookup for creation (and DIR has been held locked since).
+ DIR must always be provided as at least a hint for disk allocation
+ strategies. */
+error_t
+diskfs_create_node (struct node *dir,
+ const char *name,
+ mode_t mode,
+ struct node **newnode,
+ struct protid *cred,
+ struct dirstat *ds)
+{
+ struct node *np;
+ error_t err;
+ uid_t newuid;
+ gid_t newgid;
+
+ if (diskfs_check_readonly ())
+ {
+ *newnode = NULL;
+ return EROFS;
+ }
+
+ /* Make the node */
+ err = diskfs_alloc_node (dir, mode, newnode);
+ if (err)
+ {
+ if (name)
+ diskfs_drop_dirstat (dir, ds);
+ *newnode = NULL;
+ return err;
+ }
+
+ np = *newnode;
+
+ /* Initialize the on-disk fields. */
+ if (cred->user->uids->num)
+ newuid = cred->user->uids->ids[0];
+ else
+ {
+ newuid = dir->dn_stat.st_uid;
+ mode &= ~S_ISUID;
+ }
+ err = diskfs_validate_owner_change (np, newuid);
+ if (err)
+ goto change_err;
+ np->dn_stat.st_uid = newuid;
+ if (np->author_tracks_uid)
+ np->dn_stat.st_author = newuid;
+
+ if (!_diskfs_no_inherit_dir_group)
+ {
+ newgid = dir->dn_stat.st_gid;
+ if (!idvec_contains (cred->user->gids, newgid))
+ mode &= ~S_ISGID;
+ }
+ else
+ {
+ if (dir->dn_stat.st_mode & S_ISGID)
+ {
+ /* If the parent dir has the sgid bit set, inherit its gid.
+ If the new node is a directory, also inherit the sgid bit
+ set. */
+ newgid = dir->dn_stat.st_gid;
+ if (S_ISDIR (mode))
+ mode |= S_ISGID;
+ else
+ {
+ if (!idvec_contains (cred->user->gids, newgid))
+ mode &= ~S_ISGID;
+ }
+ }
+ else
+ {
+ if (cred->user->gids->num)
+ newgid = cred->user->gids->ids[0];
+ else
+ {
+ newgid = dir->dn_stat.st_gid;
+ mode &= ~S_ISGID;
+ }
+ }
+ }
+
+ err = diskfs_validate_group_change (np, newgid);
+ if (err)
+ goto change_err;
+ np->dn_stat.st_gid = newgid;
+
+ np->dn_stat.st_rdev = 0;
+ np->dn_stat.st_nlink = !!name;
+ err = diskfs_validate_mode_change (np, mode);
+ if (err)
+ goto change_err;
+ np->dn_stat.st_mode = mode;
+
+ np->dn_stat.st_blocks = 0;
+ np->dn_stat.st_size = 0;
+ np->dn_stat.st_flags = 0;
+ np->dn_set_atime = 1;
+ np->dn_set_mtime = 1;
+ np->dn_set_ctime = 1;
+
+ diskfs_node_update (np, 1);
+
+ if (err)
+ {
+ change_err:
+ np->dn_stat.st_mode = 0;
+ np->dn_stat.st_nlink = 0;
+ if (name)
+ diskfs_drop_dirstat (dir, ds);
+ *newnode = NULL;
+ return err;
+ }
+
+ if (name)
+ {
+ err = diskfs_direnter (dir, name, np, ds, cred);
+ if (err)
+ {
+ np->dn_stat.st_nlink = 0;
+ np->dn_set_ctime = 1;
+ diskfs_nput (np);
+ }
+
+ /* For fatfs the "." and ".." directory entries should be
+ created after the directory was created and not before the
+ directory was created. */
+ if (S_ISDIR (mode))
+ err = diskfs_init_dir (np, dir, cred);
+
+ if (err)
+ {
+ struct dirstat *ds = alloca (diskfs_dirstat_size);
+ struct node *foo;
+ /* Keep old error intact. */
+ error_t err;
+
+ np->dn_stat.st_nlink = 0;
+
+ err = diskfs_lookup (dir, name, REMOVE, &foo, ds, cred);
+ if (err)
+ {
+ /* The new node couldn't be removed, we have a big
+ problem now. */
+ *newnode = NULL;
+ return err;
+ }
+
+ err = diskfs_dirremove (dir, foo, name, ds);
+ if (err)
+ {
+ diskfs_nput (np);
+ *newnode = NULL;
+ return err;
+ }
+ }
+
+ diskfs_node_update (np, 1);
+ }
+ if (err)
+ *newnode = NULL;
+
+ return err;
+}
diff --git a/fatfs/pager.c b/fatfs/pager.c
new file mode 100644
index 00000000..f855ecfc
--- /dev/null
+++ b/fatfs/pager.c
@@ -0,0 +1,1065 @@
+/* pager.c - Pager for fatfs.
+ Copyright (C) 1997, 1999, 2002, 2003 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG and Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <error.h>
+#include <string.h>
+#include <hurd/store.h>
+#include "fatfs.h"
+
+/* A ports bucket to hold disk pager ports. */
+struct port_bucket *disk_pager_bucket;
+
+/* A ports bucket to hold file pager ports. */
+struct port_bucket *file_pager_bucket;
+
+/* Mapped image of the FAT. */
+void *fat_image;
+
+pthread_spinlock_t node_to_page_lock = PTHREAD_SPINLOCK_INITIALIZER;
+
+#ifdef DONT_CACHE_MEMORY_OBJECTS
+#define MAY_CACHE 0
+#else
+#define MAY_CACHE 1
+#endif
+
+#define STAT_INC(field) (void) 0
+
+#define MAX_FREE_PAGE_BUFS 32
+
+static pthread_spinlock_t free_page_bufs_lock = PTHREAD_SPINLOCK_INITIALIZER;
+static void *free_page_bufs = 0;
+static int num_free_page_bufs = 0;
+
+/* Returns a single page page-aligned buffer. */
+static void *
+get_page_buf ()
+{
+ void *buf;
+
+ pthread_spin_lock (&free_page_bufs_lock);
+
+ buf = free_page_bufs;
+ if (buf == 0)
+ {
+ pthread_spin_unlock (&free_page_bufs_lock);
+ buf = mmap (0, vm_page_size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (buf == (void *) -1)
+ buf = 0;
+ }
+ else
+ {
+ free_page_bufs = *(void **)buf;
+ num_free_page_bufs--;
+ pthread_spin_unlock (&free_page_bufs_lock);
+ }
+
+ return buf;
+}
+
+/* Frees a block returned by get_page_buf. */
+static void
+free_page_buf (void *buf)
+{
+ pthread_spin_lock (&free_page_bufs_lock);
+ if (num_free_page_bufs < MAX_FREE_PAGE_BUFS)
+ {
+ *(void **)buf = free_page_bufs;
+ free_page_bufs = buf;
+ num_free_page_bufs++;
+ pthread_spin_unlock (&free_page_bufs_lock);
+ }
+ else
+ {
+ pthread_spin_unlock (&free_page_bufs_lock);
+ munmap (buf, vm_page_size);
+ }
+}
+
+/* Find the location on disk of page OFFSET in NODE. Return the disk
+ cluster in CLUSTER. If *LOCK is 0, then it a reader
+ lock is acquired on NODE's ALLOC_LOCK before doing anything, and left
+ locked after return -- even if an error is returned. 0 on success or an
+ error code otherwise is returned. */
+static error_t
+find_cluster (struct node *node, vm_offset_t offset,
+ cluster_t *cluster, pthread_rwlock_t **lock)
+{
+ error_t err;
+
+ if (!*lock)
+ {
+ *lock = &node->dn->alloc_lock;
+ pthread_rwlock_rdlock (*lock);
+ }
+
+ if (round_cluster (offset) > node->allocsize)
+ return EIO;
+
+ err = fat_getcluster (node, offset >> log2_bytes_per_cluster, 0, cluster);
+
+ return err;
+}
+
+/* Read one page for the root dir pager at offset PAGE, into BUF. This
+ may need to select several filesystem sectors to satisfy one page.
+ Assumes that fat_type is FAT12 or FAT16, and that vm_page_size is a
+ power of two multiple of bytes_per_sector (which happens to be true).
+*/
+static error_t
+root_dir_pager_read_page (vm_offset_t page, void **buf, int *writelock)
+{
+ error_t err;
+ daddr_t addr;
+ int overrun = 0;
+ size_t read = 0;
+
+ *writelock = 0;
+
+ if (page >= diskfs_root_node->allocsize)
+ {
+ return EIO;
+ }
+
+ pthread_rwlock_rdlock (&diskfs_root_node->dn->alloc_lock);
+
+ addr = first_root_dir_byte + page;
+ if (page + vm_page_size > diskfs_root_node->allocsize)
+ overrun = page + vm_page_size - diskfs_root_node->allocsize;
+
+ err = store_read (store, addr >> store->log2_block_size,
+ vm_page_size, (void **) buf, &read);
+ if (!err && read != vm_page_size)
+ err = EIO;
+
+ pthread_rwlock_unlock (&diskfs_root_node->dn->alloc_lock);
+
+ if (overrun)
+ bzero ((void *) *buf + vm_page_size - overrun, overrun);
+
+ return err;
+}
+
+/* Read one page for the pager backing NODE at offset PAGE, into BUF. This
+ may need to select only a part of a filesystem block to satisfy one page.
+ Assumes that bytes_per_cluster is a power of two multiple of vm_page_size.
+*/
+static error_t
+file_pager_read_small_page (struct node *node, vm_offset_t page,
+ void **buf, int *writelock)
+{
+ error_t err;
+ pthread_rwlock_t *lock = NULL;
+ cluster_t cluster;
+ size_t read = 0;
+
+ *writelock = 0;
+
+ if (page >= node->allocsize)
+ {
+ return EIO;
+ }
+
+ err = find_cluster (node, page, &cluster, &lock);
+
+ if (!err)
+ {
+ err = store_read (store,
+ FAT_FIRST_CLUSTER_BLOCK(cluster)
+ + ((page % bytes_per_cluster)
+ >> store->log2_block_size),
+ vm_page_size, (void **) buf, &read);
+
+ if (read != vm_page_size)
+ err = EIO;
+ }
+
+ if (lock)
+ pthread_rwlock_unlock (lock);
+
+ return err;
+}
+
+/* Read one page for the pager backing NODE at offset PAGE, into BUF. This
+ may need to read several filesystem blocks to satisfy one page, and tries
+ to consolidate the i/o if possible.
+ Assumes that vm_page_size is a power of two multiple of bytes_per_cluster.
+*/
+static error_t
+file_pager_read_huge_page (struct node *node, vm_offset_t page,
+ void **buf, int *writelock)
+{
+ error_t err;
+ int offs = 0;
+ pthread_rwlock_t *lock = NULL;
+ int left = vm_page_size;
+ cluster_t pending_clusters = 0;
+ int num_pending_clusters = 0;
+
+ /* Read the NUM_PENDING_CLUSTERS cluster in PENDING_CLUSTERS, into the buffer
+ pointed to by BUF (allocating it if necessary) at offset OFFS. OFFS in
+ adjusted by the amount read, and NUM_PENDING_CLUSTERS is zeroed. Any read
+ error is returned. */
+ error_t do_pending_reads ()
+ {
+ if (num_pending_clusters > 0)
+ {
+ size_t dev_block = FAT_FIRST_CLUSTER_BLOCK(pending_clusters);
+ size_t amount = num_pending_clusters << log2_bytes_per_cluster;
+ /* The buffer we try to read into; on the first read, we pass in a
+ size of zero, so that the read is guaranteed to allocate a new
+ buffer, otherwise, we try to read directly into the tail of the
+ buffer we've already got. */
+ void *new_buf = *buf + offs;
+ size_t new_len = offs == 0 ? 0 : vm_page_size - offs;
+
+ STAT_INC (file_pagein_reads);
+
+ err = store_read (store, dev_block, amount, &new_buf, &new_len);
+ if (err)
+ return err;
+ else if (amount != new_len)
+ return EIO;
+
+ if (new_buf != *buf + offs)
+ {
+ /* The read went into a different buffer than the one we
+ passed. */
+ if (offs == 0)
+ /* First read, make the returned page be our buffer. */
+ *buf = new_buf;
+ else
+ /* We've already got some buffer, so copy into it. */
+ {
+ memcpy (*buf + offs, new_buf, new_len);
+ free_page_buf (new_buf); /* Return NEW_BUF to our pool. */
+ STAT_INC (file_pagein_freed_bufs);
+ }
+ }
+
+ offs += new_len;
+ num_pending_clusters = 0;
+ }
+
+ return 0;
+ }
+
+ STAT_INC (file_pageins);
+
+ *writelock = 0;
+
+ if (page >= node->allocsize)
+ {
+ err = EIO;
+ left = 0;
+ }
+ else if (page + left > node->allocsize)
+ left = node->allocsize - page;
+
+ while (left > 0)
+ {
+ cluster_t cluster;
+
+ err = find_cluster (node, page, &cluster, &lock);
+ if (err)
+ break;
+
+ if (cluster != pending_clusters + num_pending_clusters)
+ {
+ err = do_pending_reads ();
+ if (err)
+ break;
+ pending_clusters = cluster;
+ }
+
+ num_pending_clusters++;
+
+ page += bytes_per_cluster;
+ left -= bytes_per_cluster;
+ }
+
+ if (!err && num_pending_clusters > 0)
+ err = do_pending_reads();
+
+ if (lock)
+ pthread_rwlock_unlock (lock);
+
+ return err;
+}
+
+struct pending_clusters
+ {
+ /* The cluster number of the first of the clusters. */
+ cluster_t cluster;
+ /* How many clusters we have. */
+ loff_t num;
+ /* A (page-aligned) buffer pointing to the data we're dealing with. */
+ void *buf;
+ /* And an offset into BUF. */
+ int offs;
+};
+
+/* Write the any pending clusters in PC. */
+static error_t
+pending_clusters_write (struct pending_clusters *pc)
+{
+ if (pc->num > 0)
+ {
+ error_t err;
+ size_t dev_block = FAT_FIRST_CLUSTER_BLOCK(pc->cluster);
+
+ size_t length = pc->num << log2_bytes_per_cluster, amount;
+
+ if (pc->offs > 0)
+ /* Put what we're going to write into a page-aligned buffer. */
+ {
+ void *page_buf = get_page_buf ();
+ memcpy ((void *) page_buf, pc->buf + pc->offs, length);
+ err = store_write (store, dev_block, page_buf, length, &amount);
+ free_page_buf (page_buf);
+ }
+ else
+ err = store_write (store, dev_block, pc->buf, length, &amount);
+ if (err)
+ return err;
+ else if (amount != length)
+ return EIO;
+
+ pc->offs += length;
+ pc->num = 0;
+ }
+
+ return 0;
+}
+
+static void
+pending_clusters_init (struct pending_clusters *pc, void *buf)
+{
+ pc->buf = buf;
+ pc->cluster = 0;
+ pc->num = 0;
+ pc->offs = 0;
+}
+
+/* Add the disk cluster CLUSTER to the list of destination disk clusters pending in
+ PC. */
+static error_t
+pending_clusters_add (struct pending_clusters *pc, cluster_t cluster)
+{
+ if (cluster != pc->cluster + pc->num)
+ {
+ error_t err = pending_clusters_write (pc);
+ if (err)
+ return err;
+ pc->cluster = cluster;
+ }
+ pc->num++;
+ return 0;
+}
+
+/* Write one page for the pager backing NODE, at offset PAGE, into BUF. This
+ may need to write several filesystem blocks to satisfy one page, and tries
+ to consolidate the i/o if possible.
+ Assumes that vm_page_size is a power of two multiple of bytes_per_cluster.
+*/
+static error_t
+file_pager_write_huge_page (struct node *node, vm_offset_t offset, void *buf)
+{
+ error_t err = 0;
+ struct pending_clusters pc;
+ pthread_rwlock_t *lock = &node->dn->alloc_lock;
+ cluster_t cluster;
+ int left = vm_page_size;
+
+ pending_clusters_init (&pc, buf);
+
+ /* Holding NODE->dn->alloc_lock effectively locks NODE->allocsize,
+ at least for the cases we care about: pager_unlock_page,
+ diskfs_grow and diskfs_truncate. */
+ pthread_rwlock_rdlock (&node->dn->alloc_lock);
+
+ if (offset >= node->allocsize)
+ left = 0;
+ else if (offset + left > node->allocsize)
+ left = node->allocsize - offset;
+
+ STAT_INC (file_pageouts);
+
+ while (left > 0)
+ {
+ err = find_cluster (node, offset, &cluster, &lock);
+ if (err)
+ break;
+ pending_clusters_add (&pc, cluster);
+ offset += bytes_per_cluster;
+ left -= bytes_per_cluster;
+ }
+
+ if (!err)
+ pending_clusters_write (&pc);
+
+ pthread_rwlock_unlock (&node->dn->alloc_lock);
+
+ return err;
+}
+
+/* Write one page for the root dir pager, at offset OFFSET, into BUF. This
+ may need to write several filesystem blocks to satisfy one page, and tries
+ to consolidate the i/o if possible.
+ Assumes that fat_type is FAT12 or FAT16 and that vm_page_size is a
+ power of two multiple of bytes_per_sector.
+*/
+static error_t
+root_dir_pager_write_page (vm_offset_t offset, void *buf)
+{
+ error_t err;
+ daddr_t addr;
+ size_t length;
+ size_t write = 0;
+
+ if (offset >= diskfs_root_node->allocsize)
+ return 0;
+
+ /* Holding NODE->dn->alloc_lock effectively locks NODE->allocsize,
+ at least for the cases we care about: pager_unlock_page,
+ diskfs_grow and diskfs_truncate. */
+ pthread_rwlock_rdlock (&diskfs_root_node->dn->alloc_lock);
+
+ addr = first_root_dir_byte + offset;
+
+ if (offset + vm_page_size > diskfs_root_node->allocsize)
+ length = diskfs_root_node->allocsize - offset;
+ else
+ length = vm_page_size;
+
+ err = store_write (store, addr >> store->log2_block_size, (void **) buf,
+ length, &write);
+ if (!err && write != length)
+ err = EIO;
+
+ pthread_rwlock_unlock (&diskfs_root_node->dn->alloc_lock);
+
+ return err;
+}
+
+/* Write one page for the pager backing NODE, at offset OFFSET, into BUF. This
+ may need to write several filesystem blocks to satisfy one page, and tries
+ to consolidate the i/o if possible.
+ Assumes that bytes_per_cluster is a power of two multiple of vm_page_size.
+*/
+static error_t
+file_pager_write_small_page (struct node *node, vm_offset_t offset, void *buf)
+{
+ error_t err;
+ pthread_rwlock_t *lock = NULL;
+ cluster_t cluster;
+ size_t write = 0;
+
+ if (offset >= node->allocsize)
+ return 0;
+
+ /* Holding NODE->dn->alloc_lock effectively locks NODE->allocsize,
+ at least for the cases we care about: pager_unlock_page,
+ diskfs_grow and diskfs_truncate. */
+ pthread_rwlock_rdlock (&node->dn->alloc_lock);
+
+ err = find_cluster (node, offset, &cluster, &lock);
+
+ if (!err)
+ {
+ err = store_write (store, FAT_FIRST_CLUSTER_BLOCK(cluster)
+ + ((offset % bytes_per_cluster)
+ >> store->log2_block_size),
+ (void **) buf, vm_page_size, &write);
+ if (write != vm_page_size)
+ err = EIO;
+ }
+
+ if (lock)
+ pthread_rwlock_unlock (lock);
+
+ return err;
+}
+
+static error_t
+fat_pager_read_page (vm_offset_t page, void **buf, int *writelock)
+{
+ error_t err;
+ size_t length = vm_page_size, read = 0;
+ vm_size_t fat_end = bytes_per_sector * sectors_per_fat;
+
+ if (page + vm_page_size > fat_end)
+ length = fat_end - page;
+
+ page += first_fat_sector * bytes_per_sector;
+ err = store_read (store, page >> store->log2_block_size, length, buf, &read);
+ if (read != length)
+ return EIO;
+ if (!err && length != vm_page_size)
+ memset ((void *)(*buf + length), 0, vm_page_size - length);
+
+ *writelock = 0;
+
+ return err;
+}
+
+static error_t
+fat_pager_write_page (vm_offset_t page, void *buf)
+{
+ error_t err = 0;
+ size_t length = vm_page_size, amount;
+ vm_size_t fat_end = bytes_per_sector * sectors_per_fat;
+
+ if (page + vm_page_size > fat_end)
+ length = fat_end - page;
+
+ page += first_fat_sector * bytes_per_sector;
+ err = store_write (store, page >> store->log2_block_size,
+ buf, length, &amount);
+ if (!err && length != amount)
+ err = EIO;
+
+ return err;
+}
+
+/* Satisfy a pager read request for either the disk pager or file pager
+ PAGER, to the page at offset PAGE into BUF. WRITELOCK should be set if
+ the pager should make the page writeable. */
+error_t
+pager_read_page (struct user_pager_info *pager, vm_offset_t page,
+ vm_address_t *buf, int *writelock)
+{
+ if (pager->type == FAT)
+ return fat_pager_read_page (page, (void **)buf, writelock);
+ else
+ {
+ if (pager->node == diskfs_root_node
+ && (fat_type == FAT12 || fat_type == FAT16))
+ return root_dir_pager_read_page (page, (void **)buf, writelock);
+ else
+ {
+ if (bytes_per_cluster < vm_page_size)
+ return file_pager_read_huge_page (pager->node, page,
+ (void **)buf, writelock);
+ else
+ return file_pager_read_small_page (pager->node, page,
+ (void **)buf, writelock);
+ }
+ }
+}
+
+/* Satisfy a pager write request for either the disk pager or file pager
+ PAGER, from the page at offset PAGE from BUF. */
+error_t
+pager_write_page (struct user_pager_info *pager, vm_offset_t page,
+ vm_address_t buf)
+{
+ if (pager->type == FAT)
+ return fat_pager_write_page (page, (void *)buf);
+ else
+ {
+ if (pager->node == diskfs_root_node
+ && (fat_type == FAT12 || fat_type == FAT16))
+ return root_dir_pager_write_page (page, (void *)buf);
+ else
+ {
+ if (bytes_per_cluster < vm_page_size)
+ return file_pager_write_huge_page (pager->node, page,
+ (void *)buf);
+ else
+ return file_pager_write_small_page (pager->node, page,
+ (void *)buf);
+ }
+ }
+}
+
+/* Make page PAGE writable, at least up to ALLOCSIZE. */
+error_t
+pager_unlock_page (struct user_pager_info *pager,
+ vm_offset_t page)
+{
+ /* All pages are writeable. The disk pages anyway, and the file
+ pages because blocks are directly allocated in diskfs_grow. */
+ return 0;
+}
+
+void
+pager_notify_evict (struct user_pager_info *pager,
+ vm_offset_t page)
+{
+ assert (!"unrequested notification on eviction");
+}
+
+/* Grow the disk allocated to locked node NODE to be at least SIZE
+ bytes, and set NODE->allocsize to the actual allocated size. (If
+ the allocated size is already SIZE bytes, do nothing.) CRED
+ identifies the user responsible for the call. Note that this will
+ only be called for real files, so there is no need to be careful
+ about the root dir node on FAT12/16. */
+error_t
+diskfs_grow (struct node *node, loff_t size, struct protid *cred)
+{
+ diskfs_check_readonly ();
+ assert (!diskfs_readonly);
+
+ if (size > node->allocsize)
+ {
+ error_t err = 0;
+ loff_t old_size;
+ volatile loff_t new_size;
+ volatile cluster_t end_cluster;
+ cluster_t new_end_cluster;
+ struct disknode *dn = node->dn;
+
+ pthread_rwlock_wrlock (&dn->alloc_lock);
+
+ old_size = node->allocsize;
+ new_size = ((size + bytes_per_cluster - 1) >> log2_bytes_per_cluster)
+ << log2_bytes_per_cluster;
+
+ /* The first unallocated clusters after the old and new ends of
+ the file, respectively. */
+ end_cluster = old_size >> log2_bytes_per_cluster;
+ new_end_cluster = new_size >> log2_bytes_per_cluster;
+
+ if (new_end_cluster > end_cluster)
+ {
+ err = diskfs_catch_exception ();
+ while (!err && end_cluster < new_end_cluster)
+ {
+ cluster_t disk_cluster;
+ err = fat_getcluster (node, end_cluster++, 1, &disk_cluster);
+ }
+ diskfs_end_catch_exception ();
+
+ if (err)
+ /* Reflect how much we allocated successfully. */
+ new_size = (end_cluster - 1) >> log2_bytes_per_cluster;
+ }
+
+ STAT_INC (file_grows);
+
+ node->allocsize = new_size;
+
+ pthread_rwlock_unlock (&dn->alloc_lock);
+
+ return err;
+ }
+ else
+ return 0;
+}
+
+/* This syncs a single file (NODE) to disk. Wait for all I/O to
+ complete if WAIT is set. NODE->lock must be held. */
+void
+diskfs_file_update (struct node *node, int wait)
+{
+ struct pager *pager;
+
+ pthread_spin_lock (&node_to_page_lock);
+ pager = node->dn->pager;
+ if (pager)
+ ports_port_ref (pager);
+ pthread_spin_unlock (&node_to_page_lock);
+
+ if (pager)
+ {
+ pager_sync (pager, wait);
+ ports_port_deref (pager);
+ }
+
+ diskfs_node_update (node, wait);
+}
+
+/* Invalidate any pager data associated with NODE. */
+void
+flush_node_pager (struct node *node)
+{
+ struct pager *pager;
+ struct disknode *dn = node->dn;
+
+ pthread_spin_lock (&node_to_page_lock);
+ pager = dn->pager;
+ if (pager)
+ ports_port_ref (pager);
+ pthread_spin_unlock (&node_to_page_lock);
+
+ if (pager)
+ {
+ pager_flush (pager, 1);
+ ports_port_deref (pager);
+ }
+}
+
+/* Return in *OFFSET and *SIZE the minimum valid address the pager
+ will accept and the size of the object. */
+inline error_t
+pager_report_extent (struct user_pager_info *pager,
+ vm_address_t *offset, vm_size_t *size)
+{
+ assert (pager->type == FAT || pager->type == FILE_DATA);
+
+ *offset = 0;
+
+ if (pager->type == FAT)
+ *size = bytes_per_sector * sectors_per_fat;
+ else
+ *size = pager->node->allocsize;
+
+ return 0;
+}
+
+/* This is called when a pager is being deallocated after all extant
+ send rights have been destroyed. */
+void
+pager_clear_user_data (struct user_pager_info *upi)
+{
+ if (upi->type == FILE_DATA)
+ {
+ struct pager *pager;
+
+ pthread_spin_lock (&node_to_page_lock);
+ pager = upi->node->dn->pager;
+ if (pager && pager_get_upi (pager) == upi)
+ upi->node->dn->pager = 0;
+ pthread_spin_unlock (&node_to_page_lock);
+
+ diskfs_nrele_light (upi->node);
+ }
+
+ free (upi);
+}
+
+/* This will be called when the ports library wants to drop weak
+ references. The pager library creates no weak references itself.
+ If the user doesn't either, then it's OK for this function to do
+ nothing. */
+void
+pager_dropweak (struct user_pager_info *p __attribute__ ((unused)))
+{
+}
+
+/* A top-level function for the paging thread that just services paging
+ requests. */
+static void *
+service_paging_requests (void *arg)
+{
+ struct port_bucket *pager_bucket = arg;
+ ports_manage_port_operations_multithread (pager_bucket,
+ pager_demuxer,
+ 1000,
+ 0,
+ NULL);
+ /* Not reached. */
+ return NULL;
+}
+
+/* Create the disk pager. */
+void
+create_fat_pager (void)
+{
+ pthread_t thread;
+ pthread_attr_t attr;
+ error_t err;
+
+ /* The disk pager. */
+ struct user_pager_info *upi = malloc (sizeof (struct user_pager_info));
+ upi->type = FAT;
+ disk_pager_bucket = ports_create_bucket ();
+ diskfs_start_disk_pager (upi, disk_pager_bucket, MAY_CACHE, 0,
+ bytes_per_sector * sectors_per_fat,
+ &fat_image);
+
+ /* The file pager. */
+ file_pager_bucket = ports_create_bucket ();
+
+#define STACK_SIZE (64 * 1024)
+ pthread_attr_init (&attr);
+ pthread_attr_setstacksize (&attr, STACK_SIZE);
+#undef STACK_SIZE
+
+ /* Make a thread to service file paging requests. */
+ err = pthread_create (&thread, &attr,
+ service_paging_requests, file_pager_bucket);
+ if (err)
+ error (2, err, "pthread_create");
+ pthread_detach (thread);
+}
+
+/* Call this to create a FILE_DATA pager and return a send right.
+ NODE must be locked. */
+mach_port_t
+diskfs_get_filemap (struct node *node, vm_prot_t prot)
+{
+ mach_port_t right;
+
+ assert (S_ISDIR (node->dn_stat.st_mode)
+ || S_ISREG (node->dn_stat.st_mode)
+ || (S_ISLNK (node->dn_stat.st_mode)));
+
+ pthread_spin_lock (&node_to_page_lock);
+ do
+ {
+ struct pager *pager = node->dn->pager;
+ if (pager)
+ {
+ /* Because PAGER is not a real reference, this might be
+ nearly deallocated. If that's so, then the port right
+ will be null. In that case, clear here and loop. The
+ deallocation will complete separately. */
+ right = pager_get_port (pager);
+ if (right == MACH_PORT_NULL)
+ node->dn->pager = 0;
+ else
+ pager_get_upi (pager)->max_prot |= prot;
+ }
+ else
+ {
+ struct user_pager_info *upi =
+ malloc (sizeof (struct user_pager_info));
+ upi->type = FILE_DATA;
+ upi->node = node;
+ upi->max_prot = prot;
+ diskfs_nref_light (node);
+ node->dn->pager =
+ pager_create (upi, file_pager_bucket, MAY_CACHE,
+ MEMORY_OBJECT_COPY_DELAY, 0);
+ if (node->dn->pager == 0)
+ {
+ diskfs_nrele_light (node);
+ free (upi);
+ pthread_spin_unlock (&node_to_page_lock);
+ return MACH_PORT_NULL;
+ }
+
+ right = pager_get_port (node->dn->pager);
+ ports_port_deref (node->dn->pager);
+ }
+ }
+ while (right == MACH_PORT_NULL);
+ pthread_spin_unlock (&node_to_page_lock);
+
+ mach_port_insert_right (mach_task_self (), right, right,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ return right;
+}
+
+/* Call this when we should turn off caching so that unused memory
+ object ports get freed. */
+void
+drop_pager_softrefs (struct node *node)
+{
+ struct pager *pager;
+
+ pthread_spin_lock (&node_to_page_lock);
+ pager = node->dn->pager;
+ if (pager)
+ ports_port_ref (pager);
+ pthread_spin_unlock (&node_to_page_lock);
+
+ if (MAY_CACHE && pager)
+ pager_change_attributes (pager, 0, MEMORY_OBJECT_COPY_DELAY, 0);
+ if (pager)
+ ports_port_deref (pager);
+}
+
+/* Call this when we should turn on caching because it's no longer
+ important for unused memory object ports to get freed. */
+void
+allow_pager_softrefs (struct node *node)
+{
+ struct pager *pager;
+
+ pthread_spin_lock (&node_to_page_lock);
+ pager = node->dn->pager;
+ if (pager)
+ ports_port_ref (pager);
+ pthread_spin_unlock (&node_to_page_lock);
+
+ if (MAY_CACHE && pager)
+ pager_change_attributes (pager, 1, MEMORY_OBJECT_COPY_DELAY, 0);
+ if (pager)
+ ports_port_deref (pager);
+}
+
+/* Call this to find out the struct pager * corresponding to the
+ FILE_DATA pager of inode IP. This should be used *only* as a
+ subsequent argument to register_memory_fault_area, and will be
+ deleted when the kernel interface is fixed. NODE must be
+ locked. */
+struct pager *
+diskfs_get_filemap_pager_struct (struct node *node)
+{
+ /* This is safe because pager can't be cleared; there must be an
+ active mapping for this to be called. */
+ return node->dn->pager;
+}
+
+/* Shutdown all the pagers (except the disk pager). */
+void
+diskfs_shutdown_pager ()
+{
+ error_t shutdown_one (void *v_p)
+ {
+ struct pager *p = v_p;
+ pager_shutdown (p);
+ return 0;
+ }
+
+ write_all_disknodes ();
+
+ ports_bucket_iterate (file_pager_bucket, shutdown_one);
+
+ pager_sync (diskfs_disk_pager, 1);
+
+ /* Despite the name of this function, we never actually shutdown the
+ disk pager, just make sure it's synced. */
+}
+
+/* Sync all the pagers. */
+void
+diskfs_sync_everything (int wait)
+{
+ error_t sync_one (void *v_p)
+ {
+ struct pager *p = v_p;
+ pager_sync (p, wait);
+ return 0;
+ }
+
+ write_all_disknodes ();
+ ports_bucket_iterate (file_pager_bucket, sync_one);
+ pager_sync (diskfs_disk_pager, wait);
+}
+
+static void
+disable_caching ()
+{
+ error_t block_cache (void *arg)
+ {
+ struct pager *p = arg;
+
+ pager_change_attributes (p, 0, MEMORY_OBJECT_COPY_DELAY, 1);
+ return 0;
+ }
+
+ /* Loop through the pagers and turn off caching one by one,
+ synchronously. That should cause termination of each pager. */
+ ports_bucket_iterate (disk_pager_bucket, block_cache);
+ ports_bucket_iterate (file_pager_bucket, block_cache);
+}
+
+static void
+enable_caching ()
+{
+ error_t enable_cache (void *arg)
+ {
+ struct pager *p = arg;
+ struct user_pager_info *upi = pager_get_upi (p);
+
+ pager_change_attributes (p, 1, MEMORY_OBJECT_COPY_DELAY, 0);
+
+ /* It's possible that we didn't have caching on before, because
+ the user here is the only reference to the underlying node
+ (actually, that's quite likely inside this particular
+ routine), and if that node has no links. So dinkle the node
+ ref counting scheme here, which will cause caching to be
+ turned off, if that's really necessary. */
+ if (upi->type == FILE_DATA)
+ {
+ diskfs_nref (upi->node);
+ diskfs_nrele (upi->node);
+ }
+
+ return 0;
+ }
+
+ ports_bucket_iterate (disk_pager_bucket, enable_cache);
+ ports_bucket_iterate (file_pager_bucket, enable_cache);
+}
+
+/* Tell diskfs if there are pagers exported, and if none, then
+ prevent any new ones from showing up. */
+int
+diskfs_pager_users ()
+{
+ int npagers = ports_count_bucket (file_pager_bucket);
+
+ if (npagers == 0)
+ return 0;
+
+ if (MAY_CACHE)
+ {
+ disable_caching ();
+
+ /* Give it a second; the kernel doesn't actually shutdown
+ immediately. XXX */
+ sleep (1);
+
+ npagers = ports_count_bucket (file_pager_bucket);
+ if (npagers == 0)
+ return 0;
+
+ /* Darn, there are actual honest users. Turn caching back on,
+ and return failure. */
+ enable_caching ();
+ }
+
+ ports_enable_bucket (file_pager_bucket);
+
+ return 1;
+}
+
+/* Return the bitwise or of the maximum prot parameter (the second arg
+ to diskfs_get_filemap) for all active user pagers. */
+vm_prot_t
+diskfs_max_user_pager_prot ()
+{
+ vm_prot_t max_prot = 0;
+ int npagers = ports_count_bucket (file_pager_bucket);
+
+ if (npagers > 0)
+ {
+ error_t add_pager_max_prot (void *v_p)
+ {
+ struct pager *p = v_p;
+ struct user_pager_info *upi = pager_get_upi (p);
+ max_prot |= upi->max_prot;
+ /* Stop iterating if MAX_PROT is as filled as it is going to
+ get. */
+ return max_prot == (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE);
+ }
+
+ disable_caching (); /* Make any silly pagers go away. */
+
+ /* Give it a second; the kernel doesn't actually shutdown
+ immediately. XXX */
+ sleep (1);
+
+ ports_bucket_iterate (file_pager_bucket, add_pager_max_prot);
+
+ enable_caching ();
+ }
+
+ ports_enable_bucket (file_pager_bucket);
+
+ return max_prot;
+}
diff --git a/fatfs/virt-inode.c b/fatfs/virt-inode.c
new file mode 100644
index 00000000..71381699
--- /dev/null
+++ b/fatfs/virt-inode.c
@@ -0,0 +1,229 @@
+/* Virtual Inode management routines
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* TODO: Improve NEW by keeping a bitmap of free inodes.
+ TODO: Improve RLOOKUP by keeping an open hash for keys (need to change
+ CHANGE and FREE, too).
+ TODO: Improve FREE by keeping the highest inode in use and keep it
+ up-to-date. When a table page can be freed, do so. */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <pthread.h>
+#include "virt-inode.h"
+
+/* Each virtual inode contains the UNIQUE key it belongs to,
+ which must not be zero. */
+
+vi_key_t vi_zero_key = {0, 0};
+
+struct v_inode
+{
+ vi_key_t key;
+};
+
+/* All inodes are stored in a table by their index number - 1.
+ Decrementing by one is necessary because inode numbers start from 1,
+ but our table is zero based. */
+
+#define LOG2_TABLE_PAGE_SIZE 10
+#define TABLE_PAGE_SIZE (1 << LOG2_TABLE_PAGE_SIZE)
+
+struct table_page
+{
+ struct table_page *next;
+
+ struct v_inode vi[TABLE_PAGE_SIZE];
+};
+
+struct table_page *inode_table;
+
+pthread_spinlock_t inode_table_lock = PTHREAD_SPINLOCK_INITIALIZER;
+
+/* See vi_new and vi_rlookup. */
+error_t
+_vi_new(vi_key_t key, ino_t *inode, inode_t *v_inode)
+{
+ struct table_page *table = inode_table;
+ struct table_page *prev_table = 0;
+ int page = 0;
+ int offset = 0;
+
+ while (table && memcmp(&vi_zero_key, &table->vi[offset].key, sizeof(vi_key_t)))
+ {
+ offset++;
+ if (offset == TABLE_PAGE_SIZE)
+ {
+ offset = 0;
+ page++;
+ prev_table = table;
+ table = table->next;
+ }
+ }
+
+ if (table)
+ {
+ table->vi[offset].key = key;
+ /* See above for rationale of increment. */
+ *inode = (page << LOG2_TABLE_PAGE_SIZE) + offset + 1;
+ *v_inode = &table->vi[offset];
+ }
+ else
+ {
+ struct table_page **pagep;
+
+ if (prev_table)
+ pagep = &prev_table->next;
+ else
+ pagep = &inode_table;
+ *pagep = (struct table_page *) malloc (sizeof (struct table_page));
+ if (!*pagep)
+ {
+ return ENOSPC;
+ }
+ memset (*pagep, 0, sizeof (struct table_page));
+ (*pagep)->vi[0].key = key;
+ /* See above for rationale of increment. */
+ *inode = (page << LOG2_TABLE_PAGE_SIZE) + 1;
+ *v_inode = &(*pagep)->vi[0];
+ }
+
+ return 0;
+}
+
+/* Allocate a new inode number INODE for KEY and return it as well as
+ the virtual inode V_INODE. Return 0 on success, otherwise an error
+ value (ENOSPC). */
+error_t
+vi_new(vi_key_t key, ino_t *inode, inode_t *v_inode)
+{
+ error_t err;
+
+ assert (memcmp(&vi_zero_key, &key, sizeof (vi_key_t)));
+
+ pthread_spin_lock (&inode_table_lock);
+ err = _vi_new(key, inode, v_inode);
+ pthread_spin_unlock (&inode_table_lock);
+
+ return err;
+}
+
+/* Get the key for virtual inode V_INODE. */
+vi_key_t
+vi_key(inode_t v_inode)
+{
+ return v_inode->key;
+}
+
+/* Get the inode V_INODE belonging to inode number INODE.
+ Returns 0 if this inode number is free. */
+inode_t
+vi_lookup(ino_t inode)
+{
+ struct table_page *table = inode_table;
+ /* See above for rationale of decrement. */
+ int page = (inode - 1) >> LOG2_TABLE_PAGE_SIZE;
+ int offset = (inode - 1) & (TABLE_PAGE_SIZE - 1);
+ inode_t v_inode = 0;
+
+ pthread_spin_lock (&inode_table_lock);
+
+ while (table && page > 0)
+ {
+ page--;
+ table = table->next;
+ }
+
+ if (table)
+ v_inode = &table->vi[offset];
+
+ pthread_spin_unlock (&inode_table_lock);
+
+ return v_inode;
+}
+
+/* Get the inode number and virtual inode belonging to key KEY.
+ Returns 0 on success and EINVAL if no inode is found for KEY and
+ CREATE is false. Otherwise, if CREATE is true, allocate new inode. */
+error_t
+vi_rlookup(vi_key_t key, ino_t *inode, inode_t *v_inode, int create)
+{
+ error_t err = 0;
+ struct table_page *table = inode_table;
+ int page = 0;
+ int offset = 0;
+
+ assert (memcmp(&vi_zero_key, &key, sizeof (vi_key_t)));
+
+ pthread_spin_lock (&inode_table_lock);
+
+ while (table && memcmp(&table->vi[offset].key, &key, sizeof (vi_key_t)))
+ {
+ offset++;
+ if (offset == TABLE_PAGE_SIZE)
+ {
+ offset = 0;
+ page++;
+ table = table->next;
+ }
+ }
+
+ if (table)
+ {
+ /* See above for rationale of increment. */
+ *inode = (page << LOG2_TABLE_PAGE_SIZE) + offset + 1;
+ *v_inode = &table->vi[offset];
+ }
+ else
+ {
+ if (create)
+ err = _vi_new (key, inode, v_inode);
+ else
+ err = EINVAL;
+ }
+
+ pthread_spin_unlock (&inode_table_lock);
+
+ return err;
+}
+
+/* Change the key of virtual inode V_INODE to KEY and return the old
+ key. */
+vi_key_t vi_change(inode_t v_inode, vi_key_t key)
+{
+ vi_key_t okey = v_inode->key;
+
+ assert (memcmp(&vi_zero_key, &key, sizeof (vi_key_t)));
+ v_inode->key = key;
+ return okey;
+}
+
+/* Release virtual inode V_INODE, freeing the inode number. Return
+ the key. */
+vi_key_t vi_free(inode_t v_inode)
+{
+ vi_key_t key;
+ pthread_spin_lock (&inode_table_lock);
+ key = v_inode->key;
+ v_inode->key = vi_zero_key;
+ pthread_spin_unlock (&inode_table_lock);
+ return key;
+}
diff --git a/fatfs/virt-inode.h b/fatfs/virt-inode.h
new file mode 100644
index 00000000..5b889d23
--- /dev/null
+++ b/fatfs/virt-inode.h
@@ -0,0 +1,69 @@
+/* virt-inode.h - Public interface for the virtual inode management routines.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef VIRT_INODE_H
+#define VIRT_INODE_H
+
+#include <errno.h>
+#include <dirent.h>
+
+/* Define struct vi_key to match your needs. It is passed by copy,
+ so don't make it too huge. Equality is tested with memcpy, because
+ C == operator doesn't work on structs. */
+
+struct vi_key
+{
+ ino_t dir_inode;
+ int dir_offset;
+};
+
+typedef struct vi_key vi_key_t;
+
+extern vi_key_t vi_zero_key;
+
+typedef struct v_inode *inode_t;
+
+/* Allocate a new inode number INODE for KEY and return it as well as
+ the virtual inode V_INODE. Return 0 on success, otherwise an error
+ value (ENOSPC). */
+error_t vi_new(vi_key_t key, ino_t *inode, inode_t *v_inode);
+
+/* Get the key for virtual inode V_INODE. */
+vi_key_t vi_key(inode_t v_inode);
+
+/* Get the inode V_INODE belonging to inode number INODE.
+ Returns 0 if this inode number is free. */
+inode_t vi_lookup(ino_t inode);
+
+/* Get the inode number and virtual inode belonging to key KEY.
+ Returns 0 on success and EINVAL if no inode is found for KEY and
+ CREATE is false. Otherwise, if CREATE is true, allocate a new
+ inode. */
+error_t vi_rlookup(vi_key_t key, ino_t *inode, inode_t *v_inode, int create);
+
+/* Change the key of virtual inode V_INODE to KEY and return the old
+ key. */
+vi_key_t vi_change(inode_t v_inode, vi_key_t key);
+
+/* Release virtual inode V_INODE, freeing the inode number. Return
+ the key. */
+vi_key_t vi_free(inode_t v_inode);
+
+#endif
diff --git a/fstests/Makefile b/fstests/Makefile
new file mode 100644
index 00000000..63742424
--- /dev/null
+++ b/fstests/Makefile
@@ -0,0 +1,29 @@
+# Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with the GNU Hurd; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := fstests
+makemode := utilities
+
+SRCS = fstests.c fdtests.c timertest.c opendisk.c
+targets = timertest fstests # opendisk fdtests
+
+include ../Makeconf
+
+timertest: timertest.o
+fstests: fstests.o
+opendisk: opendisk.o
+fdtests: fdtests.o
diff --git a/fstests/fdtests.c b/fstests/fdtests.c
new file mode 100644
index 00000000..b952c7a5
--- /dev/null
+++ b/fstests/fdtests.c
@@ -0,0 +1,96 @@
+/* Test filesystem behavior
+ Copyright (C) 1993, 1994 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <mach.h>
+#include <stdio.h>
+#include <hurd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <hurd/fd.h>
+
+int
+main ()
+{
+ int fd;
+ FILE *fp;
+ static const char string[] = "Did this get into the file?\n";
+ int written;
+
+ setlinebuf (stdout);
+ setlinebuf (stderr);
+
+ if (unlink ("CREATED") < 0 && errno != ENOENT)
+ printf ("Error on unlink: %d\n", errno);
+
+ fd = open ("CREATED", O_WRITE | O_CREAT, 0666);
+ if (fd < 0)
+ printf ("Error on open: %d\n", errno);
+
+ /* written = write (fd, string, strlen (string)); */
+
+ {
+ size_t nbytes = strlen (string);
+ struct hurd_userlink __dt_ulink;
+ error_t __result;
+ struct hurd_fd_user __d = _hurd_fd_get (fd, &__dt_ulink);
+ if (__d.d == NULL)
+ __result = EBADF;
+ else
+ {
+ struct hurd_fd *const descriptor = __d.d;
+ __result = _hurd_fd_write (descriptor, string, &nbytes);
+ _hurd_fd_free (__d, &__dt_ulink);
+ }
+ if (__result)
+ errno = __result, written = -1;
+ else
+ written = nbytes;
+ }
+
+ if (written < 0)
+ printf ("Error on write: %d\n", errno);
+ else if (written != strlen (string))
+ printf ("Short write: %d\n", written);
+ else if (sync ())
+ printf ("Error on sync: %d\n", errno);
+
+
+ fp = fopen ("CREATED", "r");
+ if (! fp)
+ perror ("fopen");
+ else
+ {
+ char *line = NULL;
+ size_t len = 0;
+ ssize_t n = getline (&line, &len, fp);
+ if (n < 0)
+ perror ("getline");
+ else
+ printf ("Read %d bytes: %.*s", n, n, line);
+ free (line);
+ }
+
+ printf ("All done.\n");
+ malloc (0);
+
+ return 0;
+}
diff --git a/fstests/fstests.c b/fstests/fstests.c
new file mode 100644
index 00000000..4a0b35d5
--- /dev/null
+++ b/fstests/fstests.c
@@ -0,0 +1,95 @@
+/* Test filesystem behavior
+ Copyright (C) 1993,94,2000,01,02 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include <mach.h>
+#include <stdio.h>
+#include <hurd/hurd_types.h>
+#include <hurd/fs.h>
+#include <hurd/io.h>
+#include <hurd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <error.h>
+#include <unistd.h>
+
+int check_refs (mach_port_t port) /* To call from gdb */
+{
+ int err;
+ mach_port_urefs_t refs;
+ err = mach_port_get_refs (mach_task_self (),
+ port, MACH_PORT_RIGHT_SEND, &refs);
+ return err ? -err : refs;
+}
+
+int
+main ()
+{
+ mach_port_t root;
+#if HURDISH_TESTS
+ extern file_t *_hurd_init_dtable;
+ char string[] = "Did this get into the file?\n";
+ file_t filetowrite;
+ retry_type retry;
+ char pathbuf[1024];
+ int written;
+ error_t err;
+#endif
+
+ root = getcrdir ();
+
+ printf ("fstests running...\n");
+
+#if HURDISH_TESTS
+ if ((err = dir_unlink (root, "CREATED")) && err != ENOENT)
+ error (0, err, "Error on unlink");
+ else if (err = dir_lookup (root, "CREATED", O_WRITE | O_CREAT, 0666,
+ &retry, pathbuf, &filetowrite))
+ error (0, err, "Error on lookup");
+ else if (err = io_write (filetowrite, string, strlen (string), -1, &written))
+ error (0, err, "Error on write");
+ else if (written != strlen (string))
+ error (0, 0, "Short write: %d\n", written);
+ else if (err = file_syncfs (filetowrite, 1, 0))
+ error (0, err, "Error on sync");
+#else
+
+ if (unlink ("/newdir"))
+ error (0, errno, "unlink");
+ if (rmdir ("/newdir"))
+ error (0, errno, "1st rmdir");
+ if (mkdir ("/newdir", 0777))
+ error (0, errno, "1st mkdir");
+ if (rename ("/newdir", "/newdir2"))
+ error (0, errno, "1st rename");
+ if (rmdir ("/foo"))
+ error (0, errno, "2nd rmdir");
+ if (mkdir ("/foo", 0777))
+ error (0, errno, "2nd mkdir");
+ if (rename ("/newdir2", "/foo"))
+ error (0, errno, "2nd rename");
+ sync ();
+#endif
+
+ printf ("All done.\n");
+ malloc (0);
+
+ return 0;
+}
diff --git a/fstests/opendisk.c b/fstests/opendisk.c
new file mode 100644
index 00000000..0e87b141
--- /dev/null
+++ b/fstests/opendisk.c
@@ -0,0 +1,122 @@
+/* Attempt to open a disk device
+ Copyright (C) 1994 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <device/device.h>
+#include <errno.h>
+#include <hurd.h>
+#include <stdio.h>
+
+/* Boneheaded CMU people decided to gratuitously screw us. */
+#include "/gd/gnu/mach/sys/ioctl.h"
+
+#define DKTYPENAMES
+#include <device/disk_status.h>
+
+int
+main (int argc, char **argv)
+{
+ mach_port_t hostpriv, devicemaster;
+ mach_port_t device;
+ int sizes[DEV_GET_SIZE_COUNT];
+ int sizescnt = DEV_GET_SIZE_COUNT;
+ struct disklabel label;
+ int labelcnt = sizeof label / sizeof (int);
+ int i;
+
+ errno = get_privileged_ports (&hostpriv, &devicemaster);
+
+ if (errno)
+ {
+ perror ("Cannot get privileged ports");
+ exit (1);
+ }
+
+ errno = device_open (devicemaster, D_READ, argv[1], &device);
+
+ if (errno)
+ {
+ perror (argv[1]);
+ exit (1);
+ }
+
+ errno = device_get_status (device, DEV_GET_SIZE, sizes, &sizescnt);
+
+ if (errno)
+ {
+ perror ("device_get_status");
+ exit (1);
+ }
+
+ printf ("Record size: %d\nDevice size: %d\n",
+ sizes[DEV_GET_SIZE_RECORD_SIZE], sizes[DEV_GET_SIZE_DEVICE_SIZE]);
+
+
+ errno = device_get_status (device, DIOCGDINFO, &label, &labelcnt);
+
+ if (errno)
+ {
+ perror ("reading disk label");
+ exit (1);
+ }
+
+ printf ("Magic: %#x", label.d_magic);
+ if (label.d_magic != DISKMAGIC)
+ printf ("Should be %#x\n", DISKMAGIC);
+ else
+ printf ("\n");
+
+ printf ("Type %s\tSubtype %d\nTypename %s\n",
+ dktypenames[label.d_type], label.d_subtype, label.d_typename);
+
+ printf ("Pack name %s\n", label.d_packname);
+
+ printf ("Secsize %d\tnsect %d\tntrack %d\tncyl %d\tspc %d\tspu %d\n",
+ label.d_secsize, label.d_nsectors, label.d_ntracks,
+ label.d_ncylinders, label.d_secpercyl, label.d_secperunit);
+
+ printf ("Spares per track %d\tSpares per cyl %d\tAlternates %d\n",
+ label.d_sparespertrack, label.d_sparespercyl,
+ label.d_acylinders);
+
+ printf ("RPM %d\tileave %d\ttskew %d\tcskew %d\theadsw %d\ttrkseek %d\n",
+ label.d_rpm, label.d_interleave, label.d_trackskew,
+ label.d_cylskew, label.d_headswitch, label.d_trkseek);
+
+ printf ("flags: %d\n", label.d_flags);
+
+ printf ("npartitions: %d\n", label.d_npartitions);
+
+ printf ("bbsize %d\tsbsize %d\n", label.d_bbsize, label.d_sbsize);
+
+ printf ("part\tsize\toff\tfsize\tfstype\tfrag\tcpg\n");
+ for (i = 0; i < label.d_npartitions; i++)
+ {
+ printf ("%c:\t%d\t%d\t%d\t%s\t%d\t%d\n",
+ 'a' + i,
+ label.d_partitions[i].p_size,
+ label.d_partitions[i].p_offset,
+ label.d_partitions[i].p_fsize,
+ fstypenames[label.d_partitions[i].p_fstype],
+ label.d_partitions[i].p_frag,
+ label.d_partitions[i].p_cpg);
+ }
+ exit (0);
+}
diff --git a/fstests/timertest.c b/fstests/timertest.c
new file mode 100644
index 00000000..2d602560
--- /dev/null
+++ b/fstests/timertest.c
@@ -0,0 +1,64 @@
+/* A test for the Hurd timer and getchar
+ Copyright (C) 1994,2001,02 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <signal.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <error.h>
+
+void
+alarm_handler (int signo)
+{
+ printf ("Received alarm\n");
+ fflush (stdout);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct itimerval real_timer;
+
+ real_timer.it_interval.tv_usec = 0;
+ real_timer.it_interval.tv_sec = 1;
+ real_timer.it_value.tv_usec = 0;
+ real_timer.it_value.tv_sec = 1;
+
+ signal (SIGALRM, alarm_handler);
+
+ if (setitimer (ITIMER_REAL, &real_timer, 0) < 0)
+ error (1, errno, "Setting timer");
+
+ while (1)
+ {
+ int c;
+ puts ("Pausing for input or one second...");
+ fflush (stdout);
+ c = getchar ();
+ if (ferror (stdin))
+ error (1, errno, "getchar");
+ if (c == EOF)
+ {
+ puts ("Saw EOF. Pausing (no input)...");
+ fflush (stdout);
+ sigpause (0);
+ }
+ else
+ printf ("Saw %.3o\n", c);
+ }
+}
diff --git a/ftpfs/Makefile b/ftpfs/Makefile
new file mode 100644
index 00000000..70398908
--- /dev/null
+++ b/ftpfs/Makefile
@@ -0,0 +1,30 @@
+# Makefile for ftpfs
+#
+# Copyright (C) 1997, 2000, 2012 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := ftpfs
+makemode := server
+
+target = ftpfs
+
+SRCS = ftpfs.c fs.c host.c netfs.c dir.c conn.c ccache.c node.c ncache.c
+
+OBJS = $(SRCS:.c=.o)
+HURDLIBS = netfs fshelp iohelp ports ihash ftpconn shouldbeinlibc
+OTHERLIBS = -lpthread
+
+include ../Makeconf
diff --git a/ftpfs/ccache.c b/ftpfs/ccache.c
new file mode 100644
index 00000000..fa63c687
--- /dev/null
+++ b/ftpfs/ccache.c
@@ -0,0 +1,293 @@
+/* Remote file contents caching
+
+ Copyright (C) 1997, 1999 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include <hurd/netfs.h>
+
+#include "ccache.h"
+
+#define READ_CHUNK_SIZE (8*1024)
+#define ALLOC_CHUNK_SIZE (64*1024)
+
+/* Read LEN bytes at OFFS in the file referred to by CC into DATA, or return
+ an error. */
+error_t
+ccache_read (struct ccache *cc, off_t offs, size_t len, void *data)
+{
+ error_t err = 0;
+ size_t max = offs + len;
+
+ pthread_mutex_lock (&cc->lock);
+
+ if (max > cc->size)
+ max = cc->size;
+
+ while (cc->max < max && !err)
+ {
+ if (cc->fetching_active)
+ /* Some thread is fetching data, so just let it do its thing, but get
+ a wakeup call when it's done. */
+ {
+ if (pthread_hurd_cond_wait_np (&cc->wakeup, &cc->lock))
+ err = EINTR;
+ }
+ else
+ {
+ int re_connected = 0;
+ struct netnode *nn = cc->node->nn;
+
+ cc->fetching_active = 1;
+
+ while (cc->max < max && !err)
+ {
+ pthread_mutex_unlock (&cc->lock);
+
+ if (! cc->conn)
+ /* We need to setup a connection to fetch data over. */
+ {
+ err = ftpfs_get_ftp_conn (nn->fs, &cc->conn);
+ if (! err)
+ {
+ err = ftp_conn_start_retrieve (cc->conn, nn->rmt_path,
+ &cc->data_conn);
+ if (err == ENOENT)
+ err = ESTALE;
+ if (err)
+ {
+ ftpfs_release_ftp_conn (nn->fs, cc->conn);
+ cc->conn = 0;
+ }
+ else
+ cc->data_conn_pos = 0;
+ }
+ re_connected = 1;
+ }
+
+ if (! err)
+ /* Try and read some data over the connection. */
+ {
+ size_t new_end = cc->max + READ_CHUNK_SIZE;
+
+ if (new_end > cc->size)
+ new_end = cc->size;
+
+ if (new_end > cc->alloced)
+ /* Make some room in memory for the new part of the
+ image. */
+ {
+ size_t alloc_end = cc->alloced + ALLOC_CHUNK_SIZE;
+
+ if (alloc_end < new_end)
+ alloc_end = new_end;
+ else if (alloc_end > cc->size)
+ alloc_end = cc->size;
+
+ if (cc->alloced == 0)
+ {
+ vm_address_t addr = 0;
+ addr = (vm_address_t) mmap (0, alloc_end,
+ PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ err = (addr == -1) ? errno : 0;
+ if (! err)
+ cc->image = (char *)addr;
+ }
+ else
+ {
+ vm_address_t addr =
+ (vm_address_t)cc->image + cc->alloced;
+ /* XXX. This can't be replaced with mmap until we
+ have MAP_EXCL. -tb */
+ err = vm_allocate (mach_task_self (),
+ &addr, alloc_end - cc->alloced,
+ 0);
+ if (err == EKERN_NO_SPACE)
+ /* Gack. We've goota move the whole splooge. */
+ {
+ addr = 0;
+ addr = (vm_address_t) mmap (0, alloc_end,
+ PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ err = (addr == -1) ? errno : 0;
+ if (! err)
+ /* That worked; copy what's already-fetched. */
+ {
+ bcopy (cc->image, (void *)addr, cc->max);
+ munmap (cc->image, cc->alloced);
+ cc->image = (char *)addr;
+ }
+ }
+ }
+ if (! err)
+ cc->alloced = alloc_end;
+ }
+
+ if (! err)
+ {
+ ssize_t rd =
+ read (cc->data_conn,
+ cc->image + cc->data_conn_pos,
+ new_end - cc->data_conn_pos);
+ if (rd < 0)
+ err = errno;
+ else if (rd == 0)
+ /* EOF. This either means the file changed size, or
+ our data-connection got closed; we just try to
+ open the connection a second time, and then if
+ that fails, assume the size changed. */
+ {
+ if (re_connected)
+ err = EIO; /* Something's fucked */
+ else
+ /* Try opening the connection again. */
+ {
+ close (cc->data_conn);
+ ftp_conn_finish_transfer (cc->conn);
+ ftpfs_release_ftp_conn (nn->fs, cc->conn);
+ cc->conn = 0;
+ }
+ }
+ else
+ {
+ cc->data_conn_pos += rd;
+ if (cc->data_conn_pos > cc->max)
+ cc->max = cc->data_conn_pos;
+ }
+ }
+
+ if (!err && ports_self_interrupted ())
+ err = EINTR;
+ }
+
+ pthread_mutex_lock (&cc->lock);
+
+ if (cc->max < max && !err)
+ /* If anyone's waiting for data, let them look (if we're done
+ fetching, this gets delayed until below). */
+ pthread_cond_broadcast (&cc->wakeup);
+ }
+
+ if (!err && cc->conn && cc->max == cc->size)
+ /* We're finished reading all data, close the data connection. */
+ {
+ close (cc->data_conn);
+ ftp_conn_finish_transfer (cc->conn);
+ ftpfs_release_ftp_conn (nn->fs, cc->conn);
+ cc->conn = 0;
+ }
+
+ /* We're done, error or no. */
+ cc->fetching_active = 0;
+
+ /* Let others know something's going on. */
+ pthread_cond_broadcast (&cc->wakeup);
+ }
+ }
+
+ if (! err)
+ bcopy (cc->image + offs, data, max - offs);
+
+ pthread_mutex_unlock (&cc->lock);
+
+ return err;
+}
+
+/* Discard any cached contents in CC. */
+error_t
+ccache_invalidate (struct ccache *cc)
+{
+ error_t err = 0;
+
+ pthread_mutex_lock (&cc->lock);
+
+ while (cc->fetching_active && !err)
+ /* Some thread is fetching data, so just let it do its thing, but get
+ a wakeup call when it's done. */
+ {
+ if (pthread_hurd_cond_wait_np (&cc->wakeup, &cc->lock))
+ err = EINTR;
+ }
+
+ if (! err)
+ {
+ if (cc->alloced > 0)
+ {
+ munmap (cc->image, cc->alloced);
+ cc->image = 0;
+ cc->alloced = 0;
+ cc->max = 0;
+ }
+ if (cc->conn)
+ {
+ close (cc->data_conn);
+ ftp_conn_finish_transfer (cc->conn);
+ ftpfs_release_ftp_conn (cc->node->nn->fs, cc->conn);
+ cc->conn = 0;
+ }
+ }
+
+ pthread_mutex_unlock (&cc->lock);
+
+ return err;
+}
+
+/* Return a ccache object for NODE in CC. */
+error_t
+ccache_create (struct node *node, struct ccache **cc)
+{
+ struct ccache *new = malloc (sizeof (struct ccache));
+
+ if (! new)
+ return ENOMEM;
+
+ new->node = node;
+ new->image = 0;
+ new->size = node->nn_stat.st_size;
+ new->max = 0;
+ new->alloced = 0;
+ pthread_mutex_init (&new->lock, NULL);
+ pthread_cond_init (&new->wakeup, NULL);
+ new->fetching_active = 0;
+ new->conn = 0;
+ new->data_conn = -1;
+
+ *cc = new;
+
+ return 0;
+}
+
+/* Free all resources used by CC. */
+void
+ccache_free (struct ccache *cc)
+{
+ if (cc->alloced > 0)
+ munmap (cc->image, cc->alloced);
+ if (cc->data_conn >= 0)
+ close (cc->data_conn);
+ if (cc->conn)
+ {
+ ftp_conn_finish_transfer (cc->conn);
+ ftpfs_release_ftp_conn (cc->node->nn->fs, cc->conn);
+ }
+ free (cc);
+}
diff --git a/ftpfs/ccache.h b/ftpfs/ccache.h
new file mode 100644
index 00000000..78f90bd2
--- /dev/null
+++ b/ftpfs/ccache.h
@@ -0,0 +1,73 @@
+/* Remote file contents caching
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef __CCACHE_H__
+#define __CCACHE_H__
+
+#include "ftpfs.h"
+
+struct ccache
+{
+ /* The filesystem node this is a cache of. */
+ struct node *node;
+
+ /* In memory file image, alloced using vm_allocate. */
+ char *image;
+
+ /* Size of data. */
+ off_t size;
+
+ /* Upper bounds of fetched image. */
+ off_t max;
+
+ /* Amount of IMAGE that has been allocated. */
+ size_t alloced;
+
+ pthread_mutex_t lock;
+
+ /* People can wait for a reading thread on this condition. */
+ pthread_cond_t wakeup;
+
+ /* True if some thread is now fetching data. Only that thread should
+ modify the DATA_CONN, DATA_CONN_POS, and MAX fields. */
+ int fetching_active;
+
+ /* Ftp connection over which data is being fetched, or 0. */
+ struct ftp_conn *conn;
+ /* File descriptor over which data is being fetched. */
+ int data_conn;
+ /* Where DATA_CONN points in the file. */
+ off_t data_conn_pos;
+};
+
+/* Read LEN bytes at OFFS in the file referred to by CC into DATA, or return
+ an error. */
+error_t ccache_read (struct ccache *cc, off_t offs, size_t len, void *data);
+
+/* Discard any cached contents in CC. */
+error_t ccache_invalidate (struct ccache *cc);
+
+/* Return a ccache object for NODE in CC. */
+error_t ccache_create (struct node *node, struct ccache **cc);
+
+/* Free all resources used by CC. */
+void ccache_free (struct ccache *cc);
+
+#endif /* __CCACHE_H__ */
diff --git a/ftpfs/conn.c b/ftpfs/conn.c
new file mode 100644
index 00000000..0f1b0973
--- /dev/null
+++ b/ftpfs/conn.c
@@ -0,0 +1,106 @@
+/* Ftp connection management
+
+ Copyright (C) 1997,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "ftpfs.h"
+
+/* A particular connection. */
+struct ftpfs_conn
+{
+ struct ftp_conn *conn;
+ struct ftpfs_conn *next;
+};
+
+/* For debugging purposes, give each connection a unique integer id. */
+static unsigned conn_id = 0;
+
+/* Get an ftp connection to use for an operation. */
+error_t
+ftpfs_get_ftp_conn (struct ftpfs *fs, struct ftp_conn **conn)
+{
+ struct ftpfs_conn *fsc;
+
+ pthread_spin_lock (&fs->conn_lock);
+ fsc = fs->free_conns;
+ if (fsc)
+ fs->free_conns = fsc->next;
+ pthread_spin_unlock (&fs->conn_lock);
+
+ if (! fsc)
+ {
+ error_t err;
+
+ fsc = malloc (sizeof (struct ftpfs_conn));
+ if (! fsc)
+ return ENOMEM;
+
+ err = ftp_conn_create (fs->ftp_params, fs->ftp_hooks, &fsc->conn);
+
+ if (! err)
+ {
+ /* Set connection type to binary. */
+ err = ftp_conn_set_type (fsc->conn, "I");
+ if (err)
+ ftp_conn_free (fsc->conn);
+ }
+
+ if (err)
+ {
+ free (fsc);
+ return err;
+ }
+
+ /* For debugging purposes, give each connection a unique integer id. */
+ fsc->conn->hook = (void *)(uintptr_t)conn_id++;
+ }
+
+ pthread_spin_lock (&fs->conn_lock);
+ fsc->next = fs->conns;
+ fs->conns = fsc;
+ pthread_spin_unlock (&fs->conn_lock);
+
+ *conn = fsc->conn;
+
+ return 0;
+}
+
+/* Return CONN to the pool of free connections in FS. */
+void
+ftpfs_release_ftp_conn (struct ftpfs *fs, struct ftp_conn *conn)
+{
+ struct ftpfs_conn *fsc, *pfsc;
+
+ pthread_spin_lock (&fs->conn_lock);
+ for (pfsc = 0, fsc = fs->conns; fsc; pfsc = fsc, fsc = fsc->next)
+ if (fsc->conn == conn)
+ {
+ if (pfsc)
+ pfsc->next = fsc->next;
+ else
+ fs->conns = fsc->next;
+ fsc->next = fs->free_conns;
+ fs->free_conns = fsc;
+ break;
+ }
+ assert (fsc);
+ pthread_spin_unlock (&fs->conn_lock);
+}
diff --git a/ftpfs/dir.c b/ftpfs/dir.c
new file mode 100644
index 00000000..da5ddbe5
--- /dev/null
+++ b/ftpfs/dir.c
@@ -0,0 +1,885 @@
+/* Directory operations
+
+ Copyright (C) 1997,98,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <unistd.h>
+#include <string.h>
+
+#include <hurd/netfs.h>
+
+#include "ftpfs.h"
+#include "ccache.h"
+
+/* Free the directory entry E and all resources it consumes. */
+void
+free_entry (struct ftpfs_dir_entry *e)
+{
+ assert (! e->self_p); /* We should only free deleted nodes. */
+ free (e->name);
+ if (e->symlink_target)
+ free (e->symlink_target);
+ free (e);
+}
+
+/* Put the directory entry E into the hash table HTABLE, of length HTABLE_LEN. */
+static void
+insert (struct ftpfs_dir_entry *e,
+ struct ftpfs_dir_entry **htable, size_t htable_len)
+{
+ struct ftpfs_dir_entry **t = &htable[e->hv % htable_len];
+ if (*t)
+ (*t)->self_p = &e->next;
+ e->next = *t;
+ e->self_p = t;
+ *t = e;
+}
+
+/* Replace DIR's hashtable with a new one of length NEW_LEN, retaining all
+ existing entries. */
+static error_t
+rehash (struct ftpfs_dir *dir, size_t new_len)
+{
+ int i;
+ size_t old_len = dir->htable_len;
+ struct ftpfs_dir_entry **old_htable = dir->htable;
+ struct ftpfs_dir_entry **new_htable =
+ malloc (new_len * sizeof (struct ftpfs_dir_entry *));
+
+ if (! new_htable)
+ return ENOMEM;
+
+ bzero (new_htable, new_len * sizeof (struct ftpfs_dir_entry *));
+
+ for (i = 0; i < old_len; i++)
+ while (old_htable[i])
+ {
+ struct ftpfs_dir_entry *e = old_htable[i];
+
+ /* Remove E from the old table (don't bother to fixup
+ e->next->self_p). */
+ old_htable[i] = e->next;
+
+ insert (e, new_htable, new_len);
+ }
+
+ free (old_htable);
+
+ dir->htable = new_htable;
+ dir->htable_len = new_len;
+
+ return 0;
+}
+
+/* Calculate NAME's hash value. */
+static size_t
+hash (const char *name)
+{
+ size_t hv = 0;
+ while (*name)
+ hv = ((hv << 5) + *name++) & 0xFFFFFF;
+ return hv;
+}
+
+/* Lookup NAME in DIR and return its entry. If there is no such entry, and
+ ADD is true, then a new entry is allocated and returned, otherwise 0 is
+ returned (if ADD is true then 0 can be returned if a memory allocation
+ error occurs). */
+struct ftpfs_dir_entry *
+lookup (struct ftpfs_dir *dir, const char *name, int add)
+{
+ size_t hv = hash (name);
+ struct ftpfs_dir_entry *h = dir->htable[hv % dir->htable_len], *e = h;
+
+ while (e && strcmp (name, e->name) != 0)
+ e = e->next;
+
+ if (!e && add)
+ {
+ if (dir->num_entries > dir->htable_len)
+ /* Grow the hash table. */
+ if (rehash (dir, (dir->htable_len + 1) * 2 - 1) != 0)
+ return 0;
+
+ e = malloc (sizeof *e);
+ if (e)
+ {
+ e->hv = hv;
+ e->name = strdup (name);
+ e->node = 0;
+ e->dir = dir;
+ e->stat_timestamp = 0;
+ bzero (&e->stat, sizeof e->stat);
+ e->symlink_target = 0;
+ e->noent = 0;
+ e->valid = 0;
+ e->name_timestamp = e->stat_timestamp = 0;
+ e->ordered_next = 0;
+ e->ordered_self_p = 0;
+ e->next = 0;
+ e->self_p = 0;
+ insert (e, dir->htable, dir->htable_len);
+ dir->num_entries++;
+ }
+ }
+
+ return e;
+}
+
+/* Remove E from its position in the ordered_next chain. */
+static void
+ordered_unlink (struct ftpfs_dir_entry *e)
+{
+ if (e->ordered_self_p)
+ *e->ordered_self_p = e->ordered_next;
+ if (e->ordered_next)
+ e->ordered_next->self_p = e->ordered_self_p;
+}
+
+/* Delete E from its directory, freeing any resources it holds. */
+static void
+delete (struct ftpfs_dir_entry *e, struct ftpfs_dir *dir)
+{
+ dir->num_entries--;
+
+ /* Take out of the hash chain. */
+ if (e->self_p)
+ *e->self_p = e->next;
+ if (e->next)
+ e->next->self_p = e->self_p;
+
+ /* This indicates a deleted entry. */
+ e->self_p = 0;
+ e->next = 0;
+
+ /* Take out of the directory ordered list. */
+ ordered_unlink (e);
+
+ /* If there's a node attached, we'll delete the entry whenever it goes
+ away, otherwise, just delete it now. */
+ if (! e->node)
+ free_entry (e);
+}
+
+/* Clear the valid bit in all DIR's htable. */
+static void
+mark (struct ftpfs_dir *dir)
+{
+ size_t len = dir->htable_len, i;
+ struct ftpfs_dir_entry **htable = dir->htable, *e;
+
+ for (i = 0; i < len; i++)
+ for (e = htable[i]; e; e = e->next)
+ e->valid = 0;
+}
+
+/* Delete any entries in DIR which don't have their valid bit set. */
+static void
+sweep (struct ftpfs_dir *dir)
+{
+ size_t len = dir->htable_len, i;
+ struct ftpfs_dir_entry **htable = dir->htable, *e;
+
+ for (i = 0; i < len; i++)
+ for (e = htable[i]; e; e = e->next)
+ if (!e->valid && !e->noent)
+ delete (e, dir);
+}
+
+/* Update the directory entry for NAME to reflect ST and SYMLINK_TARGET.
+ True is returned if successful, or false if there was a memory allocation
+ error. TIMESTAMP is used to record the time of this update. */
+static void
+update_entry (struct ftpfs_dir_entry *e, const struct stat *st,
+ const char *symlink_target, time_t timestamp)
+{
+ ino_t ino;
+ struct ftpfs *fs = e->dir->fs;
+
+ if (e->stat.st_ino)
+ ino = e->stat.st_ino;
+ else
+ ino = fs->next_inode++;
+
+ e->name_timestamp = timestamp;
+
+ if (st)
+ /* The ST and SYMLINK_TARGET parameters are only valid if ST isn't 0. */
+ {
+ e->stat = *st;
+ e->stat_timestamp = timestamp;
+
+ if (!e->symlink_target || !symlink_target
+ || strcmp (e->symlink_target, symlink_target) != 0)
+ {
+ if (e->symlink_target)
+ free (e->symlink_target);
+ e->symlink_target = symlink_target ? strdup (symlink_target) : 0;
+ }
+ }
+
+ /* The st_ino field is always valid. */
+ e->stat.st_ino = ino;
+ e->stat.st_fsid = fs->fsid;
+ e->stat.st_fstype = FSTYPE_FTP;
+}
+
+/* Add the timestamp TIMESTAMP to the set used to detect bulk stats, and
+ return true if there have been enough individual stats recently to call
+ for just refetching the whole directory. */
+static int
+need_bulk_stat (time_t timestamp, struct ftpfs_dir *dir)
+{
+ time_t period = dir->fs->params.bulk_stat_period;
+ unsigned threshold = dir->fs->params.bulk_stat_threshold;
+
+ if (timestamp > dir->bulk_stat_base_stamp + period * 3)
+ /* No stats done in a while, just start over. */
+ {
+ dir->bulk_stat_count_first_half = 1;
+ dir->bulk_stat_count_second_half = 0;
+ dir->bulk_stat_base_stamp = (timestamp / period) * period;
+ }
+ else if (timestamp > dir->bulk_stat_base_stamp + period * 2)
+ /* Start a new period, but keep the second half of the old one. */
+ {
+ dir->bulk_stat_count_first_half = dir->bulk_stat_count_second_half;
+ dir->bulk_stat_count_second_half = 1;
+ dir->bulk_stat_base_stamp += period;
+ }
+ else if (timestamp > dir->bulk_stat_base_stamp + period)
+ dir->bulk_stat_count_second_half++;
+ else
+ dir->bulk_stat_count_first_half++;
+
+ return
+ (dir->bulk_stat_count_first_half + dir->bulk_stat_count_second_half)
+ > threshold;
+}
+
+static void
+reset_bulk_stat_info (struct ftpfs_dir *dir)
+{
+ dir->bulk_stat_count_first_half = 0;
+ dir->bulk_stat_count_second_half = 0;
+ dir->bulk_stat_base_stamp = 0;
+}
+
+/* State shared between ftpfs_dir_refresh and update_ordered_entry. */
+struct dir_fetch_state
+{
+ struct ftpfs_dir *dir;
+ time_t timestamp;
+
+ /* A pointer to the NEXT-field of the previously seen entry, or a pointer
+ to the ORDERED field in the directory if this is the first. */
+ struct ftpfs_dir_entry **prev_entry_next_p;
+};
+
+/* Update the directory entry for NAME to reflect ST and SYMLINK_TARGET, also
+ rearranging the entries to reflect the order in which they are sent from
+ the server, and setting their valid bits so that obsolete entries can be
+ deleted. HOOK points to the state from ftpfs_dir_fetch. */
+static error_t
+update_ordered_entry (const char *name, const struct stat *st,
+ const char *symlink_target, void *hook)
+{
+ struct dir_fetch_state *dfs = hook;
+ struct ftpfs_dir_entry *e = lookup (dfs->dir, name, 1);
+
+ if (! e)
+ return ENOMEM;
+
+ update_entry (e, st, symlink_target, dfs->timestamp);
+ e->valid = 1;
+
+ if (! e->ordered_self_p)
+ /* Position E in the ordered chain following the previously seen entry. */
+ {
+ /* The PREV_ENTRY_NEXT_P field holds a pointer to the NEXT-field of the
+ previous entry, or a pointer to the ORDERED field in the directory. */
+ e->ordered_self_p = dfs->prev_entry_next_p;
+
+ if (*e->ordered_self_p)
+ /* Update the self_p pointer of the previous successor. */
+ (*e->ordered_self_p)->ordered_self_p = &e->ordered_next;
+
+ /* E comes before the previous successor. */
+ e->ordered_next = *e->ordered_self_p;
+
+ *e->ordered_self_p = e; /* Put E there. */
+ }
+
+ /* Put the next entry after this one. */
+ dfs->prev_entry_next_p = &e->ordered_next;
+
+ return 0;
+}
+
+/* Update the directory entry for NAME, rearranging the entries to reflect
+ the order in which they are sent from the server, and setting their valid
+ bits so that obsolete entries can be deleted. HOOK points to the state
+ from ftpfs_dir_fetch. */
+static error_t
+update_ordered_name (const char *name, void *hook)
+{
+ /* We just do the same thing as for stats, but without the stat info. */
+ return update_ordered_entry (name, 0, 0, hook);
+}
+
+/* Refresh DIR from the directory DIR_NAME in the filesystem FS. If
+ UPDATE_STATS is true, then directory stat information will also be
+ updated. If PRESERVE_ENTRY is non-0, that entry won't be deleted if it's
+ not in the directory after the refresh, but instead will have its NOENT
+ flag turned on. */
+static error_t
+refresh_dir (struct ftpfs_dir *dir, int update_stats, time_t timestamp,
+ struct ftpfs_dir_entry *preserve_entry)
+{
+ error_t err;
+ struct ftp_conn *conn;
+ struct dir_fetch_state dfs;
+
+ if ((update_stats
+ ? dir->stat_timestamp + dir->fs->params.stat_timeout
+ : dir->name_timestamp + dir->fs->params.name_timeout)
+ >= timestamp)
+ /* We've already refreshed this directory recently. */
+ return 0;
+
+ err = ftpfs_get_ftp_conn (dir->fs, &conn);
+ if (err)
+ return err;
+
+ /* Mark directory entries so we can GC them later using sweep. */
+ mark (dir);
+
+ if (update_stats)
+ /* We're doing a bulk stat now, so don't do another for a while. */
+ reset_bulk_stat_info (dir);
+
+ /* Info passed to update_ordered_entry. */
+ dfs.dir = dir;
+ dfs.timestamp = timestamp;
+ dfs.prev_entry_next_p = &dir->ordered;
+
+ /* Make sure `.' and `..' are always included (if the actual list also
+ includes `.' and `..', the ordered may be rearranged). */
+ err = update_ordered_name (".", &dfs);
+ if (! err)
+ err = update_ordered_name ("..", &dfs);
+
+ /* Refetch the directory from the server. */
+ if (update_stats)
+ /* Fetch both names and stat info. */
+ err = ftp_conn_get_stats (conn, dir->rmt_path, 1,
+ update_ordered_entry, &dfs);
+ else
+ /* Just fetch names. */
+ err = ftp_conn_get_names (conn, dir->rmt_path, update_ordered_name, &dfs);
+
+ if (! err)
+ /* GC any directory entries that weren't seen this time. */
+ {
+ dir->name_timestamp = timestamp;
+ if (update_stats)
+ dir->stat_timestamp = timestamp;
+ if (preserve_entry && !preserve_entry->valid)
+ {
+ preserve_entry->noent = 1;
+ preserve_entry->name_timestamp = timestamp;
+ }
+ sweep (dir);
+ }
+
+ ftpfs_release_ftp_conn (dir->fs, conn);
+
+ return err;
+}
+
+/* Refresh DIR. */
+error_t
+ftpfs_dir_refresh (struct ftpfs_dir *dir)
+{
+ time_t timestamp = NOW;
+ return refresh_dir (dir, 0, timestamp, 0);
+}
+
+/* State shared between ftpfs_dir_entry_refresh and update_old_entry. */
+struct refresh_entry_state
+{
+ struct ftpfs_dir_entry *entry;
+ time_t timestamp;
+};
+
+/* Update the directory entry for NAME to reflect ST and SYMLINK_TARGET.
+ HOOK points to the state from ftpfs_dir_fetch_entry. */
+static error_t
+update_old_entry (const char *name, const struct stat *st,
+ const char *symlink_target, void *hook)
+{
+ struct refresh_entry_state *res = hook;
+
+ if (strcmp (name, res->entry->name) != 0)
+ return EGRATUITOUS;
+
+ update_entry (res->entry, st, symlink_target, res->timestamp);
+
+ return 0;
+}
+
+/* Refresh stat information for NODE. This may actually refresh the whole
+ directory if that is deemed desirable. NODE should be locked. */
+error_t
+ftpfs_refresh_node (struct node *node)
+{
+ struct netnode *nn = node->nn;
+ struct ftpfs_dir_entry *entry = nn->dir_entry;
+
+ if (! entry)
+ /* This is a deleted node, don't attempt to do anything. */
+ return 0;
+ else
+ {
+ error_t err = 0;
+ time_t timestamp = NOW;
+ struct ftpfs_dir *dir = entry->dir;
+
+ pthread_mutex_lock (&dir->node->lock);
+
+ if (! entry->self_p)
+ /* This is a deleted entry, just awaiting disposal; do so. */
+ {
+ nn->dir_entry = 0;
+ free_entry (entry);
+ return 0;
+ }
+ else if ((entry->name_timestamp + dir->fs->params.name_timeout
+ >= timestamp)
+ && entry->noent)
+ err = ENOENT;
+ else if (entry->stat_timestamp + dir->fs->params.stat_timeout < timestamp)
+ {
+ /* Stat information needs updating. */
+ if (need_bulk_stat (timestamp, dir))
+ /* Refetch the whole directory from the server. */
+ {
+ err = refresh_dir (entry->dir, 1, timestamp, entry);
+ if (!err && entry->noent)
+ err = ENOENT;
+ }
+ else if (*(entry->name))
+ {
+ /* The root node is treated separately below. */
+ struct ftp_conn *conn;
+
+ err = ftpfs_get_ftp_conn (dir->fs, &conn);
+
+ if (! err)
+ {
+ char *rmt_path;
+
+ err = ftp_conn_append_name (conn, dir->rmt_path, entry->name,
+ &rmt_path);
+ if (! err)
+ {
+ struct refresh_entry_state res;
+
+ res.entry = entry;
+ res.timestamp = timestamp;
+
+ if (! err)
+ err = ftp_conn_get_stats (conn, rmt_path, 0,
+ update_old_entry, &res);
+
+ free (rmt_path);
+ }
+
+ ftpfs_release_ftp_conn (dir->fs, conn);
+ }
+
+ if (err == ENOENT)
+ {
+ entry->noent = 1; /* A negative entry. */
+ entry->name_timestamp = timestamp;
+ }
+ }
+ else
+ {
+ /* Refresh the root node with the old stat
+ information. */
+ struct refresh_entry_state res;
+ res.entry = entry;
+ res.timestamp = timestamp;
+ err = update_old_entry (entry->name,
+ &netfs_root_node->nn_stat,
+ NULL, &res);
+ }
+ }
+
+ if ((entry->stat.st_mtim.tv_sec < node->nn_stat.st_mtim.tv_sec
+ || (entry->stat.st_mtim.tv_sec == node->nn_stat.st_mtim.tv_sec
+ && entry->stat.st_mtim.tv_nsec < node->nn_stat.st_mtim.tv_nsec)
+ || entry->stat.st_size != node->nn_stat.st_size)
+ && nn && nn->contents)
+ /* The file has changed. */
+ ccache_invalidate (nn->contents);
+
+ node->nn_stat = entry->stat;
+ node->nn_translated = S_ISLNK (entry->stat.st_mode) ? S_IFLNK : 0;
+ if (!nn->dir && S_ISDIR (entry->stat.st_mode))
+ ftpfs_dir_create (nn->fs, node, nn->rmt_path, &nn->dir);
+
+ pthread_mutex_unlock (&dir->node->lock);
+
+ ftpfs_cache_node (node);
+
+ return err;
+ }
+}
+
+/* Remove NODE from its entry (if the entry is still valid, it will remain
+ without a node). NODE should be locked. */
+error_t
+ftpfs_detach_node (struct node *node)
+{
+ struct netnode *nn = node->nn;
+ struct ftpfs_dir_entry *entry = nn->dir_entry;
+
+ if (entry)
+ /* NODE is still attached to some entry, so detach it. */
+ {
+ struct ftpfs_dir *dir = entry->dir;
+
+ pthread_mutex_lock (&dir->node->lock);
+
+ if (entry->self_p)
+ /* Just detach NODE from the still active entry. */
+ entry->node = 0;
+ else
+ /* This is a deleted entry, just awaiting disposal; do so. */
+ {
+ nn->dir_entry = 0;
+ free_entry (entry);
+ }
+
+ if (--dir->num_live_entries == 0)
+ netfs_nput (dir->node);
+ else
+ pthread_mutex_unlock (&dir->node->lock);
+ }
+
+ return 0;
+}
+
+/* State shared between ftpfs_dir_lookup and update_new_entry. */
+struct new_entry_state
+{
+ time_t timestamp;
+ struct ftpfs_dir *dir;
+ struct ftpfs_dir_entry *entry;
+};
+
+/* Update the directory entry for NAME to reflect ST and SYMLINK_TARGET.
+ HOOK->entry will be updated to reflect the new entry. */
+static error_t
+update_new_entry (const char *name, const struct stat *st,
+ const char *symlink_target, void *hook)
+{
+ struct ftpfs_dir_entry *e;
+ struct new_entry_state *nes = hook;
+
+ e = lookup (nes->dir, name, 1);
+ if (! e)
+ return ENOMEM;
+
+ update_entry (e, st, symlink_target, nes->timestamp);
+ nes->entry = e;
+
+ return 0;
+}
+
+/* Lookup NAME in DIR, returning its entry, or an error. DIR's node should
+ be locked, and will be unlocked after returning; *NODE will contain the
+ result node, locked, and with an additional reference, or 0 if an error
+ occurs. */
+error_t
+ftpfs_dir_lookup (struct ftpfs_dir *dir, const char *name,
+ struct node **node)
+{
+ struct ftp_conn *conn;
+ struct ftpfs_dir_entry *e;
+ error_t err = 0;
+ char *rmt_path = 0;
+ time_t timestamp = NOW;
+
+ if (*name == '\0' || strcmp (name, ".") == 0)
+ /* Current directory -- just add an additional reference to DIR's node
+ and return it. */
+ {
+ netfs_nref (dir->node);
+ *node = dir->node;
+ return 0;
+ }
+ else if (strcmp (name, "..") == 0)
+ /* Parent directory. */
+ {
+ if (dir->node->nn->dir_entry)
+ {
+ *node = dir->node->nn->dir_entry->dir->node;
+ pthread_mutex_lock (&(*node)->lock);
+ netfs_nref (*node);
+ }
+ else
+ {
+ err = ENOENT; /* No .. */
+ *node = 0;
+ }
+
+ pthread_mutex_unlock (&dir->node->lock);
+
+ return err;
+ }
+
+ e = lookup (dir, name, 0);
+ if (!e || e->name_timestamp + dir->fs->params.name_timeout < timestamp)
+ /* Try to fetch info about NAME. */
+ {
+ if (need_bulk_stat (timestamp, dir))
+ /* Refetch the whole directory from the server. */
+ {
+ err = refresh_dir (dir, 1, timestamp, e);
+ if (!err && !e)
+ e = lookup (dir, name, 0);
+ }
+ else
+ {
+ err = ftpfs_get_ftp_conn (dir->fs, &conn);
+ if (! err)
+ {
+ err = ftp_conn_append_name (conn, dir->rmt_path, name,
+ &rmt_path);
+ if (! err)
+ {
+ struct new_entry_state nes;
+
+ nes.dir = dir;
+ nes.timestamp = timestamp;
+ nes.entry = NULL;
+
+ err = ftp_conn_get_stats (conn, rmt_path, 0,
+ update_new_entry, &nes);
+ if (! err)
+ e = nes.entry;
+ else if (err == ENOENT)
+ {
+ e = lookup (dir, name, 1);
+ if (! e)
+ err = ENOMEM;
+ else
+ {
+ e->noent = 1; /* A negative entry. */
+ e->name_timestamp = timestamp;
+ }
+ }
+ }
+
+ ftpfs_release_ftp_conn (dir->fs, conn);
+ }
+ }
+ }
+
+ if (! err)
+ {
+ if (e && !e->noent)
+ /* We've got a dir entry, get a node for it. */
+ {
+ /* If there's already a node, add a ref so that it doesn't go
+ away. */
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ if (e->node)
+ e->node->references++;
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+
+ if (! e->node)
+ /* No node; make one and install it into E. */
+ {
+ if (! rmt_path)
+ /* We have to cons up the absolute path. We need the
+ connection just for the pathname frobbing functions. */
+ {
+ err = ftpfs_get_ftp_conn (dir->fs, &conn);
+ if (! err)
+ {
+ err = ftp_conn_append_name (conn, dir->rmt_path, name,
+ &rmt_path);
+ ftpfs_release_ftp_conn (dir->fs, conn);
+ }
+ }
+
+ if (! err)
+ {
+ err = ftpfs_create_node (e, rmt_path, &e->node);
+
+ if (!err && dir->num_live_entries++ == 0)
+ /* Keep a reference to dir's node corresponding to
+ children. */
+ {
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ dir->node->references++;
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+ }
+ }
+ }
+
+ if (! err)
+ {
+ *node = e->node;
+ /* We have to unlock DIR's node before locking the child node
+ because the locking order is always child-parent. We know
+ the child node won't go away because we already hold the
+ additional reference to it. */
+ pthread_mutex_unlock (&dir->node->lock);
+ pthread_mutex_lock (&e->node->lock);
+ }
+ }
+ else
+ err = ENOENT;
+ }
+
+ if (err)
+ {
+ *node = 0;
+ pthread_mutex_unlock (&dir->node->lock);
+ }
+
+ if (rmt_path)
+ free (rmt_path);
+
+ return err;
+}
+
+/* Lookup the null name in DIR, and return a node for it in NODE. Unlike
+ ftpfs_dir_lookup, this won't attempt to validate the existence of the
+ entry (to avoid opening a new connection if possible) -- that will happen
+ the first time the entry is refreshed. Also unlink ftpfs_dir_lookup, this
+ function doesn't expect DIR to be locked, and won't return *NODE locked.
+ This function is only used for bootstrapping the root node. */
+error_t
+ftpfs_dir_null_lookup (struct ftpfs_dir *dir, struct node **node)
+{
+ struct ftpfs_dir_entry *e;
+ error_t err = 0;
+
+ e = lookup (dir, "", 1);
+ if (! e)
+ return ENOMEM;
+
+ if (! e->noent)
+ /* We've got a dir entry, get a node for it. */
+ {
+ /* If there's already a node, add a ref so that it doesn't go away. */
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ if (e->node)
+ e->node->references++;
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+
+ if (! e->node)
+ /* No node; make one and install it into E. */
+ {
+ err = ftpfs_create_node (e, dir->rmt_path, &e->node);
+
+ if (!err && dir->num_live_entries++ == 0)
+ /* Keep a reference to dir's node corresponding to children. */
+ {
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ dir->node->references++;
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+ }
+ }
+
+ if (! err)
+ *node = e->node;
+ }
+ else
+ err = ENOENT;
+
+ return err;
+}
+
+/* Size of initial htable for a new directory. */
+#define INIT_HTABLE_LEN 5
+
+/* Return in DIR a new ftpfs directory, in the filesystem FS, with node NODE
+ and remote path RMT_PATH. RMT_PATH is *not copied*, so it shouldn't ever
+ change while this directory is active. */
+error_t
+ftpfs_dir_create (struct ftpfs *fs, struct node *node, const char *rmt_path,
+ struct ftpfs_dir **dir)
+{
+ struct ftpfs_dir *new = malloc (sizeof (struct ftpfs_dir));
+ struct ftpfs_dir_entry **htable
+ = calloc (INIT_HTABLE_LEN, sizeof (struct ftpfs_dir_entry *));
+
+ if (!new || !htable)
+ {
+ if (new)
+ free (new);
+ if (htable)
+ free (htable);
+ return ENOMEM;
+ }
+
+ /* Hold a reference to the new dir's node. */
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ node->references++;
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+
+ new->num_entries = 0;
+ new->num_live_entries = 0;
+ new->htable_len = INIT_HTABLE_LEN;
+ new->htable = htable;
+ new->ordered = 0;
+ new->rmt_path = rmt_path;
+ new->fs = fs;
+ new->node = node;
+ new->stat_timestamp = 0;
+ new->name_timestamp = 0;
+ new->bulk_stat_base_stamp = 0;
+ new->bulk_stat_count_first_half = 0;
+ new->bulk_stat_count_second_half = 0;
+
+ *dir = new;
+
+ return 0;
+}
+
+void
+ftpfs_dir_free (struct ftpfs_dir *dir)
+{
+ /* Free all entries. */
+ mark (dir);
+ sweep (dir);
+
+ if (dir->htable)
+ free (dir->htable);
+
+ netfs_nrele (dir->node);
+
+ free (dir);
+}
diff --git a/ftpfs/fs.c b/ftpfs/fs.c
new file mode 100644
index 00000000..d3a93070
--- /dev/null
+++ b/ftpfs/fs.c
@@ -0,0 +1,86 @@
+/* Fs operations
+
+ Copyright (C) 1997, 2001, 2003 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stddef.h>
+#include <string.h>
+
+#include <hurd/ihash.h>
+#include <hurd/netfs.h>
+
+#include "ftpfs.h"
+
+/* Create a new ftp filesystem with the given parameters. */
+error_t
+ftpfs_create (char *rmt_path, int fsid,
+ struct ftp_conn_params *ftp_params,
+ struct ftp_conn_hooks *ftp_hooks,
+ struct ftpfs_params *params,
+ struct ftpfs **fs)
+{
+ error_t err;
+ /* Since nodes keep some of their state in the enclosing directory, we need
+ one for the root node. */
+ struct ftpfs_dir *super_root_dir;
+ /* And also a super-root node, just used for locking SUPER_ROOT_DIR. */
+ struct node *super_root;
+ /* The new node. */
+ struct ftpfs *new = malloc (sizeof (struct ftpfs));
+
+ if (! new)
+ return ENOMEM;
+
+ new->free_conns = 0;
+ new->conns = 0;
+ pthread_spin_init (&new->conn_lock, PTHREAD_PROCESS_PRIVATE);
+ new->node_cache_mru = new->node_cache_lru = 0;
+ new->node_cache_len = 0;
+ pthread_mutex_init (&new->node_cache_lock, NULL);
+
+ new->fsid = fsid;
+ new->next_inode = 2;
+
+ new->params = *params;
+ new->ftp_params = ftp_params;
+ new->ftp_hooks = ftp_hooks;
+
+ hurd_ihash_init (&new->inode_mappings,
+ offsetof (struct ftpfs_dir_entry, inode_locp));
+ pthread_spin_init (&new->inode_mappings_lock, PTHREAD_PROCESS_PRIVATE);
+
+ super_root = netfs_make_node (0);
+ if (! super_root)
+ err = ENOMEM;
+ else
+ {
+ err = ftpfs_dir_create (new, super_root, rmt_path, &super_root_dir);
+ if (! err)
+ err = ftpfs_dir_null_lookup (super_root_dir, &new->root);
+ }
+
+ if (err)
+ {
+ hurd_ihash_destroy (&new->inode_mappings);
+ free (new);
+ }
+ else
+ *fs = new;
+
+ return err;
+}
diff --git a/ftpfs/ftpfs.c b/ftpfs/ftpfs.c
new file mode 100644
index 00000000..794439b4
--- /dev/null
+++ b/ftpfs/ftpfs.c
@@ -0,0 +1,430 @@
+/* Ftp filesystem
+
+ Copyright (C) 1997,98,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+#include <unistd.h>
+#include <argp.h>
+#include <error.h>
+#include <argz.h>
+#include <netdb.h>
+#include <sys/stat.h>
+
+#include <version.h>
+
+#include <hurd/netfs.h>
+
+#include "ftpfs.h"
+
+char *netfs_server_name = "ftpfs";
+char *netfs_server_version = HURD_VERSION;
+
+const char *argp_program_version = STANDARD_HURD_VERSION (ftpfs);
+
+static char *args_doc = "REMOTE_FS [SERVER]";
+static char *doc = "Hurd ftp filesystem translator."
+"\vIf SERVER is not specified, an attempt is made to extract"
+" it from REMOTE_FS, using `SERVER:FS' notation."
+" SERVER can be a hostname, in which case anonymous ftp is used,"
+" or may include a user and password like `USER:PASSWORD@HOST' (the"
+" `:PASSWORD' part is optional).";
+
+/* The filesystem. */
+struct ftpfs *ftpfs;
+
+/* Parameters describing the server we're connecting to. */
+struct ftp_conn_params *ftpfs_ftp_params = 0;
+
+/* customization hooks. */
+struct ftp_conn_hooks ftpfs_ftp_hooks = { interrupt_check: ports_self_interrupted };
+
+/* The (user-specified) name of the SERVER:FILESYSTEM we're connected too. */
+char *ftpfs_remote_fs;
+
+/* The FILESYSTEM component of FTPFS_REMOTE_FS. */
+char *ftpfs_remote_root;
+
+/* Random parameters for the filesystem. */
+struct ftpfs_params ftpfs_params;
+
+volatile struct mapped_time_value *ftpfs_maptime;
+
+int netfs_maxsymlinks = 12;
+
+extern error_t lookup_server (const char *server,
+ struct ftp_conn_params **params, int *h_err);
+
+static FILE *debug_stream = 0;
+static char *debug_stream_name = 0;
+static pthread_mutex_t debug_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Prints ftp connection log to DEBUG_STREAM. */
+static void
+cntl_debug (struct ftp_conn *conn, int type, const char *txt)
+{
+ char *type_str;
+
+ switch (type)
+ {
+ case FTP_CONN_CNTL_DEBUG_CMD: type_str = ">"; break;
+ case FTP_CONN_CNTL_DEBUG_REPLY: type_str = "="; break;
+ default: type_str = "?"; break;
+ }
+
+ pthread_mutex_lock (&debug_lock);
+ if (debug_stream)
+ {
+ fprintf (debug_stream, "%u.%s%s\n",
+ (unsigned)(uintptr_t)conn->hook, type_str, txt);
+ fflush (debug_stream);
+ }
+ pthread_mutex_unlock (&debug_lock);
+}
+
+/* Various default parameters. */
+#define DEFAULT_NAME_TIMEOUT 300
+#define DEFAULT_STAT_TIMEOUT 120
+
+#define DEFAULT_BULK_STAT_PERIOD 10
+#define DEFAULT_BULK_STAT_THRESHOLD 5
+
+#define DEFAULT_NODE_CACHE_MAX 50
+
+/* Return a string corresponding to the printed rep of DEFAULT_what */
+#define ___D(what) #what
+#define __D(what) ___D(what)
+#define _D(what) __D(DEFAULT_ ## what)
+
+/* Common (runtime & startup) options. */
+
+#define OPT_NO_DEBUG 1
+
+#define OPT_NAME_TIMEOUT 5
+#define OPT_STAT_TIMEOUT 7
+#define OPT_NODE_CACHE_MAX 8
+#define OPT_BULK_STAT_PERIOD 9
+#define OPT_BULK_STAT_THRESHOLD 10
+
+/* Options usable both at startup and at runtime. */
+static const struct argp_option common_options[] =
+{
+ {"debug", 'D', "FILE", OPTION_ARG_OPTIONAL, "Print debug output to FILE"},
+ {"no-debug", OPT_NO_DEBUG, 0, OPTION_HIDDEN },
+
+ {0,0,0,0, "Parameters:"},
+ {"name-timeout", OPT_NAME_TIMEOUT, "SECS", 0,
+ "Time directory names are cached (default " _D(NAME_TIMEOUT) ")"},
+ {"stat-timeout", OPT_STAT_TIMEOUT, "SECS", 0,
+ "Time stat information is cached (default " _D(STAT_TIMEOUT) ")"},
+ {"node-cache-size", OPT_NODE_CACHE_MAX, "ENTRIES", 0,
+ "Number of recently used filesystem nodes that are cached (default "
+ _D(NODE_CACHE_MAX) ")"},
+
+ {"bulk-stat-period", OPT_BULK_STAT_PERIOD, "SECS", 0,
+ "Period for detecting bulk stats (default " _D(BULK_STAT_PERIOD) ")"},
+ {"bulk-stat-threshold", OPT_BULK_STAT_THRESHOLD, "SECS", 0,
+ "Number of stats within the bulk-stat-period that trigger a bulk stat"
+ " (default " _D(BULK_STAT_THRESHOLD) ")"},
+
+ {0, 0}
+};
+
+static error_t
+parse_common_opt (int key, char *arg, struct argp_state *state)
+{
+ error_t err = 0;
+ struct ftpfs_params *params = state->input;
+
+ switch (key)
+ {
+ case 'D':
+ pthread_mutex_lock (&debug_lock);
+
+ if (debug_stream && debug_stream != stderr)
+ fclose (debug_stream);
+ if (debug_stream_name)
+ {
+ free (debug_stream_name);
+ debug_stream_name = 0;
+ }
+
+ if (arg)
+ {
+ debug_stream_name = strdup (arg);
+ if (! debug_stream_name)
+ {
+ argp_failure (state, 0, ENOMEM, "%s: Cannot open debugging file", arg);
+ err = ENOMEM;
+ }
+
+ if (! err)
+ {
+ debug_stream = fopen (arg, "w+");
+ if (! debug_stream)
+ {
+ err = errno;
+ argp_failure (state, 0, err, "%s: Cannot open debugging file", arg);
+ }
+ }
+ }
+ else
+ debug_stream = stderr;
+
+ if (! err)
+ ftpfs_ftp_hooks.cntl_debug = cntl_debug;
+
+ pthread_mutex_unlock (&debug_lock);
+
+ return err;
+
+ case OPT_NO_DEBUG:
+ pthread_mutex_lock (&debug_lock);
+ if (debug_stream && debug_stream != stderr)
+ fclose (debug_stream);
+ ftpfs_ftp_hooks.cntl_debug = 0;
+ pthread_mutex_unlock (&debug_lock);
+ break;
+
+ case OPT_NODE_CACHE_MAX:
+ params->node_cache_max = atoi (arg); break;
+ case OPT_NAME_TIMEOUT:
+ params->name_timeout = atoi (arg); break;
+ case OPT_STAT_TIMEOUT:
+ params->stat_timeout = atoi (arg); break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static struct argp common_argp = { common_options, parse_common_opt };
+
+/* Startup options. */
+
+static const struct argp_option startup_options[] =
+{
+ { 0 }
+};
+
+/* Parse a single command line option/argument. */
+static error_t
+parse_startup_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case ARGP_KEY_ARG:
+ if (state->arg_num > 1)
+ argp_usage (state);
+ else if (state->arg_num == 0)
+ ftpfs_remote_fs = arg;
+ else
+ /* If the fs & server are two separate args, glom them together into the
+ ":" notation. */
+ {
+ char *rfs = malloc (strlen (ftpfs_remote_fs) + 1 + strlen (arg) + 1);
+ if (! rfs)
+ argp_failure (state, 99, ENOMEM, "%s", arg);
+ stpcpy (stpcpy (stpcpy (rfs, arg), ":"), ftpfs_remote_fs);
+ ftpfs_remote_fs = rfs;
+ }
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ /* Validate the remote fs arg; at this point FTPFS_REMOTE_FS is in
+ SERVER:FS notation. */
+ if (state->arg_num == 0)
+ argp_error (state, "No remote filesystem specified");
+ else
+ {
+ int h_err; /* Host lookup error. */
+ error_t err;
+ char *sep = strrchr (ftpfs_remote_fs, '@');
+
+ if (sep)
+ /* FTPFS_REMOTE_FS includes a '@', which means that it's in
+ USER[:PASS]@HOST:FS notation, so we have to be careful not to
+ choose the wrong `:' as the SERVER-FS separator. */
+ sep = strchr (sep, ':');
+ else
+ sep = strchr (ftpfs_remote_fs, ':');
+
+ if (! sep)
+ /* We have just a host name, so treat it as "HOST:/". */
+ ftpfs_remote_root = "/";
+ else
+ ftpfs_remote_root = sep + 1;
+
+ /* Lookup the ftp server (the part before the `:'). */
+ if (sep)
+ *sep = '\0';
+ err = lookup_server (ftpfs_remote_fs, &ftpfs_ftp_params, &h_err);
+ if (err == EINVAL)
+ argp_failure (state, 10, 0, "%s: %s",
+ ftpfs_remote_fs, hstrerror (h_err));
+ else if (err)
+ argp_failure (state, 11, err, "%s", ftpfs_remote_fs);
+ if (sep)
+ *sep = ':';
+ }
+
+ case ARGP_KEY_INIT:
+ /* Setup up state for our first child parser (common options). */
+ state->child_inputs[0] = &ftpfs_params;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+/* Runtime options. */
+
+/* Parse a single command line option/argument. */
+static error_t
+parse_runtime_opt (int key, char *arg, struct argp_state *state)
+{
+ if (key == ARGP_KEY_INIT)
+ /* Setup up state for our first child parser (common options). */
+ {
+ state->child_inputs[0] = &ftpfs->params;
+ return 0;
+ }
+ else
+ return ARGP_ERR_UNKNOWN;
+}
+
+static const struct argp_child runtime_argp_children[] =
+ { {&common_argp}, {&netfs_std_runtime_argp}, {0} };
+static struct argp runtime_argp =
+ { 0, parse_runtime_opt, 0, 0, runtime_argp_children };
+
+/* Use by netfs_set_options to handle runtime option parsing. */
+struct argp *netfs_runtime_argp = &runtime_argp;
+
+/* Return an argz string describing the current options. Fill *ARGZ
+ with a pointer to newly malloced storage holding the list and *LEN
+ to the length of that storage. */
+error_t
+netfs_append_args (char **argz, size_t *argz_len)
+{
+ char buf[80];
+ error_t err = 0;
+
+#define FOPT(fmt, arg) \
+ do { \
+ if (! err) \
+ { \
+ snprintf (buf, sizeof buf, fmt, arg); \
+ err = argz_add (argz, argz_len, buf); \
+ } \
+ } while (0)
+
+ pthread_mutex_lock (&debug_lock);
+ if (ftpfs_ftp_hooks.cntl_debug && debug_stream)
+ {
+ if (debug_stream != stderr)
+ {
+ char *rep;
+ asprintf (&rep, "--debug=%s", debug_stream_name);
+ err = argz_add (argz, argz_len, rep);
+ free (rep);
+ }
+ else
+ err = argz_add (argz, argz_len, "--debug");
+ }
+ pthread_mutex_unlock (&debug_lock);
+
+ if (ftpfs->params.name_timeout != DEFAULT_NAME_TIMEOUT)
+ FOPT ("--name-timeout=%ld", ftpfs->params.name_timeout);
+ if (ftpfs->params.stat_timeout != DEFAULT_STAT_TIMEOUT)
+ FOPT ("--stat-timeout=%ld", ftpfs->params.stat_timeout);
+ if (ftpfs->params.node_cache_max != DEFAULT_NODE_CACHE_MAX)
+ FOPT ("--node-cache-size=%Zu", ftpfs->params.node_cache_max);
+ if (ftpfs->params.bulk_stat_period != DEFAULT_BULK_STAT_PERIOD)
+ FOPT ("--bulk-stat-period=%ld", ftpfs->params.bulk_stat_period);
+ if (ftpfs->params.bulk_stat_threshold != DEFAULT_BULK_STAT_THRESHOLD)
+ FOPT ("--bulk-stat-threshold=%d", ftpfs->params.bulk_stat_threshold);
+
+ return argz_add (argz, argz_len, ftpfs_remote_fs);
+}
+
+/* Program entry point. */
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap, underlying_node;
+ struct stat underlying_stat;
+ const struct argp_child argp_children[] =
+ { {&common_argp}, {&netfs_std_startup_argp}, {0} };
+ struct argp argp =
+ { startup_options, parse_startup_opt, args_doc, doc, argp_children };
+
+ ftpfs_params.name_timeout = DEFAULT_NAME_TIMEOUT;
+ ftpfs_params.stat_timeout = DEFAULT_STAT_TIMEOUT;
+ ftpfs_params.node_cache_max = DEFAULT_NODE_CACHE_MAX;
+ ftpfs_params.bulk_stat_period = DEFAULT_BULK_STAT_PERIOD;
+ ftpfs_params.bulk_stat_threshold = DEFAULT_BULK_STAT_THRESHOLD;
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+
+ netfs_init ();
+
+ err = maptime_map (0, 0, &ftpfs_maptime);
+ if (err)
+ error (3, err, "mapping time");
+
+ err = ftpfs_create (ftpfs_remote_root, getpid (),
+ ftpfs_ftp_params, &ftpfs_ftp_hooks,
+ &ftpfs_params, &ftpfs);
+ if (err)
+ error (4, err, "%s", ftpfs_remote_fs);
+
+ netfs_root_node = ftpfs->root;
+
+ underlying_node = netfs_startup (bootstrap, 0);
+ err = io_stat (underlying_node, &underlying_stat);
+ if (err)
+ error (1, err, "cannot stat underling node");
+
+ /* Initialize stat information of the root node. */
+ netfs_root_node->nn_stat = underlying_stat;
+ netfs_root_node->nn_stat.st_mode =
+ S_IFDIR | (underlying_stat.st_mode & ~S_IFMT & ~S_ITRANS);
+
+ /* If the underlying node isn't a directory, propagate read permission to
+ execute permission since we need that for lookups. */
+ if (! S_ISDIR (underlying_stat.st_mode))
+ {
+ if (underlying_stat.st_mode & S_IRUSR)
+ netfs_root_node->nn_stat.st_mode |= S_IXUSR;
+ if (underlying_stat.st_mode & S_IRGRP)
+ netfs_root_node->nn_stat.st_mode |= S_IXGRP;
+ if (underlying_stat.st_mode & S_IROTH)
+ netfs_root_node->nn_stat.st_mode |= S_IXOTH;
+ }
+
+ for (;;)
+ netfs_server_loop ();
+}
diff --git a/ftpfs/ftpfs.h b/ftpfs/ftpfs.h
new file mode 100644
index 00000000..206726f1
--- /dev/null
+++ b/ftpfs/ftpfs.h
@@ -0,0 +1,250 @@
+/* Ftp filesystem
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef __FTPFS_H__
+#define __FTPFS_H__
+
+#include <stdlib.h>
+#include <pthread.h>
+#include <ftpconn.h>
+#include <maptime.h>
+#include <hurd/ihash.h>
+
+/* Anonymous types. */
+struct ccache;
+struct ftpfs_conn;
+
+/* A single entry in a directory. */
+struct ftpfs_dir_entry
+{
+ char *name; /* Name of this entry */
+ size_t hv; /* Hash value of NAME (before mod'ing) */
+
+ /* The active node referred to by this name (may be 0).
+ NETFS_NODE_REFCNT_LOCK should be held while frobbing this. */
+ struct node *node;
+
+ struct stat stat;
+ char *symlink_target;
+ time_t stat_timestamp;
+
+ /* The directory to which this entry belongs. */
+ struct ftpfs_dir *dir;
+
+ /* Link to next entry in hash bucket, and address of previous entry's (or
+ hash table's) pointer to this entry. If the SELF_P field is 0, then
+ this is a deleted entry, awaiting final disposal. */
+ struct ftpfs_dir_entry *next, **self_p;
+
+ /* Next entry in `directory order', or 0 if none known. */
+ struct ftpfs_dir_entry *ordered_next, **ordered_self_p;
+
+ /* When the presence/absence of this file was last checked. */
+ time_t name_timestamp;
+
+ hurd_ihash_locp_t inode_locp; /* Used for removing this entry */
+
+ int noent : 1; /* A negative lookup result. */
+ int valid : 1; /* Marker for GC'ing. */
+};
+
+/* A directory. */
+struct ftpfs_dir
+{
+ /* Number of entries in HTABLE. */
+ size_t num_entries;
+
+ /* The number of entries that have nodes attached. We keep an additional
+ reference to our node if there are any, to prevent it from going away. */
+ size_t num_live_entries;
+
+ /* Hash table of entries. */
+ struct ftpfs_dir_entry **htable;
+ size_t htable_len; /* # of elements in HTABLE (not bytes). */
+
+ /* List of dir entries in `directory order', in a linked list using the
+ ORDERED_NEXT and ORDERED_SELF_P fields in each entry. Not all entries
+ in HTABLE need be in this list. */
+ struct ftpfs_dir_entry *ordered;
+
+ /* The filesystem node that this is the directory for. */
+ struct node *node;
+
+ /* The filesystem this directory is in. */
+ struct ftpfs *fs;
+
+ /* The path to this directory on the server. */
+ const char *rmt_path;
+
+ time_t stat_timestamp;
+ time_t name_timestamp;
+
+ /* Stuff for detecting bulk stats. */
+
+ /* The timestamp of the first sample in bulk_stat_count1, rounded to
+ BULK_STAT_PERIOD seconds. */
+ time_t bulk_stat_base_stamp;
+
+ /* The number of stats done in the period [bulk_stat_base_stamp,
+ bulk_stat_base_stamp+BULK_STAT_PERIOD). */
+ unsigned bulk_stat_count_first_half;
+ /* The number of stats done in the period
+ [bulk_stat_base_stamp+BULK_STAT_PERIOD,
+ bulk_stat_base_stamp+BULK_STAT_PERIOD*2). */
+ unsigned bulk_stat_count_second_half;
+};
+
+/* libnetfs node structure. */
+struct netnode
+{
+ /* The remote filesystem. */
+ struct ftpfs *fs;
+
+ /* The directory entry for this node. */
+ struct ftpfs_dir_entry *dir_entry;
+
+ /* The path in FS that this file corresponds to. */
+ const char *rmt_path;
+
+ /* If this is a regular file, an optional cache of the contents. This may
+ be 0, if no cache has yet been created, but once created, it only goes
+ away when the node is destroyed. */
+ struct ccache *contents;
+
+ /* If this is a directory, the contents, or 0 if not fetched. */
+ struct ftpfs_dir *dir;
+
+ /* Position in the node cache. */
+ struct node *ncache_next, *ncache_prev;
+};
+
+/* Various parameters that can be used to change the behavior of an ftpfs. */
+struct ftpfs_params
+{
+ /* Amount of time name existence is cached. */
+ time_t name_timeout;
+
+ /* Amount of time stat information is cached. */
+ time_t stat_timeout;
+
+ /* Parameters for detecting bulk stats; if more than BULK_STAT_THRESHOLD
+ stats are done within BULK_STAT_PERIOD seconds, the whole enclosing
+ directory is fetched. */
+ time_t bulk_stat_period;
+ unsigned bulk_stat_threshold;
+
+ /* The size of the node cache. */
+ size_t node_cache_max;
+};
+
+/* A particular filesystem. */
+struct ftpfs
+{
+ /* Root of filesystem. */
+ struct node *root;
+
+ /* A pool of ftp connections for server threads to use. */
+ struct ftpfs_conn *free_conns;
+ struct ftpfs_conn *conns;
+ pthread_spinlock_t conn_lock;
+
+ /* Parameters for making new ftp connections. */
+ struct ftp_conn_params *ftp_params;
+ struct ftp_conn_hooks *ftp_hooks;
+
+ /* Inode numbers are assigned sequentially in order of creation. */
+ ino_t next_inode;
+ int fsid;
+
+ /* A hash table mapping inode numbers to directory entries. */
+ struct hurd_ihash inode_mappings;
+ pthread_spinlock_t inode_mappings_lock;
+
+ struct ftpfs_params params;
+
+ /* A cache that holds a reference to recently used nodes. */
+ struct node *node_cache_mru, *node_cache_lru;
+ size_t node_cache_len; /* Number of entries in it. */
+ pthread_mutex_t node_cache_lock;
+};
+
+extern volatile struct mapped_time_value *ftpfs_maptime;
+
+/* The current time. */
+#define NOW \
+ ({ struct timeval tv; maptime_read (ftpfs_maptime, &tv); tv.tv_sec; })
+
+/* Create a new ftp filesystem with the given parameters. */
+error_t ftpfs_create (char *rmt_root, int fsid,
+ struct ftp_conn_params *ftp_params,
+ struct ftp_conn_hooks *ftp_hooks,
+ struct ftpfs_params *params,
+ struct ftpfs **fs);
+
+/* Refresh stat information for NODE. This may actually refresh the whole
+ directory if that is deemed desirable. */
+error_t ftpfs_refresh_node (struct node *node);
+
+/* Remove NODE from its entry (if the entry is still valid, it will remain
+ without a node). NODE should be locked. */
+error_t ftpfs_detach_node (struct node *node);
+
+/* Return a new node in NODE, with a name NAME, and return the new node
+ with a single reference in NODE. E may be 0, if this is the root node. */
+error_t ftpfs_create_node (struct ftpfs_dir_entry *e, const char *rmt_path,
+ struct node **node);
+
+/* Add NODE to the recently-used-node cache, which adds a reference to
+ prevent it from going away. NODE should be locked. */
+void ftpfs_cache_node (struct node *node);
+
+/* Get an ftp connection to use for an operation. */
+error_t ftpfs_get_ftp_conn (struct ftpfs *fs, struct ftp_conn **conn);
+
+/* Return CONN to the pool of free connections in FS. */
+void ftpfs_release_ftp_conn (struct ftpfs *fs, struct ftp_conn *conn);
+
+/* Return in DIR a new ftpfs directory, in the filesystem FS, with node NODE
+ and remote path RMT_PATH. RMT_PATH is *not copied*, so it shouldn't ever
+ change while this directory is active. */
+error_t ftpfs_dir_create (struct ftpfs *fs, struct node *node,
+ const char *rmt_path, struct ftpfs_dir **dir);
+
+void ftpfs_dir_free (struct ftpfs_dir *dir);
+
+/* Refresh DIR. */
+error_t ftpfs_dir_refresh (struct ftpfs_dir *dir);
+
+/* Lookup NAME in DIR, returning its entry, or an error. DIR's node should
+ be locked, and will be unlocked after returning; *NODE will contain the
+ result node, locked, and with an additional reference, or 0 if an error
+ occurs. */
+error_t ftpfs_dir_lookup (struct ftpfs_dir *dir, const char *name,
+ struct node **node);
+
+/* Lookup the null name in DIR, and return a node for it in NODE. Unlike
+ ftpfs_dir_lookup, this won't attempt to validate the existence of the
+ entry (to avoid opening a new connection if possible) -- that will happen
+ the first time the entry is refreshed. Also unlink ftpfs_dir_lookup, this
+ function doesn't expect DIR to be locked, and won't return *NODE locked.
+ This function is only used for bootstrapping the root node. */
+error_t ftpfs_dir_null_lookup (struct ftpfs_dir *dir, struct node **node);
+
+#endif /* __FTPFS_H__ */
diff --git a/ftpfs/host.c b/ftpfs/host.c
new file mode 100644
index 00000000..cd6fd4c0
--- /dev/null
+++ b/ftpfs/host.c
@@ -0,0 +1,154 @@
+/* Server lookup
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <ftpconn.h>
+
+/* Split the server-specification string SERVER into its components: a
+ hostname (returned in HOST), username (USER), and password (PASSWD). */
+static error_t
+split_server_name (const char *server, char **host, char **user, char **passwd)
+{
+ size_t plim;
+ const char *p = server, *sep;
+
+ *host = 0;
+ *user = 0;
+ *passwd = 0;
+
+ /* Extract the hostname; syntax is either `HOST:...', `...@HOST', or just
+ HOST if there are no user parameters specified. */
+ sep = strrchr (p, '@');
+ if (sep)
+ /* ...@HOST */
+ {
+ *host = strdup (sep + 1);
+ if (! *host)
+ return ENOMEM;
+ plim = sep - server;
+ }
+ else
+ {
+ sep = strchr (server, ':');
+ if (sep)
+ /* HOST:... */
+ {
+ *host = strndup (server, sep - server);
+ if (! *host)
+ return ENOMEM;
+ p = sep + 1;
+ plim = strlen (p);
+ }
+ else
+ /* Just HOST */
+ {
+ *host = strdup (server);
+ if (! *host)
+ return ENOMEM;
+ return 0;
+ }
+ }
+
+ /* Now P...P+PLIM contains any user parameters for HOST. */
+ sep = memchr (p, ':', plim);
+ if (sep)
+ /* USERNAME:PASSWD */
+ {
+ *user = strndup (p, sep - p);
+ *passwd = strndup (sep + 1, plim - (sep + 1 - p));
+ if (!*user || !*passwd)
+ {
+ if (*user)
+ free (*user);
+ if (*passwd)
+ free (*passwd);
+ free (*host);
+ return ENOMEM;
+ }
+ }
+ else
+ /* Just USERNAME */
+ {
+ *user = strndup (p, plim);
+ if (! *user)
+ free (*user);
+ }
+
+ return 0;
+}
+
+/* */
+error_t
+lookup_server (const char *server, struct ftp_conn_params **params, int *h_err)
+{
+ char hostent_data[2048]; /* XXX what size should this be???? */
+ struct hostent _he, *he;
+ char *host, *user, *passwd;
+ error_t err = split_server_name (server, &host, &user, &passwd);
+
+ if (err)
+ return err;
+
+ /* We didn't find a pre-existing host entry. Make a new one. Note that
+ since we don't lock anything while making up our new structure, another
+ thread could have inserted a duplicate entry for the same host name, but
+ this isn't really a problem, just annoying. */
+
+ if (gethostbyname_r (host, &_he, hostent_data, sizeof hostent_data,
+ &he, h_err) == 0)
+ {
+ *params = malloc (sizeof (struct ftp_conn_params));
+ if (! *params)
+ err = ENOMEM;
+ else
+ {
+ (*params)->addr = malloc (he->h_length);
+ if (! (*params)->addr)
+ {
+ free (*params);
+ err = ENOMEM;
+ }
+ else
+ {
+ bcopy (he->h_addr_list[0], (*params)->addr, he->h_length);
+ (*params)->addr_len = he->h_length;
+ (*params)->addr_type = he->h_addrtype;
+ (*params)->user = user;
+ (*params)->pass = passwd;
+ (*params)->acct = 0;
+ }
+ }
+ }
+ else
+ err = EINVAL;
+
+ free (host);
+
+ if (err)
+ {
+ free (user);
+ free (passwd);
+ }
+
+ return err;
+}
diff --git a/ftpfs/ncache.c b/ftpfs/ncache.c
new file mode 100644
index 00000000..612dc081
--- /dev/null
+++ b/ftpfs/ncache.c
@@ -0,0 +1,87 @@
+/* Node caching
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <unistd.h>
+#include <string.h>
+
+#include <hurd/netfs.h>
+
+#include "ftpfs.h"
+
+/* Remove NN's node from its position in FS's node cache. */
+static void
+node_unlink (struct node *node, struct ftpfs *fs)
+{
+ struct netnode *nn = node->nn;
+ if (nn->ncache_next)
+ nn->ncache_next->nn->ncache_prev = nn->ncache_prev;
+ if (nn->ncache_prev)
+ nn->ncache_prev->nn->ncache_next = nn->ncache_next;
+ if (fs->node_cache_mru == node)
+ fs->node_cache_mru = nn->ncache_next;
+ if (fs->node_cache_lru == node)
+ fs->node_cache_lru = nn->ncache_prev;
+ nn->ncache_next = 0;
+ nn->ncache_prev = 0;
+ fs->node_cache_len--;
+}
+
+/* Add NODE to the recently-used-node cache, which adds a reference to
+ prevent it from going away. NODE should be locked. */
+void
+ftpfs_cache_node (struct node *node)
+{
+ struct netnode *nn = node->nn;
+ struct ftpfs *fs = nn->fs;
+
+ pthread_mutex_lock (&fs->node_cache_lock);
+
+ if (fs->params.node_cache_max > 0 || fs->node_cache_len > 0)
+ {
+ if (fs->node_cache_mru != node)
+ {
+ if (nn->ncache_next || nn->ncache_prev)
+ /* Node is already in the cache. */
+ node_unlink (node, fs);
+ else
+ /* Add a reference from the cache. */
+ netfs_nref (node);
+
+ nn->ncache_next = fs->node_cache_mru;
+ nn->ncache_prev = 0;
+ if (fs->node_cache_mru)
+ fs->node_cache_mru->nn->ncache_prev = node;
+ if (! fs->node_cache_lru)
+ fs->node_cache_lru = node;
+ fs->node_cache_mru = node;
+ fs->node_cache_len++;
+ }
+
+ /* Forget the least used nodes. */
+ while (fs->node_cache_len > fs->params.node_cache_max)
+ {
+ struct node *lru = fs->node_cache_lru;
+ node_unlink (lru, fs);
+ netfs_nrele (lru);
+ }
+ }
+
+ pthread_mutex_unlock (&fs->node_cache_lock);
+}
diff --git a/ftpfs/netfs.c b/ftpfs/netfs.c
new file mode 100644
index 00000000..5359acb9
--- /dev/null
+++ b/ftpfs/netfs.c
@@ -0,0 +1,463 @@
+/* ftpfs interface to libnetfs
+
+ Copyright (C) 1997, 1998, 1999, 2001, 2007 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include <hurd/netfs.h>
+
+#include "ftpfs.h"
+#include "ccache.h"
+
+/* Attempt to create a file named NAME in DIR for USER with MODE. Set *NODE
+ to the new node upon return. On any error, clear *NODE. *NODE should be
+ locked on success; no matter what, unlock DIR before returning. */
+error_t
+netfs_attempt_create_file (struct iouser *user, struct node *dir,
+ char *name, mode_t mode, struct node **node)
+{
+ *node = 0;
+ pthread_mutex_unlock (&dir->lock);
+ return EOPNOTSUPP;
+}
+
+/* Node NODE is being opened by USER, with FLAGS. NEWNODE is nonzero if we
+ just created this node. Return an error if we should not permit the open
+ to complete because of a permission restriction. */
+error_t
+netfs_check_open_permissions (struct iouser *user, struct node *node,
+ int flags, int newnode)
+{
+ error_t err = ftpfs_refresh_node (node);
+ if (!err && (flags & O_READ))
+ err = fshelp_access (&node->nn_stat, S_IREAD, user);
+ if (!err && (flags & O_WRITE))
+ err = fshelp_access (&node->nn_stat, S_IWRITE, user);
+ if (!err && (flags & O_EXEC))
+ err = fshelp_access (&node->nn_stat, S_IEXEC, user);
+ return err;
+}
+
+/* This should attempt a utimes call for the user specified by CRED on node
+ NODE, to change the atime to ATIME and the mtime to MTIME. */
+error_t
+netfs_attempt_utimes (struct iouser *cred, struct node *node,
+ struct timespec *atime, struct timespec *mtime)
+{
+ error_t err = ftpfs_refresh_node (node);
+ int flags = TOUCH_CTIME;
+
+ if (! err)
+ err = fshelp_isowner (&node->nn_stat, cred);
+
+ if (! err)
+ {
+ if (atime)
+ node->nn_stat.st_atim = *atime;
+ else
+ flags |= TOUCH_ATIME;
+
+ if (mtime)
+ node->nn_stat.st_mtim = *mtime;
+ else
+ flags |= TOUCH_MTIME;
+
+ fshelp_touch (&node->nn_stat, flags, ftpfs_maptime);
+ }
+
+ return err;
+}
+
+/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and O_EXEC)
+ in *TYPES for file NODE and user CRED. */
+error_t
+netfs_report_access (struct iouser *cred, struct node *node, int *types)
+{
+ error_t err = ftpfs_refresh_node (node);
+
+ if (! err)
+ {
+ *types = 0;
+ if (fshelp_access (&node->nn_stat, S_IREAD, cred) == 0)
+ *types |= O_READ;
+ if (fshelp_access (&node->nn_stat, S_IWRITE, cred) == 0)
+ *types |= O_WRITE;
+ if (fshelp_access (&node->nn_stat, S_IEXEC, cred) == 0)
+ *types |= O_EXEC;
+ }
+
+ return err;
+}
+
+/* Trivial definitions. */
+
+/* Make sure that NP->nn_stat is filled with current information. CRED
+ identifies the user responsible for the operation. */
+error_t
+netfs_validate_stat (struct node *node, struct iouser *cred)
+{
+ return ftpfs_refresh_node (node);
+}
+
+/* This should sync the file NODE completely to disk, for the user CRED. If
+ WAIT is set, return only after sync is completely finished. */
+error_t
+netfs_attempt_sync (struct iouser *cred, struct node *node, int wait)
+{
+ return 0;
+}
+
+/* The granularity with which we allocate space to return our result. */
+#define DIRENTS_CHUNK_SIZE (8*1024)
+
+/* Returned directory entries are aligned to blocks this many bytes long.
+ Must be a power of two. */
+#define DIRENT_ALIGN 4
+#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name)
+
+/* Length is structure before the name + the name + '\0', all
+ padded to a four-byte alignment. */
+#define DIRENT_LEN(name_len) \
+ ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \
+ & ~(DIRENT_ALIGN - 1))
+
+/* Fetch a directory, as for netfs_get_dirents. */
+static error_t
+get_dirents (struct ftpfs_dir *dir,
+ int first_entry, int max_entries, char **data,
+ mach_msg_type_number_t *data_len,
+ vm_size_t max_data_len, int *data_entries)
+{
+ struct ftpfs_dir_entry *e;
+ error_t err = 0;
+
+ if (! dir)
+ return ENOTDIR;
+
+ e = dir->ordered;
+
+ /* Find the first entry. */
+ while (first_entry-- > 0)
+ if (! e)
+ {
+ max_entries = 0;
+ break;
+ }
+ else
+ e = e->ordered_next;
+
+ if (max_entries != 0)
+ {
+ size_t size =
+ (max_data_len == 0 || max_data_len > DIRENTS_CHUNK_SIZE
+ ? DIRENTS_CHUNK_SIZE
+ : max_data_len);
+
+ *data = mmap (0, size, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ err = ((void *) *data == (void *) -1) ? errno : 0;
+
+ if (! err)
+ {
+ char *p = *data;
+ int count = 0;
+
+ /* See how much space we need for the result. */
+ while ((max_entries == -1 || count < max_entries) && e)
+ {
+ struct dirent hdr;
+ size_t name_len = strlen (e->name);
+ size_t sz = DIRENT_LEN (name_len);
+ int entry_type =
+ e->stat_timestamp ? IFTODT (e->stat.st_mode) : DT_UNKNOWN;
+
+ if ((p - *data) + sz > size)
+ {
+ if (max_data_len > 0)
+ break;
+ else
+ /* Try to grow our return buffer. */
+ {
+ vm_address_t extension = (vm_address_t)(*data + size);
+ err = vm_allocate (mach_task_self (), &extension,
+ DIRENTS_CHUNK_SIZE, 0);
+ if (err)
+ break;
+ size += DIRENTS_CHUNK_SIZE;
+ }
+ }
+
+ hdr.d_namlen = name_len;
+ hdr.d_fileno = e->stat.st_ino;
+ hdr.d_reclen = sz;
+ hdr.d_type = entry_type;
+
+ memcpy (p, &hdr, DIRENT_NAME_OFFS);
+ strcpy (p + DIRENT_NAME_OFFS, e->name);
+ p += sz;
+
+ count++;
+ e = e->ordered_next;
+ }
+
+ if (err)
+ munmap (*data, size);
+ else
+ {
+ vm_address_t alloc_end = (vm_address_t)(*data + size);
+ vm_address_t real_end = round_page (p);
+ if (alloc_end > real_end)
+ munmap ((caddr_t) real_end, alloc_end - real_end);
+ *data_len = p - *data;
+ *data_entries = count;
+ }
+ }
+ }
+ else
+ {
+ *data_len = 0;
+ *data_entries = 0;
+ }
+
+ return err;
+}
+
+error_t
+netfs_get_dirents (struct iouser *cred, struct node *dir,
+ int first_entry, int max_entries, char **data,
+ mach_msg_type_number_t *data_len,
+ vm_size_t max_data_len, int *data_entries)
+{
+ error_t err = ftpfs_refresh_node (dir);
+
+ if (! err)
+ {
+ if (dir->nn->dir)
+ {
+ err = ftpfs_dir_refresh (dir->nn->dir);
+ if (! err)
+ err = get_dirents (dir->nn->dir, first_entry, max_entries,
+ data, data_len, max_entries, data_entries);
+ }
+ else
+ err = ENOTDIR;
+ }
+
+ return err;
+}
+
+/* Lookup NAME in DIR for USER; set *NODE to the found name upon return. If
+ the name was not found, then return ENOENT. On any error, clear *NODE.
+ (*NODE, if found, should be locked, this call should unlock DIR no matter
+ what.) */
+error_t netfs_attempt_lookup (struct iouser *user, struct node *dir,
+ char *name, struct node **node)
+{
+ error_t err = ftpfs_refresh_node (dir);
+ if (! err)
+ err = ftpfs_dir_lookup (dir->nn->dir, name, node);
+ return err;
+}
+
+/* Delete NAME in DIR for USER. */
+error_t netfs_attempt_unlink (struct iouser *user, struct node *dir,
+ char *name)
+{
+ return EROFS;
+}
+
+/* Note that in this one call, neither of the specific nodes are locked. */
+error_t netfs_attempt_rename (struct iouser *user, struct node *fromdir,
+ char *fromname, struct node *todir,
+ char *toname, int excl)
+{
+ return EROFS;
+}
+
+/* Attempt to create a new directory named NAME in DIR for USER with mode
+ MODE. */
+error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir,
+ char *name, mode_t mode)
+{
+ return EROFS;
+}
+
+/* Attempt to remove directory named NAME in DIR for USER. */
+error_t netfs_attempt_rmdir (struct iouser *user,
+ struct node *dir, char *name)
+{
+ return EROFS;
+}
+
+/* This should attempt a chmod call for the user specified by CRED on node
+ NODE, to change the owner to UID and the group to GID. */
+error_t netfs_attempt_chown (struct iouser *cred, struct node *node,
+ uid_t uid, uid_t gid)
+{
+ return EROFS;
+}
+
+/* This should attempt a chauthor call for the user specified by CRED on node
+ NODE, to change the author to AUTHOR. */
+error_t netfs_attempt_chauthor (struct iouser *cred, struct node *node,
+ uid_t author)
+{
+ return EROFS;
+}
+
+/* This should attempt a chmod call for the user specified by CRED on node
+ NODE, to change the mode to MODE. Unlike the normal Unix and Hurd meaning
+ of chmod, this function is also used to attempt to change files into other
+ types. If such a transition is attempted which is impossible, then return
+ EOPNOTSUPP. */
+error_t netfs_attempt_chmod (struct iouser *cred, struct node *node,
+ mode_t mode)
+{
+ return EROFS;
+}
+
+/* Attempt to turn NODE (user CRED) into a symlink with target NAME. */
+error_t netfs_attempt_mksymlink (struct iouser *cred, struct node *node,
+ char *name)
+{
+ return EROFS;
+}
+
+/* Attempt to turn NODE (user CRED) into a device. TYPE is either S_IFBLK or
+ S_IFCHR. */
+error_t netfs_attempt_mkdev (struct iouser *cred, struct node *node,
+ mode_t type, dev_t indexes)
+{
+ return EROFS;
+}
+
+/* Attempt to set the passive translator record for FILE to ARGZ (of length
+ ARGZLEN) for user CRED. */
+error_t netfs_set_translator (struct iouser *cred, struct node *node,
+ char *argz, size_t argzlen)
+{
+ return EROFS;
+}
+
+/* This should attempt a chflags call for the user specified by CRED on node
+ NODE, to change the flags to FLAGS. */
+error_t netfs_attempt_chflags (struct iouser *cred, struct node *node,
+ int flags)
+{
+ return EROFS;
+}
+
+/* This should attempt to set the size of the file NODE (for user CRED) to
+ SIZE bytes long. */
+error_t netfs_attempt_set_size (struct iouser *cred, struct node *node,
+ off_t size)
+{
+ return EROFS;
+}
+
+/* This should attempt to fetch filesystem status information for the remote
+ filesystem, for the user CRED. */
+error_t
+netfs_attempt_statfs (struct iouser *cred, struct node *node,
+ struct statfs *st)
+{
+ bzero (st, sizeof *st);
+ st->f_type = FSTYPE_FTP;
+ st->f_fsid = getpid ();
+ return 0;
+}
+
+/* This should sync the entire remote filesystem. If WAIT is set, return
+ only after sync is completely finished. */
+error_t netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+ return 0;
+}
+
+/* Create a link in DIR with name NAME to FILE for USER. Note that neither
+ DIR nor FILE are locked. If EXCL is set, do not delete the target, but
+ return EEXIST if NAME is already found in DIR. */
+error_t netfs_attempt_link (struct iouser *user, struct node *dir,
+ struct node *file, char *name, int excl)
+{
+ return EROFS;
+}
+
+/* Attempt to create an anonymous file related to DIR for USER with MODE.
+ Set *NODE to the returned file upon success. No matter what, unlock DIR. */
+error_t netfs_attempt_mkfile (struct iouser *user, struct node *dir,
+ mode_t mode, struct node **node)
+{
+ *node = 0;
+ pthread_mutex_unlock (&dir->lock);
+ return EROFS;
+}
+
+/* Read the contents of NODE (a symlink), for USER, into BUF. */
+error_t netfs_attempt_readlink (struct iouser *user, struct node *node, char *buf)
+{
+ error_t err = ftpfs_refresh_node (node);
+ if (! err)
+ {
+ struct ftpfs_dir_entry *e = node->nn->dir_entry;
+ if (e)
+ bcopy (e->symlink_target, buf, node->nn_stat.st_size);
+ else
+ err = EINVAL;
+ }
+ return err;
+}
+
+/* Read from the file NODE for user CRED starting at OFFSET and continuing for
+ up to *LEN bytes. Put the data at DATA. Set *LEN to the amount
+ successfully read upon return. */
+error_t netfs_attempt_read (struct iouser *cred, struct node *node,
+ off_t offset, size_t *len, void *data)
+{
+ error_t err = 0;
+
+ if (! node->nn->contents)
+ err = ccache_create (node, &node->nn->contents);
+ if (! err)
+ {
+ if (*len > node->nn_stat.st_size - offset)
+ *len = node->nn_stat.st_size - offset;
+ if (*len > 0)
+ err = ccache_read (node->nn->contents, offset, *len, data);
+ }
+
+ return err;
+}
+
+/* Write to the file NODE for user CRED starting at OFSET and continuing for up
+ to *LEN bytes from DATA. Set *LEN to the amount seccessfully written upon
+ return. */
+error_t netfs_attempt_write (struct iouser *cred, struct node *node,
+ off_t offset, size_t *len, void *data)
+{
+ return EROFS;
+}
diff --git a/ftpfs/node.c b/ftpfs/node.c
new file mode 100644
index 00000000..74cd402e
--- /dev/null
+++ b/ftpfs/node.c
@@ -0,0 +1,114 @@
+/* General fs node functions
+
+ Copyright (C) 1997, 2006 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <fcntl.h>
+#include <string.h>
+
+#include <hurd/ihash.h>
+#include <hurd/fshelp.h>
+#include <hurd/iohelp.h>
+#include <hurd/netfs.h>
+
+#include "ftpfs.h"
+#include "ccache.h"
+
+/* Node maintenance. */
+
+/* Return a new node in NODE, with a name NAME, and return the new node
+ with a single reference in NODE. E may be 0, if this is the root node. */
+error_t
+ftpfs_create_node (struct ftpfs_dir_entry *e, const char *rmt_path,
+ struct node **node)
+{
+ struct node *new;
+ struct netnode *nn = malloc (sizeof (struct netnode));
+ error_t err;
+
+ if (! nn)
+ return ENOMEM;
+
+ nn->fs = e->dir->fs;
+ nn->dir_entry = e;
+ nn->contents = 0;
+ nn->dir = 0;
+ nn->rmt_path = strdup (rmt_path);
+ nn->ncache_next = nn->ncache_prev = 0;
+
+ new = netfs_make_node (nn);
+ if (! new)
+ {
+ free (nn);
+ return ENOMEM;
+ }
+
+ fshelp_touch (&new->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
+ ftpfs_maptime);
+
+ pthread_spin_lock (&nn->fs->inode_mappings_lock);
+ err = hurd_ihash_add (&nn->fs->inode_mappings, e->stat.st_ino, e);
+ pthread_spin_unlock (&nn->fs->inode_mappings_lock);
+
+ if (err)
+ {
+ free (nn);
+ free (new);
+ return err;
+ }
+
+ e->node = new;
+ *node = new;
+
+ return 0;
+}
+
+/* Node NP is all done; free all its associated storage. */
+void
+netfs_node_norefs (struct node *node)
+{
+ struct netnode *nn = node->nn;
+
+ /* Ftpfs_detach_node does ref count frobbing (of other nodes), so we have
+ to unlock NETFS_NODE_REFCNT_LOCK during it. */
+ node->references++;
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+
+ /* Remove NODE from any entry it is attached to. */
+ ftpfs_detach_node (node);
+
+ if (nn->dir)
+ {
+ assert (nn->dir->num_live_entries == 0);
+ ftpfs_dir_free (nn->dir);
+ }
+
+ /* Remove this entry from the set of known inodes. */
+ pthread_spin_lock (&nn->fs->inode_mappings_lock);
+ hurd_ihash_locp_remove (&nn->fs->inode_mappings, nn->dir_entry->inode_locp);
+ pthread_spin_unlock (&nn->fs->inode_mappings_lock);
+
+ if (nn->contents)
+ ccache_free (nn->contents);
+
+ free (nn);
+ free (node);
+
+ /* Caller expects us to leave this locked... */
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+}
diff --git a/gitlog-to-changelog b/gitlog-to-changelog
new file mode 100755
index 00000000..e02d34c2
--- /dev/null
+++ b/gitlog-to-changelog
@@ -0,0 +1,432 @@
+eval '(exit $?0)' && eval 'exec perl -wS "$0" ${1+"$@"}'
+ & eval 'exec perl -wS "$0" $argv:q'
+ if 0;
+# Convert git log output to ChangeLog format.
+
+my $VERSION = '2012-07-29 06:11'; # UTC
+# The definition above must lie within the first 8 lines in order
+# for the Emacs time-stamp write hook (at end) to update it.
+# If you change this file with Emacs, please let the write hook
+# do its job. Otherwise, update this string manually.
+
+# Copyright (C) 2008-2013 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Written by Jim Meyering
+
+use strict;
+use warnings;
+use Getopt::Long;
+use POSIX qw(strftime);
+
+(my $ME = $0) =~ s|.*/||;
+
+# use File::Coda; # http://meyering.net/code/Coda/
+END {
+ defined fileno STDOUT or return;
+ close STDOUT and return;
+ warn "$ME: failed to close standard output: $!\n";
+ $? ||= 1;
+}
+
+sub usage ($)
+{
+ my ($exit_code) = @_;
+ my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
+ if ($exit_code != 0)
+ {
+ print $STREAM "Try '$ME --help' for more information.\n";
+ }
+ else
+ {
+ print $STREAM <<EOF;
+Usage: $ME [OPTIONS] [ARGS]
+
+Convert git log output to ChangeLog format. If present, any ARGS
+are passed to "git log". To avoid ARGS being parsed as options to
+$ME, they may be preceded by '--'.
+
+OPTIONS:
+
+ --amend=FILE FILE maps from an SHA1 to perl code (i.e., s/old/new/) that
+ makes a change to SHA1's commit log text or metadata.
+ --append-dot append a dot to the first line of each commit message if
+ there is no other punctuation or blank at the end.
+ --no-cluster never cluster commit messages under the same date/author
+ header; the default is to cluster adjacent commit messages
+ if their headers are the same and neither commit message
+ contains multiple paragraphs.
+ --srcdir=DIR the root of the source tree, from which the .git/
+ directory can be derived.
+ --since=DATE convert only the logs since DATE;
+ the default is to convert all log entries.
+ --format=FMT set format string for commit subject and body;
+ see 'man git-log' for the list of format metacharacters;
+ the default is '%s%n%b%n'
+ --strip-tab remove one additional leading TAB from commit message lines.
+ --strip-cherry-pick remove data inserted by "git cherry-pick";
+ this includes the "cherry picked from commit ..." line,
+ and the possible final "Conflicts:" paragraph.
+ --help display this help and exit
+ --version output version information and exit
+
+EXAMPLE:
+
+ $ME --since=2008-01-01 > ChangeLog
+ $ME -- -n 5 foo > last-5-commits-to-branch-foo
+
+SPECIAL SYNTAX:
+
+The following types of strings are interpreted specially when they appear
+at the beginning of a log message line. They are not copied to the output.
+
+ Copyright-paperwork-exempt: Yes
+ Append the "(tiny change)" notation to the usual "date name email"
+ ChangeLog header to mark a change that does not require a copyright
+ assignment.
+ Co-authored-by: Joe User <user\@example.com>
+ List the specified name and email address on a second
+ ChangeLog header, denoting a co-author.
+ Signed-off-by: Joe User <user\@example.com>
+ These lines are simply elided.
+
+In a FILE specified via --amend, comment lines (starting with "#") are ignored.
+FILE must consist of <SHA,CODE+> pairs where SHA is a 40-byte SHA1 (alone on
+a line) referring to a commit in the current project, and CODE refers to one
+or more consecutive lines of Perl code. Pairs must be separated by one or
+more blank line.
+
+Here is sample input for use with --amend=FILE, from coreutils:
+
+3a169f4c5d9159283548178668d2fae6fced3030
+# fix typo in title:
+s/all tile types/all file types/
+
+1379ed974f1fa39b12e2ffab18b3f7a607082202
+# Due to a bug in vc-dwim, I mis-attributed a patch by Paul to myself.
+# Change the author to be Paul. Note the escaped "@":
+s,Jim .*>,Paul Eggert <eggert\\\@cs.ucla.edu>,
+
+EOF
+ }
+ exit $exit_code;
+}
+
+# If the string $S is a well-behaved file name, simply return it.
+# If it contains white space, quotes, etc., quote it, and return the new string.
+sub shell_quote($)
+{
+ my ($s) = @_;
+ if ($s =~ m![^\w+/.,-]!)
+ {
+ # Convert each single quote to '\''
+ $s =~ s/\'/\'\\\'\'/g;
+ # Then single quote the string.
+ $s = "'$s'";
+ }
+ return $s;
+}
+
+sub quoted_cmd(@)
+{
+ return join (' ', map {shell_quote $_} @_);
+}
+
+# Parse file F.
+# Comment lines (starting with "#") are ignored.
+# F must consist of <SHA,CODE+> pairs where SHA is a 40-byte SHA1
+# (alone on a line) referring to a commit in the current project, and
+# CODE refers to one or more consecutive lines of Perl code.
+# Pairs must be separated by one or more blank line.
+sub parse_amend_file($)
+{
+ my ($f) = @_;
+
+ open F, '<', $f
+ or die "$ME: $f: failed to open for reading: $!\n";
+
+ my $fail;
+ my $h = {};
+ my $in_code = 0;
+ my $sha;
+ while (defined (my $line = <F>))
+ {
+ $line =~ /^\#/
+ and next;
+ chomp $line;
+ $line eq ''
+ and $in_code = 0, next;
+
+ if (!$in_code)
+ {
+ $line =~ /^([0-9a-fA-F]{40})$/
+ or (warn "$ME: $f:$.: invalid line; expected an SHA1\n"),
+ $fail = 1, next;
+ $sha = lc $1;
+ $in_code = 1;
+ exists $h->{$sha}
+ and (warn "$ME: $f:$.: duplicate SHA1\n"),
+ $fail = 1, next;
+ }
+ else
+ {
+ $h->{$sha} ||= '';
+ $h->{$sha} .= "$line\n";
+ }
+ }
+ close F;
+
+ $fail
+ and exit 1;
+
+ return $h;
+}
+
+# git_dir_option $SRCDIR
+#
+# From $SRCDIR, the --git-dir option to pass to git (none if $SRCDIR
+# is undef). Return as a list (0 or 1 element).
+sub git_dir_option($)
+{
+ my ($srcdir) = @_;
+ my @res = ();
+ if (defined $srcdir)
+ {
+ my $qdir = shell_quote $srcdir;
+ my $cmd = "cd $qdir && git rev-parse --show-toplevel";
+ my $qcmd = shell_quote $cmd;
+ my $git_dir = qx($cmd);
+ defined $git_dir
+ or die "$ME: cannot run $qcmd: $!\n";
+ $? == 0
+ or die "$ME: $qcmd had unexpected exit code or signal ($?)\n";
+ chomp $git_dir;
+ push @res, "--git-dir=$git_dir/.git";
+ }
+ @res;
+}
+
+{
+ my $since_date;
+ my $format_string = '%s%n%b%n';
+ my $amend_file;
+ my $append_dot = 0;
+ my $cluster = 1;
+ my $strip_tab = 0;
+ my $strip_cherry_pick = 0;
+ my $srcdir;
+ GetOptions
+ (
+ help => sub { usage 0 },
+ version => sub { print "$ME version $VERSION\n"; exit },
+ 'since=s' => \$since_date,
+ 'format=s' => \$format_string,
+ 'amend=s' => \$amend_file,
+ 'append-dot' => \$append_dot,
+ 'cluster!' => \$cluster,
+ 'strip-tab' => \$strip_tab,
+ 'strip-cherry-pick' => \$strip_cherry_pick,
+ 'srcdir=s' => \$srcdir,
+ ) or usage 1;
+
+ defined $since_date
+ and unshift @ARGV, "--since=$since_date";
+
+ # This is a hash that maps an SHA1 to perl code (i.e., s/old/new/)
+ # that makes a correction in the log or attribution of that commit.
+ my $amend_code = defined $amend_file ? parse_amend_file $amend_file : {};
+
+ my @cmd = ('git',
+ git_dir_option $srcdir,
+ qw(log --log-size),
+ '--pretty=format:%H:%ct %an <%ae>%n%n'.$format_string, @ARGV);
+ open PIPE, '-|', @cmd
+ or die ("$ME: failed to run '". quoted_cmd (@cmd) ."': $!\n"
+ . "(Is your Git too old? Version 1.5.1 or later is required.)\n");
+
+ my $prev_multi_paragraph;
+ my $prev_date_line = '';
+ my @prev_coauthors = ();
+ while (1)
+ {
+ defined (my $in = <PIPE>)
+ or last;
+ $in =~ /^log size (\d+)$/
+ or die "$ME:$.: Invalid line (expected log size):\n$in";
+ my $log_nbytes = $1;
+
+ my $log;
+ my $n_read = read PIPE, $log, $log_nbytes;
+ $n_read == $log_nbytes
+ or die "$ME:$.: unexpected EOF\n";
+
+ # Extract leading hash.
+ my ($sha, $rest) = split ':', $log, 2;
+ defined $sha
+ or die "$ME:$.: malformed log entry\n";
+ $sha =~ /^[0-9a-fA-F]{40}$/
+ or die "$ME:$.: invalid SHA1: $sha\n";
+
+ # If this commit's log requires any transformation, do it now.
+ my $code = $amend_code->{$sha};
+ if (defined $code)
+ {
+ eval 'use Safe';
+ my $s = new Safe;
+ # Put the unpreprocessed entry into "$_".
+ $_ = $rest;
+
+ # Let $code operate on it, safely.
+ my $r = $s->reval("$code")
+ or die "$ME:$.:$sha: failed to eval \"$code\":\n$@\n";
+
+ # Note that we've used this entry.
+ delete $amend_code->{$sha};
+
+ # Update $rest upon success.
+ $rest = $_;
+ }
+
+ # Remove lines inserted by "git cherry-pick".
+ if ($strip_cherry_pick)
+ {
+ $rest =~ s/^\s*Conflicts:\n.*//sm;
+ $rest =~ s/^\s*\(cherry picked from commit [\da-f]+\)\n//m;
+ }
+
+ my @line = split "\n", $rest;
+ my $author_line = shift @line;
+ defined $author_line
+ or die "$ME:$.: unexpected EOF\n";
+ $author_line =~ /^(\d+) (.*>)$/
+ or die "$ME:$.: Invalid line "
+ . "(expected date/author/email):\n$author_line\n";
+
+ # Format 'Copyright-paperwork-exempt: Yes' as a standard ChangeLog
+ # `(tiny change)' annotation.
+ my $tiny = (grep (/^Copyright-paperwork-exempt:\s+[Yy]es$/, @line)
+ ? ' (tiny change)' : '');
+
+ my $date_line = sprintf "%s %s$tiny\n",
+ strftime ("%F", localtime ($1)), $2;
+
+ my @coauthors = grep /^Co-authored-by:.*$/, @line;
+ # Omit meta-data lines we've already interpreted.
+ @line = grep !/^(?:Signed-off-by:[ ].*>$
+ |Co-authored-by:[ ]
+ |Copyright-paperwork-exempt:[ ]
+ )/x, @line;
+
+ # Remove leading and trailing blank lines.
+ if (@line)
+ {
+ while ($line[0] =~ /^\s*$/) { shift @line; }
+ while ($line[$#line] =~ /^\s*$/) { pop @line; }
+ }
+
+ # Record whether there are two or more paragraphs.
+ my $multi_paragraph = grep /^\s*$/, @line;
+
+ # Format 'Co-authored-by: A U Thor <email@example.com>' lines in
+ # standard multi-author ChangeLog format.
+ for (@coauthors)
+ {
+ s/^Co-authored-by:\s*/\t /;
+ s/\s*</ </;
+
+ /<.*?@.*\..*>/
+ or warn "$ME: warning: missing email address for "
+ . substr ($_, 5) . "\n";
+ }
+
+ # If clustering of commit messages has been disabled, if this header
+ # would be different from the previous date/name/email/coauthors header,
+ # or if this or the previous entry consists of two or more paragraphs,
+ # then print the header.
+ if ( ! $cluster
+ || $date_line ne $prev_date_line
+ || "@coauthors" ne "@prev_coauthors"
+ || $multi_paragraph
+ || $prev_multi_paragraph)
+ {
+ $prev_date_line eq ''
+ or print "\n";
+ print $date_line;
+ @coauthors
+ and print join ("\n", @coauthors), "\n";
+ }
+ $prev_date_line = $date_line;
+ @prev_coauthors = @coauthors;
+ $prev_multi_paragraph = $multi_paragraph;
+
+ # If there were any lines
+ if (@line == 0)
+ {
+ warn "$ME: warning: empty commit message:\n $date_line\n";
+ }
+ else
+ {
+ if ($append_dot)
+ {
+ # If the first line of the message has enough room, then
+ if (length $line[0] < 72)
+ {
+ # append a dot if there is no other punctuation or blank
+ # at the end.
+ $line[0] =~ /[[:punct:]\s]$/
+ or $line[0] .= '.';
+ }
+ }
+
+ # Remove one additional leading TAB from each line.
+ $strip_tab
+ and map { s/^\t// } @line;
+
+ # Prefix each non-empty line with a TAB.
+ @line = map { length $_ ? "\t$_" : '' } @line;
+
+ print "\n", join ("\n", @line), "\n";
+ }
+
+ defined ($in = <PIPE>)
+ or last;
+ $in ne "\n"
+ and die "$ME:$.: unexpected line:\n$in";
+ }
+
+ close PIPE
+ or die "$ME: error closing pipe from " . quoted_cmd (@cmd) . "\n";
+ # FIXME-someday: include $PROCESS_STATUS in the diagnostic
+
+ # Complain about any unused entry in the --amend=F specified file.
+ my $fail = 0;
+ foreach my $sha (keys %$amend_code)
+ {
+ warn "$ME:$amend_file: unused entry: $sha\n";
+ $fail = 1;
+ }
+
+ exit $fail;
+}
+
+# Local Variables:
+# mode: perl
+# indent-tabs-mode: nil
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "my $VERSION = '"
+# time-stamp-format: "%:y-%02m-%02d %02H:%02M"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "'; # UTC"
+# End:
diff --git a/hostmux/Makefile b/hostmux/Makefile
new file mode 100644
index 00000000..939a9f62
--- /dev/null
+++ b/hostmux/Makefile
@@ -0,0 +1,30 @@
+# Makefile for hostmux
+#
+# Copyright (C) 1997, 1999, 2000, 2012 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := hostmux
+makemode := server
+
+target = hostmux
+
+SRCS = hostmux.c mux.c leaf.c node.c stubs.c
+
+OBJS = $(SRCS:.c=.o)
+HURDLIBS = netfs fshelp iohelp ports ihash shouldbeinlibc
+OTHERLIBS = -lpthread
+
+include ../Makeconf
diff --git a/hostmux/hostmux-xinl.c b/hostmux/hostmux-xinl.c
new file mode 100644
index 00000000..4e11968e
--- /dev/null
+++ b/hostmux/hostmux-xinl.c
@@ -0,0 +1,22 @@
+/* Real definitions for extern inline functions in hostmux.h
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#define HOSTMUX_DEFINE_EI
+#include "hostmux.h"
diff --git a/hostmux/hostmux.c b/hostmux/hostmux.c
new file mode 100644
index 00000000..5296527b
--- /dev/null
+++ b/hostmux/hostmux.c
@@ -0,0 +1,174 @@
+/* Multiplexing filesystems by host
+
+ Copyright (C) 1997, 2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <unistd.h>
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <sys/time.h>
+
+#include <version.h>
+
+#include "hostmux.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (hostmux);
+
+char *netfs_server_name = "hostmux";
+char *netfs_server_version = HURD_VERSION;
+int netfs_maxsymlinks = 25;
+
+volatile struct mapped_time_value *hostmux_mapped_time;
+
+#define DEFAULT_HOST_PAT "${host}"
+
+/* Startup options. */
+static const struct argp_option options[] =
+{
+ { "host-pattern", 'H', "PAT", 0,
+ "The string to replace in the translator specification with the hostname;"
+ " if empty, or doesn't occur, the hostname is appended as additional"
+ " argument instead (default `" DEFAULT_HOST_PAT "')" },
+ { "canonicalize", 'C', 0, 0,
+ "Canonicalize hostname before passing it to TRANSLATOR, aliases will"
+ " show up as symbolic links to the canonicalized entry" },
+ { 0 }
+};
+static const char args_doc[] = "TRANSLATOR [ARG...]";
+static const char doc[] =
+ "A translator for invoking host-specific translators."
+ "\vThis translator appears like a directory in which hostnames can be"
+ " looked up, and will start TRANSLATOR to service each resulting node.";
+
+/* Return an argz string describing the current options. Fill *ARGZ
+ with a pointer to newly malloced storage holding the list and *LEN
+ to the length of that storage. */
+error_t
+netfs_append_args (char **argz, size_t *argz_len)
+{
+ char buf[80];
+ error_t err = 0;
+ struct hostmux *mux = netfs_root_node->nn->mux;
+
+#define FOPT(fmt, arg) \
+ do { \
+ if (! err) \
+ { \
+ snprintf (buf, sizeof buf, fmt, arg); \
+ err = argz_add (argz, argz_len, buf); \
+ } \
+ } while (0)
+
+ if (strcmp (mux->host_pat, DEFAULT_HOST_PAT) != 0)
+ FOPT ("--host-pattern=%s", mux->host_pat);
+
+ if (! err)
+ err = argz_append (argz, argz_len,
+ mux->trans_template, mux->trans_template_len);
+
+ return err;
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ struct stat ul_stat;
+ mach_port_t bootstrap;
+ struct hostmux mux = { host_pat: DEFAULT_HOST_PAT, next_fileno: 10 };
+ struct netnode root_nn = { mux: &mux };
+
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'H':
+ mux.host_pat = arg; break;
+ case 'C':
+ mux.canonicalize = 1; break;
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ case ARGP_KEY_ARGS:
+ /* Steal the entire tail of arg vector for our own use. */
+ return argz_create (state->argv + state->next,
+ &mux.trans_template, &mux.trans_template_len);
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = { options, parse_opt, args_doc, doc };
+
+ /* Parse our command line arguments. */
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ netfs_init ();
+
+ /* Create the root node (some attributes initialized below). */
+ netfs_root_node = netfs_make_node (&root_nn);
+ if (! netfs_root_node)
+ error (5, ENOMEM, "Cannot create root node");
+
+ err = maptime_map (0, 0, &hostmux_maptime);
+ if (err)
+ error (6, err, "Cannot map time");
+
+ /* Handshake with the party trying to start the translator. */
+ mux.underlying = netfs_startup (bootstrap, 0);
+
+ /* We inherit various attributes from the node underlying this translator. */
+ err = io_stat (mux.underlying, &ul_stat);
+ if (err)
+ error (7, err, "Cannot stat underlying node");
+
+ /* MUX.stat_template contains some fields that are inherited by all nodes
+ we create. */
+ mux.stat_template.st_uid = ul_stat.st_uid;
+ mux.stat_template.st_gid = ul_stat.st_gid;
+ mux.stat_template.st_author = ul_stat.st_author;
+ mux.stat_template.st_fsid = getpid ();
+ mux.stat_template.st_nlink = 1;
+ mux.stat_template.st_fstype = FSTYPE_MISC;
+
+ /* Initialize the root node's stat information. */
+ netfs_root_node->nn_stat = mux.stat_template;
+ netfs_root_node->nn_stat.st_ino = 2;
+ netfs_root_node->nn_stat.st_mode =
+ S_IFDIR | (ul_stat.st_mode & ~S_IFMT & ~S_ITRANS);
+ netfs_root_node->nn_translated = 0;
+
+ /* If the underlying node isn't a directory, propagate read permission to
+ execute permission since we need that for lookups. */
+ if (! S_ISDIR (ul_stat.st_mode))
+ {
+ if (ul_stat.st_mode & S_IRUSR)
+ netfs_root_node->nn_stat.st_mode |= S_IXUSR;
+ if (ul_stat.st_mode & S_IRGRP)
+ netfs_root_node->nn_stat.st_mode |= S_IXGRP;
+ if (ul_stat.st_mode & S_IROTH)
+ netfs_root_node->nn_stat.st_mode |= S_IXOTH;
+ }
+
+ fshelp_touch (&netfs_root_node->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
+ hostmux_maptime);
+
+ for (;;) /* ?? */
+ netfs_server_loop ();
+}
diff --git a/hostmux/hostmux.h b/hostmux/hostmux.h
new file mode 100644
index 00000000..4f971473
--- /dev/null
+++ b/hostmux/hostmux.h
@@ -0,0 +1,99 @@
+/* Multiplexing filesystems by host
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef __HOSTMUX_H__
+#define __HOSTMUX_H__
+
+#include <hurd/netfs.h>
+#include <pthread.h>
+#include <maptime.h>
+#include <features.h>
+
+#ifdef HOSTMUX_DEFINE_EI
+#define HOSTMUX_EI
+#else
+#define HOSTMUX_EI __extern_inline
+#endif
+
+/* Handy source of time. */
+volatile struct mapped_time_value *hostmux_maptime;
+
+/* The state associated with a host multiplexer translator. */
+struct hostmux
+{
+ /* The host hodes in this mux. */
+ struct hostmux_name *names;
+ pthread_rwlock_t names_lock;
+
+ /* The next inode number we'll use; protected by NAMES_LOCK. */
+ ino_t next_fileno;
+
+ /* A template argz, which is used to start each host-specific translator
+ with the host name appropriately added. */
+ char *trans_template;
+ size_t trans_template_len;
+
+ /* What string to replace in TRANS_TEMPLATE with the name of the host; if
+ 0, or it doesn't occur, the host name is appended as an additional
+ argument. */
+ char *host_pat;
+
+ /* Whether we should canonicalize host names or not. */
+ boolean_t canonicalize;
+
+ /* Constant fields for host stat entries. */
+ struct stat stat_template;
+
+ /* The file that this translator is sitting on top of; we inherit various
+ characteristics from it. */
+ file_t underlying;
+};
+
+/* The name of a recently looked up host entry. */
+struct hostmux_name
+{
+ const char *name; /* Looked up name (may be a number). */
+ const char *canon; /* The canonical (fq) host name. */
+
+ /* A filesystem node associated with NAME. If canonicalize is 0 or
+ NAME = CANON, then this will refer to a node with a translator for that
+ host, otherwise, the node will be a symbolic link to the canonical name.
+ */
+ struct node *node;
+
+ ino_t fileno; /* The inode number for this entry. */
+
+ struct hostmux_name *next;
+};
+
+/* The fs specific storage that libnetfs associates with each filesystem
+ node. */
+struct netnode
+{
+ /* The mux this node belongs to (the node can either be the mux root, or
+ one of the hosts served by it). */
+ struct hostmux *mux;
+
+ /* For mux nodes, 0, and for leaf nodes, the name under which the node was
+ looked up. */
+ struct hostmux_name *name;
+};
+
+#endif /* __HOSTMUX_H__ */
diff --git a/hostmux/leaf.c b/hostmux/leaf.c
new file mode 100644
index 00000000..fb53622f
--- /dev/null
+++ b/hostmux/leaf.c
@@ -0,0 +1,124 @@
+/* Hostmux leaf node functions
+
+ Copyright (C) 1997,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+#include <argz.h>
+
+#include "hostmux.h"
+
+/* Read the contents of NODE (a symlink), for USER, into BUF. */
+error_t
+netfs_attempt_readlink (struct iouser *user, struct node *node, char *buf)
+{
+ assert (node->nn->name);
+ memcpy (buf, node->nn->name->canon, node->nn_stat.st_size);
+ fshelp_touch (&node->nn_stat, TOUCH_ATIME, hostmux_maptime);
+ return 0;
+}
+
+/* For locked node NODE with S_IPTRANS set in its mode, look up the name of
+ its translator. Store the name into newly malloced storage, and return it
+ in *ARGZ; set *ARGZ_LEN to the total length.
+
+ For hostmux, this creates a new translator string by instantiating the
+ global translator template. */
+error_t
+netfs_get_translator (struct node *node, char **argz, size_t *argz_len)
+{
+ if (! node->nn->name)
+ return EINVAL;
+ else
+ {
+ error_t err = 0;
+ unsigned replace_count = 0;
+ struct hostmux *mux = node->nn->mux;
+
+ *argz = 0; /* Initialize return value. */
+ *argz_len = 0;
+
+ /* Return a copy of MUX's translator template, with occurrences of
+ HOST_PAT replaced by the canonical hostname. */
+ err = argz_append (argz, argz_len,
+ mux->trans_template, mux->trans_template_len);
+ if (! err)
+ err = argz_replace (argz, argz_len,
+ mux->host_pat, node->nn->name->canon,
+ &replace_count);
+
+ if (!err && replace_count == 0)
+ /* Default, if no instances of HOST_PAT occur, is to append the
+ hostname. */
+ err = argz_add (argz, argz_len, node->nn->name->canon);
+
+ if (err && *argz_len > 0)
+ free (*argz);
+
+ return err;
+ }
+}
+
+/* Create a new leaf node in MUX, with a name NAME, and return the new node
+ with a single reference in NODE. */
+error_t
+create_host_node (struct hostmux *mux, struct hostmux_name *name,
+ struct node **node)
+{
+ struct node *new;
+ struct netnode *nn = malloc (sizeof (struct netnode));
+
+ if (! nn)
+ return ENOMEM;
+
+ nn->mux = mux;
+ nn->name = name;
+
+ new = netfs_make_node (nn);
+ if (! new)
+ {
+ free (nn);
+ return ENOMEM;
+ }
+
+ new->nn_stat = mux->stat_template;
+ new->nn_stat.st_ino = name->fileno;
+
+ if (strcmp (name->name, name->canon) == 0)
+ /* The real name of the host, make a real node. */
+ {
+ new->nn_stat.st_mode = (S_IFREG | S_IPTRANS | 0666);
+ new->nn_stat.st_size = 0;
+ }
+ else
+ /* An alias for this host, make a symlink instead. */
+ {
+ new->nn_stat.st_mode = (S_IFLNK | 0666);
+ new->nn_stat.st_size = strlen (name->canon);
+ }
+ new->nn_translated = new->nn_stat.st_mode;
+
+ fshelp_touch (&new->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
+ hostmux_maptime);
+
+ name->node = new;
+
+ *node = new;
+
+ return 0;
+}
diff --git a/hostmux/mux.c b/hostmux/mux.c
new file mode 100644
index 00000000..81d3961f
--- /dev/null
+++ b/hostmux/mux.c
@@ -0,0 +1,462 @@
+/* Root hostmux node
+
+ Copyright (C) 1997,99,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stddef.h>
+#include <string.h>
+#include <dirent.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/mman.h>
+
+#include "hostmux.h"
+
+error_t create_host_node (struct hostmux *mux, struct hostmux_name *name,
+ struct node **node);
+
+/* Returned directory entries are aligned to blocks this many bytes long.
+ Must be a power of two. */
+#define DIRENT_ALIGN 4
+#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name)
+
+/* Length is structure before the name + the name + '\0', all
+ padded to a four-byte alignment. */
+#define DIRENT_LEN(name_len) \
+ ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \
+ & ~(DIRENT_ALIGN - 1))
+
+static error_t lookup_host (struct hostmux *mux, const char *host,
+ struct node **node); /* fwd decl */
+
+/* [root] Directory operations. */
+
+/* Lookup NAME in DIR for USER; set *NODE to the found name upon return. If
+ the name was not found, then return ENOENT. On any error, clear *NODE.
+ (*NODE, if found, should be locked, this call should unlock DIR no matter
+ what.) */
+error_t
+netfs_attempt_lookup (struct iouser *user, struct node *dir,
+ char *name, struct node **node)
+{
+ error_t err;
+
+ if (dir->nn->name)
+ err = ENOTDIR;
+ else
+ err = fshelp_access (&dir->nn_stat, S_IEXEC, user);
+
+ if (! err)
+ {
+ if (strcmp (name, ".") == 0)
+ /* Current directory -- just add an additional reference to DIR and
+ return it. */
+ {
+ netfs_nref (dir);
+ *node = dir;
+ err = 0;
+ }
+ else if (strcmp (name, "..") == 0)
+ err = EAGAIN;
+ else
+ err = lookup_host (dir->nn->mux, name, node);
+
+ fshelp_touch (&dir->nn_stat, TOUCH_ATIME, hostmux_maptime);
+ }
+
+ pthread_mutex_unlock (&dir->lock);
+ if (err)
+ *node = 0;
+ else
+ pthread_mutex_lock (&(*node)->lock);
+
+ return err;
+}
+
+/* Implement the netfs_get_directs callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_get_dirents (struct iouser *cred, struct node *dir,
+ int first_entry, int num_entries, char **data,
+ mach_msg_type_number_t *data_len,
+ vm_size_t max_data_len, int *data_entries)
+{
+ error_t err;
+ int count;
+ size_t size = 0; /* Total size of our return block. */
+ struct hostmux_name *first_name, *nm;
+
+ /* Add the length of a directory entry for NAME to SIZE and return true,
+ unless it would overflow MAX_DATA_LEN or NUM_ENTRIES, in which case
+ return false. */
+ int bump_size (const char *name)
+ {
+ if (num_entries == -1 || count < num_entries)
+ {
+ size_t new_size = size + DIRENT_LEN (strlen (name));
+ if (max_data_len > 0 && new_size > max_data_len)
+ return 0;
+ size = new_size;
+ count++;
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ if (dir->nn->name)
+ return ENOTDIR;
+
+ pthread_rwlock_rdlock (&dir->nn->mux->names_lock);
+
+ /* Find the first entry. */
+ for (first_name = dir->nn->mux->names, count = 2;
+ first_name && first_entry > count;
+ first_name = first_name->next)
+ if (first_name->node)
+ count++;
+
+ count = 0;
+
+ /* Make space for the `.' and `..' entries. */
+ if (first_entry == 0)
+ bump_size (".");
+ if (first_entry <= 1)
+ bump_size ("..");
+
+ /* See how much space we need for the result. */
+ for (nm = first_name; nm; nm = nm->next)
+ if (nm->node && !bump_size (nm->name))
+ break;
+
+ /* Allocate it. */
+ *data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ err = ((void *) *data == (void *) -1) ? errno : 0;
+
+ if (! err)
+ /* Copy out the result. */
+ {
+ char *p = *data;
+
+ int add_dir_entry (const char *name, ino_t fileno, int type)
+ {
+ if (num_entries == -1 || count < num_entries)
+ {
+ struct dirent hdr;
+ size_t name_len = strlen (name);
+ size_t sz = DIRENT_LEN (name_len);
+
+ if (sz > size)
+ return 0;
+ else
+ size -= sz;
+
+ hdr.d_fileno = fileno;
+ hdr.d_reclen = sz;
+ hdr.d_type = type;
+ hdr.d_namlen = name_len;
+
+ memcpy (p, &hdr, DIRENT_NAME_OFFS);
+ strcpy (p + DIRENT_NAME_OFFS, name);
+ p += sz;
+
+ count++;
+
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ *data_len = size;
+ *data_entries = count;
+
+ count = 0;
+
+ /* Add `.' and `..' entries. */
+ if (first_entry == 0)
+ add_dir_entry (".", 2, DT_DIR);
+ if (first_entry <= 1)
+ add_dir_entry ("..", 2, DT_DIR);
+
+ /* Fill in the real directory entries. */
+ for (nm = first_name; nm; nm = nm->next)
+ if (nm->node
+ && !add_dir_entry (nm->name, nm->fileno,
+ strcmp (nm->canon, nm->name) == 0
+ ? DT_REG : DT_LNK))
+ break;
+ }
+
+ pthread_rwlock_unlock (&dir->nn->mux->names_lock);
+
+ fshelp_touch (&dir->nn_stat, TOUCH_ATIME, hostmux_maptime);
+
+ return err;
+}
+
+/* Host lookup. */
+
+/* Free storage allocated consumed by the host mux name NM, but not the node
+ it points to. */
+static void
+free_name (struct hostmux_name *nm)
+{
+ if (nm->name != nm->canon)
+ free ((char *)nm->canon);
+ free ((char *)nm->name);
+ free (nm);
+}
+
+/* See if there's an existing entry for the name HOST, and if so, return its
+ node in NODE with an additional references. True is returned iff the
+ lookup succeeds. If PURGE is true, then any nodes with a null node are
+ removed. */
+static int
+lookup_cached (struct hostmux *mux, const char *host, int purge,
+ struct node **node)
+{
+ struct hostmux_name *nm = mux->names, **prevl = &mux->names;
+
+ while (nm)
+ {
+ struct hostmux_name *next = nm->next;
+
+ if (strcasecmp (host, nm->name) == 0)
+ {
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ if (nm->node)
+ nm->node->references++;
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+
+ if (nm->node)
+ {
+ *node = nm->node;
+ return 1;
+ }
+ }
+
+ if (purge && !nm->node)
+ {
+ *prevl = nm->next;
+ free_name (nm);
+ }
+ else
+ prevl = &nm->next;
+
+ nm = next;
+ }
+
+ return 0;
+}
+
+/* See if there's an existing entry for the name HOST, and if so, return its
+ node in NODE, with an additional reference, otherwise, create a new node
+ for the host HE as referred to by HOST, and return that instead, with a
+ single reference. The type of node created is either a translator node,
+ if HOST refers to the official name of the host, or a symlink node to the
+ official name, if it doesn't. */
+static error_t
+lookup_addrinfo (struct hostmux *mux, const char *host, struct addrinfo *he,
+ struct node **node)
+{
+ error_t err;
+ struct hostmux_name *nm = malloc (sizeof (struct hostmux_name));
+
+ if (! nm)
+ return ENOMEM;
+
+ nm->name = strdup (host);
+ if (!he || strcmp (host, he->ai_canonname) == 0)
+ nm->canon = nm->name;
+ else
+ nm->canon = strdup (he->ai_canonname);
+
+ err = create_host_node (mux, nm, node);
+ if (err)
+ {
+ free_name (nm);
+ return err;
+ }
+
+ pthread_rwlock_wrlock (&mux->names_lock);
+ if (lookup_cached (mux, host, 1, node))
+ /* An entry for HOST has already been created between the time we last
+ looked and now (which is possible because we didn't lock MUX).
+ Just throw away our version and return the one already in the cache. */
+ {
+ pthread_rwlock_unlock (&mux->names_lock);
+ nm->node->nn->name = 0; /* Avoid touching the mux name list. */
+ netfs_nrele (nm->node); /* Free the tentative new node. */
+ free_name (nm); /* And the name it was under. */
+ }
+ else
+ /* Enter NM into MUX's list of names, and return the new node. */
+ {
+ nm->fileno = mux->next_fileno++; /* Now that we hold the lock... */
+ nm->next = mux->names;
+ mux->names = nm;
+ pthread_rwlock_unlock (&mux->names_lock);
+ }
+
+ return 0;
+}
+
+/* Lookup the host HOST in MUX, and return the resulting node in NODE, with
+ an additional reference, or an error. */
+static error_t
+lookup_host (struct hostmux *mux, const char *host, struct node **node)
+{
+ int was_cached;
+ int h_err;
+ struct addrinfo *ai;
+ struct addrinfo hints;
+
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = PF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_IP;
+
+ pthread_rwlock_rdlock (&mux->names_lock);
+ was_cached = lookup_cached (mux, host, 0, node);
+ pthread_rwlock_unlock (&mux->names_lock);
+
+ if (was_cached)
+ return 0;
+
+ if (mux->canonicalize)
+ {
+ h_err = getaddrinfo (host, NULL, &hints, &ai);
+ if (! h_err)
+ {
+ h_err = lookup_addrinfo (mux, host, ai, node);
+ freeaddrinfo (ai);
+ }
+ }
+ else
+ h_err = lookup_addrinfo (mux, host, NULL, node);
+
+ return h_err;
+}
+
+/* This should sync the entire remote filesystem. If WAIT is set, return
+ only after sync is completely finished. */
+error_t
+netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+ return 0;
+}
+
+/* This should attempt a chmod call for the user specified by CRED on node
+ NODE, to change the owner to UID and the group to GID. */
+error_t
+netfs_attempt_chown (struct iouser *cred, struct node *node, uid_t uid, uid_t gid)
+{
+ if (node->nn->name)
+ return EOPNOTSUPP;
+ else
+ {
+ struct hostmux *mux = node->nn->mux;
+ error_t err = file_chown (mux->underlying, uid, gid);
+
+ if (! err)
+ {
+ struct hostmux_name *nm;
+
+ /* Change NODE's owner. */
+ mux->stat_template.st_uid = uid;
+ mux->stat_template.st_gid = gid;
+ node->nn_stat.st_uid = uid;
+ node->nn_stat.st_gid = gid;
+
+ /* Change the owner of each leaf node. */
+ pthread_rwlock_rdlock (&mux->names_lock);
+ for (nm = mux->names; nm; nm = nm->next)
+ if (nm->node)
+ {
+ nm->node->nn_stat.st_uid = uid;
+ nm->node->nn_stat.st_gid = gid;
+ }
+ pthread_rwlock_unlock (&mux->names_lock);
+
+ fshelp_touch (&node->nn_stat, TOUCH_CTIME, hostmux_maptime);
+ }
+
+ return err;
+ }
+}
+
+/* This should attempt a chauthor call for the user specified by CRED on node
+ NODE, to change the author to AUTHOR. */
+error_t
+netfs_attempt_chauthor (struct iouser *cred, struct node *node, uid_t author)
+{
+ if (node->nn->name)
+ return EOPNOTSUPP;
+ else
+ {
+ struct hostmux *mux = node->nn->mux;
+ error_t err = file_chauthor (mux->underlying, author);
+
+ if (! err)
+ {
+ struct hostmux_name *nm;
+
+ /* Change NODE's owner. */
+ mux->stat_template.st_author = author;
+ node->nn_stat.st_author = author;
+
+ /* Change the owner of each leaf node. */
+ pthread_rwlock_rdlock (&mux->names_lock);
+ for (nm = mux->names; nm; nm = nm->next)
+ if (nm->node)
+ nm->node->nn_stat.st_author = author;
+ pthread_rwlock_unlock (&mux->names_lock);
+
+ fshelp_touch (&node->nn_stat, TOUCH_CTIME, hostmux_maptime);
+ }
+
+ return err;
+ }
+}
+
+/* This should attempt a chmod call for the user specified by CRED on node
+ NODE, to change the mode to MODE. Unlike the normal Unix and Hurd meaning
+ of chmod, this function is also used to attempt to change files into other
+ types. If such a transition is attempted which is impossible, then return
+ EOPNOTSUPP. */
+error_t
+netfs_attempt_chmod (struct iouser *cred, struct node *node, mode_t mode)
+{
+ mode &= ~S_ITRANS;
+ if ((mode & S_IFMT) == 0)
+ mode |= (node->nn_stat.st_mode & S_IFMT);
+ if (node->nn->name || ((mode & S_IFMT) != (node->nn_stat.st_mode & S_IFMT)))
+ return EOPNOTSUPP;
+ else
+ {
+ error_t err = file_chmod (node->nn->mux->underlying, mode & ~S_IFMT);
+ if (! err)
+ {
+ node->nn_stat.st_mode = mode;
+ fshelp_touch (&node->nn_stat, TOUCH_CTIME, hostmux_maptime);
+ }
+ return err;
+ }
+}
diff --git a/hostmux/node.c b/hostmux/node.c
new file mode 100644
index 00000000..7167300f
--- /dev/null
+++ b/hostmux/node.c
@@ -0,0 +1,127 @@
+/* General fs node functions
+
+ Copyright (C) 1997, 1999, 2007 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <fcntl.h>
+
+#include "hostmux.h"
+
+/* Node maintenance. */
+
+/* Node NP is all done; free all its associated storage. */
+void
+netfs_node_norefs (struct node *node)
+{
+ if (node->nn->name)
+ /* Remove our name's pointer to us; the name itself will eventually be
+ freed by another party. */
+ node->nn->name->node = 0;
+ free (node->nn);
+ free (node);
+}
+
+/* Attempt to create a file named NAME in DIR for USER with MODE. Set *NODE
+ to the new node upon return. On any error, clear *NODE. *NODE should be
+ locked on success; no matter what, unlock DIR before returning. */
+error_t
+netfs_attempt_create_file (struct iouser *user, struct node *dir,
+ char *name, mode_t mode, struct node **node)
+{
+ *node = 0;
+ pthread_mutex_unlock (&dir->lock);
+ return EOPNOTSUPP;
+}
+
+/* Node NODE is being opened by USER, with FLAGS. NEWNODE is nonzero if we
+ just created this node. Return an error if we should not permit the open
+ to complete because of a permission restriction. */
+error_t
+netfs_check_open_permissions (struct iouser *user, struct node *node,
+ int flags, int newnode)
+{
+ error_t err = 0;
+ if (flags & O_READ)
+ err = fshelp_access (&node->nn_stat, S_IREAD, user);
+ if (!err && (flags & O_WRITE))
+ err = fshelp_access (&node->nn_stat, S_IWRITE, user);
+ if (!err && (flags & O_EXEC))
+ err = fshelp_access (&node->nn_stat, S_IEXEC, user);
+ return err;
+}
+
+/* This should attempt a utimes call for the user specified by CRED on node
+ NODE, to change the atime to ATIME and the mtime to MTIME. */
+error_t
+netfs_attempt_utimes (struct iouser *cred, struct node *node,
+ struct timespec *atime, struct timespec *mtime)
+{
+ error_t err = fshelp_isowner (&node->nn_stat, cred);
+ int flags = TOUCH_CTIME;
+
+ if (! err)
+ {
+ if (mtime)
+ node->nn_stat.st_mtim = *mtime;
+ else
+ flags |= TOUCH_MTIME;
+
+ if (atime)
+ node->nn_stat.st_atim = *atime;
+ else
+ flags |= TOUCH_ATIME;
+
+ fshelp_touch (&node->nn_stat, flags, hostmux_maptime);
+ }
+ return err;
+}
+
+/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and O_EXEC)
+ in *TYPES for file NODE and user CRED. */
+error_t
+netfs_report_access (struct iouser *cred, struct node *node, int *types)
+{
+ *types = 0;
+ if (fshelp_access (&node->nn_stat, S_IREAD, cred) == 0)
+ *types |= O_READ;
+ if (fshelp_access (&node->nn_stat, S_IWRITE, cred) == 0)
+ *types |= O_WRITE;
+ if (fshelp_access (&node->nn_stat, S_IEXEC, cred) == 0)
+ *types |= O_EXEC;
+ return 0;
+}
+
+/* Trivial definitions. */
+
+/* Make sure that NP->nn_stat is filled with current information. CRED
+ identifies the user responsible for the operation. */
+error_t
+netfs_validate_stat (struct node *node, struct iouser *cred)
+{
+ return 0;
+}
+
+/* This should sync the file NODE completely to disk, for the user CRED. If
+ WAIT is set, return only after sync is completely finished. */
+error_t
+netfs_attempt_sync (struct iouser *cred, struct node *node, int wait)
+{
+ return 0;
+}
diff --git a/hostmux/stubs.c b/hostmux/stubs.c
new file mode 100644
index 00000000..1d94f219
--- /dev/null
+++ b/hostmux/stubs.c
@@ -0,0 +1,145 @@
+/* Stub routines for hostmux
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <hurd/netfs.h>
+
+/* Attempt to turn NODE (user CRED) into a symlink with target NAME. */
+error_t
+netfs_attempt_mksymlink (struct iouser *cred, struct node *node, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to turn NODE (user CRED) into a device. TYPE is either S_IFBLK or
+ S_IFCHR. */
+error_t
+netfs_attempt_mkdev (struct iouser *cred, struct node *node,
+ mode_t type, dev_t indexes)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to set the passive translator record for FILE to ARGZ (of length
+ ARGZLEN) for user CRED. */
+error_t
+netfs_set_translator (struct iouser *cred, struct node *node,
+ char *argz, size_t argzlen)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should attempt a chflags call for the user specified by CRED on node
+ NODE, to change the flags to FLAGS. */
+error_t
+netfs_attempt_chflags (struct iouser *cred, struct node *node, int flags)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should attempt to set the size of the file NODE (for user CRED) to
+ SIZE bytes long. */
+error_t
+netfs_attempt_set_size (struct iouser *cred, struct node *node, off_t size)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should attempt to fetch filesystem status information for the remote
+ filesystem, for the user CRED. */
+error_t
+netfs_attempt_statfs (struct iouser *cred, struct node *node,
+ struct statfs *st)
+{
+ return EOPNOTSUPP;
+}
+
+/* Delete NAME in DIR for USER. */
+error_t
+netfs_attempt_unlink (struct iouser *user, struct node *dir, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+/* Note that in this one call, neither of the specific nodes are locked. */
+error_t
+netfs_attempt_rename (struct iouser *user, struct node *fromdir,
+ char *fromname, struct node *todir,
+ char *toname, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to create a new directory named NAME in DIR for USER with mode
+ MODE. */
+error_t
+netfs_attempt_mkdir (struct iouser *user, struct node *dir,
+ char *name, mode_t mode)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to remove directory named NAME in DIR for USER. */
+error_t
+netfs_attempt_rmdir (struct iouser *user,
+ struct node *dir, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+/* Create a link in DIR with name NAME to FILE for USER. Note that neither
+ DIR nor FILE are locked. If EXCL is set, do not delete the target, but
+ return EEXIST if NAME is already found in DIR. */
+error_t
+netfs_attempt_link (struct iouser *user, struct node *dir,
+ struct node *file, char *name, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to create an anonymous file related to DIR for USER with MODE.
+ Set *NODE to the returned file upon success. No matter what, unlock DIR. */
+error_t
+netfs_attempt_mkfile (struct iouser *user, struct node *dir,
+ mode_t mode, struct node **node)
+{
+ *node = 0;
+ pthread_mutex_unlock (&dir->lock);
+ return EOPNOTSUPP;
+}
+
+/* Read from the file NODE for user CRED starting at OFFSET and continuing for
+ up to *LEN bytes. Put the data at DATA. Set *LEN to the amount
+ successfully read upon return. */
+error_t
+netfs_attempt_read (struct iouser *cred, struct node *node,
+ off_t offset, size_t *len, void *data)
+{
+ return EOPNOTSUPP;
+}
+
+/* Write to the file NODE for user CRED starting at OFSET and continuing for up
+ to *LEN bytes from DATA. Set *LEN to the amount seccessfully written upon
+ return. */
+error_t
+netfs_attempt_write (struct iouser *cred, struct node *node,
+ off_t offset, size_t *len, void *data)
+{
+ return EOPNOTSUPP;
+}
diff --git a/hurd.boot b/hurd.boot
new file mode 100644
index 00000000..c3c08a9e
--- /dev/null
+++ b/hurd.boot
@@ -0,0 +1,13 @@
+# Boot script file for booting GNU Hurd. Each line specifies a file to be
+# loaded by the boot loader (the first word), and actions to be done with it.
+
+# First, the bootstrap filesystem. It needs several ports as arguments,
+# as well as the user flags from the boot loader.
+/hurd/ext2fs --multiboot-command-line=${kernel-command-line} --host-priv-port=${host-port} --device-master-port=${device-port} --exec-server-task=${exec-task} -T device ${root-device} $(task-create) $(task-resume)
+
+# Now the exec server; to load the dynamically-linked exec server program,
+# we have the boot loader in fact load and run ld.so, which in turn
+# loads and runs /hurd/exec. This task is created, and its task port saved
+# in ${exec-task} to be passed to the fs above, but it is left suspended;
+# the fs will resume the exec task once it is ready.
+/lib/ld.so /hurd/exec $(exec-task=task-create)
diff --git a/hurd/=pending-changes b/hurd/=pending-changes
new file mode 100644
index 00000000..d57270a9
--- /dev/null
+++ b/hurd/=pending-changes
@@ -0,0 +1,24 @@
+User visible:
+
+Add notification calls to process.defs (and create process_notify.defs).
+(not yet)
+
+Add file_exchange_contents (not yet)
+
+Change file_getfh and fsys_getfile to use more convenient interface.
+
+Add serverport arg to auth_user_authenticate; change auth to use it to
+abort auth_user_authenticate when it's dead.
+
+Add optional timeout arg to msg.defs.
+
+Add file_fetch_dir.
+
+Delete release arg from register version crap.
+
+Delete non-string args from io_server_version; separate name and version.
+
+Not user visible:
+
+Format of /var/login should be user:1 not user-1.
+ [Or as subdirectories, if a server supplies directory ops anyway]
diff --git a/hurd/Makefile b/hurd/Makefile
new file mode 100644
index 00000000..4273ff3d
--- /dev/null
+++ b/hurd/Makefile
@@ -0,0 +1,87 @@
+# Copyright (C) 1993,94,95,96,99,2002,2012 Free Software Foundation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := hurd
+makemode := misc
+
+hdrs = $(wildcard $(srcdir)/*.defs $(srcdir)/*.h)
+
+INSTHDRS = hurd_types.h version.h ioctl_types.h paths.h shared.h console.h \
+ $(notdir $(wildcard $(srcdir)/*.defs))
+MSGIDS := hurd.msgids $(patsubst %.defs,%.msgids,$(filter %.defs,$(INSTHDRS)))
+
+include ../Makeconf
+
+all: $(MSGIDS)
+
+install: install-msgids install-headers
+install-headers: $(includedir)/hurd \
+ $(addprefix $(includedir)/hurd/,$(INSTHDRS))
+install-msgids: $(MSGIDS) $(datadir)/msgids; $(INSTALL_DATA) $^
+
+$(includedir)/hurd/%: $(srcdir)/%; $(INSTALL_DATA) $< $@
+
+$(datadir)/msgids $(includedir)/hurd:;mkdir -p $@
+
+%.msgids: $(srcdir)/%.defs
+ if grep -q '^subsystem' $<; \
+ then $(CPP) $(CPPFLAGS) $< | $(MIGCOM) -n -list $@; \
+ else > $@; fi
+hurd.msgids: $(patsubst %.defs,%.msgids,$(filter %.defs,$(INSTHDRS)))
+ cat $^ > $@
+
+#
+# The following rules assist in creating an `Xioctl.defs' file
+# to define RPCs that are sent primarily by ioctl commands.
+# To use them, write a file `Xioctl-headers.h', e.g. for `mioctl-headers.h':
+# #include <sys/mtio.h>
+# with an #include for each header that defines ioctl request macros
+# using _IO('X') et al. Then `make Xioctl-proto.defs' will create
+# a prototype file for you to hand-edit into `Xioctl.defs'.
+
+# Building foo.h from foo.sym:
+%.symc: %.sym
+ $(AWK) -f $(srcdir)/gensym.awk $< >$*.symc
+%.symc.o: %.symc
+ $(CC) -S $(CPPFLAGS) $(CFLAGS) $(CPPFLAGS-$@) -x c -o $@ $<
+%.h: %.symc.o
+ sed <$< -e 's/^[^*].*$$//' | \
+ sed -e 's/^[*]/#define/' -e 's/mAgIc[^-0-9]*//' -e '/^ *$$/d' >$@
+
+%-ioctls.sym: tmpl-ioctls.sym
+ sed 's|HEADER|<$(subst +,/,$*)>|' $< > $@
+
+cpp = $(CC) $(CPPFLAGS) $(CFLAGS) $(CPPFLAGS-$@) -E -x c
+
+%ioctl-requests.list: %ioctl-headers.h
+ $(cpp) $< | sed -n 's/^#.*"\([^"]*\)".*$$/\1/p' | sort | uniq | \
+ while read f; do \
+ sed -n 's/^[ ]*#[ ]*define[ ]*\([A-Z0-9_]*\)[^A-Z0-9_][^A-Z0-9_]*_IO.*'\'$*\'.*$$'/\1/p' $$f; \
+ done | sort | uniq > $@
+
+%ioctl.defs: %ioctl.sym
+
+%ioctl-values.sym: %ioctl-headers.h %ioctl-requests.list ioctl-tmpl.sym
+ (sed 's%@HEADER_LIST@%$<%;s/@GROUP@/$*/g' < $(filter %.sym,$^); \
+ while read r; do \
+ for x in CMD SUBID INOUT TYPE \
+ TYPE0 TYPE1 TYPE2 COUNT0 COUNT1 COUNT2; do \
+ echo "expr $${x}($${r}) $${r}_$${x}"; \
+ done; \
+ done < $(filter %.list,$^)) > $@
+
+%ioctl-proto.defs: %ioctl-values.h ioctl.awk
+ sed 's/^#define//;s/_/ /g' $< | $(AWK) -f $(filter %.awk,$^) > $@
diff --git a/hurd/auth.defs b/hurd/auth.defs
new file mode 100644
index 00000000..ebb682fd
--- /dev/null
+++ b/hurd/auth.defs
@@ -0,0 +1,79 @@
+/* Definitions for the authentication server
+ Copyright (C) 1991,92,93,94,96,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+subsystem auth 25000;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef AUTH_IMPORTS
+AUTH_IMPORTS
+#endif
+
+INTR_INTERFACE
+
+/* Throughout, uid's and gid's are passed as arrays. One of these
+ arrays is called the "effective" ids; these id's should be used to
+ compute whether a given user is permitted a given operation. The
+ other array is called the "available" ids; these should not be used
+ for such computation (though they can be turned into effective ids
+ when calling auth_makeauth). The first available id is
+ conventionally called the "real" id, and the second the "saved" id. */
+
+/* Given an authentication handle, return the identification. */
+routine auth_getids (
+ handle: auth_t;
+ out euids: idarray_t;
+ out auids: idarray_t;
+ out egids: idarray_t;
+ out agids: idarray_t);
+
+/* Create a new authentication handle. */
+routine auth_makeauth (
+ handle: auth_t;
+ other_handles: portarray_t;
+ euids: idarray_t;
+ auids: idarray_t;
+ egids: idarray_t;
+ agids: idarray_t;
+ out newhandle: mach_port_make_send_t);
+
+/* Called by a user in a reauthentication transaction. The rendezvous
+ port is used to match the request up with the server's
+ auth_server_authenticate call. The newport is a port provided by
+ the server. */
+routine auth_user_authenticate (
+ handle: auth_t;
+ sreplyport reply: mach_port_poly_t;
+ rendezvous: mach_port_send_t;
+ out newport: mach_port_send_t);
+
+/* Called by a server in a reauthentication transaction. The
+ rendezvous port is used to match the request up with the user's
+ auth_user_authenticate call. The newport is passed to the user
+ through the authentication server. The identification information
+ is returned. */
+routine auth_server_authenticate (
+ handle: auth_t;
+ sreplyport reply: mach_port_poly_t;
+ rendezvous: mach_port_send_t;
+ newport: mach_port_poly_t;
+ out euids: idarray_t;
+ out auids: idarray_t;
+ out egids: idarray_t;
+ out agids: idarray_t);
diff --git a/hurd/auth_reply.defs b/hurd/auth_reply.defs
new file mode 100644
index 00000000..63dc7dca
--- /dev/null
+++ b/hurd/auth_reply.defs
@@ -0,0 +1,43 @@
+/* Reply-only side of auth interface
+ Copyright (C) 1991,93,94,2001 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+subsystem auth_reply 25100; /* must match auth.defs + 100 */
+
+#include <hurd/hurd_types.defs>
+
+type reply_port_t = polymorphic | MACH_MSG_TYPE_PORT_SEND_ONCE
+ ctype: mach_port_t;
+
+skip; /* auth_getids */
+skip; /* auth_makeauth */
+
+simpleroutine auth_user_authenticate_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ in newhandle: mach_port_send_t);
+
+simpleroutine auth_server_authenticate_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ in gen_uids: idarray_t;
+ in aux_uids: idarray_t;
+ in gen_gids: idarray_t;
+ in aux_gids: idarray_t);
diff --git a/hurd/auth_request.defs b/hurd/auth_request.defs
new file mode 100644
index 00000000..a1f9e271
--- /dev/null
+++ b/hurd/auth_request.defs
@@ -0,0 +1,36 @@
+/* Request-only side of auth interface
+ Copyright (C) 2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+subsystem auth_reply 25000; /* must match auth.defs */
+
+#include <hurd/hurd_types.defs>
+
+skip; /* auth_getids */
+skip; /* auth_makeauth */
+
+simpleroutine auth_user_authenticate_request (
+ handle: auth_t;
+ ureplyport reply: mach_port_poly_t;
+ rendezvous: mach_port_send_t);
+
+simpleroutine auth_server_authenticate_request (
+ handle: auth_t;
+ ureplyport reply: mach_port_poly_t;
+ rendezvous: mach_port_send_t;
+ newport: mach_port_poly_t);
diff --git a/hurd/console.h b/hurd/console.h
new file mode 100644
index 00000000..baf03942
--- /dev/null
+++ b/hurd/console.h
@@ -0,0 +1,309 @@
+/* console.h -- Public interface to the console server.
+ Copyright (C) 2002,10 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _HURD_CONSOLE_H
+#define _HURD_CONSOLE_H
+
+#include <stdint.h>
+#include <wchar.h>
+
+typedef enum
+ {
+ CONS_COLOR_BLACK = 0, CONS_COLOR_RED, CONS_COLOR_GREEN, CONS_COLOR_YELLOW,
+ CONS_COLOR_BLUE, CONS_COLOR_MAGENTA, CONS_COLOR_CYAN, CONS_COLOR_WHITE
+ } cons_color_t;
+#define CONS_COLOR_MAX (CONS_COLOR_WHITE)
+
+typedef struct
+{
+ /* The intensity is traditionally a color attribute. */
+#define CONS_ATTR_INTENSITY_NORMAL 000000000000
+#define CONS_ATTR_INTENSITY_BOLD 000000000001
+#define CONS_ATTR_INTENSITY_DIM 000000000002
+ uint32_t intensity : 2;
+
+ uint32_t underlined : 1;
+ uint32_t blinking : 1;
+ uint32_t reversed : 1;
+ uint32_t concealed : 1;
+
+ /* Color attributes. */
+ uint32_t bgcol : 3;
+ uint32_t fgcol : 3;
+
+ /* Font attributes. */
+ uint32_t italic : 1;
+ uint32_t bold : 1;
+} conchar_attr_t;
+
+/* We support double-width characters by using an extra bit to identify the
+ continuation in the character matrix. The constants below document our
+ usage of wchar_t. */
+#define CONS_WCHAR_MASK ((wchar_t) 0x401fffff)
+#define CONS_WCHAR_CONTINUED ((wchar_t) 0x40000000)
+
+typedef struct
+{
+ wchar_t chr;
+ conchar_attr_t attr;
+} conchar_t;
+
+typedef union
+{
+ struct
+ {
+ /* Only the first 31 bits are available (see WHAT.not_matrix). */
+ uint32_t start;
+ uint32_t end;
+ } matrix;
+ struct
+ {
+ uint32_t cursor_pos : 1;
+ uint32_t cursor_status : 1;
+ uint32_t screen_cur_line : 1;
+ uint32_t screen_scr_lines : 1;
+ uint32_t bell_audible : 1;
+ uint32_t bell_visible : 1;
+ uint32_t flags : 1;
+ uint32_t _unused : 24;
+ uint32_t not_matrix : 1;
+ /* Here are 32 more unused bits. */
+ } what;
+} cons_change_t;
+
+struct cons_display
+{
+#define CONS_MAGIC 0x48555244 /* Hex for "HURD". */
+ uint32_t magic; /* CONS_MAGIC, use to detect
+ endianess. */
+#define CONS_VERSION_MAJ 0x0
+#define CONS_VERSION_MAJ_SHIFT 16
+#define CONS_VERSION_AGE 0x0
+ uint32_t version; /* Version of interface. Lower 16
+ bits define the age, upper 16 bits
+ the major version. */
+
+
+ /* Various one bit flags that don't deserve their own field. */
+
+ /* The output is stopped. The client can display the status of this
+ flag, but should not otherwise interpret it. */
+#define CONS_FLAGS_SCROLL_LOCK 0x00000001
+
+ /* Tracking mouse events is requested. See CONS_MOUSE_* macros
+ further down. */
+#define CONS_FLAGS_TRACK_MOUSE 0x00000002
+
+ uint32_t flags;
+
+
+ struct
+ {
+ uint32_t width; /* Width of screen matrix. */
+ uint32_t lines; /* Length of whole matrix. */
+ uint32_t cur_line; /* Virtual start of visible area. Needs to be
+ taken module LINES to get the real start of
+ visible area in the matrix. This is only
+ ever increased by the server, so clients
+ can optimize scrolling. */
+ uint32_t scr_lines; /* Number of lines in scrollback buffer
+ preceding CUR_LINE. */
+ uint32_t height; /* Number of lines in visible area following
+ (and including) CUR_LINE. */
+ uint32_t matrix; /* Index (in uint32_t) of the beginning of
+ screen matrix in this structure. */
+ } screen;
+
+ struct
+ {
+ uint32_t col; /* Current column (x-position) of cursor. */
+ uint32_t row; /* Current row (y-position) of cursor. */
+
+#define CONS_CURSOR_INVISIBLE 0
+#define CONS_CURSOR_NORMAL 1
+#define CONS_CURSOR_VERY_VISIBLE 2
+ uint32_t status; /* Visibility status of cursor. */
+ } cursor;
+
+ struct
+ {
+ uint32_t audible; /* Audible bell. */
+ uint32_t visible; /* Visible bell. */
+ } bell;
+
+ struct
+ {
+ uint32_t buffer; /* Index (in uint32_t) of the beginning of the
+ changes buffer in this structure. */
+ uint32_t length; /* Length of buffer. */
+ uint32_t written; /* Number of records written by server. */
+
+#define _CONS_CHANGES_LENGTH 512
+ cons_change_t _buffer[_CONS_CHANGES_LENGTH];
+ } changes;
+
+ /* Don't use this, use ((wchar_t *) cons_display +
+ cons_display.screen.matrix) instead. This will make your client
+ upward compatible with future versions of this interface. */
+ conchar_t _matrix[0];
+};
+
+
+/* The console server will use the following UCS-4 characters for the
+ specified terminal graphic characters. If the display driver is
+ UCS-4 capable, it can print them without interpretation. */
+
+/* ACS_BLOCK maps to FULL BLOCK. */
+#define CONS_CHAR_BLOCK ((wchar_t) 0x2588)
+/* ACS_DIAMOND maps to BLACK DIAMOND. */
+#define CONS_CHAR_DIAMOND ((wchar_t) 0x25c6)
+/* ACS_CKBOARD maps to MEDIUM SHADE. */
+#define CONS_CHAR_CKBOARD ((wchar_t) 0x2592)
+/* ACS_BOARD maps to LIGHT SHADE. */
+#define CONS_CHAR_BOARD ((wchar_t) 0x2591)
+/* ACS_BULLET maps to BULLET (Linux maps it to MIDDLE DOT 0x00b7). */
+#define CONS_CHAR_BULLET ((wchar_t) 0x2022)
+/* ACS_STERLING maps to POUND STERLING. */
+#define CONS_CHAR_STERLING ((wchar_t) 0x00a3)
+/* ACS_DEGREE maps to DEGREE SIGN. */
+#define CONS_CHAR_DEGREE ((wchar_t) 0x00b0)
+/* ACS_PLMINUS maps to PLUS-MINUS SIGN. */
+#define CONS_CHAR_PLMINUS ((wchar_t) 0x00b1)
+/* ACS_PI maps to GREEK SMALL LETTER PI. */
+#define CONS_CHAR_PI ((wchar_t) 0x03c0)
+/* ACS_LANTERN maps to BLACK HOURGLASS. XXX Is this appropriate? */
+#define CONS_CHAR_LANTERN ((wchar_t) 0x29d7)
+
+/* ACS_RARROW maps to RIGHTWARDS ARROW. */
+#define CONS_CHAR_RARROW ((wchar_t) 0x2192)
+/* ACS_LARROW maps to LEFTWARDS ARROW. */
+#define CONS_CHAR_LARROW ((wchar_t) 0x2190)
+/* ACS_UARROW maps to UPWARDS ARROW. */
+#define CONS_CHAR_UARROW ((wchar_t) 0x2191)
+/* ACS_DARROW maps to DOWNWARDS ARROW. */
+#define CONS_CHAR_DARROW ((wchar_t) 0x2193)
+
+/* ACS_LRCORNER maps to BOX DRAWINGS LIGHT UP AND LEFT. */
+#define CONS_CHAR_LRCORNER ((wchar_t) 0x2518)
+/* ACS_URCORNER maps to BOX DRAWINGS LIGHT DOWN AND LEFT. */
+#define CONS_CHAR_URCORNER ((wchar_t) 0x2510)
+/* ACS_ULCORNER maps to BOX DRAWINGS LIGHT DOWN AND RIGHT. */
+#define CONS_CHAR_ULCORNER ((wchar_t) 0x250c)
+/* ACS_LLCORNER maps to BOX DRAWINGS LIGHT UP AND RIGHT. */
+#define CONS_CHAR_LLCORNER ((wchar_t) 0x2514)
+/* ACS_PLUS maps to BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL. */
+#define CONS_CHAR_PLUS ((wchar_t) 0x253c)
+/* ACS_HLINE maps to BOX DRAWINGS LIGHT HORIZONTAL. */
+#define CONS_CHAR_HLINE ((wchar_t) 0x2500)
+/* ACS_LTEE maps to BOX DRAWINGS LIGHT VERTICAL AND RIGHT. */
+#define CONS_CHAR_LTEE ((wchar_t) 0x251c)
+/* ACS_RTEE maps to BOX DRAWINGS LIGHT VERTICAL AND LEFT. */
+#define CONS_CHAR_RTEE ((wchar_t) 0x2524)
+/* ACS_BTEE maps to BOX DRAWINGS LIGHT UP AND HORIZONTAL. */
+#define CONS_CHAR_BTEE ((wchar_t) 0x2534)
+/* ACS_TTEE maps to BOX DRAWINGS LIGHT DOWN AND HORIZONTAL. */
+#define CONS_CHAR_TTEE ((wchar_t) 0x252c)
+/* ACS_VLINE maps to BOX DRAWINGS LIGHT VERTICAL. */
+#define CONS_CHAR_VLINE ((wchar_t) 0x2502)
+
+/* ACS_S1 maps to HORIZONTAL SCAN LINE-1. */
+#define CONS_CHAR_S1 ((wchar_t) 0x23ba)
+/* ACS_S3 maps to HORIZONTAL SCAN LINE-3. */
+#define CONS_CHAR_S3 ((wchar_t) 0x23bb)
+/* ACS_S7 maps to HORIZONTAL SCAN LINE-1. */
+#define CONS_CHAR_S7 ((wchar_t) 0x23bc)
+/* ACS_S9 maps to HORIZONTAL SCAN LINE-1. */
+#define CONS_CHAR_S9 ((wchar_t) 0x23bd)
+
+/* ACS_NEQUAL maps to NOT EQUAL TO. */
+#define CONS_CHAR_NEQUAL ((wchar_t) 0x2260)
+/* ACS_LEQUAL maps to LESS-THAN OR EQUAL TO. */
+#define CONS_CHAR_LEQUAL ((wchar_t) 0x2264)
+/* ACS_GEQUAL maps to GREATER-THAN OR EQUAL TO. */
+#define CONS_CHAR_GEQUAL ((wchar_t) 0x2265)
+
+
+
+/* The input driver should emit these escape sequences for special
+ keys which don't represent characters in UTF-8. */
+#define CONS_KEY_UP "\eOA" /* Cursor up. */
+#define CONS_KEY_DOWN "\eOB" /* Cursor down. */
+#define CONS_KEY_RIGHT "\eOC" /* Cursor right. */
+#define CONS_KEY_LEFT "\eOD" /* Cursor left. */
+#define CONS_KEY_BACKSPACE "\177" /* Backspace key. */
+#define CONS_KEY_F1 "\eOP" /* Function key 1. */
+#define CONS_KEY_F2 "\eOQ" /* Function key 2. */
+#define CONS_KEY_F3 "\eOR" /* Function key 3. */
+#define CONS_KEY_F4 "\eOS" /* Function key 4. */
+#define CONS_KEY_F5 "\e[15~" /* Function key 5. */
+#define CONS_KEY_F6 "\e[17~" /* Function key 6. */
+#define CONS_KEY_F7 "\e[18~" /* Function key 7. */
+#define CONS_KEY_F8 "\e[19~" /* Function key 8. */
+#define CONS_KEY_F9 "\e[20~" /* Function key 9. */
+#define CONS_KEY_F10 "\e[21~" /* Function key 10. */
+#define CONS_KEY_F11 "\e[23~" /* Function key 11. */
+#define CONS_KEY_F12 "\e[24~" /* Function key 12. */
+#define CONS_KEY_F13 "\e[25~" /* Function key 13. */
+#define CONS_KEY_F14 "\e[26~" /* Function key 14. */
+#define CONS_KEY_F15 "\e[28~" /* Function key 15. */
+#define CONS_KEY_F16 "\e[29~" /* Function key 16. */
+#define CONS_KEY_F17 "\e[31~" /* Function key 17. */
+#define CONS_KEY_F18 "\e[32~" /* Function key 18. */
+#define CONS_KEY_F19 "\e[33~" /* Function key 19. */
+#define CONS_KEY_F20 "\e[34~" /* Function key 20. */
+#define CONS_KEY_HOME "\e[1~" /* Home key. */
+#define CONS_KEY_IC "\e[2~" /* Insert char mode. */
+#define CONS_KEY_DC "\e[3~" /* Delete character. */
+#define CONS_KEY_END "\e[4~" /* End key. */
+#define CONS_KEY_PPAGE "\e[5~" /* Previous page. */
+#define CONS_KEY_NPAGE "\e[6~" /* Next page. */
+#define CONS_KEY_BTAB "\e[Z" /* Back tab key. */
+#define CONS_KEY_B2 "\e[G" /* Center of keypad. */
+
+/* Mouse support is compatible to xterm's mouse tracking feature. */
+
+#define CONS_MOUSE_BUTTON_MASK 0x03
+#define CONS_MOUSE_BUTTON1 0x00
+#define CONS_MOUSE_BUTTON2 0x01
+#define CONS_MOUSE_BUTTON3 0x02
+#define CONS_MOUSE_RELEASE 0x03
+#define CONS_MOUSE_MOD_MASK 0x1c
+#define CONS_MOUSE_MOD_SHIFT 0x04
+#define CONS_MOUSE_MOD_META 0x08
+#define CONS_MOUSE_MOD_CTRL 0x10
+
+/* Screen positions are offset by this value. */
+#define CONS_MOUSE_OFFSET_BASE 0x20
+
+#define CONS_MOUSE_EVENT_LENGTH 6
+#define CONS_MOUSE_EVENT_PREFIX "\e[M"
+
+/* This macro populates STR with the mouse event EVENT at position X
+ and Y, and returns 1 if successul and 0 if X or Y is out of
+ range. X and Y start from 0. */
+#define CONS_MOUSE_EVENT(str,event,x,y) \
+ (((int)(x) < 0 || (int)(x) + CONS_MOUSE_OFFSET_BASE > 255 \
+ || (int)(y) < 0 || (int)(y) + CONS_MOUSE_OFFSET_BASE > 255) ? 0 \
+ : ((*(str) = CONS_MOUSE_EVENT_PREFIX[0]), \
+ (*((str) + 1) = CONS_MOUSE_EVENT_PREFIX[1]), \
+ (*((str) + 2) = CONS_MOUSE_EVENT_PREFIX[2]), \
+ (*((str) + 3) = (char)((int)(event) + CONS_MOUSE_OFFSET_BASE)), \
+ (*((str) + 4) = (char)((int)(x) + CONS_MOUSE_OFFSET_BASE)), \
+ (*((str) + 5) = (char)((int)(y) + CONS_MOUSE_OFFSET_BASE), 1)))
+
+#endif /* _HURD_CONSOLE_H */
diff --git a/hurd/crash.defs b/hurd/crash.defs
new file mode 100644
index 00000000..442957d1
--- /dev/null
+++ b/hurd/crash.defs
@@ -0,0 +1,45 @@
+/* MiG protocol for handling program crashes.
+ Copyright (C) 1992, 1994, 1995, 1996 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Roland McGrath. */
+
+subsystem crash 32000;
+
+#include <hurd/hurd_types.defs>
+
+/* Handle a crashing task, whose task control port is TASK.
+
+ FILE is a file port open for writing. The caller will link it to "core"
+ (or whatever name) if the RPC returns success.
+
+ SIGNO, SIGCODE, and SIGERROR indicate the signal that killed the
+ process. EXC is zero for a software signal; otherwise EXC, CODE, and
+ SUBCODE are the original Mach exception codes.
+
+ CTTYID_PORT is the process's ctty's identification port, for use
+ in sending stop signals to the process group. */
+
+routine crash_dump_task (
+ crashserver: mach_port_t;
+ sreplyport reply: sreply_port_t;
+ task: task_t;
+ file: file_t;
+ signo: int; sigcode: integer_t; sigerror: int;
+ exc: natural_t; code: natural_t; subcode: natural_t;
+ cttyid_port: mach_port_send_t);
diff --git a/hurd/crash_reply.defs b/hurd/crash_reply.defs
new file mode 100644
index 00000000..ec6ddcd6
--- /dev/null
+++ b/hurd/crash_reply.defs
@@ -0,0 +1,30 @@
+/* MiG protocol for handling program crashes, reply half.
+ Copyright (C) 1992,94,95,2001 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Roland McGrath. */
+
+subsystem crash_reply 32100; /* must match crash + 100 */
+
+#include <hurd/hurd_types.defs>
+
+/* This file must correspond to crash.defs. */
+
+simpleroutine crash_dump_task_reply (
+ ureply_port: mach_port_poly_t;
+ RETURN_CODE_ARG);
diff --git a/hurd/default_pager.defs b/hurd/default_pager.defs
new file mode 100644
index 00000000..48855039
--- /dev/null
+++ b/hurd/default_pager.defs
@@ -0,0 +1,99 @@
+/* -*- C -*-
+ Version of <mach/default_pager.defs> modified for Hurd implementation.
+*/
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+subsystem default_pager 2275;
+
+#include <mach/std_types.defs>
+#include <mach/mach_types.defs>
+#include <mach/default_pager_types.defs>
+#include <device/device_types.defs>
+
+import <hurd/default_pager_types.h>; /* XXX */
+
+#ifdef DEFAULT_PAGER_IMPORTS
+DEFAULT_PAGER_IMPORTS
+#endif
+
+routine default_pager_object_create(
+ default_pager : mach_port_t;
+ out memory_object : memory_object_t =
+ MACH_MSG_TYPE_MAKE_SEND;
+ object_size : vm_size_t);
+
+routine default_pager_info(
+ default_pager : mach_port_t;
+ out info : default_pager_info_t);
+
+routine default_pager_objects(
+ default_pager : mach_port_t;
+ out objects : default_pager_object_array_t,
+ CountInOut, Dealloc;
+ out ports : mach_port_array_t =
+ array[] of mach_port_move_send_t,
+ CountInOut, Dealloc);
+
+routine default_pager_object_pages(
+ default_pager : mach_port_t;
+ memory_object : memory_object_name_t;
+ out pages : default_pager_page_array_t,
+ CountInOut, Dealloc);
+
+/* This is the original Mach call, now deprecated in favor
+ of default_pager_paging_storage. */
+routine default_pager_paging_file(
+ default_pager : mach_port_t;
+ master_device_port : mach_port_t;
+ filename : default_pager_filename_t;
+ add : boolean_t);
+
+skip; /* default_pager_register_fileserver */
+
+/* Add or remove an area of paging storage, which is a subset of the
+ Mach device for which device_open returned DEVICE_PORT. The area
+ consists of the concatenation of contiguous regions described by
+ RUNS. Each even-numbered element of RUNS gives the starting record
+ number of a region whose length is given by the next odd-numbered
+ element. NAME is used in any diagnostics the default pager prints
+ about device errors when paging. When removing a paging area, NAME
+ and RUNS must match exactly. */
+routine default_pager_paging_storage(
+ default_pager : mach_port_t;
+ device_port : mach_port_t;
+ runs : recnum_array_t =
+ array[] of recnum_t;
+ name : default_pager_filename_t;
+ add : boolean_t);
+
+/* This call is made on a memory object returned by default_pager_object_create
+ to fix the object's maximum size. Any references to pages beyond the limit
+ will fail. */
+routine default_pager_object_set_size(
+ memory_object : memory_object_t;
+ msgseqno seqno : mach_port_seqno_t;
+ object_size_limit : vm_size_t);
diff --git a/hurd/default_pager_reply.defs b/hurd/default_pager_reply.defs
new file mode 100644
index 00000000..0f9ff86b
--- /dev/null
+++ b/hurd/default_pager_reply.defs
@@ -0,0 +1,17 @@
+/* Reply half of default_pager.defs. */
+
+subsystem default_pager_reply 2375; /* 2275 + 100 */
+
+#include <hurd/hurd_types.defs>
+
+skip; /* default_pager_object_create */
+skip; /* default_pager_info */
+skip; /* default_pager_objects */
+skip; /* default_pager_object_pages */
+skip; /* default_pager_paging_file */
+skip; /* default_pager_register_fileserver */
+skip; /* default_pager_paging_storage */
+
+simpleroutine default_pager_object_set_size_reply(
+ reply_port: mach_port_send_once_t;
+ RETURN_CODE_ARG);
diff --git a/hurd/default_pager_types.h b/hurd/default_pager_types.h
new file mode 100644
index 00000000..7cd14a3d
--- /dev/null
+++ b/hurd/default_pager_types.h
@@ -0,0 +1,28 @@
+/* C declarations for Hurd default pager interface
+ Copyright (C) 2001 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _DEFAULT_PAGER_TYPES_H
+#define _DEFAULT_PAGER_TYPES_H
+
+#include <mach/std_types.h> /* For mach_port_t et al. */
+#include <device/device_types.h> /* For recnum_t. */
+
+typedef recnum_t *recnum_array_t;
+
+#endif
diff --git a/hurd/exec.defs b/hurd/exec.defs
new file mode 100644
index 00000000..2888fb1e
--- /dev/null
+++ b/hurd/exec.defs
@@ -0,0 +1,57 @@
+/* Interface definitions for the exec servers.
+ Copyright (C) 1991,92,93,94,95,2001 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell and Roland McGrath. */
+
+subsystem exec 30000;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef EXEC_IMPORTS
+EXEC_IMPORTS
+#endif
+
+INTR_INTERFACE
+
+routine exec_exec (
+ execserver: file_t;
+ file: mach_port_send_t;
+ oldtask: task_t;
+ flags: int;
+ argv: data_t SCP;
+ envp: data_t SCP;
+ dtable: portarray_t SCP;
+ portarray: portarray_t SCP;
+ intarray: intarray_t SCP;
+ deallocnames: mach_port_name_array_t;
+ destroynames: mach_port_name_array_t);
+
+skip; /* obsolete exec_startup */
+
+/* This call is made by the bootstrapping filesystem to give the
+ execserver its auth handle. */
+routine exec_init (
+ execserver: file_t;
+ auth_handle: auth_t;
+ proc_server: mach_port_send_t);
+
+simpleroutine exec_setexecdata (
+ execserver: file_t;
+ ports: portarray_t SCP;
+ ints: intarray_t SCP);
diff --git a/hurd/exec_startup.defs b/hurd/exec_startup.defs
new file mode 100644
index 00000000..697f6b22
--- /dev/null
+++ b/hurd/exec_startup.defs
@@ -0,0 +1,50 @@
+/* Interface definitions for process startup.
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Roland McGrath. */
+
+subsystem exec_startup 30500;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef EXEC_STARTUP_IMPORTS
+EXEC_STARTUP_IMPORTS
+#endif
+
+/* This call is made by a new task to its bootstrap port to get its
+ startup ports and information. */
+
+routine exec_startup_get_info (
+ bootstrap: exec_startup_t;
+ /* These describe the entry point and program header data
+ of the user program loaded into the task. */
+ out user_entry: vm_address_t;
+ out phdr_data: vm_address_t;
+ out phdr_size: vm_size_t;
+ /* These are the base address and size of the initial stack
+ allocated by the exec server. */
+ out stack_base: vm_address_t;
+ out stack_size: vm_size_t;
+ /* The rest of the information is that passed by exec_exec. */
+ out flags: int;
+ out argv: data_t, dealloc;
+ out envp: data_t, dealloc;
+ out dtable: portarray_t, dealloc;
+ out portarray: portarray_t, dealloc;
+ out intarray: intarray_t, dealloc);
diff --git a/hurd/fs.defs b/hurd/fs.defs
new file mode 100644
index 00000000..24526826
--- /dev/null
+++ b/hurd/fs.defs
@@ -0,0 +1,373 @@
+/* Definitions for the filesystem interface.
+ Copyright (C) 1994,95,96,97,98,99,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* All these objects also implement the generic IO facilities. */
+
+subsystem fs 20000;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef FILE_IMPORTS
+FILE_IMPORTS
+#endif
+
+/* Operations supported on all files */
+
+INTR_INTERFACE
+
+/* Overlay a task with a file. Necessary initialization, including
+ authentication changes associated with set[ug]id execution must be
+ handled by the filesystem. Filesystems normally implement this by
+ using exec_newtask or exec_loadtask as appropriate. */
+routine file_exec (
+ exec_file: file_t;
+ RPT
+ exec_task: task_t;
+ flags: int;
+ argv: data_t SCP;
+ envp: data_t SCP;
+ fdarray: portarray_t SCP;
+ portarray: portarray_t SCP;
+ intarray: intarray_t SCP;
+ deallocnames: mach_port_name_array_t SCP;
+ destroynames: mach_port_name_array_t SCP);
+
+/* Change owner and/or group */
+routine file_chown (
+ chown_file: file_t;
+ RPT
+ new_owner: uid_t;
+ new_group: gid_t);
+
+/*
+Whan that Aprill with hith thoureth thoote
+The droghte of March hath perthed to the roote,
+And bathed every veyne in thwith licour,
+Of which vertu engendred is the flour;
+Whan Zephiruth eek with hith thweete breeth
+Inthpired hath in every holt and heeth
+The tender croppeth, and the yonge thonne
+Hath in the Ram his halve courth yronne,
+And thmale foweleth maken melodye,
+That thlepen all the nyght with open ye
+(Tho Priketh hem Nature in hir corageth),
+Thanne longen folk to goon on pligrimageth,
+And palmereth for to theken thtraunge thtrondeth,
+To ferne halweth, kowthe in thondry londeth:
+And thpethially, from every thireth ende
+Of Engelond to Cantebury they wende,
+The hooly blithful martyr for to theke,
+That hem hath holpen whan that they were theeke.
+*/
+routine file_chauthor (
+ chauth_file: file_t;
+ RPT
+ new_author: uid_t);
+
+/* Change mode bits */
+routine file_chmod (
+ chmod_file: file_t;
+ RPT
+ new_mode: mode_t);
+
+/* Change file flags */
+routine file_chflags (
+ chflags_file: file_t;
+ RPT
+ new_flags: int);
+
+/* Change access and modify times */
+/* If the microseconds value is -1 (all bits on) then the time should be
+ set to the current time and the remainder of the time_value_t ignored. */
+routine file_utimes (
+ utimes_file: file_t;
+ RPT
+ new_atime: time_value_t;
+ new_mtime: time_value_t);
+
+/* Change the size of the file. If the size increases, new blocks are
+ zero-filled. After successful return, it is safe to reference mapped
+ areas of the file up to NEW_SIZE. */
+routine file_set_size (
+ trunc_file: file_t;
+ RPT
+ new_size: loff_t);
+
+/* Apply/manipulate advisory lock */
+routine file_lock (
+ lock_file: file_t;
+ RPT
+ flags: int);
+
+/* Return current lock status. Mystatus tells what kind of lock the
+ caller has; otherstatus tells what kind of lock anyone has
+ (including the caller). */
+routine file_lock_stat (
+ lock_file: file_t;
+ RPT
+ out mystatus: int;
+ out otherstatus: int);
+
+/* Find out what kind of access this file permits the current user
+ (regardless of the current open modes for this port). ALLOWED is a
+ bitwise OR of O_READ, O_WRITE, and O_EXEC. This is not necessarily the
+ same as what an open or exec would allow; O_EXEC is set for root even if
+ no executable bits are on (in which case file_exec should fail) and
+ O_WRITE is set a directory can be modified, even though it can't be
+ written directly. */
+routine file_check_access (
+ file: file_t;
+ RPT
+ out allowed: int);
+
+/* Notice changes to file FILE. Send notification messages (see
+ fs_notify.defs) to PORT as they occur. */
+routine file_notice_changes (
+ file: file_t;
+ RPT
+ port: mach_port_send_t);
+
+/* Return control port for this filesystem */
+routine file_getcontrol (
+ file: file_t;
+ RPT
+ out control: mach_port_send_t);
+
+/* Return filesystem status */
+routine file_statfs (
+ file: file_t;
+ RPT
+ out info: fsys_statfsbuf_t);
+
+/* Sync the individual file. If OMIT_METADATA is set, then it is only
+necessary for the server to updated the actual contents of the file,
+not any associated metadata. */
+routine file_sync (
+ file: file_t;
+ RPT
+ wait: int;
+ omit_metadata: int);
+
+/* Sync the entire filesystem */
+routine file_syncfs (
+ file: file_t;
+ RPT
+ wait: int;
+ do_children: int);
+
+/* Return information on the storage used to hold this file. See the comment
+ for enum file_storage_class in <hurd/hurd_types.h> the details. */
+routine file_get_storage_info (
+ file: file_t;
+ RPT
+ out ports: portarray_t, dealloc;
+ out ints: intarray_t, dealloc;
+ out offsets: off_array_t, dealloc;
+ out data: data_t, dealloc);
+
+/* Return the node for hard links to this potentially translated file.
+ This returns a potentially unauthenticated node. */
+routine file_getlinknode (
+ file: file_t;
+ RPT
+ out linknode: mach_port_send_t);
+
+/* Return a file handle for this file. This can be used by NFS and such.
+ It is not guaranteed that this call will work...if it doesn't, then this
+ filesystem cannot be NFS mounted. */
+routine file_getfh (
+ file: file_t;
+ RPT
+ out filehandle: data_t, dealloc);
+
+/* Operations supported on directories */
+
+/* Translate a file name, following all symlinks. Upon return, if DO_RETRY
+ is FS_RETRY_MAGICAL then RETRY_NAME specifies what to do, the list
+ of possibilities is documented in <hurd/hurd_types.h>; if
+ FS_RETRY_REAUTH, then RESULT should be reauthenticated before being
+ used. If RETRY_NAME is the empty string and the retry type is
+ FS_RETRY_NORMAL, then no further dir_lookup calls are required;
+ RESULT is the port to use. Otherwise the dir_lookup call should be
+ repeated, sent to RESULT (or the reauthenticated port) with
+ RETRY_NAME passed for FILE_NAME. This call is required to be
+ supported by all files (even non-directories) if the filename is
+ null, and should function in that case as a re-open of the file. */
+routine dir_lookup (
+ start_dir: file_t;
+ RPT
+ file_name: string_t;
+ flags: int;
+ mode: mode_t;
+ out do_retry: retry_type;
+ out retry_name: string_t;
+ out result: mach_port_send_t);
+
+/* Read entries from the directory. Each entry is identified
+ by an index number starting at 0 and running through the file. This
+ call fetches NENTRIES (or any convenient number if NENTRIES is -1)
+ entries starting at ENTRY, returning an array of struct directs in DATA.
+ The number of entries successfully read is returned in AMOUNT. If ENTRY
+ is bigger than the index of the last entry, then 0 is returned in
+ AMOUNT. If BUFSIZE is nonzero, never return more than BUFSIZE bytes of
+ data regardless. */
+routine dir_readdir (
+ dir: file_t;
+ RPT
+ out data: data_t, dealloc[];
+ entry: int;
+ nentries: int;
+ bufsiz: vm_size_t;
+ out amount: int);
+
+/* Create directory */
+routine dir_mkdir (
+ directory: file_t;
+ RPT
+ name: string_t;
+ mode: mode_t);
+
+/* Remove directory */
+routine dir_rmdir (
+ directory: file_t;
+ RPT
+ name: string_t);
+
+/* Remove non-directory */
+routine dir_unlink (
+ directory: file_t;
+ RPT
+ name: string_t);
+
+/* Create a hard link.
+
+ If DIR and FILE are not implemented by the same filesystem,
+ EXDEV should be returned. If the two filesystems, however can
+ inter-operate and guarantee the appropriate Posix semantics, they can
+ communicate by a private protocol and allow hard links between them.
+ If EXCL is set, then fail if NAME already exists in DIR. */
+routine dir_link (
+ dir: file_t;
+ RPT
+ file: file_t;
+ name: string_t;
+ excl: int);
+
+/* Rename file -- comments similar to those for dir_link apply here
+ about EXDEV. If EXCL is set, then fail if NEWNAME already exists in
+ NEWDIRECTORY. */
+routine dir_rename (
+ olddirectory: file_t;
+ RPT
+ oldname: string_t;
+ newdirectory: file_t;
+ newname: string_t;
+ excl: int);
+
+/* Create a new file without linking it into the filesystem. You
+ still must have write permission on the specified directory, even
+ though it will not actually be written. Return in *newnode a port
+ to the file. Flags are the same as for dir_lookup, but
+ O_CREAT and O_TRUNC are assumed even if not specified. */
+routine dir_mkfile (
+ directory: file_t;
+ RPT
+ flags: int;
+ mode: mode_t;
+ out newnode: mach_port_send_t);
+
+/* Notice changes to directory DIR. Send directory change notifications
+ (see fs_notify.defs) to PORT as they occur. */
+routine dir_notice_changes (
+ directory: file_t;
+ RPT
+ port: mach_port_send_t);
+
+/* To get or set the translator currently running on a file, use
+ file_set_translator, file_get_translator, or
+ file_get_translator_cntl on a port gotten with the
+ FS_LOOKUP_NOTRANS flag to dir_lookup. You can send these RPCs
+ to a port to a translated node (looked up without
+ FS_LOOKUP_NOTRANS) to stack a new translator on top of the existing
+ one. */
+
+/* Set a translator for future lookups to a file.
+
+ PASSIVE is the passive translator;
+ ACTIVE is the active translator.
+
+ The FLAGS are FS_TRANS_*, defined in <hurd/hurd_types.h>.
+ OLDFLAGS are sent in an fsys_goaway to an existing active translator
+ if there is one and it is to be killed. */
+routine file_set_translator (
+ file: file_t;
+ RPT
+ passive_flags: int;
+ active_flags: int;
+ oldtrans_flags: int;
+ passive: data_t SCP;
+ active: mach_port_send_t);
+
+/* Return the stored permanent translator for this file. */
+routine file_get_translator (
+ file: file_t;
+ RPT
+ out translator: data_t, dealloc);
+
+/* Return the translator control port to the
+ active translator (if any) for this file. */
+routine file_get_translator_cntl (
+ file: file_t;
+ RPT
+ out translator_cntl: mach_port_send_t);
+
+/* Return the options describing the way the receiving filesystem is
+ running. (Suitable as an arg for fsys_set_options). */
+routine file_get_fs_options (
+ file: file_t;
+ RPT
+ out options: data_t, dealloc);
+
+/* Return a new file, NEW_FILE, with the same semantics as FILE, but
+ with lookups of `..' (if FILE is a directory) redirected to PARENT. */
+routine file_reparent (
+ file: file_t;
+ RPT
+ parent: mach_port_t;
+ out new_file: mach_port_send_t);
+
+/* Return any active translators bound to nodes below FILE. CHILDREN
+ is an argz vector containing file names relative to the root of the
+ receiving translator. */
+routine file_get_children (
+ file: file_t;
+ RPT
+ out children: data_t);
+
+/* Return information about the source of FILE. If the concept of a
+ source is applicable, SOURCE should refer to the source of FILE and
+ should be a description considered appropriate in the context of
+ the translator. For example, if FILE refers to a node on a
+ filesystems, SOURCE should be the file name of the underlying block
+ device. */
+routine file_get_source (
+ file: file_t;
+ RPT
+ out source: string_t);
diff --git a/hurd/fs_notify.defs b/hurd/fs_notify.defs
new file mode 100644
index 00000000..3a30fe6d
--- /dev/null
+++ b/hurd/fs_notify.defs
@@ -0,0 +1,55 @@
+/* Miscellaneous callbacks from Hurd fs servers to their clients.
+ Copyright (C) 1991,92,93,94,95,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+subsystem fs_notify 20500;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef FS_NOTIFY_IMPORTS
+FS_NOTIFY_IMPORTS
+#endif
+
+/* For maximum robustness, the server must not wait for the client to
+ receive the notification message. This is achieved by setting a
+ send timeout (XXX which is implicitely 0 with MACH_MSG_TIMEOUT_NONE). */
+MsgOption MACH_SEND_TIMEOUT;
+
+/* This is sent by a filesystem (after being requested with
+ dir_notice_changes) every time a directory is changed.
+ CHANGE identifies the sort of change that has occurred (see hurd_types.h);
+ NAME is the name that was changed. TICKNO is a sequential number
+ that allows the client to verify that it got all notifications. */
+simpleroutine dir_changed (
+ notify_port: fs_notify_t;
+ tickno: natural_t;
+ change: dir_changed_type_t;
+ name: string_t);
+
+/* This is sent by a filesystem (after being requested with
+ file_notice_changes) every time a file or its stat info is changed.
+ CHANGE identifies the sort of change that has occurred (see hurd_types.h);
+ START and END identify the affected regions of the file's data.
+ TICKNO is a sequential number that allows the client to verify that
+ it got all notifications. */
+simpleroutine file_changed (
+ notify_port: fs_notify_t;
+ tickno: natural_t;
+ change: file_changed_type_t;
+ start: loff_t;
+ end: loff_t);
diff --git a/hurd/fsys.defs b/hurd/fsys.defs
new file mode 100644
index 00000000..b36b9447
--- /dev/null
+++ b/hurd/fsys.defs
@@ -0,0 +1,132 @@
+/* Definitions for the filesystem control interface
+ Copyright (C) 1992,93,94,95,96,97, 2002,13 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+subsystem fsys 22000;
+
+#include <hurd/hurd_types.defs>
+
+/* When modifying this file in any way, please remember to keep
+ fsys_reply.defs up to date. */
+
+/* Note that libdiskfs/start-translator-long.c depends silently on the
+ definition of fsys_startup below. */
+
+#ifdef FSYS_IMPORTS
+FSYS_IMPORTS
+#endif
+
+INTR_INTERFACE
+
+/* Sent by filesystem on its bootstrap port upon startup.
+ REALNODE is the node this filesystem is the translator for,
+ opened with flags FLAGS (O_NOTRANS is assumed even if not
+ provided). */
+routine fsys_startup (
+ bootstrap: mach_port_t;
+ RPT
+ openflags: int;
+ control_port: mach_port_send_t;
+ out realnode: mach_port_send_t);
+
+/* Filesystem should go away. Bye. */
+routine fsys_goaway (
+ fsys: fsys_t;
+ RPT
+ flags: int);
+
+/* Return a file to the root of the filesystem.
+ FLAGS are as for dir_lookup (but O_CREAT and O_EXCL are not
+ meaningful). DO_RETRY, RETRY_NAME, and RESULT are as
+ for dir_lookup. The port should be authenticated with GEN_UIDS
+ and GEN_GIDS (except, of course, for FS_RETRY_REAUTH and
+ FS_RETRY_MAGICAL). DOTDOT_NODE is an unauthenticated port for the
+ directory in which this root is located. */
+routine fsys_getroot(
+ fsys: fsys_t;
+ RPT
+#ifdef FSYS_GETROOT_UREPLY
+ ureplyport ureply: mig_reply_port_t;
+#endif
+ dotdot_node: mach_port_send_t;
+ gen_uids: idarray_t;
+ gen_gids: idarray_t;
+ flags: int;
+ out do_retry: retry_type;
+ out retry_name: string_t;
+ out file: mach_port_send_t);
+
+/* Get a file given a file handle (see file_getfh). */
+routine fsys_getfile (
+ fsys: fsys_t;
+ RPT
+ gen_uids: idarray_t;
+ gen_gids: idarray_t;
+ filehandle: data_t;
+ out file: mach_port_send_t);
+
+/* Sync a filesystem. Args are the same as for file_syncfs in fs.defs. */
+routine fsys_syncfs (
+ fsys: fsys_t;
+ RPT
+ wait: int;
+ do_children: int);
+
+/* Pass a server-specific options string. This usually includes flags
+ similar to command line options, e.g., --readonly, or --sync=30. */
+routine fsys_set_options (
+ fsys: fsys_t;
+ RPT
+ options: data_t;
+ do_children: int);
+
+/* The following two calls are only implemented by bootstrap filesystems. */
+
+routine fsys_getpriv (
+ fsys: fsys_t;
+ RPT
+ out host_priv: mach_port_send_t;
+ out device_master: mach_port_send_t;
+ out fstask: mach_port_send_t);
+
+routine fsys_init (
+ fsys: fsys_t;
+ sreplyport reply_port: sreply_port_t;
+ proc_server: mach_port_send_t;
+ auth_handle: auth_t);
+
+/* Ask SERVER to provide fsys translation service for us. REQUESTOR is
+ the bootstrap port supplied to the original translator, and ARGV are
+ the command line arguments. If the recipient accepts the request, he
+ (or some delegate) should send fsys_startup to REQUESTOR to start the
+ filesystem up. */
+routine fsys_forward (
+ server: mach_port_t;
+ RPT
+ requestor: mach_port_send_t;
+ argv: data_t);
+
+/* Return the options describing the operation of the receiving
+ filesystem (sutiable for fsys_set_options). */
+routine fsys_get_options (
+ server: fsys_t;
+ RPT
+ out options: data_t, dealloc);
+
+skip; /* Was fsys_get_children */
+skip; /* Was fsys_get_source */
diff --git a/hurd/fsys_reply.defs b/hurd/fsys_reply.defs
new file mode 100644
index 00000000..89fb3a4b
--- /dev/null
+++ b/hurd/fsys_reply.defs
@@ -0,0 +1,88 @@
+/* Reply half of fsys
+
+ Copyright (C) 1991, 1993, 1994, 1995, 2001, 2007, 2013
+ Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Roland McGrath. */
+
+subsystem fsys_reply 22100; /* must match fsys.defs + 100 */
+
+#include <hurd/hurd_types.defs>
+
+type reply_port_t = polymorphic | MACH_MSG_TYPE_PORT_SEND_ONCE
+ ctype: mach_port_t;
+
+simpleroutine fsys_startup_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ realnode: mach_port_send_t);
+
+simpleroutine fsys_goaway_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine fsys_getroot_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ do_retry: retry_type;
+ retry_name: string_t;
+ file: mach_port_send_t);
+
+simpleroutine fsys_getfile_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ file: mach_port_send_t);
+
+simpleroutine fsys_syncfs_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine fsys_set_options_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine fsys_getpriv_reply (
+ reply_port_: reply_port_t;
+ RETURN_CODE_ARG;
+ host_priv: mach_port_send_t;
+ device_master: mach_port_send_t;
+ fstask: mach_port_send_t);
+
+simpleroutine fsys_init_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine fsys_forward_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine fsys_get_options_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ options: data_t);
+
+simpleroutine fsys_get_children_reply (
+ server: fsys_t;
+ RETURN_CODE_ARG;
+ children: data_t);
+
+simpleroutine fsys_get_source_reply (
+ server: fsys_t;
+ RETURN_CODE_ARG;
+ source: string_t);
diff --git a/hurd/gensym.awk b/hurd/gensym.awk
new file mode 100644
index 00000000..6c6e1a17
--- /dev/null
+++ b/hurd/gensym.awk
@@ -0,0 +1,77 @@
+#
+# Copyright (c) 1994 The University of Utah and
+# the Computer Systems Laboratory (CSL). All rights reserved.
+#
+# Permission to use, copy, modify and distribute this software and its
+# documentation is hereby granted, provided that both the copyright
+# notice and this permission notice appear in all copies of the
+# software, derivative works or modified versions, and any portions
+# thereof, and that both notices appear in supporting documentation.
+#
+# THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+# IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+# ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+#
+# CSL requests users of this software to return to csl-dist@cs.utah.edu any
+# improvements that they make and grant CSL redistribution rights.
+#
+# Author: Bryan Ford, University of Utah CSL
+#
+
+BEGIN {
+ bogus_printed = "no"
+}
+
+# Start the bogus function just before the first sym directive,
+# so that any #includes higher in the file don't get stuffed inside it.
+/^[a-z]/ {
+ if (bogus_printed == "no")
+ {
+ print "void bogus() {";
+ bogus_printed = "yes";
+ }
+}
+
+# Take an arbitrarily complex C symbol or expression and constantize it.
+/^expr/ {
+ print "__asm (\"";
+ if ($3 == "")
+ printf "* %s mAgIc%%0\" : : \"i\" (%s));\n", $2, $2;
+ else
+ printf "* %s mAgIc%%0\" : : \"i\" (%s));\n", $3, $2;
+}
+
+# Output a symbol defining the size of a C structure.
+/^size/ {
+ print "__asm (\"";
+ if ($4 == "")
+ printf "* %s_SIZE mAgIc%%0\" : : \"i\" (sizeof(struct %s)));\n",
+ toupper($3), $2;
+ else
+ printf "* %s mAgIc%%0\" : : \"i\" (sizeof(struct %s)));\n",
+ $4, $2;
+}
+
+# Output a symbol defining the byte offset of an element of a C structure.
+/^offset/ {
+ print "__asm (\"";
+ if ($5 == "")
+ {
+ printf "* %s_%s mAgIc%%0\" : : \"i\" (&((struct %s*)0)->%s));\n",
+ toupper($3), toupper($4), $2, $4;
+ }
+ else
+ {
+ printf "* %s mAgIc%%0\" : : \"i\" (&((struct %s*)0)->%s));\n",
+ toupper($5), $2, $4;
+ }
+}
+
+# Copy through all preprocessor directives.
+/^#/ {
+ print
+}
+
+END {
+ print "}"
+}
diff --git a/hurd/hurd_types.defs b/hurd/hurd_types.defs
new file mode 100644
index 00000000..129a68cf
--- /dev/null
+++ b/hurd/hurd_types.defs
@@ -0,0 +1,268 @@
+/* MiG type declarations for Hurd interfaces -*- C -*-
+ Copyright (C) 1993,94,95,96,98,2001,02 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <mach/std_types.defs>
+#include <mach/mach_types.defs>
+#include <device/device_types.defs>
+
+type file_t = mach_port_copy_send_t
+#ifdef FILE_INTRAN
+intran: FILE_INTRAN
+#endif
+#ifdef FILE_OUTTRAN
+outtran: FILE_OUTTRAN
+#endif
+#ifdef FILE_DESTRUCTOR
+destructor: FILE_DESTRUCTOR
+#endif
+;
+
+type fsys_t = mach_port_copy_send_t
+#ifdef FSYS_INTRAN
+intran: FSYS_INTRAN
+#endif
+#ifdef FSYS_OUTTRAN
+outtran: FSYS_OUTTRAN
+#endif
+#ifdef FSYS_DESTRUCTOR
+destructor: FSYS_DESTRUCTOR
+#endif
+;
+
+
+type io_t = mach_port_copy_send_t
+#ifdef IO_INTRAN
+intran: IO_INTRAN
+#endif
+#ifdef IO_OUTTRAN
+outtran: IO_OUTTRAN
+#endif
+#ifdef IO_DESTRUCTOR
+destructor: IO_DESTRUCTOR
+#endif
+;
+
+type process_t = mach_port_copy_send_t
+#ifdef PROCESS_INTRAN
+intran: PROCESS_INTRAN
+#endif
+#ifdef PROCESS_OUTTRAN
+outtran: PROCESS_OUTTRAN
+#endif
+#ifdef PROCESS_DESTRUCTOR
+destructor: PROCESS_DESTRUCTOR
+#endif
+;
+
+type auth_t = mach_port_copy_send_t
+#ifdef AUTH_INTRAN
+intran: AUTH_INTRAN
+#endif
+#ifdef AUTH_OUTTRAN
+outtran: AUTH_OUTTRAN
+#endif
+#ifdef AUTH_DESTRUCTOR
+destructor: AUTH_DESTRUCTOR
+#endif
+;
+
+type socket_t = mach_port_copy_send_t
+#ifdef SOCKET_INTRAN
+intran: SOCKET_INTRAN
+#endif
+#ifdef SOCKET_OUTTRAN
+outtran: SOCKET_OUTTRAN
+#endif
+#ifdef SOCKET_DESTRUCTOR
+destructor: SOCKET_DESTRUCTOR
+#endif
+;
+
+/* Protocol family */
+type pf_t = mach_port_copy_send_t
+#ifdef PF_INTRAN
+intran: PF_INTRAN
+#endif
+#ifdef PF_OUTTRAN
+outtran: PF_OUTTRAN
+#endif
+#ifdef PF_DESTRUCTOR
+destructor: PF_DESTRUCTOR
+#endif
+;
+
+type addr_port_t = mach_port_copy_send_t
+#ifdef ADDRPORT_INTRAN
+intran: ADDRPORT_INTRAN
+#endif
+#ifdef ADDRPORT_OUTTRAN
+outtran: ADDRPORT_OUTTRAN
+#endif
+#ifdef ADDRPORT_DESTRUCTOR
+destructor: ADDRPORT_DESTRUCTOR
+#endif
+;
+
+type term_t = mach_port_copy_send_t
+#ifdef TERM_INTRAN
+intran: TERM_INTRAN
+#endif
+#ifdef TERM_OUTTRAN
+outtran: TERM_OUTTRAN
+#endif
+#ifdef TERM_DESTRUCTOR
+destructor: TERM_DESTRUCTOR
+#endif
+;
+
+type startup_t = mach_port_copy_send_t
+#ifdef STARTUP_INTRAN
+intran: STARTUP_INTRAN
+#endif
+#ifdef STARTUP_OUTTRAN
+outtran: STARTUP_OUTTRAN
+#endif
+#ifdef STARTUP_DESTRUCTOR
+destructor: STARTUP_DESTRUCTOR
+#endif
+;
+
+type fs_notify_t = mach_port_copy_send_t
+#ifdef FS_NOTIFY_INTRAN
+intran: FS_NOTIFY_INTRAN
+#endif
+#ifdef FS_NOTIFY_OUTTRAN
+outtran: FS_NOTIFY_OUTTRAN
+#endif
+#ifdef FS_NOTIFY_DESTRUCTOR
+destructor: FS_NOTIFY_DESTRUCTOR
+#endif
+;
+
+type exec_startup_t = mach_port_copy_send_t
+#ifdef EXEC_STARTUP_INTRAN
+intran: EXEC_STARTUP_INTRAN
+#endif
+#ifdef EXEC_STARTUP_OUTTRAN
+outtran: EXEC_STARTUP_OUTTRAN
+#endif
+#ifdef EXEC_STARTUP_DESTRUCTOR
+destructor: EXEC_STARTUP_DESTRUCTOR
+#endif
+;
+
+type interrupt_t = mach_port_copy_send_t
+#ifdef INTERRUPT_INTRAN
+intran: INTERRUPT_INTRAN
+#endif
+#ifdef INTERRUPT_OUTTRAN
+outtran: INTERRUPT_OUTTRAN
+#endif
+#ifdef INTERRUPT_DESTRUCTOR
+destructor: INTERRUPT_DESTRUCTOR
+#endif
+;
+
+
+type proccoll_t = mach_port_copy_send_t;
+
+type sreply_port_t = MACH_MSG_TYPE_MAKE_SEND_ONCE | polymorphic
+ ctype: mach_port_t;
+
+/* These macros are used in some .defs files so that every routine has a
+ server reply port argument #ifdef REPLY_PORTS. */
+#ifdef REPLY_PORTS
+#define RPTDECL sreplyport reply: sreply_port_t
+#define RPT RPTDECL;
+#define RPTLAST ; RPTDECL
+#else
+#define RPTLAST
+#define RPT
+#endif
+
+/* This macros are used in some .defs files so that every out data_t
+ (or equivalent) has a servercopy keyword #ifdef SERVERCOPY. */
+#ifdef SERVERCOPY
+#define SCP , servercopy
+#else
+#define SCP
+#endif
+
+#ifdef HAVE_MIG_RETCODE
+#define RETURN_CODE_ARG in return_code: kern_return_t, retcode
+#else
+#define RETURN_CODE_ARG in return_code: kern_return_t
+#endif
+
+
+#ifdef USERPREFIX
+userprefix USERPREFIX;
+#endif
+
+#ifdef SERVERPREFIX
+serverprefix SERVERPREFIX;
+#endif
+
+/* RPC interfaces which are interrupt compliant (see interrupt.defs)
+ should put ``INTR_INTERFACE'' at the beginning of their .defs file. */
+#ifndef INTR_INTERFACE
+#define INTR_INTERFACE /* Nothing special. */
+#endif
+
+type data_t = array[] of char;
+type string_t = c_string[1024]; /* XXX */
+type io_statbuf_t = struct[32] of int;
+type uid_t = unsigned32;
+type gid_t = unsigned32;
+type mode_t = unsigned32;
+type retry_type = unsigned32;
+type pid_t = int32;
+type wait_status_t = int32;
+type loff_t = int64;
+type ino64_t = int64;
+type file_changed_type_t = unsigned32;
+type dir_changed_type_t = unsigned32;
+
+type portarray_t = array[] of mach_port_send_t;
+type intarray_t = array[] of int;
+type off_array_t = array[] of loff_t;
+
+type pidarray_t = array[] of pid_t;
+type procinfo_t = array[] of int;
+type fsys_statfsbuf_t=struct[22] of int;
+
+type idarray_t = array[] of uid_t;
+
+type rusage_t = struct[18] of int; /* XXX */
+
+type flock_t = struct[5] of int;
+
+type timespec_t = struct[2] of int;
+
+#define _SYS_UTSNAME_H /* Inhibit warning from <bits/utsname.h>. */
+#include <bits/utsname.h>
+type utsname_t = struct[5 * _UTSNAME_LENGTH] of char;
+
+import <sys/types.h>;
+import <sys/stat.h>;
+import <sys/statfs.h>;
+import <sys/resource.h>;
+import <sys/utsname.h>;
+import <hurd/hurd_types.h>;
diff --git a/hurd/hurd_types.h b/hurd/hurd_types.h
new file mode 100644
index 00000000..8eac2060
--- /dev/null
+++ b/hurd/hurd_types.h
@@ -0,0 +1,374 @@
+/* C declarations for Hurd server interfaces
+ Copyright (C) 1993,94,95,96,98,99,2001,02 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _HURD_TYPES_H
+#define _HURD_TYPES_H
+
+#include <time.h> /* For struct timespec. */
+#include <mach/std_types.h> /* For mach_port_t et al. */
+#include <mach/message.h> /* For mach_msg_id_t et al. */
+#include <sys/types.h> /* For pid_t and uid_t. */
+
+/* A string identifying this release of the GNU Hurd. Our
+ interpretation of the term "release" is that it refers to a set of
+ server interface definitions. A "version" in Posix terminology is
+ a distribution of the Hurd; there may be more than one distribution
+ without changing the release number. */
+#define HURD_RELEASE "0.0"
+
+
+/* Simple type declarations */
+
+/* These types identify certain kinds of ports used by the Hurd. */
+typedef mach_port_t file_t;
+typedef mach_port_t fsys_t;
+typedef mach_port_t io_t;
+typedef mach_port_t process_t;
+typedef mach_port_t auth_t;
+typedef mach_port_t socket_t;
+typedef mach_port_t pf_t; /* Protocol family */
+typedef mach_port_t addr_port_t;
+typedef mach_port_t startup_t;
+typedef mach_port_t fs_notify_t;
+typedef mach_port_t exec_startup_t;
+typedef mach_port_t interrupt_t;
+typedef mach_port_t proccoll_t;
+
+#include <errno.h> /* Defines `error_t'. */
+
+/* These names exist only because of MiG deficiencies.
+ You should not use them in C source; use the normal C types instead. */
+typedef char *data_t;
+typedef char string_t [1024];
+typedef int *intarray_t;
+typedef int *fd_mask_t;
+typedef mach_port_t *portarray_t;
+typedef pid_t *pidarray_t;
+typedef uid_t *idarray_t;
+typedef loff_t *off_array_t;
+typedef struct rusage rusage_t;
+typedef struct flock flock_t;
+typedef struct utsname utsname_t;
+#if _FILE_OFFSET_BITS == 64
+typedef struct stat io_statbuf_t;
+typedef struct statfs fsys_statfsbuf_t;
+#else
+typedef struct stat64 io_statbuf_t;
+typedef struct statfs64 fsys_statfsbuf_t;
+#endif
+typedef struct timespec timespec_t;
+
+
+/* Parameters and flags in RPC calls */
+
+/* Many such parameters and flags are also defined in various libc
+ headers. */
+
+/* Bits for flags in fs.defs:file_exec and exec.defs:exec_* calls: */
+#define EXEC_NEWTASK 0x00000001 /* Create new task; kill old one. */
+#define EXEC_SECURE 0x00000002 /* Use secure values of portarray, etc. */
+#define EXEC_DEFAULTS 0x00000004 /* Use defaults for unspecified ports. */
+#define EXEC_SIGTRAP 0x00000008 /* Simulate SIGTRAP on startup. */
+/* This flag is passed through by the exec server but not examined by it. */
+#define EXEC_STACK_ARGS 0x00000010 /* Use arguments from stack, not RPC. */
+
+/* Bits for flags in fs.defs:file_set_translator call: */
+#define FS_TRANS_FORCE 0x00000001 /* Must use translator(no sht circuit) */
+#define FS_TRANS_EXCL 0x00000002 /* Don't do it if already translated. */
+#define FS_TRANS_SET 0x00000004 /* Set or clear translator */
+#define FS_TRANS_ORPHAN 0x00000008 /* Orphan the active translator. */
+
+/* Values for retry field in fs.defs:dir_lookup call: */
+enum retry_type
+{
+ FS_RETRY_NORMAL = 1, /* Retry normally if retry_name is not null. */
+ FS_RETRY_REAUTH = 2, /* Retry after reauthenticating retry port.
+ Even if the retry name is null, a retry
+ is still necessary with this code after the
+ reauthentication is complete. */
+ FS_RETRY_MAGICAL = 3, /* Retry string is magical. */
+ /* "tty" means controlling tty;
+
+ "fd/%u" means file descriptor N;
+
+ "machtype/..." means replace `machtype' with the numbers in decimal
+ returned by the user's kernel as the cpu_type (N) and
+ cpu_subtype (M) (producing N/M/...) and then retry
+ as for FS_RETRY_NORMAL.
+
+ "/..." means retry "...", but starting from the users root directory.
+ */
+};
+typedef enum retry_type retry_type;
+
+/* Types for fs_notify.defs:dir_changed call: */
+enum dir_changed_type
+{
+ DIR_CHANGED_NULL, /* Always sent first for sync. */
+ DIR_CHANGED_NEW, /* Specified name has been added. */
+ DIR_CHANGED_UNLINK, /* Specified name has been removed. */
+ DIR_CHANGED_RENUMBER, /* Name has been the target of rename. */
+};
+typedef enum dir_changed_type dir_changed_type_t;
+
+/* Types for fs_notify.defs:file_changed call: */
+enum file_changed_type
+{
+ FILE_CHANGED_NULL, /* Always sent first for sync. */
+ FILE_CHANGED_WRITE, /* File data has been written. */
+ FILE_CHANGED_EXTEND, /* File has grown. */
+ FILE_CHANGED_TRUNCATE, /* File has been truncated. */
+ FILE_CHANGED_META, /* Stat information has changed, and none
+ of the previous three apply. Not sent
+ for changes in node times. */
+};
+typedef enum file_changed_type file_changed_type_t;
+
+/* Select types for io.defs:io_select call: */
+#define SELECT_READ 0x00000001
+#define SELECT_WRITE 0x00000002
+#define SELECT_URG 0x00000004
+
+/* Flags for fsys.defs:fsys_goaway. Also, these flags are sent as the
+ oldtrans_flags in fs.defs:file_set_translator to describe how to
+ terminate the old translator. */
+#define FSYS_GOAWAY_NOWAIT 0x00000001 /* Return immediately. */
+#define FSYS_GOAWAY_NOSYNC 0x00000002 /* Don't update physical media. */
+#define FSYS_GOAWAY_FORCE 0x00000004 /* Go away despite current users. */
+#define FSYS_GOAWAY_UNLINK 0x00000008 /* Go away only if non-directory. */
+#define FSYS_GOAWAY_RECURSE 0x00000010 /* Shutdown children too. */
+
+/* Types of ports the terminal driver can run on top of;
+ used in term.defs:term_get_bottom_type. */
+enum term_bottom_type
+{
+ TERM_ON_MACHDEV,
+ TERM_ON_HURDIO,
+ TERM_ON_MASTERPTY,
+};
+
+/* Types of storage, as returned by file_get_storage_info.
+
+ STORAGE_DEVICE is a mach device_t (for random access devices)
+ STORAGE_HURD_FILE is a hurd file_t (as if a file were mapped)
+ STORAGE_TASK is a task_t (the storage is in the vm of the task)
+ STORAGE_MEMORY is a memory object port
+ STORAGE_ZERO is a fixed-size constant source of zeros
+ STORAGE_INTERLEAVE is a set of other storage types interleaved at a fixed
+ interval
+ STORAGE_CONCAT is a set of other storage types concatenated end-to-end
+ STORAGE_LAYER is a set of storage types, representing the same address
+ range; all will be written too, and will be read in turn until one
+ succeeds
+ STORAGE_REMAP is a layer on top of another store that remaps its blocks
+ STORAGE_COPY is a memory snapshot of another store
+ STORAGE_NETWORK means that the file is stored elsewhere on the
+ network; all the remaining fields contan type-specific information.
+ STORAGE_OTHER means none of these apply; and should be used when no
+ meaningful answer can be given
+
+ The vectors returned by file_get_storage_info encode each of the above
+ (note that the first int is always the storage type). There are four:
+ ports, ints, offsets (off_t), and data (char); each type of store uses the
+ following entries in each vector:
+
+ -type- -ports- -ints- -offsets- -data- -kids-
+ device DEVICE TY, FL, BS, NR, NL, ML NR * (OFFS, LEN) NL + ML -
+ file FILE TY, FL, BS, NR, NL, ML NR * (OFFS, LEN) NL + ML -
+ memory MEMOBJ TY, FL, BS, NR, NL, ML NR * (OFFS, LEN) NL + ML -
+ task TASK TY, FL, BS, NR, NL, ML NR * (OFFS, LEN) NL + ML -
+ (the data for the above is a name (incl '\0') and a misc data block)
+ null - TY, FL SIZE - -
+ (BS is 1)
+ ileave - TY, FL, IL, NC - - NC
+ (BS is the LCM of its children; SIZE is the minimum of theirs * IL)
+ concat - TY, FL, NC - - NC
+ (BS is the LCM of its children; SIZE is the sum of theirs)
+ layer - TY, FL, NC - - NC
+ (BS is the LCM of its children; SIZE is the minimum of theirs)
+ remap - TY, FL, NR NR * (OFFS, LEN) - 1
+ (BS and SIZE are that of the child)
+ copy - TY, FL, SIZE - DATA -
+ (DATA is preceded by padding to the next page boundary, and is
+ SIZE bytes long itself)
+
+ For ileave, concat, and layer, the children are encoded following the parent.
+ The first int must always be TY.
+
+ key: TY = type code, FL = flags, BS = block size, NR = num runs,
+ NL = name len, ML = misc len, NC = num children,
+ IL = interleave (bytes), SIZE = Size of storage (blocks),
+ LEN = run length (blocks), OFFS = run offset (blocks),
+
+ The NR * (OFFS, LEN) offsets for some of the types is the set of block
+ ranges in the underlying address space that, concatenated, make up the
+ contents of the storage -- for instance, doing file_get_storage_info on a
+ file may return storage of type STORAGE_DEVICE, and the accompanying block
+ ranges are the set of blocks on the given device that correspond to that
+ file. Any OFFS == -1 designates a hole in the address range. Note that
+ the total size (SIZE) for these types is the sum of their LEN's.
+
+ The optional NAME returned by some types (if NL != 0) is a type specific
+ name for the same object referenced by the port also returned. E.g.:
+ device -- The mach device name
+ file -- The file name (unreliable, as the root may not be the same)
+ task -- The pid
+ Unless it is MACH_PORT_NULL, the port should generally be used instead of
+ trying to regenerate it from the associated name, which is intended more for
+ printing messages, etc. */
+enum file_storage_class
+{
+ STORAGE_OTHER,
+ STORAGE_DEVICE,
+ STORAGE_HURD_FILE,
+ STORAGE_NETWORK,
+ STORAGE_MEMORY,
+ STORAGE_TASK,
+ STORAGE_ZERO,
+ STORAGE_CONCAT,
+ STORAGE_INTERLEAVE,
+ STORAGE_LAYER,
+ STORAGE_REMAP,
+ STORAGE_COPY,
+};
+
+/* Flags for the flags word returned by some types . */
+#define STORAGE_MUTATED 0x00000001 /* data as stored is munged from file */
+
+/* Data types */
+
+#include <mach/task_info.h>
+#include <mach/thread_info.h>
+#ifndef THREAD_SCHED_INFO
+#include <mach/policy.h>
+#endif
+
+/* Flags sent in proc_getprocinfo request. */
+#define PI_FETCH_TASKINFO 0x0001
+#define PI_FETCH_TASKEVENTS 0x0020
+#define PI_FETCH_THREADS 0x0002
+#define PI_FETCH_THREAD_BASIC 0x0004
+#define PI_FETCH_THREAD_SCHED 0x0008
+#define PI_FETCH_THREAD_WAITS 0x0010
+
+struct procinfo
+{
+ int state;
+ uid_t owner;
+ pid_t ppid;
+ pid_t pgrp;
+ pid_t session;
+ pid_t logincollection;
+ int exitstatus;
+ int sigcode;
+
+ int nthreads; /* size of pi_threadinfos */
+
+ struct task_basic_info taskinfo;
+ struct task_events_info taskevents;
+#ifdef TASK_SCHED_TIMESHARE_INFO
+ struct policy_timeshare_base timeshare_base_info;
+#endif
+ struct
+ {
+ int died; /* this thread died in the middle of call */
+ mach_msg_id_t rpc_block; /* thread is blocked on this RPC */
+ struct thread_basic_info pis_bi;
+#ifdef THREAD_SCHED_INFO
+ struct thread_sched_info pis_si;
+#else
+ struct policy_infos pis_pi;
+#endif
+ } threadinfos[0];
+};
+typedef int *procinfo_t;
+
+/* Bits in struct procinfo state: */
+#define PI_STOPPED 0x00000001 /* Proc server thinks is stopped. */
+#define PI_EXECED 0x00000002 /* Has called proc_exec. */
+#define PI_WAITING 0x00000004 /* Process is waiting for child to exit */
+#define PI_ORPHAN 0x00000008 /* Process group is orphaned. */
+#define PI_NOMSG 0x00000010 /* Process has no message port. */
+#define PI_SESSLD 0x00000020 /* Session leader. */
+#define PI_NOTOWNED 0x0000040 /* Process has no owner. */
+#define PI_NOPARENT 0x0000080 /* Hasn't identified a parent. */
+#define PI_ZOMBIE 0x00000100 /* Has no associated task. */
+#define PI_TRACED 0x00000200 /* Process is being traced */
+#define PI_GETMSG 0x00000400 /* Process is blocked in proc_getmsgport. */
+#define PI_LOGINLD 0x00000800 /* Process is leader of login collection */
+
+
+/* Conventions */
+
+
+/* st_fstype in struct stat and fsys_stb_type in fsys_statfsbuf is one of: */
+#define FSTYPE_UFS 0x00000000 /* 4.x BSD Fast File System */
+#define FSTYPE_NFS 0x00000001 /* Network File System ala Sun */
+#define FSTYPE_GFS 0x00000002 /* GNU file system */
+#define FSTYPE_LFS 0x00000003 /* Logging File System ala Sprite */
+#define FSTYPE_SYSV 0x00000004 /* Old U*x filesystem ala System V */
+#define FSTYPE_FTP 0x00000005 /* Transparent FTP */
+#define FSTYPE_TAR 0x00000006 /* Transparent TAR */
+#define FSTYPE_AR 0x00000007 /* Transparent AR */
+#define FSTYPE_CPIO 0x00000008 /* Transparent CPIO */
+#define FSTYPE_MSLOSS 0x00000009 /* MS-DOS */
+#define FSTYPE_CPM 0x0000000a /* CP/M */
+#define FSTYPE_HFS 0x0000000b /* Don't ask */
+#define FSTYPE_DTFS 0x0000000c /* used by desktop to provide more info */
+#define FSTYPE_GRFS 0x0000000d /* GNU Remote File System */
+#define FSTYPE_TERM 0x0000000e /* GNU Terminal driver */
+#define FSTYPE_DEV 0x0000000f /* GNU Special file server */
+#define FSTYPE_PROC 0x00000010 /* /proc filesystem ala Version 9 */
+#define FSTYPE_IFSOCK 0x00000011 /* PF_LOCAL socket naming point */
+#define FSTYPE_AFS 0x00000012 /* Andrew File System 3.xx */
+#define FSTYPE_DFS 0x00000013 /* Distributed File Sys (OSF) == AFS 4.xx */
+#define FSTYPE_PROC9 0x00000014 /* /proc filesystem ala Plan 9 */
+#define FSTYPE_SOCKET 0x00000015 /* io_t that isn't a file but a socket */
+#define FSTYPE_MISC 0x00000016 /* generic trivfs server */
+#define FSTYPE_EXT2FS 0x00000017 /* Linux filesystem by Remy Card */
+#define FSTYPE_HTTP 0x00000018 /* Transparent HTTP */
+#define FSTYPE_MEMFS 0x00000019 /* In-core filesystem */
+#define FSTYPE_ISO9660 0x0000001a /* ISO9660 */
+
+/* Standard port assignments for file_exec and exec_* */
+enum
+ {
+ INIT_PORT_CWDIR,
+ INIT_PORT_CRDIR,
+ INIT_PORT_AUTH,
+ INIT_PORT_PROC,
+ INIT_PORT_CTTYID,
+ /* If MACH_PORT_NULL is given for the bootstrap port,
+ the bootstrap port of the old task is used. */
+ INIT_PORT_BOOTSTRAP,
+ INIT_PORT_MAX
+ };
+
+/* Standard ints for file_exec and exec_* */
+enum
+ {
+ INIT_UMASK,
+ INIT_SIGMASK,
+ INIT_SIGIGN,
+ INIT_SIGPENDING,
+ INIT_TRACEMASK,
+ INIT_INT_MAX,
+ };
+
+#endif
diff --git a/hurd/ifsock.defs b/hurd/ifsock.defs
new file mode 100644
index 00000000..c61f8957
--- /dev/null
+++ b/hurd/ifsock.defs
@@ -0,0 +1,34 @@
+/* S_IFSOCK filesystem node protocol
+ Copyright (C) 1991, 1993, 1994 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+/* This protocol is a superset of the filesystem protocol. */
+
+subsystem ifsock 34000;
+#include <hurd/hurd_types.defs>
+
+#ifdef IFSOCK_IMPORTS
+IFSOCK_IMPORTS
+#endif
+
+routine
+ifsock_getsockaddr (
+ file: file_t;
+ out sockaddr: mach_port_copy_send_t);
diff --git a/hurd/iioctl.defs b/hurd/iioctl.defs
new file mode 100644
index 00000000..dfa89033
--- /dev/null
+++ b/hurd/iioctl.defs
@@ -0,0 +1,183 @@
+/* Definitions for interface ioctls
+ Copyright (C) 2000, 2007 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <hurd/hurd_types.defs>
+
+#ifdef IIOCTL_IMPORTS
+IIOCTL_IMPORTS
+#endif
+
+INTR_INTERFACE
+
+/* Ioctl class `i'; the subsystem is derived from calculations in
+ <ioctls.h>. */
+subsystem iioctl 112000; /* XXX */
+
+import <hurd/ioctl_types.h>; /* XXX */
+
+/* This is the first arg for a struct ifreq_something as specified by the
+ definition of _IOT_ifreq_something in <net/if.h>. */
+type ifname_t = array[16] of char; /* IFNAMSIZ is 16. */
+
+/* This is the second arg of struct ifreq as specified by the
+ definition of _IOT_ifreq in <net/if.h>. */
+type sockaddr_t = struct[16] of char; /* sizeof(struct sockaddr) is 16. */
+
+skip; skip; skip; skip; /* 0 1 2 3 unused */
+skip; skip; skip; skip; /* 4 5 6 7 unused */
+skip; skip; skip; skip; /* 8 9 10 11 unused */
+
+/* 12 SIOCSIFADDR */
+routine iioctl_siocsifaddr (
+ reqport: io_t;
+ ifnam: ifname_t;
+ addr: sockaddr_t);
+
+skip; /* 13 unused */
+
+/* 14 SIOCSIFDSTADDR */
+routine iioctl_siocsifdstaddr (
+ reqport: io_t;
+ ifnam: ifname_t;
+ dstaddr: sockaddr_t);
+
+skip; /* 15 unused */
+
+/* 16 SIOCSIFFLAGS */
+routine iioctl_siocsifflags (
+ reqport: io_t;
+ ifnam: ifname_t;
+ flags: short);
+
+/* 17 SIOCGIFFLAGS */
+routine iioctl_siocgifflags (
+ reqport: io_t;
+ inout ifnam: ifname_t;
+ inout flags: short);
+
+skip; /* 18 unused */
+
+/* 19 SIOCSIFBRDADDR */
+routine iioctl_siocsifbrdaddr (
+ reqport: io_t;
+ ifnam: ifname_t;
+ brdaddr: sockaddr_t);
+
+skip; skip; /* 20 21 unused */
+
+/* 22 SIOCSIFNETMASK */
+routine iioctl_siocsifnetmask (
+ reqport: io_t;
+ ifnam: ifname_t;
+ netmask: sockaddr_t);
+
+/* 23 SIOCGIFMETRIC */
+routine iioctl_siocgifmetric (
+ reqport: io_t;
+ inout ifnam: ifname_t;
+ inout metric: int);
+
+/* 24 SIOCSIFMETRIC */
+routine iioctl_siocsifmetric (
+ reqport: io_t;
+ ifnam: ifname_t;
+ metric: int);
+
+/* 25 SIOCDIFADDR */
+routine iioctl_siocdifaddr (
+ reqport: io_t;
+ ifnam: ifname_t;
+ addr: sockaddr_t);
+
+skip; skip; skip; skip; /* 26 27 28 29 unused */
+skip; skip; skip; /* 30 31 32 unused */
+
+/* 33 SIOCGIFADDR */
+routine iioctl_siocgifaddr (
+ reqport: io_t;
+ inout ifnam: ifname_t;
+ inout addr: sockaddr_t);
+
+/* 34 SIOCGIFDSTADDR */
+routine iioctl_siocgifdstaddr (
+ reqport: io_t;
+ inout ifnam: ifname_t;
+ inout dstaddr: sockaddr_t);
+
+/* 35 SIOCGIFBRDADDR */
+routine iioctl_siocgifbrdaddr (
+ reqport: io_t;
+ inout ifnam: ifname_t;
+ inout brdaddr: sockaddr_t);
+
+skip; /* 36 SIOCGIFCONF -- implemented in C library */
+
+/* 37 SIOCGIFNETMASK */
+routine iioctl_siocgifnetmask (
+ reqport: io_t;
+ inout ifnam: ifname_t;
+ inout netmask: sockaddr_t);
+
+skip; /* 38 SIOCGARP -- Not implemented yet */
+
+routine iioctl_siocgifhwaddr (
+ reqport: io_t;
+ inout ifnam: ifname_t;
+ inout netmask: sockaddr_t);
+
+skip; skip; /* 40, 41 unused */
+skip; skip; skip; skip; /* 42, 43, 44, 45 unused */
+skip; skip; skip; skip; /* 46, 47, 48, 49 unused */
+skip; /* 50 unused */
+
+/* 51 SIOCGIFMTU */
+routine iioctl_siocgifmtu (
+ reqport: io_t;
+ inout ifnam: ifname_t;
+ inout mtu: int);
+
+/* 52 SIOCSIFMTU */
+routine iioctl_siocsifmtu (
+ reqport: io_t;
+ ifnam: ifname_t;
+ mtu: int);
+
+skip; skip; skip; skip; /* 53, 54, 55, 56 unused */
+skip; skip; skip; skip; /* 57, 58, 59, 60 unused */
+skip; skip; skip; skip; /* 61, 62, 63, 64 unused */
+skip; skip; skip; skip; /* 65, 66, 67, 68 unused */
+skip; skip; skip; skip; /* 69, 70, 71, 72 unused */
+skip; skip; skip; skip; /* 73, 74, 75, 76 unused */
+skip; skip; skip; skip; /* 77, 78, 79, 80 unused */
+skip; skip; skip; skip; /* 81, 82, 83, 84 unused */
+skip; skip; skip; skip; /* 85, 86, 87, 88 unused */
+skip; /* 89 unused */
+
+/* 90 SIOCGIFINDEX */
+routine iioctl_siocgifindex (
+ reqport: io_t;
+ inout ifnam: ifname_t;
+ inout index: int);
+
+/* 91 SIOCGIFNAME */
+routine iioctl_siocgifname (
+ reqport: io_t;
+ inout ifnam: ifname_t;
+ inout index: int);
diff --git a/hurd/interrupt.defs b/hurd/interrupt.defs
new file mode 100644
index 00000000..9981aed1
--- /dev/null
+++ b/hurd/interrupt.defs
@@ -0,0 +1,36 @@
+/* Interrup [get the phone, will ya?] tion. -*- C -*-
+ Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+subsystem interrupt 33000;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef INTERRUPT_IMPORTS
+INTERRUPT_IMPORTS
+#endif
+
+/* Cause a pending request on this object to immediately return. The
+ exact semantics are dependent on the specific object. */
+
+routine
+interrupt_operation (object: interrupt_t;
+ waittime timeout: natural_t;
+ msgseqno seqno: mach_port_seqno_t);
diff --git a/hurd/io.defs b/hurd/io.defs
new file mode 100644
index 00000000..ba0b8077
--- /dev/null
+++ b/hurd/io.defs
@@ -0,0 +1,333 @@
+/* Definitions for generic IO interface
+ Copyright (C) 1991,93,94,95,96,99,2001,02,04 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* All changes to this file must be reflected in io_request.defs and
+ io_reply.defs. */
+
+subsystem io 21000;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef IO_IMPORTS
+IO_IMPORTS
+#endif
+
+INTR_INTERFACE
+
+
+/* Write data to an IO object. If offset is -1, write at the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount successfully written is returned in AMOUNT. A
+ given user should not have more than one outstanding io_write on an
+ object at a time; servers implement congestion control by delaying
+ responses to io_write. Servers may drop data (returning ENOBUFS)
+ if they recevie more than one write when not prepared for it. */
+routine io_write (
+ io_object: io_t;
+ RPT
+ data: data_t SCP;
+ offset: loff_t;
+ out amount: vm_size_t);
+
+/* Read data from an IO object. If offset if -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMOUNT. */
+routine io_read (
+ io_object: io_t;
+ RPT
+ out data: data_t, dealloc;
+ offset: loff_t;
+ amount: vm_size_t);
+
+/* Change current read/write offset */
+routine io_seek (
+ io_object: io_t;
+ RPT
+ offset: loff_t;
+ whence: int;
+ out newp: loff_t);
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+routine io_readable (
+ io_object: io_t;
+ RPT
+ out amount: vm_size_t);
+
+/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and
+ O_NONBLOCK bits for the IO object. In addition, io_get_openmodes
+ will tell you which of O_READ, O_WRITE, and O_EXEC the object can
+ be used for. The O_ASYNC bit affects icky async I/O; good async
+ I/O is done through io_async which is orthogonal to these calls. */
+routine io_set_all_openmodes (
+ io_object: io_t;
+ RPT
+ newbits: int);
+
+routine io_get_openmodes (
+ io_object: io_t;
+ RPT
+ out bits: int);
+
+routine io_set_some_openmodes (
+ io_object: io_t;
+ RPT
+ bits_to_set: int);
+
+routine io_clear_some_openmodes (
+ io_object: io_t;
+ RPT
+ bits_to_clear: int);
+
+/* This requests that the IO object send SIGIO and SIGURG signals,
+ when appropriate, to the designated port using sig_post. A
+ port is also returned which will be used as the reference port in
+ sending such signals (this is the "async IO ID" port). The async
+ call is cancelled by deleting all references to the async_id_port.
+ Each call to io_async generates a new ASYNC_ID_PORT.
+ */
+routine io_async (
+ io_object: io_t;
+ RPT
+ notify_port: mach_port_send_t;
+ out async_id_port: mach_port_send_t);
+
+/* Get/set the owner of the IO object. For terminals, this affects
+ controlling terminal behavior (see term_become_ctty). For all
+ objects this affects old-style async IO. Negative values represent
+ pgrps. This has nothing to do with the owner of a file (as
+ returned by io_stat, and as used for various permission checks by
+ filesystems). An owner of 0 indicates that there is no owner. */
+routine io_mod_owner (
+ io_object: io_t;
+ RPT
+ owner: pid_t);
+
+routine io_get_owner (
+ io_object: io_t;
+ RPT
+ out owner: pid_t);
+
+/* This provides "old style" async IO. This is deprecated, and
+ provided only for backward compatibility with 4.3 BSD. This
+ implements a per-object (not per-open) flag controlling old-style
+ async mode (O_ASYNC). If the flag is set, then the IO object will
+ send SIGIO and SIGURG signals (in precisely the same circumstances
+ as io_async) to the current owner (pid or pgrp) as set by
+ io_set_own. The reference port for the signal sends is the
+ icky_async_id_port returned by this call; it is up to the caller to
+ communicate this to potential recipients of the signal. (Such
+ communication needs to be done both by the caller of the call and
+ the caller of io_mod_owner, in order to get the BSD functionality.)
+ One async_id_port is shared by all users of io_get_icky_async_id. */
+/* Fetch the current old-style async ID port. */
+routine io_get_icky_async_id (
+ io_object: io_t;
+ RPT
+ out icky_async_id_port: mach_port_send_t);
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. */
+/* INTR */
+routine io_select (
+ io_object: io_t;
+#if defined (REPLY_PORTS) || defined (IO_SELECT_REPLY_PORT)
+ replyport reply: sreply_port_t;
+#else
+ ureplyport reply: mach_port_make_send_t;
+#endif
+ waittime timeout: natural_t;
+ inout select_type: int);
+
+/* Return the current status of the object. Not all the fields of the
+ io_statuf_t are meaningful for all objects; however, the access and
+ modify times, the optimal IO size, and the fs type are meaningful
+ for all objects. */
+routine io_stat (
+ stat_object: io_t;
+ RPT
+ out stat_info: io_statbuf_t);
+
+/* Get a reauthenticated port to an io object. The user should follow
+ this with a call to auth_user_authenticate. The new_port passed
+ through the auth server will be a port usable with the new
+ authentication. */
+simpleroutine io_reauthenticate (
+ auth_object: io_t;
+ RPT
+ rendezvous2: mach_port_send_t);
+
+/* Return another port which has been restricted to do only those
+ things which both the current user and the newly specified user can
+ do. */
+routine io_restrict_auth (
+ io_object: io_t;
+ RPT
+ out new_object: mach_port_send_t;
+ uids: idarray_t SCP;
+ gids: idarray_t SCP);
+
+/* Return a new port with the same semantics as the existing port. */
+routine io_duplicate (
+ io_object: io_t;
+ RPT
+ out newport: mach_port_send_t);
+
+/* Get version information about the server exporting the IO object. */
+routine io_server_version (
+ vers_object: io_t;
+ RPT
+ out server_name: string_t;
+ out server_major_version: int;
+ out server_minor_version: int;
+ out server_edit_level: int);
+
+/* Definitions for mapped io */
+
+/* Return objects mapping the data underlying this memory object. If
+ the object can be read then memobjrd will be provided; if the
+ object can be written then memobjwr will be provided. For objects
+ where read data and write data are the same, these objects will be
+ equal, otherwise they will be disjoint. Servers are permitted to
+ implement io_map but not io_map_cntl. Some objects do not provide
+ mapping; they will set none of the ports and return an error. Such
+ objects can still be accessed by io_read and io_write. */
+routine io_map (
+ io_object: io_t;
+ RPT
+ out memobjrd: mach_port_send_t;
+ out memobjwt: mach_port_send_t);
+
+/* This call can only be made once per request port. If it returns
+ EBUSY, then the user should duplicate the port (using io_duplicate)
+ and try again. This maps the shared page data structures
+ corresponding to the data maps in io_map. The format and meaning
+ of the shared page is described in shared.h and the calls below.
+ This call may be unimplemented by some servers; they will return
+ EOPNOTSUPP.
+ */
+routine io_map_cntl (
+ io_object: io_t;
+ RPT
+ out memobj: mach_port_send_t);
+
+/* Users of the shared page who don't have the conch and want it
+ should call this function. The server will endeavor to have
+ USER_HAS_CONCH when this returns, but users should call io_get_it
+ in a loop for safety. */
+routine io_get_conch (
+ io_object: io_t RPTLAST);
+
+/* When the user is done with the shared page, while holding the
+ conch, the filesystem may have changed the conch status to
+ USER_RELEASE_CONCH. In that case, rather than downgrading
+ USER_HAS_CONCH to USER_COULD_HAVE_CONCH, the user should call
+ io_release_conch. Also, when the user is done with an IO object
+ and wants permanent characteristics of the object (like file size)
+ to be updated, the user should call io_release_conch. Upon return,
+ the conch status might be either USER_COULD_HAVE_CONCH or
+ USER_HAS_NOT_CONCH. */
+routine io_release_conch (
+ io_object: io_t RPTLAST);
+
+/* This routine should be called while the user has the conch, after
+ the user has encountered an eof condition (where the file pointer
+ is equal to the file size). This could be used by terminals, for
+ example, to clear the eof condition after it is read once. The
+ routine should be called while the user has the conch. The user
+ will keep it upon return. */
+routine io_eofnotify (
+ io_object: io_t RPTLAST);
+
+/* If the user wants to write past the prenotify size, a call needs to
+ be made to io_prenotify giving the paramters of the write. Upon
+ return from io_prenotify, there is no guarantee that the prenotify
+ size will now permit the write, so it should be re-checked. The
+ routine should be called while the user has the conch. The user
+ will keep it upon return. */
+routine io_prenotify (
+ io_object: io_t;
+ RPT
+ write_start: vm_offset_t;
+ write_end: vm_offset_t);
+
+/* After doing a write which extends past the postnotify_size, the
+ user needs to call io_postnotify. The routine should be called
+ while the user has the conch. The user will keep it upon return.
+ */
+routine io_postnotify (
+ io_object: io_t;
+ RPT
+ write_start: vm_offset_t;
+ write_end: vm_offset_t);
+
+/* After moving rd_file_pointer past readnotify_size, the user should
+ call this routine, while holding the conch. The user will keep the
+ conch upon return. */
+routine io_readnotify (
+ io_object: io_t RPTLAST);
+
+/* This routine sleeps until the read_size is increased. The routine
+ should be called while the user has the conch. The user will keep
+ it upon return. */
+routine io_readsleep (
+ io_object: io_t RPTLAST);
+
+/* The shared user has just done some IO, and a signal needs to be
+ sent for async users. */
+routine io_sigio (
+ io_object: io_t RPTLAST);
+
+/* Return Posix.1 pathconf information. */
+routine io_pathconf (
+ io_object: io_t;
+ RPT
+ name: int;
+ out value: int);
+
+/* Return the identity port for the object underlying IO_OBJECT.
+ Different I/O ports for the same object all have the same identity port.
+ FSIDPORT is an identity port returned by for all objects in the same
+ filesystem. FILENO is the same value returned by io_stat for `st_ino'.
+ */
+routine io_identity (
+ io_object: io_t;
+ RPT
+ out idport: mach_port_send_t;
+ out fsidport: mach_port_send_t;
+ out fileno: ino64_t);
+
+/* Revoke the access of all descriptors except this one currently open
+ on the specified object. */
+routine io_revoke (
+ io_object: io_t RPTLAST);
+
+/* INTR */
+routine io_select_timeout (
+ io_object: io_t;
+#if defined (REPLY_PORTS) || defined (IO_SELECT_REPLY_PORT)
+ replyport reply: sreply_port_t;
+#else
+ ureplyport reply: mach_port_make_send_t;
+#endif
+ timeout: timespec_t;
+ inout select_type: int);
diff --git a/hurd/io_reply.defs b/hurd/io_reply.defs
new file mode 100644
index 00000000..eee68244
--- /dev/null
+++ b/hurd/io_reply.defs
@@ -0,0 +1,182 @@
+/* Definitions for generic IO interface
+ Copyright (C) 1991,93,94,95,2000,01,02 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+subsystem io_reply 21100; /* much mach io.defs + 100 */
+
+/* These functions are server reply stubs for the functions in io.defs;
+ all the comments there apply exactly to here. */
+
+#include <hurd/hurd_types.defs>
+
+#ifdef IO_IMPORTS
+IO_IMPORTS
+#endif
+
+type reply_port_t = polymorphic | MACH_MSG_TYPE_MAKE_SEND_ONCE
+ ctype: mach_port_t;
+
+simpleroutine io_write_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ amount: vm_size_t);
+
+simpleroutine io_read_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ data: data_t);
+
+simpleroutine io_seek_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ newp: loff_t);
+
+simpleroutine io_readable_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ amount: vm_size_t);
+
+simpleroutine io_set_all_openmodes_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine io_get_openmodes_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ bits: int);
+
+simpleroutine io_set_some_openmodes_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine io_clear_some_openmodes_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine io_async_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ async_id_port: mach_port_send_t);
+
+simpleroutine io_mod_owner_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine io_get_owner_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ owner: pid_t);
+
+simpleroutine io_get_icky_async_id_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ id_port: mach_port_send_t);
+
+simpleroutine io_select_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ select_result: int);
+
+simpleroutine io_stat_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ stat_info: io_statbuf_t);
+
+skip; /* io_reauthenticate has no reply */
+
+simpleroutine io_restrict_auth_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ new_object: mach_port_send_t);
+
+simpleroutine io_duplicate_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ newport: mach_port_send_t);
+
+simpleroutine io_server_version_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ server_name: string_t;
+ server_major_version: int;
+ server_minor_version: int;
+ server_edit_level: int);
+
+simpleroutine io_map_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ rdobject: mach_port_send_t;
+ wrobject: mach_port_send_t);
+
+simpleroutine io_map_cntl_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ memobj: mach_port_send_t);
+
+simpleroutine io_get_conch_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine io_release_conch_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine io_eofnotify_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine io_prenotify_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine io_postnotify_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine io_readnotify_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine io_readsleep_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine io_sigio_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine io_pathconf_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ value: int);
+
+simpleroutine io_identity_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ idport: mach_port_send_t;
+ fsidport: mach_port_send_t;
+ fileno: ino64_t);
+
+simpleroutine io_revoke_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG);
+
+simpleroutine io_select_timeout_reply (
+ reply: reply_port_t;
+ RETURN_CODE_ARG;
+ select_result: int);
diff --git a/hurd/io_request.defs b/hurd/io_request.defs
new file mode 100644
index 00000000..0d5e36dd
--- /dev/null
+++ b/hurd/io_request.defs
@@ -0,0 +1,180 @@
+/* Definitions for generic IO interface
+ Copyright (C) 1991,93,94,95,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+subsystem io 21000;
+
+/* These functions are user-side stubs for the functions in io.defs;
+ all the comments there apply exactly to here. */
+
+#include <hurd/hurd_types.defs>
+
+#ifdef IO_IMPORTS
+IO_IMPORTS
+#endif
+
+type reply_port_t = MACH_MSG_TYPE_MAKE_SEND_ONCE | polymorphic
+ ctype: mach_port_t;
+
+simpleroutine io_write_request (
+ io_object: io_t;
+ reply: reply_port_t;
+ data: data_t;
+ offset: loff_t);
+
+simpleroutine io_read_request (
+ io_object: io_t;
+ reply: reply_port_t;
+ offset: loff_t;
+ amount: vm_size_t);
+
+simpleroutine io_seek_request (
+ io_object: io_t;
+ reply: reply_port_t;
+ offset: loff_t;
+ whence: int);
+
+simpleroutine io_readable_request (
+ io_object: io_t;
+ reply: reply_port_t);
+
+simpleroutine io_set_all_openmodes_request (
+ io_object: io_t;
+ reply: reply_port_t;
+ newbits: int);
+
+simpleroutine io_get_openmodes_request (
+ io_object: io_t;
+ reply: reply_port_t);
+
+simpleroutine io_set_some_openmodes_request (
+ io_object: io_t;
+ reply: reply_port_t;
+ bits_to_set: int);
+
+simpleroutine io_clear_some_openmodes_request (
+ io_object: io_t;
+ reply: reply_port_t;
+ bits_to_clear: int);
+
+simpleroutine io_async_request (
+ io_object: io_t;
+ reply: reply_port_t;
+ notify_port: mach_port_send_t);
+
+simpleroutine io_mod_owner_request (
+ io_object: io_t;
+ reply: reply_port_t;
+ owner: pid_t);
+
+simpleroutine io_get_owner_request (
+ io_object: io_t;
+ reply: reply_port_t);
+
+simpleroutine io_get_icky_async_id_request (
+ io_object: io_t;
+ reply: reply_port_t);
+
+simpleroutine io_select_request (
+ io_object: io_t;
+ ureplyport reply: mach_port_make_send_t;
+ select_type: int);
+
+simpleroutine io_stat_request (
+ stat_object: io_t;
+ reply: reply_port_t);
+
+/* io_reauthenticate is a simpleroutine already. */
+skip;
+
+simpleroutine io_restrict_auth_request (
+ io_object: io_t;
+ reply: reply_port_t;
+ uids: idarray_t;
+ gids: idarray_t);
+
+simpleroutine io_duplicate_request (
+ io_object: io_t;
+ reply: reply_port_t);
+
+simpleroutine io_server_version_request (
+ vers_object: io_t;
+ reply: reply_port_t);
+
+simpleroutine io_map_request (
+ io_object: io_t;
+ reply: reply_port_t);
+
+simpleroutine io_map_cntl_request (
+ io_object: io_t;
+ reply: reply_port_t);
+
+simpleroutine io_get_conch_request (
+ io_object: io_t;
+ reply: reply_port_t);
+
+simpleroutine io_release_conch_request (
+ io_object: io_t;
+ reply: reply_port_t);
+
+simpleroutine io_eofnotify_request (
+ io_object: io_t;
+ reply: reply_port_t);
+
+simpleroutine io_prenotify_request (
+ io_object: io_t;
+ reply: reply_port_t;
+ write_start: vm_offset_t;
+ write_end: vm_offset_t);
+
+simpleroutine io_postnotify_request (
+ io_object: io_t;
+ reply: reply_port_t;
+ write_start: vm_offset_t;
+ write_end: vm_offset_t);
+
+simpleroutine io_readnotify_request (
+ io_object: io_t;
+ reply: reply_port_t);
+
+simpleroutine io_readsleep_request (
+ io_object: io_t;
+ reply: reply_port_t);
+
+simpleroutine io_sigio_request (
+ io_object: io_t;
+ reply: reply_port_t);
+
+simpleroutine io_pathconf_request (
+ io_object: io_t;
+ reply: reply_port_t;
+ name: int);
+
+simpleroutine io_identity_request (
+ io_object: io_t;
+ reply: reply_port_t);
+
+simpleroutine io_revoke_request (
+ io_object: io_t;
+ reply: reply_port_t);
+
+simpleroutine io_select_timeout_request (
+ io_object: io_t;
+ ureplyport reply: mach_port_make_send_t;
+ timeout: timespec_t;
+ select_type: int);
diff --git a/hurd/ioctl-decode.h b/hurd/ioctl-decode.h
new file mode 100644
index 00000000..f65880cf
--- /dev/null
+++ b/hurd/ioctl-decode.h
@@ -0,0 +1,15 @@
+/* This file is used by the Makefile rules for generating
+ Xioctl-proto.defs, see Makefile for details. */
+
+#define CMD(request) _IOC_COMMAND (request)
+#define TYPE(request) _IOC_TYPE (request)
+#define INOUT(request) _IOC_INOUT (request)
+
+#define SUBID(request) IOC_COMMAND_SUBID (_IOC_COMMAND (request))
+
+#define TYPE0(request) _IOT_TYPE0 (_IOC_TYPE (request))
+#define TYPE1(request) _IOT_TYPE1 (_IOC_TYPE (request))
+#define TYPE2(request) _IOT_TYPE2 (_IOC_TYPE (request))
+#define COUNT0(request) _IOT_COUNT0 (_IOC_TYPE (request))
+#define COUNT1(request) _IOT_COUNT1 (_IOC_TYPE (request))
+#define COUNT2(request) _IOT_COUNT2 (_IOC_TYPE (request))
diff --git a/hurd/ioctl-tmpl.sym b/hurd/ioctl-tmpl.sym
new file mode 100644
index 00000000..8029ec00
--- /dev/null
+++ b/hurd/ioctl-tmpl.sym
@@ -0,0 +1,13 @@
+/* This file is used by the Makefile rules for generating
+ Xioctl-proto.defs, see Makefile for details. */
+
+#include <sys/ioctl.h>
+#include <hurd/ioctls.defs>
+
+#include "ioctl-decode.h"
+
+#include "@HEADER_LIST@"
+#define GROUPCHAR '@GROUP@'
+
+expr GROUPCHAR GROUP
+expr IOC_GROUP_SUBSYSTEM(GROUPCHAR) SUBSYSTEM
diff --git a/hurd/ioctl.awk b/hurd/ioctl.awk
new file mode 100644
index 00000000..289a9ab9
--- /dev/null
+++ b/hurd/ioctl.awk
@@ -0,0 +1,127 @@
+#
+# This awk script is used by the Makefile rules for generating
+# Xioctl-proto.defs, see Makefile for details.
+#
+
+$1 == "SUBSYSTEM" { subsystem = $2 + 0; next }
+$1 == "GROUP" { groupchar = $2; next }
+
+$2 == "CMD" { cmd[tolower($1)] = $3;
+ c = $3 + 0;
+ if (c > highcmd) highcmd = c;
+ icmd[c] = tolower($1);
+ next }
+$2 == "SUBID" { subid[tolower($1)] = $3;
+ c = $3 + 0;
+ if (c > highid) highid = c;
+ id2cmdname[c] = tolower($1);
+ next }
+$2 == "TYPE" { type[tolower($1)] = $3; next }
+$2 == "INOUT" { inout[tolower($1)] = $3; next }
+$2 == "TYPE0" { type0[tolower($1)] = $3; next }
+$2 == "TYPE1" { type1[tolower($1)] = $3; next }
+$2 == "TYPE2" { type2[tolower($1)] = $3; next }
+$2 == "COUNT0" { count0[tolower($1)] = $3; next }
+$2 == "COUNT1" { count1[tolower($1)] = $3; next }
+$2 == "COUNT2" { count2[tolower($1)] = $3; next }
+
+END {
+ group = sprintf("%cioctl", groupchar);
+
+ printf "subsystem %s %d; /* see ioctls.defs for calculation */\n\n", \
+ group, subsystem;
+
+ typemap[0] = "char";
+ typemap[1] = "char";
+ typemap[2] = "short";
+ typemap[3] = "int";
+ typemap[4] = "???64 bits???";
+ inoutmap[1] = "out";
+ inoutmap[2] = "in";
+ inoutmap[3] = "inout";
+
+ print "";
+ for (cmdname in type0) {
+ if (count0[cmdname] > 1) {
+ typecount = type0[cmdname] "," count0[cmdname];
+ if (!tc[typecount]) {
+ tc[typecount] = typemap[type0[cmdname]] "array_" count0[cmdname] "_t";
+ print "type", tc[typecount], "=", ("array[" count0[cmdname] "]"), \
+ "of", (typemap[type0[cmdname]] ";"), "/* XXX rename this! */";
+ }
+ argtype["0," cmdname] = tc[typecount];
+ }
+ else if (count0[cmdname] == 1) {
+ argtype["0," cmdname] = typemap[type0[cmdname]]
+ }
+ }
+
+ for (cmdname in type1) {
+ if (count1[cmdname] > 1) {
+ typecount = type1[cmdname] "," count1[cmdname];
+ if (!tc[typecount]) {
+ tc[typecount] = typemap[type1[cmdname]] "array_" count1[cmdname] "_t";
+ print "type", tc[typecount], "=", ("array[" count1[cmdname] "]"), \
+ "of", (typemap[type1[cmdname]] ";"), "/* XXX rename this! */";
+ }
+ argtype["1," cmdname] = tc[typecount];
+ }
+ else if (count1[cmdname] == 1) {
+ argtype["1," cmdname] = typemap[type1[cmdname]]
+ }
+ }
+
+ for (cmdname in type2) {
+ if (count2[cmdname] > 1) {
+ typecount = type2[cmdname] "," count2[cmdname];
+ if (!tc[typecount]) {
+ tc[typecount] = typemap[type2[cmdname]] "array_" count2[cmdname] "_t";
+ print "type", tc[typecount], "=", ("array[" count2[cmdname] "]"), \
+ "of", (typemap[type2[cmdname]] ";"), "/* XXX rename this! */";
+ }
+ argtype["2," cmdname] = tc[typecount];
+ }
+ else if (count2[cmdname] == 1) {
+ argtype["2," cmdname] = typemap[type2[cmdname]]
+ }
+ }
+ print "";
+
+ lastid = -1;
+ for (i = 0; i <= highid; ++i)
+ if (id2cmdname[i]) {
+ cmdname = id2cmdname[i];
+
+ if (lastid < 100 && i > 100) {
+ if (lastid == 98)
+ print "\nskip; /* 99 unused */"
+ else
+ printf "\nskip %d; /* %d-99 unused */\n", 100 - lastid, lastid + 1;
+ print "\n\
+/* Because MiG defines reply ports as 100 more than request ports, we\n\
+ have to leave one hundred empty RPC's here. */\n\
+skip 100;";
+ lastid = 199;
+ }
+
+ if (i == lastid + 2)
+ print "\nskip; /*", lastid + 1, "unused */";
+ else if (i != lastid + 1)
+ printf "\nskip %d; /* %d-%d unused */\n", \
+ i - lastid - 1, lastid + 1, i - 1;
+ lastid = i;
+ print "\n/*", i, toupper(cmdname), "*/";
+ printf "routine %s_%s (\n\treqport: io_t", group, cmdname;
+ if (inout[cmdname]) {
+ io = inoutmap[inout[cmdname]];
+ for (argidx = 0; argidx <= 2; ++argidx)
+ if (argtype[argidx "," cmdname])
+ printf ";\n\t%s\targ%d: %s", \
+ io, argidx, argtype[argidx "," cmdname];
+ }
+ else {
+ printf ";\n\tin\trequest: int";
+ }
+ print ");"
+ }
+}
diff --git a/hurd/ioctl_types.h b/hurd/ioctl_types.h
new file mode 100644
index 00000000..8baa3604
--- /dev/null
+++ b/hurd/ioctl_types.h
@@ -0,0 +1,33 @@
+/* Types used in RPC definitions corresponding to ioctls.
+ Copyright (C) 1994, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _HURD_IOCTL_TYPES_H
+#define _HURD_IOCTL_TYPES_H
+
+#include <termios.h>
+typedef tcflag_t modes_t[4];
+typedef speed_t speeds_t[2];
+typedef cc_t ccs_t[NCCS];
+
+#include <sys/ioctl.h>
+typedef struct winsize winsize_t;
+
+#include <net/if.h>
+typedef struct sockaddr sockaddr_t;
+typedef char ifname_t[16];
+
+#endif /* hurd/ioctl_types.h */
diff --git a/hurd/ioctls.defs b/hurd/ioctls.defs
new file mode 100644
index 00000000..bcad5489
--- /dev/null
+++ b/hurd/ioctls.defs
@@ -0,0 +1,48 @@
+/* Macro definitions for defining and using ioctl-based RPC interfaces.
+ Copyright (C) 1999 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _HURD_IOCTLS_DEFS
+#define _HURD_IOCTLS_DEFS 1
+
+#ifndef _SYS_IOCTL_H
+#define _SYS_IOCTL_H /* Suppress #error in <bits/ioctl.h>. */
+#endif
+#include <bits/ioctls.h>
+
+
+/* Calculate the MiG subsystem (i.e. first message ID)
+ for the RPCs produced by ioctl requests in the given group. */
+#define IOC_GROUP_SUBSYSTEM(group) (100000 + ((group) - 'f') * 4000)
+
+/* Because of MiG's poorly chosen algorithm of adding 100 to a request
+ msgid to produce the reply msgid, we cannot just add the command part of
+ the ioctl request to the subsystem base msgid. For ioctl requests past
+ 99, we must skip blocks of 100 msgids to allow for the reply msgids
+ corresponding to the earlier requests. Since our ioctl request format
+ allows only 7 bits for the command portion, we know that it cannot
+ exceed 127 and thus we can handle just 100+ as a special case. */
+#define IOC_COMMAND_SUBID(cmd) ((cmd) + ((cmd) < 100 ? 0 : 100))
+
+#define IOC_CONSTRUCT_MSGID(group, cmd) \
+ (IOC_GROUP_SUBSYSTEM (group) + IOC_COMMAND_SUBID (cmd))
+#define IOC_MSGID(request) \
+ IOC_CONSTRUCT_MSGID (_IOC_GROUP (request), _IOC_COMMAND (request))
+
+
+#endif /* !_HURD_IOCTLS_DEFS */
diff --git a/hurd/kdioctl.defs b/hurd/kdioctl.defs
new file mode 100644
index 00000000..64f9b9ea
--- /dev/null
+++ b/hurd/kdioctl.defs
@@ -0,0 +1,39 @@
+/* Definitions for kd ioctls
+ Copyright (C) 1991, 1993, 1994, 1995, 1996, 1998, 2005 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd/hurd_types.defs>
+
+/* Ioctl class `k'; the subsystem is derived from calculations in
+ <ioctls.h>. */
+subsystem kdioctl 120000;
+
+import <hurd/ioctl_types.h>;
+
+skip; /* 0 unused */
+
+/* 1 KDSKBDMODE */
+routine kdioctl_kdskbdmode (
+ port: io_t;
+ in mode: int);
+/* 2 KDGKBDMODE */
+routine kdioctl_kdgkbdmode (
+ port: io_t;
+ out mode: int);
+
+/* 3 - 256 unused */
diff --git a/hurd/kernel_boot.defs b/hurd/kernel_boot.defs
new file mode 100644
index 00000000..d3b3975e
--- /dev/null
+++ b/hurd/kernel_boot.defs
@@ -0,0 +1,29 @@
+/* Communication between the kernel and the startup task.
+ Copyright (C) 1992, 1993 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Roland McGrath. */
+
+subsystem kernel_boot 999999;
+
+#include <mach/mach_types.defs>
+
+routine kernel_boot_get_priv_ports (
+ bootstrap: mach_port_t; /* Bootstrap port of the startup task. */
+ out host_priv: mach_port_t;
+ out device_master: mach_port_t);
diff --git a/hurd/login.defs b/hurd/login.defs
new file mode 100644
index 00000000..7adcbcd7
--- /dev/null
+++ b/hurd/login.defs
@@ -0,0 +1,58 @@
+/* Definitions for the database of logged-in users
+ Copyright (C) 1994 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+subsystem login 36000;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef LOGIN_IMPORTS
+LOGIN_IMPORTS
+#endif
+
+/* For each logged in user, a file is conventionally created in the
+ directory `/var/logins' with the name of the user, a hyphen, and a small
+ integer. (The suffix is there to distinguish multiple logons of the
+ same user.) On each such node an active translator should be found
+ which implements this interface. */
+
+/* Send an immediate message to the user. */
+routine login_message_user (
+ login: file_t;
+ message: data_t);
+
+/* Return a human-readable string describing the user's physical location. */
+routine login_get_location (
+ login: file_t;
+ out location: data_t, dealloc);
+
+/* Return how much time has passed since the user last used an input device. */
+routine login_get_idle_time (
+ login: file_t;
+ out idletime: time_value_t);
+
+/* Return a list of file names for input devices being used, separated by null
+ characters. This call is optional; clients should not depend on it. */
+routine login_get_input_devices (
+ login: file_t;
+ out devices: data_t, dealloc);
+
+/* Return the process collection ID for the user's login collection. */
+routine login_get_login_collection (
+ login: file_t;
+ out id: pid_t);
diff --git a/hurd/msg.defs b/hurd/msg.defs
new file mode 100644
index 00000000..95802472
--- /dev/null
+++ b/hurd/msg.defs
@@ -0,0 +1,202 @@
+/* RPCs which a friendly Hurd process will understand on its message port.
+ Copyright (C) 1991,92,93,94,95,96,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+subsystem msg 23000;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef MSG_IMPORTS
+MSG_IMPORTS
+#endif
+
+/* WARNING: The file hurd/proc/stubs.c knows the RPC message ID's
+ and formats for some of the RPC's in this file. */
+
+/* Post a signal to the process. The refport indicates some
+ permission attribute that the caller provides in the hope that the
+ receiver will honor the signal. Conventional values include:
+
+Port Signals honored
+
+task port all
+session ID (see proc_getsidport) CONT
+ctty port (see term_getctty) INT, QUIT, TSTP, HUP
+async IO ID (see io_async) URG, IO
+*/
+routine msg_sig_post (
+ process: mach_port_t;
+ sreplyport reply_port: sreply_port_t;
+ signal: int;
+ sigcode: natural_t;
+ refport: mach_port_t);
+
+/* The parent or the process group of a process has been changed, or
+ the orphaned status has changed. The task port is sent so that the
+ recipient can know it's OK to honor the call. */
+routine msg_proc_newids (
+ process: mach_port_t;
+ task: task_t;
+ ppid: pid_t;
+ pgrp: pid_t;
+ orphaned: int);
+
+/* Do a reauth after or-ing in new id's. */
+routine msg_add_auth (
+ process: mach_port_t;
+ auth: auth_t);
+
+/* Do a reauth after removing auth information. The task port is
+ provided as an attempt at security. */
+routine msg_del_auth (
+ process: mach_port_t;
+ task: task_t;
+ uids: intarray_t;
+ gids: intarray_t);
+
+/* These eight calls fetch and store the ports and other information
+ that were passed to the process at exec time (see hurd_types.h).
+ The REFPORT can be the task port or the auth port. */
+
+routine msg_get_init_port (
+ process: mach_port_t;
+ refport: mach_port_t;
+ which: int;
+ out port: mach_port_send_t);
+
+routine msg_set_init_port (
+ process: mach_port_t;
+ refport: mach_port_t;
+ which: int;
+ port: mach_port_send_t);
+
+routine msg_get_init_ports (
+ process: mach_port_t;
+ refport: mach_port_t;
+ out ports: portarray_t, dealloc);
+
+routine msg_set_init_ports (
+ process: mach_port_t;
+ refport: mach_port_t;
+ ports: portarray_t);
+
+routine msg_get_init_int (
+ process: mach_port_t;
+ refport: mach_port_t;
+ which: int;
+ out value: int);
+
+routine msg_set_init_int (
+ process: mach_port_t;
+ refport: mach_port_t;
+ which: int;
+ value: int);
+
+routine msg_get_init_ints (
+ process: mach_port_t;
+ refport: mach_port_t;
+ out values: intarray_t, dealloc);
+
+routine msg_set_init_ints (
+ process: mach_port_t;
+ refport: mach_port_t;
+ values: intarray_t);
+
+/* These two calls fetch and store the file descriptor table. */
+
+routine msg_get_dtable (
+ process: mach_port_t;
+ refport: mach_port_t;
+ out dtable: portarray_t, dealloc);
+
+routine msg_set_dtable (
+ process: mach_port_t;
+ refport: mach_port_t;
+ dtable: portarray_t);
+
+/* These two calls fetch and store a single file descriptor. */
+
+routine msg_get_fd (
+ process: mach_port_t;
+ refport: mach_port_t;
+ fd: int;
+ out port: mach_port_send_t);
+
+routine msg_set_fd (
+ process: mach_port_t;
+ refport: mach_port_t;
+ fd: int;
+ port: mach_port_send_t);
+
+/* These two calls fetch and store the whole environment,
+ in "a=b\0c=d\0" form. */
+
+routine msg_get_environment (
+ process: mach_port_t;
+ out value: data_t, dealloc);
+
+routine msg_set_environment (
+ process: mach_port_t;
+ refport: mach_port_t;
+ value: data_t);
+
+/* These two calls fetch and store a single environment variable. */
+
+routine msg_get_env_variable (
+ process: mach_port_t;
+ variable: string_t;
+ out value: data_t, dealloc);
+
+routine msg_set_env_variable (
+ process: mach_port_t;
+ refport: mach_port_t;
+ variable: string_t;
+ value: string_t;
+ replace: boolean_t);
+
+skip; /* Obsolete io_select_done. */
+skip; /* Obsolete msg_startup_dosync. */
+
+/* Like msg_sig_post, but the receiver should ignore his trace bit
+ and deliver the signal normally even if traced, first resuming if
+ he was suspended. */
+routine msg_sig_post_untraced (
+ process: mach_port_t;
+ sreplyport reply_port: sreply_port_t;
+ signal: int;
+ sigcode: natural_t;
+ refport: mach_port_t);
+
+/* Return a description of why THREAD is waiting. THREAD must
+ be a thread in this task. If the thread is not waiting, or the reason
+ is unknown, return zero. WAIT_RPC is the RPC code of the RPC the thread
+ is waiting on, or zero if it is not blocked in an RPC. */
+routine msg_report_wait (
+ process: mach_port_t;
+ thread: thread_t;
+ out wait_desc: string_t;
+ out wait_rpc: mach_msg_id_t);
+
+/* Given a list of port names in NAMES, return a description of the
+ corresponding port from libc's point of view, in a '\0'-separated vector.
+ Ports that libc doesn't know about should result in a zero-length entry. */
+routine msg_describe_ports (
+ process: mach_port_t;
+ refport: mach_port_t;
+ names: mach_port_name_array_t;
+ out descriptions: data_t);
diff --git a/hurd/msg_reply.defs b/hurd/msg_reply.defs
new file mode 100644
index 00000000..c1b95fdc
--- /dev/null
+++ b/hurd/msg_reply.defs
@@ -0,0 +1,55 @@
+/* Reply side of miscellaneous callbacks from Hurd servers to their clients
+ Copyright (C) 1994,95,2001 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+subsystem msg_reply 23100; /* must match msg.defs + 100 */
+
+#include <hurd/hurd_types.defs>
+
+type reply_port_t = polymorphic | MACH_MSG_TYPE_PORT_SEND_ONCE
+ ctype: mach_port_t;
+
+simpleroutine msg_sig_post_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG);
+
+skip; /* msg_proc_newids */
+skip; /* msg_add_auth */
+skip; /* msg_del_auth */
+skip; /* msg_get_init_port */
+skip; /* msg_set_init_port */
+skip; /* msg_get_init_ports */
+skip; /* msg_set_init_ports */
+skip; /* msg_get_init_int */
+skip; /* msg_set_init_int */
+skip; /* msg_get_init_ints */
+skip; /* msg_set_init_ints */
+skip; /* msg_get_dtable */
+skip; /* msg_set_dtable */
+skip; /* msg_get_fd */
+skip; /* msg_set_fd */
+skip; /* msg_get_environment */
+skip; /* msg_set_environment */
+skip; /* msg_get_env_variable */
+skip; /* msg_set_env_variable */
+skip; /* Obsolete io_select_done. */
+skip; /* msg_startup_dosync */
+
+simpleroutine msg_sig_post_untraced_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG);
diff --git a/hurd/msg_request.defs b/hurd/msg_request.defs
new file mode 100644
index 00000000..e3c0ad3f
--- /dev/null
+++ b/hurd/msg_request.defs
@@ -0,0 +1,61 @@
+/* Reply side of canonical Hurd process message port RPCs.
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+subsystem msg_request 23000; /* must match msg.defs */
+
+#include <hurd/hurd_types.defs>
+
+type reply_port_t = polymorphic | polymorphic
+ ctype: mach_port_t;
+
+simpleroutine msg_sig_post_request (
+ process: mach_port_t;
+ replyport reply_port: reply_port_t;
+ signal: int;
+ sigcode: natural_t;
+ refport: mach_port_t);
+
+skip; /* msg_proc_newids */
+skip; /* msg_add_auth */
+skip; /* msg_del_auth */
+skip; /* msg_get_init_port */
+skip; /* msg_set_init_port */
+skip; /* msg_get_init_ports */
+skip; /* msg_set_init_ports */
+skip; /* msg_get_init_int */
+skip; /* msg_set_init_int */
+skip; /* msg_get_init_ints */
+skip; /* msg_set_init_ints */
+skip; /* msg_get_dtable */
+skip; /* msg_set_dtable */
+skip; /* msg_get_fd */
+skip; /* msg_set_fd */
+skip; /* msg_get_environment */
+skip; /* msg_set_environment */
+skip; /* msg_get_env_variable */
+skip; /* msg_set_env_variable */
+skip; /* Obsolete io_select_done. */
+skip; /* msg_startup_dosync */
+
+simpleroutine msg_sig_post_untraced_request (
+ process: mach_port_t;
+ replyport reply_port: reply_port_t;
+ signal: int;
+ sigcode: natural_t;
+ refport: mach_port_t);
diff --git a/hurd/newterm.defs b/hurd/newterm.defs
new file mode 100644
index 00000000..d44d0926
--- /dev/null
+++ b/hurd/newterm.defs
@@ -0,0 +1,38 @@
+/* Creation of terminal processors
+ Copyright (C) 1991, 1993 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+subsystem newterm 27000;
+
+#include <hurd/hurd_types.defs>
+
+/* Create a new terminal thingie with no bottom half. (You need to do
+ one of term_on_* before this can be used for I/O.) */
+routine newterm_from_device (
+ newterm: mach_port_t;
+ out terminal: io_t);
+
+/* Register a terminal as a node in the filesystem. */
+routine newterm_makenode (
+ newterm: mach_port_t;
+ terminal: io_t;
+ mode: mode_t;
+ server_picks_name: int;
+ name: string_t);
diff --git a/hurd/password.defs b/hurd/password.defs
new file mode 100644
index 00000000..b6d74198
--- /dev/null
+++ b/hurd/password.defs
@@ -0,0 +1,50 @@
+/* Protocol for password checker
+ Copyright (C) 1997, 1998 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Thomas Bushnell, n/BSG. */
+
+subsystem password 38000;
+
+#ifdef PASSWORD_IMPORTS
+PASSWORD_IMPORTS
+#endif
+
+#include <hurd/hurd_types.defs>
+
+/* Service for this protocol normally lives on /servers/password as
+ specified by _SERVERS_PASSWORD in <hurd/paths.h>. */
+
+/* Check to see if the password for user USER is really PW. Return
+ an error if it fails, and return an auth port for the id in AUTH
+ if it succeeded. */
+routine password_check_user (
+ server: io_t;
+ user: uid_t;
+ pw: string_t;
+ out auth: mach_port_send_t);
+
+
+/* Check to see if the password for GROUP is really PW. Return
+ an error if it fails, and return an auth port for the id in AUTH
+ if it succeeded. */
+routine password_check_group (
+ server: io_t;
+ group: uid_t;
+ pw: string_t;
+ out auth: mach_port_send_t);
diff --git a/hurd/paths.h b/hurd/paths.h
new file mode 100644
index 00000000..48771325
--- /dev/null
+++ b/hurd/paths.h
@@ -0,0 +1,56 @@
+/* Standard Hurd pathnames.
+ Copyright (C) 1992,94,95,97,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _HURD_PATHS_H
+#define _HURD_PATHS_H
+
+/* Port rendezvous points are specified by symbols _SERVERS_FOO,
+ the canonical pathname being /servers/foo. */
+
+#define _SERVERS "/servers/"
+#define _SERVERS_CRASH _SERVERS "crash"
+#define _SERVERS_EXEC _SERVERS "exec"
+#define _SERVERS_PROC _SERVERS "proc"
+#define _SERVERS_PASSWORD _SERVERS "password"
+#define _SERVERS_DEFPAGER _SERVERS "default-pager"
+
+/* Directory containing naming points for socket servers.
+ Entries are named by the string representing the domain number
+ in simple decimal (e.g. "/servers/socket/23"). */
+#define _SERVERS_SOCKET _SERVERS "socket"
+
+/* Hurd servers are specified by symbols _HURD_FOO,
+ the canonical pathname being /hurd/foo. */
+
+#define _HURD "/hurd/"
+#define _HURD_INIT _HURD "init"
+#define _HURD_PROC _HURD "proc"
+#define _HURD_AUTH _HURD "auth"
+
+/* Standard translators for special node types.
+ These pathnames are used by the C library.
+ UFS and perhaps other filesystems short-circuit these translators. */
+#define _HURD_SYMLINK _HURD "symlink" /* S_IFLNK */
+#define _HURD_CHRDEV _HURD "chrdev" /* S_IFCHR */
+#define _HURD_BLKDEV _HURD "blkdev" /* S_IFBLK */
+#define _HURD_FIFO _HURD "fifo" /* S_IFIFO */
+#define _HURD_IFSOCK _HURD "ifsock" /* S_IFSOCK */
+
+
+#endif /* hurd/paths.h */
diff --git a/hurd/pfinet.defs b/hurd/pfinet.defs
new file mode 100644
index 00000000..ec0b03e3
--- /dev/null
+++ b/hurd/pfinet.defs
@@ -0,0 +1,39 @@
+/* Definitions for pfinet-specific calls
+ Copyright (C) 1999,2000,02 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+subsystem pfinet 37000;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef SOCKET_IMPORTS
+SOCKET_IMPORTS
+#endif
+
+INTR_INTERFACE
+
+/* Return a list of interfaces as expected by the SIOCGIFCONF ioctl.
+ The maximum number of bytes returned can be given in AMOUNT, but if
+ AMOUNT is -1, all interfaces will be returned. Always
+ succeeds (and interface list will be truncated to fit into AMOUNT
+ space) for BSD compatibility. */
+routine pfinet_siocgifconf (
+ port: io_t;
+ amount: vm_size_t;
+ out buf: data_t, dealloc
+);
diff --git a/hurd/process.defs b/hurd/process.defs
new file mode 100644
index 00000000..bf905564
--- /dev/null
+++ b/hurd/process.defs
@@ -0,0 +1,402 @@
+/* Definitions for process server interface
+ Copyright (C) 1992,93,94,95,96,97,2001,2013 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+subsystem process 24000;
+
+#include <hurd/hurd_types.defs>
+
+/* If any calls are added or deleted, modify process_reply.defs
+ and process_request.defs accordingly. */
+
+#ifdef PROCESS_IMPORTS
+PROCESS_IMPORTS
+#endif
+
+INTR_INTERFACE
+
+
+/*** Host management ***/
+
+skip; /* Was proc_sethostid */
+skip; /* Was proc_gethostid */
+skip; /* Was proc_sethostname */
+skip; /* Was proc_gethostname */
+
+/* Get the privileged host port and the device master port. */
+routine proc_getprivports (
+ process: process_t;
+ out host_priv: host_priv_t;
+ out device_master: device_t);
+
+/* Return all the pids of all live processes. */
+routine proc_getallpids (
+ process: process_t;
+ out pidarray: pidarray_t, dealloc);
+
+/* Set the "standard exec data", used by programs that want predefined
+ values for exec. */
+routine proc_setexecdata (
+ process: process_t;
+ ports: portarray_t;
+ ints: intarray_t);
+
+/* Get the standard exec data */
+routine proc_getexecdata (
+ process: process_t;
+ out ports: portarray_t, dealloc;
+ out ints: intarray_t, dealloc);
+
+/* Request notification with exec_setexecdata on NOTIFY. */
+routine proc_execdata_notify (
+ process: process_t;
+ notify: mach_port_send_t);
+
+/* Return a uname structure for the currently running system. */
+routine proc_uname (
+ process: process_t;
+ out uname: utsname_t);
+
+/* Tell the server who we are for inclusion in the uname string. */
+routine proc_register_version (
+ process: process_t;
+ credential: host_priv_t;
+ name: string_t;
+ release: string_t;
+ version: string_t);
+
+/*** Process creation and simple management. ***/
+
+/* There is a 1-1 relationship between tasks and processes; to create
+ a new process, just create a new task and use proc_task2proc. */
+
+/* Change the current authentication of the process. The user should
+ follow this call with a call to auth_user_authenticate. The
+ new_port passed back through the auth server will be
+ MACH_PORT_NULL. */
+simpleroutine proc_reauthenticate (
+ process: process_t;
+ rendezvous2: mach_port_send_t);
+
+/* Declare that a task is a child of the caller. The task's state
+ will then inherit from the caller. This call can be made only once
+ per task. */
+routine proc_child (
+ process: process_t;
+ child: task_t);
+
+/* Atomically change the message port of a process. */
+routine proc_setmsgport (
+ process: process_t;
+ sreplyport reply_port: sreply_port_t;
+ newmsgport: mach_port_t;
+ out oldmsgport: mach_port_send_t);
+
+/* Cause a task to take over the pid and all other state from an
+ existing task, and kill the old task with task_terminate. The
+ message port will be set to MACH_PORT_NULL. This is principally
+ used for doing exec's with the EXEC_NEWTASK bit set. The request
+ port assigned to NEWTASK does not change. The new task cannot have
+ had proc_child called on its behalf, it cannot have a pgrp, a session,
+ a login name set with proc_setlogin, it cannot have called proc_child,
+ and it cannot have a message port. */
+/* If this call is made for a process which is no longer alive or
+ does not exist, then newtask will be killed (and no error
+ is returned). */
+routine proc_reassign (
+ process: process_t;
+ newtask: task_t);
+
+/* Set the owner of the process. The new owner must be within the
+ current authentication of the caller. If CLEAR is set, then ignore
+ OWNER and restore the process to unowned status. */
+routine proc_setowner (
+ process: process_t;
+ owner: uid_t;
+ clear: int);
+
+/* Get the process ID and the parent process ID, also find out if we
+ are orphaned. */
+routine proc_getpids (
+ process: process_t;
+ out pid: pid_t;
+ out ppid: pid_t;
+ out orphaned: int);
+
+/* Set the locations of ARGV and ENVP which will be examined
+ by proc_getprocargs and proc_getprocenv. */
+routine proc_set_arg_locations (
+ process: process_t;
+ argv: vm_address_t;
+ envp: vm_address_t);
+
+/* Fetch the locations set by proc_set_arg_locations. */
+routine proc_get_arg_locations (
+ process: process_t;
+ out argv: vm_address_t;
+ out envp: vm_address_t);
+
+/* Fetch the message port of a process */
+routine proc_getmsgport (
+ process: process_t;
+ sreplyport reply_port: sreply_port_t;
+ pid: pid_t;
+ out msgport: mach_port_t);
+
+/* Wait for a child process to exit. If pid is zero, it waits for any
+ child in the same pgrp as the parent. If pid is -1, it waits for
+ any child. Otherwise, if pid is negative, it waits for any process
+ in the specified process group. If pid is positive, it waits for
+ the specified process. The exit status and resource usage of the
+ process are returned in status and rusage respectively. */
+routine proc_wait (
+ process: process_t;
+ sreplyport reply_port: sreply_port_t;
+ pid: pid_t;
+ options: int;
+ out status: int;
+ out sigcode: int;
+ out rusage: rusage_t;
+ out pid_status: pid_t);
+
+/* Have the process server stop all threads except contthread. */
+routine proc_dostop (
+ process: process_t;
+ contthread: thread_t);
+
+/* Take over exception messages received on MSGPORT. Any exception
+ messages received will be forwarded to FORWARDPORT. In addition,
+ the thread causing the exception will be aborted (with thread_abort)
+ and have its state adjusted (as by thread_set_state). */
+routine proc_handle_exceptions (
+ process: process_t;
+ msgport: mach_port_move_receive_t;
+ forwardport: mach_port_send_t;
+ flavor: int;
+ new_state: thread_state_t);
+
+
+
+/*** Mark bits. Some of these (exec, traced, eg) modify small pieces
+ of the proc server's behavior; others are purely informational. ***/
+
+/* Mark the process as stopped on a signal. */
+routine proc_mark_stop (
+ process: process_t;
+ signo: int;
+ sigcode: int);
+
+/* Mark the process as continued after a stop. */
+routine proc_mark_cont (
+ process: process_t);
+
+/* Tell the process server that the process is going away. If it dies
+ without calling this, it will look to the parent like the process
+ died with SIGKILL. This call allows for a cleaner exit. */
+routine proc_mark_exit (
+ process: process_t;
+ status: int;
+ sigcode: int);
+
+/* Inform the process server that the process has completed an exec. */
+routine proc_mark_exec (
+ process: process_t);
+
+/* Inform the process server that the process has asked to be traced.
+ The only result of this is to change the behavior of wait by the
+ parent slightly. */
+routine proc_mark_traced (
+ process: process_t);
+
+/* Inform the process server whether SIGCHLD should be sent for stopped
+ child processes. */
+routine proc_mod_stopchild (
+ process: process_t;
+ doit: int);
+
+
+/*** Miscellaneous process information queries. ***/
+
+routine proc_pid2task (
+ process: process_t;
+ pid: pid_t;
+ out task: task_t);
+
+routine proc_task2pid (
+ process: process_t;
+ task: task_t;
+ out pid: pid_t);
+
+/* Return the procserver port for the specified task. */
+routine proc_task2proc (
+ process: process_t;
+ task: task_t;
+ out proc: mach_port_make_send_t);
+
+routine proc_proc2task (
+ process: process_t;
+ out task: task_t);
+
+routine proc_pid2proc (
+ process: process_t;
+ pid: pid_t;
+ out proc: mach_port_make_send_t);
+
+routine proc_getprocinfo (
+ process: process_t;
+ which: pid_t;
+ inout flags: int;
+ out procinfo: procinfo_t, dealloc;
+ out threadwaits: data_t, dealloc);
+
+routine proc_getprocargs (
+ process: process_t;
+ which: pid_t;
+ out procargs: data_t, dealloc);
+
+routine proc_getprocenv (
+ process: process_t;
+ which: pid_t;
+ out procenv: data_t, dealloc);
+
+/* Create a new login collection. The pid of PROCESS will be the id
+ of the collection. All the children (and their children, and so forth) of
+ PROCESS will automatically join the collection. If PROCESS dies its
+ children (and their children, and so forth) will become part of init's
+ process collection. */
+routine proc_make_login_coll (
+ process: process_t);
+
+/* Get the process collection ID for the process specified by pid. */
+routine proc_getloginid (
+ process: process_t;
+ pid: pid_t;
+ out login_id: pid_t);
+
+/* Get the pids of all the members of process collection ID. */
+routine proc_getloginpids (
+ process: process_t;
+ id: pid_t;
+ out pids: pidarray_t, dealloc);
+
+/* You are not expected to understand this. */
+routine proc_setlogin (
+ process: process_t;
+ logname: string_t);
+
+routine proc_getlogin (
+ process: process_t;
+ out logname: string_t);
+
+
+
+/*** Sessions and process groups. ***/
+
+/* Create a new session. The process's pgrp is set to its pid, and
+ becomes the session leader. */
+routine proc_setsid (
+ process: process_t);
+
+/* Return the pid of a process's session leader. */
+routine proc_getsid (
+ process: process_t;
+ pid: pid_t;
+ out sid: pid_t);
+
+/* Get the pgids of all the members of a session. */
+routine proc_getsessionpgids (
+ process: process_t;
+ sid: pid_t;
+ out pgidset: pidarray_t, dealloc);
+
+/* Get the pids of all the members of a session. */
+routine proc_getsessionpids (
+ process: process_t;
+ sid: pid_t;
+ out pidset: pidarray_t, dealloc);
+
+/* Return a "session ID" port for the session of the caller.
+ This port is generally only available to members of the session.
+ (It has no listener, but is useful for identification purposes.) */
+routine proc_getsidport (
+ process: process_t;
+ out sessport: mach_port_send_t);
+
+/* Set the process group of a process. */
+routine proc_setpgrp (
+ process: process_t;
+ pid: pid_t;
+ pgrp: pid_t);
+
+/* Get the process group of a process. */
+routine proc_getpgrp (
+ process: process_t;
+ pid: pid_t;
+ out pgrp: pid_t);
+
+/* Get the pids of all the members of a pgrp. */
+routine proc_getpgrppids (
+ process: process_t;
+ pgrp: pid_t;
+ out pidset: pidarray_t, dealloc);
+
+
+/*** Other miscelleneous info queries ***/
+
+/* Return the controlling TTY used by PID in TTY; opened without read or
+ write access. */
+routine proc_get_tty (
+ calling_process: process_t;
+ target_process: pid_t;
+ out tty: mach_port_send_t);
+
+/* Return the number of Mach ports used by PID */
+routine proc_getnports (
+ process: process_t;
+ which: pid_t;
+ out nports: mach_msg_type_number_t);
+
+/*** Routines related to early server bootstrapping ***/
+
+skip; /* Reserved for proc_set_init_task */
+
+/* Inform the process server that the process is important. */
+routine proc_mark_important (
+ process: process_t);
+
+/* Query whether the process is important. */
+routine proc_is_important (
+ process: process_t;
+ out essential: boolean_t);
+
+/* Set the processes start_code and end_code locations. Any
+ executable segments loaded from the ELF binary are in this
+ range. */
+routine proc_set_code (
+ process: process_t;
+ start_code: vm_address_t;
+ end_code: vm_address_t);
+
+/* Get the processes start_code and end_code locations. Any
+ executable segments loaded from the ELF binary are in this range.
+ If zero is returned for these values, the requested information has
+ never been set. */
+routine proc_get_code (
+ process: process_t;
+ out start_code: vm_address_t;
+ out end_code: vm_address_t);
diff --git a/hurd/process_reply.defs b/hurd/process_reply.defs
new file mode 100644
index 00000000..ed46d55b
--- /dev/null
+++ b/hurd/process_reply.defs
@@ -0,0 +1,194 @@
+/* Reply half of wait
+ Copyright (C) 1991,93,94,96,2001,13 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+subsystem process_reply 24100; /* must match process.defs + 100 */
+
+#include <hurd/hurd_types.defs>
+
+type reply_port_t = polymorphic | MACH_MSG_TYPE_PORT_SEND_ONCE
+ ctype: mach_port_t;
+
+skip; skip; /* get/set hostid */
+skip; skip; /* get/set hostname */
+skip; /* getprivports */
+skip; /* getallpids */
+skip; skip; /* set/get execdata */
+skip; /* execdata_notify */
+skip; skip; /* proc_uname, proc_register_version */
+
+skip; /* reauthenticate */
+skip; /* child */
+
+simpleroutine proc_setmsgport_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ in oldmsgport: mach_port_t);
+
+skip; /* reassign */
+skip; /* setowner */
+skip; /* getpids */
+skip; /* set_arg_locations */
+skip; /* get_arg_locations */
+
+simpleroutine proc_getmsgport_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ in msgports: mach_port_t);
+
+simpleroutine proc_wait_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ in status: int;
+ in sigcode: int;
+ in rusage: rusage_t;
+ in pid_status: pid_t);
+
+skip; /* proc_dostop */
+skip; /* proc_handle_exceptions */
+skip; /* proc_mark_stop */
+skip; /* proc_mark_cont */
+skip; /* proc_mark_exit */
+skip; /* proc_mark_exec */
+skip; /* proc_mark_traced */
+skip; /* proc_mod_stopchild */
+
+simpleroutine proc_pid2task_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ task: task_t);
+
+simpleroutine proc_task2pid_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ pid: pid_t);
+
+simpleroutine proc_task2proc_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ proc: mach_port_make_send_t);
+
+simpleroutine proc_proc2task_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ task: task_t);
+
+simpleroutine proc_pid2proc_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ proc: mach_port_make_send_t);
+
+simpleroutine proc_getprocinfo_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ flags: int;
+ procinfo: procinfo_t, dealloc;
+ threadwaits: data_t, dealloc);
+
+simpleroutine proc_getprocargs_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ procargs: data_t, dealloc);
+
+simpleroutine proc_getprocenv_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ procenv: data_t, dealloc);
+
+skip; /* proc_make_login_coll */
+
+simpleroutine proc_getloginid_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ login_id: pid_t);
+
+simpleroutine proc_getloginpids_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ pids: pidarray_t, dealloc);
+
+skip; /* proc_setlogin */
+
+simpleroutine proc_getlogin_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ logname: string_t);
+
+skip; /* proc_setsid */
+
+simpleroutine proc_getsid_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ sid: pid_t);
+
+simpleroutine proc_getsessionpgids_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ pgidset: pidarray_t, dealloc);
+
+simpleroutine proc_getsessionpids_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ pidset: pidarray_t, dealloc);
+
+simpleroutine proc_getsidport_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ sessport: mach_port_send_t);
+
+skip; /* proc_setpgrp */
+
+simpleroutine proc_getpgrp_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ pgrp: pid_t);
+
+simpleroutine proc_getpgrppids_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ pidset: pidarray_t, dealloc);
+
+simpleroutine proc_get_tty_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ tty: mach_port_send_t);
+
+simpleroutine proc_getnports_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ nports: mach_msg_type_number_t);
+
+/*** Routines related to early server bootstrapping ***/
+
+skip; /* Reserved for proc_set_init_task */
+skip; /* proc_mark_important */
+
+simpleroutine proc_is_important_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ essential: boolean_t);
+
+skip; /* proc_set_code */
+
+simpleroutine proc_get_code_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ start_code: vm_address_t;
+ end_code: vm_address_t);
diff --git a/hurd/process_request.defs b/hurd/process_request.defs
new file mode 100644
index 00000000..38e71461
--- /dev/null
+++ b/hurd/process_request.defs
@@ -0,0 +1,404 @@
+/* Definitions for process server interface (request-only version)
+
+ Copyright (C) 1992, 93, 94, 95, 96, 98, 2013 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+subsystem process 24000;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef PROCESS_IMPORTS
+PROCESS_IMPORTS
+#endif
+
+type reply_port_t = MACH_MSG_TYPE_MAKE_SEND_ONCE | polymorphic
+ ctype: mach_port_t;
+
+
+/*** Host management ***/
+
+skip; /* Was proc_sethostid */
+skip; /* Was proc_gethostid */
+skip; /* Was proc_sethostname */
+skip; /* Was proc_gethostname */
+
+/* Get the privileged host port and the device master port. */
+simpleroutine proc_getprivports_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
+
+/* Return all the pids of all live processes. */
+simpleroutine proc_getallpids_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
+
+/* Set the "standard exec data", used by programs that want predefined
+ values for exec. */
+simpleroutine proc_setexecdata_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ ports: portarray_t;
+ ints: intarray_t);
+
+/* Get the standard exec data */
+simpleroutine proc_getexecdata_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
+
+/* Request notification with exec_setexecdata on NOTIFY. */
+simpleroutine proc_execdata_notify_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ notify: mach_port_send_t);
+
+/* Return a uname structure for the currently running system. */
+simpleroutine proc_uname_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
+
+/* Tell the server who we are for inclusion in the uname string. */
+simpleroutine proc_register_version_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ credential: host_priv_t;
+ name: string_t;
+ release: string_t;
+ version: string_t);
+
+/*** Process creation and simple management. ***/
+
+/* There is a 1-1 relationship between tasks and processes; to create
+ a new process, just create a new task and use proc_task2proc. */
+
+/* Change the current authentication of the process. The user should
+ follow this call with a call to auth_user_authenticate. The
+ new_port passed back through the auth server will be
+ MACH_PORT_NULL. */
+simpleroutine proc_reauthenticate_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ rendezvous2: mach_port_send_t);
+
+/* Declare that a task is a child of the caller. The task's state
+ will then inherit from the caller. This call can be made only once
+ per task. */
+simpleroutine proc_child_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ child: task_t);
+
+/* Atomically change the message port of a process. */
+simpleroutine proc_setmsgport_request (
+ process: process_t;
+ replyport reply: reply_port_t;
+ newmsgport: mach_port_t);
+
+/* Cause a task to take over the pid and all other state from an
+ existing task, and kill the old task with task_terminate. The
+ message port will be set to MACH_PORT_NULL. This is principally
+ used for doing exec's with the EXEC_NEWTASK bit set. The request
+ port assigned to NEWTASK does not change. The new task cannot have
+ had proc_child called on its behalf, it cannot have a pgrp, a session,
+ a login name set with proc_setlogin, it cannot have called proc_child,
+ and it cannot have a message port. */
+/* If this call is made for a process which is no longer alive or
+ does not exist, then newtask will be killed (and no error
+ is returned). */
+simpleroutine proc_reassign_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ newtask: task_t);
+
+/* Set the owner of the process. The new owner must be within the
+ current authentication of the caller. */
+simpleroutine proc_setowner_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ owner: uid_t;
+ clear: int);
+
+/* Get the process ID and the parent process ID, also find out if we
+ are orphaned. */
+simpleroutine proc_getpids_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
+
+/* Set the locations of ARGV and ENVP which will be examined
+ by proc_getprocargs and proc_getprocenv. */
+simpleroutine proc_set_arg_locations_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ argv: vm_address_t;
+ envp: vm_address_t);
+
+/* Fetch the locations set by proc_set_arg_locations. */
+simpleroutine proc_get_arg_locations_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
+
+/* Fetch the message port of a process */
+simpleroutine proc_getmsgport_request (
+ process: process_t;
+ replyport reply: reply_port_t;
+ pid: pid_t);
+
+/* Wait for a child process to exit. If pid is zero, it waits for any
+ child in the same pgrp as the parent. If pid is -1, it waits for
+ any child. Otherwise, if pid is negative, it waits for any process
+ in the specified process group. If pid is positive, it waits for
+ the specified process. The exit status and resource usage of the
+ process are returned in status and rusage respectively. */
+simpleroutine proc_wait_request (
+ process: process_t;
+ replyport reply: reply_port_t;
+ pid: pid_t;
+ options: int);
+
+/* Have the process server stop all threads except contthread. */
+simpleroutine proc_dostop_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ contthread: thread_t);
+
+/* Take over exception messages received on MSGPORT. Any exception
+ messages received will be forwarded to FORWARDPORT. In addition,
+ the thread causing the exception will be aborted (with thread_abort)
+ and have its state adjusted (as by thread_set_state). */
+simpleroutine proc_handle_exceptions_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ msgport: mach_port_move_receive_t;
+ forwardport: mach_port_send_t;
+ flavor: int;
+ new_state: thread_state_t);
+
+
+
+/*** Mark bits. Some of these (exec, traced, eg) modify small pieces
+ of the proc server's behavior; others are purely informational. ***/
+
+/* Mark the process as stopped on a signal. */
+simpleroutine proc_mark_stop_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ signo: int;
+ sigcode: int);
+
+/* Mark the process as continued after a stop. */
+simpleroutine proc_mark_cont_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
+
+/* Tell the process server that the process is going away. If it dies
+ without calling this, it will look to the parent like the process
+ died with SIGKILL. This call allows for a cleaner exit. */
+simpleroutine proc_mark_exit_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ status: int;
+ sigcode:int );
+
+/* Inform the process server that the process has completed an exec. */
+simpleroutine proc_mark_exec_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
+
+/* Inform the process server that the process has asked to be traced.
+ The only result of this is to change the behavior of wait by the
+ parent slightly. */
+simpleroutine proc_mark_traced_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
+
+/* Inform the process server whether SIGCHLD should be sent for stopped
+ child processes. */
+simpleroutine proc_mod_stopchild_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ doit: int);
+
+
+/*** Miscellaneous process information queries. ***/
+
+simpleroutine proc_pid2task_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ pid: pid_t);
+
+simpleroutine proc_task2pid_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ task: task_t);
+
+/* Return the procserver port for the specified task. */
+simpleroutine proc_task2proc_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ task: task_t);
+
+simpleroutine proc_proc2task_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
+
+simpleroutine proc_pid2proc_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ pid: pid_t);
+
+simpleroutine proc_getprocinfo_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ which: pid_t;
+ flags: int);
+
+simpleroutine proc_getprocargs_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ which: pid_t);
+
+simpleroutine proc_getprocenv_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ which: pid_t);
+
+/* Create a new login collection. The pid of PROCESS will be the id
+ of the collection. All the children (and their children, and so forth) of
+ PROCESS will automatically join the collection. If PROCESS dies its
+ children (and their children, and so forth) will become part of init's
+ process collection. */
+simpleroutine proc_make_login_coll_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
+
+/* Get the process collection ID for the process specified by pid. */
+simpleroutine proc_getloginid_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ pid: pid_t);
+
+/* Get the pids of all the members of process collection ID. */
+simpleroutine proc_getloginpids_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ id: pid_t);
+
+/* These next two should not actually be used; they are here
+ for "historic reasons." You are not expected to understand this. */
+simpleroutine proc_setlogin_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ logname: string_t);
+
+simpleroutine proc_getlogin_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
+
+
+/*** Sessions and process groups. ***/
+
+/* Create a new session. The process's pgrp is set to its pid, and
+ becomes the session leader. */
+simpleroutine proc_setsid_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
+
+/* Return the pid of a process's session leader. */
+simpleroutine proc_getsid_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ pid: pid_t);
+
+/* Get the pgids of all the members of a session. */
+simpleroutine proc_getsessionpgids_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ sid: pid_t);
+
+/* Get the pids of all the members of a session. */
+simpleroutine proc_getsessionpids_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ sid: pid_t);
+
+/* Return a "session ID" port for the session of the caller.
+ This port is generally only available to members of the session.
+ (It has no listener, but is useful for identification purposes.) */
+simpleroutine proc_getsidport_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
+
+/* Set the process group of a process. */
+simpleroutine proc_setpgrp_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ pid: pid_t;
+ pgrp: pid_t);
+
+/* Get the process group of a process. */
+simpleroutine proc_getpgrp_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ pid: pid_t);
+
+/* Get the pids of all the members of a pgrp. */
+simpleroutine proc_getpgrppids_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ pgrp: pid_t);
+
+simpleroutine proc_get_tty_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ pid: pid_t);
+
+/* Return the number of Mach ports used by PID */
+simpleroutine proc_getnports_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ which: pid_t);
+
+/*** Routines related to early server bootstrapping ***/
+
+skip; /* Reserved for proc_set_init_task */
+
+/* Inform the process server that the process is important. */
+simpleroutine proc_mark_important_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
+
+/* Query whether the process is important. */
+simpleroutine proc_is_important_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
+
+/* Set the processes start_code and end_code locations. Any
+ executable segments loaded from the ELF binary are in this
+ range. */
+simpleroutine proc_set_code_request (
+ process: process_t;
+ ureplyport reply: reply_port_t;
+ start_code: vm_address_t;
+ end_code: vm_address_t);
+
+/* Get the processes start_code and end_code locations. Any
+ executable segments loaded from the ELF binary are in this range.
+ If zero is returned for these values, the requested information has
+ never been set. */
+simpleroutine proc_get_code_request (
+ process: process_t;
+ ureplyport reply: reply_port_t);
diff --git a/hurd/shared.h b/hurd/shared.h
new file mode 100644
index 00000000..25747eb9
--- /dev/null
+++ b/hurd/shared.h
@@ -0,0 +1,158 @@
+/* Definitions for shared IO control pages
+ Copyright (C) 1992, 1993, 1994 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <pthread.h>
+#include <sys/types.h> /* Defines `off_t'. */
+
+struct shared_io
+{
+ int shared_page_magic;
+
+ /* This lock protects against modification to conch_status. */
+ pthread_spinlock_t lock;
+
+ enum
+ {
+ USER_HAS_CONCH, /* User is it */
+ USER_COULD_HAVE_CONCH, /* User can become it */
+ USER_RELEASE_CONCH, /* User is it, should release it promptly */
+ USER_HAS_NOT_CONCH, /* User is not it */
+ } conch_status;
+
+
+ /* While you hold the conch, the shared page will not change (except the
+ conch-status word might be changed from USER_HAS_CONCH to
+ USER_RELEASE_CONCH). In addition, cooperating users will not change
+ the contents of the file. The I/O server is a cooperating user itself
+ in its implementation of io_read, io_write, and so forth. The I/O
+ server is a separate user from all the shared I/O users. If a user
+ does not release the conch "promptly" then the conch may be stolen
+ from that user by the I/O server. "Promptly" will probably mean a few
+ seconds.
+
+ As a consequence of these rules, if you hold the shared page, io_read
+ and so forth will block until you release the conch. You cannot
+ reliably predict what I/O operations in the server (in the io.defs
+ preceding the comment `Definitions for mapped I/O') might need the
+ conch, as a consequence, you should normally not call such functions
+ while you are holding the conch if that could cause a deadlock. */
+
+
+ /* These values are set by the IO server only: */
+
+ int append_mode; /* append on each write */
+
+ int eof_notify; /* notify filesystem upon read of eof */
+ int do_sigio; /* call io_sigio after each operation */
+
+ int use_file_size; /* file_size is meaningful */
+
+ int use_read_size; /* read_size is meaningful */
+ off_t read_size;
+
+ blksize_t optimal_transfer_size; /* users should try to have the
+ arguments to io_prenotify, etc. be
+ multiples of this value if it is
+ nonzero. */
+
+ enum
+ {
+ /* This means that there isn't any data to be read */
+ RBR_NO_DATA,
+
+ /* This means that more data cannot be added to the buffer. If
+ the rd_file_pointer is advanced, then more data might become
+ readable. This condition has priority over NO_DATA: protocols
+ might refuse to receive data when the buffer is full; then this
+ will be BUFFER_FULL. If file pointer gets advanced, then the
+ protocol will tell the sender to go ahead, and the read_block_reason
+ will be NO_DATA until the first data arrives.
+ */
+ RBR_BUFFER_FULL,
+
+ /* These conditions are generally only meaningful for nonseekable
+ objects. */
+ }
+ read_block_reason; /* identifies what holds up reading */
+
+ int seekable; /* the file pointer can be reduced */
+
+ int use_prenotify_size; /* prenotify_size is meaningful */
+ int use_postnotify_size; /* postnotify_size is meaningful */
+ int use_readnotify_size; /* readnotify_size is meaningful */
+
+ off_t prenotify_size;
+ off_t postnotify_size;
+ off_t readnotify_size;
+
+
+ /* These are set by both the IO server and the user: */
+
+ /* If the read and write objects returned by io_map are the same,
+ then use the xx_file_pointer for read, write, and seek. If the
+ read and write objects are not the same, then use the
+ rd_file_pointer for read and the wr_file_pointer for write.
+ Normally in this case the seekable value will be false.
+ The unused file pointers will be set to -1 by the I/O server. */
+ off_t rd_file_pointer;
+ off_t wr_file_pointer;
+ off_t xx_file_pointer;
+
+ off_t file_size;
+
+ /* These two indicate that the appropriate times need updated */
+ int written;
+ int accessed;
+
+
+ /* File structuring: */
+
+ /* If the file is not seekable and read data is separate from write
+ data, then the read data might be structured. Each record is
+ identified by one of these structures. The "auxil" field
+ contains extra data which might be of interest to some readers,
+ but is not part of the data proper (for example, UDP and raw IP
+ put the internet headers there).
+
+ The IO server guarantees that these will be consecutive, and that
+ the file_pointer_start of each record will be that of the last
+ plus its data_length. The last valid structure might grow
+ whenever the server is it. All previous records from the
+ rd_file_pointer to the current read_size/file_size will not
+ change. Records before that can be dropped and the valid records
+ moved forward in the array (when the server is it); if this
+ happens indexes_changed will be set to the number of records
+ dropped. */
+
+ int indexes_changed; /* users can clear this when they want */
+
+ /* Users should not modify the rest of this: */
+ int use_structure; /* structure is being used */
+ struct iomap_structure
+ {
+ int file_pointer_start; /* file pointer offset of data */
+ int object_start; /* offset of auxil in memory object */
+ int auxil_length; /* length of auxil data */
+ int data_length; /* length of real data */
+ } structure[0];
+};
+
+/* Look at this value to determine the byte order the server is using,
+ and then use it. */
+#define SHARED_PAGE_MAGIC 0xaabbccdd
diff --git a/hurd/socket.defs b/hurd/socket.defs
new file mode 100644
index 00000000..53219077
--- /dev/null
+++ b/hurd/socket.defs
@@ -0,0 +1,136 @@
+/* Definitions for socket interface
+ Copyright (C) 1991,93,94,95,2001,02 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Sockets also support the generic IO facilities. */
+
+subsystem socket 26000;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef SOCKET_IMPORTS
+SOCKET_IMPORTS
+#endif
+
+INTR_INTERFACE
+
+/* Create a new socket. Sock type is, for example, SOCK_STREAM,
+ SOCK_DGRAM, or some such. */
+routine socket_create (
+ server: pf_t;
+ sock_type: int;
+ protocol: int;
+ out sock: mach_port_send_t);
+
+/* Prepare a socket of appropriate type for future accept operations. */
+routine socket_listen (
+ sock: socket_t;
+ queue_limit: int);
+
+/* Return a new connection from a socket previously listened. */
+routine socket_accept (
+ sock: socket_t;
+ out conn_sock: mach_port_send_t;
+ out peer_addr: mach_port_send_t);
+
+/* Connect to an address. */
+routine socket_connect (
+ sock: socket_t;
+ addr: addr_port_t);
+
+/* Bind a socket to an address. */
+routine socket_bind (
+ sock: socket_t;
+ addr: addr_port_t);
+
+/* Find out the name of a socket. */
+routine socket_name (
+ sock: socket_t;
+ out addr: mach_port_send_t);
+
+/* Find out the name of the socket's peer. */
+routine socket_peername (
+ sock: socket_t;
+ out addr: mach_port_send_t);
+
+/* Connect two sockets */
+routine socket_connect2 (
+ sock1: socket_t;
+ sock2: socket_t);
+
+/* Create an address from a sockaddr. */
+routine socket_create_address (
+ server: mach_port_t; /* Can be either pf_t or socket_t. */
+ sockaddr_type: int;
+ sockaddr: data_t SCP;
+ out addr: mach_port_send_t);
+
+/* Create an address without any sockaddr. */
+routine socket_fabricate_address (
+ server: mach_port_t; /* Can be either pf_t or socket_t. */
+ sockaddr_type: int;
+ out addr: mach_port_send_t);
+
+/* Find the sockaddr name of an address. */
+routine socket_whatis_address(
+ addr: addr_port_t;
+ out sockaddr_type: int;
+ out sockaddr: data_t, dealloc);
+
+/* Shutdown a socket for reading or writing. */
+routine socket_shutdown (
+ sock: socket_t;
+ direction: int);
+
+/* XXX to become ioctl hack */
+/* Get a socket option. */
+routine socket_getopt (
+ sock: socket_t;
+ level: int;
+ option: int;
+ out optval: data_t, dealloc);
+
+/* XXX to become ioctl hack */
+/* Set a socket option. */
+routine socket_setopt (
+ sock: socket_t;
+ level: int;
+ option: int;
+ optval: data_t SCP);
+
+/* Send data over a socket, possibly including Mach ports. */
+routine socket_send (
+ sock: socket_t;
+ addr: addr_port_t;
+ flags: int;
+ data: data_t SCP;
+ ports: portarray_t SCP;
+ control: data_t SCP;
+ out amount: vm_size_t);
+
+/* Receive data from a socket, possibly including Mach ports. */
+routine socket_recv (
+ sock: socket_t;
+ out addr: mach_port_send_t;
+ flags: int;
+ out data: data_t, dealloc;
+ out ports: portarray_t, dealloc;
+ out control: data_t, dealloc;
+ out outflags: int;
+ amount: vm_size_t);
diff --git a/hurd/startup.defs b/hurd/startup.defs
new file mode 100644
index 00000000..4b14e206
--- /dev/null
+++ b/hurd/startup.defs
@@ -0,0 +1,80 @@
+/* Definitions for startup server interface
+ Copyright (C) 1991, 1992, 1993, 1994, 1996, 1999 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+subsystem startup 29000;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef STARTUP_IMPORTS
+STARTUP_IMPORTS
+#endif
+
+
+/* This call registers a task as "essential", which means that if it
+ dies, the system should crash. If the specified task dies or an
+ exception message is sent to the exception port, then we will print
+ a message on the console and do the crash. */
+routine startup_essential_task (
+ server: startup_t;
+ sreplyport reply_port: sreply_port_t;
+ task: task_t;
+ excpt: mach_port_move_receive_t;
+ name: string_t;
+ credential: host_priv_t);
+
+/* This call registers a task as "notified", which means that if the
+ system is going down, we should be told about it and get a chance to
+ run. A startup_dosync message (see startup_notify.defs) will be
+ sent to the notify port. NAME will be used to provide a helpful
+ message to the user when the system is shutting down. */
+routine startup_request_notification (
+ server: startup_t;
+ notify_port: mach_port_send_t;
+ name: string_t);
+
+/* This call causes the system to die. */
+routine startup_reboot (
+ server: startup_t;
+ refport: mach_port_t;
+ reboot_code: int);
+
+/* NOTE: All changes to these last two must be reflected in
+ startup_reply.defs. */
+
+/* This call is made by the proc server for its initialization, on its
+ bootstrap port. */
+routine startup_procinit (
+ server: startup_t;
+ sreplyport reply_port: sreply_port_t;
+ startup_proc: process_t;
+ out startup_task: task_t;
+ out auth: auth_t;
+ out host_priv: mach_port_send_t;
+ out device_master: mach_port_send_t);
+
+/* This call is made by the auth server for its initialization.
+ The auth server will create an all-root authentication handle
+ to give to the startup server. */
+routine startup_authinit (
+ server: startup_t;
+ sreplyport reply_port: sreply_port_t;
+ auth: mach_port_send_t;
+ out proc: mach_port_send_t);
diff --git a/hurd/startup_notify.defs b/hurd/startup_notify.defs
new file mode 100644
index 00000000..50712e9d
--- /dev/null
+++ b/hurd/startup_notify.defs
@@ -0,0 +1,35 @@
+/* Callbacks issued by startup server
+ Copyright (C) 1996 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+subsystem startup_notify 29500;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef STARTUP_NOTIFY_IMPORTS
+STARTUP_NOTIFY_IMPORTS
+#endif
+
+/* The system is going down; the recipient of this call is receiving
+ the notification previously requested by
+ startup_request_notification. */
+routine startup_dosync (
+ notify_port: mach_port_t;
+ waittime timeout: natural_t);
diff --git a/hurd/startup_reply.defs b/hurd/startup_reply.defs
new file mode 100644
index 00000000..a8462676
--- /dev/null
+++ b/hurd/startup_reply.defs
@@ -0,0 +1,52 @@
+/* Server-reply definitions for startup server interface
+ NOTE: All changes here must be reflected in startup.defs.
+ Copyright (C) 1991,92,93,94,2001 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell and Roland McGrath. */
+
+subsystem startup_reply 29100; /* Must 100 more than startup subsystem. */
+
+#include <hurd/hurd_types.defs>
+
+type reply_port_t = polymorphic | MACH_MSG_TYPE_PORT_SEND_ONCE
+ ctype: mach_port_t;
+
+#ifdef STARTUP_IMPORTS
+STARTUP_IMPORTS
+#endif
+
+simpleroutine startup_essential_task_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG);
+
+skip; /* startup_request_notification */
+skip; /* startup_reboot */
+
+simpleroutine startup_procinit_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ startup_task: task_t;
+ auth: auth_t;
+ host_priv: mach_port_send_t;
+ device_master: mach_port_send_t);
+
+simpleroutine startup_authinit_reply (
+ reply_port: reply_port_t;
+ RETURN_CODE_ARG;
+ proc: mach_port_send_t);
diff --git a/hurd/subsystems b/hurd/subsystems
new file mode 100644
index 00000000..c05895c2
--- /dev/null
+++ b/hurd/subsystems
@@ -0,0 +1,45 @@
+These are the base MiG code numbers for each subsystem in the GNU
+Hurd. Each subsystem takes 200 in space. Subsystems marked with a *
+are Mach kernel subsystems. Subsystems marked with ? may or may not
+be used in the Hurd and are defined by Mach non-kernel source.
+
+
+* notify 64 IPC notifications
+* mach 2000 Generic kernel calls
+* memory_object 2200 Kernel -> pager calls
+* memory_object_default 2250 Extension to memory_object for default pager
+? default_pager 2275 Default pager control
+* exc 2400 Exceptions
+* mach_host 2600 Kernel calls, mostly for multiprocessors
+* device 2800 Physical device interface
+* mach_debug 3000 Kernel debugging and statistics
+* mach_port 3200 Port name and IPC frobbing
+* mach4 4000 PC sampling
+fs 20000 Filesystem nodes
+fs_notify 20500 Notification callbacks from fs servers to their clients
+io 21000 Generic IO
+fsys 22000 Filesystem control operations
+msg 23000 Calls made on process message ports
+process 24000 Process abstraction
+auth 25000 Authentication
+socket 26000 Sockets
+newterm 27000 Creation of terminal processing thingies
+term 28000 Terminal-specific operations
+startup 29000 System initialization and destruction
+startup_notify 29500 Callbacks from startup server
+exec 30000 Process execution
+exec_startup 30500 Process startup communication
+crash 32000 Program crash handling (core dumps)
+intr 33000 Interruption
+ifsock 34000 S_IFSOCK node protocol for AF_LOCAL rendezvous
+tape 35000 Special control operations for magtapes
+login 36000 Database of logged-in users
+pfinet 37000 Internet configuration calls
+password 38000 Password checker
+<ioctl space> 100000- First subsystem of ioctl class 'f' (lowest class)
+tioctl 156000 Ioctl class 't' (terminals)
+tioctl 156200 (continued)
+<ioctl space> 164200 Last subsystem of ioctl class 'v' (highest class)
+* mach_norma 555000 NORMA machine additions; sort of deprecated right now
+* norma_task 666000 NORMA remote execution
+? dp_helper 888888 pager -> fileserver; asking for more paging space
diff --git a/hurd/term.defs b/hurd/term.defs
new file mode 100644
index 00000000..17ba4f38
--- /dev/null
+++ b/hurd/term.defs
@@ -0,0 +1,122 @@
+/* Special protocol for terminal driver
+ Copyright (C) 1991, 1993, 1994, 1999 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+subsystem term 28000;
+
+#include <hurd/hurd_types.defs>
+
+#ifdef TERM_IMPORTS
+TERM_IMPORTS
+#endif
+
+INTR_INTERFACE
+
+/* Find out what the controlling terminal ID port is. */
+routine term_getctty (
+ terminal: io_t;
+ out ctty: mach_port_send_t);
+
+/*
+Return a controlling terminal port for this terminal. This has the
+following effects:
+
+Certain input characters in certain modes will cause signals to be
+sent to foreground processes which have made this call. The ctty ID
+will be used as the reference port. Certain conditions will cause
+SIGHUP to be sent. The foreground process group is the same as the
+owner defined by io_set_owner and io_get_owner.
+
+When background processes do certain operations on a port returned by
+term_become_ctty, the EBACKGROUND error may be returned. This occurs
+for io_read always, io_write if the appropriate bit is set in the
+status, and various control operations always. The vanilla port to
+the terminal can still be used to do these operations.
+
+A "foreground process" is one making a request over a port returned by
+term_become_ctty whose pid (as provided in the term_become_ctty call)
+matches the current owner (as set by io_mod_owner) or whose pgrp
+matches the owner in the same fashion. A "background process" is one
+making a request over a port returned by term_become_ctty which is not
+a foreground process. */
+routine term_open_ctty (
+ terminal: io_t;
+ pid: pid_t;
+ pgrp: pid_t;
+ out newtty: mach_port_send_t);
+
+/* This sets the name returned by future get_nodename calls. This is
+ conventionally the name of a file which can be opened, resulting in
+ a clone of this port. */
+routine term_set_nodename (
+ terminal: io_t;
+ name: string_t);
+
+/* Return the last value set with set_nodename. */
+routine term_get_nodename (
+ terminal: io_t;
+ out name: string_t);
+
+/* Set the underlying file to be used for chown/chmod, etc. */
+routine term_set_filenode (
+ terminal: io_t;
+ filenode: file_t);
+
+/* Find out what the bottom half of this terminal is using. */
+routine term_get_bottom_type (
+ terminal: io_t;
+ out ttype: int);
+
+/* Start running with the bottom half as a device port using the Mach
+ kernel device interface. The old bottom half (if any) is
+ discarded. */
+routine term_on_machdev (
+ terminal: io_t;
+ machdev: device_t);
+
+/* Start running with the bottom half as a hurd I/O port. (It is
+ assumed that io_t is being served by a Hurd I/O server). */
+routine term_on_hurddev (
+ terminal: io_t;
+ hurddev: io_t);
+
+/* Start running with the bottom half being an exported hurd I/O port.
+ This differs from term_on_hurddev in that with term_on_pty the
+ terminal driver will serve the port. The returned port is a pty,
+ similar in operation to the entity of the same name in BSD. */
+routine term_on_pty (
+ terminal: io_t;
+ out ptymaster: io_t);
+
+/* This call is made to the ctty port returned by term_getctty; it may
+ not be made to terminal I/O ports. Return an unauthenticated I/O
+ port for the terminal opened as with flags FLAGS. */
+routine termctty_open_terminal (
+ ctty: mach_port_t;
+ flags: int;
+ out terminal: mach_port_send_t);
+
+/* This call is only supported for PTY-based terminals. For a slave,
+ it returns a filename which, when opened, would yield the master for
+ this terminal. For a master, it returns a filename which, when
+ opened, would yield the slave for this terminal. */
+routine term_get_peername (
+ terminal: io_t;
+ out name: string_t);
diff --git a/hurd/tioctl.defs b/hurd/tioctl.defs
new file mode 100644
index 00000000..58ee698f
--- /dev/null
+++ b/hurd/tioctl.defs
@@ -0,0 +1,258 @@
+/* Definitions for terminal ioctls
+ Copyright (C) 1991,93,94,95,96,99,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <hurd/hurd_types.defs>
+
+#ifdef TIOCTL_IMPORTS
+TIOCTL_IMPORTS
+#endif
+
+INTR_INTERFACE
+
+/* Ioctl class `t'; the subsystem is derived from calculations in
+ <ioctls.h>. */
+subsystem tioctl 156000; /* XXX */
+
+import <hurd/ioctl_types.h>; /* XXX */
+
+/* These are the pieces of a struct termios as specified by the
+ definition of _IOT_termios in <termbits.h>. */
+type modes_t = array[4] of int;
+type ccs_t = array[20] of char;
+type speeds_t = array[2] of int;
+
+/* This is the arg for a struct winsize as specified by the
+ definition of _IOT_winsize in <sys/ioctl.h>. */
+type winsize_t = struct[4] of short;
+
+skip; skip; skip; /* 0 1 2 unused */
+
+/* 3 TIOCMODG */
+routine tioctl_tiocmodg (
+ reqport: io_t;
+ out state: int);
+
+/* 4 TIOCMODS */
+routine tioctl_tiocmods (
+ reqport: io_t;
+ state: int);
+
+skip; skip; skip; skip; /* 5 6 7 8 unused */
+skip; skip; skip; skip; /* 9 10 11 12 unused */
+
+/* 13 TIOCEXCL */
+routine tioctl_tiocexcl (
+ reqport: io_t);
+
+/* 14 TIOCNXCL */
+routine tioctl_tiocnxcl (
+ reqport: io_t);
+
+skip; /* 15 unused */
+
+/* 16 TIOCFLUSH */
+routine tioctl_tiocflush (
+ reqport: io_t;
+ queue_selector: int);
+
+skip; skip; /* 17 18 unused */
+
+/* 19 TIOCGETA */
+routine tioctl_tiocgeta (
+ port: io_t;
+ out modes: modes_t;
+ out ccs: ccs_t;
+ out speeds: speeds_t);
+
+/* 20 TIOCSETA */
+routine tioctl_tiocseta (
+ port: io_t;
+ modes: modes_t;
+ ccs: ccs_t;
+ speeds: speeds_t);
+
+/* 21 TIOCSETAW */
+/* INTR */
+routine tioctl_tiocsetaw (
+ port: io_t;
+ modes: modes_t;
+ ccs: ccs_t;
+ speeds: speeds_t);
+
+/* 22 TIOCSETAF */
+/* INTR */
+routine tioctl_tiocsetaf (
+ port: io_t;
+ modes: modes_t;
+ ccs: ccs_t;
+ speeds: speeds_t);
+
+skip; skip; skip; /* 23 24 25 unused */
+
+/* 26 TIOCGETD */
+routine tioctl_tiocgetd (
+ port: io_t;
+ out discipline: int);
+
+/* 27 TIOCSETD */
+routine tioctl_tiocsetd (
+ port: io_t;
+ discipline: int);
+
+skip; skip; /* 28 29 unused */
+skip; skip; skip; skip; skip; skip; skip; skip; skip; skip; /* 30-39 unused */
+skip; skip; skip; skip; skip; skip; skip; skip; skip; skip; /* 40-49 unused */
+skip; skip; skip; skip; skip; skip; skip; skip; skip; skip; /* 50-59 unused */
+skip; skip; skip; skip; skip; skip; skip; skip; skip; skip; /* 60-69 unused */
+skip; skip; skip; skip; skip; skip; skip; skip; skip; skip; /* 70-79 unused */
+skip; skip; skip; skip; skip; skip; skip; skip; skip; skip; /* 80-89 unused */
+skip; skip; skip; skip; /* 90 91 92 93 unused */
+
+/* 94 TIOCDRAIN */
+/* INTR */
+routine tioctl_tiocdrain (
+ port: io_t);
+
+/* 95 TIOCSIG */
+routine tioctl_tiocsig (
+ port: io_t;
+ signal: int);
+
+/* 96 TIOCEXT */
+routine tioctl_tiocext (
+ port: io_t;
+ mode: int);
+
+skip; /* 97 TIOCSCTTY -- implemented in C library */
+skip; /* 98 TIOCCONS -- implemented in C library */
+
+skip; /* 99 unused */
+
+/* Because MiG defines reply ports as 100 more than request ports, we
+ have to leave one hundred empty RPC's here. */
+skip; skip; skip; skip; skip; skip; skip; skip; skip; skip;
+skip; skip; skip; skip; skip; skip; skip; skip; skip; skip;
+skip; skip; skip; skip; skip; skip; skip; skip; skip; skip;
+skip; skip; skip; skip; skip; skip; skip; skip; skip; skip;
+skip; skip; skip; skip; skip; skip; skip; skip; skip; skip;
+
+skip; skip; skip; skip; skip; skip; skip; skip; skip; skip;
+skip; skip; skip; skip; skip; skip; skip; skip; skip; skip;
+skip; skip; skip; skip; skip; skip; skip; skip; skip; skip;
+skip; skip; skip; skip; skip; skip; skip; skip; skip; skip;
+skip; skip; skip; skip; skip; skip; skip; skip; skip; skip;
+
+
+skip; skip; /* 100 101 unused */
+
+/* 102 TIOCUCNTL */
+routine tioctl_tiocucntl (
+ port: io_t;
+ set_or_clear: int);
+
+/* 103 TIOCSWINSZ */
+routine tioctl_tiocswinsz (
+ port: io_t;
+ sizes: winsize_t);
+
+/* 104 TIOCGWINSZ */
+routine tioctl_tiocgwinsz (
+ port: io_t;
+ out sizes: winsize_t);
+
+/* 105 TIOCREMOTE */
+routine tioctl_tiocremote (
+ port: io_t;
+ on_or_off: int);
+
+/* 106 TIOCMGET */
+routine tioctl_tiocmget (
+ port: io_t;
+ out bits: int);
+
+/* 107 TIOCMBIC */
+routine tioctl_tiocmbic (
+ port: io_t;
+ bits: int);
+
+/* 108 TIOCMBIS */
+routine tioctl_tiocmbis (
+ port: io_t;
+ bits: int);
+
+/* 109 TIOCMSET */
+routine tioctl_tiocmset (
+ port: io_t;
+ bits: int);
+
+/* 110 TIOCSTART */
+routine tioctl_tiocstart (
+ port: io_t);
+
+/* 111 TIOCSTOP */
+routine tioctl_tiocstop (
+ port: io_t);
+
+/* 112 TIOCPKT */
+routine tioctl_tiocpkt (
+ port: io_t;
+ on_or_off: int);
+
+skip; /* 113 TIOCNOTTY -- implemented in C library */
+
+/* 114 TIOCSTI */
+routine tioctl_tiocsti (
+ port: io_t;
+ datum: char);
+
+/* 115 TIOCOUTQ */
+routine tioctl_tiocoutq (
+ port: io_t;
+ out queue_size: int);
+
+skip; skip; /* 116 117 unused */
+
+/* 118 TIOCSPGRP */
+routine tioctl_tiocspgrp (
+ port: io_t;
+ pgrp: int);
+
+/* 119 TIOCGPGRP */
+routine tioctl_tiocgpgrp (
+ port: io_t;
+ out pgrp: int);
+
+/* 120 TIOCCDTR */
+routine tioctl_tioccdtr (
+ port: io_t);
+
+/* 121 TIOCSDTR */
+routine tioctl_tiocsdtr (
+ port: io_t);
+
+/* 122 TIOCCBRK */
+routine tioctl_tioccbrk (
+ port: io_t);
+
+/* 123 TIOCSBRK */
+routine tioctl_tiocsbrk (
+ port: io_t);
+
+/* 124 - 256 unused */
diff --git a/hurd/version.h b/hurd/version.h
new file mode 100644
index 00000000..9b85b5a5
--- /dev/null
+++ b/hurd/version.h
@@ -0,0 +1,6 @@
+/* This file just gives a value that can be tested easily with #if as a
+ gross check of the Hurd interface version. It has the format YYYYMMDD
+ and will only ever be increased. This will be bumped whenever either
+ the RPC interfaces or the library APIs change. */
+
+#define HURD_INTERFACE_VERSION 20020609
diff --git a/include/Makefile b/include/Makefile
new file mode 100644
index 00000000..4de165d1
--- /dev/null
+++ b/include/Makefile
@@ -0,0 +1,32 @@
+# Makefile for include subdirectory.
+#
+# Copyright (C) 1996, 2002, 2010, 2012 Free Software Foundation, Inc.
+# Written by Michael I. Bushnell, p/BSG.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+
+dir := include
+makemode := misc
+
+installhdrs := sys/procfs.h refcount.h
+
+include ../Makeconf
+
+install-headers install: $(installhdrs:%=$(includedir)/%)
+$(includedir)/%: $(srcdir)/%
+ @$(MKINSTALLDIRS) $(@D)
+ $(INSTALL_DATA) $< $@
diff --git a/include/pids.h b/include/pids.h
new file mode 100644
index 00000000..22415f4f
--- /dev/null
+++ b/include/pids.h
@@ -0,0 +1,29 @@
+/* List of special processes.
+
+ Copyright (C) 2013 Free Software Foundation
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _HURD_PROCESSES_H
+#define _HURD_PROCESSES_H
+
+#define HURD_PID_STARTUP 1
+#define HURD_PID_KERNEL 2
+#define HURD_PID_PROC 3
+
+#endif /* _HURD_PROCESSES_H */
diff --git a/include/refcount.h b/include/refcount.h
new file mode 100644
index 00000000..785b052a
--- /dev/null
+++ b/include/refcount.h
@@ -0,0 +1,263 @@
+/* Lock-less reference counting primitives
+
+ Copyright (C) 2014 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _HURD_REFCOUNT_H_
+#define _HURD_REFCOUNT_H_
+
+#include <assert.h>
+#include <limits.h>
+#include <stdint.h>
+
+/* Simple reference counting. */
+
+/* An opaque type. You must not access these values directly. */
+typedef unsigned int refcount_t;
+
+/* Initialize REF with REFERENCES. */
+static inline void
+refcount_init (refcount_t *ref, unsigned int references)
+{
+ *ref = references;
+}
+
+/* Increment REF. Return the result of the operation. This function
+ uses atomic operations. It is not required to serialize calls to
+ this function. */
+static inline unsigned int
+refcount_ref (refcount_t *ref)
+{
+ unsigned int r;
+ r = __atomic_add_fetch (ref, 1, __ATOMIC_RELAXED);
+ assert (r != UINT_MAX || !"refcount overflowed!");
+ return r;
+}
+
+/* Decrement REF. Return the result of the operation. This function
+ uses atomic operations. It is not required to serialize calls to
+ this function. */
+static inline unsigned int
+refcount_deref (refcount_t *ref)
+{
+ unsigned int r;
+ r = __atomic_sub_fetch (ref, 1, __ATOMIC_RELAXED);
+ assert (r != UINT_MAX || !"refcount underflowed!");
+ return r;
+}
+
+/* Return REF. This function uses atomic operations. It is not
+ required to serialize calls to this function. */
+static inline unsigned int
+refcount_references (refcount_t *ref)
+{
+ return __atomic_load_n (ref, __ATOMIC_RELAXED);
+}
+
+/* Reference counting with weak references. */
+
+/* An opaque type. You must not access these values directly. */
+typedef union _references refcounts_t;
+
+/* Instead, the functions manipulating refcounts_t values write the
+ results into this kind of objects. */
+struct references {
+ /* We chose the layout of this struct so that when it is used in the
+ union _references, the hard reference counts occupy the least
+ significant bits. We rely on this layout for atomic promotion
+ and demotion of references. See refcounts_promote and
+ refcounts_demote for details. */
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ uint32_t hard;
+ uint32_t weak;
+#else
+ uint32_t weak;
+ uint32_t hard;
+#endif
+};
+
+/* We use a union to convert struct reference values to uint64_t which
+ we can manipulate atomically. While this behavior is not
+ guaranteed by the C standard, it is supported by all major
+ compilers. */
+union _references {
+ struct references references;
+ uint64_t value;
+};
+
+/* Initialize REF with HARD and WEAK references. */
+static inline void
+refcounts_init (refcounts_t *ref, uint32_t hard, uint32_t weak)
+{
+ ref->references = (struct references) { .hard = hard, .weak = weak };
+}
+
+/* Increment the hard reference count of REF. If RESULT is not NULL,
+ the result of the operation is written there. This function uses
+ atomic operations. It is not required to serialize calls to this
+ function. */
+static inline void
+refcounts_ref (refcounts_t *ref, struct references *result)
+{
+ const union _references op = { .references = { .hard = 1 } };
+ union _references r;
+ r.value = __atomic_add_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
+ assert (r.references.hard != UINT32_MAX || !"refcount overflowed!");
+ if (result)
+ *result = r.references;
+}
+
+/* Decrement the hard reference count of REF. If RESULT is not NULL,
+ the result of the operation is written there. This function uses
+ atomic operations. It is not required to serialize calls to this
+ function. */
+static inline void
+refcounts_deref (refcounts_t *ref, struct references *result)
+{
+ const union _references op = { .references = { .hard = 1 } };
+ union _references r;
+ r.value = __atomic_sub_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
+ assert (r.references.hard != UINT32_MAX || !"refcount underflowed!");
+ if (result)
+ *result = r.references;
+}
+
+/* Promote a weak reference to a hard reference. If RESULT is not
+ NULL, the result of the operation is written there. This function
+ uses atomic operations. It is not required to serialize calls to
+ this function. */
+static inline void
+refcounts_promote (refcounts_t *ref, struct references *result)
+{
+ /* To promote a weak reference, we need to atomically subtract 1
+ from the weak reference count, and add 1 to the hard reference
+ count.
+
+ We can subtract by 1 by adding the two's complement of 1 = ~0 to
+ a fixed-width value, discarding the overflow.
+
+ We do the same in our uint64_t value, but we have chosen the
+ layout of struct references so that when it is used in the union
+ _references, the weak reference counts occupy the most
+ significant bits. When we add ~0 to the weak references, the
+ overflow will be discarded as unsigned arithmetic is modulo 2^n.
+ So we just add a hard reference. In combination, this is the
+ desired operation. */
+ const union _references op =
+ { .references = { .weak = ~0U, .hard = 1} };
+ union _references r;
+ r.value = __atomic_add_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
+ assert (r.references.hard != UINT32_MAX || !"refcount overflowed!");
+ assert (r.references.weak != UINT32_MAX || !"refcount underflowed!");
+ if (result)
+ *result = r.references;
+}
+
+/* Demote a hard reference to a weak reference. If RESULT is not
+ NULL, the result of the operation is written there. This function
+ uses atomic operations. It is not required to serialize calls to
+ this function. */
+static inline void
+refcounts_demote (refcounts_t *ref, struct references *result)
+{
+ /* To demote a hard reference, we need to atomically subtract 1 from
+ the hard reference count, and add 1 to the weak reference count.
+
+ We can subtract by 1 by adding the two's complement of 1 = ~0 to
+ a fixed-width value, discarding the overflow.
+
+ We do the same in our uint64_t value, but we have chosen the
+ layout of struct references so that when it is used in the union
+ _references, the hard reference counts occupy the least
+ significant bits. When we add ~0 to the hard references, it will
+ overflow into the weak references. This is the desired
+ operation. */
+ const union _references op = { .references = { .hard = ~0U } };
+ union _references r;
+ r.value = __atomic_add_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
+ assert (r.references.hard != UINT32_MAX || !"refcount underflowed!");
+ assert (r.references.weak != UINT32_MAX || !"refcount overflowed!");
+ if (result)
+ *result = r.references;
+}
+
+/* Increment the weak reference count of REF. If RESULT is not NULL,
+ the result of the operation is written there. This function uses
+ atomic operations. It is not required to serialize calls to this
+ function. */
+static inline void
+refcounts_ref_weak (refcounts_t *ref, struct references *result)
+{
+ const union _references op = { .references = { .weak = 1 } };
+ union _references r;
+ r.value = __atomic_add_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
+ assert (r.references.weak != UINT32_MAX || !"refcount overflowed!");
+ if (result)
+ *result = r.references;
+}
+
+/* Decrement the weak reference count of REF. If RESULT is not NULL,
+ the result of the operation is written there. This function uses
+ atomic operations. It is not required to serialize calls to this
+ function. */
+static inline void
+refcounts_deref_weak (refcounts_t *ref, struct references *result)
+{
+ const union _references op = { .references = { .weak = 1 } };
+ union _references r;
+ r.value = __atomic_sub_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
+ assert (r.references.weak != UINT32_MAX || !"refcount underflowed!");
+ if (result)
+ *result = r.references;
+}
+
+/* Store the current reference counts of REF in RESULT. This function
+ uses atomic operations. It is not required to serialize calls to
+ this function. */
+static inline void
+refcounts_references (refcounts_t *ref, struct references *result)
+{
+ union _references r;
+ r.value =__atomic_load_n (&ref->value, __ATOMIC_RELAXED);
+ *result = r.references;
+}
+
+/* Return the hard reference count of REF. This function uses atomic
+ operations. It is not required to serialize calls to this
+ function. */
+static inline uint32_t
+refcounts_hard_references (refcounts_t *ref)
+{
+ struct references result;
+ refcounts_references (ref, &result);
+ return result.hard;
+}
+
+/* Return the weak reference count of REF. This function uses atomic
+ operations. It is not required to serialize calls to this
+ function. */
+static inline uint32_t
+refcounts_weak_references (refcounts_t *ref)
+{
+ struct references result;
+ refcounts_references (ref, &result);
+ return result.weak;
+}
+
+#endif /* _HURD_REFCOUNT_H_ */
diff --git a/include/sys/procfs.h b/include/sys/procfs.h
new file mode 100644
index 00000000..ec82308b
--- /dev/null
+++ b/include/sys/procfs.h
@@ -0,0 +1,120 @@
+/* <sys/procfs.h> -- data structures describing ELF core file formats
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifndef _SYS_PROCFS_H
+#define _SYS_PROCFS_H 1
+
+/* This poorly-named file describes the format of the note segments in ELF
+ core files. It doesn't have anything to do with a `/proc' file system.
+
+ Anyway, the whole purpose of this file is for GDB and GDB only.
+ Don't read too much into it. Don't use it for anything other than
+ GDB unless you know what you are doing. */
+
+#include <features.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <ucontext.h>
+
+__BEGIN_DECLS
+
+/* This structure gives some general information about the process, things
+ that `ps' would tell you (hence the name). We have chosen the names and
+ the layout of the structure to coincide with the Solaris 8 definition.
+ The `file' program happens to know to look at 84 bytes into this
+ structure for the 16-byte `pr_fname' member, so we don't disappoint it. */
+#define ELF_PRARGSZ (80) /* Number of chars of argument list saved. */
+struct elf_psinfo
+{
+ int pr_flag; /* Meaningless flag bits. */
+ int pr_nlwp; /* Number of threads in this process. */
+ pid_t pr_pid; /* Process ID. */
+ pid_t pr_ppid; /* Parent's process ID. */
+ pid_t pr_pgid; /* Process group ID. */
+ pid_t pr_sid; /* Session ID. */
+ uid_t pr_uid, pr_euid; /* Real and effective UID (first one). */
+ gid_t pr_gid, pr_egid; /* Real and effective GID (first one). */
+ size_t pr_size; /* Virtual memory size of process (KB). */
+ size_t pr_rssize; /* Resident set size of process (KB). */
+ uint16_t pr_pctcpu; /* % of CPU used by all threads. */
+ uint16_t pr_pctmem; /* % of virtual memory used by process. */
+ struct timespec pr_start; /* Date & time of task creation. */
+ struct timespec pr_time; /* CPU time used by this process. */
+ struct timespec pr_ctime; /* CPU time used by dead children. */
+ uint32_t pr_reserved1[2]; /* Padding to place pr_psargs at offset 84. */
+ char pr_fname[16]; /* Initial part of executable file name. */
+ char pr_psargs[ELF_PRARGSZ]; /* Initial part of argument list. */
+ int pr_wstat; /* Zombie exit status (not really used). */
+ int pr_argc; /* The argument count at startup. */
+ uintptr_t pr_argv; /* Original argument vector address. */
+ uintptr_t pr_envp; /* Original environment vector address. */
+};
+typedef struct elf_psinfo psinfo_t;
+
+/* This structure also gives general information about the process.
+ The Solaris version gives the status of "the representative thread",
+ but GDB does not actually use this structure for anything but the PID. */
+struct elf_pstatus
+{
+ int pr_flags; /* Meaningless flags bits. */
+ int pr_nlwp; /* Number of threads in this process. */
+ pid_t pr_pid; /* Process ID. */
+ pid_t pr_ppid; /* Parent's process ID. */
+ pid_t pr_pgid; /* Process group ID. */
+ pid_t pr_sid; /* Session ID. */
+ struct timespec pr_utime; /* User CPU time used by this process. */
+ struct timespec pr_stime; /* System CPU time used by this process. */
+ struct timespec pr_cutime; /* User CPU time used by dead children. */
+ struct timespec pr_cstime; /* System CPU time used by dead children. */
+};
+typedef struct elf_pstatus pstatus_t;
+
+/* Information about a signal, the signal that killed the process. */
+struct elf_siginfo
+{
+ int si_signo; /* Signal number. */
+ int si_code; /* Extra code. */
+ int si_errno; /* Errno. */
+};
+
+typedef gregset_t prgregset_t;
+typedef fpregset_t prfpregset_t;
+
+/* This structure describes the state of one thread. GDB examines
+ pr_cursig to see what killed the process, so those are set in the
+ record for every thread. The `pr_lwpid' member contains a value
+ that is unique among the threads in this code file, but otherwise
+ meaningless. GDB examines that and the `pr_reg' and `pr_fpreg'
+ members for the register state of the thread. */
+struct elf_lwpstatus
+{
+ int pr_flags; /* Meaningless flags bits. */
+ int pr_lwpid; /* Identifies this thread from others. */
+ int pr_cursig; /* Signal that killed the thread. */
+ struct elf_siginfo pr_info; /* Details about the signal. */
+ prgregset_t pr_reg; /* State of thread's general registers. */
+ prfpregset_t pr_fpreg; /* State of its floating-point registers. */
+};
+typedef struct elf_lwpstatus lwpstatus_t;
+
+
+__END_DECLS
+
+#endif /* sys/procfs.h */
diff --git a/init/Makefile b/init/Makefile
new file mode 100644
index 00000000..ffb82ffd
--- /dev/null
+++ b/init/Makefile
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 1994,95,96,99,2001 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := init
+makemode := server
+
+SRCS = init.c stubs.c
+OBJS = $(SRCS:.c=.o) \
+ startupServer.o notifyServer.o startup_replyUser.o msgServer.o \
+ startup_notifyUser.o
+target = init
+HURDLIBS = shouldbeinlibc
+
+include ../Makeconf
+
+mung_msg_S.h: msg_S.h
+ sed 's/msg_server/mung_msg_server/' < $< > $@
diff --git a/init/init.c b/init/init.c
new file mode 100644
index 00000000..b7b40bd2
--- /dev/null
+++ b/init/init.c
@@ -0,0 +1,1593 @@
+/* Start and maintain hurd core servers and system run state
+
+ Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2005, 2008, 2013 Free Software Foundation, Inc.
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell and Roland McGrath. */
+
+/* This is probably more include files than I've ever seen before for
+ one file. */
+#include <hurd.h>
+#include <hurd/fs.h>
+#include <hurd/fsys.h>
+#include <device/device.h>
+#include <stdio.h>
+#include <assert.h>
+#include <hurd/paths.h>
+#include <sys/reboot.h>
+#include <sys/file.h>
+#include <unistd.h>
+#include <string.h>
+#include <mach/notify.h>
+#include <stdlib.h>
+#include <hurd/msg.h>
+#include <hurd/term.h>
+#include <hurd/fshelp.h>
+#include <paths.h>
+#include <sys/mman.h>
+#include <hurd/msg_server.h>
+#include <wire.h>
+#include <sys/wait.h>
+#include <error.h>
+#include <hurd/msg_reply.h>
+#include <argz.h>
+#include <maptime.h>
+#include <version.h>
+#include <argp.h>
+#include <pids.h>
+
+#include "startup_notify_U.h"
+#include "startup_reply_U.h"
+#include "startup_S.h"
+#include "notify_S.h"
+#include "mung_msg_S.h"
+
+/* host_reboot flags for when we crash. */
+static int crash_flags = RB_AUTOBOOT;
+
+#define BOOT(flags) ((flags & RB_HALT) ? "halt" : "reboot")
+
+
+const char *argp_program_version = STANDARD_HURD_VERSION (init);
+
+static struct argp_option
+options[] =
+{
+ {"single-user", 's', 0, 0, "Startup system in single-user mode"},
+ {"query", 'q', 0, 0, "Ask for the names of servers to start"},
+ {"init-name", 'n', 0, 0 },
+ {"crash-debug", 'H', 0, 0, "On system crash, go to kernel debugger"},
+ {"debug", 'd', 0, 0 },
+ {"fake-boot", 'f', 0, 0, "This hurd hasn't been booted on the raw machine"},
+ {0, 'x', 0, OPTION_HIDDEN},
+ {0}
+};
+
+static char doc[] = "Start and maintain hurd core servers and system run state";
+
+static int booted; /* Set when the core servers are up. */
+
+/* This structure keeps track of each notified task. */
+struct ntfy_task
+ {
+ mach_port_t notify_port;
+ struct ntfy_task *next;
+ char *name;
+ };
+
+/* This structure keeps track of each registered essential task. */
+struct ess_task
+ {
+ struct ess_task *next;
+ task_t task_port;
+ char *name;
+ };
+
+/* These are linked lists of all of the registered items. */
+static struct ess_task *ess_tasks;
+static struct ntfy_task *ntfy_tasks;
+
+
+/* Our receive right */
+static mach_port_t startup;
+
+/* Ports to the kernel */
+static mach_port_t host_priv, device_master;
+
+/* Args to bootstrap, expressed as flags */
+static int bootstrap_args = 0;
+
+/* Stored information for returning proc and auth startup messages. */
+static mach_port_t procreply, authreply;
+static mach_msg_type_name_t procreplytype, authreplytype;
+
+/* Our ports to auth and proc. */
+static mach_port_t authserver;
+static mach_port_t procserver;
+
+/* Our bootstrap port, on which we call fsys_getpriv and fsys_init. */
+static mach_port_t bootport;
+
+/* Set iff we are a `fake' bootstrap. */
+static int fakeboot;
+
+/* The tasks of auth and proc and the bootstrap filesystem. */
+static task_t authtask, proctask, fstask;
+
+static mach_port_t default_ports[INIT_PORT_MAX];
+static mach_port_t default_dtable[3];
+static int default_ints[INIT_INT_MAX];
+
+static char **global_argv;
+static char *startup_envz;
+static size_t startup_envz_len;
+
+void launch_system (void);
+void process_signal (int signo);
+
+/** Utility functions **/
+
+/* Read a string from stdin into BUF. */
+static int
+getstring (char *buf, size_t bufsize)
+{
+ if (fgets (buf, bufsize, stdin) != NULL && buf[0] != '\0')
+ {
+ size_t len = strlen (buf);
+ if (buf[len - 1] == '\n' || buf[len - 1] == '\r')
+ buf[len - 1] = '\0';
+ return 1;
+ }
+ return 0;
+}
+
+
+/** System shutdown **/
+
+/* Reboot the microkernel. */
+void
+reboot_mach (int flags)
+{
+ if (fakeboot)
+ {
+ printf ("%s: Would %s Mach with flags %#x\n",
+ program_invocation_short_name, BOOT (flags), flags);
+ fflush (stdout);
+ exit (1);
+ }
+ else
+ {
+ error_t err;
+ printf ("%s: %sing Mach (flags %#x)...\n",
+ program_invocation_short_name, BOOT (flags), flags);
+ fflush (stdout);
+ sleep (5);
+ while ((err = host_reboot (host_priv, flags)))
+ error (0, err, "reboot");
+ for (;;);
+ }
+}
+
+/* Reboot the microkernel, specifying that this is a crash. */
+void
+crash_mach (void)
+{
+ reboot_mach (crash_flags);
+}
+
+/* Notify all tasks that have requested shutdown notifications */
+void
+notify_shutdown (const char *msg)
+{
+ struct ntfy_task *n;
+
+ for (n = ntfy_tasks; n != NULL; n = n->next)
+ {
+ error_t err;
+ printf ("%s: notifying %s of %s...",
+ program_invocation_short_name, n->name, msg);
+ fflush (stdout);
+ err = startup_dosync (n->notify_port, 60000); /* 1 minute to reply */
+ if (err == MACH_SEND_INVALID_DEST)
+ puts ("(no longer present)");
+ else if (err)
+ puts (strerror (err));
+ else
+ puts ("done");
+ fflush (stdout);
+ }
+}
+
+/* Reboot the Hurd. */
+void
+reboot_system (int flags)
+{
+ notify_shutdown ("shutdown");
+
+ if (fakeboot)
+ {
+ pid_t *pp;
+ size_t npids = 0;
+ error_t err;
+ int ind;
+
+ err = proc_getallpids (procserver, &pp, &npids);
+ if (err == MACH_SEND_INVALID_DEST)
+ {
+ procbad:
+ /* The procserver must have died. Give up. */
+ error (0, 0, "Can't simulate crash; proc has died");
+ reboot_mach (flags);
+ }
+ for (ind = 0; ind < npids; ind++)
+ {
+ task_t task;
+
+ err = proc_pid2task (procserver, pp[ind], &task);
+ if (err == MACH_SEND_INVALID_DEST)
+ goto procbad;
+ else if (err)
+ {
+ error (0, err, "Getting task for pid %d", pp[ind]);
+ continue;
+ }
+
+ /* Postpone self so we can finish; postpone proc
+ so that we can finish. */
+ if (task != mach_task_self () && task != proctask)
+ {
+ struct procinfo *pi = 0;
+ size_t pisize = 0;
+ char *noise;
+ size_t noise_len = 0;
+ int flags;
+ err = proc_getprocinfo (procserver, pp[ind], &flags,
+ (int **)&pi, &pisize,
+ &noise, &noise_len);
+ if (err == MACH_SEND_INVALID_DEST)
+ goto procbad;
+ if (err)
+ {
+ error (0, err, "Getting procinfo for pid %d", pp[ind]);
+ continue;
+ }
+ if (!(pi->state & PI_NOPARENT))
+ {
+ printf ("%s: Killing pid %d\n",
+ program_invocation_short_name, pp[ind]);
+ fflush (stdout);
+ task_terminate (task);
+ }
+ if (noise_len > 0)
+ munmap (noise, noise_len);
+ }
+ }
+ printf ("%s: Killing proc server\n", program_invocation_short_name);
+ fflush (stdout);
+ task_terminate (proctask);
+ printf ("%s: Exiting", program_invocation_short_name);
+ fflush (stdout);
+ }
+ reboot_mach (flags);
+}
+
+/* Reboot the Hurd, specifying that this is a crash. */
+void
+crash_system (void)
+{
+ reboot_system (crash_flags);
+}
+
+
+
+/* Request a dead-name notification sent to our port. */
+static void
+request_dead_name (mach_port_t name)
+{
+ mach_port_t prev;
+ mach_port_request_notification (mach_task_self (), name,
+ MACH_NOTIFY_DEAD_NAME, 1, startup,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
+ if (prev != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), prev);
+}
+
+/* Record an essential task in the list. */
+static error_t
+record_essential_task (const char *name, task_t task)
+{
+ struct ess_task *et;
+ /* Record this task as essential. */
+ et = malloc (sizeof (struct ess_task));
+ if (et == NULL)
+ return ENOMEM;
+ et->task_port = task;
+ et->name = strdup (name);
+ if (et->name == NULL)
+ {
+ free (et);
+ return ENOMEM;
+ }
+ et->next = ess_tasks;
+ ess_tasks = et;
+
+ /* Dead-name notification on the task port will tell us when it dies. */
+ request_dead_name (task);
+
+#if 0
+ /* Taking over the exception port will give us a better chance
+ if the task tries to get wedged on a fault. */
+ task_set_special_port (task, TASK_EXCEPTION_PORT, startup);
+#endif
+
+ return 0;
+}
+
+
+/** Starting programs **/
+
+/* Run SERVER, giving it INIT_PORT_MAX initial ports from PORTS.
+ Set TASK to be the task port of the new image. */
+void
+run (const char *server, mach_port_t *ports, task_t *task)
+{
+ char buf[BUFSIZ];
+ const char *prog = server;
+
+ if (bootstrap_args & RB_INITNAME)
+ {
+ printf ("Server file name (default %s): ", server);
+ if (getstring (buf, sizeof (buf)))
+ prog = buf;
+ }
+
+ while (1)
+ {
+ file_t file;
+ error_t err;
+
+ file = file_name_lookup (prog, O_EXEC, 0);
+ if (file == MACH_PORT_NULL)
+ error (0, errno, "%s", prog);
+ else
+ {
+ task_create (mach_task_self (),
+#ifdef KERN_INVALID_LEDGER
+ NULL, 0, /* OSF Mach */
+#endif
+ 0, task);
+ if (bootstrap_args & RB_KDB)
+ {
+ printf ("Pausing for %s\n", prog);
+ getchar ();
+ }
+ err = file_exec (file, *task, 0,
+ (char *)prog, strlen (prog) + 1, /* Args. */
+ startup_envz, startup_envz_len,
+ default_dtable, MACH_MSG_TYPE_COPY_SEND, 3,
+ ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
+ default_ints, INIT_INT_MAX,
+ NULL, 0, NULL, 0);
+ if (!err)
+ break;
+
+ error (0, err, "%s", prog);
+ }
+
+ printf ("File name for server %s (or nothing to reboot): ", server);
+ if (getstring (buf, sizeof (buf)))
+ prog = buf;
+ else
+ crash_system ();
+ }
+
+#if 0
+ printf ("started %s\n", prog);
+ fflush (stdout);
+#endif
+
+ /* Dead-name notification on the task port will tell us when it dies,
+ so we can crash if we don't make it to a fully bootstrapped Hurd. */
+ request_dead_name (*task);
+}
+
+/* Run FILENAME as root with ARGS as its argv (length ARGLEN). Return
+ the task that we started. If CTTY is set, then make that the
+ controlling terminal of the new process and put it in its own login
+ collection. If SETSID is set, put it in a new session. Return
+ 0 if the task was not created successfully. */
+pid_t
+run_for_real (char *filename, char *args, int arglen, mach_port_t ctty,
+ int setsid)
+{
+ file_t file;
+ error_t err;
+ task_t task;
+ char *progname;
+ int pid;
+
+#if 0
+ char buf[512];
+ do
+ {
+ printf ("File name [%s]: ", filename);
+ if (getstring (buf, sizeof (buf)) && *buf)
+ filename = buf;
+ file = file_name_lookup (filename, O_EXEC, 0);
+ if (file == MACH_PORT_NULL)
+ error (0, errno, "%s", filename);
+ }
+ while (file == MACH_PORT_NULL);
+#else
+ file = file_name_lookup (filename, O_EXEC, 0);
+ if (file == MACH_PORT_NULL)
+ {
+ error (0, errno, "%s", filename);
+ return 0;
+ }
+#endif
+
+ task_create (mach_task_self (),
+#ifdef KERN_INVALID_LEDGER
+ NULL, 0, /* OSF Mach */
+#endif
+ 0, &task);
+ proc_child (procserver, task);
+ proc_task2pid (procserver, task, &pid);
+ proc_task2proc (procserver, task, &default_ports[INIT_PORT_PROC]);
+ proc_mark_exec (default_ports[INIT_PORT_PROC]);
+ if (setsid)
+ proc_setsid (default_ports[INIT_PORT_PROC]);
+ if (ctty != MACH_PORT_NULL)
+ {
+ term_getctty (ctty, &default_ports[INIT_PORT_CTTYID]);
+ io_mod_owner (ctty, -pid);
+ proc_make_login_coll (default_ports[INIT_PORT_PROC]);
+ }
+ if (bootstrap_args & RB_KDB)
+ {
+ printf ("Pausing for %s\n", filename);
+ getchar ();
+ }
+ progname = strrchr (filename, '/');
+ if (progname)
+ ++progname;
+ else
+ progname = filename;
+ err = file_exec (file, task, 0,
+ args, arglen,
+ startup_envz, startup_envz_len,
+ default_dtable, MACH_MSG_TYPE_COPY_SEND, 3,
+ default_ports, MACH_MSG_TYPE_COPY_SEND,
+ INIT_PORT_MAX,
+ default_ints, INIT_INT_MAX,
+ NULL, 0, NULL, 0);
+ mach_port_deallocate (mach_task_self (), default_ports[INIT_PORT_PROC]);
+ mach_port_deallocate (mach_task_self (), task);
+ if (ctty != MACH_PORT_NULL)
+ {
+ mach_port_deallocate (mach_task_self (),
+ default_ports[INIT_PORT_CTTYID]);
+ default_ports[INIT_PORT_CTTYID] = MACH_PORT_NULL;
+ }
+ mach_port_deallocate (mach_task_self (), file);
+ if (err)
+ {
+ error (0, err, "Cannot execute %s", filename);
+ return 0;
+ }
+ return pid;
+}
+
+
+/** Main program and setup **/
+
+static int
+demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ extern int notify_server (), startup_server (), msg_server ();
+
+ return (notify_server (inp, outp) ||
+ msg_server (inp, outp) ||
+ startup_server (inp, outp));
+}
+
+static int
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'q': bootstrap_args |= RB_ASKNAME; break;
+ case 's': bootstrap_args |= RB_SINGLE; break;
+ case 'd': bootstrap_args |= RB_KDB; break;
+ case 'n': bootstrap_args |= RB_INITNAME; break;
+ case 'f': fakeboot = 1; break;
+ case 'H': crash_flags = RB_DEBUGGER; break;
+ case 'x': /* NOP */ break;
+ default: return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+int
+main (int argc, char **argv, char **envp)
+{
+ volatile int err;
+ int i;
+ int flags;
+ mach_port_t consdev;
+ struct argp argp = { options, parse_opt, 0, doc };
+
+ /* Parse the arguments. We don't want the vector reordered, we
+ should pass on to our child the exact arguments we got and just
+ ignore any arguments that aren't flags for us. ARGP_NO_ERRS
+ suppresses --help and --version, so we only use that option if we
+ are booting. */
+ flags = ARGP_IN_ORDER;
+ if (getpid () == 0)
+ flags |= ARGP_NO_ERRS;
+ argp_parse (&argp, argc, argv, flags, 0, 0);
+
+ if (getpid () > 0)
+ error (2, 0, "can only be run by bootstrap filesystem");
+
+ global_argv = argv;
+
+ /* Fetch a port to the bootstrap filesystem, the host priv and
+ master device ports, and the console. */
+ if (task_get_bootstrap_port (mach_task_self (), &bootport)
+ || fsys_getpriv (bootport, &host_priv, &device_master, &fstask)
+ || device_open (device_master, D_WRITE, "console", &consdev))
+ crash_mach ();
+
+ wire_task_self ();
+
+ /* Clear our bootstrap port so our children don't inherit it. */
+ task_set_bootstrap_port (mach_task_self (), MACH_PORT_NULL);
+
+ stderr = stdout = mach_open_devstream (consdev, "w");
+ stdin = mach_open_devstream (consdev, "r");
+ if (stdout == NULL || stdin == NULL)
+ crash_mach ();
+ setbuf (stdout, NULL);
+
+ err = argz_create (envp, &startup_envz, &startup_envz_len);
+ assert_perror (err);
+
+ /* At this point we can use assert to check for errors. */
+ err = mach_port_allocate (mach_task_self (),
+ MACH_PORT_RIGHT_RECEIVE, &startup);
+ assert_perror (err);
+ err = mach_port_insert_right (mach_task_self (), startup, startup,
+ MACH_MSG_TYPE_MAKE_SEND);
+ assert_perror (err);
+
+ /* Crash if the boot filesystem task dies. */
+ request_dead_name (fstask);
+
+ /* Set up the set of ports we will pass to the programs we exec. */
+ for (i = 0; i < INIT_PORT_MAX; i++)
+ switch (i)
+ {
+ case INIT_PORT_CRDIR:
+ default_ports[i] = getcrdir ();
+ break;
+ case INIT_PORT_CWDIR:
+ default_ports[i] = getcwdir ();
+ break;
+ default:
+ default_ports[i] = MACH_PORT_NULL;
+ break;
+ }
+
+ default_dtable[0] = getdport (0);
+ default_dtable[1] = getdport (1);
+ default_dtable[2] = getdport (2);
+
+ /* All programs we start should ignore job control stop signals.
+ That way Posix.1 B.2.2.2 is satisfied where it says that programs
+ not run under job control shells are protected. */
+ default_ints[INIT_SIGIGN] = (sigmask (SIGTSTP)
+ | sigmask (SIGTTIN)
+ | sigmask (SIGTTOU));
+
+ default_ports[INIT_PORT_BOOTSTRAP] = startup;
+ run ("/hurd/proc", default_ports, &proctask);
+ printf (" proc");
+ fflush (stdout);
+ run ("/hurd/auth", default_ports, &authtask);
+ printf (" auth");
+ fflush (stdout);
+ default_ports[INIT_PORT_BOOTSTRAP] = MACH_PORT_NULL;
+
+ /* Wait for messages. When both auth and proc have started, we
+ run launch_system which does the rest of the boot. */
+ while (1)
+ {
+ err = mach_msg_server (demuxer, 0, startup);
+ assert_perror (err);
+ }
+}
+
+void
+launch_core_servers (void)
+{
+ mach_port_t old;
+ mach_port_t authproc, fsproc, procproc;
+ error_t err;
+
+ /* Reply to the proc and auth servers. */
+ startup_procinit_reply (procreply, procreplytype, 0,
+ mach_task_self (), authserver,
+ host_priv, MACH_MSG_TYPE_COPY_SEND,
+ device_master, MACH_MSG_TYPE_COPY_SEND);
+ if (!fakeboot)
+ {
+ mach_port_deallocate (mach_task_self (), device_master);
+ device_master = 0;
+ }
+
+ /* Mark us as important. */
+ proc_mark_important (procserver);
+ proc_mark_exec (procserver);
+
+ /* Declare that the filesystem and auth are our children. */
+ proc_child (procserver, fstask);
+ proc_child (procserver, authtask);
+
+ proc_task2proc (procserver, authtask, &authproc);
+ proc_mark_important (authproc);
+ proc_mark_exec (authproc);
+ startup_authinit_reply (authreply, authreplytype, 0, authproc,
+ MACH_MSG_TYPE_COPY_SEND);
+ mach_port_deallocate (mach_task_self (), authproc);
+
+ /* Give the library our auth and proc server ports. */
+ _hurd_port_set (&_hurd_ports[INIT_PORT_AUTH], authserver);
+ _hurd_port_set (&_hurd_ports[INIT_PORT_PROC], procserver);
+
+ /* Do NOT run _hurd_proc_init! That will start signals, which we do not
+ want. We listen to our own message port. Tell the proc server where
+ our args and environment are. */
+ proc_set_arg_locations (procserver,
+ (vm_address_t) global_argv, (vm_address_t) environ);
+
+ default_ports[INIT_PORT_AUTH] = authserver;
+
+ /* Declare that the proc server is our child. */
+ proc_child (procserver, proctask);
+ err = proc_task2proc (procserver, proctask, &procproc);
+ if (!err)
+ {
+ proc_mark_important (procproc);
+ proc_mark_exec (procproc);
+ mach_port_deallocate (mach_task_self (), procproc);
+ }
+
+ proc_register_version (procserver, host_priv, "init", "", HURD_VERSION);
+
+ /* Get the bootstrap filesystem's proc server port.
+ We must do this before calling proc_setmsgport below. */
+ proc_task2proc (procserver, fstask, &fsproc);
+ proc_mark_important (fsproc);
+ proc_mark_exec (fsproc);
+
+#if 0
+ printf ("Init has completed.\n");
+ fflush (stdout);
+#endif
+ printf (".\n");
+ fflush (stdout);
+
+ /* Tell the proc server our msgport. Be sure to do this after we are all
+ done making requests of proc. Once we have done this RPC, proc
+ assumes it can send us requests, so we cannot block on proc again
+ before accepting more RPC requests! However, we must do this before
+ calling fsys_init, because fsys_init blocks on exec_init, and
+ exec_init will block waiting on our message port. */
+ proc_setmsgport (procserver, startup, &old);
+ if (old != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), old);
+
+ /* Give the bootstrap FS its proc and auth ports. */
+ err = fsys_init (bootport, fsproc, MACH_MSG_TYPE_COPY_SEND, authserver);
+ mach_port_deallocate (mach_task_self (), fsproc);
+ if (err)
+ error (0, err, "fsys_init"); /* Not necessarily fatal. */
+}
+
+/* Set up the initial value of the standard exec data. */
+void
+init_stdarrays ()
+{
+ auth_t nullauth;
+ mach_port_t pt;
+ mach_port_t ref;
+ mach_port_t *std_port_array;
+ int *std_int_array;
+ int i;
+
+ std_port_array = alloca (sizeof (mach_port_t) * INIT_PORT_MAX);
+ std_int_array = alloca (sizeof (int) * INIT_INT_MAX);
+
+ bzero (std_port_array, sizeof (mach_port_t) * INIT_PORT_MAX);
+ bzero (std_int_array, sizeof (int) * INIT_INT_MAX);
+
+ __USEPORT (AUTH, auth_makeauth (port, 0, MACH_MSG_TYPE_COPY_SEND, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, &nullauth));
+
+ /* MAKE_SEND is safe in these transactions because we destroy REF
+ ourselves each time. */
+ pt = getcwdir ();
+ ref = mach_reply_port ();
+ io_reauthenticate (pt, ref, MACH_MSG_TYPE_MAKE_SEND);
+ auth_user_authenticate (nullauth, ref, MACH_MSG_TYPE_MAKE_SEND,
+ &std_port_array[INIT_PORT_CWDIR]);
+ mach_port_destroy (mach_task_self (), ref);
+ mach_port_deallocate (mach_task_self (), pt);
+
+ pt = getcrdir ();
+ ref = mach_reply_port ();
+ io_reauthenticate (pt, ref, MACH_MSG_TYPE_MAKE_SEND);
+ auth_user_authenticate (nullauth, ref, MACH_MSG_TYPE_MAKE_SEND,
+ &std_port_array[INIT_PORT_CRDIR]);
+ mach_port_destroy (mach_task_self (), ref);
+ mach_port_deallocate (mach_task_self (), pt);
+
+ std_port_array[INIT_PORT_AUTH] = nullauth;
+
+ std_int_array[INIT_UMASK] = CMASK;
+
+ __USEPORT (PROC, proc_setexecdata (port, std_port_array,
+ MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
+ std_int_array, INIT_INT_MAX));
+ for (i = 0; i < INIT_PORT_MAX; i++)
+ mach_port_deallocate (mach_task_self (), std_port_array[i]);
+}
+
+/* Frobnicate the kernel task and the proc server's idea of it (PID 2),
+ so the kernel command line can be read as for a normal Hurd process. */
+
+void
+frob_kernel_process (void)
+{
+ error_t err;
+ int argc, i;
+ char *argz, *entry;
+ size_t argzlen;
+ size_t windowsz;
+ vm_address_t mine, his;
+ task_t task;
+ process_t proc, kbs;
+
+ err = proc_pid2task (procserver, HURD_PID_KERNEL, &task);
+ if (err)
+ {
+ error (0, err, "cannot get kernel task port");
+ return;
+ }
+ err = proc_task2proc (procserver, task, &proc);
+ if (err)
+ {
+ error (0, err, "cannot get kernel task's proc server port");
+ mach_port_deallocate (mach_task_self (), task);
+ return;
+ }
+
+ /* Mark the kernel task as an essential task so that we or the proc server
+ never want to task_terminate it. */
+ proc_mark_important (proc);
+
+ err = record_essential_task ("kernel", task);
+ assert_perror (err);
+
+ err = task_get_bootstrap_port (task, &kbs);
+ assert_perror (err);
+ if (kbs == MACH_PORT_NULL)
+ {
+ /* The kernel task has no bootstrap port set, so we are presumably
+ the first Hurd to boot. Install the kernel task's proc port from
+ this Hurd's proc server as the task bootstrap port. Additional
+ Hurds will see this. */
+
+ err = task_set_bootstrap_port (task, proc);
+ if (err)
+ error (0, err, "cannot set kernel task's bootstrap port");
+
+ if (fakeboot)
+ error (0, 0, "warning: --fake-boot specified but I see no other Hurd");
+ }
+ else
+ {
+ /* The kernel task has a bootstrap port set. Perhaps it is its proc
+ server port from another Hurd. If so, propagate the kernel
+ argument locations from that Hurd rather than diddling with the
+ kernel task ourselves. */
+
+ vm_address_t kargv, kenvp;
+ err = proc_get_arg_locations (kbs, &kargv, &kenvp);
+ mach_port_deallocate (mach_task_self (), kbs);
+ if (err)
+ error (0, err, "kernel task bootstrap port (ignoring)");
+ else
+ {
+ err = proc_set_arg_locations (proc, kargv, kenvp);
+ if (err)
+ error (0, err, "cannot propagate original kernel command line");
+ else
+ {
+ mach_port_deallocate (mach_task_self (), proc);
+ mach_port_deallocate (mach_task_self (), task);
+ if (! fakeboot)
+ error (0, 0, "warning: "
+ "I see another Hurd, but --fake-boot was not given");
+ return;
+ }
+ }
+ }
+
+ /* Our arguments make up the multiboot command line used to boot the
+ kernel. We'll write into the kernel task a page containing a
+ canonical argv array and argz of those words. */
+
+ err = argz_create (&global_argv[1], &argz, &argzlen);
+ assert_perror (err);
+ argc = argz_count (argz, argzlen);
+
+ windowsz = round_page (((argc + 1) * sizeof (char *)) + argzlen);
+
+ mine = (vm_address_t) mmap (0, windowsz, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ assert (mine != -1);
+ err = vm_allocate (task, &his, windowsz, 1);
+ if (err)
+ {
+ error (0, err, "cannot allocate %Zu bytes in kernel task", windowsz);
+ free (argz);
+ mach_port_deallocate (mach_task_self (), proc);
+ mach_port_deallocate (mach_task_self (), task);
+ munmap ((caddr_t) mine, windowsz);
+ return;
+ }
+
+ for (i = 0, entry = argz; entry != NULL;
+ ++i, entry = argz_next (argz, argzlen, entry))
+ ((char **) mine)[i] = ((char *) &((char **) his)[argc + 1]
+ + (entry - argz));
+ ((char **) mine)[argc] = NULL;
+ memcpy (&((char **) mine)[argc + 1], argz, argzlen);
+
+ free (argz);
+
+ /* We have the data all set up in our copy, now just write it over. */
+ err = vm_write (task, his, mine, windowsz);
+ mach_port_deallocate (mach_task_self (), task);
+ munmap ((caddr_t) mine, windowsz);
+ if (err)
+ {
+ error (0, err, "cannot write command line into kernel task");
+ return;
+ }
+
+ /* The argument vector is set up in the kernel task at address HIS.
+ Finally, we can inform the proc server where to find it. */
+ err = proc_set_arg_locations (proc, his, his + (argc * sizeof (char *)));
+ mach_port_deallocate (mach_task_self (), proc);
+ if (err)
+ error (0, err, "proc_set_arg_locations for kernel task");
+}
+
+/** Running userland. **/
+
+/* In the "split-init" setup, we just run a single program (usually
+ /libexec/runsystem) that is not expected to ever exit (or stop).
+ If it does exit (or can't be started), we go to an emergency single-user
+ shell as a fallback. */
+
+
+static pid_t child_pid; /* PID of the child we run */
+static task_t child_task; /* and its (original) task port */
+
+error_t send_signal (mach_port_t msgport, int signal, mach_port_t refport,
+ mach_msg_timeout_t);
+
+static void launch_something (const char *why);
+
+
+/* SIGNO has arrived and has been validated. Do whatever work it
+ implies. */
+void
+process_signal (int signo)
+{
+ if (signo == SIGCHLD)
+ {
+ /* A child died. Find its status. */
+ int status;
+ pid_t pid;
+
+ while (1)
+ {
+ pid = waitpid (WAIT_ANY, &status, WNOHANG | WUNTRACED);
+ if (pid <= 0)
+ break; /* No more children. */
+
+ /* Since we are init, orphaned processes get reparented to us and
+ alas, all our adopted children eventually die. Woe is us. We
+ just need to reap the zombies to relieve the proc server of
+ its burden, and then we can forget about the little varmints. */
+
+ if (pid == child_pid)
+ {
+ /* The big magilla bit the dust. */
+
+ char *desc = 0;
+
+ mach_port_deallocate (mach_task_self (), child_task);
+ child_task = MACH_PORT_NULL;
+ child_pid = -1;
+
+ if (WIFSIGNALED (status))
+ asprintf (&desc, "terminated abnormally (%s)",
+ strsignal (WTERMSIG (status)));
+ else if (WIFSTOPPED (status))
+ asprintf (&desc, "stopped abnormally (%s)",
+ strsignal (WTERMSIG (status)));
+ else if (WEXITSTATUS (status) == 0)
+ desc = strdup ("finished");
+ else
+ asprintf (&desc, "exited with status %d",
+ WEXITSTATUS (status));
+
+ {
+ char buf[40];
+ snprintf (buf, sizeof buf, "%d", status);
+ setenv ("STATUS", buf, 1);
+ }
+
+ launch_something (desc);
+ free (desc);
+ }
+ }
+ }
+ else
+ {
+ /* Pass the signal on to the child. */
+ task_t task;
+ error_t err;
+
+ err = proc_pid2task (procserver, child_pid, &task);
+ if (err)
+ {
+ error (0, err, "proc_pid2task on %d", child_pid);
+ task = child_task;
+ }
+ else
+ {
+ mach_port_deallocate (mach_task_self (), child_task);
+ child_task = task;
+ }
+
+ if (signo == SIGKILL)
+ {
+ err = task_terminate (task);
+ if (err != MACH_SEND_INVALID_DEST)
+ error (0, err, "task_terminate");
+ }
+ else
+ {
+ mach_port_t msgport;
+ err = proc_getmsgport (procserver, child_pid, &msgport);
+ if (err)
+ error (0, err, "proc_getmsgport");
+ else
+ {
+ err = send_signal (msgport, signo, task,
+ 500); /* Block only half a second. */
+ mach_port_deallocate (mach_task_self (), msgport);
+ if (err)
+ {
+ error (0, err, "cannot send %s to child %d",
+ strsignal (signo), child_pid);
+ err = task_terminate (task);
+ if (err != MACH_SEND_INVALID_DEST)
+ error (0, err, "task_terminate");
+ }
+ }
+ }
+ }
+}
+
+/* Start the child program PROG. It is run via /libexec/console-run
+ with the given additional arguments. */
+static int
+start_child (const char *prog, char **progargs)
+{
+ file_t file;
+ error_t err;
+ char *args;
+ size_t arglen;
+
+ if (progargs == 0)
+ {
+ const char *argv[] = { "/libexec/console-run", prog, 0 };
+ err = argz_create ((char **) argv, &args, &arglen);
+ }
+ else
+ {
+ int argc = 0;
+ while (progargs[argc] != 0)
+ ++argc;
+ {
+ const char *argv[2 + argc + 1];
+ argv[0] = "/libexec/console-run";
+ argv[1] = prog;
+ argv[2 + argc] = 0;
+ while (argc-- > 0)
+ argv[2 + argc] = progargs[argc];
+ err = argz_create ((char **) argv, &args, &arglen);
+ }
+ }
+ assert_perror (err);
+
+ file = file_name_lookup (args, O_EXEC, 0);
+ if (file == MACH_PORT_NULL)
+ {
+ error (0, errno, "%s", args);
+ free (args);
+ return -1;
+ }
+
+ task_create (mach_task_self (),
+#ifdef KERN_INVALID_LEDGER
+ NULL, 0, /* OSF Mach */
+#endif
+ 0, &child_task);
+ proc_child (procserver, child_task);
+ proc_task2pid (procserver, child_task, &child_pid);
+ proc_task2proc (procserver, child_task, &default_ports[INIT_PORT_PROC]);
+
+ if (bootstrap_args & RB_KDB)
+ {
+ printf ("Pausing for %s\n", args);
+ getchar ();
+ }
+
+ err = file_exec (file, child_task, 0,
+ args, arglen,
+ startup_envz, startup_envz_len,
+ NULL, MACH_MSG_TYPE_COPY_SEND, 0, /* No fds. */
+ default_ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
+ default_ints, INIT_INT_MAX,
+ NULL, 0, NULL, 0);
+ proc_mark_important (default_ports[INIT_PORT_PROC]);
+ mach_port_deallocate (mach_task_self (), default_ports[INIT_PORT_PROC]);
+ mach_port_deallocate (mach_task_self (), file);
+ if (err)
+ {
+ error (0, err, "Cannot execute %s", args);
+ free (args);
+ return -1;
+ }
+ free (args);
+ return 0;
+}
+
+static void
+launch_something (const char *why)
+{
+ file_t something;
+ static unsigned int try;
+ static const char *const tries[] =
+ {
+ "/libexec/runsystem",
+ _PATH_BSHELL,
+ "/bin/shd", /* XXX */
+ };
+
+ if (why)
+ error (0, 0, "%s %s", tries[try - 1], why);
+
+ something = file_name_lookup (tries[try], O_EXEC, 0);
+ if (something != MACH_PORT_NULL)
+ {
+ mach_port_deallocate (mach_task_self (), something);
+ if (try == 0 && start_child (tries[try++], &global_argv[1]) == 0)
+ return;
+ }
+ else
+ try++;
+
+ while (try < sizeof tries / sizeof tries[0])
+ {
+ something = file_name_lookup (tries[try], O_EXEC, 0);
+ if (something != MACH_PORT_NULL)
+ {
+ mach_port_deallocate (mach_task_self (), something);
+ if (start_child (tries[try++], NULL) == 0)
+ return;
+ }
+ }
+
+ crash_system ();
+}
+
+void
+launch_system (void)
+{
+ launch_something (0);
+}
+
+/** RPC servers **/
+
+kern_return_t
+S_startup_procinit (startup_t server,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_porttype,
+ process_t proc,
+ mach_port_t *startuptask,
+ auth_t *auth,
+ mach_port_t *priv,
+ mach_msg_type_name_t *hostprivtype,
+ mach_port_t *dev,
+ mach_msg_type_name_t *devtype)
+{
+ if (procserver)
+ /* Only one proc server. */
+ return EPERM;
+
+ procserver = proc;
+
+ procreply = reply;
+ procreplytype = reply_porttype;
+
+ /* Save the reply port until we get startup_authinit. */
+ if (authserver)
+ launch_core_servers ();
+
+ return MIG_NO_REPLY;
+}
+
+/* Called by the auth server when it starts up. */
+
+kern_return_t
+S_startup_authinit (startup_t server,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_porttype,
+ mach_port_t auth,
+ mach_port_t *proc,
+ mach_msg_type_name_t *proctype)
+{
+ if (authserver)
+ /* Only one auth server. */
+ return EPERM;
+
+ authserver = auth;
+
+ /* Save the reply port until we get startup_procinit. */
+ authreply = reply;
+ authreplytype = reply_porttype;
+
+ if (procserver)
+ launch_core_servers ();
+
+ return MIG_NO_REPLY;
+}
+
+
+kern_return_t
+S_startup_essential_task (mach_port_t server,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ task_t task,
+ mach_port_t excpt,
+ char *name,
+ mach_port_t credential)
+{
+ static int authinit, procinit, execinit;
+ int fail;
+
+ /* Always deallocate the extra reference this message carries. */
+ if (MACH_PORT_VALID (credential))
+ mach_port_deallocate (mach_task_self (), credential);
+
+ if (credential != host_priv)
+ return EPERM;
+
+ fail = record_essential_task (name, task);
+ if (fail)
+ return fail;
+
+ if (!booted)
+ {
+ if (!strcmp (name, "auth"))
+ authinit = 1;
+ else if (!strcmp (name, "exec"))
+ {
+ execinit = 1;
+ mach_port_t execproc;
+ proc_task2proc (procserver, task, &execproc);
+ proc_mark_important (execproc);
+ }
+ else if (!strcmp (name, "proc"))
+ procinit = 1;
+
+ if (authinit && execinit && procinit)
+ {
+ /* Reply to this RPC, after that everything
+ is ready for real startup to begin. */
+ startup_essential_task_reply (reply, replytype, 0);
+
+ init_stdarrays ();
+ frob_kernel_process ();
+
+ launch_system ();
+
+ booted = 1;
+
+ return MIG_NO_REPLY;
+ }
+ }
+
+ return 0;
+}
+
+kern_return_t
+S_startup_request_notification (mach_port_t server,
+ mach_port_t notify,
+ char *name)
+{
+ struct ntfy_task *nt;
+
+ request_dead_name (notify);
+
+ /* Note that the ntfy_tasks list is kept in inverse order of the
+ calls; this is important. We need later notification requests
+ to get executed first. */
+ nt = malloc (sizeof (struct ntfy_task));
+ nt->notify_port = notify;
+ nt->next = ntfy_tasks;
+ ntfy_tasks = nt;
+ nt->name = malloc (strlen (name) + 1);
+ strcpy (nt->name, name);
+ return 0;
+}
+
+kern_return_t
+do_mach_notify_dead_name (mach_port_t notify,
+ mach_port_t name)
+{
+ struct ntfy_task *nt, *pnt;
+ struct ess_task *et;
+
+ assert (notify == startup);
+
+ /* Deallocate the extra reference the notification carries. */
+ mach_port_deallocate (mach_task_self (), name);
+
+ for (et = ess_tasks; et != NULL; et = et->next)
+ if (et->task_port == name)
+ /* An essential task has died. */
+ {
+ error (0, 0, "Crashing system; essential task %s died", et->name);
+ crash_system ();
+ }
+
+ for (nt = ntfy_tasks, pnt = NULL; nt != NULL; pnt = nt, nt = nt->next)
+ if (nt->notify_port == name)
+ {
+ /* Someone who wanted to be notified is gone. */
+ mach_port_deallocate (mach_task_self (), name);
+ if (pnt != NULL)
+ pnt->next = nt->next;
+ else
+ ntfy_tasks = nt->next;
+ free (nt);
+
+ return 0;
+ }
+
+ if (! booted)
+ {
+ /* The system has not come up yet, so essential tasks are not yet
+ registered. But the essential servers involved in the bootstrap
+ handshake might crash before completing it, so we have requested
+ dead-name notification on those tasks. */
+ static const struct { task_t *taskp; const char *name; } boots[] =
+ {
+ {&fstask, "bootstrap filesystem"},
+ {&authtask, "auth"},
+ {&proctask, "proc"},
+ };
+ size_t i;
+ for (i = 0; i < sizeof boots / sizeof boots[0]; ++i)
+ if (name == *boots[i].taskp)
+ {
+ error (0, 0, "Crashing system; %s server died during bootstrap",
+ boots[i].name);
+ crash_mach ();
+ }
+ error (0, 0, "BUG! Unexpected dead-name notification (name %#zx)",
+ name);
+ crash_mach ();
+ }
+
+ return 0;
+}
+
+kern_return_t
+S_startup_reboot (mach_port_t server,
+ mach_port_t refpt,
+ int code)
+{
+ if (refpt != host_priv)
+ return EPERM;
+
+ reboot_system (code);
+ for (;;);
+}
+
+/* Stubs for unused notification RPCs. */
+
+kern_return_t
+do_mach_notify_port_destroyed (mach_port_t notify,
+ mach_port_t rights)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_send_once (mach_port_t notify)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_no_senders (mach_port_t port, mach_port_mscount_t mscount)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_port_deleted (mach_port_t notify,
+ mach_port_t name)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+do_mach_notify_msg_accepted (mach_port_t notify,
+ mach_port_t name)
+{
+ return EOPNOTSUPP;
+}
+
+/* msg server */
+
+kern_return_t
+S_msg_sig_post_untraced (mach_port_t msgport,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int signo, natural_t sigcode, mach_port_t refport)
+{
+ if (refport != mach_task_self ())
+ return EPERM;
+ mach_port_deallocate (mach_task_self (), refport);
+
+ /* Reply immediately */
+ msg_sig_post_untraced_reply (reply, reply_type, 0);
+
+ process_signal (signo);
+ return MIG_NO_REPLY;
+}
+
+kern_return_t
+S_msg_sig_post (mach_port_t msgport,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int signo, natural_t sigcode, mach_port_t refport)
+{
+ if (refport != mach_task_self ())
+ return EPERM;
+ mach_port_deallocate (mach_task_self (), refport);
+
+ /* Reply immediately */
+ msg_sig_post_reply (reply, reply_type, 0);
+
+ process_signal (signo);
+ return MIG_NO_REPLY;
+}
+
+
+/* For the rest of the msg functions, just call the C library's
+ internal server stubs usually run in the signal thread. */
+
+kern_return_t
+S_msg_proc_newids (mach_port_t process,
+ mach_port_t task,
+ pid_t ppid,
+ pid_t pgrp,
+ int orphaned)
+{ return _S_msg_proc_newids (process, task, ppid, pgrp, orphaned); }
+
+
+kern_return_t
+S_msg_add_auth (mach_port_t process,
+ auth_t auth)
+{ return _S_msg_add_auth (process, auth); }
+
+
+kern_return_t
+S_msg_del_auth (mach_port_t process,
+ mach_port_t task,
+ intarray_t uids,
+ mach_msg_type_number_t uidsCnt,
+ intarray_t gids,
+ mach_msg_type_number_t gidsCnt)
+{ return _S_msg_del_auth (process, task, uids, uidsCnt, gids, gidsCnt); }
+
+
+kern_return_t
+S_msg_get_init_port (mach_port_t process,
+ mach_port_t refport,
+ int which,
+ mach_port_t *port,
+ mach_msg_type_name_t *portPoly)
+{ return _S_msg_get_init_port (process, refport, which, port, portPoly); }
+
+
+kern_return_t
+S_msg_set_init_port (mach_port_t process,
+ mach_port_t refport,
+ int which,
+ mach_port_t port)
+{ return _S_msg_set_init_port (process, refport, which, port); }
+
+
+kern_return_t
+S_msg_get_init_ports (mach_port_t process,
+ mach_port_t refport,
+ portarray_t *ports,
+ mach_msg_type_name_t *portsPoly,
+ mach_msg_type_number_t *portsCnt)
+{ return _S_msg_get_init_ports (process, refport, ports, portsPoly, portsCnt); }
+
+
+kern_return_t
+S_msg_set_init_ports (mach_port_t process,
+ mach_port_t refport,
+ portarray_t ports,
+ mach_msg_type_number_t portsCnt)
+{ return _S_msg_set_init_ports (process, refport, ports, portsCnt); }
+
+
+kern_return_t
+S_msg_get_init_int (mach_port_t process,
+ mach_port_t refport,
+ int which,
+ int *value)
+{ return _S_msg_get_init_int (process, refport, which, value); }
+
+
+kern_return_t
+S_msg_set_init_int (mach_port_t process,
+ mach_port_t refport,
+ int which,
+ int value)
+{ return _S_msg_set_init_int (process, refport, which, value); }
+
+
+kern_return_t
+S_msg_get_init_ints (mach_port_t process,
+ mach_port_t refport,
+ intarray_t *values,
+ mach_msg_type_number_t *valuesCnt)
+{ return _S_msg_get_init_ints (process, refport, values, valuesCnt); }
+
+
+kern_return_t
+S_msg_set_init_ints (mach_port_t process,
+ mach_port_t refport,
+ intarray_t values,
+ mach_msg_type_number_t valuesCnt)
+{ return _S_msg_set_init_ints (process, refport, values, valuesCnt); }
+
+
+kern_return_t
+S_msg_get_dtable (mach_port_t process,
+ mach_port_t refport,
+ portarray_t *dtable,
+ mach_msg_type_name_t *dtablePoly,
+ mach_msg_type_number_t *dtableCnt)
+{ return _S_msg_get_dtable (process, refport, dtable, dtablePoly, dtableCnt); }
+
+
+kern_return_t
+S_msg_set_dtable (mach_port_t process,
+ mach_port_t refport,
+ portarray_t dtable,
+ mach_msg_type_number_t dtableCnt)
+{ return _S_msg_set_dtable (process, refport, dtable, dtableCnt); }
+
+
+kern_return_t
+S_msg_get_fd (mach_port_t process,
+ mach_port_t refport,
+ int fd,
+ mach_port_t *port,
+ mach_msg_type_name_t *portPoly)
+{ return _S_msg_get_fd (process, refport, fd, port, portPoly); }
+
+
+kern_return_t
+S_msg_set_fd (mach_port_t process,
+ mach_port_t refport,
+ int fd,
+ mach_port_t port)
+{ return _S_msg_set_fd (process, refport, fd, port); }
+
+
+kern_return_t
+S_msg_get_environment (mach_port_t process,
+ data_t *value,
+ mach_msg_type_number_t *valueCnt)
+{ return _S_msg_get_environment (process, value, valueCnt); }
+
+
+kern_return_t
+S_msg_set_environment (mach_port_t process,
+ mach_port_t refport,
+ data_t value,
+ mach_msg_type_number_t valueCnt)
+{ return _S_msg_set_environment (process, refport, value, valueCnt); }
+
+
+kern_return_t
+S_msg_get_env_variable (mach_port_t process,
+ string_t variable,
+ data_t *value,
+ mach_msg_type_number_t *valueCnt)
+{ return _S_msg_get_env_variable (process, variable, value, valueCnt); }
+
+
+kern_return_t
+S_msg_set_env_variable (mach_port_t process,
+ mach_port_t refport,
+ string_t variable,
+ string_t value,
+ boolean_t replace)
+{ return _S_msg_set_env_variable (process, refport, variable, value, replace); }
+
+error_t
+S_msg_describe_ports (mach_port_t process,
+ mach_port_t refport,
+ mach_port_array_t names,
+ mach_msg_type_number_t namesCnt,
+ data_t *descriptions,
+ mach_msg_type_number_t *descriptionsCnt)
+{
+ return _S_msg_describe_ports (process, refport, names, namesCnt,
+ descriptions, descriptionsCnt);
+}
+
+error_t
+S_msg_report_wait (mach_port_t process, thread_t thread,
+ string_t desc, mach_msg_id_t *rpc)
+{
+ *desc = 0;
+ *rpc = 0;
+ return 0;
+}
diff --git a/init/stubs.c b/init/stubs.c
new file mode 100644
index 00000000..5292ab68
--- /dev/null
+++ b/init/stubs.c
@@ -0,0 +1,139 @@
+/* By-hand stubs for some RPC calls
+ Copyright (C) 1994,96,99,2000 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+#include <hurd/hurd_types.h>
+#include <mach.h>
+#include <string.h>
+#include <assert.h>
+
+/* From hurd/msg.defs: */
+#define RPCID_SIG_POST 23000
+
+
+/* Send signal SIGNO to MSGPORT with REFPORT as reference. Don't
+ block in any fashion. */
+error_t
+send_signal (mach_port_t msgport,
+ int signal,
+ mach_port_t refport,
+ mach_msg_timeout_t timeout)
+{
+ error_t err;
+
+ /* This message buffer might be modified by mach_msg in some error cases,
+ so we cannot safely reuse a static buffer. */
+ struct
+ {
+ mach_msg_header_t head;
+ mach_msg_type_t signaltype;
+ int signal;
+ mach_msg_type_t sigcode_type;
+ natural_t sigcode;
+ mach_msg_type_t refporttype;
+ mach_port_t refport;
+ }
+ message =
+ {
+ {
+ /* Message header: */
+ (MACH_MSGH_BITS_COMPLEX
+ | MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE)), /* msgh_bits */
+ sizeof message, /* msgh_size */
+ msgport, /* msgh_remote_port */
+ MACH_PORT_NULL, /* msgh_local_port */
+ 0, /* msgh_seqno */
+ RPCID_SIG_POST, /* msgh_id */
+ },
+ {
+ /* Type descriptor for signo */
+ MACH_MSG_TYPE_INTEGER_32, /* msgt_name */
+ 32, /* msgt_size */
+ 1, /* msgt_number */
+ 1, /* msgt_inline */
+ 0, /* msgt_longform */
+ 0, /* msgt_deallocate */
+ 0, /* msgt_unused */
+ },
+ /* Signal number */
+ signal,
+ /* Type descriptor for sigcode */
+ {
+ MACH_MSG_TYPE_INTEGER_32, /* msgt_name */
+ 32, /* msgt_size */
+ 1, /* msgt_number */
+ 1, /* msgt_inline */
+ 0, /* msgt_longform */
+ 0, /* msgt_deallocate */
+ 0, /* msgt_unused */
+ },
+ /* Sigcode */
+ 0,
+ {
+ /* Type descriptor for refport */
+ MACH_MSG_TYPE_COPY_SEND, /* msgt_name */
+ 32, /* msgt_size */
+ 1, /* msgt_number */
+ 1, /* msgt_inline */
+ 0, /* msgt_longform */
+ 0, /* msgt_deallocate */
+ 0, /* msgt_unused */
+ },
+ /* Reference port */
+ refport
+ };
+
+ err = mach_msg (&message.head,
+ MACH_SEND_MSG|MACH_SEND_TIMEOUT, sizeof message, 0,
+ MACH_PORT_NULL, timeout, MACH_PORT_NULL);
+
+ switch (err)
+ {
+ case MACH_SEND_TIMED_OUT:
+ /* The send could not complete in time. In this error case, the
+ kernel has modified the message buffer in a pseudo-receive
+ operation. That means our COPY_SEND refs might now be MOVE_SEND
+ refs, in which case each has gained user ref accordingly. To
+ avoid leaking those refs, we must clean up the buffer. We don't
+ use mach_msg_destroy because it assumes the local/remote ports in
+ the header have been reversed as from a real receive, while a
+ pseudo-receive leaves them as they were. */
+ if (MACH_MSGH_BITS_REMOTE (message.head.msgh_bits)
+ == MACH_MSG_TYPE_MOVE_SEND)
+ mach_port_deallocate (mach_task_self (),
+ message.head.msgh_remote_port);
+ if (message.refporttype.msgt_name == MACH_MSG_TYPE_MOVE_SEND)
+ mach_port_deallocate (mach_task_self (), message.refport);
+ break;
+
+ /* These are the other codes that mean a pseudo-receive modified
+ the message buffer and we might need to clean up the send rights.
+ None of them should be possible in our usage. */
+ case MACH_SEND_INTERRUPTED:
+ case MACH_SEND_INVALID_NOTIFY:
+ case MACH_SEND_NO_NOTIFY:
+ case MACH_SEND_NOTIFY_IN_PROGRESS:
+ assert_perror (err);
+ break;
+
+ default: /* Other errors are safe to ignore. */
+ break;
+ }
+
+ return err;
+}
diff --git a/install-sh b/install-sh
new file mode 100644
index 00000000..a5897de6
--- /dev/null
+++ b/install-sh
@@ -0,0 +1,519 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2006-12-25.00
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+nl='
+'
+IFS=" "" $nl"
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit=${DOITPROG-}
+if test -z "$doit"; then
+ doit_exec=exec
+else
+ doit_exec=$doit
+fi
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_glob='?'
+initialize_posix_glob='
+ test "$posix_glob" != "?" || {
+ if (set -f) 2>/dev/null; then
+ posix_glob=
+ else
+ posix_glob=:
+ fi
+ }
+'
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+no_target_directory=
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+ or: $0 [OPTION]... SRCFILES... DIRECTORY
+ or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+ or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+ --help display this help and exit.
+ --version display version info and exit.
+
+ -c (ignored)
+ -C install only if different (preserve the last data modification time)
+ -d create directories instead of installing files.
+ -g GROUP $chgrpprog installed files to GROUP.
+ -m MODE $chmodprog installed files to MODE.
+ -o USER $chownprog installed files to USER.
+ -s $stripprog installed files.
+ -t DIRECTORY install into DIRECTORY.
+ -T report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+ CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+ RMPROG STRIPPROG
+"
+
+while test $# -ne 0; do
+ case $1 in
+ -c) ;;
+
+ -C) copy_on_change=true;;
+
+ -d) dir_arg=true;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift;;
+
+ --help) echo "$usage"; exit $?;;
+
+ -m) mode=$2
+ case $mode in
+ *' '* | *' '* | *'
+'* | *'*'* | *'?'* | *'['*)
+ echo "$0: invalid mode: $mode" >&2
+ exit 1;;
+ esac
+ shift;;
+
+ -o) chowncmd="$chownprog $2"
+ shift;;
+
+ -s) stripcmd=$stripprog;;
+
+ -t) dst_arg=$2
+ shift;;
+
+ -T) no_target_directory=true;;
+
+ --version) echo "$0 $scriptversion"; exit $?;;
+
+ --) shift
+ break;;
+
+ -*) echo "$0: invalid option: $1" >&2
+ exit 1;;
+
+ *) break;;
+ esac
+ shift
+done
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+ # When -d is used, all remaining arguments are directories to create.
+ # When -t is used, the destination is already specified.
+ # Otherwise, the last argument is the destination. Remove it from $@.
+ for arg
+ do
+ if test -n "$dst_arg"; then
+ # $@ is not empty: it contains at least $arg.
+ set fnord "$@" "$dst_arg"
+ shift # fnord
+ fi
+ shift # arg
+ dst_arg=$arg
+ done
+fi
+
+if test $# -eq 0; then
+ if test -z "$dir_arg"; then
+ echo "$0: no input file specified." >&2
+ exit 1
+ fi
+ # It's OK to call `install-sh -d' without argument.
+ # This can happen when creating conditional directories.
+ exit 0
+fi
+
+if test -z "$dir_arg"; then
+ trap '(exit $?); exit' 1 2 13 15
+
+ # Set umask so as not to create temps with too-generous modes.
+ # However, 'strip' requires both read and write access to temps.
+ case $mode in
+ # Optimize common cases.
+ *644) cp_umask=133;;
+ *755) cp_umask=22;;
+
+ *[0-7])
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw='% 200'
+ fi
+ cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+ *)
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw=,u+rw
+ fi
+ cp_umask=$mode$u_plus_rw;;
+ esac
+fi
+
+for src
+do
+ # Protect names starting with `-'.
+ case $src in
+ -*) src=./$src;;
+ esac
+
+ if test -n "$dir_arg"; then
+ dst=$src
+ dstdir=$dst
+ test -d "$dstdir"
+ dstdir_status=$?
+ else
+
+ # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+ # might cause directories to be created, which would be especially bad
+ # if $src (and thus $dsttmp) contains '*'.
+ if test ! -f "$src" && test ! -d "$src"; then
+ echo "$0: $src does not exist." >&2
+ exit 1
+ fi
+
+ if test -z "$dst_arg"; then
+ echo "$0: no destination specified." >&2
+ exit 1
+ fi
+
+ dst=$dst_arg
+ # Protect names starting with `-'.
+ case $dst in
+ -*) dst=./$dst;;
+ esac
+
+ # If destination is a directory, append the input filename; won't work
+ # if double slashes aren't ignored.
+ if test -d "$dst"; then
+ if test -n "$no_target_directory"; then
+ echo "$0: $dst_arg: Is a directory" >&2
+ exit 1
+ fi
+ dstdir=$dst
+ dst=$dstdir/`basename "$src"`
+ dstdir_status=0
+ else
+ # Prefer dirname, but fall back on a substitute if dirname fails.
+ dstdir=`
+ (dirname "$dst") 2>/dev/null ||
+ expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$dst" : 'X\(//\)[^/]' \| \
+ X"$dst" : 'X\(//\)$' \| \
+ X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
+ echo X"$dst" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'
+ `
+
+ test -d "$dstdir"
+ dstdir_status=$?
+ fi
+ fi
+
+ obsolete_mkdir_used=false
+
+ if test $dstdir_status != 0; then
+ case $posix_mkdir in
+ '')
+ # Create intermediate dirs using mode 755 as modified by the umask.
+ # This is like FreeBSD 'install' as of 1997-10-28.
+ umask=`umask`
+ case $stripcmd.$umask in
+ # Optimize common cases.
+ *[2367][2367]) mkdir_umask=$umask;;
+ .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+ *[0-7])
+ mkdir_umask=`expr $umask + 22 \
+ - $umask % 100 % 40 + $umask % 20 \
+ - $umask % 10 % 4 + $umask % 2
+ `;;
+ *) mkdir_umask=$umask,go-w;;
+ esac
+
+ # With -d, create the new directory with the user-specified mode.
+ # Otherwise, rely on $mkdir_umask.
+ if test -n "$dir_arg"; then
+ mkdir_mode=-m$mode
+ else
+ mkdir_mode=
+ fi
+
+ posix_mkdir=false
+ case $umask in
+ *[123567][0-7][0-7])
+ # POSIX mkdir -p sets u+wx bits regardless of umask, which
+ # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+ ;;
+ *)
+ tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+ if (umask $mkdir_umask &&
+ exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
+ then
+ if test -z "$dir_arg" || {
+ # Check for POSIX incompatibilities with -m.
+ # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+ # other-writeable bit of parent directory when it shouldn't.
+ # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+ ls_ld_tmpdir=`ls -ld "$tmpdir"`
+ case $ls_ld_tmpdir in
+ d????-?r-*) different_mode=700;;
+ d????-?--*) different_mode=755;;
+ *) false;;
+ esac &&
+ $mkdirprog -m$different_mode -p -- "$tmpdir" && {
+ ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
+ test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+ }
+ }
+ then posix_mkdir=:
+ fi
+ rmdir "$tmpdir/d" "$tmpdir"
+ else
+ # Remove any dirs left behind by ancient mkdir implementations.
+ rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
+ fi
+ trap '' 0;;
+ esac;;
+ esac
+
+ if
+ $posix_mkdir && (
+ umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+ )
+ then :
+ else
+
+ # The umask is ridiculous, or mkdir does not conform to POSIX,
+ # or it failed possibly due to a race condition. Create the
+ # directory the slow way, step by step, checking for races as we go.
+
+ case $dstdir in
+ /*) prefix='/';;
+ -*) prefix='./';;
+ *) prefix='';;
+ esac
+
+ eval "$initialize_posix_glob"
+
+ oIFS=$IFS
+ IFS=/
+ $posix_glob set -f
+ set fnord $dstdir
+ shift
+ $posix_glob set +f
+ IFS=$oIFS
+
+ prefixes=
+
+ for d
+ do
+ test -z "$d" && continue
+
+ prefix=$prefix$d
+ if test -d "$prefix"; then
+ prefixes=
+ else
+ if $posix_mkdir; then
+ (umask=$mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+ # Don't fail if two instances are running concurrently.
+ test -d "$prefix" || exit 1
+ else
+ case $prefix in
+ *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) qprefix=$prefix;;
+ esac
+ prefixes="$prefixes '$qprefix'"
+ fi
+ fi
+ prefix=$prefix/
+ done
+
+ if test -n "$prefixes"; then
+ # Don't fail if two instances are running concurrently.
+ (umask $mkdir_umask &&
+ eval "\$doit_exec \$mkdirprog $prefixes") ||
+ test -d "$dstdir" || exit 1
+ obsolete_mkdir_used=true
+ fi
+ fi
+ fi
+
+ if test -n "$dir_arg"; then
+ { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+ { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+ else
+
+ # Make a couple of temp file names in the proper directory.
+ dsttmp=$dstdir/_inst.$$_
+ rmtmp=$dstdir/_rm.$$_
+
+ # Trap to clean up those temp files at exit.
+ trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+ # Copy the file name to the temp name.
+ (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+
+ # and set any options; do chmod last to preserve setuid bits.
+ #
+ # If any of these fail, we abort the whole thing. If we want to
+ # ignore errors from any of these, just make sure not to ignore
+ # errors from the above "$doit $cpprog $src $dsttmp" command.
+ #
+ { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+ { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+ { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+ # If -C, don't bother to copy if it wouldn't change the file.
+ if $copy_on_change &&
+ old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
+ new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
+
+ eval "$initialize_posix_glob" &&
+ $posix_glob set -f &&
+ set X $old && old=:$2:$4:$5:$6 &&
+ set X $new && new=:$2:$4:$5:$6 &&
+ $posix_glob set +f &&
+
+ test "$old" = "$new" &&
+ $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+ then
+ rm -f "$dsttmp"
+ else
+ # Rename the file to the real destination.
+ $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+ # The rename failed, perhaps because mv can't rename something else
+ # to itself, or perhaps because mv is so ancient that it does not
+ # support -f.
+ {
+ # Now remove or move aside any old file at destination location.
+ # We try this two ways since rm can't unlink itself on some
+ # systems and the destination file might be busy for other
+ # reasons. In this case, the final cleanup might fail but the new
+ # file should still install successfully.
+ {
+ test ! -f "$dst" ||
+ $doit $rmcmd -f "$dst" 2>/dev/null ||
+ { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+ { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+ } ||
+ { echo "$0: cannot unlink or rename $dst" >&2
+ (exit 1); exit 1
+ }
+ } &&
+
+ # Now rename the file to the real destination.
+ $doit $mvcmd "$dsttmp" "$dst"
+ }
+ fi || exit 1
+
+ trap '' 0
+ fi
+done
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/isofs/EXTENSIONS b/isofs/EXTENSIONS
new file mode 100644
index 00000000..8843aa5f
--- /dev/null
+++ b/isofs/EXTENSIONS
@@ -0,0 +1,15 @@
+-*- Text -*-
+
+These are the SUSP and RockRidge compliant extensions that GNU uses:
+
+See rr.h for format details.
+
+AU: Author, an additional uid on a file. If this is not present, the
+author is the same as the owner.
+
+TR: Translator, specifying a command line to run as a translator.
+
+MD: A full 32 bit hurd mode; if not present, the mode is the one found
+in the PX record.
+
+FL: 32 bits of flags; if not present, the flags are zero.
diff --git a/isofs/Makefile b/isofs/Makefile
new file mode 100644
index 00000000..6475c521
--- /dev/null
+++ b/isofs/Makefile
@@ -0,0 +1,29 @@
+# Copyright (C) 1997, 2000, 2005, 2012 Free Software Foundation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := isofs
+makemode := server
+
+target = iso9660fs
+SRCS = inode.c main.c lookup.c pager.c rr.c
+
+OBJS = $(SRCS:.c=.o)
+HURDLIBS = diskfs iohelp fshelp store pager ports ihash shouldbeinlibc
+OTHERLIBS = -lpthread $(and $(HAVE_LIBBZ2),-lbz2) $(and $(HAVE_LIBZ),-lz)
+
+include ../Makeconf
+
+iso9660fs.static: $(boot-store-types:%=../libstore/libstore_%.a)
diff --git a/isofs/ext.c b/isofs/ext.c
new file mode 100644
index 00000000..5e5f3edc
--- /dev/null
+++ b/isofs/ext.c
@@ -0,0 +1,58 @@
+/*
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* Extensions to ISO 9660 we support */
+
+#include "isofs.h"
+
+struct susp_field susp_extension[] =
+{
+ { 'C', 'E', 1, process_su_ce },
+ { 'P', 'D', 1, process_su_pd },
+ { 'S', 'P', 1, process_su_sp },
+ { 'E', 'R', 1, process_su_er },
+ { 'S', 'T', 1, process_su_st },
+ { 0, 0, 0, 0 },
+};
+
+struct susp_field rr_extension[] =
+{
+ { 'P', 'X', 1, process_rr_px },
+ { 'P', 'N', 1, process_rr_pn },
+ { 'S', 'L', 1, process_rr_sl },
+ { 'N', 'M', 1, process_rr_nm },
+ { 'C', 'L', 1, process_rr_cl },
+ { 'P', 'L', 1, process_rr_pl },
+ { 'R', 'E', 1, process_rr_re },
+ { 'T', 'F', 1, process_rr_tf },
+ { 'S', 'F', 1, process_rr_sf },
+ { 0, 0, 0, 0 },
+};
+
+struct susp_ext extensions[] =
+{
+ { "RRIP_1991A", 1,
+ "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS",
+ "ROCK RIDGE SPECIFICATION VERSION 1 REVISION 1.10 JULY 13 1993",
+ rr_extensions
+ },
+ { 0, 0, 0, 0, susp_extensions },
+ { 0, 0, 0, 0, 0 },
+}
diff --git a/isofs/inode.c b/isofs/inode.c
new file mode 100644
index 00000000..cdc05ae3
--- /dev/null
+++ b/isofs/inode.c
@@ -0,0 +1,635 @@
+/*
+ Copyright (C) 1997, 1998, 2002, 2007 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include "isofs.h"
+#include "libdiskfs/fs_S.h"
+
+
+/* There is no such thing as an inode in this format, all such
+ information being recorded in the directory entry. So we report
+ inode numbers as absolute offsets from DISK_IMAGE. We use the directory
+ record for symlinks and zero length files, and file_start otherwise.
+ Only for hard links to zero length files we get extra inodes. */
+
+#define INOHSZ 512
+#if ((INOHSZ&(INOHSZ-1)) == 0)
+#define INOHASH(ino) ((ino>>8)&(INOHSZ-1))
+#else
+#define INOHASH(ino) (((unsigned)(ino>>8))%INOHSZ)
+#endif
+
+struct node_cache
+{
+ struct dirrect *dr; /* somewhere in disk_image */
+ off_t file_start; /* start of file */
+
+ off_t id; /* UNIQUE identifier. */
+
+ struct node *np; /* if live */
+};
+
+static int node_cache_size = 0;
+static int node_cache_alloced = 0;
+struct node_cache *node_cache = 0;
+
+/* Forward */
+static error_t read_disknode (struct node *,
+ struct dirrect *, struct rrip_lookup *);
+
+
+/* See if node with identifier ID is in the cache. If so, return it,
+ with one additional reference. diskfs_node_refcnt_lock must be held
+ on entry to the call, and will be released iff the node was found
+ in the cache. */
+void
+inode_cache_find (off_t id, struct node **npp)
+{
+ int i;
+
+ for (i = 0; i < node_cache_size; i++)
+ if (node_cache[i].id == id
+ && node_cache[i].np)
+ {
+ *npp = node_cache[i].np;
+ (*npp)->references++;
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ pthread_mutex_lock (&(*npp)->lock);
+ return;
+ }
+ *npp = 0;
+}
+
+
+/* Determine if we use file_start or struct dirrect * as node id. */
+int
+use_file_start_id (struct dirrect *record, struct rrip_lookup *rr)
+{
+ /* If it is a symlink or a zero length file, don't use file_start. */
+ if (rr->valid & VALID_SL || isonum_733 (record->size) == 0)
+ return 0;
+
+ return 1;
+}
+
+/* Enter NP into the cache. The directory entry we used is DR, the
+ cached Rock-Ridge info RR. diskfs_node_refcnt_lock must be held. */
+void
+cache_inode (struct node *np, struct dirrect *record,
+ struct rrip_lookup *rr)
+{
+ int i;
+ struct node_cache *c = 0;
+ off_t id;
+
+ if (use_file_start_id (record, rr))
+ id = np->dn->file_start << store->log2_block_size;
+ else
+ id = (off_t) ((void *) record - (void *) disk_image);
+
+ /* First see if there's already an entry. */
+ for (i = 0; i < node_cache_size; i++)
+ if (node_cache[i].id == id)
+ break;
+
+ if (i == node_cache_size)
+ {
+ if (node_cache_size >= node_cache_alloced)
+ {
+ if (!node_cache_alloced)
+ {
+ /* Initialize */
+ node_cache_alloced = 10;
+ node_cache = malloc (sizeof (struct node_cache) * 10);
+ }
+ else
+ {
+ node_cache_alloced *= 2;
+ node_cache = realloc (node_cache,
+ sizeof (struct node_cache)
+ * node_cache_alloced);
+ }
+ assert (node_cache);
+ }
+ node_cache_size++;
+ }
+
+ c = &node_cache[i];
+ c->id = id;
+ c->dr = record;
+ c->file_start = np->dn->file_start;
+ c->np = np;
+
+ /* PLUS 1 so that we don't store zero cache ID's (not allowed by diskfs) */
+ np->cache_id = i + 1;
+}
+
+/* Fetch inode with cache id ID; set *NPP to the node structure;
+ gain one user reference and lock the node. */
+error_t
+diskfs_cached_lookup (ino_t id, struct node **npp)
+{
+ struct node *np;
+ error_t err;
+
+ /* Cache ID's are incremented when presented to diskfs
+ to avoid presenting zero cache ID's. */
+ id--;
+
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ assert (id < node_cache_size);
+
+ np = node_cache[id].np;
+
+ if (!np)
+ {
+ struct node_cache *c = &node_cache[id];
+ struct rrip_lookup rr;
+ struct disknode *dn;
+
+ rrip_lookup (node_cache[id].dr, &rr, 1);
+
+ /* We should never cache the wrong directory entry */
+ assert (!(rr.valid & VALID_CL));
+
+ dn = malloc (sizeof (struct disknode));
+ if (!dn)
+ {
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ release_rrip (&rr);
+ return ENOMEM;
+ }
+ dn->fileinfo = 0;
+ dn->dr = c->dr;
+ dn->file_start = c->file_start;
+ np = diskfs_make_node (dn);
+ if (!np)
+ {
+ free (dn);
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ release_rrip (&rr);
+ return ENOMEM;
+ }
+ np->cache_id = id + 1; /* see above for rationale for increment */
+ pthread_mutex_lock (&np->lock);
+ c->np = np;
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+
+ err = read_disknode (np, node_cache[id].dr, &rr);
+ if (!err)
+ *npp = np;
+
+ release_rrip (&rr);
+
+ return err;
+ }
+
+
+ np->references++;
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ pthread_mutex_lock (&np->lock);
+ *npp = np;
+ return 0;
+}
+
+
+/* Return Epoch-based time from a seven byte according to 9.1.5 */
+char *
+isodate_915 (char *c, struct timespec *ts)
+{
+ struct tm tm;
+ signed char tz;
+
+ /* Copy into a struct TM. */
+ tm.tm_year = *c++;
+ tm.tm_mon = *c++ - 1;
+ tm.tm_mday = *c++;
+ tm.tm_hour = *c++;
+ tm.tm_min = *c++;
+ tm.tm_sec = *c++;
+ tz = *c++;
+
+ tm.tm_isdst = 0;
+ ts->tv_sec = timegm (&tm);
+ ts->tv_nsec = 0;
+
+ /* Only honor TZ offset if it makes sense */
+ if (-48 <= tz && tz <= 52)
+ ts->tv_sec -= 15 * 60 * tz; /* TZ is in fifteen minute chunks */
+
+ return c;
+}
+
+/* Return Epoch-based time from a seventeen byte according to 8.4.26.1 */
+char *
+isodate_84261 (char *c, struct timespec *ts)
+{
+ struct tm tm;
+ int hsec;
+ signed char tz;
+
+ sscanf (c, "%4d%2d%2d%2d%2d%2d%2d",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
+ &hsec);
+
+ /* Convert to appropriate units */
+ ts->tv_nsec = hsec * 10000000;
+ tm.tm_year -= 1900;
+ tm.tm_mon--;
+
+ tm.tm_isdst = 0;
+ ts->tv_sec = timegm (&tm);
+
+ tz = c[16];
+
+ /* Only honor TZ offset if it makes sense */
+ if (-48 <= tz && tz <= 52)
+ ts->tv_sec -= 15 * 60 * tz; /* TZ is in fifteen minute chunks */
+
+ return c + 17;
+}
+
+/* Calculate the file start (in store blocks) of the file at RECORD. */
+error_t
+calculate_file_start (struct dirrect *record, off_t *file_start,
+ struct rrip_lookup *rr)
+{
+ error_t err;
+
+ if (rr && (rr->valid & VALID_CL))
+ {
+ *file_start = (void *) rr->realdirent - (void *)disk_image;
+ *file_start >>= store->log2_block_size;
+ }
+ else if (rr && (rr->valid & VALID_PL))
+ *file_start = rr->realfilestart;
+ else
+ {
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ *file_start = ((isonum_733 (record->extent) + record->ext_attr_len)
+ * (logical_block_size / store->block_size));
+
+ diskfs_end_catch_exception ();
+ }
+ return 0;
+}
+
+
+/* Load the inode with directory entry RECORD and cached Rock-Ridge
+ info RR into NP. The directory entry is at OFFSET in BLOCK. */
+error_t
+load_inode (struct node **npp, struct dirrect *record,
+ struct rrip_lookup *rr)
+{
+ error_t err;
+ off_t file_start;
+ struct disknode *dn;
+ struct node *np;
+
+ err = calculate_file_start (record, &file_start, rr);
+ if (err)
+ return err;
+ if (rr->valid & VALID_CL)
+ record = rr->realdirent;
+
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+
+ /* First check the cache */
+ if (use_file_start_id (record, rr))
+ inode_cache_find (file_start << store->log2_block_size, npp);
+ else
+ inode_cache_find ((off_t) ((void *) record - (void *) disk_image), npp);
+
+ if (*npp)
+ {
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ return 0;
+ }
+
+ /* Create a new node */
+ dn = malloc (sizeof (struct disknode));
+ if (!dn)
+ {
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ return ENOMEM;
+ }
+ dn->fileinfo = 0;
+ dn->dr = record;
+ dn->file_start = file_start;
+
+ np = diskfs_make_node (dn);
+ if (!np)
+ {
+ free (dn);
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ return ENOMEM;
+ }
+
+ pthread_mutex_lock (&np->lock);
+
+ cache_inode (np, record, rr);
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+
+ err = read_disknode (np, record, rr);
+ *npp = np;
+ return err;
+}
+
+
+/* Read stat information from the directory entry at DR and the
+ contents of RL. */
+static error_t
+read_disknode (struct node *np, struct dirrect *dr,
+ struct rrip_lookup *rl)
+{
+ error_t err;
+ struct stat *st = &np->dn_stat;
+ st->st_fstype = FSTYPE_ISO9660;
+ st->st_fsid = getpid ();
+ if (use_file_start_id (dr, rl))
+ st->st_ino = (ino_t) np->dn->file_start << store->log2_block_size;
+ else
+ st->st_ino = (ino_t) ((void *) dr - (void *) disk_image);
+ st->st_gen = 0;
+ st->st_rdev = 0;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ return err;
+
+ if (rl->valid & VALID_PX)
+ {
+ if ((rl->valid & VALID_MD) == 0)
+ st->st_mode = rl->mode;
+ st->st_nlink = rl->nlink;
+ st->st_uid = rl->uid;
+ st->st_gid = rl->gid;
+ }
+ else
+ {
+ if ((rl->valid & VALID_MD) == 0)
+ {
+ /* If there are no periods, it's a directory. */
+ if (((rl->valid & VALID_NM) && !index (rl->name, '.'))
+ || (!(rl->valid & VALID_NM) && !memchr (dr->name, '.',
+ dr->namelen)))
+ st->st_mode = S_IFDIR | 0777;
+ else
+ st->st_mode = S_IFREG | 0666;
+ }
+ st->st_nlink = 1;
+ st->st_uid = 0;
+ st->st_gid = 0;
+ }
+
+ if (rl->valid & VALID_MD)
+ st->st_mode = rl->allmode;
+
+ if (rl->valid & VALID_AU)
+ st->st_author = rl->author;
+ else
+ st->st_author = st->st_gid;
+
+ st->st_size = isonum_733 (dr->size);
+
+ if ((rl->valid & VALID_PN)
+ && (S_ISCHR (st->st_mode) || S_ISBLK (st->st_mode)))
+ st->st_rdev = rl->rdev;
+ else
+ st->st_rdev = 0;
+
+ if (dr->ileave)
+ /* XXX ??? */
+ st->st_size = 0;
+
+ /* Calculate these if we'll need them */
+ if (!(rl->valid & VALID_TF)
+ || ((rl->tfflags & (TF_CREATION|TF_ACCESS|TF_MODIFY))
+ != (TF_CREATION|TF_ACCESS|TF_MODIFY)))
+ {
+ struct timespec ts;
+ isodate_915 ((char *) dr->date, &ts);
+ st->st_ctim = st->st_mtim = st->st_atim = ts;
+ }
+
+ /* Override what we have better info for */
+ if (rl->valid & VALID_TF)
+ {
+ if (rl->tfflags & TF_CREATION)
+ st->st_ctim = rl->ctime;
+
+ if (rl->tfflags & TF_ACCESS)
+ st->st_atim = rl->atime;
+
+ if (rl->tfflags & TF_MODIFY)
+ st->st_mtim = rl->mtime;
+ }
+
+ st->st_blksize = logical_block_size;
+ st->st_blocks = (st->st_size - 1) / 512 + 1;
+
+ if (rl->valid & VALID_FL)
+ st->st_flags = rl->flags;
+ else
+ st->st_flags = 0;
+
+ if (S_ISLNK (st->st_mode))
+ {
+ if (rl->valid & VALID_SL)
+ {
+ np->dn->link_target = rl->target;
+ rl->target = 0;
+ st->st_size = strlen (np->dn->link_target);
+ }
+ else
+ {
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= S_IFREG;
+ }
+ }
+
+ if (rl->valid & VALID_TR)
+ {
+ st->st_mode |= S_IPTRANS;
+ np->dn->translen = rl->translen;
+ np->dn->translator = rl->trans;
+ rl->trans = 0;
+ }
+ else
+ {
+ np->dn->translator = 0;
+ np->dn->translen = 0;
+ }
+
+ diskfs_end_catch_exception ();
+
+ return 0;
+}
+
+/* Symlink targets are never stored in files, so always use this. */
+static error_t
+read_symlink_hook (struct node *np, char *buf)
+{
+ memcpy (buf, np->dn->link_target, np->dn_stat.st_size + 1);
+ return 0;
+}
+error_t (*diskfs_read_symlink_hook) (struct node *, char *)
+ = read_symlink_hook;
+
+
+/* The last reference to NP has gone away; drop it from the cache
+ and clean all state in the dn structure. */
+void
+diskfs_node_norefs (struct node *np)
+{
+ assert (node_cache[np->cache_id - 1].np == np);
+ node_cache[np->cache_id - 1].np = 0;
+
+ if (np->dn->translator)
+ free (np->dn->translator);
+
+ assert (!np->dn->fileinfo);
+ free (np->dn);
+ free (np);
+}
+
+/* The last hard reference to a node has gone away; arrange to have
+ all the weak references dropped that can be. */
+void
+diskfs_try_dropping_softrefs (struct node *np)
+{
+ drop_pager_softrefs (np);
+}
+
+void
+diskfs_lost_hardrefs (struct node *np)
+{
+}
+
+void
+diskfs_new_hardrefs (struct node *np)
+{
+ allow_pager_softrefs (np);
+}
+
+error_t
+diskfs_truncate (struct node *np, off_t length)
+{
+ return EROFS;
+}
+
+error_t
+diskfs_grow (struct node *np, off_t end, struct protid *cred)
+{
+ return EROFS;
+}
+
+error_t
+diskfs_set_translator (struct node *np,
+ const char *name, u_int namelen,
+ struct protid *cred)
+{
+ return EROFS;
+}
+
+error_t
+diskfs_get_translator (struct node *np, char **namep, u_int *namelen)
+{
+ return EOPNOTSUPP;
+}
+
+void
+diskfs_shutdown_soft_ports ()
+{
+ /* Should initiate termination of internally held pager ports
+ (the only things that should be soft) XXX */
+}
+
+error_t
+diskfs_node_reload (struct node *node)
+{
+ /* Never necessary on a read-only medium */
+ return 0;
+}
+
+error_t
+diskfs_validate_author_change (struct node *np, uid_t author)
+{
+ return EROFS;
+}
+
+error_t
+diskfs_node_iterate (error_t (*fun)(struct node *))
+{
+ /* We never actually have to do anything, because this function
+ is only used for things that have to do with read-write media. */
+ return 0;
+}
+
+void
+diskfs_write_disknode (struct node *np, int wait)
+{
+}
+
+error_t
+diskfs_set_statfs (struct statfs *st)
+{
+ /* There is no easy way to determine the number of files on an
+ ISO 9660 filesystem. */
+ bzero (st, sizeof *st);
+ st->f_type = FSTYPE_ISO9660;
+ st->f_bsize = logical_block_size;
+ st->f_blocks = isonum_733 (sblock->vol_sp_size);
+ st->f_fsid = getpid ();
+ st->f_frsize = logical_block_size;
+ return 0;
+}
+
+error_t
+diskfs_S_file_get_storage_info (struct protid *cred,
+ mach_port_t **ports,
+ mach_msg_type_name_t *ports_type,
+ mach_msg_type_number_t *num_ports,
+ int **ints, mach_msg_type_number_t *num_ints,
+ off_t **offsets,
+ mach_msg_type_number_t *num_offsets,
+ char **data, mach_msg_type_number_t *data_len)
+{
+ /* XXX */
+ return EOPNOTSUPP;
+}
+
+void
+diskfs_free_node (struct node *no, mode_t mode)
+{
+ abort ();
+}
+
+error_t
+diskfs_alloc_node (struct node *dp, mode_t mode, struct node **np)
+{
+ return EROFS;
+}
diff --git a/isofs/iso9660.h b/isofs/iso9660.h
new file mode 100644
index 00000000..2fd8cc2b
--- /dev/null
+++ b/isofs/iso9660.h
@@ -0,0 +1,125 @@
+/*
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* Specification of ISO 9660 format */
+
+/* Volume descriptor */
+
+struct voldesc
+{
+ unsigned char type;
+ unsigned char id[5];
+ unsigned char version;
+ unsigned char data[0];
+};
+
+/* Volume descriptor types */
+#define VOLDESC_PRIMARY 1
+#define VOLDESC_END 255
+
+/* We don't support any other types */
+
+/* Expected ID */
+#define ISO_STANDARD_ID "CD001"
+
+/* Primary descriptor */
+struct sblock
+{
+ unsigned char type;
+ unsigned char id[5];
+ unsigned char version;
+ unsigned char skip1;
+ unsigned char sysid[32];
+ unsigned char volid[32];
+ unsigned char skip2[8];
+ unsigned char vol_sp_size[8]; /* total number of logical blocks */
+ unsigned char skip[32];
+ unsigned char vol_set_size[4];
+ unsigned char vol_seqno[4];
+ unsigned char blksize[4]; /* logical block size */
+ unsigned char ptsize[8];
+ unsigned char type_l_pt[4];
+ unsigned char opt_type_l_pt[4];
+ unsigned char type_m_pt[4];
+ unsigned char opt_type_m_pt[4];
+ unsigned char root[34];
+ unsigned char volset_id[128];
+ unsigned char pub_id[128];
+ unsigned char prep_id[128];
+ unsigned char app_id[128];
+ unsigned char copyr_id[37];
+ unsigned char abstr_id[37];
+ unsigned char biblio_id[37];
+ unsigned char creation_time[17];
+ unsigned char mod_time[17];
+ unsigned char expir_time[17];
+ unsigned char effect_time[17];
+ unsigned char file_structure;
+ unsigned char skip4;
+ unsigned char appl_data[512];
+ unsigned char skip5[652];
+};
+
+/* Directory record */
+struct dirrect
+{
+ unsigned char len;
+ unsigned char ext_attr_len;
+ unsigned char extent[8];
+ unsigned char size[8];
+ unsigned char date[7];
+ unsigned char flags;
+ unsigned char file_unit_size;
+ unsigned char ileave;
+ unsigned char vol_seqno[4];
+ unsigned char namelen;
+ unsigned char name[0];
+};
+
+
+
+/* Numeric conversions for these fields */
+
+#include <endian.h>
+
+static inline unsigned int
+isonum_733 (unsigned char *addr)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return *(unsigned int *)addr;
+#elif BYTE_ORDER == BIG_ENDIAN
+ return *(unsigned int *)(addr + 4);
+#else
+ return
+ addr[0] | (addr[1] << 8) | (addr[2] << 16) | (addr[3] << 24);
+#endif
+}
+
+static inline unsigned int
+isonum_723 (unsigned char *addr)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return *(unsigned short *)addr;
+#elif BYTE_ORDER == BIG_ENDIAN
+ return *(unsigned short *)addr + 2;
+#else
+ return addr[0] | (addr[1] << 8);
+#endif
+}
diff --git a/isofs/isofs.h b/isofs/isofs.h
new file mode 100644
index 00000000..68a94e93
--- /dev/null
+++ b/isofs/isofs.h
@@ -0,0 +1,94 @@
+/*
+ Copyright (C) 1997, 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <hurd/diskfs.h>
+#include <hurd/diskfs-pager.h>
+#include <hurd/store.h>
+
+#include "rr.h"
+
+/* There is no such thing as an inode in this format, all such informatio n
+ being recorded in the directory entry. So we report inode numbers as
+ absolute offsets from DISK_IMAGE. */
+
+struct disknode
+{
+ struct dirrect *dr; /* Somewhere in disk_image. */
+
+ off_t file_start; /* In store->block_size units */
+
+ struct user_pager_info *fileinfo;
+
+ char *link_target; /* for S_ISLNK */
+
+ size_t translen;
+ char *translator;
+};
+
+struct user_pager_info
+{
+ struct node *np;
+ enum pager_type
+ {
+ DISK,
+ FILE_DATA,
+ } type;
+ struct pager *p;
+};
+
+/* The physical media */
+extern struct store *store;
+
+char *host_name;
+
+/* Name we are mounted on, with trailing slash */
+char *mounted_on;
+
+/* Mapped image of disk */
+void *disk_image;
+
+/* Processed sblock info */
+
+/* Block size of pointers etc. on disk (6.2.2). */
+size_t logical_block_size;
+
+/* Size of "logical sectors" (6.1.2). These are 2048 or the
+ largest power of two that will fit in a physical sector, whichever is
+ greater. I don't know how to fetch the physical sector size; so
+ we'll just use a constant. */
+#define logical_sector_size 2048
+
+/* Unprocessed superblock */
+struct sblock *sblock;
+
+
+
+void drop_pager_softrefs (struct node *);
+void allow_pager_softrefs (struct node *);
+void create_disk_pager (void);
+
+error_t load_inode (struct node **, struct dirrect *, struct rrip_lookup *);
+error_t calculate_file_start (struct dirrect *, off_t *, struct rrip_lookup *);
+
+char *isodate_915 (char *, struct timespec *);
+char *isodate_84261 (char *, struct timespec *);
diff --git a/isofs/lookup.c b/isofs/lookup.c
new file mode 100644
index 00000000..b5b814db
--- /dev/null
+++ b/isofs/lookup.c
@@ -0,0 +1,457 @@
+/*
+ Copyright (C) 1997,98,99,2001,02 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include "isofs.h"
+
+/* From inode.c */
+int use_file_start_id (struct dirrect *record, struct rrip_lookup *rr);
+
+/* Forward */
+static error_t dirscanblock (void *, const char *, size_t,
+ struct dirrect **, struct rrip_lookup *);
+
+static int
+isonamematch (const char *dirname, size_t dnamelen,
+ const char *username, size_t unamelen)
+{
+ /* Special representations for `.' and `..' */
+ if (dnamelen == 1 && dirname[0] == '\0')
+ return unamelen == 1 && username[0] == '.';
+
+ if (dnamelen == 1 && dirname[0] == '\1')
+ return unamelen == 2 && username[0] == '.' && username[1] == '.';
+
+ if (unamelen > dnamelen)
+ return 0;
+
+ if (!strncasecmp (dirname, username, unamelen))
+ {
+ /* A prefix has matched. Check if it's acceptable. */
+ if (dnamelen == unamelen)
+ return 1;
+
+ /* User has omitted the version number */
+ if (dirname[unamelen] == ';')
+ return 1;
+
+ /* User has omitted an empty extension */
+ if (dirname[unamelen] == '.'
+ && (dirname[unamelen+1] == '\0' || dirname[unamelen+1] == ';'))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Implement the diskfs_lookup callback from the diskfs library. See
+ <hurd/diskfs.h> for the interface specification. */
+error_t
+diskfs_lookup_hard (struct node *dp, const char *name, enum lookup_type type,
+ struct node **npp, struct dirstat *ds, struct protid *cred)
+{
+ error_t err = 0;
+ struct dirrect *record;
+ int namelen;
+ int spec_dotdot;
+ void *buf;
+ void *blockaddr;
+ struct rrip_lookup rr;
+
+ if ((type == REMOVE) || (type == RENAME))
+ assert (npp);
+
+ if (npp)
+ *npp = 0;
+
+ spec_dotdot = type & SPEC_DOTDOT;
+ type &= ~SPEC_DOTDOT;
+
+ namelen = strlen (name);
+
+ /* This error is constant, but the errors for CREATE and REMOVE depend
+ on whether NAME exists. */
+ if (type == RENAME)
+ return EROFS;
+
+ buf = disk_image + (dp->dn->file_start << store->log2_block_size);
+
+ for (blockaddr = buf;
+ blockaddr < buf + dp->dn_stat.st_size;
+ blockaddr += logical_sector_size)
+ {
+ err = dirscanblock (blockaddr, name, namelen, &record, &rr);
+
+ if (!err)
+ break;
+
+ if (err != ENOENT)
+ return err;
+ }
+
+ if ((!err && type == REMOVE)
+ || (err == ENOENT && type == CREATE))
+ err = EROFS;
+
+ if (err)
+ return err;
+
+ /* Load the inode */
+ if (namelen == 2 && name[0] == '.' && name[1] == '.')
+ {
+ if (dp == diskfs_root_node)
+ err = EAGAIN;
+ else if (spec_dotdot)
+ {
+ /* renames and removes can't get this far. */
+ assert (type == LOOKUP);
+ diskfs_nput (dp);
+ err = load_inode (npp, record, &rr);
+ }
+ else
+ {
+ /* We don't have to do the normal rigamarole, because
+ we are permanently read-only, so things are necessarily
+ quiescent. Just be careful to honor the locking order. */
+ pthread_mutex_unlock (&dp->lock);
+ err = load_inode (npp, record, &rr);
+ pthread_mutex_lock (&dp->lock);
+ }
+ }
+ else if (namelen == 1 && name[0] == '.')
+ {
+ *npp = dp;
+ diskfs_nref (dp);
+ }
+ else
+ err = load_inode (npp, record, &rr);
+
+ release_rrip (&rr);
+ return err;
+}
+
+
+/* Scan one logical sector of directory contents (at address BLKADDR)
+ for NAME of length NAMELEN. Return its address in *RECORD. */
+static error_t
+dirscanblock (void *blkaddr, const char *name, size_t namelen,
+ struct dirrect **record, struct rrip_lookup *rr)
+{
+ struct dirrect *entry;
+ void *currentoff;
+ size_t reclen;
+ size_t entry_namelen;
+ int matchrr;
+ int matchnormal;
+
+ for (currentoff = blkaddr;
+ currentoff < blkaddr + logical_sector_size;
+ currentoff += reclen)
+ {
+ entry = (struct dirrect *) currentoff;
+
+ reclen = entry->len;
+
+ /* Validate reclen */
+ if (reclen == 0
+ || reclen < sizeof (struct dirrect)
+ || currentoff + reclen > blkaddr + logical_sector_size)
+ break;
+
+ entry_namelen = entry->namelen;
+
+ /* More validation */
+ if (reclen < sizeof (struct dirrect) + entry_namelen)
+ break;
+
+ /* Check to see if the name matches the directory entry. */
+ if (isonamematch ((const char *) entry->name, entry_namelen, name, namelen))
+ matchnormal = 1;
+ else
+ matchnormal = 0;
+
+ /* Now scan for RRIP fields */
+ matchrr = rrip_match_lookup (entry, name, namelen, rr);
+
+ /* Ignore RE entries */
+ if (rr->valid & VALID_RE)
+ {
+ release_rrip (rr);
+ continue;
+ }
+
+ /* See if the name matches */
+ if (((rr->valid & VALID_NM) && matchrr)
+ || (!(rr->valid & VALID_NM) && matchnormal))
+ {
+ /* We've got it. Return success */
+ *record = entry;
+ return 0;
+ }
+
+ release_rrip (rr);
+ }
+
+ /* Wasn't there. */
+ *record = 0;
+ return ENOENT;
+}
+
+error_t
+diskfs_get_directs (struct node *dp,
+ int entry,
+ int nentries,
+ char **data,
+ size_t *datacnt,
+ vm_size_t bufsiz,
+ int *amt)
+{
+ volatile vm_size_t allocsize;
+ struct dirrect *ep;
+ struct dirent *userp;
+ int i;
+ void *dirbuf, *bufp;
+ char *datap;
+ volatile int ouralloc = 0;
+ error_t err;
+
+ /* Allocate some space to hold the returned data. */
+ allocsize = bufsiz ? round_page (bufsiz) : vm_page_size * 4;
+ if (allocsize > *datacnt)
+ {
+ *data = mmap (0, allocsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ ouralloc = 1;
+ }
+
+ err = diskfs_catch_exception ();
+ if (err)
+ {
+ if (ouralloc)
+ munmap (*data, allocsize);
+ return err;
+ }
+
+ /* Skip to ENTRY */
+ dirbuf = disk_image + (dp->dn->file_start << store->log2_block_size);
+ bufp = dirbuf;
+ for (i = 0; i < entry; i ++)
+ {
+ struct rrip_lookup rr;
+
+ ep = (struct dirrect *) bufp;
+ rrip_lookup (ep, &rr, 0);
+
+ /* Ignore and skip RE entries */
+ if (rr.valid & VALID_RE)
+ i--;
+ else
+ {
+ if (bufp - dirbuf >= dp->dn_stat.st_size)
+ {
+ /* Not that many entries in the directory; return nothing. */
+ release_rrip (&rr);
+ if (allocsize > *datacnt)
+ munmap (data, allocsize);
+ *datacnt = 0;
+ *amt = 0;
+ return 0;
+ }
+ }
+ bufp = bufp + ep->len;
+ release_rrip (&rr);
+
+ /* If BUFP points at a null, then we have hit the last
+ record in this logical sector. In that case, skip up to
+ the next logical sector. */
+ if (*(char *)bufp == '\0')
+ bufp = (void *) (((long) bufp & ~(logical_sector_size - 1))
+ + logical_sector_size);
+ }
+
+ /* Now copy entries one at a time */
+ i = 0;
+ datap = *data;
+ while (((nentries == -1) || (i < nentries))
+ && (!bufsiz || datap - *data < bufsiz)
+ && ((void *) bufp - dirbuf < dp->dn_stat.st_size))
+ {
+ struct rrip_lookup rr;
+ const char *name;
+ size_t namlen, reclen;
+
+ ep = (struct dirrect *) bufp;
+
+ /* Fetch Rock-Ridge information for this file */
+ rrip_lookup (ep, &rr, 0);
+
+ /* Ignore and skip RE entries */
+ if (! (rr.valid & VALID_RE))
+ {
+ /* See if there's room to hold this one */
+ name = rr.valid & VALID_NM ? rr.name : (char *) ep->name;
+ namlen = rr.valid & VALID_NM ? strlen (name) : ep->namelen;
+
+ /* Name frobnication */
+ if (!(rr.valid & VALID_NM))
+ {
+ if (namlen == 1 && name[0] == '\0')
+ {
+ name = ".";
+ namlen = 1;
+ }
+ else if (namlen == 1 && name[0] == '\1')
+ {
+ name = "..";
+ namlen = 2;
+ }
+ /* Perhaps downcase it too? */
+ }
+
+ reclen = sizeof (struct dirent) + namlen;
+ reclen = (reclen + 3) & ~3;
+
+ /* Expand buffer if necessary */
+ if (datap - *data + reclen > allocsize)
+ {
+ vm_address_t newdata;
+
+ vm_allocate (mach_task_self (), &newdata,
+ (ouralloc
+ ? (allocsize *= 2)
+ : (allocsize = vm_page_size * 2)), 1);
+ bcopy ((void *) *data, (void *)newdata, datap - *data);
+
+ if (ouralloc)
+ munmap (*data, allocsize / 2);
+
+ datap = (char *) newdata + (datap - *data);
+ *data = (char *) newdata;
+ ouralloc = 1;
+ }
+
+ userp = (struct dirent *) datap;
+
+ /* Fill in entry */
+
+ if (use_file_start_id (ep, &rr))
+ {
+ off_t file_start;
+
+ err = calculate_file_start (ep, &file_start, &rr);
+ if (err)
+ {
+ release_rrip (&rr);
+ diskfs_end_catch_exception ();
+ if (ouralloc)
+ munmap (*data, allocsize);
+ return err;
+ }
+
+ userp->d_fileno = file_start << store->log2_block_size;
+ }
+ else
+ userp->d_fileno = (ino_t) ((void *) ep - (void *) disk_image);
+
+ userp->d_type = DT_UNKNOWN;
+ userp->d_reclen = reclen;
+ userp->d_namlen = namlen;
+ bcopy (name, userp->d_name, namlen);
+ userp->d_name[namlen] = '\0';
+
+ /* And move along */
+ datap = datap + reclen;
+ i++;
+ }
+
+ release_rrip (&rr);
+ bufp = bufp + ep->len;
+
+ /* If BUFP points at a null, then we have hit the last
+ record in this logical sector. In that case, skip up to
+ the next logical sector. */
+ if (*(char *)bufp == '\0')
+ bufp = (void *) (((long) bufp & ~(logical_sector_size - 1))
+ + logical_sector_size);
+ }
+
+ diskfs_end_catch_exception ();
+
+ /* If we didn't use all the pages of a buffer we allocated, free
+ the excess. */
+ if (ouralloc
+ && round_page (datap - *data) < round_page (allocsize))
+ munmap ((caddr_t) round_page (datap),
+ round_page (allocsize) - round_page (datap - *data));
+
+ /* Return */
+ *amt = i;
+ *datacnt = datap - *data;
+ return 0;
+}
+
+/* We have no dirstats at all. */
+const size_t diskfs_dirstat_size = 0;
+
+void
+diskfs_null_dirstat (struct dirstat *ds)
+{
+}
+
+error_t
+diskfs_drop_dirstat (struct node *dp, struct dirstat *ds)
+{
+ return 0;
+}
+
+/* These should never be called. */
+
+error_t
+diskfs_direnter_hard(struct node *dp,
+ const char *name,
+ struct node *np,
+ struct dirstat *ds,
+ struct protid *cred)
+{
+ abort ();
+}
+
+error_t
+diskfs_dirremove_hard(struct node *dp,
+ struct dirstat *ds)
+{
+ abort ();
+}
+
+error_t
+diskfs_dirrewrite_hard(struct node *dp,
+ struct node *np,
+ struct dirstat *ds)
+{
+ abort ();
+}
+
+int
+diskfs_dirempty(struct node *dp,
+ struct protid *cred)
+{
+ abort ();
+}
diff --git a/isofs/main.c b/isofs/main.c
new file mode 100644
index 00000000..5d002aff
--- /dev/null
+++ b/isofs/main.c
@@ -0,0 +1,170 @@
+/*
+ Copyright (C) 1997,98,99,2002 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+#include <error.h>
+#include <argp.h>
+#include <version.h>
+#include <limits.h>
+#include "isofs.h"
+
+struct node *diskfs_root_node;
+struct store *store = 0;
+struct store_parsed *store_parsed = 0;
+char *diskfs_disk_name = 0;
+
+char *diskfs_server_name = "iso9660fs";
+char *diskfs_server_version = HURD_VERSION;
+char *diskfs_extra_version = "GNU Hurd";
+int diskfs_synchronous = 0;
+
+int diskfs_link_max = INT_MAX;
+int diskfs_name_max = 255; /* see iso9660.h: struct dirrect::namelen */
+int diskfs_maxsymlinks = 8;
+
+
+/* Fetch the root node */
+static void
+fetch_root ()
+{
+ struct rrip_lookup rl;
+ struct dirrect *dr;
+ error_t err;
+
+ dr = (struct dirrect *) sblock->root;
+
+ /* First check for SUSP and all relevant extensions */
+ rrip_initialize (dr);
+
+ /* Now rescan the node for real */
+ rrip_lookup (dr, &rl, 1);
+
+ /* And fetch the node. */
+ err = load_inode (&diskfs_root_node, dr, &rl);
+ assert_perror (err);
+
+ pthread_mutex_unlock (&diskfs_root_node->lock);
+}
+
+
+/* Find and read the superblock. */
+static void
+read_sblock ()
+{
+ struct voldesc *vd;
+ error_t err;
+ struct sblock * volatile sb = 0;
+
+ err = diskfs_catch_exception ();
+ if (err)
+ error (4, err, "reading superblock");
+
+ /* Start at logical sector 16 and keep going until
+ we find a matching superblock */
+ for (vd = disk_image + (logical_sector_size * 16);
+ (void *) vd < disk_image + (logical_sector_size * 500); /* for sanity */
+ vd = (void *) vd + logical_sector_size)
+ {
+ if (vd->type == VOLDESC_END)
+ break;
+
+ if (vd->type == VOLDESC_PRIMARY
+ && !memcmp (ISO_STANDARD_ID, vd->id, 5)
+ && vd->version == 1)
+ {
+ /* Here's a valid primary descriptor. */
+ sb = (struct sblock *) vd;
+ break;
+ }
+ }
+
+ if (!sb)
+ error (1, 0, "Could not find valid superblock");
+
+ sblock = malloc (sizeof (struct sblock));
+ if (!sblock)
+ error (1, errno, "Could not allocate memory for superblock");
+ bcopy (sb, sblock, sizeof (struct sblock));
+ diskfs_end_catch_exception ();
+
+ /* Parse some important bits of this */
+ logical_block_size = isonum_723 (sblock->blksize);
+}
+
+/* Override the standard diskfs routine so we can add our own output. */
+error_t
+diskfs_append_args (char **argz, size_t *argz_len)
+{
+ error_t err;
+
+ /* Get the standard things. */
+ err = diskfs_append_std_options (argz, argz_len);
+
+ if (! err)
+ err = store_parsed_append_args (store_parsed, argz, argz_len);
+
+ return err;
+}
+
+int
+main (int argc, char **argv)
+{
+ mach_port_t bootstrap;
+
+ /* This filesystem is never capable of writing. */
+ diskfs_readonly = 1;
+ diskfs_hard_readonly = 1;
+
+ /* Initialize the diskfs library, parse arguments, and open the store.
+ This starts the first diskfs thread for us. */
+ store = diskfs_init_main (NULL, argc, argv, &store_parsed, &bootstrap);
+
+ create_disk_pager ();
+
+ read_sblock ();
+
+ fetch_root ();
+
+ diskfs_startup_diskfs (bootstrap, 0);
+
+ pthread_exit (NULL);
+
+ return 0;
+}
+
+/* Nothing to do for read-only medium */
+error_t
+diskfs_reload_global_state ()
+{
+ return 0;
+}
+
+error_t
+diskfs_set_hypermetadata (int wait, int clean)
+{
+ return 0;
+}
+
+void
+diskfs_readonly_changed (int readonly)
+{
+ /* We should never get here because we set diskfs_hard_readonly above. */
+ abort ();
+}
diff --git a/isofs/pager.c b/isofs/pager.c
new file mode 100644
index 00000000..d72a5144
--- /dev/null
+++ b/isofs/pager.c
@@ -0,0 +1,363 @@
+/*
+ Copyright (C) 1997, 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <error.h>
+#include <string.h>
+#include "isofs.h"
+
+pthread_spinlock_t node2pagelock = PTHREAD_SPINLOCK_INITIALIZER;
+
+struct port_bucket *pager_bucket;
+
+/* Mapped image of the disk */
+void *disk_image;
+
+
+/* Implement the pager_read_page callback from the pager library. See
+ <hurd/pager.h> for the interface definition. */
+error_t
+pager_read_page (struct user_pager_info *upi,
+ vm_offset_t page,
+ vm_address_t *buf,
+ int *writelock)
+{
+ error_t err;
+ daddr_t addr;
+ struct node *np = upi->np;
+ size_t read = 0;
+ size_t overrun = 0;
+
+ /* This is a read-only medium */
+ *writelock = 1;
+
+ if (upi->type == FILE_DATA)
+ {
+ addr = np->dn->file_start + (page >> store->log2_block_size);
+
+ if (page >= np->dn_stat.st_size)
+ {
+ *buf = (vm_address_t) mmap (0, vm_page_size, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ return 0;
+ }
+
+ if (page + vm_page_size > np->dn_stat.st_size)
+ overrun = page + vm_page_size - np->dn_stat.st_size;
+ }
+ else
+ {
+ assert (upi->type == DISK);
+ addr = page >> store->log2_block_size;
+ }
+
+ err = store_read (store, addr, vm_page_size, (void **) buf, &read);
+ if (err)
+ return err;
+
+ if (read != vm_page_size)
+ return EIO;
+
+ if (overrun)
+ bzero ((void *) *buf + vm_page_size - overrun, overrun);
+
+ return 0;
+}
+
+/* This function should never be called. */
+error_t
+pager_write_page (struct user_pager_info *pager,
+ vm_offset_t page,
+ vm_address_t buf)
+{
+ assert (0);
+}
+
+/* Never permit unlocks to succeed. */
+error_t
+pager_unlock_page (struct user_pager_info *pager,
+ vm_offset_t address)
+{
+ return EROFS;
+}
+
+void
+pager_notify_evict (struct user_pager_info *pager,
+ vm_offset_t page)
+{
+ assert (!"unrequested notification on eviction");
+}
+
+/* Tell how big the file is. */
+error_t
+pager_report_extent (struct user_pager_info *pager,
+ vm_address_t *offset,
+ vm_size_t *size)
+{
+ *offset = 0;
+ *size = pager->np->dn_stat.st_size;
+ return 0;
+}
+
+/* Implement the pager_clear_user_data callback from the pager library. */
+void
+pager_clear_user_data (struct user_pager_info *upi)
+{
+ if (upi->type == FILE_DATA)
+ {
+ pthread_spin_lock (&node2pagelock);
+ if (upi->np->dn->fileinfo == upi)
+ upi->np->dn->fileinfo = 0;
+ pthread_spin_unlock (&node2pagelock);
+ diskfs_nrele_light (upi->np);
+ }
+ free (upi);
+}
+
+void
+pager_dropweak (struct user_pager_info *upi)
+{
+}
+
+
+/* Create the disk pager */
+void
+create_disk_pager (void)
+{
+ struct user_pager_info *upi = malloc (sizeof (struct user_pager_info));
+
+ if (!upi)
+ error (1, errno, "Could not create disk pager");
+ upi->type = DISK;
+ upi->np = 0;
+ pager_bucket = ports_create_bucket ();
+ diskfs_start_disk_pager (upi, pager_bucket, 1, 0, store->size, &disk_image);
+ upi->p = diskfs_disk_pager;
+}
+
+/* This need not do anything */
+void
+diskfs_file_update (struct node *np,
+ int wait)
+{
+}
+
+/* Create a FILE_DATA pager for the specified node */
+mach_port_t
+diskfs_get_filemap (struct node *np, vm_prot_t prot)
+{
+ struct user_pager_info *upi;
+ mach_port_t right;
+
+ assert (S_ISDIR (np->dn_stat.st_mode)
+ || S_ISREG (np->dn_stat.st_mode)
+ || S_ISLNK (np->dn_stat.st_mode));
+
+ pthread_spin_lock (&node2pagelock);
+
+ do
+ if (!np->dn->fileinfo)
+ {
+ upi = malloc (sizeof (struct user_pager_info));
+ upi->type = FILE_DATA;
+ upi->np = np;
+ diskfs_nref_light (np);
+ upi->p = pager_create (upi, pager_bucket, 1,
+ MEMORY_OBJECT_COPY_DELAY, 0);
+ if (upi->p == 0)
+ {
+ diskfs_nrele_light (np);
+ free (upi);
+ pthread_spin_unlock (&node2pagelock);
+ return MACH_PORT_NULL;
+ }
+ np->dn->fileinfo = upi;
+ right = pager_get_port (np->dn->fileinfo->p);
+ ports_port_deref (np->dn->fileinfo->p);
+ }
+ else
+ {
+ /* Because NP->dn->fileinfo->p is not a real reference,
+ this might be nearly deallocated. If that's so, then
+ the port right will be null. In that case, clear here
+ and loop. The deallocation will complete separately. */
+ right = pager_get_port (np->dn->fileinfo->p);
+ if (right == MACH_PORT_NULL)
+ np->dn->fileinfo = 0;
+ }
+ while (right == MACH_PORT_NULL);
+
+ pthread_spin_unlock (&node2pagelock);
+
+ mach_port_insert_right (mach_task_self (), right, right,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ return right;
+}
+
+/* Call this when we should turn off caching so that unused memory
+ object ports get freed. */
+void
+drop_pager_softrefs (struct node *np)
+{
+ struct user_pager_info *upi;
+
+ pthread_spin_lock (&node2pagelock);
+ upi = np->dn->fileinfo;
+ if (upi)
+ ports_port_ref (upi->p);
+ pthread_spin_unlock (&node2pagelock);
+
+ if (upi)
+ {
+ pager_change_attributes (upi->p, 0, MEMORY_OBJECT_COPY_DELAY, 0);
+ ports_port_deref (upi->p);
+ }
+}
+
+/* Call this when we should turn on caching because it's no longer
+ important for unused memory object ports to get freed. */
+void
+allow_pager_softrefs (struct node *np)
+{
+ struct user_pager_info *upi;
+
+ pthread_spin_lock (&node2pagelock);
+ upi = np->dn->fileinfo;
+ if (upi)
+ ports_port_ref (upi->p);
+ pthread_spin_unlock (&node2pagelock);
+
+ if (upi)
+ {
+ pager_change_attributes (upi->p, 1, MEMORY_OBJECT_COPY_DELAY, 0);
+ ports_port_deref (upi->p);
+ }
+}
+
+
+static void
+block_caching ()
+{
+ error_t block_cache (void *arg)
+ {
+ struct pager *p = arg;
+
+ pager_change_attributes (p, 0, MEMORY_OBJECT_COPY_DELAY, 1);
+ return 0;
+ }
+
+ /* Loop through the pagers and turn off caching one by one,
+ synchronously. That should cause termination of each pager. */
+ ports_bucket_iterate (pager_bucket, block_cache);
+}
+
+static void
+enable_caching ()
+{
+ error_t enable_cache (void *arg)
+ {
+ struct pager *p = arg;
+ struct user_pager_info *upi = pager_get_upi (p);
+
+ pager_change_attributes (p, 1, MEMORY_OBJECT_COPY_DELAY, 0);
+
+ /* It's possible that we didn't have caching on before, because
+ the user here is the only reference to the underlying node
+ (actually, that's quite likely inside this particular
+ routine), and if that node has no links. So dinkle the node
+ ref counting scheme here, which will cause caching to be
+ turned off, if that's really necessary. */
+ if (upi->type == FILE_DATA)
+ {
+ diskfs_nref (upi->np);
+ diskfs_nrele (upi->np);
+ }
+
+ return 0;
+ }
+
+ ports_bucket_iterate (pager_bucket, enable_cache);
+}
+
+
+/* Tell diskfs if there are pagers exported, and if none, then
+ prevent any new ones from showing up. */
+int
+diskfs_pager_users ()
+{
+ int npagers = ports_count_bucket (pager_bucket);
+
+ if (npagers <= 1)
+ return 0;
+
+ block_caching ();
+
+ /* Give it a second; the kernel doesn't actually shutdown
+ immediately. XXX */
+ sleep (1);
+
+ npagers = ports_count_bucket (pager_bucket);
+ if (npagers <= 1)
+ return 0;
+
+ /* Darn, there are actual honest users. Turn caching back on,
+ and return failure. */
+ enable_caching ();
+
+ ports_enable_bucket (pager_bucket);
+
+ return 1;
+}
+
+/* Return the bitwise or of the maximum prot parameter (the second arg to
+ diskfs_get_filemap) for all active user pagers. */
+vm_prot_t
+diskfs_max_user_pager_prot ()
+{
+ /* We never allow writing, so there's no need to carefully check it. */
+ return VM_PROT_READ | VM_PROT_EXECUTE;
+}
+
+/* Call this to find out the struct pager * corresponding to the
+ FILE_DATA pager of inode IP. This should be used *only* as a subsequent
+ argument to register_memory_fault_area, and will be deleted when
+ the kernel interface is fixed. NP must be locked. */
+struct pager *
+diskfs_get_filemap_pager_struct (struct node *np)
+{
+ /* This is safe because fileinfo can't be cleared; there must be
+ an active mapping for this to be called. */
+ return np->dn->fileinfo->p;
+}
+
+/* Shutdown all the pagers. */
+void
+diskfs_shutdown_pager ()
+{
+ /* Because there's no need to ever sync, we don't have to do anything
+ here. */
+}
+
+/* Sync all the pagers. */
+void
+diskfs_sync_everything (int wait)
+{
+ /* ditto */
+}
diff --git a/isofs/rr.c b/isofs/rr.c
new file mode 100644
index 00000000..be4395d6
--- /dev/null
+++ b/isofs/rr.c
@@ -0,0 +1,649 @@
+/*
+ Copyright (C) 1997,99,2002 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* Parse Rock-Ridge and related SUSP conformant extensions. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include "isofs.h"
+
+/* These tell whether the specified extensions are on or not. */
+int susp_live = 0;
+int rock_live = 0;
+int gnuext_live = 0;
+
+/* How far to skip when reading SUSP fields. */
+int susp_skip = 0;
+
+void
+release_rrip (struct rrip_lookup *rr)
+{
+ if ((rr->valid & VALID_NM) && rr->name)
+ free (rr->name);
+ if ((rr->valid & VALID_SL) && rr->target)
+ free (rr->target);
+ if ((rr->valid & VALID_TR) && rr->trans)
+ free (rr->trans);
+}
+
+
+/* Work function combining the three interfaces below. */
+static int
+rrip_work (struct dirrect *dr, struct rrip_lookup *rr,
+ const char *match_name, size_t match_name_len,
+ int initializing, int ignorenm)
+{
+ void *bp, *terminus;
+ void *slbuf, *nmbuf;
+ size_t slbufsize, nmbufsize;
+ int nomorenm, nomoresl;
+
+ /* Initialize RR */
+ rr->valid = 0;
+ rr->target = rr->name = 0;
+
+ if (!susp_live && !initializing)
+ return 0;
+
+ /* The only extensions we currently support are rock-ridge, so
+ this test will cut a lot of useless work. */
+ if (!rock_live && !initializing)
+ return 0;
+
+ /* Initialized the name buffers */
+ nmbuf = slbuf = 0;
+ nmbufsize = slbufsize = 0;
+ nomorenm = nomoresl = 0;
+
+ /* Find the beginning and end of the SUSP area */
+
+ /* If this is the root node, from the root directory, then
+ we look in a special place. This only happens during the two
+ calls in fetch_root, and we know exactly what the value passed
+ is there. */
+
+ if (dr == (struct dirrect *)sblock->root)
+ {
+ struct dirrect *p;
+ off_t filestart;
+ char *c;
+ error_t err;
+
+ /* Look at the first directory entry in root. */
+ err = calculate_file_start (dr, &filestart, 0);
+ if (err)
+ return 0; /* give up */
+ p = disk_image + (filestart << store->log2_block_size);
+
+ /* Set C to the system use area. */
+ c = p->name + p->namelen;
+ if ((uintptr_t)c & 1)
+ c++;
+
+ /* There needs to be an SUSP SP field right here; make sure there is */
+ if (!bcmp (c, "SP\7\1\276\357", 6))
+ bp = c;
+ else if (!bcmp (c + 15, "SP\7\1\276\357", 6))
+ /* Detect CD-ROM XA disk */
+ bp = c + 15;
+ else
+ /* No SUSP, give up. */
+ return 0;
+
+ terminus = (char *) p + p->len;
+ }
+ else
+ {
+ /* It's in the normal place. */
+ bp = dr->name + dr->namelen;
+ if ((uintptr_t) bp & 1)
+ bp++; /* must be even */
+ bp += susp_skip; /* skip to start of susp area */
+ terminus = (char *) dr + dr->len;
+ }
+
+
+ /* Loop across all the fields, processing them one at a time. */
+
+ while (bp < terminus)
+ {
+ struct su_header *susp = bp;
+ void *body;
+
+ /* Make sure the whole thing fits */
+ if (bp + sizeof (struct su_header) > terminus
+ || bp + susp->len > terminus)
+ break;
+
+ body = (char *) susp + sizeof (struct su_header);
+
+ /* CE means that further extension fields are elsewhere on
+ the disk. We just reset the pointers and keep going. */
+ if (susp->sig[0] == 'C'
+ && susp->sig[1] == 'E'
+ && susp->version == 1)
+ {
+ int offset;
+ int location;
+ int size;
+ struct su_ce *ce = body;
+
+ offset = isonum_733 (ce->offset);
+ location = isonum_733 (ce->continuation);
+ size = isonum_733 (ce->size);
+
+ /* Reset pointers */
+ bp = disk_image + (location * logical_block_size) + offset;
+ terminus = bp + size;
+
+ /* NOT goto next_field */
+ continue;
+ }
+
+ /* Only on the root node; SP signals that the sharing protocol
+ is in use. */
+ if (initializing
+ && susp->sig[0] == 'S'
+ && susp->sig[1] == 'P'
+ && susp->version == 1)
+ {
+ /* Sharing Protocol */
+ struct su_sp *sp = body;
+
+ /* Verify magic numbers */
+ if (sp->check[0] == SU_SP_CHECK_0
+ && sp->check[1] == SU_SP_CHECK_1)
+ susp_live = 1;
+
+ susp_skip = sp->skip;
+
+ goto next_field;
+ }
+
+ /* Only on the root node; ER signals that a specified extension
+ is present. We implement and check for only the Rock Ridge
+ extension. */
+ if (initializing
+ && susp->sig[0] == 'E'
+ && susp->sig[1] == 'R'
+ && susp->version == 1)
+ {
+ /* Extension Reference */
+ struct su_er *er = body;
+
+ /* Make sure the ER field is valid */
+ if ((void *) er->more + er->len_id + er->len_des + er->len_src
+ < terminus)
+ goto next_field;
+
+ /* Check for rock-ridge */
+ if (er->ext_ver == ROCK_VERS
+ && !memcmp (ROCK_ID, er->more, er->len_id))
+ rock_live = 1;
+
+ /* Check for Gnuext */
+ else if (er->ext_ver == GNUEXT_VERS
+ && !memcmp (GNUEXT_ID, er->more, er->len_id))
+ gnuext_live = 1;
+ }
+
+ /* PD fields are padding and just get ignored. */
+ if (susp->sig[0] == 'P'
+ && susp->sig[1] == 'D'
+ && susp->version == 1)
+ goto next_field;
+
+ /* ST fields mean that there are no more SUSP fields to be processed. */
+ if (susp->sig[0] == 'S'
+ && susp->sig[1] == 'T'
+ && susp->version == 1)
+ /* All done */
+ break;
+
+ /* The rest are Rock-Ridge, and are not interesting if we are doing
+ setup. */
+
+ if (initializing || !rock_live)
+ goto next_field;
+
+ /* RE is present in a directory entry to mean that the node
+ is specified by a CL field elsewhere. So this entry needs
+ to be ignored by anyone who understands CL fields. */
+ if (susp->sig[0] == 'R'
+ && susp->sig[1] == 'E'
+ && susp->version == 1)
+ {
+ rr->valid |= VALID_RE;
+
+ /* No point in parsing anything else now. */
+ break;
+ }
+
+ /* NM identifies the real name of the file; it overrides
+ the name in the directory. */
+ if (susp->sig[0] == 'N'
+ && susp->sig[1] == 'M'
+ && susp->version == 1
+ && !ignorenm)
+ {
+ struct rr_nm *nm = body;
+ size_t nmlen = susp->len - 5;
+ char *name;
+ size_t namelen;
+
+ if (nomorenm)
+ goto next_field;
+
+ if (nm->flags & NAME_DOT)
+ {
+ name = ".";
+ namelen = 1;
+ goto finalize_nm;
+ }
+ else if (nm->flags & NAME_DOTDOT)
+ {
+ name = "..";
+ namelen = 2;
+ goto finalize_nm;
+ }
+ else if (nm->flags & NAME_HOST)
+ {
+ name = host_name;
+ namelen = strlen (host_name);
+ goto finalize_nm;
+ }
+
+ /* Add this component to the list. */
+
+ /* We don't store a trailing null here, but we always leave
+ room for it. The null gets stored in the finalization
+ code below. */
+ if (!nmbuf)
+ nmbuf = malloc ((nmbufsize = nmlen) + 1);
+ else
+ nmbuf = realloc (nmbuf, (nmbufsize += nmlen) + 1);
+ assert (nmbuf);
+
+ bcopy (nm->name, nmbuf + nmbufsize - nmlen, nmlen);
+
+ if (nm->flags & NAME_CONTINUE)
+ goto next_field;
+
+ name = nmbuf;
+ namelen = nmbufsize;
+
+ finalize_nm:
+ nomorenm = 1;
+
+ /* Is this a failed match? */
+ if (match_name && (match_name_len != namelen
+ || memcmp (match_name, name, match_name_len)))
+ {
+ if (nmbuf)
+ free (nmbuf);
+ return 0;
+ }
+
+ /* Store the name */
+ rr->valid |= VALID_NM;
+ if (name != nmbuf)
+ {
+ rr->name = strdup (name);
+ assert (rr->name);
+ }
+ else
+ {
+ rr->name = name;
+ name[namelen] = '\0';
+ }
+
+ if (rr->valid & VALID_CL)
+ /* Finalize CL processing. */
+ goto clrecurse;
+
+ goto next_field;
+ }
+
+ /* PX gives mode, nlink, uid, and gid posix-style attributes. */
+ if (susp->sig[0] == 'P'
+ && susp->sig[1] == 'X'
+ && susp->version == 1)
+ {
+ struct rr_px *px = body;
+
+ rr->valid |= VALID_PX;
+
+ rr->mode = isonum_733 (px->mode);
+ rr->nlink = isonum_733 (px->nlink);
+ rr->uid = isonum_733 (px->uid);
+ rr->gid = isonum_733 (px->gid);
+
+ goto next_field;
+ }
+
+ /* PN, for S_ISCHR and S_ISDEV devices gives the magic numbers */
+ if (susp->sig[0] == 'P'
+ && susp->sig[1] == 'N'
+ && susp->version == 1)
+ {
+ struct rr_pn *pn = body;
+
+ rr->valid |= VALID_PN;
+ rr->rdev = makedev (isonum_733 (pn->high), isonum_733 (pn->low));
+
+ goto next_field;
+ }
+
+ /* SL tells, for a symlink, what the target of the link is */
+ if (susp->sig[0] == 'S'
+ && susp->sig[1] == 'L'
+ && susp->version == 1)
+ {
+ struct rr_sl *sl = body;
+ size_t crlen = susp->len - 5;
+ struct rr_sl_comp *comp;
+ void *cp;
+ size_t targalloced, targused;
+
+ void add_comp (char *cname, size_t cnamelen)
+ {
+ if (rr->target == 0)
+ {
+ rr->target = malloc (cnamelen * 2);
+ targused = 0;
+ targalloced = cnamelen * 2;
+ }
+ else while (targused + cnamelen > targalloced)
+ rr->target = realloc (rr->target, targalloced *= 2);
+ assert (rr->target);
+
+ bcopy (cname, rr->target + targused, cnamelen);
+ targused += cnamelen;
+ }
+
+ if (nomoresl)
+ goto next_field;
+
+ /* Append the component use fields to the records we are saving
+ up */
+
+ if (!slbuf)
+ slbuf = malloc (slbufsize = crlen);
+ else
+ slbuf = realloc (slbuf, slbufsize += crlen);
+ assert (slbuf);
+
+ bcopy (sl->data, slbuf + slbufsize - crlen, crlen);
+
+ if (sl->flags & 1)
+ /* We'll finish later. */
+ goto next_field;
+
+ /* Do the symlink translation */
+ for (cp = slbuf; cp < slbuf + slbufsize; cp += comp->len + 2)
+ {
+ comp = (struct rr_sl_comp *)cp;
+ nomoresl = 1;
+
+ /* Put in a slash after each component as we go,
+ unless it's a "continuation" component. */
+
+ if (comp->flags & NAME_DOT)
+ add_comp ("./", 2);
+ else if (comp->flags & NAME_DOTDOT)
+ add_comp ("../", 3);
+ else if (comp->flags & NAME_ROOT)
+ {
+ targused = 0;
+ add_comp ("/", 1);
+ }
+ else if (comp->flags & NAME_VOLROOT)
+ {
+ targused = 0;
+ add_comp (mounted_on, strlen (mounted_on));
+ }
+ else if (comp->flags & NAME_HOST)
+ {
+ add_comp (host_name, strlen (host_name));
+ add_comp ("/", 1);
+ }
+ else
+ {
+ add_comp (comp->name, comp->len);
+ if (!(comp->flags & NAME_CONTINUE))
+ add_comp ("/", 1);
+ }
+ }
+
+ /* And turn the final character, if it's a slash, into a null.
+ Otherwise, add a null. */
+ if (rr->target[targused - 1] == '/')
+ rr->target[targused - 1] = '\0';
+ else
+ add_comp ("", 1);
+
+ rr->valid |= VALID_SL;
+
+ free (slbuf);
+ goto next_field;
+ }
+
+ /* TF gives atime, mtime, ctime (and others we don't care about);
+ this overrides the time specified in the directory. */
+ if (susp->sig[0] == 'T'
+ && susp->sig[1] == 'F'
+ && susp->version == 1)
+ {
+ char *(*convert)(char *, struct timespec *);
+ struct rr_tf *tf = body;
+ char *c;
+
+ if (tf->flags & TF_LONG_FORM)
+ convert = isodate_84261;
+ else
+ convert = isodate_915;
+
+ rr->valid |= VALID_TF;
+ rr->tfflags = tf->flags;
+ c = tf->data;
+
+ if (rr->tfflags & TF_CREATION)
+ c = (*convert) (c, &rr->ctime);
+ if (rr->tfflags & TF_MODIFY)
+ c = (*convert) (c, &rr->mtime);
+ if (rr->tfflags & TF_ACCESS)
+ c = (*convert) (c, &rr->atime);
+
+ goto next_field;
+ }
+
+ /* CL means that this entry is a relocated directory. We ignore
+ the attributes in this directory entry (except for NM); they
+ are fetched from the "." entry of the directory itself. The
+ CL field identifies the location of the directory, overriding
+ the location given in the present directory. This directory
+ is listed somewhere else too (to keep the format ISO 9660 compliant),
+ but there's an RE entry on that one so that we ignore it. */
+ if (susp->sig[0] == 'C'
+ && susp->sig[1] == 'L'
+ && susp->version == 1)
+ {
+ struct rr_cl *cl = body;
+
+ rr->realdirent
+ = disk_image + (isonum_733 (cl->loc) * logical_block_size);
+ rr->valid |= VALID_CL;
+
+ if (rr->valid & VALID_NM)
+ {
+ /* We've gotten all we care about from this node.
+ Remember the NM name, and load all the contents
+ from the new location. */
+ char *savename;
+ struct dirrect *realdir;
+
+ clrecurse:
+ /* It might look like VALID_NM is alway set here, but if
+ we got here from the exit point of the function, then
+ VALID_NM is actually clear. */
+
+ /* Save these, because rrip_work will clear them. */
+ savename = (rr->valid & VALID_NM) ? rr->name : 0;
+ realdir = rr->realdirent;
+
+ rrip_work (realdir, rr, 0, 0, 0, 1);
+
+ rr->valid |= VALID_CL;
+ rr->realdirent = realdir;
+ if (savename)
+ {
+ rr->valid |= VALID_NM;
+ rr->name = savename;
+ }
+
+ /* If there's an NM field, then we must have matched
+ if we got here. */
+ return (rr->valid & VALID_NM) ? 1 : 0;
+ }
+
+ /* We must keep looking for an NM record. When we find one,
+ the NM code will goto the above piece of code. */
+ goto next_field;
+ }
+
+ /* PL is found in the ".." entry of a relocated directory.
+ The present directory entry points to the fictitious parent
+ (the one that holds the fictitious RE link here); the PL
+ field identifies the real parent (the one that has the CL
+ entry). */
+ if (susp->sig[0] == 'P'
+ && susp->sig[1] == 'L'
+ && susp->version == 1)
+ {
+ struct rr_pl *pl = body;
+
+ rr->realfilestart = (isonum_733 (pl->loc)
+ * (logical_block_size
+ >> store->log2_block_size));
+ rr->valid |= VALID_PL;
+ goto next_field;
+ }
+
+ /* The rest are GNU ext. */
+ if (!gnuext_live)
+ goto next_field;
+
+ /* Author */
+ if (susp->sig[0] == 'A'
+ && susp->sig[1] == 'U'
+ && susp->version == 1)
+ {
+ struct gn_au *au = body;
+
+ rr->author = isonum_733 (au->author);
+ rr->valid |= VALID_AU;
+
+ goto next_field;
+ }
+
+ if (susp->sig[0] == 'T'
+ && susp->sig[1] == 'R'
+ && susp->version == 1)
+ {
+ struct gn_tr *tr = body;
+
+ rr->translen = tr->len;
+ rr->trans = malloc (rr->translen);
+ assert (rr->trans);
+ memcpy (tr->data, rr->trans, rr->translen);
+ rr->valid |= VALID_TR;
+
+ goto next_field;
+ }
+
+ if (susp->sig[0] == 'M'
+ && susp->sig[1] == 'D'
+ && susp->version == 1)
+ {
+ struct gn_md *md = body;
+
+ rr->allmode = isonum_733 (md->mode);
+ rr->valid |= VALID_MD;
+
+ goto next_field;
+ }
+
+ if (susp->sig[0] == 'F'
+ && susp->sig[1] == 'L'
+ && susp->version == 1)
+ {
+ struct gn_fl *fl = body;
+
+ rr->flags = isonum_733 (fl->flags);
+ rr->valid |= VALID_FL;
+
+ goto next_field;
+ }
+
+ next_field:
+ bp = bp + susp->len;
+ }
+
+ if (rr->valid & VALID_CL)
+ goto clrecurse;
+
+ /* If we saw an NM field, then it matched; otherwise we
+ didn't see one. */
+ return rr->valid & VALID_NM ? 1 : 0;
+}
+
+/* Parse extensions for directory entry DR. If we encounter an NM
+ record, and it does not match NAME (length NAMELEN), then stop
+ immediately (but do note the NM file in RR->valid) and return zero.
+ If we encounter no NM record at all, then process all the fields
+ normally and return zero. If we encounter an NM field which matches
+ the provided name, then process all the fields and return 1. In any
+ case, fill RR with information corresponding to the fields we do
+ encounter. */
+int
+rrip_match_lookup (struct dirrect *dr, const char *name, size_t namelen,
+ struct rrip_lookup *rr)
+{
+ return rrip_work (dr, rr, name, namelen, 0, 0);
+}
+
+/* Parse extensions for dirrect DR and store the results in RR.
+ If IGNORENM, then do not bother with NM records. */
+void
+rrip_lookup (struct dirrect *dr, struct rrip_lookup *rr, int ignorenm)
+{
+ rrip_work (dr, rr, 0, 0, 0, ignorenm);
+}
+
+/* Scan extensions on dirrect DR looking for the tags that are supposed
+ to be on the root directory. */
+void
+rrip_initialize (struct dirrect *dr)
+{
+ struct rrip_lookup rr;
+ rrip_work (dr, &rr, 0, 0, 1, 1);
+ release_rrip (&rr);
+}
diff --git a/isofs/rr.h b/isofs/rr.h
new file mode 100644
index 00000000..ab80e4bd
--- /dev/null
+++ b/isofs/rr.h
@@ -0,0 +1,266 @@
+/*
+ Copyright (C) 1997, 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "iso9660.h"
+
+/* The results of an rrip_scan_lookup call are one of these */
+struct rrip_lookup
+{
+ /* PX */
+ mode_t mode;
+ nlink_t nlink;
+ uid_t uid;
+ gid_t gid;
+
+ /* PN */
+ dev_t rdev;
+
+ /* SL */
+ char *target;
+
+ /* NM */
+ char *name; /* name of this entry if changed (malloced) */
+
+ /* CL */
+ off_t newloc; /* relocated directory */
+
+ /* PL */
+ off_t parloc; /* parent of relocated directory */
+
+ /* TF */
+ int tfflags;
+ struct timespec atime, mtime, ctime; /* file times */
+
+ /* CL */
+ struct dirrect *realdirent; /* actual directory entry for attributes */
+
+ /* RL */
+ off_t realfilestart; /* override file start in dir entry */
+
+ /* AU */
+ uid_t author;
+
+ /* TR */
+ size_t translen;
+ char *trans;
+
+ /* MD */
+ mode_t allmode;
+
+ /* FL */
+ long flags;
+
+ int valid;
+};
+
+/* VALID in one of these is from the following bits */
+#define VALID_PX 0x0001
+#define VALID_PN 0x0002
+#define VALID_SL 0x0004
+#define VALID_NM 0x0008
+#define VALID_CL 0x0010
+#define VALID_PL 0x0020
+#define VALID_TF 0x0040
+#define VALID_RE 0x0080
+#define VALID_AU 0x0100
+#define VALID_TR 0x0200
+#define VALID_MD 0x0400
+#define VALID_FL 0x0800
+
+
+/* Definitions for System Use Sharing Protocol.
+ Version 1. Revision 1.10. Dated July 16, 1993. */
+
+/* A system use field begins with the following header */
+struct su_header
+{
+ char sig[2];
+ unsigned char len;
+ char version;
+};
+
+/* The body of a CE (Continuation Area) field */
+struct su_ce
+{
+ char continuation[8];
+ char offset[8];
+ char size[8];
+};
+
+/* The body of a SP (Sharing Protocol Indicator) field */
+struct su_sp
+{
+ unsigned char check[2];
+ u_char skip;
+};
+
+#define SU_SP_CHECK_0 0xbe
+#define SU_SP_CHECK_1 0xef
+
+/* The body of a ER (Extension Reference) field */
+struct su_er
+{
+ u_char len_id;
+ u_char len_des;
+ u_char len_src;
+ u_char ext_ver;
+ char more[0];
+};
+
+
+
+
+/* Definitions for Rock Ridge extensions.
+ Version 1. Revision 1.10. Dated July 13, 1993. */
+
+/* These are the ER values to indicate the presence of Rock-Ridge
+ extensions. */
+#define ROCK_VERS 1
+#define ROCK_ID "RRIP_1991A"
+#define ROCK_DES \
+ "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS"
+#define ROCK_SRC \
+ "ROCK RIDGE SPECIFICATION VERSION 1 REVISION 1.10 JULY 13 1993"
+
+/* The body of a PX (Posix Attributes) field. */
+struct rr_px
+{
+ char mode[8];
+ char nlink[8];
+ char uid[8];
+ char gid[8];
+};
+
+/* The body of a PN (Posix Device Node) field. */
+struct rr_pn
+{
+ char high[8];
+ char low[8];
+};
+
+/* The body of a SL (Symbolic Link) field. */
+struct rr_sl
+{
+ u_char flags;
+ char data[0];
+};
+
+/* Each component in the DATA is: */
+struct rr_sl_comp
+{
+ u_char flags;
+ u_char len;
+ char name[0];
+};
+
+/* The body of a NM (Alternate Name) field. */
+struct rr_nm
+{
+ u_char flags;
+ char name[0];
+};
+
+/* Flags for SL and NM components */
+#define NAME_CONTINUE 0x01
+#define NAME_DOT 0x02
+#define NAME_DOTDOT 0x04
+#define NAME_ROOT 0x08
+#define NAME_VOLROOT 0x10
+#define NAME_HOST 0x20
+
+/* The body of a CL (Child Directory Location) field. */
+struct rr_cl
+{
+ char loc[8];
+};
+
+/* The body of a PL (Parent Directory Location) field. */
+struct rr_pl
+{
+ char loc[8];
+};
+
+/* The body of a TF (Time Stamp) field. */
+struct rr_tf
+{
+ u_char flags;
+ char data[0];
+};
+
+/* Flags for a TF */
+#define TF_CREATION 0x01
+#define TF_MODIFY 0x02
+#define TF_ACCESS 0x04
+#define TF_ATTRIBUTES 0x08
+#define TF_BACKUP 0x10
+#define TF_EXPIRATION 0x20
+#define TF_EFFECTIVE 0x40
+#define TF_LONG_FORM 0x80
+
+
+/* The body of a SF (Sparse File) field. */
+struct rr_sf
+{
+ char size[8];
+};
+
+
+/* GNU extensions */
+
+#define GNUEXT_VERS 1
+#define GNUEXT_ID "GNUEXT_1997"
+#define GNUEXT_DES \
+ "The GNU Extensions provide support for special GNU filesystem features"
+#define GNUEXT_SRC \
+ "GNU Hurd source release 0.3 or later"
+
+/* AU -- author (version 1) */
+struct gn_au
+{
+ char author[8];
+};
+
+/* TR -- translator (version 1) */
+struct gn_tr
+{
+ u_char len;
+ char data[0];
+};
+
+/* MD -- full mode (version 1) */
+struct gn_md
+{
+ char mode[8];
+};
+
+/* FL -- flags (version 1) */
+struct gn_fl
+{
+ char flags[8];
+};
+
+
+/* Rock-Ridge related functions. */
+
+int rrip_match_lookup (struct dirrect *, const char *,
+ size_t, struct rrip_lookup *);
+void rrip_lookup (struct dirrect *, struct rrip_lookup *, int);
+void rrip_initialize (struct dirrect *);
+void release_rrip (struct rrip_lookup *);
diff --git a/libcons/Makefile b/libcons/Makefile
new file mode 100644
index 00000000..a0df9f63
--- /dev/null
+++ b/libcons/Makefile
@@ -0,0 +1,38 @@
+# Copyright (C) 1994,95,96,97,98,99,2000,01,02,2005,2010,2012 Free Software
+# Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libcons
+makemode := library
+
+libname = libcons
+SRCS= demuxer.c init-init.c init-loop.c opts-version.c extra-version.c \
+ dir-changed.c file-changed.c opts-std-startup.c cons-lookup.c \
+ cons-switch.c vcons-remove.c vcons-add.c vcons-open.c \
+ vcons-close.c vcons-destroy.c vcons-refresh.c vcons-scrollback.c \
+ vcons-input.c vcons-move-mouse.c vcons-event.c
+installhdrs = cons.h
+
+fs_notify-MIGSFLAGS = -imacros $(srcdir)/mutations.h
+MIGSTUBS = fs_notifyServer.o
+OBJS = $(sort $(SRCS:.c=.o) $(MIGSTUBS))
+
+HURDLIBS = ports
+LDLIBS += -lpthread
+
+MIGCOMSFLAGS = -prefix cons_
+
+include ../Makeconf
diff --git a/libcons/cons-lookup.c b/libcons/cons-lookup.c
new file mode 100644
index 00000000..d91cc3ce
--- /dev/null
+++ b/libcons/cons-lookup.c
@@ -0,0 +1,107 @@
+/* cons-lookup.c - Looking up virtual consoles.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+#include <malloc.h>
+#include <sys/mman.h>
+
+#include "cons.h"
+
+/* Lookup the virtual console entry with number ID in the console
+ CONS, and return it in R_VCONS_ENTRY. If CREATE is true, the
+ virtual console entry will be created if it doesn't exist yet. If
+ CREATE is true, and ID 0, the first free virtual console id is
+ used. CONS must be locked. */
+error_t
+cons_lookup (cons_t cons, int id, int create, vcons_list_t *r_vcons_entry)
+{
+ vcons_list_t previous_vcons_entry = 0;
+ vcons_list_t vcons_entry;
+
+ if (!id && !create)
+ return EINVAL;
+
+ if (id)
+ {
+ if (cons->vcons_list && cons->vcons_list->id <= id)
+ {
+ previous_vcons_entry = cons->vcons_list;
+ while (previous_vcons_entry->next
+ && previous_vcons_entry->next->id <= id)
+ previous_vcons_entry = previous_vcons_entry->next;
+ if (previous_vcons_entry->id == id)
+ {
+ *r_vcons_entry = previous_vcons_entry;
+ return 0;
+ }
+ }
+ else if (!create)
+ return ESRCH;
+ }
+ else
+ {
+ id = 1;
+ if (cons->vcons_list && cons->vcons_list->id == 1)
+ {
+ previous_vcons_entry = cons->vcons_list;
+ while (previous_vcons_entry && previous_vcons_entry->id == id)
+ {
+ id++;
+ previous_vcons_entry = previous_vcons_entry->next;
+ }
+ }
+ }
+
+ vcons_entry = calloc (1, sizeof (struct vcons_list));
+ if (!vcons_entry)
+ return ENOMEM;
+
+ vcons_entry->id = id;
+ vcons_entry->vcons = NULL;
+
+ /* Insert the virtual console into the doubly linked list. */
+ if (previous_vcons_entry)
+ {
+ vcons_entry->prev = previous_vcons_entry;
+ if (previous_vcons_entry->next)
+ {
+ previous_vcons_entry->next->prev = vcons_entry;
+ vcons_entry->next = previous_vcons_entry->next;
+ }
+ else
+ cons->vcons_last = vcons_entry;
+ previous_vcons_entry->next = vcons_entry;
+ }
+ else
+ {
+ if (cons->vcons_list)
+ {
+ cons->vcons_list->prev = vcons_entry;
+ vcons_entry->next = cons->vcons_list;
+ }
+ else
+ cons->vcons_last = vcons_entry;
+ cons->vcons_list = vcons_entry;
+ }
+
+ cons_vcons_add (cons, vcons_entry);
+ *r_vcons_entry = vcons_entry;
+ return 0;
+}
diff --git a/libcons/cons-switch.c b/libcons/cons-switch.c
new file mode 100644
index 00000000..d8af50af
--- /dev/null
+++ b/libcons/cons-switch.c
@@ -0,0 +1,89 @@
+/* cons-switch.c - Switch to another virtual console.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+#include <assert.h>
+#include <pthread.h>
+
+#include "cons.h"
+
+/* Open the virtual console ID or the virtual console DELTA steps away
+ from VCONS in the linked list and return it in R_VCONS, which will
+ be locked. */
+error_t
+cons_switch (vcons_t vcons, int id, int delta, vcons_t *r_vcons)
+{
+ error_t err = 0;
+ cons_t cons = vcons->cons;
+ vcons_list_t vcons_entry = NULL;
+
+ if (!id && !delta)
+ return 0;
+
+ pthread_mutex_lock (&cons->lock);
+ if (id)
+ {
+ vcons_entry = cons->vcons_list;
+ while (vcons_entry && vcons_entry->id != id)
+ vcons_entry = vcons_entry->next;
+ }
+ else if (delta > 0)
+ {
+ vcons_entry = vcons->vcons_entry;
+ while (delta-- > 0)
+ {
+ vcons_entry = vcons_entry->next;
+ if (!vcons_entry)
+ vcons_entry = cons->vcons_list;
+ }
+ }
+ else
+ {
+ assert (delta < 0);
+ vcons_entry = vcons->vcons_entry;
+ while (delta++ < 0)
+ {
+ vcons_entry = vcons_entry->prev;
+ if (!vcons_entry)
+ vcons_entry = cons->vcons_last;
+ }
+ }
+
+ if (!vcons_entry)
+ {
+ pthread_mutex_unlock (&cons->lock);
+ return ESRCH;
+ }
+
+ if (vcons_entry->vcons)
+ {
+ *r_vcons = vcons_entry->vcons;
+ pthread_mutex_lock (&vcons_entry->vcons->lock);
+ }
+ else
+ {
+ err = cons_vcons_open (cons, vcons_entry, r_vcons);
+ if (!err)
+ vcons_entry->vcons = *r_vcons;
+ }
+
+ pthread_mutex_unlock (&cons->lock);
+ return err;
+}
diff --git a/libcons/cons.h b/libcons/cons.h
new file mode 100644
index 00000000..78675228
--- /dev/null
+++ b/libcons/cons.h
@@ -0,0 +1,331 @@
+/* cons.h - Definitions for cons helper and callback functions.
+ Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef _HURD_CONS_H
+#define _HURD_CONS_H
+
+#include <dirent.h>
+
+#include <hurd/ports.h>
+#include <mach.h>
+
+#include <hurd/console.h>
+
+typedef struct cons *cons_t;
+typedef struct vcons_list *vcons_list_t;
+typedef struct vcons *vcons_t;
+typedef struct cons_notify *cons_notify_t;
+
+struct vcons_list
+{
+ cons_t cons;
+ vcons_list_t next;
+ vcons_list_t prev;
+
+ /* The ID of the virtual console entry in the list. */
+ int id;
+
+ /* The opened vcons port on which we receive notifications. */
+ vcons_t vcons;
+};
+
+struct cons_notify
+{
+ struct port_info pi;
+
+ /* This is set for the dir notification port. */
+ cons_t cons;
+};
+
+struct vcons
+{
+ /* This must come first for the port info structure. */
+ struct cons_notify notify;
+
+ /* These elements are static from creation time. */
+ cons_t cons;
+ vcons_list_t vcons_entry;
+ int id;
+
+ /* The lock that protects all other members. */
+ pthread_mutex_t lock;
+
+ /* The FD of the input node. */
+ int input;
+
+ /* The shared memory of the display. */
+ struct cons_display *display;
+ size_t display_size;
+
+ struct
+ {
+ uint32_t flags;
+ struct
+ {
+ uint32_t col;
+ uint32_t row;
+ uint32_t status;
+ } cursor;
+ struct
+ {
+ uint32_t width;
+ uint32_t height;
+ uint32_t lines;
+ uint32_t cur_line;
+ uint32_t scr_lines;
+ conchar_t *matrix;
+ } screen;
+ struct
+ {
+ uint32_t audible;
+ uint32_t visible;
+ } bell;
+ struct
+ {
+ uint32_t written;
+ uint32_t length;
+ cons_change_t *buffer;
+ } changes;
+ } state;
+
+ uint32_t scrolling;
+};
+
+struct cons
+{
+ /* Protects the cons structure and the linked list in
+ VCONS_LIST. */
+ pthread_mutex_t lock;
+ vcons_list_t vcons_list;
+ vcons_list_t vcons_last;
+
+ struct port_class *port_class;
+ struct port_bucket *port_bucket;
+ DIR *dir;
+ io_t dirport;
+ int slack;
+};
+
+/* Determines if the mouse moves relatively, to an absolute location
+ or to an absolute location expressed by a percentage. */
+enum mouse_movement
+ {
+ CONS_VCONS_MOUSE_MOVE_REL,
+ CONS_VCONS_MOUSE_MOVE_ABS,
+ CONS_VCONS_MOUSE_MOVE_ABS_PERCENT
+ };
+
+/* The status of a mouse button. */
+enum mouse_button
+ {
+ CONS_VCONS_MOUSE_BUTTON_NO_OP,
+ CONS_VCONS_MOUSE_BUTTON_PRESSED,
+ CONS_VCONS_MOUSE_BUTTON_RELEASED
+ };
+
+/* An event produced by mouse movement an button presses. */
+typedef struct mouse_event
+{
+ enum mouse_movement mouse_movement;
+ float x;
+ float y;
+
+ enum mouse_button mouse_button;
+ int button;
+} *mouse_event_t;
+
+
+/* The user must define this variable. Set this to the name of the
+ console client. */
+extern const char *cons_client_name;
+
+/* The user must define this variable. Set this to be the client
+ version number. */
+extern const char *cons_client_version;
+
+/* The user may define this variable. Set this to be any additional
+ version specification that should be printed for --version. */
+extern char *cons_extra_version;
+
+/* The user must define this function. Deallocate the scarce
+ resources (like font glyph slots, colors etc) in the LENGTH entries
+ of the screen matrix starting from position COL and ROW. This call
+ is immediately followed by calls to cons_vcons_write that cover the
+ same area. If there are no scarce resources, the caller might do
+ nothing. */
+void cons_vcons_clear (vcons_t vcons, size_t length,
+ uint32_t col, uint32_t row);
+
+/* The user must define this function. Write LENGTH characters
+ starting from STR on the virtual console VCONS, which is locked,
+ starting from position COL and ROW. */
+void cons_vcons_write (vcons_t vcons, conchar_t *str, size_t length,
+ uint32_t col, uint32_t row);
+
+/* The user must define this function. Set the cursor on virtual
+ console VCONS, which is locked, to position COL and ROW. */
+void cons_vcons_set_cursor_pos (vcons_t vcons, uint32_t col, uint32_t row);
+
+/* The user must define this function. Set the cursor status of
+ virtual console VCONS, which is locked, to STATUS. */
+void cons_vcons_set_cursor_status (vcons_t vcons, uint32_t status);
+
+/* The user must define this function. Scroll the content of virtual
+ console VCONS, which is locked, up by DELTA if DELTA is positive or
+ down by -DELTA if DELTA is negative. DELTA will never be zero, and
+ the absolute value if DELTA will be smaller than or equal to the
+ height of the screen matrix.
+
+ This call will be immediately followed by corresponding
+ cons_vcons_write calls to fill the resulting gap on the screen, and
+ VCONS will be looked throughout the whole time. The purpose of the
+ function is two-fold: It is called with an absolute value of DELTA
+ smaller than the screen height to perform scrolling. It is called
+ with an absolute value of DELTA equal to the screen height to
+ prepare a full refresh of the screen. In the latter case the user
+ should not really perform any scrolling. Instead it might
+ deallocate limited resources (like display glyph slots and palette
+ colors) if that helps to perform the subsequent write, just like
+ cons_vcons_clear. It goes without saying that the same
+ deallocation, if any, should be performed on the area that will be
+ filled with the scrolled in content.
+
+ XXX Possibly need a function to invalidate scrollback buffer, or in
+ general to signal a switch of the console so state can be reset.
+ Only do this if we make guarantees about validity of scrollback
+ buffer, of course.
+
+ The driver is allowed to delay the effect of this operation until
+ the UPDATE function is called. */
+void cons_vcons_scroll (vcons_t vcons, int delta);
+
+/* The user may define this function. Make the changes from
+ cons_vcons_write, cons_vcons_set_cursor_pos,
+ cons_vcons_set_cursor_status and cons_vcons_scroll active. VCONS
+ is locked and will have been continuously locked from the first
+ change since the last update on. This is the latest possible point
+ the user must make the changes visible from. The user can always
+ make the changes visible at a more convenient, earlier time. */
+void cons_vcons_update (vcons_t vcons);
+
+/* The user must define this function. Make the virtual console
+ VCONS, which is locked, beep audibly. */
+void cons_vcons_beep (vcons_t vcons);
+
+/* The user must define this function. Make the virtual console
+ VCONS, which is locked, flash visibly. */
+void cons_vcons_flash (vcons_t vcons);
+
+/* The user must define this function. Notice the current status of
+ the scroll lock flag. */
+void cons_vcons_set_scroll_lock (vcons_t vcons, int onoff);
+
+/* The user must define this function. It is called whenever a
+ virtual console is selected to be the active one. It is the user's
+ responsibility to close the console at some later time. */
+error_t cons_vcons_activate (vcons_t vcons);
+
+/* The user may define this function. It is called after a
+ virtual console entry was added. CONS is locked. */
+void cons_vcons_add (cons_t cons, vcons_list_t vcons_entry);
+
+/* The user may define this function. It is called just before a
+ virtual console entry is removed. CONS is locked. */
+void cons_vcons_remove (cons_t cons, vcons_list_t vcons_entry);
+
+/* Open the virtual console ID or the virtual console DELTA steps away
+ from VCONS in the linked list and return it in R_VCONS, which will
+ be locked. */
+error_t cons_switch (vcons_t vcons, int id, int delta, vcons_t *r_vcons);
+
+/* Enter SIZE bytes from the buffer BUF into the virtual console
+ VCONS. */
+error_t cons_vcons_input (vcons_t vcons, char *buf, size_t size);
+
+/* The user must define this function. Clear the existing screen
+ matrix and set the size of the screen matrix to the dimension COL x
+ ROW. This call will be immediately followed by a call to
+ cons_vcons_write that covers the whole new screen matrix. */
+error_t cons_vcons_set_dimension (vcons_t vcons,
+ uint32_t col, uint32_t row);
+
+typedef enum
+ {
+ CONS_SCROLL_DELTA_LINES, CONS_SCROLL_DELTA_SCREENS,
+ CONS_SCROLL_ABSOLUTE_LINE, CONS_SCROLL_ABSOLUTE_PERCENTAGE
+ } cons_scroll_t;
+
+/* Scroll back into the history of VCONS. If TYPE is
+ CONS_SCROLL_DELTA_LINES, scroll up or down by VALUE lines. If TYPE
+ is CONS_SCROLL_DELTA_SCREENS, scroll up or down by VALUE multiples
+ of a screen height. If TYPE is CONS_SCROLL_ABSOLUTE_LINE, scroll to
+ line VALUE (where 0 is the lowest line). If TYPE is
+ CONS_SCROLL_ABSOLUTE_PERCENTAGE, scroll to the position determined
+ by VALUE, where 0 is the bottom and 1 is the top.
+
+ The function returns the number of lines actually scrolled up or
+ down. */
+int cons_vcons_scrollback (vcons_t vcons, cons_scroll_t type, float value);
+
+/* Set the mouse cursor position to X, Y. VCONS is locked. */
+error_t cons_vcons_set_mousecursor_pos (vcons_t vcons, float x, float y);
+
+/* If STATUS is set to 0, hide the mouse cursor, otherwise show
+ it. VCONS is locked. */
+error_t cons_vcons_set_mousecursor_status (vcons_t vcons, int status);
+
+
+
+extern const struct argp cons_startup_argp;
+
+extern struct port_bucket *cons_port_bucket;
+extern struct port_class *cons_port_class;
+
+/* The filename of the console server. */
+extern char *cons_file;
+
+error_t cons_init (void);
+void cons_server_loop (void);
+int cons_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp);
+
+/* Lookup the virtual console with number ID in the console CONS,
+ acquire a reference for it, and return its list entry in R_VCONS.
+ If CREATE is true, the virtual console will be created if it
+ doesn't exist yet. If CREATE is true, and ID 0, the first free
+ virtual console id is used. CONS must be locked. */
+error_t cons_lookup (cons_t cons, int id, int create, vcons_list_t *r_vcons);
+
+/* Open the virtual console for VCONS_ENTRY. CONS is locked. */
+error_t cons_vcons_open (cons_t cons, vcons_list_t vcons_entry,
+ vcons_t *r_vcons);
+
+/* Close the virtual console VCONS. VCONS->cons is locked. */
+void cons_vcons_close (vcons_t vcons);
+
+/* Destroy the virtual console VCONS. */
+void cons_vcons_destroy (void *port);
+
+/* Redraw the virtual console VCONS, which is locked. */
+void cons_vcons_refresh (vcons_t vcons);
+
+/* Handle the event EV on the virtual console VCONS. */
+error_t cons_vcons_move_mouse (vcons_t vcons, mouse_event_t ev);
+
+#endif /* hurd/cons.h */
diff --git a/libcons/demuxer.c b/libcons/demuxer.c
new file mode 100644
index 00000000..2db08905
--- /dev/null
+++ b/libcons/demuxer.c
@@ -0,0 +1,29 @@
+/* demuxer.c - Message demuxer for console client library.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "cons.h"
+
+int
+cons_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ int cons_fs_notify_server (mach_msg_header_t *inp, mach_msg_header_t *outp);
+
+ return (cons_fs_notify_server (inp, outp));
+}
diff --git a/libcons/dir-changed.c b/libcons/dir-changed.c
new file mode 100644
index 00000000..8498649c
--- /dev/null
+++ b/libcons/dir-changed.c
@@ -0,0 +1,129 @@
+/* dir-changed.c - Handling dir changed notifications.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+#include <dirent.h>
+#include <assert.h>
+#include <mach.h>
+#include <pthread.h>
+
+#include "cons.h"
+#include "fs_notify_S.h"
+
+
+static error_t
+add_one (cons_t cons, char *name)
+{
+ unsigned long int nr;
+ char *tail;
+
+ errno = 0;
+ nr = strtoul (name, &tail, 10);
+ if (!errno && *tail == '\0' && nr > 0)
+ {
+ vcons_list_t vcons_entry;
+ return cons_lookup (cons, nr, 1, &vcons_entry);
+ }
+ return 0;
+}
+
+static error_t
+lookup_one (cons_t cons, char *name, vcons_list_t *vcons_entry)
+{
+ unsigned long int nr;
+ char *tail;
+
+ errno = 0;
+ nr = strtoul (name, &tail, 10);
+ if (!errno && *tail == '\0' && nr > 0)
+ return cons_lookup (cons, nr, 0, vcons_entry);
+ return 0;
+}
+
+
+kern_return_t
+cons_S_dir_changed (cons_notify_t notify, natural_t tickno,
+ dir_changed_type_t change, string_t name)
+{
+ error_t err;
+ cons_t cons;
+
+ if (!notify || !notify->cons)
+ return EOPNOTSUPP;
+ cons = notify->cons;
+
+ pthread_mutex_lock (&cons->lock);
+
+ switch (change)
+ {
+ case DIR_CHANGED_NULL:
+ {
+ DIR *dir = cons->dir;
+ struct dirent *dent;
+ do
+ {
+ errno = 0;
+ dent = readdir (dir);
+ if (!dent && errno)
+ err = errno;
+ else if (dent)
+ err = add_one (cons, dent->d_name);
+ }
+ while (dent && !err);
+ if (err)
+ assert ("Unexpected error"); /* XXX */
+ }
+ break;
+ case DIR_CHANGED_NEW:
+ {
+ err = add_one (cons, name);
+ if (err)
+ assert ("Unexpected error"); /* XXX */
+ }
+ break;
+ case DIR_CHANGED_UNLINK:
+ {
+ vcons_list_t vcons_entry;
+ err = lookup_one (cons, name, &vcons_entry);
+ if (!err)
+ {
+ cons_vcons_remove (cons, vcons_entry);
+ if (vcons_entry->prev)
+ vcons_entry->prev->next = vcons_entry->next;
+ else
+ cons->vcons_list = vcons_entry->next;
+ if (vcons_entry->next)
+ vcons_entry->next->prev = vcons_entry->prev;
+ else
+ cons->vcons_last = vcons_entry->prev;
+
+ free (vcons_entry);
+ }
+ }
+ break;
+ case DIR_CHANGED_RENUMBER:
+ default:
+ assert ("Unexpected dir-changed type.");
+ pthread_mutex_unlock (&cons->lock);
+ return EINVAL;
+ }
+ pthread_mutex_unlock (&cons->lock);
+ return 0;
+}
diff --git a/libcons/extra-version.c b/libcons/extra-version.c
new file mode 100644
index 00000000..4ff54d85
--- /dev/null
+++ b/libcons/extra-version.c
@@ -0,0 +1,24 @@
+/* Default value for cons_extra_version
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include "priv.h"
+
+char *cons_extra_version = "";
diff --git a/libcons/file-changed.c b/libcons/file-changed.c
new file mode 100644
index 00000000..fa5cebd7
--- /dev/null
+++ b/libcons/file-changed.c
@@ -0,0 +1,367 @@
+/* file-changed.c - Handling file changed notifications.
+ Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+#include <assert.h>
+#include <pthread.h>
+
+#include <mach.h>
+
+#include "cons.h"
+#include "fs_notify_S.h"
+
+kern_return_t
+cons_S_file_changed (cons_notify_t notify, natural_t tickno,
+ file_changed_type_t change,
+ off_t start, off_t end)
+{
+ error_t err = 0;
+ vcons_t vcons = (vcons_t) notify;
+
+ if (!notify || notify->cons)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&vcons->lock);
+ switch (change)
+ {
+ case FILE_CHANGED_NULL:
+ /* Always sent first for sync. */
+ cons_vcons_refresh (vcons);
+ break;
+ case FILE_CHANGED_WRITE:
+ /* File data has been written. */
+ while (vcons->state.changes.written < vcons->display->changes.written)
+ {
+ cons_change_t change;
+
+ if (vcons->display->changes.written - vcons->state.changes.written
+ > vcons->cons->slack)
+ {
+ cons_vcons_refresh (vcons);
+ continue;
+ }
+ change = vcons->state.changes.buffer[vcons->state.changes.written
+ % vcons->state.changes.length];
+ if (vcons->display->changes.written - vcons->state.changes.written
+ > vcons->state.changes.length - 1)
+ {
+ /* While we were reading the entry, the server might
+ have overwritten it. */
+ cons_vcons_refresh (vcons);
+ continue;
+ }
+ vcons->state.changes.written++;
+
+ if (change.what.not_matrix)
+ {
+ if (change.what.cursor_pos)
+ {
+ uint32_t old_row = vcons->state.cursor.row;
+ uint32_t height = vcons->state.screen.height;
+ uint32_t row;
+
+ vcons->state.cursor.col = vcons->display->cursor.col;
+ row = vcons->state.cursor.row = vcons->display->cursor.row;
+
+ if (row + vcons->scrolling < height)
+ {
+ cons_vcons_set_cursor_pos (vcons,
+ vcons->state.cursor.col,
+ row + vcons->scrolling);
+ if (old_row + vcons->scrolling >= height)
+ /* The cursor was invisible before. */
+ cons_vcons_set_cursor_status (vcons,
+ vcons->state.cursor.status);
+ }
+ else if (old_row + vcons->scrolling < height)
+ /* The cursor was visible before. */
+ cons_vcons_set_cursor_status (vcons, CONS_CURSOR_INVISIBLE);
+
+ _cons_vcons_console_event (vcons, CONS_EVT_OUTPUT);
+ cons_vcons_update (vcons);
+ }
+ if (change.what.cursor_status)
+ {
+ vcons->state.cursor.status = vcons->display->cursor.status;
+ cons_vcons_set_cursor_status (vcons,
+ vcons->state.cursor.status);
+ cons_vcons_update (vcons);
+ }
+ if (change.what.screen_cur_line)
+ {
+ uint32_t new_cur_line;
+
+ new_cur_line = vcons->display->screen.cur_line;
+
+ if (new_cur_line != vcons->state.screen.cur_line)
+ {
+ off_t size = vcons->state.screen.width
+ * vcons->state.screen.lines;
+ off_t vis_start;
+ uint32_t scrolling;
+ off_t start;
+ off_t end;
+
+ if (new_cur_line > vcons->state.screen.cur_line)
+ scrolling = new_cur_line
+ - vcons->state.screen.cur_line;
+ else
+ scrolling = UINT32_MAX - vcons->state.screen.cur_line
+ + 1 + new_cur_line;
+
+ /* If we are scrolling back, defer scrolling
+ until absolutely necessary. */
+ if (vcons->scrolling)
+ {
+ if (_cons_jump_down_on_output)
+ _cons_vcons_scrollback
+ (vcons, CONS_SCROLL_ABSOLUTE_LINE, 0);
+ else
+ {
+ if (vcons->scrolling + scrolling
+ <= vcons->state.screen.scr_lines)
+ {
+ vcons->scrolling += scrolling;
+ scrolling = 0;
+ }
+ else
+ {
+ scrolling -= vcons->state.screen.scr_lines
+ - vcons->scrolling;
+ vcons->scrolling
+ = vcons->state.screen.scr_lines;
+ }
+ }
+ }
+
+ if (scrolling)
+ {
+ uint32_t cur_disp_line;
+
+ if (new_cur_line >= vcons->scrolling)
+ cur_disp_line = new_cur_line - vcons->scrolling;
+ else
+ cur_disp_line = (UINT32_MAX - (vcons->scrolling - new_cur_line)) + 1;
+
+ if (scrolling > vcons->state.screen.height)
+ scrolling = vcons->state.screen.height;
+ if (scrolling < vcons->state.screen.height)
+ cons_vcons_scroll (vcons, scrolling);
+ else
+ cons_vcons_clear (vcons, vcons->state.screen.width
+ * vcons->state.screen.height,
+ 0, 0);
+ vis_start = vcons->state.screen.width
+ * (cur_disp_line % vcons->state.screen.lines);
+ start = (((cur_disp_line % vcons->state.screen.lines)
+ + vcons->state.screen.height - scrolling)
+ * vcons->state.screen.width) % size;
+ end = start + scrolling * vcons->state.screen.width - 1;
+ cons_vcons_write (vcons,
+ vcons->state.screen.matrix + start,
+ end < size
+ ? end - start + 1
+ : size - start,
+ 0, vcons->state.screen.height
+ - scrolling);
+ if (end >= size)
+ cons_vcons_write (vcons,
+ vcons->state.screen.matrix,
+ end - size + 1,
+ 0, (size - vis_start)
+ / vcons->state.screen.width);
+ _cons_vcons_console_event (vcons, CONS_EVT_OUTPUT);
+ cons_vcons_update (vcons);
+ }
+ vcons->state.screen.cur_line = new_cur_line;
+ }
+ }
+ if (change.what.screen_scr_lines)
+ {
+ vcons->state.screen.scr_lines
+ = vcons->display->screen.scr_lines;
+ if (vcons->state.screen.scr_lines < vcons->scrolling)
+ assert (!"Implement shrinking scrollback buffer! XXX");
+ }
+ if (change.what.bell_audible)
+ {
+ while (vcons->state.bell.audible
+ < vcons->display->bell.audible)
+ {
+ if (_cons_audible_bell == BELL_AUDIBLE)
+ cons_vcons_beep (vcons);
+ else if (_cons_audible_bell == BELL_VISUAL)
+ cons_vcons_flash (vcons);
+ vcons->state.bell.audible++;
+ }
+ }
+ if (change.what.bell_visible)
+ {
+ while (vcons->state.bell.visible
+ < vcons->display->bell.visible)
+ {
+ if (_cons_visual_bell == BELL_VISUAL)
+ cons_vcons_flash (vcons);
+ else if (_cons_visual_bell == BELL_AUDIBLE)
+ cons_vcons_beep (vcons);
+ vcons->state.bell.visible++;
+ }
+ }
+ if (change.what.flags)
+ {
+ uint32_t flags = vcons->display->flags;
+
+ if ((flags & CONS_FLAGS_SCROLL_LOCK)
+ != (vcons->state.flags & CONS_FLAGS_SCROLL_LOCK))
+ cons_vcons_set_scroll_lock (vcons, flags
+ & CONS_FLAGS_SCROLL_LOCK);
+ vcons->state.flags = flags;
+ }
+ }
+ else
+ {
+ /* For clipping. */
+ off_t size = vcons->state.screen.width*vcons->state.screen.lines;
+ off_t rotate;
+ off_t vis_end = vcons->state.screen.height
+ * vcons->state.screen.width - 1;
+ off_t end2 = -1;
+ off_t start_rel = 0; /* start relative to visible start. */
+ off_t start = change.matrix.start;
+ off_t end = change.matrix.end;
+
+ if (vcons->scrolling && _cons_jump_down_on_output)
+ _cons_vcons_scrollback (vcons, CONS_SCROLL_ABSOLUTE_LINE, 0);
+
+ if (vcons->state.screen.cur_line >= vcons->scrolling)
+ rotate = vcons->state.screen.cur_line - vcons->scrolling;
+ else
+ rotate = (UINT32_MAX - (vcons->scrolling - vcons->state.screen.cur_line)) + 1;
+ rotate = vcons->state.screen.width * (rotate % vcons->state.screen.lines);
+
+ /* Rotate the buffer. */
+ start -= rotate;
+ if (start < 0)
+ start += size;
+ end -= rotate;
+ if (end < 0)
+ end += size;
+
+ /* Find the intersection. */
+ if (start > vis_end)
+ {
+ if (end < start)
+ {
+ start = 0;
+ if (vis_end < end)
+ end = vis_end;
+ }
+ else
+ start = -1;
+ }
+ else
+ {
+ if (end >= start)
+ {
+ if (end > vis_end)
+ end = vis_end;
+ }
+ else
+ {
+ end2 = end;
+ end = vis_end;
+ }
+ }
+ /* We now have three cases: No intersection if start ==
+ -1, one intersection [start;end] if end2 == -1, and
+ two intersections [start;end] and [0;end2] if end2 !=
+ -1. However, we still have to undo the buffer
+ rotation. */
+ if (start != -1)
+ {
+ start_rel = start;
+ start += rotate;
+ if (start >= size)
+ start -= size;
+ end += rotate;
+ if (end >= size)
+ end -= size;
+ if (start > end)
+ end += size;
+ }
+ if (end2 != -1)
+ /* The interval should be [vis_start:end2]. */
+ end2 += rotate;
+
+ if (start != -1)
+ {
+ cons_vcons_clear (vcons, end - start + 1,
+ start_rel % vcons->state.screen.width,
+ start_rel / vcons->state.screen.width);
+ cons_vcons_write (vcons, vcons->state.screen.matrix + start,
+ end < size
+ ? end - start + 1
+ : size - start,
+ start_rel % vcons->state.screen.width,
+ start_rel / vcons->state.screen.width);
+ if (end >= size)
+ cons_vcons_write (vcons, vcons->state.screen.matrix,
+ end - size + 1,
+ (size - rotate)
+ % vcons->state.screen.width,
+ (size - rotate)
+ / vcons->state.screen.width);
+ if (end2 != -1)
+ {
+ cons_vcons_clear (vcons, end2 - rotate + 1, 0, 0);
+ cons_vcons_write (vcons,
+ vcons->state.screen.matrix + rotate,
+ end2 < size
+ ? end2 - rotate + 1
+ : size - rotate,
+ 0, 0);
+ if (end2 >= size)
+ cons_vcons_write (vcons, vcons->state.screen.matrix,
+ end2 - size + 1,
+ (size - rotate)
+ % vcons->state.screen.width,
+ (size - rotate)
+ / vcons->state.screen.width);
+ }
+ _cons_vcons_console_event (vcons, CONS_EVT_OUTPUT);
+ cons_vcons_update (vcons);
+ }
+ }
+ }
+ break;
+ case FILE_CHANGED_EXTEND:
+ /* File has grown. */
+ case FILE_CHANGED_TRUNCATE:
+ /* File has been truncated. */
+ case FILE_CHANGED_META:
+ /* Stat information has changed, and none of the previous three
+ apply. Not sent for changes in node times. */
+ default:
+ err = EINVAL;
+ };
+
+ pthread_mutex_unlock (&vcons->lock);
+ return err;
+}
diff --git a/libcons/init-init.c b/libcons/init-init.c
new file mode 100644
index 00000000..ea3b37f7
--- /dev/null
+++ b/libcons/init-init.c
@@ -0,0 +1,98 @@
+/* init-init.c - Initialize the console library.
+ Copyright (C) 1995, 1996, 2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG and Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+#include <malloc.h>
+#include <pthread.h>
+
+#include <hurd.h>
+#include <hurd/ports.h>
+
+#include <mach.h>
+
+#include "cons.h"
+#include "priv.h"
+
+struct port_bucket *cons_port_bucket;
+struct port_class *cons_port_class;
+
+
+error_t
+cons_init (void)
+{
+ error_t err;
+ cons_t cons;
+ cons_notify_t dir_notify_port;
+ mach_port_t dir_notify;
+
+ cons_port_bucket = ports_create_bucket ();
+ if (!cons_port_bucket)
+ return errno;
+
+ cons_port_class = ports_create_class (cons_vcons_destroy, NULL);
+ if (!cons_port_class)
+ return errno;
+
+ /* Create the console structure. */
+ cons = malloc (sizeof (*cons));
+ if (!cons)
+ return errno;
+ pthread_mutex_init (&cons->lock, NULL);
+ cons->vcons_list = NULL;
+ cons->vcons_last = NULL;
+ cons->dir = opendir (cons_file);
+ cons->slack = _cons_slack;
+ if (!cons->dir)
+ {
+ free (cons);
+ return errno;
+ }
+ cons->dirport = getdport (dirfd (cons->dir));
+ if (cons->dirport == MACH_PORT_NULL)
+ {
+ closedir (cons->dir);
+ free (cons);
+ return errno;
+ }
+
+ /* Request directory notifications. */
+ err = ports_create_port (cons_port_class, cons_port_bucket,
+ sizeof (*dir_notify_port), &dir_notify_port);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), cons->dirport);
+ closedir (cons->dir);
+ free (cons);
+ return err;
+ }
+ dir_notify_port->cons = cons;
+
+ dir_notify = ports_get_right (dir_notify_port);
+ err = dir_notice_changes (cons->dirport, dir_notify,
+ MACH_MSG_TYPE_MAKE_SEND);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), cons->dirport);
+ closedir (cons->dir);
+ free (cons);
+ return err;
+ }
+ return 0;
+}
diff --git a/libcons/init-loop.c b/libcons/init-loop.c
new file mode 100644
index 00000000..987754df
--- /dev/null
+++ b/libcons/init-loop.c
@@ -0,0 +1,31 @@
+/* init-loop.c - Server loop for console client library.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <hurd/ports.h>
+
+#include "cons.h"
+
+void
+cons_server_loop (void)
+{
+ ports_manage_port_operations_one_thread (cons_port_bucket,
+ cons_demuxer, 0);
+ /* Not reached. */
+}
diff --git a/libcons/mutations.h b/libcons/mutations.h
new file mode 100644
index 00000000..c895447a
--- /dev/null
+++ b/libcons/mutations.h
@@ -0,0 +1,26 @@
+/* mutations.h - MIG mutations for the console client library.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* Only CPP macro definitions should go in this file. */
+
+#define FS_NOTIFY_INTRAN cons_notify_t begin_using_notify_port (fs_notify_t)
+#define FS_NOTIFY_DESTRUCTOR end_using_notify_port (cons_notify_t)
+
+#define FS_NOTIFY_IMPORTS import "priv.h";
diff --git a/libcons/opts-std-startup.c b/libcons/opts-std-startup.c
new file mode 100644
index 00000000..23bd9971
--- /dev/null
+++ b/libcons/opts-std-startup.c
@@ -0,0 +1,229 @@
+/* opts-std-startup.c - Standard startup-time command line parser.
+ Copyright (C) 1995,96,97,98,99,2001,02,2003,2004,2005 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org> and Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <argp.h>
+#include <string.h>
+
+#include "priv.h"
+
+
+/* Option keys for long-only options in argp_option. */
+#define OPT_SLACK 600 /* --slack */
+#define OPT_JUMP_DOWN_ON_INPUT 601 /* --jump-down-on-input */
+#define OPT_NO_JUMP_DOWN_ON_INPUT 602 /* --no-jump-down-on-input */
+#define OPT_JUMP_DOWN_ON_OUTPUT 603 /* --jump-down-on-output */
+#define OPT_NO_JUMP_DOWN_ON_OUTPUT 604 /* --no-jump-down-on-output */
+#define OPT_VISUAL_BELL 605 /* --visual-bell */
+#define OPT_AUDIBLE_BELL 606 /* --audible-bell */
+#define OPT_MOUSE_SHOW 607 /* --mouse-show-on */
+#define OPT_MOUSE_HIDE 608 /* --mouse-hide-on */
+#define OPT_MOUSE_SENS 609 /* --mouse-sensitivity */
+
+/* The number of records the client is allowed to lag behind the server. */
+#define DEFAULT_SLACK 100
+#define DEFAULT_SLACK_STRING STRINGIFY(DEFAULT_SLACK)
+#define STRINGIFY(x) STRINGIFY_1(x)
+#define STRINGIFY_1(x) #x
+
+/* The mouse sensitivity. */
+#define DEFAULT_MOUSE_SENS 3.0
+#define DEFAULT_MOUSE_SENS_STRING STRINGIFY(DEFAULT_MOUSE_SENS)
+
+/* Number of records the client is allowed to lag behind the
+ server. */
+int _cons_slack = DEFAULT_SLACK;
+
+/* If we jump down on input. */
+int _cons_jump_down_on_input = 1;
+
+/* If we jump down on output. */
+int _cons_jump_down_on_output;
+
+/* The filename of the console server. */
+char *cons_file;
+
+/* The type of bell used for the visual bell. */
+bell_type_t _cons_visual_bell = BELL_VISUAL;
+
+/* The type of bell used for the audible bell. */
+bell_type_t _cons_audible_bell = BELL_AUDIBLE;
+
+/* The type of events that will make the mouse cursor visible. */
+int _cons_show_mouse = CONS_EVT_MOUSE_MOVE;
+
+/* The type of events that will hide the mouse cursor. */
+int _cons_hide_mouse = CONS_EVT_KEYPRESS;
+
+/* The mouse sensitivity. */
+float _cons_mouse_sens = DEFAULT_MOUSE_SENS;
+
+static const struct argp_option
+startup_options[] =
+{
+ { "slack", OPT_SLACK, "RECORDS", 0, "Max number of records the client is"
+ " allowed to lag behind the server (default " DEFAULT_SLACK_STRING ")" },
+ { "jump-down-on-input", OPT_JUMP_DOWN_ON_INPUT, NULL, 0,
+ "End scrollback when something is entered (default)" },
+ { "no-jump-down-on-input", OPT_NO_JUMP_DOWN_ON_INPUT, NULL, 0,
+ "End scrollback when something is entered" },
+ { "jump-down-on-output", OPT_JUMP_DOWN_ON_OUTPUT, NULL, 0,
+ "End scrollback when something is printed" },
+ { "no-jump-down-on-output", OPT_NO_JUMP_DOWN_ON_OUTPUT, NULL, 0,
+ "End scrollback when something is printed (default)" },
+ { "visual-bell", OPT_VISUAL_BELL, "BELL", 0, "Visual bell: on (default), "
+ "off, visual, audible" },
+ { "audible-bell", OPT_AUDIBLE_BELL, "BELL", 0, "Audible bell: on (default), "
+ "off, visual, audible" },
+ { "mouse-show-on", OPT_MOUSE_SHOW, "EVENTS", 0, "One or more of the events"
+ " mousemove, mousebutton, keypress, output (default is mousemove), if one"
+ " of these events occur the mouse cursor will be made visible" },
+ { "mouse-hide-on", OPT_MOUSE_HIDE, "EVENTS", 0, "One or more of the events"
+ " mousemove, mousebutton, keypress, output (default is keypress), if one"
+ " of these events occur the mouse cursor will be hidden " },
+ { "mouse-sensitivity", OPT_MOUSE_SENS, "SENSITIVITY", 0, "The mouse"
+ " sensitivity (default " DEFAULT_MOUSE_SENS_STRING "). A lower value"
+ " means more sensitive" },
+ { 0, 0 }
+};
+
+static const char args_doc[] = "CONSOLE";
+static const char doc[] = "A console client.";
+
+
+static error_t
+parse_startup_opt (int opt, char *arg, struct argp_state *state)
+{
+ int parse_events (char *events)
+ {
+ char *evtstr = strdupa (events);
+ char *tok = strtok (evtstr, ",");
+ int evmask = 0;
+
+ while (tok)
+ {
+ if (!strcasecmp ("mousemove", tok))
+ evmask |= CONS_EVT_MOUSE_MOVE;
+ else if (!strcasecmp ("mousebutton", tok))
+ evmask |= CONS_EVT_MOUSE_BUTTON;
+ else if (!strcasecmp ("keypress", tok))
+ evmask |= CONS_EVT_KEYPRESS;
+ else if (!strcasecmp ("output", tok))
+ evmask |= CONS_EVT_OUTPUT;
+ else
+ argp_error (state, "The event can be one of: MOUSEMOVE,"
+ " MOUSEBUTTON, KEYPRESS or OUTPUT");
+ tok = strtok (NULL, ",");
+ }
+ return evmask;
+ }
+
+ switch (opt)
+ {
+ case OPT_SLACK:
+ _cons_slack = atoi (arg);
+ break;
+
+ case OPT_JUMP_DOWN_ON_INPUT:
+ _cons_jump_down_on_input = 1;
+ break;
+
+ case OPT_NO_JUMP_DOWN_ON_INPUT:
+ _cons_jump_down_on_input = 0;
+ break;
+
+ case OPT_JUMP_DOWN_ON_OUTPUT:
+ _cons_jump_down_on_output = 1;
+ break;
+
+ case OPT_NO_JUMP_DOWN_ON_OUTPUT:
+ _cons_jump_down_on_output = 0;
+ break;
+
+ case OPT_AUDIBLE_BELL:
+ if (!strcasecmp ("on", arg) || !strcasecmp ("audible", arg))
+ _cons_audible_bell = BELL_AUDIBLE;
+ else if (!strcasecmp ("off", arg))
+ _cons_audible_bell = BELL_OFF;
+ else if (!strcasecmp ("visual", arg))
+ _cons_audible_bell = BELL_VISUAL;
+ else
+ argp_error (state, "The audible bell can be one of: on, off, visual, "
+ "audible");
+ break;
+
+ case OPT_VISUAL_BELL:
+ if (!strcasecmp ("on", arg) || !strcasecmp ("visual", arg))
+ _cons_visual_bell = BELL_VISUAL;
+ else if (!strcasecmp ("off", arg))
+ _cons_visual_bell = BELL_OFF;
+ else if (!strcasecmp ("audible", arg))
+ _cons_visual_bell = BELL_AUDIBLE;
+ else
+ argp_error (state, "The visual bell can be one of: on, off, visual, "
+ "audible");
+ break;
+
+ case OPT_MOUSE_SHOW:
+ _cons_show_mouse = parse_events (arg);
+ break;
+
+ case OPT_MOUSE_HIDE:
+ _cons_hide_mouse = parse_events (arg);
+ break;
+
+ case OPT_MOUSE_SENS:
+ {
+ char *tail;
+
+ errno = 0;
+ _cons_mouse_sens = strtod (arg, &tail);
+ if (tail == NULL || tail == arg || *tail != '\0')
+ argp_error (state, "SENSITIVITY is not a number: %s", arg);
+ if (errno)
+ argp_error (state, "Overflow in argument SENSITIVITY %s", arg);
+ break;
+ }
+
+ case ARGP_KEY_ARG:
+ if (state->arg_num > 0)
+ /* Too many arguments. */
+ argp_error (state, "Too many non option arguments");
+ cons_file = arg;
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_error (state, "Filename of console server missing");
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+/* An argp structure for the standard console client command line
+ arguments. */
+const struct argp
+cons_startup_argp =
+{
+ startup_options, parse_startup_opt, args_doc, doc
+};
diff --git a/libcons/opts-version.c b/libcons/opts-version.c
new file mode 100644
index 00000000..f8751490
--- /dev/null
+++ b/libcons/opts-version.c
@@ -0,0 +1,44 @@
+/* opts-version.c - Default hook for argp --version handling
+ Copyright (C) 1996, 2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu> and Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <argp.h>
+#include <version.h>
+
+#include "priv.h"
+
+static void
+_print_version (FILE *stream, struct argp_state *state)
+{
+ if (argp_program_version)
+ /* If this is non-zero, then the program's probably defined it, so let
+ that take precedence over the default. */
+ fputs (argp_program_version, stream);
+ else if (cons_extra_version && *cons_extra_version)
+ fprintf (stream, "%s (%s) %s\n",
+ cons_client_name, cons_extra_version, cons_client_version);
+ else
+ fprintf (stream, "%s %s\n", cons_client_name, cons_client_version);
+
+ fputs (STANDARD_HURD_VERSION (libcons) "\n", stream);
+}
+
+void (*argp_program_version_hook) (FILE *stream, struct argp_state *state)
+ = _print_version;
diff --git a/libcons/priv.h b/libcons/priv.h
new file mode 100644
index 00000000..38971ff8
--- /dev/null
+++ b/libcons/priv.h
@@ -0,0 +1,93 @@
+/* Private declarations for cons library
+ Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _CONS_PRIV_H
+#define _CONS_PRIV_H
+
+#include "cons.h"
+
+
+/* The kind of bells available. */
+typedef enum
+ {
+ BELL_OFF,
+ BELL_VISUAL,
+ BELL_AUDIBLE
+ } bell_type_t;
+
+#define CONS_EVT_MOUSE_MOVE (1 << 1)
+#define CONS_EVT_MOUSE_BUTTON (1 << 2)
+#define CONS_EVT_KEYPRESS (1 << 4)
+#define CONS_EVT_OUTPUT (1 << 8)
+
+
+/* Number of records the client is allowed to lag behind the
+ server. */
+extern int _cons_slack;
+
+/* If we jump down at input. */
+extern int _cons_jump_down_on_input;
+
+/* If we jump down at output. */
+extern int _cons_jump_down_on_output;
+
+/* The type of bell used for the visual bell. */
+extern bell_type_t _cons_visual_bell;
+
+/* The type of bell used for the audible bell. */
+extern bell_type_t _cons_audible_bell;
+
+/* The type of events that will make the mouse cursor visible. */
+extern int _cons_show_mouse;
+
+/* The type of events that will hide the mouse cursor. */
+extern int _cons_hide_mouse;
+
+/* The mouse sensitivity. */
+extern float _cons_mouse_sens;
+
+
+/* Non-locking version of cons_vcons_scrollback. Does also not update
+ the display. */
+int _cons_vcons_scrollback (vcons_t vcons, cons_scroll_t type, float value);
+
+/* Non-locking version of cons_vcons_input. */
+error_t _cons_vcons_input (vcons_t vcons, char *buf, size_t size);
+
+/* Generate the console event EVENT for console VCONS. */
+void _cons_vcons_console_event (vcons_t vcons, int event);
+
+
+/* Called by MiG to translate ports into cons_notify_t. mutations.h
+ arranges for this to happen for the fs_notify interfaces. */
+static inline cons_notify_t
+begin_using_notify_port (fs_notify_t port)
+{
+ return ports_lookup_port (cons_port_bucket, port, cons_port_class);
+}
+
+/* Called by MiG after server routines have been run; this balances
+ begin_using_notify_port, and is arranged for the fs_notify
+ interfaces by mutations.h. */
+static inline void
+end_using_notify_port (cons_notify_t cred)
+{
+ if (cred)
+ ports_port_deref (cred);
+}
+
+#endif /* _CONS_PRIV_H */
diff --git a/libcons/vcons-add.c b/libcons/vcons-add.c
new file mode 100644
index 00000000..1a6eb204
--- /dev/null
+++ b/libcons/vcons-add.c
@@ -0,0 +1,30 @@
+/* vcons-add.c - Add a virtual console.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+
+#include "cons.h"
+
+/* The virtual console entry VCONS_ENTRY was just added. CONS is
+ locked. */
+void
+cons_vcons_add (cons_t cons, vcons_list_t vcons_entry)
+{
+}
diff --git a/libcons/vcons-close.c b/libcons/vcons-close.c
new file mode 100644
index 00000000..554bfa80
--- /dev/null
+++ b/libcons/vcons-close.c
@@ -0,0 +1,45 @@
+/* vcons-close.c - Close a virtual console.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <assert.h>
+
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <pthread.h>
+
+#include "cons.h"
+
+/* Close the virtual console VCONS. */
+void
+cons_vcons_close (vcons_t vcons)
+{
+ cons_t cons = vcons->cons;
+ vcons_list_t vcons_entry = vcons->vcons_entry;
+
+ pthread_mutex_lock (&cons->lock);
+ /* The same virtual console should never be opened twice. */
+ assert (vcons_entry->vcons == vcons);
+ vcons_entry->vcons = NULL;
+ pthread_mutex_unlock (&cons->lock);
+
+ /* Destroy the port. */
+ ports_port_deref (vcons);
+ ports_destroy_right (vcons);
+}
diff --git a/libcons/vcons-destroy.c b/libcons/vcons-destroy.c
new file mode 100644
index 00000000..ca1c5c37
--- /dev/null
+++ b/libcons/vcons-destroy.c
@@ -0,0 +1,52 @@
+/* vcons-destroy.c - Clean up the resources for a virtual console.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/fcntl.h>
+
+#include <hurd.h>
+#include <mach.h>
+
+#include "cons.h"
+
+/* Destroy the virtual console VCONS. */
+void
+cons_vcons_destroy (void *port)
+{
+ cons_notify_t notify = (cons_notify_t) port;
+ vcons_t vcons = (vcons_t) port;
+
+ if (notify->cons)
+ return;
+
+ if (vcons->input >= 0)
+ {
+ close (vcons->input);
+ vcons->input = -1;
+ }
+ if (vcons->display != MAP_FAILED)
+ {
+ munmap (vcons->display, vcons->display_size);
+ vcons->display = MAP_FAILED;
+ }
+}
diff --git a/libcons/vcons-event.c b/libcons/vcons-event.c
new file mode 100644
index 00000000..d8c31dc0
--- /dev/null
+++ b/libcons/vcons-event.c
@@ -0,0 +1,31 @@
+/* vcons-event.c - Handle console events.
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+ Written by Marco Gerards.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "cons.h"
+#include "priv.h"
+
+void
+_cons_vcons_console_event (vcons_t vcons, int event)
+{
+ if (_cons_show_mouse & event)
+ cons_vcons_set_mousecursor_status (vcons, 1);
+ else if (_cons_hide_mouse & event)
+ cons_vcons_set_mousecursor_status (vcons, 0);
+}
diff --git a/libcons/vcons-input.c b/libcons/vcons-input.c
new file mode 100644
index 00000000..ccc7532b
--- /dev/null
+++ b/libcons/vcons-input.c
@@ -0,0 +1,65 @@
+/* vcons-input.c - Add input to a virtual console.
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include "cons.h"
+#include "priv.h"
+
+/* Non-locking version of cons_vcons_input. */
+error_t
+_cons_vcons_input (vcons_t vcons, char *buf, size_t size)
+{
+ int ret;
+
+ do
+ {
+ ret = write (vcons->input, buf, size);
+ if (ret > 0)
+ {
+ size -= ret;
+ buf += ret;
+ }
+ }
+ while (size && (ret != -1 || errno == EINTR));
+
+ return 0;
+}
+
+
+/* Enter SIZE bytes from the buffer BUF into the virtual console
+ VCONS. */
+error_t
+cons_vcons_input (vcons_t vcons, char *buf, size_t size)
+{
+ pthread_mutex_lock (&vcons->lock);
+
+ _cons_vcons_console_event (vcons, CONS_EVT_KEYPRESS);
+
+ if (vcons->scrolling && _cons_jump_down_on_input)
+ _cons_vcons_scrollback (vcons, CONS_SCROLL_ABSOLUTE_LINE, 0);
+
+ _cons_vcons_input (vcons, buf, size);
+
+ pthread_mutex_unlock (&vcons->lock);
+ return 0;
+}
diff --git a/libcons/vcons-move-mouse.c b/libcons/vcons-move-mouse.c
new file mode 100644
index 00000000..631f2ef5
--- /dev/null
+++ b/libcons/vcons-move-mouse.c
@@ -0,0 +1,105 @@
+/* vcons-move-mouse.c - Catch mouse events.
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+ Written by Marco Gerards.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include "cons.h"
+#include "priv.h"
+
+static float mousepos_x;
+static float mousepos_y;
+
+error_t
+cons_vcons_move_mouse (vcons_t vcons, mouse_event_t ev)
+{
+ char event[CONS_MOUSE_EVENT_LENGTH];
+ uint32_t report_events;
+
+ pthread_mutex_lock (&vcons->lock);
+ report_events = vcons->display->flags & CONS_FLAGS_TRACK_MOUSE;
+
+ switch (ev->mouse_movement)
+ {
+ case CONS_VCONS_MOUSE_MOVE_REL:
+ mousepos_x += ((float) ev->x / _cons_mouse_sens);
+ mousepos_y += ((float) ev->y / _cons_mouse_sens);
+ break;
+
+ case CONS_VCONS_MOUSE_MOVE_ABS_PERCENT:
+ mousepos_x = vcons->state.screen.width * ev->x / 100;
+ mousepos_y = vcons->state.screen.height * ev->y / 100;
+ break;
+
+ case CONS_VCONS_MOUSE_MOVE_ABS:
+ mousepos_x = ev->x;
+ mousepos_y = ev->y;
+ break;
+ }
+
+ /* Keep the mouse cursor in range of the VC. */
+ if (mousepos_x < 0)
+ mousepos_x = 0;
+ if (mousepos_y < 0)
+ mousepos_y = 0;
+ if (mousepos_x >= (float) vcons->state.screen.width)
+ mousepos_x = vcons->state.screen.width - 1;
+ if (mousepos_y >= (float) vcons->state.screen.height)
+ mousepos_y = vcons->state.screen.height - 1;
+
+ cons_vcons_set_mousecursor_pos (vcons, (float) mousepos_x, (float) mousepos_y);
+
+ /* Report a mouse movement event. */
+ if (ev->x || ev->y)
+ _cons_vcons_console_event (vcons, CONS_EVT_MOUSE_MOVE);
+
+ /* Report a mouse button event. */
+ if (ev->mouse_button != CONS_VCONS_MOUSE_BUTTON_NO_OP)
+ _cons_vcons_console_event (vcons, CONS_EVT_MOUSE_BUTTON);
+
+ if (report_events)
+ {
+ switch (ev->mouse_button)
+ {
+ case CONS_VCONS_MOUSE_BUTTON_NO_OP:
+ break;
+
+ case CONS_VCONS_MOUSE_BUTTON_PRESSED:
+ /* Make an xterm like event string. */
+ CONS_MOUSE_EVENT (event, ev->button, (int) mousepos_x + 1, (int) mousepos_y + 1);
+
+ _cons_vcons_input (vcons, event, CONS_MOUSE_EVENT_LENGTH);
+ /* And send it to the server. */
+ break;
+
+ case CONS_VCONS_MOUSE_BUTTON_RELEASED:
+ /* Make an xterm like event string. */
+ CONS_MOUSE_EVENT (event, CONS_MOUSE_RELEASE, (int) mousepos_x + 1, (int) mousepos_y + 1);
+
+ /* And send it to the server. */
+ _cons_vcons_input (vcons, event, CONS_MOUSE_EVENT_LENGTH);
+ break;
+ }
+ }
+
+ pthread_mutex_unlock (&vcons->lock);
+ return 0;
+}
diff --git a/libcons/vcons-open.c b/libcons/vcons-open.c
new file mode 100644
index 00000000..22d64303
--- /dev/null
+++ b/libcons/vcons-open.c
@@ -0,0 +1,176 @@
+/* vcons-open.c - Open a virtual console.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/fcntl.h>
+#include <pthread.h>
+
+#include <hurd.h>
+#include <mach.h>
+
+#include "cons.h"
+
+/* Open the virtual console for VCONS_ENTRY. CONS is locked.
+ Afterwards, R_VCONS will be locked. */
+error_t
+cons_vcons_open (cons_t cons, vcons_list_t vcons_entry, vcons_t *r_vcons)
+{
+ error_t err = 0;
+ char *name;
+ file_t vconsp = MACH_PORT_NULL;
+ file_t file = MACH_PORT_NULL;
+ int fd = -1;
+ struct stat statbuf;
+ mach_port_t notify = MACH_PORT_NULL;
+ vcons_t vcons;
+
+ if (asprintf (&name, "%u", vcons_entry->id) < 0)
+ return err;
+
+ /* Set up the port we receive notification messages on. */
+ err = ports_create_port (cons_port_class, cons_port_bucket,
+ sizeof (*vcons), &vcons);
+ if (err)
+ goto err;
+ vcons->notify.cons = NULL;
+ vcons->cons = cons;
+ vcons->vcons_entry = vcons_entry;
+ vcons->id = vcons_entry->id;
+ pthread_mutex_init (&vcons->lock, NULL);
+ vcons->input = -1;
+ vcons->display = MAP_FAILED;
+ vcons->scrolling = 0;
+
+ /* Open the directory port of the virtual console. */
+ vconsp = file_name_lookup_under (cons->dirport, name,
+ O_DIRECTORY | O_RDONLY, 0);
+ if (vconsp == MACH_PORT_NULL)
+ {
+ err = errno;
+ goto err;
+ }
+
+ /* Within that directory, open the input node. */
+ file = file_name_lookup_under (vconsp, "input", O_WRONLY /* | O_NONBLOCK */, 0);
+ if (file == MACH_PORT_NULL)
+ err = errno;
+ else
+ {
+ vcons->input = openport (file, O_WRONLY /* | O_NONBLOCK */);
+ if (vcons->input < 0)
+ err = errno;
+ else
+ /* openport() consumed the reference. */
+ file = MACH_PORT_NULL;
+ }
+ if (err)
+ goto err;
+
+ /* Within that directory, also open the display node. */
+ file = file_name_lookup_under (vconsp, "display", O_RDONLY, 0);
+ if (file == MACH_PORT_NULL)
+ err = errno;
+ else
+ {
+ /* Acquire an additional reference for openport(). */
+ err = mach_port_mod_refs (mach_task_self (), file,
+ MACH_PORT_RIGHT_SEND, +1);
+ if (err)
+ goto err;
+ fd = openport (file, O_RDONLY);
+ if (fd < 0)
+ err = errno;
+ }
+ if (err)
+ goto err;
+
+ /* Map the whole file. */
+ if (fstat (fd, &statbuf) < 0)
+ {
+ err = errno;
+ goto err;
+ }
+ vcons->display_size = statbuf.st_size;
+ vcons->display = mmap (0, vcons->display_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (vcons->display == MAP_FAILED)
+ {
+ err = errno;
+ goto err;
+ }
+
+ if (vcons->display->magic != CONS_MAGIC
+ || vcons->display->version >> CONS_VERSION_MAJ_SHIFT != 0)
+ {
+ err = EINVAL;
+ goto err;
+ }
+ vcons->state.screen.width = vcons->display->screen.width;
+ vcons->state.screen.height = vcons->display->screen.height;
+ vcons->state.screen.lines = vcons->display->screen.lines;
+ vcons->state.screen.matrix = (conchar_t *)
+ (((uint32_t *) vcons->display) + vcons->display->screen.matrix);
+ vcons->state.changes.length = vcons->display->changes.length;
+ vcons->state.changes.buffer = (cons_change_t *)
+ (((uint32_t *) vcons->display) + vcons->display->changes.buffer);
+
+ /* Request notification messages. */
+ notify = ports_get_right (vcons);
+ mach_port_set_qlimit (mach_task_self (), notify, 1);
+
+ /* When this succeeds, we will immediately receive notification
+ messages for this virtual console. */
+ pthread_mutex_lock (&vcons->lock);
+ err = file_notice_changes (file, notify, MACH_MSG_TYPE_MAKE_SEND);
+ if (!err)
+ {
+ *r_vcons = vcons;
+ goto out;
+ }
+
+ err:
+ if (vcons->input >= 0)
+ {
+ close (vcons->input);
+ vcons->input = -1;
+ }
+ if (vcons->display != MAP_FAILED)
+ {
+ munmap (vcons->display, vcons->display_size);
+ vcons->display = MAP_FAILED;
+ }
+ if (notify)
+ {
+ mach_port_deallocate (mach_task_self (), notify);
+ ports_port_deref (vcons);
+ }
+ ports_destroy_right (vcons);
+ out:
+ if (fd > 0)
+ close (fd);
+ if (file != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), file);
+ if (vconsp != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), vconsp);
+ free (name);
+ return err;
+}
diff --git a/libcons/vcons-refresh.c b/libcons/vcons-refresh.c
new file mode 100644
index 00000000..ce6807fe
--- /dev/null
+++ b/libcons/vcons-refresh.c
@@ -0,0 +1,76 @@
+/* vcons-refresh.c - Redraw a virtual console.
+ Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+#include <assert.h>
+
+#include "cons.h"
+#include "priv.h"
+
+/* Redraw the virtual console VCONS, which is locked. */
+void
+cons_vcons_refresh (vcons_t vcons)
+{
+ uint32_t start;
+ vcons->state.screen.cur_line = vcons->display->screen.cur_line;
+ vcons->state.screen.scr_lines = vcons->display->screen.scr_lines;
+ vcons->state.cursor.col = vcons->display->cursor.col;
+ vcons->state.cursor.row = vcons->display->cursor.row;
+ vcons->state.cursor.status = vcons->display->cursor.status;
+ vcons->state.bell.audible = vcons->display->bell.audible;
+ vcons->state.bell.visible = vcons->display->bell.visible;
+ vcons->state.flags = vcons->display->flags;
+ vcons->state.changes.written = vcons->display->changes.written;
+
+ if (vcons->state.screen.scr_lines < vcons->scrolling)
+ vcons->scrolling = vcons->scrolling;
+
+ cons_vcons_set_dimension (vcons, vcons->state.screen.width,
+ vcons->state.screen.height);
+
+ if (vcons->state.screen.cur_line >= vcons->scrolling)
+ start = vcons->state.screen.cur_line - vcons->scrolling;
+ else
+ start = (UINT32_MAX
+ - (vcons->scrolling - vcons->state.screen.cur_line)) + 1;
+ start %= vcons->state.screen.lines;
+
+ cons_vcons_write (vcons, vcons->state.screen.matrix
+ + start * vcons->state.screen.width,
+ ((vcons->state.screen.lines - start
+ < vcons->state.screen.height)
+ ? vcons->state.screen.lines - start
+ : vcons->state.screen.height)
+ * vcons->state.screen.width, 0, 0);
+ if (vcons->state.screen.lines - start < vcons->state.screen.height)
+ cons_vcons_write (vcons, vcons->state.screen.matrix,
+ vcons->state.screen.height * vcons->state.screen.width
+ - (vcons->state.screen.lines - start)
+ * vcons->state.screen.width, 0,
+ vcons->state.screen.lines - start);
+
+ cons_vcons_set_cursor_pos (vcons, vcons->state.cursor.col,
+ vcons->state.cursor.row);
+ cons_vcons_set_cursor_status (vcons, vcons->state.cursor.status);
+ cons_vcons_set_scroll_lock (vcons, vcons->state.flags
+ & CONS_FLAGS_SCROLL_LOCK);
+ _cons_vcons_console_event (vcons, CONS_EVT_OUTPUT);
+ cons_vcons_update (vcons);
+}
diff --git a/libcons/vcons-remove.c b/libcons/vcons-remove.c
new file mode 100644
index 00000000..34b31d6f
--- /dev/null
+++ b/libcons/vcons-remove.c
@@ -0,0 +1,31 @@
+/* vcons-remove.c - Remove a virtual console.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <assert.h>
+
+#include "cons.h"
+
+/* The virtual console VCONS_ENTRY is going to be removed.
+ VCONS_ENTRY->cons is locked. */
+void
+cons_vcons_remove (cons_t cons, vcons_list_t vcons_entry)
+{
+ assert (!vcons_entry->vcons);
+}
diff --git a/libcons/vcons-scrollback.c b/libcons/vcons-scrollback.c
new file mode 100644
index 00000000..0cf6b267
--- /dev/null
+++ b/libcons/vcons-scrollback.c
@@ -0,0 +1,163 @@
+/* vcons-scrollback.c - Move forward and backward in the scrollback buffer.
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdint.h>
+
+#include <pthread.h>
+
+#include "cons.h"
+#include "priv.h"
+
+/* Non-locking version of cons_vcons_scrollback. Does also not update
+ the display. */
+int
+_cons_vcons_scrollback (vcons_t vcons, cons_scroll_t type, float value)
+{
+ int scrolling;
+ uint32_t new_scr;
+
+ switch (type)
+ {
+ case CONS_SCROLL_DELTA_LINES:
+ scrolling = vcons->scrolling + ((uint32_t) value);
+ break;
+ case CONS_SCROLL_DELTA_SCREENS:
+ scrolling = vcons->scrolling
+ + ((uint32_t) (value * vcons->state.screen.height));
+ break;
+ case CONS_SCROLL_ABSOLUTE_LINE:
+ scrolling = (uint32_t) value;
+ break;
+ case CONS_SCROLL_ABSOLUTE_PERCENTAGE:
+ scrolling = (uint32_t) (value * vcons->state.screen.scr_lines);
+ break;
+ default:
+ return 0;
+ }
+
+ if (scrolling < 0)
+ new_scr = 0;
+ else if (scrolling > vcons->state.screen.scr_lines)
+ new_scr = vcons->state.screen.scr_lines;
+ else
+ new_scr = scrolling;
+
+ if (new_scr == vcons->scrolling)
+ return 0;
+
+ scrolling = vcons->scrolling - new_scr;
+ {
+ uint32_t new_cur_line;
+ off_t size = vcons->state.screen.width
+ * vcons->state.screen.lines;
+ off_t vis_start;
+ off_t start;
+ off_t end;
+
+ if (vcons->state.screen.cur_line >= new_scr)
+ new_cur_line = vcons->state.screen.cur_line - new_scr;
+ else
+ new_cur_line = (UINT32_MAX - (new_scr - vcons->state.screen.cur_line)) + 1;
+
+ if (scrolling > 0 && (uint32_t) scrolling > vcons->state.screen.height)
+ scrolling = vcons->state.screen.height;
+ else if (scrolling < 0
+ && (uint32_t) (-scrolling) > vcons->state.screen.height)
+ scrolling = -vcons->state.screen.height;
+ if ((scrolling > 0 && scrolling < vcons->state.screen.height)
+ || (scrolling < 0
+ && (uint32_t) (-scrolling) < vcons->state.screen.height))
+ cons_vcons_scroll (vcons, scrolling);
+ else if ((scrolling > 0 && scrolling == vcons->state.screen.height)
+ || (scrolling < 0
+ && (uint32_t) (-scrolling) == vcons->state.screen.height))
+ cons_vcons_clear (vcons, vcons->state.screen.width
+ * vcons->state.screen.height, 0, 0);
+
+ vis_start = vcons->state.screen.width
+ * (new_cur_line % vcons->state.screen.lines);
+ if (scrolling > 0)
+ start = (((new_cur_line % vcons->state.screen.lines)
+ + vcons->state.screen.height - scrolling)
+ * vcons->state.screen.width) % size;
+ else
+ start = vis_start;
+ end = start + abs (scrolling) * vcons->state.screen.width - 1;
+
+ cons_vcons_write (vcons,
+ vcons->state.screen.matrix + start,
+ end < size
+ ? end - start + 1
+ : size - start,
+ 0, (scrolling > 0)
+ ? vcons->state.screen.height - scrolling : 0);
+ if (end >= size)
+ cons_vcons_write (vcons,
+ vcons->state.screen.matrix,
+ end - size + 1,
+ 0, (size - vis_start)
+ / vcons->state.screen.width);
+ }
+
+ /* Set the new cursor position. */
+ {
+ uint32_t row = vcons->state.cursor.row;
+ uint32_t height = vcons->state.screen.height;
+
+ if (row + new_scr < height)
+ {
+ cons_vcons_set_cursor_pos (vcons, vcons->state.cursor.col,
+ row + new_scr);
+ if (row + vcons->scrolling >= height)
+ /* The cursor was invisible before. */
+ cons_vcons_set_cursor_status (vcons, vcons->state.cursor.status);
+ }
+ else if (row + vcons->scrolling < height)
+ /* The cursor was visible before. */
+ cons_vcons_set_cursor_status (vcons, CONS_CURSOR_INVISIBLE);
+ }
+
+ vcons->scrolling -= scrolling;
+
+ return -scrolling;
+}
+
+/* Scroll back into the history of VCONS. If TYPE is
+ CONS_SCROLL_DELTA_LINES, scroll up or down by VALUE lines. If TYPE
+ is CONS_SCROLL_DELTA_SCREENS, scroll up or down by VALUE multiples
+ of a screen height. If TYPE is CONS_SCROLL_ABSOLUTE_LINE, scroll to
+ line VALUE (where 0 is the lowest line). If TYPE is
+ CONS_SCROLL_ABSOLUTE_PERCENTAGE, scroll to the position determined
+ by VALUE, where 0 is the bottom and 1 is the top.
+
+ The function returns the number of lines actually scrolled up or
+ down. */
+int
+cons_vcons_scrollback (vcons_t vcons, cons_scroll_t type, float value)
+{
+ int ret;
+
+ pthread_mutex_lock (&vcons->lock);
+ ret = _cons_vcons_scrollback (vcons, type, value);
+ _cons_vcons_console_event (vcons, CONS_EVT_OUTPUT);
+ cons_vcons_update (vcons);
+ pthread_mutex_unlock (&vcons->lock);
+ return ret;
+}
diff --git a/libdirmgt/Makefile b/libdirmgt/Makefile
new file mode 100644
index 00000000..f4e9f758
--- /dev/null
+++ b/libdirmgt/Makefile
@@ -0,0 +1,23 @@
+# Copyright (C) 1995, 2012 Free Software Foundation
+# Written by Michael I. Bushnell.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libdirmgt
+makemode := misc
+
+include ../Makeconf
diff --git a/libdirmgt/dirmgt.h b/libdirmgt/dirmgt.h
new file mode 100644
index 00000000..15921609
--- /dev/null
+++ b/libdirmgt/dirmgt.h
@@ -0,0 +1,51 @@
+/*
+ Copyright (C) 1994 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This library manages directories for users who want to
+ write filesystem servers. */
+
+/* Search directory DIR for name NAME. If NODEP is nonzero, then
+ set *NODEP to the node found. If TYPE is nonzero, then
+ set *TYPE to the type of the node found. */
+error_t
+dirmgt_lookup (struct directory *dir, char *name, struct node **nodep,
+ int *type);
+
+/* Add NODE to DIR under name NAME. If NAME is already present in
+ the directory, the existing entry under that name is replaced without
+ further notice. TYPE is the DT_* name for the type of node. */
+error_t
+dirmgt_enter (struct directory *dir, char *name, struct node *node,
+ int type);
+
+/* Add SUBDIR to DIR under name NAME. If NAME is already present in
+ the directory, then EBUSY is returned. */
+error_t
+dirmgt_enter_dir (struct directory *dir, char *name, struct directory *subdir);
+
+/* Return directory contents to a user; args are exactly as for
+ the fs.defs:dir_readdir RPC. */
+error_t
+dirmgt_readdir (struct directory *dir, char **data, u_int *datacnt,
+ int entry, int nentries, vm_size_t bufsiz, int *amt);
+
+/* If this routine is defined, then it will be called when a lookup on
+ a directory fails. If this routine returns success, the lookup will
+ then be repeated. If it returns an error, then the lookup will fail
+ with the reported error. */
+error_t
+(*dirmgt_find_entry)(struct directory *dir, char *name);
diff --git a/libdiskfs/Makefile b/libdiskfs/Makefile
new file mode 100644
index 00000000..996e86a0
--- /dev/null
+++ b/libdiskfs/Makefile
@@ -0,0 +1,74 @@
+# Copyright (C) 1994,95,96,97,98,99,2000,01,2006,2012
+# Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libdiskfs
+makemode := library
+
+libname = libdiskfs
+FSSRCS= dir-chg.c dir-link.c dir-lookup.c dir-mkdir.c dir-mkfile.c \
+ dir-readdir.c dir-rename.c dir-rmdir.c dir-unlink.c \
+ file-access.c file-chauthor.c file-chflags.c file-chg.c \
+ file-chmod.c file-chown.c file-exec.c file-get-fs-opts.c \
+ file-get-trans.c file-get-transcntl.c file-getcontrol.c \
+ file-getfh.c file-getlinknode.c file-lock-stat.c \
+ file-lock.c file-set-size.c file-set-trans.c file-statfs.c \
+ file-sync.c file-syncfs.c file-utimes.c file-reparent.c
+IOSRCS= io-async-icky.c io-async.c io-duplicate.c io-get-conch.c io-revoke.c \
+ io-map-cntl.c io-map.c io-modes-get.c io-modes-off.c \
+ io-modes-on.c io-modes-set.c io-owner-mod.c io-owner-get.c \
+ io-pathconf.c io-prenotify.c io-read.c io-readable.c io-identity.c \
+ io-reauthenticate.c io-rel-conch.c io-restrict-auth.c io-seek.c \
+ io-select.c io-stat.c io-stubs.c io-write.c io-version.c io-sigio.c
+FSYSSRCS=fsys-getroot.c fsys-goaway.c fsys-startup.c fsys-getfile.c \
+ fsys-options.c fsys-syncfs.c fsys-forward.c \
+ file-get-children.c file-get-source.c
+IFSOCKSRCS=ifsock.c
+OTHERSRCS = conch-fetch.c conch-set.c dir-clear.c dir-init.c dir-renamed.c \
+ extern-inline.c \
+ node-create.c node-drop.c node-make.c node-rdwr.c node-update.c \
+ node-nref.c node-nput.c node-nrele.c node-nrefl.c node-nputl.c \
+ node-nrelel.c \
+ peropen-make.c peropen-rele.c protid-make.c protid-rele.c \
+ init-init.c init-startup.c init-first.c init-main.c \
+ rdwr-internal.c boot-start.c demuxer.c node-times.c shutdown.c \
+ sync-interval.c sync-default.c \
+ opts-set.c opts-get.c opts-std-startup.c opts-std-runtime.c \
+ opts-append-std.c opts-common.c opts-runtime.c opts-version.c \
+ trans-callback.c readonly.c readonly-changed.c \
+ remount.c console.c disk-pager.c \
+ name-cache.c direnter.c dirrewrite.c dirremove.c lookup.c dead-name.c \
+ validate-mode.c validate-group.c validate-author.c validate-flags.c \
+ validate-rdev.c validate-owner.c extra-version.c get-source.c
+SRCS = $(OTHERSRCS) $(FSSRCS) $(IOSRCS) $(FSYSSRCS) $(IFSOCKSRCS)
+installhdrs = diskfs.h diskfs-pager.h
+
+MIGSTUBS = fsServer.o ioServer.o fsysServer.o exec_startupServer.o \
+ fsys_replyUser.o fs_notifyUser.o ifsockServer.o \
+ startup_notifyServer.o
+OBJS = $(sort $(SRCS:.c=.o) $(MIGSTUBS))
+
+HURDLIBS = fshelp iohelp store ports shouldbeinlibc pager
+LDLIBS += -lpthread
+
+fsys-MIGSFLAGS = -imacros $(srcdir)/fsmutations.h -DREPLY_PORTS
+fs-MIGSFLAGS = -imacros $(srcdir)/fsmutations.h
+io-MIGSFLAGS = -imacros $(srcdir)/fsmutations.h
+ifsock-MIGSFLAGS = -imacros $(srcdir)/fsmutations.h
+exec_startup-MIGSFLAGS = -imacros $(srcdir)/fsmutations.h
+MIGCOMSFLAGS = -prefix diskfs_
+
+include ../Makeconf
diff --git a/libdiskfs/boot-start.c b/libdiskfs/boot-start.c
new file mode 100644
index 00000000..4cc7bb8d
--- /dev/null
+++ b/libdiskfs/boot-start.c
@@ -0,0 +1,650 @@
+/*
+ Copyright (C) 1993,94,95,96,97,98,99,2000,01,02,10,11
+ Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include <stdio.h>
+#include <hurd.h>
+#include <hurd/fsys.h>
+#include <hurd/exec.h>
+#include <hurd/startup.h>
+#include <hurd/paths.h>
+#include <fcntl.h>
+#include <device/device.h>
+#include <sys/reboot.h>
+#include <string.h>
+#include <argz.h>
+#include <error.h>
+#include <pids.h>
+#include "exec_S.h"
+#include "exec_startup_S.h"
+#include "fsys_S.h"
+#include "fsys_reply_U.h"
+
+static mach_port_t diskfs_exec_ctl;
+extern task_t diskfs_exec_server_task;
+static task_t parent_task = MACH_PORT_NULL;
+
+static pthread_mutex_t execstartlock;
+static pthread_cond_t execstarted;
+
+const char *diskfs_boot_init_program = _HURD_INIT;
+
+static void start_execserver ();
+
+char **diskfs_argv = 0;
+
+static mach_port_t
+get_console ()
+{
+ mach_port_t device_master, console;
+ error_t err = get_privileged_ports (0, &device_master);
+
+ if (err)
+ return MACH_PORT_NULL;
+
+ err = device_open (device_master, D_WRITE | D_READ, "console", &console);
+ if (err)
+ return MACH_PORT_NULL;
+
+ return console;
+}
+
+/* Make sure we have the privileged ports. */
+void
+_diskfs_boot_privports (void)
+{
+ assert (diskfs_boot_filesystem ());
+ if (_hurd_host_priv == MACH_PORT_NULL)
+ {
+ /* We are the boot command run by the real bootstrap filesystem.
+ We get the privileged ports from it as init would. */
+ mach_port_t bootstrap;
+ error_t err = task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ assert_perror (err);
+ err = fsys_getpriv (bootstrap, &_hurd_host_priv, &_hurd_device_master,
+ &parent_task);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ assert_perror (err);
+ }
+}
+
+/* Once diskfs_root_node is set, call this if we are a bootstrap
+ filesystem. */
+void
+diskfs_start_bootstrap ()
+{
+ mach_port_t root_pt, startup_pt, bootpt;
+ retry_type retry;
+ char pathbuf[1024];
+ string_t retry_name;
+ mach_port_t portarray[INIT_PORT_MAX];
+ mach_port_t fdarray[3]; /* XXX */
+ task_t newt;
+ error_t err;
+ char *exec_argv, *exec_env;
+ const char *initname;
+ size_t exec_argvlen, exec_envlen;
+ struct port_info *bootinfo;
+ struct protid *rootpi;
+ struct peropen *rootpo;
+ mach_port_t diskfs_exec;
+ unsigned int init_lookups = 0;
+
+ /* Create the port for current and root directory. */
+ err = diskfs_make_peropen (diskfs_root_node, O_READ | O_EXEC, 0,
+ &rootpo);
+ assert_perror (err);
+
+ err = diskfs_create_protid (rootpo, 0, &rootpi);
+ assert_perror (err);
+
+ /* Get us a send right to copy around. */
+ root_pt = ports_get_send_right (rootpi);
+ ports_port_deref (rootpi);
+
+ if (diskfs_exec_server_task == MACH_PORT_NULL)
+ {
+ /* We are the boot command run by the real bootstrap filesystem.
+ Our parent (the real bootstrap filesystem) provides us a root
+ directory where we look up /servers/exec like any non-bootstrap
+ filesystem would. */
+ assert (_hurd_ports);
+ assert (_hurd_ports[INIT_PORT_CRDIR].port != MACH_PORT_NULL);
+ diskfs_exec = file_name_lookup (_SERVERS_EXEC, 0, 0);
+ if (diskfs_exec == MACH_PORT_NULL)
+ error (1, errno, "%s", _SERVERS_EXEC);
+ else
+ {
+#ifndef NDEBUG
+ /* Make sure this is really a port to another server. */
+ struct port_info *pi = ports_lookup_port (diskfs_port_bucket,
+ diskfs_exec, 0);
+ assert (!pi);
+#endif
+ }
+
+ /* Here we assume the parent has already printed:
+ Hurd server bootstrap: bootfs[bootdev] exec ourfs
+ */
+ printf ("\nContinuing on new root filesystem %s:", diskfs_disk_name);
+ fflush (stdout);
+ }
+ else
+ {
+ uid_t idlist[] = {0, 0, 0};
+ file_t execnode;
+
+ printf ("Hurd server bootstrap: %s[%s]",
+ program_invocation_short_name, diskfs_disk_name);
+ fflush (stdout);
+
+ /* Get the execserver going and wait for its fsys_startup */
+ pthread_mutex_init (&execstartlock, NULL);
+ pthread_cond_init (&execstarted, NULL);
+ pthread_mutex_lock (&execstartlock);
+ start_execserver ();
+ pthread_cond_wait (&execstarted, &execstartlock);
+ pthread_mutex_unlock (&execstartlock);
+ assert (diskfs_exec_ctl != MACH_PORT_NULL);
+
+ /* Contact the exec server. */
+ err = fsys_getroot (diskfs_exec_ctl, root_pt, MACH_MSG_TYPE_COPY_SEND,
+ idlist, 3, idlist, 3, 0,
+ &retry, retry_name, &diskfs_exec);
+ assert_perror (err);
+ assert (retry == FS_RETRY_NORMAL);
+ assert (retry_name[0] == '\0');
+ assert (diskfs_exec != MACH_PORT_NULL);
+
+ /* Attempt to set the active translator for the exec server so that
+ filesystems other than the bootstrap can find it. */
+ err = dir_lookup (root_pt, _SERVERS_EXEC, O_NOTRANS, 0,
+ &retry, pathbuf, &execnode);
+ if (err)
+ {
+ error (0, err, "cannot set translator on %s", _SERVERS_EXEC);
+ mach_port_deallocate (mach_task_self (), diskfs_exec_ctl);
+ }
+ else
+ {
+ assert (retry == FS_RETRY_NORMAL);
+ assert (retry_name[0] == '\0');
+ assert (execnode != MACH_PORT_NULL);
+ err = file_set_translator (execnode, 0, FS_TRANS_SET, 0, 0, 0,
+ diskfs_exec_ctl, MACH_MSG_TYPE_COPY_SEND);
+ mach_port_deallocate (mach_task_self (), diskfs_exec_ctl);
+ mach_port_deallocate (mach_task_self (), execnode);
+ assert_perror (err);
+ }
+ diskfs_exec_ctl = MACH_PORT_NULL; /* Not used after this. */
+ }
+
+ /* Cache the exec server port for file_exec to use. */
+ _hurd_port_set (&_diskfs_exec_portcell, diskfs_exec);
+
+ if (_diskfs_boot_command)
+ {
+ /* We have a boot command line to run instead of init. */
+ err = argz_create (_diskfs_boot_command, &exec_argv, &exec_argvlen);
+ assert_perror (err);
+ initname = exec_argv;
+ while (*initname == '/')
+ initname++;
+ }
+ else
+ {
+ /* Choose the name of the startup server to execute. */
+ initname = diskfs_boot_init_program;
+ while (*initname == '/')
+ initname++;
+
+ int len = asprintf (&exec_argv, "/%s%c", initname, '\0');
+ assert (len != -1);
+ exec_argvlen = (size_t) len;
+ err = argz_add_sep (&exec_argv, &exec_argvlen,
+ diskfs_boot_command_line, ' ');
+ assert_perror (err);
+
+ initname = exec_argv + 1;
+ }
+
+ lookup_init:
+ err = dir_lookup (root_pt, initname, O_READ, 0,
+ &retry, pathbuf, &startup_pt);
+ init_lookups++;
+ if (err)
+ {
+ printf ("\nCannot find startup program `%s': %s\n",
+ initname, strerror (err));
+ fflush (stdout);
+ free (exec_argv);
+ assert_perror (err); /* XXX this won't reboot properly */
+ }
+ else if (retry == FS_RETRY_MAGICAL && pathbuf[0] == '/')
+ {
+ assert (sysconf (_SC_SYMLOOP_MAX) < 0 ||
+ init_lookups < sysconf (_SC_SYMLOOP_MAX));
+
+ /* INITNAME is a symlink with an absolute target, so try again. */
+ initname = strdupa (pathbuf);
+ goto lookup_init;
+ }
+
+ assert (retry == FS_RETRY_NORMAL);
+ assert (pathbuf[0] == '\0');
+
+ err = ports_create_port (diskfs_initboot_class, diskfs_port_bucket,
+ sizeof (struct port_info), &bootinfo);
+ assert_perror (err);
+ bootpt = ports_get_send_right (bootinfo);
+ ports_port_deref (bootinfo);
+
+ portarray[INIT_PORT_CRDIR] = root_pt;
+ portarray[INIT_PORT_CWDIR] = root_pt;
+ portarray[INIT_PORT_AUTH] = MACH_PORT_NULL;
+ portarray[INIT_PORT_PROC] = MACH_PORT_NULL;
+ portarray[INIT_PORT_CTTYID] = MACH_PORT_NULL;
+ portarray[INIT_PORT_BOOTSTRAP] = bootpt;
+
+ fdarray[0] = fdarray[1] = fdarray[2] = get_console (); /* XXX */
+
+ err = argz_create (environ, &exec_env, &exec_envlen);
+ assert_perror (err);
+
+ err = task_create (mach_task_self (),
+#ifdef KERN_INVALID_LEDGER
+ NULL, 0, /* OSF Mach */
+#endif
+ 0, &newt);
+ assert_perror (err);
+ if (_diskfs_boot_pause)
+ {
+ printf ("pausing for %s...\n", exec_argv);
+ getc (stdin);
+ }
+ printf (" %s", basename (exec_argv));
+ fflush (stdout);
+ err = exec_exec (diskfs_exec, startup_pt, MACH_MSG_TYPE_COPY_SEND,
+ newt, 0, exec_argv, exec_argvlen, exec_env, exec_envlen,
+ fdarray, MACH_MSG_TYPE_COPY_SEND, 3,
+ portarray, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
+ /* Supply no intarray, since we have no info for it.
+ With none supplied, it will use the defaults. */
+ NULL, 0, 0, 0, 0, 0);
+ free (exec_argv);
+ free (exec_env);
+ mach_port_deallocate (mach_task_self (), root_pt);
+ mach_port_deallocate (mach_task_self (), startup_pt);
+ mach_port_deallocate (mach_task_self (), bootpt);
+ assert_perror (err);
+}
+
+/* We look like an execserver to the execserver itself; it makes this
+ call (as does any task) to get its state. We can't give it all of
+ its ports (we'll provide those with a later call to exec_init). */
+kern_return_t
+diskfs_S_exec_startup_get_info (struct bootinfo *upt,
+ vm_address_t *user_entry,
+ vm_address_t *phdr_data,
+ vm_size_t *phdr_size,
+ vm_address_t *base_addr,
+ vm_size_t *stack_size,
+ int *flags,
+ char **argvP,
+ mach_msg_type_number_t *argvlen,
+ char **envpP __attribute__ ((unused)),
+ mach_msg_type_number_t *envplen,
+ mach_port_t **dtableP,
+ mach_msg_type_name_t *dtablepoly,
+ mach_msg_type_number_t *dtablelen,
+ mach_port_t **portarrayP,
+ mach_msg_type_name_t *portarraypoly,
+ mach_msg_type_number_t *portarraylen,
+ int **intarrayP,
+ mach_msg_type_number_t *intarraylen)
+{
+ error_t err;
+ mach_port_t *portarray, *dtable;
+ mach_port_t rootport;
+ struct protid *rootpi;
+ struct peropen *rootpo;
+
+ if (! upt)
+ return EOPNOTSUPP;
+
+ *user_entry = 0;
+ *phdr_data = *base_addr = 0;
+ *phdr_size = *stack_size = 0;
+
+ /* We have no args for it. Tell it to look on its stack
+ for the args placed there by the boot loader. */
+ *argvlen = *envplen = 0;
+ *flags = EXEC_STACK_ARGS;
+
+ if (*portarraylen < INIT_PORT_MAX)
+ *portarrayP = mmap (0, INIT_PORT_MAX * sizeof (mach_port_t),
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ portarray = *portarrayP;
+ *portarraylen = INIT_PORT_MAX;
+
+ if (*dtablelen < 3)
+ *dtableP = mmap (0, 3 * sizeof (mach_port_t), PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ dtable = *dtableP;
+ *dtablelen = 3;
+ dtable[0] = dtable[1] = dtable[2] = get_console (); /* XXX */
+
+ *intarrayP = NULL;
+ *intarraylen = 0;
+
+ err = diskfs_make_peropen (diskfs_root_node, O_READ | O_EXEC, 0, &rootpo);
+ assert_perror (err);
+
+ err = diskfs_create_protid (rootpo, 0, &rootpi);
+ assert_perror (err);
+
+ rootport = ports_get_right (rootpi);
+ ports_port_deref (rootpi);
+ portarray[INIT_PORT_CWDIR] = rootport;
+ portarray[INIT_PORT_CRDIR] = rootport;
+ portarray[INIT_PORT_AUTH] = MACH_PORT_NULL;
+ portarray[INIT_PORT_PROC] = MACH_PORT_NULL;
+ portarray[INIT_PORT_CTTYID] = MACH_PORT_NULL;
+ portarray[INIT_PORT_BOOTSTRAP] = upt->pi.port_right; /* use the same port */
+
+ *portarraypoly = MACH_MSG_TYPE_MAKE_SEND;
+
+ *dtablepoly = MACH_MSG_TYPE_COPY_SEND;
+
+ return 0;
+}
+
+/* Called by S_fsys_startup for execserver bootstrap. The execserver
+ is able to function without a real node, hence this fraud. */
+error_t
+diskfs_execboot_fsys_startup (mach_port_t port, int flags,
+ mach_port_t ctl,
+ mach_port_t *real,
+ mach_msg_type_name_t *realpoly)
+{
+ error_t err;
+ string_t pathbuf;
+ enum retry_type retry;
+ struct port_info *pt;
+ struct protid *rootpi;
+ struct peropen *rootpo;
+ mach_port_t rootport;
+
+ if (!(pt = ports_lookup_port (diskfs_port_bucket, port,
+ diskfs_execboot_class)))
+ return EOPNOTSUPP;
+
+ err = diskfs_make_peropen (diskfs_root_node, flags, 0, &rootpo);
+ assert_perror (err);
+ err = diskfs_create_protid (rootpo, 0, &rootpi);
+ assert_perror (err);
+ rootport = ports_get_send_right (rootpi);
+ ports_port_deref (rootpi);
+
+ err = dir_lookup (rootport, _SERVERS_EXEC, flags|O_NOTRANS, 0,
+ &retry, pathbuf, real);
+ assert_perror (err);
+ assert (retry == FS_RETRY_NORMAL);
+ assert (pathbuf[0] == '\0');
+ *realpoly = MACH_MSG_TYPE_MOVE_SEND;
+
+ mach_port_deallocate (mach_task_self (), rootport);
+
+ diskfs_exec_ctl = ctl;
+
+ pthread_mutex_lock (&execstartlock);
+ pthread_cond_signal (&execstarted);
+ pthread_mutex_unlock (&execstartlock);
+ ports_port_deref (pt);
+ return 0;
+}
+
+/* Called by init to get the privileged ports as described
+ in <hurd/fsys.defs>. */
+kern_return_t
+diskfs_S_fsys_getpriv (struct diskfs_control *init_bootstrap_port,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mach_port_t *host_priv, mach_msg_type_name_t *hp_type,
+ mach_port_t *dev_master, mach_msg_type_name_t *dm_type,
+ mach_port_t *fstask, mach_msg_type_name_t *task_type)
+{
+ error_t err;
+
+ if (!init_bootstrap_port
+ || init_bootstrap_port->pi.class != diskfs_initboot_class)
+ return EOPNOTSUPP;
+
+ err = get_privileged_ports (host_priv, dev_master);
+ if (!err)
+ {
+ *fstask = mach_task_self ();
+ *hp_type = *dm_type = MACH_MSG_TYPE_MOVE_SEND;
+ *task_type = MACH_MSG_TYPE_COPY_SEND;
+ }
+
+ return err;
+}
+
+/* Called by init to give us ports to the procserver and authserver as
+ described in <hurd/fsys.defs>. */
+kern_return_t
+diskfs_S_fsys_init (struct diskfs_control *pt,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ mach_port_t procserver,
+ mach_port_t authhandle)
+{
+ static int initdone = 0;
+ mach_port_t host, startup;
+ error_t err;
+ mach_port_t root_pt;
+ struct protid *rootpi;
+ struct peropen *rootpo;
+
+ if (!pt
+ || pt->pi.class != diskfs_initboot_class)
+ return EOPNOTSUPP;
+
+ if (initdone)
+ return EOPNOTSUPP;
+ initdone = 1;
+
+ /* init is single-threaded, so we must reply to its RPC before doing
+ anything which might attempt to send an RPC to init. */
+ fsys_init_reply (reply, replytype, 0);
+
+ /* Allocate our references here; _hurd_init will consume a reference
+ for the library itself. */
+ err = mach_port_mod_refs (mach_task_self (),
+ procserver, MACH_PORT_RIGHT_SEND, +1);
+ assert_perror (err);
+ err = mach_port_mod_refs (mach_task_self (),
+ authhandle, MACH_PORT_RIGHT_SEND, +1);
+ assert_perror (err);
+
+ if (diskfs_auth_server_port != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), diskfs_auth_server_port);
+ diskfs_auth_server_port = authhandle;
+
+ if (diskfs_exec_server_task != MACH_PORT_NULL)
+ {
+ process_t execprocess;
+ err = proc_task2proc (procserver, diskfs_exec_server_task, &execprocess);
+ assert_perror (err);
+
+ /* Declare that the exec server is our child. */
+ proc_child (procserver, diskfs_exec_server_task);
+ proc_mark_exec (execprocess);
+
+ /* Don't start this until now so that exec is fully authenticated
+ with proc. */
+ HURD_PORT_USE (&_diskfs_exec_portcell,
+ exec_init (port, authhandle,
+ execprocess, MACH_MSG_TYPE_COPY_SEND));
+ mach_port_deallocate (mach_task_self (), execprocess);
+
+ /* We don't need this anymore. */
+ mach_port_deallocate (mach_task_self (), diskfs_exec_server_task);
+ diskfs_exec_server_task = MACH_PORT_NULL;
+ }
+ else
+ {
+ mach_port_t bootstrap;
+ process_t parent_proc;
+
+ assert (parent_task != MACH_PORT_NULL);
+
+ /* Tell the proc server that our parent task is our child. This
+ makes the process hierarchy fail to represent the real order of
+ who created whom, but it sets the owner and authentication ids to
+ root. It doesn't really matter that the parent fs task be
+ authenticated, but the exec server needs to be authenticated to
+ complete the boot handshakes with init. The exec server gets its
+ privilege by the parent fs doing proc_child (code above) after
+ we send it fsys_init (below). */
+
+ err = proc_child (procserver, parent_task);
+ assert_perror (err);
+
+ /* Get the parent's proc server port so we can send it in the fsys_init
+ RPC just as init would. */
+ err = proc_task2proc (procserver, parent_task, &parent_proc);
+ assert_perror (err);
+
+ /* We don't need this anymore. */
+ mach_port_deallocate (mach_task_self (), parent_task);
+ parent_task = MACH_PORT_NULL;
+
+ proc_mark_exec (parent_proc);
+
+ /* Give our parent (the real bootstrap filesystem) an fsys_init
+ RPC of its own, as init would have sent it. */
+ err = task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ assert_perror (err);
+ err = fsys_init (bootstrap, parent_proc, MACH_MSG_TYPE_COPY_SEND,
+ authhandle);
+ mach_port_deallocate (mach_task_self (), parent_proc);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ assert_perror (err);
+ }
+
+ /* Get a port to the root directory to put in the library's
+ data structures. */
+ err = diskfs_make_peropen (diskfs_root_node, O_READ|O_EXEC, 0, &rootpo);
+ assert_perror (err);
+ err = diskfs_create_protid (rootpo, 0, &rootpi);
+ assert_perror (err);
+ root_pt = ports_get_send_right (rootpi);
+ ports_port_deref (rootpi);
+
+ /* We need two send rights, for the crdir and cwdir slots. */
+ mach_port_mod_refs (mach_task_self (), root_pt,
+ MACH_PORT_RIGHT_SEND, +1);
+
+ if (_hurd_ports)
+ {
+ /* We already have a portarray, because somebody responded to
+ exec_startup on our initial bootstrap port, even though we are
+ supposedly the bootstrap program. The losing `boot' that runs on
+ UX does this. */
+ _hurd_port_set (&_hurd_ports[INIT_PORT_PROC], procserver); /* Consume. */
+ _hurd_port_set (&_hurd_ports[INIT_PORT_AUTH], authhandle); /* Consume. */
+ _hurd_port_set (&_hurd_ports[INIT_PORT_CRDIR], root_pt); /* Consume. */
+ _hurd_port_set (&_hurd_ports[INIT_PORT_CWDIR], root_pt); /* Consume. */
+ _hurd_proc_init (diskfs_argv, NULL, 0);
+ }
+ else
+ {
+ /* We have no portarray or intarray because there was
+ no exec_startup data; _hurd_init was never called.
+ We now have the crucial ports, so create a portarray
+ and call _hurd_init. */
+ mach_port_t *portarray;
+ unsigned int i;
+ portarray = mmap (0, INIT_PORT_MAX * sizeof *portarray,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (MACH_PORT_NULL != (mach_port_t) 0)
+ for (i = 0; i < INIT_PORT_MAX; ++i)
+ portarray[i] = MACH_PORT_NULL;
+ portarray[INIT_PORT_PROC] = procserver;
+ portarray[INIT_PORT_AUTH] = authhandle;
+ portarray[INIT_PORT_CRDIR] = root_pt;
+ portarray[INIT_PORT_CWDIR] = root_pt;
+ _hurd_init (0, diskfs_argv, portarray, INIT_PORT_MAX, NULL, 0);
+ }
+
+ err = get_privileged_ports (&host, 0);
+ if (err)
+ return err;
+
+ proc_register_version (procserver, host, diskfs_server_name, "",
+ diskfs_server_version);
+
+ err = proc_getmsgport (procserver, HURD_PID_STARTUP, &startup);
+ if (!err)
+ {
+ startup_essential_task (startup, mach_task_self (), MACH_PORT_NULL,
+ diskfs_server_name, host);
+ mach_port_deallocate (mach_task_self (), startup);
+ }
+
+ mach_port_deallocate (mach_task_self (), host);
+ mach_port_deallocate (mach_task_self (), procserver);
+
+ _diskfs_init_completed ();
+
+ return MIG_NO_REPLY; /* Already replied above. */
+}
+
+/* Start the execserver running (when we are a bootstrap filesystem). */
+static void
+start_execserver (void)
+{
+ error_t err;
+ mach_port_t right;
+ extern task_t diskfs_exec_server_task; /* Set in opts-std-startup.c. */
+ struct port_info *execboot_info;
+
+ assert (diskfs_exec_server_task != MACH_PORT_NULL);
+
+ err = ports_create_port (diskfs_execboot_class, diskfs_port_bucket,
+ sizeof (struct port_info), &execboot_info);
+ assert_perror (err);
+ right = ports_get_send_right (execboot_info);
+ ports_port_deref (execboot_info);
+ task_set_special_port (diskfs_exec_server_task, TASK_BOOTSTRAP_PORT, right);
+ mach_port_deallocate (mach_task_self (), right);
+
+ if (_diskfs_boot_pause)
+ {
+ printf ("pausing for exec\n");
+ getc (stdin);
+ }
+ task_resume (diskfs_exec_server_task);
+
+ printf (" exec");
+ fflush (stdout);
+}
diff --git a/libdiskfs/conch-fetch.c b/libdiskfs/conch-fetch.c
new file mode 100644
index 00000000..43a0e870
--- /dev/null
+++ b/libdiskfs/conch-fetch.c
@@ -0,0 +1,71 @@
+/*
+ Copyright (C) 1994, 1995, 1996, 1999 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <hurd/iohelp.h>
+
+/* Update our copy of the relevant fields from a shared page. Callers
+ must have the share lock on the shared page as well as the inode
+ toplock. This is called by the conch management facilities of
+ libioserver as well as by us. */
+void
+iohelp_fetch_shared_data (void *arg)
+{
+ struct protid *cred = arg;
+ int mod = 0;
+
+ /* Don't allow the user to grow the file past the alloc size. */
+ if (cred->mapped->file_size > cred->po->np->allocsize)
+ cred->mapped->file_size = cred->po->np->allocsize;
+
+ /* Don't allow the user to truncate the file this way. */
+ if (cred->mapped->file_size < cred->po->np->dn_stat.st_size)
+ cred->mapped->file_size = cred->po->np->dn_stat.st_size;
+ else if (cred->po->np->dn_stat.st_size != cred->mapped->file_size)
+ {
+ /* The user can validly set the size, but block the attempt
+ if we are readonly. */
+ if (diskfs_check_readonly ())
+ cred->mapped->file_size = cred->po->np->dn_stat.st_size;
+ else
+ {
+ cred->po->np->dn_stat.st_size = cred->mapped->file_size;
+ cred->po->np->dn_set_ctime = 1;
+ mod = 1;
+ }
+ }
+
+ cred->po->filepointer = cred->mapped->xx_file_pointer;
+
+ if (!diskfs_check_readonly ())
+ {
+ if (cred->mapped->written)
+ {
+ cred->po->np->dn_set_mtime = 1;
+ mod = 1;
+ }
+ if (cred->mapped->accessed && ! _diskfs_noatime)
+ {
+ cred->po->np->dn_set_atime = 1;
+ mod = 1;
+ }
+ }
+ cred->mapped->written = 0;
+ cred->mapped->accessed = 0;
+ if (diskfs_synchronous && mod)
+ diskfs_node_update (cred->po->np, 1);
+}
diff --git a/libdiskfs/conch-set.c b/libdiskfs/conch-set.c
new file mode 100644
index 00000000..86d3905d
--- /dev/null
+++ b/libdiskfs/conch-set.c
@@ -0,0 +1,49 @@
+/*
+ Copyright (C) 1994, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <hurd/iohelp.h>
+#include <fcntl.h>
+
+/* Write current values into the shared page. Callers must have the
+ share lock on the shared page, as well as the inode toplock.
+ This is called by the conch management facilities of libioserver
+ as well as by us. */
+void
+iohelp_put_shared_data (void *arg)
+{
+ struct protid *cred = arg;
+
+ cred->mapped->append_mode = (cred->po->openstat & O_APPEND);
+ cred->mapped->eof_notify = 0;
+ cred->mapped->do_sigio = (cred->po->openstat & O_FSYNC);
+ cred->mapped->use_file_size = 1;
+ cred->mapped->use_read_size = 0;
+ cred->mapped->optimal_transfer_size = cred->po->np->dn_stat.st_blksize;
+ cred->mapped->seekable = 1;
+ cred->mapped->use_prenotify_size = 1;
+ cred->mapped->use_postnotify_size = 0;
+ cred->mapped->use_readnotify_size = 0;
+ cred->mapped->prenotify_size = cred->po->np->allocsize;
+
+ cred->mapped->xx_file_pointer = cred->po->filepointer;
+ cred->mapped->rd_file_pointer = -1;
+ cred->mapped->wr_file_pointer = -1;
+ cred->mapped->file_size = cred->po->np->dn_stat.st_size;
+ cred->mapped->written = 0;
+ cred->mapped->accessed = 0;
+}
diff --git a/libdiskfs/console.c b/libdiskfs/console.c
new file mode 100644
index 00000000..a4c3a1af
--- /dev/null
+++ b/libdiskfs/console.c
@@ -0,0 +1,71 @@
+/* Redirect stdio to the console if possible
+
+ Copyright (C) 1995,96,98,99,2001 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <mach/mach.h>
+#include <device/device.h>
+#include <hurd.h>
+
+#include "priv.h"
+
+/* Make sure errors go somewhere reasonable. */
+void
+diskfs_console_stdio ()
+{
+ if (getpid () > 0)
+ {
+ if (write (2, "", 0) == 0)
+ /* We have a working stderr from our parent (e.g. settrans -a).
+ Just use it. */
+ dup2 (2, 1);
+ else
+ {
+ int fd = open ("/dev/console", O_RDWR);
+
+ dup2 (fd, 0);
+ dup2 (fd, 1);
+ dup2 (fd, 2);
+ if (fd > 2)
+ close (fd);
+ }
+ }
+ else
+ {
+ mach_port_t dev, cons;
+ error_t err;
+ if (diskfs_boot_filesystem ())
+ _diskfs_boot_privports ();
+ err = get_privileged_ports (NULL, &dev);
+ assert_perror (err);
+ err = device_open (dev, D_READ|D_WRITE, "console", &cons);
+ mach_port_deallocate (mach_task_self (), dev);
+ assert_perror (err);
+ stdin = mach_open_devstream (cons, "r");
+ stdout = stderr = mach_open_devstream (cons, "w");
+ mach_port_deallocate (mach_task_self (), cons);
+ }
+}
diff --git a/libdiskfs/dead-name.c b/libdiskfs/dead-name.c
new file mode 100644
index 00000000..6ca208e4
--- /dev/null
+++ b/libdiskfs/dead-name.c
@@ -0,0 +1,47 @@
+/* Handle dead name notifications on ports
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+void
+ports_dead_name (void *notify, mach_port_t dead_name)
+{
+ struct protid *pi = ports_lookup_port (diskfs_port_bucket, dead_name,
+ diskfs_protid_class);
+ struct node *np;
+
+ if (pi)
+ {
+ np = pi->po->np;
+ pthread_mutex_lock (&np->lock);
+ if (dead_name == np->sockaddr)
+ {
+ mach_port_deallocate (mach_task_self (), np->sockaddr);
+ np->sockaddr = MACH_PORT_NULL;
+ diskfs_nput (np);
+ }
+ else
+ pthread_mutex_unlock (&np->lock);
+ }
+
+ fshelp_remove_active_translator (dead_name);
+
+ ports_interrupt_notified_rpcs (notify, dead_name, MACH_NOTIFY_DEAD_NAME);
+}
diff --git a/libdiskfs/demuxer.c b/libdiskfs/demuxer.c
new file mode 100644
index 00000000..4a1c4fb4
--- /dev/null
+++ b/libdiskfs/demuxer.c
@@ -0,0 +1,49 @@
+/* Demultiplexer for diskfs library
+ Copyright (C) 1994, 1995, 1996, 2013 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+#include "io_S.h"
+#include "fs_S.h"
+#include "../libports/notify_S.h"
+#include "fsys_S.h"
+#include "../libports/interrupt_S.h"
+#include "ifsock_S.h"
+#include "startup_notify_S.h"
+#include "exec_startup_S.h"
+
+int
+diskfs_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ mig_routine_t routine;
+ if ((routine = diskfs_io_server_routine (inp)) ||
+ (routine = diskfs_fs_server_routine (inp)) ||
+ (routine = ports_notify_server_routine (inp)) ||
+ (routine = diskfs_fsys_server_routine (inp)) ||
+ (routine = ports_interrupt_server_routine (inp)) ||
+ (diskfs_shortcut_ifsock ?
+ (routine = diskfs_ifsock_server_routine (inp)) : 0) ||
+ (routine = diskfs_startup_notify_server_routine (inp)) ||
+ (routine = diskfs_exec_startup_server_routine (inp)))
+ {
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
diff --git a/libdiskfs/dir-chg.c b/libdiskfs/dir-chg.c
new file mode 100644
index 00000000..ed8f40c1
--- /dev/null
+++ b/libdiskfs/dir-chg.c
@@ -0,0 +1,82 @@
+/* Notifications of directory changes.
+ Copyright (C) 1994, 1995, 1998, 2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+#include "fs_notify_U.h"
+
+kern_return_t
+diskfs_S_dir_notice_changes (struct protid *cred,
+ mach_port_t notify)
+{
+ error_t err;
+ struct modreq *req;
+ struct node *np;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+ pthread_mutex_lock (&np->lock);
+ if (!S_ISDIR (np->dn_stat.st_mode))
+ {
+ pthread_mutex_unlock (&np->lock);
+ return ENOTDIR;
+ }
+ err = dir_changed (notify, np->dirmod_tick, DIR_CHANGED_NULL, "");
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return err;
+ }
+ req = malloc (sizeof (struct modreq));
+ if (! req)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return ENOMEM;
+ }
+ req->port = notify;
+ req->next = np->dirmod_reqs;
+ np->dirmod_reqs = req;
+ pthread_mutex_unlock (&np->lock);
+ return 0;
+}
+
+void
+diskfs_notice_dirchange (struct node *dp, enum dir_changed_type type,
+ const char *name)
+{
+ error_t err;
+ struct modreq **preq;
+
+ dp->dirmod_tick++;
+ preq = &dp->dirmod_reqs;
+ while (*preq)
+ {
+ struct modreq *req = *preq;
+ err = dir_changed (req->port, dp->dirmod_tick, type, name);
+ if (err && err != MACH_SEND_TIMED_OUT)
+ {
+ /* Remove notify port. */
+ *preq = req->next;
+ mach_port_deallocate (mach_task_self (), req->port);
+ free (req);
+ }
+ else
+ preq = &req->next;
+ }
+}
diff --git a/libdiskfs/dir-clear.c b/libdiskfs/dir-clear.c
new file mode 100644
index 00000000..7cf32358
--- /dev/null
+++ b/libdiskfs/dir-clear.c
@@ -0,0 +1,70 @@
+/*
+ Copyright (C) 1994, 1995, 1996, 1997 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* Clear the `.' and `..' entries from directory DP. Its parent is PDP,
+ and the user responsible for this is identified by CRED. Both
+ directories must be locked. */
+error_t
+diskfs_clear_directory (struct node *dp,
+ struct node *pdp,
+ struct protid *cred)
+{
+ error_t err;
+ struct dirstat *ds = alloca (diskfs_dirstat_size);
+ struct node *np;
+
+ /* Find and remove the `.' entry. */
+ err = diskfs_lookup (dp, ".", REMOVE, &np, ds, cred);
+ assert (err != ENOENT);
+ if (!err)
+ {
+ assert (np == dp);
+ err = diskfs_dirremove (dp, np, ".", ds);
+ diskfs_nrele (np);
+ }
+ else
+ diskfs_drop_dirstat (dp, ds);
+ if (err)
+ return err;
+
+ /* Decrement the link count */
+ dp->dn_stat.st_nlink--;
+ dp->dn_set_ctime = 1;
+
+ /* Find and remove the `..' entry. */
+ err = diskfs_lookup (dp, "..", REMOVE | SPEC_DOTDOT, &np, ds, cred);
+ assert (err != ENOENT);
+ if (!err)
+ {
+ assert (np == pdp);
+ err = diskfs_dirremove (dp, np, "..", ds);
+ }
+ else
+ diskfs_drop_dirstat (dp, ds);
+ if (err)
+ return err;
+
+ /* Decrement the link count on the parent */
+ pdp->dn_stat.st_nlink--;
+ pdp->dn_set_ctime = 1;
+
+ diskfs_truncate (dp, 0);
+
+ return err;
+}
diff --git a/libdiskfs/dir-init.c b/libdiskfs/dir-init.c
new file mode 100644
index 00000000..4efded07
--- /dev/null
+++ b/libdiskfs/dir-init.c
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) 1994, 1995, 1996, 1997 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* Locked node DP is a new directory; add whatever links are necessary
+ to give it structure; its parent is the (locked) node PDP.
+ This routine may not call diskfs_lookup on PDP. The new directory
+ must be clear within the meaning of diskfs_dirempty.
+ CRED identifies the user making the call. */
+error_t
+diskfs_init_dir (struct node *dp, struct node *pdp, struct protid *cred)
+{
+ struct dirstat *ds = alloca (diskfs_dirstat_size);
+ struct node *foo;
+ error_t err;
+
+ /* Fabricate a protid that represents root credentials. */
+ static uid_t zero = 0;
+ static struct idvec vec = {&zero, 1, 1};
+ static struct iouser user = {&vec, &vec, 0};
+ struct protid lookupcred = {{0, 0, 0, 0}, &user, cred->po, 0, 0};
+
+ /* New links */
+ if (pdp->dn_stat.st_nlink == diskfs_link_max - 1)
+ return EMLINK;
+
+ dp->dn_stat.st_nlink++; /* for `.' */
+ dp->dn_set_ctime = 1;
+ err = diskfs_lookup (dp, ".", CREATE, &foo, ds, &lookupcred);
+ assert (err == ENOENT);
+ err = diskfs_direnter (dp, ".", dp, ds, cred);
+ if (err)
+ {
+ dp->dn_stat.st_nlink--;
+ dp->dn_set_ctime = 1;
+ return err;
+ }
+
+ pdp->dn_stat.st_nlink++; /* for `..' */
+ pdp->dn_set_ctime = 1;
+ err = diskfs_lookup (dp, "..", CREATE, &foo, ds, &lookupcred);
+ assert (err == ENOENT);
+ err = diskfs_direnter (dp, "..", pdp, ds, cred);
+ if (err)
+ {
+ pdp->dn_stat.st_nlink--;
+ pdp->dn_set_ctime = 1;
+ return err;
+ }
+
+ diskfs_node_update (dp, diskfs_synchronous);
+ return 0;
+}
diff --git a/libdiskfs/dir-link.c b/libdiskfs/dir-link.c
new file mode 100644
index 00000000..ca5dd561
--- /dev/null
+++ b/libdiskfs/dir-link.c
@@ -0,0 +1,133 @@
+/* libdiskfs implementation of fs.defs: dir_link
+ Copyright (C) 1992,93,94,95,96,97,99 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+/* Implement dir_link as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_dir_link (struct protid *dircred,
+ struct protid *filecred,
+ char *name,
+ int excl)
+{
+ struct node *np; /* node being linked */
+ struct node *tnp; /* node being deleted implicitly */
+ struct node *dnp; /* directory of new entry */
+ struct dirstat *ds = alloca (diskfs_dirstat_size);
+ error_t err;
+
+ if (!dircred)
+ return EOPNOTSUPP;
+
+ if (diskfs_check_readonly ())
+ return EROFS;
+
+ if (!filecred)
+ return EXDEV;
+
+ np = filecred->po->np;
+ pthread_mutex_lock (&np->lock);
+ if (S_ISDIR (np->dn_stat.st_mode))
+ {
+ pthread_mutex_unlock (&np->lock);
+ return EPERM;
+ }
+ pthread_mutex_unlock (&np->lock);
+
+ dnp = dircred->po->np;
+ pthread_mutex_lock (&dnp->lock);
+
+ /* Lookup new location */
+ err = diskfs_lookup (dnp, name, RENAME, &tnp, ds, dircred);
+ if (!err && excl)
+ {
+ err = EEXIST;
+ diskfs_nput (tnp);
+ }
+ if (err && err != ENOENT)
+ {
+ if (err == EAGAIN)
+ err = EINVAL;
+ diskfs_drop_dirstat (dnp, ds);
+ pthread_mutex_unlock (&dnp->lock);
+ return err;
+ }
+
+ if (np == tnp)
+ {
+ diskfs_drop_dirstat (dnp, ds);
+ pthread_mutex_unlock (&dnp->lock);
+ pthread_mutex_unlock (&tnp->lock);
+ mach_port_deallocate (mach_task_self (), filecred->pi.port_right);
+ return 0;
+ }
+
+ if (tnp && S_ISDIR (tnp->dn_stat.st_mode))
+ {
+ diskfs_drop_dirstat (dnp, ds);
+ pthread_mutex_unlock (&dnp->lock);
+ pthread_mutex_unlock (&tnp->lock);
+ return EISDIR;
+ }
+
+ /* Create new entry for NP */
+
+ /* This is safe because NP is not a directory (thus not DNP) and
+ not TNP and is a leaf. */
+ pthread_mutex_lock (&np->lock);
+
+ /* Increment link count */
+ if (np->dn_stat.st_nlink == diskfs_link_max - 1)
+ {
+ diskfs_drop_dirstat (dnp, ds);
+ pthread_mutex_unlock (&np->lock);
+ pthread_mutex_unlock (&dnp->lock);
+ return EMLINK;
+ }
+ np->dn_stat.st_nlink++;
+ np->dn_set_ctime = 1;
+ diskfs_node_update (np, diskfs_synchronous);
+
+ /* Attach it */
+ if (tnp)
+ {
+ assert (!excl);
+ err = diskfs_dirrewrite (dnp, tnp, np, name, ds);
+ if (!err)
+ {
+ /* Deallocate link on TNP */
+ tnp->dn_stat.st_nlink--;
+ tnp->dn_set_ctime = 1;
+ if (diskfs_synchronous)
+ diskfs_node_update (tnp, 1);
+ }
+ diskfs_nput (tnp);
+ }
+ else
+ err = diskfs_direnter (dnp, name, np, ds, dircred);
+
+ if (diskfs_synchronous)
+ diskfs_node_update (dnp, 1);
+
+ pthread_mutex_unlock (&dnp->lock);
+ pthread_mutex_unlock (&np->lock);
+ if (!err)
+ /* MiG won't do this for us, which it ought to. */
+ mach_port_deallocate (mach_task_self (), filecred->pi.port_right);
+ return err;
+}
diff --git a/libdiskfs/dir-lookup.c b/libdiskfs/dir-lookup.c
new file mode 100644
index 00000000..3950bf9c
--- /dev/null
+++ b/libdiskfs/dir-lookup.c
@@ -0,0 +1,561 @@
+/* libdiskfs implementation of fs.defs:dir_lookup
+ Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ 2002, 2008, 2013, 2014 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/file.h>
+#include <hurd/fsys.h>
+#include <hurd/paths.h>
+
+#include "priv.h"
+#include "fs_S.h"
+
+/* Implement dir_lookup as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_dir_lookup (struct protid *dircred,
+ char *path,
+ int flags,
+ mode_t mode,
+ enum retry_type *retry,
+ char *retryname,
+ file_t *returned_port,
+ mach_msg_type_name_t *returned_port_poly)
+{
+ struct node *dnp;
+ struct node *np;
+ int nsymlink = 0;
+ char *nextname;
+ char *relpath;
+ int nextnamelen;
+ error_t error = 0;
+ char *pathbuf = 0;
+ int pathbuflen = 0;
+ int newnamelen;
+ int create, excl;
+ int lastcomp = 0;
+ int newnode = 0;
+ struct dirstat *ds = 0;
+ int mustbedir = 0;
+ size_t amt;
+ int type;
+ struct protid *newpi = 0;
+ struct peropen *newpo = 0;
+
+ if (!dircred)
+ return EOPNOTSUPP;
+
+ flags &= O_HURD;
+
+ create = (flags & O_CREAT);
+ excl = (flags & O_EXCL);
+
+ /* Skip leading slashes */
+ while (path[0] == '/')
+ path++;
+
+ /* Preserve the path relative to diruser->po->path. */
+ relpath = strdup (path);
+ if (! relpath)
+ return ENOMEM;
+
+ /* Keep a pointer to the start of the path for length
+ calculations. */
+ char *path_start = path;
+
+ *returned_port_poly = MACH_MSG_TYPE_MAKE_SEND;
+ *retry = FS_RETRY_NORMAL;
+ retryname[0] = '\0';
+
+ if (path[0] == '\0')
+ {
+ /* Set things up in the state expected by the code from gotit: on. */
+ dnp = 0;
+ np = dircred->po->np;
+ pthread_mutex_lock (&np->lock);
+ diskfs_nref (np);
+ goto gotit;
+ }
+
+ dnp = dircred->po->np;
+
+ pthread_mutex_lock (&dnp->lock);
+ np = 0;
+
+ diskfs_nref (dnp); /* acquire a reference for later diskfs_nput */
+
+ do
+ {
+ assert (!lastcomp);
+
+ /* Find the name of the next pathname component */
+ nextname = index (path, '/');
+
+ if (nextname)
+ {
+ *nextname++ = '\0';
+ while (*nextname == '/')
+ nextname++;
+ if (*nextname == '\0')
+ {
+ /* These are the rules for filenames ending in /. */
+ nextname = 0;
+ lastcomp = 1;
+ mustbedir = 1;
+ create = 0;
+ }
+ else
+ lastcomp = 0;
+ }
+ else
+ lastcomp = 1;
+
+ np = 0;
+
+ /* diskfs_lookup the next pathname component */
+ if (lastcomp && create)
+ {
+ if (!ds)
+ ds = alloca (diskfs_dirstat_size);
+ error = diskfs_lookup (dnp, path, CREATE, &np, ds, dircred);
+ }
+ else
+ error = diskfs_lookup (dnp, path, LOOKUP, &np, 0, dircred);
+
+ if (lastcomp && create && excl && (!error || error == EAGAIN))
+ error = EEXIST;
+
+ /* If we get an error we're done */
+ if (error == EAGAIN)
+ {
+ if (dnp == dircred->po->shadow_root)
+ /* We're at the root of a shadow tree. */
+ {
+ if (dircred->po->shadow_root_parent == MACH_PORT_NULL)
+ {
+ /* This is a shadow root with no parent, meaning
+ we should treat it as a virtual root disconnected
+ from its real .. directory. */
+ error = 0;
+ np = dnp;
+ diskfs_nref (np);
+ }
+ else
+ {
+ /* Punt the client up to the shadow root parent. */
+ *retry = FS_RETRY_REAUTH;
+ *returned_port = dircred->po->shadow_root_parent;
+ *returned_port_poly = MACH_MSG_TYPE_COPY_SEND;
+ if (! lastcomp)
+ strcpy (retryname, nextname);
+ error = 0;
+ goto out;
+ }
+ }
+ else if (dircred->po->root_parent != MACH_PORT_NULL)
+ /* We're at a real translator root; even if DIRCRED->po has a
+ shadow root, we can get here if its in a directory that was
+ renamed out from under it... */
+ {
+ *retry = FS_RETRY_REAUTH;
+ *returned_port = dircred->po->root_parent;
+ *returned_port_poly = MACH_MSG_TYPE_COPY_SEND;
+ if (!lastcomp)
+ strcpy (retryname, nextname);
+ error = 0;
+ goto out;
+ }
+ else
+ /* We're at a REAL root, as in there's no way up from here. */
+ {
+ error = 0;
+ np = dnp;
+ diskfs_nref (np);
+ }
+ }
+
+ /* Create the new node if necessary */
+ if (lastcomp && create)
+ {
+ if (error == ENOENT)
+ {
+ mode &= ~(S_IFMT | S_ISPARE | S_ISVTX | S_ITRANS);
+ mode |= S_IFREG;
+ error = diskfs_create_node (dnp, path, mode, &np, dircred, ds);
+ if (diskfs_synchronous)
+ {
+ diskfs_file_update (dnp, 1);
+ diskfs_file_update (np, 1);
+ }
+ newnode = 1;
+ }
+ else
+ diskfs_drop_dirstat (dnp, ds);
+ }
+
+ if (error)
+ goto out;
+
+ /* If this is translated, start the translator (if necessary)
+ and return. */
+ if ((((flags & O_NOTRANS) == 0) || !lastcomp)
+ && ((np->dn_stat.st_mode & S_IPTRANS)
+ || S_ISFIFO (np->dn_stat.st_mode)
+ || S_ISCHR (np->dn_stat.st_mode)
+ || S_ISBLK (np->dn_stat.st_mode)
+ || fshelp_translated (&np->transbox)))
+ {
+ mach_port_t dirport;
+ struct iouser *user;
+
+ /* A callback function for short-circuited translators.
+ Symlink & ifsock are handled elsewhere. */
+ error_t short_circuited_callback1 (void *cookie1, void *cookie2,
+ uid_t *uid, gid_t *gid,
+ char **argz, size_t *argz_len)
+ {
+ struct node *node = cookie1;
+
+ switch (node->dn_stat.st_mode & S_IFMT)
+ {
+ case S_IFCHR:
+ case S_IFBLK:
+ asprintf (argz, "%s%c%d%c%d",
+ (S_ISCHR (node->dn_stat.st_mode)
+ ? _HURD_CHRDEV : _HURD_BLKDEV),
+ 0, major (node->dn_stat.st_rdev),
+ 0, minor (node->dn_stat.st_rdev));
+ *argz_len = strlen (*argz) + 1;
+ *argz_len += strlen (*argz + *argz_len) + 1;
+ *argz_len += strlen (*argz + *argz_len) + 1;
+ break;
+ case S_IFIFO:
+ asprintf (argz, "%s", _HURD_FIFO);
+ *argz_len = strlen (*argz) + 1;
+ break;
+ default:
+ return ENOENT;
+ }
+
+ *uid = node->dn_stat.st_uid;
+ *gid = node->dn_stat.st_gid;
+
+ return 0;
+ }
+
+ /* Create an unauthenticated port for DNP, and then
+ unlock it. */
+ error = iohelp_create_empty_iouser (&user);
+ if (! error)
+ {
+ error = diskfs_make_peropen (dnp, 0, dircred->po, &newpo);
+ if (! error)
+ {
+ error = diskfs_create_protid (newpo, user, &newpi);
+ if (! error)
+ newpo = 0;
+ }
+
+ iohelp_free_iouser (user);
+ }
+
+ if (error)
+ goto out;
+
+ dirport = ports_get_send_right (newpi);
+ if (np != dnp)
+ pthread_mutex_unlock (&dnp->lock);
+
+ /* Check if an active translator is currently running. If
+ not, fshelp_fetch_root will start one. In that case, we
+ need to register it in the list of active
+ translators. */
+ boolean_t register_translator =
+ np->transbox.active == MACH_PORT_NULL;
+
+ error = fshelp_fetch_root (&np->transbox, dircred->po,
+ dirport, dircred->user,
+ lastcomp ? flags : 0,
+ ((np->dn_stat.st_mode & S_IPTRANS)
+ ? _diskfs_translator_callback1
+ : short_circuited_callback1),
+ _diskfs_translator_callback2,
+ retry, retryname, returned_port);
+
+ /* fetch_root copies DIRPORT for success, so we always should
+ deallocate our send right. */
+ mach_port_deallocate (mach_task_self (), dirport);
+
+ if (error != ENOENT)
+ {
+ *returned_port_poly = MACH_MSG_TYPE_MOVE_SEND;
+ if (!lastcomp && !error)
+ {
+ char *end = strchr (retryname, '\0');
+ *end++ = '/';
+ strcpy (end, nextname);
+ }
+
+ if (register_translator)
+ {
+ char *translator_path = strdupa (relpath);
+ if (nextname != NULL)
+ {
+ /* This was not the last path component.
+ NEXTNAME points to the next component, locate
+ the end of the current component and use it
+ to trim TRANSLATOR_PATH. */
+ char *end = nextname;
+ while (*end != 0)
+ end--;
+ translator_path[end - path_start] = '\0';
+ }
+
+ error = fshelp_set_active_translator (&newpi->pi,
+ translator_path,
+ np->transbox.active);
+ if (error)
+ goto out;
+ }
+
+ goto out;
+ }
+
+ ports_port_deref (newpi);
+ newpi = NULL;
+
+ /* ENOENT means there was a hiccup, and the translator
+ vanished while NP was unlocked inside fshelp_fetch_root.
+ Reacquire the locks, and continue as normal. */
+ error = 0;
+ if (np != dnp)
+ {
+ if (!strcmp (path, ".."))
+ pthread_mutex_lock (&dnp->lock);
+ else
+ {
+ if (pthread_mutex_trylock (&dnp->lock))
+ {
+ pthread_mutex_unlock (&np->lock);
+ pthread_mutex_lock (&dnp->lock);
+ pthread_mutex_lock (&np->lock);
+ }
+ }
+ }
+ }
+
+ if (S_ISLNK (np->dn_stat.st_mode)
+ && (!lastcomp
+ || mustbedir /* "foo/" must see that foo points to a dir */
+ || !(flags & (O_NOLINK|O_NOTRANS))))
+ {
+ /* Handle symlink interpretation */
+
+ if (nsymlink++ > diskfs_maxsymlinks)
+ {
+ error = ELOOP;
+ goto out;
+ }
+
+ nextnamelen = nextname ? strlen (nextname) + 1 : 0;
+ newnamelen = nextnamelen + np->dn_stat.st_size + 1 + 1;
+ if (pathbuflen < newnamelen)
+ {
+ pathbuf = alloca (newnamelen);
+ pathbuflen = newnamelen;
+ }
+
+ if (diskfs_read_symlink_hook)
+ error = (*diskfs_read_symlink_hook)(np, pathbuf);
+ if (!diskfs_read_symlink_hook || error == EINVAL)
+ {
+ error = diskfs_node_rdwr (np, pathbuf,
+ 0, np->dn_stat.st_size, 0,
+ dircred, &amt);
+ if (!error)
+ assert (amt == np->dn_stat.st_size);
+ }
+ if (error)
+ goto out;
+
+ if (np->dn_stat.st_size == 0) /* symlink to "" */
+ path = nextname;
+ else
+ {
+ if (nextname)
+ {
+ pathbuf[np->dn_stat.st_size] = '/';
+ memcpy (pathbuf + np->dn_stat.st_size + 1,
+ nextname, nextnamelen - 1);
+ }
+ pathbuf[nextnamelen + np->dn_stat.st_size] = '\0';
+
+ if (pathbuf[0] == '/')
+ {
+ /* Punt to the caller. */
+ *retry = FS_RETRY_MAGICAL;
+ *returned_port = MACH_PORT_NULL;
+ memcpy (retryname, pathbuf,
+ nextnamelen + np->dn_stat.st_size + 1);
+ if (mustbedir)
+ {
+ retryname[nextnamelen + np->dn_stat.st_size] = '/';
+ retryname[nextnamelen + np->dn_stat.st_size + 1] = '\0';
+ }
+ goto out;
+ }
+
+ path = pathbuf;
+ }
+
+ if (lastcomp)
+ lastcomp = 0;
+
+ diskfs_nput (np);
+ np = 0;
+
+ if (path == 0) /* symlink to "" was the last component */
+ {
+ np = dnp;
+ dnp = 0;
+ break;
+ }
+ }
+ else
+ {
+ /* Handle normal nodes */
+ path = nextname;
+ if (np == dnp)
+ diskfs_nrele (dnp);
+ else
+ diskfs_nput (dnp);
+ if (!lastcomp)
+ {
+ dnp = np;
+ np = 0;
+ }
+ else
+ dnp = 0;
+ }
+ } while (path && *path);
+
+ /* At this point, np is the node to return. If newnode is set, then
+ we just created this node. */
+
+ gotit:
+ type = np->dn_stat.st_mode & S_IFMT;
+
+ if (mustbedir && type != S_IFDIR)
+ {
+ error = ENOTDIR;
+ goto out;
+ }
+
+ if (!newnode)
+ /* Check permissions on existing nodes, but not new ones. */
+ {
+ if (((type == S_IFSOCK || type == S_IFBLK || type == S_IFCHR ||
+ type == S_IFIFO)
+ && (flags & (O_READ|O_WRITE|O_EXEC)))
+ || (type == S_IFLNK && (flags & (O_WRITE|O_EXEC))))
+ error = EACCES;
+
+ if (!error && (flags & O_READ))
+ error = fshelp_access (&np->dn_stat, S_IREAD, dircred->user);
+
+ if (!error && (flags & O_EXEC))
+ error = fshelp_access (&np->dn_stat, S_IEXEC, dircred->user);
+
+ if (!error && (flags & O_WRITE))
+ {
+ if (type == S_IFDIR)
+ error = EISDIR;
+ else if (diskfs_check_readonly ())
+ error = EROFS;
+ else
+ error = fshelp_access (&np->dn_stat, S_IWRITE, dircred->user);
+ }
+
+ if (error)
+ goto out;
+ }
+
+ if ((flags & O_NOATIME)
+ && (fshelp_isowner (&np->dn_stat, dircred->user) == EPERM))
+ flags &= ~O_NOATIME;
+
+ error = diskfs_make_peropen (np, (flags &~OPENONLY_STATE_MODES),
+ dircred->po, &newpo);
+
+ if (! error)
+ error = diskfs_create_protid (newpo, dircred->user, &newpi);
+
+ if (! error)
+ {
+ newpo = 0;
+ if (flags & O_EXLOCK)
+ error = fshelp_acquire_lock (&np->userlock, &newpi->po->lock_status,
+ &np->lock, LOCK_EX);
+ else if (flags & O_SHLOCK)
+ error = fshelp_acquire_lock (&np->userlock, &newpi->po->lock_status,
+ &np->lock, LOCK_SH);
+ }
+
+ if (! error)
+ {
+ free (newpi->po->path);
+ if (dircred->po->path == NULL)
+ {
+ /* dircred is the root directory. */
+ newpi->po->path = relpath;
+ relpath = NULL; /* Do not free relpath. */
+ }
+ else
+ {
+ newpi->po->path = NULL;
+ asprintf (&newpi->po->path, "%s/%s", dircred->po->path, relpath);
+ }
+
+ if (! newpi->po->path)
+ error = errno;
+
+ *returned_port = ports_get_right (newpi);
+ ports_port_deref (newpi);
+ newpi = 0;
+ }
+
+ out:
+ if (np)
+ {
+ if (dnp == np)
+ diskfs_nrele (np);
+ else
+ diskfs_nput (np);
+ }
+ if (dnp)
+ diskfs_nput (dnp);
+
+ if (newpi)
+ ports_port_deref (newpi);
+ if (newpo)
+ diskfs_release_peropen (newpo);
+
+ free (relpath);
+
+ return error;
+}
diff --git a/libdiskfs/dir-mkdir.c b/libdiskfs/dir-mkdir.c
new file mode 100644
index 00000000..1ddb8087
--- /dev/null
+++ b/libdiskfs/dir-mkdir.c
@@ -0,0 +1,70 @@
+/* libdiskfs implementation of fs.defs: dir_mkdir
+ Copyright (C) 1992,93,94,95,96,97,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+/* Implement dir_mkdir as found in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_dir_mkdir (struct protid *dircred,
+ char *name,
+ mode_t mode)
+{
+ struct node *dnp;
+ struct node *np = 0;
+ struct dirstat *ds = alloca (diskfs_dirstat_size);
+ int error;
+
+ if (!dircred)
+ return EOPNOTSUPP;
+
+ dnp = dircred->po->np;
+ if (diskfs_check_readonly ())
+ return EROFS;
+
+ pthread_mutex_lock (&dnp->lock);
+
+ error = diskfs_lookup (dnp, name, CREATE, 0, ds, dircred);
+ if (error == EAGAIN)
+ error = EEXIST;
+ if (!error)
+ error = EEXIST;
+
+ if (error != ENOENT)
+ {
+ diskfs_drop_dirstat (dnp, ds);
+ pthread_mutex_unlock (&dnp->lock);
+ return error;
+ }
+
+ mode &= ~(S_ISPARE | S_IFMT | S_ITRANS);
+ mode |= S_IFDIR;
+
+ error = diskfs_create_node (dnp, name, mode, &np, dircred, ds);
+
+ if (diskfs_synchronous)
+ {
+ diskfs_file_update (dnp, 1);
+ diskfs_file_update (np, 1);
+ }
+
+ if (!error)
+ diskfs_nput (np);
+
+ pthread_mutex_unlock (&dnp->lock);
+ return error;
+}
diff --git a/libdiskfs/dir-mkfile.c b/libdiskfs/dir-mkfile.c
new file mode 100644
index 00000000..a38d89a5
--- /dev/null
+++ b/libdiskfs/dir-mkfile.c
@@ -0,0 +1,91 @@
+/* libdiskfs implementation of fs.defs: dir_mkfile
+ Copyright (C) 1994,95,96,97,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "fs_S.h"
+#include <fcntl.h>
+
+/* Implement dir_mkfile as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_dir_mkfile (struct protid *cred,
+ int flags,
+ mode_t mode,
+ mach_port_t *newnode,
+ mach_msg_type_name_t *newnodetype)
+{
+ struct node *dnp, *np;
+ error_t err;
+ struct protid *newpi;
+ struct peropen *newpo;
+
+ if (!cred)
+ return EOPNOTSUPP;
+ if (diskfs_check_readonly ())
+ return EROFS;
+ dnp = cred->po->np;
+ pthread_mutex_lock (&dnp->lock);
+ if (!S_ISDIR (dnp->dn_stat.st_mode))
+ {
+ pthread_mutex_unlock (&dnp->lock);
+ return ENOTDIR;
+ }
+ err = fshelp_access (&dnp->dn_stat, S_IWRITE, cred->user);
+ if (err)
+ {
+ pthread_mutex_unlock (&dnp->lock);
+ return err;
+ }
+
+ mode &= ~(S_IFMT | S_ISPARE | S_ISVTX | S_ITRANS);
+ mode |= S_IFREG;
+ err = diskfs_create_node (dnp, 0, mode, &np, cred, 0);
+ pthread_mutex_unlock (&dnp->lock);
+
+ if (diskfs_synchronous)
+ {
+ diskfs_file_update (dnp, 1);
+ diskfs_file_update (np, 1);
+ }
+
+ if (err)
+ return err;
+
+ flags &= ~OPENONLY_STATE_MODES; /* These bits are all meaningless here. */
+
+ err = diskfs_make_peropen (np, flags, cred->po, &newpo);
+ if (! err)
+ {
+ err = diskfs_create_protid (newpo, cred->user, &newpi);
+ if (err)
+ diskfs_release_peropen (newpo);
+ }
+
+ if (! err)
+ {
+ *newnode = ports_get_right (newpi);
+ *newnodetype = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newpi);
+ }
+
+ diskfs_nput (np);
+
+ return err;
+}
diff --git a/libdiskfs/dir-readdir.c b/libdiskfs/dir-readdir.c
new file mode 100644
index 00000000..1393e26f
--- /dev/null
+++ b/libdiskfs/dir-readdir.c
@@ -0,0 +1,61 @@
+/*
+ Copyright (C) 1993,94,96,99,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include <fcntl.h>
+#include "priv.h"
+#include "fs_S.h"
+
+kern_return_t
+diskfs_S_dir_readdir (struct protid *cred,
+ char **data,
+ size_t *datacnt,
+ boolean_t *data_dealloc,
+ int entry,
+ int nentries,
+ vm_size_t bufsiz,
+ int *amt)
+{
+ error_t err;
+ struct node *np;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+ pthread_mutex_lock (&np->lock);
+
+ if ((cred->po->openstat & O_READ) == 0)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return EBADF;
+ }
+
+ if ((np->dn_stat.st_mode & S_IFMT) != S_IFDIR)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return ENOTDIR;
+ }
+
+ err = diskfs_get_directs (np, entry, nentries, data, datacnt, bufsiz, amt);
+ *data_dealloc = 1; /* XXX */
+ pthread_mutex_unlock (&np->lock);
+ return err;
+}
diff --git a/libdiskfs/dir-rename.c b/libdiskfs/dir-rename.c
new file mode 100644
index 00000000..ff9dead0
--- /dev/null
+++ b/libdiskfs/dir-rename.c
@@ -0,0 +1,239 @@
+/* libdiskfs implementation of fs.defs: dir_rename
+
+ Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 2007
+ Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+#include <string.h>
+
+/* To avoid races in checkpath, and to prevent a directory from being
+ simultaneously renamed by two processes, we serialize all renames of
+ directores with this lock */
+static pthread_mutex_t renamedirlock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Implement dir_rename as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_dir_rename (struct protid *fromcred,
+ char *fromname,
+ struct protid *tocred,
+ char *toname,
+ int excl)
+{
+ struct node *fdp, *tdp, *fnp, *tnp, *tmpnp;
+ error_t err;
+ struct dirstat *ds = alloca (diskfs_dirstat_size);
+
+ if (!fromcred)
+ return EOPNOTSUPP;
+
+ /* Verify that tocred really is a port to us. */
+ if (! tocred)
+ return EXDEV;
+
+ if (!strcmp (fromname, ".") || !strcmp (fromname, "..")
+ || !strcmp (toname, ".") || !strcmp (toname, ".."))
+ return EINVAL;
+
+ if (tocred->po->shadow_root != fromcred->po->shadow_root)
+ /* Same translator, but in different shadow trees. */
+ return EXDEV;
+
+ if (diskfs_check_readonly ())
+ return EROFS;
+
+ fdp = fromcred->po->np;
+ tdp = tocred->po->np;
+
+ try_again:
+ /* Acquire the source; hold a reference to it. This
+ will prevent anyone from deleting it before we create
+ the new link. */
+ pthread_mutex_lock (&fdp->lock);
+ err = diskfs_lookup (fdp, fromname, LOOKUP, &fnp, 0, fromcred);
+ pthread_mutex_unlock (&fdp->lock);
+ if (err == EAGAIN)
+ err = EINVAL;
+ if (err)
+ return err;
+
+ if (S_ISDIR (fnp->dn_stat.st_mode))
+ {
+ pthread_mutex_unlock (&fnp->lock);
+ if (pthread_mutex_trylock (&renamedirlock))
+ {
+ diskfs_nrele (fnp);
+ pthread_mutex_lock (&renamedirlock);
+ goto try_again;
+ }
+ err = diskfs_rename_dir (fdp, fnp, fromname, tdp, toname, fromcred,
+ tocred);
+ if (diskfs_synchronous)
+ {
+ pthread_mutex_lock (&fdp->lock);
+ diskfs_file_update (fdp, 1);
+ pthread_mutex_unlock (&fdp->lock);
+
+ pthread_mutex_lock (&fnp->lock);
+ diskfs_file_update (fnp, 1);
+ pthread_mutex_unlock (&fnp->lock);
+
+ pthread_mutex_lock (&tdp->lock);
+ diskfs_file_update (tdp, 1);
+ pthread_mutex_unlock (&tdp->lock);
+ }
+
+ diskfs_nrele (fnp);
+ pthread_mutex_unlock (&renamedirlock);
+ if (!err)
+ /* MiG won't do this for us, which it ought to. */
+ mach_port_deallocate (mach_task_self (), tocred->pi.port_right);
+ return err;
+ }
+
+ pthread_mutex_unlock (&fnp->lock);
+
+ /* We now hold no locks */
+
+ /* Link the node into the new directory. */
+ pthread_mutex_lock (&tdp->lock);
+
+ err = diskfs_lookup (tdp, toname, RENAME, &tnp, ds, tocred);
+ if (err == EAGAIN)
+ err = EINVAL;
+ else if (!err && excl)
+ {
+ err = EEXIST;
+ diskfs_nput (tnp);
+ }
+ if (err && err != ENOENT)
+ {
+ diskfs_drop_dirstat (tdp, ds);
+ diskfs_nrele (fnp);
+ pthread_mutex_unlock (&tdp->lock);
+ return err;
+ }
+
+ /* rename("foo", "link-to-foo") is guaranteed to return 0 and
+ do nothing by Posix. */
+ if (tnp == fnp)
+ {
+ diskfs_drop_dirstat (tdp, ds);
+ diskfs_nrele (fnp);
+ diskfs_nput (tnp);
+ pthread_mutex_unlock (&tdp->lock);
+ mach_port_deallocate (mach_task_self (), tocred->pi.port_right);
+ return 0;
+ }
+
+ /* rename("foo", dir) should fail. */
+ if (tnp && S_ISDIR (tnp->dn_stat.st_mode))
+ {
+ diskfs_drop_dirstat (tdp, ds);
+ diskfs_nrele (fnp);
+ diskfs_nput (tnp);
+ pthread_mutex_unlock (&tdp->lock);
+ return EISDIR;
+ }
+
+ pthread_mutex_lock (&fnp->lock);
+
+ /* Increment the link count for the upcoming link */
+ if (fnp->dn_stat.st_nlink == diskfs_link_max - 1)
+ {
+ diskfs_drop_dirstat (tdp, ds);
+ diskfs_nput (fnp);
+ if (tnp)
+ diskfs_nput (tnp);
+ pthread_mutex_unlock (&tdp->lock);
+ return EMLINK;
+ }
+ fnp->dn_stat.st_nlink++;
+ fnp->dn_set_ctime = 1;
+ diskfs_node_update (fnp, diskfs_synchronous);
+
+ if (tnp)
+ {
+ err = diskfs_dirrewrite (tdp, tnp, fnp, toname, ds);
+ if (!err)
+ {
+ tnp->dn_stat.st_nlink--;
+ tnp->dn_set_ctime = 1;
+ if (diskfs_synchronous)
+ diskfs_node_update (tnp, 1);
+ }
+ diskfs_nput (tnp);
+ }
+ else
+ err = diskfs_direnter (tdp, toname, fnp, ds, tocred);
+
+ if (diskfs_synchronous)
+ diskfs_node_update (tdp, 1);
+
+ pthread_mutex_unlock (&tdp->lock);
+ pthread_mutex_unlock (&fnp->lock);
+ if (err)
+ {
+ diskfs_nrele (fnp);
+ return err;
+ }
+
+ /* We now hold no locks */
+
+ /* Now we remove the source. Unfortunately, we haven't held
+ fdp locked (nor could we), so someone else might have already
+ removed it. */
+ pthread_mutex_lock (&fdp->lock);
+ err = diskfs_lookup (fdp, fromname, REMOVE, &tmpnp, ds, fromcred);
+ if (err)
+ {
+ diskfs_drop_dirstat (tdp, ds);
+ pthread_mutex_unlock (&fdp->lock);
+ diskfs_nrele (fnp);
+ return err;
+ }
+
+ if (tmpnp != fnp)
+ {
+ /* This is no longer the node being renamed, so just return. */
+ diskfs_drop_dirstat (tdp, ds);
+ diskfs_nput (tmpnp);
+ diskfs_nrele (fnp);
+ pthread_mutex_unlock (&fdp->lock);
+ mach_port_deallocate (mach_task_self (), tocred->pi.port_right);
+ return 0;
+ }
+
+ diskfs_nrele (tmpnp);
+
+ err = diskfs_dirremove (fdp, fnp, fromname, ds);
+ if (diskfs_synchronous)
+ diskfs_node_update (fdp, 1);
+
+ fnp->dn_stat.st_nlink--;
+ fnp->dn_set_ctime = 1;
+
+ if (diskfs_synchronous)
+ diskfs_node_update (fnp, 1);
+
+ diskfs_nput (fnp);
+ pthread_mutex_unlock (&fdp->lock);
+ if (!err)
+ mach_port_deallocate (mach_task_self (), tocred->pi.port_right);
+
+ return err;
+}
diff --git a/libdiskfs/dir-renamed.c b/libdiskfs/dir-renamed.c
new file mode 100644
index 00000000..9e37e234
--- /dev/null
+++ b/libdiskfs/dir-renamed.c
@@ -0,0 +1,241 @@
+/*
+ Copyright (C) 1994,95,96,97,98,99,2001,2003 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+
+/* Check if source directory is in the path of the target directory.
+ We get target locked, source unlocked but with a reference. When
+ we return, nothing is locked, and target has lost its reference.
+ This routine assumes that no renames of directories will happen
+ while it is running; as a result, ufs_rename serializes all renames
+ of directories. */
+static error_t
+checkpath(struct node *source,
+ struct node *target,
+ struct protid *cred)
+{
+ error_t err;
+ struct node *np;
+
+ for (np = target, err = 0;
+ /* nothing */;
+ /* This special lookup does a diskfs_nput on its first argument
+ when it succeeds. */
+ err = diskfs_lookup (np, "..", LOOKUP | SPEC_DOTDOT, &np, 0, cred))
+ {
+ if (err)
+ {
+ diskfs_nput (np);
+ return err;
+ }
+
+ if (np == source)
+ {
+ diskfs_nput (np);
+ return EINVAL;
+ }
+
+ if (np == diskfs_root_node || np == cred->po->shadow_root)
+ {
+ diskfs_nput (np);
+ return 0;
+ }
+ }
+}
+
+/* Rename directory node FNP (whose parent is FDP, and which has name
+ FROMNAME in that directory) to have name TONAME inside directory
+ TDP. None of these nodes are locked, and none should be locked
+ upon return. This routine is serialized, so it doesn't have to be
+ reentrant. Directories will never be renamed except by this
+ routine. FROMCRED and TOCRED are the users responsible for
+ FDP/FNP and TDP respectively. */
+error_t
+diskfs_rename_dir (struct node *fdp, struct node *fnp, const char *fromname,
+ struct node *tdp, const char *toname,
+ struct protid *fromcred, struct protid *tocred)
+{
+ error_t err;
+ struct node *tnp, *tmpnp;
+ void *buf = alloca (diskfs_dirstat_size);
+ struct dirstat *ds;
+ struct dirstat *tmpds;
+
+ pthread_mutex_lock (&tdp->lock);
+ diskfs_nref (tdp); /* reference and lock will get consumed by
+ checkpath */
+ err = checkpath (fnp, tdp, tocred);
+
+ if (err)
+ return err;
+
+ /* Now, lock the parent directories. This is legal because tdp is not
+ a child of fnp (guaranteed by checkpath above). */
+ pthread_mutex_lock (&fdp->lock);
+ if (fdp != tdp)
+ pthread_mutex_lock (&tdp->lock);
+
+ /* 1: Lookup target; if it exists, make sure it's an empty directory. */
+ ds = buf;
+ err = diskfs_lookup (tdp, toname, RENAME, &tnp, ds, tocred);
+ assert (err != EAGAIN); /* <-> assert (TONAME != "..") */
+
+ if (tnp == fnp)
+ {
+ diskfs_drop_dirstat (tdp, ds);
+ diskfs_nput (tnp);
+ pthread_mutex_unlock (&tdp->lock);
+ if (fdp != tdp)
+ pthread_mutex_unlock (&fdp->lock);
+ return 0;
+ }
+
+ /* Check permissions to remove FROMNAME and lock FNP. */
+ tmpds = alloca (diskfs_dirstat_size);
+ err = diskfs_lookup (fdp, fromname, REMOVE, &tmpnp, tmpds, fromcred);
+ assert (!tmpnp || tmpnp == fnp);
+ if (tmpnp)
+ diskfs_nrele (tmpnp);
+ diskfs_drop_dirstat (fdp, tmpds);
+ if (err)
+ goto out;
+
+ if (tnp)
+ {
+ if (! S_ISDIR(tnp->dn_stat.st_mode))
+ err = ENOTDIR;
+ else if (!diskfs_dirempty (tnp, tocred))
+ err = ENOTEMPTY;
+ }
+
+ if (err && err != ENOENT)
+ goto out;
+
+ /* 2: Set our .. to point to the new parent */
+ if (fdp != tdp)
+ {
+ if (tdp->dn_stat.st_nlink == diskfs_link_max - 1)
+ {
+ err = EMLINK;
+ goto out;
+ }
+ tdp->dn_stat.st_nlink++;
+ tdp->dn_set_ctime = 1;
+ if (diskfs_synchronous)
+ diskfs_node_update (tdp, 1);
+
+ tmpds = alloca (diskfs_dirstat_size);
+ err = diskfs_lookup (fnp, "..", RENAME | SPEC_DOTDOT,
+ &tmpnp, tmpds, fromcred);
+ assert (err != ENOENT);
+ if (err)
+ {
+ diskfs_drop_dirstat (fnp, tmpds);
+ goto out;
+ }
+ assert (tmpnp == fdp);
+
+ err = diskfs_dirrewrite (fnp, fdp, tdp, "..", tmpds);
+ if (diskfs_synchronous)
+ diskfs_file_update (fnp, 1);
+ if (err)
+ goto out;
+
+ fdp->dn_stat.st_nlink--;
+ fdp->dn_set_ctime = 1;
+ if (diskfs_synchronous)
+ diskfs_node_update (fdp, 1);
+ }
+
+
+ /* 3: Increment the link count on the node being moved and rewrite
+ tdp. */
+ if (fnp->dn_stat.st_nlink == diskfs_link_max - 1)
+ {
+ pthread_mutex_unlock (&fnp->lock);
+ diskfs_drop_dirstat (tdp, ds);
+ pthread_mutex_unlock (&tdp->lock);
+ if (tnp)
+ diskfs_nput (tnp);
+ return EMLINK;
+ }
+ fnp->dn_stat.st_nlink++;
+ fnp->dn_set_ctime = 1;
+ diskfs_node_update (fnp, diskfs_synchronous);
+
+ if (tnp)
+ {
+ err = diskfs_dirrewrite (tdp, tnp, fnp, toname, ds);
+ ds = 0;
+ if (!err)
+ {
+ tnp->dn_stat.st_nlink--;
+ tnp->dn_set_ctime = 1;
+ }
+ diskfs_clear_directory (tnp, tdp, tocred);
+ if (diskfs_synchronous)
+ diskfs_file_update (tnp, 1);
+ }
+ else
+ {
+ err = diskfs_direnter (tdp, toname, fnp, ds, tocred);
+ if (diskfs_synchronous)
+ diskfs_file_update (tdp, 1);
+ }
+
+ if (err)
+ goto out;
+
+ /* 4: Remove the entry in fdp. */
+ ds = buf;
+ pthread_mutex_unlock (&fnp->lock);
+ err = diskfs_lookup (fdp, fromname, REMOVE, &tmpnp, ds, fromcred);
+ assert (!tmpnp || tmpnp == fnp);
+ if (tmpnp)
+ diskfs_nrele (tmpnp);
+ if (err)
+ {
+ assert (!tmpnp);
+ /* diskfs_lookup has not locked fnp then, do not unlock it. */
+ fnp = NULL;
+ goto out;
+ }
+
+ diskfs_dirremove (fdp, fnp, fromname, ds);
+ ds = 0;
+ fnp->dn_stat.st_nlink--;
+ fnp->dn_set_ctime = 1;
+ if (diskfs_synchronous)
+ {
+ diskfs_file_update (fdp, 1);
+ diskfs_node_update (fnp, 1);
+ }
+
+ out:
+ if (tdp)
+ pthread_mutex_unlock (&tdp->lock);
+ if (tnp)
+ diskfs_nput (tnp);
+ if (fdp && fdp != tdp)
+ pthread_mutex_unlock (&fdp->lock);
+ if (fnp)
+ pthread_mutex_unlock (&fnp->lock);
+ if (ds)
+ diskfs_drop_dirstat (tdp, ds);
+ return err;
+}
diff --git a/libdiskfs/dir-rmdir.c b/libdiskfs/dir-rmdir.c
new file mode 100644
index 00000000..83ec37b3
--- /dev/null
+++ b/libdiskfs/dir-rmdir.c
@@ -0,0 +1,94 @@
+/* libdsikfs implementation of fs.defs: dir_rmdir
+ Copyright (C) 1992,93,94,95,96,97,99 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+#include <hurd/fsys.h>
+
+/* Implement dir_rmdir as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_dir_rmdir (struct protid *dircred,
+ char *name)
+{
+ struct node *dnp;
+ struct node *np;
+ struct dirstat *ds = alloca (diskfs_dirstat_size);
+ error_t error;
+
+ /* This routine cleans up the state we have after calling diskfs_lookup.
+ After that call, all returns are done with `return done (ERROR, NP);'. */
+ inline error_t done (error_t error, struct node *np)
+ {
+ if (np)
+ diskfs_nput (np);
+
+ if (ds)
+ diskfs_drop_dirstat (dnp, ds);
+ pthread_mutex_unlock (&dnp->lock);
+
+ return error;
+ }
+
+ if (!dircred)
+ return EOPNOTSUPP;
+
+ dnp = dircred->po->np;
+ if (diskfs_check_readonly ())
+ return EROFS;
+
+ pthread_mutex_lock (&dnp->lock);
+
+ error = diskfs_lookup (dnp, name, REMOVE, &np, ds, dircred);
+ if (error)
+ return done (error == EAGAIN ? ENOTEMPTY : error, 0);
+
+ if (dnp == np)
+ {
+ /* Attempt to rmdir(".") */
+ diskfs_nrele (np);
+ diskfs_drop_dirstat (dnp, ds);
+ pthread_mutex_unlock (&dnp->lock);
+ return EINVAL;
+ }
+
+ if ((np->dn_stat.st_mode & S_IPTRANS) || fshelp_translated (&np->transbox))
+ /* Attempt to rmdir a translated node. */
+ return done (EBUSY, np);
+
+ if (!S_ISDIR (np->dn_stat.st_mode))
+ return done (ENOTDIR, np);
+
+ if (!diskfs_dirempty (np, dircred))
+ return done (ENOTEMPTY, np);
+
+ /* Here we go! */
+ error = diskfs_dirremove (dnp, np, name, ds);
+ ds = 0;
+
+ if (!error)
+ {
+ np->dn_stat.st_nlink--;
+ np->dn_set_ctime = 1;
+ diskfs_clear_directory (np, dnp, dircred);
+ if (diskfs_synchronous)
+ diskfs_file_update (np, 1);
+ }
+ if (diskfs_synchronous)
+ diskfs_file_update (dnp, 1);
+
+ return done (error, np);
+}
diff --git a/libdiskfs/dir-unlink.c b/libdiskfs/dir-unlink.c
new file mode 100644
index 00000000..cf02c227
--- /dev/null
+++ b/libdiskfs/dir-unlink.c
@@ -0,0 +1,98 @@
+/* libdiskfs implementation of fs.defs: dir_unlink
+ Copyright (C) 1992,93,94,95,96,97,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+#include <hurd/fsys.h>
+
+/* Implement dir_unlink as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_dir_unlink (struct protid *dircred,
+ char *name)
+{
+ struct node *dnp;
+ struct node *np;
+ struct dirstat *ds = alloca (diskfs_dirstat_size);
+ error_t err;
+ mach_port_t control = MACH_PORT_NULL;
+
+ if (!dircred)
+ return EOPNOTSUPP;
+
+ dnp = dircred->po->np;
+ if (diskfs_check_readonly ())
+ return EROFS;
+
+ pthread_mutex_lock (&dnp->lock);
+
+ err = diskfs_lookup (dnp, name, REMOVE, &np, ds, dircred);
+ if (err == EAGAIN)
+ err = EPERM; /* 1003.1-1996 5.5.1.4 */
+ if (err)
+ {
+ diskfs_drop_dirstat (dnp, ds);
+ pthread_mutex_unlock (&dnp->lock);
+ return err;
+ }
+
+ /* This isn't the BSD behavior, but it is Posix compliant and saves
+ us on several race conditions.*/
+ if (S_ISDIR(np->dn_stat.st_mode))
+ {
+ if (np == dnp) /* gotta catch '.' */
+ diskfs_nrele (np);
+ else
+ diskfs_nput (np);
+ diskfs_drop_dirstat (dnp, ds);
+ pthread_mutex_unlock (&dnp->lock);
+ return EPERM; /* 1003.1-1996 5.5.1.4 */
+ }
+
+ err = diskfs_dirremove (dnp, np, name, ds);
+ if (diskfs_synchronous)
+ diskfs_node_update (dnp, 1);
+ if (err)
+ {
+ diskfs_nput (np);
+ pthread_mutex_unlock (&dnp->lock);
+ return err;
+ }
+
+ np->dn_stat.st_nlink--;
+ np->dn_set_ctime = 1;
+ if (diskfs_synchronous)
+ diskfs_node_update (np, 1);
+
+ if (np->dn_stat.st_nlink == 0)
+ fshelp_fetch_control (&np->transbox, &control);
+
+ /* This check is necessary because we might get here on an error while
+ checking the mode on something which happens to be `.'. */
+ if (np == dnp)
+ diskfs_nrele (np);
+ else
+ diskfs_nput (np);
+ pthread_mutex_unlock (&dnp->lock);
+
+ if (control)
+ {
+ fsys_goaway (control, FSYS_GOAWAY_UNLINK);
+ mach_port_deallocate (mach_task_self (), control);
+ }
+
+ return err;
+}
diff --git a/libdiskfs/direnter.c b/libdiskfs/direnter.c
new file mode 100644
index 00000000..cb9b76ca
--- /dev/null
+++ b/libdiskfs/direnter.c
@@ -0,0 +1,48 @@
+/* Wrapper for diskfs_direnter_hard
+ Copyright (C) 1996, 1998 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include "priv.h"
+
+/* Add NP to directory DP under the name NAME. This will only be
+ called after an unsuccessful call to diskfs_lookup of type CREATE
+ or RENAME; DP has been locked continuously since that call and DS
+ is as that call set it, NP is locked. CRED identifies the user
+ responsible for the call (to be used only to validate directory
+ growth). This function is a wrapper for diskfs_direnter_hard. */
+error_t
+diskfs_direnter (struct node *dp,
+ const char *name,
+ struct node *np,
+ struct dirstat *ds,
+ struct protid *cred)
+{
+ error_t err;
+
+ err = diskfs_direnter_hard (dp, name, np, ds, cred);
+ if (err)
+ return err;
+
+ if (dp->dirmod_reqs)
+ diskfs_notice_dirchange (dp, DIR_CHANGED_NEW, name);
+
+ diskfs_enter_lookup_cache (dp, np, name);
+ return 0;
+}
diff --git a/libdiskfs/dirremove.c b/libdiskfs/dirremove.c
new file mode 100644
index 00000000..239daa72
--- /dev/null
+++ b/libdiskfs/dirremove.c
@@ -0,0 +1,45 @@
+/* Wrapper for diskfs_dirremove_hard
+ Copyright (C) 1996, 1998 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+/* This will only be called after a successful call to diskfs_lookup
+ of type REMOVE; this call should remove the name found from the
+ directory DS. DP has been locked continuously since the call to
+ diskfs_lookup and DS is as that call set it. This routine should
+ call diskfs_notice_dirchange if DP->dirmod_reqs is nonzero. This
+ function is a wrapper for diskfs_dirremove_hard. The entry being
+ removed has name NAME and refers to NP. */
+error_t
+diskfs_dirremove (struct node *dp,
+ struct node *np,
+ const char *name,
+ struct dirstat *ds)
+{
+ error_t err;
+
+ diskfs_purge_lookup_cache (dp, np);
+
+ err = diskfs_dirremove_hard (dp, ds);
+
+ if (!err && dp->dirmod_reqs)
+ diskfs_notice_dirchange (dp, DIR_CHANGED_UNLINK, name);
+ return err;
+}
diff --git a/libdiskfs/dirrewrite.c b/libdiskfs/dirrewrite.c
new file mode 100644
index 00000000..8f713960
--- /dev/null
+++ b/libdiskfs/dirrewrite.c
@@ -0,0 +1,50 @@
+/* Wrapper for diskfs_dirrewrite_hard
+ Copyright (C) 1996, 1998 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include "priv.h"
+
+/* This will only be called after a successful call to diskfs_lookup
+ of type RENAME; this call should change the name found in directory
+ DP to point to node NP instead of its previous referent, OLDNP. DP
+ has been locked continuously since the call to diskfs_lookup and DS
+ is as that call set it; NP is locked. This routine should call
+ diskfs_notice_dirchange if DP->dirmod_reqs is nonzero. NAME is the
+ name of OLDNP inside DP; it is this reference which is being
+ rewritten. This function is a wrapper for diskfs_dirrewrite_hard. */
+error_t diskfs_dirrewrite (struct node *dp,
+ struct node *oldnp,
+ struct node *np,
+ const char *name,
+ struct dirstat *ds)
+{
+ error_t err;
+
+ diskfs_purge_lookup_cache (dp, oldnp);
+
+ err = diskfs_dirrewrite_hard (dp, np, ds);
+ if (err)
+ return err;
+
+ if (dp->dirmod_reqs)
+ diskfs_notice_dirchange (dp, DIR_CHANGED_RENUMBER, name);
+ diskfs_enter_lookup_cache (dp, np, name);
+ return 0;
+}
diff --git a/libdiskfs/disk-pager.c b/libdiskfs/disk-pager.c
new file mode 100644
index 00000000..9a0d9d88
--- /dev/null
+++ b/libdiskfs/disk-pager.c
@@ -0,0 +1,131 @@
+/* Map the disk image and handle faults accessing it.
+ Copyright (C) 1996,97,99,2001,02 Free Software Foundation, Inc.
+ Written by Roland McGrath.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "diskfs-pager.h"
+#include <hurd/sigpreempt.h>
+#include <error.h>
+
+__thread struct disk_image_user *diskfs_exception_diu;
+
+struct pager *diskfs_disk_pager;
+
+static void fault_handler (int sig, long int sigcode, struct sigcontext *scp);
+static struct hurd_signal_preemptor preemptor =
+ {
+ signals: sigmask (SIGSEGV) | sigmask (SIGBUS),
+ preemptor: NULL,
+ handler: (sighandler_t) &fault_handler,
+ };
+
+/* A top-level function for the paging thread that just services paging
+ requests. */
+static void *
+service_paging_requests (void *arg)
+{
+ struct port_bucket *pager_bucket = arg;
+ for (;;)
+ ports_manage_port_operations_multithread (pager_bucket,
+ pager_demuxer,
+ 1000 * 60 * 2,
+ 1000 * 60 * 10, 0);
+ return NULL;
+}
+
+void
+diskfs_start_disk_pager (struct user_pager_info *upi,
+ struct port_bucket *pager_bucket,
+ int may_cache, int notify_on_evict,
+ size_t size, void **image)
+{
+ pthread_t thread;
+ error_t err;
+ mach_port_t disk_pager_port;
+
+ /* Make a thread to service paging requests. */
+ err = pthread_create (&thread, NULL, service_paging_requests, pager_bucket);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+
+ /* Create the pager. */
+ diskfs_disk_pager = pager_create (upi, pager_bucket,
+ may_cache, MEMORY_OBJECT_COPY_NONE,
+ notify_on_evict);
+ if (diskfs_disk_pager == NULL)
+ error (2, errno, "creating diskfs_disk_pager failed");
+
+ /* Get a port to the disk pager. */
+ disk_pager_port = pager_get_port (diskfs_disk_pager);
+ mach_port_insert_right (mach_task_self (), disk_pager_port, disk_pager_port,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ /* Now map the disk image. */
+ err = vm_map (mach_task_self (), (vm_address_t *)image, size,
+ 0, 1, disk_pager_port, 0, 0,
+ VM_PROT_READ | (diskfs_readonly ? 0 : VM_PROT_WRITE),
+ VM_PROT_READ | VM_PROT_WRITE,
+ VM_INHERIT_NONE);
+ if (err)
+ error (2, err, "cannot vm_map whole disk");
+
+ /* Set up the signal preemptor to catch faults on the disk image. */
+ preemptor.first = (vm_address_t) *image;
+ preemptor.last = ((vm_address_t) *image + size);
+ hurd_preempt_signals (&preemptor);
+
+ /* We have the mapping; we no longer need the send right. */
+ mach_port_deallocate (mach_task_self (), disk_pager_port);
+}
+
+static void
+fault_handler (int sig, long int sigcode, struct sigcontext *scp)
+{
+ error_t err;
+
+#ifndef NDEBUG
+ if (diskfs_exception_diu == NULL)
+ {
+ error (0, 0,
+ "BUG: unexpected fault on disk image (%d, %#lx) in [%#lx,%#lx)"
+ " eip %#zx err %#x",
+ sig, sigcode,
+ preemptor.first, preemptor.last,
+ scp->sc_pc, scp->sc_error);
+ assert (scp->sc_error == EKERN_MEMORY_ERROR);
+ err = pager_get_error (diskfs_disk_pager, sigcode);
+ assert (err);
+ assert_perror (err);
+ }
+#endif
+
+ /* Clear the record, since the faulting thread will not. */
+ diskfs_exception_diu = NULL;
+
+ /* Fetch the error code from the pager. */
+ assert (scp->sc_error == EKERN_MEMORY_ERROR);
+ err = pager_get_error (diskfs_disk_pager, sigcode);
+ assert (err);
+
+ /* Make `diskfault_catch' return the error code. */
+ longjmp (diskfs_exception_diu->env, err);
+}
diff --git a/libdiskfs/diskfs-pager.h b/libdiskfs/diskfs-pager.h
new file mode 100644
index 00000000..a253069b
--- /dev/null
+++ b/libdiskfs/diskfs-pager.h
@@ -0,0 +1,73 @@
+/* Map the disk image and handle faults accessing it.
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+ Written by Roland McGrath.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _HURD_DISKFS_PAGER_H
+#define _HURD_DISKFS_PAGER_H 1
+
+#include <hurd/pager.h>
+#include <hurd/ports.h>
+#include <setjmp.h>
+#include <pthread.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdlib.h>
+
+extern __thread struct disk_image_user *diskfs_exception_diu;
+
+/* Start a pager for the whole disk, and store it in DISKFS_DISK_PAGER,
+ preparing a signal preemptor so that the `diskfs_catch_exception' macro
+ below works. SIZE should be the size of the image to map, and the address
+ mapped is returned in IMAGE. INFO, PAGER_BUCKET, & MAY_CACHE are passed
+ to `pager_create'. */
+extern void diskfs_start_disk_pager (struct user_pager_info *info,
+ struct port_bucket *pager_bucket,
+ int may_cache, int notify_on_evict,
+ size_t size, void **image);
+
+extern struct pager *diskfs_disk_pager;
+
+struct disk_image_user
+ {
+ jmp_buf env;
+ struct disk_image_user *next;
+ };
+
+/* Return zero now. Return a second time with a nonzero error_t
+ if this thread faults accessing `disk_image' before calling
+ `diskfs_end_catch_exception' (below). */
+#define diskfs_catch_exception() \
+({ \
+ struct disk_image_user *diu = alloca (sizeof *diu); \
+ error_t err; \
+ diu->next = diskfs_exception_diu; \
+ err = setjmp (diu->env); \
+ if (err == 0) \
+ diskfs_exception_diu = diu; \
+ err; \
+})
+
+/* No longer handle faults on `disk_image' in this thread.
+ Any unexpected fault hereafter will crash the program. */
+#define diskfs_end_catch_exception() \
+({ \
+ struct disk_image_user *diu = diskfs_exception_diu; \
+ diskfs_exception_diu = diu->next; \
+})
+
+
+#endif /* hurd/diskfs-pager.h */
diff --git a/libdiskfs/diskfs.h b/libdiskfs/diskfs.h
new file mode 100644
index 00000000..e3285271
--- /dev/null
+++ b/libdiskfs/diskfs.h
@@ -0,0 +1,1076 @@
+/* Definitions for fileserver helper functions
+
+ Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2001, 2002, 2007, 2008,
+ 2009, 2013 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _HURD_DISKFS
+#define _HURD_DISKFS
+
+#include <assert.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <hurd/ports.h>
+#include <hurd/fshelp.h>
+#include <hurd/iohelp.h>
+#include <idvec.h>
+#include <features.h>
+#include <refcount.h>
+
+#ifdef DISKFS_DEFINE_EXTERN_INLINE
+#define DISKFS_EXTERN_INLINE
+#else
+#define DISKFS_EXTERN_INLINE __extern_inline
+#endif
+
+/* Each user port referring to a file points to one of these
+ (with the aid of the ports library). */
+struct protid
+{
+ struct port_info pi; /* libports info block */
+
+ /* User identification */
+ struct iouser *user;
+
+ /* Object this refers to */
+ struct peropen *po;
+
+ /* Shared memory I/O information. */
+ memory_object_t shared_object;
+ struct shared_io *mapped;
+};
+
+/* One of these is created for each node opened by dir_lookup. */
+struct peropen
+{
+ off_t filepointer;
+ int lock_status;
+ refcount_t refcnt;
+ int openstat;
+
+ struct node *np;
+
+ /* The parent of the translator's root node. */
+ mach_port_t root_parent;
+
+ /* If this node is in a shadow tree, the parent of its root. */
+ mach_port_t shadow_root_parent;
+ /* If in a shadow tree, its root node in this translator. */
+ struct node *shadow_root;
+
+ /* Path relative to the root of the translator. */
+ char *path;
+};
+
+/* A unique one of these exists for each node currently in use (and
+ possibly for some not currently in use, but with links) in the
+ filesystem. */
+struct node
+{
+ struct node *next, **prevp;
+
+ struct disknode *dn;
+
+ io_statbuf_t dn_stat;
+
+ /* Stat has been modified if one of the following four fields
+ is nonzero. Also, if one of the dn_set_?time fields is nonzero,
+ the appropriate dn_stat.st_?tim field needs to be updated. */
+ int dn_set_ctime;
+ int dn_set_atime;
+ int dn_set_mtime;
+ int dn_stat_dirty;
+
+ pthread_mutex_t lock;
+
+ int references; /* hard references */
+ int light_references; /* light references */
+
+ mach_port_t sockaddr; /* address for S_IFSOCK shortcut */
+
+ int owner;
+
+ struct transbox transbox;
+
+ struct lock_box userlock;
+
+ struct conch conch;
+
+ struct modreq *dirmod_reqs;
+ unsigned int dirmod_tick;
+
+ struct modreq *filemod_reqs;
+ unsigned int filemod_tick;
+
+ loff_t allocsize;
+
+ ino64_t cache_id;
+
+ int author_tracks_uid;
+};
+
+struct diskfs_control
+{
+ struct port_info pi;
+};
+
+struct bootinfo
+{
+ struct port_info pi;
+};
+
+/* Possibly lookup types for diskfs_lookup call */
+enum lookup_type
+{
+ LOOKUP,
+ CREATE,
+ REMOVE,
+ RENAME,
+};
+
+/* Pending directory and file modification request */
+struct modreq
+{
+ mach_port_t port;
+ struct modreq *next;
+};
+
+
+/* Special flag for diskfs_lookup. */
+#define SPEC_DOTDOT 0x10000000
+
+struct argp; /* opaque in this file */
+struct argp_child; /* opaque in this file */
+struct store; /* opaque in this file */
+struct store_parsed; /* opaque in this file */
+
+/* Declarations of variables the library sets. */
+
+extern mach_port_t diskfs_default_pager; /* send right */
+extern auth_t diskfs_auth_server_port; /* send right */
+
+/* The io_identity identity port for the filesystem. */
+extern mach_port_t diskfs_fsys_identity;
+
+/* The command line diskfs was started, set by the default argument parser.
+ If you don't use it, set this yourself. This is only used for bootstrap
+ file systems, to give the procserver. */
+extern char **diskfs_argv;
+
+/* When this is a bootstrap filesystem, the multiboot kernel command
+ line passed from the kernel. If not a bootstrap filesystem, it is
+ 0. As such, it can be used to distinguish between the two cases.
+ Note: this is only valid after the arguments have been parsed by,
+ for example, diskfs_init_main. */
+extern const char *diskfs_boot_command_line;
+#define diskfs_boot_filesystem() (diskfs_boot_command_line != 0)
+
+/* When this is a bootstrap filesystem, nonzero if starting each bootstrap
+ program should pause for a keystroke, for debugging purposes. */
+extern int _diskfs_boot_pause;
+
+/* Name of the init program run when this is a bootstrap filesystem. */
+extern const char *diskfs_boot_init_program;
+
+/* Hold this lock while do fsys level operations. Innocuous users can just
+ hold a reader lock, and anyone who's going to do nasty things that would
+ screw anyone else should hold a writer lock. */
+extern pthread_rwlock_t diskfs_fsys_lock;
+
+extern volatile struct mapped_time_value *diskfs_mtime;
+
+/* True iff we should do every operation synchronously. It
+ is the format-specific code's responsibility to keep allocation
+ information permanently in sync if this is set; the rest will
+ be done by format independent code. */
+extern int diskfs_synchronous;
+
+extern pthread_spinlock_t diskfs_node_refcnt_lock;
+
+extern int pager_port_type;
+
+/* Whether the filesystem is currently writable or not. */
+extern int diskfs_readonly;
+
+
+struct pager;
+
+/* Port classes we manage */
+extern struct port_class *diskfs_protid_class;
+extern struct port_class *diskfs_control_class;
+extern struct port_class *diskfs_execboot_class;
+extern struct port_class *diskfs_initboot_class;
+extern struct port_class *diskfs_shutdown_notification_class;
+
+extern struct port_bucket *diskfs_port_bucket;
+
+
+
+/* Declarations of things the user must or may define. */
+
+/* The user must define this type. This should hold information
+ between calls to diskfs_lookup and diskfs_dir{enter,rewrite,rename}
+ so that those calls work as described below. */
+struct dirstat;
+
+/* The user must define this variable; it should be the size in bytes
+ of a struct dirstat. */
+extern const size_t diskfs_dirstat_size;
+
+/* The user must define this variable; it is the maximum number of
+ links to any one file. The implementation of dir_rename does not know
+ how to succeed if this is only one; on such formats you need to
+ reimplement dir_rename yourself. */
+extern int diskfs_link_max;
+
+/* The user must define this variable; it is the maximum length of
+ a single pathname component (i.e. file name within directory).
+ The filesystem code does not use this for anything, but it is
+ returned to user queries for _PC_NAME_MAX. */
+extern int diskfs_name_max;
+
+/* The user must define this variable; it is the maximum number of
+ symlinks to be traversed within a single call to dir_lookup.
+ If this is exceeded, dir_lookup will return ELOOP. */
+extern int diskfs_maxsymlinks;
+
+/* This variable is defined by diskfs; the user should set it if
+ the filesystem media cannot be made writeable. */
+extern int diskfs_hard_readonly;
+
+/* The user must define this variable. Set this to be the node
+ of root of the filesystem. */
+extern struct node *diskfs_root_node;
+
+/* The user must define this variable. Set this to the name of the
+ filesystem server. */
+extern char *diskfs_server_name;
+
+/* The user must define this variables. Set this to be the server
+ version number. */
+extern char *diskfs_server_version;
+
+/* The user may define this variable. Set this to be any additional
+ version specification that should be printed for --version. */
+extern char *diskfs_extra_version;
+
+/* The user may define this variable. This should be nonzero iff the
+ filesystem format supports shortcutting symlink translation.
+ The library guarantees that users will not be able to read or write
+ the contents of the node directly, and the library will only do so
+ if the symlink hook functions return EINVAL or are not defined.
+ The library knows that the dn_stat.st_size field is the length of
+ the symlink, even if the hook functions are used. */
+int diskfs_shortcut_symlink;
+
+/* The user may define this variable. This should be nonzero iff the
+ filesystem format supports shortcutting chrdev translation. */
+int diskfs_shortcut_chrdev;
+
+/* The user may define this variable. This should be nonzero iff the
+ filesystem format supports shortcutting blkdev translation. */
+int diskfs_shortcut_blkdev;
+
+/* The user may define this variable. This should be nonzero iff the
+ filesystem format supports shortcutting fifo translation. */
+int diskfs_shortcut_fifo;
+
+/* The user may define this variable. This should be nonzero iff the
+ filesystem format supports shortcutting ifsock translation. */
+int diskfs_shortcut_ifsock;
+
+/* The user may define this variable, otherwise it has a default value of 30.
+ diskfs_set_sync_interval is called with this value when the first diskfs
+ thread is started up (in diskfs_spawn_first_threa). */
+extern int diskfs_default_sync_interval;
+
+/* The user must define this variable, which should be a string that somehow
+ identifies the particular disk this filesystem is interpreting. It is
+ generally only used to print messages or to distinguish instances of the
+ same filesystem type from one another. If this filesystem accesses no
+ external media, then define this to be 0. */
+extern char *diskfs_disk_name;
+
+/* The user must define this function. Set *STATFSBUF with
+ appropriate values to reflect the current state of the filesystem.
+ The buffer will be initialized to all zeros by the caller;
+ the caller will set f_namelen to diskfs_name_max. */
+error_t diskfs_set_statfs (fsys_statfsbuf_t *statfsbuf);
+
+/* The user must define this function. Lookup in directory DP (which
+ is locked) the name NAME. TYPE will either be LOOKUP, CREATE,
+ RENAME, or REMOVE. CRED identifies the user making the call.
+
+ If the name is found, return zero, and (if NP is nonzero) set *NP
+ to point to the node for it, locked. If the name is not found,
+ return ENOENT, and (if NP is nonzero) set *NP to zero. If NP is
+ zero, then the node found must not be locked, even transitorily.
+ Lookups for REMOVE and RENAME (which must often check permissions
+ on the node being found) will always set NP.
+
+ If DS is nonzero then:
+ For LOOKUP: set *DS to be ignored by diskfs_drop_dirstat.
+ For CREATE: on success, set *DS to be ignored by diskfs_drop_dirstat.
+ on failure, set *DS for a future call to diskfs_direnter.
+ For RENAME: on success, set *DS for a future call to diskfs_dirrewrite.
+ on failure, set *DS for a future call to diskfs_direnter.
+ For REMOVE: on success, set *DS for a future call to diskfs_dirremove.
+ on failure, set *DS to be ignored by diskfs_drop_dirstat.
+ The caller of this function guarantees that if DS is nonzero, then
+ either the appropriate call listed above or diskfs_drop_dirstat will
+ be called with DS before the directory DP is unlocked, and guarantees
+ that no lookup calls will be made on this directory between this
+ lookup and the use (or descruction) of *DS.
+
+ If you use the library's versions of diskfs_rename_dir,
+ diskfs_clear_directory, and diskfs_init_dir, then lookups for `..'
+ might have the flag SPEC_DOTDOT or'd in. This has the following special
+ meaning:
+ For LOOKUP: DP should be unlocked and its reference dropped before
+ returning.
+ For RENAME and REMOVE: The node being found (*NP) is already held
+ locked, so don't lock it or add a reference to it.
+ (SPEC_DOTDOT will not be given with CREATE.)
+
+ Return ENOENT if NAME isn't in the directory.
+ Return EAGAIN if NAME refers to the `..' of this filesystem's root.
+ Return EIO if appropriate.
+*/
+error_t diskfs_lookup_hard (struct node *dp,
+ const char *name, enum lookup_type type,
+ struct node **np, struct dirstat *ds,
+ struct protid *cred);
+
+/* The user must define this function. Add NP to directory DP
+ under the name NAME. This will only be called after an
+ unsuccessful call to diskfs_lookup of type CREATE or RENAME; DP
+ has been locked continuously since that call and DS is as that call
+ set it, NP is locked. CRED identifies the user responsible
+ for the call (to be used only to validate directory growth). */
+error_t diskfs_direnter_hard (struct node *dp, const char *name,
+ struct node *np, struct dirstat *ds,
+ struct protid *cred);
+
+/* The user must define this function. This will only be called after
+ a successful call to diskfs_lookup of type RENAME; this call should change
+ the name found in directory DP to point to node NP instead of its previous
+ referent. DP has been locked continuously since the call to diskfs_lookup
+ and DS is as that call set it; NP is locked. */
+error_t diskfs_dirrewrite_hard (struct node *dp, struct node *np,
+ struct dirstat *ds);
+
+/* The user must define this function. This will only be called after a
+ successful call to diskfs_lookup of type REMOVE; this call should remove
+ the name found from the directory DS. DP has been locked continuously since
+ the call to diskfs_lookup and DS is as that call set it. */
+error_t diskfs_dirremove_hard (struct node *dp, struct dirstat *ds);
+
+/* The user must define this function. Initialize DS such that
+ diskfs_drop_dirstat will ignore it. */
+void diskfs_null_dirstat (struct dirstat *ds);
+
+/* The user must define this function. DS has been set by a previous
+ call to diskfs_lookup on directory DP; this function is
+ guaranteed to be called if none of
+ diskfs_dir{enter,rename,rewrite} is, and should free any state
+ retained by a struct dirstat. DP has been locked continuously since
+ the call to diskfs_lookup. */
+error_t diskfs_drop_dirstat (struct node *dp, struct dirstat *ds);
+
+/* The user must define this function. Return N directory entries
+ starting at ENTRY from locked directory node DP. Fill *DATA with
+ the entries; that pointer currently points to *DATACNT bytes. If
+ it isn't big enough, vm_allocate into *DATA. Set *DATACNT with the
+ total size used. Fill AMT with the number of entries copied.
+ Regardless, never copy more than BUFSIZ bytes. If BUFSIZ is 0,
+ then there is no limit on *DATACNT; if N is -1, then there is no limit
+ on AMT. */
+error_t diskfs_get_directs (struct node *dp, int entry, int n,
+ char **data, size_t *datacnt,
+ vm_size_t bufsiz, int *amt);
+
+/* The user must define this function. For locked node NP (for which
+ diskfs_node_translated is true) look up the name of its translator.
+ Store the name into newly malloced storage; set *NAMELEN to the
+ total length. */
+error_t diskfs_get_translator (struct node *np, char **namep, u_int *namelen);
+
+/* The user must define this function. For locked node NP, set
+ the name of the translating program to be NAME, length NAMELEN. CRED
+ identifies the user responsible for the call. */
+error_t diskfs_set_translator (struct node *np,
+ const char *name, u_int namelen,
+ struct protid *cred);
+
+/* The user must define this function. Truncate locked node NP to be SIZE
+ bytes long. (If NP is already less than or equal to SIZE bytes
+ long, do nothing.) If this is a symlink (and diskfs_shortcut_symlink
+ is set) then this should clear the symlink, even if
+ diskfs_create_symlink_hook stores the link target elsewhere. */
+error_t diskfs_truncate (struct node *np, loff_t size);
+
+/* The user must define this function. Grow the disk allocated to locked node
+ NP to be at least SIZE bytes, and set NP->allocsize to the actual
+ allocated size. (If the allocated size is already SIZE bytes, do
+ nothing.) CRED identifies the user responsible for the call. */
+error_t diskfs_grow (struct node *np, loff_t size, struct protid *cred);
+
+/* The user must define this function. Write to disk (synchronously
+ iff WAIT is nonzero) from format-specific buffers any non-paged
+ metadata. If CLEAN is nonzero, then after this is written the
+ filesystem will be absolutely clean, and the non-paged metadata can
+ so indicate. */
+error_t diskfs_set_hypermetadata (int wait, int clean);
+
+/* The user must define this function. Allocate a new node to be of
+ mode MODE in locked directory DP (don't actually set the mode or
+ modify the dir, that will be done by the caller); the user
+ responsible for the request can be identified with CRED. Set *NP
+ to be the newly allocated node. */
+error_t diskfs_alloc_node (struct node *dp, mode_t mode, struct node **np);
+
+/* Free node NP; the on disk copy has already been synced with
+ diskfs_node_update (where NP->dn_stat.st_mode was 0). It's
+ mode used to be MODE. */
+void diskfs_free_node (struct node *np, mode_t mode);
+
+/* Node NP has no more references; free local state, including *NP
+ if it isn't to be retained. diskfs_node_refcnt_lock is held. */
+void diskfs_node_norefs (struct node *np);
+
+/* The user must define this function. Node NP has some light
+ references, but has just lost its last hard references. Take steps
+ so that if any light references can be freed, they are. NP is locked
+ as is the pager refcount lock. This function will be called after
+ diskfs_lost_hardrefs. */
+void diskfs_try_dropping_softrefs (struct node *np);
+
+/* The user must define this funcction. Node NP has some light
+ references but has just lost its last hard reference. NP is locked. */
+void diskfs_lost_hardrefs (struct node *np);
+
+/* The user must define this function. Node NP has just acquired
+ a hard reference where it had none previously. It is thus now
+ OK again to have light references without real users. NP is
+ locked. */
+void diskfs_new_hardrefs (struct node *np);
+
+/* The user must define this function. Return non-zero if locked
+ directory DP is empty. If the user does not redefine
+ diskfs_clear_directory and diskfs_init_directory, then `empty'
+ means `possesses entries labelled . and .. only'. CRED
+ identifies the user making the call (if this user can't search
+ the directory, then this routine should fail). */
+int diskfs_dirempty (struct node *dp, struct protid *cred);
+
+/* The user may define this function. Return 0 if NP's mode can be
+ changed to MODE; otherwise return an error code. It must always be
+ possible to clear the mode; diskfs will not ask for permission
+ before doing so. */
+error_t diskfs_validate_mode_change (struct node *np, mode_t mode);
+
+/* The user may define this function. Return 0 if NP's owner can be
+ changed to UID; otherwise return an error code. */
+error_t diskfs_validate_owner_change (struct node *np, uid_t uid);
+
+/* The user may define this function. Return 0 if NP's group can be
+ changed to GID; otherwise return an error code. */
+error_t diskfs_validate_group_change (struct node *np, gid_t gid);
+
+/* The user may define this function. Return 0 if NP's author can be
+ changed to AUTHOR; otherwise return an error code. */
+error_t diskfs_validate_author_change (struct node *np, uid_t author);
+
+/* The user may define this function. Return 0 if NP's flags can be
+ changed to FLAGS; otherwise return an error code. It must always
+ be possible to clear the flags. */
+error_t diskfs_validate_flags_change (struct node *np, int flags);
+
+/* The user may define this function. Return 0 if NP's rdev can be
+ changed to RDEV; otherwise return an error code. */
+error_t diskfs_validate_rdev_change (struct node *np, dev_t rdev);
+
+/* The user must define this function. Sync the info in NP->dn_stat
+ and any associated format-specific information to disk. If WAIT is true,
+ then return only after the physicial media has been completely updated. */
+void diskfs_write_disknode (struct node *np, int wait);
+
+/* The user must define this function. Sync the file contents and all
+ associated meta data of file NP to disk (generally this will involve
+ calling diskfs_node_update for much of the metadata). If WAIT is true,
+ then return only after the physical media has been completely updated. */
+void diskfs_file_update (struct node *np, int wait);
+
+/* The user must define this function. For each active node, call
+ FUN. The node is to be locked around the call to FUN. If FUN
+ returns non-zero for any node, then immediately stop, and return
+ that value. */
+error_t diskfs_node_iterate (error_t (*fun)(struct node *));
+
+/* The user must define this function. Sync all the pagers and any
+ data belonging on disk except for the hypermetadata. If WAIT is true,
+ then return only after the physicial media has been completely updated. */
+void diskfs_sync_everything (int wait);
+
+/* Shutdown all pagers; this is done when the filesystem is exiting and is
+ irreversable. */
+void diskfs_shutdown_pager ();
+
+/* The user must define this function. Return a memory object port (send
+ right) for the file contents of NP. PROT is the maximum allowable
+ access. On errors, return MACH_PORT_NULL and set errno. */
+mach_port_t diskfs_get_filemap (struct node *np, vm_prot_t prot);
+
+/* The user must define this function. Return true if there are pager
+ ports exported that might be in use by users. If this returns false, then
+ further pager creation is also blocked. */
+int diskfs_pager_users ();
+
+/* Return the bitwise or of the maximum prot parameter (the second arg to
+ diskfs_get_filemap) for all active user pagers. */
+vm_prot_t diskfs_max_user_pager_prot ();
+
+/* The user must define this function. Return a `struct pager *' suitable
+ for use as an argument to diskfs_register_memory_fault_area that
+ refers to the pager returned by diskfs_get_filemap for node NP.
+ NP is locked. */
+struct pager *diskfs_get_filemap_pager_struct (struct node *np);
+
+/* The user may define this function. It is called when the disk has been
+ changed from read-only to read-write mode or vice-versa. READONLY is the
+ new state (which is also reflected in DISKFS_READONLY). This function is
+ also called during initial startup if the filesystem is to be writable. */
+void diskfs_readonly_changed (int readonly);
+
+/* The user must define this function. It must invalidate all cached global
+ state, and re-read it as necessary from disk, without writing anything.
+ It is always called with DISKFS_READONLY true. diskfs_node_reload is
+ subsequently called on all active nodes, so this call needn't re-read any
+ node-specific data. */
+error_t diskfs_reload_global_state ();
+
+/* The user must define this function. It must re-read all data specific to
+ NODE from disk, without writing anything. It is always called with
+ DISKFS_READONLY true. */
+error_t diskfs_node_reload (struct node *node);
+
+/* If this function is nonzero (and diskfs_shortcut_symlink is set) it
+ is called to set a symlink. If it returns EINVAL or isn't set,
+ then the normal method (writing the contents into the file data) is
+ used. If it returns any other error, it is returned to the user. */
+error_t (*diskfs_create_symlink_hook)(struct node *np, const char *target);
+
+/* If this function is nonzero (and diskfs_shortcut_symlink is set) it
+ is called to read the contents of a symlink. If it returns EINVAL or
+ isn't set, then the normal method (reading from the file data) is
+ used. If it returns any other error, it is returned to the user. */
+error_t (*diskfs_read_symlink_hook)(struct node *np, char *target);
+
+/* The user may define this function. The function must set source to
+ the source of CRED. The function may return an EOPNOTSUPP to
+ indicate that the concept of a source device is not applicable. The
+ default function always returns EOPNOTSUPP. */
+error_t diskfs_get_source (struct protid *cred,
+ char *source, size_t source_len);
+
+/* The library exports the following functions for general use */
+
+/* Call this after arguments have been parsed to initialize the library.
+ You must call this before calling any other diskfs functions, and after
+ parsing diskfs options. */
+error_t diskfs_init_diskfs (void);
+
+/* Call this once the filesystem is fully initialized, to advertise the new
+ filesystem control port to our parent filesystem. If BOOTSTRAP is set,
+ the diskfs will call fsys_startup on that port as appropriate and return
+ the REALNODE returned in that call; otherwise we return MACH_PORT_NULL.
+ FLAGS specifies how to open REALNODE (from the O_* set). */
+mach_port_t diskfs_startup_diskfs (mach_port_t bootstrap, int flags);
+
+/* Call this after all format-specific initialization is done (except
+ for setting diskfs_root_node); at this point the pagers should be
+ ready to go. DEMUXER is the demuxer to user. Normally, this is
+ just diskfs_demuxer. */
+void diskfs_spawn_first_thread (ports_demuxer_type demuxer);
+
+/* Once diskfs_root_node is set, call this if we are a bootstrap
+ filesystem. If you call this, then the library will call
+ diskfs_init_completed once it has a valid proc and auth port. */
+void diskfs_start_bootstrap ();
+
+/* Node NP now has no more references; clean all state. The
+ _diskfs_node_refcnt_lock must be held, and will be released
+ upon return. NP must be locked. */
+void diskfs_drop_node (struct node *np);
+
+/* Set on disk fields from NP->dn_stat; update ctime, atime, and mtime
+ if necessary. If WAIT is true, then return only after the physical
+ media has been completely updated. */
+void diskfs_node_update (struct node *np, int wait);
+
+/* Add a hard reference to a node. If there were no hard
+ references previously, then the node cannot be locked
+ (because you must hold a hard reference to hold the lock). */
+void diskfs_nref (struct node *np);
+
+/* Unlock node NP and release a hard reference; if this is the last
+ hard reference and there are no links to the file then request
+ soft references to be dropped. */
+void diskfs_nput (struct node *np);
+
+/* Release a hard reference on NP. If NP is locked by anyone, then
+ this cannot be the last hard reference (because you must hold a
+ hard reference in order to hold the lock). If this is the last
+ hard reference and there are no links, then request soft references
+ to be dropped. */
+void diskfs_nrele (struct node *np);
+
+/* Add a light reference to a node. */
+void diskfs_nref_light (struct node *np);
+
+/* Unlock node NP and release a light reference */
+void diskfs_nput_light (struct node *np);
+
+/* Release a light reference on NP. If NP is locked by anyone, then
+ this cannot be the last reference (because you must hold a
+ hard reference in order to hold the lock). */
+void diskfs_nrele_light (struct node *np);
+
+/* Reading and writing of files. this is called by other filesystem
+ routines and handles extension of files automatically. NP is the
+ node to be read or written, and must be locked. DATA will be
+ written or filled. OFF identifies where in thi fel the I/O is to
+ take place (-1 is not allowed). AMT is the size of DATA and tells
+ how much to copy. DIR is 1 for writing and 0 for reading. CRED is
+ the user doing the access (only used to validate attempted file
+ extension). For reads, *AMTREAD is filled with the amount actually
+ read. */
+error_t
+diskfs_node_rdwr (struct node *np, char *data, loff_t off,
+ size_t amt, int dir, struct protid *cred,
+ size_t *amtread);
+
+
+/* Send notifications to users who have requested them with
+ dir_notice_changes for directory DP. The type of modification and
+ affected name are TYPE and NAME respectively. This should be
+ called by diskfs_direnter, diskfs_dirremove, and diskfs_dirrewrite,
+ and anything else that changes the directory, after the change is
+ fully completed. */
+void
+diskfs_notice_dirchange (struct node *dp, enum dir_changed_type type,
+ const char *name);
+
+/* Send notifications to users who have requested them with
+ file_notice_changes for file NP. The type of modification is TYPE.
+ START and END identify the affected region of the file's data.
+ This should be called after the change is fully completed. */
+void
+diskfs_notice_filechange (struct node *np, enum file_changed_type type,
+ loff_t start, loff_t end);
+
+/* Create a new node structure with DS as its physical disknode.
+ The new node will have one hard reference and no light references. */
+struct node *diskfs_make_node (struct disknode *dn);
+
+/* Create a new node structure. Also allocate SIZE bytes for the
+ disknode. The address of the disknode can be obtained using
+ diskfs_node_disknode. The new node will have one hard reference
+ and no light references. */
+struct node *diskfs_make_node_alloc (size_t size);
+
+/* To avoid breaking the ABI whenever sizeof (struct node) changes, we
+ explicitly provide the size. The following two functions will use
+ this value for offset calculations. */
+extern const size_t _diskfs_sizeof_struct_node;
+
+/* Return the address of the disknode for NODE. NODE must have been
+ allocated using diskfs_make_node_alloc. */
+static inline struct disknode *
+diskfs_node_disknode (struct node *node)
+{
+ return (struct disknode *) ((char *) node + _diskfs_sizeof_struct_node);
+}
+
+/* Return the address of the node for DISKNODE. DISKNODE must have
+ been allocated using diskfs_make_node_alloc. */
+static inline struct node *
+diskfs_disknode_node (struct disknode *disknode)
+{
+ return (struct node *) ((char *) disknode - _diskfs_sizeof_struct_node);
+}
+
+
+/* The library also exports the following functions; they are not generally
+ useful unless you are redefining other functions the library provides. */
+
+/* Lookup in directory DP (which is locked) the name NAME. TYPE will
+ either be LOOKUP, CREATE, RENAME, or REMOVE. CRED identifies the
+ user making the call.
+
+ NAME will have leading and trailing slashes stripped. It is an
+ error if there are internal slashes. NAME will be modified in
+ place if there are slashes in it; it is therefore an error to
+ specify a constant NAME which contains slashes.
+
+ If the name is found, return zero, and (if NP is nonzero) set *NP
+ to point to the node for it, locked. If the name is not found,
+ return ENOENT, and (if NP is nonzero) set *NP to zero. If NP is
+ zero, then the node found must not be locked, even transitorily.
+ Lookups for REMOVE and RENAME (which must often check permissions
+ on the node being found) will always set NP.
+
+ If DS is nonzero then:
+ For LOOKUP: set *DS to be ignored by diskfs_drop_dirstat.
+ For CREATE: on success, set *DS to be ignored by diskfs_drop_dirstat.
+ on failure, set *DS for a future call to diskfs_direnter.
+ For RENAME: on success, set *DS for a future call to diskfs_dirrewrite.
+ on failure, set *DS for a future call to diskfs_direnter.
+ For REMOVE: on success, set *DS for a future call to diskfs_dirremove.
+ on failure, set *DS to be ignored by diskfs_drop_dirstat.
+ The caller of this function guarantees that if DS is nonzero, then
+ either the appropriate call listed above or diskfs_drop_dirstat will
+ be called with DS before the directory DP is unlocked, and guarantees
+ that no lookup calls will be made on this directory between this
+ lookup and the use (or descruction) of *DS.
+
+ If you use the library's versions of diskfs_rename_dir,
+ diskfs_clear_directory, and diskfs_init_dir, then lookups for `..'
+ might have the flag SPEC_DOTDOT or'd in. This has the following special
+ meaning:
+ For LOOKUP: DP should be unlocked and its reference dropped before
+ returning.
+ For RENAME and REMOVE: The node being found (*NP) is already held
+ locked, so don't lock it or add a reference to it.
+ (SPEC_DOTDOT will not be given with CREATE.)
+
+ Return ENOTDIR if DP is not a directory.
+ Return EACCES if CRED isn't allowed to search DP.
+ Return EACCES if completing the operation will require writing
+ the directory and diskfs_checkdirmod won't allow the modification.
+ Return ENOENT if NAME isn't in the directory.
+ Return EAGAIN if NAME refers to the `..' of this filesystem's root.
+ Return EIO if appropriate.
+
+ This function is a wrapper for diskfs_lookup_hard.
+*/
+error_t diskfs_lookup (struct node *dp,
+ const char *name, enum lookup_type type,
+ struct node **np, struct dirstat *ds,
+ struct protid *cred);
+
+/* Add NP to directory DP under the name NAME. This will only be
+ called after an unsuccessful call to diskfs_lookup of type CREATE
+ or RENAME; DP has been locked continuously since that call and DS
+ is as that call set it, NP is locked. CRED identifies the user
+ responsible for the call (to be used only to validate directory
+ growth). This function is a wrapper for diskfs_direnter_hard. */
+error_t
+diskfs_direnter (struct node *dp, const char *name, struct node *np,
+ struct dirstat *ds, struct protid *cred);
+
+/* This will only be called after a successful call to diskfs_lookup
+ of type RENAME; this call should change the name found in directory
+ DP to point to node NP instead of its previous referent, OLDNP. DP
+ has been locked continuously since the call to diskfs_lookup and DS
+ is as that call set it; NP is locked. This routine should call
+ diskfs_notice_dirchange if DP->dirmod_reqs is nonzero. NAME is the
+ name of OLDNP inside DP; it is this reference which is being
+ rewritten. This function is a wrapper for diskfs_dirrewrite_hard. */
+error_t diskfs_dirrewrite (struct node *dp, struct node *oldnp,
+ struct node *np, const char *name,
+ struct dirstat *ds);
+
+/* This will only be called after a successful call to diskfs_lookup
+ of type REMOVE; this call should remove the name found from the
+ directory DS. DP has been locked continuously since the call to
+ diskfs_lookup and DS is as that call set it. This routine should
+ call diskfs_notice_dirchange if DP->dirmod_reqs is nonzero. This
+ function is a wrapper for diskfs_dirremove_hard. The entry being
+ removed has name NAME and refers to NP. */
+error_t diskfs_dirremove (struct node *dp, struct node *np,
+ const char *name, struct dirstat *ds);
+
+/* Return the node corresponding to CACHE_ID in *NPP. */
+error_t diskfs_cached_lookup (ino64_t cache_id, struct node **npp);
+
+/* Create a new node. Give it MODE; if that includes IFDIR, also
+ initialize `.' and `..' in the new directory. Return the node in NPP.
+ CRED identifies the user responsible for the call. If NAME is nonzero,
+ then link the new node into DIR with name NAME; DS is the result of a
+ prior diskfs_lookup for creation (and DIR has been held locked since).
+ DIR must always be provided as at least a hint for disk allocation
+ strategies. */
+error_t
+diskfs_create_node (struct node *dir, const char *name, mode_t mode,
+ struct node **newnode, struct protid *cred,
+ struct dirstat *ds);
+
+/* Create and return a protid for an existing peropen PO in CRED,
+ referring to user USER. */
+error_t diskfs_create_protid (struct peropen *po, struct iouser *user,
+ struct protid **cred);
+
+/* Build and return in CRED a protid which has no user identification, for
+ peropen PO. */
+error_t diskfs_start_protid (struct peropen *po, struct protid **cred);
+
+/* Finish building protid CRED started with diskfs_start_protid;
+ the user to install is USER. */
+void diskfs_finish_protid (struct protid *cred, struct iouser *user);
+
+extern struct protid * diskfs_begin_using_protid_port (file_t port);
+extern struct diskfs_control * diskfs_begin_using_control_port (fsys_t port);
+extern struct bootinfo *diskfs_begin_using_bootinfo_port (exec_startup_t port);
+
+extern void diskfs_end_using_protid_port (struct protid *cred);
+extern void diskfs_end_using_control_port (struct diskfs_control *cred);
+extern void diskfs_end_using_bootinfo (struct bootinfo *upt);
+
+#if defined(__USE_EXTERN_INLINES) || defined(DISKFS_DEFINE_EXTERN_INLINE)
+
+/* Called by MiG to translate ports into struct protid *.
+ fsmutations.h arranges for this to happen for the io and
+ fs interfaces. */
+DISKFS_EXTERN_INLINE struct protid *
+diskfs_begin_using_protid_port (file_t port)
+{
+ return ports_lookup_port (diskfs_port_bucket, port, diskfs_protid_class);
+}
+
+/* And for the fsys interface. */
+DISKFS_EXTERN_INLINE struct diskfs_control *
+diskfs_begin_using_control_port (fsys_t port)
+{
+ return ports_lookup_port (diskfs_port_bucket, port, NULL);
+}
+
+/* And for the exec_startup interface. */
+DISKFS_EXTERN_INLINE struct bootinfo *
+diskfs_begin_using_bootinfo_port (exec_startup_t port)
+{
+ return ports_lookup_port (diskfs_port_bucket, port, diskfs_execboot_class);
+}
+
+
+/* Called by MiG after server routines have been run; this
+ balances begin_using_protid_port, and is arranged for the io
+ and fs interfaces by fsmutations.h. */
+DISKFS_EXTERN_INLINE void
+diskfs_end_using_protid_port (struct protid *cred)
+{
+ if (cred)
+ ports_port_deref (cred);
+}
+
+/* And for the fsys interface. */
+DISKFS_EXTERN_INLINE void
+diskfs_end_using_control_port (struct diskfs_control *cred)
+{
+ if (cred)
+ ports_port_deref (cred);
+}
+
+/* And for the exec_startup interface. */
+DISKFS_EXTERN_INLINE void
+diskfs_end_using_bootinfo (struct bootinfo *b)
+{
+ if (b)
+ ports_port_deref (b);
+}
+
+#endif /* Use extern inlines. */
+
+/* Called when a protid CRED has no more references. (Because references\
+ to protids are maintained by the port management library, this is
+ installed in the clean routines list.) The ports library will
+ free the structure for us. */
+void diskfs_protid_rele (void *arg);
+
+/* Create a new peropen structure on node NP with open flags FLAGS in
+ *PPO. The initial values for the root_parent, shadow_root, and
+ shadow_root_parent fields are copied from CONTEXT if it's non-zero,
+ otherwise they are zeroed. */
+error_t
+diskfs_make_peropen (struct node *np, int flags,
+ struct peropen *context, struct peropen **ppo);
+
+/* Decrement the reference count on a peropen structure. */
+void diskfs_release_peropen (struct peropen *po);
+
+/* Node NP has just been found in DIR with NAME. If NP is null, that
+ means that this name has been confirmed as absent in the directory. */
+void diskfs_enter_lookup_cache (struct node *dir, struct node *np,
+ const char *name);
+
+/* Purge all references in the cache to NP as a node inside
+ directory DP. */
+void diskfs_purge_lookup_cache (struct node *dp, struct node *np);
+
+/* Scan the cache looking for NAME inside DIR. If we don't know
+ anything entry at all, then return 0. If the entry is confirmed to
+ not exist, then return -1. Otherwise, return NP for the entry, with
+ a newly allocated reference. */
+struct node *diskfs_check_lookup_cache (struct node *dir, const char *name);
+
+/* Rename directory node FNP (whose parent is FDP, and which has name
+ FROMNAME in that directory) to have name TONAME inside directory
+ TDP. None of these nodes are locked, and none should be locked
+ upon return. This routine is serialized, so it doesn't have to be
+ reentrant. Directories will never be renamed except by this
+ routine. FROMCRED and TOCRED are the users responsible for
+ FDP/FNP and TDP respectively. This routine assumes the usual
+ convention where `.' and `..' are represented by ordinary links;
+ if that is not true for your format, you have to redefine this
+ function.*/
+error_t
+diskfs_rename_dir (struct node *fdp, struct node *fnp, const char *fromname,
+ struct node *tdp, const char *toname,
+ struct protid *fromcred, struct protid *tocred);
+
+/* Clear the `.' and `..' entries from directory DP. Its parent is
+ PDP, and the user responsible for this is identified by CRED. Both
+ directories must be locked. This routine assumes the usual
+ convention where `.' and `..' are represented by ordinary links; if
+ that is not true for your format, you have to redefine this
+ function. */
+error_t diskfs_clear_directory (struct node *dp, struct node *pdp,
+ struct protid *cred);
+
+/* Locked node DP is a new directory; add whatever links are necessary
+ to give it structure; its parent is the (locked) node PDP.
+ This routine may not call diskfs_lookup on PDP. The new directory
+ must be clear within the meaning of diskfs_dirempty. This routine
+ assumes the usual convention where `.' and `..' are represented by
+ ordinary links; if that is not true for your format, you have to
+ redefine this function. CRED identifies the user making the call. */
+error_t
+diskfs_init_dir (struct node *dp, struct node *pdp, struct protid *cred);
+
+/* If disk is not readonly and the noatime option is not enabled, set
+ NP->dn_set_atime. */
+void diskfs_set_node_atime (struct node *np);
+
+/* If NP->dn_set_ctime is set, then modify NP->dn_stat.st_ctim
+ appropriately; do the analogous operation for atime and mtime as well. */
+void diskfs_set_node_times (struct node *np);
+
+/* Shutdown the filesystem; flags are as for fsys_goaway. */
+error_t diskfs_shutdown (int flags);
+
+/* Change an active filesystem between read-only and writable modes, setting
+ the global variable DISKFS_READONLY to reflect the current mode. If an
+ error is returned, nothing will have changed. DISKFS_FSYS_LOCK should be
+ held while calling this routine. */
+error_t diskfs_set_readonly (int readonly);
+
+/* Re-read all incore data structures from disk. This will only work if
+ DISKFS_READONLY is true. DISKFS_FSYS_LOCK should be held while calling
+ this routine. */
+error_t diskfs_remount ();
+
+/* Called by S_fsys_startup for execserver bootstrap. The execserver
+ is able to function without a real node, hence this fraud. Arguments
+ are all as for fsys_startup in <hurd/fsys.defs>. */
+error_t diskfs_execboot_fsys_startup (mach_port_t port, int flags,
+ mach_port_t ctl, mach_port_t *real,
+ mach_msg_type_name_t *realpoly);
+
+/* Establish a thread to sync the filesystem every INTERVAL seconds, or
+ never, if INTERVAL is zero. If an error occurs creating the thread, it is
+ returned, otherwise 0. Subsequent calls will create a new thread and
+ (eventually) get rid of the old one; the old thread won't do any more
+ syncs, regardless. */
+error_t diskfs_set_sync_interval (int interval);
+
+/* Parse and execute the runtime options in ARGZ & ARGZ_LEN. EINVAL is
+ returned if some option is unrecognized. The default definition of this
+ routine will parse them using DISKFS_RUNTIME_ARGP, which see. */
+error_t diskfs_set_options (const char *argz, size_t argz_len);
+
+/* Append to the malloced string *ARGZ of length *ARGZ_LEN a NUL-separated
+ list of the arguments to this translator. The default definition of this
+ routine simply calls diskfs_append_std_options. */
+error_t diskfs_append_args (char **argz, size_t *argz_len);
+
+/* If this is defined or set to an argp structure, it will be used by the
+ default diskfs_set_options to handle runtime option parsing. The default
+ definition is initialized to a pointer to DISKFS_STD_RUNTIME_ARGP. */
+extern struct argp *diskfs_runtime_argp;
+
+/* An argp for the standard diskfs runtime options. The default definition
+ of DISKFS_RUNTIME_ARGP points to this, although if the user redefines
+ that, he may chain this onto his argp as well. */
+extern const struct argp diskfs_std_runtime_argp;
+
+/* An argp structure for the standard diskfs command line arguments. The
+ user may call argp_parse on this to parse the command line, chain it onto
+ the end of his own argp structure, or ignore it completely. */
+extern const struct argp diskfs_startup_argp;
+
+/* An argp structure for the standard diskfs command line arguments plus a
+ store specification. The address of a location in which to return the
+ resulting struct store_parsed structure should be passed as the input
+ argument to argp_parse; see the declaration for STORE_ARGP in
+ <hurd/store.h> for more information. */
+extern const struct argp diskfs_store_startup_argp;
+
+/* *Appends* to ARGZ & ARGZ_LEN '\0'-separated options describing the standard
+ diskfs option state (note that unlike diskfs_get_options, ARGZ & ARGZ_LEN
+ must already have a sane value). */
+error_t diskfs_append_std_options (char **argz, size_t *argz_len);
+
+/* Demultiplex incoming messages on ports created by libdiskfs. */
+int diskfs_demuxer (mach_msg_header_t *, mach_msg_header_t *);
+
+/* Check if the filesystem is readonly before an operation that
+ writes it. Return 1 if readonly, zero otherwise. */
+int diskfs_check_readonly (void);
+
+/* The diskfs library provides functions to demultiplex the fs, io,
+ fsys, interrupt, and notify interfaces. All the server routines
+ have the prefix `diskfs_S_'; `in' arguments of type file_t or io_t
+ appear as `struct protid *' to the stub. */
+
+
+/* All-in-one initialization function for diskfs filesystems using
+ libstore. This parses arguments using STARTUP_ARGP (defaulting to
+ diskfs_store_startup_argp if it's null; note that the ARGP_IN_ORDER
+ flag is always used); it calls diskfs_init_diskfs; it opens the
+ store with store_parsed_open, and sets diskfs_hard_readonly and
+ diskfs_readonly if the store is unwritable; it calls
+ diskfs_spawn_first_thread; finally, it returns the store and its
+ description in *STORE and *STORE_PARSED, and the bootstrap port in
+ *BOOTSTRAP. The caller should pass *BOOTSTRAP to
+ diskfs_startup_diskfs after setting diskfs_root_node.
+ (See <argp.h> and <hurd/store.h>.)
+
+ This call cannot return failure; if it encounters a fatal problem,
+ it prints a diagnostic on stderr (or the console) and exits the
+ program. */
+struct store *diskfs_init_main (struct argp *startup_argp,
+ int argc, char **argv,
+ struct store_parsed **store_parsed,
+ mach_port_t *bootstrap);
+
+/* The following are optional convenience routines and global variable, which
+ can be used by any user program that uses a mach device to hold the
+ underlying filesystem. */
+
+/* Make errors go somewhere reasonable. */
+void diskfs_console_stdio ();
+
+#endif /* hurd/diskfs.h */
diff --git a/libdiskfs/extern-inline.c b/libdiskfs/extern-inline.c
new file mode 100644
index 00000000..43de88d6
--- /dev/null
+++ b/libdiskfs/extern-inline.c
@@ -0,0 +1,20 @@
+/* Run time callable functions for extern inlines.
+ Copyright (C) 2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define DISKFS_DEFINE_EXTERN_INLINE
+
+#include "diskfs.h"
diff --git a/libdiskfs/extra-version.c b/libdiskfs/extra-version.c
new file mode 100644
index 00000000..b1d78084
--- /dev/null
+++ b/libdiskfs/extra-version.c
@@ -0,0 +1,24 @@
+/* Default value for diskfs_extra_version
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include "priv.h"
+
+char *diskfs_extra_version = "";
diff --git a/libdiskfs/fhandle.h b/libdiskfs/fhandle.h
new file mode 100644
index 00000000..bd827d84
--- /dev/null
+++ b/libdiskfs/fhandle.h
@@ -0,0 +1,36 @@
+/* File handle type (for nfs server support)
+
+ Copyright (C) 1997,99 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __FHANDLE_H__
+#define __FHANDLE_H__
+
+/* Must be exactly 28 bytes long */
+union diskfs_fhandle
+{
+ unsigned char bytes[28];
+ struct
+ {
+ int pad1;
+ int cache_id;
+ unsigned int gen;
+ } data;
+};
+
+#endif /* __FHANDLE_H__ */
diff --git a/libdiskfs/file-access.c b/libdiskfs/file-access.c
new file mode 100644
index 00000000..f7e129aa
--- /dev/null
+++ b/libdiskfs/file-access.c
@@ -0,0 +1,44 @@
+/*
+ Copyright (C) 1994 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+#include <fcntl.h>
+
+kern_return_t
+diskfs_S_file_check_access (struct protid *cred,
+ int *type)
+{
+ struct node *np;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+ pthread_mutex_lock (&np->lock);
+ *type = 0;
+ if (fshelp_access (&np->dn_stat, S_IREAD, cred->user) == 0)
+ *type |= O_READ;
+ if (fshelp_access (&np->dn_stat, S_IWRITE, cred->user) == 0)
+ *type |= O_WRITE;
+ if (fshelp_access (&np->dn_stat, S_IEXEC, cred->user) == 0)
+ *type |= O_EXEC;
+
+ pthread_mutex_unlock (&np->lock);
+
+ return 0;
+}
diff --git a/libdiskfs/file-chauthor.c b/libdiskfs/file-chauthor.c
new file mode 100644
index 00000000..6e49c53f
--- /dev/null
+++ b/libdiskfs/file-chauthor.c
@@ -0,0 +1,42 @@
+/* libdithkfth implementation of fth.defth: file_chauthor
+ Copyright (C) 1992, 1993, 1994, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "lithp.h"
+
+#include "priv.h"
+#include fth_TH_dot_h
+
+/* Implement file_chauthor as dethcribed in <hurd/fth.defth>. */
+kern_return_t
+dithkfth_TH_file_chauthor (struct protid *cred,
+ uid_t author)
+{
+ CHANGE_NODE_FIELD (cred,
+ ({
+ err = fthhelp_ithowner (&np->dn_thtat, cred->uther);
+ if (!err)
+ err = dithkfth_validate_author_change (np, author);
+ if (!err)
+ {
+ np->dn_thtat.tht_author = author;
+ np->dn_thet_theetime = 1;
+ if (np->filemod_reqs)
+ diskfs_notice_filechange(np, FILE_CHANGED_META,
+ 0, 0);
+ }
+ }));
+}
diff --git a/libdiskfs/file-chflags.c b/libdiskfs/file-chflags.c
new file mode 100644
index 00000000..01dc495c
--- /dev/null
+++ b/libdiskfs/file-chflags.c
@@ -0,0 +1,40 @@
+/* libdiskfs implementation of fs.defs:file_chflags
+ Copyright (C) 1992, 1993, 1994, 1996, 1998 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+/* Implement file_chflags as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_file_chflags (struct protid *cred,
+ int flags)
+{
+ CHANGE_NODE_FIELD (cred,
+ ({
+ err = fshelp_isowner (&np->dn_stat, cred->user);
+ if (!err)
+ err = diskfs_validate_flags_change (np, flags);
+ if (!err)
+ {
+ np->dn_stat.st_flags = flags;
+ np->dn_set_ctime = 1;
+ }
+ if (!err && np->filemod_reqs)
+ diskfs_notice_filechange(np, FILE_CHANGED_META,
+ 0, 0);
+ }));
+}
diff --git a/libdiskfs/file-chg.c b/libdiskfs/file-chg.c
new file mode 100644
index 00000000..9da43e79
--- /dev/null
+++ b/libdiskfs/file-chg.c
@@ -0,0 +1,71 @@
+/*
+ Copyright (C) 1994, 1995 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_notify_S.h"
+#include "fs_notify_U.h"
+
+kern_return_t
+diskfs_S_file_notice_changes (struct protid *cred, mach_port_t notify)
+{
+ error_t err;
+ struct modreq *req;
+ struct node *np;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+ pthread_mutex_lock (&np->lock);
+ err = file_changed (notify, np->filemod_tick, FILE_CHANGED_NULL, 0, 0);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return err;
+ }
+ req = malloc (sizeof (struct modreq));
+ req->port = notify;
+ req->next = np->filemod_reqs;
+ np->filemod_reqs = req;
+ pthread_mutex_unlock (&np->lock);
+ return 0;
+}
+
+void
+diskfs_notice_filechange (struct node *dp, enum file_changed_type type,
+ off_t start, off_t end)
+{
+ error_t err;
+ struct modreq **preq;
+
+ dp->filemod_tick++;
+ preq = &dp->filemod_reqs;
+ while (*preq)
+ {
+ struct modreq *req = *preq;
+ err = file_changed (req->port, dp->filemod_tick, type, start, end);
+ if (err && err != MACH_SEND_TIMED_OUT)
+ {
+ /* Remove notify port. */
+ *preq = req->next;
+ mach_port_deallocate (mach_task_self (), req->port);
+ free (req);
+ }
+ else
+ preq = &req->next;
+ }
+}
diff --git a/libdiskfs/file-chmod.c b/libdiskfs/file-chmod.c
new file mode 100644
index 00000000..df262ea3
--- /dev/null
+++ b/libdiskfs/file-chmod.c
@@ -0,0 +1,57 @@
+/* libdiskfs implementation of fs.defs: file_chmod
+ Copyright (C) 1992,93,94,96,97,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+/* Implement file_chmod as described in <hurd/fs.defs>. */
+error_t
+diskfs_S_file_chmod (struct protid *cred,
+ mode_t mode)
+{
+ mode &= ~(S_IFMT | S_ISPARE | S_ITRANS);
+
+ CHANGE_NODE_FIELD (cred,
+ ({
+ if (!(err = fshelp_isowner (&np->dn_stat, cred->user)))
+ {
+ if (!idvec_contains (cred->user->uids, 0))
+ {
+ if (!S_ISDIR (np->dn_stat.st_mode))
+ mode &= ~S_ISVTX;
+ if (!idvec_contains (cred->user->gids,
+ np->dn_stat.st_gid))
+ mode &= ~S_ISGID;
+ if (!idvec_contains (cred->user->uids,
+ np->dn_stat.st_uid))
+ mode &= ~S_ISUID;
+ }
+ mode |= (np->dn_stat.st_mode
+ & (S_IFMT | S_ISPARE | S_ITRANS));
+ err = diskfs_validate_mode_change (np, mode);
+ if (!err)
+ {
+ np->dn_stat.st_mode = mode;
+ np->dn_set_ctime = 1;
+ if (np->filemod_reqs)
+ diskfs_notice_filechange (np,
+ FILE_CHANGED_META,
+ 0, 0);
+ }
+ }
+ }));
+}
diff --git a/libdiskfs/file-chown.c b/libdiskfs/file-chown.c
new file mode 100644
index 00000000..ecb851f2
--- /dev/null
+++ b/libdiskfs/file-chown.c
@@ -0,0 +1,64 @@
+/* libdiskfs implementetation of fs.defs: file_chown
+ Copyright (C) 1992, 1993, 1994, 1996, 1999 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+/* Implement file_chown as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_file_chown (struct protid *cred,
+ uid_t uid,
+ gid_t gid)
+{
+ if (uid == (uid_t) -1 && gid == (gid_t) -1) /* No change requested. */
+ return 0;
+
+ CHANGE_NODE_FIELD (cred,
+ ({
+ err = fshelp_isowner (&np->dn_stat, cred->user);
+ if (err
+ || (((uid != (uid_t) -1
+ && !idvec_contains (cred->user->uids, uid))
+ || (gid != (gid_t) -1
+ && !idvec_contains (cred->user->gids, gid)))
+ && !idvec_contains (cred->user->uids, 0)))
+ err = EPERM;
+ else
+ {
+ if (uid != (uid_t) -1)
+ err = diskfs_validate_owner_change (np, uid);
+ if (!err && gid != (gid_t) -1)
+ err = diskfs_validate_group_change (np, gid);
+ if (!err)
+ {
+ if (uid != (uid_t) -1)
+ {
+ np->dn_stat.st_uid = uid;
+ if (np->author_tracks_uid)
+ np->dn_stat.st_author = uid;
+ }
+ if (gid != (gid_t) -1)
+ np->dn_stat.st_gid = gid;
+ np->dn_set_ctime = 1;
+ if (np->filemod_reqs)
+ diskfs_notice_filechange(np,
+ FILE_CHANGED_META,
+ 0, 0);
+ }
+ }
+ }));
+}
diff --git a/libdiskfs/file-exec.c b/libdiskfs/file-exec.c
new file mode 100644
index 00000000..9572dbed
--- /dev/null
+++ b/libdiskfs/file-exec.c
@@ -0,0 +1,212 @@
+/* File execution (file_exec RPC) for diskfs servers, using exec server.
+ Copyright (C) 1993,94,95,96,97,98,2000,02 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "fs_S.h"
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <hurd/exec.h>
+#include <hurd/paths.h>
+#include <string.h>
+#include <idvec.h>
+
+kern_return_t
+diskfs_S_file_exec (struct protid *cred,
+ task_t task,
+ int flags,
+ char *argv,
+ size_t argvlen,
+ char *envp,
+ size_t envplen,
+ mach_port_t *fds,
+ size_t fdslen,
+ mach_port_t *portarray,
+ size_t portarraylen,
+ int *intarray,
+ size_t intarraylen,
+ mach_port_t *deallocnames,
+ size_t deallocnameslen,
+ mach_port_t *destroynames,
+ size_t destroynameslen)
+{
+ struct node *np;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ int suid, sgid;
+ struct protid *newpi;
+ struct peropen *newpo;
+ error_t err = 0;
+ mach_port_t execserver;
+ int cached_exec;
+ struct hurd_userlink ulink;
+ mach_port_t right;
+
+#define RETURN(code) do { err = (code); goto out; } while (0)
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ /* Get a light reference to the cached exec server port. */
+ execserver = _hurd_port_get (&_diskfs_exec_portcell, &ulink);
+ cached_exec = (execserver != MACH_PORT_NULL);
+ if (execserver == MACH_PORT_NULL)
+ {
+ /* No cached port. Look up the canonical naming point. */
+ execserver = file_name_lookup (_SERVERS_EXEC, 0, 0);
+ if (execserver == MACH_PORT_NULL)
+ return EOPNOTSUPP; /* No exec server, no exec. */
+ else
+ {
+ /* Install the newly-gotten exec server port for other
+ threads to use, then get a light reference for this call. */
+ _hurd_port_set (&_diskfs_exec_portcell, execserver);
+ execserver = _hurd_port_get (&_diskfs_exec_portcell, &ulink);
+ }
+ }
+
+ np = cred->po->np;
+
+ pthread_mutex_lock (&np->lock);
+ mode = np->dn_stat.st_mode;
+ uid = np->dn_stat.st_uid;
+ gid = np->dn_stat.st_gid;
+ pthread_mutex_unlock (&np->lock);
+
+ if (_diskfs_noexec)
+ RETURN (EACCES);
+
+ if ((cred->po->openstat & O_EXEC) == 0)
+ RETURN (EBADF);
+
+ if (!((mode & (S_IXUSR|S_IXGRP|S_IXOTH))
+ || ((mode & S_IUSEUNK) && (mode & (S_IEXEC << S_IUNKSHIFT)))))
+ RETURN (EACCES);
+
+ if ((mode & S_IFMT) == S_IFDIR)
+ RETURN (EACCES);
+
+ suid = mode & S_ISUID;
+ sgid = mode & S_ISGID;
+ if (!_diskfs_nosuid && (suid || sgid))
+ {
+ int secure = 0;
+ error_t get_file_ids (struct idvec *uids, struct idvec *gids)
+ {
+ error_t err = idvec_merge (uids, cred->user->uids);
+ if (! err)
+ err = idvec_merge (gids, cred->user->gids);
+ return err;
+ }
+ err =
+ fshelp_exec_reauth (suid, uid, sgid, gid,
+ diskfs_auth_server_port, get_file_ids,
+ portarray, portarraylen, fds, fdslen, &secure);
+ if (secure)
+ flags |= EXEC_SECURE | EXEC_NEWTASK;
+ }
+
+ /* If the user can't read the file, then we should use a new task,
+ which would be inaccessible to the user. Actually, this doesn't
+ work, because the proc server will still give out the task port
+ to the user. Too many things depend on that that it can't be
+ changed. So this vague attempt isn't even worth trying. */
+#if 0
+ if (fshelp_access (&np->dn_stat, S_IREAD, cred->user))
+ flags |= EXEC_NEWTASK;
+#endif
+
+ if (! err)
+ /* Make a new peropen for the exec server to access the file, since any
+ seeking the exec server might want to do should not affect the
+ original peropen on which file_exec was called. (The new protid for
+ this peropen clones the caller's iouser to preserve the caller's
+ authentication credentials.) The new peropen's openmodes must have
+ O_READ even if the caller had only O_EXEC privilege, so the exec
+ server can read the executable file. We also include O_EXEC so that
+ the exec server can turn this peropen into a file descriptor in the
+ target process and permit it to exec its /dev/fd/N pseudo-file. */
+ {
+ err = diskfs_make_peropen (np, O_READ|O_EXEC, cred->po, &newpo);
+ if (! err)
+ {
+ err = diskfs_create_protid (newpo, cred->user, &newpi);
+ if (err)
+ diskfs_release_peropen (newpo);
+ }
+ }
+
+ if (! err)
+ {
+ do
+ {
+ right = ports_get_send_right (newpi);
+ err = exec_exec (execserver,
+ right, MACH_MSG_TYPE_COPY_SEND,
+ task, flags, argv, argvlen, envp, envplen,
+ fds, MACH_MSG_TYPE_COPY_SEND, fdslen,
+ portarray, MACH_MSG_TYPE_COPY_SEND, portarraylen,
+ intarray, intarraylen,
+ deallocnames, deallocnameslen,
+ destroynames, destroynameslen);
+ mach_port_deallocate (mach_task_self (), right);
+ if (err == MACH_SEND_INVALID_DEST)
+ {
+ if (cached_exec)
+ {
+ /* We were using a previously looked-up exec server port.
+ Try looking up a new one before giving an error. */
+ cached_exec = 0;
+ _hurd_port_free (&_diskfs_exec_portcell, &ulink, execserver);
+
+ execserver = file_name_lookup (_SERVERS_EXEC, 0, 0);
+ if (execserver == MACH_PORT_NULL)
+ err = EOPNOTSUPP;
+ else
+ {
+ _hurd_port_set (&_diskfs_exec_portcell, execserver);
+ execserver = _hurd_port_get (&_diskfs_exec_portcell,
+ &ulink);
+ }
+ }
+ else
+ err = EOPNOTSUPP;
+ }
+ } while (err == MACH_SEND_INVALID_DEST);
+ ports_port_deref (newpi);
+ }
+
+ if (! err)
+ {
+ unsigned int i;
+
+ mach_port_deallocate (mach_task_self (), task);
+ for (i = 0; i < fdslen; i++)
+ mach_port_deallocate (mach_task_self (), fds[i]);
+ for (i = 0; i < portarraylen; i++)
+ mach_port_deallocate (mach_task_self (), portarray[i]);
+ }
+
+ out:
+ _hurd_port_free (&_diskfs_exec_portcell, &ulink, execserver);
+
+ return err;
+}
diff --git a/libdiskfs/file-get-children.c b/libdiskfs/file-get-children.c
new file mode 100644
index 00000000..4581e4e0
--- /dev/null
+++ b/libdiskfs/file-get-children.c
@@ -0,0 +1,95 @@
+/* file_get_children
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+#include <argz.h>
+
+/* Return any active translators bound to nodes of the receiving
+ filesystem. CHILDREN is an argz vector containing file names
+ relative to the root of the receiving translator. */
+error_t
+diskfs_S_file_get_children (struct protid *cred,
+ char **children,
+ mach_msg_type_number_t *children_len)
+{
+ error_t err;
+ if (! cred
+ || cred->pi.bucket != diskfs_port_bucket
+ || cred->pi.class != diskfs_protid_class)
+ return EOPNOTSUPP;
+
+ /* check_access performs the same permission check as is normally
+ done, i.e. it checks that all but the last path components are
+ executable by the requesting user and that the last component is
+ readable. */
+ error_t check_access (const char *path)
+ {
+ error_t err;
+ char *elements = NULL;
+ size_t elements_len = 0;
+
+ err = argz_create_sep (path, '/', &elements, &elements_len);
+ if (err)
+ return err;
+
+ struct node *dp = diskfs_root_node;
+
+ for (char *entry = elements;
+ entry;
+ entry = argz_next (elements, elements_len, entry))
+ {
+ struct node *next;
+ err = diskfs_lookup (dp, entry, LOOKUP, &next, NULL, cred);
+
+ if (dp != diskfs_root_node)
+ diskfs_nput (dp);
+
+ if (err)
+ return err;
+
+ dp = next;
+ }
+
+ err = fshelp_access (&dp->dn_stat, S_IRUSR, cred->user);
+ diskfs_nput (dp);
+ return err;
+ }
+
+
+ char *c = NULL;
+ size_t c_len = 0;
+
+ err = fshelp_get_active_translators (&c, &c_len, check_access);
+ if (err)
+ goto errout;
+
+ err = iohelp_return_malloced_buffer (c, c_len, children, children_len);
+ if (err)
+ goto errout;
+
+ c = NULL; /* c was freed by iohelp_return_malloced_buffer. */
+
+ errout:
+ free (c);
+ return err;
+}
diff --git a/libdiskfs/file-get-fs-opts.c b/libdiskfs/file-get-fs-opts.c
new file mode 100644
index 00000000..d7593118
--- /dev/null
+++ b/libdiskfs/file-get-fs-opts.c
@@ -0,0 +1,54 @@
+/* Get run-time file system options
+
+ Copyright (C) 1995,96,98,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <string.h>
+#include <argz.h>
+#include "priv.h"
+#include "fs_S.h"
+
+error_t
+diskfs_S_file_get_fs_options (struct protid *cred,
+ char **data, size_t *data_len)
+{
+ error_t err;
+ char *argz = 0;
+ size_t argz_len = 0;
+
+ if (! cred)
+ return EOPNOTSUPP;
+
+ err = argz_add (&argz, &argz_len, program_invocation_name);
+ if (err)
+ return err;
+
+ pthread_rwlock_rdlock (&diskfs_fsys_lock);
+ err = diskfs_append_args (&argz, &argz_len);
+ pthread_rwlock_unlock (&diskfs_fsys_lock);
+
+ if (! err)
+ /* Move ARGZ from a malloced buffer into a vm_alloced one. */
+ err = iohelp_return_malloced_buffer (argz, argz_len, data, data_len);
+ else
+ free (argz);
+
+ return err;
+}
diff --git a/libdiskfs/file-get-source.c b/libdiskfs/file-get-source.c
new file mode 100644
index 00000000..b5c31845
--- /dev/null
+++ b/libdiskfs/file-get-source.c
@@ -0,0 +1,37 @@
+/* file_get_source
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+/* Return information about the source of the receiving
+ filesystem. */
+error_t
+diskfs_S_file_get_source (struct protid *cred,
+ char *source)
+{
+ if (! cred
+ || cred->pi.bucket != diskfs_port_bucket
+ || cred->pi.class != diskfs_protid_class)
+ return EOPNOTSUPP;
+
+ return diskfs_get_source (cred, source, 1024 /* XXX */);
+}
diff --git a/libdiskfs/file-get-trans.c b/libdiskfs/file-get-trans.c
new file mode 100644
index 00000000..db5bbdad
--- /dev/null
+++ b/libdiskfs/file-get-trans.c
@@ -0,0 +1,138 @@
+/* libdiskfs implementation of fs.defs: file_get_translator
+ Copyright (C) 1992,93,94,95,96,98,99,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <hurd/paths.h>
+#include <string.h>
+#include <stdio.h>
+#include "fs_S.h"
+
+/* Implement file_get_translator as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_file_get_translator (struct protid *cred,
+ char **trans,
+ size_t *translen)
+{
+ struct node *np;
+ error_t err = 0;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+
+ pthread_mutex_lock (&np->lock);
+
+ /* First look for short-circuited translators. */
+ if (S_ISLNK (np->dn_stat.st_mode))
+ {
+ unsigned int len = sizeof _HURD_SYMLINK + np->dn_stat.st_size + 1;
+ size_t amt;
+ assert (diskfs_shortcut_symlink);
+ if (len > *translen)
+ *trans = mmap (0, len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ bcopy (_HURD_SYMLINK, *trans, sizeof _HURD_SYMLINK);
+
+ if (diskfs_read_symlink_hook)
+ err = (*diskfs_read_symlink_hook) (np,
+ *trans + sizeof _HURD_SYMLINK);
+ if (!diskfs_read_symlink_hook || err == EINVAL)
+ {
+ err = diskfs_node_rdwr (np, *trans + sizeof _HURD_SYMLINK,
+ 0, np->dn_stat.st_size, 0, cred, &amt);
+ if (!err)
+ assert (amt == np->dn_stat.st_size);
+ }
+ if (!err)
+ {
+ (*trans)[sizeof _HURD_SYMLINK + np->dn_stat.st_size] = '\0';
+ *translen = len;
+ }
+ else if (len > *translen)
+ munmap (trans, len);
+ }
+ else if (S_ISCHR (np->dn_stat.st_mode) || S_ISBLK (np->dn_stat.st_mode))
+ {
+ char *buf;
+ unsigned int buflen;
+
+ if (S_ISCHR (np->dn_stat.st_mode))
+ assert (diskfs_shortcut_chrdev);
+ else
+ assert (diskfs_shortcut_blkdev);
+
+ buflen = asprintf (&buf, "%s%c%d%c%d",
+ (S_ISCHR (np->dn_stat.st_mode)
+ ? _HURD_CHRDEV
+ : _HURD_BLKDEV),
+ '\0', (np->dn_stat.st_rdev >> 8) & 0377,
+ '\0', (np->dn_stat.st_rdev) & 0377);
+ buflen++; /* terminating nul */
+
+ if (buflen > *translen)
+ *trans = mmap (0, buflen, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ bcopy (buf, *trans, buflen);
+ free (buf);
+ *translen = buflen;
+ err = 0;
+ }
+ else if (S_ISFIFO (np->dn_stat.st_mode))
+ {
+ unsigned int len;
+
+ len = sizeof _HURD_FIFO;
+ if (len > *translen)
+ *trans = mmap (0, len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ bcopy (_HURD_FIFO, *trans, sizeof _HURD_FIFO);
+ *translen = len;
+ err = 0;
+ }
+ else if (S_ISSOCK (np->dn_stat.st_mode))
+ {
+ unsigned int len;
+
+ len = sizeof _HURD_IFSOCK;
+ if (len > *translen)
+ *trans = mmap (0, len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ bcopy (_HURD_IFSOCK, *trans, sizeof _HURD_IFSOCK);
+ *translen = len;
+ err = 0;
+ }
+ else
+ {
+ if (! (np->dn_stat.st_mode & S_IPTRANS))
+ err = EINVAL;
+ else
+ {
+ char *string;
+ u_int len;
+ err = diskfs_get_translator (np, &string, &len);
+ if (!err)
+ {
+ if (len > *translen)
+ *trans = mmap (0, len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ bcopy (string, *trans, len);
+ *translen = len;
+ free (string);
+ }
+ }
+ }
+
+ pthread_mutex_unlock (&np->lock);
+
+ return err;
+}
diff --git a/libdiskfs/file-get-transcntl.c b/libdiskfs/file-get-transcntl.c
new file mode 100644
index 00000000..311d23ef
--- /dev/null
+++ b/libdiskfs/file-get-transcntl.c
@@ -0,0 +1,48 @@
+/* libkdiskfs implementation of fs.defs: file_get_translator_cntl
+ Copyright (C) 1992, 1993, 1994, 1995, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+/* Implement file_get_translator_cntl as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_file_get_translator_cntl (struct protid *cred,
+ mach_port_t *ctl,
+ mach_msg_type_name_t *ctltype)
+{
+ struct node *np;
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+
+ pthread_mutex_lock (&np->lock);
+
+ err = fshelp_isowner (&np->dn_stat, cred->user);
+ if (!err)
+ err = fshelp_fetch_control (&np->transbox, ctl);
+ if (!err && *ctl == MACH_PORT_NULL)
+ err = ENXIO;
+ if (!err)
+ *ctltype = MACH_MSG_TYPE_MOVE_SEND;
+
+ pthread_mutex_unlock (&np->lock);
+
+ return err;
+}
diff --git a/libdiskfs/file-getcontrol.c b/libdiskfs/file-getcontrol.c
new file mode 100644
index 00000000..fc6f777e
--- /dev/null
+++ b/libdiskfs/file-getcontrol.c
@@ -0,0 +1,51 @@
+/* libdiskfs implementation of fs.defs:file_getcontrol.c
+ Copyright (C) 1992,93,94,95,96,2001 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+#include <hurd/fshelp.h>
+
+/* Implement file_getcontrol as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_file_getcontrol (struct protid *cred,
+ mach_port_t *control,
+ mach_msg_type_name_t *controltype)
+{
+ int error;
+ struct port_info *newpi;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ error = fshelp_iscontroller (&diskfs_root_node->dn_stat, cred->user);
+ if (error)
+ return error;
+
+ error = ports_create_port (diskfs_control_class, diskfs_port_bucket,
+ sizeof (struct port_info), &newpi);
+ if (error)
+ return error;
+
+ pthread_spin_lock (&_diskfs_control_lock);
+ _diskfs_ncontrol_ports++;
+ pthread_spin_unlock (&_diskfs_control_lock);
+ *control = ports_get_right (newpi);
+ *controltype = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newpi);
+
+ return 0;
+}
diff --git a/libdiskfs/file-getfh.c b/libdiskfs/file-getfh.c
new file mode 100644
index 00000000..035705b5
--- /dev/null
+++ b/libdiskfs/file-getfh.c
@@ -0,0 +1,60 @@
+/* Return a file handle (for nfs server support)
+
+ Copyright (C) 1997,99,2002 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h>
+
+#include "priv.h"
+#include "fs_S.h"
+#include "fhandle.h"
+
+/* Return an NFS file handle for CRED in FH & FN_LEN. */
+error_t
+diskfs_S_file_getfh (struct protid *cred, char **fh, size_t *fh_len)
+{
+ struct node *node;
+ union diskfs_fhandle *f;
+
+ if (! cred)
+ return EOPNOTSUPP;
+
+ if (! idvec_contains (cred->user->uids, 0))
+ return EPERM;
+
+ assert (sizeof *f == sizeof f->bytes);
+
+ node = cred->po->np;
+
+ pthread_mutex_lock (&node->lock);
+
+ if (*fh_len < sizeof (union diskfs_fhandle))
+ *fh = mmap (0, sizeof (union diskfs_fhandle), PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ *fh_len = sizeof *f;
+
+ f = (union diskfs_fhandle *) *fh;
+
+ bzero (f, sizeof *f);
+ f->data.cache_id = node->cache_id;
+ f->data.gen = node->dn_stat.st_gen;
+
+ pthread_mutex_unlock (&node->lock);
+
+ return 0;
+}
diff --git a/libdiskfs/file-getlinknode.c b/libdiskfs/file-getlinknode.c
new file mode 100644
index 00000000..4b346c96
--- /dev/null
+++ b/libdiskfs/file-getlinknode.c
@@ -0,0 +1,39 @@
+/* libdiskfs implementation of fs.defs: file_getlinknode
+ Copyright (C) 1992, 1993, 1994 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+/* Implement file_getlinknode as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_file_getlinknode (struct protid *cred,
+ file_t *port,
+ mach_msg_type_name_t *portpoly)
+{
+ struct node *np;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+ if (np == diskfs_root_node)
+ return EBUSY;
+
+ *port = ports_get_right (cred);
+ *portpoly = MACH_MSG_TYPE_MAKE_SEND;
+ return 0;
+}
diff --git a/libdiskfs/file-lock-stat.c b/libdiskfs/file-lock-stat.c
new file mode 100644
index 00000000..4c371e29
--- /dev/null
+++ b/libdiskfs/file-lock-stat.c
@@ -0,0 +1,38 @@
+/*
+ Copyright (C) 1994, 1995 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+kern_return_t
+diskfs_S_file_lock_stat (struct protid *cred,
+ int *mystatus,
+ int *otherstatus)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&cred->po->np->lock);
+ *mystatus = cred->po->lock_status;
+ *otherstatus = cred->po->np->userlock.type;
+ pthread_mutex_unlock (&cred->po->np->lock);
+ return 0;
+}
diff --git a/libdiskfs/file-lock.c b/libdiskfs/file-lock.c
new file mode 100644
index 00000000..bff1df7b
--- /dev/null
+++ b/libdiskfs/file-lock.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 1993, 1994 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+kern_return_t
+diskfs_S_file_lock (struct protid *cred, int flags)
+{
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+ pthread_mutex_lock (&cred->po->np->lock);
+ err = fshelp_acquire_lock (&cred->po->np->userlock, &cred->po->lock_status,
+ &cred->po->np->lock, flags);
+ pthread_mutex_unlock (&cred->po->np->lock);
+ return err;
+}
diff --git a/libdiskfs/file-reparent.c b/libdiskfs/file-reparent.c
new file mode 100644
index 00000000..da6a51d2
--- /dev/null
+++ b/libdiskfs/file-reparent.c
@@ -0,0 +1,70 @@
+/* Reparent a file
+
+ Copyright (C) 1997,2002 Free Software Foundation
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+error_t
+diskfs_S_file_reparent (struct protid *cred, mach_port_t parent,
+ mach_port_t *new, mach_msg_type_name_t *new_type)
+{
+ error_t err;
+ struct node *node;
+ struct protid *new_cred;
+ struct peropen *new_po;
+
+ if (! cred)
+ return EOPNOTSUPP;
+
+ node = cred->po->np;
+
+ pthread_mutex_lock (&node->lock);
+ err = diskfs_make_peropen (node, cred->po->openstat, cred->po, &new_po);
+ if (! err)
+ {
+ err = diskfs_create_protid (new_po, cred->user, &new_cred);
+ if (err)
+ diskfs_release_peropen (new_po);
+ }
+ pthread_mutex_unlock (&node->lock);
+
+ if (! err)
+ {
+ /* Remove old shadow root state. */
+ if (new_cred->po->shadow_root && new_cred->po->shadow_root != node)
+ diskfs_nrele (new_cred->po->shadow_root);
+ if (new_cred->po->shadow_root_parent)
+ mach_port_deallocate (mach_task_self (),
+ new_cred->po->shadow_root_parent);
+
+ /* And install PARENT instead. */
+ new_cred->po->shadow_root = node;
+ new_cred->po->shadow_root_parent = parent;
+
+ *new = ports_get_right (new_cred);
+ *new_type = MACH_MSG_TYPE_MAKE_SEND;
+
+ ports_port_deref (new_cred);
+ }
+
+ return err;
+}
diff --git a/libdiskfs/file-set-size.c b/libdiskfs/file-set-size.c
new file mode 100644
index 00000000..fe2125ae
--- /dev/null
+++ b/libdiskfs/file-set-size.c
@@ -0,0 +1,55 @@
+/* libdiskfs implementation of fs.defs: file_set_size
+ Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+#include <fcntl.h>
+
+/* Implement file_set_size as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_file_set_size (struct protid *cred,
+ off_t size)
+{
+ CHANGE_NODE_FIELD (cred,
+ ({
+ if (!(cred->po->openstat & O_WRITE) || (size < 0))
+ err = EINVAL;
+ else if (size < np->dn_stat.st_size)
+ {
+ err = diskfs_truncate (np, size);
+ if (!err && np->filemod_reqs)
+ diskfs_notice_filechange (np,
+ FILE_CHANGED_TRUNCATE,
+ 0, size);
+ }
+ else if (size > np->dn_stat.st_size)
+ {
+ err = diskfs_grow (np, size, cred);
+ if (! err)
+ {
+ np->dn_stat.st_size = size;
+ np->dn_set_ctime = np->dn_set_mtime = 1;
+ if (np->filemod_reqs)
+ diskfs_notice_filechange (np,
+ FILE_CHANGED_EXTEND,
+ 0, size);
+ }
+ }
+ else
+ err = 0; /* Setting to same size. */
+ }));
+}
diff --git a/libdiskfs/file-set-trans.c b/libdiskfs/file-set-trans.c
new file mode 100644
index 00000000..6e1a61d2
--- /dev/null
+++ b/libdiskfs/file-set-trans.c
@@ -0,0 +1,215 @@
+/* libdiskfs implementation of fs.defs: file_set_translator
+ Copyright (C) 1992,93,94,95,96,99,2001,02,13,14
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+#include <hurd/paths.h>
+#include <hurd/fsys.h>
+
+/* Implement file_set_translator as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_file_set_translator (struct protid *cred,
+ int passive_flags,
+ int active_flags,
+ int killtrans_flags,
+ char *passive,
+ size_t passivelen,
+ fsys_t active)
+{
+ struct node *np;
+ error_t err;
+ mach_port_t control = MACH_PORT_NULL;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (!(passive_flags & FS_TRANS_SET) && !(active_flags & FS_TRANS_SET))
+ return 0;
+
+ if ((passive_flags & FS_TRANS_SET) && diskfs_check_readonly ())
+ return EROFS;
+
+ if (passivelen && passive[passivelen - 1])
+ return EINVAL;
+
+ np = cred->po->np;
+
+ pthread_mutex_lock (&np->lock);
+
+ err = fshelp_isowner (&np->dn_stat, cred->user);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return err;
+ }
+
+ if ((active_flags & FS_TRANS_SET)
+ && ! (active_flags & FS_TRANS_ORPHAN))
+ {
+ err = fshelp_fetch_control (&np->transbox, &control);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return err;
+ }
+
+ if ((control != MACH_PORT_NULL) && ((active_flags & FS_TRANS_EXCL) == 0))
+ {
+ pthread_mutex_unlock (&np->lock);
+ err = fsys_goaway (control, killtrans_flags);
+ mach_port_deallocate (mach_task_self (), control);
+ if (err && (err != MIG_SERVER_DIED)
+ && (err != MACH_SEND_INVALID_DEST))
+ return err;
+ err = 0;
+ pthread_mutex_lock (&np->lock);
+ }
+ else if (control != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), control);
+ }
+
+ /* Handle exclusive passive bit *first*. */
+ if ((passive_flags & FS_TRANS_SET)
+ && (passive_flags & FS_TRANS_EXCL)
+ && (np->dn_stat.st_mode & S_IPTRANS))
+ {
+ pthread_mutex_unlock (&np->lock);
+ return EBUSY;
+ }
+
+ if (active_flags & FS_TRANS_SET)
+ {
+ err = fshelp_set_active (&np->transbox, active,
+ active_flags & FS_TRANS_EXCL);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return err;
+ }
+ }
+
+ /* Set passive translator */
+ if (passive_flags & FS_TRANS_SET)
+ {
+ if (!(passive_flags & FS_TRANS_FORCE))
+ {
+ /* Handle the short-circuited translators */
+ mode_t newmode = 0;
+
+ if (diskfs_shortcut_symlink && !strcmp (passive, _HURD_SYMLINK))
+ newmode = S_IFLNK;
+ else if (diskfs_shortcut_chrdev && !(strcmp (passive, _HURD_CHRDEV)))
+ newmode = S_IFCHR;
+ else if (diskfs_shortcut_blkdev && !strcmp (passive, _HURD_BLKDEV))
+ newmode = S_IFBLK;
+ else if (diskfs_shortcut_fifo && !strcmp (passive, _HURD_FIFO))
+ newmode = S_IFIFO;
+ else if (diskfs_shortcut_ifsock && !strcmp (passive, _HURD_IFSOCK))
+ newmode = S_IFSOCK;
+
+ if (newmode)
+ {
+ if (S_ISDIR (np->dn_stat.st_mode))
+ {
+ /* We can't allow this, because if the mode of the directory
+ changes, the links will be lost. Perhaps it might be
+ allowed for empty directories, but that's too much of a
+ pain. */
+ pthread_mutex_unlock (&np->lock);
+ return EISDIR;
+ }
+ if (newmode == S_IFBLK || newmode == S_IFCHR)
+ {
+ /* Find the device number from the arguments
+ of the translator. */
+ int major, minor;
+ char *arg;
+
+ arg = passive + strlen (passive) + 1;
+ assert (arg <= passive + passivelen);
+ if (arg == passive + passivelen)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return EINVAL;
+ }
+ major = strtol (arg, 0, 0);
+
+ arg = arg + strlen (arg) + 1;
+ assert (arg < passive + passivelen);
+ if (arg == passive + passivelen)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return EINVAL;
+ }
+ minor = strtol (arg, 0, 0);
+
+ err = diskfs_validate_rdev_change (np,
+ makedev (major, minor));
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return err;
+ }
+ np->dn_stat.st_rdev = makedev (major, minor);
+ }
+
+ diskfs_truncate (np, 0);
+ if (newmode == S_IFLNK)
+ {
+ char *arg = passive + strlen (passive) + 1;
+ assert (arg <= passive + passivelen);
+ if (arg == passive + passivelen)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return EINVAL;
+ }
+
+ if (diskfs_create_symlink_hook)
+ err = (*diskfs_create_symlink_hook)(np, arg);
+ if (!diskfs_create_symlink_hook || err == EINVAL)
+ /* Store the argument in the file as the
+ target of the link */
+ err = diskfs_node_rdwr (np, arg, 0, strlen (arg),
+ 1, cred, 0);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return err;
+ }
+ }
+ newmode = (np->dn_stat.st_mode & ~S_IFMT) | newmode;
+ err = diskfs_validate_mode_change (np, newmode);
+ if (!err)
+ {
+ np->dn_stat.st_mode = newmode;
+ diskfs_node_update (np, diskfs_synchronous);
+ }
+ pthread_mutex_unlock (&np->lock);
+ return err;
+ }
+ }
+ err = diskfs_set_translator (np, passive, passivelen, cred);
+ }
+
+ pthread_mutex_unlock (&np->lock);
+
+ if (! err && cred->po->path && active_flags & FS_TRANS_SET)
+ err = fshelp_set_active_translator (&cred->pi, cred->po->path, active);
+
+ return err;
+}
diff --git a/libdiskfs/file-statfs.c b/libdiskfs/file-statfs.c
new file mode 100644
index 00000000..817b0115
--- /dev/null
+++ b/libdiskfs/file-statfs.c
@@ -0,0 +1,50 @@
+/* libdiskfs implementation of fs.defs: file_statfs
+ Copyright (C) 1992,93,94,98,2000 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h>
+#include <sys/statvfs.h>
+
+#include "priv.h"
+#include "fs_S.h"
+
+/* Implement file_getcontrol as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_file_statfs (struct protid *file,
+ fsys_statfsbuf_t *statbuf)
+{
+ if (!file)
+ return EOPNOTSUPP;
+
+ /* Start will all zeros, so the fs can skip fields for which
+ it has no information to contribute. */
+ bzero (statbuf, sizeof *statbuf);
+
+ if (diskfs_readonly)
+ statbuf->f_flag |= ST_RDONLY;
+ if (_diskfs_nosuid)
+ statbuf->f_flag |= ST_NOSUID;
+ if (_diskfs_noexec)
+ statbuf->f_flag |= ST_NOEXEC;
+ if (diskfs_synchronous)
+ statbuf->f_flag |= ST_SYNCHRONOUS;
+
+ diskfs_set_statfs (statbuf);
+
+ statbuf->f_namelen = diskfs_name_max;
+
+ return 0;
+}
diff --git a/libdiskfs/file-sync.c b/libdiskfs/file-sync.c
new file mode 100644
index 00000000..20fa005c
--- /dev/null
+++ b/libdiskfs/file-sync.c
@@ -0,0 +1,42 @@
+/* libdiskfs implementation of fs.defs: file_seek
+ Copyright (C) 1992, 1993, 1994, 1995, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+/* Implement file_sync as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_file_sync (struct protid *cred,
+ int wait,
+ int omitmetadata)
+{
+ struct node *np;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (diskfs_synchronous)
+ wait = 1;
+
+ np = cred->po->np;
+
+ pthread_mutex_lock (&np->lock);
+ iohelp_get_conch (&np->conch);
+ pthread_mutex_unlock (&np->lock);
+ diskfs_file_update (np, wait);
+ return 0;
+}
diff --git a/libdiskfs/file-syncfs.c b/libdiskfs/file-syncfs.c
new file mode 100644
index 00000000..b7d20a88
--- /dev/null
+++ b/libdiskfs/file-syncfs.c
@@ -0,0 +1,61 @@
+/* libdiskfs implementation of fs.defs: file_syncfs
+ Copyright (C) 1992, 1993, 1994, 1995, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+#include <hurd/fsys.h>
+
+/* Implement file_syncfs as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_file_syncfs (struct protid *cred,
+ int wait,
+ int dochildren)
+{
+ error_t
+ helper (struct node *np)
+ {
+ error_t error;
+ mach_port_t control;
+
+ error = fshelp_fetch_control (&np->transbox, &control);
+ pthread_mutex_unlock (&np->lock);
+ if (!error && (control != MACH_PORT_NULL))
+ {
+ fsys_syncfs (control, wait, 1);
+ mach_port_deallocate (mach_task_self (), control);
+ }
+ pthread_mutex_lock (&np->lock);
+ return 0;
+ }
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (dochildren)
+ diskfs_node_iterate (helper);
+
+ if (diskfs_synchronous)
+ wait = 1;
+
+ if (! diskfs_readonly)
+ {
+ diskfs_sync_everything (wait);
+ diskfs_set_hypermetadata (wait, 0);
+ }
+
+ return 0;
+}
diff --git a/libdiskfs/file-utimes.c b/libdiskfs/file-utimes.c
new file mode 100644
index 00000000..39fac504
--- /dev/null
+++ b/libdiskfs/file-utimes.c
@@ -0,0 +1,57 @@
+/* libdiskfs implementation of fs.defs: file_utimes
+ Copyright (C) 1992, 1993, 1994, 1998, 1999 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+/* Implement file_utimes as described in <hurd/fs.defs>. */
+kern_return_t
+diskfs_S_file_utimes (struct protid *cred,
+ time_value_t atime,
+ time_value_t mtime)
+{
+ CHANGE_NODE_FIELD (cred,
+ ({
+ if (!(err = fshelp_isowner (&np->dn_stat, cred->user)))
+ {
+ if (atime.microseconds == -1)
+ np->dn_set_atime = 1;
+ else
+ {
+ np->dn_stat.st_atim.tv_sec = atime.seconds;
+ np->dn_stat.st_atim.tv_nsec = atime.microseconds * 1000;
+ np->dn_set_atime = 0;
+ }
+
+ if (mtime.microseconds == -1)
+ np->dn_set_mtime = 1;
+ else
+ {
+ np->dn_stat.st_mtim.tv_sec = mtime.seconds;
+ np->dn_stat.st_mtim.tv_nsec = mtime.microseconds * 1000;
+ np->dn_set_mtime = 0;
+ }
+
+ np->dn_set_ctime = 1;
+
+ if (np->filemod_reqs)
+ diskfs_notice_filechange (np,
+ FILE_CHANGED_META,
+ 0, 0);
+ }
+ }));
+}
diff --git a/libdiskfs/fsmutations.h b/libdiskfs/fsmutations.h
new file mode 100644
index 00000000..3f9362b9
--- /dev/null
+++ b/libdiskfs/fsmutations.h
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 1994,2001 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Only CPP macro definitions should go in this file. */
+
+#define FILE_INTRAN protid_t diskfs_begin_using_protid_port (file_t)
+#define FILE_DESTRUCTOR diskfs_end_using_protid_port (protid_t)
+
+#define IO_INTRAN protid_t diskfs_begin_using_protid_port (io_t)
+#define IO_DESTRUCTOR diskfs_end_using_protid_port (protid_t)
+
+#define FSYS_INTRAN control_t diskfs_begin_using_control_port (fsys_t)
+#define FSYS_DESTRUCTOR diskfs_end_using_control_port (control_t)
+
+#define FILE_IMPORTS import "libdiskfs/priv.h";
+#define IO_IMPORTS import "libdiskfs/priv.h";
+#define FSYS_IMPORTS import "libdiskfs/priv.h";
+#define IFSOCK_IMPORTS import "libdiskfs/priv.h";
+
+#define EXEC_STARTUP_INTRAN \
+ bootinfo_t diskfs_begin_using_bootinfo_port (exec_startup_t)
+#define EXEC_STARTUP_DESTRUCTOR \
+ diskfs_end_using_bootinfo (bootinfo_t)
+#define EXEC_STARTUP_IMPORTS \
+ import "libdiskfs/priv.h";
diff --git a/libdiskfs/fsys-forward.c b/libdiskfs/fsys-forward.c
new file mode 100644
index 00000000..87441573
--- /dev/null
+++ b/libdiskfs/fsys-forward.c
@@ -0,0 +1,37 @@
+/* fsys_forward
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fsys_S.h"
+
+/* Ask SERVER to provide fsys translation service for us. REQUESTOR is
+ the bootstrap port supplied to the original translator, and ARGV are
+ the command line arguments. If the recipient accepts the request, he
+ (or some delegate) should send fsys_startup to REQUESTOR to start the
+ filesystem up. */
+error_t
+diskfs_S_fsys_forward (mach_port_t server,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mach_port_t requestor, char *argz, size_t argz_len)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libdiskfs/fsys-getfile.c b/libdiskfs/fsys-getfile.c
new file mode 100644
index 00000000..9dd5d732
--- /dev/null
+++ b/libdiskfs/fsys-getfile.c
@@ -0,0 +1,104 @@
+/* Return the file for a given handle (for nfs server support)
+
+ Copyright (C) 1997,99,2001,02 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <fcntl.h>
+
+#include "priv.h"
+#include "fsys_S.h"
+#include "fhandle.h"
+
+/* Return in FILE & FILE_TYPE the file in FSYS corresponding to the NFS file
+ handle HANDLE & HANDLE_LEN. */
+error_t
+diskfs_S_fsys_getfile (struct diskfs_control *pt,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ uid_t *uids, mach_msg_type_number_t nuids,
+ gid_t *gids, mach_msg_type_number_t ngids,
+ char *handle, mach_msg_type_number_t handle_len,
+ mach_port_t *file, mach_msg_type_name_t *file_type)
+{
+ int flags;
+ error_t err;
+ struct node *node;
+ const union diskfs_fhandle *f;
+ struct protid *new_cred;
+ struct peropen *new_po;
+ struct iouser *user;
+
+ if (!pt
+ || pt->pi.class != diskfs_control_class)
+ return EOPNOTSUPP;
+
+ if (handle_len != sizeof *f)
+ {
+ return EINVAL;
+ }
+
+ f = (const union diskfs_fhandle *) handle;
+
+ err = diskfs_cached_lookup (f->data.cache_id, &node);
+ if (err)
+ {
+ return err;
+ }
+
+ if (node->dn_stat.st_gen != f->data.gen)
+ {
+ diskfs_nput (node);
+ return ESTALE;
+ }
+
+ err = iohelp_create_complex_iouser (&user, uids, nuids, gids, ngids);
+ if (err)
+ {
+ diskfs_nput (node);
+ return err;
+ }
+
+ flags = 0;
+ if (! fshelp_access (&node->dn_stat, S_IREAD, user))
+ flags |= O_READ;
+ if (! fshelp_access (&node->dn_stat, S_IEXEC, user))
+ flags |= O_EXEC;
+ if (! fshelp_access (&node->dn_stat, S_IWRITE, user)
+ && ! S_ISDIR (node->dn_stat.st_mode)
+ && ! diskfs_check_readonly ())
+ flags |= O_WRITE;
+
+ err = diskfs_make_peropen (node, flags, 0, &new_po);
+ if (! err)
+ {
+ err = diskfs_create_protid (new_po, user, &new_cred);
+ if (err)
+ diskfs_release_peropen (new_po);
+ }
+
+ iohelp_free_iouser (user);
+
+ diskfs_nput (node);
+
+ if (! err)
+ {
+ *file = ports_get_right (new_cred);
+ *file_type = MACH_MSG_TYPE_MAKE_SEND;
+ }
+
+ return err;
+}
diff --git a/libdiskfs/fsys-getroot.c b/libdiskfs/fsys-getroot.c
new file mode 100644
index 00000000..10793c87
--- /dev/null
+++ b/libdiskfs/fsys-getroot.c
@@ -0,0 +1,207 @@
+/*
+ Copyright (C) 1993,94,95,96,97,98,2002 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "fsys_S.h"
+#include <hurd/fsys.h>
+#include <fcntl.h>
+
+/* Implement fsys_getroot as described in <hurd/fsys.defs>. */
+kern_return_t
+diskfs_S_fsys_getroot (struct diskfs_control *pt,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t dotdot,
+ uid_t *uids,
+ size_t nuids,
+ uid_t *gids,
+ size_t ngids,
+ int flags,
+ retry_type *retry,
+ char *retryname,
+ file_t *returned_port,
+ mach_msg_type_name_t *returned_port_poly)
+{
+ error_t err = 0;
+ mode_t type;
+ struct protid *newpi;
+ struct peropen *newpo;
+ struct iouser user;
+ struct peropen peropen_context =
+ {
+ root_parent: dotdot,
+ shadow_root_parent: MACH_PORT_NULL,
+ shadow_root: _diskfs_chroot_directory ? diskfs_root_node : NULL, /* XXX */
+ path: NULL,
+ };
+
+ if (!pt
+ || pt->pi.class != diskfs_control_class)
+ return EOPNOTSUPP;
+
+ flags &= O_HURD;
+
+ user.uids = make_idvec ();
+ user.gids = make_idvec ();
+ idvec_set_ids (user.uids, uids, nuids);
+ idvec_set_ids (user.gids, gids, ngids);
+#define drop_idvec() idvec_free (user.gids); idvec_free (user.uids)
+
+ pthread_rwlock_rdlock (&diskfs_fsys_lock);
+ pthread_mutex_lock (&diskfs_root_node->lock);
+
+ /* This code is similar (but not the same as) the code in
+ dir-lookup.c that does the same thing. Perhaps a way should
+ be found to share the logic. */
+
+ type = diskfs_root_node->dn_stat.st_mode & S_IFMT;
+
+ if (((diskfs_root_node->dn_stat.st_mode & S_IPTRANS)
+ || fshelp_translated (&diskfs_root_node->transbox))
+ && !(flags & O_NOTRANS))
+ {
+ err = fshelp_fetch_root (&diskfs_root_node->transbox,
+ &peropen_context, dotdot, &user, flags,
+ _diskfs_translator_callback1,
+ _diskfs_translator_callback2,
+ retry, retryname, returned_port);
+ if (err != ENOENT)
+ {
+ pthread_mutex_unlock (&diskfs_root_node->lock);
+ pthread_rwlock_unlock (&diskfs_fsys_lock);
+ drop_idvec ();
+ if (!err)
+ *returned_port_poly = MACH_MSG_TYPE_MOVE_SEND;
+ return err;
+ }
+
+ /* ENOENT means the translator was removed in the interim. */
+ err = 0;
+ }
+
+ if (type == S_IFLNK && !(flags & (O_NOLINK | O_NOTRANS)))
+ {
+ /* Handle symlink interpretation */
+ char pathbuf[diskfs_root_node->dn_stat.st_size + 1];
+
+ if (diskfs_read_symlink_hook)
+ err = (*diskfs_read_symlink_hook) (diskfs_root_node, pathbuf);
+ if (!diskfs_read_symlink_hook || err == EINVAL)
+ {
+ size_t amt = 0;
+ err = diskfs_node_rdwr (diskfs_root_node, pathbuf, 0,
+ diskfs_root_node->dn_stat.st_size, 0,
+ 0, &amt);
+ pathbuf[amt] = '\0';
+ }
+
+ pthread_mutex_unlock (&diskfs_root_node->lock);
+ pthread_rwlock_unlock (&diskfs_fsys_lock);
+ if (err)
+ {
+ drop_idvec ();
+ return err;
+ }
+
+ if (pathbuf[0] == '/')
+ {
+ *retry = FS_RETRY_MAGICAL;
+ *returned_port = MACH_PORT_NULL;
+ *returned_port_poly = MACH_MSG_TYPE_COPY_SEND;
+ strcpy (retryname, pathbuf);
+ mach_port_deallocate (mach_task_self (), dotdot);
+ drop_idvec ();
+ return 0;
+ }
+ else
+ {
+ *retry = FS_RETRY_REAUTH;
+ *returned_port = dotdot;
+ *returned_port_poly = MACH_MSG_TYPE_MOVE_SEND;
+ strcpy (retryname, pathbuf);
+ drop_idvec ();
+ return 0;
+ }
+ }
+
+ if ((type == S_IFSOCK || type == S_IFBLK
+ || type == S_IFCHR || type == S_IFIFO)
+ && (flags & (O_READ|O_WRITE|O_EXEC)))
+ err = EOPNOTSUPP;
+
+ if (!err && (flags & O_READ))
+ err = fshelp_access (&diskfs_root_node->dn_stat, S_IREAD, &user);
+
+ if (!err && (flags & O_EXEC))
+ err = fshelp_access (&diskfs_root_node->dn_stat, S_IEXEC, &user);
+
+ if (!err && (flags & (O_WRITE)))
+ {
+ if (type == S_IFDIR)
+ err = EISDIR;
+ else if (diskfs_check_readonly ())
+ err = EROFS;
+ else
+ err = fshelp_access (&diskfs_root_node->dn_stat,
+ S_IWRITE, &user);
+ }
+
+ if (err)
+ {
+ pthread_mutex_unlock (&diskfs_root_node->lock);
+ pthread_rwlock_unlock (&diskfs_fsys_lock);
+ drop_idvec ();
+ return err;
+ }
+
+ if ((flags & O_NOATIME)
+ && (fshelp_isowner (&diskfs_root_node->dn_stat, &user)
+ == EPERM))
+ flags &= ~O_NOATIME;
+
+ flags &= ~OPENONLY_STATE_MODES;
+
+ err = diskfs_make_peropen (diskfs_root_node, flags,
+ &peropen_context, &newpo);
+ if (! err)
+ {
+ err = diskfs_create_protid (newpo, &user, &newpi);
+ if (err)
+ diskfs_release_peropen (newpo);
+ }
+
+ if (! err)
+ {
+ mach_port_deallocate (mach_task_self (), dotdot);
+ *retry = FS_RETRY_NORMAL;
+ *retryname = '\0';
+ *returned_port = ports_get_right (newpi);
+ *returned_port_poly = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newpi);
+ }
+
+ pthread_mutex_unlock (&diskfs_root_node->lock);
+ pthread_rwlock_unlock (&diskfs_fsys_lock);
+
+ drop_idvec ();
+
+ return err;
+}
diff --git a/libdiskfs/fsys-goaway.c b/libdiskfs/fsys-goaway.c
new file mode 100644
index 00000000..b9103873
--- /dev/null
+++ b/libdiskfs/fsys-goaway.c
@@ -0,0 +1,51 @@
+/*
+ Copyright (C) 1993, 1994, 1995 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "fsys_S.h"
+#include "fsys_reply_U.h"
+
+/* Implement fsys_goaway as described in <hurd/fsys.defs>. */
+error_t
+diskfs_S_fsys_goaway (struct diskfs_control *pt,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int flags)
+{
+ error_t ret;
+
+ if (!pt
+ || pt->pi.class != diskfs_control_class)
+ return EOPNOTSUPP;
+
+ /* XXX FSYS_GOAWAY_NOWAIT not implemented. */
+
+ ret = diskfs_shutdown (flags);
+
+ if (ret == 0)
+ {
+ /* We are supposed to exit, but first notify the caller. */
+ fsys_goaway_reply (reply, reply_type, 0);
+ exit (0);
+ }
+
+ return ret;
+}
diff --git a/libdiskfs/fsys-options.c b/libdiskfs/fsys-options.c
new file mode 100644
index 00000000..b366d143
--- /dev/null
+++ b/libdiskfs/fsys-options.c
@@ -0,0 +1,112 @@
+/* Parse run-time options
+
+ Copyright (C) 1995, 1996, 1998 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <argz.h>
+#include <hurd/fsys.h>
+#include <string.h>
+
+#include "priv.h"
+#include "fsys_S.h"
+
+/* Implement fsys_set_options as described in <hurd/fsys.defs>. */
+kern_return_t
+diskfs_S_fsys_set_options (struct diskfs_control *pt,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ char *data, mach_msg_type_number_t len,
+ int do_children)
+{
+ error_t err = 0;
+
+ error_t
+ helper (struct node *np)
+ {
+ error_t error;
+ mach_port_t control;
+
+ error = fshelp_fetch_control (&np->transbox, &control);
+ pthread_mutex_unlock (&np->lock);
+ if (!error && (control != MACH_PORT_NULL))
+ {
+ error = fsys_set_options (control, data, len, do_children);
+ mach_port_deallocate (mach_task_self (), control);
+ }
+ else
+ error = 0;
+ pthread_mutex_lock (&np->lock);
+
+ if ((error == MIG_SERVER_DIED) || (error == MACH_SEND_INVALID_DEST))
+ error = 0;
+ return error;
+ }
+
+ if (!pt
+ || pt->pi.class != diskfs_control_class)
+ return EOPNOTSUPP;
+
+ if (do_children)
+ {
+ pthread_rwlock_wrlock (&diskfs_fsys_lock);
+ err = diskfs_node_iterate (helper);
+ pthread_rwlock_unlock (&diskfs_fsys_lock);
+ }
+
+ if (!err)
+ {
+ pthread_rwlock_wrlock (&diskfs_fsys_lock);
+ err = diskfs_set_options (data, len);
+ pthread_rwlock_unlock (&diskfs_fsys_lock);
+ }
+
+ return err;
+}
+
+/* Implement fsys_get_options as described in <hurd/fsys.defs>. */
+error_t
+diskfs_S_fsys_get_options (struct diskfs_control *port,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ char **data, mach_msg_type_number_t *data_len)
+{
+ char *argz = 0;
+ size_t argz_len = 0;
+ error_t err;
+
+ if (!port
+ || port->pi.class != diskfs_control_class)
+ return EOPNOTSUPP;
+
+ err = argz_add (&argz, &argz_len, program_invocation_name);
+ if (err)
+ return err;
+
+ pthread_rwlock_rdlock (&diskfs_fsys_lock);
+ err = diskfs_append_args (&argz, &argz_len);
+ pthread_rwlock_unlock (&diskfs_fsys_lock);
+
+ if (! err)
+ /* Move ARGZ from a malloced buffer into a vm_alloced one. */
+ err = iohelp_return_malloced_buffer (argz, argz_len, data, data_len);
+ else
+ free (argz);
+
+ return err;
+}
diff --git a/libdiskfs/fsys-startup.c b/libdiskfs/fsys-startup.c
new file mode 100644
index 00000000..f68438e2
--- /dev/null
+++ b/libdiskfs/fsys-startup.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 1993, 1994, 1995 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "fsys_S.h"
+
+/* Implement fsys_startup as described in <hurd/fsys.defs>. */
+kern_return_t
+diskfs_S_fsys_startup (mach_port_t port,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int flags,
+ mach_port_t ctl,
+ mach_port_t *real,
+ mach_msg_type_name_t *realpoly)
+{
+ return diskfs_execboot_fsys_startup (port, flags, ctl, real, realpoly);
+}
diff --git a/libdiskfs/fsys-syncfs.c b/libdiskfs/fsys-syncfs.c
new file mode 100644
index 00000000..4dceed7f
--- /dev/null
+++ b/libdiskfs/fsys-syncfs.c
@@ -0,0 +1,70 @@
+/*
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fsys_S.h"
+#include <hurd/fsys.h>
+
+/* Implement fsys_syncfs as described in <hurd/fsys.defs>. */
+kern_return_t
+diskfs_S_fsys_syncfs (struct diskfs_control *pi,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int wait,
+ int children)
+{
+ error_t
+ helper (struct node *np)
+ {
+ error_t error;
+ mach_port_t control;
+
+ error = fshelp_fetch_control (&np->transbox, &control);
+ pthread_mutex_unlock (&np->lock);
+ if (!error && (control != MACH_PORT_NULL))
+ {
+ fsys_syncfs (control, wait, 1);
+ mach_port_deallocate (mach_task_self (), control);
+ }
+ pthread_mutex_lock (&np->lock);
+ return 0;
+ }
+
+ if (!pi
+ || pi->pi.class != diskfs_control_class)
+ return EOPNOTSUPP;
+
+ pthread_rwlock_rdlock (&diskfs_fsys_lock);
+
+ if (children)
+ diskfs_node_iterate (helper);
+
+ if (diskfs_synchronous)
+ wait = 1;
+
+ if (! diskfs_readonly)
+ {
+ diskfs_sync_everything (wait);
+ diskfs_set_hypermetadata (wait, 0);
+ }
+
+ pthread_rwlock_unlock (&diskfs_fsys_lock);
+ return 0;
+}
diff --git a/libdiskfs/get-source.c b/libdiskfs/get-source.c
new file mode 100644
index 00000000..43994464
--- /dev/null
+++ b/libdiskfs/get-source.c
@@ -0,0 +1,33 @@
+/* Default version of diskfs_get_source
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "priv.h"
+
+error_t
+diskfs_get_source (struct protid *cred, char *source, size_t source_len)
+{
+ if (diskfs_disk_name == NULL)
+ return EOPNOTSUPP;
+
+ strncpy (source, diskfs_disk_name, source_len - 1);
+ source[source_len - 1] = '\0';
+ return 0;
+}
diff --git a/libdiskfs/ifsock.c b/libdiskfs/ifsock.c
new file mode 100644
index 00000000..caf66885
--- /dev/null
+++ b/libdiskfs/ifsock.c
@@ -0,0 +1,131 @@
+/* Implement ifsock inteface
+ Copyright (C) 1994, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "ifsock_S.h"
+#include <hurd/paths.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <hurd/socket.h>
+
+static pthread_spinlock_t pflocalserverlock = PTHREAD_SPINLOCK_INITIALIZER;
+static mach_port_t pflocalserver = MACH_PORT_NULL;
+
+kern_return_t
+diskfs_S_ifsock_getsockaddr (struct protid *cred,
+ mach_port_t *address)
+{
+ error_t err;
+ struct node *np;
+ unsigned restart_tries = 0;
+
+ /* Make sure this is a socket */
+ if (!cred)
+ return EOPNOTSUPP;
+ np = cred->po->np;
+
+ retry:
+ pthread_mutex_lock (&np->lock);
+ if ((np->dn_stat.st_mode & S_IFMT) != S_IFSOCK)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return EOPNOTSUPP;
+ }
+ err = fshelp_access (&np->dn_stat, S_IWRITE, cred->user);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return err;
+ }
+
+ if (np->sockaddr == MACH_PORT_NULL)
+ {
+ mach_port_t server;
+ mach_port_t sockaddr;
+ mach_port_t old;
+
+ pthread_mutex_unlock (&np->lock);
+
+ /* Fetch a port to the PF_LOCAL server, caching it. */
+
+ pthread_spin_lock (&pflocalserverlock);
+ if (pflocalserver == MACH_PORT_NULL)
+ {
+ /* Find out who the PF_LOCAL server is. */
+ char buf[100];
+
+ pthread_spin_unlock (&pflocalserverlock);
+
+ /* Look it up */
+ sprintf (buf, "%s/%d", _SERVERS_SOCKET, PF_LOCAL);
+ server = file_name_lookup (buf, 0, 0);
+ if (server == MACH_PORT_NULL)
+ return EIEIO;
+
+ /* Set it unless someone is already here */
+ pthread_spin_lock (&pflocalserverlock);
+ if (pflocalserver != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), server);
+ else
+ pflocalserver = server;
+ pthread_spin_unlock (&pflocalserverlock);
+
+ goto retry;
+ }
+ server = pflocalserver;
+ pthread_spin_unlock (&pflocalserverlock);
+
+ /* Create an address for the node */
+ err = socket_fabricate_address (server, AF_LOCAL, &sockaddr);
+ if ((err == MACH_SEND_INVALID_DEST || err == MIG_SERVER_DIED)
+ && restart_tries++ == 0)
+ /* The PF_LOCAL server died; try to restart it. */
+ {
+ pthread_spin_lock (&pflocalserverlock);
+ if (pflocalserver == server)
+ pflocalserver = MACH_PORT_NULL;
+ pthread_spin_unlock (&pflocalserverlock);
+ goto retry;
+ }
+ if (err)
+ return EIEIO;
+
+ pthread_mutex_lock (&np->lock);
+ if (np->sockaddr != MACH_PORT_NULL)
+ /* Someone beat us */
+ mach_port_deallocate (mach_task_self (), sockaddr);
+ else
+ {
+ /* The receive right of the sockaddr holds a reference;
+ when we get a dead name on that right we drop our
+ reference. */
+ mach_port_request_notification (mach_task_self (), sockaddr,
+ MACH_NOTIFY_DEAD_NAME, 1,
+ cred->pi.port_right,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &old);
+ if (old != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), old);
+ np->sockaddr = sockaddr;
+ diskfs_nref_light (np);
+ }
+ }
+
+ *address = np->sockaddr;
+ pthread_mutex_unlock (&np->lock);
+ return 0;
+}
diff --git a/libdiskfs/init-first.c b/libdiskfs/init-first.c
new file mode 100644
index 00000000..6dd5fc4f
--- /dev/null
+++ b/libdiskfs/init-first.c
@@ -0,0 +1,65 @@
+/*
+ Copyright (C) 1994,95,97,2001 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include <stdlib.h>
+#include <hurd/ports.h>
+
+static int thread_timeout = 1000 * 60 * 2; /* two minutes */
+static int server_timeout = 1000 * 60 * 10; /* ten minutes */
+
+
+static void *
+master_thread_function (void *demuxer)
+{
+ error_t err;
+
+ do
+ {
+ ports_manage_port_operations_multithread (diskfs_port_bucket,
+ (ports_demuxer_type) demuxer,
+ thread_timeout,
+ server_timeout,
+ 0);
+ err = diskfs_shutdown (0);
+ }
+ while (err);
+
+ exit (0);
+ /* NOTREACHED */
+ return NULL;
+}
+
+void
+diskfs_spawn_first_thread (ports_demuxer_type demuxer)
+{
+ pthread_t thread;
+ error_t err;
+
+ err = pthread_create (&thread, NULL, master_thread_function, demuxer);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+}
diff --git a/libdiskfs/init-init.c b/libdiskfs/init-init.c
new file mode 100644
index 00000000..7a7f2485
--- /dev/null
+++ b/libdiskfs/init-init.c
@@ -0,0 +1,111 @@
+/*
+ Copyright (C) 1994, 95, 96, 97, 98, 99, 2001 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include <device/device.h>
+#include <hurd/fsys.h>
+#include <stdio.h>
+#include <maptime.h>
+
+/* For safe inlining of diskfs_node_disknode and
+ diskfs_disknode_node. */
+size_t const _diskfs_sizeof_struct_node = sizeof (struct node);
+
+mach_port_t diskfs_default_pager;
+mach_port_t diskfs_auth_server_port;
+volatile struct mapped_time_value *diskfs_mtime;
+
+pthread_rwlock_t diskfs_fsys_lock = PTHREAD_RWLOCK_INITIALIZER;
+mach_port_t diskfs_fsys_identity;
+
+int _diskfs_nosuid, _diskfs_noexec;
+int _diskfs_noatime;
+
+struct hurd_port _diskfs_exec_portcell;
+
+pthread_spinlock_t diskfs_node_refcnt_lock = PTHREAD_SPINLOCK_INITIALIZER;
+
+pthread_spinlock_t _diskfs_control_lock = PTHREAD_SPINLOCK_INITIALIZER;
+int _diskfs_ncontrol_ports;
+
+struct port_class *diskfs_protid_class;
+struct port_class *diskfs_control_class;
+struct port_class *diskfs_initboot_class;
+struct port_class *diskfs_execboot_class;
+struct port_class *diskfs_shutdown_notification_class;
+
+struct port_bucket *diskfs_port_bucket;
+
+/* Call this after arguments have been parsed to initialize the
+ library. */
+error_t
+diskfs_init_diskfs (void)
+{
+ error_t err;
+
+ if (diskfs_boot_filesystem ())
+ /* This is a boot filesystem, we have to do some things specially. */
+ {
+ mach_port_t host;
+ err = get_privileged_ports (&host, 0);
+ if (! err)
+ {
+ diskfs_default_pager = MACH_PORT_NULL;
+ err = vm_set_default_memory_manager (host, &diskfs_default_pager);
+ mach_port_deallocate (mach_task_self (), host);
+
+ if (!err)
+ err = maptime_map (1, 0, &diskfs_mtime);
+ }
+ }
+ else
+ err = maptime_map (0, 0, &diskfs_mtime);
+
+ if (err)
+ return err;
+
+ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &diskfs_fsys_identity);
+ if (err)
+ return err;
+
+ diskfs_auth_server_port = getauth ();
+
+ diskfs_protid_class = ports_create_class (diskfs_protid_rele, 0);
+ diskfs_control_class = ports_create_class (_diskfs_control_clean, 0);
+ diskfs_initboot_class = ports_create_class (0, 0);
+ diskfs_execboot_class = ports_create_class (0, 0);
+ diskfs_shutdown_notification_class = ports_create_class (0, 0);
+
+ diskfs_port_bucket = ports_create_bucket ();
+
+ _hurd_port_init (&_diskfs_exec_portcell, MACH_PORT_NULL);
+
+ return 0;
+}
+
+void
+_diskfs_control_clean (void *arg __attribute__ ((unused)))
+{
+ pthread_spin_lock (&_diskfs_control_lock);
+ _diskfs_ncontrol_ports--;
+ pthread_spin_unlock (&_diskfs_control_lock);
+}
diff --git a/libdiskfs/init-main.c b/libdiskfs/init-main.c
new file mode 100644
index 00000000..16fdafa4
--- /dev/null
+++ b/libdiskfs/init-main.c
@@ -0,0 +1,78 @@
+/* diskfs_init_main -- initialize diskfs world, parse arguments, and open store
+ Copyright (C) 1999, 2001 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "diskfs.h"
+#include <argp.h>
+#include <assert.h>
+#include <error.h>
+#include <hurd/store.h>
+
+struct store *
+diskfs_init_main (struct argp *startup_argp,
+ int argc, char **argv,
+ struct store_parsed **store_parsed,
+ mach_port_t *bootstrap)
+{
+ error_t err;
+ struct store_argp_params store_params = { 0 };
+ struct store *store;
+
+ /* We must use ARGP_IN_ORDER for the parsing of --boot-command to work. */
+ err = argp_parse (startup_argp ?: &diskfs_store_startup_argp,
+ argc, argv, ARGP_IN_ORDER, NULL,
+ &store_params);
+ assert_perror (err);
+ *store_parsed = store_params.result;
+
+ err = store_parsed_name (*store_parsed, &diskfs_disk_name);
+ if (err)
+ error (2, err, "store_parsed_name");
+
+ /* This must come after the args have been parsed, as this is where the
+ host priv ports are set for booting. */
+ diskfs_console_stdio ();
+
+ if (diskfs_boot_filesystem ())
+ /* We are the bootstrap filesystem. */
+ *bootstrap = MACH_PORT_NULL;
+ else
+ {
+ task_get_bootstrap_port (mach_task_self (), bootstrap);
+ if (*bootstrap == MACH_PORT_NULL)
+ error (2, 0, "Must be started as a translator");
+ }
+
+ /* Initialize the diskfs library. Must come before any other diskfs call. */
+ err = diskfs_init_diskfs ();
+ if (err)
+ error (4, err, "init");
+
+ err = store_parsed_open (*store_parsed, diskfs_readonly ? STORE_READONLY : 0,
+ &store);
+ if (err)
+ error (3, err, "%s", diskfs_disk_name);
+
+ if (store->flags & STORE_HARD_READONLY)
+ diskfs_readonly = diskfs_hard_readonly = 1;
+
+ /* Start the first request thread, to handle RPCs and page requests. */
+ diskfs_spawn_first_thread (diskfs_demuxer);
+
+ return store;
+}
diff --git a/libdiskfs/init-startup.c b/libdiskfs/init-startup.c
new file mode 100644
index 00000000..d10c9641
--- /dev/null
+++ b/libdiskfs/init-startup.c
@@ -0,0 +1,224 @@
+/* diskfs_startup_diskfs -- advertise our fsys control port to our parent FS.
+ Copyright (C) 1994,95,96,98,99,2000,02 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Roland McGrath. */
+
+#include "priv.h"
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <error.h>
+#include <hurd/fsys.h>
+#include <hurd/startup.h>
+#include <pids.h>
+
+#include "startup_S.h"
+
+char *_diskfs_chroot_directory;
+
+mach_port_t
+diskfs_startup_diskfs (mach_port_t bootstrap, int flags)
+{
+ error_t err;
+ mach_port_t realnode, right;
+ struct port_info *newpi;
+
+ if (_diskfs_chroot_directory != NULL)
+ {
+ /* The boot options requested we change to a subdirectory
+ and treat that as the root of the filesystem. */
+ struct node *np, *old;
+ struct protid *rootpi;
+ struct peropen *rootpo;
+
+ /* Skip leading slashes. */
+ while (*_diskfs_chroot_directory == '/')
+ ++_diskfs_chroot_directory;
+
+ pthread_mutex_lock (&diskfs_root_node->lock);
+
+ /* Create a protid we can use in diskfs_lookup. */
+ err = diskfs_make_peropen (diskfs_root_node, O_READ|O_EXEC,
+ 0, &rootpo);
+ assert_perror (err);
+ err = diskfs_create_protid (rootpo, 0, &rootpi);
+ assert_perror (err);
+
+ /* Look up the directory name. */
+ err = diskfs_lookup (diskfs_root_node, _diskfs_chroot_directory,
+ LOOKUP, &np, NULL, rootpi);
+ pthread_mutex_unlock (&diskfs_root_node->lock);
+ ports_port_deref (rootpi);
+
+ if (err == EAGAIN)
+ error (1, 0, "`--virtual-root=%s' specifies the real root directory",
+ _diskfs_chroot_directory);
+ else if (err)
+ error (1, err, "`%s' not found", _diskfs_chroot_directory);
+
+ if (!S_ISDIR (np->dn_stat.st_mode))
+ {
+ pthread_mutex_unlock (&np->lock);
+ error (1, ENOTDIR, "%s", _diskfs_chroot_directory);
+ }
+
+ /* Install this node as the new root, forgetting about the real root
+ node. The last essential piece that makes the virtual root work
+ is in fsys-getroot.c, which sets the first peropen's shadow_root
+ if _diskfs_chroot_directory is non-null. */
+ old = diskfs_root_node;
+ diskfs_root_node = np;
+ pthread_mutex_unlock (&np->lock);
+ diskfs_nput (old);
+ }
+
+ if (bootstrap != MACH_PORT_NULL)
+ {
+ err = ports_create_port (diskfs_control_class, diskfs_port_bucket,
+ sizeof (struct port_info), &newpi);
+ if (! err)
+ {
+ right = ports_get_send_right (newpi);
+ err = fsys_startup (bootstrap, flags, right,
+ MACH_MSG_TYPE_COPY_SEND, &realnode);
+ mach_port_deallocate (mach_task_self (), right);
+ ports_port_deref (newpi);
+ }
+ if (err)
+ error (1, err, "Translator startup failure: fsys_startup");
+
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ _diskfs_ncontrol_ports++;
+
+ _diskfs_init_completed ();
+ }
+ else
+ {
+ realnode = MACH_PORT_NULL;
+
+ /* We are the bootstrap filesystem; do special boot-time setup. */
+ diskfs_start_bootstrap ();
+ }
+
+ if (diskfs_default_sync_interval)
+ /* Start 'em sync'n */
+ diskfs_set_sync_interval (diskfs_default_sync_interval);
+
+ return realnode;
+}
+
+error_t
+diskfs_S_startup_dosync (mach_port_t handle)
+{
+ error_t err = 0;
+ struct port_info *pi
+ = ports_lookup_port (diskfs_port_bucket, handle,
+ diskfs_shutdown_notification_class);
+
+ if (!pi)
+ return EOPNOTSUPP;
+
+ if (! diskfs_readonly)
+ {
+ /* First start a sync so that if something goes wrong
+ we at least get this much done. */
+ diskfs_sync_everything (0);
+ diskfs_set_hypermetadata (0, 0);
+
+ pthread_rwlock_wrlock (&diskfs_fsys_lock);
+
+ /* Permit all the current RPC's to finish, and then suspend new ones */
+ err = ports_inhibit_class_rpcs (diskfs_protid_class);
+ if (! err)
+ {
+ diskfs_sync_everything (1);
+ diskfs_set_hypermetadata (1, 1);
+ _diskfs_diskdirty = 0;
+
+ /* XXX: if some application writes something after that, we will
+ * crash. That is still better than creating pending writes before
+ * poweroff, and thus fsck on next reboot.
+ */
+ diskfs_readonly = 1;
+ diskfs_readonly_changed (1);
+
+ ports_resume_class_rpcs (diskfs_protid_class);
+ }
+
+ pthread_rwlock_unlock (&diskfs_fsys_lock);
+ }
+
+ ports_port_deref (pi);
+
+ return err;
+}
+
+/* This is called when we have an ordinary environment, complete
+ with proc and auth ports. */
+void
+_diskfs_init_completed ()
+{
+ startup_t init;
+ process_t proc;
+ error_t err;
+ struct port_info *pi;
+ mach_port_t notify;
+ char *name;
+
+ /* Contact the startup server and register our shutdown request.
+ If we get an error, print an informational message. */
+
+ proc = getproc ();
+ assert (proc);
+
+ err = ports_create_port (diskfs_shutdown_notification_class,
+ diskfs_port_bucket, sizeof (struct port_info),
+ &pi);
+ if (err)
+ goto errout;
+
+ /* Mark us as important. */
+ err = proc_mark_important (proc);
+ /* This might fail due to permissions or because the old proc server
+ is still running, ignore any such errors. */
+ if (err && err != EPERM && err != EMIG_BAD_ID)
+ goto errout;
+
+ err = proc_getmsgport (proc, HURD_PID_STARTUP, &init);
+ mach_port_deallocate (mach_task_self (), proc);
+ if (err)
+ goto errout;
+
+ notify = ports_get_send_right (pi);
+ ports_port_deref (pi);
+ asprintf (&name,
+ "%s %s", program_invocation_short_name, diskfs_disk_name ?: "-");
+ err = startup_request_notification (init, notify,
+ MACH_MSG_TYPE_COPY_SEND, name);
+ mach_port_deallocate (mach_task_self (), notify);
+ free (name);
+ if (err)
+ goto errout;
+
+ mach_port_deallocate (mach_task_self (), init);
+ return;
+
+ errout:
+ error (0, err, "Cannot request shutdown notification");
+}
diff --git a/libdiskfs/io-async-icky.c b/libdiskfs/io-async-icky.c
new file mode 100644
index 00000000..22cb3abb
--- /dev/null
+++ b/libdiskfs/io-async-icky.c
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 1994, 1995 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+
+/* Implement io_get_icky_async_id as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_get_icky_async_id (struct protid *cred,
+ mach_port_t *idport,
+ mach_msg_type_name_t *idport_type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ *idport = MACH_PORT_NULL;
+ *idport_type = MACH_MSG_TYPE_COPY_SEND;
+ return 0;
+}
diff --git a/libdiskfs/io-async.c b/libdiskfs/io-async.c
new file mode 100644
index 00000000..b619ddde
--- /dev/null
+++ b/libdiskfs/io-async.c
@@ -0,0 +1,30 @@
+/*
+ Copyright (C) 1994, 1995 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+
+/* Implement io_async as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_async (struct protid *cred __attribute__ ((unused)),
+ mach_port_t notify __attribute__ ((unused)),
+ mach_port_t *idport __attribute__ ((unused)),
+ mach_msg_type_name_t *idport_type
+ __attribute__ ((unused)))
+{
+ return EOPNOTSUPP;
+}
diff --git a/libdiskfs/io-duplicate.c b/libdiskfs/io-duplicate.c
new file mode 100644
index 00000000..45c4df5d
--- /dev/null
+++ b/libdiskfs/io-duplicate.c
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+
+/* Implement io_duplicate as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_duplicate (struct protid *cred,
+ mach_port_t *port,
+ mach_msg_type_name_t *portpoly)
+{
+ error_t err;
+ struct protid *newpi;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&cred->po->np->lock);
+
+ err = diskfs_create_protid (cred->po, cred->user, &newpi);
+ if (! err)
+ {
+ *port = ports_get_right (newpi);
+ *portpoly = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newpi);
+ }
+
+ pthread_mutex_unlock (&cred->po->np->lock);
+
+ return err;
+}
diff --git a/libdiskfs/io-get-conch.c b/libdiskfs/io-get-conch.c
new file mode 100644
index 00000000..fa63a398
--- /dev/null
+++ b/libdiskfs/io-get-conch.c
@@ -0,0 +1,44 @@
+/*
+ Copyright (C) 1994, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+
+/* Implement io_get_conch as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_get_conch (struct protid *cred)
+{
+ struct node *np;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+
+ pthread_mutex_lock (&np->lock);
+
+ if (!cred->mapped)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return EINVAL;
+ }
+
+ iohelp_handle_io_get_conch (&np->conch, cred, cred->mapped);
+
+ pthread_mutex_unlock (&np->lock);
+ return 0;
+}
diff --git a/libdiskfs/io-identity.c b/libdiskfs/io-identity.c
new file mode 100644
index 00000000..0f3fce0f
--- /dev/null
+++ b/libdiskfs/io-identity.c
@@ -0,0 +1,67 @@
+/* libdiskfs implementation of io_identity RPC
+ Copyright (C) 1996,97,98,2001,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+
+/* Implement io_identity as described in <hurd/io.defs>. */
+error_t
+diskfs_S_io_identity (struct protid *cred,
+ mach_port_t *id,
+ mach_msg_type_name_t *idtype,
+ mach_port_t *fsys,
+ mach_msg_type_name_t *fsystype,
+ ino_t *fileno)
+{
+ struct node *np;
+ error_t err;
+ ino_t inum;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+ pthread_mutex_lock (&np->lock);
+ inum = np->dn_stat.st_ino;
+ pthread_mutex_unlock (&np->lock);
+
+ err = fshelp_get_identity (diskfs_port_bucket, inum, id);
+ if (! err)
+ {
+ if (cred->po->shadow_root && cred->po->shadow_root != diskfs_root_node)
+ {
+ err = fshelp_get_identity (diskfs_port_bucket,
+ cred->po->shadow_root->dn_stat.st_ino,
+ fsys);
+ if (err)
+ mach_port_deallocate (mach_task_self (), *id);
+ }
+ else
+ *fsys = diskfs_fsys_identity;
+ }
+ if (! err)
+ {
+ *idtype = MACH_MSG_TYPE_MAKE_SEND;
+ *fsystype = MACH_MSG_TYPE_MAKE_SEND;
+ *fileno = inum;
+ }
+
+ return err;
+}
diff --git a/libdiskfs/io-map-cntl.c b/libdiskfs/io-map-cntl.c
new file mode 100644
index 00000000..2e9b9e98
--- /dev/null
+++ b/libdiskfs/io-map-cntl.c
@@ -0,0 +1,53 @@
+/*
+ Copyright (C) 1994,96,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+#include <mach/default_pager.h>
+
+/* Implement io_map_cntl as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_map_cntl (struct protid *cred,
+ memory_object_t *ctlobj,
+ mach_msg_type_name_t *ctlobj_type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ assert (__vm_page_size >= sizeof (struct shared_io));
+ pthread_mutex_lock (&cred->po->np->lock);
+ if (!cred->mapped)
+ {
+ default_pager_object_create (diskfs_default_pager, &cred->shared_object,
+ __vm_page_size);
+ vm_map (mach_task_self (), (vm_address_t *)&cred->mapped, vm_page_size,
+ 0, 1, cred->shared_object, 0, 0,
+ VM_PROT_READ|VM_PROT_WRITE, VM_PROT_READ|VM_PROT_WRITE, 0);
+ cred->mapped->shared_page_magic = SHARED_PAGE_MAGIC;
+ cred->mapped->conch_status = USER_HAS_NOT_CONCH;
+ pthread_spin_init (&cred->mapped->lock, PTHREAD_PROCESS_PRIVATE);
+ *ctlobj = cred->shared_object;
+ *ctlobj_type = MACH_MSG_TYPE_COPY_SEND;
+ pthread_mutex_unlock (&cred->po->np->lock);
+ return 0;
+ }
+ else
+ {
+ pthread_mutex_unlock (&cred->po->np->lock);
+ return EBUSY;
+ }
+}
diff --git a/libdiskfs/io-map.c b/libdiskfs/io-map.c
new file mode 100644
index 00000000..843b6b16
--- /dev/null
+++ b/libdiskfs/io-map.c
@@ -0,0 +1,72 @@
+/*
+ Copyright (C) 1994, 1997 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <fcntl.h>
+
+#include "priv.h"
+#include "io_S.h"
+
+/* Implement io_map as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_map (struct protid *cred,
+ memory_object_t *rdobj,
+ mach_msg_type_name_t *rdtype,
+ memory_object_t *wrobj,
+ mach_msg_type_name_t *wrtype)
+{
+ int flags;
+ struct node *node;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ *wrobj = *rdobj = MACH_PORT_NULL;
+
+ node = cred->po->np;
+ flags = cred->po->openstat & (O_READ | O_WRITE);
+
+ pthread_mutex_lock (&node->lock);
+ switch (flags)
+ {
+ case O_READ | O_WRITE:
+ *wrobj = *rdobj = diskfs_get_filemap (node, VM_PROT_READ |VM_PROT_WRITE);
+ if (*wrobj == MACH_PORT_NULL)
+ goto error;
+ mach_port_mod_refs (mach_task_self (), *rdobj, MACH_PORT_RIGHT_SEND, 1);
+ break;
+ case O_READ:
+ *rdobj = diskfs_get_filemap (node, VM_PROT_READ);
+ if (*rdobj == MACH_PORT_NULL)
+ goto error;
+ break;
+ case O_WRITE:
+ *wrobj = diskfs_get_filemap (node, VM_PROT_WRITE);
+ if (*wrobj == MACH_PORT_NULL)
+ goto error;
+ break;
+ }
+ pthread_mutex_unlock (&node->lock);
+
+ *rdtype = MACH_MSG_TYPE_MOVE_SEND;
+ *wrtype = MACH_MSG_TYPE_MOVE_SEND;
+
+ return 0;
+
+error:
+ pthread_mutex_unlock (&node->lock);
+ return errno;
+}
diff --git a/libdiskfs/io-modes-get.c b/libdiskfs/io-modes-get.c
new file mode 100644
index 00000000..9ed3b434
--- /dev/null
+++ b/libdiskfs/io-modes-get.c
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 1994 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+
+/* Implement io_get_openmodes as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_get_openmodes (struct protid *cred,
+ int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&cred->po->np->lock);
+ *bits = cred->po->openstat;
+ pthread_mutex_unlock (&cred->po->np->lock);
+ return 0;
+}
diff --git a/libdiskfs/io-modes-off.c b/libdiskfs/io-modes-off.c
new file mode 100644
index 00000000..b1e8f978
--- /dev/null
+++ b/libdiskfs/io-modes-off.c
@@ -0,0 +1,35 @@
+/*
+ Copyright (C) 1994, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+#include <fcntl.h>
+
+/* Implement io_clear_some_openmodes as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_clear_some_openmodes (struct protid *cred,
+ int offbits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&cred->po->np->lock);
+ iohelp_get_conch (&cred->po->np->conch);
+ cred->po->openstat &= ~(offbits & HONORED_STATE_MODES);
+ pthread_mutex_unlock (&cred->po->np->lock);
+ return 0;
+}
diff --git a/libdiskfs/io-modes-on.c b/libdiskfs/io-modes-on.c
new file mode 100644
index 00000000..c6ac3b12
--- /dev/null
+++ b/libdiskfs/io-modes-on.c
@@ -0,0 +1,35 @@
+/*
+ Copyright (C) 1994, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+#include <fcntl.h>
+
+/* Implement io_set_some_openmodes as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_set_some_openmodes (struct protid *cred,
+ int newbits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&cred->po->np->lock);
+ iohelp_get_conch (&cred->po->np->conch);
+ cred->po->openstat |= (newbits & HONORED_STATE_MODES);
+ pthread_mutex_unlock (&cred->po->np->lock);
+ return 0;
+}
diff --git a/libdiskfs/io-modes-set.c b/libdiskfs/io-modes-set.c
new file mode 100644
index 00000000..a8e2d7fa
--- /dev/null
+++ b/libdiskfs/io-modes-set.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 1994, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+#include <fcntl.h>
+
+/* Implement io_set_all_openmodes as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_set_all_openmodes (struct protid *cred,
+ int newbits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&cred->po->np->lock);
+ iohelp_get_conch (&cred->po->np->conch);
+ cred->po->openstat &= ~HONORED_STATE_MODES;
+ cred->po->openstat |= (newbits & HONORED_STATE_MODES);
+ pthread_mutex_unlock (&cred->po->np->lock);
+ return 0;
+}
diff --git a/libdiskfs/io-owner-get.c b/libdiskfs/io-owner-get.c
new file mode 100644
index 00000000..2f3e7e33
--- /dev/null
+++ b/libdiskfs/io-owner-get.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 1994 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+
+/* Implement io_get_owner as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_get_owner (struct protid *cred,
+ pid_t *owner)
+{
+ struct node *np;
+
+ if (!cred)
+ return EOPNOTSUPP;
+ np = cred->po->np;
+
+ pthread_mutex_lock (&np->lock);
+ *owner = np->owner;
+ pthread_mutex_unlock (&np->lock);
+ return 0;
+}
diff --git a/libdiskfs/io-owner-mod.c b/libdiskfs/io-owner-mod.c
new file mode 100644
index 00000000..938c3d0d
--- /dev/null
+++ b/libdiskfs/io-owner-mod.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 1994 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+
+/* Implement io_mod_owner as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_mod_owner (struct protid *cred,
+ pid_t owner)
+{
+ struct node *np;
+
+ if (!cred)
+ return EOPNOTSUPP;
+ np = cred->po->np;
+
+ pthread_mutex_lock (&np->lock);
+ np->owner = owner;
+ pthread_mutex_unlock (&np->lock);
+ return 0;
+}
diff --git a/libdiskfs/io-pathconf.c b/libdiskfs/io-pathconf.c
new file mode 100644
index 00000000..38e277c3
--- /dev/null
+++ b/libdiskfs/io-pathconf.c
@@ -0,0 +1,80 @@
+/* libdiskfs implementation of io.defs: io_pathconf
+ Copyright (C) 1992, 1993, 1994, 1995, 1999 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include "priv.h"
+#include "io_S.h"
+#include <dirent.h>
+#include <limits.h>
+
+/* Implement io_pathconf as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_pathconf (struct protid *cred,
+ int name,
+ int *value)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ switch (name)
+ {
+ case _PC_LINK_MAX:
+ *value = diskfs_link_max;
+ break;
+
+ case _PC_MAX_CANON:
+ case _PC_MAX_INPUT:
+ case _PC_PIPE_BUF:
+ case _PC_VDISABLE:
+ case _PC_SOCK_MAXBUF:
+ case _PC_PATH_MAX:
+ *value = -1;
+ break;
+
+ case _PC_NAME_MAX:
+ /* <hurd/hurd_types.defs> string_t constrains the upper bound.
+ The `struct dirent' format defined by libc further contrains it. */
+#define D_NAMLEN_MAX (UCHAR_MAX * sizeof (((struct dirent *) 0)->d_namlen))
+ if (diskfs_name_max > D_NAMLEN_MAX || diskfs_name_max < 0)
+ diskfs_name_max = D_NAMLEN_MAX;
+ *value = diskfs_name_max;
+ break;
+
+ case _PC_NO_TRUNC: /* enforced in diskfs_lookup */
+ *value = 1; /* diskfs_name_max >= 0; */ /* see above */
+ break;
+
+ case _PC_CHOWN_RESTRICTED:
+ case _PC_SYNC_IO:
+ case _PC_ASYNC_IO:
+ *value = 1;
+ break;
+
+ case _PC_PRIO_IO:
+ *value = 0;
+ break;
+
+ case _PC_FILESIZEBITS:
+ *value = 32;
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ return 0;
+}
diff --git a/libdiskfs/io-prenotify.c b/libdiskfs/io-prenotify.c
new file mode 100644
index 00000000..4eb1c657
--- /dev/null
+++ b/libdiskfs/io-prenotify.c
@@ -0,0 +1,72 @@
+/*
+ Copyright (C) 1994, 1995, 1996, 2001 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+
+/* Implement io_prenotify as described in <hurd/io.defs>.
+
+ We set the prenotify size to be the allocated size of the file;
+ then users are forced to call this routine before writing past
+ that, and we can do allocation (or return ENOSPC if necessary). */
+kern_return_t
+diskfs_S_io_prenotify (struct protid *cred,
+ vm_offset_t start __attribute__ ((unused)),
+ vm_offset_t end)
+{
+ struct node *np;
+ int err = 0;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+
+ /* Clamp it down */
+ pthread_mutex_lock (&np->lock);
+
+ if (!cred->mapped)
+ {
+ err = EINVAL;
+ goto out;
+ }
+
+ err = iohelp_verify_user_conch (&np->conch, cred);
+ if (err)
+ goto out;
+
+ iohelp_fetch_shared_data (cred);
+
+ if ((off_t) end < np->allocsize)
+ {
+ /* The user didn't need to do this, so we'll make sure they
+ have the right shared page info. */
+ pthread_spin_lock (&cred->mapped->lock);
+ iohelp_put_shared_data (cred);
+ pthread_spin_unlock (&cred->mapped->lock);
+ goto out;
+ }
+
+ err = diskfs_grow (np, end, cred);
+ if (diskfs_synchronous)
+ diskfs_node_update (np, 1);
+ if (!err && np->filemod_reqs)
+ diskfs_notice_filechange (np, FILE_CHANGED_EXTEND, 0, end);
+ out:
+ pthread_mutex_unlock (&np->lock);
+ return err;
+}
diff --git a/libdiskfs/io-read.c b/libdiskfs/io-read.c
new file mode 100644
index 00000000..c849434b
--- /dev/null
+++ b/libdiskfs/io-read.c
@@ -0,0 +1,109 @@
+/*
+ Copyright (C) 1994,95,96,97,99,2001 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+#include <fcntl.h>
+
+/* Implement io_read as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_read (struct protid *cred,
+ char **data,
+ mach_msg_type_number_t *datalen,
+ off_t offset,
+ mach_msg_type_number_t maxread)
+{
+ struct node *np;
+ int err;
+ off_t off = offset;
+ char *buf;
+ int ourbuf = 0;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+ if (!(cred->po->openstat & O_READ))
+ return EBADF;
+
+ pthread_mutex_lock (&np->lock);
+
+ iohelp_get_conch (&np->conch);
+
+ if (off == -1)
+ off = cred->po->filepointer;
+ if (off < 0)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return EINVAL;
+ }
+
+ if (off > np->dn_stat.st_size)
+ maxread = 0;
+ else if (off + (off_t) maxread > np->dn_stat.st_size)
+ maxread = np->dn_stat.st_size - off;
+
+ if (maxread > *datalen)
+ {
+ ourbuf = 1;
+ buf = mmap (0, maxread, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ *data = buf;
+ }
+ else
+ buf = *data;
+
+ *datalen = maxread;
+
+ if (maxread == 0)
+ err = 0;
+ else if (S_ISLNK (np->dn_stat.st_mode))
+ {
+ /* Read from a symlink. */
+ if (! diskfs_read_symlink_hook)
+ err = EINVAL;
+ else
+ {
+ if (off == 0 && maxread == np->dn_stat.st_size)
+ err = (*diskfs_read_symlink_hook)(np, buf);
+ else
+ {
+ char *whole_link = alloca (np->dn_stat.st_size);
+ err = (*diskfs_read_symlink_hook)(np, whole_link);
+ if (! err)
+ memcpy (buf, whole_link + off, maxread);
+ }
+ }
+ }
+ else
+ err = EINVAL; /* Use read below. */
+
+ if (err == EINVAL)
+ err = _diskfs_rdwr_internal (np, buf, off, datalen, 0,
+ cred->po->openstat & O_NOATIME);
+
+ if (diskfs_synchronous)
+ diskfs_node_update (np, 1); /* atime! */
+
+ if (offset == -1 && !err)
+ cred->po->filepointer += *datalen;
+
+ if (err && ourbuf)
+ munmap (buf, maxread);
+
+ pthread_mutex_unlock (&np->lock);
+ return err;
+}
diff --git a/libdiskfs/io-readable.c b/libdiskfs/io-readable.c
new file mode 100644
index 00000000..c3debac3
--- /dev/null
+++ b/libdiskfs/io-readable.c
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+#include <fcntl.h>
+
+/* Implement io_readable as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_readable (struct protid *cred,
+ mach_msg_type_number_t *amount)
+{
+ struct node *np;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (!(cred->po->openstat & O_READ))
+ return EINVAL;
+
+ np = cred->po->np;
+
+ pthread_mutex_lock (&np->lock);
+ iohelp_get_conch (&np->conch);
+ if (np->dn_stat.st_size > cred->po->filepointer)
+ *amount = np->dn_stat.st_size - cred->po->filepointer;
+ else
+ *amount = 0;
+
+ pthread_mutex_unlock (&np->lock);
+ return 0;
+}
diff --git a/libdiskfs/io-reauthenticate.c b/libdiskfs/io-reauthenticate.c
new file mode 100644
index 00000000..69d78bc5
--- /dev/null
+++ b/libdiskfs/io-reauthenticate.c
@@ -0,0 +1,66 @@
+/*
+ Copyright (C) 1994,95,96,2000,01 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+
+/* Implement io_reathenticate as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_reauthenticate (struct protid *cred,
+ mach_port_t rend_port)
+{
+ struct protid *newcred;
+ error_t err;
+ mach_port_t newright;
+ struct iouser *user;
+
+ if (cred == 0)
+ return EOPNOTSUPP;
+
+ /* This routine must carefully ignore EINTR because we
+ are a simpleroutine, so callers won't know to restart. */
+
+ pthread_mutex_lock (&cred->po->np->lock);
+ do
+ err = diskfs_start_protid (cred->po, &newcred);
+ while (err == EINTR);
+ if (err)
+ {
+ pthread_mutex_unlock (&cred->po->np->lock);
+ return err;
+ }
+
+ newright = ports_get_send_right (newcred);
+ assert (newright != MACH_PORT_NULL);
+
+ err = iohelp_reauth (&user, diskfs_auth_server_port, rend_port,
+ newright, 1);
+ if (! err)
+ {
+ diskfs_finish_protid (newcred, user);
+ iohelp_free_iouser (user);
+ mach_port_deallocate (mach_task_self (), rend_port);
+ }
+
+ mach_port_deallocate (mach_task_self (), newright);
+
+ pthread_mutex_unlock (&cred->po->np->lock);
+
+ ports_port_deref (newcred);
+
+ return err;
+}
diff --git a/libdiskfs/io-rel-conch.c b/libdiskfs/io-rel-conch.c
new file mode 100644
index 00000000..a42d1ba5
--- /dev/null
+++ b/libdiskfs/io-rel-conch.c
@@ -0,0 +1,44 @@
+/*
+ Copyright (C) 1994, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+
+/* Implement io_release_conch as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_release_conch (struct protid *cred)
+{
+ struct node *np;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+ pthread_mutex_lock (&np->lock);
+ if (!cred->mapped)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return EINVAL;
+ }
+
+ np = cred->po->np;
+
+ iohelp_handle_io_release_conch (&np->conch, cred);
+
+ pthread_mutex_unlock (&np->lock);
+ return 0;
+}
diff --git a/libdiskfs/io-restrict-auth.c b/libdiskfs/io-restrict-auth.c
new file mode 100644
index 00000000..011aa19e
--- /dev/null
+++ b/libdiskfs/io-restrict-auth.c
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 1994,95,96,2001, 2002 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+
+
+/* Implement io_restrict_auth as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_restrict_auth (struct protid *cred,
+ mach_port_t *newport,
+ mach_msg_type_name_t *newportpoly,
+ uid_t *uids,
+ size_t nuids,
+ gid_t *gids,
+ size_t ngids)
+{
+ error_t err;
+ struct iouser *user;
+ struct protid *newpi;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ err = iohelp_restrict_iouser (&user, cred->user, uids, nuids, gids, ngids);
+ if (err)
+ return err;
+
+ pthread_mutex_lock (&cred->po->np->lock);
+ err = diskfs_create_protid (cred->po, user, &newpi);
+ if (! err)
+ {
+ *newport = ports_get_right (newpi);
+ *newportpoly = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newpi);
+ }
+ pthread_mutex_unlock (&cred->po->np->lock);
+
+ iohelp_free_iouser (user);
+ return err;
+}
diff --git a/libdiskfs/io-revoke.c b/libdiskfs/io-revoke.c
new file mode 100644
index 00000000..a07777e2
--- /dev/null
+++ b/libdiskfs/io-revoke.c
@@ -0,0 +1,59 @@
+/*
+ Copyright (C) 1999 Free Software Foundation
+ Written by Thomas Bushnell, BSG.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+
+/* Implement io_revoke as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_revoke (struct protid *cred)
+{
+ error_t err;
+ struct node *np;
+
+ error_t
+ iterator_function (void *port)
+ {
+ struct protid *user = port;
+
+ if ((user != cred)
+ && (user->po->np == np))
+ ports_destroy_right (user);
+ return 0;
+ }
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+
+ pthread_mutex_lock (&np->lock);
+
+ err = fshelp_isowner (&np->dn_stat, cred->user);
+
+ pthread_mutex_unlock (&np->lock);
+
+ if (err)
+ return err;
+
+ ports_inhibit_bucket_rpcs (diskfs_port_bucket);
+ ports_class_iterate (diskfs_protid_class, iterator_function);
+ ports_resume_bucket_rpcs (diskfs_port_bucket);
+
+ return 0;
+}
diff --git a/libdiskfs/io-seek.c b/libdiskfs/io-seek.c
new file mode 100644
index 00000000..9e3ff093
--- /dev/null
+++ b/libdiskfs/io-seek.c
@@ -0,0 +1,61 @@
+/*
+ Copyright (C) 1994,1995,1996,2000,2006 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+#include <unistd.h>
+
+/* Implement io_seek as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_seek (struct protid *cred,
+ off_t offset,
+ int whence,
+ off_t *newoffset)
+{
+ error_t err = 0;
+ struct node *np;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+
+ pthread_mutex_lock (&np->lock);
+
+ iohelp_get_conch (&np->conch);
+ switch (whence)
+ {
+ case SEEK_CUR:
+ offset += cred->po->filepointer;
+ goto check;
+ case SEEK_END:
+ offset += np->dn_stat.st_size;
+ case SEEK_SET:
+ check:
+ if (offset >= 0)
+ {
+ *newoffset = cred->po->filepointer = offset;
+ break;
+ }
+ default:
+ err = EINVAL;
+ break;
+ }
+
+ pthread_mutex_unlock (&np->lock);
+ return err;
+}
diff --git a/libdiskfs/io-select.c b/libdiskfs/io-select.c
new file mode 100644
index 00000000..ddac738f
--- /dev/null
+++ b/libdiskfs/io-select.c
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+#include <fcntl.h>
+
+/* Implement io_select as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_select (struct protid *cred,
+ int *type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ *type &= ~SELECT_URG;
+ return 0;
+}
+
+kern_return_t
+diskfs_S_io_select_timeout (struct protid *cred,
+ struct timespec ts,
+ int *type)
+{
+ return diskfs_S_io_select (cred, type);
+}
diff --git a/libdiskfs/io-sigio.c b/libdiskfs/io-sigio.c
new file mode 100644
index 00000000..1e4c461b
--- /dev/null
+++ b/libdiskfs/io-sigio.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 1994, 1995 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+#include <fcntl.h>
+
+/* Implement io_sigio as described in <hurd/io.defs>. We do this
+ for O_FSYNC right now, but will eventually do it for async I/O
+ too. */
+kern_return_t
+diskfs_S_io_sigio (struct protid *cred)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&cred->po->np->lock);
+ if ((cred->po->openstat & O_FSYNC) || diskfs_synchronous)
+ diskfs_file_update (cred->po->np, 1);
+ pthread_mutex_unlock (&cred->po->np->lock);
+ return 0;
+}
diff --git a/libdiskfs/io-stat.c b/libdiskfs/io-stat.c
new file mode 100644
index 00000000..211b53a8
--- /dev/null
+++ b/libdiskfs/io-stat.c
@@ -0,0 +1,51 @@
+/*
+ Copyright (C) 1994,95,96,97,2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+#include <string.h>
+
+/* Implement io_stat as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_stat (struct protid *cred,
+ io_statbuf_t *statbuf)
+{
+ struct node *np;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+ pthread_mutex_lock (&np->lock);
+
+ iohelp_get_conch (&np->conch);
+ if (diskfs_synchronous)
+ diskfs_node_update (np, 1);
+ else
+ diskfs_set_node_times (np);
+
+ memcpy (statbuf, &np->dn_stat, sizeof (struct stat));
+ statbuf->st_mode &= ~(S_IATRANS | S_IROOT);
+ if (fshelp_translated (&np->transbox))
+ statbuf->st_mode |= S_IATRANS;
+ if (cred->po->shadow_root == np || np == diskfs_root_node)
+ statbuf->st_mode |= S_IROOT;
+
+ pthread_mutex_unlock (&np->lock);
+
+ return 0;
+}
diff --git a/libdiskfs/io-stubs.c b/libdiskfs/io-stubs.c
new file mode 100644
index 00000000..592b3d8b
--- /dev/null
+++ b/libdiskfs/io-stubs.c
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 1994, 1995 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+
+/* Unimplemented stubs. */
+
+/* Implement io_readsleep as described in <hurd/io.defs>
+ Semantics of ordinary files say this shouldn't happen, because
+ we never set use_read_size in the shared data. */
+kern_return_t
+diskfs_S_io_readsleep (struct protid *cred)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ return 0;
+}
+
+/* Implement io_eofnotify as described in <hurd/io.defs>.
+ We don't use this feature. */
+kern_return_t
+diskfs_S_io_eofnotify (struct protid *cred)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ return 0;
+}
+
+/* Implement io_postnotify as described in <hurd/io.defs>.
+ We don't use this feature. */
+kern_return_t
+diskfs_S_io_postnotify (struct protid *cred,
+ vm_offset_t start __attribute__ ((unused)),
+ vm_offset_t end __attribute__ ((unused)))
+{
+ return cred ? 0 : EOPNOTSUPP;
+}
+
+/* Implement io_readnotify as described in <hurd/io.defs>.
+ We don't use this feature. */
+kern_return_t
+diskfs_S_io_readnotify (struct protid *cred)
+{
+ return cred ? 0 : EOPNOTSUPP;
+}
diff --git a/libdiskfs/io-version.c b/libdiskfs/io-version.c
new file mode 100644
index 00000000..b19dd0d1
--- /dev/null
+++ b/libdiskfs/io-version.c
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 1994, 1996 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include <stdio.h>
+
+#include "priv.h"
+#include "io_S.h"
+
+kern_return_t
+diskfs_S_io_server_version (struct protid *cred,
+ char *server_name,
+ int *major,
+ int *minor,
+ int *edit)
+{
+ if (cred)
+ {
+ snprintf (server_name, sizeof (string_t), "%s %s",
+ diskfs_server_name, diskfs_server_version);
+ return 0;
+ }
+ else
+ return EOPNOTSUPP;
+}
diff --git a/libdiskfs/io-write.c b/libdiskfs/io-write.c
new file mode 100644
index 00000000..2967c4c8
--- /dev/null
+++ b/libdiskfs/io-write.c
@@ -0,0 +1,93 @@
+/*
+ Copyright (C) 1994,95,96,97,2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "io_S.h"
+#include <fcntl.h>
+
+/* Implement io_write as described in <hurd/io.defs>. */
+kern_return_t
+diskfs_S_io_write (struct protid *cred,
+ char *data,
+ mach_msg_type_number_t datalen,
+ off_t offset,
+ mach_msg_type_number_t *amt)
+{
+ struct node *np;
+ error_t err;
+ off_t off = offset;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+ if (!(cred->po->openstat & O_WRITE))
+ return EBADF;
+
+ pthread_mutex_lock (&np->lock);
+
+ assert (!S_ISDIR(np->dn_stat.st_mode));
+
+ iohelp_get_conch (&np->conch);
+
+ if (off == -1)
+ {
+ if (cred->po->openstat & O_APPEND)
+ cred->po->filepointer = np->dn_stat.st_size;
+ off = cred->po->filepointer;
+ }
+ if (off < 0)
+ {
+ err = EINVAL;
+ goto out;
+ }
+
+ while (off + (off_t) datalen > np->allocsize)
+ {
+ err = diskfs_grow (np, off + datalen, cred);
+ if (diskfs_synchronous)
+ diskfs_node_update (np, 1);
+ if (err)
+ goto out;
+ if (np->filemod_reqs)
+ diskfs_notice_filechange (np, FILE_CHANGED_EXTEND, 0, off + datalen);
+ }
+
+ if (off + (off_t) datalen > np->dn_stat.st_size)
+ {
+ np->dn_stat.st_size = off + datalen;
+ np->dn_set_ctime = 1;
+ if (diskfs_synchronous)
+ diskfs_node_update (np, 1);
+ }
+
+ *amt = datalen;
+ err = _diskfs_rdwr_internal (np, data, off, amt, 1, 0);
+
+ if (!err && offset == -1)
+ cred->po->filepointer += *amt;
+
+ if (!err
+ && ((cred->po->openstat & O_FSYNC) || diskfs_synchronous))
+ diskfs_file_update (np, 1);
+
+ if (!err && np->filemod_reqs)
+ diskfs_notice_filechange (np, FILE_CHANGED_WRITE, off, off + *amt);
+ out:
+ pthread_mutex_unlock (&np->lock);
+ return err;
+}
diff --git a/libdiskfs/lithp.h b/libdiskfs/lithp.h
new file mode 100644
index 00000000..be56377d
--- /dev/null
+++ b/libdiskfs/lithp.h
@@ -0,0 +1,31 @@
+/*
+ Copyright (C) 1994, 1996 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+/* Hmm. */
+
+#define dithkfth_TH_file_chauthor diskfs_S_file_chauthor
+#define fthhelp_ithowner fshelp_isowner
+#define dithkfth_validate_author_change diskfs_validate_author_change
+#define dn_thtat dn_stat
+#define tht_author st_author
+#define dn_thet_theetime dn_set_ctime
+#define fth_TH_dot_h "fs_S.h"
+#define uther user
diff --git a/libdiskfs/lookup.c b/libdiskfs/lookup.c
new file mode 100644
index 00000000..bc2ad01e
--- /dev/null
+++ b/libdiskfs/lookup.c
@@ -0,0 +1,234 @@
+/* Wrapper for diskfs_lookup_hard
+ Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+#include <string.h>
+
+static struct
+{
+ int present;
+ int absent;
+ int errors;
+ int dot;
+ int dotdot;
+} cache_misses;
+static pthread_spinlock_t cm_lock = PTHREAD_SPINLOCK_INITIALIZER;
+
+
+/* Lookup in directory DP (which is locked) the name NAME. TYPE will
+ either be LOOKUP, CREATE, RENAME, or REMOVE. CRED identifies the
+ user making the call.
+
+ NAME will have leading and trailing slashes stripped. It is an
+ error if there are internal slashes. NAME will be modified in
+ place if there are slashes in it; it is therefore an error to
+ specify a constant NAME which contains slashes.
+
+ If the name is found, return zero, and (if NP is nonzero) set *NP
+ to point to the node for it, locked. If the name is not found,
+ return ENOENT, and (if NP is nonzero) set *NP to zero. If NP is
+ zero, then the node found must not be locked, even transitorily.
+ Lookups for REMOVE and RENAME (which must often check permissions
+ on the node being found) will always set NP.
+
+ If DS is nonzero then:
+ For LOOKUP: set *DS to be ignored by diskfs_drop_dirstat.
+ For CREATE: on success, set *DS to be ignored by diskfs_drop_dirstat.
+ on failure, set *DS for a future call to diskfs_direnter.
+ For RENAME: on success, set *DS for a future call to diskfs_dirrewrite.
+ on failure, set *DS for a future call to diskfs_direnter.
+ For REMOVE: on success, set *DS for a future call to diskfs_dirremove.
+ on failure, set *DS to be ignored by diskfs_drop_dirstat.
+ The caller of this function guarantees that if DS is nonzero, then
+ either the appropriate call listed above or diskfs_drop_dirstat will
+ be called with DS before the directory DP is unlocked, and guarantees
+ that no lookup calls will be made on this directory between this
+ lookup and the use (or descruction) of *DS.
+
+ If you use the library's versions of diskfs_rename_dir,
+ diskfs_clear_directory, and diskfs_init_dir, then lookups for `..'
+ might have the flag SPEC_DOTDOT or'd in. This has the following special
+ meaning:
+ For LOOKUP: DP should be unlocked and its reference dropped before
+ returning.
+ For RENAME and REMOVE: The node being found (*NP) is already held
+ locked, so don't lock it or add a reference to it.
+ (SPEC_DOTDOT will not be given with CREATE.)
+
+ Return ENOTDIR if DP is not a directory.
+ Return EACCES if CRED isn't allowed to search DP.
+ Return EACCES if completing the operation will require writing
+ the directory and diskfs_checkdirmod won't allow the modification.
+ Return ENOENT if NAME isn't in the directory.
+ Return EAGAIN if NAME refers to the `..' of this filesystem's root.
+ Return EIO if appropriate.
+
+ This function is a wrapper for diskfs_lookup_hard. */
+error_t
+diskfs_lookup (struct node *dp, const char *name, enum lookup_type type,
+ struct node **np, struct dirstat *ds, struct protid *cred)
+{
+ error_t err;
+ struct node *cached;
+
+ if (type == REMOVE || type == RENAME)
+ assert (np);
+
+ if (!S_ISDIR (dp->dn_stat.st_mode))
+ {
+ if (ds)
+ diskfs_null_dirstat (ds);
+ return ENOTDIR;
+ }
+
+ /* Strip leading and trailing slashes. */
+ while (*name == '/')
+ name++;
+
+ if (name[0] == '\0')
+ {
+ if (ds)
+ diskfs_null_dirstat (ds);
+ return EINVAL;
+ }
+ else
+ {
+ char *p = strchr (name, '/');
+ if (p != 0)
+ {
+ *p = '\0';
+ do
+ ++p;
+ while (*p == '/');
+ if (*p != '\0')
+ {
+ if (ds)
+ diskfs_null_dirstat (ds);
+ return EINVAL;
+ }
+ }
+ }
+
+
+ err = fshelp_access (&dp->dn_stat, S_IEXEC, cred->user);
+ if (err)
+ {
+ if (ds)
+ diskfs_null_dirstat (ds);
+ return err;
+ }
+
+ if (dp == cred->po->shadow_root
+ && name[0] == '.' && name[1] == '.' && name[2] == '\0')
+ /* Ran into the root. */
+ {
+ if (ds)
+ diskfs_null_dirstat (ds);
+ return EAGAIN;
+ }
+
+ if (type == LOOKUP)
+ /* Check the cache first */
+ cached = diskfs_check_lookup_cache (dp, name);
+ else
+ cached = 0;
+
+ if (cached == (struct node *)-1)
+ /* Negative lookup cached. */
+ {
+ if (np)
+ *np = 0;
+ return ENOENT;
+ }
+ else if (cached)
+ {
+ if (np)
+ *np = cached; /* Return what we found. */
+ else
+ /* Ick, the user doesn't want the result, we have to drop our
+ reference. */
+ if (cached == dp)
+ diskfs_nrele (cached);
+ else
+ diskfs_nput (cached);
+
+ if (ds)
+ diskfs_null_dirstat (ds);
+ }
+ else
+ {
+ err = diskfs_lookup_hard (dp, name, type, np, ds, cred);
+
+ pthread_spin_lock (&cm_lock);
+ if (type == LOOKUP)
+ {
+ if (err == ENOENT)
+ cache_misses.absent++;
+ else if (err)
+ cache_misses.errors++;
+ else
+ cache_misses.present++;
+ if (name[0] == '.')
+ {
+ if (name[1] == '\0')
+ cache_misses.dot++;
+ else if (name[1] == '.' && name[2] == '\0')
+ cache_misses.dotdot++;
+ }
+ }
+ pthread_spin_unlock (&cm_lock);
+
+ if (err && err != ENOENT)
+ return err;
+
+ if (type == RENAME
+ || (type == CREATE && err == ENOENT)
+ || (type == REMOVE && err != ENOENT))
+ {
+ error_t err2;
+
+ if (diskfs_name_max > 0 && strlen (name) > diskfs_name_max)
+ err2 = ENAMETOOLONG;
+ else
+ err2 = fshelp_checkdirmod (&dp->dn_stat,
+ (err || !np) ? 0 : &(*np)->dn_stat,
+ cred->user);
+ if (err2)
+ {
+ if (np && !err)
+ {
+ if (*np == dp)
+ diskfs_nrele (*np);
+ else
+ diskfs_nput (*np);
+ *np = 0;
+ }
+ return err2;
+ }
+ }
+
+ if ((type == LOOKUP || type == CREATE) && !err && np)
+ diskfs_enter_lookup_cache (dp, *np, name);
+ else if (type == LOOKUP && err == ENOENT)
+ diskfs_enter_lookup_cache (dp, 0, name);
+ }
+
+ return err;
+}
diff --git a/libdiskfs/name-cache.c b/libdiskfs/name-cache.c
new file mode 100644
index 00000000..d8f86b15
--- /dev/null
+++ b/libdiskfs/name-cache.c
@@ -0,0 +1,376 @@
+/* Directory name lookup caching
+
+ Copyright (C) 1996, 1997, 1998, 2014 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG, & Miles Bader.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+#include <assert.h>
+#include <string.h>
+
+/* The name cache is implemented using a hash table.
+
+ We use buckets of a fixed size. We approximate the
+ least-frequently used cache algorithm by counting the number of
+ lookups using saturating arithmetic in the two lowest bits of the
+ pointer to the name. Using this strategy we achieve a constant
+ worst-case lookup and insertion time. */
+
+/* Number of buckets. Must be a power of two. */
+#define CACHE_SIZE 256
+
+/* Entries per bucket. */
+#define BUCKET_SIZE 4
+
+/* A mask for fast binary modulo. */
+#define CACHE_MASK (CACHE_SIZE - 1)
+
+/* Cache bucket with BUCKET_SIZE entries.
+
+ The layout of the bucket is chosen so that it will be straight
+ forward to use vector operations in the future. */
+struct cache_bucket
+{
+ /* Name of the node NODE_CACHE_ID in the directory DIR_CACHE_ID. If
+ NULL, the entry is unused. */
+ unsigned long name[BUCKET_SIZE];
+
+ /* The key. */
+ unsigned long key[BUCKET_SIZE];
+
+ /* Used to indentify nodes to the fs dependent code. */
+ ino64_t dir_cache_id[BUCKET_SIZE];
+
+ /* 0 for NODE_CACHE_ID means a `negative' entry -- recording that
+ there's definitely no node with this name. */
+ ino64_t node_cache_id[BUCKET_SIZE];
+};
+
+/* The cache. */
+static struct cache_bucket name_cache[CACHE_SIZE];
+
+/* Protected by this lock. */
+static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Given VALUE, return the char pointer. */
+static inline char *
+charp (unsigned long value)
+{
+ return (char *) (value & ~3L);
+}
+
+/* Given VALUE, return the approximation of use frequency. */
+static inline unsigned long
+frequ (unsigned long value)
+{
+ return value & 3;
+}
+
+/* Add an entry in the Ith slot of the given bucket. If there is a
+ value there, remove it first. */
+static inline void
+add_entry (struct cache_bucket *b, int i,
+ const char *name, unsigned long key,
+ ino64_t dir_cache_id, ino64_t node_cache_id)
+{
+ if (b->name[i])
+ free (charp (b->name[i]));
+
+ b->name[i] = (unsigned long) strdup (name);
+ assert ((b->name[i] & 3) == 0);
+ if (b->name[i] == 0)
+ return;
+
+ b->key[i] = key;
+ b->dir_cache_id[i] = dir_cache_id;
+ b->node_cache_id[i] = node_cache_id;
+}
+
+/* Remove the entry in the Ith slot of the given bucket. */
+static inline void
+remove_entry (struct cache_bucket *b, int i)
+{
+ if (b->name[i])
+ free (charp (b->name[i]));
+ b->name[i] = 0;
+}
+
+/* Check if the entry in the Ith slot of the given bucket is
+ valid. */
+static inline int
+valid_entry (struct cache_bucket *b, int i)
+{
+ return b->name[i] != 0;
+}
+
+/* This is the Murmur3 hash algorithm. */
+
+#define FORCE_INLINE inline __attribute__((always_inline))
+
+inline uint32_t rotl32 ( uint32_t x, int8_t r )
+{
+ return (x << r) | (x >> (32 - r));
+}
+
+#define ROTL32(x,y) rotl32(x,y)
+
+/* Block read - if your platform needs to do endian-swapping or can
+ only handle aligned reads, do the conversion here. */
+
+FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i )
+{
+ return p[i];
+}
+
+/* Finalization mix - force all bits of a hash block to avalanche. */
+
+FORCE_INLINE uint32_t fmix32 ( uint32_t h )
+{
+ h ^= h >> 16;
+ h *= 0x85ebca6b;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35;
+ h ^= h >> 16;
+
+ return h;
+}
+
+/* The Murmur3 hash function. */
+void MurmurHash3_x86_32 ( const void * key, int len,
+ uint32_t seed, void * out )
+{
+ const uint8_t * data = (const uint8_t*)key;
+ const int nblocks = len / 4;
+
+ uint32_t h1 = seed;
+
+ const uint32_t c1 = 0xcc9e2d51;
+ const uint32_t c2 = 0x1b873593;
+
+ /* body */
+
+ const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
+
+ for(int i = -nblocks; i; i++)
+ {
+ uint32_t k1 = getblock32(blocks,i);
+
+ k1 *= c1;
+ k1 = ROTL32(k1,15);
+ k1 *= c2;
+
+ h1 ^= k1;
+ h1 = ROTL32(h1,13);
+ h1 = h1*5+0xe6546b64;
+ }
+
+ /* tail */
+
+ const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
+
+ uint32_t k1 = 0;
+
+ switch(len & 3)
+ {
+ case 3: k1 ^= tail[2] << 16;
+ case 2: k1 ^= tail[1] << 8;
+ case 1: k1 ^= tail[0];
+ k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
+ };
+
+ /* finalization */
+
+ h1 ^= len;
+
+ h1 = fmix32(h1);
+
+ *(uint32_t*)out = h1;
+}
+
+/* If there is no best candidate to replace, pick any. We approximate
+ any by picking the slot depicted by REPLACE, and increment REPLACE
+ then. */
+static int replace;
+
+/* Lookup (DIR_CACHE_ID, NAME, KEY) in the cache. If it is found,
+ return 1 and set BUCKET and INDEX to the item. Otherwise, return 0
+ and set BUCKET and INDEX to the slot where the item should be
+ inserted. */
+static inline int
+lookup (ino64_t dir_cache_id, const char *name, unsigned long key,
+ struct cache_bucket **bucket, int *index)
+{
+ struct cache_bucket *b = *bucket = &name_cache[key & CACHE_MASK];
+ unsigned long best = 3;
+ int i;
+
+ for (i = 0; i < BUCKET_SIZE; i++)
+ {
+ unsigned long f = frequ (b->name[i]);
+
+ if (valid_entry (b, i)
+ && b->key[i] == key
+ && b->dir_cache_id[i] == dir_cache_id
+ && strcmp (charp (b->name[i]), name) == 0)
+ {
+ if (f < 3)
+ b->name[i] += 1;
+
+ *index = i;
+ return 1;
+ }
+
+ /* Keep track of the replacement candidate. */
+ if (f < best)
+ {
+ best = f;
+ *index = i;
+ }
+ }
+
+ /* If there was no entry with a lower use frequency, just replace
+ any entry. */
+ if (best == 3)
+ {
+ *index = replace;
+ replace = (replace + 1) & (BUCKET_SIZE - 1);
+ }
+
+ return 0;
+}
+
+/* Hash the directory cache_id and the name. */
+static inline unsigned long
+hash (ino64_t dir_cache_id, const char *name)
+{
+ unsigned long h;
+ MurmurHash3_x86_32 (&dir_cache_id, sizeof dir_cache_id, 0, &h);
+ MurmurHash3_x86_32 (name, strlen (name), h, &h);
+ return h;
+}
+
+/* Node NP has just been found in DIR with NAME. If NP is null, that
+ means that this name has been confirmed as absent in the directory. */
+void
+diskfs_enter_lookup_cache (struct node *dir, struct node *np, const char *name)
+{
+ unsigned long key = hash (dir->cache_id, name);
+ ino64_t value = np ? np->cache_id : 0;
+ struct cache_bucket *bucket;
+ int i = 0, found;
+
+ pthread_mutex_lock (&cache_lock);
+ found = lookup (dir->cache_id, name, key, &bucket, &i);
+ if (! found)
+ add_entry (bucket, i, name, key, dir->cache_id, value);
+ else
+ if (bucket->node_cache_id[i] != value)
+ bucket->node_cache_id[i] = value;
+
+ pthread_mutex_unlock (&cache_lock);
+}
+
+/* Purge all references in the cache to NP as a node inside
+ directory DP. */
+void
+diskfs_purge_lookup_cache (struct node *dp, struct node *np)
+{
+ int i;
+ struct cache_bucket *b;
+
+ pthread_mutex_lock (&cache_lock);
+
+ for (b = &name_cache[0]; b < &name_cache[CACHE_SIZE]; b++)
+ for (i = 0; i < BUCKET_SIZE; i++)
+ if (valid_entry (b, i)
+ && b->dir_cache_id[i] == dp->cache_id
+ && b->node_cache_id[i] == np->cache_id)
+ remove_entry (b, i);
+
+ pthread_mutex_unlock (&cache_lock);
+}
+
+/* Scan the cache looking for NAME inside DIR. If we don't know
+ anything entry at all, then return 0. If the entry is confirmed to
+ not exist, then return -1. Otherwise, return NP for the entry, with
+ a newly allocated reference. */
+struct node *
+diskfs_check_lookup_cache (struct node *dir, const char *name)
+{
+ unsigned long key = hash (dir->cache_id, name);
+ int lookup_parent = name[0] == '.' && name[1] == '.' && name[2] == '\0';
+ struct cache_bucket *bucket;
+ int i, found;
+
+ if (lookup_parent && dir == diskfs_root_node)
+ /* This is outside our file system, return cache miss. */
+ return NULL;
+
+ pthread_mutex_lock (&cache_lock);
+ found = lookup (dir->cache_id, name, key, &bucket, &i);
+ if (found)
+ {
+ ino64_t id = bucket->node_cache_id[i];
+ pthread_mutex_unlock (&cache_lock);
+
+ if (id == 0)
+ /* A negative cache entry. */
+ return (struct node *) -1;
+ else if (id == dir->cache_id)
+ /* The cached node is the same as DIR. */
+ {
+ diskfs_nref (dir);
+ return dir;
+ }
+ else
+ /* Just a normal entry in DIR; get the actual node. */
+ {
+ struct node *np;
+ error_t err;
+
+ if (lookup_parent)
+ {
+ pthread_mutex_unlock (&dir->lock);
+ err = diskfs_cached_lookup (id, &np);
+ pthread_mutex_lock (&dir->lock);
+
+ /* In the window where DP was unlocked, we might
+ have lost. So check the cache again, and see
+ if it's still there; if so, then we win. */
+ pthread_mutex_lock (&cache_lock);
+ found = lookup (dir->cache_id, name, key, &bucket, &i);
+ if (! found
+ || ! bucket->node_cache_id[i] != id)
+ {
+ pthread_mutex_unlock (&cache_lock);
+
+ /* Lose */
+ diskfs_nput (np);
+ return 0;
+ }
+ pthread_mutex_unlock (&cache_lock);
+ }
+ else
+ err = diskfs_cached_lookup (id, &np);
+ return err ? 0 : np;
+ }
+ }
+
+ pthread_mutex_unlock (&cache_lock);
+ return 0;
+}
diff --git a/libdiskfs/node-create.c b/libdiskfs/node-create.c
new file mode 100644
index 00000000..5b5e4639
--- /dev/null
+++ b/libdiskfs/node-create.c
@@ -0,0 +1,163 @@
+/* Making new files
+ Copyright (C) 1992,93,94,96,98,2001 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* This enables SysV style group behaviour. New nodes inherit the GID
+ of the user creating them unless the SGID bit is set of the parent
+ directory. */
+int _diskfs_no_inherit_dir_group;
+
+/* Create a new node. Give it MODE; if that includes IFDIR, also
+ initialize `.' and `..' in the new directory. Return the node in NPP.
+ CRED identifies the user responsible for the call. If NAME is nonzero,
+ then link the new node into DIR with name NAME; DS is the result of a
+ prior diskfs_lookup for creation (and DIR has been held locked since).
+ DIR must always be provided as at least a hint for disk allocation
+ strategies. */
+error_t
+diskfs_create_node (struct node *dir,
+ const char *name,
+ mode_t mode,
+ struct node **newnode,
+ struct protid *cred,
+ struct dirstat *ds)
+{
+ struct node *np;
+ error_t err;
+ uid_t newuid;
+ gid_t newgid;
+
+ if (diskfs_check_readonly ())
+ {
+ *newnode = NULL;
+ return EROFS;
+ }
+
+ /* Make the node */
+ err = diskfs_alloc_node (dir, mode, newnode);
+ if (err)
+ {
+ if (name)
+ diskfs_drop_dirstat (dir, ds);
+ *newnode = NULL;
+ return err;
+ }
+
+ np = *newnode;
+
+ /* Initialize the on-disk fields. */
+ if (cred->user->uids->num)
+ newuid = cred->user->uids->ids[0];
+ else
+ {
+ newuid = dir->dn_stat.st_uid;
+ mode &= ~S_ISUID;
+ }
+ err = diskfs_validate_owner_change (np, newuid);
+ if (err)
+ goto change_err;
+ np->dn_stat.st_uid = newuid;
+ if (np->author_tracks_uid)
+ np->dn_stat.st_author = newuid;
+
+ if (!_diskfs_no_inherit_dir_group)
+ {
+ newgid = dir->dn_stat.st_gid;
+ if (!idvec_contains (cred->user->gids, newgid))
+ mode &= ~S_ISGID;
+ }
+ else
+ {
+ if (dir->dn_stat.st_mode & S_ISGID)
+ {
+ /* If the parent dir has the sgid bit set, inherit its gid.
+ If the new node is a directory, also inherit the sgid bit
+ set. */
+ newgid = dir->dn_stat.st_gid;
+ if (S_ISDIR (mode))
+ mode |= S_ISGID;
+ else
+ {
+ if (!idvec_contains (cred->user->gids, newgid))
+ mode &= ~S_ISGID;
+ }
+ }
+ else
+ {
+ if (cred->user->gids->num)
+ newgid = cred->user->gids->ids[0];
+ else
+ {
+ newgid = dir->dn_stat.st_gid;
+ mode &= ~S_ISGID;
+ }
+ }
+ }
+
+ err = diskfs_validate_group_change (np, newgid);
+ if (err)
+ goto change_err;
+ np->dn_stat.st_gid = newgid;
+
+ np->dn_stat.st_rdev = 0;
+ np->dn_stat.st_nlink = !!name;
+ err = diskfs_validate_mode_change (np, mode);
+ if (err)
+ goto change_err;
+ np->dn_stat.st_mode = mode;
+
+ np->dn_stat.st_blocks = 0;
+ np->dn_stat.st_size = 0;
+ np->dn_stat.st_flags = 0;
+ np->dn_set_atime = 1;
+ np->dn_set_mtime = 1;
+ np->dn_set_ctime = 1;
+
+ if (S_ISDIR (mode))
+ err = diskfs_init_dir (np, dir, cred);
+
+ diskfs_node_update (np, diskfs_synchronous);
+
+ if (err)
+ {
+ change_err:
+ np->dn_stat.st_mode = 0;
+ np->dn_stat.st_nlink = 0;
+ if (name)
+ diskfs_drop_dirstat (dir, ds);
+ *newnode = NULL;
+ return err;
+ }
+
+ if (name)
+ {
+ err = diskfs_direnter (dir, name, np, ds, cred);
+ if (err)
+ {
+ if (S_ISDIR (mode))
+ diskfs_clear_directory (np, dir, cred);
+ np->dn_stat.st_nlink = 0;
+ np->dn_set_ctime = 1;
+ diskfs_nput (np);
+ }
+ }
+ if (err)
+ *newnode = NULL;
+
+ return err;
+}
diff --git a/libdiskfs/node-drop.c b/libdiskfs/node-drop.c
new file mode 100644
index 00000000..83eb5909
--- /dev/null
+++ b/libdiskfs/node-drop.c
@@ -0,0 +1,98 @@
+/*
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* Free the list of modification requests MR */
+static void
+free_modreqs (struct modreq *mr)
+{
+ struct modreq *tmp;
+ for (; mr; mr = tmp)
+ {
+ mach_port_deallocate (mach_task_self (), mr->port);
+ tmp = mr->next;
+ free (mr);
+ }
+}
+
+
+/* Node NP now has no more references; clean all state. The
+ diskfs_node_refcnt_lock must be held, and will be released
+ upon return. NP must be locked. */
+void
+diskfs_drop_node (struct node *np)
+{
+ mode_t savemode;
+
+ if (np->dn_stat.st_nlink == 0)
+ {
+ diskfs_check_readonly ();
+ assert (!diskfs_readonly);
+
+ if (np->dn_stat.st_mode & S_IPTRANS)
+ diskfs_set_translator (np, 0, 0, 0);
+
+ if (np->allocsize != 0
+ || (diskfs_create_symlink_hook
+ && S_ISLNK (np->dn_stat.st_mode)
+ && np->dn_stat.st_size))
+ {
+ /* If the node needs to be truncated, then a complication
+ arises, because truncation might require gaining
+ new references to the node. So, we give ourselves
+ a reference back, unlock the refcnt lock. Then
+ we are in the state of a normal user, and do the truncate
+ and an nput. The next time through, this routine
+ will notice that the size is zero, and not have to
+ do anything. */
+ np->references++;
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ diskfs_truncate (np, 0);
+
+ /* Force allocsize to zero; if truncate consistently fails this
+ will at least prevent an infinite loop in this routine. */
+ np->allocsize = 0;
+
+ diskfs_nput (np);
+ return;
+ }
+
+ assert (np->dn_stat.st_size == 0);
+
+ savemode = np->dn_stat.st_mode;
+ np->dn_stat.st_mode = 0;
+ np->dn_stat.st_rdev = 0;
+ np->dn_set_ctime = np->dn_set_atime = 1;
+ diskfs_node_update (np, diskfs_synchronous);
+ diskfs_free_node (np, savemode);
+ }
+ else
+ diskfs_node_update (np, diskfs_synchronous);
+
+ fshelp_drop_transbox (&np->transbox);
+
+ if (np->dirmod_reqs)
+ free_modreqs (np->dirmod_reqs);
+ if (np->filemod_reqs)
+ free_modreqs (np->filemod_reqs);
+
+ assert (!np->sockaddr);
+
+ diskfs_node_norefs (np);
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+}
diff --git a/libdiskfs/node-make.c b/libdiskfs/node-make.c
new file mode 100644
index 00000000..ff0cc0d4
--- /dev/null
+++ b/libdiskfs/node-make.c
@@ -0,0 +1,75 @@
+/*
+ Copyright (C) 1994,95,96,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <fcntl.h>
+
+
+static struct node *
+init_node (struct node *np, struct disknode *dn)
+{
+ np->dn = dn;
+ np->dn_set_ctime = 0;
+ np->dn_set_atime = 0;
+ np->dn_set_mtime = 0;
+ np->dn_stat_dirty = 0;
+
+ pthread_mutex_init (&np->lock, NULL);
+ np->references = 1;
+ np->light_references = 0;
+ np->owner = 0;
+ np->sockaddr = MACH_PORT_NULL;
+
+ np->dirmod_reqs = 0;
+ np->dirmod_tick = 0;
+ np->filemod_reqs = 0;
+ np->filemod_tick = 0;
+
+ fshelp_transbox_init (&np->transbox, &np->lock, np);
+ iohelp_initialize_conch (&np->conch, &np->lock);
+ fshelp_lock_init (&np->userlock);
+
+ return np;
+}
+
+/* Create a and return new node structure with DN as its physical disknode.
+ The node will have one hard reference and no light references. */
+struct node *
+diskfs_make_node (struct disknode *dn)
+{
+ struct node *np = malloc (sizeof (struct node));
+
+ if (np == 0)
+ return 0;
+
+ return init_node (np, dn);
+}
+
+/* Create a new node structure. Also allocate SIZE bytes for the
+ disknode. The address of the disknode can be obtained using
+ diskfs_node_disknode. The new node will have one hard reference
+ and no light references. */
+struct node *
+diskfs_make_node_alloc (size_t size)
+{
+ struct node *np = malloc (sizeof (struct node) + size);
+
+ if (np == NULL)
+ return NULL;
+
+ return init_node (np, diskfs_node_disknode (np));
+}
diff --git a/libdiskfs/node-nput.c b/libdiskfs/node-nput.c
new file mode 100644
index 00000000..5043ad1a
--- /dev/null
+++ b/libdiskfs/node-nput.c
@@ -0,0 +1,81 @@
+/*
+ Copyright (C) 1999, 2001 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+/* Unlock node NP and release a hard reference; if this is the last
+ hard reference and there are no links to the file then request
+ weak references to be dropped. */
+void
+diskfs_nput (struct node *np)
+{
+ int tried_drop_softrefs = 0;
+
+ loop:
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ assert (np->references);
+ np->references--;
+ if (np->references + np->light_references == 0)
+ diskfs_drop_node (np);
+ else if (np->references == 0 && !tried_drop_softrefs)
+ {
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+
+ /* This is our cue that something akin to "last process closes file"
+ in the POSIX.1 sense happened, so make sure any pending node time
+ updates now happen in a timely fashion. */
+ diskfs_set_node_times (np);
+
+ diskfs_lost_hardrefs (np);
+ if (!np->dn_stat.st_nlink)
+ {
+ /* There are no links. If there are soft references that
+ can be dropped, we can't let them postpone deallocation.
+ So attempt to drop them. But that's a user-supplied
+ routine, which might result in further recursive calls to
+ the ref-counting system. So we have to reacquire our
+ reference around the call to forestall disaster. */
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ np->references++;
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+
+ if (np->sockaddr != MACH_PORT_NULL)
+ {
+ mach_port_deallocate (mach_task_self (), np->sockaddr);
+ np->sockaddr = MACH_PORT_NULL;
+ }
+
+ diskfs_try_dropping_softrefs (np);
+
+ /* But there's no value in looping forever in this
+ routine; only try to drop soft refs once. */
+ tried_drop_softrefs = 1;
+
+ /* Now we can drop the reference back... */
+ goto loop;
+ }
+ pthread_mutex_unlock (&np->lock);
+ }
+ else
+ {
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ pthread_mutex_unlock (&np->lock);
+ }
+}
diff --git a/libdiskfs/node-nputl.c b/libdiskfs/node-nputl.c
new file mode 100644
index 00000000..19596654
--- /dev/null
+++ b/libdiskfs/node-nputl.c
@@ -0,0 +1,38 @@
+/*
+ Copyright (C) 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include "priv.h"
+
+/* Unlock node NP and release a light reference */
+void
+diskfs_nput_light (struct node *np)
+{
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ assert (np->light_references);
+ np->light_references--;
+ if (np->references + np->light_references == 0)
+ diskfs_drop_node (np);
+ else
+ {
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ pthread_mutex_unlock (&np->lock);
+ }
+}
diff --git a/libdiskfs/node-nref.c b/libdiskfs/node-nref.c
new file mode 100644
index 00000000..13cea056
--- /dev/null
+++ b/libdiskfs/node-nref.c
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+/* Add a hard reference to a node. If there were no hard
+ references previously, then the node cannot be locked
+ (because you must hold a hard reference to hold the lock). */
+void
+diskfs_nref (struct node *np)
+{
+ int new_hardref;
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ np->references++;
+ new_hardref = (np->references == 1);
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ if (new_hardref)
+ {
+ pthread_mutex_lock (&np->lock);
+ diskfs_new_hardrefs (np);
+ pthread_mutex_unlock (&np->lock);
+ }
+}
diff --git a/libdiskfs/node-nrefl.c b/libdiskfs/node-nrefl.c
new file mode 100644
index 00000000..96922471
--- /dev/null
+++ b/libdiskfs/node-nrefl.c
@@ -0,0 +1,30 @@
+/*
+ Copyright (C) 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+/* Add a light reference to a node. */
+void
+diskfs_nref_light (struct node *np)
+{
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ np->light_references++;
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+}
diff --git a/libdiskfs/node-nrele.c b/libdiskfs/node-nrele.c
new file mode 100644
index 00000000..cc680893
--- /dev/null
+++ b/libdiskfs/node-nrele.c
@@ -0,0 +1,65 @@
+/*
+ Copyright (C) 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+/* Release a hard reference on NP. If NP is locked by anyone, then
+ this cannot be the last hard reference (because you must hold a
+ hard reference in order to hold the lock). If this is the last
+ hard reference and there are no links, then request soft references
+ to be dropped. */
+void
+diskfs_nrele (struct node *np)
+{
+ int tried_drop_softrefs = 0;
+
+ loop:
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ assert (np->references);
+ np->references--;
+ if (np->references + np->light_references == 0)
+ {
+ pthread_mutex_lock (&np->lock);
+ diskfs_drop_node (np);
+ }
+ else if (np->references == 0)
+ {
+ pthread_mutex_lock (&np->lock);
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ diskfs_lost_hardrefs (np);
+ if (!np->dn_stat.st_nlink && !tried_drop_softrefs)
+ {
+ /* Same issue here as in nput; see that for explanation */
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ np->references++;
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+
+ diskfs_try_dropping_softrefs (np);
+ tried_drop_softrefs = 1;
+
+ /* Now we can drop the reference back... */
+ pthread_mutex_unlock (&np->lock);
+ goto loop;
+ }
+ pthread_mutex_unlock (&np->lock);
+ }
+ else
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+}
diff --git a/libdiskfs/node-nrelel.c b/libdiskfs/node-nrelel.c
new file mode 100644
index 00000000..ee53b227
--- /dev/null
+++ b/libdiskfs/node-nrelel.c
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+/* Release a light reference on NP. If NP is locked by anyone, then
+ this cannot be the last reference (because you must hold a
+ hard reference in order to hold the lock). */
+void
+diskfs_nrele_light (struct node *np)
+{
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ assert (np->light_references);
+ np->light_references--;
+ if (np->references + np->light_references == 0)
+ {
+ pthread_mutex_lock (&np->lock);
+ diskfs_drop_node (np);
+ }
+ else
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+}
diff --git a/libdiskfs/node-rdwr.c b/libdiskfs/node-rdwr.c
new file mode 100644
index 00000000..ed94df44
--- /dev/null
+++ b/libdiskfs/node-rdwr.c
@@ -0,0 +1,77 @@
+/*
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* Reading and writing of files. this is called by other filesystem
+ routines and handles extension of files automatically. NP is the
+ node to be read or written, and must be locked. DATA will be
+ written or filled. OFF identifies where in thi fel the I/O is to
+ take place (-1 is not allowed). AMT is the size of DATA and tells
+ how much to copy. DIR is 1 for writing and 0 for reading. CRED is
+ the user doing the access (only used to validate attempted file
+ extension). For reads, *AMTREAD is filled with the amount actually
+ read. */
+error_t
+diskfs_node_rdwr (struct node *np,
+ char *data,
+ off_t off,
+ size_t amt,
+ int dir,
+ struct protid *cred,
+ size_t *amtread)
+{
+ error_t err;
+
+ iohelp_get_conch (&np->conch);
+
+ if (dir)
+ while (off + amt > np->allocsize)
+ {
+ err = diskfs_grow (np, off + amt, cred);
+ if (err)
+ return err;
+ if (np->filemod_reqs)
+ diskfs_notice_filechange (np, FILE_CHANGED_EXTEND, 0, off + amt);
+ }
+
+ if (off + amt > np->dn_stat.st_size)
+ {
+ if (dir)
+ {
+ np->dn_stat.st_size = off + amt;
+ np->dn_set_ctime = 1;
+ }
+ else
+ amt = np->dn_stat.st_size - off;
+ }
+
+ if (amtread)
+ *amtread = amt;
+ else
+ amtread = &amt;
+ err = _diskfs_rdwr_internal (np, data, off, amtread, dir, 0);
+ if (*amtread && diskfs_synchronous)
+ {
+ if (dir)
+ diskfs_file_update (np, 1);
+ else
+ diskfs_node_update (np, 1);
+ }
+
+ return err;
+}
diff --git a/libdiskfs/node-times.c b/libdiskfs/node-times.c
new file mode 100644
index 00000000..67f0142e
--- /dev/null
+++ b/libdiskfs/node-times.c
@@ -0,0 +1,74 @@
+/* Process st_?tim updates marked for a diskfs node.
+
+ Copyright (C) 1994, 1996, 1999, 2000, 2007, 2009 Free Software Foundation,
+ Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include <maptime.h>
+
+/* If disk is not readonly and the noatime option is not enabled, set
+ NP->dn_set_atime. */
+void
+diskfs_set_node_atime (struct node *np)
+{
+ if (!_diskfs_noatime && !diskfs_check_readonly ())
+ np->dn_set_atime = 1;
+}
+
+/* If NP->dn_set_ctime is set, then modify NP->dn_stat.st_ctim
+ appropriately; do the analogous operation for atime and mtime as well. */
+void
+diskfs_set_node_times (struct node *np)
+{
+ struct timeval t;
+
+ if (!np->dn_set_mtime && !np->dn_set_atime && !np->dn_set_ctime)
+ return;
+
+ maptime_read (diskfs_mtime, &t);
+
+ /* We are careful to test and reset each of these individually, so there
+ is no race condition where a dn_set_?time flag setting gets lost. It
+ is not a problem to have the kind of race where the flag is set after
+ we've tested it and done nothing--as long as the flag remains set so
+ the update will happen at the next call. */
+ if (np->dn_set_mtime)
+ {
+ np->dn_stat.st_mtim.tv_sec = t.tv_sec;
+ np->dn_stat.st_mtim.tv_nsec = t.tv_usec * 1000;
+ np->dn_stat_dirty = 1;
+ np->dn_set_mtime = 0;
+ }
+ if (np->dn_set_atime)
+ {
+ np->dn_stat.st_atim.tv_sec = t.tv_sec;
+ np->dn_stat.st_atim.tv_nsec = t.tv_usec * 1000;
+ np->dn_stat_dirty = 1;
+ np->dn_set_atime = 0;
+ }
+ if (np->dn_set_ctime)
+ {
+ np->dn_stat.st_ctim.tv_sec = t.tv_sec;
+ np->dn_stat.st_ctim.tv_nsec = t.tv_usec * 1000;
+ np->dn_stat_dirty = 1;
+ np->dn_set_ctime = 0;
+ }
+}
diff --git a/libdiskfs/node-update.c b/libdiskfs/node-update.c
new file mode 100644
index 00000000..453dea59
--- /dev/null
+++ b/libdiskfs/node-update.c
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 1993, 1994 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+
+/* Set on disk fields from NP->dn_stat; update ctime, atime, and mtime
+ if necessary. If WAIT is true, then return only after the physical
+ media has been completely updated. */
+void diskfs_node_update (struct node *np, int wait)
+{
+ diskfs_set_node_times (np);
+ if (np->dn_stat_dirty)
+ diskfs_write_disknode (np, wait);
+}
diff --git a/libdiskfs/opts-append-std.c b/libdiskfs/opts-append-std.c
new file mode 100644
index 00000000..b951bf93
--- /dev/null
+++ b/libdiskfs/opts-append-std.c
@@ -0,0 +1,66 @@
+/* Get standard diskfs run-time options
+
+ Copyright (C) 1995, 96,97,98,99,2002 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <argz.h>
+
+#include "priv.h"
+
+error_t
+diskfs_append_std_options (char **argz, size_t *argz_len)
+{
+ error_t err;
+ extern int diskfs_sync_interval;
+
+ if (diskfs_readonly)
+ err = argz_add (argz, argz_len, "--readonly");
+ else
+ err = argz_add (argz, argz_len, "--writable");
+
+ if (!err && _diskfs_nosuid)
+ err = argz_add (argz, argz_len, "--no-suid");
+ if (!err && _diskfs_noexec)
+ err = argz_add (argz, argz_len, "--no-exec");
+ if (!err && _diskfs_noatime)
+ err = argz_add (argz, argz_len, "--no-atime");
+ if (!err && _diskfs_no_inherit_dir_group)
+ err = argz_add (argz, argz_len, "--no-inherit-dir-group");
+
+ if (! err)
+ {
+ if (diskfs_synchronous)
+ err = argz_add (argz, argz_len, "--sync");
+ else if (DEFAULT_SYNC_INTERVAL != diskfs_sync_interval)
+ {
+ if (diskfs_sync_interval == 0)
+ err = argz_add (argz, argz_len, "--no-sync");
+ else
+ {
+ char buf[80];
+ sprintf (buf, "--sync=%d", diskfs_sync_interval);
+ err = argz_add (argz, argz_len, buf);
+ }
+ }
+ }
+
+ return err;
+}
diff --git a/libdiskfs/opts-common.c b/libdiskfs/opts-common.c
new file mode 100644
index 00000000..d37c2868
--- /dev/null
+++ b/libdiskfs/opts-common.c
@@ -0,0 +1,59 @@
+/* Options common to both startup and runtime
+
+ Copyright (C) 1995, 1996, 1997, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <argp.h>
+#include "priv.h"
+
+const struct argp_option diskfs_common_options[] =
+{
+ {"readonly", 'r', 0, 0, "Never write to disk or allow opens for writing"},
+ {"rdonly", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"ro", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"writable", 'w', 0, 0, "Use normal read/write behavior"},
+ {"rdwr", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"rw", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"sync", 's', "INTERVAL", OPTION_ARG_OPTIONAL,
+ "If INTERVAL is supplied, sync all data not actually written to disk"
+ " every INTERVAL seconds, otherwise operate in synchronous mode (the"
+ " default is to sync every " DEFAULT_SYNC_INTERVAL_STRING " seconds)"},
+ {"no-sync", 'n', 0, 0, "Don't automatically sync data to disk"},
+ {"nosync", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"no-suid", 'S', 0, 0, "Don't permit set-uid or set-gid execution"},
+ {"nosuid", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"suid-ok", OPT_SUID_OK, 0, 0, "Enable set-uid execution"},
+ {"no-exec", 'E', 0, 0, "Don't permit any execution of files on this filesystem"},
+ {"noexec", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"exec-ok", OPT_EXEC_OK, 0, 0, "Enable execution of files"},
+ {"no-atime", 'A', 0, 0,
+ "Do not update file access times on disk for reads"},
+ {"noatime", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"atime", OPT_ATIME, 0, 0, "Do update file access times for reads normally"},
+ {"no-inherit-dir-group", OPT_NO_INHERIT_DIR_GROUP, 0, 0,
+ "Create new nodes with gid of the process"},
+ {"nogrpid", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"sysvgroups", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"inherit-dir-group", OPT_INHERIT_DIR_GROUP, 0, 0,
+ "Create new nodes with gid of parent dir (default)"},
+ {"grpid", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"bsdgroups", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {0, 0}
+};
diff --git a/libdiskfs/opts-get.c b/libdiskfs/opts-get.c
new file mode 100644
index 00000000..c23e4bec
--- /dev/null
+++ b/libdiskfs/opts-get.c
@@ -0,0 +1,28 @@
+/* Get run-time options
+
+ Copyright (C) 1995,96,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+error_t
+diskfs_append_args (char **argz, size_t *argz_len)
+{
+ return diskfs_append_std_options (argz, argz_len);
+}
diff --git a/libdiskfs/opts-runtime.c b/libdiskfs/opts-runtime.c
new file mode 100644
index 00000000..f0d1a382
--- /dev/null
+++ b/libdiskfs/opts-runtime.c
@@ -0,0 +1,24 @@
+/* Default definition for diskfs_runtime_argp
+
+ Copyright (C) 1996, 2004 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <argp.h>
+
+struct argp *diskfs_runtime_argp = (struct argp *)&diskfs_std_runtime_argp;
diff --git a/libdiskfs/opts-set.c b/libdiskfs/opts-set.c
new file mode 100644
index 00000000..3cfe3f6b
--- /dev/null
+++ b/libdiskfs/opts-set.c
@@ -0,0 +1,34 @@
+/* Set run-time options
+
+ Copyright (C) 1996, 1998 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd/fshelp.h>
+
+#include "priv.h"
+
+error_t
+diskfs_set_options (const char *argz, size_t argz_len)
+{
+ if (diskfs_runtime_argp)
+ return fshelp_set_options (diskfs_runtime_argp, 0, argz, argz_len, 0);
+ else
+ return EOPNOTSUPP;
+}
diff --git a/libdiskfs/opts-std-runtime.c b/libdiskfs/opts-std-runtime.c
new file mode 100644
index 00000000..177dfaf6
--- /dev/null
+++ b/libdiskfs/opts-std-runtime.c
@@ -0,0 +1,161 @@
+/* Parse standard run-time options
+
+ Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <argp.h>
+
+#include "priv.h"
+
+static const struct argp_option
+std_runtime_options[] =
+{
+ {"update", 'u', 0, 0, "Flush any meta-data cached in core"},
+ {"remount", 0, 0, OPTION_HIDDEN | OPTION_ALIAS}, /* deprecated */
+ {0, 0}
+};
+
+struct parse_hook
+{
+ int readonly, sync, sync_interval, remount, nosuid, noexec, noatime,
+ noinheritdirgroup;
+};
+
+/* Implement the options in H, and free H. */
+static error_t
+set_opts (struct parse_hook *h)
+{
+ error_t err = 0;
+
+ /* Do things in this order: remount, change readonly, change-sync; always
+ do the remount while the disk is readonly, even if only temporarily. */
+
+ if (h->remount)
+ {
+ /* We can only remount while readonly. */
+ err = diskfs_set_readonly (1);
+ if (!err)
+ err = diskfs_remount ();
+ }
+
+ if (h->readonly != diskfs_readonly)
+ {
+ if (err)
+ diskfs_set_readonly (h->readonly); /* keep the old error. */
+ else
+ err = diskfs_set_readonly (h->readonly);
+ }
+
+ /* Change sync mode. */
+ if (h->sync)
+ {
+ diskfs_synchronous = 1;
+ diskfs_set_sync_interval (0); /* Don't waste time syncing. */
+ }
+ else
+ {
+ diskfs_synchronous = 0;
+ if (h->sync_interval >= 0)
+ diskfs_set_sync_interval (h->sync_interval);
+ }
+
+ if (h->nosuid != -1)
+ _diskfs_nosuid = h->nosuid;
+ if (h->noexec != -1)
+ _diskfs_noexec = h->noexec;
+ if (h->noatime != -1)
+ _diskfs_noatime = h->noatime;
+ if (h->noinheritdirgroup != -1)
+ _diskfs_no_inherit_dir_group = h->noinheritdirgroup;
+
+ free (h);
+
+ return err;
+}
+
+/* Parse diskfs standard runtime options. */
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ struct parse_hook *h = state->hook;
+ switch (opt)
+ {
+ case 'r': h->readonly = 1; break;
+ case 'w': h->readonly = 0; break;
+ case 'u': h->remount = 1; break;
+ case 'S': h->nosuid = 1; break;
+ case 'E': h->noexec = 1; break;
+ case 'A': h->noatime = 1; break;
+ case OPT_SUID_OK: h->nosuid = 0; break;
+ case OPT_EXEC_OK: h->noexec = 0; break;
+ case OPT_ATIME: h->noatime = 0; break;
+ case OPT_NO_INHERIT_DIR_GROUP: h->noinheritdirgroup = 1; break;
+ case OPT_INHERIT_DIR_GROUP: h->noinheritdirgroup = 0; break;
+ case 'n': h->sync_interval = 0; h->sync = 0; break;
+ case 's':
+ if (arg)
+ {
+ h->sync = 0;
+ h->sync_interval = atoi (arg);
+ }
+ else
+ h->sync = 1;
+ break;
+
+ case ARGP_KEY_INIT:
+ if (state->input)
+ state->hook = state->input; /* Share hook with parent. */
+ else
+ {
+ h = state->hook = malloc (sizeof (struct parse_hook));
+ if (! h)
+ return ENOMEM;
+ h->readonly = diskfs_readonly;
+ h->sync = diskfs_synchronous;
+ h->sync_interval = -1;
+ h->remount = 0;
+ h->nosuid = h->noexec = h->noatime = h->noinheritdirgroup = -1;
+
+ /* We know that we have one child, with which we share our hook. */
+ state->child_inputs[0] = h;
+ }
+ break;
+
+ case ARGP_KEY_ERROR:
+ if (! state->input)
+ free (h);
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ if (! state->input)
+ return set_opts (h);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static const struct argp common_argp = { diskfs_common_options, parse_opt };
+
+static const struct argp_child children[] = { {&common_argp}, {0} };
+const struct argp diskfs_std_runtime_argp =
+{
+ std_runtime_options, parse_opt, 0, 0, children
+};
diff --git a/libdiskfs/opts-std-startup.c b/libdiskfs/opts-std-startup.c
new file mode 100644
index 00000000..6fe28758
--- /dev/null
+++ b/libdiskfs/opts-std-startup.c
@@ -0,0 +1,178 @@
+/* Standard startup-time command line parser
+
+ Copyright (C) 1995, 1996, 1997, 1998, 1999, 2001, 2007
+ Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <argp.h>
+#include <hurd/store.h>
+#include <hurd/paths.h>
+#include "priv.h"
+
+const char *diskfs_boot_command_line;
+char **_diskfs_boot_command;
+
+int _diskfs_boot_pause;
+
+extern char **diskfs_argv;
+
+mach_port_t diskfs_exec_server_task = MACH_PORT_NULL;
+
+/* ---------------------------------------------------------------- */
+
+#define OPT_HOST_PRIV_PORT (-1)
+#define OPT_DEVICE_MASTER_PORT (-2)
+#define OPT_EXEC_SERVER_TASK (-3)
+#define OPT_BOOT_CMDLINE (-4)
+#define OPT_BOOT_COMMAND (-5)
+#define OPT_BOOT_INIT_PROGRAM (-6)
+#define OPT_BOOT_PAUSE (-7)
+
+static const struct argp_option
+startup_options[] =
+{
+ {"directory", 'C', "DIRECTORY", 0,
+ "Use DIRECTORY as the root of the filesystem"},
+ {"virtual-root", 0, 0, OPTION_ALIAS},
+ {"chroot", 0, 0, OPTION_ALIAS},
+
+ {0,0,0,0, "Boot options:", -2},
+ {"multiboot-command-line", OPT_BOOT_CMDLINE, "ARGS", 0,
+ "Required for bootstrap filesystem, the multiboot kernel command line"},
+ {"bootflags", 0, 0, OPTION_ALIAS|OPTION_HIDDEN},
+ {"boot-init-program", OPT_BOOT_INIT_PROGRAM, "FILE", 0,
+ "For bootstrap filesystem, init program to run (default " _HURD_INIT ")"},
+ {"boot-debug-pause", OPT_BOOT_PAUSE, 0, 0,
+ "Pause for keystroke before starting bootstrap programs"},
+ {"boot-command", OPT_BOOT_COMMAND, 0, 0,
+ "Remaining arguments form command line to run"
+ " at bootstrap instead of init"},
+ {"host-priv-port", OPT_HOST_PRIV_PORT, "PORT"},
+ {"device-master-port", OPT_DEVICE_MASTER_PORT, "PORT"},
+ {"exec-server-task", OPT_EXEC_SERVER_TASK, "PORT"},
+
+ {0}
+};
+
+static error_t
+parse_startup_opt (int opt, char *arg, struct argp_state *state)
+{
+ switch (opt)
+ {
+#define TOGGLE(var, on, off) \
+ case on: var = 1; break; \
+ case off: var = 0; break;
+ TOGGLE (diskfs_readonly, 'r', 'w');
+ TOGGLE (_diskfs_nosuid, 'S', OPT_SUID_OK);
+ TOGGLE (_diskfs_noexec, 'E', OPT_EXEC_OK);
+ TOGGLE (_diskfs_noatime, 'A', OPT_ATIME);
+ TOGGLE (_diskfs_no_inherit_dir_group, OPT_NO_INHERIT_DIR_GROUP,
+ OPT_INHERIT_DIR_GROUP);
+#undef TOGGLE
+
+ case 's':
+ if (arg == NULL)
+ diskfs_synchronous = 1;
+ else
+ diskfs_default_sync_interval = atoi (arg);
+ break;
+ case 'n':
+ diskfs_synchronous = 0;
+ diskfs_default_sync_interval = 0;
+ break;
+
+ /* Boot options */
+ case OPT_DEVICE_MASTER_PORT:
+ _hurd_device_master = atoi (arg); break;
+ case OPT_HOST_PRIV_PORT:
+ _hurd_host_priv = atoi (arg); break;
+ case OPT_EXEC_SERVER_TASK:
+ diskfs_exec_server_task = atoi (arg); break;
+ case OPT_BOOT_CMDLINE:
+ diskfs_boot_command_line = arg; break;
+ case OPT_BOOT_INIT_PROGRAM:
+ diskfs_boot_init_program = arg; break;
+ case OPT_BOOT_PAUSE:
+ _diskfs_boot_pause = 1; break;
+ case 'C':
+ _diskfs_chroot_directory = arg; break;
+
+ case OPT_BOOT_COMMAND:
+ if (state->next == state->argc)
+ argp_error (state, "Command line must follow --boot-command option");
+ _diskfs_boot_command = state->argv + state->next;
+ state->next = state->argc; /* stop parsing */
+ {char **p; for (p = _diskfs_boot_command; *p; ++p) printf("BC %s\n",*p);}
+ break;
+
+ case ARGP_KEY_END:
+ diskfs_argv = state->argv; break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+/* Suck in the common arguments. */
+static const struct argp startup_common_argp =
+ { diskfs_common_options, parse_startup_opt };
+static const struct argp_child startup_argp_children[] =
+ { {&startup_common_argp}, {0} };
+
+/* This may be used with argp_parse to parse standard diskfs startup
+ options, possible chained onto the end of a user argp structure. */
+const struct argp
+diskfs_startup_argp =
+{
+ startup_options, parse_startup_opt, 0, 0, startup_argp_children
+};
+
+static error_t
+parse_store_startup_opt (int opt, char *arg, struct argp_state *state)
+{
+ switch (opt)
+ {
+ case ARGP_KEY_INIT:
+ /* Propagate our input to our STORE_ARGP child , which it will use to
+ return what it parses. */
+ state->child_inputs[1] = state->input; break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static const struct argp_child store_argp_children[] =
+ { {&diskfs_startup_argp}, {&store_argp}, {0} };
+
+/* An argp structure for the standard diskfs command line arguments plus a
+ store specification. The address of a location in which to return the
+ resulting struct store_parsed structure should be passed as the input
+ argument to argp_parse; see the declaration for STORE_ARGP in
+ <hurd/store.h> for more information. */
+const struct argp
+diskfs_store_startup_argp =
+{
+ 0, parse_store_startup_opt, 0, 0, store_argp_children
+};
diff --git a/libdiskfs/opts-version.c b/libdiskfs/opts-version.c
new file mode 100644
index 00000000..c26334b2
--- /dev/null
+++ b/libdiskfs/opts-version.c
@@ -0,0 +1,47 @@
+/* Default hook for argp --version handling
+
+ Copyright (C) 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <argp.h>
+#include <version.h>
+
+#include "priv.h"
+
+static void
+_print_version (FILE *stream, struct argp_state *state)
+{
+ if (argp_program_version)
+ /* If this is non-zero, then the program's probably defined it, so let
+ that take precedence over the default. */
+ fputs (argp_program_version, stream);
+ else if (diskfs_extra_version && *diskfs_extra_version)
+ fprintf (stream, "%s (%s) %s\n",
+ diskfs_server_name, diskfs_extra_version, diskfs_server_version);
+ else
+ fprintf (stream, "%s %s\n", diskfs_server_name, diskfs_server_version);
+
+ /* And because diskfs is big and huge, put our information out too. */
+ fputs (STANDARD_HURD_VERSION (libdiskfs) "\n", stream);
+}
+
+void (*argp_program_version_hook) (FILE *stream, struct argp_state *state)
+ = _print_version;
diff --git a/libdiskfs/peropen-make.c b/libdiskfs/peropen-make.c
new file mode 100644
index 00000000..6d5ca014
--- /dev/null
+++ b/libdiskfs/peropen-make.c
@@ -0,0 +1,72 @@
+/*
+ Copyright (C) 1994,97,99,2001,02 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <sys/file.h>
+
+/* Create and return a new peropen structure on node NP with open
+ flags FLAGS. */
+error_t
+diskfs_make_peropen (struct node *np, int flags, struct peropen *context,
+ struct peropen **ppo)
+{
+ struct peropen *po = *ppo = malloc (sizeof (struct peropen));
+
+ if (! po)
+ return ENOMEM;
+
+ po->filepointer = 0;
+ po->lock_status = LOCK_UN;
+ refcount_init (&po->refcnt, 0);
+ po->openstat = flags;
+ po->np = np;
+ po->path = NULL;
+
+ if (context)
+ {
+ if (context->path)
+ {
+ po->path = strdup (context->path);
+ if (! po->path)
+ return ENOMEM;
+ }
+
+ po->root_parent = context->root_parent;
+ if (po->root_parent != MACH_PORT_NULL)
+ mach_port_mod_refs (mach_task_self (), po->root_parent,
+ MACH_PORT_RIGHT_SEND, 1);
+
+ po->shadow_root = context->shadow_root;
+ if (po->shadow_root)
+ diskfs_nref (po->shadow_root);
+
+ po->shadow_root_parent = context->shadow_root_parent;
+ if (po->shadow_root_parent != MACH_PORT_NULL)
+ mach_port_mod_refs (mach_task_self (), po->shadow_root_parent,
+ MACH_PORT_RIGHT_SEND, 1);
+ }
+ else
+ {
+ po->root_parent = MACH_PORT_NULL;
+ po->shadow_root_parent = MACH_PORT_NULL;
+ po->shadow_root = _diskfs_chroot_directory ? diskfs_root_node : 0;
+ }
+
+ diskfs_nref (np);
+
+ return 0;
+}
diff --git a/libdiskfs/peropen-rele.c b/libdiskfs/peropen-rele.c
new file mode 100644
index 00000000..877137bb
--- /dev/null
+++ b/libdiskfs/peropen-rele.c
@@ -0,0 +1,49 @@
+/*
+ Copyright (C) 1994, 1996, 1997 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/file.h>
+#include "priv.h"
+
+/* Decrement the reference count on a peropen structure. */
+void
+diskfs_release_peropen (struct peropen *po)
+{
+ if (refcount_deref (&po->refcnt) > 0)
+ return;
+
+ if (po->root_parent)
+ mach_port_deallocate (mach_task_self (), po->root_parent);
+
+ if (po->shadow_root && po->shadow_root != po->np)
+ diskfs_nrele (po->shadow_root);
+
+ if (po->shadow_root_parent)
+ mach_port_deallocate (mach_task_self (), po->shadow_root_parent);
+
+ if (po->lock_status != LOCK_UN)
+ {
+ pthread_mutex_lock (&po->np->lock);
+ fshelp_acquire_lock (&po->np->userlock, &po->lock_status,
+ &po->np->lock, LOCK_UN);
+ diskfs_nput (po->np);
+ }
+ else
+ diskfs_nrele (po->np);
+
+ free (po->path);
+ free (po);
+}
diff --git a/libdiskfs/priv.h b/libdiskfs/priv.h
new file mode 100644
index 00000000..2ac3c9ef
--- /dev/null
+++ b/libdiskfs/priv.h
@@ -0,0 +1,141 @@
+/* Private declarations for fileserver library
+
+ Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2001, 2006, 2009 Free
+ Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef DISKFS_PRIV_H
+#define DISKFS_PRIV_H
+
+#include <mach.h>
+#include <hurd.h>
+#include <sys/mman.h>
+#include <hurd/ports.h>
+#include <hurd/fshelp.h>
+#include <hurd/iohelp.h>
+#include <hurd/port.h>
+#include <assert.h>
+#include <argp.h>
+
+#include "diskfs.h"
+
+/* These inhibit setuid or exec. */
+extern int _diskfs_nosuid, _diskfs_noexec;
+
+/* This relaxes the requirement to set `st_atim'. */
+extern int _diskfs_noatime;
+
+/* This enables SysV style group behaviour. New nodes inherit the GID
+ of the user creating them unless the SGID bit is set of the parent
+ directory. */
+extern int _diskfs_no_inherit_dir_group;
+
+/* This is the -C argument value. */
+extern char *_diskfs_chroot_directory;
+
+/* If --boot-command is given, this points to the program and args. */
+extern char **_diskfs_boot_command;
+
+/* Port cell holding a cached port to the exec server. */
+extern struct hurd_port _diskfs_exec_portcell;
+
+volatile struct mapped_time_value *_diskfs_mtime;
+
+extern const struct argp_option diskfs_common_options[];
+/* Option keys for long-only options in diskfs_common_options. */
+#define OPT_SUID_OK 600 /* --suid-ok */
+#define OPT_EXEC_OK 601 /* --exec-ok */
+#define OPT_ATIME 602 /* --atime */
+#define OPT_NO_INHERIT_DIR_GROUP 603 /* --no-inherit-dir-group */
+#define OPT_INHERIT_DIR_GROUP 604 /* --inherit-dir-group */
+
+/* Common value for diskfs_common_options and diskfs_default_sync_interval. */
+#define DEFAULT_SYNC_INTERVAL 30
+#define DEFAULT_SYNC_INTERVAL_STRING STRINGIFY(DEFAULT_SYNC_INTERVAL)
+#define STRINGIFY(x) STRINGIFY_1(x)
+#define STRINGIFY_1(x) #x
+
+/* Diskfs thinks the disk is dirty if this is set. */
+extern int _diskfs_diskdirty;
+
+/* Needed for MiG. */
+typedef struct protid *protid_t;
+typedef struct diskfs_control *control_t;
+typedef struct bootinfo *bootinfo_t;
+
+/* Actually read or write a file. The file size must already permit
+ the requested access. NP is the file to read/write. DATA is a buffer
+ to write from or fill on read. OFFSET is the absolute address (-1
+ not permitted here); AMT is the size of the read/write to perform;
+ DIR is set for writing and clear for reading. The inode must
+ be locked. If NOTIME is set, then don't update the access or
+ modify times on the file. */
+error_t _diskfs_rdwr_internal (struct node *np, char *data, off_t offset,
+ size_t *amt, int dir, int notime);
+
+/* Called when we have a real user environment (complete with proc
+ and auth ports). */
+void _diskfs_init_completed (void);
+
+/* Called in a bootstrap filesystem only, to get the privileged ports. */
+void _diskfs_boot_privports (void);
+
+/* Clean routine for control port. */
+void _diskfs_control_clean (void *);
+
+/* Number of outstanding PT_CTL ports. */
+extern int _diskfs_ncontrol_ports;
+
+/* Lock for _diskfs_ncontrol_ports. */
+extern pthread_spinlock_t _diskfs_control_lock;
+
+/* Callback routines for active translator startup */
+extern fshelp_fetch_root_callback1_t _diskfs_translator_callback1;
+extern fshelp_fetch_root_callback2_t _diskfs_translator_callback2;
+
+/* This macro locks the node associated with PROTID, and then
+ evaluates the expression OPERATION; then it syncs the inode
+ (without waiting) and unlocks everything, and then returns
+ the value `err' (which can be set by OPERATION if desired). */
+#define CHANGE_NODE_FIELD(PROTID, OPERATION) \
+({ \
+ error_t err = 0; \
+ struct node *np; \
+ \
+ if (!(PROTID)) \
+ return EOPNOTSUPP; \
+ \
+ if (diskfs_check_readonly ()) \
+ return EROFS; \
+ \
+ np = (PROTID)->po->np; \
+ \
+ pthread_mutex_lock (&np->lock); \
+ (OPERATION); \
+ if (diskfs_synchronous) \
+ diskfs_node_update (np, 1); \
+ pthread_mutex_unlock (&np->lock); \
+ return err; \
+})
+
+/* Bits the user is permitted to set with io_*_openmodes */
+#define HONORED_STATE_MODES (O_APPEND|O_ASYNC|O_FSYNC|O_NONBLOCK|O_NOATIME)
+
+/* Bits that are turned off after open */
+#define OPENONLY_STATE_MODES \
+ (O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS|O_NONBLOCK|O_EXLOCK|O_SHLOCK)
+
+#endif
diff --git a/libdiskfs/protid-make.c b/libdiskfs/protid-make.c
new file mode 100644
index 00000000..22aaa2e1
--- /dev/null
+++ b/libdiskfs/protid-make.c
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) 1994,95,96,2001 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <string.h>
+#include <assert.h>
+
+/* Build and return in CRED a protid which has no user identification, for
+ peropen PO. */
+error_t
+diskfs_start_protid (struct peropen *po, struct protid **cred)
+{
+ error_t err =
+ ports_create_port_noinstall (diskfs_protid_class, diskfs_port_bucket,
+ sizeof (struct protid), cred);
+ if (! err)
+ {
+ refcount_ref (&po->refcnt);
+ (*cred)->po = po;
+ (*cred)->shared_object = MACH_PORT_NULL;
+ (*cred)->mapped = 0;
+ }
+ return err;
+}
+
+/* Finish building protid CRED started with diskfs_start_protid;
+ the user to install is USER. */
+void
+diskfs_finish_protid (struct protid *cred, struct iouser *user)
+{
+ error_t err;
+
+ if (!user)
+ err = iohelp_create_simple_iouser (&cred->user, 0, 0);
+ else
+ err = iohelp_dup_iouser (&cred->user, user);
+ assert_perror (err);
+
+ err = mach_port_move_member (mach_task_self (), cred->pi.port_right,
+ diskfs_port_bucket->portset);
+ assert_perror (err);
+}
+
+/* Create and return a protid for an existing peropen PO in CRED for
+ USER. */
+error_t
+diskfs_create_protid (struct peropen *po, struct iouser *user,
+ struct protid **cred)
+{
+ error_t err = diskfs_start_protid (po, cred);
+ if (! err)
+ diskfs_finish_protid (*cred, user);
+ return err;
+}
diff --git a/libdiskfs/protid-rele.c b/libdiskfs/protid-rele.c
new file mode 100644
index 00000000..433b8c9f
--- /dev/null
+++ b/libdiskfs/protid-rele.c
@@ -0,0 +1,35 @@
+/*
+ Copyright (C) 1994, 1999 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* Called when a protid CRED has no more references. (Because references
+ to protids are maintained by the port management library, this is
+ installed in the clean routines list.) The ports library will
+ free the structure for us. */
+void
+diskfs_protid_rele (void *arg)
+{
+ struct protid *cred = arg;
+
+ iohelp_free_iouser (cred->user);
+ if (cred->shared_object)
+ mach_port_deallocate (mach_task_self (), cred->shared_object);
+ if (cred->mapped)
+ munmap (cred->mapped, vm_page_size);
+ diskfs_release_peropen (cred->po);
+}
diff --git a/libdiskfs/rdwr-internal.c b/libdiskfs/rdwr-internal.c
new file mode 100644
index 00000000..18a4ae1e
--- /dev/null
+++ b/libdiskfs/rdwr-internal.c
@@ -0,0 +1,74 @@
+/*
+ Copyright (C) 1994,95,96,97,99,2000 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <string.h>
+#include <fcntl.h>
+#include <hurd/pager.h>
+
+/* Actually read or write a file. The file size must already permit
+ the requested access. NP is the file to read/write. DATA is a buffer
+ to write from or fill on read. OFFSET is the absolute address (-1
+ not permitted here); AMT is the size of the read/write to perform;
+ DIR is set for writing and clear for reading. The inode must
+ be locked. If NOTIME is set, then don't update the mtime or atime. */
+error_t
+_diskfs_rdwr_internal (struct node *np,
+ char *data,
+ off_t offset,
+ size_t *amt,
+ int dir,
+ int notime)
+{
+ memory_object_t memobj;
+ vm_prot_t prot = dir ? (VM_PROT_READ | VM_PROT_WRITE) : VM_PROT_READ;
+ error_t err = 0;
+
+ if (dir)
+ assert (!diskfs_readonly);
+
+ if (*amt == 0)
+ /* Zero-length writes do not update mtime or anything else, by POSIX. */
+ return 0;
+
+ if (!diskfs_check_readonly () && !notime)
+ {
+ if (dir)
+ np->dn_set_mtime = 1;
+ else if (! _diskfs_noatime)
+ np->dn_set_atime = 1;
+ }
+
+ memobj = diskfs_get_filemap (np, prot);
+
+ if (memobj == MACH_PORT_NULL)
+ return errno;
+
+ err = pager_memcpy (diskfs_get_filemap_pager_struct (np), memobj,
+ offset, data, amt, prot);
+
+ if (!diskfs_check_readonly () && !notime)
+ {
+ if (dir)
+ np->dn_set_mtime = 1;
+ else if (!_diskfs_noatime)
+ np->dn_set_atime = 1;
+ }
+
+ mach_port_deallocate (mach_task_self (), memobj);
+ return err;
+}
diff --git a/libdiskfs/readonly-changed.c b/libdiskfs/readonly-changed.c
new file mode 100644
index 00000000..44ee9225
--- /dev/null
+++ b/libdiskfs/readonly-changed.c
@@ -0,0 +1,31 @@
+/* Default hook for change to/from read-only
+
+ Copyright (C) 2001 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "diskfs.h"
+
+/* The user may define this function. It is called when the disk has been
+ changed from read-only to read-write mode or vice-versa. READONLY is the
+ new state (which is also reflected in DISKFS_READONLY). This function is
+ also called during initial startup if the filesystem is to be writable. */
+void
+diskfs_readonly_changed (int readonly)
+{
+ /* By default do nothing at all. */
+}
diff --git a/libdiskfs/readonly.c b/libdiskfs/readonly.c
new file mode 100644
index 00000000..02f583c8
--- /dev/null
+++ b/libdiskfs/readonly.c
@@ -0,0 +1,111 @@
+/* Change to/from read-only
+
+ Copyright (C) 1995, 1996, 1997, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <fcntl.h>
+#include <error.h>
+
+#include "priv.h"
+
+int _diskfs_diskdirty;
+int diskfs_readonly = 0;
+int diskfs_hard_readonly = 0;
+
+int
+diskfs_check_readonly ()
+{
+ error_t err;
+
+ if (diskfs_readonly)
+ return 1;
+ else
+ {
+ if (!_diskfs_diskdirty)
+ {
+ err = diskfs_set_hypermetadata (1, 0);
+ if (err)
+ {
+ error (0, 0,
+ "%s: MEDIA NOT WRITABLE; switching to READ-ONLY",
+ diskfs_disk_name ?: "-");
+ diskfs_hard_readonly = diskfs_readonly = 1;
+ return 1;
+ }
+ _diskfs_diskdirty = 1;
+ }
+ return 0;
+ }
+}
+
+/* Change an active filesystem between read-only and writable modes, setting
+ the global variable DISKFS_READONLY to reflect the current mode. If an
+ error is returned, nothing will have changed. The user should hold
+ DISKFS_FSYS_LOCK while calling this routine. */
+error_t
+diskfs_set_readonly (int readonly)
+{
+ error_t err = 0;
+
+ if (diskfs_hard_readonly)
+ return readonly ? 0 : EROFS;
+
+ if (readonly != diskfs_readonly)
+ {
+ err = ports_inhibit_class_rpcs (diskfs_protid_class);
+ if (! err)
+ {
+ if (readonly)
+ {
+ error_t peropen_writable (void *pi)
+ {
+ struct protid *const cred = pi;
+ return (cred->po->openstat & O_WRITE) ? EBUSY : 0;
+ }
+
+ /* Any writable open files? */
+ err = ports_class_iterate (diskfs_protid_class,
+ peropen_writable);
+
+ /* Any writable pagers? */
+ if (!err && (diskfs_max_user_pager_prot () & VM_PROT_WRITE))
+ err = EBUSY;
+
+ if (!err)
+ /* Sync */
+ {
+ diskfs_sync_everything (1);
+ diskfs_set_hypermetadata (1, 1);
+ _diskfs_diskdirty = 0;
+ }
+ }
+
+ if (!err)
+ {
+ diskfs_readonly = readonly;
+ diskfs_readonly_changed (readonly);
+ }
+
+ ports_resume_class_rpcs (diskfs_protid_class);
+ }
+ }
+
+ return err;
+}
diff --git a/libdiskfs/remount.c b/libdiskfs/remount.c
new file mode 100644
index 00000000..1ed622f2
--- /dev/null
+++ b/libdiskfs/remount.c
@@ -0,0 +1,47 @@
+/* Remount an active filesystem
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* Re-read all incore data structures from disk. This will only work if
+ DISKFS_READONLY is true. DISKFS_FSYS_LOCK should be held while calling
+ this routine. */
+error_t
+diskfs_remount ()
+{
+ error_t err;
+
+ if (! diskfs_check_readonly ())
+ return EBUSY;
+
+ err = ports_inhibit_class_rpcs (diskfs_protid_class);
+ if (err)
+ return err;
+
+ err = diskfs_reload_global_state ();
+ if (!err)
+ err = diskfs_node_iterate (diskfs_node_reload);
+
+ ports_resume_class_rpcs (diskfs_protid_class);
+
+ return err;
+}
diff --git a/libdiskfs/shutdown.c b/libdiskfs/shutdown.c
new file mode 100644
index 00000000..66deb59a
--- /dev/null
+++ b/libdiskfs/shutdown.c
@@ -0,0 +1,102 @@
+/*
+ Copyright (C) 1993,94,95,96,98,99,2001 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include <hurd/fsys.h>
+
+/* Shutdown the filesystem; flags are as for fsys_goaway. */
+error_t
+diskfs_shutdown (int flags)
+{
+ int nports = -1;
+ int err;
+
+ error_t
+ helper (struct node *np)
+ {
+ error_t error;
+ mach_port_t control;
+
+ error = fshelp_fetch_control (&np->transbox, &control);
+ pthread_mutex_unlock (&np->lock);
+ if (!error && (control != MACH_PORT_NULL))
+ {
+ error = fsys_goaway (control, flags);
+ mach_port_deallocate (mach_task_self (), control);
+ }
+ else
+ error = 0;
+ pthread_mutex_lock (&np->lock);
+
+ if ((error == MIG_SERVER_DIED) || (error == MACH_SEND_INVALID_DEST))
+ error = 0;
+
+ return error;
+ }
+
+ if ((flags & FSYS_GOAWAY_UNLINK)
+ && S_ISDIR (diskfs_root_node->dn_stat.st_mode))
+ return EBUSY;
+
+ if (flags & FSYS_GOAWAY_RECURSE)
+ {
+ err = diskfs_node_iterate (helper);
+ if (err)
+ return err;
+ }
+
+ pthread_rwlock_wrlock (&diskfs_fsys_lock);
+
+ /* Permit all the current RPC's to finish, and then
+ suspend new ones. */
+ err = ports_inhibit_class_rpcs (diskfs_protid_class);
+ if (err)
+ {
+ pthread_rwlock_unlock (&diskfs_fsys_lock);
+ return err;
+ }
+
+ /* Write everything out and set "clean" state. Even if we don't in fact
+ shut down now, this has the nice effect that a disk that has not been
+ written for a long time will not need checking after a crash. */
+ diskfs_sync_everything (1);
+ diskfs_set_hypermetadata (1, 1);
+ _diskfs_diskdirty = 0;
+
+ /* First, see if there are outstanding user ports. */
+ nports = ports_count_class (diskfs_protid_class);
+ if (((flags & FSYS_GOAWAY_FORCE) == 0)
+ && (nports || diskfs_pager_users ()))
+ {
+ ports_enable_class (diskfs_protid_class);
+ ports_resume_class_rpcs (diskfs_protid_class);
+ pthread_rwlock_unlock (&diskfs_fsys_lock);
+ return EBUSY;
+ }
+
+ if (!diskfs_readonly && (flags & FSYS_GOAWAY_NOSYNC) == 0)
+ {
+ diskfs_shutdown_pager ();
+ diskfs_set_hypermetadata (1, 1);
+ }
+
+ return 0;
+}
diff --git a/libdiskfs/sync-default.c b/libdiskfs/sync-default.c
new file mode 100644
index 00000000..0d1fd93b
--- /dev/null
+++ b/libdiskfs/sync-default.c
@@ -0,0 +1,23 @@
+/* A variable holding the initial sync interval
+
+ Copyright (C) 1995, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+int diskfs_default_sync_interval = DEFAULT_SYNC_INTERVAL;
diff --git a/libdiskfs/sync-interval.c b/libdiskfs/sync-interval.c
new file mode 100644
index 00000000..14405f29
--- /dev/null
+++ b/libdiskfs/sync-interval.c
@@ -0,0 +1,136 @@
+/* Support for periodic syncing
+
+ Copyright (C) 1995,96,99,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include <hurd/fsys.h>
+
+#include "priv.h"
+
+/* A user-readable variable reflecting the last set sync interval. */
+int diskfs_sync_interval = 0;
+
+/* The thread that's doing the syncing. */
+static pthread_t periodic_sync_thread;
+
+/* This port represents the periodic sync service as if it were
+ an RPC. We can use ports_inhibit_port_rpcs on this port to guarantee
+ that the periodic_sync_thread is quiescent. */
+static struct port_info *pi;
+
+static void * periodic_sync (void *);
+
+/* Establish a thread to sync the filesystem every INTERVAL seconds, or
+ never, if INTERVAL is zero. If an error occurs creating the thread, it is
+ returned, otherwise 0. Subsequent calls will create a new thread and
+ (eventually) get rid of the old one; the old thread won't do any more
+ syncs, regardless. */
+error_t
+diskfs_set_sync_interval (int interval)
+{
+ error_t err = 0;
+
+ if (! pi)
+ {
+ err = ports_create_port (diskfs_control_class, diskfs_port_bucket,
+ sizeof (struct port_info), &pi);
+ if (err)
+ return err;
+ }
+
+ err = ports_inhibit_port_rpcs (pi);
+ if (err)
+ return err;
+
+ /* Here we just set the new thread; any existing thread will notice when it
+ wakes up and go away silently. */
+ if (interval == 0)
+ periodic_sync_thread = 0;
+ else
+ {
+ err = pthread_create (&periodic_sync_thread, NULL, periodic_sync,
+ (void *)(intptr_t) interval);
+ if (!err)
+ pthread_detach (periodic_sync_thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+ }
+
+ if (!err)
+ diskfs_sync_interval = interval;
+
+ ports_resume_port_rpcs (pi);
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Sync the filesystem (pointed to by the variable CONTROL_PORT above) every
+ INTERVAL seconds, as long as it's in the thread pointed to by the global
+ variable PERIODIC_SYNC_THREAD. */
+static void *
+periodic_sync (void * arg)
+{
+ int interval = (int) arg;
+ for (;;)
+ {
+ error_t err;
+ struct rpc_info link;
+
+ /* This acts as a lock against creation of a new sync thread
+ while we are in the process of syncing. */
+ err = ports_begin_rpc (pi, 0, &link);
+
+ if (periodic_sync_thread != pthread_self ())
+ {
+ /* We've been superseded as the sync thread. Just die silently. */
+ ports_end_rpc (pi, &link);
+ return NULL;
+ }
+
+ if (! err)
+ {
+ if (! diskfs_readonly)
+ {
+ pthread_rwlock_rdlock (&diskfs_fsys_lock);
+ /* Only sync if we need to, to avoid clearing the clean flag
+ when it's just been set. Any other thread doing a sync
+ will have held the lock while it did its work. */
+ if (_diskfs_diskdirty)
+ {
+ diskfs_sync_everything (0);
+ diskfs_set_hypermetadata (0, 0);
+ }
+ pthread_rwlock_unlock (&diskfs_fsys_lock);
+ }
+ ports_end_rpc (pi, &link);
+ }
+
+ /* Wait until next time. */
+ sleep (interval);
+ }
+
+ return NULL;
+}
diff --git a/libdiskfs/trans-callback.c b/libdiskfs/trans-callback.c
new file mode 100644
index 00000000..283b184f
--- /dev/null
+++ b/libdiskfs/trans-callback.c
@@ -0,0 +1,91 @@
+/*
+ Copyright (C) 1995,96,97,98,2001,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <fcntl.h>
+
+/* Callback function needed for calls to fshelp_fetch_root. See
+ <hurd/fshelp.h> for the interface description. */
+static error_t
+_diskfs_translator_callback1_fn (void *cookie1, void *cookie2,
+ uid_t *uid, gid_t *gid,
+ char **argz, size_t *argz_len)
+{
+ error_t err;
+ struct node *np = cookie1;
+
+ if (! (np->dn_stat.st_mode & S_IPTRANS))
+ return ENOENT;
+
+ err = diskfs_get_translator (np, argz, (u_int *) argz_len);
+ if (err)
+ {
+ assert (err != EOPNOTSUPP);
+ return err;
+ }
+
+ *uid = np->dn_stat.st_uid;
+ *gid = np->dn_stat.st_gid;
+
+ return 0;
+}
+
+/* Callback function needed for calls to fshelp_fetch_root. See
+ <hurd/fshelp.h> for the interface description. */
+static error_t
+_diskfs_translator_callback2_fn (void *cookie1, void *cookie2,
+ int flags,
+ mach_port_t *underlying,
+ mach_msg_type_name_t *underlying_type)
+{
+ struct node *np = cookie1;
+ struct protid *cred;
+ struct peropen *po;
+ error_t err;
+ struct iouser *user;
+
+ err = iohelp_create_simple_iouser (&user, np->dn_stat.st_uid,
+ np->dn_stat.st_gid);
+ if (err)
+ return err;
+
+ err = diskfs_make_peropen (np, flags, cookie2, &po);
+ if (! err)
+ {
+ err = diskfs_create_protid (po, user, &cred);
+ if (err)
+ diskfs_release_peropen (po);
+ }
+
+ iohelp_free_iouser (user);
+
+ if (! err)
+ {
+ *underlying = ports_get_right (cred);
+ *underlying_type = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (cred);
+ }
+ return err;
+}
+
+fshelp_fetch_root_callback1_t _diskfs_translator_callback1 =
+ _diskfs_translator_callback1_fn;
+fshelp_fetch_root_callback2_t _diskfs_translator_callback2 =
+ _diskfs_translator_callback2_fn;
diff --git a/libdiskfs/validate-author.c b/libdiskfs/validate-author.c
new file mode 100644
index 00000000..d7dacabd
--- /dev/null
+++ b/libdiskfs/validate-author.c
@@ -0,0 +1,27 @@
+/* Default version of diskfs_validate_author_change
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+error_t
+diskfs_validate_author_change (struct node *np, uid_t author)
+{
+ return 0;
+}
diff --git a/libdiskfs/validate-flags.c b/libdiskfs/validate-flags.c
new file mode 100644
index 00000000..bb54dadd
--- /dev/null
+++ b/libdiskfs/validate-flags.c
@@ -0,0 +1,27 @@
+/* Default version of diskfs_validate_flags_change
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+error_t
+diskfs_validate_flags_change (struct node *np, int flags)
+{
+ return 0;
+}
diff --git a/libdiskfs/validate-group.c b/libdiskfs/validate-group.c
new file mode 100644
index 00000000..556c5aaa
--- /dev/null
+++ b/libdiskfs/validate-group.c
@@ -0,0 +1,27 @@
+/* Default version of diskfs_validate_group_change
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+error_t
+diskfs_validate_group_change (struct node *np, gid_t group)
+{
+ return 0;
+}
diff --git a/libdiskfs/validate-mode.c b/libdiskfs/validate-mode.c
new file mode 100644
index 00000000..1cf150e6
--- /dev/null
+++ b/libdiskfs/validate-mode.c
@@ -0,0 +1,27 @@
+/* Default version of diskfs_validate_mode_change
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+error_t
+diskfs_validate_mode_change (struct node *np, mode_t mode)
+{
+ return 0;
+}
diff --git a/libdiskfs/validate-owner.c b/libdiskfs/validate-owner.c
new file mode 100644
index 00000000..0cbe296c
--- /dev/null
+++ b/libdiskfs/validate-owner.c
@@ -0,0 +1,27 @@
+/* Default version of diskfs_validate_owner_change
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+error_t
+diskfs_validate_owner_change (struct node *np, uid_t uid)
+{
+ return 0;
+}
diff --git a/libdiskfs/validate-rdev.c b/libdiskfs/validate-rdev.c
new file mode 100644
index 00000000..2d766346
--- /dev/null
+++ b/libdiskfs/validate-rdev.c
@@ -0,0 +1,27 @@
+/* Default version of diskfs_validate_rdev_change
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+error_t
+diskfs_validate_rdev_change (struct node *np, dev_t rdev)
+{
+ return 0;
+}
diff --git a/libfshelp/Makefile b/libfshelp/Makefile
new file mode 100644
index 00000000..6ba6a14f
--- /dev/null
+++ b/libfshelp/Makefile
@@ -0,0 +1,40 @@
+# Copyright (C) 1994, 95, 96, 98, 1999, 2006, 2012 Free Software Foundation,
+# Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libfshelp
+makemode := library
+
+libname = libfshelp
+SRCS = lock-acquire.c lock-init.c \
+ translator-list.c \
+ start-translator-long.c start-translator.c \
+ fetch-root.c transbox-init.c set-active.c fetch-control.c \
+ drop-transbox.c translated.c \
+ delegate.c \
+ exec-reauth.c \
+ set-options.c \
+ get-identity.c \
+ perms-isowner.c perms-iscontroller.c perms-access.c \
+ perms-checkdirmod.c \
+ touch.c
+installhdrs = fshelp.h
+
+HURDLIBS = shouldbeinlibc iohelp ports ihash
+LDLIBS += -lpthread
+OBJS = $(subst .c,.o,$(SRCS))
+
+include ../Makeconf
diff --git a/libfshelp/delegate.c b/libfshelp/delegate.c
new file mode 100644
index 00000000..a44310f0
--- /dev/null
+++ b/libfshelp/delegate.c
@@ -0,0 +1,64 @@
+/* fshelp_delegate_translation
+
+ Copyright (C) 1995,96,99,2000,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <string.h>
+#include <argz.h>
+#include <hurd.h>
+#include <hurd/fsys.h>
+#include <hurd/paths.h>
+
+/* Try to hand off responsibility from a translator to the server located on
+ the node SERVER_NAME. REQUESTOR is the translator's bootstrap port, and
+ ARGV is the command line. If SERVER_NAME is NULL, then a name is
+ concocted by appending ARGV[0] to _SERVERS. */
+error_t
+fshelp_delegate_translation (const char *server_name,
+ mach_port_t requestor, char **argv)
+{
+ error_t err;
+ file_t server;
+
+ if (! server_name)
+ {
+ char *buf = alloca (strlen (argv[0]) + sizeof (_SERVERS));
+ strcpy (buf, _SERVERS);
+ strcat (buf, argv[0]);
+ server_name = buf;
+ }
+
+ server = file_name_lookup (server_name, 0, 0);
+ if (server != MACH_PORT_NULL)
+ {
+ char *argz;
+ size_t argz_len;
+ err = argz_create (argv, &argz, &argz_len);
+ if (!err)
+ {
+ err = fsys_forward (server,
+ requestor, MACH_MSG_TYPE_COPY_SEND,
+ argz, argz_len);
+ free (argz);
+ }
+ }
+ else
+ err = errno;
+
+ return err;
+}
diff --git a/libfshelp/drop-transbox.c b/libfshelp/drop-transbox.c
new file mode 100644
index 00000000..d9087488
--- /dev/null
+++ b/libfshelp/drop-transbox.c
@@ -0,0 +1,28 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fshelp.h"
+
+void
+fshelp_drop_transbox (struct transbox *box)
+{
+ if (box->active != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), box->active);
+}
diff --git a/libfshelp/exec-reauth.c b/libfshelp/exec-reauth.c
new file mode 100644
index 00000000..d9a82974
--- /dev/null
+++ b/libfshelp/exec-reauth.c
@@ -0,0 +1,152 @@
+/* Setuid reauthentication for exec
+
+ Copyright (C) 1995,96,97,2002 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>,
+ from the original by Michael I. Bushnell p/BSG <mib@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd/io.h>
+#include <hurd/process.h>
+#include <hurd/auth.h>
+#include <idvec.h>
+
+#include "fshelp.h"
+
+extern error_t
+exec_reauth (auth_t auth, int secure, int must_reauth,
+ mach_port_t *ports, unsigned num_ports,
+ mach_port_t *fds, unsigned num_fds);
+
+/* If SUID or SGID is true, adds UID and/or GID respectively to the
+ authentication in PORTS[INIT_PORT_AUTH], and replaces it with the result.
+ All the other ports in PORTS and FDS are then reauthenticated, using any
+ privileges available through AUTH. If GET_FILE_IDS is non-NULL, and the
+ auth port in PORTS[INIT_PORT_AUTH] is bogus, it is called to get a list of
+ uids and gids from the file to use as a replacement. If SECURE is
+ non-NULL, whether not the added ids are new is returned in it. If either
+ the uid or gid case fails, then the other may still be applied. */
+error_t
+fshelp_exec_reauth (int suid, uid_t uid, int sgid, gid_t gid,
+ auth_t auth,
+ error_t
+ (*get_file_ids)(struct idvec *uids, struct idvec *gids),
+ mach_port_t *ports, mach_msg_type_number_t num_ports,
+ mach_port_t *fds, mach_msg_type_number_t num_fds,
+ int *secure)
+{
+ error_t err = 0;
+ int _secure = 0;
+
+ if (suid || sgid)
+ {
+ int already_root = 0;
+ auth_t newauth;
+ /* These variables describe the auth port that the user gave us. */
+ struct idvec *eff_uids = make_idvec (), *avail_uids = make_idvec ();
+ struct idvec *eff_gids = make_idvec (), *avail_gids = make_idvec ();
+
+ if (!eff_uids || !avail_uids || !eff_gids || !avail_gids)
+ goto abandon_suid; /* Allocation error; probably toast, but... */
+
+ /* STEP 0: Fetch the user's current id's. */
+ err = idvec_merge_auth (eff_uids, avail_uids, eff_gids, avail_gids,
+ ports[INIT_PORT_AUTH]);
+ if (err)
+ goto abandon_suid;
+
+ already_root =
+ idvec_contains (eff_uids, 0) || idvec_contains (avail_uids, 0);
+
+ /* If the user's auth port is fraudulent, then these values will be
+ wrong. No matter; we will repeat these checks using secure id sets
+ later if the port turns out to be bogus. */
+ if (suid)
+ err = idvec_setid (eff_uids, avail_uids, uid, &_secure);
+ if (sgid && !err)
+ err = idvec_setid (eff_gids, avail_gids, gid, &_secure);
+ if (err)
+ goto abandon_suid;
+
+ /* STEP 3: Attempt to create this new auth handle. */
+ err = auth_makeauth (auth, &ports[INIT_PORT_AUTH],
+ MACH_MSG_TYPE_COPY_SEND, 1,
+ eff_uids->ids, eff_uids->num,
+ avail_uids->ids, avail_uids->num,
+ eff_gids->ids, eff_gids->num,
+ avail_gids->ids, avail_gids->num,
+ &newauth);
+ if (err == EINVAL && get_file_ids)
+ /* The user's auth port was bogus. As we can't trust what the user
+ has told us about ids, we use the authentication on the file being
+ execed (which we know is good), as the effective ids, and assume
+ no aux ids. */
+ {
+ /* Get rid of all ids from the bogus auth port. */
+ idvec_clear (eff_uids);
+ idvec_clear (avail_uids);
+ idvec_clear (eff_gids);
+ idvec_clear (avail_gids);
+
+ /* Now add some from a source we trust. */
+ err = (*get_file_ids)(eff_uids, eff_gids);
+
+ already_root = idvec_contains (eff_uids, 0);
+ if (suid && !err)
+ err = idvec_setid (eff_uids, avail_uids, uid, &_secure);
+ if (sgid && !err)
+ err = idvec_setid (eff_gids, avail_gids, gid, &_secure);
+ if (err)
+ goto abandon_suid;
+
+ /* Trrrry again... */
+ err = auth_makeauth (auth, 0, MACH_MSG_TYPE_COPY_SEND, 1,
+ eff_uids->ids, eff_uids->num,
+ avail_uids->ids, avail_uids->num,
+ eff_gids->ids, eff_gids->num,
+ avail_gids->ids, avail_gids->num,
+ &newauth);
+ }
+
+ if (err)
+ goto abandon_suid;
+
+ if (already_root)
+ _secure = 0; /* executive privilege */
+
+ /* Re-authenticate the exec parameters. */
+ exec_reauth (newauth, _secure, 0, ports, num_ports, fds, num_fds);
+
+ proc_setowner (ports[INIT_PORT_PROC],
+ eff_uids->num > 0 ? eff_uids->ids[0] : 0,
+ !eff_uids->num);
+
+ abandon_suid:
+ if (eff_uids)
+ idvec_free (eff_uids);
+ if (avail_uids)
+ idvec_free (avail_uids);
+ if (eff_gids)
+ idvec_free (eff_gids);
+ if (avail_gids)
+ idvec_free (avail_gids);
+ }
+
+ if (secure)
+ *secure = _secure;
+
+ return err;
+}
diff --git a/libfshelp/fetch-control.c b/libfshelp/fetch-control.c
new file mode 100644
index 00000000..26c12d88
--- /dev/null
+++ b/libfshelp/fetch-control.c
@@ -0,0 +1,31 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fshelp.h"
+
+error_t
+fshelp_fetch_control (struct transbox *box,
+ mach_port_t *control)
+{
+ *control = box->active;
+ if (*control != MACH_PORT_NULL)
+ mach_port_mod_refs (mach_task_self (), *control, MACH_PORT_RIGHT_SEND, 1);
+ return 0;
+}
diff --git a/libfshelp/fetch-root.c b/libfshelp/fetch-root.c
new file mode 100644
index 00000000..45c7dd09
--- /dev/null
+++ b/libfshelp/fetch-root.c
@@ -0,0 +1,195 @@
+/*
+ Copyright (C) 1995,96,99,2000,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "trans.h"
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+#include <hurd/fsys.h>
+
+error_t
+fshelp_fetch_root (struct transbox *box, void *cookie,
+ file_t dotdot,
+ struct iouser *user,
+ int flags,
+ fshelp_fetch_root_callback1_t callback1,
+ fshelp_fetch_root_callback2_t callback2,
+ retry_type *retry, char *retryname,
+ file_t *root)
+{
+ error_t err;
+ mach_port_t control;
+ int cancel;
+ int i;
+
+ start_over:
+
+ if (box->active != MACH_PORT_NULL)
+ assert ((box->flags & TRANSBOX_STARTING) == 0);
+ else
+ {
+ uid_t uid, gid;
+ char *argz;
+ size_t argz_len;
+ error_t err;
+ mach_port_t ports[INIT_PORT_MAX];
+ int ints[INIT_INT_MAX];
+ mach_port_t fds[STDERR_FILENO + 1];
+ auth_t ourauth, newauth;
+
+ mach_port_t reauth (mach_port_t port) /* Consumes PORT. */
+ {
+ mach_port_t rend, ret;
+ error_t err;
+
+ if (port == MACH_PORT_NULL)
+ return port;
+
+ if (ourauth == MACH_PORT_NULL)
+ /* We have no auth server, so we aren't doing reauthentications.
+ Just pass on our own ports directly. */
+ return port;
+
+ rend = mach_reply_port ();
+
+ /* MAKE_SEND is safe here because we destroy REND ourselves. */
+ err = io_reauthenticate (port, rend,
+ MACH_MSG_TYPE_MAKE_SEND);
+ mach_port_deallocate (mach_task_self (), port);
+ if (! err)
+ err = auth_user_authenticate (newauth, rend,
+ MACH_MSG_TYPE_MAKE_SEND, &ret);
+ if (err)
+ ret = MACH_PORT_NULL;
+
+ mach_port_mod_refs (mach_task_self (), rend, MACH_PORT_RIGHT_RECEIVE, -1);
+
+ return ret;
+ }
+ error_t fetch_underlying (int flags, mach_port_t *underlying,
+ mach_msg_type_name_t *underlying_type,
+ task_t task, void *cookie)
+ {
+ return
+ (*callback2) (box->cookie, cookie, flags,
+ underlying, underlying_type);
+ }
+
+ if (box->flags & TRANSBOX_STARTING)
+ {
+ box->flags |= TRANSBOX_WANTED;
+ cancel = pthread_hurd_cond_wait_np (&box->wakeup, box->lock);
+ if (cancel)
+ return EINTR;
+ goto start_over;
+ }
+ box->flags |= TRANSBOX_STARTING;
+ pthread_mutex_unlock (box->lock);
+
+ err = (*callback1) (box->cookie, cookie, &uid, &gid, &argz, &argz_len);
+ if (err)
+ goto return_error;
+
+ ourauth = getauth ();
+ if (ourauth == MACH_PORT_NULL)
+ newauth = ourauth;
+ else
+ {
+ uid_t uidarray[2] = { uid, uid };
+ gid_t gidarray[2] = { gid, gid };
+ err = auth_makeauth (ourauth, 0, MACH_MSG_TYPE_COPY_SEND, 0,
+ uidarray, 1, uidarray, 2,
+ gidarray, 1, gidarray, 2, &newauth);
+ if (err)
+ goto return_error;
+ }
+
+ bzero (ports, INIT_PORT_MAX * sizeof (mach_port_t));
+ bzero (fds, (STDERR_FILENO + 1) * sizeof (mach_port_t));
+ bzero (ints, INIT_INT_MAX * sizeof (int));
+
+ ports[INIT_PORT_CWDIR] = dotdot;
+ ports[INIT_PORT_CRDIR] = reauth (getcrdir ());
+ ports[INIT_PORT_AUTH] = newauth;
+
+ fds[STDERR_FILENO] = reauth (getdport (STDERR_FILENO));
+
+ err = fshelp_start_translator_long (fetch_underlying, NULL,
+ argz, argz, argz_len,
+ fds, MACH_MSG_TYPE_COPY_SEND,
+ STDERR_FILENO + 1,
+ ports, MACH_MSG_TYPE_COPY_SEND,
+ INIT_PORT_MAX,
+ ints, INIT_INT_MAX,
+ uid,
+ 0, &control);
+ for (i = 0; i <= STDERR_FILENO; i++)
+ mach_port_deallocate (mach_task_self (), fds[i]);
+
+ for (i = 0; i < INIT_PORT_MAX; i++)
+ if (i != INIT_PORT_CWDIR)
+ mach_port_deallocate (mach_task_self (), ports[i]);
+
+ pthread_mutex_lock (box->lock);
+
+ free (argz);
+
+ return_error:
+
+ box->flags &= ~TRANSBOX_STARTING;
+ if (box->flags & TRANSBOX_WANTED)
+ {
+ box->flags &= ~TRANSBOX_WANTED;
+ pthread_cond_broadcast (&box->wakeup);
+ }
+
+ if (err)
+ return err;
+
+ if (! MACH_PORT_VALID (control))
+ /* The start translator succeeded, but it returned a bogus port. */
+ return EDIED;
+
+ box->active = control;
+ }
+
+ control = box->active;
+ mach_port_mod_refs (mach_task_self (), control,
+ MACH_PORT_RIGHT_SEND, 1);
+ pthread_mutex_unlock (box->lock);
+
+ /* Cancellation point XXX */
+ err = fsys_getroot (control, dotdot, MACH_MSG_TYPE_COPY_SEND,
+ user->uids->ids, user->uids->num,
+ user->gids->ids, user->gids->num,
+ flags, retry, retryname, root);
+
+ pthread_mutex_lock (box->lock);
+
+ if ((err == MACH_SEND_INVALID_DEST || err == MIG_SERVER_DIED)
+ && control == box->active)
+ fshelp_set_active (box, MACH_PORT_NULL, 0);
+ mach_port_deallocate (mach_task_self (), control);
+
+ if (err == MACH_SEND_INVALID_DEST || err == MIG_SERVER_DIED)
+ goto start_over;
+
+ return err;
+}
diff --git a/libfshelp/fshelp.h b/libfshelp/fshelp.h
new file mode 100644
index 00000000..5d3a0ceb
--- /dev/null
+++ b/libfshelp/fshelp.h
@@ -0,0 +1,308 @@
+/* FS helper library definitions
+ Copyright (C) 1994,95,96,97,98,99,2000,01,02,13,14
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _HURD_FSHELP_
+#define _HURD_FSHELP_
+
+/* This library implements various things that are generic to
+ all or most implementors of the filesystem protocol. It
+ presumes that you are using the iohelp library as well. It
+ is divided into separate facilities which may be used independently. */
+
+#include <errno.h>
+#include <mach.h>
+#include <hurd/hurd_types.h>
+#include <pthread.h>
+#include <hurd/iohelp.h>
+#include <sys/stat.h>
+#include <maptime.h>
+
+
+/* Keeping track of active translators */
+/* These routines keep a list of active translators. They do not
+ require multi threading but depend on the ports library. */
+
+struct port_info;
+
+/* Record an active translator being bound to the given file name
+ NAME. ACTIVE is the control port of the translator. PI references
+ a receive port that is used to request dead name notifications,
+ typically the port for the underlying node passed to the
+ translator. */
+error_t
+fshelp_set_active_translator (struct port_info *pi,
+ const char *name,
+ mach_port_t active);
+
+/* Remove the active translator specified by its control port ACTIVE.
+ If there is no active translator with the given control port, this
+ does nothing. */
+error_t
+fshelp_remove_active_translator (mach_port_t active);
+
+/* This kind of function is used by fshelp_get_active_translators to
+ filter the list of translators to return. If a filter returns an
+ error for a given PATH, the translator bound to the PATH is not
+ included in the list. */
+typedef error_t (*fshelp_filter) (const char *path);
+
+/* Records the list of active translators into the argz vector
+ specified by TRANSLATORS filtered by FILTER. */
+error_t
+fshelp_get_active_translators (char **translators,
+ size_t *translators_len,
+ fshelp_filter filter);
+
+
+/* Passive translator linkage */
+/* These routines are self-contained and start passive translators,
+ returning the control port. They do not require multi threading
+ or the ports library. */
+
+/* A callback used by the translator starting functions, which should be a
+ function that given some open flags, opens the appropriate file, and
+ returns the node port. */
+typedef error_t (*fshelp_open_fn_t) (int flags,
+ file_t *node,
+ mach_msg_type_name_t *node_type,
+ task_t, void *cookie);
+
+/* Start a passive translator NAME with arguments ARGZ (length
+ ARGZ_LEN). Initialize the initports to PORTS (length PORTS_LEN),
+ the initints to INTS (length INTS_LEN), and the file descriptor
+ table to FDS (length FDS_LEN). Return the control port in
+ *CONTROL. If the translator doesn't respond or die in TIMEOUT
+ milliseconds (if TIMEOUT > 0), return an appropriate error. If the
+ translator dies before responding, return EDIED. Set the new
+ task's owner to OWNER_UID (or, if OWNER_UID is -1, then clear the
+ new task's owner. */
+error_t
+fshelp_start_translator_long (fshelp_open_fn_t underlying_open_fn, void *cookie,
+ char *name, char *argz, int argz_len,
+ mach_port_t *fds,
+ mach_msg_type_name_t fds_type, int fds_len,
+ mach_port_t *ports,
+ mach_msg_type_name_t ports_type, int ports_len,
+ int *ints, int ints_len,
+ uid_t owner_uid,
+ int timeout, fsys_t *control);
+
+
+/* Same as fshelp_start_translator_long, except the initports and ints
+ are copied from our own state, fd[2] is copied from our own stderr,
+ and the other fds are cleared. */
+error_t
+fshelp_start_translator (fshelp_open_fn_t underlying_open_fn, void *cookie,
+ char *name, char *argz, int argz_len,
+ int timeout, fsys_t *control);
+
+
+/* Active translator linkage */
+
+/* These routines implement the linkage to active translators needed
+ by any filesystem which supports them. They require pthreads and
+ use the passive translator routines above, but they don't require
+ the ports library at all. */
+
+struct transbox
+{
+ fsys_t active;
+ pthread_mutex_t *lock;
+ int flags;
+ pthread_cond_t wakeup;
+ void *cookie;
+};
+#define TRANSBOX_STARTING 1
+#define TRANSBOX_WANTED 2
+
+/* This interface is complex, because creating the ports and state
+ necessary for start_translator_long is expensive. The caller to
+ fshelp_fetch_root should not need to create them on every call, since
+ usually there will be an existing active translator. */
+
+/* This routine is called by fshelp_fetch_root to fetch more information.
+ Return the owner and group of the underlying translated file in *UID and
+ *GID; point *ARGZ at the entire passive translator spec for the file
+ (setting *ARGZ_LEN to the length.) If there is no passive translator,
+ then return ENOENT. COOKIE1 is the cookie passed in fshelp_transbox_init.
+ COOKIE2 is the cookie passed in the call to fshelp_fetch_root. */
+typedef error_t (*fshelp_fetch_root_callback1_t) (void *cookie1, void *cookie2,
+ uid_t *uid, gid_t *gid,
+ char **argz, size_t *argz_len);
+
+/* This routine is called by fshelp_fetch_root to fetch more information.
+ Return an unauthenticated node for the file itself in *UNDERLYING and
+ *UNDERLYING_TYPE (opened with FLAGS). COOKIE1 is the cookie passed in
+ fshelp_transbox_init. COOKIE2 is the cookie passed in the call to
+ fshelp_fetch_root. */
+typedef error_t (*fshelp_fetch_root_callback2_t) (void *cookie1, void *cookie2,
+ int flags,
+ mach_port_t *underlying,
+ mach_msg_type_name_t
+ *underlying_type);
+
+/* Fetch the root from TRANSBOX. DOTDOT is an unauthenticated port
+ for the directory in which we are looking; USER specifies the ids
+ of the user responsible for the call. FLAGS are as for dir_lookup
+ (but O_CREAT and O_EXCL are not meaningful and are ignored). The
+ transbox lock (as set by fshelp_transbox_init) must be held before
+ the call, and will be held upon return, but may be released during
+ the operation of the call. */
+error_t
+fshelp_fetch_root (struct transbox *transbox, void *cookie,
+ file_t dotdot,
+ struct iouser *user,
+ int flags,
+ fshelp_fetch_root_callback1_t callback1,
+ fshelp_fetch_root_callback2_t callback2,
+ retry_type *retry, char *retryname, mach_port_t *root);
+
+void
+fshelp_transbox_init (struct transbox *transbox,
+ pthread_mutex_t *lock,
+ void *cookie);
+
+/* Return true iff there is an active translator on this box */
+int fshelp_translated (struct transbox *box);
+
+/* Atomically replace the existing active translator port for this box
+ with NEWACTIVE. If EXCL is non-zero then don't frob an existing
+ active; return EBUSY instead. */
+error_t fshelp_set_active (struct transbox *box,
+ fsys_t newactive, int excl);
+
+/* Fetch the control port to make a request on it. It's a bad idea
+ to do fsys_getroot with the result; use fetch_root instead. */
+error_t fshelp_fetch_control (struct transbox *box,
+ mach_port_t *control);
+
+/* A transbox is being deallocated, clean associated state. */
+void fshelp_drop_transbox (struct transbox *box);
+
+
+
+/* Flock handling. */
+struct lock_box
+{
+ int type;
+ pthread_cond_t wait;
+ int waiting;
+ int shcount;
+};
+
+/* Call when a user makes a request to acquire an lock via file_lock.
+ There should be one lock box per object and one int per open; these
+ are passed as arguments BOX and USER respectively. FLAGS are as
+ per file_lock. MUT is a mutex which will be held whenever this
+ routine is called, to lock BOX->wait. */
+error_t fshelp_acquire_lock (struct lock_box *box, int *user,
+ pthread_mutex_t *mut, int flags);
+
+
+/* Initialize lock_box BOX. (The user int passed to fshelp_acquire_lock
+ should be initialized with LOCK_UN.). */
+void fshelp_lock_init (struct lock_box *box);
+
+
+
+struct port_bucket; /* shut up C compiler */
+/* Return an identity port in *PT for the node numbered FILENO,
+ suitable for returning from io_identity; exactly one send right
+ must be created from the returned value. FILENO should be the same
+ value returned as the `fileno' out-parameter in io_identity, and in
+ the enclosing directory (except for mount points), and in the
+ st_ino stat field. BUCKET should be a ports port bucket; fshelp
+ requires the caller to make sure port operations (for no-senders
+ notifications) are used.
+ */
+error_t fshelp_get_identity (struct port_bucket *bucket,
+ ino64_t fileno, mach_port_t *pt);
+
+
+
+/* Try to hand off responsibility from a translator to the server located on
+ the node SERVER_NAME. REQUESTOR is the translator's bootstrap port, and
+ ARGV is the command line. If SERVER_NAME is NULL, then a name is
+ concocted by appending ARGV[0] to _SERVERS. */
+error_t fshelp_delegate_translation (const char *server_name,
+ mach_port_t requestor, char **argv);
+
+struct idvec; /* Include <idvec.h> to get the real thing. */
+
+/* If SUID or SGID is true, adds UID and/or GID respectively to the
+ authentication in PORTS[INIT_PORT_AUTH], and replaces it with the result.
+ All the other ports in PORTS and FDS are then reauthenticated, using any
+ privileges available through AUTH. If GET_FILE_IDS is non-NULL, and the
+ auth port in PORTS[INIT_PORT_AUTH] is bogus, it is called to get a list of
+ uids and gids from the file to use as a replacement. If SECURE is
+ non-NULL, whether not the added ids are new is returned in it. If either
+ the uid or gid case fails, then the other may still be applied. */
+error_t
+fshelp_exec_reauth (int suid, uid_t uid, int sgid, gid_t gid,
+ auth_t auth,
+ error_t
+ (*get_file_ids)(struct idvec *uids, struct idvec *gids),
+ mach_port_t *ports, mach_msg_type_number_t num_ports,
+ mach_port_t *fds, mach_msg_type_number_t num_fds,
+ int *secure);
+
+struct argp; /* Include <argp.h> to get the real thing. */
+
+/* Invoke ARGP with data from DATA & LEN, in the standard way. */
+error_t fshelp_set_options (const struct argp *argp, int flags,
+ const char *argz, size_t argz_len, void *input);
+
+
+/* Standardized filesystem permission checking */
+
+/* Check to see whether USER should be considered the owner of the
+ file identified by ST. If so, return zero; otherwise return an
+ appropriate error code. */
+error_t fshelp_isowner (io_statbuf_t *st, struct iouser *user);
+
+/* Check to see whether USER should be considered a controller of the
+ filesystem. Which is to say, check to see if we should give USER the
+ control port. ST is the stat of the root node. USER is the user
+ asking for a send right to the control port. */
+error_t
+fshelp_iscontroller (io_statbuf_t *st, struct iouser *user);
+
+/* Check to see whether the user USER can operate on a file identified
+ by ST. OP is one of S_IREAD, S_IWRITE, and S_IEXEC. If the access
+ is permitted, return zero; otherwise return an appropriate error
+ code. */
+error_t fshelp_access (io_statbuf_t *st, int op, struct iouser *user);
+
+/* Check to see whether USER is allowed to modify DIR with respect to
+ existing file ST. (If there is no existing file, pass 0 for ST.)
+ If the access is permissible return 0; otherwise return an
+ appropriate error code. */
+error_t fshelp_checkdirmod (io_statbuf_t *dir, io_statbuf_t *st,
+ struct iouser *user);
+
+
+/* Timestamps to change. */
+#define TOUCH_ATIME 0x1
+#define TOUCH_MTIME 0x2
+#define TOUCH_CTIME 0x4
+
+/* Change the stat times of NODE as indicated by WHAT (from the set TOUCH_*)
+ to the current time. */
+void fshelp_touch (io_statbuf_t *st, unsigned what,
+ volatile struct mapped_time_value *maptime);
+#endif
diff --git a/libfshelp/get-identity.c b/libfshelp/get-identity.c
new file mode 100644
index 00000000..2dbd254c
--- /dev/null
+++ b/libfshelp/get-identity.c
@@ -0,0 +1,92 @@
+/* Helper function for io_identity
+ Copyright (C) 1996, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include <fshelp.h>
+#include <hurd/ports.h>
+#include <hurd/ihash.h>
+#include <stddef.h>
+#include <assert.h>
+
+static struct port_class *idclass = 0;
+static pthread_mutex_t idlock = PTHREAD_MUTEX_INITIALIZER;
+
+struct idspec
+{
+ struct port_info pi;
+ hurd_ihash_locp_t id_hashloc;
+};
+
+static struct hurd_ihash idhash
+ = HURD_IHASH_INITIALIZER (offsetof (struct idspec, id_hashloc));
+
+static void
+id_clean (void *cookie)
+{
+ struct idspec *i = cookie;
+ pthread_mutex_lock (&idlock);
+ hurd_ihash_locp_remove (&idhash, i->id_hashloc);
+ pthread_mutex_unlock (&idlock);
+}
+
+static void
+id_initialize ()
+{
+ assert (!idclass);
+ idclass = ports_create_class (id_clean, NULL);
+}
+
+error_t
+fshelp_get_identity (struct port_bucket *bucket,
+ ino_t fileno,
+ mach_port_t *pt)
+{
+ struct idspec *i;
+ error_t err = 0;
+
+ pthread_mutex_lock (&idlock);
+ if (!idclass)
+ id_initialize ();
+
+ i = hurd_ihash_find (&idhash, (hurd_ihash_key_t) fileno);
+ if (i == NULL)
+ {
+ err = ports_create_port (idclass, bucket, sizeof (struct idspec), &i);
+ if (err)
+ goto lose;
+ err = hurd_ihash_add (&idhash, (hurd_ihash_key_t) fileno, i);
+ if (err)
+ goto lose_port;
+
+ *pt = ports_get_right (i);
+ ports_port_deref (i);
+ }
+ else
+ *pt = ports_get_right (i);
+
+ /* Success! */
+ goto lose;
+
+ lose_port:
+ ports_destroy_right (i);
+ lose:
+ pthread_mutex_unlock (&idlock);
+ return err;
+}
diff --git a/libfshelp/lock-acquire.c b/libfshelp/lock-acquire.c
new file mode 100644
index 00000000..574bc5cb
--- /dev/null
+++ b/libfshelp/lock-acquire.c
@@ -0,0 +1,131 @@
+/*
+ Copyright (C) 1993, 1994, 1996 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "locks.h"
+
+#define EWOULDBLOCK EAGAIN /* XXX */
+
+error_t
+fshelp_acquire_lock (struct lock_box *box, int *user, pthread_mutex_t *mut,
+ int flags)
+{
+ if (!(flags & (LOCK_UN | LOCK_EX | LOCK_SH)))
+ return 0;
+
+ if ((flags & LOCK_UN)
+ && (flags & (LOCK_SH | LOCK_EX)))
+ return EINVAL;
+
+ if (flags & LOCK_EX)
+ flags &= ~LOCK_SH;
+
+ /* flags now contains exactly one of LOCK_UN, LOCK_SH, or LOCK_EX. */
+
+ if (flags & LOCK_UN)
+ {
+ if (*user & LOCK_UN)
+ return 0;
+
+ assert (*user == box->type);
+ assert (*user == LOCK_SH || *user == LOCK_EX);
+
+ if (*user == LOCK_SH)
+ {
+ if (!--box->shcount)
+ box->type = LOCK_UN;
+ }
+ else if (*user == LOCK_EX)
+ box->type = LOCK_UN;
+
+ if (box->type == LOCK_UN && box->waiting)
+ {
+ box->waiting = 0;
+ pthread_cond_broadcast (&box->wait);
+ }
+ *user = LOCK_UN;
+ }
+ else
+ {
+ /* If we have an exclusive lock, release it. */
+ if (*user == LOCK_EX)
+ {
+ *user = LOCK_UN;
+ box->type = LOCK_UN;
+ if (box->waiting)
+ {
+ box->waiting = 0;
+ pthread_cond_broadcast (&box->wait);
+ }
+ }
+
+ /* If there is an exclusive lock, wait for it to end. */
+ while (box->type == LOCK_EX)
+ {
+ if (flags & LOCK_NB)
+ return EWOULDBLOCK;
+ box->waiting = 1;
+ if (pthread_hurd_cond_wait_np (&box->wait, mut))
+ return EINTR;
+ }
+
+ /* If we have a shared lock, release it. */
+ if (*user == LOCK_SH)
+ {
+ *user = LOCK_UN;
+ if (!--box->shcount)
+ {
+ box->type = LOCK_UN;
+ if (box->waiting)
+ {
+ box->waiting = 0;
+ pthread_cond_broadcast (&box->wait);
+ }
+ }
+ }
+
+ assert ((flags & LOCK_SH) || (flags & LOCK_EX));
+ if (flags & LOCK_SH)
+ {
+ assert (box->type != LOCK_EX);
+ *user = LOCK_SH;
+ box->type = LOCK_SH;
+ box->shcount++;
+ }
+ else if (flags & LOCK_EX)
+ {
+ /* Wait for any shared (and exclusive) locks to finish. */
+ while (box->type != LOCK_UN)
+ {
+ if (flags & LOCK_NB)
+ return EWOULDBLOCK;
+ else
+ {
+ box->waiting = 1;
+ if (pthread_hurd_cond_wait_np (&box->wait, mut))
+ return EINTR;
+ }
+ }
+ box->type = LOCK_EX;
+ *user = LOCK_EX;
+ }
+ }
+ return 0;
+}
diff --git a/libfshelp/lock-init.c b/libfshelp/lock-init.c
new file mode 100644
index 00000000..66046aaa
--- /dev/null
+++ b/libfshelp/lock-init.c
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 1993, 1994 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "locks.h"
+
+/* Initialize a lock box. */
+void
+fshelp_lock_init (struct lock_box *box)
+{
+ box->type = LOCK_UN;
+ pthread_cond_init (&box->wait, NULL);
+ box->waiting = 0;
+ box->shcount = 0;
+}
diff --git a/libfshelp/locks.h b/libfshelp/locks.h
new file mode 100644
index 00000000..a950f610
--- /dev/null
+++ b/libfshelp/locks.h
@@ -0,0 +1,28 @@
+/*
+ Copyright (C) 1994 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include <mach.h>
+#include <hurd.h>
+#include <pthread.h>
+#include <hurd/ports.h>
+#include "fshelp.h"
+#include <sys/file.h>
+#include <assert.h>
diff --git a/libfshelp/perms-access.c b/libfshelp/perms-access.c
new file mode 100644
index 00000000..67e52812
--- /dev/null
+++ b/libfshelp/perms-access.c
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include "fshelp.h"
+
+/* Check to see whether the user USER can operate on a file identified
+ by ST. OP is one of S_IREAD, S_IWRITE, and S_IEXEC. If the access
+ is permitted, return zero; otherwise return an appropriate error
+ code. */
+error_t
+fshelp_access (struct stat *st, int op, struct iouser *user)
+{
+ int gotit;
+ if (idvec_contains (user->uids, 0))
+ gotit = (op != S_IEXEC) || !S_ISREG(st->st_mode) || (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH));
+ else if (user->uids->num == 0 && (st->st_mode & S_IUSEUNK))
+ gotit = st->st_mode & (op << S_IUNKSHIFT);
+ else if (!fshelp_isowner (st, user))
+ gotit = st->st_mode & op;
+ else if (idvec_contains (user->gids, st->st_gid))
+ gotit = st->st_mode & (op >> 3);
+ else
+ gotit = st->st_mode & (op >> 6);
+ return gotit ? 0 : EACCES;
+}
diff --git a/libfshelp/perms-checkdirmod.c b/libfshelp/perms-checkdirmod.c
new file mode 100644
index 00000000..823c9f63
--- /dev/null
+++ b/libfshelp/perms-checkdirmod.c
@@ -0,0 +1,44 @@
+/*
+ Copyright (C) 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include "fshelp.h"
+
+/* Check to see whether USER is allowed to modify DIR with respect to
+ existing file ST. (If there is no existing file, pass 0 for ST.)
+ If the access is permissible return 0; otherwise return an
+ appropriate error code. */
+error_t
+fshelp_checkdirmod (struct stat *dir, struct stat *st, struct iouser *user)
+{
+ error_t err;
+
+ /* The user must be able to write the directory. */
+ err = fshelp_access (dir, S_IWRITE, user);
+ if (err)
+ return err;
+
+ /* If the directory is sticky, the user must own either it or the file. */
+ if ((dir->st_mode & S_ISVTX) && st
+ && fshelp_isowner (dir, user) && fshelp_isowner (st, user))
+ return EACCES;
+
+ return 0;
+}
diff --git a/libfshelp/perms-iscontroller.c b/libfshelp/perms-iscontroller.c
new file mode 100644
index 00000000..0adfdf22
--- /dev/null
+++ b/libfshelp/perms-iscontroller.c
@@ -0,0 +1,38 @@
+/* see whether a user should be considered a controller of the filesystem
+ Copyright (C) 2001, 2008 Free Software Foundation, Inc.
+ Written by Neal H Walfield <neal@cs.uml.edu>.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <unistd.h>
+#include "fshelp.h"
+
+/* Check to see whether USER should be considered a controller of the
+ filesystem. Which is to say, check to see if we should give USER the
+ control port. ST is the stat of the root node. USER is the user
+ asking for a send right to the control port. */
+error_t
+fshelp_iscontroller (struct stat *st, struct iouser *user)
+{
+ /* Permitted if USER has the superuser uid, the owner uid or if the
+ USER has authority over the process's effective id. */
+ if (idvec_contains (user->uids, 0)
+ || idvec_contains (user->uids, st->st_uid)
+ || idvec_contains (user->uids, geteuid ()))
+ return 0;
+ return EPERM;
+}
diff --git a/libfshelp/perms-isowner.c b/libfshelp/perms-isowner.c
new file mode 100644
index 00000000..d1975993
--- /dev/null
+++ b/libfshelp/perms-isowner.c
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "fshelp.h"
+
+/* Check to see whether USER should be considered the owner of the
+ file identified by ST. If so, return zero; otherwise return an
+ appropriate error code. */
+error_t
+fshelp_isowner (struct stat *st, struct iouser *user)
+{
+ /* Permitted if the user has the owner UID, the superuser UID, or if
+ the user is in the group of the file and has the group ID as
+ their user ID. */
+ if (idvec_contains (user->uids, st->st_uid)
+ || idvec_contains (user->uids, 0)
+ || (idvec_contains (user->gids, st->st_gid)
+ && idvec_contains (user->uids, st->st_gid)))
+ return 0;
+ else
+ return EPERM;
+}
diff --git a/libfshelp/set-active.c b/libfshelp/set-active.c
new file mode 100644
index 00000000..00f2a186
--- /dev/null
+++ b/libfshelp/set-active.c
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fshelp.h"
+#include <hurd/fsys.h>
+
+error_t
+fshelp_set_active (struct transbox *box,
+ mach_port_t active,
+ int excl)
+{
+ int cancel;
+
+ if (excl)
+ {
+ if (box->flags & TRANSBOX_STARTING)
+ return EBUSY;
+ if (box->active != MACH_PORT_NULL)
+ /* It looks like there's an existing translator, but make sure. */
+ {
+ mach_port_urefs_t dead_refs;
+ error_t err =
+ mach_port_get_refs (mach_task_self (),
+ box->active, MACH_PORT_RIGHT_DEAD_NAME,
+ &dead_refs);
+ if (!err && dead_refs == 0)
+ /* Still active, we lose. */
+ return EBUSY;
+ }
+ }
+
+ while (box->flags & TRANSBOX_STARTING)
+ {
+ box->flags |= TRANSBOX_WANTED;
+ cancel = pthread_hurd_cond_wait_np (&box->wakeup, box->lock);
+ if (cancel)
+ return EINTR;
+ }
+
+ if (box->active != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), box->active);
+
+ box->active = active;
+ return 0;
+}
diff --git a/libfshelp/set-options.c b/libfshelp/set-options.c
new file mode 100644
index 00000000..a627b841
--- /dev/null
+++ b/libfshelp/set-options.c
@@ -0,0 +1,47 @@
+/* Standard filesystem runtime option parsing
+
+ Copyright (C) 1996, 1998, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <mach.h>
+#include <argp.h>
+#include <argz.h>
+#include <alloca.h>
+
+#include "fshelp.h"
+
+/* XXX this is not currently so useful, but the new fsys_set_options will
+ have more commonly used code that can be put here. */
+
+/* Invoke ARGP with data from DATA & LEN, in the standard way. */
+error_t
+fshelp_set_options (const struct argp *argp, int flags,
+ const char *argz, size_t argz_len, void *input)
+{
+ int argc = argz_count (argz, argz_len);
+ char **argv = alloca (sizeof (char *) * (argc + 1));
+
+ argz_extract ((char *) argz, argz_len, argv);
+
+ return
+ argp_parse (argp, argc, argv,
+ flags | ARGP_NO_ERRS | ARGP_NO_HELP | ARGP_PARSE_ARGV0,
+ 0, input);
+}
diff --git a/libfshelp/start-translator-long.c b/libfshelp/start-translator-long.c
new file mode 100644
index 00000000..64a20bed
--- /dev/null
+++ b/libfshelp/start-translator-long.c
@@ -0,0 +1,328 @@
+/*
+ Copyright (C) 1995,96,99,2000,02, 04 Free Software Foundation, Inc.
+ Written by Miles Bader and Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <mach/notify.h>
+#include <mach.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+#include "fshelp.h"
+
+
+/* The data passed in the various messages we're interested in. */
+struct fsys_startup_request
+{
+ mach_msg_header_t head;
+ mach_msg_type_t flagsType;
+ int flags;
+ mach_msg_type_t control_portType;
+ mach_port_t control_port;
+};
+
+struct fsys_startup_reply
+{
+ mach_msg_header_t head;
+ mach_msg_type_t RetCodeType;
+ kern_return_t RetCode;
+ mach_msg_type_t realnodeType;
+ mach_port_t realnode;
+};
+
+/* Wait around for an fsys_startup message on the port PORT from the
+ translator on NODE (timing out after TIMEOUT milliseconds), and return a
+ send right for the resulting fsys control port in CONTROL. If a no-senders
+ notification is received on PORT, then it will be assumed that the
+ translator died, and EDIED will be returned. If an error occurs, the
+ error code is returned, otherwise 0. */
+static error_t
+service_fsys_startup (fshelp_open_fn_t underlying_open_fn, void *cookie,
+ mach_port_t port, long timeout, fsys_t *control,
+ task_t task)
+{
+ /* These should be optimized away to pure integer constants. */
+ const mach_msg_type_t flagsCheck =
+ {
+ MACH_MSG_TYPE_INTEGER_32, /* msgt_name = */
+ 32, /* msgt_size = */
+ 1, /* msgt_number = */
+ TRUE, /* msgt_inline = */
+ FALSE, /* msgt_longform = */
+ FALSE, /* msgt_deallocate = */
+ 0 /* msgt_unused = */
+ };
+ const mach_msg_type_t control_portCheck =
+ {
+ MACH_MSG_TYPE_PORT_SEND, /* msgt_name = */
+ 32, /* msgt_size = */
+ 1, /* msgt_number = */
+ TRUE, /* msgt_inline = */
+ FALSE, /* msgt_longform = */
+ FALSE, /* msgt_deallocate = */
+ 0 /* msgt_unused = */
+ };
+ const mach_msg_type_t RetCodeType =
+ {
+ MACH_MSG_TYPE_INTEGER_32, /* msgt_name = */
+ 32, /* msgt_size = */
+ 1, /* msgt_number = */
+ TRUE, /* msgt_inline = */
+ FALSE, /* msgt_longform = */
+ FALSE, /* msgt_deallocate = */
+ 0 /* msgt_unused = */
+ };
+ const mach_msg_type_t realnodeType =
+ {
+ -1, /* msgt_name = */
+ 32, /* msgt_size = */
+ 1, /* msgt_number = */
+ TRUE, /* msgt_inline = */
+ FALSE, /* msgt_longform = */
+ FALSE, /* msgt_deallocate = */
+ 0 /* msgt_unused = */
+ };
+
+ /* Return true iff TYPE fails to match CHECK. */
+ inline int type_check (const mach_msg_type_t *type,
+ const mach_msg_type_t *check)
+ {
+ union
+ {
+ unsigned32_t word;
+ mach_msg_type_t type;
+ } t, c;
+ t.type = *type;
+ c.type = *check;
+ return t.word != c.word;
+ }
+
+ error_t err;
+ union
+ {
+ mach_msg_header_t head;
+ struct fsys_startup_request startup;
+ }
+ request;
+ struct fsys_startup_reply reply;
+
+ /* Wait for the fsys_startup message... */
+ err = mach_msg (&request.head, (MACH_RCV_MSG | MACH_RCV_INTERRUPT
+ | (timeout ? MACH_RCV_TIMEOUT : 0)),
+ 0, sizeof(request), port, timeout, MACH_PORT_NULL);
+ if (err)
+ return err;
+
+ /* Check whether we actually got a no-senders notification instead. */
+ if (request.head.msgh_id == MACH_NOTIFY_NO_SENDERS)
+ return EDIED;
+
+ /* Construct our reply to the fsys_startup rpc. */
+ reply.head.msgh_size = sizeof(reply);
+ reply.head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request.head.msgh_bits), 0);
+ reply.head.msgh_remote_port = request.head.msgh_remote_port;
+ reply.head.msgh_local_port = MACH_PORT_NULL;
+ reply.head.msgh_seqno = 0;
+ reply.head.msgh_id = request.head.msgh_id + 100;
+ reply.RetCodeType = RetCodeType;
+
+ if (request.head.msgh_id != 22000)
+ reply.RetCode = MIG_BAD_ID;
+ else if (type_check (&request.startup.control_portType, &control_portCheck)
+ || type_check (&request.startup.flagsType, &flagsCheck))
+ reply.RetCode = MIG_BAD_ARGUMENTS;
+ else
+ {
+ mach_msg_type_name_t realnode_type;
+
+ *control = request.startup.control_port;
+
+ reply.RetCode =
+ (*underlying_open_fn) (request.startup.flags,
+ &reply.realnode, &realnode_type, task,
+ cookie);
+
+ reply.realnodeType = realnodeType;
+ reply.realnodeType.msgt_name = realnode_type;
+
+ if (!reply.RetCode && reply.realnode != MACH_PORT_NULL)
+ /* The message can't be simple because of the port. */
+ reply.head.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
+ }
+
+ err = mach_msg (&reply.head, MACH_SEND_MSG | MACH_SEND_INTERRUPT,
+ sizeof(reply), 0,
+ request.head.msgh_remote_port,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if (err == MACH_SEND_INTERRUPTED
+ && reply.realnodeType.msgt_name == MACH_MSG_TYPE_MOVE_SEND)
+ /* For MACH_SEND_INTERRUPTED, we'll have pseudo-received the message
+ and might have to clean up a generated send right. */
+ mach_port_deallocate (mach_task_self (), reply.realnode);
+
+ if (reply.RetCode)
+ /* Make our error return be the earlier one. */
+ err = reply.RetCode;
+
+ return err;
+}
+
+
+error_t
+fshelp_start_translator_long (fshelp_open_fn_t underlying_open_fn,
+ void *cookie, char *name, char *argz,
+ int argz_len, mach_port_t *fds,
+ mach_msg_type_name_t fds_type, int fds_len,
+ mach_port_t *ports,
+ mach_msg_type_name_t ports_type, int ports_len,
+ int *ints, int ints_len,
+ uid_t owner_uid,
+ int timeout, fsys_t *control)
+{
+ error_t err;
+ file_t executable;
+ mach_port_t bootstrap = MACH_PORT_NULL;
+ mach_port_t task = MACH_PORT_NULL;
+ mach_port_t prev_notify, proc, saveport, childproc;
+ int ports_moved = 0;
+
+ /* Find the translator itself. Since argz has zero-separated elements, we
+ can use it as a normal string representing the first element. */
+ executable = file_name_lookup(name, O_EXEC, 0);
+ if (executable == MACH_PORT_NULL)
+ return errno;
+
+ /* Create a bootstrap port for the translator. */
+ err =
+ mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &bootstrap);
+ if (err)
+ goto lose;
+
+ /* Create the task for the translator. */
+ err = task_create (mach_task_self (),
+#ifdef KERN_INVALID_LEDGER
+ NULL, 0, /* OSF Mach */
+#endif
+ 0, &task);
+ if (err)
+ goto lose;
+
+ /* XXX 25 is BASEPRI_USER, which isn't exported by the kernel. Ideally,
+ nice values should be used, perhaps with a simple wrapper to convert
+ them to Mach priorities. */
+ err = task_priority(task, 25, FALSE);
+
+ if (err)
+ goto lose;
+
+ /* Designate TASK as our child and set it's owner accordingly. */
+ proc = getproc ();
+ proc_child (proc, task);
+ err = proc_task2proc (proc, task, &childproc);
+ mach_port_deallocate (mach_task_self (), proc);
+ if (err)
+ goto lose;
+ err = proc_setowner (childproc, owner_uid, owner_uid == (uid_t) -1);
+ mach_port_deallocate (mach_task_self (), childproc);
+ if (err)
+ goto lose;
+
+ assert (ports_len > INIT_PORT_BOOTSTRAP);
+ switch (ports_type)
+ {
+ case MACH_MSG_TYPE_MAKE_SEND:
+ case MACH_MSG_TYPE_MAKE_SEND_ONCE:
+ break;
+
+ case MACH_MSG_TYPE_MOVE_SEND:
+ if (ports[INIT_PORT_BOOTSTRAP] != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), ports[INIT_PORT_BOOTSTRAP]);
+ mach_port_insert_right (mach_task_self (), bootstrap, bootstrap,
+ MACH_MSG_TYPE_MAKE_SEND);
+ break;
+
+ case MACH_MSG_TYPE_COPY_SEND:
+ mach_port_insert_right (mach_task_self (), bootstrap, bootstrap,
+ MACH_MSG_TYPE_MAKE_SEND);
+ break;
+
+ default:
+ abort ();
+ }
+
+ saveport = ports[INIT_PORT_BOOTSTRAP];
+ ports[INIT_PORT_BOOTSTRAP] = bootstrap;
+
+ /* Try and exec the translator in TASK... */
+ err = file_exec (executable, task, EXEC_DEFAULTS,
+ argz, argz_len, 0, 0,
+ fds, fds_type, fds_len,
+ ports, ports_type, ports_len,
+ ints, ints_len, 0, 0, 0, 0);
+ ports_moved = 1;
+
+ if (ports_type == MACH_MSG_TYPE_COPY_SEND)
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ ports[INIT_PORT_BOOTSTRAP] = saveport;
+
+ if (err)
+ goto lose_task;
+
+ /* Ask to be told if TASK dies. */
+ err =
+ mach_port_request_notification(mach_task_self(),
+ bootstrap, MACH_NOTIFY_NO_SENDERS, 0,
+ bootstrap, MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &prev_notify);
+ if (err)
+ goto lose_task;
+
+ /* Ok, cool, we've got a running(?) program, now rendezvous with it if
+ possible using the startup protocol on the bootstrap port... */
+ err = service_fsys_startup(underlying_open_fn, cookie, bootstrap,
+ timeout, control, task);
+
+ lose_task:
+ if (err)
+ task_terminate (task);
+
+ lose:
+ if (!ports_moved)
+ {
+ int i;
+
+ if (fds_type == MACH_MSG_TYPE_MOVE_SEND)
+ for (i = 0; i < fds_len; i++)
+ mach_port_deallocate (mach_task_self (), fds[i]);
+ if (ports_type == MACH_MSG_TYPE_MOVE_SEND)
+ for (i = 0; i < ports_len; i++)
+ mach_port_deallocate (mach_task_self (), ports[i]);
+ }
+ if (bootstrap != MACH_PORT_NULL)
+ mach_port_destroy(mach_task_self(), bootstrap);
+ if (executable != MACH_PORT_NULL)
+ mach_port_deallocate(mach_task_self(), executable);
+ if (task != MACH_PORT_NULL)
+ mach_port_deallocate(mach_task_self(), task);
+
+ return err;
+}
diff --git a/libfshelp/start-translator.c b/libfshelp/start-translator.c
new file mode 100644
index 00000000..ba5418ec
--- /dev/null
+++ b/libfshelp/start-translator.c
@@ -0,0 +1,63 @@
+/*
+ Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fshelp.h"
+#include <unistd.h>
+#include <string.h>
+#include <hurd.h>
+
+error_t
+fshelp_start_translator (fshelp_open_fn_t underlying_open_fn,
+ void *cookie, char *name, char *argz,
+ int argz_len, int timeout, fsys_t *control)
+{
+ mach_port_t ports[INIT_PORT_MAX];
+ mach_port_t fds[STDERR_FILENO + 1];
+ int ints[INIT_INT_MAX];
+ int i;
+ error_t err;
+
+ for (i = 0; i < INIT_PORT_MAX; i++)
+ ports[i] = MACH_PORT_NULL;
+ for (i = 0; i < STDERR_FILENO + 1; i++)
+ fds[i] = MACH_PORT_NULL;
+ bzero (ints, INIT_INT_MAX * sizeof (int));
+
+ ports[INIT_PORT_CWDIR] = getcwdir ();
+ ports[INIT_PORT_CRDIR] = getcrdir ();
+ ports[INIT_PORT_AUTH] = getauth ();
+ fds[STDERR_FILENO] = getdport (STDERR_FILENO);
+
+ err = fshelp_start_translator_long (underlying_open_fn, cookie,
+ name, argz, argz_len,
+ fds, MACH_MSG_TYPE_COPY_SEND,
+ STDERR_FILENO + 1,
+ ports, MACH_MSG_TYPE_COPY_SEND,
+ INIT_PORT_MAX,
+ ints, INIT_INT_MAX,
+ geteuid (),
+ timeout, control);
+ for (i = 0; i < INIT_PORT_MAX; i++)
+ mach_port_deallocate (mach_task_self (), ports[i]);
+ for (i = 0; i <= STDERR_FILENO; i++)
+ mach_port_deallocate (mach_task_self (), fds[i]);
+
+ return err;
+}
diff --git a/libfshelp/touch.c b/libfshelp/touch.c
new file mode 100644
index 00000000..00d90edd
--- /dev/null
+++ b/libfshelp/touch.c
@@ -0,0 +1,49 @@
+/*
+ Copyright (C) 1999, 2007 Free Software Foundation, Inc.
+
+ Written by Thomas Bushnell, BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "fshelp.h"
+
+/* Change the stat times of NODE as indicated by WHAT (from the set TOUCH_*)
+ to the current time. */
+void
+fshelp_touch (struct stat *st, unsigned what,
+ volatile struct mapped_time_value *maptime)
+{
+ struct timeval tv;
+
+ maptime_read (maptime, &tv);
+
+ if (what & TOUCH_ATIME)
+ {
+ st->st_atim.tv_sec = tv.tv_sec;
+ st->st_atim.tv_nsec = tv.tv_usec * 1000;
+ }
+ if (what & TOUCH_CTIME)
+ {
+ st->st_ctim.tv_sec = tv.tv_sec;
+ st->st_ctim.tv_nsec = tv.tv_usec * 1000;
+ }
+ if (what & TOUCH_MTIME)
+ {
+ st->st_mtim.tv_sec = tv.tv_sec;
+ st->st_mtim.tv_nsec = tv.tv_usec * 1000;
+ }
+}
diff --git a/libfshelp/trans.h b/libfshelp/trans.h
new file mode 100644
index 00000000..a9ea6487
--- /dev/null
+++ b/libfshelp/trans.h
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 1994 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <mach.h>
+#include <hurd.h>
+#include <pthread.h>
+#include <hurd/ports.h>
+#include "fshelp.h"
+
+struct transboot
+{
+ struct port_info pi;
+ file_t node;
+ struct trans_link *link;
+};
+
+pthread_spinlock_t _fshelp_translistlock;
+struct trans_link *_fshelp_translist;
diff --git a/libfshelp/transbox-init.c b/libfshelp/transbox-init.c
new file mode 100644
index 00000000..11a1ab41
--- /dev/null
+++ b/libfshelp/transbox-init.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fshelp.h"
+#include <pthread.h>
+
+void
+fshelp_transbox_init (struct transbox *transbox,
+ pthread_mutex_t *lock,
+ void *cookie)
+{
+ transbox->active = MACH_PORT_NULL;
+ transbox->flags = 0;
+ transbox->lock = lock;
+ pthread_cond_init (&transbox->wakeup, NULL);
+ transbox->cookie = cookie;
+}
diff --git a/libfshelp/translated.c b/libfshelp/translated.c
new file mode 100644
index 00000000..2dc724b6
--- /dev/null
+++ b/libfshelp/translated.c
@@ -0,0 +1,28 @@
+/*
+ Copyright (C) 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "fshelp.h"
+
+/* Return true iff there is an active translator on this box */
+int
+fshelp_translated (struct transbox *box)
+{
+ return (box->active != MACH_PORT_NULL);
+}
diff --git a/libfshelp/translator-list.c b/libfshelp/translator-list.c
new file mode 100644
index 00000000..3ece7112
--- /dev/null
+++ b/libfshelp/translator-list.c
@@ -0,0 +1,202 @@
+/* A list of active translators.
+
+ Copyright (C) 2013,14 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <argz.h>
+#include <hurd/fsys.h>
+#include <hurd/ihash.h>
+#include <hurd/ports.h>
+#include <mach.h>
+#include <mach/notify.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+
+#include "fshelp.h"
+
+struct translator
+{
+ struct port_info *pi;
+ char *name;
+ mach_port_t active;
+};
+
+/* The list of active translators. */
+static struct hurd_ihash translator_ihash
+ = HURD_IHASH_INITIALIZER (HURD_IHASH_NO_LOCP);
+
+/* The lock protecting the translator_ihash. */
+static pthread_mutex_t translator_ihash_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static void
+translator_ihash_cleanup (void *element, void *arg)
+{
+ struct translator *translator = element;
+
+ if (translator->pi)
+ ports_port_deref (translator->pi);
+ /* No need to deallocate translator->active, we only keep the name of
+ the port, not a reference. */
+ free (translator->name);
+ free (translator);
+}
+
+/* Record an active translator being bound to the given file name
+ NAME. ACTIVE is the control port of the translator. */
+error_t
+fshelp_set_active_translator (struct port_info *pi,
+ const char *name,
+ mach_port_t active)
+{
+ error_t err = 0;
+ pthread_mutex_lock (&translator_ihash_lock);
+
+ if (! translator_ihash.cleanup)
+ hurd_ihash_set_cleanup (&translator_ihash, translator_ihash_cleanup, NULL);
+
+ struct translator *t = NULL;
+ HURD_IHASH_ITERATE (&translator_ihash, value)
+ {
+ t = value;
+ if (strcmp (name, t->name) == 0)
+ goto update; /* Entry exists. */
+ }
+
+ t = malloc (sizeof (struct translator));
+ if (! t)
+ return ENOMEM;
+
+ t->active = MACH_PORT_NULL;
+ t->pi = NULL;
+ t->name = strdup (name);
+ if (! t->name)
+ {
+ err = errno;
+ free (t);
+ goto out;
+ }
+
+ err = hurd_ihash_add (&translator_ihash, (hurd_ihash_key_t) t, t);
+ if (err)
+ goto out;
+
+ update:
+ if (active)
+ {
+ if (t->pi != pi)
+ {
+ mach_port_t old;
+ err = mach_port_request_notification (mach_task_self (), active,
+ MACH_NOTIFY_DEAD_NAME, 0,
+ pi->port_right,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &old);
+ if (err)
+ return err;
+ if (old != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), old);
+
+ if (t->pi)
+ ports_port_deref (t->pi);
+
+ ports_port_ref (pi);
+ t->pi = pi;
+ }
+
+ /* No need to increment the reference count, we only keep the
+ name, not a reference. */
+ t->active = active;
+ }
+ else
+ hurd_ihash_remove (&translator_ihash, (hurd_ihash_key_t) t);
+
+ out:
+ pthread_mutex_unlock (&translator_ihash_lock);
+ return err;
+}
+
+/* Remove the active translator specified by its control port ACTIVE.
+ If there is no active translator with the given control port, this
+ does nothing. */
+error_t
+fshelp_remove_active_translator (mach_port_t active)
+{
+ error_t err = 0;
+ pthread_mutex_lock (&translator_ihash_lock);
+
+ struct translator *t = NULL;
+ HURD_IHASH_ITERATE (&translator_ihash, value)
+ {
+ struct translator *v = value;
+ if (active == v->active)
+ {
+ t = v;
+ break;
+ }
+ }
+
+ if (t)
+ hurd_ihash_remove (&translator_ihash, (hurd_ihash_key_t) t);
+
+ pthread_mutex_unlock (&translator_ihash_lock);
+ return err;
+}
+
+/* Records the list of active translators into the argz vector
+ specified by TRANSLATORS filtered by FILTER. */
+error_t
+fshelp_get_active_translators (char **translators,
+ size_t *translators_len,
+ fshelp_filter filter)
+{
+ error_t err = 0;
+ pthread_mutex_lock (&translator_ihash_lock);
+
+ HURD_IHASH_ITERATE (&translator_ihash, value)
+ {
+ struct translator *t = value;
+ if (filter)
+ {
+ char *dir = strdup (t->name);
+ if (! dir)
+ {
+ err = ENOMEM;
+ break;
+ }
+
+ err = filter (dirname (dir));
+ free (dir);
+ if (err)
+ {
+ err = 0;
+ continue; /* Skip this entry. */
+ }
+ }
+
+ err = argz_add (translators, translators_len,
+ t->name);
+ if (err)
+ break;
+ }
+
+ pthread_mutex_unlock (&translator_ihash_lock);
+ return err;
+}
diff --git a/libftpconn/Makefile b/libftpconn/Makefile
new file mode 100644
index 00000000..02b72f50
--- /dev/null
+++ b/libftpconn/Makefile
@@ -0,0 +1,33 @@
+# Makefile for libftpconn
+#
+# Copyright (C) 1997, 2012 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libftpconn
+makemode := library
+
+libname = libftpconn
+installhdrs = ftpconn.h
+installhdrsubdir = .
+
+SRCS = addr.c cmd.c create.c cwd.c errs.c names.c open.c reply.c \
+ rmt.c set-type.c stats.c unix.c xfer.c xinl.c fname.c
+
+OBJS = $(SRCS:.c=.o)
+
+CPPFLAGS += -DHAVE_HURD_HURD_TYPES_H -DHAVE_STAT_ST_AUTHOR
+
+include ../Makeconf
diff --git a/libftpconn/addr.c b/libftpconn/addr.c
new file mode 100644
index 00000000..3b668f34
--- /dev/null
+++ b/libftpconn/addr.c
@@ -0,0 +1,79 @@
+/* Send/receive data-connection addresses
+
+ Copyright (C) 1997, 1998 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+#include <ftpconn.h>
+#include "priv.h"
+
+error_t
+ftp_conn_get_pasv_addr (struct ftp_conn *conn, struct sockaddr **addr)
+{
+ int reply;
+ const char *txt;
+ error_t err = ftp_conn_cmd_reopen (conn, "pasv", 0, &reply, &txt);
+
+ if (! err)
+ {
+ if (reply == REPLY_PASV_OK)
+ err = (*(conn->syshooks.pasv_addr ?: ftp_conn_unix_pasv_addr))
+ (conn, txt, addr);
+ else
+ err = unexpected_reply (conn, reply, txt, 0);
+ }
+
+ return err;
+}
+
+error_t
+ftp_conn_send_actv_addr (struct ftp_conn *conn, struct sockaddr *addr)
+{
+ error_t err;
+
+ if (addr == 0)
+ err = EINVAL;
+ else if (addr->sa_family != AF_INET)
+ err = EAFNOSUPPORT;
+ else
+ {
+ char buf[50];
+ int reply;
+ unsigned char *a =
+ (unsigned char *)&((struct sockaddr_in *)addr)->sin_addr.s_addr;
+ unsigned char *p =
+ (unsigned char *)&((struct sockaddr_in *)addr)->sin_port;
+
+ snprintf (buf, sizeof buf, "%d,%d,%d,%d,%d,%d",
+ a[0], a[1], a[2], a[3], p[0], p[1]);
+ err = ftp_conn_cmd_reopen (conn, "port", buf, &reply, 0);
+
+ if (! err)
+ {
+ if (reply == REPLY_OK)
+ err = 0;
+ else
+ err = unexpected_reply (conn, reply, 0, 0);
+ }
+ }
+
+ return err;
+}
diff --git a/libftpconn/cmd.c b/libftpconn/cmd.c
new file mode 100644
index 00000000..803dda7c
--- /dev/null
+++ b/libftpconn/cmd.c
@@ -0,0 +1,169 @@
+/* Send commands to the ftp server
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <arpa/telnet.h>
+
+#include <ftpconn.h>
+#include "priv.h"
+
+/* Version of write that writes all LEN bytes of BUF if possible to FD. */
+static error_t
+_write (int fd, const void *buf, size_t len)
+{
+ while (len > 0)
+ {
+ ssize_t wr = write (fd, buf, len);
+ if (wr < 0)
+ return errno;
+ else if (wr == 0)
+ return EPIPE;
+ buf += wr;
+ len -= wr;
+ }
+ return 0;
+}
+
+static error_t
+_skip_write (int fd, const void *buf, size_t len, size_t *skip)
+{
+ size_t sk = *skip;
+ error_t err = 0;
+
+ if (len > sk)
+ {
+ err = _write (fd, buf + sk, len - sk);
+ *skip = 0;
+ }
+ else
+ *skip = sk - len;
+
+ return err;
+}
+
+/* Ridiculous function to deal with the never-to-occur case of the ftp
+ command being too long for the buffer in ftp_conn_cmd; just writes the
+ portion of the command that wasn't written there. */
+static error_t
+_long_cmd (int fd, const char *cmd, const char *arg, size_t skip)
+{
+ error_t err = _skip_write (fd, cmd, strlen (cmd), &skip);
+ if (!err && arg)
+ {
+ err = _skip_write (fd, " ", 1, &skip);
+ if (! err)
+ err = _skip_write (fd, arg, strlen (arg), &skip);
+ }
+ if (! err)
+ err = _skip_write (fd, "\r\n", 2, &skip);
+ return err;
+}
+
+/* Send the ftp command CMD, with optional argument ARG (if non-zero) to
+ CONN's ftp server. If either of REPLY or REPLY_TXT is non-zero, then a
+ reply is waited for and returned as with ftp_conn_get_reply, otherwise
+ the next reply from the server is left unconsumed. */
+error_t
+ftp_conn_cmd (struct ftp_conn *conn, const char *cmd, const char *arg,
+ int *reply, const char **reply_txt)
+{
+ error_t err = 0;
+
+ if (conn->control < 0)
+ err = EPIPE;
+ else
+ /* (This used to try to call dprintf to output to conn->control, but that
+ function doesn't appear to work.) */
+ {
+ char buf[200];
+ size_t out =
+ snprintf (buf, sizeof buf, arg ? "%s %s\r\n" : "%s\r\n", cmd, arg);
+ err = _write (conn->control, buf, out);
+
+ if (!err && conn->hooks && conn->hooks->cntl_debug)
+ {
+ buf[out - 2] = '\0'; /* Stomp the CR & NL. */
+ (* conn->hooks->cntl_debug) (conn, FTP_CONN_CNTL_DEBUG_CMD, buf);
+ }
+
+ if (!err && out == sizeof buf)
+ err = _long_cmd (conn->control, cmd, arg, sizeof buf);
+ }
+
+ if (!err && (reply || reply_txt))
+ err = ftp_conn_get_reply (conn, reply, reply_txt);
+
+ return err;
+}
+
+/* Send an ftp command to CONN's server, and optionally await a reply as with
+ ftp_conn_cmd, but also open a new connection if it appears that the old
+ one has died (as when the ftp server times it out). */
+error_t
+ftp_conn_cmd_reopen (struct ftp_conn *conn, const char *cmd, const char *arg,
+ int *reply, const char **reply_txt)
+{
+ int _reply;
+ error_t err;
+
+ err = ftp_conn_cmd (conn, cmd, arg, &_reply, reply_txt);
+ if (err == EPIPE || (!err && _reply == REPLY_CLOSED))
+ /* Retry once after reopening the connection. */
+ {
+ err = ftp_conn_open (conn);
+ if (! err)
+ err = ftp_conn_cmd (conn, cmd, arg, reply, reply_txt);
+ }
+ else if (reply)
+ *reply = _reply;
+
+ return err;
+}
+
+/* Send an ftp ABOR command to CONN's server, aborting any transfer in
+ progress. */
+void
+ftp_conn_abort (struct ftp_conn *conn)
+{
+ if (conn->control >= 0)
+ {
+ static const char ip[] = { IAC, IP, IAC };
+ static const char abor[] = { DM, 'a', 'b', 'o', 'r', '\r', '\n' };
+
+ if (conn->hooks && conn->hooks->cntl_debug)
+ (* conn->hooks->cntl_debug) (conn, FTP_CONN_CNTL_DEBUG_CMD, "abor");
+
+ if (send (conn->control, ip, sizeof ip, MSG_OOB) == sizeof ip
+ && write (conn->control, abor, sizeof abor) == sizeof abor)
+ {
+ int reply;
+ error_t err;
+ do
+ err = ftp_conn_get_raw_reply (conn, &reply, 0);
+ while (reply == REPLY_ABORTED);
+ if (reply != REPLY_TRANS_OK && reply != REPLY_ABORT_OK)
+ ftp_conn_close (conn);
+ }
+ else
+ ftp_conn_close (conn);
+ }
+}
diff --git a/libftpconn/create.c b/libftpconn/create.c
new file mode 100644
index 00000000..20a64561
--- /dev/null
+++ b/libftpconn/create.c
@@ -0,0 +1,87 @@
+/* Create a new ftp connection
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <ftpconn.h>
+
+/* Create a new ftp connection as specified by PARAMS, and return it in CONN;
+ HOOKS contains customization hooks used by the connection. Neither PARAMS
+ nor HOOKS is copied, so a copy of it should be made if necessary before
+ calling this function; if it should be freed later, a FINI hook may be
+ used to do so. */
+error_t
+ftp_conn_create (const struct ftp_conn_params *params,
+ const struct ftp_conn_hooks *hooks,
+ struct ftp_conn **conn)
+{
+ error_t err;
+ struct ftp_conn *new = malloc (sizeof (struct ftp_conn));
+
+ if (! new)
+ return ENOMEM;
+
+ new->control = -1;
+ new->line = 0;
+ new->line_sz = 0;
+ new->line_offs = 0;
+ new->line_len = 0;
+ new->reply_txt = 0;
+ new->reply_txt_sz = 0;
+ new->params = params;
+ new->hooks = hooks;
+ new->syshooks_valid = 0;
+ new->use_passive = 1;
+ new->actv_data_addr = 0;
+ new->cwd = 0;
+ new->type = 0;
+ bzero (&new->syshooks, sizeof new->syshooks);
+
+ if (new->hooks && new->hooks->init)
+ err = (*new->hooks->init) (new);
+ else
+ err = 0;
+
+ if (err)
+ ftp_conn_free (new);
+ else
+ *conn = new;
+
+ return err;
+}
+
+/* Free the ftp connection CONN, closing it first, and freeing all resources
+ it uses. */
+void
+ftp_conn_free (struct ftp_conn *conn)
+{
+ ftp_conn_close (conn);
+ if (conn->hooks && conn->hooks->fini)
+ (* conn->hooks->fini) (conn);
+ if (conn->line)
+ free (conn->line);
+ if (conn->reply_txt)
+ free (conn->reply_txt);
+ if (conn->actv_data_addr)
+ free (conn->actv_data_addr);
+ free (conn);
+}
diff --git a/libftpconn/cwd.c b/libftpconn/cwd.c
new file mode 100644
index 00000000..868150f1
--- /dev/null
+++ b/libftpconn/cwd.c
@@ -0,0 +1,113 @@
+/* Get/set connection current working directory
+
+ Copyright (C) 1997, 1998 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <ftpconn.h>
+#include "priv.h"
+
+static error_t
+_cache_cwd (struct ftp_conn *conn, int reopen)
+{
+ int reply;
+ const char *txt;
+ error_t err =
+ (reopen ? ftp_conn_cmd_reopen : ftp_conn_cmd) (conn, "pwd", 0, &reply, &txt);
+
+ if (! err)
+ {
+ if (reply == REPLY_DIR_NAME)
+ {
+ char *cwd = malloc (strlen (txt));
+ if (! cwd)
+ err = ENOMEM;
+ else if (sscanf (txt, "\"%[^\"]\"", cwd) != 1)
+ err = EGRATUITOUS;
+ else
+ {
+ if (conn->cwd)
+ free (conn->cwd);
+ conn->cwd = cwd;
+ }
+ }
+ else
+ err = unexpected_reply (conn, reply, txt, 0);
+ }
+
+ return err;
+}
+
+/* Return a malloced string containing CONN's working directory in CWD. */
+error_t
+ftp_conn_get_cwd (struct ftp_conn *conn, char **cwd)
+{
+ error_t err = 0;
+ if (! conn->cwd)
+ err = _cache_cwd (conn, 1);
+ if (! err)
+ {
+ *cwd = strdup (conn->cwd);
+ if (! *cwd)
+ err = ENOMEM;
+ }
+ return err;
+}
+
+/* Return a malloced string containing CONN's working directory in CWD. */
+error_t
+ftp_conn_cwd (struct ftp_conn *conn, const char *cwd)
+{
+ error_t err = 0;
+ if (conn->cwd && strcmp (conn->cwd, cwd) == 0)
+ err = 0;
+ else
+ {
+ int reply;
+ const char *txt;
+ err = ftp_conn_cmd_reopen (conn, "cwd", cwd, &reply, &txt);
+ if (! err)
+ {
+ if (reply == REPLY_FCMD_OK)
+ err = _cache_cwd (conn, 0);
+ else
+ err = unexpected_reply (conn, reply, txt, ftp_conn_poss_file_errs);
+ }
+ }
+ return err;
+}
+
+/* Return a malloced string containing CONN's working directory in CWD. */
+error_t
+ftp_conn_cdup (struct ftp_conn *conn)
+{
+ int reply;
+ const char *txt;
+ error_t err = ftp_conn_cmd_reopen (conn, "cdup", 0, &reply, &txt);
+ if (! err)
+ {
+ if (reply == REPLY_OK)
+ err = _cache_cwd (conn, 0);
+ else
+ err = unexpected_reply (conn, reply, txt, ftp_conn_poss_file_errs);
+ }
+ return err;
+}
diff --git a/libftpconn/errs.c b/libftpconn/errs.c
new file mode 100644
index 00000000..5a93fb55
--- /dev/null
+++ b/libftpconn/errs.c
@@ -0,0 +1,32 @@
+/* Error codes we think may result from file operations we do
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <ftpconn.h>
+#include "priv.h"
+
+/* Error codes we think may result from file operations we do. */
+const error_t
+ftp_conn_poss_file_errs[] =
+{
+ EIO, ENOENT, EPERM, EACCES, ENOTDIR, ENAMETOOLONG, ELOOP, EISDIR, EROFS,
+ EMFILE, ENFILE, ENXIO, EOPNOTSUPP, ENOSPC, EDQUOT, ETXTBSY, EEXIST,
+ 0
+};
diff --git a/libftpconn/fname.c b/libftpconn/fname.c
new file mode 100644
index 00000000..3be6eeec
--- /dev/null
+++ b/libftpconn/fname.c
@@ -0,0 +1,78 @@
+/* Filename frobbing
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+
+#include "ftpconn.h"
+
+/* Give a name which refers to a directory file, and a name in that
+ directory, this should return in COMPOSITE the composite name referring to
+ that name in that directory, in malloced storage. */
+error_t
+ftp_conn_append_name (struct ftp_conn *conn,
+ const char *dir, const char *name,
+ char **composite)
+{
+ error_t err = ftp_conn_validate_syshooks (conn);
+ if (err)
+ return err;
+ else if (conn->syshooks.append_name)
+ return (*conn->syshooks.append_name) (conn, dir, name, composite);
+ else
+ return EOPNOTSUPP;
+}
+
+/* If the name of a file COMPOSITE is a composite name (containing both a
+ filename and a directory name), this function will return the name
+ component only in BASE, in malloced storage, otherwise it simply returns a
+ newly malloced copy of COMPOSITE in BASE. */
+error_t
+ftp_conn_basename (struct ftp_conn *conn, const char *composite, char **base)
+{
+ error_t err = ftp_conn_validate_syshooks (conn);
+
+ if (err)
+ return err;
+
+ if (conn->syshooks.basename)
+ {
+ size_t in_size = strlen (composite) + 1;
+ char *in = strdup (composite), *out = in;
+
+ if (! in)
+ return ENOMEM;
+
+ err = (*conn->syshooks.basename) (conn, &out);
+ if (err || out != in)
+ {
+ if (!err && out >= in && out < in + in_size)
+ /* OUT uses storage from IN, but not at the beginning. */
+ out = strdup (out);
+ free (in);
+ }
+
+ if (! err)
+ *base = out;
+
+ return err;
+ }
+ else
+ return EOPNOTSUPP;
+}
diff --git a/libftpconn/ftpconn.h b/libftpconn/ftpconn.h
new file mode 100644
index 00000000..6bff5918
--- /dev/null
+++ b/libftpconn/ftpconn.h
@@ -0,0 +1,394 @@
+/* Manage an ftp connection
+
+ Copyright (C) 1997,2001,02 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __FTPCONN_H__
+#define __FTPCONN_H__
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <features.h>
+
+#define __need_error_t
+#include <errno.h>
+
+#ifndef __error_t_defined
+typedef int error_t;
+#define __error_t_defined
+#endif
+
+#ifdef FTP_CONN_DEFINE_EI
+#define FTP_CONN_EI
+#else
+#define FTP_CONN_EI __extern_inline
+#endif
+
+struct ftp_conn;
+struct ftp_conn_params;
+struct ftp_conn_stat;
+
+/* The type of the function called by ...get_stats to add each new stat.
+ NAME is the file in question, STAT is stat info about it, and if NAME is a
+ symlink, SYMLINK_TARGET is what it is linked to, or 0 if it's not a
+ symlink. NAME and SYMLINK_TARGET should be copied if they are used
+ outside of this function. HOOK is as passed into ...get_stats. */
+typedef error_t (*ftp_conn_add_stat_fun_t) (const char *name,
+# if _FILE_OFFSET_BITS == 64
+ const struct stat *stat,
+# else
+ const struct stat64 *stat,
+# endif
+ const char *symlink_target,
+ void *hook);
+
+/* Hooks that customize behavior for particular types of remote system. */
+struct ftp_conn_syshooks
+{
+ /* Should return in ADDR a malloced struct sockaddr containing the address
+ of the host referenced by the PASV reply contained in TXT. */
+ error_t (*pasv_addr) (struct ftp_conn *conn, const char *txt,
+ struct sockaddr **addr);
+
+ /* Look at the error string in TXT, and try to guess an error code to
+ return. If POSS_ERRS is non-zero, it contains a list of errors
+ that are likely to occur with the previous command, terminated with 0.
+ If no match is found and POSS_ERRS is non-zero, the first error in
+ POSS_ERRS should be returned by default. */
+ error_t (*interp_err) (struct ftp_conn *conn, const char *txt,
+ const error_t *poss_errs);
+
+ /* Start an operation to get a list of file-stat structures for NAME (this
+ is often similar to ftp_conn_start_dir, but with OS-specific flags), and
+ return a file-descriptor for reading on, and a state structure in STATE
+ suitable for passing to cont_get_stats. If CONTENTS is true, NAME must
+ refer to a directory, and the contents will be returned, otherwise, the
+ (single) result will refer to NAME. */
+ error_t (*start_get_stats) (struct ftp_conn *conn, const char *name,
+ int contents, int *fd, void **state);
+
+ /* Read stats information from FD, calling ADD_STAT for each new stat (HOOK
+ is passed to ADD_STAT). FD and STATE should be returned from
+ start_get_stats. If this function returns EAGAIN, then it should be
+ called again to finish the job (possibly after calling select on FD); if
+ it returns 0, then it is finishe,d and FD and STATE are deallocated. */
+ error_t (*cont_get_stats) (struct ftp_conn *conn, int fd, void *state,
+ ftp_conn_add_stat_fun_t add_stat, void *hook);
+
+ /* Give a name which refers to a directory file, and a name in that
+ directory, this should return in COMPOSITE the composite name referring
+ to that name in that directory, in malloced storage. */
+ error_t (*append_name) (struct ftp_conn *conn,
+ const char *dir, const char *name,
+ char **composite);
+
+ /* If the name of a file *NAME is a composite name (containing both a
+ filename and a directory name), this function should change *NAME to be
+ the name component only; if the result is shorter than the original
+ *NAME, the storage pointed to it may be modified, otherwise, *NAME
+ should be changed to point to malloced storage holding the result, which
+ will be freed by the caller. */
+ error_t (*basename) (struct ftp_conn *conn, char **name);
+};
+
+/* Type parameter for the cntl_debug hook. */
+#define FTP_CONN_CNTL_DEBUG_CMD 1
+#define FTP_CONN_CNTL_DEBUG_REPLY 2
+
+/* Type parameter for the get_login_param hook. */
+#define FTP_CONN_GET_LOGIN_PARAM_USER 1
+#define FTP_CONN_GET_LOGIN_PARAM_PASS 2
+#define FTP_CONN_GET_LOGIN_PARAM_ACCT 3
+
+/* General connection customization. */
+struct ftp_conn_hooks
+{
+ /* If non-zero, should look at the SYST reply in SYST, and fill in CONN's
+ syshooks (with ftp_conn_set_hooks) appropriately; SYST may be zero if
+ the remote system doesn't support that command. If zero, then the
+ default ftp_conn_choose_syshooks is used. */
+ void (*choose_syshooks) (struct ftp_conn *conn, const char *syst);
+
+ /* If non-zero, called during io on the ftp control connection -- TYPE is
+ FTP_CONN_CNTL_DEBUG_CMD for commands, and FTP_CONN_CNTL_DEBUG_REPLY for
+ replies; TXT is the actual text. */
+ void (*cntl_debug) (struct ftp_conn *conn, int type, const char *txt);
+
+ /* Called after CONN's connection the server has been opened (or reopened). */
+ void (*opened) (struct ftp_conn *conn);
+
+ /* If the remote system requires some login parameter that isn't available,
+ this hook is called to try and get it, returning a value in TXT. The
+ return value should be in a malloced block of memory. The returned
+ value will only be used once; if it's desired that it should `stick',
+ the user may modify the value stored in CONN's params field, but that is
+ an issue outside of the scope of this interface -- params are only read,
+ never written. */
+ error_t (*get_login_param) (struct ftp_conn *conn, int type, char **txt);
+
+ /* Called after CONN's connection the server has closed for some reason. */
+ void (*closed) (struct ftp_conn *conn);
+
+ /* Called when CONN is initially created before any other hook calls. An
+ error return causes the creation to fail with that error code. */
+ error_t (*init) (struct ftp_conn *conn);
+
+ /* Called when CONN is about to be destroyed. No hook calls are ever made
+ after this one. */
+ void (*fini) (struct ftp_conn *conn);
+
+ /* This hook should return true if the current thread has been interrupted
+ in some way, and EINTR (or a short count in some cases) should be
+ returned from a blocking function. */
+ int (*interrupt_check) (struct ftp_conn *conn);
+};
+
+/* A single ftp connection. */
+struct ftp_conn
+{
+ const struct ftp_conn_params *params; /* machine, user, &c */
+ const struct ftp_conn_hooks *hooks; /* Customization hooks. */
+
+ struct ftp_conn_syshooks syshooks; /* host-dependent hook functions */
+ int syshooks_valid : 1; /* True if the system type has been determined. */
+
+ int control; /* fd for ftp control connection */
+
+ char *line; /* buffer for reading control replies */
+ size_t line_sz; /* allocated size of LINE */
+ size_t line_offs; /* Start of unread input in LINE. */
+ size_t line_len; /* End of the contents in LINE. */
+
+ char *reply_txt; /* A buffer for the text of entire replies */
+ size_t reply_txt_sz; /* size of it */
+
+ char *cwd; /* Last know CWD, or 0 if unknown. */
+ const char *type; /* Connection type, or 0 if default. */
+
+ void *hook; /* Random user data. */
+
+ int use_passive : 1; /* If true, first try passive data conns. */
+
+ struct sockaddr *actv_data_addr;/* Address of port for active data conns. */
+};
+
+/* Parameters for an ftp connection; doesn't include any actual connection
+ state. */
+struct ftp_conn_params
+{
+ void *addr; /* Address. */
+ size_t addr_len; /* Length in bytes of ADDR. */
+ int addr_type; /* Type of ADDR (AF_*). */
+
+ char *user, *pass, *acct; /* Parameters for logging into ftp. */
+};
+
+/* Unix hooks */
+extern error_t ftp_conn_unix_pasv_addr (struct ftp_conn *conn, const char *txt,
+ struct sockaddr **addr);
+extern error_t ftp_conn_unix_interp_err (struct ftp_conn *conn, const char *txt,
+ const error_t *poss_errs);
+extern error_t ftp_conn_unix_start_get_stats (struct ftp_conn *conn,
+ const char *name,
+ int contents, int *fd,
+ void **state);
+extern error_t ftp_conn_unix_cont_get_stats (struct ftp_conn *conn,
+ int fd, void *state,
+ ftp_conn_add_stat_fun_t add_stat,
+ void *hook);
+error_t ftp_conn_unix_append_name (struct ftp_conn *conn,
+ const char *dir, const char *name,
+ char **composite);
+error_t ftp_conn_unix_basename (struct ftp_conn *conn, char **name);
+
+extern struct ftp_conn_syshooks ftp_conn_unix_syshooks;
+
+error_t
+ftp_conn_get_raw_reply (struct ftp_conn *conn,
+ int *reply, const char **reply_txt);
+error_t
+ftp_conn_get_reply (struct ftp_conn *conn, int *reply, const char **reply_txt);
+
+error_t
+ftp_conn_cmd (struct ftp_conn *conn, const char *cmd, const char *arg,
+ int *reply, const char **reply_txt);
+
+error_t
+ftp_conn_cmd_reopen (struct ftp_conn *conn, const char *cmd, const char *arg,
+ int *reply, const char **reply_txt);
+
+void ftp_conn_abort (struct ftp_conn *conn);
+
+/* Sets CONN's syshooks to a copy of SYSHOOKS. */
+void ftp_conn_set_syshooks (struct ftp_conn *conn,
+ struct ftp_conn_syshooks *syshooks);
+
+error_t ftp_conn_open (struct ftp_conn *conn);
+
+void ftp_conn_close (struct ftp_conn *conn);
+
+extern error_t ftp_conn_validate_syshooks (struct ftp_conn *conn);
+
+#if defined(__USE_EXTERN_INLINES) || defined(FTP_CONN_DEFINE_EI)
+/* Makes sure that CONN's syshooks are set according to the remote system
+ type. */
+FTP_CONN_EI error_t
+ftp_conn_validate_syshooks (struct ftp_conn *conn)
+{
+ if (conn->syshooks_valid)
+ return 0;
+ else
+ /* Opening the connection should set the syshooks. */
+ return ftp_conn_open (conn);
+}
+#endif /* Use extern inlines. */
+
+/* Create a new ftp connection as specified by PARAMS, and return it in CONN;
+ HOOKS contains customization hooks used by the connection. Neither PARAMS
+ nor HOOKS is copied, so a copy of it should be made if necessary before
+ calling this function; if it should be freed later, a FINI hook may be
+ used to do so. */
+error_t ftp_conn_create (const struct ftp_conn_params *params,
+ const struct ftp_conn_hooks *hooks,
+ struct ftp_conn **conn);
+
+/* Free the ftp connection CONN, closing it first, and freeing all resources
+ it uses. */
+void ftp_conn_free (struct ftp_conn *conn);
+
+/* Start a transfer command CMD (and optional args ...), returning a file
+ descriptor in DATA. POSS_ERRS is a list of errnos to try matching
+ against any resulting error text. */
+error_t
+ftp_conn_start_transfer (struct ftp_conn *conn,
+ const char *cmd, const char *arg,
+ const error_t *poss_errs,
+ int *data);
+
+/* Wait for the reply signalling the end of a data transfer. */
+error_t ftp_conn_finish_transfer (struct ftp_conn *conn);
+
+/* Start retreiving file NAME over CONN, returning a file descriptor in DATA
+ over which the data can be read. */
+error_t ftp_conn_start_retrieve (struct ftp_conn *conn, const char *name, int *data);
+
+/* Start retreiving a list of files in NAME over CONN, returning a file
+ descriptor in DATA over which the data can be read. */
+error_t ftp_conn_start_list (struct ftp_conn *conn, const char *name, int *data);
+
+/* Start retreiving a directory listing of NAME over CONN, returning a file
+ descriptor in DATA over which the data can be read. */
+error_t ftp_conn_start_dir (struct ftp_conn *conn, const char *name, int *data);
+
+/* Start storing into file NAME over CONN, returning a file descriptor in DATA
+ into which the data can be written. */
+error_t ftp_conn_start_store (struct ftp_conn *conn, const char *name, int *data);
+
+/* Transfer the output of SRC_CMD/SRC_NAME on SRC_CONN to DST_NAME on
+ DST_CONN, moving the data directly between servers. */
+error_t
+ftp_conn_rmt_transfer (struct ftp_conn *src_conn,
+ const char *src_cmd, const char *src_name,
+ const int *src_poss_errs,
+ struct ftp_conn *dst_conn, const char *dst_name);
+
+/* Copy the SRC_NAME on SRC_CONN to DST_NAME on DST_CONN, moving the data
+ directly between servers. */
+error_t
+ftp_conn_rmt_copy (struct ftp_conn *src_conn, const char *src_name,
+ struct ftp_conn *dst_conn, const char *dst_name);
+
+/* Return a malloced string containing CONN's working directory in CWD. */
+error_t ftp_conn_get_cwd (struct ftp_conn *conn, char **cwd);
+
+/* Return a malloced string containing CONN's working directory in CWD. */
+error_t ftp_conn_cwd (struct ftp_conn *conn, const char *cwd);
+
+/* Return a malloced string containing CONN's working directory in CWD. */
+error_t ftp_conn_cdup (struct ftp_conn *conn);
+
+/* Set the ftp connection type of CONN to TYPE, or return an error. */
+error_t ftp_conn_set_type (struct ftp_conn *conn, const char *type);
+
+/* Start an operation to get a list of file-stat structures for NAME (this
+ is often similar to ftp_conn_start_dir, but with OS-specific flags), and
+ return a file-descriptor for reading on, and a state structure in STATE
+ suitable for passing to cont_get_stats. If CONTENTS is true, NAME must
+ refer to a directory, and the contents will be returned, otherwise, the
+ (single) result will refer to NAME. */
+error_t ftp_conn_start_get_stats (struct ftp_conn *conn,
+ const char *name, int contents,
+ int *fd, void **state);
+
+/* Read stats information from FD, calling ADD_STAT for each new stat (HOOK
+ is passed to ADD_STAT). FD and STATE should be returned from
+ start_get_stats. If this function returns EAGAIN, then it should be
+ called again to finish the job (possibly after calling select on FD); if
+ it returns 0, then it is finishe,d and FD and STATE are deallocated. */
+error_t ftp_conn_cont_get_stats (struct ftp_conn *conn, int fd, void *state,
+ ftp_conn_add_stat_fun_t add_stat, void *hook);
+
+/* Get a list of file-stat structures for NAME, calling ADD_STAT for each one
+ (HOOK is passed to ADD_STAT). If CONTENTS is true, NAME must refer to a
+ directory, and the contents will be returned, otherwise, the (single)
+ result will refer to NAME. This function may block. */
+error_t ftp_conn_get_stats (struct ftp_conn *conn,
+ const char *name, int contents,
+ ftp_conn_add_stat_fun_t add_stat, void *hook);
+
+/* The type of the function called by ...get_names to add each new name.
+ NAME is the name in question and HOOK is as passed into ...get_stats. */
+typedef error_t (*ftp_conn_add_name_fun_t) (const char *name, void *hook);
+
+/* Start an operation to get a list of filenames in the directory NAME, and
+ return a file-descriptor for reading on, and a state structure in STATE
+ suitable for passing to cont_get_names. */
+error_t ftp_conn_start_get_names (struct ftp_conn *conn,
+ const char *name, int *fd, void **state);
+
+/* Read filenames from FD, calling ADD_NAME for each new NAME (HOOK is passed
+ to ADD_NAME). FD and STATE should be returned from start_get_stats. If
+ this function returns EAGAIN, then it should be called again to finish the
+ job (possibly after calling select on FD); if it returns 0, then it is
+ finishe,d and FD and STATE are deallocated. */
+error_t ftp_conn_cont_get_names (struct ftp_conn *conn, int fd, void *state,
+ ftp_conn_add_name_fun_t add_name, void *hook);
+
+/* Get a list of names in the directory NAME, calling ADD_NAME for each one
+ (HOOK is passed to ADD_NAME). This function may block. */
+error_t ftp_conn_get_names (struct ftp_conn *conn, const char *name,
+ ftp_conn_add_name_fun_t add_name, void *hook);
+
+/* Give a name which refers to a directory file, and a name in that
+ directory, this should return in COMPOSITE the composite name referring to
+ that name in that directory, in malloced storage. */
+error_t ftp_conn_append_name (struct ftp_conn *conn,
+ const char *dir, const char *name,
+ char **composite);
+
+/* If the name of a file COMPOSITE is a composite name (containing both a
+ filename and a directory name), this function will return the name
+ component only in BASE, in malloced storage, otherwise it simply returns a
+ newly malloced copy of COMPOSITE in BASE. */
+error_t ftp_conn_basename (struct ftp_conn *conn,
+ const char *composite, char **base);
+
+#endif /* __FTPCONN_H__ */
diff --git a/libftpconn/names.c b/libftpconn/names.c
new file mode 100644
index 00000000..9bf0767c
--- /dev/null
+++ b/libftpconn/names.c
@@ -0,0 +1,237 @@
+/* Fetch directory file names
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <ftpconn.h>
+
+struct get_names_state
+{
+ char *name; /* Last read (maybe partial) name. */
+ size_t name_len; /* Valid length of NAME, *not including* '\0'. */
+ size_t name_alloced; /* Allocated size of NAME (>= NAME_LEN). */
+ int name_partial; /* True if NAME isn't complete. */
+
+ size_t buf_len; /* Length of contents in BUF. */
+ char buf[7000];
+};
+
+/* Start an operation to get a list of filenames in the directory NAME, and
+ return a file-descriptor for reading on, and a state structure in STATE
+ suitable for passing to cont_get_names. */
+error_t
+ftp_conn_start_get_names (struct ftp_conn *conn,
+ const char *name, int *fd, void **state)
+{
+ error_t err;
+ struct get_names_state *s = malloc (sizeof (struct get_names_state));
+
+ if (! s)
+ return ENOMEM;
+
+ err = ftp_conn_start_list (conn, name, fd);
+
+ if (err)
+ free (s);
+ else
+ {
+ s->name = 0;
+ s->name_len = s->name_alloced = 0;
+ s->name_partial = 0;
+ s->buf_len = 0;
+ *state = s;
+ }
+
+ return err;
+}
+
+/* Read filenames from FD, calling ADD_NAME for each new NAME (HOOK is passed
+ to ADD_NAME). FD and STATE should be returned from start_get_names. If
+ this function returns EAGAIN, then it should be called again to finish the
+ job (possibly after calling select on FD); if it returns 0, then it is
+ finishe,d and FD and STATE are deallocated. */
+error_t
+ftp_conn_cont_get_names (struct ftp_conn *conn, int fd, void *state,
+ ftp_conn_add_name_fun_t add_name, void *hook)
+{
+ char *p, *nl;
+ ssize_t rd;
+ size_t name_len;
+ error_t err = 0;
+ struct get_names_state *s = state;
+ int (*icheck) (struct ftp_conn *conn) = conn->hooks->interrupt_check;
+
+ /* We always consume full lines, so we know that we have to read more when
+ we first get called. */
+ rd = read (fd, s->buf + s->buf_len, sizeof (s->buf) - s->buf_len);
+ if (rd < 0)
+ {
+ err = errno;
+ goto finished;
+ }
+
+ if (icheck && (*icheck) (conn))
+ {
+ err = EINTR;
+ goto finished;
+ }
+
+ if (rd == 0)
+ /* EOF */
+ if (s->buf_len == 0)
+ /* We're done! Clean up and return the result in NAMES. */
+ goto finished;
+ else
+ /* Partial line at end of file? */
+ nl = s->buf + s->buf_len;
+ else
+ /* Look for a new line in what we read (we know that there weren't any in
+ the buffer before that). */
+ {
+ nl = memchr (s->buf + s->buf_len, '\n', rd);
+ s->buf_len += rd;
+ }
+
+ if (!nl && s->buf_len < sizeof (s->buf))
+ /* We didn't find any newlines (which implies we didn't hit EOF), and we
+ still have room to grow the buffer, so just wait until next time to do
+ anything. */
+ return EAGAIN;
+
+ /* Where we start parsing. */
+ p = s->buf;
+
+ do
+ {
+ /* Fill in S->name, possibly extending it from a previous buffer. */
+ name_len = (nl ? nl - p : s->buf + s->buf_len - p);
+ if (name_len > 0 && p[name_len - 1] == '\r')
+ name_len--;
+ if (name_len > 0)
+ /* Extending s->name. */
+ {
+ size_t old_len = s->name_len;
+ size_t total_len = old_len + name_len + 1;
+
+ if (total_len > s->name_alloced)
+ {
+ char *new_name = realloc (s->name, total_len);
+ if (! new_name)
+ goto enomem;
+ s->name = new_name;
+ s->name_alloced = total_len;
+ }
+
+ strncpy (s->name + old_len, p, name_len);
+ s->name[old_len + name_len] = '\0';
+ s->name_len = total_len - 1;
+ }
+
+ if (nl)
+ {
+ char *name = s->name;
+
+ if (conn->syshooks.basename)
+ /* Fixup any screwy names returned by the server. */
+ {
+ err = (*conn->syshooks.basename) (conn, &name);
+ if (err)
+ goto finished;
+ }
+
+ /* Call the callback function to process the current entry. */
+ err = (*add_name) (name, hook);
+
+ if (name < s->name || name > s->name + s->name_len)
+ /* User-allocated NAME from the fix_nlist_name hook. */
+ free (name);
+
+ if (err)
+ goto finished;
+
+ s->name_len = 0;
+ s->name_partial = 0;
+
+ p = nl + 1;
+ nl = memchr (p, '\n', s->buf + s->buf_len - p);
+ }
+ else
+ /* We found no newline, so the name extends past what we read; we'll
+ try to read more next time. */
+ {
+ s->name_partial = 1;
+ /* Skip over the partial name for the next iteration. */
+ p += name_len;
+ }
+ }
+ while (nl);
+
+ /* Move any remaining characters in the buffer to the beginning for the
+ next call. */
+ s->buf_len -= (p - s->buf);
+ if (s->buf_len > 0)
+ memmove (s->buf, p, s->buf_len);
+
+ /* Try again later. */
+ return EAGAIN;
+
+enomem:
+ /* Some memory allocation failed. */
+ err = ENOMEM;
+
+finished:
+ /* We're finished (with an error if ERR != 0), deallocate everything &
+ return. */
+ if (s->name)
+ free (s->name);
+ free (s);
+ close (fd);
+
+ if (err && rd > 0)
+ ftp_conn_abort (conn);
+ else if (err)
+ ftp_conn_finish_transfer (conn);
+ else
+ err = ftp_conn_finish_transfer (conn);
+
+ return err;
+}
+
+/* Get a list of names in the directory NAME, calling ADD_NAME for each one
+ (HOOK is passed to ADD_NAME). This function may block. */
+error_t
+ftp_conn_get_names (struct ftp_conn *conn, const char *name,
+ ftp_conn_add_name_fun_t add_name, void *hook)
+{
+ int fd;
+ void *state;
+ error_t err = ftp_conn_start_get_names (conn, name, &fd, &state);
+
+ if (err)
+ return err;
+
+ do
+ err = ftp_conn_cont_get_names (conn, fd, state, add_name, hook);
+ while (err == EAGAIN);
+
+ return err;
+}
diff --git a/libftpconn/open.c b/libftpconn/open.c
new file mode 100644
index 00000000..f52bf4d0
--- /dev/null
+++ b/libftpconn/open.c
@@ -0,0 +1,252 @@
+/* Connection initiation
+
+ Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+
+#include <ftpconn.h>
+#include "priv.h"
+
+static error_t
+ftp_conn_login (struct ftp_conn *conn)
+{
+ int reply;
+ error_t err = 0;
+ const struct ftp_conn_params *p = conn->params;
+
+ err = ftp_conn_cmd (conn, "user", p->user ?: "anonymous", &reply, 0);
+
+ if (!err && reply == REPLY_NEED_ACCT)
+ {
+ char *acct = p->acct;
+ if (!acct && conn->hooks && conn->hooks->get_login_param)
+ err = (* conn->hooks->get_login_param) (conn,
+ FTP_CONN_GET_LOGIN_PARAM_ACCT,
+ &acct);
+ if (! err)
+ err = acct ? ftp_conn_cmd (conn, "acct", acct, &reply, 0) : EACCES;
+ if (acct && !p->acct)
+ free (acct);
+ }
+
+ if (!err && reply == REPLY_NEED_PASS)
+ {
+ char *pass = p->pass;
+ if (!pass && conn->hooks && conn->hooks->get_login_param)
+ err = (* conn->hooks->get_login_param) (conn,
+ FTP_CONN_GET_LOGIN_PARAM_PASS,
+ &pass);
+ if (! err)
+ {
+ if (pass)
+ err = ftp_conn_cmd (conn, "pass", pass, &reply, 0);
+ else
+ {
+ pass = getenv ("USER");
+ if (! pass)
+ pass = getenv ("LOGNAME");
+ if (! pass)
+ {
+ struct passwd *pe = getpwuid (getuid ());
+ pass = pe ? pe->pw_name : "?";
+ }
+
+ /* Append a '@' */
+ pass = strdup (pass);
+ if (pass)
+ pass = realloc (pass, strlen (pass) + 1);
+ if (pass)
+ {
+ strcat (pass, "@");
+ err = ftp_conn_cmd (conn, "pass", pass, &reply, 0);
+ }
+ else
+ err = ENOMEM;
+ }
+ }
+ if (pass && !p->pass)
+ free (pass);
+ }
+
+ if (!err && reply != REPLY_LOGIN_OK)
+ {
+ if (REPLY_IS_FAILURE (reply))
+ err = EACCES;
+ else
+ err = unexpected_reply (conn, reply, 0, 0);
+ }
+
+ return err;
+}
+
+static error_t
+ftp_conn_hello (struct ftp_conn *conn)
+{
+ int reply;
+ error_t err;
+
+ do
+ err = ftp_conn_get_reply (conn, &reply, 0);
+ while (!err && reply == REPLY_DELAY);
+
+ if (err)
+ return err;
+
+ if (reply == REPLY_CLOSED)
+ return ECONNREFUSED;
+ if (reply != REPLY_HELLO)
+ return EGRATUITOUS;
+
+ return 0;
+}
+
+/* Sets CONN's syshooks to a copy of SYSHOOKS. */
+void
+ftp_conn_set_syshooks (struct ftp_conn *conn, struct ftp_conn_syshooks *syshooks)
+{
+ conn->syshooks = *syshooks;
+}
+
+void
+ftp_conn_choose_syshooks (struct ftp_conn *conn, const char *syst)
+{
+ if (!syst || (strncasecmp (syst, "UNIX", 4) == 0 && !isalnum (syst[4])))
+ ftp_conn_set_syshooks (conn, &ftp_conn_unix_syshooks);
+}
+
+/* Sets CONN's syshooks by querying the remote system to see what type it is. */
+static error_t
+ftp_conn_sysify (struct ftp_conn *conn)
+{
+ int reply;
+ const char *txt;
+ error_t err = ftp_conn_cmd (conn, "syst", 0, &reply, &txt);
+
+ if (! err)
+ {
+ if (reply == REPLY_SYSTYPE ||
+ reply == REPLY_BAD_CMD || reply == REPLY_UNIMP_CMD || REPLY_NO_LOGIN)
+ {
+ if (reply == REPLY_BAD_CMD || reply == REPLY_UNIMP_CMD
+ || reply == REPLY_NO_LOGIN)
+ txt = 0;
+ if (conn->hooks && conn->hooks->choose_syshooks)
+ (*conn->hooks->choose_syshooks) (conn, txt);
+ else
+ ftp_conn_choose_syshooks (conn, txt);
+ conn->syshooks_valid = 1;
+ }
+ else
+ err = unexpected_reply (conn, reply, txt, 0);
+ }
+
+ return err;
+}
+
+error_t
+ftp_conn_open (struct ftp_conn *conn)
+{
+ static int ftp_port = 0;
+ int csock;
+ error_t err;
+ struct sockaddr_in ftp_addr;
+
+ if (conn->params->addr_type != AF_INET)
+ return EAFNOSUPPORT;
+
+ if (! ftp_port)
+ {
+ struct servent *se = getservbyname ("ftp", "tcp");
+ if (! se)
+ return EGRATUITOUS;
+ ftp_port = se->s_port;
+ }
+
+ if (conn->control >= 0)
+ {
+ close (conn->control);
+ conn->control = -1;
+ }
+ bzero (&conn->syshooks, sizeof conn->syshooks);
+
+ csock = socket (PF_INET, SOCK_STREAM, 0);
+ if (csock < 0)
+ return errno;
+
+ ftp_addr.sin_len = sizeof ftp_addr;
+ ftp_addr.sin_family = conn->params->addr_type;
+ ftp_addr.sin_addr = *(struct in_addr *)conn->params->addr;
+ ftp_addr.sin_port = ftp_port;
+
+ if (connect (csock, (struct sockaddr *)&ftp_addr, sizeof ftp_addr) < 0)
+ {
+ err = errno;
+ close (csock);
+ return err;
+ }
+
+ conn->control = csock;
+
+ err = ftp_conn_hello (conn);
+
+ if (!err && conn->hooks && conn->hooks->opened)
+ (* conn->hooks->opened) (conn);
+
+ if (! err)
+ /* Make any machine-dependent customizations. */
+ ftp_conn_sysify (conn);
+
+ if (! err)
+ /* login */
+ err = ftp_conn_login (conn);
+
+ if (!err && !conn->syshooks_valid)
+ /* Try again now. */
+ err = ftp_conn_sysify (conn);
+
+ if (!err && conn->type)
+ /* Set the connection type. */
+ {
+ int reply;
+ err = ftp_conn_cmd (conn, "type", conn->type, &reply, 0);
+ if (!err && reply != REPLY_OK)
+ err = unexpected_reply (conn, reply, 0, 0);
+ }
+
+ if (err)
+ ftp_conn_close (conn);
+
+ return err;
+}
+
+void
+ftp_conn_close (struct ftp_conn *conn)
+{
+ if (conn->control >= 0)
+ close (conn->control);
+ conn->control = -1;
+ if (conn->hooks && conn->hooks->closed)
+ (* conn->hooks->closed) (conn);
+}
diff --git a/libftpconn/priv.h b/libftpconn/priv.h
new file mode 100644
index 00000000..570ea290
--- /dev/null
+++ b/libftpconn/priv.h
@@ -0,0 +1,98 @@
+/* libftpconn private definitions
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __FTPCONN_PRIV_H__
+#define __FTPCONN_PRIV_H__
+
+#include <features.h>
+
+#ifdef FTP_CONN_DEFINE_EI
+#define FTP_CONN_EI
+#else
+#define FTP_CONN_EI __extern_inline
+#endif
+
+/* Ftp reply codes. */
+#define REPLY_DELAY 120 /* Service ready in nnn minutes */
+
+#define REPLY_OK 200 /* Command OK */
+#define REPLY_SYSTYPE 215 /* NAME version */
+#define REPLY_HELLO 220 /* Service ready for new user */
+#define REPLY_ABORT_OK 225 /* ABOR command successful */
+#define REPLY_TRANS_OK 226 /* Closing data connection; requested file
+ action successful */
+#define REPLY_PASV_OK 227 /* Entering passive mode */
+#define REPLY_LOGIN_OK 230 /* User logged in, proceed */
+#define REPLY_FCMD_OK 250 /* Requested file action okay, completed */
+#define REPLY_DIR_NAME 257 /* "DIR" msg */
+
+#define REPLY_NEED_PASS 331 /* User name okay, need password */
+#define REPLY_NEED_ACCT 332 /* Need account for login */
+
+#define REPLY_CLOSED 421 /* Service not available, closing control connection */
+#define REPLY_ABORTED 426 /* Connection closed; transfer aborted */
+
+#define REPLY_BAD_CMD 500 /* Syntax error; command unrecognized */
+#define REPLY_BAD_ARG 501 /* Synax error in parameters or arguments */
+#define REPLY_UNIMP_CMD 502 /* Command not implemented */
+#define REPLY_UNIMP_ARG 504 /* Command not implemented for that parameter */
+
+#define REPLY_NO_LOGIN 530 /* Not logged in */
+#define REPLY_NO_ACCT 532 /* Need account for storing files */
+#define REPLY_NO_SPACE 552 /* Requested file action aborted
+ Exceeded storage allocation */
+
+#define REPLY_IS_PRELIM(rep) ((rep) >= 100 && (rep) < 200)
+#define REPLY_IS_SUCCESS(rep) ((rep) >= 200 && (rep) < 300)
+#define REPLY_IS_INCOMPLETE(rep) ((rep) >= 300 && (rep) < 400)
+#define REPLY_IS_TRANSIENT(rep) ((rep) >= 400 && (rep) < 500)
+#define REPLY_IS_FAILURE(rep) ((rep) >= 500 && (rep) < 600)
+
+extern error_t unexpected_reply (struct ftp_conn *conn, int reply, const char *reply_txt,
+ const error_t *poss_errs);
+#if defined(__USE_EXTERN_INLINES) || defined(FTP_CONN_DEFINE_EI)
+FTP_CONN_EI error_t
+unexpected_reply (struct ftp_conn *conn, int reply, const char *reply_txt,
+ const error_t *poss_errs)
+{
+ if (reply == REPLY_CLOSED)
+ return EPIPE;
+ else if (reply == REPLY_UNIMP_CMD || reply == REPLY_UNIMP_ARG)
+ return EOPNOTSUPP;
+ else if (reply == REPLY_BAD_ARG)
+ return EINVAL;
+ else if (REPLY_IS_FAILURE (reply) && reply_txt
+ && conn->syshooks.interp_err && poss_errs)
+ return (*conn->syshooks.interp_err) (conn, reply_txt, poss_errs);
+ else if (REPLY_IS_TRANSIENT (reply))
+ return EAGAIN;
+ else
+ return EGRATUITOUS;
+}
+#endif /* Use extern inlines. */
+
+/* Error codes we think may result from file operations we do. */
+extern const error_t ftp_conn_poss_file_errs[];
+
+error_t ftp_conn_get_pasv_addr (struct ftp_conn *conn, struct sockaddr **addr);
+
+error_t ftp_conn_send_actv_addr (struct ftp_conn *conn, struct sockaddr *addr);
+
+#endif /* __FTPCONN_PRIV_H__ */
diff --git a/libftpconn/reply.c b/libftpconn/reply.c
new file mode 100644
index 00000000..d39cdb02
--- /dev/null
+++ b/libftpconn/reply.c
@@ -0,0 +1,241 @@
+/* Parse ftp server replies
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <ftpconn.h>
+#include "priv.h"
+
+/* Add STR (of size LEN) to CONN's reply_txt buffer, at offset *OFFS,
+ updating *OFFS. */
+static inline error_t
+ftp_conn_add_reply_txt (struct ftp_conn *conn, size_t *offs,
+ const char *str, size_t len)
+{
+ if (*offs + len + 1 > conn->reply_txt_sz)
+ {
+ size_t new_sz = *offs + len + 50;
+ char *new = realloc (conn->reply_txt, new_sz);
+ if (! new)
+ return ENOMEM;
+ conn->reply_txt = new;
+ conn->reply_txt_sz = new_sz;
+ }
+
+ bcopy (str, conn->reply_txt + *offs, len);
+ conn->reply_txt[*offs + len] = '\0'; /* Make sure nul terminated. */
+
+ *offs += len;
+
+ return 0;
+}
+
+/* Return a new line read from CONN's control connection in LINE & LINE_LEN;
+ LINE points into storage allocated in CONN, and is only valid until the
+ next call to this function, or return an error code. (we used to just use
+ the stdio getline function, and keep a stdio stream for the control
+ connection, but interleaved I/O didn't work correctly.) */
+static inline error_t
+ftp_conn_getline (struct ftp_conn *conn, const char **line, size_t *line_len)
+{
+ char *l = conn->line;
+ size_t offs = conn->line_offs, len = conn->line_len, sz = conn->line_sz;
+ int (*icheck) (struct ftp_conn *conn) = conn->hooks->interrupt_check;
+
+ for (;;)
+ {
+ int rd;
+
+ if (offs < len)
+ /* See if there's a newline in the active part of the line buffer. */
+ {
+ char *nl = memchr (l + offs, '\n', len - offs);
+ if (nl)
+ /* There is! Consume and return the whole line we found. */
+ {
+ *line = l + offs;
+
+ offs = nl + 1 - l; /* Consume the line */
+
+ /* Null terminate the result by overwriting the newline; if
+ there's a CR preceding it, get rid of that too. */
+ if (nl > *line && nl[-1] == '\r')
+ nl--;
+ *nl = '\0';
+
+ *line_len = nl - *line;
+
+ if (offs == len)
+ conn->line_offs = conn->line_len = 0;
+ else
+ conn->line_offs = offs;
+
+ return 0;
+ }
+ }
+
+ /* No newline yet, so read some more! */
+
+ if (offs > (len << 2) && offs < len)
+ /* Relocate the current contents of the buffer to the beginning. */
+ {
+ len -= offs;
+ bcopy (l + offs, l, len - offs);
+ offs = conn->line_offs = 0;
+ conn->line_len = len;
+ }
+ if (len == sz)
+ /* Grow the line buffer; there's no space left. */
+ {
+ sz = sz + len ?: 50;
+ l = realloc (l, sz);
+ if (! l)
+ return ENOMEM;
+ conn->line = l;
+ conn->line_sz = sz;
+ }
+
+ /* Actually read something. */
+ rd = read (conn->control, l + len, sz - len);
+ if (rd < 0)
+ return errno;
+ else if (rd == 0)
+ {
+ *line = l + offs;
+ *line_len = 0;
+ return 0;
+ }
+
+ len += rd;
+ conn->line_len = len;
+
+ if (icheck && (*icheck) (conn))
+ return EINTR;
+ }
+}
+
+/* Get the next reply from CONN's ftp server, returning the reply code in
+ REPLY, if REPLY is non-zero, and the text of the reply (not including the
+ reply code) in REPLY_TXT (if it isn't zero), or return an error code. If
+ the reply is multiple lines, all of them are included in REPLY_TXT,
+ separated by newlines. */
+inline error_t
+ftp_conn_get_raw_reply (struct ftp_conn *conn, int *reply,
+ const char **reply_txt)
+{
+ size_t reply_txt_offs = 0; /* End of a multi-line reply in accum buf. */
+ int multi = 0; /* If a multi-line reply, the reply code. */
+
+ if (!reply && !reply_txt)
+ return 0; /* nop */
+
+ do
+ {
+ const char *l;
+ size_t len;
+ error_t err = ftp_conn_getline (conn, &l, &len);
+
+ if (err)
+ return err;
+ if (!multi && len == 0)
+ return EPIPE;
+
+#define ACCUM(txt, len) \
+ do { \
+ if (reply_txt) /* Only accumulate if wanted. */ \
+ { \
+ error_t err = \
+ ftp_conn_add_reply_txt (conn, &reply_txt_offs, txt, len); \
+ if (err) \
+ return err; \
+ } \
+ } while (0)
+
+ if (conn->hooks && conn->hooks->cntl_debug)
+ (*conn->hooks->cntl_debug) (conn, FTP_CONN_CNTL_DEBUG_REPLY, l);
+
+ if (isdigit (l[0]) && isdigit (l[1]) && isdigit (l[2]))
+ /* A reply code. */
+ {
+ int code = (l[0] - '0')*100 + (l[1] - '0')*10 + (l[2] - '0');
+
+ if (multi && code != multi)
+ /* Two codes in a multi-line reply don't match. */
+ return EGRATUITOUS;
+
+ if (l[3] == '-')
+ /* The non-terminal line of a multi-line reply. RFC959 actually
+ claims there shouldn't be more than one multi-line code (other
+ lines in between the two shouldn't have a numeric code at
+ all), but real ftp servers don't obey this rule. */
+ multi = code;
+ else if (l[3] != ' ')
+ /* Some syntax error. */
+ return EGRATUITOUS;
+ else
+ /* The end of the reply (and perhaps the only line). */
+ {
+ multi = 0;
+ if (reply)
+ *reply = code;
+ }
+
+ ACCUM (l + 4, len - 4);
+ }
+ else if (multi)
+ /* The lines between the first and last in a multi-line reply may be
+ anything as long as they don't start with a digit. */
+ ACCUM (l, len);
+ else
+ return EGRATUITOUS;
+ }
+ while (multi);
+
+ if (reply_txt)
+ *reply_txt = conn->reply_txt;
+
+ return 0;
+}
+
+/* Get the next reply from CONN's ftp server, returning the reply code in
+ REPLY, if REPLY is non-zero, and the text of the reply (not including the
+ reply code) in REPLY_TXT (if it isn't zero), or return an error code. If
+ the reply is multiple lines, all of them are included in REPLY_TXT,
+ separated by newlines. This differs from ftp_conn_get_raw_reply in that
+ it eats REPLY_ABORT_OK replies on the assumption that they're junk left
+ over from the last abort command. */
+error_t
+ftp_conn_get_reply (struct ftp_conn *conn, int *reply, const char **reply_txt)
+{
+ int code;
+ error_t err;
+
+ do
+ err = ftp_conn_get_raw_reply (conn, &code, reply_txt);
+ while (!err && code == REPLY_ABORT_OK);
+
+ if (!err && reply)
+ *reply = code;
+
+ return err;
+}
diff --git a/libftpconn/rmt.c b/libftpconn/rmt.c
new file mode 100644
index 00000000..7ada5774
--- /dev/null
+++ b/libftpconn/rmt.c
@@ -0,0 +1,94 @@
+/* Remote (server-to-server) transfer
+
+ Copyright (C) 1997, 1998 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <ftpconn.h>
+#include "priv.h"
+
+/* Transfer the output of SRC_CMD/SRC_NAME on SRC_CONN to DST_NAME on
+ DST_CONN, moving the data directly between servers. */
+error_t
+ftp_conn_rmt_transfer (struct ftp_conn *src_conn,
+ const char *src_cmd, const char *src_name,
+ const int *src_poss_errs,
+ struct ftp_conn *dst_conn, const char *dst_name)
+{
+ struct sockaddr *src_addr;
+ error_t err = ftp_conn_get_pasv_addr (src_conn, &src_addr);
+
+ if (! err)
+ {
+ err = ftp_conn_send_actv_addr (dst_conn, src_addr);
+
+ if (! err)
+ {
+ int reply;
+ const char *txt;
+ err = ftp_conn_cmd (src_conn, src_cmd, src_name, 0, 0);
+
+ if (! err)
+ {
+ err = ftp_conn_cmd (dst_conn, "stor", dst_name, &reply, &txt);
+
+ if (! err)
+ {
+ if (REPLY_IS_PRELIM (reply))
+ {
+ err = ftp_conn_get_reply (src_conn, &reply, &txt);
+ if (!err && !REPLY_IS_PRELIM (reply))
+ err = unexpected_reply (src_conn, reply, txt,
+ src_poss_errs);
+
+ if (err)
+ ftp_conn_abort (dst_conn);
+ else
+ err = ftp_conn_finish_transfer (dst_conn);
+ }
+ else
+ err = unexpected_reply (dst_conn, reply, txt,
+ ftp_conn_poss_file_errs);
+ }
+ if (err)
+ /* Ftp servers seem to hang trying to abort at this point, so
+ just close the connection entirely. */
+ ftp_conn_close (src_conn);
+ else
+ err = ftp_conn_finish_transfer (src_conn);
+ }
+ }
+
+ free (src_addr);
+ }
+
+ return err;
+}
+
+/* Copy the SRC_NAME on SRC_CONN to DST_NAME on DST_CONN, moving the data
+ directly between servers. */
+error_t
+ftp_conn_rmt_copy (struct ftp_conn *src_conn, const char *src_name,
+ struct ftp_conn *dst_conn, const char *dst_name)
+{
+ return
+ ftp_conn_rmt_transfer (src_conn, "retr", src_name, ftp_conn_poss_file_errs,
+ dst_conn, dst_name);
+}
diff --git a/libftpconn/set-type.c b/libftpconn/set-type.c
new file mode 100644
index 00000000..6c500ce6
--- /dev/null
+++ b/libftpconn/set-type.c
@@ -0,0 +1,60 @@
+/* Set connection type
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <ftpconn.h>
+#include "priv.h"
+
+/* Set the ftp connection type of CONN to TYPE, or return an error. */
+error_t
+ftp_conn_set_type (struct ftp_conn *conn, const char *type)
+{
+ error_t err = 0;
+
+ if (! type)
+ return EINVAL;
+
+ if (!conn->type || strcmp (type, conn->type) != 0)
+ {
+ type = strdup (type);
+ if (! type)
+ err = ENOMEM;
+ else
+ {
+ int reply;
+ error_t err = ftp_conn_cmd (conn, "type", type, &reply, 0);
+
+ if (!err && reply != REPLY_OK && reply != REPLY_CLOSED)
+ err = unexpected_reply (conn, reply, 0, 0);
+
+ if (!err || err == EPIPE)
+ {
+ if (conn->type)
+ free ((char *)conn->type);
+ conn->type = type;
+ }
+ }
+ }
+
+ return err;
+}
diff --git a/libftpconn/stats.c b/libftpconn/stats.c
new file mode 100644
index 00000000..1b37a7f3
--- /dev/null
+++ b/libftpconn/stats.c
@@ -0,0 +1,80 @@
+/* Fetch file stats
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <ftpconn.h>
+
+/* Start an operation to get a list of file-stat structures for NAME (this
+ is often similar to ftp_conn_start_dir, but with OS-specific flags), and
+ return a file-descriptor for reading on, and a state structure in STATE
+ suitable for passing to cont_get_stats. If CONTENTS is true, NAME must
+ refer to a directory, and the contents will be returned, otherwise, the
+ (single) result will refer to NAME. */
+error_t
+ftp_conn_start_get_stats (struct ftp_conn *conn,
+ const char *name, int contents,
+ int *fd, void **state)
+{
+ if (conn->syshooks.start_get_stats)
+ return
+ (*conn->syshooks.start_get_stats) (conn, name, contents, fd, state);
+ else
+ return EOPNOTSUPP;
+}
+
+/* Read stats information from FD, calling ADD_STAT for each new stat (HOOK
+ is passed to ADD_STAT). FD and STATE should be returned from
+ start_get_stats. If this function returns EAGAIN, then it should be
+ called again to finish the job (possibly after calling select on FD); if
+ it returns 0, then it is finishe,d and FD and STATE are deallocated. */
+error_t
+ftp_conn_cont_get_stats (struct ftp_conn *conn, int fd, void *state,
+ ftp_conn_add_stat_fun_t add_stat, void *hook)
+{
+ if (conn->syshooks.cont_get_stats)
+ return (*conn->syshooks.cont_get_stats) (conn, fd, state, add_stat, hook);
+ else
+ return EOPNOTSUPP;
+}
+
+/* Get a list of file-stat structures for NAME, calling ADD_STAT for each one
+ (HOOK is passed to ADD_STAT). If CONTENTS is true, NAME must refer to a
+ directory, and the contents will be returned, otherwise, the (single)
+ result will refer to NAME. This function may block. */
+error_t
+ftp_conn_get_stats (struct ftp_conn *conn,
+ const char *name, int contents,
+ ftp_conn_add_stat_fun_t add_stat, void *hook)
+{
+ int fd;
+ void *state;
+ error_t err = ftp_conn_start_get_stats (conn, name, contents, &fd, &state);
+
+ if (err)
+ return err;
+
+ do
+ err = ftp_conn_cont_get_stats (conn, fd, state, add_stat, hook);
+ while (err == EAGAIN);
+
+ return err;
+}
diff --git a/libftpconn/unix.c b/libftpconn/unix.c
new file mode 100644
index 00000000..28efefdd
--- /dev/null
+++ b/libftpconn/unix.c
@@ -0,0 +1,772 @@
+/* Unix-specific ftpconn hooks
+
+ Copyright (C) 1997, 1998, 2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <libgen.h> /* For dirname(). */
+#ifdef HAVE_HURD_HURD_TYPES_H
+#include <hurd/hurd_types.h>
+#endif
+
+#include <ftpconn.h>
+
+/* Uid/gid to use when we don't know about a particular user name. */
+#define DEFAULT_UID 65535
+#define DEFAULT_GID 65535
+
+struct ftp_conn_syshooks ftp_conn_unix_syshooks = {
+ ftp_conn_unix_pasv_addr, ftp_conn_unix_interp_err,
+ ftp_conn_unix_start_get_stats, ftp_conn_unix_cont_get_stats,
+ ftp_conn_unix_append_name, ftp_conn_unix_basename
+};
+
+/* Try to get an internet address out of the reply to a PASV command.
+ Unfortunately, the format of the reply such isn't standardized. */
+error_t
+ftp_conn_unix_pasv_addr (struct ftp_conn *conn, const char *txt,
+ struct sockaddr **addr)
+{
+ unsigned a0, a1, a2, a3; /* Parts of the inet address */
+ unsigned p0, p1; /* Parts of the prot (msb, lsb) */
+
+ if (sscanf (txt, "%*[^0-9]%d,%d,%d,%d,%d,%d", &a0,&a1,&a2,&a3, &p0,&p1) != 6)
+ return EGRATUITOUS;
+ else
+ {
+ unsigned char *a, *p;
+
+ *addr = malloc (sizeof (struct sockaddr_in));
+ if (! *addr)
+ return ENOMEM;
+
+ (*addr)->sa_len = sizeof (struct sockaddr_in);
+ (*addr)->sa_family = AF_INET;
+
+ a = (unsigned char *)&((struct sockaddr_in *)*addr)->sin_addr.s_addr;
+ a[0] = a0 & 0xff;
+ a[1] = a1 & 0xff;
+ a[2] = a2 & 0xff;
+ a[3] = a3 & 0xff;
+
+ p = (unsigned char *)&((struct sockaddr_in *)*addr)->sin_port;
+ p[0] = p0 & 0xff;
+ p[1] = p1 & 0xff;
+
+ return 0;
+ }
+}
+
+/* Compare strings P & Q in a most forgiving manner, ignoring case and
+ everything but alphanumeric characters. */
+static int
+strlaxcmp (const char *p, const char *q)
+{
+ for (;;)
+ {
+ int ch1, ch2;
+
+ while (*p && !isalnum (*p))
+ p++;
+ while (*q && !isalnum (*q))
+ q++;
+
+ if (!*p || !*q)
+ break;
+
+ ch1 = tolower (*p);
+ ch2 = tolower (*q);
+ if (ch1 != ch2)
+ break;
+
+ p++;
+ q++;
+ }
+
+ return *p - *q;
+}
+
+/* Try to convert an error message in TXT into an error code. POSS_ERRS
+ contains a list of likely errors to try; if no other clue is found, the
+ first thing in poss_errs is returned. */
+error_t
+ftp_conn_unix_interp_err (struct ftp_conn *conn, const char *txt,
+ const error_t *poss_errs)
+{
+ const char *p;
+ const error_t *e;
+
+ if (!poss_errs || !poss_errs[0])
+ return EIO;
+
+ /* ignore everything before the last colon. */
+ p = strrchr (txt, ':');
+ if (p)
+ p++;
+ else
+ p = txt;
+
+ /* Now, for each possible error, do a string compare ignoring case and
+ anything non-alphanumberic. */
+ for (e = poss_errs; *e; e++)
+ if (strlaxcmp (p, strerror (*e)) == 0)
+ return *e;
+
+ return poss_errs[0];
+}
+
+struct get_stats_state
+{
+ char *name; /* Last read (maybe partial) name. */
+ size_t name_len; /* Valid length of NAME, *not including* '\0'. */
+ size_t name_alloced; /* Allocated size of NAME (>= NAME_LEN). */
+ int name_partial; /* True if NAME isn't complete. */
+
+ int contents; /* Are we looking for directory contents? */
+ char *searched_name; /* If we are not, then we are only
+ looking for this name. */
+
+ int added_slash; /* Did we prefix the name with `./'? */
+
+ struct stat stat; /* Last read stat info. */
+
+ int start; /* True if at beginning of output. */
+
+ size_t buf_len; /* Length of contents in BUF. */
+ char buf[7000];
+};
+
+/* Start an operation to get a list of file-stat structures for NAME (this is
+ often similar to ftp_conn_start_dir, but with OS-specific flags), and
+ return a file-descriptor for reading on, and a state structure in STATE
+ suitable for passing to cont_get_stats. If CONTENTS is true, NAME must
+ refer to a directory, and the contents will be returned, otherwise, the
+ (single) result will refer to NAME. */
+error_t
+ftp_conn_unix_start_get_stats (struct ftp_conn *conn,
+ const char *name, int contents,
+ int *fd, void **state)
+{
+ error_t err = 0;
+ size_t req_len;
+ char *req = NULL;
+ struct get_stats_state *s = NULL;
+ const char *flags = "-A";
+ const char *slash = strchr (name, '/');
+ char *searched_name = NULL;
+
+ s = (struct get_stats_state *) malloc (sizeof (struct get_stats_state));
+ if (! s)
+ {
+ err = ENOMEM;
+ goto out;
+ }
+ if (! contents)
+ {
+ if (! strcmp (name, "/"))
+ {
+ /* Listing only the directory itself and not the directory
+ content seems to be not supported by all FTP servers. If
+ the directory in question is not the root directory, we
+ can simply lookup `..', but that doesn't work if we are
+ already on the root directory. */
+ err = EINVAL;
+ }
+ else
+ {
+ searched_name = strdup (basename ((char *) name));
+ if (! searched_name)
+ err = ENOMEM;
+ }
+ if (err)
+ goto out;
+ }
+
+ if (strcspn (name, "*? \t\n{}$`\\\"'") < strlen (name))
+ /* NAME contains some metacharacters, which makes the behavior of various
+ ftp servers unpredictable, so punt. */
+ {
+ err = EINVAL;
+ goto out;
+ }
+
+ /* We pack the ls options and the name into the list argument, in REQ,
+ which will do the right thing on most unix ftp servers. */
+
+ req_len = strlen (flags) + 2; /* space character + null character. */
+ if (! contents)
+ {
+ /* If we are looking for a directory rather than its content,
+ lookup the parent directory and search for the entry, rather
+ than looking it up directly, as not all ftp servers support
+ the -d option to ls. To make sure we get a directory, append
+ '/', except for the root directory. */
+ char *dirn = dirname (strdupa (name));
+ int is_root = ! strcmp (dirn, "/");
+ req_len += strlen (dirn) + (is_root ? 0 : 1);
+ req = malloc (req_len);
+ if (! req)
+ err = ENOMEM;
+ else
+ sprintf (req, "%s %s%s", flags, dirn, (is_root ? "" : "/"));
+ }
+ else
+ {
+ /* If NAME doesn't contain a slash, we prepend `./' to it so that we can
+ tell from the results whether it's a directory or not. */
+ req_len += strlen (name) + (slash ? 0 : 2);
+ req = malloc (req_len);
+ if (! req)
+ err = ENOMEM;
+ else
+ sprintf (req, "%s %s%s", flags, slash ? "" : "./", name);
+ }
+
+ if (err)
+ goto out;
+
+ /* Make the actual request. */
+ err = ftp_conn_start_dir (conn, req, fd);
+
+ out:
+
+ if (req)
+ free (req);
+ if (err)
+ {
+ if (s)
+ free (s);
+ if (searched_name)
+ free (searched_name);
+ }
+ else
+ {
+ s->contents = contents;
+ s->searched_name = searched_name;
+ s->added_slash = !slash;
+ s->name = 0;
+ s->name_len = s->name_alloced = 0;
+ s->name_partial = 0;
+ s->buf_len = 0;
+ s->start = 1;
+ *state = s;
+ }
+
+ return err;
+}
+
+static char *months[] =
+{
+ "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct",
+ "nov", "dec", 0
+};
+
+/* Translate the information in the ls output in *LINE as best we can into
+ STAT, and update *LINE to point to the filename at the end of the line.
+ If *LINE should be ignored, EAGAIN is returned. */
+static error_t
+parse_dir_entry (char **line, struct stat *stat)
+{
+ char **m;
+ struct tm tm;
+ char *p = *line, *e;
+
+ /*
+drwxrwxrwt 3 root wheel 1024 May 1 16:58 /tmp
+drwxrwxrwt 5 root daemon 4096 May 1 17:15 /tmp
+drwxrwxrwt 4 root 0 1024 May 1 14:34 /tmp
+drwxrwxrwt 6 root wheel 284 May 1 12:46 /tmp
+drwxrwxrwt 4 sys sys 482 May 1 17:11 /tmp
+drwxrwxrwt 7 34 archive 512 May 1 14:28 /tmp
+ */
+
+ if (strncasecmp (p, "total ", 6) == 0)
+ return EAGAIN;
+
+ bzero (stat, sizeof *stat);
+
+#ifdef FSTYPE_FTP
+ stat->st_fstype = FSTYPE_FTP;
+#endif
+
+ /* File format (S_IFMT) bits. */
+ switch (*p++)
+ {
+ case '-': stat->st_mode |= S_IFREG; break;
+ case 'd': stat->st_mode |= S_IFDIR; break;
+ case 'c': stat->st_mode |= S_IFCHR; break;
+ case 'b': stat->st_mode |= S_IFBLK; break;
+ case 'l': stat->st_mode |= S_IFLNK; break;
+ case 's': stat->st_mode |= S_IFSOCK; break;
+ case 'p': stat->st_mode |= S_IFIFO; break;
+ default: return EGRATUITOUS;
+ }
+
+ /* User perm bits. */
+ switch (*p++)
+ {
+ case '-': break;
+ case 'r': stat->st_mode |= S_IRUSR; break;
+ default: return EGRATUITOUS;
+ }
+ switch (*p++)
+ {
+ case '-': break;
+ case 'w': stat->st_mode |= S_IWUSR; break;
+ default: return EGRATUITOUS;
+ }
+ switch (*p++)
+ {
+ case '-': break;
+ case 'x': stat->st_mode |= S_IXUSR; break;
+ case 's': stat->st_mode |= S_IXUSR | S_ISUID; break;
+ case 'S': stat->st_mode |= S_ISUID; break;
+ default: return EGRATUITOUS;
+ }
+
+ /* Group perm bits. */
+ switch (*p++)
+ {
+ case '-': break;
+ case 'r': stat->st_mode |= S_IRGRP; break;
+ default: return EGRATUITOUS;
+ }
+ switch (*p++)
+ {
+ case '-': break;
+ case 'w': stat->st_mode |= S_IWGRP; break;
+ default: return EGRATUITOUS;
+ }
+ switch (*p++)
+ {
+ case '-': break;
+ case 'x': stat->st_mode |= S_IXGRP; break;
+ case 's': stat->st_mode |= S_IXGRP | S_ISGID; break;
+ case 'S': stat->st_mode |= S_ISGID; break;
+ default: return EGRATUITOUS;
+ }
+
+ /* `Other' perm bits. */
+ switch (*p++)
+ {
+ case '-': break;
+ case 'r': stat->st_mode |= S_IROTH; break;
+ default: return EGRATUITOUS;
+ }
+ switch (*p++)
+ {
+ case '-': break;
+ case 'w': stat->st_mode |= S_IWOTH; break;
+ default: return EGRATUITOUS;
+ }
+ switch (*p++)
+ {
+ case '-': break;
+ case 'x': stat->st_mode |= S_IXOTH; break;
+ case 't': stat->st_mode |= S_IXOTH | S_ISVTX; break;
+ case 'T': stat->st_mode |= S_ISVTX; break;
+ default: return EGRATUITOUS;
+ }
+
+#define SKIP_WS() \
+ while (isspace (*p)) p++;
+#define PARSE_INT() ({ \
+ unsigned u = strtoul (p, &e, 10); \
+ if (e == p || isalnum (*e)) \
+ return EGRATUITOUS; \
+ p = e; \
+ u; \
+ })
+
+ /* Link count. */
+ SKIP_WS ();
+ stat->st_nlink = PARSE_INT ();
+
+ /* File owner. */
+ SKIP_WS ();
+ if (isdigit (*p))
+ stat->st_uid = PARSE_INT ();
+ else
+ {
+ struct passwd *pw;
+
+ e = p + strcspn (p, " \t\n");
+ *e++ = '\0';
+
+ pw = getpwnam (p);
+
+ if (pw)
+ stat->st_uid = pw->pw_uid;
+ else
+ stat->st_uid = DEFAULT_UID;
+
+ p = e;
+ }
+
+#ifdef HAVE_STAT_ST_AUTHOR
+ stat->st_author = stat->st_uid;
+#endif
+
+ /* File group. */
+ SKIP_WS ();
+ if (isdigit (*p))
+ stat->st_gid = PARSE_INT ();
+ else
+ {
+ struct group *gr;
+
+ e = p + strcspn (p, " \t\n");
+ *e++ = '\0';
+
+ gr = getgrnam (p);
+
+ if (gr)
+ stat->st_gid = gr->gr_gid;
+ else
+ stat->st_gid = DEFAULT_GID;
+
+ p = e;
+ }
+
+ /* File size / device numbers. */
+ SKIP_WS ();
+ if (S_ISCHR (stat->st_mode) || S_ISBLK (stat->st_mode))
+ /* Block and character devices show the block params instead of the file
+ size. */
+ {
+ stat->st_dev = PARSE_INT ();
+ if (*p != ',')
+ return EGRATUITOUS;
+ stat->st_dev = (stat->st_dev << 8) | PARSE_INT ();
+ stat->st_size = 0;
+ }
+ else
+ /* File size. */
+ stat->st_size = PARSE_INT ();
+
+ stat->st_blocks = stat->st_size >> 9;
+
+ /* Date. Ick. */
+ /* Formats: MONTH DAY HH:MM and MONTH DAY YEAR */
+
+ bzero (&tm, sizeof tm);
+
+ SKIP_WS ();
+ e = p + strcspn (p, " \t\n");
+ for (m = months; *m; m++)
+ if (strncasecmp (*m, p, e - p) == 0)
+ {
+ tm.tm_mon = m - months;
+ break;
+ }
+ if (! *m)
+ return EGRATUITOUS;
+ p = e;
+
+ SKIP_WS ();
+ tm.tm_mday = PARSE_INT ();
+
+ SKIP_WS ();
+ if (p[1] == ':' || p[2] == ':')
+ {
+ struct tm *now_tm;
+ struct timeval now_tv;
+
+ tm.tm_hour = PARSE_INT ();
+ p++;
+ tm.tm_min = PARSE_INT ();
+
+ if (gettimeofday (&now_tv, 0) != 0)
+ return errno;
+
+ now_tm = localtime (&now_tv.tv_sec);
+ if (now_tm->tm_mon < tm.tm_mon)
+ tm.tm_year = now_tm->tm_year - 1;
+ else
+ tm.tm_year = now_tm->tm_year;
+ }
+ else
+ tm.tm_year = PARSE_INT () - 1900;
+
+ stat->st_mtim.tv_sec = mktime (&tm);
+ if (stat->st_mtim.tv_sec == (time_t)-1)
+ return EGRATUITOUS;
+
+ /* atime and ctime are the same as mtime. */
+ stat->st_atim.tv_sec = stat->st_ctim.tv_sec = stat->st_mtim.tv_sec;
+ stat->st_atim.tv_nsec = stat->st_ctim.tv_nsec = stat->st_mtim.tv_nsec = 0;
+
+ /* Update *LINE to point to the filename. */
+ SKIP_WS ();
+ *line = p;
+
+ return 0;
+}
+
+/* Read stats information from FD, calling ADD_STAT for each new stat (HOOK
+ is passed to ADD_STAT). FD and STATE should be returned from
+ start_get_stats. If this function returns EAGAIN, then it should be
+ called again to finish the job (possibly after calling select on FD); if
+ it returns 0, then it is finishe,d and FD and STATE are deallocated. */
+error_t
+ftp_conn_unix_cont_get_stats (struct ftp_conn *conn, int fd, void *state,
+ ftp_conn_add_stat_fun_t add_stat, void *hook)
+{
+ char *p, *nl;
+ ssize_t rd;
+ size_t name_len;
+ error_t err = 0;
+ struct get_stats_state *s = state;
+ int (*icheck) (struct ftp_conn *conn) = conn->hooks->interrupt_check;
+
+ /* We always consume full lines, so we know that we have to read more when
+ we first get called. */
+ rd = read (fd, s->buf + s->buf_len, sizeof (s->buf) - s->buf_len);
+ if (rd < 0)
+ {
+ err = errno;
+ goto finished;
+ }
+
+ if (icheck && (*icheck) (conn))
+ {
+ err = EINTR;
+ goto finished;
+ }
+
+ if (rd == 0)
+ /* EOF */
+ if (s->buf_len == 0)
+ /* We're done! Clean up and return the result in STATS. */
+ {
+ if (s->start)
+ /* No output at all. From many ftp servers, this means that the
+ specified file wasn't found. */
+ err = ENOENT;
+ goto finished;
+ }
+ else
+ /* Partial line at end of file? */
+ nl = s->buf + s->buf_len;
+ else
+ /* Look for a new line in what we read (we know that there weren't any in
+ the buffer before that). */
+ {
+ nl = memchr (s->buf + s->buf_len, '\n', rd);
+ s->buf_len += rd;
+ }
+
+ s->start = 0; /* We've read past the start. */
+
+ if (!nl && s->buf_len < sizeof (s->buf))
+ /* We didn't find any newlines (which implies we didn't hit EOF), and we
+ still have room to grow the buffer, so just wait until next time to do
+ anything. */
+ return EAGAIN;
+
+ /* Where we start parsing. */
+ p = s->buf;
+
+ do
+ {
+ if (! s->name_partial)
+ /* We aren't continuing to read an overflowed name from the previous
+ call, so we know that we are at the start of a line, and can parse
+ the info here as a directory entry. */
+ {
+ /* Parse the directory entry info, updating P to point to the
+ beginning of the name. */
+ err = parse_dir_entry (&p, &s->stat);
+ if (err == EAGAIN)
+ /* This line isn't a real entry and should be ignored. */
+ goto skip_line;
+ if (err)
+ goto finished;
+ }
+
+ /* Now fill in S->last_stat's name field, possibly extending it from a
+ previous buffer. */
+ name_len = (nl ? nl - p : s->buf + s->buf_len - p);
+ if (name_len > 0 && p[name_len - 1] == '\r')
+ name_len--;
+ if (name_len > 0)
+ /* Extending s->name. */
+ {
+ size_t old_len = s->name_len;
+ size_t total_len = old_len + name_len + 1;
+
+ if (total_len > s->name_alloced)
+ {
+ char *new_name = realloc (s->name, total_len);
+ if (! new_name)
+ goto enomem;
+ s->name = new_name;
+ s->name_alloced = total_len;
+ }
+
+ strncpy (s->name + old_len, p, name_len);
+ s->name[old_len + name_len] = '\0';
+ s->name_len = total_len - 1;
+ }
+
+ if (nl)
+ {
+ char *name = s->name;
+ char *symlink_target = 0;
+
+ if (S_ISLNK (s->stat.st_mode))
+ /* A symlink, see if we can find the link target. */
+ {
+ symlink_target = strstr (name, " -> ");
+ if (symlink_target)
+ {
+ *symlink_target = '\0';
+ symlink_target += 4;
+ }
+ }
+
+ if (strchr (name, '/'))
+ {
+ if (s->contents)
+ /* We know that the name originally request had a slash in
+ it (because we added one if necessary), so if a name in
+ the listing has one too, it can't be the contents of a
+ directory; if this is the case and we wanted the
+ contents, this must not be a directory. */
+ {
+ err = ENOTDIR;
+ goto finished;
+ }
+ else if (s->added_slash)
+ /* S->name must be the same name we passed; if we added a
+ `./' prefix, removed it so the client gets back what it
+ passed. */
+ name += 2;
+ }
+
+ /* Pass only directory-relative names to the callback function. */
+ name = basename (name);
+
+ if (s->contents || ! strcmp (s->name, s->searched_name))
+ {
+ /* We are only interested in searched_name. */
+
+ /* Call the callback function to process the current entry; it is
+ responsible for freeing S->name and SYMLINK_TARGET. */
+ err = (*add_stat) (name, &s->stat, symlink_target, hook);
+ if (err)
+ goto finished;
+ }
+
+ s->name_len = 0;
+ s->name_partial = 0;
+
+ skip_line:
+ p = nl + 1;
+ nl = memchr (p, '\n', s->buf + s->buf_len - p);
+ }
+ else
+ /* We found no newline, so the name extends past what we read; we'll
+ try to read more next time. */
+ {
+ s->name_partial = 1;
+ /* Skip over the partial name for the next iteration. */
+ p += name_len;
+ }
+ }
+ while (nl);
+
+ /* Move any remaining characters in the buffer to the beginning for the
+ next call. */
+ s->buf_len -= (p - s->buf);
+ if (s->buf_len > 0)
+ memmove (s->buf, p, s->buf_len);
+
+ /* Try again later. */
+ return EAGAIN;
+
+enomem:
+ /* Some memory allocation failed. */
+ err = ENOMEM;
+
+finished:
+ /* We're finished (with an error if ERR != 0), deallocate everything &
+ return. */
+ if (s->name)
+ free (s->name);
+ if (s->searched_name)
+ free (s->searched_name);
+ free (s);
+ close (fd);
+
+ if (err && rd > 0)
+ ftp_conn_abort (conn);
+ else if (err)
+ ftp_conn_finish_transfer (conn);
+ else
+ err = ftp_conn_finish_transfer (conn);
+
+ return err;
+}
+
+/* Give a name which refers to a directory file, and a name in that
+ directory, this should return in COMPOSITE the composite name referring to
+ that name in that directory, in malloced storage. */
+error_t
+ftp_conn_unix_append_name (struct ftp_conn *conn,
+ const char *dir, const char *name,
+ char **composite)
+{
+ char *path = malloc (strlen (dir) + 1 + strlen (name) + 1);
+
+ if (! path)
+ return ENOMEM;
+
+ /* Form the path name. */
+ if (name && *name)
+ if (dir[0] == '/' && dir[1] == '\0')
+ stpcpy (stpcpy (path, dir), name);
+ else
+ stpcpy (stpcpy (stpcpy (path, dir), "/"), name);
+ else
+ strcpy (path, dir);
+
+ *composite = path;
+
+ return 0;
+}
+
+/* If the name of a file *NAME is a composite name (containing both a
+ filename and a directory name), this function should change *NAME to be
+ the name component only; if the result is shorter than the original
+ *NAME, the storage pointed to it may be modified, otherwise, *NAME
+ should be changed to point to malloced storage holding the result, which
+ will be freed by the caller. */
+error_t
+ftp_conn_unix_basename (struct ftp_conn *conn, char **name)
+{
+ *name = basename (*name);
+ return 0;
+}
diff --git a/libftpconn/xfer.c b/libftpconn/xfer.c
new file mode 100644
index 00000000..b3d0786d
--- /dev/null
+++ b/libftpconn/xfer.c
@@ -0,0 +1,280 @@
+/* Start/stop data channel transfer
+
+ Copyright (C) 1997,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include <ftpconn.h>
+#include "priv.h"
+
+/* Open an active data connection, returning the file descriptor in DATA. */
+static error_t
+ftp_conn_start_open_actv_data (struct ftp_conn *conn, int *data)
+{
+ error_t err = 0;
+ /* DCQ is a socket on which to listen for data connections from the server. */
+ int dcq;
+ struct sockaddr *addr = conn->actv_data_addr;
+ socklen_t addr_len = sizeof *addr;
+
+ if (! addr)
+ /* Generate an address for the data connection (which we must know,
+ so we can tell the server). */
+ {
+ addr = conn->actv_data_addr = malloc (sizeof (struct sockaddr_in));
+ if (! addr)
+ return ENOMEM;
+
+ /* Get the local address chosen by the system. */
+ if (conn->control < 0)
+ err = EBADF;
+ else if (getsockname (conn->control, addr, &addr_len) < 0)
+ err = errno;
+
+ if (err == EBADF || err == EPIPE)
+ /* Control connection has closed; reopen it and try again. */
+ {
+ err = ftp_conn_open (conn);
+ if (!err && getsockname (conn->control, addr, &addr_len) < 0)
+ err = errno;
+ }
+
+ if (err)
+ {
+ free (addr);
+ conn->actv_data_addr = 0;
+ return err;
+ }
+ }
+
+ dcq = socket (AF_INET, SOCK_STREAM, 0);
+ if (dcq < 0)
+ return errno;
+
+ /* Let the system choose a port for us. */
+ ((struct sockaddr_in *)addr)->sin_port = 0;
+
+ /* Use ADDR as the data socket's local address. */
+ if (!err && bind (dcq, addr, addr_len) < 0)
+ err = errno;
+
+ /* See what port was chosen by the system. */
+ if (!err && getsockname (dcq, addr, &addr_len) < 0)
+ err = errno;
+
+ /* Set the incoming connection queue length. */
+ if (!err && listen (dcq, 1) < 0)
+ err = errno;
+
+ if (err)
+ close (dcq);
+ else
+ err = ftp_conn_send_actv_addr (conn, conn->actv_data_addr);
+
+ if (! err)
+ *data = dcq;
+
+ return err;
+}
+
+/* Finish opening the active data connection *DATA opened with
+ ftp_conn_start_open_actv_data, following the sending of the command that
+ uses the connection to the server. This function closes the file
+ descriptor in *DATA, and returns a new file descriptor for the actual data
+ connection. */
+static error_t
+ftp_conn_finish_open_actv_data (struct ftp_conn *conn, int *data)
+{
+ struct sockaddr_in rmt_addr;
+ socklen_t rmt_addr_len = sizeof rmt_addr;
+ int real = accept (*data, &rmt_addr, &rmt_addr_len);
+
+ close (*data);
+
+ if (real < 0)
+ return errno;
+
+ *data = real;
+
+ return 0;
+}
+
+/* Abort an active data connection open sequence; this function should be
+ called if ftp_conn_start_open_actv_data succeeds, but an error happens
+ before ftp_conn_finish_open_actv_data can be called. */
+static void
+ftp_conn_abort_open_actv_data (struct ftp_conn *conn, int data)
+{
+ close (data);
+}
+
+/* Return a data connection, which may not be in a completely open state;
+ this call should be followed by the command that uses the connection, and
+ a call to ftp_conn_finish_open_data, if that succeeds. */
+static error_t
+ftp_conn_start_open_data (struct ftp_conn *conn, int *data)
+{
+ error_t err;
+
+ if (conn->use_passive)
+ /* First try a passive connection. */
+ {
+ struct sockaddr *addr;
+
+ /* Tell the server we wan't to use passive mode, for which it should
+ give us an address to connect to. */
+ err = ftp_conn_get_pasv_addr (conn, &addr);
+
+ if (! err)
+ {
+ int dsock = socket (PF_INET, SOCK_STREAM, 0);
+
+ if (dsock < 0)
+ err = errno;
+ else if (connect (dsock, addr, addr->sa_len) < 0)
+ {
+ err = errno;
+ close (dsock);
+ }
+ else
+ *data = dsock;
+
+ free (addr);
+ }
+ }
+ else
+ err = EAGAIN;
+
+ if (err)
+ /* Using a passive connection didn't work, try an active one. */
+ {
+ conn->use_passive = 0; /* Don't try again. */
+ err = ftp_conn_start_open_actv_data (conn, data);
+ }
+
+ return err;
+}
+
+/* Finish opening the data connection *DATA opened with
+ ftp_conn_start_open_data, following the sending of the command that uses
+ the connection to the server. This function may change *DATA, in which
+ case the old file descriptor is closed. */
+static error_t
+ftp_conn_finish_open_data (struct ftp_conn *conn, int *data)
+{
+ if (conn->use_passive)
+ /* Passive connections should already have been completely opened. */
+ return 0;
+ else
+ return ftp_conn_finish_open_actv_data (conn, data);
+}
+
+/* Abort a data connection open sequence; this function should be called if
+ ftp_conn_start_open_data succeeds, but an error happens before
+ ftp_conn_finish_open_data can be called. */
+static void
+ftp_conn_abort_open_data (struct ftp_conn *conn, int data)
+{
+ if (conn->use_passive)
+ close (data);
+ else
+ return ftp_conn_abort_open_actv_data (conn, data);
+}
+
+/* Start a transfer command CMD/ARG, returning a file descriptor in DATA.
+ POSS_ERRS is a list of errnos to try matching against any resulting error
+ text. */
+error_t
+ftp_conn_start_transfer (struct ftp_conn *conn,
+ const char *cmd, const char *arg,
+ const error_t *poss_errs,
+ int *data)
+{
+ error_t err = ftp_conn_start_open_data (conn, data);
+
+ if (! err)
+ {
+ int reply;
+ const char *txt;
+
+ err = ftp_conn_cmd (conn, cmd, arg, &reply, &txt);
+ if (!err && !REPLY_IS_PRELIM (reply))
+ err = unexpected_reply (conn, reply, txt, poss_errs);
+
+ if (err)
+ ftp_conn_abort_open_data (conn, *data);
+ else
+ err = ftp_conn_finish_open_data (conn, data);
+ }
+
+ return err;
+}
+
+/* Wait for the reply signalling the end of a data transfer. */
+error_t
+ftp_conn_finish_transfer (struct ftp_conn *conn)
+{
+ int reply;
+ error_t err = ftp_conn_get_reply (conn, &reply, 0);
+ if (!err && reply != REPLY_TRANS_OK && reply != REPLY_FCMD_OK)
+ err = unexpected_reply (conn, reply, 0, 0);
+ return err;
+}
+
+/* Start retreiving file NAME over CONN, returning a file descriptor in DATA
+ over which the data can be read. */
+error_t
+ftp_conn_start_retrieve (struct ftp_conn *conn, const char *name, int *data)
+{
+ if (! name)
+ return EINVAL;
+ return
+ ftp_conn_start_transfer (conn, "retr", name, ftp_conn_poss_file_errs, data);
+}
+
+/* Start retreiving a list of files in NAME over CONN, returning a file
+ descriptor in DATA over which the data can be read. */
+error_t
+ftp_conn_start_list (struct ftp_conn *conn, const char *name, int *data)
+{
+ return
+ ftp_conn_start_transfer (conn, "nlst", name, ftp_conn_poss_file_errs, data);
+}
+
+/* Start retreiving a directory listing of NAME over CONN, returning a file
+ descriptor in DATA over which the data can be read. */
+error_t
+ftp_conn_start_dir (struct ftp_conn *conn, const char *name, int *data)
+{
+ return
+ ftp_conn_start_transfer (conn, "list", name, ftp_conn_poss_file_errs, data);
+}
+
+/* Start storing into file NAME over CONN, returning a file descriptor in DATA
+ into which the data can be written. */
+error_t
+ftp_conn_start_store (struct ftp_conn *conn, const char *name, int *data)
+{
+ if (! name)
+ return EINVAL;
+ return
+ ftp_conn_start_transfer (conn, "stor", name, ftp_conn_poss_file_errs, data);
+}
diff --git a/libftpconn/xinl.c b/libftpconn/xinl.c
new file mode 100644
index 00000000..72b7fdcf
--- /dev/null
+++ b/libftpconn/xinl.c
@@ -0,0 +1,24 @@
+/* Real definitions for extern inline functions in priv.h
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#define FTP_CONN_DEFINE_EI
+#include <ftpconn.h>
+#include "priv.h"
diff --git a/libhurdbugaddr/Makefile b/libhurdbugaddr/Makefile
new file mode 100644
index 00000000..9fad3448
--- /dev/null
+++ b/libhurdbugaddr/Makefile
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 1996 Free Software Foundation, Inc.
+# Written by Miles Bader
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libhurdbugaddr
+makemode := library
+libname := libhurdbugaddr
+
+SRCS=bugaddr.c
+OBJS=bugaddr.o
+
+include ../Makeconf
diff --git a/libhurdbugaddr/bugaddr.c b/libhurdbugaddr/bugaddr.c
new file mode 100644
index 00000000..b04c9352
--- /dev/null
+++ b/libhurdbugaddr/bugaddr.c
@@ -0,0 +1,23 @@
+/* Hurd default for ARGP_PROGRAM_BUG_ADDRESS
+
+ Copyright (C) 1996, 1998 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+const char *argp_program_bug_address = "<bug-hurd@gnu.org>";
diff --git a/libihash/Makefile b/libihash/Makefile
new file mode 100644
index 00000000..09bb1362
--- /dev/null
+++ b/libihash/Makefile
@@ -0,0 +1,28 @@
+# Copyright (C) 1995, 1996, 2001, 2003, 2012 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libihash
+makemode := library
+
+libname := libihash
+SRCS = ihash.c
+installhdrs = ihash.h
+
+OBJS = $(SRCS:.c=.o)
+
+include ../Makeconf
diff --git a/libihash/ihash.c b/libihash/ihash.c
new file mode 100644
index 00000000..fa29257b
--- /dev/null
+++ b/libihash/ihash.c
@@ -0,0 +1,346 @@
+/* ihash.c - Integer-keyed hash table functions.
+ Copyright (C) 1993-1997, 2001, 2003, 2004, 2006
+ Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+ Revised by Miles Bader <miles@gnu.org>.
+ Revised by Marcus Brinkmann <marcus@gnu.org>.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include "ihash.h"
+
+/* Return 1 if the slot with the index IDX in the hash table HT is
+ empty, and 0 otherwise. */
+static inline int
+index_empty (hurd_ihash_t ht, unsigned int idx)
+{
+ return ht->items[idx].value == _HURD_IHASH_EMPTY
+ || ht->items[idx].value == _HURD_IHASH_DELETED;
+}
+
+
+/* Return 1 if the index IDX in the hash table HT is occupied by the
+ element with the key KEY. */
+static inline int
+index_valid (hurd_ihash_t ht, unsigned int idx, hurd_ihash_key_t key)
+{
+ return !index_empty (ht, idx) && ht->items[idx].key == key;
+}
+
+
+/* Given a hash table HT, and a key KEY, find the index in the table
+ of that key. You must subsequently check with index_valid() if the
+ returned index is valid. */
+static inline int
+find_index (hurd_ihash_t ht, hurd_ihash_key_t key)
+{
+ unsigned int idx;
+ unsigned int up_idx;
+ unsigned int mask = ht->size - 1;
+
+ idx = key & mask;
+
+ if (ht->items[idx].value == _HURD_IHASH_EMPTY || ht->items[idx].key == key)
+ return idx;
+
+ up_idx = idx;
+
+ do
+ {
+ up_idx = (up_idx + 1) & mask;
+ if (ht->items[up_idx].value == _HURD_IHASH_EMPTY
+ || ht->items[up_idx].key == key)
+ return up_idx;
+ }
+ while (up_idx != idx);
+
+ /* If we end up here, the item could not be found. Return any
+ invalid index. */
+ return idx;
+}
+
+
+/* Remove the entry pointed to by the location pointer LOCP from the
+ hashtable HT. LOCP is the location pointer of which the address
+ was provided to hurd_ihash_add(). */
+static inline void
+locp_remove (hurd_ihash_t ht, hurd_ihash_locp_t locp)
+{
+ if (ht->cleanup)
+ (*ht->cleanup) (*locp, ht->cleanup_data);
+ *locp = _HURD_IHASH_DELETED;
+ ht->nr_items--;
+}
+
+
+/* Construction and destruction of hash tables. */
+
+/* Initialize the hash table at address HT. */
+void
+hurd_ihash_init (hurd_ihash_t ht, intptr_t locp_offs)
+{
+ ht->nr_items = 0;
+ ht->size = 0;
+ ht->locp_offset = locp_offs;
+ ht->max_load = HURD_IHASH_MAX_LOAD_DEFAULT;
+ ht->cleanup = 0;
+}
+
+
+/* Destroy the hash table at address HT. This first removes all
+ elements which are still in the hash table, and calling the cleanup
+ function for them (if any). */
+void
+hurd_ihash_destroy (hurd_ihash_t ht)
+{
+ if (ht->cleanup)
+ {
+ hurd_ihash_cleanup_t cleanup = ht->cleanup;
+ void *cleanup_data = ht->cleanup_data;
+
+ HURD_IHASH_ITERATE (ht, value)
+ (*cleanup) (value, cleanup_data);
+ }
+
+ if (ht->size > 0)
+ free (ht->items);
+}
+
+
+/* Create a hash table, initialize it and return it in HT. If a
+ memory allocation error occurs, ENOMEM is returned, otherwise 0. */
+error_t
+hurd_ihash_create (hurd_ihash_t *ht, intptr_t locp_offs)
+{
+ *ht = malloc (sizeof (struct hurd_ihash));
+ if (*ht == NULL)
+ return ENOMEM;
+
+ hurd_ihash_init (*ht, locp_offs);
+
+ return 0;
+}
+
+
+/* Destroy the hash table HT and release the memory allocated for it
+ by hurd_ihash_create(). */
+void
+hurd_ihash_free (hurd_ihash_t ht)
+{
+ hurd_ihash_destroy (ht);
+ free (ht);
+}
+
+
+/* Set the cleanup function for the hash table HT to CLEANUP. The
+ second argument to CLEANUP will be CLEANUP_DATA on every
+ invocation. */
+void
+hurd_ihash_set_cleanup (hurd_ihash_t ht, hurd_ihash_cleanup_t cleanup,
+ void *cleanup_data)
+{
+ ht->cleanup = cleanup;
+ ht->cleanup_data = cleanup_data;
+}
+
+
+/* Set the maximum load factor in binary percent to MAX_LOAD, which
+ should be between 64 and 128. The default is
+ HURD_IHASH_MAX_LOAD_DEFAULT. New elements are only added to the
+ hash table while the number of hashed elements is that much binary
+ percent of the total size of the hash table. If more elements are
+ added, the hash table is first expanded and reorganized. A
+ MAX_LOAD of 128 will always fill the whole table before enlarging
+ it, but note that this will increase the cost of operations
+ significantly when the table is almost full.
+
+ If the value is set to a smaller value than the current load
+ factor, the next reorganization will happen when a new item is
+ added to the hash table. */
+void
+hurd_ihash_set_max_load (hurd_ihash_t ht, unsigned int max_load)
+{
+ ht->max_load = max_load;
+}
+
+
+/* Helper function for hurd_ihash_add. Return 1 if the item was
+ added, and 0 if it could not be added because no empty slot was
+ found. The arguments are identical to hurd_ihash_add.
+
+ We are using open address hashing. As the hash function we use the
+ division method with linear probe. */
+static inline int
+add_one (hurd_ihash_t ht, hurd_ihash_key_t key, hurd_ihash_value_t value)
+{
+ unsigned int idx;
+ unsigned int first_free;
+ unsigned int mask = ht->size - 1;
+
+ idx = key & mask;
+ first_free = idx;
+
+ if (ht->items[idx].value != _HURD_IHASH_EMPTY && ht->items[idx].key != key)
+ {
+ unsigned int up_idx = idx;
+
+ do
+ {
+ up_idx = (up_idx + 1) & mask;
+ if (ht->items[up_idx].value == _HURD_IHASH_EMPTY
+ || ht->items[up_idx].key == key)
+ {
+ idx = up_idx;
+ break;
+ }
+ }
+ while (up_idx != idx);
+ }
+
+ /* Remove the old entry for this key if necessary. */
+ if (index_valid (ht, idx, key))
+ locp_remove (ht, &ht->items[idx].value);
+
+ /* If we have not found an empty slot, maybe the last one we
+ looked at was empty (or just got deleted). */
+ if (!index_empty (ht, first_free))
+ first_free = idx;
+
+ if (index_empty (ht, first_free))
+ {
+ ht->nr_items++;
+ ht->items[first_free].value = value;
+ ht->items[first_free].key = key;
+
+ if (ht->locp_offset != HURD_IHASH_NO_LOCP)
+ *((hurd_ihash_locp_t *) (((char *) value) + ht->locp_offset))
+ = &ht->items[first_free].value;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* Add ITEM to the hash table HT under the key KEY. If there already
+ is an item under this key, call the cleanup function (if any) for
+ it before overriding the value. If a memory allocation error
+ occurs, ENOMEM is returned, otherwise 0. */
+error_t
+hurd_ihash_add (hurd_ihash_t ht, hurd_ihash_key_t key, hurd_ihash_value_t item)
+{
+ struct hurd_ihash old_ht = *ht;
+ int was_added;
+ unsigned int i;
+
+ if (ht->size)
+ {
+ /* Only fill the hash table up to its maximum load factor. */
+ if (hurd_ihash_get_load (ht) <= ht->max_load)
+ if (add_one (ht, key, item))
+ return 0;
+ }
+
+ /* The hash table is too small, and we have to increase it. */
+ ht->nr_items = 0;
+ if (ht->size == 0)
+ ht->size = HURD_IHASH_MIN_SIZE;
+ else
+ ht->size <<= 1;
+
+ /* calloc() will initialize all values to _HURD_IHASH_EMPTY implicitly. */
+ ht->items = calloc (ht->size, sizeof (struct _hurd_ihash_item));
+
+ if (ht->items == NULL)
+ {
+ *ht = old_ht;
+ return ENOMEM;
+ }
+
+ /* We have to rehash the old entries. */
+ for (i = 0; i < old_ht.size; i++)
+ if (!index_empty (&old_ht, i))
+ {
+ was_added = add_one (ht, old_ht.items[i].key, old_ht.items[i].value);
+ assert (was_added);
+ }
+
+ /* Finally add the new element! */
+ was_added = add_one (ht, key, item);
+ assert (was_added);
+
+ if (old_ht.size > 0)
+ free (old_ht.items);
+
+ return 0;
+}
+
+
+/* Find and return the item in the hash table HT with key KEY, or NULL
+ if it doesn't exist. */
+hurd_ihash_value_t
+hurd_ihash_find (hurd_ihash_t ht, hurd_ihash_key_t key)
+{
+ if (ht->size == 0)
+ return NULL;
+ else
+ {
+ int idx = find_index (ht, key);
+ return index_valid (ht, idx, key) ? ht->items[idx].value : NULL;
+ }
+}
+
+
+/* Remove the entry with the key KEY from the hash table HT. If such
+ an entry was found and removed, 1 is returned, otherwise 0. */
+int
+hurd_ihash_remove (hurd_ihash_t ht, hurd_ihash_key_t key)
+{
+ if (ht->size != 0)
+ {
+ int idx = find_index (ht, key);
+
+ if (index_valid (ht, idx, key))
+ {
+ locp_remove (ht, &ht->items[idx].value);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+/* Remove the entry pointed to by the location pointer LOCP from the
+ hashtable HT. LOCP is the location pointer of which the address
+ was provided to hurd_ihash_add(). This call is faster than
+ hurd_ihash_remove(). */
+void
+hurd_ihash_locp_remove (hurd_ihash_t ht, hurd_ihash_locp_t locp)
+{
+ locp_remove (ht, locp);
+}
diff --git a/libihash/ihash.h b/libihash/ihash.h
new file mode 100644
index 00000000..345630d5
--- /dev/null
+++ b/libihash/ihash.h
@@ -0,0 +1,278 @@
+/* ihash.h - Integer keyed hash table interface.
+ Copyright (C) 1995, 2003, 2004 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>.
+ Revised by Marcus Brinkmann <marcus@gnu.org>.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _HURD_IHASH_H
+#define _HURD_IHASH_H 1
+
+#include <errno.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <stdint.h>
+
+
+/* The type of the values corresponding to the keys. Must be a
+ pointer type. The values (hurd_ihash_value_t) 0 and
+ (hurd_ihash_value_t) ~0 are reserved for the implementation. */
+typedef void *hurd_ihash_value_t;
+
+/* When an value entry in the hash table is _HURD_IHASH_EMPTY or
+ _HURD_IHASH_DELETED, then the location is available, and none of
+ the other members of the item are valid at that index. The
+ difference is that searches continue though _HURD_IHASH_DELETED,
+ but stop at _HURD_IHASH_EMPTY. */
+#define _HURD_IHASH_EMPTY ((hurd_ihash_value_t) 0)
+#define _HURD_IHASH_DELETED ((hurd_ihash_value_t) -1)
+
+/* The type of integer we want to use for the keys. */
+typedef uintptr_t hurd_ihash_key_t;
+
+/* The type of a location pointer, which is a pointer to the hash
+ value stored in the hash table. */
+typedef hurd_ihash_value_t *hurd_ihash_locp_t;
+
+
+/* The type of the cleanup function, which is called for every value
+ removed from the hash table. */
+typedef void (*hurd_ihash_cleanup_t) (hurd_ihash_value_t value, void *arg);
+
+
+struct _hurd_ihash_item
+{
+ /* The value of this hash item. Must be the first element of
+ the struct for the HURD_IHASH_ITERATE macro. */
+ hurd_ihash_value_t value;
+
+ /* The integer key of this hash item. */
+ hurd_ihash_key_t key;
+};
+typedef struct _hurd_ihash_item *_hurd_ihash_item_t;
+
+struct hurd_ihash
+{
+ /* The number of hashed elements. */
+ size_t nr_items;
+
+ /* An array of (key, value) pairs. */
+ _hurd_ihash_item_t items;
+
+ /* The length of the array ITEMS. */
+ size_t size;
+
+ /* The offset of the location pointer from the hash value. */
+ intptr_t locp_offset;
+
+ /* The maximum load factor in binary percent. */
+ unsigned int max_load;
+
+ /* When freeing or overwriting an element, this function is called
+ with the value as the first argument, and CLEANUP_DATA as the
+ second argument. This does not happen if CLEANUP is NULL. */
+ hurd_ihash_cleanup_t cleanup;
+ void *cleanup_data;
+};
+typedef struct hurd_ihash *hurd_ihash_t;
+
+
+/* Construction and destruction of hash tables. */
+
+/* The size of the initial allocation in number of items. This must
+ be a power of two. */
+#define HURD_IHASH_MIN_SIZE 32
+
+/* The default value for the maximum load factor in binary percent.
+ 96b% is equivalent to 75%, 128b% to 100%. */
+#define HURD_IHASH_MAX_LOAD_DEFAULT 96
+
+/* The LOCP_OFFS to use if no location pointer is available. */
+#define HURD_IHASH_NO_LOCP INTPTR_MIN
+
+/* The static initializer for a struct hurd_ihash. */
+#define HURD_IHASH_INITIALIZER(locp_offs) \
+ { .nr_items = 0, .size = 0, .cleanup = (hurd_ihash_cleanup_t) 0, \
+ .max_load = HURD_IHASH_MAX_LOAD_DEFAULT, \
+ .locp_offset = (locp_offs)}
+
+/* Initialize the hash table at address HT. If LOCP_OFFSET is not
+ HURD_IHASH_NO_LOCP, then this is an offset (in bytes) from the
+ address of a hash value where a location pointer can be found. The
+ location pointer must be of type hurd_ihash_locp_t and can be used
+ for fast removal with hurd_ihash_locp_remove(). */
+void hurd_ihash_init (hurd_ihash_t ht, intptr_t locp_offs);
+
+/* Destroy the hash table at address HT. This first removes all
+ elements which are still in the hash table, and calling the cleanup
+ function for them (if any). */
+void hurd_ihash_destroy (hurd_ihash_t ht);
+
+/* Create a hash table, initialize it and return it in HT. If
+ LOCP_OFFSET is not HURD_IHASH_NO_LOCP, then this is an offset (in
+ bytes) from the address of a hash value where a location pointer
+ can be found. The location pointer must be of type
+ hurd_ihash_locp_t and can be used for fast removal with
+ hurd_ihash_locp_remove(). If a memory allocation error occurs,
+ ENOMEM is returned, otherwise 0. */
+error_t hurd_ihash_create (hurd_ihash_t *ht, intptr_t locp_offs);
+
+/* Destroy the hash table HT and release the memory allocated for it
+ by hurd_ihash_create(). */
+void hurd_ihash_free (hurd_ihash_t ht);
+
+
+/* Configuration of the hash table. */
+
+/* Set the cleanup function for the hash table HT to CLEANUP. The
+ second argument to CLEANUP will be CLEANUP_DATA on every
+ invocation. */
+void hurd_ihash_set_cleanup (hurd_ihash_t ht, hurd_ihash_cleanup_t cleanup,
+ void *cleanup_data);
+
+/* Set the maximum load factor in binary percent to MAX_LOAD, which
+ should be between 64 and 128. The default is
+ HURD_IHASH_MAX_LOAD_DEFAULT. New elements are only added to the
+ hash table while the number of hashed elements is that much binary
+ percent of the total size of the hash table. If more elements are
+ added, the hash table is first expanded and reorganized. A
+ MAX_LOAD of 128 will always fill the whole table before enlarging
+ it, but note that this will increase the cost of operations
+ significantly when the table is almost full.
+
+ If the value is set to a smaller value than the current load
+ factor, the next reorganization will happen when a new item is
+ added to the hash table. */
+void hurd_ihash_set_max_load (hurd_ihash_t ht, unsigned int max_load);
+
+
+/* Get the current load factor of HT in binary percent, where 128b%
+ corresponds to 100%. The reason we do this is that it is so
+ efficient to compute:
+
+ As the size is always a power of two, and 128 is also, the quotient
+ of both is also a power of two. Therefore, we can use bit shifts
+ to scale the number of items.
+
+ load = nr_items * 128 / size
+ = nr_items * 2^{log2 (128) - log2 (size)}
+ = nr_items >> (log2 (size) - log2 (128))
+ -- if size >= 128
+ = nr_items << (log2 (128) - log2 (size))
+ -- otherwise
+
+ If you want to convert this to percent, just divide by 1.28. */
+static inline unsigned int
+hurd_ihash_get_load (hurd_ihash_t ht)
+{
+ int d = __builtin_ctzl (ht->size) - 7;
+ return d >= 0 ? ht->nr_items >> d : ht->nr_items << -d;
+}
+
+
+/* Add ITEM to the hash table HT under the key KEY. If there already
+ is an item under this key, call the cleanup function (if any) for
+ it before overriding the value. If a memory allocation error
+ occurs, ENOMEM is returned, otherwise 0. */
+error_t hurd_ihash_add (hurd_ihash_t ht, hurd_ihash_key_t key,
+ hurd_ihash_value_t item);
+
+/* Find and return the item in the hash table HT with key KEY, or NULL
+ if it doesn't exist. */
+hurd_ihash_value_t hurd_ihash_find (hurd_ihash_t ht, hurd_ihash_key_t key);
+
+/* Iterate over all elements in the hash table. You use this macro
+ with a block, for example like this:
+
+ error_t err;
+ HURD_IHASH_ITERATE (ht, value)
+ {
+ err = foo (value);
+ if (err)
+ break;
+ }
+ if (err)
+ cleanup_and_return ();
+
+ Or even like this:
+
+ HURD_IHASH_ITERATE (ht, value)
+ foo (value);
+
+ The block will be run for every element in the hash table HT. The
+ value of the current element is available in the variable VALUE
+ (which is declared for you and local to the block). */
+
+/* The implementation of this macro is peculiar. We want the macro to
+ execute a block following its invocation, so we can only prepend
+ code. This excludes creating an outer block. However, we must
+ define two variables: The hash value variable VALUE, and the loop
+ variable.
+
+ We can define variables inside the for-loop initializer (C99), but
+ we can only use one basic type to do that. We can not use two
+ for-loops, because we want a break statement inside the iterator
+ block to terminate the operation. So we must have both variables
+ of the same basic type, but we can make one (or both) of them a
+ pointer type.
+
+ The pointer to the value can be used as the loop variable. This is
+ also the first element of the hash item, so we can cast the pointer
+ freely between these two types. The pointer is only dereferenced
+ after the loop condition is checked (but of course the value the
+ pointer pointed to must not have an influence on the condition
+ result, so the comma operator is used to make sure this
+ subexpression is always true). */
+#define HURD_IHASH_ITERATE(ht, val) \
+ for (hurd_ihash_value_t val, \
+ *_hurd_ihash_valuep = (ht)->size ? &(ht)->items[0].value : 0; \
+ (ht)->size \
+ && ((_hurd_ihash_item_t) _hurd_ihash_valuep) - &(ht)->items[0] \
+ < (ht)->size \
+ && (val = *_hurd_ihash_valuep, 1); \
+ _hurd_ihash_valuep = (hurd_ihash_value_t *) \
+ (((_hurd_ihash_item_t) _hurd_ihash_valuep) + 1)) \
+ if (val != _HURD_IHASH_EMPTY && val != _HURD_IHASH_DELETED)
+
+/* Iterate over all elements in the hash table making both the key and
+ the value available. You use this macro with a block, for example
+ like this:
+
+ HURD_IHASH_ITERATE_ITEMS (ht, item)
+ foo (item->key, item->value);
+
+ The block will be run for every element in the hash table HT. The
+ key and value of the current element is available as ITEM->key and
+ ITEM->value. */
+#define HURD_IHASH_ITERATE_ITEMS(ht, item) \
+ for (_hurd_ihash_item_t item = (ht)->size? &(ht)->items[0]: 0; \
+ (ht)->size && item - &(ht)->items[0] < (ht)->size; \
+ item++) \
+ if (item->value != _HURD_IHASH_EMPTY && \
+ item->value != _HURD_IHASH_DELETED)
+
+/* Remove the entry with the key KEY from the hash table HT. If such
+ an entry was found and removed, 1 is returned, otherwise 0. */
+int hurd_ihash_remove (hurd_ihash_t ht, hurd_ihash_key_t key);
+
+/* Remove the entry pointed to by the location pointer LOCP from the
+ hash table HT. LOCP is the location pointer of which the address
+ was provided to hurd_ihash_add(). This call is faster than
+ hurd_ihash_remove(). */
+void hurd_ihash_locp_remove (hurd_ihash_t ht, hurd_ihash_locp_t locp);
+
+#endif /* _HURD_IHASH_H */
diff --git a/libiohelp/Makefile b/libiohelp/Makefile
new file mode 100644
index 00000000..f0dd48f0
--- /dev/null
+++ b/libiohelp/Makefile
@@ -0,0 +1,31 @@
+# Copyright (C) 1993, 1994, 1995, 1996, 1998, 2002, 2008, 2012 Free Software
+# Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libiohelp
+makemode := library
+
+SRCS = get_conch.c handle_io_get_conch.c handle_io_release_conch.c \
+ initialize_conch.c verify_user_conch.c iouser-create.c \
+ iouser-dup.c iouser-reauth.c iouser-free.c iouser-restrict.c \
+ shared.c return-buffer.c
+OBJS = $(SRCS:.c=.o)
+HURDLIBS = shouldbeinlibc
+LDLIBS += -lpthread
+libname = libiohelp
+installhdrs = iohelp.h
+
+include ../Makeconf
diff --git a/libiohelp/get_conch.c b/libiohelp/get_conch.c
new file mode 100644
index 00000000..e3af6939
--- /dev/null
+++ b/libiohelp/get_conch.c
@@ -0,0 +1,57 @@
+/*
+ Copyright (C) 1993, 1994, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "iohelp.h"
+
+/* The conch must be locked when calling this routine. */
+/* Remove any current holder of conch C. */
+void
+iohelp_get_conch (struct conch *c)
+{
+ struct shared_io *user_sh;
+
+ again:
+ user_sh = c->holder_shared_page;
+
+ if (user_sh)
+ {
+ pthread_spin_lock (&user_sh->lock);
+ switch (user_sh->conch_status)
+ {
+ case USER_HAS_CONCH:
+ user_sh->conch_status = USER_RELEASE_CONCH;
+ /* fall through ... */
+ case USER_RELEASE_CONCH:
+ pthread_spin_unlock (&user_sh->lock);
+ pthread_cond_wait (&c->wait, c->lock);
+ /* Anything can have happened */
+ goto again;
+
+ case USER_COULD_HAVE_CONCH:
+ user_sh->conch_status = USER_HAS_NOT_CONCH;
+ pthread_spin_unlock (&user_sh->lock);
+ iohelp_fetch_shared_data (c->holder);
+ break;
+
+ case USER_HAS_NOT_CONCH:
+ pthread_spin_unlock (&user_sh->lock);
+ break;
+ }
+ }
+ c->holder = 0;
+ c->holder_shared_page = 0;
+}
diff --git a/libiohelp/handle_io_get_conch.c b/libiohelp/handle_io_get_conch.c
new file mode 100644
index 00000000..452b3cfb
--- /dev/null
+++ b/libiohelp/handle_io_get_conch.c
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 19931996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "iohelp.h"
+
+/* Called by an I/O server when an io_get_conch message is received.
+ The user represented by USER and USER_SH wants conch C; give it
+ to her or return an error. */
+void
+iohelp_handle_io_get_conch (struct conch *c, void *user,
+ struct shared_io *user_sh)
+{
+
+ if (c->holder == user)
+ {
+ if (user_sh->conch_status != USER_HAS_NOT_CONCH)
+ iohelp_fetch_shared_data (user);
+ else
+ user_sh->accessed = user_sh->written = 0;
+
+ iohelp_put_shared_data (user);
+ user_sh->conch_status = USER_HAS_CONCH;
+ }
+ else
+ {
+ iohelp_get_conch (c);
+
+ c->holder = user;
+ c->holder_shared_page = user_sh;
+ if (user_sh->conch_status == USER_HAS_NOT_CONCH)
+ user_sh->accessed = user_sh->written = 0;
+ user_sh->conch_status = USER_HAS_CONCH;
+ iohelp_put_shared_data (user);
+ }
+}
diff --git a/libiohelp/handle_io_release_conch.c b/libiohelp/handle_io_release_conch.c
new file mode 100644
index 00000000..01328fe2
--- /dev/null
+++ b/libiohelp/handle_io_release_conch.c
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 1993, 1994, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "iohelp.h"
+
+/* Called by an I/O server upon receipt of an io_release_conch message;
+ The user identified by USER is done with conch C; release it and
+ allow a waiting user to obtain the conch. */
+void
+iohelp_handle_io_release_conch (struct conch *c, void *user)
+{
+ struct shared_io *user_sh = c->holder_shared_page;
+
+ pthread_spin_lock (&user_sh->lock);
+ if (c->holder_shared_page->conch_status != USER_HAS_NOT_CONCH)
+ {
+ c->holder_shared_page->conch_status = USER_HAS_NOT_CONCH;
+ iohelp_fetch_shared_data (c->holder);
+ }
+ pthread_spin_unlock (&user_sh->lock);
+
+ if (c->holder == user)
+ {
+ c->holder = 0;
+ c->holder_shared_page = 0;
+ }
+
+ pthread_cond_broadcast (&c->wait);
+}
diff --git a/libiohelp/initialize_conch.c b/libiohelp/initialize_conch.c
new file mode 100644
index 00000000..50bab231
--- /dev/null
+++ b/libiohelp/initialize_conch.c
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 1993, 1994, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "iohelp.h"
+
+/* Called by an I/O server to initialize a conch structure C;
+ M will be used to lock conch data structures. */
+void
+iohelp_initialize_conch (struct conch *c, pthread_mutex_t *m)
+{
+ c->lock = m;
+ pthread_cond_init (&c->wait, NULL);
+ c->holder = 0;
+ c->holder_shared_page = 0;
+}
diff --git a/libiohelp/iohelp.h b/libiohelp/iohelp.h
new file mode 100644
index 00000000..0597d6c7
--- /dev/null
+++ b/libiohelp/iohelp.h
@@ -0,0 +1,133 @@
+/* Library providing helper functions for io servers.
+ Copyright (C) 1993,94,96,98,2001,02 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _HURD_IOHELP_
+#define _HURD_IOHELP_
+
+#include <mach.h>
+#include <hurd/hurd_types.h>
+#include <pthread.h>
+#include <hurd/shared.h>
+
+/* Conch manipulation. */
+struct conch
+{
+ pthread_mutex_t *lock;
+ pthread_cond_t wait;
+ void *holder;
+ struct shared_io *holder_shared_page;
+};
+
+/* Initialize a conch box */
+void iohelp_initialize_conch (struct conch *, pthread_mutex_t *);
+
+/* These routines are not reentrant. The server is responsible
+ for ensuring that all calls to these routines are serialized
+ by locking the lock passed to initialize_conch. */
+
+/* Handle a user request to obtain the conch (io_get_conch) */
+void iohelp_handle_io_get_conch (struct conch *, void *,
+ struct shared_io *);
+
+/* Obtain the conch for the server */
+void iohelp_get_conch (struct conch *);
+
+/* Handle a user request to release the conch (io_release_conch). */
+void iohelp_handle_io_release_conch (struct conch *, void *);
+
+/* Check if the user is allowed to make a shared-data notification
+ message. */
+error_t iohelp_verify_user_conch (struct conch *, void *);
+
+/* This function must by defined by the server. It should transfer
+ information from the current conch holder's shared page to the server's
+ data (the arg is the conch owner). */
+void iohelp_fetch_shared_data (void *);
+
+/* This function must be defined by the server. It should transfer
+ information from the server's data to the current conch holder's
+ shared page (the arg is the conch owner). */
+void iohelp_put_shared_data (void *);
+
+
+
+/* User identification */
+
+#include <idvec.h>
+
+struct iouser
+{
+ struct idvec *uids, *gids;
+ void *hook; /* Never used by iohelp library */
+};
+
+/* Return a copy of IOUSER in CLONE. On error, *CLONE is set to NULL. */
+error_t iohelp_dup_iouser (struct iouser **clone, struct iouser *iouser);
+
+/* Free a reference to IOUSER. */
+void iohelp_free_iouser (struct iouser *iouser);
+
+/* Create a new IOUSER in USER for the specified idvecs. On error, *USER
+ is set to NULL. */
+error_t iohelp_create_iouser (struct iouser **user, struct idvec *uids,
+ struct idvec *gids);
+
+/* Create a new IOUSER in USER for the specified arrays. On error, *USER
+ is set to NULL. */
+error_t iohelp_create_complex_iouser (struct iouser **user,
+ const uid_t *uids, int nuids,
+ const gid_t *gids, int ngids);
+
+/* Create a new IOUSER in USER for the specified uid and gid. On error,
+ *USER is set to NULL. */
+error_t iohelp_create_simple_iouser (struct iouser **user,
+ uid_t uid, gid_t gid);
+
+/* Create a new IOUSER in USER with no identity. On error, *USER is set
+ to NULL. */
+error_t iohelp_create_empty_iouser (struct iouser **user);
+
+/* Create a new IOUSER in NEW_USER that restricts OLD_USER to the subset
+ specified by the two ID lists. This is appropriate for implementing
+ io_restrict_auth. */
+error_t iohelp_restrict_iouser (struct iouser **new_user,
+ const struct iouser *old_user,
+ const uid_t *uids, int nuids,
+ const gid_t *gids, int ngids);
+
+/* Conduct a reauthentication transaction, returning a new iouser in
+ USER. AUTHSERVER is the I/O servers auth port. The rendezvous port
+ provided by the user is REND_PORT. If the transaction cannot be
+ completed, return zero, unless PERMIT_FAILURE is non-zero. If
+ PERMIT_FAILURE is nonzero, then should the transaction fail, return
+ an iouser that has no ids. The new port to be sent to the user is
+ newright. On error, *USER is set to NULL. */
+error_t iohelp_reauth (struct iouser **user, auth_t authserver,
+ mach_port_t rend_port, mach_port_t newright,
+ int permit_failure);
+
+
+/* Puts data from the malloced buffer BUF, LEN bytes long, into RBUF & RLEN,
+ suitable for returning from a mach rpc. If LEN > 0, BUF is freed,
+ regardless of whether an error is returned or not. */
+error_t iohelp_return_malloced_buffer (char *buf, size_t len,
+ char **rbuf,
+ mach_msg_type_number_t *rlen);
+
+
+
+#endif
diff --git a/libiohelp/iouser-create.c b/libiohelp/iouser-create.c
new file mode 100644
index 00000000..980c8a1e
--- /dev/null
+++ b/libiohelp/iouser-create.c
@@ -0,0 +1,112 @@
+/*
+ Copyright (C) 1996,2001,02 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "iohelp.h"
+
+error_t
+iohelp_create_iouser (struct iouser **user, struct idvec *uids,
+ struct idvec *gids)
+{
+ struct iouser *new;
+ *user = new = malloc (sizeof (struct iouser));
+ if (!new)
+ return ENOMEM;
+
+ new->uids = uids;
+ new->gids = gids;
+ new->hook = 0;
+
+ return 0;
+}
+
+#define E(err_) \
+ do { \
+ error_t err = err_; \
+ if (err) \
+ { \
+ *user = 0; \
+ if (! uids) \
+ return err; \
+ idvec_free (uids); \
+ if (! gids) \
+ return err; \
+ idvec_free (gids); \
+ return err; \
+ } \
+ } while (0)
+
+error_t
+iohelp_create_empty_iouser (struct iouser **user)
+{
+ struct idvec *uids, *gids;
+
+ uids = make_idvec ();
+ if (! uids)
+ E (ENOMEM);
+
+ gids = make_idvec ();
+ if (! gids)
+ E (ENOMEM);
+
+ E (iohelp_create_iouser (user, uids, gids));
+
+ return 0;
+}
+
+error_t
+iohelp_create_simple_iouser (struct iouser **user, uid_t uid, gid_t gid)
+{
+ struct idvec *uids, *gids;
+
+ uids = make_idvec ();
+ if (! uids)
+ E (ENOMEM);
+
+ gids = make_idvec ();
+ if (! gids)
+ E (ENOMEM);
+
+ E (idvec_add (uids, uid));
+ E (idvec_add (gids, gid));
+
+ E (iohelp_create_iouser (user, uids, gids));
+
+ return 0;
+}
+
+error_t
+iohelp_create_complex_iouser (struct iouser **user,
+ const uid_t *uvec, int nuids,
+ const gid_t *gvec, int ngids)
+{
+ struct idvec *uids, *gids;
+
+ uids = make_idvec ();
+ if (! uids)
+ E (ENOMEM);
+
+ gids = make_idvec ();
+ if (! gids)
+ E (ENOMEM);
+
+ E (idvec_set_ids (uids, uvec, nuids));
+ E (idvec_set_ids (gids, gvec, ngids));
+
+ E (iohelp_create_iouser (user, uids, gids));
+
+ return 0;
+}
diff --git a/libiohelp/iouser-dup.c b/libiohelp/iouser-dup.c
new file mode 100644
index 00000000..ae55ad1d
--- /dev/null
+++ b/libiohelp/iouser-dup.c
@@ -0,0 +1,58 @@
+/*
+ Copyright (C) 1996,2001 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "iohelp.h"
+
+#include <stdlib.h>
+
+error_t
+iohelp_dup_iouser (struct iouser **clone, struct iouser *iouser)
+{
+ struct iouser *new;
+ error_t err = 0;
+
+ *clone = new = malloc (sizeof (struct iouser));
+ if (!new)
+ return ENOMEM;
+
+ new->uids = make_idvec ();
+ new->gids = make_idvec ();
+ new->hook = 0;
+ if (!new->uids || !new->gids)
+ {
+ err = ENOMEM;
+ goto lose;
+ }
+
+ err = idvec_set (new->uids, iouser->uids);
+ if (!err)
+ err = idvec_set (new->gids, iouser->gids);
+
+ if (err)
+ {
+ lose:
+ if (new->uids)
+ idvec_free (new->uids);
+ if (new->gids)
+ idvec_free (new->gids);
+ free (new);
+ *clone = 0;
+ return err;
+ }
+
+ return 0;
+}
diff --git a/libiohelp/iouser-free.c b/libiohelp/iouser-free.c
new file mode 100644
index 00000000..06229603
--- /dev/null
+++ b/libiohelp/iouser-free.c
@@ -0,0 +1,28 @@
+/*
+ Copyright (C) 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "iohelp.h"
+
+#include <stdlib.h>
+
+void
+iohelp_free_iouser (struct iouser *iouser)
+{
+ idvec_free (iouser->uids);
+ idvec_free (iouser->gids);
+ free (iouser);
+}
diff --git a/libiohelp/iouser-reauth.c b/libiohelp/iouser-reauth.c
new file mode 100644
index 00000000..9edab7cf
--- /dev/null
+++ b/libiohelp/iouser-reauth.c
@@ -0,0 +1,107 @@
+/*
+ Copyright (C) 1996,99,2001,02 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "iohelp.h"
+#include <hurd/auth.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+
+/* Conduct a reauthentication transaction, returning a new iouser.
+ AUTHSERVER is the I/O servers auth port. The rendezvous port
+ provided by the user is REND_PORT. If the transaction cannot be
+ completed, return zero, unless PERMIT_FAILURE is non-zero. If
+ PERMIT_FAILURE is nonzero, then should the transaction fail, return
+ an iouser that has no ids. The new port to be sent to the user is
+ newright. */
+error_t iohelp_reauth (struct iouser **user,
+ auth_t authserver, mach_port_t rend_port,
+ mach_port_t newright, int permit_failure)
+{
+ uid_t gubuf[20], ggbuf[20], aubuf[20], agbuf[20];
+ uid_t *gen_uids, *gen_gids, *aux_uids, *aux_gids;
+ size_t genuidlen, gengidlen, auxuidlen, auxgidlen;
+ error_t err;
+ struct iouser *new;
+
+ *user = new = malloc (sizeof (struct iouser));
+ if (!new)
+ return ENOMEM;
+
+ new->uids = make_idvec ();
+ new->gids = make_idvec ();
+ if (!new->uids || !new->gids)
+ {
+ if (new->uids)
+ idvec_free (new->uids);
+ if (new->gids)
+ idvec_free (new->gids);
+ free (new);
+ return ENOMEM;
+ }
+
+ genuidlen = gengidlen = auxuidlen = auxgidlen = 20;
+ gen_uids = gubuf;
+ gen_gids = ggbuf;
+ aux_uids = aubuf;
+ aux_gids = agbuf;
+
+ do
+ err = auth_server_authenticate (authserver,
+ rend_port,
+ MACH_MSG_TYPE_COPY_SEND,
+ newright,
+ MACH_MSG_TYPE_COPY_SEND,
+ &gen_uids, &genuidlen,
+ &aux_uids, &auxuidlen,
+ &gen_gids, &gengidlen,
+ &aux_gids, &auxgidlen);
+ while (err == EINTR);
+
+ if (err)
+ {
+ if (permit_failure)
+ genuidlen = gengidlen = 0;
+ else
+ goto out;
+ }
+
+ err = idvec_set_ids (new->uids, gen_uids, genuidlen);
+ if (!err)
+ err = idvec_set_ids (new->gids, gen_gids, gengidlen);
+
+ if (gubuf != gen_uids)
+ munmap ((caddr_t) gen_uids, genuidlen * sizeof (uid_t));
+ if (ggbuf != gen_gids)
+ munmap ((caddr_t) gen_gids, gengidlen * sizeof (uid_t));
+ if (aubuf != aux_uids)
+ munmap ((caddr_t) aux_uids, auxuidlen * sizeof (uid_t));
+ if (agbuf != aux_gids)
+ munmap ((caddr_t) aux_gids, auxgidlen * sizeof (uid_t));
+
+ if (err)
+ {
+ out:
+ idvec_free (new->uids);
+ idvec_free (new->gids);
+ free (new);
+ *user = 0;
+ return err;
+ }
+
+ *user = new;
+ return 0;
+}
diff --git a/libiohelp/iouser-restrict.c b/libiohelp/iouser-restrict.c
new file mode 100644
index 00000000..184061e1
--- /dev/null
+++ b/libiohelp/iouser-restrict.c
@@ -0,0 +1,83 @@
+/* iohelp_restrict_iouser -- helper for io_restrict_auth implementations
+ Copyright (C) 2002 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "iohelp.h"
+
+/* Tell if the array LIST (of size N) contains a member equal to QUERY. */
+static inline int
+listmember (const uid_t *list, uid_t query, int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ if (list[i] == query)
+ return 1;
+ return 0;
+}
+
+error_t
+iohelp_restrict_iouser (struct iouser **new_user,
+ const struct iouser *old_user,
+ const uid_t *uids, int nuids,
+ const gid_t *gids, int ngids)
+{
+ if (idvec_contains (old_user->uids, 0))
+ /* OLD_USER has root access, and so may use any ids. */
+ return iohelp_create_complex_iouser (new_user, uids, nuids, gids, ngids);
+ else
+ {
+ struct idvec *uvec, *gvec;
+ unsigned int i;
+ error_t err;
+
+ uvec = make_idvec ();
+ if (! uvec)
+ return ENOMEM;
+
+ gvec = make_idvec ();
+ if (! gvec)
+ {
+ idvec_free (uvec);
+ return ENOMEM;
+ }
+
+ /* Otherwise, use any of the requested ids that OLD_USER already has. */
+ for (i = 0; i < old_user->uids->num; i++)
+ if (listmember (uids, old_user->uids->ids[i], nuids))
+ {
+ err = idvec_add (uvec, old_user->uids->ids[i]);
+ if (err)
+ goto out;
+ }
+ for (i = 0; i < old_user->gids->num; i++)
+ if (listmember (gids, old_user->gids->ids[i], ngids))
+ {
+ err = idvec_add (gvec, old_user->gids->ids[i]);
+ if (err)
+ goto out;
+ }
+
+ err = iohelp_create_iouser (new_user, uvec, gvec);
+
+ if (err)
+ {
+ out:
+ idvec_free (uvec);
+ idvec_free (gvec);
+ }
+ return err;
+ }
+}
diff --git a/libiohelp/return-buffer.c b/libiohelp/return-buffer.c
new file mode 100644
index 00000000..e186698f
--- /dev/null
+++ b/libiohelp/return-buffer.c
@@ -0,0 +1,55 @@
+/* Make a malloced buffer suitable for returning from a mach rpc
+
+ Copyright (C) 1996, 1998, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h>
+#include <mach.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+
+#include "iohelp.h"
+
+/* Puts data from the malloced buffer BUF, LEN bytes long, into RBUF & RLEN,
+ suitable for returning from a mach rpc. If LEN > 0, BUF is freed,
+ regardless of whether an error is returned or not. */
+error_t
+iohelp_return_malloced_buffer (char *buf, size_t len,
+ char **rbuf, mach_msg_type_number_t *rlen)
+{
+ error_t err = 0;
+
+ if (*rlen < len)
+ {
+ *rbuf = mmap (0, len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ err = (*rbuf == (char *) -1) ? errno : 0;
+ }
+ if (! err)
+ {
+ if (len)
+ bcopy (buf, *rbuf, len);
+ *rlen = len;
+ }
+
+ if (len > 0)
+ free (buf);
+
+ return err;
+}
diff --git a/libiohelp/shared.c b/libiohelp/shared.c
new file mode 100644
index 00000000..b31a7170
--- /dev/null
+++ b/libiohelp/shared.c
@@ -0,0 +1,35 @@
+/* Default functions
+ Copyright (C) 1996, 2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "iohelp.h"
+
+#include <stdlib.h>
+
+/* These definitions exist to satisfy the linker. */
+
+void __attribute__ ((weak))
+iohelp_fetch_shared_data (void *foo)
+{
+ abort ();
+}
+
+void __attribute__ ((weak))
+iohelp_put_shared_data (void *foo)
+{
+ abort ();
+}
diff --git a/libiohelp/verify_user_conch.c b/libiohelp/verify_user_conch.c
new file mode 100644
index 00000000..e975cc88
--- /dev/null
+++ b/libiohelp/verify_user_conch.c
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 1993, 1994, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "iohelp.h"
+#include <errno.h>
+
+/* Check to see if the user identified by USER has conch C; if not,
+ return an error, else, return 0. */
+error_t
+iohelp_verify_user_conch (struct conch *c, void *user)
+{
+ struct shared_io *user_sh;
+
+ if (user != c->holder)
+ return EPERM;
+ user_sh = c->holder_shared_page;
+ pthread_spin_lock (&user_sh->lock);
+ if (user_sh->conch_status != USER_HAS_CONCH
+ && user_sh->conch_status != USER_RELEASE_CONCH)
+ {
+ pthread_spin_unlock (&user_sh->lock);
+ return EPERM;
+ }
+ pthread_spin_unlock (&user_sh->lock);
+ return 0;
+}
diff --git a/libnetfs/Makefile b/libnetfs/Makefile
new file mode 100644
index 00000000..c3830c03
--- /dev/null
+++ b/libnetfs/Makefile
@@ -0,0 +1,75 @@
+# Copyright (C) 1995, 1996, 1997, 1999, 2001, 2002, 2008, 2012 Free Software
+# Foundation
+#
+# Written by Michael I. Bushnell.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libnetfs
+makemode := library
+libname = libnetfs
+
+HURDLIBS = fshelp iohelp ports shouldbeinlibc
+LDLIBS += -lpthread
+
+FSSRCS= dir-link.c dir-lookup.c dir-mkdir.c dir-mkfile.c \
+ dir-notice-changes.c dir-readdir.c dir-rename.c \
+ dir-rmdir.c dir-unlink.c file-chauthor.c \
+ file-check-access.c file-chflags.c file-chmod.c file-chown.c \
+ file-exec.c file-get-fs-options.c file-get-storage-info.c \
+ file-get-translator.c file-getcontrol.c file-getlinknode.c \
+ file-lock-stat.c file-lock.c file-set-size.c \
+ file-set-translator.c file-statfs.c file-sync.c file-syncfs.c \
+ file-utimes.c file-reparent.c fsstubs.c file-get-transcntl.c \
+ get-source.c
+
+IOSRCS= io-read.c io-readable.c io-seek.c io-write.c io-stat.c io-async.c \
+ io-set-all-openmodes.c io-get-openmodes.c io-set-some-openmodes.c \
+ io-clear-some-openmodes.c io-mod-owner.c io-get-owner.c io-select.c \
+ io-get-icky-async-id.c io-reauthenticate.c io-restrict-auth.c \
+ io-duplicate.c iostubs.c io-identity.c io-revoke.c io-pathconf.c \
+ io-version.c
+
+FSYSSRCS= fsys-syncfs.c fsys-getroot.c fsys-get-options.c fsys-set-options.c \
+ fsys-goaway.c fsysstubs.c file-get-children.c file-get-source.c
+
+IFSOCKSRCS=
+OTHERSRCS= drop-node.c init-init.c make-node.c make-peropen.c make-protid.c \
+ init-loop.c demuxer.c shutdown.c release-protid.c release-peropen.c \
+ init-startup.c startup-argp.c set-options.c append-args.c \
+ runtime-argp.c std-runtime-argp.c std-startup-argp.c \
+ append-std-options.c trans-callback.c set-get-trans.c \
+ nref.c nrele.c nput.c file-get-storage-info-default.c dead-name.c
+
+SRCS= $(OTHERSRCS) $(FSSRCS) $(IOSRCS) $(FSYSSRCS) $(IFSOCKSRCS)
+
+installhdrs=netfs.h
+
+MIGSTUBS= ioServer.o fsServer.o fsysServer.o fsys_replyUser.o ifsockServer.o
+
+OBJS=$(sort $(SRCS:.c=.o) $(MIGSTUBS))
+
+fsys-MIGSFLAGS = -imacros $(srcdir)/mutations.h -DREPLY_PORTS
+fs-MIGSFLAGS = -imacros $(srcdir)/mutations.h
+io-MIGSFLAGS = -imacros $(srcdir)/mutations.h
+ifsock-MIGSFLAGS = -imacros $(srcdir)/mutations.h
+MIGCOMSFLAGS = -prefix netfs_
+
+
+include ../Makeconf
+
+fsysServer.c fsys_S.h fsServer.c fs_S.h ioServer.c io_S.h ifsockServer.c ifsock_S.h: mutations.h
diff --git a/libnetfs/append-args.c b/libnetfs/append-args.c
new file mode 100644
index 00000000..867c7bbd
--- /dev/null
+++ b/libnetfs/append-args.c
@@ -0,0 +1,31 @@
+/* Append current command-line arguments
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "netfs.h"
+
+/* Append to the malloced string *ARGZ of length *ARGZ_LEN a NUL-separated
+ list of the arguments to this translator. */
+error_t
+netfs_append_args (char **argz, size_t *argz_len)
+{
+ return netfs_append_std_options (argz, argz_len);
+}
diff --git a/libnetfs/append-std-options.c b/libnetfs/append-std-options.c
new file mode 100644
index 00000000..e5d79631
--- /dev/null
+++ b/libnetfs/append-std-options.c
@@ -0,0 +1,30 @@
+/* Get standard netfs run-time options
+
+ Copyright (C) 1995,96,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "netfs.h"
+
+/* Appends to ARGZ & ARGZ_LEN '\0'-separated options describing the standard
+ netfs option state. */
+error_t
+netfs_append_std_options (char **argz, size_t *argz_len)
+{
+ return 0;
+}
diff --git a/libnetfs/callbacks.h b/libnetfs/callbacks.h
new file mode 100644
index 00000000..1dc3ab06
--- /dev/null
+++ b/libnetfs/callbacks.h
@@ -0,0 +1,25 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+/* Translator callback function declarations. */
+
+fshelp_fetch_root_callback1_t _netfs_translator_callback1;
+fshelp_fetch_root_callback2_t _netfs_translator_callback2;
diff --git a/libnetfs/dead-name.c b/libnetfs/dead-name.c
new file mode 100644
index 00000000..6f2d78d6
--- /dev/null
+++ b/libnetfs/dead-name.c
@@ -0,0 +1,47 @@
+/* Handle dead name notifications on ports
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+void
+ports_dead_name (void *notify, mach_port_t dead_name)
+{
+ struct protid *pi = ports_lookup_port (netfs_port_bucket, dead_name,
+ netfs_protid_class);
+ struct node *np;
+
+ if (pi)
+ {
+ np = pi->po->np;
+ pthread_mutex_lock (&np->lock);
+ if (dead_name == np->sockaddr)
+ {
+ mach_port_deallocate (mach_task_self (), np->sockaddr);
+ np->sockaddr = MACH_PORT_NULL;
+ netfs_nput (np);
+ }
+ else
+ pthread_mutex_unlock (&np->lock);
+ }
+
+ fshelp_remove_active_translator (dead_name);
+
+ ports_interrupt_notified_rpcs (notify, dead_name, MACH_NOTIFY_DEAD_NAME);
+}
diff --git a/libnetfs/demuxer.c b/libnetfs/demuxer.c
new file mode 100644
index 00000000..4c20ab62
--- /dev/null
+++ b/libnetfs/demuxer.c
@@ -0,0 +1,47 @@
+/*
+ Copyright (C) 1996, 2013 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+
+#include "io_S.h"
+#include "fs_S.h"
+#include "../libports/notify_S.h"
+#include "fsys_S.h"
+#include "../libports/interrupt_S.h"
+#include "ifsock_S.h"
+
+int
+netfs_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ mig_routine_t routine;
+ if ((routine = netfs_io_server_routine (inp)) ||
+ (routine = netfs_fs_server_routine (inp)) ||
+ (routine = ports_notify_server_routine (inp)) ||
+ (routine = netfs_fsys_server_routine (inp)) ||
+ (routine = ports_interrupt_server_routine (inp)) ||
+ (routine = netfs_ifsock_server_routine (inp)))
+ {
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
diff --git a/libnetfs/dir-link.c b/libnetfs/dir-link.c
new file mode 100644
index 00000000..637f4a3a
--- /dev/null
+++ b/libnetfs/dir-link.c
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_dir_link (struct protid *diruser, struct protid *fileuser, char *name,
+ int excl)
+{
+ error_t err;
+
+ if (!diruser)
+ return EOPNOTSUPP;
+
+ if (!fileuser)
+ return EXDEV;
+
+ /* Note that nothing is locked here */
+ err = netfs_attempt_link (diruser->user, diruser->po->np,
+ fileuser->po->np, name, excl);
+ if (!err)
+ mach_port_deallocate (mach_task_self (), fileuser->pi.port_right);
+ return err;
+}
diff --git a/libnetfs/dir-lookup.c b/libnetfs/dir-lookup.c
new file mode 100644
index 00000000..99a8746e
--- /dev/null
+++ b/libnetfs/dir-lookup.c
@@ -0,0 +1,463 @@
+/*
+ Copyright (C) 1995,96,97,98,99,2000,01,02,13,14
+ Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <fcntl.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <hurd/paths.h>
+#include "netfs.h"
+#include "fs_S.h"
+#include "callbacks.h"
+#include "misc.h"
+
+error_t
+netfs_S_dir_lookup (struct protid *diruser,
+ char *filename,
+ int flags,
+ mode_t mode,
+ retry_type *do_retry,
+ char *retry_name,
+ mach_port_t *retry_port,
+ mach_msg_type_name_t *retry_port_type)
+{
+ int create; /* true if O_CREAT flag set */
+ int excl; /* true if O_EXCL flag set */
+ int mustbedir = 0; /* true if the result must be S_IFDIR */
+ int lastcomp = 0; /* true if we are at the last component */
+ int newnode = 0; /* true if this node is newly created */
+ int nsymlinks = 0;
+ struct node *dnp, *np;
+ char *nextname;
+ char *relpath;
+ error_t error;
+ struct protid *newpi;
+ struct iouser *user;
+
+ if (!diruser)
+ return EOPNOTSUPP;
+
+ create = (flags & O_CREAT);
+ excl = (flags & O_EXCL);
+
+ /* Skip leading slashes */
+ while (*filename == '/')
+ filename++;
+
+ /* Preserve the path relative to diruser->po->path. */
+ relpath = strdup (filename);
+ if (! relpath)
+ return ENOMEM;
+
+ /* Keep a pointer to the start of the filename for length
+ calculations. */
+ char *filename_start = filename;
+
+ *retry_port_type = MACH_MSG_TYPE_MAKE_SEND;
+ *do_retry = FS_RETRY_NORMAL;
+ *retry_name = '\0';
+
+ if (*filename == '\0')
+ {
+ /* Set things up in the state expected by the code from gotit: on. */
+ dnp = 0;
+ np = diruser->po->np;
+ pthread_mutex_lock (&np->lock);
+ netfs_nref (np);
+ goto gotit;
+ }
+
+ dnp = diruser->po->np;
+ pthread_mutex_lock (&dnp->lock);
+
+ netfs_nref (dnp); /* acquire a reference for later netfs_nput */
+
+ do
+ {
+ assert (!lastcomp);
+
+ /* Find the name of the next pathname component */
+ nextname = index (filename, '/');
+
+ if (nextname)
+ {
+ *nextname++ = '\0';
+ while (*nextname == '/')
+ nextname++;
+ if (*nextname == '\0')
+ {
+ /* These are the rules for filenames ending in /. */
+ nextname = 0;
+ lastcomp = 1;
+ mustbedir = 1;
+ create = 0;
+ }
+ else
+ lastcomp = 0;
+ }
+ else
+ lastcomp = 1;
+
+ np = 0;
+
+ retry_lookup:
+
+ if ((dnp == netfs_root_node || dnp == diruser->po->shadow_root)
+ && filename[0] == '.' && filename[1] == '.' && filename[2] == '\0')
+ if (dnp == diruser->po->shadow_root)
+ /* We're at the root of a shadow tree. */
+ {
+ *do_retry = FS_RETRY_REAUTH;
+ *retry_port = diruser->po->shadow_root_parent;
+ *retry_port_type = MACH_MSG_TYPE_COPY_SEND;
+ if (! lastcomp)
+ strcpy (retry_name, nextname);
+ error = 0;
+ pthread_mutex_unlock (&dnp->lock);
+ goto out;
+ }
+ else if (diruser->po->root_parent != MACH_PORT_NULL)
+ /* We're at a real translator root; even if DIRUSER->po has a
+ shadow root, we can get here if its in a directory that was
+ renamed out from under it... */
+ {
+ *do_retry = FS_RETRY_REAUTH;
+ *retry_port = diruser->po->root_parent;
+ *retry_port_type = MACH_MSG_TYPE_COPY_SEND;
+ if (!lastcomp)
+ strcpy (retry_name, nextname);
+ error = 0;
+ pthread_mutex_unlock (&dnp->lock);
+ goto out;
+ }
+ else
+ /* We are global root */
+ {
+ error = 0;
+ np = dnp;
+ netfs_nref (np);
+ }
+ else
+ /* Attempt a lookup on the next pathname component. */
+ error = netfs_attempt_lookup (diruser->user, dnp, filename, &np);
+
+ /* At this point, DNP is unlocked */
+
+ /* Implement O_EXCL flag here */
+ if (lastcomp && create && excl && !error)
+ error = EEXIST;
+
+ /* Create the new node if necessary */
+ if (lastcomp && create && error == ENOENT)
+ {
+ mode &= ~(S_IFMT | S_ISPARE | S_ISVTX);
+ mode |= S_IFREG;
+ pthread_mutex_lock (&dnp->lock);
+ error = netfs_attempt_create_file (diruser->user, dnp,
+ filename, mode, &np);
+
+ /* If someone has already created the file (between our lookup
+ and this create) then we just got EEXIST. If we are
+ EXCL, that's fine; otherwise, we have to retry the lookup. */
+ if (error == EEXIST && !excl)
+ {
+ pthread_mutex_lock (&dnp->lock);
+ goto retry_lookup;
+ }
+
+ newnode = 1;
+ }
+
+ /* All remaining errors get returned to the user */
+ if (error)
+ goto out;
+
+ error = netfs_validate_stat (np, diruser->user);
+ if (error)
+ goto out;
+
+ if ((((flags & O_NOTRANS) == 0) || !lastcomp)
+ && ((np->nn_translated & S_IPTRANS)
+ || S_ISFIFO (np->nn_translated)
+ || S_ISCHR (np->nn_translated)
+ || S_ISBLK (np->nn_translated)
+ || fshelp_translated (&np->transbox)))
+ {
+ mach_port_t dirport;
+
+ /* A callback function for short-circuited translators.
+ S_ISLNK and S_IFSOCK are handled elsewhere. */
+ error_t short_circuited_callback1 (void *cookie1, void *cookie2,
+ uid_t *uid, gid_t *gid,
+ char **argz, size_t *argz_len)
+ {
+ struct node *np = cookie1;
+ error_t err;
+
+ err = netfs_validate_stat (np, diruser->user);
+ if (err)
+ return err;
+
+ switch (np->nn_translated & S_IFMT)
+ {
+ case S_IFCHR:
+ case S_IFBLK:
+ if (asprintf (argz, "%s%c%d%c%d",
+ (S_ISCHR (np->nn_translated)
+ ? _HURD_CHRDEV : _HURD_BLKDEV),
+ 0, major (np->nn_stat.st_rdev),
+ 0, minor (np->nn_stat.st_rdev)) < 0)
+ return ENOMEM;
+ *argz_len = strlen (*argz) + 1;
+ *argz_len += strlen (*argz + *argz_len) + 1;
+ *argz_len += strlen (*argz + *argz_len) + 1;
+ break;
+ case S_IFIFO:
+ if (asprintf (argz, "%s", _HURD_FIFO) < 0)
+ return ENOMEM;
+ *argz_len = strlen (*argz) + 1;
+ break;
+ default:
+ return ENOENT;
+ }
+
+ *uid = np->nn_stat.st_uid;
+ *gid = np->nn_stat.st_gid;
+
+ return 0;
+ }
+
+ /* Create an unauthenticated port for DNP, and then
+ unlock it. */
+ error = iohelp_create_empty_iouser (&user);
+ if (! error)
+ {
+ newpi = netfs_make_protid (netfs_make_peropen (dnp, 0,
+ diruser->po),
+ user);
+ if (! newpi)
+ {
+ error = errno;
+ iohelp_free_iouser (user);
+ }
+ }
+
+ boolean_t register_translator;
+ if (! error)
+ {
+ dirport = ports_get_send_right (newpi);
+
+ /* Check if an active translator is currently running. If
+ not, fshelp_fetch_root will start one. In that case, we
+ need to register it in the list of active
+ translators. */
+ register_translator = np->transbox.active == MACH_PORT_NULL;
+
+ error = fshelp_fetch_root (&np->transbox, diruser->po,
+ dirport,
+ diruser->user,
+ lastcomp ? flags : 0,
+ ((np->nn_translated & S_IPTRANS)
+ ? _netfs_translator_callback1
+ : short_circuited_callback1),
+ _netfs_translator_callback2,
+ do_retry, retry_name, retry_port);
+ /* fetch_root copies DIRPORT for success, so we always should
+ deallocate our send right. */
+ mach_port_deallocate (mach_task_self (), dirport);
+ }
+
+ if (error != ENOENT)
+ {
+ *retry_port_type = MACH_MSG_TYPE_MOVE_SEND;
+ if (!lastcomp && !error)
+ {
+ strcat (retry_name, "/");
+ strcat (retry_name, nextname);
+ }
+
+ if (register_translator)
+ {
+ char *translator_path = strdupa (relpath);
+ if (nextname != NULL)
+ {
+ /* This was not the last path component.
+ NEXTNAME points to the next component, locate
+ the end of the current component and use it
+ to trim TRANSLATOR_PATH. */
+ char *end = nextname;
+ while (*end != 0)
+ end--;
+ translator_path[end - filename_start] = '\0';
+ }
+
+ error = fshelp_set_active_translator (&newpi->pi,
+ translator_path,
+ np->transbox.active);
+ if (error)
+ {
+ ports_port_deref (newpi);
+ goto out;
+ }
+ }
+
+ ports_port_deref (newpi);
+ goto out;
+ }
+
+ ports_port_deref (newpi);
+
+ /* ENOENT means there was a hiccup, and the translator vanished
+ while NP was unlocked inside fshelp_fetch_root; continue as normal. */
+ error = 0;
+ }
+
+ if (S_ISLNK (np->nn_translated)
+ && (!lastcomp
+ || mustbedir /* "foo/" must see that foo points to a dir */
+ || !(flags & (O_NOLINK|O_NOTRANS))))
+ {
+ size_t nextnamelen, newnamelen, linklen;
+ char *linkbuf;
+
+ /* Handle symlink interpretation */
+ if (nsymlinks++ > netfs_maxsymlinks)
+ {
+ error = ELOOP;
+ goto out;
+ }
+
+ linklen = np->nn_stat.st_size;
+
+ nextnamelen = nextname ? strlen (nextname) + 1 : 0;
+ newnamelen = nextnamelen + linklen + 1;
+ linkbuf = alloca (newnamelen);
+
+ error = netfs_attempt_readlink (diruser->user, np, linkbuf);
+ if (error)
+ goto out;
+
+ if (nextname)
+ {
+ linkbuf[linklen] = '/';
+ memcpy (linkbuf + linklen + 1, nextname,
+ nextnamelen - 1);
+ }
+ linkbuf[nextnamelen + linklen] = '\0';
+
+ if (linkbuf[0] == '/')
+ {
+ /* Punt to the caller */
+ *do_retry = FS_RETRY_MAGICAL;
+ *retry_port = MACH_PORT_NULL;
+ strcpy (retry_name, linkbuf);
+ goto out;
+ }
+
+ filename = linkbuf;
+ if (lastcomp)
+ {
+ lastcomp = 0;
+
+ /* Symlinks to nonexistent files aren't allowed to cause
+ creation, so clear the flag here. */
+ create = 0;
+ }
+ netfs_nput (np);
+ pthread_mutex_lock (&dnp->lock);
+ np = 0;
+ }
+ else
+ {
+ /* Normal nodes here for next filename component */
+ filename = nextname;
+ netfs_nrele (dnp);
+
+ if (lastcomp)
+ dnp = 0;
+ else
+ {
+ dnp = np;
+ np = 0;
+ }
+ }
+ }
+ while (filename && *filename);
+
+ /* At this point, NP is the node to return. */
+ gotit:
+
+ if (mustbedir)
+ {
+ netfs_validate_stat (np, diruser->user);
+ if (!S_ISDIR (np->nn_stat.st_mode))
+ {
+ error = ENOTDIR;
+ goto out;
+ }
+ }
+ error = netfs_check_open_permissions (diruser->user, np,
+ flags, newnode);
+ if (error)
+ goto out;
+
+ flags &= ~OPENONLY_STATE_MODES;
+
+ error = iohelp_dup_iouser (&user, diruser->user);
+ if (error)
+ goto out;
+
+ newpi = netfs_make_protid (netfs_make_peropen (np, flags, diruser->po),
+ user);
+ if (! newpi)
+ {
+ iohelp_free_iouser (user);
+ error = errno;
+ goto out;
+ }
+
+ free (newpi->po->path);
+ if (diruser->po->path == NULL)
+ {
+ /* diruser is the root directory. */
+ newpi->po->path = relpath;
+ relpath = NULL; /* Do not free relpath. */
+ }
+ else
+ {
+ newpi->po->path = NULL;
+ asprintf (&newpi->po->path, "%s/%s", diruser->po->path, relpath);
+ }
+
+ if (! newpi->po->path)
+ error = errno;
+
+ *retry_port = ports_get_right (newpi);
+ ports_port_deref (newpi);
+
+ out:
+ if (np)
+ netfs_nput (np);
+ if (dnp)
+ netfs_nrele (dnp);
+ free (relpath);
+ return error;
+}
diff --git a/libnetfs/dir-mkdir.c b/libnetfs/dir-mkdir.c
new file mode 100644
index 00000000..c8bebac8
--- /dev/null
+++ b/libnetfs/dir-mkdir.c
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_dir_mkdir (struct protid *user, char *name, mode_t mode)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ mode &= ~(S_IFMT|S_ISPARE|S_ISVTX);
+ mode |= S_IFDIR;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = netfs_attempt_mkdir (user->user, user->po->np, name, mode);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
diff --git a/libnetfs/dir-mkfile.c b/libnetfs/dir-mkfile.c
new file mode 100644
index 00000000..fcbc9da8
--- /dev/null
+++ b/libnetfs/dir-mkfile.c
@@ -0,0 +1,63 @@
+/*
+ Copyright (C) 1995,96,97,2001 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "misc.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_dir_mkfile (struct protid *diruser, int flags, mode_t mode,
+ mach_port_t *newfile, mach_msg_type_name_t *newfiletype)
+{
+ error_t err;
+ struct node *np;
+ struct iouser *user;
+ struct protid *newpi;
+
+ pthread_mutex_lock (&diruser->po->np->lock);
+ err = netfs_attempt_mkfile (diruser->user, diruser->po->np, mode, &np);
+
+ if (!err)
+ {
+ /* the dir is now unlocked and NP is locked */
+ flags &= OPENONLY_STATE_MODES;
+ err = iohelp_dup_iouser (&user, diruser->user);
+ if (! err)
+ {
+ newpi = netfs_make_protid (netfs_make_peropen (np, flags,
+ diruser->po),
+ user);
+ if (newpi)
+ {
+ *newfile = ports_get_right (newpi);
+ *newfiletype = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newpi);
+ }
+ else
+ {
+ err = errno;
+ iohelp_free_iouser (user);
+ }
+ }
+ netfs_nput (np);
+ }
+
+ return err;
+}
diff --git a/libnetfs/dir-notice-changes.c b/libnetfs/dir-notice-changes.c
new file mode 100644
index 00000000..e124e0bd
--- /dev/null
+++ b/libnetfs/dir-notice-changes.c
@@ -0,0 +1,30 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_dir_notice_changes (struct protid *dir,
+ mach_port_t port)
+{
+ /* Don't even try */
+ return EOPNOTSUPP;
+}
diff --git a/libnetfs/dir-readdir.c b/libnetfs/dir-readdir.c
new file mode 100644
index 00000000..4ab03d8a
--- /dev/null
+++ b/libnetfs/dir-readdir.c
@@ -0,0 +1,58 @@
+/*
+ Copyright (C) 1996,99,2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <fcntl.h>
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_dir_readdir (struct protid *user,
+ char **data,
+ mach_msg_type_number_t *datacnt,
+ boolean_t *data_dealloc,
+ int entry,
+ int nentries,
+ vm_size_t bufsiz,
+ int *amt)
+{
+ error_t err;
+ struct node *np;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ np = user->po->np;
+ pthread_mutex_lock (&np->lock);
+
+ err = 0;
+ if ((user->po->openstat & O_READ) == 0)
+ err = EBADF;
+ if (!err)
+ err = netfs_validate_stat (np, user->user);
+ if (!err && (np->nn_stat.st_mode & S_IFMT) != S_IFDIR)
+ err = ENOTDIR;
+ if (!err)
+ err = netfs_get_dirents (user->user, np, entry, nentries, data,
+ datacnt, bufsiz, amt);
+ *data_dealloc = 1; /* XXX */
+ pthread_mutex_unlock (&np->lock);
+ return err;
+}
diff --git a/libnetfs/dir-rename.c b/libnetfs/dir-rename.c
new file mode 100644
index 00000000..21362194
--- /dev/null
+++ b/libnetfs/dir-rename.c
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_dir_rename (struct protid *fromdiruser, char *fromname,
+ struct protid *todiruser, char *toname, int excl)
+{
+ error_t err;
+
+ if (!fromdiruser)
+ return EOPNOTSUPP;
+
+ if (!todiruser)
+ return EXDEV;
+
+ /* Note that nothing is locked here */
+ err = netfs_attempt_rename (fromdiruser->user, fromdiruser->po->np,
+ fromname, todiruser->po->np, toname, excl);
+ if (!err)
+ mach_port_deallocate (mach_task_self (), todiruser->pi.port_right);
+ return err;
+}
diff --git a/libnetfs/dir-rmdir.c b/libnetfs/dir-rmdir.c
new file mode 100644
index 00000000..96196fd4
--- /dev/null
+++ b/libnetfs/dir-rmdir.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_dir_rmdir (struct protid *diruser, char *name)
+{
+ error_t err;
+
+ if (!diruser)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&diruser->po->np->lock);
+ err = netfs_attempt_rmdir (diruser->user, diruser->po->np, name);
+ pthread_mutex_unlock (&diruser->po->np->lock);
+ return err;
+}
diff --git a/libnetfs/dir-unlink.c b/libnetfs/dir-unlink.c
new file mode 100644
index 00000000..6f1eaca6
--- /dev/null
+++ b/libnetfs/dir-unlink.c
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_dir_unlink (struct protid *user, char *name)
+{
+ error_t err;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = netfs_attempt_unlink (user->user, user->po->np, name);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
diff --git a/libnetfs/drop-node.c b/libnetfs/drop-node.c
new file mode 100644
index 00000000..2fe5ce9f
--- /dev/null
+++ b/libnetfs/drop-node.c
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+
+void
+netfs_drop_node (struct node *np)
+{
+ fshelp_drop_transbox (&np->transbox);
+ netfs_node_norefs (np);
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+}
diff --git a/libnetfs/execserver.h b/libnetfs/execserver.h
new file mode 100644
index 00000000..6c432386
--- /dev/null
+++ b/libnetfs/execserver.h
@@ -0,0 +1,23 @@
+/*
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+/* To contact the execserver */
+file_t _netfs_exec;
diff --git a/libnetfs/file-chauthor.c b/libnetfs/file-chauthor.c
new file mode 100644
index 00000000..7041b2b8
--- /dev/null
+++ b/libnetfs/file-chauthor.c
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_file_chauthor (struct protid *user,
+ uid_t author)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = netfs_attempt_chauthor (user->user, user->po->np, author);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
diff --git a/libnetfs/file-check-access.c b/libnetfs/file-check-access.c
new file mode 100644
index 00000000..d8773ff2
--- /dev/null
+++ b/libnetfs/file-check-access.c
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_file_check_access (struct protid *user,
+ int *types)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = netfs_report_access (user->user, user->po->np, types);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
diff --git a/libnetfs/file-chflags.c b/libnetfs/file-chflags.c
new file mode 100644
index 00000000..a5c132d4
--- /dev/null
+++ b/libnetfs/file-chflags.c
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_file_chflags (struct protid *user,
+ int flags)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = netfs_attempt_chflags (user->user, user->po->np, flags);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
diff --git a/libnetfs/file-chmod.c b/libnetfs/file-chmod.c
new file mode 100644
index 00000000..413261ca
--- /dev/null
+++ b/libnetfs/file-chmod.c
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_file_chmod (struct protid *user,
+ mode_t mode)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ mode &= ~(S_IFMT | S_ISPARE | S_ITRANS);
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = netfs_attempt_chmod (user->user, user->po->np, mode);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
diff --git a/libnetfs/file-chown.c b/libnetfs/file-chown.c
new file mode 100644
index 00000000..d02876c7
--- /dev/null
+++ b/libnetfs/file-chown.c
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_file_chown (struct protid *user,
+ uid_t owner,
+ uid_t group)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = netfs_attempt_chown (user->user, user->po->np,
+ owner, group);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
diff --git a/libnetfs/file-exec.c b/libnetfs/file-exec.c
new file mode 100644
index 00000000..638f0ae8
--- /dev/null
+++ b/libnetfs/file-exec.c
@@ -0,0 +1,167 @@
+/*
+ Copyright (C) 1996,97,2000,01,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* Written by Michael I. Bushnell, p/BSG. */
+
+#include "netfs.h"
+#include "execserver.h"
+#include "fs_S.h"
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <hurd/exec.h>
+#include <hurd/paths.h>
+#include <string.h>
+#include <idvec.h>
+
+kern_return_t
+netfs_S_file_exec (struct protid *cred,
+ task_t task,
+ int flags,
+ char *argv,
+ size_t argvlen,
+ char *envp,
+ size_t envplen,
+ mach_port_t *fds,
+ size_t fdslen,
+ mach_port_t *portarray,
+ size_t portarraylen,
+ int *intarray,
+ size_t intarraylen,
+ mach_port_t *deallocnames,
+ size_t deallocnameslen,
+ mach_port_t *destroynames,
+ size_t destroynameslen)
+{
+ struct node *np;
+ error_t err;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ int suid, sgid;
+ mach_port_t right;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (_netfs_exec == MACH_PORT_NULL)
+ _netfs_exec = file_name_lookup (_SERVERS_EXEC, 0, 0);
+ if (_netfs_exec == MACH_PORT_NULL)
+ return EOPNOTSUPP;
+
+ if ((cred->po->openstat & O_EXEC) == 0)
+ return EBADF;
+
+ np = cred->po->np;
+
+ pthread_mutex_lock (&np->lock);
+ mode = np->nn_stat.st_mode;
+ uid = np->nn_stat.st_uid;
+ gid = np->nn_stat.st_gid;
+ err = netfs_validate_stat (np, cred->user);
+ pthread_mutex_unlock (&np->lock);
+
+ if (err)
+ return err;
+
+ if (!((mode & (S_IXUSR|S_IXGRP|S_IXOTH))
+ || ((mode & S_IUSEUNK) && (mode & (S_IEXEC << S_IUNKSHIFT)))))
+ return EACCES;
+
+ if ((mode & S_IFMT) == S_IFDIR)
+ return EACCES;
+
+ suid = mode & S_ISUID;
+ sgid = mode & S_ISGID;
+ if (suid || sgid)
+ {
+ int secure = 0;
+ error_t get_file_ids (struct idvec *uidsvec, struct idvec *gidsvec)
+ {
+ error_t err;
+
+ err = idvec_merge (uidsvec, cred->user->uids);
+ if (! err)
+ err = idvec_merge (gidsvec, cred->user->gids);
+
+ return err;
+ }
+ err =
+ fshelp_exec_reauth (suid, uid, sgid, gid,
+ netfs_auth_server_port, get_file_ids,
+ portarray, portarraylen, fds, fdslen, &secure);
+ if (secure)
+ flags |= EXEC_SECURE | EXEC_NEWTASK;
+ }
+
+ /* If the user can't read the file, then we should use a new task,
+ which would be inaccessible to the user. Actually, this doesn't
+ work, because the proc server will still give out the task port
+ to the user. Too many things depend on that that it can't be
+ changed. So this vague attempt isn't even worth trying. */
+#if 0
+ if (diskfs_access (np, S_IREAD, cred))
+ flags |= EXEC_NEWTASK;
+#endif
+
+ if (! err)
+ {
+ struct iouser *user;
+ struct protid *newpi;
+
+ err = iohelp_dup_iouser (&user, cred->user);
+ if (! err)
+ {
+ newpi = netfs_make_protid (netfs_make_peropen (np, O_READ, cred->po),
+ user);
+ if (newpi)
+ {
+ right = ports_get_send_right (newpi);
+ err = exec_exec (_netfs_exec,
+ right, MACH_MSG_TYPE_COPY_SEND,
+ task, flags, argv, argvlen, envp, envplen,
+ fds, MACH_MSG_TYPE_COPY_SEND, fdslen,
+ portarray, MACH_MSG_TYPE_COPY_SEND, portarraylen,
+ intarray, intarraylen,
+ deallocnames, deallocnameslen,
+ destroynames, destroynameslen);
+ mach_port_deallocate (mach_task_self (), right);
+ ports_port_deref (newpi);
+ }
+ else
+ {
+ err = errno;
+ iohelp_free_iouser (user);
+ }
+ }
+ }
+
+ if (! err)
+ {
+ unsigned int i;
+
+ mach_port_deallocate (mach_task_self (), task);
+ for (i = 0; i < fdslen; i++)
+ mach_port_deallocate (mach_task_self (), fds[i]);
+ for (i = 0; i < portarraylen; i++)
+ mach_port_deallocate (mach_task_self (), portarray[i]);
+ }
+
+ return err;
+}
diff --git a/libnetfs/file-get-children.c b/libnetfs/file-get-children.c
new file mode 100644
index 00000000..bd7e8fcf
--- /dev/null
+++ b/libnetfs/file-get-children.c
@@ -0,0 +1,109 @@
+/* file_get_children
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+#include <argz.h>
+
+/* Return any active translators bound to nodes of the receiving
+ filesystem. CHILDREN is an argz vector containing file names
+ relative to the root of the receiving translator. */
+error_t
+netfs_S_file_get_children (struct protid *cred,
+ char **children,
+ mach_msg_type_number_t *children_len)
+{
+ error_t err;
+ if (! cred
+ || cred->pi.bucket != netfs_port_bucket
+ || cred->pi.class != netfs_protid_class)
+ return EOPNOTSUPP;
+
+ /* check_access performs the same permission check as is normally
+ done, i.e. it checks that all but the last path components are
+ executable by the requesting user and that the last component is
+ readable. */
+ error_t check_access (const char *path)
+ {
+ error_t err;
+ char *elements = NULL;
+ size_t elements_len = 0;
+
+ err = argz_create_sep (path, '/', &elements, &elements_len);
+ if (err)
+ return err;
+
+ struct node *dp = netfs_root_node;
+
+ /* Lock the root node. netfs_attempt_lookup expects the directory to
+ be locked. */
+ pthread_mutex_lock (&dp->lock);
+
+ /* Increase the reference count, it will be decremented in the loop
+ ahead. */
+ netfs_nref (dp);
+
+ for (char *entry = elements;
+ entry;
+ entry = argz_next (elements, elements_len, entry))
+ {
+ struct node *next;
+ err = netfs_attempt_lookup (cred->user, dp, entry, &next);
+ /* netfs_attempt_lookup has unlocked dp and returned next
+ locked, so there is no locking to do here. */
+
+ /* Decrease reference count. */
+ netfs_nrele (dp);
+
+ if (err)
+ goto errout;
+
+ dp = next;
+ }
+
+ err = fshelp_access (&dp->nn_stat, S_IRUSR, cred->user);
+
+ errout:
+ /* Unlock and unreference the last node. */
+ netfs_nput (dp);
+
+ free (elements);
+ return err;
+ }
+
+ char *c = NULL;
+ size_t c_len = 0;
+
+ err = fshelp_get_active_translators (&c, &c_len, check_access);
+ if (err)
+ goto errout;
+
+ err = iohelp_return_malloced_buffer (c, c_len, children, children_len);
+ if (err)
+ goto errout;
+
+ c = NULL; /* c was freed by iohelp_return_malloced_buffer. */
+
+ errout:
+ free (c);
+ return err;
+}
diff --git a/libnetfs/file-get-fs-options.c b/libnetfs/file-get-fs-options.c
new file mode 100644
index 00000000..a8029916
--- /dev/null
+++ b/libnetfs/file-get-fs-options.c
@@ -0,0 +1,62 @@
+/* Unparse run-time options
+
+ Copyright (C) 1995, 1996, 1998 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <argz.h>
+#include <string.h>
+
+#include "netfs.h"
+#include "fs_S.h"
+
+/* This code is originally from libdiskfs; things surrounded by `#if NOT_YET'
+ are pending libnetfs being fleshed out some more. */
+
+/* Implement file_get_fs_options as described in <hurd/fs.defs>. */
+error_t
+netfs_S_file_get_fs_options (struct protid *user,
+ char **data, mach_msg_type_number_t *data_len)
+{
+ error_t err;
+ char *argz = 0;
+ size_t argz_len = 0;
+
+ if (! user)
+ return EOPNOTSUPP;
+
+ err = argz_add (&argz, &argz_len, program_invocation_name);
+ if (! err)
+ {
+#if NOT_YET
+ pthread_rwlock_rdlock (&netfs_fsys_lock);
+#endif
+ err = netfs_append_args (&argz, &argz_len);
+#if NOT_YET
+ pthread_rwlock_unlock (&netfs_fsys_lock);
+#endif
+ }
+
+ if (! err)
+ /* Move ARGZ from a malloced buffer into a vm_alloced one. */
+ err = iohelp_return_malloced_buffer (argz, argz_len, data, data_len);
+ else
+ free (argz);
+
+ return err;
+}
diff --git a/libnetfs/file-get-source.c b/libnetfs/file-get-source.c
new file mode 100644
index 00000000..8b73d5a1
--- /dev/null
+++ b/libnetfs/file-get-source.c
@@ -0,0 +1,37 @@
+/* file_get_source
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+/* Return information about the source of the receiving
+ filesystem. */
+error_t
+netfs_S_file_get_source (struct protid *cred,
+ char *source)
+{
+ if (! cred
+ || cred->pi.bucket != netfs_port_bucket
+ || cred->pi.class != netfs_protid_class)
+ return EOPNOTSUPP;
+
+ return netfs_get_source (cred, source, 1024 /* XXX */);
+}
diff --git a/libnetfs/file-get-storage-info-default.c b/libnetfs/file-get-storage-info-default.c
new file mode 100644
index 00000000..0ca68c96
--- /dev/null
+++ b/libnetfs/file-get-storage-info-default.c
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+#include <sys/mman.h>
+
+
+error_t
+netfs_file_get_storage_info (struct iouser *cred,
+ struct node *np,
+ mach_port_t **ports,
+ mach_msg_type_name_t *ports_type,
+ mach_msg_type_number_t *num_ports,
+ int **ints,
+ mach_msg_type_number_t *num_ints,
+ off_t **offsets,
+ mach_msg_type_number_t *num_offsets,
+ char **data,
+ mach_msg_type_number_t *data_len)
+{
+ *data_len = 0;
+ *num_offsets = 0;
+ *num_ports = 0;
+
+ if (*num_ints == 0)
+ /* Argh */
+ {
+ *ints = mmap (0, sizeof (int), PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*ints == (int *) -1)
+ return errno;
+ }
+
+ *num_ints = 1;
+ (*ints)[0] = STORAGE_NETWORK;
+
+ return 0;
+}
diff --git a/libnetfs/file-get-storage-info.c b/libnetfs/file-get-storage-info.c
new file mode 100644
index 00000000..d2c9d8fc
--- /dev/null
+++ b/libnetfs/file-get-storage-info.c
@@ -0,0 +1,47 @@
+/*
+ Copyright (C) 2001 Free Software Foundation, Inc.
+ Written by Neal H Walfield <neal@cs.uml.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_file_get_storage_info (struct protid *user,
+ mach_port_t **ports,
+ mach_msg_type_name_t *ports_type,
+ mach_msg_type_number_t *num_ports,
+ int **ints, mach_msg_type_number_t *num_ints,
+ off_t **offsets,
+ mach_msg_type_number_t *num_offsets,
+ char **data, mach_msg_type_number_t *data_len)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = netfs_file_get_storage_info (user->user, user->po->np, ports,
+ ports_type, num_ports, ints,
+ num_ints, offsets, num_offsets,
+ data, data_len);
+ pthread_mutex_unlock (&user->po->np->lock);
+
+ return err;
+}
diff --git a/libnetfs/file-get-transcntl.c b/libnetfs/file-get-transcntl.c
new file mode 100644
index 00000000..04596d72
--- /dev/null
+++ b/libnetfs/file-get-transcntl.c
@@ -0,0 +1,52 @@
+/* libnetfs implementation of fs.defs: file_get_translator_cntl
+
+ Copyright (C) 1992, 1993, 1994, 1995, 1996 Free Software Foundation
+
+ This is a trivially adapted version of
+ libdiskfs/file-get-transcntl.c.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "fs_S.h"
+
+/* Implement file_get_translator_cntl as described in <hurd/fs.defs>. */
+error_t
+netfs_S_file_get_translator_cntl (struct protid *cred,
+ mach_port_t *ctl,
+ mach_msg_type_name_t *ctltype)
+{
+ struct node *np;
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+
+ pthread_mutex_lock (&np->lock);
+
+ err = fshelp_isowner (&np->nn_stat, cred->user);
+ if (!err)
+ err = fshelp_fetch_control (&np->transbox, ctl);
+ if (!err && *ctl == MACH_PORT_NULL)
+ err = ENXIO;
+ if (!err)
+ *ctltype = MACH_MSG_TYPE_MOVE_SEND;
+
+ pthread_mutex_unlock (&np->lock);
+
+ return err;
+}
diff --git a/libnetfs/file-get-translator.c b/libnetfs/file-get-translator.c
new file mode 100644
index 00000000..3a54ff10
--- /dev/null
+++ b/libnetfs/file-get-translator.c
@@ -0,0 +1,132 @@
+/*
+ Copyright (C) 1996, 1999, 2000, 2008 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+#include <stdio.h>
+#include <hurd/paths.h>
+#include "netfs.h"
+#include "fs_S.h"
+#include <sys/mman.h>
+
+error_t
+netfs_S_file_get_translator (struct protid *user,
+ char **trans,
+ mach_msg_type_number_t *translen)
+{
+ struct node *np;
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ np = user->po->np;
+ pthread_mutex_lock (&np->lock);
+ err = netfs_validate_stat (np, user->user);
+
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return err;
+ }
+
+ if (S_ISLNK (np->nn_stat.st_mode))
+ {
+ unsigned int len = sizeof _HURD_SYMLINK + np->nn_stat.st_size + 1;
+
+ if (len > *translen)
+ *trans = mmap (0, len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ memcpy (*trans, _HURD_SYMLINK, sizeof _HURD_SYMLINK);
+
+ err = netfs_attempt_readlink (user->user, np,
+ *trans + sizeof _HURD_SYMLINK);
+ if (!err)
+ {
+ (*trans)[sizeof _HURD_SYMLINK + np->nn_stat.st_size] = '\0';
+ *translen = len;
+ }
+ }
+ else if (S_ISCHR (np->nn_stat.st_mode) || S_ISBLK (np->nn_stat.st_mode))
+ {
+ char *buf;
+ unsigned int buflen;
+
+ buflen = asprintf (&buf, "%s%c%d%c%d",
+ (S_ISCHR (np->nn_stat.st_mode)
+ ? _HURD_CHRDEV
+ : _HURD_BLKDEV),
+ '\0', major (np->nn_stat.st_rdev),
+ '\0', minor (np->nn_stat.st_rdev));
+ if (buflen < 0)
+ err = ENOMEM;
+ else
+ {
+ buflen++; /* terminating nul */
+
+ if (buflen > *translen)
+ *trans = mmap (0, buflen, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ memcpy (*trans, buf, buflen);
+ free (buf);
+ *translen = buflen;
+ err = 0;
+ }
+ }
+ else if (S_ISFIFO (np->nn_stat.st_mode))
+ {
+ unsigned int len;
+
+ len = sizeof _HURD_FIFO;
+ if (len > *translen)
+ *trans = mmap (0, len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ memcpy (*trans, _HURD_FIFO, sizeof _HURD_FIFO);
+ *translen = len;
+ err = 0;
+ }
+ else if (S_ISSOCK (np->nn_stat.st_mode))
+ {
+ unsigned int len;
+
+ len = sizeof _HURD_IFSOCK;
+ if (len > *translen)
+ *trans = mmap (0, len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ memcpy (*trans, _HURD_IFSOCK, sizeof _HURD_IFSOCK);
+ *translen = len;
+ err = 0;
+ }
+ else if (np->nn_translated & S_IPTRANS)
+ {
+ char *string = NULL;
+ size_t len = 0;
+ err = netfs_get_translator (np, &string, &len);
+ if (!err)
+ {
+ if (len > *translen)
+ *trans = mmap (0, len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ memcpy (*trans, string, len);
+ *translen = len;
+ free (string);
+ }
+ }
+ else
+ err = EINVAL;
+
+ pthread_mutex_unlock (&np->lock);
+
+ return err;
+}
diff --git a/libnetfs/file-getcontrol.c b/libnetfs/file-getcontrol.c
new file mode 100644
index 00000000..0e29ccf9
--- /dev/null
+++ b/libnetfs/file-getcontrol.c
@@ -0,0 +1,51 @@
+/* Return the filesystem corresponding to a file
+
+ Copyright (C) 1995, 1996, 2001 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+#include <hurd/fshelp.h>
+
+error_t
+netfs_S_file_getcontrol (struct protid *user,
+ mach_port_t *control,
+ mach_msg_type_name_t *controltype)
+{
+ error_t err;
+ struct port_info *pi;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ err = fshelp_iscontroller (&netfs_root_node->nn_stat, user->user);
+ if (err)
+ return err;
+
+ /* They've have the appropriate credentials; give it to them. */
+ err = ports_create_port (netfs_control_class, netfs_port_bucket,
+ sizeof (struct port_info), &pi);
+ if (err)
+ return err;
+
+ *control = ports_get_right (pi);
+ *controltype = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (pi);
+ return 0;
+}
diff --git a/libnetfs/file-getlinknode.c b/libnetfs/file-getlinknode.c
new file mode 100644
index 00000000..a15c8d5f
--- /dev/null
+++ b/libnetfs/file-getlinknode.c
@@ -0,0 +1,38 @@
+/*
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_file_getlinknode (struct protid *user,
+ file_t *port,
+ mach_msg_type_name_t *porttype)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ if (user->po->np == netfs_root_node)
+ return EBUSY;
+
+ *port = ports_get_right (user);
+ *porttype = MACH_MSG_TYPE_MAKE_SEND;
+ return 0;
+}
diff --git a/libnetfs/file-lock-stat.c b/libnetfs/file-lock-stat.c
new file mode 100644
index 00000000..49529cf4
--- /dev/null
+++ b/libnetfs/file-lock-stat.c
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_file_lock_stat (struct protid *user,
+ int *mystatus,
+ int *otherstatus)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ *mystatus = user->po->lock_status;
+ *otherstatus = user->po->np->userlock.type;
+ pthread_mutex_unlock (&user->po->np->lock);
+ return 0;
+}
diff --git a/libnetfs/file-lock.c b/libnetfs/file-lock.c
new file mode 100644
index 00000000..0010d4be
--- /dev/null
+++ b/libnetfs/file-lock.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_file_lock (struct protid *user,
+ int flags)
+{
+ error_t err;
+ if (!user)
+ return EOPNOTSUPP;
+ pthread_mutex_lock (&user->po->np->lock);
+ err = fshelp_acquire_lock (&user->po->np->userlock, &user->po->lock_status,
+ &user->po->np->lock, flags);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
diff --git a/libnetfs/file-reparent.c b/libnetfs/file-reparent.c
new file mode 100644
index 00000000..61b7e37f
--- /dev/null
+++ b/libnetfs/file-reparent.c
@@ -0,0 +1,74 @@
+/* Reparent a file
+
+ Copyright (C) 1997, 2001 Free Software Foundation
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "fs_S.h"
+
+error_t
+netfs_S_file_reparent (struct protid *cred, mach_port_t parent,
+ mach_port_t *new_file, mach_msg_type_name_t *new_file_type)
+{
+ error_t err;
+ struct node *node;
+ struct protid *new_cred;
+ struct iouser *user;
+
+ if (! cred)
+ return EOPNOTSUPP;
+
+ err = iohelp_dup_iouser (&user, cred->user);
+ if (err)
+ return err;
+
+ node = cred->po->np;
+
+ pthread_mutex_lock (&node->lock);
+
+ new_cred =
+ netfs_make_protid (netfs_make_peropen (node, cred->po->openstat, cred->po),
+ user);
+ pthread_mutex_unlock (&node->lock);
+
+ if (new_cred)
+ {
+ /* Remove old shadow root state. */
+ if (new_cred->po->shadow_root && new_cred->po->shadow_root != node)
+ {
+ pthread_mutex_lock (&new_cred->po->shadow_root->lock);
+ netfs_nput (new_cred->po->shadow_root);
+ }
+ if (new_cred->po->shadow_root_parent)
+ mach_port_deallocate (mach_task_self (), new_cred->po->shadow_root_parent);
+
+ /* And install PARENT instead. */
+ new_cred->po->shadow_root = node;
+ new_cred->po->shadow_root_parent = parent;
+
+ *new_file = ports_get_right (new_cred);
+ *new_file_type = MACH_MSG_TYPE_MAKE_SEND;
+
+ ports_port_deref (new_cred);
+
+ return 0;
+ }
+ else
+ return errno;
+}
diff --git a/libnetfs/file-set-size.c b/libnetfs/file-set-size.c
new file mode 100644
index 00000000..1700ee18
--- /dev/null
+++ b/libnetfs/file-set-size.c
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_file_set_size (struct protid *user,
+ off_t size)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+ else if (size < 0)
+ return EINVAL;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = netfs_attempt_set_size (user->user, user->po->np, size);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
diff --git a/libnetfs/file-set-translator.c b/libnetfs/file-set-translator.c
new file mode 100644
index 00000000..02c55836
--- /dev/null
+++ b/libnetfs/file-set-translator.c
@@ -0,0 +1,186 @@
+/*
+ Copyright (C) 1996, 1997, 1999, 2001, 2013, 2014
+ Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+#include <hurd/paths.h>
+#include <hurd/fsys.h>
+
+error_t
+netfs_S_file_set_translator (struct protid *user,
+ int passive_flags, int active_flags,
+ int killtrans_flags, char *passive,
+ mach_msg_type_number_t passivelen,
+ mach_port_t active)
+{
+ struct node *np;
+ error_t err = 0;
+ mach_port_t control;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ if (!(passive_flags & FS_TRANS_SET) && !(active_flags & FS_TRANS_SET))
+ return 0;
+
+ if (passivelen && passive[passivelen - 1])
+ return EINVAL;
+
+ np = user->po->np;
+ pthread_mutex_lock (&np->lock);
+
+ if (active_flags & FS_TRANS_SET
+ && ! (active_flags & FS_TRANS_ORPHAN))
+ {
+ /* Validate--user must be owner */
+ err = netfs_validate_stat (np, user->user);
+ if (err)
+ goto out;
+
+ err = fshelp_isowner (&np->nn_stat, user->user);
+ if (err)
+ goto out;
+
+ err = fshelp_fetch_control (&np->transbox, &control);
+ if (err)
+ goto out;
+
+ if (control != MACH_PORT_NULL
+ && (active_flags & FS_TRANS_EXCL) == 0)
+ {
+ pthread_mutex_unlock (&np->lock);
+ err = fsys_goaway (control, killtrans_flags);
+ if (err && err != MIG_SERVER_DIED && err != MACH_SEND_INVALID_DEST)
+ return err;
+ err = 0;
+ pthread_mutex_lock (&np->lock);
+ }
+ }
+
+ if ((passive_flags & FS_TRANS_SET)
+ && (passive_flags & FS_TRANS_EXCL))
+ {
+ err = netfs_validate_stat (np, user->user);
+ if (!err && (np->nn_stat.st_mode & S_IPTRANS))
+ err = EBUSY;
+ if (err)
+ goto out;
+ }
+
+ if (active_flags & FS_TRANS_SET)
+ {
+ err = fshelp_set_active (&np->transbox, active,
+ active_flags & FS_TRANS_EXCL);
+ if (err)
+ goto out;
+ }
+
+ if (passive_flags & FS_TRANS_SET)
+ {
+ mode_t newmode = 0;
+ if (!(passive_flags & FS_TRANS_FORCE))
+ {
+ /* Short circuited translators */
+
+ if (!strcmp (passive, _HURD_SYMLINK))
+ newmode = S_IFLNK;
+ else if (!(strcmp (passive, _HURD_CHRDEV)))
+ newmode = S_IFCHR;
+ else if (!strcmp (passive, _HURD_BLKDEV))
+ newmode = S_IFBLK;
+ else if (!strcmp (passive, _HURD_FIFO))
+ newmode = S_IFIFO;
+ else if (!strcmp (passive, _HURD_IFSOCK))
+ newmode = S_IFSOCK;
+ }
+
+ switch (newmode)
+ {
+ int major, minor;
+ char *arg;
+
+ case S_IFBLK:
+ case S_IFCHR:
+ /* Find the device number from the arguments
+ of the translator. */
+ arg = passive + strlen (passive) + 1;
+ assert (arg <= passive + passivelen);
+ if (arg == passive + passivelen)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return EINVAL;
+ }
+ major = strtol (arg, 0, 0);
+
+ arg = arg + strlen (arg) + 1;
+ assert (arg < passive + passivelen);
+ if (arg == passive + passivelen)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return EINVAL;
+ }
+ minor = strtol (arg, 0, 0);
+
+ err = netfs_attempt_mkdev (user->user, np,
+ newmode, makedev (major, minor));
+ if (err == EOPNOTSUPP)
+ goto fallback;
+ break;
+
+ case S_IFLNK:
+ arg = passive + strlen (passive) + 1;
+ assert (arg <= passive + passivelen);
+ if (arg == passive + passivelen)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return EINVAL;
+ }
+
+ err = netfs_attempt_mksymlink (user->user, np, arg);
+ if (err == EOPNOTSUPP)
+ goto fallback;
+ break;
+
+ default:
+ err = netfs_validate_stat (np, user->user);
+ if (!err)
+ err = netfs_attempt_chmod (user->user, np,
+ ((np->nn_stat.st_mode & ~S_IFMT)
+ | newmode));
+ if (err == EOPNOTSUPP)
+ goto fallback;
+ break;
+
+ case 0:
+ fallback:
+ err = netfs_set_translator (user->user, np,
+ passive, passivelen);
+ break;
+ }
+ }
+
+ if (! err && user->po->path && active_flags & FS_TRANS_SET)
+ err = fshelp_set_active_translator (&user->pi, user->po->path, active);
+
+ out:
+ pthread_mutex_unlock (&np->lock);
+ return err;
+}
diff --git a/libnetfs/file-statfs.c b/libnetfs/file-statfs.c
new file mode 100644
index 00000000..13ae7d94
--- /dev/null
+++ b/libnetfs/file-statfs.c
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_file_statfs (struct protid *user,
+ struct statfs *st)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = netfs_attempt_statfs (user->user, user->po->np, st);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
diff --git a/libnetfs/file-sync.c b/libnetfs/file-sync.c
new file mode 100644
index 00000000..4f6b4db8
--- /dev/null
+++ b/libnetfs/file-sync.c
@@ -0,0 +1,38 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_file_sync (struct protid *user,
+ int wait,
+ int omitmeta)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = netfs_attempt_sync (user->user, user->po->np, wait);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
diff --git a/libnetfs/file-syncfs.c b/libnetfs/file-syncfs.c
new file mode 100644
index 00000000..2302e926
--- /dev/null
+++ b/libnetfs/file-syncfs.c
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_file_syncfs (struct protid *user,
+ int wait,
+ int dochildren)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ /* Translators not yet supported by netfs. XXX */
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = netfs_attempt_syncfs (user->user, wait);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
diff --git a/libnetfs/file-utimes.c b/libnetfs/file-utimes.c
new file mode 100644
index 00000000..19156094
--- /dev/null
+++ b/libnetfs/file-utimes.c
@@ -0,0 +1,53 @@
+/*
+ Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+
+error_t
+netfs_S_file_utimes (struct protid *user,
+ time_value_t atimein,
+ time_value_t mtimein)
+{
+ struct timespec atime, mtime;
+ error_t err;
+
+ if (atimein.microseconds != -1)
+ {
+ atime.tv_sec = atimein.seconds;
+ atime.tv_nsec = atimein.microseconds * 1000;
+ }
+
+ if (mtimein.microseconds != -1)
+ {
+ mtime.tv_sec = mtimein.seconds;
+ mtime.tv_nsec = mtimein.microseconds * 1000;
+ }
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = netfs_attempt_utimes (user->user, user->po->np,
+ atimein.microseconds != -1 ? &atime : 0,
+ mtimein.microseconds != -1 ? &mtime : 0);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
diff --git a/libnetfs/fsstubs.c b/libnetfs/fsstubs.c
new file mode 100644
index 00000000..75bd7903
--- /dev/null
+++ b/libnetfs/fsstubs.c
@@ -0,0 +1,45 @@
+/* Unimplemented rpcs from <hurd/fs.defs>
+
+ Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fs_S.h"
+#include "ifsock_S.h"
+
+error_t
+netfs_S_file_notice_changes (struct protid *user,
+ mach_port_t port)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_S_file_getfh (struct protid *user,
+ char **data, mach_msg_type_number_t *ndata)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_S_ifsock_getsockaddr (struct protid *user,
+ mach_port_t *address)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libnetfs/fsys-get-options.c b/libnetfs/fsys-get-options.c
new file mode 100644
index 00000000..836806b2
--- /dev/null
+++ b/libnetfs/fsys-get-options.c
@@ -0,0 +1,65 @@
+/* Unparse run-time options
+
+ Copyright (C) 1995, 1996, 1998, 2001 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <argz.h>
+#include <hurd/fsys.h>
+#include <string.h>
+
+#include "netfs.h"
+#include "fsys_S.h"
+
+/* This code is originally from libdiskfs; things surrounded by `#if NOT_YET'
+ are pending libnetfs being fleshed out some more. */
+
+/* Implement fsys_get_options as described in <hurd/fsys.defs>. */
+error_t
+netfs_S_fsys_get_options (struct netfs_control *port,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ char **data, mach_msg_type_number_t *data_len)
+{
+ error_t err;
+ char *argz = 0;
+ size_t argz_len = 0;
+
+ if (!port)
+ return EOPNOTSUPP;
+
+ err = argz_add (&argz, &argz_len, program_invocation_name);
+ if (! err)
+ {
+#if NOT_YET
+ pthread_rwlock_rdlock (&netfs_fsys_lock);
+#endif
+ err = netfs_append_args (&argz, &argz_len);
+#if NOT_YET
+ pthread_rwlock_unlock (&netfs_fsys_lock);
+#endif
+ }
+
+ if (! err)
+ /* Move ARGZ from a malloced buffer into a vm_alloced one. */
+ err = iohelp_return_malloced_buffer (argz, argz_len, data, data_len);
+ else
+ free (argz);
+
+ return err;
+}
diff --git a/libnetfs/fsys-getroot.c b/libnetfs/fsys-getroot.c
new file mode 100644
index 00000000..2d02120e
--- /dev/null
+++ b/libnetfs/fsys-getroot.c
@@ -0,0 +1,147 @@
+/*
+ Copyright (C) 1996,97,2001,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fsys_S.h"
+#include "misc.h"
+#include "callbacks.h"
+#include <fcntl.h>
+
+error_t
+netfs_S_fsys_getroot (struct netfs_control *pt,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mach_port_t dotdot,
+ uid_t *uids, mach_msg_type_number_t nuids,
+ uid_t *gids, mach_msg_type_number_t ngids,
+ int flags,
+ retry_type *do_retry,
+ char *retry_name,
+ mach_port_t *retry_port,
+ mach_msg_type_name_t *retry_port_type)
+{
+ struct iouser *cred;
+ error_t err;
+ struct protid *newpi;
+ mode_t type;
+ struct peropen peropen_context =
+ {
+ root_parent: dotdot,
+ path: NULL,
+ };
+
+ if (!pt)
+ return EOPNOTSUPP;
+
+ err = iohelp_create_complex_iouser (&cred, uids, nuids, gids, ngids);
+ if (err)
+ return err;
+
+ flags &= O_HURD;
+
+ pthread_mutex_lock (&netfs_root_node->lock);
+ err = netfs_validate_stat (netfs_root_node, cred);
+ if (err)
+ goto out;
+
+ type = netfs_root_node->nn_stat.st_mode & S_IFMT;
+
+ if (((netfs_root_node->nn_stat.st_mode & S_IPTRANS)
+ || fshelp_translated (&netfs_root_node->transbox))
+ && !(flags & O_NOTRANS))
+ {
+ err = fshelp_fetch_root (&netfs_root_node->transbox,
+ &peropen_context, dotdot, cred, flags,
+ _netfs_translator_callback1,
+ _netfs_translator_callback2,
+ do_retry, retry_name, retry_port);
+ if (err != ENOENT)
+ {
+ pthread_mutex_unlock (&netfs_root_node->lock);
+ iohelp_free_iouser (cred);
+ if (!err)
+ *retry_port_type = MACH_MSG_TYPE_MOVE_SEND;
+ return err;
+ }
+ /* ENOENT means translator has vanished inside fshelp_fetch_root. */
+ err = 0;
+ }
+
+ if (type == S_IFLNK && !(flags & (O_NOLINK | O_NOTRANS)))
+ {
+ char pathbuf[netfs_root_node->nn_stat.st_size + 1];
+
+ err = netfs_attempt_readlink (cred, netfs_root_node, pathbuf);
+
+ if (err)
+ goto out;
+
+ pthread_mutex_unlock (&netfs_root_node->lock);
+ iohelp_free_iouser (cred);
+
+ if (pathbuf[0] == '/')
+ {
+ *do_retry = FS_RETRY_MAGICAL;
+ *retry_port = MACH_PORT_NULL;
+ *retry_port_type = MACH_MSG_TYPE_COPY_SEND;
+ strcpy (retry_name, pathbuf);
+ mach_port_deallocate (mach_task_self (), dotdot);
+ return 0;
+ }
+ else
+ {
+ *do_retry = FS_RETRY_REAUTH;
+ *retry_port = dotdot;
+ *retry_port_type = MACH_MSG_TYPE_MOVE_SEND;
+ strcpy (retry_name, pathbuf);
+ return 0;
+ }
+ }
+
+ if ((type == S_IFSOCK || type == S_IFBLK || type == S_IFCHR
+ || type == S_IFIFO) && (flags & (O_READ|O_WRITE|O_EXEC)))
+ {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ err = netfs_check_open_permissions (cred, netfs_root_node, flags, 0);
+ if (err)
+ goto out;
+
+ flags &= ~OPENONLY_STATE_MODES;
+
+ newpi = netfs_make_protid (netfs_make_peropen (netfs_root_node, flags,
+ &peropen_context),
+ cred);
+ mach_port_deallocate (mach_task_self (), dotdot);
+
+ *do_retry = FS_RETRY_NORMAL;
+ *retry_port = ports_get_right (newpi);
+ *retry_port_type = MACH_MSG_TYPE_MAKE_SEND;
+ retry_name[0] = '\0';
+ ports_port_deref (newpi);
+
+ out:
+ if (err)
+ iohelp_free_iouser (cred);
+ pthread_mutex_unlock (&netfs_root_node->lock);
+ return err;
+}
diff --git a/libnetfs/fsys-goaway.c b/libnetfs/fsys-goaway.c
new file mode 100644
index 00000000..872d0730
--- /dev/null
+++ b/libnetfs/fsys-goaway.c
@@ -0,0 +1,48 @@
+/* implement the fsys_goaway RPC for libnetfs
+ Copyright (C) 2001 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fsys_S.h"
+#include "fsys_reply_U.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <hurd/ports.h>
+
+error_t
+netfs_S_fsys_goaway (struct netfs_control *pt,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int flags)
+{
+ error_t err;
+
+
+ if (!pt)
+ return EOPNOTSUPP;
+
+ err = netfs_shutdown (flags);
+ if (! err)
+ {
+ fsys_goaway_reply (reply, reply_type, 0);
+ exit (0);
+ }
+
+ return err;
+}
diff --git a/libnetfs/fsys-set-options.c b/libnetfs/fsys-set-options.c
new file mode 100644
index 00000000..fb1c87ef
--- /dev/null
+++ b/libnetfs/fsys-set-options.c
@@ -0,0 +1,88 @@
+/* Parse run-time options
+
+ Copyright (C) 1995, 1996, 2001 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <argz.h>
+#include <hurd/fsys.h>
+#include <string.h>
+
+#include "netfs.h"
+#include "fsys_S.h"
+
+/* This code is originally from libdiskfs; things surrounded by `#if NOT_YET'
+ are pending libnetfs being fleshed out some more. */
+
+/* Implement fsys_set_options as described in <hurd/fsys.defs>. */
+error_t
+netfs_S_fsys_set_options (struct netfs_control *pt,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ char *data, mach_msg_type_number_t data_len,
+ int do_children)
+{
+ error_t err = 0;
+ if (!pt)
+ return EOPNOTSUPP;
+
+ error_t
+ helper (struct node *np)
+ {
+ error_t error;
+ mach_port_t control;
+
+ error = fshelp_fetch_control (&np->transbox, &control);
+ pthread_mutex_unlock (&np->lock);
+ if (!error && (control != MACH_PORT_NULL))
+ {
+ error = fsys_set_options (control, data, data_len, do_children);
+ mach_port_deallocate (mach_task_self (), control);
+ }
+ else
+ error = 0;
+ pthread_mutex_lock (&np->lock);
+
+ if ((error == MIG_SERVER_DIED) || (error == MACH_SEND_INVALID_DEST))
+ error = 0;
+
+ return error;
+ }
+
+#if NOT_YET
+ if (do_children)
+ {
+ pthread_rwlock_wrlock (&netfs_fsys_lock);
+ err = netfs_node_iterate (helper);
+ pthread_rwlock_unlock (&netfs_fsys_lock);
+ }
+#endif
+
+ if (!err)
+ {
+#if NOT_YET
+ pthread_rwlock_wrlock (&netfs_fsys_lock);
+#endif
+ err = netfs_set_options (data, data_len);
+#if NOT_YET
+ pthread_rwlock_unlock (&netfs_fsys_lock);
+#endif
+ }
+
+ return err;
+}
diff --git a/libnetfs/fsys-syncfs.c b/libnetfs/fsys-syncfs.c
new file mode 100644
index 00000000..e232936e
--- /dev/null
+++ b/libnetfs/fsys-syncfs.c
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 1996, 2001 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fsys_S.h"
+
+error_t
+netfs_S_fsys_syncfs (struct netfs_control *cntl,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int wait,
+ int children)
+{
+ struct iouser *cred;
+ error_t err;
+
+ err = iohelp_create_simple_iouser (&cred, 0, 0);
+ if (err)
+ return err;
+ err = netfs_attempt_syncfs (cred, wait);
+ iohelp_free_iouser (cred);
+ return err;
+}
diff --git a/libnetfs/fsysstubs.c b/libnetfs/fsysstubs.c
new file mode 100644
index 00000000..a64fd643
--- /dev/null
+++ b/libnetfs/fsysstubs.c
@@ -0,0 +1,77 @@
+/* Unimplemented rpcs from <hurd/fsys.defs>
+
+ Copyright (C) 1995, 1996, 2001 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "fsys_S.h"
+
+error_t
+netfs_S_fsys_getfile (struct netfs_control *cntl,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ uid_t *uids, mach_msg_type_number_t nuids,
+ gid_t *gids, mach_msg_type_number_t ngids,
+ char *handle, mach_msg_type_number_t handlelen,
+ mach_port_t *file, mach_msg_type_name_t *filetype)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_S_fsys_getpriv (struct netfs_control *cntl,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mach_port_t *host, mach_msg_type_name_t *hosttp,
+ mach_port_t *dev, mach_msg_type_name_t *devtp,
+ mach_port_t *fs, mach_msg_type_name_t *fstp)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_S_fsys_init (struct netfs_control *cntl,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mach_port_t proc, auth_t auth)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_S_fsys_forward (mach_port_t cntl,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mach_port_t request,
+ char *argv, mach_msg_type_number_t argvlen)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_S_fsys_startup (mach_port_t bootstrap,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int flags,
+ mach_port_t contrl,
+ mach_port_t *realnod,
+ mach_msg_type_name_t *realnodetype)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libnetfs/get-source.c b/libnetfs/get-source.c
new file mode 100644
index 00000000..73e48be8
--- /dev/null
+++ b/libnetfs/get-source.c
@@ -0,0 +1,28 @@
+/* Default version of netfs_get_source
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "priv.h"
+
+error_t
+netfs_get_source (struct protid *cred, char *source, size_t source_len)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libnetfs/init-init.c b/libnetfs/init-init.c
new file mode 100644
index 00000000..a088ad51
--- /dev/null
+++ b/libnetfs/init-init.c
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include "netfs.h"
+
+/* For safe inlining of netfs_node_netnode and netfs_netnode_node. */
+size_t const _netfs_sizeof_struct_node = sizeof (struct node);
+
+pthread_spinlock_t netfs_node_refcnt_lock = PTHREAD_SPINLOCK_INITIALIZER;
+
+struct node *netfs_root_node = 0;
+struct port_bucket *netfs_port_bucket = 0;
+struct port_class *netfs_protid_class = 0;
+struct port_class *netfs_control_class = 0;
+auth_t netfs_auth_server_port = 0;
+mach_port_t netfs_fsys_identity;
+
+
+void
+netfs_init ()
+{
+ netfs_protid_class = ports_create_class (netfs_release_protid, 0);
+ netfs_control_class = ports_create_class (0, 0);
+ netfs_port_bucket = ports_create_bucket ();
+ netfs_auth_server_port = getauth ();
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &netfs_fsys_identity);
+}
diff --git a/libnetfs/init-loop.c b/libnetfs/init-loop.c
new file mode 100644
index 00000000..61471c2f
--- /dev/null
+++ b/libnetfs/init-loop.c
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+
+static int thread_timeout = 1000 * 60 * 2; /* two minutes */
+static int server_timeout = 1000 * 60 * 10; /* ten minutes */
+
+void
+netfs_server_loop ()
+{
+ error_t err;
+
+ do
+ {
+ ports_manage_port_operations_multithread (netfs_port_bucket,
+ netfs_demuxer,
+ thread_timeout,
+ server_timeout,
+ 0);
+ err = netfs_shutdown (0);
+ }
+ while (err);
+
+ exit (0);
+}
diff --git a/libnetfs/init-startup.c b/libnetfs/init-startup.c
new file mode 100644
index 00000000..e17c4f16
--- /dev/null
+++ b/libnetfs/init-startup.c
@@ -0,0 +1,66 @@
+/*
+ Copyright (C) 1996,97,2000,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdio.h>
+#include <error.h>
+#include <hurd/fsys.h>
+#include "netfs.h"
+
+mach_port_t
+netfs_startup (mach_port_t bootstrap, int flags)
+{
+ error_t err;
+ mach_port_t realnode, right;
+ struct port_info *newpi;
+
+ if (bootstrap == MACH_PORT_NULL)
+ error (10, 0, "Must be started as a translator");
+
+ err = ports_create_port (netfs_control_class, netfs_port_bucket,
+ sizeof (struct port_info), &newpi);
+ if (!err)
+ {
+ right = ports_get_send_right (newpi);
+ err = fsys_startup (bootstrap, flags, right, MACH_MSG_TYPE_COPY_SEND,
+ &realnode);
+ mach_port_deallocate (mach_task_self (), right);
+ ports_port_deref (newpi);
+ }
+ if (err)
+ error (11, err, "Translator startup failure: fsys_startup");
+
+ mach_port_deallocate (mach_task_self (), bootstrap);
+
+ /* Mark us as important. */
+ mach_port_t proc = getproc ();
+ if (proc == MACH_PORT_NULL)
+ error (12, err, "Translator startup failure: getproc");
+
+ err = proc_mark_important (proc);
+
+ /* This might fail due to permissions or because the old proc server
+ is still running, ignore any such errors. */
+ if (err && err != EPERM && err != EMIG_BAD_ID)
+ error (13, err, "Translator startup failure: proc_mark_important");
+
+ mach_port_deallocate (mach_task_self (), proc);
+
+ return realnode;
+}
diff --git a/libnetfs/io-async.c b/libnetfs/io-async.c
new file mode 100644
index 00000000..10504adb
--- /dev/null
+++ b/libnetfs/io-async.c
@@ -0,0 +1,30 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include "netfs.h"
+#include "io_S.h"
+
+error_t
+netfs_S_io_async (struct protid *user, mach_port_t notify, mach_port_t *id,
+ mach_msg_type_name_t *idt)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libnetfs/io-clear-some-openmodes.c b/libnetfs/io-clear-some-openmodes.c
new file mode 100644
index 00000000..1da5e476
--- /dev/null
+++ b/libnetfs/io-clear-some-openmodes.c
@@ -0,0 +1,35 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "io_S.h"
+#include "modes.h"
+
+error_t
+netfs_S_io_clear_some_openmodes (struct protid *user, int bits)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ user->po->openstat &= ~(bits & HONORED_STATE_MODES);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return 0;
+}
diff --git a/libnetfs/io-duplicate.c b/libnetfs/io-duplicate.c
new file mode 100644
index 00000000..b2c3a3a1
--- /dev/null
+++ b/libnetfs/io-duplicate.c
@@ -0,0 +1,44 @@
+/*
+ Copyright (C) 1995,96,2001 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "io_S.h"
+
+error_t
+netfs_S_io_duplicate (struct protid *user,
+ mach_port_t *newport,
+ mach_msg_type_name_t *newporttp)
+{
+ error_t err;
+ struct protid *newpi;
+ struct iouser *clone;
+
+ err = iohelp_dup_iouser (&clone, user->user);
+ if (err)
+ return err;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ newpi = netfs_make_protid (user->po, clone);
+ *newport = ports_get_right (newpi);
+ pthread_mutex_unlock (&user->po->np->lock);
+ *newporttp = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newpi);
+ return 0;
+}
diff --git a/libnetfs/io-get-icky-async-id.c b/libnetfs/io-get-icky-async-id.c
new file mode 100644
index 00000000..23dac534
--- /dev/null
+++ b/libnetfs/io-get-icky-async-id.c
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "io_S.h"
+
+error_t
+netfs_S_io_get_icky_async_id (struct protid *user, mach_port_t *pt,
+ mach_msg_type_name_t *ptt)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libnetfs/io-get-openmodes.c b/libnetfs/io-get-openmodes.c
new file mode 100644
index 00000000..698c2937
--- /dev/null
+++ b/libnetfs/io-get-openmodes.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "io_S.h"
+
+error_t
+netfs_S_io_get_openmodes (struct protid *user, int *bits)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ *bits = user->po->openstat;
+ pthread_mutex_unlock (&user->po->np->lock);
+ return 0;
+}
diff --git a/libnetfs/io-get-owner.c b/libnetfs/io-get-owner.c
new file mode 100644
index 00000000..9bbf41de
--- /dev/null
+++ b/libnetfs/io-get-owner.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "io_S.h"
+
+error_t
+netfs_S_io_get_owner (struct protid *user, pid_t *owner)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ *owner = user->po->np->owner;
+ pthread_mutex_unlock (&user->po->np->lock);
+ return 0;
+}
diff --git a/libnetfs/io-identity.c b/libnetfs/io-identity.c
new file mode 100644
index 00000000..0c6a191c
--- /dev/null
+++ b/libnetfs/io-identity.c
@@ -0,0 +1,61 @@
+/* libnetfs implementation of io_identity RPC
+ Copyright (C) 1996, 2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "io_S.h"
+
+error_t
+netfs_S_io_identity (struct protid *cred,
+ mach_port_t *id,
+ mach_msg_type_name_t *idtype,
+ mach_port_t *fsys,
+ mach_msg_type_name_t *fsystype,
+ ino_t *fileno)
+{
+ struct node *np;
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+ pthread_mutex_lock (&np->lock);
+
+ err = netfs_validate_stat (np, cred->user);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return err;
+ }
+
+ err = fshelp_get_identity (netfs_port_bucket, np->nn_stat.st_ino, id);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return err;
+ }
+ *idtype = MACH_MSG_TYPE_MAKE_SEND;
+ *fsys = netfs_fsys_identity;
+ *fsystype = MACH_MSG_TYPE_MAKE_SEND;
+ *fileno = np->nn_stat.st_ino;
+
+ pthread_mutex_unlock (&np->lock);
+ return 0;
+}
diff --git a/libnetfs/io-mod-owner.c b/libnetfs/io-mod-owner.c
new file mode 100644
index 00000000..91cbdb03
--- /dev/null
+++ b/libnetfs/io-mod-owner.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "io_S.h"
+
+error_t
+netfs_S_io_mod_owner (struct protid *user, pid_t owner)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ user->po->np->owner = owner;
+ pthread_mutex_unlock (&user->po->np->lock);
+ return 0;
+}
diff --git a/libnetfs/io-pathconf.c b/libnetfs/io-pathconf.c
new file mode 100644
index 00000000..2fd3f5bc
--- /dev/null
+++ b/libnetfs/io-pathconf.c
@@ -0,0 +1,70 @@
+/*
+ Copyright (C) 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <unistd.h>
+#include "netfs.h"
+#include "io_S.h"
+
+
+error_t
+netfs_S_io_pathconf (struct protid *user,
+ int name,
+ int *value)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ switch (name)
+ {
+ case _PC_LINK_MAX:
+ case _PC_MAX_CANON:
+ case _PC_MAX_INPUT:
+ case _PC_PIPE_BUF:
+ case _PC_VDISABLE:
+ case _PC_SOCK_MAXBUF:
+ case _PC_PATH_MAX:
+ *value = -1;
+ break;
+
+ case _PC_NAME_MAX:
+ *value = 1024; /* see <hurd/hurd_types.defs> string_t */
+ break;
+
+ case _PC_CHOWN_RESTRICTED:
+ case _PC_NO_TRUNC: /* look at string_t trunc behavior in MiG */
+ *value = 1;
+ break;
+
+ case _PC_PRIO_IO:
+ case _PC_SYNC_IO:
+ case _PC_ASYNC_IO:
+ *value = 0;
+ break;
+
+ case _PC_FILESIZEBITS:
+ *value = 32;
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ return 0;
+}
diff --git a/libnetfs/io-read.c b/libnetfs/io-read.c
new file mode 100644
index 00000000..0ce75db1
--- /dev/null
+++ b/libnetfs/io-read.c
@@ -0,0 +1,109 @@
+/*
+ Copyright (C) 1995, 1996, 1997, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "io_S.h"
+#include <fcntl.h>
+#include <sys/mman.h>
+
+error_t
+netfs_S_io_read (struct protid *user,
+ char **data,
+ mach_msg_type_number_t *datalen,
+ off_t offset,
+ mach_msg_type_number_t amount)
+{
+ error_t err;
+ off_t start;
+ struct node *node;
+ int alloced = 0;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ node = user->po->np;
+ pthread_mutex_lock (&user->po->np->lock);
+
+ if ((user->po->openstat & O_READ) == 0)
+ {
+ pthread_mutex_unlock (&node->lock);
+ return EBADF;
+ }
+
+ if (amount > *datalen)
+ {
+ alloced = 1;
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ }
+ *datalen = amount;
+
+ start = (offset == -1 ? user->po->filepointer : offset);
+
+ if (start < 0)
+ err = EINVAL;
+ else if (S_ISLNK (node->nn_stat.st_mode))
+ /* Read from a symlink. */
+ {
+ off_t size = node->nn_stat.st_size;
+
+ if (start + amount > size)
+ amount = size - start;
+ if (amount > size)
+ amount = size;
+
+ if (start >= size)
+ {
+ *datalen = 0;
+ err = 0;
+ }
+ else if (amount < size || start > 0)
+ {
+ char *whole_link = alloca (size);
+ err = netfs_attempt_readlink (user->user, node, whole_link);
+ if (! err)
+ {
+ memcpy (*data, whole_link + start, amount);
+ *datalen = amount;
+ }
+ }
+ else
+ {
+ err = netfs_attempt_readlink (user->user, node, *data);
+ *datalen = amount;
+ }
+ }
+ else
+ /* Read from a normal file. */
+ err = netfs_attempt_read (user->user, node, start, datalen, *data);
+
+ if (offset == -1 && !err)
+ user->po->filepointer += *datalen;
+
+ pthread_mutex_unlock (&node->lock);
+
+ if (err && alloced)
+ munmap (*data, amount);
+
+ if (!err && alloced && (round_page (*datalen) < round_page (amount)))
+ munmap (*data + round_page (*datalen),
+ round_page (amount) - round_page (*datalen));
+
+ return err;
+}
diff --git a/libnetfs/io-readable.c b/libnetfs/io-readable.c
new file mode 100644
index 00000000..07becf66
--- /dev/null
+++ b/libnetfs/io-readable.c
@@ -0,0 +1,49 @@
+
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <fcntl.h>
+#include "netfs.h"
+#include "io_S.h"
+
+error_t
+netfs_S_io_readable (struct protid *user,
+ mach_msg_type_number_t *amount)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ if (!(user->po->openstat & O_READ))
+ return EINVAL;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = netfs_validate_stat (user->po->np, user->user);
+ if (!err)
+ {
+ if (user->po->np->nn_stat.st_size > user->po->filepointer)
+ *amount = user->po->np->nn_stat.st_size - user->po->filepointer;
+ else
+ *amount = 0;
+ }
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
diff --git a/libnetfs/io-reauthenticate.c b/libnetfs/io-reauthenticate.c
new file mode 100644
index 00000000..1d2d935a
--- /dev/null
+++ b/libnetfs/io-reauthenticate.c
@@ -0,0 +1,53 @@
+/*
+ Copyright (C) 1995,96,2000,01 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "io_S.h"
+
+error_t
+netfs_S_io_reauthenticate (struct protid *user, mach_port_t rend_port)
+{
+ error_t err;
+ struct protid *newpi;
+ mach_port_t newright;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ newpi = netfs_make_protid (user->po, 0);
+
+ newright = ports_get_send_right (newpi);
+ assert (newright != MACH_PORT_NULL);
+
+ err = iohelp_reauth (&newpi->user, netfs_auth_server_port, rend_port,
+ newright, 1);
+
+ mach_port_deallocate (mach_task_self (), rend_port);
+ mach_port_deallocate (mach_task_self (), newright);
+
+ mach_port_move_member (mach_task_self (), newpi->pi.port_right,
+ netfs_port_bucket->portset);
+
+ pthread_mutex_unlock (&user->po->np->lock);
+ ports_port_deref (newpi);
+
+ return err;
+}
diff --git a/libnetfs/io-restrict-auth.c b/libnetfs/io-restrict-auth.c
new file mode 100644
index 00000000..0c3403d8
--- /dev/null
+++ b/libnetfs/io-restrict-auth.c
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 1995,96,2001,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "io_S.h"
+
+error_t
+netfs_S_io_restrict_auth (struct protid *user,
+ mach_port_t *newport,
+ mach_msg_type_name_t *newporttype,
+ uid_t *uids,
+ mach_msg_type_number_t nuids,
+ gid_t *gids,
+ mach_msg_type_number_t ngids)
+{
+ error_t err;
+ struct protid *newpi;
+ struct iouser *new_user;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ err = iohelp_restrict_iouser (&new_user, user->user,
+ uids, nuids, gids, ngids);
+ if (err)
+ return err;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ newpi = netfs_make_protid (user->po, new_user);
+ if (newpi)
+ {
+ *newport = ports_get_right (newpi);
+ pthread_mutex_unlock (&user->po->np->lock);
+ *newporttype = MACH_MSG_TYPE_MAKE_SEND;
+ }
+ else
+ {
+ pthread_mutex_unlock (&user->po->np->lock);
+ iohelp_free_iouser (new_user);
+ err = ENOMEM;
+ }
+
+ ports_port_deref (newpi);
+ return err;
+}
diff --git a/libnetfs/io-revoke.c b/libnetfs/io-revoke.c
new file mode 100644
index 00000000..7877eabe
--- /dev/null
+++ b/libnetfs/io-revoke.c
@@ -0,0 +1,60 @@
+/*
+ Copyright (C) 1999 Free Software Foundation
+ Written by Thomas Bushnell, BSG.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "netfs.h"
+#include "io_S.h"
+
+/* Implement io_revoke as described in <hurd/io.defs>. */
+kern_return_t
+netfs_S_io_revoke (struct protid *cred)
+{
+ error_t err;
+ struct node *np;
+
+ error_t iterator_function (void *port)
+ {
+ struct protid *user = port;
+
+ if ((user != cred)
+ && (user->po->np == np))
+ ports_destroy_right (user);
+ return 0;
+ }
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ np = cred->po->np;
+
+ pthread_mutex_lock (&np->lock);
+
+ err = netfs_validate_stat (np, cred->user);
+ if (!err)
+ err = fshelp_isowner (&np->nn_stat, cred->user);
+
+ pthread_mutex_unlock (&np->lock);
+
+ if (err)
+ return err;
+
+ ports_inhibit_bucket_rpcs (netfs_port_bucket);
+ ports_class_iterate (netfs_protid_class, iterator_function);
+ ports_resume_bucket_rpcs (netfs_port_bucket);
+
+ return 0;
+}
diff --git a/libnetfs/io-seek.c b/libnetfs/io-seek.c
new file mode 100644
index 00000000..1dc7d322
--- /dev/null
+++ b/libnetfs/io-seek.c
@@ -0,0 +1,67 @@
+/*
+ Copyright (C) 1995, 1996, 2000, 2006 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <unistd.h>
+#include "netfs.h"
+#include "io_S.h"
+
+error_t
+netfs_S_io_seek (struct protid *user,
+ off_t offset,
+ int whence,
+ off_t *newoffset)
+{
+ error_t err = 0;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ switch (whence)
+ {
+ case SEEK_CUR:
+ offset += user->po->filepointer;
+ goto check;
+ case SEEK_END:
+ {
+ struct node *np;
+
+ np = user->po->np;
+ pthread_mutex_lock (&np->lock);
+
+ err = netfs_validate_stat (np, user->user);
+ if (!err)
+ offset += np->nn_stat.st_size;
+
+ pthread_mutex_unlock (&np->lock);
+ }
+ case SEEK_SET:
+ check:
+ if (offset >= 0)
+ {
+ *newoffset = user->po->filepointer = offset;
+ break;
+ }
+ default:
+ err = EINVAL;
+ break;
+ }
+
+ return err;
+}
diff --git a/libnetfs/io-select.c b/libnetfs/io-select.c
new file mode 100644
index 00000000..63036145
--- /dev/null
+++ b/libnetfs/io-select.c
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 1995, 1996, 2004 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "io_S.h"
+#include <hurd/ports.h>
+
+error_t
+netfs_S_io_select (struct protid *user,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int *type)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ *type &= ~SELECT_URG;
+ return 0;
+}
+
+error_t
+netfs_S_io_select_timeout (struct protid *user,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ struct timespec ts,
+ int *type)
+{
+ return netfs_S_io_select (user, reply, replytype, type);
+}
diff --git a/libnetfs/io-set-all-openmodes.c b/libnetfs/io-set-all-openmodes.c
new file mode 100644
index 00000000..2424282e
--- /dev/null
+++ b/libnetfs/io-set-all-openmodes.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "io_S.h"
+#include "modes.h"
+
+error_t
+netfs_S_io_set_all_openmodes (struct protid *user, int newbits)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ user->po->openstat &= ~HONORED_STATE_MODES;
+ user->po->openstat |= (newbits & HONORED_STATE_MODES);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return 0;
+}
diff --git a/libnetfs/io-set-some-openmodes.c b/libnetfs/io-set-some-openmodes.c
new file mode 100644
index 00000000..48fbeed6
--- /dev/null
+++ b/libnetfs/io-set-some-openmodes.c
@@ -0,0 +1,35 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "io_S.h"
+#include "modes.h"
+
+error_t
+netfs_S_io_set_some_openmodes (struct protid *user, int bits)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ user->po->openstat |= (bits & HONORED_STATE_MODES);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return 0;
+}
diff --git a/libnetfs/io-stat.c b/libnetfs/io-stat.c
new file mode 100644
index 00000000..81e42bdb
--- /dev/null
+++ b/libnetfs/io-stat.c
@@ -0,0 +1,53 @@
+/*
+ Copyright (C) 1995, 1996, 1997, 2000 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "io_S.h"
+#include <string.h>
+
+error_t
+netfs_S_io_stat (struct protid *user, io_statbuf_t *statbuf)
+{
+ error_t err;
+ struct node *node;
+
+ if (! user)
+ return EOPNOTSUPP;
+
+ node = user->po->np;
+ pthread_mutex_lock (&node->lock);
+
+ err = netfs_validate_stat (node, user->user);
+ if (! err)
+ {
+ memcpy (statbuf, &node->nn_stat, sizeof (struct stat));
+
+ /* Set S_IATRANS and S_IROOT bits as appropriate. */
+ statbuf->st_mode &= ~(S_IATRANS | S_IROOT);
+ if (fshelp_translated (&node->transbox))
+ statbuf->st_mode |= S_IATRANS; /* Has an active translator. */
+ if (user->po->shadow_root == node || node == netfs_root_node)
+ statbuf->st_mode |= S_IROOT; /* Is a root node. */
+ }
+
+ pthread_mutex_unlock (&node->lock);
+
+ return err;
+}
diff --git a/libnetfs/io-version.c b/libnetfs/io-version.c
new file mode 100644
index 00000000..d38b194a
--- /dev/null
+++ b/libnetfs/io-version.c
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 1994, 1996, 2002 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include <stdio.h>
+
+#include "netfs.h"
+#include "io_S.h"
+
+kern_return_t
+netfs_S_io_server_version (struct protid *cred,
+ char *server_name,
+ int *major,
+ int *minor,
+ int *edit)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ snprintf (server_name, sizeof (string_t), "%s %s",
+ netfs_server_name, netfs_server_version);
+ return 0;
+}
diff --git a/libnetfs/io-write.c b/libnetfs/io-write.c
new file mode 100644
index 00000000..19bcf09c
--- /dev/null
+++ b/libnetfs/io-write.c
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) 1995, 1996, 2000 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include "io_S.h"
+#include <fcntl.h>
+
+error_t
+netfs_S_io_write (struct protid *user,
+ char *data,
+ mach_msg_type_number_t datalen,
+ off_t offset,
+ mach_msg_type_number_t *amount)
+{
+ error_t err;
+ off_t off = offset;
+ struct node *np;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ if ((user->po->openstat & O_WRITE) == 0)
+ return EBADF;
+
+ *amount = datalen;
+
+ np = user->po->np;
+ pthread_mutex_lock (&np->lock);
+
+ if (off == -1)
+ {
+ if (user->po->openstat & O_APPEND)
+ {
+ err = netfs_validate_stat (np, user->user);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return err;
+ }
+ user->po->filepointer = np->nn_stat.st_size;
+ }
+ off = user->po->filepointer;
+ }
+
+ err = netfs_attempt_write (user->user, np, off, amount, data);
+ if (offset == -1 && !err)
+ user->po->filepointer += *amount;
+ pthread_mutex_unlock (&np->lock);
+
+ return err;
+}
diff --git a/libnetfs/iostubs.c b/libnetfs/iostubs.c
new file mode 100644
index 00000000..1465b43a
--- /dev/null
+++ b/libnetfs/iostubs.c
@@ -0,0 +1,89 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include "netfs.h"
+#include "io_S.h"
+
+error_t
+netfs_S_io_map (struct protid *user,
+ mach_port_t *rdobj, mach_msg_type_name_t *rdobjtype,
+ mach_port_t *wrobj, mach_msg_type_name_t *wrobjtype)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_S_io_map_cntl (struct protid *user,
+ mach_port_t *obj,
+ mach_msg_type_name_t *objtype)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_S_io_get_conch (struct protid *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_S_io_release_conch (struct protid *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_S_io_eofnotify (struct protid *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_S_io_prenotify (struct protid *user,
+ vm_offset_t start, vm_offset_t stop)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_S_io_postnotify (struct protid *user,
+ vm_offset_t start, vm_offset_t stop)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_S_io_readnotify (struct protid *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_S_io_readsleep (struct protid *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_S_io_sigio (struct protid *user)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libnetfs/make-node.c b/libnetfs/make-node.c
new file mode 100644
index 00000000..6bd8109c
--- /dev/null
+++ b/libnetfs/make-node.c
@@ -0,0 +1,59 @@
+/*
+ Copyright (C) 1995, 1996, 2000 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include <hurd/fshelp.h>
+
+static struct node *
+init_node (struct node *np, struct netnode *nn)
+{
+ np->nn = nn;
+
+ pthread_mutex_init (&np->lock, NULL);
+ np->references = 1;
+ np->sockaddr = MACH_PORT_NULL;
+ np->owner = 0;
+
+ fshelp_transbox_init (&np->transbox, &np->lock, np);
+ fshelp_lock_init (&np->userlock);
+
+ return np;
+}
+
+struct node *
+netfs_make_node (struct netnode *nn)
+{
+ struct node *np = malloc (sizeof (struct node));
+ if (! np)
+ return NULL;
+
+ return init_node (np, nn);
+}
+
+struct node *
+netfs_make_node_alloc (size_t size)
+{
+ struct node *np = malloc (sizeof (struct node) + size);
+
+ if (np == NULL)
+ return NULL;
+
+ return init_node (np, netfs_node_netnode (np));
+}
diff --git a/libnetfs/make-peropen.c b/libnetfs/make-peropen.c
new file mode 100644
index 00000000..f7be58b1
--- /dev/null
+++ b/libnetfs/make-peropen.c
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) 1995, 1997 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include <sys/file.h>
+
+struct peropen *
+netfs_make_peropen (struct node *np, int flags, struct peropen *context)
+{
+ struct peropen *po = malloc (sizeof (struct peropen));
+
+ if (!po)
+ return NULL;
+
+ po->filepointer = 0;
+ po->lock_status = LOCK_UN;
+ po->refcnt = 0;
+ po->openstat = flags;
+ po->np = np;
+ po->path = NULL;
+
+ if (context)
+ {
+ if (context->path)
+ {
+ po->path = strdup (context->path);
+ if (! po->path) {
+ free(po);
+ return NULL;
+ }
+ }
+
+ po->root_parent = context->root_parent;
+ if (po->root_parent != MACH_PORT_NULL)
+ mach_port_mod_refs (mach_task_self (), po->root_parent,
+ MACH_PORT_RIGHT_SEND, 1);
+
+ po->shadow_root = context->shadow_root;
+ if (po->shadow_root)
+ netfs_nref (po->shadow_root);
+
+ po->shadow_root_parent = context->shadow_root_parent;
+ if (po->shadow_root_parent != MACH_PORT_NULL)
+ mach_port_mod_refs (mach_task_self (), po->shadow_root_parent,
+ MACH_PORT_RIGHT_SEND, 1);
+ }
+
+ netfs_nref (np);
+
+ return po;
+}
diff --git a/libnetfs/make-protid.c b/libnetfs/make-protid.c
new file mode 100644
index 00000000..bf18283c
--- /dev/null
+++ b/libnetfs/make-protid.c
@@ -0,0 +1,45 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+
+struct protid *
+netfs_make_protid (struct peropen *po, struct iouser *cred)
+{
+ struct protid *pi;
+
+ if (cred)
+ errno = ports_create_port (netfs_protid_class, netfs_port_bucket,
+ sizeof (struct protid), &pi);
+ else
+ errno = ports_create_port_noinstall (netfs_protid_class,
+ netfs_port_bucket,
+ sizeof (struct protid), &pi);
+
+ if (errno)
+ return 0;
+
+ po->refcnt++;
+ pi->po = po;
+ pi->user = cred;
+ pi->shared_object = MACH_PORT_NULL;
+ pi->mapped = 0;
+ return pi;
+}
diff --git a/libnetfs/misc.h b/libnetfs/misc.h
new file mode 100644
index 00000000..4845d252
--- /dev/null
+++ b/libnetfs/misc.h
@@ -0,0 +1,24 @@
+/*
+ Copyright (C) 1995, 2004 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <fcntl.h>
+
+/* Bits that are turned off after open */
+#define OPENONLY_STATE_MODES (O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS)
diff --git a/libnetfs/modes.h b/libnetfs/modes.h
new file mode 100644
index 00000000..e5f52ed1
--- /dev/null
+++ b/libnetfs/modes.h
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <fcntl.h>
+#define HONORED_STATE_MODES (O_APPEND|O_ASYNC|O_FSYNC|O_NONBLOCK|O_NOATIME)
diff --git a/libnetfs/mutations.h b/libnetfs/mutations.h
new file mode 100644
index 00000000..e60a2208
--- /dev/null
+++ b/libnetfs/mutations.h
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 1995, 2004 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* Only CPP macro definitions should go in this file. */
+
+#define IO_SELECT_REPLY_PORT
+
+#define FILE_INTRAN protid_t begin_using_protid_port (file_t)
+#define FILE_DESTRUCTOR end_using_protid_port (protid_t)
+
+#define IO_INTRAN protid_t begin_using_protid_port (io_t)
+#define IO_DESTRUCTOR end_using_protid_port (protid_t)
+
+#define FSYS_INTRAN control_t begin_using_control_port (fsys_t)
+#define FSYS_DESTRUCTOR end_using_control_port (control_t)
+
+#define FILE_IMPORTS import "libnetfs/netfs.h"; import "libnetfs/priv.h";
+#define IO_IMPORTS import "libnetfs/netfs.h"; import "libnetfs/priv.h";
+#define FSYS_IMPORTS import "libnetfs/netfs.h"; import "libnetfs/priv.h";
+#define IFSOCK_IMPORTS import "libnetfs/netfs.h"; import "libnetfs/priv.h";
diff --git a/libnetfs/netfs.h b/libnetfs/netfs.h
new file mode 100644
index 00000000..fbe2c60d
--- /dev/null
+++ b/libnetfs/netfs.h
@@ -0,0 +1,475 @@
+/*
+
+ Copyright (C) 1994,95,96,97,99,2000,02,13 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _HURD_NETFS_H_
+#define _HURD_NETFS_H_
+
+#include <hurd/ports.h>
+#include <hurd/fshelp.h>
+#include <hurd/iohelp.h>
+#include <assert.h>
+#include <pthread.h>
+
+/* This library supports client-side network file system
+ implementations. It is analogous to the diskfs library provided for
+ disk-based filesystems. */
+
+struct argp;
+
+struct protid
+{
+ struct port_info pi;
+
+ /* User identification */
+ struct iouser *user;
+
+ /* Object this refers to */
+ struct peropen *po;
+
+ /* Shared memory I/O information. */
+ memory_object_t shared_object;
+ struct shared_io *mapped;
+};
+
+/* One of these is created for each open */
+struct peropen
+{
+ loff_t filepointer;
+ int lock_status;
+ int refcnt;
+ int openstat;
+
+ struct node *np;
+
+ /* The parent of the translator's root node. */
+ mach_port_t root_parent;
+
+ /* If this node is in a shadow tree, the parent of its root. */
+ mach_port_t shadow_root_parent;
+ /* If in a shadow tree, its root node in this translator. */
+ struct node *shadow_root;
+
+ char *path;
+};
+
+/* A unique one of these exists for each node currently in use. */
+struct node
+{
+ struct node *next, **prevp;
+
+ /* Protocol specific stuff; defined by user. */
+ struct netnode *nn;
+
+ /* The stat information for this particular node. */
+ io_statbuf_t nn_stat;
+ /* The S_IPTRANS and S_IFMT bits here are examined instead of nn_stat.st_mode
+ to decide whether to do passive translator processing. Other bits
+ are ignored, so you can set this to nn_stat.st_mode if you want that. */
+ mode_t nn_translated;
+
+ pthread_mutex_t lock;
+
+ /* The number of references to this node. */
+ int references;
+
+ mach_port_t sockaddr;
+
+ int owner;
+
+ struct transbox transbox;
+
+ struct lock_box userlock;
+
+ struct conch conch;
+
+ struct dirmod *dirmod_reqs;
+};
+
+struct netfs_control
+{
+ struct port_info pi;
+};
+
+/* The user must define this variable. Set this to the name of the
+ filesystem server. */
+extern char *netfs_server_name;
+
+/* The user must define this variables. Set this to be the server
+ version number. */
+extern char *netfs_server_version;
+
+/* The user must define this function. Make sure that NP->nn_stat is
+ filled with the most current information. CRED identifies the user
+ responsible for the operation. NP is locked. */
+error_t netfs_validate_stat (struct node *np, struct iouser *cred);
+
+/* The user must define this function. This should attempt a chmod
+ call for the user specified by CRED on locked node NP, to change
+ the owner to UID and the group to GID. */
+error_t netfs_attempt_chown (struct iouser *cred, struct node *np,
+ uid_t uid, uid_t gid);
+
+/* The user must define this function. This should attempt a chauthor
+ call for the user specified by CRED on locked node NP, thereby
+ changing the author to AUTHOR. */
+error_t netfs_attempt_chauthor (struct iouser *cred, struct node *np,
+ uid_t author);
+
+/* The user must define this function. This should attempt a chmod
+ call for the user specified by CRED on locked node NODE, to change
+ the mode to MODE. Unlike the normal Unix and Hurd meaning of
+ chmod, this function is also used to attempt to change files into
+ other types. If such a transition is attempted which is
+ impossible, then return EOPNOTSUPP. */
+error_t netfs_attempt_chmod (struct iouser *cred, struct node *np,
+ mode_t mode);
+
+/* The user must define this function. Attempt to turn locked node NP
+ (user CRED) into a symlink with target NAME. */
+error_t netfs_attempt_mksymlink (struct iouser *cred, struct node *np,
+ char *name);
+
+/* The user must define this function. Attempt to turn NODE (user
+ CRED) into a device. TYPE is either S_IFBLK or S_IFCHR. NP is
+ locked. */
+error_t netfs_attempt_mkdev (struct iouser *cred, struct node *np,
+ mode_t type, dev_t indexes);
+
+/* The user may define this function. Attempt to set the passive
+ translator record for FILE to ARGZ (of length ARGZLEN) for user
+ CRED. NP is locked. */
+error_t netfs_set_translator (struct iouser *cred, struct node *np,
+ char *argz, size_t argzlen);
+
+/* The user may define this function (but should define it together
+ with netfs_set_translator). For locked node NODE with S_IPTRANS
+ set in its mode, look up the name of its translator. Store the
+ name into newly malloced storage, and return it in *ARGZ; set
+ *ARGZ_LEN to the total length. */
+error_t netfs_get_translator (struct node *node, char **argz,
+ size_t *argz_len);
+
+/* The user must define this function. This should attempt a chflags
+ call for the user specified by CRED on locked node NP, to change
+ the flags to FLAGS. */
+error_t netfs_attempt_chflags (struct iouser *cred, struct node *np,
+ int flags);
+
+/* The user must define this function. This should attempt a utimes
+ call for the user specified by CRED on locked node NP, to change
+ the atime to ATIME and the mtime to MTIME. If ATIME or MTIME is
+ null, then set to the current time. */
+error_t netfs_attempt_utimes (struct iouser *cred, struct node *np,
+ struct timespec *atime, struct timespec *mtime);
+
+/* The user must define this function. This should attempt to set the
+ size of the locked file NP (for user CRED) to SIZE bytes long. */
+error_t netfs_attempt_set_size (struct iouser *cred, struct node *np,
+ loff_t size);
+
+/* The user must define this function. This should attempt to fetch
+ filesystem status information for the remote filesystem, for the
+ user CRED. NP is locked. */
+error_t netfs_attempt_statfs (struct iouser *cred, struct node *np,
+ fsys_statfsbuf_t *st);
+
+/* The user must define this function. This should sync the locked
+ file NP completely to disk, for the user CRED. If WAIT is set,
+ return only after the sync is completely finished. */
+error_t netfs_attempt_sync (struct iouser *cred, struct node *np,
+ int wait);
+
+/* The user must define this function. This should sync the entire
+ remote filesystem. If WAIT is set, return only after the sync is
+ completely finished. */
+error_t netfs_attempt_syncfs (struct iouser *cred, int wait);
+
+/* The user must define this function. Lookup NAME in DIR (which is
+ locked) for USER; set *NP to the found name upon return. If the
+ name was not found, then return ENOENT. On any error, clear *NP.
+ (*NP, if found, should be locked and a reference to it generated.
+ This call should unlock DIR no matter what.) */
+error_t netfs_attempt_lookup (struct iouser *user, struct node *dir,
+ char *name, struct node **np);
+
+/* The user must define this function. Delete NAME in DIR (which is
+ locked) for USER. */
+error_t netfs_attempt_unlink (struct iouser *user, struct node *dir,
+ char *name);
+
+/* The user must define this function. Attempt to rename the
+ directory FROMDIR to TODIR. Note that neither of the specific nodes
+ are locked. */
+error_t netfs_attempt_rename (struct iouser *user, struct node *fromdir,
+ char *fromname, struct node *todir,
+ char *toname, int excl);
+
+/* The user must define this function. Attempt to create a new
+ directory named NAME in DIR (which is locked) for USER with mode
+ MODE. */
+error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir,
+ char *name, mode_t mode);
+
+/* The user must define this function. Attempt to remove directory
+ named NAME in DIR (which is locked) for USER. */
+error_t netfs_attempt_rmdir (struct iouser *user,
+ struct node *dir, char *name);
+
+
+/* The user must define this function. Create a link in DIR with name
+ NAME to FILE for USER. Note that neither DIR nor FILE are
+ locked. If EXCL is set, do not delete the target. Return EEXIST if
+ NAME is already found in DIR. */
+error_t netfs_attempt_link (struct iouser *user, struct node *dir,
+ struct node *file, char *name, int excl);
+
+/* The user must define this function. Attempt to create an anonymous
+ file related to DIR (which is locked) for USER with MODE. Set *NP
+ to the returned file upon success. No matter what, unlock DIR. */
+error_t netfs_attempt_mkfile (struct iouser *user, struct node *dir,
+ mode_t mode, struct node **np);
+
+/* The user must define this function. Attempt to create a file named
+ NAME in DIR (which is locked) for USER with MODE. Set *NP to the
+ new node upon return. On any error, clear *NP. *NP should be
+ locked on success; no matter what, unlock DIR before returning. */
+error_t netfs_attempt_create_file (struct iouser *user, struct node *dir,
+ char *name, mode_t mode, struct node **np);
+
+/* The user must define this function. Read the contents of locked
+ node NP (a symlink), for USER, into BUF. */
+error_t netfs_attempt_readlink (struct iouser *user, struct node *np,
+ char *buf);
+
+/* The user must define this function. Locked node NP is being opened
+ by USER, with FLAGS. NEWNODE is nonzero if we just created this
+ node. Return an error if we should not permit the open to complete
+ because of a permission restriction. */
+error_t netfs_check_open_permissions (struct iouser *user, struct node *np,
+ int flags, int newnode);
+
+/* The user must define this function. Read from the locked file NP
+ for user CRED starting at OFFSET and continuing for up to *LEN
+ bytes. Put the data at DATA. Set *LEN to the amount successfully
+ read upon return. */
+error_t netfs_attempt_read (struct iouser *cred, struct node *np,
+ loff_t offset, size_t *len, void *data);
+
+/* The user must define this function. Write to the locked file NP
+ for user CRED starting at OFSET and continuing for up to *LEN bytes
+ from DATA. Set *LEN to the amount successfully written upon
+ return. */
+error_t netfs_attempt_write (struct iouser *cred, struct node *np,
+ loff_t offset, size_t *len, void *data);
+
+/* The user must define this function. Return the valid access
+ types (bitwise OR of O_READ, O_WRITE, and O_EXEC) in *TYPES for
+ locked file NP and user CRED. */
+error_t netfs_report_access (struct iouser *cred, struct node *np,
+ int *types);
+
+/* The user must define this function. Create a new user from the
+ specified UID and GID arrays. */
+struct iouser *netfs_make_user (uid_t *uids, int nuids,
+ uid_t *gids, int ngids);
+
+/* The user must define this function. Node NP has no more references;
+ free all its associated storage. */
+void netfs_node_norefs (struct node *np);
+
+/* The user must define this function. Fill the array *DATA of size
+ BUFSIZE with up to NENTRIES dirents from DIR (which is locked)
+ starting with entry ENTRY for user CRED. The number of entries in
+ the array is stored in *AMT and the number of bytes in *DATACNT.
+ If the supplied buffer is not large enough to hold the data, it
+ should be grown. */
+error_t netfs_get_dirents (struct iouser *cred, struct node *dir,
+ int entry, int nentries, char **data,
+ mach_msg_type_number_t *datacnt,
+ vm_size_t bufsize, int *amt);
+
+/* The user may define this function. For a full description,
+ see hurd/hurd_types.h. The default response indicates a network
+ store. If the supplied buffers are not large enough, they should
+ be grown as necessary. NP is locked. */
+error_t netfs_file_get_storage_info (struct iouser *cred,
+ struct node *np,
+ mach_port_t **ports,
+ mach_msg_type_name_t *ports_type,
+ mach_msg_type_number_t *num_ports,
+ int **ints,
+ mach_msg_type_number_t *num_ints,
+ loff_t **offsets,
+ mach_msg_type_number_t *num_offsets,
+ char **data,
+ mach_msg_type_number_t *data_len);
+
+/* The user may define this function. The function must set source to
+ the source of CRED. The function may return an EOPNOTSUPP to
+ indicate that the concept of a source device is not applicable. The
+ default function always returns EOPNOTSUPP. */
+error_t netfs_get_source (struct protid *cred,
+ char *source, size_t source_len);
+
+/* Option parsing */
+
+/* Parse and execute the runtime options in ARGZ & ARGZ_LEN. EINVAL is
+ returned if some option is unrecognized. The default definition of this
+ routine will parse them using NETFS_RUNTIME_ARGP. */
+error_t netfs_set_options (char *argz, size_t argz_len);
+
+/* Append to the malloced string *ARGZ of length *ARGZ_LEN a NUL-separated
+ list of the arguments to this translator. The default definition of this
+ routine simply calls netfs_append_std_options. */
+error_t netfs_append_args (char **argz, size_t *argz_len);
+
+/* If this is defined or set to a pointer to an argp structure, it will be
+ used by the default netfs_set_options to handle runtime option parsing.
+ The default definition is initialized to a pointer to
+ NETFS_STD_RUNTIME_ARGP. Setting this variable is the usual way to add
+ option parsing to a program using libnetfs. */
+extern struct argp *netfs_runtime_argp;
+
+/* An argp for the standard netfs runtime options. The default definition
+ of NETFS_RUNTIME_ARGP points to this, although if the user redefines
+ that, he may chain this onto his argp as well. */
+extern const struct argp netfs_std_runtime_argp;
+
+/* An argp structure for the standard netfs command line arguments. The
+ user may call argp_parse on this to parse the command line, chain it onto
+ the end of his own argp structure, or ignore it completely. */
+extern const struct argp netfs_std_startup_argp;
+
+/* *Appends* to ARGZ & ARGZ_LEN '\0'-separated options describing the standard
+ netfs option state (note that unlike netfs_get_options, ARGZ & ARGZ_LEN
+ must already have a sane value). */
+error_t netfs_append_std_options (char **argz, size_t *argz_len);
+
+/* Definitions provided by user */
+
+/* Maximum number of symlinks to follow before returning ELOOP. */
+extern int netfs_maxsymlinks;
+
+/* Definitions provided by netfs. */
+
+/* Given a netnode created by the user program, wraps it in a node
+ structure. The new node is not locked and has a single reference.
+ If an error occurs, NULL is returned. */
+struct node *netfs_make_node (struct netnode *);
+
+/* Create a new node structure. Also allocate SIZE bytes for the
+ netnode. The address of the netnode can be obtained using
+ netfs_node_netnode. The new node will have one hard reference and
+ no light references. If an error occurs, NULL is returned. */
+struct node *netfs_make_node_alloc (size_t size);
+
+/* To avoid breaking the ABI whenever sizeof (struct node) changes, we
+ explicitly provide the size. The following two functions will use
+ this value for offset calculations. */
+extern const size_t _netfs_sizeof_struct_node;
+
+/* Return the address of the netnode for NODE. NODE must have been
+ allocated using netfs_make_node_alloc. */
+static inline struct netnode *
+netfs_node_netnode (struct node *node)
+{
+ return (struct netnode *) ((char *) node + _netfs_sizeof_struct_node);
+}
+
+/* Return the address of the node for NETNODE. NETNODE must have been
+ allocated using netfs_make_node_alloc. */
+static inline struct node *
+netfs_netnode_node (struct netnode *netnode)
+{
+ return (struct node *) ((char *) netnode - _netfs_sizeof_struct_node);
+}
+
+/* Whenever node->references is to be touched, this lock must be
+ held. Cf. netfs_nrele, netfs_nput, netfs_nref and netfs_drop_node. */
+extern pthread_spinlock_t netfs_node_refcnt_lock;
+
+/* Normally called in main. This function sets up some of the netfs
+ server's internal state. */
+void netfs_init (void);
+
+/* Starts the netfs server. Called after netfs_init. BOOTSTRAP is
+ the bootstrap port. FLAGS indicate how to open the underlying node
+ (Cf. hurd/fsys.defs). */
+mach_port_t netfs_startup (mach_port_t bootstrap, int flags);
+
+/* Normally called as the last function in main. The netfs server now
+ begins answering requests. This function never returns. */
+void netfs_server_loop (void);
+
+/* Creates a new credential from USER which can be NULL on the peropen
+ PO. Returns NULL and sets errno on error. */
+struct protid *netfs_make_protid (struct peropen *po, struct iouser *user);
+
+/* Create and return a new peropen structure on node NP with open
+ flags FLAGS. The initial values for the root_parent, shadow_root,
+ and shadow_root_parent fields are copied from CONTEXT if it's
+ non-zero, otherwise zeroed. */
+struct peropen *netfs_make_peropen (struct node *, int,
+ struct peropen *context);
+
+/* Add a reference to node NP. Unless you already hold a reference,
+ NP must be locked. */
+void netfs_nref (struct node *np);
+
+/* Releases a node. Drops a reference to node NP, which must not be
+ locked by the caller. If this was the last reference, drops the
+ node. The node cannot be used again without first obtaining a
+ reference to it. */
+void netfs_nrele (struct node *np);
+
+/* Puts a node back. Drops a reference to the node NP, which must be
+ locked by the caller (this lock will be released by netfs_nput).
+ If this was the last reference, drops the node. The node cannot be
+ used again without first obtaining a reference to it. */
+void netfs_nput (struct node *np);
+
+/* Called internally when no more references to node NP exist. */
+void netfs_drop_node (struct node *np);
+
+/* Called internally when no more references to a protid exit. */
+void netfs_release_protid (void *);
+
+/* Called internally when no more references to a protid exit. */
+void netfs_release_peropen (struct peropen *);
+
+/* The netfs message demuxer. */
+int netfs_demuxer (mach_msg_header_t *, mach_msg_header_t *);
+
+/* Called to ask the filesystem to shutdown. If it returns, an error
+ occurred. FLAGS are passed to fsys_goaway. */
+error_t netfs_shutdown (int flags);
+
+extern struct port_class *netfs_protid_class;
+extern struct port_class *netfs_control_class;
+extern struct port_bucket *netfs_port_bucket;
+extern struct node *netfs_root_node;
+extern mach_port_t netfs_fsys_identity;
+extern auth_t netfs_auth_server_port;
+
+
+/* Mig gook. */
+typedef struct protid *protid_t;
+typedef struct netfs_control *control_t;
+
+
+#endif /* _HURD_NETFS_H_ */
diff --git a/libnetfs/nput.c b/libnetfs/nput.c
new file mode 100644
index 00000000..522c714a
--- /dev/null
+++ b/libnetfs/nput.c
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+void
+netfs_nput (struct node *np)
+{
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ assert (np->references);
+ np->references--;
+ if (np->references == 0)
+ netfs_drop_node (np);
+ /* netfs_drop_node drops netfs_node_refcnt_lock for us. */
+ else
+ {
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+ pthread_mutex_unlock (&np->lock);
+ }
+}
diff --git a/libnetfs/nref.c b/libnetfs/nref.c
new file mode 100644
index 00000000..86b49927
--- /dev/null
+++ b/libnetfs/nref.c
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+void
+netfs_nref (struct node *np)
+{
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ np->references++;
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+}
diff --git a/libnetfs/nrele.c b/libnetfs/nrele.c
new file mode 100644
index 00000000..6f9a0144
--- /dev/null
+++ b/libnetfs/nrele.c
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+void
+netfs_nrele (struct node *np)
+{
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ assert (np->references);
+ np->references--;
+ if (np->references == 0)
+ {
+ pthread_mutex_lock (&np->lock);
+ netfs_drop_node (np);
+ /* netfs_drop_node drops netfs_node_refcnt_lock for us. */
+ }
+ else
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+}
diff --git a/libnetfs/priv.h b/libnetfs/priv.h
new file mode 100644
index 00000000..ba310802
--- /dev/null
+++ b/libnetfs/priv.h
@@ -0,0 +1,54 @@
+/*
+ Copyright (C) 1995,96,2001 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef _LIBNETFS_PRIV_H
+#define _LIBNETFS_PRIV_H
+
+#include <hurd/hurd_types.h>
+
+#include "netfs.h"
+
+static inline struct protid * __attribute__ ((unused))
+begin_using_protid_port (file_t port)
+{
+ return ports_lookup_port (netfs_port_bucket, port, netfs_protid_class);
+}
+
+static inline void __attribute__ ((unused))
+end_using_protid_port (struct protid *cred)
+{
+ if (cred)
+ ports_port_deref (cred);
+}
+
+static inline struct netfs_control * __attribute__ ((unused))
+begin_using_control_port (fsys_t port)
+{
+ return ports_lookup_port (netfs_port_bucket, port, netfs_control_class);
+}
+
+static inline void __attribute__ ((unused))
+end_using_control_port (struct netfs_control *cred)
+{
+ if (cred)
+ ports_port_deref (cred);
+}
+
+#endif
diff --git a/libnetfs/release-peropen.c b/libnetfs/release-peropen.c
new file mode 100644
index 00000000..c206b438
--- /dev/null
+++ b/libnetfs/release-peropen.c
@@ -0,0 +1,52 @@
+/*
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <sys/file.h>
+#include "netfs.h"
+
+void
+netfs_release_peropen (struct peropen *po)
+{
+ pthread_mutex_lock (&po->np->lock);
+ if (--po->refcnt)
+ pthread_mutex_unlock (&po->np->lock);
+ else
+ {
+ if (po->root_parent)
+ mach_port_deallocate (mach_task_self (), po->root_parent);
+
+ if (po->shadow_root && po->shadow_root != po->np)
+ {
+ pthread_mutex_lock (&po->shadow_root->lock);
+ netfs_nput (po->shadow_root);
+ }
+ if (po->shadow_root_parent)
+ mach_port_deallocate (mach_task_self (), po->shadow_root_parent);
+
+ if (po->lock_status != LOCK_UN)
+ fshelp_acquire_lock (&po->np->userlock, &po->lock_status,
+ &po->np->lock, LOCK_UN);
+
+ netfs_nput (po->np);
+
+ free (po->path);
+ free (po);
+ }
+}
diff --git a/libnetfs/release-protid.c b/libnetfs/release-protid.c
new file mode 100644
index 00000000..b1bac0eb
--- /dev/null
+++ b/libnetfs/release-protid.c
@@ -0,0 +1,35 @@
+/*
+ Copyright (C) 1996, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+#include <sys/mman.h>
+
+void
+netfs_release_protid (void *arg)
+{
+ struct protid *user = arg;
+
+ iohelp_free_iouser (user->user);
+ if (user->shared_object)
+ mach_port_deallocate (mach_task_self (), user->shared_object);
+ if (user->mapped)
+ munmap (user->mapped, vm_page_size);
+ netfs_release_peropen (user->po);
+}
diff --git a/libnetfs/runtime-argp.c b/libnetfs/runtime-argp.c
new file mode 100644
index 00000000..9e5989c7
--- /dev/null
+++ b/libnetfs/runtime-argp.c
@@ -0,0 +1,24 @@
+/* Default definition for netfs_runtime_argp
+
+ Copyright (C) 1996, 2004 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "netfs.h"
+#include <argp.h>
+
+struct argp *netfs_runtime_argp = (struct argp *)&netfs_std_runtime_argp;
diff --git a/libnetfs/set-get-trans.c b/libnetfs/set-get-trans.c
new file mode 100644
index 00000000..b58668d8
--- /dev/null
+++ b/libnetfs/set-get-trans.c
@@ -0,0 +1,47 @@
+/* Default versions of netfs_set_translator & netfs_get_translator
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "netfs.h"
+
+/* Default implementations of the netfs_set_translator and
+ netfs_set_translator functions. */
+
+/* The user may define this function. Attempt to set the passive
+ translator record for FILE to ARGZ (of length ARGZLEN) for user
+ CRED. */
+error_t
+netfs_set_translator (struct iouser *cred, struct node *np,
+ char *argz, size_t argzlen)
+{
+ return EOPNOTSUPP;
+}
+
+/* The user may define this function (but should define it together with
+ netfs_set_translator). For locked node NODE with S_IPTRANS set in its
+ mode, look up the name of its translator. Store the name into newly
+ malloced storage, and return it in *ARGZ; set *ARGZ_LEN to the total
+ length. */
+error_t
+netfs_get_translator (struct node *node, char **argz, size_t *argz_len)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libnetfs/set-options.c b/libnetfs/set-options.c
new file mode 100644
index 00000000..416d9123
--- /dev/null
+++ b/libnetfs/set-options.c
@@ -0,0 +1,30 @@
+/* Parse run-time options
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "netfs.h"
+
+error_t
+netfs_set_options (char *argz, size_t argz_len)
+{
+ if (netfs_runtime_argp)
+ return fshelp_set_options (netfs_runtime_argp, 0, argz, argz_len, 0);
+ else
+ return EOPNOTSUPP;
+}
diff --git a/libnetfs/shutdown.c b/libnetfs/shutdown.c
new file mode 100644
index 00000000..94d4b207
--- /dev/null
+++ b/libnetfs/shutdown.c
@@ -0,0 +1,106 @@
+/*
+ Copyright (C) 1993,94,95,96,98,99,2001 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include <errno.h>
+#include <sys/stat.h>
+#include <hurd/fsys.h>
+#include <hurd/fshelp.h>
+#include <pthread.h>
+
+/* Shutdown the filesystem; flags are as for fsys_goaway. */
+error_t
+netfs_shutdown (int flags)
+{
+ error_t
+ helper (struct node *node)
+ {
+ error_t err;
+ mach_port_t control;
+
+ err = fshelp_fetch_control (&node->transbox, &control);
+ if (!err && (control != MACH_PORT_NULL))
+ {
+ pthread_mutex_unlock (&node->lock);
+ err = fsys_goaway (control, flags);
+ mach_port_deallocate (mach_task_self (), control);
+ pthread_mutex_lock (&node->lock);
+ }
+ else
+ err = 0;
+
+ if ((err == MIG_SERVER_DIED) || (err == MACH_SEND_INVALID_DEST))
+ err = 0;
+
+ return err;
+ }
+
+ int nports;
+ int err;
+
+ if ((flags & FSYS_GOAWAY_UNLINK)
+ && S_ISDIR (netfs_root_node->nn_stat.st_mode))
+ return EBUSY;
+
+#ifdef NOTYET
+ if (flags & FSYS_GOAWAY_RECURSE)
+ {
+ err = netfs_node_iterate (helper);
+ if (err)
+ return err;
+ }
+#endif
+
+#ifdef NOTYET
+ pthread_rwlock_wrlock (&netfs_fsys_lock);
+#endif
+
+ /* Permit all current RPC's to finish, and then suspend any new ones. */
+ err = ports_inhibit_class_rpcs (netfs_protid_class);
+ if (err)
+ {
+#ifdef NOTYET
+ pthread_rwlock_unlock (&netfs_fsys_lock);
+#endif
+ return err;
+ }
+
+ nports = ports_count_class (netfs_protid_class);
+ if (((flags & FSYS_GOAWAY_FORCE) == 0) && nports)
+ /* There are outstanding user ports; resume operations. */
+ {
+ ports_enable_class (netfs_protid_class);
+ ports_resume_class_rpcs (netfs_protid_class);
+#ifdef NOTYET
+ pthread_rwlock_unlock (&netfs_fsys_lock);
+#endif
+ return EBUSY;
+ }
+
+ if (!(flags & FSYS_GOAWAY_NOSYNC))
+ {
+ err = netfs_attempt_syncfs (0, flags);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
diff --git a/libnetfs/startup-argp.c b/libnetfs/startup-argp.c
new file mode 100644
index 00000000..d922f698
--- /dev/null
+++ b/libnetfs/startup-argp.c
@@ -0,0 +1,49 @@
+/* Standard startup-time command line parser
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <argp.h>
+
+#include "netfs.h"
+
+static const struct argp_option
+startup_options[] =
+{
+ {0}
+};
+
+static error_t
+parse_startup_opt (int opt, char *arg, struct argp_state *state)
+{
+ switch (opt)
+ {
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static const struct argp
+startup_argp =
+ { startup_options, parse_startup_opt };
+
+const struct argp *netfs_startup_argp = &startup_argp;
diff --git a/libnetfs/std-runtime-argp.c b/libnetfs/std-runtime-argp.c
new file mode 100644
index 00000000..9e2ad1d6
--- /dev/null
+++ b/libnetfs/std-runtime-argp.c
@@ -0,0 +1,24 @@
+/* Parse standard run-time options
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <argp.h>
+#include "netfs.h"
+
+const struct argp netfs_std_runtime_argp = { 0 };
diff --git a/libnetfs/std-startup-argp.c b/libnetfs/std-startup-argp.c
new file mode 100644
index 00000000..3de84a57
--- /dev/null
+++ b/libnetfs/std-startup-argp.c
@@ -0,0 +1,27 @@
+/* Standard startup-time command line parser
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <argp.h>
+#include "netfs.h"
+
+const struct argp
+netfs_std_startup_argp = { 0 };
diff --git a/libnetfs/trans-callback.c b/libnetfs/trans-callback.c
new file mode 100644
index 00000000..f4f0c62b
--- /dev/null
+++ b/libnetfs/trans-callback.c
@@ -0,0 +1,86 @@
+/* Callback functions for starting translators
+
+ Copyright (C) 1995,96,97,2001,02 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <fcntl.h>
+
+/* Callback function needed for calls to fshelp_fetch_root. See
+ <hurd/fshelp.h> for the interface description. */
+static error_t
+_netfs_translator_callback1_fn (void *cookie1, void *cookie2,
+ uid_t *uid, gid_t *gid,
+ char **argz, size_t *argz_len)
+{
+ error_t err;
+ struct node *np = cookie1;
+
+ if (! (np->nn_stat.st_mode & S_IPTRANS))
+ return ENOENT;
+
+ err = netfs_get_translator (np, argz, argz_len);
+ if (err)
+ {
+ assert (err != EOPNOTSUPP);
+ return err;
+ }
+
+ *uid = np->nn_stat.st_uid;
+ *gid = np->nn_stat.st_gid;
+
+ return 0;
+}
+
+/* Callback function needed for calls to fshelp_fetch_root. See
+ <hurd/fshelp.h> for the interface description. */
+static error_t
+_netfs_translator_callback2_fn (void *cookie1, void *cookie2, int flags,
+ mach_port_t *underlying,
+ mach_msg_type_name_t *underlying_type)
+{
+ error_t err;
+ struct protid *cred;
+ struct node *node = cookie1;
+ struct iouser *user;
+
+ err = iohelp_create_simple_iouser (&user, node->nn_stat.st_uid,
+ node->nn_stat.st_gid);
+ if (err)
+ return err;
+
+ cred = netfs_make_protid (netfs_make_peropen (node, flags, cookie2),
+ user);
+ if (cred)
+ {
+ *underlying = ports_get_right (cred);
+ *underlying_type = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (cred);
+ return 0;
+ }
+ else
+ {
+ iohelp_free_iouser (user);
+ return errno;
+ }
+}
+
+fshelp_fetch_root_callback1_t _netfs_translator_callback1 =
+ _netfs_translator_callback1_fn;
+fshelp_fetch_root_callback2_t _netfs_translator_callback2 =
+ _netfs_translator_callback2_fn;
diff --git a/libpager/Makefile b/libpager/Makefile
new file mode 100644
index 00000000..b6222950
--- /dev/null
+++ b/libpager/Makefile
@@ -0,0 +1,37 @@
+# Copyright (C) 1994, 1995, 1996, 2012 Free Software Foundation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libpager
+makemode := library
+
+libname = libpager
+SRCS = data-request.c data-return.c data-unlock.c pager-port.c \
+ inhibit-term.c lock-completed.c lock-object.c mark-error.c \
+ no-senders.c object-init.c object-terminate.c pagemap.c \
+ pager-create.c pager-flush.c pager-shutdown.c pager-sync.c \
+ stubs.c seqnos.c demuxer.c chg-compl.c pager-attr.c clean.c \
+ dropweak.c notify-stubs.c get-upi.c pager-memcpy.c pager-return.c \
+ offer-page.c
+installhdrs = pager.h
+
+HURDLIBS= ports
+LDLIBS += -lpthread
+OBJS = $(SRCS:.c=.o) memory_objectServer.o notifyServer.o
+
+MIGSFLAGS = -DSEQNOS -imacros $(srcdir)/mig-mutate.h
+MIGCOMSFLAGS = -prefix _pager_
+
+include ../Makeconf
diff --git a/libpager/chg-compl.c b/libpager/chg-compl.c
new file mode 100644
index 00000000..d77c46cf
--- /dev/null
+++ b/libpager/chg-compl.c
@@ -0,0 +1,53 @@
+/* Completion of memory_object_change_attributes
+ Copyright (C) 1994, 1995 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <stdio.h>
+
+/* The kernel calls this (as described in <mach/memory_object.defs>)
+ when a memory_object_change_attributes call has completed. Read this
+ in combination with pager-attr.c. */
+kern_return_t
+_pager_seqnos_memory_object_change_completed (struct pager *p,
+ mach_port_seqno_t seq,
+ boolean_t maycache,
+ memory_object_copy_strategy_t strat)
+{
+ struct attribute_request *ar;
+
+ if (!p
+ || p->port.class != _pager_class)
+ {
+ printf ("Bad change completed\n");
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&p->interlock);
+ _pager_wait_for_seqno (p, seq);
+
+ for (ar = p->attribute_requests; ar; ar = ar->next)
+ if (ar->may_cache == maycache && ar->copy_strategy == strat)
+ {
+ if (ar->attrs_pending && !--ar->attrs_pending)
+ pthread_cond_broadcast (&p->wakeup);
+ break;
+ }
+
+ _pager_release_seqno (p, seq);
+ pthread_mutex_unlock (&p->interlock);
+ return 0;
+}
diff --git a/libpager/clean.c b/libpager/clean.c
new file mode 100644
index 00000000..031f7d1b
--- /dev/null
+++ b/libpager/clean.c
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+/* Called by port management routines when the last send-right
+ to a pager has gone away. This is a dual of pager_create. */
+void
+_pager_clean (void *arg)
+{
+ struct pager *p = arg;
+#ifdef KERNEL_INIT_RACE
+ struct pending_init *i, *tmp;
+#endif
+
+ if (p->pager_state != NOTINIT)
+ {
+ pthread_mutex_lock (&p->interlock);
+ _pager_free_structure (p);
+#ifdef KERNEL_INIT_RACE
+ for (i = p->init_head; i; i = tmp)
+ {
+ mach_port_deallocate (mach_task_self (), i->control);
+ mach_port_deallocate (mach_task_self (), i->name);
+ tmp = i->next;
+ free (i);
+ }
+#endif
+ pthread_mutex_unlock (&p->interlock);
+ }
+
+ pager_clear_user_data (p->upi);
+}
diff --git a/libpager/data-request.c b/libpager/data-request.c
new file mode 100644
index 00000000..82ce9041
--- /dev/null
+++ b/libpager/data-request.c
@@ -0,0 +1,145 @@
+/* Implementation of memory_object_data_request for pager library
+ Copyright (C) 1994,95,96,97,2000,02,10 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "memory_object_S.h"
+#include <stdio.h>
+#include <string.h>
+
+/* Implement pagein callback as described in <mach/memory_object.defs>. */
+kern_return_t
+_pager_seqnos_memory_object_data_request (struct pager *p,
+ mach_port_seqno_t seqno,
+ mach_port_t control,
+ vm_offset_t offset,
+ vm_size_t length,
+ vm_prot_t access)
+{
+ short *pm_entry;
+ int doread, doerror;
+ error_t err;
+ vm_address_t page;
+ int write_lock;
+
+ if (!p
+ || p->port.class != _pager_class)
+ return EOPNOTSUPP;
+
+ /* Acquire the right to meddle with the pagemap */
+ pthread_mutex_lock (&p->interlock);
+ _pager_wait_for_seqno (p, seqno);
+
+ /* sanity checks -- we don't do multi-page requests yet. */
+ if (control != p->memobjcntl)
+ {
+ printf ("incg data request: wrong control port\n");
+ goto release_out;
+ }
+ if (length != __vm_page_size)
+ {
+ printf ("incg data request: bad length size %zd\n", length);
+ goto release_out;
+ }
+ if (offset % __vm_page_size)
+ {
+ printf ("incg data request: misaligned request\n");
+ goto release_out;
+ }
+
+ _pager_block_termination (p); /* prevent termination until
+ mark_object_error is done */
+
+ if (p->pager_state != NORMAL)
+ {
+ printf ("pager in wrong state for read\n");
+ goto allow_release_out;
+ }
+
+ err = _pager_pagemap_resize (p, offset + length);
+ if (err)
+ goto allow_release_out; /* Can't do much about the actual error. */
+
+ /* If someone is paging this out right now, the disk contents are
+ unreliable, so we have to wait. It is too expensive (right now) to
+ find the data and return it, and then interrupt the write, so we just
+ mark the page and have the writing thread do m_o_data_supply when it
+ gets around to it. */
+ pm_entry = &p->pagemap[offset / __vm_page_size];
+ if (*pm_entry & PM_PAGINGOUT)
+ {
+ doread = 0;
+ *pm_entry |= PM_PAGEINWAIT;
+ }
+ else
+ doread = 1;
+
+ if (*pm_entry & PM_INVALID)
+ doerror = 1;
+ else
+ doerror = 0;
+
+ *pm_entry |= PM_INCORE;
+
+ if (PM_NEXTERROR (*pm_entry) != PAGE_NOERR && (access & VM_PROT_WRITE))
+ {
+ memory_object_data_error (control, offset, length,
+ _pager_page_errors[PM_NEXTERROR (*pm_entry)]);
+ _pager_mark_object_error (p, offset, length,
+ _pager_page_errors[PM_NEXTERROR (*pm_entry)]);
+ *pm_entry = SET_PM_NEXTERROR (*pm_entry, PAGE_NOERR);
+ doread = 0;
+ }
+
+ /* Let someone else in. */
+ _pager_release_seqno (p, seqno);
+ pthread_mutex_unlock (&p->interlock);
+
+ if (!doread)
+ goto allow_term_out;
+ if (doerror)
+ goto error_read;
+
+ err = pager_read_page (p->upi, offset, &page, &write_lock);
+ if (err)
+ goto error_read;
+
+ memory_object_data_supply (p->memobjcntl, offset, page, length, 1,
+ write_lock ? VM_PROT_WRITE : VM_PROT_NONE,
+ p->notify_on_evict ? 1 : 0,
+ MACH_PORT_NULL);
+ pthread_mutex_lock (&p->interlock);
+ _pager_mark_object_error (p, offset, length, 0);
+ _pager_allow_termination (p);
+ pthread_mutex_unlock (&p->interlock);
+ return 0;
+
+ error_read:
+ memory_object_data_error (p->memobjcntl, offset, length, EIO);
+ _pager_mark_object_error (p, offset, length, EIO);
+ allow_term_out:
+ pthread_mutex_lock (&p->interlock);
+ _pager_allow_termination (p);
+ pthread_mutex_unlock (&p->interlock);
+ return 0;
+
+ allow_release_out:
+ _pager_allow_termination (p);
+ release_out:
+ _pager_release_seqno (p, seqno);
+ pthread_mutex_unlock (&p->interlock);
+ return 0;
+}
diff --git a/libpager/data-return.c b/libpager/data-return.c
new file mode 100644
index 00000000..ee6c6e83
--- /dev/null
+++ b/libpager/data-return.c
@@ -0,0 +1,272 @@
+/* Implementation of memory_object_data_return for pager library
+ Copyright (C) 1994,95,96,99,2000,02 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "memory_object_S.h"
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+/* Worker function used by _pager_seqnos_memory_object_data_return
+ and _pager_seqnos_memory_object_data_initialize. All args are
+ as for _pager_seqnos_memory_object_data_return; the additional
+ INITIALIZING arg identifies which function is calling us. */
+kern_return_t
+_pager_do_write_request (struct pager *p,
+ mach_port_seqno_t seqno,
+ mach_port_t control,
+ vm_offset_t offset,
+ pointer_t data,
+ vm_size_t length,
+ int dirty,
+ int kcopy,
+ int initializing)
+{
+ short *pm_entries;
+ int npages, i;
+ char *notified;
+ error_t *pagerrs;
+ struct lock_request *lr;
+ struct lock_list {struct lock_request *lr;
+ struct lock_list *next;} *lock_list, *ll;
+ int wakeup;
+ int omitdata = 0;
+
+ if (!p
+ || p->port.class != _pager_class)
+ return EOPNOTSUPP;
+
+ /* Acquire the right to meddle with the pagemap */
+ pthread_mutex_lock (&p->interlock);
+ _pager_wait_for_seqno (p, seqno);
+
+ /* sanity checks -- we don't do multi-page requests yet. */
+ if (control != p->memobjcntl)
+ {
+ printf ("incg data return: wrong control port\n");
+ goto release_out;
+ }
+ if (length % __vm_page_size)
+ {
+ printf ("incg data return: bad length size %zd\n", length);
+ goto release_out;
+ }
+ if (offset % __vm_page_size)
+ {
+ printf ("incg data return: misaligned request\n");
+ goto release_out;
+ }
+
+ if (p->pager_state != NORMAL)
+ {
+ printf ("pager in wrong state for write\n");
+ goto release_out;
+ }
+
+ npages = length / __vm_page_size;
+ pagerrs = alloca (npages * sizeof (error_t));
+
+ notified = alloca (npages * (sizeof *notified));
+#ifndef NDEBUG
+ memset (notified, -1, npages * (sizeof *notified));
+#endif
+
+ _pager_block_termination (p); /* until we are done with the pagemap
+ when the write completes. */
+
+ _pager_pagemap_resize (p, offset + length);
+
+ pm_entries = &p->pagemap[offset / __vm_page_size];
+
+ if (! dirty)
+ {
+ munmap ((void *) data, length);
+ if (!kcopy) {
+ /* Prepare notified array. */
+ for (i = 0; i < npages; i++)
+ notified[i] = (p->notify_on_evict
+ && ! (pm_entries[i] & PM_PAGEINWAIT));
+
+ _pager_release_seqno (p, seqno);
+ goto notify;
+ }
+ else {
+ _pager_allow_termination (p);
+ goto release_out;
+ }
+ }
+
+ /* Make sure there are no other in-progress writes for any of these
+ pages before we begin. This imposes a little more serialization
+ than we really have to require (because *all* future writes on
+ this object are going to wait for seqno while we wait for the
+ previous write), but the case is relatively infrequent. */
+ retry:
+ for (i = 0; i < npages; i++)
+ if (pm_entries[i] & PM_PAGINGOUT)
+ {
+ pm_entries[i] |= PM_WRITEWAIT;
+ pthread_cond_wait (&p->wakeup, &p->interlock);
+ goto retry;
+ }
+
+ /* Mark these pages as being paged out. */
+ if (initializing)
+ {
+ assert (npages <= 32);
+ for (i = 0; i < npages; i++)
+ {
+ if (pm_entries[i] & PM_INIT)
+ omitdata |= 1 << i;
+ else
+ pm_entries[i] |= PM_PAGINGOUT | PM_INIT;
+ }
+ }
+ else
+ for (i = 0; i < npages; i++)
+ pm_entries[i] |= PM_PAGINGOUT | PM_INIT;
+
+ /* If this write occurs while a lock is pending, record
+ it. We have to keep this list because a lock request
+ might come in while we do the I/O; in that case there
+ would be a new entry on p->lock_requests and we must
+ make sure we don't decrement it. So we have to keep
+ track independently of which lock requests we incremented. */
+ lock_list = 0;
+ for (lr = p->lock_requests; lr; lr = lr->next)
+ if (offset < lr->end && offset + length >= lr->start)
+ {
+ ll = alloca (sizeof (struct lock_list));
+ ll->lr = lr;
+ ll->next = lock_list;
+ lock_list = ll;
+ lr->pending_writes++;
+ }
+
+ /* Let someone else in. */
+ _pager_release_seqno (p, seqno);
+ pthread_mutex_unlock (&p->interlock);
+
+ /* This is inefficient; we should send all the pages to the device at once
+ but until the pager library interface is changed, this will have to do. */
+
+ for (i = 0; i < npages; i++)
+ if (!(omitdata & (1 << i)))
+ pagerrs[i] = pager_write_page (p->upi,
+ offset + (vm_page_size * i),
+ data + (vm_page_size * i));
+
+ /* Acquire the right to meddle with the pagemap */
+ pthread_mutex_lock (&p->interlock);
+ _pager_pagemap_resize (p, offset + length);
+ pm_entries = &p->pagemap[offset / __vm_page_size];
+
+ wakeup = 0;
+ for (i = 0; i < npages; i++)
+ {
+ if (omitdata & (1 << i))
+ {
+ notified[i] = 0;
+ continue;
+ }
+
+ if (pm_entries[i] & PM_WRITEWAIT)
+ wakeup = 1;
+
+ if (pagerrs[i] && ! (pm_entries[i] & PM_PAGEINWAIT))
+ /* The only thing we can do here is mark the page, and give
+ errors from now on when it is to be read. This is
+ imperfect, because if all users go away, the pagemap will
+ be freed, and this information lost. Oh well. It's still
+ better than Un*x. Of course, if we are about to hand this
+ data to the kernel, the error isn't a problem, hence the
+ check for pageinwait. */
+ pm_entries[i] |= PM_INVALID;
+
+ if (pm_entries[i] & PM_PAGEINWAIT)
+ {
+ memory_object_data_supply (p->memobjcntl,
+ offset + (vm_page_size * i),
+ data + (vm_page_size * i),
+ vm_page_size, 1,
+ VM_PROT_NONE, 0, MACH_PORT_NULL);
+ notified[i] = 0;
+ }
+ else
+ {
+ munmap ((void *) (data + (vm_page_size * i)),
+ vm_page_size);
+ notified[i] = (! kcopy && p->notify_on_evict);
+ if (! kcopy)
+ pm_entries[i] &= ~PM_INCORE;
+ }
+
+ pm_entries[i] &= ~(PM_PAGINGOUT | PM_PAGEINWAIT | PM_WRITEWAIT);
+ }
+
+ for (ll = lock_list; ll; ll = ll->next)
+ if (!--ll->lr->pending_writes && !ll->lr->locks_pending)
+ wakeup = 1;
+
+ if (wakeup)
+ pthread_cond_broadcast (&p->wakeup);
+
+ notify:
+ _pager_allow_termination (p);
+ pthread_mutex_unlock (&p->interlock);
+
+ for (i = 0; i < npages; i++)
+ {
+ assert (notified[i] == 0 || notified[i] == 1);
+ if (notified[i])
+ {
+ short *pm_entry = &pm_entries[i];
+
+ /* Do notify user. */
+ pager_notify_evict (p->upi, offset + (i * vm_page_size));
+
+ /* Clear any error that is left. Notification on eviction
+ is used only to change association of page, so any
+ error may no longer be valid. */
+ pthread_mutex_lock (&p->interlock);
+ *pm_entry = SET_PM_ERROR (SET_PM_NEXTERROR (*pm_entry, 0), 0);
+ pthread_mutex_unlock (&p->interlock);
+ }
+ }
+
+ return 0;
+
+ release_out:
+ _pager_release_seqno (p, seqno);
+ pthread_mutex_unlock (&p->interlock);
+ return 0;
+}
+
+/* Implement pageout call back as described by <mach/memory_object.defs>. */
+kern_return_t
+_pager_seqnos_memory_object_data_return (struct pager *p,
+ mach_port_seqno_t seqno,
+ mach_port_t control,
+ vm_offset_t offset,
+ pointer_t data,
+ vm_size_t length,
+ int dirty,
+ int kcopy)
+{
+ return _pager_do_write_request (p, seqno, control, offset, data,
+ length, dirty, kcopy, 0);
+}
diff --git a/libpager/data-unlock.c b/libpager/data-unlock.c
new file mode 100644
index 00000000..599237c1
--- /dev/null
+++ b/libpager/data-unlock.c
@@ -0,0 +1,87 @@
+/* Implementation of memory_object_data_unlock for pager library
+ Copyright (C) 1994,95,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "memory_object_S.h"
+#include <stdio.h>
+
+/* Implement kernel requests for access as described in
+ <mach/memory_object.defs>. */
+kern_return_t
+_pager_seqnos_memory_object_data_unlock (struct pager *p,
+ mach_port_seqno_t seqno,
+ mach_port_t control,
+ vm_offset_t offset,
+ vm_size_t length,
+ vm_prot_t access)
+{
+ volatile int err;
+
+ if (!p
+ || p->port.class != _pager_class)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&p->interlock);
+ _pager_wait_for_seqno (p, seqno);
+ _pager_release_seqno (p, seqno);
+ pthread_mutex_unlock (&p->interlock);
+
+ if (p->pager_state != NORMAL)
+ {
+ printf ("pager in wrong state for unlock\n");
+ goto out;
+ }
+
+ if (control != p->memobjcntl)
+ {
+ printf ("incg data unlock: wrong control port\n");
+ goto out;
+ }
+ /* The only thing we ever block is writes */
+ if ((access & VM_PROT_WRITE) == 0)
+ {
+ printf ("incg data unlock: not unlock writes\n");
+ goto out;
+ }
+ if (offset % __vm_page_size)
+ {
+ printf ("incg data unlock: misaligned request\n");
+ goto out;
+ }
+ if (length != __vm_page_size)
+ {
+ printf ("incg data unlock: bad length size %zd\n", length);
+ goto out;
+ }
+
+ err = pager_unlock_page (p->upi, offset);
+
+ if (!err)
+ /* We can go ahead and release the lock. */
+ _pager_lock_object (p, offset, length, MEMORY_OBJECT_RETURN_NONE, 0,
+ VM_PROT_NONE, 0);
+ else
+ {
+ /* Flush the page, and set a bit so that m_o_data_request knows
+ to issue an error. */
+ _pager_lock_object (p, offset, length, MEMORY_OBJECT_RETURN_NONE, 1,
+ VM_PROT_WRITE, 1);
+ _pager_mark_next_request_error (p, offset, length, err);
+ }
+ out:
+ return 0;
+}
diff --git a/libpager/demuxer.c b/libpager/demuxer.c
new file mode 100644
index 00000000..b4d40541
--- /dev/null
+++ b/libpager/demuxer.c
@@ -0,0 +1,40 @@
+/* Demuxer for pager library
+ Copyright (C) 1994, 1995, 2002, 2011 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "memory_object_S.h"
+#include "notify_S.h"
+
+/* Demultiplex a single message directed at a pager port; INP is the
+ message received; fill OUTP with the reply. */
+int
+pager_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ mig_routine_t routine;
+ if ((routine = _pager_seqnos_memory_object_server_routine (inp)) ||
+ (routine = _pager_seqnos_notify_server_routine (inp)))
+ {
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+
+ /* Synchronize our bookkeeping of the port's seqno with the one
+ consumed by this bogus message. */
+ _pager_update_seqno (inp->msgh_local_port, inp->msgh_seqno);
+ return FALSE;
+}
diff --git a/libpager/dropweak.c b/libpager/dropweak.c
new file mode 100644
index 00000000..54f16b60
--- /dev/null
+++ b/libpager/dropweak.c
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+void
+_pager_real_dropweak (void *arg)
+{
+ struct pager *p = arg;
+
+ pager_dropweak (p->upi);
+}
diff --git a/libpager/get-upi.c b/libpager/get-upi.c
new file mode 100644
index 00000000..e9e92924
--- /dev/null
+++ b/libpager/get-upi.c
@@ -0,0 +1,27 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+struct user_pager_info *
+pager_get_upi (struct pager *p)
+{
+ return p->upi;
+}
diff --git a/libpager/inhibit-term.c b/libpager/inhibit-term.c
new file mode 100644
index 00000000..3f1e2ffe
--- /dev/null
+++ b/libpager/inhibit-term.c
@@ -0,0 +1,35 @@
+/* Locks to inhibit termination races for pager library
+ Copyright (C) 1994 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* Prevent this memory object from being terminated. */
+/* Must be called with interlock held. */
+void
+_pager_block_termination (struct pager *p)
+{
+ p->noterm++;
+}
+
+/* Allow termination again. */
+/* Must be called with interlock held. */
+void
+_pager_allow_termination (struct pager *p)
+{
+ if (!--p->noterm && p->termwaiting)
+ pthread_cond_broadcast (&p->wakeup);
+}
diff --git a/libpager/lock-completed.c b/libpager/lock-completed.c
new file mode 100644
index 00000000..a3f3f168
--- /dev/null
+++ b/libpager/lock-completed.c
@@ -0,0 +1,66 @@
+/* Implementation of memory_object_lock_completed for pager library
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "memory_object_S.h"
+#include <stdio.h>
+
+/* The kernel calls this (as described in <mach/memory_object.defs>)
+ when a memory_object_lock_request call has completed. Read this
+ in combination with lock-object.c. */
+kern_return_t
+_pager_seqnos_memory_object_lock_completed (struct pager *p,
+ mach_port_seqno_t seqno,
+ mach_port_t control,
+ vm_offset_t offset,
+ vm_size_t length)
+{
+ error_t err = 0;
+ struct lock_request *lr;
+
+ if (!p
+ || p->port.class != _pager_class)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&p->interlock);
+ _pager_wait_for_seqno (p, seqno);
+
+ if (control != p->memobjcntl)
+ {
+ printf ("lock_completed: bad control port\n");
+ err = EPERM;
+ goto out;
+ }
+
+ mach_port_deallocate (mach_task_self (), control);
+
+ for (lr = p->lock_requests; lr; lr = lr->next)
+ if (lr->start == offset && lr->end == offset + length)
+ {
+ if (lr->locks_pending)
+ --lr->locks_pending;
+ if (!lr->locks_pending && !lr->pending_writes)
+ pthread_cond_broadcast (&p->wakeup);
+ break;
+ }
+
+ out:
+ _pager_release_seqno (p, seqno);
+ pthread_mutex_unlock (&p->interlock);
+
+ return err;
+}
diff --git a/libpager/lock-object.c b/libpager/lock-object.c
new file mode 100644
index 00000000..c022d0c4
--- /dev/null
+++ b/libpager/lock-object.c
@@ -0,0 +1,107 @@
+/* Synchronous wrapper for memory_object_lock_request
+ Copyright (C) 1993, 1994, 1996, 1997, 2000 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* Request a lock from the kernel on pager P. Parameters OFFSET,
+ SIZE, SHOULD_RETURN, SHOULD_FLUSH, and LOCK_VALUE are as for
+ memory_object_lock_request. If SYNC is set, then wait for the
+ operation to fully complete before returning. */
+void
+_pager_lock_object (struct pager *p,
+ vm_offset_t offset,
+ vm_size_t size,
+ int should_return,
+ int should_flush,
+ vm_prot_t lock_value,
+ int sync)
+{
+ int i;
+ struct lock_request *lr = 0;
+
+ pthread_mutex_lock (&p->interlock);
+ if (p->pager_state != NORMAL)
+ {
+ pthread_mutex_unlock (&p->interlock);
+ return;
+ }
+
+ if (sync)
+ {
+ for (lr = p->lock_requests; lr; lr = lr->next)
+ if (lr->start == offset && lr->end == offset + size)
+ {
+ lr->locks_pending++;
+ lr->threads_waiting++;
+ break;
+ }
+ if (!lr)
+ {
+ lr = malloc (sizeof (struct lock_request));
+ lr->start = offset;
+ lr->end = offset + size;
+ lr->pending_writes = 0;
+ lr->locks_pending = 1;
+ lr->threads_waiting = 1;
+ lr->next = p->lock_requests;
+ if (lr->next)
+ lr->next->prevp = &lr->next;
+ lr->prevp = &p->lock_requests;
+ p->lock_requests = lr;
+ }
+ }
+
+ pthread_mutex_unlock (&p->interlock);
+ memory_object_lock_request (p->memobjcntl, offset, size, should_return,
+ should_flush, lock_value,
+ sync ? p->port.port_right : MACH_PORT_NULL);
+ pthread_mutex_lock (&p->interlock);
+
+ if (sync)
+ {
+ while (lr->locks_pending || lr->pending_writes)
+ pthread_cond_wait (&p->wakeup, &p->interlock);
+
+ if (! --lr->threads_waiting)
+ {
+ *lr->prevp = lr->next;
+ if (lr->next)
+ lr->next->prevp = lr->prevp;
+ free (lr);
+ }
+
+ if (should_flush)
+ {
+ vm_offset_t pm_offs = offset / __vm_page_size;
+
+ _pager_pagemap_resize (p, offset + size);
+ if (p->pagemapsize > pm_offs)
+ {
+ short *pm_entries = &p->pagemap[pm_offs];
+ vm_offset_t bound = size / vm_page_size;
+
+ if (bound > p->pagemapsize)
+ bound = p->pagemapsize;
+
+ for (i = 0; i < bound; i++)
+ pm_entries[i] &= ~PM_INCORE;
+ }
+ }
+ }
+
+ pthread_mutex_unlock (&p->interlock);
+}
diff --git a/libpager/mark-error.c b/libpager/mark-error.c
new file mode 100644
index 00000000..48ceb950
--- /dev/null
+++ b/libpager/mark-error.c
@@ -0,0 +1,122 @@
+/* Recording errors for pager library
+ Copyright (C) 1994, 1997 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+
+int _pager_page_errors[] = {KERN_SUCCESS, ENOSPC, EIO, EDQUOT};
+
+/* Some error has happened indicating that the page cannot be written.
+ (Usually this is ENOSPC or EDQOUT.) On the next pagein which
+ requests write access, return the error to the kernel. (This is
+ screwy because of the rules associated with m_o_lock_request.)
+ Currently the only errors permitted are ENOSPC, EIO, and EDQUOT. */
+void
+_pager_mark_next_request_error(struct pager *pager,
+ vm_address_t offset,
+ vm_size_t length,
+ error_t error)
+{
+ int page_error;
+ short *p;
+
+ offset /= __vm_page_size;
+ length /= __vm_page_size;
+
+ switch (error)
+ {
+ case 0:
+ page_error = PAGE_NOERR;
+ break;
+ case ENOSPC:
+ page_error = PAGE_ENOSPC;
+ break;
+ case EIO:
+ default:
+ page_error = PAGE_EIO;
+ break;
+ case EDQUOT:
+ page_error = PAGE_EDQUOT;
+ break;
+ }
+
+ for (p = pager->pagemap + offset; p < pager->pagemap + offset + length; p++)
+ *p = SET_PM_NEXTERROR (*p, page_error);
+}
+
+/* We are returning a pager error to the kernel. Write down
+ in the pager what that error was so that the exception handling
+ routines can find out. (This is only necessary because the
+ XP interface is not completely implemented in the kernel.)
+ Again, only ENOSPC, EIO, and EDQUOT are permitted. */
+void
+_pager_mark_object_error(struct pager *pager,
+ vm_address_t offset,
+ vm_size_t length,
+ error_t error)
+{
+ int page_error = 0;
+ short *p;
+
+ offset /= __vm_page_size;
+ length /= __vm_page_size;
+
+ switch (error)
+ {
+ case 0:
+ page_error = PAGE_NOERR;
+ break;
+ case ENOSPC:
+ page_error = PAGE_ENOSPC;
+ break;
+ case EIO:
+ default:
+ page_error = PAGE_EIO;
+ break;
+ case EDQUOT:
+ page_error = PAGE_EDQUOT;
+ break;
+ }
+
+ for (p = pager->pagemap + offset; p < pager->pagemap + offset + length; p++)
+ *p = SET_PM_ERROR (*p, page_error);
+}
+
+/* Tell us what the error (set with mark_object_error) for
+ pager P is on page ADDR. */
+error_t
+pager_get_error (struct pager *p, vm_address_t addr)
+{
+ error_t err;
+
+ pthread_mutex_lock (&p->interlock);
+
+ addr /= vm_page_size;
+
+ /* If there really is no error for ADDR, we should be able to exted the
+ pagemap table; otherwise, if some previous operation failed because it
+ couldn't extend the table, this attempt will *probably* (heh) fail for
+ the same reason. */
+ err = _pager_pagemap_resize (p, addr);
+
+ if (! err)
+ err = _pager_page_errors[PM_ERROR(p->pagemap[addr])];
+
+ pthread_mutex_unlock (&p->interlock);
+
+ return err;
+}
diff --git a/libpager/mig-decls.h b/libpager/mig-decls.h
new file mode 100644
index 00000000..0c7b402f
--- /dev/null
+++ b/libpager/mig-decls.h
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Justus Winter.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __LIBPAGER_MIG_DECLS_H__
+#define __LIBPAGER_MIG_DECLS_H__
+
+#include "priv.h"
+
+typedef struct pager *pager_t;
+
+/* Called by server stub functions. */
+
+static inline struct pager * __attribute__ ((unused))
+begin_using_pager (mach_port_t port)
+{
+ return ports_lookup_port (0, port, _pager_class);
+}
+
+static inline void __attribute__ ((unused))
+end_using_pager (struct pager *p)
+{
+ if (p)
+ ports_port_deref (p);
+}
+
+#endif /* __LIBPAGER_MIG_DECLS_H__ */
diff --git a/libpager/mig-mutate.h b/libpager/mig-mutate.h
new file mode 100644
index 00000000..9e9065fa
--- /dev/null
+++ b/libpager/mig-mutate.h
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Justus Winter.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#define MEMORY_OBJECT_INTRAN pager_t begin_using_pager (memory_object_t)
+#define MEMORY_OBJECT_DESTRUCTOR end_using_pager (pager_t)
+#define MEMORY_OBJECT_IMPORTS import "mig-decls.h";
+
+#define NOTIFY_INTRAN \
+ port_info_t begin_using_port_info_port (mach_port_t)
+#define NOTIFY_DESTRUCTOR \
+ end_using_port_info (port_info_t)
+#define NOTIFY_IMPORTS \
+ import "libports/mig-decls.h";
diff --git a/libpager/no-senders.c b/libpager/no-senders.c
new file mode 100644
index 00000000..c21dfc2f
--- /dev/null
+++ b/libpager/no-senders.c
@@ -0,0 +1,36 @@
+/* Called when a nosenders notification happens
+ Copyright (C) 1994, 1995 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "priv.h"
+#include <mach/notify.h>
+#include "notify_S.h"
+
+error_t
+_pager_do_seqnos_mach_notify_no_senders (struct port_info *pi,
+ mach_port_seqno_t seqno,
+ mach_port_mscount_t mscount)
+{
+ if (!pi ||
+ pi->class != _pager_class)
+ return EOPNOTSUPP;
+
+ _pager_update_seqno_p ((struct pager *) pi, seqno);
+ ports_no_senders (pi, mscount);
+
+ return 0;
+}
diff --git a/libpager/notify-stubs.c b/libpager/notify-stubs.c
new file mode 100644
index 00000000..ba138824
--- /dev/null
+++ b/libpager/notify-stubs.c
@@ -0,0 +1,76 @@
+/*
+ Copyright (C) 1995, 2011 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+#include "notify_S.h"
+#include <errno.h>
+
+error_t
+_pager_do_seqnos_mach_notify_port_deleted (struct port_info *pi,
+ mach_port_seqno_t seqno,
+ mach_port_t name
+ __attribute__ ((unused)))
+{
+ _pager_update_seqno_p ((struct pager *) pi, seqno);
+
+ return 0;
+}
+
+error_t
+_pager_do_seqnos_mach_notify_msg_accepted (struct port_info *pi,
+ mach_port_seqno_t seqno,
+ mach_port_t name
+ __attribute__ ((unused)))
+{
+ _pager_update_seqno_p ((struct pager *) pi, seqno);
+
+ return 0;
+}
+
+error_t
+_pager_do_seqnos_mach_notify_port_destroyed (struct port_info *pi,
+ mach_port_seqno_t seqno,
+ mach_port_t name
+ __attribute__ ((unused)))
+{
+ _pager_update_seqno_p ((struct pager *) pi, seqno);
+
+ return 0;
+}
+
+error_t
+_pager_do_seqnos_mach_notify_send_once (struct port_info *pi,
+ mach_port_seqno_t seqno)
+{
+ _pager_update_seqno_p ((struct pager *) pi, seqno);
+
+ return 0;
+}
+
+error_t
+_pager_do_seqnos_mach_notify_dead_name (struct port_info *pi,
+ mach_port_seqno_t seqno,
+ mach_port_t name
+ __attribute__ ((unused)))
+{
+ _pager_update_seqno_p ((struct pager *) pi, seqno);
+
+ return 0;
+}
diff --git a/libpager/object-init.c b/libpager/object-init.c
new file mode 100644
index 00000000..6683e24d
--- /dev/null
+++ b/libpager/object-init.c
@@ -0,0 +1,76 @@
+/* Implementation of memory_object_init for pager library
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "memory_object_S.h"
+#include <stdio.h>
+
+/* Implement the object initialiation call as described in
+ <mach/memory_object.defs>. */
+kern_return_t
+_pager_seqnos_memory_object_init (struct pager *p,
+ mach_port_seqno_t seqno,
+ mach_port_t control,
+ mach_port_t name,
+ vm_size_t pagesize)
+{
+ if (!p
+ || p->port.class != _pager_class)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&p->interlock);
+ _pager_wait_for_seqno (p, seqno);
+
+ if (pagesize != __vm_page_size)
+ {
+ printf ("incg init: bad page size");
+ goto out;
+ }
+
+ if (p->pager_state != NOTINIT)
+ {
+#ifdef KERNEL_INIT_RACE
+ struct pending_init *i = malloc (sizeof (struct pending_init));
+ printf ("pager out-of-sequence init\n");
+ i->control = control;
+ i->name = name;
+
+ i->next = 0;
+ if (p->init_tail)
+ p->init_tail->next = i;
+ else
+ p->init_head = i;
+ p->init_tail = i;
+#else
+ printf ("pager dup init\n");
+#endif
+ goto out;
+ }
+
+ p->memobjcntl = control;
+ p->memobjname = name;
+
+ memory_object_ready (control, p->may_cache, p->copy_strategy);
+
+ p->pager_state = NORMAL;
+
+ out:
+ _pager_release_seqno (p, seqno);
+ pthread_mutex_unlock (&p->interlock);
+
+ return 0;
+}
diff --git a/libpager/object-terminate.c b/libpager/object-terminate.c
new file mode 100644
index 00000000..365ba275
--- /dev/null
+++ b/libpager/object-terminate.c
@@ -0,0 +1,130 @@
+/* Implementation of memory_object_terminate for pager library
+ Copyright (C) 1994, 1995, 1996, 1999, 2000 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "memory_object_S.h"
+#include <stdio.h>
+
+/* Implement the object termination call from the kernel as described
+ in <mach/memory_object.defs>. */
+kern_return_t
+_pager_seqnos_memory_object_terminate (struct pager *p,
+ mach_port_seqno_t seqno,
+ mach_port_t control,
+ mach_port_t name)
+{
+ if (!p
+ || p->port.class != _pager_class)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&p->interlock);
+ _pager_wait_for_seqno (p, seqno);
+
+ if (control != p->memobjcntl)
+ {
+ printf ("incg terminate: wrong control port");
+ goto out;
+ }
+ if (name != p->memobjname)
+ {
+ printf ("incg terminate: wrong name port");
+ goto out;
+ }
+
+ while (p->noterm)
+ {
+ p->termwaiting = 1;
+ pthread_cond_wait (&p->wakeup, &p->interlock);
+ }
+
+ /* Destry the ports we received; mark that in P so that it doesn't bother
+ doing it again. */
+ mach_port_destroy (mach_task_self (), control);
+ mach_port_destroy (mach_task_self (), name);
+ p->memobjcntl = p->memobjname = MACH_PORT_NULL;
+
+ _pager_free_structure (p);
+
+#ifdef KERNEL_INIT_RACE
+ if (p->init_head)
+ {
+ struct pending_init *i = p->init_head;
+ p->init_head = i->next;
+ if (!i->next)
+ p->init_tail = 0;
+ p->memobjcntl = i->control;
+ p->memobjname = i->name;
+ memory_object_ready (i->control, p->may_cache, p->copy_strategy);
+ p->pager_state = NORMAL;
+ free (i);
+ }
+#endif
+
+ out:
+ _pager_release_seqno (p, seqno);
+ pthread_mutex_unlock (&p->interlock);
+
+ return 0;
+}
+
+/* Shared code for termination from memory_object_terminate and
+ no-senders. The pager must be locked. This routine will
+ deallocate all the ports and memory that pager P references. */
+void
+_pager_free_structure (struct pager *p)
+{
+ int wakeup;
+ struct lock_request *lr;
+ struct attribute_request *ar;
+
+ wakeup = 0;
+ for (lr = p->lock_requests; lr; lr = lr->next)
+ {
+ lr->locks_pending = 0;
+ if (!lr->pending_writes)
+ wakeup = 1;
+ }
+ for (ar = p->attribute_requests; ar; ar = ar->next)
+ {
+ ar->attrs_pending = 0;
+ wakeup = 1;
+ }
+
+ if (wakeup)
+ pthread_cond_broadcast (&p->wakeup);
+
+ if (p->memobjcntl != MACH_PORT_NULL)
+ {
+ mach_port_deallocate (mach_task_self (), p->memobjcntl);
+ p->memobjcntl = MACH_PORT_NULL;
+ }
+ if (p->memobjname != MACH_PORT_NULL)
+ {
+ mach_port_deallocate (mach_task_self (), p->memobjname);
+ p->memobjname = MACH_PORT_NULL;
+ }
+
+ /* Free the pagemap */
+ if (p->pagemapsize)
+ {
+ munmap (p->pagemap, p->pagemapsize * sizeof (* p->pagemap));
+ p->pagemapsize = 0;
+ p->pagemap = 0;
+ }
+
+ p->pager_state = NOTINIT;
+}
diff --git a/libpager/offer-page.c b/libpager/offer-page.c
new file mode 100644
index 00000000..9f090bcb
--- /dev/null
+++ b/libpager/offer-page.c
@@ -0,0 +1,51 @@
+/* Wrapper for unsolicited memory_object_data_supply
+ Copyright (C) 1996, 1997, 2000 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include "priv.h"
+
+void
+pager_offer_page (struct pager *p,
+ int precious,
+ int writelock,
+ vm_offset_t offset,
+ vm_address_t buf)
+{
+ pthread_mutex_lock (&p->interlock);
+
+ if (_pager_pagemap_resize (p, offset + vm_page_size))
+ {
+ short *pm_entry = &p->pagemap[offset / vm_page_size];
+
+ while (*pm_entry & PM_INCORE)
+ {
+ pthread_mutex_unlock (&p->interlock);
+ pager_flush_some (p, offset, vm_page_size, 1);
+ pthread_mutex_lock (&p->interlock);
+ }
+ *pm_entry |= PM_INCORE;
+
+ memory_object_data_supply (p->memobjcntl, offset, buf, vm_page_size, 0,
+ writelock ? VM_PROT_WRITE : VM_PROT_NONE,
+ precious, MACH_PORT_NULL);
+ }
+
+ pthread_mutex_unlock (&p->interlock);
+}
diff --git a/libpager/pagemap.c b/libpager/pagemap.c
new file mode 100644
index 00000000..b8b3362c
--- /dev/null
+++ b/libpager/pagemap.c
@@ -0,0 +1,47 @@
+/* Pagemap manipulation for pager library
+ Copyright (C) 1994, 1997, 1999, 2000 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <string.h>
+
+/* Grow the pagemap of pager P as necessary to deal with address OFF */
+error_t
+_pager_pagemap_resize (struct pager *p, vm_address_t off)
+{
+ error_t err = 0;
+
+ off /= __vm_page_size;
+
+ if (p->pagemapsize < off)
+ {
+ void *newaddr;
+ int newsize = round_page (off);
+
+ newaddr = mmap (0, newsize * sizeof (*p->pagemap),
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ err = (newaddr == (void *) -1) ? errno : 0;
+ if (! err)
+ {
+ bcopy (p->pagemap, newaddr, p->pagemapsize * sizeof (*p->pagemap));
+ munmap (p->pagemap, p->pagemapsize * sizeof (*p->pagemap));
+ p->pagemap = newaddr;
+ p->pagemapsize = newsize;
+ }
+ }
+
+ return err;
+}
diff --git a/libpager/pager-attr.c b/libpager/pager-attr.c
new file mode 100644
index 00000000..ad1560ea
--- /dev/null
+++ b/libpager/pager-attr.c
@@ -0,0 +1,101 @@
+/* Changing pager attributes synchronously
+ Copyright (C) 1994, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <assert.h>
+
+/* Change the attributes of the memory object underlying pager P.
+ Arguments MAY_CACHE and COPY_STRATEGY are as for
+ memory_object_change_atributes. Wait for the kernel to report
+ completion if WAIT is set. */
+void
+pager_change_attributes (struct pager *p,
+ boolean_t may_cache,
+ memory_object_copy_strategy_t copy_strategy,
+ int wait)
+{
+ struct attribute_request *ar = 0;
+
+ pthread_mutex_lock (&p->interlock);
+
+ /* If there's nothing to do we might be able to return. However,
+ if the user asked us to wait, and there are pending changes,
+ then we have to do the work anyway because we must follow the
+ pending change. */
+ if (p->may_cache == may_cache && p->copy_strategy == copy_strategy
+ && ! (p->attribute_requests && wait))
+ {
+ pthread_mutex_unlock (&p->interlock);
+ return;
+ }
+
+ p->may_cache = may_cache;
+ p->copy_strategy = copy_strategy;
+
+ if (p->pager_state == NOTINIT)
+ {
+ pthread_mutex_unlock (&p->interlock);
+ return;
+ }
+
+ if (wait)
+ {
+ for (ar = p->attribute_requests; ar; ar = ar->next)
+ if (ar->may_cache == may_cache
+ && ar->copy_strategy == copy_strategy)
+ {
+ ar->attrs_pending++;
+ ar->threads_waiting++;
+ break;
+ }
+ if (!ar)
+ {
+ ar = malloc (sizeof (struct attribute_request));
+ ar->may_cache = may_cache;
+ ar->copy_strategy = copy_strategy;
+ ar->attrs_pending = 1;
+ ar->threads_waiting = 1;
+ ar->next = p->attribute_requests;
+ if (ar->next)
+ ar->next->prevp = &ar->next;
+ ar->prevp = &p->attribute_requests;
+ p->attribute_requests = ar;
+ }
+ }
+
+ pthread_mutex_unlock (&p->interlock);
+ memory_object_change_attributes (p->memobjcntl, may_cache, copy_strategy,
+ wait ? p->port.port_right : MACH_PORT_NULL);
+
+ if (wait)
+ {
+ pthread_mutex_lock (&p->interlock);
+
+ while (ar->attrs_pending)
+ pthread_cond_wait (&p->wakeup, &p->interlock);
+
+ if (! --ar->threads_waiting)
+ {
+ *ar->prevp = ar->next;
+ if (ar->next)
+ ar->next->prevp = ar->prevp;
+ free (ar);
+ }
+
+ pthread_mutex_unlock (&p->interlock);
+ }
+}
diff --git a/libpager/pager-create.c b/libpager/pager-create.c
new file mode 100644
index 00000000..1fc15b83
--- /dev/null
+++ b/libpager/pager-create.c
@@ -0,0 +1,64 @@
+/* Pager creation
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* Create and return a new pager with user info UPI. */
+struct pager *
+pager_create (struct user_pager_info *upi,
+ struct port_bucket *bucket,
+ boolean_t may_cache,
+ memory_object_copy_strategy_t copy_strategy,
+ boolean_t notify_on_evict)
+{
+ struct pager *p;
+
+ errno = ports_create_port (_pager_class, bucket, sizeof (struct pager), &p);
+ if (errno)
+ return 0;
+
+ p->upi = upi;
+ p->pager_state = NOTINIT;
+ pthread_mutex_init (&p->interlock, NULL);
+ pthread_cond_init (&p->wakeup, NULL);
+ p->lock_requests = 0;
+ p->attribute_requests = 0;
+ p->may_cache = may_cache;
+ p->copy_strategy = copy_strategy;
+ p->notify_on_evict = notify_on_evict;
+ p->memobjcntl = MACH_PORT_NULL;
+ p->memobjname = MACH_PORT_NULL;
+ p->seqno = -1;
+ p->noterm = 0;
+ p->termwaiting = 0;
+ p->waitingforseqno = 0;
+ p->pagemap = 0;
+ p->pagemapsize = 0;
+
+ return p;
+}
+
+
+/* This causes the function to be run at startup by compiler magic. */
+static void create_class (void) __attribute__ ((constructor));
+
+static void
+create_class ()
+{
+ _pager_class = ports_create_class (_pager_clean, _pager_real_dropweak);
+ (void) &create_class; /* Avoid warning */
+}
diff --git a/libpager/pager-flush.c b/libpager/pager-flush.c
new file mode 100644
index 00000000..bd7697ca
--- /dev/null
+++ b/libpager/pager-flush.c
@@ -0,0 +1,44 @@
+/* Functions for flushing data
+ Copyright (C) 1994, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* Have the kernel flush all pages in the pager; if WAIT is set, then
+ wait for them to be finally expunged before returning. */
+void
+pager_flush (struct pager *p, int wait)
+{
+ vm_address_t offset;
+ vm_size_t len;
+
+ pager_report_extent (p->upi, &offset, &len);
+
+ _pager_lock_object (p, offset, len, MEMORY_OBJECT_RETURN_NONE, 1,
+ VM_PROT_NO_CHANGE, wait);
+}
+
+
+/* Have the kernel write back some pages of a pager from OFFSET to
+ OFFSET+SIZE; if WAIT is set, then wait for them to be finally
+ written before returning. */
+void
+pager_flush_some (struct pager *p, vm_address_t offset,
+ vm_size_t size, int wait)
+{
+ _pager_lock_object (p, offset, size, MEMORY_OBJECT_RETURN_NONE, 1,
+ VM_PROT_NO_CHANGE, wait);
+}
diff --git a/libpager/pager-memcpy.c b/libpager/pager-memcpy.c
new file mode 100644
index 00000000..f2be5585
--- /dev/null
+++ b/libpager/pager-memcpy.c
@@ -0,0 +1,218 @@
+/* Fault-safe copy into or out of pager-backed memory.
+ Copyright (C) 1996,97,99, 2000,01,02 Free Software Foundation, Inc.
+ Written by Roland McGrath.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "pager.h"
+#include <sys/mman.h>
+#include <hurd/sigpreempt.h>
+#include <assert.h>
+#include <string.h>
+
+/* Start using vm_copy over memcpy when we have that many page. This is
+ roughly the L1 cache size. (This value *cannot* be less than
+ vm_page_size.) */
+#define VMCOPY_BETTER_THAN_MEMCPY (8*vm_page_size)
+
+/* Try to copy *SIZE bytes between the region OTHER points to
+ and the region at OFFSET in the pager indicated by PAGER and MEMOBJ.
+ If PROT is VM_PROT_READ, copying is from the pager to OTHER;
+ if PROT contains VM_PROT_WRITE, copying is from OTHER into the pager.
+ *SIZE is always filled in the actual number of bytes successfully copied.
+ Returns an error code if the pager-backed memory faults;
+ if there is no fault, returns 0 and *SIZE will be unchanged. */
+error_t
+pager_memcpy (struct pager *pager, memory_object_t memobj,
+ vm_offset_t offset, void *other, size_t *size,
+ vm_prot_t prot)
+{
+ error_t err;
+ size_t n = *size;
+
+#define VMCOPY_WINDOW_DEFAULT_SIZE (32 * vm_page_size)
+#define MEMCPY_WINDOW_DEFAULT_SIZE (32 * vm_page_size)
+ vm_address_t window;
+ vm_size_t window_size;
+
+ error_t do_vm_copy (void)
+ {
+ assert ((offset & (vm_page_size - 1)) == 0);
+ assert (((vm_address_t) other & (vm_page_size - 1)) == 0);
+ assert (n >= vm_page_size);
+
+ do
+ {
+ window_size =
+ VMCOPY_WINDOW_DEFAULT_SIZE > n
+ ? (n - (n & (vm_page_size - 1)))
+ : VMCOPY_WINDOW_DEFAULT_SIZE;
+
+ assert (window_size >= VMCOPY_BETTER_THAN_MEMCPY);
+ assert ((window_size & (vm_page_size - 1)) == 0);
+
+ err = vm_map (mach_task_self (), &window, window_size, 0, 1,
+ memobj, offset, 0, prot, prot, VM_INHERIT_NONE);
+ if (err)
+ return err;
+
+ if (prot == VM_PROT_READ)
+ err = vm_copy (mach_task_self (), window, window_size,
+ (vm_address_t) other);
+ else
+ err = vm_copy (mach_task_self (), (vm_address_t) other,
+ window_size, window);
+
+ vm_deallocate (mach_task_self (), window, window_size);
+
+ if (err)
+ return err;
+
+ other += window_size;
+ offset += window_size;
+ n -= window_size;
+ }
+ while (n >= VMCOPY_BETTER_THAN_MEMCPY);
+
+ return 0;
+ }
+
+ error_t do_copy (struct hurd_signal_preemptor *preemptor)
+ {
+ error_t do_memcpy (size_t to_copy)
+ {
+ window_size = MEMCPY_WINDOW_DEFAULT_SIZE;
+
+ do
+ {
+ size_t pageoff = offset & (vm_page_size - 1);
+ size_t copy_count = window_size - pageoff;
+
+ /* Map in and copy a standard-sized window, unless that is
+ more than the total left to be copied. */
+
+ if (window_size >= round_page (pageoff + to_copy))
+ {
+ copy_count = to_copy;
+ window_size = round_page (pageoff + to_copy);
+ }
+
+ err = vm_map (mach_task_self (), &window, window_size, 0, 1,
+ memobj, offset - pageoff, 0,
+ prot, prot, VM_INHERIT_NONE);
+ if (err)
+ return err;
+
+ /* Realign the fault preemptor for the new mapping window. */
+ preemptor->first = window;
+ preemptor->last = window + window_size;
+
+ if (prot == VM_PROT_READ)
+ memcpy (other, (const void *) window + pageoff, copy_count);
+ else
+ memcpy ((void *) window + pageoff, other, copy_count);
+
+ vm_deallocate (mach_task_self (), window, window_size);
+
+ offset += copy_count;
+ other += copy_count;
+ to_copy -= copy_count;
+ n -= copy_count;
+
+ assert (n >= 0);
+ assert (to_copy >= 0);
+ }
+ while (to_copy > 0);
+
+ return 0;
+ }
+
+ /* Can we use vm_copy? */
+ if ((((vm_address_t) other & (vm_page_size - 1))
+ == (offset & (vm_page_size - 1)))
+ && (n >= (VMCOPY_BETTER_THAN_MEMCPY + vm_page_size
+ - ((vm_address_t) other & (vm_page_size - 1)))))
+ /* 1) other and offset are aligned with repect to each other;
+ and 2) we have at least VMCOPY_BETTER_THAN_MEMCPY fully
+ aligned pages. */
+ {
+ err = do_memcpy (vm_page_size
+ - ((vm_address_t) other & (vm_page_size - 1)));
+ if (err)
+ return err;
+
+ assert (n >= VMCOPY_BETTER_THAN_MEMCPY);
+
+ err = do_vm_copy ();
+ if (err || n == 0)
+ /* We failed or we finished. */
+ return err;
+
+ assert (n < VMCOPY_BETTER_THAN_MEMCPY);
+ }
+
+ return do_memcpy (n);
+ }
+
+ jmp_buf buf;
+ void fault (int signo, long int sigcode, struct sigcontext *scp)
+ {
+ assert (scp->sc_error == EKERN_MEMORY_ERROR);
+ err = pager_get_error (pager, sigcode - window + offset);
+ n -= sigcode - window;
+ vm_deallocate (mach_task_self (), window, window_size);
+ longjmp (buf, 1);
+ }
+
+ if (n == 0)
+ /* Nothing to do. */
+ return 0;
+
+ if (((vm_address_t) other & (vm_page_size - 1)) == 0
+ && (offset & (vm_page_size - 1)) == 0
+ && n >= VMCOPY_BETTER_THAN_MEMCPY)
+ /* 1) the start address is page aligned; 2) the offset is page
+ aligned; and 3) we have more than VMCOPY_BETTER_THAN_MEMCPY
+ pages. */
+ {
+ err = do_vm_copy ();
+ if (err || n == 0)
+ /* We failed or we finished. */
+ {
+ *size -= n;
+ return err;
+ }
+
+ assert (n < VMCOPY_BETTER_THAN_MEMCPY);
+ }
+
+ /* Need to do it the hard way. */
+
+ window = 0;
+ window_size = 0;
+
+ if (setjmp (buf) == 0)
+ hurd_catch_signal (sigmask (SIGSEGV) | sigmask (SIGBUS),
+ window, window + window_size,
+ &do_copy, (sighandler_t) &fault);
+
+ if (! err)
+ assert (n == 0);
+
+ *size -= n;
+
+ return err;
+}
diff --git a/libpager/pager-port.c b/libpager/pager-port.c
new file mode 100644
index 00000000..e5d10457
--- /dev/null
+++ b/libpager/pager-port.c
@@ -0,0 +1,26 @@
+/* Return the port corresponding to a pager.
+ Copyright (C) 1993, 1994 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* Return the port (receive right) corresponding to a pager. It is
+ essential that a new send right be created from this port. */
+mach_port_t
+pager_get_port (struct pager *p)
+{
+ return ports_get_right (p);
+}
diff --git a/libpager/pager-return.c b/libpager/pager-return.c
new file mode 100644
index 00000000..7563d449
--- /dev/null
+++ b/libpager/pager-return.c
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+
+/* Have all dirty pages written back, and also flush the contents of
+ the kernel's cache. */
+void
+pager_return (struct pager *p, int wait)
+{
+ vm_address_t offset;
+ vm_size_t len;
+
+ pager_report_extent (p->upi, &offset, &len);
+
+ _pager_lock_object (p, offset, len, MEMORY_OBJECT_RETURN_ALL, 1,
+ VM_PROT_NO_CHANGE, wait);
+}
+
+void
+pager_return_some (struct pager *p, vm_address_t offset,
+ vm_size_t size, int wait)
+{
+ _pager_lock_object (p, offset, size, MEMORY_OBJECT_RETURN_ALL, 1,
+ VM_PROT_NO_CHANGE, wait);
+}
diff --git a/libpager/pager-shutdown.c b/libpager/pager-shutdown.c
new file mode 100644
index 00000000..c1aacfeb
--- /dev/null
+++ b/libpager/pager-shutdown.c
@@ -0,0 +1,32 @@
+/* Pager shutdown in pager library
+ Copyright (C) 1994, 1995 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <mach/notify.h>
+
+/* Shutdown pager P and prevent any future paging activity on it. */
+void
+pager_shutdown (struct pager *p)
+{
+ /* Sync and flush pager */
+ pager_sync (p, 1);
+ pager_flush (p, 1);
+ pthread_mutex_lock (&p->interlock);
+ p->pager_state = SHUTDOWN;
+ ports_destroy_right (p);
+ pthread_mutex_unlock (&p->interlock);
+}
diff --git a/libpager/pager-sync.c b/libpager/pager-sync.c
new file mode 100644
index 00000000..ec024b6d
--- /dev/null
+++ b/libpager/pager-sync.c
@@ -0,0 +1,44 @@
+/* Functions for sync.
+ Copyright (C) 1994, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* Have the kernel write back all dirty pages in the pager; if
+ WAIT is set, then wait for them to be finally written before
+ returning. */
+void
+pager_sync (struct pager *p, int wait)
+{
+ vm_address_t offset;
+ vm_size_t len;
+
+ pager_report_extent (p->upi, &offset, &len);
+
+ _pager_lock_object (p, offset, len, MEMORY_OBJECT_RETURN_ALL, 0,
+ VM_PROT_NO_CHANGE, wait);
+}
+
+
+/* Have the kernel write back some pages of a pager; if WAIT is set,
+ then wait for them to be finally written before returning. */
+void
+pager_sync_some (struct pager *p, vm_address_t offset,
+ vm_size_t size, int wait)
+{
+ _pager_lock_object (p, offset, size, MEMORY_OBJECT_RETURN_ALL, 0,
+ VM_PROT_NO_CHANGE, wait);
+}
diff --git a/libpager/pager.h b/libpager/pager.h
new file mode 100644
index 00000000..d0572af1
--- /dev/null
+++ b/libpager/pager.h
@@ -0,0 +1,210 @@
+/* Definitions for multi-threaded pager library
+ Copyright (C) 1994, 1995, 1996, 1997, 1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#ifndef _HURD_PAGER_
+#define _HURD_PAGER_
+
+#include <hurd/ports.h>
+
+/* This declaration exists to place struct user_pager_info in the proper
+ scope. */
+struct user_pager_info;
+
+/* This de-muxer function is for use within libports_demuxer. */
+/* INP is a message we've received; OUTP will be filled in with
+ a reply message. */
+int pager_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp);
+
+/* Create a new pager. The pager will have a port created for it
+ (using libports, in BUCKET) and will be immediately ready
+ to receive requests. U_PAGER will be provided to later calls to
+ pager_find_address. The pager will have one user reference
+ created. MAY_CACHE and COPY_STRATEGY are the original values of
+ those attributes as for memory_object_ready. If NOTIFY_ON_EVICT is
+ non-zero, pager_notify_evict user callback will be called when page
+ is evicted. Users may create references to pagers by use of the
+ relevant ports library functions. On errors, return null and set
+ errno. */
+struct pager *
+pager_create (struct user_pager_info *u_pager,
+ struct port_bucket *bucket,
+ boolean_t may_cache,
+ memory_object_copy_strategy_t copy_strategy,
+ boolean_t notify_on_evict);
+
+/* Return the user_pager_info struct associated with a pager. */
+struct user_pager_info *
+pager_get_upi (struct pager *p);
+
+/* Sync data from pager PAGER to backing store; wait for
+ all the writes to complete iff WAIT is set. */
+void
+pager_sync (struct pager *pager,
+ int wait);
+
+/* Sync some data (starting at START, for LEN bytes) from pager PAGER
+ to backing store. Wait for all the writes to complete iff WAIT is
+ set. */
+void
+pager_sync_some (struct pager *pager,
+ vm_address_t start,
+ vm_size_t len,
+ int wait);
+
+/* Flush data from the kernel for pager PAGER and force any pending
+ delayed copies. Wait for all pages to be flushed iff WAIT is set. */
+void
+pager_flush (struct pager *pager,
+ int wait);
+
+
+/* Flush some data (starting at START, for LEN bytes) for pager PAGER
+ from the kernel. Wait for all pages to be flushed iff WAIT is set. */
+void
+pager_flush_some (struct pager *pager,
+ vm_address_t start,
+ vm_size_t len,
+ int wait);
+
+/* Flush data from the kernel for pager PAGER and force any pending
+ delayed copies. Wait for all pages to be flushed iff WAIT is set.
+ Have the kernel write back modifications. */
+void
+pager_return (struct pager *pager,
+ int wait);
+
+
+/* Flush some data (starting at START, for LEN bytes) for pager PAGER
+ from the kernel. Wait for all pages to be flushed iff WAIT is set.
+ Have the kernel write back modifications. */
+void
+pager_return_some (struct pager *pager,
+ vm_address_t start,
+ vm_size_t len,
+ int wait);
+
+/* Offer a page of data to the kernel. If PRECIOUS is set, then this
+ page will be paged out at some future point, otherwise it might be
+ dropped by the kernel. If the page is currently in core, the
+ kernel might ignore this call. */
+void
+pager_offer_page (struct pager *pager,
+ int precious,
+ int writelock,
+ vm_offset_t page,
+ vm_address_t buf);
+
+/* Change the attributes of the memory object underlying pager PAGER.
+ Arguments MAY_CACHE and COPY_STRATEGY are as for
+ memory_object_change_atributes. Wait for the kernel to report
+ completion if WAIT is set. */
+void
+pager_change_attributes (struct pager *pager,
+ boolean_t may_cache,
+ memory_object_copy_strategy_t copy_strategy,
+ int wait);
+
+/* Return the port (receive right) for requests to the pager. It is
+ absolutely necessary that a new send right be created from this
+ receive right. */
+mach_port_t
+pager_get_port (struct pager *pager);
+
+/* Force termination of a pager. After this returns, no
+ more paging requests on the pager will be honored, and the
+ pager will be deallocated. (The actual deallocation might
+ occur asynchronously if there are currently outstanding paging
+ requests that will complete first.) */
+void
+pager_shutdown (struct pager *pager);
+
+/* Return the error code of the last page error for pager P at address ADDR;
+ this will be deleted when the kernel interface is fixed. */
+error_t
+pager_get_error (struct pager *p, vm_address_t addr);
+
+/* Try to copy *SIZE bytes between the region OTHER points to
+ and the region at OFFSET in the pager indicated by PAGER and MEMOBJ.
+ If PROT is VM_PROT_READ, copying is from the pager to OTHER;
+ if PROT contains VM_PROT_WRITE, copying is from OTHER into the pager.
+ *SIZE is always filled in the actual number of bytes successfully copied.
+ Returns an error code if the pager-backed memory faults;
+ if there is no fault, returns 0 and *SIZE will be unchanged. */
+error_t
+pager_memcpy (struct pager *pager, memory_object_t memobj,
+ vm_offset_t offset, void *other, size_t *size,
+ vm_prot_t prot);
+
+/* The user must define this function. For pager PAGER, read one
+ page from offset PAGE. Set *BUF to be the address of the page,
+ and set *WRITE_LOCK if the page must be provided read-only.
+ The only permissible error returns are EIO, EDQUOT, and ENOSPC. */
+error_t
+pager_read_page (struct user_pager_info *pager,
+ vm_offset_t page,
+ vm_address_t *buf,
+ int *write_lock);
+
+/* The user must define this function. For pager PAGER, synchronously
+ write one page from BUF to offset PAGE. In addition, mfree
+ (or equivalent) BUF. The only permissible error returns are EIO,
+ EDQUOT, and ENOSPC. */
+error_t
+pager_write_page (struct user_pager_info *pager,
+ vm_offset_t page,
+ vm_address_t buf);
+
+/* The user must define this function. A page should be made writable. */
+error_t
+pager_unlock_page (struct user_pager_info *pager,
+ vm_offset_t address);
+
+/* The user must define this function. It is used when you want be
+ able to change association of pages to backing store. To use it,
+ pass non-zero value in NOTIFY_ON_EVICT when pager is created with
+ pager_create. You can change association of page only when
+ pager_notify_evict has been called and you haven't touched page
+ content after that. Note there is a possibility that a page is
+ evicted, but user is not notified about that. The user should be
+ able to handle this case. */
+void
+pager_notify_evict (struct user_pager_info *pager,
+ vm_offset_t page);
+
+/* The user must define this function. It should report back (in
+ *OFFSET and *SIZE the minimum valid address the pager will accept
+ and the size of the object. */
+error_t
+pager_report_extent (struct user_pager_info *pager,
+ vm_address_t *offset,
+ vm_size_t *size);
+
+/* The user must define this function. This is called when a pager is
+ being deallocated after all extant send rights have been destroyed. */
+void
+pager_clear_user_data (struct user_pager_info *pager);
+
+/* The use must define this function. This will be called when the ports
+ library wants to drop weak references. The pager library creates no
+ weak references itself. If the user doesn't either, then it's OK for
+ this function to do nothing. */
+void
+pager_dropweak (struct user_pager_info *p);
+
+#endif
diff --git a/libpager/priv.h b/libpager/priv.h
new file mode 100644
index 00000000..1f8405af
--- /dev/null
+++ b/libpager/priv.h
@@ -0,0 +1,153 @@
+/* Private data for pager library.
+ Copyright (C) 1994-1997, 1999, 2000, 2011 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _LIBPAGER_PRIV_H
+#define _LIBPAGER_PRIV_H
+
+#include <mach.h>
+#include <hurd.h>
+#include <sys/mman.h>
+#include <pthread.h>
+#include "pager.h"
+#include <hurd/ports.h>
+
+/* Define this if you think the kernel is sending memory_object_init
+ out of sequence with memory_object_terminate. */
+/* #undef KERNEL_INIT_RACE */
+
+struct pager
+{
+ struct port_info port;
+ struct user_pager_info *upi;
+
+ enum
+ {
+ NOTINIT, /* before memory_object_init */
+ NORMAL, /* while running */
+ SHUTDOWN, /* ignore all further requests */
+ } pager_state;
+
+ pthread_mutex_t interlock;
+ pthread_cond_t wakeup;
+
+ struct lock_request *lock_requests; /* pending lock requests */
+ struct attribute_request *attribute_requests; /* pending attr requests */
+
+ boolean_t may_cache;
+ memory_object_copy_strategy_t copy_strategy;
+ boolean_t notify_on_evict;
+
+ /* Interface ports */
+ memory_object_control_t memobjcntl;
+ memory_object_name_t memobjname;
+
+ mach_port_seqno_t seqno;
+
+ int noterm; /* number of threads blocking termination */
+
+ int termwaiting:1;
+ int waitingforseqno:1;
+
+#ifdef KERNEL_INIT_RACE
+ /* Out of sequence object_init calls waiting for
+ terminations. */
+ struct pending_init *init_head, *init_tail;
+#endif
+
+ short *pagemap;
+ int pagemapsize; /* number of elements in PAGEMAP */
+};
+
+struct lock_request
+{
+ struct lock_request *next, **prevp;
+ vm_address_t start, end;
+ int pending_writes;
+ int locks_pending;
+ int threads_waiting;
+};
+
+struct attribute_request
+{
+ struct attribute_request *next, **prevp;
+ boolean_t may_cache;
+ memory_object_copy_strategy_t copy_strategy;
+ int threads_waiting;
+ int attrs_pending;
+};
+
+#ifdef KERNEL_INIT_RACE
+struct pending_init
+{
+ mach_port_t control;
+ mach_port_t name;
+ struct pending_init *next;
+};
+#endif
+
+enum page_errors
+{
+ PAGE_NOERR,
+ PAGE_ENOSPC,
+ PAGE_EIO,
+ PAGE_EDQUOT,
+};
+
+extern int _pager_page_errors[];
+
+/* Pagemap format */
+/* These are binary state bits */
+#define PM_WRITEWAIT 0x0200 /* queue wakeup once write is done */
+#define PM_INIT 0x0100 /* data has been written */
+#define PM_INCORE 0x0080 /* kernel might have a copy */
+#define PM_PAGINGOUT 0x0040 /* being written to disk */
+#define PM_PAGEINWAIT 0x0020 /* provide data back when write done */
+#define PM_INVALID 0x0010 /* data on disk is irrevocably wrong */
+
+/* These take values of enum page_errors */
+
+/* Doesn't belong here; this is the error that should have been passed
+ through m_o_data_error to the user but isn't; this lets internal use
+ of the pager know what the error is. */
+#define PM_ERROR(byte) (((byte) & 0xc) >> 2)
+#define SET_PM_ERROR(byte,err) (((byte) & ~0xc) | ((err) << 2))
+
+/* Issue this error on next data_request, but only if it asks for
+ write access. */
+#define PM_NEXTERROR(byte) ((byte) & 0x3)
+#define SET_PM_NEXTERROR(byte,err) (((byte) & ~0x3) | (err))
+
+struct port_class *_pager_class;
+
+
+void _pager_wait_for_seqno (struct pager *, mach_port_seqno_t);
+void _pager_release_seqno (struct pager *, mach_port_seqno_t);
+void _pager_update_seqno (mach_port_t, mach_port_seqno_t);
+void _pager_update_seqno_p (struct pager *, mach_port_seqno_t);
+void _pager_block_termination (struct pager *);
+void _pager_allow_termination (struct pager *);
+error_t _pager_pagemap_resize (struct pager *, vm_address_t);
+void _pager_mark_next_request_error (struct pager *, vm_address_t,
+ vm_size_t, error_t);
+void _pager_mark_object_error (struct pager *, vm_address_t,
+ vm_size_t, error_t);
+void _pager_lock_object (struct pager *, vm_offset_t, vm_size_t, int, int,
+ vm_prot_t, int);
+void _pager_free_structure (struct pager *);
+void _pager_clean (void *arg);
+void _pager_real_dropweak (void *arg);
+#endif
diff --git a/libpager/seqnos.c b/libpager/seqnos.c
new file mode 100644
index 00000000..cab2f33d
--- /dev/null
+++ b/libpager/seqnos.c
@@ -0,0 +1,79 @@
+/* Sequence number synchronization routines for pager library
+ Copyright (C) 1994, 2011 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <assert.h>
+
+/* The message with seqno SEQNO has just been dequeued for pager P;
+ wait until all preceding messages have had a chance and then
+ return. */
+void
+_pager_wait_for_seqno (struct pager *p,
+ mach_port_seqno_t seqno)
+{
+ while (seqno != p->seqno + 1)
+ {
+ p->waitingforseqno = 1;
+ pthread_cond_wait (&p->wakeup, &p->interlock);
+ }
+}
+
+
+/* Allow the next message for pager P (potentially blocked in
+ _pager_wait_for_seqno) to be handled. */
+void
+_pager_release_seqno (struct pager *p,
+ mach_port_seqno_t seqno)
+{
+ assert (seqno == p->seqno + 1);
+ p->seqno = seqno;
+ if (p->waitingforseqno)
+ {
+ p->waitingforseqno = 0;
+ pthread_cond_broadcast (&p->wakeup);
+ }
+}
+
+
+/* Just update the seqno. */
+void
+_pager_update_seqno (mach_port_t object,
+ mach_port_seqno_t seqno)
+{
+ struct pager *p;
+
+ p = ports_lookup_port (0, object, _pager_class);
+ _pager_update_seqno_p (p, seqno);
+ if (p)
+ ports_port_deref (p);
+}
+
+
+/* Just update the seqno, pointer version. */
+void
+_pager_update_seqno_p (struct pager *p,
+ mach_port_seqno_t seqno)
+{
+ if (p
+ && p->port.class == _pager_class)
+ {
+ pthread_mutex_lock (&p->interlock);
+ _pager_wait_for_seqno (p, seqno);
+ _pager_release_seqno (p, seqno);
+ pthread_mutex_unlock (&p->interlock);
+ }
+}
diff --git a/libpager/stubs.c b/libpager/stubs.c
new file mode 100644
index 00000000..411f4839
--- /dev/null
+++ b/libpager/stubs.c
@@ -0,0 +1,67 @@
+ /* Unused memory object interface stubs
+ Copyright (C) 1994, 2011 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include "priv.h"
+#include "memory_object_S.h"
+#include <stdio.h>
+
+kern_return_t
+_pager_seqnos_memory_object_copy (struct pager *p,
+ mach_port_seqno_t seq,
+ memory_object_control_t obj_ctl,
+ vm_offset_t off,
+ vm_size_t len,
+ mach_port_t new)
+{
+ printf ("m_o_copy called\n");
+
+ _pager_update_seqno_p (p, seq);
+
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+_pager_seqnos_memory_object_data_write (struct pager *p,
+ mach_port_seqno_t seq,
+ mach_port_t ctl,
+ vm_offset_t off,
+ pointer_t data,
+ vm_size_t data_cnt)
+{
+ printf ("m_o_data_write called\n");
+
+ _pager_update_seqno_p (p, seq);
+
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+_pager_seqnos_memory_object_supply_completed (struct pager *p,
+ mach_port_seqno_t seq,
+ mach_port_t ctl,
+ vm_offset_t off,
+ vm_size_t len,
+ kern_return_t result,
+ vm_offset_t err_off)
+{
+ printf ("m_o_supply_completed called\n");
+
+ _pager_update_seqno_p (p, seq);
+
+ return EOPNOTSUPP;
+}
diff --git a/libpipe/Makefile b/libpipe/Makefile
new file mode 100644
index 00000000..27a0d2d2
--- /dev/null
+++ b/libpipe/Makefile
@@ -0,0 +1,31 @@
+# Makefile for libpipe
+#
+# Copyright (C) 1995, 1996, 2012 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libpipe
+makemode := library
+
+libname = libpipe
+installhdrs = pipe.h pq.h
+
+SRCS = pq.c dgram.c pipe.c stream.c seqpack.c addr.c pq-funcs.c pipe-funcs.c
+
+OBJS = $(SRCS:.c=.o)
+HURDLIBS= ports
+LDLIBS += -lpthread
+
+include ../Makeconf
diff --git a/libpipe/addr.c b/libpipe/addr.c
new file mode 100644
index 00000000..3af3ef8f
--- /dev/null
+++ b/libpipe/addr.c
@@ -0,0 +1,29 @@
+/* Default address deallocate function
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd/ports.h>
+
+/* This routine may be provided by the user, in which case, it should be a
+ function taking a non-NULL source address and deallocating it. It
+ defaults to calling ports_port_deref. */
+void pipe_dealloc_addr (void *addr)
+{
+ ports_port_deref (addr);
+}
diff --git a/libpipe/dgram.c b/libpipe/dgram.c
new file mode 100644
index 00000000..30695f1e
--- /dev/null
+++ b/libpipe/dgram.c
@@ -0,0 +1,59 @@
+/* The SOCK_DGRAM pipe class
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/socket.h> /* For SOCK_DGRAM */
+
+#include "pipe.h"
+#include "pq.h"
+
+/* See the definition of struct pipe_class in "pipe.h" for documentation. */
+
+static error_t
+dgram_write (struct pq *pq, void *source,
+ char *data, size_t data_len, size_t *amount)
+{
+ struct packet *packet = pq_queue (pq, PACKET_TYPE_DATA, source);
+ if (!packet)
+ return ENOBUFS;
+ else
+ return packet_write (packet, data, data_len, amount);
+}
+
+static error_t
+dgram_read (struct packet *packet, int *dequeue, unsigned *flags,
+ char **data, size_t *data_len, size_t amount)
+{
+ if (flags && *flags & MSG_PEEK)
+ {
+ *dequeue = 0;
+ return packet_peek (packet, data, data_len, amount);
+ }
+ else
+ {
+ *dequeue = 1;
+ return packet_read (packet, data, data_len, amount);
+ }
+}
+
+struct pipe_class _dgram_pipe_class =
+{
+ SOCK_DGRAM, PIPE_CLASS_CONNECTIONLESS, dgram_read, dgram_write
+};
+struct pipe_class *dgram_pipe_class = &_dgram_pipe_class;
diff --git a/libpipe/pipe-funcs.c b/libpipe/pipe-funcs.c
new file mode 100644
index 00000000..79cda2a4
--- /dev/null
+++ b/libpipe/pipe-funcs.c
@@ -0,0 +1,2 @@
+#define PIPE_DEFINE_EI
+#include "pipe.h"
diff --git a/libpipe/pipe.c b/libpipe/pipe.c
new file mode 100644
index 00000000..f9300e7c
--- /dev/null
+++ b/libpipe/pipe.c
@@ -0,0 +1,486 @@
+/* Generic one-way pipes
+
+ Copyright (C) 1995, 1998 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h> /* For bzero() */
+#include <assert.h>
+#include <stdlib.h>
+
+#include <mach/time_value.h>
+#include <mach/mach_host.h>
+
+#include <hurd/hurd_types.h>
+
+#include "pipe.h"
+
+static inline void
+timestamp (time_value_t *stamp)
+{
+ host_get_time (mach_host_self (), stamp);
+}
+
+/* Hold this lock before attempting to lock multiple pipes. */
+pthread_mutex_t pipe_multiple_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* ---------------------------------------------------------------- */
+
+#define pipe_is_connless(p) ((p)->class->flags & PIPE_CLASS_CONNECTIONLESS)
+
+/* Creates a new pipe of class CLASS and returns it in RESULT. */
+error_t
+pipe_create (struct pipe_class *class, struct pipe **pipe)
+{
+ struct pipe *new = malloc (sizeof (struct pipe));
+
+ if (new == NULL)
+ return ENOMEM;
+
+ new->readers = 0;
+ new->writers = 0;
+ new->flags = 0;
+ new->class = class;
+ new->write_limit = 16*1024;
+ new->write_atomic = 16*1024;
+
+ bzero (&new->read_time, sizeof (new->read_time));
+ bzero (&new->write_time, sizeof (new->write_time));
+
+ pthread_cond_init (&new->pending_reads, NULL);
+ pthread_cond_init (&new->pending_read_selects, NULL);
+ pthread_cond_init (&new->pending_writes, NULL);
+ pthread_cond_init (&new->pending_write_selects, NULL);
+ new->pending_selects = NULL;
+ pthread_mutex_init (&new->lock, NULL);
+
+ pq_create (&new->queue);
+
+ if (! pipe_is_connless (new))
+ new->flags |= PIPE_BROKEN;
+
+ *pipe = new;
+ return 0;
+}
+
+/* Free PIPE and any resources it holds. */
+void
+pipe_free (struct pipe *pipe)
+{
+ pq_free (pipe->queue);
+ free (pipe);
+}
+
+static void
+pipe_add_select_cond (struct pipe *pipe, struct pipe_select_cond *cond)
+{
+ struct pipe_select_cond *first, *last;
+
+ first = pipe->pending_selects;
+
+ if (first == NULL)
+ {
+ cond->next = cond;
+ cond->prev = cond;
+ pipe->pending_selects = cond;
+ return;
+ }
+
+ last = first->prev;
+ cond->next = first;
+ cond->prev = last;
+ first->prev = cond;
+ last->next = cond;
+}
+
+static void
+pipe_remove_select_cond (struct pipe *pipe, struct pipe_select_cond *cond)
+{
+ cond->prev->next = cond->next;
+ cond->next->prev = cond->prev;
+
+ if (pipe->pending_selects == cond)
+ {
+ if (cond->next == cond)
+ pipe->pending_selects = NULL;
+ else
+ pipe->pending_selects = cond->next;
+ }
+}
+
+static void
+pipe_select_cond_broadcast (struct pipe *pipe)
+{
+ struct pipe_select_cond *cond, *last;
+
+ cond = pipe->pending_selects;
+
+ if (cond == NULL)
+ return;
+
+ last = cond->prev;
+
+ do
+ {
+ pthread_cond_broadcast (&cond->cond);
+ cond = cond->next;
+ }
+ while (cond != last);
+}
+
+/* Take any actions necessary when PIPE acquires its first writer. */
+void _pipe_first_writer (struct pipe *pipe)
+{
+ if (pipe->readers > 0)
+ pipe->flags &= ~PIPE_BROKEN;
+}
+
+/* Take any actions necessary when PIPE acquires its first reader. */
+void _pipe_first_reader (struct pipe *pipe)
+{
+ if (pipe->writers > 0)
+ pipe->flags &= ~PIPE_BROKEN;
+}
+
+/* Take any actions necessary when PIPE's last reader has gone away. PIPE
+ should be locked. */
+void _pipe_no_readers (struct pipe *pipe)
+{
+ if (pipe->writers == 0)
+ pipe_free (pipe);
+ else
+ {
+ if (! pipe_is_connless (pipe))
+ {
+ pipe->flags |= PIPE_BROKEN;
+ if (pipe->writers)
+ /* Wake up writers for the bad news... */
+ {
+ pthread_cond_broadcast (&pipe->pending_writes);
+ pthread_cond_broadcast (&pipe->pending_write_selects);
+ pipe_select_cond_broadcast (pipe);
+ }
+ }
+ pthread_mutex_unlock (&pipe->lock);
+ }
+}
+
+/* Take any actions necessary when PIPE's last writer has gone away. PIPE
+ should be locked. */
+void _pipe_no_writers (struct pipe *pipe)
+{
+ if (pipe->readers == 0)
+ pipe_free (pipe);
+ else
+ {
+ if (! pipe_is_connless (pipe))
+ {
+ pipe->flags |= PIPE_BROKEN;
+ if (pipe->readers)
+ /* Wake up readers for the bad news... */
+ {
+ pthread_cond_broadcast (&pipe->pending_reads);
+ pthread_cond_broadcast (&pipe->pending_read_selects);
+ pipe_select_cond_broadcast (pipe);
+ }
+ }
+ pthread_mutex_unlock (&pipe->lock);
+ }
+}
+
+/* Return when either RPIPE is available for reading (if SELECT_READ is set
+ in *SELECT_TYPE), or WPIPE is available for writing (if select_write is
+ set in *SELECT_TYPE). *SELECT_TYPE is modified to reflect which (or both)
+ is now available. DATA_ONLY should be true if only data packets should be
+ waited for on RPIPE. Neither RPIPE or WPIPE should be locked when calling
+ this function (unlike most pipe functions). */
+error_t
+pipe_pair_select (struct pipe *rpipe, struct pipe *wpipe,
+ struct timespec *tsp, int *select_type, int data_only)
+{
+ error_t err = 0;
+
+ *select_type &= SELECT_READ | SELECT_WRITE;
+
+ if (*select_type == SELECT_READ)
+ {
+ pthread_mutex_lock (&rpipe->lock);
+ err = pipe_select_readable (rpipe, tsp, data_only);
+ pthread_mutex_unlock (&rpipe->lock);
+ }
+ else if (*select_type == SELECT_WRITE)
+ {
+ pthread_mutex_lock (&wpipe->lock);
+ err = pipe_select_writable (wpipe, tsp);
+ pthread_mutex_unlock (&wpipe->lock);
+ }
+ else
+ /* ugh */
+ {
+ int rpipe_blocked, wpipe_blocked;
+ struct pipe_select_cond pending_select;
+ size_t wlimit = wpipe->write_limit;
+ pthread_mutex_t *lock =
+ (wpipe == rpipe ? &rpipe->lock : &pipe_multiple_lock);
+
+ pthread_cond_init (&pending_select.cond, NULL);
+
+ pthread_mutex_lock (lock);
+ if (rpipe == wpipe)
+ pipe_add_select_cond (rpipe, &pending_select);
+ else
+ {
+ pthread_mutex_lock (&rpipe->lock);
+ pthread_mutex_lock (&wpipe->lock);
+ pipe_add_select_cond (rpipe, &pending_select);
+ pipe_add_select_cond (wpipe, &pending_select);
+ }
+
+ rpipe_blocked =
+ ! ((rpipe->flags & PIPE_BROKEN) || pipe_is_readable (rpipe, data_only));
+ wpipe_blocked =
+ ! ((wpipe->flags & PIPE_BROKEN) || pipe_readable (wpipe, 1) < wlimit);
+ while (!err && rpipe_blocked && wpipe_blocked)
+ {
+ if (rpipe != wpipe)
+ {
+ pthread_mutex_unlock (&rpipe->lock);
+ pthread_mutex_unlock (&wpipe->lock);
+ }
+ err = pthread_hurd_cond_timedwait_np (&pending_select.cond, lock,
+ tsp);
+ if (rpipe != wpipe)
+ {
+ pthread_mutex_lock (&rpipe->lock);
+ pthread_mutex_lock (&wpipe->lock);
+ }
+ rpipe_blocked =
+ ! ((rpipe->flags & PIPE_BROKEN)
+ || pipe_is_readable (rpipe, data_only));
+ wpipe_blocked =
+ ! ((wpipe->flags & PIPE_BROKEN)
+ || pipe_readable (wpipe, 1) < wlimit);
+ }
+
+ if (!err)
+ {
+ if (rpipe_blocked)
+ *select_type &= ~SELECT_READ;
+ if (wpipe_blocked)
+ *select_type &= ~SELECT_WRITE;
+ }
+
+ if (rpipe == wpipe)
+ pipe_remove_select_cond (rpipe, &pending_select);
+ else
+ {
+ pipe_remove_select_cond (rpipe, &pending_select);
+ pipe_remove_select_cond (wpipe, &pending_select);
+ pthread_mutex_unlock (&rpipe->lock);
+ pthread_mutex_unlock (&wpipe->lock);
+ }
+ pthread_mutex_unlock (lock);
+ }
+
+ if (err == ETIMEDOUT)
+ {
+ err = 0;
+ *select_type = 0;
+ }
+
+ return err;
+}
+
+/* Writes up to LEN bytes of DATA, to PIPE, which should be locked, and
+ returns the amount written in AMOUNT. If present, the information in
+ CONTROL & PORTS is written in a preceding control packet. If an error is
+ returned, nothing is done. */
+error_t
+pipe_send (struct pipe *pipe, int noblock, void *source,
+ char *data, size_t data_len,
+ char *control, size_t control_len,
+ mach_port_t *ports, size_t num_ports,
+ size_t *amount)
+{
+ error_t err = 0;
+
+ err = pipe_wait_writable (pipe, noblock);
+ if (err)
+ return err;
+
+ if (noblock)
+ {
+ size_t left = pipe->write_limit - pipe_readable (pipe, 1);
+ if (left < data_len)
+ {
+ if (data_len <= pipe->write_atomic)
+ return EWOULDBLOCK;
+ else
+ data_len = left;
+ }
+ }
+
+ if (control_len > 0 || num_ports > 0)
+ /* Write a control packet. */
+ {
+ /* Note that we don't record the source address in control packets, as
+ it's recorded in the following data packet anyway, and this prevents
+ it from being dealloc'd twice; this depends on the fact that we
+ always write a data packet. */
+ struct packet *control_packet =
+ pq_queue (pipe->queue, PACKET_TYPE_CONTROL, NULL);
+
+ if (control_packet == NULL)
+ err = ENOBUFS;
+ else
+ {
+ err = packet_write (control_packet, control, control_len, NULL);
+ if (!err)
+ err = packet_set_ports (control_packet, ports, num_ports);
+ if (err)
+ {
+ /* Trash CONTROL_PACKET somehow XXX */
+ }
+ }
+ }
+
+ if (!err)
+ err = (*pipe->class->write)(pipe->queue, source, data, data_len, amount);
+
+ if (!err)
+ {
+ timestamp (&pipe->write_time);
+
+ /* And wakeup anyone that might be interested in it. */
+ pthread_cond_broadcast (&pipe->pending_reads);
+ pthread_mutex_unlock (&pipe->lock);
+
+ pthread_mutex_lock (&pipe->lock); /* Get back the lock on PIPE. */
+ /* Only wakeup selects if there's still data available. */
+ if (pipe_is_readable (pipe, 0))
+ {
+ pthread_cond_broadcast (&pipe->pending_read_selects);
+ pipe_select_cond_broadcast (pipe);
+ /* We leave PIPE locked here, assuming the caller will soon unlock
+ it and allow others access. */
+ }
+ }
+
+ return err;
+}
+
+/* Reads up to AMOUNT bytes from PIPE, which should be locked, into DATA, and
+ returns the amount read in DATA_LEN. If NOBLOCK is true, EWOULDBLOCK is
+ returned instead of block when no data is immediately available. If an
+ error is returned, nothing is done. If source isn't NULL, the address of
+ the socket from which the data was sent is returned in it; this may be
+ NULL if it wasn't specified by the sender (which is usually the case with
+ connection-oriented protcols).
+
+ If there is control data waiting (before any data), then the behavior
+ depends on whether this is an `ordinary read' (when CONTROL and PORTS are
+ both NULL), in which case any control data is skipped, or a `msg read', in
+ which case the contents of the first control packet is returned (in
+ CONTROL and PORTS), as well as the first data packet following that (if
+ the control packet is followed by another control packet or no packet in
+ this case, a zero length data buffer is returned; the user should be
+ careful to distinguish this case from EOF (when no control or ports data
+ is returned either). */
+error_t
+pipe_recv (struct pipe *pipe, int noblock, unsigned *flags, void **source,
+ char **data, size_t *data_len, size_t amount,
+ char **control, size_t *control_len,
+ mach_port_t **ports, size_t *num_ports)
+{
+ error_t err;
+ struct packet *packet;
+ struct pq *pq = pipe->queue;
+ /* True if the user isn't asking for any `control' data. */
+ int data_only = (control == NULL && ports == NULL);
+
+ err = pipe_wait_readable (pipe, noblock, data_only);
+ if (err)
+ return err;
+
+ packet = pq_head (pq, PACKET_TYPE_ANY, 0);
+
+ if (data_only)
+ /* The user doesn't want to know about control info, so skip any... */
+ while (packet && packet->type == PACKET_TYPE_CONTROL)
+ packet = pq_next (pq, PACKET_TYPE_ANY, 0);
+ else if (packet && packet->type == PACKET_TYPE_CONTROL)
+ /* Read this control packet first, before looking for a data packet. */
+ {
+ if (control != NULL)
+ packet_read (packet, control, control_len, packet_readable (packet));
+ if (ports != NULL)
+ /* Copy out the port rights being sent. */
+ packet_read_ports (packet, ports, num_ports);
+
+ packet = pq_next (pq, PACKET_TYPE_DATA, NULL);
+ assert (packet); /* pipe_write always writes a data packet. */
+ }
+ else
+ /* No control data... */
+ {
+ if (control_len)
+ *control_len = 0;
+ if (num_ports)
+ *num_ports = 0;
+ }
+
+ if (!err)
+ {
+ if (packet)
+ /* Read some data (PACKET must be a data packet at this point). */
+ {
+ int dq = 1; /* True if we should dequeue this packet. */
+
+ if (source)
+ packet_read_source (packet, source);
+
+ err = (*pipe->class->read)(packet, &dq, flags,
+ data, data_len, amount);
+ if (dq)
+ pq_dequeue (pq);
+ }
+ else
+ /* Return EOF. */
+ *data_len = 0;
+ }
+
+ if (!err && packet)
+ {
+ timestamp (&pipe->read_time);
+
+ /* And wakeup anyone that might be interested in it. */
+ pthread_cond_broadcast (&pipe->pending_writes);
+ pthread_mutex_unlock (&pipe->lock);
+
+ pthread_mutex_lock (&pipe->lock); /* Get back the lock on PIPE. */
+ /* Only wakeup selects if there's still writing space available. */
+ if (pipe_readable (pipe, 1) < pipe->write_limit)
+ {
+ pthread_cond_broadcast (&pipe->pending_write_selects);
+ pipe_select_cond_broadcast (pipe);
+ /* We leave PIPE locked here, assuming the caller will soon unlock
+ it and allow others access. */
+ }
+ }
+
+ return err;
+}
diff --git a/libpipe/pipe.h b/libpipe/pipe.h
new file mode 100644
index 00000000..040204d5
--- /dev/null
+++ b/libpipe/pipe.h
@@ -0,0 +1,444 @@
+/* Generic one-way pipes
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __PIPE_H__
+#define __PIPE_H__
+
+#define EWOULDBLOCK EAGAIN /* XXX */
+
+#include <pthread.h> /* For conditions & mutexes */
+#include <features.h>
+
+#ifdef PIPE_DEFINE_EI
+#define PIPE_EI
+#else
+#define PIPE_EI __extern_inline
+#endif
+
+#include "pq.h"
+
+
+/* A description of a class of pipes and how to operate on them. */
+struct pipe_class
+{
+ /* What sort of socket this corresponds too. */
+ int sock_type;
+
+ /* Flags, from PIPE_CLASS_*, below. */
+ unsigned flags;
+
+ /* Operations: */
+ /* Read from PACKET into DATA &c, and set *DEQUEUE to true if PACKET should
+ be subsequently discarded. */
+ error_t (*read)(struct packet *packet, int *dequeue, unsigned *flags,
+ char **data, size_t *data_len, size_t amount);
+ /* Write DATA &c into the packet queue PQ. */
+ error_t (*write)(struct pq *pq, void *source,
+ char *data, size_t data_len, size_t *amount);
+};
+
+/* pipe_class flags */
+#define PIPE_CLASS_CONNECTIONLESS 0x1 /* A non-stream protocol. */
+
+/* Some pre-defined pipe_classes. */
+extern struct pipe_class *stream_pipe_class;
+extern struct pipe_class *dgram_pipe_class;
+extern struct pipe_class *seqpack_pipe_class;
+
+struct pipe_select_cond
+{
+ struct pipe_select_cond *next;
+ struct pipe_select_cond *prev;
+ pthread_cond_t cond;
+};
+
+/* A unidirectional data pipe; it transfers data from READER to WRITER. */
+struct pipe
+{
+ /* What kind of pipe we are. */
+ struct pipe_class *class;
+
+ /* We use this to keep track of active threads using this pipe, so that
+ while a thread is waiting to read from a pipe and that pipe gets
+ deallocated (say by socket_shutdown), it doesn't actually go away until
+ the reader realizes what happened. It is normally frobbed using
+ pipe_acquire & pipe_release, which do locking as well.. */
+ unsigned readers, writers;
+
+ /* Various flags, from PIPE_* below. */
+ unsigned flags;
+
+ /* Various timestamps for this pipe. */
+ time_value_t read_time;
+ time_value_t write_time;
+
+ pthread_cond_t pending_reads;
+ pthread_cond_t pending_read_selects;
+
+ pthread_cond_t pending_writes;
+ pthread_cond_t pending_write_selects;
+
+ struct pipe_select_cond *pending_selects;
+
+ /* The maximum number of characters that this pipe will hold without
+ further writes blocking. */
+ size_t write_limit;
+
+ /* Write requests of less than this much are always done atomically. */
+ size_t write_atomic;
+
+ pthread_mutex_t lock;
+
+ /* A queue of incoming packets, of type either PACKET_TYPE_DATA or
+ PACKET_TYPE_CONTROL. Each data packet represents one datagram for
+ protocols that maintain record boundaries. Control packets always
+ represent the control information to be returned from one read
+ operation, and will be returned in conjunction with the following data
+ packet (if any). Reads interested only in data just skip control
+ packets until they find a data packet. */
+ struct pq *queue;
+};
+
+/* Pipe flags. */
+#define PIPE_BROKEN 0x1 /* This pipe isn't connected. */
+
+
+extern size_t pipe_readable (struct pipe *pipe, int data_only);
+
+extern int pipe_is_readable (struct pipe *pipe, int data_only);
+
+extern error_t pipe_wait_readable (struct pipe *pipe, int noblock, int data_only);
+
+extern error_t pipe_select_readable (struct pipe *pipe, struct timespec *tsp,
+ int data_only);
+
+extern error_t pipe_wait_writable (struct pipe *pipe, int noblock);
+
+extern error_t pipe_select_writable (struct pipe *pipe, struct timespec *tsp);
+
+#if defined(__USE_EXTERN_INLINES) || defined(PIPE_DEFINE_EI)
+
+/* Returns the number of characters quickly readable from PIPE. If DATA_ONLY
+ is true, then `control' packets are ignored. */
+PIPE_EI size_t
+pipe_readable (struct pipe *pipe, int data_only)
+{
+ size_t readable = 0;
+ struct pq *pq = pipe->queue;
+ struct packet *packet = pq_head (pq, PACKET_TYPE_ANY, NULL);
+ while (packet)
+ {
+ if (packet->type == PACKET_TYPE_DATA)
+ readable += packet_readable (packet);
+ packet = packet->next;
+ }
+ return readable;
+}
+
+/* Returns true if there's any data available in PIPE. If DATA_ONLY is true,
+ then `control' packets are ignored. Note that this is different than
+ (pipe_readable (PIPE) > 0) in the case where a control packet containing
+ only ports is present. */
+PIPE_EI int
+pipe_is_readable (struct pipe *pipe, int data_only)
+{
+ struct pq *pq = pipe->queue;
+ struct packet *packet = pq_head (pq, PACKET_TYPE_ANY, NULL);
+ if (data_only)
+ while (packet && packet->type == PACKET_TYPE_CONTROL)
+ packet = packet->next;
+ return (packet != NULL);
+}
+
+/* Waits for PIPE to be readable, or an error to occur. If NOBLOCK is true,
+ this operation will return EWOULDBLOCK instead of blocking when no data is
+ immediately available. If DATA_ONLY is true, then `control' packets are
+ ignored. */
+PIPE_EI error_t
+pipe_wait_readable (struct pipe *pipe, int noblock, int data_only)
+{
+ while (! pipe_is_readable (pipe, data_only) && ! (pipe->flags & PIPE_BROKEN))
+ {
+ if (noblock)
+ return EWOULDBLOCK;
+ if (pthread_hurd_cond_wait_np (&pipe->pending_reads, &pipe->lock))
+ return EINTR;
+ }
+ return 0;
+}
+
+/* Waits for PIPE to be readable, or an error to occur. This call only
+ returns once threads waiting using pipe_wait_readable have been woken and
+ given a chance to read, and if there is still data available thereafter.
+ If DATA_ONLY is true, then `control' packets are ignored. */
+PIPE_EI error_t
+pipe_select_readable (struct pipe *pipe, struct timespec *tsp, int data_only)
+{
+ error_t err = 0;
+ while (! pipe_is_readable (pipe, data_only) && ! (pipe->flags & PIPE_BROKEN))
+ {
+ err = pthread_hurd_cond_timedwait_np (&pipe->pending_read_selects,
+ &pipe->lock, tsp);
+ if (err)
+ break;
+ }
+ return err;
+}
+
+/* Block until data can be written to PIPE. If NOBLOCK is true, then
+ EWOULDBLOCK is returned instead of blocking if this can't be done
+ immediately. */
+PIPE_EI error_t
+pipe_wait_writable (struct pipe *pipe, int noblock)
+{
+ size_t limit = pipe->write_limit;
+ if (pipe->flags & PIPE_BROKEN)
+ return EPIPE;
+ while (pipe_readable (pipe, 1) >= limit)
+ {
+ if (noblock)
+ return EWOULDBLOCK;
+ if (pthread_hurd_cond_wait_np (&pipe->pending_writes, &pipe->lock))
+ return EINTR;
+ if (pipe->flags & PIPE_BROKEN)
+ return EPIPE;
+ }
+ return 0;
+}
+
+/* Block until some data can be written to PIPE. This call only returns once
+ threads waiting using pipe_wait_writable have been woken and given a
+ chance to write, and if there is still space available thereafter. */
+PIPE_EI error_t
+pipe_select_writable (struct pipe *pipe, struct timespec *tsp)
+{
+ size_t limit = pipe->write_limit;
+ error_t err = 0;
+ while (! (pipe->flags & PIPE_BROKEN) && pipe_readable (pipe, 1) >= limit)
+ {
+ err = pthread_hurd_cond_timedwait_np (&pipe->pending_writes,
+ &pipe->lock, tsp);
+ if (err)
+ break;
+ }
+ return err;
+}
+
+#endif /* Use extern inlines. */
+
+/* Creates a new pipe of class CLASS and returns it in RESULT. */
+error_t pipe_create (struct pipe_class *class, struct pipe **pipe);
+
+/* Free PIPE and any resources it holds. */
+void pipe_free (struct pipe *pipe);
+
+/* Take any actions necessary when PIPE acquires its first reader. */
+void _pipe_first_reader (struct pipe *pipe);
+
+/* Take any actions necessary when PIPE acquires its first writer. */
+void _pipe_first_writer (struct pipe *pipe);
+
+/* Take any actions necessary when PIPE's last reader has gone away. PIPE
+ should be locked. */
+void _pipe_no_readers (struct pipe *pipe);
+
+/* Take any actions necessary when PIPE's last writer has gone away. PIPE
+ should be locked. */
+void _pipe_no_writers (struct pipe *pipe);
+
+extern void pipe_acquire_reader (struct pipe *pipe);
+
+extern void pipe_acquire_writer (struct pipe *pipe);
+
+extern void pipe_release_reader (struct pipe *pipe);
+
+extern void pipe_release_writer (struct pipe *pipe);
+
+extern void pipe_add_reader (struct pipe *pipe);
+
+extern void pipe_add_writer (struct pipe *pipe);
+
+extern void pipe_remove_reader (struct pipe *pipe);
+
+extern void pipe_remove_writer (struct pipe *pipe);
+
+extern void pipe_drain (struct pipe *pipe);
+
+#if defined(__USE_EXTERN_INLINES) || defined(PIPE_DEFINE_EI)
+
+/* Lock PIPE and increment its readers count. */
+PIPE_EI void
+pipe_acquire_reader (struct pipe *pipe)
+{
+ pthread_mutex_lock (&pipe->lock);
+ if (pipe->readers++ == 0)
+ _pipe_first_reader (pipe);
+}
+
+/* Lock PIPE and increment its writers count. */
+PIPE_EI void
+pipe_acquire_writer (struct pipe *pipe)
+{
+ pthread_mutex_lock (&pipe->lock);
+ if (pipe->writers++ == 0)
+ _pipe_first_writer (pipe);
+}
+
+/* Decrement PIPE's (which should be locked) reader count and unlock it. If
+ there are no more refs to PIPE, it will be destroyed. */
+PIPE_EI void
+pipe_release_reader (struct pipe *pipe)
+{
+ if (--pipe->readers == 0)
+ _pipe_no_readers (pipe);
+ else
+ pthread_mutex_unlock (&pipe->lock);
+}
+
+/* Decrement PIPE's (which should be locked) writer count and unlock it. If
+ there are no more refs to PIPE, it will be destroyed. */
+PIPE_EI void
+pipe_release_writer (struct pipe *pipe)
+{
+ if (--pipe->writers == 0)
+ _pipe_no_writers (pipe);
+ else
+ pthread_mutex_unlock (&pipe->lock);
+}
+
+/* Increment PIPE's reader count. PIPE should be unlocked. */
+PIPE_EI void
+pipe_add_reader (struct pipe *pipe)
+{
+ pipe_acquire_reader (pipe);
+ pthread_mutex_unlock (&pipe->lock);
+}
+
+/* Increment PIPE's writer count. PIPE should be unlocked. */
+PIPE_EI void
+pipe_add_writer (struct pipe *pipe)
+{
+ pipe_acquire_writer (pipe);
+ pthread_mutex_unlock (&pipe->lock);
+}
+
+/* Decrement PIPE's (which should be unlocked) reader count and unlock it. If
+ there are no more refs to PIPE, it will be destroyed. */
+PIPE_EI void
+pipe_remove_reader (struct pipe *pipe)
+{
+ pthread_mutex_lock (&pipe->lock);
+ pipe_release_reader (pipe);
+}
+
+/* Decrement PIPE's (which should be unlocked) writer count and unlock it. If
+ there are no more refs to PIPE, it will be destroyed. */
+PIPE_EI void
+pipe_remove_writer (struct pipe *pipe)
+{
+ pthread_mutex_lock (&pipe->lock);
+ pipe_release_writer (pipe);
+}
+
+/* Empty out PIPE of any data. PIPE should be locked. */
+PIPE_EI void
+pipe_drain (struct pipe *pipe)
+{
+ pq_drain (pipe->queue);
+}
+
+#endif /* Use extern inlines. */
+
+/* Writes up to LEN bytes of DATA, to PIPE, which should be locked, and
+ returns the amount written in AMOUNT. If present, the information in
+ CONTROL & PORTS is written in a preceding control packet. If an error is
+ returned, nothing is done. If non-NULL, SOURCE is recorded as the source
+ of the data, to be provided to any readers of it; if no reader ever reads
+ it, it's deallocated by calling pipe_dealloc_addr. */
+error_t pipe_send (struct pipe *pipe, int noblock, void *source,
+ char *data, size_t data_len,
+ char *control, size_t control_len,
+ mach_port_t *ports, size_t num_ports,
+ size_t *amount);
+
+/* Writes up to LEN bytes of DATA, to PIPE, which should be locked, and
+ returns the amount written in AMOUNT. If an error is returned, nothing is
+ done. If non-NULL, SOURCE is recorded as the source of the data, to be
+ provided to any readers of it; if no reader ever reads it, it's
+ deallocated by calling pipe_dealloc_addr. */
+#define pipe_write(pipe, noblock, source, data, data_len, amount) \
+ pipe_send (pipe, noblock, source, data, data_len, 0, 0, 0, 0, amount)
+
+/* Reads up to AMOUNT bytes from PIPE, which should be locked, into DATA, and
+ returns the amount read in DATA_LEN. If NOBLOCK is true, EWOULDBLOCK is
+ returned instead of block when no data is immediately available. If an
+ error is returned, nothing is done. If source isn't NULL, the
+ corresponding source provided by the sender is returned in it; this may be
+ NULL if it wasn't specified by the sender (which is usually the case with
+ connection-oriented protcols).
+
+ If there is control data waiting (before any data), then the behavior
+ depends on whether this is an `ordinary read' (when CONTROL and PORTS are
+ both NULL), in which case any control data is skipped, or a `msg read', in
+ which case the contents of the first control packet is returned (in
+ CONTROL and PORTS), as well as the first data packet following that (if
+ the control packet is followed by another control packet or no packet in
+ this case, a zero length data buffer is returned; the user should be
+ careful to distinguish this case from EOF (when no control or ports data
+ is returned either). */
+error_t pipe_recv (struct pipe *pipe, int noblock, unsigned *flags,
+ void **source,
+ char **data, size_t *data_len, size_t amount,
+ char **control, size_t *control_len,
+ mach_port_t **ports, size_t *num_ports);
+
+
+/* Reads up to AMOUNT bytes from PIPE, which should be locked, into DATA, and
+ returns the amount read in DATA_LEN. If NOBLOCK is true, EWOULDBLOCK is
+ returned instead of block when no data is immediately available. If an
+ error is returned, nothing is done. If source isn't NULL, the
+ corresponding source provided by the sender is returned in it; this may be
+ NULL if it wasn't specified by the sender (which is usually the case with
+ connection-oriented protcols). */
+#define pipe_read(pipe, noblock, source, data, data_len, amount) \
+ pipe_recv (pipe, noblock, 0, source, data, data_len, amount, 0,0,0,0)
+
+/* Hold this lock before attempting to lock multiple pipes. */
+extern pthread_mutex_t pipe_multiple_lock;
+
+/* Return when either RPIPE is available for reading (if SELECT_READ is set
+ in *SELECT_TYPE), or WPIPE is available for writing (if select_write is
+ set in *SELECT_TYPE). *SELECT_TYPE is modified to reflect which (or both)
+ is now available. DATA_ONLY should be true if only data packets should be
+ waited for on RPIPE. Neither RPIPE or WPIPE should be locked when calling
+ this function (unlike most pipe functions). */
+error_t pipe_pair_select (struct pipe *rpipe, struct pipe *wpipe,
+ struct timespec *tsp, int *select_type,
+ int data_only);
+
+/* ---------------------------------------------------------------- */
+/* User-provided functions. */
+
+/* This routine may be provided by the user, in which case, it should be a
+ function taking a non-NULL source address and deallocating it. It
+ defaults to calling ports_port_deref. */
+void pipe_dealloc_addr (void *addr);
+
+#endif /* __PIPE_H__ */
diff --git a/libpipe/pq-funcs.c b/libpipe/pq-funcs.c
new file mode 100644
index 00000000..57061419
--- /dev/null
+++ b/libpipe/pq-funcs.c
@@ -0,0 +1,2 @@
+#define PQ_DEFINE_EI
+#include "pq.h"
diff --git a/libpipe/pq.c b/libpipe/pq.c
new file mode 100644
index 00000000..102f3ee3
--- /dev/null
+++ b/libpipe/pq.c
@@ -0,0 +1,462 @@
+/* Packet queues
+
+ Copyright (C) 1995, 1996, 1998, 1999, 2002, 2006
+ Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <malloc.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/mman.h>
+
+#include "pq.h"
+
+/* ---------------------------------------------------------------- */
+
+/* Create a new packet queue, returning it in PQ. The only possible error is
+ ENOMEM. */
+error_t
+pq_create (struct pq **pq)
+{
+ *pq = malloc (sizeof (struct pq));
+
+ if (! *pq)
+ return ENOMEM;
+
+ (*pq)->head = (*pq)->tail = 0;
+ (*pq)->free = 0;
+
+ return 0;
+}
+
+/* Free every packet (and its contents) in the linked list rooted at HEAD. */
+static void
+free_packets (struct packet *head)
+{
+ if (head)
+ {
+ struct packet *next = head->next;
+ if (head->ports)
+ free (head->ports);
+ if (head->buf_len > 0)
+ {
+ if (head->buf_vm_alloced)
+ munmap (head->buf, head->buf_len);
+ else
+ free (head->buf);
+ }
+ free (head);
+ free_packets (next);
+ }
+}
+
+/* Frees PQ and any resources it holds, including deallocating any ports in
+ packets left in the queue. */
+void
+pq_free (struct pq *pq)
+{
+ pq_drain (pq);
+ free_packets (pq->free);
+ free (pq);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Remove the first packet (if any) in PQ, deallocating any resources it
+ holds. True is returned if a packet was found, false otherwise. */
+int
+pq_dequeue (struct pq *pq)
+{
+ extern void pipe_dealloc_addr (void *addr);
+ struct packet *packet = pq->head;
+
+ if (! packet)
+ return 0;
+
+ /* Deallocate any resource in PACKET. */
+ if (packet->num_ports)
+ packet_dealloc_ports (packet);
+ if (packet->source)
+ pipe_dealloc_addr (packet->source);
+
+ pq->head = packet->next;
+ packet->next = pq->free;
+ pq->free = packet;
+ if (pq->head)
+ pq->head->prev = 0;
+ else
+ pq->tail = 0;
+
+ return 1;
+}
+
+/* Empties out PQ. This *will* deallocate any ports in any of the packets. */
+void
+pq_drain (struct pq *pq)
+{
+ while (pq_dequeue (pq))
+ ;
+}
+
+/* Pushes a new packet of type TYPE and source SOURCE onto the tail of the
+ queue, and returns it, or 0 if there was an allocation error. */
+struct packet *
+pq_queue (struct pq *pq, unsigned type, void *source)
+{
+ struct packet *packet = pq->free;
+
+ if (!packet)
+ {
+ packet = malloc (sizeof (struct packet));
+ if (!packet)
+ return 0;
+ packet->buf = 0;
+ packet->buf_len = 0;
+ packet->ports = 0;
+ packet->ports_alloced = 0;
+ packet->buf_vm_alloced = 0;
+ }
+ else
+ pq->free = packet->next;
+
+ packet->num_ports = 0;
+ packet->buf_start = packet->buf_end = packet->buf;
+
+ packet->type = type;
+ packet->source = source;
+ packet->next = 0;
+ packet->prev = pq->tail;
+ if (pq->tail)
+ pq->tail->next = packet;
+ pq->tail = packet;
+ if (!pq->head)
+ pq->head = packet;
+
+ return packet;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Returns a legal size to which PACKET can be set allowing enough room for
+ EXTRA bytes more than what's already in it, and perhaps more. */
+size_t
+packet_new_size (struct packet *packet, size_t extra)
+{
+ size_t new_len = (packet->buf_end - packet->buf) + extra;
+ if (packet->buf_vm_alloced || new_len >= PACKET_SIZE_LARGE)
+ /* Round NEW_LEN up to a page boundary (OLD_LEN should already be). */
+ return round_page (new_len);
+ else
+ /* Otherwise, just round up to a multiple of 512 bytes. */
+ return (new_len + 511) & ~511;
+}
+
+/* Try to extend PACKET to be NEW_LEN bytes long, which should be greater
+ than the current packet size. This should be a valid length -- i.e., if
+ it's greater than PACKET_SIZE_LARGE, it should be a mulitple of
+ VM_PAGE_SIZE. If PACKET cannot be extended for some reason, false is
+ returned, otherwise true. */
+int
+packet_extend (struct packet *packet, size_t new_len)
+{
+ size_t old_len = packet->buf_len;
+
+ if (old_len == 0)
+ /* No existing buffer to extend. */
+ return 0;
+
+ if (packet->buf_vm_alloced)
+ /* A vm_alloc'd packet. */
+ {
+ char *extension = packet->buf + old_len;
+ /* Try to allocate memory at the end of our current buffer. */
+ if (vm_allocate (mach_task_self (),
+ (vm_address_t *)&extension, new_len - old_len, 0) != 0)
+ return 0;
+ }
+ else
+ /* A malloc'd packet. */
+ {
+ char *new_buf;
+ char *old_buf = packet->buf;
+
+ if (new_len >= PACKET_SIZE_LARGE)
+ /* The old packet length is malloc'd, but we want to vm_allocate the
+ new length, so we'd have to copy the old contents. */
+ return 0;
+
+ new_buf = realloc (old_buf, new_len);
+ if (! new_buf)
+ return 0;
+
+ packet->buf = new_buf;
+ packet->buf_start = new_buf + (packet->buf_start - old_buf);
+ packet->buf_end = new_buf + (packet->buf_end - old_buf);
+ }
+
+ packet->buf_len = new_len;
+
+ return 1;
+}
+
+/* Reallocate PACKET to have NEW_LEN bytes of buffer space, which should be
+ greater than the current packet size. This should be a valid length --
+ i.e., if it's greater than PACKET_SIZE_LARGE, it should be a multiple of
+ VM_PAGE_SIZE. If an error occurs, PACKET is not modified and the error is
+ returned. */
+error_t
+packet_realloc (struct packet *packet, size_t new_len)
+{
+ error_t err;
+ char *new_buf;
+ char *old_buf = packet->buf;
+ int vm_alloc = (new_len >= PACKET_SIZE_LARGE);
+
+ /* Make a new buffer. */
+ if (vm_alloc)
+ {
+ new_buf = mmap (0, new_len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ err = (new_buf == (char *) -1) ? errno : 0;
+ }
+ else
+ {
+ new_buf = malloc (new_len);
+ err = (new_buf ? 0 : ENOMEM);
+ }
+
+ if (! err)
+ {
+ size_t old_len = packet->buf_len;
+ char *start = packet->buf_start, *end = packet->buf_end;
+
+ /* Copy what we must. */
+ if (end != start)
+ /* If there was an operation like vm_move, we could use that in the
+ case where both the old and the new buffers were vm_alloced (on
+ the assumption that creating COW pages is somewhat more costly).
+ But there's not, and bcopy will do vm_copy where it can. Will we
+ still takes faults on the new copy, even though we've deallocated
+ the old one??? XXX */
+ bcopy (start, new_buf, end - start);
+
+ /* And get rid of the old buffer. */
+ if (old_len > 0)
+ {
+ if (packet->buf_vm_alloced)
+ vm_deallocate (mach_task_self (), (vm_address_t)old_buf, old_len);
+ else
+ free (old_buf);
+ }
+
+ packet->buf = new_buf;
+ packet->buf_len = new_len;
+ packet->buf_vm_alloced = vm_alloc;
+ packet->buf_start = new_buf;
+ packet->buf_end = new_buf + (end - start);
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* If PACKET has any ports, deallocates them. */
+void
+packet_dealloc_ports (struct packet *packet)
+{
+ unsigned i;
+ for (i = 0; i < packet->num_ports; i++)
+ {
+ mach_port_t port = packet->ports[i];
+ if (port != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), port);
+ }
+}
+
+/* Sets PACKET's ports to be PORTS, of length NUM_PORTS. ENOMEM is returned
+ if a memory allocation error occurred, otherwise, 0. */
+error_t
+packet_set_ports (struct packet *packet,
+ mach_port_t *ports, size_t num_ports)
+{
+ if (packet->num_ports > 0)
+ packet_dealloc_ports (packet);
+ if (num_ports > packet->ports_alloced)
+ {
+ mach_port_t *new_ports = malloc (sizeof (mach_port_t) * num_ports);
+ if (! new_ports)
+ return ENOMEM;
+ free (packet->ports);
+ packet->ports = new_ports;
+ packet->ports_alloced = num_ports;
+ }
+ bcopy (ports, packet->ports, sizeof (mach_port_t) * num_ports);
+ packet->num_ports = num_ports;
+ return 0;
+}
+
+/* Returns any ports in PACKET in PORTS and NUM_PORTS, and removes them from
+ PACKET. */
+error_t
+packet_read_ports (struct packet *packet,
+ mach_port_t **ports, size_t *num_ports)
+{
+ int length = packet->num_ports * sizeof (mach_port_t);
+ if (*num_ports < packet->num_ports)
+ {
+ *ports = mmap (0, length, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*ports == (mach_port_t *) -1)
+ return errno;
+ }
+ *num_ports = packet->num_ports;
+ bcopy (packet->ports, *ports, length);
+ packet->num_ports = 0;
+ return 0;
+}
+
+/* Append the bytes in DATA, of length DATA_LEN, to what's already in PACKET,
+ and return the amount appended in AMOUNT if that's not the null pointer. */
+error_t
+packet_write (struct packet *packet,
+ char *data, size_t data_len, size_t *amount)
+{
+ error_t err = packet_ensure (packet, data_len);
+
+ if (err)
+ return err;
+
+ /* Add the new data. */
+ bcopy (data, packet->buf_end, data_len);
+ packet->buf_end += data_len;
+ if (amount != NULL)
+ *amount = data_len;
+
+ return 0;
+}
+
+/* Remove or peek up to AMOUNT bytes from the beginning of the data in PACKET, and
+ puts it into *DATA, and the amount read into DATA_LEN. If more than the
+ original *DATA_LEN bytes are available, new memory is vm_allocated, and
+ the address and length of this array put into DATA and DATA_LEN. */
+static error_t
+packet_fetch (struct packet *packet,
+ char **data, size_t *data_len, size_t amount, int remove)
+{
+ char *start = packet->buf_start;
+ char *end = packet->buf_end;
+
+ if (amount > end - start)
+ amount = end - start;
+
+ if (amount > 0)
+ {
+ char *buf = packet->buf;
+
+ if (remove && packet->buf_vm_alloced && amount >= vm_page_size)
+ /* We can return memory from BUF directly without copying. */
+ {
+ if (buf + vm_page_size <= start)
+ /* BUF_START has been advanced past the start of the buffer
+ (perhaps by a series of small reads); as we're going to assume
+ everything before START is gone, make sure we deallocate any
+ memory on pages before those we return to the user. */
+ vm_deallocate (mach_task_self (),
+ (vm_address_t)buf,
+ trunc_page (start) - (vm_address_t)buf);
+
+ *data = start; /* Return the buffer directly. */
+ start += amount; /* Advance the read point. */
+
+ if (start < end)
+ /* Since returning a partial page actually means returning the
+ whole page, we have to be careful not to grab past the page
+ boundary before the end of the data we want. */
+ {
+ char *non_aligned_start = start;
+ start = (char *)trunc_page (start);
+ amount -= non_aligned_start - start;
+ }
+ else
+ /* This read will be up to the end of the buffer, so we can just
+ consume any space on the page following BUF_END (vm_alloced
+ buffers are always allocated in whole pages). */
+ {
+ start = (char *)round_page (start);
+ packet->buf_end = start; /* Ensure BUF_START <= BUF_END. */
+ }
+
+ /* We've actually consumed the memory at the start of BUF. */
+ packet->buf = start;
+ packet->buf_start = start;
+ packet->buf_len -= start - buf;
+ }
+ else
+ /* Just copy the data the old fashioned way.... */
+ {
+ if (*data_len < amount)
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+
+ bcopy (start, *data, amount);
+ start += amount;
+
+ if (remove && start - buf > 2 * PACKET_SIZE_LARGE)
+ /* Get rid of unused space at the beginning of the buffer -- we
+ know it's vm_alloced because of the size, and this will allow
+ the buffer to just slide through memory. Because we wait for
+ a relatively large amount of free space before doing this, and
+ packet_write() would have gotten rid the free space if it
+ didn't require copying much data, it's unlikely that this will
+ happen if it would have been cheaper to just move the packet
+ contents around to make space for the next write. */
+ {
+ vm_size_t dealloc = trunc_page (start) - (vm_address_t)buf;
+ vm_deallocate (mach_task_self (), (vm_address_t)buf, dealloc);
+ packet->buf = buf + dealloc;
+ packet->buf_len -= dealloc;
+ }
+
+ if (remove)
+ packet->buf_start = start;
+ }
+ }
+ *data_len = amount;
+
+ return 0;
+}
+
+/* Removes up to AMOUNT bytes from the beginning of the data in PACKET, and
+ puts it into *DATA, and the amount read into DATA_LEN. If more than the
+ original *DATA_LEN bytes are available, new memory is vm_allocated, and
+ the address and length of this array put into DATA and DATA_LEN. */
+error_t
+packet_read (struct packet *packet,
+ char **data, size_t *data_len, size_t amount)
+{
+ return packet_fetch (packet, data, data_len, amount, 1);
+}
+
+/* Peek up to AMOUNT bytes from the beginning of the data in PACKET, and
+ puts it into *DATA, and the amount read into DATA_LEN. If more than the
+ original *DATA_LEN bytes are available, new memory is vm_allocated, and
+ the address and length of this array put into DATA and DATA_LEN. */
+error_t
+packet_peek (struct packet *packet,
+ char **data, size_t *data_len, size_t amount)
+{
+ return packet_fetch (packet, data, data_len, amount, 0);
+}
diff --git a/libpipe/pq.h b/libpipe/pq.h
new file mode 100644
index 00000000..4e500b6c
--- /dev/null
+++ b/libpipe/pq.h
@@ -0,0 +1,306 @@
+/* Packet queues
+
+ Copyright (C) 1995, 1996, 2006 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __PQ_H__
+#define __PQ_H__
+
+#include <errno.h>
+#include <stddef.h> /* for size_t */
+#include <string.h>
+#include <mach/mach.h>
+#include <features.h>
+
+#ifdef PQ_DEFINE_EI
+#define PQ_EI
+#else
+#define PQ_EI __extern_inline
+#endif
+
+
+struct packet
+{
+ /* The packet type, from PACKET_* below. */
+ unsigned short type;
+
+ /* Where this packet was sent from. */
+ void *source;
+
+ /* Buffer space. */
+ char *buf;
+ size_t buf_len;
+ /* Pointers to the data within BUF. */
+ char *buf_start, *buf_end;
+ /* True if BUF was allocated using vm_allocate rather than malloc; only
+ valid if BUF_LEN > 0. */
+ int buf_vm_alloced;
+
+ /* Port data */
+ mach_port_t *ports;
+ size_t num_ports, ports_alloced;
+
+ /* Next and previous packets within the packet queue we're part of. If
+ PREV is null, we're at the head of the queue, and if NEXT is null, we're
+ at the tail. */
+ struct packet *next, *prev;
+};
+
+#define PACKET_TYPE_ANY 0 /* matches any type of packet */
+#define PACKET_TYPE_DATA 1
+#define PACKET_TYPE_CONTROL 2
+
+/* Sets PACKET's ports to be PORTS, of length NUM_PORTS. ENOMEM is returned
+ if a memory allocation error occurred, otherwise, 0. */
+error_t packet_set_ports (struct packet *packet,
+ mach_port_t *ports, size_t num_ports);
+
+/* If PACKET has any ports, deallocates them. */
+void packet_dealloc_ports (struct packet *packet);
+
+extern size_t packet_readable (struct packet *packet);
+
+#if defined(__USE_EXTERN_INLINES) || defined(PQ_DEFINE_EI)
+
+/* Returns the number of bytes of data in PACKET. */
+PQ_EI size_t
+packet_readable (struct packet *packet)
+{
+ return packet->buf_end - packet->buf_start;
+}
+
+#endif /* Use extern inlines. */
+
+/* Append the bytes in DATA, of length DATA_LEN, to what's already in PACKET,
+ and return the amount appended in AMOUNT if that's not the null pointer. */
+error_t packet_write (struct packet *packet,
+ char *data, size_t data_len, size_t *amount);
+
+/* Removes up to AMOUNT bytes from the beginning of the data in PACKET, and
+ puts it into *DATA, and the amount read into DATA_LEN. If more than the
+ original *DATA_LEN bytes are available, new memory is vm_allocated, and
+ the address and length of this array put into DATA and DATA_LEN. */
+error_t packet_read (struct packet *packet,
+ char **data, size_t *data_len, size_t amount);
+
+/* Peek up to AMOUNT bytes from the beginning of the data in PACKET, and
+ puts it into *DATA, and the amount read into DATA_LEN. If more than the
+ original *DATA_LEN bytes are available, new memory is vm_allocated, and
+ the address and length of this array put into DATA and DATA_LEN. */
+error_t packet_peek (struct packet *packet,
+ char **data, size_t *data_len, size_t amount);
+
+/* Returns any ports in PACKET in PORTS and NUM_PORTS, and removes them from
+ PACKET. */
+error_t packet_read_ports (struct packet *packet,
+ mach_port_t **ports, size_t *num_ports);
+
+extern void packet_read_source (struct packet *packet, void **source);
+
+#if defined(__USE_EXTERN_INLINES) || defined(PQ_DEFINE_EI)
+
+/* Return the source addressd in PACKET in SOURCE, deallocating it from
+ PACKET. */
+PQ_EI void
+packet_read_source (struct packet *packet, void **source)
+{
+ *source = packet->source;
+ packet->source = 0;
+}
+
+#endif /* Use extern inlines. */
+
+/* The packet size above which we start to do things differently to avoid
+ copying around data. */
+#define PACKET_SIZE_LARGE 8192
+
+/* Returns a legal size to which PACKET can be set allowing enough room for
+ EXTRA bytes more than what's already in it, and perhaps more. */
+size_t packet_new_size (struct packet *packet, size_t extra);
+
+/* Try to extend PACKET to be NEW_LEN bytes long, which should be greater
+ than the current packet size. This should be a valid length -- i.e., if
+ it's greater than PAGE_PACKET_SIZE, it should be a mulitple of
+ VM_PAGE_SIZE. If PACKET cannot be extended for some reason, false is
+ returned, otherwise true. */
+int packet_extend (struct packet *packet, size_t new_len);
+
+/* Reallocate PACKET to have NEW_LEN bytes of buffer space, which should be
+ greater than the current packet size. This should be a valid length --
+ i.e., if it's greater than PAGE_PACKET_SIZE, it should be a multiple of
+ VM_PAGE_SIZE. If an error occurs, PACKET is not modified and the error is
+ returned. */
+error_t packet_realloc (struct packet *packet, size_t new_len);
+
+extern int packet_fit (struct packet *packet, size_t amount);
+
+extern error_t packet_ensure (struct packet *packet, size_t amount);
+
+extern int packet_ensure_efficiently (struct packet *packet, size_t amount);
+
+#if defined(__USE_EXTERN_INLINES) || defined(PQ_DEFINE_EI)
+
+/* Try to make space in PACKET for AMOUNT more bytes without growing the
+ buffer, returning true if we could do it. */
+PQ_EI int
+packet_fit (struct packet *packet, size_t amount)
+{
+ char *buf = packet->buf, *end = packet->buf_end;
+ size_t buf_len = packet->buf_len;
+ size_t left = buf + buf_len - end; /* Free space at the end of the buffer. */
+
+ if (amount > left)
+ {
+ char *start = packet->buf_start;
+ size_t cur_len = end - start; /* Amount of data currently in the buf. */
+
+ if (buf_len - cur_len >= amount
+ && cur_len < PACKET_SIZE_LARGE && cur_len < (buf_len >> 2))
+ /* If we could fit the data in by moving what's already in the
+ buffer, and there's not too much there, and it represents less
+ than 25% of the buffer size, then move the data instead of growing
+ the buffer. */
+ {
+ bcopy (start, buf, cur_len);
+ packet->buf_start = buf;
+ packet->buf_end = buf + cur_len;
+ }
+ else
+ return 0; /* We failed... */
+ }
+
+ return 1;
+}
+
+/* Make sure that PACKET has room for at least AMOUNT more bytes, or return
+ the reason why not. */
+PQ_EI error_t
+packet_ensure (struct packet *packet, size_t amount)
+{
+ if (! packet_fit (packet, amount))
+ /* We must make the buffer bigger. */
+ {
+ size_t new_len = packet_new_size (packet, amount);
+ if (! packet_extend (packet, new_len))
+ return packet_realloc (packet, new_len);
+ }
+ return 0;
+}
+
+/* Make sure that PACKET has room for at least AMOUNT more bytes, *only* if
+ it can be done efficiently, e.g., the packet can be grown in place, rather
+ than moving the contents (or there is little enough data so that copying
+ it is OK). True is returned if room was made, false otherwise. */
+PQ_EI int
+packet_ensure_efficiently (struct packet *packet, size_t amount)
+{
+ if (! packet_fit (packet, amount))
+ {
+ size_t new_len = packet_new_size (packet, amount);
+ if (packet_extend (packet, new_len))
+ return 1;
+ if ((packet->buf_end - packet->buf_start) < PACKET_SIZE_LARGE)
+ return packet_realloc (packet, new_len) == 0;
+ }
+ return 0;
+}
+
+#endif /* Use extern inlines. */
+
+struct pq
+{
+ struct packet *head, *tail; /* Packet queue */
+ struct packet *free; /* Free packets */
+};
+
+/* Pushes a new packet of type TYPE and source SOURCE, and returns it, or
+ NULL if there was an allocation error. SOURCE is returned to readers of
+ the packet, or deallocated by calling pipe_dealloc_addr. */
+struct packet *pq_queue (struct pq *pq, unsigned type, void *source);
+
+extern struct packet * pq_tail (struct pq *pq, unsigned type, void *source);
+
+#if defined(__USE_EXTERN_INLINES) || defined(PQ_DEFINE_EI)
+
+/* Returns the tail of the packet queue PQ, which may mean pushing a new
+ packet if TYPE and SOURCE do not match the current tail, or this is the
+ first packet. */
+PQ_EI struct packet *
+pq_tail (struct pq *pq, unsigned type, void *source)
+{
+ struct packet *tail = pq->tail;
+ if (!tail
+ || (type && tail->type != type) || (source && tail->source != source))
+ tail = pq_queue (pq, type, source);
+ return tail;
+}
+
+#endif /* Use extern inlines. */
+
+/* Remove the first packet (if any) in PQ, deallocating any resources it
+ holds. True is returned if a packet was found, false otherwise. */
+int pq_dequeue (struct pq *pq);
+
+extern struct packet * pq_head (struct pq *pq, unsigned type, void *source);
+
+extern struct packet * pq_next (struct pq *pq, unsigned type, void *source);
+
+#if defined(__USE_EXTERN_INLINES) || defined(PQ_DEFINE_EI)
+
+/* Returns the next available packet in PQ, without removing it from the
+ queue, or NULL if there is none, or the next packet isn't appropriate.
+ A packet is inappropriate if SOURCE is non-NULL its source field doesn't
+ match it, or TYPE is non-NULL and the packet's type field doesn't match
+ it. */
+PQ_EI struct packet *
+pq_head (struct pq *pq, unsigned type, void *source)
+{
+ struct packet *head = pq->head;
+ if (!head)
+ return 0;
+ if (type && head->type != type)
+ return 0;
+ if (source && head->source != source)
+ return 0;
+ return head;
+}
+
+/* The same as pq_head, but first discards the head of the queue. */
+PQ_EI struct packet *
+pq_next (struct pq *pq, unsigned type, void *source)
+{
+ if (!pq->head)
+ return 0;
+ pq_dequeue (pq);
+ return pq_head (pq, type, source);
+}
+
+#endif /* Use extern inlines. */
+
+/* Dequeues all packets in PQ. */
+void pq_drain (struct pq *pq);
+
+/* Create a new packet queue, returning it in PQ. The only possible error is
+ ENOMEM. */
+error_t pq_create (struct pq **pq);
+
+/* Frees PQ and any resources it holds, including deallocating any ports in
+ packets left in the queue. */
+void pq_free (struct pq *pq);
+
+#endif /* __PQ_H__ */
diff --git a/libpipe/seqpack.c b/libpipe/seqpack.c
new file mode 100644
index 00000000..041abb74
--- /dev/null
+++ b/libpipe/seqpack.c
@@ -0,0 +1,64 @@
+/* The SOCK_SEQPACKET pipe class
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/socket.h> /* For SOCK_SEQPACKET */
+
+#include "pipe.h"
+#include "pq.h"
+
+/* See the definition of struct pipe_class in "pipe.h" for documentation. */
+
+/* This type of pipe is the same as a SOCK_STREAM, but maintains record
+ boundaries. */
+
+static error_t
+seqpack_write (struct pq *pq, void *source,
+ char *data, size_t data_len, size_t *amount)
+{
+ struct packet *packet = pq_queue (pq, PACKET_TYPE_DATA, source);
+ if (!packet)
+ return ENOBUFS;
+ else
+ return packet_write (packet, data, data_len, amount);
+}
+
+static error_t
+seqpack_read (struct packet *packet, int *dequeue, unsigned *flags,
+ char **data, size_t *data_len, size_t amount)
+{
+ error_t err;
+ if (flags && *flags & MSG_PEEK)
+ {
+ err = packet_peek (packet, data, data_len, amount);
+ *dequeue = 0;
+ }
+ else
+ {
+ err = packet_read (packet, data, data_len, amount);
+ *dequeue = (packet_readable (packet) == 0);
+ }
+ return err;
+}
+
+struct pipe_class _seqpack_pipe_class =
+{
+ SOCK_SEQPACKET, 0, seqpack_read, seqpack_write
+};
+struct pipe_class *seqpack_pipe_class = &_seqpack_pipe_class;
diff --git a/libpipe/stream.c b/libpipe/stream.c
new file mode 100644
index 00000000..671907e7
--- /dev/null
+++ b/libpipe/stream.c
@@ -0,0 +1,77 @@
+/* The SOCK_STREAM pipe class
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/socket.h> /* For SOCK_STREAM */
+
+#include "pipe.h"
+#include "pq.h"
+
+/* See the definition of struct pipe_class in "pipe.h" for an explanation. */
+
+/* This should be in some system header... XXX */
+static inline int page_aligned (vm_offset_t num)
+{
+ return trunc_page (num) == num;
+}
+
+static error_t
+stream_write (struct pq *pq, void *source,
+ char *data, size_t data_len, size_t *amount)
+{
+ struct packet *packet = pq_tail (pq, PACKET_TYPE_DATA, source);
+
+ if (packet_readable (packet) > 0
+ && data_len > PACKET_SIZE_LARGE
+ && (! page_aligned (data - packet->buf_end)
+ || ! packet_ensure_efficiently (packet, data_len)))
+ /* Put a large page-aligned transfer in its own packet, if it's
+ page-aligned `differently' than the end of the current packet, or if
+ the current packet can't be extended in place. */
+ packet = pq_queue (pq, PACKET_TYPE_DATA, source);
+
+ if (!packet)
+ return ENOBUFS;
+ else
+ return packet_write (packet, data, data_len, amount);
+}
+
+static error_t
+stream_read (struct packet *packet, int *dequeue, unsigned *flags,
+ char **data, size_t *data_len, size_t amount)
+{
+ error_t err;
+ if (flags && *flags & MSG_PEEK)
+ {
+ err = packet_peek (packet, data, data_len, amount);
+ *dequeue = 0;
+ }
+ else
+ {
+ err = packet_read (packet, data, data_len, amount);
+ *dequeue = (packet_readable (packet) == 0);
+ }
+ return err;
+}
+
+struct pipe_class _stream_pipe_class =
+{
+ SOCK_STREAM, 0, stream_read, stream_write
+};
+struct pipe_class *stream_pipe_class = &_stream_pipe_class;
diff --git a/libports/Makefile b/libports/Makefile
new file mode 100644
index 00000000..30da1c11
--- /dev/null
+++ b/libports/Makefile
@@ -0,0 +1,50 @@
+# Copyright (C) 1995,96,97,99,2000,2012 Free Software Foundation, Inc.
+# Written by Michael I. Bushnell.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libports
+makemode := library
+
+libname = libports
+SRCS = create-bucket.c create-class.c \
+ reallocate-port.c reallocate-from-external.c destroy-right.c \
+ lookup-port.c port-ref.c port-ref-weak.c port-deref.c port-deref-weak.c \
+ no-senders.c begin-rpc.c end-rpc.c manage-one-thread.c manage-multithread.c \
+ inhibit-port-rpcs.c inhibit-class-rpcs.c inhibit-bucket-rpcs.c \
+ inhibit-all-rpcs.c resume-port-rpcs.c resume-class-rpcs.c \
+ resume-bucket-rpcs.c resume-all-rpcs.c interrupt-rpcs.c \
+ init.c complete-deallocate.c get-right.c get-send-right.c \
+ count-class.c count-bucket.c \
+ enable-class.c enable-bucket.c bucket-iterate.c class-iterate.c stubs.c \
+ notify-dead-name.c notify-no-senders.c notify-port-destroyed.c \
+ notify-msg-accepted.c notify-port-deleted.c notify-send-once.c \
+ interrupt-operation.c interrupt-on-notify.c interrupt-notified-rpcs.c \
+ dead-name.c create-port.c import-port.c default-uninhibitable-rpcs.c \
+ claim-right.c transfer-right.c create-port-noinstall.c create-internal.c \
+ interrupted.c
+
+installhdrs = ports.h
+
+HURDLIBS= ihash
+LDLIBS += -lpthread
+OBJS = $(SRCS:.c=.o) notifyServer.o interruptServer.o
+
+MIGCOMSFLAGS = -prefix ports_
+MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+
+include ../Makeconf
diff --git a/libports/begin-rpc.c b/libports/begin-rpc.c
new file mode 100644
index 00000000..142af981
--- /dev/null
+++ b/libports/begin-rpc.c
@@ -0,0 +1,108 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+
+#define INHIBITED (PORTS_INHIBITED | PORTS_INHIBIT_WAIT)
+
+error_t
+ports_begin_rpc (void *portstruct, mach_msg_id_t msg_id, struct rpc_info *info)
+{
+ int *block_flags = 0;
+
+ struct port_info *pi = portstruct;
+
+ pthread_mutex_lock (&_ports_lock);
+
+ do
+ {
+ /* If our receive right is gone, then abandon the RPC. */
+ if (pi->port_right == MACH_PORT_NULL)
+ {
+ pthread_mutex_unlock (&_ports_lock);
+ return EOPNOTSUPP;
+ }
+
+ if (_ports_flags & INHIBITED)
+ /* All RPC's are inhibited. */
+ block_flags = &_ports_flags;
+ else if (pi->bucket->flags & INHIBITED)
+ /* RPC's are inhibited for this port's bucket. */
+ block_flags = &pi->bucket->flags;
+ else if (pi->class->flags & INHIBITED)
+ /* RPC's are inhibited for this port's class. */
+ block_flags = &pi->class->flags;
+ else if (pi->flags & INHIBITED)
+ /* RPC's are inhibited for this port itself. */
+ block_flags = &pi->flags;
+ else
+ block_flags = 0;
+
+ if (block_flags)
+ /* We maybe want to block. */
+ {
+ if (msg_id)
+ /* See if this particular RPC shouldn'be be inhibitable. */
+ {
+ struct ports_msg_id_range *range = pi->class->uninhibitable_rpcs;
+ while (range)
+ if (msg_id >= range->start && msg_id < range->end)
+ {
+ block_flags = 0;
+ break;
+ }
+ else
+ range = range->next;
+ }
+
+ if (block_flags)
+ {
+ *block_flags |= PORTS_BLOCKED;
+ if (pthread_hurd_cond_wait_np (&_ports_block, &_ports_lock))
+ /* We've been cancelled, just return EINTR. If we were the
+ only one blocking, PORTS_BLOCKED will still be turned on,
+ but that's ok, it will just cause a (harmless) extra
+ pthread_cond_broadcast(). */
+ {
+ pthread_mutex_unlock (&_ports_lock);
+ return EINTR;
+ }
+ }
+ }
+ }
+ while (block_flags);
+
+ /* Record that that an RPC is in progress */
+ info->thread = hurd_thread_self ();
+ info->next = pi->current_rpcs;
+ info->notifies = 0;
+ if (pi->current_rpcs)
+ pi->current_rpcs->prevp = &info->next;
+ info->prevp = &pi->current_rpcs;
+ pi->current_rpcs = info;
+
+ pi->class->rpcs++;
+ pi->bucket->rpcs++;
+ _ports_total_rpcs++;
+
+ pthread_mutex_unlock (&_ports_lock);
+
+ return 0;
+}
diff --git a/libports/bucket-iterate.c b/libports/bucket-iterate.c
new file mode 100644
index 00000000..babc2045
--- /dev/null
+++ b/libports/bucket-iterate.c
@@ -0,0 +1,93 @@
+/* Iterate a function over the ports in a bucket.
+ Copyright (C) 1995, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "ports.h"
+#include <hurd/ihash.h>
+
+
+/* Internal entrypoint for both ports_bucket_iterate and ports_class_iterate.
+ If CLASS is non-null, call FUN only for ports in that class. */
+error_t
+_ports_bucket_class_iterate (struct port_bucket *bucket,
+ struct port_class *class,
+ error_t (*fun)(void *))
+{
+ /* This is obscenely ineffecient. ihash and ports need to cooperate
+ more closely to do it efficiently. */
+ void **p;
+ size_t i, n, nr_items;
+ error_t err;
+
+ pthread_mutex_lock (&_ports_lock);
+
+ if (bucket->htable.nr_items == 0)
+ {
+ pthread_mutex_unlock (&_ports_lock);
+ return 0;
+ }
+
+ nr_items = bucket->htable.nr_items;
+ p = malloc (nr_items * sizeof *p);
+ if (p == NULL)
+ {
+ pthread_mutex_unlock (&_ports_lock);
+ return ENOMEM;
+ }
+
+ n = 0;
+ HURD_IHASH_ITERATE (&bucket->htable, arg)
+ {
+ struct port_info *const pi = arg;
+
+ if (class == 0 || pi->class == class)
+ {
+ pi->refcnt++;
+ p[n] = pi;
+ n++;
+ }
+ }
+ pthread_mutex_unlock (&_ports_lock);
+
+ if (n != nr_items)
+ {
+ /* We allocated too much. Release unused memory. */
+ void **new = realloc (p, n * sizeof *p);
+ if (new)
+ p = new;
+ }
+
+ err = 0;
+ for (i = 0; i < n; i++)
+ {
+ if (!err)
+ err = (*fun)(p[i]);
+ ports_port_deref (p[i]);
+ }
+
+ free (p);
+ return err;
+}
+
+error_t
+ports_bucket_iterate (struct port_bucket *bucket,
+ error_t (*fun)(void *))
+{
+ return _ports_bucket_class_iterate (bucket, 0, fun);
+}
diff --git a/libports/claim-right.c b/libports/claim-right.c
new file mode 100644
index 00000000..4851ea3c
--- /dev/null
+++ b/libports/claim-right.c
@@ -0,0 +1,52 @@
+/* Take a receive right away from a port
+ Copyright (C) 1996, 2001 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include "ports.h"
+#include <assert.h>
+#include <errno.h>
+#include <hurd/ihash.h>
+
+mach_port_t
+ports_claim_right (void *portstruct)
+{
+ error_t err;
+ struct port_info *pi = portstruct;
+ mach_port_t ret = pi->port_right;
+
+ if (ret == MACH_PORT_NULL)
+ return ret;
+
+ pthread_mutex_lock (&_ports_lock);
+ hurd_ihash_locp_remove (&pi->bucket->htable, pi->hentry);
+ err = mach_port_move_member (mach_task_self (), ret, MACH_PORT_NULL);
+ assert_perror (err);
+ pi->port_right = MACH_PORT_NULL;
+ if (pi->flags & PORT_HAS_SENDRIGHTS)
+ {
+ pi->flags &= ~PORT_HAS_SENDRIGHTS;
+ pthread_mutex_unlock (&_ports_lock);
+ ports_port_deref (pi);
+ }
+ else
+ pthread_mutex_unlock (&_ports_lock);
+
+ return ret;
+}
diff --git a/libports/class-iterate.c b/libports/class-iterate.c
new file mode 100644
index 00000000..1f8878a5
--- /dev/null
+++ b/libports/class-iterate.c
@@ -0,0 +1,35 @@
+/* Iterate a function over the ports in a class.
+ Copyright (C) 1999 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "ports.h"
+
+error_t
+ports_class_iterate (struct port_class *class,
+ error_t (*fun)(void *))
+{
+ pthread_mutex_lock (&_ports_lock);
+ if (class->ports != 0)
+ {
+ struct port_bucket *bucket = class->ports->bucket;
+ pthread_mutex_unlock (&_ports_lock);
+ return _ports_bucket_class_iterate (bucket, class, fun);
+ }
+ pthread_mutex_unlock (&_ports_lock);
+ return 0;
+}
diff --git a/libports/complete-deallocate.c b/libports/complete-deallocate.c
new file mode 100644
index 00000000..8ce095b1
--- /dev/null
+++ b/libports/complete-deallocate.c
@@ -0,0 +1,51 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <assert.h>
+#include <hurd/ihash.h>
+
+void
+_ports_complete_deallocate (struct port_info *pi)
+{
+ assert ((pi->flags & PORT_HAS_SENDRIGHTS) == 0);
+
+ if (pi->port_right)
+ {
+ hurd_ihash_locp_remove (&pi->bucket->htable, pi->hentry);
+ mach_port_mod_refs (mach_task_self (), pi->port_right,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+ pi->port_right = MACH_PORT_NULL;
+ }
+
+ *pi->prevp = pi->next;
+ if (pi->next)
+ pi->next->prevp = pi->prevp;
+
+ pi->bucket->count--;
+ pi->class->count--;
+
+ pthread_mutex_unlock (&_ports_lock);
+
+ if (pi->class->clean_routine)
+ (*pi->class->clean_routine)(pi);
+
+ free (pi);
+}
diff --git a/libports/count-bucket.c b/libports/count-bucket.c
new file mode 100644
index 00000000..63feb6be
--- /dev/null
+++ b/libports/count-bucket.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+
+int
+ports_count_bucket (struct port_bucket *bucket)
+{
+ int ret;
+
+ pthread_mutex_lock (&_ports_lock);
+ ret = bucket->count;
+ bucket->flags |= PORT_BUCKET_NO_ALLOC;
+ pthread_mutex_unlock (&_ports_lock);
+
+ return ret;
+}
diff --git a/libports/count-class.c b/libports/count-class.c
new file mode 100644
index 00000000..0c48b46e
--- /dev/null
+++ b/libports/count-class.c
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+
+int
+ports_count_class (struct port_class *class)
+{
+ int ret;
+
+ pthread_mutex_lock (&_ports_lock);
+ ret = class->count;
+ class->flags |= PORT_CLASS_NO_ALLOC;
+ pthread_mutex_unlock (&_ports_lock);
+ return ret;
+}
diff --git a/libports/create-bucket.c b/libports/create-bucket.c
new file mode 100644
index 00000000..52d50c35
--- /dev/null
+++ b/libports/create-bucket.c
@@ -0,0 +1,58 @@
+/* Create a port bucket
+ Copyright (C) 1995, 1997, 2001, 2003 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <stddef.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <hurd/ihash.h>
+
+struct port_bucket *
+ports_create_bucket ()
+{
+ struct port_bucket *ret;
+ error_t err;
+
+ ret = malloc (sizeof (struct port_bucket));
+ if (! ret)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_PORT_SET,
+ &ret->portset);
+ if (err)
+ {
+ errno = err;
+ free (ret);
+ return NULL;
+ }
+
+ hurd_ihash_init (&ret->htable, offsetof (struct port_info, hentry));
+ ret->rpcs = ret->flags = ret->count = 0;
+
+ pthread_mutex_lock (&_ports_lock);
+ ret->next = _ports_all_buckets;
+ _ports_all_buckets = ret;
+ pthread_mutex_unlock (&_ports_lock);
+
+ return ret;
+}
diff --git a/libports/create-class.c b/libports/create-class.c
new file mode 100644
index 00000000..12c8add9
--- /dev/null
+++ b/libports/create-class.c
@@ -0,0 +1,47 @@
+/*
+ Copyright (C) 1995,2001 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <stdlib.h>
+#include <errno.h>
+
+struct port_class *
+ports_create_class (void (*clean_routine)(void *),
+ void (*dropweak_routine)(void *))
+{
+ struct port_class *cl;
+
+ cl = malloc (sizeof (struct port_class));
+ if (! cl)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ cl->clean_routine = clean_routine;
+ cl->dropweak_routine = dropweak_routine;
+ cl->flags = 0;
+ cl->rpcs = 0;
+ cl->ports = NULL;
+ cl->count = 0;
+ cl->uninhibitable_rpcs = ports_default_uninhibitable_rpcs;
+
+ return cl;
+}
diff --git a/libports/create-internal.c b/libports/create-internal.c
new file mode 100644
index 00000000..85512970
--- /dev/null
+++ b/libports/create-internal.c
@@ -0,0 +1,120 @@
+/*
+ Copyright (C) 1996,2001 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <assert.h>
+#include <hurd/ihash.h>
+#include "ports.h"
+
+/* Create and return in RESULT a new port in CLASS and BUCKET; SIZE
+ bytes will be allocated to hold the port structure and whatever
+ private data the user desires. If INSTALL is set, put the port
+ right into BUCKET's port set. */
+error_t
+_ports_create_port_internal (struct port_class *class,
+ struct port_bucket *bucket,
+ size_t size, void *result,
+ int install)
+{
+ mach_port_t port;
+ error_t err;
+ struct port_info *pi;
+
+ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &port);
+ if (err)
+ return err;
+
+ if (size < sizeof (struct port_info))
+ size = sizeof (struct port_info);
+
+ pi = malloc (size);
+ if (! pi)
+ {
+ err = mach_port_mod_refs (mach_task_self (), port,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+ assert_perror (err);
+ return ENOMEM;
+ }
+
+ pi->class = class;
+ pi->refcnt = 1;
+ pi->weakrefcnt = 0;
+ pi->cancel_threshold = 0;
+ pi->mscount = 0;
+ pi->flags = 0;
+ pi->port_right = port;
+ pi->current_rpcs = 0;
+ pi->bucket = bucket;
+
+ pthread_mutex_lock (&_ports_lock);
+
+ loop:
+ if (class->flags & PORT_CLASS_NO_ALLOC)
+ {
+ class->flags |= PORT_CLASS_ALLOC_WAIT;
+ if (pthread_hurd_cond_wait_np (&_ports_block, &_ports_lock))
+ goto cancelled;
+ goto loop;
+ }
+ if (bucket->flags & PORT_BUCKET_NO_ALLOC)
+ {
+ bucket->flags |= PORT_BUCKET_ALLOC_WAIT;
+ if (pthread_hurd_cond_wait_np (&_ports_block, &_ports_lock))
+ goto cancelled;
+ goto loop;
+ }
+
+ err = hurd_ihash_add (&bucket->htable, port, pi);
+ if (err)
+ goto lose;
+
+ pi->next = class->ports;
+ pi->prevp = &class->ports;
+ if (class->ports)
+ class->ports->prevp = &pi->next;
+ class->ports = pi;
+ bucket->count++;
+ class->count++;
+ pthread_mutex_unlock (&_ports_lock);
+
+ if (install)
+ {
+ err = mach_port_move_member (mach_task_self (), pi->port_right,
+ bucket->portset);
+ if (err)
+ goto lose_unlocked;
+ }
+
+ *(void **)result = pi;
+ return 0;
+
+ cancelled:
+ err = EINTR;
+ lose:
+ pthread_mutex_unlock (&_ports_lock);
+ lose_unlocked:;
+ error_t e;
+ e = mach_port_mod_refs (mach_task_self (), port,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+ assert_perror (e);
+ free (pi);
+
+ return err;
+}
diff --git a/libports/create-port-noinstall.c b/libports/create-port-noinstall.c
new file mode 100644
index 00000000..fd5a6403
--- /dev/null
+++ b/libports/create-port-noinstall.c
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "ports.h"
+
+/* Create and return in RESULT a new port in CLASS and BUCKET; SIZE bytes
+ will be allocated to hold the port structure and whatever private data the
+ user desires. */
+error_t
+ports_create_port_noinstall (struct port_class *class,
+ struct port_bucket *bucket,
+ size_t size, void *result)
+{
+ return _ports_create_port_internal (class, bucket, size, result, 0);
+}
diff --git a/libports/create-port.c b/libports/create-port.c
new file mode 100644
index 00000000..104a18d0
--- /dev/null
+++ b/libports/create-port.c
@@ -0,0 +1,32 @@
+/* Create a new port structure
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+
+/* Create and return in RESULT a new port in CLASS and BUCKET; SIZE bytes
+ will be allocated to hold the port structure and whatever private data the
+ user desires. */
+error_t
+ports_create_port (struct port_class *class, struct port_bucket *bucket,
+ size_t size, void *result)
+{
+ return _ports_create_port_internal (class, bucket, size, result, 1);
+}
diff --git a/libports/dead-name.c b/libports/dead-name.c
new file mode 100644
index 00000000..de89ba6b
--- /dev/null
+++ b/libports/dead-name.c
@@ -0,0 +1,28 @@
+/* Handle various ports internal uses of dead-name notification
+
+ Copyright (C) 1995, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <mach/notify.h>
+
+void
+ports_dead_name (void *notify, mach_port_t dead_name)
+{
+ ports_interrupt_notified_rpcs (notify, dead_name, MACH_NOTIFY_DEAD_NAME);
+}
diff --git a/libports/default-uninhibitable-rpcs.c b/libports/default-uninhibitable-rpcs.c
new file mode 100644
index 00000000..60aeccab
--- /dev/null
+++ b/libports/default-uninhibitable-rpcs.c
@@ -0,0 +1,27 @@
+/* Default definition of default_uninhibitable_rpcs
+
+ Copyright (C) 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+
+static struct ports_msg_id_range
+interrupt_operation_ids = { 33000, 33001, 0 };
+
+struct ports_msg_id_range *
+ports_default_uninhibitable_rpcs = &interrupt_operation_ids;
diff --git a/libports/destroy-right.c b/libports/destroy-right.c
new file mode 100644
index 00000000..65e19c78
--- /dev/null
+++ b/libports/destroy-right.c
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <hurd/ihash.h>
+#include <assert.h>
+
+error_t
+ports_destroy_right (void *portstruct)
+{
+ struct port_info *pi = portstruct;
+ error_t err;
+
+ if (pi->port_right != MACH_PORT_NULL)
+ {
+ pthread_mutex_lock (&_ports_lock);
+ hurd_ihash_locp_remove (&pi->bucket->htable, pi->hentry);
+ err = mach_port_mod_refs (mach_task_self (), pi->port_right,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+ assert_perror (err);
+ pthread_mutex_unlock (&_ports_lock);
+
+ pi->port_right = MACH_PORT_NULL;
+
+ if (pi->flags & PORT_HAS_SENDRIGHTS)
+ {
+ pi->flags &= ~PORT_HAS_SENDRIGHTS;
+ ports_port_deref (pi);
+ }
+ }
+
+ return 0;
+}
diff --git a/libports/enable-bucket.c b/libports/enable-bucket.c
new file mode 100644
index 00000000..f9c7b859
--- /dev/null
+++ b/libports/enable-bucket.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+
+void
+ports_enable_bucket (struct port_bucket *bucket)
+{
+ pthread_mutex_lock (&_ports_lock);
+ bucket->flags &= ~PORT_BUCKET_NO_ALLOC;
+ if (bucket->flags & PORT_BUCKET_ALLOC_WAIT)
+ {
+ bucket->flags &= ~PORT_BUCKET_ALLOC_WAIT;
+ pthread_cond_broadcast (&_ports_block);
+ }
+ pthread_mutex_unlock (&_ports_lock);
+}
diff --git a/libports/enable-class.c b/libports/enable-class.c
new file mode 100644
index 00000000..b3894ebb
--- /dev/null
+++ b/libports/enable-class.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+
+void
+ports_enable_class (struct port_class *class)
+{
+ pthread_mutex_lock (&_ports_lock);
+ class->flags &= ~PORT_CLASS_NO_ALLOC;
+ if (class->flags & PORT_CLASS_ALLOC_WAIT)
+ {
+ class->flags &= ~PORT_CLASS_ALLOC_WAIT;
+ pthread_cond_broadcast (&_ports_block);
+ }
+ pthread_mutex_unlock (&_ports_lock);
+}
diff --git a/libports/end-rpc.c b/libports/end-rpc.c
new file mode 100644
index 00000000..b5dcb3a2
--- /dev/null
+++ b/libports/end-rpc.c
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 1995, 1996, 1997, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+
+void
+ports_end_rpc (void *port, struct rpc_info *info)
+{
+ struct port_info *pi = port;
+
+ pthread_mutex_lock (&_ports_lock);
+
+ if (info->notifies)
+ _ports_remove_notified_rpc (info);
+
+ *info->prevp = info->next;
+ if (info->next)
+ info->next->prevp = info->prevp;
+ pi->class->rpcs--;
+ _ports_total_rpcs--;
+ pi->bucket->rpcs--;
+
+ if ((pi->flags & PORT_INHIBIT_WAIT)
+ || (pi->bucket->flags & PORT_BUCKET_INHIBIT_WAIT)
+ || (pi->class->flags & PORT_CLASS_INHIBIT_WAIT)
+ || (_ports_flags & _PORTS_INHIBIT_WAIT))
+ pthread_cond_broadcast (&_ports_block);
+
+ /* This removes the current thread's rpc (which should be INFO) from the
+ ports interrupted list. */
+ ports_self_interrupted ();
+
+ /* Clear the cancellation flag for this thread since the current
+ RPC is now finished anwhow. */
+ hurd_check_cancel ();
+
+ pthread_mutex_unlock (&_ports_lock);
+}
diff --git a/libports/get-right.c b/libports/get-right.c
new file mode 100644
index 00000000..89050c6b
--- /dev/null
+++ b/libports/get-right.c
@@ -0,0 +1,58 @@
+/*
+ Copyright (C) 1995,96,2000 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <mach/notify.h>
+#include <assert.h>
+
+mach_port_t
+ports_get_right (void *port)
+{
+ struct port_info *pi = port;
+ mach_port_t foo;
+ error_t err;
+
+ pthread_mutex_lock (&_ports_lock);
+
+ if (pi->port_right == MACH_PORT_NULL)
+ {
+ pthread_mutex_unlock (&_ports_lock);
+ return MACH_PORT_NULL;
+ }
+
+ pi->mscount++;
+ if ((pi->flags & PORT_HAS_SENDRIGHTS) == 0)
+ {
+ pi->flags |= PORT_HAS_SENDRIGHTS;
+ pi->refcnt++;
+ err = mach_port_request_notification (mach_task_self (),
+ pi->port_right,
+ MACH_NOTIFY_NO_SENDERS,
+ pi->mscount,
+ pi->port_right,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &foo);
+ assert_perror (err);
+ if (foo != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), foo);
+ }
+ pthread_mutex_unlock (&_ports_lock);
+ return pi->port_right;
+}
diff --git a/libports/get-send-right.c b/libports/get-send-right.c
new file mode 100644
index 00000000..3e00276c
--- /dev/null
+++ b/libports/get-send-right.c
@@ -0,0 +1,38 @@
+/* ports_get_send_right -- get a send right to a ports object
+ Copyright (C) 2000 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <assert.h>
+
+mach_port_t
+ports_get_send_right (void *port)
+{
+ error_t err;
+ mach_port_t right;
+
+ right = ports_get_right (port);
+ if (right == MACH_PORT_NULL)
+ return MACH_PORT_NULL;
+
+ err = mach_port_insert_right (mach_task_self (),
+ right, right, MACH_MSG_TYPE_MAKE_SEND);
+ assert_perror (err);
+
+ return right;
+}
diff --git a/libports/import-port.c b/libports/import-port.c
new file mode 100644
index 00000000..226f47e3
--- /dev/null
+++ b/libports/import-port.c
@@ -0,0 +1,115 @@
+/* Create a new port structure using an externally supplied receive right
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <assert.h>
+#include <hurd/ihash.h>
+#include <mach/notify.h>
+
+/* For an existing receive right PORT, create and return in RESULT a new port
+ structure; BUCKET, SIZE, and CLASS args are as for ports_create_port. */
+error_t
+ports_import_port (struct port_class *class, struct port_bucket *bucket,
+ mach_port_t port, size_t size, void *result)
+{
+ error_t err;
+ mach_port_status_t stat;
+ struct port_info *pi;
+ mach_port_t foo;
+
+ err = mach_port_get_receive_status (mach_task_self (), port, &stat);
+ if (err)
+ return err;
+
+ if (size < sizeof (struct port_info))
+ size = sizeof (struct port_info);
+
+ pi = malloc (size);
+ if (! pi)
+ return ENOMEM;
+
+ pi->class = class;
+ pi->refcnt = 1 + !!stat.mps_srights;
+ pi->weakrefcnt = 0;
+ pi->cancel_threshold = 0;
+ pi->mscount = stat.mps_mscount;
+ pi->flags = stat.mps_srights ? PORT_HAS_SENDRIGHTS : 0;
+ pi->port_right = port;
+ pi->current_rpcs = 0;
+ pi->bucket = bucket;
+
+ pthread_mutex_lock (&_ports_lock);
+
+ loop:
+ if (class->flags & PORT_CLASS_NO_ALLOC)
+ {
+ class->flags |= PORT_CLASS_ALLOC_WAIT;
+ if (pthread_hurd_cond_wait_np (&_ports_block, &_ports_lock))
+ goto cancelled;
+ goto loop;
+ }
+ if (bucket->flags & PORT_BUCKET_NO_ALLOC)
+ {
+ bucket->flags |= PORT_BUCKET_ALLOC_WAIT;
+ if (pthread_hurd_cond_wait_np (&_ports_block, &_ports_lock))
+ goto cancelled;
+ goto loop;
+ }
+
+ err = hurd_ihash_add (&bucket->htable, port, pi);
+ if (err)
+ goto lose;
+
+ pi->next = class->ports;
+ pi->prevp = &class->ports;
+ if (class->ports)
+ class->ports->prevp = &pi->next;
+ class->ports = pi;
+ bucket->count++;
+ class->count++;
+ pthread_mutex_unlock (&_ports_lock);
+
+ mach_port_move_member (mach_task_self (), port, bucket->portset);
+
+ if (stat.mps_srights)
+ {
+ err = mach_port_request_notification (mach_task_self (), port,
+ MACH_NOTIFY_NO_SENDERS,
+ stat.mps_mscount,
+ port, MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &foo);
+ assert_perror (err);
+ if (foo != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), foo);
+ }
+
+ *(void **)result = pi;
+ return 0;
+
+ cancelled:
+ err = EINTR;
+ lose:
+ pthread_mutex_unlock (&_ports_lock);
+ free (pi);
+
+ return err;
+}
diff --git a/libports/inhibit-all-rpcs.c b/libports/inhibit-all-rpcs.c
new file mode 100644
index 00000000..d4a54ba5
--- /dev/null
+++ b/libports/inhibit-all-rpcs.c
@@ -0,0 +1,77 @@
+/*
+ Copyright (C) 1995, 1996, 2000 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <hurd.h>
+#include <hurd/ihash.h>
+
+error_t
+ports_inhibit_all_rpcs ()
+{
+ error_t err = 0;
+
+ pthread_mutex_lock (&_ports_lock);
+
+ if (_ports_flags & (_PORTS_INHIBITED | _PORTS_INHIBIT_WAIT))
+ err = EBUSY;
+ else
+ {
+ struct port_bucket *bucket;
+ int this_one = 0;
+
+ for (bucket = _ports_all_buckets; bucket; bucket = bucket->next)
+ {
+ HURD_IHASH_ITERATE (&bucket->htable, portstruct)
+ {
+ struct rpc_info *rpc;
+ struct port_info *pi = portstruct;
+
+ for (rpc = pi->current_rpcs; rpc; rpc = rpc->next)
+ {
+ /* Avoid cancelling the calling thread if it's currently
+ handling a RPC. */
+ if (rpc->thread == hurd_thread_self ())
+ this_one = 1;
+ else
+ hurd_thread_cancel (rpc->thread);
+ }
+ }
+ }
+
+ while (_ports_total_rpcs > this_one)
+ {
+ _ports_flags |= _PORTS_INHIBIT_WAIT;
+ if (pthread_hurd_cond_wait_np (&_ports_block, &_ports_lock))
+ /* We got cancelled. */
+ {
+ err = EINTR;
+ break;
+ }
+ }
+
+ _ports_flags &= ~_PORTS_INHIBIT_WAIT;
+ if (! err)
+ _ports_flags |= _PORTS_INHIBITED;
+ }
+
+ pthread_mutex_unlock (&_ports_lock);
+
+ return err;
+}
diff --git a/libports/inhibit-bucket-rpcs.c b/libports/inhibit-bucket-rpcs.c
new file mode 100644
index 00000000..965aa036
--- /dev/null
+++ b/libports/inhibit-bucket-rpcs.c
@@ -0,0 +1,73 @@
+/*
+ Copyright (C) 1995, 1996, 2000 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <hurd.h>
+#include <hurd/ihash.h>
+
+error_t
+ports_inhibit_bucket_rpcs (struct port_bucket *bucket)
+{
+ error_t err = 0;
+
+ pthread_mutex_lock (&_ports_lock);
+
+ if (bucket->flags & (PORT_BUCKET_INHIBITED | PORT_BUCKET_INHIBIT_WAIT))
+ err = EBUSY;
+ else
+ {
+ int this_one = 0;
+
+ HURD_IHASH_ITERATE (&bucket->htable, portstruct)
+ {
+ struct rpc_info *rpc;
+ struct port_info *pi = portstruct;
+
+ for (rpc = pi->current_rpcs; rpc; rpc = rpc->next)
+ {
+ /* Avoid cancelling the calling thread. */
+ if (rpc->thread == hurd_thread_self ())
+ this_one = 1;
+ else
+ hurd_thread_cancel (rpc->thread);
+ }
+ }
+
+
+ while (bucket->rpcs > this_one)
+ {
+ bucket->flags |= PORT_BUCKET_INHIBIT_WAIT;
+ if (pthread_hurd_cond_wait_np (&_ports_block, &_ports_lock))
+ /* We got cancelled. */
+ {
+ err = EINTR;
+ break;
+ }
+ }
+
+ bucket->flags &= ~PORT_BUCKET_INHIBIT_WAIT;
+ if (! err)
+ bucket->flags |= PORT_BUCKET_INHIBITED;
+ }
+
+ pthread_mutex_unlock (&_ports_lock);
+
+ return err;
+}
diff --git a/libports/inhibit-class-rpcs.c b/libports/inhibit-class-rpcs.c
new file mode 100644
index 00000000..7ee86538
--- /dev/null
+++ b/libports/inhibit-class-rpcs.c
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) 1995, 1996, 2000 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <hurd.h>
+
+error_t
+ports_inhibit_class_rpcs (struct port_class *class)
+{
+ error_t err = 0;
+
+ pthread_mutex_lock (&_ports_lock);
+
+ if (class->flags & (PORT_CLASS_INHIBITED | PORT_CLASS_INHIBIT_WAIT))
+ err = EBUSY;
+ else
+ {
+ struct port_info *pi;
+ struct rpc_info *rpc;
+ int this_one = 0;
+
+ for (pi = class->ports; pi; pi = pi->next)
+ for (rpc = pi->current_rpcs; rpc; rpc = rpc->next)
+ {
+ /* Avoid cancelling the calling thread. */
+ if (rpc->thread == hurd_thread_self ())
+ this_one = 1;
+ else
+ hurd_thread_cancel (rpc->thread);
+ }
+
+ while (class->rpcs > this_one)
+ {
+ class->flags |= PORT_CLASS_INHIBIT_WAIT;
+ if (pthread_hurd_cond_wait_np (&_ports_block, &_ports_lock))
+ /* We got cancelled. */
+ {
+ err = EINTR;
+ break;
+ }
+ }
+
+ class->flags &= ~PORT_CLASS_INHIBIT_WAIT;
+ if (! err)
+ class->flags |= PORT_CLASS_INHIBITED;
+ }
+
+ pthread_mutex_unlock (&_ports_lock);
+
+ return err;
+}
diff --git a/libports/inhibit-port-rpcs.c b/libports/inhibit-port-rpcs.c
new file mode 100644
index 00000000..b741eeb7
--- /dev/null
+++ b/libports/inhibit-port-rpcs.c
@@ -0,0 +1,69 @@
+/*
+ Copyright (C) 1995, 1996, 2000 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <hurd.h>
+
+error_t
+ports_inhibit_port_rpcs (void *portstruct)
+{
+ error_t err = 0;
+ struct port_info *pi = portstruct;
+
+ pthread_mutex_lock (&_ports_lock);
+
+ if (pi->flags & (PORT_INHIBITED | PORT_INHIBIT_WAIT))
+ err = EBUSY;
+ else
+ {
+ struct rpc_info *rpc;
+ struct rpc_info *this_rpc = 0;
+
+ for (rpc = pi->current_rpcs; rpc; rpc = rpc->next)
+ {
+ /* Avoid cancelling the calling thread. */
+ if (rpc->thread == hurd_thread_self ())
+ this_rpc = rpc;
+ else
+ hurd_thread_cancel (rpc->thread);
+ }
+
+ while (pi->current_rpcs
+ /* If this thread's RPC is the only one left, it doesn't count. */
+ && !(pi->current_rpcs == this_rpc && ! this_rpc->next))
+ {
+ pi->flags |= PORT_INHIBIT_WAIT;
+ if (pthread_hurd_cond_wait_np (&_ports_block, &_ports_lock))
+ /* We got cancelled. */
+ {
+ err = EINTR;
+ break;
+ }
+ }
+
+ pi->flags &= ~PORT_INHIBIT_WAIT;
+ if (! err)
+ pi->flags |= PORT_INHIBITED;
+ }
+
+ pthread_mutex_unlock (&_ports_lock);
+
+ return err;
+}
diff --git a/libports/init.c b/libports/init.c
new file mode 100644
index 00000000..3ef53888
--- /dev/null
+++ b/libports/init.c
@@ -0,0 +1,27 @@
+/*
+ Copyright (C) 1995, 2001 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+
+pthread_mutex_t _ports_lock = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t _ports_block = PTHREAD_COND_INITIALIZER;
+struct port_bucket *_ports_all_buckets;
+int _ports_total_rpcs;
+int _ports_flags;
diff --git a/libports/interrupt-notified-rpcs.c b/libports/interrupt-notified-rpcs.c
new file mode 100644
index 00000000..49a15d0c
--- /dev/null
+++ b/libports/interrupt-notified-rpcs.c
@@ -0,0 +1,116 @@
+/* Handle interruping rpcs because of notification
+
+ Copyright (C) 1995, 2001 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+
+/* A linked list of ports for which notification has been requested. */
+struct ports_notify *_ports_notifications;
+
+/* Free lists for notify structures. */
+struct ports_notify *_ports_free_ports_notifies;
+struct rpc_notify *_ports_free_rpc_notifies;
+
+/* Interrupt any rpcs on OBJECT that have requested such. */
+void
+ports_interrupt_notified_rpcs (void *object,
+ mach_port_t port, mach_msg_id_t what)
+{
+ if (_ports_notifications)
+ {
+ struct ports_notify *np;
+
+ pthread_mutex_lock (&_ports_lock);
+ for (np = _ports_notifications; np; np = np->next)
+ if (np->port == port && np->what == what)
+ {
+ struct rpc_notify *req;
+ for (req = np->reqs; req; req = req->next_req)
+ if (req->pending)
+ {
+ req->pending--;
+ hurd_thread_cancel (req->rpc->thread);
+ }
+ break;
+ }
+ pthread_mutex_unlock (&_ports_lock);
+ }
+}
+
+static void
+remove_req (struct rpc_notify *req)
+{
+ struct ports_notify *np = req->notify;
+
+ /* Take REQ out of the list of notified rpcs. */
+ if (req->next_req)
+ req->next_req->prev_req_p = req->prev_req_p;
+ *req->prev_req_p = req->next_req;
+
+ if (np->reqs == 0)
+ /* Now NP has no more reqests, so we can free it too. */
+ {
+ /* Take NP out of the active list... */
+ if (np->next)
+ np->next->prevp = np->prevp;
+ *np->prevp = np->next;
+
+ /* And put it on the free list. */
+ np->next = _ports_free_ports_notifies;
+ _ports_free_ports_notifies = np;
+
+ if (np->pending)
+ /* And cancel the associated notification. */
+ {
+ mach_port_t old;
+ error_t err =
+ mach_port_request_notification (mach_task_self (), np->port,
+ np->what, 0, MACH_PORT_NULL,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &old);
+ if (! err && old != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), old);
+ }
+ }
+}
+
+/* Remove RPC from the list of notified rpcs, cancelling any pending
+ notifications. _PORTS_LOCK should be held. */
+void
+_ports_remove_notified_rpc (struct rpc_info *rpc)
+{
+ struct rpc_notify *req = rpc->notifies;
+
+ if (req)
+ /* Cancel RPC's notify requests. */
+ {
+ struct rpc_notify *last = req;
+
+ while (last->next)
+ {
+ remove_req (last);
+ last = last->next;
+ }
+ remove_req (last);
+
+ /* Put the whole chain on the free list. */
+ last->next = _ports_free_rpc_notifies;
+ _ports_free_rpc_notifies = req;
+ }
+}
diff --git a/libports/interrupt-on-notify.c b/libports/interrupt-on-notify.c
new file mode 100644
index 00000000..b358e840
--- /dev/null
+++ b/libports/interrupt-on-notify.c
@@ -0,0 +1,184 @@
+/* Mark an rpc to be interrupted when a port dies
+
+ Copyright (C) 1995, 96, 99 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <assert.h>
+
+/* Arrange for hurd_cancel to be called on RPC's thread if OBJECT gets notified
+ that any of the things in COND have happened to PORT. RPC should be an
+ rpc on OBJECT. */
+error_t
+ports_interrupt_rpc_on_notification (void *object,
+ struct rpc_info *rpc,
+ mach_port_t port, mach_msg_id_t what)
+{
+ int req_notify;
+ struct ports_notify *pn;
+ struct rpc_notify *new_req, *req;
+ struct port_info *pi = object;
+
+ pthread_mutex_lock (&_ports_lock);
+
+ if (! MACH_PORT_VALID (port))
+ /* PORT is already dead or bogus, so interrupt the rpc immediately. */
+ {
+ hurd_thread_cancel (rpc->thread);
+ pthread_mutex_unlock (&_ports_lock);
+ return 0;
+ }
+
+ new_req = _ports_free_rpc_notifies;
+ if (new_req)
+ /* We got a req off the free list. */
+ _ports_free_rpc_notifies = new_req->next;
+ else
+ /* No free notify structs, allocate one; it's expected that 99% of the
+ time we'll add a new structure, so we malloc while we don't have the
+ lock, and free it if we're wrong. */
+ {
+ pthread_mutex_unlock (&_ports_lock); /* Don't hold the lock during malloc. */
+ new_req = malloc (sizeof (struct rpc_notify));
+ if (! new_req)
+ return ENOMEM;
+ pthread_mutex_lock (&_ports_lock);
+ }
+
+ /* Find any existing entry for PORT/WHAT. */
+ for (pn = _ports_notifications; pn; pn = pn->next)
+ if (pn->port == port && pn->what == what)
+ break;
+
+ if (! pn)
+ /* A notification on a new port. */
+ {
+ pn = _ports_free_ports_notifies;
+
+ if (pn)
+ _ports_free_ports_notifies = pn->next;
+ else
+ {
+ pn = malloc (sizeof (struct ports_notify));
+ if (! pn)
+ /* sigh. Free what we've alloced and return. */
+ {
+ new_req->next = _ports_free_rpc_notifies;
+ _ports_free_rpc_notifies = new_req;
+ pthread_mutex_unlock (&_ports_lock);
+ return ENOMEM;
+ }
+ }
+
+ pn->reqs = 0;
+ pn->port = port;
+ pn->what = what;
+ pn->pending = 0;
+ pthread_mutex_init (&pn->lock, NULL);
+
+ pn->next = _ports_notifications;
+ pn->prevp = &_ports_notifications;
+ if (_ports_notifications)
+ _ports_notifications->prevp = &pn->next;
+ _ports_notifications = pn;
+ }
+
+ for (req = rpc->notifies; req; req = req->next)
+ if (req->notify == pn)
+ break;
+
+ if (req)
+ /* REQ is already pending for PORT/WHAT on RPC, so free NEW_REQ. */
+ {
+ new_req->next = _ports_free_rpc_notifies;
+ _ports_free_rpc_notifies = new_req;
+ }
+ else
+ /* Add a new request for PORT/WHAT on RPC. */
+ {
+ req = new_req;
+
+ req->rpc = rpc;
+ req->notify = pn;
+ req->pending = 0;
+
+ req->next_req = pn->reqs;
+ req->prev_req_p = &pn->reqs;
+ if (pn->reqs)
+ pn->reqs->prev_req_p = &req->next_req;
+ pn->reqs = req;
+
+ req->next = rpc->notifies;
+ rpc->notifies = req;
+ }
+
+ /* Make sure that this request results in an interrupt. */
+ req->pending++;
+
+ /* Find out whether we should request a new notification (after we release
+ _PORTS_LOCK) -- PN may be new, or left over after a previous
+ notification (in which case our new request is likely to trigger an
+ immediate notification). */
+ req_notify = !pn->pending;
+ if (req_notify)
+ pthread_mutex_lock (&pn->lock);
+
+ pthread_mutex_unlock (&_ports_lock);
+
+ if (req_notify)
+ {
+ mach_port_t old;
+ error_t err =
+ mach_port_request_notification (mach_task_self (), port,
+ what, 1, pi->port_right,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &old);
+
+ if (! err && old != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), old);
+
+ pn->pending = 1;
+ pthread_mutex_unlock (&pn->lock);
+
+ return err;
+ }
+ else
+ return 0;
+}
+
+/* Arrange for hurd_cancel to be called on the current thread, which should
+ be an rpc on OBJECT, if PORT gets notified with the condition WHAT. */
+error_t
+ports_interrupt_self_on_notification (void *object,
+ mach_port_t port, mach_msg_id_t what)
+{
+ struct rpc_info *rpc;
+ struct port_info *pi = object;
+ thread_t thread = hurd_thread_self ();
+
+ pthread_mutex_lock (&_ports_lock);
+ for (rpc = pi->current_rpcs; rpc; rpc = rpc->next)
+ if (rpc->thread == thread)
+ break;
+ pthread_mutex_unlock (&_ports_lock);
+
+ assert (rpc);
+
+ /* We don't have to worry about RPC going away after we dropped the lock
+ because we're that thread, and we're still here. */
+ return ports_interrupt_rpc_on_notification (object, rpc, port, what);
+}
diff --git a/libports/interrupt-operation.c b/libports/interrupt-operation.c
new file mode 100644
index 00000000..943bd4f4
--- /dev/null
+++ b/libports/interrupt-operation.c
@@ -0,0 +1,38 @@
+/* interrupt_operation
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include "interrupt_S.h"
+
+/* Cause a pending request on this object to immediately return. The
+ exact semantics are dependent on the specific object. */
+kern_return_t
+ports_S_interrupt_operation (struct port_info *pi,
+ mach_port_seqno_t seqno)
+{
+ if (!pi)
+ return EOPNOTSUPP;
+ pthread_mutex_lock (&_ports_lock);
+ if (pi->cancel_threshold < seqno)
+ pi->cancel_threshold = seqno;
+ pthread_mutex_unlock (&_ports_lock);
+ ports_interrupt_rpcs (pi);
+ return 0;
+}
diff --git a/libports/interrupt-rpcs.c b/libports/interrupt-rpcs.c
new file mode 100644
index 00000000..42f51a5e
--- /dev/null
+++ b/libports/interrupt-rpcs.c
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <hurd.h>
+
+void
+ports_interrupt_rpcs (void *portstruct)
+{
+ struct port_info *pi = portstruct;
+ struct rpc_info *rpc;
+
+ pthread_mutex_lock (&_ports_lock);
+
+ for (rpc = pi->current_rpcs; rpc; rpc = rpc->next)
+ {
+ hurd_thread_cancel (rpc->thread);
+ _ports_record_interruption (rpc);
+ }
+
+ pthread_mutex_unlock (&_ports_lock);
+}
diff --git a/libports/interrupted.c b/libports/interrupted.c
new file mode 100644
index 00000000..88fb9ef0
--- /dev/null
+++ b/libports/interrupted.c
@@ -0,0 +1,75 @@
+/* Keeping track of thread interruption
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "ports.h"
+
+static pthread_spinlock_t interrupted_lock = PTHREAD_SPINLOCK_INITIALIZER;
+
+/* True if some active rpc has been interrupted. */
+static struct rpc_info *interrupted = 0;
+
+/* If the current thread's rpc has been interrupted with
+ ports_interrupt_rpcs, return true (and clear the interrupted flag). */
+int
+ports_self_interrupted ()
+{
+ struct rpc_info **rpc_p, *rpc;
+ thread_t self = hurd_thread_self ();
+
+ pthread_spin_lock (&interrupted_lock);
+ for (rpc_p = &interrupted; *rpc_p; rpc_p = &rpc->interrupted_next)
+ {
+ rpc = *rpc_p;
+ if (rpc->thread == self)
+ {
+ *rpc_p = rpc->interrupted_next;
+ pthread_spin_unlock (&interrupted_lock);
+ rpc->interrupted_next = 0;
+ return 1;
+ }
+ }
+ pthread_spin_unlock (&interrupted_lock);
+
+ return 0;
+}
+
+/* Add RPC to the list of rpcs that have been interrupted. */
+void
+_ports_record_interruption (struct rpc_info *rpc)
+{
+ struct rpc_info *i;
+
+ pthread_spin_lock (&interrupted_lock);
+
+ /* See if RPC is already in the interrupted list. */
+ for (i = interrupted; i; i = i->interrupted_next)
+ if (i == rpc)
+ /* Yup, it is, so just leave it there. */
+ {
+ pthread_spin_unlock (&interrupted_lock);
+ return;
+ }
+
+ /* Nope, put it at the beginning. */
+ rpc->interrupted_next = interrupted;
+ interrupted = rpc;
+
+ pthread_spin_unlock (&interrupted_lock);
+}
diff --git a/libports/lookup-port.c b/libports/lookup-port.c
new file mode 100644
index 00000000..f79f6f0c
--- /dev/null
+++ b/libports/lookup-port.c
@@ -0,0 +1,52 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <hurd/ihash.h>
+
+void *
+ports_lookup_port (struct port_bucket *bucket,
+ mach_port_t port,
+ struct port_class *class)
+{
+ struct port_info *pi = 0;
+
+ pthread_mutex_lock (&_ports_lock);
+
+ if (bucket)
+ pi = hurd_ihash_find (&bucket->htable, port);
+ else
+ for (bucket = _ports_all_buckets; bucket; bucket = bucket->next)
+ {
+ pi = hurd_ihash_find (&bucket->htable, port);
+ if (pi)
+ break;
+ }
+
+ if (pi && class && pi->class != class)
+ pi = 0;
+
+ if (pi)
+ pi->refcnt++;
+
+ pthread_mutex_unlock (&_ports_lock);
+
+ return pi;
+}
diff --git a/libports/manage-multithread.c b/libports/manage-multithread.c
new file mode 100644
index 00000000..2067cbaa
--- /dev/null
+++ b/libports/manage-multithread.c
@@ -0,0 +1,246 @@
+/*
+ Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <assert.h>
+#include <error.h>
+#include <stdio.h>
+#include <mach/message.h>
+#include <mach/thread_info.h>
+#include <mach/thread_switch.h>
+
+#define STACK_SIZE (64 * 1024)
+
+#define THREAD_PRI 2
+
+/* XXX To reduce starvation, the priority of new threads is initially
+ depressed. This helps already existing threads complete their job and be
+ recycled to handle new messages. The duration of this depression is made
+ a function of the total number of threads because more threads imply
+ more contention, and the priority of threads blocking on a contended spin
+ lock is also implicitely depressed.
+
+ Then, if permitted, a greater priority is requested to further decrease
+ the need for additional threads. */
+static void
+adjust_priority (unsigned int totalthreads)
+{
+ mach_port_t host_priv, self, pset, pset_priv;
+ unsigned int t;
+ error_t err;
+
+ t = 10 + (((totalthreads - 1) / 100) + 1) * 10;
+ thread_switch (MACH_PORT_NULL, SWITCH_OPTION_DEPRESS, t);
+
+ err = get_privileged_ports (&host_priv, NULL);
+ if (err)
+ goto error_host_priv;
+
+ self = mach_thread_self ();
+ err = thread_get_assignment (self, &pset);
+ if (err)
+ goto error_pset;
+
+ err = host_processor_set_priv (host_priv, pset, &pset_priv);
+ if (err)
+ goto error_pset_priv;
+
+ err = thread_max_priority (self, pset_priv, 0);
+ if (err)
+ goto error_max_priority;
+
+ err = thread_priority (self, THREAD_PRI, 0);
+ if (err)
+ goto error_priority;
+
+ mach_port_deallocate (mach_task_self (), pset_priv);
+ mach_port_deallocate (mach_task_self (), pset);
+ mach_port_deallocate (mach_task_self (), self);
+ mach_port_deallocate (mach_task_self (), host_priv);
+ return;
+
+error_priority:
+error_max_priority:
+ mach_port_deallocate (mach_task_self (), pset_priv);
+error_pset_priv:
+ mach_port_deallocate (mach_task_self (), pset);
+error_pset:
+ mach_port_deallocate (mach_task_self (), self);
+ mach_port_deallocate (mach_task_self (), host_priv);
+error_host_priv:
+ if (err != EPERM)
+ error (0, err, "unable to adjust libports thread priority");
+}
+
+void
+ports_manage_port_operations_multithread (struct port_bucket *bucket,
+ ports_demuxer_type demuxer,
+ int thread_timeout,
+ int global_timeout,
+ void (*hook)())
+{
+ /* totalthreads is the number of total threads created. nreqthreads
+ is the number of threads not currently servicing any client. The
+ initial values account for the main thread. */
+ unsigned int totalthreads = 1;
+ unsigned int nreqthreads = 1;
+
+ pthread_attr_t attr;
+
+ auto void * thread_function (void *);
+
+ pthread_attr_init (&attr);
+ pthread_attr_setstacksize (&attr, STACK_SIZE);
+
+ int
+ internal_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outheadp)
+ {
+ int status;
+ struct port_info *pi;
+ struct rpc_info link;
+ register mig_reply_header_t *outp = (mig_reply_header_t *) outheadp;
+ static const mach_msg_type_t RetCodeType = {
+ /* msgt_name = */ MACH_MSG_TYPE_INTEGER_32,
+ /* msgt_size = */ 32,
+ /* msgt_number = */ 1,
+ /* msgt_inline = */ TRUE,
+ /* msgt_longform = */ FALSE,
+ /* msgt_deallocate = */ FALSE,
+ /* msgt_unused = */ 0
+ };
+
+ if (__atomic_sub_fetch (&nreqthreads, 1, __ATOMIC_RELAXED) == 0)
+ /* No thread would be listening for requests, spawn one. */
+ {
+ pthread_t pthread_id;
+ error_t err;
+
+ __atomic_add_fetch (&totalthreads, 1, __ATOMIC_RELAXED);
+ __atomic_add_fetch (&nreqthreads, 1, __ATOMIC_RELAXED);
+
+ err = pthread_create (&pthread_id, &attr, thread_function, NULL);
+ if (!err)
+ pthread_detach (pthread_id);
+ else
+ {
+ __atomic_sub_fetch (&totalthreads, 1, __ATOMIC_RELAXED);
+ __atomic_sub_fetch (&nreqthreads, 1, __ATOMIC_RELAXED);
+ /* There is not much we can do at this point. The code
+ and design of the Hurd servers just don't handle
+ thread creation failure. */
+ errno = err;
+ perror ("pthread_create");
+ }
+ }
+
+ /* Fill in default response. */
+ outp->Head.msgh_bits
+ = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(inp->msgh_bits), 0);
+ outp->Head.msgh_size = sizeof *outp;
+ outp->Head.msgh_remote_port = inp->msgh_remote_port;
+ outp->Head.msgh_local_port = MACH_PORT_NULL;
+ outp->Head.msgh_seqno = 0;
+ outp->Head.msgh_id = inp->msgh_id + 100;
+ outp->RetCodeType = RetCodeType;
+ outp->RetCode = MIG_BAD_ID;
+
+ pi = ports_lookup_port (bucket, inp->msgh_local_port, 0);
+ if (pi)
+ {
+ error_t err = ports_begin_rpc (pi, inp->msgh_id, &link);
+ if (err)
+ {
+ outp->RetCode = err;
+ status = 1;
+ }
+ else
+ {
+ pthread_mutex_lock (&_ports_lock);
+ if (inp->msgh_seqno < pi->cancel_threshold)
+ hurd_thread_cancel (link.thread);
+ pthread_mutex_unlock (&_ports_lock);
+ status = demuxer (inp, outheadp);
+ ports_end_rpc (pi, &link);
+ }
+ ports_port_deref (pi);
+ }
+ else
+ {
+ outp->RetCode = EOPNOTSUPP;
+ status = 1;
+ }
+
+ __atomic_add_fetch (&nreqthreads, 1, __ATOMIC_RELAXED);
+
+ return status;
+ }
+
+ void *
+ thread_function (void *arg)
+ {
+ int master = (int) arg;
+ int timeout;
+ error_t err;
+
+ adjust_priority (__atomic_load_n (&totalthreads, __ATOMIC_RELAXED));
+
+ if (hook)
+ (*hook) ();
+
+ if (master)
+ timeout = global_timeout;
+ else
+ timeout = thread_timeout;
+
+ startover:
+
+ do
+ err = mach_msg_server_timeout (internal_demuxer, 0, bucket->portset,
+ timeout ? MACH_RCV_TIMEOUT : 0,
+ timeout);
+ while (err != MACH_RCV_TIMED_OUT);
+
+ if (master)
+ {
+ if (__atomic_load_n (&totalthreads, __ATOMIC_RELAXED) != 1)
+ goto startover;
+ }
+ else
+ {
+ if (__atomic_sub_fetch (&nreqthreads, 1, __ATOMIC_RELAXED) == 0)
+ {
+ /* No other thread is listening for requests, continue. */
+ __atomic_add_fetch (&nreqthreads, 1, __ATOMIC_RELAXED);
+ goto startover;
+ }
+ __atomic_sub_fetch (&totalthreads, 1, __ATOMIC_RELAXED);
+ }
+ return NULL;
+ }
+
+ /* XXX It is currently unsafe for most servers to terminate based on
+ inactivity because a request may arrive after a server has started
+ shutting down, causing the client to receive an error. Prevent the
+ master thread from going away. */
+ global_timeout = 0;
+
+ thread_function ((void *) 1);
+}
diff --git a/libports/manage-one-thread.c b/libports/manage-one-thread.c
new file mode 100644
index 00000000..cbd2df7d
--- /dev/null
+++ b/libports/manage-one-thread.c
@@ -0,0 +1,100 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Writtenb by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+
+void
+ports_manage_port_operations_one_thread (struct port_bucket *bucket,
+ ports_demuxer_type demuxer,
+ int timeout)
+{
+ error_t err;
+
+ int
+ internal_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outheadp)
+ {
+ struct port_info *pi;
+ struct rpc_info link;
+ int status;
+ error_t err;
+ register mig_reply_header_t *outp = (mig_reply_header_t *) outheadp;
+ static const mach_msg_type_t RetCodeType = {
+ /* msgt_name = */ MACH_MSG_TYPE_INTEGER_32,
+ /* msgt_size = */ 32,
+ /* msgt_number = */ 1,
+ /* msgt_inline = */ TRUE,
+ /* msgt_longform = */ FALSE,
+ /* msgt_deallocate = */ FALSE,
+ /* msgt_unused = */ 0
+ };
+
+ /* Fill in default response. */
+ outp->Head.msgh_bits
+ = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(inp->msgh_bits), 0);
+ outp->Head.msgh_size = sizeof *outp;
+ outp->Head.msgh_remote_port = inp->msgh_remote_port;
+ outp->Head.msgh_local_port = MACH_PORT_NULL;
+ outp->Head.msgh_seqno = 0;
+ outp->Head.msgh_id = inp->msgh_id + 100;
+ outp->RetCodeType = RetCodeType;
+ outp->RetCode = MIG_BAD_ID;
+
+ pi = ports_lookup_port (bucket, inp->msgh_local_port, 0);
+ if (pi)
+ {
+ err = ports_begin_rpc (pi, inp->msgh_id, &link);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), inp->msgh_remote_port);
+ outp->RetCode = err;
+ status = 1;
+ }
+ else
+ {
+ /* No need to check cancel threshold here, because
+ in a single threaded server the cancel is always
+ handled in order. */
+ status = demuxer (inp, outheadp);
+ ports_end_rpc (pi, &link);
+ }
+ ports_port_deref (pi);
+ }
+ else
+ {
+ outp->RetCode = EOPNOTSUPP;
+ status = 1;
+ }
+
+ return status;
+ }
+
+ /* XXX It is currently unsafe for most servers to terminate based on
+ inactivity because a request may arrive after a server has
+ started shutting down, causing the client to receive an error.
+ Prevent the service loop from terminating by setting TIMEOUT to
+ zero. */
+ timeout = 0;
+
+ do
+ err = mach_msg_server_timeout (internal_demuxer, 0, bucket->portset,
+ timeout ? MACH_RCV_TIMEOUT : 0, timeout);
+ while (err != MACH_RCV_TIMED_OUT);
+}
diff --git a/libports/mig-decls.h b/libports/mig-decls.h
new file mode 100644
index 00000000..f8c4f15c
--- /dev/null
+++ b/libports/mig-decls.h
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Justus Winter.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __LIBPORTS_MIG_DECLS_H__
+#define __LIBPORTS_MIG_DECLS_H__
+
+#include "ports.h"
+
+/* Called by server stub functions. */
+
+static inline struct port_info * __attribute__ ((unused))
+begin_using_port_info_port (mach_port_t port)
+{
+ return ports_lookup_port (0, port, 0);
+}
+
+static inline void __attribute__ ((unused))
+end_using_port_info (struct port_info *p)
+{
+ if (p)
+ ports_port_deref (p);
+}
+
+#endif /* __LIBPORTS_MIG_DECLS_H__ */
diff --git a/libports/mig-mutate.h b/libports/mig-mutate.h
new file mode 100644
index 00000000..4c011b6a
--- /dev/null
+++ b/libports/mig-mutate.h
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Justus Winter.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#define NOTIFY_INTRAN \
+ port_info_t begin_using_port_info_port (mach_port_t)
+#define NOTIFY_DESTRUCTOR \
+ end_using_port_info (port_info_t)
+#define NOTIFY_IMPORTS \
+ import "libports/mig-decls.h";
+
+#define INTERRUPT_INTRAN \
+ port_info_t begin_using_port_info_port (mach_port_t)
+#define INTERRUPT_DESTRUCTOR \
+ end_using_port_info (port_info_t)
+#define INTERRUPT_IMPORTS \
+ import "libports/mig-decls.h";
diff --git a/libports/no-senders.c b/libports/no-senders.c
new file mode 100644
index 00000000..1a6084b1
--- /dev/null
+++ b/libports/no-senders.c
@@ -0,0 +1,65 @@
+/*
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <mach/notify.h>
+
+void
+ports_no_senders (void *portstruct,
+ mach_port_mscount_t mscount)
+{
+ struct port_info *pi = portstruct;
+ int dealloc;
+ mach_port_t old;
+
+ pthread_mutex_lock (&_ports_lock);
+ if ((pi->flags & PORT_HAS_SENDRIGHTS) == 0)
+ {
+ pthread_mutex_unlock (&_ports_lock);
+ return;
+ }
+ if (mscount >= pi->mscount)
+ {
+ dealloc = 1;
+ pi->flags &= ~PORT_HAS_SENDRIGHTS;
+ }
+ else
+ {
+ /* Request a new notification. The sync value is because
+ we might have accounted for a new sender but not actually
+ made the send right yet. */
+ mach_port_request_notification (mach_task_self (), pi->port_right,
+ MACH_NOTIFY_NO_SENDERS, pi->mscount,
+ pi->port_right,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &old);
+ if (old)
+ mach_port_deallocate (mach_task_self (), old);
+ dealloc = 0;
+ }
+ pthread_mutex_unlock (&_ports_lock);
+
+ if (dealloc)
+ {
+ ports_interrupt_notified_rpcs (portstruct, pi->port_right,
+ MACH_NOTIFY_NO_SENDERS);
+ ports_interrupt_rpcs (pi);
+ ports_port_deref (pi);
+ }
+}
diff --git a/libports/notify-dead-name.c b/libports/notify-dead-name.c
new file mode 100644
index 00000000..f974e331
--- /dev/null
+++ b/libports/notify-dead-name.c
@@ -0,0 +1,36 @@
+/* Dead name notification
+
+ Copyright (C) 1995, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include "notify_S.h"
+
+error_t
+ports_do_mach_notify_dead_name (struct port_info *pi,
+ mach_port_t dead_name)
+{
+ if (!pi)
+ return EOPNOTSUPP;
+ ports_dead_name (pi, dead_name);
+
+ /* Drop gratuitous extra reference that the notification creates. */
+ mach_port_deallocate (mach_task_self (), dead_name);
+
+ return 0;
+}
diff --git a/libports/notify-msg-accepted.c b/libports/notify-msg-accepted.c
new file mode 100644
index 00000000..0e49715d
--- /dev/null
+++ b/libports/notify-msg-accepted.c
@@ -0,0 +1,29 @@
+/* Msg accepted notification
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include "notify_S.h"
+
+error_t
+ports_do_mach_notify_msg_accepted (struct port_info *pi,
+ mach_port_t name)
+{
+ return 0;
+}
diff --git a/libports/notify-no-senders.c b/libports/notify-no-senders.c
new file mode 100644
index 00000000..55aa853f
--- /dev/null
+++ b/libports/notify-no-senders.c
@@ -0,0 +1,32 @@
+/* No sender notification
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include "notify_S.h"
+
+error_t
+ports_do_mach_notify_no_senders (struct port_info *pi,
+ mach_port_mscount_t count)
+{
+ if (!pi)
+ return EOPNOTSUPP;
+ ports_no_senders (pi, count);
+ return 0;
+}
diff --git a/libports/notify-port-deleted.c b/libports/notify-port-deleted.c
new file mode 100644
index 00000000..cfd33793
--- /dev/null
+++ b/libports/notify-port-deleted.c
@@ -0,0 +1,29 @@
+/* Port deleted notification
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include "notify_S.h"
+
+error_t
+ports_do_mach_notify_port_deleted (struct port_info *pi,
+ mach_port_t name)
+{
+ return 0;
+}
diff --git a/libports/notify-port-destroyed.c b/libports/notify-port-destroyed.c
new file mode 100644
index 00000000..b8ece2a3
--- /dev/null
+++ b/libports/notify-port-destroyed.c
@@ -0,0 +1,29 @@
+/* Port destroyed notification
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include "notify_S.h"
+
+error_t
+ports_do_mach_notify_port_destroyed (struct port_info *pi,
+ mach_port_t name)
+{
+ return 0;
+}
diff --git a/libports/notify-send-once.c b/libports/notify-send-once.c
new file mode 100644
index 00000000..ad0ba334
--- /dev/null
+++ b/libports/notify-send-once.c
@@ -0,0 +1,28 @@
+/* Send once notification
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include "notify_S.h"
+
+error_t
+ports_do_mach_notify_send_once (struct port_info *pi)
+{
+ return 0;
+}
diff --git a/libports/port-deref-weak.c b/libports/port-deref-weak.c
new file mode 100644
index 00000000..beb48423
--- /dev/null
+++ b/libports/port-deref-weak.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <assert.h>
+
+void
+ports_port_deref_weak (void *portstruct)
+{
+ struct port_info *pi = portstruct;
+
+ pthread_mutex_lock (&_ports_lock);
+ assert (pi->weakrefcnt);
+ pi->weakrefcnt--;
+ if (pi->refcnt == 0 && pi->weakrefcnt == 0)
+ _ports_complete_deallocate (pi);
+ else
+ pthread_mutex_unlock (&_ports_lock);
+}
diff --git a/libports/port-deref.c b/libports/port-deref.c
new file mode 100644
index 00000000..cf9b2383
--- /dev/null
+++ b/libports/port-deref.c
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 1995, 2001 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <assert.h>
+
+void
+ports_port_deref (void *portstruct)
+{
+ struct port_info *pi = portstruct;
+ int trieddroppingweakrefs = 0;
+
+ retry:
+
+ pthread_mutex_lock (&_ports_lock);
+
+ if (pi->refcnt == 1 && pi->weakrefcnt
+ && pi->class->dropweak_routine && !trieddroppingweakrefs)
+ {
+ pthread_mutex_unlock (&_ports_lock);
+ (*pi->class->dropweak_routine) (pi);
+ trieddroppingweakrefs = 1;
+ goto retry;
+ }
+
+ assert (pi->refcnt);
+
+ pi->refcnt--;
+ if (pi->refcnt == 0 && pi->weakrefcnt == 0)
+ _ports_complete_deallocate (pi);
+ else
+ pthread_mutex_unlock (&_ports_lock);
+}
diff --git a/libports/port-ref-weak.c b/libports/port-ref-weak.c
new file mode 100644
index 00000000..c7d3c690
--- /dev/null
+++ b/libports/port-ref-weak.c
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <assert.h>
+
+void
+ports_port_ref_weak (void *portstruct)
+{
+ struct port_info *pi = portstruct;
+
+ pthread_mutex_lock (&_ports_lock);
+ assert (pi->refcnt || pi->weakrefcnt);
+ pi->weakrefcnt++;
+ pthread_mutex_unlock (&_ports_lock);
+}
diff --git a/libports/port-ref.c b/libports/port-ref.c
new file mode 100644
index 00000000..92b71183
--- /dev/null
+++ b/libports/port-ref.c
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <assert.h>
+
+void
+ports_port_ref (void *portstruct)
+{
+ struct port_info *pi = portstruct;
+
+ pthread_mutex_lock (&_ports_lock);
+ assert (pi->refcnt || pi->weakrefcnt);
+ pi->refcnt++;
+ pthread_mutex_unlock (&_ports_lock);
+}
diff --git a/libports/ports.h b/libports/ports.h
new file mode 100644
index 00000000..7f131240
--- /dev/null
+++ b/libports/ports.h
@@ -0,0 +1,415 @@
+/* Ports library for server construction
+ Copyright (C) 1993,94,95,96,97,99,2000 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _HURD_PORTS_
+#define _HURD_PORTS_
+
+#include <mach.h>
+#include <stdlib.h>
+#include <hurd.h>
+#include <hurd/ihash.h>
+#include <mach/notify.h>
+#include <pthread.h>
+
+/* These are global values for common flags used in the various structures.
+ Not all of these are meaningful in all flag fields. */
+#define PORTS_INHIBITED 0x0100 /* block RPC's */
+#define PORTS_BLOCKED 0x0200 /* if INHIBITED, someone is blocked */
+#define PORTS_INHIBIT_WAIT 0x0400 /* someone wants to start inhibit */
+#define PORTS_NO_ALLOC 0x0800 /* block allocation */
+#define PORTS_ALLOC_WAIT 0x1000 /* someone wants to allocate */
+
+struct port_info
+{
+ struct port_class *class;
+ int refcnt;
+ int weakrefcnt;
+ mach_port_mscount_t mscount;
+ mach_msg_seqno_t cancel_threshold;
+ int flags;
+ mach_port_t port_right;
+ struct rpc_info *current_rpcs;
+ struct port_bucket *bucket;
+ hurd_ihash_locp_t hentry;
+ struct port_info *next, **prevp; /* links on port_class list */
+};
+typedef struct port_info *port_info_t;
+
+/* FLAGS above are the following: */
+#define PORT_HAS_SENDRIGHTS 0x0001 /* send rights extant */
+#define PORT_INHIBITED PORTS_INHIBITED
+#define PORT_BLOCKED PORTS_BLOCKED
+#define PORT_INHIBIT_WAIT PORTS_INHIBIT_WAIT
+
+struct port_bucket
+{
+ mach_port_t portset;
+ struct hurd_ihash htable;
+ int rpcs;
+ int flags;
+ int count;
+ struct port_bucket *next;
+};
+/* FLAGS above are the following: */
+#define PORT_BUCKET_INHIBITED PORTS_INHIBITED
+#define PORT_BUCKET_BLOCKED PORTS_BLOCKED
+#define PORT_BUCKET_INHIBIT_WAIT PORTS_INHIBIT_WAIT
+#define PORT_BUCKET_NO_ALLOC PORTS_NO_ALLOC
+#define PORT_BUCKET_ALLOC_WAIT PORTS_ALLOC_WAIT
+
+struct port_class
+{
+ int flags;
+ int rpcs;
+ struct port_info *ports;
+ int count;
+ void (*clean_routine) (void *);
+ void (*dropweak_routine) (void *);
+ struct ports_msg_id_range *uninhibitable_rpcs;
+};
+/* FLAGS are the following: */
+#define PORT_CLASS_INHIBITED PORTS_INHIBITED
+#define PORT_CLASS_BLOCKED PORTS_BLOCKED
+#define PORT_CLASS_INHIBIT_WAIT PORTS_INHIBIT_WAIT
+#define PORT_CLASS_NO_ALLOC PORTS_NO_ALLOC
+#define PORT_CLASS_ALLOC_WAIT PORTS_ALLOC_WAIT
+
+struct rpc_info
+{
+ thread_t thread;
+ struct rpc_info *next, **prevp;
+ struct rpc_notify *notifies;
+ struct rpc_info *interrupted_next;
+};
+
+/* An rpc has requested interruption on a port notification. */
+struct rpc_notify
+{
+ struct rpc_info *rpc; /* Which rpc this is for. */
+ struct ports_notify *notify; /* Which port/request this refers too. */
+
+ struct rpc_notify *next; /* Notify for this rpc. */
+ unsigned pending; /* Number of requests this represents. */
+
+ struct rpc_notify *next_req; /* rpc for this notify. */
+ struct rpc_notify **prev_req_p; /* who points to this rpc_notify. */
+};
+
+/* A notification request on a (not necessarily registered) port. */
+struct ports_notify
+{
+ mach_port_t port; /* */
+ mach_msg_id_t what; /* MACH_NOTIFY_* */
+ unsigned pending : 1; /* There's a notification outstanding. */
+ pthread_mutex_t lock;
+
+ struct rpc_notify *reqs; /* Which rpcs are notified by this port. */
+ struct ports_notify *next, **prevp; /* Linked list of all notified ports. */
+};
+
+/* A linked list of ports that have had notification requested. */
+extern struct ports_notify *_ports_notifications;
+
+/* Free lists for notify structures. */
+extern struct ports_notify *_ports_free_ports_notifies;
+extern struct rpc_notify *_ports_free_rpc_notifies;
+
+/* Remove RPC from the list of notified rpcs, cancelling any pending
+ notifications. _PORTS_LOCK should be held. */
+void _ports_remove_notified_rpc (struct rpc_info *rpc);
+
+struct ports_msg_id_range
+{
+ mach_msg_id_t start, end;
+ struct ports_msg_id_range *next;
+};
+
+/* This is the initial value for the uninhibitable_rpcs field in new
+ port_class structures. The user may define this variable; the default
+ value contains only an entry for interrupt_operation. */
+extern struct ports_msg_id_range *ports_default_uninhibitable_rpcs;
+
+/* Port creation and port right frobbing */
+
+/* Create and return a new bucket. */
+struct port_bucket *ports_create_bucket (void);
+
+/* Create and return a new port class. If nonzero, CLEAN_ROUTINE will
+ be called for each allocated port object in this class when it is
+ being destroyed. If nonzero, DROPWEAK_ROUTINE will be called
+ to request weak references to be dropped. (If DROPWEAK_ROUTINE is null,
+ then normal references and hard references will be identical for
+ ports of this class.) */
+struct port_class *ports_create_class (void (*clean_routine)(void *),
+ void (*dropweak_routine)(void *));
+
+/* Create and return in RESULT a new port in CLASS and BUCKET; SIZE bytes
+ will be allocated to hold the port structure and whatever private data the
+ user desires. */
+error_t ports_create_port (struct port_class *class,
+ struct port_bucket *bucket,
+ size_t size,
+ void *result);
+
+/* Just like ports_create_port, except don't actually put the port
+ into the portset underlying BUCKET. This is intended to be used
+ for cases where the port right must be given out before the port is
+ fully initialized; with this call you are guaranteed that no RPC
+ service will occur on the port until you have finished initializing
+ it and installed it into the portset yourself. */
+error_t
+ports_create_port_noinstall (struct port_class *class,
+ struct port_bucket *bucket,
+ size_t size,
+ void *result);
+
+/* For an existing RECEIVE right, create and return in RESULT a new port
+ structure; BUCKET, SIZE, and CLASS args are as for ports_create_port. */
+error_t ports_import_port (struct port_class *class,
+ struct port_bucket *bucket,
+ mach_port_t port, size_t size,
+ void *result);
+
+/* Destroy the receive right currently associated with PORT and allocate
+ a new one. */
+void ports_reallocate_port (void *port);
+
+/* Destroy the receive right currently associated with PORT and designate
+ RECEIVE as the new one. */
+void ports_reallocate_from_external (void *port, mach_port_t receive);
+
+/* Destroy the receive right currently associated with PORT. After
+ this call, ports_reallocate_port and ports_reallocate_from_external
+ may not be used. Always returns 0, for convenient use as an iterator. */
+error_t ports_destroy_right (void *port);
+
+/* Return the receive right currently associated with PORT. The effects
+ on PORT are the same as in ports_destroy_right, except that the receive
+ right itself is not affected. Note that in multi-threaded servers,
+ messages might already have been dequeued for this port before it gets
+ removed from the portset; such messages will get EOPNOTSUPP errors. */
+mach_port_t ports_claim_right (void *port);
+
+/* Transfer the receive right from FROMPT to TOPT. FROMPT ends up
+ with a destroyed right (as if ports_destroy_right were called) and
+ TOPT's old right is destroyed (as if ports_reallocate_from_external
+ were called. */
+error_t ports_transfer_right (void *topt, void *frompt);
+
+/* Return the name of the receive right associated with PORT. This assumes
+ that send rights will shortly be created, and arranges for notifications
+ accordingly. The user is responsible for creating an ordinary send
+ right from this name. */
+mach_port_t ports_get_right (void *port);
+
+/* This convenience function uses ports_get_right, and
+ deals with the creation of a send right as well. */
+mach_port_t ports_get_send_right (void *port);
+
+
+/* Reference counting */
+
+/* Look up PORT and return the associated port structure, allocating a
+ reference. If the call fails, return 0. If BUCKET is nonzero,
+ then it specifies a bucket to search; otherwise all buckets will be
+ searched. If CLASS is nonzero, then the lookup will fail if PORT
+ is not in CLASS. */
+void *ports_lookup_port (struct port_bucket *bucket,
+ mach_port_t port, struct port_class *class);
+
+/* Allocate another reference to PORT. */
+void ports_port_ref (void *port);
+
+/* Allocate a weak reference to PORT. */
+void ports_port_ref_weak (void *port);
+
+/* Drop a reference to PORT. */
+void ports_port_deref (void *port);
+
+/* Drop a weak reference to PORT. */
+void ports_port_deref_weak (void *port);
+
+/* The user is responsible for listening for no senders notifications;
+ when one arrives, call this routine for the PORT the message was
+ sent to, providing the MSCOUNT from the notification. */
+void ports_no_senders (void *port, mach_port_mscount_t mscount);
+void ports_dead_name (void *notify, mach_port_t dead_name);
+
+/* Block port creation of new ports in CLASS. Return the number
+ of ports currently in CLASS. */
+int ports_count_class (struct port_class *class);
+
+/* Block port creation of new ports in BUCKET. Return the number
+ of ports currently in BUCKET. */
+int ports_count_bucket (struct port_bucket *bucket);
+
+/* Permit suspended port creation (blocked by ports_count_class)
+ to continue. */
+void ports_enable_class (struct port_class *class);
+
+/* Permit suspend port creation (blocked by ports_count_bucket)
+ to continue. */
+void ports_enable_bucket (struct port_bucket *bucket);
+
+/* Call FUN once for each port in BUCKET. */
+error_t ports_bucket_iterate (struct port_bucket *bucket,
+ error_t (*fun)(void *port));
+
+/* Call FUN once for each port in CLASS. */
+error_t ports_class_iterate (struct port_class *class,
+ error_t (*fun)(void *port));
+
+/* Internal entrypoint for above two. */
+error_t _ports_bucket_class_iterate (struct port_bucket *bucket,
+ struct port_class *class,
+ error_t (*fun)(void *port));
+
+/* RPC management */
+
+/* Type of MiG demuxer routines. */
+typedef int (*ports_demuxer_type)(mach_msg_header_t *inp,
+ mach_msg_header_t *outp);
+
+/* Call this when an RPC is beginning on PORT. INFO should be
+ allocated by the caller and will be used to hold dynamic state.
+ If this RPC should be abandoned, return EDIED; otherwise we
+ return zero. */
+error_t ports_begin_rpc (void *port, mach_msg_id_t msg_id,
+ struct rpc_info *info);
+
+/* Call this when an RPC is concluding. Args must be as for the
+ paired call to ports_begin_rpc. */
+void ports_end_rpc (void *port, struct rpc_info *info);
+
+/* Begin handling operations for the ports in BUCKET, calling DEMUXER
+ for each incoming message. Return if TIMEOUT is nonzero and no
+ messages have been received for TIMEOUT milliseconds. Use
+ only one thread (the calling thread). */
+void ports_manage_port_operations_one_thread(struct port_bucket *bucket,
+ ports_demuxer_type demuxer,
+ int timeout);
+
+/* Begin handling operations for the ports in BUCKET, calling DEMUXER
+ for each incoming message. Return if GLOBAL_TIMEOUT is nonzero and
+ no messages have been receieved for GLOBAL_TIMEOUT milliseconds.
+ Create threads as necessary to handle incoming messages so that no
+ port is starved because of sluggishness on another port. If
+ LOCAL_TIMEOUT is non-zero, then individual threads will die off if
+ they handle no incoming messages for LOCAL_TIMEOUT milliseconds.
+ HOOK (if not null) will be called in each new thread immediately
+ after it is created. */
+void ports_manage_port_operations_multithread (struct port_bucket *bucket,
+ ports_demuxer_type demuxer,
+ int thread_timeout,
+ int global_timeout,
+ void (*hook)(void));
+
+/* Interrupt any pending RPC on PORT. Wait for all pending RPC's to
+ finish, and then block any new RPC's starting on that port. */
+error_t ports_inhibit_port_rpcs (void *port);
+
+/* Similar to ports_inhibit_port_rpcs, but affects all ports in CLASS. */
+error_t ports_inhibit_class_rpcs (struct port_class *class);
+
+/* Similar to ports_inhibit_port_rpcs, but affects all ports in BUCKET. */
+error_t ports_inhibit_bucket_rpcs (struct port_bucket *bucket);
+
+/* Similar to ports_inhibit_port_rpcs, but affects all ports whatsoever. */
+error_t ports_inhibit_all_rpcs (void);
+
+/* Reverse the effect of a previous ports_inhibit_port_rpcs for this PORT,
+ allowing blocked RPC's to continue. */
+void ports_resume_port_rpcs (void *port);
+
+/* Reverse the effect of a previous ports_inhibit_class_rpcs for CLASS. */
+void ports_resume_class_rpcs (struct port_class *class);
+
+/* Reverse the effect of a previous ports_inhibit_bucket_rpcs for BUCKET. */
+void ports_resume_bucket_rpcs (struct port_bucket *bucket);
+
+/* Reverse the effect of a previous ports_inhibit_all_rpcs. */
+void ports_resume_all_rpcs (void);
+
+/* Cancel (with thread_cancel) any RPC's in progress on PORT. */
+void ports_interrupt_rpcs (void *port);
+
+/* If the current thread's rpc has been interrupted with
+ ports_interrupt_rpcs, return true (and clear the interrupted flag). */
+int ports_self_interrupted ();
+
+/* Add RPC to the list of rpcs that have been interrupted. */
+void _ports_record_interruption (struct rpc_info *rpc);
+
+/* Arrange for hurd_cancel to be called on RPC's thread if OBJECT gets notified
+ that any of the things in COND have happened to PORT. RPC should be an
+ rpc on OBJECT. */
+error_t
+ports_interrupt_rpc_on_notification (void *object,
+ struct rpc_info *rpc,
+ mach_port_t port, mach_msg_id_t what);
+
+/* Arrange for hurd_cancel to be called on the current thread, which should
+ be an rpc on OBJECT, if PORT gets notified with the condition WHAT. */
+error_t
+ports_interrupt_self_on_notification (void *object,
+ mach_port_t port, mach_msg_id_t what);
+
+/* Some handy aliases. */
+#define ports_interrupt_self_on_port_death(obj, port) \
+ ports_interrupt_self_on_notification (obj, port, MACH_NOTIFY_DEAD_NAME)
+
+/* Interrupt any rpcs on OBJECT that have requested such. */
+void ports_interrupt_notified_rpcs (void *object, mach_port_t port,
+ mach_msg_id_t what);
+
+/* Default servers */
+
+/* A notification server that calls the ports_do_mach_notify_* routines. */
+int ports_notify_server (mach_msg_header_t *, mach_msg_header_t *);
+
+/* Notification server routines called by ports_notify_server. */
+extern kern_return_t
+ ports_do_mach_notify_dead_name (struct port_info *pi, mach_port_t deadport);
+extern kern_return_t
+ ports_do_mach_notify_msg_accepted (struct port_info *pi, mach_port_t name);
+extern kern_return_t
+ ports_do_mach_notify_no_senders (struct port_info *pi,
+ mach_port_mscount_t count);
+extern kern_return_t
+ ports_do_mach_notify_port_deleted (struct port_info *pi, mach_port_t name);
+extern kern_return_t
+ ports_do_mach_notify_port_destroyed (struct port_info *pi, mach_port_t name);
+extern kern_return_t
+ ports_do_mach_notify_send_once (struct port_info *pi);
+
+/* Private data */
+extern pthread_mutex_t _ports_lock;
+extern pthread_cond_t _ports_block;
+extern struct port_bucket *_ports_all_buckets;
+extern int _ports_total_rpcs;
+extern int _ports_flags;
+#define _PORTS_INHIBITED PORTS_INHIBITED
+#define _PORTS_BLOCKED PORTS_BLOCKED
+#define _PORTS_INHIBIT_WAIT PORTS_INHIBIT_WAIT
+void _ports_complete_deallocate (struct port_info *);
+error_t _ports_create_port_internal (struct port_class *, struct port_bucket *,
+ size_t, void *, int);
+
+#endif
diff --git a/libports/reallocate-from-external.c b/libports/reallocate-from-external.c
new file mode 100644
index 00000000..8cccb2a7
--- /dev/null
+++ b/libports/reallocate-from-external.c
@@ -0,0 +1,83 @@
+/*
+ Copyright (C) 1995, 1996, 2003 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <assert.h>
+#include <hurd/ihash.h>
+#include <mach/notify.h>
+
+void
+ports_reallocate_from_external (void *portstruct, mach_port_t receive)
+{
+ struct port_info *pi = portstruct;
+ mach_port_status_t stat;
+ int dropref = 0;
+ mach_port_t foo;
+ error_t err;
+
+ err = mach_port_get_receive_status (mach_task_self (), receive, &stat);
+ assert_perror (err);
+
+ pthread_mutex_lock (&_ports_lock);
+
+ assert (pi->port_right);
+
+ err = mach_port_mod_refs (mach_task_self (), pi->port_right,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+ assert_perror (err);
+
+ hurd_ihash_locp_remove (&pi->bucket->htable, pi->hentry);
+
+ if ((pi->flags & PORT_HAS_SENDRIGHTS) && !stat.mps_srights)
+ {
+ dropref = 1;
+ pi->flags &= ~PORT_HAS_SENDRIGHTS;
+ }
+ else if (((pi->flags & PORT_HAS_SENDRIGHTS) == 0) && stat.mps_srights)
+ {
+ pi->flags |= PORT_HAS_SENDRIGHTS;
+ pi->refcnt++;
+ }
+
+ pi->port_right = receive;
+ pi->cancel_threshold = 0;
+ pi->mscount = stat.mps_mscount;
+
+ err = hurd_ihash_add (&pi->bucket->htable, receive, pi);
+ assert_perror (err);
+ pthread_mutex_unlock (&_ports_lock);
+
+ mach_port_move_member (mach_task_self (), receive, pi->bucket->portset);
+
+ if (stat.mps_srights)
+ {
+ err = mach_port_request_notification (mach_task_self (), receive,
+ MACH_NOTIFY_NO_SENDERS,
+ stat.mps_mscount, receive,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &foo);
+ assert_perror (err);
+ if (foo != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), foo);
+ }
+
+ if (dropref)
+ ports_port_deref (pi);
+}
diff --git a/libports/reallocate-port.c b/libports/reallocate-port.c
new file mode 100644
index 00000000..d2adaebb
--- /dev/null
+++ b/libports/reallocate-port.c
@@ -0,0 +1,61 @@
+/*
+ Copyright (C) 1995, 1996, 2001, 2003 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <hurd/ihash.h>
+#include <assert.h>
+
+void
+ports_reallocate_port (void *portstruct)
+{
+ struct port_info *pi = portstruct;
+ error_t err;
+ int dropref = 0;
+
+ pthread_mutex_lock (&_ports_lock);
+ assert (pi->port_right);
+
+ err = mach_port_mod_refs (mach_task_self (), pi->port_right,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+ assert_perror (err);
+
+ hurd_ihash_locp_remove (&pi->bucket->htable, pi->hentry);
+
+ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &pi->port_right);
+ assert_perror (err);
+ if (pi->flags & PORT_HAS_SENDRIGHTS)
+ {
+ pi->flags &= ~PORT_HAS_SENDRIGHTS;
+ dropref = 1;
+ }
+ pi->cancel_threshold = 0;
+ pi->mscount = 0;
+ err = hurd_ihash_add (&pi->bucket->htable, pi->port_right, pi);
+ assert_perror (err);
+ pthread_mutex_unlock (&_ports_lock);
+
+ err = mach_port_move_member (mach_task_self (), pi->port_right,
+ pi->bucket->portset);
+ assert_perror (err);
+
+ if (dropref)
+ ports_port_deref (pi);
+}
diff --git a/libports/resume-all-rpcs.c b/libports/resume-all-rpcs.c
new file mode 100644
index 00000000..e4befffc
--- /dev/null
+++ b/libports/resume-all-rpcs.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <assert.h>
+
+void
+ports_resume_all_rpcs ()
+{
+ pthread_mutex_lock (&_ports_lock);
+ assert (_ports_flags & _PORTS_INHIBITED);
+ _ports_flags &= ~_PORTS_INHIBITED;
+ if (_ports_flags & _PORTS_BLOCKED)
+ {
+ _ports_flags &= ~_PORTS_BLOCKED;
+ pthread_cond_broadcast (&_ports_block);
+ }
+ pthread_mutex_unlock (&_ports_lock);
+}
diff --git a/libports/resume-bucket-rpcs.c b/libports/resume-bucket-rpcs.c
new file mode 100644
index 00000000..cf4db91c
--- /dev/null
+++ b/libports/resume-bucket-rpcs.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <assert.h>
+
+void
+ports_resume_bucket_rpcs (struct port_bucket *bucket)
+{
+ pthread_mutex_lock (&_ports_lock);
+ assert (bucket->flags & PORT_BUCKET_INHIBITED);
+ bucket->flags &= ~PORT_BUCKET_INHIBITED;
+ if (bucket->flags & PORT_BUCKET_BLOCKED)
+ {
+ bucket->flags &= ~PORT_BUCKET_BLOCKED;
+ pthread_cond_broadcast (&_ports_block);
+ }
+ pthread_mutex_unlock (&_ports_lock);
+}
diff --git a/libports/resume-class-rpcs.c b/libports/resume-class-rpcs.c
new file mode 100644
index 00000000..60a2b12e
--- /dev/null
+++ b/libports/resume-class-rpcs.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <assert.h>
+
+void
+ports_resume_class_rpcs (struct port_class *class)
+{
+ pthread_mutex_lock (&_ports_lock);
+ assert (class->flags & PORT_CLASS_INHIBITED);
+ class->flags &= ~PORT_CLASS_INHIBITED;
+ if (class->flags & PORT_CLASS_BLOCKED)
+ {
+ class->flags &= ~PORT_CLASS_BLOCKED;
+ pthread_cond_broadcast (&_ports_block);
+ }
+ pthread_mutex_unlock (&_ports_lock);
+}
diff --git a/libports/resume-port-rpcs.c b/libports/resume-port-rpcs.c
new file mode 100644
index 00000000..6d71ab50
--- /dev/null
+++ b/libports/resume-port-rpcs.c
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "ports.h"
+#include <assert.h>
+
+void
+ports_resume_port_rpcs (void *portstruct)
+{
+ struct port_info *pi = portstruct;
+
+ pthread_mutex_lock (&_ports_lock);
+
+ assert (pi->flags & PORT_INHIBITED);
+ pi->flags &= ~PORT_INHIBITED;
+ if (pi->flags & PORT_BLOCKED)
+ {
+ pi->flags &= ~PORT_BLOCKED;
+ pthread_cond_broadcast (&_ports_block);
+ }
+ pthread_mutex_unlock (&_ports_lock);
+}
diff --git a/libports/stubs.c b/libports/stubs.c
new file mode 100644
index 00000000..de4ae4dd
--- /dev/null
+++ b/libports/stubs.c
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* This file contains stubs for some cthreads functions.
+ It should only get used if the user isn't otherwise using cthreads. */
+
+#if 0
+#include <cthreads.h>
+
+void condition_wait (condition_t c, mutex_t m) __attribute__ ((weak));
+
+void
+condition_wait (condition_t c, mutex_t m)
+{
+}
+
+void cond_broadcast (condition_t c) __attribute__ ((weak));
+
+void
+cond_broadcast (condition_t c)
+{
+}
+#endif
diff --git a/libports/transfer-right.c b/libports/transfer-right.c
new file mode 100644
index 00000000..72488a99
--- /dev/null
+++ b/libports/transfer-right.c
@@ -0,0 +1,98 @@
+/* Transfer the receive right from one port structure to another
+ Copyright (C) 1996, 2003 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include "ports.h"
+#include <assert.h>
+#include <hurd/ihash.h>
+
+error_t
+ports_transfer_right (void *tostruct,
+ void *fromstruct)
+{
+ struct port_info *topi = tostruct;
+ struct port_info *frompi = fromstruct;
+ mach_port_t port;
+ int dereffrompi = 0;
+ int dereftopi = 0;
+ int hassendrights = 0;
+ error_t err;
+
+ pthread_mutex_lock (&_ports_lock);
+
+ /* Fetch the port in FROMPI and clear its use */
+ port = frompi->port_right;
+ if (port != MACH_PORT_NULL)
+ {
+ hurd_ihash_locp_remove (&frompi->bucket->htable, frompi->hentry);
+ frompi->port_right = MACH_PORT_NULL;
+ if (frompi->flags & PORT_HAS_SENDRIGHTS)
+ {
+ frompi->flags &= ~PORT_HAS_SENDRIGHTS;
+ hassendrights = 1;
+ dereffrompi = 1;
+ }
+ }
+
+ /* Destroy the existing right in TOPI. */
+ if (topi->port_right != MACH_PORT_NULL)
+ {
+ hurd_ihash_locp_remove (&topi->bucket->htable, topi->hentry);
+ err = mach_port_mod_refs (mach_task_self (), topi->port_right,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+ assert_perror (err);
+ if ((topi->flags & PORT_HAS_SENDRIGHTS) && !hassendrights)
+ {
+ dereftopi = 1;
+ topi->flags &= ~PORT_HAS_SENDRIGHTS;
+ }
+ else if (((topi->flags & PORT_HAS_SENDRIGHTS) == 0) && hassendrights)
+ {
+ topi->flags |= PORT_HAS_SENDRIGHTS;
+ topi->refcnt++;
+ }
+ }
+
+ /* Install the new right in TOPI. */
+ topi->port_right = port;
+ topi->cancel_threshold = frompi->cancel_threshold;
+ topi->mscount = frompi->mscount;
+
+ if (port)
+ {
+ err = hurd_ihash_add (&topi->bucket->htable, port, topi);
+ assert_perror (err);
+ if (topi->bucket != frompi->bucket)
+ {
+ err = mach_port_move_member (mach_task_self (), port,
+ topi->bucket->portset);
+ assert_perror (err);
+ }
+ }
+
+ pthread_mutex_unlock (&_ports_lock);
+
+ /* Take care of any lowered reference counts. */
+ if (dereffrompi)
+ ports_port_deref (frompi);
+ if (dereftopi)
+ ports_port_deref (topi);
+ return 0;
+}
diff --git a/libps/Makefile b/libps/Makefile
new file mode 100644
index 00000000..d3c0b187
--- /dev/null
+++ b/libps/Makefile
@@ -0,0 +1,40 @@
+# Makefile for libps
+#
+# Copyright (C) 1995,96,99,2002,2012 Free Software Foundation, Inc.
+# Written by Michael I. Bushnell.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libps
+makemode := library
+
+libname = libps
+SRCS = context.c filters.c fmt.c host.c proclist.c procstat.c spec.c \
+ tty.c user.c write.c
+installhdrs = ps.h
+installhdrsubdir = .
+
+HURDLIBS=ihash shouldbeinlibc
+OBJS = $(SRCS:.c=.o) msgUser.o termUser.o
+
+msg-MIGUFLAGS = -D'MSG_IMPORTS=waittime 1000;' -DUSERPREFIX=ps_
+term-MIGUFLAGS = -D'TERM_IMPORTS=waittime 1000;' -DUSERPREFIX=ps_
+
+ps_%.h: %_U.h
+ sed 's/_$*_user_/_ps_$*_user_/g' $< > $@
+
+include ../Makeconf
diff --git a/libps/common.h b/libps/common.h
new file mode 100644
index 00000000..6c44641e
--- /dev/null
+++ b/libps/common.h
@@ -0,0 +1,44 @@
+/* Handy common functions for things in libps.
+
+ Copyright (C) 1995, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/mman.h>
+
+#define ABS(x) ((x) < 0 ? -(x) : (x))
+#define MAX(x, y) ((x) < (y) ? (y) : (x))
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+/* Allocate memory to store an element of type TYPE */
+#define NEW(type) ((type *)malloc(sizeof(type)))
+/* Allocate a vector of type TYPE *, consisting of LEN elements of type TYPE */
+
+#define NEWVEC(type,len) ((type *)malloc(sizeof(type)*(len)))
+/* Change the size of the vector OLD, of type TYPE *, to be LEN elements of type TYPE */
+#define GROWVEC(old,type,len) \
+ ((type *)realloc((void *)(old),(unsigned)(sizeof(type)*(len))))
+
+#define FREE(x) (void)free((void *)x)
+#define VMFREE(x, len) munmap((caddr_t)x, len)
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
diff --git a/libps/context.c b/libps/context.c
new file mode 100644
index 00000000..3082d83b
--- /dev/null
+++ b/libps/context.c
@@ -0,0 +1,151 @@
+/* The ps_context type, for per-procserver and somewhat global state.
+
+ Copyright (C) 1995,96,99,2000,02 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <hurd/term.h>
+
+#include "ps.h"
+#include "common.h"
+
+/* ---------------------------------------------------------------- */
+
+/* Returns in PC a new ps_context for the proc server SERVER. If a memory
+ allocation error occurs, ENOMEM is returned, otherwise 0. */
+error_t
+ps_context_create (process_t server, struct ps_context **pc)
+{
+ *pc = NEW (struct ps_context);
+ if (*pc == NULL)
+ return ENOMEM;
+
+ (*pc)->server = server;
+ (*pc)->user_hooks = 0;
+ hurd_ihash_init (&(*pc)->procs, HURD_IHASH_NO_LOCP);
+ hurd_ihash_init (&(*pc)->ttys, HURD_IHASH_NO_LOCP);
+ hurd_ihash_init (&(*pc)->ttys_by_cttyid, HURD_IHASH_NO_LOCP);
+ hurd_ihash_init (&(*pc)->users, HURD_IHASH_NO_LOCP);
+
+ hurd_ihash_set_cleanup (&(*pc)->procs,
+ (hurd_ihash_cleanup_t) _proc_stat_free, NULL);
+ hurd_ihash_set_cleanup (&(*pc)->ttys,
+ (hurd_ihash_cleanup_t) ps_tty_free, NULL);
+ hurd_ihash_set_cleanup (&(*pc)->users,
+ (hurd_ihash_cleanup_t) ps_user_free, NULL);
+
+ return 0;
+}
+
+/* Frees PC and any resources it consumes. */
+void
+ps_context_free (struct ps_context *pc)
+{
+ hurd_ihash_destroy (&pc->procs);
+ hurd_ihash_destroy (&pc->ttys);
+ hurd_ihash_destroy (&pc->ttys_by_cttyid);
+ hurd_ihash_destroy (&pc->users);
+ free (pc);
+}
+
+
+/* ---------------------------------------------------------------- */
+
+/* Return the value in HT indexed by the key ID. If it doesn't exist create
+ it by calling CREATE with ID and a return location pointer as arguments
+ (CREATE should return either an error-code or 0 if no error occurs), and
+ cache it in HT. */
+static error_t
+lookup (int id, hurd_ihash_t ht, error_t (*create)(int id, void **),
+ void **value)
+{
+ *value = hurd_ihash_find (ht, id);
+ if (*value == NULL)
+ {
+ error_t err = create (id, value);
+ if (err)
+ return err;
+ hurd_ihash_add (ht, id, *value);
+ }
+ return 0;
+}
+
+/* Find a proc_stat for the process referred to by PID, and return it in
+ PS. If an error occurs, it is returned, otherwise 0. */
+error_t
+ps_context_find_proc_stat (struct ps_context *pc, pid_t pid, struct proc_stat **ps)
+{
+ error_t create (int pid, void **value)
+ {
+ return _proc_stat_create (pid, pc, (struct proc_stat **)value);
+ }
+ return lookup (pid, &pc->procs, create, (void **)ps);
+}
+
+/* Find a ps_tty for the terminal referred to by the port TTY_PORT, and
+ return it in TTY. If an error occurs, it is returned, otherwise 0. */
+error_t
+ps_context_find_tty (struct ps_context *pc, mach_port_t tty_port,
+ struct ps_tty **tty)
+{
+ return lookup (tty_port,
+ &pc->ttys,
+ (error_t (*)(int id, void **result))ps_tty_create,
+ (void **)tty);
+}
+
+/* Find a ps_tty for the terminal referred to by the ctty id port
+ CTTYID_PORT, and return it in TTY. If an error occurs, it is returned,
+ otherwise 0. */
+error_t
+ps_context_find_tty_by_cttyid (struct ps_context *pc, mach_port_t cttyid_port,
+ struct ps_tty **tty)
+{
+ error_t create (int cttyid_port, void **value)
+ {
+ if (cttyid_port == MACH_PORT_NULL)
+ {
+ *value = 0;
+ return 0;
+ }
+ else
+ {
+ mach_port_t tty_port;
+ error_t err = termctty_open_terminal (cttyid_port, 0, &tty_port);
+ if (err)
+ return err;
+ else
+ return ps_context_find_tty (pc, tty_port, (struct ps_tty **)value);
+ }
+ }
+
+ return lookup (cttyid_port, &pc->ttys_by_cttyid, create, (void **)tty);
+}
+
+/* Find a ps_user for the user referred to by UID, and return it in U. */
+error_t
+ps_context_find_user (struct ps_context *pc, uid_t uid, struct ps_user **u)
+{
+ return lookup (uid,
+ &pc->users,
+ (error_t (*)(int id, void **result))ps_user_create,
+ (void **) u);
+}
diff --git a/libps/filters.c b/libps/filters.c
new file mode 100644
index 00000000..6663e61e
--- /dev/null
+++ b/libps/filters.c
@@ -0,0 +1,91 @@
+/* Some ps_filters to restrict proc_stat_lists in various ways.
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#include "ps.h"
+#include "common.h"
+
+/* ---------------------------------------------------------------- */
+
+static int
+ps_own_p (struct proc_stat *ps)
+{
+ static int own_uid = -2; /* -1 means no uid at all. */
+ if (own_uid == -2)
+ own_uid = getuid ();
+ return own_uid >= 0 && own_uid == proc_stat_owner_uid (ps);
+}
+const struct ps_filter ps_own_filter =
+{"own", PSTAT_OWNER_UID, ps_own_p};
+
+static int
+ps_not_leader_p (struct proc_stat *ps)
+{
+ return
+ !(proc_stat_state (ps) & (PSTAT_STATE_P_SESSLDR | PSTAT_STATE_P_LOGINLDR));
+}
+const struct ps_filter ps_not_leader_filter =
+{"not-sess-leader", PSTAT_STATE, ps_not_leader_p};
+
+static int
+ps_unorphaned_p (struct proc_stat *ps)
+{
+ int state = proc_stat_state (ps);
+ return
+ !(state & PSTAT_STATE_P_ORPHAN)
+ || (state & (PSTAT_STATE_P_SESSLDR | PSTAT_STATE_P_LOGINLDR));
+}
+const struct ps_filter ps_unorphaned_filter =
+{"unorphaned", PSTAT_STATE, ps_unorphaned_p};
+
+static int
+ps_ctty_p (struct proc_stat *ps)
+{
+ return proc_stat_cttyid (ps) != MACH_PORT_NULL;
+}
+const struct ps_filter ps_ctty_filter =
+{"ctty", PSTAT_CTTYID, ps_ctty_p};
+
+static int
+ps_parent_p (struct proc_stat *ps)
+{
+ return !(proc_stat_state (ps) & PSTAT_STATE_P_NOPARENT);
+}
+const struct ps_filter ps_parent_filter =
+{"parent", PSTAT_STATE, ps_parent_p};
+
+static int
+ps_alive_p (struct proc_stat *ps)
+{
+ ps_flags_t test_flag =
+ proc_stat_is_thread (ps) ? PSTAT_THREAD_BASIC : PSTAT_PROC_INFO;
+ if (proc_stat_has (ps, test_flag))
+ return 1;
+ proc_stat_set_flags (ps, test_flag);
+ return proc_stat_has (ps, test_flag);
+}
+const struct ps_filter ps_alive_filter =
+{"alive", 0, ps_alive_p};
diff --git a/libps/fmt.c b/libps/fmt.c
new file mode 100644
index 00000000..0465555d
--- /dev/null
+++ b/libps/fmt.c
@@ -0,0 +1,575 @@
+/* Implements the ps_fmt type, which describes how to output a user-readable
+ version of a proc_stat.
+
+ Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "ps.h"
+#include "common.h"
+
+/* An internal version of ps_fmt_create that takes various extra args. If
+ POSIX is true, parse a posix-std format string. If ERR_STRING is non-0
+ and EINVAL is returned, then a malloced string will be returned in
+ ERR_STRING describing why. */
+static error_t
+_fmt_create (char *src, int posix, struct ps_fmt_specs *fmt_specs,
+ struct ps_fmt **fmt, char **err_string)
+{
+ struct ps_fmt *new_fmt;
+ int needs = 0;
+ int fields_alloced = 10;
+ /* Initial values for CLR_FLAGS & INV_FLAGS, so the user may specify
+ string-wide defaults. */
+ int global_clr_flags = 0, global_inv_flags = 0;
+ struct ps_fmt_field *fields = NEWVEC (struct ps_fmt_field, fields_alloced);
+ struct ps_fmt_field *field = fields; /* current last field */
+
+ if (fields == NULL)
+ return ENOMEM;
+
+ new_fmt = NEW (struct ps_fmt);
+ if (fmt == NULL)
+ {
+ FREE (fields);
+ return ENOMEM;
+ }
+
+ /* Make a private copy of SRC so we can mutate it. */
+ new_fmt->src_len = strlen (src) + 1;
+ new_fmt->src = strdup (src);
+ if (new_fmt->src == NULL)
+ {
+ FREE (fields);
+ FREE (new_fmt);
+ return ENOMEM;
+ }
+
+ src = new_fmt->src;
+ while (*src != '\0')
+ {
+ if (field - fields == fields_alloced)
+ /* Time to grow FIELDS to make room for more. */
+ {
+ int offs = field - fields;
+
+ fields_alloced += 10;
+ fields = GROWVEC (fields, struct ps_fmt_field, fields_alloced);
+
+ if (fields == NULL)
+ {
+ FREE (new_fmt);
+ FREE (new_fmt->src);
+ return ENOMEM;
+ }
+
+ field = fields + offs;
+ }
+
+ if (posix)
+ /* Posix fields are always adjacent to one another. */
+ {
+ field->pfx = " ";
+ field->pfx_len = 1;
+ }
+ else
+ /* Find the text to be reproduced verbatim between the last field and
+ the next one; we'll add this a prefix to FIELD. */
+ {
+ field->pfx = src;
+ while (*src != '\0' && *src != '%')
+ src++;
+ field->pfx_len = src - field->pfx;
+ }
+
+ field->spec = NULL;
+ field->title = NULL;
+ field->width = 0;
+
+ if (*src != '\0')
+ /* Another format-spec. */
+ {
+ char *name;
+ int sign = 1;
+ int explicit_width = 0, explicit_precision = 0;
+ int quoted_name = 0; /* True if the name is quoted with { ... }. */
+ /* Modifications to the spec's flags -- the bits in CLR_FLAGS are
+ cleared from it, and then the bits in INV_FLAGS are inverted. */
+ int clr_flags = global_clr_flags, inv_flags = global_inv_flags;
+
+ if (! posix)
+ src++; /* skip the '%' */
+
+ /* Set modifiers. */
+ while (*src == '@' || *src == ':'
+ || *src == '!' || *src == '?' || *src == '^')
+ {
+ if (*src == '@')
+ inv_flags ^= PS_FMT_FIELD_AT_MOD; /* Toggle */
+ else if (*src == ':')
+ inv_flags ^= PS_FMT_FIELD_COLON_MOD; /* Toggle */
+ else if (*src == '^')
+ inv_flags ^= PS_FMT_FIELD_UPCASE_TITLE; /* Toggle */
+ else if (*src == '!')
+ { /* Set */
+ clr_flags |= PS_FMT_FIELD_KEEP;
+ inv_flags |= PS_FMT_FIELD_KEEP;
+ }
+ else if (*src == '?')
+ { /* Clear */
+ clr_flags |= PS_FMT_FIELD_KEEP;
+ inv_flags &= ~PS_FMT_FIELD_KEEP;
+ }
+ src++;
+ }
+
+ /* Read an explicit field width. */
+ field->width = 0;
+ if (*src == '-')
+ sign = -1, src++;
+ while (isdigit (*src))
+ {
+ field->width = field->width * 10 + (*src++ - '0');
+ explicit_width = TRUE;
+ }
+
+ /* Read an explicit field precision. */
+ field->precision = 0;
+ if (*src == '.')
+ while (isdigit (*++src))
+ {
+ field->precision = field->precision * 10 + (*src - '0');
+ explicit_precision = 1;
+ }
+
+ /* Skip `{' between optional width and spec name. */
+ if (*src == '{')
+ {
+ src++;
+ quoted_name = 1;
+ }
+ else if (!isalnum (*src) && *src != '_')
+ /* This field spec doesn't have a name, so use its flags fields
+ to set the global ones, and skip it. */
+ {
+ global_clr_flags = clr_flags;
+ global_inv_flags = inv_flags;
+ continue;
+ }
+
+ name = src;
+
+ if (posix)
+ /* Posix-style field spec: `NAME' or `NAME=TITLE'. Only commas
+ can separate fields. */
+ {
+ int stop = quoted_name ? '}' : ',';
+ while (*src != '\0' && *src != stop && *src != '=')
+ src++;
+ if (*src == '=')
+ /* An explicit title. */
+ {
+ *src++ = '\0'; /* NUL-terminate NAME. */
+ field->title = src;
+ while (*src != '\0' && *src != stop)
+ src++;
+ }
+
+ if (*src)
+ *src++ = '\0'; /* NUL terminate NAME. */
+ }
+ else
+ /* A gnu-style field spec: `NAME' or `NAME:TITLE'. */
+ {
+ while (quoted_name
+ ? (*src != '\0' && *src != '}' && *src != ':')
+ : (isalnum (*src) || *src == '_'))
+ src++;
+ if (quoted_name && *src == ':')
+ /* An explicit title. */
+ {
+ *src++ = '\0'; /* NUL-terminate SRC. */
+ field->title = src;
+ while (*src != '\0' && *src != '}')
+ src++;
+ }
+
+ /* Move the name down one byte (we know there's room, at least
+ the leading `%') so that we have room to NUL-terminate the
+ name for which we're searching. We also adjust any pointers
+ into this spec-string accordingly. */
+ bcopy (name, name - 1, src - name);
+ name--;
+ if (field->title)
+ field->title--;
+
+ /* Now that we've made room, do the termination of NAME. */
+ src[-1] = '\0';
+ }
+
+ field->spec = ps_fmt_specs_find (fmt_specs, name);
+ if (! field->spec)
+ /* Failed to find any named spec called NAME. */
+ {
+ if (err_string)
+ asprintf (err_string, "%s: Unknown format spec", name);
+
+ FREE (new_fmt->src);
+ FREE (fields);
+ FREE (new_fmt);
+
+ return EINVAL;
+ }
+
+ if (! field->title)
+ {
+ /* No explicit title specified in the fmt string. */
+ if (field->spec->title)
+ field->title = field->spec->title; /* But the spec has one. */
+ else
+ field->title = field->spec->name; /* Just use field name. */
+ }
+
+ /* Add FIELD's required pstat_flags to FMT's set */
+ needs |= ps_getter_needs (ps_fmt_spec_getter (field->spec));
+
+ if (! explicit_width)
+ field->width = field->spec->width;
+ if (! explicit_precision)
+ field->precision = field->spec->precision;
+
+ field->flags = (field->spec->flags & ~clr_flags) ^ inv_flags;
+
+ if (quoted_name && *src == '}')
+ /* Skip optional trailing `}' after the spec name. */
+ src++;
+ if (posix)
+ /* Skip interfield noise. */
+ {
+ if (*src == ',')
+ src++;
+ while (isspace (*src))
+ src++;
+ }
+
+ /* Remember the width's sign (we put it here after possibly using a
+ default width so that the user may include a `-' with no width
+ to flip the justification of the default width). */
+ field->width *= sign;
+
+ {
+ /* Force the field to be wide enough to hold the title. */
+ int width = field->width;
+ int tlen = strlen (field->title);
+ if (width != 0 && tlen > ABS (width))
+ field->width = (width > 0 ? tlen : -tlen);
+ }
+ }
+
+ field++;
+ }
+
+ new_fmt->fields = fields;
+ new_fmt->num_fields = field - fields;
+ new_fmt->needs = needs;
+ new_fmt->inapp = posix ? "-" : 0;
+ new_fmt->error = "?";
+
+ *fmt = new_fmt;
+
+ return 0;
+}
+
+/* Make a PS_FMT_T by parsing the string SRC, searching for any named
+ field specs in FMT_SPECS, and returning the result in FMT. If a memory
+ allocation error occurs, ENOMEM is returned. If SRC contains an unknown
+ field name, EINVAL is returned. Otherwise 0 is returned. If POSIX is
+ true, a posix-style format string is parsed, otherwise see ps.h for an
+ explanation of how FMT is derived from SRC. */
+error_t
+ps_fmt_create (char *src, int posix, struct ps_fmt_specs *fmt_specs,
+ struct ps_fmt **fmt)
+{
+ return _fmt_create (src, posix, fmt_specs, fmt, 0);
+}
+
+/* Given the same arguments as a previous call to ps_fmt_create that returned
+ an error, this function returns a malloced string describing the error. */
+void
+ps_fmt_creation_error (char *src, int posix, struct ps_fmt_specs *fmt_specs,
+ char **error)
+{
+ struct ps_fmt *fmt;
+ error_t err = _fmt_create (src, posix, fmt_specs, &fmt, error);
+ if (err != EINVAL) /* ? */
+ asprintf (error, "%s", strerror (err));
+ if (! err)
+ ps_fmt_free (fmt);
+}
+
+/* Free FMT, and any resources it consumes. */
+void
+ps_fmt_free (struct ps_fmt *fmt)
+{
+ FREE (fmt->src);
+ FREE (fmt->fields);
+ FREE (fmt);
+}
+
+/* Return a copy of FMT in COPY, or an error. This is useful if, for
+ instance, you would like squash a format without destroying the original. */
+error_t
+ps_fmt_clone (struct ps_fmt *fmt, struct ps_fmt **copy)
+{
+ struct ps_fmt *new = NEW (struct ps_fmt);
+ struct ps_fmt_field *fields = NEWVEC (struct ps_fmt_field, fmt->num_fields);
+ char *src = malloc (fmt->src_len);
+
+ if (!new || !fields || !src)
+ {
+ if (new)
+ free (new);
+ if (fields)
+ free (fields);
+ if (src)
+ free (src);
+ return ENOMEM;
+ }
+
+ bcopy (fmt->src, src, fmt->src_len);
+ bcopy (fmt->fields, fields, fmt->num_fields * sizeof (struct ps_fmt_field));
+
+ new->fields = fields;
+ new->num_fields = fmt->num_fields;
+ new->src = src;
+ new->src_len = fmt->src_len;
+ new->inapp = fmt->inapp;
+ new->error = fmt->error;
+ *copy = new;
+
+ return 0;
+}
+
+/* Write an appropriate header line for FMT, containing the titles of all its
+ fields appropriately aligned with where the values would be printed, to
+ STREAM (without a trailing newline). If count is non-NULL, the total
+ number number of characters output is added to the integer it points to.
+ If any fatal error occurs, the error code is returned, otherwise 0. */
+error_t
+ps_fmt_write_titles (struct ps_fmt *fmt, struct ps_stream *stream)
+{
+ error_t err = 0;
+ struct ps_fmt_field *field = ps_fmt_fields (fmt);
+ int left = ps_fmt_num_fields (fmt);
+
+ while (left-- > 0 && !err)
+ {
+ const char *pfx = ps_fmt_field_prefix (field);
+ int pfx_len = ps_fmt_field_prefix_length (field);
+
+ if (pfx_len > 0)
+ err = ps_stream_write (stream, pfx, pfx_len);
+
+ if (ps_fmt_field_fmt_spec (field) != NULL && !err)
+ {
+ const char *title = ps_fmt_field_title (field) ?: "??";
+ int width = ps_fmt_field_width (field);
+
+ if (field->flags & PS_FMT_FIELD_UPCASE_TITLE)
+ {
+ int len = strlen (title), i;
+ char upcase_title[len + 1];
+ for (i = 0; i < len; i++)
+ upcase_title[i] = toupper (title[i]);
+ upcase_title[len] = '\0';
+ err = ps_stream_write_field (stream, upcase_title, width);
+ }
+ else
+ err = ps_stream_write_field (stream, title, width);
+ }
+
+ field++;
+ }
+
+ return err;
+}
+
+/* Format a description as instructed by FMT, of the process described by PS
+ to STREAM (without a trailing newline). If count is non-NULL, the total
+ number number of characters output is added to the integer it points to.
+ If any fatal error occurs, the error code is returned, otherwise 0. */
+error_t
+ps_fmt_write_proc_stat (struct ps_fmt *fmt, struct proc_stat *ps, struct ps_stream *stream)
+{
+ error_t err = 0;
+ struct ps_fmt_field *field = ps_fmt_fields (fmt);
+ int nfields = ps_fmt_num_fields (fmt);
+ ps_flags_t have = ps->flags;
+ ps_flags_t inapp = ps->inapp;
+
+ while (nfields-- > 0 && !err)
+ {
+ const struct ps_fmt_spec *spec = ps_fmt_field_fmt_spec (field);
+ const char *pfx = ps_fmt_field_prefix (field);
+ int pfx_len = ps_fmt_field_prefix_length (field);
+
+ if (pfx_len > 0)
+ err = ps_stream_write (stream, pfx, pfx_len);
+
+ if (spec != NULL && !err)
+ {
+ ps_flags_t need = ps_getter_needs (ps_fmt_spec_getter (spec));
+
+ /* do we have the resources to print this field? */
+ if ((need & have) == need)
+ /* Yup */
+ err = (*spec->output_fn) (ps, field, stream);
+ else if (need & ~have & inapp)
+ /* This field is inappropriate for PS. */
+ err =
+ ps_stream_write_field (stream, fmt->inapp ?: "", field->width);
+ else
+ /* This field is appropriate, but couldn't be fetched. */
+ err =
+ ps_stream_write_field (stream, fmt->error ?: "", field->width);
+ }
+
+ field++;
+ }
+
+ return err;
+}
+
+/* Remove those fields from FMT for which the function FN, when called on the
+ field, returns true. Appropriate inter-field characters are also removed:
+ those *following* deleted fields at the beginning of the fmt, and those
+ *preceding* deleted fields *not* at the beginning. */
+void
+ps_fmt_squash (struct ps_fmt *fmt, int (*fn)(struct ps_fmt_field *field))
+{
+ int nfields = fmt->num_fields;
+ struct ps_fmt_field *fields = fmt->fields, *field = fields;
+ /* As we're removing some fields, we must recalculate the set of ps flags
+ needed by all fields. */
+ ps_flags_t need = 0;
+
+ while ((field - fields) < nfields)
+ if (field->spec != NULL && (*fn)(field))
+ /* Squash this field! */
+ {
+ /* Save the old prefix, in case we're deleting the first field,
+ and need to prepend it to the next field. */
+ const char *beg_pfx = field->pfx;
+ int beg_pfx_len = field->pfx_len;
+
+ nfields--;
+
+ /* Shift down all following fields over this one. */
+ if (nfields > 0)
+ bcopy (field + 1, field,
+ (nfields - (field - fields)) * sizeof *field);
+
+ if (field == fields)
+ /* This is the first field, so move its prefix to the
+ following field (overwriting that field's prefix). This
+ is to ensure that the beginning of the format string is
+ preserved in preference to the middle, as it is more
+ likely to be significant. */
+ {
+ if (nfields == 0)
+ /* no following fields, so just make a new end field (we're
+ sure to have room, as we just vacated a space). */
+ {
+ nfields++;
+ field->pfx = beg_pfx;
+ field->pfx_len = beg_pfx_len;
+ field->spec = NULL;
+ }
+ else if (field->spec == NULL)
+ /* One following field with only a prefix -- the suffix
+ of the format string. Tack the prefix on before the
+ suffix so we preserve both the beginning and the end
+ of the format string. We know there's space in our
+ copy of the source string, because we've just squashed
+ a field which took at least that much room (as it
+ previously contained the same prefix). */
+ {
+ field->pfx -= beg_pfx_len;
+ field->pfx_len += beg_pfx_len;
+ bcopy (beg_pfx, (char *)field->pfx, beg_pfx_len);
+ }
+ else
+ /* otherwise just replace the next field's prefix with
+ the beginning one */
+ {
+ field->pfx = beg_pfx;
+ field->pfx_len = beg_pfx_len;
+ }
+ }
+ }
+ else
+ /* don't squash this field, just move to the next one */
+ {
+ if (field->spec)
+ need |= ps_getter_needs (field->spec->getter);
+ field++;
+ }
+
+ fmt->num_fields = nfields;
+ fmt->needs = need;
+}
+
+/* Remove those fields from FMT which would need the proc_stat flags FLAGS.
+ Appropriate inter-field characters are also removed: those *following*
+ deleted fields at the beginning of the fmt, and those *preceding* deleted
+ fields *not* at the beginning. */
+void
+ps_fmt_squash_flags (struct ps_fmt *fmt, ps_flags_t flags)
+{
+ int squashable_field (struct ps_fmt_field *field)
+ {
+ return field->spec->getter->needs & flags;
+ }
+
+ ps_fmt_squash (fmt, squashable_field);
+}
+
+/* Try and restrict the number of output columns in FMT to WIDTH. */
+void
+ps_fmt_set_output_width (struct ps_fmt *fmt, int width)
+{
+ struct ps_fmt_field *field = ps_fmt_fields (fmt);
+ int nfields = ps_fmt_num_fields (fmt);
+
+ /* We're not very clever about this -- just add up the width of all the
+ fields but the last, and if the last has no existing width (as is
+ the case in most output formats), give it whatever is left over. */
+ while (--nfields > 0)
+ {
+ int fw = field->width;
+ width -= field->pfx_len + (fw < 0 ? -fw : fw);
+ field++;
+ }
+ if (nfields == 0 && field->width == 0 && width > 0)
+ field->width = width - field->pfx_len - 1; /* 1 for the CR. */
+}
diff --git a/libps/host.c b/libps/host.c
new file mode 100644
index 00000000..9a46e008
--- /dev/null
+++ b/libps/host.c
@@ -0,0 +1,114 @@
+/* Routines to get global host info.
+
+ Copyright (C) 1995,96,2001,02 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "ps.h"
+#include "common.h"
+
+/* ---------------------------------------------------------------- */
+
+/*
+ The Basic & Sched info types are pretty must static so we cache them.
+ However, as load info is dynamic, we do not cache it.
+
+ See <mach/host_info.h> for information on the data types these routines
+ return.
+*/
+
+/* Return the current host port. */
+mach_port_t
+ps_get_host ()
+{
+ static mach_port_t host = MACH_PORT_NULL;
+ if (host == MACH_PORT_NULL)
+ host = mach_host_self ();
+ return host;
+}
+
+/* Return a pointer to the basic info about the current host in INFO.
+ Since this is static global information, we just use a static buffer.
+ If a system error occurs, the error code is returned, otherwise 0 is
+ returned. */
+error_t
+ps_host_basic_info (host_basic_info_t *info)
+{
+ static int initialized;
+ static host_basic_info_data_t buf;
+
+ if (!initialized)
+ {
+ size_t size = sizeof (buf);
+ error_t err = host_info (ps_get_host (), HOST_BASIC_INFO,
+ (host_info_t) &buf, &size);
+ if (err)
+ return err;
+ initialized = 1;
+ }
+
+ *info = &buf;
+ return 0;
+}
+
+/* Return a pointer to the scheduling info about the current host in INFO.
+ Since this is static global information, we just use a static buffer.
+ If a system error occurs, the error code is returned, otherwise 0 is
+ returned. */
+error_t
+ps_host_sched_info (host_sched_info_t *info)
+{
+ static int initialized;
+ static host_sched_info_data_t buf;
+
+ if (!initialized)
+ {
+ size_t size = sizeof (buf);
+ error_t err = host_info (ps_get_host (), HOST_SCHED_INFO,
+ (host_info_t) &buf, &size);
+ if (err)
+ return err;
+ initialized = 1;
+ }
+
+ *info = &buf;
+ return 0;
+}
+
+/* Return a pointer to the load info about the current host in INFO. Since
+ this is global information, we just use a static buffer (if someone desires
+ to keep old load info, they should copy the returned buffer). If a system
+ error occurs, the error code is returned, otherwise 0 is returned. */
+error_t
+ps_host_load_info (host_load_info_t *info)
+{
+ static host_load_info_data_t buf;
+ size_t size = sizeof (buf);
+ error_t err = host_info (ps_get_host (), HOST_LOAD_INFO,
+ (host_info_t) &buf, &size);
+
+ if (err)
+ return err;
+
+ *info = &buf;
+ return 0;
+}
diff --git a/libps/proclist.c b/libps/proclist.c
new file mode 100644
index 00000000..9a4d6ed5
--- /dev/null
+++ b/libps/proclist.c
@@ -0,0 +1,695 @@
+/* The type proc_stat_list_t, which holds lists of proc_stats.
+
+ Copyright (C) 1995,96,2002 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "ps.h"
+#include "common.h"
+
+/* Creates a new proc_stat_list_t for processes from CONTEXT, which is
+ returned in PP, and returns 0, or else returns ENOMEM if there wasn't
+ enough memory. */
+error_t
+proc_stat_list_create (struct ps_context *context, struct proc_stat_list **pp)
+{
+ *pp = NEW (struct proc_stat_list);
+ if (*pp == NULL)
+ return ENOMEM;
+
+ (*pp)->proc_stats = 0;
+ (*pp)->num_procs = 0;
+ (*pp)->alloced = 0;
+ (*pp)->context = context;
+
+ return 0;
+}
+
+/* Free PP, and any resources it consumes. */
+void
+proc_stat_list_free (struct proc_stat_list *pp)
+{
+ proc_stat_list_remove_threads (pp);
+ FREE (pp->proc_stats);
+ FREE (pp);
+}
+
+/* Returns a copy of PP in COPY, or an error. */
+error_t
+proc_stat_list_clone (struct proc_stat_list *pp, struct proc_stat_list **copy)
+{
+ struct proc_stat_list *new = NEW (struct proc_stat_list);
+ struct proc_stat **procs = NEWVEC (struct proc_stat *, pp->num_procs);
+
+ if (!new || !procs)
+ {
+ if (new)
+ free (new);
+ if (procs)
+ free (procs);
+ return ENOMEM;
+ }
+
+ bcopy (pp->proc_stats, procs, pp->num_procs);
+
+ new->proc_stats = procs;
+ new->num_procs = pp->num_procs;
+ new->alloced = pp->num_procs;
+ new->context = pp->context;
+ *copy = new;
+
+ return 0;
+}
+
+/* Make sure there are at least AMOUNT new locations allocated in PP's
+ proc_stat array (but without changing NUM_PROCS). Returns ENOMEM if a
+ memory allocation error occurred, 0 otherwise. */
+static error_t
+proc_stat_list_grow (struct proc_stat_list *pp, int amount)
+{
+ amount += pp->num_procs;
+
+ if (amount > pp->alloced)
+ {
+ struct proc_stat **new_procs =
+ GROWVEC (pp->proc_stats, struct proc_stat *, amount);
+
+ if (new_procs == NULL)
+ return ENOMEM;
+
+ pp->alloced = amount;
+ pp->proc_stats = new_procs;
+ }
+
+ return 0;
+}
+
+/* Add proc_stat entries to PP for each process with a process id in the
+ array PIDS (where NUM_PROCS is the length of PIDS). Entries are only
+ added for processes not already in PP. ENOMEM is returned if a memory
+ allocation error occurs, otherwise 0. PIDs is not referenced by the
+ resulting proc_stat_list_t, and so may be subsequently freed. If
+ PROC_STATS is non-NULL, a malloced array NUM_PROCS entries long of the
+ resulting proc_stats is returned in it. */
+error_t
+proc_stat_list_add_pids (struct proc_stat_list *pp,
+ pid_t *pids, unsigned num_procs,
+ struct proc_stat ***proc_stats)
+{
+ error_t err = proc_stat_list_grow (pp, num_procs);
+
+ if (err)
+ return err;
+ else
+ {
+ int i;
+ struct proc_stat **end = pp->proc_stats + pp->num_procs;
+
+ if (proc_stats)
+ *proc_stats = NEWVEC (struct proc_stat *, num_procs);
+
+ for (i = 0; i < num_procs; i++)
+ {
+ int pid = *pids++;
+ struct proc_stat *ps = proc_stat_list_pid_proc_stat (pp, pid);
+
+ if (ps == NULL)
+ {
+ err = ps_context_find_proc_stat (pp->context, pid, end);
+ if (err)
+ {
+ if (proc_stats)
+ free (*proc_stats);
+ return err;
+ }
+ else
+ ps = *end++;
+ }
+
+ if (proc_stats)
+ (*proc_stats)[i] = ps;
+ }
+
+ pp->num_procs = end - pp->proc_stats;
+
+ return 0;
+ }
+}
+
+/* Add a proc_stat for the process designated by PID at PP's proc context to
+ PP. If PID already has an entry in PP, nothing is done. If a memory
+ allocation error occurs, ENOMEM is returned, otherwise 0. If PS is
+ non-NULL, the resulting entry is returned in it. */
+error_t
+proc_stat_list_add_pid (struct proc_stat_list *pp, pid_t pid, struct proc_stat **ps)
+{
+ struct proc_stat *_ps = proc_stat_list_pid_proc_stat (pp, pid);
+
+ if (_ps == NULL)
+ {
+ error_t err;
+
+ if (pp->num_procs == pp->alloced)
+ {
+ err = proc_stat_list_grow (pp, 32);
+ if (err)
+ return err;
+ }
+
+ err = ps_context_find_proc_stat (pp->context, pid, &_ps);
+ if (err)
+ return err;
+
+ pp->proc_stats[pp->num_procs++] = _ps;
+ }
+
+ if (ps)
+ *ps = _ps;
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Returns the proc_stat in PP with a process-id of PID, if there's one,
+ otherwise, NULL. */
+struct proc_stat *
+proc_stat_list_pid_proc_stat (struct proc_stat_list *pp, pid_t pid)
+{
+ unsigned nprocs = pp->num_procs;
+ struct proc_stat **procs = pp->proc_stats;
+
+ while (nprocs-- > 0)
+ if (proc_stat_pid (*procs) == pid)
+ return *procs;
+ else
+ procs++;
+
+ return NULL;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Adds all proc_stats in MERGEE to PP that don't correspond to processes
+ already in PP; the resulting order of proc_stats in PP is undefined.
+ If MERGEE and PP point to different proc contexts, EINVAL is returned. If a
+ memory allocation error occurs, ENOMEM is returned. Otherwise 0 is
+ returned, and MERGEE is freed. */
+error_t
+proc_stat_list_merge (struct proc_stat_list *pp, struct proc_stat_list *mergee)
+{
+ if (pp->context != mergee->context)
+ return EINVAL;
+ else
+ {
+ /* Make sure there's room for the max number of new elements in PP. */
+ error_t err = proc_stat_list_grow (pp, mergee->num_procs);
+
+ if (err)
+ return err;
+ else
+ {
+ int mnprocs = mergee->num_procs;
+ struct proc_stat **mprocs = mergee->proc_stats;
+ int nprocs = pp->num_procs;
+ struct proc_stat **procs = pp->proc_stats;
+
+ /* Transfer over any proc_stats from MERGEE to PP that don't
+ already exist there; for each of these, we set its entry in
+ MERGEE's proc_stat array to NULL, which prevents
+ proc_list_free () from freeing them. */
+ while (mnprocs-- > 0)
+ if (proc_stat_list_pid_proc_stat(pp, proc_stat_pid (mprocs[mnprocs]))
+ == NULL)
+ {
+ procs[nprocs++] = mprocs[mnprocs];
+ mprocs[mnprocs] = NULL;
+ }
+
+ proc_stat_list_free (mergee);
+
+ return 0;
+ }
+ }
+}
+
+/* ---------------------------------------------------------------- */
+
+/* the number of max number pids that will fit in our static buffers (above
+ which mig will vm_allocate space for them) */
+#define STATICPIDS 200
+
+/* Add to PP entries for all processes in the collection fetched from the
+ proc server by the function FETCH_FN. If an error occurs, the system
+ error code is returned, otherwise 0. If PROC_STATS and NUM_PROCS are
+ non-NULL, a malloced vector of the resulting entries is returned in them. */
+static error_t
+proc_stat_list_add_fn_pids (struct proc_stat_list *pp,
+ kern_return_t (*fetch_fn)(process_t proc,
+ pid_t **pids,
+ size_t *num_pids),
+ struct proc_stat ***proc_stats, size_t *num_procs)
+{
+ error_t err;
+ pid_t pid_array[STATICPIDS], *pids = pid_array;
+ size_t num_pids = STATICPIDS;
+
+ err = (*fetch_fn)(ps_context_server (pp->context), &pids, &num_pids);
+ if (err)
+ return err;
+
+ err = proc_stat_list_add_pids (pp, pids, num_pids, proc_stats);
+ if (!err && num_procs)
+ *num_procs = num_pids;
+
+ if (pids != pid_array)
+ VMFREE(pids, sizeof (pid_t) * num_pids);
+
+ return err;
+}
+
+/* Add to PP entries for all processes in the collection fetched from the
+ proc server by the function FETCH_FN and ID. If an error occurs, the
+ system error code is returned, otherwise 0. If PROC_STATS and NUM_PROCS
+ are non-NULL, a malloced vector of the resulting entries is returned in
+ them. */
+static error_t
+proc_stat_list_add_id_fn_pids (struct proc_stat_list *pp, unsigned id,
+ kern_return_t (*fetch_fn)(process_t proc,
+ pid_t id,
+ pid_t **pids,
+ size_t *num_pids),
+ struct proc_stat ***proc_stats,
+ size_t *num_procs)
+{
+ error_t id_fetch_fn (process_t proc, pid_t **pids, size_t *num_pids)
+ {
+ return (*fetch_fn)(proc, id, pids, num_pids);
+ }
+ return proc_stat_list_add_fn_pids (pp, id_fetch_fn, proc_stats, num_procs);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Add to PP entries for all processes at its context. If an error occurs,
+ the system error code is returned, otherwise 0. If PROC_STATS and
+ NUM_PROCS are non-NULL, a malloced vector of the resulting entries is
+ returned in them. */
+error_t
+proc_stat_list_add_all (struct proc_stat_list *pp,
+ struct proc_stat ***proc_stats, size_t *num_procs)
+{
+ return
+ proc_stat_list_add_fn_pids (pp, proc_getallpids, proc_stats, num_procs);
+}
+
+/* Add to PP entries for all processes in the login collection LOGIN_ID at
+ its context. If an error occurs, the system error code is returned,
+ otherwise 0. If PROC_STATS and NUM_PROCS are non-NULL, a malloced vector
+ of the resulting entries is returned in them. */
+error_t
+proc_stat_list_add_login_coll (struct proc_stat_list *pp, pid_t login_id,
+ struct proc_stat ***proc_stats,
+ size_t *num_procs)
+{
+ return
+ proc_stat_list_add_id_fn_pids (pp, login_id, proc_getloginpids,
+ proc_stats, num_procs);
+}
+
+/* Add to PP entries for all processes in the session SESSION_ID at its
+ context. If an error occurs, the system error code is returned, otherwise
+ 0. If PROC_STATS and NUM_PROCS are non-NULL, a malloced vector of the
+ resulting entries is returned in them. */
+error_t
+proc_stat_list_add_session (struct proc_stat_list *pp, pid_t session_id,
+ struct proc_stat ***proc_stats,
+ size_t *num_procs)
+{
+ return
+ proc_stat_list_add_id_fn_pids (pp, session_id, proc_getsessionpids,
+ proc_stats, num_procs);
+}
+
+/* Add to PP entries for all processes in the process group PGRP at its
+ context. If an error occurs, the system error code is returned, otherwise
+ 0. If PROC_STATS and NUM_PROCS are non-NULL, a malloced vector of the
+ resulting entries is returned in them. */
+error_t
+proc_stat_list_add_pgrp (struct proc_stat_list *pp, pid_t pgrp,
+ struct proc_stat ***proc_stats, size_t *num_procs)
+{
+ return
+ proc_stat_list_add_id_fn_pids (pp, pgrp, proc_getpgrppids,
+ proc_stats, num_procs);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Try to set FLAGS in each proc_stat in PP (but they may still not be set
+ -- you have to check). If a fatal error occurs, the error code is
+ returned, otherwise 0. */
+error_t
+proc_stat_list_set_flags (struct proc_stat_list *pp, ps_flags_t flags)
+{
+ unsigned nprocs = pp->num_procs;
+ struct proc_stat **procs = pp->proc_stats;
+
+ while (nprocs-- > 0)
+ {
+ struct proc_stat *ps = *procs++;
+
+ if (!proc_stat_has (ps, flags))
+ {
+ error_t err = proc_stat_set_flags (ps, flags);
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Destructively modify PP to only include proc_stats for which the
+ function PREDICATE returns true; if INVERT is true, only proc_stats for
+ which PREDICATE returns false are kept. FLAGS is the set of pstat_flags
+ that PREDICATE requires be set as precondition. Regardless of the value
+ of INVERT, all proc_stats for which the predicate's preconditions can't
+ be satisfied are kept. If a fatal error occurs, the error code is
+ returned, it returns 0. */
+error_t
+proc_stat_list_filter1(struct proc_stat_list *pp,
+ int (*predicate)(struct proc_stat *ps), ps_flags_t flags,
+ int invert)
+{
+ unsigned which = 0;
+ unsigned num_procs = pp->num_procs;
+ struct proc_stat **procs = pp->proc_stats;
+ /* We compact the proc array as we filter, and KEPT points to end of the
+ compacted part that we've already processed. */
+ struct proc_stat **kept = procs;
+ error_t err = proc_stat_list_set_flags (pp, flags);
+
+ if (err)
+ return err;
+
+ invert = !!invert; /* Convert to a boolean. */
+
+ while (which < num_procs)
+ {
+ struct proc_stat *ps = procs[which++];
+
+ /* See if we should keep PS; if PS doesn't satisfy the set of flags we
+ need, we don't attempt to call PREDICATE at all, and keep PS. */
+
+ if (!proc_stat_has(ps, flags) || !!predicate (ps) != invert)
+ *kept++ = ps;
+ /* ... otherwise implicitly delete PS from PP by not putting it in the
+ KEPT sequence. */
+ }
+
+ pp->num_procs = kept - procs;
+
+ return 0;
+}
+
+/* Destructively modify PP to only include proc_stats for which the
+ predicate function in FILTER returns true; if INVERT is true, only
+ proc_stats for which the predicate returns false are kept. Regardless
+ of the value of INVERT, all proc_stats for which the predicate's
+ preconditions can't be satisfied are kept. If a fatal error occurs,
+ the error code is returned, it returns 0. */
+error_t
+proc_stat_list_filter (struct proc_stat_list *pp,
+ const struct ps_filter *filter, int invert)
+{
+ return
+ proc_stat_list_filter1(pp,
+ ps_filter_predicate (filter),
+ ps_filter_needs (filter),
+ invert);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Destructively sort proc_stats in PP by ascending value of the field
+ returned by GETTER, and compared by CMP_FN; If REVERSE is true, use the
+ opposite order. If a fatal error occurs, the error code is returned, it
+ returns 0. */
+error_t
+proc_stat_list_sort1 (struct proc_stat_list *pp,
+ const struct ps_getter *getter,
+ int (*cmp_fn)(struct proc_stat *ps1,
+ struct proc_stat *ps2,
+ const struct ps_getter *getter),
+ int reverse)
+{
+ int needs = ps_getter_needs (getter);
+ struct proc_stat **procs = pp->proc_stats;
+ error_t err = proc_stat_list_set_flags (pp, needs);
+
+ /* Lessp is a nested function so it may use state variables from
+ proc_stat_list_sort1, which qsort gives no other way of passing in. */
+ int lessp (const void *p1, const void *p2)
+ {
+ struct proc_stat *ps1 = *(struct proc_stat **)p1;
+ struct proc_stat *ps2 = *(struct proc_stat **)p2;
+ int is_th_1 = proc_stat_is_thread (ps1);
+ int is_th_2 = proc_stat_is_thread (ps2);
+
+ if (!is_th_1 || !is_th_2
+ || proc_stat_thread_origin(ps1) != proc_stat_thread_origin (ps2))
+ /* Compare the threads' origins to keep them ordered after their
+ respective processes. The exception is when they're both from the
+ same process, in which case we want to compare them directly so that
+ a process's threads are sorted among themselves (in most cases this
+ just fails because the thread doesn't have the proper fields; this
+ should just result in the threads remaining in their original
+ order). */
+ {
+ if (is_th_1)
+ ps1 = proc_stat_thread_origin (ps1);
+ if (is_th_2)
+ ps2 = proc_stat_thread_origin (ps2);
+ }
+
+ if (ps1 == ps2 || !proc_stat_has(ps1, needs) || !proc_stat_has (ps2, needs))
+ /* If we're comparing a thread with it's process (and so the thread's
+ been replaced by the process), or we can't call CMP_FN on either
+ proc_stat due to lack of the necessary preconditions, then compare
+ their original positions, to retain the same order. */
+ return p1 - p2;
+ else if (reverse)
+ return cmp_fn (ps2, ps1, getter);
+ else
+ return cmp_fn (ps1, ps2, getter);
+ }
+
+ if (err)
+ return err;
+
+ qsort((void *)procs, (size_t)pp->num_procs, sizeof (struct proc_stat *), lessp);
+
+ return 0;
+}
+
+/* Destructively sort proc_stats in PP by ascending value of the field KEY;
+ if REVERSE is true, use the opposite order. If KEY isn't a valid sort
+ key, EINVAL is returned. If a fatal error occurs the error code is
+ returned. Otherwise, 0 is returned. */
+error_t
+proc_stat_list_sort (struct proc_stat_list *pp,
+ const struct ps_fmt_spec *key, int reverse)
+{
+ int (*cmp_fn)() = ps_fmt_spec_compare_fn (key);
+ if (cmp_fn == NULL)
+ return EINVAL;
+ else
+ return
+ proc_stat_list_sort1 (pp, ps_fmt_spec_getter (key), cmp_fn, reverse);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Format a description as instructed by FMT, of the processes in PP to
+ STREAM, separated by newlines (and with a terminating newline). If a
+ fatal error occurs, the error code is returned, otherwise 0. */
+error_t
+proc_stat_list_fmt (struct proc_stat_list *pp, struct ps_fmt *fmt,
+ struct ps_stream *stream)
+{
+ unsigned nprocs = pp->num_procs;
+ struct proc_stat **procs = pp->proc_stats;
+ error_t err = proc_stat_list_set_flags(pp, ps_fmt_needs (fmt));
+
+ while (!err && nprocs-- > 0)
+ {
+ err = ps_fmt_write_proc_stat (fmt, *procs++, stream);
+ if (! err)
+ ps_stream_newline (stream);
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Modifies FLAGS to be the subset which can't be set in any proc_stat in
+ PP (and as a side-effect, adds as many bits from FLAGS to each proc_stat
+ as possible). If a fatal error occurs, the error code is returned,
+ otherwise 0. */
+error_t
+proc_stat_list_find_bogus_flags (struct proc_stat_list *pp, ps_flags_t *flags)
+{
+ unsigned nprocs = pp->num_procs;
+ struct proc_stat **procs = pp->proc_stats;
+ error_t err = proc_stat_list_set_flags (pp, *flags);
+
+ if (err)
+ return err;
+
+ while (nprocs-- > 0 && *flags != 0)
+ *flags &= ~proc_stat_flags (*procs++);
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Add thread entries for for every process in PP, located immediately after
+ the containing process in sequence. Subsequent sorting of PP will leave
+ the thread entries located after the containing process, although the
+ order of the thread entries themselves may change. If a fatal error
+ occurs, the error code is returned, otherwise 0. */
+error_t
+proc_stat_list_add_threads (struct proc_stat_list *pp)
+{
+ error_t err = proc_stat_list_set_flags (pp, PSTAT_NUM_THREADS);
+
+ if (err)
+ return err;
+ else
+ {
+ int new_entries = 0;
+ int nprocs = pp->num_procs;
+ struct proc_stat **procs = pp->proc_stats;
+
+ /* First, count the number of threads that will be added. */
+ while (nprocs-- > 0)
+ {
+ struct proc_stat *ps = *procs++;
+ if (proc_stat_has (ps, PSTAT_NUM_THREADS))
+ new_entries += proc_stat_num_threads (ps);
+ }
+
+ /* And make room for them... */
+ err = proc_stat_list_grow (pp, new_entries);
+ if (err)
+ return err;
+ else
+ /* Now add thread entries for every existing entry in PP; we go
+ through them backwards so we can do it in place. */
+ {
+ struct proc_stat **end = pp->proc_stats + pp->num_procs + new_entries;
+
+ nprocs = pp->num_procs;
+ procs = pp->proc_stats + nprocs;
+
+ while (nprocs-- > 0)
+ {
+ struct proc_stat *ps = *--procs;
+ if (proc_stat_has (ps, PSTAT_NUM_THREADS))
+ {
+ int nthreads = proc_stat_num_threads (ps);
+ while (nthreads-- > 0)
+ proc_stat_thread_create (ps, nthreads, --end);
+ }
+ *--end = ps;
+ }
+
+ pp->num_procs += new_entries;
+ }
+ }
+
+ return 0;
+}
+
+error_t
+proc_stat_list_remove_threads (struct proc_stat_list *pp)
+{
+ int is_thread (struct proc_stat *ps)
+ {
+ return proc_stat_is_thread (ps);
+ }
+ return proc_stat_list_filter1(pp, is_thread, 0, FALSE);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Calls FN in order for each proc_stat in PP. If FN ever returns a non-zero
+ value, then the iteration is stopped, and the value is returned
+ immediately; otherwise, 0 is returned. */
+int
+proc_stat_list_for_each (struct proc_stat_list *pp, int (*fn)(struct proc_stat *ps))
+{
+ unsigned nprocs = pp->num_procs;
+ struct proc_stat **procs = pp->proc_stats;
+
+ while (nprocs-- > 0)
+ {
+ int val = (*fn)(*procs++);
+ if (val)
+ return val;
+ }
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Returns true if SPEC is `nominal' in every entry in PP. */
+int
+proc_stat_list_spec_nominal (struct proc_stat_list *pp,
+ const struct ps_fmt_spec *spec)
+{
+ int (*nominal_fn)(struct proc_stat *ps, const struct ps_getter *getter) =
+ spec->nominal_fn;
+
+ if (nominal_fn == NULL)
+ return FALSE;
+ else
+ {
+ const struct ps_getter *getter = ps_fmt_spec_getter (spec);
+ ps_flags_t needs = ps_getter_needs (getter);
+ int interesting (struct proc_stat *ps)
+ {
+ return proc_stat_has (ps, needs) && !(*nominal_fn)(ps, getter);
+ }
+
+ proc_stat_list_set_flags (pp, needs);
+
+ return !proc_stat_list_for_each (pp, interesting);
+ }
+}
diff --git a/libps/procstat.c b/libps/procstat.c
new file mode 100644
index 00000000..0d4a565b
--- /dev/null
+++ b/libps/procstat.c
@@ -0,0 +1,1147 @@
+/* The proc_stat type, which holds information about a hurd process.
+
+ Copyright (C) 1995,96,97,98,99,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "ps.h"
+#include "common.h"
+
+#include "ps_msg.h"
+
+/* ---------------------------------------------------------------- */
+
+/* These directly correspond to the bits in a state, starting from 0. See
+ ps.h for an explanation of what each of these means. */
+char *proc_stat_state_tags = "TZRHDSIN<u+slfmpoxwg";
+
+/* ---------------------------------------------------------------- */
+
+/* The type of the per-thread data returned by proc_getprocinfo. */
+typedef typeof (((struct procinfo *)0)->threadinfos[0]) threadinfo_data_t;
+typedef threadinfo_data_t *threadinfo_t;
+
+/* Return the PSTAT_STATE_ bits describing the state of an individual thread,
+ from that thread's thread_basic_info_t struct */
+static int
+thread_state (thread_basic_info_t bi)
+{
+ int state = 0;
+
+ switch (bi->run_state)
+ {
+ case TH_STATE_RUNNING:
+ state |= PSTAT_STATE_T_RUN;
+ break;
+ case TH_STATE_UNINTERRUPTIBLE:
+ state |= PSTAT_STATE_T_WAIT;
+ break;
+ case TH_STATE_HALTED:
+ state |= PSTAT_STATE_T_HALT;
+ break;
+ case TH_STATE_STOPPED:
+ state |= PSTAT_STATE_T_HALT | PSTAT_STATE_T_UNCLEAN;
+ break;
+ case TH_STATE_WAITING:
+ /* Act like unix: waits of less than 20 seconds means a process is
+ `sleeping' and >= 20 seconds means it's `idle' */
+ state |= bi->sleep_time < 20 ? PSTAT_STATE_T_SLEEP : PSTAT_STATE_T_IDLE;
+ break;
+ }
+
+ if (bi->base_priority < 25)
+ state |= PSTAT_STATE_T_NASTY;
+ else if (bi->base_priority > 25)
+ state |= PSTAT_STATE_T_NICE;
+
+ return state;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* The set of things we get from procinfo that are per-thread. */
+#define PSTAT_PROCINFO_THREAD \
+ (PSTAT_THREAD_BASIC | PSTAT_THREAD_SCHED | PSTAT_THREAD_WAIT)
+
+/* The set of things we get from procinfo that are per-task, and thread dependent. */
+#define PSTAT_PROCINFO_TASK_THREAD_DEP \
+ (PSTAT_PROCINFO_THREAD | PSTAT_NUM_THREADS | PSTAT_THREAD_WAITS)
+
+/* The set of things we get from procinfo that are per-task (note that this
+ includes thread fields, because tasks use them for thread summaries). */
+#define PSTAT_PROCINFO_TASK \
+ (PSTAT_PROCINFO_TASK_THREAD_DEP | PSTAT_PROC_INFO \
+ | PSTAT_TASK_BASIC | PSTAT_TASK_EVENTS)
+
+/* The set of PSTAT_ flags that we get using proc_getprocinfo. */
+#define PSTAT_PROCINFO PSTAT_PROCINFO_TASK
+
+/* The set of things in PSTAT_PROCINFO that we will not attempt to refetch on
+ subsequent getprocinfo calls. */
+#define PSTAT_PROCINFO_MERGE (PSTAT_TASK_BASIC | PSTAT_TASK_EVENTS)
+#define PSTAT_PROCINFO_REFETCH (PSTAT_PROCINFO - PSTAT_PROCINFO_MERGE)
+
+/* Fetches process information from the set in PSTAT_PROCINFO, returning it
+ in PI & PI_SIZE. NEED is the information, and HAVE is the what we already
+ have. */
+static error_t
+fetch_procinfo (process_t server, pid_t pid,
+ ps_flags_t need, ps_flags_t *have,
+ struct procinfo **pi, size_t *pi_size,
+ char **waits, size_t *waits_len)
+{
+ static const struct { ps_flags_t ps_flag; int pi_flags; } map[] =
+ {
+ { PSTAT_TASK_BASIC, PI_FETCH_TASKINFO },
+ { PSTAT_TASK_EVENTS, PI_FETCH_TASKEVENTS },
+ { PSTAT_NUM_THREADS, PI_FETCH_THREADS },
+ { PSTAT_THREAD_BASIC, PI_FETCH_THREAD_BASIC | PI_FETCH_THREADS },
+ { PSTAT_THREAD_SCHED, PI_FETCH_THREAD_SCHED | PI_FETCH_THREADS },
+ { PSTAT_THREAD_WAITS, PI_FETCH_THREAD_WAITS | PI_FETCH_THREADS },
+ { 0, }
+ };
+ int pi_flags = 0;
+ int i;
+
+ for (i = 0; map[i].ps_flag; i++)
+ if ((need & map[i].ps_flag) && !(*have & map[i].ps_flag))
+ pi_flags |= map[i].pi_flags;
+
+ if (pi_flags || ((need & PSTAT_PROC_INFO) && !(*have & PSTAT_PROC_INFO)))
+ {
+ error_t err;
+
+ *pi_size /= sizeof (int); /* getprocinfo takes an array of ints. */
+ err = proc_getprocinfo (server, pid, &pi_flags,
+ (procinfo_t *)pi, pi_size, waits, waits_len);
+ *pi_size *= sizeof (int);
+
+ if (! err)
+ /* Update *HAVE to reflect what we've successfully fetched. */
+ {
+ *have |= PSTAT_PROC_INFO;
+ for (i = 0; map[i].ps_flag; i++)
+ if ((pi_flags & map[i].pi_flags) == map[i].pi_flags)
+ *have |= map[i].ps_flag;
+ }
+ return err;
+ }
+ else
+ return 0;
+}
+
+/* The size of the initial buffer malloced to try and avoid getting
+ vm_alloced memory for the procinfo structure returned by getprocinfo.
+ Here we just give enough for four threads. */
+#define PROCINFO_MALLOC_SIZE \
+ (sizeof (struct procinfo) + 4 * sizeof (threadinfo_data_t))
+
+#define WAITS_MALLOC_SIZE 128
+
+/* Fetches process information from the set in PSTAT_PROCINFO, returning it
+ in PI & PI_SIZE, and if *PI_SIZE is non-zero, merges the new information
+ with what was in *PI, and deallocates *PI. NEED is the information, and
+ *HAVE is the what we already have (which will be updated). */
+static ps_flags_t
+merge_procinfo (struct proc_stat *ps, ps_flags_t need, ps_flags_t have)
+{
+ error_t err;
+ struct procinfo *new_pi, old_pi_hdr;
+ size_t new_pi_size;
+ char *new_waits = 0;
+ size_t new_waits_len = 0;
+ /* We always re-fetch any thread-specific info, as the set of threads could
+ have changed since the last time we did this, and we can't tell. */
+ ps_flags_t really_need = need | (have & PSTAT_PROCINFO_REFETCH);
+ ps_flags_t really_have = have & ~PSTAT_PROCINFO_REFETCH;
+
+ /* Give NEW_PI, the default buffer to receive procinfo data, some storage. */
+ if (have & PSTAT_PROCINFO)
+ /* We already have some procinfo stuff, so try to reuse its storage,
+ first saving the old values. We know that below we'll never try to
+ merge anything beyond the static struct procinfo header, so just save
+ that. */
+ old_pi_hdr = *ps->proc_info;
+ else
+ /* The first time we're getting procinfo stuff. Malloc a block that's
+ probably big enough for everything. */
+ {
+ ps->proc_info = malloc (PROCINFO_MALLOC_SIZE);
+ ps->proc_info_size = PROCINFO_MALLOC_SIZE;
+ ps->proc_info_vm_alloced = 0;
+ if (! ps->proc_info)
+ return ENOMEM;
+ }
+ new_pi = ps->proc_info;
+ new_pi_size = ps->proc_info_size;
+
+ if (really_need & PSTAT_THREAD_WAITS)
+ /* We're going to get thread waits info, so make some storage for it too.*/
+ {
+ if (! (have & PSTAT_THREAD_WAITS))
+ {
+ ps->thread_waits = malloc (WAITS_MALLOC_SIZE);
+ ps->thread_waits_len = WAITS_MALLOC_SIZE;
+ ps->thread_waits_vm_alloced = 0;
+ }
+ new_waits = ps->thread_waits;
+ new_waits_len = ps->thread_waits_len;
+ }
+
+ err = fetch_procinfo (ps->context->server, ps->pid, really_need, &really_have,
+ &new_pi, &new_pi_size,
+ &new_waits, &new_waits_len);
+ if (err)
+ /* Just keep what we had before. If that was nothing, we have to free
+ the first-time storage we malloced. */
+ {
+ if (! (have & PSTAT_PROCINFO))
+ free (new_pi);
+ if ((really_need & PSTAT_THREAD_WAITS) && !(have & PSTAT_THREAD_WAITS))
+ free (new_waits);
+ return have;
+ }
+
+ /* There was old information, try merging it. */
+ if (have & PSTAT_TASK_BASIC)
+ /* Task info. */
+ bcopy (&old_pi_hdr.taskinfo, &new_pi->taskinfo,
+ sizeof (struct task_basic_info));
+ if (have & PSTAT_TASK_EVENTS)
+ /* Event info. */
+ bcopy (&old_pi_hdr.taskevents, &new_pi->taskevents,
+ sizeof (struct task_events_info));
+ /* That's it for now. */
+
+ if (new_pi != ps->proc_info)
+ /* We got new memory vm_alloced by the getprocinfo, discard the old. */
+ {
+ if (ps->proc_info_vm_alloced)
+ munmap (ps->proc_info, ps->proc_info_size);
+ else
+ free (ps->proc_info);
+ ps->proc_info = new_pi;
+ ps->proc_info_size = new_pi_size;
+ ps->proc_info_vm_alloced = 1;
+ }
+
+ if (really_need & PSTAT_THREAD_WAITS)
+ /* We were trying to get thread waits.... */
+ {
+ if (! (really_have & PSTAT_THREAD_WAITS))
+ /* Failed to do so. Make sure we deallocate memory for it. */
+ new_waits = 0;
+ if (new_waits != ps->thread_waits)
+ /* We got new memory vm_alloced by the getprocinfo, discard the old. */
+ {
+ if (ps->thread_waits_vm_alloced)
+ munmap (ps->thread_waits, ps->thread_waits_len);
+ else
+ free (ps->thread_waits);
+ ps->thread_waits = new_waits;
+ ps->thread_waits_len = new_waits_len;
+ ps->thread_waits_vm_alloced = 1;
+ }
+ }
+
+ /* Return what thread info we have -- this may *decrease*, as all
+ previously fetched thread-info is out-of-date now, so we have to make do
+ with whatever we've fetched this time. */
+ return really_have;
+}
+
+/* Returns FLAGS augmented with any other flags that are necessary
+ preconditions to setting them. */
+static ps_flags_t
+add_preconditions (ps_flags_t flags, struct ps_context *context)
+{
+ /* Implement any inter-flag dependencies: if the new flags in FLAGS depend on
+ some other set of flags to be set, make sure those are also in FLAGS. */
+
+ if ((flags & PSTAT_USER_MASK)
+ && context->user_hooks && context->user_hooks->dependencies)
+ /* There are some user flags needing to be set... See what they need. */
+ flags |= (*context->user_hooks->dependencies) (flags & PSTAT_USER_MASK);
+
+ if (flags & PSTAT_TTY)
+ flags |= PSTAT_CTTYID;
+ if (flags & PSTAT_STATE)
+ flags |= PSTAT_PROC_INFO | PSTAT_THREAD_BASIC;
+ if (flags & PSTAT_OWNER)
+ flags |= PSTAT_OWNER_UID;
+ if (flags & PSTAT_OWNER_UID)
+ flags |= PSTAT_PROC_INFO;
+ if (flags & PSTAT_SUSPEND_COUNT)
+ /* We just request the resources require for both the thread and task
+ versions, as the extraneous info won't be possible to acquire anyway. */
+ flags |= PSTAT_TASK_BASIC | PSTAT_THREAD_BASIC;
+ if (flags & PSTAT_TIMES)
+ flags |= PSTAT_TASK_BASIC | PSTAT_THREAD_BASIC;
+ if (flags & (PSTAT_CTTYID | PSTAT_CWDIR | PSTAT_AUTH | PSTAT_UMASK)
+ && !(flags & PSTAT_NO_MSGPORT))
+ {
+ flags |= PSTAT_MSGPORT;
+ flags |= PSTAT_TASK; /* for authentication */
+ }
+
+ return flags;
+}
+
+/* Those flags that should be set before calling should_suppress_msgport. */
+#define PSTAT_TEST_MSGPORT \
+ (PSTAT_NUM_THREADS | PSTAT_SUSPEND_COUNT | PSTAT_THREAD_BASIC)
+
+/* Those flags that need the msg port, perhaps implicitly. */
+#define PSTAT_USES_MSGPORT \
+ (PSTAT_MSGPORT | PSTAT_THREAD_WAIT | PSTAT_THREAD_WAITS)
+
+/* Return true when there's some condition indicating that we shouldn't use
+ PS's msg port. For this routine to work correctly, PS's flags should
+ contain as many flags in PSTAT_TEST_MSGPORT as possible. */
+static int
+should_suppress_msgport (struct proc_stat *ps)
+{
+ ps_flags_t have = ps->flags;
+
+ if ((have & PSTAT_SUSPEND_COUNT) && ps->suspend_count != 0)
+ /* Task is suspended. */
+ return TRUE;
+
+ if ((have & PSTAT_THREAD_BASIC) && ps->thread_basic_info->suspend_count != 0)
+ /* All threads are suspended. */
+ return TRUE;
+
+ if ((have & PSTAT_NUM_THREADS) && ps->num_threads == 0)
+ /* No threads (some bug could cause the procserver still to think there's
+ a msgport). */
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Returns FLAGS with PSTAT_MSGPORT turned off and PSTAT_NO_MSGPORT on. */
+#define SUPPRESS_MSGPORT_FLAGS(flags) \
+ (((flags) & ~PSTAT_USES_MSGPORT) | PSTAT_NO_MSGPORT)
+
+/* ---------------------------------------------------------------- */
+
+/* Returns a new malloced struct thread_basic_info containing a summary of
+ all the thread basic info in PI. Sizes and cumulative times are summed,
+ delta time are averaged. The run_states are added by having running
+ thread take precedence over waiting ones, and if there are any other
+ incompatible states, simply using a bogus value of -1. */
+static struct thread_basic_info *
+summarize_thread_basic_info (struct procinfo *pi, ps_flags_t have)
+{
+ int i;
+ unsigned num_threads = 0, num_run_threads = 0;
+ thread_basic_info_t tbi = malloc (sizeof (struct thread_basic_info));
+ int run_base_priority = 0, run_cur_priority = 0;
+ int total_base_priority = 0, total_cur_priority = 0;
+
+ if (!tbi)
+ return 0;
+
+ bzero (tbi, sizeof *tbi);
+
+ for (i = 0; i < pi->nthreads; i++)
+ if (! pi->threadinfos[i].died
+ && ! (pi->threadinfos[i].pis_bi.flags & TH_FLAGS_IDLE))
+ {
+ thread_basic_info_t bi = &pi->threadinfos[i].pis_bi;
+ int thread_run_state = bi->run_state;
+
+ /* Construct some aggregate run-state for the process: besides the
+ defined thread run_states, we use 0 to mean no threads, and -1
+ to mean two threads have conflicting run_stats. */
+
+ if (tbi->run_state == 0)
+ /* No prior state, just copy this thread's. */
+ tbi->run_state = thread_run_state;
+ else if (tbi->run_state == TH_STATE_RUNNING
+ || thread_run_state == TH_STATE_RUNNING)
+ /* If any thread is running, mark the process as running. */
+ tbi->run_state = TH_STATE_RUNNING;
+ else if (tbi->run_state != bi->run_state)
+ /* Otherwise there are two conflicting non-running states, so
+ just give up and say we don't know. */
+ tbi->run_state = -1;
+
+ tbi->cpu_usage += bi->cpu_usage;
+ tbi->sleep_time += bi->sleep_time;
+
+ /* The aggregate suspend count is the minimum of all threads. */
+ if (i == 0 || tbi->suspend_count > bi->suspend_count)
+ tbi->suspend_count = bi->suspend_count;
+
+ tbi->user_time.seconds += bi->user_time.seconds;
+ tbi->user_time.microseconds += bi->user_time.microseconds;
+ tbi->system_time.seconds += bi->system_time.seconds;
+ tbi->system_time.microseconds += bi->system_time.microseconds;
+
+ if (tbi->run_state == TH_STATE_RUNNING)
+ {
+ run_base_priority += bi->base_priority;
+ run_cur_priority += bi->base_priority;
+ num_run_threads++;
+ }
+ else
+ {
+ total_base_priority += bi->base_priority;
+ total_cur_priority += bi->base_priority;
+ }
+
+ num_threads++;
+ }
+
+ if (num_threads > 0)
+ {
+ tbi->sleep_time /= num_threads;
+ if (num_run_threads > 0)
+ {
+ tbi->base_priority = run_base_priority / num_run_threads;
+ tbi->cur_priority = run_cur_priority / num_run_threads;
+ }
+ else
+ {
+ tbi->base_priority = total_base_priority / num_threads;
+ tbi->cur_priority = total_cur_priority / num_threads;
+ }
+ }
+
+ /* For tasks, include the run time of terminated threads. */
+ if (have & PSTAT_TASK_BASIC)
+ {
+ tbi->user_time.seconds += pi->taskinfo.user_time.seconds;
+ tbi->user_time.microseconds += pi->taskinfo.user_time.microseconds;
+ tbi->system_time.seconds += pi->taskinfo.system_time.seconds;
+ tbi->system_time.microseconds += pi->taskinfo.system_time.microseconds;
+ }
+
+ tbi->user_time.seconds += tbi->user_time.microseconds / 1000000;
+ tbi->user_time.microseconds %= 1000000;
+ tbi->system_time.seconds += tbi->system_time.microseconds / 1000000;
+ tbi->system_time.microseconds %= 1000000;
+
+ return tbi;
+}
+
+/* Returns a new malloced struct thread_sched_info containing a summary of
+ all the thread scheduling info in PI. The prioritys are an average of the
+ thread priorities. */
+static struct thread_sched_info *
+summarize_thread_sched_info (struct procinfo *pi)
+{
+ int i;
+ unsigned num_threads = 0;
+ thread_sched_info_t tsi = malloc (sizeof (struct thread_sched_info));
+
+ if (!tsi)
+ return 0;
+
+ bzero (tsi, sizeof *tsi);
+
+ for (i = 0; i < pi->nthreads; i++)
+ if (! pi->threadinfos[i].died
+ && ! (pi->threadinfos[i].pis_bi.flags & TH_FLAGS_IDLE))
+ {
+ thread_sched_info_t si = &pi->threadinfos[i].pis_si;
+ tsi->base_priority += si->base_priority;
+ tsi->cur_priority += si->cur_priority;
+ tsi->max_priority += si->max_priority;
+ tsi->depress_priority += si->depress_priority;
+ num_threads++;
+ }
+
+ if (num_threads > 0)
+ {
+ tsi->base_priority /= num_threads;
+ tsi->cur_priority /= num_threads;
+ tsi->max_priority /= num_threads;
+ tsi->depress_priority /= num_threads;
+ }
+
+ return tsi;
+}
+
+/* Returns the union of the state bits for all the threads in PI. */
+static int
+summarize_thread_states (struct procinfo *pi)
+{
+ int i;
+ int state = 0;
+
+ /* The union of all thread state bits... */
+ for (i = 0; i < pi->nthreads; i++)
+ if (! pi->threadinfos[i].died
+ && ! (pi->threadinfos[i].pis_bi.flags & TH_FLAGS_IDLE))
+ state |= thread_state (&pi->threadinfos[i].pis_bi);
+
+ return state;
+}
+
+/* Returns what's blocking the first blocked thread in PI in WAIT and RPC. */
+static void
+summarize_thread_waits (struct procinfo *pi, char *waits, size_t waits_len,
+ char **wait, mach_msg_id_t *rpc)
+{
+ int i;
+ char *next_wait = waits;
+
+ *wait = 0; /* Defaults */
+ *rpc = 0;
+
+ for (i = 0; i < pi->nthreads; i++)
+ if (! pi->threadinfos[i].died)
+ {
+ if (next_wait > waits + waits_len)
+ break;
+ else
+ {
+ int left = waits + waits_len - next_wait;
+
+ if (pi->threadinfos[i].pis_bi.flags & TH_FLAGS_IDLE)
+ ; /* kernel idle thread; ignore */
+ else if (strncmp (next_wait, "msgport", left) == 0
+ || strncmp (next_wait, "itimer", left) == 0)
+ ; /* libc internal threads; ignore. */
+ else if (*wait)
+ /* There are multiple user threads. Punt. */
+ {
+ *wait = "*";
+ *rpc = 0;
+ break;
+ }
+ else
+ {
+ *wait = next_wait;
+ *rpc = pi->threadinfos[i].rpc_block;
+ }
+
+ /* Advance NEXT_WAIT to the next wait string. */
+ next_wait += strnlen (next_wait, left) + 1;
+ }
+ }
+}
+
+/* Returns the number of threads in PI that aren't marked dead. */
+static unsigned
+count_threads (struct procinfo *pi, ps_flags_t have)
+{
+ if (have & (PSTAT_PROCINFO_TASK_THREAD_DEP & ~PSTAT_NUM_THREADS))
+ /* If we have thread info besides the number of threads, then the
+ threadinfos structures in PI are valid (we use the died bit). */
+ {
+ int i;
+ unsigned num_threads = 0;
+
+ /* The union of all thread state bits... */
+ for (i = 0; i < pi->nthreads; i++)
+ if (! pi->threadinfos[i].died)
+ num_threads++;
+
+ return num_threads;
+ }
+ else
+ /* Otherwise just use the number proc gave us. */
+ return pi->nthreads;
+}
+
+/* Returns the threadinfo for the INDEX'th thread from PI that isn't marked
+ dead. */
+threadinfo_t
+get_thread_info (struct procinfo *pi, unsigned index)
+{
+ int i;
+
+ /* The union of all thread state bits... */
+ for (i = 0; i < pi->nthreads; i++)
+ if (! pi->threadinfos[i].died && index-- == 0)
+ return &pi->threadinfos[i];
+
+ return 0;
+}
+
+/* Returns a pointer to the Nth entry in the '\0'-separated vector of strings
+ in ARGZ & ARGZ_LEN. Note that we don't have to do the bit with only
+ counting non-dead threads like get_thread_info does, because the
+ thread_waits string vector only contains entries for live threads. */
+char *
+get_thread_wait (char *waits, size_t waits_len, unsigned n)
+{
+ char *wait = waits;
+ while (n-- && wait)
+ if (wait >= waits + waits_len)
+ wait = 0;
+ else
+ wait += strnlen (wait, waits + waits_len - wait) + 1;
+ return wait;
+}
+
+/* Returns a malloced block of memory SIZE bytes long, containing a copy of
+ SRC. */
+static void *
+clone (void *src, size_t size)
+{
+ void *dst = malloc (size);
+ if (dst)
+ bcopy (src, dst, size);
+ return dst;
+}
+
+/* Add the information specified by NEED to PS which we can get with
+ proc_getprocinfo. */
+static ps_flags_t
+set_procinfo_flags (struct proc_stat *ps, ps_flags_t need, ps_flags_t have)
+{
+ if (have & PSTAT_PID)
+ {
+ struct procinfo *pi;
+ ps_flags_t had = have;
+
+ if (! (have & PSTAT_PROCINFO))
+ /* Never got any before; zero out our pointers. */
+ {
+ ps->proc_info = 0;
+ ps->proc_info_size = 0;
+ ps->thread_waits = 0;
+ ps->thread_waits_len = 0;
+ }
+
+ if ((need & PSTAT_THREAD_WAIT) && !(need & PSTAT_THREAD_WAITS))
+ /* We need thread wait information only for summarization. This is
+ expensive and pointless for lots of threads, so try to avoid it
+ in that case. */
+ {
+ if (! (have & PSTAT_NUM_THREADS))
+ /* We've don't know how many threads there are yet; find out. */
+ {
+ have = merge_procinfo (ps, PSTAT_NUM_THREADS, have);
+ if (have & PSTAT_NUM_THREADS)
+ ps->num_threads = count_threads (ps->proc_info, have);
+ }
+ if ((have & PSTAT_NUM_THREADS) && ps->num_threads <= 3)
+ /* Perhaps only 1 user thread -- thread-wait info may be
+ meaningful! */
+ need |= PSTAT_THREAD_WAITS;
+ }
+
+ have = merge_procinfo (ps, need, have);
+ pi = ps->proc_info;
+
+ /* Update dependent fields. We redo these even if we've already
+ gotten them, as the information will be newer. */
+ if (have & PSTAT_TASK_BASIC)
+ ps->task_basic_info = &pi->taskinfo;
+ if (have & PSTAT_TASK_EVENTS)
+ ps->task_events_info = &pi->taskevents;
+ if (have & PSTAT_NUM_THREADS)
+ ps->num_threads = count_threads (pi, have);
+ if (had & PSTAT_THREAD_BASIC)
+ free (ps->thread_basic_info);
+ if (have & PSTAT_THREAD_BASIC)
+ ps->thread_basic_info = summarize_thread_basic_info (pi, have);
+ if (had & PSTAT_THREAD_SCHED)
+ free (ps->thread_sched_info);
+ if (have & PSTAT_THREAD_SCHED)
+ ps->thread_sched_info = summarize_thread_sched_info (pi);
+ if (have & PSTAT_THREAD_WAITS)
+ /* Thread-waits info can be used to generate thread-wait info. */
+ {
+ summarize_thread_waits (pi,
+ ps->thread_waits, ps->thread_waits_len,
+ &ps->thread_wait, &ps->thread_rpc);
+ have |= PSTAT_THREAD_WAIT;
+ }
+ else if (!(have & PSTAT_NO_MSGPORT)
+ && (have & PSTAT_NUM_THREADS) && ps->num_threads > 3)
+ /* More than 3 threads (1 user thread + libc signal thread +
+ possible itimer thread) always results in this value for the
+ process's thread_wait field. For fewer threads, we should
+ have fetched thread_waits info and hit the previous case. */
+ {
+ ps->thread_wait = "*";
+ ps->thread_rpc = 0;
+ have |= PSTAT_THREAD_WAIT;
+ }
+ }
+ else
+ /* For a thread, we get use the proc_info from the containing process. */
+ {
+ struct proc_stat *origin = ps->thread_origin;
+ /* Fetch for the containing process basically the same information we
+ want for the thread, but it also needs all the thread wait info. */
+ ps_flags_t oflags =
+ (need & PSTAT_PROCINFO_THREAD)
+ | ((need & PSTAT_THREAD_WAIT) ? PSTAT_THREAD_WAITS : 0);
+
+ proc_stat_set_flags (origin, oflags);
+ oflags = origin->flags;
+
+ if (oflags & PSTAT_PROCINFO_THREAD)
+ /* Got some threadinfo at least. */
+ {
+ threadinfo_t ti =
+ get_thread_info (origin->proc_info, ps->thread_index);
+
+ /* Now copy out the information for this particular thread from the
+ ORIGIN's list of thread information. */
+
+ need &= ~have;
+
+ if ((need & PSTAT_THREAD_BASIC) && (oflags & PSTAT_THREAD_BASIC)
+ && (ps->thread_basic_info =
+ clone (&ti->pis_bi, sizeof (struct thread_basic_info))))
+ have |= PSTAT_THREAD_BASIC;
+
+ if ((need & PSTAT_THREAD_SCHED) && (oflags & PSTAT_THREAD_SCHED)
+ && (ps->thread_sched_info =
+ clone (&ti->pis_si, sizeof (struct thread_sched_info))))
+ have |= PSTAT_THREAD_SCHED;
+
+ if ((need & PSTAT_THREAD_WAIT) && (oflags & PSTAT_THREAD_WAITS))
+ {
+ ps->thread_wait =
+ get_thread_wait (origin->thread_waits,
+ origin->thread_waits_len,
+ ps->thread_index);
+ if (ps->thread_wait)
+ {
+ ps->thread_rpc = ti->rpc_block;
+ have |= PSTAT_THREAD_WAIT;
+ }
+ }
+ }
+
+ /* Mark things that don't apply to threads (note that we don't do the
+ analogous thing for tasks above, as tasks do have thread fields
+ containing summary information for all their threads). */
+ ps->inapp |= need & ~have & PSTAT_PROCINFO & ~PSTAT_PROCINFO_THREAD;
+ }
+
+ return have;
+}
+
+/* Add FLAGS to PS's flags, fetching information as necessary to validate
+ the corresponding fields in PS. Afterwards you must still check the flags
+ field before using new fields, as something might have failed. Returns
+ a system error code if a fatal error occurred, or 0 if none. */
+error_t
+proc_stat_set_flags (struct proc_stat *ps, ps_flags_t flags)
+{
+ ps_flags_t have = ps->flags; /* flags set in ps */
+ ps_flags_t need; /* flags not set in ps, but desired to be */
+ ps_flags_t no_msgport_flags; /* a version of FLAGS for use when we can't
+ use the msg port. */
+ ps_flags_t test_msgport_flags; /* Flags needed to test for msgport
+ validity, including any preconditions. */
+ process_t server = ps_context_server (ps->context);
+
+ /* Turn off use of the msg port if we decide somewhere along the way that
+ it's hosed. */
+ void suppress_msgport ()
+ {
+ /* Turn off those things that were only good given the msg port. */
+ need &= ~(flags & ~no_msgport_flags);
+ have = SUPPRESS_MSGPORT_FLAGS (have);
+ }
+
+ flags &= ~ps->failed; /* Don't try to get things we can't. */
+
+ /* Propagate PSTAT_NO_MSGPORT. */
+ if (flags & PSTAT_NO_MSGPORT)
+ have = SUPPRESS_MSGPORT_FLAGS (have);
+ if (have & PSTAT_NO_MSGPORT)
+ flags = SUPPRESS_MSGPORT_FLAGS (flags);
+
+ no_msgport_flags =
+ add_preconditions (SUPPRESS_MSGPORT_FLAGS (flags), ps->context);
+ flags = add_preconditions (flags, ps->context);
+
+ if (flags & PSTAT_USES_MSGPORT)
+ /* Add in some values that we can use to determine whether the msgport
+ shouldn't be used. */
+ {
+ test_msgport_flags = add_preconditions (PSTAT_TEST_MSGPORT, ps->context);
+ flags |= test_msgport_flags;
+ }
+ else
+ test_msgport_flags = 0;
+
+ need = flags & ~have & ~ps->failed;
+
+ /* Returns true if (1) FLAGS is in NEED, and (2) the appropriate
+ preconditions PRECOND are available; if only (1) is true, FLAG is added
+ to the INAPP set if appropriate (to distinguish it from an error), and
+ returns false. */
+#define NEED(flag, precond) \
+ ({ \
+ ps_flags_t __flag = (flag), _precond = (precond); \
+ int val; \
+ if (! (__flag & need)) \
+ val = 0; \
+ else if ((_precond & have) == _precond) \
+ val = 1; \
+ else \
+ { \
+ val = 0; \
+ if (_precond & ps->inapp) \
+ ps->inapp |= __flag; \
+ } \
+ val; \
+ })
+
+ /* MGET: If we're trying to set FLAG, and the preconditions PRECOND are set
+ in the flags already, then eval CALL and set ERR to the result.
+ If the resulting ERR is 0 add FLAG to the set of valid flags. ERR is
+ returned. */
+#define MGET(flag, precond, call) \
+ ({ \
+ error_t err; \
+ ps_flags_t _flag = (flag); \
+ if (NEED (_flag, precond)) \
+ { \
+ err = (call); \
+ if (!err) \
+ have |= _flag; \
+ } \
+ else \
+ err = 0; \
+ err; \
+ })
+
+ /* A version of MGET specifically for the msg port, that turns off the msg
+ port if a call to it times out. It also implies a precondition of
+ PSTAT_MSGPORT. */
+#define MP_MGET(flag, precond, call) \
+ ({ error_t err = MGET (flag, (precond) | PSTAT_MSGPORT, call); \
+ if (err == EMACH_RCV_TIMED_OUT) suppress_msgport (); \
+ err; \
+ })
+
+ if (need & ~have & test_msgport_flags & PSTAT_PROCINFO)
+ /* Pre-fetch information returned by set_procinfo_flags that we need for
+ msgport validity testing; if we need other procinfo stuff, we get that
+ later. */
+ have = set_procinfo_flags (ps, need & ~have & test_msgport_flags, have);
+
+ if (NEED (PSTAT_SUSPEND_COUNT,
+ ((have & PSTAT_PID) ? PSTAT_TASK_BASIC : PSTAT_THREAD_BASIC)))
+ {
+ if (have & PSTAT_PID)
+ ps->suspend_count = ps->task_basic_info->suspend_count;
+ else
+ ps->suspend_count = ps->thread_basic_info->suspend_count;
+ have |= PSTAT_SUSPEND_COUNT;
+ }
+
+ ps->flags = have; /* should_suppress_msgport looks at them. */
+ if (should_suppress_msgport (ps))
+ suppress_msgport ();
+
+ /* the process's struct procinfo as returned by proc_getprocinfo. */
+ if (need & ~have & PSTAT_PROCINFO)
+ have = set_procinfo_flags (ps, need, have);
+
+ /* The process's libc msg port (see <hurd/msg.defs>). */
+ MGET(PSTAT_MSGPORT, PSTAT_PID, proc_getmsgport (server, ps->pid, &ps->msgport));
+ /* The process's process port. */
+ MGET(PSTAT_PROCESS, PSTAT_PID, proc_pid2proc (server, ps->pid, &ps->process));
+ /* The process's task port. */
+ MGET(PSTAT_TASK, PSTAT_PID, proc_pid2task (server, ps->pid, &ps->task));
+
+ /* PSTAT_STATE_ bits for the process and all its threads. */
+ if ((need & PSTAT_STATE) && (have & (PSTAT_PROC_INFO | PSTAT_THREAD_BASIC)))
+ {
+ ps->state = 0;
+
+ if (have & PSTAT_THREAD_BASIC)
+ {
+ /* Thread states. */
+ if (have & PSTAT_THREAD)
+ ps->state |= thread_state (ps->thread_basic_info);
+ else
+ /* For a process, we use the thread list instead of
+ PS->thread_basic_info because it contains more information. */
+ ps->state |= summarize_thread_states (ps->proc_info);
+ }
+
+ if (have & PSTAT_PROC_INFO)
+ /* Process state. */
+ {
+ int pi_flags = ps->proc_info->state;
+ if (pi_flags & PI_STOPPED)
+ ps->state |= PSTAT_STATE_P_STOP;
+ if (pi_flags & PI_ZOMBIE)
+ ps->state |= PSTAT_STATE_P_ZOMBIE;
+ if (pi_flags & PI_SESSLD)
+ ps->state |= PSTAT_STATE_P_SESSLDR;
+ if (pi_flags & PI_LOGINLD)
+ ps->state |= PSTAT_STATE_P_LOGINLDR;
+ if (!(pi_flags & PI_EXECED))
+ ps->state |= PSTAT_STATE_P_FORKED;
+ if (pi_flags & PI_NOMSG)
+ ps->state |= PSTAT_STATE_P_NOMSG;
+ if (pi_flags & PI_NOPARENT)
+ ps->state |= PSTAT_STATE_P_NOPARENT;
+ if (pi_flags & PI_ORPHAN)
+ ps->state |= PSTAT_STATE_P_ORPHAN;
+ if (pi_flags & PI_TRACED)
+ ps->state |= PSTAT_STATE_P_TRACE;
+ if (pi_flags & PI_WAITING)
+ ps->state |= PSTAT_STATE_P_WAIT;
+ if (pi_flags & PI_GETMSG)
+ ps->state |= PSTAT_STATE_P_GETMSG;
+ }
+
+ have |= PSTAT_STATE;
+ }
+
+ /* The process's exec arguments */
+ if (NEED (PSTAT_ARGS, PSTAT_PID))
+ {
+ char *buf = malloc (100);
+ ps->args_len = 100;
+ ps->args = buf;
+ if (ps->args)
+ {
+ if (proc_getprocargs (server, ps->pid, &ps->args, &ps->args_len))
+ free (buf);
+ else
+ {
+ have |= PSTAT_ARGS;
+ ps->args_vm_alloced = (ps->args != buf);
+ if (ps->args_vm_alloced)
+ free (buf);
+ }
+ }
+ }
+
+ /* The process's exec environment */
+ if (NEED (PSTAT_ENV, PSTAT_PID))
+ {
+ char *buf = malloc (100);
+ ps->env_len = 100;
+ ps->env = buf;
+ if (ps->env)
+ {
+ if (proc_getprocenv (server, ps->pid, &ps->env, &ps->env_len))
+ free (buf);
+ else
+ {
+ have |= PSTAT_ENV;
+ ps->env_vm_alloced = (ps->env != buf);
+ if (ps->env_vm_alloced)
+ free (buf);
+ }
+ }
+ }
+
+ /* The ctty id port; note that this is just a magic cookie;
+ we use it to fetch a port to the actual terminal -- it's not useful for
+ much else. */
+ MP_MGET (PSTAT_CTTYID, PSTAT_TASK,
+ ps_msg_get_init_port (ps->msgport, ps->task,
+ INIT_PORT_CTTYID, &ps->cttyid));
+
+ /* A port to the process's current working directory. */
+ MP_MGET (PSTAT_CWDIR, PSTAT_TASK,
+ ps_msg_get_init_port (ps->msgport, ps->task,
+ INIT_PORT_CWDIR, &ps->cwdir));
+
+ /* The process's auth port, which we can use to determine who the process
+ is authenticated as. */
+ MP_MGET (PSTAT_AUTH, PSTAT_TASK,
+ ps_msg_get_init_port (ps->msgport, ps->task, INIT_PORT_AUTH,
+ &ps->auth));
+
+ /* The process's umask, which controls which protection bits won't be set
+ when creating a file. */
+ MP_MGET (PSTAT_UMASK, PSTAT_TASK,
+ ps_msg_get_init_int (ps->msgport, ps->task, INIT_UMASK,
+ (int *) &ps->umask));
+
+ if (NEED (PSTAT_OWNER_UID, PSTAT_PROC_INFO))
+ {
+ if (ps->proc_info->state & PI_NOTOWNED)
+ ps->owner_uid = -1;
+ else
+ ps->owner_uid = ps->proc_info->owner;
+ have |= PSTAT_OWNER_UID;
+ }
+
+ /* A ps_user object for the process's owner. */
+ if (NEED (PSTAT_OWNER, PSTAT_OWNER_UID))
+ {
+ if (ps->owner_uid < 0)
+ {
+ ps->owner = 0;
+ have |= PSTAT_OWNER;
+ }
+ else if (! ps_context_find_user (ps->context, ps->owner_uid, &ps->owner))
+ have |= PSTAT_OWNER;
+ }
+
+ /* A ps_tty for the process's controlling terminal, or NULL if it
+ doesn't have one. */
+ if (NEED (PSTAT_TTY, PSTAT_CTTYID))
+ if (ps_context_find_tty_by_cttyid (ps->context, ps->cttyid, &ps->tty) == 0)
+ have |= PSTAT_TTY;
+
+ /* The number of Mach ports in the task. */
+ MGET (PSTAT_NUM_PORTS, PSTAT_PID,
+ proc_getnports (server, ps->pid, &ps->num_ports));
+
+ /* User and system times. */
+ if ((need & PSTAT_TIMES) && (have & (PSTAT_TASK_BASIC | PSTAT_THREAD_BASIC)))
+ have |= PSTAT_TIMES;
+
+ /* Update PS's flag state. We haven't tried user flags yet, so don't mark
+ them as having failed. We do this before checking user bits so that the
+ user fetch hook sees PS in a consistent state. */
+ ps->failed |= (need & ~PSTAT_USER_MASK) & ~have;
+ ps->flags = have;
+
+ need &= ~have;
+ if (need && ps->context->user_hooks && ps->context->user_hooks->fetch)
+ /* There is some user state we need to fetch. */
+ {
+ have |= (*ps->context->user_hooks->fetch) (ps, need, have);
+ /* Update the flag state again having tried the user bits. We allow
+ the user hook to turn on non-user bits, in which case we remove them
+ from the failed set; the user hook may know some way of getting the
+ info that we don't. */
+ ps->failed = (ps->failed | need) & ~have;
+ ps->flags = have;
+ }
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+/* Discard PS and any resources it holds. */
+void
+_proc_stat_free (ps)
+ struct proc_stat *ps;
+{
+ if (ps->context->user_hooks && ps->context->user_hooks->cleanup)
+ /* Free any user state. */
+ (*ps->context->user_hooks->cleanup) (ps);
+
+ /* Free the mach port PORT if FLAG is set in PS's flags. */
+#define MFREEPORT(flag, port) \
+ ((ps->flags & (flag)) \
+ ? mach_port_deallocate(mach_task_self (), (ps->port)) : 0)
+
+ /* If FLAG is set in PS's flags, then if VM_ALLOCED is zero, free the malloced
+ field MEM in PS; othrewise, vm_deallocate MEM, consisting of SIZE
+ elements of type ELTYPE, *unless* MEM == SBUF, which usually means
+ that MEM points to a static buffer somewhere instead of vm_alloc'd
+ memory. */
+#define MFREEMEM(flag, mem, size, vm_alloced, sbuf, eltype) \
+ (((ps->flags & (flag)) && ps->mem != sbuf) \
+ ? (vm_alloced ? (VMFREE(ps->mem, size * sizeof (eltype))) : free (ps->mem)) : 0)
+
+ /* free PS's ports */
+ MFREEPORT (PSTAT_PROCESS, process);
+ MFREEPORT (PSTAT_TASK, task);
+ MFREEPORT (PSTAT_MSGPORT, msgport);
+ MFREEPORT (PSTAT_CTTYID, cttyid);
+ MFREEPORT (PSTAT_CWDIR, cwdir);
+ MFREEPORT (PSTAT_AUTH, auth);
+
+ /* free any allocated memory pointed to by PS */
+ MFREEMEM (PSTAT_PROC_INFO, proc_info, ps->proc_info_size,
+ ps->proc_info_vm_alloced, 0, char);
+ MFREEMEM (PSTAT_THREAD_BASIC, thread_basic_info, 0, 0, 0, 0);
+ MFREEMEM (PSTAT_THREAD_SCHED, thread_sched_info, 0, 0, 0, 0);
+ MFREEMEM (PSTAT_ARGS, args, ps->args_len, ps->args_vm_alloced, 0, char);
+ MFREEMEM (PSTAT_ENV, env, ps->env_len, ps->env_vm_alloced, 0, char);
+ MFREEMEM (PSTAT_TASK_EVENTS, task_events_info, ps->task_events_info_size,
+ 0, &ps->task_events_info_buf, char);
+ MFREEMEM (PSTAT_THREAD_WAITS, thread_waits, ps->thread_waits_len,
+ ps->thread_waits_vm_alloced, 0, char);
+
+ FREE (ps);
+}
+
+/* Returns in PS a new proc_stat for the process PID at the process context
+ CONTEXT. If a memory allocation error occurs, ENOMEM is returned,
+ otherwise 0. */
+error_t
+_proc_stat_create (pid_t pid, struct ps_context *context, struct proc_stat **ps)
+{
+ *ps = NEW (struct proc_stat);
+ if (*ps == NULL)
+ return ENOMEM;
+
+ (*ps)->pid = pid;
+ (*ps)->flags = PSTAT_PID;
+ (*ps)->failed = 0;
+ (*ps)->inapp = PSTAT_THREAD;
+ (*ps)->context = context;
+ (*ps)->hook = 0;
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Returns in THREAD_PS a proc_stat for the Nth thread in the proc_stat
+ PS (N should be between 0 and the number of threads in the process). The
+ resulting proc_stat isn't fully functional -- most flags can't be set in
+ it. It also contains a pointer to PS, so PS shouldn't be freed without
+ also freeing THREAD_PS. If N was out of range, EINVAL is returned. If a
+ memory allocation error occurred, ENOMEM is returned. Otherwise, 0 is
+ returned. */
+error_t
+proc_stat_thread_create (struct proc_stat *ps, unsigned index, struct proc_stat **thread_ps)
+{
+ error_t err = proc_stat_set_flags (ps, PSTAT_NUM_THREADS);
+
+ if (err)
+ return err;
+ else if (index >= ps->num_threads)
+ return EINVAL;
+ else
+ {
+ struct proc_stat *tps = NEW (struct proc_stat);
+
+ if (tps == NULL)
+ return ENOMEM;
+
+ /* A value of -1 for the PID indicates that this is a thread. */
+ tps->pid = -1;
+ tps->flags = PSTAT_THREAD;
+ tps->failed = 0;
+ tps->inapp = PSTAT_PID;
+
+ tps->thread_origin = ps;
+ tps->thread_index = index;
+
+ tps->context = ps->context;
+
+ *thread_ps = tps;
+
+ return 0;
+ }
+}
diff --git a/libps/ps.h b/libps/ps.h
new file mode 100644
index 00000000..06af96bc
--- /dev/null
+++ b/libps/ps.h
@@ -0,0 +1,1056 @@
+/* Routines to gather and print process information.
+
+ Copyright (C) 1995,96,99,2001,02 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __PS_H__
+#define __PS_H__
+
+#include <hurd/hurd_types.h>
+#include <hurd/ihash.h>
+#include <mach/mach.h>
+
+#include <pwd.h>
+#include <errno.h>
+
+/* A PS_USER holds info about a particular user. */
+
+/* Possible states a ps_user's passwd can be in: valid, not fet */
+enum ps_user_passwd_state
+ { PS_USER_PASSWD_OK, PS_USER_PASSWD_PENDING, PS_USER_PASSWD_ERROR };
+
+struct ps_user
+{
+ /* Which user this refers to. */
+ uid_t uid;
+
+ /* The status */
+ enum ps_user_passwd_state passwd_state;
+
+ /* The user's password file entry. Only valid if PASSWD_STATE ==
+ PS_USER_PASSWD_OK. */
+ struct passwd passwd;
+
+ /* String storage for strings pointed to by ENTRY. */
+ char *storage;
+};
+
+#define ps_user_uid(u) ((u)->uid)
+
+/* Create a ps_user for the user referred to by UID, returning it in U.
+ If a memory allocation error occurs, ENOMEM is returned, otherwise 0. */
+error_t ps_user_create (uid_t uid, struct ps_user **u);
+
+/* Create a ps_user for the user referred to by UNAME, returning it in U.
+ If a memory allocation error occurs, ENOMEM is returned. If no such user
+ is known, EINVAL is returned. */
+error_t ps_user_uname_create (char *uname, struct ps_user **u);
+
+/* Makes makes a ps_user containing PW (which is copied). */
+error_t ps_user_passwd_create (struct passwd *pw, struct ps_user **u);
+
+/* Free U and any resources it consumes. */
+void ps_user_free (struct ps_user *u);
+
+/* Returns the password file entry (struct passwd, from <pwd.h>) for the user
+ referred to by U, or NULL if it can't be gotten. */
+struct passwd *ps_user_passwd (struct ps_user *u);
+
+/* Returns the user name for the user referred to by U, or NULL if it can't
+ be gotten. */
+char *ps_user_name (struct ps_user *u);
+
+/* A ps_tty holds information about a terminal. */
+
+/* Possible states a ps_tty's name can be in: valid, not fetched yet,
+ couldn't fetch. */
+enum ps_tty_name_state
+ { PS_TTY_NAME_OK, PS_TTY_NAME_PENDING, PS_TTY_NAME_ERROR };
+
+struct ps_tty {
+ /* Which tty this refers to. */
+ file_t port;
+
+ /* The name of the tty, if we could figure it out. */
+ const char *name;
+ /* What state the name is in. */
+ enum ps_tty_name_state name_state;
+
+ /* A more abbreviated name for the tty, or NULL if no name at all. */
+ const char *short_name;
+ int short_name_alloced : 1;
+};
+
+#define ps_tty_port(tty) ((tty)->port)
+
+/* Create a ps_tty for the tty referred to by PORT, returning it in TTY.
+ If a memory allocation error occurs, ENOMEM is returned, otherwise 0. */
+error_t ps_tty_create (file_t port, struct ps_tty **tty);
+
+/* Frees TTY and any resources it consumes. */
+void ps_tty_free (struct ps_tty *tty);
+
+/* Returns the name of the tty, or NULL if it can't be figured out. */
+const char *ps_tty_name (struct ps_tty *tty);
+
+/* Returns the standard abbreviated name of the tty, the whole name if there
+ is no standard abbreviation, or NULL if it can't be figured out. */
+const char *ps_tty_short_name (struct ps_tty *tty);
+
+/* A PS_CONTEXT holds various information resulting from querying a
+ particular process server, in particular a group of proc_stats, ps_users,
+ and ps_ttys. This information sticks around until the context is freed
+ (subsets may be created by making proc_stat_lists). */
+
+struct proc_stat; /* Fwd declared */
+
+struct ps_context
+{
+ /* The process server our process info is from. */
+ process_t server;
+
+ /* proc_stats for every process we know about, indexed by process id. */
+ struct hurd_ihash procs;
+
+ /* ps_ttys for every tty we know about, indexed by the terminal port. */
+ struct hurd_ihash ttys;
+
+ /* ps_ttys for every tty we know about, indexed by their ctty id port
+ (from libc). */
+ struct hurd_ihash ttys_by_cttyid;
+
+ /* A ps_user for every user we know about, indexed by user-id. */
+ struct hurd_ihash users;
+
+ /* Functions that can be set to extend the behavior of proc_stats. */
+ struct ps_user_hooks *user_hooks;
+};
+
+#define ps_context_server(pc) ((pc)->server)
+
+/* Returns in PC a new ps_context for the proc server SERVER. If a memory
+ allocation error occurs, ENOMEM is returned, otherwise 0. */
+error_t ps_context_create (process_t server, struct ps_context **pc);
+
+/* Frees PC and any resources it consumes. */
+void ps_context_free (struct ps_context *pc);
+
+/* Find a proc_stat for the process referred to by PID, and return it in
+ PS. If an error occurs, it is returned, otherwise 0. */
+error_t ps_context_find_proc_stat (struct ps_context *pc, pid_t pid,
+ struct proc_stat **ps);
+
+/* Find a ps_tty for the terminal referred to by the port TTY_PORT, and
+ return it in TTY. If an error occurs, it is returned, otherwise 0. */
+error_t ps_context_find_tty (struct ps_context *pc, mach_port_t tty_port,
+ struct ps_tty **tty);
+
+/* Find a ps_tty for the terminal referred to by the ctty id port
+ CTTYID_PORT, and return it in TTY. If an error occurs, it is returned,
+ otherwise 0. */
+error_t ps_context_find_tty_by_cttyid (struct ps_context *pc,
+ mach_port_t cttyid_port,
+ struct ps_tty **tty);
+
+/* Find a ps_user for the user referred to by UID, and return it in U. */
+error_t ps_context_find_user (struct ps_context *pc, uid_t uid,
+ struct ps_user **u);
+
+/* A PROC_STAT holds lots of info about the process PID at SERVER; exactly
+ which info is dependent on its FLAGS field. */
+
+typedef unsigned ps_flags_t;
+typedef unsigned ps_state_t;
+
+struct proc_stat
+{
+ /* Which process server this is from. */
+ struct ps_context *context;
+
+ /* The proc's process id; if <0 then this is a thread, not a process. */
+ pid_t pid;
+
+ /* Flags describing which fields in this structure are valid. */
+ ps_flags_t flags;
+ ps_flags_t failed; /* flags that we tried to set and couldn't. */
+ ps_flags_t inapp; /* flags that don't apply to this procstat;
+ subset of FAILED. */
+
+ /* Thread fields -- these are valid if PID < 0. */
+ struct proc_stat *thread_origin; /* A proc_stat for the task we're in. */
+ unsigned thread_index; /* Which thread in our proc we are. */
+
+ /* A process_t port for the process. */
+ process_t process;
+
+ /* The mach task port for the process. */
+ task_t task;
+
+ /* A libc msgport for the process. This port is responded to by the
+ process itself (usually by the c library); see <hurd/msg.defs> for the
+ standard set of rpcs you can send to this port. Accordingly, you
+ cannot depend on a timely (or any) reply to messages sent here --
+ program carefully! */
+ mach_port_t msgport;
+
+ /* A pointer to the process's procinfo structure (as returned by
+ proc_getinfo; see <hurd/hurd_types.h>). Vm_alloced. */
+ struct procinfo *proc_info;
+ /* The size of the info structure for deallocation purposes. */
+ unsigned proc_info_size;
+
+ /* If present, these are just pointers into the proc_info structure. */
+ unsigned num_threads;
+ task_basic_info_t task_basic_info;
+
+ /* For a thread, the obvious structures; for a process, summaries of the
+ proc's thread_{basic,sched}_info_t structures: sizes and cumulative
+ times are summed, prioritys and delta time are averaged. The
+ run_states are added by having running thread take precedence over
+ waiting ones, and if there are any other incompatible states, simply
+ using a bogus value of -1. Malloced. */
+ thread_basic_info_t thread_basic_info;
+ thread_sched_info_t thread_sched_info;
+
+ /* For a blocked thread, these next fields describe how it's blocked. */
+
+ /* A string (pointing into the thread_waits field of the parent
+ procstat), describing what's being blocked on. If "KERNEL", a system
+ call (not mach_msg), and thread_rpc is the system call number.
+ Otherwise if thread_rpc isn't zero, this string describes the port the
+ rpc is on; if thread_rpc is 0, this string describes a non-rpc event. */
+ char *thread_wait;
+ /* The rpc that it's blocked on. For a process the rpc blocking the
+ first blocked thread (if any). 0 means no block. */
+ mach_msg_id_t thread_rpc;
+
+ /* Storage for child-thread waits. */
+ char *thread_waits;
+ size_t thread_waits_len;
+
+ /* The task or thread suspend count (whatever this proc_stat refers to). */
+ int suspend_count;
+
+ /* A bitmask summarizing the scheduling state of this process and all its
+ threads. See the PSTAT_STATE_ defines below for a list of bits. */
+ ps_state_t state;
+
+ /* A ps_user object for the owner of this process, or NULL if none. */
+ struct ps_user *owner;
+ int owner_uid; /* The corresponding UID, or -1. */
+
+ /* The process's argv, as a string with each element separated by '\0'. */
+ char *args;
+ /* The length of ARGS. */
+ size_t args_len;
+
+ /* Virtual memory statistics for the process, as returned by task_info;
+ see <mach/task_info.h> for a description of task_events_info_t. */
+ /* FIXME: we are actually currently storing it into proc_info, see
+ fetch_procinfo. */
+ task_events_info_t task_events_info;
+ task_events_info_data_t task_events_info_buf;
+ size_t task_events_info_size;
+
+ /* Flags showing whether a field is vm_alloced or malloced. */
+ unsigned proc_info_vm_alloced : 1;
+ unsigned thread_waits_vm_alloced : 1;
+ unsigned args_vm_alloced : 1;
+ unsigned env_vm_alloced : 1;
+
+ /* Various libc ports: */
+
+ /* The process's ctty id port, or MACH_PORT_NULL if the process has no
+ controlling terminal. Note that this is just a magic cookie; we use
+ it to fetch a port to the actual terminal -- it's not useful for much
+ else. */
+ mach_port_t cttyid;
+
+ /* A port to the process's current working directory. */
+ mach_port_t cwdir;
+
+ /* The process's auth port, which we can use to determine who the process
+ is authenticated as. */
+ mach_port_t auth;
+
+ /* The process's umask, which controls which protection bits won't be set
+ when creating a file. */
+ unsigned umask;
+
+ /* A ps_tty object for the process's controlling terminal. */
+ struct ps_tty *tty;
+
+ /* A hook for the user to use. */
+ void *hook;
+
+ /* XXX these members added at the end for binary compatibility */
+ /* The process's envp, as a string with each element separated by '\0'. */
+ char *env;
+ /* The length of ENV. */
+ size_t env_len;
+
+ unsigned num_ports;
+};
+
+/* Proc_stat flag bits; each bit is set in the FLAGS field if that
+ information is currently valid. */
+#define PSTAT_PID 0x00001 /* Process ID */
+#define PSTAT_THREAD 0x00002 /* thread_index & thread_origin */
+#define PSTAT_PROCESS 0x00004 /* The process_t for the process */
+#define PSTAT_TASK 0x00008 /* The task port for the process */
+#define PSTAT_MSGPORT 0x00010 /* The process's msgport */
+#define PSTAT_PROC_INFO 0x00020 /* Basic process info. */
+#define PSTAT_TASK_BASIC 0x00040 /* The task's struct task_basic_info. */
+#define PSTAT_TASK_EVENTS 0x00080 /* A task_events_info_t for the proc. */
+#define PSTAT_NUM_THREADS 0x00100 /* The number of threads in the task. */
+/* Note that for a process-procstat, the thread information fields generally
+ are a summary of the process's threads, and imply that the corresponding
+ information has been fetched for all its threads. The exception is
+ thread-wait information (PSTAT_THREAD_WAIT), which is expensive to fetch
+ for processes with lots of threads, and not terrible useful. In this
+ case, the thread waits vector containing per-thread information is only
+ guaranteed to be valid if PSTAT_THREAD_WAITS is true as well. */
+#define PSTAT_THREAD_BASIC 0x00200 /* A struct thread_basic_info. */
+#define PSTAT_THREAD_SCHED 0x00400 /* A struct thread_sched_info. */
+#define PSTAT_THREAD_WAIT 0x00800 /* The rpc the thread is waiting on. */
+#define PSTAT_THREAD_WAITS 0x01000 /* Thread waits for this PS's threads */
+#define PSTAT_ARGS 0x02000 /* The process's args */
+#define PSTAT_ENV 0x2000000 /* The process's environment */
+#define PSTAT_STATE 0x04000 /* A bitmask describing the process's
+ state (see below) */
+#define PSTAT_SUSPEND_COUNT 0x08000 /* Task/thread suspend count */
+#define PSTAT_CTTYID 0x10000 /* The process's CTTYID port */
+#define PSTAT_CWDIR 0x20000 /* A file_t for the proc's CWD */
+#define PSTAT_AUTH 0x40000 /* The proc's auth port */
+#define PSTAT_TTY 0x80000 /* A ps_tty for the proc's terminal.*/
+#define PSTAT_OWNER 0x100000 /* A ps_user for the proc's owner */
+#define PSTAT_OWNER_UID 0x200000 /* The uid of the the proc's owner */
+#define PSTAT_UMASK 0x400000 /* The proc's current umask */
+#define PSTAT_HOOK 0x800000 /* Has a non-zero hook */
+#define PSTAT_NUM_PORTS 0x4000000 /* Number of Mach ports in the task */
+#define PSTAT_TIMES 0x8000000 /* Task/thread user and system times */
+
+/* Flag bits that don't correspond precisely to any field. */
+#define PSTAT_NO_MSGPORT 0x1000000 /* Don't use the msgport at all */
+
+/* Bits from PSTAT_USER_BASE on up are available for user-use. */
+#define PSTAT_USER_BASE 0x10000000
+#define PSTAT_USER_MASK ~(PSTAT_USER_BASE - 1)
+
+/* If the PSTAT_STATE flag is set, then the proc_stats state field holds a
+ bitmask of the following bits, describing the process's run state. If you
+ change the value of these, you must change proc_stat_state_tags as well! */
+
+/* Process global state. */
+
+/* Mutually exclusive bits, each of which is a possible process `state'. */
+#define PSTAT_STATE_P_STOP 0x00001 /* T stopped (e.g., by ^Z) */
+#define PSTAT_STATE_P_ZOMBIE 0x00002 /* Z process exited but not reaped */
+
+#define PSTAT_STATE_P_STATES (PSTAT_STATE_P_STOP | PSTAT_STATE_P_ZOMBIE)
+
+/* Independent bits describing additional attributes of the process. */
+#define PSTAT_STATE_P_FG 0x00400 /* + in foreground process group */
+#define PSTAT_STATE_P_SESSLDR 0x00800 /* s session leader */
+#define PSTAT_STATE_P_LOGINLDR 0x01000 /* l login collection leader */
+#define PSTAT_STATE_P_FORKED 0x02000 /* f has forked and not execed */
+#define PSTAT_STATE_P_NOMSG 0x04000 /* m no msg port */
+#define PSTAT_STATE_P_NOPARENT 0x08000 /* p no parent */
+#define PSTAT_STATE_P_ORPHAN 0x10000 /* o orphaned */
+#define PSTAT_STATE_P_TRACE 0x20000 /* x traced */
+#define PSTAT_STATE_P_WAIT 0x40000 /* w process waiting for a child */
+#define PSTAT_STATE_P_GETMSG 0x80000 /* g waiting for a msgport */
+
+#define PSTAT_STATE_P_ATTRS (PSTAT_STATE_P_FG | PSTAT_STATE_P_SESSLDR \
+ | PSTAT_STATE_P_LOGINLDR | PSTAT_STATE_P_FORKED \
+ | PSTAT_STATE_P_NOMSG | PSTAT_STATE_P_NOPARENT \
+ | PSTAT_STATE_P_ORPHAN | PSTAT_STATE_P_TRACE \
+ | PSTAT_STATE_P_WAIT | PSTAT_STATE_P_GETMSG)
+
+/* Per-thread state; in a process, these represent the union of its threads. */
+
+/* Mutually exclusive bits, each of which is a possible thread `state'. */
+#define PSTAT_STATE_T_RUN 0x00004 /* R thread is running */
+#define PSTAT_STATE_T_HALT 0x00008 /* H thread is halted */
+#define PSTAT_STATE_T_WAIT 0x00010 /* D uninterruptable wait */
+#define PSTAT_STATE_T_SLEEP 0x00020 /* S sleeping */
+#define PSTAT_STATE_T_IDLE 0x00040 /* I idle (sleeping > 20 seconds) */
+
+#define PSTAT_STATE_T_STATES (PSTAT_STATE_T_RUN | PSTAT_STATE_T_HALT \
+ | PSTAT_STATE_T_WAIT | PSTAT_STATE_T_SLEEP \
+ | PSTAT_STATE_T_IDLE)
+
+/* Independent bits describing additional attributes of the thread. */
+#define PSTAT_STATE_T_NICE 0x00080 /* N lowered priority */
+#define PSTAT_STATE_T_NASTY 0x00100 /* < raised priority */
+#define PSTAT_STATE_T_UNCLEAN 0x00200 /* u thread is uncleanly halted */
+
+#define PSTAT_STATE_T_ATTRS (PSTAT_STATE_T_UNCLEAN \
+ | PSTAT_STATE_T_NICE | PSTAT_STATE_T_NASTY)
+
+/* This is a constant string holding a single character for each possible bit
+ in a proc_stats STATE field, in order from bit zero. These are intended
+ for printing a user-readable summary of a process's state. */
+extern char *proc_stat_state_tags;
+
+/* Process info accessor functions.
+
+ You must be sure that the associated flag bit is set before accessing a
+ field in a proc_stat! A field FOO (with accessor macro proc_foo ()), has
+ a flag named PSTAT_FOO. If the flag is'nt set, you may attempt to set it
+ with proc_stat_set_flags (but note that this may not succeed). */
+
+/* FLAGS doesn't have a flag bit; it's always valid */
+#define proc_stat_flags(ps) ((ps)->flags)
+
+/* These both use the flag PSTAT_THREAD. */
+#define proc_stat_thread_origin(ps) ((ps)->thread_origin)
+#define proc_stat_thread_index(ps) ((ps)->thread_index)
+
+#define proc_stat_pid(ps) ((ps)->pid)
+#define proc_stat_process(ps) ((ps)->process)
+#define proc_stat_task(ps) ((ps)->task)
+#define proc_stat_msgport(ps) ((ps)->msgport)
+#define proc_stat_proc_info(ps) ((ps)->proc_info)
+#define proc_stat_num_threads(ps) ((ps)->num_threads)
+#define proc_stat_task_basic_info(ps) ((ps)->task_basic_info)
+#define proc_stat_thread_basic_info(ps) ((ps)->thread_basic_info)
+#define proc_stat_thread_sched_info(ps) ((ps)->thread_sched_info)
+#define proc_stat_thread_rpc(ps) ((ps)->thread_rpc)
+#define proc_stat_thread_wait(ps) ((ps)->thread_rpc)
+#define proc_stat_suspend_count(ps) ((ps)->suspend_count)
+#define proc_stat_args(ps) ((ps)->args)
+#define proc_stat_args_len(ps) ((ps)->args_len)
+#define proc_stat_env(ps) ((ps)->env)
+#define proc_stat_env_len(ps) ((ps)->env_len)
+#define proc_stat_state(ps) ((ps)->state)
+#define proc_stat_cttyid(ps) ((ps)->cttyid)
+#define proc_stat_cwdir(ps) ((ps)->cwdir)
+#define proc_stat_owner(ps) ((ps)->owner)
+#define proc_stat_owner_uid(ps) ((ps)->owner_uid)
+#define proc_stat_auth(ps) ((ps)->auth)
+#define proc_stat_umask(ps) ((ps)->umask)
+#define proc_stat_tty(ps) ((ps)->tty)
+#define proc_stat_task_events_info(ps) ((ps)->task_events_info)
+#define proc_stat_num_ports(ps) ((ps)->num_ports)
+#define proc_stat_has(ps, needs) (((ps)->flags & needs) == needs)
+
+/* True if PS refers to a thread and not a process. */
+#define proc_stat_is_thread(ps) ((ps)->pid < 0)
+
+/* Returns in PS a new proc_stat for the process PID in the ps context PC.
+ If a memory allocation error occurs, ENOMEM is returned, otherwise 0.
+ Users shouldn't use this routine, use ps_context_find_proc_stat instead. */
+error_t _proc_stat_create (pid_t pid, struct ps_context *context,
+ struct proc_stat **ps);
+
+/* Frees PS and any memory/ports it references. Users shouldn't use this
+ routine; proc_stats are normally freed only when their ps_context goes
+ away. Insubordinate users will make sure they free the thread proc_stats
+ before they free the corresponding process proc_stat since the thread_wait
+ fields of the former may reference the latter. */
+void _proc_stat_free (struct proc_stat *ps);
+
+/* Adds FLAGS to PS's flags, fetching information as necessary to validate
+ the corresponding fields in PS. Afterwards you must still check the flags
+ field before using new fields, as something might have failed. Returns
+ a system error code if a fatal error occurred, and 0 otherwise. */
+error_t proc_stat_set_flags (struct proc_stat *ps, ps_flags_t flags);
+
+/* Returns in THREAD_PS a proc_stat for the Nth thread in the proc_stat
+ PS (N should be between 0 and the number of threads in the process). The
+ resulting proc_stat isn't fully functional -- most flags can't be set in
+ it. If N was out of range, EINVAL is returned. If a memory allocation
+ error occurred, ENOMEM is returned. Otherwise, 0 is returned. */
+error_t proc_stat_thread_create (struct proc_stat *ps, unsigned n,
+ struct proc_stat **thread_ps);
+
+/* A struct ps_user_hooks holds functions that allow the user to extend the
+ behavior of libps. */
+
+struct ps_user_hooks
+{
+ /* Given a set of flags in the range defined by PSTAT_USER_MASK, should
+ return any other flags (user or system) which should be set as a
+ precondition to setting them. */
+ ps_flags_t (*dependencies) (ps_flags_t flags);
+
+ /* Try and fetch the information corresponding to NEED (which is in the
+ range defined by PSTAT_USER_MASK), and fill in the necessary fields in
+ PS (probably in a user defined structure pointed to by the hook field).
+ The user flags corresponding to what is successfully fetched should be
+ returned. HAVE are the flags defining whas is currently valid in PS. */
+ ps_flags_t (*fetch) (struct proc_stat *ps, ps_flags_t need, ps_flags_t have);
+
+ /* When a proc_stat goes away, this function is called on it. */
+ void (*cleanup) (struct proc_stat *ps);
+};
+
+/* A PS_GETTER describes how to get a particular value from a PROC_STAT.
+
+ To get a value from a proc_stat PS with a getter, you must make sure all
+ the pstat_flags returned by ps_getter_needs (GETTER) are set in PS, and
+ then call the function returned ps_getter_function (GETTER) with PS as the
+ first argument.
+
+ The way the actual value is returned from this funciton is dependent on
+ the type of the value:
+ For ints and floats, the value is the return value.
+ For strings, you must pass in two extra arguments, a char **, which is
+ filled in with a pointer to the string, or NULL if the string is NULL,
+ and an int *, which is filled in with the length of the string. */
+
+struct ps_getter
+ {
+ /* The getter's name */
+ char *name;
+
+ /* What proc_stat flags need to be set as a precondition to calling this
+ getter's function. */
+ ps_flags_t needs;
+
+ /* A function that will get the value; the protocol between this function
+ and its caller is type-dependent. */
+ void (*fn) ();
+ };
+
+/* Access macros: */
+#define ps_getter_name(g) ((g)->name)
+#define ps_getter_needs(g) ((g)->needs)
+#define ps_getter_function(g) ((g)->fn)
+
+/* A PS_FILTER_T describes how to select some subset of a PROC_STAT_LIST_T */
+
+struct ps_filter
+ {
+ /* Name of this filter. */
+ char *name;
+
+ /* The flags that need to be set in each proc_stat in the list to
+ call the filter's predicate function; if these flags can't be set in a
+ particular proc_stat, the function is not called, and it isn't deleted
+ from the list. */
+ ps_flags_t needs;
+
+ /* A function that returns true if called on a proc_stat that the
+ filter accepts, or false if the filter rejects it. */
+ int (*fn) (struct proc_stat *ps);
+ };
+
+/* Access macros: */
+#define ps_filter_name(f) ((f)->name)
+#define ps_filter_needs(f) ((f)->needs)
+#define ps_filter_predicate(f) ((f)->fn)
+
+/* Some predefined filters. These are structures; you must use the &
+ operator to get a ps_filter from them */
+
+/* A filter that retains only processes owned by getuid () */
+extern const struct ps_filter ps_own_filter;
+/* A filter that retains only processes that aren't session or login leaders */
+extern const struct ps_filter ps_not_leader_filter;
+/* A filter that retains only processes with a controlling terminal */
+extern const struct ps_filter ps_ctty_filter;
+/* A filter that retains only `unorphaned' process. A process is unorphaned
+ if it's a session leader, or the process's process group is not orphaned */
+extern const struct ps_filter ps_unorphaned_filter;
+/* A filter that retains only `parented' process. Typically only hurd
+ processes have parents. */
+extern const struct ps_filter ps_parent_filter;
+/* A filter that retains only processes/threads that aren't totally dead. */
+extern const struct ps_filter ps_alive_filter;
+
+/* A ps_stream describes an output stream for libps to use. */
+
+struct ps_stream
+{
+ FILE *stream; /* The actual destination. */
+ int pos; /* The number of characters output. */
+ int spaces; /* The number of spaces pending. */
+};
+
+/* Create a stream outputing to DEST, and return it in STREAM, or an error. */
+error_t ps_stream_create (FILE *dest, struct ps_stream **stream);
+
+/* Frees STREAM. The destination file is *not* closed. */
+void ps_stream_free (struct ps_stream *stream);
+
+/* Write at most MAX_LEN characters of STRING to STREAM (if MAX_LEN > the
+ length of STRING, then write all of it; if MAX_LEN == -1, then write all
+ of STRING regardless). */
+error_t ps_stream_write (struct ps_stream *stream,
+ const char *string, int max_len);
+
+/* Write NUM spaces to STREAM. NUM may be negative, in which case the same
+ number of adjacent spaces (written by other calls to ps_stream_space) are
+ consumed if possible. If an error occurs, the error code is returned,
+ otherwise 0. */
+error_t ps_stream_space (struct ps_stream *stream, int num);
+
+/* Write as many spaces to STREAM as required to make a field of width SOFAR
+ be at least WIDTH characters wide (the absolute value of WIDTH is used).
+ If an error occurs, the error code is returned, otherwise 0. */
+error_t ps_stream_pad (struct ps_stream *stream, int sofar, int width);
+
+/* Write a newline to STREAM, resetting its position to zero. */
+error_t ps_stream_newline (struct ps_stream *stream);
+
+/* Write the string BUF to STREAM, padded on one side with spaces to be at
+ least the absolute value of WIDTH long: if WIDTH >= 0, then on the left
+ side, otherwise on the right side. If an error occurs, the error code is
+ returned, otherwise 0. */
+error_t ps_stream_write_field (struct ps_stream *stream,
+ const char *buf, int width);
+
+/* Like ps_stream_write_field, but truncates BUF to make it fit into WIDTH. */
+error_t ps_stream_write_trunc_field (struct ps_stream *stream,
+ const char *buf, int width);
+
+/* Write the decimal representation of VALUE to STREAM, padded on one side
+ with spaces to be at least the absolute value of WIDTH long: if WIDTH >=
+ 0, then on the left side, otherwise on the right side. If an error
+ occurs, the error code is returned, otherwise 0. */
+error_t ps_stream_write_int_field (struct ps_stream *stream,
+ int value, int width);
+
+/* A PS_FMT_SPEC describes how to output something from a PROC_STAT; it
+ is a combination of a getter (describing how to get the value), an output
+ function (which outputs the result of the getter), and a compare function
+ (which can be used to sort proc_stats according to how they are
+ output). It also specifies the default width of the field in which the
+ output should be printed. */
+
+struct ps_fmt_field; /* fwd decl */
+
+struct ps_fmt_spec
+ {
+ /* The name of the spec (and it's title, if TITLE is NULL). */
+ const char *name;
+
+ /* The title to be printed in the headers. */
+ const char *title;
+
+ /* The width of the field that this spec will be printed in if not
+ overridden. */
+ int width;
+
+ /* A default value for the fields `precision'. */
+ int precision;
+
+ /* Default values for PS_FMT_FIELD_ flags. */
+ int flags;
+
+ const struct ps_getter *getter;
+
+ /* A function that outputs what FIELD specifies in PS to STREAM. */
+ error_t (*output_fn)(struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream);
+
+ /* A function that, given two pses and a getter, will compare what
+ the getter gets for each ps, and return an integer ala qsort. This
+ may be NULL, in which case values in this field cannot be compared. */
+ int (*cmp_fn)(struct proc_stat *ps1, struct proc_stat *ps2,
+ const struct ps_getter *getter);
+
+ /* A function that, given a ps and a getter, will return true if what the
+ getter gets from the ps is `nominal' -- a default unexciting value.
+ This may be NULL, in which case values in this field are _always_
+ exciting... */
+ int (*nominal_fn)(struct proc_stat *ps, const struct ps_getter *getter);
+ };
+
+/* Accessor macros: */
+#define ps_fmt_spec_name(spec) ((spec)->name)
+#define ps_fmt_spec_title(spec) ((spec)->title)
+#define ps_fmt_spec_width(spec) ((spec)->width)
+#define ps_fmt_spec_output_fn(spec) ((spec)->output_fn)
+#define ps_fmt_spec_compare_fn(spec) ((spec)->cmp_fn)
+#define ps_fmt_spec_nominal_fn(spec) ((spec)->nominal_fn)
+#define ps_fmt_spec_getter(spec) ((spec)->getter)
+
+/* Returns true if a pointer into an array of struct ps_fmt_specs is at the
+ end. */
+#define ps_fmt_spec_is_end(spec) ((spec)->name == NULL)
+
+struct ps_fmt_specs
+{
+ const struct ps_fmt_spec *specs; /* An array of specs. */
+ struct ps_fmt_specs *parent; /* A link to more specs shadowed by us. */
+ struct ps_fmt_spec_block *expansions; /* Storage for expanded aliases. */
+};
+
+/* An struct ps_fmt_specs, suitable for use with ps_fmt_specs_find,
+ containing specs for most values in a proc_stat. */
+extern struct ps_fmt_specs ps_std_fmt_specs;
+
+/* Searches for a spec called NAME in SPECS and returns it if found,
+ otherwise NULL. */
+const struct ps_fmt_spec *ps_fmt_specs_find (struct ps_fmt_specs *specs,
+ const char *name);
+
+/* A PS_FMT describes how to output user-readable version of a proc_stat.
+ It consists of a series of PS_FMT_FIELD_Ts, each describing how to output
+ one value. */
+
+/* Flags for ps_fmt_fields. */
+#define PS_FMT_FIELD_AT_MOD 0x1 /* `@' modifier */
+#define PS_FMT_FIELD_COLON_MOD 0x2 /* `:' modifier */
+#define PS_FMT_FIELD_KEEP 0x4 /* Never nominalize this field. */
+#define PS_FMT_FIELD_UPCASE_TITLE 0x8 /* Upcase this field's title. */
+
+/* PS_FMT_FIELD */
+struct ps_fmt_field
+ {
+ /* A ps_fmt_spec describing how to output this field's value, or NULL
+ if there is no value (in which case this is the last field, and exists
+ just to output its prefix string). */
+ const struct ps_fmt_spec *spec;
+
+ /* A non-zero-terminated string of characters that should be output
+ between the previous field and this one. */
+ const char *pfx;
+ /* The number of characters from PFX that should be output. */
+ unsigned pfx_len;
+
+ /* The number of characters that the value portion of this field should
+ consume. If this field is negative, then the absolute value is used,
+ and the field should be right-aligned, otherwise, it is left-aligned. */
+ int width;
+
+ /* User-specifiable attributes, interpreted by each output format. */
+ int precision; /* fraction following field width */
+
+ /* Flags, from the set PS_FMT_FIELD_. */
+ int flags;
+
+ /* Returns the title used when printing a header line for this field. */
+ const char *title;
+ };
+
+/* Accessor macros: */
+#define ps_fmt_field_fmt_spec(field) ((field)->spec)
+#define ps_fmt_field_prefix(field) ((field)->pfx)
+#define ps_fmt_field_prefix_length(field) ((field)->pfx_len)
+#define ps_fmt_field_width(field) ((field)->width)
+#define ps_fmt_field_title(field) ((field)->title)
+
+/* PS_FMT */
+struct ps_fmt
+{
+ /* A pointer to an array of struct ps_fmt_fields holding the individual
+ fields to be formatted. */
+ struct ps_fmt_field *fields;
+ /* The (valid) length of the fields array. */
+ unsigned num_fields;
+
+ /* A set of proc_stat flags describing what a proc_stat needs to hold in
+ order to print out every field in the fmt. */
+ ps_flags_t needs;
+
+ /* Storage for various strings pointed to by the fields. */
+ char *src;
+ size_t src_len; /* Size of SRC. */
+
+ /* The string displayed by default for fields that aren't appropriate for
+ this procstat. */
+ char *inapp;
+
+ /* The string displayed by default for fields which are appropriate, but
+ couldn't be fetched due to some error. */
+ char *error;
+};
+
+/* Accessor macros: */
+#define ps_fmt_fields(fmt) ((fmt)->fields)
+#define ps_fmt_num_fields(fmt) ((fmt)->num_fields)
+#define ps_fmt_needs(fmt) ((fmt)->needs)
+#define ps_fmt_inval (fmt) ((fmt)->inval)
+
+/* Make a PS_FMT by parsing the string SRC, searching for any named
+ field specs in FMT_SPECS, and returning the result in FMT. If a memory
+ allocation error occurs, ENOMEM is returned. If SRC contains an unknown
+ field name, EINVAL is returned. Otherwise 0 is returned.
+
+ If POSIX is true, a posix-style format string is parsed, otherwise
+ the syntax of SRC is:
+
+ SRC: FIELD* [ SUFFIX ]
+ FIELD: [ PREFIX ] SPEC
+ SPEC: `%' [ FLAGS ] [ `-' ] [ WIDTH ] [ `.' PRECISION ] NAMESPEC
+ FLAGS: `[!?@:]+'
+ WIDTH, PRECISION: `[0-9]+'
+ NAMESPEC: `{' NAME [ `:' TITLE ] `}' | NAME_AN
+ NAME, TITLE: `[^}]*'
+ NAME_AN: `[A-Za-z0-9_]*'
+
+ PREFIXes and SUFFIXes are printed verbatim, and specs are replaced by the
+ output of the named spec with that name (each spec specifies what
+ proc_stat field to print, and how to print it, as well as a default field
+ width into which put the output). WIDTH is used to override the spec's
+ default width. If a `-' is included, the output is right-aligned within
+ this width, otherwise it is left-aligned. The FLAGS `@' & `:' are
+ spec-specific, `!' means never omit a nominal field, and `?' means omit a
+ field if it's nominal (in case the defualt is to never do so). PRECISION
+ has a spec-specific meaning. */
+error_t ps_fmt_create (char *src, int posix, struct ps_fmt_specs *fmt_specs,
+ struct ps_fmt **fmt);
+
+/* Given the same arguments as a previous call to ps_fmt_create that returned
+ an error, this function returns a malloced string describing the error. */
+void ps_fmt_creation_error (char *src, int posix,
+ struct ps_fmt_specs *fmt_specs,
+ char **error);
+
+/* Free FMT, and any resources it consumes. */
+void ps_fmt_free (struct ps_fmt *fmt);
+
+/* Return a copy of FMT in COPY, or an error. This is useful if, for
+ instance, you would like squash a format without destroying the original. */
+error_t ps_fmt_clone (struct ps_fmt *fmt, struct ps_fmt **copy);
+
+/* Write an appropriate header line for FMT, containing the titles of all its
+ fields appropiately aligned with where the values would be printed, to
+ STREAM (without a trailing newline). If count is non-NULL, the total
+ number number of characters output is added to the integer it points to.
+ If any fatal error occurs, the error code is returned, otherwise 0. */
+error_t ps_fmt_write_titles (struct ps_fmt *fmt, struct ps_stream *stream);
+
+/* Format a description as instructed by FMT, of the process described by PS
+ to STREAM (without a trailing newline). If count is non-NULL, the total
+ number number of characters output is added to the integer it points to.
+ If any fatal error occurs, the error code is returned, otherwise 0. */
+error_t ps_fmt_write_proc_stat (struct ps_fmt *fmt, struct proc_stat *ps,
+ struct ps_stream *stream);
+
+/* Remove those fields from FMT for which the function FN, when called on the
+ field, returns true. Appropriate inter-field characters are also removed:
+ those *following* deleted fields at the beginning of the fmt, and those
+ *preceding* deleted fields *not* at the beginning. */
+void ps_fmt_squash (struct ps_fmt *fmt, int (*fn)(struct ps_fmt_field *field));
+
+/* Remove those fields from FMT which would need the proc_stat flags FLAGS.
+ Appropriate inter-field characters are also removed: those *following*
+ deleted fields at the beginning of the fmt, and those *preceding* deleted
+ fields *not* at the beginning. */
+void ps_fmt_squash_flags (struct ps_fmt *fmt, ps_flags_t flags);
+
+/* Try and restrict the number of output columns in FMT to WIDTH. */
+void ps_fmt_set_output_width (struct ps_fmt *fmt, int width);
+
+/* A PROC_STAT_LIST represents a list of proc_stats */
+
+struct proc_stat_list
+ {
+ /* An array of proc_stats for the processes in this list. */
+ struct proc_stat **proc_stats;
+
+ /* The number of processes in the list. */
+ unsigned num_procs;
+
+ /* The actual allocated length of PROC_STATS (in case we want to add more
+ processes). */
+ unsigned alloced;
+
+ /* Returns the proc context that these processes are from. */
+ struct ps_context *context;
+ };
+
+/* Accessor macros: */
+#define proc_stat_list_num_procs(pp) ((pp)->num_procs)
+#define proc_stat_list_context(pp) ((pp)->context)
+
+/* Creates a new proc_stat_list_t for processes from CONTEXT, which is
+ returned in PP, and returns 0, or else returns ENOMEM if there wasn't
+ enough memory. */
+error_t proc_stat_list_create (struct ps_context *context,
+ struct proc_stat_list **pp);
+
+/* Free PP, and any resources it consumes. */
+void proc_stat_list_free (struct proc_stat_list *pp);
+
+/* Returns a copy of PP in COPY, or an error. */
+error_t proc_stat_list_clone (struct proc_stat_list *pp,
+ struct proc_stat_list **copy);
+
+/* Returns the proc_stat in PP with a process-id of PID, if there's one,
+ otherwise, NULL. */
+struct proc_stat *proc_stat_list_pid_proc_stat (struct proc_stat_list *pp,
+ pid_t pid);
+
+/* Add proc_stat entries to PP for each process with a process id in the
+ array PIDS (where NUM_PROCS is the length of PIDS). Entries are only
+ added for processes not already in PP. ENOMEM is returned if a memory
+ allocation error occurs, otherwise 0. PIDs is not referenced by the
+ resulting proc_stat_list_t, and so may be subsequently freed. If
+ PROC_STATS is non-NULL, a malloced array NUM_PROCS entries long of the
+ resulting proc_stats is returned in it. */
+error_t proc_stat_list_add_pids (struct proc_stat_list *pp,
+ pid_t *pids, unsigned num_procs,
+ struct proc_stat ***proc_stats);
+
+/* Add a proc_stat for the process designated by PID at PP's proc context
+ to PP. If PID already has an entry in PP, nothing is done. If a memory
+ allocation error occurs, ENOMEM is returned, otherwise 0. If PS is
+ non-NULL, the resulting entry is returned in it. */
+error_t proc_stat_list_add_pid (struct proc_stat_list *pp, pid_t pid,
+ struct proc_stat **ps);
+
+/* Adds all proc_stats in MERGEE to PP that don't correspond to processes
+ already in PP; the resulting order of proc_stats in PP is undefined.
+ If MERGEE and PP point to different proc contexts, EINVAL is returned. If a
+ memory allocation error occurs, ENOMEM is returned. Otherwise 0 is
+ returned, and MERGEE is freed. */
+error_t proc_stat_list_merge (struct proc_stat_list *pp,
+ struct proc_stat_list *mergee);
+
+/* Add to PP entries for all processes at its context. If an error occurs,
+ the system error code is returned, otherwise 0. If PROC_STATS and
+ NUM_PROCS are non-NULL, a malloced vector of the resulting entries is
+ returned in them. */
+error_t proc_stat_list_add_all (struct proc_stat_list *pp,
+ struct proc_stat ***proc_stats,
+ size_t *num_procs);
+
+/* Add to PP entries for all processes in the login collection LOGIN_ID at
+ its context. If an error occurs, the system error code is returned,
+ otherwise 0. If PROC_STATS and NUM_PROCS are non-NULL, a malloced vector
+ of the resulting entries is returned in them. */
+error_t proc_stat_list_add_login_coll (struct proc_stat_list *pp,
+ pid_t login_id,
+ struct proc_stat ***proc_stats,
+ size_t *num_procs);
+
+/* Add to PP entries for all processes in the session SESSION_ID at its
+ context. If an error occurs, the system error code is returned, otherwise
+ 0. If PROC_STATS and NUM_PROCS are non-NULL, a malloced vector of the
+ resulting entries is returned in them. */
+error_t proc_stat_list_add_session (struct proc_stat_list *pp,
+ pid_t session_id,
+ struct proc_stat ***proc_stats,
+ size_t *num_procs);
+
+/* Add to PP entries for all processes in the process group PGRP at its
+ context. If an error occurs, the system error code is returned, otherwise
+ 0. If PROC_STATS and NUM_PROCS are non-NULL, a malloced vector of the
+ resulting entries is returned in them. */
+error_t proc_stat_list_add_pgrp (struct proc_stat_list *pp, pid_t pgrp,
+ struct proc_stat ***proc_stats,
+ size_t *num_procs);
+
+/* Try to set FLAGS in each proc_stat in PP (but they may still not be set
+ -- you have to check). If a fatal error occurs, the error code is
+ returned, otherwise 0. */
+error_t proc_stat_list_set_flags (struct proc_stat_list *pp, ps_flags_t flags);
+
+/* Destructively modify PP to only include proc_stats for which the
+ function PREDICATE returns true; if INVERT is true, only proc_stats for
+ which PREDICATE returns false are kept. FLAGS is the set of pstat_flags
+ that PREDICATE requires be set as precondition. Regardless of the value
+ of INVERT, all proc_stats for which the predicate's preconditions can't
+ be satisfied are kept. If a fatal error occurs, the error code is
+ returned, it returns 0. */
+error_t proc_stat_list_filter1 (struct proc_stat_list *pp,
+ int (*predicate)(struct proc_stat *ps),
+ ps_flags_t flags,
+ int invert);
+
+/* Destructively modify PP to only include proc_stats for which the
+ predicate function in FILTER returns true; if INVERT is true, only
+ proc_stats for which the predicate returns false are kept. Regardless
+ of the value of INVERT, all proc_stats for which the predicate's
+ preconditions can't be satisfied are kept. If a fatal error occurs,
+ the error code is returned, it returns 0. */
+error_t proc_stat_list_filter (struct proc_stat_list *pp,
+ const struct ps_filter *filter, int invert);
+
+/* Destructively sort proc_stats in PP by ascending value of the field
+ returned by GETTER, and compared by CMP_FN; If REVERSE is true, use the
+ opposite order. If a fatal error occurs, the error code is returned, it
+ returns 0. */
+error_t proc_stat_list_sort1 (struct proc_stat_list *pp,
+ const struct ps_getter *getter,
+ int (*cmp_fn)(struct proc_stat *ps1,
+ struct proc_stat *ps2,
+ const struct ps_getter *getter),
+ int reverse);
+
+/* Destructively sort proc_stats in PP by ascending value of the field KEY;
+ if REVERSE is true, use the opposite order. If KEY isn't a valid sort
+ key, EINVAL is returned. If a fatal error occurs the error code is
+ returned. Otherwise, 0 is returned. */
+error_t proc_stat_list_sort (struct proc_stat_list *pp,
+ const struct ps_fmt_spec *key, int reverse);
+
+/* Format a description as instructed by FMT, of the processes in PP to
+ STREAM, separated by newlines (and with a terminating newline). If COUNT
+ is non-NULL, it points to an integer which is incremented by the number of
+ characters output. If a fatal error occurs, the error code is returned,
+ otherwise 0. */
+error_t proc_stat_list_fmt (struct proc_stat_list *pp, struct ps_fmt *fmt,
+ struct ps_stream *stream);
+
+/* Modifies FLAGS to be the subset which can't be set in any proc_stat in
+ PP (and as a side-effect, adds as many bits from FLAGS to each proc_stat
+ as possible). If a fatal error occurs, the error code is returned,
+ otherwise 0. */
+error_t proc_stat_list_find_bogus_flags (struct proc_stat_list *pp,
+ ps_flags_t *flags);
+
+/* Add thread entries for for every process in PP, located immediately after
+ the containing process in sequence. Subsequent sorting of PP will leave
+ the thread entries located after the containing process, although the
+ order of the thread entries themselves may change. If a fatal error
+ occurs, the error code is returned, otherwise 0. */
+error_t proc_stat_list_add_threads (struct proc_stat_list *pp);
+
+error_t proc_stat_list_remove_threads (struct proc_stat_list *pp);
+
+/* Calls FN in order for each proc_stat in PP. If FN ever returns a non-zero
+ value, then the iteration is stopped, and the value is returned
+ immediately; otherwise, 0 is returned. */
+int proc_stat_list_for_each (struct proc_stat_list *pp,
+ int (*fn)(struct proc_stat *ps));
+
+/* Returns true if SPEC is `nominal' in every entry in PP. */
+int proc_stat_list_spec_nominal (struct proc_stat_list *pp,
+ const struct ps_fmt_spec *spec);
+
+/* The Basic & Sched info types are pretty static, so we cache them, but load
+ info is dynamic so we don't cache that. See <mach/host_info.h> for
+ information on the data types these routines return. */
+
+/* Return the current host port. */
+mach_port_t ps_get_host ();
+
+/* Return a pointer to basic info about the current host in HOST_INFO. Since
+ this is static global information we just use a static buffer. If a
+ system error occurs, the error code is returned, otherwise 0. */
+error_t ps_host_basic_info (host_basic_info_t *host_info);
+
+/* Return a pointer to scheduling info about the current host in HOST_INFO.
+ Since this is static global information we just use a static buffer. If a
+ system error occurs, the error code is returned, otherwise 0. */
+error_t ps_host_sched_info (host_sched_info_t *host_info);
+
+/* Return a pointer to load info about the current host in HOST_INFO. Since
+ this is global information we just use a static buffer (if someone desires
+ to keep old load info, they should copy the buffer we return a pointer
+ to). If a system error occurs, the error code is returned, otherwise 0. */
+error_t ps_host_load_info (host_load_info_t *host_info);
+
+#endif /* __PS_H__ */
diff --git a/libps/pshost.h b/libps/pshost.h
new file mode 100644
index 00000000..62b74876
--- /dev/null
+++ b/libps/pshost.h
@@ -0,0 +1,53 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __PSHOST_H__
+#define __PSHOST_H__
+
+#include <mach/mach_types.h>
+#include <mach/host_info.h>
+
+/* ---------------------------------------------------------------- */
+
+/*
+ The Basic & Sched info types are pretty static, so we cache them, but load
+ info is dynamic so we don't cache that.
+
+ See <mach/host_info.h> for information on the data types these routines
+ return.
+*/
+
+/* Return the current host port. */
+host_t ps_get_host();
+
+/* Return a pointer to basic info about the current host in HOST_INFO. Since
+ this is static global information we just use a static buffer. If a
+ system error occurs, the error code is returned, otherwise 0. */
+error_t ps_host_basic_info(host_basic_info_t *host_info);
+
+/* Return a pointer to scheduling info about the current host in HOST_INFO.
+ Since this is static global information we just use a static buffer. If a
+ system error occurs, the error code is returned, otherwise 0. */
+error_t ps_host_sched_info(host_sched_info_t *host_info);
+
+/* Return a pointer to load info about the current host in HOST_INFO. Since
+ this is global information we just use a static buffer (if someone desires
+ to keep old load info, they should copy the buffer we return a pointer
+ to). If a system error occurs, the error code is returned, otherwise 0. */
+error_t ps_host_load_info(host_load_info_t *host_info);
+
+#endif /* __PSHOST_H__ */
diff --git a/libps/spec.c b/libps/spec.c
new file mode 100644
index 00000000..d645b825
--- /dev/null
+++ b/libps/spec.c
@@ -0,0 +1,1171 @@
+/* Access, formatting, & comparison routines for printing process info.
+
+ Copyright (C) 1995,96,97,99,2001,02 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <pwd.h>
+#include <hurd/resource.h>
+#include <unistd.h>
+#include <string.h>
+#include <timefmt.h>
+#include <sys/time.h>
+
+#include "ps.h"
+#include "common.h"
+
+/* XXX */
+static char *get_syscall_name (int num) { return 0; }
+static char *get_rpc_name (mach_msg_id_t it) { return 0; }
+
+/* ---------------------------------------------------------------- */
+/* Getter definitions */
+
+typedef void (*vf)();
+
+static int
+ps_get_pid (struct proc_stat *ps)
+{
+ return proc_stat_pid (ps);
+}
+const struct ps_getter ps_pid_getter =
+{"pid", PSTAT_PID, (vf) ps_get_pid};
+
+static int
+ps_get_thread_index (struct proc_stat *ps)
+{
+ return proc_stat_thread_index (ps);
+}
+const struct ps_getter ps_thread_index_getter =
+{"thread_index", PSTAT_THREAD, (vf) ps_get_thread_index};
+
+static struct ps_user *
+ps_get_owner (struct proc_stat *ps)
+{
+ return proc_stat_owner (ps);
+}
+const struct ps_getter ps_owner_getter =
+{"owner", PSTAT_OWNER, (vf) ps_get_owner};
+
+static int
+ps_get_owner_uid (struct proc_stat *ps)
+{
+ return proc_stat_owner_uid (ps);
+}
+const struct ps_getter ps_owner_uid_getter =
+{"uid", PSTAT_OWNER_UID, (vf) ps_get_owner_uid};
+
+static int
+ps_get_ppid (struct proc_stat *ps)
+{
+ return proc_stat_proc_info (ps)->ppid;
+}
+const struct ps_getter ps_ppid_getter =
+{"ppid", PSTAT_PROC_INFO, (vf) ps_get_ppid};
+
+static int
+ps_get_pgrp (struct proc_stat *ps)
+{
+ return proc_stat_proc_info (ps)->pgrp;
+}
+const struct ps_getter ps_pgrp_getter =
+{"pgrp", PSTAT_PROC_INFO, (vf) ps_get_pgrp};
+
+static int
+ps_get_session (struct proc_stat *ps)
+{
+ return proc_stat_proc_info (ps)->session;
+}
+const struct ps_getter ps_session_getter =
+{"session", PSTAT_PROC_INFO, (vf) ps_get_session};
+
+static int
+ps_get_login_col (struct proc_stat *ps)
+{
+ return proc_stat_proc_info (ps)->logincollection;
+}
+const struct ps_getter ps_login_col_getter =
+{"login_col", PSTAT_PROC_INFO, (vf) ps_get_login_col};
+
+static int
+ps_get_num_threads (struct proc_stat *ps)
+{
+ return proc_stat_num_threads (ps);
+}
+const struct ps_getter ps_num_threads_getter =
+{"num_threads", PSTAT_NUM_THREADS, (vf)ps_get_num_threads};
+
+static void
+ps_get_args (struct proc_stat *ps, char **args_p, int *args_len_p)
+{
+ *args_p = proc_stat_args (ps);
+ *args_len_p = proc_stat_args_len (ps);
+}
+const struct ps_getter ps_args_getter =
+{"args", PSTAT_ARGS, ps_get_args};
+
+static void
+ps_get_env (struct proc_stat *ps, char **env_p, int *env_len_p)
+{
+ *env_p = proc_stat_env (ps);
+ *env_len_p = proc_stat_env_len (ps);
+}
+const struct ps_getter ps_env_getter =
+{"env", PSTAT_ENV, ps_get_env};
+
+static int
+ps_get_state (struct proc_stat *ps)
+{
+ return proc_stat_state (ps);
+}
+const struct ps_getter ps_state_getter =
+{"state", PSTAT_STATE, (vf) ps_get_state};
+
+static void
+ps_get_wait (struct proc_stat *ps, char **wait, int *rpc)
+{
+ *wait = ps->thread_wait;
+ *rpc = ps->thread_rpc;
+}
+const struct ps_getter ps_wait_getter =
+{"wait", PSTAT_THREAD_WAIT, ps_get_wait};
+
+static size_t
+ps_get_vsize (struct proc_stat *ps)
+{
+ return proc_stat_task_basic_info (ps)->virtual_size;
+}
+const struct ps_getter ps_vsize_getter =
+{"vsize", PSTAT_TASK_BASIC, (vf) ps_get_vsize};
+
+static size_t
+ps_get_rsize (struct proc_stat *ps)
+{
+ return proc_stat_task_basic_info (ps)->resident_size;
+}
+const struct ps_getter ps_rsize_getter =
+{"rsize", PSTAT_TASK_BASIC, (vf) ps_get_rsize};
+
+static int
+ps_get_cur_priority (struct proc_stat *ps)
+{
+ return proc_stat_thread_basic_info (ps)->cur_priority;
+}
+const struct ps_getter ps_cur_priority_getter =
+{"cur_priority", PSTAT_THREAD_BASIC, (vf) ps_get_cur_priority};
+
+static int
+ps_get_base_priority (struct proc_stat *ps)
+{
+ return proc_stat_thread_basic_info (ps)->base_priority;
+}
+const struct ps_getter ps_base_priority_getter =
+{"base_priority", PSTAT_THREAD_BASIC, (vf) ps_get_base_priority};
+
+static int
+ps_get_max_priority (struct proc_stat *ps)
+{
+ return proc_stat_thread_sched_info (ps)->max_priority;
+}
+const struct ps_getter ps_max_priority_getter =
+{"max_priority", PSTAT_THREAD_SCHED, (vf) ps_get_max_priority};
+
+static void
+ps_get_usr_time (struct proc_stat *ps, struct timeval *tv)
+{
+ time_value_t tvt = proc_stat_thread_basic_info (ps)->user_time;
+ tv->tv_sec = tvt.seconds;
+ tv->tv_usec = tvt.microseconds;
+}
+const struct ps_getter ps_usr_time_getter =
+{"usr_time", PSTAT_TIMES, ps_get_usr_time};
+
+static void
+ps_get_sys_time (struct proc_stat *ps, struct timeval *tv)
+{
+ time_value_t tvt = proc_stat_thread_basic_info (ps)->system_time;
+ tv->tv_sec = tvt.seconds;
+ tv->tv_usec = tvt.microseconds;
+}
+const struct ps_getter ps_sys_time_getter =
+{"sys_time", PSTAT_TIMES, ps_get_sys_time};
+
+static void
+ps_get_tot_time (struct proc_stat *ps, struct timeval *tv)
+{
+ time_value_t tvt = proc_stat_thread_basic_info (ps)->user_time;
+ time_value_add (&tvt, &proc_stat_thread_basic_info (ps)->system_time);
+ tv->tv_sec = tvt.seconds;
+ tv->tv_usec = tvt.microseconds;
+}
+const struct ps_getter ps_tot_time_getter =
+{"tot_time", PSTAT_TIMES, ps_get_tot_time};
+
+static void
+ps_get_start_time (struct proc_stat *ps, struct timeval *tv)
+{
+ time_value_t *const tvt = &proc_stat_task_basic_info (ps)->creation_time;
+ tv->tv_sec = tvt->seconds;
+ tv->tv_usec = tvt->microseconds;
+}
+const struct ps_getter ps_start_time_getter =
+{"start_time", PSTAT_TASK_BASIC, ps_get_start_time};
+
+static float
+ps_get_rmem_frac (struct proc_stat *ps)
+{
+ static size_t mem_size = 0;
+
+ if (mem_size == 0)
+ {
+ host_basic_info_t info;
+ error_t err = ps_host_basic_info (&info);
+ if (err == 0)
+ mem_size = info->memory_size;
+ }
+
+ if (mem_size > 0)
+ return
+ (float)proc_stat_task_basic_info (ps)->resident_size
+ / (float)mem_size;
+ else
+ return 0.0;
+}
+const struct ps_getter ps_rmem_frac_getter =
+{"rmem_frac", PSTAT_TASK_BASIC, (vf) ps_get_rmem_frac};
+
+static float
+ps_get_cpu_frac (struct proc_stat *ps)
+{
+ return (float) proc_stat_thread_basic_info (ps)->cpu_usage
+ / (float) TH_USAGE_SCALE;
+}
+const struct ps_getter ps_cpu_frac_getter =
+{"cpu_frac", PSTAT_THREAD_BASIC, (vf) ps_get_cpu_frac};
+
+static int
+ps_get_sleep (struct proc_stat *ps)
+{
+ return proc_stat_thread_basic_info (ps)->sleep_time;
+}
+const struct ps_getter ps_sleep_getter =
+{"sleep", PSTAT_THREAD_BASIC, (vf) ps_get_sleep};
+
+static int
+ps_get_susp_count (struct proc_stat *ps)
+{
+ return proc_stat_suspend_count (ps);
+}
+const struct ps_getter ps_susp_count_getter =
+{"susp_count", PSTAT_SUSPEND_COUNT, (vf) ps_get_susp_count};
+
+static int
+ps_get_proc_susp_count (struct proc_stat *ps)
+{
+ return proc_stat_task_basic_info (ps)->suspend_count;
+}
+const struct ps_getter ps_proc_susp_count_getter =
+{"proc_susp_count", PSTAT_TASK_BASIC, (vf) ps_get_proc_susp_count};
+
+static int
+ps_get_thread_susp_count (struct proc_stat *ps)
+{
+ return proc_stat_thread_basic_info (ps)->suspend_count;
+}
+const struct ps_getter ps_thread_susp_count_getter =
+{"thread_susp_count", PSTAT_SUSPEND_COUNT, (vf) ps_get_thread_susp_count};
+
+static struct ps_tty *
+ps_get_tty (struct proc_stat *ps)
+{
+ return proc_stat_tty (ps);
+}
+const struct ps_getter ps_tty_getter =
+{"tty", PSTAT_TTY, (vf)ps_get_tty};
+
+static int
+ps_get_page_faults (struct proc_stat *ps)
+{
+ return proc_stat_task_events_info (ps)->faults;
+}
+const struct ps_getter ps_page_faults_getter =
+{"page_faults", PSTAT_TASK_EVENTS, (vf) ps_get_page_faults};
+
+static int
+ps_get_cow_faults (struct proc_stat *ps)
+{
+ return proc_stat_task_events_info (ps)->cow_faults;
+}
+const struct ps_getter ps_cow_faults_getter =
+{"cow_faults", PSTAT_TASK_EVENTS, (vf) ps_get_cow_faults};
+
+static int
+ps_get_pageins (struct proc_stat *ps)
+{
+ return proc_stat_task_events_info (ps)->pageins;
+}
+const struct ps_getter ps_pageins_getter =
+{"pageins", PSTAT_TASK_EVENTS, (vf) ps_get_pageins};
+
+static int
+ps_get_msgs_sent (struct proc_stat *ps)
+{
+ return proc_stat_task_events_info (ps)->messages_sent;
+}
+const struct ps_getter ps_msgs_sent_getter =
+{"msgs_sent", PSTAT_TASK_EVENTS, (vf) ps_get_msgs_sent};
+
+static int
+ps_get_msgs_rcvd (struct proc_stat *ps)
+{
+ return proc_stat_task_events_info (ps)->messages_received;
+}
+const struct ps_getter ps_msgs_rcvd_getter =
+{"msgs_rcvd", PSTAT_TASK_EVENTS, (vf) ps_get_msgs_rcvd};
+
+static int
+ps_get_zero_fills (struct proc_stat *ps)
+{
+ return proc_stat_task_events_info (ps)->zero_fills;
+}
+const struct ps_getter ps_zero_fills_getter =
+{"zero_fills", PSTAT_TASK_EVENTS, (vf) ps_get_zero_fills};
+
+static int
+ps_get_num_ports (struct proc_stat *ps)
+{
+ return proc_stat_num_ports (ps);
+}
+const struct ps_getter ps_num_ports_getter =
+{"num_ports", PSTAT_NUM_PORTS, (vf) ps_get_num_ports};
+
+/* ---------------------------------------------------------------- */
+/* some printing functions */
+
+/* G () is a helpful macro that just returns the getter G's access function
+ cast into a function pointer returning TYPE, as how the function should be
+ called varies depending on the getter. */
+#define G(getter,type) ((type (*)())((getter)->fn))
+
+/* Similar to G, but takes a fmt field and uses its getter. */
+#define FG(field,type) G(field->spec->getter, type)
+
+error_t
+ps_emit_int (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ return ps_stream_write_int_field (stream, FG (field, int)(ps), field->width);
+}
+
+error_t
+ps_emit_nz_int (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ int value = FG (field, int)(ps);
+ if (value)
+ return ps_stream_write_int_field (stream, value, field->width);
+ else
+ return ps_stream_write_field (stream, "-", field->width);
+}
+
+error_t
+ps_emit_priority (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ return
+ ps_stream_write_int_field (stream,
+ MACH_PRIORITY_TO_NICE (FG (field, int)(ps)),
+ field->width);
+}
+
+error_t
+ps_emit_num_blocks (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ char buf[20];
+ sprintf(buf, "%d", FG (field, int)(ps) / 1024);
+ return ps_stream_write_field (stream, buf, field->width);
+}
+
+size_t
+sprint_frac_value (char *buf,
+ size_t value, int min_value_len,
+ size_t frac, int frac_scale,
+ int width)
+{
+ int value_len = 0;
+ int frac_len = 0;
+
+ if (value >= 1000) /* the integer part */
+ value_len = 4; /* values 1000-1023 */
+ else if (value >= 100)
+ value_len = 3;
+ else if (value >= 10)
+ value_len = 2;
+ else
+ value_len = 1;
+
+ while (value_len < min_value_len--)
+ *buf++ = '0';
+
+ for (frac_len = frac_scale
+ ; frac_len > 0 && (width < value_len + 1 + frac_len || frac % 10 == 0)
+ ; frac_len--)
+ frac /= 10;
+
+ if (frac_len > 0)
+ sprintf (buf, "%zd.%0*zd", value, frac_len, frac);
+ else
+ sprintf (buf, "%zd", value);
+
+ return strlen (buf);
+}
+
+error_t
+ps_emit_percent (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ char buf[20];
+ int width = field->width;
+ float perc = FG (field, float)(ps) * 100;
+
+ if (width == 0)
+ sprintf (buf, "%g", perc);
+ else if (ABS (width) > 3)
+ sprintf(buf, "%.*f", ABS (width) - 3, perc);
+ else
+ sprintf (buf, "%d", (int) perc);
+
+ return ps_stream_write_field (stream, buf, width);
+}
+
+/* prints its value nicely */
+error_t
+ps_emit_nice_size_t (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ char buf[20];
+ size_t value = FG (field, size_t)(ps);
+ char *sfx = " KMG";
+ size_t frac = 0;
+
+ while (value >= 1024)
+ {
+ frac = ((value & 0x3FF) * 1000) >> 10;
+ value >>= 10;
+ sfx++;
+ }
+
+ sprintf(buf
+ + sprint_frac_value (buf, value, 1, frac, 3, ABS (field->width) - 1),
+ "%c", *sfx);
+
+ return ps_stream_write_field (stream, buf, field->width);
+}
+
+error_t
+ps_emit_seconds (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ char buf[20];
+ struct timeval tv;
+ int width = field->width, prec = field->precision;
+
+ FG (field, void)(ps, &tv);
+
+ if ((field->flags & PS_FMT_FIELD_COLON_MOD) && tv.tv_sec == 0)
+ strcpy (buf, "-");
+ else
+ fmt_seconds (&tv, !(field->flags & PS_FMT_FIELD_AT_MOD), prec, ABS (width),
+ buf, sizeof (buf));
+
+ return ps_stream_write_field (stream, buf, width);
+}
+
+error_t
+ps_emit_minutes (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ char buf[20];
+ struct timeval tv;
+ int width = field->width;
+
+ FG (field, void)(ps, &tv);
+
+ if ((field->flags & PS_FMT_FIELD_COLON_MOD) && tv.tv_sec < 60)
+ strcpy (buf, "-");
+ else
+ fmt_minutes (&tv, !(field->flags & PS_FMT_FIELD_AT_MOD), ABS (width),
+ buf, sizeof (buf));
+
+ return ps_stream_write_field (stream, buf, width);
+}
+
+error_t
+ps_emit_past_time (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ static struct timeval now;
+ char buf[20];
+ struct timeval tv;
+ int width = field->width;
+
+ FG (field, void)(ps, &tv);
+
+ if (now.tv_sec == 0 && gettimeofday (&now, 0) < 0)
+ return errno;
+
+ fmt_past_time (&tv, &now, ABS (width), buf, sizeof buf);
+
+ return ps_stream_write_field (stream, buf, width);
+}
+
+error_t
+ps_emit_uid (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ int uid = FG (field, int)(ps);
+ if (uid < 0)
+ return ps_stream_write_field (stream, "-", field->width);
+ else
+ return ps_stream_write_int_field (stream, uid, field->width);
+}
+
+error_t
+ps_emit_uname (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ int width = field->width;
+ struct ps_user *u = FG (field, struct ps_user *)(ps);
+ if (u)
+ {
+ struct passwd *pw = ps_user_passwd (u);
+ if (pw == NULL)
+ return ps_stream_write_int_field (stream, ps_user_uid (u), width);
+ else
+ return ps_stream_write_field (stream, pw->pw_name, width);
+ }
+ else
+ return ps_stream_write_field (stream, "-", width);
+}
+
+error_t
+ps_emit_user_name (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ int width = field->width;
+ struct ps_user *u = FG (field, struct ps_user *)(ps);
+ if (u)
+ {
+ struct passwd *pw = ps_user_passwd (u);
+ if (pw == NULL)
+ {
+ char buf[20];
+ sprintf (buf, "(UID %d)", u->uid);
+ return ps_stream_write_field (stream, buf, width);
+ }
+ else
+ return ps_stream_write_field (stream, pw->pw_gecos, width);
+ }
+ else
+ return ps_stream_write_field (stream, "-", width);
+}
+
+/* prints a string with embedded nuls as spaces */
+error_t
+ps_emit_args (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ char *s0, *p, *q;
+ int s0len;
+ int width = field->width;
+ int fwidth = ABS (width);
+ char static_buf[200];
+ char *buf = static_buf;
+
+ FG (field, void)(ps, &s0, &s0len);
+
+ if (!s0 || s0len == 0 )
+ strcpy (buf, "-");
+ else
+ {
+ if (s0len > sizeof static_buf)
+ {
+ buf = malloc (s0len + 1);
+ if (buf == NULL)
+ return ENOMEM;
+ }
+
+ if (fwidth == 0 || fwidth > s0len)
+ fwidth = s0len;
+
+ for (p = buf, q = s0; fwidth-- > 0; p++, q++)
+ {
+ int ch = *q;
+ *p = (ch == '\0' ? ' ' : ch);
+ }
+ if (q > s0 && *(q - 1) == '\0')
+ *--p = '\0';
+ else
+ *p = '\0';
+ }
+
+ {
+ error_t err = ps_stream_write_trunc_field (stream, buf, width);
+ if (buf != static_buf)
+ free (buf);
+ return err;
+ }
+}
+
+error_t
+ps_emit_string (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ char *str;
+ int len;
+
+ FG (field, void)(ps, &str, &len);
+
+ if (!str || len == 0)
+ str = "-";
+
+ return ps_stream_write_trunc_field (stream, str, field->width);
+}
+
+error_t
+ps_emit_tty_name (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ const char *name = "-";
+ struct ps_tty *tty = FG (field, struct ps_tty *)(ps);
+
+ if (tty)
+ {
+ name = ps_tty_short_name (tty);
+ if (name == NULL || *name == '\0')
+ name = "?";
+ }
+
+ return ps_stream_write_field (stream, name, field->width);
+}
+
+struct state_shadow
+{
+ /* If any states in STATES are set, the states in shadow are suppressed. */
+ int states;
+ int shadow;
+};
+
+static const struct state_shadow
+state_shadows[] = {
+ /* If the process has no parent, it's not a hurd process, and various hurd
+ process bits are likely to be noise, so turn them off (but leave the
+ noparent bit on). */
+ { PSTAT_STATE_P_NOPARENT, (PSTAT_STATE_P_ATTRS & ~PSTAT_STATE_P_NOPARENT) },
+ /* Don't show sleeping thread if one is running, or the process is stopped.*/
+ { PSTAT_STATE_T_RUN | PSTAT_STATE_P_STOP,
+ PSTAT_STATE_T_SLEEP | PSTAT_STATE_T_IDLE | PSTAT_STATE_T_WAIT },
+ /* Only show the longest sleep. */
+ { PSTAT_STATE_T_IDLE, PSTAT_STATE_T_SLEEP | PSTAT_STATE_T_WAIT },
+ { PSTAT_STATE_T_SLEEP, PSTAT_STATE_T_WAIT },
+ /* Turn off the thread stop bits if any thread is not stopped. This is
+ generally reasonable, as threads are often suspended to be frobed; if
+ they're all suspended, then something's odd (probably in the debugger,
+ or crashed). */
+ { PSTAT_STATE_T_STATES & ~PSTAT_STATE_T_HALT,
+ PSTAT_STATE_T_HALT | PSTAT_STATE_T_UNCLEAN },
+ { 0 }
+};
+
+error_t
+ps_emit_state (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ char *tags;
+ int raw_state = FG (field, int)(ps);
+ int state = raw_state;
+ char buf[20], *p = buf;
+ const struct state_shadow *shadow = state_shadows;
+
+ while (shadow->states)
+ {
+ if (raw_state & shadow->states)
+ state &= ~shadow->shadow;
+ shadow++;
+ }
+
+ for (tags = proc_stat_state_tags
+ ; state != 0 && *tags != '\0'
+ ; state >>= 1, tags++)
+ if (state & 1)
+ *p++ = *tags;
+
+ *p = '\0';
+
+ return ps_stream_write_field (stream, buf, field->width);
+}
+
+error_t
+ps_emit_wait (struct proc_stat *ps, struct ps_fmt_field *field,
+ struct ps_stream *stream)
+{
+ int rpc;
+ char *wait;
+ char buf[80];
+
+ FG (field, void)(ps, &wait, &rpc);
+
+ if (wait == 0)
+ return ps_stream_write_field (stream, "?", field->width);
+ else if (*wait == 0)
+ return ps_stream_write_field (stream, "-", field->width);
+ else if (strcmp (wait, "kernel") == 0)
+ /* A syscall. RPC is actually the syscall number. */
+ {
+ char *name = get_syscall_name (rpc);
+ if (! name)
+ {
+ sprintf (buf, "syscall:%d", -rpc);
+ name = buf;
+ }
+ return ps_stream_write_field (stream, name, field->width);
+ }
+ else if (rpc)
+ /* An rpc (with msg id RPC); WAIT describes the dest port. */
+ {
+ char port_name_buf[20];
+ char *name = get_rpc_name (rpc);
+
+ /* See if we should give a more useful name for the port. */
+ if (strcmp (wait, "init#0") == 0)
+ wait = "cwd"; /* Current directory */
+ else if (strcmp (wait, "init#1") == 0)
+ wait = "root"; /* Root directory */
+ else if (strcmp (wait, "init#2") == 0)
+ wait = "auth"; /* Auth port */
+ else if (strcmp (wait, "init#3") == 0)
+ wait = "proc"; /* Proc port */
+ else if (strcmp (wait, "init#4") == 0)
+ wait = "cttyid"; /* Ctty id port */
+ else if (strcmp (wait, "init#5") == 0)
+ wait = "boot"; /* Bootstrap port */
+ else
+ /* See if we can shorten the name to fit better. */
+ {
+ char *abbrev = 0, *num = 0;
+ if (strncmp (wait, "fd#", 3) == 0)
+ abbrev = "fd", num = wait + 3;
+ else if (strncmp (wait, "bgfd#", 5) == 0)
+ abbrev = "bg", num = wait + 5;
+ else if (strncmp (wait, "port#", 5) == 0)
+ abbrev = "", num = wait + 5;
+ if (abbrev)
+ {
+ snprintf (port_name_buf, sizeof port_name_buf,
+ "%s%s", abbrev, num);
+ wait = port_name_buf;
+ }
+ }
+
+ if (name)
+ snprintf (buf, sizeof buf, "%s:%s", wait, name);
+ else
+ snprintf (buf, sizeof buf, "%s:%d", wait, rpc);
+
+ return ps_stream_write_field (stream, buf, field->width);
+ }
+ else
+ return ps_stream_write_field (stream, wait, field->width);
+}
+/* ---------------------------------------------------------------- */
+/* comparison functions */
+
+/* Evaluates CALL if both s1 & s2 are non-NULL, and otherwise returns -1, 0,
+ or 1 ala strcmp, considering NULL to be less than non-NULL. */
+#define GUARDED_CMP(s1, s2, call) \
+ ((s1) == NULL ? (((s2) == NULL) ? 0 : -1) : ((s2) == NULL ? 1 : (call)))
+
+int
+ps_cmp_ints (struct proc_stat *ps1, struct proc_stat *ps2,
+ const struct ps_getter *getter)
+{
+ int (*gf)() = G (getter, int);
+ int v1 = gf(ps1), v2 = gf (ps2);
+ return v1 == v2 ? 0 : v1 < v2 ? -1 : 1;
+}
+
+int
+ps_cmp_floats (struct proc_stat *ps1, struct proc_stat *ps2,
+ const struct ps_getter *getter)
+{
+ float (*gf)() = G (getter, float);
+ float v1 = gf(ps1), v2 = gf (ps2);
+ return v1 == v2 ? 0 : v1 < v2 ? -1 : 1;
+}
+
+int
+ps_cmp_size_ts (struct proc_stat *ps1, struct proc_stat *ps2,
+ const struct ps_getter *getter)
+{
+ size_t (*gf)() = G (getter, size_t);
+ size_t v1 = gf(ps1), v2 = gf (ps2);
+ return v1 == v2 ? 0 : v1 < v2 ? -1 : 1;
+}
+
+int
+ps_cmp_uids (struct proc_stat *ps1, struct proc_stat *ps2,
+ const struct ps_getter *getter)
+{
+ struct ps_user *(*gf)() = G (getter, struct ps_user *);
+ struct ps_user *u1 = gf (ps1), *u2 = gf (ps2);
+ return (u1 ? ps_user_uid (u1) : -1) - (u2 ? ps_user_uid (u2) : -1);
+}
+
+int
+ps_cmp_unames (struct proc_stat *ps1, struct proc_stat *ps2,
+ const struct ps_getter *getter)
+{
+ struct ps_user *(*gf)() = G (getter, struct ps_user *);
+ struct ps_user *u1 = gf (ps1), *u2 = gf (ps2);
+ struct passwd *pw1 = u1 ? ps_user_passwd (u1) : 0;
+ struct passwd *pw2 = u2 ? ps_user_passwd (u2) : 0;
+ return GUARDED_CMP (pw1, pw2, strcmp (pw1->pw_name, pw2->pw_name));
+}
+
+int
+ps_cmp_strings (struct proc_stat *ps1, struct proc_stat *ps2,
+ const struct ps_getter *getter)
+{
+ void (*gf)() = G (getter, void);
+ char *s1, *s2;
+ int s1len, s2len;
+
+ /* Get both strings */
+ gf (ps1, &s1, &s1len);
+ gf (ps2, &s2, &s2len);
+
+ return GUARDED_CMP(s1, s2, strncmp(s1, s2, MIN (s1len, s2len)));
+}
+
+int
+ps_cmp_times (struct proc_stat *ps1, struct proc_stat *ps2,
+ const struct ps_getter *getter)
+{
+ void (*g)() = G (getter, void);
+ struct timeval tv1, tv2;
+
+ g (ps1, &tv1);
+ g (ps2, &tv2);
+
+ return
+ tv1.tv_sec > tv2.tv_sec ? 1
+ : tv1.tv_sec < tv2.tv_sec ? -1
+ : tv1.tv_usec > tv2.tv_usec ? 1
+ : tv2.tv_usec < tv2.tv_usec ? -1
+ : 0;
+}
+
+/* ---------------------------------------------------------------- */
+/* `Nominal' functions -- return true for `unexciting' values. */
+
+/* For many things, zero is not so interesting. */
+int
+ps_nominal_zint (struct proc_stat *ps, const struct ps_getter *getter)
+{
+ return G (getter, int)(ps) == 0;
+}
+
+/* Neither is an empty string. */
+int
+ps_nominal_string (struct proc_stat *ps, const struct ps_getter *getter)
+{
+ char *str;
+ size_t len;
+ G (getter, char *)(ps, &str, &len);
+ return !str || len == 0 || (len == 1 && *str == '-');
+}
+
+/* Priorities are similar, but have to be converted to the unix nice scale
+ first. */
+int
+ps_nominal_pri (struct proc_stat *ps, const struct ps_getter *getter)
+{
+ return MACH_PRIORITY_TO_NICE(G (getter, int)(ps)) == 0;
+}
+
+/* Hurd processes usually have 2 threads; XXX is there someplace we get get
+ this number from? */
+int
+ps_nominal_nth (struct proc_stat *ps, const struct ps_getter *getter)
+{
+ return G (getter, int)(ps) == 2;
+}
+
+static int own_uid = -2; /* -1 means no uid at all. */
+
+/* A user is nominal if it's the current user. */
+int
+ps_nominal_user (struct proc_stat *ps, const struct ps_getter *getter)
+{
+ struct ps_user *u = G (getter, struct ps_user *)(ps);
+ if (own_uid == -2)
+ own_uid = getuid ();
+ return own_uid >= 0 && u && u->uid == own_uid;
+}
+
+/* A uid is nominal if it's that of the current user. */
+int
+ps_nominal_uid (struct proc_stat *ps, const struct ps_getter *getter)
+{
+ uid_t uid = G (getter, uid_t)(ps);
+ if (own_uid == -2)
+ own_uid = getuid ();
+ return own_uid >= 0 && uid == own_uid;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Returns the first entry called NAME in the vector of fmt_specs SPECS. If
+ the result is in fact an alias entry, returns in ALIASED_TO the name of
+ the desired source. */
+static const struct ps_fmt_spec *
+specv_find (const struct ps_fmt_spec *specs, const char *name,
+ char **aliased_to)
+{
+ while (! ps_fmt_spec_is_end (specs))
+ {
+ char *alias = index (specs->name, '=');
+ if (alias)
+ {
+ unsigned name_len = strlen (name);
+
+ if (name_len == alias - specs->name
+ && strncasecmp (name, specs->name, name_len) == 0)
+ /* SPECS is an alias, lookup what it refs to. */
+ {
+ *aliased_to = alias + 1;
+ return specs;
+ }
+ }
+ else
+ if (strcasecmp (specs->name, name) == 0)
+ return specs;
+ specs++;
+ }
+
+ return 0;
+}
+
+/* Number of specs allocated in each block of expansions. */
+#define EXP_BLOCK_SIZE 20
+
+/* A node in a linked list of spec vectors. */
+struct ps_fmt_spec_block
+{
+ struct ps_fmt_spec_block *next;
+ struct ps_fmt_spec specs[EXP_BLOCK_SIZE];
+};
+
+/* Adds a new alias expansion, using fields from ALIAS, where non-zero,
+ otherwise SRC, to SPECS. */
+struct ps_fmt_spec *
+specs_add_alias (struct ps_fmt_specs *specs,
+ const struct ps_fmt_spec *alias,
+ const struct ps_fmt_spec *src)
+{
+ struct ps_fmt_spec *exp;
+ struct ps_fmt_spec_block *block;
+ char *name_end = index (alias->name, '=');
+ size_t name_len = name_end ? name_end - alias->name : strlen (alias->name);
+
+ for (block = specs->expansions; block; block = block->next)
+ {
+ exp = block->specs;
+ while (! ps_fmt_spec_is_end (exp))
+ exp++;
+ if (exp + 1 < block->specs + EXP_BLOCK_SIZE)
+ /* Found some empty space at EXP. */
+ break;
+ }
+
+ if (! block)
+ /* Ran out of blocks, we gotta make a new one. */
+ {
+ block = malloc (sizeof (struct ps_fmt_spec_block));
+ if (! block)
+ return 0;
+ block->next = specs->expansions;
+ specs->expansions = block;
+ exp = block->specs;
+ }
+
+ /* EXP gets its name from ALIAS, but only the bit before the alias marker. */
+ exp->name = malloc (name_len + 1);
+ if (! exp->name)
+ return 0;
+ bcopy ((char *)alias->name, (char *)exp->name, name_len);
+ ((char *)exp->name)[name_len] = '\0';
+
+ /* Copy the rest of the fields from ALIAS, but defaulting to SRC. */
+ exp->title = alias->title ?: src->title;
+ exp->width = alias->width ?: src->width;
+ exp->precision = alias->precision >= 0 ? alias->precision : src->precision;
+ exp->flags = src->flags ^ alias->flags;
+ exp->getter = alias->getter ?: src->getter;
+ exp->output_fn = alias->output_fn ?: src->output_fn;
+ exp->cmp_fn = alias->cmp_fn ?: src->cmp_fn;
+ exp->nominal_fn = alias->nominal_fn ?: src->nominal_fn;
+
+ /* Now add the list-end marker. */
+ bzero (exp + 1, sizeof (*exp));
+
+ return exp;
+}
+
+const struct ps_fmt_spec *
+ps_fmt_specs_find (struct ps_fmt_specs *specs, const char *name)
+{
+ if (specs) /* Allow NULL to make recursion more handy. */
+ {
+ struct ps_fmt_spec_block *block;
+ char *aliased_to = 0;
+ const struct ps_fmt_spec *s = 0;
+
+ /* If SPECS contains any alias expansions, look there first. */
+ for (block = specs->expansions; block && !s; block = block->next)
+ s = specv_find (block->specs, name, &aliased_to);
+
+ if (! s)
+ /* Look in the local list of specs. */
+ s = specv_find (specs->specs, name, &aliased_to);
+
+ if (s)
+ {
+ if (aliased_to)
+ {
+ const struct ps_fmt_spec *src; /* What S is an alias to. */
+
+ if (strcasecmp (name, aliased_to) == 0)
+ /* An alias to the same name (useful to just change some
+ property) -- start looking up in the parent. */
+ src = ps_fmt_specs_find (specs->parent, aliased_to);
+ else
+ src = ps_fmt_specs_find (specs, aliased_to);
+
+ if (! src)
+ return 0;
+
+ s = specs_add_alias (specs, s, src);
+ }
+ }
+ else
+ /* Try again with our parent. */
+ s = ps_fmt_specs_find (specs->parent, name);
+
+ return s;
+ }
+ else
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+static const struct ps_fmt_spec specs[] =
+{
+ {"PID", 0, -5, -1, 0,
+ &ps_pid_getter, ps_emit_int, ps_cmp_ints, 0},
+ {"TH", "TH#", -2, -1, 0,
+ &ps_thread_index_getter,ps_emit_int, ps_cmp_ints, 0},
+ {"PPID", 0, -5, -1, 0,
+ &ps_ppid_getter, ps_emit_int, ps_cmp_ints, 0},
+ {"UID", 0, -4, -1, PS_FMT_FIELD_KEEP,
+ &ps_owner_uid_getter, ps_emit_uid, ps_cmp_ints, ps_nominal_uid},
+ {"User", 0, 8, -1, PS_FMT_FIELD_KEEP,
+ &ps_owner_getter, ps_emit_uname, ps_cmp_unames, ps_nominal_user},
+ {"NTh", 0, -2, -1, 0,
+ &ps_num_threads_getter, ps_emit_int, ps_cmp_ints, ps_nominal_nth},
+ {"PGrp", 0, -5, -1, 0,
+ &ps_pgrp_getter, ps_emit_int, ps_cmp_ints, 0},
+ {"Sess", 0, -5, -1, 0,
+ &ps_session_getter, ps_emit_int, ps_cmp_ints, 0},
+ {"LColl", 0, -5, -1, 0,
+ &ps_login_col_getter, ps_emit_int, ps_cmp_ints, 0},
+ {"Args", 0, 0, -1, 0,
+ &ps_args_getter, ps_emit_args, ps_cmp_strings,ps_nominal_string},
+ {"Arg0", 0, 0, -1, 0,
+ &ps_args_getter, ps_emit_string, ps_cmp_strings,ps_nominal_string},
+ {"Env", 0, 0, -1, 0,
+ &ps_env_getter, ps_emit_args, ps_cmp_strings,ps_nominal_string},
+ {"Start", 0, -7, 1, 0,
+ &ps_start_time_getter, ps_emit_past_time, ps_cmp_times,0},
+ {"Time", 0, -8, 2, 0,
+ &ps_tot_time_getter, ps_emit_seconds, ps_cmp_times, 0},
+ {"UTime", 0, -8, 2, 0,
+ &ps_usr_time_getter, ps_emit_seconds, ps_cmp_times, 0},
+ {"STime", 0, -8, 2, 0,
+ &ps_sys_time_getter, ps_emit_seconds, ps_cmp_times, 0},
+ {"VSize", 0, -5, -1, 0,
+ &ps_vsize_getter, ps_emit_nice_size_t,ps_cmp_size_ts, 0},
+ {"RSize", 0, -5, -1, 0,
+ &ps_rsize_getter, ps_emit_nice_size_t,ps_cmp_size_ts, 0},
+ {"Pri", 0, -3, -1, 0,
+ &ps_cur_priority_getter,ps_emit_priority,ps_cmp_ints, ps_nominal_pri},
+ {"BPri", 0, -3, -1, 0,
+ &ps_base_priority_getter,ps_emit_priority,ps_cmp_ints, ps_nominal_pri},
+ {"MPri", 0, -3, -1, 0,
+ &ps_max_priority_getter,ps_emit_priority,ps_cmp_ints, ps_nominal_pri},
+ {"Mem", "%Mem", -4, -1, 0,
+ &ps_rmem_frac_getter, ps_emit_percent, ps_cmp_floats, 0},
+ {"CPU", "%CPU", -4, -1, 0,
+ &ps_cpu_frac_getter, ps_emit_percent, ps_cmp_floats, 0},
+ {"State", 0, 4, -1, 0,
+ &ps_state_getter, ps_emit_state, 0, 0},
+ {"Wait", 0, 10, -1, 0,
+ &ps_wait_getter, ps_emit_wait, 0, 0},
+ {"Sleep", 0, -2, -1, 0,
+ &ps_sleep_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint},
+ {"Susp", 0, -2, -1, 0,
+ &ps_susp_count_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint},
+ {"PSusp", 0, -2, -1, 0,
+ &ps_proc_susp_count_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint},
+ {"TSusp", 0, -2, -1, 0,
+ &ps_thread_susp_count_getter, ps_emit_int,ps_cmp_ints, ps_nominal_zint},
+ {"TTY", 0, -2, -1, 0,
+ &ps_tty_getter, ps_emit_tty_name,ps_cmp_strings,0},
+ {"PgFlts", 0, -5, -1, 0,
+ &ps_page_faults_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint},
+ {"COWFlts", 0, -5, -1, 0,
+ &ps_cow_faults_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint},
+ {"PgIns", 0, -5, -1, 0,
+ &ps_pageins_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint},
+ {"MsgIn", 0, -5, -1, 0,
+ &ps_msgs_rcvd_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint},
+ {"MsgOut", 0, -5, -1, 0,
+ &ps_msgs_sent_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint},
+ {"ZFills", 0, -5, -1, 0,
+ &ps_zero_fills_getter, ps_emit_int, ps_cmp_ints, ps_nominal_zint},
+ {"Ports", 0, -5, -1, 0,
+ &ps_num_ports_getter, ps_emit_int, ps_cmp_ints, 0},
+ {0}
+};
+
+struct ps_fmt_specs ps_std_fmt_specs = { specs, 0 };
diff --git a/libps/tty.c b/libps/tty.c
new file mode 100644
index 00000000..3ab72ee8
--- /dev/null
+++ b/libps/tty.c
@@ -0,0 +1,155 @@
+/* The ps_tty type, for per-tty info.
+
+ Copyright (C) 1995,1996,2000 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <hurd/term.h>
+
+#include "ps.h"
+#include "common.h"
+
+#include "ps_term.h"
+
+/* ---------------------------------------------------------------- */
+
+/* Create a ps_tty for the tty referred to by PORT, returning it in TTY.
+ If a memory allocation error occurs, ENOMEM is returned, otherwise 0. */
+error_t
+ps_tty_create (file_t port, struct ps_tty **tty)
+{
+ *tty = NEW (struct ps_tty);
+ if (*tty == NULL)
+ return ENOMEM;
+
+ (*tty)->port = port;
+ (*tty)->name_state = PS_TTY_NAME_PENDING;
+ (*tty)->short_name = NULL;
+ (*tty)->short_name_alloced = FALSE;
+
+ return 0;
+}
+
+/* Frees TTY and any resources it consumes. */
+void
+ps_tty_free (struct ps_tty *tty)
+{
+ mach_port_deallocate(mach_task_self (), tty->port);
+ if (tty->name_state == PS_TTY_NAME_OK && tty->name != NULL)
+ free ((char *)tty->name);
+ if (tty->short_name_alloced)
+ free ((char *)tty->short_name);
+ free (tty);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Returns the name of the tty, or NULL if it can't be figured out. */
+const char *
+ps_tty_name (struct ps_tty *tty)
+{
+ if (tty->name_state == PS_TTY_NAME_PENDING)
+ {
+ string_t buf;
+
+ if (ps_term_get_nodename (tty->port, buf) != 0)
+ /* There is a terminal there, but we can't figure out its name. */
+ tty->name_state = PS_TTY_NAME_ERROR;
+ else
+ {
+ tty->name = strdup (buf);
+ tty->name_state = (tty->name ? PS_TTY_NAME_OK : PS_TTY_NAME_ERROR);
+ }
+ }
+
+ if (tty->name_state == PS_TTY_NAME_OK)
+ return tty->name;
+ else
+ return NULL;
+}
+
+/* ---------------------------------------------------------------- */
+
+struct ps_tty_abbrev
+{
+ const char *pfx;
+ const char *subst;
+};
+
+const struct ps_tty_abbrev ps_tty_abbrevs[] =
+{
+ { "/tmp/console", "oc" }, /* temp hack */
+ { "/dev/console", "co" },
+ { "/dev/tty", "" },
+ { "/dev/pty", "" },
+ { "/dev/com", "c" },
+ { "/dev/", "" },
+ { 0 }
+};
+
+/* Returns the standard abbreviated name of the tty, the whole name if there
+ is no standard abbreviation, or NULL if it can't be figured out. */
+const char *
+ps_tty_short_name (struct ps_tty *tty)
+{
+ if (tty->short_name != NULL)
+ return tty->short_name;
+ else
+ {
+ const struct ps_tty_abbrev *abbrev;
+ const char *name = ps_tty_name (tty);
+
+ if (name)
+ for (abbrev = ps_tty_abbrevs; abbrev->pfx != NULL; abbrev++)
+ {
+ const char *subst = abbrev->subst;
+ size_t pfx_len = strlen (abbrev->pfx);
+
+ if (strncmp (name, abbrev->pfx, pfx_len) == 0)
+ {
+ if (name[pfx_len] == '\0')
+ tty->short_name = abbrev->subst;
+ else if (!subst || subst[0] == '\0')
+ tty->short_name = name + pfx_len;
+ else
+ {
+ size_t slen = strlen (subst);
+ size_t nlen = strlen (name + pfx_len) + 1;
+ char *n = malloc (slen + nlen);
+ if (n)
+ {
+ memcpy (n, subst, slen);
+ memcpy (&n[slen], &name[pfx_len], nlen);
+ tty->short_name = n;
+ tty->short_name_alloced = TRUE;
+ }
+ }
+ break;
+ }
+ }
+
+ if (tty->short_name == NULL)
+ tty->short_name = name;
+
+ return tty->short_name;
+ }
+}
diff --git a/libps/user.c b/libps/user.c
new file mode 100644
index 00000000..0b87ace3
--- /dev/null
+++ b/libps/user.c
@@ -0,0 +1,163 @@
+/* The ps_user type, for per-user info.
+
+ Copyright (C) 1995,96,2001 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "ps.h"
+#include "common.h"
+
+static error_t
+install_passwd (struct ps_user *u, struct passwd *pw)
+{
+ int needed = 0;
+
+#define COUNT(field) if (pw->field != NULL) (needed += strlen(pw->field) + 1)
+ COUNT (pw_name);
+ COUNT (pw_passwd);
+ COUNT (pw_gecos);
+ COUNT (pw_dir);
+ COUNT (pw_shell);
+
+ u->storage = malloc (needed);
+ if (u->storage != NULL)
+ {
+ char *p = u->storage;
+
+ /* Copy each string field into storage allocated in the u
+ structure and point the fields at that instead of the static
+ storage that pw currently points to. */
+#define COPY(field) \
+if (pw->field != NULL) \
+strcpy(p, pw->field), (pw->field = p), (p += strlen (p) + 1)
+ COPY (pw_name);
+ COPY (pw_passwd);
+ COPY (pw_gecos);
+ COPY (pw_dir);
+ COPY (pw_shell);
+
+ u->passwd = *pw;
+
+ return 0;
+ }
+ else
+ return ENOMEM;
+}
+
+/* Create a ps_user for the user referred to by UID, returning it in U.
+ If a memory allocation error occurs, ENOMEM is returned, otherwise 0. */
+error_t
+ps_user_create (uid_t uid, struct ps_user **u)
+{
+ *u = NEW (struct ps_user);
+ if (*u == NULL)
+ return ENOMEM;
+
+ (*u)->uid = uid;
+ (*u)->passwd_state = PS_USER_PASSWD_PENDING;
+
+ return 0;
+}
+
+/* Create a ps_user for the user referred to by UNAME, returning it in U.
+ If a memory allocation error occurs, ENOMEM is returned. If no such user
+ is known, EINVAL is returned. */
+error_t
+ps_user_uname_create (char *uname, struct ps_user **u)
+{
+ struct passwd *pw = getpwnam (uname);
+ if (pw)
+ return ps_user_passwd_create (pw, u);
+ else
+ return EINVAL;
+}
+
+/* Makes makes a ps_user containing PW (which is copied). */
+error_t
+ps_user_passwd_create (struct passwd *pw, struct ps_user **u)
+{
+ error_t err = 0;
+
+ *u = NEW (struct ps_user);
+ if (*u == NULL)
+ err = ENOMEM;
+ else
+ {
+ err = install_passwd (*u, pw);
+ if (err)
+ FREE (*u);
+ else
+ {
+ (*u)->passwd_state = PS_USER_PASSWD_OK;
+ (*u)->uid = pw->pw_uid;
+ }
+ }
+
+ return err;
+}
+
+/* Free U and any resources it consumes. */
+void
+ps_user_free (struct ps_user *u)
+{
+ if (u->passwd_state == PS_USER_PASSWD_OK)
+ free (u->storage);
+ free (u);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Returns the password file entry (struct passwd, from <pwd.h>) for the user
+ referred to by U, or NULL if it can't be gotten. */
+struct passwd *ps_user_passwd (struct ps_user *u)
+{
+ if (u->passwd_state == PS_USER_PASSWD_OK)
+ return &u->passwd;
+ else if (u->passwd_state == PS_USER_PASSWD_ERROR)
+ return NULL;
+ else
+ {
+ struct passwd *pw = getpwuid (u->uid);
+ if (pw != NULL && install_passwd (u, pw) == 0)
+ {
+ u->passwd_state = PS_USER_PASSWD_OK;
+ return &u->passwd;
+ }
+ else
+ {
+ u->passwd_state = PS_USER_PASSWD_ERROR;
+ return NULL;
+ }
+ }
+}
+
+/* Returns the user name for the user referred to by U, or NULL if it can't
+ be gotten. */
+char *ps_user_name (struct ps_user *u)
+{
+ struct passwd *pw = ps_user_passwd (u);
+ if (pw)
+ return pw->pw_name;
+ else
+ return NULL;
+}
diff --git a/libps/write.c b/libps/write.c
new file mode 100644
index 00000000..1f7f52cc
--- /dev/null
+++ b/libps/write.c
@@ -0,0 +1,264 @@
+/* Ps stream output
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "ps.h"
+#include "common.h"
+
+/* True if CH is a `control character'. */
+#define iscntl(ch) ((unsigned)(ch) < 32)
+
+/* *BEG and NEW - 1 are the bounds of a buffer, which write to S (the last
+ character just before NEW isn't included, because something different
+ about it is what caused the flush), and update *BEG to be NEW. True is
+ returned if a write error occurs. */
+static int
+flush (const char **beg, const char *new, FILE *s)
+{
+ const char *b = *beg;
+ if (new > b)
+ *beg = new;
+ if (new - 1 > b)
+ {
+ size_t len = new - 1 - b;
+ int ret = fwrite (b, 1, len, s);
+ if (ret < len)
+ return 1;
+ }
+ return 0;
+}
+
+/* Write T to S, up to MAX characters (unless MAX == 0), making sure not to
+ write any unprintable characters. */
+error_t
+noise_write (const char *t, ssize_t max, FILE *s)
+{
+ int ch;
+ const char *ok = t;
+ size_t len = 0;
+
+ while ((ch = *t++) && (max < 0 || len < max))
+ if (isgraph (ch) || ch == ' ')
+ len++;
+ else
+ {
+ int is_cntl = iscntl (ch);
+
+ if (flush (&ok, t, s))
+ return errno;
+
+ len += (is_cntl ? 2 : 4);
+ if (max >= 0 && len > max)
+ break;
+
+ if (is_cntl)
+ fprintf (s, "^%c", ch + 'A');
+ else
+ fprintf (s, "\\%03o", ch);
+ }
+
+ if (flush (&ok, t, s))
+ return errno;
+
+ return 0;
+}
+
+/* Return what noise_write would write with arguments of T and MAX. */
+size_t
+noise_len (const char *t, ssize_t max)
+{
+ int ch;
+ size_t len = 0;
+
+ while ((ch = *t++) && (max == 0 || len < max))
+ if (isgraph (ch) || ch == ' ')
+ len++;
+ else
+ {
+ size_t rep_len = iscntl (ch) ? 2 : 4;
+ if (max >= 0 && rep_len + len > max)
+ break;
+ len += rep_len;
+ }
+
+ return len;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Write at most MAX_LEN characters of STRING to STREAM (if MAX_LEN > the
+ length of STRING, then write all of it; if MAX_LEN == -1, then write all
+ of STRING regardless). */
+error_t
+ps_stream_write (struct ps_stream *stream, const char *string, ssize_t max_len)
+{
+ size_t len = noise_len (string, max_len);
+
+ if (len > 0)
+ {
+ error_t err;
+ ssize_t spaces_needed = stream->spaces;
+
+ stream->spaces = 0;
+ while (spaces_needed > 0)
+ {
+ static char spaces[] = " ";
+#define spaces_len (sizeof(spaces) - 1)
+ size_t chunk = spaces_needed > spaces_len ? spaces_len : spaces_needed;
+ error_t err =
+ ps_stream_write (stream, spaces + spaces_len - chunk, chunk);
+ if (err)
+ return err;
+ spaces_needed -= chunk;
+ }
+ stream->spaces = spaces_needed;
+
+ err = noise_write (string, len, stream->stream);
+ if (err)
+ return err;
+
+ stream->pos += len;
+ }
+
+ return 0;
+}
+
+/* Write NUM spaces to STREAM. NUM may be negative, in which case the same
+ number of adjacent spaces (written by other calls to ps_stream_space) are
+ consumed if possible. If an error occurs, the error code is returned,
+ otherwise 0. */
+error_t
+ps_stream_space (struct ps_stream *stream, ssize_t num)
+{
+ stream->spaces += num;
+ return 0;
+}
+
+/* Write as many spaces to STREAM as required to make a field of width SOFAR
+ be at least WIDTH characters wide (the absolute value of WIDTH is used).
+ If an error occurs, the error code is returned, otherwise 0. */
+error_t
+ps_stream_pad (struct ps_stream *stream, ssize_t sofar, ssize_t width)
+{
+ return ps_stream_space (stream, ABS (width) - sofar);
+}
+
+/* Write a newline to STREAM, resetting its position to zero. */
+error_t
+ps_stream_newline (struct ps_stream *stream)
+{
+ putc ('\n', stream->stream);
+ stream->spaces = 0;
+ stream->pos = 0;
+ return 0;
+}
+
+/* Write the string BUF to STREAM, padded on one side with spaces to be at
+ least the absolute value of WIDTH long: if WIDTH >= 0, then on the left
+ side, otherwise on the right side. If an error occurs, the error code is
+ returned, otherwise 0. */
+error_t
+_ps_stream_write_field (struct ps_stream *stream,
+ const char *buf, size_t max_width,
+ int width)
+{
+ error_t err;
+ size_t len;
+
+ while (isspace (*buf))
+ buf++;
+
+ if (stream->spaces < 0 && max_width >= 0)
+ /* Take some of our spacing deficit out of a truncatable field. */
+ max_width += stream->spaces;
+
+ len = noise_len (buf, max_width);
+
+ if (width > 0)
+ {
+ err = ps_stream_write (stream, buf, len);
+ if (!err)
+ err = ps_stream_space (stream, width - len);
+ }
+ else if (width < 0)
+ {
+ err = ps_stream_space (stream, -width - len);
+ if (!err)
+ err = ps_stream_write (stream, buf, len);
+ }
+ else
+ err = ps_stream_write (stream, buf, len);
+
+ return err;
+}
+
+/* Write the string BUF to STREAM, padded on one side with spaces to be at
+ least the absolute value of WIDTH long: if WIDTH >= 0, then on the left
+ side, otherwise on the right side. If an error occurs, the error code is
+ returned, otherwise 0. */
+error_t
+ps_stream_write_field (struct ps_stream *stream, const char *buf, int width)
+{
+ return _ps_stream_write_field (stream, buf, -1, width);
+}
+
+/* Like ps_stream_write_field, but truncates BUF to make it fit into WIDTH. */
+error_t
+ps_stream_write_trunc_field (struct ps_stream *stream,
+ const char *buf, int width)
+{
+ return _ps_stream_write_field (stream, buf, width ? ABS (width) : -1, width);
+}
+
+/* Write the decimal representation of VALUE to STREAM, padded on one side
+ with spaces to be at least the absolute value of WIDTH long: if WIDTH >=
+ 0, then on the left side, otherwise on the right side. If an error
+ occurs, the error code is returned, otherwise 0. */
+error_t
+ps_stream_write_int_field (struct ps_stream *stream, int value, int width)
+{
+ char buf[20];
+ sprintf (buf, "%d", value);
+ return ps_stream_write_field (stream, buf, width);
+}
+
+/* Create a stream outputing to DEST, and return it in STREAM, or an error. */
+error_t
+ps_stream_create (FILE *dest, struct ps_stream **stream)
+{
+ *stream = malloc (sizeof (struct ps_stream));
+ if (! *stream)
+ return ENOMEM;
+ (*stream)->stream = dest;
+ (*stream)->spaces = 0;
+ (*stream)->pos = 0;
+ return 0;
+}
+
+/* Frees STREAM. The destination file is *not* closed. */
+void
+ps_stream_free (struct ps_stream *stream)
+{
+ free (stream);
+}
diff --git a/libshouldbeinlibc/Makefile b/libshouldbeinlibc/Makefile
new file mode 100644
index 00000000..14a7939d
--- /dev/null
+++ b/libshouldbeinlibc/Makefile
@@ -0,0 +1,37 @@
+# Makefile for libshouldbeinlibc
+#
+# Copyright (C) 1995,96,97,98,99,2002,2012 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libshouldbeinlibc
+makemode := library
+
+libname = libshouldbeinlibc
+SRCS = termsize.c timefmt.c exec-reauth.c maptime-funcs.c \
+ canon-host.c maptime.c shared-dom.c localhost.c wire.c portinfo.c \
+ xportinfo.c portxlate.c lcm.c cacheq.c fsysops.c \
+ idvec.c idvec-auth.c idvec-funcs.c \
+ idvec-impgids.c idvec-verify.c idvec-rep.c \
+ ugids.c ugids-argp.c ugids-rep.c ugids-verify.c ugids-subtract.c \
+ ugids-auth.c ugids-xinl.c ugids-merge.c ugids-imply.c ugids-posix.c \
+ ugids-verify-auth.c nullauth.c
+installhdrs = idvec.h timefmt.h maptime.h \
+ wire.h portinfo.h portxlate.h cacheq.h ugids.h nullauth.h
+installhdrsubdir = .
+
+OBJS = $(SRCS:.c=.o)
+
+include ../Makeconf
diff --git a/libshouldbeinlibc/cacheq.c b/libshouldbeinlibc/cacheq.c
new file mode 100644
index 00000000..c1be59c0
--- /dev/null
+++ b/libshouldbeinlibc/cacheq.c
@@ -0,0 +1,143 @@
+/* Helper functions for maintaining a fixed-size lru-ordered queue
+
+ Copyright (C) 1996, 1998 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "cacheq.h"
+
+/* Move ENTRY to the most-recently-used end of CACHEQ. */
+void
+cacheq_make_mru (struct cacheq *cq, void *entry)
+{
+ struct cacheq_hdr *h = entry;
+
+ if (h != cq->mru)
+ {
+ /* First remove it. We known H->prev isn't 0 because H wasn't
+ previously == MRU. */
+ ((struct cacheq_hdr *)h->prev)->next = h->next;
+ if (h->next)
+ ((struct cacheq_hdr *)h->next)->prev = h->prev;
+ else
+ cq->lru = h->prev;
+
+ /* Now make it MRU. */
+ h->next = cq->mru;
+ h->prev = 0;
+ ((struct cacheq_hdr *)cq->mru)->prev = h;
+ cq->mru = h;
+ }
+}
+
+/* Move ENTRY to the least-recently-used end of CACHEQ. */
+void
+cacheq_make_lru (struct cacheq *cq, void *entry)
+{
+ struct cacheq_hdr *h = entry;
+
+ if (h != cq->lru)
+ {
+ /* First remove it. We known H->next isn't 0 because H wasn't
+ previously == LRU. */
+ ((struct cacheq_hdr *)h->next)->prev = h->prev;
+ if (h->prev)
+ ((struct cacheq_hdr *)h->prev)->next = h->next;
+ else
+ cq->mru = h->next;
+
+ /* Now make it LRU. */
+ h->prev = cq->lru;
+ h->next = 0;
+ ((struct cacheq_hdr *)cq->lru)->next = h;
+ cq->lru = h;
+ }
+}
+
+/* Change CQ's size to be LENGTH entries. */
+error_t
+cacheq_set_length (struct cacheq *cq, int length)
+{
+ if (length != cq->length)
+ {
+ size_t esz = cq->entry_size;
+ void *new_entries = malloc (esz * length);
+ /* Source entries. */
+ struct cacheq_hdr *fh = cq->mru;
+ /* Destination entries (and limit). */
+ struct cacheq_hdr *th = new_entries;
+ struct cacheq_hdr *end = new_entries + esz * (length - 1);
+ struct cacheq_hdr *prev_th = 0;
+
+ if (! new_entries)
+ return ENOMEM;
+
+ while (fh || th)
+ {
+ struct cacheq_hdr *next_th =
+ (!th || th >= end) ? 0 : (void *)th + esz;
+
+ if (fh && th)
+ bcopy (fh, th, esz); /* Copy the bits in a moved entry. */
+ else if (th)
+ bzero (th, esz); /* Zero the bits in a new entry. */
+
+ if (th)
+ /* Fixup headers. */
+ {
+ th->prev = prev_th;
+ th->next = next_th;
+ }
+
+ /* Call user hooks as appropriate. */
+ if (fh && th)
+ {
+ if (cq->move_entry)
+ (*cq->move_entry) (fh, th);
+ }
+ else if (th)
+ {
+ if (cq->init_entry)
+ (*cq->init_entry) (th);
+ }
+ else
+ {
+ if (cq->finalize_entry)
+ (*cq->finalize_entry) (fh);
+ }
+
+ if (fh)
+ fh = fh->next;
+ if (th)
+ {
+ prev_th = th;
+ th = next_th;
+ }
+ }
+
+ free (cq->entries);
+ cq->entries = new_entries;
+ cq->mru = new_entries;
+ cq->lru = prev_th;
+ cq->length = length;
+ }
+
+ return 0;
+}
diff --git a/libshouldbeinlibc/cacheq.h b/libshouldbeinlibc/cacheq.h
new file mode 100644
index 00000000..a221a7a7
--- /dev/null
+++ b/libshouldbeinlibc/cacheq.h
@@ -0,0 +1,87 @@
+/* Helper functions for maintaining a fixed-size lru-ordered queue
+
+ Copyright (C) 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __CACHEQ_H__
+#define __CACHEQ_H__
+
+#include <stddef.h>
+#include <errno.h>
+
+/* This header occurs at the start of every cacheq entry. */
+struct cacheq_hdr
+{
+ /* Next and prev entries in the cache, linked in LRU order. These are of
+ type `void *' so that it's conveient to iterate through the list using a
+ variable pointing to a structure that contains the header, by using
+ something like `VAR = VAR->hdr.next'. */
+ void *next, *prev;
+};
+
+/* A cacheq. Note that this structure is laid out to allow convenient use as
+ static initialized data. */
+struct cacheq
+{
+ /* The size of each entry, including its cacheq_hdr. */
+ size_t entry_size;
+
+ /* If non-0, then when making new entries (for instance, when the cacheq is
+ initialized, or when its size is increased), this function is called on
+ each new entry (with it's header already initialized). If this function
+ isn't defined, then each entry is simply zeroed. */
+ void (*init_entry) (void *entry);
+
+ /* When an entry is moved from one place in memory to another (for
+ instance, changing the size of the cache, new storage is used), this is
+ called for each entry, with FROM and TO the old and new locations of the
+ entry (and TO contains a bitwise copy of FROM). This is often useful
+ when the entry points to something that contains a backpointer to it. */
+ void (*move_entry) (void *from, void *to);
+
+ /* When entries are removed for some reason (for instance, when reducing
+ the size of the cacheq), this function is called on each. */
+ void (*finalize_entry) (void *entry);
+
+ /* The number of entries in the cache. This number is fixed. */
+ int length;
+
+ /* A buffer holding malloc'd memory for all the entries -- NUM_ENTRIES
+ entries of size ENTRY_SIZE. */
+ void *entries;
+
+ /* The least, and most, recently used entries in the cache. These point to
+ either end of a linked list composed of all the elements of the cache.
+ This list will always be the same length -- if an element is `removed',
+ its entry is simply marked inactive, and moved to the LRU end of the list
+ so it will be reused first. These pointers are of type `void *' so they
+ can be conveniently used by client code (see comment in struct
+ cacheq_hdr). */
+ void *lru, *mru;
+};
+
+/* Move ENTRY to the most-recently-used end of CACHEQ. */
+void cacheq_make_mru (struct cacheq *cq, void *entry);
+
+/* Move ENTRY to the least-recently-used end of CACHEQ. */
+void cacheq_make_lru (struct cacheq *cq, void *entry);
+
+/* Change CQ's size to be LENGTH entries. */
+error_t cacheq_set_length (struct cacheq *cq, int length);
+
+#endif /* __CACHEQ_H__ */
diff --git a/libshouldbeinlibc/canon-host.c b/libshouldbeinlibc/canon-host.c
new file mode 100644
index 00000000..41068d30
--- /dev/null
+++ b/libshouldbeinlibc/canon-host.c
@@ -0,0 +1,57 @@
+/* Host name canonicalization
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ [This file is from sh-utils/lib; maybe something can be done to share them.]
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/* Returns the canonical hostname associated with HOST (allocated in a static
+ buffer), or 0 if it can't be determined. */
+char *
+canon_host (char *host)
+{
+ struct hostent *he = gethostbyname (host);
+
+ if (he)
+ {
+ char *addr = 0;
+
+ /* Try and get an ascii version of the numeric host address. */
+ switch (he->h_addrtype)
+ {
+ case AF_INET:
+ addr = inet_ntoa (*(struct in_addr *)he->h_addr);
+ break;
+ }
+
+ if (addr && strcmp (he->h_name, addr) == 0)
+ /* gethostbyname() cheated! Lookup the host name via the address
+ this time to get the actual host name. */
+ he = gethostbyaddr (he->h_addr, he->h_length, he->h_addrtype);
+
+ if (he)
+ return he->h_name;
+ }
+
+ return 0;
+}
diff --git a/libshouldbeinlibc/exec-reauth.c b/libshouldbeinlibc/exec-reauth.c
new file mode 100644
index 00000000..263b1408
--- /dev/null
+++ b/libshouldbeinlibc/exec-reauth.c
@@ -0,0 +1,108 @@
+/* Re-authentication in preparation for an exec
+
+ Copyright (C) 1995, 96, 98, 2000 Free Software Foundation, Inc.
+
+ Stolen by Miles Bader <miles@gnu.ai.mit.edu>, but really
+ written by Michael I. Bushnell p/BSG <mib@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <mach.h>
+#include <hurd/auth.h>
+#include <hurd/io.h>
+#include <hurd/process.h>
+
+/* Re-authenticates the ports in PORTS and FDS appropriately (replacing
+ PORTS[INIT_PORT_AUTH] with AUTH) for a following exec using the auth port
+ AUTH. Each replaced port has a reference consumed; if an error is
+ returned, then PORTS and FDS may contain a mixture of old and new ports,
+ however AUTH will only be placed in PORTS upon success. If SECURE is
+ true, then it is assumed the exec will use EXEC_SECURE, and certain ports
+ may be replaced by MACH_PORT_NULL, with the expectation that exec will
+ fill these in itself; if all ports should be re-authenticated, use 0 for
+ this argument, regardless of whether EXEC_SECURE will be used. If
+ MUST_REAUTH is true, then any failure to re-authenticate a port will
+ result in the function return the error, otherwise, such failures are
+ silently ignored. */
+error_t
+exec_reauth (auth_t auth, int secure, int must_reauth,
+ mach_port_t *ports, unsigned num_ports,
+ mach_port_t *fds, unsigned num_fds)
+{
+ unsigned int i;
+ error_t err = 0;
+
+ error_t reauth (mach_port_t *port, int isproc)
+ {
+ if (*port != MACH_PORT_NULL)
+ {
+ mach_port_t newport;
+ mach_port_t ref = mach_reply_port ();
+ error_t err =
+ (isproc ? proc_reauthenticate : io_reauthenticate)
+ (*port, ref, MACH_MSG_TYPE_MAKE_SEND);
+
+ /* MAKE_SEND is safe here because we destroy REF ourselves. */
+
+ if (!err)
+ err = auth_user_authenticate (auth, ref, MACH_MSG_TYPE_MAKE_SEND,
+ &newport);
+ mach_port_mod_refs (mach_task_self (), ref, MACH_PORT_RIGHT_RECEIVE, -1);
+ if (err)
+ {
+ if (must_reauth)
+ return err;
+ /* Nothing Happens. */
+ }
+ else
+ {
+ if (isproc)
+ mach_port_deallocate (mach_task_self (), newport);
+ else
+ {
+ mach_port_deallocate (mach_task_self (), *port);
+ *port = newport;
+ }
+ }
+ }
+ return 0;
+ }
+
+ /* Re-authenticate all the ports we are handing to the user
+ with this new port, and install the new auth port in ports. */
+ for (i = 0; i < num_fds && !err; ++i)
+ err = reauth (&fds[i], 0);
+
+ if (!err)
+ {
+ if (secure)
+ /* Not worth doing; the exec server will just do it again. */
+ ports[INIT_PORT_CRDIR] = MACH_PORT_NULL;
+ else
+ err = reauth (&ports[INIT_PORT_CRDIR], 0);
+ }
+ if (!err)
+ err = reauth (&ports[INIT_PORT_PROC], 1);
+ if (!err)
+ err = reauth (&ports[INIT_PORT_CWDIR], 0);
+
+ if (!err)
+ {
+ mach_port_deallocate (mach_task_self (), ports[INIT_PORT_AUTH]);
+ ports[INIT_PORT_AUTH] = auth;
+ }
+
+ return 0;
+}
diff --git a/libshouldbeinlibc/fsysops.c b/libshouldbeinlibc/fsysops.c
new file mode 100644
index 00000000..dbcae672
--- /dev/null
+++ b/libshouldbeinlibc/fsysops.c
@@ -0,0 +1,96 @@
+/* Some handy utility routines for fsys control ports
+
+ Copyright (C) 1996, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <argz.h>
+#include <mach.h>
+#include <sys/mman.h>
+#include <hurd/fsys.h>
+#include <string.h>
+
+/* Make FSYS readonly or writable. */
+error_t
+fsys_set_readonly (fsys_t fsys, int readonly)
+{
+ error_t err;
+ char *opts = readonly ? "--readonly" : "--writable";
+ size_t opts_len = strlen (opts) + 1;
+ err = fsys_set_options (fsys, opts, opts_len, 0);
+ if (err == EINVAL)
+ err = EOPNOTSUPP;
+ return err;
+}
+
+/* Ask FSYS whether it's readonly, returning the result in READONLY; we don't
+ really have a good method for this, other than asking for it's options and
+ looking for `--readonly' or `--writable'. If we see neither, return
+ EOPNOTSUPP. */
+error_t
+fsys_get_readonly (fsys_t fsys, int *readonly)
+{
+ error_t err;
+ char _opts[200], *opts = _opts;
+ size_t opts_len = sizeof opts;
+
+ err = fsys_get_options (fsys, &opts, &opts_len);
+ if (! err)
+ {
+ char *opt;
+ int ok = 0;
+
+ for (opt = opts
+ ; !ok && opt && opt < opts + opts_len
+ ; opt = argz_next (opts, opts_len, opt))
+ if (strcasecmp (opt, "--readonly") == 0)
+ {
+ *readonly = 1;
+ ok = 1;
+ }
+ else if (strcasecmp (opt, "--writable") == 0)
+ {
+ *readonly = 0;
+ ok = 1;
+ }
+
+ if (! ok)
+ err = EOPNOTSUPP; /* So far as we know... */
+
+ if (opts != _opts)
+ /* Free out-of-line memory returned by fsys_get_options. */
+ munmap (opts, opts_len);
+ }
+
+ return err;
+}
+
+/* Tell FSYS to remount itself. */
+error_t
+fsys_update (fsys_t fsys, int readonly)
+{
+ error_t err;
+ char *opts = "--update";
+ size_t opts_len = strlen (opts) + 1;
+ err = fsys_set_options (fsys, opts, opts_len, 0);
+ if (err == EINVAL)
+ err = EOPNOTSUPP;
+ return err;
+}
diff --git a/libshouldbeinlibc/idvec-auth.c b/libshouldbeinlibc/idvec-auth.c
new file mode 100644
index 00000000..1858bd6a
--- /dev/null
+++ b/libshouldbeinlibc/idvec-auth.c
@@ -0,0 +1,85 @@
+/* Idvec functions that interact with an auth server
+
+ Copyright (C) 1995, 1998, 1999, 2001, 2002, 2008
+ Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <mach.h>
+#include <sys/mman.h>
+#include <hurd/auth.h>
+#include <errno.h>
+
+#include "idvec.h"
+
+/* Add to all of EFF_UIDS, AVAIL_UIDS, EFF_GIDS, AVAIL_GIDS (as if with
+ idvec_merge_ids()) the ids associated with the auth port AUTH. Any of
+ these parameters may be NULL if that information isn't desired. */
+error_t
+idvec_merge_auth (struct idvec *eff_uids, struct idvec *avail_uids,
+ struct idvec *eff_gids, struct idvec *avail_gids,
+ auth_t auth)
+{
+ error_t err;
+ uid_t eff_uid_buf[10], avail_uid_buf[20];
+ uid_t *_eff_uids = eff_uid_buf, *_avail_uids = avail_uid_buf;
+ size_t num_eff_uids = 10, num_avail_uids = 20;
+ uid_t eff_gid_buf[10], avail_gid_buf[20];
+ uid_t *_eff_gids = eff_gid_buf, *_avail_gids = avail_gid_buf;
+ size_t num_eff_gids = 10, num_avail_gids = 20;
+
+ err = auth_getids (auth,
+ &_eff_uids, &num_eff_uids, &_avail_uids, &num_avail_uids,
+ &_eff_gids, &num_eff_gids, &_avail_gids, &num_avail_gids);
+ if (err)
+ return err;
+
+ if (eff_uids)
+ err = idvec_grow (eff_uids, num_eff_uids);
+ if (avail_uids && !err)
+ err = idvec_grow (avail_uids, num_avail_uids);
+ if (eff_gids && !err)
+ err = idvec_grow (eff_gids, num_eff_gids);
+ if (avail_gids && !err)
+ err = idvec_grow (avail_gids, num_avail_gids);
+
+ if (!err)
+ /* Now that we've ensured there's enough space, none of these should
+ return an error. */
+ {
+ if (eff_uids)
+ idvec_merge_ids (eff_uids, _eff_uids, num_eff_uids);
+ if (avail_uids)
+ idvec_merge_ids (avail_uids, _avail_uids, num_avail_uids);
+ if (eff_gids)
+ idvec_merge_ids (eff_gids, _eff_gids, num_eff_gids);
+ if (avail_gids)
+ idvec_merge_ids (avail_gids, _avail_gids, num_avail_gids);
+ }
+
+ /* Deallocate any out-of-line memory we got back. */
+ if (_eff_uids != eff_uid_buf)
+ munmap ((caddr_t) _eff_uids, num_eff_uids * sizeof (uid_t));
+ if (_avail_uids != avail_uid_buf)
+ munmap ((caddr_t) _avail_uids, num_avail_uids * sizeof (uid_t));
+ if (_eff_gids != eff_gid_buf)
+ munmap ((caddr_t) _eff_gids, num_eff_gids * sizeof (gid_t));
+ if (_avail_gids != avail_gid_buf)
+ munmap ((caddr_t) _avail_gids, num_avail_gids * sizeof (gid_t));
+
+ return err;
+}
diff --git a/libshouldbeinlibc/idvec-funcs.c b/libshouldbeinlibc/idvec-funcs.c
new file mode 100644
index 00000000..3bb0318d
--- /dev/null
+++ b/libshouldbeinlibc/idvec-funcs.c
@@ -0,0 +1,2 @@
+#define IDVEC_DEFINE_EI
+#include "idvec.h"
diff --git a/libshouldbeinlibc/idvec-impgids.c b/libshouldbeinlibc/idvec-impgids.c
new file mode 100644
index 00000000..d89f4873
--- /dev/null
+++ b/libshouldbeinlibc/idvec-impgids.c
@@ -0,0 +1,115 @@
+/* Add gids implied by a user
+
+ Copyright (C) 1997, 2001, 2014 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <idvec.h>
+#include <pwd.h>
+#include <grp.h>
+
+#define NUM_STATIC_GIDS 100 /* Initial size of static gid array. */
+
+/* The set of gids implied by a uid. */
+struct uid_implies
+{
+ uid_t uid; /* this uid... */
+ struct idvec *implies; /* implies these gids. */
+ struct uid_implies *next;
+};
+
+/* Cache of previously calculated results for add_implied_gids. */
+static struct uid_implies *uid_implies_cache = 0;
+
+/* Add to IMPLIED_GIDS those group ids implied by the user UID. */
+static error_t
+_merge_implied_gids (struct idvec *implied_gids, uid_t uid)
+{
+ struct uid_implies *ui;
+
+ for (ui = uid_implies_cache; ui; ui = ui->next)
+ if (ui->uid == uid)
+ return idvec_merge (implied_gids, ui->implies);
+
+ {
+ error_t err = 0;
+ struct passwd *pw = getpwuid (uid);
+
+ if (! pw)
+ err = EINVAL;
+ else
+ {
+ struct idvec *cache = make_idvec ();
+ gid_t _gids[NUM_STATIC_GIDS], *gids = _gids;
+ int maxgids = NUM_STATIC_GIDS;
+ int ngids = getgrouplist (pw->pw_name, pw->pw_gid, gids, &maxgids);
+
+ if (ngids == -1)
+ {
+ gids = malloc (maxgids * sizeof (gid_t));
+ if (! gids)
+ err = ENOMEM;
+ else
+ ngids = getgrouplist (pw->pw_name, pw->pw_gid, gids, &maxgids);
+ }
+
+ if (! cache)
+ err = ENOMEM;
+
+ if (! err)
+ {
+ err = idvec_merge_ids (cache, gids, ngids);
+ if (gids != _gids)
+ free (gids);
+ }
+
+ if (! err)
+ {
+ idvec_merge (implied_gids, cache);
+ ui = malloc (sizeof (struct uid_implies));
+ if (ui)
+ {
+ ui->uid = uid;
+ ui->implies = cache;
+ ui->next = uid_implies_cache;
+ uid_implies_cache = ui;
+ }
+ else
+ idvec_free (cache);
+ }
+ }
+
+ return err;
+ }
+}
+
+/* Add to GIDS those group ids implied by the users in UIDS. */
+error_t
+idvec_merge_implied_gids (struct idvec *gids, const struct idvec *uids)
+{
+ unsigned int i;
+ error_t err = 0;
+ for (i = 0; i < uids->num; i++)
+ {
+ error_t this_err = _merge_implied_gids (gids, uids->ids[i]);
+ if (this_err && !err)
+ err = this_err;
+ }
+ return err;
+}
diff --git a/libshouldbeinlibc/idvec-rep.c b/libshouldbeinlibc/idvec-rep.c
new file mode 100644
index 00000000..16408a4d
--- /dev/null
+++ b/libshouldbeinlibc/idvec-rep.c
@@ -0,0 +1,164 @@
+/* idvec string representation
+
+ Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <idvec.h>
+#include <grp.h>
+#include <pwd.h>
+
+/* Return a string representation of the ids in IDVEC, each id separated by
+ the string SEP (default ","). SHOW_VALUES and SHOW_NAMES reflect how each
+ id is printed (if SHOW_NAMES is true values are used where names aren't
+ available); if both are true, the `VALUE(NAME)' format is used.
+ ID_NAME_FN is used to map each id to a name; it should return a malloced
+ string, which will be freed here. The empty string is returned for an
+ empty list, and 0 for an allocation error. */
+char *
+idvec_rep (const struct idvec *idvec, int show_values, int show_names,
+ char *(*id_name_fn) (uid_t id), const char *sep)
+{
+ size_t sep_len;
+ char *rep = 0;
+ size_t rep_len = 0, rep_sz = 0;
+
+ int ensure_room (size_t amount)
+ {
+ size_t end = rep_len + amount;
+ if (end > rep_sz)
+ {
+ size_t new_sz = rep_sz + end;
+ char *new_rep = realloc (rep, new_sz);
+ if (new_rep)
+ {
+ rep = new_rep;
+ rep_sz = new_sz;
+ }
+ else
+ return 0;
+ }
+ return 1;
+ }
+ int add_id (uid_t val, char *name)
+ {
+ if (!name || show_values)
+ {
+ if (! ensure_room (10))
+ return 0;
+ rep_len += snprintf (rep + rep_len, 10, "%d", val);
+ }
+ if (name)
+ {
+ size_t nlen = strlen (name) + 3;
+ if (! ensure_room (nlen))
+ {
+ free (name);
+ return 0;
+ }
+ rep_len +=
+ snprintf (rep + rep_len, nlen, show_values ? "(%s)" : "%s", name);
+ free (name);
+ }
+ return 1;
+ }
+
+ if (! sep)
+ sep = ",";
+ sep_len = strlen (sep);
+
+ if (idvec->num > 0)
+ {
+ unsigned int i;
+
+ for (i = 0; i < idvec->num; i++)
+ {
+ char *name = 0;
+ uid_t val = idvec->ids[i];
+
+ if (i > 0)
+ {
+ if (ensure_room (sep_len))
+ {
+ strcpy (rep + rep_len, sep);
+ rep_len += sep_len;
+ }
+ else
+ break;
+ }
+
+ if (show_names || !show_values)
+ name = (*id_name_fn) (val);
+ if (! add_id (val, name))
+ break;
+ }
+
+ if (i < idvec->num)
+ {
+ free (rep);
+ return 0;
+ }
+
+ return rep;
+ }
+
+ return strdup ("");
+}
+
+/* Return a malloced string with the name of the user UID. */
+static char *
+lookup_uid (uid_t uid)
+{
+ char buf[1024];
+ struct passwd _pw, *pw;
+ if (getpwuid_r (uid, &_pw, buf, sizeof buf, &pw) == 0)
+ return strdup (pw->pw_name);
+ else
+ return 0;
+}
+
+/* Return a malloced string with the name of the group GID. */
+static char *
+lookup_gid (gid_t gid)
+{
+ char buf[1024];
+ struct group _gr, *gr;
+ if (getgrgid_r (gid, &_gr, buf, sizeof buf, &gr) == 0)
+ return strdup (gr->gr_name);
+ else
+ return 0;
+}
+
+/* Like idvec_rep, mapping ids to user names. */
+char *
+idvec_uids_rep (const struct idvec *idvec, int show_values, int show_names,
+ const char *sep)
+{
+ return idvec_rep (idvec, show_values, show_names, lookup_uid, sep);
+}
+
+/* Like idvec_rep, mapping ids to group names. */
+char *
+idvec_gids_rep (const struct idvec *idvec, int show_values, int show_names,
+ const char *sep)
+{
+ return idvec_rep (idvec, show_values, show_names, lookup_gid, sep);
+}
diff --git a/libshouldbeinlibc/idvec-verify.c b/libshouldbeinlibc/idvec-verify.c
new file mode 100644
index 00000000..4d9b6dbe
--- /dev/null
+++ b/libshouldbeinlibc/idvec-verify.c
@@ -0,0 +1,362 @@
+/* Verify user passwords
+
+ Copyright (C) 1996, 1997, 1998, 1999, 2002, 2008
+ Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <idvec.h>
+#include <grp.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <crypt.h>
+
+#define SHADOW_PASSWORD_STRING "x" /* pw_passwd contents for shadow passwd */
+
+#pragma weak crypt
+
+static error_t verify_id (); /* FWD */
+
+/* Get a password from the user, returning it in malloced storage. */
+static char *
+get_passwd (const char *prompt,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook)
+{
+ char *st = getpass (prompt);
+ if (st)
+ st = strdup (st);
+ return st;
+}
+
+/* Verify PASSWORD using /etc/passwd (and maybe /etc/shadow). */
+static error_t
+verify_passwd (const char *password,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook)
+{
+ const char *encrypted;
+ int wheel_uid = (intptr_t)hook;
+ const char *sys_encrypted;
+
+ if (! pwd_or_grp)
+ /* No password db entry for ID; if ID is root, the system is probably
+ really fucked up, so grant it (heh). */
+ return (id == 0 ? 0 : EACCES);
+
+ /* The encrypted password in the passwd db. */
+ sys_encrypted =
+ (is_group
+ ? ((struct passwd *)pwd_or_grp)->pw_passwd
+ : ((struct group *)pwd_or_grp)->gr_passwd);
+
+ if (sys_encrypted[0] == '\0')
+ return 0; /* No password. */
+
+ if (crypt)
+ /* Encrypt the password entered by the user (SYS_ENCRYPTED is the salt). */
+ encrypted = crypt (password, sys_encrypted);
+ else
+ /* No crypt on this system! Use plain-text passwords. */
+ encrypted = password;
+
+ if (! encrypted)
+ /* Crypt failed. */
+ return errno;
+
+ /* See whether the user's password matches the system one. */
+ if (strcmp (encrypted, sys_encrypted) == 0)
+ /* Password check succeeded. */
+ return 0;
+ else if (id == 0 && !is_group && wheel_uid)
+ /* Special hack: a user attempting to gain root access can use
+ their own password (instead of root's) if they're in group 0. */
+ {
+ struct passwd _pw, *pw;
+ char lookup_buf[1024];
+ char sp_lookup_buf[1024];
+
+ const char *check_shadow (struct passwd *pw)
+ {
+ if (strcmp (pw->pw_passwd, SHADOW_PASSWORD_STRING) == 0)
+ {
+ /* When encrypted password is "x", try shadow passwords. */
+ struct spwd _sp, *sp;
+ if (getspnam_r (pw->pw_name, &_sp, sp_lookup_buf,
+ sizeof sp_lookup_buf, &sp) == 0)
+ return sp->sp_pwdp;
+ }
+ return pw->pw_passwd;
+ }
+
+ if (getpwuid_r (wheel_uid, &_pw, lookup_buf, sizeof lookup_buf, &pw))
+ return errno ?: EINVAL;
+
+ sys_encrypted = check_shadow (pw);
+
+ encrypted = crypt (password, sys_encrypted);
+ if (! encrypted)
+ /* Crypt failed. */
+ return errno;
+
+ if (strcmp (encrypted, sys_encrypted) == 0)
+ /* *this* password is correct! */
+ return 0;
+ }
+
+ return EACCES;
+}
+
+/* Make sure the user has the right to the ids in UIDS and GIDS, given that
+ we know he already has HAVE_UIDS and HAVE_GIDS, asking for passwords (with
+ GETPASS_FN) where necessary; any of the arguments may be 0, which is
+ treated the same as if they were empty. 0 is returned if access should be
+ allowed, otherwise EINVAL if an incorrect password was entered, or an
+ error relating to resource failure. Any uid/gid < 0 will be guaranteed to
+ fail regardless of what the user types. GETPASS_FN should ask for a
+ password from the user, and return it in malloced storage; it defaults to
+ using the standard libc function getpass. If VERIFY_FN is 0, then the
+ users password will be encrypted with crypt and compared with the
+ password/group entry's encrypted password, otherwise, VERIFY_FN will be
+ called to check the entered password's validity; it should return 0 if the
+ given password is correct, or an error code. The common arguments to
+ GETPASS_FN and VERIFY_FN are: ID, the user/group id; IS_GROUP, true if its
+ a group, or false if a user; PWD_OR_GRP, a pointer to either the passwd or
+ group entry for ID, and HOOK, containing the appropriate hook passed into
+ idvec_verify. */
+error_t
+idvec_verify (const struct idvec *uids, const struct idvec *gids,
+ const struct idvec *have_uids, const struct idvec *have_gids,
+ char *(*getpass_fn) (const char *prompt,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook),
+ void *getpass_hook,
+ error_t (*verify_fn) (const char *password,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook),
+ void *verify_hook)
+{
+ if (have_uids && idvec_contains (have_uids, 0))
+ /* Root can do anything. */
+ return 0;
+ else
+ {
+ unsigned int i;
+ int multiple = 0; /* Asking for multiple ids? */
+ error_t err = 0; /* Our return status. */
+ struct idvec implied_gids = IDVEC_INIT; /* Gids implied by uids. */
+ /* If we already are in group 0 (`wheel'), this user's password can be
+ used to get root privileges instead of root's. */
+ int wheel_uid =
+ ((have_uids && have_gids
+ && (idvec_contains (have_gids, 0) && have_uids->num > 0))
+ ? have_uids->ids[0]
+ : 0);
+
+ if (! verify_fn)
+ {
+ verify_fn = verify_passwd;
+ verify_hook = (void *)(intptr_t)wheel_uid;
+ }
+
+ /* See if there are multiple ids in contention, in which case we should
+ name each user/group as we ask for its password. */
+ if (uids && gids)
+ {
+ int num_non_implied_gids = 0;
+
+ /* Calculate which groups we need not ask about because they are
+ implied by the uids which we (will) have verified. Note that we
+ ignore any errors; at most, it means we will ask for too many
+ passwords. */
+ idvec_merge_implied_gids (&implied_gids, uids);
+
+ for (i = 0; i < gids->num; i++)
+ if (! idvec_contains (&implied_gids, gids->ids[i]))
+ num_non_implied_gids++;
+
+ multiple = (uids->num + num_non_implied_gids) > 1;
+ }
+ else if (uids)
+ multiple = uids->num > 1;
+ else if (gids)
+ multiple = gids->num > 1;
+
+ if (uids && idvec_contains (uids, 0))
+ /* root is being asked for, which, once granted will provide access for
+ all the others. */
+ err = verify_id (0, 0, multiple,
+ getpass_fn, getpass_hook, verify_fn, verify_hook);
+ else
+ {
+ if (uids)
+ /* Check uids */
+ for (i = 0; i < uids->num && !err; i++)
+ {
+ uid_t uid = uids->ids[i];
+ if (!have_uids || !idvec_contains (have_uids, uid))
+ err = verify_id (uid, 0, multiple,
+ getpass_fn, getpass_hook, verify_fn, verify_hook);
+ }
+
+ if (gids)
+ /* Check gids */
+ for (i = 0; i < gids->num && !err; i++)
+ {
+ gid_t gid = gids->ids[i];
+ if ((!have_gids || !idvec_contains (have_gids, gid))
+ && !idvec_contains (&implied_gids, gid))
+ err = verify_id (gid, 1, multiple,
+ getpass_fn, getpass_hook, verify_fn, verify_hook);
+ }
+ }
+
+ idvec_fini (&implied_gids);
+
+ return err;
+ }
+}
+
+/* Verify that the user should be allowed to assume the indentity of the
+ user/group ID (depending on whether IS_GROUP is false/true). If MULTIPLE
+ is true, then this is one of multiple ids being verified, so */
+static error_t
+verify_id (uid_t id, int is_group, int multiple,
+ char *(*getpass_fn) (const char *prompt,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook),
+ void *getpass_hook,
+ error_t (*verify_fn) (const char *password,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook),
+ void *verify_hook)
+{
+ int err;
+ void *pwd_or_grp = 0;
+ char *name = 0;
+ char *prompt = 0, *password;
+ char id_lookup_buf[1024];
+ char sp_lookup_buf[1024];
+
+ /* VERIFY_FN should have been defaulted in idvec_verify if necessary. */
+ assert (verify_fn);
+
+ if (id != (uid_t) -1)
+ do
+ {
+ if (is_group)
+ {
+ struct group _gr, *gr;
+ if (getgrgid_r (id, &_gr, id_lookup_buf, sizeof id_lookup_buf, &gr)
+ == 0)
+ {
+ if (!gr->gr_passwd || !*gr->gr_passwd)
+ return (*verify_fn) ("", id, 1, gr, verify_hook);
+ name = gr->gr_name;
+ pwd_or_grp = gr;
+ }
+ }
+ else
+ {
+ struct passwd _pw, *pw;
+ if (getpwuid_r (id, &_pw, id_lookup_buf, sizeof id_lookup_buf, &pw)
+ == 0)
+ {
+ if (strcmp (pw->pw_passwd, SHADOW_PASSWORD_STRING) == 0)
+ {
+ /* When encrypted password is "x", check shadow
+ passwords to see if there is an empty password. */
+ struct spwd _sp, *sp;
+ if (getspnam_r (pw->pw_name, &_sp, sp_lookup_buf,
+ sizeof sp_lookup_buf, &sp) == 0)
+ /* The storage for the password string is in
+ SP_LOOKUP_BUF, a local variable in this function.
+ We Know that the only use of PW->pw_passwd will be
+ in the VERIFY_FN call in this function, and that
+ the pointer will not be stored past the call. */
+ pw->pw_passwd = sp->sp_pwdp;
+ }
+
+ if (pw->pw_passwd[0] == '\0')
+ return (*verify_fn) ("", id, 0, pw, verify_hook);
+ name = pw->pw_name;
+ pwd_or_grp = pw;
+ }
+ }
+ if (! name)
+ {
+ /* [ug]id lookup failed! */
+ if (id != 0 || is_group)
+ /* If ID != 0, then it's probably just an unknown id, so ask for
+ the root password instead -- root should be able to do
+ anything. */
+ {
+ id = 0; /* Root */
+ is_group = 0; /* uid */
+ multiple = 1; /* Explicitly ask for root's password. */
+ }
+ else
+ /* No password entry for root. */
+ name = "root";
+ }
+ }
+ while (! name);
+
+ if (! getpass_fn)
+ /* Default GETPASS_FN to using getpass. */
+ getpass_fn = get_passwd;
+
+ if (multiple)
+ {
+ if (name)
+ asprintf (&prompt, "Password for %s%s:",
+ is_group ? "group " : "", name);
+ else
+ asprintf (&prompt, "Password for %s %d:",
+ is_group ? "group" : "user", id);
+ }
+
+ /* Prompt the user for the password. */
+ if (prompt)
+ {
+ password =
+ (*getpass_fn) (prompt, id, is_group, pwd_or_grp, getpass_hook);
+ free (prompt);
+ }
+ else
+ password =
+ (*getpass_fn) ("Password:", id, is_group, pwd_or_grp, getpass_hook);
+
+ /* Check the user's answer. */
+ if (password)
+ {
+ err = (*verify_fn) (password, id, is_group, pwd_or_grp, verify_hook);
+
+ /* Paranoia may destroya. */
+ memset (password, 0, strlen (password));
+
+ free (password);
+ }
+ else
+ err = EACCES;
+
+ return err;
+}
diff --git a/libshouldbeinlibc/idvec.c b/libshouldbeinlibc/idvec.c
new file mode 100644
index 00000000..7fdee104
--- /dev/null
+++ b/libshouldbeinlibc/idvec.c
@@ -0,0 +1,339 @@
+/* Routines for vectors of uids/gids
+
+ Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <malloc.h>
+#include <string.h>
+
+#include "idvec.h"
+
+/* Return a new, empty, idvec, or NULL if there wasn't enough memory. */
+struct idvec *
+make_idvec ()
+{
+ struct idvec *idvec = malloc (sizeof (struct idvec));
+ if (idvec)
+ {
+ idvec->alloced = idvec->num = 0;
+ idvec->ids = 0;
+ }
+ return idvec;
+}
+
+/* Free's IDVEC, but not the storage pointed to by the IDS field. */
+void
+idvec_free_wrapper (struct idvec *idvec)
+{
+ free (idvec);
+}
+
+void
+idvec_free_contents (struct idvec *idvec)
+{
+ if (idvec->alloced)
+ free (idvec->ids);
+}
+
+void
+idvec_free (struct idvec *idvec)
+{
+ idvec_free_contents (idvec);
+ idvec_free_wrapper (idvec);
+}
+
+/* Ensure that IDVEC has enough spaced allocated to hold NUM ids, thus
+ ensuring that any subsequent ids added won't return a memory allocation
+ error unless it would result in more ids that NUM. ENOMEM is returned if
+ a memory allocation error occurs. */
+error_t
+idvec_ensure (struct idvec *idvec, unsigned num)
+{
+ if (num > idvec->alloced)
+ {
+ uid_t *_ids = realloc (idvec->ids, num * sizeof (uid_t));
+ if (! _ids)
+ return ENOMEM;
+ idvec->ids = _ids;
+ idvec->alloced = num;
+ }
+ return 0;
+}
+
+/* Like idvec_ensure(), but takes INC, the increment of the number of ids
+ already in IDVEC as an argument. */
+error_t
+idvec_grow (struct idvec *idvec, unsigned inc)
+{
+ return idvec_ensure (idvec, idvec->num + inc);
+}
+
+/* Returns true if IDVEC contains ID, at or after position POS. */
+int
+idvec_tail_contains (const struct idvec *idvec, unsigned pos, uid_t id)
+{
+ uid_t *ids = idvec->ids, *end = ids + idvec->num, *p = ids + pos;
+ while (p < end)
+ if (*p++ == id)
+ return 1;
+ return 0;
+}
+
+/* Insert ID into IDVEC at position POS, returning ENOMEM if there wasn't
+ enough memory, or 0. */
+error_t
+idvec_insert (struct idvec *idvec, unsigned pos, uid_t id)
+{
+ error_t err = 0;
+ unsigned num = idvec->num;
+ unsigned new_num = (pos < num ? num + 1 : pos + 1);
+
+ if (idvec->alloced == num)
+ /* If we seem to be growing by just one, actually prealloc some more. */
+ err = idvec_ensure (idvec, new_num + num);
+ else
+ err = idvec_ensure (idvec, new_num);
+
+ if (! err)
+ {
+ uid_t *ids = idvec->ids;
+ if (pos < num)
+ bcopy (ids + pos, ids + pos + 1, (num - pos) * sizeof (uid_t));
+ else if (pos > num)
+ bzero (ids + num, (pos - num) * sizeof (uid_t));
+ ids[pos] = id;
+ idvec->num = new_num;
+ }
+
+ return err;
+}
+
+/* Add ID onto the end of IDVEC, returning ENOMEM if there's not enough memory,
+ or 0. */
+error_t
+idvec_add (struct idvec *idvec, uid_t id)
+{
+ return idvec_insert (idvec, idvec->num, id);
+}
+
+/* If IDVEC doesn't contain ID, add it onto the end, returning ENOMEM if
+ there's not enough memory; otherwise, do nothing. */
+error_t
+idvec_add_new (struct idvec *idvec, uid_t id)
+{
+ if (idvec_contains (idvec, id))
+ return 0;
+ else
+ return idvec_add (idvec, id);
+}
+
+/* If IDVEC doesn't contain ID at position POS or after, insert it at POS,
+ returning ENOMEM if there's not enough memory; otherwise, do nothing. */
+error_t
+idvec_insert_new (struct idvec *idvec, unsigned pos, uid_t id)
+{
+ if (idvec_tail_contains (idvec, pos, id))
+ return 0;
+ else
+ return idvec_insert (idvec, pos, id);
+}
+
+/* Set the ids in IDVEC to IDS (NUM elements long); delete whatever
+ the previous ids were. */
+error_t
+idvec_set_ids (struct idvec *idvec, const uid_t *ids, unsigned num)
+{
+ error_t err;
+
+ err = idvec_ensure (idvec, num);
+ if (!err)
+ {
+ bcopy (ids, idvec->ids, num * sizeof (uid_t));
+ idvec->num = num;
+ }
+ return err;
+}
+
+/* Like idvec_set_ids, but get the new ids from new. */
+error_t
+idvec_set (struct idvec *idvec, const struct idvec *new)
+{
+ return idvec_set_ids (idvec, new->ids, new->num);
+}
+
+/* Adds each id in the vector IDS (NUM elements long) to IDVEC, as long as it
+ wasn't previously in IDVEC. */
+error_t
+idvec_merge_ids (struct idvec *idvec, const uid_t *ids, unsigned num)
+{
+ error_t err = 0;
+ unsigned num_old = idvec->num;
+ while (num-- > 0 && !err)
+ {
+ unsigned int i;
+ for (i = 0; i < num_old; i++)
+ if (idvec->ids[i] == *ids)
+ break;
+ if (i == num_old)
+ err = idvec_add (idvec, *ids);
+ ids++;
+ }
+ return err;
+}
+
+/* Adds each id from NEW to IDVEC, as if with idvec_add_new(). */
+error_t
+idvec_merge (struct idvec *idvec, const struct idvec *new)
+{
+ return idvec_merge_ids (idvec, new->ids, new->num);
+}
+
+/* Remove any occurrences of ID in IDVEC after position POS.
+ Returns true if anything was done. */
+int
+idvec_remove (struct idvec *idvec, unsigned pos, uid_t id)
+{
+ if (pos < idvec->num)
+ {
+ int left = idvec->num - pos;
+ uid_t *ids = idvec->ids + pos, *targ = ids;
+ while (left--)
+ {
+ if (*ids != id)
+ {
+ if (ids != targ)
+ *targ = *ids;
+ targ++;
+ }
+ ids++;
+ }
+ if (ids == targ)
+ return 0;
+ idvec->num = targ - idvec->ids;
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* Remove all ids in SUB from IDVEC, returning true if anything was done. */
+int
+idvec_subtract (struct idvec *idvec, const struct idvec *sub)
+{
+ unsigned int i;
+ int done = 0;
+ for (i = 0; i < sub->num; i++)
+ done |= idvec_remove (idvec, 0, sub->ids[i]);
+ return done;
+}
+
+/* Remove all ids from IDVEC that are *not* in KEEP, returning true if
+ anything was changed. */
+int
+idvec_keep (struct idvec *idvec, const struct idvec *keep)
+{
+ uid_t *old = idvec->ids, *new = old, *end = old + idvec->num;
+
+ while (old < end)
+ {
+ uid_t id = *old++;
+ if (idvec_contains (keep, id))
+ {
+ if (old != new)
+ *new = id;
+ new++;
+ }
+ }
+
+ if (old != new)
+ {
+ idvec->num = new - idvec->ids;
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* Deleted the id at position POS in IDVEC. */
+void
+idvec_delete (struct idvec *idvec, unsigned pos)
+{
+ unsigned num = idvec->num;
+ if (pos < num)
+ {
+ uid_t *ids = idvec->ids;
+ idvec->num = --num;
+ if (num > pos)
+ bcopy (ids + pos + 1, ids + pos, (num - pos) * sizeof (uid_t));
+ }
+}
+
+/* Insert ID at position POS in IDVEC, remove any instances of ID previously
+ present at POS or after. ENOMEM is returned if there's not enough memory,
+ otherwise 0. */
+error_t
+idvec_insert_only (struct idvec *idvec, unsigned pos, uid_t id)
+{
+ if (idvec->num > pos && idvec->ids[pos] == id)
+ return 0;
+ else
+ {
+ idvec_remove (idvec, pos, id);
+ return idvec_insert (idvec, pos, id);
+ }
+}
+
+/* EFF and AVAIL should be idvec's corresponding to a processes
+ effective and available ids. ID replaces the first id in EFF, and,
+ if there are any IDs in AVAIL, replaces the second ID in AVAIL;
+ what it replaces in any case is preserved by adding it to AVAIL if
+ not already present. In addition, the If SECURE is non-NULL, and
+ ID was not previously present in either EFF or AVAIL, then *SECURE
+ is set to true. ENOMEM is returned if a malloc fails, otherwise 0.
+ The return parameters are only touched if this call succeeds. */
+error_t
+idvec_setid (struct idvec *eff, struct idvec *avail, uid_t id, int *secure)
+{
+ error_t err;
+ /* True if ID was not previously present in either EFF or AVAIL. */
+ int _secure = !idvec_contains (eff, id) && !idvec_contains (avail, id);
+
+ if (eff->num > 0)
+ {
+ /* If there are any old effective ids, we replace eff[0] with
+ ID, and try to preserve the old eff[0] by putting it in AVAIL
+ list if necessary. */
+ err = idvec_add_new (avail, eff->ids[0]);
+ if (!err)
+ eff->ids[0] = id;
+ }
+ else
+ /* No previous effective ids, just make ID the first one. */
+ err = idvec_add (eff, id);
+
+ if (avail->num > 0 && !err)
+ err = idvec_insert_only (avail, 1, id);
+
+ if (err)
+ return err;
+
+ if (_secure && secure && !*secure)
+ *secure = 1;
+
+ return 0;
+}
diff --git a/libshouldbeinlibc/idvec.h b/libshouldbeinlibc/idvec.h
new file mode 100644
index 00000000..d6ec1553
--- /dev/null
+++ b/libshouldbeinlibc/idvec.h
@@ -0,0 +1,236 @@
+/* Routines for vectors of uids/gids
+
+ Copyright (C) 1995,96,97,99,2001 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __IDVEC_H__
+#define __IDVEC_H__
+
+#include <sys/types.h>
+#include <hurd/hurd_types.h>
+#include <string.h>
+#include <features.h>
+
+#ifdef IDVEC_DEFINE_EI
+#define IDVEC_EI
+#else
+#define IDVEC_EI __extern_inline
+#endif
+
+struct idvec
+{
+ uid_t *ids;
+ unsigned num, alloced;
+};
+
+#define IDVEC_INIT { 0 }
+
+/* Return a new, empty, idvec, or NULL if there wasn't enough memory. */
+struct idvec *make_idvec (void);
+
+/* Free the storage pointed to by IDVEC->ids. */
+void idvec_free_contents (struct idvec *idvec);
+#define idvec_fini idvec_free_contents
+
+/* Free IDVEC, but not the storage pointed to by the IDS field. */
+void idvec_free_wrapper (struct idvec *idvec);
+
+/* Free IDVEC and any storage associated with it. */
+void idvec_free (struct idvec *idvec);
+
+extern void idvec_clear (struct idvec *idvec);
+
+extern int idvec_is_empty (const struct idvec *idvec);
+
+extern int idvec_equal (const struct idvec *idvec1, const struct idvec *idvec2);
+
+#if defined(__USE_EXTERN_INLINES) || defined(IDVEC_DEFINE_EI)
+
+/* Mark IDVEC as not containing any ids. */
+IDVEC_EI void
+idvec_clear (struct idvec *idvec)
+{
+ idvec->num = 0;
+}
+
+/* Returns true if IDVEC contains no ids. */
+IDVEC_EI int
+idvec_is_empty (const struct idvec *idvec)
+{
+ return idvec->num == 0;
+}
+
+/* Return true if IDVEC1 has contents identical to IDVEC2. */
+IDVEC_EI int
+idvec_equal (const struct idvec *idvec1, const struct idvec *idvec2)
+{
+ size_t num = idvec1->num;
+ return idvec2->num == num
+ && (num == 0
+ || memcmp (idvec1->ids, idvec2->ids, num * sizeof *idvec1->ids) == 0);
+}
+
+#endif /* Use extern inlines. */
+
+/* Ensure that IDVEC has enough spaced allocated to hold NUM ids, thus
+ ensuring that any subsequent ids added won't return a memory allocation
+ error unless it would result in more ids that NUM. ENOMEM is returned if
+ a memory allocation error occurs. */
+error_t idvec_ensure (struct idvec *idvec, unsigned num);
+
+/* Like idvec_ensure(), but takes INC, the increment of the number of ids
+ already in IDVEC as an argument. */
+error_t idvec_grow (struct idvec *idvec, unsigned inc);
+
+/* Returns true if IDVEC contains ID, at or after position POS. */
+int idvec_tail_contains (const struct idvec *idvec, unsigned pos, uid_t id);
+
+extern int idvec_contains (const struct idvec *idvec, uid_t id);
+
+#if defined(__USE_EXTERN_INLINES) || defined(IDVEC_DEFINE_EI)
+
+/* Returns true if IDVEC contains ID. */
+IDVEC_EI int
+idvec_contains (const struct idvec *idvec, uid_t id)
+{
+ return idvec_tail_contains (idvec, 0, id);
+}
+
+#endif /* Use extern inlines. */
+
+/* Insert ID into IDVEC at position POS, returning ENOMEM if there wasn't
+ enough memory, or 0. */
+error_t idvec_insert (struct idvec *idvec, unsigned pos, uid_t id);
+
+/* Add ID onto the end of IDVEC, returning ENOMEM if there's not enough memory,
+ or 0. */
+error_t idvec_add (struct idvec *idvec, uid_t id);
+
+/* If IDVEC doesn't contain ID, add it onto the end, returning ENOMEM if
+ there's not enough memory; otherwise, do nothing. */
+error_t idvec_add_new (struct idvec *idvec, uid_t id);
+
+/* If IDVEC doesn't contain ID at position POS or after, insert it at POS,
+ returning ENOMEM if there's not enough memory; otherwise, do nothing. */
+error_t idvec_insert_new (struct idvec *idvec, unsigned pos, uid_t id);
+
+/* Set the ids in IDVEC to IDS (NUM elements long); delete whatever
+ the previous ids were. */
+error_t idvec_set_ids (struct idvec *idvec, const uid_t *ids, unsigned num);
+
+/* Like idvec_set_ids, but get the new ids from new. */
+error_t idvec_set (struct idvec *idvec, const struct idvec *new);
+
+/* Adds each id in the vector IDS (NUM elements long) to IDVEC, as if with
+ idvec_add_new(). */
+error_t idvec_merge_ids (struct idvec *idvec, const uid_t *ids, unsigned num);
+
+/* Adds each id from NEW to IDVEC, as if with idvec_add_new(). */
+error_t idvec_merge (struct idvec *idvec, const struct idvec *new);
+
+/* Remove all ids in SUB from IDVEC, returning true if anything was done. */
+int idvec_subtract (struct idvec *idvec, const struct idvec *sub);
+
+/* Remove all ids from IDVEC that are *not* in KEEP, returning true if
+ anything was changed. */
+int idvec_keep (struct idvec *idvec, const struct idvec *keep);
+
+/* Remove any occurrences of ID in IDVEC after position POS> Returns true if
+ anything was done. */
+int idvec_remove (struct idvec *idvec, unsigned pos, uid_t id);
+
+/* Deleted the id at position POS in IDVEC. */
+void idvec_delete (struct idvec *idvec, unsigned pos);
+
+/* Insert ID at position POS in IDVEC, remove any instances of ID previously
+ present at POS or after. ENOMEM is returned if there's not enough memory,
+ otherwise 0. */
+error_t idvec_insert_only (struct idvec *idvec, unsigned pos, uid_t id);
+
+/* EFF and AVAIL should be idvec's corresponding to a process's
+ effective and available ids. ID replaces the first id in EFF, and,
+ if there are any IDs in AVAIL, replaces the second ID in AVAIL;
+ what it replaces in any case is preserved by adding it to AVAIL if
+ not already present. In addition, the If SECURE is non-NULL, and
+ ID was not previously present in either EFF or AVAIL, then *SECURE
+ is set to true. ENOMEM is returned if a malloc fails, otherwise 0.
+ The return parameters are only touched if this call succeeds. */
+error_t idvec_setid (struct idvec *eff, struct idvec *avail, uid_t id,
+ int *secure);
+
+/* Add to all of EFF_UIDS, AVAIL_UIDS, EFF_GIDS, AVAIL_GIDS (as if with
+ idvec_merge) the ids associated with the auth port AUTH. Any of these
+ parameters may be NULL if that information isn't desired. */
+error_t idvec_merge_auth (struct idvec *eff_uids, struct idvec *avail_uids,
+ struct idvec *eff_gids, struct idvec *avail_gids,
+ auth_t auth);
+
+/* Add to GIDS those group ids implied by the users in UIDS. */
+error_t idvec_merge_implied_gids (struct idvec *gids, const struct idvec *uids);
+
+/* Make sure the user has the right to the ids in UIDS and GIDS, given that
+ we know he already has HAVE_UIDS and HAVE_GIDS, asking for passwords (with
+ GETPASS_FN) where necessary; any of the arguments may be 0, which is
+ treated the same as if they were empty. 0 is returned if access should be
+ allowed, otherwise EINVAL if an incorrect password was entered, or an
+ error relating to resource failure. Any uid/gid < 0 will be guaranteed to
+ fail regardless of what the user types. GETPASS_FN should ask for a
+ password from the user, and return it in malloced storage; it defaults to
+ using the standard libc function getpass. If VERIFY_FN is 0, then the
+ users password will be encrypted with crypt and compared with the
+ password/group entry's encrypted password, otherwise, VERIFY_FN will be
+ called to check the entered password's validity; it should return 0 if the
+ given password is correct, or an error code. The common arguments to
+ GETPASS_FN and VERIFY_FN are: ID, the user/group id; IS_GROUP, true if its
+ a group, or false if a user; PWD_OR_GRP, a pointer to either the passwd or
+ group entry for ID, and HOOK, containing the appropriate hook passed into
+ idvec_verify. */
+error_t idvec_verify (const struct idvec *uids, const struct idvec *gids,
+ const struct idvec *have_uids,
+ const struct idvec *have_gids,
+ char *(*getpass_fn) (const char *prompt,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook),
+ void *getpass_hook,
+ error_t (*verify_fn) (const char *password,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook),
+ void *verify_hook);
+
+/* Return a string representation of the ids in IDVEC, each id separated by
+ the string SEP (default ","). SHOW_VALUES and SHOW_NAMES reflect how each
+ id is printed (if SHOW_NAMES is true values are used where names aren't
+ available); if both are true, the `VALUE(NAME)' format is used.
+ ID_NAME_FN is used to map each id to a name; it should return a malloced
+ string, which will be freed here. The empty string is returned for an
+ empty list, and 0 for an allocation error. */
+char *idvec_rep (const struct idvec *idvec,
+ int show_values, int show_names,
+ char *(*id_name_fn) (uid_t id),
+ const char *sep);
+
+/* Like idvec_rep, mapping ids to user names. */
+char *idvec_uids_rep (const struct idvec *idvec,
+ int show_values, int show_names,
+ const char *sep);
+
+/* Like idvec_rep, mapping ids to group names. */
+char *idvec_gids_rep (const struct idvec *idvec,
+ int show_values, int show_names,
+ const char *sep);
+
+#endif /* __IDVEC_H__ */
diff --git a/libshouldbeinlibc/lcm.c b/libshouldbeinlibc/lcm.c
new file mode 100644
index 00000000..606f4eba
--- /dev/null
+++ b/libshouldbeinlibc/lcm.c
@@ -0,0 +1,46 @@
+/* Lcm (least common multiple), and gcd (greatest common divisor)
+
+ Copyright (C) 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* There are probably more efficient ways to do these... */
+
+/* Return the greatest common divisor of p & q. */
+inline long
+gcd (long p, long q)
+{
+ if (p == 0)
+ return q;
+ else if (q == 0)
+ return p;
+ else if (p == q)
+ return p;
+ else if (q > p)
+ return gcd (q, p);
+ else
+ return gcd (q, p % q);
+}
+
+/* Return the least common multiple of p & q. */
+long
+lcm (long p, long q)
+{
+ return (p / gcd (p, q)) * q;
+}
diff --git a/libshouldbeinlibc/localhost.c b/libshouldbeinlibc/localhost.c
new file mode 100644
index 00000000..9b7d4e09
--- /dev/null
+++ b/libshouldbeinlibc/localhost.c
@@ -0,0 +1,75 @@
+/* A slightly more convenient wrapper for gethostname
+
+ Copyright (C) 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* Return the name of the localhost. This is just a wrapper for gethostname,
+ which takes care of allocating a big enough buffer, and caches the result
+ after the first call (so the result should be copied before modification).
+ If something goes wrong, 0 is returned, and errno set. */
+char *
+localhost ()
+{
+ static char *buf = 0;
+ static size_t buf_len = 0;
+
+ if (! buf)
+ {
+ do {
+ errno = 0;
+
+ if (buf) {
+ char *new;
+ buf_len += buf_len;
+ new = realloc (buf, buf_len);
+ if (! new)
+ {
+ free (buf);
+ buf = 0;
+ errno = ENOMEM;
+ return 0;
+ }
+ else
+ buf = new;
+ } else {
+ buf_len = 128; /* Initial guess */
+ buf = malloc (buf_len);
+ if (! buf)
+ {
+ errno = ENOMEM;
+ return 0;
+ }
+ }
+ } while ((gethostname(buf, buf_len) == 0 && !memchr (buf, '\0', buf_len))
+ || errno == ENAMETOOLONG);
+
+ if (errno)
+ /* gethostname failed, abort. */
+ {
+ free (buf);
+ buf = 0;
+ }
+ }
+
+ return buf;
+}
diff --git a/libshouldbeinlibc/maptime-funcs.c b/libshouldbeinlibc/maptime-funcs.c
new file mode 100644
index 00000000..080e3ae6
--- /dev/null
+++ b/libshouldbeinlibc/maptime-funcs.c
@@ -0,0 +1,5 @@
+#define MAPTIME_DEFINE_EI
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include "maptime.h"
diff --git a/libshouldbeinlibc/maptime.c b/libshouldbeinlibc/maptime.c
new file mode 100644
index 00000000..f0b69db4
--- /dev/null
+++ b/libshouldbeinlibc/maptime.c
@@ -0,0 +1,84 @@
+/* Support for mach's mapped time
+
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <fcntl.h>
+#include <hurd.h>
+#include <device/device.h>
+
+#include "maptime.h"
+
+/* Return the mach mapped time page in MTIME. If USE_MACH_DEV is false, then
+ the hurd time device DEV_NAME, or "/dev/time" if DEV_NAME is 0, is
+ used. If USE_MACH_DEV is true, the mach device DEV_NAME, or "time" if
+ DEV_NAME is 0, is used; this is a privileged operation. The mapped time
+ may be converted to a struct timeval at any time using maptime_read. */
+error_t
+maptime_map (int use_mach_dev, char *dev_name,
+ volatile struct mapped_time_value **mtime)
+{
+ error_t err;
+ mach_port_t memobj;
+
+ if (use_mach_dev)
+ {
+ device_t device;
+ mach_port_t device_master;
+
+ err = get_privileged_ports (0, &device_master);
+ if (err)
+ return err;
+
+ err = device_open (device_master, 0, dev_name ?: "time", &device);
+ mach_port_deallocate (mach_task_self (), device_master);
+ if (err)
+ return err;
+
+ err = device_map (device, VM_PROT_READ, 0, sizeof *mtime, &memobj, 0);
+
+ /* Deallocate the device port. The mapping is independent of
+ this port. */
+ mach_port_deallocate (mach_task_self (), device);
+ }
+ else
+ {
+ mach_port_t wr_memobj;
+ file_t node = file_name_lookup (dev_name ?: "/dev/time", O_RDONLY, 0);
+
+ if (node == MACH_PORT_NULL)
+ return errno;
+
+ err = io_map (node, &memobj, &wr_memobj);
+ if (!err && wr_memobj != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), wr_memobj);
+
+ mach_port_deallocate (mach_task_self (), node);
+ }
+
+ if (! err)
+ {
+ *mtime = 0;
+ err =
+ vm_map (mach_task_self (), (vm_address_t *)mtime, sizeof *mtime, 0, 1,
+ memobj, 0, 0, VM_PROT_READ, VM_PROT_READ, VM_INHERIT_NONE);
+ mach_port_deallocate (mach_task_self (), memobj);
+ }
+
+ return err;
+}
diff --git a/libshouldbeinlibc/maptime.h b/libshouldbeinlibc/maptime.h
new file mode 100644
index 00000000..947ad640
--- /dev/null
+++ b/libshouldbeinlibc/maptime.h
@@ -0,0 +1,61 @@
+/* Support for mach's mapped time
+
+ Copyright (C) 1996, 1997, 2000, 2007 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __MAPTIME_H__
+#define __MAPTIME_H__
+
+#include <mach/time_value.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <features.h>
+
+#ifdef MAPTIME_DEFINE_EI
+#define MAPTIME_EI
+#else
+#define MAPTIME_EI __extern_inline
+#endif
+
+/* Return the mach mapped time page in MTIME. If USE_MACH_DEV is false, then
+ the hurd time device DEV_NAME, or "/dev/time" if DEV_NAME is 0, is
+ used. If USE_MACH_DEV is true, the mach device DEV_NAME, or "time" if
+ DEV_NAME is 0, is used; this is a privileged operation. The mapped time
+ may be converted to a struct timeval at any time using maptime_read. */
+error_t maptime_map (int use_mach_dev, char *dev_name,
+ volatile struct mapped_time_value **mtime);
+
+extern void maptime_read (volatile struct mapped_time_value *mtime, struct timeval *tv);
+
+#if defined(__USE_EXTERN_INLINES) || defined(MAPTIME_DEFINE_EI)
+
+/* Read the current time from MTIME into TV. This should be very fast. */
+MAPTIME_EI void
+maptime_read (volatile struct mapped_time_value *mtime, struct timeval *tv)
+{
+ do
+ {
+ tv->tv_sec = mtime->seconds;
+ tv->tv_usec = mtime->microseconds;
+ }
+ while (tv->tv_sec != mtime->check_seconds);
+}
+
+#endif /* Use extern inlines. */
+
+#endif /* __MAPTIME_H__ */
diff --git a/libshouldbeinlibc/nullauth.c b/libshouldbeinlibc/nullauth.c
new file mode 100644
index 00000000..3a98e558
--- /dev/null
+++ b/libshouldbeinlibc/nullauth.c
@@ -0,0 +1,45 @@
+/* Drop all authentication credentials.
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <hurd.h>
+
+/* Obtain an empty authentication handle and use it for further
+ authentication purposes. This effectively drops all Unix
+ privileges. */
+error_t
+setnullauth (void)
+{
+ error_t err;
+
+ auth_t nullauth;
+ err = auth_makeauth (getauth (),
+ NULL, MACH_MSG_TYPE_COPY_SEND, 0,
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ &nullauth);
+ if (err)
+ return err;
+
+ err = setauth (nullauth);
+ return err;
+}
diff --git a/libshouldbeinlibc/nullauth.h b/libshouldbeinlibc/nullauth.h
new file mode 100644
index 00000000..efdb5f3a
--- /dev/null
+++ b/libshouldbeinlibc/nullauth.h
@@ -0,0 +1,31 @@
+/* Drop all authentication credentials.
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __NULLAUTH_H__
+#define __NULLAUTH_H__
+
+/* Obtain an empty authentication handle and use it for further
+ authentication purposes. This effectively drops all Unix
+ privileges. */
+error_t
+setnullauth (void);
+
+#endif /* __NULLAUTH_H__ */
diff --git a/libshouldbeinlibc/portinfo.c b/libshouldbeinlibc/portinfo.c
new file mode 100644
index 00000000..e6305c6e
--- /dev/null
+++ b/libshouldbeinlibc/portinfo.c
@@ -0,0 +1,158 @@
+/* Print information about a task's ports
+
+ Copyright (C) 1996,98,99,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include "portinfo.h"
+
+/* Prints info about NAME in TASK to STREAM, in a way described by the flags
+ in SHOW. If TYPE is non-zero, it should be what mach_port_type returns
+ for NAME. */
+error_t
+print_port_info (mach_port_t name, mach_port_type_t type, task_t task,
+ unsigned show, FILE *stream)
+{
+ int hex_names = (show & PORTINFO_HEX_NAMES);
+ int first = 1;
+ void comma ()
+ {
+ if (first)
+ first = 0;
+ else
+ fprintf (stream, ", ");
+ }
+ void prefs (mach_port_right_t right)
+ {
+ mach_port_urefs_t refs;
+ error_t err = mach_port_get_refs (task, name, right, &refs);
+ if (! err)
+ fprintf (stream, " (refs: %zu)", refs);
+ }
+
+ if (type == 0)
+ {
+ error_t err = mach_port_type (task, name, &type);
+ if (err)
+ return err;
+ }
+
+ fprintf (stream, hex_names ? "%#6zx: " : "%6zd: ", name);
+
+ if (type & MACH_PORT_TYPE_RECEIVE)
+ {
+ comma ();
+ fprintf (stream, "receive");
+ if (show & PORTINFO_DETAILS)
+ {
+ struct mach_port_status status;
+ error_t err = mach_port_get_receive_status (task, name, &status);
+ if (! err)
+ {
+ fprintf (stream, " (");
+ if (status.mps_pset != MACH_PORT_NULL)
+ fprintf (stream,
+ hex_names ? "port-set: %#zx, " : "port-set: %zd, ",
+ status.mps_pset);
+ fprintf (stream, "seqno: %zu", status.mps_seqno);
+ if (status.mps_mscount)
+ fprintf (stream, ", ms-count: %zu", status.mps_mscount);
+ if (status.mps_qlimit != MACH_PORT_QLIMIT_DEFAULT)
+ fprintf (stream, ", qlimit: %zu", status.mps_qlimit);
+ if (status.mps_msgcount)
+ fprintf (stream, ", msgs: %zu", status.mps_msgcount);
+ fprintf (stream, "%s%s%s)",
+ status.mps_srights ? ", send-rights" : "",
+ status.mps_pdrequest ? ", pd-req" : "",
+ status.mps_nsrequest ? ", ns-req" : "");
+ }
+ }
+ }
+ if (type & MACH_PORT_TYPE_SEND)
+ {
+ comma ();
+ fprintf (stream, "send");
+ if (show & PORTINFO_DETAILS)
+ prefs (MACH_PORT_RIGHT_SEND);
+ }
+ if (type & MACH_PORT_TYPE_SEND_ONCE)
+ {
+ comma ();
+ fprintf (stream, "send-once");
+ }
+ if (type & MACH_PORT_TYPE_DEAD_NAME)
+ {
+ comma ();
+ fprintf (stream, "dead-name");
+ if (show & PORTINFO_DETAILS)
+ prefs (MACH_PORT_RIGHT_DEAD_NAME);
+ }
+ if (type & MACH_PORT_TYPE_PORT_SET)
+ {
+ comma ();
+ fprintf (stream, "port-set");
+ if (show & PORTINFO_DETAILS)
+ {
+ mach_port_t *members = 0;
+ mach_msg_type_number_t members_len = 0, i;
+ error_t err =
+ mach_port_get_set_status (task, name, &members, &members_len);
+ if (! err)
+ {
+ if (members_len == 0)
+ fprintf (stream, " (empty)");
+ else
+ {
+ fprintf (stream, hex_names ? " (%#zx" : " (%zu", members[0]);
+ for (i = 1; i < members_len; i++)
+ fprintf (stream, hex_names ? ", %#zx" : ", %zu",
+ members[i]);
+ fprintf (stream, ")");
+ munmap ((caddr_t) members, members_len * sizeof *members);
+ }
+ }
+ }
+ }
+ putc ('\n', stream);
+
+ return 0;
+}
+
+/* Prints info about every port in TASK that has a type in ONLY to STREAM. */
+error_t
+print_task_ports_info (task_t task, mach_port_type_t only,
+ unsigned show, FILE *stream)
+{
+ mach_port_t *names = 0;
+ mach_port_type_t *types = 0;
+ mach_msg_type_number_t names_len = 0, types_len = 0, i;
+ error_t err = mach_port_names (task, &names, &names_len, &types, &types_len);
+
+ if (err)
+ return err;
+
+ for (i = 0; i < names_len; i++)
+ if (types[i] & only)
+ print_port_info (names[i], types[i], task, show, stream);
+
+ munmap ((caddr_t) names, names_len * sizeof *names);
+ munmap ((caddr_t) types, types_len * sizeof *types);
+
+ return 0;
+}
diff --git a/libshouldbeinlibc/portinfo.h b/libshouldbeinlibc/portinfo.h
new file mode 100644
index 00000000..143c2898
--- /dev/null
+++ b/libshouldbeinlibc/portinfo.h
@@ -0,0 +1,58 @@
+/* Print information about a task's ports
+
+ Copyright (C) 1996, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __PORTINFO_H__
+#define __PORTINFO_H__
+
+#include <stdio.h>
+#include <errno.h>
+#include <mach.h>
+
+#include <portxlate.h>
+
+/* Flags describing what to show. */
+#define PORTINFO_DETAILS 0x1
+#define PORTINFO_MEMBERS 0x4
+#define PORTINFO_HEX_NAMES 0x8
+
+/* Prints info about NAME in TASK to STREAM, in a way described by the flags
+ in SHOW. If TYPE is non-zero, it should be what mach_port_type returns
+ for NAME. */
+error_t print_port_info (mach_port_t name, mach_port_type_t type, task_t task,
+ unsigned show, FILE *stream);
+
+/* Prints info about every port in TASK that has a type in ONLY to STREAM. */
+error_t print_task_ports_info (task_t task, mach_port_type_t only,
+ unsigned show, FILE *stream);
+
+/* Prints info about NAME translated through X to STREAM, in a way described
+ by the flags in SHOW. If TYPE is non-zero, it should be what
+ mach_port_type returns for NAME in X->to_task. */
+error_t print_xlated_port_info (mach_port_t name, mach_port_type_t type,
+ struct port_name_xlator *x,
+ unsigned show, FILE *stream);
+
+/* Prints info about every port common to both tasks in X, but only if the
+ port in X->from_task has a type in ONLY, to STREAM. */
+error_t print_xlated_task_ports_info (struct port_name_xlator *x,
+ mach_port_type_t only,
+ unsigned show, FILE *stream);
+
+#endif /* __PORTINFO_H__ */
diff --git a/libshouldbeinlibc/portxlate.c b/libshouldbeinlibc/portxlate.c
new file mode 100644
index 00000000..f78abbf1
--- /dev/null
+++ b/libshouldbeinlibc/portxlate.c
@@ -0,0 +1,179 @@
+/* Translate mach port names between two tasks
+
+ Copyright (C) 1996,99,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include "portxlate.h"
+
+/* Return a new port name translator translating names between FROM_TASK and
+ TO_TASK, in XLATOR, or an error. */
+error_t
+port_name_xlator_create (mach_port_t from_task, mach_port_t to_task,
+ struct port_name_xlator **xlator)
+{
+ error_t err;
+ struct port_name_xlator *x = malloc (sizeof (struct port_name_xlator));
+
+ if (! x)
+ return ENOMEM;
+
+ mach_port_mod_refs (mach_task_self (), from_task, MACH_PORT_RIGHT_SEND, +1);
+ x->from_task = from_task;
+ mach_port_mod_refs (mach_task_self (), to_task, MACH_PORT_RIGHT_SEND, +1);
+ x->to_task = to_task;
+ x->to_names = 0;
+ x->to_types = 0;
+ x->to_names_len = 0;
+ x->to_types_len = 0;
+
+ /* Cache a list of names in TO_TASK. */
+ err = mach_port_names (to_task,
+ &x->to_names, &x->to_names_len,
+ &x->to_types, &x->to_types_len);
+
+ if (! err)
+ /* Make an array to hold ports from TO_TASK which have been translated
+ into our namespace. */
+ {
+ x->ports = malloc (sizeof (mach_port_t) * x->to_names_len);
+ if (x->ports)
+ {
+ unsigned int i;
+ for (i = 0; i < x->to_names_len; i++)
+ x->ports[i] = MACH_PORT_NULL;
+ }
+ else
+ {
+ munmap ((caddr_t) x->to_names,
+ x->to_names_len * sizeof (mach_port_t));
+ munmap ((caddr_t) x->to_types,
+ x->to_types_len * sizeof (mach_port_type_t));
+
+ mach_port_deallocate (mach_task_self (), x->to_task);
+ mach_port_deallocate (mach_task_self (), x->from_task);
+
+ err = ENOMEM;
+ }
+ }
+
+ if (err)
+ free (x);
+ else
+ *xlator = x;
+
+ return err;
+}
+
+/* Free the port name translator X and any resources it holds. */
+void
+port_name_xlator_free (struct port_name_xlator *x)
+{
+ unsigned int i;
+
+ for (i = 0; i < x->to_names_len; i++)
+ if (x->ports[i] != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), x->ports[i]);
+ free (x->ports);
+
+ munmap ((caddr_t) x->to_names, x->to_names_len * sizeof (mach_port_t));
+ munmap ((caddr_t) x->to_types, x->to_types_len * sizeof (mach_port_type_t));
+
+ mach_port_deallocate (mach_task_self (), x->to_task);
+ mach_port_deallocate (mach_task_self (), x->from_task);
+
+ free (x);
+}
+
+/* Translate the port FROM between the tasks in X, returning the translated
+ name in TO, and the types of TO in TO_TYPE, or an error. If TYPE is
+ non-zero, it should be what mach_port_type returns for FROM. */
+error_t
+port_name_xlator_xlate (struct port_name_xlator *x,
+ mach_port_t from, mach_port_type_t from_type,
+ mach_port_t *to, mach_port_type_t *to_type)
+{
+ error_t err;
+ mach_port_t port;
+ mach_msg_type_number_t i;
+ mach_msg_type_name_t aquired_type;
+ mach_msg_type_name_t valid_to_types;
+
+ if (from_type == 0)
+ {
+ error_t err = mach_port_type (x->from_task, from, &from_type);
+ if (err)
+ return err;
+ }
+
+ if (from_type & MACH_PORT_TYPE_RECEIVE)
+ valid_to_types = MACH_PORT_TYPE_SEND;
+ else if (from_type & MACH_PORT_TYPE_SEND)
+ valid_to_types = MACH_PORT_TYPE_SEND | MACH_PORT_TYPE_RECEIVE;
+ else
+ return EKERN_INVALID_RIGHT;
+
+ /* Translate the name FROM, in FROM_TASK's namespace into our namespace. */
+ err =
+ mach_port_extract_right (x->from_task, from,
+ ((from_type & MACH_PORT_TYPE_RECEIVE)
+ ? MACH_MSG_TYPE_MAKE_SEND
+ : MACH_MSG_TYPE_COPY_SEND),
+ &port,
+ &aquired_type);
+
+ if (err)
+ return err;
+
+ /* Look for likely candidates in TO_TASK's namespace to test against PORT. */
+ for (i = 0; i < x->to_names_len; i++)
+ {
+ if (x->ports[i] == MACH_PORT_NULL && (x->to_types[i] & valid_to_types))
+ /* Port I shows possibilities... */
+ {
+ err =
+ mach_port_extract_right (x->to_task,
+ x->to_names[i],
+ ((x->to_types[i] & MACH_PORT_TYPE_RECEIVE)
+ ? MACH_MSG_TYPE_MAKE_SEND
+ : MACH_MSG_TYPE_COPY_SEND),
+ &x->ports[i],
+ &aquired_type);
+ if (err)
+ x->to_types[i] = 0; /* Don't try to fetch this port again. */
+ }
+
+ if (x->ports[i] == port)
+ /* We win! Port I in TO_TASK is the same as PORT. */
+ break;
+ }
+
+ mach_port_deallocate (mach_task_self (), port);
+
+ if (i < x->to_names_len)
+ /* Port I is the right translation; return its name in TO_TASK. */
+ {
+ *to = x->to_names[i];
+ *to_type = x->to_types[i];
+ return 0;
+ }
+ else
+ return EKERN_INVALID_NAME;
+}
diff --git a/libshouldbeinlibc/portxlate.h b/libshouldbeinlibc/portxlate.h
new file mode 100644
index 00000000..13ddb148
--- /dev/null
+++ b/libshouldbeinlibc/portxlate.h
@@ -0,0 +1,67 @@
+/* Translate mach port names between two tasks
+
+ Copyright (C) 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __PORTXLATE_H__
+#define __PORTXLATE_H__
+
+#include <errno.h>
+#include <mach.h>
+
+/* A data structure specifying two tasks, and info used to translate port
+ names between them. */
+struct port_name_xlator
+{
+ /* The tasks between which we are translating port names. */
+ mach_port_t from_task;
+ mach_port_t to_task;
+
+ /* True if we're translating receive rights in FROM_TASK; otherwise, we're
+ translating send rights. */
+ int from_is_receive;
+
+ /* Arrays of port names and type masks from TO_TASK, fetched by
+ mach_port_names. These are vm_allocated. */
+ mach_port_t *to_names;
+ mach_msg_type_number_t to_names_len;
+ mach_port_type_t *to_types;
+ mach_msg_type_number_t to_types_len;
+
+ /* An array of rights in the current task to the ports in TO_NAMES/TO_TASK,
+ or MACH_PORT_NULL, indicating that none has been fetched yet.
+ This vector is malloced. */
+ mach_port_t *ports;
+};
+
+/* Return a new port name translator translating names between FROM_TASK and
+ TO_TASK, in XLATOR, or an error. */
+error_t port_name_xlator_create (mach_port_t from_task, mach_port_t to_task,
+ struct port_name_xlator **xlator);
+
+/* Free the port name translator X and any resources it holds. */
+void port_name_xlator_free (struct port_name_xlator *x);
+
+/* Translate the port FROM between the tasks in X, returning the translated
+ name in TO, and the types of TO in TO_TYPE, or an error. If TYPE is
+ non-zero, it should be what mach_port_type returns for FROM. */
+error_t port_name_xlator_xlate (struct port_name_xlator *x,
+ mach_port_t from, mach_port_type_t from_type,
+ mach_port_t *to, mach_port_type_t *to_type);
+
+#endif /* __PORTXLATE_H__ */
diff --git a/libshouldbeinlibc/shared-dom.c b/libshouldbeinlibc/shared-dom.c
new file mode 100644
index 00000000..0115fced
--- /dev/null
+++ b/libshouldbeinlibc/shared-dom.c
@@ -0,0 +1,54 @@
+/* Deduce the shared portion of two hostnames
+
+ Copyright (C) 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h>
+
+/* Returns a pointer into HOST1 that is the part of the domain shared with
+ HOST2. If the two do not share anything, the return value will point to
+ the end of HOST1. If either host is NULL, NULL is returned. */
+char *
+shared_domain (char *host1, char *host2)
+{
+ char *shared, *e1, *e2;
+
+ if (!host1 || !host2)
+ return 0;
+
+ /* Now compare HOST1 and HOST2 from the end. */
+ e2 = host2 + strlen (host2);
+ e1 = host1 + strlen (host1);
+ shared = e1;
+
+ /* Ignore `absolute' syntax. */
+ if (*e1 == '.')
+ e1--;
+ if (*e2 == '.')
+ e2--;
+
+ while (e1 > host1 && e2 > host2 && *e2 == *e1)
+ {
+ if (*e1 == '.')
+ shared = e1; /* A common domain level has been passed. */
+ e1--;
+ e2--;
+ }
+
+ return shared;
+}
diff --git a/libshouldbeinlibc/termsize.c b/libshouldbeinlibc/termsize.c
new file mode 100644
index 00000000..46666975
--- /dev/null
+++ b/libshouldbeinlibc/termsize.c
@@ -0,0 +1,54 @@
+/* Function to try and deduce what size the terminal is
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/ioctl.h>
+
+/* Returns what we think is the size of the terminal attached to
+ file descriptor FD, of type TYPE, in WIDTH and/or HEIGHT. If FD is
+ negative, the terminal isn't queried, and if TYPE is NULL, it isn't used.
+ Both WIDTH and HEIGHT may be NULL if only partial information is needed.
+ True is returned upon success. Even if false is returned, both output
+ values are still written, with 0 for unknown, in case partial information
+ is useful. */
+int
+deduce_term_size (int fd, char *type, int *width, int *height)
+{
+ int w = 0, h = 0;
+ struct winsize ws;
+
+ if (fd >= 0 && ioctl (fd, TIOCGWINSZ, &ws) == 0)
+ /* Look at the actual terminal. */
+ {
+ w = ws.ws_col;
+ h = ws.ws_row;
+ }
+ if (((width && !w) || (height && !h)) && type)
+ /* Try the terminal type. */
+ {
+ /* XXX */
+ }
+
+ if (width)
+ *width = w;
+ if (height)
+ *height = h;
+
+ return (!width || w) && (!height && h);
+}
diff --git a/libshouldbeinlibc/timefmt.c b/libshouldbeinlibc/timefmt.c
new file mode 100644
index 00000000..a28f58bd
--- /dev/null
+++ b/libshouldbeinlibc/timefmt.c
@@ -0,0 +1,359 @@
+/* Routines for formatting time
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "timefmt.h"
+
+#define SECOND 1
+#define MINUTE 60
+#define HOUR (60*MINUTE)
+#define DAY (24*HOUR)
+#define WEEK (7*DAY)
+#define MONTH (31*DAY) /* Not strictly accurate, but oh well. */
+#define YEAR (365*DAY) /* ditto */
+
+/* Returns the number of digits in the integer N. */
+static unsigned
+int_len (unsigned n)
+{
+ unsigned len = 1;
+ while (n >= 10)
+ {
+ n /= 10;
+ len++;
+ }
+ return len;
+}
+
+/* Returns TV1 divided by TV2. */
+static unsigned
+tv_div (struct timeval *tv1, struct timeval *tv2)
+{
+ return
+ tv2->tv_sec
+ ? tv1->tv_sec / tv2->tv_sec
+ : (tv1->tv_usec / tv2->tv_usec
+ + (tv1->tv_sec ? tv1->tv_sec * 1000000 / tv2->tv_usec : 0));
+}
+
+/* Returns true if TV is zero. */
+static inline int
+tv_is_zero (struct timeval *tv)
+{
+ return tv->tv_sec == 0 && tv->tv_usec == 0;
+}
+
+/* Returns true if TV1 >= TV2. */
+static inline int
+tv_is_ge (struct timeval *tv1, struct timeval *tv2)
+{
+ return
+ tv1->tv_sec > tv2->tv_sec
+ || (tv1->tv_sec == tv2->tv_sec && tv1->tv_usec >= tv2->tv_usec);
+}
+
+/* Format into BUF & BUF_LEN the time interval represented by TV, trying to
+ make the result less than WIDTH characters wide. The number of characters
+ used is returned. */
+size_t
+fmt_named_interval (struct timeval *tv, size_t width,
+ char *buf, size_t buf_len)
+{
+ struct tscale
+ {
+ struct timeval thresh; /* Minimum time to use this scale. */
+ struct timeval unit; /* Unit this scale is based on. */
+ struct timeval frac_thresh; /* If a emitting a single digit of precision
+ will cause at least this much error, also
+ emit a single fraction digit. */
+ char *sfxs[5]; /* Names to use, in descending length. */
+ }
+ time_scales[] =
+ {
+ {{2*YEAR, 0}, {YEAR, 0}, {MONTH, 0},{" years", "years", "yrs", "y", 0 }},
+ {{3*MONTH, 0}, {MONTH, 0}, {WEEK, 0}, {" months","months","mo", 0 }},
+ {{2*WEEK, 0}, {WEEK, 0}, {DAY, 0}, {" weeks", "weeks", "wks", "w", 0 }},
+ {{2*DAY, 0}, {DAY, 0}, {HOUR, 0}, {" days", "days", "dys", "d", 0 }},
+ {{2*HOUR, 0}, {HOUR, 0}, {MINUTE, 0},{" hours","hours", "hrs", "h", 0 }},
+ {{2*MINUTE, 0},{MINUTE, 0},{1, 0}, {" minutes","min", "mi", "m", 0 }},
+ {{1, 100000}, {1, 0}, {0, 100000},{" seconds", "sec", "s", 0 }},
+ {{1, 0}, {1, 0}, {0, 0}, {" second", "sec", "s", 0 }},
+ {{0, 1100}, {0, 1000}, {0, 100}, {" milliseconds", "ms", 0 }},
+ {{0, 1000}, {0, 1000}, {0, 0}, {" millisecond", "ms", 0 }},
+ {{0, 2}, {0, 1}, {0, 0}, {" microseconds", "us", 0 }},
+ {{0, 1}, {0, 1}, {0, 0}, {" microsecond", "us", 0 }},
+ {{0, 0} }
+ };
+ struct tscale *ts = time_scales;
+
+ if (width <= 0 || width >= buf_len)
+ width = buf_len - 1;
+
+ for (ts = time_scales; !tv_is_zero (&ts->thresh); ts++)
+ if (tv_is_ge (tv, &ts->thresh))
+ {
+ char **sfx;
+ struct timeval *u = &ts->unit;
+ unsigned num = tv_div (tv, u);
+ unsigned frac = 0;
+ unsigned num_len = int_len (num);
+
+ if (num < 10
+ && !tv_is_zero (&ts->frac_thresh)
+ && tv_is_ge (tv, &ts->frac_thresh))
+ /* Calculate another place of prec, but only for low numbers. */
+ {
+ /* TV times 10. */
+ struct timeval tv10 =
+ { tv->tv_sec * 10 + tv->tv_usec / 100000,
+ (tv->tv_usec % 100000) * 10 };
+ frac = tv_div (&tv10, u) - num * 10;
+ if (frac)
+ num_len += 2; /* Account for the extra `.' + DIGIT. */
+ }
+
+ /* While we have a choice, find a suffix that fits in WIDTH. */
+ for (sfx = ts->sfxs; sfx[1]; sfx++)
+ if (num_len + strlen (*sfx) <= width)
+ break;
+
+ if (!sfx[1] && frac)
+ /* We couldn't find a suffix that fits, and we're printing a
+ fraction digit. Sacrifice the fraction to make it fit. */
+ {
+ num_len -= 2;
+ frac = 0;
+ for (sfx = ts->sfxs; sfx[1]; sfx++)
+ if (num_len + strlen (*sfx) <= width)
+ break;
+ }
+
+ if (!sfx[1])
+ /* Still couldn't find a suffix that fits. Oh well, use the best. */
+ sfx--;
+
+ if (frac)
+ return snprintf (buf, buf_len, "%d.%d%s", num, frac, *sfx);
+ else
+ return snprintf (buf, buf_len, "%d%s", num, *sfx);
+ }
+
+ return sprintf (buf, "0"); /* Whatever */
+}
+
+/* Prints the number of units of size UNIT in *SECS, subtracting them from
+ *SECS, to BUF (the result *must* fit!), followed by SUFFIX; if the number
+ of units is zero, however, and *LEADING_ZEROS is false, print nothing (and
+ if something *is* printed, set *LEADING_ZEROS to true). MIN_WIDTH is the
+ minimum *total width* (including other fields) needed to print these
+ units. WIDTH is the amount of (total) space available. The number of
+ characters printed is returned. */
+static size_t
+add_field (int *secs, int unit, int *leading_zeros,
+ size_t min_width, char *suffix,
+ size_t width, char *buf)
+{
+ int units = *secs / unit;
+ if (units || (width >= min_width && *leading_zeros))
+ {
+ *secs -= units * unit;
+ *leading_zeros = 1;
+ return
+ sprintf (buf,
+ (width == min_width ? "%d%s"
+ : width == min_width + 1 ? "%2d%s"
+ : "%02d%s"),
+ units, suffix);
+ }
+ else
+ return 0;
+}
+
+/* Format into BUF & BUF_LEN the time interval represented by TV, using
+ HH:MM:SS notation where possible, with FRAC_PLACES digits after the
+ decimal point, and trying to make the result less than WIDTH characters
+ wide. If LEADING_ZEROS is true, then any fields that are zero-valued, but
+ would fit in the given width are printed. If FRAC_PLACES is negative,
+ then any space remaining after printing the time, up to WIDTH, is used for
+ the fraction. The number of characters used is returned. */
+size_t
+fmt_seconds (struct timeval *tv, int leading_zeros, int frac_places,
+ size_t width, char *buf, size_t buf_len)
+{
+ char *p = buf;
+ int secs = tv->tv_sec;
+
+ if (width <= 0 || width >= buf_len)
+ width = buf_len - 1;
+
+ if (tv->tv_sec > DAY)
+ return fmt_named_interval (tv, width, buf, buf_len);
+
+ if (frac_places > 0)
+ width -= frac_places + 1;
+
+ /* See if this time won't fit at all in fixed format. */
+ if ((secs > 10*HOUR && width < 8)
+ || (secs > HOUR && width < 7)
+ || (secs > 10*MINUTE && width < 5)
+ || (secs > MINUTE && width < 4)
+ || (secs > 10 && width < 2)
+ || width < 1)
+ return fmt_named_interval (tv, width, buf, buf_len);
+
+ p += add_field (&secs, HOUR, &leading_zeros, 7, ":", width, p);
+ p += add_field (&secs, MINUTE, &leading_zeros, 4, ":", width, p);
+ p += add_field (&secs, SECOND, &leading_zeros, 1, "", width, p);
+
+ if (frac_places < 0 && (p - buf) < (int) width - 2)
+ /* If FRAC_PLACES is < 0, then use any space remaining before WIDTH. */
+ frac_places = width - (p - buf) - 1;
+
+ if (frac_places > 0)
+ /* Print fractions of a second. */
+ {
+ int frac = tv->tv_usec, i;
+ for (i = 6; i > frac_places; i--)
+ frac /= 10;
+ return (p - buf) + sprintf (p, ".%0*d", frac_places, frac);
+ }
+ else
+ return (p - buf);
+}
+
+/* Format into BUF & BUF_LEN the time interval represented by TV, using HH:MM
+ notation where possible, and trying to make the result less than WIDTH
+ characters wide. If LEADING_ZEROS is true, then any fields that are
+ zero-valued, but would fit in the given width are printed. The number of
+ characters used is returned. */
+size_t
+fmt_minutes (struct timeval *tv, int leading_zeros,
+ size_t width, char *buf, size_t buf_len)
+{
+ char *p = buf;
+ int secs = tv->tv_sec;
+
+ if (width <= 0 || width >= buf_len)
+ width = buf_len - 1;
+
+ if (secs > DAY)
+ return fmt_named_interval (tv, width, buf, buf_len);
+
+ /* See if this time won't fit at all in fixed format. */
+ if ((secs > 10*HOUR && width < 5)
+ || (secs > HOUR && width < 4)
+ || (secs > 10*MINUTE && width < 2)
+ || width < 1)
+ return fmt_named_interval (tv, width, buf, buf_len);
+
+ p += add_field (&secs, HOUR, &leading_zeros, 4, ":", width, p);
+ p += add_field (&secs, MINUTE, &leading_zeros, 1, "", width, p);
+
+ return p - buf;
+}
+
+/* Format into BUF & BUF_LEN the absolute time represented by TV. An attempt
+ is made to fit the result in less than WIDTH characters, by omitting
+ fields implied by the current time, NOW (if NOW is 0, then no assumption
+ is made, so the resulting times will be somewhat long). The number of
+ characters used is returned. */
+size_t
+fmt_past_time (struct timeval *tv, struct timeval *now,
+ size_t width, char *buf, size_t buf_len)
+{
+ static char *time_fmts[] = { "%-r", "%-l:%M%p", "%-l%p", 0 };
+ static char *week_fmts[] = { "%A", "%a", 0 };
+ static char *month_fmts[] = { "%A %-d", "%a %-d", "%a%-d", 0 };
+ static char *date_fmts[] =
+ { "%A, %-d %B", "%a, %-d %b", "%-d %B", "%-d %b", "%-d%b", 0 };
+ static char *year_fmts[] =
+ { "%A, %-d %B %Y", "%a, %-d %b %Y", "%a, %-d %b %y", "%-d %b %y", "%-d%b%y", 0 };
+ struct tm tm;
+ int used = 0; /* Number of characters generated. */
+ long diff = now ? (now->tv_sec - tv->tv_sec) : tv->tv_sec;
+
+ if (diff < 0)
+ diff = -diff; /* XXX */
+
+ bcopy (localtime ((time_t *) &tv->tv_sec), &tm, sizeof tm);
+
+ if (width <= 0 || width >= buf_len)
+ width = buf_len - 1;
+
+ if (diff < DAY)
+ {
+ char **fmt;
+ for (fmt = time_fmts; *fmt && !used; fmt++)
+ used = strftime (buf, width + 1, *fmt, &tm);
+ if (! used)
+ /* Nothing worked, perhaps WIDTH is too small, but BUF_LEN will work.
+ We know FMT is one past the end the array, so FMT[-1] should be
+ the shortest possible option. */
+ used = strftime (buf, buf_len, fmt[-1], &tm);
+ }
+ else
+ {
+ static char *seps[] = { ", ", " ", "", 0 };
+ char **fmt, **dfmt, **dfmts, **sep;
+
+ if (diff < WEEK)
+ dfmts = week_fmts;
+ else if (diff < MONTH)
+ dfmts = month_fmts;
+ else if (diff < YEAR)
+ dfmts = date_fmts;
+ else
+ dfmts = year_fmts;
+
+ /* This ordering (date varying most quickly, then the separator, then
+ the time) preserves time detail as long as possible, and seems to
+ produce a graceful degradation of the result with decreasing widths. */
+ for (fmt = time_fmts; *fmt && !used; fmt++)
+ for (sep = seps; *sep && !used; sep++)
+ for (dfmt = dfmts; *dfmt && !used; dfmt++)
+ {
+ char whole_fmt[strlen (*dfmt) + strlen (*sep) + strlen (*fmt) + 1];
+ char *end = whole_fmt;
+
+ end = stpcpy (end, *dfmt);
+ end = stpcpy (end, *sep);
+ stpcpy (end, *fmt);
+
+ used = strftime (buf, width + 1, whole_fmt, &tm);
+ }
+
+ if (! used)
+ /* No concatenated formats worked, try just date formats. */
+ for (dfmt = dfmts; *dfmt && !used; dfmt++)
+ used = strftime (buf, width + 1, *dfmt, &tm);
+
+ if (! used)
+ /* Absolutely nothing has worked, perhaps WIDTH is too small, but
+ BUF_LEN will work. We know DFMT is one past the end the array, so
+ DFMT[-1] should be the shortest possible option. */
+ used = strftime (buf, buf_len, dfmt[-1], &tm);
+ }
+
+ return used;
+}
diff --git a/libshouldbeinlibc/timefmt.h b/libshouldbeinlibc/timefmt.h
new file mode 100644
index 00000000..134cf56a
--- /dev/null
+++ b/libshouldbeinlibc/timefmt.h
@@ -0,0 +1,58 @@
+/* Routines for formatting time
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __TIMEFMT_H__
+#define __TIMEFMT_H__
+
+struct timeval;
+
+/* Format into BUF & BUF_LEN the time interval represented by TV, trying to
+ make the result less than WIDTH characters wide. The number of characters
+ used is returned. */
+size_t fmt_named_interval (struct timeval *tv, size_t width,
+ char *buf, size_t buf_len);
+
+/* Format into BUF & BUF_LEN the time interval represented by TV, using
+ HH:MM:SS notation where possible, with FRAC_PLACES digits after the
+ decimal point, and trying to make the result less than WIDTH characters
+ wide. If LEADING_ZEROS is true, then any fields that are zero-valued, but
+ would fit in the given width are printed. If FRAC_PLACES is negative,
+ then any space remaining after printing the time, up to WIDTH, is used for
+ the fraction. The number of characters used is returned. */
+size_t fmt_seconds (struct timeval *tv, int leading_zeros, int frac_places,
+ size_t width, char *buf, size_t buf_len);
+
+/* Format into BUF & BUF_LEN the time interval represented by TV, using HH:MM
+ notation where possible, and trying to make the result less than WIDTH
+ characters wide. If LEADING_ZEROS is true, then any fields that are
+ zero-valued, but would fit in the given width are printed. The number of
+ characters used is returned. */
+size_t fmt_minutes (struct timeval *tv, int leading_zeros,
+ size_t width, char *buf, size_t buf_len);
+
+/* Format into BUF & BUF_LEN the absolute time represented by TV. An attempt
+ is made to fit the result in less than WIDTH characters, by omitting
+ fields implied by the current time, NOW (if NOW is 0, then no assumptions
+ are made, so the resulting times will be somewhat long). The number of
+ characters used is returned. */
+size_t fmt_past_time (struct timeval *tv, struct timeval *now,
+ size_t width, char *buf, size_t buf_len);
+
+#endif /* __TIMEFMT_H__ */
diff --git a/libshouldbeinlibc/ugids-argp.c b/libshouldbeinlibc/ugids-argp.c
new file mode 100644
index 00000000..43a54d70
--- /dev/null
+++ b/libshouldbeinlibc/ugids-argp.c
@@ -0,0 +1,174 @@
+/* Parse user and group ids
+
+ Copyright (C) 1997, 1999, 2008 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <argp.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+
+#include "ugids.h"
+
+#define OA OPTION_ARG_OPTIONAL
+
+static const struct argp_option options[] =
+{
+ {"user", 'u', "USER", 0, "Add USER to the effective uids"},
+ {"avail-user",'U', "USER", 0, "Add USER to the available uids"},
+ {"group", 'g', "GROUP", 0, "Add GROUP to the effective groups"},
+ {"avail-group",'G',"GROUP", 0, "Add GROUP to the available groups"},
+ { 0 }
+};
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ char id_lookup_buf[1024];
+ struct ugids_argp_params *params = state->input;
+ struct ugids *ugids = params->ugids;
+
+ switch (key)
+ {
+ uid_t uid;
+
+ case 'u':
+ case 'U':
+ case ARGP_KEY_ARG:
+ case ARGP_KEY_END:
+ if (key == ARGP_KEY_ARG && !params->parse_user_args)
+ /* Let someone else parse this argument. */
+ return ARGP_ERR_UNKNOWN;
+
+ if (key == ARGP_KEY_END)
+ {
+ if (ugids_is_empty (ugids))
+ {
+ if (params->default_user >= 0)
+ uid = params->default_user;
+ else if (params->require_ids)
+ {
+ argp_error (state, "No ids specified");
+ return EINVAL;
+ }
+ else
+ break;
+ }
+ else
+ break;
+ }
+ else if (isdigit (*arg))
+ uid = atoi (arg);
+ else if (strcmp (arg, "-") == 0)
+ break;
+ else
+ {
+ struct passwd _pw, *pw;
+ int err;
+ err = getpwnam_r (arg, &_pw, id_lookup_buf,
+ sizeof id_lookup_buf, &pw);
+ if (err == 0)
+ {
+ if (pw == NULL)
+ {
+ argp_failure (state, 10, 0, "%s: Unknown user", arg);
+ return EINVAL;
+ }
+
+ uid = pw->pw_uid;
+ }
+ else
+ {
+ argp_failure (state, 12, err,
+ "Could not get uid for user: %s", arg);
+ return err;
+ }
+ }
+
+ if (key == ARGP_KEY_ARG || key == ARGP_KEY_END)
+ {
+ /* A user arg, which means add the user, and any appropriate
+ groups. */
+ if (!params->user_args_are_effective
+ && !params->user_args_are_available)
+ return ugids_set_posix_user (ugids, uid);
+ else
+ {
+ error_t err = 0;
+ if (params->user_args_are_effective)
+ err = ugids_add_user (ugids, uid, 0);
+ if (!err && params->user_args_are_available)
+ err = ugids_add_user (ugids, uid, 1);
+ return err;
+ }
+ }
+ else
+ /* Add an individual specific effective/auxiliary uid. */
+ return ugids_add_uid (ugids, uid, key == 'U');
+
+ case 'g':
+ case 'G':
+ if (isdigit (*arg))
+ return ugids_add_gid (ugids, atoi (arg), key == 'G');
+ else
+ {
+ struct group _gr, *gr;
+ int err = getgrnam_r (arg, &_gr, id_lookup_buf,
+ sizeof id_lookup_buf, &gr);
+ if (err == 0)
+ {
+ if (gr == NULL)
+ {
+ argp_failure (state, 11, 0, "%s: Unknown group", arg);
+ return EINVAL;
+ }
+
+ return ugids_add_gid (ugids, gr->gr_gid, key == 'G');
+ }
+ else
+ {
+ argp_failure (state, 13, err,
+ "Could not get gid for group: %s", arg);
+ return err;
+ }
+ }
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Filtering of help output strings for UGIDS_ARGP. */
+static char *
+help_filter (int key, const char *text, void *input)
+{
+ struct ugids_argp_params *params = input;
+
+ /* Describe the optional behavior of parsing normal args as ugids. */
+ if (key == ARGP_KEY_HELP_ARGS_DOC && params->parse_user_args)
+ return strdup ("[USER...]");
+
+ return (char *)text;
+}
+
+/* A parser for selecting a set of ugids. */
+struct argp ugids_argp = { options, parse_opt, 0, 0, 0, help_filter };
diff --git a/libshouldbeinlibc/ugids-auth.c b/libshouldbeinlibc/ugids-auth.c
new file mode 100644
index 00000000..0e4f84dc
--- /dev/null
+++ b/libshouldbeinlibc/ugids-auth.c
@@ -0,0 +1,53 @@
+/* Translate user and group ids to/from auth ports
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+
+#include "idvec.h"
+#include "ugids.h"
+
+/* Make an auth port from UGIDS and return it in AUTH, using authority in
+ both the auth port FROM and the current auth port. */
+error_t
+ugids_make_auth (const struct ugids *ugids,
+ const auth_t *from, size_t num_from,
+ auth_t *auth)
+{
+ auth_t cur_auth = getauth ();
+ error_t err =
+ auth_makeauth (cur_auth, (auth_t *)from, MACH_MSG_TYPE_COPY_SEND, num_from,
+ ugids->eff_uids.ids, ugids->eff_uids.num,
+ ugids->avail_uids.ids, ugids->avail_uids.num,
+ ugids->eff_gids.ids, ugids->eff_gids.num,
+ ugids->avail_gids.ids, ugids->avail_gids.num,
+ auth);
+ mach_port_deallocate (mach_task_self (), cur_auth);
+ return err;
+}
+
+/* Merge the ids from the auth port AUTH into UGIDS. */
+error_t
+ugids_merge_auth (struct ugids *ugids, auth_t auth)
+{
+ return
+ idvec_merge_auth (&ugids->eff_uids, &ugids->avail_uids,
+ &ugids->eff_gids, &ugids->avail_gids,
+ auth);
+}
diff --git a/libshouldbeinlibc/ugids-imply.c b/libshouldbeinlibc/ugids-imply.c
new file mode 100644
index 00000000..272ba664
--- /dev/null
+++ b/libshouldbeinlibc/ugids-imply.c
@@ -0,0 +1,36 @@
+/* Calculate implied group ids from user ids
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+
+#include "idvec.h"
+#include "ugids.h"
+
+/* Mark as implied all gids in UGIDS that can be implied from its uids. */
+error_t
+ugids_imply_all (struct ugids *ugids)
+{
+ error_t err;
+ err = idvec_merge_implied_gids (&ugids->imp_eff_gids, &ugids->eff_uids);
+ if (! err)
+ err =
+ idvec_merge_implied_gids (&ugids->imp_avail_gids, &ugids->avail_uids);
+ return err;
+}
diff --git a/libshouldbeinlibc/ugids-merge.c b/libshouldbeinlibc/ugids-merge.c
new file mode 100644
index 00000000..f97da07c
--- /dev/null
+++ b/libshouldbeinlibc/ugids-merge.c
@@ -0,0 +1,110 @@
+/* Merging of ugids
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+
+#include "idvec.h"
+#include "ugids.h"
+
+static error_t
+_merge_gids (struct idvec *gids, struct idvec *gids_imp,
+ const struct idvec *new, const struct idvec *new_imp)
+{
+ error_t err;
+ /* Gids that exist in both GIDS and NEW can only be implied in the result
+ if they are implied in both; here GIDS_STRONG and NEW_STRONG contain
+ those gids which shouldn't be implied in the result because they are not
+ in either of the sources. */
+ struct idvec gids_strong = IDVEC_INIT;
+ struct idvec new_strong = IDVEC_INIT;
+
+ err = idvec_set (&gids_strong, gids);
+ if (! err)
+ err = idvec_set (&new_strong, new);
+ if (! err)
+ {
+ idvec_subtract (&gids_strong, gids_imp);
+ idvec_subtract (&new_strong, new_imp);
+
+ err = idvec_merge (gids, new);
+ if (! err)
+ {
+ err = idvec_merge (gids_imp, new_imp);
+ if (! err)
+ {
+ idvec_subtract (gids_imp, &gids_strong);
+ idvec_subtract (gids_imp, &new_strong);
+ }
+ }
+ }
+
+ idvec_fini (&gids_strong);
+ idvec_fini (&new_strong);
+
+ return err;
+}
+
+/* Add all ids in NEW to UGIDS. */
+error_t
+ugids_merge (struct ugids *ugids, const struct ugids *new)
+{
+ error_t err;
+ err = idvec_merge (&ugids->eff_uids, &new->eff_uids);
+ if (! err)
+ err = idvec_merge (&ugids->avail_uids, &new->avail_uids);
+ if (! err)
+ err = _merge_gids (&ugids->eff_gids, &ugids->imp_eff_gids,
+ &new->eff_gids, &new->imp_eff_gids);
+ if (! err)
+ err = _merge_gids (&ugids->avail_gids, &ugids->imp_avail_gids,
+ &new->avail_gids, &new->imp_avail_gids);
+ return err;
+}
+
+/* Set the ids in UGIDS to those in NEW. */
+error_t
+ugids_set (struct ugids *ugids, const struct ugids *new)
+{
+ idvec_clear (&ugids->eff_uids);
+ idvec_clear (&ugids->eff_gids);
+ idvec_clear (&ugids->avail_uids);
+ idvec_clear (&ugids->avail_gids);
+ idvec_clear (&ugids->imp_eff_gids);
+ idvec_clear (&ugids->imp_avail_gids);
+ return ugids_merge (ugids, new);
+}
+
+/* Save any effective ids in UGIDS by merging them into the available ids,
+ and removing them from the effective ones. */
+error_t
+ugids_save (struct ugids *ugids)
+{
+ error_t err = idvec_merge (&ugids->avail_uids, &ugids->eff_uids);
+ if (! err)
+ err = _merge_gids (&ugids->avail_gids, &ugids->imp_avail_gids,
+ &ugids->eff_gids, &ugids->imp_eff_gids);
+ if (! err)
+ {
+ idvec_clear (&ugids->eff_uids);
+ idvec_clear (&ugids->eff_gids);
+ idvec_clear (&ugids->imp_eff_gids);
+ }
+ return err;
+}
diff --git a/libshouldbeinlibc/ugids-posix.c b/libshouldbeinlibc/ugids-posix.c
new file mode 100644
index 00000000..35d73e32
--- /dev/null
+++ b/libshouldbeinlibc/ugids-posix.c
@@ -0,0 +1,95 @@
+/* Set posix-compatible ugids
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+
+#include "ugids.h"
+
+/* Install UID into UGIDS as the main user, making sure that the posix
+ `real' and `saved' uid slots are filled in, and similarly add all
+ groups to which UID belongs. */
+error_t
+ugids_set_posix_user (struct ugids *ugids, uid_t uid)
+{
+ error_t err;
+ struct idvec imp_gids = IDVEC_INIT;
+ uid_t uids_ids[] = { uid };
+ struct idvec uids = { uids_ids, 1 };
+
+ error_t update_real (struct idvec *avail_ids, uid_t id)
+ {
+ if (avail_ids->num == 0
+ || !idvec_tail_contains (avail_ids, 1, avail_ids->ids[0]))
+ return idvec_insert (avail_ids, 0, id);
+ else
+ avail_ids->ids[0] = id;
+ return 0;
+ }
+
+ idvec_merge_implied_gids (&imp_gids, &uids);
+
+ /* Try to add UID. */
+ err = idvec_insert_only (&ugids->eff_uids, 0, uid); /* Effective */
+ if (! err)
+ err = update_real (&ugids->avail_uids, uid); /* Real */
+ if (! err)
+ err = idvec_insert_only (&ugids->avail_uids, 1, uid); /* Saved */
+
+ if (!err && imp_gids.num > 0)
+ /* Now do the gids. */
+ {
+ /* The main gid associated with UID (usually from /etc/passwd). */
+ gid_t gid = imp_gids.ids[0];
+ /* True if GID was already an available gid. */
+ int gid_was_avail = idvec_contains (&ugids->avail_gids, gid);
+
+ /* Update the implied sets for the gids: they're implied unless
+ they were present as non-implied gids before. Here we
+ remove existing effective gids from the IMP_GIDS before we
+ added it to the implied sets -- if some of those gids were
+ actually implied, they'll already be present in the implied
+ set. */
+ idvec_subtract (&imp_gids, &ugids->eff_gids);
+
+ /* Now add GID, as effective, real, and saved gids. */
+ if (! err) /* Effective */
+ err = idvec_insert_only (&ugids->eff_gids, 0, gid);
+ if (! err) /* Real */
+ err = update_real (&ugids->avail_gids, gid);
+ if (! err) /* Saved */
+ err = idvec_insert_only (&ugids->avail_gids, 1, gid);
+
+ /* Mark GID as implied in the available gids unless it was already
+ present (in which case its implied status is already settled). */
+ if (!err && !gid_was_avail)
+ err = idvec_add (&ugids->imp_avail_gids, gid);
+
+ /* Add the other implied gids to the end of the effective gids. */
+ if (! err)
+ err = idvec_merge (&ugids->eff_gids, &imp_gids);
+ /* And make them implied. */
+ if (! err)
+ err = idvec_merge (&ugids->imp_eff_gids, &imp_gids);
+ }
+
+ idvec_fini (&imp_gids);
+
+ return err;
+}
diff --git a/libshouldbeinlibc/ugids-rep.c b/libshouldbeinlibc/ugids-rep.c
new file mode 100644
index 00000000..3e6e59d5
--- /dev/null
+++ b/libshouldbeinlibc/ugids-rep.c
@@ -0,0 +1,118 @@
+/* String representation of ugids
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "ugids.h"
+
+/* Return a string representation of the ids in UGIDS. SHOW_VALUES and
+ SHOW_NAMES reflect how each id is printed (if SHOW_NAMES is true values
+ are used where names aren't available); if both are true, the
+ `VALUE(NAME)' format is used. ID_SEP, TYPE_SEP, and HDR_SEP contain the
+ strings that separate, respectively, multiple ids of a particular type
+ (default ","), the various types of ids (default ", "), and the name of
+ each type from its ids (default ": "). The empty string is returned for
+ an empty list, and 0 for an allocation error. */
+char *
+ugids_rep (const struct ugids *ugids, int show_values, int show_names,
+ const char *id_sep, const char *type_sep, const char *hdr_sep)
+{
+ size_t type_sep_len, hdr_sep_len;
+ int first = 1;
+ char *rep = 0; /* Result */
+ size_t len = 0; /* Total length of result. */
+ char *euid_rep = 0, *egid_rep = 0, *auid_rep = 0, *agid_rep = 0;
+
+ /* Calculate the rep for NAME, with ids IDS, returning the rep for the ids
+ in REP, and updates LEN to include everything needed by this type (the
+ length of *REP *plus* the length of NAME and any separators). True is
+ returned unless an allocation error occurs. */
+ int type_rep (const char *name, const struct idvec *ids, int is_group,
+ char **rep)
+ {
+ if (ids->num > 0)
+ {
+ if (first)
+ first = 0;
+ else
+ len += type_sep_len;
+ len += strlen (name);
+ len += hdr_sep_len;
+ *rep =
+ (is_group ? idvec_gids_rep : idvec_uids_rep)
+ (ids, show_values, show_names, id_sep);
+ if (*rep)
+ len += strlen (*rep);
+ else
+ return 0;
+ }
+ return 1;
+ }
+ void add_type_rep (char **to, const char *name, const char *rep)
+ {
+ if (rep)
+ {
+ if (first)
+ first = 0;
+ else
+ *to = stpcpy (*to, type_sep);
+ *to = stpcpy (*to, name);
+ *to = stpcpy (*to, hdr_sep);
+ *to = stpcpy (*to, rep);
+ }
+ }
+
+ if (! type_sep)
+ type_sep = ", ";
+ if (! hdr_sep)
+ hdr_sep = ": ";
+
+ type_sep_len = strlen (type_sep);
+ hdr_sep_len = strlen (hdr_sep);
+
+ if (type_rep ("euids", &ugids->eff_uids, 0, &euid_rep)
+ && type_rep ("egids", &ugids->eff_gids, 1, &egid_rep)
+ && type_rep ("auids", &ugids->avail_uids, 0, &auid_rep)
+ && type_rep ("agids", &ugids->avail_gids, 1, &agid_rep))
+ {
+ char *p = malloc (len + 1);
+ if (p)
+ {
+ rep = p;
+ first = 1;
+ add_type_rep (&p, "euids", euid_rep);
+ add_type_rep (&p, "egids", egid_rep);
+ add_type_rep (&p, "auids", auid_rep);
+ add_type_rep (&p, "agids", agid_rep);
+ }
+ }
+
+ if (euid_rep)
+ free (euid_rep);
+ if (egid_rep)
+ free (egid_rep);
+ if (auid_rep)
+ free (auid_rep);
+ if (agid_rep)
+ free (agid_rep);
+
+ return rep;
+}
diff --git a/libshouldbeinlibc/ugids-subtract.c b/libshouldbeinlibc/ugids-subtract.c
new file mode 100644
index 00000000..b56e397b
--- /dev/null
+++ b/libshouldbeinlibc/ugids-subtract.c
@@ -0,0 +1,130 @@
+/* Subtract one set of user and group ids from another
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+
+#include "idvec.h"
+#include "ugids.h"
+
+/* Remove the gids in SUB from those in GIDS, except where they are implied
+ in SUB (as represented by SUB_IMP), but not in GIDS (as represented by
+ GIDS_IMP). */
+static
+error_t _sub_gids (struct idvec *gids, struct idvec *gids_imp,
+ const struct idvec *sub, const struct idvec *sub_imp)
+{
+ error_t err;
+ /* What we'll remove from GIDS. */
+ struct idvec delta = IDVEC_INIT;
+ /* Those implied ids in SUB that we *won't* remove, because they're not
+ also implied in GIDS. */
+ struct idvec delta_suppress = IDVEC_INIT;
+
+ err = idvec_set (&delta, sub);
+ if (! err)
+ err = idvec_set (&delta_suppress, sub_imp);
+ if (! err)
+ {
+ /* Don't suppress those implied ids that are implied in both. */
+ idvec_subtract (&delta_suppress, gids_imp);
+ idvec_subtract (&delta, &delta_suppress);
+
+ /* Actually remove the gids. */
+ idvec_subtract (gids, &delta);
+ }
+
+ idvec_fini (&delta);
+ idvec_fini (&delta_suppress);
+
+ return err;
+}
+
+/* Remove the in SUB from those in GIDS, except where they are implied
+ in SUB (as represented by SUB_IMP), but not in GIDS (as represented by
+ GIDS_IMP). */
+static
+error_t _sub (struct idvec *uids, struct idvec *gids, struct idvec *gids_imp,
+ const struct idvec *sub_uids,
+ const struct idvec *sub_gids, const struct idvec *sub_gids_imp)
+{
+ error_t err;
+ struct idvec new_uids = IDVEC_INIT; /* The set of uids after subtraction. */
+ struct idvec no_sub_gids = IDVEC_INIT; /* Gids we *don't* want to remove
+ from GIDS, despite what's in
+ SUB_GIDS. */
+ struct idvec new_sub_gids = IDVEC_INIT;
+ struct idvec new_sub_gids_imp = IDVEC_INIT;
+
+ err = idvec_set (&new_uids, uids);
+ if (! err)
+ err = idvec_set (&new_sub_gids, sub_gids);
+ if (! err)
+ err = idvec_set (&new_sub_gids_imp, sub_gids_imp);
+ if (! err)
+ {
+ idvec_subtract (&new_uids, sub_uids);
+
+ err = idvec_merge_implied_gids (&no_sub_gids, &new_uids);
+ if (! err)
+ {
+ /* NO_SUB_GIDS is the intersection of implied gids in GIDS,
+ implied gids in SUB_GIDS, and implied gids after the subtraction
+ of uids -- we don't want to remove those implied gids because we
+ can't be sure which uids implied them (as there will be
+ appropriately implicative uids left after the subtraction). */
+ idvec_keep (&no_sub_gids, gids_imp);
+ idvec_keep (&no_sub_gids, sub_gids_imp);
+
+ /* Remove those gids we don't want to subtract. */
+ idvec_subtract (&new_sub_gids, &no_sub_gids);
+ idvec_subtract (&new_sub_gids_imp, &no_sub_gids);
+
+ /* Do the group subtraction. */
+ err = _sub_gids (gids, gids_imp, &new_sub_gids, &new_sub_gids_imp);
+ if (! err)
+ /* Finally, if no problems, do the uid subtraction. */
+ err = idvec_set (uids, &new_uids);
+ }
+ }
+
+ idvec_fini (&new_uids);
+ idvec_fini (&no_sub_gids);
+ idvec_fini (&new_sub_gids);
+ idvec_fini (&new_sub_gids_imp);
+
+ return err;
+}
+
+/* Remove the ids in SUB from those in UGIDS. */
+error_t
+ugids_subtract (struct ugids *ugids, const struct ugids *sub)
+{
+ error_t err =
+ _sub (&ugids->eff_uids, &ugids->eff_gids, &ugids->imp_eff_gids,
+ &sub->eff_uids, &sub->eff_gids, &sub->imp_eff_gids);
+
+ if (! err)
+ /* If this second call to _sub fails, ugids will be in an inconsistent
+ state, but oh well. */
+ err = _sub (&ugids->avail_uids, &ugids->avail_gids, &ugids->imp_avail_gids,
+ &sub->avail_uids, &sub->avail_gids, &sub->imp_avail_gids);
+
+ return err;
+}
diff --git a/libshouldbeinlibc/ugids-verify-auth.c b/libshouldbeinlibc/ugids-verify-auth.c
new file mode 100644
index 00000000..0e85b1b6
--- /dev/null
+++ b/libshouldbeinlibc/ugids-verify-auth.c
@@ -0,0 +1,185 @@
+/* Verify user/group passwords and authenticate accordingly
+
+ Copyright (C) 1997, 1998 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdlib.h>
+#include <hurd.h>
+
+#include <hurd/paths.h>
+#include <hurd/password.h>
+
+#include "idvec.h"
+#include "ugids.h"
+
+/* Accumulated information from authentication various passwords. */
+struct svma_state
+{
+ /* The password server. */
+ file_t server;
+
+ /* An auth port for each password that was verify by the server. */
+ auth_t *auths;
+ size_t num_auths;
+};
+
+/* Append the auth ports in AUTHS, of length NUM_AUTHS, to the auth port
+ vector in SS, returning 0 if successful, or an error. */
+static error_t
+svma_state_add_auths (struct svma_state *ss,
+ const auth_t *auths, size_t num_auths)
+{
+ auth_t *new = realloc (ss->auths,
+ (ss->num_auths + num_auths) * sizeof (auth_t));
+ if (new)
+ {
+ ss->auths = new;
+ while (num_auths--)
+ ss->auths[ss->num_auths++] = *auths++;
+ return 0;
+ }
+ else
+ return ENOMEM;
+}
+
+/* Get authentication from PASSWORD using the hurd password server. */
+static error_t
+server_verify_make_auth (const char *password,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook)
+{
+ auth_t auth;
+ struct svma_state *svma_state = hook;
+ error_t (*check) (io_t server, uid_t id, const char *passwd, auth_t *auth) =
+ is_group ? password_check_group : password_check_user;
+ error_t err = (*check) (svma_state->server, id, password, &auth);
+
+ if (! err)
+ /* PASSWORD checked out ok; the corresponding authentication is in AUTH. */
+ {
+ err = svma_state_add_auths (svma_state, &auth, 1);
+ if (err)
+ mach_port_deallocate (mach_task_self (), auth);
+ }
+
+ return err;
+}
+
+/* Verify that we have the right to the ids in UGIDS, given that we already
+ possess those in HAVE_UIDS and HAVE_GIDS (asking for passwords where
+ necessary), and return corresponding authentication in AUTH; the auth
+ ports in FROM, of length NUM_FROM, are used to supplement the auth port of
+ the current process if necessary. 0 is returned if access should be
+ allowed, otherwise EINVAL if an incorrect password was entered, or an
+ error relating to resource failure. GETPASS_FN and GETPASS_HOOK are as
+ for the idvec_verify function in <idvec.h>. */
+error_t
+ugids_verify_make_auth (const struct ugids *ugids,
+ const struct idvec *have_uids,
+ const struct idvec *have_gids,
+ char *(*getpass_fn) (const char *prompt,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook),
+ void *getpass_hook,
+ const auth_t *from, size_t num_from,
+ auth_t *auth)
+{
+ error_t err;
+ /* By default, get authentication from the password server. */
+ struct svma_state svma_state;
+ error_t (*verify_fn) (const char *password,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook)
+ = server_verify_make_auth;
+ void *verify_hook = &svma_state;
+
+ /* Try to open the hurd password server. */
+ svma_state.server = file_name_lookup (_SERVERS_PASSWORD, 0, 0);
+
+ if (svma_state.server == MACH_PORT_NULL)
+ /* Can't open the password server, try to use our own authority in
+ the traditional unix manner. */
+ {
+ verify_fn = 0;
+ verify_hook = 0;
+ }
+ else
+ {
+ /* Must initialize list to empty so svma_state_add_auths works. */
+ svma_state.auths = NULL;
+ svma_state.num_auths = 0;
+ }
+
+ /* Check passwords. */
+ err = ugids_verify (ugids, have_uids, have_gids,
+ getpass_fn, getpass_hook, verify_fn, verify_hook);
+
+ if (! err)
+ {
+ /* The user apparently has access to all the ids, try to grant the
+ corresponding authentication. */
+ if (verify_fn)
+ /* Merge the authentication we got from the password server into our
+ result. */
+ {
+ if (num_from > 0)
+ /* Use FROM as well as the passwords to get authentication. */
+ err = svma_state_add_auths (&svma_state, from, num_from);
+
+ if (! err)
+ {
+ auth_t cur_auth = getauth ();
+
+ err =
+ auth_makeauth (cur_auth,
+ svma_state.auths, MACH_MSG_TYPE_COPY_SEND,
+ svma_state.num_auths,
+ ugids->eff_uids.ids, ugids->eff_uids.num,
+ ugids->avail_uids.ids, ugids->avail_uids.num,
+ ugids->eff_gids.ids, ugids->eff_gids.num,
+ ugids->avail_gids.ids, ugids->avail_gids.num,
+ auth);
+ mach_port_deallocate (mach_task_self (), cur_auth);
+
+ /* Avoid deallocating FROM when we clean up SVMA_STATE. */
+ svma_state.num_auths -= num_from;
+ }
+ }
+ else
+ /* Try to authenticate the old fashioned way... */
+ err = ugids_make_auth (ugids, from, num_from, auth);
+ }
+
+ if (verify_fn)
+ /* Clean up any left over state. */
+ {
+ unsigned int i;
+
+ /* Get rid of auth ports. */
+ for (i = 0; i < svma_state.num_auths; i++)
+ mach_port_deallocate (mach_task_self (), svma_state.auths[i]);
+
+ /* Close password server. */
+ mach_port_deallocate (mach_task_self (), svma_state.server);
+
+ if (svma_state.num_auths > 0)
+ free (svma_state.auths);
+ }
+
+ return err;
+}
diff --git a/libshouldbeinlibc/ugids-verify.c b/libshouldbeinlibc/ugids-verify.c
new file mode 100644
index 00000000..5686bdf8
--- /dev/null
+++ b/libshouldbeinlibc/ugids-verify.c
@@ -0,0 +1,65 @@
+/* Verify user/group passwords
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <argp.h>
+
+#include "idvec.h"
+#include "ugids.h"
+
+/* Verify that we have the right to the ids in UGIDS, given that we already
+ possess those in HAVE_UIDS and HAVE_GIDS, asking for passwords where
+ necessary. 0 is returned if access should be allowed, otherwise
+ EINVAL if an incorrect password was entered, or an error relating to
+ resource failure. The GETPASS_FN, GETPASS_HOOK, VERIFY_FN, and
+ VERIFY_HOOK arguments are as for the idvec_verify function (in <idvec.h>). */
+error_t
+ugids_verify (const struct ugids *ugids,
+ const struct idvec *have_uids, const struct idvec *have_gids,
+ char *(*getpass_fn) (const char *prompt,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook),
+ void *getpass_hook,
+ error_t (*verify_fn) (const char *password,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook),
+ void *verify_hook)
+{
+ error_t err;
+ struct idvec check_uids = IDVEC_INIT; /* User-ids to verify. */
+ struct idvec check_gids = IDVEC_INIT; /* group-ids to verify. */
+
+ err = idvec_merge (&check_uids, &ugids->eff_uids);
+ if (! err)
+ err = idvec_merge (&check_uids, &ugids->avail_uids);
+ if (! err)
+ err = idvec_merge (&check_gids, &ugids->eff_gids);
+ if (! err)
+ err = idvec_merge (&check_gids, &ugids->avail_gids);
+
+ if (! err)
+ err = idvec_verify (&check_uids, &check_gids, have_uids, have_gids,
+ getpass_fn, getpass_hook, verify_fn, verify_hook);
+
+ idvec_fini (&check_uids);
+ idvec_fini (&check_gids);
+
+ return err;
+}
diff --git a/libshouldbeinlibc/ugids-xinl.c b/libshouldbeinlibc/ugids-xinl.c
new file mode 100644
index 00000000..107de8b9
--- /dev/null
+++ b/libshouldbeinlibc/ugids-xinl.c
@@ -0,0 +1,23 @@
+/* Real definitions for extern inline functions in ugids.h
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#define UGIDS_DEFINE_EI
+#include "ugids.h"
diff --git a/libshouldbeinlibc/ugids.c b/libshouldbeinlibc/ugids.c
new file mode 100644
index 00000000..db1ce3c8
--- /dev/null
+++ b/libshouldbeinlibc/ugids.c
@@ -0,0 +1,98 @@
+/* Frob user and group ids
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "idvec.h"
+#include "ugids.h"
+
+/* Return a new ugids structure, or 0 if an allocation error occurs. */
+struct ugids *
+make_ugids ()
+{
+ struct ugids *u = malloc (sizeof (struct ugids));
+ if (u)
+ bzero (u, sizeof *u);
+ return u;
+}
+
+/* Add a new uid to UGIDS. If AVAIL is true, it's added to the avail uids
+ instead of the effective ones. */
+error_t
+ugids_add_uid (struct ugids *ugids, uid_t uid, int avail)
+{
+ return idvec_add_new (avail ? &ugids->avail_uids : &ugids->eff_uids, uid);
+}
+
+/* Add a new gid to UGIDS. If AVAIL is true, it's added to the avail gids
+ instead of the effective ones. */
+error_t
+ugids_add_gid (struct ugids *ugids, gid_t gid, int avail)
+{
+ error_t err =
+ idvec_add_new (avail ? &ugids->avail_gids : &ugids->eff_gids, gid);
+ if (! err)
+ /* Since this gid is now explicit, remove it from the appropriate implied
+ set. */
+ idvec_remove (avail ? &ugids->imp_avail_gids : &ugids->imp_eff_gids,
+ 0, gid);
+ return err;
+}
+
+/* Add UID to UGIDS, plus any gids to which that user belongs. If AVAIL is
+ true, the are added to the avail gids instead of the effective ones. */
+error_t
+ugids_add_user (struct ugids *ugids, uid_t uid, int avail)
+{
+ error_t err;
+ struct idvec imp_gids = IDVEC_INIT;
+ uid_t uids_ids[] = { uid };
+ struct idvec uids = { uids_ids, 1 };
+ struct idvec *gids = avail ? &ugids->avail_gids : &ugids->eff_gids;
+
+ idvec_merge_implied_gids (&imp_gids, &uids);
+
+ /* Now remove any gids we already know about from IMP_GIDS. For gids
+ that weren't in the appropriate implied set before, this will
+ ensure that they remain out after we merge IMP_GIDS into it, and
+ ones that *were*, they will remain so. */
+ idvec_subtract (&imp_gids, gids);
+
+ /* Try to add UID. */
+ err = idvec_add_new (avail ? &ugids->avail_uids : &ugids->eff_uids, uid);
+
+ if (! err)
+ /* Now that we've added UID, we can add appropriate implied gids.
+ [If this fails, UGIDS will be an inconsistent state, but things
+ are probably fucked anyhow] */
+ err =
+ idvec_merge (avail ? &ugids->avail_gids : &ugids->eff_gids,
+ &imp_gids);
+ if (! err)
+ err = idvec_merge ((avail
+ ? &ugids->imp_avail_gids
+ : &ugids->imp_eff_gids),
+ &imp_gids);
+
+ idvec_fini (&imp_gids);
+
+ return err;
+}
diff --git a/libshouldbeinlibc/ugids.h b/libshouldbeinlibc/ugids.h
new file mode 100644
index 00000000..5d0e1134
--- /dev/null
+++ b/libshouldbeinlibc/ugids.h
@@ -0,0 +1,231 @@
+/* Uid/gid parsing/frobbing
+
+ Copyright (C) 1997,2001 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __UGIDS_H__
+#define __UGIDS_H__
+
+#include <stdlib.h> /* For inline function stuff. */
+#include <idvec.h>
+#include <features.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#ifdef UGIDS_DEFINE_EI
+#define UGIDS_EI
+#else
+#define UGIDS_EI __extern_inline
+#endif
+
+/* A structure holding a set of the common various types of ids. */
+struct ugids
+{
+ struct idvec eff_uids; /* Effective UIDs */
+ struct idvec eff_gids; /* Effective GIDs */
+ struct idvec avail_uids; /* Available UIDs */
+ struct idvec avail_gids; /* Available GIDs */
+
+ /* These should be a subset of EFF/AVAIL_GIDS, containing those gids which
+ are present only by implication from uids in EFF/AVAIL_UIDS. */
+ struct idvec imp_eff_gids;
+ struct idvec imp_avail_gids;
+};
+
+#define UGIDS_INIT { IDVEC_INIT, IDVEC_INIT, IDVEC_INIT, IDVEC_INIT, IDVEC_INIT, IDVEC_INIT }
+
+/* Return a new ugids structure, or 0 if an allocation error occurs. */
+struct ugids *make_ugids ();
+
+extern void ugids_fini (struct ugids *ugids);
+
+extern void ugids_free (struct ugids *ugids);
+
+extern int ugids_is_empty (const struct ugids *ugids);
+
+extern int ugids_equal (const struct ugids *ugids1, const struct ugids *ugids2);
+
+#if defined(__USE_EXTERN_INLINES) || defined(UGIDS_DEFINE_EI)
+
+/* Free all resources used by UGIDS except UGIDS itself. */
+UGIDS_EI void
+ugids_fini (struct ugids *ugids)
+{
+ idvec_fini (&ugids->eff_uids);
+ idvec_fini (&ugids->eff_gids);
+ idvec_fini (&ugids->avail_uids);
+ idvec_fini (&ugids->avail_gids);
+ idvec_fini (&ugids->imp_eff_gids);
+ idvec_fini (&ugids->imp_avail_gids);
+}
+
+/* Free all resources used by UGIDS. */
+UGIDS_EI void
+ugids_free (struct ugids *ugids)
+{
+ ugids_fini (ugids);
+ free (ugids);
+}
+
+/* Return true if UGIDS contains no ids. */
+UGIDS_EI int
+ugids_is_empty (const struct ugids *ugids)
+{
+ /* We needn't test the imp_*_gids vectors because they are subsets of the
+ corresponding *_gids vectors. */
+ return
+ idvec_is_empty (&ugids->eff_uids)
+ && idvec_is_empty (&ugids->eff_gids)
+ && idvec_is_empty (&ugids->avail_uids)
+ && idvec_is_empty (&ugids->avail_gids);
+}
+
+/* Free all resources used by UGIDS except UGIDS itself. */
+UGIDS_EI int
+ugids_equal (const struct ugids *ugids1, const struct ugids *ugids2)
+{
+ return
+ idvec_equal (&ugids1->eff_uids, &ugids2->eff_uids)
+ && idvec_equal (&ugids1->eff_gids, &ugids2->eff_gids)
+ && idvec_equal (&ugids1->avail_uids, &ugids2->avail_uids)
+ && idvec_equal (&ugids1->avail_gids, &ugids2->avail_gids)
+ && idvec_equal (&ugids1->imp_eff_gids, &ugids2->imp_eff_gids)
+ && idvec_equal (&ugids1->imp_avail_gids, &ugids2->imp_avail_gids);
+}
+
+#endif /* Use extern inlines. */
+
+/* Add all ids in NEW to UGIDS. */
+error_t ugids_merge (struct ugids *ugids, const struct ugids *new);
+
+/* Set the ids in UGIDS to those in NEW. */
+error_t ugids_set (struct ugids *ugids, const struct ugids *new);
+
+/* Remove the ids in SUB from those in UGIDS. */
+error_t ugids_subtract (struct ugids *ugids, const struct ugids *sub);
+
+/* Mark as implied all gids in UGIDS that can be implied from its uids. */
+error_t ugids_imply_all (struct ugids *ugids);
+
+/* Save any effective ids in UGIDS by merging them into the available ids,
+ and removing them from the effective ones. */
+error_t ugids_save (struct ugids *ugids);
+
+/* Verify that we have the right to the ids in UGIDS, given that we already
+ possess those in HAVE_UIDS and HAVE_GIDS, asking for passwords where
+ necessary. 0 is returned if access should be allowed, otherwise
+ EINVAL if an incorrect password was entered, or an error relating to
+ resource failure. The GETPASS_FN, GETPASS_HOOK, VERIFY_FN, and
+ VERIFY_HOOK arguments are as for the idvec_verify function (in <idvec.h>). */
+error_t ugids_verify (const struct ugids *ugids,
+ const struct idvec *have_uids,
+ const struct idvec *have_gids,
+ char *(*getpass_fn) (const char *prompt,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook),
+ void *getpass_hook,
+ error_t (*verify_fn) (const char *password,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook),
+ void *verify_hook);
+
+/* Make an auth port from UGIDS and return it in AUTH, using authority in
+ both the auth port FROM and the current auth port. */
+error_t ugids_make_auth (const struct ugids *ugids,
+ const auth_t *from, size_t num_from,
+ auth_t *auth);
+
+/* Verify that we have the right to the ids in UGIDS, given that we already
+ possess those in HAVE_UIDS and HAVE_GIDS (asking for passwords where
+ necessary), and return corresponding authentication in AUTH; the auth
+ ports in FROM, of length NUM_FROM, are used to supplement the auth port of
+ the current process if necessary. 0 is returned if access should be
+ allowed, otherwise EINVAL if an incorrect password was entered, or an
+ error relating to resource failure. GETPASS_FN and GETPASS_HOOK are as
+ for the idvec_verify function in <idvec.h>. */
+error_t ugids_verify_make_auth (const struct ugids *ugids,
+ const struct idvec *have_uids,
+ const struct idvec *have_gids,
+ char *(*getpass_fn) (const char *prompt,
+ uid_t id, int is_group,
+ void *pwd_or_grp,
+ void *hook),
+ void *getpass_hook,
+ const auth_t *from, size_t num_from,
+ auth_t *auth);
+
+/* Merge the ids from the auth port AUTH into UGIDS. */
+error_t ugids_merge_auth (struct ugids *ugids, auth_t auth);
+
+/* Return a string representation of the ids in UGIDS. SHOW_VALUES and
+ SHOW_NAMES reflect how each id is printed (if SHOW_NAMES is true values
+ are used where names aren't available); if both are true, the
+ `VALUE(NAME)' format is used. ID_SEP, TYPE_SEP, and HDR_SEP contain the
+ strings that separate, respectively, multiple ids of a particular type
+ (default ","), the various types of ids (default ", "), and the name of
+ each type from its ids (default ": "). The empty string is returned for
+ an empty list, and 0 for an allocation error. */
+char *ugids_rep (const struct ugids *ugids, int show_values, int show_names,
+ const char *id_sep, const char *type_sep,
+ const char *hdr_sep);
+
+/* Add a new uid to UGIDS. If AVAIL is true, it's added to the avail uids
+ instead of the effective ones. */
+error_t ugids_add_uid (struct ugids *ugids, uid_t uid, int avail);
+
+/* Add a new gid to UGIDS. If AVAIL is true, it's added to the avail gids
+ instead of the effective ones. */
+error_t ugids_add_gid (struct ugids *ugids, gid_t gid, int avail);
+
+/* Add UID to UGIDS, plus any gids to which that user belongs. If AVAIL is
+ true, the are added to the avail gids instead of the effective ones. */
+error_t ugids_add_user (struct ugids *ugids, uid_t uid, int avail);
+
+/* Install UID into UGIDS as the main user, making sure that the posix
+ `real' and `saved' uid slots are filled in, and similarly add all
+ groups to which UID belongs. */
+error_t ugids_set_posix_user (struct ugids *ugids, uid_t uid);
+
+/* Params to be passed as the input when parsing UGIDS_ARGP. */
+struct ugids_argp_params
+{
+ /* Parsed ids should be added here. */
+ struct ugids *ugids;
+
+ /* If true, parse multiple args as user otherwise, parse none. */
+ int parse_user_args;
+
+ /* If true, and PARSE_USER_ARGS is true, add user args to the available
+ ids, not the effective ones. If both are true, add them to both.
+ If both are false, use the special ugids_set_posix_user instead (which
+ sets both, in a particular way). */
+ int user_args_are_effective;
+ int user_args_are_available;
+
+ /* If >= 0, a user that should be added if none are specified on the
+ command line (following the same rules). */
+ int default_user;
+
+ /* If true, at least one id has to be specified. */
+ int require_ids;
+};
+
+/* A parser for selecting a set of ugids. */
+extern struct argp ugids_argp;
+
+#endif /* __UGIDS_H__ */
diff --git a/libshouldbeinlibc/wire.c b/libshouldbeinlibc/wire.c
new file mode 100644
index 00000000..ca5d32b1
--- /dev/null
+++ b/libshouldbeinlibc/wire.c
@@ -0,0 +1,186 @@
+/* Function to wire down text and data (including from shared libraries)
+ Copyright (C) 1996,99,2000,01,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include <link.h>
+#include <dlfcn.h>
+#include <hurd.h>
+#include <error.h>
+#include <elf.h>
+
+#pragma weak _DYNAMIC
+#pragma weak dlopen
+#pragma weak dlclose
+#pragma weak dlerror
+#pragma weak dlsym
+#ifndef RTLD_NOLOAD
+#define RTLD_NOLOAD 0
+#endif
+
+/* Find the list of shared objects */
+static struct link_map *
+loaded (void)
+{
+ ElfW(Dyn) *d;
+
+ if (&_DYNAMIC == 0) /* statically linked */
+ return 0;
+
+ for (d = _DYNAMIC; d->d_tag != DT_NULL; ++d)
+ if (d->d_tag == DT_DEBUG)
+ {
+ struct r_debug *r = (void *) d->d_un.d_ptr;
+ return r->r_map;
+ }
+
+ return 0; /* ld broken */
+}
+
+/* Compute the extent of a particular shared object. */
+static ElfW(Addr)
+map_extent (struct link_map *map)
+{
+ /* In fact, LIB == MAP, but doing it this way makes it entirely kosher. */
+ void *lib = dlopen (map->l_name, RTLD_NOLOAD);
+ if (lib == 0)
+ {
+ error (2, 0, "cannot dlopen %s: %s", map->l_name, dlerror ());
+ /* NOTREACHED */
+ return 0;
+ }
+ else
+ {
+ /* Find the _end symbol's runtime address and subtract the load base. */
+ void *end = dlsym (lib, "_end");
+ if (end == 0)
+ error (2, 0, "cannot wire library %s with no _end symbol: %s",
+ map->l_name, dlerror ());
+ dlclose (lib);
+ return (ElfW(Addr)) end - map->l_addr;
+ }
+}
+
+/* Wire down all memory currently allocated at START for LEN bytes;
+ host_priv is the privileged host port. */
+static void
+wire_segment_internal (vm_address_t start,
+ vm_size_t len,
+ host_priv_t host_priv)
+{
+ vm_address_t addr;
+ vm_size_t size;
+ vm_prot_t protection;
+ vm_prot_t max_protection;
+ vm_inherit_t inheritance;
+ boolean_t shared;
+ mach_port_t object_name;
+ vm_offset_t offset;
+ error_t err;
+ volatile char *poke;
+
+ do
+ {
+ addr = start;
+ err = vm_region (mach_task_self (), &addr, &size, &protection,
+ &max_protection, &inheritance, &shared, &object_name,
+ &offset);
+ if (err)
+ return;
+
+ /* The current region begins at ADDR and is SIZE long. If it
+ extends beyond the LEN, prune it. */
+ if (addr + size > start + len)
+ size = len - (addr - start);
+
+ /* Set protection to allow all access possible */
+ vm_protect (mach_task_self (), addr, size, 0, max_protection);
+
+ /* Generate write faults */
+ for (poke = (char *) addr;
+ (vm_address_t) poke < addr + size;
+ poke += vm_page_size)
+ *poke = *poke;
+
+ /* Wire pages */
+ vm_wire (host_priv, mach_task_self (), addr, size, max_protection);
+
+ /* Set protection back to what it was */
+ vm_protect (mach_task_self (), addr, size, 0, protection);
+
+
+ mach_port_deallocate (mach_task_self (), object_name);
+
+ len -= (addr - start) + size;
+ start = addr + size;
+ }
+ while (len);
+}
+
+/* Wire down all memory currently allocated at START for LEN bytes. */
+void
+wire_segment (vm_address_t start,
+ vm_size_t len)
+{
+ mach_port_t host, device;
+ error_t err;
+
+ err = get_privileged_ports (&host, &device);
+ if (!err)
+ {
+ wire_segment_internal (start, len, host);
+ mach_port_deallocate (mach_task_self (), host);
+ mach_port_deallocate (mach_task_self (), device);
+ }
+}
+
+
+/* Wire down all the text and data (including from shared libraries)
+ for the current program. */
+void
+wire_task_self ()
+{
+ struct link_map *map;
+ mach_port_t host, device;
+ error_t err;
+ extern char _edata, _etext, __data_start;
+
+ err = get_privileged_ports (&host, &device);
+ if (err)
+ return;
+
+ map = loaded ();
+ if (!map)
+ {
+ extern void _start ();
+ vm_address_t text_start = (vm_address_t) &_start;
+ wire_segment_internal (text_start,
+ (vm_size_t) (&_etext - text_start),
+ host);
+ wire_segment_internal ((vm_address_t) &__data_start,
+ (vm_size_t) (&_edata - &__data_start),
+ host);
+ }
+ else
+ while (map)
+ wire_segment ((vm_address_t) map->l_addr, map_extent (map));
+
+ mach_port_deallocate (mach_task_self (), host);
+ mach_port_deallocate (mach_task_self (), device);
+}
diff --git a/libshouldbeinlibc/wire.h b/libshouldbeinlibc/wire.h
new file mode 100644
index 00000000..6783cc50
--- /dev/null
+++ b/libshouldbeinlibc/wire.h
@@ -0,0 +1,26 @@
+/* Function to wire down text and data (including from shared libraries)
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* Wire down all the text and data (including from shared libraries)
+ for the current program. */
+void wire_task_self (void);
+
+/* Wire down all memory currently allocated at START for LEN bytes. */
+void wire_segment (vm_address_t start, vm_size_t len);
diff --git a/libshouldbeinlibc/xportinfo.c b/libshouldbeinlibc/xportinfo.c
new file mode 100644
index 00000000..cce6fb6c
--- /dev/null
+++ b/libshouldbeinlibc/xportinfo.c
@@ -0,0 +1,69 @@
+/* Print information about a port, with the name translated between tasks
+
+ Copyright (C) 1996, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include "portinfo.h"
+
+/* Prints info about NAME translated through X to STREAM, in a way described
+ by the flags in SHOW. If TYPE is non-zero, it should be what
+ mach_port_type returns for NAME in X->to_task. */
+error_t
+print_xlated_port_info (mach_port_t name, mach_port_type_t type,
+ struct port_name_xlator *x,
+ unsigned show, FILE *stream)
+{
+ mach_port_t old_name = name;
+ error_t err = port_name_xlator_xlate (x, name, type, &name, &type);
+ if (! err)
+ {
+ fprintf (stream, (show & PORTINFO_HEX_NAMES) ? "%#6zx => " : "%6zd => ",
+ old_name);
+ err = print_port_info (name, type, x->to_task, show, stream);
+ }
+ return err;
+}
+
+/* Prints info about every port common to both tasks in X, but only if the
+ port in X->from_task has a type in ONLY, to STREAM. */
+error_t
+print_xlated_task_ports_info (struct port_name_xlator *x,
+ mach_port_type_t only,
+ unsigned show, FILE *stream)
+{
+ mach_port_t *names = 0;
+ mach_port_type_t *types = 0;
+ mach_msg_type_number_t names_len = 0, types_len = 0, i;
+ error_t err =
+ mach_port_names (x->from_task, &names, &names_len, &types, &types_len);
+
+ if (err)
+ return err;
+
+ for (i = 0; i < names_len; i++)
+ if (types[i] & only)
+ print_xlated_port_info (names[i], types[i], x, show, stream);
+
+ munmap ((caddr_t) names, names_len * sizeof *names);
+ munmap ((caddr_t) types, types_len * sizeof *types);
+
+ return 0;
+}
diff --git a/libstore/Makefile b/libstore/Makefile
new file mode 100644
index 00000000..28f56609
--- /dev/null
+++ b/libstore/Makefile
@@ -0,0 +1,89 @@
+# Makefile for libstore
+#
+# Copyright (C) 1995,96,97,2001,02 Free Software Foundation, Inc.
+# Written by Miles Bader <miles@gnu.org>
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+
+dir := libstore
+makemode := library
+
+libname = libstore
+SRCS = create.c derive.c make.c rdwr.c set.c \
+ enc.c encode.c decode.c clone.c argp.c kids.c flags.c \
+ open.c xinl.c typed.c map.c url.c unknown.c \
+ stripe.c $(filter-out ileave.c concat.c,$(store-types:=.c))
+
+store-types = \
+ concat \
+ copy \
+ device \
+ file \
+ ileave \
+ memobj \
+ module \
+ mvol \
+ nbd \
+ remap \
+ task \
+ zero
+
+# This has to be evaluated after config.make has been included;
+# as a consequence, using 'ifneq' or similar is not an option.
+store-types += \
+ $(and $(PARTED_LIBS),part) \
+ $(and $(HAVE_LIBBZ2),bunzip2) \
+ $(and $(HAVE_LIBZ),gunzip) \
+
+libstore.so-LDLIBS += $(PARTED_LIBS) -ldl
+installhdrs=store.h
+
+HURDLIBS = shouldbeinlibc
+LDLIBS += -lpthread $(and $(HAVE_LIBBZ2),-lbz2) $(and $(HAVE_LIBZ),-lz)
+GUNZIP_OBJS = do-gunzip.o util.o
+BUNZIP2_OBJS = do-bunzip2.o
+OBJS = $(SRCS:.c=.o) \
+ $(and $(HAVE_LIBZ),$(GUNZIP_OBJS)) \
+ $(and $(HAVE_LIBBZ2),$(BUNZIP2_OBJS))
+
+include ../Makeconf
+
+module-CPPFLAGS = -D'STORE_SONAME_SUFFIX=".so.$(hurd-version)"'
+module-DEPS = $(..)config.make
+
+libstore_gunzip.so.$(hurd-version): $(GUNZIP_OBJS:.o=_pic.o)
+libstore_bunzip2.so.$(hurd-version): $(BUNZIP2_OBJS:.o=_pic.o)
+
+# You can use this rule to make a dynamically-loadable version of any
+# of the modules. We don't make any of these by default, since we
+# just include all the standard store types in libstore.so itself.
+libstore_%.so.$(hurd-version): %_pic.o libstore.so
+ $(CC) -shared -Wl,-soname=$@ -o $@ \
+ $(rpath) $(CFLAGS) $(LDFLAGS) $(libstore_$*.so-LDFLAGS) $^
+
+# Each libstore_TYPE.a is in fact an object file script so that `-lstore_TYPE'
+# just has the same effect as `-u store_TYPE_class'.
+$(store-types:%=libstore_%.a): libstore_%.a: $(srcdir)/Makefile
+ $(CC) -r -nostdlib -nostartfiles -x c /dev/null \
+ -o $@ -u store_$*_class
+cleantarg += $(store-types:%=libstore_%.a)
+
+all: $(store-types:%=libstore_%.a)
+
+install: $(store-types:%=$(libdir)/libstore_%.a)
+$(store-types:%=$(libdir)/libstore_%.a): $(libdir)/%: %
+ $(INSTALL_DATA) $< $@
diff --git a/libstore/argp.c b/libstore/argp.c
new file mode 100644
index 00000000..6ed79964
--- /dev/null
+++ b/libstore/argp.c
@@ -0,0 +1,394 @@
+/* Store argument parsing
+
+ Copyright (C) 1996,97,98,99,2001,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+#include <assert.h>
+#include <hurd.h>
+#include <argp.h>
+#include <argz.h>
+
+#include "store.h"
+
+/* We use this class variable instead of just the name so that we ensure
+ linking in store_open to define it. */
+#define DEFAULT_STORE_CLASS store_query_class
+
+static const struct argp_option options[] = {
+ {"store-type",'T', "TYPE", 0, "Each DEVICE names a store of type TYPE"},
+ {"machdev", 'm', 0, OPTION_HIDDEN}, /* deprecated */
+ {"interleave",'I', "BLOCKS", 0, "Interleave in runs of length BLOCKS"},
+ {"layer", 'L', 0, 0, "Layer multiple devices for redundancy"},
+ {0}
+};
+
+static const char args_doc[] = "DEVICE...";
+static const char doc[] = "\vIf neither --interleave or --layer is specified,"
+" multiple DEVICEs are concatenated.";
+
+struct store_parsed
+{
+ /* Names of devices parsed. */
+ char *names;
+ size_t names_len;
+
+ /* Prefix that should be applied to each member of NAMES. */
+ char *name_prefix;
+
+ /* --store-type specified. This defaults to the `query' type. */
+ const struct store_class *type;
+
+ /* A vector of class pointers used to lookup class names. Defaults to
+ STORE_STD_CLASSES. */
+ const struct store_class *const *classes;
+
+ /* DEFAULT_TYPE field passed to parser. */
+ const struct store_class *default_type;
+
+ store_offset_t interleave; /* --interleave value */
+ int layer : 1; /* --layer specified */
+};
+
+void
+store_parsed_free (struct store_parsed *parsed)
+{
+ if (parsed->names_len > 0)
+ free (parsed->names);
+ if (parsed->name_prefix)
+ free (parsed->name_prefix);
+ free (parsed);
+}
+
+/* Add the arguments PARSED, and return the corresponding store in STORE. */
+error_t
+store_parsed_append_args (const struct store_parsed *parsed,
+ char **args, size_t *args_len)
+{
+ char buf[40];
+ error_t err = 0;
+ size_t num_names = argz_count (parsed->names, parsed->names_len);
+
+ if (!err && num_names > 1 && (parsed->interleave || parsed->layer))
+ {
+ if (parsed->interleave)
+ snprintf (buf, sizeof buf, "--interleave=%Ld", parsed->interleave);
+ else
+ snprintf (buf, sizeof buf, "--layer=%d", parsed->layer);
+ err = argz_add (args, args_len, buf);
+ }
+
+ if (!err && parsed->type != parsed->default_type)
+ {
+ if (parsed->name_prefix)
+ /* A name prefix of "PFX" is equivalent to appending ":PFX" to the
+ type name. */
+ {
+ size_t npfx_len = strlen (parsed->name_prefix);
+ char tname[strlen ("--store-type=")
+ + strlen (parsed->type->name) + 1 + npfx_len + 1];
+ snprintf (tname, sizeof tname, "--store-type=%s:%.*s",
+ parsed->type->name, (int) npfx_len, parsed->name_prefix);
+ err = argz_add (args, args_len, tname);
+ }
+ else
+ /* A simple type name. */
+ {
+ snprintf (buf, sizeof buf, "--store-type=%s", parsed->type->name);
+ err = argz_add (args, args_len, buf);
+ }
+ }
+
+ if (! err)
+ err = argz_append (args, args_len, parsed->names, parsed->names_len);
+
+ return err;
+}
+
+error_t
+store_parsed_name (const struct store_parsed *parsed, char **name)
+{
+ char buf[40];
+ char *pfx = 0;
+
+ if (argz_count (parsed->names, parsed->names_len) > 1)
+ {
+ if (parsed->interleave)
+ {
+ snprintf (buf, sizeof buf, "interleave(%Ld,", parsed->interleave);
+ pfx = buf;
+ }
+ else if (parsed->layer)
+ pfx = "layer(";
+ }
+
+ if (pfx)
+ *name = malloc (strlen (pfx) + parsed->names_len + 1);
+ else
+ *name = malloc (parsed->names_len);
+
+ if (! *name)
+ return ENOMEM;
+
+ if (pfx)
+ {
+ char *end = stpcpy (*name, pfx);
+ bcopy (parsed->names, end, parsed->names_len);
+ argz_stringify (end, parsed->names_len, ',');
+ strcpy (end + parsed->names_len, ")");
+ }
+ else
+ {
+ bcopy (parsed->names, *name, parsed->names_len);
+ argz_stringify (*name, parsed->names_len, ',');
+ }
+
+ return 0;
+}
+
+/* Open PARSED, and return the corresponding store in STORE. */
+error_t
+store_parsed_open (const struct store_parsed *parsed, int flags,
+ struct store **store)
+{
+ size_t pfx_len = parsed->name_prefix ? strlen (parsed->name_prefix) : 0;
+ size_t num = argz_count (parsed->names, parsed->names_len);
+
+ error_t open (char *name, struct store **store)
+ {
+ const struct store_class *type = parsed->type;
+ if (type->open)
+ {
+ if (parsed->name_prefix)
+ /* If there's a name prefix, we prefix any names we open with that
+ and a colon. */
+ {
+ char pfxed_name[pfx_len + 1 + strlen (name) + 1];
+ stpcpy (stpcpy (stpcpy (pfxed_name, parsed->name_prefix),
+ ":"),
+ name);
+ return (*type->open) (pfxed_name, flags, parsed->classes, store);
+ }
+ else
+ return (*type->open) (name, flags, parsed->classes, store);
+ }
+ else
+ return EOPNOTSUPP;
+ }
+
+ if (num == 1)
+ return open (parsed->names, store);
+ else if (num == 0)
+ return open (0, store);
+ else
+ {
+ int i;
+ char *name;
+ error_t err = 0;
+ struct store **stores = malloc (sizeof (struct store *) * num);
+
+ if (! stores)
+ return ENOMEM;
+
+ for (i = 0, name = parsed->names;
+ !err && i < num;
+ i++, name = argz_next (parsed->names, parsed->names_len, name))
+ err = open (name, &stores[i]);
+
+ if (! err)
+ {
+ if (parsed->interleave)
+ err =
+ store_ileave_create (stores, num, parsed->interleave,
+ flags, store);
+ else if (parsed->layer)
+ assert (! parsed->layer);
+ else
+ err = store_concat_create (stores, num, flags, store);
+ }
+
+ if (err)
+ {
+ while (i > 0)
+ store_free (stores[i--]);
+ free (stores);
+ }
+
+ return err;
+ }
+}
+
+static const struct store_class *
+find_class (const char *name, const struct store_class *const *const classes)
+{
+ const struct store_class *const *cl;
+ for (cl = classes ?: __start_store_std_classes;
+ classes ? *cl != 0 : cl < __stop_store_std_classes;
+ ++cl)
+ if ((*cl)->name && strcmp (name, (*cl)->name) == 0)
+ return *cl;
+
+# pragma weak store_module_find_class
+ if (! classes && store_module_find_class)
+ {
+ const struct store_class *cl;
+ if (store_module_find_class (name, strchr (name, '\0'), &cl) == 0)
+ return cl;
+ }
+
+ return 0;
+}
+
+/* Print a parsing error message and (if exiting is turned off) return the
+ error code ERR. Requires a variable called STATE to be in scope. */
+#define PERR(err, fmt, args...) \
+ do { argp_error (state, fmt , ##args); return err; } while (0)
+
+/* Parse a --store-type/-T option. */
+static error_t
+parse_type (char *arg, struct argp_state *state, struct store_parsed *parsed)
+{
+ char *name_prefix = 0;
+ char *type_name = arg;
+ const struct store_class *type;
+ char *class_sep = strchr (arg, ':');
+
+ if (class_sep)
+ /* A `:'-separated class name "T1:T2" is equivalent to prepending "T2:"
+ to the device name passed to T1, and is useful for the case where T1
+ takes typed names of the form "T:NAME". A trailing `:', like "T1:" is
+ equivalent to prefixing `:' to the device name, which causes NAME to
+ be opened with store_open, as a file. */
+ {
+ type_name = strndupa (arg, class_sep - arg);
+ name_prefix = class_sep + 1;
+ }
+
+ type = find_class (type_name, parsed->classes);
+ if (!type || !type->open)
+ PERR (EINVAL, "%s: Invalid argument to --store-type", arg);
+ else if (type != parsed->type && parsed->type != parsed->default_type)
+ PERR (EINVAL, "--store-type specified multiple times");
+
+ parsed->type = type;
+ parsed->name_prefix = name_prefix;
+
+ return 0;
+}
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ error_t err;
+ struct store_parsed *parsed = state->hook;
+
+ switch (opt)
+ {
+ case 'm':
+ arg = "device";
+ /* fall through */
+ case 'T':
+ return parse_type (arg, state, parsed);
+
+ case 'I':
+ if (parsed->layer)
+ PERR (EINVAL, "--layer and --interleave are exclusive");
+ if (parsed->interleave)
+ /* Actually no reason why we couldn't support this.... */
+ PERR (EINVAL, "--interleave specified multiple times");
+
+ parsed->interleave = atoi (arg);
+ if (! parsed->interleave)
+ PERR (EINVAL, "%s: Bad value for --interleave", arg);
+ break;
+
+ case 'L':
+#if 1
+ argp_failure (state, 5, 0, "--layer not implemented");
+ return EINVAL;
+#else
+ if (parsed->interleave)
+ PERR (EINVAL, "--layer and --interleave are exclusive");
+ parsed->layer = 1;
+#endif
+ break;
+
+ case ARGP_KEY_ARG:
+ /* A store device to use! */
+ if (parsed->type->validate_name)
+ err = (*parsed->type->validate_name) (arg, parsed->classes);
+ else
+ err = 0;
+ if (! err)
+ err = argz_add (&parsed->names, &parsed->names_len, arg);
+ if (err)
+ argp_failure (state, 1, err, "%s", arg);
+ return err;
+ break;
+
+ case ARGP_KEY_INIT:
+ /* Initialize our parsing state. */
+ {
+ struct store_argp_params *params = state->input;
+ if (! params)
+ return EINVAL; /* Need at least a way to return a result. */
+ parsed = state->hook = malloc (sizeof (struct store_parsed));
+ if (! parsed)
+ return ENOMEM;
+ bzero (parsed, sizeof (struct store_parsed));
+ parsed->classes = params->classes;
+ parsed->default_type =
+ find_class (params->default_type ?: DEFAULT_STORE_CLASS.name,
+ parsed->classes);
+ if (! parsed->default_type)
+ {
+ free (parsed);
+ return EINVAL;
+ }
+ parsed->type = parsed->default_type;
+ }
+ break;
+
+ case ARGP_KEY_ERROR:
+ /* Parsing error occurred, free everything. */
+ store_parsed_free (parsed); break;
+
+ case ARGP_KEY_SUCCESS:
+ /* Successfully finished parsing, return a result. */
+ if (parsed->names == 0
+ && (!parsed->type->validate_name
+ || (*parsed->type->validate_name) (0, parsed->classes) != 0))
+ {
+ struct store_argp_params *params = state->input;
+ store_parsed_free (parsed);
+ if (!params->store_optional)
+ PERR (EINVAL, "No store specified");
+ parsed = 0;
+ }
+ ((struct store_argp_params *)state->input)->result = parsed;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+struct argp
+store_argp = { options, parse_opt, args_doc, doc };
diff --git a/libstore/bunzip2.c b/libstore/bunzip2.c
new file mode 100644
index 00000000..ec0630ec
--- /dev/null
+++ b/libstore/bunzip2.c
@@ -0,0 +1,13 @@
+#include <errno.h>
+
+extern void do_bunzip2 (void); /* Entry point to bunzip2 engine. */
+
+static error_t
+DO_UNZIP (void)
+{
+ do_bunzip2 ();
+ return 0;
+}
+
+#define UNZIP bunzip2
+#include "unzipstore.c"
diff --git a/libstore/clone.c b/libstore/clone.c
new file mode 100644
index 00000000..cdbd5747
--- /dev/null
+++ b/libstore/clone.c
@@ -0,0 +1,91 @@
+/* Store cloning
+
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "store.h"
+
+/* Return in TO a copy of FROM. */
+error_t
+store_clone (struct store *from, struct store **to)
+{
+ struct store *c;
+ error_t err =
+ _store_create (from->class, from->port, from->flags, from->block_size,
+ from->runs, from->num_runs, from->end, &c);
+
+ if (err)
+ return err;
+
+ if (from->name)
+ {
+ c->name = strdup (from->name);
+ if (! c->name)
+ err = ENOMEM;
+ }
+
+ if (from->misc_len)
+ {
+ c->misc = malloc (from->misc_len);
+ if (! c->misc)
+ err = ENOMEM;
+ }
+
+ if (!err && c->port != MACH_PORT_NULL)
+ {
+ err = mach_port_mod_refs (mach_task_self (),
+ c->port, MACH_PORT_RIGHT_SEND, 1);
+ if (err)
+ c->port = MACH_PORT_NULL; /* Don't deallocate it. */
+ }
+ if (!err && from->source != MACH_PORT_NULL)
+ {
+ err = mach_port_mod_refs (mach_task_self (),
+ from->source, MACH_PORT_RIGHT_SEND, 1);
+ if (! err)
+ c->source = from->source;
+ }
+ if (!err && from->num_children > 0)
+ {
+ int k;
+
+ c->children = malloc (from->num_children * sizeof (struct store *));
+ if (! c->children)
+ err = ENOMEM;
+
+ for (k = 0; !err && k < from->num_children; k++)
+ {
+ err = store_clone (from->children[k], &c->children[k]);
+ if (! err)
+ c->num_children++;
+ }
+ }
+
+ if (!err && from->class->clone)
+ err = (*from->class->clone)(from, c);
+
+ if (err)
+ store_free (c);
+ else
+ *to = c;
+
+ return err;
+}
diff --git a/libstore/copy.c b/libstore/copy.c
new file mode 100644
index 00000000..c670ebf3
--- /dev/null
+++ b/libstore/copy.c
@@ -0,0 +1,267 @@
+/* Copy store backend
+
+ Copyright (C) 1995,96,97,99,2000,01,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include <sys/mman.h>
+#include <mach.h>
+
+#define page_aligned(addr) (((size_t) addr & (vm_page_size - 1)) == 0)
+
+#include "store.h"
+
+static error_t
+copy_read (struct store *store, store_offset_t addr, size_t index,
+ size_t amount, void **buf, size_t *len)
+{
+ char *data = store->hook + (addr * store->block_size);
+
+ if (page_aligned (data) && page_aligned (amount))
+ /* When reading whole pages, we can avoid any real copying. */
+ return vm_read (mach_task_self (),
+ (vm_address_t) data, amount,
+ (pointer_t *) buf, len);
+
+ if (*len < amount)
+ /* Have to allocate memory for the return value. */
+ {
+ *buf = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*buf == MAP_FAILED)
+ return errno;
+ }
+
+ memcpy (*buf, data, amount);
+ *len = amount;
+ return 0;
+}
+
+static error_t
+copy_write (struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf, size_t len, size_t *amount)
+{
+ char *data = store->hook + (addr * store->block_size);
+
+ if (page_aligned (data) && page_aligned (len) && page_aligned (buf))
+ {
+ /* When writing whole pages, we can avoid any real copying. */
+ error_t err = vm_write (mach_task_self (),
+ (vm_address_t) data, (vm_address_t) buf, len);
+ *amount = len;
+ return err;
+ }
+
+ memcpy (data, buf, len);
+ *amount = len;
+ return 0;
+}
+
+static error_t
+copy_set_size (struct store *store, size_t newsize)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+copy_allocate_encoding (const struct store *store, struct store_enc *enc)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+copy_encode (const struct store *store, struct store_enc *enc)
+{
+ return EOPNOTSUPP;
+}
+
+static error_t
+copy_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ return EOPNOTSUPP;
+}
+
+static error_t
+copy_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_copy_open (name, flags, classes, store);
+}
+
+static error_t
+copy_set_flags (struct store *store, int flags)
+{
+ if ((flags & ~(STORE_INACTIVE | STORE_ENFORCED)) != 0)
+ /* Trying to set flags we don't support. */
+ return EINVAL;
+
+ /* ... */
+
+ store->flags |= flags; /* When inactive, anything goes. */
+
+ return 0;
+}
+
+static error_t
+copy_clear_flags (struct store *store, int flags)
+{
+ error_t err = 0;
+ if ((flags & ~(STORE_INACTIVE | STORE_ENFORCED)) != 0)
+ err = EINVAL;
+ /* ... */
+ if (! err)
+ store->flags &= ~flags;
+ return err;
+}
+
+/* Called just before deallocating STORE. */
+void
+copy_cleanup (struct store *store)
+{
+ if (store->size > 0)
+ munmap (store->hook, store->size);
+}
+
+/* Copy any format-dependent fields in FROM to TO; if there's some reason
+ why the copy can't be made, an error should be returned. This call is
+ made after all format-indendependent fields have been cloned. */
+error_t
+copy_clone (const struct store *from, struct store *to)
+{
+ void *buf;
+ buf = mmap (0, to->size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (buf != (void *) -1)
+ {
+ to->hook = buf;
+ memcpy (to->hook, from->hook, from->size);
+ return 0;
+ }
+ return errno;
+}
+
+const struct store_class
+store_copy_class =
+{
+ STORAGE_COPY, "copy", copy_read, copy_write, copy_set_size,
+ copy_allocate_encoding, copy_encode, copy_decode,
+ copy_set_flags, copy_clear_flags, copy_cleanup, copy_clone, 0, copy_open
+};
+STORE_STD_CLASS (copy);
+
+/* Return a new store in STORE which contains a snapshot of the contents of
+ the store FROM; FROM is consumed. */
+error_t
+store_copy_create (struct store *from, int flags, struct store **store)
+{
+ error_t err;
+ struct store_run run;
+
+ run.start = 0;
+ run.length = from->size;
+
+ flags |= STORE_ENFORCED; /* Only uses local resources. */
+
+ err =
+ _store_create (&store_copy_class,
+ MACH_PORT_NULL, flags, from->block_size, &run, 1, 0,
+ store);
+ if (! err)
+ {
+ size_t buf_len = 0;
+
+ /* Copy the input store. */
+ err = store_read (from, 0, from->size, &(*store)->hook, &buf_len);
+
+ if (! err)
+ /* Set the store name. */
+ {
+ if (from->name)
+ {
+ size_t len =
+ strlen (from->class->name) + 1 + strlen (from->name) + 1;
+ (*store)->name = malloc (len);
+ if ((*store)->name)
+ snprintf ((*store)->name, len,
+ "%s:%s", from->class->name, from->name);
+ }
+ else
+ (*store)->name = strdup (from->class->name);
+
+ if (! (*store)->name)
+ err = ENOMEM;
+ }
+
+ if (err)
+ store_free (*store);
+ }
+
+ return err;
+}
+
+/* Return a new store in STORE which contains the memory buffer BUF, of
+ length BUF_LEN, and uses the block size BLOCK_SIZE. BUF must be
+ vm_allocated, and will be consumed, and BUF_LEN must be a multiple of
+ BLOCK_SIZE. */
+error_t
+store_buffer_create (void *buf, size_t buf_len, int flags,
+ struct store **store)
+{
+ error_t err;
+ struct store_run run;
+
+ run.start = 0;
+ run.length = buf_len;
+
+ flags |= STORE_ENFORCED; /* Only uses local resources. */
+
+ err =
+ _store_create (&store_copy_class,
+ MACH_PORT_NULL, flags, 1, &run, 1, 0, store);
+ if (! err)
+ (*store)->hook = buf;
+
+ return err;
+}
+
+/* Open the copy store NAME -- which consists of another store-class name, a
+ ':', and a name for that store class to open -- and return the
+ corresponding store in STORE. CLASSES is used to select classes specified
+ by the type name; if it is 0, STORE_STD_CLASSES is used. */
+error_t
+store_copy_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ struct store *from;
+ error_t err =
+ store_typed_open (name, flags | STORE_HARD_READONLY, classes, &from);
+
+ if (! err)
+ {
+ err = store_copy_create (from, flags, store);
+ if (err)
+ store_free (from);
+ }
+
+ return err;
+}
diff --git a/libstore/create.c b/libstore/create.c
new file mode 100644
index 00000000..010a053e
--- /dev/null
+++ b/libstore/create.c
@@ -0,0 +1,79 @@
+/* Store creation
+
+ Copyright (C) 1995,96,97,2001 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <hurd/fs.h>
+
+#include "store.h"
+
+/* Return a new store in STORE, which refers to the storage underlying SOURCE.
+ CLASSES is used to select classes specified by the provider; if it is 0,
+ STORE_STD_CLASSES is used. FLAGS is set with store_set_flags, with the
+ exception of STORE_INACTIVE, which merely indicates that no attempt should
+ be made to activate an inactive store; if STORE_INACTIVE is not specified,
+ and the store returned for SOURCE is inactive, an attempt is made to
+ activate it (failure of which causes an error to be returned). A reference
+ to SOURCE is created (but may be destroyed with store_close_source). */
+error_t
+store_create (file_t source, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ error_t err;
+ struct store_enc enc;
+ mach_port_t inline_ports[10];
+ int inline_ints[60];
+ off_t inline_offsets[60];
+ char inline_data[100];
+
+ store_enc_init (&enc, inline_ports, 10, inline_ints, 60,
+ inline_offsets, 60, inline_data, 100);
+
+ err = file_get_storage_info (source,
+ &enc.ports, &enc.num_ports,
+ &enc.ints, &enc.num_ints,
+ &enc.offsets, &enc.num_offsets,
+ &enc.data, &enc.data_len);
+ if (err)
+ return err;
+
+ err = store_decode (&enc, classes, store);
+ if (! err)
+ {
+ if (flags & STORE_INACTIVE)
+ flags &= ~STORE_INACTIVE; /* Don't actually make store inactive. */
+ else if ((*store)->flags & STORE_INACTIVE)
+ err = store_clear_flags (*store, STORE_INACTIVE);
+ if (!err && flags)
+ err = store_set_flags (*store, flags);
+ if (err)
+ store_free (*store);
+ }
+ else if (err == EINVAL && (flags &~ STORE_INACTIVE) == STORE_NO_FILEIO)
+ /* Open a generic "unknown" store that can regurgitate this encoding. */
+ err = store_unknown_decode (&enc, classes, store);
+
+ store_enc_dealloc (&enc);
+
+ if (! err)
+ /* Keep a reference to SOURCE around. */
+ (*store)->source = source;
+
+ return err;
+}
diff --git a/libstore/crypt.h b/libstore/crypt.h
new file mode 100644
index 00000000..2a4c203c
--- /dev/null
+++ b/libstore/crypt.h
@@ -0,0 +1,12 @@
+/* crypt.h (dummy version) -- do not perform encryption
+ * Hardly worth copyrighting :-)
+ */
+
+#ifdef CRYPT
+# undef CRYPT /* dummy version */
+#endif
+
+#define RAND_HEAD_LEN 12 /* length of encryption random header */
+
+#define zencode
+#define zdecode
diff --git a/libstore/decode.c b/libstore/decode.c
new file mode 100644
index 00000000..64405ecd
--- /dev/null
+++ b/libstore/decode.c
@@ -0,0 +1,203 @@
+/* Store wire decoding
+
+ Copyright (C) 1996,97,98,2001,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+#include <malloc.h>
+
+#include "store.h"
+
+/* The maximum number of runs for which we allocate run vectors on the stack. */
+#define MAX_STACK_RUNS (16*1024 / sizeof (struct store_run))
+
+/* Decodes the standard leaf encoding that's common to various builtin
+ formats, and calls CREATE to actually create the store. */
+error_t
+store_std_leaf_decode (struct store_enc *enc,
+ store_std_leaf_create_t create,
+ struct store **store)
+{
+ char *misc, *name;
+ error_t err;
+ int type, flags;
+ mach_port_t port;
+ size_t block_size, num_runs, name_len, misc_len;
+ /* Call CREATE appriately from within store_with_decoded_runs. */
+ error_t call_create (const struct store_run *runs, size_t num_runs)
+ {
+ return (*create)(port, flags, block_size, runs, num_runs, store);
+ }
+
+ /* Make sure there are enough encoded ints and ports. */
+ if (enc->cur_int + 6 > enc->num_ints || enc->cur_port + 1 > enc->num_ports)
+ return EINVAL;
+
+ /* Read encoded ints. */
+ type = enc->ints[enc->cur_int++];
+ flags = enc->ints[enc->cur_int++];
+ block_size = enc->ints[enc->cur_int++];
+ num_runs = enc->ints[enc->cur_int++];
+ name_len = enc->ints[enc->cur_int++];
+ misc_len = enc->ints[enc->cur_int++];
+
+ /* Make sure there are enough encoded offsets and data. */
+ if (enc->cur_offset + num_runs * 2 > enc->num_offsets
+ || enc->cur_data + name_len + misc_len > enc->data_len)
+ return EINVAL;
+
+ if (name_len > 0 && enc->data[enc->cur_data + name_len - 1] != '\0')
+ return EINVAL; /* Name not terminated. */
+
+ if (name_len > 0)
+ {
+ name = strdup (enc->data + enc->cur_data);
+ if (! name)
+ return ENOMEM;
+ enc->cur_data += name_len;
+ }
+ else
+ name = 0;
+
+ if (misc_len > 0)
+ {
+ misc = malloc (misc_len);
+ if (! misc)
+ {
+ if (name)
+ free (name);
+ return ENOMEM;
+ }
+ memcpy (misc, enc->data + enc->cur_data + name_len, misc_len);
+ enc->cur_data += misc_len;
+ }
+ else
+ misc = 0;
+
+ /* Read encoded ports (be careful to deallocate this if we barf). */
+ port = enc->ports[enc->cur_port++];
+
+ err = store_with_decoded_runs (enc, num_runs, call_create);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), port);
+ if (misc)
+ free (misc);
+ if (name)
+ free (name);
+ }
+ else
+ {
+ (*store)->flags = flags;
+ (*store)->name = name;
+ (*store)->misc = misc;
+ (*store)->misc_len = misc_len;
+ }
+
+ return err;
+}
+
+/* Call FUN with the vector RUNS of length RUNS_LEN extracted from ENC. */
+error_t
+store_with_decoded_runs (struct store_enc *enc, size_t num_runs,
+ error_t (*fun) (const struct store_run *runs,
+ size_t num_runs))
+{
+ int i;
+ error_t err;
+
+ /* Since the runs are passed in an array of off_t pairs, and we use struct
+ store_run, we have to make a temporary array to hold the (probably
+ bitwise identical) converted representation to pass to CREATE. */
+ if (num_runs <= MAX_STACK_RUNS)
+ {
+ struct store_run runs[num_runs];
+ off_t *e = enc->offsets + enc->cur_offset;
+ for (i = 0; i < num_runs; i++)
+ {
+ runs[i].start = *e++;
+ runs[i].length = *e++;
+ }
+ enc->cur_offset = e - enc->offsets;
+ err = (*fun)(runs, num_runs);
+ }
+ else
+ /* Ack. Too many runs to allocate the temporary RUNS array on the stack.
+ This will probably never happen. */
+ {
+ struct store_run *runs = malloc (num_runs * sizeof (struct store_run));
+ if (runs)
+ {
+ off_t *e = enc->offsets + enc->cur_offset;
+ for (i = 0; i < num_runs; i++)
+ {
+ runs[i].start = *e++;
+ runs[i].length = *e++;
+ }
+ enc->cur_offset = e - enc->offsets;
+ err = (*fun) (runs, num_runs);
+ free (runs);
+ }
+ else
+ err = ENOMEM;
+ }
+
+ return err;
+}
+
+/* Decode ENC, either returning a new store in STORE, or an error. CLASSES
+ defines the mapping from hurd storage class ids to store classes; if it is
+ 0, STORE_STD_CLASSES is used. If nothing else is to be done with ENC, its
+ contents may then be freed using store_enc_dealloc. */
+error_t
+store_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ const struct store_class *const *cl;
+
+ if (enc->cur_int >= enc->num_ints)
+ /* The first int should always be the type. */
+ return EINVAL;
+
+ if (enc->ints[enc->cur_int] == STORAGE_NETWORK)
+ /* This is a special case because store classes supporting
+ individual URL types will also use STORAGE_NETWORK,
+ and we want the generic dispatcher to come first. */
+ return store_url_decode (enc, classes, store);
+
+ for (cl = classes ?: __start_store_std_classes;
+ classes ? *cl != 0 : cl < __stop_store_std_classes;
+ ++cl)
+ if ((*cl)->id == enc->ints[enc->cur_int])
+ {
+ if ((*cl)->decode)
+ return (*(*cl)->decode) (enc, classes, store);
+ else
+ return EOPNOTSUPP;
+ }
+
+# pragma weak store_module_decode
+ if (! classes && store_module_decode)
+ {
+ error_t err = store_module_decode (enc, classes, store);
+ if (err != ENOENT)
+ return err;
+ }
+
+ return EINVAL;
+}
diff --git a/libstore/derive.c b/libstore/derive.c
new file mode 100644
index 00000000..a76fbe1c
--- /dev/null
+++ b/libstore/derive.c
@@ -0,0 +1,87 @@
+/* Calculation of various derived store fields
+
+ Copyright (C) 1995-97,2001 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <assert.h>
+#include <sys/types.h>
+#include <mach.h>
+
+#include "store.h"
+
+/* Fills in the values of the various fields in STORE that are derivable from
+ the set of runs & the block size. */
+void
+_store_derive (struct store *store)
+{
+ unsigned i;
+ struct store_run *runs = store->runs;
+ unsigned num_runs = store->num_runs;
+ size_t bsize = store->block_size;
+
+ /* BLOCK & SIZE */
+ store->blocks = 0;
+ store->wrap_src = 0;
+
+ for (i = 0; i < num_runs; i++)
+ {
+ store->wrap_src += runs[i].length;
+ if (runs[i].start >= 0) /* Not a hole */
+ store->blocks += runs[i].length;
+ }
+
+ if (store->end == 0)
+ /* END not set; set it using the info from RUNS. */
+ store->end = store->wrap_src;
+ else if (store->wrap_src < store->end)
+ /* A wrapped disk! RUNS is repeated N times to reach END. Adjust BLOCKS
+ to include all iterations. */
+ {
+ size_t num_iters = store->end / store->wrap_src;
+ store_offset_t last_part_base = num_iters * store->wrap_src;
+
+ store->blocks *= num_iters;
+
+ for (i = 0; i < num_runs; i++)
+ if (last_part_base + runs[i].length < store->end)
+ {
+ store->blocks += store->end - (last_part_base + runs[i].length);
+ break;
+ }
+ else if (runs[i].start >= 0)
+ store->blocks += runs[i].length;
+
+ /* WRAP_DST must be set by the caller. */
+ }
+
+ store->size = store->end * bsize;
+
+ store->log2_block_size = 0;
+ store->log2_blocks_per_page = 0;
+
+ if (bsize != 0)
+ {
+ while ((1 << store->log2_block_size) < bsize)
+ store->log2_block_size++;
+ assert ((1 << store->log2_block_size) == bsize);
+
+ while ((bsize << store->log2_blocks_per_page) < vm_page_size)
+ store->log2_blocks_per_page++;
+ assert ((bsize << store->log2_blocks_per_page) == vm_page_size);
+ }
+}
diff --git a/libstore/device.c b/libstore/device.c
new file mode 100644
index 00000000..3a72df48
--- /dev/null
+++ b/libstore/device.c
@@ -0,0 +1,342 @@
+/* Mach device store backend
+
+ Copyright (C) 1995,96,97,99,2001,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <hurd.h>
+
+#include "store.h"
+
+static inline error_t
+dev_error (error_t err)
+{
+ /* Give the canonical POSIX error codes,
+ rather than letting the Mach code propagate up. */
+ switch (err)
+ {
+ case D_IO_ERROR: return EIO;
+ case D_WOULD_BLOCK: return EAGAIN;
+ case D_NO_SUCH_DEVICE: return ENXIO;
+ case D_ALREADY_OPEN: return EBUSY;
+ case D_DEVICE_DOWN: return ENXIO; /* ? */
+ case D_INVALID_OPERATION: return EBADF; /* ? */
+ case D_NO_MEMORY: return ENOMEM;
+ default:
+ break;
+ }
+ /* Anything unexpected propagates up where weirdness will get noticed. */
+ return err;
+}
+
+static error_t
+dev_read (struct store *store,
+ store_offset_t addr, size_t index, mach_msg_type_number_t amount,
+ void **buf, mach_msg_type_number_t *len)
+{
+ return dev_error (device_read (store->port, 0, addr, amount,
+ (io_buf_ptr_t *)buf, len));
+}
+
+static error_t
+dev_write (struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf, mach_msg_type_number_t len,
+ mach_msg_type_number_t *amount)
+{
+ error_t err = dev_error (device_write (store->port, 0, addr,
+ (io_buf_ptr_t)buf, len,
+ (int *) amount));
+ *amount = *(int *) amount; /* stupid device.defs uses int */
+ return err;
+}
+
+static error_t
+dev_set_size (struct store *store, size_t newsize)
+{
+ return EOPNOTSUPP;
+}
+
+static error_t
+dev_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_std_leaf_decode (enc, _store_device_create, store);
+}
+
+static error_t
+dev_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ return dev_error (store_device_open (name, flags, store));
+}
+
+static error_t
+dopen (const char *name, device_t *device, int *mod_flags)
+{
+ device_t dev_master;
+ error_t err = get_privileged_ports (0, &dev_master);
+ if (! err)
+ {
+ if (*mod_flags & STORE_HARD_READONLY)
+ err = device_open (dev_master, D_READ, (char *)name, device);
+ else
+ {
+ err = device_open (dev_master, D_WRITE | D_READ, (char *)name, device);
+ if (err == ED_READ_ONLY)
+ {
+ err = device_open (dev_master, D_READ, (char *)name, device);
+ if (! err)
+ *mod_flags |= STORE_HARD_READONLY;
+ }
+ else if (! err)
+ *mod_flags &= ~STORE_HARD_READONLY;
+ }
+ mach_port_deallocate (mach_task_self (), dev_master);
+ }
+ return err;
+}
+
+static void
+dclose (struct store *store)
+{
+ mach_port_deallocate (mach_task_self (), store->port);
+ store->port = MACH_PORT_NULL;
+}
+
+/* Return 0 if STORE's range is enforce by the kernel, otherwise an error. */
+static error_t
+enforced (struct store *store)
+{
+ error_t err;
+ dev_status_data_t sizes;
+ size_t sizes_len = DEV_STATUS_MAX;
+
+ if (store->num_runs != 1 || store->runs[0].start != 0)
+ /* Can't enforce non-contiguous ranges, or one not starting at 0. */
+ return EINVAL;
+ else
+ /* See if the the current (one) range is that the kernel is enforcing. */
+ {
+#ifdef DEV_GET_RECORDS
+ err =
+ device_get_status (store->port, DEV_GET_RECORDS, sizes, &sizes_len);
+
+ if (err && err != D_INVALID_OPERATION)
+ return EINVAL;
+
+ if (!err)
+ {
+ assert (sizes_len == DEV_GET_RECORDS_COUNT);
+
+ if (sizes[DEV_GET_RECORDS_RECORD_SIZE] != store->block_size
+ || (store->runs[0].length !=
+ sizes[DEV_GET_RECORDS_DEVICE_RECORDS]))
+ return EINVAL;
+
+ return 0;
+ }
+ else
+#endif
+ {
+ sizes_len = DEV_STATUS_MAX;
+ err =
+ device_get_status (store->port, DEV_GET_SIZE, sizes, &sizes_len);
+
+ if (err)
+ return EINVAL;
+
+ assert (sizes_len == DEV_GET_SIZE_COUNT);
+
+ if (sizes[DEV_GET_SIZE_RECORD_SIZE] != store->block_size
+ || (store->runs[0].length !=
+ sizes[DEV_GET_SIZE_DEVICE_SIZE] >> store->log2_block_size))
+ return EINVAL;
+
+ return 0;
+ }
+ }
+}
+
+static error_t
+dev_set_flags (struct store *store, int flags)
+{
+ if ((flags & ~(STORE_INACTIVE | STORE_ENFORCED)) != 0)
+ /* Trying to set flags we don't support. */
+ return EINVAL;
+
+ if (! ((store->flags | flags) & STORE_INACTIVE))
+ /* Currently active and staying that way, so we must be trying to set the
+ STORE_ENFORCED flag. */
+ {
+ error_t err = enforced (store);
+ if (err)
+ return err;
+ }
+
+ if (flags & STORE_INACTIVE)
+ dclose (store);
+
+ store->flags |= flags; /* When inactive, anything goes. */
+
+ return 0;
+}
+
+static error_t
+dev_clear_flags (struct store *store, int flags)
+{
+ error_t err = 0;
+ if ((flags & ~(STORE_INACTIVE | STORE_ENFORCED)) != 0)
+ err = EINVAL;
+ if (!err && (flags & STORE_INACTIVE))
+ err = store->name ? dopen (store->name, &store->port, &store->flags) : ENODEV;
+ if (! err)
+ store->flags &= ~flags;
+ return err;
+}
+
+static error_t
+dev_map (const struct store *store, vm_prot_t prot, mach_port_t *memobj)
+{
+ size_t nruns = store->num_runs;
+
+ if (nruns > 1 || (nruns == 1 && store->runs[0].start != 0))
+ return EOPNOTSUPP;
+ else
+ {
+ /* Note that older Mach drivers (through GNU Mach 1.x) ignore
+ the OFFSET and SIZE parameters. The OSKit-Mach drivers obey
+ them, and so the size we pass must be large enough (or zero
+ only if the size is indeterminable). If using only the newer
+ drivers, we could remove the `start != 0' condition above and
+ support kernel mapping of partial devices. However, since
+ the older drivers silently ignore the OFFSET argument, that
+ would produce scrambled results on old kernels. */
+ error_t err = device_map (store->port, prot,
+ store->runs[0].start,
+ store->runs[0].length,
+ memobj, 0);
+ if (err == ED_INVALID_OPERATION)
+ err = EOPNOTSUPP; /* This device doesn't support paging. */
+ return err;
+ }
+}
+
+const struct store_class
+store_device_class =
+{
+ STORAGE_DEVICE, "device", dev_read, dev_write, dev_set_size,
+ store_std_leaf_allocate_encoding, store_std_leaf_encode, dev_decode,
+ dev_set_flags, dev_clear_flags, 0, 0, 0, dev_open, 0, dev_map
+};
+STORE_STD_CLASS (device);
+
+/* Return a new store in STORE referring to the mach device DEVICE. Consumes
+ the send right DEVICE. */
+error_t
+store_device_create (device_t device, int flags, struct store **store)
+{
+ struct store_run run;
+ size_t block_size = 0;
+ dev_status_data_t sizes;
+ size_t sizes_len = DEV_STATUS_MAX;
+ error_t err;
+
+#ifdef DEV_GET_RECORDS
+ err = device_get_status (device, DEV_GET_RECORDS, sizes, &sizes_len);
+ if (! err && sizes_len == DEV_GET_RECORDS_COUNT)
+ {
+ block_size = sizes[DEV_GET_RECORDS_RECORD_SIZE];
+
+ if (block_size)
+ {
+ run.start = 0;
+ run.length = sizes[DEV_GET_RECORDS_DEVICE_RECORDS];
+ }
+ }
+ else
+#endif
+ {
+ /* Some Mach devices do not implement device_get_status, but do not
+ return an error. To detect these devices we set the size of the
+ input buffer to something larger than DEV_GET_SIZE_COUNT. If the
+ size of the returned device status is not equal to
+ DEV_GET_SIZE_COUNT, we know that something is wrong. */
+ sizes_len = DEV_STATUS_MAX;
+ err = device_get_status (device, DEV_GET_SIZE, sizes, &sizes_len);
+ if (! err && sizes_len == DEV_GET_SIZE_COUNT)
+ {
+ block_size = sizes[DEV_GET_SIZE_RECORD_SIZE];
+
+ if (block_size)
+ {
+ run.start = 0;
+ run.length = sizes[DEV_GET_SIZE_DEVICE_SIZE] / block_size;
+
+ if (run.length * block_size != sizes[DEV_GET_SIZE_DEVICE_SIZE])
+ /* Bogus results (which some mach devices return). */
+ block_size = 0;
+ }
+ }
+ }
+
+ flags |= STORE_ENFORCED; /* 'cause it's the whole device. */
+
+ if (block_size == 0)
+ /* Treat devices that can't do device_get_status as zero-length. */
+ return _store_device_create (device, flags, 0, &run, 0, store);
+ else
+ /* Make a store with one run covering the whole device. */
+ return _store_device_create (device, flags, block_size, &run, 1, store);
+}
+
+/* Like store_device_create, but doesn't query the device for information. */
+error_t
+_store_device_create (device_t device, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ return
+ _store_create (&store_device_class, device, flags, block_size,
+ runs, num_runs, 0, store);
+}
+
+/* Open the device NAME, and return the corresponding store in STORE. */
+error_t
+store_device_open (const char *name, int flags, struct store **store)
+{
+ device_t device;
+ error_t err = dopen (name, &device, &flags);
+ if (! err)
+ {
+ err = store_device_create (device, flags, store);
+ if (! err)
+ {
+ err = store_set_name (*store, name);
+ if (err)
+ store_free (*store);
+ }
+ if (err)
+ mach_port_deallocate (mach_task_self (), device);
+ }
+ return err;
+}
diff --git a/libstore/do-bunzip2.c b/libstore/do-bunzip2.c
new file mode 100644
index 00000000..d2bc9da1
--- /dev/null
+++ b/libstore/do-bunzip2.c
@@ -0,0 +1,87 @@
+/* bunzip2 decompression
+
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Ignazio Sgalmuzzo <ignaker@gmail.com>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <bzlib.h>
+
+/* I/O interface */
+extern int (*unzip_read) (char *buf, size_t maxread);
+extern void (*unzip_write) (const char *buf, size_t nwrite);
+extern void (*unzip_read_error) (void);
+extern void (*unzip_error) (const char *msg);
+
+/* bzip2 doesn't require window sliding. Just for buffering. */
+#define INBUFSIZ 0x1000
+#define OUTBUFSIZ 0x1000
+
+static char inbuf[INBUFSIZ];
+static char outbuf[OUTBUFSIZ];
+
+#ifdef SMALL_BZIP2
+#define SMALL_MODE 1
+#else
+#define SMALL_MODE 0
+#endif
+
+void
+do_bunzip2 (void)
+{
+ int result;
+ bz_stream strm;
+
+ strm.bzalloc = NULL;
+ strm.bzfree = NULL;
+ strm.opaque = NULL;
+
+ strm.avail_in = 0;
+ strm.next_out = outbuf;
+ strm.avail_out = OUTBUFSIZ;
+
+ result = BZ2_bzDecompressInit (&strm, 0, SMALL_MODE);
+
+ while (result == BZ_OK)
+ {
+ if (strm.avail_in == 0)
+ {
+ strm.next_in = inbuf;
+ strm.avail_in = (*unzip_read)(strm.next_in, INBUFSIZ);
+
+ if (strm.avail_in == 0)
+ break;
+ }
+
+ result = BZ2_bzDecompress (&strm);
+
+ if ((result != BZ_OK) && (result != BZ_STREAM_END))
+ break;
+
+ if ((strm.avail_out == 0) || (result == BZ_STREAM_END))
+ {
+ (*unzip_write) (outbuf, OUTBUFSIZ - strm.avail_out);
+ strm.next_out = outbuf;
+ strm.avail_out = OUTBUFSIZ;
+ }
+ }
+
+ BZ2_bzDecompressEnd (&strm);
+
+ if (result != BZ_STREAM_END)
+ (*unzip_error) (NULL);
+}
diff --git a/libstore/do-gunzip.c b/libstore/do-gunzip.c
new file mode 100644
index 00000000..a9d019e7
--- /dev/null
+++ b/libstore/do-gunzip.c
@@ -0,0 +1,80 @@
+/* gzip decompression
+
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Ignazio Sgalmuzzo <ignaker@gmail.com>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <zlib.h>
+
+/* I/O interface */
+extern int (*unzip_read) (char *buf, size_t maxread);
+extern void (*unzip_write) (const char *buf, size_t nwrite);
+extern void (*unzip_read_error) (void);
+extern void (*unzip_error) (const char *msg);
+
+#define INBUFSIZ 0x1000
+#define OUTBUFSIZ 0x1000
+
+static unsigned char inbuf[INBUFSIZ];
+static unsigned char outbuf[OUTBUFSIZ];
+
+void
+do_gunzip (void)
+{
+ int result;
+ z_stream strm;
+
+ strm.zalloc = NULL;
+ strm.zfree = NULL;
+ strm.opaque = NULL;
+
+ strm.avail_in = 0;
+ strm.next_out = outbuf;
+ strm.avail_out = OUTBUFSIZ;
+
+ result = inflateInit2 (&strm, 32 + MAX_WBITS);
+
+ while (result == Z_OK)
+ {
+ if (strm.avail_in == 0)
+ {
+ strm.next_in = inbuf;
+ strm.avail_in = (*unzip_read)((char*) strm.next_in, INBUFSIZ);
+
+ if (strm.avail_in == 0)
+ break;
+ }
+
+ result = inflate (&strm, Z_NO_FLUSH);
+
+ if ((result != Z_OK) && (result != Z_STREAM_END))
+ break;
+
+ if ((strm.avail_out == 0) || (result == Z_STREAM_END))
+ {
+ (*unzip_write) ((char*) outbuf, OUTBUFSIZ - strm.avail_out);
+ strm.next_out = outbuf;
+ strm.avail_out = OUTBUFSIZ;
+ }
+ }
+
+ inflateEnd (&strm);
+
+ if (result != Z_STREAM_END)
+ (*unzip_error) (NULL);
+}
diff --git a/libstore/enc.c b/libstore/enc.c
new file mode 100644
index 00000000..d5002a0e
--- /dev/null
+++ b/libstore/enc.c
@@ -0,0 +1,98 @@
+/* Store wire encoding/decoding
+
+ Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+#include <sys/mman.h>
+
+#include "store.h"
+
+/* Initialize ENC. The given vector and sizes will be used for the encoding
+ if they are big enough (otherwise new ones will be automatically
+ allocated). */
+void
+store_enc_init (struct store_enc *enc,
+ mach_port_t *ports, mach_msg_type_number_t num_ports,
+ int *ints, mach_msg_type_number_t num_ints,
+ off_t *offsets, mach_msg_type_number_t num_offsets,
+ char *data, mach_msg_type_number_t data_len)
+{
+ bzero (enc, sizeof (*enc));
+
+ enc->ports = enc->init_ports = ports;
+ enc->num_ports = num_ports;
+ enc->ints = enc->init_ints = ints;
+ enc->num_ints = num_ints;
+ enc->offsets = enc->init_offsets = offsets;
+ enc->num_offsets = num_offsets;
+ enc->data = enc->init_data = data;
+ enc->data_len = data_len;
+}
+
+/* Deallocate storage used by the fields in ENC (but nothing is done with ENC
+ itself). */
+void
+store_enc_dealloc (struct store_enc *enc)
+{
+ if (enc->ports && enc->num_ports > 0)
+ /* For ports, we must deallocate each port as well. */
+ {
+ while (enc->cur_port < enc->num_ports)
+ {
+ mach_port_t port = enc->ports[enc->cur_port++];
+ if (port != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), port);
+ }
+
+ if (enc->ports != enc->init_ports)
+ munmap ((caddr_t) enc->ports, enc->num_ports * sizeof (*enc->ports));
+ }
+
+ if (enc->ints && enc->num_ints > 0 && enc->ints != enc->init_ints)
+ munmap ((caddr_t) enc->ints, enc->num_ints * sizeof (*enc->ints));
+
+ if (enc->offsets && enc->num_offsets > 0
+ && enc->offsets != enc->init_offsets)
+ munmap ((caddr_t) enc->offsets, enc->num_offsets * sizeof (*enc->offsets));
+
+ if (enc->data && enc->data_len > 0 && enc->data != enc->init_data)
+ munmap (enc->data, enc->data_len);
+
+ /* For good measure... */
+ bzero (enc, sizeof (*enc));
+}
+
+/* Copy out the parameters from ENC into the given variables suitably for
+ returning from a file_get_storage_info rpc, and deallocate ENC. */
+void
+store_enc_return (struct store_enc *enc,
+ mach_port_t **ports, mach_msg_type_number_t *num_ports,
+ int **ints, mach_msg_type_number_t *num_ints,
+ off_t **offsets, mach_msg_type_number_t *num_offsets,
+ char **data, mach_msg_type_number_t *data_len)
+{
+ *ports = enc->ports;
+ *num_ports = enc->num_ports;
+ *ints = enc->ints;
+ *num_ints = enc->num_ints;
+ *offsets = enc->offsets;
+ *num_offsets = enc->num_offsets;
+ *data = enc->data;
+ *data_len = enc->data_len;
+}
diff --git a/libstore/encode.c b/libstore/encode.c
new file mode 100644
index 00000000..df27250d
--- /dev/null
+++ b/libstore/encode.c
@@ -0,0 +1,178 @@
+/* Store wire encoding
+
+ Copyright (C) 1996,97,99,2001,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+#include <sys/mman.h>
+
+#include "store.h"
+
+/* Standard encoding used for most leaf store types. */
+
+error_t
+store_std_leaf_allocate_encoding (const struct store *store,
+ struct store_enc *enc)
+{
+ enc->num_ports++;
+ enc->num_ints += 6;
+ enc->num_offsets += store->num_runs * 2;
+ if (store->name)
+ enc->data_len += strlen (store->name) + 1;
+ enc->data_len += store->misc_len;
+ return 0;
+}
+
+/* The RPC protocol uses 32-bit off_t's, but store_offset_t is now 64 bits. */
+static inline int too_big (store_offset_t ofs)
+{
+ off_t o = (off_t) ofs;
+ return o < 0 || ((store_offset_t) o != ofs);
+}
+
+error_t
+store_std_leaf_encode (const struct store *store, struct store_enc *enc)
+{
+ int i;
+ size_t name_len = (store->name ? strlen (store->name) + 1 : 0);
+
+ enc->ports[enc->cur_port++] = store->port;
+
+ enc->ints[enc->cur_int++] = store->class->id;
+ enc->ints[enc->cur_int++] = store->flags;
+ enc->ints[enc->cur_int++] = store->block_size;
+ enc->ints[enc->cur_int++] = store->num_runs;
+ enc->ints[enc->cur_int++] = name_len;
+ enc->ints[enc->cur_int++] = store->misc_len;
+
+ for (i = 0; i < store->num_runs; i++)
+ {
+ if (sizeof (*enc->offsets) != sizeof (store->runs[i].start)
+ && (too_big (store->runs[i].start)
+ || too_big (store->runs[i].start + store->runs[i].length)))
+ return EOVERFLOW;
+ enc->offsets[enc->cur_offset++] = store->runs[i].start;
+ enc->offsets[enc->cur_offset++] = store->runs[i].length;
+ }
+
+ if (store->name)
+ {
+ bcopy (store->name, enc->data + enc->cur_data, name_len);
+ enc->cur_data += name_len;
+ }
+ if (store->misc_len)
+ {
+ bcopy (store->misc, enc->data + enc->cur_data, store->misc_len);
+ enc->cur_data += store->misc_len;
+ }
+
+ return 0;
+}
+
+/* Encode STORE into ENC, which should have been prepared with
+ store_enc_init, or return an error. The contents of ENC may then be
+ return as the value of file_get_storage_info; if for some reason this
+ can't be done, store_enc_dealloc may be used to deallocate the mmemory
+ used by the unsent vectors. */
+error_t
+store_encode (const struct store *store, struct store_enc *enc)
+{
+ void *buf;
+ error_t err;
+ const struct store_class *class = store->class;
+ /* We zero each vector length for the allocate_encoding method to work, so
+ save the old values. */
+ mach_msg_type_number_t init_num_ports = enc->num_ports;
+ mach_msg_type_number_t init_num_ints = enc->num_ints;
+ mach_msg_type_number_t init_num_offsets = enc->num_offsets;
+ mach_msg_type_number_t init_data_len = enc->data_len;
+
+ if (!class->allocate_encoding || !class->encode)
+ return EOPNOTSUPP;
+
+ enc->num_ports = 0;
+ enc->num_ints = 0;
+ enc->num_offsets = 0;
+ enc->data_len = 0;
+ err = (*class->allocate_encoding) (store, enc);
+ if (err)
+ return err;
+
+ errno = 0;
+ if (enc->num_ports > init_num_ports)
+ {
+ buf = mmap (0, enc->num_ports * sizeof *enc->ports,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (buf != (void *) -1)
+ enc->ports = buf;
+ }
+ if (!errno && enc->num_ints > init_num_ints)
+ {
+ buf = mmap (0, enc->num_ints * sizeof *enc->ints,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (buf != (void *) -1)
+ enc->ints = buf;
+ }
+ if (!errno && enc->num_offsets > init_num_offsets)
+ {
+ buf = mmap (0, enc->num_offsets * sizeof *enc->offsets,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (buf != (void *) -1)
+ enc->offsets = buf;
+
+ }
+ if (!errno && enc->data_len > init_data_len)
+ {
+ buf = mmap (0, enc->data_len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (buf != (void *) -1)
+ enc->data = buf;
+ }
+ err = errno;
+ if (! err)
+ err = (*class->encode) (store, enc);
+
+ enc->cur_port = enc->cur_int = enc->cur_offset = enc->cur_data = 0;
+
+ if (err)
+ store_enc_dealloc (enc);
+
+ return err;
+}
+
+/* Encode STORE into the given return variables, suitably for returning from a
+ file_get_storage_info rpc. */
+error_t
+store_return (const struct store *store,
+ mach_port_t **ports, mach_msg_type_number_t *num_ports,
+ int **ints, mach_msg_type_number_t *num_ints,
+ off_t **offsets, mach_msg_type_number_t *num_offsets,
+ char **data, mach_msg_type_number_t *data_len)
+{
+ error_t err;
+ struct store_enc enc;
+
+ store_enc_init (&enc, *ports, *num_ports, *ints, *num_ints,
+ *offsets, *num_offsets, *data, *data_len);
+ err = store_encode (store, &enc);
+ if (err)
+ store_enc_dealloc (&enc);
+ else
+ store_enc_return (&enc, ports, num_ports, ints, num_ints,
+ offsets, num_offsets, data, data_len);
+ return err;
+}
diff --git a/libstore/file.c b/libstore/file.c
new file mode 100644
index 00000000..49f1c3fb
--- /dev/null
+++ b/libstore/file.c
@@ -0,0 +1,303 @@
+/* File store backend
+
+ Copyright (C) 1995,96,97,98,2001, 2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <hurd.h>
+
+#include <hurd/io.h>
+
+#include "store.h"
+
+/* Return 0 if STORE's range is enforced by the filesystem, otherwise an
+ error. */
+static error_t
+enforced (struct store *store)
+{
+ if (store->num_runs != 1 || store->runs[0].start != 0)
+ /* Can't enforce non-contiguous ranges, or one not starting at 0. */
+ return EINVAL;
+ else
+ {
+ /* See if the the current (one) range is that the kernel is enforcing. */
+ struct stat st;
+ error_t err = io_stat (store->port, &st);
+
+ if (!err
+ && store->runs[0].length != (st.st_size >> store->log2_block_size))
+ /* The single run is not the whole file. */
+ err = EINVAL;
+
+ return err;
+ }
+}
+
+static error_t
+file_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount, void **buf,
+ size_t *len)
+{
+ size_t bsize = store->block_size;
+ return io_read (store->port, (char **)buf, len, addr * bsize, amount);
+}
+
+static error_t
+file_write (struct store *store,
+ store_offset_t addr, size_t index, const void *buf, size_t len,
+ size_t *amount)
+{
+ size_t bsize = store->block_size;
+ return io_write (store->port, buf, len, addr * bsize, amount);
+}
+
+static error_t
+file_store_set_size (struct store *store, size_t newsize)
+{
+ error_t err;
+
+ if (enforced (store) != 0)
+ /* Bail out if there is more than a single run. */
+ return EOPNOTSUPP;
+
+ err = file_set_size (store->port, newsize);
+
+ if (!err)
+ {
+ /* Update STORE's size and run. */
+ store->size = newsize;
+ store->runs[0].length = newsize >> store->log2_block_size;
+ }
+
+ return err;
+}
+
+static error_t
+file_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_std_leaf_decode (enc, _store_file_create, store);
+}
+
+static error_t
+file_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_file_open (name, flags, store);
+}
+
+static error_t
+fiopen (const char *name, file_t *file, int *mod_flags)
+{
+ if (*mod_flags & STORE_HARD_READONLY)
+ *file = file_name_lookup (name, O_RDONLY, 0);
+ else
+ {
+ *file = file_name_lookup (name, O_RDWR, 0);
+ if (*file == MACH_PORT_NULL
+ && (errno == EACCES || errno == EROFS))
+ {
+ *file = file_name_lookup (name, O_RDONLY, 0);
+ if (*file != MACH_PORT_NULL)
+ *mod_flags |= STORE_HARD_READONLY;
+ }
+ else if (*file != MACH_PORT_NULL)
+ *mod_flags &= ~STORE_HARD_READONLY;
+ }
+ return *file == MACH_PORT_NULL ? errno : 0;
+}
+
+static void
+ficlose (struct store *store)
+{
+ mach_port_deallocate (mach_task_self (), store->port);
+ store->port = MACH_PORT_NULL;
+}
+
+
+
+static error_t
+file_set_flags (struct store *store, int flags)
+{
+ if ((flags & ~(STORE_INACTIVE | STORE_ENFORCED)) != 0)
+ /* Trying to set flags we don't support. */
+ return EINVAL;
+
+ if (! ((store->flags | flags) & STORE_INACTIVE))
+ /* Currently active and staying that way, so we must be trying to set the
+ STORE_ENFORCED flag. */
+ {
+ error_t err = enforced (store);
+ if (err)
+ return err;
+ }
+
+ if (flags & STORE_INACTIVE)
+ ficlose (store);
+
+ store->flags |= flags; /* When inactive, anything goes. */
+
+ return 0;
+}
+
+static error_t
+file_clear_flags (struct store *store, int flags)
+{
+ error_t err = 0;
+ if ((flags & ~(STORE_INACTIVE | STORE_ENFORCED)) != 0)
+ err = EINVAL;
+ if (!err && (flags & STORE_INACTIVE))
+ err = store->name
+ ? fiopen (store->name, &store->port, &store->flags)
+ : ENOENT;
+ if (! err)
+ store->flags &= ~flags;
+ return err;
+}
+
+static error_t
+file_map (const struct store *store, vm_prot_t prot, mach_port_t *memobj)
+{
+ error_t err;
+ mach_port_t rd_memobj, wr_memobj;
+ int ro = (store->flags & STORE_HARD_READONLY);
+
+ if (store->num_runs != 1 || store->runs[0].start != 0)
+ return EOPNOTSUPP;
+
+ if ((prot & VM_PROT_WRITE) && ro)
+ return EACCES;
+
+ err = io_map (store->port, &rd_memobj, &wr_memobj);
+ if (err)
+ return err;
+
+ *memobj = rd_memobj;
+
+ if (ro && wr_memobj == MACH_PORT_NULL)
+ return 0;
+ else if (rd_memobj == wr_memobj)
+ {
+ if (rd_memobj != MACH_PORT_NULL)
+ mach_port_mod_refs (mach_task_self (), rd_memobj,
+ MACH_PORT_RIGHT_SEND, -1);
+ }
+ else
+ {
+ if (rd_memobj != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), rd_memobj);
+ if (wr_memobj != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), wr_memobj);
+ err = EOPNOTSUPP;
+ }
+
+ return err;
+}
+
+const struct store_class
+store_file_class =
+{
+ STORAGE_HURD_FILE, "file", file_read, file_write, file_store_set_size,
+ store_std_leaf_allocate_encoding, store_std_leaf_encode, file_decode,
+ file_set_flags, file_clear_flags, 0, 0, 0, file_open, 0, file_map
+};
+STORE_STD_CLASS (file);
+
+static error_t
+file_byte_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount,
+ void **buf, size_t *len)
+{
+ return io_read (store->port, (char **)buf, len, addr, amount);
+}
+
+static error_t
+file_byte_write (struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf, size_t len,
+ size_t *amount)
+{
+ return io_write (store->port, buf, len, addr, amount);
+}
+
+struct store_class
+store_file_byte_class =
+{
+ STORAGE_HURD_FILE, "file", file_byte_read, file_byte_write,
+ file_store_set_size,
+ store_std_leaf_allocate_encoding, store_std_leaf_encode, file_decode,
+ file_set_flags, file_clear_flags, 0, 0, 0, file_open, 0, file_map
+};
+
+/* Return a new store in STORE referring to the mach file FILE. Consumes
+ the send right FILE. */
+error_t
+store_file_create (file_t file, int flags, struct store **store)
+{
+ struct store_run run;
+ struct stat stat;
+ error_t err = io_stat (file, &stat);
+
+ if (err)
+ return err;
+
+ run.start = 0;
+ run.length = stat.st_size;
+
+ flags |= STORE_ENFORCED; /* 'cause it's the whole file. */
+
+ return _store_file_create (file, flags, 1, &run, 1, store);
+}
+
+/* Like store_file_create, but doesn't query the file for information. */
+error_t
+_store_file_create (file_t file, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ if (block_size == 1)
+ return _store_create (&store_file_byte_class,
+ file, flags, 1, runs, num_runs, 0, store);
+ else
+ return _store_create (&store_file_class,
+ file, flags, block_size, runs, num_runs, 0, store);
+}
+
+/* Open the file NAME, and return the corresponding store in STORE. */
+error_t
+store_file_open (const char *name, int flags, struct store **store)
+{
+ file_t file;
+ error_t err = fiopen (name, &file, &flags);
+ if (! err)
+ {
+ err = store_file_create (file, flags, store);
+ if (! err)
+ {
+ err = store_set_name (*store, name);
+ if (err)
+ store_free (*store);
+ }
+ if (err)
+ mach_port_deallocate (mach_task_self (), file);
+ }
+ return err;
+}
diff --git a/libstore/flags.c b/libstore/flags.c
new file mode 100644
index 00000000..d80181ac
--- /dev/null
+++ b/libstore/flags.c
@@ -0,0 +1,66 @@
+/* Setting various store flags
+
+ Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <malloc.h>
+#include <string.h>
+
+#include "store.h"
+
+/* Add FLAGS to STORE's currently set flags. */
+error_t
+store_set_flags (struct store *store, int flags)
+{
+ error_t err = 0;
+ int orig = store->flags, new = flags & ~orig;
+
+ if (new & STORE_BACKEND_FLAGS)
+ {
+ if (store->class->set_flags)
+ err = (*store->class->set_flags) (store, new);
+ else
+ err = EINVAL;
+ }
+
+ if (! err)
+ store->flags |= (new & ~STORE_BACKEND_FLAGS);
+
+ return err;
+}
+
+/* Remove FLAGS from STORE's currently set flags. */
+error_t
+store_clear_flags (struct store *store, int flags)
+{
+ error_t err = 0;
+ int orig = store->flags, kill = flags & orig;
+
+ if (kill & STORE_BACKEND_FLAGS)
+ {
+ if (store->class->clear_flags)
+ err = (*store->class->clear_flags) (store, kill);
+ else
+ err = EINVAL;
+ }
+
+ if (! err)
+ store->flags &= ~(kill & ~STORE_BACKEND_FLAGS);
+
+ return err;
+}
diff --git a/libstore/gunzip.c b/libstore/gunzip.c
new file mode 100644
index 00000000..f47c0b2b
--- /dev/null
+++ b/libstore/gunzip.c
@@ -0,0 +1,33 @@
+/* Decompressing store backend
+
+ Copyright (C) 1997, 1999, 2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <errno.h>
+
+extern void do_gunzip (void); /* Entry point to gunzip engine. */
+
+static error_t
+DO_UNZIP (void)
+{
+ do_gunzip ();
+ return 0;
+}
+
+#define UNZIP gunzip
+#include "unzipstore.c"
diff --git a/libstore/kids.c b/libstore/kids.c
new file mode 100644
index 00000000..901a7f85
--- /dev/null
+++ b/libstore/kids.c
@@ -0,0 +1,312 @@
+/* Managing sub-stores
+
+ Copyright (C) 1995,96,97,2001,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "store.h"
+
+/* Set STORE's current children list to (a copy of) CHILDREN and NUM_CHILDREN. */
+error_t
+store_set_children (struct store *store,
+ struct store *const *children, size_t num_children)
+{
+ unsigned size = num_children * sizeof (struct store *);
+ struct store **copy = malloc (size);
+
+ if (!copy)
+ return ENOMEM;
+
+ if (store->children)
+ free (store->children);
+
+ memcpy (copy, children, size);
+ store->children = copy;
+ store->num_children = num_children;
+
+ return 0;
+}
+
+/* Calls the allocate_encoding method in each child store of STORE,
+ propagating any errors. If any child does not hae such a method,
+ EOPNOTSUPP is returned. */
+error_t
+store_allocate_child_encodings (const struct store *store,
+ struct store_enc *enc)
+{
+ int i;
+ error_t err = 0;
+ for (i = 0; i < store->num_children && !err; i++)
+ {
+ struct store *k = store->children[i];
+ if (k->class->allocate_encoding)
+ (*k->class->allocate_encoding) (k, enc);
+ else
+ err = EOPNOTSUPP;
+ }
+ return err;
+}
+
+/* Calls the encode method in each child store of STORE, propagating any
+ errors. If any child does not hae such a method, EOPNOTSUPP is returned. */
+error_t
+store_encode_children (const struct store *store, struct store_enc *enc)
+{
+ int i;
+ error_t err = 0;
+ for (i = 0; i < store->num_children && !err; i++)
+ {
+ struct store *k = store->children[i];
+ if (k->class->encode)
+ (*k->class->encode) (k, enc);
+ else
+ err = EOPNOTSUPP;
+ }
+ return err;
+}
+
+/* Decodes NUM_CHILDREN from ENC, storing the results into successive
+ positions in CHILDREN. */
+error_t
+store_decode_children (struct store_enc *enc, int num_children,
+ const struct store_class *const *classes,
+ struct store **children)
+{
+ int i;
+ error_t err = 0;
+ for (i = 0; i < num_children && !err; i++)
+ err = store_decode (enc, classes, &children[i]);
+ if (err)
+ /* Deallocate anything we've already created. */
+ while (--i >= 0)
+ store_free (children[i]);
+ return err;
+}
+
+/* Set FLAGS in all children of STORE, and if successful, add FLAGS to
+ STORE's flags. */
+error_t
+store_set_child_flags (struct store *store, int flags)
+{
+ int i;
+ error_t err = 0;
+ int old_child_flags[store->num_children];
+
+ for (i = 0; i < store->num_children && !err; i++)
+ {
+ old_child_flags[i] = store->children[i]->flags;
+ err = store_set_flags (store->children[i], flags);
+ }
+
+ if (err)
+ while (i-- > 0)
+ store_clear_flags (store->children[i], flags & ~old_child_flags[i]);
+ else
+ store->flags |= flags;
+
+ return err;
+}
+
+/* Clear FLAGS in all children of STORE, and if successful, remove FLAGS from
+ STORE's flags. */
+error_t
+store_clear_child_flags (struct store *store, int flags)
+{
+ int i;
+ error_t err = 0;
+ int old_child_flags[store->num_children];
+
+ for (i = 0; i < store->num_children && !err; i++)
+ {
+ old_child_flags[i] = store->children[i]->flags;
+ err = store_clear_flags (store->children[i], flags);
+ }
+
+ if (err)
+ while (i-- > 0)
+ store_set_flags (store->children[i], flags & ~old_child_flags[i]);
+ else
+ store->flags &= ~flags;
+
+ return err;
+}
+
+/* Parse multiple store names in NAME, and open each individually, returning
+ all in the vector STORES, and the number in NUM_STORES. The syntax of
+ NAME is a single non-alpha-numeric separator character, followed by each
+ child store name separated by the same separator; each child name is
+ TYPE:NAME notation as parsed by store_typed_open. If every child uses the
+ same TYPE: prefix, then it may be factored out and put before the child
+ list instead (the two types of notation are differentiated by whether the
+ first character of name is alpha-numeric or not). */
+error_t
+store_open_children (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store ***stores, size_t *num_stores)
+{
+ char *pfx = 0; /* Prefix applied to each part name. */
+ size_t pfx_len = 0; /* Space PFX + separator takes up. */
+ char sep = *name; /* Character separating individual names. */
+
+ if (sep && isalnum (sep))
+ /* If the first character is a `name' character, it's likely to be either
+ a type prefix (e.g, TYPE:@NAME1@NAME2@), so we distribute the type
+ prefix among the elements (@TYPE:NAME1@TYPE:NAME2@). */
+ {
+ const char *pfx_end = name;
+
+ while (isalnum (*pfx_end))
+ pfx_end++;
+
+ if (*pfx_end++ != ':')
+ return EINVAL;
+
+ /* Make a copy of the prefix. */
+ pfx = strndupa (name, pfx_end - name);
+ pfx_len = pfx_end - name;
+
+ sep = *pfx_end;
+ }
+
+ if (sep)
+ /* Parse a list of store specs separated by SEP. */
+ {
+ int k;
+ const char *p, *end;
+ error_t err = 0;
+ size_t count = 0;
+
+ /* First, see how many there are. */
+ for (p = name; p && p[1]; p = strchr (p + 1, sep))
+ count++;
+
+ /* Make a vector to hold them. */
+ *stores = malloc (count * sizeof (struct store *));
+ *num_stores = count;
+ if (! *stores)
+ return ENOMEM;
+
+ bzero (*stores, count * sizeof (struct store *));
+
+ /* Open each child store. */
+ for (p = name, k = 0; !err && p && p[1]; p = end, k++)
+ {
+ size_t kname_len;
+
+ end = strchr (p + 1, sep);
+ kname_len = (end ? end - p - 1 : strlen (p + 1));
+
+ {
+ /* Allocate temporary child name on the stack. */
+ char kname[pfx_len + kname_len + 1];
+
+ if (pfx)
+ /* Add type prefix to child name. */
+ memcpy (kname, pfx, pfx_len);
+
+ memcpy (kname + pfx_len, p + 1, kname_len);
+ kname[pfx_len + kname_len] = '\0';
+
+ err = store_typed_open (kname, flags, classes, &(*stores)[k]);
+ }
+ }
+
+ if (err)
+ /* Failure opening some child, deallocate what we've done so far. */
+ {
+ while (--k >= 0)
+ store_free ((*stores)[k]);
+ free (*stores);
+ }
+
+ return err;
+ }
+ else
+ /* Empty list. */
+ {
+ *stores = 0;
+ *num_stores = 0;
+ return 0;
+ }
+}
+
+/* Try to come up with a name for the children in STORE, combining the names
+ of each child in a way that could be used to parse them with
+ store_open_children. This is done heuristically, and so may not succeed.
+ If a child doesn't have a name, EINVAL is returned. */
+error_t
+store_children_name (const struct store *store, char **name)
+{
+ static char try_seps[] = "@+=,._%|;^!~'&";
+ struct store **kids = store->children;
+ size_t num_kids = store->num_children;
+
+ if (num_kids == 0)
+ {
+ *name = strdup ("");
+ return *name ? 0 : ENOMEM;
+ }
+ else
+ {
+ int k;
+ char *s; /* Current separator in search for one. */
+ int fail; /* If we couldn't use *S as as sep. */
+ size_t total_len = 0; /* Length of name we will return. */
+
+ /* Detect children without names, and calculate the total length of the
+ name we will return (which is the sum of the lengths of the child
+ names plus room for the types and separator characters. */
+ for (k = 0; k < num_kids; k++)
+ if (!kids[k] || !kids[k]->name)
+ return EINVAL;
+ else
+ total_len +=
+ /* separator + type name + type separator + child name */
+ 1 + strlen (kids[k]->class->name) + 1 + strlen (kids[k]->name);
+
+ /* Look for a separator character from those in TRY_SEPS that doesn't
+ occur in any of the the child names. */
+ for (s = try_seps, fail = 1; *s && fail; s++)
+ for (k = 0, fail = 0; k < num_kids && !fail; k++)
+ if (strchr (kids[k]->name, *s))
+ fail = 1;
+
+ if (*s)
+ /* We found a usable separator! */
+ {
+ char *p = malloc (total_len + 1);
+
+ if (! p)
+ return ENOMEM;
+ *name = p;
+
+ for (k = 0; k < num_kids; k++)
+ p +=
+ sprintf (p, "%c%s:%s", *s, kids[k]->class->name, kids[k]->name);
+
+ return 0;
+ }
+ else
+ return EGRATUITOUS;
+ }
+}
diff --git a/libstore/make.c b/libstore/make.c
new file mode 100644
index 00000000..8f289533
--- /dev/null
+++ b/libstore/make.c
@@ -0,0 +1,100 @@
+/* Store allocation/deallocation
+
+ Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <malloc.h>
+
+#include "store.h"
+
+/* Allocate a new store structure with meths METHS, and the various other
+ fields initialized to the given parameters. */
+error_t
+_store_create (const struct store_class *class,
+ mach_port_t port, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ store_offset_t end, struct store **store)
+{
+ if ((block_size & (block_size - 1)) || (block_size == 0 && num_runs > 0))
+ return EINVAL; /* block size not a power of two. */
+ else
+ {
+ struct store *new = malloc (sizeof (struct store));
+ if (new)
+ {
+ error_t err;
+
+ new->name = 0;
+ new->port = port;
+ new->runs = 0;
+ new->num_runs = 0;
+ new->wrap_src = 0;
+ new->wrap_dst = 0;
+ new->flags = flags;
+ new->end = end;
+ new->block_size = block_size;
+ new->source = MACH_PORT_NULL;
+ new->blocks = 0;
+ new->size = 0;
+ new->log2_block_size = 0;
+ new->log2_blocks_per_page = 0;
+ new->misc = 0;
+ new->misc_len = 0;
+ new->hook = 0;
+ new->children = 0;
+ new->num_children = 0;
+
+ new->class = class;
+
+ /* store_set_runs calls _store_derive to derive other fields. */
+ err = store_set_runs (new, runs, num_runs);
+ if (err)
+ free (new);
+ else
+ *store = new;
+
+ return err;
+ }
+ else
+ return ENOMEM;
+ }
+}
+
+void
+store_free (struct store *store)
+{
+ int k;
+
+ if (store->class->cleanup)
+ (*store->class->cleanup) (store);
+
+ for (k = 0; k < store->num_children; k++)
+ store_free (store->children[k]);
+
+ if (store->port != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), store->port);
+ if (store->source != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), store->source);
+
+ if (store->name)
+ free (store->name);
+ if (store->runs)
+ free (store->runs);
+
+ free (store);
+}
diff --git a/libstore/map.c b/libstore/map.c
new file mode 100644
index 00000000..b7533cbb
--- /dev/null
+++ b/libstore/map.c
@@ -0,0 +1,79 @@
+/* Direct store mapping
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <hurd.h>
+#include <hurd/io.h>
+
+#include "store.h"
+
+/* Return a memory object paging on STORE. [among other reasons,] this may
+ fail because store contains non-contiguous regions on the underlying
+ object. In such a case you can try calling some of the routines below to
+ get a pager. */
+error_t
+store_map (const struct store *store, vm_prot_t prot,
+ mach_port_t *memobj)
+{
+ error_t (*map) (const struct store *store, vm_prot_t prot,
+ mach_port_t *memobj) =
+ store->class->map;
+ error_t err = map ? (*map) (store, prot, memobj) : EOPNOTSUPP;
+
+ if (err == EOPNOTSUPP && store->source != MACH_PORT_NULL)
+ /* Can't map the store directly, but we know it represents the file
+ STORE->source, so we can try mapping that instead. */
+ {
+ mach_port_t rd_memobj, wr_memobj;
+ int ro = (store->flags & STORE_HARD_READONLY);
+
+ if ((prot & VM_PROT_WRITE) && ro)
+ return EACCES;
+
+ err = io_map (store->port, &rd_memobj, &wr_memobj);
+ if (! err)
+ {
+ *memobj = rd_memobj;
+
+ if (!ro || wr_memobj != MACH_PORT_NULL)
+ /* If either we or the server think this object is writable, then
+ the write-memory-object must be the same as the read one (if
+ we only care about reading, then it can be null too). */
+ {
+ if (rd_memobj == wr_memobj)
+ {
+ if (rd_memobj != MACH_PORT_NULL)
+ mach_port_mod_refs (mach_task_self (), rd_memobj,
+ MACH_PORT_RIGHT_SEND, -1);
+ }
+ else
+ {
+ if (rd_memobj != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), rd_memobj);
+ if (wr_memobj != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), wr_memobj);
+ err = EOPNOTSUPP;
+ }
+ }
+ }
+ }
+
+ return err;
+}
diff --git a/libstore/memobj.c b/libstore/memobj.c
new file mode 100644
index 00000000..0d5c816e
--- /dev/null
+++ b/libstore/memobj.c
@@ -0,0 +1,197 @@
+/* Store backend using a Mach memory object
+ Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "store.h"
+#include <hurd.h>
+#include <hurd/sigpreempt.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <assert.h>
+
+
+/* Return a new store in STORE referring to the memory object MEMOBJ.
+ Consumes the send right MEMOBJ. */
+error_t
+store_memobj_create (memory_object_t memobj, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ return _store_create (&store_memobj_class,
+ memobj, flags, block_size, runs, num_runs, 0, store);
+}
+
+
+/* This one is pretty easy. */
+static error_t
+memobj_map (const struct store *store, vm_prot_t prot, mach_port_t *memobj)
+{
+ *memobj = store->port;
+ return mach_port_mod_refs (mach_task_self (), *memobj,
+ MACH_PORT_RIGHT_SEND, +1);
+}
+
+
+static error_t /* swiped from libpager's pager_memcpy */
+memobj_memcpy (memory_object_t memobj,
+ vm_offset_t offset, void *other, size_t *size,
+ vm_prot_t prot)
+{
+ vm_address_t window = 0;
+ vm_size_t windowsize = 8 * vm_page_size;
+ size_t to_copy = *size;
+ error_t err;
+
+ error_t copy (struct hurd_signal_preemptor *preemptor)
+ {
+ while (to_copy > 0)
+ {
+ size_t pageoff = offset & (vm_page_size - 1);
+
+ if (window)
+ /* Deallocate the old window. */
+ munmap ((caddr_t) window, windowsize);
+
+ /* Map in and copy a standard-sized window, unless that is
+ more than the total left to be copied. */
+
+ if (windowsize > pageoff + to_copy)
+ windowsize = pageoff + to_copy;
+
+ window = 0;
+ err = vm_map (mach_task_self (), &window, windowsize, 0, 1,
+ memobj, offset - pageoff, 0,
+ prot, prot, VM_INHERIT_NONE);
+ if (err)
+ return 0;
+
+ /* Realign the fault preemptor for the new mapping window. */
+ preemptor->first = window;
+ preemptor->last = window + windowsize;
+
+ if (prot == VM_PROT_READ)
+ memcpy (other, (const void *) window + pageoff,
+ windowsize - pageoff);
+ else
+ memcpy ((void *) window + pageoff, other, windowsize - pageoff);
+
+ offset += windowsize - pageoff;
+ other += windowsize - pageoff;
+ to_copy -= windowsize - pageoff;
+ }
+ return 0;
+ }
+
+ jmp_buf buf;
+ void fault (int signo, long int sigcode, struct sigcontext *scp)
+ {
+ assert (scp->sc_error == EKERN_MEMORY_ERROR);
+ err = EIO;
+ to_copy -= sigcode - window;
+ longjmp (buf, 1);
+ }
+
+ if (to_copy == 0)
+ /* Short-circuit return if nothing to do.
+ ERR would not be initialized by the copy loop in this case. */
+ return 0;
+
+ if (setjmp (buf) == 0)
+ hurd_catch_signal (sigmask (SIGSEGV) | sigmask (SIGBUS),
+ window, window + windowsize,
+ &copy, (sighandler_t) &fault);
+
+ if (window)
+ munmap ((caddr_t) window, windowsize);
+
+ *size -= to_copy;
+
+ return err;
+}
+
+static error_t
+memobj_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount,
+ void **buf, size_t *len)
+{
+ addr <<= store->log2_block_size;
+ if (((size_t) addr & (vm_page_size - 1)) == 0)
+ {
+ *len = amount;
+ return vm_map (mach_task_self (), (vm_address_t *) buf, amount,
+ 0, 1, store->port, addr << store->log2_block_size, 0,
+ VM_PROT_READ, VM_PROT_ALL, VM_INHERIT_NONE);
+ }
+ else
+ {
+ error_t err;
+ int alloced = 0;
+ if (*len < amount)
+ {
+ *buf = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*buf == MAP_FAILED)
+ return errno;
+ alloced = 1;
+ }
+ *len = amount;
+ err = memobj_memcpy (store->port, addr, *buf, len, VM_PROT_READ);
+ if (err && alloced)
+ munmap (*buf, amount);
+ else if (alloced && round_page (*len) < round_page (amount))
+ munmap (*buf + round_page (*len),
+ round_page (amount) - round_page (*len));
+ return err;
+ }
+}
+
+static error_t
+memobj_write (struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf, size_t len, size_t *amount)
+{
+ *amount = len;
+ return memobj_memcpy (store->port, addr << store->log2_block_size,
+ (void *) buf, amount, VM_PROT_WRITE);
+}
+
+static error_t
+memobj_set_size (struct store *store, size_t newsize)
+{
+ return EOPNOTSUPP;
+}
+
+static error_t
+memobj_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_std_leaf_decode (enc, store_memobj_create, store);
+}
+
+const struct store_class
+store_memobj_class =
+{
+ STORAGE_MEMORY, "memobj",
+ map: memobj_map,
+ read: memobj_read,
+ set_size: memobj_set_size,
+ write: memobj_write,
+ allocate_encoding: store_std_leaf_allocate_encoding,
+ encode: store_std_leaf_encode,
+ decode: memobj_decode,
+};
+STORE_STD_CLASS (memobj);
diff --git a/libstore/module.c b/libstore/module.c
new file mode 100644
index 00000000..6e75099a
--- /dev/null
+++ b/libstore/module.c
@@ -0,0 +1,178 @@
+/* Dynamic loading of store class modules
+ Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "store.h"
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <error.h> /* XXX */
+
+static error_t
+open_class (int need_open,
+ const char *name, const char *clname_end,
+ const struct store_class **classp)
+{
+ char *modname, *clsym;
+ void *mod;
+
+ /* Construct the name of the shared object for this module. */
+ if (asprintf (&modname,
+ "libstore_%.*s%s", (int) (clname_end - name), name,
+ STORE_SONAME_SUFFIX) < 0)
+ return ENOMEM;
+
+ /* Now try to load the module.
+
+ Note we never dlclose the module, and add a ref every time we open it
+ anew. We can't dlclose it until no stores of this class exist, so
+ we'd need a creation/deletion hook for that. */
+
+ errno = 0;
+ mod = dlopen (modname, RTLD_LAZY);
+ if (mod == NULL)
+ {
+ const char *errstring = dlerror (); /* Must always call or it leaks! */
+ if (errno != ENOENT)
+ /* XXX not good, but how else to report the error? */
+ error (0, 0, "cannot load %s: %s", modname, errstring);
+ }
+ free (modname);
+ if (mod == NULL)
+ return errno ?: ENOENT;
+
+ if (asprintf (&clsym, "store_%.*s_class",
+ (int) (clname_end - name), name) < 0)
+ {
+ dlclose (mod);
+ return ENOMEM;
+ }
+
+ *classp = dlsym (mod, clsym);
+ free (clsym);
+ if (*classp == NULL)
+ {
+ error (0, 0, "invalid store module %.*s: %s",
+ (int) (clname_end - name), name, dlerror ());
+ dlclose (mod);
+ return EGRATUITOUS;
+ }
+
+ if (need_open && ! (*classp)->open)
+ {
+ /* This class cannot be opened as needed. */
+ dlclose (mod);
+ return EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+error_t
+store_module_find_class (const char *name, const char *clname_end,
+ const struct store_class **classp)
+{
+ return open_class (0, name, clname_end, classp);
+}
+
+error_t
+store_module_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ const struct store_class *cl;
+ const char *clname_end = strchrnul (name, ':');
+ error_t err;
+
+ err = open_class (1, name, clname_end, &cl);
+ if (err)
+ return err;
+
+ if (*clname_end)
+ /* Skip the ':' separating the class-name from the device name. */
+ clname_end++;
+
+ if (! *clname_end)
+ /* The class-specific portion of the name is empty, so make it *really*
+ empty. */
+ clname_end = 0;
+
+ return (*cl->open) (clname_end, flags, classes, store);
+}
+
+const struct store_class store_module_open_class =
+{ -1, "module", open: store_module_open };
+STORE_STD_CLASS (module_open);
+
+error_t
+store_module_decode (struct store_enc *enc,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ char *modname;
+ void *mod;
+ const struct store_class *const *cl, *const *clend;
+ enum file_storage_class id;
+
+ if (enc->cur_int >= enc->num_ints)
+ /* The first int should always be the type. */
+ return EINVAL;
+
+ id = enc->ints[enc->cur_int];
+
+ /* Construct the name of the shared object for this module. */
+ if (asprintf (&modname, "libstore_type-%d%s", id, STORE_SONAME_SUFFIX) < 0)
+ return ENOMEM;
+
+ /* Try to open the module. */
+ mod = dlopen (modname, RTLD_LAZY);
+ free (modname);
+ if (mod == NULL)
+ {
+ (void) dlerror (); /* otherwise it leaks */
+ return ENOENT;
+ }
+
+ /* Now find its "store_std_classes" section, which points to each
+ `struct store_class' defined in this module. */
+ cl = dlsym (mod, "__start_store_std_classes");
+ if (cl == NULL)
+ {
+ error (0, 0, "invalid store module type-%d: %s", id, dlerror ());
+ dlclose (mod);
+ return EGRATUITOUS;
+ }
+ clend = dlsym (mod, "__stop_store_std_classes");
+ if (clend == NULL)
+ {
+ error (0, 0, "invalid store module type-%d: %s", id, dlerror ());
+ dlclose (mod);
+ return EGRATUITOUS;
+ }
+
+ while (cl < clend)
+ if ((*cl)->decode && (*cl)->id == id)
+ return (*(*cl)->decode) (enc, classes, store);
+ else
+ ++cl;
+
+ /* This class cannot be opened via store_decode. */
+ dlclose (mod);
+ return EOPNOTSUPP;
+}
diff --git a/libstore/mvol.c b/libstore/mvol.c
new file mode 100644
index 00000000..d243cc8a
--- /dev/null
+++ b/libstore/mvol.c
@@ -0,0 +1,158 @@
+/* Multiple-volume store backend
+
+ Copyright (C) 1996,97,2001, 2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "store.h"
+
+struct mvol_state
+{
+ /* The current `volume'. */
+ ssize_t cur_vol;
+
+ /* A function to change volumes, making NEW_VOL readable on the store
+ instead of OLD_VOL. OLD_VOL is initially -1, */
+ error_t (*swap_vols) (struct store *store, size_t new_vol, ssize_t old_vol);
+};
+
+static error_t
+ensure_vol (struct store *store, size_t vol)
+{
+ error_t err = 0;
+ struct mvol_state *mv = store->hook;
+ if (vol != mv->cur_vol)
+ {
+ err = (*mv->swap_vols) (store, vol, mv->cur_vol);
+ if (! err)
+ mv->cur_vol = vol;
+ }
+ return err;
+}
+
+static error_t
+mvol_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount,
+ void **buf, size_t *len)
+{
+ error_t err = ensure_vol (store, index);
+ if (! err)
+ err = store_read (store->children[0], addr, amount, buf, len);
+ return err;
+}
+
+static error_t
+mvol_write (struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf, size_t len, size_t *amount)
+{
+ error_t err = ensure_vol (store, index);
+ if (! err)
+ err = store_write (store->children[0], addr, buf, len, amount);
+ return err;
+}
+
+static error_t
+mvol_set_size (struct store *store, size_t newsize)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+mvol_remap (struct store *source,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ return store_remap_create (source, runs, num_runs, 0, store);
+}
+
+const struct store_class
+store_mvol_class =
+{
+ -1, "mvol", mvol_read, mvol_write, mvol_set_size,
+ 0, 0, 0,
+ store_set_child_flags, store_clear_child_flags, 0, 0, mvol_remap
+};
+STORE_STD_CLASS (mvol);
+
+/* Return a new store in STORE that multiplexes multiple physical volumes
+ from PHYS as one larger virtual volume. SWAP_VOLS is a function that will
+ be called whenever the volume currently active isn't correct. PHYS is
+ consumed. */
+error_t
+store_mvol_create (struct store *phys,
+ error_t (*swap_vols) (struct store *store, size_t new_vol,
+ ssize_t old_vol),
+ int flags,
+ struct store **store)
+{
+ error_t err;
+ struct store_run run;
+
+ run.start = 0;
+ run.length = phys->end;
+
+ err = _store_create (&store_mvol_class, MACH_PORT_NULL,
+ flags | phys->flags, phys->block_size,
+ &run, 1, 0, store);
+ if (! err)
+ {
+ struct mvol_state *mv = malloc (sizeof (struct mvol_state));
+ if (mv)
+ {
+ mv->swap_vols = swap_vols;
+ mv->cur_vol = -1;
+ (*store)->hook = mv;
+ }
+ else
+ err = ENOMEM;
+
+ if (! err)
+ err = store_set_children (*store, &phys, 1);
+
+ if (! err)
+ {
+ if (phys->name)
+ {
+ size_t nlen =
+ strlen (phys->class->name) + 1 + strlen (phys->name) + 1;
+ char *name = malloc (nlen);
+
+ if (name)
+ {
+ snprintf (name, nlen, "%s:%s", phys->class->name, phys->name);
+ (*store)->name = name;
+ }
+ else
+ err = ENOMEM;
+ }
+ }
+
+ if (err)
+ {
+ if (mv)
+ free (mv);
+ store_free (*store);
+ }
+ }
+
+ return err;
+}
diff --git a/libstore/nbd.c b/libstore/nbd.c
new file mode 100644
index 00000000..3138af01
--- /dev/null
+++ b/libstore/nbd.c
@@ -0,0 +1,529 @@
+/* "Network Block Device" store backend compatible with Linux `nbd' driver
+ Copyright (C) 2001, 2002, 2008 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "store.h"
+#include <hurd.h>
+#include <hurd/io.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+
+// Avoid dragging in the resolver when linking statically.
+#pragma weak gethostbyname
+
+
+/* The nbd protocol, such as it is, is not really specified anywhere.
+ These message layouts and constants were culled from the nbd-server and
+ Linux kernel nbd module sources. */
+
+#define NBD_INIT_MAGIC "NBDMAGIC\x00\x00\x42\x02\x81\x86\x12\x53"
+
+#define NBD_REQUEST_MAGIC (htonl (0x25609513))
+#define NBD_REPLY_MAGIC (htonl (0x67446698))
+
+#define NBD_IO_MAX 10240
+
+struct nbd_startup
+{
+ char magic[16]; /* NBD_INIT_MAGIC */
+ uint64_t size; /* size in bytes, 64 bits in net order */
+ char reserved[128]; /* zeros, we don't check it */
+};
+
+struct nbd_request
+{
+ uint32_t magic; /* NBD_REQUEST_MAGIC */
+ uint32_t type; /* 0 read, 1 write, 2 disconnect */
+ uint64_t handle; /* returned in reply */
+ uint64_t from;
+ uint32_t len;
+} __attribute__ ((packed));
+
+struct nbd_reply
+{
+ uint32_t magic; /* NBD_REPLY_MAGIC */
+ uint32_t error;
+ uint64_t handle; /* value from request */
+} __attribute__ ((packed));
+
+
+/* i/o functions. */
+
+#if BYTE_ORDER == BIG_ENDIAN
+# define htonll(x) (x)
+#elif BYTE_ORDER == LITTLE_ENDIAN
+# include <byteswap.h>
+# define htonll(x) (bswap_64 (x))
+#else
+# error what endian?
+#endif
+#define ntohll htonll
+
+
+static inline error_t
+read_reply (struct store *store, uint64_t handle)
+{
+ struct nbd_reply reply;
+ char *buf = (void *) &reply;
+ mach_msg_type_number_t cc = sizeof reply;
+ error_t err = io_read (store->port, &buf, &cc, -1, cc);
+ if (err)
+ return err;
+ if (buf != (void *) &reply)
+ {
+ memcpy (&reply, buf, sizeof reply);
+ munmap (buf, cc);
+ }
+ if (cc != sizeof reply)
+ return EIO;
+ if (reply.magic != NBD_REPLY_MAGIC)
+ return EIO;
+ if (reply.handle != handle)
+ return EIO;
+ if (reply.error != 0)
+ return EIO;
+ return 0;
+}
+
+static error_t
+nbd_write (struct store *store,
+ store_offset_t addr, size_t index, const void *buf, size_t len,
+ size_t *amount)
+{
+ struct nbd_request req =
+ {
+ magic: NBD_REQUEST_MAGIC,
+ type: htonl (1), /* WRITE */
+ };
+ error_t err;
+ mach_msg_type_number_t cc;
+
+ addr <<= store->log2_block_size;
+ *amount = 0;
+
+ do
+ {
+ size_t chunk = len < NBD_IO_MAX ? len : NBD_IO_MAX, nwrote;
+ req.from = htonll (addr);
+ req.len = htonl (chunk);
+
+ err = io_write (store->port, (char *) &req, sizeof req, -1, &cc);
+ if (err)
+ return err;
+ if (cc != sizeof req)
+ return EIO;
+
+ nwrote = 0;
+ do
+ {
+ err = io_write (store->port, (char *) buf, chunk - nwrote, -1, &cc);
+ if (err)
+ return err;
+ buf += cc;
+ nwrote += cc;
+ } while (nwrote < chunk);
+
+ err = read_reply (store, req.handle);
+ if (err)
+ return err;
+
+ addr += chunk;
+ *amount += chunk;
+ len -= chunk;
+ } while (len > 0);
+
+ return 0;
+}
+
+static error_t
+nbd_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount,
+ void **buf, size_t *len)
+{
+ struct nbd_request req =
+ {
+ magic: NBD_REQUEST_MAGIC,
+ type: htonl (0), /* READ */
+ };
+ error_t err;
+ size_t ofs, chunk;
+ char *databuf, *piecebuf;
+ size_t databuflen, piecelen;
+
+ /* Send a request for the largest possible piece of remaining data and
+ read the first piece of its reply into PIECEBUF, PIECELEN. The amount
+ requested can be found in CHUNK. */
+ inline error_t request_chunk (char **buf, size_t *len)
+ {
+ mach_msg_type_number_t cc;
+
+ chunk = (amount - ofs) < NBD_IO_MAX ? (amount - ofs) : NBD_IO_MAX;
+
+ req.from = htonll (addr);
+ req.len = htonl (chunk);
+
+ /* Advance ADDR immediately, so it always points past what we've
+ already requested. */
+ addr += chunk;
+
+ return (io_write (store->port, (char *) &req, sizeof req, -1, &cc) ?
+ : cc != sizeof req ? EIO
+ : read_reply (store, req.handle) ?
+ : io_read (store->port, buf, len, (off_t) -1, chunk));
+ }
+
+ addr <<= store->log2_block_size;
+
+ /* Read the first piece, which can go directly into the caller's buffer. */
+ databuf = *buf;
+ piecelen = databuflen = *len;
+ err = request_chunk (&databuf, &piecelen);
+ if (err)
+ return err;
+ if (databuflen >= amount)
+ {
+ /* That got it all. We're done. */
+ *buf = databuf;
+ *len = piecelen;
+ return 0;
+ }
+
+ /* We haven't read the entire amount yet. */
+ ofs = 0;
+ do
+ {
+ /* Account for what we just read. */
+ ofs += piecelen;
+ chunk -= piecelen;
+ if (ofs == amount)
+ {
+ /* That got it all. We're done. */
+ *buf = databuf;
+ *len = ofs;
+ return 0;
+ }
+
+ /* Now we'll read another piece of the data, hopefully
+ into the latter part of the existing buffer. */
+ piecebuf = databuf + ofs;
+ piecelen = databuflen - ofs;
+
+ if (chunk > 0)
+ /* We haven't finishing reading the last chunk we requested. */
+ err = io_read (store->port, &piecebuf, &piecelen,
+ (off_t) -1, chunk);
+ else
+ /* Request the next chunk from the server. */
+ err = request_chunk (&piecebuf, &piecelen);
+
+ if (!err && piecebuf != databuf + ofs)
+ {
+ /* Now we have two discontiguous pieces of the buffer. */
+ size_t newbuflen = round_page (databuflen + piecelen);
+ char *newbuf = mmap (0, newbuflen,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (newbuf == MAP_FAILED)
+ {
+ err = errno;
+ break;
+ }
+ memcpy (newbuf, databuf, ofs);
+ memcpy (newbuf + ofs, piecebuf, piecelen);
+ if (databuf != *buf)
+ munmap (databuf, databuflen);
+ databuf = newbuf;
+ databuflen = newbuflen;
+ }
+ } while (! err);
+
+ if (databuf != *buf)
+ munmap (databuf, databuflen);
+ return err;
+}
+
+static error_t
+nbd_set_size (struct store *store, size_t newsize)
+{
+ return EOPNOTSUPP;
+}
+
+
+
+/* Setup hooks. */
+
+static error_t
+nbd_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_std_leaf_decode (enc, _store_nbd_create, store);
+}
+
+static error_t
+nbd_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_nbd_open (name, flags, store);
+}
+
+static const char url_prefix[] = "nbd://";
+
+/* Valid name syntax is [nbd://]HOSTNAME:PORT[/BLOCKSIZE].
+ If "/BLOCKSIZE" is omitted, the block size is 1. */
+static error_t
+nbd_validate_name (const char *name,
+ const struct store_class *const *classes)
+{
+ char *p, *endp;
+
+ if (!strncmp (name, url_prefix, sizeof url_prefix - 1))
+ name += sizeof url_prefix - 1;
+
+ p = strchr (name, ':');
+ if (p == 0)
+ return EINVAL;
+ endp = 0;
+ strtoul (++p, &endp, 0);
+ if (endp == 0 || endp == p)
+ return EINVAL;
+ switch (*endp)
+ {
+ default:
+ return EINVAL;
+ case '\0':
+ break;
+ case '/':
+ p = endp + 1;
+ strtoul (p, &endp, 0);
+ if (endp == 0 || endp == p)
+ return EINVAL;
+ if (*endp != '\0')
+ return EINVAL;
+ }
+ return 0;
+}
+
+static error_t
+nbdopen (const char *name, int *mod_flags,
+ socket_t *sockport, size_t *blocksize, store_offset_t *size)
+{
+ int sock;
+ struct sockaddr_in sin;
+ const struct hostent *he;
+ char **ap;
+ struct nbd_startup ns;
+ ssize_t cc;
+ size_t ofs;
+ unsigned long int port;
+ char *hostname, *p, *endp;
+
+ if (!strncmp (name, url_prefix, sizeof url_prefix - 1))
+ name += sizeof url_prefix - 1;
+
+ /* First we have to parse the store name to get the host name and TCP
+ port number to connect to and the block size to use. */
+
+ hostname = strdupa (name);
+ p = strchr (hostname, ':');
+
+ if (p == 0)
+ return EINVAL;
+ *p++ = '\0';
+ port = strtoul (p, &endp, 0);
+ if (endp == 0 || endp == p || port > 0xffffUL)
+ return EINVAL;
+ switch (*endp)
+ {
+ default:
+ return EINVAL;
+ case '\0':
+ *blocksize = 1;
+ break;
+ case '/':
+ p = endp + 1;
+ *blocksize = strtoul (p, &endp, 0);
+ if (endp == 0 || endp == p)
+ return EINVAL;
+ if (*endp != '\0')
+ return EINVAL;
+ }
+
+ /* Now look up the host name and get a TCP connection. */
+
+ he = gethostbyname (hostname);
+ if (he == 0) /* XXX emit an error message? */
+ return errno; /* XXX what value will this have? */
+
+ sock = socket (PF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ return errno;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons (port);
+ for (ap = he->h_addr_list; *ap != 0; ++ap)
+ {
+ sin.sin_addr = *(const struct in_addr *) *ap;
+ errno = 0;
+ if (connect (sock, &sin, sizeof sin) == 0 || errno == ECONNREFUSED)
+ break;
+ }
+ if (errno != 0) /* last connect failed */
+ {
+ error_t err = errno;
+ close (sock);
+ return err;
+ }
+
+ /* Read the startup packet, which tells us the size of the store. */
+ ofs = 0;
+ do {
+ cc = read (sock, (char *) &ns + ofs, sizeof ns - ofs);
+ if (cc < 0)
+ {
+ error_t err = errno;
+ close (sock);
+ return err;
+ }
+ ofs += cc;
+ } while (cc > 0 && ofs < sizeof ns);
+
+ if (ofs != sizeof ns
+ || memcmp (ns.magic, NBD_INIT_MAGIC, sizeof ns.magic) != 0)
+ {
+ close (sock);
+ return EGRATUITOUS; /* ? */
+ }
+
+ *size = ntohll (ns.size);
+ *sockport = getdport (sock);
+ close (sock);
+
+ return 0;
+}
+
+static void
+nbdclose (struct store *store)
+{
+ if (store->port != MACH_PORT_NULL)
+ {
+ /* Send a disconnect message, but don't wait for a reply. */
+ struct nbd_request req =
+ {
+ magic: NBD_REQUEST_MAGIC,
+ type: htonl (2), /* disconnect */
+ };
+ mach_msg_type_number_t cc;
+ (void) io_write (store->port, (char *) &req, sizeof req, -1, &cc);
+
+ /* Close the socket. */
+ mach_port_deallocate (mach_task_self (), store->port);
+ store->port = MACH_PORT_NULL;
+ }
+}
+
+static error_t
+nbd_set_flags (struct store *store, int flags)
+{
+ if ((flags & ~STORE_INACTIVE) != 0)
+ /* Trying to set flags we don't support. */
+ return EINVAL;
+
+ nbdclose (store);
+ store->flags |= STORE_INACTIVE;
+
+ return 0;
+}
+
+static error_t
+nbd_clear_flags (struct store *store, int flags)
+{
+ error_t err = 0;
+ if ((flags & ~STORE_INACTIVE) != 0)
+ err = EINVAL;
+ err = store->name
+ ? nbdopen (store->name, &store->flags,
+ &store->port, &store->block_size, &store->size)
+ : ENOENT;
+ if (! err)
+ store->flags &= ~STORE_INACTIVE;
+ return err;
+}
+
+const struct store_class store_nbd_class =
+{
+ STORAGE_NETWORK, "nbd",
+ open: nbd_open,
+ validate_name: nbd_validate_name,
+ read: nbd_read,
+ write: nbd_write,
+ set_size: nbd_set_size,
+ allocate_encoding: store_std_leaf_allocate_encoding,
+ encode: store_std_leaf_encode,
+ decode: nbd_decode,
+ set_flags: nbd_set_flags, clear_flags: nbd_clear_flags,
+};
+STORE_STD_CLASS (nbd);
+
+/* Create a store from an existing socket to an nbd server.
+ The initial handshake has already been done. */
+error_t
+_store_nbd_create (mach_port_t port, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ return _store_create (&store_nbd_class,
+ port, flags, block_size, runs, num_runs, 0, store);
+}
+
+/* Open a new store backed by the named nbd server. */
+error_t
+store_nbd_open (const char *name, int flags, struct store **store)
+{
+ error_t err;
+ socket_t sock;
+ struct store_run run;
+ size_t blocksize;
+
+ run.start = 0;
+ err = nbdopen (name, &flags, &sock, &blocksize, &run.length);
+ if (!err)
+ {
+ run.length /= blocksize;
+ err = _store_nbd_create (sock, flags, blocksize, &run, 1, store);
+ if (! err)
+ {
+ if (!strncmp (name, url_prefix, sizeof url_prefix - 1))
+ err = store_set_name (*store, name);
+ else
+ asprintf (&(*store)->name, "%s%s", url_prefix, name);
+ if (err)
+ store_free (*store);
+ }
+ if (err)
+ mach_port_deallocate (mach_task_self (), sock);
+ }
+ return err;
+}
diff --git a/libstore/open.c b/libstore/open.c
new file mode 100644
index 00000000..5c00e107
--- /dev/null
+++ b/libstore/open.c
@@ -0,0 +1,65 @@
+/* Store creation from a file name
+
+ Copyright (C) 1996,97,98,2001, 2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <fcntl.h>
+#include <hurd.h>
+
+#include "store.h"
+
+/* Open the file NAME, and return a new store in STORE, which refers to the
+ storage underlying it. CLASSES is used to select classes specified by the
+ provider; if it is 0, STORE_STD_CLASSES is used. FLAGS is set with
+ store_set_flags. A reference to the open file is created (but may be
+ destroyed with store_close_source). */
+error_t
+store_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ error_t err;
+ int open_flags = (flags & STORE_HARD_READONLY) ? O_RDONLY : O_RDWR;
+ file_t node = file_name_lookup (name, open_flags, 0);
+
+ if (node == MACH_PORT_NULL && !(flags & STORE_HARD_READONLY)
+ && (errno == EACCES || errno == EROFS))
+ {
+ flags |= STORE_HARD_READONLY;
+ node = file_name_lookup (name, O_RDONLY, 0);
+ }
+
+ if (node == MACH_PORT_NULL)
+ return errno;
+
+ err = store_create (node, flags, classes, store);
+ if (err)
+ {
+ if (! (flags & STORE_NO_FILEIO))
+ /* Try making a store that does file io to NODE. */
+ err = store_file_create (node, flags, store);
+ if (err)
+ mach_port_deallocate (mach_task_self (), node);
+ }
+
+ return err;
+}
+
+const struct store_class
+store_query_class = { -1, "query", open: store_open };
+STORE_STD_CLASS (query);
diff --git a/libstore/part.c b/libstore/part.c
new file mode 100644
index 00000000..60ef6c21
--- /dev/null
+++ b/libstore/part.c
@@ -0,0 +1,209 @@
+/* Partition store backend
+ Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+ Written by Neal H Walfield <neal@cs.uml.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "store.h"
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <pthread.h>
+
+#include <parted/parted.h>
+/*#include <parted/device_gnu.h>*/
+
+/* XXX Until the Hurd specific header is available, provide the
+ declaration of ped_device_new_from_store here. */
+#warning "Using local declaration of ped_device_new_from_store."
+
+/* Initialize a PedDevice using SOURCE. The SOURCE will NOT be destroyed;
+ the caller created it, it is the caller's responsilbility to free it
+ after it calls ped_device_destory. SOURCE is not registered in Parted's
+ list of devices. */
+PedDevice* ped_device_new_from_store (struct store *source);
+
+#include <string.h>
+#include <error.h>
+
+#define NEED_PARTED_VERSION "1.5.4"
+#ifndef PED_SECTOR_SIZE
+#define PED_SECTOR_SIZE PED_SECTOR_SIZE_DEFAULT
+#endif
+
+/* Return a new store in STORE which contains a remap store of partition
+ PART from the contents of SOURCE; SOURCE is consumed. */
+error_t
+store_part_create (struct store *source, int index, int flags,
+ struct store **store)
+{
+ static pthread_mutex_t parted_lock = PTHREAD_MUTEX_INITIALIZER;
+ static int version_check;
+
+ error_t err = 0;
+ PedDevice *dev;
+ PedDisk *disk;
+ PedPartition *part;
+ struct store_run run;
+
+ if ((source->block_size < PED_SECTOR_SIZE
+ && PED_SECTOR_SIZE % source->block_size != 0)
+ || (source->block_size > PED_SECTOR_SIZE
+ && source->block_size % PED_SECTOR_SIZE != 0))
+ return EINVAL;
+
+ pthread_mutex_lock (&parted_lock);
+
+ /* Since Parted provides no source-level information about
+ version compatibility, we have to check at run time. */
+ if (version_check == 0)
+ {
+ const char *version = ped_get_version ();
+ version_check = -1;
+ if (version == 0)
+ error (0, 0, "cannot get version of Parted library!");
+ else if (strverscmp (version, NEED_PARTED_VERSION) < 0)
+ error (0, 0, "Parted library version %s older than needed %s",
+ version, NEED_PARTED_VERSION);
+ else
+ version_check = 1;
+ }
+ if (version_check <= 0)
+ {
+ error (0, 0, "the `part' store type is not available");
+ pthread_mutex_unlock (&parted_lock);
+ return ENOTSUP;
+ }
+
+ ped_exception_fetch_all ();
+
+ dev = ped_device_new_from_store (source);
+ if (! dev)
+ {
+ ped_exception_catch ();
+ err = EIO;
+ goto out;
+ }
+
+ assert (ped_device_open (dev) != 0);
+
+ disk = ped_disk_new (dev);
+ if (! disk)
+ {
+ ped_exception_catch ();
+ err = EIO;
+ goto out_with_dev;
+ }
+
+ for (part = ped_disk_next_partition (disk, NULL); part;
+ part = ped_disk_next_partition (disk, part))
+ {
+ if (part->type != PED_PARTITION_LOGICAL
+ && part->type != 0 /* PED_PARTITION_PRIMARY */)
+ continue;
+
+ assert (part->num);
+ if (part->num == index)
+ break;
+ }
+
+ if (! part)
+ {
+ err = EIO;
+ goto out_with_disk;
+ }
+
+ if (source->block_size == PED_SECTOR_SIZE)
+ {
+ run.start = part->geom.start;
+ run.length = part->geom.length;
+ }
+ else if (source->block_size < PED_SECTOR_SIZE)
+ {
+ run.start = part->geom.start * (PED_SECTOR_SIZE / source->block_size);
+ run.length = part->geom.length * (PED_SECTOR_SIZE / source->block_size);
+ }
+ else
+ /* source->block_size > PED_SECTOR_SIZE */
+ {
+ run.start = part->geom.start * PED_SECTOR_SIZE;
+ if (run.start % source->block_size != 0)
+ err = EIO;
+ else
+ {
+ run.start /= source->block_size;
+ run.length = part->geom.length * PED_SECTOR_SIZE;
+ if (run.length % source->block_size != 0)
+ err = EIO;
+ else
+ run.length /= source->block_size;
+ }
+ }
+
+out_with_disk:
+ assert (ped_device_close (dev) != 0);
+ ped_disk_destroy (disk);
+out_with_dev:
+ ped_device_destroy (dev);
+out:
+ ped_exception_leave_all ();
+ pthread_mutex_unlock (&parted_lock);
+
+ if (! err)
+ err = store_remap (source, &run, 1, store);
+
+ return err;
+}
+
+/* Open the part NAME. NAME consists of a partition number, a ':', a another
+ store class name, a ':' and a name for to by passed to the store class.
+ E.g. "2:device:hd0" would open the second partition on a DEVICE store
+ named "hd0". FLAGS indicate how to open the store. CLASSES is used to
+ select classes specified by the type NAME; if it is 0, STORE_STD_CLASSES
+ is used. The new store is returned in *STORE. */
+error_t
+store_part_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ int part;
+ char *endp;
+ struct store *source;
+ error_t err;
+
+ part = strtol (name, &endp, 0);
+ if (endp == name || *endp != ':')
+ return EINVAL;
+
+ name = endp + 1;
+ if (*name == '\0')
+ return EINVAL;
+
+ err = store_typed_open (name, flags, classes, &source);
+ if (! err)
+ {
+ err = store_part_create (source, part, flags, store);
+ if (err)
+ store_free (source);
+ }
+
+ return err;
+}
+
+const struct store_class
+store_part_class = { -1, "part", open: store_part_open };
+STORE_STD_CLASS (part);
diff --git a/libstore/rdwr.c b/libstore/rdwr.c
new file mode 100644
index 00000000..9737c515
--- /dev/null
+++ b/libstore/rdwr.c
@@ -0,0 +1,298 @@
+/* Store I/O
+
+ Copyright (C) 1995-1999,2001,2002,2003 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+#include <sys/mman.h>
+
+#include "store.h"
+
+/* Returns in RUN the tail of STORE's run list, who's first run contains
+ ADDR, and is not a hole, and in RUNS_END a pointer pointing at the end of
+ the run list. Returns the offset within it at which ADDR occurs. Also
+ returns BASE, which should be added to offsets from RUNS. */
+static inline store_offset_t
+store_find_first_run (struct store *store, store_offset_t addr,
+ struct store_run **run, struct store_run **runs_end,
+ store_offset_t *base, size_t *index)
+{
+ struct store_run *tail = store->runs, *tail_end = tail + store->num_runs;
+ store_offset_t wrap_src = store->wrap_src;
+
+ if (addr >= wrap_src && addr < store->end)
+ /* Locate the correct position within a repeating pattern of runs. */
+ {
+ *base = addr / store->wrap_dst;
+ addr %= wrap_src;
+ }
+ else
+ *base = 0;
+
+ /* XXX: this isn't going to be very efficient if RUNS is very complex...
+ But it should do dandy if it's short. For long run lists, we could do a
+ binary search or something. */
+ while (tail < tail_end)
+ {
+ store_offset_t run_blocks = tail->length;
+
+ if (run_blocks > addr)
+ {
+ *run = tail;
+ *runs_end = tail_end;
+ *index = tail - store->runs;
+ return addr;
+ }
+
+ /* Not to the right place yet, move on... */
+ addr -= run_blocks;
+ tail++;
+ }
+
+ return -1;
+}
+
+/* Update RUN, BASE, & INDEX to point to the next elemement in the runs
+ array. RUNS_END is the point where RUNS will wrap. Returns true if
+ things are still kosher. */
+static inline int
+store_next_run (struct store *store, struct store_run *runs_end,
+ struct store_run **run, store_offset_t *base, size_t *index)
+{
+ (*run)++;
+ (*index)++;
+
+ if (*run == runs_end)
+ /* Wrap around in a repeating RUNS. */
+ {
+ *run = store->runs;
+ *base += store->wrap_dst;
+ *index = 0;
+ return (*base < store->end);
+ }
+ else
+ return 1;
+}
+
+/* Write LEN bytes from BUF to STORE at ADDR. Returns the amount written
+ in AMOUNT. ADDR is in BLOCKS (as defined by STORE->block_size). */
+error_t
+store_write (struct store *store,
+ store_offset_t addr, const void *buf, size_t len, size_t *amount)
+{
+ error_t err;
+ size_t index;
+ store_offset_t base;
+ struct store_run *run, *runs_end;
+ int block_shift = store->log2_block_size;
+ store_write_meth_t write = store->class->write;
+
+ if (store->flags & STORE_READONLY)
+ return EROFS; /* XXX */
+
+ if ((addr << block_shift) + len > store->size)
+ return EIO;
+
+ if (store->block_size != 0 && (len & (store->block_size - 1)) != 0)
+ return EINVAL;
+
+ addr = store_find_first_run (store, addr, &run, &runs_end, &base, &index);
+ if (addr < 0)
+ err = EIO;
+ else if ((len >> block_shift) <= run->length - addr)
+ /* The first run has it all... */
+ err = (*write)(store, base + run->start + addr, index, buf, len, amount);
+ else
+ /* ARGH, we've got to split up the write ... */
+ {
+ mach_msg_type_number_t try, written;
+
+ /* Write the initial bit in the first run. Errors here are returned. */
+ try = (run->length - addr) << block_shift;
+ err = (*write) (store, base + run->start + addr, index, buf, try,
+ &written);
+
+ if (!err && written == try)
+ /* Wrote the first bit successfully, now do the rest. Any errors
+ will just result in a short write. */
+ {
+ buf += written;
+ len -= written;
+
+ while (store_next_run (store, runs_end, &run, &base, &index)
+ && run->start >= 0) /* Check for holes. */
+ /* Ok, we can write in this run, at least a bit. */
+ {
+ mach_msg_type_number_t seg_written;
+
+ if ((len >> block_shift) <= run->length)
+ try = len;
+ else
+ try = run->length << block_shift;
+
+ err = (*write)(store, base + run->start, index, buf, try,
+ &seg_written);
+ if (err)
+ break; /* Ack */
+ written += seg_written;
+
+ if (seg_written < try)
+ break; /* Didn't use up the run, we're done. */
+
+ len -= seg_written;
+ if (len == 0)
+ break; /* Nothing left to write! */
+
+ buf += seg_written;
+ }
+ }
+
+ *amount = written;
+ }
+
+ return err;
+}
+
+/* Read AMOUNT bytes from STORE at ADDR into BUF & LEN (which follows the
+ usual mach buffer-return semantics) to STORE at ADDR. ADDR is in BLOCKS
+ (as defined by STORE->block_size). */
+error_t
+store_read (struct store *store,
+ store_offset_t addr, size_t amount, void **buf, size_t *len)
+{
+ size_t index;
+ store_offset_t base;
+ struct store_run *run, *runs_end;
+ int block_shift = store->log2_block_size;
+ store_read_meth_t read = store->class->read;
+
+ addr = store_find_first_run (store, addr, &run, &runs_end, &base, &index);
+ if (addr < 0 || run->start < 0)
+ return EIO; /* Reading from a hole. */
+
+ if ((addr << block_shift) + amount > store->size)
+ amount = store->size - (addr << block_shift);
+
+ if (store->block_size != 0 && (amount & (store->block_size - 1)) != 0)
+ return EINVAL;
+
+ if ((amount >> block_shift) <= run->length - addr)
+ /* The first run has it all... */
+ return (*read) (store, base + run->start + addr, index, amount, buf, len);
+ else
+ /* ARGH, we've got to split up the read ... This isn't fun. */
+ {
+ error_t err;
+ int all;
+ /* WHOLE_BUF and WHOLE_BUF_LEN will point to a buff that's large enough
+ to hold the entire request. This is initially whatever the user
+ passed in, but we'll change it as necessary. */
+ void *whole_buf = *buf, *buf_end;
+ size_t whole_buf_len = *len;
+
+ /* Read LEN bytes from the store address ADDR into BUF_END. BUF_END
+ and AMOUNT are adjusted by the amount actually read. Whether or not
+ the amount read is the same as what was request is returned in ALL. */
+ inline error_t seg_read (store_offset_t addr, size_t len, int *all)
+ {
+ /* SEG_BUF and SEG_LEN are the buffer for a particular bit of the
+ whole (within one run). */
+ void *seg_buf = buf_end;
+ size_t seg_buf_len = len;
+ error_t err =
+ (*read)(store, addr, index, len, &seg_buf, &seg_buf_len);
+ if (!err)
+ {
+ /* If for some bizarre reason, the underlying storage chose not
+ to use the buffer space we so kindly gave it, copy it to
+ that space. */
+ if (seg_buf != buf_end)
+ {
+ memcpy (buf_end, seg_buf, seg_buf_len);
+ munmap (seg_buf, seg_buf_len);
+ }
+ buf_end += seg_buf_len;
+ amount -= seg_buf_len;
+ *all = (seg_buf_len == len);
+ }
+ return err;
+ }
+
+ if (whole_buf_len < amount)
+ /* Not enough room in the user's buffer to hold everything, better
+ make room. */
+ {
+ whole_buf_len = amount;
+ whole_buf = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (whole_buf == (void *) -1)
+ return errno; /* Punt early, there's nothing to clean up. */
+ }
+
+ buf_end = whole_buf;
+
+ err = seg_read (base + run->start + addr,
+ (run->length - addr) << block_shift, &all);
+ while (!err && all && amount > 0
+ && store_next_run (store, runs_end, &run, &base, &index))
+ {
+ if (run->start < 0)
+ /* A hole! Can't read here. Must stop. */
+ break;
+ else
+ err = seg_read (base + run->start,
+ (amount >> block_shift) <= run->length
+ ? amount /* This run has the rest. */
+ : (run->length << block_shift), /* Whole run. */
+ &all);
+ }
+
+ /* The actual amount read. */
+ *len = buf_end - whole_buf;
+ if (*len > 0)
+ err = 0; /* Return a short read instead of an error. */
+
+ /* Deallocate any amount of WHOLE_BUF we didn't use. */
+ if (whole_buf != *buf)
+ {
+ if (err)
+ munmap (whole_buf, whole_buf_len);
+ else
+ {
+ vm_size_t unused = whole_buf_len - round_page (*len);
+ if (unused)
+ munmap (whole_buf + whole_buf_len - unused, unused);
+ *buf = whole_buf;
+ }
+ }
+
+ return err;
+ }
+}
+
+/* Set STORE's size to NEWSIZE (in bytes). */
+error_t
+store_set_size (struct store *store, size_t newsize)
+{
+ error_t err;
+ store_set_size_meth_t set_size = store->class->set_size;
+
+ /* Updating the runs list is up to the class set_size method. */
+ err = (* set_size) (store, newsize);
+
+ return err;
+}
diff --git a/libstore/remap.c b/libstore/remap.c
new file mode 100644
index 00000000..55ab51ac
--- /dev/null
+++ b/libstore/remap.c
@@ -0,0 +1,345 @@
+/* Block address translation
+
+ Copyright (C) 1996,97,99,2001, 2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <hurd/fs.h>
+
+#include "store.h"
+
+static error_t
+remap_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount,
+ void **buf, size_t *len)
+{
+ return store_read (store->children[0], addr, amount, buf, len);
+}
+
+static error_t
+remap_write (struct store *store,
+ store_offset_t addr, size_t index, const void *buf, size_t len,
+ size_t *amount)
+{
+ return store_write (store->children[0], addr, buf, len, amount);
+}
+
+static error_t
+remap_set_size (struct store *store, size_t newsize)
+{
+ return store_set_size (store->children[0], newsize);
+}
+
+error_t
+remap_allocate_encoding (const struct store *store, struct store_enc *enc)
+{
+ enc->num_ints += 3;
+ enc->num_offsets += store->num_runs * 2;
+ return store_allocate_child_encodings (store, enc);
+}
+
+error_t
+remap_encode (const struct store *store, struct store_enc *enc)
+{
+ int i;
+ enc->ints[enc->cur_int++] = store->class->id;
+ enc->ints[enc->cur_int++] = store->flags;
+ enc->ints[enc->cur_int++] = store->num_runs;
+ for (i = 0; i < store->num_runs; i++)
+ {
+ enc->offsets[enc->cur_offset++] = store->runs[i].start;
+ enc->offsets[enc->cur_offset++] = store->runs[i].length;
+ }
+ return store_encode_children (store, enc);
+}
+
+error_t
+remap_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ if (enc->cur_int + 3 > enc->num_ints)
+ return EINVAL;
+ else
+ {
+ int type __attribute__((unused)) = enc->ints[enc->cur_int++];
+ int flags = enc->ints[enc->cur_int++];
+ int num_runs = enc->ints[enc->cur_int++];
+ error_t create_remap (const struct store_run *runs, size_t num_runs)
+ {
+ struct store *source;
+ error_t err = store_decode_children (enc, 1, classes, &source);
+ if (! err)
+ err = store_remap_create (source, runs, num_runs, flags, store);
+ return err;
+ }
+ return store_with_decoded_runs (enc, num_runs, create_remap);
+ }
+}
+
+error_t
+remap_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ error_t err;
+ struct store *from;
+ const char *end, *p;
+ struct store_run *runs;
+ size_t nruns;
+
+ end = strchr (name, ':');
+ if (!end)
+ return EINVAL;
+
+ runs = alloca ((end - name) * sizeof runs[0]);
+
+ nruns = 0;
+ p = name;
+ do
+ {
+ char *endp;
+ runs[nruns].start = strtoul (p, &endp, 0);
+ if (*endp == '+')
+ {
+ if (endp == p) /* Syntax "+5,+7" means "0+5,0+7". */
+ runs[nruns].start = 0;
+ p = endp + 1;
+ if (p == end || *p == ',')
+ {
+ /* Syntax "100+" means block 100 to the end of the store.
+ Since we don't know the size yet, we use -1 as a marker
+ for the code below. */
+ runs[nruns++].length = (store_offset_t) -1;
+ break;
+ }
+ runs[nruns].length = strtoul (p, &endp, 0);
+ if (endp == p)
+ return EINVAL;
+ }
+ else if (endp == p) /* Must have a number unless starts with +. */
+ return EINVAL;
+ else
+ runs[nruns].length = 1;
+ ++nruns;
+ p = endp;
+ if (*p == ',')
+ ++p;
+ } while (p < end);
+
+ err = store_typed_open (end + 1, flags, classes, &from);
+ if (!err)
+ {
+ /* Check for any runs marked as "through the end of the store"
+ and update them to use the actual size of the store. */
+ size_t i;
+ for (i = 0; i < nruns; ++i)
+ if (runs[i].length == (store_offset_t) -1)
+ runs[i].length = from->blocks - runs[i].start;
+
+ /* Now do the remapping according to RUNS. */
+ err = store_remap (from, runs, nruns, store);
+ if (err)
+ store_free (from);
+ }
+ return err;
+}
+
+error_t
+remap_validate_name (const char *name,
+ const struct store_class *const *classes)
+{
+ const char *end = strchr (name, ':');
+ const char *p;
+
+ if (!end)
+ return EINVAL;
+
+ p = name;
+ do
+ {
+ if (*p != '+')
+ {
+ if (!isdigit (*p))
+ return EINVAL;
+ do
+ ++p;
+ while (isdigit (*p));
+ }
+
+ if (*p == '+')
+ {
+ ++p;
+ if (!isdigit (*p))
+ return EINVAL;
+ do
+ ++p;
+ while (isdigit (*p));
+ }
+
+ if (*p == ',')
+ ++p;
+ else if (*p == ':')
+ return 0;
+ } while (*p != '\0');
+
+ return EINVAL;
+}
+
+
+const struct store_class
+store_remap_class =
+{
+ STORAGE_REMAP, "remap", remap_read, remap_write, remap_set_size,
+ remap_allocate_encoding, remap_encode, remap_decode,
+ store_set_child_flags, store_clear_child_flags,
+ NULL, NULL, NULL, /* cleanup, clone, remap */
+ remap_open, remap_validate_name
+};
+STORE_STD_CLASS (remap);
+
+/* Return a new store in STORE that reflects the blocks in RUNS & RUNS_LEN
+ from SOURCE; SOURCE is consumed, but RUNS is not. Unlike the
+ store_remap function, this function always operates by creating a new
+ store of type `remap' which has SOURCE as a child, and so may be less
+ efficient than store_remap for some types of stores. */
+error_t
+store_remap_create (struct store *source,
+ const struct store_run *runs, size_t num_runs,
+ int flags, struct store **store)
+{
+ error_t err =
+ _store_create (&store_remap_class, MACH_PORT_NULL, flags | source->flags,
+ source->block_size, runs, num_runs, 0, store);
+
+ if (! err)
+ {
+ err = store_set_children (*store, &source, 1);
+ if (err)
+ store_free (*store);
+ }
+
+ return err;
+}
+
+/* For each run in RUNS, of length NUM_RUNS, translate the */
+error_t
+store_remap_runs (const struct store_run *runs, size_t num_runs,
+ const struct store_run *base_runs, size_t num_base_runs,
+ struct store_run **xruns, size_t *num_xruns)
+{
+ int i, j;
+ size_t xruns_alloced = num_runs + num_base_runs;
+
+ /* Add the single run [ADDR, LEN) to *XRUNS, returning true if successful. */
+ int add_run (store_offset_t addr, store_offset_t len)
+ {
+ if (*num_xruns == xruns_alloced)
+ /* Make some more space in *XRUNS. */
+ {
+ struct store_run *new;
+ xruns_alloced *= 2;
+ new = realloc (*xruns, xruns_alloced * sizeof (struct store_run));
+ if (! new)
+ return 0;
+ *xruns = new;
+ }
+ (*xruns)[(*num_xruns)++] = (struct store_run){ addr, len };
+ return 1;
+ }
+
+ *xruns = malloc (xruns_alloced * sizeof (struct store_run));
+ if (! *xruns)
+ return ENOMEM;
+
+ /* Clean up and return error code CODE. */
+#define ERR(code) do { free (*xruns); return (code); } while (0)
+
+ for (i = 0; i < num_runs; i++)
+ {
+ store_offset_t addr = runs[i].start, left = runs[i].length;
+
+ if (addr >= 0)
+ for (j = 0; j < num_base_runs && left > 0; j++)
+ {
+ store_offset_t baddr = base_runs[j].start;
+ store_offset_t blen = base_runs[j].length;
+
+ if (addr >= blen)
+ addr -= blen;
+ else if (baddr < 0)
+ /* A hole, which is invalid. */
+ ERR (EINVAL);
+ else
+ /* Add another output run. */
+ {
+ store_offset_t len = blen - addr; /* Size of next output run. */
+ if (! add_run (baddr + addr, len > left ? left : len))
+ ERR (ENOMEM);
+ addr = 0;
+ left -= len;
+ }
+ }
+ else
+ /* a hole */
+ if (! add_run (-1, left))
+ ERR (ENOMEM);
+ }
+
+ if (xruns_alloced > *num_xruns)
+ *xruns = realloc (*xruns, *num_xruns * sizeof (struct store_run));
+
+ return 0;
+}
+
+/* Return a store in STORE that reflects the blocks in RUNS & RUNS_LEN from
+ SOURCE; SOURCE is consumed, but not RUNS. Unlike the store_remap_create
+ function, this may simply modify SOURCE and return it. */
+error_t
+store_remap (struct store *source,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ if (source->class->remap)
+ /* Use the class-specific remaping function. */
+ return (* source->class->remap) (source, runs, num_runs, store);
+ else
+ /* Just replace SOURCE's runs-list by an appropiately translated RUNS. */
+ {
+ struct store_run *xruns = 0;
+ size_t num_xruns = 0;
+ error_t err =
+ store_remap_runs (runs, num_runs, source->runs, source->num_runs,
+ &xruns, &num_xruns);
+ if (! err)
+ {
+ /* Don't use store_set_runs -- we've already allocated the
+ storage. */
+ free (source->runs);
+ source->runs = xruns;
+ source->num_runs = num_xruns;
+ source->flags &= ~STORE_ENFORCED;
+ source->end = 0; /* Needed to make _store_derive work. */
+ store_close_source (source);
+ _store_derive (source);
+ *store = source;
+ }
+ return err;
+ }
+}
diff --git a/libstore/set.c b/libstore/set.c
new file mode 100644
index 00000000..b9ff4f41
--- /dev/null
+++ b/libstore/set.c
@@ -0,0 +1,78 @@
+/* Setting various store fields
+
+ Copyright (C) 1995,96,97,2001,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <mach.h>
+
+#include "store.h"
+
+/* Set STORE's current runs list to (a copy of) RUNS and NUM_RUNS. */
+error_t
+store_set_runs (struct store *store,
+ const struct store_run *runs, size_t num_runs)
+{
+ unsigned size = num_runs * sizeof (struct store_run);
+ struct store_run *copy = malloc (size);
+
+ if (!copy)
+ return ENOMEM;
+
+ if (store->runs)
+ free (store->runs);
+
+ memcpy (copy, runs, size);
+ store->runs = copy;
+ store->num_runs = num_runs;
+
+ if (store->block_size > 0)
+ _store_derive (store);
+
+ return 0;
+}
+
+/* Sets the name associated with STORE to a copy of NAME. */
+error_t
+store_set_name (struct store *store, const char *name)
+{
+ char *copy = strdup (name);
+
+ if (!copy)
+ return ENOMEM;
+
+ if (store->name)
+ free (store->name);
+
+ store->name = copy;
+
+ return 0;
+}
+
+/* If STORE was created using store_create, remove the reference to the
+ source from which it was created. */
+void store_close_source (struct store *store)
+{
+ if (store->source != MACH_PORT_NULL)
+ {
+ mach_port_deallocate (mach_task_self (), store->source);
+ store->source = MACH_PORT_NULL;
+ }
+}
diff --git a/libstore/std.c b/libstore/std.c
new file mode 100644
index 00000000..4784a8a5
--- /dev/null
+++ b/libstore/std.c
@@ -0,0 +1,44 @@
+/* List of standard store classes
+
+ Copyright (C) 1996,97,2001 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "store.h"
+
+const struct store_class *const __attribute__ ((section ("store_std_classes")))
+store_std_classes[] =
+{
+ &store_device_class,
+#if HAVE_PARTED_PARTED_H
+ &store_part_class,
+#endif
+ &store_file_class,
+ &store_zero_class,
+ &store_task_class,
+ &store_ileave_class, &store_concat_class, &store_remap_class,
+ &store_query_class,
+ &store_copy_class, &store_gunzip_class, &store_bunzip2_class,
+
+ /* This pseudo-class must appear before any real STORAGE_NETWORK class,
+ to parse STORAGE_NETWORK file_get_storage_info results properly. */
+ &store_url_open_class,
+ &store_nbd_class,
+
+ &store_typed_open_class,
+ 0
+};
diff --git a/libstore/store.h b/libstore/store.h
new file mode 100644
index 00000000..ae334a1d
--- /dev/null
+++ b/libstore/store.h
@@ -0,0 +1,800 @@
+/* Store I/O
+
+ Copyright (C) 1995,96,97,98,99,2001,02,04,05 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* A `store' is a fixed-size block of storage, which can be read and perhaps
+ written to. This library implements many different backends which allow
+ the abstract store interface to be used with common types of storage --
+ devices, files, memory, tasks, etc. It also allows stores to be combined
+ and filtered in various ways. */
+
+#ifndef __STORE_H__
+#define __STORE_H__
+
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <mach.h>
+#include <device/device.h>
+#include <hurd/hurd_types.h>
+#include <features.h>
+
+#ifdef STORE_DEFINE_EI
+#define STORE_EI
+#else
+#define STORE_EI __extern_inline
+#endif
+
+/* Type for addresses inside the store. */
+typedef off64_t store_offset_t;
+
+/* A portion of a store. If START == -1, it's a hole. */
+struct store_run
+{
+ store_offset_t start, length;
+};
+
+struct store
+{
+ /* If this store was created using store_create, the file from which we got
+ our store. */
+ file_t source;
+
+ /* Address ranges in the underlying storage which make up our contiguous
+ address space. In units of BLOCK_SIZE, below. */
+ struct store_run *runs; /* Malloced */
+ size_t num_runs; /* Length of RUNS. */
+
+ /* Maximum valid offset. This is the same as SIZE, but in blocks. */
+ store_offset_t end;
+
+ /* WRAP_SRC is the sum of the run lengths in RUNS. If this is less than
+ END, then RUNS describes a repeating pattern, of length WRAP_SRC -- each
+ successive iteration having an additional offset of WRAP_DST. */
+ store_offset_t wrap_src;
+ store_offset_t wrap_dst; /* Only meaningful if WRAP_SRC < END */
+
+ /* Handles for the underlying storage. */
+ char *name; /* Malloced */
+ mach_port_t port; /* Send right */
+
+ /* The size of a `block' on this storage. */
+ size_t block_size;
+
+ /* The number of blocks (of size BLOCK_SIZE) in this storage. */
+ store_offset_t blocks;
+ /* The number of bytes in this storage, including holes. */
+ store_offset_t size;
+
+ /* Log_2 (BLOCK_SIZE) or 0 if not a power of 2. */
+ unsigned log2_block_size;
+ /* Log_2 (VM_PAGE_SIZE / BLOCK_SIZE); only valid if LOG2_BLOCK_SIZE is. */
+ unsigned log2_blocks_per_page;
+
+ /* Random flags. */
+ int flags;
+
+ void *misc; /* malloced */
+ size_t misc_len;
+
+ const struct store_class *class;
+
+ /* A list of sub-stores. The interpretation of this is type-specific. */
+ struct store **children;
+ size_t num_children;
+
+ void *hook; /* Type specific noise. */
+};
+
+/* Store flags. These are in addition to the STORAGE_ flags defined in
+ <hurd/hurd_types.h>. XXX synchronize these values. */
+
+/* Flags that reflect something immutable about the object. */
+#define STORE_IMMUTABLE_FLAGS 0x00FF
+
+/* Flags implemented by generic store code. */
+#define STORE_READONLY 0x0100 /* No writing allowed. */
+#define STORE_NO_FILEIO 0x0200 /* If store_create can't fetch store
+ information, don't create a store
+ using file io instead. */
+#define STORE_GENERIC_FLAGS (STORE_READONLY | STORE_NO_FILEIO)
+
+/* Flags implemented by each backend. */
+#define STORE_HARD_READONLY 0x1000 /* Can't be made writable. */
+#define STORE_ENFORCED 0x2000 /* Range is enforced by device. */
+#define STORE_INACTIVE 0x4000 /* Not in a usable state. */
+#define STORE_INNOCUOUS 0x8000 /* Cannot modify anything dangerous. */
+#define STORE_BACKEND_SPEC_BASE 0x10000 /* Here up are backend-specific */
+#define STORE_BACKEND_FLAGS (STORE_HARD_READONLY | STORE_ENFORCED \
+ | STORE_INACTIVE \
+ | ~(STORE_BACKEND_SPEC_BASE - 1))
+
+typedef error_t (*store_write_meth_t)(struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf,
+ mach_msg_type_number_t len,
+ mach_msg_type_number_t *amount);
+typedef error_t (*store_read_meth_t)(struct store *store,
+ store_offset_t addr, size_t index,
+ mach_msg_type_number_t amount,
+ void **buf, mach_msg_type_number_t *len);
+typedef error_t (*store_set_size_meth_t)(struct store *store,
+ size_t newsize);
+
+struct store_enc; /* fwd decl */
+
+struct store_class
+{
+ /* The type of storage this is (see STORAGE_ in hurd/hurd_types.h). */
+ enum file_storage_class id;
+
+ /* Name of the class. */
+ const char *name;
+
+ /* Read up to AMOUNT bytes at the underlying address ADDR from the storage
+ into BUF and LEN. INDEX varies from 0 to the number of runs in STORE. */
+ store_read_meth_t read;
+ /* Write up to LEN bytes from BUF to the storage at the underlying address
+ ADDR. INDEX varies from 0 to the number of runs in STORE. */
+ store_write_meth_t write;
+ /* Set store's size to NEWSIZE (in bytes). */
+ store_set_size_meth_t set_size;
+
+ /* To the lengths of each for the four arrays in ENC, add how much STORE
+ would need to be encoded. */
+ error_t (*allocate_encoding)(const struct store *store,
+ struct store_enc *enc);
+ /* Append the encoding for STORE to ENC. */
+ error_t (*encode) (const struct store *store, struct store_enc *enc);
+
+ /* Decode from ENC a new store, which return in STORE. CLASSES is used to
+ lookup child classes. */
+ error_t (*decode) (struct store_enc *enc,
+ const struct store_class *const *classes,
+ struct store **store);
+
+ /* Modify flags that reflect backend state, such as STORE_HARD_READONLY and
+ STORE_ENFORCED. */
+ error_t (*set_flags) (struct store *store, int flags);
+ error_t (*clear_flags) (struct store *store, int flags);
+
+ /* Called just before deallocating STORE. */
+ void (*cleanup) (struct store *store);
+
+ /* Copy any format-dependent fields in FROM to TO; if there's some reason
+ why the copy can't be made, an error should be returned. This call is
+ made after all format-indendependent fields have been cloned. */
+ error_t (*clone) (const struct store *from, struct store *to);
+
+ /* Return in STORE a store that only contains the parts of SOURCE as
+ enumerated in RUNS & RUNS_LEN, consuming SOURCE in the process. The
+ default behavior, if REMAP is 0, is to replace SOURCE's run list with
+ the subset selected by RUNS, and return SOURCE. */
+ error_t (*remap) (struct store *source,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store);
+
+ /* Open a new store called NAME in this class. CLASSES is supplied in case
+ it's desirable to open a sub-store in some manner. */
+ error_t (*open) (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+ /* Given a user argument ARG, this function should check it for syntactic
+ validity, or print a syntax error, using ARGP_STATE in the normal
+ manner; if zero is returned, then this argument is assumed valid, and
+ can be passed to the open function. If ARG is 0, then there were *no*
+ arguments specified; in this case, returning EINVAL means that this is
+ not kosher. If PARSE is 0, then it is assumed that if this class has an
+ OPEN function, then validity can't be syntactically determined. */
+ error_t (*validate_name) (const char *name,
+ const struct store_class *const *classes);
+
+ /* Return a memory object paging on STORE. */
+ error_t (*map) (const struct store *store, vm_prot_t prot, mach_port_t *memobj);
+};
+
+/* Return a new store in STORE, which refers to the storage underlying
+ SOURCE. CLASSES is as if passed to store_find_class, which see. FLAGS
+ is set with store_set_flags, with the exception of STORE_INACTIVE, which
+ merely indicates that no attempt should be made to activate an inactive
+ store; if STORE_INACTIVE is not specified, and the store returned for
+ SOURCE is inactive, an attempt is made to activate it (failure of which
+ causes an error to be returned). A reference to SOURCE is created (but
+ may be destroyed with store_close_source). */
+error_t store_create (file_t source, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+void store_free (struct store *store);
+
+/* Open the file NAME, and return a new store in STORE, which refers to the
+ storage underlying it. CLASSES is as if passed to store_find_class,
+ which see. FLAGS is set with store_set_flags. A reference to the open
+ file is created (but may be destroyed with store_close_source). */
+error_t store_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Allocate a new store structure, returned in STORE, with class CLASS and
+ the various other fields initialized to the given parameters. */
+error_t
+_store_create (const struct store_class *class, mach_port_t port,
+ int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ store_offset_t end, struct store **store);
+
+/* Set STORE's current runs list to (a copy of) RUNS and NUM_RUNS. */
+error_t store_set_runs (struct store *store,
+ const struct store_run *runs, size_t num_runs);
+
+/* Set STORE's current children to (a copy of) CHILDREN and NUM_CHILDREN
+ (note that just the vector CHILDREN is copied, not the actual children). */
+error_t store_set_children (struct store *store,
+ struct store *const *children, size_t num_children);
+
+/* Try to come up with a name for the children in STORE, combining the names
+ of each child in a way that could be used to parse them with
+ store_open_children. This is done heuristically, and so may not succeed.
+ If a child doesn't have a name, EINVAL is returned. */
+error_t store_children_name (const struct store *store, char **name);
+
+/* Sets the name associated with STORE to a copy of NAME. */
+error_t store_set_name (struct store *store, const char *name);
+
+/* Add FLAGS to STORE's currently set flags. */
+error_t store_set_flags (struct store *store, int flags);
+
+/* Remove FLAGS from STORE's currently set flags. */
+error_t store_clear_flags (struct store *store, int flags);
+
+/* Set FLAGS in all children of STORE, and if successful, add FLAGS to
+ STORE's flags. */
+error_t store_set_child_flags (struct store *store, int flags);
+
+/* Clear FLAGS in all children of STORE, and if successful, remove FLAGS from
+ STORE's flags. */
+error_t store_clear_child_flags (struct store *store, int flags);
+
+extern int store_is_securely_returnable (struct store *store, int open_flags);
+
+#if defined(__USE_EXTERN_INLINES) || defined(STORE_DEFINE_EI)
+
+/* Returns true if STORE can safely be returned to a user who has accessed it
+ via a node using OPEN_FLAGS, without compromising security. */
+STORE_EI int
+store_is_securely_returnable (struct store *store, int open_flags)
+{
+ int flags = store->flags;
+ return
+ (flags & (STORE_INNOCUOUS | STORE_INACTIVE))
+ || ((flags & STORE_ENFORCED)
+ && (((open_flags & O_ACCMODE) == O_RDWR)
+ || (flags & STORE_HARD_READONLY)));
+}
+
+#endif /* Use extern inlines. */
+
+/* Fills in the values of the various fields in STORE that are derivable from
+ the set of runs & the block size. */
+void _store_derive (struct store *store);
+
+/* Return in TO a copy of FROM. */
+error_t store_clone (struct store *from, struct store **to);
+
+/* Return a store in STORE that reflects the blocks in RUNS & RUNS_LEN from
+ source; SOURCE is consumed, but not RUNS. Unlike the store_remap_create
+ function, this may simply modify SOURCE and return it. */
+error_t store_remap (struct store *source,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store);
+
+/* Write LEN bytes from BUF to STORE at ADDR. Returns the amount written in
+ AMOUNT (in bytes). ADDR is in BLOCKS (as defined by STORE->block_size). */
+error_t store_write (struct store *store,
+ store_offset_t addr, const void *buf, size_t len,
+ size_t *amount);
+
+/* Read AMOUNT bytes from STORE at ADDR into BUF & LEN (which following the
+ usual mach buffer-return semantics) to STORE at ADDR. ADDR is in BLOCKS
+ (as defined by STORE->block_size). Note that LEN is in bytes. */
+error_t store_read (struct store *store,
+ store_offset_t addr, size_t amount, void **buf, size_t *len);
+
+/* Set STORE's size to NEWSIZE (in bytes). */
+error_t store_set_size (struct store *store, size_t newsize);
+
+/* If STORE was created using store_create, remove the reference to the
+ source from which it was created. */
+void store_close_source (struct store *store);
+
+/* Return a memory object paging on STORE. If this call fails with
+ EOPNOTSUPP, you can try calling some of the routines below to get a pager. */
+error_t store_map (const struct store *store, vm_prot_t prot,
+ mach_port_t *memobj);
+
+#if 0
+
+/* Create a new pager and paging threads paging on STORE, and return the
+ resulting memory object in PAGER. */
+error_t store_create_pager (struct store *store, vm_prot_t prot, ...,
+ mach_port_t *memobj)
+
+#endif
+
+/* Creating specific types of stores. */
+
+/* Return a new zero store SIZE bytes long in STORE. */
+error_t store_zero_create (store_offset_t size, int flags, struct store **store);
+
+/* Return a new store in STORE referring to the mach device DEVICE. Consumes
+ the send right DEVICE. */
+error_t store_device_create (device_t device, int flags, struct store **store);
+
+/* Like store_device_create, but doesn't query the device for information. */
+error_t _store_device_create (device_t device, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store);
+
+/* Open the device NAME, and return the corresponding store in STORE. */
+error_t store_device_open (const char *name, int flags, struct store **store);
+
+/* Return a new store in STORE which contains a remap store of partition
+ PART from the contents of SOURCE; SOURCE is consumed. */
+error_t store_part_create (struct store *source, int index, int flags,
+ struct store **store);
+
+/* Open the part NAME. NAME consists of a partition number, a ':', a
+ another store class name, a ':' and a name for to by passed to the
+ store class. E.g. "2:device:hd0" would open the second partition
+ on a DEVICE store named "hd0". FLAGS indicate how to open the
+ store. CLASSES is as if passed to store_find_class, which see.
+ The new store is returned in *STORE. */
+error_t store_part_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Return a new store in STORE referring to the file FILE. Unlike
+ store_create, this will always use file i/o, even it would be possible to
+ be more direct. This may work in more cases, for instance if the file has
+ holes. Consumes the send right FILE. */
+error_t store_file_create (file_t file, int flags, struct store **store);
+
+/* Like store_file_create, but doesn't query the file for information. */
+error_t _store_file_create (file_t file, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store);
+
+/* Open the file NAME, and return the corresponding store in STORE. */
+error_t store_file_open (const char *name, int flags, struct store **store);
+
+/* Return a new store in STORE referring to the task TASK, consuming TASK. */
+error_t store_task_create (task_t task, int flags, struct store **store);
+
+/* Like store_task_create, but doesn't query the task for information. */
+error_t _store_task_create (task_t task, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store);
+
+/* Open the task NAME (NAME should be the task's pid), and return the
+ corresponding store in STORE. */
+error_t store_task_open (const char *name, int flags, struct store **store);
+
+/* Return a new store in STORE referring to the memory object MEMOBJ.
+ Consumes the send right MEMOBJ. */
+error_t store_memobj_create (memory_object_t memobj, int flags,
+ size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store);
+
+/* Open the network block device NAME (parsed as "HOSTNAME:PORT[/BLOCKSIZE]"),
+ and return the corresponding store in STORE. This opens a socket and
+ initial connection handshake, which determine the size of the device,
+ and then uses _store_nbd_create with the open socket port. */
+error_t store_nbd_open (const char *name, int flags, struct store **store);
+
+/* Create a store that works by talking to an nbd server on an existing
+ socket port. */
+error_t _store_nbd_create (mach_port_t port, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store);
+
+/* Return a new store of type "unknown" that holds a copy of the
+ given encoding. The name of the store is taken from ENC->data.
+ Future calls to store_encode/store_return will produce exactly
+ the encoding supplied here. All i/o operations fail with EFTYPE. */
+error_t store_unknown_decode (struct store_enc *enc,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Return a new store in STORE that interleaves all the stores in STRIPES
+ (NUM_STRIPES of them) every INTERLEAVE bytes; INTERLEAVE must be an
+ integer multiple of each stripe's block size. The stores in STRIPES are
+ consumed -- that is, will be freed when this store is (however, the
+ *array* STRIPES is copied, and so should be freed by the caller). */
+error_t store_ileave_create (struct store * const *stripes, size_t num_stripes,
+ store_offset_t interleave, int flags,
+ struct store **store);
+
+/* Return a new store in STORE that concatenates all the stores in STORES
+ (NUM_STORES of them). The stores in STRIPES are consumed -- that is, will
+ be freed when this store is (however, the *array* STRIPES is copied, and
+ so should be freed by the caller). */
+error_t store_concat_create (struct store * const *stores, size_t num_stores,
+ int flags, struct store **store);
+
+/* Return a new store that concatenates the stores created by opening all the
+ individual stores described in NAME; for the syntax of NAME, see
+ store_open_children. */
+error_t store_concat_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Return a new store in STORE that reflects the blocks in RUNS & RUNS_LEN
+ from SOURCE; SOURCE is consumed, but RUNS is not. Unlike the store_remap
+ function, this function always operates by creating a new store of type
+ `remap' which has SOURCE as a child, and so may be less efficient than
+ store_remap for some types of stores. */
+error_t store_remap_create (struct store *source,
+ const struct store_run *runs, size_t num_runs,
+ int flags, struct store **store);
+
+/* Return a new store in STORE which contains a snapshot of the contents of
+ the store FROM; FROM is consumed. */
+error_t store_copy_create (struct store *from, int flags, struct store **store);
+
+/* Open the copy store NAME -- which consists of another store-class
+ name, a ':', and a name for that store class to open -- and return
+ the corresponding store in STORE. CLASSES is as if passed to
+ store_find_class, which see. */
+error_t store_copy_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Return a new store in STORE which contains the memory buffer BUF, of
+ length BUF_LEN. BUF must be vm_allocated, and will be consumed. */
+error_t store_buffer_create (void *buf, size_t buf_len, int flags,
+ struct store **store);
+
+/* Return a new store in STORE which contains a snapshot of the uncompressed
+ contents of the store FROM; FROM is consumed. BLOCK_SIZE is the desired
+ block size of the result. */
+error_t store_gunzip_create (struct store *from, int flags,
+ struct store **store);
+
+/* Open the gunzip NAME -- which consists of another store-class name, a
+ ':', and a name for that store class to open -- and return the
+ corresponding store in STORE. CLASSES is as if passed to
+ store_find_class, which see. */
+error_t store_gunzip_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Return a new store in STORE which contains a snapshot of the uncompressed
+ contents of the store FROM; FROM is consumed. BLOCK_SIZE is the desired
+ block size of the result. */
+error_t store_bunzip2_create (struct store *from, int flags,
+ struct store **store);
+
+/* Open the bunzip2 NAME -- which consists of another store-class name, a ':',
+ and a name for that store class to open -- and return the corresponding
+ store in STORE. CLASSES is as if passed to store_find_class, which see. */
+error_t store_bunzip2_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Return a new store in STORE that multiplexes multiple physical volumes
+ from PHYS as one larger virtual volume. SWAP_VOLS is a function that will
+ be called whenever the volume currently active isn't correct. PHYS is
+ consumed. */
+error_t store_mvol_create (struct store *phys,
+ error_t (*swap_vols) (struct store *store, size_t new_vol,
+ ssize_t old_vol),
+ int flags,
+ struct store **store);
+
+/* Opening stores from a standard set of store classes.
+
+ These first two functions underlie the following functions, and
+ other functions such as store_open taking a CLASSES argument that
+ can be null. The standard set of classes to be searched when that
+ argument is null includes all the `const struct store_class *'
+ pointers found in the `store_std_classes' section of the executable
+ and all loaded shared objects; store_find_class searches that set
+ for the named class. The store_typed_open and store_url_open
+ functions also try store_module_find_class, but only if the
+ function has already been linked in; it's always available in the
+ shared library, and available for static linking with
+ -lstore_module -ldl.
+
+ The macro STORE_STD_CLASS produces a reference in the `store_std_classes'
+ section, so that linking in a module containing that definition will add
+ the referenced class to the standard search list. In the shared library,
+ the various standard classes are included this way. In the static
+ library, only the pseudo classes like `query' and `typed' will normally
+ be linked in (via referenced to store_open and so forth); to force
+ specific store type modules to be linked in, you must specify an
+ `-lstore_CLASS' option for each individual class to be statically linked.
+*/
+
+/* Find a store class by name. CLNAME_END points to the character
+ after the class name NAME points to; if null, then NAME is just the
+ null-terminated class name. */
+const struct store_class *
+store_find_class (const char *name,
+ const char *clname_end,
+ const struct store_class *const *classes);
+
+/* This is the underlying function that tries to load a module to
+ define the store type called NAME. On success, returns zero
+ and sets *CLASSP to the descriptor found. Returns ENOENT if
+ there is no such module, or other error codes if there is a
+ module but it does not load correctly. */
+error_t store_module_find_class (const char *name,
+ const char *clname_end,
+ const struct store_class **classp);
+
+
+/* Open the store indicated by NAME, which should consist of a store
+ type name followed by a ':' and any type-specific name, returning the
+ new store in STORE. CLASSES is as if passed to store_find_class,
+ which see. */
+error_t store_typed_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Similar to store_typed_open, but NAME must be in URL format, i.e. a
+ class name followed by a ':' and any type-specific name. A leading ':'
+ or no ':' at all is invalid syntax. (See store_module_open, below.) */
+error_t store_url_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* This attempts to decode a standard-form STORAGE_NETWORK encoding whose
+ encoded name is in URL format, by finding the store type indicated in
+ the URL (as for store_url_open) and that type's decode function. */
+error_t store_url_decode (struct store_enc *enc,
+ const struct store_class *const *classes,
+ struct store **store);
+
+
+/* Similar to store_typed_open, but the store type's code is found
+ dynamically rather than statically in CLASSES. A shared object name
+ for `dlopen' and symbol names for `dlsym' are derived from the type
+ name and used to find the `struct store_class' for the named type.
+ (CLASSES is used only by the type's own open function, e.g. if that
+ type accepts a child-store syntax in its name.)
+
+ In fact, when this code is linked in (always in the shared library,
+ only with `-lstore_module -ldl -lstore' for static linking), all
+ the functions documented as using STORE_STD_CLASSES will also
+ check for loadable modules if the type name is not found statically. */
+error_t store_module_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+
+/* This attempts to find a module that can decode ENC. If no such
+ module can be found it returns ENOENT. Otherwise it returns
+ the result of the loaded store type's `decode' function. */
+error_t store_module_decode (struct store_enc *enc,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Parse multiple store names in NAME, and open each individually, returning
+ all in the vector STORES, and the number in NUM_STORES. The syntax of
+ NAME is a single non-alpha-numeric separator character, followed by each
+ child store name separated by the same separator; each child name is
+ TYPE:NAME notation as parsed by store_typed_open. If every child uses the
+ same TYPE: prefix, then it may be factored out and put before the child
+ list instead (the two types of notation are differentiated by whether the
+ first character of name is alpha-numeric or not). */
+error_t store_open_children (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store ***stores, size_t *num_stores);
+
+
+/* Standard store classes implemented by libstore. */
+extern const struct store_class store_device_class;
+extern const struct store_class store_part_class;
+extern const struct store_class store_file_class;
+extern const struct store_class store_task_class;
+extern const struct store_class store_nbd_class;
+extern const struct store_class store_memobj_class;
+extern const struct store_class store_zero_class;
+extern const struct store_class store_ileave_class;
+extern const struct store_class store_concat_class;
+extern const struct store_class store_remap_class;
+extern const struct store_class store_query_class;
+extern const struct store_class store_copy_class;
+extern const struct store_class store_gunzip_class;
+extern const struct store_class store_bunzip2_class;
+extern const struct store_class store_typed_open_class;
+extern const struct store_class store_url_open_class;
+extern const struct store_class store_module_open_class;
+extern const struct store_class store_unknown_class;
+
+/* The following are not included in STORE_STD_CLASSES. */
+extern const struct store_class store_mvol_class;
+
+#define STORE_STD_CLASS(name) \
+ static const struct store_class *const store_std_classes_##name[] \
+ __attribute_used__ __attribute__ ((section ("store_std_classes"))) \
+ = { &store_##name##_class }
+
+
+extern const struct store_class *const __start_store_std_classes[] __attribute__ ((weak));
+extern const struct store_class *const __stop_store_std_classes[] __attribute__ ((weak));
+
+/* Used to hold the various bits that make up the representation of a store
+ for transmission via rpc. See <hurd/hurd_types.h> for an explanation of
+ the encodings for the various storage types. */
+struct store_enc
+{
+ /* Each of the four vectors used. All are vm_allocated. */
+ mach_port_t *ports;
+ int *ints;
+ loff_t *offsets;
+ char *data;
+
+ /* The sizes of the vectors. */
+ mach_msg_type_number_t num_ports, num_ints, num_offsets, data_len;
+
+ /* Offsets into the above vectors, for an encoding/decoding in progress. */
+ size_t cur_port, cur_int, cur_offset, cur_data;
+
+ /* Each of these is an `initial' version of the associated vector. When
+ store_enc_dealloc is called, any vector that is the same as its `init_'
+ version won't be deallocated. */
+ mach_port_t *init_ports;
+ int *init_ints;
+ loff_t *init_offsets;
+ char *init_data;
+};
+
+/* Initialize ENC. The given vector and sizes will be used for the encoding
+ if they are big enough (otherwise new ones will be automatically
+ allocated). */
+void store_enc_init (struct store_enc *enc,
+ mach_port_t *ports, mach_msg_type_number_t num_ports,
+ int *ints, mach_msg_type_number_t num_ints,
+ loff_t *offsets, mach_msg_type_number_t num_offsets,
+ char *data, mach_msg_type_number_t data_len);
+
+/* Deallocate storage used by the fields in ENC (but nothing is done with ENC
+ itself). */
+void store_enc_dealloc (struct store_enc *enc);
+
+/* Copy out the parameters from ENC into the given variables suitably for
+ returning from a file_get_storage_info rpc, and deallocate ENC. */
+void store_enc_return (struct store_enc *enc,
+ mach_port_t **ports, mach_msg_type_number_t *num_ports,
+ int **ints, mach_msg_type_number_t *num_ints,
+ loff_t **offsets, mach_msg_type_number_t *num_offsets,
+ char **data, mach_msg_type_number_t *data_len);
+
+/* Encode STORE into the given return variables, suitably for returning from a
+ file_get_storage_info rpc. */
+error_t store_return (const struct store *store,
+ mach_port_t **ports, mach_msg_type_number_t *num_ports,
+ int **ints, mach_msg_type_number_t *num_ints,
+ loff_t **offsets, mach_msg_type_number_t *num_offsets,
+ char **data, mach_msg_type_number_t *data_len);
+
+/* Encode STORE into ENC, which should have been prepared with
+ store_enc_init, or return an error. The contents of ENC may then be
+ return as the value of file_get_storage_info; if for some reason this
+ can't be done, store_enc_dealloc may be used to deallocate the mmemory
+ used by the unsent vectors. */
+error_t store_encode (const struct store *store, struct store_enc *enc);
+
+/* Decode ENC, either returning a new store in STORE, or an error.
+ CLASSES is as if passed to store_find_class, which see. If nothing
+ else is to be done with ENC, its contents may then be freed using
+ store_enc_dealloc. */
+error_t store_decode (struct store_enc *enc,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Calls the allocate_encoding method in each child store of STORE,
+ propagating any errors. If any child does not hae such a method,
+ EOPNOTSUPP is returned. */
+error_t store_allocate_child_encodings (const struct store *store,
+ struct store_enc *enc);
+
+/* Calls the encode method in each child store of STORE, propagating any
+ errors. If any child does not hae such a method, EOPNOTSUPP is returned. */
+error_t store_encode_children (const struct store *store,
+ struct store_enc *enc);
+
+/* Decodes NUM_CHILDREN from ENC, storing the results into successive
+ positions in CHILDREN. */
+error_t store_decode_children (struct store_enc *enc, int num_children,
+ const struct store_class *const *classes,
+ struct store **children);
+
+/* Call FUN with the vector RUNS of length NUM_RUNS extracted from ENC. */
+error_t store_with_decoded_runs (struct store_enc *enc, size_t num_runs,
+ error_t (*fun) (const struct store_run *runs,
+ size_t num_runs));
+
+/* Standard encoding used for most leaf store types. */
+error_t store_std_leaf_allocate_encoding (const struct store *store,
+ struct store_enc *enc);
+error_t store_std_leaf_encode (const struct store *store,
+ struct store_enc *enc);
+
+/* Creation function signature used by store_std_leaf_decode. */
+typedef error_t (*store_std_leaf_create_t)(mach_port_t port,
+ int flags,
+ size_t block_size,
+ const struct store_run *runs,
+ size_t num_runs,
+ struct store **store);
+
+/* Decodes the standard leaf encoding that's common to various builtin
+ formats, and calls CREATE to actually create the store. */
+error_t store_std_leaf_decode (struct store_enc *enc,
+ store_std_leaf_create_t create,
+ struct store **store);
+
+/* An argument parser that may be used for parsing a simple command line
+ specification for stores. The accompanying input parameter must be a
+ pointer to a struct store_argp_params. */
+extern struct argp store_argp;
+
+/* The structure used to pass args back and forth from STORE_ARGP. */
+struct store_argp_params
+{
+ /* The resulting parsed result. */
+ struct store_parsed *result;
+
+ /* If --store-type isn't specified use this; 0 is equivalent to "query". */
+ const char *default_type;
+
+ /* The set of classes used to validate store-types and argument syntax. */
+ const struct store_class *const *classes;
+
+ /* This controls the behavior when no store arguments are specified.
+ If zero, the parser fails with the error message "No store specified".
+ If nonzero, the parser succeeds and sets `result' to null. */
+ int store_optional;
+};
+
+/* The result of parsing a store, which should be enough information to open
+ it, or return the arguments. */
+struct store_parsed;
+
+/* Free all resources used by PARSED. */
+void store_parsed_free (struct store_parsed *parsed);
+
+/* Open PARSED, and return the corresponding store in STORE. */
+error_t store_parsed_open (const struct store_parsed *parsed, int flags,
+ struct store **store);
+
+/* Add the arguments used to create PARSED to ARGZ & ARGZ_LEN. */
+error_t store_parsed_append_args (const struct store_parsed *parsed,
+ char **argz, size_t *argz_len);
+
+/* Make a string describing PARSED, and return it in malloced storage in
+ NAME. */
+error_t store_parsed_name (const struct store_parsed *parsed, char **name);
+
+
+#endif /* __STORE_H__ */
diff --git a/libstore/stripe.c b/libstore/stripe.c
new file mode 100644
index 00000000..e9c58466
--- /dev/null
+++ b/libstore/stripe.c
@@ -0,0 +1,293 @@
+/* Striped store backend
+
+ Copyright (C) 1996,97,99,2001, 2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "store.h"
+
+extern long lcm (long p, long q);
+
+/* Return ADDR adjust for any block size difference between STORE and
+ STRIPE. We assume that STORE's block size is no less than STRIPE's. */
+static inline store_offset_t
+addr_adj (store_offset_t addr, struct store *store, struct store *stripe)
+{
+ unsigned common_bs = store->log2_block_size;
+ unsigned stripe_bs = stripe->log2_block_size;
+ if (common_bs == stripe_bs)
+ return addr;
+ else
+ return addr << (common_bs - stripe_bs);
+}
+
+static error_t
+stripe_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount,
+ void **buf, size_t *len)
+{
+ struct store *stripe = store->children[index];
+ return store_read (stripe, addr_adj (addr, store, stripe), amount, buf, len);
+}
+
+static error_t
+stripe_write (struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf, size_t len, size_t *amount)
+{
+ struct store *stripe = store->children[index];
+ return
+ store_write (stripe, addr_adj (addr, store, stripe), buf, len, amount);
+}
+
+error_t
+stripe_set_size (struct store *store, size_t newsize)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+stripe_remap (struct store *source,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ return store_remap_create (source, runs, num_runs, 0, store);
+}
+
+error_t
+ileave_allocate_encoding (const struct store *store, struct store_enc *enc)
+{
+ enc->num_ints += 4;
+ return store_allocate_child_encodings (store, enc);
+}
+
+error_t
+ileave_encode (const struct store *store, struct store_enc *enc)
+{
+ enc->ints[enc->cur_int++] = store->class->id;
+ enc->ints[enc->cur_int++] = store->flags;
+ enc->ints[enc->cur_int++] = store->wrap_dst; /* interleave factor */
+ enc->ints[enc->cur_int++] = store->num_children;
+ return store_encode_children (store, enc);
+}
+
+error_t
+ileave_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ if (enc->cur_int + 4 > enc->num_ints)
+ return EINVAL;
+ else
+ {
+ int type __attribute__((unused)) = enc->ints[enc->cur_int++];
+ int flags = enc->ints[enc->cur_int++];
+ int interleave = enc->ints[enc->cur_int++];
+ int nkids = enc->ints[enc->cur_int++];
+ struct store *kids[nkids];
+ error_t err = store_decode_children (enc, nkids, classes, kids);
+ if (! err)
+ err = store_ileave_create (kids, nkids, interleave, flags, store);
+ return err;
+ }
+}
+
+const struct store_class
+store_ileave_class =
+{
+ STORAGE_INTERLEAVE, "interleave", stripe_read, stripe_write, stripe_set_size,
+ ileave_allocate_encoding, ileave_encode, ileave_decode,
+ store_set_child_flags, store_clear_child_flags, 0, 0, stripe_remap
+};
+STORE_STD_CLASS (ileave);
+
+error_t
+concat_allocate_encoding (const struct store *store, struct store_enc *enc)
+{
+ enc->num_ints += 3;
+ return store_allocate_child_encodings (store, enc);
+}
+
+error_t
+concat_encode (const struct store *store, struct store_enc *enc)
+{
+ enc->ints[enc->cur_int++] = store->class->id;
+ enc->ints[enc->cur_int++] = store->flags;
+ enc->ints[enc->cur_int++] = store->num_children;
+ return store_encode_children (store, enc);
+}
+
+error_t
+concat_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ if (enc->cur_int + 3 > enc->num_ints)
+ return EINVAL;
+ else
+ {
+ int type __attribute__((unused)) = enc->ints[enc->cur_int++];
+ int flags = enc->ints[enc->cur_int++];
+ int nkids = enc->ints[enc->cur_int++];
+ struct store *kids[nkids];
+ error_t err = store_decode_children (enc, nkids, classes, kids);
+ if (! err)
+ err = store_concat_create (kids, nkids, flags, store);
+ return err;
+ }
+}
+
+const struct store_class
+store_concat_class =
+{
+ STORAGE_CONCAT, "concat", stripe_read, stripe_write, stripe_set_size,
+ concat_allocate_encoding, concat_encode, concat_decode,
+ store_set_child_flags, store_clear_child_flags, 0, 0, stripe_remap,
+ store_concat_open
+};
+STORE_STD_CLASS (concat);
+
+/* Return a new store in STORE that interleaves all the stores in STRIPES
+ (NUM_STRIPES of them) every INTERLEAVE bytes; INTERLEAVE must be an
+ integer multiple of each stripe's block size. The stores in STRIPES are
+ consumed -- that is, will be freed when this store is (however, the
+ *array* STRIPES is copied, and so should be freed by the caller). */
+error_t
+store_ileave_create (struct store *const *stripes, size_t num_stripes,
+ store_offset_t interleave, int flags,
+ struct store **store)
+{
+ size_t i;
+ error_t err;
+ size_t block_size = 1;
+ store_offset_t min_end = 0;
+ struct store_run runs[num_stripes];
+ int common_flags = STORE_BACKEND_FLAGS;
+
+ /* Find a common block size. */
+ for (i = 0; i < num_stripes; i++)
+ block_size = lcm (block_size, stripes[i]->block_size);
+
+ if (interleave < block_size || (interleave % block_size) != 0)
+ return EINVAL;
+
+ interleave /= block_size;
+
+ for (i = 0; i < num_stripes; i++)
+ {
+ /* The stripe's end adjusted to the common block size. */
+ store_offset_t end = stripes[i]->end;
+
+ runs[i].start = 0;
+ runs[i].length = interleave;
+
+ if (stripes[i]->block_size != block_size)
+ end /= (block_size / stripes[i]->block_size);
+
+ if (min_end < 0)
+ min_end = end;
+ else if (min_end > end)
+ /* Only use as much space as the smallest stripe has. */
+ min_end = end;
+
+ common_flags &= stripes[i]->flags;
+ }
+
+ err = _store_create (&store_ileave_class, MACH_PORT_NULL,
+ common_flags | flags, block_size,
+ runs, num_stripes, min_end, store);
+ if (! err)
+ {
+ (*store)->wrap_dst = interleave;
+
+ err = store_set_children (*store, stripes, num_stripes);
+ if (err)
+ store_free (*store);
+ }
+
+ return err;
+}
+
+/* Return a new store in STORE that concatenates all the stores in STORES
+ (NUM_STORES of them). The stores in STRIPES are consumed -- that is, will
+ be freed when this store is (however, the *array* STRIPES is copied, and
+ so should be freed by the caller). */
+error_t
+store_concat_create (struct store * const *stores, size_t num_stores,
+ int flags, struct store **store)
+{
+ size_t i;
+ error_t err;
+ size_t block_size = 1;
+ int common_flags = STORE_BACKEND_FLAGS;
+ struct store_run runs[num_stores];
+
+ /* Find a common block size. */
+ for (i = 0; i < num_stores; i++)
+ block_size = lcm (block_size, stores[i]->block_size);
+
+ for (i = 0; i < num_stores; i++)
+ {
+ runs[i].start = 0;
+ runs[i].length = stores[i]->end;
+ common_flags &= stores[i]->flags;
+ }
+
+ err = _store_create (&store_concat_class, MACH_PORT_NULL,
+ flags | common_flags, block_size,
+ runs, num_stores, 0, store);
+ if (! err)
+ {
+ err = store_set_children (*store, stores, num_stores);
+ if (! err)
+ {
+ err = store_children_name (*store, &(*store)->name);
+ if (err == EINVAL)
+ err = 0; /* Can't find a name; deal. */
+ }
+ if (err)
+ store_free (*store);
+ }
+
+ return err;
+}
+
+/* Return a new store that concatenates the stores created by opening all the
+ individual stores described in NAME; for the syntax of NAME, see
+ store_open_children. */
+error_t
+store_concat_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ struct store **stores;
+ size_t num_stores;
+ error_t err =
+ store_open_children (name, flags, classes, &stores, &num_stores);
+ if (! err)
+ {
+ err = store_concat_create (stores, num_stores, flags, store);
+ if (err)
+ {
+ size_t k;
+ for (k = 0; k < (*store)->num_children; k++)
+ store_free ((*store)->children[k]);
+ }
+ }
+ return err;
+}
diff --git a/libstore/task.c b/libstore/task.c
new file mode 100644
index 00000000..ea1475c8
--- /dev/null
+++ b/libstore/task.c
@@ -0,0 +1,205 @@
+/* Mach task store backend
+
+ Copyright (C) 1995,96,97,2001, 2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include <hurd.h>
+
+#include <mach/machine/vm_param.h>
+
+#include "store.h"
+
+static process_t
+proc_server ()
+{
+ static process_t proc = MACH_PORT_NULL;
+ if (proc == MACH_PORT_NULL)
+ proc = getproc ();
+ return proc;
+}
+
+static error_t
+topen (const char *name, task_t *task)
+{
+ char *name_end;
+ pid_t pid = strtoul (name, &name_end, 0);
+
+ if (*name == '\0' || *name_end != '\0')
+ return EINVAL;
+
+ return proc_pid2task (proc_server (), pid, task);
+}
+
+static void
+tclose (struct store *store)
+{
+ mach_port_deallocate (mach_task_self (), store->port);
+ store->port = MACH_PORT_NULL;
+}
+
+static error_t
+task_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount, void **buf, size_t *len)
+{
+ size_t bsize = store->block_size;
+ return vm_read (store->port, addr * bsize, amount, (vm_address_t *)buf, len);
+}
+
+static error_t
+task_write (struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf, size_t len, size_t *amount)
+{
+ size_t bsize = store->block_size;
+ error_t err = vm_write (store->port, addr * bsize, (vm_address_t)buf, len);
+ if (! err)
+ *amount = len;
+ return err;
+}
+
+static error_t
+task_set_size (struct store *store, size_t newsize)
+{
+ return EOPNOTSUPP;
+}
+
+static error_t
+task_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_std_leaf_decode (enc, _store_task_create, store);
+}
+
+static error_t
+task_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_task_open (name, flags, store);
+}
+
+static error_t
+task_set_flags (struct store *store, int flags)
+{
+ if ((flags & ~(STORE_INACTIVE | STORE_ENFORCED)) != 0)
+ /* Trying to set flags we don't support. */
+ return EINVAL;
+
+ if ((flags & STORE_ENFORCED)
+ && (store->num_runs > 0
+ || store->runs[0].start != 0
+ || store->runs[0].length != (VM_MAX_ADDRESS >> store->log2_block_size)))
+ /* Kernel only enforces the whole thing... */
+ return EINVAL;
+
+ if (flags & STORE_INACTIVE)
+ tclose (store);
+
+ store->flags |= flags; /* When inactive, anything goes. */
+
+ return 0;
+}
+
+static error_t
+task_clear_flags (struct store *store, int flags)
+{
+ error_t err = 0;
+ if ((flags & ~(STORE_INACTIVE | STORE_ENFORCED)) != 0)
+ err = EINVAL;
+ if (!err && (flags & STORE_INACTIVE))
+ err = store->name ? topen (store->name, &store->port) : ESRCH;
+ if (! err)
+ store->flags &= ~flags;
+ return err;
+}
+
+const struct store_class
+store_task_class =
+{
+ STORAGE_TASK, "task", task_read, task_write, task_set_size,
+ store_std_leaf_allocate_encoding, store_std_leaf_encode, task_decode,
+ task_set_flags, task_clear_flags, 0, 0, 0, task_open
+};
+STORE_STD_CLASS (task);
+
+/* Return a new store in STORE referring to the mach task TASK. Consumes
+ the send right TASK. */
+error_t
+store_task_create (task_t task, int flags, struct store **store)
+{
+ struct store_run run;
+
+ run.start = 0;
+ run.length = VM_MAX_ADDRESS / vm_page_size;
+
+ flags |= STORE_ENFORCED; /* 'cause it's the whole task. */
+
+ return _store_task_create (task, flags, vm_page_size, &run, 1, store);
+}
+
+/* Like store_task_create, but doesn't query the task for information. */
+error_t
+_store_task_create (task_t task, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ error_t err = 0;
+
+ if (block_size >= vm_page_size)
+ err = _store_create (&store_task_class,
+ task, flags, block_size, runs, num_runs, 0, store);
+ else
+ err = EINVAL; /* block size less than page size. */
+
+ if (! err)
+ {
+ pid_t pid;
+
+ err = proc_task2pid (proc_server (), task, &pid);
+ if (! err)
+ {
+ char buf[20];
+ snprintf (buf, sizeof buf, "%u", pid);
+ err = store_set_name (*store, buf);
+ }
+
+ if (err)
+ store_free (*store);
+ }
+
+ return err;
+}
+
+/* Open the task NAME, and return the corresponding store in STORE. */
+error_t
+store_task_open (const char *name, int flags, struct store **store)
+{
+ task_t task;
+ error_t err = topen (name, &task);
+
+ if (! err)
+ {
+ err = store_task_create (task, flags, store);
+ if (err)
+ mach_port_deallocate (mach_task_self (), task);
+ }
+
+ return err;
+}
diff --git a/libstore/typed.c b/libstore/typed.c
new file mode 100644
index 00000000..b1be747d
--- /dev/null
+++ b/libstore/typed.c
@@ -0,0 +1,177 @@
+/* Support for opening `typed' stores
+
+ Copyright (C) 1997,1998,2001,2002,2003,2004 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "store.h"
+#include <string.h>
+#include <dlfcn.h>
+#include <link.h>
+
+
+const struct store_class *
+store_find_class (const char *name, const char *clname_end,
+ const struct store_class *const *classes)
+{
+ const struct store_class *const *cl;
+
+ if (! clname_end)
+ clname_end = strchr (name, '\0');
+
+ if (classes != 0)
+ {
+ /* The caller gave a class list, so that's is all we'll use. */
+ for (cl = classes; *cl != 0; ++cl)
+ if (strlen ((*cl)->name) == (clname_end - name)
+ && !memcmp (name, (*cl)->name, (clname_end - name)))
+ break;
+ return *cl;
+ }
+
+ /* Check the statically-linked set of classes found in the
+ "store_std_classes" section. For static linking, this is the section
+ in the program executable itself and it has been populated by the set
+ of -lstore_TYPE pseudo-libraries included in the link. For dynamic
+ linking with just -lstore, these symbols will be found in libstore.so
+ and have the set statically included when the shared object was built.
+ If a dynamically-linked program has its own "store_std_classes"
+ section, e.g. by -lstore_TYPE objects included in the link, this will
+ be just that section and libstore.so itself is covered below. */
+ for (cl = __start_store_std_classes; cl < __stop_store_std_classes; ++cl)
+ if (strlen ((*cl)->name) == (clname_end - name)
+ && strncmp (name, (*cl)->name, (clname_end - name)) == 0)
+ return *cl;
+
+ /* Now we will iterate through all of the dynamic objects loaded
+ and examine each one's "store_std_classes" section. */
+# pragma weak _r_debug
+# pragma weak dlsym
+# pragma weak dlopen
+# pragma weak dlclose
+# pragma weak dlerror
+ if (dlsym)
+ {
+ struct link_map *map;
+ for (map = _r_debug.r_map; map != 0; map = map->l_next)
+ {
+ const struct store_class *const *start, *const *stop;
+
+ /* We cannot just use MAP directly because it may not have been
+ opened by dlopen such that its data structures are fully set
+ up for dlsym. */
+ void *module = dlopen (map->l_name, RTLD_NOLOAD);
+ if (module == 0)
+ {
+ (void) dlerror (); /* Required to avoid a leak! */
+ continue;
+ }
+
+ start = dlsym (map, "__start_store_std_classes");
+ if (start == 0)
+ (void) dlerror (); /* Required to avoid a leak! */
+ else if (start != __start_store_std_classes) /* */
+ {
+ stop = dlsym (map, "__stop_store_std_classes");
+ if (stop == 0)
+ (void) dlerror (); /* Required to avoid a leak! */
+ else
+ for (cl = start; cl < stop; ++cl)
+ if (strlen ((*cl)->name) == (clname_end - name)
+ && strncmp (name, (*cl)->name, (clname_end - name)) == 0)
+ {
+ dlclose (module);
+ return *cl;
+ }
+ }
+ dlclose (module);
+ }
+ }
+
+ return 0;
+}
+
+
+/* Open the store indicated by NAME, which should consist of a store type
+ name followed by a ':' and any type-specific name, returning the new store
+ in STORE. If NAME doesn't contain a `:', then it will be interpreted as
+ either a class name, if such a class occurs in CLASSES, or a filename,
+ which is opened by calling store_open on NAME; a `:' at the end or the
+ beginning of NAME unambiguously causes the remainder to be treated as a
+ class-name or a filename, respectively. CLASSES is used to select classes
+ specified by the type name; if it is 0, STORE_STD_CLASSES is used. */
+error_t
+store_typed_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ const struct store_class *cl;
+ const char *clname_end = strchrnul (name, ':');
+
+ if (clname_end == name && *clname_end)
+ /* Open NAME with store_open. */
+ return store_open (name + 1, flags, classes, store);
+
+ /* Try to find an existing class by the given name. */
+ cl = store_find_class (name, clname_end, classes);
+ if (cl != 0)
+ {
+ if (! cl->open)
+ /* CL cannot be opened. */
+ return EOPNOTSUPP;
+
+ if (*clname_end)
+ /* Skip the ':' separating the class-name from the device name. */
+ clname_end++;
+
+ if (! *clname_end)
+ /* The class-specific portion of the name is empty, so make it *really*
+ empty. */
+ clname_end = 0;
+
+ return (*cl->open) (clname_end, flags, classes, store);
+ }
+
+ /* Try to open a store by loading a module to define the class, if we
+ have the module-loading support linked in. We don't just use
+ store_module_find_class, because store_module_open will unload the new
+ module if the open doesn't succeed and we have no other way to unload
+ it. We always leave modules loaded once a store from the module has
+ been successfully opened and so can leave unbounded numbers of old
+ modules loaded after closing all the stores using them. But at least
+ we can avoid having modules loaded for stores we never even opened. */
+# pragma weak store_module_open
+ if (store_module_open)
+ {
+ error_t err = store_module_open (name, flags, classes, store);
+ if (err != ENOENT)
+ return err;
+ }
+
+ /* No class with the given name found. */
+ if (*clname_end)
+ /* NAME really should be a class name, which doesn't exist. */
+ return EINVAL;
+ else
+ /* Try opening NAME by querying it as a file instead. */
+ return store_open (name, flags, classes, store);
+}
+
+const struct store_class
+store_typed_open_class = { -1, "typed", open: store_typed_open };
+STORE_STD_CLASS (typed_open);
diff --git a/libstore/unknown.c b/libstore/unknown.c
new file mode 100644
index 00000000..8b7f4268
--- /dev/null
+++ b/libstore/unknown.c
@@ -0,0 +1,231 @@
+/* Store backend for unknown encodings
+
+ Copyright (C) 2001,02 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "store.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+/* You can't do anything with an unknown store but encode it. */
+
+static error_t
+noread (struct store *store, store_offset_t addr, size_t index,
+ size_t amount, void **buf, size_t *len)
+{
+ return EFTYPE;
+}
+
+static error_t
+nowrite (struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf, size_t len, size_t *amount)
+{
+ return EFTYPE;
+}
+
+static error_t
+noset_size (struct store *store, size_t newsize)
+{
+ return EFTYPE;
+}
+
+static error_t
+noflags (struct store *store, int flags)
+{
+ return EINVAL;
+}
+
+/* This is the only way that stores of the "unknown" class get created.
+ We save the store encoding verbatim and regurgitate it as our own. */
+
+static struct store_enc *
+duplicate_encoding (struct store_enc *enc)
+{
+ struct store_enc *us;
+ size_t i;
+
+ us = calloc (1, sizeof *us);
+ if (us == NULL)
+ return NULL;
+
+ us->ports = mmap (0, enc->num_ports * sizeof *enc->ports,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (us->ports == MAP_FAILED)
+ {
+ no_memory:
+ store_enc_dealloc (us);
+ free (us);
+ return NULL;
+ }
+ us->ints = mmap (0, enc->num_ints * sizeof *enc->ints,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (us->ints == MAP_FAILED)
+ goto no_memory;
+ us->offsets = mmap (0, enc->num_offsets * sizeof *enc->offsets,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (us->offsets == MAP_FAILED)
+ goto no_memory;
+ us->data = mmap (0, enc->data_len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (us->data == MAP_FAILED)
+ goto no_memory;
+
+ memcpy (us->ports, enc->ports, enc->num_ports * sizeof *enc->ports);
+ memcpy (us->ints, enc->ints, enc->num_ints * sizeof *enc->ints);
+ memcpy (us->offsets, enc->offsets, enc->num_offsets * sizeof *enc->offsets);
+ memcpy (us->data, enc->data, enc->data_len);
+
+ us->num_ports = enc->num_ports;
+ us->num_ints = enc->num_ints;
+ us->num_offsets = enc->num_offsets;
+ us->data_len = enc->data_len;
+
+ for (i = 0; i < us->num_ports; ++i)
+ mach_port_mod_refs (mach_task_self (), us->ports[i],
+ MACH_PORT_RIGHT_SEND, +1);
+
+ return us;
+}
+
+error_t
+store_unknown_decode (struct store_enc *enc,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ struct store_enc *us;
+ error_t err = _store_create (&store_unknown_class,
+ MACH_PORT_NULL, STORE_ENFORCED, 0, NULL, 0, 0,
+ store);
+ if (err)
+ return err;
+
+ us = duplicate_encoding (enc);
+ if (us == NULL)
+ {
+ store_free (*store);
+ return ENOMEM;
+ }
+ (*store)->hook = us;
+
+ /* Derive a name for this unknown store from its encoded type field
+ (or lack thereof) and the leading string of its encoded data bytes. */
+ if (enc->cur_int == enc->num_ints)
+ asprintf (&(*store)->name, "notype:%.*s",
+ (int) (us->data_len - us->cur_data), us->data + us->cur_data);
+ else
+ asprintf (&(*store)->name, "type-%d:%.*s", enc->ints[enc->cur_int],
+ (int) ( us->data_len - us->cur_data), us->data + us->cur_data);
+
+ return 0;
+}
+
+/* Re-encode just the way we got it. */
+error_t
+unknown_allocate_encoding (const struct store *store, struct store_enc *enc)
+{
+ const struct store_enc *us = store->hook;
+ if (us == NULL)
+ return EOPNOTSUPP;
+ enc->num_ports += us->num_ports;
+ enc->num_ints += us->num_ints;
+ enc->num_offsets += us->num_offsets;
+ enc->data_len += us->data_len;
+ return 0;
+}
+
+error_t
+unknown_encode (const struct store *store, struct store_enc *enc)
+{
+ const struct store_enc *us = store->hook;
+ if (us == NULL)
+ return EOPNOTSUPP;
+
+ memcpy (enc->ports, us->ports, us->num_ports * sizeof enc->ports[0]);
+ enc->ports += us->num_ports;
+ memcpy (enc->ints, us->ints, us->num_ints * sizeof enc->ints[0]);
+ enc->ints += us->num_ints;
+ memcpy (enc->offsets, us->offsets, us->num_offsets * sizeof enc->offsets[0]);
+ enc->offsets += us->num_offsets;
+ memcpy (enc->data + enc->cur_data, us->data, us->data_len);
+ enc->cur_data += us->data_len;
+
+ return 0;
+}
+
+
+/* Called just before deallocating STORE. */
+static void
+unknown_cleanup (struct store *store)
+{
+ if (store->hook != NULL)
+ {
+ store_enc_dealloc (store->hook);
+ free (store->hook);
+ }
+}
+
+/* Copy any format-dependent fields in FROM to TO; if there's some reason
+ why the copy can't be made, an error should be returned. This call is
+ made after all format-independent fields have been cloned. */
+static error_t
+unknown_clone (const struct store *from, struct store *to)
+{
+ if (from->hook == NULL)
+ return 0;
+ to->hook = duplicate_encoding (from->hook);
+ return to->hook ? 0 : ENOMEM;
+}
+
+/* Unknown stores cannot be opened with a name. */
+static error_t
+unknown_validate_name (const char *name,
+ const struct store_class *const *classes)
+{
+ return name == NULL ? 0 : EINVAL;
+}
+
+static error_t
+unknown_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ return (name == NULL
+ ? _store_create (&store_unknown_class, MACH_PORT_NULL,
+ STORE_ENFORCED, 0, NULL, 0, 0, store)
+ : EINVAL);
+}
+
+const struct store_class store_unknown_class =
+{
+ -1, "unknown",
+ read: noread,
+ write: nowrite,
+ set_size: noset_size,
+ allocate_encoding: unknown_allocate_encoding,
+ encode: unknown_encode,
+ decode: store_unknown_decode,
+ set_flags: noflags,
+ clear_flags: noflags,
+ cleanup: unknown_cleanup,
+ clone: unknown_clone,
+ open: unknown_open,
+ validate_name: unknown_validate_name,
+};
+STORE_STD_CLASS (unknown);
diff --git a/libstore/unzipstore.c b/libstore/unzipstore.c
new file mode 100644
index 00000000..8d500c1b
--- /dev/null
+++ b/libstore/unzipstore.c
@@ -0,0 +1,267 @@
+/* Decompressing store backend (common code for gunzip and bunzip2)
+
+ Copyright (C) 1998, 1999, 2002 Free Software Foundation, Inc.
+ Written by okuji@kuicr.kyoto-u.ac.jp <okuji@kuicr.kyoto-u.ac.jp>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+#include <pthread.h>
+#include <sys/mman.h>
+
+#include "store.h"
+
+#define IN_BUFFERING (256*1024)
+#define OUT_BUFFERING (512*1024)
+
+static pthread_mutex_t unzip_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#define STORE_UNZIP(name) STORE_UNZIP_1 (UNZIP, name)
+#define STORE_UNZIP_1(unzip,name) STORE_UNZIP_2 (unzip, name)
+#define STORE_UNZIP_2(unzip,name) store_##unzip##_##name
+#define STORE_STD_CLASS_1(name) STORE_STD_CLASS(name)
+#define STRINGIFY(name) STRINGIFY_1(name)
+#define STRINGIFY_1(name) #name
+
+
+/* Uncompress the contents of FROM, which should contain a valid bzip2 file,
+ into memory, returning the result buffer in BUF & BUF_LEN. */
+static error_t
+unzip_store (struct store *from, void **buf, size_t *buf_len)
+{
+ /* Callbacks from decompression engine for I/O and error interface. */
+ extern int (*unzip_read) (char *buf, size_t maxread);
+ extern void (*unzip_write) (const char *buf, size_t nwrite);
+ extern void (*unzip_read_error) (void);
+ extern void (*unzip_error) (const char *msg);
+
+ /* How we return errors from our hook functions. */
+ jmp_buf zerr_jmp_buf;
+ error_t zerr;
+
+ /* vm_alloced buffer for the input store. */
+ void *in_buf = 0;
+ size_t in_buf_len = 0; /* Amount of valid data in IN_BUF. */
+ size_t in_buf_size = 0; /* Allocated space for IN_BUF. */
+ size_t in_buf_offs = 0; /* Offset of read point in IN_BUF. */
+ off_t in_buf_addr = 0; /* Address in FROM of *next* IN_BUF. */
+
+ /* Buffer input in units that are least IN_BUFFERING bytes, but are also a
+ multiple of FROM's block size. */
+ size_t in_addr_mask = ((1 << from->log2_block_size) - 1);
+ size_t in_buffering = ((IN_BUFFERING + in_addr_mask) & ~in_addr_mask);
+
+ /* Read at most MAXREAD (or 0 if eof) bytes into BUF from our current
+ position in FROM. */
+ int zread (char *buf, size_t maxread)
+ {
+ size_t did_read = 0;
+
+ while (maxread > 0)
+ {
+ size_t left = in_buf_len - in_buf_offs;
+
+ if (left > 0)
+ /* Fill BUF with what we can from IN_BUF. */
+ {
+ if (left > maxread)
+ left = maxread;
+ bcopy (in_buf + in_buf_offs, buf, left);
+ in_buf_offs += left;
+ buf += left;
+ maxread -= left;
+ did_read += left;
+ }
+
+ /* Limit MAXREAD to the number of bytes left in the input. */
+ if (maxread > (from->size - (in_buf_addr << from->log2_block_size)))
+ maxread = from->size - (in_buf_addr << from->log2_block_size);
+
+ if (maxread > 0)
+ /* Have to fill IN_BUF again. */
+ {
+ void *new_in_buf = in_buf;
+ size_t new_in_buf_len = in_buf_len;
+
+ zerr = store_read (from, in_buf_addr, in_buffering,
+ &new_in_buf, &new_in_buf_len);
+ if (zerr)
+ longjmp (zerr_jmp_buf, 1);
+
+ in_buf_addr += (new_in_buf_len >> from->log2_block_size);
+
+ if (new_in_buf != in_buf)
+ {
+ if (in_buf_size > 0)
+ munmap (in_buf, in_buf_size);
+ in_buf = new_in_buf;
+ in_buf_size = new_in_buf_len;
+ }
+
+ in_buf_len = new_in_buf_len;
+ in_buf_offs = 0;
+ }
+ }
+ return did_read;
+ }
+
+ size_t out_buf_offs = 0; /* Position in the output buffer. */
+
+ /* Write compress data to our output buffer. */
+ void zwrite (const char *wbuf, size_t nwrite)
+ {
+ size_t old_buf_len = *buf_len;
+
+ if (out_buf_offs + nwrite > old_buf_len)
+ /* Have to grow the output buffer. */
+ {
+ void *old_buf = *buf;
+ void *new_buf = old_buf + old_buf_len; /* First try. */
+ size_t new_buf_len = round_page (old_buf_len + old_buf_len + nwrite);
+
+ /* Try to grow the buffer. */
+ zerr =
+ vm_allocate (mach_task_self (),
+ (vm_address_t *)&new_buf, new_buf_len - old_buf_len,
+ 0);
+ if (zerr)
+ /* Can't do that, try to make a bigger buffer elsewhere. */
+ {
+ new_buf = mmap (0, new_buf_len, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ zerr = (new_buf == (void *) -1) ? errno : 0;
+ if (zerr)
+ longjmp (zerr_jmp_buf, 1);
+
+ if (out_buf_offs > 0)
+ /* Copy the old buffer into the start of the new & free it. */
+ bcopy (old_buf, new_buf, out_buf_offs);
+
+ munmap (old_buf, old_buf_len);
+
+ *buf = new_buf;
+ }
+
+ *buf_len = new_buf_len;
+ }
+
+ bcopy (wbuf, *buf + out_buf_offs, nwrite);
+ out_buf_offs += nwrite;
+ }
+
+ void zreaderr (void)
+ {
+ zerr = EIO;
+ longjmp (zerr_jmp_buf, 1);
+ }
+ void zerror (const char *msg)
+ {
+ zerr = EINVAL;
+ longjmp (zerr_jmp_buf, 2);
+ }
+
+ /* Try to guess a reasonable output buffer size. */
+ *buf_len = round_page (from->size * 2);
+ *buf = mmap (0, *buf_len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ zerr = (*buf == (void *) -1) ? errno : 0;
+ if (zerr)
+ return zerr;
+
+ pthread_mutex_lock (&unzip_lock);
+
+ unzip_read = zread;
+ unzip_write = zwrite;
+ unzip_read_error = zreaderr;
+ unzip_error = zerror;
+
+ if (! setjmp (zerr_jmp_buf))
+ {
+ /* Call the decompression engine. */
+ zerr = DO_UNZIP ();
+ }
+
+ pthread_mutex_unlock (&unzip_lock);
+
+ if (in_buf_size > 0)
+ munmap (in_buf, in_buf_size);
+
+ if (zerr)
+ {
+ if (*buf_len > 0)
+ munmap (*buf, *buf_len);
+ }
+ else if (out_buf_offs < *buf_len)
+ /* Trim the output buffer to be the right length. */
+ {
+ size_t end = round_page (out_buf_offs);
+ if (end < *buf_len)
+ munmap (*buf + end, *buf_len - end);
+ *buf_len = out_buf_offs;
+ }
+
+ return zerr;
+}
+
+
+/* Return a new store in STORE which contains a snapshot of the uncompressed
+ contents of the store FROM; FROM is consumed. */
+error_t
+STORE_UNZIP(create) (struct store *from, int flags, struct store **store)
+{
+ void *buf;
+ size_t buf_len;
+ error_t err = unzip_store (from, &buf, &buf_len);
+
+ if (! err)
+ {
+ err = store_buffer_create (buf, buf_len, flags, store);
+ if (err)
+ munmap (buf, buf_len);
+ else
+ store_free (from);
+ }
+
+ return err;
+}
+
+/* Open the compressed store NAME -- which consists of another store-class
+ name, a ':', and a name for that store class to open -- and return the
+ corresponding store in STORE. CLASSES is used to select classes
+ specified by the type name; if it is 0, STORE_STD_CLASSES is used. */
+error_t
+STORE_UNZIP(open) (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ struct store *from;
+ error_t err =
+ store_typed_open (name, flags | STORE_HARD_READONLY, classes, &from);
+
+ if (! err)
+ {
+ err = STORE_UNZIP(create) (from, flags, store);
+ if (err)
+ store_free (from);
+ }
+
+ return err;
+}
+
+const struct store_class STORE_UNZIP(class) =
+{ -1, STRINGIFY(UNZIP), open: STORE_UNZIP(open) };
+STORE_STD_CLASS_1 (UNZIP);
diff --git a/libstore/url.c b/libstore/url.c
new file mode 100644
index 00000000..9b5f524e
--- /dev/null
+++ b/libstore/url.c
@@ -0,0 +1,92 @@
+/* Support for opening stores named in URL syntax.
+
+ Copyright (C) 2001,02 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "store.h"
+#include <string.h>
+#include <stdlib.h>
+
+
+/* Similar to store_typed_open, but NAME must be in URL format,
+ i.e. a class name followed by a ':' and any type-specific name.
+ Store classes opened this way must strip off the "class:" prefix.
+ A leading ':' or no ':' at all is invalid syntax. */
+
+error_t
+store_url_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ if (name == 0 || name[0] == ':' || strchr (name, ':') == 0)
+ return EINVAL;
+
+ return store_typed_open (name, flags, classes, store);
+}
+
+error_t
+store_url_decode (struct store_enc *enc,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ const struct store_class *cl;
+
+ /* This is pretty bogus. We use decode.c's code just to validate
+ the generic format and extract the name from the data. */
+ struct store dummy, *dummyptr;
+ error_t dummy_create (mach_port_t port, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+ {
+ *store = &dummy;
+ return 0;
+ }
+ struct store_enc dummy_enc = *enc;
+ error_t err = store_std_leaf_decode (&dummy_enc, &dummy_create, &dummyptr);
+ if (err)
+ return err;
+
+ /* Find the class matching this name. */
+ cl = store_find_class (dummy.name, strchr (dummy.name, ':'), classes);
+# pragma weak store_module_find_class
+ if (cl == 0 && store_module_find_class)
+ err = store_module_find_class (dummy.name, strchr (dummy.name, ':'),
+ &cl);
+ free (dummy.name);
+ free (dummy.misc);
+
+ if (cl == 0)
+ return EINVAL;
+
+ /* Now that we have the class, we just punt to its own decode hook. */
+
+ return (!cl->decode ? EOPNOTSUPP : (*cl->decode) (enc, classes, store));
+}
+
+/* This class is only trivially different from the "typed" class when used
+ by name. Its real purpose is to decode file_get_storage_info results
+ that use the STORAGE_NETWORK type, for which the convention is that the
+ name be in URL format (i.e. "type:something"). */
+
+const struct store_class store_url_open_class =
+{
+ STORAGE_NETWORK, "url",
+ open: store_url_open,
+ decode: store_url_decode
+};
+STORE_STD_CLASS (url_open);
diff --git a/libstore/util.c b/libstore/util.c
new file mode 100644
index 00000000..59085241
--- /dev/null
+++ b/libstore/util.c
@@ -0,0 +1,19 @@
+/* Hacked and slashed by roland@gnu.ai.mit.edu for use in Hurd exec server. */
+
+/* util.c -- utility functions for gzip support
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+#ifdef RCSID
+static char rcsid[] = "$Id: util.c,v 1.1 1994/12/14 04:29:37 roland Exp $";
+#endif
+
+#include <stddef.h>
+
+/* I/O interface */
+int (*unzip_read) (char *buf, size_t maxread);
+void (*unzip_write) (const char *buf, size_t nwrite);
+void (*unzip_read_error) (void);
+void (*unzip_error) (const char *msg);
diff --git a/libstore/xinl.c b/libstore/xinl.c
new file mode 100644
index 00000000..90242212
--- /dev/null
+++ b/libstore/xinl.c
@@ -0,0 +1,2 @@
+#define STORE_DEFINE_EI
+#include "store.h"
diff --git a/libstore/zero.c b/libstore/zero.c
new file mode 100644
index 00000000..2fba72cc
--- /dev/null
+++ b/libstore/zero.c
@@ -0,0 +1,196 @@
+/* Zero store backend
+
+ Copyright (C) 1995,96,97,99,2000,01, 02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/mman.h>
+
+#include "store.h"
+
+static error_t
+zero_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount, void **buf,
+ size_t *len)
+{
+ if (*len < amount)
+ {
+ *buf = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*buf == MAP_FAILED)
+ return errno;
+ *len = amount;
+ return 0;
+ }
+ else
+ memset (*buf, 0, amount);
+
+ *len = amount;
+ return 0;
+}
+
+static error_t
+zero_write (struct store *store,
+ store_offset_t addr, size_t index, const void *buf, size_t len,
+ size_t *amount)
+{
+ return 0;
+}
+
+static error_t
+zero_set_size (struct store *store, size_t newsize)
+{
+ return EOPNOTSUPP;
+}
+
+/* Modify SOURCE to reflect those runs in RUNS, and return it in STORE. */
+error_t
+zero_remap (struct store *source,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ /* Because all blocks are the same, a zero store always contains just one
+ run; here we simply count up the number of blocks specified by RUNS, and
+ modify SOURCE's one run to reflect that. */
+ int i;
+ store_offset_t length = 0, old_length = source->runs[0].length;
+ for (i = 0; i < num_runs; i++)
+ if (runs[i].start < 0 || runs[i].start + runs[i].length >= old_length)
+ return EINVAL;
+ else
+ length += runs[i].length;
+ source->runs[0].length = length;
+ *store = source;
+ return 0;
+}
+
+error_t
+zero_allocate_encoding (const struct store *store, struct store_enc *enc)
+{
+ enc->num_ints += 2;
+ enc->num_offsets += 1;
+ return 0;
+}
+
+error_t
+zero_encode (const struct store *store, struct store_enc *enc)
+{
+ enc->ints[enc->cur_int++] = store->class->id;
+ enc->ints[enc->cur_int++] = store->flags;
+ enc->offsets[enc->cur_offset++] = store->size;
+ return 0;
+}
+
+error_t
+zero_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ store_offset_t size;
+ int type, flags;
+
+ if (enc->cur_int + 2 > enc->num_ints
+ || enc->cur_offset + 1 > enc->num_offsets)
+ return EINVAL;
+
+ type = enc->ints[enc->cur_int++];
+ flags = enc->ints[enc->cur_int++];
+ size = enc->offsets[enc->cur_offset++];
+
+ return store_zero_create (size, flags, store);
+}
+
+static error_t
+zero_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ if (name)
+ {
+ char *end;
+ store_offset_t size = strtoull (name, &end, 0);
+ if (end == name || end == NULL)
+ return EINVAL;
+ switch (*end)
+ {
+ case 'b':
+ size *= 512;
+ break;
+ case 'k':
+ case 'K':
+ size *= 1024;
+ break;
+ case 'm':
+ case 'M':
+ size *= 1024 * 1024;
+ break;
+ case 'g':
+ case 'G':
+ size *= 1024 * 1024 * 1024;
+ break;
+ }
+ return store_zero_create (size, flags, store);
+ }
+ else
+ {
+ store_offset_t max_offs = ~((store_offset_t)1
+ << (CHAR_BIT * sizeof (store_offset_t) - 1));
+ return store_zero_create (max_offs, flags, store);
+ }
+}
+
+static error_t
+zero_validate_name (const char *name, const struct store_class *const *classes)
+{
+ if (name)
+ {
+ char *end;
+ strtoul (name, &end, 0);
+ return end == name ? EINVAL : 0;
+ }
+ else
+ return 0; /* `maximum size' */
+}
+
+static error_t
+zero_map (const struct store *store, vm_prot_t prot, mach_port_t *memobj)
+{
+ *memobj = MACH_PORT_NULL;
+ return 0;
+}
+
+const struct store_class
+store_zero_class =
+{
+ STORAGE_ZERO, "zero", zero_read, zero_write, zero_set_size,
+ zero_allocate_encoding, zero_encode, zero_decode,
+ 0, 0, 0, 0, zero_remap, zero_open, zero_validate_name,
+ zero_map
+};
+STORE_STD_CLASS (zero);
+
+/* Return a new zero store SIZE bytes long in STORE. */
+error_t
+store_zero_create (store_offset_t size, int flags, struct store **store)
+{
+ struct store_run run = { 0, size };
+ return
+ _store_create (&store_zero_class, MACH_PORT_NULL,
+ flags | STORE_INNOCUOUS, 1, &run, 1, 0, store);
+}
diff --git a/libthreads/GNUmakefile.old b/libthreads/GNUmakefile.old
new file mode 100644
index 00000000..cc0f6de8
--- /dev/null
+++ b/libthreads/GNUmakefile.old
@@ -0,0 +1,38 @@
+CPPFLAGS = -nostdinc -I. -I/home/gd3/hurdinst/include
+CFLAGS = -g -O
+CPP=/usr1/gnu/DIST/lib/gcc-lib/i386-compaq-mach/2.4.5/cpp
+AS = as
+AR = ar
+RANLIB = ranlib
+CC = gcc
+
+VPATH=.:i386
+
+OBJS = cprocs.o cthreads.o malloc.o \
+ mig_support.o stack.o sync.o \
+ thread.o lock.o csw.o cthread_data.o
+
+all: libthreads.a
+
+install: all
+ cp libthreads.a /home/gd3/hurdinst/lib/libthreads.a
+ ranlib /home/gd3/hurdinst/lib/libthreads.a
+ cp cthreads.h /home/gd3/hurdinst/include/cthreads.h
+ cp i386/cthreads.h /home/gd3/hurdinst/include/i386/cthreads.h
+
+clean:
+ rm -f lib*.a Makedep* a.out core errs \
+ *.d *.s *.S *.o *.BAK *.CKP */*.BAK */*.CKP
+
+libthreads.a: $(OBJS)
+ rm -f $@
+ $(AR) crv $@ $(OBJS)
+ $(RANLIB) $@
+
+%.o: %.s
+ $(CPP) $(CPPFLAGS) $< > $*.as
+ $(AS) -o $@ $*.as
+ rm -f $*.as
+
+TAGS: *.c *.h
+ etags *.c *.h
diff --git a/libthreads/Makefile b/libthreads/Makefile
new file mode 100644
index 00000000..9c2f9195
--- /dev/null
+++ b/libthreads/Makefile
@@ -0,0 +1,48 @@
+# Copyright (C) 1994,95,96,97,2000,2010,2012 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libthreads
+makemode := library
+
+# In GNU mig_support.c, sync.c and machine/lock.s are omitted; that work is
+# all done in libc.
+#SRCS := call.c cprocs.c cthread_data.c cthreads.c stack.c \
+# cancel-cond.c rwlock.c lockfile.c
+#I386SRCS := i386/csw.S i386/thread.c
+
+# In GNU machine/cthreads.h is omitted; that work is done in libc headers.
+
+#OBJS = $(addsuffix .o,$(basename $(notdir $(SRCS) $(I386SRCS))))
+
+#OTHERTAGS = $(I386SRCS) $(I386HDRS)
+
+libname = libthreads
+installhdrs = cthreads.h rwlock.h
+installhdrsubdir = .
+
+VPATH += $(srcdir)/$(asm_syntax)
+
+include ../Makeconf
+
+# The threads library was written by CMU. If you've ever experienced
+# what that means, you'll understand this line.
+#CFLAGS := $(filter-out -Wall,$(CFLAGS))
+
+#ifeq ($(VERSIONING),yes)
+# Adding this dependency gets it included in the command line,
+# where ld will read it as a linker script.
+#$(libname).so.$(hurd-version): $(srcdir)/$(libname).map
+#endif
diff --git a/libthreads/Makefile.CMU b/libthreads/Makefile.CMU
new file mode 100644
index 00000000..5c6cf544
--- /dev/null
+++ b/libthreads/Makefile.CMU
@@ -0,0 +1,93 @@
+#
+# Mach Operating System
+# Copyright (c) 1991,1990,1989 Carnegie Mellon University
+# All Rights Reserved.
+#
+# Permission to use, copy, modify and distribute this software and its
+# documentation is hereby granted, provided that both the copyright
+# notice and this permission notice appear in all copies of the
+# software, derivative works or modified versions, and any portions
+# thereof, and that both notices appear in supporting documentation.
+#
+# CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+# CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+# ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+#
+# Carnegie Mellon requests users of this software to return to
+#
+# Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+# School of Computer Science
+# Carnegie Mellon University
+# Pittsburgh PA 15213-3890
+#
+# any improvements or extensions that they make and grant Carnegie Mellon
+# the rights to redistribute these changes.
+#
+#
+# HISTORY
+# $Log: Makefile,v $
+# Revision 2.21 92/05/22 18:38:31 jfriedl
+# From Mike Kupfer <kupfer@sprite.Berkeley.EDU>:
+# Enable CTHREAD_DATA for the kernel bootstrap program.
+#
+# Revision 2.20 92/03/05 22:46:34 rpd
+# Changed to use double-colon rules for top-level targets.
+# [92/02/28 rpd]
+#
+# Revision 2.19 92/01/23 15:22:53 rpd
+# Revised for new Makefile organization.
+# [92/01/16 rpd]
+#
+# Revision 1.4 91/09/09 15:55:51 kupfer
+# MK63 merge.
+#
+# Revision 1.2 91/08/23 19:12:32 kupfer
+# Put back the changes for the Posix thread local data support.
+#
+
+# Define CTHREAD_DATA to enable source compatibility with the old
+# "cthread_data" interface.
+DEFS = -DCTHREAD_DATA
+
+include ${MAKETOP}Makefile-common
+
+# find machine-dependent files in machine subdirectory
+
+VPATH = .:${cpu}
+
+all :: libthreads.a
+
+install :: ${INSTALLDIR}/lib/libthreads.a
+
+release :: ${TRELEASEDIR}/lib/libthreads.a
+
+clean ::
+ ${RM} ${INSTALLDIR}/lib/libthreads.a
+
+# installation rules
+
+${INSTALLDIR}/lib/libthreads.a : libthreads.a
+ ${RM} $@
+ ${CP} $? $@
+ ${CHMOD_LIB} $@
+
+# release rules
+
+${TRELEASEDIR}/lib/libthreads.a : ${FRELEASEDIR}/lib/libthreads.a
+ ${RM} $@
+ ${CP} $? $@
+
+# build rules
+
+OBJS = cprocs.o cthreads.o malloc.o \
+ mig_support.o stack.o sync.o \
+ thread.o lock.o csw.o cthread_data.o
+
+libthreads.a : ${OBJS}
+ ${RM} $@
+ ${AR} cq $@ ${OBJS}
+ ${RANLIB} $@
+
+# For lint, do ``lint -I. -un *.c mips/*.c''
+
+-include Makedep
diff --git a/libthreads/Makefile.GNU b/libthreads/Makefile.GNU
new file mode 100644
index 00000000..fd33c9a7
--- /dev/null
+++ b/libthreads/Makefile.GNU
@@ -0,0 +1,33 @@
+CPPFLAGS = -nostdinc -I. -I/usr1/gnu/DIST/include
+CFLAGS = -g -O
+CPP = /usr1/gnu/DIST/lib/gcc-cpp
+AS = as
+AR = ar
+RANLIB = ranlib
+CC = gcc
+
+VPATH=.:i386
+
+OBJS = cprocs.o cthreads.o malloc.o \
+ mig_support.o stack.o sync.o \
+ thread.o lock.o csw.o
+
+all: libthreads.a
+
+install: all
+ cp libthreads.a /usr1/gnu/DIST/lib/libthreads.a
+ ranlib /usr1/gnu/DIST/lib/libthreads.a
+
+clean:
+ rm -f lib*.a Makedep* a.out core errs \
+ *.d *.s *.S *.o *.BAK *.CKP */*.BAK */*.CKP
+
+libthreads.a: $(OBJS)
+ rm -f $@
+ $(AR) crv $@ $(OBJS)
+ $(RANLIB) $@
+
+%.o: %.s
+ $(CPP) $(CPPFLAGS) $< > $*.as
+ $(AS) -o $@ $*.as
+ rm -f $*.as
diff --git a/libthreads/Makefile.GNU2 b/libthreads/Makefile.GNU2
new file mode 100644
index 00000000..bcddc41e
--- /dev/null
+++ b/libthreads/Makefile.GNU2
@@ -0,0 +1,33 @@
+CPPFLAGS = -nostdinc -I. -I/usr1/gnu/DIST/include -I/usr1/gnu/DIST/hurd/include
+CFLAGS = -g -O
+CPP = /usr1/gnu/DIST/lib/gcc-cpp
+AS = as
+AR = ar
+RANLIB = ranlib
+CC = gcc
+
+VPATH=.:i386
+
+OBJS = cprocs.o cthreads.o malloc.o \
+ mig_support.o stack.o sync.o \
+ thread.o lock.o csw.o cthread_data.o
+
+all: libthreads.a
+
+install: all
+ cp libthreads.a /usr1/gnu/DIST/lib/libthreads.a
+ ranlib /usr1/gnu/DIST/lib/libthreads.a
+
+clean:
+ rm -f lib*.a Makedep* a.out core errs \
+ *.d *.s *.S *.o *.BAK *.CKP */*.BAK */*.CKP
+
+libthreads.a: $(OBJS)
+ rm -f $@
+ $(AR) crv $@ $(OBJS)
+ $(RANLIB) $@
+
+%.o: %.s
+ $(CPP) $(CPPFLAGS) $< > $*.as
+ $(AS) -o $@ $*.as
+ rm -f $*.as
diff --git a/libthreads/alpha/csw.S b/libthreads/alpha/csw.S
new file mode 100644
index 00000000..8c6ae309
--- /dev/null
+++ b/libthreads/alpha/csw.S
@@ -0,0 +1,200 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993,1992 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * $Log: csw.s,v $
+ * Revision 2.3 93/01/19 08:55:56 danner
+ * Locks are longs now. Put MBs before and after releasing
+ * locks.
+ * [93/01/15 af]
+ *
+ * Revision 2.2 93/01/14 18:04:23 danner
+ * Fixed bug in cproc_prepare, it was not setting up
+ * the PV register properly. Safer GP usage too.
+ * ANSIfied comments.
+ * [92/12/22 03:00:50 af]
+ *
+ * Created.
+ * [92/05/31 af]
+ *
+ */
+/*
+ * alpha/csw.s
+ *
+ * Context switch and cproc startup for ALPHA COROUTINE implementation.
+ */
+#include <mach/alpha/asm.h>
+
+ .text
+ .align 4
+
+#define CSW_IMASK \
+ IM_S0|IM_S1|IM_S2|IM_S3|IM_S4|IM_S5|IM_S6|IM_RA|IM_GP
+
+#define ARG_SAVE (6*8)
+#define SAVED_S0 (6*8)
+#define SAVED_S1 (7*8)
+#define SAVED_S2 (8*8)
+#define SAVED_S3 (9*8)
+#define SAVED_S4 (10*8)
+#define SAVED_S5 (11*8)
+#define SAVED_S6 (12*8)
+#define SAVED_GP (13*8)
+#define SAVED_PC (14*8)
+#define SAVED_BYTES (15*8)
+
+/*
+ * Suspend the current thread and resume the next one.
+ *
+ * void
+ * cproc_switch(cur, next, lock)
+ * long *cur;
+ * long *next;
+ * simple_lock *lock;
+ */
+LEAF(cproc_switch,3)
+ subq sp,SAVED_BYTES,sp /* allocate space for registers */
+ /* Save them registers */
+ stq ra,SAVED_PC(sp)
+ stq gp,SAVED_GP(sp)
+ stq s0,SAVED_S0(sp)
+ stq s1,SAVED_S1(sp)
+ stq s2,SAVED_S2(sp)
+ stq s3,SAVED_S3(sp)
+ stq s4,SAVED_S4(sp)
+ stq s5,SAVED_S5(sp)
+ stq s6,SAVED_S6(sp)
+
+ stq sp,0(a0) /* save current sp */
+ ldq sp,0(a1) /* restore next sp */
+ /* Reload them registers */
+ ldq ra,SAVED_PC(sp)
+ ldq gp,SAVED_GP(sp)
+ ldq s0,SAVED_S0(sp)
+ ldq s1,SAVED_S1(sp)
+ ldq s2,SAVED_S2(sp)
+ ldq s3,SAVED_S3(sp)
+ ldq s4,SAVED_S4(sp)
+ ldq s5,SAVED_S5(sp)
+ ldq s6,SAVED_S6(sp)
+ /* return to next thread */
+ .set noreorder
+ mb
+ stq zero,0(a2) /* clear lock */
+ mb
+ .set reorder
+ addq sp,SAVED_BYTES,sp
+ RET
+ END(cproc_switch)
+
+/*
+ * void
+ * cproc_start_wait(parent_context, child, stackp, lock)
+ * long *parent_context;
+ * cproc_t child;
+ * long stackp;
+ * simple_lock *lock;
+ */
+NESTED(cproc_start_wait, 4, SAVED_BYTES, zero, CSW_IMASK, 0)
+ ldgp gp,0(pv)
+ subq sp,SAVED_BYTES,sp /* allocate space for registers */
+ /* Save parent registers */
+ stq ra,SAVED_PC(sp)
+ stq gp,SAVED_GP(sp)
+ stq s0,SAVED_S0(sp)
+ stq s1,SAVED_S1(sp)
+ stq s2,SAVED_S2(sp)
+ stq s3,SAVED_S3(sp)
+ stq s4,SAVED_S4(sp)
+ stq s5,SAVED_S5(sp)
+ stq s6,SAVED_S6(sp)
+
+ stq sp,0(a0) /* save parent sp */
+
+ .set noreorder
+ mb
+ stq zero,0(a3) /* release lock */
+ mb
+ .set reorder
+
+ mov a2,sp /* get child sp */
+ subq sp,ARG_SAVE,sp /* regsave (sanity) */
+ mov a1,a0
+ CALL(cproc_waiting) /* cproc_waiting(child) */
+ /*
+ * Control never returns here.
+ */
+ END(cproc_start_wait)
+
+/*
+ * void
+ * cproc_prepare(child, child_context, stack)
+ * long *child_context;
+ * long *stack;
+ */
+LEAF(cproc_prepare,3)
+ ldgp gp,0(pv)
+ subq a2,ARG_SAVE,a2 /* cthread_body's fake frame */
+ stq a0,0(a2) /* cthread_body(child) */
+ subq a2,SAVED_BYTES,a2 /* cproc_switch's ``frame'' */
+ stq s0,SAVED_S0(a2)
+ stq s1,SAVED_S1(a2)
+ stq s2,SAVED_S2(a2)
+ stq s3,SAVED_S3(a2)
+ stq s4,SAVED_S4(a2)
+ stq s5,SAVED_S5(a2)
+ stq s6,SAVED_S6(a2)
+ stq gp,SAVED_GP(a2)
+ stq a2,0(a1) /* child context */
+ lda v0,1f
+ stq v0,SAVED_PC(a2)
+ RET
+
+ /*
+ * The reason we are getting here is to load
+ * arguments in registers where they are supposed
+ * to be. The code above only put the argument(s)
+ * on the stack, now we'll load them.
+ */
+1: ldgp gp,0(ra) /* we get here from a cswitch */
+ lda v0,cthread_body
+ ldq a0,0(sp)
+ mov v0,ra
+ mov ra,pv /* funcall or return, either way */
+ RET
+ END(cproc_prepare)
+
+/*
+ * unsigned long
+ * cthread_sp()
+ *
+ * Returns the current stack pointer.
+ */
+
+LEAF(cthread_sp,0)
+ mov sp, v0
+ RET
+ END(cthread_sp);
diff --git a/libthreads/alpha/cthreads.h b/libthreads/alpha/cthreads.h
new file mode 100644
index 00000000..f2218f40
--- /dev/null
+++ b/libthreads/alpha/cthreads.h
@@ -0,0 +1,61 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993,1992 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * $Log: cthreads.h,v $
+ * Revision 2.4 93/11/17 19:00:42 dbg
+ * When compiling with GCC, inline cthread_sp().
+ * [93/09/21 af]
+ *
+ * Revision 2.3 93/01/19 08:56:02 danner
+ * Locks are now 64bits.
+ * [92/12/30 af]
+ *
+ * Revision 2.2 93/01/14 18:04:28 danner
+ * Created.
+ * [92/05/31 af]
+ *
+ */
+
+#ifndef _MACHINE_CTHREADS_H_
+#define _MACHINE_CTHREADS_H_
+
+typedef long spin_lock_t;
+#define SPIN_LOCK_INITIALIZER 0
+#define spin_lock_init(s) *(s)=0
+#define spin_lock_locked(s) (*(s) != 0)
+
+#if defined(__GNUC__)
+
+#define cthread_sp() \
+ ({ register vm_offset_t _sp__; \
+ __asm__("or $31,$30,%0" \
+ : "=r" (_sp__) ); \
+ _sp__; })
+
+#endif /* __GNUC__ */
+
+#endif _MACHINE_CTHREADS_H_
diff --git a/libthreads/alpha/lock.S b/libthreads/alpha/lock.S
new file mode 100644
index 00000000..e7acb039
--- /dev/null
+++ b/libthreads/alpha/lock.S
@@ -0,0 +1,83 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * $Log: lock.s,v $
+ * Revision 2.3 93/03/09 10:59:04 danner
+ * Lost in previous merge:
+ * Added memory barriers where needed.
+ * Locks are longs now.
+ * [93/01/15 af]
+ *
+ * Revision 2.2 93/01/14 18:04:31 danner
+ * Mumble, "try_lock" really means "try_hard_once".
+ * That is, try hard until you either get it or lose it.
+ * [92/12/24 af]
+ * Created a while back.
+ * [92/12/10 af]
+ *
+ */
+
+#include <mach/alpha/asm.h>
+
+/*
+ The C interface for this function is
+
+ boolean_t
+ spin_try_lock_sw(m)
+ long * m;
+
+ The function has a slightly different semantics than TAS: it will
+ return a boolean value that indicates whether the lock was acquired
+ or not. If not, we'll presume that the user will retry after some
+ appropriate delay.
+ */
+ .text
+ .align 4
+ .set noreorder
+
+LEAF(spin_try_lock,1)
+ mb
+ ldq_l t0,0(a0)
+ or zero,2,v0 /* build lock value */
+ bne t0,nope /* already set, forget it */
+ stq_c v0,0(a0) /* see if we still had the lock */
+ beq v0,yipe /* if we just took an interrupt.. */
+ RET /* if v0 != 0 then we got it */
+nope:
+ mov zero,v0 /* failed to acquire lock */
+ RET
+yipe: br zero,spin_try_lock /* I love branch predictions.. */
+
+ END(spin_try_lock)
+
+LEAF(spin_unlock,1)
+ mb /* but this might be needed.. */
+ stq zero,0(a0) /* no need for interlocks (sec 10.5.2) */
+ mb /* but this might be needed.. */
+ RET
+
+ END(spin_unlock)
diff --git a/libthreads/alpha/thread.c b/libthreads/alpha/thread.c
new file mode 100644
index 00000000..db2cb0c8
--- /dev/null
+++ b/libthreads/alpha/thread.c
@@ -0,0 +1,100 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * $Log: thread.c,v $
+ * Revision 1.1 2002/05/27 02:13:47 roland
+ * 2002-05-26 Roland McGrath <roland@frob.com>
+ *
+ * * alpha/cthreads.h, alpha/thread.c, alpha/csw.S, alpha/lock.S:
+ * New files, verbatim from CMU release MK83a user/threads/alpha.
+ *
+ * Revision 2.3 93/02/01 09:56:49 danner
+ * mach/mach.h -> mach.h
+ * [93/01/28 danner]
+ *
+ * Revision 2.2 93/01/14 18:04:35 danner
+ * Created.
+ * [92/05/31 af]
+ *
+ */
+/*
+ * alpha/thread.c
+ *
+ * Cproc startup for ALPHA Cthreads implementation.
+ */
+
+
+#include <cthreads.h>
+#include "cthread_internals.h"
+
+#include <mach.h>
+
+#if 0
+/*
+ * C library imports:
+ */
+extern bzero();
+#endif
+
+/*
+ * Set up the initial state of a MACH thread
+ * so that it will invoke routine(child)
+ * when it is resumed.
+ */
+#warning TLS support not implemented
+void
+cproc_setup(
+ register cproc_t child,
+ thread_t thread,
+ tcbhead_t *tcb,
+ void (*routine)(cproc_t))
+{
+ register integer_t *top;
+ struct alpha_thread_state state;
+ register struct alpha_thread_state *ts;
+ kern_return_t r;
+
+ /*
+ * Set up ALPHA call frame and registers.
+ */
+ ts = &state;
+ bzero((char *) ts, sizeof(struct alpha_thread_state));
+
+ top = (integer_t *) (child->stack_base + child->stack_size);
+
+ /*
+ * Set pc & pv to procedure entry, pass one arg in register,
+ * allocate room for 6 regsave on the stack frame (sanity).
+ */
+ ts->pc = (natural_t) routine;
+ ts->r27 = (natural_t) routine;
+ ts->r16 = (integer_t) child;
+ ts->r30 = (integer_t) (top - 6); /* see ARG_SAVE in csw.s */
+
+
+ MACH_CALL(thread_set_state(thread,ALPHA_THREAD_STATE,(thread_state_t) &state,ALPHA_THREAD_STATE_COUNT),r);
+}
diff --git a/libthreads/call.c b/libthreads/call.c
new file mode 100644
index 00000000..85bd4f38
--- /dev/null
+++ b/libthreads/call.c
@@ -0,0 +1,79 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992,1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * $Log: call.c,v $
+ * Revision 2.5 93/01/14 18:04:38 danner
+ * Converted file to ANSI C.
+ * [92/12/18 pds]
+ *
+ * Revision 2.4 91/05/14 17:56:00 mrt
+ * Correcting copyright
+ *
+ * Revision 2.3 91/02/14 14:19:20 mrt
+ * Added new Mach copyright
+ * [91/02/13 12:40:44 mrt]
+ *
+ * Revision 2.2 90/01/19 14:36:50 rwd
+ * Created. Routines to replace thread_* and cthread_call_on.
+ * [90/01/03 rwd]
+ *
+ */
+
+#include <cthreads.h>
+#include "cthread_internals.h"
+
+#if defined(THREAD_CALLS)
+kern_return_t cthread_get_state(cthread_t thread)
+{
+ cproc_t p = thread->ur;
+}
+
+kern_return_t cthread_set_state(cthread_t thread)
+{
+ cproc_t p = thread->ur;
+}
+
+kern_return_t cthread_abort(cthread_t thread)
+{
+ cproc_t p = thread->ur;
+}
+
+kern_return_t cthread_resume(cthread_t thread)
+{
+ cproc_t p = thread->ur;
+}
+
+kern_return_t cthread_suspend(cthread_t thread)
+{
+ cproc_t p = thread->ur;
+}
+
+kern_return_t cthread_call_on(cthread_t thread)
+{
+ cproc_t p = thread->ur;
+}
+#endif /* defined(THREAD_CALLS) */
diff --git a/libthreads/cancel-cond.c b/libthreads/cancel-cond.c
new file mode 100644
index 00000000..b7793f85
--- /dev/null
+++ b/libthreads/cancel-cond.c
@@ -0,0 +1,116 @@
+/* Modified condition_wait that checks for cancellation.
+Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <hurd/signal.h>
+#include <cthreads.h>
+#include "cthread_internals.h"
+#include <assert.h>
+
+/* Just like condition_wait, but cancellable. Returns true if cancelled. */
+int
+hurd_condition_wait (condition_t c, mutex_t m)
+{
+ /* This function will be called by hurd_thread_cancel while we are blocked
+ in the condition_wait. We wake up all threads blocked on C,
+ so our thread will wake up and notice the cancellation flag. */
+ void cancel_me (void)
+ {
+ condition_broadcast (c);
+ }
+ struct hurd_sigstate *ss = _hurd_self_sigstate ();
+ cproc_t p = cproc_self ();
+ int cancel;
+
+ assert (ss->intr_port == MACH_PORT_NULL); /* Sanity check for signal bugs. */
+
+ p->state = CPROC_CONDWAIT | CPROC_SWITCHING;
+
+ /* Atomically enqueue our cproc on the condition variable's queue of
+ waiters, and mark our sigstate to indicate that `cancel_me' must be
+ called to wake us up. We must hold the sigstate lock while acquiring
+ the condition variable's lock and tweaking it, so that
+ hurd_thread_cancel can never suspend us and then deadlock in
+ condition_broadcast waiting for the condition variable's lock. */
+
+ spin_lock (&ss->lock);
+ spin_lock (&c->lock);
+ cancel = ss->cancel;
+ if (cancel)
+ /* We were cancelled before doing anything. Don't block at all. */
+ ss->cancel = 0;
+ else
+ {
+ /* Put us on the queue so that condition_broadcast will know to wake
+ us up. */
+ cthread_queue_enq (&c->queue, p);
+ /* Tell hurd_thread_cancel how to unblock us. */
+ ss->cancel_hook = &cancel_me;
+ }
+ spin_unlock (&c->lock);
+ spin_unlock (&ss->lock);
+
+ if (cancel)
+ {
+ /* Cancelled on entry. Just leave the mutex locked. */
+ m = NULL;
+ p->state = CPROC_RUNNING;
+ }
+ else
+ {
+ /* Now unlock the mutex and block until woken. */
+
+#ifdef WAIT_DEBUG
+ p->waiting_for = (char *)c;
+#endif /* WAIT_DEBUG */
+
+ mutex_unlock (m);
+
+ spin_lock (&p->lock);
+ if (p->state & CPROC_SWITCHING)
+ cproc_block ();
+ else
+ {
+ /* We were woken up someplace before reacquiring P->lock.
+ We can just continue on. */
+ p->state = CPROC_RUNNING;
+ spin_unlock(&p->lock);
+ }
+
+#ifdef WAIT_DEBUG
+ p->waiting_for = (char *)0;
+#endif /* WAIT_DEBUG */
+ }
+
+ spin_lock (&ss->lock);
+ /* Clear the hook, now that we are done blocking. */
+ ss->cancel_hook = NULL;
+ /* Check the cancellation flag; we might have unblocked due to
+ cancellation rather than a normal condition_signal or
+ condition_broadcast (or we might have just happened to get cancelled
+ right after waking up). */
+ cancel |= ss->cancel;
+ ss->cancel = 0;
+ spin_unlock (&ss->lock);
+
+ if (m)
+ /* Reacquire the mutex and return. */
+ mutex_lock (m);
+
+ return cancel;
+}
diff --git a/libthreads/cprocs.c b/libthreads/cprocs.c
new file mode 100644
index 00000000..5459d7de
--- /dev/null
+++ b/libthreads/cprocs.c
@@ -0,0 +1,1214 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * 26-Oct-94 Johannes Helander (jvh) Helsinki University of Technology
+ * Set the wait_type field.
+ *
+ * $Log: cprocs.c,v $
+ * Revision 1.16 2002/05/27 02:50:10 roland
+ * 2002-05-26 Roland McGrath <roland@frob.com>
+ *
+ * Changes merged from CMU MK83a version:
+ * * cthreads.h, options.h: Various cleanups.
+ * * call.c, cthread_data.c, sync.c, mig_support.c: Likewise.
+ * * i386/cthreads.h, i386/thread.c, i386/lock.s: Likewise.
+ * * cthread_internals.h: Add decls for internal functions.
+ * (struct cproc): Use vm_offset_t for stack_base and stack_size members.
+ * Use natural_t for context member.
+ * * cprocs.c: Use prototypes for all defns.
+ * * cthreads.c: Likewise.
+ * (cthread_exit): Cast any_t to integer_t before int.
+ *
+ * Revision 2.18 93/03/09 10:59:10 danner
+ * Lint.
+ * [93/03/06 af]
+ *
+ * Revision 2.17 93/01/19 08:55:44 danner
+ * Added missing spin_lock_t type from cproc_list_lock decl.
+ * [92/12/30 af]
+ *
+ *
+ * Revision 2.16 93/01/14 18:04:46 danner
+ * Convert file to ANSI C.
+ * [92/12/18 pds]
+ * 64bit cleanup.
+ * [92/12/10 21:08:32 af]
+ *
+ * Revision 2.15 92/03/06 14:09:31 rpd
+ * Replaced swtch_pri with yield.
+ * [92/03/06 rpd]
+ *
+ * Revision 2.14 91/08/28 11:19:16 jsb
+ * Fixed the loop in cproc_fork_child that frees cprocs.
+ * [91/08/23 rpd]
+ *
+ * Revision 2.13 91/07/31 18:33:04 dbg
+ * Fix some more bad types. Ints are NOT pointers.
+ *
+ * Fix argument type mismatch in cproc_create.
+ * [91/07/30 17:32:59 dbg]
+ *
+ * Revision 2.12 91/05/14 17:56:11 mrt
+ * Correcting copyright
+ *
+ * Revision 2.11 91/02/14 14:19:26 mrt
+ * Added new Mach copyright
+ * [91/02/13 12:40:50 mrt]
+ *
+ * Revision 2.10 90/11/05 14:36:41 rpd
+ * Added cproc_fork_{prepare,parent,child}.
+ * [90/11/02 rwd]
+ *
+ * Fix for positive stack growth.
+ * [90/11/01 rwd]
+ *
+ * Add spin_lock_t.
+ * [90/10/31 rwd]
+ *
+ * Revision 2.9 90/10/12 13:07:12 rpd
+ * Fix type
+ * [90/10/10 15:09:59 rwd]
+ *
+ * Comment code.
+ * [90/10/02 rwd]
+ *
+ * Revision 2.8 90/09/09 14:34:44 rpd
+ * Remove special mutex. Remove thread_calls and debug_mutex
+ * [90/08/24 rwd]
+ * Fix up old call to cthread_msg_busy to new format.
+ * [90/08/22 rwd]
+ *
+ * Revision 2.7 90/08/06 15:09:17 rwd
+ * Fixed arguments to cthread_mach_msg.
+ * [90/06/26 rwd]
+ * Add additional STATISTICS.
+ * [90/06/07 rwd]
+ *
+ * Attempt to reduce number of times a cthread is released to to a
+ * msg_receive by adding min/max instead of single number to
+ * cthread_msg calls.
+ * [90/06/06 rwd]
+ *
+ * Revision 2.6 90/06/02 15:13:36 rpd
+ * Converted to new IPC.
+ * [90/03/20 20:46:16 rpd]
+ *
+ * Revision 2.5 90/05/29 18:40:11 rwd
+ * Don't incr special field until the mutex grab is successful.
+ * [90/05/09 rwd]
+ *
+ * Revision 2.4 90/03/14 21:12:02 rwd
+ * Added WAIT_DEBUG code for deadlock debugging.
+ * [90/03/01 rwd]
+ * Insert cprocs in cproc_list as allocated.
+ * [90/03/01 10:20:16 rwd]
+ *
+ * Revision 2.3 90/01/19 14:36:57 rwd
+ * Make cthread_msg_busy only release new thread if this is still
+ * busy. Ie don't release two on back to back calls.
+ * [90/01/11 rwd]
+ * Add THREAD_CALL code. Add CPROC_ARUN state.
+ * [90/01/03 rwd]
+ * Add new cthread_msg_rpc call
+ * [89/12/20 rwd]
+ * Change cproc_self pointer to top of stack. Now need to change
+ * the stack of the first thread.
+ * [89/12/12 rwd]
+ *
+ * Revision 2.2 89/12/08 19:53:13 rwd
+ * Added CPROC_CONDWAIT state to deal with lock held
+ * across mutex_unlock problem.
+ * [89/11/29 rwd]
+ * Changed mutexes to not hand off. MUTEX_EXTRA conditional is
+ * now obsolete.
+ * [89/11/27 rwd]
+ *
+ * Add MUTEX_EXTRA code for extra kernel threads to serve special
+ * mutexes in time of need.
+ * [89/11/25 rwd]
+ * Add MUTEX_SPECIAL and DEBUG_MUTEX code
+ * [89/11/24 rwd]
+ * Changed mutex_lock to mutex_lock_solid. Mutex_lock is now a
+ * macro which tries the spin_lock before making a subroutine call.
+ * Mutex_unlock is now a macro with mutex_unlock_solid for worst case.
+ * [89/11/13 rwd]
+ *
+ * Rewrite most to merge coroutine and thread implementation.
+ * New routines are cthread_set_kernel_limit, cthread_kernel_limit,
+ * cthread_wire, cthread_unwire, and cthread_receive.
+ * [89/10/23 rwd]
+ *
+ * Revision 2.1 89/08/03 17:07:10 rwd
+ * Created.
+ *
+ * 11-Apr-89 David Golub (dbg) at Carnegie-Mellon University
+ * Made condition_yield loop break if swtch_pri returns TRUE (in
+ * case we fix it).
+ *
+ * 31-Mar-89 David Golub (dbg) at Carnegie-Mellon University
+ * Change cond_signal, cond_broadcast, and cproc_continue so that
+ * the condition's spin lock is not held while continuing the
+ * process.
+ *
+ * 16-Jan-89 David Golub (dbg) at Carnegie-Mellon University
+ * Changes for stand-alone library to run on pure kernel:
+ * . made IPC_WAIT standard, as calls that are used if IPC_WAIT == 0
+ * vanished a year ago.
+ * . Removed (as much as possible) references to stdio or other U*X
+ * features.
+ *
+ *
+ * 01-Apr-88 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed condition_clear(c) to acquire c->lock,
+ * to serialize after any threads still doing condition_signal(c).
+ * Suggested by Dan Julin.
+ *
+ * 19-Feb-88 Eric Cooper (ecc) at Carnegie Mellon University
+ * Extended the inline scripts to handle spin_unlock() and mutex_unlock().
+ *
+ * 28-Jan-88 David Golub (dbg) at Carnegie Mellon University
+ * Removed thread_data argument from thread_create
+ * and converted to new thread_set_state call.
+ *
+ * 01-Dec-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Added inline expansion for cthread_sp() function.
+ *
+ * 21-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Fixed uninitialized reply_port in cproc_alloc() (found by rds).
+ *
+ * 14-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Tried using return value of swtch() to guide condition_wait().
+ * Performance was worse than using a hybrid spin/yield/block
+ * scheme, so the version using swtch() was commented out.
+ * Disabled IPC_WAIT in released version.
+ *
+ * 13-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Added IPC_WAIT option.
+ * If defined, thread synchronization (condition_wait() and
+ * cproc_continue()) are implemented using msg_receive() and
+ * msg_send() instead of thread_suspend() and thread_resume().
+ *
+ * 11-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Moved thread reply port to cproc structure in cthread_internals.h,
+ * because mig calls are made while cproc is idle (no cthread structure).
+ * Changed cproc_switch() and cproc_start (COROUTINE implementation)
+ * to use address of saved context, rather than address of enclosing cproc,
+ * to eliminate dependency on cproc layout.
+ */
+/*
+ * File: cprocs.c
+ * Author: Eric Cooper, Carnegie Mellon University
+ * Date: Aug, 1987
+ *
+ * Implementation of cprocs (lightweight processes)
+ * and primitive synchronization operations.
+ */
+
+
+#include <cthreads.h>
+#include "cthread_internals.h"
+#include <mach/message.h>
+#include <hurd/threadvar.h> /* GNU */
+#include <assert.h>
+
+/*
+ * Port_entry's are used by cthread_mach_msg to store information
+ * about each port/port_set for which it is managing threads
+ */
+
+typedef struct port_entry {
+ struct port_entry *next; /* next port_entry */
+ mach_port_t port; /* which port/port_set */
+ struct cthread_queue queue; /* queue of runnable threads for
+ this port/port_set */
+ int min; /* minimum number of kernel threads
+ to be used by this port/port_set */
+ int max; /* maximum number of kernel threads
+ to be used by this port/port_set */
+ int held; /* actual number of kernel threads
+ currentlt in use */
+ spin_lock_t lock; /* lock governing all above fields */
+} *port_entry_t;
+
+#define PORT_ENTRY_NULL ((port_entry_t) 0)
+
+/* Available to outside for statistics */
+
+int cthread_wait_stack_size = 8192; /* stack size for idle threads */
+int cthread_max_kernel_threads = 0; /* max kernel threads */
+int cthread_kernel_threads = 0; /* current kernel threads */
+private spin_lock_t n_kern_lock = SPIN_LOCK_INITIALIZER;
+ /* lock for 2 above */
+#ifdef STATISTICS
+int cthread_ready = 0; /* currently runnable */
+int cthread_running = 1; /* currently running */
+int cthread_waiting = 0; /* currently waiting */
+int cthread_wired = 0; /* currently wired */
+private spin_lock_t wired_lock = SPIN_LOCK_INITIALIZER;
+ /* lock for above */
+int cthread_wait_stacks = 0; /* total cthread waiting stacks */
+int cthread_waiters = 0; /* total of watiers */
+int cthread_wakeup = 0; /* total times woken when starting to
+ block */
+int cthread_blocked = 0; /* total blocked */
+int cthread_rnone = 0; /* total times no cthread available
+ to meet minimum for port_entry */
+int cthread_yields = 0; /* total cthread_yields */
+int cthread_none = 0; /* total idle wakeups w/o runnable */
+int cthread_switches = 0; /* total number of cproc_switches */
+int cthread_no_mutex = 0; /* total number times woken to get
+ mutex and couldn't */
+private spin_lock_t mutex_count_lock = SPIN_LOCK_INITIALIZER;
+ /* lock for above */
+#endif /* STATISTICS */
+
+cproc_t cproc_list = NO_CPROC; /* list of all cprocs */
+private spin_lock_t cproc_list_lock = SPIN_LOCK_INITIALIZER;
+ /* lock for above */
+private int cprocs_started = FALSE; /* initialized? */
+private struct cthread_queue ready = QUEUE_INITIALIZER;
+ /* ready queue */
+private int ready_count = 0; /* number of ready threads on ready
+ queue - number of messages sent */
+private spin_lock_t ready_lock = SPIN_LOCK_INITIALIZER;
+ /* lock for 2 above */
+private mach_port_t wait_port = MACH_PORT_NULL;
+ /* port on which idle threads wait */
+private int wait_count = 0; /* number of waiters - messages pending
+ to wake them */
+private struct cthread_queue waiters = QUEUE_INITIALIZER;
+ /* queue of cthreads to run as idle */
+private spin_lock_t waiters_lock = SPIN_LOCK_INITIALIZER;
+ /* lock for 2 above */
+private port_entry_t port_list = PORT_ENTRY_NULL;
+ /* master list of port_entries */
+private spin_lock_t port_lock = SPIN_LOCK_INITIALIZER;
+ /* lock for above queue */
+private mach_msg_header_t wakeup_msg; /* prebuilt message used by idle
+ threads */
+
+/*
+ * Return current value for max kernel threads
+ * Note: 0 means no limit
+ */
+int
+cthread_kernel_limit(void)
+{
+ return cthread_max_kernel_threads;
+}
+
+/*
+ * Set max number of kernel threads
+ * Note: This will not currently terminate existing threads
+ * over maximum.
+ */
+
+void
+cthread_set_kernel_limit(int n)
+{
+ cthread_max_kernel_threads = n;
+}
+
+/*
+ * Wire a cthread to its current kernel thread
+ */
+
+void
+cthread_wire(void)
+{
+ register cproc_t p = cproc_self();
+
+ /* In GNU, we wire all threads on creation (in cproc_alloc). */
+ assert (p->wired != MACH_PORT_NULL);
+}
+
+/*
+ * Unwire a cthread. Deallocate its wait port.
+ */
+
+void
+cthread_unwire(void)
+{
+ register cproc_t p = cproc_self();
+
+ /* This is bad juju in GNU, where all cthreads must be wired. */
+ abort();
+#if 0
+ if (p->wired != MACH_PORT_NULL) {
+ MACH_CALL(mach_port_mod_refs(mach_task_self(), p->wired,
+ MACH_PORT_RIGHT_SEND, -1), r);
+ MACH_CALL(mach_port_mod_refs(mach_task_self(), p->wired,
+ MACH_PORT_RIGHT_RECEIVE, -1), r);
+ p->wired = MACH_PORT_NULL;
+#ifdef STATISTICS
+ spin_lock(&wired_lock);
+ cthread_wired--;
+ spin_unlock(&wired_lock);
+#endif /* STATISTICS */
+ }
+#endif
+}
+
+private cproc_t
+cproc_alloc(void)
+{
+ register cproc_t p = (cproc_t) malloc(sizeof(struct cproc));
+ kern_return_t r;
+
+ p->incarnation = NO_CTHREAD;
+#if 0
+ /* This member is not used in GNU. */
+ p->reply_port = MACH_PORT_NULL;
+#endif
+
+ spin_lock_init(&p->lock);
+ p->state = CPROC_RUNNING;
+ p->busy = 0;
+
+ /*
+ * In GNU, every cthread must be wired. So we just
+ * initialize P->wired on creation.
+ *
+ * A wired thread has a port associated with it for all
+ * of its wait/block cases. We also prebuild a wakeup
+ * message.
+ */
+
+ MACH_CALL(mach_port_allocate(mach_task_self(),
+ MACH_PORT_RIGHT_RECEIVE,
+ &p->wired), r);
+ MACH_CALL(mach_port_insert_right(mach_task_self(),
+ p->wired, p->wired,
+ MACH_MSG_TYPE_MAKE_SEND), r);
+ p->msg.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+ p->msg.msgh_size = 0; /* initialized in call */
+ p->msg.msgh_remote_port = p->wired;
+ p->msg.msgh_local_port = MACH_PORT_NULL;
+ p->msg.msgh_kind = MACH_MSGH_KIND_NORMAL;
+ p->msg.msgh_id = 0;
+
+ spin_lock(&cproc_list_lock);
+ p->list = cproc_list;
+ cproc_list = p;
+ spin_unlock(&cproc_list_lock);
+
+ return p;
+}
+
+/*
+ * Called by cthread_init to set up initial data structures.
+ */
+
+vm_offset_t
+cproc_init(void)
+{
+ kern_return_t r;
+
+ cproc_t p = cproc_alloc();
+
+ cthread_kernel_threads = 1;
+
+ MACH_CALL(mach_port_allocate(mach_task_self(),
+ MACH_PORT_RIGHT_RECEIVE,
+ &wait_port), r);
+ MACH_CALL(mach_port_insert_right(mach_task_self(),
+ wait_port, wait_port,
+ MACH_MSG_TYPE_MAKE_SEND), r);
+
+ wakeup_msg.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+ wakeup_msg.msgh_size = 0; /* initialized in call */
+ wakeup_msg.msgh_remote_port = wait_port;
+ wakeup_msg.msgh_local_port = MACH_PORT_NULL;
+ wakeup_msg.msgh_kind = MACH_MSGH_KIND_NORMAL;
+ wakeup_msg.msgh_id = 0;
+
+ cprocs_started = TRUE;
+
+
+ /*
+ * We pass back the new stack which should be switched to
+ * by crt0. This guarantess correct size and alignment.
+ */
+ return (stack_init(p));
+}
+
+/*
+ * Insert cproc on ready queue. Make sure it is ready for queue by
+ * synching on its lock. Just send message to wired cproc.
+ */
+
+private boolean_t cproc_ready(register cproc_t p, register int preq)
+{
+ register cproc_t s=cproc_self();
+ kern_return_t r;
+
+ if (p->wired != MACH_PORT_NULL) {
+ r = mach_msg(&p->msg, MACH_SEND_MSG,
+ sizeof p->msg, 0, MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+#ifdef CHECK_STATUS
+ if (r != MACH_MSG_SUCCESS) {
+ mach_error("mach_msg", r);
+ exit(1);
+ }
+#endif /* CHECK_STATUS */
+ return TRUE;
+ }
+ spin_lock(&p->lock); /* is it ready to be queued? It
+ can appear on a queue before
+ being switched from. This lock
+ is released by cproc_switch as
+ its last operation. */
+ if (p->state & CPROC_SWITCHING) {
+ /*
+ * We caught it early on. Just set to RUNNING
+ * and we will save a lot of time.
+ */
+ p->state = (p->state & ~CPROC_SWITCHING) | CPROC_RUNNING;
+ spin_unlock(&p->lock);
+ return TRUE;
+ }
+ spin_unlock(&p->lock);
+
+ spin_lock(&ready_lock);
+
+ if (preq) {
+ cthread_queue_preq(&ready, p);
+ } else {
+ cthread_queue_enq(&ready, p);
+ }
+#ifdef STATISTICS
+ cthread_ready++;
+#endif /* STATISTICS */
+ ready_count++;
+
+ if ((s->state & CPROC_CONDWAIT) && !(s->wired)) {
+ /*
+ * This is an optimiztion. Don't bother waking anyone to grab
+ * this guy off the ready queue since my thread will block
+ * momentarily for the condition wait.
+ */
+
+ spin_unlock(&ready_lock);
+ return TRUE;
+ }
+
+ if ((ready_count > 0) && wait_count) {
+ wait_count--;
+ ready_count--;
+ spin_unlock(&ready_lock);
+ r = mach_msg(&wakeup_msg, MACH_SEND_MSG,
+ sizeof wakeup_msg, 0, MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+#ifdef CHECK_STATUS
+ if (r != MACH_MSG_SUCCESS) {
+ mach_error("mach_msg", r);
+ exit(1);
+ }
+#endif /* CHECK_STATUS */
+ return TRUE;
+ }
+ spin_unlock(&ready_lock);
+ return FALSE;
+}
+
+/*
+ * This is only run on a partial "waiting" stack and called from
+ * cproc_start_wait
+ */
+
+void
+cproc_waiting(cproc_t p)
+{
+ mach_msg_header_t msg;
+ register cproc_t new;
+ kern_return_t r;
+
+#ifdef STATISTICS
+ spin_lock(&ready_lock);
+ cthread_waiting++;
+ cthread_waiters++;
+ spin_unlock(&ready_lock);
+#endif /* STATISTICS */
+ for (;;) {
+ MACH_CALL(mach_msg(&msg, MACH_RCV_MSG,
+ 0, sizeof msg, wait_port,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL), r);
+ spin_lock(&ready_lock);
+ cthread_queue_deq(&ready, cproc_t, new);
+ if (new != NO_CPROC) break;
+ wait_count++;
+ ready_count++;
+#ifdef STATISTICS
+ cthread_none++;
+#endif /* STATISTICS */
+ spin_unlock(&ready_lock);
+ }
+#ifdef STATISTICS
+ cthread_ready--;
+ cthread_running++;
+ cthread_waiting--;
+#endif /* STATISTICS */
+ spin_unlock(&ready_lock);
+ spin_lock(&new->lock);
+ new->state = CPROC_RUNNING;
+ spin_unlock(&new->lock);
+ spin_lock(&waiters_lock);
+ cthread_queue_enq(&waiters, p);
+ spin_lock(&p->lock);
+ spin_unlock(&waiters_lock);
+ cproc_switch(&p->context,&new->context,&p->lock);
+}
+
+/*
+ * Get a waiter with stack
+ *
+ */
+
+private cproc_t
+cproc_waiter(void)
+{
+ register cproc_t waiter;
+
+ spin_lock(&waiters_lock);
+ cthread_queue_deq(&waiters, cproc_t, waiter);
+ spin_unlock(&waiters_lock);
+ if (waiter == NO_CPROC) {
+ vm_address_t base;
+ kern_return_t r;
+#ifdef STATISTICS
+ spin_lock(&waiters_lock);
+ cthread_wait_stacks++;
+ spin_unlock(&waiters_lock);
+#endif /* STATISTICS */
+ waiter = cproc_alloc();
+ MACH_CALL(vm_allocate(mach_task_self(), &base,
+ cthread_wait_stack_size, TRUE), r);
+ waiter->stack_base = base;
+ waiter->stack_size = cthread_wait_stack_size;
+ }
+ return (waiter);
+}
+
+
+/*
+ * Current cproc is blocked so switch to any ready cprocs, or, if
+ * none, go into the wait state.
+ *
+ * You must hold cproc_self()->lock when called.
+ */
+
+void
+cproc_block(void)
+{
+ extern unsigned int __hurd_threadvar_max; /* GNU */
+ register cproc_t waiter, new, p = cproc_self();
+
+ if (p->wired != MACH_PORT_NULL) {
+ mach_msg_header_t msg;
+ kern_return_t r;
+
+ spin_unlock(&p->lock);
+ MACH_CALL(mach_msg(&msg, MACH_RCV_MSG,
+ 0, sizeof msg, p->wired,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL), r);
+ return;
+ }
+ p->state = CPROC_SWITCHING;
+ spin_unlock(&p->lock);
+ spin_lock(&ready_lock);
+#ifdef STATISTICS
+ cthread_blocked++;
+#endif /* STATISTICS */
+ cthread_queue_deq(&ready, cproc_t, new);
+ if (new) {
+#ifdef STATISTICS
+ cthread_ready--;
+ cthread_switches++;
+#endif /* STATISTICS */
+ ready_count--;
+ spin_unlock(&ready_lock);
+ spin_lock(&p->lock);
+ if (p->state == CPROC_RUNNING) { /* have we been saved */
+ spin_unlock(&p->lock);
+#ifdef STATISTICS
+ spin_lock(&ready_lock);
+ cthread_wakeup++;
+ cthread_switches--;
+ spin_unlock(&ready_lock);
+#endif /* STATISTICS */
+ cproc_ready(new, 1); /* requeue at head were it was */
+ } else {
+ p->state = CPROC_BLOCKED;
+ spin_lock(&new->lock); /* incase still switching */
+ new->state = CPROC_RUNNING;
+ spin_unlock(&new->lock);
+ cproc_switch(&p->context,&new->context,&p->lock);
+ }
+ } else {
+ wait_count++;
+#ifdef STATISTICS
+ cthread_running--;
+#endif /* STATISTICS */
+ spin_unlock(&ready_lock);
+ waiter = cproc_waiter();
+ spin_lock(&p->lock);
+ if (p->state == CPROC_RUNNING) { /* we have been saved */
+ spin_unlock(&p->lock);
+ spin_lock(&ready_lock);
+ wait_count--;
+#ifdef STATISTICS
+ cthread_running++;
+ cthread_wakeup++;
+#endif /* STATISTICS */
+ spin_unlock(&ready_lock);
+ spin_lock(&waiters_lock);
+ cthread_queue_preq(&waiters, waiter);
+ spin_unlock(&waiters_lock);
+ } else {
+ p->state = CPROC_BLOCKED;
+ spin_lock(&waiter->lock); /* in case still switching */
+ spin_unlock(&waiter->lock);
+ cproc_start_wait
+ (&p->context, waiter,
+ cproc_stack_base(waiter,
+ sizeof(ur_cthread_t *) +
+ /* Account for GNU per-thread
+ variables. */
+ __hurd_threadvar_max *
+ sizeof (long int)),
+ &p->lock);
+ }
+ }
+}
+
+/*
+ * Implement C threads using MACH threads.
+ */
+cproc_t
+cproc_create(void)
+{
+ register cproc_t child = cproc_alloc();
+ register kern_return_t r;
+ extern void cproc_setup();
+ extern void cproc_prepare();
+ extern void cthread_body();
+ thread_t n;
+
+ alloc_stack(child);
+ spin_lock(&n_kern_lock);
+ if (cthread_max_kernel_threads == 0 ||
+ cthread_kernel_threads < cthread_max_kernel_threads) {
+ tcbhead_t *tcb = _dl_allocate_tls(NULL);
+ cthread_kernel_threads++;
+ spin_unlock(&n_kern_lock);
+ MACH_CALL(thread_create(mach_task_self(), &n), r);
+ cproc_setup(child, n, tcb, cthread_body); /* machine dependent */
+ MACH_CALL(thread_resume(n), r);
+#ifdef STATISTICS
+ spin_lock(&ready_lock);
+ cthread_running++;
+ spin_unlock(&ready_lock);
+#endif /* STATISTICS */
+ } else {
+ vm_offset_t stack;
+ spin_unlock(&n_kern_lock);
+ child->state = CPROC_BLOCKED;
+ /* The original CMU code does the excessively clever
+ optimization of putting CHILD at the base of the stack
+ and setting up to be the argument to cthread_body in the
+ same place (by passing zero as the second arg to
+ cproc_stack_base here).. This doesn't fly for GNU,
+ because we need some more space allocated at the base of
+ the stack, after the cproc_self pointer (where CHILD is
+ stored). */
+ stack = cproc_stack_base(child,
+ sizeof(ur_cthread_t *) +
+ /* Account for GNU per-thread
+ variables. */
+ __hurd_threadvar_max *
+ sizeof (long int));
+ cproc_prepare(child, &child->context, stack, &cthread_body);
+ /* Set up the cproc_self ptr at the base of CHILD's stack. */
+ ur_cthread_ptr(stack) = (ur_cthread_t) child;
+ cproc_ready(child,0);
+ }
+ return child;
+}
+
+void
+condition_wait(condition_t c, mutex_t m)
+{
+ register cproc_t p = cproc_self();
+
+ p->state = CPROC_CONDWAIT | CPROC_SWITCHING;
+
+ spin_lock(&c->lock);
+ cthread_queue_enq(&c->queue, p);
+ spin_unlock(&c->lock);
+#ifdef WAIT_DEBUG
+ p->waiting_for = (char *)c;
+#endif /* WAIT_DEBUG */
+
+ mutex_unlock(m);
+
+ spin_lock(&p->lock);
+ if (p->state & CPROC_SWITCHING) {
+ cproc_block();
+ } else {
+ p->state = CPROC_RUNNING;
+ spin_unlock(&p->lock);
+ }
+
+
+#ifdef WAIT_DEBUG
+ p->waiting_for = (char *)0;
+#endif /* WAIT_DEBUG */
+
+ /*
+ * Re-acquire the mutex and return.
+ */
+ mutex_lock(m);
+}
+
+/* Declare that IMPLICATOR should consider IMPLICATAND's waiter queue
+ to be an extension of its own queue. It is an error for either
+ condition to be deallocated as long as the implication persists. */
+void
+condition_implies (condition_t implicator, condition_t implicatand)
+{
+ struct cond_imp *imp;
+
+ imp = malloc (sizeof (struct cond_imp));
+ imp->implicatand = implicatand;
+ imp->next = implicator->implications;
+ implicator->implications = imp;
+}
+
+/* Declare that the implication relationship from IMPLICATOR to
+ IMPLICATAND should cease. */
+void
+condition_unimplies (condition_t implicator, condition_t implicatand)
+{
+ struct cond_imp **impp;
+
+ for (impp = &implicator->implications; *impp; impp = &(*impp)->next)
+ {
+ if ((*impp)->implicatand == implicatand)
+ {
+ struct cond_imp *tmp = *impp;
+ *impp = (*impp)->next;
+ free (tmp);
+ return;
+ }
+ }
+}
+
+/* Signal one waiter on C. If there were no waiters at all, return
+ 0, else return 1. */
+int
+cond_signal(condition_t c)
+{
+ register cproc_t p;
+ struct cond_imp *imp;
+
+ spin_lock(&c->lock);
+ cthread_queue_deq(&c->queue, cproc_t, p);
+ spin_unlock(&c->lock);
+ if (p != NO_CPROC) {
+ cproc_ready(p,0);
+ return 1;
+ }
+ else {
+ for (imp = c->implications; imp; imp = imp->next)
+ if (cond_signal (imp->implicatand))
+ return 1;
+ }
+ return 0;
+}
+
+void
+cond_broadcast(condition_t c)
+{
+ register cproc_t p;
+ struct cthread_queue blocked_queue;
+ struct cond_imp *imp;
+
+ cthread_queue_init(&blocked_queue);
+
+ spin_lock(&c->lock);
+ for (;;) {
+ cthread_queue_deq(&c->queue, cproc_t, p);
+ if (p == NO_CPROC)
+ break;
+ cthread_queue_enq(&blocked_queue, p);
+ }
+ spin_unlock(&c->lock);
+
+ for(;;) {
+ cthread_queue_deq(&blocked_queue, cproc_t, p);
+ if (p == NO_CPROC)
+ break;
+ cproc_ready(p,0);
+ }
+
+ for (imp = c->implications; imp; imp = imp->next)
+ condition_broadcast (imp->implicatand);
+}
+
+void
+cthread_yield(void)
+{
+ register cproc_t new, p = cproc_self();
+
+ if (p->wired != MACH_PORT_NULL) {
+ yield();
+ return;
+ }
+ spin_lock(&ready_lock);
+#ifdef STATISTICS
+ cthread_yields++;
+#endif /* STATISTICS */
+ cthread_queue_deq(&ready, cproc_t, new);
+ if (new) {
+ cthread_queue_enq(&ready, p);
+ spin_lock(&p->lock);
+ p->state = CPROC_BLOCKED;
+ spin_unlock(&ready_lock);
+ spin_lock(&new->lock);
+ new->state = CPROC_RUNNING;
+ spin_unlock(&new->lock);
+ cproc_switch(&p->context,&new->context,&p->lock);
+ } else {
+ spin_unlock(&ready_lock);
+ yield();
+ }
+}
+
+/*
+ * Mutex objects.
+ */
+
+void
+__mutex_lock_solid(void *ptr)
+{
+ register mutex_t m = ptr;
+ register cproc_t p = cproc_self();
+ register int queued;
+ register int tried = 0;
+
+#ifdef WAIT_DEBUG
+ p->waiting_for = (char *)m;
+#endif /* WAIT_DEBUG */
+ while (1) {
+ spin_lock(&m->lock);
+ if (cthread_queue_head(&m->queue, cproc_t) == NO_CPROC) {
+ cthread_queue_enq(&m->queue, p);
+ queued = 1;
+ } else {
+ queued = 0;
+ }
+ if (spin_try_lock(&m->held)) {
+ if (queued) cthread_queue_deq(&m->queue, cproc_t, p);
+ spin_unlock(&m->lock);
+#ifdef WAIT_DEBUG
+ p->waiting_for = (char *)0;
+#endif /* WAIT_DEBUG */
+ return;
+ } else {
+ if (!queued) cthread_queue_enq(&m->queue, p);
+ spin_lock(&p->lock);
+ spin_unlock(&m->lock);
+ cproc_block();
+ if (spin_try_lock(&m->held)) {
+#ifdef WAIT_DEBUG
+ p->waiting_for = (char *)0;
+#endif /* WAIT_DEBUG */
+ return;
+ }
+#ifdef STATISTICS
+ spin_lock(&mutex_count_lock);
+ cthread_no_mutex++;
+ spin_unlock(&mutex_count_lock);
+#endif /* STATISTICS */
+ }
+ }
+}
+
+void
+__mutex_unlock_solid(void *ptr)
+{
+ register mutex_t m = ptr;
+ register cproc_t new;
+
+ if (!spin_try_lock(&m->held))
+ return;
+ spin_lock(&m->lock);
+ cthread_queue_deq(&m->queue, cproc_t, new);
+ spin_unlock(&m->held);
+ spin_unlock(&m->lock);
+ if (new) {
+ cproc_ready(new,0);
+ }
+}
+
+
+/*
+ * Use instead of mach_msg in a multi-threaded server so as not
+ * to tie up excessive kernel threads. This uses a simple linked list for
+ * ports since this should never be more than a few.
+ */
+
+/*
+ * A cthread holds a reference to a port_entry even after it receives a
+ * message. This reference is not released until the thread does a
+ * cthread_msg_busy. This allows the fast case of a single mach_msg
+ * call to occur as often as is possible.
+ */
+
+private port_entry_t
+get_port_entry(mach_port_t port, int min, int max)
+{
+ register port_entry_t i;
+
+ spin_lock(&port_lock);
+ for(i=port_list;i!=PORT_ENTRY_NULL;i=i->next)
+ if (i->port == port) {
+ spin_unlock(&port_lock);
+ return i;
+ }
+ i = (port_entry_t)malloc(sizeof(struct port_entry));
+ cthread_queue_init(&i->queue);
+ i->port = port;
+ i->next = port_list;
+ port_list = i;
+ i->min = min;
+ i->max = max;
+ i->held = 0;
+ spin_lock_init(&i->lock);
+ spin_unlock(&port_lock);
+ return i;
+}
+
+void
+cthread_msg_busy(mach_port_t port, int min, int max)
+{
+ register port_entry_t port_entry;
+ register cproc_t new, p = cproc_self();
+
+ if (p->busy) {
+ port_entry = get_port_entry(port, min, max);
+ spin_lock(&port_entry->lock);
+ p->busy = 0;
+ if (port_entry->held <= port_entry->min) {
+ cthread_queue_deq(&port_entry->queue, cproc_t, new);
+ if (new != NO_CPROC){
+ spin_unlock(&port_entry->lock);
+ cproc_ready(new,0);
+ } else {
+ port_entry->held--;
+ spin_unlock(&port_entry->lock);
+#ifdef STATISTICS
+ spin_lock(&port_lock);
+ cthread_rnone++;
+ spin_unlock(&port_lock);
+#endif /* STATISTICS */
+ }
+ } else {
+ port_entry->held--;
+ spin_unlock(&port_entry->lock);
+ }
+ }
+
+}
+
+void
+cthread_msg_active(mach_port_t port, int min, int max)
+{
+ register cproc_t p = cproc_self();
+ register port_entry_t port_entry;
+
+ if (!p->busy) {
+ port_entry = get_port_entry(port, min, max);
+ if (port_entry == 0) return;
+ spin_lock(&port_entry->lock);
+ if (port_entry->held < port_entry->max) {
+ port_entry->held++;
+ p->busy = port_entry;
+ }
+ spin_unlock(&port_entry->lock);
+ }
+}
+
+mach_msg_return_t
+cthread_mach_msg(register mach_msg_header_t *header,
+ register mach_msg_option_t option, mach_msg_size_t send_size,
+ mach_msg_size_t rcv_size, register mach_port_t rcv_name,
+ mach_msg_timeout_t timeout, mach_port_t notify, int min,
+ int max)
+{
+ register port_entry_t port_entry;
+ register cproc_t p = cproc_self();
+ register int sent=0;
+ mach_msg_return_t r;
+ port_entry_t op = (port_entry_t)p->busy;
+
+ port_entry = get_port_entry(rcv_name, min, max);
+
+ if (op && (port_entry_t)op != port_entry)
+ cthread_msg_busy(op->port, op->min, op->max);
+ spin_lock(&port_entry->lock);
+ if (!(port_entry == (port_entry_t)p->busy)) {
+ if (port_entry->held >= max) {
+ if (option & MACH_SEND_MSG) {
+ spin_unlock(&port_entry->lock);
+ r = mach_msg(header, option &~ MACH_RCV_MSG,
+ send_size, 0, MACH_PORT_NULL,
+ timeout, notify);
+ if (r != MACH_MSG_SUCCESS) return r;
+ spin_lock(&port_entry->lock);
+ sent=1;
+ }
+ if (port_entry->held >= max) {
+ spin_lock(&p->lock);
+ cthread_queue_preq(&port_entry->queue, p);
+ spin_unlock(&port_entry->lock);
+#ifdef WAIT_DEBUG
+ p->waiting_for = (char *)port_entry;
+#endif /* WAIT_DEBUG */
+ cproc_block();
+ } else {
+ port_entry->held++;
+ spin_unlock(&port_entry->lock);
+ }
+ } else {
+ port_entry->held++;
+ spin_unlock(&port_entry->lock);
+ }
+ } else {
+ spin_unlock(&port_entry->lock);
+ }
+#ifdef WAIT_DEBUG
+ p->waiting_for = (char *)0;
+#endif /* WAIT_DEBUG */
+ p->busy = port_entry;
+ if ((option & MACH_SEND_MSG) && !sent) {
+ r = mach_msg(header, option,
+ send_size, rcv_size, rcv_name,
+ timeout, notify);
+ } else {
+ r = mach_msg(header, option &~ MACH_SEND_MSG,
+ 0, rcv_size, rcv_name,
+ timeout, notify);
+ }
+ return r;
+}
+
+void
+cproc_fork_prepare(void)
+{
+ register cproc_t p = cproc_self();
+
+ vm_inherit(mach_task_self(),p->stack_base, p->stack_size, VM_INHERIT_COPY);
+ spin_lock(&port_lock);
+ spin_lock(&cproc_list_lock);
+}
+
+void
+cproc_fork_parent(void)
+{
+ register cproc_t p = cproc_self();
+
+ spin_unlock(&cproc_list_lock);
+ spin_unlock(&port_lock);
+ vm_inherit(mach_task_self(),p->stack_base, p->stack_size, VM_INHERIT_NONE);
+}
+
+void
+cproc_fork_child(void)
+{
+ register cproc_t l,p = cproc_self();
+ cproc_t m;
+ register port_entry_t pe;
+ port_entry_t pet;
+ kern_return_t r;
+
+
+ vm_inherit(mach_task_self(),p->stack_base, p->stack_size, VM_INHERIT_NONE);
+ spin_lock_init(&n_kern_lock);
+ cthread_kernel_threads=0;
+#ifdef STATISTICS
+ cthread_ready = 0;
+ cthread_running = 1;
+ cthread_waiting = 0;
+ cthread_wired = 0;
+ spin_lock_init(&wired_lock);
+ cthread_wait_stacks = 0;
+ cthread_waiters = 0;
+ cthread_wakeup = 0;
+ cthread_blocked = 0;
+ cthread_rnone = 0;
+ cthread_yields = 0;
+ cthread_none = 0;
+ cthread_switches = 0;
+ cthread_no_mutex = 0;
+ spin_lock_init(&mutex_count_lock);
+#endif /* STATISTICS */
+
+ for(l=cproc_list;l!=NO_CPROC;l=m) {
+ m=l->next;
+ if (l!=p)
+ free(l);
+ }
+
+ cproc_list = p;
+ p->next = NO_CPROC;
+ spin_lock_init(&cproc_list_lock);
+ cprocs_started = FALSE;
+ cthread_queue_init(&ready);
+ ready_count = 0;
+ spin_lock_init(&ready_lock);
+
+ MACH_CALL(mach_port_allocate(mach_task_self(),
+ MACH_PORT_RIGHT_RECEIVE,
+ &wait_port), r);
+ MACH_CALL(mach_port_insert_right(mach_task_self(),
+ wait_port, wait_port,
+ MACH_MSG_TYPE_MAKE_SEND), r);
+ wakeup_msg.msgh_remote_port = wait_port;
+ wait_count = 0;
+ cthread_queue_init(&waiters);
+ spin_lock_init(&waiters_lock);
+ for(pe=port_list;pe!=PORT_ENTRY_NULL;pe=pet) {
+ pet = pe->next;
+ free(pe);
+ }
+ port_list = PORT_ENTRY_NULL;
+ spin_lock_init(&port_lock);
+
+ if (p->wired) cthread_wire();
+}
diff --git a/libthreads/cthread_data.c b/libthreads/cthread_data.c
new file mode 100644
index 00000000..02e6fa82
--- /dev/null
+++ b/libthreads/cthread_data.c
@@ -0,0 +1,195 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992,1991 Carnegie-Mellon University
+ * All rights reserved. The CMU software License Agreement specifies
+ * the terms and conditions for use and redistribution.
+ */
+/*
+ * HISTORY
+ * $Log: cthread_data.c,v $
+ * Revision 2.4 93/05/10 17:51:20 rvb
+ * Make cthread_set_data and cthread_data macros.
+ * [93/05/06 rvb]
+ *
+ * Revision 2.3 93/01/14 18:04:52 danner
+ * Converted file to ANSI C.
+ * Removed usage of obsolete type any_t.
+ * [92/12/18 pds]
+ *
+ * Revision 2.2 92/05/23 11:35:17 jfriedl
+ * Snarfed from multi-server sources at CMU.
+ * No stdio (for use with single-server).
+ *
+ *
+ * Revision 2.2 91/03/25 14:14:45 jjc
+ * For compatibility with cthread_data:
+ * 1) Added routines, cthread_data and cthread_set_data,
+ * which use the new routines in here.
+ * 2) Defined CTHREAD_KEY_RESERVED as the key used to
+ * access cthread_data.
+ * 3) Changed the first free key from CTHREAD_KEY_NULL
+ * to CTHREAD_KEY_FIRST.
+ * [91/03/18 jjc]
+ * Made simple implementation from POSIX threads specification for
+ * thread specific data.
+ * [91/03/07 jjc]
+ *
+ */
+
+#include <stdio.h>
+#include <cthreads.h>
+
+#define CTHREAD_KEY_MAX (cthread_key_t)8 /* max. no. of keys */
+#define CTHREAD_KEY_NULL (cthread_key_t)0
+
+#if defined(CTHREAD_DATA)
+/*
+ * Key reserved for cthread_data
+ */
+#define CTHREAD_KEY_RESERVED CTHREAD_KEY_NULL
+
+#define CTHREAD_KEY_FIRST (cthread_key_t)1 /* first free key */
+#else /* not defined(CTHREAD_DATA) */
+#define CTHREAD_KEY_FIRST CTHREAD_KEY_NULL /* first free key */
+#endif /* defined(CTHREAD_DATA) */
+
+
+/* lock protecting key creation */
+struct mutex cthread_data_lock = MUTEX_INITIALIZER;
+
+/* next free key */
+cthread_key_t cthread_key = CTHREAD_KEY_FIRST;
+
+
+/*
+ * Create key to private data visible to all threads in task.
+ * Different threads may use same key, but the values bound to the key are
+ * maintained on a thread specific basis.
+ * Returns 0 if successful and returns -1 otherwise.
+ */
+int
+cthread_keycreate(cthread_key_t *key)
+{
+ if (cthread_key >= CTHREAD_KEY_FIRST && cthread_key < CTHREAD_KEY_MAX) {
+ mutex_lock((mutex_t)&cthread_data_lock);
+ *key = cthread_key++;
+ mutex_unlock((mutex_t)&cthread_data_lock);
+ return(0);
+ }
+ else { /* out of keys */
+ *key = CTHREAD_KEY_INVALID;
+ return(-1);
+ }
+}
+
+
+/*
+ * Get private data associated with given key
+ * Returns 0 if successful and returns -1 if the key is invalid.
+ * If the calling thread doesn't have a value for the given key,
+ * the value returned is CTHREAD_DATA_VALUE_NULL.
+ */
+int
+cthread_getspecific(cthread_key_t key, void **value)
+{
+ register cthread_t self;
+ register void **thread_data;
+
+ *value = CTHREAD_DATA_VALUE_NULL;
+ if (key < CTHREAD_KEY_NULL || key >= cthread_key)
+ return(-1);
+
+ self = cthread_self();
+ thread_data = (void **)(self->private_data);
+ if (thread_data != NULL)
+ *value = thread_data[key];
+
+ return(0);
+}
+
+
+/*
+ * Set private data associated with given key
+ * Returns 0 if successful and returns -1 otherwise.
+ */
+int
+cthread_setspecific(cthread_key_t key, void *value)
+{
+ register int i;
+ register cthread_t self;
+ register void **thread_data;
+
+ if (key < CTHREAD_KEY_NULL || key >= cthread_key)
+ return(-1);
+
+ self = cthread_self();
+ thread_data = (void **)(self->private_data);
+ if (thread_data != NULL)
+ thread_data[key] = value;
+ else {
+ /*
+ * Allocate and initialize thread data table,
+ * point cthread_data at it, and then set the
+ * data for the given key with the given value.
+ */
+ thread_data = malloc(CTHREAD_KEY_MAX * sizeof(void *));
+ if (thread_data == NULL) {
+ printf("cthread_setspecific: malloc failed\n");
+ return(-1);
+ }
+ self->private_data = thread_data;
+
+ for (i = 0; i < CTHREAD_KEY_MAX; i++)
+ thread_data[i] = CTHREAD_DATA_VALUE_NULL;
+
+ thread_data[key] = value;
+ }
+ return(0);
+}
+
+
+#if defined(CTHREAD_DATA_XX)
+/*
+ * Set thread specific "global" variable,
+ * using new POSIX routines.
+ * Crash and burn if the thread given isn't the calling thread.
+ * XXX For compatibility with old cthread_set_data() XXX
+ */
+int
+cthread_set_data(cthread_t t, void *x)
+{
+ register cthread_t self;
+
+ self = cthread_self();
+ if (t == self)
+ return(cthread_setspecific(CTHREAD_KEY_RESERVED, x));
+ else {
+ ASSERT(t == self);
+ return(-1);
+ }
+}
+
+
+/*
+ * Get thread specific "global" variable,
+ * using new POSIX routines.
+ * Crash and burn if the thread given isn't the calling thread.
+ * XXX For compatibility with old cthread_data() XXX
+ */
+void *
+cthread_data(cthread_t t)
+{
+ register cthread_t self;
+ void *value;
+
+ self = cthread_self();
+ if (t == self) {
+ (void)cthread_getspecific(CTHREAD_KEY_RESERVED, &value);
+ return(value);
+ }
+ else {
+ ASSERT(t == self);
+ return(NULL);
+ }
+}
+#endif /* defined(CTHREAD_DATA_XX) */
diff --git a/libthreads/cthread_internals.h b/libthreads/cthread_internals.h
new file mode 100644
index 00000000..81e3b91c
--- /dev/null
+++ b/libthreads/cthread_internals.h
@@ -0,0 +1,324 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992,1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * 26-Oct-94 Johannes Helander (jvh) Helsinki University of Technology
+ * Defined WAIT_DEBUG and initialized wait_enum
+ *
+ * $Log: cthread_internals.h,v $
+ * Revision 1.6 2002/05/27 02:50:10 roland
+ * 2002-05-26 Roland McGrath <roland@frob.com>
+ *
+ * Changes merged from CMU MK83a version:
+ * * cthreads.h, options.h: Various cleanups.
+ * * call.c, cthread_data.c, sync.c, mig_support.c: Likewise.
+ * * i386/cthreads.h, i386/thread.c, i386/lock.s: Likewise.
+ * * cthread_internals.h: Add decls for internal functions.
+ * (struct cproc): Use vm_offset_t for stack_base and stack_size members.
+ * Use natural_t for context member.
+ * * cprocs.c: Use prototypes for all defns.
+ * * cthreads.c: Likewise.
+ * (cthread_exit): Cast any_t to integer_t before int.
+ *
+ * Revision 2.17 93/05/10 21:33:36 rvb
+ * Context is a natural_t. Assumming, that is, that on
+ * some future architecture one word might be enough.
+ * [93/05/06 09:19:35 af]
+ *
+ * Revision 2.16 93/05/10 17:51:23 rvb
+ * Flush stdlib
+ * [93/05/05 09:12:29 rvb]
+ *
+ * Revision 2.15 93/01/14 18:04:56 danner
+ * Added declarations for library-internal routines.
+ * [92/12/18 pds]
+ *
+ * Replaced malloc and mach_error declarations with includes of
+ * mach_error.h and stdlib.h.
+ * [92/06/13 pds]
+ * 64bit cleanup.
+ * [92/12/01 af]
+ *
+ * Revision 2.14 92/08/03 18:03:56 jfriedl
+ * Made state element of struct cproc volatile.
+ * [92/08/02 jfriedl]
+ *
+ * Revision 2.13 92/03/06 14:09:24 rpd
+ * Added yield, defined using thread_switch.
+ * [92/03/06 rpd]
+ *
+ * Revision 2.12 92/03/01 00:40:23 rpd
+ * Removed exit declaration. It conflicted with the real thing.
+ * [92/02/29 rpd]
+ *
+ * Revision 2.11 91/08/28 11:19:23 jsb
+ * Fixed MACH_CALL to allow multi-line expressions.
+ * [91/08/23 rpd]
+ *
+ * Revision 2.10 91/07/31 18:33:33 dbg
+ * Protect against redefinition of ASSERT.
+ * [91/07/30 17:33:21 dbg]
+ *
+ * Revision 2.9 91/05/14 17:56:24 mrt
+ * Correcting copyright
+ *
+ * Revision 2.8 91/02/14 14:19:42 mrt
+ * Added new Mach copyright
+ * [91/02/13 12:41:02 mrt]
+ *
+ * Revision 2.7 90/11/05 14:36:55 rpd
+ * Added spin_lock_t.
+ * [90/10/31 rwd]
+ *
+ * Revision 2.6 90/09/09 14:34:51 rpd
+ * Remove special field.
+ * [90/08/24 rwd]
+ *
+ * Revision 2.5 90/06/02 15:13:44 rpd
+ * Converted to new IPC.
+ * [90/03/20 20:52:47 rpd]
+ *
+ * Revision 2.4 90/03/14 21:12:11 rwd
+ * Added waiting_for field for debugging deadlocks.
+ * [90/03/01 rwd]
+ * Added list field to keep a master list of all cprocs.
+ * [90/03/01 rwd]
+ *
+ * Revision 2.3 90/01/19 14:37:08 rwd
+ * Keep track of real thread for use in thread_* substitutes.
+ * Add CPROC_ARUN for about to run and CPROC_HOLD to avoid holding
+ * spin_locks over system calls.
+ * [90/01/03 rwd]
+ * Add busy field to be used by cthread_msg calls to make sure we
+ * have the right number of blocked kernel threads.
+ * [89/12/21 rwd]
+ *
+ * Revision 2.2 89/12/08 19:53:28 rwd
+ * Added CPROC_CONDWAIT state
+ * [89/11/28 rwd]
+ * Added on_special field.
+ * [89/11/26 rwd]
+ * Removed MSGOPT conditionals
+ * [89/11/25 rwd]
+ * Removed old debugging code. Add wired port/flag. Add state
+ * for small state machine.
+ * [89/10/30 rwd]
+ * Added CPDEBUG code
+ * [89/10/26 rwd]
+ * Change TRACE to {x;} else.
+ * [89/10/24 rwd]
+ * Rewrote to work for limited number of kernel threads. This is
+ * basically a merge of coroutine and thread. Added
+ * cthread_receivce call for use by servers.
+ * [89/10/23 rwd]
+ *
+ */
+/*
+ * cthread_internals.h
+ *
+ *
+ * Private definitions for the C Threads implementation.
+ *
+ * The cproc structure is used for different implementations
+ * of the basic schedulable units that execute cthreads.
+ *
+ */
+
+
+#include "options.h"
+#include <mach/port.h>
+#include <mach/message.h>
+#include <mach/thread_switch.h>
+
+#if !defined(__STDC__) && !defined(volatile)
+# ifdef __GNUC__
+# define volatile __volatile__
+# else
+# define volatile /* you lose */
+# endif
+#endif
+
+/* Type of the TCB. */
+typedef struct
+{
+ void *tcb; /* Points to this structure. */
+ void *dtv; /* Vector of pointers to TLS data. */
+ thread_t self; /* This thread's control port. */
+} tcbhead_t;
+
+/*
+ * Low-level thread implementation.
+ * This structure must agree with struct ur_cthread in cthreads.h
+ */
+typedef struct cproc {
+ struct cproc *next; /* for lock, condition, and ready queues */
+ cthread_t incarnation; /* for cthread_self() */
+
+ struct cproc *list; /* for master cproc list */
+#ifdef WAIT_DEBUG
+ volatile char *waiting_for; /* address of mutex/cond waiting for */
+#endif /* WAIT_DEBUG */
+
+#if 0
+ /* This is not needed in GNU; libc handles it. */
+ mach_port_t reply_port; /* for mig_get_reply_port() */
+#endif
+
+ natural_t context;
+ spin_lock_t lock;
+ volatile int state; /* current state */
+#define CPROC_RUNNING 0
+#define CPROC_SWITCHING 1
+#define CPROC_BLOCKED 2
+#define CPROC_CONDWAIT 4
+
+ mach_port_t wired; /* is cthread wired to kernel thread */
+ void *busy; /* used with cthread_msg calls */
+
+ mach_msg_header_t msg;
+
+ vm_offset_t stack_base;
+ vm_offset_t stack_size;
+} *cproc_t;
+
+#define NO_CPROC ((cproc_t) 0)
+#define cproc_self() ((cproc_t) ur_cthread_self())
+
+#if 0
+/* This declaration conflicts with <stdlib.h> in GNU. */
+/*
+ * C Threads imports:
+ */
+extern char *malloc();
+#endif
+
+/*
+ * Mach imports:
+ */
+extern void mach_error();
+
+/*
+ * Macro for MACH kernel calls.
+ */
+#ifdef CHECK_STATUS
+#define MACH_CALL(expr, ret) \
+ if (((ret) = (expr)) != KERN_SUCCESS) { \
+ quit(1, "error in %s at %d: %s\n", __FILE__, __LINE__, \
+ mach_error_string(ret)); \
+ } else
+#else /* CHECK_STATUS */
+#define MACH_CALL(expr, ret) (ret) = (expr)
+#endif /* CHECK_STATUS */
+
+#define private static
+#ifndef ASSERT
+#define ASSERT(x)
+#endif
+#define TRACE(x)
+
+/*
+ * What we do to yield the processor:
+ * (This depresses the thread's priority for up to 10ms.)
+ */
+
+#define yield() \
+ (void) thread_switch(MACH_PORT_NULL, SWITCH_OPTION_DEPRESS, 10)
+
+/*
+ * Functions implemented in malloc.c.
+ */
+
+#if defined(DEBUG)
+extern void print_malloc_free_list(void);
+#endif /* defined(DEBUG) */
+
+extern void malloc_fork_prepare(void);
+
+extern void malloc_fork_parent(void);
+
+extern void malloc_fork_child(void);
+
+
+/*
+ * Functions implemented in stack.c.
+ */
+
+extern vm_offset_t stack_init(cproc_t _cproc);
+
+extern void alloc_stack(cproc_t _cproc);
+
+extern vm_offset_t cproc_stack_base(cproc_t _cproc, int _offset);
+
+extern void stack_fork_child(void);
+
+/*
+ * Functions implemented in cprocs.c.
+ */
+
+extern vm_offset_t cproc_init(void);
+
+extern void cproc_waiting(cproc_t _waiter);
+
+extern void cproc_block(void);
+
+extern cproc_t cproc_create(void);
+
+extern void cproc_fork_prepare(void);
+
+extern void cproc_fork_parent(void);
+
+extern void cproc_fork_child(void);
+
+/*
+ * Function implemented in cthreads.c.
+ */
+
+extern void cthread_body(cproc_t _self);
+
+/*
+ * Functions from machine dependent files.
+ */
+
+extern void cproc_switch(natural_t *_cur, const natural_t *_new,
+ spin_lock_t *_lock);
+
+extern void cproc_start_wait(natural_t *_parent, cproc_t _child,
+ vm_offset_t _stackp,
+ spin_lock_t *_lock);
+
+extern void cproc_prepare(cproc_t _child,
+ natural_t *_child_context,
+ vm_offset_t _stackp,
+ void (*cthread_body_pc)());
+
+extern void cproc_setup(cproc_t _child, thread_t _mach_thread,
+ tcbhead_t *tcb, void (*_routine)(cproc_t));
+
+
+/* From glibc. */
+
+/* Dynamic linker TLS allocation. */
+extern void *_dl_allocate_tls(void *);
diff --git a/libthreads/cthreads.c b/libthreads/cthreads.c
new file mode 100644
index 00000000..1361b8b3
--- /dev/null
+++ b/libthreads/cthreads.c
@@ -0,0 +1,488 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992,1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * 20-Oct-93 Tero Kivinen (kivinen) at Helsinki University of Technology
+ * Renamed cthread_t->catch to to cthread_t->catch_exit, because
+ * catch is reserved word in c++.
+ *
+ * $Log: cthreads.c,v $
+ * Revision 1.15 2002/07/31 02:35:14 marcus
+ * Add comment to last change, for the benefit of the next merge :)
+ *
+ * Revision 1.14 2002/07/31 02:20:44 marcus
+ * 2002-07-29 Marcus Brinkmann <marcus@gnu.org>
+ *
+ * * cthreads.c (cthread_init): Move cthread_alloc call before
+ * cproc_init call (lost in merge).
+ *
+ * Revision 1.13 2002/05/28 23:55:55 roland
+ * 2002-05-28 Roland McGrath <roland@frob.com>
+ *
+ * * cthreads.c (cthread_fork_prepare, cthread_fork_parent,
+ * cthread_fork_child): Don't call malloc_fork_* (lost in merge).
+ *
+ * Revision 1.12 2002/05/27 02:50:10 roland
+ * 2002-05-26 Roland McGrath <roland@frob.com>
+ *
+ * Changes merged from CMU MK83a version:
+ * * cthreads.h, options.h: Various cleanups.
+ * * call.c, cthread_data.c, sync.c, mig_support.c: Likewise.
+ * * i386/cthreads.h, i386/thread.c, i386/lock.s: Likewise.
+ * * cthread_internals.h: Add decls for internal functions.
+ * (struct cproc): Use vm_offset_t for stack_base and stack_size members.
+ * Use natural_t for context member.
+ * * cprocs.c: Use prototypes for all defns.
+ * * cthreads.c: Likewise.
+ * (cthread_exit): Cast any_t to integer_t before int.
+ *
+ * Revision 2.13 93/01/21 12:27:55 danner
+ * Remove deadlock in cproc_fork_child; must release malloc lock first.
+ * [93/01/19 16:37:43 bershad]
+ *
+ * Revision 2.12 93/01/14 18:05:00 danner
+ * Converted file to ANSI C.
+ * Removed use of obsolete type any_t.
+ * [92/12/18 pds]
+ * 64bit cleanup.
+ * [92/12/01 af]
+ *
+ * Free private_data in cthread_exit, from Mike Kupfer.
+ * [92/11/30 af]
+ *
+ * Revision 2.11 92/07/20 13:33:37 cmaeda
+ * In cthread_init, do machine dependent initialization if it's defined.
+ * [92/05/11 14:41:08 cmaeda]
+ *
+ * Revision 2.10 91/08/28 11:19:26 jsb
+ * Fixed mig_init initialization in cthread_fork_child.
+ * [91/08/23 rpd]
+ *
+ * Revision 2.9 91/07/31 18:34:23 dbg
+ * Fix bad self-pointer reference.
+ *
+ * Don't declare _setjmp and _longjmp; they are included by
+ * cthreads.h.
+ * [91/07/30 17:33:50 dbg]
+ *
+ * Revision 2.8 91/05/14 17:56:31 mrt
+ * Correcting copyright
+ *
+ * Revision 2.7 91/02/14 14:19:47 mrt
+ * Added new Mach copyright
+ * [91/02/13 12:41:07 mrt]
+ *
+ * Revision 2.6 90/11/05 14:37:03 rpd
+ * Added cthread_fork_{prepare,parent,child}.
+ * [90/11/02 rwd]
+ *
+ * Add spin_lock_t.
+ * [90/10/31 rwd]
+ *
+ * Revision 2.5 90/08/07 14:30:58 rpd
+ * Removed RCS keyword nonsense.
+ *
+ * Revision 2.4 90/06/02 15:13:49 rpd
+ * Converted to new IPC.
+ * [90/03/20 20:56:44 rpd]
+ *
+ * Revision 2.3 90/01/19 14:37:12 rwd
+ * Make cthread_init return pointer to new stack.
+ * [89/12/18 19:17:45 rwd]
+ *
+ * Revision 2.2 89/12/08 19:53:37 rwd
+ * Change cproc and cthread counters to globals with better names.
+ * [89/11/02 rwd]
+ *
+ * Revision 2.1 89/08/03 17:09:34 rwd
+ * Created.
+ *
+ *
+ * 31-Dec-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed cthread_exit() logic for the case of the main thread,
+ * to fix thread and stack memory leak found by Camelot group.
+ *
+ * 21-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Added consistency check in beginning of cthread_body().
+ *
+ * 11-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Removed cthread_port() and cthread_set_port().
+ * Removed port deallocation from cthread_free().
+ * Minor changes to cthread_body(), cthread_exit(), and cthread_done().
+ *
+ * 10-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed call to mig_init() in cthread_init() to pass 1 as argument.
+ *
+ * 31-Jul-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Added call to mig_init() from cthread_init().
+ */
+/*
+ * File: cthreads.c
+ * Author: Eric Cooper, Carnegie Mellon University
+ * Date: July, 1987
+ *
+ * Implementation of fork, join, exit, etc.
+ */
+
+#include <cthreads.h>
+#include <mach/mig_support.h>
+#include "cthread_internals.h"
+#include <locale.h>
+
+/*
+ * Thread status bits.
+ */
+#define T_MAIN 0x1
+#define T_RETURNED 0x2
+#define T_DETACHED 0x4
+
+#if defined(DEBUG)
+int cthread_debug = FALSE;
+#endif /* defined(DEBUG) */
+
+private struct cthread_queue cthreads = QUEUE_INITIALIZER;
+private struct mutex cthread_lock = MUTEX_INITIALIZER;
+private struct condition cthread_needed = CONDITION_INITIALIZER;
+private struct condition cthread_idle = CONDITION_INITIALIZER;
+int cthread_cprocs = 0;
+int cthread_cthreads = 0;
+int cthread_max_cprocs = 0;
+
+private cthread_t free_cthreads = NO_CTHREAD; /* free list */
+private spin_lock_t free_lock = SPIN_LOCK_INITIALIZER; /* unlocked */
+
+private struct cthread initial_cthread = { 0 };
+
+private cthread_t
+cthread_alloc(cthread_fn_t func, void *arg)
+{
+ register cthread_t t = NO_CTHREAD;
+
+ if (free_cthreads != NO_CTHREAD) {
+ /*
+ * Don't try for the lock unless
+ * the list is likely to be nonempty.
+ * We can't be sure, though, until we lock it.
+ */
+ spin_lock(&free_lock);
+ t = free_cthreads;
+ if (t != NO_CTHREAD)
+ free_cthreads = t->next;
+ spin_unlock(&free_lock);
+ }
+ if (t == NO_CTHREAD) {
+ /*
+ * The free list was empty.
+ * We may have only found this out after
+ * locking it, which is why this isn't an
+ * "else" branch of the previous statement.
+ */
+ t = (cthread_t) malloc(sizeof(struct cthread));
+ }
+ *t = initial_cthread;
+ t->func = func;
+ t->arg = arg;
+ return t;
+}
+
+private void
+cthread_free(cthread_t t)
+{
+ spin_lock(&free_lock);
+ t->next = free_cthreads;
+ free_cthreads = t;
+ spin_unlock(&free_lock);
+}
+
+vm_offset_t
+cthread_init(void)
+{
+ static int cthreads_started = FALSE;
+ register cproc_t p;
+ register cthread_t t;
+ vm_offset_t stack;
+
+ if (cthreads_started)
+ return 0;
+
+ /* cthread_alloc must be called before cproc_init, because
+ malloc is not usable between initializing the new stack and
+ switching to it. */
+ t = cthread_alloc((cthread_fn_t) 0, (any_t) 0);
+ stack = cproc_init();
+ cthread_cprocs = 1;
+
+#ifdef cthread_md_init
+ cthread_md_init();
+#endif
+
+ cthread_cthreads = 1;
+ t->state |= T_MAIN;
+ cthread_set_name(t, "main");
+
+ /* cproc_self() doesn't work yet, because
+ we haven't yet switched to the new stack. */
+
+ p = *(cproc_t *)&ur_cthread_ptr(stack);
+ p->incarnation = t;
+ /* The original CMU code passes P to mig_init. In GNU, mig_init
+ does not know about cproc_t; instead it expects to be passed the
+ stack pointer of the initial thread. */
+ mig_init((void *) stack); /* enable multi-threaded mig interfaces */
+
+ cthreads_started = TRUE;
+ return stack;
+}
+
+/*
+ * Used for automatic initialization by crt0.
+ */
+vm_offset_t (*_cthread_init_routine)(void) = cthread_init;
+
+
+/*
+ * Procedure invoked at the base of each cthread.
+ */
+void
+cthread_body(cproc_t self)
+{
+ register cthread_t t;
+
+ ASSERT(cproc_self() == self);
+ TRACE(printf("[idle] cthread_body(%x)\n", self));
+ mutex_lock(&cthread_lock);
+ for (;;) {
+ /*
+ * Dequeue a thread invocation request.
+ */
+ cthread_queue_deq(&cthreads, cthread_t, t);
+ if (t != NO_CTHREAD) {
+ /*
+ * We have a thread to execute.
+ */
+ mutex_unlock(&cthread_lock);
+ cthread_assoc(self, t); /* assume thread's identity */
+ if (_setjmp(t->catch_exit) == 0) { /* catch for cthread_exit() */
+ /*
+ * Execute the fork request.
+ */
+
+ /* A fresh thread needs to be bound to the
+ global locale. */
+ uselocale (LC_GLOBAL_LOCALE);
+
+ t->result = (*(t->func))(t->arg);
+ }
+ /*
+ * Return result from thread.
+ */
+ TRACE(printf("[%s] done()\n", cthread_name(t)));
+ mutex_lock(&t->lock);
+ if (t->state & T_DETACHED) {
+ mutex_unlock(&t->lock);
+ cthread_free(t);
+ } else {
+ t->state |= T_RETURNED;
+ mutex_unlock(&t->lock);
+ condition_signal(&t->done);
+ }
+ cthread_assoc(self, NO_CTHREAD);
+ mutex_lock(&cthread_lock);
+ cthread_cthreads -= 1;
+ } else {
+ /*
+ * Queue is empty.
+ * Signal that we're idle in case the main thread
+ * is waiting to exit, then wait for reincarnation.
+ */
+ condition_signal(&cthread_idle);
+ condition_wait(&cthread_needed, &cthread_lock);
+ }
+ }
+}
+
+cthread_t
+cthread_fork(cthread_fn_t func, void *arg)
+{
+ register cthread_t t;
+
+ TRACE(printf("[%s] fork()\n", cthread_name(cthread_self())));
+ mutex_lock(&cthread_lock);
+ t = cthread_alloc(func, arg);
+ cthread_queue_enq(&cthreads, t);
+ if (++cthread_cthreads > cthread_cprocs && (cthread_max_cprocs == 0 || cthread_cprocs < cthread_max_cprocs)) {
+ cthread_cprocs += 1;
+ cproc_create();
+ }
+ mutex_unlock(&cthread_lock);
+ condition_signal(&cthread_needed);
+ return t;
+}
+
+void
+cthread_detach(t)
+ cthread_t t;
+{
+ TRACE(printf("[%s] detach(%s)\n", cthread_name(cthread_self()), cthread_name(t)));
+ mutex_lock(&t->lock);
+ if (t->state & T_RETURNED) {
+ mutex_unlock(&t->lock);
+ cthread_free(t);
+ } else {
+ t->state |= T_DETACHED;
+ mutex_unlock(&t->lock);
+ }
+}
+
+void *
+cthread_join(cthread_t t)
+{
+ void *result;
+
+ TRACE(printf("[%s] join(%s)\n", cthread_name(cthread_self()), cthread_name(t)));
+ mutex_lock(&t->lock);
+ ASSERT(! (t->state & T_DETACHED));
+ while (! (t->state & T_RETURNED))
+ condition_wait(&t->done, &t->lock);
+ result = t->result;
+ mutex_unlock(&t->lock);
+ cthread_free(t);
+ return result;
+}
+
+void
+cthread_exit(void *result)
+{
+ register cthread_t t = cthread_self();
+
+ TRACE(printf("[%s] exit()\n", cthread_name(t)));
+ t->result = result;
+ if (t->private_data != 0) {
+ free((char *)t->private_data);
+ t->private_data = 0;
+ }
+ if (t->state & T_MAIN) {
+ mutex_lock(&cthread_lock);
+ while (cthread_cthreads > 1)
+ condition_wait(&cthread_idle, &cthread_lock);
+ mutex_unlock(&cthread_lock);
+ exit((int) (integer_t) result);
+ } else {
+ _longjmp(t->catch_exit, TRUE);
+ }
+}
+
+/*
+ * Used for automatic finalization by crt0. Cast needed since too many C
+ * compilers choke on the type void (*)().
+ */
+int (*_cthread_exit_routine)() = (int (*)()) cthread_exit;
+
+void
+cthread_set_name(cthread_t t, const char *name)
+{
+ t->name = name;
+}
+
+const char *
+cthread_name(cthread_t t)
+{
+ return (t == NO_CTHREAD ? "idle" : (t->name == 0 ? "?" : t->name));
+}
+
+int
+cthread_limit(void)
+{
+ return cthread_max_cprocs;
+}
+
+void
+cthread_set_limit(int n)
+{
+ cthread_max_cprocs = n;
+}
+
+int
+cthread_count(void)
+{
+ return cthread_cthreads;
+}
+
+void
+cthread_fork_prepare(void)
+{
+ spin_lock(&free_lock);
+ mutex_lock(&cthread_lock);
+ cproc_fork_prepare();
+}
+
+void
+cthread_fork_parent(void)
+{
+ cproc_fork_parent();
+ mutex_unlock(&cthread_lock);
+ spin_unlock(&free_lock);
+}
+
+void
+cthread_fork_child(void)
+{
+ cthread_t t;
+ cproc_t p;
+
+ cproc_fork_child();
+ mutex_unlock(&cthread_lock);
+ spin_unlock(&free_lock);
+ condition_init(&cthread_needed);
+ condition_init(&cthread_idle);
+
+ cthread_max_cprocs = 0;
+
+ stack_fork_child();
+
+ while (TRUE) { /* Free cthread runnable list */
+ cthread_queue_deq(&cthreads, cthread_t, t);
+ if (t == NO_CTHREAD) break;
+ free((char *) t);
+ }
+
+ while (free_cthreads != NO_CTHREAD) { /* Free cthread free list */
+ t = free_cthreads;
+ free_cthreads = free_cthreads->next;
+ free((char *) t);
+ }
+
+ cthread_cprocs = 1;
+ t = cthread_self();
+ cthread_cthreads = 1;
+ t->state |= T_MAIN;
+ cthread_set_name(t, "main");
+
+ p = cproc_self();
+ p->incarnation = t;
+ /* XXX needs hacking for GNU */
+ mig_init(p); /* enable multi-threaded mig interfaces */
+}
diff --git a/libthreads/cthreads.h b/libthreads/cthreads.h
new file mode 100644
index 00000000..d937dcca
--- /dev/null
+++ b/libthreads/cthreads.h
@@ -0,0 +1,710 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993,1992,1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * 20-Oct-93 Tero Kivinen (kivinen) at Helsinki University of Technology
+ * Renamed cthread_t->catch to to cthread_t->catch_exit, because
+ * catch is reserved word in c++.
+ *
+ * 12-Oct-93 Johannes Helander (jvh) at Helsinki University of Technology
+ * Added CONDITION_NAMED_INITIALIZER and MUTEX_NAMED_INITIALIZER1
+ * macros. They take one argument: a name string.
+ *
+ * $Log: cthreads.h,v $
+ * Revision 1.19 2002/05/28 23:55:58 roland
+ * 2002-05-28 Roland McGrath <roland@frob.com>
+ *
+ * * cthreads.h (hurd_condition_wait, condition_implies,
+ * condition_unimplies): Restore decls lost in merge.
+ * (mutex_clear): Define as mutex_init instead of bogon (lost in merge).
+ *
+ * Revision 1.18 2002/05/27 02:50:10 roland
+ * 2002-05-26 Roland McGrath <roland@frob.com>
+ *
+ * Changes merged from CMU MK83a version:
+ * * cthreads.h, options.h: Various cleanups.
+ * * call.c, cthread_data.c, sync.c, mig_support.c: Likewise.
+ * * i386/cthreads.h, i386/thread.c, i386/lock.s: Likewise.
+ * * cthread_internals.h: Add decls for internal functions.
+ * (struct cproc): Use vm_offset_t for stack_base and stack_size members.
+ * Use natural_t for context member.
+ * * cprocs.c: Use prototypes for all defns.
+ * * cthreads.c: Likewise.
+ * (cthread_exit): Cast any_t to integer_t before int.
+ *
+ * Revision 2.17 93/05/10 19:43:11 rvb
+ * Removed include of stdlib.h and just define malloc
+ * [93/04/27 mrt]
+ *
+ * Revision 2.16 93/05/10 17:51:26 rvb
+ * Just imagine how much more useful TWO special/fast lookup
+ * variables could be. (Actually, I am planning on using this
+ * for bsdss -- for multiple threads per task. If I don't, I'll
+ * remove the feature.)
+ * [93/05/10 rvb]
+ * Big mistake here! CTHREAD_DATA must always be set TRUE.
+ * cthreads.h is included by import_mach.h by lots of files
+ * that are not compiled with -DCTHREAD_DATA. This means
+ * they see a different structure for cthread_t than the
+ * cthread library -- which is compiled with CTHREAD_DATA.
+ * Also, make cthread_set_data and cthread_data macros.
+ * [93/05/06 rvb]
+ * Flush stdlib
+ * [93/05/05 rvb]
+ *
+ * Revision 2.15 93/01/27 09:03:32 danner
+ * Updated include of mach/mach.h to mach.h
+ *
+ *
+ * Revision 2.14 93/01/24 13:24:50 danner
+ * Get MACRO_BEGIN, MACRO_END, NEVER, ... from sys/macro_help.h
+ * why define it here.
+ * [92/10/20 rvb]
+ *
+ * Revision 2.13 93/01/14 18:05:04 danner
+ * Added MACRO_BEGIN and MACRO_END to definition of spin_lock.
+ * Fixed return value of cthread_set_data.
+ * Added prototypes for other miscellaneous functions.
+ * [92/12/18 pds]
+ * Converted file to ANSI C.
+ * Added declarations of cthread_fork_{prepare,parent,child}.
+ * Added include of <sys/macro_help.h>.
+ * [92/12/13 pds]
+ *
+ * Replaced calloc declaration with an include of stdlib.h.
+ * [92/06/15 pds]
+ * 64bit cleanup.
+ * [92/12/02 af]
+ *
+ * Revision 2.12 92/05/22 18:38:36 jfriedl
+ * From Mike Kupfer <kupfer@sprite.Berkeley.EDU>:
+ * Add declaration for cthread_wire().
+ * Merge in Jonathan Chew's changes for thread-local data.
+ * Use MACRO_BEGIN and MACRO_END.
+ *
+ * Revision 1.8 91/03/25 14:14:49 jjc
+ * For compatibility with cthread_data:
+ * 1) Added private_data field to cthread structure
+ * for use by POSIX thread specific data routines.
+ * 2) Conditionalized old data field used by cthread_data
+ * under CTHREAD_DATA for binary compatibility.
+ * 3) Changed macros, cthread_set_data and cthread_data,
+ * into routines which use the POSIX routines for
+ * source compatibility.
+ * Also, conditionalized under CTHREAD_DATA.
+ * [91/03/18 jjc]
+ * Added support for multiplexing the thread specific global
+ * variable, cthread_data, using the POSIX threads interface
+ * for thread private data.
+ * [91/03/14 jjc]
+ *
+ * Revision 2.11 91/08/03 18:20:15 jsb
+ * Removed the infamous line 122.
+ * [91/08/01 22:40:24 jsb]
+ *
+ * Revision 2.10 91/07/31 18:35:42 dbg
+ * Fix the standard-C conditional: it's __STDC__.
+ *
+ * Allow for macro-redefinition of cthread_sp, spin_try_lock,
+ * spin_unlock (from machine/cthreads.h).
+ * [91/07/30 17:34:28 dbg]
+ *
+ * Revision 2.9 91/05/14 17:56:42 mrt
+ * Correcting copyright
+ *
+ * Revision 2.8 91/02/14 14:19:52 mrt
+ * Added new Mach copyright
+ * [91/02/13 12:41:15 mrt]
+ *
+ * Revision 2.7 90/11/05 14:37:12 rpd
+ * Include machine/cthreads.h. Added spin_lock_t.
+ * [90/10/31 rwd]
+ *
+ * Revision 2.6 90/10/12 13:07:24 rpd
+ * Channge to allow for positive stack growth.
+ * [90/10/10 rwd]
+ *
+ * Revision 2.5 90/09/09 14:34:56 rpd
+ * Remove mutex_special and debug_mutex.
+ * [90/08/24 rwd]
+ *
+ * Revision 2.4 90/08/07 14:31:14 rpd
+ * Removed RCS keyword nonsense.
+ *
+ * Revision 2.3 90/01/19 14:37:18 rwd
+ * Add back pointer to cthread structure.
+ * [90/01/03 rwd]
+ * Change definition of cthread_init and change ur_cthread_self macro
+ * to reflect movement of self pointer on stack.
+ * [89/12/18 19:18:34 rwd]
+ *
+ * Revision 2.2 89/12/08 19:53:49 rwd
+ * Change spin_try_lock to int.
+ * [89/11/30 rwd]
+ * Changed mutex macros to deal with special mutexs
+ * [89/11/26 rwd]
+ * Make mutex_{set,clear}_special routines instead of macros.
+ * [89/11/25 rwd]
+ * Added mutex_special to specify a need to context switch on this
+ * mutex.
+ * [89/11/21 rwd]
+ *
+ * Made mutex_lock a macro trying to grab the spin_lock first.
+ * [89/11/13 rwd]
+ * Removed conditionals. Mutexes are more like conditions now.
+ * Changed for limited kernel thread version.
+ * [89/10/23 rwd]
+ *
+ * Revision 2.1 89/08/03 17:09:40 rwd
+ * Created.
+ *
+ *
+ * 28-Oct-88 Eric Cooper (ecc) at Carnegie Mellon University
+ * Implemented spin_lock() as test and test-and-set logic
+ * (using mutex_try_lock()) in sync.c. Changed ((char *) 0)
+ * to 0, at Mike Jones's suggestion, and turned on ANSI-style
+ * declarations in either C++ or _STDC_.
+ *
+ * 29-Sep-88 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed NULL to ((char *) 0) to avoid dependency on <stdio.h>,
+ * at Alessandro Forin's suggestion.
+ *
+ * 08-Sep-88 Alessandro Forin (af) at Carnegie Mellon University
+ * Changed queue_t to cthread_queue_t and string_t to char *
+ * to avoid conflicts.
+ *
+ * 01-Apr-88 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed compound statement macros to use the
+ * do { ... } while (0) trick, so that they work
+ * in all statement contexts.
+ *
+ * 19-Feb-88 Eric Cooper (ecc) at Carnegie Mellon University
+ * Made spin_unlock() and mutex_unlock() into procedure calls
+ * rather than macros, so that even smart compilers can't reorder
+ * the clearing of the lock. Suggested by Jeff Eppinger.
+ * Removed the now empty <machine>/cthreads.h.
+ *
+ * 01-Dec-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed cthread_self() to mask the current SP to find
+ * the self pointer stored at the base of the stack.
+ *
+ * 22-Jul-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Fixed bugs in mutex_set_name and condition_set_name
+ * due to bad choice of macro formal parameter name.
+ *
+ * 21-Jul-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Moved #include <machine/cthreads.h> to avoid referring
+ * to types before they are declared (required by C++).
+ *
+ * 9-Jul-87 Michael Jones (mbj) at Carnegie Mellon University
+ * Added conditional type declarations for C++.
+ * Added _cthread_init_routine and _cthread_exit_routine variables
+ * for automatic initialization and finalization by crt0.
+ */
+/*
+ * File: cthreads.h
+ * Author: Eric Cooper, Carnegie Mellon University
+ * Date: Jul, 1987
+ *
+ * Definitions for the C Threads package.
+ *
+ */
+
+
+#ifndef _CTHREADS_
+#define _CTHREADS_ 1
+
+#if 0
+/* This is CMU's machine-dependent file. In GNU all of the machine
+ dependencies are dealt with in libc. */
+#include <machine/cthreads.h>
+#include <mach.h>
+#include <sys/macro_help.h>
+#include <mach/machine/vm_param.h>
+
+#ifdef __STDC__
+extern void *malloc();
+#else
+extern char *malloc();
+#endif
+
+#else /* GNU */
+# include <stdlib.h>
+# include <mach.h>
+# include <mach/machine/vm_param.h>
+# include <machine-sp.h>
+# define cthread_sp() ((vm_address_t) __thread_stack_pointer ())
+# define MACRO_BEGIN __extension__ ({
+# define MACRO_END 0; })
+#endif
+
+typedef void *any_t; /* XXX - obsolete, should be deleted. */
+
+#if defined(TRUE)
+#else /* not defined(TRUE) */
+#define TRUE 1
+#define FALSE 0
+#endif
+
+/* Enable mutex holder debugging */
+/* #define WAIT_DEBUG */
+/* Record function name instead of thread pointer */
+/* #define WAIT_FUNC_DEBUG */
+
+/*
+ * C Threads package initialization.
+ */
+
+extern vm_offset_t cthread_init(void);
+
+
+/*
+ * Queues.
+ */
+typedef struct cthread_queue {
+ struct cthread_queue_item *head;
+ struct cthread_queue_item *tail;
+} *cthread_queue_t;
+
+typedef struct cthread_queue_item {
+ struct cthread_queue_item *next;
+} *cthread_queue_item_t;
+
+#define NO_QUEUE_ITEM ((cthread_queue_item_t) 0)
+
+#define QUEUE_INITIALIZER { NO_QUEUE_ITEM, NO_QUEUE_ITEM }
+
+#define cthread_queue_alloc() ((cthread_queue_t) calloc(1, sizeof(struct cthread_queue)))
+#define cthread_queue_init(q) ((q)->head = (q)->tail = 0)
+#define cthread_queue_free(q) free((q))
+
+#define cthread_queue_enq(q, x) \
+ MACRO_BEGIN \
+ (x)->next = 0; \
+ if ((q)->tail == 0) \
+ (q)->head = (cthread_queue_item_t) (x); \
+ else \
+ (q)->tail->next = (cthread_queue_item_t) (x); \
+ (q)->tail = (cthread_queue_item_t) (x); \
+ MACRO_END
+
+#define cthread_queue_preq(q, x) \
+ MACRO_BEGIN \
+ if ((q)->tail == 0) \
+ (q)->tail = (cthread_queue_item_t) (x); \
+ ((cthread_queue_item_t) (x))->next = (q)->head; \
+ (q)->head = (cthread_queue_item_t) (x); \
+ MACRO_END
+
+#define cthread_queue_head(q, t) ((t) ((q)->head))
+
+#define cthread_queue_deq(q, t, x) \
+ MACRO_BEGIN \
+ if (((x) = (t) ((q)->head)) != 0 && \
+ ((q)->head = (cthread_queue_item_t) ((x)->next)) == 0) \
+ (q)->tail = 0; \
+ MACRO_END
+
+#define cthread_queue_map(q, t, f) \
+ MACRO_BEGIN \
+ register cthread_queue_item_t x, next; \
+ for (x = (cthread_queue_item_t) ((q)->head); x != 0; x = next){\
+ next = x->next; \
+ (*(f))((t) x); \
+ } \
+ MACRO_END
+
+#if 1
+
+/* In GNU, spin locks are implemented in libc.
+ Just include its header file. */
+#include <spin-lock.h>
+
+#else /* Unused CMU code. */
+
+/*
+ * Spin locks.
+ */
+extern void spin_lock_solid(spin_lock_t *_lock);
+
+#if defined(spin_unlock)
+#else /* not defined(spin_unlock) */
+extern void spin_unlock(spin_lock_t *_lock);
+#endif
+
+#if defined(spin_try_lock)
+#else /* not defined(spin_try_lock) */
+extern boolean_t spin_try_lock(spin_lock_t *_lock);
+#endif
+
+#define spin_lock(p) \
+ MACRO_BEGIN \
+ if (!spin_try_lock(p)) { \
+ spin_lock_solid(p); \
+ } \
+ MACRO_END
+
+#endif /* End unused CMU code. */
+
+/*
+ * Mutex objects.
+ */
+typedef struct mutex {
+ /* The `held' member must be first in GNU. The GNU C library relies on
+ being able to cast a `struct mutex *' to a `spin_lock_t *' (which is
+ kosher if it is the first member) and spin_try_lock that address to
+ see if it gets the mutex. */
+ spin_lock_t held;
+ spin_lock_t lock;
+ const char *name;
+ struct cthread_queue queue;
+ /* holder is for WAIT_DEBUG. Not ifdeffed to keep size constant. */
+#ifdef WAIT_FUNC_DEBUG
+ const char *fname;
+#else /* WAIT_FUNC_DEBUG */
+ struct cthread *holder;
+#endif /* WAIT_FUNC_DEBUG */
+} *mutex_t;
+
+#ifdef WAIT_DEBUG
+#ifdef WAIT_FUNC_DEBUG
+#define WAIT_CLEAR_DEBUG(m) (m)->fname = 0
+#define WAIT_SET_DEBUG(m) (m)->fname = __FUNCTION__
+#else /* WAIT_FUNC_DEBUG */
+#define WAIT_CLEAR_DEBUG(m) (m)->holder = 0
+#define WAIT_SET_DEBUG(m) (m)->holder = cthread_self()
+#endif /* WAIT_FUNC_DEBUG */
+#else /* WAIT_DEBUG */
+#define WAIT_CLEAR_DEBUG(m) (void) 0
+#define WAIT_SET_DEBUG(m) (void) 0
+#endif /* WAIT_DEBUG */
+
+/* Rearranged accordingly for GNU: */
+#define MUTEX_INITIALIZER { SPIN_LOCK_INITIALIZER, SPIN_LOCK_INITIALIZER, 0, QUEUE_INITIALIZER, }
+#define MUTEX_NAMED_INITIALIZER(Name) { SPIN_LOCK_INITIALIZER, SPIN_LOCK_INITIALIZER, Name, QUEUE_INITIALIZER, }
+
+#define mutex_alloc() ((mutex_t) calloc(1, sizeof(struct mutex)))
+#define mutex_init(m) \
+ MACRO_BEGIN \
+ spin_lock_init(&(m)->lock); \
+ cthread_queue_init(&(m)->queue); \
+ spin_lock_init(&(m)->held); \
+ WAIT_CLEAR_DEBUG(m); \
+ MACRO_END
+#define mutex_set_name(m, x) ((m)->name = (x))
+#define mutex_name(m) ((m)->name != 0 ? (m)->name : "?")
+#define mutex_clear(m) mutex_init(m)
+#define mutex_free(m) free((m))
+
+#define mutex_try_lock(m) (spin_try_lock(&(m)->held) ? WAIT_SET_DEBUG(m), 1 : 0)
+#define mutex_lock(m) \
+ MACRO_BEGIN \
+ if (!spin_try_lock(&(m)->held)) { \
+ __mutex_lock_solid(m); \
+ } \
+ WAIT_SET_DEBUG(m); \
+ MACRO_END
+#define mutex_unlock(m) \
+ MACRO_BEGIN \
+ if (spin_unlock(&(m)->held), \
+ cthread_queue_head(&(m)->queue, vm_offset_t) != 0) { \
+ __mutex_unlock_solid(m); \
+ } \
+ WAIT_CLEAR_DEBUG(m); \
+ MACRO_END
+/*
+ * Condition variables.
+ */
+typedef struct condition {
+ spin_lock_t lock;
+ struct cthread_queue queue;
+ const char *name;
+ struct cond_imp *implications;
+} *condition_t;
+
+struct cond_imp
+{
+ struct condition *implicatand;
+ struct cond_imp *next;
+};
+
+#define CONDITION_INITIALIZER { SPIN_LOCK_INITIALIZER, QUEUE_INITIALIZER, 0, 0 }
+#define CONDITION_NAMED_INITIALIZER(Name) { SPIN_LOCK_INITIALIZER, QUEUE_INITIALIZER, Name, 0 }
+
+#define condition_alloc() \
+ ((condition_t) calloc(1, sizeof(struct condition)))
+#define condition_init(c) \
+ MACRO_BEGIN \
+ spin_lock_init(&(c)->lock); \
+ cthread_queue_init(&(c)->queue); \
+ (c)->name = 0; \
+ (c)->implications = 0; \
+ MACRO_END
+#define condition_set_name(c, x) ((c)->name = (x))
+#define condition_name(c) ((c)->name != 0 ? (c)->name : "?")
+#define condition_clear(c) \
+ MACRO_BEGIN \
+ condition_broadcast(c); \
+ spin_lock(&(c)->lock); \
+ MACRO_END
+#define condition_free(c) \
+ MACRO_BEGIN \
+ condition_clear(c); \
+ free((c)); \
+ MACRO_END
+
+#define condition_signal(c) \
+ MACRO_BEGIN \
+ if ((c)->queue.head || (c)->implications) { \
+ cond_signal(c); \
+ } \
+ MACRO_END
+
+#define condition_broadcast(c) \
+ MACRO_BEGIN \
+ if ((c)->queue.head || (c)->implications) { \
+ cond_broadcast(c); \
+ } \
+ MACRO_END
+
+extern int cond_signal(condition_t _cond);
+
+extern void cond_broadcast(condition_t _cond);
+
+extern void condition_wait(condition_t _cond, mutex_t _mutex);
+extern int hurd_condition_wait(condition_t _cond, mutex_t _mutex);
+
+extern void condition_implies(condition_t _implicator,
+ condition_t _implicatand);
+extern void condition_unimplies(condition_t _implicator,
+ condition_t _implicatand);
+
+/*
+ * Threads.
+ */
+
+typedef void * (*cthread_fn_t)(void *arg);
+
+#include <setjmp.h>
+
+typedef struct cthread {
+ struct cthread *next;
+ struct mutex lock;
+ struct condition done;
+ int state;
+ jmp_buf catch_exit;
+ cthread_fn_t func;
+ void *arg;
+ void *result;
+ const char *name;
+ void *data;
+ void *ldata;
+ void *private_data;
+ struct ur_cthread *ur;
+} *cthread_t;
+
+#define NO_CTHREAD ((cthread_t) 0)
+
+extern cthread_t cthread_fork(cthread_fn_t _func, void *_arg);
+
+extern void cthread_detach(cthread_t _thread);
+
+extern any_t cthread_join(cthread_t _thread);
+
+extern void cthread_yield(void);
+
+extern void cthread_exit(void *_result);
+
+/*
+ * This structure must agree with struct cproc in cthread_internals.h
+ */
+typedef struct ur_cthread {
+ struct ur_cthread *next;
+ cthread_t incarnation;
+} *ur_cthread_t;
+
+#ifndef cthread_sp
+extern vm_offset_t
+cthread_sp(void);
+#endif
+
+extern vm_offset_t cthread_stack_mask;
+
+#if defined(STACK_GROWTH_UP)
+#define ur_cthread_ptr(sp) \
+ (* (ur_cthread_t *) ((sp) & cthread_stack_mask))
+#else /* not defined(STACK_GROWTH_UP) */
+#define ur_cthread_ptr(sp) \
+ (* (ur_cthread_t *) ( ((sp) | cthread_stack_mask) + 1 \
+ - sizeof(ur_cthread_t *)) )
+#endif /* defined(STACK_GROWTH_UP) */
+
+#define ur_cthread_self() (ur_cthread_ptr(cthread_sp()))
+
+#define cthread_assoc(id, t) ((((ur_cthread_t) (id))->incarnation = (t)), \
+ ((t) ? ((t)->ur = (ur_cthread_t)(id)) : 0))
+#define cthread_self() (ur_cthread_self()->incarnation)
+
+extern void cthread_set_name(cthread_t _thread, const char *_name);
+
+extern const char * cthread_name(cthread_t _thread);
+
+extern int cthread_count(void);
+
+extern void cthread_set_limit(int _limit);
+
+extern int cthread_limit(void);
+
+extern void cthread_set_kernel_limit(int _n);
+
+extern int cthread_kernel_limit(void);
+
+extern void cthread_wire(void);
+
+extern void cthread_unwire(void);
+
+extern void cthread_msg_busy(mach_port_t _port, int _min, int _max);
+
+extern void cthread_msg_active(mach_port_t _prt, int _min, int _max);
+
+extern mach_msg_return_t cthread_mach_msg(mach_msg_header_t *_header,
+ mach_msg_option_t _option,
+ mach_msg_size_t _send_size,
+ mach_msg_size_t _rcv_size,
+ mach_port_t _rcv_name,
+ mach_msg_timeout_t _timeout,
+ mach_port_t _notify,
+ int _min, int _max);
+
+extern void cthread_fork_prepare(void);
+
+extern void cthread_fork_parent(void);
+
+extern void cthread_fork_child(void);
+
+#if defined(THREAD_CALLS)
+/*
+ * Routines to replace thread_*.
+ */
+extern kern_return_t cthread_get_state(cthread_t _thread);
+
+extern kern_return_t cthread_set_state(cthread_t _thread);
+
+extern kern_return_t cthread_abort(cthread_t _thread);
+
+extern kern_return_t cthread_resume(cthread_t _thread);
+
+extern kern_return_t cthread_suspend(cthread_t _thread);
+
+extern kern_return_t cthread_call_on(cthread_t _thread);
+#endif /* defined(THREAD_CALLS) */
+
+#if defined(CTHREAD_DATA_XX)
+/*
+ * Set or get thread specific "global" variable
+ *
+ * The thread given must be the calling thread (ie. thread_self).
+ * XXX This is for compatibility with the old cthread_data. XXX
+ */
+extern int cthread_set_data(cthread_t _thread, void *_val);
+
+extern void * cthread_data(cthread_t _thread);
+#else /* defined(CTHREAD_DATA_XX) */
+
+#define cthread_set_data(_thread, _val) ((_thread)->data) = (void *)(_val);
+#define cthread_data(_thread) ((_thread)->data)
+
+#define cthread_set_ldata(_thread, _val) ((_thread)->ldata) = (void *)(_val);
+#define cthread_ldata(_thread) ((_thread)->ldata)
+
+#endif /* defined(CTHREAD_DATA_XX) */
+
+
+/*
+ * Support for POSIX thread specific data
+ *
+ * Multiplexes a thread specific "global" variable
+ * into many thread specific "global" variables.
+ */
+#define CTHREAD_DATA_VALUE_NULL (void *)0
+#define CTHREAD_KEY_INVALID (cthread_key_t)-1
+
+typedef int cthread_key_t;
+
+/*
+ * Create key to private data visible to all threads in task.
+ * Different threads may use same key, but the values bound to the key are
+ * maintained on a thread specific basis.
+ */
+extern int cthread_keycreate(cthread_key_t *_key);
+
+/*
+ * Get value currently bound to key for calling thread
+ */
+extern int cthread_getspecific(cthread_key_t _key, void **_value);
+
+/*
+ * Bind value to given key for calling thread
+ */
+extern int cthread_setspecific(cthread_key_t _key, void *_value);
+
+/*
+ * Debugging support.
+ */
+#if defined(DEBUG)
+
+#if defined(ASSERT)
+#else /* not defined(ASSERT) */
+/*
+ * Assertion macro, similar to <assert.h>
+ */
+#include <stdio.h>
+#define ASSERT(p) \
+ MACRO_BEGIN \
+ if (!(p)) { \
+ fprintf(stderr, \
+ "File %s, line %d: assertion p failed.\n", \
+ __FILE__, __LINE__); \
+ abort(); \
+ } \
+ MACRO_END
+
+#endif /* defined(ASSERT) */
+
+#define SHOULDNT_HAPPEN 0
+
+extern int cthread_debug;
+
+#else /* not defined(DEBUG) */
+
+#if defined(ASSERT)
+#else /* not defined(ASSERT) */
+#define ASSERT(p)
+#endif /* defined(ASSERT) */
+
+#endif /* defined(DEBUG) */
+
+#endif /* not defined(_CTHREADS_) */
diff --git a/libthreads/i386/csw.S b/libthreads/i386/csw.S
new file mode 100644
index 00000000..69c93652
--- /dev/null
+++ b/libthreads/i386/csw.S
@@ -0,0 +1,194 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * $Log: csw.S,v $
+ * Revision 1.9 1998/07/20 06:58:28 roland
+ * 1998-07-20 Roland McGrath <roland@baalperazim.frob.com>
+ *
+ * * i386/csw.S (cproc_prepare): Take address of cthread_body as third
+ * arg, so we don't have to deal with PIC magic to find its address
+ * without producing a text reloc.
+ * * cprocs.c (cproc_create): Pass &cthread_body to cproc_prepare.
+ *
+ * Revision 1.8 1997/04/04 01:31:16 thomas
+ * Thu Apr 3 20:29:27 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+ *
+ * * i386/csw.S: Define __ELF__ too.
+ *
+ * Revision 1.7 1996/10/24 19:30:10 thomas
+ * Mon Oct 21 22:05:48 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+ *
+ * * i386/csw.S (CALL_MCOUNT): New macro.
+ * (cproc_swtich, cproc_start_wait, cproc_prepare): Use CALL_MCOUNT.
+ *
+ * Revision 1.6 1996/08/29 17:44:42 thomas
+ * *** empty log message ***
+ *
+ * Revision 1.5 1995/10/04 20:55:28 roland
+ * (JUMPTARGET): New macro, versions for [PIC] and not.
+ * Use it in place of EXT.
+ *
+ * Revision 1.4 1995/10/04 20:22:17 roland
+ * [PIC] (EXT): Redefine to use PLT.
+ *
+ * Revision 1.3 1995/05/12 18:35:55 roland
+ * Use EXT macro instead of explicit underscores.
+ *
+# Revision 1.2 1994/05/04 19:01:50 mib
+# entered into RCS
+#
+ * Revision 2.7 91/07/31 18:36:32 dbg
+ * Fix for ANSI C preprocessor.
+ * [91/07/30 17:35:16 dbg]
+ *
+ * Revision 2.6 91/05/14 17:56:56 mrt
+ * Correcting copyright
+ *
+ * Revision 2.5 91/05/08 13:35:49 dbg
+ * Unlock lock with a locked instruction (xchg).
+ * [91/03/20 dbg]
+ *
+ * Revision 2.4 91/02/14 14:20:02 mrt
+ * Changed to new Mach copyright
+ * [91/02/13 12:15:27 mrt]
+ *
+ * Revision 2.3 91/01/08 16:46:20 rpd
+ * Don't use Times - horta doesn't like it for some reason.
+ * [91/01/06 rpd]
+ *
+ * Revision 2.2 90/05/03 15:54:37 dbg
+ * Created.
+ * [90/02/05 dbg]
+ *
+ */
+#define ELF
+#undef __ELF__
+#define __ELF__ 1
+#include <mach/i386/asm.h>
+
+#ifdef PIC
+#define JUMPTARGET(name) EXT(name##@PLT)
+#else
+#define JUMPTARGET(name) EXT(name)
+#endif
+
+#ifdef PROF
+#define CALL_MCOUNT \
+ pushl %ebp; movl %esp, %ebp; call JUMPTARGET(mcount); popl %ebp;
+#else
+#define CALL_MCOUNT
+#endif
+
+
+/*
+ * Suspend the current thread and resume the next one.
+ *
+ * void cproc_switch(int *cur, int *next, int *lock)
+ */
+ENTRY(cproc_switch)
+ CALL_MCOUNT
+ pushl %ebp /* save ebp */
+ movl %esp,%ebp /* set frame pointer to get arguments */
+ pushl %ebx /* save ebx */
+ pushl %esi /* esi */
+ pushl %edi /* edi */
+ movl B_ARG0,%eax /* get cur */
+ movl %esp,(%eax) /* save current esp */
+ movl B_ARG2,%edx /* get address of lock before switching */
+ /* stacks */
+ movl B_ARG1,%eax /* get next */
+ movl (%eax),%esp /* get new stack pointer */
+ xorl %eax,%eax /* unlock */
+ xchgl %eax,(%edx) /* the lock - now old thread can run */
+
+ popl %edi /* restore di */
+ popl %esi /* si */
+ popl %ebx /* bx */
+ popl %ebp /* and bp (don`t use "leave" - bp */
+ /* still points to old stack) */
+ ret
+
+/*
+ * Create a new stack frame for a 'waiting' thread,
+ * save current thread's frame, and switch to waiting thread.
+ *
+ * void cproc_start_wait(int *cur,
+ * cproc_t child,
+ * int stackp,
+ * int *lock)
+ */
+ENTRY(cproc_start_wait)
+ CALL_MCOUNT
+ pushl %ebp /* save ebp */
+ movl %esp,%ebp /* set frame pointer */
+ pushl %ebx /* save ebx */
+ pushl %esi /* esi */
+ pushl %edi /* edi */
+ movl B_ARG0,%eax /* get cur */
+ movl %esp,(%eax) /* save current esp */
+ movl B_ARG1,%eax /* get child thread */
+ movl B_ARG3,%edx /* point to lock before switching stack */
+ movl B_ARG2,%esp /* get new stack */
+ pushl %eax /* push child thread as argument */
+ movl $0,%ebp /* (clear frame pointer) */
+ xorl %eax,%eax /* unlock */
+ xchgl %eax,(%edx) /* the lock - now old thread can run */
+ call JUMPTARGET(cproc_waiting)/* call cproc_waiting */
+ /*NOTREACHED*/
+
+/*
+ * Set up a thread's stack so that when cproc_switch switches to
+ * it, it will start up as if it called
+ * cproc_body(child)
+ *
+ * void cproc_prepare(cproc_t child, int *context, int stack,
+ * void (*cthread_body)(cproc_t));
+ */
+ENTRY(cproc_prepare)
+ CALL_MCOUNT
+ pushl %ebp /* save ebp */
+ movl %esp,%ebp /* set frame pointer */
+ movl B_ARG2,%edx /* get child`s stack */
+ subl $28,%edx
+ /* make room for context: */
+ /* 0 saved edi () */
+ /* 4 saved esi () */
+ /* 8 saved ebx () */
+ /* 12 saved ebp () */
+ /* 16 return PC from cproc_switch */
+ /* 20 return PC from cthread_body */
+ /* 24 argument to cthread_body */
+ movl $0,12(%edx) /* clear frame pointer */
+ movl B_ARG3,%ecx /* get address of cthread_body passed in */
+ movl %ecx,16(%edx) /* set child to resume at cthread_body */
+ movl $0,20(%edx) /* fake return address from cthread_body */
+ movl B_ARG0,%ecx /* get child thread pointer */
+ movl %ecx,24(%edx) /* set as argument to cthread_body */
+ movl B_ARG1,%ecx /* get pointer to context */
+ movl %edx,(%ecx) /* save context */
+ leave
+ ret
diff --git a/libthreads/i386/cthread_inline.awk b/libthreads/i386/cthread_inline.awk
new file mode 100644
index 00000000..8e0cb7d4
--- /dev/null
+++ b/libthreads/i386/cthread_inline.awk
@@ -0,0 +1,86 @@
+#
+# Mach Operating System
+# Copyright (c) 1991,1990 Carnegie Mellon University
+# All Rights Reserved.
+#
+# Permission to use, copy, modify and distribute this software and its
+# documentation is hereby granted, provided that both the copyright
+# notice and this permission notice appear in all copies of the
+# software, derivative works or modified versions, and any portions
+# thereof, and that both notices appear in supporting documentation.
+#
+# CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+# CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+# ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+#
+# Carnegie Mellon requests users of this software to return to
+#
+# Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+# School of Computer Science
+# Carnegie Mellon University
+# Pittsburgh PA 15213-3890
+#
+# any improvements or extensions that they make and grant Carnegie Mellon
+# the rights to redistribute these changes.
+#
+#
+# HISTORY
+# $Log: cthread_inline.awk,v $
+# Revision 2.5 91/05/14 17:57:03 mrt
+# Correcting copyright
+#
+# Revision 2.4 91/05/08 13:36:05 dbg
+# Unlock lock with a locked instruction (xchg).
+# [91/03/20 dbg]
+#
+# Revision 2.3 91/02/14 14:20:06 mrt
+# Added new Mach copyright
+# [91/02/13 12:33:05 mrt]
+#
+# Revision 2.2 90/05/03 15:54:56 dbg
+# Created (from 68020 version).
+# [90/02/05 dbg]
+#
+# Revision 2.2 89/12/08 19:54:30 rwd
+# Inlines are now spins instead of mutexes.
+# [89/10/23 rwd]
+#
+# Revision 2.1 89/08/04 15:15:14 rwd
+# Created.
+#
+# Revision 1.3 89/05/05 19:00:33 mrt
+# Cleanup for Mach 2.5
+#
+#
+
+# sun/cthread_inline.awk
+#
+# Awk script to inline critical C Threads primitives on i386
+
+NF == 2 && $1 == "call" && $2 == "_spin_try_lock" {
+ print "/ BEGIN INLINE spin_try_lock"
+ print " movl (%esp),%ecx / point at mutex"
+ print " movl $1,%eax / set locked value in acc"
+ print " xchg %eax,(%ecx) / locked swap with mutex"
+ print " xorl $1,%eax / logical complement"
+ print "/ END INLINE spin_try_lock"
+ continue
+}
+NF == 2 && $1 == "call" && $2 == "_spin_unlock" {
+ print "/ BEGIN INLINE " $2
+ print " movl (%esp),%ecx"
+ print " xorl %eax,%eax / set unlocked value in acc"
+ print " xchg %eax,(%ecx) / locked swap with mutex"
+ print "/ END INLINE " $2
+ continue
+}
+NF == 2 && $1 == "call" && $2 == "_cthread_sp" {
+ print "/ BEGIN INLINE cthread_sp"
+ print " movl %esp,%eax"
+ print "/ END INLINE cthread_sp"
+ continue
+}
+# default:
+{
+ print
+}
diff --git a/libthreads/i386/cthreads.h b/libthreads/i386/cthreads.h
new file mode 100644
index 00000000..694387b6
--- /dev/null
+++ b/libthreads/i386/cthreads.h
@@ -0,0 +1,117 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993,1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * $Log: cthreads.h,v $
+ * Revision 1.3 2007/03/03 23:57:37 sthibaul
+ * 2006-03-04 Samuel Thibault <samuel.thibault@ens-lyon.org>
+ *
+ * * libpthread/sysdeps/i386/machine-sp.h (thread_stack_pointer):
+ * Optimize esp read.
+ * * libpthread/i386/cthreads.h (cthread_sp): Likewise.
+ *
+ * Revision 1.2 2002/05/27 02:50:10 roland
+ * 2002-05-26 Roland McGrath <roland@frob.com>
+ *
+ * Changes merged from CMU MK83a version:
+ * * cthreads.h, options.h: Various cleanups.
+ * * call.c, cthread_data.c, sync.c, mig_support.c: Likewise.
+ * * i386/cthreads.h, i386/thread.c, i386/lock.s: Likewise.
+ * * cthread_internals.h: Add decls for internal functions.
+ * (struct cproc): Use vm_offset_t for stack_base and stack_size members.
+ * Use natural_t for context member.
+ * * cprocs.c: Use prototypes for all defns.
+ * * cthreads.c: Likewise.
+ * (cthread_exit): Cast any_t to integer_t before int.
+ *
+ * Revision 2.9 93/01/24 13:24:58 danner
+ * Move ! in spin_try_lock to give the compiler
+ * a fighting chance.
+ * [92/11/19 rvb]
+ *
+ * Revision 2.8 93/01/14 18:05:09 danner
+ * asm -> __asm__
+ * [93/01/10 danner]
+ *
+ * Revision 2.7 92/01/03 20:36:59 dbg
+ * Add volatile to spin_lock_t. Change spin_unlock and
+ * spin_try_lock definitions back to memory operands, but rely on
+ * volatile attribute to keep from using value in memory.
+ * [91/09/04 dbg]
+ *
+ * Revision 2.6 91/08/28 20:18:39 jsb
+ * Safer definitions for spin_unlock and spin_try_lock from mib.
+ *
+ * Revision 2.5 91/07/31 18:36:49 dbg
+ * Add inline substitution for cthread_sp, spin_unlock,
+ * spin_try_lock.
+ * [91/07/30 17:35:53 dbg]
+ *
+ * Revision 2.4 91/05/14 17:57:11 mrt
+ * Correcting copyright
+ *
+ * Revision 2.3 91/02/14 14:20:14 mrt
+ * Changed to new Mach copyright
+ * [91/02/13 12:20:00 mrt]
+ *
+ * Revision 2.2 90/11/05 14:37:23 rpd
+ * Created.
+ * [90/11/01 rwd]
+ *
+ *
+ */
+
+#ifndef _MACHINE_CTHREADS_H_
+#define _MACHINE_CTHREADS_H_
+
+typedef volatile int spin_lock_t;
+#define SPIN_LOCK_INITIALIZER 0
+#define spin_lock_init(s) (*(s) = 0)
+#define spin_lock_locked(s) (*(s) != 0)
+
+#ifdef __GNUC__
+
+#define spin_unlock(p) \
+ ({ register int _u__ ; \
+ __asm__ volatile("xorl %0, %0; \n\
+ xchgl %0, %1" \
+ : "=&r" (_u__), "=m" (*(p)) :: "memory" ); \
+ 0; })
+
+#define spin_try_lock(p)\
+ (!({ boolean_t _r__; \
+ __asm__ volatile("movl $1, %0; \n\
+ xchgl %0, %1" \
+ : "=&r" (_r__), "=m" (*(p)) :: "memory" ); \
+ _r__; }))
+
+#define cthread_sp() \
+ ({ register int _sp__ asm("esp"); \
+ _sp__; })
+
+#endif /* __GNUC__ */
+
+#endif _MACHINE_CTHREADS_H_
diff --git a/libthreads/i386/lock.s b/libthreads/i386/lock.s
new file mode 100644
index 00000000..f064e5bb
--- /dev/null
+++ b/libthreads/i386/lock.s
@@ -0,0 +1,74 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * $Log: lock.s,v $
+ * Revision 2.6 93/05/10 17:51:38 rvb
+ * Use C Comment
+ * [93/05/04 18:14:05 rvb]
+ *
+ * Revision 2.5 91/05/14 17:57:20 mrt
+ * Correcting copyright
+ *
+ * Revision 2.4 91/05/08 13:36:15 dbg
+ * Unlock lock with a locked instruction (xchg).
+ * [91/03/20 dbg]
+ *
+ * Revision 2.3 91/02/14 14:20:18 mrt
+ * Changed to new Mach copyright
+ * [91/02/13 12:20:06 mrt]
+ *
+ * Revision 2.2 90/05/03 15:54:59 dbg
+ * Created.
+ * [90/02/05 dbg]
+ *
+ */
+
+#include <i386/asm.h>
+
+/*
+ * boolean_t spin_try_lock(int *m)
+ */
+ENTRY(spin_try_lock)
+ movl 4(%esp),%ecx /* point at mutex */
+ movl $1,%eax /* set locked value in acc */
+ xchg %eax,(%ecx) /* swap with mutex */
+ /* xchg with memory is automatically */
+ /* locked */
+ xorl $1,%eax /* 1 (locked) => FALSE */
+ /* 0 (locked) => TRUE */
+ ret
+
+/*
+ * void spin_unlock(int *m)
+ */
+ENTRY(spin_unlock)
+ movl 4(%esp),%ecx /* point at mutex */
+ xorl %eax,%eax /* set unlocked value in acc */
+ xchg %eax,(%ecx) /* swap with mutex */
+ /* xchg with memory is automatically */
+ /* locked */
+ ret
diff --git a/libthreads/i386/thread.c b/libthreads/i386/thread.c
new file mode 100644
index 00000000..00537be5
--- /dev/null
+++ b/libthreads/i386/thread.c
@@ -0,0 +1,135 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992,1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * $Log: thread.c,v $
+ * Revision 2.8 93/02/02 21:54:58 mrt
+ * Changed include of mach/mach.h to mach.h.
+ * [93/02/02 mrt]
+ *
+ * Revision 2.7 93/01/14 18:05:15 danner
+ * Converted file to ANSI C.
+ * Fixed argument types.
+ * [92/12/18 pds]
+ *
+ * Revision 2.6 91/07/31 18:37:07 dbg
+ * Undefine cthread_sp macro around function definition.
+ * [91/07/30 17:36:23 dbg]
+ *
+ * Revision 2.3 90/06/02 15:13:53 rpd
+ * Added definition of cthread_sp.
+ * [90/06/02 rpd]
+ *
+ * Revision 2.2 90/05/03 15:55:03 dbg
+ * Created (from 68020 version).
+ * [90/02/05 dbg]
+ *
+ */
+/*
+ * i386/thread.c
+ *
+ */
+
+#ifndef lint
+char rcs_id[] = "$Header: cvs-sans-libpthread/hurd/libthreads/i386/thread.c,v 1.7 2002/05/27 02:50:10 roland Exp $";
+#endif /* not lint */
+
+
+#include <cthreads.h>
+#include "cthread_internals.h"
+#include <mach.h>
+#include <mach/i386/mach_i386.h>
+#include <mach/mig_errors.h>
+
+#define HURD_TLS_DESC_DECL(desc, tcb) \
+ struct descriptor desc = \
+ { /* low word: */ \
+ 0xffff /* limit 0..15 */ \
+ | (((unsigned int) (tcb)) << 16) /* base 0..15 */ \
+ , /* high word: */ \
+ ((((unsigned int) (tcb)) >> 16) & 0xff) /* base 16..23 */ \
+ | ((0x12 | 0x60 | 0x80) << 8) /* access = ACC_DATA_W|ACC_PL_U|ACC_P */ \
+ | (0xf << 16) /* limit 16..19 */ \
+ | ((4 | 8) << 20) /* granularity = SZ_32|SZ_G */ \
+ | (((unsigned int) (tcb)) & 0xff000000) /* base 24..31 */ \
+ }
+
+/*
+ * Set up the initial state of a MACH thread
+ * so that it will invoke cthread_body(child)
+ * when it is resumed.
+ */
+void
+cproc_setup(register cproc_t child, thread_t thread, tcbhead_t *tcb, void (*routine)(cproc_t))
+{
+ extern unsigned int __hurd_threadvar_max; /* GNU */
+ register int *top = (int *)
+ cproc_stack_base (child,
+ sizeof(ur_cthread_t *) +
+ /* Account for GNU per-thread variables. */
+ __hurd_threadvar_max *
+ sizeof (long int));
+ struct i386_thread_state state;
+ register struct i386_thread_state *ts = &state;
+ kern_return_t r;
+ unsigned int count;
+ HURD_TLS_DESC_DECL(desc, tcb);
+ int sel;
+
+ /*
+ * Set up i386 call frame and registers.
+ * Read registers first to get correct segment values.
+ */
+ count = i386_THREAD_STATE_COUNT;
+ MACH_CALL(thread_get_state(thread,i386_REGS_SEGS_STATE,(thread_state_t) &state,&count),r);
+
+ ts->eip = (int) routine;
+ *--top = (int) child; /* argument to function */
+ *--top = 0; /* fake return address */
+ ts->uesp = (int) top; /* set stack pointer */
+ ts->ebp = 0; /* clear frame pointer */
+
+ asm ("mov %%gs, %w0" : "=q" (sel) : "0" (0));
+ tcb->tcb = tcb;
+ tcb->self = thread;
+ if (__builtin_expect (sel, 0x48) & 4) /* LDT selector */
+ __i386_set_ldt (thread, sel, &desc, 1);
+ else
+ __i386_set_gdt (thread, &sel, desc);
+ ts->gs = sel;
+
+ MACH_CALL(thread_set_state(thread,i386_REGS_SEGS_STATE,(thread_state_t) &state,i386_THREAD_STATE_COUNT),r);
+}
+
+#if defined(cthread_sp)
+#undef cthread_sp
+#endif
+
+int
+cthread_sp(void)
+{
+ return (int) __thread_stack_pointer ();
+}
diff --git a/libthreads/libthreads.map b/libthreads/libthreads.map
new file mode 100644
index 00000000..1a041f71
--- /dev/null
+++ b/libthreads/libthreads.map
@@ -0,0 +1,27 @@
+/* This is the version script file used for building libthreads.so. -*- C -*-
+ It is in the form of a linker script, to be including as an input
+ file in the link command, rather than with --version-script. */
+
+/* We only use this file when we build against a libio-using libc,
+ which we presume will be 2.2 that is expecting our lockfile.c
+ hooks. Therefore the flockfile functions are added to the
+ GLIBC_2.2.6 version node. The rest of the library's symbols will use
+ HURD_CTHREADS_0.3. */
+
+VERSION
+{
+ GLIBC_2.2.6
+ {
+ global:
+ _IO_flockfile; _IO_funlockfile; _IO_ftrylockfile;
+ flockfile; funlockfile; ftrylockfile;
+ local:
+ _cthreads_flockfile; _cthreads_funlockfile; _cthreads_ftrylockfile;
+ };
+
+ HURD_CTHREADS_0.3
+ {
+ global:
+ *;
+ };
+};
diff --git a/libthreads/lockfile.c b/libthreads/lockfile.c
new file mode 100644
index 00000000..eeb1a2f3
--- /dev/null
+++ b/libthreads/lockfile.c
@@ -0,0 +1,65 @@
+/* lockfile - Handle locking and unlocking of streams. Hurd cthreads version.
+ Copyright (C) 2000,01,02 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <cthreads.h> /* Must come before <stdio.h>! */
+#include <stdio.h>
+
+
+#ifdef _STDIO_USES_IOSTREAM
+
+void
+_cthreads_flockfile (_IO_FILE *fp)
+{
+ _IO_lock_lock (*fp->_lock);
+}
+
+void
+_cthreads_funlockfile (_IO_FILE *fp)
+{
+ _IO_lock_unlock (*fp->_lock);
+}
+
+int
+_cthreads_ftrylockfile (_IO_FILE *fp)
+{
+ return __libc_lock_trylock_recursive (*fp->_lock);
+}
+
+# undef _IO_flockfile
+# undef _IO_funlockfile
+# undef _IO_ftrylockfile
+# undef flockfile
+# undef funlockfile
+# undef ftrylockfile
+
+void _IO_flockfile (_IO_FILE *)
+ __attribute__ ((alias ("_cthreads_flockfile")));
+void _IO_funlockfile (_IO_FILE *)
+ __attribute__ ((alias ("_cthreads_funlockfile")));
+int _IO_ftrylockfile (_IO_FILE *)
+ __attribute__ ((alias ("_cthreads_ftrylockfile")));
+
+void flockfile (_IO_FILE *)
+ __attribute__ ((alias ("_cthreads_flockfile")));
+void funlockfile (_IO_FILE *)
+ __attribute__ ((alias ("_cthreads_funlockfile")));
+int ftrylockfile (_IO_FILE *)
+ __attribute__ ((alias ("_cthreads_ftrylockfile")));
+
+#endif /* _STDIO_USES_IOSTREAM */
diff --git a/libthreads/mig_support.c b/libthreads/mig_support.c
new file mode 100644
index 00000000..cd0d4124
--- /dev/null
+++ b/libthreads/mig_support.c
@@ -0,0 +1,234 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * 26-Feb-94 Johannes Helander (jvh) at Helsinki University of Technology
+ * Added mach_put_reply_port. mig_dealloc_reply_port now takes the
+ * port as an argument.
+ *
+ * $Log: mig_support.c,v $
+ * Revision 2.8 93/01/24 13:27:14 danner
+ * Corrrected include of mach/mach.h to mach.h
+ * [93/01/16 mrt]
+ *
+ * Revision 2.7 93/01/14 18:05:37 danner
+ * Converted file to ANSI C.
+ * Made argument to mig_init a void * for compatibility with
+ * mig_init in libmach.
+ * [92/12/18 pds]
+ *
+ * Revision 2.6 91/05/14 17:57:41 mrt
+ * Correcting copyright
+ *
+ * Revision 2.5 91/02/14 14:20:30 mrt
+ * Added new Mach copyright
+ * [91/02/13 12:41:26 mrt]
+ *
+ * Revision 2.4 90/08/07 14:31:41 rpd
+ * Removed RCS keyword nonsense.
+ *
+ * Revision 2.3 90/08/07 14:27:48 rpd
+ * When we recycle the global reply port by giving it to the first
+ * cthread, clear the global reply port. This will take care of
+ * someone accidentally calling this twice.
+ * [90/08/07 rwd]
+ *
+ * Revision 2.2 90/06/02 15:14:04 rpd
+ * Converted to new IPC.
+ * [90/03/20 20:56:50 rpd]
+ *
+ * Revision 2.1 89/08/03 17:09:50 rwd
+ * Created.
+ *
+ * 18-Jan-89 David Golub (dbg) at Carnegie-Mellon University
+ * Replaced task_data() by thread_reply().
+ *
+ *
+ * 27-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed mig_support.c to avoid deadlock that can occur
+ * if tracing is turned on during calls to mig_get_reply_port().
+ *
+ * 10-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed mig_support.c to use MACH_CALL.
+ * Changed "is_init" to "multithreaded" and reversed its sense.
+ *
+ * 30-Jul-87 Mary Thompson (mrt) at Carnegie Mellon University
+ * Created.
+ */
+/*
+ * File: mig_support.c
+ * Author: Mary R. Thompson, Carnegie Mellon University
+ * Date: July, 1987
+ *
+ * Routines to set and deallocate the mig reply port for the current thread.
+ * Called from mig-generated interfaces.
+ *
+ */
+
+
+#include <mach.h>
+#include <mach/mig_support.h>
+#include <mach/mach_traps.h>
+#include <cthreads.h>
+#include "cthread_internals.h"
+
+private boolean_t multithreaded = FALSE;
+/* use a global reply port before becoming multi-threaded */
+private mach_port_t mig_reply_port = MACH_PORT_NULL;
+
+/*
+ * Called by mach_init with 0 before cthread_init is
+ * called and again with initial cproc at end of cthread_init.
+ */
+void
+mig_init(register void *initial)
+{
+ if (initial == NO_CPROC) {
+ /* called from mach_init before cthread_init,
+ possibly after a fork. clear global reply port. */
+
+ multithreaded = FALSE;
+ mig_reply_port = MACH_PORT_NULL;
+ } else {
+ /* recycle global reply port as this cthread's reply port */
+
+ multithreaded = TRUE;
+ ((cproc_t) initial)->reply_port = mig_reply_port;
+ mig_reply_port = MACH_PORT_NULL;
+ }
+}
+
+/*
+ * Called by mig interface code whenever a reply port is needed.
+ */
+mach_port_t
+mig_get_reply_port(void)
+{
+ register mach_port_t reply_port;
+
+ if (multithreaded) {
+ register cproc_t self;
+
+ self = cproc_self();
+ ASSERT(self != NO_CPROC);
+
+ if ((reply_port = self->reply_port) == MACH_PORT_NULL)
+ self->reply_port = reply_port = mach_reply_port();
+ } else {
+ if ((reply_port = mig_reply_port) == MACH_PORT_NULL)
+ mig_reply_port = reply_port = mach_reply_port();
+ }
+
+ return reply_port;
+}
+
+/*
+ * Called by mig interface code after a timeout on the reply port.
+ * May also be called by user.
+ */
+/*ARGSUSED*/
+void
+mig_dealloc_reply_port(mach_port_t p)
+{
+ register mach_port_t reply_port;
+
+ if (multithreaded) {
+ register cproc_t self;
+
+ self = cproc_self();
+ ASSERT(self != NO_CPROC);
+
+ reply_port = self->reply_port;
+ self->reply_port = MACH_PORT_NULL;
+ } else {
+ reply_port = mig_reply_port;
+ mig_reply_port = MACH_PORT_NULL;
+ }
+
+ (void) mach_port_mod_refs(mach_task_self(), reply_port,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+}
+
+/*
+ * Called by mig interfaces when done with a port.
+ * Used to provide the same interface as needed when a custom
+ * allocator is used.
+ */
+
+/*ARGSUSED*/
+void
+mig_put_reply_port(mach_port_t port)
+{
+ /* Do nothing */
+}
+
+void
+__mig_init(register void *initial)
+{
+ mig_init (initial);
+}
+
+mach_port_t
+__mig_get_reply_port(void)
+{
+ return mig_get_reply_port();
+}
+
+void
+__mig_dealloc_reply_port(mach_port_t p)
+{
+ mig_dealloc_reply_port(p);
+}
+
+void
+__mig_put_reply_port(mach_port_t port)
+{
+ mig_put_reply_port(port);
+}
+
+void
+__mig_dealloc_reply_port (void)
+{
+ mig_dealloc_reply_port ();
+}
+
+/* XXX shouldn't need these */
+/* Called by MiG to allocate space. */
+void
+__mig_allocate (vm_address_t *addr,
+ vm_size_t size)
+{
+ if (__vm_allocate (__mach_task_self (), addr, size, 1) != KERN_SUCCESS)
+ *addr = 0;
+}
+
+/* Called by MiG to deallocate space. */
+void
+__mig_deallocate (vm_address_t addr,
+ vm_size_t size)
+{
+ (void) __vm_deallocate (__mach_task_self (), addr, size);
+}
diff --git a/libthreads/options.h b/libthreads/options.h
new file mode 100644
index 00000000..b8074693
--- /dev/null
+++ b/libthreads/options.h
@@ -0,0 +1,104 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * $Log: options.h,v $
+ * Revision 1.2 2002/05/27 02:50:10 roland
+ * 2002-05-26 Roland McGrath <roland@frob.com>
+ *
+ * Changes merged from CMU MK83a version:
+ * * cthreads.h, options.h: Various cleanups.
+ * * call.c, cthread_data.c, sync.c, mig_support.c: Likewise.
+ * * i386/cthreads.h, i386/thread.c, i386/lock.s: Likewise.
+ * * cthread_internals.h: Add decls for internal functions.
+ * (struct cproc): Use vm_offset_t for stack_base and stack_size members.
+ * Use natural_t for context member.
+ * * cprocs.c: Use prototypes for all defns.
+ * * cthreads.c: Likewise.
+ * (cthread_exit): Cast any_t to integer_t before int.
+ *
+ * Revision 2.8 91/05/14 17:58:35 mrt
+ * Correcting copyright
+ *
+ * Revision 2.7 91/02/14 14:21:03 mrt
+ * Added new Mach copyright
+ * [91/02/13 12:41:31 mrt]
+ *
+ * Revision 2.6 90/09/09 14:35:04 rpd
+ * Remove special option , debug_mutex and thread_calls.
+ * [90/08/24 rwd]
+ *
+ * Revision 2.5 90/06/02 15:14:14 rpd
+ * Removed RCS Source, Header lines.
+ * [90/05/03 00:07:27 rpd]
+ *
+ * Revision 2.4 90/03/14 21:12:15 rwd
+ * Added new option:
+ * WAIT_DEBUG: keep track of who a blocked thread is
+ * waiting for.
+ * [90/03/01 rwd]
+ *
+ * Revision 2.3 90/01/19 14:37:25 rwd
+ * New option:
+ * THREAD_CALLS: cthread_* version of thread_* calls.
+ * [90/01/03 rwd]
+ *
+ * Revision 2.2 89/12/08 19:54:09 rwd
+ * Added code:
+ * MUTEX_SPECIAL: Have extra kernel threads available for
+ * special mutexes to avoid deadlocks
+ * Removed options:
+ * MSGOPT, RECEIVE_YIELD
+ * [89/11/25 rwd]
+ * Added option:
+ * MUTEX_SPECIAL: Allow special mutexes which will
+ * garuntee the resulting threads runs
+ * on a mutex_unlock
+ * [89/11/21 rwd]
+ * Options added are:
+ * STATISTICS: collect [kernel/c]thread state stats.
+ * SPIN_RESCHED: call swtch_pri(0) when spin will block.
+ * MSGOPT: try to minimize message sends
+ * CHECK_STATUS: check status of mach calls
+ * RECEIVE_YIELD: yield thread if no waiting threads after
+ * cthread_msg_receive
+ * RED_ZONE: make redzone at end of stacks
+ * DEBUG_MUTEX: in conjunction with same in cthreads.h
+ * use slow mutex with held=cproc_self().
+ * [89/11/13 rwd]
+ * Added copyright. Removed all options.
+ * [89/10/23 rwd]
+ *
+ */
+/*
+ * options.h
+ */
+
+/*#define STATISTICS*/
+#define SPIN_RESCHED
+/*#define CHECK_STATUS*/
+/*#define RED_ZONE*/
+/*#define WAIT_DEBUG*/
diff --git a/libthreads/rwlock.c b/libthreads/rwlock.c
new file mode 100644
index 00000000..ae6a7c48
--- /dev/null
+++ b/libthreads/rwlock.c
@@ -0,0 +1,2 @@
+#define RWLOCK_DEFINE_EI
+#include "rwlock.h"
diff --git a/libthreads/rwlock.h b/libthreads/rwlock.h
new file mode 100644
index 00000000..44d9a35d
--- /dev/null
+++ b/libthreads/rwlock.h
@@ -0,0 +1,128 @@
+/* Simple reader/writer locks.
+
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _RWLOCK_H
+#define _RWLOCK_H 1
+
+#include <cthreads.h>
+#include <assert.h>
+#include <features.h>
+
+#ifdef RWLOCK_DEFINE_EI
+#define RWLOCK_EI
+#else
+#define RWLOCK_EI __extern_inline
+#endif
+
+struct rwlock
+{
+ struct mutex master;
+ struct condition wakeup;
+ int readers;
+ int writers_waiting;
+ int readers_waiting;
+};
+
+extern void rwlock_reader_lock (struct rwlock *lock);
+
+extern void rwlock_writer_lock (struct rwlock *lock);
+
+extern void rwlock_reader_unlock (struct rwlock *lock);
+
+extern void rwlock_writer_unlock (struct rwlock *lock);
+
+extern void rwlock_init (struct rwlock *lock);
+
+#if defined(__USE_EXTERN_INLINES) || defined(RWLOCK_DEFINE_EI)
+
+/* Get a reader lock on reader-writer lock LOCK for disknode DN */
+RWLOCK_EI void
+rwlock_reader_lock (struct rwlock *lock)
+{
+ mutex_lock (&lock->master);
+ if (lock->readers == -1 || lock->writers_waiting)
+ {
+ lock->readers_waiting++;
+ do
+ condition_wait (&lock->wakeup, &lock->master);
+ while (lock->readers == -1 || lock->writers_waiting);
+ lock->readers_waiting--;
+ }
+ lock->readers++;
+ mutex_unlock (&lock->master);
+}
+
+/* Get a writer lock on reader-writer lock LOCK for disknode DN */
+RWLOCK_EI void
+rwlock_writer_lock (struct rwlock *lock)
+{
+ mutex_lock (&lock->master);
+ if (lock->readers)
+ {
+ lock->writers_waiting++;
+ do
+ condition_wait (&lock->wakeup, &lock->master);
+ while (lock->readers);
+ lock->writers_waiting--;
+ }
+ lock->readers = -1;
+ mutex_unlock (&lock->master);
+}
+
+/* Release a reader lock on reader-writer lock LOCK for disknode DN */
+RWLOCK_EI void
+rwlock_reader_unlock (struct rwlock *lock)
+{
+ mutex_lock (&lock->master);
+ assert (lock->readers);
+ lock->readers--;
+ if (lock->readers_waiting || lock->writers_waiting)
+ condition_broadcast (&lock->wakeup);
+ mutex_unlock (&lock->master);
+}
+
+/* Release a writer lock on reader-writer lock LOCK for disknode DN */
+RWLOCK_EI void
+rwlock_writer_unlock (struct rwlock *lock)
+{
+ mutex_lock (&lock->master);
+ assert (lock->readers == -1);
+ lock->readers = 0;
+ if (lock->readers_waiting || lock->writers_waiting)
+ condition_broadcast (&lock->wakeup);
+ mutex_unlock (&lock->master);
+}
+
+/* Initialize reader-writer lock LOCK */
+RWLOCK_EI void
+rwlock_init (struct rwlock *lock)
+{
+ mutex_init (&lock->master);
+ condition_init (&lock->wakeup);
+ lock->readers = 0;
+ lock->readers_waiting = 0;
+ lock->writers_waiting = 0;
+}
+
+#endif /* Use extern inlines. */
+
+#define RWLOCK_INITIALIZER \
+ { MUTEX_INITIALIZER, CONDITION_INITIALIZER, 0, 0, 0 }
+
+
+#endif /* rwlock.h */
diff --git a/libthreads/stack.c b/libthreads/stack.c
new file mode 100644
index 00000000..21a9b6be
--- /dev/null
+++ b/libthreads/stack.c
@@ -0,0 +1,424 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992,1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * $Log: stack.c,v $
+ * Revision 1.8 2002/05/27 02:50:41 roland
+ * 2002-05-26 Roland McGrath <roland@frob.com>
+ *
+ * Changes merged from CMU MK83a version:
+ * * cthreads.h, options.h: Various cleanups.
+ * * call.c, cthread_data.c, sync.c, mig_support.c, stack.c: Likewise.
+ * * i386/cthreads.h, i386/thread.c, i386/lock.s: Likewise.
+ * * cthread_internals.h: Add decls for internal functions.
+ * (struct cproc): Use vm_offset_t for stack_base and stack_size members.
+ * Use natural_t for context member.
+ * * cprocs.c: Use prototypes for all defns.
+ * * cthreads.c: Likewise.
+ * (cthread_exit): Cast any_t to integer_t before int.
+ *
+ * Revision 2.14 93/01/14 18:05:58 danner
+ * Converted file to ANSI C.
+ * [92/12/18 pds]
+ * 64bit cleanup.
+ * [92/12/02 af]
+ *
+ * Revision 2.13 92/01/14 16:48:54 rpd
+ * Fixed addr_range_check to deallocate the object port from vm_region.
+ * [92/01/14 rpd]
+ *
+ * Revision 2.12 92/01/03 20:37:10 dbg
+ * Export cthread_stack_size, and use it if non-zero instead of
+ * probing the stack. Fix error in deallocating unused initial
+ * stack (STACK_GROWTH_UP case).
+ * [91/08/28 dbg]
+ *
+ * Revision 2.11 91/07/31 18:39:34 dbg
+ * Fix some bad stack references (stack direction).
+ * [91/07/30 17:36:50 dbg]
+ *
+ * Revision 2.10 91/05/14 17:58:49 mrt
+ * Correcting copyright
+ *
+ * Revision 2.9 91/02/14 14:21:08 mrt
+ * Added new Mach copyright
+ * [91/02/13 12:41:35 mrt]
+ *
+ * Revision 2.8 90/11/05 18:10:46 rpd
+ * Added cproc_stack_base. Add stack_fork_child().
+ * [90/11/01 rwd]
+ *
+ * Revision 2.7 90/11/05 14:37:51 rpd
+ * Fixed addr_range_check for new vm_region semantics.
+ * [90/11/02 rpd]
+ *
+ * Revision 2.6 90/10/12 13:07:34 rpd
+ * Deal with positively growing stacks.
+ * [90/10/10 rwd]
+ * Deal with initial user stacks that are not perfectly aligned.
+ * [90/09/26 11:51:46 rwd]
+ *
+ * Leave extra stack page around in case it is needed before we
+ * switch stacks.
+ * [90/09/25 rwd]
+ *
+ * Revision 2.5 90/08/07 14:31:46 rpd
+ * Removed RCS keyword nonsense.
+ *
+ * Revision 2.4 90/06/02 15:14:18 rpd
+ * Moved cthread_sp to machine-dependent files.
+ * [90/04/24 rpd]
+ * Converted to new IPC.
+ * [90/03/20 20:56:35 rpd]
+ *
+ * Revision 2.3 90/01/19 14:37:34 rwd
+ * Move self pointer to top of stack
+ * [89/12/12 rwd]
+ *
+ * Revision 2.2 89/12/08 19:49:52 rwd
+ * Back out change from af.
+ * [89/12/08 rwd]
+ *
+ * Revision 2.1.1.3 89/12/06 12:54:17 rwd
+ * Gap fix from af
+ * [89/12/06 rwd]
+ *
+ * Revision 2.1.1.2 89/11/21 15:01:40 rwd
+ * Add RED_ZONE ifdef.
+ * [89/11/20 rwd]
+ *
+ * Revision 2.1.1.1 89/10/24 13:00:44 rwd
+ * Remove conditionals.
+ * [89/10/23 rwd]
+ *
+ * Revision 2.1 89/08/03 17:10:05 rwd
+ * Created.
+ *
+ * 18-Jan-89 David Golub (dbg) at Carnegie-Mellon University
+ * Altered for stand-alone use:
+ * use vm_region to probe for the bottom of the initial thread's
+ * stack.
+ *
+ *
+ * 01-Dec-87 Eric Cooper (ecc) at Carnegie Mellon University
+ * Changed cthread stack allocation to use aligned stacks
+ * and store self pointer at base of stack.
+ * Added inline expansion for cthread_sp() function.
+ */
+/*
+ * File: stack.c
+ * Author: Eric Cooper, Carnegie Mellon University
+ * Date: Dec, 1987
+ *
+ * C Thread stack allocation.
+ *
+ */
+
+#include <cthreads.h>
+#include "cthread_internals.h"
+#include <hurd/threadvar.h>
+
+#define BYTES_TO_PAGES(b) (((b) + vm_page_size - 1) / vm_page_size)
+
+vm_offset_t cthread_stack_mask;
+vm_size_t cthread_stack_size;
+private vm_address_t next_stack_base;
+
+/*
+ * Set up a stack segment for a thread.
+ * Segment has a red zone (invalid page)
+ * for early detection of stack overflow.
+ * The cproc_self pointer is stored at the top.
+ *
+ * --------- (high address)
+ * | self |
+ * | ... |
+ * | |
+ * | stack |
+ * | |
+ * | ... |
+ * | |
+ * ---------
+ * | |
+ * |invalid|
+ * | |
+ * --------- (stack base)
+ * --------- (low address)
+ *
+ * or the reverse, if the stack grows up.
+ */
+
+private void
+setup_stack(p, base)
+ register cproc_t p;
+ register vm_address_t base;
+{
+ register kern_return_t r;
+
+ p->stack_base = base;
+ /*
+ * Stack size is segment size minus size of self pointer
+ */
+ p->stack_size = cthread_stack_size;
+ /*
+ * Protect red zone.
+ */
+#ifdef STACK_GROWTH_UP
+ MACH_CALL(vm_protect(mach_task_self(), base + cthread_stack_size - 2*vm_page_size, vm_page_size, FALSE, VM_PROT_NONE), r);
+#else
+ MACH_CALL(vm_protect(mach_task_self(), base + vm_page_size, vm_page_size, FALSE, VM_PROT_NONE), r);
+#endif
+ /*
+ * Store self pointer.
+ */
+ *(cproc_t *)&ur_cthread_ptr(base) = p;
+}
+
+#if 0 /* GNU */
+private vm_offset_t
+addr_range_check(vm_offset_t start_addr, vm_offset_t end_addr,
+ vm_prot_t desired_protection)
+{
+ register vm_offset_t addr;
+
+ addr = start_addr;
+ while (addr < end_addr) {
+ vm_offset_t r_addr;
+ vm_size_t r_size;
+ vm_prot_t r_protection,
+ r_max_protection;
+ vm_inherit_t r_inheritance;
+ boolean_t r_is_shared;
+ memory_object_name_t r_object_name;
+ vm_offset_t r_offset;
+ kern_return_t kr;
+
+ r_addr = addr;
+ kr = vm_region(mach_task_self(), &r_addr, &r_size,
+ &r_protection, &r_max_protection, &r_inheritance,
+ &r_is_shared, &r_object_name, &r_offset);
+ if ((kr == KERN_SUCCESS) && MACH_PORT_VALID(r_object_name))
+ (void) mach_port_deallocate(mach_task_self(), r_object_name);
+
+ if ((kr != KERN_SUCCESS) ||
+ (r_addr > addr) ||
+ ((r_protection & desired_protection) != desired_protection))
+ return (0);
+ addr = r_addr + r_size;
+ }
+ return (addr);
+}
+
+/*
+ * Probe for bottom and top of stack.
+ * Assume:
+ * 1. stack grows DOWN
+ * 2. There is an unallocated region below the stack.
+ */
+private void
+probe_stack(vm_offset_t *stack_bottom, vm_offset_t *stack_top)
+{
+ /*
+ * Since vm_region returns the region starting at
+ * or ABOVE the given address, we cannot use it
+ * directly to search downwards. However, we
+ * also want a size that is the closest power of
+ * 2 to the stack size (so we can mask off the stack
+ * address and get the stack base). So we probe
+ * in increasing powers of 2 until we find a gap
+ * in the stack.
+ */
+ vm_offset_t start_addr, end_addr;
+ vm_offset_t last_start_addr, last_end_addr;
+ vm_size_t stack_size;
+
+ /*
+ * Start with a page
+ */
+ start_addr = cthread_sp() & ~(vm_page_size - 1);
+ end_addr = start_addr + vm_page_size;
+
+ stack_size = vm_page_size;
+
+ /*
+ * Increase the tentative stack size, by doubling each
+ * time, until we have exceeded the stack (some of the
+ * range is not valid).
+ */
+ do {
+ /*
+ * Save last addresses
+ */
+ last_start_addr = start_addr;
+ last_end_addr = end_addr;
+
+ /*
+ * Double the stack size
+ */
+ stack_size <<= 1;
+ start_addr = end_addr - stack_size;
+
+ /*
+ * Check that the entire range exists and is writable
+ */
+ } while ((end_addr = addr_range_check(start_addr, end_addr,
+ VM_PROT_READ|VM_PROT_WRITE)));
+ /*
+ * Back off to previous power of 2.
+ */
+ *stack_bottom = last_start_addr;
+ *stack_top = last_end_addr;
+}
+#endif
+
+/* For GNU: */
+extern unsigned long int __hurd_threadvar_stack_mask;
+extern unsigned long int __hurd_threadvar_stack_offset;
+extern unsigned int __hurd_threadvar_max;
+
+vm_offset_t
+stack_init(cproc_t p)
+{
+#if 0
+ vm_offset_t stack_bottom,
+ stack_top,
+ start;
+ vm_size_t size;
+ kern_return_t r;
+
+ /*
+ * Probe for bottom and top of stack, as a power-of-2 size.
+ */
+ probe_stack(&stack_bottom, &stack_top);
+
+ /*
+ * Use the stack size found for the Cthread stack size,
+ * if not already specified.
+ */
+ if (cthread_stack_size == 0)
+ cthread_stack_size = stack_top - stack_bottom;
+#else /* GNU */
+ if (cthread_stack_size == 0)
+ cthread_stack_size = vm_page_size * 16; /* Reasonable default. */
+#endif /* GNU */
+
+#if defined(STACK_GROWTH_UP)
+ cthread_stack_mask = ~(cthread_stack_size - 1);
+#else /* not defined(STACK_GROWTH_UP) */
+ cthread_stack_mask = cthread_stack_size - 1;
+#endif /* defined(STACK_GROWTH_UP) */
+
+ /* Set up the variables so GNU can find its per-thread variables. */
+ __hurd_threadvar_stack_mask = ~(cthread_stack_size - 1);
+ /* The GNU per-thread variables will be stored just after the
+ cthread-self pointer at the base of the stack. */
+#ifdef STACK_GROWTH_UP
+ __hurd_threadvar_stack_offset = sizeof (ur_cthread_t *);
+#else
+ __hurd_threadvar_stack_offset = (cthread_stack_size -
+ sizeof (ur_cthread_t *) -
+ __hurd_threadvar_max * sizeof (long));
+#endif
+
+ /*
+ * Guess at first available region for stack.
+ */
+ next_stack_base = 0;
+
+ /*
+ * Set up stack for main thread.
+ */
+ alloc_stack(p);
+
+#if 0 /* GNU */
+ /*
+ * Delete rest of old stack.
+ */
+
+#if defined(STACK_GROWTH_UP)
+ start = (cthread_sp() | (vm_page_size - 1)) + 1 + vm_page_size;
+ size = stack_top - start;
+#else /* not defined(STACK_GROWTH_UP) */
+ start = stack_bottom;
+ size = (cthread_sp() & ~(vm_page_size - 1)) - stack_bottom -
+ vm_page_size;
+#endif /* defined(STACK_GROWTH_UP) */
+ MACH_CALL(vm_deallocate(mach_task_self(),start,size),r);
+#endif /* GNU */
+
+ /*
+ * Return new stack; it gets passed back to the caller
+ * of cthread_init who must switch to it.
+ */
+ return cproc_stack_base(p, sizeof(ur_cthread_t *) +
+ /* Account for GNU per-thread variables. */
+ __hurd_threadvar_max * sizeof (long int));
+}
+
+/*
+ * Allocate a stack segment for a thread.
+ * Stacks are never deallocated.
+ *
+ * The variable next_stack_base is used to align stacks.
+ * It may be updated by several threads in parallel,
+ * but mutual exclusion is unnecessary: at worst,
+ * the vm_allocate will fail and the thread will try again.
+ */
+
+void
+alloc_stack(cproc_t p)
+{
+ vm_address_t base = next_stack_base;
+
+ for (base = next_stack_base;
+ vm_allocate(mach_task_self(), &base, cthread_stack_size, FALSE) != KERN_SUCCESS;
+ base += cthread_stack_size)
+ ;
+ next_stack_base = base + cthread_stack_size;
+ setup_stack(p, base);
+}
+
+vm_offset_t
+cproc_stack_base(cproc, offset)
+ register cproc_t cproc;
+ register int offset;
+{
+#if defined(STACK_GROWTH_UP)
+ return (cproc->stack_base + offset);
+#else /* not defined(STACK_GROWTH_UP) */
+ return (cproc->stack_base + cproc->stack_size - offset);
+#endif /* defined(STACK_GROWTH_UP) */
+
+}
+
+void stack_fork_child()
+/*
+ * Called in the child after a fork(). Resets stack data structures to
+ * coincide with the reality that we now have a single cproc and cthread.
+ */
+{
+ next_stack_base = 0;
+}
diff --git a/libthreads/sync.c b/libthreads/sync.c
new file mode 100644
index 00000000..d65eb654
--- /dev/null
+++ b/libthreads/sync.c
@@ -0,0 +1,86 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992,1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * $Log: sync.c,v $
+ * Revision 2.8 93/01/14 18:06:23 danner
+ * Converted file to ANSI C.
+ * [92/12/18 pds]
+ *
+ * Revision 2.7 92/03/06 14:09:59 rpd
+ * Replaced swtch_pri with yield.
+ * [92/03/06 rpd]
+ *
+ * Revision 2.6 91/05/14 17:59:54 mrt
+ * Correcting copyright
+ *
+ * Revision 2.5 91/02/14 14:21:38 mrt
+ * Added new Mach copyright
+ * [91/02/13 12:41:42 mrt]
+ *
+ * Revision 2.4 90/11/05 14:38:08 rpd
+ * Fix casting. Use new macros.
+ * [90/10/31 rwd]
+ *
+ * Revision 2.3 90/08/07 14:31:50 rpd
+ * Removed RCS keyword nonsense.
+ *
+ * Revision 2.2 89/12/08 19:55:01 rwd
+ * Changed spin_lock to spin_lock_solid to optimize.
+ * [89/11/13 rwd]
+ * Added copyright. Move mutexes to cproc.c. Spin locks are now
+ * old mutexes.
+ * [89/10/23 rwd]
+ *
+ */
+/*
+ * sync.c
+ *
+ * Spin locks
+ */
+
+#include <cthreads.h>
+#include "cthread_internals.h"
+
+/*
+ * Spin locks.
+ * Use test and test-and-set logic on all architectures.
+ */
+
+int cthread_spin_count=0;
+
+void
+spin_lock_solid(register spin_lock_t *p)
+{
+ while (spin_lock_locked(p) || !spin_try_lock(p)) {
+#ifdef STATISTICS
+ cthread_spin_count++;
+#endif
+#ifdef SPIN_RESCHED
+ yield();
+#endif
+ }
+}
diff --git a/libtreefs/Makefile b/libtreefs/Makefile
new file mode 100644
index 00000000..025040cf
--- /dev/null
+++ b/libtreefs/Makefile
@@ -0,0 +1,38 @@
+# Makefile for libtreefs
+#
+# Copyright (C) 1995, 2012 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libtreefs
+makemode := library
+
+libname = libtreefs
+installhdrs = treefs.h
+# RPC stubs
+S_SRCS = s-file.c s-dir.c s-io.c s-fsys.c
+OTHERSRCS = defhooks.c dir-hooks.c dir-lookup.c fsys-getroot.c fsys-hooks.c \
+ fsys-startup.c hooks.c mdir.c nlist.c node-hooks.c rights.c \
+ trans-help.c trans-start.c xinl.c
+SRCS = $(OTHERSRCS) $(S_SRCS)
+
+MIGSTUBS = fsServer.o ioServer.o fsysServer.o
+OBJS = $(sort $(SRCS:.c=.o)) $(MIGSTUBS)
+
+MIGSFLAGS = -imacros fs-mutate.h
+MIGCOMSFLAGS = -prefix treefs_
+notify-MIGSFLAGS = -DSEQNOS
+
+include ../Makeconf
diff --git a/libtreefs/defhooks.c b/libtreefs/defhooks.c
new file mode 100644
index 00000000..bed0b550
--- /dev/null
+++ b/libtreefs/defhooks.c
@@ -0,0 +1,80 @@
+/* Default functions for the hook vector
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "treefs.h"
+#include "treefs-s-hooks.h"
+
+typedef void (*vf)();
+
+static error_t unsupp () { return EOPNOTSUPP; }
+static error_t nop () { return 0; }
+static int true () { return 1; }
+
+/* Most hooks return an error_t, so the default for anything not mentioned in
+ this array is to return EOPNOTSUPP. Any hooks returning different types,
+ or with some different default behavior should be mentioned here. */
+treefs_hook_vector_init_t treefs_default_hooks =
+{
+ /* directory rpcs */
+ [TREEFS_HOOK_S_DIR_LOOKUP] = (vf)_treefs_s_dir_lookup,
+
+ [TREEFS_HOOK_S_FSYS_GETROOT] = (vf)_treefs_s_fsys_getroot,
+ [TREEFS_HOOK_S_FSYS_SYNCFS] = (vf)nop,
+
+ /* Non-rpc fsys hooks */
+ [TREEFS_HOOK_FSYS_CREATE_NODE] = (vf)_treefs_fsys_create_node,
+ [TREEFS_HOOK_FSYS_DESTROY_NODE] = (vf)_treefs_fsys_destroy_node,
+ [TREEFS_HOOK_FSYS_GET_ROOT] = (vf)_treefs_fsys_get_root,
+
+ /* Node hooks */
+ [TREEFS_HOOK_NODE_TYPE] = (vf)_treefs_node_type,
+ [TREEFS_HOOK_NODE_UNLINKED] = (vf)true,
+ [TREEFS_HOOK_NODE_MOD_LINK_COUNT] = (vf)_treefs_node_mod_link_count,
+ [TREEFS_HOOK_DIR_LOOKUP] = (vf)_treefs_dir_lookup,
+ [TREEFS_HOOK_DIR_NOENT] = (vf)_treefs_dir_noent,
+ [TREEFS_HOOK_DIR_CREATE_CHILD] = (vf)_treefs_dir_create_child,
+ [TREEFS_HOOK_DIR_LINK] = (vf)_treefs_dir_link,
+ [TREEFS_HOOK_DIR_UNLINK] = (vf)_treefs_dir_unlink,
+ [TREEFS_HOOK_NODE_OWNED] = (vf)_treefs_node_owned,
+ [TREEFS_HOOK_NODE_ACCESS] = (vf)_treefs_node_access,
+ [TREEFS_HOOK_NODE_START_TRANSLATOR] = (vf)_treefs_node_start_translator,
+ [TREEFS_HOOK_NODE_INIT] = (vf)nop,
+ [TREEFS_HOOK_DIR_INIT] = (vf)nop,
+ [TREEFS_HOOK_NODE_INIT_PEROPEN] = (vf)nop,
+ [TREEFS_HOOK_NODE_INIT_HANDLE] = (vf)nop,
+ [TREEFS_HOOK_NODE_FINALIZE] = (vf)nop,
+ [TREEFS_HOOK_NODE_FINALIZE_PEROPEN] = (vf)nop,
+ [TREEFS_HOOK_NODE_FINALIZE_HANDLE] = (vf)nop,
+
+ /* Reference counting support */
+ [TREEFS_HOOK_NODE_NEW_REFS] = (vf)nop,
+ [TREEFS_HOOK_NODE_LOST_REFS] = (vf)nop,
+ [TREEFS_HOOK_NODE_TRY_DROPPING_WEAK_REFS] = (vf)nop,
+};
+
+void _treefs_init_defhooks()
+{
+ int i;
+ for (i = 0; i < TREEFS_NUM_HOOKS; i++)
+ if (!treefs_default_hooks[i])
+ treefs_default_hooks[i] = (vf)unsupp;
+}
diff --git a/libtreefs/dir-hooks.c b/libtreefs/dir-hooks.c
new file mode 100644
index 00000000..5c54039a
--- /dev/null
+++ b/libtreefs/dir-hooks.c
@@ -0,0 +1,136 @@
+/* Default hooks for directory nodes
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/fcntl.h>
+
+#include "treefs.h"
+
+/* ---------------------------------------------------------------- */
+
+/* Return in CHILD a new node with one reference, presumably a possible child
+ of DIR, with a mode MODE. All attempts to create a new node go through
+ this hook, so it may be overridden to easily control creation (e.g.,
+ replacing it with a hook that always returns EPERM). Note that this
+ routine doesn't actually enter the child into the directory, or give the
+ node a non-zero link count, that should be done by the caller. */
+error_t
+_treefs_dir_create_child (struct treefs_node *dir,
+ mode_t mode, struct treefs_auth *auth,
+ struct treefs_node **child)
+{
+ error_t err;
+
+ if (!treefs_node_isdir (dir))
+ return ENOTDIR;
+
+ err = treefs_fsys_create_node (dir->fsys, dir, mode, auth, child);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Lookup NAME in DIR, returning the result in CHILD; AUTH should be used to
+ do authentication. FLAGS is the open flags; if FLAGS contains O_CREAT,
+ and NAME is not found, then an entry should be created with a mode of
+ CREATE_MODE (which includes the S_IFMT bits, e.g., S_IFREG means a normal
+ file), unless O_EXCL is also set, in which case EEXIST should be returned.
+ Possible special errors returned include: EAGAIN -- result would be the
+ parent of our filesystem root. */
+error_t
+_treefs_dir_lookup (struct treefs_node *dir, char *name,
+ struct treefs_auth *auth,
+ int flags, int create_mode,
+ struct treefs_node **child)
+{
+ error_t err;
+
+ if (strcmp (name, "..") == 0 && dir == dir->fsys->root)
+ /* Whoops, the result is above our heads. */
+ err = EAGAIN;
+ else
+ /* See if we've got an in-core entry for this file. */
+ err = treefs_mdir_get (dir, name, child);
+
+ if (err == 0 && (flags & O_EXCL))
+ return EEXIST;
+
+ if (err == ENOENT)
+ /* See if there's some other way of getting this file. */
+ err = treefs_dir_noent (dir, name, auth, flags, create_mode, child);
+
+ if (err == ENOENT && (flags & O_CREAT))
+ /* No such file, but the user wants to create it. */
+ {
+ err = treefs_dir_create_child (dir, create_mode, auth, child);
+ if (!err)
+ {
+ err = treefs_dir_link (dir, name, *child, auth);
+ if (err)
+ treefs_node_unref (*child);
+ }
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Link the node CHILD into DIR as NAME, using AUTH to check authentication.
+ DIR should be locked and CHILD shouldn't be. The default hook puts it
+ into DIR's in-core directory, and uses a reference to CHILD. */
+error_t
+_treefs_dir_link (struct treefs_node *dir, char *name,
+ struct treefs_node *child, struct treefs_auth *auth)
+{
+ struct treefs_node *old_child;
+ error_t err = treefs_node_mod_link_count (child, 1);
+ if (!err)
+ {
+ err = treefs_mdir_add (dir, name, child, &old_child);
+ if (err)
+ /* back out */
+ treefs_node_mod_link_count (child, -1);
+ else if (old_child)
+ treefs_node_mod_link_count (old_child, -1);
+ }
+ return err;
+}
+
+/* Remove the entry NAME from DIR, using AUTH to check authentication. DIR
+ should be locked. The default hook removes NAME from DIR's in-core
+ directory. */
+error_t
+_treefs_dir_unlink (struct treefs_node *dir, char *name,
+ struct treefs_auth *auth)
+{
+ struct treefs_node *old_child;
+ error_t err = treefs_mdir_remove (dir, name, &old_child);
+ if (!err && old_child)
+ {
+ treefs_node_mod_link_count (old_child, -1);
+ treefs_node_unref (old_child);
+ }
+ return err;
+}
diff --git a/libtreefs/dir-lookup.c b/libtreefs/dir-lookup.c
new file mode 100644
index 00000000..8665059c
--- /dev/null
+++ b/libtreefs/dir-lookup.c
@@ -0,0 +1,306 @@
+/* Default treefs_s_dir_lookup hook
+
+ Copyright (C) 1992, 1993, 1994, 1995, 1998 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <fcntl.h>
+#include <string.h>
+
+#include <hurd/fsys.h>
+
+#include "treefs.h"
+#include "treefs-s-hooks.h"
+
+/* Default dir_lookup hook. This code was originally copied from diskfs. */
+error_t
+_treefs_s_dir_lookup (struct treefs_handle *h,
+ char *path, int flags, mode_t mode,
+ enum retry_type *retry, char *retry_name,
+ file_t *result, mach_msg_type_name_t *result_type)
+{
+ struct treefs_node *dir;
+ struct treefs_node *node;
+ unsigned symlink_expansions = 0;
+ error_t err = 0;
+ char *path_buf = 0;
+ int path_buf_len = 0;
+ int lastcomp = 0;
+ int mustbedir = 0;
+
+ flags &= O_HURD;
+ mode &= ~S_IFMT;
+
+ /* Skip leading slashes */
+ while (path[0] == '/')
+ path++;
+
+ *result_type = MACH_MSG_TYPE_MAKE_SEND;
+ *retry = FS_RETRY_NORMAL;
+ retry_name[0] = '\0';
+
+ if (path[0] == '\0')
+ {
+ /* Set things up in the state expected by the code from gotit: on. */
+ dir = 0;
+ node = h->po->node;
+ pthread_mutex_lock (&node->lock);
+ treefs_node_ref (node);
+ goto gotit;
+ }
+
+ dir = h->po->node;
+ pthread_mutex_lock (&dir->lock);
+ node = 0;
+
+ treefs_node_ref (dir); /* acquire a ref for later node_release */
+
+ do
+ {
+ char *nextname;
+
+ assert (!lastcomp);
+
+ /* Find the name of the next pathname component */
+ nextname = index (path, '/');
+
+ if (nextname)
+ {
+ *nextname++ = '\0';
+ while (*nextname == '/')
+ nextname++;
+ if (*nextname == '\0')
+ {
+ /* These are the rules for filenames ending in /. */
+ nextname = 0;
+ lastcomp = 1;
+ mustbedir = 1;
+
+ }
+ else
+ lastcomp = 0;
+ }
+ else
+ lastcomp = 1;
+
+ node = 0;
+
+ /* Lookup the next pathname component. */
+ if (!lastcomp)
+ err = treefs_dir_lookup (dir, path, h->auth, 0, 0, &node);
+ else
+ /* ... and in this case, the last. Note that the S_IFREG only
+ applies in the case of O_CREAT, which is turned off for
+ directories anyway. */
+ err =
+ treefs_dir_lookup (dir, path, h->auth, flags, mode | S_IFREG, &node);
+
+ /* If we get an error we're done */
+ if (err == EAGAIN)
+ {
+ if (h->po->parent_port != MACH_PORT_NULL)
+ {
+ *retry = FS_RETRY_REAUTH;
+ *result = h->po->parent_port;
+ *result_type = MACH_MSG_TYPE_COPY_SEND;
+ if (!lastcomp)
+ strcpy (retry_name, nextname);
+ err = 0;
+ goto out;
+ }
+ else
+ /* The global filesystem root... .. == . */
+ {
+ err = 0;
+ node = dir;
+ treefs_node_ref (node);
+ }
+ }
+
+ if (err)
+ goto out;
+
+ /* If this is translated, start the translator (if necessary)
+ and return. */
+ /* The check for `node != dir' simplifies this code a great
+ deal. Such a translator should already have been started,
+ so there's no lossage in doing it this way. */
+ if ((!lastcomp || !(flags & O_NOTRANS))
+ && node != dir)
+ {
+ file_t dir_port = MACH_PORT_NULL, child_fsys;
+
+ /* Be very careful not to hold an inode lock while fetching
+ a translator lock and vice versa. */
+
+ pthread_mutex_unlock (&node->lock);
+ pthread_mutex_unlock (&dir->lock);
+
+ do
+ {
+ err =
+ treefs_node_get_active_trans (node, dir, h->po->parent_port,
+ &dir_port, &child_fsys);
+ if (err == 0 && child_fsys != MACH_PORT_NULL)
+ {
+ err =
+ fsys_getroot (child_fsys, dir_port,
+ MACH_MSG_TYPE_COPY_SEND,
+ h->auth->uids, h->auth->nuids,
+ h->auth->gids, h->auth->ngids,
+ lastcomp ? flags : 0,
+ retry, retry_name, result);
+ /* If we got MACH_SEND_INVALID_DEST or MIG_SERVER_DIED, then
+ the server is dead. Zero out the old control port and try
+ everything again. */
+ if (err == MACH_SEND_INVALID_DEST || err == EMIG_SERVER_DIED)
+ treefs_node_drop_active_trans (node, child_fsys);
+ }
+ }
+ while (err == MACH_SEND_INVALID_DEST || err == EMIG_SERVER_DIED);
+
+ if (err || child_fsys)
+ {
+ /* We're done; return to the user. If there are more
+ components after this name, be sure to append them to the
+ user's retry path. */
+ if (!err && !lastcomp)
+ {
+ strcat (retry_name, "/");
+ strcat (retry_name, nextname);
+ }
+
+ *result_type = MACH_MSG_TYPE_MOVE_SEND;
+
+ treefs_node_unref (dir);
+ treefs_node_unref (node);
+ if (dir_port)
+ mach_port_deallocate (mach_task_self (), dir_port);
+
+ return err;
+ }
+
+ /* We're here if we tried the translator check, and it
+ failed. Lock everything back, and make sure we do it
+ in the right order. */
+ if (strcmp (path, "..") != 0)
+ {
+ if (pthread_mutex_trylock (&dir->lock))
+ {
+ pthread_mutex_unlock (&node->lock);
+ pthread_mutex_lock (&dir->lock);
+ pthread_mutex_lock (&node->lock);
+ }
+ }
+ else
+ pthread_mutex_lock (&dir->lock);
+ }
+
+ if (treefs_node_type (node) == S_IFLNK
+ && !(lastcomp && (flags & (O_NOLINK|O_NOTRANS))))
+ /* Handle symlink interpretation */
+ {
+ unsigned nextname_len = nextname ? strlen (nextname) + 1 : 0;
+ /* max space we currently have for the sym link */
+ unsigned sym_len = path_buf_len - nextname_len - 1;
+
+ if (symlink_expansions++ > node->fsys->max_symlinks)
+ {
+ err = ELOOP;
+ goto out;
+ }
+
+ err = treefs_node_get_symlink (node, path_buf, &sym_len);
+ if (err == E2BIG)
+ /* Symlink contents + extra path won't fit in our buffer, so
+ reallocate it and try again. */
+ {
+ path_buf_len = sym_len + nextname_len + 1;
+ path_buf = alloca (path_buf_len);
+ err = treefs_node_get_symlink (node, path_buf, &sym_len);
+ }
+ if (err)
+ goto out;
+
+ if (nextname)
+ {
+ path_buf[sym_len] = '/';
+ bcopy (nextname, path_buf + sym_len + 1, nextname_len - 1);
+ }
+ path_buf[nextname_len + sym_len] = '\0';
+
+ if (path_buf[0] == '/')
+ {
+ /* Punt to the caller. */
+ *retry = FS_RETRY_MAGICAL;
+ *result = MACH_PORT_NULL;
+ strcpy (retry_name, path_buf);
+ goto out;
+ }
+
+ path = path_buf;
+ if (lastcomp)
+ {
+ lastcomp = 0;
+ /* Symlinks to nonexistent files aren't allowed to cause
+ creation, so clear the flag here. */
+ flags &= ~O_CREAT;
+ }
+ treefs_node_release (node);
+ node = 0;
+ }
+ else
+ {
+ /* Handle normal nodes */
+ path = nextname;
+ if (node == dir)
+ treefs_node_unref (dir);
+ else
+ treefs_node_release (dir);
+ if (!lastcomp)
+ {
+ dir = node;
+ node = 0;
+ }
+ else
+ dir = 0;
+ }
+ } while (path && *path);
+
+ gotit:
+ /* At this point, node is the node to return. */
+
+ if (mustbedir && !treefs_node_isdir (node))
+ err = ENOTDIR;
+ if (err)
+ goto out;
+
+ err = treefs_node_create_right (node, flags, h->po->parent_port, h->auth,
+ result);
+
+ out:
+ if (node)
+ {
+ if (dir == node)
+ treefs_node_unref (node);
+ else
+ treefs_node_release (node);
+ }
+ if (dir)
+ treefs_node_release (dir);
+ return err;
+}
diff --git a/libtreefs/fs-mutate.h b/libtreefs/fs-mutate.h
new file mode 100644
index 00000000..6ddec3fa
--- /dev/null
+++ b/libtreefs/fs-mutate.h
@@ -0,0 +1,30 @@
+/* Automagic type transformation for our mig interfaces
+
+ Copyright (C) 1994 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Only CPP macro definitions should go in this file. */
+
+#define FILE_INTRAN treefs_handle_t treefs_begin_using_handle_port (file_t)
+#define FILE_DESTRUCTOR treefs_end_using_handle_port (treefs_handle_t)
+
+#define IO_INTRAN treefs_handle_t treefs_begin_using_handle_port (io_t)
+#define IO_DESTRUCTOR treefs_end_using_handle_port (treefs_handle_t)
+
+#define FILE_IMPORTS import "mig-decls.h";
+#define IO_IMPORTS import "mig-decls.h";
+#define FSYS_IMPORTS import "mig-decls.h";
+#define IFSOCK_IMPORTS import "mig-decls.h";
diff --git a/libtreefs/fsys-getroot.c b/libtreefs/fsys-getroot.c
new file mode 100644
index 00000000..cd735bfe
--- /dev/null
+++ b/libtreefs/fsys-getroot.c
@@ -0,0 +1,144 @@
+/*
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <fcntl.h>
+
+#include <hurd/fsys.h>
+
+#include "treefs.h"
+
+error_t
+_treefs_s_fsys_getroot (struct treefs_fsys *fsys,
+ mach_port_t dotdot,
+ uid_t *uids, unsigned nuids,
+ uid_t *gids, unsigned ngids,
+ int flags, retry_type *retry, char *retry_name,
+ file_t *result, mach_msg_type_name_t *result_type)
+{
+ error_t err;
+ mode_t type;
+ struct treefs_node *root;
+ struct treefs_auth *auth;
+
+ flags &= O_HURD;
+
+ err = treefs_fsys_get_root (fsys, &root);
+ if (err)
+ return err;
+
+ if (!(flags & O_NOTRANS))
+ /* Try starting up any translator on the root node. */
+ {
+ fsys_t child_fsys;
+
+ do
+ {
+ err =
+ treefs_node_get_active_trans (root, 0, 0, &dotdot, &child_fsys);
+ if (err == 0 && child_fsys != MACH_PORT_NULL)
+ /* We think there's an active translator; try contacting it. */
+ {
+ err =
+ fsys_getroot (child_fsys, dotdot, MACH_MSG_TYPE_COPY_SEND,
+ uids, nuids, gids, ngids,
+ flags, retry, retry_name, result);
+ /* If we got MACH_SEND_INVALID_DEST or MIG_SERVER_DIED, then
+ the server is dead. Zero out the old control port and try
+ everything again. */
+ if (err == MACH_SEND_INVALID_DEST || err == EMIG_SERVER_DIED)
+ treefs_node_drop_active_trans (root, control_port);
+ }
+ }
+ while (err == MACH_SEND_INVALID_DEST || err == MIG_SERVER_DIED);
+
+ /* If we got a translator, or an error trying, return immediately. */
+ if (err || child_fsys)
+ {
+ if (!err && *result != MACH_PORT_NULL)
+ *result_type = MACH_MSG_TYPE_MOVE_SEND;
+ else
+ *result_type = MACH_MSG_TYPE_COPY_SEND;
+
+ if (!err)
+ mach_port_deallocate (mach_task_self (), dotdot);
+ treefs_node_unref (root);
+
+ return err;
+ }
+ }
+
+ pthread_mutex_lock (&root->lock);
+
+ type = treefs_node_type (root);
+ if (type == S_IFLNK && !(flags & (O_NOLINK | O_NOTRANS)))
+ /* Handle symlink interpretation */
+ {
+ int sym_len = 1000;
+ char path_buf[sym_len + 1], *path = path_buf;
+
+ err = treefs_node_get_symlink (root, path, &sym_len);
+ if (err == E2BIG)
+ /* Symlink contents won't fit in our buffer, so
+ reallocate it and try again. */
+ {
+ path = alloca (sym_len + 1);
+ err = treefs_node_get_symlink (node, path, &sym_len);
+ }
+
+ if (err)
+ goto out;
+
+ if (*path == '/')
+ {
+ *retry = FS_RETRY_MAGICAL;
+ *result = MACH_PORT_NULL;
+ *result_type = MACH_MSG_TYPE_COPY_SEND;
+ mach_port_deallocate (mach_task_self (), dotdot);
+ }
+ else
+ {
+ *retry = FS_RETRY_REAUTH;
+ *result = dotdot;
+ *result_type = MACH_MSG_TYPE_COPY_SEND;
+ }
+
+ strcpy (retry_name, path);
+ goto out;
+ }
+
+ err = treefs_node_create_auth (root, uids, nuids, gids, ngids, &auth);
+ if (err)
+ goto out;
+
+ *retry = FS_RETRY_NORMAL;
+ *retry_name = '\0';
+ *result_type = MACH_MSG_TYPE_MAKE_SEND;
+
+ err = treefs_node_create_right (root, dotdot, flags, auth, result);
+
+ treefs_node_auth_unref (root, auth);
+
+ out:
+ treefs_node_release (root);
+
+ return err;
+}
diff --git a/libtreefs/fsys-hooks.c b/libtreefs/fsys-hooks.c
new file mode 100644
index 00000000..ecff092b
--- /dev/null
+++ b/libtreefs/fsys-hooks.c
@@ -0,0 +1,91 @@
+/* Default hooks for filesystems
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "treefs.h"
+
+/* Called to get the root node of the a filesystem, with a reference,
+ returning it in ROOT, or return an error if it can't be had. The default
+ hook just returns FSYS->root or an error if it's NULL. Note that despite
+ the similar name, this is very different from fsys_s_getroot! FSYS must
+ not be locked. */
+error_t
+_treefs_fsys_get_root (struct treefs_fsys *fsys, struct treefs_node **root)
+{
+ pthread_mutex_lock (&fsys->lock);
+ *root = fsys->root;
+ if (*root != NULL)
+ treefs_node_ref (*root);
+ pthread_mutex_unlock (&fsys->lock);
+
+ return *root ? 0 : EOPNOTSUPP;
+}
+
+/* Called on a filesystem to create a new node in that filesystem, returning
+ it in NODE. DIR, if non-NULL, is the nominal parent directory, and MODE
+ and CRED are the desired mode and user info respectively. This hook
+ should also call node_init_stat & node_init to initialize the various user
+ bits. */
+error_t
+_treefs_fsys_create_node (struct treefs_fsys *fsys,
+ struct treefs_node *dir,
+ mode_t mode, struct treefs_protid *cred,
+ struct treefs_node **node)
+{
+ error_t err = treefs_create_node (fsys, node);
+
+ if (err)
+ return err;
+
+ err = treefs_node_init_stat (*node, dir, mode, cred);
+ if (!err)
+ err = treefs_node_init (*node, dir, mode, cred);
+ if (S_ISDIR (mode))
+ {
+ treefs_dir_init (*node, dir, mode, cred);
+ treefs_mdir_init (*node, dir, mode, cred);
+ }
+ if (err)
+ {
+ /* There shouldn't be any other state to free at this point --
+ node_init_stat shouldn't do more than init the stat structure, and
+ if node_init fails, it should clean up after itself. */
+ treefs_free_node (*node);
+ return err;
+ }
+
+ return 0;
+}
+
+/* Called on a filesystem to destroy a node in that filesystem. This call
+ should *really* destroy it, i.e., it's only called once all references are
+ gone. */
+void
+_treefs_fsys_destroy_node (struct treefs_fsys *fsys, struct treefs_node *node)
+{
+ if (treefs_node_isdir (node))
+ {
+ treefs_mdir_finalize (node);
+ treefs_dir_finalize (node);
+ }
+ treefs_node_finalize (node);
+ treefs_free_node (node);
+}
diff --git a/libtreefs/fsys-startup.c b/libtreefs/fsys-startup.c
new file mode 100644
index 00000000..fcee3535
--- /dev/null
+++ b/libtreefs/fsys-startup.c
@@ -0,0 +1,36 @@
+/* fsys startup RPC
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+error_t
+treefs_S_fsys_startup (mach_port_t child_boot_port, mach_port_t control_port,
+ mach_port_t *real, mach_msg_type_name_t *real_type)
+{
+ error_t err;
+ struct port_info *child_boot =
+ ports_check_port_type (child_boot_port, PT_TRANSBOOT);
+
+ assert (child_boot); /* XXX deal with exec server boot */
+ err = fshelp_handle_fsys_startup (child_boot, control_port, real, real_type);
+ ports_done_with_port (child_boot);
+
+ return err;
+}
diff --git a/libtreefs/fsys.c b/libtreefs/fsys.c
new file mode 100644
index 00000000..d6565bb9
--- /dev/null
+++ b/libtreefs/fsys.c
@@ -0,0 +1,127 @@
+/*
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "treefs.h"
+
+/* ---------------------------------------------------------------- */
+
+/* Create a basic node, with one reference and no user-specific fields
+ initialized, and return it in NODE */
+error_t
+treefs_create_node (struct treefs_fsys *fsys, struct treefs_node **node)
+{
+ struct treefs_node *n = malloc (sizeof (struct treefs_node));
+
+ if (n == NULL)
+ return ENOMEM;
+
+ n->fsys = fsys;
+ n->refs = 1;
+ n->light_refs = 0;
+ n->hooks = fsys->hooks;
+ n->children = NULL;
+ n->u = NULL;
+ pthread_mutex_init (&n->lock, NULL);
+ fshelp_init_trans_link (&n->active_trans);
+ fshelp_lock_init (&n->lock_box);
+
+ *node = n;
+ return 0;
+}
+
+/* Immediately destroy NODE, with no user-finalization. */
+error_t
+treefs_free_node (struct treefs_node *node)
+{
+ free (node);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Returns a new filesystem in FSYS. */
+error_t
+treefs_create_fsys (struct port_bucket *port_bucket,
+ treefs_hook_vector_t hook_overrides,
+ struct treefs_fsys **fsys)
+{
+ treefs_hook_vector_t hooks = treefs_default_hooks;
+
+ if (hook_overrides)
+ {
+ hooks = treefs_hooks_clone (hooks);
+ treefs_hooks_override (hooks, hooks_overrides);
+ }
+
+ *fsys =
+ ports_allocate_port (port_bucket, sizeof (struct trivfs_control),
+ treefs_fsys_port_class);
+ if (*fsys == NULL)
+ return ENOMEM;
+
+ pthread_mutex_init (&(*fsys)->lock, NULL);
+ (*fsys)->root = NULL;
+
+ (*fsys)->underlying_port = MACH_PORT_NULL;
+ bzero (&(*fsys)->underlying_stat, sizeof (struct stat));
+
+ (*fsys)->flags = treefs_default_flags;
+ (*fsys)->max_symlinks = treefs_default_max_symlinks;
+ (*fsys)->sync_interval = treefs_default_sync_interval;
+
+ (*fsys)->fs_type = treefs_default_fsys_type;
+ (*fsys)->fs_id = getpid();
+
+ (*fsys)->hooks = hooks;
+
+ (*fsys)->port_bucket = port_bucket;
+ (*fsys)->protid_ports_class = treefs_protid_port_class;
+
+ (*fsys)->u = NULL;
+
+ return 0;
+}
+
+
+void ACKACKACK()
+{
+ /* Create a fake root node to bootstrap the filesystem. */
+ err = treefs_create_node(*fsys, &fake_root);
+ if (err)
+ goto barf;
+
+ /* Remember stat info for the node we're mounted on. */
+ bzero (&(*fsys)->underlying_stat, sizeof (struct stat));
+ file_stat (realnode, &(*fsys)->underlying_stat);
+
+ /* Note that it points to *FSYS, but *FSYS's root doesn't point to it...
+ If the user wants this to be the real root, it's his responsility to
+ initialize if further and install it. */
+ fake_root->fsys = *fsys;
+ bcopy (&(*fsys)->underlying_stat, &fake_root->stat);
+ err = treefs_fsys_init (fake_root);
+ if (err)
+ goto barf;
+
+ /* See if the silly user has in fact done so. */
+ if ((*fsys)->root != fake_root)
+ treefs_free_node (fake_root);
+}
diff --git a/libtreefs/hooks.c b/libtreefs/hooks.c
new file mode 100644
index 00000000..b8ec103f
--- /dev/null
+++ b/libtreefs/hooks.c
@@ -0,0 +1,59 @@
+/* Functions for manipulating hook vectors.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "treefs.h"
+
+#define HV_SIZE (sizeof (void (*)()) * TREEFS_NUM_HOOKS)
+
+/* Returns a copy of the treefs hook vector HOOKS, or a zero'd vector if HOOKS
+ is NULL. If HOOKS is NULL, treefs_default_hooks is used. If a memory
+ allocation error occurs, NULL is returned. */
+treefs_hook_vector_t
+treefs_hooks_clone (treefs_hook_vector_t hooks)
+{
+ treefs_hook_vector_t clone = malloc (HV_SIZE);
+ if (clone != NULL)
+ {
+ if (hooks == NULL)
+ hooks = treefs_default_hooks;
+ bcopy (hooks, clone, HV_SIZE);
+ }
+ return clone;
+}
+
+/* Copies each non-NULL entry in OVERRIDES into HOOKS. */
+void
+treefs_hooks_override (treefs_hook_vector_t hooks,
+ treefs_hook_vector_t overrides)
+{
+ int num;
+ for (num = 0; num < TREEFS_NUM_HOOKS; num++)
+ if (overrides[num] != NULL)
+ hooks[num] = overrides[num];
+}
+
+/* Sets the hook NUM in HOOKS to HOOK. */
+void
+treefs_hooks_set (treefs_hook_vector_t hooks, unsigned num, void (*hook)())
+{
+ hooks[num] = hook;
+}
diff --git a/libtreefs/mdir.c b/libtreefs/mdir.c
new file mode 100644
index 00000000..ddc1516a
--- /dev/null
+++ b/libtreefs/mdir.c
@@ -0,0 +1,92 @@
+/* Low-level directory manipulation functions
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Low level dir management routines. If called on a non-dir, ENOTDIR is
+ returned. */
+
+/* Add CHILD to DIR as NAME, replacing any existing entry. If OLD_CHILD is
+ NULL, and NAME already exists in dir, EEXIST is returned, otherwise, any
+ previous child is replaced and returned in OLD_CHILD. DIR should be
+ locked. */
+error_t
+treefs_mdir_add (struct treefs_node *dir, char *name,
+ struct treefs_node *child, struct treefs_node **old_child)
+{
+ if (!treefs_node_isdir (dir))
+ return ENOTDIR;
+ else
+ {
+ if (dir->children == NULL)
+ {
+ dir->children = treefs_make_node_list ();
+ if (dir->children == NULL)
+ return ENOMEM;
+ }
+ return treefs_node_list_add (dir->children, name, child, old_child);
+ }
+}
+
+/* Remove any entry in DIR called NAME. If there is no such entry, ENOENT is
+ returned. If OLD_CHILD is non-NULL, any removed entry is returned in it.
+ DIR should be locked. */
+error_t
+treefs_mdir_remove (struct treefs_node *dir, char *name,
+ struct treefs_node *old_child)
+{
+ if (!treefs_node_isdir (dir))
+ return ENOTDIR;
+ else if (dir->children == NULL)
+ /* Normally this shouldn't happen. */
+ return ENOENT;
+ else
+ return treefs_node_list_remove (dir->children, name, old_child);
+}
+
+/* Returns in CHILD any entry called NAME in DIR, or NULL (and ENOENT) if
+ there isn't such. DIR should be locked. */
+error_t
+treefs_mdir_get (struct treefs_node *dir, char *name,
+ struct treefs_node **child)
+{
+ if (!treefs_node_isdir (dir))
+ return ENOTDIR;
+ else if (dir->children == NULL)
+ /* Normally this shouldn't happen. */
+ return ENOENT;
+ else
+ return treefs_node_list_get (dir->children, name, child);
+}
+
+/* Call FUN on each child of DIR; if FUN returns a non-zero value at any
+ point, stop iterating and return that value immediately. */
+error_t
+treefs_mdir_for_each (struct treefs_node *dir,
+ error_t (*fun)(char *name, struct treefs_node *child))
+{
+ if (!treefs_node_isdir (dir))
+ return ENOTDIR;
+ else if (dir->children == NULL)
+ /* Normally this shouldn't happen. */
+ return 0;
+ else
+ return treefs_node_list_for_each (dir->children, fun);
+}
diff --git a/libtreefs/mig-decls.h b/libtreefs/mig-decls.h
new file mode 100644
index 00000000..e17f6196
--- /dev/null
+++ b/libtreefs/mig-decls.h
@@ -0,0 +1,46 @@
+/* Type decls for mig-produced server stubs
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "treefs.h"
+
+/* For mig */
+typedef struct treefs_handle *treefs_handle_t;
+
+extern treefs_handle_t treefs_begin_using_handle_port(mach_port_t port);
+extern void treefs_end_using_handle_port (treefs_handle_t handle);
+
+#if defined(__USE_EXTERN_INLINES) || defined(TREEFS_DEFINE_EI)
+TREEFS_EI
+treefs_handle_t treefs_begin_using_handle_port(mach_port_t port)
+{
+ return
+ (struct treefs_handle *)
+ ports_lookup_port (0, port, treefs_fsys_port_class);
+}
+
+TREEFS_EI void
+treefs_end_using_handle_port (treefs_handle_t handle)
+{
+ if (handle != NULL)
+ ports_port_deref (&handle->pi);
+}
+#endif /* Use extern inlines. */
diff --git a/libtreefs/nlist.c b/libtreefs/nlist.c
new file mode 100644
index 00000000..326bb0f8
--- /dev/null
+++ b/libtreefs/nlist.c
@@ -0,0 +1,70 @@
+/* Functions for dealing with node lists
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "treefs.h"
+
+/* ---------------------------------------------------------------- */
+struct treefs_node_list
+{
+ unsigned short num_nodes, nodes_alloced;
+
+};
+
+/* Return a new node list, or NULL if a memory allocation error occurs. */
+struct treefs_node_list *
+treefs_make_node_list ()
+{
+ struct treefs_node_list *nl = malloc (sizeof (struct treefs_node_list));
+ if (!nl)
+ return NULL;
+
+ nl->nodes_alloced = 0;
+ nl->num_nodes = 0;
+
+ return nl;
+}
+
+/* Add NODE to LIST as NAME, replacing any existing entry. If OLD_NODE is
+ NULL, and an entry NAME already exists, EEXIST is returned, otherwise, any
+ previous child is replaced and returned in OLD_NODE. */
+error_t
+treefs_node_list_add (struct treefs_node_list *list, char *name,
+ struct treefs_node *node, struct treefs_node **old_node)
+{
+
+}
+
+/* Remove any entry in LIST called NAME. If there is no such entry, ENOENT is
+ returned. If OLD_NODE is non-NULL, any removed entry is returned in it. */
+error_t
+treefs_node_list_remove (struct treefs_node_list *list, char *name,
+ struct treefs_node **old_node)
+{
+}
+
+/* Returns in NODE any entry called NAME in LIST, or NULL (and ENOENT) if
+ there isn't such. */
+error_t
+treefs_node_list_get (struct treefs_node_list *list, char *name,
+ struct treefs_node **node)
+{
+}
diff --git a/libtreefs/node-hooks.c b/libtreefs/node-hooks.c
new file mode 100644
index 00000000..faffcd6a
--- /dev/null
+++ b/libtreefs/node-hooks.c
@@ -0,0 +1,176 @@
+/* Default hooks for nodes
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "treefs.h"
+
+/* ---------------------------------------------------------------- */
+/* These default hooks depend on stat information being correct. */
+
+/* Returns the type of NODE, as an S_IFMT value (e.g., S_IFDIR). The
+ default routine just looks at NODE's stat mode. */
+int
+_treefs_node_type (struct treefs_node *node)
+{
+ return node->stat.st_mode & S_IFMT;
+}
+
+/* Return TRUE if NODE is `unlinked' -- that is, can be deleted when all
+ (in-memory) references go away. */
+int
+_treefs_node_unlinked (struct treefs_node *node)
+{
+ return node->stat.st_nlinks == 0;
+}
+
+/* Changes the link count of NODE by CHANGE; if any error is returned, the
+ operation trying to change the link count will fail, so filesystems that
+ don't support real links can restrict it to 1 or 0. This is mostly used
+ by the in-core directory code when it makes a link. The default hook uses
+ the link field of NODE's stat entry. */
+error_t
+_treefs_node_mod_link_count (struct treefs_node *node, int change)
+{
+ node->stat.st_nlinks += change;
+}
+
+
+/* ---------------------------------------------------------------- */
+/* These default hooks depend on stat information being correct. */
+
+/* Returns the user and group that a newly started translator should be
+ authenticated as. The default just returns the owner/group of NODE. */
+error_t
+_treefs_node_get_trans_auth (struct treefs_node *node, uid_t *uid, gid_t *gid)
+{
+ *uid = node->stat.st_uid;
+ *gid = node->stat.st_gid;
+ return 0;
+}
+
+/* Check to see is the user identified by AUTH is permitted to do
+ operation OP on node NP. Op is one of S_IREAD, S_IWRITE, or S_IEXEC.
+ Return 0 if the operation is permitted and EACCES if not. */
+error_t
+_treefs_node_access (struct treefs_node *node,
+ int op, struct treefs_auth *auth)
+{
+ int gotit;
+ if (diskfs_auth_has_uid (auth, 0))
+ gotit = 1;
+ else if (auth->nuids == 0 && (node->stat.st_mode & S_IUSEUNK))
+ gotit = node->stat.st_mode & (op << S_IUNKSHIFT);
+ else if (!treefs_node_owned (node, auth))
+ gotit = node->stat.st_mode & op;
+ else if (treefs_auth_in_group (auth, node->stat.st_gid))
+ gotit = node->stat.st_mode & (op >> 3);
+ else
+ gotit = node->stat.st_mode & (op >> 6);
+ return gotit ? 0 : EACCES;
+}
+
+/* Check to see if the user identified by AUTH is permitted to do owner-only
+ operations on node NP; if so, return 0; if not, return EPERM. */
+error_t
+_treefs_node_owned (struct treefs_node *node, struct treefs_auth *auth)
+{
+ /* Permitted if the user is the owner, superuser, or if the user
+ is in the group of the file and has the group ID as their user
+ ID. (This last is colloquially known as `group leader'.) */
+ if (treefs_auth_has_uid (auth, node->stat.st_uid)
+ || treefs_auth_has_uid (auth, 0)
+ || (treefs_auth_in_group (auth, node->stat.st_gid)
+ && treefs_auth_has_uid (auth, node->stat.st_gid)))
+ return 0;
+ else
+ return EPERM;
+}
+
+/* ---------------------------------------------------------------- */
+
+error_t
+_treefs_node_init_stat (struct treefs_node *node, struct treefs_node *dir,
+ mode_t mode, struct treefs_auth *auth)
+{
+ if (auth->nuids)
+ node->stat.st_uid = auth->uids[0];
+ else
+ {
+ mode &= ~S_ISUID;
+ if (dir)
+ node->stat.st_uid = dir->stat.st_uid;
+ else
+ node->stat.st_uid = -1; /* XXX */
+ }
+
+ if (dir && diskfs_ingroup (dir->stat.st_gid, auth))
+ node->stat.st_gid = dir->stat.st_gid;
+ else if (auth->ngids)
+ node->stat.st_gid = auth->gids[0];
+ else
+ {
+ mode &= ~S_ISGID;
+ if (dir)
+ node->stat.st_gid = dir->stat.st_gid;
+ else
+ node->stat.st_gid = -1; /* XXX */
+ }
+
+ node->stat.st_rdev = 0;
+ node->stat.st_nlink = 0;
+ node->stat.st_mode = mode;
+
+ node->stat.st_blocks = 0;
+ node->stat.st_size = 0;
+ node->stat.st_flags = 0;
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Called when the new peropen structure PO is made for NODE, with the
+ authorization in AUTH, opened with the flags FLAGS. If an error is
+ returned, the open will fail with that error. The default hook does
+ explicit authorization checks against AUTH using treefs_node_access, and
+ otherwise does nothing. */
+error_t
+_treefs_init_peropen (struct treefs_node *node, struct treefs_peropen *po,
+ int flags, struct treefs_auth *auth)
+{
+ error_t err;
+
+ if (flags & O_READ)
+ err = treefs_node_access (node, S_IREAD, auth);
+ if (!err && (flags & O_EXEC))
+ err = treefs_node_access (node, S_IEXEC, auth);
+ if (!err && (flags & O_WRITE))
+ {
+ if (type == S_IFDIR)
+ err = EISDIR;
+ else if (auth->po->node->fsys->readonly)
+ err = EROFS;
+ else
+ err = treefs_node_access (node, S_IWRITE, auth);
+ }
+
+ return err;
+}
diff --git a/libtreefs/rights.c b/libtreefs/rights.c
new file mode 100644
index 00000000..f803b029
--- /dev/null
+++ b/libtreefs/rights.c
@@ -0,0 +1,96 @@
+/* Functions for making send rights in various ways
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Return in PORT a send right for a new protid, pointing at the peropen PO,
+ with rights initialized from AUTH. */
+error_t
+treefs_peropen_create_right (struct treefs_peropen *po,
+ struct treefs_auth *auth,
+ mach_port_t *port)
+{
+ struct treefs_node *node = po->node;
+ struct treefs_fsys *fsys = node->fsys;
+ struct treefs_handle *handle =
+ ports_allocate_port (fsys->port_bucket,
+ sizeof (struct treefs_handle),
+ fsys->handle_port_class);
+
+ if (handle == NULL)
+ return MACH_PORT_NULL;
+
+ handle->po = po;
+ po->refs++;
+ handle->auth = auth;
+ auth->refs++;
+
+ err = treefs_node_init_handle (node, handle);
+ if (err)
+ {
+ po->refs--;
+ auth->refs--;
+ }
+
+ *port = ports_get_right (handle);
+
+ return 0;
+}
+
+/* Return in PORT a send right for a new handle and a new peropen, pointing
+ at NODE, with rights initialized from AUTH. FLAGS and PARENT_PORT are used
+ to initialize the corresponding fields in the new peropen. */
+error_t
+treefs_node_create_right (struct treefs_node *node, int flags,
+ mach_port_t parent_port, struct treefs_auth *auth,
+ mach_port_t *port)
+{
+ struct treefs_peropen *po = malloc (sizeof (struct treefs_peropen));
+
+ if (po == NULL)
+ return ENOMEM;
+
+ /* Initialize the peropen structure. */
+ po->refs = 0;
+ po->node = node;
+ po->open_flags = flags;
+ po->user_lock_state = LOCK_UN;
+ po->parent_port = parent_port;
+ if (parent_port != MACH_PORT_NULL)
+ mach_port_mod_refs (mach_task_self (),
+ parent_port, MACH_PORT_RIGHT_SEND, 1);
+
+ treefs_node_ref (node);
+
+ err = treefs_node_init_peropen (node, po, flags, auth);
+ if (err)
+ goto puke;
+
+ err = treefs_peropen_create_right (po, auth, port);
+ if (err)
+ goto puke;
+
+ return 0;
+
+ puke:
+ treefs_node_unref (node);
+ free (po);
+ return err;
+}
diff --git a/libtreefs/s-dir.c b/libtreefs/s-dir.c
new file mode 100644
index 00000000..08c4eb99
--- /dev/null
+++ b/libtreefs/s-dir.c
@@ -0,0 +1,112 @@
+/* File_t rpc stubs for directories; see <hurd/fs.defs> for more info
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ Note that since the user overrides the builtin routines via hook vectors
+ instead of declaring his own stubs, it doesn't make a lot of sense to put
+ these routines in separate files like diskfs. This way should compile
+ faster; with dynamic libraries it won't matter in any case.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+error_t
+treefs_S_dir_notice_changes (struct treefs_protid *cred,
+ mach_port_t notify)
+{
+ if (cred == NULL)
+ return EOPNOTSUPP;
+ return treefs_s_dir_notice_changes (cred, notify);
+}
+
+error_t
+treefs_S_dir_link (struct treefs_protid *dir_cred,
+ struct treefs_protid *file_cred,
+ char *name)
+{
+ if (cred == NULL)
+ return EOPNOTSUPP;
+ return treefs_s_dir_link (dir_cred, file_cred, name);
+}
+
+error_t
+treefs_S_dir_lookup (struct treefs_protid *cred,
+ char *path, int flags, mode_t mode,
+ enum retry_type *retry, char *retry_name,
+ file_t *result, mach_msg_type_name_t *result_type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_dir_lookup (cred, path, flags, mode, retry, retry_name,
+ result, result_type);
+}
+
+error_t
+treefs_S_dir_mkdir (struct treefs_protid *cred, char *name, mode_t mode)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_dir_mkdir (cred, name, mode);
+}
+
+error_t
+treefs_S_dir_mkfile (struct treefs_protid *cred,
+ int flags, mode_t mode,
+ mach_port_t *newnode, mach_msg_type_name_t *newnode_type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_dir_mkfile (cred, flags, mode, newnode, newnode_type);
+}
+
+error_t
+treefs_S_dir_readdir (struct treefs_protid *cred,
+ char **data, unsigned *datacnt,
+ int entry, int num_entries,
+ vm_size_t bufsiz, int *amt)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_dir_readdir (cred, data, datacnt,
+ entry, num_entries, bufsiz, amt);
+}
+
+error_t
+treefs_S_dir_rename (struct treefs_protid *cred, char *name,
+ struct treefs_protid *to_cred, char *to_name)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_dir_rename (cred, name, to_cred, to_name);
+}
+
+error_t
+treefs_S_dir_rmdir (struct treefs_protid *cred, char *name)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_dir_rmdir (cred, name);
+}
+
+error_t
+treefs_S_dir_unlink (struct treefs_protid *cred, char *name)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_dir_unlink (cred, name);
+}
diff --git a/libtreefs/s-file.c b/libtreefs/s-file.c
new file mode 100644
index 00000000..c24d645e
--- /dev/null
+++ b/libtreefs/s-file.c
@@ -0,0 +1,235 @@
+/* File_t rpc stubs; see <hurd/fs.defs> for more info
+
+ Copyright (C) 1995, 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ Note that since the user overrides the builtin routines via hook vectors
+ instead of declaring his own stubs, it doesn't make a lot of sense to put
+ these routines in separate files (like diskfs). This way should compile
+ faster, with dynamic libraries it won't matter in any case.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+error_t
+treefs_S_file_check_access (struct treefs_protid *cred, int *type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_check_access (cred, type);
+}
+
+error_t
+treefs_S_file_chauthor (struct treefs_protid *cred, uid_t author)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_chauthor (cred, author);
+}
+
+error_t
+treefs_S_file_chflags (struct treefs_protid *cred, int flags)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_chflags (cred, flags);
+}
+
+error_t
+treefs_S_file_notice_changes (struct treefs_protid *cred, mach_port_t notify)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_notice_changes (cred, notify);
+}
+
+error_t
+treefs_S_file_chmod (struct treefs_protid *cred, mode_t mode)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ mode &= ~(S_IFMT | S_ISPARE | S_ITRANS);
+ return treefs_s_file_chmod (cred, mode);
+}
+
+error_t
+treefs_S_file_chown (struct treefs_protid *cred, uid_t uid, gid_t gid)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_chown (cred, uid, gid);
+}
+
+error_t
+treefs_S_file_exec (struct treefs_protid *cred,
+ task_t task, int flags,
+ char *argv, unsigned argv_len,
+ char *envp, unsigned envp_len,
+ mach_port_t *fds, unsigned fds_len,
+ mach_port_t *ports, unsigned ports_len,
+ int *ints, unsigned ints_len,
+ mach_port_t *dealloc, unsigned dealloc_len,
+ mach_port_t *destroy, unsigned destroy_len)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_exec (cred, task, flags, argv, argv_len, envp, envp_len,
+ fds, fds_len, ports, ports_len, ints, ints_len,
+ dealloc, dealloc_len, destroy, destroy_len);
+}
+
+error_t
+treefs_S_file_get_translator (struct treefs_protid *cred,
+ char **trans, unsigned *trans_len)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_get_translator (cred, trans, trans_len);
+}
+
+error_t
+treefs_S_file_get_translator_cntl (struct treefs_protid *cred,
+ mach_port_t *ctl,
+ mach_msg_type_name_t *ctl_type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_get_translator_cntl (cred, ctl, ctl_type);
+}
+
+error_t
+treefs_S_file_getcontrol (struct treefs_protid *cred,
+ mach_port_t *control,
+ mach_msg_type_name_t *control_type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_getcontrol (cred, control, control_type);
+}
+
+error_t
+treefs_S_file_getfh (struct treefs_protid *cred,
+ char **data, unsigned *data_len)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_getfh (cred, data, data_len);
+}
+
+error_t
+treefs_S_file_getlinknode (struct treefs_protid *cred,
+ file_t *port, mach_msg_type_name_t *port_type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_t (cred, port, port_type);
+}
+
+error_t
+treefs_S_file_invoke_translator (struct treefs_protid *cred,
+ int flags,
+ retry_type *retry, char *retry_name,
+ mach_port_t *retry_port,
+ mach_msg_type_name_t *retry_port_type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_invoke_translator (cred, flags, retry, retry_name,
+ retry_port, retry_port_type);
+}
+
+error_t
+treefs_S_file_lock_stat (struct treefs_protid *cred,
+ int *self_status, int *other_status)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_lock_stat (cred, self_status, other_status);
+}
+
+error_t
+treefs_S_file_lock (struct treefs_protid *cred, int flags)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_lock (cred, flags);
+}
+
+error_t
+treefs_S_file_pathconf (struct treefs_protid *cred, int name, int *value)
+ {
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_pathconf (cred, name, value);
+}
+
+error_t
+treefs_S_file_set_translator (struct treefs_protid *cred,
+ int passive_flags, int active_flags,
+ int killtrans_flags,
+ char *passive, unsigned passive_len,
+ fsys_t active)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_set_translator (cred, passive_flags, active_flags,
+ killtrans_flags, passive, passive_len,
+ active);
+}
+
+error_t
+treefs_S_file_statfs (struct treefs_protid *cred, fsys_statfsbuf_t *statbuf)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_statfs (cred, statbuf);
+}
+
+error_t
+treefs_S_file_sync (struct treefs_protid *cred, int wait)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_sync (cred, wait);
+}
+
+error_t
+treefs_S_file_syncfs (struct treefs_protid *cred, int wait, int recurse)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_syncfs (cred, wait, recurse);
+}
+
+error_t
+treefs_S_file_set_size (struct treefs_protid *cred, off_t size)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (size < 0)
+ return EINVAL;
+ return treefs_s_file_set_size (cred, size);
+}
+
+error_t
+treefs_S_file_utimes (struct treefs_protid *cred,
+ time_value_t atime, time_value_t mtime)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_file_utimes (cred, atime, mtime);
+}
diff --git a/libtreefs/s-fsys.c b/libtreefs/s-fsys.c
new file mode 100644
index 00000000..e218a963
--- /dev/null
+++ b/libtreefs/s-fsys.c
@@ -0,0 +1,76 @@
+/* fsys_t rpc stubs; see <hurd/fsys.defs> for more info
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "treefs.h"
+
+#define CALL_FSYS_HOOK(hook, fsys_port, args...) \
+{ \
+ error_t _err; \
+ struct treefs_fsys *_fsys = (struct treefs_fsys *) \
+ ports_lookup_port (0, fsys_port, treefs_fsys_port_class); \
+ if (!_fsys) \
+ return EOPNOTSUPP; \
+ err = hook(_fsys , ##args); \
+ ports_port_deref (&_fsys->pi); \
+ return _err; \
+}
+
+error_t
+treefs_S_fsys_getroot (fsys_t fsys_port, mach_port_t dotdot,
+ uid_t *uids, unsigned nuids,
+ gid_t *gids, unsigned ngids,
+ int flags, retry_type *retry, char *retry_name,
+ file_t *result, mach_msg_type_name_t *result_type)
+{
+ CALL_FSYS_HOOK(treefs_s_fsys_getroot, fsys_port, dotdot, uids, nuids, gids,
+ ngids, flags, retry, retry_name, result, result_type);
+}
+
+error_t
+treefs_S_fsys_set_options (fsys_t fsys_port,
+ char *data, unsigned len, int recurse)
+{
+ CALL_FSYS_HOOK(treefs_s_fsys_set_options, fsys_port, data, len, recurse);
+}
+
+error_t
+treefs_S_fsys_goaway (fsys_t fsys_port, int flags)
+{
+ CALL_FSYS_HOOK(treefs_s_fsys_goaway, fsys_port, flags);
+}
+
+error_t
+treefs_S_fsys_getfile (mach_port_t fsys_port,
+ uid_t *gen_uids, unsigned ngen_uids,
+ gid_t *gen_gids, unsigned ngen_gids,
+ char *handle, unsigned handle_len,
+ mach_port_t *file, mach_msg_type_name_t *file_type)
+{
+ CALL_FSYS_HOOK(treefs_s_fsys_getfile, fsys_port, gen_uids, ngen_uids,
+ gen_gids, ngen_gids, handle, handle_len, file, file_type);
+}
+
+error_t
+treefs_S_fsys_syncfs (fsys_t fsys_port, int wait, int recurse)
+{
+ CALL_FSYS_HOOK(treefs_s_fsys_syncfs, fsys_port, wait, recurse);
+}
diff --git a/libtreefs/s-io.c b/libtreefs/s-io.c
new file mode 100644
index 00000000..6fdc504a
--- /dev/null
+++ b/libtreefs/s-io.c
@@ -0,0 +1,284 @@
+/* io_t rpc stubs; see <hurd/io.defs> for more info
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ Note that since the user overrides the builtin routines via hook vectors
+ instead of declaring his own stubs, it doesn't make a lot of sense to put
+ these routines in separate files (like diskfs). This way should compile
+ faster, with dynamic libraries it won't matter in any case.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+error_t
+treefs_S_io_get_icky_async_id (struct treefs_protid *cred,
+ mach_port_t *id, mach_msg_type_name_t *id_type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_get_icky_async_id (cred, id, id_type);
+}
+
+error_t
+treefs_S_io_async (struct treefs_protid *cred,
+ mach_port_t notify,
+ mach_port_t *id, mach_msg_type_name_t *id_type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_async (cred, notify, id, id_type);
+}
+
+error_t
+treefs_S_io_duplicate (struct treefs_protid *cred,
+ mach_port_t *port,
+ mach_msg_type_name_t *port_type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_duplicate (cred, port, port_type);
+}
+
+error_t
+treefs_S_io_get_conch (struct treefs_protid *cred)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_get_conch (cred);
+}
+
+error_t
+treefs_S_io_interrupt (struct treefs_protid *cred)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_interrupt (cred);
+}
+
+error_t
+treefs_S_io_map_cntl (struct treefs_protid *cred,
+ memory_object_t *ctlobj,
+ mach_msg_type_name_t *ctlobj_type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_map_cntl (cred, ctlobj, ctlobj_type);
+}
+
+error_t
+treefs_S_io_map (struct treefs_protid *cred,
+ memory_object_t *rdobj, mach_msg_type_name_t *rd_type,
+ memory_object_t *wrobj, mach_msg_type_name_t *wr_type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_map (cred, rdobj, rd_type, wrobj, wr_type);
+}
+
+error_t
+treefs_S_io_get_openmodes (struct treefs_protid *cred, int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_get_openmodes (cred, bits);
+}
+
+error_t
+treefs_S_io_clear_some_openmodes (struct treefs_protid *cred, int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_clear_some_openmodes (cred, bits);
+}
+
+error_t
+treefs_S_io_set_some_openmodes (struct treefs_protid *cred, int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_set_some_openmodes (cred, bits);
+}
+
+error_t
+treefs_S_io_set_all_openmodes (struct treefs_protid *cred, int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_set_all_openmodes (cred, bits);
+}
+
+error_t
+treefs_S_io_get_owner (struct treefs_protid *cred, pid_t *owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_get_owner (cred, owner);
+}
+
+error_t
+treefs_S_io_mod_owner (struct treefs_protid *cred, pid_t owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_mod_owner (cred, owner);
+}
+
+error_t
+treefs_S_io_prenotify (struct treefs_protid *cred,
+ vm_offset_t start, vm_offset_t end)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_prenotify (cred, start, end);
+}
+
+error_t
+treefs_S_io_read (struct treefs_protid *cred,
+ char **data,
+ mach_msg_type_number_t *data_len,
+ off_t offset,
+ mach_msg_type_number_t max)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_read (cred, data, data_len, offset, max);
+}
+
+error_t
+treefs_S_io_readable (struct treefs_protid *cred,
+ mach_msg_type_number_t *amount)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_readable (cred, amount);
+}
+
+error_t
+treefs_S_io_reauthenticate (struct treefs_protid *cred, mach_port_t rend_port)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_reauthenticate (cred, rend_port);
+}
+
+error_t
+treefs_S_io_release_conch (struct treefs_protid *cred)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_release_conch (cred);
+}
+
+error_t
+treefs_S_io_restrict_auth (struct treefs_protid *cred,
+ mach_port_t *newport,
+ mach_msg_type_name_t *newport_type,
+ uid_t *uids, unsigned nuids,
+ gid_t *gids, unsigned ngids)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_restrict_auth (cred, newport, newport_type,
+ uids, nuids, gids, ngids);
+}
+
+error_t
+treefs_S_io_seek (struct treefs_protid *cred,
+ off_t offset, int whence, off_t *new_offset)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_seek (cred, offset, whence, new_offset);
+}
+
+error_t
+treefs_S_io_select (struct treefs_protid *cred, int *type, int *tag)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_select (cred, type, tag);
+}
+
+error_t
+treefs_S_io_sigio (struct treefs_protid *cred)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_sigio (cred);
+}
+
+error_t
+treefs_S_io_stat (struct treefs_protid *cred, io_statbuf_t *statbuf)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_statbuf_t (cred, statbuf);
+}
+
+error_t
+treefs_S_io_readsleep (struct treefs_protid *cred)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_readsleep (cred);
+}
+
+error_t
+treefs_S_io_eofnotify (struct treefs_protid *cred)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_eofnotify (cred);
+}
+
+error_t
+treefs_S_io_postnotify (struct treefs_protid *cred,
+ vm_offset_t start, vm_offset_t end)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_postnotify (cred, start, end);
+}
+
+error_t
+treefs_S_io_readnotify (struct treefs_protid *cred)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_readnotify (cred);
+}
+
+error_t
+treefs_S_io_server_version (struct treefs_protid *cred,
+ char *server_name,
+ int *major, int *minor, int *edit)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_server_version (cred, server_version, major, minor, edit);
+}
+
+error_t
+treefs_S_io_write (struct treefs_protid *cred,
+ char *data, mach_msg_type_number_t data_len,
+ off_t offset, mach_msg_type_number_t *amount)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return treefs_s_io_write (cred, data, data_len, offset, amount);
+}
diff --git a/libtreefs/trans-help.c b/libtreefs/trans-help.c
new file mode 100644
index 00000000..7d3841c0
--- /dev/null
+++ b/libtreefs/trans-help.c
@@ -0,0 +1,129 @@
+/* Helper routines for dealing with translators
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "trivfs.h"
+
+/* Return the active translator control port for NODE. If there is no
+ translator, active or passive, MACH_PORT_NULL is returned in CONTROL_PORT.
+ If there is a translator, it is started if necessary, and returned in
+ CONTROL_PORT. *DIR_PORT should be a port right to use as the new
+ translators parent directory. If it is MACH_PORT_NULL, a port is created
+ from DIR and PARENT_PORT and stored in *DIR_PORT; otherwise DIR and
+ PARENT_PORT are not used. Neither NODE or DIR should be locked when
+ calling this function. */
+error_t
+treefs_node_get_active_trans (struct treefs_node *node,
+ struct treefs_node *dir,
+ mach_port_t parent_port,
+ mach_port_t *control_port,
+ mach_port_t *dir_port)
+{
+ /* Fill in dir_port */
+ void make_dir_port ()
+ {
+ pthread_mutex_lock (&dir->lock);
+ *dir_port = treefs_node_make_right (dir, 0, parent_port, 0);
+ mach_port_insert_right (mach_task_self (),
+ *dir_port, *dir_port, MACH_MSG_TYPE_MAKE_SEND);
+ pthread_mutex_unlock (&dir->lock);
+ }
+
+ pthread_mutex_lock (&node->active_trans.lock);
+
+ if (node->active_trans.control != MACH_PORT_NULL)
+ {
+ mach_port_t control = node->active_trans.control;
+ mach_port_mod_refs (mach_task_self (), control,
+ MACH_PORT_RIGHT_SEND, 1);
+ pthread_mutex_unlock (&node->active_trans.lock);
+
+ /* Now we have a copy of the translator port that isn't
+ dependent on the translator lock itself. Relock
+ the directory, make a port from it, and then call
+ fsys_getroot. */
+
+ if (*dir_port == MACH_PORT_NULL)
+ make_dir_port ();
+
+ *control_port = control;
+
+ return 0;
+ }
+
+ pthread_mutex_unlock (&node->active_trans.lock);
+
+ /* If we get here, then we have no active control port.
+ Check to see if there is a passive translator, and if so
+ repeat the translator check. */
+ pthread_mutex_lock (&node->lock);
+ if (!node->istranslated)
+ {
+ *control_port = MACH_PORT_NULL;
+ return 0;
+ }
+
+ err = treefs_node_get_translator (node, trans, &trans_len);
+ if (err == E2BIG)
+ {
+ trans = alloca (trans_len);
+ err = treefs_node_get_translator (node, trans, &trans_len);
+ }
+ if (err)
+ {
+ pthread_mutex_unlock (&node->lock);
+ return err;
+ }
+
+ if (*dir_port == MACH_PORT_NULL)
+ {
+ pthread_mutex_unlock (&node->lock);
+ make_dir_port ();
+ pthread_mutex_lock (&node->lock);
+ }
+
+ /* Try starting the translator (this unlocks NODE). */
+ err = treefs_start_translator (node, trans, trans_len, *dir_port);
+ if (err)
+ return err;
+
+ /* Try again now that we've started the translator... This call
+ should be tail recursive. */
+ return
+ treefs_node_get_active_trans (node, dir, parent_port,
+ control_port, dir_port);
+}
+
+/* Drop the active translator CONTROL_PORT on NODE, unless it's no longer the
+ current active translator, in which case just drop a reference to it. */
+void
+treefs_node_drop_active_trans (struct treefs_node *node,
+ mach_port_t control_port)
+{
+ pthread_mutex_lock (&node->active_trans.lock);
+ /* Only zero the control port if it hasn't changed. */
+ if (node->active_trans.control == control)
+ fshelp_translator_drop (&node->active_trans);
+ pthread_mutex_unlock (&node->active_trans.lock);
+
+ /* And we're done with this port. */
+ mach_port_deallocate (mach_task_self (), control_port);
+}
diff --git a/libtreefs/trans-start.c b/libtreefs/trans-start.c
new file mode 100644
index 00000000..b27c38f4
--- /dev/null
+++ b/libtreefs/trans-start.c
@@ -0,0 +1,66 @@
+/* Starting a passive translator
+
+ Copyright (C) 1994, 1995, 1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "treefs.h"
+
+#include <fcntl.h>
+
+int fshelp_transboot_port_type = PT_TRANSBOOT;
+
+/* Start the translator TRANS (of length TRANS_LEN) on NODE, which should be
+ locked, and will be unlocked when this function returns. PARENT_PORT is
+ a send right to use as the parent port passed to the translator. */
+error_t
+_treefs_node_start_translator (struct treefs_node *node,
+ char *trans, unsigned trans_len,
+ file_t parent_port)
+{
+ error_t err;
+ int mode = O_READ | O_EXEC;
+ struct treefs_auth *auth;
+ file_t node_port;
+ uid_t uid, gid;
+
+ err = treefs_node_get_trans_auth (node, &auth);
+ if (err)
+ return err;
+
+ if (!node->fsys->readonly && treefs_node_type (node) == S_IFREG)
+ mode |= O_WRITE;
+
+ /* Create the REALNODE port for the new filesystem. */
+ node_port = treefs_node_make_right (node, mode, parent_port, auth);
+ mach_port_insert_right (mach_task_self (), node_port, node_port,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+
+ pthread_mutex_unlock (&node->lock);
+
+ /* XXXX Change libfshelp so that it take more than 1 uid/gid? */
+ uid = auth->nuids > 0 ? auth->uids[0] : -1;
+ gid = auth->ngids > 0 ? auth->gids[0] : -1;
+
+ /* XXX this should use fshelp_start_translator_long. */
+ err =
+ fshelp_start_translator (&node->active_trans, NULL, trans, trans_len,
+ parent_port, node_port, uid, gid);
+
+ treefs_node_auth_unref (node, auth);
+
+ return err;
+}
diff --git a/libtreefs/treefs-hooks.h b/libtreefs/treefs-hooks.h
new file mode 100644
index 00000000..3af4a546
--- /dev/null
+++ b/libtreefs/treefs-hooks.h
@@ -0,0 +1,401 @@
+/* Hooks in libtreefs (also see "treefs-s-hooks.h")
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __TREEFS_HOOKS_H__
+#define __TREEFS_HOOKS_H__
+
+#include "treefs.h"
+
+/* ---------------------------------------------------------------- */
+
+/* Hook indices */
+enum
+{
+ /* file rpcs */
+ TREEFS_HOOK_S_FILE_EXEC, TREEFS_HOOK_S_FILE_CHOWN,
+ TREEFS_HOOK_S_FILE_CHAUTHOR, TREEFS_HOOK_S_FILE_CHMOD,
+ TREEFS_HOOK_S_FILE_CHFLAGS, TREEFS_HOOK_S_FILE_UTIMES,
+ TREEFS_HOOK_S_FILE_SET_SIZE, TREEFS_HOOK_S_FILE_LOCK,
+ TREEFS_HOOK_S_FILE_LOCK_STAT, TREEFS_HOOK_S_FILE_ACCESS,
+ TREEFS_HOOK_S_FILE_NOTICE, TREEFS_HOOK_S_FILE_SYNC,
+ TREEFS_HOOK_S_FILE_GET_LINK_NODE,
+
+ /* io rpcs */
+ TREEFS_HOOK_S_IO_WRITE, TREEFS_HOOK_S_IO_READ, TREEFS_HOOK_S_IO_SEEK,
+ TREEFS_HOOK_S_IO_READABLE, TREEFS_HOOK_S_IO_SET_ALL_OPENMODES,
+ TREEFS_HOOK_S_IO_GET_OPENMODES, TREEFS_HOOK_S_IO_SET_SOME_OPENMODES,
+ TREEFS_HOOK_S_IO_CLEAR_SOME_OPENMODES, TREEFS_HOOK_S_IO_ASYNC,
+ TREEFS_HOOK_S_IO_MOD_OWNER, TREEFS_HOOK_S_IO_GET_OWNER,
+ TREEFS_HOOK_S_IO_GET_ICKY_ASYNC_ID, TREEFS_HOOK_S_IO_SELECT,
+ TREEFS_HOOK_S_IO_STAT, TREEFS_HOOK_S_IO_REAUTHENTICATE,
+ TREEFS_HOOK_S_IO_RESTRICT_AUTH, TREEFS_HOOK_S_IO_DUPLICATE,
+ TREEFS_HOOK_S_IO_SERVER_VERSION, TREEFS_HOOK_S_IO_MAP,
+ TREEFS_HOOK_S_IO_MAP_CNTL, TREEFS_HOOK_S_IO_RELEASE_CONCH,
+ TREEFS_HOOK_S_IO_EOFNOTIFY, TREEFS_HOOK_S_IO_PRENOTIFY,
+ TREEFS_HOOK_S_IO_POSTNOTIFY, TREEFS_HOOK_S_IO_READNOTIFY,
+ TREEFS_HOOK_S_IO_READSLEEP, TREEFS_HOOK_S_IO_SIGIO,
+
+ /* directory rpcs */
+ TREEFS_HOOK_S_DIR_LOOKUP, TREEFS_HOOK_S_DIR_READDIR, TREEFS_HOOK_S_DIR_MKDIR,
+ TREEFS_HOOK_S_DIR_RMDIR, TREEFS_HOOK_S_DIR_UNLINK, TREEFS_HOOK_S_DIR_LINK,
+ TREEFS_HOOK_S_DIR_RENAME, TREEFS_HOOK_S_DIR_MKFILE,
+ TREEFS_HOOK_S_DIR_NOTICE_CHANGES,
+
+ /* filesystem rpcs */
+ TREEFS_HOOK_S_FSYS_GETROOT, TREEFS_HOOK_S_FSYS_SET_OPTIONS,
+ TREEFS_HOOK_S_FSYS_SYNCFS, TREEFS_HOOK_S_FSYS_GETFILE, TREEFS_S_FSYS_GOAWAY,
+
+ /* Non-rpc fsys hooks */
+ TREEFS_HOOK_FSYS_CREATE_NODE, TREEFS_HOOK_FSYS_DESTROY_NODE,
+ TREEFS_HOOK_FSYS_GET_ROOT,
+
+ /* Node hooks */
+ TREEFS_HOOK_NODE_TYPE,
+ TREEFS_HOOK_NODE_UNLINKED, TREEFS_HOOK_NODE_MOD_LINK_COUNT,
+ TREEFS_HOOK_DIR_LOOKUP, TREEFS_HOOK_DIR_NOENT,
+ TREEFS_HOOK_DIR_CREATE_CHILD, TREEFS_HOOK_DIR_LINK, TREEFS_HOOK_DIR_UNLINK,
+ TREEFS_HOOK_NODE_OWNED, TREEFS_HOOK_NODE_ACCESS,
+ TREEFS_HOOK_NODE_GET_SYMLINK, TREEFS_HOOK_NODE_GET_PASSIVE_TRANS,
+ TREEFS_HOOK_NODE_START_TRANSLATOR, TREEFS_HOOK_NODE_GET_TRANS_AUTH,
+ TREEFS_HOOK_NODE_DROP, TREEFS_HOOK_NODE_INIT, TREEFS_HOOK_DIR_INIT,
+ TREEFS_HOOK_NODE_INIT_PEROPEN, TREEFS_HOOK_NODE_INIT_HANDLE,
+ TREEFS_HOOK_NODE_FINALIZE, TREEFS_HOOK_DIR_FINALIZE,
+ TREEFS_HOOK_NODE_FINALIZE_PEROPEN, TREEFS_HOOK_NODE_FINALIZE_HANDLE,
+
+ /* Reference counting support */
+ TREEFS_HOOK_NODE_NEW_REFS, TREEFS_HOOK_NODE_LOST_REFS,
+ TREEFS_HOOK_NODE_TRY_DROPPING_WEAK_REFS,
+
+ TREEFS_NUM_HOOKS
+};
+
+/* ---------------------------------------------------------------- */
+/* Hook calling/defining macros */
+
+/* Call the hook number HOOK in the hook vector HOOKS, whose function is of
+ type TYPE, with the args ARGS (my this is a useful comment). */
+#define TREEFS_CALL_HOOK(hooks, hook, type, args...) \
+ ((type *)(hooks)[hook])(args)
+#define TREEFS_CALL_HANDLE_HOOK(h, hook, type, args...) \
+ ({struct treefs_handle *_tfs_cn_h = (h); \
+ TREEFS_CALL_HOOK(_tfs_cn_h->po->node->hooks,hook,type, \
+ _tfs_cn_h , ##args);})
+#define TREEFS_CALL_NODE_HOOK(node, hook, type, args...) \
+ ({struct treefs_node *_tfs_cn_node = (node); \
+ TREEFS_CALL_HOOK(_tfs_cn_node->hooks,hook,type, _tfs_cn_node , ##args);})
+#define TREEFS_CALL_FSYS_HOOK(fsys, hook, type, args...) \
+ ({struct treefs_fsys *_tfs_cn_fsys = (fsys); \
+ TREEFS_CALL_HOOK(_tfs_cn_fsys->hooks,hook,type, _tfs_cn_fsys , ##args);})
+
+/* Shorthand form of TREEFS_CALL_*_HOOK (only used here). */
+#define _TREEFS_CHH(h, hook_id, type_id, args...) \
+ TREEFS_CALL_HANDLE_HOOK(h, TREEFS_HOOK_##hook_id, treefs_##type_id##_t , ##args)
+#define _TREEFS_CNH(node, hook_id, type_id, args...) \
+ TREEFS_CALL_NODE_HOOK(node, TREEFS_HOOK_##hook_id, treefs_##type_id##_t , ##args)
+#define _TREEFS_CFH(fsys, hook_id, type_id, args...) \
+ TREEFS_CALL_FSYS_HOOK(fsys, TREEFS_HOOK_##hook_id, treefs_##type_id##_t , ##args)
+
+/* Forward declare some structures used before definition. */
+struct treefs_node;
+struct treefs_fsys;
+struct treefs_auth;
+struct treefs_handle;
+struct treefs_peropen;
+
+/* Shorthand for declaring the various hook types (each hook has an
+ associated type so that a user can type-check his hook routine). */
+#define DNH(name_sym, ret_type, argtypes...) \
+ typedef ret_type treefs_##name_sym##_t (struct treefs_node * , ##argtypes);
+#define DFH(name_sym, ret_type, argtypes...) \
+ typedef ret_type treefs_##name_sym##_t (struct treefs_fsys * , ##argtypes);
+
+/* ---------------------------------------------------------------- */
+/* Non RPC hooks */
+
+/* Called to get the root node of the a filesystem, with a reference,
+ returning it in ROOT, or return an error if it can't be had. The default
+ hook just returns FSYS->root or an error if it's NULL. Note that despite
+ the similar name, this is very different from fsys_s_getroot! FSYS must
+ not be locked. */
+DFH(fsys_get_root, error_t, struct treefs_node **root)
+#define treefs_fsys_get_root(fsys, args...) \
+ _TREEFS_CFH(fsys, FSYS_GET_ROOT, fsys_get_root , ##args)
+
+/* Called on a filesystem to create a new node in that filesystem, returning
+ it, with one reference, in NODE. DIR, if non-NULL, is the nominal parent
+ directory, and MODE and AUTH are the desired mode and user info
+ respectively. This hook should also call node_init_stat & node_init to
+ initialize the various user bits. */
+DFH(fsys_create_node, error_t,
+ struct treefs_node *dir, mode_t mode, struct treefs_auth *auth,
+ struct treefs_node **node)
+#define treefs_fsys_create_node(fsys, args...) \
+ _TREEFS_CFH(fsys, FSYS_CREATE_NODE, fsys_create_node , ##args)
+
+/* Called on a filesystem to destroy a node in that filesystem. This call
+ should *really* destroy it -- i.e., it's only called once all references
+ are gone. */
+DFH(fsys_destroy_node, void, struct treefs_node *node)
+#define treefs_fsys_destroy_node(fsys, node) \
+ _TREEFS_CFH(fsys, FSYS_DESTROY_NODE, fsys_destroy_node , ##args)
+
+/* Returns the type of NODE, as an S_IFMT value (e.g., S_IFDIR). The
+ default routine just looks at NODE's stat mode. */
+DNH(node_type, int);
+#define treefs_node_type(node, args...) \
+ _TREEFS_CNH(node, NODE_TYPE, node_type , ##args)
+
+#define treefs_node_isdir(node) (treefs_node_type(node) == S_IFDIR)
+#define treefs_node_isreg(node) (treefs_node_type(node) == S_IFREG)
+
+/* Return TRUE if NODE is `unlinked' -- that is, can be deleted when all
+ (in-memory) references go away. */
+DNH(node_unlinked, int);
+#define treefs_node_unlinked(node, args...) \
+ _TREEFS_CNH(node, NODE_UNLINKED, node_unlinked , ##args)
+
+/* Changes the link count of NODE by CHANGE; if any error is returned, the
+ operation trying to change the link count will fail, so filesystems that
+ don't support real links can restrict it to 1 or 0. This is mostly used
+ by the in-core directory code when it makes a link. The default hook uses
+ the link field of NODE's stat entry. */
+DNH(node_mod_link_count, error_t, int change);
+#define treefs_node_mod_link_count(node, args...) \
+ _TREEFS_CNH(node, NODE_MOD_LINK_COUNT, node_mod_link_count , ##args)
+
+/* Lookup NAME in NODE, returning the result in CHILD; AUTH should be used to
+ do authentication. If FLAGS contains O_CREAT, and NAME is not found, then
+ an entry should be created with a mode of CREATE_MODE (which includes the
+ S_IFMT bits, e.g., S_IFREG means a normal file), unless O_EXCL is also
+ set, in which case EEXIST should be returned. Possible special errors
+ returned include: EAGAIN -- result would be the parent of our filesystem
+ root. Note that is a single-level lookup, unlike treefs_s_dir_lookup. */
+DNH(dir_lookup, error_t,
+ char *name, struct treefs_auth *auth, int flags, int create_mode,
+ struct treefs_node **child)
+#define treefs_dir_lookup(dir, args...) \
+ _TREEFS_CNH(dir, DIR_LOOKUP, dir_lookup , ##args)
+
+/* Called by the default implementation of treefs_dir_lookup (and possibly
+ user-versions as well) when a directory lookup returns ENOENT, before a
+ new node is created. This hook may return the desire node in CHILD and
+ return 0, or return an error code. Note that a returned node need not
+ actually be in the directory DIR, and indeed may be anonymous. */
+DNH(dir_noent, error_t,
+ char *name, struct treefs_auth *auth, int flags, int create_mode,
+ struct treefs_node **child);
+#define treefs_dir_noent(dir, args...) \
+ _TREEFS_CNH(dir, DIR_NOENT, dir_noent , ##args)
+
+/* Return in CHILD a new node with one reference, presumably a possible child
+ of DIR, with a mode MODE. All attempts to create a new node go through
+ this hook, so it may be overridden to easily control creation (e.g.,
+ replacing it with a hook that always returns EPERM). Note that this
+ routine doesn't actually enter the child into the directory, or give the
+ node a non-zero link count, that should be done by the caller. */
+DNH(dir_create_child, error_t,
+ mode_t mode, struct treefs_auth *auth, struct treefs_node **child);
+#define treefs_dir_create_child(dir, args...) \
+ _TREEFS_CNH(dir, DIR_CREATE_CHILD, dir_create_child , ##args)
+
+/* Link the node CHILD into DIR as NAME, using AUTH to check authentication.
+ DIR should be locked and CHILD shouldn't be. The default hook puts it
+ into DIR's in-core directory, and uses a reference to CHILD (this way, a
+ node can be linked to both in-core and out-of-core directories and the
+ permanent link-count will be right). */
+DNH(dir_link, error_t,
+ char *name, struct treefs_node *child, struct treefs_auth *auth)
+#define treefs_dir_link(dir, args...) \
+ _TREEFS_CNH(dir, DIR_LINK, dir_link , ##args)
+
+/* Remove the entry NAME from DIR, using AUTH to check authentication. DIR
+ should be locked. The default hook removes NAME from DIR's in-core
+ directory. */
+DNH(dir_unlink, error_t, char *name, struct treefs_auth *auth)
+#define treefs_dir_unlink(dir, args...) \
+ _TREEFS_CNH(dir, DIR_UNLINK, dir_unlink , ##args)
+
+/* Check to see if the user identified by AUTH is permitted to do owner-only
+ operations on node NP; if so, return 0; if not, return EPERM. */
+DNH(node_owned, error_t, struct treefs_auth *auth)
+#define treefs_node_owned(node, args...) \
+ _TREEFS_CNH(node, NODE_OWNED, node_owned , ##args)
+
+/* Check to see is the user identified by AUTH is permitted to do
+ operation OP on node NP. Op is one of S_IREAD, S_IWRITE, or S_IEXEC.
+ Return 0 if the operation is permitted and EACCES if not. */
+DNH(node_access, error_t, int opt, struct treefs_auth *auth)
+#define treefs_node_access(node, args...) \
+ _TREEFS_CNH(node, NODE_ACCESS, node_access , ##args)
+
+/* NODE now has no more references; clean all state. The
+ _treefs_node_refcnt_lock must be held, and will be released upon return.
+ NODE must be locked. */
+DNH(node_drop, error_t);
+#define treefs_node_drop(node, args...) \
+ _TREEFS_CNH(node, NODE_DROP, node_drop , ##args)
+
+/* Called when a new directory is created, after trees_node_init. If this
+ routine returns an error, the new node will be destroyed and the create
+ will fail. */
+DNH(dir_init, error_t)
+#define treefs_dir_init(dir, args...) \
+ _TREEFS_CNH(dir, DIR_INIT, dir_init , ##args)
+
+/* If NODE is a symlink, copies the contents into BUF, which should have at
+ least *LEN bytes available, and returns 0; if the symlink is too big,
+ E2BIG is returned. Either way, the actual length of the symlink is
+ returned in *LEN (so if it's too big, you can allocate an appropiately
+ sized buffer and try again). If NODE is not a symlink, EINVAL is
+ returned. */
+DNH(node_get_symlink, error_t, char *buf, int *len)
+#define treefs_node_get_symlink(node, args...) \
+ _TREEFS_CNH(node, NODE_GET_SYMLINK, node_get_symlink , ##args)
+
+/* If NODE has a passive translator, copies the contents into BUF, which
+ should have at least *LEN bytes available, and returns 0; if the string is
+ too big, E2BIG is returned. Either way, the actual length of the
+ translator string is returned in *LEN (so if it's too big, you can
+ allocate an appropiately sized buffer and try again). If NODE has no
+ passive translator, EINVAL is returned. */
+DNH(node_get_passive_trans, error_t, char *buf, int *len)
+#define treefs_node_get_passive_trans(node, args...) \
+ _TREEFS_CNH(node, NODE_GET_PASSIVE_TRANS, node_get_passive_trans , ##args)
+
+/* Returns the user and group that a newly started translator should be
+ authenticated as. The default just returns the owner/group of NODE. */
+DNH(node_get_trans_auth, error_t, uid_t *uid, gid_t *gid)
+#define treefs_node_get_trans_auth(node, args...) \
+ _TREEFS_CNH(node, NODE_GET_TRANS_AUTH, node_get_trans_auth , ##args)
+
+/* Start the translator TRANS (of length TRANS_LEN) on NODE, which should be
+ locked, and will be unlocked during the execution of this function.
+ PARENT_PORT should be a send right to use as the parent port passed to the
+ translator. */
+DNH(node_start_translator, error_t,
+ char *trans, unsigned trans_len, file_t parent_port)
+#define treefs_node_start_translator(node, args...) \
+ _TREEFS_CNH(node, NODE_START_TRANSLATOR, node_start_translator , ##args)
+
+/* Called to initialize a new node's stat entry, after all default fields are
+ filled in (but before node_init is called). */
+DNH(node_init_stat, error_t,
+ struct treefs_node *dir, mode_t mode, struct treefs_auth *auth)
+#define treefs_node_init_stat(node, args...) \
+ _TREEFS_CNH(node, NODE_INIT_STAT, node_init_stat , ##args)
+
+/* Called to initialize a new node, after all default fields are filled in.
+ If this routine returns an error, the new node will be destroyed and the
+ create will fail. */
+DNH(node_init, error_t,
+ struct treefs_node *dir, mode_t mode, struct treefs_auth *auth)
+#define treefs_node_init(node, args...) \
+ _TREEFS_CNH(node, NODE_INIT, node_init , ##args)
+
+/* Called to cleanup node-specific info in a node about to be destroyed. */
+DNH(node_finalize, void)
+#define treefs_node_finalize(node, args...) \
+ _TREEFS_CNH(node, NODE_FINALIZE, node_finalize , ##args)
+
+/* Called to cleanup node-specific directory info in a node about to be
+ destroyed. Called before node_finalize. */
+DNH(dir_finalize, void)
+#define treefs_dir_finalize(dir, args...) \
+ _TREEFS_CNH(dir, DIR_FINALIZE, dir_finalize , ##args)
+
+/* Called when the new peropen structure PO is made for NODE, with the
+ authorization in AUTH, opened with the flags FLAGS (note that this a copy
+ of PO->flags, which the hook may modify). If an error is returned, the
+ open will fail with that error. The default hook does explicit checks
+ against AUTH using treefs_node_access, and otherwise does nothing. */
+DNH(node_init_peropen, error_t,
+ struct treefs_peropen *po, int flags, struct treefs_auth *auth)
+#define treefs_node_init_peropen(node, args...) \
+ _TREEFS_CNH(node, NODE_INIT_PEROPEN, node_init_peropen , ##args)
+
+/* Called the peropen structure PO for NODE is being destroyed. */
+DNH(node_finalize_peropen, void, struct treefs_peropen *po)
+#define treefs_node_finalize_peropen(node, args...) \
+ _TREEFS_CNH(node, NODE_FINALIZE_PEROPEN, node_finalize_peropen , ##args)
+
+/* Called when a new handle structure is made for a node. The default does
+ nothing. */
+DNH(node_init_handle, void, struct treefs_handle *handle)
+#define treefs_node_init_handle(node, args...) \
+ _TREEFS_CNH(node, NODE_INIT_HANDLE, node_init_handle , ##args)
+
+/* Called when the handle HANDLE for NODE is being destroyed. */
+DNH(node_finalize_handle, void, struct treefs_handle *handle)
+#define treefs_node_finalize_handle(node, args...) \
+ _TREEFS_CNH(node, NODE_FINALIZE_HANDLE, node_finalize_handle , ##args)
+
+/* ---------------------------------------------------------------- */
+/* Ref counting stuff */
+
+/* NODE has just acquired a hard reference where it had none previously. It
+ is thus now OK again to have weak references without real users. NODE is
+ locked. */
+DNH(node_new_refs, void);
+#define treefs_node_new_refs(node, args...) \
+ _TREEFS_CNH(node, NODE_NEW_REFS, node_new_refs , ##args)
+
+/* NODE has some weak references but has just lost its last hard reference.
+ NP is locked. */
+DNH(node_lost_refs, void);
+#define treefs_node_lost_refs(node, args...) \
+ _TREEFS_CNH(node, NODE_LOST_REFS, node_lost_refs , ##args)
+
+/* NODE has some weak references, but has just lost its last hard references.
+ Take steps so that if any weak references can be freed, they are. NP is
+ locked as is the pager refcount lock. This function will be called after
+ treefs_node_lost_refs. */
+DNH(node_try_dropping_weak_refs, void);
+#define treefs_node_try_dropping_weak_refs(node, args...) \
+ _TREEFS_CNH(node, NODE_TRY_DROPPING_WEAK_REFS, node_try_dropping_weak_refs , ##args)
+
+/* Turn off our shorthand notation. */
+#undef DNH
+#undef DFH
+
+/* ---------------------------------------------------------------- */
+/* Default routines for some hooks (each is the default value for the hook
+ with the same name minus the leading underscore). When you add something
+ here, you should also add it to the initialize code in defhooks.c. */
+
+treefs_fsys_create_node_t _treefs_fsys_create_node;
+treefs_fsys_destroy_node_t _treefs_fsys_destroy_node;
+treefs_fsys_get_root_t _treefs_fsys_get_root;
+treefs_node_type_t _treefs_node_type;
+treefs_node_unlinked_t _treefs_node_unlinked;
+treefs_node_mod_link_count_t _treefs_node_mod_link_count;
+treefs_node_mod_link_count_t _treefs_mod_link_count;
+treefs_dir_lookup_t _treefs_dir_lookup;
+treefs_dir_noent_t _treefs_dir_noent;
+treefs_dir_create_child_t _treefs_dir_create_child;
+treefs_dir_link_t _treefs_dir_link;
+treefs_dir_unlink_t _treefs_dir_unlink;
+treefs_node_owned_t _treefs_node_owned;
+treefs_node_access_t _treefs_node_access;
+treefs_node_start_translator_t _treefs_node_start_translator;
+treefs_node_get_trans_auth_t _treefs_node_get_trans_auth;
+
+#endif /* __TREEFS_HOOKS_H__ */
diff --git a/libtreefs/treefs-s-hooks.h b/libtreefs/treefs-s-hooks.h
new file mode 100644
index 00000000..2ea9e7ab
--- /dev/null
+++ b/libtreefs/treefs-s-hooks.h
@@ -0,0 +1,231 @@
+/* RPC server hooks in libtreefs (also see "treefs-hooks.h")
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __TREEFS_S_HOOKS_H__
+#define __TREEFS_S_HOOKS_H__
+
+#include "treefs-hooks.h"
+
+/* Shorthand for declaring the various hook types (each hook has an
+ associated type so that a user can type-check his hook routine). */
+#define DHH(name_sym, ret_type, argtypes...) \
+ typedef ret_type treefs_##name_sym##_t (struct treefs_handle * , ##argtypes);
+#define DFH(name_sym, ret_type, argtypes...) \
+ typedef ret_type treefs_##name_sym##_t (struct treefs_fsys * , ##argtypes);
+
+/* ---------------------------------------------------------------- */
+/* Hooks for file RPCs. See <hurd/fs.defs> for more info. */
+
+DHH(s_file_exec, error_t,
+ task_t, int, char *, unsigned, char *, unsigned, mach_port_t *, unsigned,
+ mach_port_t *, unsigned, int *, unsigned,
+ mach_port_t *, unsigned, mach_port_t *, unsigned)
+#define treefs_s_file_exec(h, args...) \
+ _TREEFS_CHH(h, S_FILE_EXEC, s_file_exec , ##args)
+DHH(s_file_chown, error_t, uid_t, gid_t)
+#define treefs_s_file_chown(h, args...) \
+ _TREEFS_CHH(h, S_FILE_CHOWN, s_file_chown , ##args)
+DHH(s_file_chauthor, error_t, uid_t)
+#define treefs_s_file_chauthor(h, args...) \
+ _TREEFS_CHH(h, S_FILE_CHAUTHOR, s_file_chauthor , ##args)
+DHH(s_file_chmod, error_t, mode_t)
+#define treefs_s_file_chmod(h, args...) \
+ _TREEFS_CHH(h, S_FILE_CHMOD, s_file_chmod , ##args)
+DHH(s_file_chflags, error_t, int)
+#define treefs_s_file_chflags(h, args...) \
+ _TREEFS_CHH(h, S_FILE_CHFLAGS, s_file_chflags , ##args)
+DHH(s_file_utimes, error_t, time_value_t, time_value_t)
+#define treefs_s_file_utimes(h, args...) \
+ _TREEFS_CHH(h, S_FILE_UTIMES, s_file_utimes , ##args)
+DHH(s_file_truncate, error_t, off_t)
+#define treefs_s_file_truncate(h, args...) \
+ _TREEFS_CHH(h, S_FILE_TRUNCATE, s_file_truncate , ##args)
+DHH(s_file_lock, error_t, struct treefs_handle *, int)
+#define treefs_s_file_lock(h, args...) \
+ _TREEFS_CHH(h, S_FILE_LOCK, s_file_lock , ##args)
+DHH(s_file_lock_stat, error_t, int *, int *)
+#define treefs_s_file_lock_stat(h, args...) \
+ _TREEFS_CHH(h, S_FILE_LOCK_STAT, s_file_lock_stat , ##args)
+DHH(s_file_notice_changes, error_t, mach_port_t)
+#define treefs_s_file_notice_changes(h, args...) \
+ _TREEFS_CHH(h, S_FILE_NOTICE, s_file_notice_changes , ##args)
+DHH(s_file_sync, error_t, int)
+#define treefs_s_file_sync(h, args...) \
+ _TREEFS_CHH(h, S_FILE_SYNC, s_file_sync , ##args)
+DHH(s_file_getlinknode, error_t, file_t *, mach_msg_type_name_t *)
+#define treefs_s_file_getlinknode(h, args...) \
+ _TREEFS_CHH(h, S_FILE_GET_LINK_NODE, s_file_getlinknode , ##args)
+
+/* ---------------------------------------------------------------- */
+/* Hooks for IO rpcs. See <hurd/io.defs> for more info. */
+
+DHH(s_io_write, error_t, char *, unsigned, off_t, int *)
+#define treefs_s_io_write(h, args...) \
+ _TREEFS_CHH(h, S_IO_WRITE, s_io_write , ##args)
+DHH(s_io_read, error_t, char **, unsigned *, off_t, int)
+#define treefs_s_io_read(h, args...) \
+ _TREEFS_CHH(h, S_IO_READ, s_io_read , ##args)
+DHH(s_io_seek, error_t, off_t, int, off_t *)
+#define treefs_s_io_seek(h, args...) \
+ _TREEFS_CHH(h, S_IO_SEEK, s_io_seek , ##args)
+DHH(s_io_readable, error_t, unsigned *)
+#define treefs_s_io_readable(h, args...) \
+ _TREEFS_CHH(h, S_IO_READABLE, s_io_readable , ##args)
+DHH(s_io_set_all_openmodes, error_t, int)
+#define treefs_s_io_set_all_openmodes(h, args...) \
+ _TREEFS_CHH(h, S_IO_SET_ALL_OPENMODES, s_io_set_all_openmodes , ##args)
+DHH(s_io_get_openmodes, error_t, int *)
+#define treefs_s_io_get_openmodes(h, args...) \
+ _TREEFS_CHH(h, S_IO_GET_OPENMODES, s_io_get_openmodes , ##args)
+DHH(s_io_set_some_openmodes, error_t, int)
+#define treefs_s_io_set_some_openmodes(h, args...) \
+ _TREEFS_CHH(h, S_IO_SET_SOME_OPENMODES, s_io_set_some_openmodes , ##args)
+DHH(s_io_clear_some_openmodes, error_t, int)
+#define treefs_s_io_clear_some_openmodes(h, args...) \
+ _TREEFS_CHH(h, S_IO_CLEAR_SOME_OPENMODES, s_io_clear_some_openmodes , ##args)
+DHH(s_io_async, error_t, mach_port_t, mach_port_t *, mach_msg_type_name_t *)
+#define treefs_s_io_async(h, args...) \
+ _TREEFS_CHH(h, S_IO_ASYNC, s_io_async , ##args)
+DHH(s_io_mod_owner, error_t, pid_t)
+#define treefs_s_io_mod_owner(h, args...) \
+ _TREEFS_CHH(h, S_IO_MOD_OWNER, s_io_mod_owner , ##args)
+DHH(s_io_get_owner, error_t, pid_t *)
+#define treefs_s_io_get_owner(h, args...) \
+ _TREEFS_CHH(h, S_IO_GET_OWNER, s_io_get_owner , ##args)
+DHH(s_io_get_icky_async_id, error_t, mach_port_t *, mach_msg_type_name_t *)
+#define treefs_s_io_get_icky_async_id(h, args...) \
+ _TREEFS_CHH(h, S_IO_GET_ICKY_ASYNC_ID, s_io_get_icky_async_id , ##args)
+DHH(s_io_select, error_t, int *, int *)
+#define treefs_s_io_select(h, args...) \
+ _TREEFS_CHH(h, S_IO_SELECT, s_io_select , ##args)
+DHH(s_io_stat, error_t, io_statbuf_t *)
+#define treefs_s_io_stat(h, args...) \
+ _TREEFS_CHH(h, S_IO_STAT, s_io_stat , ##args)
+DHH(s_io_reauthenticate, error_t, mach_port_t *, mach_msg_type_name_t *)
+#define treefs_s_io_reauthenticate(h, args...) \
+ _TREEFS_CHH(h, S_IO_REAUTHENTICATE, s_io_reauthenticate , ##args)
+DHH(s_io_restrict_auth, error_t,
+ mach_port_t *, mach_msg_type_name_t *, uid_t *, int, gid_t *, int);
+#define treefs_s_io_restrict_auth(h, args...) \
+ _TREEFS_CHH(h, S_IO_RESTRICT_AUTH, s_io_restrict_auth , ##args)
+DHH(s_io_duplicate, error_t, mach_port_t *, mach_msg_type_name_t *)
+#define treefs_s_io_duplicate(h, args...) \
+ _TREEFS_CHH(h, S_IO_DUPLICATE, s_io_duplicate , ##args)
+DHH(s_io_server_version, error_t, char *, int *, int *, int *)
+#define treefs_s_io_server_version(h, args...) \
+ _TREEFS_CHH(h, S_IO_SERVER_VERSION, s_io_server_version , ##args)
+DHH(s_io_map, error_t, mach_port_t *, mach_msg_type_name_t *, mach_port_t *, mach_msg_type_name_t *)
+#define treefs_s_io_map(h, args...) \
+ _TREEFS_CHH(h, S_IO_MAP, s_io_map , ##args)
+DHH(s_io_map_cntl, error_t, mach_port_t *, mach_msg_type_name_t *)
+#define treefs_s_io_map_cntl(h, args...) \
+ _TREEFS_CHH(h, S_IO_MAP_CNTL, s_io_map_cntl , ##args)
+DHH(s_io_release_conch, error_t, struct treefs_handle *)
+#define treefs_s_io_release_conch(h, args...) \
+ _TREEFS_CHH(h, S_IO_RELEASE_CONCH, s_io_release_conch, ##args)
+DHH(s_io_eofnotify, error_t);
+#define treefs_s_io_eofnotify(h, args...) \
+ _TREEFS_CHH(h, S_IO_EOFNOTIFY, s_io_eofnotify , ##args)
+DHH(s_io_prenotify, error_t, vm_offset_t, vm_offset_t);
+#define treefs_s_io_prenotify(h, args...) \
+ _TREEFS_CHH(h, S_IO_PRENOTIFY, s_io_prenotify , ##args)
+DHH(s_io_postnotify, error_t, vm_offset_t, vm_offset_t);
+#define treefs_s_io_postnotify(h, args...) \
+ _TREEFS_CHH(h, S_IO_POSTNOTIFY, s_io_postnotify , ##args)
+DHH(s_io_readnotify, error_t);
+#define treefs_s_io_readnotify(h, args...) \
+ _TREEFS_CHH(h, S_IO_READNOTIFY, s_io_readnotify , ##args)
+DHH(s_io_readsleep, error_t);
+#define treefs_s_io_readsleep(h, args...) \
+ _TREEFS_CHH(h, S_IO_READSLEEP, s_io_readsleep , ##args)
+DHH(s_io_sigio, error_t);
+#define treefs_s_io_sigio(h, args...) \
+ _TREEFS_CHH(h, S_IO_SIGIO, s_io_sigio , ##args)
+
+/* ---------------------------------------------------------------- */
+/* Hooks for directory RPCs. See <hurd/fs.defs> for more info. */
+
+DHH(s_dir_lookup, error_t,
+ char *, int, mode_t, enum retry_type *, char *,
+ file_t *, mach_msg_type_name_t *);
+#define treefs_s_dir_lookup(h, args...) \
+ _TREEFS_CHH(h, S_DIR_LOOKUP, s_dir_lookup , ##args)
+DHH(s_dir_readdir, error_t, char **, unsigned, int, int, vm_size_t, int *);
+#define treefs_s_dir_readdir(h, args...) \
+ _TREEFS_CHH(h, S_DIR_READDIR, s_dir_readdir , ##args)
+DHH(s_dir_mkdir, error_t, char *, mode_t);
+#define treefs_s_dir_mkdir(h, args...) \
+ _TREEFS_CHH(h, S_DIR_MKDIR, s_dir_mkdir , ##args)
+DHH(s_dir_rmdir, error_t, char *);
+#define treefs_s_dir_rmdir(h, args...) \
+ _TREEFS_CHH(h, S_DIR_RMDIR, s_dir_rmdir , ##args)
+DHH(s_dir_unlink, error_t, char *);
+#define treefs_s_dir_unlink(h, args...) \
+ _TREEFS_CHH(h, S_DIR_UNLINK, s_dir_unlink , ##args)
+DHH(s_dir_link, error_t, file_t, char *);
+#define treefs_s_dir_link(h, args...) \
+ _TREEFS_CHH(h, S_DIR_LINK, s_dir_link , ##args)
+DHH(s_dir_rename, error_t, char *, file_t, char *);
+#define treefs_s_dir_rename(h, args...) \
+ _TREEFS_CHH(h, S_DIR_RENAME, s_dir_rename , ##args)
+DHH(s_dir_mkfile, error_t, int, mode_t, mach_port_t *, mach_msg_type_name_t *);
+#define treefs_s_dir_mkfile(h, args...) \
+ _TREEFS_CHH(h, S_DIR_MKFILE, s_dir_mkfile , ##args)
+DHH(s_dir_notice_changes, error_t, mach_port_t *, mach_msg_type_name_t *);
+#define treefs_s_dir_notice_changes(h, args...) \
+ _TREEFS_CHH(h, S_DIR_NOTICE_CHANGES, s_dir_notice_changes , ##args)
+
+/* ---------------------------------------------------------------- */
+/* fsys RPCs (called on the filesystem itself) */
+
+DFH(s_fsys_getroot, error_t,
+ mach_port_t, uid_t *, unsigned, gid_t *, unsigned, int,
+ retry_type *, char *, file_t *, mach_msg_type_name_t *)
+#define treefs_s_fsys_getroot(fsys, args...) \
+ _TREEFS_CFH(fsys, S_FSYS_GETROOT, s_fsys_getroot , ##args)
+DFH(s_fsys_set_options, error_t, char *, unsigned, int)
+#define treefs_s_fsys_set_options(fsys, args...) \
+ _TREEFS_CFH(fsys, S_FSYS_SET_OPTIONS, s_fsys_set_options , ##args)
+DFH(s_fsys_goaway, error_t, int)
+#define treefs_s_fsys_goaway(fsys, args...) \
+ _TREEFS_CFH(fsys, S_FSYS_GOAWAY, s_fsys_goaway , ##args)
+DFH(s_fsys_getfile, error_t,
+ uid_t *, unsigned, gid_t *, unsigned, char *, unsigned,
+ mach_port_t *, mach_msg_type_name_t *)
+#define treefs_s_fsys_getfile(fsys, args...) \
+ _TREEFS_CFH(fsys, S_FSYS_GETFILE, s_fsys_getfile , ##args)
+DFH(s_fsys_syncfs, error_t, int, int)
+#define treefs_s_fsys_syncfs(fsys, args...) \
+ _TREEFS_CFH(fsys, S_FSYS_SYNCFS, s_fsys_syncfs , ##args)
+
+/* Turn off our shorthand notation. */
+#undef DHH
+
+/* ---------------------------------------------------------------- */
+/* Default routines for some hooks (each is the default value for the hook
+ with the same name minus the leading underscore). When you add something
+ here, you should also add it to the initialize code in defhooks.c. */
+
+treefs_s_dir_lookup_t _treefs_s_dir_lookup;
+treefs_s_fsys_getroot_t _treefs_s_fsys_getroot;
+
+#endif /* __TREEFS_S_HOOKS_H__ */
diff --git a/libtreefs/treefs.h b/libtreefs/treefs.h
new file mode 100644
index 00000000..0e49ef51
--- /dev/null
+++ b/libtreefs/treefs.h
@@ -0,0 +1,493 @@
+/* Hierarchial filesystem support
+
+ Copyright (C) 1995, 2002 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __TREEFS_H__
+#define __TREEFS_H__
+
+#include <errno.h>
+#include <pthread.h>
+#include <assert.h>
+#include <features.h>
+
+#include <sys/stat.h>
+
+#include <hurd/hurd_types.h>
+#include <hurd/ports.h>
+#include <hurd/fshelp.h>
+
+/* Include the hook calling macros and non-rpc hook definitions (to get
+ those, include "trees-s-hooks.h"). */
+#include "treefs-hooks.h"
+
+#ifdef TREEFS_DEFINE_EI
+#define TREEFS_EI
+#else
+#define TREEFS_EI __extern_inline
+#endif
+
+/* ---------------------------------------------------------------- */
+
+typedef void (**treefs_hook_vector_t)();
+
+/* A list of nodes. */
+struct treefs_node_list;
+
+/* Each user port referring to a file points to one of these. */
+struct treefs_handle
+{
+ struct port_info pi;
+ struct treefs_auth *auth; /* User identification */
+ struct treefs_peropen *po; /* The io object itself */
+ void *u; /* for user use */
+};
+
+/* An authentication cookie. */
+struct treefs_auth
+{
+ int refs;
+ uid_t *uids, *gids;
+ int nuids, ngids;
+ int isroot;
+ void *u; /* for user use */
+};
+
+/* Bits the user is permitted to set with io_*_openmodes */
+#define TREEFS_SETTABLE_FLAGS (O_APPEND|O_ASYNC|O_FSYNC|O_NONBLOCK|O_NOATIME)
+
+/* Bits that are turned off after open */
+#define TREEFS_OPENONLY_FLAGS (O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS|O_NONBLOCK)
+
+struct treefs_peropen
+{
+ int refs;
+ int open_flags;
+ int user_lock_state;
+
+ /* A port to the directory through which this open file was reached. */
+ mach_port_t parent_port;
+
+ void *u; /* for user use */
+
+ struct treefs_node *node;
+};
+
+/* A filesystem node in the tree. */
+struct treefs_node
+{
+ io_statbuf_t stat;
+ struct treefs_fsys *fsys;
+
+ struct trans_link active_trans;
+ char *passive_trans;
+ struct lock_box user_lock;
+
+ pthread_mutex_t lock;
+ unsigned refs, weak_refs;
+
+ /* Node ops */
+ treefs_hook_vector_t hooks;
+
+ /* If this node is a directory, then this is the directory state. */
+ struct treefs_node_list *children;
+
+ void *u; /* for user use */
+};
+
+struct treefs_node_list
+{
+ struct treefs_node **nodes;
+ unsigned short num_nodes, nodes_alloced;
+ char *names;
+ unsigned short names_len, names_alloced;
+};
+
+struct treefs_fsys
+{
+ struct port_info pi;
+ pthread_mutex_t lock;
+
+ /* The root node in this filesystem. */
+ struct treefs_node *root;
+
+ /* The port for the node which this filesystem is translating. */
+ mach_port_t underlying_port;
+ /* And stat info for it. */
+ io_statbuf_t underlying_stat;
+
+ /* Flags from the TREEFS_FSYS_ set. */
+ int flags;
+ /* Max number of symlink expansions allowed. */
+ unsigned max_symlinks;
+ /* Sync interval (in seconds). 0 means all operations should be
+ synchronous, any negative value means never sync. */
+ int sync_interval;
+
+ /* Values to return from a statfs. */
+ int fs_type;
+ int fs_id;
+
+ /* This is the hook vector that each new node in this filesystem starts out
+ with. */
+ treefs_hook_vector_t hooks;
+
+ /* The port bucket to which all of our ports belongs. */
+ struct ports_bucket *port_bucket;
+
+ /* Various classes of ports we know about. */
+ struct port_class *handle_port_class;
+
+ void *u; /* for user use */
+};
+
+/* Filesystem flags. */
+#define TREEFS_FSYS_READONLY 0x1
+
+/* ---------------------------------------------------------------- */
+/* In-core directory management routines (`mdir' == `memory dir'). These are
+ intended for keeping non-permanent directory state. If called on a
+ non-dir, ENOTDIR is returned. */
+
+/* Add CHILD to DIR as NAME, replacing any existing entry. If OLD_CHILD is
+ NULL, and NAME already exists in dir, EEXIST is returned, otherwise, any
+ previous child is replaced and returned in OLD_CHILD. DIR should be
+ locked. */
+error_t treefs_mdir_add (struct treefs_node *dir, char *name,
+ struct treefs_node *child,
+ struct treefs_node **old_child);
+
+/* Remove any entry in DIR called NAME. If there is no such entry, ENOENT is
+ returned. If OLD_CHILD is non-NULL, any removed entry is returned in it.
+ DIR should be locked. */
+error_t treefs_mdir_remove (struct treefs_node *dir, char *name,
+ struct treefs_node **old_child);
+
+/* Returns in NODE any entry called NAME in DIR, or NULL (and ENOENT) if
+ there isn't such. DIR should be locked. */
+error_t treefs_mdir_get (struct treefs_node *dir, char *name,
+ struct treefs_node **node);
+
+/* Call FUN on each child of DIR; if FUN returns a non-zero value at any
+ point, stop iterating and return that value immediately. */
+error_t treefs_mdir_for_each (struct treefs_node *dir,
+ error_t (*fun)(struct treefs_node *node));
+
+/* ---------------------------------------------------------------- */
+/* Functions for dealing with node lists. */
+
+/* Return a new node list, or NULL if a memory allocation error occurs. */
+struct treefs_node_list *treefs_make_node_list ();
+
+/* Add NODE to LIST as NAME, replacing any existing entry. If OLD_NODE is
+ NULL, and an entry NAME already exists, EEXIST is returned, otherwise, any
+ previous child is replaced and returned in OLD_NODE. */
+error_t treefs_node_list_add (struct treefs_node_list *list, char *name,
+ struct treefs_node *node,
+ struct treefs_node **old_node);
+
+/* Remove any entry in LIST called NAME. If there is no such entry, ENOENT is
+ returned. If OLD_NODE is non-NULL, any removed entry is returned in it. */
+error_t treefs_node_list_remove (struct treefs_node_list *list, char *name,
+ struct treefs_node **old_node);
+
+/* Returns in NODE any entry called NAME in LIST, or NULL (and ENOENT) if
+ there isn't such. */
+error_t treefs_node_list_get (struct treefs_node_list *list, char *name,
+ struct treefs_node **node);
+
+/* Call FUN on each node in LIST; if FUN returns a non-zero value at any
+ point, stop iterating and return that value immediately. */
+error_t treefs_node_list_for_each (struct treefs_node_list *list,
+ error_t (*fun)(char *name,
+ struct treefs_node *node));
+
+/* ---------------------------------------------------------------- */
+/* Functions for manipulating hook vectors. */
+
+typedef void (*treefs_hook_vector_init_t[TREEFS_NUM_HOOKS])();
+
+extern treefs_hook_vector_init_t treefs_default_hooks;
+
+/* Returns a copy of the treefs hook vector HOOKS, or a zero'd vector if HOOKS
+ is NULL. If HOOKS is NULL, treefs_default_hooks is used. If a memory
+ allocation error occurs, NULL is returned. */
+treefs_hook_vector_t treefs_hooks_clone (treefs_hook_vector_t hooks);
+
+/* Copies each non-NULL entry in OVERRIDES into HOOKS. */
+void treefs_hooks_override (treefs_hook_vector_t hooks,
+ treefs_hook_vector_t overrides);
+
+/* Sets the hook NUM in HOOKS to HOOK. */
+void treefs_hooks_set (treefs_hook_vector_t hooks,
+ unsigned num, void (*hook)());
+
+/* ---------------------------------------------------------------- */
+/* Reference counting function (largely stolen from diskfs). */
+
+extern pthread_spinlock_t treefs_node_refcnt_lock;
+
+extern void treefs_node_ref (struct treefs_node *node);
+extern void treefs_node_release (struct treefs_node *node);
+extern void treefs_node_unref (struct treefs_node *node);
+extern void treefs_node_ref_weak (struct treefs_node *node);
+extern void treefs_node_release_weak (struct treefs_node *node);
+extern void treefs_node_unref_weak (struct treefs_node *node);
+
+#if defined(__USE_EXTERN_INLINES) || defined(TREEFS_DEFINE_EI)
+/* Add a hard reference to a node. If there were no hard
+ references previously, then the node cannot be locked
+ (because you must hold a hard reference to hold the lock). */
+TREEFS_EI void
+treefs_node_ref (struct treefs_node *node)
+{
+ int new_ref;
+ pthread_spin_lock (&treefs_node_refcnt_lock);
+ node->refs++;
+ new_ref = (node->refs == 1);
+ pthread_spin_unlock (&treefs_node_refcnt_lock);
+ if (new_ref)
+ {
+ pthread_mutex_lock (&node->lock);
+ treefs_node_new_refs (node);
+ pthread_mutex_unlock (&node->lock);
+ }
+}
+
+/* Unlock node NODE and release a hard reference; if this is the last
+ hard reference and there are no links to the file then request
+ weak references to be dropped. */
+TREEFS_EI void
+treefs_node_release (struct treefs_node *node)
+{
+ int tried_drop_weak_refs = 0;
+
+ loop:
+ pthread_spin_lock (&treefs_node_refcnt_lock);
+ assert (node->refs);
+ node->refs--;
+ if (node->refs + node->weak_refs == 0)
+ treefs_node_drop (node);
+ else if (node->refs == 0 && !tried_drop_weak_refs)
+ {
+ pthread_spin_unlock (&treefs_node_refcnt_lock);
+ treefs_node_lost_refs (node);
+ if (treefs_node_unlinked (node))
+ {
+ /* There are no links. If there are weak references that
+ can be dropped, we can't let them postpone deallocation.
+ So attempt to drop them. But that's a user-supplied
+ routine, which might result in further recursive calls to
+ the ref-counting system. So we have to reacquire our
+ reference around the call to forestall disaster. */
+ pthread_spin_unlock (&treefs_node_refcnt_lock);
+ node->refs++;
+ pthread_spin_unlock (&treefs_node_refcnt_lock);
+
+ treefs_node_try_dropping_weak_refs (node);
+
+ /* But there's no value in looping forever in this
+ routine; only try to drop weak references once. */
+ tried_drop_weak_refs = 1;
+
+ /* Now we can drop the reference back... */
+ goto loop;
+ }
+ }
+ else
+ pthread_spin_unlock (&treefs_node_refcnt_lock);
+ pthread_mutex_unlock (&node->lock);
+}
+
+/* Release a hard reference on NODE. If NODE is locked by anyone, then
+ this cannot be the last hard reference (because you must hold a
+ hard reference in order to hold the lock). If this is the last
+ hard reference and there are no links, then request weak references
+ to be dropped. */
+TREEFS_EI void
+treefs_node_unref (struct treefs_node *node)
+{
+ int tried_drop_weak_refs = 0;
+
+ loop:
+ pthread_spin_lock (&treefs_node_refcnt_lock);
+ assert (node->refs);
+ node->refs--;
+ if (node->refs + node->weak_refs == 0)
+ {
+ pthread_mutex_lock (&node->lock);
+ treefs_node_drop (node);
+ }
+ else if (node->refs == 0)
+ {
+ pthread_mutex_lock (&node->lock);
+ pthread_spin_unlock (&treefs_node_refcnt_lock);
+ treefs_node_lost_refs (node);
+ if (treefs_node_unlinked(node) && !tried_drop_weak_refs)
+ {
+ /* Same issue here as in nodeut; see that for explanation */
+ pthread_spin_unlock (&treefs_node_refcnt_lock);
+ node->refs++;
+ pthread_spin_unlock (&treefs_node_refcnt_lock);
+
+ treefs_node_try_dropping_weak_refs (node);
+ tried_drop_weak_refs = 1;
+
+ /* Now we can drop the reference back... */
+ pthread_mutex_unlock (&node->lock);
+ goto loop;
+ }
+ pthread_mutex_unlock (&node->lock);
+ }
+ else
+ pthread_spin_unlock (&treefs_node_refcnt_lock);
+}
+
+/* Add a weak reference to a node. */
+TREEFS_EI void
+treefs_node_ref_weak (struct treefs_node *node)
+{
+ pthread_spin_lock (&treefs_node_refcnt_lock);
+ node->weak_refs++;
+ pthread_spin_unlock (&treefs_node_refcnt_lock);
+}
+
+/* Unlock node NODE and release a weak reference */
+TREEFS_EI void
+treefs_node_release_weak (struct treefs_node *node)
+{
+ pthread_spin_lock (&treefs_node_refcnt_lock);
+ assert (node->weak_refs);
+ node->weak_refs--;
+ if (node->refs + node->weak_refs == 0)
+ treefs_node_drop (node);
+ else
+ {
+ pthread_spin_unlock (&treefs_node_refcnt_lock);
+ pthread_mutex_unlock (&node->lock);
+ }
+}
+
+/* Release a weak reference on NODE. If NODE is locked by anyone, then
+ this cannot be the last reference (because you must hold a
+ hard reference in order to hold the lock). */
+TREEFS_EI void
+treefs_node_unref_weak (struct treefs_node *node)
+{
+ pthread_spin_lock (&treefs_node_refcnt_lock);
+ assert (node->weak_refs);
+ node->weak_refs--;
+ if (node->refs + node->weak_refs == 0)
+ {
+ pthread_mutex_lock (&node->lock);
+ treefs_node_drop (node);
+ }
+ else
+ pthread_spin_unlock (&treefs_node_refcnt_lock);
+}
+#endif /* Use extern inlines. */
+
+/* ---------------------------------------------------------------- */
+
+/* Return in PORT a send right for a new handle, pointing at the peropen PO,
+ with rights initialized from AUTH. */
+error_t
+treefs_peropen_create_right (struct treefs_peropen *po,
+ struct treefs_auth *auth,
+ mach_port_t *port);
+
+/* Return a send right for a new handle and a new peropen, pointing at NODE,
+ with rights initialized from AUTH. MODE and PARENT_PORT are used to
+ initialize the corresponding fields in the new peropen. */
+error_t
+treefs_node_create_right (struct treefs_node *node, int flags,
+ mach_port_t parent_port, struct treefs_auth *auth,
+ mach_port_t *port);
+
+/* ---------------------------------------------------------------- */
+/* Auth functions; copied from diskfs. */
+
+extern int treefs_auth_has_uid (struct treefs_auth *auth, uid_t uid);
+extern int treefs_auth_in_group (struct treefs_auth *auth, gid_t gid);
+
+#if defined(__USE_EXTERN_INLINES) || defined(TREEFS_DEFINE_EI)
+/* Return nonzero iff the user identified by AUTH has uid UID. */
+TREEFS_EI int
+treefs_auth_has_uid (struct treefs_auth *auth, uid_t uid)
+{
+ int i;
+ for (i = 0; i < auth->nuids; i++)
+ if (auth->uids[i] == uid)
+ return 1;
+ return 0;
+}
+
+/* Return nonzero iff the user identified by AUTH has group GID. */
+TREEFS_EI int
+treefs_auth_in_group (struct treefs_auth *auth, gid_t gid)
+{
+ int i;
+ for (i = 0; i < auth->ngids; i++)
+ if (auth->gids[i] == gid)
+ return 1;
+ return 0;
+}
+#endif /* Use extern inlines. */
+
+/* ---------------------------------------------------------------- */
+/* Helper routines for dealing with translators. */
+
+/* Return the active translator control port for NODE. If there is no
+ translator, active or passive, MACH_PORT_NULL is returned in CONTROL_PORT.
+ If there is a translator, it is started if necessary, and returned in
+ CONTROL_PORT. *DIR_PORT should be a port right to use as the new
+ translators parent directory. If it is MACH_PORT_NULL, a port is created
+ from DIR and PARENT_PORT and stored in *DIR_PORT; otherwise DIR and
+ PARENT_PORT are not used. Neither NODE or DIR should be locked when
+ calling this function. */
+error_t treefs_node_get_active_trans (struct treefs_node *node,
+ struct treefs_node *dir,
+ mach_port_t parent_port,
+ mach_port_t *control_port,
+ mach_port_t *dir_port);
+
+/* Drop the active translator CONTROL_PORT on NODE, unless it's no longer the
+ current active translator, in which case just drop a reference to it. */
+void treefs_node_drop_active_trans (struct treefs_node *node,
+ mach_port_t control_port);
+
+/* ---------------------------------------------------------------- */
+/* Basic node creation. */
+
+/* Create a basic node, with one reference and no user-specific fields
+ initialized, and return it in NODE */
+error_t
+treefs_create_node (struct treefs_fsys *fsys, struct treefs_node **node);
+
+/* Immediately destroy NODE, with no user-finalization. */
+error_t treefs_free_node (struct treefs_node *node);
+
+/* ---------------------------------------------------------------- */
+/* Some global variables. */
+
+/* The port class used by treefs to make filesystem control ports. */
+struct port_class *treefs_fsys_port_class;
+
+#endif /* __TREEFS_H__ */
diff --git a/libtreefs/xinl.c b/libtreefs/xinl.c
new file mode 100644
index 00000000..fe83e5a3
--- /dev/null
+++ b/libtreefs/xinl.c
@@ -0,0 +1,3 @@
+#define TREEFS_DEFINE_EI
+#include "treefs.h"
+#include "mig-decls.h"
diff --git a/libtrivfs/Makefile b/libtrivfs/Makefile
new file mode 100644
index 00000000..921acbea
--- /dev/null
+++ b/libtrivfs/Makefile
@@ -0,0 +1,62 @@
+# Copyright (C) 1994, 1995, 1996, 1997, 1999, 2001, 2002, 2003, 2008, 2012
+# Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libtrivfs
+makemode := library
+
+FSSRCS= dir-link.c dir-mkdir.c dir-mkfile.c dir-lookup.c dir-readdir.c \
+ dir-rename.c dir-rmdir.c dir-unlink.c file-chauthor.c \
+ file-chflags.c file-chmod.c file-chown.c file-get-trans.c \
+ file-get-transcntl.c file-getcontrol.c file-getfh.c \
+ file-getlinknode.c file-lock.c file-set-trans.c file-statfs.c \
+ file-sync.c file-syncfs.c file-set-size.c file-utimes.c file-exec.c \
+ file-access.c dir-chg.c file-chg.c file-get-storage-info.c \
+ file-get-fs-options.c file-reparent.c get-source.c
+
+IOSRCS=io-async-icky.c io-async.c io-duplicate.c io-map.c io-modes-get.c \
+ io-modes-off.c io-modes-on.c io-modes-set.c io-owner-get.c \
+ io-owner-mod.c io-pathconf.c io-read.c io-readable.c io-revoke.c \
+ io-reauthenticate.c io-restrict-auth.c io-seek.c io-select.c \
+ io-stat.c io-stubs.c io-write.c io-version.c io-identity.c
+
+FSYSSRCS=fsys-getroot.c fsys-goaway.c fsys-stubs.c fsys-syncfs.c \
+ fsys-forward.c fsys-set-options.c fsys-get-options.c \
+ file-get-children.c file-get-source.c
+
+OTHERSRCS=demuxer.c protid-clean.c protid-dup.c cntl-create.c \
+ cntl-clean.c times.c startup.c open.c \
+ runtime-argp.c set-options.c append-args.c dyn-classes.c \
+ protid-classes.c cntl-classes.c
+
+SRCS=$(FSSRCS) $(IOSRCS) $(FSYSSRCS) $(OTHERSRCS)
+
+MIGSTUBS=fsServer.o ioServer.o fsysServer.o fsys_replyUser.o
+
+libname = libtrivfs
+HURDLIBS = fshelp iohelp ports shouldbeinlibc
+OBJS= $(sort $(subst .c,.o,$(SRCS)) $(MIGSTUBS))
+MIGSFLAGS=-imacros $(srcdir)/mig-mutate.h
+MIGCOMSFLAGS = -prefix trivfs_
+installhdrs := trivfs.h
+mig-sheader-prefix = trivfs_
+ifndef no_deps
+installhdrs += $(patsubst %,trivfs_%_S.h,fs io fsys)
+endif
+
+include ../Makeconf
+
+$(MIGSTUBS:%Server.o=%.sdefsi): $(srcdir)/mig-mutate.h
diff --git a/libtrivfs/append-args.c b/libtrivfs/append-args.c
new file mode 100644
index 00000000..d5c3d4a3
--- /dev/null
+++ b/libtrivfs/append-args.c
@@ -0,0 +1,30 @@
+/* Append current command line arguments
+
+ Copyright (C) 1996 Free Software Foundation
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* Append to the malloced string *ARGZ of length *ARGZ_LEN a NUL-separated
+ list of the arguments to this translator. */
+error_t
+trivfs_append_args (struct trivfs_control *fsys,
+ char **argz, size_t *argz_len)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/cntl-classes.c b/libtrivfs/cntl-classes.c
new file mode 100644
index 00000000..356c9e92
--- /dev/null
+++ b/libtrivfs/cntl-classes.c
@@ -0,0 +1,22 @@
+/* Defaults for TRIVFS_CNTL_[N]PORTCLASSES
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "trivfs.h"
+
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_cntl_nportclasses;
diff --git a/libtrivfs/cntl-clean.c b/libtrivfs/cntl-clean.c
new file mode 100644
index 00000000..a010828f
--- /dev/null
+++ b/libtrivfs/cntl-clean.c
@@ -0,0 +1,35 @@
+/*
+ Copyright (C) 1994, 1996, 1997 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* Clean pointers in a struct trivfs_control when its last reference
+ vanishes before it's freed. */
+void
+trivfs_clean_cntl (void *arg)
+{
+ struct trivfs_control *cntl = arg;
+
+ mach_port_destroy (mach_task_self (), cntl->filesys_id);
+ mach_port_destroy (mach_task_self (), cntl->file_id);
+ mach_port_deallocate (mach_task_self (), cntl->underlying);
+
+ trivfs_remove_control_port_class (cntl->pi.class);
+ trivfs_remove_port_bucket (cntl->pi.bucket);
+ trivfs_remove_protid_port_class (cntl->protid_class);
+ trivfs_remove_port_bucket (cntl->protid_bucket);
+}
diff --git a/libtrivfs/cntl-create.c b/libtrivfs/cntl-create.c
new file mode 100644
index 00000000..eb9a8340
--- /dev/null
+++ b/libtrivfs/cntl-create.c
@@ -0,0 +1,100 @@
+/* Create a new trivfs control port
+
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "trivfs.h"
+
+/* Create a new trivfs control port, with underlying node UNDERLYING, and
+ return it in CONTROL. CONTROL_CLASS & CONTROL_BUCKET are passed to
+ the ports library to create the control port, and PROTID_CLASS &
+ PROTID_BUCKET are used when creating ports representing opens of this
+ node. */
+error_t
+trivfs_create_control (mach_port_t underlying,
+ struct port_class *control_class,
+ struct port_bucket *control_bucket,
+ struct port_class *protid_class,
+ struct port_bucket *protid_bucket,
+ struct trivfs_control **control)
+{
+ error_t err;
+
+ /* Perhaps allocate, and perhaps add the specified port classes the ones
+ recognized by trivfs. */
+ err = trivfs_add_control_port_class (&control_class);
+ if (! err)
+ err = trivfs_add_protid_port_class (&protid_class);
+ else
+ protid_class = 0;
+
+ /* Perhaps allocate new port buckets. */
+ if (! err)
+ err = trivfs_add_port_bucket (&control_bucket);
+ else
+ control_bucket = 0;
+ if (! err)
+ {
+ if (! protid_bucket)
+ /* By default, use the same port bucket for both. */
+ protid_bucket = control_bucket;
+ err = trivfs_add_port_bucket (&protid_bucket);
+ }
+ else
+ protid_bucket = 0;
+
+ if (! err)
+ err = ports_create_port (control_class, control_bucket,
+ sizeof (struct trivfs_control), control);
+
+ if (! err)
+ {
+ (*control)->underlying = underlying;
+ (*control)->protid_class = protid_class;
+ (*control)->protid_bucket = protid_bucket;
+ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &(*control)->filesys_id);
+ if (err)
+ {
+ ports_port_deref (*control);
+ goto out;
+ }
+
+ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &(*control)->file_id);
+ if (err)
+ {
+ mach_port_destroy (mach_task_self (), (*control)->filesys_id);
+ ports_port_deref (*control);
+ goto out;
+ }
+
+ (*control)->hook = 0;
+ }
+
+out:
+ if (err)
+ {
+ trivfs_remove_control_port_class (control_class);
+ trivfs_remove_protid_port_class (protid_class);
+ trivfs_remove_port_bucket (control_bucket);
+ trivfs_remove_port_bucket (protid_bucket);
+ }
+
+ return err;
+}
diff --git a/libtrivfs/demuxer.c b/libtrivfs/demuxer.c
new file mode 100644
index 00000000..306cd115
--- /dev/null
+++ b/libtrivfs/demuxer.c
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 1993, 1994, 2013 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+
+#include "trivfs_io_S.h"
+#include "trivfs_fs_S.h"
+#include "../libports/notify_S.h"
+#include "trivfs_fsys_S.h"
+#include "../libports/interrupt_S.h"
+
+int
+trivfs_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ mig_routine_t routine;
+ if ((routine = trivfs_io_server_routine (inp)) ||
+ (routine = trivfs_fs_server_routine (inp)) ||
+ (routine = ports_notify_server_routine (inp)) ||
+ (routine = trivfs_fsys_server_routine (inp)) ||
+ (routine = ports_interrupt_server_routine (inp)))
+ {
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
diff --git a/libtrivfs/dir-chg.c b/libtrivfs/dir-chg.c
new file mode 100644
index 00000000..d01abbbf
--- /dev/null
+++ b/libtrivfs/dir-chg.c
@@ -0,0 +1,26 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+kern_return_t
+trivfs_S_dir_notice_changes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mach_port_t notify)
+{
+ return cred ? ENOTDIR : EOPNOTSUPP;
+}
diff --git a/libtrivfs/dir-link.c b/libtrivfs/dir-link.c
new file mode 100644
index 00000000..f5a8c735
--- /dev/null
+++ b/libtrivfs/dir-link.c
@@ -0,0 +1,30 @@
+/*
+ Copyright (C) 1994,96,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+kern_return_t
+trivfs_S_dir_link (struct trivfs_protid *dir,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct trivfs_protid *file, char *name, int excl)
+{
+ if (!file)
+ return EOPNOTSUPP;
+ if (!dir)
+ return EXDEV;
+ return ENOTDIR;
+}
diff --git a/libtrivfs/dir-lookup.c b/libtrivfs/dir-lookup.c
new file mode 100644
index 00000000..66afac1d
--- /dev/null
+++ b/libtrivfs/dir-lookup.c
@@ -0,0 +1,89 @@
+/*
+ Copyright (C) 1994,98,99,2001,02 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include <assert.h>
+#include <fcntl.h>
+#include <string.h>
+
+kern_return_t
+trivfs_S_dir_lookup (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *filename,
+ int flags,
+ mode_t mode,
+ retry_type *retry_type,
+ char *retry_name,
+ mach_port_t *retrypt,
+ mach_msg_type_name_t *retrypt_type)
+{
+ int perms;
+ error_t err;
+ struct trivfs_protid *newcred;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (filename[0])
+ return ENOTDIR;
+
+ /* This is a null-pathname "reopen" call; do the right thing. */
+
+ /* Burn off flags we don't actually implement */
+ flags &= O_HURD;
+ flags &= ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS);
+
+ /* Validate permissions */
+ if (! trivfs_check_access_hook)
+ file_check_access (cred->realnode, &perms);
+ else
+ (*trivfs_check_access_hook) (cred->po->cntl, cred->user,
+ cred->realnode, &perms);
+ if ((flags & (O_READ|O_WRITE|O_EXEC) & perms)
+ != (flags & (O_READ|O_WRITE|O_EXEC)))
+ return EACCES;
+
+ /* Execute the open */
+ err = 0;
+ if (trivfs_check_open_hook)
+ err = (*trivfs_check_open_hook) (cred->po->cntl, cred->user, flags);
+ if (!err)
+ {
+ struct iouser *user;
+
+ err = iohelp_dup_iouser (&user, cred->user);
+ if (err)
+ return err;
+
+ err = trivfs_open (cred->po->cntl, user, flags,
+ cred->realnode, &newcred);
+ if (err)
+ iohelp_free_iouser (user);
+ else
+ mach_port_mod_refs (mach_task_self (), cred->realnode,
+ MACH_PORT_RIGHT_SEND, +1);
+ }
+ if (err)
+ return err;
+
+ *retry_type = FS_RETRY_NORMAL;
+ *retry_name = '\0';
+ *retrypt = ports_get_right (newcred);
+ *retrypt_type = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newcred);
+ return 0;
+}
diff --git a/libtrivfs/dir-mkdir.c b/libtrivfs/dir-mkdir.c
new file mode 100644
index 00000000..d39bd3f8
--- /dev/null
+++ b/libtrivfs/dir-mkdir.c
@@ -0,0 +1,26 @@
+/*
+ Copyright (C) 1994,02 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+kern_return_t
+trivfs_S_dir_mkdir (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *name, mode_t mode)
+{
+ return cred ? ENOTDIR : EOPNOTSUPP;
+}
diff --git a/libtrivfs/dir-mkfile.c b/libtrivfs/dir-mkfile.c
new file mode 100644
index 00000000..048db244
--- /dev/null
+++ b/libtrivfs/dir-mkfile.c
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+kern_return_t
+trivfs_S_dir_mkfile (struct trivfs_protid *file,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int flags,
+ mode_t mode,
+ mach_port_t *newnod,
+ mach_msg_type_name_t *newnodetype)
+{
+ return file ? ENOTDIR : EOPNOTSUPP;
+}
diff --git a/libtrivfs/dir-readdir.c b/libtrivfs/dir-readdir.c
new file mode 100644
index 00000000..f1176f07
--- /dev/null
+++ b/libtrivfs/dir-readdir.c
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 1994,99,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+kern_return_t
+trivfs_S_dir_readdir (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data,
+ size_t *datalen,
+ boolean_t *data_dealloc,
+ int entry,
+ int nentries,
+ vm_size_t bufsiz,
+ int *amount)
+{
+ return cred ? ENOTDIR : EOPNOTSUPP;
+}
diff --git a/libtrivfs/dir-rename.c b/libtrivfs/dir-rename.c
new file mode 100644
index 00000000..d68233ec
--- /dev/null
+++ b/libtrivfs/dir-rename.c
@@ -0,0 +1,31 @@
+/*
+ Copyright (C) 1994,96,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+kern_return_t
+trivfs_S_dir_rename (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *name,
+ struct trivfs_protid *cred2, char *name2, int excl)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ if (!cred2)
+ return EXDEV;
+ return ENOTDIR;
+}
diff --git a/libtrivfs/dir-rmdir.c b/libtrivfs/dir-rmdir.c
new file mode 100644
index 00000000..a4d320c4
--- /dev/null
+++ b/libtrivfs/dir-rmdir.c
@@ -0,0 +1,26 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+kern_return_t
+trivfs_S_dir_rmdir (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *name)
+{
+ return cred ? ENOTDIR : EOPNOTSUPP;
+}
diff --git a/libtrivfs/dir-unlink.c b/libtrivfs/dir-unlink.c
new file mode 100644
index 00000000..a6ef9b0c
--- /dev/null
+++ b/libtrivfs/dir-unlink.c
@@ -0,0 +1,26 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+kern_return_t
+trivfs_S_dir_unlink (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *name)
+{
+ return cred ? ENOTDIR : EOPNOTSUPP;
+}
diff --git a/libtrivfs/dyn-classes.c b/libtrivfs/dyn-classes.c
new file mode 100644
index 00000000..a6bb7de1
--- /dev/null
+++ b/libtrivfs/dyn-classes.c
@@ -0,0 +1,270 @@
+/* Dynamically allocated port classes/buckets recognized by trivfs
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* Auxiliary info for each vector element. */
+struct aux
+{
+ void (*free_el)();
+ unsigned refs;
+};
+
+/* Vectors of dynamically allocated port classes/buckets. */
+
+/* Protid port classes. */
+struct port_class **trivfs_dynamic_protid_port_classes = 0;
+size_t trivfs_num_dynamic_protid_port_classes = 0;
+static struct aux *dynamic_protid_port_classes_aux = 0;
+static size_t dynamic_protid_port_classes_sz = 0;
+
+/* Control port classes. */
+struct port_class **trivfs_dynamic_control_port_classes = 0;
+size_t trivfs_num_dynamic_control_port_classes = 0;
+static struct aux *dynamic_control_port_classes_aux = 0;
+static size_t dynamic_control_port_classes_sz = 0;
+
+/* Port buckets. */
+struct port_bucket **trivfs_dynamic_port_buckets = 0;
+size_t trivfs_num_dynamic_port_buckets = 0;
+static struct aux *dynamic_port_buckets_aux = 0;
+static size_t dynamic_port_buckets_sz = 0;
+
+/* Lock used to control access to all the above vectors. */
+static pthread_mutex_t dyn_lock = PTHREAD_MUTEX_INITIALIZER;
+
+
+/* Add EL to the vector pointed to by VEC_V, which should point to a vector
+ of pointers of some type, and has a length stored in *SZ; If there's
+ already a pointer to EL in VEC_V, nothing will actually be added, but the
+ reference count for that element will be increased. *NUM is the actual
+ number of non-null elements in the vector, and will be incremented if
+ something is actually added. AUX_VEC is a pointer to a vector of struct
+ aux elements, that contains information parralleling VEC_V. FREE_EL, if
+ non-zero, should be a function that takes a single argument of the same
+ type as EL, and deallocates it; this function is called in the following
+ cases: (1) An error is encountered trying to grow one of the vectors, (2)
+ when the element is eventually freed by drop_el. */
+static error_t
+add_el (void *el, void (*free_el)(),
+ void *vec_v, struct aux **aux_vec,
+ size_t *sz, size_t *num)
+{
+ int i;
+ size_t new_sz;
+ void ***vec, **new_vec;
+ struct aux *new_aux_vec;
+
+ if (! el)
+ return ENOMEM;
+
+ pthread_mutex_lock (&dyn_lock);
+
+ vec = vec_v;
+
+ for (i = 0; i < *sz; i++)
+ if (! (*vec)[i])
+ {
+ (*vec)[i] = el;
+ (*aux_vec)[i].free_el = free_el;
+ (*aux_vec)[i].refs = 1;
+ (*num)++;
+ pthread_mutex_unlock (&dyn_lock);
+ return 0;
+ }
+ else if ((*vec)[i] == el)
+ {
+ (*aux_vec)[i].refs++;
+ pthread_mutex_unlock (&dyn_lock);
+ return 0;
+ }
+
+ new_sz = *sz + 4;
+ new_vec = realloc (*vec, new_sz * sizeof (void *));
+ new_aux_vec = realloc (*aux_vec, new_sz * sizeof (struct aux));
+
+ if (!new_vec || !new_aux_vec)
+ {
+ if (free_el)
+ (*free_el) (el);
+ /* One of the vectors might be the wrong size, but who cares. */
+ return ENOMEM;
+ }
+
+ for (i = *sz; i < new_sz; i++)
+ new_vec[i] = 0;
+
+ new_vec[*sz] = el;
+ new_aux_vec[*sz].free_el = free_el;
+ new_aux_vec[*sz].refs = 1;
+ (*num)++;
+
+ *vec = new_vec;
+ *aux_vec = new_aux_vec;
+ *sz = new_sz;
+
+ pthread_mutex_unlock (&dyn_lock);
+
+ return 0;
+}
+
+/* Scan VEC_V, which should be a vector of SZ pointers of the same type as
+ EL, for EL; if it is found, then decrement its reference count, and if
+ that goes to zero, decrement *NUM and free EL if it had an associated free
+ routine passed to add_el. */
+static void
+drop_el (void *el, void *vec_v, struct aux *aux_vec,
+ size_t sz, size_t *num)
+{
+ int i;
+ void **vec;
+
+ if (! el)
+ return;
+
+ pthread_mutex_lock (&dyn_lock);
+
+ vec = vec_v;
+
+ for (i = 0; i < sz; i++)
+ if (vec[i] == el)
+ {
+ if (aux_vec[i].refs == 1)
+ {
+ if (aux_vec[i].free_el)
+ (*aux_vec[i].free_el) (el);
+ vec[i] = 0;
+ (*num)--;
+ }
+ else
+ aux_vec[i].refs--;
+ break;
+ }
+
+ pthread_mutex_unlock (&dyn_lock);
+}
+
+/* Add the port class *CLASS to the list of control port classes recognized
+ by trivfs; if *CLASS is 0, an attempt is made to allocate a new port
+ class, which is stored in *CLASS. */
+error_t
+trivfs_add_control_port_class (struct port_class **class)
+{
+ /* XXX Gee, there *is no* way of freeing port classes or buckets! So we
+ actually never free anything! */
+
+ if (! *class)
+ {
+ *class = ports_create_class (trivfs_clean_cntl, 0);
+ if (! *class)
+ return ENOMEM;
+ }
+
+ return
+ add_el (*class, 0,
+ &trivfs_dynamic_control_port_classes,
+ &dynamic_control_port_classes_aux,
+ &dynamic_control_port_classes_sz,
+ &trivfs_num_dynamic_control_port_classes);
+}
+
+/* Remove the previously added dynamic control port class CLASS, freeing it
+ if it was allocated by trivfs_add_control_port_class. */
+void
+trivfs_remove_control_port_class (struct port_class *class)
+{
+ drop_el (class,
+ trivfs_dynamic_control_port_classes,
+ dynamic_control_port_classes_aux,
+ dynamic_control_port_classes_sz,
+ &trivfs_num_dynamic_control_port_classes);
+}
+
+/* Add the port class *CLASS to the list of protid port classes recognized by
+ trivfs; if *CLASS is 0, an attempt is made to allocate a new port class,
+ which is stored in *CLASS. */
+error_t
+trivfs_add_protid_port_class (struct port_class **class)
+{
+ /* XXX Gee, there *is no* way of freeing port classes or buckets! So we
+ actually never free anything! */
+
+ if (! *class)
+ {
+ *class = ports_create_class (trivfs_clean_protid, 0);
+ if (! *class)
+ return ENOMEM;
+ }
+
+ return
+ add_el (*class, 0,
+ &trivfs_dynamic_protid_port_classes,
+ &dynamic_protid_port_classes_aux,
+ &dynamic_protid_port_classes_sz,
+ &trivfs_num_dynamic_protid_port_classes);
+}
+
+/* Remove the previously added dynamic protid port class CLASS, freeing it
+ if it was allocated by trivfs_add_protid_port_class. */
+void
+trivfs_remove_protid_port_class (struct port_class *class)
+{
+ drop_el (class,
+ trivfs_dynamic_protid_port_classes,
+ dynamic_protid_port_classes_aux,
+ dynamic_protid_port_classes_sz,
+ &trivfs_num_dynamic_protid_port_classes);
+}
+
+/* Add the port bucket *BUCKET to the list of dynamically allocated port
+ buckets; if *bucket is 0, an attempt is made to allocate a new port
+ bucket, which is then stored in *bucket. */
+error_t
+trivfs_add_port_bucket (struct port_bucket **bucket)
+{
+ /* XXX Gee, there *is no* way of freeing port bucketes or buckets! So we
+ actually never free anything! */
+
+ if (! *bucket)
+ {
+ *bucket = ports_create_bucket ();
+ if (! *bucket)
+ return ENOMEM;
+ }
+
+ return
+ add_el (*bucket, 0,
+ &trivfs_dynamic_port_buckets,
+ &dynamic_port_buckets_aux,
+ &dynamic_port_buckets_sz,
+ &trivfs_num_dynamic_port_buckets);
+}
+
+/* Remove the previously added dynamic port bucket BUCKET, freeing it
+ if it was allocated by trivfs_add_port_bucket. */
+void
+trivfs_remove_port_bucket (struct port_bucket *bucket)
+{
+ drop_el (bucket,
+ trivfs_dynamic_port_buckets,
+ dynamic_port_buckets_aux,
+ dynamic_port_buckets_sz,
+ &trivfs_num_dynamic_port_buckets);
+}
diff --git a/libtrivfs/file-access.c b/libtrivfs/file-access.c
new file mode 100644
index 00000000..f289a23b
--- /dev/null
+++ b/libtrivfs/file-access.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 1994,96,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+error_t
+trivfs_S_file_check_access (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *allowed)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+
+ if (! trivfs_check_access_hook)
+ file_check_access (cred->realnode, allowed);
+ else
+ (*trivfs_check_access_hook) (cred->po->cntl, cred->user,
+ cred->realnode, allowed);
+
+ return 0;
+}
diff --git a/libtrivfs/file-chauthor.c b/libtrivfs/file-chauthor.c
new file mode 100644
index 00000000..660181cb
--- /dev/null
+++ b/libtrivfs/file-chauthor.c
@@ -0,0 +1,27 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+kern_return_t
+trivfs_S_file_chauthor (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ uid_t auth)
+{
+ return cred ? file_chauthor (cred->realnode, auth) : EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-chflags.c b/libtrivfs/file-chflags.c
new file mode 100644
index 00000000..d427015f
--- /dev/null
+++ b/libtrivfs/file-chflags.c
@@ -0,0 +1,27 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+kern_return_t
+trivfs_S_file_chflags (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int flags)
+{
+ return cred ? file_chflags (cred->realnode, flags) : EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-chg.c b/libtrivfs/file-chg.c
new file mode 100644
index 00000000..0e8c8394
--- /dev/null
+++ b/libtrivfs/file-chg.c
@@ -0,0 +1,27 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+kern_return_t
+trivfs_S_file_notice_changes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mach_port_t notify)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-chmod.c b/libtrivfs/file-chmod.c
new file mode 100644
index 00000000..8c15ec2b
--- /dev/null
+++ b/libtrivfs/file-chmod.c
@@ -0,0 +1,28 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+kern_return_t
+trivfs_S_file_chmod (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mode_t mode)
+{
+ /* Is this right? */
+ return cred ? file_chmod (cred->realnode, mode) : EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-chown.c b/libtrivfs/file-chown.c
new file mode 100644
index 00000000..416103f2
--- /dev/null
+++ b/libtrivfs/file-chown.c
@@ -0,0 +1,27 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+kern_return_t
+trivfs_S_file_chown (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ uid_t uid, gid_t gid)
+{
+ return cred ? file_chown (cred->realnode, uid, gid) : EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-exec.c b/libtrivfs/file-exec.c
new file mode 100644
index 00000000..b353d8a3
--- /dev/null
+++ b/libtrivfs/file-exec.c
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+kern_return_t
+trivfs_S_file_exec (trivfs_protid_t exec_file,
+ mach_port_t reply,
+ mach_msg_type_name_t replyPoly,
+ mach_port_t exec_task,
+ int flags,
+ data_t argv,
+ mach_msg_type_number_t argvCnt,
+ data_t envp,
+ mach_msg_type_number_t envpCnt,
+ portarray_t fdarray,
+ mach_msg_type_number_t fdarrayCnt,
+ portarray_t portarray,
+ mach_msg_type_number_t portarrayCnt,
+ intarray_t intarray,
+ mach_msg_type_number_t intarrayCnt,
+ mach_port_array_t deallocnames,
+ mach_msg_type_number_t deallocnamesCnt,
+ mach_port_array_t destroynames,
+ mach_msg_type_number_t destroynamesCnt)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-get-children.c b/libtrivfs/file-get-children.c
new file mode 100644
index 00000000..4126119e
--- /dev/null
+++ b/libtrivfs/file-get-children.c
@@ -0,0 +1,36 @@
+/* file_get_children
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+/* Return any active translators bound to nodes of the receiving
+ filesystem. CHILDREN is an argz vector containing file names
+ relative to the root of the receiving translator. */
+error_t
+trivfs_S_file_get_children (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replyPoly,
+ char **children,
+ mach_msg_type_number_t *children_len)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-get-fs-options.c b/libtrivfs/file-get-fs-options.c
new file mode 100644
index 00000000..3c8bbca1
--- /dev/null
+++ b/libtrivfs/file-get-fs-options.c
@@ -0,0 +1,52 @@
+/* Get runtime options given a file handle
+
+ Copyright (C) 1996,98,2002 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <argz.h>
+#include <hurd/fshelp.h>
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+error_t
+trivfs_S_file_get_fs_options (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ char **data, mach_msg_type_number_t *len)
+{
+ error_t err;
+ char *argz = 0;
+ size_t argz_len = 0;
+
+ if (! cred)
+ return EOPNOTSUPP;
+
+ err = argz_add (&argz, &argz_len, program_invocation_name);
+ if (err)
+ return err;
+
+ err = trivfs_append_args (cred->po->cntl, &argz, &argz_len);
+ if (! err)
+ /* Put ARGZ into vm_alloced memory for the return trip. */
+ err = iohelp_return_malloced_buffer (argz, argz_len, data, len);
+ else
+ free (argz);
+
+ return err;
+}
diff --git a/libtrivfs/file-get-source.c b/libtrivfs/file-get-source.c
new file mode 100644
index 00000000..f6637d87
--- /dev/null
+++ b/libtrivfs/file-get-source.c
@@ -0,0 +1,34 @@
+/* file_get_source
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+/* Return information about the source of the receiving
+ filesystem. */
+error_t
+trivfs_S_file_get_source (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replyPoly,
+ char *source)
+{
+ return cred? trivfs_get_source (cred, source, 1024 /* XXX */): EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-get-storage-info.c b/libtrivfs/file-get-storage-info.c
new file mode 100644
index 00000000..60fdc989
--- /dev/null
+++ b/libtrivfs/file-get-storage-info.c
@@ -0,0 +1,34 @@
+/* Stub for the file_get_storage_info RPC as described in <hurd/fs.defs>.
+ Copyright (C) 1995,96,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+error_t
+trivfs_S_file_get_storage_info (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mach_port_t **ports,
+ mach_msg_type_name_t *ports_type,
+ mach_msg_type_number_t *num_ports,
+ int **ints, mach_msg_type_number_t *num_ints,
+ off_t **offsets,
+ mach_msg_type_number_t *num_offsets,
+ char **data, mach_msg_type_number_t *data_len)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-get-trans.c b/libtrivfs/file-get-trans.c
new file mode 100644
index 00000000..69de4532
--- /dev/null
+++ b/libtrivfs/file-get-trans.c
@@ -0,0 +1,28 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+kern_return_t
+trivfs_S_file_get_translator (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ char **trans, size_t *translen)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-get-transcntl.c b/libtrivfs/file-get-transcntl.c
new file mode 100644
index 00000000..7deb3044
--- /dev/null
+++ b/libtrivfs/file-get-transcntl.c
@@ -0,0 +1,28 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+kern_return_t
+trivfs_S_file_get_translator_cntl (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mach_port_t *cntl,
+ mach_msg_type_name_t *cntl_type)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-getcontrol.c b/libtrivfs/file-getcontrol.c
new file mode 100644
index 00000000..36c2ca3d
--- /dev/null
+++ b/libtrivfs/file-getcontrol.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+kern_return_t
+trivfs_S_file_getcontrol (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mach_port_t *cntl, mach_msg_type_name_t *cntltype)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ if (!cred->isroot)
+ return EPERM;
+
+ *cntl = ports_get_right (cred->po->cntl);
+ *cntltype = MACH_MSG_TYPE_MAKE_SEND;
+ return 0;
+}
diff --git a/libtrivfs/file-getfh.c b/libtrivfs/file-getfh.c
new file mode 100644
index 00000000..d585936e
--- /dev/null
+++ b/libtrivfs/file-getfh.c
@@ -0,0 +1,27 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+kern_return_t
+trivfs_S_file_getfh (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, size_t *datalen)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-getlinknode.c b/libtrivfs/file-getlinknode.c
new file mode 100644
index 00000000..1802d9a6
--- /dev/null
+++ b/libtrivfs/file-getlinknode.c
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+kern_return_t
+trivfs_S_file_getlinknode (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mach_port_t *linknode,
+ mach_msg_type_name_t *linknodetype)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ *linknode = cred->realnode;
+ *linknodetype = MACH_MSG_TYPE_COPY_SEND;
+ return 0;
+}
diff --git a/libtrivfs/file-lock.c b/libtrivfs/file-lock.c
new file mode 100644
index 00000000..c89f2fac
--- /dev/null
+++ b/libtrivfs/file-lock.c
@@ -0,0 +1,35 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+kern_return_t
+trivfs_S_file_lock (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int flags)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+trivfs_S_file_lock_stat (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *mystatus, int *otherstat)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-reparent.c b/libtrivfs/file-reparent.c
new file mode 100644
index 00000000..5892013c
--- /dev/null
+++ b/libtrivfs/file-reparent.c
@@ -0,0 +1,36 @@
+/* Reparent a directory
+
+ Copyright (C) 1997,2002,2010 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+#include "trivfs_io_S.h"
+
+error_t
+trivfs_S_file_reparent (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mach_port_t parent,
+ mach_port_t *new, mach_msg_type_name_t *new_type)
+{
+ /* This is not a directory, so we just duplicate it */
+ error_t ret = trivfs_S_io_duplicate (cred, reply, reply_type, new, new_type);
+ if (!ret)
+ mach_port_deallocate (mach_task_self (), parent);
+ return ret;
+}
diff --git a/libtrivfs/file-set-size.c b/libtrivfs/file-set-size.c
new file mode 100644
index 00000000..f90ceec6
--- /dev/null
+++ b/libtrivfs/file-set-size.c
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+#include <assert.h>
+
+kern_return_t
+trivfs_S_file_set_size (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t size)
+{
+ assert (!trivfs_support_write);
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-set-trans.c b/libtrivfs/file-set-trans.c
new file mode 100644
index 00000000..47a7f7c4
--- /dev/null
+++ b/libtrivfs/file-set-trans.c
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+kern_return_t
+trivfs_S_file_set_translator (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int actflags,
+ int passflags,
+ int oldtransflags,
+ char *trans,
+ size_t translen,
+ mach_port_t existing)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-statfs.c b/libtrivfs/file-statfs.c
new file mode 100644
index 00000000..348126e8
--- /dev/null
+++ b/libtrivfs/file-statfs.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 1994,96,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+#include <string.h>
+#include <unistd.h>
+
+kern_return_t
+trivfs_S_file_statfs (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct statfs *stb)
+{
+ if (!trivfs_fsid)
+ trivfs_fsid = getpid();
+
+ bzero (stb, sizeof (struct statfs));
+ stb->f_type = trivfs_fstype;
+ stb->f_fsid = trivfs_fsid;
+
+ return 0;
+}
diff --git a/libtrivfs/file-sync.c b/libtrivfs/file-sync.c
new file mode 100644
index 00000000..f26f3c16
--- /dev/null
+++ b/libtrivfs/file-sync.c
@@ -0,0 +1,27 @@
+/*
+ Copyright (C) 1994,96,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+kern_return_t
+trivfs_S_file_sync (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int wait, int omitmeta)
+{
+ return cred ? file_sync (cred->realnode, wait, omitmeta) : EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-syncfs.c b/libtrivfs/file-syncfs.c
new file mode 100644
index 00000000..e379df29
--- /dev/null
+++ b/libtrivfs/file-syncfs.c
@@ -0,0 +1,28 @@
+/*
+ Copyright (C) 1994,96,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+kern_return_t
+trivfs_S_file_syncfs (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int wait,
+ int dochildren)
+{
+ return cred ? file_sync (cred->realnode, wait, 0) : EOPNOTSUPP;
+}
diff --git a/libtrivfs/file-utimes.c b/libtrivfs/file-utimes.c
new file mode 100644
index 00000000..827c0554
--- /dev/null
+++ b/libtrivfs/file-utimes.c
@@ -0,0 +1,27 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fs_S.h"
+
+kern_return_t
+trivfs_S_file_utimes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ time_value_t atime, time_value_t mtime)
+{
+ return cred ? file_utimes (cred->realnode, atime, mtime) : EOPNOTSUPP;
+}
diff --git a/libtrivfs/fsys-forward.c b/libtrivfs/fsys-forward.c
new file mode 100644
index 00000000..8f92d454
--- /dev/null
+++ b/libtrivfs/fsys-forward.c
@@ -0,0 +1,39 @@
+/* fsys_forward
+
+ Copyright (C) 1995,2002 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fsys_S.h"
+
+/* Ask SERVER to provide fsys translation service for us. REQUESTOR is
+ the bootstrap port supplied to the original translator, and ARGV are
+ the command line arguments. If the recipient accepts the request, he
+ (or some delegate) should send fsys_startup to REQUESTOR to start the
+ filesystem up. */
+error_t
+trivfs_S_fsys_forward (mach_port_t server,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t requestor,
+ char *argz, size_t argz_len)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/fsys-get-options.c b/libtrivfs/fsys-get-options.c
new file mode 100644
index 00000000..8030435c
--- /dev/null
+++ b/libtrivfs/fsys-get-options.c
@@ -0,0 +1,51 @@
+/* Get runtime options
+
+ Copyright (C) 1996,98,2002 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <argz.h>
+#include <hurd/fshelp.h>
+
+#include "priv.h"
+#include "trivfs_fsys_S.h"
+
+error_t
+trivfs_S_fsys_get_options (struct trivfs_control *fsys,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, mach_msg_type_number_t *len)
+{
+ error_t err;
+ char *argz = 0;
+ size_t argz_len = 0;
+
+ if (! fsys)
+ return EOPNOTSUPP;
+
+ err = argz_add (&argz, &argz_len, program_invocation_name);
+ if (err)
+ return err;
+
+ err = trivfs_append_args (fsys, &argz, &argz_len);
+ if (! err)
+ /* Put ARGZ into vm_alloced memory for the return trip. */
+ err = iohelp_return_malloced_buffer (argz, argz_len, data, len);
+ else
+ free (argz);
+
+ return err;
+}
diff --git a/libtrivfs/fsys-getroot.c b/libtrivfs/fsys-getroot.c
new file mode 100644
index 00000000..d343e146
--- /dev/null
+++ b/libtrivfs/fsys-getroot.c
@@ -0,0 +1,120 @@
+/*
+ Copyright (C) 1993,94,95,97,99,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "fsys_reply_U.h"
+#include "trivfs_fsys_S.h"
+#include <assert.h>
+#include <fcntl.h>
+#include <string.h>
+
+kern_return_t
+trivfs_S_fsys_getroot (struct trivfs_control *cntl,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_port_type,
+ mach_port_t dotdot,
+ uid_t *uids, size_t nuids,
+ uid_t *gids, size_t ngids,
+ int flags,
+ retry_type *do_retry,
+ char *retry_name,
+ mach_port_t *newpt,
+ mach_msg_type_name_t *newpttype)
+{
+ int perms;
+ error_t err = 0;
+ mach_port_t new_realnode;
+ struct trivfs_protid *cred;
+ struct iouser *user;
+
+ if (!cntl)
+ return EOPNOTSUPP;
+
+ if (trivfs_getroot_hook)
+ {
+ err = (*trivfs_getroot_hook) (cntl, reply_port, reply_port_type, dotdot,
+ uids, nuids, gids, ngids, flags,
+ do_retry, retry_name, newpt, newpttype);
+ if (err != EAGAIN)
+ return err;
+ }
+
+ if ((flags & O_WRITE & trivfs_allow_open) != (flags & O_WRITE))
+ return EROFS;
+ if ((flags & (O_READ|O_WRITE|O_EXEC) & trivfs_allow_open)
+ != (flags & (O_READ|O_WRITE|O_EXEC)))
+ return EACCES;
+
+ /* O_CREAT and O_EXCL are not meaningful here; O_NOLINK and O_NOTRANS
+ will only be useful when trivfs supports translators (which it doesn't
+ now). */
+ flags &= O_HURD;
+ flags &= ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS);
+
+ err = io_restrict_auth (cntl->underlying,
+ &new_realnode, uids, nuids, gids, ngids);
+ if (err)
+ return err;
+
+ err = iohelp_create_complex_iouser (&user, uids, nuids, gids, ngids);
+ if (err)
+ return err;
+
+ /* Validate permissions. */
+ if (! trivfs_check_access_hook)
+ file_check_access (new_realnode, &perms);
+ else
+ (*trivfs_check_access_hook) (cntl, user, new_realnode, &perms);
+ if ((flags & (O_READ|O_WRITE|O_EXEC) & perms)
+ != (flags & (O_READ|O_WRITE|O_EXEC)))
+ err = EACCES;
+
+ if (!err && trivfs_check_open_hook)
+ err = (*trivfs_check_open_hook) (cntl, user, flags);
+ if (!err)
+ {
+ if (! trivfs_open_hook)
+ {
+ err = trivfs_open (cntl, user, flags, new_realnode, &cred);
+ if (!err)
+ mach_port_deallocate (mach_task_self (), dotdot);
+ }
+ else
+ err = (*trivfs_open_hook) (cntl, user, dotdot, flags, new_realnode,
+ &cred);
+ }
+
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), new_realnode);
+ iohelp_free_iouser (user);
+ }
+ else
+ {
+ *do_retry = FS_RETRY_NORMAL;
+ *retry_name = '\0';
+ *newpt = ports_get_right (cred);
+ *newpttype = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (cred);
+ }
+
+ return err;
+}
diff --git a/libtrivfs/fsys-goaway.c b/libtrivfs/fsys-goaway.c
new file mode 100644
index 00000000..d551d551
--- /dev/null
+++ b/libtrivfs/fsys-goaway.c
@@ -0,0 +1,35 @@
+/*
+ Copyright (C) 1994,95,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_fsys_S.h"
+
+kern_return_t
+trivfs_S_fsys_goaway (struct trivfs_control *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int flags)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ return trivfs_goaway (cred, flags);
+}
diff --git a/libtrivfs/fsys-set-options.c b/libtrivfs/fsys-set-options.c
new file mode 100644
index 00000000..135bc746
--- /dev/null
+++ b/libtrivfs/fsys-set-options.c
@@ -0,0 +1,36 @@
+/* Set runtime options
+
+ Copyright (C) 1996,2002 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd/fshelp.h>
+
+#include "priv.h"
+#include "trivfs_fsys_S.h"
+
+error_t
+trivfs_S_fsys_set_options (struct trivfs_control *cntl,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *data, mach_msg_type_number_t len,
+ int do_children)
+{
+ if (cntl)
+ return trivfs_set_options (cntl, data, len);
+ else
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/fsys-stubs.c b/libtrivfs/fsys-stubs.c
new file mode 100644
index 00000000..bcac43c9
--- /dev/null
+++ b/libtrivfs/fsys-stubs.c
@@ -0,0 +1,72 @@
+/*
+ Copyright (C) 1994,95,96,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_fsys_S.h"
+
+kern_return_t
+trivfs_S_fsys_startup (mach_port_t bootport,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int flags,
+ mach_port_t cntl,
+ mach_port_t *realnode,
+ mach_msg_type_name_t *realnodetype)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+trivfs_S_fsys_getpriv (struct trivfs_control *cntl,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t *host, mach_msg_type_name_t *host_privPoly,
+ mach_port_t *dev, mach_msg_type_name_t *devPoly,
+ mach_port_t *fstask, mach_msg_type_name_t *fstPoly)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+trivfs_S_fsys_init (struct trivfs_control *control,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t proc,
+ auth_t auth)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+trivfs_S_fsys_getfile (struct trivfs_control *cntl,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ uid_t *genuids,
+ size_t ngenuids,
+ uid_t *gengids,
+ size_t ngengids,
+ char *handle,
+ size_t handlesize,
+ mach_port_t *file,
+ mach_msg_type_name_t *filetype)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/fsys-syncfs.c b/libtrivfs/fsys-syncfs.c
new file mode 100644
index 00000000..0c337f43
--- /dev/null
+++ b/libtrivfs/fsys-syncfs.c
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 1994,96,2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_fsys_S.h"
+
+kern_return_t
+trivfs_S_fsys_syncfs (struct trivfs_control *cntl,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int wait,
+ int dochildren)
+{
+ return cntl ? file_sync (cntl->underlying, wait, 0) : EOPNOTSUPP;
+}
diff --git a/libtrivfs/get-source.c b/libtrivfs/get-source.c
new file mode 100644
index 00000000..2605dacc
--- /dev/null
+++ b/libtrivfs/get-source.c
@@ -0,0 +1,28 @@
+/* Default version of trivfs_get_source
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "priv.h"
+
+error_t
+trivfs_get_source (struct trivfs_protid *cred, char *source, size_t source_len)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/io-async-icky.c b/libtrivfs/io-async-icky.c
new file mode 100644
index 00000000..3e0cf6fc
--- /dev/null
+++ b/libtrivfs/io-async-icky.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 1993,94,99,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <assert.h>
+
+kern_return_t
+trivfs_S_io_get_icky_async_id (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t *id,
+ mach_msg_type_name_t *idtype)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/io-async.c b/libtrivfs/io-async.c
new file mode 100644
index 00000000..874c5899
--- /dev/null
+++ b/libtrivfs/io-async.c
@@ -0,0 +1,35 @@
+/*
+ Copyright (C) 1993,94,99,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <assert.h>
+
+kern_return_t
+trivfs_S_io_async (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t notify,
+ mach_port_t *id,
+ mach_msg_type_name_t *idtype)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/io-duplicate.c b/libtrivfs/io-duplicate.c
new file mode 100644
index 00000000..5e2fabfb
--- /dev/null
+++ b/libtrivfs/io-duplicate.c
@@ -0,0 +1,48 @@
+/*
+ Copyright (C) 1993,94,95,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <string.h>
+
+kern_return_t
+trivfs_S_io_duplicate (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t *newport,
+ mach_msg_type_name_t *newporttype)
+{
+ error_t err;
+ struct trivfs_protid *newcred;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ err = trivfs_protid_dup (cred, &newcred);
+ if (!err)
+ {
+ *newport = ports_get_right (newcred);
+ *newporttype = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newcred);
+ }
+
+ return err;
+}
diff --git a/libtrivfs/io-identity.c b/libtrivfs/io-identity.c
new file mode 100644
index 00000000..a67ed7ec
--- /dev/null
+++ b/libtrivfs/io-identity.c
@@ -0,0 +1,51 @@
+/* Fetching identity port
+ Copyright (C) 1996,2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+
+error_t
+trivfs_S_io_identity (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t *idport,
+ mach_msg_type_name_t *idport_type,
+ mach_port_t *fsidport,
+ mach_msg_type_name_t *fsidport_type,
+ ino_t *fileno)
+{
+ error_t err;
+ struct stat st;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ err = io_stat (cred->realnode, &st);
+ if (err)
+ return err;
+ trivfs_modify_stat (cred, &st);
+
+ *idport = cred->po->cntl->file_id;
+ *idport_type = MACH_MSG_TYPE_MAKE_SEND;
+ *fsidport = cred->po->cntl->filesys_id;
+ *fsidport_type = MACH_MSG_TYPE_MAKE_SEND;
+ *fileno = st.st_ino;
+ return 0;
+}
diff --git a/libtrivfs/io-map.c b/libtrivfs/io-map.c
new file mode 100644
index 00000000..39428ad2
--- /dev/null
+++ b/libtrivfs/io-map.c
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 1993,94,2002 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <assert.h>
+
+kern_return_t
+trivfs_S_io_map (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t *rdobj,
+ mach_msg_type_name_t *rdtype,
+ mach_port_t *wrobj,
+ mach_msg_type_name_t *wrtype)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/io-modes-get.c b/libtrivfs/io-modes-get.c
new file mode 100644
index 00000000..9ed591cc
--- /dev/null
+++ b/libtrivfs/io-modes-get.c
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 1993,94,99,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <assert.h>
+
+kern_return_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+ *bits = cred->po->openmodes;
+ return 0;
+ }
+}
diff --git a/libtrivfs/io-modes-off.c b/libtrivfs/io-modes-off.c
new file mode 100644
index 00000000..786f0a13
--- /dev/null
+++ b/libtrivfs/io-modes-off.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 1993,94,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <assert.h>
+
+kern_return_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ assert (!trivfs_support_read && !trivfs_support_write);
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/io-modes-on.c b/libtrivfs/io-modes-on.c
new file mode 100644
index 00000000..74b10ad3
--- /dev/null
+++ b/libtrivfs/io-modes-on.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 1993,94,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <assert.h>
+
+kern_return_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ assert (!trivfs_support_read && !trivfs_support_write);
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/io-modes-set.c b/libtrivfs/io-modes-set.c
new file mode 100644
index 00000000..bc576261
--- /dev/null
+++ b/libtrivfs/io-modes-set.c
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 1993, 1994 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <assert.h>
+
+error_t
+trivfs_S_io_set_all_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int mode)
+{
+ assert (!trivfs_support_read && !trivfs_support_write);
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/io-owner-get.c b/libtrivfs/io-owner-get.c
new file mode 100644
index 00000000..5ffce4d7
--- /dev/null
+++ b/libtrivfs/io-owner-get.c
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 1993,94,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <assert.h>
+
+kern_return_t
+trivfs_S_io_get_owner (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ pid_t *owner)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/io-owner-mod.c b/libtrivfs/io-owner-mod.c
new file mode 100644
index 00000000..8b85e7c8
--- /dev/null
+++ b/libtrivfs/io-owner-mod.c
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 1993,94,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <assert.h>
+
+kern_return_t
+trivfs_S_io_mod_owner (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ pid_t owner)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/io-pathconf.c b/libtrivfs/io-pathconf.c
new file mode 100644
index 00000000..f4a4edff
--- /dev/null
+++ b/libtrivfs/io-pathconf.c
@@ -0,0 +1,30 @@
+/*
+ Copyright (C) 1994,96,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+
+kern_return_t
+trivfs_S_io_pathconf (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int name, int *val)
+{
+ if (cred)
+ return io_pathconf (cred->realnode, name, val);
+ else
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/io-read.c b/libtrivfs/io-read.c
new file mode 100644
index 00000000..7dfdc190
--- /dev/null
+++ b/libtrivfs/io-read.c
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <assert.h>
+
+kern_return_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ char **data,
+ mach_msg_type_number_t *datalen,
+ off_t off,
+ mach_msg_type_number_t amt)
+{
+ assert (!trivfs_support_read);
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/io-readable.c b/libtrivfs/io-readable.c
new file mode 100644
index 00000000..90e66c77
--- /dev/null
+++ b/libtrivfs/io-readable.c
@@ -0,0 +1,30 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <assert.h>
+
+kern_return_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_msg_type_number_t *amount)
+{
+ assert (!trivfs_support_read);
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/io-reauthenticate.c b/libtrivfs/io-reauthenticate.c
new file mode 100644
index 00000000..df0ed2ee
--- /dev/null
+++ b/libtrivfs/io-reauthenticate.c
@@ -0,0 +1,94 @@
+/*
+ Copyright (C) 1993,94,95,96,2000,01,02 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <assert.h>
+#include <string.h>
+
+kern_return_t
+trivfs_S_io_reauthenticate (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t rendport)
+{
+ struct trivfs_protid *newcred;
+ error_t err;
+ auth_t auth;
+ mach_port_t newright;
+
+ if (cred == 0)
+ return EOPNOTSUPP;
+
+ do
+ err = ports_create_port_noinstall (cred->po->cntl->protid_class,
+ cred->po->cntl->protid_bucket,
+ sizeof (struct trivfs_protid),
+ &newcred);
+ while (err == EINTR);
+ if (err)
+ return err;
+
+ auth = getauth ();
+ newright = ports_get_send_right (newcred);
+ assert (newright != MACH_PORT_NULL);
+
+ err = iohelp_reauth (&newcred->user, auth, rendport, newright, 1);
+ mach_port_deallocate (mach_task_self (), rendport);
+ mach_port_deallocate (mach_task_self (), auth);
+ if (err)
+ return err;
+
+ mach_port_deallocate (mach_task_self (), newright);
+ if (idvec_contains (newcred->user->uids, 0))
+ newcred->isroot = 1;
+
+ newcred->hook = cred->hook;
+ newcred->po = cred->po;
+ refcount_ref (&newcred->po->refcnt);
+
+ do
+ err = io_restrict_auth (newcred->po->cntl->underlying, &newcred->realnode,
+ newcred->user->uids->ids,
+ newcred->user->uids->num,
+ newcred->user->gids->ids,
+ newcred->user->gids->num);
+ while (err == EINTR);
+ if (!err && trivfs_protid_create_hook)
+ {
+ do
+ err = (*trivfs_protid_create_hook) (newcred);
+ while (err == EINTR);
+ if (err)
+ mach_port_deallocate (mach_task_self (), newcred->realnode);
+ }
+
+ if (err)
+ /* Signal that the user destroy hook shouldn't be called on NEWCRED. */
+ newcred->realnode = MACH_PORT_NULL;
+
+ mach_port_move_member (mach_task_self (), newcred->pi.port_right,
+ cred->po->cntl->protid_bucket->portset);
+
+ ports_port_deref (newcred);
+
+ return err;
+}
diff --git a/libtrivfs/io-restrict-auth.c b/libtrivfs/io-restrict-auth.c
new file mode 100644
index 00000000..39670fe3
--- /dev/null
+++ b/libtrivfs/io-restrict-auth.c
@@ -0,0 +1,144 @@
+/*
+ Copyright (C) 1993,94,95,96,2001,02 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <string.h>
+
+/* Tell if the array LIST (of size N) contains a member equal to QUERY. */
+static inline int
+listmember (const uid_t *list, uid_t query, int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ if (list[i] == query)
+ return 1;
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_restrict_auth (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t *newport,
+ mach_msg_type_name_t *newporttype,
+ uid_t *uids, size_t nuids,
+ uid_t *gids, size_t ngids)
+{
+ unsigned int i;
+ error_t err;
+ struct trivfs_protid *newcred;
+ struct idvec *uvec, *gvec;
+ struct iouser *user;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->isroot)
+ /* CRED has root access, and so may use any ids. */
+ {
+ err = iohelp_create_complex_iouser (&user, uids, nuids, gids, ngids);
+ if (err)
+ return err;
+ }
+ else
+ {
+ uvec = make_idvec ();
+ if (! uvec)
+ return ENOMEM;
+
+ gvec = make_idvec ();
+ if (! gvec)
+ {
+ idvec_free (uvec);
+ return ENOMEM;
+ }
+
+ /* Otherwise, use any of the requested ids that CRED already has. */
+ for (i = 0; i < cred->user->uids->num; i++)
+ if (listmember (uids, cred->user->uids->ids[i], nuids))
+ {
+ err = idvec_add (uvec, cred->user->uids->ids[i]);
+ if (err)
+ goto out;
+ }
+
+ for (i = 0; i < cred->user->gids->num; i++)
+ if (listmember (gids, cred->user->gids->ids[i], ngids))
+ {
+ err = idvec_add (gvec, cred->user->gids->ids[i]);
+ if (err)
+ goto out;
+ }
+
+ err = iohelp_create_iouser (&user, uvec, gvec);
+ if (err)
+ {
+ out:
+ idvec_free (uvec);
+ idvec_free (gvec);
+ return err;
+ }
+ }
+
+ err = ports_create_port (cred->po->cntl->protid_class,
+ cred->po->cntl->protid_bucket,
+ sizeof (struct trivfs_protid),
+ &newcred);
+ if (err)
+ {
+ iohelp_free_iouser (user);
+ return err;
+ }
+
+ newcred->isroot = 0;
+ newcred->po = cred->po;
+ refcount_ref (&newcred->po->refcnt);
+ if (cred->isroot && idvec_contains (user->uids, 0))
+ newcred->isroot = 1;
+ newcred->user = user;
+ newcred->hook = cred->hook;
+
+ err = io_restrict_auth (cred->realnode, &newcred->realnode,
+ user->uids->ids, user->uids->num,
+ user->gids->ids, user->gids->num);
+ if (!err && trivfs_protid_create_hook)
+ {
+ err = (*trivfs_protid_create_hook) (newcred);
+ if (err)
+ mach_port_deallocate (mach_task_self (), newcred->realnode);
+ }
+
+ if (err)
+ /* Signal that the user destroy hook shouldn't be called on NEWCRED. */
+ newcred->realnode = MACH_PORT_NULL;
+ else
+ {
+ *newport = ports_get_right (newcred);
+ *newporttype = MACH_MSG_TYPE_MAKE_SEND;
+ }
+
+ /* This will destroy NEWCRED if we got an error and didn't do the
+ ports_get_right above. */
+ ports_port_deref (newcred);
+
+ return 0;
+}
diff --git a/libtrivfs/io-revoke.c b/libtrivfs/io-revoke.c
new file mode 100644
index 00000000..b2fc00e9
--- /dev/null
+++ b/libtrivfs/io-revoke.c
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 1999,2002 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, BSG.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+
+/* Implement io_revoke as described in <hurd/io.defs>. */
+kern_return_t
+trivfs_S_io_revoke (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type)
+{
+ /* Revoke of the underlying node is actually generally right,
+ because that will cause actual calls to fail. In any case,
+ we don't have the ability to check permissions ourselves
+ correctly. */
+
+ return cred ? io_revoke (cred->realnode) : EOPNOTSUPP;
+}
diff --git a/libtrivfs/io-seek.c b/libtrivfs/io-seek.c
new file mode 100644
index 00000000..cfb7f53a
--- /dev/null
+++ b/libtrivfs/io-seek.c
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <assert.h>
+
+kern_return_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ off_t off,
+ int whence,
+ off_t *newp)
+{
+ assert (!trivfs_support_read && !trivfs_support_write);
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/io-select.c b/libtrivfs/io-select.c
new file mode 100644
index 00000000..78018bb5
--- /dev/null
+++ b/libtrivfs/io-select.c
@@ -0,0 +1,49 @@
+/* Stub io_select RPC for trivfs library.
+ Copyright (C) 1993,94,95,96,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <assert.h>
+
+kern_return_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int *seltype)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ if (*seltype & (SELECT_READ|SELECT_URG))
+ assert (!trivfs_support_read);
+ if (*seltype & (SELECT_WRITE|SELECT_URG))
+ assert (!trivfs_support_write);
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ struct timespec ts,
+ int *seltype)
+{
+ return trivfs_S_io_select (cred, reply, replytype, seltype);
+}
diff --git a/libtrivfs/io-stat.c b/libtrivfs/io-stat.c
new file mode 100644
index 00000000..70047419
--- /dev/null
+++ b/libtrivfs/io-stat.c
@@ -0,0 +1,51 @@
+/*
+ Copyright (C) 1993,94,96,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <unistd.h>
+
+kern_return_t
+trivfs_S_io_stat (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ struct stat *st)
+{
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ err = io_stat (cred->realnode, st);
+
+ if (!err)
+ {
+ if (! trivfs_fsid)
+ trivfs_fsid = getpid();
+
+ st->st_fstype = trivfs_fstype;
+ st->st_fsid = trivfs_fsid;
+ st->st_mode = (st->st_mode & ~S_IFMT & ~S_ITRANS) | S_IFCHR | S_IROOT;
+
+ trivfs_modify_stat (cred, st);
+ }
+
+ return err;
+}
diff --git a/libtrivfs/io-stubs.c b/libtrivfs/io-stubs.c
new file mode 100644
index 00000000..af3ad38a
--- /dev/null
+++ b/libtrivfs/io-stubs.c
@@ -0,0 +1,101 @@
+/*
+ Copyright (C) 1993,94,2002 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+
+kern_return_t
+trivfs_S_io_map_cntl (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t *obj,
+ mach_msg_type_name_t *objtype)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+trivfs_S_io_get_conch (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+trivfs_S_io_release_conch (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+trivfs_S_io_eofnotify (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+trivfs_S_io_prenotify (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ vm_offset_t start,
+ vm_offset_t end)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+trivfs_S_io_postnotify (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ vm_offset_t start,
+ vm_offset_t end)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+trivfs_S_io_readsleep (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+trivfs_S_io_sigio (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+trivfs_S_io_readnotify (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/io-version.c b/libtrivfs/io-version.c
new file mode 100644
index 00000000..e656d5cc
--- /dev/null
+++ b/libtrivfs/io-version.c
@@ -0,0 +1,31 @@
+/*
+ Copyright (C) 1994,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+
+kern_return_t
+trivfs_S_io_server_version (trivfs_protid_t obj,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ char *name,
+ int *maj,
+ int *min,
+ int *edit)
+{
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/io-write.c b/libtrivfs/io-write.c
new file mode 100644
index 00000000..c479c55b
--- /dev/null
+++ b/libtrivfs/io-write.c
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 1994,99,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+#include "trivfs_io_S.h"
+#include <assert.h>
+#include <fcntl.h>
+
+kern_return_t
+trivfs_S_io_write (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ char *data,
+ mach_msg_type_number_t datalen,
+ off_t off,
+ mach_msg_type_number_t *amt)
+{
+ if (!(trivfs_allow_open & O_WRITE))
+ return EBADF;
+
+ assert (!trivfs_support_write);
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/mig-decls.h b/libtrivfs/mig-decls.h
new file mode 100644
index 00000000..2baaee86
--- /dev/null
+++ b/libtrivfs/mig-decls.h
@@ -0,0 +1,105 @@
+/*
+ Copyright (C) 1994, 1995, 1996, 1997, 1999 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __TRIVFS_MIG_DECLS_H__
+#define __TRIVFS_MIG_DECLS_H__
+
+#include "priv.h"
+
+/* Vectors of dynamically allocated port classes/buckets. */
+
+/* Protid port classes. */
+extern struct port_class **trivfs_dynamic_protid_port_classes;
+extern size_t trivfs_num_dynamic_protid_port_classes;
+
+/* Control port classes. */
+extern struct port_class **trivfs_dynamic_control_port_classes;
+extern size_t trivfs_num_dynamic_control_port_classes;
+
+/* Port buckets. */
+extern struct port_bucket **trivfs_dynamic_port_buckets;
+extern size_t trivfs_num_dynamic_port_buckets;
+
+static inline struct trivfs_protid * __attribute__ ((unused))
+trivfs_begin_using_protid (mach_port_t port)
+{
+ if (trivfs_protid_nportclasses + trivfs_num_dynamic_protid_port_classes > 1)
+ {
+ struct port_info *pi = ports_lookup_port (0, port, 0);
+ int i;
+
+ if (pi)
+ {
+ for (i = 0; i < trivfs_protid_nportclasses; i++)
+ if (pi->class == trivfs_protid_portclasses[i])
+ return (struct trivfs_protid *) pi;
+ for (i = 0; i < trivfs_num_dynamic_protid_port_classes; i++)
+ if (pi->class == trivfs_dynamic_protid_port_classes[i])
+ return (struct trivfs_protid *) pi;
+ ports_port_deref (pi);
+ }
+
+ return 0;
+ }
+ else if (trivfs_protid_nportclasses == 1)
+ return ports_lookup_port (0, port, trivfs_protid_portclasses[0]);
+ else
+ return ports_lookup_port (0, port, trivfs_dynamic_protid_port_classes[0]);
+}
+
+static inline void __attribute__ ((unused))
+trivfs_end_using_protid (struct trivfs_protid *cred)
+{
+ if (cred)
+ ports_port_deref (cred);
+}
+
+static inline struct trivfs_control * __attribute__ ((unused))
+trivfs_begin_using_control (mach_port_t port)
+{
+ if (trivfs_cntl_nportclasses + trivfs_num_dynamic_control_port_classes > 1)
+ {
+ struct port_info *pi = ports_lookup_port (0, port, 0);
+ int i;
+
+ if (pi)
+ {
+ for (i = 0; i < trivfs_cntl_nportclasses; i++)
+ if (pi->class == trivfs_cntl_portclasses[i])
+ return (struct trivfs_control *) pi;
+ for (i = 0; i < trivfs_num_dynamic_control_port_classes; i++)
+ if (pi->class == trivfs_dynamic_control_port_classes[i])
+ return (struct trivfs_control *) pi;
+ ports_port_deref (pi);
+ }
+
+ return 0;
+ }
+ else if (trivfs_cntl_nportclasses == 1)
+ return ports_lookup_port (0, port, trivfs_cntl_portclasses[0]);
+ else
+ return ports_lookup_port (0, port, trivfs_dynamic_control_port_classes[0]);
+}
+
+static inline void __attribute__ ((unused))
+trivfs_end_using_control (struct trivfs_control *cred)
+{
+ if (cred)
+ ports_port_deref (cred);
+}
+
+#endif /* __TRIVFS_MIG_DECLS_H__ */
diff --git a/libtrivfs/mig-mutate.h b/libtrivfs/mig-mutate.h
new file mode 100644
index 00000000..cc15d38d
--- /dev/null
+++ b/libtrivfs/mig-mutate.h
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 1994,95,2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Only CPP macro definitions should go in this file. */
+
+#define REPLY_PORTS
+
+#define FILE_INTRAN trivfs_protid_t trivfs_begin_using_protid (file_t)
+#define FILE_DESTRUCTOR trivfs_end_using_protid (trivfs_protid_t)
+#define FILE_IMPORTS import "libtrivfs/mig-decls.h";
+
+#define IO_INTRAN trivfs_protid_t trivfs_begin_using_protid (io_t)
+#define IO_DESTRUCTOR trivfs_end_using_protid (trivfs_protid_t)
+#define IO_IMPORTS import "libtrivfs/mig-decls.h";
+
+#define FSYS_INTRAN trivfs_control_t trivfs_begin_using_control (fsys_t)
+#define FSYS_DESTRUCTOR trivfs_end_using_control (trivfs_control_t)
+#define FSYS_IMPORTS import "libtrivfs/mig-decls.h";
diff --git a/libtrivfs/open.c b/libtrivfs/open.c
new file mode 100644
index 00000000..97e70a16
--- /dev/null
+++ b/libtrivfs/open.c
@@ -0,0 +1,87 @@
+/* Make a new trivfs peropen/protid
+
+ Copyright (C) 1993, 1994, 1995, 1996, 1999 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h> /* For bcopy() */
+
+#include "priv.h"
+
+/* Return a new protid pointing to a new peropen in CRED, with REALNODE as
+ the underlying node reference, with the given identity, and open flags in
+ FLAGS. CNTL is the trivfs control object. */
+error_t
+trivfs_open (struct trivfs_control *cntl,
+ struct iouser *user,
+ unsigned flags,
+ mach_port_t realnode,
+ struct trivfs_protid **cred)
+{
+ error_t err = 0;
+ struct trivfs_peropen *po = malloc (sizeof (struct trivfs_peropen));
+
+ if (!po)
+ return ENOMEM;
+
+ ports_port_ref (cntl);
+
+ refcount_init (&po->refcnt, 1);
+ po->cntl = cntl;
+ po->openmodes = flags;
+ po->hook = 0;
+
+ if (trivfs_peropen_create_hook)
+ err = (*trivfs_peropen_create_hook) (po);
+ if (!err)
+ {
+ struct trivfs_protid *new;
+
+ err = ports_create_port (cntl->protid_class, cntl->protid_bucket,
+ sizeof (struct trivfs_protid), &new);
+ if (! err)
+ {
+ new->user = user;
+ new->isroot = idvec_contains (user->uids, 0);
+
+ new->po = po;
+ new->hook = 0;
+ new->realnode = realnode;
+
+ if (!err && trivfs_protid_create_hook)
+ err = (*trivfs_protid_create_hook) (new);
+
+ if (err)
+ {
+ /* Setting REALNODE to null signals the clean routine not to
+ call the destroy hook, which we deallocate below anyway. */
+ new->realnode = MACH_PORT_NULL;
+ ports_port_deref (new);
+ }
+ else
+ *cred = new;
+ }
+ }
+
+ if (err)
+ {
+ ports_port_deref (cntl);
+ free (po);
+ }
+
+ return err;
+}
diff --git a/libtrivfs/priv.h b/libtrivfs/priv.h
new file mode 100644
index 00000000..d92fe336
--- /dev/null
+++ b/libtrivfs/priv.h
@@ -0,0 +1,26 @@
+/*
+ Copyright (C) 1994, 1997 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef TRIVFS_PRIV_H_INCLUDED
+#define TRIVFS_PRIV_H_INCLUDED
+
+#include <mach.h>
+#include <hurd.h>
+#include <hurd/ports.h>
+#include "trivfs.h"
+
+#endif
diff --git a/libtrivfs/protid-classes.c b/libtrivfs/protid-classes.c
new file mode 100644
index 00000000..204548d8
--- /dev/null
+++ b/libtrivfs/protid-classes.c
@@ -0,0 +1,22 @@
+/* Defaults for TRIVFS_PROTID_[N]PORTCLASSES
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "trivfs.h"
+
+struct port_class *trivfs_protid_portclasses[1];
+int trivfs_protid_nportclasses;
diff --git a/libtrivfs/protid-clean.c b/libtrivfs/protid-clean.c
new file mode 100644
index 00000000..86fbc191
--- /dev/null
+++ b/libtrivfs/protid-clean.c
@@ -0,0 +1,59 @@
+/*
+ Copyright (C) 1994, 1995, 1996 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+/* Clean pointers in a struct trivfs_protid when its last reference vanishes
+ before it's freed. */
+void
+trivfs_clean_protid (void *arg)
+{
+ struct trivfs_protid *cred = arg;
+ struct trivfs_control *cntl = cred->po->cntl;
+
+ if (trivfs_protid_destroy_hook && cred->realnode != MACH_PORT_NULL)
+ /* Allow the user to clean up; If the realnode field is null, then CRED
+ wasn't initialized to the point of needing user cleanup. */
+ (*trivfs_protid_destroy_hook) (cred);
+
+ /* If we hold the only reference to the peropen, try to get rid of it. */
+ if (trivfs_peropen_destroy_hook)
+ {
+ if (refcount_deref (&cred->po->refcnt) == 0)
+ {
+ /* Reaquire a reference while we call the hook. */
+ refcount_ref (&cred->po->refcnt);
+ (*trivfs_peropen_destroy_hook) (cred->po);
+ if (refcount_deref (&cred->po->refcnt) == 0)
+ {
+ ports_port_deref (cntl);
+ free (cred->po);
+ }
+ }
+ }
+ else
+ if (refcount_deref (&cred->po->refcnt) == 0)
+ {
+ ports_port_deref (cntl);
+ free (cred->po);
+ }
+
+ iohelp_free_iouser (cred->user);
+
+ if (cred->realnode != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), cred->realnode);
+}
diff --git a/libtrivfs/protid-dup.c b/libtrivfs/protid-dup.c
new file mode 100644
index 00000000..75f3ca88
--- /dev/null
+++ b/libtrivfs/protid-dup.c
@@ -0,0 +1,69 @@
+/* Duplicate a protid
+
+ Copyright (C) 1993,94,95,96,2001 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h>
+#include "priv.h"
+
+/* Return a duplicate of CRED in DUP, sharing the same peropen and hook. A
+ non-null hook may be used to detect that this is a duplicate by
+ trivfs_protid_create_hook. */
+error_t
+trivfs_protid_dup (struct trivfs_protid *cred, struct trivfs_protid **dup)
+{
+ struct trivfs_protid *new;
+ error_t err = ports_create_port (cred->po->cntl->protid_class,
+ cred->po->cntl->protid_bucket,
+ sizeof (struct trivfs_protid),
+ &new);
+
+ if (! err)
+ {
+ new->po = cred->po;
+ refcount_ref (&new->po->refcnt);
+ new->isroot = cred->isroot;
+
+ err = iohelp_dup_iouser (&new->user, cred->user);
+ if (err)
+ {
+ ports_port_deref (new);
+ return err;
+ }
+
+ new->realnode = cred->realnode;
+ mach_port_mod_refs (mach_task_self (), new->realnode,
+ MACH_PORT_RIGHT_SEND, 1);
+
+ new->hook = cred->hook;
+
+ if (trivfs_protid_create_hook)
+ err = (*trivfs_protid_create_hook) (new);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), new->realnode);
+ /* Signal that the user destroy hook shouldn't be called on NEW. */
+ new->realnode = MACH_PORT_NULL;
+ ports_port_deref (new);
+ }
+ else
+ *dup = new;
+ }
+
+ return err;
+}
diff --git a/libtrivfs/runtime-argp.c b/libtrivfs/runtime-argp.c
new file mode 100644
index 00000000..3082b8fa
--- /dev/null
+++ b/libtrivfs/runtime-argp.c
@@ -0,0 +1,23 @@
+/* Default definition for trivfs_runtime_argp
+
+ Copyright (C) 1996 Free Software Foundation
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+struct argp *trivfs_runtime_argp = 0;
diff --git a/libtrivfs/set-options.c b/libtrivfs/set-options.c
new file mode 100644
index 00000000..af5355f1
--- /dev/null
+++ b/libtrivfs/set-options.c
@@ -0,0 +1,33 @@
+/* Set runtime options
+
+ Copyright (C) 1996 Free Software Foundation
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd/fshelp.h>
+
+#include "priv.h"
+
+/* Set runtime options for FSYS to ARGZ & ARGZ_LEN. */
+error_t
+trivfs_set_options (struct trivfs_control *fsys, char *argz, size_t argz_len)
+{
+ if (trivfs_runtime_argp)
+ return fshelp_set_options (trivfs_runtime_argp, 0, argz, argz_len, fsys);
+ else
+ return EOPNOTSUPP;
+}
diff --git a/libtrivfs/startup.c b/libtrivfs/startup.c
new file mode 100644
index 00000000..4d76d47c
--- /dev/null
+++ b/libtrivfs/startup.c
@@ -0,0 +1,88 @@
+/* Contact parent filesystem and establish ourselves as the translator.
+
+ Copyright (C) 1995, 2000 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <hurd/fsys.h>
+#include <assert.h>
+
+#include "priv.h"
+
+/* Creates a control port for this filesystem and sends it to BOOTSTRAP with
+ fsys_startup. CONTROL_TYPE is the ports library type for the control
+ port, and PROTID_TYPE is the type for ports representing opens of this
+ node. If CONTROL isn't NULL, the trivfs control port is return in it. If
+ any error occurs sending fsys_startup, it is returned, otherwise 0.
+ FLAGS specifies how to open the underlying node (O_*). */
+error_t
+trivfs_startup(mach_port_t bootstrap, int flags,
+ struct port_class *control_class,
+ struct port_bucket *control_bucket,
+ struct port_class *protid_class,
+ struct port_bucket *protid_bucket,
+ struct trivfs_control **control)
+{
+ mach_port_t underlying, right;
+ struct trivfs_control *fsys;
+ error_t err =
+ trivfs_create_control (MACH_PORT_NULL,
+ control_class, control_bucket,
+ protid_class, protid_bucket,
+ &fsys);
+
+ if (err)
+ return err;
+
+ right = ports_get_send_right (fsys);
+
+ /* Contact whoever started us. */
+ err = fsys_startup (bootstrap, flags, right, MACH_MSG_TYPE_COPY_SEND,
+ &underlying);
+ mach_port_deallocate (mach_task_self (), right);
+
+ if (! err)
+ fsys->underlying = underlying;
+
+ ports_port_deref (fsys);
+
+ /* Pass back what we got, unless the caller doesn't want it. */
+ if (!err && control)
+ *control = fsys;
+
+ /* Mark us as important. */
+ if (! err)
+ {
+ mach_port_t proc = getproc ();
+ if (proc == MACH_PORT_NULL)
+ /* /hurd/exec uses libtrivfs. We have no handle to the proc
+ server in /hurd/exec when it does its handshake with the
+ root filesystem, so fail graciously here. */
+ return 0;
+
+ err = proc_mark_important (proc);
+ /* This might fail due to permissions or because the old proc
+ server is still running, ignore any such errors. */
+ if (err == EPERM || err == EMIG_BAD_ID)
+ err = 0;
+
+ mach_port_deallocate (mach_task_self (), proc);
+ }
+
+ return err;
+}
diff --git a/libtrivfs/times.c b/libtrivfs/times.c
new file mode 100644
index 00000000..5f08cb18
--- /dev/null
+++ b/libtrivfs/times.c
@@ -0,0 +1,48 @@
+/*
+ Copyright (C) 1994, 1999, 2007 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "priv.h"
+
+error_t
+trivfs_set_atime (struct trivfs_control *cntl)
+{
+ struct stat st;
+ time_value_t atime;
+ time_value_t mtime;
+
+ io_stat (cntl->underlying, &st);
+ mtime.seconds = st.st_mtim.tv_sec;
+ mtime.microseconds = st.st_mtim.tv_nsec / 1000;
+ atime.microseconds = -1;
+ file_utimes (cntl->underlying, atime, mtime);
+ return 0;
+}
+
+error_t
+trivfs_set_mtime (struct trivfs_control *cntl)
+{
+ struct stat st;
+ time_value_t atime;
+ time_value_t mtime;
+
+ io_stat (cntl->underlying, &st);
+ atime.seconds = st.st_atim.tv_sec;
+ atime.microseconds = st.st_atim.tv_nsec / 1000;
+ mtime.microseconds = -1;
+ file_utimes (cntl->underlying, atime, mtime);
+ return 0;
+}
diff --git a/libtrivfs/trivfs.h b/libtrivfs/trivfs.h
new file mode 100644
index 00000000..89023383
--- /dev/null
+++ b/libtrivfs/trivfs.h
@@ -0,0 +1,265 @@
+/*
+ Copyright (C) 1994,95,96,97,99,2002,13 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __TRIVFS_H__
+#define __TRIVFS_H__
+
+#include <errno.h>
+#include <pthread.h> /* for mutexes &c */
+#include <sys/types.h> /* for uid_t &c */
+#include <mach/mach.h>
+#include <hurd/ports.h>
+#include <hurd/iohelp.h>
+#include <refcount.h>
+
+struct trivfs_protid
+{
+ struct port_info pi;
+ struct iouser *user;
+ int isroot;
+ /* REALNODE will be null if this protid wasn't fully created (currently
+ only in the case where trivfs_protid_create_hook returns an error). */
+ mach_port_t realnode; /* restricted permissions */
+ void *hook; /* for user use */
+ struct trivfs_peropen *po;
+};
+
+struct trivfs_peropen
+{
+ void *hook; /* for user use */
+ int openmodes;
+ refcount_t refcnt;
+ struct trivfs_control *cntl;
+};
+
+struct trivfs_control
+{
+ struct port_info pi;
+ struct port_class *protid_class;
+ struct port_bucket *protid_bucket;
+ mach_port_t filesys_id;
+ mach_port_t file_id;
+ mach_port_t underlying;
+ void *hook; /* for user use */
+};
+
+
+/* The user must define these variables. */
+extern int trivfs_fstype;
+extern int trivfs_fsid;
+
+/* Set these if trivfs should allow read, write,
+ or execute of file. */
+extern int trivfs_support_read;
+extern int trivfs_support_write;
+extern int trivfs_support_exec;
+
+/* Set this some combination of O_READ, O_WRITE, and O_EXEC;
+ trivfs will only allow opens of the specified modes.
+ (trivfs_support_* is not used to validate opens, only actual
+ operations.) */
+extern int trivfs_allow_open;
+
+/* If the user defines these, they should be vectors (and the associated
+ sizes) of port classes that will be translated into control & protid
+ pointers for passing to rpcs, in addition to those passed to or created by
+ trivfs_create_control (or trivfs_startup) will automatically be
+ recognized. */
+extern struct port_class *trivfs_protid_portclasses[];
+extern int trivfs_protid_nportclasses;
+extern struct port_class *trivfs_cntl_portclasses[];
+extern int trivfs_cntl_nportclasses;
+
+/* The user must define this function. This should modify a struct
+ stat (as returned from the underlying node) for presentation to
+ callers of io_stat. It is permissible for this function to do
+ nothing. */
+void trivfs_modify_stat (struct trivfs_protid *cred, io_statbuf_t *);
+
+/* If this variable is set, it is called to find out what access this
+ file permits to USER instead of checking the underlying node.
+ REALNODE is the underlying node, and CNTL is the trivfs control
+ object. The access permissions are returned in ALLOWED. */
+error_t (*trivfs_check_access_hook) (struct trivfs_control *cntl,
+ struct iouser *user,
+ mach_port_t realnode,
+ int *allowed);
+
+/* If this variable is set, it is called every time an open happens.
+ USER and FLAGS are from the open; CNTL identifies the
+ node being opened. This call need not check permissions on the underlying
+ node. This call can block as necessary, unless O_NONBLOCK is set
+ in FLAGS. Any desired error can be returned, which will be reflected
+ to the user and prevent the open from succeeding. */
+error_t (*trivfs_check_open_hook) (struct trivfs_control *cntl,
+ struct iouser *user, int flags);
+
+/* If this variable is set, it is called in place of `trivfs_open' (below). */
+error_t (*trivfs_open_hook) (struct trivfs_control *fsys,
+ struct iouser *user,
+ mach_port_t dotdot,
+ int flags,
+ mach_port_t realnode,
+ struct trivfs_protid **cred);
+
+/* If this variable is set, it is called every time a new protid
+ structure is created and initialized. */
+error_t (*trivfs_protid_create_hook) (struct trivfs_protid *);
+
+/* If this variable is set, it is called every time a new peropen
+ structure is created and initialized. */
+error_t (*trivfs_peropen_create_hook) (struct trivfs_peropen *);
+
+/* If this variable is set, it is called every time a protid structure
+ is about to be destroyed. */
+void (*trivfs_protid_destroy_hook) (struct trivfs_protid *);
+
+/* If this variable is set, it is called every time a peropen structure
+ is about to be destroyed. */
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *);
+
+/* If this variable is set, it is called by trivfs_S_fsys_getroot before any
+ other processing takes place; if the return value is EAGAIN, normal trivfs
+ getroot processing continues, otherwise the rpc returns with that return
+ value. */
+error_t (*trivfs_getroot_hook) (struct trivfs_control *cntl,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_port_type,
+ mach_port_t dotdot,
+ uid_t *uids, u_int nuids, uid_t *gids, u_int ngids,
+ int flags,
+ retry_type *do_retry, char *retry_name,
+ mach_port_t *node, mach_msg_type_name_t *node_type);
+
+/* Creates a control port for this filesystem and sends it to BOOTSTRAP with
+ fsys_startup. CONTROL_CLASS & CONTROL_BUCKET are passed to the ports
+ library to create the control port, and PROTID_CLASS & PROTID_BUCKET are
+ used when creating ports representing opens of this node; any of these may
+ be zero, in which case an appropriate port class/bucket is created. If
+ CONTROL isn't NULL, the trivfs control port is return in it. If any error
+ occurs sending fsys_startup, it is returned, otherwise 0. FLAGS specifies
+ how to open the underlying node (O_*). */
+error_t trivfs_startup (mach_port_t bootstrap, int flags,
+ struct port_class *control_class,
+ struct port_bucket *control_bucket,
+ struct port_class *protid_class,
+ struct port_bucket *protid_bucket,
+ struct trivfs_control **control);
+
+/* Create a new trivfs control port, with underlying node UNDERLYING, and
+ return it in CONTROL. CONTROL_CLASS & CONTROL_BUCKET are passed to
+ the ports library to create the control port, and PROTID_CLASS &
+ PROTID_BUCKET are used when creating ports representing opens of this
+ node; any of these may be zero, in which case an appropriate port
+ class/bucket is created. */
+error_t
+trivfs_create_control (mach_port_t underlying,
+ struct port_class *control_class,
+ struct port_bucket *control_bucket,
+ struct port_class *protid_class,
+ struct port_bucket *protid_bucket,
+ struct trivfs_control **control);
+
+/* Install these as libports cleanroutines for trivfs_protid_class
+ and trivfs_cntl_class respectively. */
+void trivfs_clean_protid (void *);
+void trivfs_clean_cntl (void *);
+
+/* This demultiplees messages for trivfs ports. */
+int trivfs_demuxer (mach_msg_header_t *, mach_msg_header_t *);
+
+/* Return a new protid pointing to a new peropen in CRED, with REALNODE as
+ the underlying node reference, with the given identity, and open flags in
+ FLAGS. CNTL is the trivfs control object. */
+error_t trivfs_open (struct trivfs_control *fsys,
+ struct iouser *user,
+ unsigned flags,
+ mach_port_t realnode,
+ struct trivfs_protid **cred);
+
+/* Return a duplicate of CRED in DUP, sharing the same peropen and hook. A
+ non-null hook may be used to detect that this is a duplicate by
+ trivfs_peropen_create_hook. */
+error_t trivfs_protid_dup (struct trivfs_protid *cred,
+ struct trivfs_protid **dup);
+
+/* The user must define this function. Someone wants the filesystem
+ CNTL to go away. FLAGS are from the set FSYS_GOAWAY_*. */
+error_t trivfs_goaway (struct trivfs_control *cntl, int flags);
+
+/* Call this to set atime for the node to the current time. */
+error_t trivfs_set_atime (struct trivfs_control *cntl);
+
+/* Call this to set mtime for the node to the current time. */
+error_t trivfs_set_mtime (struct trivfs_control *cntl);
+
+/* If this is defined or set to an argp structure, it will be used by the
+ default trivfs_set_options to handle runtime options parsing. Redefining
+ this is the normal way to add option parsing to a trivfs program. */
+extern struct argp *trivfs_runtime_argp;
+
+/* Set runtime options for FSYS to ARGZ & ARGZ_LEN. The default definition
+ for this routine simply uses TRIVFS_RUNTIME_ARGP (supply FSYS as the argp
+ input field). */
+error_t trivfs_set_options (struct trivfs_control *fsys,
+ char *argz, size_t argz_len);
+
+/* Append to the malloced string *ARGZ of length *ARGZ_LEN a NUL-separated
+ list of the arguments to this translator. The default definition of this
+ routine simply calls diskfs_append_std_options. */
+error_t trivfs_append_args (struct trivfs_control *fsys,
+ char **argz, size_t *argz_len);
+
+/* The user may define this function. The function must set source to
+ the source device of CRED. The function may return an EOPNOTSUPP to
+ indicate that the concept of a source device is not applicable. The
+ default function always returns EOPNOTSUPP. */
+error_t trivfs_get_source (struct trivfs_protid *cred,
+ char *source, size_t source_len);
+
+/* Add the port class *CLASS to the list of control port classes recognized
+ by trivfs; if *CLASS is 0, an attempt is made to allocate a new port
+ class, which is stored in *CLASS. */
+error_t trivfs_add_control_port_class (struct port_class **class);
+
+/* Remove the previously added dynamic control port class CLASS, freeing it
+ if it was allocated by trivfs_add_control_port_class. */
+void trivfs_remove_control_port_class (struct port_class *class);
+
+/* Add the port class *CLASS to the list of protid port classes recognized by
+ trivfs; if *CLASS is 0, an attempt is made to allocate a new port class,
+ which is stored in *CLASS. */
+error_t trivfs_add_protid_port_class (struct port_class **class);
+
+/* Remove the previously added dynamic protid port class CLASS, freeing it
+ if it was allocated by trivfs_add_protid_port_class. */
+void trivfs_remove_protid_port_class (struct port_class *class);
+
+/* Add the port bucket *BUCKET to the list of dynamically allocated port
+ buckets; if *bucket is 0, an attempt is made to allocate a new port
+ bucket, which is then stored in *bucket. */
+error_t trivfs_add_port_bucket (struct port_bucket **bucket);
+
+/* Remove the previously added dynamic port bucket BUCKET, freeing it
+ if it was allocated by trivfs_add_port_bucket. */
+void trivfs_remove_port_bucket (struct port_bucket *bucket);
+
+/* Type-aliases for mig. */
+typedef struct trivfs_protid *trivfs_protid_t;
+typedef struct trivfs_control *trivfs_control_t;
+
+#endif /* __TRIVFS_H__ */
diff --git a/login/Makefile b/login/Makefile
new file mode 100644
index 00000000..fe022d73
--- /dev/null
+++ b/login/Makefile
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 1995 Free Software Foundation
+# Written by Michael I. Bushnell.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := login
+makemode := misc
+
+SRCS = utmp.c
+
+include ../Makeconf
diff --git a/login/utmp.c b/login/utmp.c
new file mode 100644
index 00000000..c7c1ac04
--- /dev/null
+++ b/login/utmp.c
@@ -0,0 +1,424 @@
+/* Login record (utmp) server.
+
+ Copyright (C) 1995, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This program maintains an translator node that represents a particular
+ login session and answers queries about that session's status. The PID of
+ the session's root process is given, and the session will be considered
+ defunct if this process goes away. */
+
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <hurd/paths.h>
+#include <sys/socket.h>
+#include <hurd/socket.h>
+#include <hurd/fsys.h>
+#include <mach/notify.h>
+#include <stdio.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <getopt.h>
+
+/* ---------------------------------------------------------------- */
+/* Global state. */
+
+/* A string describing the user's location. */
+static char *location = NULL;
+
+/* The login collection we're for. */
+static int login_collection = 0;
+
+/* A string containing a list of devices the user is using for this login
+ session, separated by '\0'. */
+static char *devices = "";
+/* The total length of DEVICES. */
+static int devices_len = 0;
+/* The amount of space allocated for DEVICES. */
+static int devices_alloced = 0;
+
+/* ---------------------------------------------------------------- */
+
+/* If NAME is in the global DEVICES vector, return a pointer to it in
+ DEVICES, otherwise return NULL. */
+static char *
+_find_device(char *name)
+{
+ char *dev = devices;
+ int left = devices_len;
+
+ while (left > 0)
+ {
+ if (strcmp(name, dev) == 0)
+ return dev;
+ else
+ {
+ int skip = strlen (dev) + 1;
+ dev += skip;
+ left -= skip;
+ }
+ }
+
+ return 0;
+}
+
+/* Add NAME to the global DEVICES vector, unless it's already there. If a
+ memory allocation error occurs, ENOMEM is returned, otherwise 0. */
+static error_t
+add_device(char *name)
+{
+ int len = strlen(name) + 1;
+
+ if (!_find_device(name))
+ {
+ if (len + devices_len > devices_alloced)
+ {
+ int alloc = len + devices_len;
+ char *new_devs = realloc(devices, alloc);
+ if (new_devs == NULL)
+ return ENOMEM;
+
+ devices = new_devs;
+ devices_alloced = alloc;
+ }
+
+ strcpy(devices + devices_len, name);
+ devices_len += len;
+ }
+
+ return 0;
+}
+
+/* Remove NAME from the global DEVICES vector, if it's there. */
+static void
+remove_device(char *name)
+{
+ char *dev = _find_device(name);
+
+ if (dev)
+ {
+ int len = strlen(name) + 1;
+ int tail_len = devices_len - ((dev - devices) + len);
+ if (tail_len > 0)
+ bcopy(dev + len, dev, tail_len);
+ }
+}
+
+/* Given that DEV points to a device name in the global DEVICES vector,
+ return a pointer to the next one, or NULL if DEV was the last. */
+static char *
+next_device(char *dev)
+{
+ int next_pos = (dev - devices) + strlen(dev) + 1;
+ if (next_pos == devices_len)
+ return NULL;
+ else
+ return devices + next_pos;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Try and start the translator for CTL_PORT on NODE. If successful, this
+ call will not return until the translator is stopped; otherwise it returns
+ an error code describing the reason why it couldn't start. */
+static error_t
+start_translator(file_t node, fsys_t ctl_port)
+{
+ mach_port_t prev;
+ error_t err =
+ file_set_translator(node, 0, FS_TRANS_EXCL, 0, NULL, 0, ctl_port);
+
+ if (err)
+ return err;
+
+ /* Set up a watch on the process we're interested in. */
+ err =
+ mach_port_request_notification(mach_task_self(),
+ watched_process, MACH_NOTIFY_DEAD_NAME, 1,
+ node,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev);
+ if (prev)
+ mach_port_deallocate(mach_task_self(), prev);
+
+ /* Boogie. */
+ ports_manage_port_operations_onethread();
+
+ return 0;
+}
+
+/* Find an unoccupied (one with no active translator) filename starting with
+ NAME_PFX, and start the translator for CTL_PORT on it. If successful, this
+ call will not return until the translator is stopped; otherwise it returns
+ an error code describing the reason why it couldn't start. When
+ successful, this function sets UTMP_NODE_NAME to the name of the file we
+ started the translator on. */
+static error_t
+find_node_and_start_translator(char *name_pfx, fsys_t ctl_port)
+{
+ error_t err;
+ int num = 0;
+
+ do
+ {
+ file_t node;
+
+ sprintf(utmp_node_name, "%s:%d", name_pfx, num++);
+
+ node = file_name_lookup(utmp_node_name, O_CREAT | O_NOTRANS, 0666);
+ if (node == MACH_PORT_NULL)
+ err = errno;
+ else
+ err = start_translator(node, ctl_port);
+ }
+ while (err == EBUSY);
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+#define SHORT_OPTIONS "p"
+
+static struct option options[] =
+{
+ {"pid", required_argument, 0, 'p'},
+ { 0 }
+}
+
+int
+main (int argc, char **argv)
+{
+ int opt;
+ error_t err;
+ fsys_t ctl_port;
+ int pid = 0;
+ process_t proc_server = getproc();
+
+ while ((opt = getopt_long(argc, argv, SHORT_OPTIONS, options, 0)) != EOF)
+ switch (opt)
+ {
+ case 'p': /* Watch pid N */
+ pid = atoi(optarg);
+ if (pid == 0)
+ error(1, 0, "%s: Bad process id", optarg);
+ break;
+ default:
+ usage(-1);
+ }
+
+ if (pid == 0)
+ /* By default watch the top of the current login collection. */
+ {
+ err = proc_getloginid(proc_server, getpid(), &pid);
+ if (err)
+ error(2, err, "Couldn't get current login collection");
+ }
+
+ /* Setup a watch on the process we're interested in. */
+ err = proc_pid2proc(proc_server, pid, &watched_process);
+ if (err)
+ error(3, err, "Couldn't get process port for watched process");
+
+ _libports_initialize ();
+
+ ctl_port = trivfs_handle_port(MACH_PORT_NULL, PT_CTL, PT_NODE);
+
+}
+
+/* Looks for the dead-name notification message that indicates the process
+ we're watching for has died, and kills off ourselves if it sees it. */
+static int
+notice_process_died_msg(mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ if (inp->msgh_id == MACH_NOTIFY_DEAD_NAME)
+ {
+ mach_dead_name_notification_t *notify_msg =
+ (mach_dead_name_notification_t *)inp;
+
+ if (notify_msg->not_port == watched_process)
+ /* This is what we've been waiting for! Now we can die. */
+ die(0);
+ }
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Copy the char array DATA, of length DATA_LEN, into the MIG return buffer
+ pointed to by BUF, whose length is pointed to by BUF_LEN, reallocating BUF
+ with vm_allocate if it isn't long enough. BUF and BUF_LEN are updated to
+ point to the final storage & real length. If a vm allocation failure
+ occurs, EKERN_NO_SPACE is returned, otherwise 0. */
+static error_t
+return_data(char *data, int data_len, char **buf, int *buf_len)
+{
+ if (data_len > *buf_len)
+ {
+ *buf = mmap (0, data_len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*buf == (char *) -1)
+ return errno;
+ }
+
+ *buf_len = data_len;
+ bcopy(data, buf, data_len);
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+/* login RPC handlers. */
+
+/* Send an immediate message to the user. */
+error_t
+S_login_message_user(file_t utmp, char *msg, int msg_len)
+{
+ error_t err;
+#if 0
+ struct trivfs_protid *cred = ports_check_port_type (sockfile, PT_NODE);
+ int perms;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ err = file_check_access (cred->realnode, &perms);
+ if (!err && !(perms & O_READ))
+ err = EACCES;
+#endif
+
+ return 0;
+}
+
+/* Return a human-readable string describing the user's physical location. */
+error_t
+S_login_get_location(file_t utmp, char **buf, unsigned *len)
+{
+ return return_data(location, strlen(location) + 1, buf, len);
+}
+
+/* Return how much time has passed since the user last used an input device. */
+error_t
+S_login_get_idle_time(file_t utmp, time_value_t *tv)
+{
+ tv->seconds = 0;
+ tv->microseconds = 0;
+
+ if (devices_len > 0)
+ {
+ char *dev;
+ for (dev = devices; dev != NULL; dev = next_device(dev))
+ {
+ struct stat stat;
+ if (stat(dev, &state) == 0
+ && (stat.st_atim.tv_sec < tv->seconds
+ || (stat.st_atim.tv_sec == tv->seconds
+ && stat.st_atim.tv_nsec / 1000 < tv->microseconds)))
+ {
+ tv->seconds = stat.st_atim.tv_sc;
+ tv->microseconds = stat.st_atim.tv_nsec / 1000;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Return a list of file names for input devices being used, separated by null
+ characters. This call is optional; clients should not depend on it. */
+error_t
+S_login_get_input_devices(file_t utmp, char **buf, unsigned *len)
+{
+ return return_data(devices, devices_len, buf, len);
+}
+
+/* Return the process collection ID for the user's login collection. */
+error_t
+S_login_get_login_collection(file_t utmp, int *pid)
+{
+ *pid = login_collection;
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+/* Trivfs hooks */
+
+/* Port types */
+#define PT_CTL 0
+#define PT_NODE 1
+#define PT_PROC 2 /* Our process handle. */
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0; /* ??? */
+
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = 0;
+
+int trivfs_protid_porttypes[] = {PT_NODE};
+int trivfs_cntl_porttypes[] = {PT_CTL};
+int trivfs_protid_nporttypes = 1;
+int trivfs_cntl_nporttypes = 1;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ die(0);
+}
+
+/* ---------------------------------------------------------------- */
+/* Ports hooks */
+
+void (*ports_cleanroutines[])(void *) =
+{
+ [PT_CTL] = trivfs_clean_cntl,
+ [PT_NODE] = trivfs_clean_protid,
+};
+
+int
+ports_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ return notice_process_died_msg(inp, outp)
+ || login_server(inp, outp) || trivfs_demuxer(inp, outp);
+}
+
+void
+ports_notice_idle (int nhard, int nsoft)
+{
+ if (!nhard && !nsoft)
+ die(0);
+}
+
+void
+ports_no_live_ports ()
+{
+ die(0);
+}
+
+void
+ports_no_hard_ports ()
+{
+ die(0);
+}
diff --git a/mach-defpager/Makefile b/mach-defpager/Makefile
new file mode 100644
index 00000000..09debeda
--- /dev/null
+++ b/mach-defpager/Makefile
@@ -0,0 +1,38 @@
+# Makefile for mach-defpager subdirectory of hurd sources
+#
+# Copyright (C) 1999, 2000, 2002, 2007, 2010, 2012 Free Software Foundation,
+# Inc.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := mach-defpager
+makemode:= server
+target := mach-defpager
+
+SRCS := default_pager.c kalloc.c wiring.c main.c setup.c
+OBJS := $(SRCS:.c=.o) \
+ $(addsuffix Server.o,\
+ memory_object default_pager memory_object_default exc) \
+ default_pager_replyUser.o
+
+HURDLIBS:= ihash
+OTHERLIBS:= -lpthread
+LDFLAGS += -static
+
+include ../Makeconf
+
+MIGSFLAGS = -DSEQNOS -imacros $(srcdir)/mig-mutate.h
diff --git a/mach-defpager/default_pager.c b/mach-defpager/default_pager.c
new file mode 100644
index 00000000..380c7246
--- /dev/null
+++ b/mach-defpager/default_pager.c
@@ -0,0 +1,3774 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Default pager. Pages to paging partition.
+ *
+ * MUST BE ABLE TO ALLOCATE WIRED-DOWN MEMORY!!!
+ */
+
+#include <mach.h>
+#include <mach/message.h>
+#include <mach/notify.h>
+#include <mach/mig_errors.h>
+#include <mach/thread_switch.h>
+#include <mach/task_info.h>
+#include <mach/default_pager_types.h>
+
+#include <pthread.h>
+#include <stddef.h>
+
+#include <device/device_types.h>
+#include <device/device.h>
+
+#include <hurd/ihash.h>
+
+#include "queue.h"
+#include "wiring.h"
+#include "kalloc.h"
+#include "default_pager.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <file_io.h>
+
+#include "memory_object_S.h"
+#include "memory_object_default_S.h"
+#include "default_pager_S.h"
+#include "exc_S.h"
+
+#include "priv.h"
+
+#define debug 0
+
+static char my_name[] = "(default pager):";
+
+static void __attribute__ ((format (printf, 1, 2), unused))
+synchronized_printf (const char *fmt, ...)
+{
+ static pthread_mutex_t printf_lock = PTHREAD_MUTEX_INITIALIZER;
+ va_list ap;
+
+ va_start (ap, fmt);
+ pthread_mutex_lock (&printf_lock);
+
+ vprintf (fmt, ap);
+ fflush (stdout);
+
+ pthread_mutex_unlock (&printf_lock);
+ va_end (ap);
+}
+
+#if 0
+#define dprintf(f, x...) synchronized_printf (f, ##x)
+#else
+#define dprintf(f, x...) (void) 0
+#endif
+
+#if 0
+#define ddprintf(f, x...) synchronized_printf (f, ##x)
+#else
+#define ddprintf(f, x...) (void) 0
+#endif
+
+/*
+ * parallel vs serial switch
+ */
+#define PARALLEL 1
+
+#if 0
+#define CHECKSUM 1
+#endif
+
+#define USE_PRECIOUS 1
+
+#define ptoa(p) ((p)*vm_page_size)
+#define atop(a) ((a)/vm_page_size)
+
+partition_t partition_of(x)
+ int x;
+{
+ if (x >= all_partitions.n_partitions || x < 0)
+ panic("partition_of x%x", x);
+ return all_partitions.partition_list[x];
+}
+
+void set_partition_of(x, p)
+ int x;
+ partition_t p;
+{
+ if (x >= all_partitions.n_partitions || x < 0)
+ panic("set_partition_of x%x", x);
+ all_partitions.partition_list[x] = p;
+}
+
+/*
+ * Simple mapping from (file)NAME to id
+ * Saves space, filenames can be long.
+ */
+unsigned int
+part_id(const char *name)
+{
+ unsigned int id, xorid;
+ size_t len;
+
+ len = strlen(name);
+ id = xorid = 0;
+ while (len--) {
+ xorid ^= *name;
+ id += *name++;
+ }
+ return (id << 8) | xorid;
+}
+
+void
+partition_init()
+{
+ pthread_mutex_init(&all_partitions.lock, NULL);
+ all_partitions.n_partitions = 0;
+}
+
+static partition_t
+new_partition (const char *name, struct file_direct *fdp,
+ int check_linux_signature)
+{
+ partition_t part;
+ vm_size_t size, bmsize;
+ vm_offset_t raddr;
+ mach_msg_type_number_t rsize;
+ int rc;
+ unsigned int id = part_id(name);
+
+ pthread_mutex_lock(&all_partitions.lock);
+ {
+ unsigned int i;
+ for (i = 0; i < all_partitions.n_partitions; i++)
+ {
+ part = partition_of(i);
+ if (part && part->id == id)
+ {
+ pthread_mutex_unlock(&all_partitions.lock);
+ return 0;
+ }
+ }
+ }
+ pthread_mutex_unlock(&all_partitions.lock);
+
+ size = atop(fdp->fd_size * fdp->fd_bsize);
+ bmsize = howmany(size, NB_BM) * sizeof(bm_entry_t);
+
+ part = (partition_t) kalloc(sizeof(struct part));
+ pthread_mutex_init(&part->p_lock, NULL);
+ part->total_size = size;
+ part->free = size;
+ part->id = id;
+ part->bitmap = (bm_entry_t *)kalloc(bmsize);
+ part->going_away= FALSE;
+ part->file = fdp;
+
+ bzero((char *)part->bitmap, bmsize);
+
+ if (check_linux_signature < 0)
+ {
+ if (check_linux_signature != -3)
+ printf("(default pager): "
+ "Paging to raw partition %s (%uk paging space)\n",
+ name, part->total_size * (vm_page_size / 1024));
+ return part;
+ }
+
+#define LINUX_PAGE_SIZE 4096 /* size of pages in Linux swap partitions */
+ rc = page_read_file_direct(part->file,
+ 0, LINUX_PAGE_SIZE,
+ &raddr,
+ &rsize);
+ if (rc)
+ panic("(default pager): cannot read first page of %s! rc=%#x\n",
+ name, rc);
+ while (rsize < LINUX_PAGE_SIZE)
+ {
+ /* Filesystem block size is smaller than page size,
+ so we must do several reads to get the whole page. */
+ vm_address_t baddr;
+ vm_size_t bsize;
+ rc = page_read_file_direct(part->file,
+ rsize, LINUX_PAGE_SIZE-rsize,
+ &baddr,
+ &bsize);
+ if (rc)
+ panic("(default pager): "
+ "cannot read first page of %s! rc=%#x at %#x\n",
+ name, rc, rsize);
+
+ memcpy ((char *) raddr + rsize, (void *) baddr, bsize);
+ rsize += bsize;
+ vm_deallocate (mach_task_self (), baddr, bsize);
+ }
+
+ if (!memcmp("SWAP-SPACE", (char *) raddr + LINUX_PAGE_SIZE-10, 10))
+ {
+ /* The partition's first page has a Linux swap signature.
+ This means the beginning of the page contains a bitmap
+ of good pages, and all others are bad. */
+ unsigned int i, j, bad, max;
+ int waste;
+
+ printf("(default pager): Found Linux 2.0 swap signature in %s\n",
+ name);
+
+ /* The first page, and the pages corresponding to the bits
+ occupied by the signature in the final 10 bytes of the page,
+ are always unavailable ("bad"). */
+ *(u_int32_t *)raddr &= ~(u_int32_t) 1;
+ memset((char *) raddr + LINUX_PAGE_SIZE-10, 0, 10);
+
+ max = LINUX_PAGE_SIZE / sizeof(u_int32_t);
+ if (max > (part->total_size + 31) / 32)
+ max = (part->total_size + 31) / 32;
+
+ bad = 0;
+ for (i = 0; i < max; ++i)
+ {
+ u_int32_t bm = ((u_int32_t *) raddr)[i];
+ if (bm == ~(u_int32_t) 0)
+ continue;
+ /* There are some zero bits in this word. */
+ for (j = 0; j < 32; ++j)
+ if ((bm & (1 << j)) == 0)
+ {
+ unsigned int p = i*32 + j;
+ if (p >= part->total_size)
+ break;
+ ++bad;
+ part->bitmap[p / NB_BM] |= 1 << (p % NB_BM);
+ }
+ }
+ part->free -= bad;
+
+ --bad; /* Don't complain about first page. */
+ waste = part->total_size - (8 * (LINUX_PAGE_SIZE-10));
+ if (waste > 0)
+ {
+ /* The wasted pages were already marked "bad". */
+ bad -= waste;
+ if (bad > 0)
+ printf("\
+(default pager): Paging to %s, %dk swap-space (%dk bad, %dk wasted at end)\n",
+ name,
+ part->free * (LINUX_PAGE_SIZE / 1024),
+ bad * (LINUX_PAGE_SIZE / 1024),
+ waste * (LINUX_PAGE_SIZE / 1024));
+ else
+ printf("\
+(default pager): Paging to %s, %dk swap-space (%dk wasted at end)\n",
+ name,
+ part->free * (LINUX_PAGE_SIZE / 1024),
+ waste * (LINUX_PAGE_SIZE / 1024));
+ }
+ else if (bad > 0)
+ printf("\
+(default pager): Paging to %s, %dk swap-space (excludes %dk marked bad)\n",
+ name,
+ part->free * (LINUX_PAGE_SIZE / 1024),
+ bad * (LINUX_PAGE_SIZE / 1024));
+ else
+ printf("\
+(default pager): Paging to %s, %dk swap-space\n",
+ name,
+ part->free * (LINUX_PAGE_SIZE / 1024));
+ }
+ else if (!memcmp("SWAPSPACE2",
+ (char *) raddr + LINUX_PAGE_SIZE-10, 10))
+ {
+ struct
+ {
+ u_int8_t bootbits[1024];
+ u_int32_t version;
+ u_int32_t last_page;
+ u_int32_t nr_badpages;
+ u_int32_t padding[125];
+ u_int32_t badpages[1];
+ } *hdr = (void *) raddr;
+
+ printf("\
+(default pager): Found Linux 2.2 swap signature (v%u) in %s...",
+ hdr->version, name);
+
+ part->bitmap[0] |= 1; /* first page unusable */
+ part->free--;
+
+ switch (hdr->version)
+ {
+ default:
+ if (check_linux_signature)
+ {
+ printf ("version %u unknown! SKIPPING %s!\n",
+ hdr->version,
+ name);
+ vm_deallocate(mach_task_self(), raddr, rsize);
+ kfree(part->bitmap, bmsize);
+ kfree(part, sizeof *part);
+ return 0;
+ }
+ else
+ printf ("version %u unknown! IGNORING SIGNATURE PAGE!"
+ " %dk swap-space\n",
+ hdr->version,
+ part->free * (LINUX_PAGE_SIZE / 1024));
+ break;
+
+ case 1:
+ {
+ unsigned int waste, i;
+ if (hdr->last_page > part->total_size)
+ {
+ printf ("signature says %uk, partition has only %uk! ",
+ hdr->last_page * (LINUX_PAGE_SIZE / 1024),
+ part->total_size * (LINUX_PAGE_SIZE / 1024));
+ waste = 0;
+ }
+ else
+ {
+ waste = part->total_size - hdr->last_page;
+ part->total_size = hdr->last_page;
+ part->free = part->total_size - 1;
+ }
+ for (i = 0; i < hdr->nr_badpages; ++i)
+ {
+ const u_int32_t bad = hdr->badpages[i];
+ part->bitmap[bad / NB_BM] |= 1 << (bad % NB_BM);
+ part->free--;
+ }
+ printf ("%uk swap-space",
+ part->free * (LINUX_PAGE_SIZE / 1024));
+ if (hdr->nr_badpages != 0)
+ printf (" (excludes %uk marked bad)",
+ hdr->nr_badpages * (LINUX_PAGE_SIZE / 1024));
+ if (waste != 0)
+ printf (" (excludes %uk at end of partition)",
+ waste * (LINUX_PAGE_SIZE / 1024));
+ printf ("\n");
+ }
+ }
+ }
+ else if (check_linux_signature)
+ {
+ printf ("(default pager): "
+ "Cannot find Linux swap signature page! "
+ "SKIPPING %s (%uk partition)!",
+ name, part->total_size * (vm_page_size / 1024));
+ kfree(part->bitmap, bmsize);
+ kfree(part, sizeof *part);
+ part = 0;
+ }
+ else
+ printf("(default pager): "
+ "Paging to raw partition %s (%uk paging space)\n",
+ name, part->total_size * (vm_page_size / 1024));
+
+ vm_deallocate(mach_task_self(), raddr, rsize);
+
+ return part;
+}
+
+/*
+ * Create a partition descriptor,
+ * add it to the list of all such.
+ * size is in BYTES.
+ */
+void
+create_paging_partition(const char *name,
+ struct file_direct *fdp, int isa_file,
+ int linux_signature)
+{
+ partition_t part;
+
+ part = new_partition (name, fdp, linux_signature);
+ if (!part)
+ return;
+
+ pthread_mutex_lock(&all_partitions.lock);
+ {
+ int i;
+
+ for (i = 0; i < all_partitions.n_partitions; i++)
+ if (partition_of(i) == 0) break;
+
+ if (i == all_partitions.n_partitions) {
+ partition_t *new_list, *old_list;
+ int n;
+
+ n = i ? (i<<1) : 2;
+ new_list = (partition_t *)
+ kalloc( n * sizeof(partition_t) );
+ if (new_list == 0) no_paging_space(TRUE);
+ bzero(new_list, n*sizeof(partition_t));
+ if (i) {
+ old_list = all_partitions.partition_list;
+ bcopy(old_list, new_list, i*sizeof(partition_t));
+ }
+ all_partitions.partition_list = new_list;
+ all_partitions.n_partitions = n;
+ if (i) kfree(old_list, i*sizeof(partition_t));
+ }
+ set_partition_of(i, part);
+ }
+ pthread_mutex_unlock(&all_partitions.lock);
+
+#if 0
+ dprintf("%s Added paging %s %s\n", my_name,
+ (isa_file) ? "file" : "device", name);
+#endif
+ overcommitted(TRUE, part->free);
+}
+
+/*
+ * Choose the most appropriate default partition
+ * for an object of SIZE bytes.
+ * Return the partition locked, unless
+ * the object has no CUR_PARTition.
+ */
+p_index_t
+choose_partition(size, cur_part)
+ unsigned int size;
+ p_index_t cur_part;
+{
+ partition_t part;
+ boolean_t found = FALSE;
+ int i;
+
+ pthread_mutex_lock(&all_partitions.lock);
+ for (i = 0; i < all_partitions.n_partitions; i++) {
+
+ /* the undesirable one ? */
+ if (i == cur_part)
+ continue;
+
+ddprintf ("choose_partition(%x,%d,%d)\n",size,cur_part,i);
+ /* one that was removed ? */
+ if ((part = partition_of(i)) == 0)
+ continue;
+
+ /* one that is being removed ? */
+ if (part->going_away)
+ continue;
+
+ /* is it big enough ? */
+ pthread_mutex_lock(&part->p_lock);
+ if (ptoa(part->free) >= size) {
+ if (cur_part != P_INDEX_INVALID) {
+ pthread_mutex_unlock(&all_partitions.lock);
+ return (p_index_t)i;
+ } else
+ found = TRUE;
+ }
+ pthread_mutex_unlock(&part->p_lock);
+
+ if (found) break;
+ }
+ pthread_mutex_unlock(&all_partitions.lock);
+ return (found) ? (p_index_t)i : P_INDEX_INVALID;
+}
+
+/*
+ * Allocate a page in a paging partition
+ * The partition is returned unlocked.
+ */
+vm_offset_t
+pager_alloc_page(pindex, lock_it)
+ p_index_t pindex;
+ boolean_t lock_it;
+{
+ int bm_e;
+ int bit;
+ int limit;
+ bm_entry_t *bm;
+ partition_t part;
+ static char here[] = "%spager_alloc_page";
+
+ if (no_partition(pindex))
+ return (NO_BLOCK);
+ddprintf ("pager_alloc_page(%d,%d)\n",pindex,lock_it);
+ part = partition_of(pindex);
+
+ /* unlikely, but possible deadlock against destroy_partition */
+ if (!part || part->going_away)
+ return (NO_BLOCK);
+
+ if (lock_it)
+ pthread_mutex_lock(&part->p_lock);
+
+ if (part->free == 0) {
+ /* out of paging space */
+ pthread_mutex_unlock(&part->p_lock);
+ return (NO_BLOCK);
+ }
+
+ limit = howmany(part->total_size, NB_BM);
+ bm = part->bitmap;
+ for (bm_e = 0; bm_e < limit; bm_e++, bm++)
+ if (*bm != BM_MASK)
+ break;
+
+ if (bm_e == limit)
+ panic(here,my_name);
+
+ /*
+ * Find and set the proper bit
+ */
+ {
+ bm_entry_t b = *bm;
+
+ for (bit = 0; bit < NB_BM; bit++)
+ if ((b & (1<<bit)) == 0)
+ break;
+ if (bit == NB_BM)
+ panic(here,my_name);
+
+ *bm = b | (1<<bit);
+ part->free--;
+
+ }
+
+ pthread_mutex_unlock(&part->p_lock);
+
+ return (bm_e*NB_BM+bit);
+}
+
+/*
+ * Deallocate a page in a paging partition
+ */
+void
+pager_dealloc_page(pindex, page, lock_it)
+ p_index_t pindex;
+ vm_offset_t page;
+ boolean_t lock_it;
+{
+ partition_t part;
+ int bit, bm_e;
+
+ /* be paranoid */
+ if (no_partition(pindex))
+ panic("%sdealloc_page",my_name);
+ddprintf ("pager_dealloc_page(%d,%x,%d)\n",pindex,page,lock_it);
+ part = partition_of(pindex);
+
+ if (page >= part->total_size)
+ panic("%sdealloc_page",my_name);
+
+ bm_e = page / NB_BM;
+ bit = page % NB_BM;
+
+ if (lock_it)
+ pthread_mutex_lock(&part->p_lock);
+
+ part->bitmap[bm_e] &= ~(1<<bit);
+ part->free++;
+
+ if (lock_it)
+ pthread_mutex_unlock(&part->p_lock);
+}
+
+/*
+ * Object sizes are rounded up to the next power of 2,
+ * unless they are bigger than a given maximum size.
+ */
+vm_size_t max_doubled_size = 4 * 1024 * 1024; /* 4 meg */
+
+/*
+ * Return first level map for pager.
+ * If there is no such map, than allocate it.
+ */
+dp_map_t pager_get_direct_map(pager)
+ dpager_t pager;
+{
+ dp_map_t mapptr, emapptr;
+ vm_size_t size = pager->size;
+
+ if (pager->map)
+ return pager->map;
+ /*
+ * Allocate and initialize the block map
+ */
+ {
+ vm_size_t alloc_size;
+ dp_map_t init_value;
+
+ if (INDIRECT_PAGEMAP(size)) {
+ alloc_size = INDIRECT_PAGEMAP_SIZE(size);
+ init_value = (dp_map_t)0;
+ } else {
+ alloc_size = PAGEMAP_SIZE(size);
+ init_value = (dp_map_t)NO_BLOCK;
+ }
+
+ mapptr = (dp_map_t) kalloc(alloc_size);
+ for (emapptr = &mapptr[(alloc_size-1) / sizeof(vm_offset_t)];
+ emapptr >= mapptr;
+ emapptr--)
+ emapptr->indirect = init_value;
+ }
+ pager->map = mapptr;
+ return mapptr;
+}
+
+/*
+ * Attach a new paging object to a paging partition
+ */
+void
+pager_alloc(pager, part, size)
+ dpager_t pager;
+ p_index_t part;
+ vm_size_t size; /* in BYTES */
+{
+ int i;
+#ifdef CHECKSUM
+ dp_map_t mapptr, emapptr;
+#endif
+
+ pthread_mutex_init(&pager->lock, NULL);
+#if DEBUG_READER_CONFLICTS
+ pager->readers = 0;
+ pager->writer = FALSE;
+#endif
+ pager->cur_partition = part;
+
+ /*
+ * Convert byte size to number of pages, then increase to the nearest
+ * power of 2.
+ */
+ size = atop(size);
+ if (size <= atop(max_doubled_size)) {
+ i = 1;
+ while (i < size)
+ i <<= 1;
+ size = i;
+ } else
+ size = ROUNDUP_TO_PAGEMAP(size);
+
+ pager->map = NULL;
+ pager->size = size;
+ pager->limit = (vm_size_t)-1;
+
+#ifdef CHECKSUM
+ if (INDIRECT_PAGEMAP(size)) {
+ mapptr = (vm_offset_t *)
+ kalloc(INDIRECT_PAGEMAP_SIZE(size));
+ for (i = INDIRECT_PAGEMAP_ENTRIES(size); --i >= 0;)
+ mapptr[i] = 0;
+ } else {
+ mapptr = (vm_offset_t *) kalloc(PAGEMAP_SIZE(size));
+ for (i = 0; i < size; i++)
+ mapptr[i] = NO_CHECKSUM;
+ }
+ pager->checksum = mapptr;
+#endif /* CHECKSUM */
+}
+
+/*
+ * Return size (in bytes) of space actually allocated to this pager.
+ * The pager is read-locked.
+ */
+
+vm_size_t
+pager_allocated(pager)
+ dpager_t pager;
+{
+ vm_size_t size;
+ dp_map_t map, emap;
+ vm_size_t asize;
+
+ size = pager->size; /* in pages */
+ asize = 0; /* allocated, in pages */
+ map = pager_get_direct_map(pager);
+
+ if (INDIRECT_PAGEMAP(size)) {
+ for (emap = &map[INDIRECT_PAGEMAP_ENTRIES(size)];
+ map < emap; map++) {
+
+ dp_map_t map2, emap2;
+
+ if ((map2 = map->indirect) == 0)
+ continue;
+
+ for (emap2 = &map2[PAGEMAP_ENTRIES];
+ map2 < emap2; map2++)
+ if ( ! no_block(*map2) )
+ asize++;
+
+ }
+ } else {
+ for (emap = &map[size]; map < emap; map++)
+ if ( ! no_block(*map) )
+ asize++;
+ }
+
+ return ptoa(asize);
+}
+
+/*
+ * Find offsets (in the object) of pages actually allocated to this pager.
+ * Returns the number of allocated pages, whether or not they all fit.
+ * The pager is read-locked.
+ */
+
+unsigned int
+pager_pages(pager, pages, numpages)
+ dpager_t pager;
+ default_pager_page_t *pages;
+ unsigned int numpages;
+{
+ vm_size_t size;
+ dp_map_t map, emap;
+ unsigned int actual;
+ vm_offset_t offset;
+
+ size = pager->size; /* in pages */
+ map = pager_get_direct_map(pager);
+ actual = 0;
+ offset = 0;
+
+ if (INDIRECT_PAGEMAP(size)) {
+ for (emap = &map[INDIRECT_PAGEMAP_ENTRIES(size)];
+ map < emap; map++) {
+
+ dp_map_t map2, emap2;
+
+ if ((map2 = map->indirect) == 0) {
+ offset += vm_page_size * PAGEMAP_ENTRIES;
+ continue;
+ }
+ for (emap2 = &map2[PAGEMAP_ENTRIES];
+ map2 < emap2; map2++)
+ if ( ! no_block(*map2) ) {
+ if (actual++ < numpages)
+ pages++->dpp_offset = offset;
+ }
+ offset += vm_page_size;
+ }
+ } else {
+ for (emap = &map[size]; map < emap; map++) {
+ if ( ! no_block(*map) ) {
+ if (actual++ < numpages)
+ pages++->dpp_offset = offset;
+ }
+ offset += vm_page_size;
+ }
+ }
+ return actual;
+}
+
+/*
+ * Extend the map for a paging object.
+ *
+ * XXX This implementation can allocate an arbitrary large amount
+ * of wired memory when extending a big block map. Because vm-privileged
+ * threads call pager_extend, this can crash the system by exhausting
+ * system memory.
+ */
+void
+pager_extend(pager, new_size)
+ dpager_t pager;
+ vm_size_t new_size; /* in pages */
+{
+ dp_map_t new_mapptr;
+ dp_map_t old_mapptr;
+ int i;
+ vm_size_t old_size;
+
+ pthread_mutex_lock(&pager->lock); /* XXX lock_write */
+#if DEBUG_READER_CONFLICTS
+ pager->writer = TRUE;
+#endif
+ /*
+ * Double current size until we cover new size.
+ * If object is 'too big' just use new size.
+ */
+ old_size = pager->size;
+
+ if (new_size <= atop(max_doubled_size)) {
+ /* New size cannot be less than 1 */
+ i = old_size ? old_size : 1;
+ while (i < new_size)
+ i <<= 1;
+ new_size = i;
+ } else
+ new_size = ROUNDUP_TO_PAGEMAP(new_size);
+
+ if (INDIRECT_PAGEMAP(old_size)) {
+ /*
+ * Pager already uses two levels. Allocate
+ * a larger indirect block.
+ */
+ new_mapptr = (dp_map_t)
+ kalloc(INDIRECT_PAGEMAP_SIZE(new_size));
+ old_mapptr = pager_get_direct_map(pager);
+ for (i = 0; i < INDIRECT_PAGEMAP_ENTRIES(old_size); i++)
+ new_mapptr[i] = old_mapptr[i];
+ for (; i < INDIRECT_PAGEMAP_ENTRIES(new_size); i++)
+ new_mapptr[i].indirect = (dp_map_t)0;
+ kfree((char *)old_mapptr, INDIRECT_PAGEMAP_SIZE(old_size));
+ pager->map = new_mapptr;
+ pager->size = new_size;
+#ifdef CHECKSUM
+ new_mapptr = (vm_offset_t *)
+ kalloc(INDIRECT_PAGEMAP_SIZE(new_size));
+ old_mapptr = pager->checksum;
+ for (i = 0; i < INDIRECT_PAGEMAP_ENTRIES(old_size); i++)
+ new_mapptr[i] = old_mapptr[i];
+ for (; i < INDIRECT_PAGEMAP_ENTRIES(new_size); i++)
+ new_mapptr[i] = 0;
+ kfree((char *)old_mapptr, INDIRECT_PAGEMAP_SIZE(old_size));
+ pager->checksum = new_mapptr;
+#endif /* CHECKSUM */
+#if DEBUG_READER_CONFLICTS
+ pager->writer = FALSE;
+#endif
+ pthread_mutex_unlock(&pager->lock);
+#if 0
+ ddprintf ("pager_extend 1 mapptr %x [3b] = %x\n", new_mapptr,
+ new_mapptr[0x3b]);
+ if (new_mapptr[0x3b].indirect > 0x10000
+ && new_mapptr[0x3b].indirect != NO_BLOCK)
+ panic ("debug panic");
+#endif
+ return;
+ }
+
+ if (INDIRECT_PAGEMAP(new_size)) {
+ /*
+ * Changing from direct map to indirect map.
+ * Allocate both indirect and direct map blocks,
+ * since second-level (direct) block must be
+ * full size (PAGEMAP_SIZE(PAGEMAP_ENTRIES)).
+ */
+
+ /*
+ * Allocate new second-level map first.
+ */
+ new_mapptr = (dp_map_t) kalloc(PAGEMAP_SIZE(PAGEMAP_ENTRIES));
+ old_mapptr = pager_get_direct_map(pager);
+ for (i = 0; i < old_size; i++)
+ new_mapptr[i] = old_mapptr[i];
+ for (; i < PAGEMAP_ENTRIES; i++)
+ invalidate_block(new_mapptr[i]);
+ kfree((char *)old_mapptr, PAGEMAP_SIZE(old_size));
+ old_mapptr = new_mapptr;
+
+#if 0
+ ddprintf ("pager_extend 2 mapptr %x [3b] = %x\n", new_mapptr,
+ new_mapptr[0x3b]);
+ if (new_mapptr[0x3b].indirect > 0x10000
+ && new_mapptr[0x3b].indirect != NO_BLOCK)
+ panic ("debug panic");
+#endif
+
+ /*
+ * Now allocate indirect map.
+ */
+ new_mapptr = (dp_map_t)
+ kalloc(INDIRECT_PAGEMAP_SIZE(new_size));
+ new_mapptr[0].indirect = old_mapptr;
+ for (i = 1; i < INDIRECT_PAGEMAP_ENTRIES(new_size); i++)
+ new_mapptr[i].indirect = 0;
+ pager->map = new_mapptr;
+ pager->size = new_size;
+#ifdef CHECKSUM
+ /*
+ * Allocate new second-level map first.
+ */
+ new_mapptr = (vm_offset_t *)kalloc(PAGEMAP_SIZE(PAGEMAP_ENTRIES));
+ old_mapptr = pager->checksum;
+ for (i = 0; i < old_size; i++)
+ new_mapptr[i] = old_mapptr[i];
+ for (; i < PAGEMAP_ENTRIES; i++)
+ new_mapptr[i] = NO_CHECKSUM;
+ kfree((char *)old_mapptr, PAGEMAP_SIZE(old_size));
+ old_mapptr = new_mapptr;
+
+ /*
+ * Now allocate indirect map.
+ */
+ new_mapptr = (vm_offset_t *)
+ kalloc(INDIRECT_PAGEMAP_SIZE(new_size));
+ new_mapptr[0] = (vm_offset_t) old_mapptr;
+ for (i = 1; i < INDIRECT_PAGEMAP_ENTRIES(new_size); i++)
+ new_mapptr[i] = 0;
+ pager->checksum = new_mapptr;
+#endif /* CHECKSUM */
+#if DEBUG_READER_CONFLICTS
+ pager->writer = FALSE;
+#endif
+ pthread_mutex_unlock(&pager->lock);
+ return;
+ }
+ /*
+ * Enlarging a direct block.
+ */
+ new_mapptr = (dp_map_t) kalloc(PAGEMAP_SIZE(new_size));
+ old_mapptr = pager_get_direct_map(pager);
+ for (i = 0; i < old_size; i++)
+ new_mapptr[i] = old_mapptr[i];
+ for (; i < new_size; i++)
+ invalidate_block(new_mapptr[i]);
+ kfree((char *)old_mapptr, PAGEMAP_SIZE(old_size));
+ pager->map = new_mapptr;
+ pager->size = new_size;
+#ifdef CHECKSUM
+ new_mapptr = (vm_offset_t *)
+ kalloc(PAGEMAP_SIZE(new_size));
+ old_mapptr = pager->checksum;
+ for (i = 0; i < old_size; i++)
+ new_mapptr[i] = old_mapptr[i];
+ for (; i < new_size; i++)
+ new_mapptr[i] = NO_CHECKSUM;
+ kfree((char *)old_mapptr, PAGEMAP_SIZE(old_size));
+ pager->checksum = new_mapptr;
+#endif /* CHECKSUM */
+#if DEBUG_READER_CONFLICTS
+ pager->writer = FALSE;
+#endif
+ pthread_mutex_unlock(&pager->lock);
+}
+
+/* This deallocates the pages necessary to truncate a direct map
+ previously of size NEW_SIZE to the smaller size OLD_SIZE. */
+static void
+dealloc_direct (dp_map_t mapptr,
+ vm_size_t old_size, vm_size_t new_size)
+{
+ vm_size_t i;
+
+ if (!mapptr)
+ return;
+
+ for (i = new_size; i < old_size; ++i)
+ {
+ const union dp_map entry = mapptr[i];
+ if (!no_block(entry))
+ {
+ pager_dealloc_page(entry.block.p_index, entry.block.p_offset,
+ TRUE);
+ invalidate_block(mapptr[i]);
+ }
+ }
+}
+
+/* Truncate a memory object. First, any pages between the new size
+ and the (larger) old size are deallocated. Then, the size of
+ the pagemap may be reduced, an indirect map may be turned into
+ a direct map.
+
+ The pager must be locked by the caller. */
+static void
+pager_truncate(dpager_t pager, vm_size_t new_size) /* in pages */
+{
+ int i;
+ vm_size_t old_size;
+
+ pthread_mutex_lock(&pager->lock); /* XXX lock_write */
+
+ if (!pager->map)
+ goto done;
+
+ old_size = pager->size;
+
+ if (INDIRECT_PAGEMAP(old_size))
+ {
+ /* First handle the entire second-levels blocks that are being freed. */
+ for (i = INDIRECT_PAGEMAP_ENTRIES(new_size);
+ i < INDIRECT_PAGEMAP_ENTRIES(old_size);
+ ++i)
+ {
+ const dp_map_t mapptr = pager->map[i].indirect;
+ pager->map[i].indirect = (dp_map_t)0;
+ dealloc_direct (mapptr, PAGEMAP_ENTRIES, 0);
+ kfree ((char *)mapptr, PAGEMAP_SIZE(PAGEMAP_ENTRIES));
+ }
+
+ /* Now truncate what's now the final nonempty direct block. */
+ dealloc_direct (pager->map[(new_size - 1) / PAGEMAP_ENTRIES].indirect,
+ old_size & (PAGEMAP_ENTRIES - 1),
+ new_size & (PAGEMAP_ENTRIES - 1));
+
+ if (INDIRECT_PAGEMAP (new_size))
+ {
+ const dp_map_t old_mapptr = pager->map;
+ pager->map = (dp_map_t) kalloc (INDIRECT_PAGEMAP_SIZE(new_size));
+ memcpy (pager->map, old_mapptr, INDIRECT_PAGEMAP_SIZE(new_size));
+ kfree ((char *) old_mapptr, INDIRECT_PAGEMAP_SIZE (old_size));
+ }
+ else
+ {
+ /* We are truncating to a size small enough that it goes to using
+ a one-level map. We already have that map, as the first and only
+ nonempty element in our indirect map. */
+ const dp_map_t mapptr = pager->map[0].indirect;
+ kfree((char *)pager->map, INDIRECT_PAGEMAP_SIZE(old_size));
+ pager->map = mapptr;
+ }
+ }
+
+ if (! INDIRECT_PAGEMAP(old_size))
+ {
+ /* First deallocate pages in the truncated region. */
+ dealloc_direct (pager->map, old_size, new_size);
+ /* Now reduce the size of the direct map itself. We don't bother
+ with kalloc/kfree if it's not shrinking enough that kalloc.c
+ would actually use less. */
+ if (PAGEMAP_SIZE (new_size) <= PAGEMAP_SIZE (old_size) / 2)
+ {
+ const dp_map_t old_mapptr = pager->map;
+ pager->map = (dp_map_t) kalloc (PAGEMAP_SIZE (new_size));
+ memcpy (pager->map, old_mapptr, PAGEMAP_SIZE (new_size));
+ kfree ((char *) old_mapptr, PAGEMAP_SIZE (old_size));
+ }
+ }
+
+ done:
+ pager->size = new_size;
+ pthread_mutex_unlock(&pager->lock);
+
+#ifdef CHECKSUM
+#error write me
+#endif /* CHECKSUM */
+}
+
+
+/*
+ * Given an offset within a paging object, find the
+ * corresponding block within the paging partition.
+ * Return NO_BLOCK if none allocated.
+ */
+union dp_map
+pager_read_offset(pager, offset)
+ dpager_t pager;
+ vm_offset_t offset;
+{
+ vm_offset_t f_page;
+ union dp_map pager_offset;
+
+ f_page = atop(offset);
+
+#if DEBUG_READER_CONFLICTS
+ if (pager->readers > 0)
+ default_pager_read_conflicts++; /* would have proceeded with
+ read/write lock */
+#endif
+ pthread_mutex_lock(&pager->lock); /* XXX lock_read */
+#if DEBUG_READER_CONFLICTS
+ pager->readers++;
+#endif
+ if (f_page >= pager->size)
+ {
+ ddprintf ("%spager_read_offset pager %x: bad page %d >= size %d",
+ my_name, pager, f_page, pager->size);
+ pthread_mutex_unlock(&pager->lock);
+ return (union dp_map) (union dp_map *) NO_BLOCK;
+#if 0
+ panic("%spager_read_offset",my_name);
+#endif
+ }
+
+ invalidate_block(pager_offset);
+ if (INDIRECT_PAGEMAP(pager->size)) {
+ dp_map_t mapptr;
+
+ if (pager->map) {
+ mapptr = pager->map[f_page/PAGEMAP_ENTRIES].indirect;
+ if (mapptr)
+ pager_offset = mapptr[f_page%PAGEMAP_ENTRIES];
+ }
+ }
+ else {
+ if (pager->map)
+ pager_offset = pager->map[f_page];
+ }
+
+#if DEBUG_READER_CONFLICTS
+ pager->readers--;
+#endif
+ pthread_mutex_unlock(&pager->lock);
+ return (pager_offset);
+}
+
+#if USE_PRECIOUS
+/*
+ * Release a single disk block.
+ */
+void pager_release_offset(pager, offset)
+ dpager_t pager;
+ vm_offset_t offset;
+{
+ union dp_map entry;
+
+ offset = atop(offset);
+
+ pthread_mutex_lock(&pager->lock); /* XXX lock_read */
+
+ assert (pager->map);
+ if (INDIRECT_PAGEMAP(pager->size)) {
+ dp_map_t mapptr;
+
+ mapptr = pager->map[offset / PAGEMAP_ENTRIES].indirect;
+ entry = mapptr[offset % PAGEMAP_ENTRIES];
+ invalidate_block(mapptr[offset % PAGEMAP_ENTRIES]);
+ } else {
+ entry = pager->map[offset];
+ invalidate_block(pager->map[offset]);
+ }
+
+ pthread_mutex_unlock(&pager->lock);
+
+ pager_dealloc_page(entry.block.p_index, entry.block.p_offset, TRUE);
+}
+#endif /*USE_PRECIOUS*/
+
+
+/*
+ * Move a page from one partition to another
+ * New partition is locked, old partition is
+ * locked unless LOCK_OLD sez otherwise.
+ */
+union dp_map
+pager_move_page(block)
+ union dp_map block;
+{
+ partition_t old_part, new_part;
+ p_index_t old_pindex, new_pindex;
+ union dp_map ret;
+ vm_size_t size;
+ vm_offset_t raddr, offset, new_offset;
+ kern_return_t rc;
+ static char here[] = "%spager_move_page";
+
+ old_pindex = block.block.p_index;
+ invalidate_block(ret);
+
+ /* See if we have room to put it anywhere else */
+ new_pindex = choose_partition( ptoa(1), old_pindex);
+ if (no_partition(new_pindex))
+ return ret;
+
+ /* this unlocks the new partition */
+ new_offset = pager_alloc_page(new_pindex, FALSE);
+ if (new_offset == NO_BLOCK)
+ panic(here,my_name);
+
+ /*
+ * Got the resources, now move the data
+ */
+ddprintf ("pager_move_page(%x,%d,%d)\n",block.block.p_offset,old_pindex,new_pindex);
+ old_part = partition_of(old_pindex);
+ offset = ptoa(block.block.p_offset);
+ rc = page_read_file_direct (old_part->file,
+ offset,
+ vm_page_size,
+ &raddr,
+ &size);
+ if (rc != 0)
+ panic(here,my_name);
+
+ /* release old */
+ pager_dealloc_page(old_pindex, block.block.p_offset, FALSE);
+
+ new_part = partition_of(new_pindex);
+ offset = ptoa(new_offset);
+ rc = page_write_file_direct (new_part->file,
+ offset,
+ raddr,
+ size,
+ &size);
+ if (rc != 0)
+ panic(here,my_name);
+
+ (void) vm_deallocate( mach_task_self(), raddr, size);
+
+ ret.block.p_offset = new_offset;
+ ret.block.p_index = new_pindex;
+
+ return ret;
+}
+
+#ifdef CHECKSUM
+/*
+ * Return the checksum for a block.
+ */
+int
+pager_get_checksum(pager, offset)
+ dpager_t pager;
+ vm_offset_t offset;
+{
+ vm_offset_t f_page;
+ int checksum;
+
+ f_page = atop(offset);
+
+ pthread_mutex_lock(&pager->lock); /* XXX lock_read */
+ if (f_page >= pager->size)
+ panic("%spager_get_checksum",my_name);
+
+ if (INDIRECT_PAGEMAP(pager->size)) {
+ vm_offset_t *mapptr;
+
+ mapptr = (vm_offset_t *)pager->checksum[f_page/PAGEMAP_ENTRIES];
+ if (mapptr == 0)
+ checksum = NO_CHECKSUM;
+ else
+ checksum = mapptr[f_page%PAGEMAP_ENTRIES];
+ }
+ else {
+ checksum = pager->checksum[f_page];
+ }
+
+ pthread_mutex_unlock(&pager->lock);
+ return (checksum);
+}
+
+/*
+ * Remember the checksum for a block.
+ */
+int
+pager_put_checksum(pager, offset, checksum)
+ dpager_t pager;
+ vm_offset_t offset;
+ int checksum;
+{
+ vm_offset_t f_page;
+ static char here[] = "%spager_put_checksum";
+
+ f_page = atop(offset);
+
+ pthread_mutex_lock(&pager->lock); /* XXX lock_read */
+ if (f_page >= pager->size)
+ panic(here,my_name);
+
+ if (INDIRECT_PAGEMAP(pager->size)) {
+ vm_offset_t *mapptr;
+
+ mapptr = (vm_offset_t *)pager->checksum[f_page/PAGEMAP_ENTRIES];
+ if (mapptr == 0)
+ panic(here,my_name);
+
+ mapptr[f_page%PAGEMAP_ENTRIES] = checksum;
+ }
+ else {
+ pager->checksum[f_page] = checksum;
+ }
+ pthread_mutex_unlock(&pager->lock);
+}
+
+/*
+ * Compute a checksum - XOR each 32-bit word.
+ */
+int
+compute_checksum(addr, size)
+ vm_offset_t addr;
+ vm_size_t size;
+{
+ int checksum = NO_CHECKSUM;
+ int *ptr;
+ int count;
+
+ ptr = (int *)addr;
+ count = size / sizeof(int);
+
+ while (--count >= 0)
+ checksum ^= *ptr++;
+
+ return (checksum);
+}
+#endif /* CHECKSUM */
+
+/*
+ * Given an offset within a paging object, find the
+ * corresponding block within the paging partition.
+ * Allocate a new block if necessary.
+ *
+ * WARNING: paging objects apparently may be extended
+ * without notice!
+ */
+union dp_map
+pager_write_offset(pager, offset)
+ dpager_t pager;
+ vm_offset_t offset;
+{
+ vm_offset_t f_page;
+ dp_map_t mapptr;
+ union dp_map block;
+
+ invalidate_block(block);
+
+ f_page = atop(offset);
+
+#if DEBUG_READER_CONFLICTS
+ if (pager->readers > 0)
+ default_pager_read_conflicts++; /* would have proceeded with
+ read/write lock */
+#endif
+ pthread_mutex_lock(&pager->lock); /* XXX lock_read */
+#if DEBUG_READER_CONFLICTS
+ pager->readers++;
+#endif
+
+ /* Catch the case where we had no initial fit partition
+ for this object, but one was added later on */
+ if (no_partition(pager->cur_partition)) {
+ p_index_t new_part;
+ vm_size_t size;
+
+ size = (f_page > pager->size) ? f_page : pager->size;
+ new_part = choose_partition(ptoa(size), P_INDEX_INVALID);
+ if (no_partition(new_part))
+ new_part = choose_partition(ptoa(1), P_INDEX_INVALID);
+ if (no_partition(new_part))
+ /* give up right now to avoid confusion */
+ goto out;
+ else
+ pager->cur_partition = new_part;
+ }
+
+ while (f_page >= pager->size) {
+ ddprintf ("pager_write_offset: extending: %x %x\n", f_page, pager->size);
+
+ /*
+ * Paging object must be extended.
+ * Remember that offset is 0-based, but size is 1-based.
+ */
+#if DEBUG_READER_CONFLICTS
+ pager->readers--;
+#endif
+ pthread_mutex_unlock(&pager->lock);
+ pager_extend(pager, f_page + 1);
+#if DEBUG_READER_CONFLICTS
+ if (pager->readers > 0)
+ default_pager_read_conflicts++; /* would have proceeded with
+ read/write lock */
+#endif
+ pthread_mutex_lock(&pager->lock); /* XXX lock_read */
+#if DEBUG_READER_CONFLICTS
+ pager->readers++;
+#endif
+ ddprintf ("pager_write_offset: done extending: %x %x\n", f_page, pager->size);
+ }
+
+ if (INDIRECT_PAGEMAP(pager->size)) {
+ ddprintf ("pager_write_offset: indirect\n");
+ mapptr = pager_get_direct_map(pager);
+ mapptr = mapptr[f_page/PAGEMAP_ENTRIES].indirect;
+ if (mapptr == 0) {
+ /*
+ * Allocate the indirect block
+ */
+ int i;
+ ddprintf ("pager_write_offset: allocating indirect\n");
+
+ mapptr = (dp_map_t) kalloc(PAGEMAP_SIZE(PAGEMAP_ENTRIES));
+ if (mapptr == 0) {
+ /* out of space! */
+ no_paging_space(TRUE);
+ goto out;
+ }
+ pager->map[f_page/PAGEMAP_ENTRIES].indirect = mapptr;
+ for (i = 0; i < PAGEMAP_ENTRIES; i++)
+ invalidate_block(mapptr[i]);
+#ifdef CHECKSUM
+ {
+ vm_offset_t *cksumptr;
+ int j;
+
+ cksumptr = (vm_offset_t *)
+ kalloc(PAGEMAP_SIZE(PAGEMAP_ENTRIES));
+ if (cksumptr == 0) {
+ /* out of space! */
+ no_paging_space(TRUE);
+ goto out;
+ }
+ pager->checksum[f_page/PAGEMAP_ENTRIES]
+ = (vm_offset_t)cksumptr;
+ for (j = 0; j < PAGEMAP_ENTRIES; j++)
+ cksumptr[j] = NO_CHECKSUM;
+ }
+#endif /* CHECKSUM */
+ }
+ f_page %= PAGEMAP_ENTRIES;
+ }
+ else {
+ mapptr = pager_get_direct_map(pager);
+ }
+
+ block = mapptr[f_page];
+ ddprintf ("pager_write_offset: block starts as %x[%x] %x\n", mapptr, f_page, block);
+ if (no_block(block)) {
+ vm_offset_t off;
+
+ /* get room now */
+ off = pager_alloc_page(pager->cur_partition, TRUE);
+ if (off == NO_BLOCK) {
+ /*
+ * Before giving up, try all other partitions.
+ */
+ p_index_t new_part;
+
+ ddprintf ("pager_write_offset: could not allocate block\n");
+ /* returns it locked (if any one is non-full) */
+ new_part = choose_partition( ptoa(1), pager->cur_partition);
+ if ( ! no_partition(new_part) ) {
+
+#if debug
+dprintf("%s partition %x filled,", my_name, pager->cur_partition);
+dprintf("extending object %x (size %x) to %x.\n",
+ pager, pager->size, new_part);
+#endif
+
+ /* this one tastes better */
+ pager->cur_partition = new_part;
+
+ /* this unlocks the partition too */
+ off = pager_alloc_page(pager->cur_partition, FALSE);
+
+ }
+
+ if (off == NO_BLOCK) {
+ /*
+ * Oh well.
+ */
+ overcommitted(FALSE, 1);
+ goto out;
+ }
+ ddprintf ("pager_write_offset: decided to allocate block\n");
+ }
+ block.block.p_offset = off;
+ block.block.p_index = pager->cur_partition;
+ mapptr[f_page] = block;
+ }
+
+out:
+
+#if DEBUG_READER_CONFLICTS
+ pager->readers--;
+#endif
+ pthread_mutex_unlock(&pager->lock);
+ return (block);
+}
+
+/*
+ * Deallocate all of the blocks belonging to a paging object.
+ * No locking needed because no other operations can be in progress.
+ */
+void
+pager_dealloc(pager)
+ dpager_t pager;
+{
+ int i, j;
+ dp_map_t mapptr;
+ union dp_map block;
+
+ if (!pager->map)
+ return;
+
+ if (INDIRECT_PAGEMAP(pager->size)) {
+ for (i = INDIRECT_PAGEMAP_ENTRIES(pager->size); --i >= 0; ) {
+ mapptr = pager->map[i].indirect;
+ if (mapptr != 0) {
+ for (j = 0; j < PAGEMAP_ENTRIES; j++) {
+ block = mapptr[j];
+ if ( ! no_block(block) )
+ pager_dealloc_page(block.block.p_index,
+ block.block.p_offset, TRUE);
+ }
+ kfree((char *)mapptr, PAGEMAP_SIZE(PAGEMAP_ENTRIES));
+ pager->map[i].indirect = (dp_map_t) 0;
+ }
+ }
+ kfree((char *)pager->map, INDIRECT_PAGEMAP_SIZE(pager->size));
+ pager->map = (dp_map_t) 0;
+#ifdef CHECKSUM
+ for (i = INDIRECT_PAGEMAP_ENTRIES(pager->size); --i >= 0; ) {
+ mapptr = (vm_offset_t *)pager->checksum[i];
+ if (mapptr) {
+ kfree((char *)mapptr, PAGEMAP_SIZE(PAGEMAP_ENTRIES));
+ }
+ }
+ kfree((char *)pager->checksum,
+ INDIRECT_PAGEMAP_SIZE(pager->size));
+#endif /* CHECKSUM */
+ }
+ else {
+ mapptr = pager->map;
+ for (i = 0; i < pager->size; i++ ) {
+ block = mapptr[i];
+ if ( ! no_block(block) )
+ pager_dealloc_page(block.block.p_index,
+ block.block.p_offset, TRUE);
+ }
+ kfree((char *)pager->map, PAGEMAP_SIZE(pager->size));
+ pager->map = (dp_map_t) 0;
+#ifdef CHECKSUM
+ kfree((char *)pager->checksum, PAGEMAP_SIZE(pager->size));
+#endif /* CHECKSUM */
+ }
+}
+
+/*
+ * Move all the pages of a PAGER that live in a
+ * partition PINDEX somewhere else.
+ * Pager should be write-locked, partition too.
+ * Returns FALSE if it could not do it, but
+ * some pages might have been moved nonetheless.
+ */
+boolean_t
+pager_realloc(pager, pindex)
+ dpager_t pager;
+ p_index_t pindex;
+{
+ dp_map_t map, emap;
+ vm_size_t size;
+ union dp_map block;
+
+ if (!pager->map)
+ return TRUE;
+
+ size = pager->size; /* in pages */
+ map = pager->map;
+
+ if (INDIRECT_PAGEMAP(size)) {
+ for (emap = &map[INDIRECT_PAGEMAP_ENTRIES(size)];
+ map < emap; map++) {
+
+ dp_map_t map2, emap2;
+
+ if ((map2 = map->indirect) == 0)
+ continue;
+
+ for (emap2 = &map2[PAGEMAP_ENTRIES];
+ map2 < emap2; map2++)
+ if ( map2->block.p_index == pindex) {
+
+ block = pager_move_page(*map2);
+ if (!no_block(block))
+ *map2 = block;
+ else
+ return FALSE;
+ }
+
+ }
+ goto ok;
+ }
+
+ /* A small one */
+ for (emap = &map[size]; map < emap; map++)
+ if (map->block.p_index == pindex) {
+ block = pager_move_page(*map);
+ if (!no_block(block))
+ *map = block;
+ else
+ return FALSE;
+ }
+ok:
+ pager->cur_partition = choose_partition(0, P_INDEX_INVALID);
+ return TRUE;
+}
+
+/*
+ * Read/write routines.
+ */
+#define PAGER_SUCCESS 0
+#define PAGER_ABSENT 1
+#define PAGER_ERROR 2
+
+/*
+ * Read data from a default pager. Addr is the address of a buffer
+ * to fill. Out_addr returns the buffer that contains the data;
+ * if it is different from <addr>, it must be deallocated after use.
+ */
+int
+default_read(ds, addr, size, offset, out_addr, deallocate, external)
+ dpager_t ds;
+ vm_offset_t addr; /* pointer to block to fill */
+ vm_size_t size;
+ vm_offset_t offset;
+ vm_offset_t *out_addr;
+ /* returns pointer to data */
+ boolean_t deallocate;
+ boolean_t external;
+{
+ union dp_map block;
+ vm_offset_t raddr;
+ vm_size_t rsize;
+ int rc;
+ boolean_t first_time;
+ partition_t part;
+#ifdef CHECKSUM
+ vm_size_t original_size = size;
+#endif /* CHECKSUM */
+ vm_offset_t original_offset = offset;
+
+ /*
+ * Find the block in the paging partition
+ */
+ block = pager_read_offset(ds, offset);
+ if ( no_block(block) ) {
+ if (external) {
+ /*
+ * An external object is requesting unswapped data,
+ * zero fill the page and return.
+ */
+ bzero((char *) addr, vm_page_size);
+ *out_addr = addr;
+ return (PAGER_SUCCESS);
+ }
+ return (PAGER_ABSENT);
+ }
+
+ /*
+ * Read it, trying for the entire page.
+ */
+ offset = ptoa(block.block.p_offset);
+ddprintf ("default_read(%x,%x,%x,%d)\n",addr,size,offset,block.block.p_index);
+ part = partition_of(block.block.p_index);
+ first_time = TRUE;
+ *out_addr = addr;
+
+ do {
+ rc = page_read_file_direct(part->file,
+ offset,
+ size,
+ &raddr,
+ &rsize);
+ if (rc != 0)
+ return (PAGER_ERROR);
+
+ /*
+ * If we got the entire page on the first read, return it.
+ */
+ if (first_time && rsize == size) {
+ *out_addr = raddr;
+ break;
+ }
+ /*
+ * Otherwise, copy the data into the
+ * buffer we were passed, and try for
+ * the next piece.
+ */
+ first_time = FALSE;
+ bcopy((char *)raddr, (char *)addr, rsize);
+ addr += rsize;
+ offset += rsize;
+ size -= rsize;
+ } while (size != 0);
+
+#if USE_PRECIOUS
+ if (deallocate)
+ pager_release_offset(ds, original_offset);
+#endif /*USE_PRECIOUS*/
+
+#ifdef CHECKSUM
+ {
+ int write_checksum,
+ read_checksum;
+
+ write_checksum = pager_get_checksum(ds, original_offset);
+ read_checksum = compute_checksum(*out_addr, original_size);
+ if (write_checksum != read_checksum) {
+ panic(
+ "PAGER CHECKSUM ERROR: offset 0x%x, written 0x%x, read 0x%x",
+ original_offset, write_checksum, read_checksum);
+ }
+ }
+#endif /* CHECKSUM */
+ return (PAGER_SUCCESS);
+}
+
+int
+default_write(ds, addr, size, offset)
+ dpager_t ds;
+ vm_offset_t addr;
+ vm_size_t size;
+ vm_offset_t offset;
+{
+ union dp_map block;
+ partition_t part;
+ vm_size_t wsize;
+ int rc;
+
+ ddprintf ("default_write: pager offset %x\n", offset);
+
+ /*
+ * Find block in paging partition
+ */
+ block = pager_write_offset(ds, offset);
+ if ( no_block(block) )
+ return (PAGER_ERROR);
+
+#ifdef CHECKSUM
+ /*
+ * Save checksum
+ */
+ {
+ int checksum;
+
+ checksum = compute_checksum(addr, size);
+ pager_put_checksum(ds, offset, checksum);
+ }
+#endif /* CHECKSUM */
+ offset = ptoa(block.block.p_offset);
+ddprintf ("default_write(%x,%x,%x,%d)\n",addr,size,offset,block.block.p_index);
+ part = partition_of(block.block.p_index);
+
+ /*
+ * There are various assumptions made here,we
+ * will not get into the next disk 'block' by
+ * accident. It might well be non-contiguous.
+ */
+ do {
+ rc = page_write_file_direct(part->file,
+ offset,
+ addr,
+ size,
+ &wsize);
+ if (rc != 0) {
+ dprintf("*** PAGER ERROR: default_write: ");
+ dprintf("ds=0x%x addr=0x%x size=0x%x offset=0x%x resid=0x%x\n",
+ ds, addr, size, offset, wsize);
+ return (PAGER_ERROR);
+ }
+ addr += wsize;
+ offset += wsize;
+ size -= wsize;
+ } while (size != 0);
+ return (PAGER_SUCCESS);
+}
+
+boolean_t
+default_has_page(ds, offset)
+ dpager_t ds;
+ vm_offset_t offset;
+{
+ return ( ! no_block(pager_read_offset(ds, offset)) );
+}
+
+#if PARALLEL
+#define dstruct_lock_init(ds) pthread_mutex_init(&ds->lock, NULL)
+#define dstruct_lock(ds) pthread_mutex_lock(&ds->lock)
+#define dstruct_unlock(ds) pthread_mutex_unlock(&ds->lock)
+#else /* PARALLEL */
+#define dstruct_lock_init(ds)
+#define dstruct_lock(ds)
+#define dstruct_unlock(ds)
+#endif /* PARALLEL */
+
+struct pager_port all_pagers;
+
+#define pager_port_list_init() \
+{ \
+ pthread_mutex_init(&all_pagers.lock, NULL); \
+ hurd_ihash_init (&all_pagers.htable, \
+ offsetof (struct dstruct, htable_locp)); \
+ queue_init(&all_pagers.leak_queue); \
+}
+
+void pager_port_list_insert(port, ds)
+ mach_port_t port;
+ default_pager_t ds;
+{
+ pthread_mutex_lock(&all_pagers.lock);
+ hurd_ihash_add (&all_pagers.htable,
+ (hurd_ihash_key_t) port,
+ (hurd_ihash_value_t) ds);
+ pthread_mutex_unlock(&all_pagers.lock);
+}
+
+void pager_port_list_delete(ds)
+ default_pager_t ds;
+{
+ pthread_mutex_lock(&all_pagers.lock);
+ hurd_ihash_locp_remove (&all_pagers.htable,
+ ds->htable_locp);
+ pthread_mutex_unlock(&all_pagers.lock);
+}
+
+/*
+ * Destroy a paging partition.
+ * XXX this is not re-entrant XXX
+ */
+kern_return_t
+destroy_paging_partition(name, pp_private)
+ char *name;
+ void **pp_private;
+{
+ unsigned int id = part_id(name);
+ partition_t part = NULL;
+ boolean_t all_ok = TRUE;
+ default_pager_t entry;
+ int pindex;
+
+ /*
+ * Find and take partition out of list
+ * This prevents choose_partition from
+ * getting in the way.
+ */
+ pthread_mutex_lock(&all_partitions.lock);
+ for (pindex = 0; pindex < all_partitions.n_partitions; pindex++) {
+ part = partition_of(pindex);
+ if (part && (part->id == id)) break;
+ }
+ if (! part) {
+ pthread_mutex_unlock(&all_partitions.lock);
+ return KERN_INVALID_ARGUMENT;
+ }
+ part->going_away = TRUE;
+ pthread_mutex_unlock(&all_partitions.lock);
+
+ /*
+ * This might take a while..
+ */
+all_over_again:
+#if debug
+dprintf("Partition x%x (id x%x) for %s, all_ok %d\n", part, id, name, all_ok);
+#endif
+ all_ok = TRUE;
+ pthread_mutex_lock(&part->p_lock);
+
+ pthread_mutex_lock(&all_pagers.lock);
+ HURD_IHASH_ITERATE (&all_pagers.htable, val) {
+ entry = (default_pager_t) val;
+
+ dstruct_lock(entry);
+
+ if (pthread_mutex_trylock(&entry->dpager.lock)) {
+
+ dstruct_unlock(entry);
+ pthread_mutex_unlock(&all_pagers.lock);
+ pthread_mutex_unlock(&part->p_lock);
+
+ /* yield the processor */
+ (void) thread_switch(MACH_PORT_NULL,
+ SWITCH_OPTION_NONE, 0);
+
+ goto all_over_again;
+
+ }
+
+ /*
+ * See if we can relocate all the pages of this object
+ * currently on this partition on some other partition
+ */
+ all_ok = pager_realloc(&entry->dpager, pindex);
+
+ pthread_mutex_unlock(&entry->dpager.lock);
+ dstruct_unlock(entry);
+
+ if (!all_ok) break;
+
+ }
+ pthread_mutex_unlock(&all_pagers.lock);
+
+ if (all_ok) {
+ /* No need to unlock partition, there are no refs left */
+
+ set_partition_of(pindex, 0);
+ *pp_private = part->file;
+ kfree(part->bitmap, howmany(part->total_size, NB_BM) * sizeof(bm_entry_t));
+ kfree(part, sizeof(struct part));
+ dprintf("%s Removed paging partition %s\n", my_name, name);
+ return KERN_SUCCESS;
+ }
+
+ /*
+ * Put partition back in.
+ */
+ part->going_away = FALSE;
+
+ return KERN_FAILURE;
+}
+
+
+/*
+ * We use the sequence numbers on requests to regulate
+ * our parallelism. In general, we allow multiple reads and writes
+ * to proceed in parallel, with the exception that reads must
+ * wait for previous writes to finish. (Because the kernel might
+ * generate a data-request for a page on the heels of a data-write
+ * for the same page, and we must avoid returning stale data.)
+ * terminate requests wait for proceeding reads and writes to finish.
+ */
+
+unsigned int default_pager_total = 0; /* debugging */
+unsigned int default_pager_wait_seqno = 0; /* debugging */
+unsigned int default_pager_wait_read = 0; /* debugging */
+unsigned int default_pager_wait_write = 0; /* debugging */
+unsigned int default_pager_wait_refs = 0; /* debugging */
+
+#if PARALLEL
+/*
+ * Waits for correct sequence number. Leaves pager locked.
+ */
+void pager_port_lock(ds, seqno)
+ default_pager_t ds;
+ mach_port_seqno_t seqno;
+{
+ default_pager_total++;
+ dstruct_lock(ds);
+ while (ds->seqno != seqno) {
+ default_pager_wait_seqno++;
+ pthread_cond_wait(&ds->waiting_seqno, &ds->lock);
+ }
+}
+
+/*
+ * Increments sequence number and unlocks pager.
+ */
+void pager_port_unlock(ds)
+ default_pager_t ds;
+{
+ ds->seqno++;
+ dstruct_unlock(ds);
+ pthread_cond_broadcast(&ds->waiting_seqno);
+}
+
+/*
+ * Start a read - one more reader. Pager must be locked.
+ */
+void pager_port_start_read(ds)
+ default_pager_t ds;
+{
+ ds->readers++;
+}
+
+/*
+ * Wait for readers. Unlocks and relocks pager if wait needed.
+ */
+void pager_port_wait_for_readers(ds)
+ default_pager_t ds;
+{
+ while (ds->readers != 0) {
+ default_pager_wait_read++;
+ pthread_cond_wait(&ds->waiting_read, &ds->lock);
+ }
+}
+
+/*
+ * Finish a read. Pager is unlocked and returns unlocked.
+ */
+void pager_port_finish_read(ds)
+ default_pager_t ds;
+{
+ dstruct_lock(ds);
+ if (--ds->readers == 0) {
+ dstruct_unlock(ds);
+ pthread_cond_broadcast(&ds->waiting_read);
+ }
+ else {
+ dstruct_unlock(ds);
+ }
+}
+
+/*
+ * Start a write - one more writer. Pager must be locked.
+ */
+void pager_port_start_write(ds)
+ default_pager_t ds;
+{
+ ds->writers++;
+}
+
+/*
+ * Wait for writers. Unlocks and relocks pager if wait needed.
+ */
+void pager_port_wait_for_writers(ds)
+ default_pager_t ds;
+{
+ while (ds->writers != 0) {
+ default_pager_wait_write++;
+ pthread_cond_wait(&ds->waiting_write, &ds->lock);
+ }
+}
+
+/*
+ * Finish a write. Pager is unlocked and returns unlocked.
+ */
+void pager_port_finish_write(ds)
+ default_pager_t ds;
+{
+ dstruct_lock(ds);
+ if (--ds->writers == 0) {
+ dstruct_unlock(ds);
+ pthread_cond_broadcast(&ds->waiting_write);
+ }
+ else {
+ dstruct_unlock(ds);
+ }
+}
+
+/*
+ * Wait for concurrent default_pager_objects.
+ * Unlocks and relocks pager if wait needed.
+ */
+void pager_port_wait_for_refs(ds)
+ default_pager_t ds;
+{
+ while (ds->name_refs == 0) {
+ default_pager_wait_refs++;
+ pthread_cond_wait(&ds->waiting_refs, &ds->lock);
+ }
+}
+
+/*
+ * Finished creating name refs - wake up waiters.
+ */
+void pager_port_finish_refs(ds)
+ default_pager_t ds;
+{
+ pthread_cond_broadcast(&ds->waiting_refs);
+}
+
+#else /* PARALLEL */
+
+#define pager_port_lock(ds,seqno)
+#define pager_port_unlock(ds)
+#define pager_port_start_read(ds)
+#define pager_port_wait_for_readers(ds)
+#define pager_port_finish_read(ds)
+#define pager_port_start_write(ds)
+#define pager_port_wait_for_writers(ds)
+#define pager_port_finish_write(ds)
+#define pager_port_wait_for_refs(ds)
+#define pager_port_finish_refs(ds)
+
+#endif /* PARALLEL */
+
+/*
+ * Default pager.
+ */
+task_t default_pager_self; /* Our task port. */
+
+mach_port_t default_pager_default_port; /* Port for memory_object_create. */
+
+/* We catch exceptions on ourself & startup using this port. */
+mach_port_t default_pager_exception_port;
+
+mach_port_t default_pager_internal_set; /* Port set for internal objects. */
+mach_port_t default_pager_external_set; /* Port set for external objects. */
+mach_port_t default_pager_default_set; /* Port set for "default" thread. */
+
+typedef struct default_pager_thread {
+ pthread_t dpt_thread; /* Server thread. */
+ vm_offset_t dpt_buffer; /* Read buffer. */
+ boolean_t dpt_internal; /* Do we handle internal objects? */
+} default_pager_thread_t;
+
+#if PARALLEL
+ /* determine number of threads at run time */
+#define DEFAULT_PAGER_INTERNAL_COUNT (0)
+
+#else /* PARALLEL */
+#define DEFAULT_PAGER_INTERNAL_COUNT (1)
+#endif /* PARALLEL */
+
+/* Memory created by default_pager_object_create should mostly be resident. */
+#define DEFAULT_PAGER_EXTERNAL_COUNT (1)
+
+unsigned int default_pager_internal_count = DEFAULT_PAGER_INTERNAL_COUNT;
+ /* Number of "internal" threads. */
+unsigned int default_pager_external_count = DEFAULT_PAGER_EXTERNAL_COUNT;
+ /* Number of "external" threads. */
+
+default_pager_t pager_port_alloc(size)
+ vm_size_t size;
+{
+ default_pager_t ds;
+ p_index_t part;
+
+ ds = (default_pager_t) kalloc(sizeof *ds);
+ if (ds == DEFAULT_PAGER_NULL)
+ panic("%spager_port_alloc",my_name);
+ bzero((char *) ds, sizeof *ds);
+
+ dstruct_lock_init(ds);
+
+ /*
+ * Get a suitable partition. If none big enough
+ * just pick one and overcommit. If no partitions
+ * at all.. well just fake one so that we will
+ * kill specific objects on pageouts rather than
+ * panicing the system now.
+ */
+ part = choose_partition(size, P_INDEX_INVALID);
+ if (no_partition(part)) {
+ overcommitted(FALSE, atop(size));
+ part = choose_partition(0,P_INDEX_INVALID);
+#if debug
+ if (no_partition(part))
+ dprintf("%s No paging space at all !!\n", my_name);
+#endif
+ }
+ pager_alloc(&ds->dpager, part, size);
+
+ return ds;
+}
+
+mach_port_urefs_t default_pager_max_urefs = 10000;
+
+/*
+ * Check user reference count on pager_request port.
+ * Pager must be locked.
+ * Unlocks and re-locks pager if needs to call kernel.
+ */
+void pager_port_check_request(ds, pager_request)
+ default_pager_t ds;
+ mach_port_t pager_request;
+{
+ mach_port_delta_t delta;
+ kern_return_t kr;
+
+ assert(ds->pager_request == pager_request);
+
+ if (++ds->request_refs > default_pager_max_urefs) {
+ delta = 1 - ds->request_refs;
+ ds->request_refs = 1;
+
+ dstruct_unlock(ds);
+
+ /*
+ * Deallocate excess user references.
+ */
+
+ kr = mach_port_mod_refs(default_pager_self, pager_request,
+ MACH_PORT_RIGHT_SEND, delta);
+ if (kr != KERN_SUCCESS)
+ panic("%spager_port_check_request",my_name);
+
+ dstruct_lock(ds);
+ }
+}
+
+void default_pager_add(ds, internal)
+ default_pager_t ds;
+ boolean_t internal;
+{
+ mach_port_t pager = ds->pager;
+ mach_port_t pset;
+ mach_port_mscount_t sync;
+ mach_port_t previous;
+ kern_return_t kr;
+ static char here[] = "%sdefault_pager_add";
+
+ /*
+ * The port currently has a make-send count of zero,
+ * because either we just created the port or we just
+ * received the port in a memory_object_create request.
+ */
+
+ if (internal) {
+ /* possibly generate an immediate no-senders notification */
+ sync = 0;
+ pset = default_pager_internal_set;
+ ds->external = FALSE;
+ } else {
+ /* delay notification till send right is created */
+ sync = 1;
+ pset = default_pager_external_set;
+ ds->external = TRUE;
+ }
+
+ kr = mach_port_request_notification(default_pager_self, pager,
+ MACH_NOTIFY_NO_SENDERS, sync,
+ pager, MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &previous);
+ if ((kr != KERN_SUCCESS) || (previous != MACH_PORT_NULL))
+ panic(here,my_name);
+
+ kr = mach_port_move_member(default_pager_self, pager, pset);
+ if (kr != KERN_SUCCESS)
+ panic(here,my_name);
+}
+
+/*
+ * Routine: memory_object_create
+ * Purpose:
+ * Handle requests for memory objects from the
+ * kernel.
+ * Notes:
+ * Because we only give out the default memory
+ * manager port to the kernel, we don't have to
+ * be so paranoid about the contents.
+ */
+kern_return_t
+seqnos_memory_object_create(old_pager, seqno, new_pager, new_size,
+ new_pager_request, new_pager_name, new_page_size)
+ mach_port_t old_pager;
+ mach_port_seqno_t seqno;
+ mach_port_t new_pager;
+ vm_size_t new_size;
+ mach_port_t new_pager_request;
+ mach_port_t new_pager_name;
+ vm_size_t new_page_size;
+{
+ default_pager_t ds;
+
+ assert(old_pager == default_pager_default_port);
+ assert(MACH_PORT_VALID(new_pager_request));
+ assert(MACH_PORT_VALID(new_pager_name));
+ assert(new_page_size == vm_page_size);
+
+ ds = pager_port_alloc(new_size);
+
+ /*
+ * Set up associations between these ports
+ * and this default_pager structure
+ */
+
+ ds->pager = new_pager;
+ ds->pager_request = new_pager_request;
+ ds->request_refs = 1;
+ ds->pager_name = new_pager_name;
+ ds->name_refs = 1;
+
+ /*
+ * After this, other threads might receive requests
+ * for this memory object or find it in the port list.
+ */
+
+ pager_port_list_insert(new_pager, ds);
+ default_pager_add(ds, TRUE);
+
+ return(KERN_SUCCESS);
+}
+
+memory_object_copy_strategy_t default_pager_copy_strategy =
+ MEMORY_OBJECT_COPY_DELAY;
+
+kern_return_t
+seqnos_memory_object_init(ds, seqno, pager_request, pager_name,
+ pager_page_size)
+ default_pager_t ds;
+ mach_port_seqno_t seqno;
+ mach_port_t pager_request;
+ mach_port_t pager_name;
+ vm_size_t pager_page_size;
+{
+ kern_return_t kr;
+ static char here[] = "%sinit";
+
+ assert(MACH_PORT_VALID(pager_request));
+ assert(MACH_PORT_VALID(pager_name));
+ assert(pager_page_size == vm_page_size);
+
+ if (ds == DEFAULT_PAGER_NULL)
+ panic(here, my_name);
+ pager_port_lock(ds, seqno);
+
+ if (ds->pager_request != MACH_PORT_NULL)
+ panic(here, my_name);
+
+ ds->pager_request = pager_request;
+ ds->request_refs = 1;
+ ds->pager_name = pager_name;
+ ds->name_refs = 1;
+
+ /*
+ * Even if the kernel immediately terminates the object,
+ * the pager_request port won't be destroyed until
+ * we process the terminate request, which won't happen
+ * until we unlock the object.
+ */
+
+ kr = memory_object_ready(pager_request,
+ FALSE, /* Do not cache */
+ default_pager_copy_strategy);
+ if (kr != KERN_SUCCESS)
+ panic(here, my_name);
+
+ pager_port_unlock(ds);
+
+ return(KERN_SUCCESS);
+}
+
+kern_return_t
+seqnos_memory_object_terminate(ds, seqno, pager_request, pager_name)
+ default_pager_t ds;
+ mach_port_seqno_t seqno;
+ mach_port_t pager_request;
+ mach_port_t pager_name;
+{
+ static char here[] = "%sterminate";
+
+ /*
+ * pager_request and pager_name are receive rights,
+ * not send rights.
+ */
+
+ if (ds == DEFAULT_PAGER_NULL)
+ panic(here, my_name);
+ddprintf ("seqnos_memory_object_terminate <%p>: pager_port_lock: <%p>[s:%d,r:%d,w:%d,l:%d], %d\n",
+ &ds, ds, ds->seqno, ds->readers, ds->writers, ds->lock.held, seqno);
+ pager_port_lock(ds, seqno);
+
+ /*
+ * Wait for read and write requests to terminate.
+ */
+
+ pager_port_wait_for_readers(ds);
+ pager_port_wait_for_writers(ds);
+
+ /*
+ * After memory_object_terminate both memory_object_init
+ * and a no-senders notification are possible, so we need
+ * to clean up the request and name ports but leave
+ * the pager port.
+ *
+ * A concurrent default_pager_objects might be allocating
+ * more references for the name port. In this case,
+ * we must first wait for it to finish.
+ */
+
+ pager_port_wait_for_refs(ds);
+
+ if (ds->external)
+ pager_request = ds->pager_request;
+ ds->pager_request = MACH_PORT_NULL;
+ ds->request_refs = 0;
+ assert(ds->pager_name == pager_name);
+ ds->pager_name = MACH_PORT_NULL;
+ ds->name_refs = 0;
+ddprintf ("seqnos_memory_object_terminate <%p>: pager_port_unlock: <%p>[s:%d,r:%d,w:%d,l:%d]\n",
+ &ds, ds, ds->seqno, ds->readers, ds->writers, ds->lock.held);
+ pager_port_unlock(ds);
+
+ /*
+ * Now we destroy our port rights.
+ */
+
+ mach_port_destroy(mach_task_self(), pager_request);
+ mach_port_destroy(mach_task_self(), pager_name);
+
+ return (KERN_SUCCESS);
+}
+
+void default_pager_no_senders(pager, seqno, mscount)
+ memory_object_t pager;
+ mach_port_seqno_t seqno;
+ mach_port_mscount_t mscount;
+{
+ default_pager_t ds;
+ kern_return_t kr;
+ static char here[] = "%sno_senders";
+
+ /*
+ * Because we don't give out multiple send rights
+ * for a memory object, there can't be a race
+ * between getting a no-senders notification
+ * and creating a new send right for the object.
+ * Hence we don't keep track of mscount.
+ */
+
+
+ ds = begin_using_default_pager(pager);
+ if (ds == DEFAULT_PAGER_NULL)
+ panic(here,my_name);
+ pager_port_lock(ds, seqno);
+
+ /*
+ * We shouldn't get a no-senders notification
+ * when the kernel has the object cached.
+ */
+
+ if (ds->pager_request != MACH_PORT_NULL)
+ panic(here,my_name);
+
+ /*
+ * Unlock the pager (though there should be no one
+ * waiting for it).
+ */
+ dstruct_unlock(ds);
+
+ /*
+ * Remove the memory object port association, and then
+ * the destroy the port itself. We must remove the object
+ * from the port list before deallocating the pager,
+ * because of default_pager_objects.
+ */
+
+ pager_port_list_delete(ds);
+ pager_dealloc(&ds->dpager);
+
+ kr = mach_port_mod_refs(default_pager_self, pager,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+ if (kr != KERN_SUCCESS)
+ panic(here,my_name);
+
+ /*
+ * Do this *after* deallocating the port name
+ */
+ kfree((char *) ds, sizeof(*ds));
+
+ /*
+ * Recover memory that we might have wasted because
+ * of name conflicts
+ */
+ pthread_mutex_lock(&all_pagers.lock);
+
+ while (!queue_empty(&all_pagers.leak_queue)) {
+
+ ds = (default_pager_t) queue_first(&all_pagers.leak_queue);
+ queue_remove_first(&all_pagers.leak_queue, ds, default_pager_t, links);
+ kfree((char *) ds, sizeof(*ds));
+ }
+
+ pthread_mutex_unlock(&all_pagers.lock);
+}
+
+int default_pager_pagein_count = 0;
+int default_pager_pageout_count = 0;
+
+static __thread default_pager_thread_t *dpt;
+
+kern_return_t
+seqnos_memory_object_data_request(ds, seqno, reply_to, offset,
+ length, protection_required)
+ default_pager_t ds;
+ mach_port_seqno_t seqno;
+ mach_port_t reply_to;
+ vm_offset_t offset;
+ vm_size_t length;
+ vm_prot_t protection_required;
+{
+ vm_offset_t addr;
+ unsigned int errors;
+ kern_return_t rc;
+ static char here[] = "%sdata_request";
+
+ if (length != vm_page_size)
+ panic(here,my_name);
+
+ if (ds == DEFAULT_PAGER_NULL)
+ panic(here,my_name);
+ddprintf ("seqnos_memory_object_data_request <%p>: pager_port_lock: <%p>[s:%d,r:%d,w:%d,l:%d], %d\n",
+ &ds, ds, ds->seqno, ds->readers, ds->writers, ds->lock.held, seqno);
+ pager_port_lock(ds, seqno);
+ pager_port_check_request(ds, reply_to);
+ pager_port_wait_for_writers(ds);
+ pager_port_start_read(ds);
+
+ /*
+ * Get error count while pager locked.
+ */
+ errors = ds->errors;
+
+ddprintf ("seqnos_memory_object_data_request <%p>: pager_port_unlock: <%p>[s:%d,r:%d,w:%d,l:%d]\n",
+ &ds, ds, ds->seqno, ds->readers, ds->writers, ds->lock.held);
+ pager_port_unlock(ds);
+
+ if (errors) {
+ dprintf("%s %s\n", my_name,
+ "dropping data_request because of previous paging errors");
+ (void) memory_object_data_error(reply_to,
+ offset, vm_page_size,
+ KERN_FAILURE);
+ goto done;
+ }
+
+ if (offset >= ds->dpager.limit)
+ rc = PAGER_ERROR;
+ else
+ rc = default_read(&ds->dpager, dpt->dpt_buffer,
+ vm_page_size, offset,
+ &addr, protection_required & VM_PROT_WRITE,
+ ds->external);
+
+ switch (rc) {
+ case PAGER_SUCCESS:
+ if (addr != dpt->dpt_buffer) {
+ /*
+ * Deallocates data buffer
+ */
+ (void) memory_object_data_supply(
+ reply_to, offset,
+ addr, vm_page_size, TRUE,
+ VM_PROT_NONE,
+ FALSE, MACH_PORT_NULL);
+ } else {
+ (void) memory_object_data_supply(
+ reply_to, offset,
+ addr, vm_page_size, FALSE,
+ VM_PROT_NONE,
+ FALSE, MACH_PORT_NULL);
+ }
+ break;
+
+ case PAGER_ABSENT:
+ (void) memory_object_data_unavailable(
+ reply_to,
+ offset,
+ vm_page_size);
+ break;
+
+ case PAGER_ERROR:
+ (void) memory_object_data_error(
+ reply_to,
+ offset,
+ vm_page_size,
+ KERN_FAILURE);
+ break;
+ }
+
+ default_pager_pagein_count++;
+
+ done:
+ pager_port_finish_read(ds);
+ return(KERN_SUCCESS);
+}
+
+/*
+ * memory_object_data_initialize: check whether we already have each page, and
+ * write it if we do not. The implementation is far from optimized, and
+ * also assumes that the default_pager is single-threaded.
+ */
+kern_return_t
+seqnos_memory_object_data_initialize(ds, seqno, pager_request,
+ offset, addr, data_cnt)
+ default_pager_t ds;
+ mach_port_seqno_t seqno;
+ mach_port_t pager_request;
+ register
+ vm_offset_t offset;
+ register
+ pointer_t addr;
+ vm_size_t data_cnt;
+{
+ vm_offset_t amount_sent;
+ static char here[] = "%sdata_initialize";
+
+#ifdef lint
+ pager_request++;
+#endif /* lint */
+
+ if (ds == DEFAULT_PAGER_NULL)
+ panic(here,my_name);
+ddprintf ("seqnos_memory_object_data_initialize <%p>: pager_port_lock: <%p>[s:%d,r:%d,w:%d,l:%d], %d\n",
+ &ds, ds, ds->seqno, ds->readers, ds->writers, ds->lock.held, seqno);
+ pager_port_lock(ds, seqno);
+ pager_port_check_request(ds, pager_request);
+ pager_port_start_write(ds);
+ddprintf ("seqnos_memory_object_data_initialize <%p>: pager_port_unlock: <%p>[s:%d,r:%d,w:%d,l:%d]\n",
+ &ds, ds, ds->seqno, ds->readers, ds->writers, ds->lock.held);
+ pager_port_unlock(ds);
+
+ for (amount_sent = 0;
+ amount_sent < data_cnt;
+ amount_sent += vm_page_size) {
+
+ if (!default_has_page(&ds->dpager, offset + amount_sent)) {
+ if (default_write(&ds->dpager,
+ addr + amount_sent,
+ vm_page_size,
+ offset + amount_sent)
+ != PAGER_SUCCESS) {
+ dprintf("%s%s write error\n", my_name, here);
+ dstruct_lock(ds);
+ ds->errors++;
+ dstruct_unlock(ds);
+ }
+ }
+ }
+
+ pager_port_finish_write(ds);
+ if (vm_deallocate(default_pager_self, addr, data_cnt) != KERN_SUCCESS)
+ panic(here,my_name);
+
+ return(KERN_SUCCESS);
+}
+
+/*
+ * memory_object_data_write: split up the stuff coming in from
+ * a memory_object_data_write call
+ * into individual pages and pass them off to default_write.
+ */
+kern_return_t
+seqnos_memory_object_data_write(ds, seqno, pager_request,
+ offset, addr, data_cnt)
+ default_pager_t ds;
+ mach_port_seqno_t seqno;
+ mach_port_t pager_request;
+ register
+ vm_offset_t offset;
+ register
+ pointer_t addr;
+ vm_size_t data_cnt;
+{
+ register
+ vm_size_t amount_sent;
+ static char here[] = "%sdata_write";
+ int err;
+
+#ifdef lint
+ pager_request++;
+#endif /* lint */
+
+ if ((data_cnt % vm_page_size) != 0)
+ panic(here,my_name);
+
+ if (ds == DEFAULT_PAGER_NULL)
+ panic(here,my_name);
+
+ pager_port_lock(ds, seqno);
+ pager_port_start_write(ds);
+
+ vm_size_t limit = ds->dpager.byte_limit;
+ pager_port_unlock(ds);
+ if ((limit != round_page(limit)) && (trunc_page(limit) == offset)) {
+ assert(trunc_page(limit) == offset);
+ assert(data_cnt == vm_page_size);
+
+ vm_offset_t tail = addr + limit - trunc_page(limit);
+ vm_size_t tail_size = round_page(limit) - limit;
+ memset((void *) tail, 0, tail_size);
+
+ memory_object_data_supply(pager_request, trunc_page(limit), addr,
+ vm_page_size, TRUE, VM_PROT_NONE,
+ TRUE, MACH_PORT_NULL);
+ dstruct_lock(ds);
+ ds->dpager.byte_limit = round_page(limit);
+ dstruct_unlock(ds);
+ pager_port_finish_write(ds);
+
+ return(KERN_SUCCESS);
+ }
+
+ for (amount_sent = 0;
+ amount_sent < data_cnt;
+ amount_sent += vm_page_size) {
+
+ int result;
+
+ result = default_write(&ds->dpager,
+ addr + amount_sent,
+ vm_page_size,
+ offset + amount_sent);
+ if (result != KERN_SUCCESS) {
+ dstruct_lock(ds);
+ ds->errors++;
+ dstruct_unlock(ds);
+ }
+ default_pager_pageout_count++;
+ }
+
+ pager_port_finish_write(ds);
+ err = vm_deallocate(default_pager_self, addr, data_cnt);
+ if (err != KERN_SUCCESS)
+ {
+ panic(here,my_name);
+ }
+
+ return(KERN_SUCCESS);
+}
+
+/*ARGSUSED*/
+kern_return_t
+seqnos_memory_object_copy(old_memory_object, seqno, old_memory_control,
+ offset, length, new_memory_object)
+ default_pager_t old_memory_object;
+ mach_port_seqno_t seqno;
+ memory_object_control_t
+ old_memory_control;
+ vm_offset_t offset;
+ vm_size_t length;
+ memory_object_t new_memory_object;
+{
+ panic("%scopy", my_name);
+ return KERN_FAILURE;
+}
+
+/* We get this when our memory_object_lock_request has completed
+ after we truncated an object. */
+kern_return_t
+seqnos_memory_object_lock_completed (default_pager_t ds,
+ mach_port_seqno_t seqno,
+ mach_port_t pager_request,
+ vm_offset_t offset,
+ vm_size_t length)
+{
+ panic("%slock_completed",my_name);
+ return KERN_FAILURE;
+}
+
+kern_return_t
+seqnos_memory_object_data_unlock(pager, seqno, pager_request,
+ offset, length, protection_required)
+ default_pager_t pager;
+ mach_port_seqno_t seqno;
+ mach_port_t pager_request;
+ vm_offset_t offset;
+ vm_size_t length;
+ vm_prot_t protection_required;
+{
+ panic("%sdata_unlock",my_name);
+ return(KERN_FAILURE);
+}
+
+kern_return_t
+seqnos_memory_object_supply_completed(ds, seqno, pager_request,
+ offset, length,
+ result, error_offset)
+ default_pager_t ds;
+ mach_port_seqno_t seqno;
+ mach_port_t pager_request;
+ vm_offset_t offset;
+ vm_size_t length;
+ kern_return_t result;
+ vm_offset_t error_offset;
+{
+ panic("%ssupply_completed",my_name);
+ return(KERN_FAILURE);
+}
+
+/*
+ * memory_object_data_return: split up the stuff coming in from
+ * a memory_object_data_write call
+ * into individual pages and pass them off to default_write.
+ */
+kern_return_t
+seqnos_memory_object_data_return(ds, seqno, pager_request,
+ offset, addr, data_cnt,
+ dirty, kernel_copy)
+ default_pager_t ds;
+ mach_port_seqno_t seqno;
+ mach_port_t pager_request;
+ vm_offset_t offset;
+ pointer_t addr;
+ vm_size_t data_cnt;
+ boolean_t dirty;
+ boolean_t kernel_copy;
+{
+
+ return seqnos_memory_object_data_write (ds, seqno, pager_request,
+ offset, addr, data_cnt);
+}
+
+kern_return_t
+seqnos_memory_object_change_completed(ds, seqno, may_cache, copy_strategy)
+ default_pager_t ds;
+ mach_port_seqno_t seqno;
+ boolean_t may_cache;
+ memory_object_copy_strategy_t copy_strategy;
+{
+ panic("%schange_completed",my_name);
+ return(KERN_FAILURE);
+}
+
+
+boolean_t default_pager_notify_server(in, out)
+ mach_msg_header_t *in, *out;
+{
+ mach_no_senders_notification_t *n =
+ (mach_no_senders_notification_t *) in;
+
+ /*
+ * The only send-once rights we create are for
+ * receiving no-more-senders notifications.
+ * Hence, if we receive a message directed to
+ * a send-once right, we can assume it is
+ * a genuine no-senders notification from the kernel.
+ */
+
+ if ((n->not_header.msgh_bits !=
+ MACH_MSGH_BITS(0, MACH_MSG_TYPE_PORT_SEND_ONCE)) ||
+ (n->not_header.msgh_id != MACH_NOTIFY_NO_SENDERS))
+ return FALSE;
+
+ assert(n->not_header.msgh_size == sizeof *n);
+ assert(n->not_header.msgh_remote_port == MACH_PORT_NULL);
+
+ assert(n->not_type.msgt_name == MACH_MSG_TYPE_INTEGER_32);
+ assert(n->not_type.msgt_size == 32);
+ assert(n->not_type.msgt_number == 1);
+ assert(n->not_type.msgt_inline);
+ assert(! n->not_type.msgt_longform);
+
+ default_pager_no_senders(n->not_header.msgh_local_port,
+ n->not_header.msgh_seqno, n->not_count);
+
+ out->msgh_remote_port = MACH_PORT_NULL;
+ return TRUE;
+}
+
+extern boolean_t seqnos_memory_object_default_server();
+extern boolean_t default_pager_server();
+extern boolean_t exc_server();
+extern boolean_t bootstrap_server();
+extern void bootstrap_compat();
+
+mach_msg_size_t default_pager_msg_size_object = 128;
+
+/* Fill in default response. */
+static void
+mig_reply_setup (
+ const mach_msg_header_t *in,
+ mach_msg_header_t *out)
+{
+ static const mach_msg_type_t RetCodeType = {
+ /* msgt_name = */ MACH_MSG_TYPE_INTEGER_32,
+ /* msgt_size = */ 32,
+ /* msgt_number = */ 1,
+ /* msgt_inline = */ TRUE,
+ /* msgt_longform = */ FALSE,
+ /* msgt_deallocate = */ FALSE,
+ /* msgt_unused = */ 0
+ };
+
+#define InP (in)
+#define OutP ((mig_reply_header_t *) out)
+ OutP->Head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InP->msgh_bits), 0);
+ OutP->Head.msgh_size = sizeof *OutP;
+ OutP->Head.msgh_remote_port = InP->msgh_remote_port;
+ OutP->Head.msgh_local_port = MACH_PORT_NULL;
+ OutP->Head.msgh_seqno = 0;
+ OutP->Head.msgh_id = InP->msgh_id + 100;
+ OutP->RetCodeType = RetCodeType;
+ OutP->RetCode = MIG_BAD_ID;
+#undef InP
+#undef OutP
+}
+
+boolean_t
+default_pager_demux_object(in, out)
+ mach_msg_header_t *in;
+ mach_msg_header_t *out;
+{
+ /*
+ * We receive memory_object_data_initialize messages in
+ * the memory_object_default interface.
+ */
+
+ int rval = FALSE;
+ ddprintf ("DPAGER DEMUX OBJECT <%p>: %d\n", in, in->msgh_id);
+ mig_reply_setup (in, out);
+
+ mig_routine_t routine;
+ if ((routine = seqnos_memory_object_server_routine (in)) ||
+ (routine = seqnos_memory_object_default_server_routine (in)) ||
+ (routine = NULL, default_pager_notify_server (in, out)) ||
+ (routine = default_pager_server_routine (in)))
+ {
+ if (routine)
+ (*routine) (in, out);
+ rval = TRUE;
+ }
+
+ ddprintf ("DPAGER DEMUX OBJECT DONE <%p>: %d\n", in, in->msgh_id);
+ return rval;
+}
+
+mach_msg_size_t default_pager_msg_size_default = 8 * 1024;
+
+boolean_t
+default_pager_demux_default(in, out)
+ mach_msg_header_t *in;
+ mach_msg_header_t *out;
+{
+ if (in->msgh_local_port == default_pager_default_port) {
+ /*
+ * We receive memory_object_create messages in
+ * the memory_object_default interface.
+ */
+
+int rval;
+ddprintf ("DPAGER DEMUX DEFAULT <%p>: %d\n", in, in->msgh_id);
+rval =
+ (seqnos_memory_object_default_server(in, out) ||
+ default_pager_server(in, out));
+ddprintf ("DPAGER DEMUX DEFAULT DONE <%p>: %d\n", in, in->msgh_id);
+return rval;
+ } else if (in->msgh_local_port == default_pager_exception_port) {
+ /*
+ * We receive exception messages for
+ * ourself and the startup task.
+ */
+
+ return exc_server(in, out);
+ } else {
+ panic(my_name);
+ return FALSE;
+ }
+}
+
+/*
+ * We use multiple threads, for two reasons.
+ *
+ * First, memory objects created by default_pager_object_create
+ * are "external", instead of "internal". This means the kernel
+ * sends data (memory_object_data_write) to the object pageable.
+ * To prevent deadlocks, the external and internal objects must
+ * be managed by different threads.
+ *
+ * Second, the default pager uses synchronous IO operations.
+ * Spreading requests across multiple threads should
+ * recover some of the performance loss from synchronous IO.
+ *
+ * We have 3+ threads.
+ * One receives memory_object_create and
+ * default_pager_object_create requests.
+ * One or more manage internal objects.
+ * One or more manage external objects.
+ */
+
+void
+default_pager_thread_privileges()
+{
+ /*
+ * Set thread privileges.
+ */
+ wire_thread(); /* grab a kernel stack and memory allocation
+ privileges */
+}
+
+void *
+default_pager_default_thread(void *arg)
+{
+ kern_return_t kr;
+ default_pager_thread_privileges ();
+ for (;;) {
+ kr = mach_msg_server(default_pager_demux_default,
+ default_pager_msg_size_default,
+ default_pager_default_set);
+ panic(my_name, kr);
+ }
+}
+
+
+
+void *
+default_pager_thread(void *arg)
+{
+ mach_port_t pset;
+ kern_return_t kr;
+
+ dpt = (default_pager_thread_t *) arg;
+
+ /*
+ * Threads handling external objects cannot have
+ * privileges. Otherwise a burst of data-requests for an
+ * external object could empty the free-page queue,
+ * because the fault code only reserves real pages for
+ * requests sent to internal objects.
+ */
+
+ if (dpt->dpt_internal) {
+ default_pager_thread_privileges();
+ pset = default_pager_internal_set;
+ } else {
+ pset = default_pager_external_set;
+ }
+
+ for (;;) {
+ kr = mach_msg_server(default_pager_demux_object,
+ default_pager_msg_size_object,
+ pset);
+ panic(my_name, kr);
+ }
+}
+
+void
+start_default_pager_thread(internal)
+ boolean_t internal;
+{
+ default_pager_thread_t *ndpt;
+ kern_return_t kr;
+ error_t err;
+
+ ndpt = (default_pager_thread_t *) kalloc(sizeof *ndpt);
+ if (ndpt == 0)
+ panic(my_name);
+
+ ndpt->dpt_internal = internal;
+
+ kr = vm_allocate(default_pager_self, &ndpt->dpt_buffer,
+ vm_page_size, TRUE);
+ if (kr != KERN_SUCCESS)
+ panic(my_name);
+ wire_memory(ndpt->dpt_buffer, vm_page_size,
+ VM_PROT_READ|VM_PROT_WRITE);
+
+ err = pthread_create(&ndpt->dpt_thread, NULL, default_pager_thread,
+ ndpt);
+ if (!err)
+ pthread_detach (ndpt->dpt_thread);
+ else {
+ errno = err;
+ perror ("pthread_create");
+ }
+}
+
+void
+default_pager_initialize(host_port)
+ mach_port_t host_port;
+{
+ memory_object_t DMM;
+ kern_return_t kr;
+
+ /*
+ * This task will become the default pager.
+ */
+ default_pager_self = mach_task_self();
+
+ /*
+ * Initialize the "default pager" port.
+ */
+ kr = mach_port_allocate(default_pager_self, MACH_PORT_RIGHT_RECEIVE,
+ &default_pager_default_port);
+ if (kr != KERN_SUCCESS)
+ panic(my_name);
+
+ DMM = default_pager_default_port;
+ kr = vm_set_default_memory_manager(host_port, &DMM);
+ if ((kr != KERN_SUCCESS) || MACH_PORT_VALID(DMM))
+ panic(my_name);
+
+ /*
+ * Initialize the exception port.
+ */
+ kr = mach_port_allocate(default_pager_self, MACH_PORT_RIGHT_RECEIVE,
+ &default_pager_exception_port);
+ if (kr != KERN_SUCCESS)
+ panic(my_name);
+
+ /*
+ * Arrange for wiring privileges.
+ */
+ wire_setup(host_port);
+
+ /*
+ * Find out how many CPUs we have, to determine the number
+ * of threads to create.
+ */
+ if (default_pager_internal_count == 0) {
+ host_basic_info_data_t h_info;
+ natural_t h_info_count;
+
+ h_info_count = HOST_BASIC_INFO_COUNT;
+ (void) host_info(host_port, HOST_BASIC_INFO,
+ (host_info_t)&h_info, &h_info_count);
+
+ /*
+ * Random computation to get more parallelism on
+ * multiprocessors.
+ */
+ default_pager_internal_count =
+ (h_info.avail_cpus > 32 ? 32 : h_info.avail_cpus) / 4 + 3;
+ }
+}
+
+/*
+ * Initialize and Run the default pager
+ */
+void
+default_pager()
+{
+ kern_return_t kr;
+ int i;
+
+ default_pager_thread_privileges();
+
+ /*
+ * Wire down code, data, stack
+ */
+ wire_all_memory();
+
+
+ /*
+ * Initialize the list of all pagers.
+ */
+ pager_port_list_init();
+
+ kr = mach_port_allocate(default_pager_self, MACH_PORT_RIGHT_PORT_SET,
+ &default_pager_internal_set);
+ if (kr != KERN_SUCCESS)
+ panic(my_name);
+
+ kr = mach_port_allocate(default_pager_self, MACH_PORT_RIGHT_PORT_SET,
+ &default_pager_external_set);
+ if (kr != KERN_SUCCESS)
+ panic(my_name);
+
+ kr = mach_port_allocate(default_pager_self, MACH_PORT_RIGHT_PORT_SET,
+ &default_pager_default_set);
+ if (kr != KERN_SUCCESS)
+ panic(my_name);
+
+ kr = mach_port_move_member(default_pager_self,
+ default_pager_default_port,
+ default_pager_default_set);
+ if (kr != KERN_SUCCESS)
+ panic(my_name);
+
+ kr = mach_port_move_member(default_pager_self,
+ default_pager_exception_port,
+ default_pager_default_set);
+ if (kr != KERN_SUCCESS)
+ panic(my_name);
+
+ /*
+ * Now we create the threads that will actually
+ * manage objects.
+ */
+
+ for (i = 0; i < default_pager_internal_count; i++)
+ start_default_pager_thread(TRUE);
+
+ for (i = 0; i < default_pager_external_count; i++)
+ start_default_pager_thread(FALSE);
+
+ default_pager_default_thread(0); /* Become the default_pager server */
+#if 0
+ cthread_fork (default_pager_default_thread, 0);
+ /* cthread_exit (cthread_self ()); */
+ thread_suspend (mach_thread_self ());
+#endif
+}
+
+/*
+ * Create an external object.
+ */
+kern_return_t
+S_default_pager_object_create (mach_port_t pager,
+ mach_port_t *mem_obj,
+ vm_size_t size)
+{
+ default_pager_t ds;
+ mach_port_t port;
+ kern_return_t result;
+
+ if (pager != default_pager_default_port)
+ return KERN_INVALID_ARGUMENT;
+
+ ds = pager_port_alloc(size);
+ result = mach_port_allocate (default_pager_self,
+ MACH_PORT_RIGHT_RECEIVE,
+ &port);
+ if (result != KERN_SUCCESS)
+ {
+ kfree ((char *) ds, sizeof *ds);
+ return result;
+ }
+
+ /*
+ * Set up associations between these ports
+ * and this default_pager structure
+ */
+
+ ds->pager = port;
+ ds->dpager.limit = size;
+ pager_port_list_insert(port, ds);
+ default_pager_add(ds, FALSE);
+
+ *mem_obj = port;
+ return (KERN_SUCCESS);
+}
+
+kern_return_t
+S_default_pager_info (mach_port_t pager,
+ default_pager_info_t *infop)
+{
+ vm_size_t total, free;
+
+ if (pager != default_pager_default_port)
+ return KERN_INVALID_ARGUMENT;
+
+ pthread_mutex_lock(&all_partitions.lock);
+ paging_space_info(&total, &free);
+ pthread_mutex_unlock(&all_partitions.lock);
+
+ infop->dpi_total_space = ptoa(total);
+ infop->dpi_free_space = ptoa(free);
+ infop->dpi_page_size = vm_page_size;
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+S_default_pager_objects (mach_port_t pager,
+ default_pager_object_array_t *objectsp,
+ natural_t *ocountp,
+ mach_port_array_t *portsp,
+ natural_t *pcountp)
+{
+ vm_offset_t oaddr = 0; /* memory for objects */
+ vm_size_t osize = 0; /* current size */
+ default_pager_object_t *objects;
+ natural_t opotential;
+
+ vm_offset_t paddr = 0; /* memory for ports */
+ vm_size_t psize = 0; /* current size */
+ mach_port_t *ports;
+ natural_t ppotential;
+
+ unsigned int actual;
+ unsigned int num_pagers;
+ kern_return_t kr;
+ default_pager_t entry;
+
+ if (pager != default_pager_default_port)
+ return KERN_INVALID_ARGUMENT;
+
+ /* start with the inline memory */
+
+ num_pagers = 0;
+
+ objects = *objectsp;
+ opotential = *ocountp;
+
+ ports = *portsp;
+ ppotential = *pcountp;
+
+ pthread_mutex_lock(&all_pagers.lock);
+ /*
+ * We will send no more than this many
+ */
+ actual = all_pagers.htable.nr_items;
+ pthread_mutex_unlock(&all_pagers.lock);
+
+ if (opotential < actual) {
+ vm_offset_t newaddr;
+ vm_size_t newsize;
+
+ newsize = 2 * round_page(actual * sizeof *objects);
+
+ kr = vm_allocate(default_pager_self, &newaddr, newsize, TRUE);
+ if (kr != KERN_SUCCESS)
+ goto nomemory;
+
+ oaddr = newaddr;
+ osize = newsize;
+ opotential = osize/sizeof *objects;
+ objects = (default_pager_object_t *) oaddr;
+ }
+
+ if (ppotential < actual) {
+ vm_offset_t newaddr;
+ vm_size_t newsize;
+
+ newsize = 2 * round_page(actual * sizeof *ports);
+
+ kr = vm_allocate(default_pager_self, &newaddr, newsize, TRUE);
+ if (kr != KERN_SUCCESS)
+ goto nomemory;
+
+ paddr = newaddr;
+ psize = newsize;
+ ppotential = psize/sizeof *ports;
+ ports = (mach_port_t *) paddr;
+ }
+
+ /*
+ * Now scan the list.
+ */
+
+ pthread_mutex_lock(&all_pagers.lock);
+
+ num_pagers = 0;
+ HURD_IHASH_ITERATE (&all_pagers.htable, val) {
+ entry = (default_pager_t) val;
+
+ mach_port_t port;
+ vm_size_t size;
+
+ if ((num_pagers >= opotential) ||
+ (num_pagers >= ppotential)) {
+ /*
+ * This should be rare. In any case,
+ * we will only miss recent objects,
+ * because they are added at the end.
+ */
+ break;
+ }
+
+ /*
+ * Avoid interfering with normal operations
+ */
+ if (pthread_mutex_trylock(&entry->dpager.lock))
+ goto not_this_one;
+ size = pager_allocated(&entry->dpager);
+ pthread_mutex_unlock(&entry->dpager.lock);
+
+ dstruct_lock(entry);
+
+ port = entry->pager_name;
+ if (port == MACH_PORT_NULL) {
+ /*
+ * The object is waiting for no-senders
+ * or memory_object_init.
+ */
+ dstruct_unlock(entry);
+ goto not_this_one;
+ }
+
+ /*
+ * We need a reference for the reply message.
+ * While we are unlocked, the bucket queue
+ * can change and the object might be terminated.
+ * memory_object_terminate will wait for us,
+ * preventing deallocation of the entry.
+ */
+
+ if (--entry->name_refs == 0) {
+ dstruct_unlock(entry);
+
+ /* keep the list locked, wont take long */
+
+ kr = mach_port_mod_refs(default_pager_self,
+ port, MACH_PORT_RIGHT_SEND,
+ default_pager_max_urefs);
+ if (kr != KERN_SUCCESS)
+ panic("%sdefault_pager_objects",my_name);
+
+ dstruct_lock(entry);
+
+ entry->name_refs += default_pager_max_urefs;
+ pager_port_finish_refs(entry);
+ }
+ dstruct_unlock(entry);
+
+ /* the arrays are wired, so no deadlock worries */
+
+ objects[num_pagers].dpo_object = (vm_offset_t) entry;
+ objects[num_pagers].dpo_size = size;
+ ports [num_pagers++] = port;
+ continue;
+not_this_one:
+ /*
+ * Do not return garbage
+ */
+ objects[num_pagers].dpo_object = (vm_offset_t) 0;
+ objects[num_pagers].dpo_size = 0;
+ ports [num_pagers++] = MACH_PORT_NULL;
+
+ }
+
+ pthread_mutex_unlock(&all_pagers.lock);
+
+ /*
+ * Deallocate and clear unused memory.
+ * (Returned memory will automagically become pageable.)
+ */
+
+ if (objects == *objectsp) {
+ /*
+ * Our returned information fit inline.
+ * Nothing to deallocate.
+ */
+
+ *ocountp = num_pagers;
+ } else if (actual == 0) {
+ (void) vm_deallocate(default_pager_self, oaddr, osize);
+
+ /* return zero items inline */
+ *ocountp = 0;
+ } else {
+ vm_offset_t used;
+
+ used = round_page(actual * sizeof *objects);
+
+ if (used != osize)
+ (void) vm_deallocate(default_pager_self,
+ oaddr + used, osize - used);
+
+ *objectsp = objects;
+ *ocountp = num_pagers;
+ }
+
+ if (ports == *portsp) {
+ /*
+ * Our returned information fit inline.
+ * Nothing to deallocate.
+ */
+
+ *pcountp = num_pagers;
+ } else if (actual == 0) {
+ (void) vm_deallocate(default_pager_self, paddr, psize);
+
+ /* return zero items inline */
+ *pcountp = 0;
+ } else {
+ vm_offset_t used;
+
+ used = round_page(actual * sizeof *ports);
+
+ if (used != psize)
+ (void) vm_deallocate(default_pager_self,
+ paddr + used, psize - used);
+
+ *portsp = ports;
+ *pcountp = num_pagers;
+ }
+
+ return KERN_SUCCESS;
+
+ nomemory:
+
+ {
+ int i;
+ for (i = 0; i < num_pagers; i++)
+ (void) mach_port_deallocate(default_pager_self, ports[i]);
+ }
+
+ if (objects != *objectsp)
+ (void) vm_deallocate(default_pager_self, oaddr, osize);
+
+ if (ports != *portsp)
+ (void) vm_deallocate(default_pager_self, paddr, psize);
+
+ return KERN_RESOURCE_SHORTAGE;
+}
+
+
+kern_return_t
+S_default_pager_object_pages (mach_port_t pager,
+ mach_port_t object,
+ default_pager_page_array_t *pagesp,
+ natural_t *countp)
+{
+ vm_offset_t addr = 0; /* memory for page offsets */
+ vm_size_t size = 0; /* current memory size */
+ default_pager_page_t *pages;
+ natural_t potential, actual;
+ kern_return_t kr;
+
+ if (pager != default_pager_default_port)
+ return KERN_INVALID_ARGUMENT;
+
+ /* we start with the inline space */
+
+ pages = *pagesp;
+ potential = *countp;
+
+ for (;;) {
+ default_pager_t entry;
+
+ pthread_mutex_lock(&all_pagers.lock);
+ HURD_IHASH_ITERATE (&all_pagers.htable, val) {
+ entry = (default_pager_t) val;
+ dstruct_lock(entry);
+ if (entry->pager_name == object) {
+ pthread_mutex_unlock(&all_pagers.lock);
+ goto found_object;
+ }
+ dstruct_unlock(entry);
+ }
+ pthread_mutex_unlock(&all_pagers.lock);
+
+ /* did not find the object */
+
+ if (pages != *pagesp)
+ (void) vm_deallocate(default_pager_self, addr, size);
+ return KERN_INVALID_ARGUMENT;
+
+ found_object:
+
+ if (pthread_mutex_trylock(&entry->dpager.lock)) {
+ /* oh well bad luck */
+
+ dstruct_unlock(entry);
+
+ /* yield the processor */
+ (void) thread_switch(MACH_PORT_NULL,
+ SWITCH_OPTION_NONE, 0);
+ continue;
+ }
+
+ actual = pager_pages(&entry->dpager, pages, potential);
+ pthread_mutex_unlock(&entry->dpager.lock);
+ dstruct_unlock(entry);
+
+ if (actual <= potential)
+ break;
+
+ /* allocate more memory */
+
+ if (pages != *pagesp)
+ (void) vm_deallocate(default_pager_self, addr, size);
+ size = round_page(actual * sizeof *pages);
+ kr = vm_allocate(default_pager_self, &addr, size, TRUE);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ pages = (default_pager_page_t *) addr;
+ potential = size/sizeof *pages;
+ }
+
+ /*
+ * Deallocate and clear unused memory.
+ * (Returned memory will automagically become pageable.)
+ */
+
+ if (pages == *pagesp) {
+ /*
+ * Our returned information fit inline.
+ * Nothing to deallocate.
+ */
+
+ *countp = actual;
+ } else if (actual == 0) {
+ (void) vm_deallocate(default_pager_self, addr, size);
+
+ /* return zero items inline */
+ *countp = 0;
+ } else {
+ vm_offset_t used;
+
+ used = round_page(actual * sizeof *pages);
+
+ if (used != size)
+ (void) vm_deallocate(default_pager_self,
+ addr + used, size - used);
+
+ *pagesp = pages;
+ *countp = actual;
+ }
+ return KERN_SUCCESS;
+}
+
+
+kern_return_t
+S_default_pager_object_set_size (default_pager_t ds,
+ mach_port_seqno_t seqno,
+ vm_size_t limit)
+{
+ kern_return_t kr = KERN_SUCCESS;
+
+ if (ds == DEFAULT_PAGER_NULL)
+ return KERN_INVALID_ARGUMENT;
+
+ pager_port_lock(ds, seqno);
+ pager_port_wait_for_readers(ds);
+ pager_port_wait_for_writers(ds);
+
+ vm_size_t rounded_limit = round_page (limit);
+ vm_size_t trunc_limit = trunc_page (limit);
+
+
+ if (ds->dpager.limit < rounded_limit)
+ {
+ /* The limit has not been exceeded heretofore. Just change it. */
+ ds->dpager.limit = rounded_limit;
+
+ /* Byte limit is used for truncation of file, which aren't rounded to
+ page boundary. But by enlarging of file we are free to increase this value*/
+ ds->dpager.byte_limit = rounded_limit;
+ kr = memory_object_lock_request(ds->pager_request, 0,
+ rounded_limit,
+ MEMORY_OBJECT_RETURN_NONE, FALSE,
+ VM_PROT_NONE, MACH_PORT_NULL);
+ if (kr != KERN_SUCCESS)
+ panic ("memory_object_lock_request: %d", kr);
+ }
+ else
+ {
+ if (ds->dpager.limit != rounded_limit)
+ {
+ kr = memory_object_lock_request(ds->pager_request, rounded_limit,
+ ds->dpager.limit - rounded_limit,
+ MEMORY_OBJECT_RETURN_NONE, TRUE,
+ VM_PROT_ALL, MACH_PORT_NULL);
+ if (kr != KERN_SUCCESS)
+ panic ("memory_object_lock_request: %d", kr);
+
+ ds->dpager.limit = rounded_limit;
+ }
+
+ /* Deallocate the old backing store pages and shrink the page map. */
+ if (ds->dpager.size > ds->dpager.limit / vm_page_size)
+ pager_truncate (&ds->dpager, ds->dpager.limit / vm_page_size);
+
+ /* If memory object size isn't page aligned, fill the tail
+ of last page with zeroes */
+ if ((limit != rounded_limit) && (ds->dpager.limit > limit))
+ {
+ /* Clean part of last page which isn't part of file.
+ For file sizes that aren't multiple of vm_page_size */
+ ds->dpager.byte_limit = limit;
+ kr = memory_object_lock_request(ds->pager_request, trunc_limit,
+ vm_page_size,
+ MEMORY_OBJECT_RETURN_ALL, TRUE,
+ VM_PROT_NONE, MACH_PORT_NULL);
+ }
+ }
+
+ pager_port_unlock(ds);
+
+ return kr;
+}
+
+/*
+ * Add/remove extra paging space
+ */
+
+extern mach_port_t bootstrap_master_device_port;
+extern mach_port_t bootstrap_master_host_port;
+
+kern_return_t
+S_default_pager_paging_file (pager, mdport, file_name, add)
+ mach_port_t pager;
+ mach_port_t mdport;
+ default_pager_filename_t file_name;
+ boolean_t add;
+{
+ kern_return_t kr;
+
+ if (pager != default_pager_default_port)
+ return KERN_INVALID_ARGUMENT;
+
+#if 0
+dprintf("bmd %x md %x\n", bootstrap_master_device_port, mdport);
+#endif
+ if (add) {
+ kr = add_paging_file(bootstrap_master_device_port,
+ file_name, 0);
+ } else {
+ kr = remove_paging_file(file_name);
+ }
+
+ /* XXXX more code needed */
+ if (mdport != bootstrap_master_device_port)
+ mach_port_deallocate( mach_task_self(), mdport);
+
+ return kr;
+}
+
+kern_return_t
+default_pager_register_fileserver(pager, fileserver)
+ mach_port_t pager;
+ mach_port_t fileserver;
+{
+ if (pager != default_pager_default_port)
+ return KERN_INVALID_ARGUMENT;
+#if notyet
+ mach_port_deallocate(mach_task_self(), fileserver);
+ if (0) dp_helper_paging_space(0,0,0);/*just linkit*/
+#endif
+ return KERN_SUCCESS;
+}
+
+/*
+ * When things do not quite workout...
+ */
+void no_paging_space(out_of_memory)
+ boolean_t out_of_memory;
+{
+ static char here[] = "%s *** NOT ENOUGH PAGING SPACE ***";
+
+ if (out_of_memory)
+ dprintf("*** OUT OF MEMORY *** ");
+ panic(here, my_name);
+}
+
+void overcommitted(got_more_space, space)
+ boolean_t got_more_space;
+ vm_size_t space; /* in pages */
+{
+ vm_size_t pages_free, pages_total;
+
+ static boolean_t user_warned = FALSE;
+ static vm_size_t pages_shortage = 0;
+
+ paging_space_info(&pages_total, &pages_free);
+
+ /*
+ * If user added more space, see if it is enough
+ */
+ if (got_more_space) {
+ pages_free -= pages_shortage;
+ if (pages_free > 0) {
+ pages_shortage = 0;
+ if (user_warned)
+ dprintf("%s paging space ok now.\n", my_name);
+ } else
+ pages_shortage = pages_free;
+ user_warned = FALSE;
+ return;
+ }
+ /*
+ * We ran out of gas, let user know.
+ */
+ pages_free -= space;
+ pages_shortage = (pages_free > 0) ? 0 : -pages_free;
+ if (!user_warned && pages_shortage) {
+ user_warned = TRUE;
+ dprintf("%s paging space over-committed.\n", my_name);
+ }
+#if debug
+ user_warned = FALSE;
+ dprintf("%s paging space over-committed [+%d (%d) pages].\n",
+ my_name, space, pages_shortage);
+#endif
+}
+
+void paging_space_info(totp, freep)
+ vm_size_t *totp, *freep;
+{
+ vm_size_t total, free;
+ partition_t part;
+ int i;
+
+ total = free = 0;
+ for (i = 0; i < all_partitions.n_partitions; i++) {
+
+ if ((part = partition_of(i)) == 0) continue;
+
+ /* no need to lock: by the time this data
+ gets back to any remote requestor it
+ will be obsolete anyways */
+ total += part->total_size;
+ free += part->free;
+#if debug
+ dprintf("Partition %d: x%x total, x%x free\n",
+ i, part->total_size, part->free);
+#endif
+ }
+ *totp = total;
+ *freep = free;
+}
+
+/*
+ * Catch exceptions.
+ */
+
+kern_return_t
+catch_exception_raise(exception_port, thread, task, exception, code, subcode)
+ mach_port_t exception_port;
+ mach_port_t thread, task;
+ int exception, code, subcode;
+{
+ ddprintf ("(default_pager)catch_exception_raise(%d,%d,%d)\n",
+ exception, code, subcode);
+ panic(my_name);
+
+ /* mach_msg_server will deallocate thread/task for us */
+
+ return KERN_FAILURE;
+}
diff --git a/mach-defpager/default_pager.h b/mach-defpager/default_pager.h
new file mode 100644
index 00000000..f4cdda64
--- /dev/null
+++ b/mach-defpager/default_pager.h
@@ -0,0 +1,43 @@
+/* Backing store access callbacks for Hurd version of Mach default pager.
+ Copyright (C) 2012 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Prototypes for working with paging partitions and files */
+
+#ifndef _DEFAULT_PAGER_H_
+#define _DEFAULT_PAGER_H_
+
+#include <file_io.h>
+
+void partition_init();
+
+void create_paging_partition(const char *name, struct file_direct *fdp,
+ int isa_file, int linux_signature);
+kern_return_t destroy_paging_partition(char *name, void **pp_private);
+
+kern_return_t add_paging_file(mach_port_t master_device_port,
+ char *file_name, int linux_signature);
+kern_return_t remove_paging_file (char *file_name);
+
+void paging_space_info(vm_size_t *totp, vm_size_t *freep);
+void no_paging_space(boolean_t out_of_memory);
+void overcommitted(boolean_t got_more_space, vm_size_t space);
+
+void panic (const char *fmt, ...);
+
+#endif /* _DEFAULT_PAGER_H_ */
diff --git a/mach-defpager/file_io.h b/mach-defpager/file_io.h
new file mode 100644
index 00000000..2721412b
--- /dev/null
+++ b/mach-defpager/file_io.h
@@ -0,0 +1,69 @@
+/* Backing store access callbacks for Hurd version of Mach default pager.
+ Copyright (C) 2000 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _file_io_h
+#define _file_io_h 1
+
+/* The original Mach default pager code used in serverboot can read
+ filesystem meta-data to find the blocks used by paging files.
+ We replace those interfaces with simpler code that only supports
+ subsets of devices represented by a list of runs a la libstore. */
+
+#include <sys/types.h>
+
+#include <device/device_types.h>
+#include <device/device.h>
+
+/* A run of device records, expressed in the device's record size. */
+struct storage_run
+{
+ recnum_t start, length;
+};
+
+struct file_direct
+{
+ mach_port_t device;
+
+ int bshift; /* size of device records (disk blocks) */
+ size_t fd_bsize; /* log2 of that */
+ recnum_t fd_size; /* number of blocks total */
+
+ /* The paging area consists of the concatentation of NRUNS contiguous
+ regions of the device, as described by RUNS. */
+ size_t nruns;
+ struct storage_run runs[0];
+};
+
+/* These are in fact only called to read or write a single page, from
+ default_pager.c::default_read/default_write. The SIZE argument is
+ always vm_page_size and OFFSET is always page-aligned. */
+
+int page_read_file_direct (struct file_direct *fdp,
+ vm_offset_t offset,
+ vm_size_t size,
+ vm_offset_t *addr, /* out */
+ vm_size_t *size_read); /* out */
+int page_write_file_direct(struct file_direct *fdp,
+ vm_offset_t offset,
+ vm_offset_t addr,
+ vm_size_t size,
+ vm_size_t *size_written); /* out */
+
+
+#endif /* file_io.h */
diff --git a/mach-defpager/kalloc.c b/mach-defpager/kalloc.c
new file mode 100644
index 00000000..2f8f002e
--- /dev/null
+++ b/mach-defpager/kalloc.c
@@ -0,0 +1,297 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1987 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: kern/kalloc.c
+ * Author: Avadis Tevanian, Jr.
+ * Date: 1985
+ *
+ * General kernel memory allocator. This allocator is designed
+ * to be used by the kernel to manage dynamic memory fast.
+ */
+
+#include <mach.h>
+#include <pthread.h> /* for spin locks */
+#include <malloc.h> /* for malloc_hook/free_hook */
+
+#include "wiring.h"
+
+static void init_hook (void);
+static void *malloc_hook (size_t size, const void *caller);
+static void free_hook (void *ptr, const void *caller);
+
+/* GNU libc 2.14 defines this macro to declare hook variables as volatile.
+ Define it as empty for older libc versions. */
+#ifndef __MALLOC_HOOK_VOLATILE
+# define __MALLOC_HOOK_VOLATILE
+#endif
+
+void (*__MALLOC_HOOK_VOLATILE __malloc_initialize_hook) (void) = init_hook;
+
+
+/* #define DEBUG */
+
+/*
+ * All allocations of size less than kalloc_max are rounded to the
+ * next highest power of 2.
+ */
+vm_size_t kalloc_max; /* max before we use vm_allocate */
+#define MINSIZE 4 /* minimum allocation size */
+
+struct free_list {
+ pthread_spinlock_t lock;
+ vm_offset_t head; /* head of free list */
+#ifdef DEBUG
+ int count;
+#endif /*DEBUG*/
+};
+
+#define KLIST_MAX 13
+ /* sizes: 4, 8, 16, 32, 64,
+ 128, 256, 512, 1024,
+ 2048, 4096, 8192, 16384 */
+struct free_list kfree_list[KLIST_MAX];
+
+pthread_spinlock_t kget_space_lock;
+vm_offset_t kalloc_next_space = 0;
+vm_offset_t kalloc_end_of_space = 0;
+
+vm_size_t kalloc_wasted_space = 0;
+
+boolean_t kalloc_initialized = FALSE;
+
+/*
+ * Initialize the memory allocator. This should be called only
+ * once on a system wide basis (i.e. first processor to get here
+ * does the initialization).
+ *
+ * This initializes all of the zones.
+ */
+
+void kalloc_init(void)
+{
+ int i;
+
+ /*
+ * Support free lists for items up to vm_page_size or
+ * 16Kbytes, whichever is less.
+ */
+
+ if (vm_page_size > 16*1024)
+ kalloc_max = 16*1024;
+ else
+ kalloc_max = vm_page_size;
+
+ for (i = 0; i < KLIST_MAX; i++) {
+ pthread_spin_init(&kfree_list[i].lock, PTHREAD_PROCESS_PRIVATE);
+ kfree_list[i].head = 0;
+ }
+ pthread_spin_init(&kget_space_lock, PTHREAD_PROCESS_PRIVATE);
+
+ /*
+ * Do not allocate memory at address 0.
+ */
+ kalloc_next_space = vm_page_size;
+ kalloc_end_of_space = vm_page_size;
+}
+
+/*
+ * Contiguous space allocator for items of less than a page size.
+ */
+vm_offset_t kget_space(vm_offset_t size)
+{
+ vm_size_t space_to_add = 0;
+ vm_offset_t new_space = 0;
+ vm_offset_t addr;
+
+ pthread_spin_lock(&kget_space_lock);
+ while (kalloc_next_space + size > kalloc_end_of_space) {
+ /*
+ * Add at least one page to allocation area.
+ */
+ space_to_add = round_page(size);
+
+ if (new_space == 0) {
+ /*
+ * Unlock and allocate memory.
+ * Try to make it contiguous with the last
+ * allocation area.
+ */
+ pthread_spin_unlock(&kget_space_lock);
+
+ new_space = kalloc_end_of_space;
+ if (vm_map(mach_task_self(),
+ &new_space, space_to_add, (vm_offset_t) 0, TRUE,
+ MEMORY_OBJECT_NULL, (vm_offset_t) 0, FALSE,
+ VM_PROT_DEFAULT, VM_PROT_ALL, VM_INHERIT_DEFAULT)
+ != KERN_SUCCESS)
+ return 0;
+ wire_memory(new_space, space_to_add,
+ VM_PROT_READ|VM_PROT_WRITE);
+ pthread_spin_lock(&kget_space_lock);
+ continue;
+ }
+
+ /*
+ * Memory was allocated in a previous iteration.
+ * Check whether the new region is contiguous with the
+ * old one.
+ */
+ if (new_space != kalloc_end_of_space) {
+ /*
+ * Throw away the remainder of the old space,
+ * and start a new one.
+ */
+ kalloc_wasted_space +=
+ kalloc_end_of_space - kalloc_next_space;
+ kalloc_next_space = new_space;
+ }
+ kalloc_end_of_space = new_space + space_to_add;
+
+ new_space = 0;
+ }
+
+ addr = kalloc_next_space;
+ kalloc_next_space += size;
+ pthread_spin_unlock(&kget_space_lock);
+
+ if (new_space != 0)
+ (void) vm_deallocate(mach_task_self(), new_space, space_to_add);
+
+ return addr;
+}
+
+void *kalloc(vm_size_t size)
+{
+ vm_size_t allocsize;
+ vm_offset_t addr;
+ struct free_list *fl;
+
+ if (!kalloc_initialized) {
+ kalloc_init();
+ kalloc_initialized = TRUE;
+ }
+
+ /* compute the size of the block that we will actually allocate */
+
+ allocsize = size;
+ if (size < kalloc_max) {
+ allocsize = MINSIZE;
+ fl = kfree_list;
+ while (allocsize < size) {
+ allocsize <<= 1;
+ fl++;
+ }
+ }
+
+ /*
+ * If our size is still small enough, check the queue for that size
+ * and allocate.
+ */
+
+ if (allocsize < kalloc_max) {
+ pthread_spin_lock(&fl->lock);
+ if ((addr = fl->head) != 0) {
+ fl->head = *(vm_offset_t *)addr;
+#ifdef DEBUG
+ fl->count--;
+#endif
+ pthread_spin_unlock(&fl->lock);
+ }
+ else {
+ pthread_spin_unlock(&fl->lock);
+ addr = kget_space(allocsize);
+ }
+ }
+ else {
+ if (vm_allocate(mach_task_self(), &addr, allocsize, TRUE)
+ != KERN_SUCCESS)
+ addr = 0;
+ }
+ return (void *) addr;
+}
+
+void
+kfree( void *data,
+ vm_size_t size)
+{
+ vm_size_t freesize;
+ struct free_list *fl;
+
+ freesize = size;
+ if (size < kalloc_max) {
+ freesize = MINSIZE;
+ fl = kfree_list;
+ while (freesize < size) {
+ freesize <<= 1;
+ fl++;
+ }
+ }
+
+ if (freesize < kalloc_max) {
+ pthread_spin_lock(&fl->lock);
+ *(vm_offset_t *)data = fl->head;
+ fl->head = (vm_offset_t) data;
+#ifdef DEBUG
+ fl->count++;
+#endif
+ pthread_spin_unlock(&fl->lock);
+ }
+ else {
+ (void) vm_deallocate(mach_task_self(), (vm_offset_t)data, freesize);
+ }
+}
+
+static void
+init_hook (void)
+{
+ __malloc_hook = malloc_hook;
+ __free_hook = free_hook;
+}
+
+static void *
+malloc_hook (size_t size, const void *caller)
+{
+ return (void *) kalloc ((vm_size_t) size);
+}
+
+static void
+free_hook (void *ptr, const void *caller)
+{
+ /* Just ignore harmless attempts at cleanliness. */
+ /* panic("free not implemented"); */
+}
+
+void malloc_fork_prepare()
+{
+}
+
+void malloc_fork_parent()
+{
+}
+
+void malloc_fork_child()
+{
+}
diff --git a/mach-defpager/kalloc.h b/mach-defpager/kalloc.h
new file mode 100644
index 00000000..8f52f1a5
--- /dev/null
+++ b/mach-defpager/kalloc.h
@@ -0,0 +1,30 @@
+/* Backing store access callbacks for Hurd version of Mach default pager.
+ Copyright (C) 2012 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * General kernel memory allocator.
+ */
+
+#ifndef _KALLOC_H_
+#define _KALLOC_H_
+
+void *kalloc (vm_size_t size);
+void kfree (void *data, vm_size_t size);
+
+#endif /* _KALLOC_H_ */
diff --git a/mach-defpager/main.c b/mach-defpager/main.c
new file mode 100644
index 00000000..e33c2b33
--- /dev/null
+++ b/mach-defpager/main.c
@@ -0,0 +1,185 @@
+/* Main program for standalone Hurd version of Mach default pager.
+ Copyright (C) 1999, 2001 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+
+#include <mach.h>
+#include <hurd.h>
+#include <pthread.h>
+#include <device/device.h>
+#include <device/device_types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <error.h>
+#include <signal.h>
+#include <string.h>
+
+/* XXX */
+#include <fcntl.h>
+#include <paths.h>
+#include <errno.h>
+#include <unistd.h>
+#include <hurd.h>
+#include <hurd/port.h>
+#include <hurd/fd.h>
+/* XXX */
+
+#include "default_pager.h"
+
+mach_port_t bootstrap_master_device_port; /* local name */
+mach_port_t bootstrap_master_host_port; /* local name */
+
+extern void default_pager();
+extern void default_pager_initialize();
+extern void default_pager_setup();
+
+/* initialized in default_pager_initialize */
+extern mach_port_t default_pager_exception_port;
+
+
+static void
+printf_init (device_t master)
+{
+ mach_port_t cons;
+ kern_return_t rc;
+ rc = device_open (master, D_READ|D_WRITE, "console", &cons);
+ if (rc)
+ error (2, rc, "cannot open kernel console device");
+ stdin = mach_open_devstream (cons, "r");
+ stdout = stderr = mach_open_devstream (cons, "w");
+ mach_port_deallocate (mach_task_self (), cons);
+ setbuf (stdout, 0);
+}
+
+
+int debug;
+
+static void
+nohandler (int sig)
+{ }
+
+int
+main (int argc, char **argv)
+{
+ const task_t my_task = mach_task_self();
+ error_t err;
+ memory_object_t defpager;
+
+ err = get_privileged_ports (&bootstrap_master_host_port,
+ &bootstrap_master_device_port);
+ if (err)
+ error (1, err, "cannot get privileged ports");
+
+ defpager = MACH_PORT_NULL;
+ err = vm_set_default_memory_manager (bootstrap_master_host_port, &defpager);
+ if (err)
+ error (1, err, "cannot check current default memory manager");
+ if (MACH_PORT_VALID (defpager))
+ error (2, 0, "Another default memory manager is already running");
+
+ if (!(argc == 2 && !strcmp (argv[1], "-d")))
+ {
+ /* We don't use the `daemon' function because we might exit back to the
+ parent before the daemon has completed vm_set_default_memory_manager.
+ Instead, the parent waits for a SIGUSR1 from the child before
+ exitting, and the child sends that signal after it is set up. */
+ sigset_t set;
+ signal (SIGUSR1, nohandler);
+ sigemptyset (&set);
+ sigaddset (&set, SIGUSR1);
+ sigprocmask (SIG_BLOCK, &set, 0);
+ switch (fork ())
+ {
+ case -1:
+ error (1, errno, "cannot become daemon");
+ case 0:
+ setsid ();
+ chdir ("/");
+ close (0);
+ close (1);
+ close (2);
+ break;
+ default:
+ sigemptyset (&set);
+ sigsuspend (&set);
+ _exit (0);
+ }
+ }
+
+ /* Mark us as important. */
+ mach_port_t proc = getproc ();
+ if (proc == MACH_PORT_NULL)
+ error (3, err, "cannot get a handle to our process");
+
+ err = proc_mark_important (proc);
+ /* This might fail due to permissions or because the old proc server
+ is still running, ignore any such errors. */
+ if (err && err != EPERM && err != EMIG_BAD_ID)
+ error (3, err, "cannot mark us as important");
+
+ mach_port_deallocate (mach_task_self (), proc);
+
+ printf_init(bootstrap_master_device_port);
+
+ /*
+ * Set up the default pager.
+ */
+ partition_init();
+
+ /*
+ * task_set_exception_port and task_set_bootstrap_port
+ * both require a send right.
+ */
+ (void) mach_port_insert_right(my_task, default_pager_exception_port,
+ default_pager_exception_port,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ /*
+ * Change our exception port.
+ */
+ if (!debug)
+ (void) task_set_exception_port(my_task, default_pager_exception_port);
+
+ default_pager_initialize (bootstrap_master_host_port);
+
+ if (!(argc == 2 && !strcmp (argv[1], "-d")))
+ kill (getppid (), SIGUSR1);
+
+ /*
+ * Become the default pager
+ */
+ default_pager();
+ /*NOTREACHED*/
+ return -1;
+}
+
+
+void
+panic (const char *fmt, ...)
+{
+ va_list ap;
+ fprintf (stderr, "%s: panic: ", program_invocation_name);
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ exit (3);
+}
diff --git a/mach-defpager/mig-decls.h b/mach-defpager/mig-decls.h
new file mode 100644
index 00000000..8118d61a
--- /dev/null
+++ b/mach-defpager/mig-decls.h
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Justus Winter.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __MACH_DEFPAGER_MIG_DECLS_H__
+#define __MACH_DEFPAGER_MIG_DECLS_H__
+
+#include "priv.h"
+
+/* Called by server stub functions. */
+
+static inline struct dstruct * __attribute__ ((unused))
+begin_using_default_pager (mach_port_t port)
+{
+ return (default_pager_t) hurd_ihash_find (&all_pagers.htable,
+ (hurd_ihash_key_t) port);
+}
+
+#endif /* __MACH_DEFPAGER_MIG_DECLS_H__ */
diff --git a/mach-defpager/mig-mutate.h b/mach-defpager/mig-mutate.h
new file mode 100644
index 00000000..54aeeba6
--- /dev/null
+++ b/mach-defpager/mig-mutate.h
@@ -0,0 +1,22 @@
+/*
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Justus Winter.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#define MEMORY_OBJECT_INTRAN default_pager_t begin_using_default_pager (mach_port_t)
+#define MEMORY_OBJECT_IMPORTS import "mig-decls.h";
+#define DEFAULT_PAGER_IMPORTS import "mig-decls.h";
diff --git a/mach-defpager/priv.h b/mach-defpager/priv.h
new file mode 100644
index 00000000..36845657
--- /dev/null
+++ b/mach-defpager/priv.h
@@ -0,0 +1,200 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#ifndef __MACH_DEFPAGER_PRIV_H__
+#define __MACH_DEFPAGER_PRIV_H__
+
+#include <mach.h>
+#include <queue.h>
+#include <hurd/ihash.h>
+
+/*
+ * Bitmap allocation.
+ */
+typedef unsigned int bm_entry_t;
+#define NB_BM 32
+#define BM_MASK 0xffffffff
+
+#define howmany(a,b) (((a) + (b) - 1)/(b))
+
+/*
+ * Value to indicate no block assigned
+ */
+#define NO_BLOCK ((vm_offset_t)-1)
+
+/*
+ * 'Partition' structure for each paging area.
+ * Controls allocation of blocks within paging area.
+ */
+struct part {
+ pthread_mutex_t p_lock; /* for bitmap/free */
+ vm_size_t total_size; /* total number of blocks */
+ vm_size_t free; /* number of blocks free */
+ unsigned int id; /* named lookup */
+ bm_entry_t *bitmap; /* allocation map */
+ boolean_t going_away; /* destroy attempt in progress */
+ struct file_direct *file; /* file paged to */
+};
+typedef struct part *partition_t;
+
+struct {
+ pthread_mutex_t lock;
+ int n_partitions;
+ partition_t *partition_list;/* array, for quick mapping */
+} all_partitions; /* list of all such */
+
+typedef unsigned char p_index_t;
+
+#define P_INDEX_INVALID ((p_index_t)-1)
+
+#define no_partition(x) ((x) == P_INDEX_INVALID)
+
+/*
+ * Allocation info for each paging object.
+ *
+ * Most operations, even pager_write_offset and pager_put_checksum,
+ * just need a read lock. Higher-level considerations prevent
+ * conflicting operations on a single page. The lock really protects
+ * the underlying size and block map memory, so pager_extend needs a
+ * write lock.
+ *
+ * An object can now span multiple paging partitions. The allocation
+ * info we keep is a pair (offset,p_index) where the index is in the
+ * array of all partition ptrs, and the offset is partition-relative.
+ * Size wise we are doing ok fitting the pair into a single integer:
+ * the offset really is in pages so we have vm_page_size bits available
+ * for the partition index.
+ */
+#define DEBUG_READER_CONFLICTS 0
+
+#if DEBUG_READER_CONFLICTS
+int default_pager_read_conflicts = 0;
+#endif
+
+union dp_map {
+
+ struct {
+ unsigned int p_offset : 24,
+ p_index : 8;
+ } block;
+
+ union dp_map *indirect;
+};
+typedef union dp_map *dp_map_t;
+
+/* quick check for part==block==invalid */
+#define no_block(e) ((e).indirect == (dp_map_t)NO_BLOCK)
+#define invalidate_block(e) ((e).indirect = (dp_map_t)NO_BLOCK)
+
+struct dpager {
+ pthread_mutex_t lock; /* lock for extending block map */
+ /* XXX should be read-write lock */
+#if DEBUG_READER_CONFLICTS
+ int readers;
+ boolean_t writer;
+#endif
+ dp_map_t map; /* block map */
+ vm_size_t size; /* size of paging object, in pages */
+ vm_size_t limit; /* limit (bytes) allowed to grow to */
+ vm_size_t byte_limit; /* limit, which wasn't
+ rounded to page boundary */
+ p_index_t cur_partition;
+#ifdef CHECKSUM
+ vm_offset_t *checksum; /* checksum - parallel to block map */
+#define NO_CHECKSUM ((vm_offset_t)-1)
+#endif /* CHECKSUM */
+};
+typedef struct dpager *dpager_t;
+
+/*
+ * A paging object uses either a one- or a two-level map of offsets
+ * into a paging partition.
+ */
+#define PAGEMAP_ENTRIES 64
+ /* number of pages in a second-level map */
+#define PAGEMAP_SIZE(npgs) ((npgs)*sizeof(vm_offset_t))
+
+#define INDIRECT_PAGEMAP_ENTRIES(npgs) \
+ ((((npgs)-1)/PAGEMAP_ENTRIES) + 1)
+#define INDIRECT_PAGEMAP_SIZE(npgs) \
+ (INDIRECT_PAGEMAP_ENTRIES(npgs) * sizeof(vm_offset_t *))
+#define INDIRECT_PAGEMAP(size) \
+ (size > PAGEMAP_ENTRIES)
+
+#define ROUNDUP_TO_PAGEMAP(npgs) \
+ (((npgs) + PAGEMAP_ENTRIES - 1) & ~(PAGEMAP_ENTRIES - 1))
+
+/*
+ * Mapping between pager port and paging object.
+ */
+struct dstruct {
+ hurd_ihash_locp_t htable_locp; /* for the ihash table */
+ queue_chain_t links; /* Link in pager-port list */
+
+ pthread_mutex_t lock; /* Lock for the structure */
+ pthread_cond_t
+ waiting_seqno, /* someone waiting on seqno */
+ waiting_read, /* someone waiting on readers */
+ waiting_write, /* someone waiting on writers */
+ waiting_refs; /* someone waiting on refs */
+
+ memory_object_t pager; /* Pager port */
+ mach_port_seqno_t seqno; /* Pager port sequence number */
+ mach_port_t pager_request; /* Request port */
+ mach_port_urefs_t request_refs; /* Request port user-refs */
+ mach_port_t pager_name; /* Name port */
+ mach_port_urefs_t name_refs; /* Name port user-refs */
+ boolean_t external; /* Is an external object? */
+
+ unsigned int readers; /* Reads in progress */
+ unsigned int writers; /* Writes in progress */
+
+ /* This is the reply port of an outstanding
+ default_pager_object_set_size call. */
+ mach_port_t lock_request;
+
+ unsigned int errors; /* Pageout error count */
+ struct dpager dpager; /* Actual pager */
+};
+typedef struct dstruct * default_pager_t;
+#define DEFAULT_PAGER_NULL ((default_pager_t)0)
+
+/*
+ * List of all pagers. A specific pager is
+ * found directly via its port, this list is
+ * only used for monitoring purposes by the
+ * default_pager_object* calls
+ */
+struct pager_port {
+ struct hurd_ihash htable;
+ pthread_mutex_t lock;
+ queue_head_t leak_queue;
+};
+
+/* The list of pagers. */
+extern struct pager_port all_pagers;
+
+#endif /* __MACH_DEFPAGER_PRIV_H__ */
diff --git a/mach-defpager/queue.h b/mach-defpager/queue.h
new file mode 100644
index 00000000..17ea7e29
--- /dev/null
+++ b/mach-defpager/queue.h
@@ -0,0 +1,316 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon rights
+ * to redistribute these changes.
+ */
+/*
+ * File: queue.h
+ * Author: Avadis Tevanian, Jr.
+ * Date: 1985
+ *
+ * Type definitions for generic queues.
+ *
+ */
+
+#ifndef _QUEUE_H_
+#define _QUEUE_H_
+
+/*
+ * Queue of abstract objects. Queue is maintained
+ * within that object.
+ *
+ * Supports fast removal from within the queue.
+ *
+ * How to declare a queue of elements of type "foo_t":
+ * In the "*foo_t" type, you must have a field of
+ * type "queue_chain_t" to hold together this queue.
+ * There may be more than one chain through a
+ * "foo_t", for use by different queues.
+ *
+ * Declare the queue as a "queue_t" type.
+ *
+ * Elements of the queue (of type "foo_t", that is)
+ * are referred to by reference, and cast to type
+ * "queue_entry_t" within this module.
+ */
+
+/*
+ * A generic doubly-linked list (queue).
+ */
+
+struct queue_entry {
+ struct queue_entry *next; /* next element */
+ struct queue_entry *prev; /* previous element */
+};
+
+typedef struct queue_entry *queue_t;
+typedef struct queue_entry queue_head_t;
+typedef struct queue_entry queue_chain_t;
+typedef struct queue_entry *queue_entry_t;
+
+/*
+ * Macro: queue_init
+ * Function:
+ * Initialize the given queue.
+ * Header:
+ * void queue_init(q)
+ * queue_t q; / * MODIFIED * /
+ */
+#define queue_init(q) ((q)->next = (q)->prev = q)
+
+/*
+ * Macro: queue_first
+ * Function:
+ * Returns the first entry in the queue,
+ * Header:
+ * queue_entry_t queue_first(q)
+ * queue_t q; / * IN * /
+ */
+#define queue_first(q) ((q)->next)
+
+/*
+ * Macro: queue_next
+ * Function:
+ * Returns the entry after an item in the queue.
+ * Header:
+ * queue_entry_t queue_next(qc)
+ * queue_t qc;
+ */
+#define queue_next(qc) ((qc)->next)
+
+/*
+ * Macro: queue_last
+ * Function:
+ * Returns the last entry in the queue.
+ * Header:
+ * queue_entry_t queue_last(q)
+ * queue_t q; / * IN * /
+ */
+#define queue_last(q) ((q)->prev)
+
+/*
+ * Macro: queue_prev
+ * Function:
+ * Returns the entry before an item in the queue.
+ * Header:
+ * queue_entry_t queue_prev(qc)
+ * queue_t qc;
+ */
+#define queue_prev(qc) ((qc)->prev)
+
+/*
+ * Macro: queue_end
+ * Function:
+ * Tests whether a new entry is really the end of
+ * the queue.
+ * Header:
+ * boolean_t queue_end(q, qe)
+ * queue_t q;
+ * queue_entry_t qe;
+ */
+#define queue_end(q, qe) ((q) == (qe))
+
+/*
+ * Macro: queue_empty
+ * Function:
+ * Tests whether a queue is empty.
+ * Header:
+ * boolean_t queue_empty(q)
+ * queue_t q;
+ */
+#define queue_empty(q) queue_end((q), queue_first(q))
+
+
+/*----------------------------------------------------------------*/
+/*
+ * Macros that operate on generic structures. The queue
+ * chain may be at any location within the structure, and there
+ * may be more than one chain.
+ */
+
+/*
+ * Macro: queue_enter
+ * Function:
+ * Insert a new element at the tail of the queue.
+ * Header:
+ * void queue_enter(q, elt, type, field)
+ * queue_t q;
+ * <type> elt;
+ * <type> is what's in our queue
+ * <field> is the chain field in (*<type>)
+ */
+#define queue_enter(head, elt, type, field) \
+{ \
+ queue_entry_t prev; \
+ \
+ prev = (head)->prev; \
+ if ((head) == prev) { \
+ (head)->next = (queue_entry_t) (elt); \
+ } \
+ else { \
+ ((type)prev)->field.next = (queue_entry_t)(elt);\
+ } \
+ (elt)->field.prev = prev; \
+ (elt)->field.next = head; \
+ (head)->prev = (queue_entry_t) elt; \
+}
+
+/*
+ * Macro: queue_enter_first
+ * Function:
+ * Insert a new element at the head of the queue.
+ * Header:
+ * void queue_enter_first(q, elt, type, field)
+ * queue_t q;
+ * <type> elt;
+ * <type> is what's in our queue
+ * <field> is the chain field in (*<type>)
+ */
+#define queue_enter_first(head, elt, type, field) \
+{ \
+ queue_entry_t next; \
+ \
+ next = (head)->next; \
+ if ((head) == next) { \
+ (head)->prev = (queue_entry_t) (elt); \
+ } \
+ else { \
+ ((type)next)->field.prev = (queue_entry_t)(elt);\
+ } \
+ (elt)->field.next = next; \
+ (elt)->field.prev = head; \
+ (head)->next = (queue_entry_t) elt; \
+}
+
+/*
+ * Macro: queue_field [internal use only]
+ * Function:
+ * Find the queue_chain_t (or queue_t) for the
+ * given element (thing) in the given queue (head)
+ */
+#define queue_field(head, thing, type, field) \
+ (((head) == (thing)) ? (head) : &((type)(thing))->field)
+
+/*
+ * Macro: queue_remove
+ * Function:
+ * Remove an arbitrary item from the queue.
+ * Header:
+ * void queue_remove(q, qe, type, field)
+ * arguments as in queue_enter
+ */
+#define queue_remove(head, elt, type, field) \
+{ \
+ queue_entry_t next, prev; \
+ \
+ next = (elt)->field.next; \
+ prev = (elt)->field.prev; \
+ \
+ if ((head) == next) \
+ (head)->prev = prev; \
+ else \
+ ((type)next)->field.prev = prev; \
+ \
+ if ((head) == prev) \
+ (head)->next = next; \
+ else \
+ ((type)prev)->field.next = next; \
+}
+
+/*
+ * Macro: queue_remove_first
+ * Function:
+ * Remove and return the entry at the head of
+ * the queue.
+ * Header:
+ * queue_remove_first(head, entry, type, field)
+ * entry is returned by reference
+ */
+#define queue_remove_first(head, entry, type, field) \
+{ \
+ queue_entry_t next; \
+ \
+ (entry) = (type) ((head)->next); \
+ next = (entry)->field.next; \
+ \
+ if ((head) == next) \
+ (head)->prev = (head); \
+ else \
+ ((type)(next))->field.prev = (head); \
+ (head)->next = next; \
+}
+
+/*
+ * Macro: queue_remove_last
+ * Function:
+ * Remove and return the entry at the tail of
+ * the queue.
+ * Header:
+ * queue_remove_last(head, entry, type, field)
+ * entry is returned by reference
+ */
+#define queue_remove_last(head, entry, type, field) \
+{ \
+ queue_entry_t prev; \
+ \
+ (entry) = (type) ((head)->prev); \
+ prev = (entry)->field.prev; \
+ \
+ if ((head) == prev) \
+ (head)->next = (head); \
+ else \
+ ((type)(prev))->field.next = (head); \
+ (head)->prev = prev; \
+}
+
+/*
+ * Macro: queue_assign
+ */
+#define queue_assign(to, from, type, field) \
+{ \
+ ((type)((from)->prev))->field.next = (to); \
+ ((type)((from)->next))->field.prev = (to); \
+ *to = *from; \
+}
+
+/*
+ * Macro: queue_iterate
+ * Function:
+ * iterate over each item in the queue.
+ * Generates a 'for' loop, setting elt to
+ * each item in turn (by reference).
+ * Header:
+ * queue_iterate(q, elt, type, field)
+ * queue_t q;
+ * <type> elt;
+ * <type> is what's in our queue
+ * <field> is the chain field in (*<type>)
+ */
+#define queue_iterate(head, elt, type, field) \
+ for ((elt) = (type) queue_first(head); \
+ !queue_end((head), (queue_entry_t)(elt)); \
+ (elt) = (type) queue_next(&(elt)->field))
+
+
+
+#endif /* _QUEUE_H_ */
diff --git a/mach-defpager/setup.c b/mach-defpager/setup.c
new file mode 100644
index 00000000..e4ec8d96
--- /dev/null
+++ b/mach-defpager/setup.c
@@ -0,0 +1,307 @@
+/* Backing store access callbacks for Hurd version of Mach default pager.
+
+ Copyright (C) 2001, 2002, 2007, 2010 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <stddef.h>
+#include <assert.h>
+#include <mach.h>
+#include <string.h>
+#include <strings.h>
+
+#include "default_pager.h"
+#include "kalloc.h"
+
+#include "file_io.h"
+#include "default_pager_S.h"
+
+/* This should be in some system header... XXX */
+int page_aligned (vm_offset_t num)
+{
+ return trunc_page (num) == num;
+}
+
+extern mach_port_t default_pager_default_port; /* default_pager.c */
+
+kern_return_t
+S_default_pager_paging_storage (mach_port_t pager,
+ mach_port_t device,
+ recnum_t *runs, mach_msg_type_number_t nrun,
+ default_pager_filename_t name,
+ boolean_t add)
+{
+ struct file_direct *fdp;
+ int sizes[DEV_GET_RECORDS_COUNT];
+ natural_t count;
+ mach_msg_type_number_t i;
+ error_t err;
+ recnum_t devsize;
+
+ if (pager != default_pager_default_port)
+ return KERN_INVALID_ARGUMENT;
+
+ if (! add)
+ return remove_paging_file (name); /* XXX ? */
+
+ if (nrun < 2 || nrun % 2 != 0)
+ return EINVAL;
+
+ count = DEV_GET_RECORDS_COUNT;
+ err = device_get_status (device, DEV_GET_RECORDS, sizes, &count);
+ if (err)
+ return err;
+ if (count < DEV_GET_RECORDS_COUNT || sizes[DEV_GET_RECORDS_RECORD_SIZE] <= 0)
+ return EINVAL;
+ devsize = sizes[DEV_GET_RECORDS_DEVICE_RECORDS];
+
+ if (vm_page_size % sizes[DEV_GET_RECORDS_RECORD_SIZE] != 0)
+ /* We can't write disk blocks larger than pages. */
+ return EINVAL;
+
+ fdp = kalloc (offsetof (struct file_direct, runs[nrun]));
+ if (fdp == 0)
+ return ENOMEM;
+
+ fdp->device = device;
+ fdp->bshift = ffs (sizes[DEV_GET_RECORDS_RECORD_SIZE]) - 1;
+ fdp->fd_bsize = sizes[DEV_GET_RECORDS_RECORD_SIZE];
+ fdp->nruns = nrun / 2;
+ fdp->fd_size = 0;
+ for (i = 0; i < nrun; i += 2)
+ {
+ fdp->runs[i].start = runs[i];
+ fdp->runs[i].length = runs[i + 1];
+ if (fdp->runs[i].start + fdp->runs[i].length > devsize)
+ {
+ kfree (fdp, offsetof (struct file_direct, runs[nrun]));
+ return EINVAL;
+ }
+ fdp->fd_size += fdp->runs[i].length;
+ }
+
+ /* Now really do it. */
+ create_paging_partition (name, fdp, 0, -3);
+ return 0;
+}
+
+
+/* Called to read a page from backing store. */
+int
+page_read_file_direct (struct file_direct *fdp,
+ vm_offset_t offset,
+ vm_size_t size,
+ vm_offset_t *addr, /* out */
+ vm_size_t *size_read) /* out */
+{
+ struct storage_run *r;
+ error_t err;
+ char *readloc;
+ char *page;
+ mach_msg_type_number_t nread;
+
+ assert (page_aligned (offset));
+ assert (size == vm_page_size);
+
+ offset >>= fdp->bshift;
+
+ assert (offset + (size >> fdp->bshift) <= fdp->fd_size);
+
+ /* Find the run containing the beginning of the page. */
+ for (r = fdp->runs; offset > r->length; ++r)
+ offset -= r->length;
+
+ if (offset + (size >> fdp->bshift) <= r->length)
+ /* The first run contains the whole page. */
+ return device_read (fdp->device, 0, r->start + offset,
+ size, (char **) addr, size_read);
+
+ /* Read the first part of the run. */
+ err = device_read (fdp->device, 0, r->start + offset,
+ (r->length - offset) << fdp->bshift,
+ (char **) addr, &nread);
+ if (err)
+ return err;
+
+ size -= nread;
+ readloc = (char *) *addr;
+ do
+ {
+ readloc += nread;
+ offset += nread >> fdp->bshift;
+ if (offset > r->length)
+ offset -= r++->length;
+
+ /* We always get another out-of-line page, so we have to copy
+ out of that page and deallocate it. */
+ err = device_read (fdp->device, 0, r->start + offset,
+ (r->length - offset) << fdp->bshift,
+ &page, &nread);
+ if (err)
+ {
+ vm_deallocate (mach_task_self (),
+ (vm_address_t) *addr, vm_page_size);
+ return err;
+ }
+ memcpy (readloc, page, nread);
+ vm_deallocate (mach_task_self (), (vm_address_t) page, vm_page_size);
+ size -= nread;
+ } while (size > 0);
+
+ *size_read = vm_page_size;
+ return 0;
+}
+
+/* Called to write a page to backing store. */
+int
+page_write_file_direct(struct file_direct *fdp,
+ vm_offset_t offset,
+ vm_offset_t addr,
+ vm_size_t size,
+ vm_size_t *size_written) /* out */
+{
+ struct storage_run *r;
+ error_t err;
+ int wrote;
+
+ assert (page_aligned (offset));
+ assert (size == vm_page_size);
+
+ offset >>= fdp->bshift;
+
+ assert (offset + (size >> fdp->bshift) <= fdp->fd_size);
+
+ /* Find the run containing the beginning of the page. */
+ for (r = fdp->runs; offset > r->length; ++r)
+ offset -= r->length;
+
+ if (offset + (size >> fdp->bshift) <= r->length)
+ {
+ /* The first run contains the whole page. */
+ err = device_write (fdp->device, 0, r->start + offset,
+ (char *) addr, size, &wrote);
+ *size_written = wrote;
+ return err;
+ }
+
+ /* Write the first part of the run. */
+ err = device_write (fdp->device, 0,
+ r->start + offset, (char *) addr,
+ (r->length - offset) << fdp->bshift,
+ &wrote);
+ if (err)
+ return err;
+
+ size -= wrote;
+ do
+ {
+ mach_msg_type_number_t segsize;
+
+ addr += wrote;
+ offset += wrote >> fdp->bshift;
+ if (offset > r->length)
+ offset -= r++->length;
+
+ segsize = (r->length - offset) << fdp->bshift;
+ if (segsize > size)
+ segsize = size;
+ err = device_write (fdp->device, 0, r->start + offset,
+ (char *) addr, segsize, &wrote);
+ if (err)
+ {
+ vm_deallocate (mach_task_self (),
+ (vm_address_t) addr, vm_page_size);
+ return err;
+ }
+
+ size -= wrote;
+ } while (size > 0);
+
+ *size_written = vm_page_size;
+ return 0;
+}
+
+
+/* Compatibility entry points used by default_pager_paging_file RPC. */
+
+kern_return_t
+add_paging_file(master_device_port, file_name, linux_signature)
+ mach_port_t master_device_port;
+ char *file_name;
+ int linux_signature;
+{
+ error_t err;
+ mach_port_t dev;
+ int sizes[DEV_GET_SIZE_COUNT];
+ natural_t count;
+ char *devname = file_name;
+
+ assert (linux_signature == 0);
+
+ if (!strncmp (file_name, "/dev/", 5))
+ devname += 5;
+
+ err = device_open (master_device_port, D_READ|D_WRITE, devname, &dev);
+ if (err)
+ return err;
+
+ count = DEV_GET_SIZE_COUNT;
+ err = device_get_status (dev, DEV_GET_SIZE, sizes, &count);
+ if (!err && count < DEV_GET_SIZE_COUNT)
+ err = EGRATUITOUS;
+ if (err)
+ mach_port_deallocate (mach_task_self (), dev);
+ else
+ {
+ struct file_direct *fdp;
+ fdp = kalloc (offsetof (struct file_direct, runs[1]));
+ if (fdp == 0)
+ return ENOMEM;
+
+ fdp->device = dev;
+ fdp->fd_bsize = sizes[DEV_GET_SIZE_RECORD_SIZE];
+ fdp->bshift = ffs (sizes[DEV_GET_SIZE_RECORD_SIZE]) - 1;
+ fdp->fd_size = sizes[DEV_GET_SIZE_DEVICE_SIZE] >> fdp->bshift;
+ fdp->nruns = 1;
+ fdp->runs[0].start = 0;
+ fdp->runs[0].length = fdp->fd_size;
+
+ /* Now really do it. */
+ create_paging_partition (file_name, fdp, 0, 0);
+ }
+
+ return err;
+}
+
+/*
+ * Destroy a paging_partition given a file name
+ */
+kern_return_t
+remove_paging_file (char *file_name)
+{
+ struct file_direct *fdp = 0;
+ kern_return_t kr;
+
+ kr = destroy_paging_partition(file_name, (void **)&fdp);
+ if (kr == KERN_SUCCESS && fdp != 0)
+ {
+ mach_port_deallocate (mach_task_self (), fdp->device);
+ kfree (fdp, (char *) &fdp->runs[fdp->nruns] - (char *) fdp);
+ }
+ return kr;
+}
diff --git a/mach-defpager/wiring.c b/mach-defpager/wiring.c
new file mode 100644
index 00000000..883770f0
--- /dev/null
+++ b/mach-defpager/wiring.c
@@ -0,0 +1,176 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Package to wire current task's memory.
+ */
+#include <mach.h>
+#include <mach_init.h>
+#include <mach/machine/vm_param.h>
+#include "default_pager.h"
+
+mach_port_t this_task; /* our task */
+mach_port_t priv_host_port = MACH_PORT_NULL;
+ /* the privileged host port */
+
+void
+wire_setup(host_priv)
+ mach_port_t host_priv;
+{
+ priv_host_port = host_priv;
+ this_task = mach_task_self();
+}
+
+void
+wire_memory(start, size, prot)
+ vm_address_t start;
+ vm_size_t size;
+ vm_prot_t prot;
+{
+ kern_return_t kr;
+
+ if (priv_host_port == MACH_PORT_NULL)
+ return;
+
+ kr = vm_wire(priv_host_port,
+ this_task,
+ start, size, prot);
+ if (kr != KERN_SUCCESS)
+ panic("mem_wire: %d", kr);
+}
+
+void
+wire_thread()
+{
+ kern_return_t kr;
+
+ if (priv_host_port == MACH_PORT_NULL)
+ return;
+
+ kr = thread_wire(priv_host_port,
+ mach_thread_self(),
+ TRUE);
+ if (kr != KERN_SUCCESS)
+ panic("wire_thread: %d", kr);
+}
+
+void
+wire_all_memory()
+{
+ kern_return_t kr;
+ vm_offset_t address;
+ vm_size_t size;
+ vm_prot_t protection;
+ vm_prot_t max_protection;
+ vm_inherit_t inheritance;
+ boolean_t is_shared;
+ memory_object_name_t object;
+ vm_offset_t offset;
+
+ if (priv_host_port == MACH_PORT_NULL)
+ return;
+
+ /* iterate thru all regions, wiring */
+ address = 0;
+ while (
+ (kr = vm_region(this_task, &address,
+ &size,
+ &protection,
+ &max_protection,
+ &inheritance,
+ &is_shared,
+ &object,
+ &offset))
+ == KERN_SUCCESS)
+ {
+ if (MACH_PORT_VALID(object))
+ (void) mach_port_deallocate(this_task, object);
+ if (protection != VM_PROT_NONE)
+ {
+ /* The VM system cannot cope with a COW fault on another
+ unrelated virtual copy happening later when we have
+ wired down the original page. So we must touch all our
+ pages before wiring to make sure that only we will ever
+ use them. */
+ void *page;
+ if (!(protection & VM_PROT_WRITE))
+ {
+ kr = vm_protect(this_task, address, size,
+ 0, max_protection);
+ }
+ for (page = (void *) address;
+ page < (void *) (address + size);
+ page += vm_page_size)
+ *(volatile int *) page = *(int *) page;
+
+ wire_memory(address, size, protection);
+
+ if (!(protection & VM_PROT_WRITE))
+ {
+ kr = vm_protect(this_task, address, size,
+ 0, protection);
+ }
+ }
+ address += size;
+ }
+}
+
+/*
+ * Alias for vm_allocate to return wired memory.
+ */
+kern_return_t
+vm_allocate(task, address, size, anywhere)
+ task_t task;
+ vm_address_t *address;
+ vm_size_t size;
+ boolean_t anywhere;
+{
+ kern_return_t kr;
+
+ if (anywhere)
+ *address = VM_MIN_ADDRESS;
+ kr = vm_map(task,
+ address, size, (vm_offset_t) 0, anywhere,
+ MEMORY_OBJECT_NULL, (vm_offset_t)0, FALSE,
+ VM_PROT_DEFAULT, VM_PROT_ALL, VM_INHERIT_DEFAULT);
+ if (kr != KERN_SUCCESS)
+ return kr;
+
+ if (task == this_task)
+ (void) vm_wire(priv_host_port, task, *address, size,
+ VM_PROT_DEFAULT);
+ return KERN_SUCCESS;
+}
+
+/* Other versions of this function in libc... */
+kern_return_t
+__vm_allocate (task, address, size, anywhere)
+ task_t task;
+ vm_address_t *address;
+ vm_size_t size;
+ boolean_t anywhere;
+{
+ return vm_allocate (task, address, size, anywhere);
+}
diff --git a/mach-defpager/wiring.h b/mach-defpager/wiring.h
new file mode 100644
index 00000000..b5f8e53f
--- /dev/null
+++ b/mach-defpager/wiring.h
@@ -0,0 +1,35 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon the
+ * rights to redistribute these changes.
+ */
+/*
+ * Package to wire current task's memory.
+ */
+#include <mach.h>
+#include <mach_init.h>
+
+extern void wire_setup(/* mach_port_t host_priv */);
+extern void wire_memory(/* vm_address_t, vm_size_t, vm_prot_t */);
+extern void wire_thread();
+extern void wire_all_memory();
diff --git a/mkinstalldirs b/mkinstalldirs
new file mode 100755
index 00000000..ac412b20
--- /dev/null
+++ b/mkinstalldirs
@@ -0,0 +1,40 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Public domain
+
+# $Id: mkinstalldirs,v 1.1 1996/07/18 04:35:29 mib Exp $
+
+errstatus=0
+
+for file
+do
+ set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+ shift
+
+ pathcomp=
+ for d
+ do
+ pathcomp="$pathcomp$d"
+ case "$pathcomp" in
+ -* ) pathcomp=./$pathcomp ;;
+ esac
+
+ if test ! -d "$pathcomp"; then
+ echo "mkdir $pathcomp" 1>&2
+
+ mkdir "$pathcomp" || lasterr=$?
+
+ if test ! -d "$pathcomp"; then
+ errstatus=$lasterr
+ fi
+ fi
+
+ pathcomp="$pathcomp/"
+ done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
diff --git a/mount/mount.defs b/mount/mount.defs
new file mode 100644
index 00000000..7e97bb9b
--- /dev/null
+++ b/mount/mount.defs
@@ -0,0 +1,109 @@
+/* A server for keeping track of filesystems
+
+ Copyright (C) 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd/hurd_types.defs>
+
+subsystem mount 37000;
+
+import <hurd/mount_defs.h>;
+
+type mount_t = mach_port_t
+#ifdef MOUNT_INTRAN
+intran: MOUNT_INTRAN
+#endif
+#ifdef MOUNT_OUTTRAN
+outtran: MOUNT_OUTTRAN
+#endif
+#ifdef MOUNT_DESTRUCTOR
+destructor: MOUNT_DESTRUCTOR
+#endif
+;
+type mount_server_t = mach_port_t
+#ifdef MOUNT_SERVER_INTRAN
+intran: MOUNT_SERVER_INTRAN
+#endif
+#ifdef MOUNT_SERVER_OUTTRAN
+outtran: MOUNT_SERVER_OUTTRAN
+#endif
+#ifdef MOUNT_SERVER_DESTRUCTOR
+destructor: MOUNT_SERVER_DESTRUCTOR
+#endif
+;
+
+type mount_state_t = int ctype: mount_state_t;
+type mount_key_class_t = int ctype: mount_key_class_t;
+type mount_excl_t = int ctype: mount_excl_t;
+
+#ifdef MOUNT_IMPORTS
+MOUNT_IMPORTS
+#endif
+
+INTR_INTERFACE
+
+/* RPCs */
+
+/* Begin mounting the filesystem designated by KEY & KEY_CLASS; the mount is
+ initially assumed to have a mode of 0 (see mount_set_mode). EXCL is the
+ mount exclusion mode which applies to KEY (only the first EXCL argument
+ for a particular KEY is used). A mount reference is returned in MOUNT,
+ and the current state of that filesystem is returned in STATE (and may be
+ used to, e.g., cause the mount to be read-only if STATE is SUSPICIOUS). */
+routine mount_begin (
+ server : mount_server_t;
+ key : string_t;
+ key_class : mount_key_class_t;
+ excl : mount_excl_t;
+ out mount : mount_t;
+ out state : mount_state_t);
+
+/* Assert that MOUNT is no longer active. If CLEAN is true, then the state
+ of the filesystem should be changed to CLEAN; however if the state was
+ previously SUSPICIOUS, this is only done if CHECKED is true. */
+routine mount_end (
+ mount : mount_t;
+ clean : boolean_t;
+ checked : boolean_t);
+
+/* Indicate that MOUNT is a real mount. TRANSLATOR and MPOINT should be the
+ filesystem control port and the node which is being translated. TIMESTAMP
+ is used to validate this mount if a reconnection to the mount server
+ becomes necessary. */
+routine mount_set_translator (
+ mount : mount_t;
+ translator : fsys_t;
+ mpoint : file_t;
+ timestamp : time_spec_t);
+
+/* If CLEAN is true, then the state of the filesystem should be changed to
+ CLEAN; however if the state was previously SUSPICIOUS, this is only done
+ if CHECKED is true. */
+routine mount_set_clean (
+ mount : mount_t
+ clean : boolean_t;
+ checked : boolean_t);
+
+/* Change the current writable state of MOUNT to MODE, which should be a
+ bitmask containg MOUNT_READ and/or MOUNT_WRITE. If other current mounts
+ preclude this, then EBUSY is returned. If MODE contains MOUNT_FORCE and
+ there are conflicting mounts, an attempt is made to use the --suspend
+ option on the other filesystems in order to force the issue. */
+routine mount_set_mode (
+ mount : mount_t
+ mode : int);
diff --git a/mount/mount.h b/mount/mount.h
new file mode 100644
index 00000000..0d3eb855
--- /dev/null
+++ b/mount/mount.h
@@ -0,0 +1,72 @@
+/* Private definitions for the mount server
+
+ Copyright (C) 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd/ports.h>
+
+#include "mount_types.h"
+
+/* A particular mount of a filesystem. */
+struct mount
+{
+ struct port_info pi; /* libports header. */
+
+ /* The filesystem this a mount of. */
+ struct mount_fsys *fsys;
+
+ /* Modes of this mount, from the set MOUNT_READ, MOUNT_WRITE, &c. */
+ int mode;
+
+ /* The filesystem control port for its translator. This may
+ MACH_PORT_NULL, in which case it's not actually a filesystem (maybe it's
+ a fsck or something). */
+ fsys_t translator;
+
+ /* A timestamp associated with TRANSLATOR, used to validate state if the
+ mount server is restarted. */
+ struct timespec timestamp;
+
+ /* The place in the filesystem where this filesystem is mounted, using the
+ mount server's root node. */
+ char *mount_point;
+
+ /* The next mount of this filesystem. */
+ struct mount *next;
+};
+
+struct mount_fsys
+{
+ /* A string identifying the `backing store' associated with this
+ filesystem, who's interpretation depends on KEY_CLASS:
+ MOUNT_KEY_UNKNOWN -- no interpretation, just a string
+ MOUNT_KEY_FILE -- KEY is a filename
+ MOUNT_KEY_DEVICE -- KEY is a (mach) device name
+ No two filesystems can have the same KEY and KEY_CLASS. */
+ char *key;
+ enum mount_key_class key_class;
+
+ /* In what condition we think the filesystem is. */
+ enum mount_state state;
+
+ /* How this file system deals with multiple mount requests. */
+ enum mount_excl excl;
+
+ /* Active mounts of this filesystem. */
+ struct mount *mounts;
+};
diff --git a/mount/mount_types.h b/mount/mount_types.h
new file mode 100644
index 00000000..f72d3a53
--- /dev/null
+++ b/mount/mount_types.h
@@ -0,0 +1,61 @@
+/* Exported types for the mount server
+
+ Copyright (C) 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Modes a particular mount may have. */
+#define MOUNT_READ 0x1 /* We only read. */
+#define MOUNT_WRITE 0x2 /* We write too. */
+#define MOUNT_FORCE 0x4 /* Used to try forcing a writable mount. */
+
+/* The condition we think a file system is in (this refers to the permanent
+ storage from which it is backed, not an active file system). */
+enum mount_state
+{
+ MOUNT_STATE_UNKNOWN, /* Just so. When this value is passed to a
+ mount routine that takes a state argument,
+ it usually means `keep the last known
+ state'. */
+ MOUNT_STATE_SUSPICIOUS, /* We think it may have been compromised. */
+ MOUNT_STATE_DIRTY, /* May be transiently inconsistent. This is
+ the normal state for an active writable
+ file system. */
+ MOUNT_STATE_CLEAN /* Peachy. */
+};
+typedef enum mount_state mount_state_t; /* For mig's use. */
+
+/* How the key associated with a filesystem is interpreted. */
+enum mount_key_class
+{
+ MOUNT_KEY_UNKNOWN, /* */
+ MOUNT_KEY_FILE, /* A file (including e.g., `/dev/rsd0a'). */
+ MOUNT_KEY_DEVICE /* A mach device name. */
+};
+typedef enum mount_key_class mount_key_class_t; /* For mig's use. */
+
+/* Types of multiple mounts of a single filesystem that are allowed. */
+enum mount_excl
+{
+ MOUNT_EXCL_NONE, /* Both multiple readers and multiple writers
+ allowed; some external private protocol
+ must be used to main consistency. */
+ MOUNT_EXCL_WRITE, /* Multiple readers allowed, but if there's a
+ writer, it must be the only mount. */
+ MOUNT_EXCL_RDWR /* Only a single mount of any type allowed. */
+};
+typedef enum mount_excl mount_excl_t; /* For mig */
diff --git a/move-if-change b/move-if-change
new file mode 100755
index 00000000..66d8b8ad
--- /dev/null
+++ b/move-if-change
@@ -0,0 +1,17 @@
+#!/bin/sh
+# Like mv $1 $2, but if the files are the same, just delete $1.
+# Status is 0 if $2 is changed, 1 otherwise.
+if
+test -r $2
+then
+if
+cmp -s $1 $2
+then
+echo $2 is unchanged
+rm -f $1
+else
+mv -f $1 $2
+fi
+else
+mv -f $1 $2
+fi
diff --git a/nfs/Makefile b/nfs/Makefile
new file mode 100644
index 00000000..d814f67d
--- /dev/null
+++ b/nfs/Makefile
@@ -0,0 +1,32 @@
+# Copyright (C) 1995, 1996, 1997, 2000, 2001, 2008, 2011, 2012 Free Software
+# Foundation, Inc.
+#
+# Written by Michael I. Bushnell.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := nfs
+makemode := server
+
+target = nfs
+SRCS = ops.c rpc.c mount.c nfs.c cache.c consts.c main.c name-cache.c \
+ storage-info.c
+OBJS = $(SRCS:.c=.o)
+HURDLIBS = netfs fshelp iohelp ports ihash shouldbeinlibc
+OTHERLIBS = -lpthread
+
+include ../Makeconf
diff --git a/nfs/cache.c b/nfs/cache.c
new file mode 100644
index 00000000..506b90ff
--- /dev/null
+++ b/nfs/cache.c
@@ -0,0 +1,211 @@
+/* cache.c - Node cache management for NFS client implementation.
+ Copyright (C) 1995, 1996, 1997, 2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "nfs.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <netinet/in.h>
+
+/* Hash table containing all the nodes currently active. XXX Was 512,
+ however, a prime is much nicer for the hash function. 509 is nice
+ as not only is it prime, it also keeps the array within a page or
+ two. */
+#define CACHESIZE 509
+static struct node *nodehash [CACHESIZE];
+
+/* Compute and return a hash key for NFS file handle DATA of LEN
+ bytes. */
+static inline int
+hash (int *data, size_t len)
+{
+ unsigned int h = 0;
+ char *cp = (char *)data;
+ int i;
+
+ for (i = 0; i < len; i++)
+ h += cp[i];
+
+ return h % CACHESIZE;
+}
+
+/* Lookup the file handle P (length LEN) in the hash table. If it is
+ not present, initialize a new node structure and insert it into the
+ hash table. Whichever course, a new reference is generated and the
+ node is returned in *NPP; the lock on the node, (*NPP)->LOCK, is
+ held. */
+void
+lookup_fhandle (void *p, size_t len, struct node **npp)
+{
+ struct node *np;
+ struct netnode *nn;
+ int h;
+
+ h = hash (p, len);
+
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ for (np = nodehash[h]; np; np = np->nn->hnext)
+ {
+ if (np->nn->handle.size != len
+ || memcmp (np->nn->handle.data, p, len) != 0)
+ continue;
+
+ np->references++;
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+ pthread_mutex_lock (&np->lock);
+ *npp = np;
+ return;
+ }
+
+ /* Could not find it */
+ nn = malloc (sizeof (struct netnode));
+ assert (nn);
+
+ nn->handle.size = len;
+ memcpy (nn->handle.data, p, len);
+ nn->stat_updated = 0;
+ nn->dtrans = NOT_POSSIBLE;
+ nn->dead_dir = 0;
+ nn->dead_name = 0;
+
+ np = netfs_make_node (nn);
+ pthread_mutex_lock (&np->lock);
+ nn->hnext = nodehash[h];
+ if (nn->hnext)
+ nn->hnext->nn->hprevp = &nn->hnext;
+ nn->hprevp = &nodehash[h];
+ nodehash[h] = np;
+
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+
+ *npp = np;
+}
+
+/* Package holding args to forked_node_delete. */
+struct fnd
+{
+ struct node *dir;
+ char *name;
+};
+
+/* Worker function to delete nodes that don't have any more local
+ references or links. */
+void *
+forked_node_delete (void *arg)
+{
+ struct fnd *args = arg;
+
+ pthread_mutex_lock (&args->dir->lock);
+ netfs_attempt_unlink ((struct iouser *)-1, args->dir, args->name);
+ netfs_nput (args->dir);
+ free (args->name);
+ free (args);
+ return 0;
+};
+
+/* Called by libnetfs when node NP has no more references. (See
+ <hurd/libnetfs.h> for details. Just clear its local state and
+ remove it from the hash table. Called and expected to leave with
+ NETFS_NODE_REFCNT_LOCK held. */
+void
+netfs_node_norefs (struct node *np)
+{
+ if (np->nn->dead_dir)
+ {
+ struct fnd *args;
+ pthread_t thread;
+ error_t err;
+
+ args = malloc (sizeof (struct fnd));
+ assert (args);
+
+ np->references++;
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+
+ args->dir = np->nn->dead_dir;
+ args->name = np->nn->dead_name;
+ np->nn->dead_dir = 0;
+ np->nn->dead_name = 0;
+ netfs_nput (np);
+
+ /* Do this in a separate thread so that we don't wait for it; it
+ acquires a lock on the dir, which we are not allowed to
+ do. */
+ err = pthread_create (&thread, NULL, forked_node_delete, args);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+
+ /* Caller expects us to leave this locked... */
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ }
+ else
+ {
+ *np->nn->hprevp = np->nn->hnext;
+ if (np->nn->hnext)
+ np->nn->hnext->nn->hprevp = np->nn->hprevp;
+ if (np->nn->dtrans == SYMLINK)
+ free (np->nn->transarg.name);
+ free (np->nn);
+ free (np);
+ }
+}
+
+/* Change the file handle used for node NP to be the handle at P.
+ Make sure the hash table stays up to date. Return the address
+ after the handle. The lock on the node should be held. */
+int *
+recache_handle (int *p, struct node *np)
+{
+ int h;
+ size_t len;
+
+ if (protocol_version == 2)
+ len = NFS2_FHSIZE;
+ else
+ {
+ len = ntohl (*p);
+ p++;
+ }
+
+ /* Unlink it */
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ *np->nn->hprevp = np->nn->hnext;
+ if (np->nn->hnext)
+ np->nn->hnext->nn->hprevp = np->nn->hprevp;
+
+ /* Change the name */
+ np->nn->handle.size = len;
+ memcpy (np->nn->handle.data, p, len);
+
+ /* Reinsert it */
+ h = hash (p, len);
+ np->nn->hnext = nodehash[h];
+ if (np->nn->hnext)
+ np->nn->hnext->nn->hprevp = &np->nn->hnext;
+ np->nn->hprevp = &nodehash[h];
+
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+ return p + len / sizeof (int);
+}
diff --git a/nfs/consts.c b/nfs/consts.c
new file mode 100644
index 00000000..9146615b
--- /dev/null
+++ b/nfs/consts.c
@@ -0,0 +1,25 @@
+/* Definition of constants required by libnetfs
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "nfs.h"
+
+/* Maximum number of times to walk through symlinks before returning
+ ELOOP. See <hurd/libnetfs.h> for details. */
+int netfs_maxsymlinks = 8;
diff --git a/nfs/main.c b/nfs/main.c
new file mode 100644
index 00000000..cd1c29a0
--- /dev/null
+++ b/nfs/main.c
@@ -0,0 +1,438 @@
+/*
+ Copyright (C) 1996, 1997, 2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <hurd/netfs.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <device/device.h>
+#include "nfs.h"
+#include <netinet/in.h>
+#include <unistd.h>
+#include <string.h>
+#include <maptime.h>
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <version.h>
+
+char *netfs_server_name = "nfs";
+char *netfs_server_version = HURD_VERSION;
+
+extern char *localhost ();
+
+/* Default number of times to retry RPCs when mounted soft. */
+#define DEFAULT_SOFT_RETRIES 3
+
+/* Default number of seconds to timeout cached stat information. */
+#define DEFAULT_STAT_TIMEOUT 3
+
+/* Default number of seconds to timeout cached file contents. */
+#define DEFAULT_CACHE_TIMEOUT 3
+
+/* Default number of seconds to timeout cache positive dir hits. */
+#define DEFAULT_NAME_CACHE_TIMEOUT 3
+
+/* Default number of seconds to timeout cache negative dir hits. */
+#define DEFAULT_NAME_CACHE_NEG_TIMEOUT 3
+
+/* Default maximum number of bytes to read at once. */
+#define DEFAULT_READ_SIZE 8192
+
+/* Default maximum number of bytes to write at once. */
+#define DEFAULT_WRITE_SIZE 8192
+
+
+/* Number of seconds to timeout cached stat information. */
+int stat_timeout = DEFAULT_STAT_TIMEOUT;
+
+/* Number of seconds to timeout cached file contents. */
+int cache_timeout = DEFAULT_CACHE_TIMEOUT;
+
+/* Number of seconds to timeout cached positive dir hits. */
+int name_cache_timeout = DEFAULT_NAME_CACHE_TIMEOUT;
+
+/* Number of seconds to timeout cached negative dir hits. */
+int name_cache_neg_timeout = DEFAULT_NAME_CACHE_NEG_TIMEOUT;
+
+/* Number of seconds to wait for first retransmission of an RPC. */
+int initial_transmit_timeout = 1;
+
+/* Maximum number of seconds to wait between retransmission of RPCs. */
+int max_transmit_timeout = 30;
+
+/* Maximum number of retries to send when mounted soft. */
+int soft_retries = DEFAULT_SOFT_RETRIES;
+
+/* True iff we are mounted soft. */
+int mounted_soft = 0;
+
+/* Maximum number of bytes to read at once. */
+int read_size = DEFAULT_READ_SIZE;
+
+/* Maximum number of bytes to write at once. */
+int write_size = DEFAULT_WRITE_SIZE;
+
+#define OPT_SOFT 's'
+#define OPT_HARD 'h'
+#define OPT_RSIZE 'R'
+#define OPT_WSIZE 'W'
+#define OPT_STAT_TO -2
+#define OPT_CACHE_TO -3
+#define OPT_INIT_TR_TO -4
+#define OPT_MAX_TR_TO -5
+#define OPT_MNT_PORT -6
+#define OPT_MNT_PORT_D -7
+#define OPT_NFS_PORT -8
+#define OPT_NFS_PORT_D -9
+#define OPT_HOLD -10
+#define OPT_MNT_PROG -11
+#define OPT_NFS_PROG -12
+#define OPT_PMAP_PORT -13
+#define OPT_NCACHE_TO -14
+#define OPT_NCACHE_NEG_TO -15
+
+/* Return a string corresponding to the printed rep of DEFAULT_what */
+#define ___D(what) #what
+#define __D(what) ___D(what)
+#define _D(what) __D(DEFAULT_ ## what)
+
+const char *argp_program_version = STANDARD_HURD_VERSION (nfs);
+
+/* Options usable both at startup and at runtime. */
+static const struct argp_option common_options[] =
+{
+ {0,0,0,0,0,1},
+ {"soft", OPT_SOFT, "RETRIES", OPTION_ARG_OPTIONAL,
+ "File system requests will eventually fail, after RETRIES tries"
+ " (default " _D(SOFT_RETRIES) ")" },
+ {"hard", OPT_HARD, 0, 0,
+ "Retry file systems requests until they succeed"},
+
+ {0,0,0,0,0,2},
+ {"read-size", OPT_RSIZE, "BYTES", 0,
+ "Max packet size for reads (default " _D(READ_SIZE) ")"},
+ {"rsize",0,0,OPTION_ALIAS},
+ {"write-size", OPT_WSIZE, "BYTES", 0,
+ "Max packet size for writes (default " _D(WRITE_SIZE)")"},
+ {"wsize",0,0,OPTION_ALIAS},
+
+ {0,0,0,0,"Timeouts:",3},
+ {"stat-timeout", OPT_STAT_TO, "SEC", 0,
+ "Timeout for cached stat information (default " _D(STAT_TIMEOUT) ")"},
+ {"cache-timeout", OPT_CACHE_TO, "SEC", 0,
+ "Timeout for cached file data (default " _D(CACHE_TIMEOUT) ")"},
+ {"name-cache-timeout", OPT_NCACHE_TO, "SEC", 0,
+ "Timeout for positive directory cache entries (default "
+ _D(NAME_CACHE_TIMEOUT) ")"},
+ {"name-cache-neg-timeout", OPT_NCACHE_NEG_TO, "SEC", 0,
+ "Timeout for negative directory cache entires (default "
+ _D(NAME_CACHE_NEG_TIMEOUT) ")"},
+ {"init-transmit-timeout", OPT_INIT_TR_TO,"SEC", 0},
+ {"max-transmit-timeout", OPT_MAX_TR_TO, "SEC", 0},
+
+ {0}
+};
+
+static error_t
+parse_common_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case OPT_SOFT:
+ mounted_soft = 1;
+ if (arg)
+ soft_retries = atoi (arg);
+ break;
+ case OPT_HARD:
+ mounted_soft = 0;
+ break;
+
+ case OPT_RSIZE: read_size = atoi (arg); break;
+ case OPT_WSIZE: write_size = atoi (arg); break;
+
+ case OPT_STAT_TO: stat_timeout = atoi (arg); break;
+ case OPT_CACHE_TO: cache_timeout = atoi (arg); break;
+ case OPT_INIT_TR_TO: initial_transmit_timeout = atoi (arg); break;
+ case OPT_MAX_TR_TO: max_transmit_timeout = atoi (arg); break;
+ case OPT_NCACHE_TO: name_cache_timeout = atoi (arg); break;
+ case OPT_NCACHE_NEG_TO: name_cache_neg_timeout = atoi (arg); break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Options usable only at startup. */
+static const struct argp_option startup_options[] = {
+ {0,0,0,0,"Server specification:",10},
+ {"mount-port", OPT_MNT_PORT, "PORT", 0,
+ "Port for mount server"},
+ {"default-mount-port", OPT_MNT_PORT_D,"PORT", 0,
+ "Port for mount server, if none can be found automatically"},
+ {"mount-program", OPT_MNT_PROG, "ID[.VERS]"},
+
+ {"nfs-port", OPT_NFS_PORT, "PORT", 0,
+ "Port for nfs operations"},
+ {"default-nfs-port", OPT_NFS_PORT_D,"PORT", 0,
+ "Port for nfs operations, if none can be found automatically"},
+ {"nfs-program", OPT_NFS_PROG, "ID[.VERS]"},
+
+ {"pmap-port", OPT_PMAP_PORT, "SVC|PORT"},
+
+ {"hold", OPT_HOLD, 0, OPTION_HIDDEN}, /* */
+ { 0 }
+};
+static char *args_doc = "REMOTE_FS [HOST]";
+static char *doc = "Hurd nfs translator"
+"\vIf HOST is not specified, an attempt is made to extract"
+" it from REMOTE_FS using either the `HOST:FS' or `FS@HOST' notations.";
+
+static const struct argp_child
+runtime_argp_children[] = { {&netfs_std_runtime_argp}, {0} };
+static struct argp
+runtime_argp = { common_options, parse_common_opt, 0, 0,
+ runtime_argp_children };
+
+/* Used by netfs_set_options to handle runtime option parsing. */
+struct argp *netfs_runtime_argp = &runtime_argp;
+
+/* Where to find the remote filesystem. */
+static char *remote_fs; /* = 0; */
+static char *host; /* = 0; */
+
+/* Return an argz string describing the current options. Fill *ARGZ
+ with a pointer to newly malloced storage holding the list and *LEN
+ to the length of that storage. */
+error_t
+netfs_append_args (char **argz, size_t *argz_len)
+{
+ char buf[80];
+ error_t err = 0;
+
+#define FOPT(fmt, arg) \
+ do { \
+ if (! err) \
+ { \
+ snprintf (buf, sizeof buf, fmt, arg); \
+ err = argz_add (argz, argz_len, buf); \
+ } \
+ } while (0)
+
+ if (mounted_soft)
+ FOPT ("--soft=%d", soft_retries);
+ else
+ err = argz_add (argz, argz_len, "--hard");
+
+ FOPT ("--read-size=%d", read_size);
+ FOPT ("--write-size=%d", write_size);
+
+ FOPT ("--stat-timeout=%d", stat_timeout);
+ FOPT ("--cache-timeout=%d", cache_timeout);
+ FOPT ("--init-transmit-timeout=%d", initial_transmit_timeout);
+ FOPT ("--max-transmit-timeout=%d", max_transmit_timeout);
+ FOPT ("--name-cache-timeout=%d", name_cache_timeout);
+ FOPT ("--name-cache-neg-timeout=%d", name_cache_neg_timeout);
+
+ if (! err)
+ err = netfs_append_std_options (argz, argz_len);
+
+ if (! err)
+ {
+ char *fs;
+ if (asprintf (&fs, "%s:%s", host, remote_fs))
+ {
+ err = argz_add (argz, argz_len, fs);
+ free (fs);
+ }
+ else
+ err = ENOMEM;
+ }
+
+ return err;
+}
+
+/* The user may define this function. The function must set source to
+ the source of CRED. The function may return an EOPNOTSUPP to
+ indicate that the concept of a source device is not applicable. The
+ default function always returns EOPNOTSUPP. */
+error_t
+netfs_get_source (struct protid *cred, char *source, size_t source_len)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+
+ snprintf (source, source_len, "%s:%s", host, remote_fs);
+ return 0;
+}
+
+/* Extract the host and remote filesystem names from SPEC, which should use
+ either HOST:FS or FS@HOST notation. Returns the malloced storage into
+ which both REMOTE_FS and HOST point, or 0 if SPEC is invalid. */
+static char *
+extract_nfs_args (char *spec, char **remote_fs, char **host)
+{
+ char *sep;
+
+ spec = strdup (spec); /* So we can trash it. */
+ if (! spec)
+ return NULL;
+
+ sep = index (spec, ':');
+ if (sep)
+ {
+ *sep++ = '\0';
+ *host = spec;
+ *remote_fs = sep;
+ return spec;
+ }
+
+ sep = index (spec, '@');
+ if (sep)
+ {
+ *sep++ = '\0';
+ *host = sep;
+ *remote_fs = spec;
+ return spec;
+ }
+
+ free (spec);
+
+ return 0;
+}
+
+static error_t
+parse_startup_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case OPT_MNT_PORT:
+ mount_port_override = 1;
+ /* fall through */
+ case OPT_MNT_PORT_D:
+ mount_port = atoi (arg);
+ break;
+
+ case OPT_NFS_PORT:
+ nfs_port_override = 1;
+ /* fall through */
+ case OPT_NFS_PORT_D:
+ nfs_port = atoi (arg);
+ break;
+
+ case ARGP_KEY_ARG:
+ if (state->arg_num == 0)
+ remote_fs = arg;
+ else if (state->arg_num == 1)
+ host = arg;
+ else
+ return ARGP_ERR_UNKNOWN;
+ break;
+
+ case ARGP_KEY_END:
+ if (!host && !extract_nfs_args (remote_fs, &remote_fs, &host))
+ argp_error (state, "No HOST specified");
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_error (state, "No REMOTE_FS specified");
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+/* NFS client main program */
+int
+main (int argc, char **argv)
+{
+ pthread_t thread;
+ error_t err;
+
+ struct argp common_argp = { common_options, parse_common_opt };
+ const struct argp_child argp_children[] =
+ { {&common_argp}, {&netfs_std_startup_argp}, {0} };
+ struct argp argp =
+ { startup_options, parse_startup_opt, args_doc, doc, argp_children };
+ mach_port_t bootstrap;
+ struct sockaddr_in addr;
+ int ret;
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ netfs_init ();
+
+ main_udp_socket = socket (PF_INET, SOCK_DGRAM, 0);
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = htons (IPPORT_RESERVED);
+ do
+ {
+ addr.sin_port = htons (ntohs (addr.sin_port) - 1);
+ ret = bind (main_udp_socket, (struct sockaddr *)&addr,
+ sizeof (struct sockaddr_in));
+ if (ret == -1 && errno == EACCES)
+ {
+ /* We aren't allowed privileged ports; no matter;
+ let the server deny us later if it wants. */
+ ret = 0;
+ break;
+ }
+ }
+ while ((ret == -1) && (errno == EADDRINUSE));
+ if (ret == -1)
+ error (1, errno, "binding main udp socket");
+
+ err = maptime_map (0, 0, &mapped_time);
+ if (err)
+ error (2, err, "mapping time");
+
+ err = pthread_create (&thread, NULL, timeout_service_thread, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+ err = pthread_create (&thread, NULL, rpc_receive_thread, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+
+ hostname = localhost ();
+
+ netfs_root_node = mount_root (remote_fs, host);
+
+ if (!netfs_root_node)
+ exit (1);
+
+ netfs_startup (bootstrap, 0);
+
+ for (;;)
+ netfs_server_loop ();
+}
diff --git a/nfs/mount.c b/nfs/mount.c
new file mode 100644
index 00000000..29b203aa
--- /dev/null
+++ b/nfs/mount.c
@@ -0,0 +1,277 @@
+/*
+ Copyright (C) 1995,96,97,98,2001,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#define malloc a_byte_for_every_bozotic_sun_lossage_and_youd_need_a_lotta_ram
+#include <rpc/types.h>
+#undef TRUE /* Get rid of sun defs. */
+#undef FALSE
+#undef malloc
+#include <rpc/pmap_prot.h>
+#include <errno.h>
+#include <error.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <stdio.h>
+
+#include "nfs.h"
+#include "mount.h"
+
+/* Service name for portmapper */
+char *pmap_service_name = "sunrpc";
+
+/* Fallback port number for portmapper */
+short pmap_service_number = PMAPPORT;
+
+/* RPC program for mount server. */
+int mount_program = MOUNTPROG;
+
+/* RPC version for mount server. */
+int mount_version = MOUNTVERS;
+
+/* Fallback port number for mount server. */
+short mount_port = 0;
+
+/* True iff MOUNT_PORT should be used even if portmapper present. */
+int mount_port_override = 0;
+
+/* RPC program number for NFS server. */
+int nfs_program = NFS_PROGRAM;
+
+/* RPC version number for NFS server. */
+int nfs_version = NFS_VERSION;
+
+/* Fallback port number for NFS server. */
+short nfs_port = NFS_PORT;
+
+/* True iff NFS_PORT should be used even if portmapper present. */
+int nfs_port_override = 0;
+
+/* Host name and port number we actually decided to use. */
+const char *mounted_hostname;
+uint16_t mounted_nfs_port; /* host order */
+
+int protocol_version = 2;
+
+/* Set up an RPC for procedure PROCNUM for talking to the portmapper.
+ Allocate storage with malloc and point *BUF at it; caller must free
+ this when done. Return the address where the args for the
+ procedure should be placed. */
+static int *
+pmap_initialize_rpc (int procnum, void **buf)
+{
+ return initialize_rpc (PMAPPROG, PMAPVERS, procnum, 0, buf, 0, 0, -1);
+}
+
+/* Set up an RPC for procedure PROCNUM for talking to the mount
+ server. Allocate storage with malloc and point *BUF at it; caller
+ must free this when done. Return the address where the args for
+ the procedure should be placed. */
+static int *
+mount_initialize_rpc (int procnum, void **buf)
+{
+ return initialize_rpc (MOUNTPROG, MOUNTVERS, procnum, 0, buf, 0, 0, -1);
+}
+
+/* Using the mount protocol, lookup NAME at host HOST.
+ Return a node for it or null for an error. If an
+ error occurs, a message is automatically sent to stderr. */
+struct node *
+mount_root (char *name, char *host)
+{
+ struct sockaddr_in addr;
+ struct hostent *h;
+ int *p;
+ void *rpcbuf;
+ int port;
+ error_t err;
+ struct node *np;
+ short pmapport;
+
+ /* Lookup the portmapper port number */
+ if (pmap_service_name)
+ {
+ struct servent *s;
+
+ /* XXX This will always fail! pmap_service_name will always be "sunrpc"
+ What should pmap_service_name really be? By definition the second
+ argument is either "tcp" or "udp" Thus, is this backwards
+ (as service_name suggests)? If so, should it read:
+ s = getservbyname (pmap_service_name, "udp");
+ or is there something I am missing here? */
+ s = getservbyname ("sunrpc", pmap_service_name);
+ if (s)
+ pmapport = s->s_port;
+ else
+ pmapport = htons (pmap_service_number);
+ }
+ else
+ pmapport = htons (pmap_service_number);
+
+ /* Lookup the host */
+ h = gethostbyname (host);
+ if (!h)
+ {
+ herror (host);
+ return 0;
+ }
+
+ addr.sin_family = h->h_addrtype;
+ memcpy (&addr.sin_addr, h->h_addr_list[0], h->h_length);
+ addr.sin_port = pmapport;
+
+ if (mount_port_override)
+ addr.sin_port = htons (mount_port);
+ else
+ {
+ /* Formulate and send a PMAPPROC_GETPORT request
+ to lookup the mount program on the server. */
+ if (connect (main_udp_socket, (struct sockaddr *)&addr,
+ sizeof (struct sockaddr_in)) == -1)
+ {
+ error (0, errno, "server mount program");
+ return 0;
+ }
+
+ p = pmap_initialize_rpc (PMAPPROC_GETPORT, &rpcbuf);
+ if (! p)
+ {
+ error (0, errno, "creating rpc packet");
+ return 0;
+ }
+
+ *(p++) = htonl (MOUNTPROG);
+ *(p++) = htonl (MOUNTVERS);
+ *(p++) = htonl (IPPROTO_UDP);
+ *(p++) = htonl (0);
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ port = ntohl (*p);
+ p++;
+ addr.sin_port = htons (port);
+ }
+ else if (mount_port)
+ addr.sin_port = htons (mount_port);
+ else
+ {
+ error (0, err, "portmap of mount");
+ goto error_with_rpcbuf;
+ }
+ free (rpcbuf);
+ }
+
+ /* Now, talking to the mount program, fetch a file handle
+ for the root. */
+ if (connect (main_udp_socket, (struct sockaddr *) &addr,
+ sizeof (struct sockaddr_in)) == -1)
+ {
+ error (0, errno, "connect");
+ goto error_with_rpcbuf;
+ }
+
+ p = mount_initialize_rpc (MOUNTPROC_MNT, &rpcbuf);
+ if (! p)
+ {
+ error (0, errno, "rpc");
+ goto error_with_rpcbuf;
+ }
+
+ p = xdr_encode_string (p, name);
+ err = conduct_rpc (&rpcbuf, &p);
+ if (err)
+ {
+ error (0, err, "%s", name);
+ goto error_with_rpcbuf;
+ }
+ /* XXX Protocol spec says this should be a "unix error code"; we'll
+ pretend that an NFS error code is what's meant; the numbers match
+ anyhow. */
+ err = nfs_error_trans (htonl (*p));
+ p++;
+ if (err)
+ {
+ error (0, err, "%s", name);
+ goto error_with_rpcbuf;
+ }
+
+ /* Create the node for root */
+ xdr_decode_fhandle (p, &np);
+ free (rpcbuf);
+ pthread_mutex_unlock (&np->lock);
+
+ if (nfs_port_override)
+ port = nfs_port;
+ else
+ {
+ /* Send another PMAPPROC_GETPORT request to lookup the nfs server. */
+ addr.sin_port = pmapport;
+ if (connect (main_udp_socket, (struct sockaddr *) &addr,
+ sizeof (struct sockaddr_in)) == -1)
+ {
+ error (0, errno, "connect");
+ return 0;
+ }
+
+ p = pmap_initialize_rpc (PMAPPROC_GETPORT, &rpcbuf);
+ if (! p)
+ {
+ error (0, errno, "rpc");
+ goto error_with_rpcbuf;
+ }
+ *(p++) = htonl (NFS_PROGRAM);
+ *(p++) = htonl (NFS_VERSION);
+ *(p++) = htonl (IPPROTO_UDP);
+ *(p++) = htonl (0);
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ port = ntohl (*p);
+ p++;
+ }
+ else if (nfs_port)
+ port = nfs_port;
+ else
+ {
+ error (0, err, "portmap of nfs server");
+ goto error_with_rpcbuf;
+ }
+ free (rpcbuf);
+ }
+
+ addr.sin_port = htons (port);
+ if (connect (main_udp_socket, (struct sockaddr *) &addr,
+ sizeof (struct sockaddr_in)) == -1)
+ {
+ error (0, errno, "connect");
+ return 0;
+ }
+
+ mounted_hostname = host;
+ mounted_nfs_port = port;
+
+ return np;
+
+error_with_rpcbuf:
+ free (rpcbuf);
+
+ return 0;
+}
diff --git a/nfs/mount.h b/nfs/mount.h
new file mode 100644
index 00000000..4cb62a83
--- /dev/null
+++ b/nfs/mount.h
@@ -0,0 +1,40 @@
+/* Manifest constants describing the mount protocol
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* These constants define the RPC mount protocol; see RFC 1094. */
+
+#ifndef NFS_MOUNT_H
+#define NFS_MOUNT_H
+
+#define MOUNTPROG 100005
+#define MOUNTVERS 1
+
+/* Obnoxious arbitrary limits */
+#define MOUNT_MNTPATHLEN 1024
+#define MOUNT_MNTNAMLEN 255
+
+#define MOUNTPROC_NULL 0
+#define MOUNTPROC_MNT 1
+#define MOUNTPROC_DUMP 2
+#define MOUNTPROC_UMNT 3
+#define MOUNTPROC_UMNTALL 4
+#define MOUNTPROC_EXPORT 5
+
+#endif /* NFS_MOUNT_H */
diff --git a/nfs/name-cache.c b/nfs/name-cache.c
new file mode 100644
index 00000000..7553d7c0
--- /dev/null
+++ b/nfs/name-cache.c
@@ -0,0 +1,305 @@
+/* Directory name lookup caching
+
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG, & Miles Bader.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "nfs.h"
+#include <string.h>
+#include <cacheq.h>
+
+
+/* Maximum number of names to cache at any given time */
+#define MAXCACHE 200
+
+/* Maximum length of file name we bother caching */
+#define CACHE_NAME_LEN 100
+
+/* Cache entry */
+struct lookup_cache
+{
+ struct cacheq_hdr hdr;
+
+ /* File handles and lengths for cache entries. 0 for NODE_CACHE_LEN
+ means a */
+ char dir_cache_fh[NFS3_FHSIZE];
+ size_t dir_cache_len;
+
+ /* Zero means a `negative' entry -- recording that there's
+ definitely no node with this name. */
+ struct node *np;
+
+ /* Name of the node NODE_CACHE_ID in the directory DIR_CACHE_ID. Entries
+ with names too long to fit in this buffer aren't cached at all. */
+ char name[CACHE_NAME_LEN];
+
+ /* Strlen of NAME. If this is zero, it's an unused entry. */
+ size_t name_len;
+
+ /* Time that this cache entry was created. */
+ time_t cache_stamp;
+
+ /* XXX */
+ int stati;
+};
+
+/* The contents of the cache in no particular order */
+static struct cacheq lookup_cache = { sizeof (struct lookup_cache) };
+
+static pthread_spinlock_t cache_lock = PTHREAD_SPINLOCK_INITIALIZER;
+
+/* Buffer to hold statistics */
+static struct stats
+{
+ long pos_hits;
+ long neg_hits;
+ long miss;
+ long fetch_errors;
+} statistics;
+
+#define PARTIAL_THRESH 100
+#define NPARTIALS (MAXCACHE / PARTIAL_THRESH)
+struct stats partial_stats [NPARTIALS];
+
+
+/* If there's an entry for NAME, of length NAME_LEN, in directory DIR in the
+ cache, return its entry, otherwise 0. CACHE_LOCK must be held. */
+static struct lookup_cache *
+find_cache (char *dir, size_t len, const char *name, size_t name_len)
+{
+ struct lookup_cache *c;
+ int i;
+
+ /* Search the list. All unused entries are contiguous at the end of the
+ list, so we can stop searching when we see the first one. */
+ for (i = 0, c = lookup_cache.mru;
+ c && c->name_len;
+ c = c->hdr.next, i++)
+ if (c->name_len == name_len
+ && c->dir_cache_len == len
+ && c->name[0] == name[0]
+ && memcmp (c->dir_cache_fh, dir, len) == 0
+ && strcmp (c->name, name) == 0)
+ {
+ c->stati = i / PARTIAL_THRESH;
+ return c;
+ }
+
+ return 0;
+}
+
+/* Node NP has just been found in DIR with NAME. If NP is null, this
+ name has been confirmed as absent in the directory. DIR is the
+ fhandle of the directory and LEN is its length. */
+void
+enter_lookup_cache (char *dir, size_t len, struct node *np, char *name)
+{
+ struct lookup_cache *c;
+ size_t name_len = strlen (name);
+
+ if (name_len > CACHE_NAME_LEN - 1)
+ return;
+
+ pthread_spin_lock (&cache_lock);
+
+ if (lookup_cache.length == 0)
+ /* There should always be an lru_cache; this being zero means that the
+ cache hasn't been initialized yet. Do so. */
+ cacheq_set_length (&lookup_cache, MAXCACHE);
+
+ /* See if there's an old entry for NAME in DIR. If not, replace the least
+ recently used entry. */
+ c = find_cache (dir, len, name, name_len) ?: lookup_cache.lru;
+
+ /* Fill C with the new entry. */
+ memcpy (c->dir_cache_fh, dir, len);
+ c->dir_cache_len = len;
+ if (c->np)
+ netfs_nrele (c->np);
+ c->np = np;
+ if (c->np)
+ netfs_nref (c->np);
+ strcpy (c->name, name);
+ c->name_len = name_len;
+ c->cache_stamp = mapped_time->seconds;
+
+ /* Now C becomes the MRU entry! */
+ cacheq_make_mru (&lookup_cache, c);
+
+ pthread_spin_unlock (&cache_lock);
+}
+
+/* Purge all references in the cache to NAME within directory DIR. */
+void
+purge_lookup_cache (struct node *dp, char *name, size_t namelen)
+{
+ struct lookup_cache *c, *next;
+
+ pthread_spin_lock (&cache_lock);
+ for (c = lookup_cache.mru; c; c = next)
+ {
+ /* Save C->hdr.next, since we may move C from this position. */
+ next = c->hdr.next;
+
+ if (c->name_len == namelen
+ && c->dir_cache_len == dp->nn->handle.size
+ && memcmp (c->dir_cache_fh, dp->nn->handle.data,
+ c->dir_cache_len) == 0
+ && strcmp (c->name, name) == 0)
+ {
+ if (c->np)
+ netfs_nrele (c->np);
+ c->name_len = 0;
+ c->np = 0;
+ cacheq_make_lru (&lookup_cache, c); /* Use C as the next free
+ entry. */
+ }
+ }
+ pthread_spin_unlock (&cache_lock);
+}
+
+/* Purge all references in the cache to node NP. */
+void
+purge_lookup_cache_node (struct node *np)
+{
+ struct lookup_cache *c, *next;
+
+ pthread_spin_lock (&cache_lock);
+ for (c = lookup_cache.mru; c; c = next)
+ {
+ next = c->hdr.next;
+
+ if (c->np == np)
+ {
+ netfs_nrele (c->np);
+ c->name_len = 0;
+ c->np = 0;
+ cacheq_make_lru (&lookup_cache, c);
+ }
+ }
+ pthread_spin_unlock (&cache_lock);
+}
+
+
+
+/* Register a negative hit for an entry in the Nth stat class */
+void
+register_neg_hit (int n)
+{
+ int i;
+
+ statistics.neg_hits++;
+
+ for (i = 0; i < n; i++)
+ partial_stats[i].miss++;
+ for (; i < NPARTIALS; i++)
+ partial_stats[i].neg_hits++;
+}
+
+/* Register a positive hit for an entry in the Nth stat class */
+void
+register_pos_hit (int n)
+{
+ int i;
+
+ statistics.pos_hits++;
+
+ for (i = 0; i < n; i++)
+ partial_stats[i].miss++;
+ for (; i < NPARTIALS; i++)
+ partial_stats[i].pos_hits++;
+}
+
+/* Register a miss */
+void
+register_miss ()
+{
+ int i;
+
+ statistics.miss++;
+ for (i = 0; i < NPARTIALS; i++)
+ partial_stats[i].miss++;
+}
+
+
+
+/* Scan the cache looking for NAME inside DIR. If we know nothing
+ about the entry, then return 0. If the entry is confirmed to not
+ exist, then return -1. Otherwise, return NP for the entry, with
+ a newly allocated reference. For all return values other than 0,
+ unlock DIR->LOCK before returning. For positive hits, lock the
+ returned node. */
+struct node *
+check_lookup_cache (struct node *dir, char *name)
+{
+ struct lookup_cache *c;
+
+ pthread_spin_lock (&cache_lock);
+
+ c = find_cache (dir->nn->handle.data, dir->nn->handle.size,
+ name, strlen (name));
+ if (c)
+ {
+ int timeout = c->np
+ ? name_cache_timeout
+ : name_cache_neg_timeout;
+
+ /* Make sure the entry is still usable; if not, zap it now. */
+ if (mapped_time->seconds - c->cache_stamp >= timeout)
+ {
+ register_neg_hit (c->stati);
+ if (c->np)
+ netfs_nrele (c->np);
+ c->name_len = 0;
+ c->np = 0;
+ cacheq_make_lru (&lookup_cache, c);
+ pthread_spin_unlock (&cache_lock);
+ return 0;
+ }
+
+ cacheq_make_mru (&lookup_cache, c); /* Record C as recently used. */
+
+ if (c->np == 0)
+ /* A negative cache entry. */
+ {
+ register_neg_hit (c->stati);
+ pthread_spin_unlock (&cache_lock);
+ pthread_mutex_unlock (&dir->lock);
+ return (struct node *)-1;
+ }
+ else
+ {
+ struct node *np;
+
+ np = c->np;
+ netfs_nref (np);
+ register_pos_hit (c->stati);
+ pthread_spin_unlock (&cache_lock);
+
+ pthread_mutex_unlock (&dir->lock);
+ pthread_mutex_lock (&np->lock);
+
+ return np;
+ }
+ }
+
+ register_miss ();
+ pthread_spin_unlock (&cache_lock);
+
+ return 0;
+}
diff --git a/nfs/nfs-spec.h b/nfs/nfs-spec.h
new file mode 100644
index 00000000..bed03874
--- /dev/null
+++ b/nfs/nfs-spec.h
@@ -0,0 +1,168 @@
+#ifndef NFS_NFS_SPEC_H
+#define NFS_NFS_SPEC_H
+
+#define NFS_PORT 2049
+#define NFS_MAXDATA 8192
+#define NFS_MAXPATHLEN 1024
+#define NFS_MAXNAMLEN 255
+#define NFS2_FHSIZE 32
+#define NFS3_FHSIZE 64
+#define NFS_COOKIESIZE 4
+#define NFS_FIFO_DEV -1
+#define NFS3_COOKIEVERFSIZE 8
+#define NFS3_CREATEVERFSIZE 8
+#define NFS3_WRITEVERFSIZE 8
+#define NFSMODE_FMT 0170000
+#define NFSMODE_DIR 0040000
+#define NFSMODE_CHR 0020000
+#define NFSMODE_BLK 0060000
+#define NFSMODE_REG 0100000
+#define NFSMODE_LNK 0120000
+#define NFSMODE_SOCK 0140000
+#define NFSMODE_FIFO 0010000
+
+enum nfsstat {
+ NFS_OK = 0,
+ NFSERR_PERM = 1,
+ NFSERR_NOENT = 2,
+ NFSERR_IO = 5,
+ NFSERR_NXIO = 6,
+ NFSERR_ACCES = 13,
+ NFSERR_EXIST = 17,
+ NFSERR_XDEV = 18, /* v3 only */
+ NFSERR_NODEV = 19,
+ NFSERR_NOTDIR = 20,
+ NFSERR_ISDIR = 21,
+ NFSERR_INVAL = 22, /* v3 only */
+ NFSERR_FBIG = 27,
+ NFSERR_NOSPC = 28,
+ NFSERR_ROFS = 30,
+ NFSERR_MLINK = 31, /* v3 only */
+ NFSERR_NAMETOOLONG = 63,
+ NFSERR_NOTEMPTY = 66,
+ NFSERR_DQUOT = 69,
+ NFSERR_STALE = 70,
+ NFSERR_REMOTE = 71, /* v3 only */
+ NFSERR_WFLUSH = 99, /* v2 only */
+ NFSERR_BADHANDLE = 10001, /* v3 only */
+ NFSERR_NOT_SYNC = 10002, /* v3 only */
+ NFSERR_BAD_COOKIE = 10003, /* v3 only */
+ NFSERR_NOTSUPP = 10004, /* v3 only */
+ NFSERR_TOOSMALL = 10005, /* v3 only */
+ NFSERR_SERVERFAULT = 10006, /* v3 only */
+ NFSERR_BADTYPE = 10007, /* v3 only */
+ NFSERR_JUKEBOX = 10008, /* v3 only */
+#define NFSERR_TRYLATER NFSERR_JUKEBOX
+};
+
+
+enum ftype {
+ NF2NON = 0, /* v2 only */
+ NFREG = 1,
+ NFDIR = 2,
+ NFBLK = 3,
+ NFCHR = 4,
+ NFLNK = 5,
+ NFSOCK = 6,
+ NF3FIFO = 7, /* v3 only */
+#define NF2BAD NF3FIFO /* v2 only */
+ NF2FIFO = 8, /* v2 only */
+};
+
+/* Ways to set the time in setattr structures */
+enum sattr_time_how
+{
+ DONT_CHANGE = 0,
+ SET_TO_SERVER_TIME = 1,
+ SET_TO_CLIENT_TIME = 2,
+};
+
+/* Construction of ACCESS arg to NFS3PROC_ACCESS. */
+#define ACCESS3_READ 0x01
+#define ACCESS3_LOOKUP 0x02
+#define ACCESS3_MODIFY 0x04
+#define ACCESS3_EXTEND 0x08
+#define ACCESS3_DELETE 0x10
+#define ACCESS3_EXECUTE 0x20
+
+/* STABLE arg to NFS3PROC_READ */
+enum stable_how {
+ UNSTABLE = 0,
+ DATA_SYNC = 1,
+ FILE_SYNC = 2,
+};
+
+/* MODE arg to NFS3PROC_CREATE */
+enum createmode
+{
+ UNCHECKED = 0,
+ GUARDED = 1,
+ EXCLUSIVE = 2,
+};
+
+#define NFS_PROGRAM ((u_long)100003)
+#define NFS_VERSION ((u_long)2)
+
+#define NFS_PROTOCOL_FUNC(proc,vers) \
+ (vers == 2 ? NFS2PROC_ ## proc : NFS3PROC_ ## proc)
+
+#define NFSPROC_NULL(v) NFS_PROTOCOL_FUNC (NULL,v)
+#define NFSPROC_GETATTR(v) NFS_PROTOCOL_FUNC (GETATTR, v)
+#define NFSPROC_SETATTR(v) NFS_PROTOCOL_FUNC (SETATTR, v)
+#define NFSPROC_LOOKUP(v) NFS_PROTOCOL_FUNC (LOOKUP, v)
+#define NFSPROC_READLINK(v) NFS_PROTOCOL_FUNC (READLINK, v)
+#define NFSPROC_READ(v) NFS_PROTOCOL_FUNC (READ, v)
+#define NFSPROC_WRITE(v) NFS_PROTOCOL_FUNC (WRITE, v)
+#define NFSPROC_CREATE(v) NFS_PROTOCOL_FUNC (CREATE, v)
+#define NFSPROC_REMOVE(v) NFS_PROTOCOL_FUNC (REMOVE, v)
+#define NFSPROC_RENAME(v) NFS_PROTOCOL_FUNC (RENAME, v)
+#define NFSPROC_LINK(v) NFS_PROTOCOL_FUNC (LINK, v)
+#define NFSPROC_SYMLINK(v) NFS_PROTOCOL_FUNC (SYMLINK, v)
+#define NFSPROC_MKDIR(v) NFS_PROTOCOL_FUNC (MKDIR, v)
+#define NFSPROC_RMDIR(v) NFS_PROTOCOL_FUNC (RMDIR, v)
+#define NFSPROC_READDIR(v) NFS_PROTOCOL_FUNC (READDIR, v)
+
+/* Values for each protocol */
+#define NFS2PROC_NULL 0
+#define NFS2PROC_GETATTR 1
+#define NFS2PROC_SETATTR 2
+#define NFS2PROC_ROOT 3
+#define NFS2PROC_LOOKUP 4
+#define NFS2PROC_READLINK 5
+#define NFS2PROC_READ 6
+#define NFS2PROC_WRITECACHE 7
+#define NFS2PROC_WRITE 8
+#define NFS2PROC_CREATE 9
+#define NFS2PROC_REMOVE 10
+#define NFS2PROC_RENAME 11
+#define NFS2PROC_LINK 12
+#define NFS2PROC_SYMLINK 13
+#define NFS2PROC_MKDIR 14
+#define NFS2PROC_RMDIR 15
+#define NFS2PROC_READDIR 16
+#define NFS2PROC_STATFS 17
+
+#define NFS3PROC_NULL 0
+#define NFS3PROC_GETATTR 1
+#define NFS3PROC_SETATTR 2
+#define NFS3PROC_LOOKUP 3
+#define NFS3PROC_ACCESS 4
+#define NFS3PROC_READLINK 5
+#define NFS3PROC_READ 6
+#define NFS3PROC_WRITE 7
+#define NFS3PROC_CREATE 8
+#define NFS3PROC_MKDIR 9
+#define NFS3PROC_SYMLINK 10
+#define NFS3PROC_MKNOD 11
+#define NFS3PROC_REMOVE 12
+#define NFS3PROC_RMDIR 13
+#define NFS3PROC_RENAME 14
+#define NFS3PROC_LINK 15
+#define NFS3PROC_READDIR 16
+#define NFS3PROC_READDIRPLUS 17
+#define NFS3PROC_FSSTAT 18
+#define NFS3PROC_FSINFO 19
+#define NFS3PROC_PATHCONF 20
+#define NFS3PROC_COMMIT 21
+
+#endif /* NFS_NFS_SPEC_H */
diff --git a/nfs/nfs.c b/nfs/nfs.c
new file mode 100644
index 00000000..4916df65
--- /dev/null
+++ b/nfs/nfs.c
@@ -0,0 +1,681 @@
+/* nfs.c - XDR frobbing and lower level routines for NFS client.
+
+ Copyright (C) 1995, 1996, 1997, 1999, 2002, 2007
+ Free Software Foundation, Inc.
+
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "nfs.h"
+
+#include <string.h>
+#include <netinet/in.h>
+#include <stdio.h>
+
+/* Convert an NFS mode (TYPE and MODE) to a Hurd mode and return
+ it. */
+mode_t
+nfs_mode_to_hurd_mode (int type, int mode)
+{
+ int hurdmode;
+
+ switch (type)
+ {
+ case NFDIR:
+ hurdmode = S_IFDIR;
+ break;
+
+ case NFCHR:
+ hurdmode = S_IFCHR;
+ break;
+
+ case NFBLK:
+ hurdmode = S_IFBLK;
+ break;
+
+ case NFREG:
+ hurdmode = S_IFREG;
+ break;
+
+ case NFLNK:
+ hurdmode = S_IFLNK;
+ break;
+
+ case NFSOCK:
+ hurdmode = S_IFSOCK;
+ break;
+
+ default:
+ if (protocol_version == 2)
+ switch (type)
+ {
+ case NF2NON:
+ case NF2BAD:
+ default:
+ hurdmode = S_IFREG;
+ break;
+
+ case NF2FIFO:
+ hurdmode = S_IFIFO;
+ break;
+ }
+ else
+ switch (type)
+ {
+ case NF3FIFO:
+ hurdmode = S_IFIFO;
+ break;
+
+ default:
+ hurdmode = S_IFREG;
+ break;
+ }
+ break;
+ }
+
+ hurdmode |= mode & ~NFSMODE_FMT;
+ return hurdmode;
+}
+
+/* Convert a Hurd mode to an NFS mode. */
+int
+hurd_mode_to_nfs_mode (mode_t mode)
+{
+ /* This function is used only for chmod; just trim the bits that NFS
+ doesn't support. */
+ return mode & 07777;
+}
+
+/* Convert a Hurd mode to an NFS type. */
+int
+hurd_mode_to_nfs_type (mode_t mode)
+{
+ switch (mode & S_IFMT)
+ {
+ case S_IFDIR:
+ return NFDIR;
+
+ case S_IFCHR:
+ default:
+ return NFCHR;
+
+ case S_IFBLK:
+ return NFBLK;
+
+ case S_IFREG:
+ return NFREG;
+
+ case S_IFLNK:
+ return NFLNK;
+
+ case S_IFSOCK:
+ return NFSOCK;
+
+ case S_IFIFO:
+ return protocol_version == 2 ? NF2FIFO : NF3FIFO;
+ }
+}
+
+
+
+/* Each of the functions on this page copies its second arg to *P,
+ converting it to XDR representation along the way. They then
+ return the address after the copied value. */
+
+/* Encode an NFS file handle. */
+int *
+xdr_encode_fhandle (int *p, struct fhandle *fhandle)
+{
+ if (protocol_version == 2)
+ {
+ memcpy (p, fhandle->data, NFS2_FHSIZE);
+ return p + INTSIZE (NFS2_FHSIZE);
+ }
+ else
+ return xdr_encode_data (p, fhandle->data, fhandle->size);
+}
+
+/* Encode uninterpreted bytes. */
+int *
+xdr_encode_data (int *p, char *data, size_t len)
+{
+ int nints = INTSIZE (len);
+
+ p[nints] = 0;
+ *(p++) = htonl (len);
+ memcpy (p, data, len);
+ return p + nints;
+}
+
+/* Encode a 64 bit integer. */
+int *
+xdr_encode_64bit (int *p, long long n)
+{
+ *(p++) = htonl (n & 0xffffffff00000000LL >> 32);
+ *(p++) = htonl (n & 0xffffffff);
+ return p;
+}
+
+/* Encode a C string. */
+int *
+xdr_encode_string (int *p, char *string)
+{
+ return xdr_encode_data (p, string, strlen (string));
+}
+
+/* Encode a MODE into an otherwise empty sattr. */
+int *
+xdr_encode_sattr_mode (int *p, mode_t mode)
+{
+ if (protocol_version == 2)
+ {
+ *(p++) = htonl (hurd_mode_to_nfs_mode (mode));
+ *(p++) = -1; /* uid */
+ *(p++) = -1; /* gid */
+ *(p++) = -1; /* size */
+ *(p++) = -1; /* atime secs */
+ *(p++) = -1; /* atime usecs */
+ *(p++) = -1; /* mtime secs */
+ *(p++) = -1; /* mtime usecs */
+ }
+ else
+ {
+ *(p++) = htonl (1); /* set mode */
+ *(p++) = htonl (hurd_mode_to_nfs_mode (mode));
+ *(p++) = 0; /* no uid */
+ *(p++) = 0; /* no gid */
+ *(p++) = 0; /* no size */
+ *(p++) = DONT_CHANGE; /* no atime */
+ *(p++) = DONT_CHANGE; /* no mtime */
+ }
+ return p;
+}
+
+/* Encode UID and GID into an otherwise empty sattr. */
+int *
+xdr_encode_sattr_ids (int *p, u_int uid, u_int gid)
+{
+ if (protocol_version == 2)
+ {
+ *(p++) = -1; /* mode */
+ *(p++) = htonl (uid);
+ *(p++) = htonl (gid);
+ *(p++) = -1; /* size */
+ *(p++) = -1; /* atime secs */
+ *(p++) = -1; /* atime usecs */
+ *(p++) = -1; /* mtime secs */
+ *(p++) = -1; /* mtime usecs */
+ }
+ else
+ {
+ *(p++) = 0; /* no mode */
+ *(p++) = htonl (1); /* set uid */
+ *(p++) = htonl (uid);
+ *(p++) = htonl (1); /* set gid */
+ *(p++) = htonl (gid);
+ *(p++) = 0; /* no size */
+ *(p++) = DONT_CHANGE; /* no atime */
+ *(p++) = DONT_CHANGE; /* no mtime */
+ }
+ return p;
+}
+
+/* Encode a file size into an otherwise empty sattr. */
+int *
+xdr_encode_sattr_size (int *p, off_t size)
+{
+ if (protocol_version == 2)
+ {
+ *(p++) = -1; /* mode */
+ *(p++) = -1; /* uid */
+ *(p++) = -1; /* gid */
+ *(p++) = htonl (size);
+ *(p++) = -1; /* atime secs */
+ *(p++) = -1; /* atime usecs */
+ *(p++) = -1; /* mtime secs */
+ *(p++) = -1; /* mtime secs */
+ }
+ else
+ {
+ *(p++) = 0; /* no mode */
+ *(p++) = 0; /* no uid */
+ *(p++) = 0; /* no gid */
+ *(p++) = htonl (1); /* size */
+ p = xdr_encode_64bit (p, size);
+ *(p++) = DONT_CHANGE; /* no atime */
+ *(p++) = DONT_CHANGE; /* no mtime */
+ }
+ return p;
+}
+
+/* Encode ATIME and MTIME into an otherwise empty sattr. */
+int *
+xdr_encode_sattr_times (int *p, struct timespec *atime, struct timespec *mtime)
+{
+ if (protocol_version == 2)
+ {
+ *(p++) = -1; /* mode */
+ *(p++) = -1; /* uid */
+ *(p++) = -1; /* gid */
+ *(p++) = -1; /* size */
+ *(p++) = htonl (atime->tv_sec);
+ *(p++) = htonl (atime->tv_nsec / 1000);
+ *(p++) = htonl (mtime->tv_sec);
+ *(p++) = htonl (mtime->tv_nsec / 1000);
+ }
+ else
+ {
+ *(p++) = 0; /* no mode */
+ *(p++) = 0; /* no uid */
+ *(p++) = 0; /* no gid */
+ *(p++) = 0; /* no size */
+ *(p++) = htonl (SET_TO_CLIENT_TIME); /* atime */
+ *(p++) = htonl (atime->tv_sec);
+ *(p++) = htonl (atime->tv_nsec);
+ *(p++) = htonl (SET_TO_CLIENT_TIME); /* mtime */
+ *(p++) = htonl (mtime->tv_sec);
+ *(p++) = htonl (mtime->tv_nsec);
+ }
+ return p;
+}
+
+/* Encode MODE, a size of zero, and the specified owner into an
+ otherwise empty sattr. */
+int *
+xdr_encode_create_state (int *p,
+ mode_t mode,
+ uid_t owner)
+{
+ if (protocol_version == 2)
+ {
+ *(p++) = htonl (hurd_mode_to_nfs_mode (mode));
+ *(p++) = htonl (owner); /* uid */
+ *(p++) = -1; /* gid */
+ *(p++) = 0; /* size */
+ *(p++) = -1; /* atime sec */
+ *(p++) = -1; /* atime usec */
+ *(p++) = -1; /* mtime sec */
+ *(p++) = -1; /* mtime usec */
+ }
+ else
+ {
+ *(p++) = htonl (1); /* mode */
+ *(p++) = htonl (hurd_mode_to_nfs_mode (mode));
+ *(p++) = htonl (1); /* set uid */
+ *(p++) = htonl (owner);
+ *(p++) = 0; /* no gid */
+ *(p++) = htonl (1); /* set size */
+ p = xdr_encode_64bit (p, 0);
+ *(p++) = htonl (SET_TO_SERVER_TIME); /* atime */
+ *(p++) = htonl (SET_TO_SERVER_TIME); /* mtime */
+ }
+ return p;
+}
+
+/* Encode ST into an sattr. */
+int *
+xdr_encode_sattr_stat (int *p,
+ struct stat *st)
+{
+ if (protocol_version == 2)
+ {
+ *(p++) = htonl (hurd_mode_to_nfs_mode (st->st_mode));
+ *(p++) = htonl (st->st_uid);
+ *(p++) = htonl (st->st_gid);
+ *(p++) = htonl (st->st_size);
+ *(p++) = htonl (st->st_atim.tv_sec);
+ *(p++) = htonl (st->st_atim.tv_nsec / 1000);
+ *(p++) = htonl (st->st_mtim.tv_sec);
+ *(p++) = htonl (st->st_mtim.tv_nsec / 1000);
+ }
+ else
+ {
+ *(p++) = htonl (1); /* set mode */
+ *(p++) = htonl (hurd_mode_to_nfs_mode (st->st_mode));
+ *(p++) = htonl (1); /* set uid */
+ *(p++) = htonl (st->st_uid);
+ *(p++) = htonl (1); /* set gid */
+ *(p++) = htonl (st->st_gid);
+ *(p++) = htonl (1); /* set size */
+ p = xdr_encode_64bit (p, st->st_size);
+ *(p++) = htonl (SET_TO_CLIENT_TIME); /* set atime */
+ *(p++) = htonl (st->st_atim.tv_sec);
+ *(p++) = htonl (st->st_atim.tv_nsec);
+ *(p++) = htonl (SET_TO_CLIENT_TIME); /* set mtime */
+ *(p++) = htonl (st->st_mtim.tv_sec);
+ *(p++) = htonl (st->st_mtim.tv_nsec);
+ }
+ return p;
+}
+
+
+/* Decode *P into a long long; return the address of the following
+ data. */
+int *
+xdr_decode_64bit (int *p, long long *n)
+{
+ long long high, low;
+ high = ntohl (*p);
+ p++;
+ low = ntohl (*p);
+ p++;
+ *n = ((high & 0xffffffff) << 32) | (low & 0xffffffff);
+ return p;
+}
+
+/* Decode *P into an fhandle and look up the associated node. Return
+ the address of the following data. */
+int *
+xdr_decode_fhandle (int *p, struct node **npp)
+{
+ size_t len;
+
+ if (protocol_version == 2)
+ len = NFS2_FHSIZE;
+ else
+ {
+ len = ntohl (*p);
+ p++;
+ }
+ /* Enter into cache. */
+ lookup_fhandle (p, len, npp);
+ return p + len / sizeof (int);
+}
+
+/* Decode *P into a stat structure; return the address of the
+ following data. */
+int *
+xdr_decode_fattr (int *p, struct stat *st)
+{
+ int type, mode;
+
+ type = ntohl (*p);
+ p++;
+ mode = ntohl (*p);
+ p++;
+ st->st_mode = nfs_mode_to_hurd_mode (type, mode);
+ st->st_nlink = ntohl (*p);
+ p++;
+ st->st_uid = ntohl (*p);
+ p++;
+ st->st_gid = ntohl (*p);
+ p++;
+ if (protocol_version == 2)
+ {
+ st->st_size = ntohl (*p);
+ p++;
+ st->st_blksize = ntohl (*p);
+ p++;
+ st->st_rdev = ntohl (*p);
+ p++;
+ st->st_blocks = ntohl (*p);
+ p++;
+ }
+ else
+ {
+ long long size;
+ int major, minor;
+ p = xdr_decode_64bit (p, &size);
+ st->st_size = size;
+ p = xdr_decode_64bit (p, &size);
+ st->st_blocks = size / 512;
+ st->st_blksize = read_size < write_size ? read_size : write_size;
+ major = ntohl (*p);
+ p++;
+ minor = ntohl (*p);
+ p++;
+ st->st_rdev = makedev (major, minor);
+ }
+ st->st_fsid = ntohl (*p);
+ p++;
+ st->st_ino = ntohl (*p);
+ p++;
+ st->st_atim.tv_sec = ntohl (*p);
+ p++;
+ st->st_atim.tv_nsec = ntohl (*p);
+ p++;
+ st->st_mtim.tv_sec = ntohl (*p);
+ p++;
+ st->st_mtim.tv_nsec = ntohl (*p);
+ p++;
+ st->st_ctim.tv_sec = ntohl (*p);
+ p++;
+ st->st_ctim.tv_nsec = ntohl (*p);
+ p++;
+
+ if (protocol_version < 3)
+ {
+ st->st_atim.tv_nsec *= 1000;
+ st->st_mtim.tv_nsec *= 1000;
+ st->st_ctim.tv_nsec *= 1000;
+ }
+
+ return p;
+
+}
+
+/* Decode *P into a string, stored at BUF; return the address
+ of the following data. */
+int *
+xdr_decode_string (int *p, char *buf)
+{
+ int len;
+
+ len = ntohl (*p);
+ p++;
+ memcpy (buf, p, len);
+ buf[len] = '\0';
+ return p + INTSIZE (len);
+}
+
+
+/* Set up an RPC for procedure RPC_PROC for talking to the NFS server.
+ Allocate storage with malloc and point *BUFP at it; caller must
+ free this when done. Initialize RPC credential information with
+ information from CRED (identifying the user making this call; -1
+ means superuser), NP (identifying the node we are operating on), and
+ SECOND_GID (specifying another GID the server might be interested
+ in). Allocate at least LEN bytes of space for bulk data in
+ addition to the normal amount for an RPC. */
+int *
+nfs_initialize_rpc (int rpc_proc, struct iouser *cred,
+ size_t len, void **bufp, struct node *np,
+ uid_t second_gid)
+{
+ uid_t uid;
+ uid_t gid;
+ error_t err;
+
+ /* Use heuristics to figure out what ids to present to the server.
+ Don't lie, but adjust ids as necessary to secure the desired
+ result. */
+
+ if (cred == (struct iouser *) -1)
+ {
+ uid = gid = 0;
+ second_gid = -1;
+ }
+ else if (cred
+ && (cred->uids->num || cred->gids->num))
+ {
+ if (idvec_contains (cred->uids, 0))
+ {
+ err = netfs_validate_stat (np, 0);
+ uid = 0;
+ gid = err ? -2 : 0;
+ if (err)
+ printf ("NFS warning, internal stat failure\n");
+ }
+ else
+ {
+ if (cred->uids->num == 0)
+ uid = -2;
+ else if (cred->uids->num == 1)
+ uid = cred->uids->ids[0];
+ else
+ {
+ err = netfs_validate_stat (np, 0);
+ if (err)
+ {
+ uid = cred->uids->ids[0];
+ printf ("NFS warning, internal stat failure\n");
+ }
+ else
+ {
+ if (idvec_contains (cred->uids, np->nn_stat.st_uid))
+ uid = np->nn_stat.st_uid;
+ else
+ uid = cred->uids->ids[0];
+ }
+ }
+
+ if (cred->gids->num == 0)
+ {
+ gid = -2;
+ second_gid = -1;
+ }
+ else if (cred->gids->num == 1)
+ {
+ gid = cred->gids->ids[0];
+ second_gid = -1;
+ }
+ else
+ {
+ err = netfs_validate_stat (np, 0);
+ if (err)
+ {
+ gid = cred->gids->ids[0];
+ printf ("NFS warning, internal stat failure\n");
+ }
+ else
+ {
+ if (idvec_contains (cred->gids, np->nn_stat.st_gid))
+ gid = np->nn_stat.st_gid;
+ else
+ gid = cred->gids->ids[0];
+ }
+ if (second_gid != -1
+ && !idvec_contains (cred->gids, second_gid))
+ second_gid = -1;
+ }
+ }
+ }
+ else
+ uid = gid = second_gid = -1;
+
+ return initialize_rpc (NFS_PROGRAM, NFS_VERSION, rpc_proc, len, bufp,
+ uid, gid, second_gid);
+}
+
+/* ERROR is an NFS error code; return the correspending Hurd
+ error. */
+error_t
+nfs_error_trans (int error)
+{
+ switch (error)
+ {
+ case NFS_OK:
+ return 0;
+
+ case NFSERR_PERM:
+ return EPERM;
+
+ case NFSERR_NOENT:
+ return ENOENT;
+
+ case NFSERR_IO:
+ return EIO;
+
+ case NFSERR_NXIO:
+ return ENXIO;
+
+ case NFSERR_ACCES:
+ return EACCES;
+
+ case NFSERR_EXIST:
+ return EEXIST;
+
+ case NFSERR_NODEV:
+ return ENODEV;
+
+ case NFSERR_NOTDIR:
+ return ENOTDIR;
+
+ case NFSERR_ISDIR:
+ return EISDIR;
+
+ case NFSERR_FBIG:
+ return E2BIG;
+
+ case NFSERR_NOSPC:
+ return ENOSPC;
+
+ case NFSERR_ROFS:
+ return EROFS;
+
+ case NFSERR_NAMETOOLONG:
+ return ENAMETOOLONG;
+
+ case NFSERR_NOTEMPTY:
+ return ENOTEMPTY;
+
+ case NFSERR_DQUOT:
+ return EDQUOT;
+
+ case NFSERR_STALE:
+ return ESTALE;
+
+ case NFSERR_WFLUSH:
+ /* Not known in v3, but we just give EINVAL for unknown errors
+ so it's the same. */
+ return EINVAL;
+
+ default:
+ if (protocol_version == 2)
+ return EINVAL;
+ else
+ switch (error)
+ {
+ case NFSERR_XDEV:
+ return EXDEV;
+
+ case NFSERR_INVAL:
+ case NFSERR_REMOTE: /* not sure about this one */
+ default:
+ return EINVAL;
+
+ case NFSERR_MLINK:
+ return EMLINK;
+
+ case NFSERR_NOTSUPP:
+ case NFSERR_BADTYPE:
+ return EOPNOTSUPP;
+
+ case NFSERR_SERVERFAULT:
+ return EIO;
+
+ case NFSERR_BADHANDLE:
+ case NFSERR_NOT_SYNC:
+ case NFSERR_BAD_COOKIE:
+ case NFSERR_TOOSMALL:
+ case NFSERR_JUKEBOX: /* ??? */
+ /* These indicate bugs in the client, so EGRATUITOUS is right. */
+ return EGRATUITOUS;
+ }
+ }
+}
diff --git a/nfs/nfs.h b/nfs/nfs.h
new file mode 100644
index 00000000..18dec001
--- /dev/null
+++ b/nfs/nfs.h
@@ -0,0 +1,204 @@
+/* Data structures and global variables for NFS client
+ Copyright (C) 1994,95,96,97,99,2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef NFS_NFS_H
+#define NFS_NFS_H
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include "nfs-spec.h"
+#include <hurd/netfs.h>
+
+/* A file handle */
+struct fhandle
+{
+ size_t size;
+
+ /* Leave enough room for the largest possible fhandle. */
+ char data[NFS3_FHSIZE];
+};
+
+/* There exists one of there for the private data needed by each client
+ node. */
+struct netnode
+{
+ struct fhandle handle;
+ time_t stat_updated;
+ struct node *hnext, **hprevp;
+
+ /* These two fields handle translators set internally but
+ unknown to the server. */
+ enum
+ {
+ NOT_POSSIBLE,
+ POSSIBLE,
+ SYMLINK,
+ CHRDEV,
+ BLKDEV,
+ FIFO,
+ SOCK,
+ } dtrans;
+ union
+ {
+ char *name;
+ dev_t indexes;
+ } transarg;
+
+#ifdef notyet
+ /* This indicates that the length of the file must be at
+ least this big because we've written this much locally,
+ even if the server thinks we haven't gone this far. */
+ off_t extend_len;
+#endif
+
+ struct user_pager_info *fileinfo;
+
+ /* If this node has been renamed by "deletion" then
+ this is the directory and the name in that directory
+ which is holding the node */
+ struct node *dead_dir;
+ char *dead_name;
+};
+
+/* Socket file descriptor for talking to RPC servers. */
+int main_udp_socket;
+
+/* Our hostname */
+char *hostname;
+
+/* The current time */
+volatile struct mapped_time_value *mapped_time;
+
+/* Some tunable parameters */
+
+/* How long to keep around stat information */
+extern int stat_timeout;
+
+/* How long to keep around file contents caches */
+extern int cache_timeout;
+
+/* How long to keep around positive dir cache entries */
+extern int name_cache_timeout;
+
+/* How long to keep around negative dir cache entries */
+extern int name_cache_neg_timeout;
+
+/* How long to wait for replies before re-sending RPC's. */
+extern int initial_transmit_timeout;
+extern int max_transmit_timeout;
+
+/* How many attempts to send before giving up on soft mounts */
+extern int soft_retries;
+
+/* Whether we are a hard or soft mount */
+extern int mounted_soft;
+
+/* Maximum amount to read at once */
+extern int read_size;
+
+/* Maximum amout to write at once */
+extern int write_size;
+
+/* Service name for portmapper */
+extern char *pmap_service_name;
+
+/* If pmap_service is null, then this is the port to use for the portmapper;
+ if the lookup of pmap_service_name fails, use this number. */
+extern short pmap_service_number;
+
+/* RPC program for the mount agent */
+extern int mount_program;
+
+/* RPC program version for the mount agent */
+extern int mount_version;
+
+/* If this is nonzero, it's the port to use for the mount agent if
+ the portmapper fails or can't be found. */
+extern short mount_port;
+
+/* If this is nonzero use mount_port without attempting to contact
+ the portmapper */
+extern int mount_port_override;
+
+/* RPC program for the NFS server */
+extern int nfs_program;
+
+/* RPC program version for the NFS server */
+extern int nfs_version;
+
+/* If this is nonzero, it's the port to be used to find the nfs agent
+ if the portmapper fails or can't be found */
+extern short nfs_port;
+
+/* If this is nonzero use nfs_port without attepting to contact the
+ portmapper. */
+extern int nfs_port_override;
+
+/* Which NFS protocol version we are using */
+extern int protocol_version;
+
+
+/* Count how many four-byte chunks it takes to hold LEN bytes. */
+#define INTSIZE(len) (((len)+3)>>2)
+
+
+/* nfs.c */
+int hurd_mode_to_nfs_type (mode_t);
+int *xdr_encode_fhandle (int *, struct fhandle *);
+int *xdr_encode_data (int *, char *, size_t);
+int *xdr_encode_string (int *, char *);
+int *xdr_encode_sattr_mode (int *, mode_t);
+int *xdr_encode_sattr_ids (int *, u_int, u_int);
+int *xdr_encode_sattr_size (int *, off_t);
+int *xdr_encode_sattr_times (int *, struct timespec *, struct timespec *);
+int *xdr_encode_sattr_stat (int *, struct stat *);
+int *xdr_encode_create_state (int *, mode_t, uid_t);
+int *xdr_decode_fattr (int *, struct stat *);
+int *xdr_decode_string (int *, char *);
+int *xdr_decode_fhandle (int *, struct node **);
+int *nfs_initialize_rpc (int, struct iouser *, size_t, void **,
+ struct node *, uid_t);
+error_t nfs_error_trans (int);
+
+/* mount.c */
+struct node *mount_root (char *, char *);
+extern const char *mounted_hostname;
+extern uint16_t mounted_nfs_port; /* host order */
+
+/* ops.c */
+int *register_fresh_stat (struct node *, int *);
+
+/* rpc.c */
+int *initialize_rpc (int, int, int, size_t, void **, uid_t, gid_t, gid_t);
+error_t conduct_rpc (void **, int **);
+void *timeout_service_thread (void *);
+void *rpc_receive_thread (void *);
+
+/* cache.c */
+void lookup_fhandle (void *, size_t, struct node **);
+int *recache_handle (int *, struct node *);
+
+/* name-cache.c */
+void enter_lookup_cache (char *, size_t, struct node *, char *);
+void purge_lookup_cache (struct node *, char *, size_t);
+struct node *check_lookup_cache (struct node *, char *);
+void purge_lookup_cache_node (struct node *);
+
+#endif /* NFS_NFS_H */
diff --git a/nfs/ops.c b/nfs/ops.c
new file mode 100644
index 00000000..a4d6ac77
--- /dev/null
+++ b/nfs/ops.c
@@ -0,0 +1,1925 @@
+/* ops.c - Libnetfs callbacks for node operations in NFS client.
+ Copyright (C) 1994,95,96,97,99,2002,2011 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "nfs.h"
+#include <hurd/netfs.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <maptime.h>
+
+/* We have fresh stat information for NP; the file attribute (fattr)
+ structure is at P. Update our entry. Return the address of the next
+ int after the fattr structure. */
+int *
+register_fresh_stat (struct node *np, int *p)
+{
+ int *ret;
+
+ ret = xdr_decode_fattr (p, &np->nn_stat);
+ np->nn->stat_updated = mapped_time->seconds;
+
+ switch (np->nn->dtrans)
+ {
+ case NOT_POSSIBLE:
+ case POSSIBLE:
+ break;
+
+ case SYMLINK:
+ np->nn_stat.st_size = strlen (np->nn->transarg.name);
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFLNK);
+ break;
+
+ case CHRDEV:
+ np->nn_stat.st_rdev = np->nn->transarg.indexes;
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFCHR);
+ break;
+
+ case BLKDEV:
+ np->nn_stat.st_rdev = np->nn->transarg.indexes;
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFBLK);
+ break;
+
+ case FIFO:
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFIFO);
+ break;
+
+ case SOCK:
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFSOCK);
+ break;
+ }
+
+ np->nn_stat.st_fsid = getpid ();
+ np->nn_stat.st_fstype = FSTYPE_NFS;
+ np->nn_stat.st_gen = 0;
+ np->nn_stat.st_author = np->nn_stat.st_uid;
+ np->nn_stat.st_flags = 0;
+ np->nn_translated = np->nn_stat.st_mode & S_IFMT;
+
+ return ret;
+}
+
+/* Handle returned wcc information for various calls. In protocol
+ version 2, this is just register_fresh_stat. In version 3, it
+ checks to see if stat information is present too. If this follows
+ an operation that we expect has modified the attributes, MOD should
+ be set. (This unpacks the post_op_attr XDR type.) */
+int *
+process_returned_stat (struct node *np, int *p, int mod)
+{
+ if (protocol_version == 2)
+ return register_fresh_stat (np, p);
+ else
+ {
+ int attrs_exist;
+
+ attrs_exist = ntohl (*p);
+ p++;
+ if (attrs_exist)
+ p = register_fresh_stat (np, p);
+ else if (mod)
+ /* We know that our values are now wrong */
+ np->nn->stat_updated = 0;
+ return p;
+ }
+}
+
+
+/* Handle returned wcc information for various calls. In protocol
+ version 2, this is just register_fresh_stat. In version 3, it does
+ the wcc_data interpretation too. If this follows an operation that
+ we expect has modified the attributes, MOD should be set.
+ (This unpacks the wcc_data XDR type.) */
+int *
+process_wcc_stat (struct node *np, int *p, int mod)
+{
+ if (protocol_version == 2)
+ return register_fresh_stat (np, p);
+ else
+ {
+ int attrs_exist;
+
+ /* First the pre_op_attr */
+ attrs_exist = ntohl (*p);
+ p++;
+ if (attrs_exist)
+ {
+ /* Just skip them for now */
+ p += 2 * sizeof (int); /* size */
+ p += 2 * sizeof (int); /* mtime */
+ p += 2 * sizeof (int); /* atime */
+ }
+
+ /* Now the post_op_attr */
+ return process_returned_stat (np, p, mod);
+ }
+}
+
+
+/* Implement the netfs_validate_stat callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_validate_stat (struct node *np, struct iouser *cred)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ if (mapped_time->seconds - np->nn->stat_updated < stat_timeout)
+ return 0;
+
+ p = nfs_initialize_rpc (NFSPROC_GETATTR (protocol_version),
+ (struct iouser *) -1, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+ if (!err)
+ register_fresh_stat (np, p);
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_chown callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_chown (struct iouser *cred, struct node *np,
+ uid_t uid, gid_t gid)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
+ cred, 0, &rpcbuf, np, gid);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_sattr_ids (p, uid, gid);
+ if (protocol_version == 3)
+ *(p++) = 0; /* guard_check == 0 */
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
+ free (rpcbuf);
+
+ return err;
+}
+
+/* Implement the netfs_attempt_chauthor callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_chauthor (struct iouser *cred, struct node *rp,
+ uid_t author)
+{
+ return EOPNOTSUPP;
+}
+
+/* Implement the netfs_attempt_chmod callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_chmod (struct iouser *cred, struct node *np,
+ mode_t mode)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ if ((mode & S_IFMT) != 0)
+ {
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ return err;
+
+ /* Has the file type changed? (e.g. from symlink to
+ directory). */
+ if ((mode & S_IFMT) != (np->nn_stat.st_mode & S_IFMT))
+ {
+ char *f = 0;
+
+ if (np->nn->dtrans == NOT_POSSIBLE)
+ return EOPNOTSUPP;
+
+ if (np->nn->dtrans == SYMLINK)
+ f = np->nn->transarg.name;
+
+ switch (mode & S_IFMT)
+ {
+ default:
+ return EOPNOTSUPP;
+
+ case S_IFIFO:
+ np->nn->dtrans = FIFO;
+ np->nn->stat_updated = 0;
+ break;
+
+ case S_IFSOCK:
+ np->nn->dtrans = SOCK;
+ np->nn->stat_updated = 0;
+ }
+ if (f)
+ free (f);
+ return 0;
+ }
+ }
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_sattr_mode (p, mode);
+ if (protocol_version == 3)
+ *(p++) = 0; /* guard check == 0 */
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_chflags callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_chflags (struct iouser *cred, struct node *np,
+ int flags)
+{
+ return EOPNOTSUPP;
+}
+
+/* Implement the netfs_attempt_utimes callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_utimes (struct iouser *cred, struct node *np,
+ struct timespec *atime, struct timespec *mtime)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ struct timeval tv;
+ struct timespec current;
+
+ /* XXX For version 3 we can actually do this right, but we don't
+ just yet. */
+ if (!atime || !mtime)
+ {
+ maptime_read (mapped_time, &tv);
+ current.tv_sec = tv.tv_sec;
+ current.tv_nsec = tv.tv_usec * 1000;
+ }
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_sattr_times (p,
+ atime ?: &current,
+ mtime ?: &current);
+ if (protocol_version == 3)
+ *(p++) = 0; /* guard check == 0 */
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_set_size callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_set_size (struct iouser *cred, struct node *np,
+ off_t size)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_sattr_size (p, size);
+ if (protocol_version == 3)
+ *(p++) = 0; /* guard_check == 0 */
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
+ /* If we got EACCES, but the user has the file open for writing,
+ then the NFS protocol has screwed us. There's nothing we can do,
+ except in the important case of opens with
+ O_TRUNC|O_CREAT|O_WRONLY|O_EXCL where the new mode does not allow
+ writing. RCS, for example, uses this to create lock files. So permit
+ cases where the O_TRUNC isn't doing anything to succeed if the user
+ does have the file open for writing. */
+ if (err == EACCES)
+ {
+ int error = netfs_validate_stat (np, cred);
+ if (!error && np->nn_stat.st_size == size)
+ err = 0;
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_statfs callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_statfs (struct iouser *cred, struct node *np,
+ struct statfs *st)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ p = nfs_initialize_rpc (NFS2PROC_STATFS, cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+
+ if (!err)
+ {
+ p++; /* skip IOSIZE field */
+ st->f_bsize = ntohl (*p);
+ p++;
+ st->f_blocks = ntohl (*p);
+ p++;
+ st->f_bfree = ntohl (*p);
+ p++;
+ st->f_bavail = ntohl (*p);
+ p++;
+ st->f_type = FSTYPE_NFS;
+ st->f_files = 0;
+ st->f_ffree = 0;
+ st->f_fsid = getpid ();
+ st->f_namelen = 0;
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_sync callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_sync (struct iouser *cred, struct node *np, int wait)
+{
+ /* We are already completely synchronous. */
+ return 0;
+}
+
+/* Implement the netfs_attempt_syncfs callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+ return 0;
+}
+
+/* Implement the netfs_attempt_read callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_read (struct iouser *cred, struct node *np,
+ off_t offset, size_t *len, void *data)
+{
+ int *p;
+ void *rpcbuf;
+ size_t trans_len;
+ error_t err;
+ size_t amt, thisamt;
+ int eof;
+
+ for (amt = *len; amt;)
+ {
+ thisamt = amt;
+ if (thisamt > read_size)
+ thisamt = read_size;
+
+ p = nfs_initialize_rpc (NFSPROC_READ (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ *(p++) = htonl (offset);
+ *(p++) = htonl (thisamt);
+ if (protocol_version == 2)
+ *(p++) = 0;
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+
+ if (!err || protocol_version == 3)
+ p = process_returned_stat (np, p, !err);
+
+ if (err)
+ {
+ free (rpcbuf);
+ return err;
+ }
+
+ trans_len = ntohl (*p);
+ p++;
+ if (trans_len > thisamt)
+ trans_len = thisamt; /* ??? */
+
+ if (protocol_version == 3)
+ {
+ eof = ntohl (*p);
+ p++;
+ }
+ else
+ eof = (trans_len < thisamt);
+
+ memcpy (data, p, trans_len);
+ free (rpcbuf);
+
+ data += trans_len;
+ offset += trans_len;
+ amt -= trans_len;
+
+ if (eof)
+ {
+ *len -= amt;
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Implement the netfs_attempt_write callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_write (struct iouser *cred, struct node *np,
+ off_t offset, size_t *len, void *data)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ size_t amt, thisamt;
+ size_t count;
+
+ for (amt = *len; amt;)
+ {
+ thisamt = amt;
+ if (thisamt > write_size)
+ thisamt = write_size;
+
+ p = nfs_initialize_rpc (NFSPROC_WRITE (protocol_version),
+ cred, thisamt, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ if (protocol_version == 2)
+ *(p++) = 0;
+ *(p++) = htonl (offset);
+ if (protocol_version == 2)
+ *(p++) = 0;
+ if (protocol_version == 3)
+ *(p++) = htonl (FILE_SYNC);
+ p = xdr_encode_data (p, data, thisamt);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ if (!err)
+ {
+ if (protocol_version == 3)
+ {
+ count = ntohl (*p);
+ p++;
+ p++; /* ignore COMMITTED */
+ /* ignore verf for now */
+ p += NFS3_WRITEVERFSIZE / sizeof (int);
+ }
+ else
+ /* assume it wrote the whole thing */
+ count = thisamt;
+
+ amt -= count;
+ data += count;
+ offset += count;
+ }
+ }
+
+ free (rpcbuf);
+
+ if (err == EINTR && amt != *len)
+ {
+ *len -= amt;
+ return 0;
+ }
+
+ if (err)
+ {
+ *len = 0;
+ return err;
+ }
+ }
+ return 0;
+}
+
+/* See if NAME exists in DIR for CRED. If so, return EEXIST. */
+error_t
+verify_nonexistent (struct iouser *cred, struct node *dir,
+ char *name)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ /* Don't use the lookup cache for this; we want a full sync to
+ get as close to real exclusive create behavior as possible. */
+
+ assert (protocol_version == 2);
+
+ p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+
+ free (rpcbuf);
+
+ if (!err)
+ return EEXIST;
+ else
+ return 0;
+}
+
+/* Implement the netfs_attempt_lookup callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_lookup (struct iouser *cred, struct node *np,
+ char *name, struct node **newnp)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ char dirhandle[NFS3_FHSIZE];
+ size_t dirlen;
+
+ /* Check the cache first. */
+ *newnp = check_lookup_cache (np, name);
+ if (*newnp)
+ {
+ if (*newnp == (struct node *) -1)
+ {
+ *newnp = 0;
+ return ENOENT;
+ }
+ else
+ return 0;
+ }
+
+ p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ /* Remember the directory handle for later cache use. */
+
+ dirlen = np->nn->handle.size;
+ memcpy (dirhandle, np->nn->handle.data, dirlen);
+
+ pthread_mutex_unlock (&np->lock);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err)
+ {
+ p = xdr_decode_fhandle (p, newnp);
+ p = process_returned_stat (*newnp, p, 1);
+ }
+ if (err)
+ *newnp = 0;
+ if (protocol_version == 3)
+ {
+ if (*newnp)
+ pthread_mutex_unlock (&(*newnp)->lock);
+ pthread_mutex_lock (&np->lock);
+ p = process_returned_stat (np, p, 0); /* XXX Do we have to lock np? */
+ pthread_mutex_unlock (&np->lock);
+ if (*newnp)
+ pthread_mutex_lock (&(*newnp)->lock);
+ }
+ }
+ else
+ *newnp = 0;
+
+ /* Notify the cache of the hit or miss. */
+ enter_lookup_cache (dirhandle, dirlen, *newnp, name);
+
+ free (rpcbuf);
+
+ return err;
+}
+
+/* Implement the netfs_attempt_mkdir callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_mkdir (struct iouser *cred, struct node *np,
+ char *name, mode_t mode)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ uid_t owner;
+ struct node *newnp;
+
+ if (cred->uids->num)
+ owner = cred->uids->ids[0];
+ else
+ {
+ err = netfs_validate_stat (np, cred);
+ owner = err ? 0 : np->nn_stat.st_uid;
+ mode &= ~S_ISUID;
+ }
+
+ purge_lookup_cache (np, name, strlen (name));
+
+ p = nfs_initialize_rpc (NFSPROC_MKDIR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_string (p, name);
+ p = xdr_encode_create_state (p, mode, owner);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+
+ if (!err)
+ {
+ p = xdr_decode_fhandle (p, &newnp);
+ p = process_returned_stat (newnp, p, 1);
+
+ /* Did we set the owner correctly? If not, try, but ignore failures. */
+ if (!netfs_validate_stat (newnp, (struct iouser *) -1)
+ && newnp->nn_stat.st_uid != owner)
+ netfs_attempt_chown ((struct iouser *) -1, newnp, owner,
+ newnp->nn_stat.st_gid);
+
+ /* We don't actually return this. */
+ netfs_nput (newnp);
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_rmdir callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_rmdir (struct iouser *cred, struct node *np,
+ char *name)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ /* Should we do the same sort of thing here as with attempt_unlink? */
+
+ purge_lookup_cache (np, name, strlen (name));
+
+ p = nfs_initialize_rpc (NFSPROC_RMDIR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_link callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_link (struct iouser *cred, struct node *dir,
+ struct node *np, char *name, int excl)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err = 0;
+
+ if (!excl)
+ {
+ /* We have no RPC available that will do an atomic replacement,
+ so we settle for second best; just doing an unlink and ignoring
+ any errors. */
+ pthread_mutex_lock (&dir->lock);
+ netfs_attempt_unlink (cred, dir, name);
+ pthread_mutex_unlock (&dir->lock);
+ }
+
+ /* If we have postponed a translator setting on an unlinked node,
+ then here's where we set it, by creating the new node instead of
+ doing a normal link. */
+
+ switch (np->nn->dtrans)
+ {
+ case POSSIBLE:
+ case NOT_POSSIBLE:
+ pthread_mutex_lock (&dir->lock);
+ p = nfs_initialize_rpc (NFSPROC_LINK (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ {
+ pthread_mutex_unlock (&dir->lock);
+ return errno;
+ }
+
+ pthread_mutex_unlock (&dir->lock);
+
+ pthread_mutex_lock (&np->lock);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ pthread_mutex_unlock (&np->lock);
+
+ pthread_mutex_lock (&dir->lock);
+ purge_lookup_cache (dir, name, strlen (name));
+
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+ pthread_mutex_unlock (&dir->lock);
+
+ free (rpcbuf);
+
+ break;
+
+ case SYMLINK:
+ pthread_mutex_lock (&dir->lock);
+ p = nfs_initialize_rpc (NFSPROC_SYMLINK (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ {
+ pthread_mutex_unlock (&dir->lock);
+ return errno;
+ }
+
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ pthread_mutex_unlock (&dir->lock);
+
+ p = xdr_encode_string (p, name);
+
+ pthread_mutex_lock (&np->lock);
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ free (rpcbuf);
+ return err;
+ }
+
+ if (protocol_version == 2)
+ {
+ p = xdr_encode_string (p, np->nn->transarg.name);
+ p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ }
+ else
+ {
+ p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ p = xdr_encode_string (p, np->nn->transarg.name);
+ }
+ pthread_mutex_unlock (&np->lock);
+
+ pthread_mutex_lock (&dir->lock);
+
+ purge_lookup_cache (dir, name, strlen (name));
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+
+ if (protocol_version == 2 && !err)
+ {
+ free (rpcbuf);
+
+ /* NFSPROC_SYMLINK stupidly does not pass back an
+ fhandle, so we have to fetch one now. */
+ p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ {
+ pthread_mutex_unlock (&dir->lock);
+ return errno;
+ }
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ pthread_mutex_unlock (&dir->lock);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+ if (!err)
+ {
+ pthread_mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ p = process_returned_stat (np, p, 1);
+ pthread_mutex_unlock (&np->lock);
+ }
+ if (err)
+ err = EGRATUITOUS; /* damn */
+ }
+ else if (protocol_version == 3)
+ {
+ if (!err)
+ {
+ pthread_mutex_unlock (&dir->lock);
+ pthread_mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ p = process_returned_stat (np, p, 1);
+ pthread_mutex_unlock (&np->lock);
+ pthread_mutex_lock (&dir->lock);
+ }
+ p = process_wcc_stat (dir, p, !err);
+ pthread_mutex_unlock (&dir->lock);
+ }
+ else
+ pthread_mutex_unlock (&dir->lock);
+ }
+ else
+ pthread_mutex_unlock (&dir->lock);
+
+ free (rpcbuf);
+ break;
+
+ case CHRDEV:
+ case BLKDEV:
+ case FIFO:
+ case SOCK:
+
+ if (protocol_version == 2)
+ {
+ pthread_mutex_lock (&dir->lock);
+ err = verify_nonexistent (cred, dir, name);
+ if (err)
+ return err;
+
+ p = nfs_initialize_rpc (NFSPROC_CREATE (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ {
+ pthread_mutex_unlock (&dir->lock);
+ return errno;
+ }
+
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+ pthread_mutex_unlock (&dir->lock);
+
+ pthread_mutex_lock (&np->lock);
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ free (rpcbuf);
+ return err;
+ }
+
+ p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ pthread_mutex_unlock (&np->lock);
+
+ pthread_mutex_lock (&dir->lock);
+ purge_lookup_cache (dir, name, strlen (name));
+ pthread_mutex_unlock (&dir->lock); /* XXX Should this really be after the
+ _lengthy_ (blocking) conduct_rpc? */
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+
+ if (!err)
+ {
+ pthread_mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ register_fresh_stat (np, p);
+ pthread_mutex_unlock (&np->lock);
+ }
+
+ free (rpcbuf);
+ }
+ else /* protocol_version != 2 */
+ {
+ pthread_mutex_lock (&dir->lock);
+ p = nfs_initialize_rpc (NFS3PROC_MKNOD, cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ {
+ pthread_mutex_unlock (&dir->lock);
+ return errno;
+ }
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+ pthread_mutex_unlock (&dir->lock);
+
+ pthread_mutex_lock (&np->lock);
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ free (rpcbuf);
+ return err;
+ }
+ *(p++) = htonl (hurd_mode_to_nfs_type (np->nn_stat.st_mode));
+ p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ if (np->nn->dtrans == BLKDEV || np->nn->dtrans == CHRDEV)
+ {
+ *(p++) = htonl (major (np->nn_stat.st_rdev));
+ *(p++) = htonl (minor (np->nn_stat.st_rdev));
+ }
+ pthread_mutex_unlock (&np->lock);
+
+ purge_lookup_cache (dir, name, strlen (name));
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+
+ if (!err)
+ {
+ pthread_mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ p = process_returned_stat (np, p, 1);
+ pthread_mutex_unlock (&np->lock);
+ }
+ pthread_mutex_lock (&dir->lock);
+ p = process_wcc_stat (dir, p, !err);
+ pthread_mutex_unlock (&dir->lock);
+ }
+ free (rpcbuf);
+ }
+ break;
+ }
+
+ if (err)
+ return err;
+
+ pthread_mutex_lock (&np->lock);
+
+ if (np->nn->dtrans == SYMLINK)
+ free (np->nn->transarg.name);
+ np->nn->dtrans = NOT_POSSIBLE;
+
+ /* If there is a dead-dir tag lying around, it's time to delete it now. */
+ if (np->nn->dead_dir)
+ {
+ struct node *dir = np->nn->dead_dir;
+ char *name = np->nn->dead_name;
+ np->nn->dead_dir = 0;
+ np->nn->dead_name = 0;
+ pthread_mutex_unlock (&np->lock);
+
+ pthread_mutex_lock (&dir->lock);
+ netfs_attempt_unlink ((struct iouser *)-1, dir, name);
+ pthread_mutex_unlock (&dir->lock);
+ }
+ else
+ pthread_mutex_unlock (&np->lock);
+
+ return 0;
+}
+
+/* Implement the netfs_attempt_mkfile callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_mkfile (struct iouser *cred, struct node *dir,
+ mode_t mode, struct node **newnp)
+{
+ error_t err;
+ char *name;
+ static int n = 0;
+
+ /* This is the best we can do. */
+
+ name = malloc (50);
+ if (! name)
+ return ENOMEM;
+
+ do
+ {
+ sprintf (name, ".nfstmpgnu.%d", n++);
+ err = netfs_attempt_create_file (cred, dir, name, mode, newnp);
+ if (err == EEXIST)
+ pthread_mutex_lock (&dir->lock); /* XXX is this right? does create need this
+ and drop this on error? Doesn't look
+ like it. */
+ }
+ while (err == EEXIST);
+
+ if (err)
+ {
+ free (name);
+ return err;
+ }
+
+ assert (!(*newnp)->nn->dead_dir);
+ assert (!(*newnp)->nn->dead_name);
+ netfs_nref (dir);
+ (*newnp)->nn->dead_dir = dir;
+ (*newnp)->nn->dead_name = name;
+ if ((*newnp)->nn->dtrans == NOT_POSSIBLE)
+ (*newnp)->nn->dtrans = POSSIBLE;
+ return 0;
+}
+
+/* Implement the netfs_attempt_create_file callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_create_file (struct iouser *cred, struct node *np,
+ char *name, mode_t mode, struct node **newnp)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ uid_t owner;
+
+ if (cred->uids->num)
+ owner = cred->uids->ids[0];
+ else
+ {
+ err = netfs_validate_stat (np, cred);
+ owner = err ? 0 : np->nn_stat.st_uid;
+ mode &= ~S_ISUID;
+ }
+
+ /* RFC 1094 says that create is always exclusive. But Sun doesn't
+ actually *implement* the spec. No, of course not. So we have to do
+ it for them. */
+ if (protocol_version == 2)
+ {
+ err = verify_nonexistent (cred, np, name);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return err;
+ }
+ }
+
+ purge_lookup_cache (np, name, strlen (name));
+
+ p = nfs_initialize_rpc (NFSPROC_CREATE (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_string (p, name);
+ if (protocol_version == 3)
+ {
+ /* We happen to know this is where the XID is. */
+ int verf = *(int *)rpcbuf;
+
+ *(p++) = ntohl (EXCLUSIVE);
+ /* 8 byte verf */
+ *(p++) = ntohl (verf);
+ p++;
+ }
+ else
+ p = xdr_encode_create_state (p, mode, owner);
+
+ err = conduct_rpc (&rpcbuf, &p);
+
+ pthread_mutex_unlock (&np->lock);
+
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err)
+ {
+ p = xdr_decode_fhandle (p, newnp);
+ p = process_returned_stat (*newnp, p, 1);
+ }
+ if (err)
+ *newnp = 0;
+ if (protocol_version == 3)
+ {
+ if (*newnp)
+ pthread_mutex_unlock (&(*newnp)->lock);
+ pthread_mutex_lock (&np->lock);
+ p = process_wcc_stat (np, p, 1);
+ pthread_mutex_unlock (&np->lock);
+ if (*newnp)
+ pthread_mutex_lock (&(*newnp)->lock);
+ }
+
+ if (*newnp && !netfs_validate_stat (*newnp, (struct iouser *) -1)
+ && (*newnp)->nn_stat.st_uid != owner)
+ netfs_attempt_chown ((struct iouser *) -1, *newnp, owner, (*newnp)->nn_stat.st_gid);
+ }
+ else
+ *newnp = 0;
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_unlink callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_unlink (struct iouser *cred, struct node *dir,
+ char *name)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ struct node *np;
+
+ /* First lookup the node being removed */
+ err = netfs_attempt_lookup (cred, dir, name, &np);
+ if (err)
+ {
+ pthread_mutex_lock (&dir->lock);
+ return err;
+ }
+
+ /* Restore the locks to sanity. */
+ pthread_mutex_unlock (&np->lock);
+ pthread_mutex_lock (&dir->lock);
+
+ /* Purge the cache of entries for this node, so that we don't
+ regard cache-held references as live. */
+ purge_lookup_cache_node (np);
+
+ /* See if there are any other users of this node than the
+ one we just got; if so, we must give this file another link
+ so that when we delete the one we are asked for it doesn't go
+ away entirely. */
+ if (np->references > 1)
+ {
+ char *newname = 0;
+ int n = 0;
+
+ pthread_mutex_unlock (&dir->lock);
+
+ newname = malloc (50);
+ if (! newname)
+ {
+ pthread_mutex_lock (&dir->lock);
+ netfs_nrele (np); /* XXX Is this the correct thing to do? */
+ return ENOMEM;
+ }
+
+ do
+ {
+ sprintf (newname, ".nfs%txgnu.%d", (ptrdiff_t) np, n++);
+ err = netfs_attempt_link (cred, dir, np, newname, 1);
+ }
+ while (err == EEXIST);
+
+ if (err)
+ {
+ free (newname);
+ pthread_mutex_lock (&dir->lock);
+ netfs_nrele (np);
+ return err;
+ }
+
+ /* Write down what name we gave it; we'll delete this when all
+ our uses vanish. */
+ pthread_mutex_lock (&np->lock);
+
+ if (np->nn->dead_dir)
+ netfs_nrele (np->nn->dead_dir);
+ netfs_nref (dir);
+ np->nn->dead_dir = dir;
+ if (np->nn->dead_name)
+ free (np->nn->dead_name);
+ np->nn->dead_name = newname;
+ if (np->nn->dtrans == NOT_POSSIBLE)
+ np->nn->dtrans = POSSIBLE;
+
+ netfs_nput (np);
+ pthread_mutex_lock (&dir->lock);
+ }
+ else
+ netfs_nrele (np);
+
+ p = nfs_initialize_rpc (NFSPROC_REMOVE (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (protocol_version == 3)
+ p = process_wcc_stat (dir, p, !err);
+ }
+
+ free (rpcbuf);
+
+ return err;
+}
+
+/* Implement the netfs_attempt_rename callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_rename (struct iouser *cred, struct node *fromdir,
+ char *fromname, struct node *todir, char *toname,
+ int excl)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ if (excl)
+ {
+ struct node *np;
+
+ /* Just do a lookup/link/unlink sequence. */
+
+ pthread_mutex_lock (&fromdir->lock);
+ err = netfs_attempt_lookup (cred, fromdir, fromname, &np);
+ pthread_mutex_unlock (&fromdir->lock);
+ if (err)
+ return err;
+
+ err = netfs_attempt_link (cred, todir, np, toname, 1);
+ netfs_nput (np);
+ if (err)
+ return err;
+
+ pthread_mutex_lock (&fromdir->lock);
+ err = netfs_attempt_unlink (cred, fromdir, fromname);
+ pthread_mutex_unlock (&fromdir->lock);
+
+ /* If the unlink failed, then back out the link */
+ if (err)
+ {
+ pthread_mutex_lock (&todir->lock);
+ netfs_attempt_unlink (cred, todir, toname);
+ pthread_mutex_unlock (&todir->lock);
+ return err;
+ }
+
+ return 0;
+ }
+
+ pthread_mutex_lock (&fromdir->lock);
+ purge_lookup_cache (fromdir, fromname, strlen (fromname));
+ p = nfs_initialize_rpc (NFSPROC_RENAME (protocol_version),
+ cred, 0, &rpcbuf, fromdir, -1);
+ if (! p)
+ {
+ pthread_mutex_unlock (&fromdir->lock);
+ return errno;
+ }
+
+ p = xdr_encode_fhandle (p, &fromdir->nn->handle);
+ p = xdr_encode_string (p, fromname);
+ pthread_mutex_unlock (&fromdir->lock);
+
+ pthread_mutex_lock (&todir->lock);
+ purge_lookup_cache (todir, toname, strlen (toname));
+ p = xdr_encode_fhandle (p, &todir->nn->handle);
+ p = xdr_encode_string (p, toname);
+ pthread_mutex_unlock (&todir->lock);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (protocol_version == 3) /* XXX Should we add `&& !err' ? */
+ {
+ pthread_mutex_lock (&fromdir->lock);
+ p = process_wcc_stat (fromdir, p, !err);
+ p = process_wcc_stat (todir, p, !err);
+ }
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_readlink callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_readlink (struct iouser *cred, struct node *np,
+ char *buf)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ if (np->nn->dtrans == SYMLINK)
+ {
+ strcpy (buf, np->nn->transarg.name);
+ return 0;
+ }
+
+ p = nfs_initialize_rpc (NFSPROC_READLINK (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (protocol_version == 3)
+ p = process_returned_stat (np, p, 0);
+ if (!err)
+ p = xdr_decode_string (p, buf);
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_check_open_permissions callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_check_open_permissions (struct iouser *cred, struct node *np,
+ int flags, int newnode)
+{
+ int modes;
+
+ if (newnode || (flags & (O_READ|O_WRITE|O_EXEC)) == 0)
+ return 0;
+
+ netfs_report_access (cred, np, &modes);
+ if ((flags & (O_READ|O_WRITE|O_EXEC)) == (flags & modes))
+ return 0;
+ else
+ return EACCES;
+}
+
+/* Implement the netfs_report_access callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_report_access (struct iouser *cred,
+ struct node *np,
+ int *types)
+{
+ error_t err;
+
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ return err;
+
+ if (protocol_version == 2)
+ {
+ /* Hope the server means the same thing for the bits as we do. */
+ *types = 0;
+ if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0)
+ *types |= O_READ;
+ if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0)
+ *types |= O_WRITE;
+ if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0)
+ *types |= O_EXEC;
+ return 0;
+ }
+ else
+ {
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ int ret;
+ int write_check, execute_check;
+
+ if (S_ISDIR (np->nn_stat.st_mode))
+ {
+ write_check = ACCESS3_MODIFY | ACCESS3_DELETE | ACCESS3_EXTEND;
+ execute_check = ACCESS3_LOOKUP;
+ }
+ else
+ {
+ write_check = ACCESS3_MODIFY;
+ execute_check = ACCESS3_EXECUTE;
+ }
+
+ p = nfs_initialize_rpc (NFS3PROC_ACCESS, cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ *(p++) = htonl (ACCESS3_READ | write_check | execute_check);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ p = process_returned_stat (np, p, 0); /* XXX Should this be
+ protected by the
+ if (!err) ? */
+ if (!err)
+ {
+ ret = ntohl (*p);
+ p++;
+ *types = ((ret & ACCESS3_READ ? O_READ : 0)
+ | (ret & write_check ? O_WRITE : 0)
+ | (ret & execute_check ? O_EXEC : 0));
+ }
+ }
+ return err;
+ }
+}
+
+/* These definitions have unfortunate side effects, don't use them,
+ clever though they are. */
+#if 0
+/* Implement the netfs_check_open_permissions callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_check_open_permissions (struct iouser *cred, struct node *np,
+ int flags, int newnode)
+{
+ char byte;
+ error_t err;
+ size_t len;
+
+ /* Sun derived nfs client implementations attempt to reproduce the
+ server's permission restrictions by hoping they look like Unix,
+ and using that to give errors at open time. Sadly, that loses
+ here. So instead what we do is try and do what the user
+ requested; if we can't, then we fail. Otherwise, we allow the
+ open, but acknowledge that the server might still give an error
+ later. (Even with our check, the server can revoke access, thus
+ violiting Posix semantics; this means that erring on the side of
+ permitting illegal opens won't harm otherwise correct programs,
+ because they need to deal with revocation anyway.) We thus here
+ have the advantage of working correctly with servers that allow
+ things Unix denies. */
+
+ if ((flags & O_READ) == 0
+ && (flags & O_WRITE) == 0
+ && (flags & O_EXEC) == 0)
+ return 0;
+
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ return err;
+
+ switch (np->nn_stat.st_mode & S_IFMT)
+ {
+ /* Don't know how to check, so return provisional success. */
+ default:
+ return 0;
+
+ case S_IFREG:
+ len = 1;
+ err = netfs_attempt_read (cred, np, 0, &len, &byte);
+ if (err)
+ {
+ if ((flags & O_READ) || (flags & O_EXEC))
+ return err;
+ else
+ /* If we couldn't read a byte, but the user wasn't actually asking
+ for read, then we shouldn't inhibit the open now. */
+ return 0;
+ }
+
+ if (len != 1)
+ /* The file is empty; reads are known to be OK, but writes can't be
+ tested, so no matter what, return success. */
+ return 0;
+
+ if (flags & O_WRITE)
+ {
+ err = netfs_attempt_write (cred, np, 0, &len, &byte);
+ return err;
+ }
+
+ /* Try as we might, we couldn't get the server to bump us, so
+ give (provisional) success. */
+ return 0;
+
+ case S_IFDIR:
+ if (flags & O_READ)
+ {
+ void *rpcbuf;
+ int *p;
+
+ /* Issue a readdir request; if it fails, then we can
+ return failure. Otherwise, succeed. */
+ p = nfs_initialize_rpc (NFSPROC_READDIR, cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ *(p++) = 0;
+ *(p++) = htonl (50);
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+ free (rpcbuf);
+
+ if (err)
+ return err;
+ }
+ return 0;
+ }
+}
+
+/* Implement the netfs_report_access callback as described in
+ <hurd/netfs.h>. */
+void
+netfs_report_access (struct iouser *cred,
+ struct node *np,
+ int *types)
+{
+ char byte;
+ error_t err;
+ size_t len;
+
+ /* Much the same logic as netfs_check_open_permissions, except that
+ here we err on the side of denying access, and that we always
+ have to check everything. */
+
+ *types = 0;
+
+ len = 1;
+ err = netfs_attempt_read (cred, np, 0, &len, &byte);
+ if (err)
+ return;
+ assert (len == 1 || len == 0);
+
+ *types |= O_READ | O_EXEC;
+
+ if (len == 1)
+ {
+ err = netfs_attempt_write (cred, np, 0, &len, &byte);
+ if (!err)
+ *types |= O_WRITE;
+ }
+ else
+ {
+ /* Oh, ugh. We have to try and write a byte and then truncate
+ back. God help us if the file becomes unwritable in-between.
+ But because of the use of this function (by setuid programs
+ wanting to see if they should write user's files) we must
+ check this and not just return a presumptive error. */
+ byte = 0;
+ err = netfs_attempt_write (cred, np, 0, &len, &byte);
+ if (!err)
+ *types |= O_WRITE;
+ netfs_attempt_set_size (cred, np, 0);
+ }
+}
+#endif
+
+/* Fetch the complete contents of DIR into a buffer of directs. Set
+ *BUFP to that buffer. *BUFP must be freed by the caller when no
+ longer needed. If an error occurs, don't touch *BUFP and return
+ the error code. Set BUFSIZEP to the amount of data used inside
+ *BUFP and TOTALENTRIES to the total number of entries copied. */
+static error_t
+fetch_directory (struct iouser *cred, struct node *dir,
+ void **bufp, size_t *bufsizep, int *totalentries)
+{
+ void *buf;
+ int cookie;
+ int *p;
+ void *rpcbuf;
+ struct dirent *entry;
+ void *bp;
+ int bufmalloced;
+ int eof;
+ error_t err;
+ int isnext;
+
+ bufmalloced = read_size;
+
+ buf = malloc (bufmalloced);
+ if (! buf)
+ return ENOMEM;
+
+ bp = buf;
+ cookie = 0;
+ eof = 0;
+ *totalentries = 0;
+
+ while (!eof)
+ {
+ /* Fetch new directory entries */
+ p = nfs_initialize_rpc (NFSPROC_READDIR (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ {
+ free (buf);
+ return errno;
+ }
+
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ *(p++) = cookie;
+ *(p++) = ntohl (read_size);
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+ if (err)
+ {
+ free (buf);
+ return err;
+ }
+
+ isnext = ntohl (*p);
+ p++;
+
+ /* Now copy them one at a time. */
+ while (isnext)
+ {
+ ino_t fileno;
+ int namlen;
+ int reclen;
+
+ fileno = ntohl (*p);
+ p++;
+ namlen = ntohl (*p);
+ p++;
+
+ /* There's a hidden +1 here for the null byte and -1 because d_name
+ has a size of one already in the sizeof. */
+ reclen = sizeof (struct dirent) + namlen;
+ reclen = (reclen + 3) & ~3; /* make it a multiple of four */
+
+ /* Expand buffer if necessary */
+ if (bp + reclen > buf + bufmalloced)
+ {
+ char *newbuf;
+
+ newbuf = realloc (buf, bufmalloced *= 2);
+ assert (newbuf);
+ if (newbuf != buf)
+ bp = newbuf + (bp - buf);
+ buf = newbuf;
+ }
+
+ /* Fill in new entry */
+ entry = (struct dirent *) bp;
+ entry->d_fileno = fileno;
+ entry->d_reclen = reclen;
+ entry->d_type = DT_UNKNOWN;
+ entry->d_namlen = namlen;
+ memcpy (entry->d_name, p, namlen);
+ entry->d_name[namlen] = '\0';
+
+ p += INTSIZE (namlen);
+ bp = bp + entry->d_reclen;
+
+ ++*totalentries;
+
+ cookie = *(p++);
+ isnext = ntohl (*p);
+ p++;
+ }
+
+ eof = ntohl (*p);
+ p++;
+ free (rpcbuf);
+ }
+
+ /* Return it all to the user */
+ *bufp = buf;
+ *bufsizep = bufmalloced;
+ return 0;
+}
+
+
+/* Implement the netfs_get_directs callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_get_dirents (struct iouser *cred, struct node *np,
+ int entry, int nentries, char **data,
+ mach_msg_type_number_t *datacnt,
+ vm_size_t bufsiz, int *amt)
+{
+ void *buf;
+ size_t our_bufsiz, allocsize;
+ void *bp;
+ char *userdp;
+ error_t err;
+ int totalentries;
+ int thisentry;
+
+ err = fetch_directory (cred, np, &buf, &our_bufsiz, &totalentries);
+ if (err)
+ return err;
+
+ /* Allocate enough space to hold the maximum we might return. */
+ if (!bufsiz || bufsiz > our_bufsiz)
+ allocsize = round_page (our_bufsiz);
+ else
+ allocsize = round_page (bufsiz);
+ if (allocsize > *datacnt)
+ *data = mmap (0, allocsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+
+ /* Skip ahead to the correct entry. */
+ bp = buf;
+ for (thisentry = 0; thisentry < entry;)
+ {
+ struct dirent *entry = (struct dirent *) bp;
+ bp += entry->d_reclen;
+ thisentry++;
+ }
+
+ /* Now copy them one at a time */
+ {
+ int entries_copied;
+
+ for (entries_copied = 0, userdp = *data;
+ (nentries == -1 || entries_copied < nentries)
+ && (!bufsiz || userdp - *data < bufsiz)
+ && thisentry < totalentries;)
+ {
+ struct dirent *entry = (struct dirent *) bp;
+ memcpy (userdp, bp, entry->d_reclen);
+ bp += entry->d_reclen;
+ userdp += entry->d_reclen;
+ entries_copied++;
+ thisentry++;
+ }
+ *amt = entries_copied;
+ }
+
+ free (buf);
+
+ /* If we allocated the buffer ourselves, but didn't use
+ all the pages, free the extra. */
+ if (allocsize > *datacnt
+ && round_page (userdp - *data) < round_page (allocsize))
+ munmap ((caddr_t) round_page (userdp),
+ round_page (allocsize) - round_page (userdp - *data));
+
+ *datacnt = userdp - *data;
+ return 0;
+}
+
+
+/* Implement the netfs_attempt_mksymlink callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_mksymlink (struct iouser *cred,
+ struct node *np,
+ char *arg)
+{
+ if (np->nn->dtrans == NOT_POSSIBLE)
+ return EOPNOTSUPP;
+
+ if (np->nn->dtrans == SYMLINK)
+ free (np->nn->transarg.name);
+
+ np->nn->transarg.name = malloc (strlen (arg) + 1);
+ strcpy (np->nn->transarg.name, arg);
+ np->nn->dtrans = SYMLINK;
+ np->nn->stat_updated = 0;
+ return 0;
+}
+
+/* Implement the netfs_attempt_mkdev callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_mkdev (struct iouser *cred,
+ struct node *np,
+ mode_t type,
+ dev_t indexes)
+{
+ if (np->nn->dtrans == NOT_POSSIBLE)
+ return EOPNOTSUPP;
+
+ if (np->nn->dtrans == SYMLINK)
+ free (np->nn->transarg.name);
+
+ np->nn->transarg.indexes = indexes;
+ if (type == S_IFBLK)
+ np->nn->dtrans = BLKDEV;
+ else
+ np->nn->dtrans = CHRDEV;
+ np->nn->stat_updated = 0;
+ return 0;
+}
diff --git a/nfs/rpc.c b/nfs/rpc.c
new file mode 100644
index 00000000..c0d0290e
--- /dev/null
+++ b/nfs/rpc.c
@@ -0,0 +1,425 @@
+/* rpc.c - SunRPC management for NFS client.
+ Copyright (C) 1994, 1995, 1996, 1997, 2002 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "nfs.h"
+
+/* Needed in order to get the RPC header files to include correctly. */
+#undef TRUE
+#undef FALSE
+#define malloc spoiufasdf /* Avoid bogus definition in rpc/types.h. */
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/rpc_msg.h>
+#include <rpc/auth_unix.h>
+
+#undef malloc /* Get rid of the sun block. */
+
+#include <netinet/in.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <unistd.h>
+#include <stdio.h>
+
+/* One of these exists for each pending RPC. */
+struct rpc_list
+{
+ struct rpc_list *next, **prevp;
+ void *reply;
+};
+
+/* A list of all pending RPCs. */
+static struct rpc_list *outstanding_rpcs;
+
+/* Wake up this condition when an outstanding RPC has received a reply
+ or we should check for timeouts. */
+static pthread_cond_t rpc_wakeup = PTHREAD_COND_INITIALIZER;
+
+/* Lock the global data and the REPLY fields of outstanding RPC's. */
+static pthread_mutex_t outstanding_lock = PTHREAD_MUTEX_INITIALIZER;
+
+
+
+/* Generate and return a new transaction ID. */
+static inline int
+generate_xid ()
+{
+ static int nextxid;
+
+ if (nextxid == 0)
+ nextxid = mapped_time->seconds;
+
+ return nextxid++;
+}
+
+/* Set up an RPC for procdeure RPC_PROC for talking to the server
+ PROGRAM of version VERSION. Allocate storage with malloc and point
+ *BUF at it; caller must free this when done. Allocate at least LEN
+ bytes more than the usual amount for the RPC. Initialize the RPC
+ credential structure with UID, GID, and SECOND_GID; any of these
+ may be -1 to indicate that it does not apply, however, exactly zero
+ or two of UID and GID must be -1. The returned address is a pointer
+ to the start of the payload. If NULL is returned, an error occurred
+ and the code is set in errno. */
+int *
+initialize_rpc (int program, int version, int rpc_proc,
+ size_t len, void **bufp,
+ uid_t uid, gid_t gid, gid_t second_gid)
+{
+ void *buf;
+ int *p, *lenaddr;
+ struct rpc_list *hdr;
+
+ buf = malloc (len + 1024);
+ if (! buf)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* First the struct rpc_list bit. */
+ hdr = buf;
+ hdr->reply = 0;
+
+ p = buf + sizeof (struct rpc_list);
+
+ /* RPC header */
+ *(p++) = htonl (generate_xid ());
+ *(p++) = htonl (CALL);
+ *(p++) = htonl (RPC_MSG_VERSION);
+ *(p++) = htonl (program);
+ *(p++) = htonl (version);
+ *(p++) = htonl (rpc_proc);
+
+ assert ((uid == -1) == (gid == -1));
+
+ if (uid == -1)
+ {
+ /* No authentication */
+ *(p++) = htonl (AUTH_NONE);
+ *(p++) = 0;
+ }
+ else
+ {
+ /* Unixy authentication */
+ *(p++) = htonl (AUTH_UNIX);
+ /* The length of the message. We do not yet know what this
+ is, so, just remember where we should put it when we know */
+ lenaddr = p++;
+ *(p++) = htonl (mapped_time->seconds);
+ p = xdr_encode_string (p, hostname);
+ *(p++) = htonl (uid);
+ *(p++) = htonl (gid);
+ if (second_gid == -1)
+ *(p++) = 0;
+ else
+ {
+ *(p++) = htonl (1);
+ *(p++) = htonl (second_gid);
+ }
+ *lenaddr = htonl ((p - (lenaddr + 1)) * sizeof (int));
+ }
+
+ /* VERF field */
+ *(p++) = htonl (AUTH_NONE);
+ *(p++) = 0;
+
+ *bufp = buf;
+ return p;
+}
+
+/* Remove HDR from the list of pending RPC's. The rpc_list's lock
+ (OUTSTANDING_LOCK) must be held. */
+static inline void
+unlink_rpc (struct rpc_list *hdr)
+{
+ *hdr->prevp = hdr->next;
+ if (hdr->next)
+ hdr->next->prevp = hdr->prevp;
+}
+
+/* Insert HDR at the head of the LIST. The rpc_list's lock
+ (OUTSTANDING_LOCK) must be held. */
+static inline void
+link_rpc (struct rpc_list **list, struct rpc_list *hdr)
+{
+ hdr->next = *list;
+ if (hdr->next)
+ hdr->next->prevp = &hdr->next;
+ hdr->prevp = list;
+ *list = hdr;
+}
+
+/* Send the specified RPC message. *RPCBUF is the initialized buffer
+ from a previous initialize_rpc call; *PP, the payload, points past
+ the filledin args. Set *PP to the address of the reply contents
+ themselves. The user will be expected to free *RPCBUF (which will
+ have changed) when done with the reply contents. The old value of
+ *RPCBUF will be freed by this routine. */
+error_t
+conduct_rpc (void **rpcbuf, int **pp)
+{
+ struct rpc_list *hdr = *rpcbuf;
+ error_t err;
+ size_t cc, nc;
+ int timeout = initial_transmit_timeout;
+ time_t lasttrans;
+ int ntransmit = 0;
+ int *p;
+ int xid;
+ int n;
+ int cancel;
+
+ pthread_mutex_lock (&outstanding_lock);
+
+ link_rpc (&outstanding_rpcs, hdr);
+
+ xid = * (int *) (*rpcbuf + sizeof (struct rpc_list));
+
+ do
+ {
+ /* If we've sent enough, give up. */
+ if (mounted_soft && ntransmit == soft_retries)
+ {
+ unlink_rpc (hdr);
+ pthread_mutex_unlock (&outstanding_lock);
+ return ETIMEDOUT;
+ }
+
+ /* Issue the RPC. */
+ lasttrans = mapped_time->seconds;
+ ntransmit++;
+ nc = (void *) *pp - *rpcbuf - sizeof (struct rpc_list);
+ cc = write (main_udp_socket, *rpcbuf + sizeof (struct rpc_list), nc);
+ if (cc == -1)
+ {
+ unlink_rpc (hdr);
+ pthread_mutex_unlock (&outstanding_lock);
+ return errno;
+ }
+ else
+ assert (cc == nc);
+
+ /* Wait for reply. */
+ cancel = 0;
+ while (!hdr->reply
+ && (mapped_time->seconds - lasttrans < timeout)
+ && !cancel)
+ cancel = pthread_hurd_cond_wait_np (&rpc_wakeup, &outstanding_lock);
+
+ if (cancel)
+ {
+ unlink_rpc (hdr);
+ pthread_mutex_unlock (&outstanding_lock);
+ return EINTR;
+ }
+
+ /* hdr->reply will have been filled in by rpc_receive_thread,
+ if it has been filled in, then the rpc has been fulfilled,
+ otherwise, retransmit and continue to wait. */
+ if (!hdr->reply)
+ {
+ timeout *= 2;
+ if (timeout > max_transmit_timeout)
+ timeout = max_transmit_timeout;
+ }
+ }
+ while (!hdr->reply);
+
+ pthread_mutex_unlock (&outstanding_lock);
+
+ /* Switch to the reply buffer. */
+ *rpcbuf = hdr->reply;
+ free (hdr);
+
+ /* Process the reply, dissecting errors. When we're done and if
+ there is no error, set *PP to the rpc return contents. */
+ p = (int *) *rpcbuf;
+
+ /* If the transmition id does not match that in the message,
+ something strange happened in rpc_receive_thread. */
+ assert (*p == xid);
+ p++;
+
+ switch (ntohl (*p))
+ {
+ default:
+ err = EBADRPC;
+ break;
+
+ case REPLY:
+ p++;
+ switch (ntohl (*p))
+ {
+ default:
+ err = EBADRPC;
+ break;
+
+ case MSG_DENIED:
+ p++;
+ switch (ntohl (*p))
+ {
+ default:
+ err = EBADRPC;
+ break;
+
+ case RPC_MISMATCH:
+ err = ERPCMISMATCH;
+ break;
+
+ case AUTH_ERROR:
+ p++;
+ switch (ntohl (*p))
+ {
+ case AUTH_BADCRED:
+ case AUTH_REJECTEDCRED:
+ err = EAUTH;
+ break;
+
+ case AUTH_TOOWEAK:
+ err = ENEEDAUTH;
+ break;
+
+ case AUTH_BADVERF:
+ case AUTH_REJECTEDVERF:
+ default:
+ err = EBADRPC;
+ break;
+ }
+ break;
+ }
+ break;
+
+ case MSG_ACCEPTED:
+ p++;
+
+ /* Process VERF field. */
+ p++; /* Skip verf type. */
+ n = ntohl (*p); /* Size of verf. */
+ p++;
+ p += INTSIZE (n); /* Skip verf itself. */
+
+ switch (ntohl (*p))
+ {
+ default:
+ case GARBAGE_ARGS:
+ err = EBADRPC;
+ break;
+
+ case PROG_UNAVAIL:
+ err = EPROGUNAVAIL;
+ break;
+
+ case PROG_MISMATCH:
+ err = EPROGMISMATCH;
+ break;
+
+ case PROC_UNAVAIL:
+ err = EPROCUNAVAIL;
+ break;
+
+ case SUCCESS:
+ p++;
+ *pp = p;
+ err = 0;
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ return err;
+}
+
+/* Dedicated thread to signal those waiting on rpc_wakeup
+ once a second. */
+void *
+timeout_service_thread (void *arg)
+{
+ (void) arg;
+
+ while (1)
+ {
+ sleep (1);
+ pthread_mutex_lock (&outstanding_lock);
+ pthread_cond_broadcast (&rpc_wakeup);
+ pthread_mutex_unlock (&outstanding_lock);
+ }
+
+ return NULL;
+}
+
+/* Dedicate thread to receive RPC replies, register them on the queue
+ of pending wakeups, and deal appropriately. */
+void *
+rpc_receive_thread (void *arg)
+{
+ void *buf;
+
+ (void) arg;
+
+ /* Allocate a receive buffer. */
+ buf = malloc (1024 + read_size);
+ assert (buf);
+
+ while (1)
+ {
+ int cc = read (main_udp_socket, buf, 1024 + read_size);
+ if (cc == -1)
+ {
+ error (0, errno, "nfs read");
+ continue;
+ }
+ else
+ {
+ struct rpc_list *r;
+ int xid = *(int *)buf;
+
+ pthread_mutex_lock (&outstanding_lock);
+
+ /* Find the rpc that we just fulfilled. */
+ for (r = outstanding_rpcs; r; r = r->next)
+ {
+ if (* (int *) &r[1] == xid)
+ {
+ unlink_rpc (r);
+ r->reply = buf;
+ pthread_cond_broadcast (&rpc_wakeup);
+ break;
+ }
+ }
+#if 0
+ if (! r)
+ fprintf (stderr, "NFS dropping reply xid %d\n", xid);
+#endif
+ pthread_mutex_unlock (&outstanding_lock);
+
+ /* If r is not null then we had a message from a pending
+ (i.e. known) rpc. Thus, it was fulfilled and if we want
+ to get another request, a new buffer is needed. */
+ if (r)
+ {
+ buf = malloc (1024 + read_size);
+ assert (buf);
+ }
+ }
+ }
+
+ return NULL;
+}
diff --git a/nfs/storage-info.c b/nfs/storage-info.c
new file mode 100644
index 00000000..7427b3d8
--- /dev/null
+++ b/nfs/storage-info.c
@@ -0,0 +1,104 @@
+/* file_get_storage_info RPC for NFS client filesystem
+ Copyright (C) 2001,02 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "nfs.h"
+#include <hurd/netfs.h>
+#include <stdio.h>
+
+error_t
+netfs_file_get_storage_info (struct iouser *cred,
+ struct node *np,
+ mach_port_t **ports,
+ mach_msg_type_name_t *ports_type,
+ mach_msg_type_number_t *num_ports,
+ int **ints,
+ mach_msg_type_number_t *num_ints,
+ off_t **offsets,
+ mach_msg_type_number_t *num_offsets,
+ char **data,
+ mach_msg_type_number_t *data_len)
+{
+ int name_len, fhpos;
+ error_t err;
+
+ inline int fmt (size_t buflen)
+ {
+ return snprintf (*data, buflen,
+ "nfsv%u://%s:%u/%n%*c?rsize=%u&wsize=%u",
+ protocol_version, mounted_hostname, mounted_nfs_port,
+ &fhpos, (int) (np->nn->handle.size * 2),
+ 'X', /* filled below */
+ read_size, write_size);
+ }
+
+ /* We return the file size, so make sure we have it up to date now. */
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ return err;
+
+ /* Format the name, and then do it again if the buffer was too short. */
+ name_len = fmt (*data_len);
+ if (name_len < 0)
+ return errno;
+ ++name_len; /* Include the terminating null. */
+ if (name_len <= *data_len)
+ *data_len = name_len;
+ else
+ {
+ *data = mmap (0, name_len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ return errno;
+ *data_len = fmt (name_len) + 1;
+ assert (*data_len == name_len);
+ }
+
+ /* Now fill in the file handle data in hexadecimal. */
+ {
+ static const char hexdigits[] = "0123456789abcdef";
+ size_t i;
+ for (i = 0; i < np->nn->handle.size; ++i)
+ {
+ (*data)[fhpos++] = hexdigits[(uint8_t)np->nn->handle.data[i] >> 4];
+ (*data)[fhpos++] = hexdigits[(uint8_t)np->nn->handle.data[i] & 0xf];
+ }
+ }
+
+ /* Now fill in the rest of the canonical-form storage-info data, which
+ just describes a single run of the file's size, a block-size of one
+ byte, and our URL as the name for the network store type. */
+
+ *num_ports = 0;
+ *ports_type = MACH_MSG_TYPE_COPY_SEND;
+
+ assert (*num_offsets >= 2); /* mig always gives us some */
+ *num_offsets = 2;
+ (*offsets)[0] = 0;
+ (*offsets)[1] = np->nn_stat.st_size;
+
+ assert (*num_ints >= 6); /* mig always gives us some */
+ *num_ints = 1;
+ (*ints)[0] = STORAGE_NETWORK;
+ (*ints)[1] = 0; /* XXX readonly if we supported it */
+ (*ints)[2] = 1; /* block size */
+ (*ints)[3] = 1; /* 1 run in offsets list */
+ (*ints)[4] = name_len;
+ (*ints)[5] = 0; /* misc len */
+
+ return 0;
+}
diff --git a/nfsd/Makefile b/nfsd/Makefile
new file mode 100644
index 00000000..6ac6dd4f
--- /dev/null
+++ b/nfsd/Makefile
@@ -0,0 +1,32 @@
+# Copyright (C) 1996, 2012 Free Software Foundation, Inc.
+# Written by Michael I. Bushnell, p/BSG.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+
+dir := nfsd
+makemode := utility
+
+SRCS = cache.c loop.c main.c ops.c fsys.c xdr.c
+OBJS = $(subst .c,.o,$(SRCS))
+target = nfsd
+installationdir = $(sbindir)
+HURDLIBS = shouldbeinlibc
+OTHERLIBS = -lpthread
+
+include ../Makeconf
+
+CPPFLAGS += -DLOCALSTATEDIR=\"$(localstatedir)\"
diff --git a/nfsd/cache.c b/nfsd/cache.c
new file mode 100644
index 00000000..778f5570
--- /dev/null
+++ b/nfsd/cache.c
@@ -0,0 +1,575 @@
+/* cache.c - Cache operations for the nfs daemon.
+ Copyright (C) 1996,98,99,2000,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include <string.h>
+#include <sys/mman.h>
+#include <hurd/fsys.h>
+#include <assert.h>
+#include <string.h>
+#include <pthread.h>
+#include <hurd/io.h>
+#include <hurd/auth.h>
+#include <mach.h>
+#include "nfsd.h"
+
+
+#undef TRUE
+#undef FALSE
+#define malloc spoogie_woogie /* ugh^2. */
+#include <rpc/types.h>
+#include <rpc/auth.h>
+#undef malloc
+
+#define IDHASH_TABLE_SIZE 1024
+#define FHHASH_TABLE_SIZE 1024
+#define REPLYHASH_TABLE_SIZE 1024
+
+
+static struct idspec *idhashtable[IDHASH_TABLE_SIZE];
+pthread_spinlock_t idhashlock = PTHREAD_SPINLOCK_INITIALIZER;
+static int nfreeids;
+static int leastidlastuse;
+
+/* Compare I against the specified set of users/groups. */
+/* Use of int in decl of UIDS and GIDS is correct here; that's
+ the NFS type because they come in in known 32 bit slots. */
+static int
+idspec_compare (struct idspec *i, int nuids, int ngids,
+ int *uids, int *gids)
+{
+ if (i->nuids != nuids
+ || i->ngids != ngids)
+ return 0;
+
+ assert (sizeof (int) == sizeof (uid_t));
+
+ if (bcmp (i->uids, uids, nuids * sizeof (uid_t))
+ || bcmp (i->gids, gids, ngids * sizeof (gid_t)))
+ return 0;
+
+ return 1;
+}
+
+/* Compute a hash value for a given user spec. */
+static int
+idspec_hash (int nuids, int ngids, int *uids, int *gids)
+{
+ int hash, n;
+
+ hash = nuids + ngids;
+ for (n = 0; n < ngids; n++)
+ hash += gids[n];
+ for (n = 0; n < nuids; n++)
+ hash += uids[n];
+ hash %= IDHASH_TABLE_SIZE;
+ return hash;
+}
+
+/* Lookup a user spec in the hash table and allocate a reference. */
+static struct idspec *
+idspec_lookup (int nuids, int ngids, int *uids, int *gids)
+{
+ int hash;
+ struct idspec *i;
+
+ hash = idspec_hash (nuids, ngids, uids, gids);
+
+ pthread_spin_lock (&idhashlock);
+ for (i = idhashtable[hash]; i; i = i->next)
+ if (idspec_compare (i, nuids, ngids, uids, gids))
+ {
+ i->references++;
+ if (i->references == 1)
+ nfreeids--;
+ pthread_spin_unlock (&idhashlock);
+ return i;
+ }
+
+ assert (sizeof (uid_t) == sizeof (int));
+ i = malloc (sizeof (struct idspec));
+ i->nuids = nuids;
+ i->ngids = ngids;
+ i->uids = malloc (nuids * sizeof (uid_t));
+ i->gids = malloc (ngids * sizeof (gid_t));
+ memcpy (i->uids, uids, nuids * sizeof (uid_t));
+ memcpy (i->gids, gids, ngids * sizeof (gid_t));
+ i->references = 1;
+
+ i->next = idhashtable[hash];
+ if (idhashtable[hash])
+ idhashtable[hash]->prevp = &i->next;
+ i->prevp = &idhashtable[hash];
+ idhashtable[hash] = i;
+
+ pthread_spin_unlock (&idhashlock);
+ return i;
+}
+
+int *
+process_cred (int *p, struct idspec **credp)
+{
+ int type;
+ int len;
+ int *uid;
+ int *gids;
+ int ngids;
+ int firstgid;
+ int i;
+
+ type = ntohl (*p);
+ p++;
+
+ if (type != AUTH_UNIX)
+ {
+ int size = ntohl (*p);
+ p++;
+ *credp = idspec_lookup (0, 0, 0, 0);
+ p += INTSIZE (size);
+ }
+ else
+ {
+ p++; /* Skip size. */
+ p++; /* Skip seconds. */
+ len = ntohl (*p);
+ p++;
+ p += INTSIZE (len); /* Skip hostname. */
+
+ uid = p++; /* Remember location of uid. */
+ *uid = ntohl (*uid);
+
+ firstgid = *(p++); /* Remember first gid. */
+ gids = p; /* Here is where the array will start. */
+ ngids = ntohl (*p);
+ p++;
+
+ /* Now swap the first gid to be the first element of the
+ array. */
+ *gids = firstgid;
+ ngids++; /* And count it. */
+
+ /* And byteswap the gids. */
+ for (i = 0; i < ngids; i++)
+ gids[i] = ntohl (gids[i]);
+
+ p += ngids - 1;
+
+ *credp = idspec_lookup (1, ngids, uid, gids);
+ }
+
+ /* Next is the verf field; skip it entirely. */
+ p++; /* Skip ID. */
+ len = htonl (*p);
+ p++;
+ p += INTSIZE (len);
+
+ return p;
+}
+
+void
+cred_rele (struct idspec *i)
+{
+ pthread_spin_lock (&idhashlock);
+ i->references--;
+ if (i->references == 0)
+ {
+ i->lastuse = mapped_time->seconds;
+ if (i->lastuse < leastidlastuse || nfreeids == 0)
+ leastidlastuse = i->lastuse;
+ nfreeids++;
+ }
+ pthread_spin_unlock (&idhashlock);
+}
+
+void
+cred_ref (struct idspec *i)
+{
+ pthread_spin_lock (&idhashlock);
+ assert (i->references);
+ i->references++;
+ pthread_spin_unlock (&idhashlock);
+}
+
+void
+scan_creds ()
+{
+ int n;
+ int newleast = mapped_time->seconds;
+
+ pthread_spin_lock (&idhashlock);
+
+ if (mapped_time->seconds - leastidlastuse > ID_KEEP_TIMEOUT)
+ {
+ for (n = 0; n < IDHASH_TABLE_SIZE && nfreeids; n++)
+ {
+ struct idspec *i = idhashtable[n];
+
+ while (i && nfreeids)
+ {
+ struct idspec *next_i = i->next;
+
+ if (!i->references
+ && mapped_time->seconds - i->lastuse > ID_KEEP_TIMEOUT)
+ {
+ nfreeids--;
+ *i->prevp = i->next;
+ if (i->next)
+ i->next->prevp = i->prevp;
+ free (i->uids);
+ free (i->gids);
+ free (i);
+ }
+ else if (!i->references && newleast > i->lastuse)
+ newleast = i->lastuse;
+
+ i = next_i;
+ }
+ }
+
+ /* If we didn't bail early, then this is valid. */
+ if (nfreeids)
+ leastidlastuse = newleast;
+ }
+ pthread_spin_unlock (&idhashlock);
+}
+
+
+
+static struct cache_handle *fhhashtable[FHHASH_TABLE_SIZE];
+pthread_mutex_t fhhashlock = PTHREAD_MUTEX_INITIALIZER;
+static int nfreefh;
+static int leastfhlastuse;
+
+static int
+fh_hash (char *fhandle, struct idspec *i)
+{
+ int hash = 0, n;
+
+ for (n = 0; n < NFS2_FHSIZE; n++)
+ hash += fhandle[n];
+ hash += (intptr_t) i >> 6;
+ return hash % FHHASH_TABLE_SIZE;
+}
+
+int *
+lookup_cache_handle (int *p, struct cache_handle **cp, struct idspec *i)
+{
+ int hash;
+ struct cache_handle *c;
+ fsys_t fsys;
+ file_t port;
+
+ hash = fh_hash ((char *)p, i);
+ pthread_mutex_lock (&fhhashlock);
+ for (c = fhhashtable[hash]; c; c = c->next)
+ if (c->ids == i && ! bcmp (c->handle, p, NFS2_FHSIZE))
+ {
+ if (c->references == 0)
+ nfreefh--;
+ c->references++;
+ pthread_mutex_unlock (&fhhashlock);
+ *cp = c;
+ return p + NFS2_FHSIZE / sizeof (int);
+ }
+
+ /* Not found. */
+
+ /* First four bytes are our internal table of filesystems. */
+ fsys = lookup_filesystem (*p);
+ if (fsys == MACH_PORT_NULL
+ || fsys_getfile (fsys, i->uids, i->nuids, i->gids, i->ngids,
+ (char *)(p + 1), NFS2_FHSIZE - sizeof (int), &port))
+ {
+ pthread_mutex_unlock (&fhhashlock);
+ *cp = 0;
+ return p + NFS2_FHSIZE / sizeof (int);
+ }
+
+ c = malloc (sizeof (struct cache_handle));
+ memcpy (c->handle, p, NFS2_FHSIZE);
+ cred_ref (i);
+ c->ids = i;
+ c->port = port;
+ c->references = 1;
+
+ c->next = fhhashtable[hash];
+ if (c->next)
+ c->next->prevp = &c->next;
+ c->prevp = &fhhashtable[hash];
+ fhhashtable[hash] = c;
+
+ pthread_mutex_unlock (&fhhashlock);
+ *cp = c;
+ return p + NFS2_FHSIZE / sizeof (int);
+}
+
+void
+cache_handle_rele (struct cache_handle *c)
+{
+ pthread_mutex_lock (&fhhashlock);
+ c->references--;
+ if (c->references == 0)
+ {
+ c->lastuse = mapped_time->seconds;
+ if (c->lastuse < leastfhlastuse || nfreefh == 0)
+ leastfhlastuse = c->lastuse;
+ nfreefh++;
+ }
+ pthread_mutex_unlock (&fhhashlock);
+}
+
+void
+scan_fhs ()
+{
+ int n;
+ int newleast = mapped_time->seconds;
+
+ pthread_mutex_lock (&fhhashlock);
+
+ if (mapped_time->seconds - leastfhlastuse > FH_KEEP_TIMEOUT)
+ {
+ for (n = 0; n < FHHASH_TABLE_SIZE && nfreefh; n++)
+ {
+ struct cache_handle *c = fhhashtable[n];
+
+ while (c && nfreefh)
+ {
+ struct cache_handle *next_c = c->next;
+
+ if (!c->references
+ && mapped_time->seconds - c->lastuse > FH_KEEP_TIMEOUT)
+ {
+ nfreefh--;
+ *c->prevp = c->next;
+ if (c->next)
+ c->next->prevp = c->prevp;
+ cred_rele (c->ids);
+ mach_port_deallocate (mach_task_self (), c->port);
+ free (c);
+ }
+ else if (!c->references && newleast > c->lastuse)
+ newleast = c->lastuse;
+
+ c = next_c;
+ }
+ }
+
+ /* If we didn't bail early, then this is valid. */
+ if (nfreefh)
+ leastfhlastuse = newleast;
+ }
+ pthread_mutex_unlock (&fhhashlock);
+}
+
+struct cache_handle *
+create_cached_handle (int fs, struct cache_handle *credc, file_t userport)
+{
+ char fhandle[NFS2_FHSIZE];
+ error_t err;
+ struct cache_handle *c;
+ int hash;
+ char *bp = fhandle + sizeof (int);
+ size_t handlelen = NFS2_FHSIZE - sizeof (int);
+ mach_port_t newport, ref;
+
+ /* Authenticate USERPORT so that we can call file_getfh on it. */
+ ref = mach_reply_port ();
+ /* MAKE_SEND is safe becaue we destroy REF ourselves. */
+ if (io_reauthenticate (userport, ref, MACH_MSG_TYPE_MAKE_SEND)
+ || auth_user_authenticate (authserver, ref, MACH_MSG_TYPE_MAKE_SEND,
+ &newport))
+ {
+ /* Reauthentication has failed, but maybe the filesystem will let
+ us call file_getfh anyway. */
+ newport = userport;
+ }
+ else
+ mach_port_deallocate (mach_task_self (), userport);
+ mach_port_destroy (mach_task_self (), ref);
+
+ /* Fetch the file handle. */
+ *(int *)fhandle = fs;
+ err = file_getfh (newport, &bp, &handlelen);
+ mach_port_deallocate (mach_task_self (), newport);
+ if (err || handlelen != NFS2_FHSIZE - sizeof (int))
+ return 0;
+ if (bp != fhandle + sizeof (int))
+ {
+ memcpy (fhandle + sizeof (int), bp, NFS2_FHSIZE - sizeof (int));
+ munmap (bp, handlelen);
+ }
+
+ /* Cache it. */
+ hash = fh_hash (fhandle, credc->ids);
+ pthread_mutex_lock (&fhhashlock);
+ for (c = fhhashtable[hash]; c; c = c->next)
+ if (c->ids == credc->ids && ! bcmp (fhandle, c->handle, NFS2_FHSIZE))
+ {
+ /* Return this one. */
+ if (c->references == 0)
+ nfreefh--;
+ c->references++;
+ pthread_mutex_unlock (&fhhashlock);
+ return c;
+ }
+
+ /* Always call fsys_getfile so that we don't depend on the
+ particular open modes of the port passed in. */
+
+ err = fsys_getfile (lookup_filesystem (fs),
+ credc->ids->uids, credc->ids->nuids,
+ credc->ids->gids, credc->ids->ngids,
+ fhandle + sizeof (int), NFS2_FHSIZE - sizeof (int),
+ &newport);
+ if (err)
+ {
+ pthread_mutex_unlock (&fhhashlock);
+ return 0;
+ }
+
+ /* Create it anew. */
+ c = malloc (sizeof (struct cache_handle));
+ memcpy (c->handle, fhandle, NFS2_FHSIZE);
+ cred_ref (credc->ids);
+ c->ids = credc->ids;
+ c->port = newport;
+ c->references = 1;
+
+ /* And add it to the hash table. */
+ c->next = fhhashtable[hash];
+ if (c->next)
+ c->next->prevp = &c->next;
+ c->prevp = &fhhashtable[hash];
+ fhhashtable[hash] = c;
+ pthread_mutex_unlock (&fhhashlock);
+
+ return c;
+}
+
+
+
+static struct cached_reply *replyhashtable [REPLYHASH_TABLE_SIZE];
+static pthread_spinlock_t replycachelock = PTHREAD_SPINLOCK_INITIALIZER;
+static int nfreereplies;
+static int leastreplylastuse;
+
+/* Check the list of cached replies to see if this is a replay of a
+ previous transaction; if so, return the cache record. Otherwise,
+ create a new cache record. */
+struct cached_reply *
+check_cached_replies (int xid,
+ struct sockaddr_in *sender)
+{
+ struct cached_reply *cr;
+ int hash;
+
+ hash = abs(xid % REPLYHASH_TABLE_SIZE);
+
+ pthread_spin_lock (&replycachelock);
+ for (cr = replyhashtable[hash]; cr; cr = cr->next)
+ if (cr->xid == xid
+ && !bcmp (sender, &cr->source, sizeof (struct sockaddr_in)))
+ {
+ cr->references++;
+ if (cr->references == 1)
+ nfreereplies--;
+ pthread_spin_unlock (&replycachelock);
+ pthread_mutex_lock (&cr->lock);
+ return cr;
+ }
+
+ cr = malloc (sizeof (struct cached_reply));
+ pthread_mutex_init (&cr->lock, NULL);
+ pthread_mutex_lock (&cr->lock);
+ memcpy (&cr->source, sender, sizeof (struct sockaddr_in));
+ cr->xid = xid;
+ cr->data = 0;
+ cr->references = 1;
+
+ cr->next = replyhashtable[hash];
+ if (replyhashtable[hash])
+ replyhashtable[hash]->prevp = &cr->next;
+ cr->prevp = &replyhashtable[hash];
+ replyhashtable[hash] = cr;
+
+ pthread_spin_unlock (&replycachelock);
+ return cr;
+}
+
+/* A cached reply returned by check_cached_replies is now no longer
+ needed by its caller. */
+void
+release_cached_reply (struct cached_reply *cr)
+{
+ pthread_mutex_unlock (&cr->lock);
+ pthread_spin_lock (&replycachelock);
+ cr->references--;
+ if (cr->references == 0)
+ {
+ cr->lastuse = mapped_time->seconds;
+ if (cr->lastuse < leastreplylastuse || nfreereplies == 0)
+ leastreplylastuse = cr->lastuse;
+ nfreereplies++;
+ }
+ pthread_spin_unlock (&replycachelock);
+}
+
+void
+scan_replies ()
+{
+ int n;
+ int newleast = mapped_time->seconds;
+
+ pthread_spin_lock (&replycachelock);
+
+ if (mapped_time->seconds - leastreplylastuse > REPLY_KEEP_TIMEOUT)
+ {
+ for (n = 0; n < REPLYHASH_TABLE_SIZE && nfreereplies; n++)
+ {
+ struct cached_reply *cr = replyhashtable[n];
+
+ while (cr && nfreereplies)
+ {
+ struct cached_reply *next_cr = cr->next;
+
+ if (!cr->references
+ && mapped_time->seconds - cr->lastuse > REPLY_KEEP_TIMEOUT)
+ {
+ nfreereplies--;
+ *cr->prevp = cr->next;
+ if (cr->next)
+ cr->next->prevp = cr->prevp;
+ if (cr->data)
+ free (cr->data);
+ free (cr);
+ }
+ else if (!cr->references && newleast > cr->lastuse)
+ newleast = cr->lastuse;
+
+ cr = next_cr;
+ }
+ }
+
+ /* If we didn't bail early, then this is valid. */
+ if (nfreereplies)
+ leastreplylastuse = newleast;
+ }
+ pthread_spin_unlock (&replycachelock);
+}
diff --git a/nfsd/fsys.c b/nfsd/fsys.c
new file mode 100644
index 00000000..7b15d150
--- /dev/null
+++ b/nfsd/fsys.c
@@ -0,0 +1,209 @@
+/* Filesystem management for NFS server
+ Copyright (C) 1996, 2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdio.h>
+#include <errno.h>
+#include <error.h>
+#include <hurd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "nfsd.h"
+
+struct fsys_spec
+{
+ fsys_t fsys;
+ char *name;
+};
+
+static struct fsys_spec *fsystable;
+static int nfsys = 0;
+static int fsystablesize = 0;
+
+file_t index_file_dir;
+char *index_file_compname;
+
+/* Read the filesystem table in from disk */
+void
+init_filesystems (void)
+{
+ int nitems;
+ char *name;
+ int index;
+ int line;
+ file_t root;
+ static FILE *index_file;
+ int i;
+
+ fsystable = (struct fsys_spec *) malloc ((fsystablesize = 10)
+ * sizeof (struct fsys_spec));
+ for (i = 0; i < fsystablesize; i++)
+ {
+ fsystable[i].fsys = MACH_PORT_NULL;
+ fsystable[i].name = 0;
+ }
+
+ if (!index_file_name)
+ return;
+
+ index_file = fopen (index_file_name, "r");
+ if (!index_file)
+ {
+ error (0, errno, "Cannot open `%s'", index_file_name);
+ return;
+ }
+
+ for (line = 1; ; line++)
+ {
+ nitems = fscanf (index_file, "%d %as\n", &index, &name);
+ if (nitems == EOF)
+ {
+ fclose (index_file);
+ return;
+ }
+
+ if (nitems != 2)
+ {
+ error (0, 0, "%s:%d Bad syntax", index_file_name, line);
+ continue;
+ }
+
+ root = file_name_lookup (name, 0, 0);
+ if (root == MACH_PORT_NULL)
+ {
+ error (0, errno, "%s:%d Filesystem `%s'",
+ index_file_name, line, name);
+ free (name);
+ continue;
+ }
+
+ if (index >= fsystablesize)
+ {
+ fsystable = (struct fsys_spec *)
+ realloc (fsystable, index * 2 * sizeof (struct fsys_spec));
+ for (i = fsystablesize; i < index * 2; i++)
+ {
+ fsystable[i].fsys = MACH_PORT_NULL;
+ fsystable[i].name = 0;
+ }
+ fsystablesize = index * 2;
+ }
+
+ if (index + 1 > nfsys)
+ nfsys = index + 1;
+
+ fsystable[index].name = name;
+ file_getcontrol (root, &fsystable[index].fsys);
+ mach_port_deallocate (mach_task_self (), root);
+ }
+}
+
+/* Write the current filesystem table to disk synchronously. */
+static void
+write_filesystems (void)
+{
+ file_t newindex;
+ FILE *f;
+ error_t err;
+ int i;
+
+ if (!index_file_name)
+ return;
+
+ if (index_file_dir == MACH_PORT_NULL)
+ {
+ index_file_dir = file_name_split (index_file_name, &index_file_compname);
+ if (index_file_dir == MACH_PORT_NULL)
+ {
+ error (0, errno, "`%s'", index_file_name);
+ index_file_name = 0;
+ return;
+ }
+ }
+
+ /* Create an anonymous file in the same directory */
+ err = dir_mkfile (index_file_dir, O_WRONLY, 0666, &newindex);
+ if (err)
+ {
+ error (0, err, "`%s'", index_file_name);
+ index_file_name = 0;
+ mach_port_deallocate (mach_task_self (), index_file_dir);
+ index_file_dir = MACH_PORT_NULL;
+ return;
+ }
+
+ f = fopenport (newindex, "w");
+
+ for (i = 0; i < nfsys; i++)
+ if (fsystable[i].name)
+ fprintf (f, "%d %s\n", i, fsystable[i].name);
+
+ /* Link it in */
+ err = dir_link (index_file_dir, newindex, index_file_compname, 0);
+ if (err)
+ error (0, err, "`%s'", index_file_name);
+ fflush (f);
+ file_sync (newindex, 1, 0);
+ fclose (f);
+}
+
+/* From a filesystem ID number, return the fsys_t for talking to that
+ filesystem; MACH_PORT_NULL if it isn't in our list. */
+fsys_t
+lookup_filesystem (int id)
+{
+ if (id >= nfsys)
+ return MACH_PORT_NULL;
+ return fsystable[id].fsys;
+}
+
+/* Enter a name in the table of filesystems; return its ID number.
+ ROOT refers to the root of this filesystem. */
+int
+enter_filesystem (char *name, file_t root)
+{
+ int i;
+
+ for (i = 0; i < nfsys; i++)
+ if (fsystable[i].name && !strcmp (fsystable[i].name, name))
+ return i;
+
+ if (nfsys == fsystablesize)
+ {
+ fsystable = (struct fsys_spec *) realloc (fsystable,
+ (fsystablesize * 2)
+ * sizeof (struct fsys_spec));
+ for (i = fsystablesize; i < fsystablesize * 2; i++)
+ {
+ fsystable[i].fsys = MACH_PORT_NULL;
+ fsystable[i].name = 0;
+ }
+ fsystablesize *= 2;
+ }
+
+ fsystable[nfsys].name = malloc (strlen (name) + 1);
+ strcpy (fsystable[nfsys].name, name);
+ file_getcontrol (root, &fsystable[nfsys].fsys);
+ nfsys++;
+
+ write_filesystems ();
+
+ return nfsys - 1;
+}
diff --git a/nfsd/loop.c b/nfsd/loop.c
new file mode 100644
index 00000000..f2d6d546
--- /dev/null
+++ b/nfsd/loop.c
@@ -0,0 +1,224 @@
+/* loop.c - Main server loop for nfs server.
+ Copyright (C) 1996,98,2002,2006 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <string.h>
+#include <fcntl.h>
+
+#include "nfsd.h"
+
+#include <rpc/pmap_prot.h>
+#include "../nfs/mount.h"
+
+#undef TRUE
+#undef FALSE
+#define malloc spoogie_woogie /* barf */
+#include <rpc/xdr.h>
+#include <rpc/types.h>
+#include <rpc/auth.h>
+#include <rpc/rpc_msg.h>
+#undef malloc
+
+void *
+server_loop (void *arg)
+{
+ int fd = (int) arg;
+ char buf[MAXIOSIZE];
+ int xid;
+ int *p, *r;
+ char *rbuf;
+ struct cached_reply *cr;
+ int program;
+ struct sockaddr_in sender;
+ int version;
+ int procedure;
+ struct proctable *table = 0;
+ struct procedure *proc;
+ struct idspec *cred;
+ struct cache_handle *c, fakec;
+ error_t err;
+ socklen_t addrlen;
+ int cc;
+
+ memset (&fakec, 0, sizeof (struct cache_handle));
+
+ for (;;)
+ {
+ p = (int *) buf;
+ proc = 0;
+ addrlen = sizeof (struct sockaddr_in);
+ cc = recvfrom (fd, buf, MAXIOSIZE, 0, &sender, &addrlen);
+ if (cc == -1)
+ continue; /* Ignore errors. */
+ xid = *(p++);
+
+ /* Ignore things that aren't proper RPCs. */
+ if (ntohl (*p) != CALL)
+ continue;
+ p++;
+
+ cr = check_cached_replies (xid, &sender);
+ if (cr->data)
+ /* This transacation has already completed. */
+ goto repost_reply;
+
+ r = (int *) (rbuf = malloc (MAXIOSIZE));
+
+ if (ntohl (*p) != RPC_MSG_VERSION)
+ {
+ /* Reject RPC. */
+ *(r++) = xid;
+ *(r++) = htonl (REPLY);
+ *(r++) = htonl (MSG_DENIED);
+ *(r++) = htonl (RPC_MISMATCH);
+ *(r++) = htonl (RPC_MSG_VERSION);
+ *(r++) = htonl (RPC_MSG_VERSION);
+ goto send_reply;
+ }
+ p++;
+
+ program = ntohl (*p);
+ p++;
+ switch (program)
+ {
+ case MOUNTPROG:
+ version = MOUNTVERS;
+ table = &mounttable;
+ break;
+
+ case NFS_PROGRAM:
+ version = NFS_VERSION;
+ table = &nfs2table;
+ break;
+
+ case PMAPPROG:
+ version = PMAPVERS;
+ table = &pmaptable;
+ break;
+
+ default:
+ /* Program unavailable. */
+ *(r++) = xid;
+ *(r++) = htonl (REPLY);
+ *(r++) = htonl (MSG_ACCEPTED);
+ *(r++) = htonl (AUTH_NULL);
+ *(r++) = htonl (0);
+ *(r++) = htonl (PROG_UNAVAIL);
+ goto send_reply;
+ }
+
+ if (ntohl (*p) != version)
+ {
+ /* Program mismatch. */
+ *(r++) = xid;
+ *(r++) = htonl (REPLY);
+ *(r++) = htonl (MSG_ACCEPTED);
+ *(r++) = htonl (AUTH_NULL);
+ *(r++) = htonl (0);
+ *(r++) = htonl (PROG_MISMATCH);
+ *(r++) = htonl (version);
+ *(r++) = htonl (version);
+ goto send_reply;
+ }
+ p++;
+
+ procedure = htonl (*p);
+ p++;
+ if (procedure < table->min
+ || procedure > table->max
+ || table->procs[procedure - table->min].func == 0)
+ {
+ /* Procedure unavailable. */
+ *(r++) = xid;
+ *(r++) = htonl (REPLY);
+ *(r++) = htonl (MSG_ACCEPTED);
+ *(r++) = htonl (AUTH_NULL);
+ *(r++) = htonl (0);
+ *(r++) = htonl (PROC_UNAVAIL);
+ *(r++) = htonl (table->min);
+ *(r++) = htonl (table->max);
+ goto send_reply;
+ }
+ proc = &table->procs[procedure - table->min];
+
+ p = process_cred (p, &cred);
+
+ if (proc->need_handle)
+ p = lookup_cache_handle (p, &c, cred);
+ else
+ {
+ fakec.ids = cred;
+ c = &fakec;
+ }
+
+ if (proc->alloc_reply)
+ {
+ size_t amt;
+ amt = (*proc->alloc_reply) (p, version) + 256;
+ if (amt > MAXIOSIZE)
+ {
+ free (rbuf);
+ r = (int *) (rbuf = malloc (amt));
+ }
+ }
+
+ /* Fill in beginning of reply. */
+ *(r++) = xid;
+ *(r++) = htonl (REPLY);
+ *(r++) = htonl (MSG_ACCEPTED);
+ *(r++) = htonl (AUTH_NULL);
+ *(r++) = htonl (0);
+ *(r++) = htonl (SUCCESS);
+ if (!proc->process_error)
+ /* The function does its own error processing, and we ignore
+ its return value. */
+ (void) (*proc->func) (c, p, &r, version);
+ else
+ {
+ if (c)
+ {
+ /* Assume success for now and patch it later if necessary. */
+ int *errloc = r;
+ *(r++) = htonl (0);
+ /* Call processing function, its output after error code. */
+ err = (*proc->func) (c, p, &r, version);
+ if (err)
+ {
+ r = errloc; /* Back up, patch error code, discard rest. */
+ *(r++) = htonl (nfs_error_trans (err, version));
+ }
+ }
+ else
+ *(r++) = htonl (nfs_error_trans (ESTALE, version));
+ }
+
+ cred_rele (cred);
+ if (c && c != &fakec)
+ cache_handle_rele (c);
+
+ send_reply:
+ cr->data = rbuf;
+ cr->len = (char *)r - rbuf;
+
+ repost_reply:
+ sendto (fd, cr->data, cr->len, 0,
+ (struct sockaddr *) &sender, addrlen);
+ release_cached_reply (cr);
+ }
+}
diff --git a/nfsd/main.c b/nfsd/main.c
new file mode 100644
index 00000000..d5607d37
--- /dev/null
+++ b/nfsd/main.c
@@ -0,0 +1,105 @@
+/* Main NFS server program
+ Copyright (C) 1996, 2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "nfsd.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <rpc/pmap_prot.h>
+#include <maptime.h>
+#include <hurd.h>
+#include <pthread.h>
+#include <error.h>
+
+int main_udp_socket, pmap_udp_socket;
+struct sockaddr_in main_address, pmap_address;
+static char index_file[] = LOCALSTATEDIR "/state/misc/nfsd.index";
+char *index_file_name = index_file;
+
+/* Launch a server loop thread */
+static void
+create_server_thread (int socket)
+{
+ pthread_t thread;
+ int fail;
+
+ fail = pthread_create (&thread, NULL, server_loop, (void *) socket);
+ if (fail)
+ error (1, fail, "Creating main server thread");
+
+ fail = pthread_detach (thread);
+ if (fail)
+ error (1, fail, "Detaching main server thread");
+}
+
+int
+main (int argc, char **argv)
+{
+ int nthreads;
+ int fail;
+
+ if (argc > 2)
+ {
+ fprintf (stderr, "%s [num-threads]\n", argv[0]);
+ exit (1);
+ }
+ if (argc == 1)
+ nthreads = 4;
+ else
+ nthreads = atoi (argv[1]);
+ if (!nthreads)
+ nthreads = 4;
+
+ authserver = getauth ();
+ maptime_map (0, 0, &mapped_time);
+
+ main_address.sin_family = AF_INET;
+ main_address.sin_port = htons (NFS_PORT);
+ main_address.sin_addr.s_addr = INADDR_ANY;
+ pmap_address.sin_family = AF_INET;
+ pmap_address.sin_port = htons (PMAPPORT);
+ pmap_address.sin_addr.s_addr = INADDR_ANY;
+
+ main_udp_socket = socket (PF_INET, SOCK_DGRAM, 0);
+ pmap_udp_socket = socket (PF_INET, SOCK_DGRAM, 0);
+ fail = bind (main_udp_socket, (struct sockaddr *)&main_address,
+ sizeof (struct sockaddr_in));
+ if (fail)
+ error (1, errno, "Binding NFS socket");
+
+ fail = bind (pmap_udp_socket, (struct sockaddr *)&pmap_address,
+ sizeof (struct sockaddr_in));
+ if (fail)
+ error (1, errno, "Binding PMAP socket");
+
+ init_filesystems ();
+
+ create_server_thread (pmap_udp_socket);
+
+ while (nthreads--)
+ create_server_thread (main_udp_socket);
+
+ for (;;)
+ {
+ sleep (1);
+ scan_fhs ();
+ scan_creds ();
+ scan_replies ();
+ }
+}
diff --git a/nfsd/nfsd.h b/nfsd/nfsd.h
new file mode 100644
index 00000000..4afff061
--- /dev/null
+++ b/nfsd/nfsd.h
@@ -0,0 +1,129 @@
+/*
+ Copyright (C) 1996,98,2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <rpc/types.h>
+#include "../nfs/nfs-spec.h" /* XXX */
+#include <hurd/fs.h>
+
+/* These should be configuration options */
+#define ID_KEEP_TIMEOUT 3600 /* one hour */
+#define FH_KEEP_TIMEOUT 600 /* ten minutes */
+#define REPLY_KEEP_TIMEOUT 120 /* two minutes */
+#define MAXIOSIZE 10240
+
+struct idspec
+{
+ struct idspec *next, **prevp;
+ int nuids, ngids;
+ uid_t *uids, *gids;
+ time_t lastuse;
+ int references;
+};
+
+struct cache_handle
+{
+ struct cache_handle *next, **prevp;
+ char handle[NFS2_FHSIZE];
+ struct idspec *ids;
+ file_t port;
+ time_t lastuse;
+ int references;
+};
+
+struct cached_reply
+{
+ struct cached_reply *next, **prevp;
+ pthread_mutex_t lock;
+ struct sockaddr_in source;
+ int xid;
+ time_t lastuse;
+ int references;
+ size_t len;
+ char *data;
+};
+
+struct procedure
+{
+ error_t (*func) (struct cache_handle *, int *, int **, int);
+ size_t (*alloc_reply) (int *, int);
+ int need_handle;
+ int process_error;
+};
+
+struct proctable
+{
+ int min;
+ int max;
+ struct procedure procs[];
+};
+
+volatile struct mapped_time_value *mapped_time;
+
+#define INTSIZE(n) (((n) + 3) >> 2)
+
+/* We don't actually distinguish between these two sockets, but
+ we have to listen on two different ports, so that's why they're here. */
+extern int main_udp_socket, pmap_udp_socket;
+extern struct sockaddr_in main_address, pmap_address;
+
+/* Name of the file on disk containing the filesystem index table */
+extern char *index_file_name;
+
+/* Our auth server */
+auth_t authserver;
+
+
+/* cache.c */
+int *process_cred (int *, struct idspec **);
+void cred_rele (struct idspec *);
+void cred_ref (struct idspec *);
+void scan_creds (void);
+int *lookup_cache_handle (int *, struct cache_handle **, struct idspec *);
+void cache_handle_rele (struct cache_handle *);
+void scan_fhs (void);
+struct cache_handle *create_cached_handle (int, struct cache_handle *, file_t);
+struct cached_reply *check_cached_replies (int, struct sockaddr_in *);
+void release_cached_reply (struct cached_reply *cr);
+void scan_replies (void);
+
+/* loop.c */
+void * server_loop (void *);
+
+/* ops.c */
+extern struct proctable nfs2table, mounttable, pmaptable;
+
+/* xdr.c */
+int nfs_error_trans (error_t, int);
+int *encode_fattr (int *, struct stat *, int version);
+int *decode_name (int *, char **);
+int *encode_fhandle (int *, char *);
+int *encode_string (int *, char *);
+int *encode_data (int *, char *, size_t);
+int *encode_statfs (int *, struct statfs *);
+
+/* fsys.c */
+fsys_t lookup_filesystem (int);
+int enter_filesystem (char *, file_t);
+void init_filesystems (void);
diff --git a/nfsd/ops.c b/nfsd/ops.c
new file mode 100644
index 00000000..6e2cbb15
--- /dev/null
+++ b/nfsd/ops.c
@@ -0,0 +1,755 @@
+/* ops.c NFS daemon protocol operations.
+
+ Copyright (C) 1996, 2001, 2002, 2007 Free Software Foundation, Inc.
+
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <hurd/io.h>
+#include <hurd/fs.h>
+#include <fcntl.h>
+#include <hurd/paths.h>
+#include <hurd.h>
+#include <dirent.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include "nfsd.h"
+#include "../nfs/mount.h" /* XXX */
+#include <rpc/pmap_prot.h>
+
+static error_t
+op_null (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ return 0;
+}
+
+static error_t
+op_getattr (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ struct stat st;
+ error_t err;
+
+ err = io_stat (c->port, &st);
+ if (!err)
+ *reply = encode_fattr (*reply, &st, version);
+ return err;
+}
+
+static error_t
+complete_setattr (mach_port_t port,
+ int *p)
+{
+ uid_t uid, gid;
+ off_t size;
+ time_value_t atime, mtime;
+ struct stat st;
+ error_t err;
+
+ err = io_stat (port, &st);
+ if (err)
+ return err;
+
+ uid = ntohl (*p);
+ p++;
+ gid = ntohl (*p);
+ p++;
+ if (uid == -1)
+ uid = st.st_uid;
+ if (gid == -1)
+ gid = st.st_gid;
+ if (uid != st.st_uid || gid != st.st_gid)
+ err = file_chown (port, uid, gid);
+ if (err)
+ return err;
+
+ size = ntohl (*p);
+ p++;
+ if (size != -1 && size != st.st_size)
+ err = file_set_size (port, size);
+ if (err)
+ return err;
+
+ atime.seconds = ntohl (*p);
+ p++;
+ atime.microseconds = ntohl (*p);
+ p++;
+ mtime.seconds = ntohl (*p);
+ p++;
+ mtime.microseconds = ntohl (*p);
+ p++;
+
+ if (atime.seconds != -1 && atime.microseconds == -1)
+ atime.microseconds = 0;
+ if (mtime.seconds != -1 && mtime.microseconds == -1)
+ mtime.microseconds = 0;
+
+ if (atime.seconds == -1)
+ atime.seconds = st.st_atim.tv_sec;
+ if (atime.microseconds == -1)
+ atime.microseconds = st.st_atim.tv_nsec / 1000;
+ if (mtime.seconds == -1)
+ mtime.seconds = st.st_mtim.tv_sec;
+ if (mtime.microseconds == -1)
+ mtime.microseconds = st.st_mtim.tv_nsec / 1000;
+
+ if (atime.seconds != st.st_atim.tv_sec
+ || atime.microseconds != st.st_atim.tv_nsec / 1000
+ || mtime.seconds != st.st_mtim.tv_sec
+ || mtime.microseconds != st.st_mtim.tv_nsec / 1000)
+ err = file_utimes (port, atime, mtime);
+
+ return err;
+}
+
+static error_t
+op_setattr (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ error_t err = 0;
+ mode_t mode;
+ struct stat st;
+
+ mode = ntohl (*p);
+ p++;
+ if (mode != -1)
+ err = file_chmod (c->port, mode);
+
+ if (!err)
+ err = complete_setattr (c->port, p);
+ if (!err)
+ err = io_stat (c->port, &st);
+ if (err)
+ return err;
+
+ *reply = encode_fattr (*reply, &st, version);
+ return 0;
+}
+
+static error_t
+op_lookup (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ error_t err;
+ char *name;
+ retry_type do_retry;
+ char retry_name [1024];
+ mach_port_t newport;
+ struct cache_handle *newc;
+ struct stat st;
+
+ decode_name (p, &name);
+
+ err = dir_lookup (c->port, name, O_NOTRANS, 0, &do_retry, retry_name,
+ &newport);
+ free (name);
+
+ /* Block attempts to bounce out of this filesystem by any technique. */
+ if (!err
+ && (do_retry != FS_RETRY_NORMAL
+ || retry_name[0] != '\0'))
+ err = EACCES;
+
+ if (!err)
+ err = io_stat (newport, &st);
+
+ if (err)
+ return err;
+
+ newc = create_cached_handle (*(int *)c->handle, c, newport);
+ if (!newc)
+ return ESTALE;
+ *reply = encode_fhandle (*reply, newc->handle);
+ *reply = encode_fattr (*reply, &st, version);
+ return 0;
+}
+
+static error_t
+op_readlink (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ char buf[2048], *transp = buf;
+ mach_msg_type_number_t len = sizeof (buf);
+ error_t err;
+
+ /* Shamelessly copied from the libc readlink. */
+ err = file_get_translator (c->port, &transp, &len);
+ if (err)
+ {
+ if (transp != buf)
+ munmap (transp, len);
+ return err;
+ }
+
+ if (len < sizeof (_HURD_SYMLINK)
+ || memcmp (transp, _HURD_SYMLINK, sizeof (_HURD_SYMLINK)))
+ return EINVAL;
+
+ transp += sizeof (_HURD_SYMLINK);
+
+ *reply = encode_string (*reply, transp);
+
+ if (transp != buf)
+ munmap (transp, len);
+
+ return 0;
+}
+
+static size_t
+count_read_buffersize (int *p, int version)
+{
+ p++; /* Skip OFFSET. */
+ return ntohl (*p); /* Return COUNT. */
+}
+
+static error_t
+op_read (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ off_t offset;
+ size_t count;
+ char buf[2048], *bp = buf;
+ mach_msg_type_number_t buflen = sizeof (buf);
+ struct stat st;
+ error_t err;
+
+ offset = ntohl (*p);
+ p++;
+ count = ntohl (*p);
+ p++;
+
+ err = io_read (c->port, &bp, &buflen, offset, count);
+ if (err)
+ {
+ if (bp != buf)
+ munmap (bp, buflen);
+ return err;
+ }
+
+ err = io_stat (c->port, &st);
+ if (err)
+ return err;
+
+ *reply = encode_fattr (*reply, &st, version);
+ *reply = encode_data (*reply, bp, buflen);
+
+ if (bp != buf)
+ munmap (bp, buflen);
+
+ return 0;
+}
+
+static error_t
+op_write (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ off_t offset;
+ size_t count;
+ error_t err;
+ mach_msg_type_number_t amt;
+ char *bp;
+ struct stat st;
+
+ p++;
+ offset = ntohl (*p);
+ p++;
+ p++;
+ count = ntohl (*p);
+ p++;
+ bp = (char *) *reply;
+
+ while (count)
+ {
+ err = io_write (c->port, bp, count, offset, &amt);
+ if (err)
+ return err;
+ if (amt == 0)
+ return EIO;
+ count -= amt;
+ bp += amt;
+ offset += amt;
+ }
+
+ file_sync (c->port, 1, 0);
+
+ err = io_stat (c->port, &st);
+ if (err)
+ return err;
+ *reply = encode_fattr (*reply, &st, version);
+ return 0;
+}
+
+static error_t
+op_create (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ error_t err;
+ char *name;
+ retry_type do_retry;
+ char retry_name [1024];
+ mach_port_t newport;
+ struct cache_handle *newc;
+ struct stat st;
+ mode_t mode;
+ int statchanged = 0;
+ off_t size;
+
+ p = decode_name (p, &name);
+ mode = ntohl (*p);
+ p++;
+
+ err = dir_lookup (c->port, name, O_NOTRANS | O_CREAT | O_TRUNC, mode,
+ &do_retry, retry_name, &newport);
+ if (!err
+ && (do_retry != FS_RETRY_NORMAL
+ || retry_name[0] != '\0'))
+ err = EACCES;
+
+ if (err)
+ return err;
+
+ if (!err)
+ err = io_stat (newport, &st);
+ if (err)
+ goto errout;
+
+ /* NetBSD ignores most of the setattr fields given; that's good enough
+ for me too. */
+
+ p++, p++; /* Skip uid and gid. */
+
+ size = ntohl (*p);
+ p++;
+ if (size != -1 && size != st.st_size)
+ {
+ err = file_set_size (newport, size);
+ statchanged = 1;
+ }
+ if (err)
+ goto errout;
+
+ /* Ignore times. */
+
+ if (statchanged)
+ err = io_stat (newport, &st);
+
+ if (err)
+ {
+ errout:
+ dir_unlink (c->port, name);
+ free (name);
+ return err;
+ }
+ free (name);
+
+ newc = create_cached_handle (*(int *)c->handle, c, newport);
+ if (!newc)
+ return ESTALE;
+
+ *reply = encode_fhandle (*reply, newc->handle);
+ *reply = encode_fattr (*reply, &st, version);
+ return 0;
+}
+
+static error_t
+op_remove (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ error_t err;
+ char *name;
+
+ decode_name (p, &name);
+
+ err = dir_unlink (c->port, name);
+ free (name);
+
+ return err;
+}
+
+static error_t
+op_rename (struct cache_handle *fromc,
+ int *p,
+ int **reply,
+ int version)
+{
+ struct cache_handle *toc;
+ char *fromname, *toname;
+ error_t err = 0;
+
+ p = decode_name (p, &fromname);
+ p = lookup_cache_handle (p, &toc, fromc->ids);
+ decode_name (p, &toname);
+
+ if (!toc)
+ err = ESTALE;
+ if (!err)
+ err = dir_rename (fromc->port, fromname, toc->port, toname, 0);
+ free (fromname);
+ free (toname);
+ return err;
+}
+
+static error_t
+op_link (struct cache_handle *filec,
+ int *p,
+ int **reply,
+ int version)
+{
+ struct cache_handle *dirc;
+ char *name;
+ error_t err = 0;
+
+ p = lookup_cache_handle (p, &dirc, filec->ids);
+ decode_name (p, &name);
+
+ if (!dirc)
+ err = ESTALE;
+ if (!err)
+ err = dir_link (dirc->port, filec->port, name, 1);
+
+ free (name);
+ return err;
+}
+
+static error_t
+op_symlink (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ char *name, *target;
+ error_t err;
+ mode_t mode;
+ file_t newport = MACH_PORT_NULL;
+ size_t len;
+ char *buf;
+
+ p = decode_name (p, &name);
+ p = decode_name (p, &target);
+ mode = ntohl (*p);
+ p++;
+ if (mode == -1)
+ mode = 0777;
+
+ len = strlen (target) + 1;
+ buf = alloca (sizeof (_HURD_SYMLINK) + len);
+ memcpy (buf, _HURD_SYMLINK, sizeof (_HURD_SYMLINK));
+ memcpy (buf + sizeof (_HURD_SYMLINK), target, len);
+
+ err = dir_mkfile (c->port, O_WRITE, mode, &newport);
+ if (!err)
+ err = file_set_translator (newport,
+ FS_TRANS_EXCL|FS_TRANS_SET,
+ FS_TRANS_EXCL|FS_TRANS_SET, 0,
+ buf, sizeof (_HURD_SYMLINK) + len,
+ MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND);
+ if (!err)
+ err = dir_link (c->port, newport, name, 1);
+
+ free (name);
+ free (target);
+
+ if (newport != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), newport);
+ return err;
+}
+
+static error_t
+op_mkdir (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ char *name;
+ mode_t mode;
+ retry_type do_retry;
+ char retry_name [1024];
+ mach_port_t newport;
+ struct stat st;
+ struct cache_handle *newc;
+ error_t err;
+
+ p = decode_name (p, &name);
+ mode = ntohl (*p);
+ p++;
+
+ err = dir_mkdir (c->port, name, mode);
+
+ if (err)
+ {
+ free (name);
+ return err;
+ }
+
+ err = dir_lookup (c->port, name, O_NOTRANS, 0, &do_retry,
+ retry_name, &newport);
+ free (name);
+ if (!err
+ && (do_retry != FS_RETRY_NORMAL
+ || retry_name[0] != '\0'))
+ err = EACCES;
+ if (err)
+ return err;
+
+ /* Ignore the rest of the sattr structure. */
+
+ if (!err)
+ err = io_stat (newport, &st);
+ if (err)
+ return err;
+
+ newc = create_cached_handle (*(int *)c->handle, c, newport);
+ if (!newc)
+ return ESTALE;
+ *reply = encode_fhandle (*reply, newc->handle);
+ *reply = encode_fattr (*reply, &st, version);
+ return 0;
+}
+
+static error_t
+op_rmdir (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ char *name;
+ error_t err;
+
+ decode_name (p, &name);
+
+ err = dir_rmdir (c->port, name);
+ free (name);
+ return err;
+}
+
+static error_t
+op_readdir (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ int cookie;
+ unsigned count;
+ error_t err;
+ char *buf;
+ struct dirent *dp;
+ size_t bufsize;
+ int nentries;
+ int i;
+ int *replystart;
+ int *r;
+
+ cookie = ntohl (*p);
+ p++;
+ count = ntohl (*p);
+ p++;
+
+ buf = (char *) 0;
+ bufsize = 0;
+ err = dir_readdir (c->port, &buf, &bufsize, cookie, -1, count, &nentries);
+ if (err)
+ {
+ if (buf)
+ munmap (buf, bufsize);
+ return err;
+ }
+
+ r = *reply;
+
+ if (nentries == 0)
+ {
+ *(r++) = htonl (0); /* No entry. */
+ *(r++) = htonl (1); /* EOF. */
+ }
+ else
+ {
+ for (i = 0, dp = (struct dirent *) buf, replystart = *reply;
+ ((char *)dp < buf + bufsize
+ && i < nentries
+ && (char *)reply < (char *)replystart + count);
+ i++, dp = (struct dirent *) ((char *)dp + dp->d_reclen))
+ {
+ *(r++) = htonl (1); /* Entry present. */
+ *(r++) = htonl (dp->d_ino);
+ r = encode_string (r, dp->d_name);
+ *(r++) = htonl (i + cookie + 1); /* Next entry. */
+ }
+ *(r++) = htonl (0); /* No more entries. */
+ *(r++) = htonl (0); /* Not EOF. */
+ }
+
+ *reply = r;
+
+ if (buf)
+ munmap (buf, bufsize);
+
+ return 0;
+}
+
+static size_t
+count_readdir_buffersize (int *p, int version)
+{
+ p++; /* Skip COOKIE. */
+ return ntohl (*p); /* Return COUNT. */
+}
+
+static error_t
+op_statfs (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ struct statfs st;
+ error_t err;
+
+ err = file_statfs (c->port, &st);
+ if (!err)
+ *reply = encode_statfs (*reply, &st);
+ return err;
+}
+
+static error_t
+op_mnt (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ file_t root;
+ struct cache_handle *newc;
+ char *name;
+
+ decode_name (p, &name);
+
+ root = file_name_lookup (name, 0, 0);
+ if (!root)
+ {
+ free (name);
+ return errno;
+ }
+
+ newc = create_cached_handle (enter_filesystem (name, root), c, root);
+ free (name);
+ if (!newc)
+ return ESTALE;
+ *reply = encode_fhandle (*reply, newc->handle);
+ return 0;
+}
+
+static error_t
+op_getport (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ int prog, vers, prot;
+
+ prog = ntohl (*p);
+ p++;
+ vers = ntohl (*p);
+ p++;
+ prot = ntohl (*p);
+ p++;
+
+ if (prot != IPPROTO_UDP)
+ *(*reply)++ = htonl (0);
+ else if ((prog == MOUNTPROG && vers == MOUNTVERS)
+ || (prog == NFS_PROGRAM && vers == NFS_VERSION))
+ *(*reply)++ = htonl (NFS_PORT);
+ else if (prog == PMAPPROG && vers == PMAPVERS)
+ *(*reply)++ = htonl (PMAPPORT);
+ else
+ *(*reply)++ = 0;
+
+ return 0;
+}
+
+
+struct proctable nfs2table =
+{
+ NFS2PROC_NULL, /* First proc. */
+ NFS2PROC_STATFS, /* Last proc. */
+ {
+ { op_null, 0, 0, 0},
+ { op_getattr, 0, 1, 1},
+ { op_setattr, 0, 1, 1},
+ { 0, 0, 0, 0 }, /* Deprecated NFSPROC_ROOT. */
+ { op_lookup, 0, 1, 1},
+ { op_readlink, 0, 1, 1},
+ { op_read, count_read_buffersize, 1, 1},
+ { 0, 0, 0, 0 }, /* Nonexistent NFSPROC_WRITECACHE. */
+ { op_write, 0, 1, 1},
+ { op_create, 0, 1, 1},
+ { op_remove, 0, 1, 1},
+ { op_rename, 0, 1, 1},
+ { op_link, 0, 1, 1},
+ { op_symlink, 0, 1, 1},
+ { op_mkdir, 0, 1, 1},
+ { op_rmdir, 0, 1, 1},
+ { op_readdir, count_readdir_buffersize, 1, 1},
+ { op_statfs, 0, 1, 1},
+ }
+};
+
+
+struct proctable mounttable =
+{
+ MOUNTPROC_NULL, /* First proc. */
+ MOUNTPROC_EXPORT, /* Last proc. */
+ {
+ { op_null, 0, 0, 0},
+ { op_mnt, 0, 0, 1},
+ { 0, 0, 0, 0}, /* MOUNTPROC_DUMP */
+ { op_null, 0, 0, 0}, /* MOUNTPROC_UMNT */
+ { op_null, 0, 0, 0}, /* MOUNTPROC_UMNTALL */
+ { 0, 0, 0, 0}, /* MOUNTPROC_EXPORT */
+ }
+};
+
+struct proctable pmaptable =
+{
+ PMAPPROC_NULL, /* First proc. */
+ PMAPPROC_CALLIT, /* Last proc. */
+ {
+ { op_null, 0, 0, 0},
+ { 0, 0, 0, 0}, /* PMAPPROC_SET */
+ { 0, 0, 0, 0}, /* PMAPPROC_UNSET */
+ { op_getport, 0, 0, 0},
+ { 0, 0, 0, 0}, /* PMAPPROC_DUMP */
+ { 0, 0, 0, 0}, /* PMAPPROC_CALLIT */
+ }
+};
diff --git a/nfsd/xdr.c b/nfsd/xdr.c
new file mode 100644
index 00000000..8b175595
--- /dev/null
+++ b/nfsd/xdr.c
@@ -0,0 +1,218 @@
+/* xdr.c - XDR packing and unpacking in nfsd.
+
+ Copyright (C) 1996, 2002, 2007 Free Software Foundation, Inc.
+
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <string.h>
+#include "nfsd.h"
+
+/* Any better ideas? */
+static int
+hurd_mode_to_nfs_mode (mode_t m)
+{
+ return m & 0177777;
+}
+
+static int
+hurd_mode_to_nfs_type (mode_t m, int version)
+{
+ switch (m & S_IFMT)
+ {
+ case S_IFDIR:
+ return NFDIR;
+
+ case S_IFCHR:
+ return NFCHR;
+
+ case S_IFBLK:
+ return NFBLK;
+
+ case S_IFREG:
+ return NFREG;
+
+ case S_IFLNK:
+ return NFLNK;
+
+ case S_IFSOCK:
+ return NFSOCK;
+
+ case S_IFIFO:
+ return (version == 2 ? NF2FIFO : NF3FIFO);
+
+ default:
+ return (version == 2 ? NF2NON : NFREG);
+ }
+}
+
+/* Encode ST into P and return the next thing to come after it. */
+int *
+encode_fattr (int *p, struct stat *st, int version)
+{
+ *(p++) = htonl (hurd_mode_to_nfs_type (st->st_mode, version));
+ *(p++) = htonl (hurd_mode_to_nfs_mode (st->st_mode));
+ *(p++) = htonl (st->st_nlink);
+ *(p++) = htonl (st->st_uid);
+ *(p++) = htonl (st->st_gid);
+ *(p++) = htonl (st->st_size);
+ *(p++) = htonl (st->st_blksize);
+ *(p++) = htonl (st->st_rdev);
+ *(p++) = htonl (st->st_blocks);
+ *(p++) = htonl (st->st_fsid);
+ *(p++) = htonl (st->st_ino);
+ *(p++) = htonl (st->st_atim.tv_sec);
+ *(p++) = htonl (st->st_atim.tv_nsec / 1000);
+ *(p++) = htonl (st->st_mtim.tv_sec);
+ *(p++) = htonl (st->st_mtim.tv_nsec / 1000);
+ *(p++) = htonl (st->st_ctim.tv_sec);
+ *(p++) = htonl (st->st_ctim.tv_nsec / 1000);
+ return p;
+}
+
+/* Decode P into NAME and return the next thing to come after it. */
+int *
+decode_name (int *p, char **name)
+{
+ int len;
+
+ len = ntohl (*p);
+ p++;
+ *name = malloc (len + 1);
+ memcpy (*name, p, len);
+ (*name)[len] = '\0';
+ return p + INTSIZE (len);
+}
+
+/* Encode HANDLE into P and return the next thing to come after it. */
+int *
+encode_fhandle (int *p, char *handle)
+{
+ memcpy (p, handle, NFS2_FHSIZE);
+ return p + INTSIZE (NFS2_FHSIZE);
+}
+
+/* Encode STRING into P and return the next thing to come after it. */
+int *
+encode_string (int *p, char *string)
+{
+ return encode_data (p, string, strlen (string));
+}
+
+/* Encode DATA into P and return the next thing to come after it. */
+int *
+encode_data (int *p, char *data, size_t len)
+{
+ int nints = INTSIZE (len);
+
+ p[nints] = 0;
+ *(p++) = htonl (len);
+ memcpy (p, data, len);
+ return p + nints;
+}
+
+/* Encode ST into P and return the next thing to come after it. */
+int *
+encode_statfs (int *p, struct statfs *st)
+{
+ *(p++) = st->f_bsize;
+ *(p++) = st->f_bsize;
+ *(p++) = st->f_blocks;
+ *(p++) = st->f_bfree;
+ *(p++) = st->f_bavail;
+ return p;
+}
+
+/* Return an NFS error corresponding to Hurd error ERR. */
+int
+nfs_error_trans (error_t err, int version)
+{
+ switch (err)
+ {
+ case 0:
+ return NFS_OK;
+
+ case EPERM:
+ return NFSERR_PERM;
+
+ case ENOENT:
+ return NFSERR_NOENT;
+
+ case EIO:
+ return NFSERR_IO;
+
+ case ENXIO:
+ return NFSERR_NXIO;
+
+ case EACCES:
+ return NFSERR_ACCES;
+
+ case EEXIST:
+ return NFSERR_EXIST;
+
+ case ENODEV:
+ return NFSERR_NODEV;
+
+ case ENOTDIR:
+ return NFSERR_NOTDIR;
+
+ case EISDIR:
+ return NFSERR_ISDIR;
+
+ case E2BIG:
+ return NFSERR_FBIG;
+
+ case ENOSPC:
+ return NFSERR_NOSPC;
+
+ case EROFS:
+ return NFSERR_ROFS;
+
+ case ENAMETOOLONG:
+ return NFSERR_NAMETOOLONG;
+
+ case ENOTEMPTY:
+ return NFSERR_NOTEMPTY;
+
+ case EDQUOT:
+ return NFSERR_DQUOT;
+
+ case ESTALE:
+ return NFSERR_STALE;
+
+ default:
+ if (version == 2)
+ return NFSERR_IO;
+ else switch (err)
+ {
+ case EXDEV:
+ return NFSERR_XDEV;
+
+ case EINVAL:
+ return NFSERR_INVAL;
+
+ case EOPNOTSUPP:
+ return NFSERR_NOTSUPP; /* Are we sure here? */
+
+ default:
+ return NFSERR_IO;
+ }
+ }
+}
diff --git a/pfinet/Makefile b/pfinet/Makefile
new file mode 100644
index 00000000..10b90ebb
--- /dev/null
+++ b/pfinet/Makefile
@@ -0,0 +1,148 @@
+# Copyright (C) 1995, 1996, 1997, 2000, 2007, 2011, 2012 Free Software
+# Foundation, Inc.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+
+dir := pfinet
+makemode := server
+
+core-srcs := datagram.c \
+ dev.c \
+ dev_mcast.c \
+ dst.c \
+ iovec.c \
+ neighbour.c \
+ skbuff.c \
+ sock.c \
+ utils.c
+arch-lib-srcs := checksum.c old-checksum.c csum_partial_copy.c
+ethernet-srcs := eth.c
+ipv4-srcs := af_inet.c \
+ arp.c \
+ devinet.c \
+ fib_frontend.c \
+ fib_hash.c \
+ fib_semantics.c \
+ icmp.c \
+ igmp.c \
+ ip_forward.c \
+ ip_fragment.c \
+ ip_input.c \
+ ip_options.c \
+ ip_output.c \
+ ip_sockglue.c \
+ protocol.c \
+ raw.c \
+ route.c \
+ syncookies.c \
+ sysctl_net_ipv4.c \
+ tcp.c \
+ tcp_input.c \
+ tcp_ipv4.c \
+ tcp_output.c \
+ tcp_timer.c \
+ timer.c \
+ udp.c \
+ utils.c
+ipv6-srcs := addrconf.c \
+ af_inet6.c \
+ datagram_ipv6.c \
+ exthdrs.c \
+ icmpv6.c \
+ ip6_fib.c \
+ ip6_flowlabel.c \
+ ip6_input.c \
+ ip6_output.c \
+ ipv6_sockglue.c \
+ mcast.c \
+ ndisc.c \
+ protocol_ipv6.c \
+ raw_ipv6.c \
+ reassembly.c \
+ route_ipv6.c \
+ tcp_ipv6.c \
+ udp_ipv6.c
+ARCHS = alpha arm i386 m68k ppc s390 sparc sparc64
+
+LINUXSRCS = $(core-srcs) $(ethernet-srcs) $(ipv4-srcs) $(ipv6-srcs)
+ARCHSRCS = $(notdir $(wildcard $(addprefix \
+ $(srcdir)/linux-src/arch/$(asm_syntax)/lib/,\
+ $(arch-lib-srcs) $(arch-lib-srcs:.c=.S))))
+SRCS = sched.c timer-emul.c socket.c main.c ethernet.c \
+ io-ops.c socket-ops.c misc.c time.c options.c loopback.c \
+ kmem_cache.c stubs.c dummy.c tunnel.c pfinet-ops.c \
+ iioctl-ops.c
+MIGSRCS = ioServer.c socketServer.c startup_notifyServer.c \
+ pfinetServer.c iioctlServer.c
+OBJS = $(patsubst %.S,%.o,$(patsubst %.c,%.o,\
+ $(LINUXSRCS) $(ARCHSRCS) $(SRCS) $(MIGSRCS)))
+LINUXHDRS = bitops.h capability.h delay.h errqueue.h etherdevice.h \
+ fddidevice.h firewall.h icmp.h icmpv6.h if_arp.h if_ether.h \
+ if_fddi.h if_packet.h if_ppp.h if_tunnel.h if_tr.h \
+ igmp.h in.h in_route.h inet.h inetdevice.h init.h ip.h \
+ ip_fw.h ipsec.h ipv6_route.h ipx.h kmod.h linkage.h lists.h \
+ module.h mroute.h net.h netdevice.h netlink.h notifier.h \
+ pkt_cls.h pkt_sched.h ppp_defs.h random.h route.h \
+ rtnetlink.h skbuff.h stddef.h sysctl.h tasks.h tcp.h times.h \
+ trdevice.h udp.h
+LINUXNETHDRS = addrconf.h arp.h br.h checksum.h datalink.h dst.h flow.h \
+ icmp.h if_inet6.h inet_common.h ip.h ip_fib.h ip6_fib.h \
+ ip6_route.h ipip.h ipv6.h ipx.h ipxcall.h ndisc.h \
+ neighbour.h p8022.h p8022call.h pkt_cls.h pkt_sched.h \
+ profile.h protocol.h psnap.h psnapcall.h rarp.h raw.h \
+ rawv6.h route.h slhc.h snmp.h sock.h tcp.h transp_v6.h udp.h
+ARCHHDRS = checksum.h
+FROBBEDLINUXHEADERS = autoconf.h binfmts.h config.h errno.h fcntl.h fs.h \
+ if.h in.h in6.h interrupt.h ioctl.h ipv6.h \
+ kernel.h limits.h major.h malloc.h mm.h param.h personality.h \
+ poll.h proc_fs.h sched.h slab.h socket.h sockios.h stat.h \
+ string.h termios.h time.h timer.h timex.h types.h un.h version.h wait.h
+ASMHEADERS = atomic.h bitops.h byteorder.h delay.h errno.h hardirq.h init.h \
+ segment.h spinlock.h system.h types.h uaccess.h
+
+HURDLIBS=trivfs fshelp ports ihash shouldbeinlibc iohelp
+OTHERLIBS = -lpthread
+
+target = pfinet
+
+include ../Makeconf
+
+vpath %.c $(addprefix $(srcdir)/linux-src/net/,core ethernet ipv4 ipv6)
+vpath %.c $(srcdir)/linux-src/arch/$(asm_syntax)/lib
+vpath %.S $(srcdir)/linux-src/arch/$(asm_syntax)/lib
+
+CPPFLAGS += -imacros $(srcdir)/config.h \
+ -I$(srcdir)/glue-include \
+ -I$(srcdir)/linux-src/include
+
+# Don't ask... We use Linux code. The problem was first noticed when
+# compiling `pfinet' with GCC 4.2.
+CFLAGS += -fno-strict-aliasing
+
+asm/checksum.h: ../config.status
+ mkdir -p $(@D)
+ echo > $@.new \
+ '#include "../linux-src/include/asm-$(asm_syntax)/checksum.h"'
+ mv -f $@.new $@
+
+io-MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+socket-MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+iioctl-MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+
+# cpp doesn't automatically make dependencies for -imacros dependencies. argh.
+io_S.h ioServer.c socket_S.h socketServer.c: mig-mutate.h
+$(OBJS): config.h
diff --git a/pfinet/README b/pfinet/README
new file mode 100644
index 00000000..e96601f0
--- /dev/null
+++ b/pfinet/README
@@ -0,0 +1,55 @@
+The Hurd's pfinet server is based on networking code taken from
+the Linux kernel sources, initially version 2.2.12 of Linux.
+The subset of the Linux kernel sources used for pfinet is kept
+in the linux-src subdirectory.
+
+This file describes the procedures for tracking new Linux kernel versions
+and updating the Linux networking code in the Hurd CVS repository.
+
+The verbatim Linux kernel sources are kept on a vendor branch in CVS. To
+simplify the process of importing and merging new versions, I have mostly
+imported whole subdirectories of the linux source tree rather than just the
+precise subset of files we are actually using. However, for the arch/*/lib
+and include/asm-* subdirectories I have taken just the checksum files (the
+only thing we use from the machine-dependent Linux code); and I have
+removed the extraneous whole subdirectories within the include/linux and
+include/net directories. It is my intention to leave the remaining
+extraneous files in the CVS repository, but to include in the distributions
+only the files we are actually using (they will be listed in the Makefile).
+They could also be left on the vendor branch and removed from the trunk, but
+I think that would have more disadvantages than advantages.
+
+The initial import was done with the following commands. It is crucial to
+use `-I !' in the `cvs import' command because of the directory
+linux-src/net/core/ ("core" is in CVS's default list of names to ignore).
+
+mkdir import-tmp
+cd import-tmp
+bunzip2 < linux-2.2.12.tar.bz2 | tar xf - linux/{'arch/*/lib/*checksum*','include/asm-*/checksum.h',include/{linux,net},net/{core,ipv4,ethernet}}
+cd linux
+rm -rf include/{net,linux}/*/
+cvs -d `cat ../../CVS/Root` import -I ! -ko -m "Import of Linux 2.2.12 subset (ipv4 stack and related)" hurd/pfinet/linux-src Linux Linux_2_2_12
+
+It should work to repeat the same procedure with later versions to upgrade
+the `Linux' vendor branch. Please import only verbatim official Linux
+kernel sources, and stick to the tag name schema. If you don't already
+know how to merge the new vendor release into the trunk and finish the
+upgrade, then you probably should not be doing this anyway.
+
+On the main branch, only a few files within the linux-src tree are
+modified, and those only lightly (the changes are described in ChangeLog).
+Linux header files that are heavily modified or wholly replaced for use in
+pfinet go into the glue-include/ subdirectory instead of modifying
+linux-src files in place. Whole C source files can just be replaced with
+new files in the top-level pfinet source directory. When modifications to
+a file in linux-src are justified, the modifications should always be
+conditionalized by #ifdef _HURD_.
+
+
+ -- Roland McGrath <roland@gnu.org> 2000-02-03
+
+---
+
+See <http://www.bddebian.com/~wiki/hurd/translator/pfinet/ipv6/> about IPv6
+support and <http://lists.gnu.org/archive/html/bug-hurd/2007-10/msg00014.html>
+for some further details.
diff --git a/pfinet/config.h b/pfinet/config.h
new file mode 100644
index 00000000..95ac0f43
--- /dev/null
+++ b/pfinet/config.h
@@ -0,0 +1,40 @@
+#define __KERNEL__ 1
+#undef __SMP__
+
+#define _HURD_ 1
+#define ENONET ENETUNREACH
+
+
+#define CONFIG_NET 1
+#define CONFIG_INET 1
+#define CONFIG_IPV6 1
+
+#undef CONFIG_IPX
+#undef CONFIG_ATALK
+#undef CONFIG_PACKET
+#undef CONFIG_UNIX
+#undef CONFIG_NETLINK
+#undef CONFIG_RTNETLINK
+
+#undef CONFIG_FIREWALL
+#undef CONFIG_FILTER
+
+#undef CONFIG_IP_MULTICAST
+#undef CONFIG_IP_ROUTER
+#undef CONFIG_IP_ADVANCED_ROUTER
+#undef CONFIG_IP_PNP
+#undef CONFIG_IP_ALIAS
+
+#undef CONFIG_NET_IPIP
+#undef CONFIG_NET_IPGRE
+
+#undef CONFIG_SYN_COOKIES
+
+#undef CONFIG_INET_RARP
+
+#define CONFIG_SKB_LARGE 1
+
+#define CONFIG_IP_NOSIOCRT 1 /* How convenient. */
+
+#define CONFIG_IPV6_EUI64 1
+#undef CONFIG_IPV6_NO_PB
diff --git a/pfinet/dummy.c b/pfinet/dummy.c
new file mode 100644
index 00000000..b744f0f1
--- /dev/null
+++ b/pfinet/dummy.c
@@ -0,0 +1,134 @@
+/*
+ Copyright (C) 1995,96,98,99,2000 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "pfinet.h"
+
+#include <device/device.h>
+#include <device/net_status.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <error.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+
+struct dummy_device
+{
+ struct dummy_device *next;
+ struct device dev;
+ struct net_device_stats stats;
+};
+
+/* Linked list of all dummy devices. */
+struct dummy_device *dummy_dev;
+
+struct net_device_stats *
+dummy_get_stats (struct device *dev)
+{
+ struct dummy_device *ddev = (struct dummy_device *) dev->priv;
+ return &ddev->stats;
+}
+
+int
+dummy_stop (struct device *dev)
+{
+ return 0;
+}
+
+void
+dummy_set_multi (struct device *dev)
+{
+}
+
+int
+dummy_open (struct device *dev)
+{
+ return 0;
+}
+
+int
+dummy_xmit (struct sk_buff *skb, struct device *dev)
+{
+ struct dummy_device *ddev = (struct dummy_device *) dev->priv;
+
+ ddev->stats.tx_packets++;
+ ddev->stats.tx_bytes += skb->len;
+
+ dev_kfree_skb (skb);
+ return 0;
+}
+
+void
+setup_dummy_device (char *name, struct device **device)
+{
+ error_t err;
+ struct dummy_device *ddev;
+ struct device *dev;
+
+ ddev = calloc (1, sizeof (struct dummy_device));
+ if (!ddev)
+ error (2, ENOMEM, "%s", name);
+ ddev->next = dummy_dev;
+ dummy_dev = ddev;
+
+ *device = dev = &ddev->dev;
+
+ dev->name = strdup (name);
+
+ dev->priv = ddev;
+ dev->get_stats = dummy_get_stats;
+
+ dev->open = dummy_open;
+ dev->stop = dummy_stop;
+ dev->hard_start_xmit = dummy_xmit;
+ dev->set_multicast_list = dummy_set_multi;
+
+ /* These are the ones set by drivers/net/net_init.c::ether_setup. */
+ dev->hard_header = eth_header;
+ dev->rebuild_header = eth_rebuild_header;
+ dev->hard_header_cache = eth_header_cache;
+ dev->header_cache_update = eth_header_cache_update;
+ dev->hard_header_parse = eth_header_parse;
+ /* We can't do these two (and we never try anyway). */
+ /* dev->change_mtu = eth_change_mtu; */
+ /* dev->set_mac_address = eth_mac_addr; */
+
+ /* Some more fields */
+ dev->type = ARPHRD_ETHER;
+ dev->hard_header_len = ETH_HLEN;
+ dev->addr_len = ETH_ALEN;
+ memset (dev->broadcast, 0xff, ETH_ALEN);
+ dev->flags = IFF_BROADCAST | IFF_MULTICAST;
+ dev_init_buffers (dev);
+
+ dev->mtu = 1500;
+ dev->tx_queue_len = 0;
+ dev->flags |= IFF_NOARP;
+ dev->flags &= ~IFF_MULTICAST;
+
+ /* That should be enough. */
+
+ /* This call adds the device to the `dev_base' chain,
+ initializes its `ifindex' member (which matters!),
+ and tells the protocol stacks about the device. */
+ err = - register_netdevice (dev);
+ assert_perror (err);
+}
diff --git a/pfinet/ethernet.c b/pfinet/ethernet.c
new file mode 100644
index 00000000..053fd1be
--- /dev/null
+++ b/pfinet/ethernet.c
@@ -0,0 +1,383 @@
+/*
+ Copyright (C) 1995, 1996, 1998, 1999, 2000, 2002, 2007
+ Free Software Foundation, Inc.
+
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* Do not include glue-include/linux/errno.h */
+#define _HACK_ERRNO_H
+#include "pfinet.h"
+
+#include <device/device.h>
+#include <device/net_status.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <error.h>
+#include <fcntl.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+
+
+struct port_class *etherreadclass;
+
+struct ether_device
+{
+ struct ether_device *next;
+ device_t ether_port;
+ struct port_info *readpt;
+ mach_port_t readptname;
+ struct device dev;
+};
+
+/* Linked list of all ethernet devices. */
+struct ether_device *ether_dev;
+
+struct enet_statistics retbuf;
+
+
+/* Mach doesn't provide this. DAMN. */
+struct enet_statistics *
+ethernet_get_stats (struct device *dev)
+{
+ return &retbuf;
+}
+
+int
+ethernet_stop (struct device *dev)
+{
+ return 0;
+}
+
+void
+ethernet_set_multi (struct device *dev)
+{
+}
+
+static short ether_filter[] =
+{
+#ifdef NETF_IN
+ /* We have to tell the packet filtering code that we're interested in
+ incoming packets. */
+ NETF_IN, /* Header. */
+#endif
+ NETF_PUSHLIT | NETF_NOP,
+ 1
+};
+static int ether_filter_len = sizeof (ether_filter) / sizeof (short);
+
+static struct bpf_insn bpf_ether_filter[] =
+{
+ {NETF_IN|NETF_BPF, 0, 0, 0}, /* Header. */
+ {BPF_LD|BPF_H|BPF_ABS, 0, 0, 12}, /* Load Ethernet type */
+ {BPF_JMP|BPF_JEQ|BPF_K, 2, 0, 0x0806}, /* Accept ARP */
+ {BPF_JMP|BPF_JEQ|BPF_K, 1, 0, 0x0800}, /* Accept IPv4 */
+ {BPF_JMP|BPF_JEQ|BPF_K, 0, 1, 0x86DD}, /* Accept IPv6 */
+ {BPF_RET|BPF_K, 0, 0, 1500}, /* And return 1500 bytes */
+ {BPF_RET|BPF_K, 0, 0, 0}, /* Or discard it all */
+};
+static int bpf_ether_filter_len = sizeof (bpf_ether_filter) / sizeof (short);
+
+static struct port_bucket *etherport_bucket;
+
+
+static void *
+ethernet_thread (void *arg)
+{
+ ports_manage_port_operations_one_thread (etherport_bucket,
+ ethernet_demuxer,
+ 0);
+ return NULL;
+}
+
+int
+ethernet_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ struct net_rcv_msg *msg = (struct net_rcv_msg *) inp;
+ struct sk_buff *skb;
+ int datalen;
+ struct ether_device *edev;
+ struct device *dev = 0;
+
+ if (inp->msgh_id != NET_RCV_MSG_ID)
+ return 0;
+
+ for (edev = ether_dev; edev; edev = edev->next)
+ if (inp->msgh_local_port == edev->readptname)
+ dev = &edev->dev;
+
+ if (! dev)
+ {
+ if (inp->msgh_remote_port != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), inp->msgh_remote_port);
+ return 1;
+ }
+
+ datalen = ETH_HLEN
+ + msg->packet_type.msgt_number - sizeof (struct packet_header);
+
+ pthread_mutex_lock (&net_bh_lock);
+ skb = alloc_skb (datalen, GFP_ATOMIC);
+ skb_put (skb, datalen);
+ skb->dev = dev;
+
+ /* Copy the two parts of the frame into the buffer. */
+ bcopy (msg->header, skb->data, ETH_HLEN);
+ bcopy (msg->packet + sizeof (struct packet_header),
+ skb->data + ETH_HLEN,
+ datalen - ETH_HLEN);
+
+ /* Drop it on the queue. */
+ skb->protocol = eth_type_trans (skb, dev);
+ netif_rx (skb);
+ pthread_mutex_unlock (&net_bh_lock);
+
+ return 1;
+}
+
+
+void
+ethernet_initialize (void)
+{
+ pthread_t thread;
+ error_t err;
+ etherport_bucket = ports_create_bucket ();
+ etherreadclass = ports_create_class (0, 0);
+
+ err = pthread_create (&thread, NULL, ethernet_thread, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+}
+
+int
+ethernet_open (struct device *dev)
+{
+ error_t err;
+ device_t master_device;
+ struct ether_device *edev = (struct ether_device *) dev->priv;
+
+ assert (edev->ether_port == MACH_PORT_NULL);
+
+ err = ports_create_port (etherreadclass, etherport_bucket,
+ sizeof (struct port_info), &edev->readpt);
+ assert_perror (err);
+ edev->readptname = ports_get_right (edev->readpt);
+ mach_port_insert_right (mach_task_self (), edev->readptname, edev->readptname,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ mach_port_set_qlimit (mach_task_self (), edev->readptname, MACH_PORT_QLIMIT_MAX);
+
+ master_device = file_name_lookup (dev->name, O_READ | O_WRITE, 0);
+ if (master_device != MACH_PORT_NULL)
+ {
+ /* The device name here is the path of a device file. */
+ err = device_open (master_device, D_WRITE | D_READ, "eth", &edev->ether_port);
+ mach_port_deallocate (mach_task_self (), master_device);
+ if (err)
+ error (2, err, "device_open on %s", dev->name);
+
+ err = device_set_filter (edev->ether_port, ports_get_right (edev->readpt),
+ MACH_MSG_TYPE_MAKE_SEND, 0,
+ bpf_ether_filter, bpf_ether_filter_len);
+ if (err)
+ error (2, err, "device_set_filter on %s", dev->name);
+ }
+ else
+ {
+ /* No, perhaps a Mach device? */
+ int file_errno = errno;
+ err = get_privileged_ports (0, &master_device);
+ if (err)
+ {
+ error (0, file_errno, "file_name_lookup %s", dev->name);
+ error (2, err, "and cannot get device master port");
+ }
+ err = device_open (master_device, D_WRITE | D_READ, dev->name, &edev->ether_port);
+ mach_port_deallocate (mach_task_self (), master_device);
+ if (err)
+ {
+ error (0, file_errno, "file_name_lookup %s", dev->name);
+ error (2, err, "device_open(%s)", dev->name);
+ }
+
+ err = device_set_filter (edev->ether_port, ports_get_right (edev->readpt),
+ MACH_MSG_TYPE_MAKE_SEND, 0,
+ ether_filter, ether_filter_len);
+ if (err)
+ error (2, err, "device_set_filter on %s", dev->name);
+ }
+
+ return 0;
+}
+
+int
+ethernet_close (struct device *dev)
+{
+ struct ether_device *edev = (struct ether_device *) dev->priv;
+
+ mach_port_deallocate (mach_task_self (), edev->readptname);
+ edev->readptname = MACH_PORT_NULL;
+ ports_destroy_right (edev->readpt);
+ edev->readpt = NULL;
+ device_close (edev->ether_port);
+ mach_port_deallocate (mach_task_self (), edev->ether_port);
+ edev->ether_port = MACH_PORT_NULL;
+}
+
+/* Transmit an ethernet frame */
+int
+ethernet_xmit (struct sk_buff *skb, struct device *dev)
+{
+ error_t err;
+ struct ether_device *edev = (struct ether_device *) dev->priv;
+ u_int count;
+ u_int tried = 0;
+
+ do
+ {
+ tried++;
+ err = device_write (edev->ether_port, D_NOWAIT, 0, skb->data, skb->len, &count);
+ if (err == EMACH_SEND_INVALID_DEST || err == EMIG_SERVER_DIED)
+ {
+ /* Device probably just died, try to reopen it. */
+
+ if (tried == 2)
+ /* Too many tries, abort */
+ break;
+
+ ethernet_close (dev);
+ ethernet_open (dev);
+ }
+ else
+ {
+ assert_perror (err);
+ assert (count == skb->len);
+ }
+ }
+ while (err);
+
+ dev_kfree_skb (skb);
+ return 0;
+}
+
+/* Set device flags (e.g. promiscuous) */
+static int
+ethernet_change_flags (struct device *dev, short flags)
+{
+ error_t err = 0;
+#ifdef NET_FLAGS
+ int status = flags;
+ struct ether_device *edev = (struct ether_device *) dev->priv;
+ err = device_set_status (edev->ether_port, NET_FLAGS, &status, 1);
+ if (err == D_INVALID_OPERATION)
+ /* Not supported, ignore. */
+ err = 0;
+#endif
+ return err;
+}
+
+void
+setup_ethernet_device (char *name, struct device **device)
+{
+ struct net_status netstat;
+ size_t count;
+ int net_address[2];
+ error_t err;
+ struct ether_device *edev;
+ struct device *dev;
+
+ edev = calloc (1, sizeof (struct ether_device));
+ if (!edev)
+ error (2, ENOMEM, "%s", name);
+ edev->next = ether_dev;
+ ether_dev = edev;
+
+ *device = dev = &edev->dev;
+
+ dev->name = strdup (name);
+ /* Functions. These ones are the true "hardware layer" in Linux. */
+ dev->open = 0; /* We set up before calling dev_open. */
+ dev->stop = ethernet_stop;
+ dev->hard_start_xmit = ethernet_xmit;
+ dev->get_stats = ethernet_get_stats;
+ dev->set_multicast_list = ethernet_set_multi;
+
+ /* These are the ones set by drivers/net/net_init.c::ether_setup. */
+ dev->hard_header = eth_header;
+ dev->rebuild_header = eth_rebuild_header;
+ dev->hard_header_cache = eth_header_cache;
+ dev->header_cache_update = eth_header_cache_update;
+ dev->hard_header_parse = eth_header_parse;
+ /* We can't do these two (and we never try anyway). */
+ /* dev->change_mtu = eth_change_mtu; */
+ /* dev->set_mac_address = eth_mac_addr; */
+
+ /* Some more fields */
+ dev->priv = edev; /* For reverse lookup. */
+ dev->type = ARPHRD_ETHER;
+ dev->hard_header_len = ETH_HLEN;
+ dev->addr_len = ETH_ALEN;
+ memset (dev->broadcast, 0xff, ETH_ALEN);
+ dev->flags = IFF_BROADCAST | IFF_MULTICAST;
+
+ /* FIXME: Receive all multicast to fix IPv6, until we implement
+ ethernet_set_multi. */
+ dev->flags |= IFF_ALLMULTI;
+
+ dev->change_flags = ethernet_change_flags;
+
+ dev_init_buffers (dev);
+
+ ethernet_open (dev);
+
+ /* Fetch hardware information */
+ count = NET_STATUS_COUNT;
+ err = device_get_status (edev->ether_port, NET_STATUS,
+ (dev_status_t) &netstat, &count);
+ if (err)
+ error (2, err, "%s: Cannot get device status", name);
+ dev->mtu = netstat.max_packet_size - dev->hard_header_len;
+ assert (netstat.header_format == HDR_ETHERNET);
+ assert (netstat.header_size == ETH_HLEN);
+ assert (netstat.address_size == ETH_ALEN);
+
+ count = 2;
+ assert (count * sizeof (int) >= ETH_ALEN);
+ err = device_get_status (edev->ether_port, NET_ADDRESS, net_address, &count);
+ if (err)
+ error (2, err, "%s: Cannot get hardware Ethernet address", name);
+ net_address[0] = ntohl (net_address[0]);
+ net_address[1] = ntohl (net_address[1]);
+ bcopy (net_address, dev->dev_addr, ETH_ALEN);
+
+ /* That should be enough. */
+
+ /* This call adds the device to the `dev_base' chain,
+ initializes its `ifindex' member (which matters!),
+ and tells the protocol stacks about the device. */
+ err = - register_netdevice (dev);
+ assert_perror (err);
+}
diff --git a/pfinet/glue-include/asm/atomic.h b/pfinet/glue-include/asm/atomic.h
new file mode 100644
index 00000000..d053854e
--- /dev/null
+++ b/pfinet/glue-include/asm/atomic.h
@@ -0,0 +1,27 @@
+#ifndef _HACK_ASM_ATOMIC_H
+#define _HACK_ASM_ATOMIC_H
+
+/* We don't need atomicity in the Linux code because we serialize all
+ entries to it. */
+
+typedef struct { int counter; } atomic_t;
+
+#define ATOMIC_INIT(i) { (i) }
+
+#define atomic_read(v) ((v)->counter)
+#define atomic_set(v,i) (((v)->counter) = (i))
+
+static __inline__ void atomic_add(int i, atomic_t *v) { v->counter += i; }
+static __inline__ void atomic_sub(int i, atomic_t *v) { v->counter -= i; }
+static __inline__ void atomic_inc(atomic_t *v) { ++v->counter; }
+static __inline__ void atomic_dec(atomic_t *v) { --v->counter; }
+static __inline__ int atomic_dec_and_test(atomic_t *v)
+{ return --v->counter == 0; }
+static __inline__ int atomic_inc_and_test_greater_zero(atomic_t *v)
+{ return ++v->counter > 0; }
+
+#define atomic_clear_mask(mask, addr) (*(addr) &= ~(mask))
+#define atomic_set_mask(mask, addr) (*(addr) |= (mask))
+
+
+#endif
diff --git a/pfinet/glue-include/asm/bitops.h b/pfinet/glue-include/asm/bitops.h
new file mode 100644
index 00000000..8c5a835d
--- /dev/null
+++ b/pfinet/glue-include/asm/bitops.h
@@ -0,0 +1,37 @@
+#ifndef _HACK_ASM_BITOPS_H
+#define _HACK_ASM_BITOPS_H
+
+/* We don't need atomicity in the Linux code because we serialize all
+ entries to it. */
+
+#include <stdint.h>
+
+#define BITOPS_WORD(nr, addr) (((uint32_t *) (addr))[(nr) / 32])
+#define BITOPS_MASK(nr) (1 << ((nr) & 31))
+
+static __inline__ void set_bit (int nr, void *addr)
+{ BITOPS_WORD (nr, addr) |= BITOPS_MASK (nr); }
+
+static __inline__ void clear_bit (int nr, void *addr)
+{ BITOPS_WORD (nr, addr) &= ~BITOPS_MASK (nr); }
+
+static __inline__ void change_bit (int nr, void *addr)
+{ BITOPS_WORD (nr, addr) ^= BITOPS_MASK (nr); }
+
+static __inline__ int test_bit (int nr, void *addr)
+{ return BITOPS_WORD (nr, addr) & BITOPS_MASK (nr); }
+
+static __inline__ int test_and_set_bit (int nr, void *addr)
+{
+ int res = BITOPS_WORD (nr, addr) & BITOPS_MASK (nr);
+ BITOPS_WORD (nr, addr) |= BITOPS_MASK (nr);
+ return res;
+}
+
+#define find_first_zero_bit #error loser
+#define find_next_zero_bit #error loser
+
+#define ffz(word) (ffs (~(unsigned int) (word)) - 1)
+
+
+#endif
diff --git a/pfinet/glue-include/asm/byteorder.h b/pfinet/glue-include/asm/byteorder.h
new file mode 100644
index 00000000..a9fe66d0
--- /dev/null
+++ b/pfinet/glue-include/asm/byteorder.h
@@ -0,0 +1,155 @@
+/* Provide the specified-byte-order access functions used in the Linux
+ kernel, implemented as macros in terms of the GNU libc facilities. */
+
+#ifndef _HACK_ASM_BYTEORDER_H
+#define _HACK_ASM_BYTEORDER_H 1
+
+#include <endian.h>
+#include <byteswap.h>
+#include <hurd.h> /* gets other includes that need BYTE_ORDER */
+
+#define BO_cvt(bits, from, to, x) \
+ ((from) == (to) ? (u_int##bits##_t) (x) : bswap_##bits (x))
+#define BO_cvtp(bits, from, to, p) \
+ BO_cvt (bits, from, to, *(const u_int##bits##_t *) (p))
+#define BO_cvts(bits, from, to, p) \
+ ({ const u_int##bits##_t *_p = (p); *_p = BO_cvt (bits, from, to, *_p); })
+
+#define __cpu_to_le64(x) BO_cvt (64, BYTE_ORDER, LITTLE_ENDIAN, (x))
+#define __le64_to_cpu(x) BO_cvt (64, LITTLE_ENDIAN, BYTE_ORDER, (x))
+#define __cpu_to_le32(x) BO_cvt (32, BYTE_ORDER, LITTLE_ENDIAN, (x))
+#define __le32_to_cpu(x) BO_cvt (32, LITTLE_ENDIAN, BYTE_ORDER, (x))
+#define __cpu_to_le16(x) BO_cvt (16, BYTE_ORDER, LITTLE_ENDIAN, (x))
+#define __le16_to_cpu(x) BO_cvt (16, LITTLE_ENDIAN, BYTE_ORDER, (x))
+#define __cpu_to_be64(x) BO_cvt (64, BYTE_ORDER, BIG_ENDIAN, (x))
+#define __be64_to_cpu(x) BO_cvt (64, BIG_ENDIAN, BYTE_ORDER, (x))
+#define __cpu_to_be32(x) BO_cvt (32, BYTE_ORDER, BIG_ENDIAN, (x))
+#define __be32_to_cpu(x) BO_cvt (32, BIG_ENDIAN, BYTE_ORDER, (x))
+#define __cpu_to_be16(x) BO_cvt (16, BYTE_ORDER, BIG_ENDIAN, (x))
+#define __be16_to_cpu(x) BO_cvt (16, BIG_ENDIAN, BYTE_ORDER, (x))
+#define __cpu_to_le64p(p) BO_cvtp (64, BYTE_ORDER, LITTLE_ENDIAN, (p))
+#define __le64_to_cpup(p) BO_cvtp (64, LITTLE_ENDIAN, BYTE_ORDER, (p))
+#define __cpu_to_le32p(p) BO_cvtp (32, BYTE_ORDER, LITTLE_ENDIAN, (p))
+#define __le32_to_cpup(p) BO_cvtp (32, LITTLE_ENDIAN, BYTE_ORDER, (p))
+#define __cpu_to_le16p(p) BO_cvtp (16, BYTE_ORDER, LITTLE_ENDIAN, (p))
+#define __le16_to_cpup(p) BO_cvtp (16, LITTLE_ENDIAN, BYTE_ORDER, (p))
+#define __cpu_to_be64p(p) BO_cvtp (64, BYTE_ORDER, BIG_ENDIAN, (p))
+#define __be64_to_cpup(p) BO_cvtp (64, BIG_ENDIAN, BYTE_ORDER, (p))
+#define __cpu_to_be32p(p) BO_cvtp (32, BYTE_ORDER, BIG_ENDIAN, (p))
+#define __be32_to_cpup(p) BO_cvtp (32, BIG_ENDIAN, BYTE_ORDER, (p))
+#define __cpu_to_be16p(p) BO_cvtp (16, BYTE_ORDER, BIG_ENDIAN, (p))
+#define __be16_to_cpup(p) BO_cvtp (16, BIG_ENDIAN, BYTE_ORDER, (p))
+#define __cpu_to_le64s(p) BO_cvts (64, BYTE_ORDER, LITTLE_ENDIAN, (p))
+#define __le64_to_cpus(p) BO_cvts (64, LITTLE_ENDIAN, BYTE_ORDER, (p))
+#define __cpu_to_le32s(p) BO_cvts (32, BYTE_ORDER, LITTLE_ENDIAN, (p))
+#define __le32_to_cpus(p) BO_cvts (32, LITTLE_ENDIAN, BYTE_ORDER, (p))
+#define __cpu_to_le16s(p) BO_cvts (16, BYTE_ORDER, LITTLE_ENDIAN, (p))
+#define __le16_to_cpus(p) BO_cvts (16, LITTLE_ENDIAN, BYTE_ORDER, (p))
+#define __cpu_to_be64s(p) BO_cvts (64, BYTE_ORDER, BIG_ENDIAN, (p))
+#define __be64_to_cpus(p) BO_cvts (64, BIG_ENDIAN, BYTE_ORDER, (p))
+#define __cpu_to_be32s(p) BO_cvts (32, BYTE_ORDER, BIG_ENDIAN, (p))
+#define __be32_to_cpus(p) BO_cvts (32, BIG_ENDIAN, BYTE_ORDER, (p))
+#define __cpu_to_be16s(p) BO_cvts (16, BYTE_ORDER, BIG_ENDIAN, (p))
+#define __be16_to_cpus(p) BO_cvts (16, BIG_ENDIAN, BYTE_ORDER, (p))
+
+#define cpu_to_le64 __cpu_to_le64
+#define le64_to_cpu __le64_to_cpu
+#define cpu_to_le32 __cpu_to_le32
+#define le32_to_cpu __le32_to_cpu
+#define cpu_to_le16 __cpu_to_le16
+#define le16_to_cpu __le16_to_cpu
+#define cpu_to_be64 __cpu_to_be64
+#define be64_to_cpu __be64_to_cpu
+#define cpu_to_be32 __cpu_to_be32
+#define be32_to_cpu __be32_to_cpu
+#define cpu_to_be16 __cpu_to_be16
+#define be16_to_cpu __be16_to_cpu
+#define cpu_to_le64p __cpu_to_le64p
+#define le64_to_cpup __le64_to_cpup
+#define cpu_to_le32p __cpu_to_le32p
+#define le32_to_cpup __le32_to_cpup
+#define cpu_to_le16p __cpu_to_le16p
+#define le16_to_cpup __le16_to_cpup
+#define cpu_to_be64p __cpu_to_be64p
+#define be64_to_cpup __be64_to_cpup
+#define cpu_to_be32p __cpu_to_be32p
+#define be32_to_cpup __be32_to_cpup
+#define cpu_to_be16p __cpu_to_be16p
+#define be16_to_cpup __be16_to_cpup
+#define cpu_to_le64s __cpu_to_le64s
+#define le64_to_cpus __le64_to_cpus
+#define cpu_to_le32s __cpu_to_le32s
+#define le32_to_cpus __le32_to_cpus
+#define cpu_to_le16s __cpu_to_le16s
+#define le16_to_cpus __le16_to_cpus
+#define cpu_to_be64s __cpu_to_be64s
+#define be64_to_cpus __be64_to_cpus
+#define cpu_to_be32s __cpu_to_be32s
+#define be32_to_cpus __be32_to_cpus
+#define cpu_to_be16s __cpu_to_be16s
+#define be16_to_cpus __be16_to_cpus
+
+
+#if BYTE_ORDER == BIG_ENDIAN
+# define __BIG_ENDIAN_BITFIELD
+#elif BYTE_ORDER == LITTLE_ENDIAN
+# define __LITTLE_ENDIAN_BITFIELD
+#else
+# error __FOO_ENDIAN_BITFIELD
+#endif
+
+
+#include <netinet/in.h> /* for htonl et al */
+
+/* Though the optimized macros defined by glibc do the constant magic,
+ there are places in the Linux code that use these in constant-only
+ places like initializers, and the ({...}) expressions the macros use are
+ not valid in those contexts. */
+#if BYTE_ORDER == BIG_ENDIAN
+# if !defined(__constant_htonl)
+# define __constant_htonl(x) (x)
+# endif
+# if !defined(__constant_htons)
+# define __constant_htons(x) (x)
+# endif
+#elif BYTE_ORDER == LITTLE_ENDIAN
+# if !defined(__constant_htonl)
+# define __constant_htonl(x) \
+ ((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \
+ (((unsigned long int)(x) & 0x0000ff00U) << 8) | \
+ (((unsigned long int)(x) & 0x00ff0000U) >> 8) | \
+ (((unsigned long int)(x) & 0xff000000U) >> 24)))
+# endif
+# if !defined(__constant_htons)
+# define __constant_htons(x) \
+ ((unsigned short int)((((unsigned short int)(x) & 0x00ff) << 8) | \
+ (((unsigned short int)(x) & 0xff00) >> 8)))
+# endif
+#else
+# error "Don't know if bytes are big- or little-endian!"
+#endif
+
+/* The transformation is the same in both directions. */
+#define __constant_ntohl __constant_htonl
+#define __constant_ntohs __constant_htons
+
+
+/* Some Linux code (e.g. <net/tcp.h>) uses #ifdef __BIG_ENDIAN et al.
+ This is not real wonderful with the glibc definitions, where
+ __BIG_ENDIAN et al are always defined (as values for __BYTE_ORDER). */
+#if BYTE_ORDER == BIG_ENDIAN
+#undef __LITTLE_ENDIAN
+#elif BYTE_ORDER == LITTLE_ENDIAN
+#undef __BIG_ENDIAN
+#endif
+#undef __PDP_ENDIAN
+
+/* Since we've now broken anything that does glibc-style tests,
+ make sure they break loudly rather than silently. Any headers
+ that need __BYTE_ORDER will just have to be included before
+ we diddle with __BIG_ENDIAN or __LITTLE_ENDIAN above. */
+#undef __BYTE_ORDER
+#define __BYTE_ORDER ?????crash?????
+
+
+#endif /* asm/byteorder.h */
diff --git a/pfinet/glue-include/asm/delay.h b/pfinet/glue-include/asm/delay.h
new file mode 100644
index 00000000..7d651a40
--- /dev/null
+++ b/pfinet/glue-include/asm/delay.h
@@ -0,0 +1 @@
+/* stub file. */
diff --git a/pfinet/glue-include/asm/errno.h b/pfinet/glue-include/asm/errno.h
new file mode 100644
index 00000000..7afb6fdc
--- /dev/null
+++ b/pfinet/glue-include/asm/errno.h
@@ -0,0 +1,3 @@
+/* This is used only by checksum.S; the C code uses <linux/errno.h>. */
+
+#define EFAULT 42 /* only used in unreached code */
diff --git a/pfinet/glue-include/asm/hardirq.h b/pfinet/glue-include/asm/hardirq.h
new file mode 100644
index 00000000..c73d7dcb
--- /dev/null
+++ b/pfinet/glue-include/asm/hardirq.h
@@ -0,0 +1 @@
+#include <linux/interrupt.h>
diff --git a/pfinet/glue-include/asm/init.h b/pfinet/glue-include/asm/init.h
new file mode 100644
index 00000000..2331be7c
--- /dev/null
+++ b/pfinet/glue-include/asm/init.h
@@ -0,0 +1,3 @@
+#define __init
+#define __initdata
+#define __initfunc(x) x
diff --git a/pfinet/glue-include/asm/segment.h b/pfinet/glue-include/asm/segment.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pfinet/glue-include/asm/segment.h
diff --git a/pfinet/glue-include/asm/spinlock.h b/pfinet/glue-include/asm/spinlock.h
new file mode 100644
index 00000000..ef13312d
--- /dev/null
+++ b/pfinet/glue-include/asm/spinlock.h
@@ -0,0 +1,73 @@
+#ifndef _HACK_ASM_SPINLOCK_H_
+#define _HACK_ASM_SPINLOCK_H_
+
+typedef struct { } spinlock_t;
+#define SPIN_LOCK_UNLOCKED { }
+
+#undef spin_lock_init
+#undef spin_lock
+#undef spin_unlock
+
+#define spin_lock_init(lock) ((void) (lock))
+#define spin_lock(lock) ((void) (lock))
+#define spin_trylock(lock) ((void) (lock), 1)
+#define spin_unlock_wait(lock) ((void) (lock))
+#define spin_unlock(lock) ((void) (lock))
+#define spin_lock_irq(lock) ((void) (lock))
+#define spin_unlock_irq(lock) ((void) (lock))
+#define spin_lock_irqsave(lock, flags) ((void) (lock), (void) (flags))
+#define spin_unlock_irqrestore(lock, flags) ((void) (lock), (void) (flags))
+
+typedef struct { } rwlock_t;
+#define read_lock(rw) do { } while(0)
+#define write_lock(rw) do { } while(0)
+#define write_unlock(rw) do { } while(0)
+#define read_unlock(rw) do { } while(0)
+
+#if 0
+#include <rwlock.h>
+
+typedef struct mutex spinlock_t;
+
+#undef spin_lock_init
+#undef spin_lock
+#undef spin_unlock
+
+#define SPIN_LOCK_UNLOCKED MUTEX_INITIALIZER
+#define spin_lock_init(lock) ({ __mutex_init (lock); })
+#define spin_lock(lock) ({ __mutex_lock (lock); })
+#define spin_trylock(lock) ({ __mutex_trylock (lock); })
+#define spin_unlock_wait(lock) ({ __mutex_unlock (lock); })
+#define spin_unlock(lock) ({ __mutex_unlock (lock); })
+#define spin_lock_irq(lock) ({ __mutex_lock (lock); })
+#define spin_unlock_irq(lock) ({ __mutex_unlock (lock); })
+
+#define spin_lock_irqsave(lock, flags) \
+ do { flags = 0; __mutex_lock (lock); } while (0)
+#define spin_unlock_irqrestore(lock, flags) ({ __mutex_unlock (lock); })
+
+
+typedef struct rwlock rwlock_t;
+
+#define read_lock(rw) rwlock_reader_lock(rw)
+#define write_lock(rw) rwlock_writer_lock(rw)
+#define write_unlock(rw) rwlock_writer_unlock(rw)
+#define read_unlock(rw) rwlock_reader_unlock(rw)
+
+#endif
+
+
+#define read_lock_irq(lock) read_lock(lock)
+#define read_unlock_irq(lock) read_unlock(lock)
+#define write_lock_irq(lock) write_lock(lock)
+#define write_unlock_irq(lock) write_unlock(lock)
+
+#define read_lock_irqsave(lock, flags) \
+ do { (flags) = 0; read_lock(lock); } while (0)
+#define read_unlock_irqrestore(lock, flags) read_unlock(lock)
+#define write_lock_irqsave(lock, flags) \
+ do { (flags) = 0; write_lock(lock); } while (0)
+#define write_unlock_irqrestore(lock, flags) write_unlock(lock)
+
+
+#endif
diff --git a/pfinet/glue-include/asm/system.h b/pfinet/glue-include/asm/system.h
new file mode 100644
index 00000000..30339b62
--- /dev/null
+++ b/pfinet/glue-include/asm/system.h
@@ -0,0 +1,20 @@
+#ifndef _HACK_ASM_SYSTEM_H
+#define _HACK_ASM_SYSTEM_H
+
+/* We don't need atomicity in the Linux code because we serialize all
+ entries to it. */
+
+#include <stdint.h>
+
+#define xchg(ptr, x) \
+ ({ \
+ __typeof__ (*(ptr)) *_ptr = (ptr), _x = *_ptr; \
+ *_ptr = (x); _x; \
+ })
+
+#define mb() ((void) 0) /* memory barrier */
+#define rmb() mb()
+#define wmb() mb()
+
+
+#endif
diff --git a/pfinet/glue-include/asm/types.h b/pfinet/glue-include/asm/types.h
new file mode 100644
index 00000000..ee9b980a
--- /dev/null
+++ b/pfinet/glue-include/asm/types.h
@@ -0,0 +1 @@
+#include <linux/types.h>
diff --git a/pfinet/glue-include/asm/uaccess.h b/pfinet/glue-include/asm/uaccess.h
new file mode 100644
index 00000000..6f6dc413
--- /dev/null
+++ b/pfinet/glue-include/asm/uaccess.h
@@ -0,0 +1,53 @@
+#ifndef _HACK_ASM_UACCESS_H_
+#define _HACK_ASM_UACCESS_H_
+
+#include <linux/mm.h>
+
+
+#define MAKE_MM_SEG(s) XXX
+#define KERNEL_DS XXX
+#define USER_DS XXX
+
+#define get_ds() XXX
+#define get_fs() XXX
+#define set_fs(x) XXX
+
+#define segment_eq(a,b) XXX
+
+extern int __verify_write(const void *, unsigned long);
+#define __verify_write XXX
+
+#define __addr_ok(addr) (1)
+#define __range_ok(addr,size) (1)
+#define access_ok(type,addr,size) (1)
+
+#define put_user(x,ptr) (*(ptr) = (x), 0)
+#define get_user(x,ptr) ((x) = *(ptr), 0)
+#define __get_user(x,ptr) get_user((x), (ptr))
+
+/* This is used to constitute an arbitrarily-sized memory clobber in an asm. */
+struct __large_struct { unsigned long buf[100]; };
+#define __m(x) (*(struct __large_struct *)(x))
+
+
+/*
+ * The "xxx_ret" versions return constant specified in third argument, if
+ * something bad happens. These macros can be optimized for the
+ * case of just returning from the function xxx_ret is used.
+ */
+
+#define put_user_ret(x,ptr,ret) ({ if (put_user(x,ptr)) return ret; })
+
+#define get_user_ret(x,ptr,ret) ({ if (get_user(x,ptr)) return ret; })
+
+
+#define copy_to_user(to,from,n) (memcpy ((to), (from), (n)), 0)
+#define copy_from_user(to,from,n) (memcpy ((to), (from), (n)), 0)
+#define clear_user(mem, len) (bzero ((mem), (len)), 0)
+
+#define copy_to_user_ret(to,from,n,retval) ({ if (copy_to_user(to,from,n)) return retval; })
+
+#define copy_from_user_ret(to,from,n,retval) ({ if (copy_from_user(to,from,n)) return retval; })
+
+
+#endif
diff --git a/pfinet/glue-include/linux/autoconf.h b/pfinet/glue-include/linux/autoconf.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pfinet/glue-include/linux/autoconf.h
diff --git a/pfinet/glue-include/linux/binfmts.h b/pfinet/glue-include/linux/binfmts.h
new file mode 100644
index 00000000..c8229dad
--- /dev/null
+++ b/pfinet/glue-include/linux/binfmts.h
@@ -0,0 +1 @@
+#include <linux/capability.h>
diff --git a/pfinet/glue-include/linux/config.h b/pfinet/glue-include/linux/config.h
new file mode 100644
index 00000000..be1e4d3f
--- /dev/null
+++ b/pfinet/glue-include/linux/config.h
@@ -0,0 +1 @@
+#include <linux/errno.h>
diff --git a/pfinet/glue-include/linux/errno.h b/pfinet/glue-include/linux/errno.h
new file mode 100644
index 00000000..7afcd9fd
--- /dev/null
+++ b/pfinet/glue-include/linux/errno.h
@@ -0,0 +1,13 @@
+#ifndef _HACK_ERRNO_H
+#define _HACK_ERRNO_H
+
+#include <errno.h>
+#include <hurd.h>
+
+#define ERESTARTSYS EINTR
+#define ENOPKG ENOSYS
+#define ENOIOCTLCMD ENOTTY
+
+#undef errno
+
+#endif
diff --git a/pfinet/glue-include/linux/fcntl.h b/pfinet/glue-include/linux/fcntl.h
new file mode 100644
index 00000000..cd304557
--- /dev/null
+++ b/pfinet/glue-include/linux/fcntl.h
@@ -0,0 +1 @@
+#include <fcntl.h>
diff --git a/pfinet/glue-include/linux/fs.h b/pfinet/glue-include/linux/fs.h
new file mode 100644
index 00000000..45dbb19f
--- /dev/null
+++ b/pfinet/glue-include/linux/fs.h
@@ -0,0 +1,21 @@
+#ifndef _HACK_FS_H
+#define _HACK_FS_H
+
+#include <linux/net.h>
+
+/* Hackery */
+struct inode
+{
+ union
+ {
+ int i_garbage;
+ struct socket socket_i; /* icmp.c actually needs this!! */
+ } u;
+};
+#define i_uid u.i_garbage
+#define i_gid u.i_garbage
+#define i_sock u.i_garbage
+#define i_ino u.i_garbage
+#define i_mode u.i_garbage
+
+#endif
diff --git a/pfinet/glue-include/linux/if.h b/pfinet/glue-include/linux/if.h
new file mode 100644
index 00000000..7d4563c3
--- /dev/null
+++ b/pfinet/glue-include/linux/if.h
@@ -0,0 +1,3 @@
+#include <net/if.h>
+
+#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ALLMULTI)
diff --git a/pfinet/glue-include/linux/in.h b/pfinet/glue-include/linux/in.h
new file mode 100644
index 00000000..074c70e3
--- /dev/null
+++ b/pfinet/glue-include/linux/in.h
@@ -0,0 +1,44 @@
+#ifndef _HACK_IN_H_
+#define _HACK_IN_H_
+
+#include <netinet/in.h>
+
+/* IP_MTU_DISCOVER values */
+#define IP_PMTUDISC_DONT 0 /* Never send DF frames */
+#define IP_PMTUDISC_WANT 1 /* Use per route hints */
+#define IP_PMTUDISC_DO 2 /* Always DF */
+
+/* These need to appear somewhere around here */
+#define IP_DEFAULT_MULTICAST_TTL 1
+#define IP_DEFAULT_MULTICAST_LOOP 1
+
+struct ip_mreqn
+{
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_address; /* local IP address of interface */
+ int imr_ifindex; /* Interface index */
+};
+
+struct in_pktinfo
+{
+ int ipi_ifindex;
+ struct in_addr ipi_spec_dst;
+ struct in_addr ipi_addr;
+};
+
+
+/* <asm/byteorder.h> contains the htonl type stuff.. */
+#include <asm/byteorder.h>
+
+#ifdef __KERNEL__
+/* Some random defines to make it easier in the kernel.. */
+#define LOOPBACK(x) (((x) & htonl(0xff000000)) == htonl(0x7f000000))
+#define MULTICAST(x) (((x) & htonl(0xf0000000)) == htonl(0xe0000000))
+#define BADCLASS(x) (((x) & htonl(0xf0000000)) == htonl(0xf0000000))
+#define ZERONET(x) (((x) & htonl(0xff000000)) == htonl(0x00000000))
+#define LOCAL_MCAST(x) (((x) & htonl(0xFFFFFF00)) == htonl(0xE0000000))
+
+#endif
+
+
+#endif
diff --git a/pfinet/glue-include/linux/in6.h b/pfinet/glue-include/linux/in6.h
new file mode 100644
index 00000000..c3e5dfc4
--- /dev/null
+++ b/pfinet/glue-include/linux/in6.h
@@ -0,0 +1,110 @@
+#ifndef GLUE_LINUX_IN6_H
+#define GLUE_LINUX_IN6_H 1
+
+#include <netinet/in.h>
+
+#if 0
+struct ipv6_mreq {
+ /* IPv6 multicast address of group */
+ struct in6_addr ipv6mr_multiaddr;
+
+ /* local IPv6 address of interface */
+ int ipv6mr_ifindex;
+};
+#endif
+
+/* In Linux's struct ipv6_mreq the second member is called ipv6mr_ifindex,
+ * however it's called ipv6mr_interface in ours.
+ */
+#define ipv6mr_ifindex ipv6mr_interface
+
+struct in6_flowlabel_req
+{
+ struct in6_addr flr_dst;
+ __u32 flr_label;
+ __u8 flr_action;
+ __u8 flr_share;
+ __u16 flr_flags;
+ __u16 flr_expires;
+ __u16 flr_linger;
+ __u32 __flr_pad;
+ /* Options in format of IPV6_PKTOPTIONS */
+};
+
+#define IPV6_FL_A_GET 0
+#define IPV6_FL_A_PUT 1
+#define IPV6_FL_A_RENEW 2
+
+#define IPV6_FL_F_CREATE 1
+#define IPV6_FL_F_EXCL 2
+
+#define IPV6_FL_S_NONE 0
+#define IPV6_FL_S_EXCL 1
+#define IPV6_FL_S_PROCESS 2
+#define IPV6_FL_S_USER 3
+#define IPV6_FL_S_ANY 255
+
+
+/*
+ * Bitmask constant declarations to help applications select out the
+ * flow label and priority fields.
+ *
+ * Note that this are in host byte order while the flowinfo field of
+ * sockaddr_in6 is in network byte order.
+ */
+
+#define IPV6_FLOWINFO_FLOWLABEL 0x000fffff
+#define IPV6_FLOWINFO_PRIORITY 0x0ff00000
+
+/* These defintions are obsolete */
+#define IPV6_PRIORITY_UNCHARACTERIZED 0x0000
+#define IPV6_PRIORITY_FILLER 0x0100
+#define IPV6_PRIORITY_UNATTENDED 0x0200
+#define IPV6_PRIORITY_RESERVED1 0x0300
+#define IPV6_PRIORITY_BULK 0x0400
+#define IPV6_PRIORITY_RESERVED2 0x0500
+#define IPV6_PRIORITY_INTERACTIVE 0x0600
+#define IPV6_PRIORITY_CONTROL 0x0700
+#define IPV6_PRIORITY_8 0x0800
+#define IPV6_PRIORITY_9 0x0900
+#define IPV6_PRIORITY_10 0x0a00
+#define IPV6_PRIORITY_11 0x0b00
+#define IPV6_PRIORITY_12 0x0c00
+#define IPV6_PRIORITY_13 0x0d00
+#define IPV6_PRIORITY_14 0x0e00
+#define IPV6_PRIORITY_15 0x0f00
+
+/*
+ * IPv6 TLV options.
+ */
+#define IPV6_TLV_PAD0 0
+#define IPV6_TLV_PADN 1
+#define IPV6_TLV_ROUTERALERT 20
+#define IPV6_TLV_JUMBO 194
+
+/*
+ * IPV6 socket options
+ */
+
+#define IPV6_ADDRFORM 1
+#define IPV6_PKTINFO 2
+#define IPV6_HOPOPTS 3
+#define IPV6_DSTOPTS 4
+#define IPV6_RTHDR 5
+#define IPV6_PKTOPTIONS 6
+#define IPV6_CHECKSUM 7
+#define IPV6_HOPLIMIT 8
+#define IPV6_NEXTHOP 9
+#define IPV6_AUTHHDR 10
+#define IPV6_FLOWINFO 11
+
+/* IPV6_MTU_DISCOVER values */
+#define IPV6_PMTUDISC_DONT 0
+#define IPV6_PMTUDISC_WANT 1
+#define IPV6_PMTUDISC_DO 2
+
+/* Flowlabel */
+#define IPV6_FLOWLABEL_MGR 32
+#define IPV6_FLOWINFO_SEND 33
+
+#endif /* not GLUE_LINUX_IN6_H */
diff --git a/pfinet/glue-include/linux/interrupt.h b/pfinet/glue-include/linux/interrupt.h
new file mode 100644
index 00000000..22312ba4
--- /dev/null
+++ b/pfinet/glue-include/linux/interrupt.h
@@ -0,0 +1,46 @@
+#ifndef _HACK_INTERRUPT_H_
+#define _HACK_INTERRUPT_H_
+
+#include <linux/netdevice.h>
+#include "pfinet.h"
+
+#define in_interrupt() (0)
+#define synchronize_irq() ((void) 0)
+
+#define synchronize_bh() ((void) 0) /* XXX ? */
+
+/* The code that can call these are already entered holding
+ global_lock, which locks out the net_bh worker thread. */
+#define start_bh_atomic() ((void) 0)
+#define end_bh_atomic() ((void) 0)
+/*
+extern pthread_mutex_t net_bh_lock;
+#define start_bh_atomic() pthread_mutex_lock (&net_bh_lock)
+#define end_bh_atomic() pthread_mutex_unlock (&net_bh_lock)
+*/
+
+/* See sched.c::net_bh_worker comments. */
+extern pthread_cond_t net_bh_wakeup;
+extern int net_bh_raised;
+
+#define NET_BH 0xb00bee51
+
+/* The only call to this ever reached is in net/core/dev.c::netif_rx,
+ to announce having enqueued a packet on `backlog'. */
+static inline void
+mark_bh (int bh)
+{
+ assert (bh == NET_BH);
+ net_bh_raised = 1;
+ pthread_cond_broadcast (&net_bh_wakeup);
+}
+
+void net_bh (void);
+static inline void
+init_bh (int bh, void (*fn) (void))
+{
+ assert (bh == NET_BH);
+ assert (fn == &net_bh);
+}
+
+#endif
diff --git a/pfinet/glue-include/linux/ioctl.h b/pfinet/glue-include/linux/ioctl.h
new file mode 100644
index 00000000..6ec92cf7
--- /dev/null
+++ b/pfinet/glue-include/linux/ioctl.h
@@ -0,0 +1 @@
+#include <sys/ioctl.h>
diff --git a/pfinet/glue-include/linux/ipv6.h b/pfinet/glue-include/linux/ipv6.h
new file mode 100644
index 00000000..3b119b90
--- /dev/null
+++ b/pfinet/glue-include/linux/ipv6.h
@@ -0,0 +1,126 @@
+#ifndef _IPV6_H
+#define _IPV6_H
+
+#include <linux/config.h>
+#include <linux/in6.h>
+#include <asm/byteorder.h>
+
+/* The latest drafts declared increase in minimal mtu up to 1280. */
+
+#define IPV6_MIN_MTU 1280
+
+/*
+ * Advanced API
+ * source interface/address selection, source routing, etc...
+ * *under construction*
+ */
+
+
+struct in6_ifreq {
+ struct in6_addr ifr6_addr;
+ __u32 ifr6_prefixlen;
+ int ifr6_ifindex;
+};
+
+#define IPV6_SRCRT_STRICT 0x01 /* this hop must be a neighbor */
+#define IPV6_SRCRT_TYPE_0 0 /* IPv6 type 0 Routing Header */
+
+/*
+ * routing header
+ */
+struct ipv6_rt_hdr {
+ __u8 nexthdr;
+ __u8 hdrlen;
+ __u8 type;
+ __u8 segments_left;
+
+ /*
+ * type specific data
+ * variable length field
+ */
+};
+
+
+struct ipv6_opt_hdr {
+ __u8 nexthdr;
+ __u8 hdrlen;
+ /*
+ * TLV encoded option data follows.
+ */
+};
+
+#define ipv6_destopt_hdr ipv6_opt_hdr
+#define ipv6_hopopt_hdr ipv6_opt_hdr
+
+#ifdef __KERNEL__
+#define ipv6_optlen(p) (((p)->hdrlen+1) << 3)
+#endif
+
+/*
+ * routing header type 0 (used in cmsghdr struct)
+ */
+
+struct rt0_hdr {
+ struct ipv6_rt_hdr rt_hdr;
+ __u32 bitmap; /* strict/loose bit map */
+ struct in6_addr addr[0];
+
+#define rt0_type rt_hdr.type;
+};
+
+/*
+ * IPv6 fixed header
+ *
+ * BEWARE, it is incorrect. The first 4 bits of flow_lbl
+ * are glued to priority now, forming "class".
+ */
+
+struct ipv6hdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 priority:4,
+ version:4;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u8 version:4,
+ priority:4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ __u8 flow_lbl[3];
+
+ __u16 payload_len;
+ __u8 nexthdr;
+ __u8 hop_limit;
+
+ struct in6_addr saddr;
+ struct in6_addr daddr;
+};
+
+#ifdef __KERNEL__
+
+/*
+ This structure contains results of exthdrs parsing
+ as offsets from skb->nh.
+ */
+
+struct inet6_skb_parm
+{
+ int iif;
+ __u16 ra;
+ __u16 hop;
+ __u16 auth;
+ __u16 dst0;
+ __u16 srcrt;
+ __u16 dst1;
+};
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#define __ipv6_only_sock(sk) (sk->net_pinfo.af_inet6.ipv6only)
+#define ipv6_only_sock(sk) ((sk)->family == PF_INET6 && __ipv6_only_sock(sk))
+#else
+#define __ipv6_only_sock(sk) 0
+#define ipv6_only_sock(sk) 0
+#endif
+
+#endif
+
+#endif
diff --git a/pfinet/glue-include/linux/kernel.h b/pfinet/glue-include/linux/kernel.h
new file mode 100644
index 00000000..a0e101b9
--- /dev/null
+++ b/pfinet/glue-include/linux/kernel.h
@@ -0,0 +1,78 @@
+#ifndef _HACK_KERNEL_H
+#define _HACK_KERNEL_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+
+/* These don't actually matter since our locking protocols are different. */
+#define barrier() ((void)0) /*__asm__ __volatile__("": : :"memory") */
+
+#define NORET_TYPE /**/
+#define ATTRIB_NORET __attribute__((noreturn))
+#define NORET_AND noreturn,
+#define FASTCALL(x) x
+
+/* XXX do something syslogy */
+#define KERN_EMERG
+#define KERN_ALERT
+#define KERN_CRIT
+#define KERN_ERR
+#define KERN_WARNING
+#define KERN_NOTICE
+#define KERN_INFO
+#define KERN_DEBUG
+
+#define panic(str...) (printk (str), assert (!"panic"))
+
+/*
+ * Display an IP address in readable format.
+ */
+
+#define NIPQUAD(addr) \
+ ((unsigned char *)&addr)[0], \
+ ((unsigned char *)&addr)[1], \
+ ((unsigned char *)&addr)[2], \
+ ((unsigned char *)&addr)[3]
+
+
+#include <linux/sched.h>
+#include <linux/bitops.h>
+
+#define printk printf
+
+extern inline int
+getname (const char *name, char **newp)
+{
+ *newp = malloc (strlen (name) + 1);
+ strcpy (*newp, name);
+ return 0;
+}
+
+extern inline void
+putname (char *p)
+{
+ free (p);
+}
+
+/* These two functions are used only to send SIGURG. But I can't
+ find any SIGIO code at all. So we'll just punt on that; clearly
+ Linux is missing the point. SIGURG should only be sent for
+ sockets that have explicitly requested it. */
+extern inline int
+kill_proc (int pid, int signo, int priv)
+{
+ assert (signo == SIGURG);
+ return 0;
+}
+
+extern inline int
+kill_pg (int pgrp, int signo, int priv)
+{
+ assert (signo == SIGURG);
+ return 0;
+}
+
+
+#endif
diff --git a/pfinet/glue-include/linux/limits.h b/pfinet/glue-include/linux/limits.h
new file mode 100644
index 00000000..856c3bde
--- /dev/null
+++ b/pfinet/glue-include/linux/limits.h
@@ -0,0 +1,8 @@
+#ifndef _HACK_LIMITS_H
+#define _HACK_LIMITS_H
+
+#include <limits.h>
+
+#define UIO_MAXIOV 1024 /* probably 1 would do */
+
+#endif
diff --git a/pfinet/glue-include/linux/major.h b/pfinet/glue-include/linux/major.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pfinet/glue-include/linux/major.h
diff --git a/pfinet/glue-include/linux/malloc.h b/pfinet/glue-include/linux/malloc.h
new file mode 100644
index 00000000..46ae1051
--- /dev/null
+++ b/pfinet/glue-include/linux/malloc.h
@@ -0,0 +1,27 @@
+#ifndef _HACK_MALLOC_H_
+#define _HACK_MALLOC_H_
+
+#include <linux/mm.h>
+
+#include <stdlib.h>
+
+static inline void *kmalloc (size_t sz, int ignored) { return malloc (sz); }
+static inline void kfree (void *ptr) { free (ptr); }
+static inline void kfree_s (void *ptr, size_t sz) { free (ptr); }
+#define free(x) kfree(x) /* just don't ask */
+
+
+typedef struct kmem_cache_s kmem_cache_t;
+
+#define SLAB_HWCACHE_ALIGN 0 /* flag everybody uses */
+#define SLAB_ATOMIC 0
+
+
+extern kmem_cache_t *kmem_cache_create(const char *, size_t, size_t, unsigned long,
+ void (*)(void *, kmem_cache_t *, unsigned long),
+ void (*)(void *, kmem_cache_t *, unsigned long));
+extern void *kmem_cache_alloc(kmem_cache_t *, int);
+extern void kmem_cache_free(kmem_cache_t *, void *);
+
+
+#endif
diff --git a/pfinet/glue-include/linux/mm.h b/pfinet/glue-include/linux/mm.h
new file mode 100644
index 00000000..546776e8
--- /dev/null
+++ b/pfinet/glue-include/linux/mm.h
@@ -0,0 +1,38 @@
+#ifndef _HACK_MM_H_
+#define _HACK_MM_H_
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+/* All memory addresses are presumptively valid, because they are
+ all internal. */
+#define verify_area(a,b,c) 0
+
+#define VERIFY_READ 0
+#define VERIFY_WRITE 0
+#define GFP_ATOMIC 0
+#define GFP_KERNEL 0
+#define GFP_BUFFER 0
+#define __GFP_WAIT 0
+
+#include <mach.h>
+#include <sys/mman.h>
+#include <stdint.h>
+
+#define PAGE_SIZE (1 << PAGE_SHIFT)
+
+/* The one use of this is by net/ipv4/tcp.c::tcp_init, which
+ uses the power of two above `num_physpages >> (20 - PAGE_SHIFT)'
+ as a starting point and halves from there the number of pages
+ it tries to allocate for the hash table of TCP connections. */
+#define num_physpages (64 << 20 >> PAGE_SHIFT) /* XXX calculate for 32MB */
+
+static inline uintptr_t
+__get_free_pages (int gfp_mask, unsigned long int order)
+{
+ void *ptr = mmap (0, PAGE_SIZE << order,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ return ptr == MAP_FAILED ? 0 : (uintptr_t) ptr;
+}
+
+#endif
diff --git a/pfinet/glue-include/linux/param.h b/pfinet/glue-include/linux/param.h
new file mode 100644
index 00000000..39efaf0d
--- /dev/null
+++ b/pfinet/glue-include/linux/param.h
@@ -0,0 +1 @@
+#include <sys/param.h>
diff --git a/pfinet/glue-include/linux/personality.h b/pfinet/glue-include/linux/personality.h
new file mode 100644
index 00000000..9e218435
--- /dev/null
+++ b/pfinet/glue-include/linux/personality.h
@@ -0,0 +1 @@
+#include <linux/linkage.h>
diff --git a/pfinet/glue-include/linux/poll.h b/pfinet/glue-include/linux/poll.h
new file mode 100644
index 00000000..c785ea74
--- /dev/null
+++ b/pfinet/glue-include/linux/poll.h
@@ -0,0 +1,24 @@
+#ifndef _HACK_POLL_H_
+#define _HACK_POLL_H_
+
+#include <hurd/hurd_types.h>
+
+#define POLLIN SELECT_READ
+#define POLLRDNORM SELECT_READ
+#define POLLOUT SELECT_WRITE
+#define POLLWRNORM SELECT_WRITE
+#define POLLWRBAND SELECT_WRITE
+#define POLLPRI SELECT_URG
+#define POLLERR 0x1000
+#define POLLHUP SELECT_READ
+
+typedef struct poll_table_struct { } poll_table;
+
+#include <linux/sched.h>
+
+static inline void
+poll_wait(struct file * filp, struct wait_queue ** wait_address, poll_table *p)
+{
+}
+
+#endif
diff --git a/pfinet/glue-include/linux/proc_fs.h b/pfinet/glue-include/linux/proc_fs.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pfinet/glue-include/linux/proc_fs.h
diff --git a/pfinet/glue-include/linux/sched.h b/pfinet/glue-include/linux/sched.h
new file mode 100644
index 00000000..26ab10a8
--- /dev/null
+++ b/pfinet/glue-include/linux/sched.h
@@ -0,0 +1,207 @@
+#ifndef _HACK_SCHED_H
+#define _HACK_SCHED_H
+
+#include <sys/signal.h>
+#include <sys/time.h>
+#include <mach.h>
+#include <hurd/hurd_types.h>
+#include <limits.h>
+#include <assert.h>
+#include <pthread.h>
+#include <errno.h>
+
+#include "mapped-time.h"
+
+#include <linux/binfmts.h>
+#include <linux/personality.h>
+#include <linux/tasks.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/times.h>
+#include <linux/timex.h>
+
+#include <asm/system.h>
+#if 0
+#include <asm/semaphore.h>
+#include <asm/page.h>
+
+#include <linux/smp.h>
+#include <linux/tty.h>
+#include <linux/sem.h>
+#include <linux/signal.h>
+#include <linux/securebits.h>
+#endif
+
+#include <linux/kernel.h>
+#include <linux/net.h>
+#include <linux/wait.h>
+#include <linux/timer.h>
+
+
+#define jiffies (fetch_jiffies ())
+
+#define current (&current_contents)
+extern struct task_struct current_contents;
+struct task_struct
+{
+ uid_t pgrp, pid;
+ int flags;
+ int timeout;
+ int signal;
+ int blocked;
+ int state, policy;
+ int isroot;
+ char *comm;
+ struct wait_queue **next_wait;
+};
+
+static inline void
+prepare_current (int isroot)
+{
+ current->signal = 0;
+ current->isroot = isroot;
+ /* All other members are constant zero and ignored. */
+}
+#define become_task(user) prepare_current ((user)->isroot)
+#define become_task_protid(protid) prepare_current ((protid)->isroot)
+
+#define signal_pending(current) ((current)->signal)
+
+/* FLAGS in task_struct's. */
+#define PF_EXITING 1
+/* STATE in task_struct's. */
+#define TASK_INTERRUPTIBLE 0
+#define TASK_RUNNING 0
+/* policy in task_struct's. */
+#define SCHED_YIELD 0
+
+struct semaphore { };
+
+
+extern inline int
+suser ()
+{
+ return current->isroot;
+};
+
+extern inline int
+capable(int cap)
+{
+ return current->isroot;
+}
+
+
+extern pthread_mutex_t global_lock;
+
+static inline int
+interruptible_sleep_on_timeout (struct wait_queue **p, struct timespec *tsp)
+{
+ pthread_cond_t **condp = (void *) p, *c;
+ int isroot;
+ struct wait_queue **next_wait;
+ error_t err;
+
+ c = *condp;
+ if (c == 0)
+ {
+ c = malloc (sizeof **condp);
+ assert (c);
+ pthread_cond_init (c, NULL);
+ *condp = c;
+ }
+
+ isroot = current->isroot; /* This is our context that needs switched. */
+ next_wait = current->next_wait; /* This too, for multiple schedule calls. */
+ current->next_wait = 0;
+ err = pthread_hurd_cond_timedwait_np(c, &global_lock, tsp);
+ if (err == EINTR)
+ current->signal = 1; /* We got cancelled, mark it for later. */
+ current->isroot = isroot; /* Switch back to our context. */
+ current->next_wait = next_wait;
+ return (err == ETIMEDOUT);
+}
+
+static inline void
+wake_up_interruptible (struct wait_queue **p)
+{
+ pthread_cond_t **condp = (void *) p, *c = *condp;
+ if (c)
+ pthread_cond_broadcast (c);
+}
+#define wake_up wake_up_interruptible
+
+
+static inline void
+add_wait_queue(struct wait_queue ** p, struct wait_queue * wait)
+{
+ assert (current->next_wait == 0);
+ current->next_wait = p;
+}
+
+static inline void
+remove_wait_queue(struct wait_queue ** p, struct wait_queue * wait)
+{
+ assert (current->next_wait == p);
+ current->next_wait = 0;
+}
+
+static inline void
+schedule (void)
+{
+ assert (current->next_wait);
+ interruptible_sleep_on_timeout (current->next_wait, NULL);
+}
+
+static inline void
+process_schedule_timeout (unsigned long data)
+{
+ struct wait_queue **sleep = (struct wait_queue **) data;
+
+ wake_up_interruptible (sleep);
+}
+
+static inline long
+schedule_timeout (long timeout)
+{
+ long expire = timeout + jiffies;
+ struct timer_list timer;
+ static struct wait_queue *sleep = 0; /* See comment in wait.h why this suffices. */
+ /* TODO: but free it !! */
+
+ init_timer (&timer);
+ timer.expires = expire;
+ timer.data = (unsigned long) &sleep;
+ timer.function = process_schedule_timeout;
+ add_timer (&timer);
+
+ interruptible_sleep_on_timeout (&sleep, NULL);
+ if (signal_pending (current))
+ {
+ /* We were canceled. */
+ del_timer (&timer);
+ expire -= jiffies;
+ if (expire >= 0)
+ return expire;
+ else
+ return 0;
+ }
+ return 0;
+}
+
+#define MAX_SCHEDULE_TIMEOUT LONG_MAX
+
+/* This function is used only to send SIGPIPE to the current
+ task. In all such cases, EPIPE is returned anyhow. In the
+ Hurd, servers are not responsible for SIGPIPE; the library
+ does that itself upon receiving EPIPE. So we can just
+ NOP such calls. */
+extern inline int
+send_sig (u_long signo, struct task_struct *task, int priv)
+{
+ assert (signo == SIGPIPE);
+ assert (task == current);
+ return 0;
+}
+
+
+#endif
diff --git a/pfinet/glue-include/linux/slab.h b/pfinet/glue-include/linux/slab.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pfinet/glue-include/linux/slab.h
diff --git a/pfinet/glue-include/linux/socket.h b/pfinet/glue-include/linux/socket.h
new file mode 100644
index 00000000..057afbdb
--- /dev/null
+++ b/pfinet/glue-include/linux/socket.h
@@ -0,0 +1,158 @@
+#ifndef _HACK_SOCKET_H_
+#define _HACK_SOCKET_H_
+
+#include <linux/types.h>
+#include <asm/system.h>
+
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <limits.h>
+
+
+/* #define IP_MAX_MEMBERSHIPS 10 */
+
+#define IPTOS_LOWDELAY 0x10
+#define IPTOS_THROUGHPUT 0x08
+#define IPTOS_RELIABILITY 0x04
+
+#define SOPRI_INTERACTIVE 0
+#define SOPRI_NORMAL 1
+#define SOPRI_BACKGROUND 2
+
+#ifndef SOL_IP
+#define SOL_IP IPPROTO_IP
+#endif
+#define SOL_TCP IPPROTO_TCP
+#ifndef SOL_IPV6
+#define SOL_IPV6 IPPROTO_IPV6
+#endif
+#ifndef SOL_ICMPV6
+#define SOL_ICMPV6 IPPROTO_ICMPV6
+#endif
+#define SOL_RAW IPPROTO_RAW
+
+/* IP options */
+#define IP_PKTINFO 190
+#define IP_PKTOPTIONS 191
+#define IP_MTU_DISCOVER 192
+#define IP_RECVERR 193
+#define IP_RECVTTL 194
+#define IP_RECVTOS 195
+#define IP_MTU 196
+#define IP_ROUTER_ALERT 197
+
+
+/* TCP options */
+#define TCP_NODELAY 1
+#define TCP_MAXSEG 2
+#define TCP_CORK 3
+
+#define SO_NO_CHECK 11
+#define SO_PRIORITY 12
+
+#define SO_PASSCRED 190
+#define SO_PEERCRED 191
+#define SO_BSDCOMPAT 192
+
+/* Maximum queue length specifiable by listen. */
+#ifndef SOMAXCONN
+#define SOMAXCONN 128
+#endif
+
+#ifndef CMSG_DATA
+#define msg_control msg_accrights
+#define msg_controllen msg_accrightslen
+struct cmsghdr { int cmsg_garbage; };
+#define cmsg_len cmsg_garbage
+#define cmsg_type cmsg_garbage
+#define cmsg_level cmsg_garbage
+static inline int
+put_cmsg(struct msghdr *msg, int level, int type, int len, void *data)
+{ return 0; }
+#define CMSG_FIRSTHDR(msg) (0)
+#define CMSG_NXTHDR(msg, cmsg) (0)
+#define CMSG_DATA(cmsg) (0)
+#define CMSG_ALIGN(size) (0)
+#define CMSG_LEN(size) (0)
+#else
+static inline int
+put_cmsg(struct msghdr *msg, int level, int type, int len, void *data)
+{ return 0; }
+#endif
+
+#ifndef MSG_NOSIGNAL
+# warning "http://lists.gnu.org/archive/html/bug-hurd/2008-10/msg00007.html"
+# define MSG_NOSIGNAL 0
+#endif
+#define MSG_ERRQUEUE 0
+
+/* There is no SOCK_PACKET, it is a bad bad thing. This chicanery is
+ because the one use of it is a comparison against a `short int' value;
+ using a value outside the range of that type ensures that the comparison
+ will always fail, and in fact it and the dead code will get optimized
+ out entirely at compile time. */
+#define SOCK_PACKET ((int)((uint32_t)USHRT_MAX) * 2)
+#define PF_PACKET 0
+
+#ifndef UIO_MAXIOV
+#define UIO_MAXIOV 4 /* 1 would do */
+#endif
+
+
+struct ucred {
+ pid_t pid;
+ uid_t uid;
+ gid_t gid;
+};
+
+
+extern inline int /* Does not modify IOV. */
+memcpy_fromiovecend (unsigned char *kdata, struct iovec *iov,
+ int offset, int len)
+{
+ assert (offset + len <= iov->iov_len);
+ memcpy (kdata, iov->iov_base + offset, len);
+ return 0;
+}
+extern inline int /* Modifies IOV to consume LEN bytes. */
+memcpy_fromiovec (unsigned char *kdata, struct iovec *iov, int len)
+{
+ assert (len <= iov->iov_len);
+ memcpy (kdata, iov->iov_base, len);
+ iov->iov_base += len;
+ iov->iov_len -= len;
+ return 0;
+}
+extern inline void /* Modifies IOV to consume LEN bytes. */
+memcpy_tokerneliovec (struct iovec *iov, unsigned char *kdata, int len)
+{
+ assert (len <= iov->iov_len);
+ memcpy (iov->iov_base, kdata, len);
+ iov->iov_base += len;
+ iov->iov_len -= len;
+}
+extern inline int /* Modifies IOV to consume LEN bytes. */
+memcpy_toiovec (struct iovec *iov, unsigned char *kdata, int len)
+{
+ memcpy_tokerneliovec (iov, kdata, len);
+ return 0;
+}
+
+extern int csum_partial_copy_fromiovecend(unsigned char *kdata,
+ struct iovec *iov,
+ int offset,
+ unsigned int len, int *csump);
+
+static inline int move_addr_to_kernel(void *uaddr, int ulen, void *kaddr)
+{
+ abort ();
+ return 0;
+}
+#if 0
+extern int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode);
+extern int move_addr_to_user(void *kaddr, int klen, void *uaddr, int *ulen);
+#endif
+
+
+#endif
diff --git a/pfinet/glue-include/linux/sockios.h b/pfinet/glue-include/linux/sockios.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pfinet/glue-include/linux/sockios.h
diff --git a/pfinet/glue-include/linux/stat.h b/pfinet/glue-include/linux/stat.h
new file mode 100644
index 00000000..5165069b
--- /dev/null
+++ b/pfinet/glue-include/linux/stat.h
@@ -0,0 +1 @@
+#include <sys/stat.h>
diff --git a/pfinet/glue-include/linux/string.h b/pfinet/glue-include/linux/string.h
new file mode 100644
index 00000000..3b2f5900
--- /dev/null
+++ b/pfinet/glue-include/linux/string.h
@@ -0,0 +1 @@
+#include <string.h>
diff --git a/pfinet/glue-include/linux/termios.h b/pfinet/glue-include/linux/termios.h
new file mode 100644
index 00000000..9e269565
--- /dev/null
+++ b/pfinet/glue-include/linux/termios.h
@@ -0,0 +1 @@
+#include <termios.h>
diff --git a/pfinet/glue-include/linux/time.h b/pfinet/glue-include/linux/time.h
new file mode 100644
index 00000000..4973c4ab
--- /dev/null
+++ b/pfinet/glue-include/linux/time.h
@@ -0,0 +1,10 @@
+#ifndef _HACK_TIME_H_
+#define _HACK_TIME_H_
+
+#include <sys/time.h>
+#include "mapped-time.h"
+
+#define do_gettimeofday(tp) maptime_read (mapped_time, (tp))
+#define get_fast_time(tp) maptime_read (mapped_time, (tp))
+
+#endif
diff --git a/pfinet/glue-include/linux/timer.h b/pfinet/glue-include/linux/timer.h
new file mode 100644
index 00000000..5497b109
--- /dev/null
+++ b/pfinet/glue-include/linux/timer.h
@@ -0,0 +1,36 @@
+#ifndef _HACK_TIMER_H_
+#define _HACK_TIMER_H_
+
+#include <pthread.h>
+
+enum tstate
+{
+ TIMER_INACTIVE,
+ TIMER_STARTING,
+ TIMER_STARTED,
+ TIMER_EXPIRED,
+ TIMER_FUNCTION_RUNNING,
+};
+
+struct timer_list
+{
+ struct timer_list *next, **prev; /* things like to test "T->prev != NULL" */
+ unsigned long expires;
+ unsigned long data;
+ void (*function)(unsigned long);
+};
+
+void add_timer (struct timer_list *);
+int del_timer (struct timer_list *);
+void mod_timer (struct timer_list *, unsigned long);
+void init_timer (struct timer_list *);
+
+
+#define time_after(a,b) ((long)(b) - (long)(a) < 0)
+#define time_before(a,b) time_after(b,a)
+
+#define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0)
+#define time_before_eq(a,b) time_after_eq(b,a)
+
+
+#endif
diff --git a/pfinet/glue-include/linux/timex.h b/pfinet/glue-include/linux/timex.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pfinet/glue-include/linux/timex.h
diff --git a/pfinet/glue-include/linux/types.h b/pfinet/glue-include/linux/types.h
new file mode 100644
index 00000000..604b8b14
--- /dev/null
+++ b/pfinet/glue-include/linux/types.h
@@ -0,0 +1,31 @@
+#ifndef _HACK_TYPES_H
+#define _HACK_TYPES_H
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+
+#define __u8 uint8_t
+#define __u16 uint16_t
+#define __u32 uint32_t
+#define __u64 uint64_t
+#define __s8 int8_t
+#define __s16 int16_t
+#define __s32 int32_t
+#define __s64 int64_t
+#define u8 uint8_t
+#define u16 uint16_t
+#define u32 uint32_t
+#define u64 uint64_t
+#define s8 int8_t
+#define s16 int16_t
+#define s32 int32_t
+#define s64 int64_t
+
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <linux/errno.h>
+
+#endif
diff --git a/pfinet/glue-include/linux/un.h b/pfinet/glue-include/linux/un.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pfinet/glue-include/linux/un.h
diff --git a/pfinet/glue-include/linux/version.h b/pfinet/glue-include/linux/version.h
new file mode 100644
index 00000000..3a49a481
--- /dev/null
+++ b/pfinet/glue-include/linux/version.h
@@ -0,0 +1,3 @@
+#define UTS_RELEASE "2.1.12"
+#define LINUX_VERSION_CODE KERNEL_VERSION(2,1,12)
+#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
diff --git a/pfinet/glue-include/linux/wait.h b/pfinet/glue-include/linux/wait.h
new file mode 100644
index 00000000..58f4960e
--- /dev/null
+++ b/pfinet/glue-include/linux/wait.h
@@ -0,0 +1,32 @@
+#ifndef _HACK_WAIT_H_
+#define _HACK_WAIT_H_
+
+#include <pthread.h>
+
+/* This data structure actually represents one waiter on a wait queue,
+ and waiters always expect to initialize it with { current, NULL }.
+ The actual wait queue is a `struct wait_queue *' stored somewhere.
+ We ignore these structures provided by the waiters entirely.
+ In the `struct wait_queue *' that is the "head of the wait queue" slot,
+ we actually store a `pthread_cond_t *' pointing to malloc'd storage. */
+
+struct wait_queue
+{
+ struct task_struct *task; /* current */
+ struct wait_queue *next; /* NULL */
+};
+
+
+struct select_table_elt
+{
+ pthread_cond_t *dependent_condition;
+ struct select_table_elt *next;
+};
+
+typedef struct select_table_struct
+{
+ pthread_cond_t master_condition;
+ struct select_table_elt *head;
+} select_table;
+
+#endif
diff --git a/pfinet/iioctl-ops.c b/pfinet/iioctl-ops.c
new file mode 100644
index 00000000..f1c503d4
--- /dev/null
+++ b/pfinet/iioctl-ops.c
@@ -0,0 +1,388 @@
+/*
+ Copyright (C) 2000, 2007 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "pfinet.h"
+
+#include <linux/netdevice.h>
+#include <linux/notifier.h>
+
+#include "iioctl_S.h"
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <mach/notify.h>
+#include <sys/mman.h>
+#include <hurd/fshelp.h>
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/sock.h>
+
+extern struct notifier_block *netdev_chain;
+
+/* devinet.c */
+extern error_t configure_device (struct device *dev, uint32_t addr,
+ uint32_t netmask, uint32_t peer,
+ uint32_t broadcast);
+extern void inquire_device (struct device *dev, uint32_t *addr,
+ uint32_t *netmask, uint32_t *peer,
+ uint32_t *broadcast);
+
+/* Truncate name, take the global lock and find device with this name. */
+struct device *get_dev (char *name)
+{
+ char ifname[16];
+ struct device *dev;
+
+ memcpy (ifname, name, IFNAMSIZ-1);
+ ifname[IFNAMSIZ-1] = 0;
+
+ pthread_mutex_lock (&global_lock);
+
+ for (dev = dev_base; dev; dev = dev->next)
+ if (strcmp (dev->name, ifname) == 0)
+ break;
+
+ return dev;
+}
+
+enum siocgif_type
+{
+ ADDR,
+ NETMASK,
+ DSTADDR,
+ BRDADDR
+};
+
+#define SIOCGIF(name, type) \
+ kern_return_t \
+ S_iioctl_siocgif##name (struct sock_user *user, \
+ ifname_t ifnam, \
+ sockaddr_t *addr) \
+ { \
+ return siocgifXaddr (user, ifnam, addr, type); \
+ }
+
+/* Get some sockaddr type of info. */
+static kern_return_t
+siocgifXaddr (struct sock_user *user,
+ ifname_t ifnam,
+ sockaddr_t *addr,
+ enum siocgif_type type)
+{
+ error_t err = 0;
+ struct device *dev;
+ struct sockaddr_in *sin = (struct sockaddr_in *) addr;
+ uint32_t addrs[4];
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ dev = get_dev (ifnam);
+ if (!dev)
+ err = ENODEV;
+ else if (user->sock->sk->family != AF_INET)
+ err = EINVAL;
+ else
+ {
+ sin->sin_family = AF_INET;
+
+ inquire_device (dev, &addrs[0], &addrs[1], &addrs[2], &addrs[3]);
+ sin->sin_addr.s_addr = addrs[type];
+ }
+
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+#define SIOCSIF(name, type) \
+ kern_return_t \
+ S_iioctl_siocsif##name (struct sock_user *user, \
+ ifname_t ifnam, \
+ sockaddr_t addr) \
+ { \
+ return siocsifXaddr (user, ifnam, &addr, type); \
+ }
+
+/* Set some sockaddr type of info. */
+static kern_return_t
+siocsifXaddr (struct sock_user *user,
+ ifname_t ifnam,
+ sockaddr_t *addr,
+ enum siocgif_type type)
+{
+ error_t err = 0;
+ struct device *dev;
+ struct sockaddr_in *sin = (struct sockaddr_in *) addr;
+ uint32_t addrs[4];
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ dev = get_dev (ifnam);
+
+ if (!user->isroot)
+ err = EPERM;
+ else if (!dev)
+ err = ENODEV;
+ else if (sin->sin_family != AF_INET)
+ err = EINVAL;
+ else if (user->sock->sk->family != AF_INET)
+ err = EINVAL;
+ else
+ {
+ inquire_device (dev, &addrs[0], &addrs[1], &addrs[2], &addrs[3]);
+ addrs[type] = sin->sin_addr.s_addr;
+ err = configure_device (dev, addrs[0], addrs[1], addrs[2], addrs[3]);
+ }
+
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* 12 SIOCSIFADDR -- Set address of a network interface. */
+SIOCSIF (addr, ADDR);
+
+/* 14 SIOCSIFDSTADDR -- Set point-to-point (peer) address of a network interface. */
+SIOCSIF (dstaddr, DSTADDR);
+
+/* 16 SIOCSIFFLAGS -- Set flags of a network interface. */
+kern_return_t
+S_iioctl_siocsifflags (struct sock_user *user,
+ ifname_t ifnam,
+ short flags)
+{
+ error_t err = 0;
+ struct device *dev;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ dev = get_dev (ifnam);
+
+ if (!user->isroot)
+ err = EPERM;
+ else if (!dev)
+ err = ENODEV;
+ else
+ err = dev_change_flags (dev, flags);
+
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* 17 SIOCGIFFLAGS -- Get flags of a network interface. */
+kern_return_t
+S_iioctl_siocgifflags (struct sock_user *user,
+ char *name,
+ short *flags)
+{
+ error_t err = 0;
+ struct device *dev;
+
+ dev = get_dev (name);
+ if (!dev)
+ err = ENODEV;
+ else
+ {
+ *flags = dev->flags;
+ }
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* 19 SIOCSIFBRDADDR -- Set broadcast address of a network interface. */
+SIOCSIF (brdaddr, BRDADDR);
+
+/* 22 SIOCSIFNETMASK -- Set netmask of a network interface. */
+SIOCSIF (netmask, NETMASK);
+
+/* 23 SIOCGIFMETRIC -- Get metric of a network interface. */
+kern_return_t
+S_iioctl_siocgifmetric (struct sock_user *user,
+ ifname_t ifnam,
+ int *metric)
+{
+ error_t err = 0;
+ struct device *dev;
+
+ dev = get_dev (ifnam);
+ if (!dev)
+ err = ENODEV;
+ else
+ {
+ *metric = 0; /* Not supported. */
+ }
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* 24 SIOCSIFMETRIC -- Set metric of a network interface. */
+kern_return_t
+S_iioctl_siocsifmetric (struct sock_user *user,
+ ifname_t ifnam,
+ int metric)
+{
+ return EOPNOTSUPP;
+}
+
+/* 25 SIOCDIFADDR -- Delete interface address. */
+kern_return_t
+S_iioctl_siocdifaddr (struct sock_user *user,
+ ifname_t ifnam,
+ sockaddr_t addr)
+{
+ return EOPNOTSUPP;
+}
+
+/* 33 SIOCGIFADDR -- Get address of a network interface. */
+SIOCGIF (addr, ADDR);
+
+/* 34 SIOCGIFDSTADDR -- Get point-to-point address of a network interface. */
+SIOCGIF (dstaddr, DSTADDR);
+
+/* 35 SIOCGIFBRDADDR -- Get broadcast address of a network interface. */
+SIOCGIF (brdaddr, BRDADDR);
+
+/* 37 SIOCGIFNETMASK -- Get netmask of a network interface. */
+SIOCGIF (netmask, NETMASK);
+
+/* 39 SIOCGIFHWADDR -- Get the hardware address of a network interface. */
+error_t
+S_iioctl_siocgifhwaddr (struct sock_user *user,
+ ifname_t ifname,
+ sockaddr_t *addr)
+{
+ error_t err = 0;
+ struct device *dev;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ dev = get_dev (ifname);
+ if (!dev)
+ err = ENODEV;
+ else
+ {
+ memcpy (addr->sa_data, dev->dev_addr, dev->addr_len);
+ addr->sa_family = dev->type;
+ }
+
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* 51 SIOCGIFMTU -- Get mtu of a network interface. */
+error_t
+S_iioctl_siocgifmtu (struct sock_user *user,
+ ifname_t ifnam,
+ int *mtu)
+{
+ error_t err = 0;
+ struct device *dev;
+
+ dev = get_dev (ifnam);
+ if (!dev)
+ err = ENODEV;
+ else
+ {
+ *mtu = dev->mtu;
+ }
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* 51 SIOCSIFMTU -- Set mtu of a network interface. */
+error_t
+S_iioctl_siocsifmtu (struct sock_user *user,
+ ifname_t ifnam,
+ int mtu)
+{
+ error_t err = 0;
+ struct device *dev;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ dev = get_dev (ifnam);
+
+ if (!user->isroot)
+ err = EPERM;
+ if (!dev)
+ err = ENODEV;
+ else if (mtu <= 0)
+ err = EINVAL;
+ else
+ {
+ if (dev->change_mtu)
+ dev->change_mtu (dev, mtu);
+ else
+ dev->mtu = mtu;
+
+ notifier_call_chain (&netdev_chain, NETDEV_CHANGEMTU, dev);
+ }
+
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* 100 SIOCGIFINDEX -- Get index number of a network interface. */
+error_t
+S_iioctl_siocgifindex (struct sock_user *user,
+ ifname_t ifnam,
+ int *index)
+{
+ error_t err = 0;
+ struct device *dev;
+
+ dev = get_dev (ifnam);
+ if (!dev)
+ err = ENODEV;
+ else
+ {
+ *index = dev->ifindex;
+ }
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* 101 SIOCGIFNAME -- Get name of a network interface from index number. */
+error_t
+S_iioctl_siocgifname (struct sock_user *user,
+ ifname_t ifnam,
+ int *index)
+{
+ error_t err = 0;
+ struct device *dev;
+
+ pthread_mutex_lock (&global_lock);
+ dev = dev_get_by_index (*index);
+ if (!dev)
+ err = ENODEV;
+ else
+ {
+ strncpy (ifnam, dev->name, IFNAMSIZ);
+ ifnam[IFNAMSIZ-1] = '\0';
+ }
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
diff --git a/pfinet/io-ops.c b/pfinet/io-ops.c
new file mode 100644
index 00000000..96dbec85
--- /dev/null
+++ b/pfinet/io-ops.c
@@ -0,0 +1,648 @@
+/*
+ Copyright (C) 1995,96,97,98,99,2000,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "pfinet.h"
+
+#include <linux/wait.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/poll.h>
+#include <net/sock.h>
+
+#include "io_S.h"
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <mach/notify.h>
+#include <sys/mman.h>
+
+error_t
+S_io_write (struct sock_user *user,
+ char *data,
+ size_t datalen,
+ off_t offset,
+ mach_msg_type_number_t *amount)
+{
+ error_t err;
+ struct iovec iov = { data, datalen };
+ struct msghdr m = { msg_name: 0, msg_namelen: 0, msg_flags: 0,
+ msg_controllen: 0, msg_iov: &iov, msg_iovlen: 1 };
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ become_task (user);
+ if (user->sock->flags & O_NONBLOCK)
+ m.msg_flags |= MSG_DONTWAIT;
+ err = (*user->sock->ops->sendmsg) (user->sock, &m, datalen, 0);
+ pthread_mutex_unlock (&global_lock);
+
+ if (err < 0)
+ err = -err;
+ else
+ {
+ *amount = err;
+ err = 0;
+ }
+
+ return err;
+}
+
+error_t
+S_io_read (struct sock_user *user,
+ char **data,
+ size_t *datalen,
+ off_t offset,
+ mach_msg_type_number_t amount)
+{
+ error_t err;
+ int alloced = 0;
+ struct iovec iov;
+ struct msghdr m = { msg_name: 0, msg_namelen: 0, msg_flags: 0,
+ msg_controllen: 0, msg_iov: &iov, msg_iovlen: 1 };
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ /* Instead of this, we should peek and the socket and only
+ allocate as much as necessary. */
+ if (amount > *datalen)
+ {
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ /* Should check whether errno is indeed ENOMEM --
+ but this can't be done in a straightforward way,
+ because the glue headers #undef errno. */
+ return ENOMEM;
+ alloced = 1;
+ }
+
+ iov.iov_base = *data;
+ iov.iov_len = amount;
+
+ pthread_mutex_lock (&global_lock);
+ become_task (user);
+ err = (*user->sock->ops->recvmsg) (user->sock, &m, amount,
+ ((user->sock->flags & O_NONBLOCK)
+ ? MSG_DONTWAIT : 0),
+ 0);
+ pthread_mutex_unlock (&global_lock);
+
+ if (err < 0)
+ {
+ err = -err;
+ if (alloced)
+ munmap (*data, amount);
+ }
+ else
+ {
+ *datalen = err;
+ if (alloced && round_page (*datalen) < round_page (amount))
+ munmap (*data + round_page (*datalen),
+ round_page (amount) - round_page (*datalen));
+ err = 0;
+ }
+ return err;
+}
+
+error_t
+S_io_seek (struct sock_user *user,
+ off_t offset,
+ int whence,
+ off_t *newp)
+{
+ return user ? ESPIPE : EOPNOTSUPP;
+}
+
+error_t
+S_io_readable (struct sock_user *user,
+ mach_msg_type_number_t *amount)
+{
+ struct sock *sk;
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ become_task (user);
+
+ /* We need to avoid calling the Linux ioctl routines,
+ so here is a rather ugly break of modularity. */
+
+ sk = user->sock->sk;
+ err = 0;
+
+ /* Linux's af_inet.c ioctl routine just calls the protocol-specific
+ ioctl routine; it's those routines that we need to simulate. So
+ this switch corresponds to the initialization of SK->prot in
+ af_inet.c:inet_create. */
+ switch (user->sock->type)
+ {
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ err = tcp_tiocinq (sk, amount);
+ break;
+
+ case SOCK_DGRAM:
+ /* These guts are copied from udp.c:udp_ioctl (TIOCINQ). */
+ if (sk->state == TCP_LISTEN)
+ err = EINVAL;
+ else
+ /* Boy, I really love the C language. */
+ *amount = (skb_peek (&sk->receive_queue)
+ ? : &((struct sk_buff){}))->len;
+ break;
+
+ case SOCK_RAW:
+ default:
+ err = EOPNOTSUPP;
+ break;
+ }
+
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+error_t
+S_io_set_all_openmodes (struct sock_user *user,
+ int bits)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ if (bits & O_NONBLOCK)
+ user->sock->flags |= O_NONBLOCK;
+ else
+ user->sock->flags &= ~O_NONBLOCK;
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+S_io_get_openmodes (struct sock_user *user,
+ int *bits)
+{
+ struct sock *sk;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ sk = user->sock->sk;
+
+ *bits = 0;
+ if (!(sk->shutdown & SEND_SHUTDOWN))
+ *bits |= O_WRITE;
+ if (!(sk->shutdown & RCV_SHUTDOWN))
+ *bits |= O_READ;
+ if (user->sock->flags & O_NONBLOCK)
+ *bits |= O_NONBLOCK;
+
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+S_io_set_some_openmodes (struct sock_user *user,
+ int bits)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ if (bits & O_NONBLOCK)
+ user->sock->flags |= O_NONBLOCK;
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+S_io_clear_some_openmodes (struct sock_user *user,
+ int bits)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ if (bits & O_NONBLOCK)
+ user->sock->flags &= ~O_NONBLOCK;
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+static error_t
+io_select_common (struct sock_user *user,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ struct timespec *tsp, int *select_type)
+{
+ const int want = *select_type | POLLERR;
+ int avail, timedout;
+ int ret = 0;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ become_task (user);
+
+ /* In Linux, this means (supposedly) that I/O will never be possible.
+ That's a lose, so prevent it from happening. */
+ assert (user->sock->ops->poll);
+
+ avail = (*user->sock->ops->poll) ((void *) 0xdeadbeef,
+ user->sock,
+ (void *) 0xdeadbead);
+ if ((avail & want) == 0)
+ {
+ ports_interrupt_self_on_notification (user, reply,
+ MACH_NOTIFY_DEAD_NAME);
+
+ do
+ {
+ /* Block until we time out, are woken or cancelled. */
+ timedout = interruptible_sleep_on_timeout (user->sock->sk->sleep,
+ tsp);
+ if (timedout)
+ {
+ pthread_mutex_unlock (&global_lock);
+ *select_type = 0;
+ return 0;
+ }
+ else if (signal_pending (current)) /* This means we were cancelled. */
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EINTR;
+ }
+ avail = (*user->sock->ops->poll) ((void *) 0xdeadbeef,
+ user->sock,
+ (void *) 0xdeadbead);
+ }
+ while ((avail & want) == 0);
+ }
+
+ if (avail & POLLERR)
+ ret = EIO;
+ else
+ /* We got something. */
+ *select_type = avail;
+
+ pthread_mutex_unlock (&global_lock);
+
+ return ret;
+}
+
+error_t
+S_io_select (struct sock_user *user,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int *select_type)
+{
+ return io_select_common (user, reply, reply_type, NULL, select_type);
+}
+
+error_t
+S_io_select_timeout (struct sock_user *user,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ struct timespec ts,
+ int *select_type)
+{
+ return io_select_common (user, reply, reply_type, &ts, select_type);
+}
+
+error_t
+S_io_stat (struct sock_user *user,
+ struct stat *st)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ bzero (st, sizeof (struct stat));
+
+ st->st_fstype = FSTYPE_SOCKET;
+ st->st_fsid = getpid ();
+ st->st_ino = user->sock->st_ino;
+
+ st->st_mode = S_IFSOCK | ACCESSPERMS;
+ st->st_blksize = 512; /* ???? */
+
+ return 0;
+}
+
+error_t
+S_io_reauthenticate (struct sock_user *user,
+ mach_port_t rend)
+{
+ struct sock_user *newuser;
+ uid_t gubuf[20], ggbuf[20], aubuf[20], agbuf[20];
+ uid_t *gen_uids, *gen_gids, *aux_uids, *aux_gids;
+ size_t genuidlen, gengidlen, auxuidlen, auxgidlen;
+ error_t err;
+ size_t i, j;
+ auth_t auth;
+ mach_port_t newright;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ genuidlen = gengidlen = auxuidlen = auxgidlen = 20;
+ gen_uids = gubuf;
+ gen_gids = ggbuf;
+ aux_uids = aubuf;
+ aux_gids = agbuf;
+
+ pthread_mutex_lock (&global_lock);
+ newuser = make_sock_user (user->sock, 0, 1, 0);
+
+ auth = getauth ();
+ newright = ports_get_send_right (newuser);
+ assert (newright != MACH_PORT_NULL);
+ do
+ err = auth_server_authenticate (auth,
+ rend,
+ MACH_MSG_TYPE_COPY_SEND,
+ newright,
+ MACH_MSG_TYPE_COPY_SEND,
+ &gen_uids, &genuidlen,
+ &aux_uids, &auxuidlen,
+ &gen_gids, &gengidlen,
+ &aux_gids, &auxgidlen);
+ while (err == EINTR);
+ mach_port_deallocate (mach_task_self (), rend);
+ mach_port_deallocate (mach_task_self (), newright);
+ mach_port_deallocate (mach_task_self (), auth);
+
+ if (err)
+ newuser->isroot = 0;
+ else
+ /* Check permission as fshelp_isowner would do. */
+ for (i = 0; i < genuidlen; i++)
+ {
+ if (gen_uids[i] == 0 || gen_uids[i] == pfinet_owner)
+ newuser->isroot = 1;
+ if (gen_uids[i] == pfinet_group)
+ for (j = 0; j < gengidlen; j++)
+ if (gen_gids[j] == pfinet_group)
+ newuser->isroot = 1;
+ }
+
+ mach_port_move_member (mach_task_self (), newuser->pi.port_right,
+ pfinet_bucket->portset);
+
+ pthread_mutex_unlock (&global_lock);
+
+ ports_port_deref (newuser);
+
+ if (gubuf != gen_uids)
+ munmap (gen_uids, genuidlen * sizeof (uid_t));
+ if (ggbuf != gen_gids)
+ munmap (gen_gids, gengidlen * sizeof (uid_t));
+ if (aubuf != aux_uids)
+ munmap (aux_uids, auxuidlen * sizeof (uid_t));
+ if (agbuf != aux_gids)
+ munmap (aux_gids, auxgidlen * sizeof (uid_t));
+
+ return 0;
+}
+
+error_t
+S_io_restrict_auth (struct sock_user *user,
+ mach_port_t *newobject,
+ mach_msg_type_name_t *newobject_type,
+ uid_t *uids, size_t uidslen,
+ uid_t *gids, size_t gidslen)
+{
+ struct sock_user *newuser;
+ int i, j;
+ int isroot;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ isroot = 0;
+ if (user->isroot)
+ /* Check permission as fshelp_isowner would do. */
+ for (i = 0; i < uidslen; i++)
+ {
+ if (uids[i] == 0 || uids[i] == pfinet_owner)
+ isroot = 1;
+ if (uids[i] == pfinet_group)
+ for (j = 0; j < gidslen; j++)
+ if (gids[j] == pfinet_group)
+ isroot = 1;
+ }
+
+ newuser = make_sock_user (user->sock, isroot, 0, 0);
+ *newobject = ports_get_right (newuser);
+ *newobject_type = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newuser);
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+S_io_duplicate (struct sock_user *user,
+ mach_port_t *newobject,
+ mach_msg_type_name_t *newobject_type)
+{
+ struct sock_user *newuser;
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ newuser = make_sock_user (user->sock, user->isroot, 0, 0);
+ *newobject = ports_get_right (newuser);
+ *newobject_type = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newuser);
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+S_io_identity (struct sock_user *user,
+ mach_port_t *id,
+ mach_msg_type_name_t *idtype,
+ mach_port_t *fsys,
+ mach_msg_type_name_t *fsystype,
+ ino_t *fileno)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ if (user->sock->identity == MACH_PORT_NULL)
+ {
+ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &user->sock->identity);
+ if (err)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return err;
+ }
+ }
+
+ *id = user->sock->identity;
+ *idtype = MACH_MSG_TYPE_MAKE_SEND;
+ *fsys = fsys_identity;
+ *fsystype = MACH_MSG_TYPE_MAKE_SEND;
+ *fileno = user->sock->st_ino;
+
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+S_io_revoke (struct sock_user *user)
+{
+ /* XXX maybe we should try */
+ return EOPNOTSUPP;
+}
+
+
+
+error_t
+S_io_async (struct sock_user *user,
+ mach_port_t notify,
+ mach_port_t *id,
+ mach_msg_type_name_t *idtype)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_mod_owner (struct sock_user *user,
+ pid_t owner)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_get_owner (struct sock_user *user,
+ pid_t *owner)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_get_icky_async_id (struct sock_user *user,
+ mach_port_t *id,
+ mach_msg_type_name_t *idtype)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_server_version (struct sock_user *user,
+ char *name,
+ int *major,
+ int *minor,
+ int *edit)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_pathconf (struct sock_user *user,
+ int name,
+ int *value)
+{
+ return EOPNOTSUPP;
+}
+
+
+
+error_t
+S_io_map (struct sock_user *user,
+ mach_port_t *rdobj,
+ mach_msg_type_name_t *rdobj_type,
+ mach_port_t *wrobj,
+ mach_msg_type_name_t *wrobj_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_map_cntl (struct sock_user *user,
+ mach_port_t *obj,
+ mach_msg_type_name_t *obj_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_get_conch (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_release_conch (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_eofnotify (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_prenotify (struct sock_user *user,
+ vm_offset_t start,
+ vm_offset_t end)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_postnotify (struct sock_user *user,
+ vm_offset_t start,
+ vm_offset_t end)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_readnotify (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_readsleep (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_sigio (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
diff --git a/pfinet/kmem_cache.c b/pfinet/kmem_cache.c
new file mode 100644
index 00000000..aab192e1
--- /dev/null
+++ b/pfinet/kmem_cache.c
@@ -0,0 +1,88 @@
+/* Hack replacement for Linux's kmem_cache_t allocator
+ Copyright (C) 2000 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* Hack replacement for Linux's kmem_cache_t allocator, using plain malloc
+ and cthreads locking. The locking here is probably unnecessary. */
+
+#include <pthread.h>
+#include <linux/malloc.h>
+
+struct kmem_cache_s
+{
+ pthread_mutex_t lock;
+
+ void *freelist;
+ size_t item_size;
+
+ void (*ctor) (void *, kmem_cache_t *, unsigned long);
+ void (*dtor) (void *, kmem_cache_t *, unsigned long);
+};
+
+kmem_cache_t *
+kmem_cache_create (const char *name, size_t item_size,
+ size_t something, unsigned long flags,
+ void (*ctor) (void *, kmem_cache_t *, unsigned long),
+ void (*dtor) (void *, kmem_cache_t *, unsigned long))
+{
+ kmem_cache_t *new = malloc (sizeof *new);
+ if (!new)
+ return 0;
+ pthread_mutex_init (&new->lock, NULL);
+ new->freelist = 0;
+ new->item_size = item_size;
+ new->ctor = ctor;
+ new->dtor = dtor;
+
+ return new;
+}
+
+
+void *
+kmem_cache_alloc (kmem_cache_t *cache, int flags)
+{
+ void *p;
+
+ pthread_mutex_lock (&cache->lock);
+ p = cache->freelist;
+ if (p != 0) {
+ cache->freelist = *(void **)(p + cache->item_size);
+ pthread_mutex_unlock (&cache->lock);
+ return p;
+ }
+ pthread_mutex_unlock (&cache->lock);
+
+ p = malloc (cache->item_size + sizeof (void *));
+ if (p && cache->ctor)
+ (*cache->ctor) (p, cache, flags);
+ return p;
+}
+
+
+void
+kmem_cache_free (kmem_cache_t *cache, void *p)
+{
+ void **const nextp = (void **) (p + cache->item_size);
+
+ pthread_mutex_lock (&cache->lock);
+ *nextp = cache->freelist;
+ cache->freelist = p;
+ pthread_mutex_unlock (&cache->lock);
+
+ /* XXX eventually destroy some... */
+}
diff --git a/pfinet/linux-src/arch/alpha/lib/checksum.c b/pfinet/linux-src/arch/alpha/lib/checksum.c
new file mode 100644
index 00000000..5165279f
--- /dev/null
+++ b/pfinet/linux-src/arch/alpha/lib/checksum.c
@@ -0,0 +1,169 @@
+/*
+ * arch/alpha/lib/checksum.c
+ *
+ * This file contains network checksum routines that are better done
+ * in an architecture-specific manner due to speed..
+ */
+
+#include <linux/string.h>
+
+#include <asm/byteorder.h>
+
+static inline unsigned short from64to16(unsigned long x)
+{
+ /* add up 32-bit words for 33 bits */
+ x = (x & 0xffffffff) + (x >> 32);
+ /* add up 16-bit and 17-bit words for 17+c bits */
+ x = (x & 0xffff) + (x >> 16);
+ /* add up 16-bit and 2-bit for 16+c bit */
+ x = (x & 0xffff) + (x >> 16);
+ /* add up carry.. */
+ x = (x & 0xffff) + (x >> 16);
+ return x;
+}
+
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented.
+ */
+unsigned short int csum_tcpudp_magic(unsigned long saddr,
+ unsigned long daddr,
+ unsigned short len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ return ~from64to16(saddr + daddr + sum +
+ ((unsigned long) ntohs(len) << 16) +
+ ((unsigned long) proto << 8));
+}
+
+unsigned int csum_tcpudp_nofold(unsigned long saddr,
+ unsigned long daddr,
+ unsigned short len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ unsigned long result;
+
+ result = (saddr + daddr + sum +
+ ((unsigned long) ntohs(len) << 16) +
+ ((unsigned long) proto << 8));
+
+ /* Fold down to 32-bits so we don't loose in the typedef-less
+ network stack. */
+ /* 64 to 33 */
+ result = (result & 0xffffffff) + (result >> 32);
+ /* 33 to 32 */
+ result = (result & 0xffffffff) + (result >> 32);
+ return result;
+}
+
+/*
+ * Do a 64-bit checksum on an arbitrary memory area..
+ *
+ * This isn't a great routine, but it's not _horrible_ either. The
+ * inner loop could be unrolled a bit further, and there are better
+ * ways to do the carry, but this is reasonable.
+ */
+static inline unsigned long do_csum(const unsigned char * buff, int len)
+{
+ int odd, count;
+ unsigned long result = 0;
+
+ if (len <= 0)
+ goto out;
+ odd = 1 & (unsigned long) buff;
+ if (odd) {
+ result = *buff << 8;
+ len--;
+ buff++;
+ }
+ count = len >> 1; /* nr of 16-bit words.. */
+ if (count) {
+ if (2 & (unsigned long) buff) {
+ result += *(unsigned short *) buff;
+ count--;
+ len -= 2;
+ buff += 2;
+ }
+ count >>= 1; /* nr of 32-bit words.. */
+ if (count) {
+ if (4 & (unsigned long) buff) {
+ result += *(unsigned int *) buff;
+ count--;
+ len -= 4;
+ buff += 4;
+ }
+ count >>= 1; /* nr of 64-bit words.. */
+ if (count) {
+ unsigned long carry = 0;
+ do {
+ unsigned long w = *(unsigned long *) buff;
+ count--;
+ buff += 8;
+ result += carry;
+ result += w;
+ carry = (w > result);
+ } while (count);
+ result += carry;
+ result = (result & 0xffffffff) + (result >> 32);
+ }
+ if (len & 4) {
+ result += *(unsigned int *) buff;
+ buff += 4;
+ }
+ }
+ if (len & 2) {
+ result += *(unsigned short *) buff;
+ buff += 2;
+ }
+ }
+ if (len & 1)
+ result += *buff;
+ result = from64to16(result);
+ if (odd)
+ result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
+out:
+ return result;
+}
+
+/*
+ * This is a version of ip_compute_csum() optimized for IP headers,
+ * which always checksum on 4 octet boundaries.
+ */
+unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl)
+{
+ return ~do_csum(iph,ihl*4);
+}
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum)
+{
+ unsigned long result = do_csum(buff, len);
+
+ /* add in old sum, and carry.. */
+ result += sum;
+ /* 32+c bits -> 32 bits */
+ result = (result & 0xffffffff) + (result >> 32);
+ return result;
+}
+
+/*
+ * this routine is used for miscellaneous IP-like checksums, mainly
+ * in icmp.c
+ */
+unsigned short ip_compute_csum(unsigned char * buff, int len)
+{
+ return ~from64to16(do_csum(buff,len));
+}
diff --git a/pfinet/linux-src/arch/alpha/lib/csum_partial_copy.c b/pfinet/linux-src/arch/alpha/lib/csum_partial_copy.c
new file mode 100644
index 00000000..71308133
--- /dev/null
+++ b/pfinet/linux-src/arch/alpha/lib/csum_partial_copy.c
@@ -0,0 +1,384 @@
+/*
+ * csum_partial_copy - do IP checksumming and copy
+ *
+ * (C) Copyright 1996 Linus Torvalds
+ *
+ * Don't look at this too closely - you'll go mad. The things
+ * we do for performance..
+ */
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+
+
+#define ldq_u(x,y) \
+__asm__ __volatile__("ldq_u %0,%1":"=r" (x):"m" (*(const unsigned long *)(y)))
+
+#define stq_u(x,y) \
+__asm__ __volatile__("stq_u %1,%0":"=m" (*(unsigned long *)(y)):"r" (x))
+
+#define extql(x,y,z) \
+__asm__ __volatile__("extql %1,%2,%0":"=r" (z):"r" (x),"r" (y))
+
+#define extqh(x,y,z) \
+__asm__ __volatile__("extqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))
+
+#define mskql(x,y,z) \
+__asm__ __volatile__("mskql %1,%2,%0":"=r" (z):"r" (x),"r" (y))
+
+#define mskqh(x,y,z) \
+__asm__ __volatile__("mskqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))
+
+#define insql(x,y,z) \
+__asm__ __volatile__("insql %1,%2,%0":"=r" (z):"r" (x),"r" (y))
+
+#define insqh(x,y,z) \
+__asm__ __volatile__("insqh %1,%2,%0":"=r" (z):"r" (x),"r" (y))
+
+
+#define __get_user_u(x,ptr) \
+({ \
+ long __guu_err; \
+ __asm__ __volatile__( \
+ "1: ldq_u %0,%2\n" \
+ "2:\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .gprel32 1b\n" \
+ " lda %0,2b-1b(%1)\n" \
+ ".previous" \
+ : "=r"(x), "=r"(__guu_err) \
+ : "m"(__m(ptr)), "1"(0)); \
+ __guu_err; \
+})
+
+#define __put_user_u(x,ptr) \
+({ \
+ long __puu_err; \
+ __asm__ __volatile__( \
+ "1: stq_u %2,%1\n" \
+ "2:\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .gprel32 1b" \
+ " lda $31,2b-1b(%0)\n" \
+ ".previous" \
+ : "=r"(__puu_err) \
+ : "m"(__m(addr)), "rJ"(x), "0"(0)); \
+ __puu_err; \
+})
+
+
+/*
+ * Ok. This isn't fun, but this is the EASY case.
+ */
+static inline unsigned long
+csum_partial_cfu_aligned(const unsigned long *src, unsigned long *dst,
+ long len, unsigned long checksum,
+ int *errp)
+{
+ unsigned long carry = 0;
+ int err = 0;
+
+ while (len >= 0) {
+ unsigned long word;
+ err |= __get_user(word, src);
+ checksum += carry;
+ src++;
+ checksum += word;
+ len -= 8;
+ carry = checksum < word;
+ *dst = word;
+ dst++;
+ }
+ len += 8;
+ checksum += carry;
+ if (len) {
+ unsigned long word, tmp;
+ err |= __get_user(word, src);
+ tmp = *dst;
+ mskql(word, len, word);
+ checksum += word;
+ mskqh(tmp, len, tmp);
+ carry = checksum < word;
+ *dst = word | tmp;
+ checksum += carry;
+ }
+ if (err) *errp = err;
+ return checksum;
+}
+
+/*
+ * This is even less fun, but this is still reasonably
+ * easy.
+ */
+static inline unsigned long
+csum_partial_cfu_dest_aligned(const unsigned long *src, unsigned long *dst,
+ unsigned long soff,
+ long len, unsigned long checksum,
+ int *errp)
+{
+ unsigned long first;
+ unsigned long word, carry;
+ unsigned long lastsrc = 7+len+(unsigned long)src;
+ int err = 0;
+
+ err |= __get_user_u(first,src);
+ carry = 0;
+ while (len >= 0) {
+ unsigned long second;
+
+ err |= __get_user_u(second, src+1);
+ extql(first, soff, word);
+ len -= 8;
+ src++;
+ extqh(second, soff, first);
+ checksum += carry;
+ word |= first;
+ first = second;
+ checksum += word;
+ *dst = word;
+ dst++;
+ carry = checksum < word;
+ }
+ len += 8;
+ checksum += carry;
+ if (len) {
+ unsigned long tmp;
+ unsigned long second;
+ err |= __get_user_u(second, lastsrc);
+ tmp = *dst;
+ extql(first, soff, word);
+ extqh(second, soff, first);
+ word |= first;
+ mskql(word, len, word);
+ checksum += word;
+ mskqh(tmp, len, tmp);
+ carry = checksum < word;
+ *dst = word | tmp;
+ checksum += carry;
+ }
+ if (err) *errp = err;
+ return checksum;
+}
+
+/*
+ * This is slightly less fun than the above..
+ */
+static inline unsigned long
+csum_partial_cfu_src_aligned(const unsigned long *src, unsigned long *dst,
+ unsigned long doff,
+ long len, unsigned long checksum,
+ unsigned long partial_dest,
+ int *errp)
+{
+ unsigned long carry = 0;
+ unsigned long word;
+ int err = 0;
+
+ mskql(partial_dest, doff, partial_dest);
+ while (len >= 0) {
+ unsigned long second_dest;
+ err |= __get_user(word, src);
+ len -= 8;
+ insql(word, doff, second_dest);
+ checksum += carry;
+ stq_u(partial_dest | second_dest, dst);
+ src++;
+ checksum += word;
+ insqh(word, doff, partial_dest);
+ carry = checksum < word;
+ dst++;
+ }
+ len += doff;
+ checksum += carry;
+ if (len >= 0) {
+ unsigned long second_dest;
+ err |= __get_user(word, src);
+ mskql(word, len-doff, word);
+ checksum += word;
+ insql(word, doff, second_dest);
+ stq_u(partial_dest | second_dest, dst);
+ carry = checksum < word;
+ if (len) {
+ ldq_u(second_dest, dst+1);
+ insqh(word, doff, partial_dest);
+ mskqh(second_dest, len, second_dest);
+ stq_u(partial_dest | second_dest, dst+1);
+ }
+ checksum += carry;
+ } else if (len & 7) {
+ unsigned long second_dest;
+ err |= __get_user(word, src);
+ ldq_u(second_dest, dst);
+ mskql(word, len-doff, word);
+ checksum += word;
+ mskqh(second_dest, len, second_dest);
+ carry = checksum < word;
+ insql(word, doff, word);
+ stq_u(partial_dest | word | second_dest, dst);
+ checksum += carry;
+ }
+ if (err) *errp = err;
+ return checksum;
+}
+
+/*
+ * This is so totally un-fun that it's frightening. Don't
+ * look at this too closely, you'll go blind.
+ */
+static inline unsigned long
+csum_partial_cfu_unaligned(const unsigned long * src, unsigned long * dst,
+ unsigned long soff, unsigned long doff,
+ long len, unsigned long checksum,
+ unsigned long partial_dest,
+ int *errp)
+{
+ unsigned long carry = 0;
+ unsigned long first;
+ unsigned long lastsrc;
+ int err = 0;
+
+ err |= __get_user_u(first, src);
+ lastsrc = 7+len+(unsigned long)src;
+ mskql(partial_dest, doff, partial_dest);
+ while (len >= 0) {
+ unsigned long second, word;
+ unsigned long second_dest;
+
+ err |= __get_user_u(second, src+1);
+ extql(first, soff, word);
+ checksum += carry;
+ len -= 8;
+ extqh(second, soff, first);
+ src++;
+ word |= first;
+ first = second;
+ insql(word, doff, second_dest);
+ checksum += word;
+ stq_u(partial_dest | second_dest, dst);
+ carry = checksum < word;
+ insqh(word, doff, partial_dest);
+ dst++;
+ }
+ len += doff;
+ checksum += carry;
+ if (len >= 0) {
+ unsigned long second, word;
+ unsigned long second_dest;
+
+ err |= __get_user_u(second, lastsrc);
+ extql(first, soff, word);
+ extqh(second, soff, first);
+ word |= first;
+ first = second;
+ mskql(word, len-doff, word);
+ checksum += word;
+ insql(word, doff, second_dest);
+ carry = checksum < word;
+ stq_u(partial_dest | second_dest, dst);
+ if (len) {
+ ldq_u(second_dest, dst+1);
+ insqh(word, doff, partial_dest);
+ mskqh(second_dest, len, second_dest);
+ stq_u(partial_dest | second_dest, dst+1);
+ }
+ checksum += carry;
+ } else if (len & 7) {
+ unsigned long second, word;
+ unsigned long second_dest;
+
+ err |= __get_user_u(second, lastsrc);
+ extql(first, soff, word);
+ extqh(second, soff, first);
+ word |= first;
+ ldq_u(second_dest, dst);
+ mskql(word, len-doff, word);
+ checksum += word;
+ mskqh(second_dest, len, second_dest);
+ carry = checksum < word;
+ insql(word, doff, word);
+ stq_u(partial_dest | word | second_dest, dst);
+ checksum += carry;
+ }
+ if (err) *errp = err;
+ return checksum;
+}
+
+static unsigned int
+do_csum_partial_copy_from_user(const char *src, char *dst, int len,
+ unsigned int sum, int *errp)
+{
+ unsigned long checksum = (unsigned) sum;
+ unsigned long soff = 7 & (unsigned long) src;
+ unsigned long doff = 7 & (unsigned long) dst;
+
+ if (len) {
+ if (!doff) {
+ if (!soff)
+ checksum = csum_partial_cfu_aligned(
+ (const unsigned long *) src,
+ (unsigned long *) dst,
+ len-8, checksum, errp);
+ else
+ checksum = csum_partial_cfu_dest_aligned(
+ (const unsigned long *) src,
+ (unsigned long *) dst,
+ soff, len-8, checksum, errp);
+ } else {
+ unsigned long partial_dest;
+ ldq_u(partial_dest, dst);
+ if (!soff)
+ checksum = csum_partial_cfu_src_aligned(
+ (const unsigned long *) src,
+ (unsigned long *) dst,
+ doff, len-8, checksum,
+ partial_dest, errp);
+ else
+ checksum = csum_partial_cfu_unaligned(
+ (const unsigned long *) src,
+ (unsigned long *) dst,
+ soff, doff, len-8, checksum,
+ partial_dest, errp);
+ }
+ /* 64 -> 33 bits */
+ checksum = (checksum & 0xffffffff) + (checksum >> 32);
+ /* 33 -> < 32 bits */
+ checksum = (checksum & 0xffff) + (checksum >> 16);
+ /* 32 -> 16 bits */
+ checksum = (checksum & 0xffff) + (checksum >> 16);
+ checksum = (checksum & 0xffff) + (checksum >> 16);
+ }
+ return checksum;
+}
+
+unsigned int
+csum_partial_copy_from_user(const char *src, char *dst, int len,
+ unsigned int sum, int *errp)
+{
+ if (!access_ok(src, len, VERIFY_READ)) {
+ *errp = -EFAULT;
+ memset(dst, 0, len);
+ return sum;
+ }
+
+ return do_csum_partial_copy_from_user(src, dst, len, sum, errp);
+}
+
+unsigned int
+csum_partial_copy_nocheck(const char *src, char *dst, int len, unsigned int sum)
+{
+ return do_csum_partial_copy_from_user(src, dst, len, sum, NULL);
+}
+
+unsigned int
+csum_partial_copy (const char *src, char *dst, int len, unsigned int sum)
+{
+ unsigned int ret;
+ int error = 0;
+
+ ret = do_csum_partial_copy_from_user(src, dst, len, sum, &error);
+ if (error)
+ printk("csum_partial_copy_old(): tell mingo to convert me!\n");
+
+ return ret;
+}
diff --git a/pfinet/linux-src/arch/arm/lib/checksum.S b/pfinet/linux-src/arch/arm/lib/checksum.S
new file mode 100644
index 00000000..bd5c78d3
--- /dev/null
+++ b/pfinet/linux-src/arch/arm/lib/checksum.S
@@ -0,0 +1,730 @@
+/*
+ * linux/arch/arm/lib/checksum.S
+ *
+ * Copyright (C) 1995, 1996, 1997, 1998 Russell King
+ */
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/errno.h>
+#include "constants.h"
+
+ .text
+
+/* Function: __u32 csum_partial(const char *src, int len, __u32)
+ * Params : r0 = buffer, r1 = len, r2 = checksum
+ * Returns : r0 = new checksum
+ */
+
+ENTRY(csum_partial)
+ tst r0, #2
+ beq 1f
+ subs r1, r1, #2
+ addmi r1, r1, #2
+ bmi 3f
+ bic r0, r0, #3
+ ldr r3, [r0], #4
+ adds r2, r2, r3, lsr #16
+ adcs r2, r2, #0
+1: adds r2, r2, #0
+ bics ip, r1, #31
+ beq 3f
+ stmfd sp!, {r4 - r6}
+2: ldmia r0!, {r3 - r6}
+ adcs r2, r2, r3
+ adcs r2, r2, r4
+ adcs r2, r2, r5
+ adcs r2, r2, r6
+ ldmia r0!, {r3 - r6}
+ adcs r2, r2, r3
+ adcs r2, r2, r4
+ adcs r2, r2, r5
+ adcs r2, r2, r6
+ sub ip, ip, #32
+ teq ip, #0
+ bne 2b
+ adcs r2, r2, #0
+ ldmfd sp!, {r4 - r6}
+3: ands ip, r1, #0x1c
+ beq 5f
+4: ldr r3, [r0], #4
+ adcs r2, r2, r3
+ sub ip, ip, #4
+ teq ip, #0
+ bne 4b
+ adcs r2, r2, #0
+5: ands ip, r1, #3
+ moveq r0, r2
+ RETINSTR(moveq,pc,lr)
+ mov ip, ip, lsl #3
+ rsb ip, ip, #32
+ ldr r3, [r0]
+ mov r3, r3, lsl ip
+ adds r2, r2, r3, lsr ip
+ adc r0, r2, #0
+ RETINSTR(mov,pc,lr)
+
+/* Function: __u32 csum_partial_copy_from_user (const char *src, char *dst, int len, __u32 sum, int *err_ptr)
+ * Params : r0 = src, r1 = dst, r2 = len, r3 = sum, [sp, #0] = &err
+ * Returns : r0 = checksum, [[sp, #0], #0] = 0 or -EFAULT
+ */
+#if defined(CONFIG_CPU_32)
+
+ .macro save_regs
+ stmfd sp!, {r1 - r2, r4 - r8, fp, ip, lr, pc}
+ .endm
+
+#define LOAD_REGS(cond) \
+ LOADREGS(##cond##ea,fp,{r1 - r2, r4 - r8, fp, sp, pc})
+
+ .macro load1b, reg1
+9999: ldrbt \reg1, [r0], $1
+ .section __ex_table, "a"
+ .align 3
+ .long 9999b, 6001f
+ .previous
+ .endm
+
+ .macro load2b, reg1, reg2
+9999: ldrbt \reg1, [r0], $1
+9998: ldrbt \reg2, [r0], $1
+ .section __ex_table, "a"
+ .long 9999b, 6001f
+ .long 9998b, 6001f
+ .previous
+ .endm
+
+ .macro load1l, reg1
+9999: ldrt \reg1, [r0], $4
+ .section __ex_table, "a"
+ .align 3
+ .long 9999b, 6001f
+ .previous
+ .endm
+
+ .macro load2l, reg1, reg2
+9999: ldrt \reg1, [r0], $4
+9998: ldrt \reg2, [r0], $4
+ .section __ex_table, "a"
+ .long 9999b, 6001f
+ .long 9998b, 6001f
+ .previous
+ .endm
+
+ .macro load4l, reg1, reg2, reg3, reg4
+9999: ldrt \reg1, [r0], $4
+9998: ldrt \reg2, [r0], $4
+9997: ldrt \reg3, [r0], $4
+9996: ldrt \reg4, [r0], $4
+ .section __ex_table, "a"
+ .long 9999b, 6001f
+ .long 9998b, 6001f
+ .long 9997b, 6001f
+ .long 9996b, 6001f
+ .previous
+ .endm
+
+#elif defined(CONFIG_CPU_26)
+
+ .macro save_regs
+ stmfd sp!, {r1 - r2, r4 - r9, fp, ip, lr, pc}
+ mov r9, sp, lsr #13
+ mov r9, r9, lsl #13
+ ldr r9, [r9, #TSK_ADDR_LIMIT]
+ mov r9, r9, lsr #24
+ .endm
+
+#define LOAD_REGS(cond) \
+ LOADREGS(##cond##ea,fp,{r1 - r2, r4 - r9, fp, sp, pc})
+
+ .macro load1b, reg1
+ tst r9, #0x01
+9999: ldreqbt \reg1, [r0], #1
+ ldrneb \reg1, [r0], #1
+ .section __ex_table, "a"
+ .align 3
+ .long 9999b, 6001f
+ .previous
+ .endm
+
+ .macro load2b, reg1, reg2
+ tst r9, #0x01
+9999: ldreqbt \reg1, [r0], #1
+ ldrneb \reg1, [r0], #1
+9998: ldreqbt \reg2, [r0], #1
+ ldrneb \reg2, [r0], #1
+ .section __ex_table, "a"
+ .long 9999b, 6001f
+ .long 9998b, 6001f
+ .previous
+ .endm
+
+ .macro load1l, reg1
+ tst r9, #0x01
+9999: ldreqt \reg1, [r0], #4
+ ldrne \reg1, [r0], #4
+ .section __ex_table, "a"
+ .align 3
+ .long 9999b, 6001f
+ .previous
+ .endm
+
+ .macro load2l, reg1, reg2
+ tst r9, #0x01
+ ldmneia r0!, {\reg1, \reg2}
+9999: ldreqt \reg1, [r0], #4
+9998: ldreqt \reg2, [r0], #4
+ .section __ex_table, "a"
+ .long 9999b, 6001f
+ .long 9998b, 6001f
+ .previous
+ .endm
+
+ .macro load4l, reg1, reg2, reg3, reg4
+ tst r9, #0x01
+ ldmneia r0!, {\reg1, \reg2, \reg3, \reg4}
+9999: ldreqt \reg1, [r0], #4
+9998: ldreqt \reg2, [r0], #4
+9997: ldreqt \reg3, [r0], #4
+9996: ldreqt \reg4, [r0], #4
+ .section __ex_table, "a"
+ .long 9999b, 6001f
+ .long 9998b, 6001f
+ .long 9997b, 6001f
+ .long 9996b, 6001f
+ .previous
+ .endm
+
+#else
+#error Unknown CPU architecture
+#endif
+
+ENTRY(csum_partial_copy_from_user)
+ mov ip, sp
+ save_regs
+ sub fp, ip, #4
+ cmp r2, #4
+ blt .too_small_user
+ tst r1, #2 @ Test destination alignment
+ beq .dst_aligned_user
+ subs r2, r2, #2 @ We do not know if SRC is aligned...
+ load2b ip, r8
+ orr ip, ip, r8, lsl #8
+ adds r3, r3, ip
+ adcs r3, r3, #0
+ strb ip, [r1], #1
+ mov ip, ip, lsr #8
+ strb ip, [r1], #1 @ Destination now aligned
+.dst_aligned_user:
+ tst r0, #3
+ bne .src_not_aligned_user
+ adds r3, r3, #0
+ bics ip, r2, #15 @ Routine for src & dst aligned
+ beq 2f
+1: load4l r4, r5, r6, r7
+ stmia r1!, {r4, r5, r6, r7}
+ adcs r3, r3, r4
+ adcs r3, r3, r5
+ adcs r3, r3, r6
+ adcs r3, r3, r7
+ sub ip, ip, #16
+ teq ip, #0
+ bne 1b
+2: ands ip, r2, #12
+ beq 4f
+ tst ip, #8
+ beq 3f
+ load2l r4, r5
+ stmia r1!, {r4, r5}
+ adcs r3, r3, r4
+ adcs r3, r3, r5
+ tst ip, #4
+ beq 4f
+3: load1l r4
+ str r4, [r1], #4
+ adcs r3, r3, r4
+4: ands r2, r2, #3
+ adceq r0, r3, #0
+ LOAD_REGS(eq)
+ load1l r4
+ tst r2, #2
+ beq .exit
+ adcs r3, r3, r4, lsl #16
+ strb r4, [r1], #1
+ mov r4, r4, lsr #8
+ strb r4, [r1], #1
+ mov r4, r4, lsr #8
+.exit: tst r2, #1
+ strneb r4, [r1], #1
+ andne r4, r4, #255
+ adcnes r3, r3, r4
+ adcs r0, r3, #0
+ LOAD_REGS(al)
+
+.too_small_user:
+ teq r2, #0
+ LOAD_REGS(eq)
+ cmp r2, #2
+ blt .too_small_user1
+ load2b ip, r8
+ orr ip, ip, r8, lsl #8
+ adds r3, r3, ip
+ strb ip, [r1], #1
+ strb r8, [r1], #1
+ tst r2, #1
+.too_small_user1: @ C = 0
+ beq .csum_exit
+ load1b ip
+ strb ip, [r1], #1
+ adcs r3, r3, ip
+.csum_exit: adc r0, r3, #0
+ LOAD_REGS(al)
+
+.src_not_aligned_user:
+ cmp r2, #4
+ blt .too_small_user
+ and ip, r0, #3
+ bic r0, r0, #3
+ load1l r4
+ cmp ip, #2
+ beq .src2_aligned_user
+ bhi .src3_aligned_user
+ mov r4, r4, lsr #8
+ adds r3, r3, #0
+ bics ip, r2, #15
+ beq 2f
+1: load4l r5, r6, r7, r8
+ orr r4, r4, r5, lsl #24
+ mov r5, r5, lsr #8
+ orr r5, r5, r6, lsl #24
+ mov r6, r6, lsr #8
+ orr r6, r6, r7, lsl #24
+ mov r7, r7, lsr #8
+ orr r7, r7, r8, lsl #24
+ stmia r1!, {r4, r5, r6, r7}
+ adcs r3, r3, r4
+ adcs r3, r3, r5
+ adcs r3, r3, r6
+ adcs r3, r3, r7
+ mov r4, r8, lsr #8
+ sub ip, ip, #16
+ teq ip, #0
+ bne 1b
+2: ands ip, r2, #12
+ beq 4f
+ tst ip, #8
+ beq 3f
+ load2l r5, r6
+ orr r4, r4, r5, lsl #24
+ mov r5, r5, lsr #8
+ orr r5, r5, r6, lsl #24
+ stmia r1!, {r4, r5}
+ adcs r3, r3, r4
+ adcs r3, r3, r5
+ mov r4, r6, lsr #8
+ tst ip, #4
+ beq 4f
+3: load1l r5
+ orr r4, r4, r5, lsl #24
+ str r4, [r1], #4
+ adcs r3, r3, r4
+ mov r4, r5, lsr #8
+4: ands r2, r2, #3
+ adceq r0, r3, #0
+ LOAD_REGS(eq)
+ tst r2, #2
+ beq .exit
+ adcs r3, r3, r4, lsl #16
+ strb r4, [r1], #1
+ mov r4, r4, lsr #8
+ strb r4, [r1], #1
+ mov r4, r4, lsr #8
+ b .exit
+
+.src2_aligned_user:
+ mov r4, r4, lsr #16
+ adds r3, r3, #0
+ bics ip, r2, #15
+ beq 2f
+1: load4l r5, r6, r7, r8
+ orr r4, r4, r5, lsl #16
+ mov r5, r5, lsr #16
+ orr r5, r5, r6, lsl #16
+ mov r6, r6, lsr #16
+ orr r6, r6, r7, lsl #16
+ mov r7, r7, lsr #16
+ orr r7, r7, r8, lsl #16
+ stmia r1!, {r4, r5, r6, r7}
+ adcs r3, r3, r4
+ adcs r3, r3, r5
+ adcs r3, r3, r6
+ adcs r3, r3, r7
+ mov r4, r8, lsr #16
+ sub ip, ip, #16
+ teq ip, #0
+ bne 1b
+2: ands ip, r2, #12
+ beq 4f
+ tst ip, #8
+ beq 3f
+ load2l r5, r6
+ orr r4, r4, r5, lsl #16
+ mov r5, r5, lsr #16
+ orr r5, r5, r6, lsl #16
+ stmia r1!, {r4, r5}
+ adcs r3, r3, r4
+ adcs r3, r3, r5
+ mov r4, r6, lsr #16
+ tst ip, #4
+ beq 4f
+3: load1l r5
+ orr r4, r4, r5, lsl #16
+ str r4, [r1], #4
+ adcs r3, r3, r4
+ mov r4, r5, lsr #16
+4: ands r2, r2, #3
+ adceq r0, r3, #0
+ LOAD_REGS(eq)
+ tst r2, #2
+ beq .exit
+ adcs r3, r3, r4, lsl #16
+ strb r4, [r1], #1
+ mov r4, r4, lsr #8
+ strb r4, [r1], #1
+ load1b r4
+ b .exit
+
+.src3_aligned_user:
+ mov r4, r4, lsr #24
+ adds r3, r3, #0
+ bics ip, r2, #15
+ beq 2f
+1: load4l r5, r6, r7, r8
+ orr r4, r4, r5, lsl #8
+ mov r5, r5, lsr #24
+ orr r5, r5, r6, lsl #8
+ mov r6, r6, lsr #24
+ orr r6, r6, r7, lsl #8
+ mov r7, r7, lsr #24
+ orr r7, r7, r8, lsl #8
+ stmia r1!, {r4, r5, r6, r7}
+ adcs r3, r3, r4
+ adcs r3, r3, r5
+ adcs r3, r3, r6
+ adcs r3, r3, r7
+ mov r4, r8, lsr #24
+ sub ip, ip, #16
+ teq ip, #0
+ bne 1b
+2: ands ip, r2, #12
+ beq 4f
+ tst ip, #8
+ beq 3f
+ load2l r5, r6
+ orr r4, r4, r5, lsl #8
+ mov r5, r5, lsr #24
+ orr r5, r5, r6, lsl #8
+ stmia r1!, {r4, r5}
+ adcs r3, r3, r4
+ adcs r3, r3, r5
+ mov r4, r6, lsr #24
+ tst ip, #4
+ beq 4f
+3: load1l r5
+ orr r4, r4, r5, lsl #8
+ str r4, [r1], #4
+ adcs r3, r3, r4
+ mov r4, r5, lsr #24
+4: ands r2, r2, #3
+ adceq r0, r3, #0
+ LOAD_REGS(eq)
+ tst r2, #2
+ beq .exit
+ adcs r3, r3, r4, lsl #16
+ strb r4, [r1], #1
+ load1l r4
+ strb r4, [r1], #1
+ adcs r3, r3, r4, lsl #24
+ mov r4, r4, lsr #8
+ b .exit
+
+#if defined(CONFIG_CPU_32)
+ .section .fixup,"ax"
+#endif
+ .align 4
+6001: mov r4, #-EFAULT
+ ldr r5, [fp, #4]
+ str r4, [r5]
+ ldmia sp, {r1, r2} @ retrieve original arguments
+ add r2, r2, r1
+ mov r3, #0 @ zero the buffer
+6002: teq r2, r1
+ strneb r3, [r1], #1
+ bne 6002b
+ LOAD_REGS(al)
+#if defined(CONFIG_CPU_32)
+ .previous
+#endif
+
+/* Function: __u32 csum_partial_copy (const char *src, char *dst, int len, __u32 sum)
+ * Params : r0 = src, r1 = dst, r2 = len, r3 = checksum
+ * Returns : r0 = new checksum
+ */
+ENTRY(csum_partial_copy_nocheck)
+ENTRY(csum_partial_copy)
+ mov ip, sp
+ stmfd sp!, {r4 - r8, fp, ip, lr, pc}
+ sub fp, ip, #4
+ cmp r2, #4
+ blt Ltoo_small
+ tst r1, #2 @ Test destination alignment
+ beq Ldst_aligned
+ subs r2, r2, #2 @ We do not know if SRC is aligned...
+ ldrb ip, [r0], #1
+ ldrb r8, [r0], #1
+ orr ip, ip, r8, lsl #8
+ adds r3, r3, ip
+ adcs r3, r3, #0
+ strb ip, [r1], #1
+ mov ip, ip, lsr #8
+ strb ip, [r1], #1 @ Destination now aligned
+Ldst_aligned: tst r0, #3
+ bne Lsrc_not_aligned
+ adds r3, r3, #0
+ bics ip, r2, #15 @ Routine for src & dst aligned
+ beq 3f
+1: ldmia r0!, {r4, r5, r6, r7}
+ stmia r1!, {r4, r5, r6, r7}
+ adcs r3, r3, r4
+ adcs r3, r3, r5
+ adcs r3, r3, r6
+ adcs r3, r3, r7
+ sub ip, ip, #16
+ teq ip, #0
+ bne 1b
+3: ands ip, r2, #12
+ beq 5f
+ tst ip, #8
+ beq 4f
+ ldmia r0!, {r4, r5}
+ stmia r1!, {r4, r5}
+ adcs r3, r3, r4
+ adcs r3, r3, r5
+ tst ip, #4
+ beq 5f
+4: ldr r4, [r0], #4
+ str r4, [r1], #4
+ adcs r3, r3, r4
+5: ands r2, r2, #3
+ adceq r0, r3, #0
+ LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
+ ldr r4, [r0], #4
+ tst r2, #2
+ beq Lexit
+ adcs r3, r3, r4, lsl #16
+ strb r4, [r1], #1
+ mov r4, r4, lsr #8
+ strb r4, [r1], #1
+ mov r4, r4, lsr #8
+ b Lexit
+
+Ltoo_small: teq r2, #0
+ LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
+ cmp r2, #2
+ blt Ltoo_small1
+ ldrb ip, [r0], #1
+ ldrb r8, [r0], #1
+ orr ip, ip, r8, lsl #8
+ adds r3, r3, ip
+ strb ip, [r1], #1
+ strb r8, [r1], #1
+Lexit: tst r2, #1
+Ltoo_small1: ldrneb ip, [r0], #1
+ strneb ip, [r1], #1
+ adcnes r3, r3, ip
+ adcs r0, r3, #0
+ LOADREGS(ea,fp,{r4 - r8, fp, sp, pc})
+
+Lsrc_not_aligned:
+ cmp r2, #4
+ blt Ltoo_small
+ and ip, r0, #3
+ bic r0, r0, #3
+ ldr r4, [r0], #4
+ cmp ip, #2
+ beq Lsrc2_aligned
+ bhi Lsrc3_aligned
+ mov r4, r4, lsr #8
+ adds r3, r3, #0
+ bics ip, r2, #15
+ beq 2f
+1: ldmia r0!, {r5, r6, r7, r8}
+ orr r4, r4, r5, lsl #24
+ mov r5, r5, lsr #8
+ orr r5, r5, r6, lsl #24
+ mov r6, r6, lsr #8
+ orr r6, r6, r7, lsl #24
+ mov r7, r7, lsr #8
+ orr r7, r7, r8, lsl #24
+ stmia r1!, {r4, r5, r6, r7}
+ adcs r3, r3, r4
+ adcs r3, r3, r5
+ adcs r3, r3, r6
+ adcs r3, r3, r7
+ mov r4, r8, lsr #8
+ sub ip, ip, #16
+ teq ip, #0
+ bne 1b
+2: ands ip, r2, #12
+ beq 4f
+ tst ip, #8
+ beq 3f
+ ldmia r0!, {r5, r6}
+ orr r4, r4, r5, lsl #24
+ mov r5, r5, lsr #8
+ orr r5, r5, r6, lsl #24
+ stmia r1!, {r4, r5}
+ adcs r3, r3, r4
+ adcs r3, r3, r5
+ mov r4, r6, lsr #8
+ tst ip, #4
+ beq 4f
+3: ldr r5, [r0], #4
+ orr r4, r4, r5, lsl #24
+ str r4, [r1], #4
+ adcs r3, r3, r4
+ mov r4, r5, lsr #8
+4: ands r2, r2, #3
+ adceq r0, r3, #0
+ LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
+ tst r2, #2
+ beq Lexit
+ adcs r3, r3, r4, lsl #16
+ strb r4, [r1], #1
+ mov r4, r4, lsr #8
+ strb r4, [r1], #1
+ mov r4, r4, lsr #8
+ b Lexit
+
+Lsrc2_aligned: mov r4, r4, lsr #16
+ adds r3, r3, #0
+ bics ip, r2, #15
+ beq 2f
+1: ldmia r0!, {r5, r6, r7, r8}
+ orr r4, r4, r5, lsl #16
+ mov r5, r5, lsr #16
+ orr r5, r5, r6, lsl #16
+ mov r6, r6, lsr #16
+ orr r6, r6, r7, lsl #16
+ mov r7, r7, lsr #16
+ orr r7, r7, r8, lsl #16
+ stmia r1!, {r4, r5, r6, r7}
+ adcs r3, r3, r4
+ adcs r3, r3, r5
+ adcs r3, r3, r6
+ adcs r3, r3, r7
+ mov r4, r8, lsr #16
+ sub ip, ip, #16
+ teq ip, #0
+ bne 1b
+2: ands ip, r2, #12
+ beq 4f
+ tst ip, #8
+ beq 3f
+ ldmia r0!, {r5, r6}
+ orr r4, r4, r5, lsl #16
+ mov r5, r5, lsr #16
+ orr r5, r5, r6, lsl #16
+ stmia r1!, {r4, r5}
+ adcs r3, r3, r4
+ adcs r3, r3, r5
+ mov r4, r6, lsr #16
+ tst ip, #4
+ beq 4f
+3: ldr r5, [r0], #4
+ orr r4, r4, r5, lsl #16
+ str r4, [r1], #4
+ adcs r3, r3, r4
+ mov r4, r5, lsr #16
+4: ands r2, r2, #3
+ adceq r0, r3, #0
+ LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
+ tst r2, #2
+ beq Lexit
+ adcs r3, r3, r4, lsl #16
+ strb r4, [r1], #1
+ mov r4, r4, lsr #8
+ strb r4, [r1], #1
+ ldrb r4, [r0], #1
+ b Lexit
+
+Lsrc3_aligned: mov r4, r4, lsr #24
+ adds r3, r3, #0
+ bics ip, r2, #15
+ beq 2f
+1: ldmia r0!, {r5, r6, r7, r8}
+ orr r4, r4, r5, lsl #8
+ mov r5, r5, lsr #24
+ orr r5, r5, r6, lsl #8
+ mov r6, r6, lsr #24
+ orr r6, r6, r7, lsl #8
+ mov r7, r7, lsr #24
+ orr r7, r7, r8, lsl #8
+ stmia r1!, {r4, r5, r6, r7}
+ adcs r3, r3, r4
+ adcs r3, r3, r5
+ adcs r3, r3, r6
+ adcs r3, r3, r7
+ mov r4, r8, lsr #24
+ sub ip, ip, #16
+ teq ip, #0
+ bne 1b
+2: ands ip, r2, #12
+ beq 4f
+ tst ip, #8
+ beq 3f
+ ldmia r0!, {r5, r6}
+ orr r4, r4, r5, lsl #8
+ mov r5, r5, lsr #24
+ orr r5, r5, r6, lsl #8
+ stmia r1!, {r4, r5}
+ adcs r3, r3, r4
+ adcs r3, r3, r5
+ mov r4, r6, lsr #24
+ tst ip, #4
+ beq 4f
+3: ldr r5, [r0], #4
+ orr r4, r4, r5, lsl #8
+ str r4, [r1], #4
+ adcs r3, r3, r4
+ mov r4, r5, lsr #24
+4: ands r2, r2, #3
+ adceq r0, r3, #0
+ LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
+ tst r2, #2
+ beq Lexit
+ adcs r3, r3, r4, lsl #16
+ strb r4, [r1], #1
+ ldr r4, [r0], #4
+ strb r4, [r1], #1
+ adcs r3, r3, r4, lsl #24
+ mov r4, r4, lsr #8
+ b Lexit
+
+ENTRY(__csum_ipv6_magic)
+ stmfd sp!, {lr}
+ adds ip, r2, r3
+ ldmia r1, {r1 - r3, lr}
+ adcs ip, ip, r1
+ adcs ip, ip, r2
+ adcs ip, ip, r3
+ adcs ip, ip, lr
+ ldmia r0, {r0 - r3}
+ adcs r0, ip, r0
+ adcs r0, r0, r1
+ adcs r0, r0, r2
+ adcs r0, r0, r3
+ ldr r3, [sp, #4]
+ adcs r0, r0, r3
+ adcs r0, r0, #0
+ LOADREGS(fd, sp!, {pc})
diff --git a/pfinet/linux-src/arch/i386/lib/checksum.S b/pfinet/linux-src/arch/i386/lib/checksum.S
new file mode 100644
index 00000000..3df25799
--- /dev/null
+++ b/pfinet/linux-src/arch/i386/lib/checksum.S
@@ -0,0 +1,447 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * IP/TCP/UDP checksumming routines
+ *
+ * Authors: Jorge Cwik, <jorge@laser.satlink.net>
+ * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ * Tom May, <ftom@netcom.com>
+ * Pentium Pro/II routines:
+ * Alexander Kjeldaas <astor@guardian.no>
+ * Finn Arne Gangstad <finnag@guardian.no>
+ * Lots of code moved from tcp.c and ip.c; see those files
+ * for more names.
+ *
+ * Changes: Ingo Molnar, converted csum_partial_copy() to 2.1 exception
+ * handling.
+ * Andi Kleen, add zeroing on error
+ * converted to pure assembler
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <asm/errno.h>
+
+/*
+ * computes a partial checksum, e.g. for TCP/UDP fragments
+ */
+
+/*
+unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum)
+ */
+
+.text
+.align 4
+.globl csum_partial
+
+#if CPU!=686
+
+ /*
+ * Experiments with Ethernet and SLIP connections show that buff
+ * is aligned on either a 2-byte or 4-byte boundary. We get at
+ * least a twofold speedup on 486 and Pentium if it is 4-byte aligned.
+ * Fortunately, it is easy to convert 2-byte alignment to 4-byte
+ * alignment for the unrolled loop.
+ */
+csum_partial:
+ pushl %esi
+ pushl %ebx
+ movl 20(%esp),%eax # Function arg: unsigned int sum
+ movl 16(%esp),%ecx # Function arg: int len
+ movl 12(%esp),%esi # Function arg: unsigned char *buff
+ testl $2, %esi # Check alignment.
+ jz 2f # Jump if alignment is ok.
+ subl $2, %ecx # Alignment uses up two bytes.
+ jae 1f # Jump if we had at least two bytes.
+ addl $2, %ecx # ecx was < 2. Deal with it.
+ jmp 4f
+1: movw (%esi), %bx
+ addl $2, %esi
+ addw %bx, %ax
+ adcl $0, %eax
+2:
+ movl %ecx, %edx
+ shrl $5, %ecx
+ jz 2f
+ testl %esi, %esi
+1: movl (%esi), %ebx
+ adcl %ebx, %eax
+ movl 4(%esi), %ebx
+ adcl %ebx, %eax
+ movl 8(%esi), %ebx
+ adcl %ebx, %eax
+ movl 12(%esi), %ebx
+ adcl %ebx, %eax
+ movl 16(%esi), %ebx
+ adcl %ebx, %eax
+ movl 20(%esi), %ebx
+ adcl %ebx, %eax
+ movl 24(%esi), %ebx
+ adcl %ebx, %eax
+ movl 28(%esi), %ebx
+ adcl %ebx, %eax
+ lea 32(%esi), %esi
+ dec %ecx
+ jne 1b
+ adcl $0, %eax
+2: movl %edx, %ecx
+ andl $0x1c, %edx
+ je 4f
+ shrl $2, %edx # This clears CF
+3: adcl (%esi), %eax
+ lea 4(%esi), %esi
+ dec %edx
+ jne 3b
+ adcl $0, %eax
+4: andl $3, %ecx
+ jz 7f
+ cmpl $2, %ecx
+ jb 5f
+ movw (%esi),%cx
+ leal 2(%esi),%esi
+ je 6f
+ shll $16,%ecx
+5: movb (%esi),%cl
+6: addl %ecx,%eax
+ adcl $0, %eax
+7:
+ popl %ebx
+ popl %esi
+ ret
+
+#else /* CPU==686 */
+
+csum_partial:
+ movl 12(%esp),%eax # Function arg: unsigned int sum
+ movl 8(%esp),%ecx # Function arg: int len
+ movl 4(%esp),%esi # Function arg: const unsigned char *buf
+
+ testl $2, %esi
+ jnz 30f
+10:
+ movl %ecx, %edx
+ movl %ecx, %ebx
+ andl $0x7c, %ebx
+ shrl $7, %ecx
+ addl %ebx,%esi
+ shrl $2, %ebx
+ negl %ebx
+ lea 45f(%ebx,%ebx,2), %ebx
+ testl %esi, %esi
+ jmp *%ebx
+
+ # Handle 2-byte-aligned regions
+20: addw (%esi), %ax
+ lea 2(%esi), %esi
+ adcl $0, %eax
+ jmp 10b
+
+30: subl $2, %ecx
+ ja 20b
+ je 32f
+ movzbl (%esi),%ebx # csumming 1 byte, 2-aligned
+ addl %ebx, %eax
+ adcl $0, %eax
+ jmp 80f
+32:
+ addw (%esi), %ax # csumming 2 bytes, 2-aligned
+ adcl $0, %eax
+ jmp 80f
+
+40:
+ addl -128(%esi), %eax
+ adcl -124(%esi), %eax
+ adcl -120(%esi), %eax
+ adcl -116(%esi), %eax
+ adcl -112(%esi), %eax
+ adcl -108(%esi), %eax
+ adcl -104(%esi), %eax
+ adcl -100(%esi), %eax
+ adcl -96(%esi), %eax
+ adcl -92(%esi), %eax
+ adcl -88(%esi), %eax
+ adcl -84(%esi), %eax
+ adcl -80(%esi), %eax
+ adcl -76(%esi), %eax
+ adcl -72(%esi), %eax
+ adcl -68(%esi), %eax
+ adcl -64(%esi), %eax
+ adcl -60(%esi), %eax
+ adcl -56(%esi), %eax
+ adcl -52(%esi), %eax
+ adcl -48(%esi), %eax
+ adcl -44(%esi), %eax
+ adcl -40(%esi), %eax
+ adcl -36(%esi), %eax
+ adcl -32(%esi), %eax
+ adcl -28(%esi), %eax
+ adcl -24(%esi), %eax
+ adcl -20(%esi), %eax
+ adcl -16(%esi), %eax
+ adcl -12(%esi), %eax
+ adcl -8(%esi), %eax
+ adcl -4(%esi), %eax
+45:
+ lea 128(%esi), %esi
+ adcl $0, %eax
+ dec %ecx
+ jge 40b
+ movl %edx, %ecx
+50: andl $3, %ecx
+ jz 80f
+
+ # Handle the last 1-3 bytes without jumping
+ notl %ecx # 1->2, 2->1, 3->0, higher bits are masked
+ movl $0xffffff,%ebx # by the shll and shrl instructions
+ shll $3,%ecx
+ shrl %cl,%ebx
+ andl -128(%esi),%ebx # esi is 4-aligned so should be ok
+ addl %ebx,%eax
+ adcl $0,%eax
+80:
+ ret
+
+#endif /* CPU==686 */
+
+/*
+unsigned int csum_partial_copy_generic (const char *src, char *dst,
+ int len, int sum, int *src_err_ptr, int *dst_err_ptr)
+ */
+
+/*
+ * Copy from ds while checksumming, otherwise like csum_partial
+ *
+ * The macros SRC and DST specify the type of access for the instruction.
+ * thus we can call a custom exception handler for all access types.
+ *
+ * FIXME: could someone double-check whether I haven't mixed up some SRC and
+ * DST definitions? It's damn hard to trigger all cases. I hope I got
+ * them all but there's no guarantee.
+ */
+
+#define SRC(y...) \
+ 9999: y; \
+ .section __ex_table, "a"; \
+ .long 9999b, 6001f ; \
+ .previous
+
+#define DST(y...) \
+ 9999: y; \
+ .section __ex_table, "a"; \
+ .long 9999b, 6002f ; \
+ .previous
+
+.align 4
+.globl csum_partial_copy_generic
+
+#if CPU!=686
+
+#define ARGBASE 16
+#define FP 12
+
+csum_partial_copy_generic:
+ subl $4,%esp
+ pushl %edi
+ pushl %esi
+ pushl %ebx
+ movl ARGBASE+16(%esp),%eax # sum
+ movl ARGBASE+12(%esp),%ecx # len
+ movl ARGBASE+4(%esp),%esi # src
+ movl ARGBASE+8(%esp),%edi # dst
+
+ testl $2, %edi # Check alignment.
+ jz 2f # Jump if alignment is ok.
+ subl $2, %ecx # Alignment uses up two bytes.
+ jae 1f # Jump if we had at least two bytes.
+ addl $2, %ecx # ecx was < 2. Deal with it.
+ jmp 4f
+SRC(1: movw (%esi), %bx )
+ addl $2, %esi
+DST( movw %bx, (%edi) )
+ addl $2, %edi
+ addw %bx, %ax
+ adcl $0, %eax
+2:
+ movl %ecx, FP(%esp)
+ shrl $5, %ecx
+ jz 2f
+ testl %esi, %esi
+SRC(1: movl (%esi), %ebx )
+SRC( movl 4(%esi), %edx )
+ adcl %ebx, %eax
+DST( movl %ebx, (%edi) )
+ adcl %edx, %eax
+DST( movl %edx, 4(%edi) )
+
+SRC( movl 8(%esi), %ebx )
+SRC( movl 12(%esi), %edx )
+ adcl %ebx, %eax
+DST( movl %ebx, 8(%edi) )
+ adcl %edx, %eax
+DST( movl %edx, 12(%edi) )
+
+SRC( movl 16(%esi), %ebx )
+SRC( movl 20(%esi), %edx )
+ adcl %ebx, %eax
+DST( movl %ebx, 16(%edi) )
+ adcl %edx, %eax
+DST( movl %edx, 20(%edi) )
+
+SRC( movl 24(%esi), %ebx )
+SRC( movl 28(%esi), %edx )
+ adcl %ebx, %eax
+DST( movl %ebx, 24(%edi) )
+ adcl %edx, %eax
+DST( movl %edx, 28(%edi) )
+
+ lea 32(%esi), %esi
+ lea 32(%edi), %edi
+ dec %ecx
+ jne 1b
+ adcl $0, %eax
+2: movl FP(%esp), %edx
+ movl %edx, %ecx
+ andl $0x1c, %edx
+ je 4f
+ shrl $2, %edx # This clears CF
+SRC(3: movl (%esi), %ebx )
+ adcl %ebx, %eax
+DST( movl %ebx, (%edi) )
+ lea 4(%esi), %esi
+ lea 4(%edi), %edi
+ dec %edx
+ jne 3b
+ adcl $0, %eax
+4: andl $3, %ecx
+ jz 7f
+ cmpl $2, %ecx
+ jb 5f
+SRC( movw (%esi), %cx )
+ leal 2(%esi), %esi
+DST( movw %cx, (%edi) )
+ leal 2(%edi), %edi
+ je 6f
+ shll $16,%ecx
+SRC(5: movb (%esi), %cl )
+DST( movb %cl, (%edi) )
+6: addl %ecx, %eax
+ adcl $0, %eax
+7:
+5000:
+
+/* Exception handler: */
+.section .fixup, "ax"
+
+6001:
+ movl ARGBASE+20(%esp), %ebx # src_err_ptr
+ movl $-EFAULT, (%ebx)
+
+ /* zero the complete destination - computing the rest
+ is too much work */
+ movl ARGBASE+8(%esp), %edi # dst
+ movl ARGBASE+12(%esp), %ecx # len
+ xorl %eax,%eax
+ rep ; stosb
+
+ jmp 5000b
+
+6002:
+ movl ARGBASE+24(%esp), %ebx # dst_err_ptr
+ movl $-EFAULT,(%ebx)
+ jmp 5000b
+
+.previous
+
+ popl %ebx
+ popl %esi
+ popl %edi
+ popl %ecx # equivalent to addl $4,%esp
+ ret
+
+#else
+
+/* Version for PentiumII/PPro */
+
+#define ROUND1(x) \
+ SRC(movl x(%esi), %ebx ) ; \
+ addl %ebx, %eax ; \
+ DST(movl %ebx, x(%edi) ) ;
+
+#define ROUND(x) \
+ SRC(movl x(%esi), %ebx ) ; \
+ adcl %ebx, %eax ; \
+ DST(movl %ebx, x(%edi) ) ;
+
+#define ARGBASE 12
+
+csum_partial_copy_generic:
+ pushl %ebx
+ pushl %edi
+ pushl %esi
+ movl ARGBASE+4(%esp),%esi #src
+ movl ARGBASE+8(%esp),%edi #dst
+ movl ARGBASE+12(%esp),%ecx #len
+ movl ARGBASE+16(%esp),%eax #sum
+ movl %ecx, %edx
+ movl %ecx, %ebx
+ shrl $6, %ecx
+ andl $0x3c, %ebx
+ negl %ebx
+ subl %ebx, %esi
+ subl %ebx, %edi
+ lea 3f(%ebx,%ebx), %ebx
+ testl %esi, %esi
+ jmp *%ebx
+1: addl $64,%esi
+ addl $64,%edi
+ ROUND1(-64) ROUND(-60) ROUND(-56) ROUND(-52)
+ ROUND (-48) ROUND(-44) ROUND(-40) ROUND(-36)
+ ROUND (-32) ROUND(-28) ROUND(-24) ROUND(-20)
+ ROUND (-16) ROUND(-12) ROUND(-8) ROUND(-4)
+3: adcl $0,%eax
+ dec %ecx
+ jge 1b
+4: andl $3, %edx
+ jz 7f
+ cmpl $2, %edx
+ jb 5f
+SRC( movw (%esi), %dx )
+ leal 2(%esi), %esi
+DST( movw %dx, (%edi) )
+ leal 2(%edi), %edi
+ je 6f
+ shll $16,%edx
+5:
+SRC( movb (%esi), %dl )
+DST( movb %dl, (%edi) )
+6: addl %edx, %eax
+ adcl $0, %eax
+7:
+.section .fixup, "ax"
+6001: movl ARGBASE+20(%esp), %ebx # src_err_ptr
+ movl $-EFAULT, (%ebx)
+ # zero the complete destination (computing the rest is too much work)
+ movl ARGBASE+8(%esp),%edi # dst
+ movl ARGBASE+12(%esp),%ecx # len
+ xorl %eax,%eax
+ rep; stosb
+ jmp 7b
+6002: movl ARGBASE+24(%esp), %ebx # dst_err_ptr
+ movl $-EFAULT, (%ebx)
+ jmp 7b
+.previous
+
+ popl %esi
+ popl %edi
+ popl %ebx
+ ret
+
+#undef ROUND
+#undef ROUND1
+
+#endif /* CPU==i686 */
diff --git a/pfinet/linux-src/arch/i386/lib/old-checksum.c b/pfinet/linux-src/arch/i386/lib/old-checksum.c
new file mode 100644
index 00000000..df741335
--- /dev/null
+++ b/pfinet/linux-src/arch/i386/lib/old-checksum.c
@@ -0,0 +1,17 @@
+/*
+ * FIXME: old compatibility stuff, will be removed soon.
+ */
+
+#include <net/checksum.h>
+
+unsigned int csum_partial_copy( const char *src, char *dst, int len, int sum)
+{
+ int src_err=0, dst_err=0;
+
+ sum = csum_partial_copy_generic ( src, dst, len, sum, &src_err, &dst_err);
+
+ if (src_err || dst_err)
+ printk("old csum_partial_copy_fromuser(), tell mingo to convert me.\n");
+
+ return sum;
+}
diff --git a/pfinet/linux-src/arch/m68k/lib/checksum.c b/pfinet/linux-src/arch/m68k/lib/checksum.c
new file mode 100644
index 00000000..5110cac4
--- /dev/null
+++ b/pfinet/linux-src/arch/m68k/lib/checksum.c
@@ -0,0 +1,420 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * IP/TCP/UDP checksumming routines
+ *
+ * Authors: Jorge Cwik, <jorge@laser.satlink.net>
+ * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ * Tom May, <ftom@netcom.com>
+ * Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de>
+ * Lots of code moved from tcp.c and ip.c; see those files
+ * for more names.
+ *
+ * 03/02/96 Jes Sorensen, Andreas Schwab, Roman Hodek:
+ * Fixed some nasty bugs, causing some horrible crashes.
+ * A: At some points, the sum (%0) was used as
+ * length-counter instead of the length counter
+ * (%1). Thanks to Roman Hodek for pointing this out.
+ * B: GCC seems to mess up if one uses too many
+ * data-registers to hold input values and one tries to
+ * specify d0 and d1 as scratch registers. Letting gcc
+ * choose these registers itself solves the problem.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * 1998/8/31 Andreas Schwab:
+ * Zero out rest of buffer on exception in
+ * csum_partial_copy_from_user.
+ */
+
+#include <net/checksum.h>
+
+/*
+ * computes a partial checksum, e.g. for TCP/UDP fragments
+ */
+
+unsigned int
+csum_partial (const unsigned char *buff, int len, unsigned int sum)
+{
+ unsigned long tmp1, tmp2;
+ /*
+ * Experiments with ethernet and slip connections show that buff
+ * is aligned on either a 2-byte or 4-byte boundary.
+ */
+ __asm__("movel %2,%3\n\t"
+ "btst #1,%3\n\t" /* Check alignment */
+ "jeq 2f\n\t"
+ "subql #2,%1\n\t" /* buff%4==2: treat first word */
+ "jgt 1f\n\t"
+ "addql #2,%1\n\t" /* len was == 2, treat only rest */
+ "jra 4f\n"
+ "1:\t"
+ "addw %2@+,%0\n\t" /* add first word to sum */
+ "clrl %3\n\t"
+ "addxl %3,%0\n" /* add X bit */
+ "2:\t"
+ /* unrolled loop for the main part: do 8 longs at once */
+ "movel %1,%3\n\t" /* save len in tmp1 */
+ "lsrl #5,%1\n\t" /* len/32 */
+ "jeq 2f\n\t" /* not enough... */
+ "subql #1,%1\n"
+ "1:\t"
+ "movel %2@+,%4\n\t"
+ "addxl %4,%0\n\t"
+ "movel %2@+,%4\n\t"
+ "addxl %4,%0\n\t"
+ "movel %2@+,%4\n\t"
+ "addxl %4,%0\n\t"
+ "movel %2@+,%4\n\t"
+ "addxl %4,%0\n\t"
+ "movel %2@+,%4\n\t"
+ "addxl %4,%0\n\t"
+ "movel %2@+,%4\n\t"
+ "addxl %4,%0\n\t"
+ "movel %2@+,%4\n\t"
+ "addxl %4,%0\n\t"
+ "movel %2@+,%4\n\t"
+ "addxl %4,%0\n\t"
+ "dbra %1,1b\n\t"
+ "clrl %4\n\t"
+ "addxl %4,%0\n\t" /* add X bit */
+ "clrw %1\n\t"
+ "subql #1,%1\n\t"
+ "jcc 1b\n"
+ "2:\t"
+ "movel %3,%1\n\t" /* restore len from tmp1 */
+ "andw #0x1c,%3\n\t" /* number of rest longs */
+ "jeq 4f\n\t"
+ "lsrw #2,%3\n\t"
+ "subqw #1,%3\n"
+ "3:\t"
+ /* loop for rest longs */
+ "movel %2@+,%4\n\t"
+ "addxl %4,%0\n\t"
+ "dbra %3,3b\n\t"
+ "clrl %4\n\t"
+ "addxl %4,%0\n" /* add X bit */
+ "4:\t"
+ /* now check for rest bytes that do not fit into longs */
+ "andw #3,%1\n\t"
+ "jeq 7f\n\t"
+ "clrl %4\n\t" /* clear tmp2 for rest bytes */
+ "subqw #2,%1\n\t"
+ "jlt 5f\n\t"
+ "movew %2@+,%4\n\t" /* have rest >= 2: get word */
+ "swap %4\n\t" /* into bits 16..31 */
+ "tstw %1\n\t" /* another byte? */
+ "jeq 6f\n"
+ "5:\t"
+ "moveb %2@,%4\n\t" /* have odd rest: get byte */
+ "lslw #8,%4\n\t" /* into bits 8..15; 16..31 untouched */
+ "6:\t"
+ "addl %4,%0\n\t" /* now add rest long to sum */
+ "clrl %4\n\t"
+ "addxl %4,%0\n" /* add X bit */
+ "7:\t"
+ : "=d" (sum), "=d" (len), "=a" (buff),
+ "=&d" (tmp1), "=&d" (tmp2)
+ : "0" (sum), "1" (len), "2" (buff)
+ );
+ return(sum);
+}
+
+
+
+/*
+ * copy from user space while checksumming, with exception handling.
+ */
+
+unsigned int
+csum_partial_copy_from_user(const char *src, char *dst, int len,
+ int sum, int *csum_err)
+{
+ /*
+ * GCC doesn't like more than 10 operands for the asm
+ * statements so we have to use tmp2 for the error
+ * code.
+ */
+ unsigned long tmp1, tmp2;
+
+ __asm__("movel %2,%4\n\t"
+ "btst #1,%4\n\t" /* Check alignment */
+ "jeq 2f\n\t"
+ "subql #2,%1\n\t" /* buff%4==2: treat first word */
+ "jgt 1f\n\t"
+ "addql #2,%1\n\t" /* len was == 2, treat only rest */
+ "jra 4f\n"
+ "1:\n"
+ "10:\t"
+ "movesw %2@+,%4\n\t" /* add first word to sum */
+ "addw %4,%0\n\t"
+ "movew %4,%3@+\n\t"
+ "clrl %4\n\t"
+ "addxl %4,%0\n" /* add X bit */
+ "2:\t"
+ /* unrolled loop for the main part: do 8 longs at once */
+ "movel %1,%4\n\t" /* save len in tmp1 */
+ "lsrl #5,%1\n\t" /* len/32 */
+ "jeq 2f\n\t" /* not enough... */
+ "subql #1,%1\n"
+ "1:\n"
+ "11:\t"
+ "movesl %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "12:\t"
+ "movesl %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "13:\t"
+ "movesl %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "14:\t"
+ "movesl %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "15:\t"
+ "movesl %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "16:\t"
+ "movesl %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "17:\t"
+ "movesl %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "18:\t"
+ "movesl %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "dbra %1,1b\n\t"
+ "clrl %5\n\t"
+ "addxl %5,%0\n\t" /* add X bit */
+ "clrw %1\n\t"
+ "subql #1,%1\n\t"
+ "jcc 1b\n"
+ "2:\t"
+ "movel %4,%1\n\t" /* restore len from tmp1 */
+ "andw #0x1c,%4\n\t" /* number of rest longs */
+ "jeq 4f\n\t"
+ "lsrw #2,%4\n\t"
+ "subqw #1,%4\n"
+ "3:\n"
+ /* loop for rest longs */
+ "19:\t"
+ "movesl %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "dbra %4,3b\n\t"
+ "clrl %5\n\t"
+ "addxl %5,%0\n" /* add X bit */
+ "4:\t"
+ /* now check for rest bytes that do not fit into longs */
+ "andw #3,%1\n\t"
+ "jeq 7f\n\t"
+ "clrl %5\n\t" /* clear tmp2 for rest bytes */
+ "subqw #2,%1\n\t"
+ "jlt 5f\n\t"
+ "20:\t"
+ "movesw %2@+,%5\n\t" /* have rest >= 2: get word */
+ "movew %5,%3@+\n\t"
+ "swap %5\n\t" /* into bits 16..31 */
+ "tstw %1\n\t" /* another byte? */
+ "jeq 6f\n"
+ "5:\n"
+ "21:\t"
+ "movesb %2@,%5\n\t" /* have odd rest: get byte */
+ "moveb %5,%3@+\n\t"
+ "lslw #8,%5\n\t" /* into bits 8..15; 16..31 untouched */
+ "6:\t"
+ "addl %5,%0\n\t" /* now add rest long to sum */
+ "clrl %5\n\t"
+ "addxl %5,%0\n\t" /* add X bit */
+ "7:\t"
+ "clrl %5\n" /* no error - clear return value */
+ "8:\n"
+ ".section .fixup,\"ax\"\n"
+ ".even\n"
+ /* If any execption occurs zero out the rest.
+ Similarities with the code above are intentional :-) */
+ "90:\t"
+ "clrw %3@+\n\t"
+ "movel %1,%4\n\t"
+ "lsrl #5,%1\n\t"
+ "jeq 1f\n\t"
+ "subql #1,%1\n"
+ "91:\t"
+ "clrl %3@+\n"
+ "92:\t"
+ "clrl %3@+\n"
+ "93:\t"
+ "clrl %3@+\n"
+ "94:\t"
+ "clrl %3@+\n"
+ "95:\t"
+ "clrl %3@+\n"
+ "96:\t"
+ "clrl %3@+\n"
+ "97:\t"
+ "clrl %3@+\n"
+ "98:\t"
+ "clrl %3@+\n\t"
+ "dbra %1,91b\n\t"
+ "clrw %1\n\t"
+ "subql #1,%1\n\t"
+ "jcc 91b\n"
+ "1:\t"
+ "movel %4,%1\n\t"
+ "andw #0x1c,%4\n\t"
+ "jeq 1f\n\t"
+ "lsrw #2,%4\n\t"
+ "subqw #1,%4\n"
+ "99:\t"
+ "clrl %3@+\n\t"
+ "dbra %4,99b\n\t"
+ "1:\t"
+ "andw #3,%1\n\t"
+ "jeq 9f\n"
+ "100:\t"
+ "clrw %3@+\n\t"
+ "tstw %1\n\t"
+ "jeq 9f\n"
+ "101:\t"
+ "clrb %3@+\n"
+ "9:\t"
+#define STR(X) STR1(X)
+#define STR1(X) #X
+ "moveq #-" STR(EFAULT) ",%5\n\t"
+ "jra 8b\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ ".long 10b,90b\n"
+ ".long 11b,91b\n"
+ ".long 12b,92b\n"
+ ".long 13b,93b\n"
+ ".long 14b,94b\n"
+ ".long 15b,95b\n"
+ ".long 16b,96b\n"
+ ".long 17b,97b\n"
+ ".long 18b,98b\n"
+ ".long 19b,99b\n"
+ ".long 20b,100b\n"
+ ".long 21b,101b\n"
+ ".previous"
+ : "=d" (sum), "=d" (len), "=a" (src), "=a" (dst),
+ "=&d" (tmp1), "=d" (tmp2)
+ : "0" (sum), "1" (len), "2" (src), "3" (dst)
+ );
+
+ *csum_err = tmp2;
+
+ return(sum);
+}
+
+/*
+ * copy from kernel space while checksumming, otherwise like csum_partial
+ */
+
+unsigned int
+csum_partial_copy(const char *src, char *dst, int len, int sum)
+{
+ unsigned long tmp1, tmp2;
+ __asm__("movel %2,%4\n\t"
+ "btst #1,%4\n\t" /* Check alignment */
+ "jeq 2f\n\t"
+ "subql #2,%1\n\t" /* buff%4==2: treat first word */
+ "jgt 1f\n\t"
+ "addql #2,%1\n\t" /* len was == 2, treat only rest */
+ "jra 4f\n"
+ "1:\t"
+ "movew %2@+,%4\n\t" /* add first word to sum */
+ "addw %4,%0\n\t"
+ "movew %4,%3@+\n\t"
+ "clrl %4\n\t"
+ "addxl %4,%0\n" /* add X bit */
+ "2:\t"
+ /* unrolled loop for the main part: do 8 longs at once */
+ "movel %1,%4\n\t" /* save len in tmp1 */
+ "lsrl #5,%1\n\t" /* len/32 */
+ "jeq 2f\n\t" /* not enough... */
+ "subql #1,%1\n"
+ "1:\t"
+ "movel %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "movel %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "movel %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "movel %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "movel %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "movel %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "movel %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "movel %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "dbra %1,1b\n\t"
+ "clrl %5\n\t"
+ "addxl %5,%0\n\t" /* add X bit */
+ "clrw %1\n\t"
+ "subql #1,%1\n\t"
+ "jcc 1b\n"
+ "2:\t"
+ "movel %4,%1\n\t" /* restore len from tmp1 */
+ "andw #0x1c,%4\n\t" /* number of rest longs */
+ "jeq 4f\n\t"
+ "lsrw #2,%4\n\t"
+ "subqw #1,%4\n"
+ "3:\t"
+ /* loop for rest longs */
+ "movel %2@+,%5\n\t"
+ "addxl %5,%0\n\t"
+ "movel %5,%3@+\n\t"
+ "dbra %4,3b\n\t"
+ "clrl %5\n\t"
+ "addxl %5,%0\n" /* add X bit */
+ "4:\t"
+ /* now check for rest bytes that do not fit into longs */
+ "andw #3,%1\n\t"
+ "jeq 7f\n\t"
+ "clrl %5\n\t" /* clear tmp2 for rest bytes */
+ "subqw #2,%1\n\t"
+ "jlt 5f\n\t"
+ "movew %2@+,%5\n\t" /* have rest >= 2: get word */
+ "movew %5,%3@+\n\t"
+ "swap %5\n\t" /* into bits 16..31 */
+ "tstw %1\n\t" /* another byte? */
+ "jeq 6f\n"
+ "5:\t"
+ "moveb %2@,%5\n\t" /* have odd rest: get byte */
+ "moveb %5,%3@+\n\t"
+ "lslw #8,%5\n" /* into bits 8..15; 16..31 untouched */
+ "6:\t"
+ "addl %5,%0\n\t" /* now add rest long to sum */
+ "clrl %5\n\t"
+ "addxl %5,%0\n" /* add X bit */
+ "7:\t"
+ : "=d" (sum), "=d" (len), "=a" (src), "=a" (dst),
+ "=&d" (tmp1), "=&d" (tmp2)
+ : "0" (sum), "1" (len), "2" (src), "3" (dst)
+ );
+ return(sum);
+}
diff --git a/pfinet/linux-src/arch/ppc/lib/checksum.S b/pfinet/linux-src/arch/ppc/lib/checksum.S
new file mode 100644
index 00000000..66a2e3aa
--- /dev/null
+++ b/pfinet/linux-src/arch/ppc/lib/checksum.S
@@ -0,0 +1,194 @@
+/*
+ * This file contains assembly-language implementations
+ * of IP-style 1's complement checksum routines.
+ *
+ * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Severely hacked about by Paul Mackerras (paulus@cs.anu.edu.au).
+ */
+
+#include <linux/sys.h>
+#include <asm/processor.h>
+#include <asm/errno.h>
+#include "../kernel/ppc_asm.tmpl"
+
+ .text
+
+/*
+ * ip_fast_csum(buf, len) -- Optimized for IP header
+ * len is in words and is always >= 5.
+ */
+_GLOBAL(ip_fast_csum)
+ lwz r0,0(r3)
+ lwzu r5,4(r3)
+ addi r4,r4,-2
+ addc r0,r0,r5
+ mtctr r4
+1: lwzu r4,4(r3)
+ adde r0,r0,r4
+ bdnz 1b
+ addze r0,r0 /* add in final carry */
+ rlwinm r3,r0,16,0,31 /* fold two halves together */
+ add r3,r0,r3
+ not r3,r3
+ srwi r3,r3,16
+ blr
+
+/*
+ * Compute checksum of TCP or UDP pseudo-header:
+ * csum_tcpudp_magic(saddr, daddr, len, proto, sum)
+ */
+_GLOBAL(csum_tcpudp_magic)
+ rlwimi r5,r6,16,0,15 /* put proto in upper half of len */
+ addc r0,r3,r4 /* add 4 32-bit words together */
+ adde r0,r0,r5
+ adde r0,r0,r7
+ addze r0,r0 /* add in final carry */
+ rlwinm r3,r0,16,0,31 /* fold two halves together */
+ add r3,r0,r3
+ not r3,r3
+ srwi r3,r3,16
+ blr
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * csum_partial(buff, len, sum)
+ */
+_GLOBAL(csum_partial)
+ addic r0,r5,0
+ subi r3,r3,4
+ srwi. r6,r4,2
+ beq 3f /* if we're doing < 4 bytes */
+ andi. r5,r3,2 /* Align buffer to longword boundary */
+ beq+ 1f
+ lhz r5,4(r3) /* do 2 bytes to get aligned */
+ addi r3,r3,2
+ subi r4,r4,2
+ addc r0,r0,r5
+ srwi. r6,r4,2 /* # words to do */
+ beq 3f
+1: mtctr r6
+2: lwzu r5,4(r3) /* the bdnz has zero overhead, so it should */
+ adde r0,r0,r5 /* be unnecessary to unroll this loop */
+ bdnz 2b
+ andi. r4,r4,3
+3: cmpi 0,r4,2
+ blt+ 4f
+ lhz r5,4(r3)
+ addi r3,r3,2
+ subi r4,r4,2
+ adde r0,r0,r5
+4: cmpi 0,r4,1
+ bne+ 5f
+ lbz r5,4(r3)
+ slwi r5,r5,8 /* Upper byte of word */
+ adde r0,r0,r5
+5: addze r3,r0 /* add in final carry */
+ blr
+
+/*
+ * Computes the checksum of a memory block at src, length len,
+ * and adds in "sum" (32-bit), while copying the block to dst.
+ * If an access exception occurs on src or dst, it stores -EFAULT
+ * to *src_err or *dst_err respectively, and (for an error on
+ * src) zeroes the rest of dst.
+ *
+ * csum_partial_copy_generic(src, dst, len, sum, src_err, dst_err)
+ */
+_GLOBAL(csum_partial_copy_generic)
+ addic r0,r6,0
+ subi r3,r3,4
+ subi r4,r4,4
+ srwi. r6,r5,2
+ beq 3f /* if we're doing < 4 bytes */
+ andi. r9,r4,2 /* Align dst to longword boundary */
+ beq+ 1f
+81: lhz r6,4(r3) /* do 2 bytes to get aligned */
+ addi r3,r3,2
+ subi r5,r5,2
+91: sth r6,4(r4)
+ addi r4,r4,2
+ addc r0,r0,r6
+ srwi. r6,r5,2 /* # words to do */
+ beq 3f
+1: mtctr r6
+82: lwzu r6,4(r3) /* the bdnz has zero overhead, so it should */
+92: stwu r6,4(r4) /* be unnecessary to unroll this loop */
+ adde r0,r0,r6
+ bdnz 82b
+ andi. r5,r5,3
+3: cmpi 0,r5,2
+ blt+ 4f
+83: lhz r6,4(r3)
+ addi r3,r3,2
+ subi r5,r5,2
+93: sth r6,4(r4)
+ addi r4,r4,2
+ adde r0,r0,r6
+4: cmpi 0,r5,1
+ bne+ 5f
+84: lbz r6,4(r3)
+94: stb r6,4(r4)
+ slwi r6,r6,8 /* Upper byte of word */
+ adde r0,r0,r6
+5: addze r3,r0 /* add in final carry */
+ blr
+
+/* These shouldn't go in the fixup section, since that would
+ cause the ex_table addresses to get out of order. */
+
+src_error_1:
+ li r6,0
+ subi r5,r5,2
+95: sth r6,4(r4)
+ addi r4,r4,2
+ srwi. r6,r5,2
+ beq 3f
+ mtctr r6
+src_error_2:
+ li r6,0
+96: stwu r6,4(r4)
+ bdnz 96b
+3: andi. r5,r5,3
+ beq src_error
+src_error_3:
+ li r6,0
+ mtctr r5
+ addi r4,r4,3
+97: stbu r6,1(r4)
+ bdnz 97b
+src_error:
+ cmpi 0,r7,0
+ beq 1f
+ li r6,-EFAULT
+ stw r6,0(r7)
+1: addze r3,r0
+ blr
+
+dst_error:
+ cmpi 0,r8,0
+ beq 1f
+ li r6,-EFAULT
+ stw r6,0(r8)
+1: addze r3,r0
+ blr
+
+.section __ex_table,"a"
+ .long 81b,src_error_1
+ .long 91b,dst_error
+ .long 82b,src_error_2
+ .long 92b,dst_error
+ .long 83b,src_error_3
+ .long 93b,dst_error
+ .long 84b,src_error_3
+ .long 94b,dst_error
+ .long 95b,dst_error
+ .long 96b,dst_error
+ .long 97b,dst_error
diff --git a/pfinet/linux-src/arch/s390/lib/checksum.c b/pfinet/linux-src/arch/s390/lib/checksum.c
new file mode 100644
index 00000000..bb3b1ab1
--- /dev/null
+++ b/pfinet/linux-src/arch/s390/lib/checksum.c
@@ -0,0 +1,55 @@
+/*
+ * arch/s390/lib/checksum.c
+ * S390 fast network checksum routines
+ *
+ * S390 version
+ * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Ulrich Hild (first version),
+ * Martin Schwidefsky (schwidefsky@de.ibm.com),
+ * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
+ *
+ * This file contains network checksum routines
+ */
+
+#include <linux/string.h>
+#include <linux/types.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+#include <asm/checksum.h>
+
+/*
+ * computes a partial checksum, e.g. for TCP/UDP fragments
+ */
+unsigned int
+csum_partial (const unsigned char *buff, int len, unsigned int sum)
+{
+ /*
+ * Experiments with ethernet and slip connections show that buff
+ * is aligned on either a 2-byte or 4-byte boundary.
+ */
+ __asm__ __volatile__ (
+ " lr 2,%1\n" /* address in gpr 2 */
+ " lr 3,%2\n" /* length in gpr 3 */
+ "0: cksm %0,2\n" /* do checksum on longs */
+ " jo 0b\n"
+ : "+&d" (sum)
+ : "d" (buff), "d" (len)
+ : "cc", "2", "3" );
+ return sum;
+}
+
+/*
+ * Fold a partial checksum without adding pseudo headers
+ */
+unsigned short csum_fold(unsigned int sum)
+{
+ __asm__ __volatile__ (
+ " sr 3,3\n" /* %0 = H*65536 + L */
+ " lr 2,%0\n" /* %0 = H L, R2/R3 = H L / 0 0 */
+ " srdl 2,16\n" /* %0 = H L, R2/R3 = 0 H / L 0 */
+ " alr 2,3\n" /* %0 = H L, R2/R3 = L H / L 0 */
+ " alr %0,2\n" /* %0 = H+L+C L+H */
+ " srl %0,16\n" /* %0 = H+L+C */
+ : "+d" (sum) : : "cc", "2", "3");
+ return ((unsigned short) ~sum);
+}
diff --git a/pfinet/linux-src/arch/sparc/lib/checksum.S b/pfinet/linux-src/arch/sparc/lib/checksum.S
new file mode 100644
index 00000000..3dc58259
--- /dev/null
+++ b/pfinet/linux-src/arch/sparc/lib/checksum.S
@@ -0,0 +1,581 @@
+/* checksum.S: Sparc optimized checksum code.
+ *
+ * Copyright(C) 1995 Linus Torvalds
+ * Copyright(C) 1995 Miguel de Icaza
+ * Copyright(C) 1996 David S. Miller
+ * Copyright(C) 1997 Jakub Jelinek
+ *
+ * derived from:
+ * Linux/Alpha checksum c-code
+ * Linux/ix86 inline checksum assembly
+ * RFC1071 Computing the Internet Checksum (esp. Jacobsons m68k code)
+ * David Mosberger-Tang for optimized reference c-code
+ * BSD4.4 portable checksum routine
+ */
+
+#include <asm/cprefix.h>
+#include <asm/errno.h>
+
+#define CSUM_BIGCHUNK(buf, offset, sum, t0, t1, t2, t3, t4, t5) \
+ ldd [buf + offset + 0x00], t0; \
+ ldd [buf + offset + 0x08], t2; \
+ addxcc t0, sum, sum; \
+ addxcc t1, sum, sum; \
+ ldd [buf + offset + 0x10], t4; \
+ addxcc t2, sum, sum; \
+ addxcc t3, sum, sum; \
+ ldd [buf + offset + 0x18], t0; \
+ addxcc t4, sum, sum; \
+ addxcc t5, sum, sum; \
+ addxcc t0, sum, sum; \
+ addxcc t1, sum, sum;
+
+#define CSUM_LASTCHUNK(buf, offset, sum, t0, t1, t2, t3) \
+ ldd [buf - offset - 0x08], t0; \
+ ldd [buf - offset - 0x00], t2; \
+ addxcc t0, sum, sum; \
+ addxcc t1, sum, sum; \
+ addxcc t2, sum, sum; \
+ addxcc t3, sum, sum;
+
+ /* Do end cruft out of band to get better cache patterns. */
+csum_partial_end_cruft:
+ be 1f ! caller asks %o1 & 0x8
+ andcc %o1, 4, %g0 ! nope, check for word remaining
+ ldd [%o0], %g2 ! load two
+ addcc %g2, %o2, %o2 ! add first word to sum
+ addxcc %g3, %o2, %o2 ! add second word as well
+ add %o0, 8, %o0 ! advance buf ptr
+ addx %g0, %o2, %o2 ! add in final carry
+ andcc %o1, 4, %g0 ! check again for word remaining
+1: be 1f ! nope, skip this code
+ andcc %o1, 3, %o1 ! check for trailing bytes
+ ld [%o0], %g2 ! load it
+ addcc %g2, %o2, %o2 ! add to sum
+ add %o0, 4, %o0 ! advance buf ptr
+ addx %g0, %o2, %o2 ! add in final carry
+ andcc %o1, 3, %g0 ! check again for trailing bytes
+1: be 1f ! no trailing bytes, return
+ addcc %o1, -1, %g0 ! only one byte remains?
+ bne 2f ! at least two bytes more
+ subcc %o1, 2, %o1 ! only two bytes more?
+ b 4f ! only one byte remains
+ or %g0, %g0, %o4 ! clear fake hword value
+2: lduh [%o0], %o4 ! get hword
+ be 6f ! jmp if only hword remains
+ add %o0, 2, %o0 ! advance buf ptr either way
+ sll %o4, 16, %o4 ! create upper hword
+4: ldub [%o0], %o5 ! get final byte
+ sll %o5, 8, %o5 ! put into place
+ or %o5, %o4, %o4 ! coalese with hword (if any)
+6: addcc %o4, %o2, %o2 ! add to sum
+1: retl ! get outta here
+ addx %g0, %o2, %o0 ! add final carry into retval
+
+ /* Also do alignment out of band to get better cache patterns. */
+csum_partial_fix_alignment:
+ cmp %o1, 6
+ bl cpte - 0x4
+ andcc %o0, 0x2, %g0
+ be 1f
+ andcc %o0, 0x4, %g0
+ lduh [%o0 + 0x00], %g2
+ sub %o1, 2, %o1
+ add %o0, 2, %o0
+ sll %g2, 16, %g2
+ addcc %g2, %o2, %o2
+ srl %o2, 16, %g3
+ addx %g0, %g3, %g2
+ sll %o2, 16, %o2
+ sll %g2, 16, %g3
+ srl %o2, 16, %o2
+ andcc %o0, 0x4, %g0
+ or %g3, %o2, %o2
+1: be cpa
+ andcc %o1, 0xffffff80, %o3
+ ld [%o0 + 0x00], %g2
+ sub %o1, 4, %o1
+ addcc %g2, %o2, %o2
+ add %o0, 4, %o0
+ addx %g0, %o2, %o2
+ b cpa
+ andcc %o1, 0xffffff80, %o3
+
+ /* The common case is to get called with a nicely aligned
+ * buffer of size 0x20. Follow the code path for that case.
+ */
+ .globl C_LABEL(csum_partial)
+C_LABEL(csum_partial): /* %o0=buf, %o1=len, %o2=sum */
+ andcc %o0, 0x7, %g0 ! alignment problems?
+ bne csum_partial_fix_alignment ! yep, handle it
+ sethi %hi(cpte - 8), %g7 ! prepare table jmp ptr
+ andcc %o1, 0xffffff80, %o3 ! num loop iterations
+cpa: be 3f ! none to do
+ andcc %o1, 0x70, %g1 ! clears carry flag too
+5: CSUM_BIGCHUNK(%o0, 0x00, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
+ CSUM_BIGCHUNK(%o0, 0x20, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
+ CSUM_BIGCHUNK(%o0, 0x40, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
+ CSUM_BIGCHUNK(%o0, 0x60, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
+ addx %g0, %o2, %o2 ! sink in final carry
+ subcc %o3, 128, %o3 ! detract from loop iters
+ bne 5b ! more to do
+ add %o0, 128, %o0 ! advance buf ptr
+ andcc %o1, 0x70, %g1 ! clears carry flag too
+3: be cpte ! nope
+ andcc %o1, 0xf, %g0 ! anything left at all?
+ srl %g1, 1, %o4 ! compute offset
+ sub %g7, %g1, %g7 ! adjust jmp ptr
+ sub %g7, %o4, %g7 ! final jmp ptr adjust
+ jmp %g7 + %lo(cpte - 8) ! enter the table
+ add %o0, %g1, %o0 ! advance buf ptr
+cptbl: CSUM_LASTCHUNK(%o0, 0x68, %o2, %g2, %g3, %g4, %g5)
+ CSUM_LASTCHUNK(%o0, 0x58, %o2, %g2, %g3, %g4, %g5)
+ CSUM_LASTCHUNK(%o0, 0x48, %o2, %g2, %g3, %g4, %g5)
+ CSUM_LASTCHUNK(%o0, 0x38, %o2, %g2, %g3, %g4, %g5)
+ CSUM_LASTCHUNK(%o0, 0x28, %o2, %g2, %g3, %g4, %g5)
+ CSUM_LASTCHUNK(%o0, 0x18, %o2, %g2, %g3, %g4, %g5)
+ CSUM_LASTCHUNK(%o0, 0x08, %o2, %g2, %g3, %g4, %g5)
+ addx %g0, %o2, %o2 ! fetch final carry
+ andcc %o1, 0xf, %g0 ! anything left at all?
+cpte: bne csum_partial_end_cruft ! yep, handle it
+ andcc %o1, 8, %g0 ! check how much
+cpout: retl ! get outta here
+ mov %o2, %o0 ! return computed csum
+
+ .globl C_LABEL(__csum_partial_copy_start), C_LABEL(__csum_partial_copy_end)
+C_LABEL(__csum_partial_copy_start):
+
+#define EX(x,y,a,b,z) \
+98: x,y; \
+ .section .fixup,z##alloc,z##execinstr; \
+ .align 4; \
+99: ba 30f; \
+ a, b, %o3; \
+ .section __ex_table,z##alloc; \
+ .align 4; \
+ .word 98b, 99b; \
+ .text; \
+ .align 4
+
+#define EX2(x,y,z) \
+98: x,y; \
+ .section __ex_table,z##alloc; \
+ .align 4; \
+ .word 98b, 30f; \
+ .text; \
+ .align 4
+
+#define EX3(x,y,z) \
+98: x,y; \
+ .section __ex_table,z##alloc; \
+ .align 4; \
+ .word 98b, 96f; \
+ .text; \
+ .align 4
+
+#define EXT(start,end,handler,z) \
+ .section __ex_table,z##alloc; \
+ .align 4; \
+ .word start, 0, end, handler; \
+ .text; \
+ .align 4
+
+ /* This aligned version executes typically in 8.5 superscalar cycles, this
+ * is the best I can do. I say 8.5 because the final add will pair with
+ * the next ldd in the main unrolled loop. Thus the pipe is always full.
+ * If you change these macros (including order of instructions),
+ * please check the fixup code below as well.
+ */
+#define CSUMCOPY_BIGCHUNK_ALIGNED(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7) \
+ ldd [src + off + 0x00], t0; \
+ ldd [src + off + 0x08], t2; \
+ addxcc t0, sum, sum; \
+ ldd [src + off + 0x10], t4; \
+ addxcc t1, sum, sum; \
+ ldd [src + off + 0x18], t6; \
+ addxcc t2, sum, sum; \
+ std t0, [dst + off + 0x00]; \
+ addxcc t3, sum, sum; \
+ std t2, [dst + off + 0x08]; \
+ addxcc t4, sum, sum; \
+ std t4, [dst + off + 0x10]; \
+ addxcc t5, sum, sum; \
+ std t6, [dst + off + 0x18]; \
+ addxcc t6, sum, sum; \
+ addxcc t7, sum, sum;
+
+ /* 12 superscalar cycles seems to be the limit for this case,
+ * because of this we thus do all the ldd's together to get
+ * Viking MXCC into streaming mode. Ho hum...
+ */
+#define CSUMCOPY_BIGCHUNK(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7) \
+ ldd [src + off + 0x00], t0; \
+ ldd [src + off + 0x08], t2; \
+ ldd [src + off + 0x10], t4; \
+ ldd [src + off + 0x18], t6; \
+ st t0, [dst + off + 0x00]; \
+ addxcc t0, sum, sum; \
+ st t1, [dst + off + 0x04]; \
+ addxcc t1, sum, sum; \
+ st t2, [dst + off + 0x08]; \
+ addxcc t2, sum, sum; \
+ st t3, [dst + off + 0x0c]; \
+ addxcc t3, sum, sum; \
+ st t4, [dst + off + 0x10]; \
+ addxcc t4, sum, sum; \
+ st t5, [dst + off + 0x14]; \
+ addxcc t5, sum, sum; \
+ st t6, [dst + off + 0x18]; \
+ addxcc t6, sum, sum; \
+ st t7, [dst + off + 0x1c]; \
+ addxcc t7, sum, sum;
+
+ /* Yuck, 6 superscalar cycles... */
+#define CSUMCOPY_LASTCHUNK(src, dst, sum, off, t0, t1, t2, t3) \
+ ldd [src - off - 0x08], t0; \
+ ldd [src - off - 0x00], t2; \
+ addxcc t0, sum, sum; \
+ st t0, [dst - off - 0x08]; \
+ addxcc t1, sum, sum; \
+ st t1, [dst - off - 0x04]; \
+ addxcc t2, sum, sum; \
+ st t2, [dst - off - 0x00]; \
+ addxcc t3, sum, sum; \
+ st t3, [dst - off + 0x04];
+
+ /* Handle the end cruft code out of band for better cache patterns. */
+cc_end_cruft:
+ be 1f
+ andcc %o3, 4, %g0
+ EX(ldd [%o0 + 0x00], %g2, and %o3, 0xf,#)
+ add %o1, 8, %o1
+ addcc %g2, %g7, %g7
+ add %o0, 8, %o0
+ addxcc %g3, %g7, %g7
+ EX2(st %g2, [%o1 - 0x08],#)
+ addx %g0, %g7, %g7
+ andcc %o3, 4, %g0
+ EX2(st %g3, [%o1 - 0x04],#)
+1: be 1f
+ andcc %o3, 3, %o3
+ EX(ld [%o0 + 0x00], %g2, add %o3, 4,#)
+ add %o1, 4, %o1
+ addcc %g2, %g7, %g7
+ EX2(st %g2, [%o1 - 0x04],#)
+ addx %g0, %g7, %g7
+ andcc %o3, 3, %g0
+ add %o0, 4, %o0
+1: be 1f
+ addcc %o3, -1, %g0
+ bne 2f
+ subcc %o3, 2, %o3
+ b 4f
+ or %g0, %g0, %o4
+2: EX(lduh [%o0 + 0x00], %o4, add %o3, 2,#)
+ add %o0, 2, %o0
+ EX2(sth %o4, [%o1 + 0x00],#)
+ be 6f
+ add %o1, 2, %o1
+ sll %o4, 16, %o4
+4: EX(ldub [%o0 + 0x00], %o5, add %g0, 1,#)
+ EX2(stb %o5, [%o1 + 0x00],#)
+ sll %o5, 8, %o5
+ or %o5, %o4, %o4
+6: addcc %o4, %g7, %g7
+1: retl
+ addx %g0, %g7, %o0
+
+ /* Also, handle the alignment code out of band. */
+cc_dword_align:
+ cmp %g1, 6
+ bl,a ccte
+ andcc %g1, 0xf, %o3
+ andcc %o0, 0x1, %g0
+ bne ccslow
+ andcc %o0, 0x2, %g0
+ be 1f
+ andcc %o0, 0x4, %g0
+ EX(lduh [%o0 + 0x00], %g4, add %g1, 0,#)
+ sub %g1, 2, %g1
+ EX2(sth %g4, [%o1 + 0x00],#)
+ add %o0, 2, %o0
+ sll %g4, 16, %g4
+ addcc %g4, %g7, %g7
+ add %o1, 2, %o1
+ srl %g7, 16, %g3
+ addx %g0, %g3, %g4
+ sll %g7, 16, %g7
+ sll %g4, 16, %g3
+ srl %g7, 16, %g7
+ andcc %o0, 0x4, %g0
+ or %g3, %g7, %g7
+1: be 3f
+ andcc %g1, 0xffffff80, %g0
+ EX(ld [%o0 + 0x00], %g4, add %g1, 0,#)
+ sub %g1, 4, %g1
+ EX2(st %g4, [%o1 + 0x00],#)
+ add %o0, 4, %o0
+ addcc %g4, %g7, %g7
+ add %o1, 4, %o1
+ addx %g0, %g7, %g7
+ b 3f
+ andcc %g1, 0xffffff80, %g0
+
+ /* Sun, you just can't beat me, you just can't. Stop trying,
+ * give up. I'm serious, I am going to kick the living shit
+ * out of you, game over, lights out.
+ */
+ .align 8
+ .globl C_LABEL(__csum_partial_copy_sparc_generic)
+C_LABEL(__csum_partial_copy_sparc_generic):
+ /* %o0=src, %o1=dest, %g1=len, %g7=sum */
+ xor %o0, %o1, %o4 ! get changing bits
+ andcc %o4, 3, %g0 ! check for mismatched alignment
+ bne ccslow ! better this than unaligned/fixups
+ andcc %o0, 7, %g0 ! need to align things?
+ bne cc_dword_align ! yes, we check for short lengths there
+ andcc %g1, 0xffffff80, %g0 ! can we use unrolled loop?
+3: be 3f ! nope, less than one loop remains
+ andcc %o1, 4, %g0 ! dest aligned on 4 or 8 byte boundary?
+ be ccdbl + 4 ! 8 byte aligned, kick ass
+5: CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x00,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+ CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+ CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+ CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x60,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+10: EXT(5b, 10b, 20f,#) ! note for exception handling
+ sub %g1, 128, %g1 ! detract from length
+ addx %g0, %g7, %g7 ! add in last carry bit
+ andcc %g1, 0xffffff80, %g0 ! more to csum?
+ add %o0, 128, %o0 ! advance src ptr
+ bne 5b ! we did not go negative, continue looping
+ add %o1, 128, %o1 ! advance dest ptr
+3: andcc %g1, 0x70, %o2 ! can use table?
+ccmerge:be ccte ! nope, go and check for end cruft
+ andcc %g1, 0xf, %o3 ! get low bits of length (clears carry btw)
+ srl %o2, 1, %o4 ! begin negative offset computation
+ sethi %hi(12f), %o5 ! set up table ptr end
+ add %o0, %o2, %o0 ! advance src ptr
+ sub %o5, %o4, %o5 ! continue table calculation
+ sll %o2, 1, %g2 ! constant multiplies are fun...
+ sub %o5, %g2, %o5 ! some more adjustments
+ jmp %o5 + %lo(12f) ! jump into it, duff style, wheee...
+ add %o1, %o2, %o1 ! advance dest ptr (carry is clear btw)
+cctbl: CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x68,%g2,%g3,%g4,%g5)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x58,%g2,%g3,%g4,%g5)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x48,%g2,%g3,%g4,%g5)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x38,%g2,%g3,%g4,%g5)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x28,%g2,%g3,%g4,%g5)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x18,%g2,%g3,%g4,%g5)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x08,%g2,%g3,%g4,%g5)
+12: EXT(cctbl, 12b, 22f,#) ! note for exception table handling
+ addx %g0, %g7, %g7
+ andcc %o3, 0xf, %g0 ! check for low bits set
+ccte: bne cc_end_cruft ! something left, handle it out of band
+ andcc %o3, 8, %g0 ! begin checks for that code
+ retl ! return
+ mov %g7, %o0 ! give em the computed checksum
+ccdbl: CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x00,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+ CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+ CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+ CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x60,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+11: EXT(ccdbl, 11b, 21f,#) ! note for exception table handling
+ sub %g1, 128, %g1 ! detract from length
+ addx %g0, %g7, %g7 ! add in last carry bit
+ andcc %g1, 0xffffff80, %g0 ! more to csum?
+ add %o0, 128, %o0 ! advance src ptr
+ bne ccdbl ! we did not go negative, continue looping
+ add %o1, 128, %o1 ! advance dest ptr
+ b ccmerge ! finish it off, above
+ andcc %g1, 0x70, %o2 ! can use table? (clears carry btw)
+
+ccslow: cmp %g1, 0
+ mov 0, %g5
+ bleu 4f
+ andcc %o0, 1, %o5
+ be,a 1f
+ srl %g1, 1, %g4
+ sub %g1, 1, %g1
+ EX(ldub [%o0], %g5, add %g1, 1,#)
+ add %o0, 1, %o0
+ EX2(stb %g5, [%o1],#)
+ srl %g1, 1, %g4
+ add %o1, 1, %o1
+1: cmp %g4, 0
+ be,a 3f
+ andcc %g1, 1, %g0
+ andcc %o0, 2, %g0
+ be,a 1f
+ srl %g4, 1, %g4
+ EX(lduh [%o0], %o4, add %g1, 0,#)
+ sub %g1, 2, %g1
+ srl %o4, 8, %g2
+ sub %g4, 1, %g4
+ EX2(stb %g2, [%o1],#)
+ add %o4, %g5, %g5
+ EX2(stb %o4, [%o1 + 1],#)
+ add %o0, 2, %o0
+ srl %g4, 1, %g4
+ add %o1, 2, %o1
+1: cmp %g4, 0
+ be,a 2f
+ andcc %g1, 2, %g0
+ EX3(ld [%o0], %o4,#)
+5: srl %o4, 24, %g2
+ srl %o4, 16, %g3
+ EX2(stb %g2, [%o1],#)
+ srl %o4, 8, %g2
+ EX2(stb %g3, [%o1 + 1],#)
+ add %o0, 4, %o0
+ EX2(stb %g2, [%o1 + 2],#)
+ addcc %o4, %g5, %g5
+ EX2(stb %o4, [%o1 + 3],#)
+ addx %g5, %g0, %g5 ! I am now to lazy to optimize this (question it
+ add %o1, 4, %o1 ! is worthy). Maybe some day - with the sll/srl
+ subcc %g4, 1, %g4 ! tricks
+ bne,a 5b
+ EX3(ld [%o0], %o4,#)
+ sll %g5, 16, %g2
+ srl %g5, 16, %g5
+ srl %g2, 16, %g2
+ andcc %g1, 2, %g0
+ add %g2, %g5, %g5
+2: be,a 3f
+ andcc %g1, 1, %g0
+ EX(lduh [%o0], %o4, and %g1, 3,#)
+ andcc %g1, 1, %g0
+ srl %o4, 8, %g2
+ add %o0, 2, %o0
+ EX2(stb %g2, [%o1],#)
+ add %g5, %o4, %g5
+ EX2(stb %o4, [%o1 + 1],#)
+ add %o1, 2, %o1
+3: be,a 1f
+ sll %g5, 16, %o4
+ EX(ldub [%o0], %g2, add %g0, 1,#)
+ sll %g2, 8, %o4
+ EX2(stb %g2, [%o1],#)
+ add %g5, %o4, %g5
+ sll %g5, 16, %o4
+1: addcc %o4, %g5, %g5
+ srl %g5, 16, %o4
+ addx %g0, %o4, %g5
+ orcc %o5, %g0, %g0
+ be 4f
+ srl %g5, 8, %o4
+ and %g5, 0xff, %g2
+ and %o4, 0xff, %o4
+ sll %g2, 8, %g2
+ or %g2, %o4, %g5
+4: addcc %g7, %g5, %g7
+ retl
+ addx %g0, %g7, %o0
+C_LABEL(__csum_partial_copy_end):
+
+/* We do these strange calculations for the csum_*_from_user case only, ie.
+ * we only bother with faults on loads... */
+
+/* o2 = ((g2%20)&3)*8
+ * o3 = g1 - (g2/20)*32 - o2 */
+20:
+ cmp %g2, 20
+ blu,a 1f
+ and %g2, 3, %o2
+ sub %g1, 32, %g1
+ b 20b
+ sub %g2, 20, %g2
+1:
+ sll %o2, 3, %o2
+ b 31f
+ sub %g1, %o2, %o3
+
+/* o2 = (!(g2 & 15) ? 0 : (((g2 & 15) + 1) & ~1)*8)
+ * o3 = g1 - (g2/16)*32 - o2 */
+21:
+ andcc %g2, 15, %o3
+ srl %g2, 4, %g2
+ be,a 1f
+ clr %o2
+ add %o3, 1, %o3
+ and %o3, 14, %o3
+ sll %o3, 3, %o2
+1:
+ sll %g2, 5, %g2
+ sub %g1, %g2, %o3
+ b 31f
+ sub %o3, %o2, %o3
+
+/* o0 += (g2/10)*16 - 0x70
+ * 01 += (g2/10)*16 - 0x70
+ * o2 = (g2 % 10) ? 8 : 0
+ * o3 += 0x70 - (g2/10)*16 - o2 */
+22:
+ cmp %g2, 10
+ blu,a 1f
+ sub %o0, 0x70, %o0
+ add %o0, 16, %o0
+ add %o1, 16, %o1
+ sub %o3, 16, %o3
+ b 22b
+ sub %g2, 10, %g2
+1:
+ sub %o1, 0x70, %o1
+ add %o3, 0x70, %o3
+ clr %o2
+ tst %g2
+ bne,a 1f
+ mov 8, %o2
+1:
+ b 31f
+ sub %o3, %o2, %o3
+96:
+ and %g1, 3, %g1
+ sll %g4, 2, %g4
+ add %g1, %g4, %o3
+30:
+/* %o1 is dst
+ * %o3 is # bytes to zero out
+ * %o4 is faulting address
+ * %o5 is %pc where fault occurred */
+ clr %o2
+31:
+/* %o0 is src
+ * %o1 is dst
+ * %o2 is # of bytes to copy from src to dst
+ * %o3 is # bytes to zero out
+ * %o4 is faulting address
+ * %o5 is %pc where fault occurred */
+ save %sp, -104, %sp
+ mov %i5, %o0
+ mov %i7, %o1
+ mov %i4, %o2
+ call C_LABEL(lookup_fault)
+ mov %g7, %i4
+ cmp %o0, 2
+ bne 1f
+ add %g0, -EFAULT, %i5
+ tst %i2
+ be 2f
+ mov %i0, %o1
+ mov %i1, %o0
+5:
+ call C_LABEL(__memcpy)
+ mov %i2, %o2
+ tst %o0
+ bne,a 2f
+ add %i3, %i2, %i3
+ add %i1, %i2, %i1
+2:
+ mov %i1, %o0
+6:
+ call C_LABEL(__bzero)
+ mov %i3, %o1
+1:
+ ld [%sp + 168], %o2 ! struct_ptr of parent
+ st %i5, [%o2]
+ ret
+ restore
+
+ .section __ex_table,#alloc
+ .align 4
+ .word 5b,2
+ .word 6b,2
diff --git a/pfinet/linux-src/arch/sparc64/lib/checksum.S b/pfinet/linux-src/arch/sparc64/lib/checksum.S
new file mode 100644
index 00000000..c0f4356c
--- /dev/null
+++ b/pfinet/linux-src/arch/sparc64/lib/checksum.S
@@ -0,0 +1,277 @@
+/* checksum.S: Sparc V9 optimized checksum code.
+ *
+ * Copyright(C) 1995 Linus Torvalds
+ * Copyright(C) 1995 Miguel de Icaza
+ * Copyright(C) 1996 David S. Miller
+ * Copyright(C) 1997 Jakub Jelinek
+ *
+ * derived from:
+ * Linux/Alpha checksum c-code
+ * Linux/ix86 inline checksum assembly
+ * RFC1071 Computing the Internet Checksum (esp. Jacobsons m68k code)
+ * David Mosberger-Tang for optimized reference c-code
+ * BSD4.4 portable checksum routine
+ */
+
+#include <asm/errno.h>
+#include <asm/head.h>
+#include <asm/ptrace.h>
+#include <asm/asi.h>
+#include <asm/page.h>
+
+ /* The problem with the "add with carry" instructions on Ultra
+ * are two fold. Firstly, they cannot pair with jack shit,
+ * and also they only add in the 32-bit carry condition bit
+ * into the accumulated sum. The following is much better.
+ * For larger chunks we use VIS code, which is faster ;)
+ */
+
+#define src o0
+#define dst o1
+#define len o2
+#define sum o3
+
+ .text
+ /* I think I have an erection... Once _AGAIN_ the SunSoft
+ * engineers are caught asleep at the keyboard, tsk tsk...
+ */
+
+#define CSUMCOPY_LASTCHUNK(off, t0, t1) \
+ ldxa [%src - off - 0x08] %asi, t0; \
+ ldxa [%src - off - 0x00] %asi, t1; \
+ nop; nop; \
+ addcc t0, %sum, %sum; \
+ stw t0, [%dst - off - 0x04]; \
+ srlx t0, 32, t0; \
+ bcc,pt %xcc, 51f; \
+ stw t0, [%dst - off - 0x08]; \
+ add %sum, 1, %sum; \
+51: addcc t1, %sum, %sum; \
+ stw t1, [%dst - off + 0x04]; \
+ srlx t1, 32, t1; \
+ bcc,pt %xcc, 52f; \
+ stw t1, [%dst - off - 0x00]; \
+ add %sum, 1, %sum; \
+52:
+
+cpc_start:
+cc_end_cruft:
+ andcc %g7, 8, %g0 ! IEU1 Group
+ be,pn %icc, 1f ! CTI
+ and %g7, 4, %g5 ! IEU0
+ ldxa [%src + 0x00] %asi, %g2 ! Load Group
+ add %dst, 8, %dst ! IEU0
+ add %src, 8, %src ! IEU1
+ addcc %g2, %sum, %sum ! IEU1 Group + 2 bubbles
+ stw %g2, [%dst - 0x04] ! Store
+ srlx %g2, 32, %g2 ! IEU0
+ bcc,pt %xcc, 1f ! CTI Group
+ stw %g2, [%dst - 0x08] ! Store
+ add %sum, 1, %sum ! IEU0
+1: brz,pt %g5, 1f ! CTI Group
+ clr %g2 ! IEU0
+ lduwa [%src + 0x00] %asi, %g2 ! Load
+ add %dst, 4, %dst ! IEU0 Group
+ add %src, 4, %src ! IEU1
+ stw %g2, [%dst - 0x04] ! Store Group + 2 bubbles
+ sllx %g2, 32, %g2 ! IEU0
+1: andcc %g7, 2, %g0 ! IEU1
+ be,pn %icc, 1f ! CTI Group
+ clr %o4 ! IEU1
+ lduha [%src + 0x00] %asi, %o4 ! Load
+ add %src, 2, %src ! IEU0 Group
+ add %dst, 2, %dst ! IEU1
+ sth %o4, [%dst - 0x2] ! Store Group + 2 bubbles
+ sll %o4, 16, %o4 ! IEU0
+1: andcc %g7, 1, %g0 ! IEU1
+ be,pn %icc, 1f ! CTI Group
+ clr %o5 ! IEU0
+ lduba [%src + 0x00] %asi, %o5 ! Load
+ stb %o5, [%dst + 0x00] ! Store Group + 2 bubbles
+ sll %o5, 8, %o5 ! IEU0
+1: or %g2, %o4, %o4 ! IEU1
+ or %o5, %o4, %o4 ! IEU0 Group
+ addcc %o4, %sum, %sum ! IEU1
+ bcc,pt %xcc, ccfold ! CTI
+ sethi %uhi(PAGE_OFFSET), %g4 ! IEU0 Group
+ b,pt %xcc, ccfold ! CTI
+ add %sum, 1, %sum ! IEU1
+
+cc_fixit:
+ cmp %len, 6 ! IEU1 Group
+ bl,a,pn %icc, ccte ! CTI
+ andcc %len, 0xf, %g7 ! IEU1 Group
+ andcc %src, 2, %g0 ! IEU1 Group
+ be,pn %icc, 1f ! CTI
+ andcc %src, 0x4, %g0 ! IEU1 Group
+ lduha [%src + 0x00] %asi, %g4 ! Load
+ sub %len, 2, %len ! IEU0
+ add %src, 2, %src ! IEU0 Group
+ add %dst, 2, %dst ! IEU1
+ sll %g4, 16, %g3 ! IEU0 Group + 1 bubble
+ addcc %g3, %sum, %sum ! IEU1
+ bcc,pt %xcc, 0f ! CTI
+ srl %sum, 16, %g3 ! IEU0 Group
+ add %g3, 1, %g3 ! IEU0 4 clocks (mispredict)
+0: andcc %src, 0x4, %g0 ! IEU1 Group
+ sth %g4, [%dst - 0x2] ! Store
+ sll %sum, 16, %sum ! IEU0
+ sll %g3, 16, %g3 ! IEU0 Group
+ srl %sum, 16, %sum ! IEU0 Group
+ or %g3, %sum, %sum ! IEU0 Group (regdep)
+1: be,pt %icc, ccmerge ! CTI
+ andcc %len, 0xf0, %g1 ! IEU1
+ lduwa [%src + 0x00] %asi, %g4 ! Load Group
+ sub %len, 4, %len ! IEU0
+ add %src, 4, %src ! IEU1
+ add %dst, 4, %dst ! IEU0 Group
+ addcc %g4, %sum, %sum ! IEU1 Group + 1 bubble
+ stw %g4, [%dst - 0x4] ! Store
+ bcc,pt %xcc, ccmerge ! CTI
+ andcc %len, 0xf0, %g1 ! IEU1 Group
+ b,pt %xcc, ccmerge ! CTI 4 clocks (mispredict)
+ add %sum, 1, %sum ! IEU0
+
+ .align 32
+ .globl csum_partial_copy_sparc64
+csum_partial_copy_sparc64: /* %o0=src, %o1=dest, %o2=len, %o3=sum */
+ xorcc %src, %dst, %o4 ! IEU1 Group
+ srl %sum, 0, %sum ! IEU0
+ andcc %o4, 3, %g0 ! IEU1 Group
+ srl %len, 0, %len ! IEU0
+ bne,pn %icc, ccslow ! CTI
+ andcc %src, 1, %g0 ! IEU1 Group
+ bne,pn %icc, ccslow ! CTI
+ cmp %len, 256 ! IEU1 Group
+ bgeu,pt %icc, csum_partial_copy_vis ! CTI
+ andcc %src, 7, %g0 ! IEU1 Group
+ bne,pn %icc, cc_fixit ! CTI
+ andcc %len, 0xf0, %g1 ! IEU1 Group
+ccmerge:be,pn %icc, ccte ! CTI
+ andcc %len, 0xf, %g7 ! IEU1 Group
+ sll %g1, 2, %o4 ! IEU0
+13: sethi %hi(12f), %o5 ! IEU0 Group
+ add %src, %g1, %src ! IEU1
+ sub %o5, %o4, %o5 ! IEU0 Group
+ jmpl %o5 + %lo(12f), %g0 ! CTI Group brk forced
+ add %dst, %g1, %dst ! IEU0 Group
+cctbl: CSUMCOPY_LASTCHUNK(0xe8,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(0xd8,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(0xc8,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(0xb8,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(0xa8,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(0x98,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(0x88,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(0x78,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(0x68,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(0x58,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(0x48,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(0x38,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(0x28,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(0x18,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(0x08,%g2,%g3)
+12:
+ andcc %len, 0xf, %g7 ! IEU1 Group
+ccte: bne,pn %icc, cc_end_cruft ! CTI
+ sethi %uhi(PAGE_OFFSET), %g4 ! IEU0
+ccfold: sllx %sum, 32, %o0 ! IEU0 Group
+ addcc %sum, %o0, %o0 ! IEU1 Group (regdep)
+ srlx %o0, 32, %o0 ! IEU0 Group (regdep)
+ bcs,a,pn %xcc, 1f ! CTI
+ add %o0, 1, %o0 ! IEU1 4 clocks (mispredict)
+1: retl ! CTI Group brk forced
+ sllx %g4, 32, %g4 ! IEU0 Group
+
+ccslow: mov 0, %g5
+ brlez,pn %len, 4f
+ andcc %src, 1, %o5
+ be,a,pt %icc, 1f
+ srl %len, 1, %g7
+ sub %len, 1, %len
+ lduba [%src] %asi, %g5
+ add %src, 1, %src
+ stb %g5, [%dst]
+ srl %len, 1, %g7
+ add %dst, 1, %dst
+1: brz,a,pn %g7, 3f
+ andcc %len, 1, %g0
+ andcc %src, 2, %g0
+ be,a,pt %icc, 1f
+ srl %g7, 1, %g7
+ lduha [%src] %asi, %o4
+ sub %len, 2, %len
+ srl %o4, 8, %g2
+ sub %g7, 1, %g7
+ stb %g2, [%dst]
+ add %o4, %g5, %g5
+ stb %o4, [%dst + 1]
+ add %src, 2, %src
+ srl %g7, 1, %g7
+ add %dst, 2, %dst
+1: brz,a,pn %g7, 2f
+ andcc %len, 2, %g0
+ lduwa [%src] %asi, %o4
+5: srl %o4, 24, %g2
+ srl %o4, 16, %g3
+ stb %g2, [%dst]
+ srl %o4, 8, %g2
+ stb %g3, [%dst + 1]
+ add %src, 4, %src
+ stb %g2, [%dst + 2]
+ addcc %o4, %g5, %g5
+ stb %o4, [%dst + 3]
+ addc %g5, %g0, %g5
+ add %dst, 4, %dst
+ subcc %g7, 1, %g7
+ bne,a,pt %icc, 5b
+ lduwa [%src] %asi, %o4
+ sll %g5, 16, %g2
+ srl %g5, 16, %g5
+ srl %g2, 16, %g2
+ andcc %len, 2, %g0
+ add %g2, %g5, %g5
+2: be,a,pt %icc, 3f
+ andcc %len, 1, %g0
+ lduha [%src] %asi, %o4
+ andcc %len, 1, %g0
+ srl %o4, 8, %g2
+ add %src, 2, %src
+ stb %g2, [%dst]
+ add %g5, %o4, %g5
+ stb %o4, [%dst + 1]
+ add %dst, 2, %dst
+3: be,a,pt %icc, 1f
+ sll %g5, 16, %o4
+ lduba [%src] %asi, %g2
+ sll %g2, 8, %o4
+ stb %g2, [%dst]
+ add %g5, %o4, %g5
+ sll %g5, 16, %o4
+1: addcc %o4, %g5, %g5
+ srl %g5, 16, %o4
+ addc %g0, %o4, %g5
+ brz,pt %o5, 4f
+ srl %g5, 8, %o4
+ and %g5, 0xff, %g2
+ and %o4, 0xff, %o4
+ sll %g2, 8, %g2
+ or %g2, %o4, %g5
+4: addcc %sum, %g5, %sum
+ addc %g0, %sum, %o0
+ retl
+ srl %o0, 0, %o0
+cpc_end:
+
+ .globl cpc_handler
+cpc_handler:
+ ldx [%sp + 0x7ff + 128], %g1
+ sub %g0, EFAULT, %g2
+ brnz,a,pt %g1, 1f
+ st %g2, [%g1]
+1: sethi %uhi(PAGE_OFFSET), %g4
+ retl
+ sllx %g4, 32, %g4
+
+ .section __ex_table
+ .align 4
+ .word cpc_start, 0, cpc_end, cpc_handler
diff --git a/pfinet/linux-src/include/asm-alpha/checksum.h b/pfinet/linux-src/include/asm-alpha/checksum.h
new file mode 100644
index 00000000..2f6b82e6
--- /dev/null
+++ b/pfinet/linux-src/include/asm-alpha/checksum.h
@@ -0,0 +1,90 @@
+#ifndef _ALPHA_CHECKSUM_H
+#define _ALPHA_CHECKSUM_H
+
+
+/*
+ * This is a version of ip_compute_csum() optimized for IP headers,
+ * which always checksum on 4 octet boundaries.
+ */
+extern unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl);
+
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented
+ */
+extern unsigned short int csum_tcpudp_magic(unsigned long saddr,
+ unsigned long daddr,
+ unsigned short len,
+ unsigned short proto,
+ unsigned int sum);
+
+unsigned int csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr,
+ unsigned short len, unsigned short proto,
+ unsigned int sum);
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+extern unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum);
+
+/*
+ * the same as csum_partial, but copies from src while it
+ * checksums
+ *
+ * here even more important to align src and dst on a 32-bit (or even
+ * better 64-bit) boundary
+ */
+unsigned int csum_partial_copy(const char *src, char *dst, int len, unsigned int sum);
+
+/*
+ * the same as csum_partial, but copies from user space (but on the alpha
+ * we have just one address space, so this is identical to the above)
+ *
+ * this is obsolete and will go away.
+ */
+#define csum_partial_copy_fromuser csum_partial_copy
+
+/*
+ * this is a new version of the above that records errors it finds in *errp,
+ * but continues and zeros the rest of the buffer.
+ */
+unsigned int csum_partial_copy_from_user(const char *src, char *dst, int len, unsigned int sum, int *errp);
+
+unsigned int csum_partial_copy_nocheck(const char *src, char *dst, int len, unsigned int sum);
+
+
+/*
+ * this routine is used for miscellaneous IP-like checksums, mainly
+ * in icmp.c
+ */
+
+extern unsigned short ip_compute_csum(unsigned char * buff, int len);
+
+/*
+ * Fold a partial checksum without adding pseudo headers
+ */
+
+static inline unsigned short csum_fold(unsigned int sum)
+{
+ sum = (sum & 0xffff) + (sum >> 16);
+ sum = (sum & 0xffff) + (sum >> 16);
+ return ~sum;
+}
+
+#define _HAVE_ARCH_IPV6_CSUM
+extern unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
+ struct in6_addr *daddr,
+ __u16 len,
+ unsigned short proto,
+ unsigned int sum);
+
+#endif
diff --git a/pfinet/linux-src/include/asm-arm/checksum.h b/pfinet/linux-src/include/asm-arm/checksum.h
new file mode 100644
index 00000000..2323bb5d
--- /dev/null
+++ b/pfinet/linux-src/include/asm-arm/checksum.h
@@ -0,0 +1,162 @@
+/*
+ * linux/include/asm-arm/checksum.h
+ *
+ * IP checksum routines
+ *
+ * Copyright (C) Original authors of ../asm-i386/checksum.h
+ * Copyright (C) 1996,1997,1998 Russell King
+ */
+#ifndef __ASM_ARM_CHECKSUM_H
+#define __ASM_ARM_CHECKSUM_H
+
+#ifndef __ASM_ARM_SEGMENT_H
+#include <asm/segment.h>
+#endif
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum);
+
+/*
+ * the same as csum_partial, but copies from src while it
+ * checksums, and handles user-space pointer exceptions correctly, when needed.
+ *
+ * here even more important to align src and dst on a 32-bit (or even
+ * better 64-bit) boundary
+ */
+
+unsigned int
+csum_partial_copy_nocheck(const char *src, char *dst, int len, int sum);
+
+unsigned int
+csum_partial_copy_from_user(const char *src, char *dst, int len, int sum, int *err_ptr);
+
+#if 0
+/*
+ * This combination is currently not used, but possible:
+ */
+unsigned int
+csum_partial_copy_to_user(const char *src, char *dst, int len, int sum, int *err_ptr);
+#endif
+
+/*
+ * These are the old (and unsafe) way of doing checksums, a warning message will be
+ * printed if they are used and an exception occurs.
+ *
+ * these functions should go away after some time.
+ */
+#define csum_partial_copy_fromuser csum_partial_copy
+unsigned int
+csum_partial_copy(const char *src, char *dst, int len, int sum);
+
+/*
+ * This is a version of ip_compute_csum() optimized for IP headers,
+ * which always checksum on 4 octet boundaries.
+ *
+ * Converted and optimised for ARM by R. M. King.
+ *
+ * Note: the order that the LDM registers are loaded with respect to
+ * the adc's doesn't matter.
+ */
+static inline unsigned short
+ip_fast_csum(unsigned char * iph, unsigned int ihl)
+{
+ unsigned int sum, tmp1;
+
+ __asm__ __volatile__("
+ sub %2, %2, #5
+ ldr %0, [%1], #4
+ ldr %3, [%1], #4
+ adds %0, %0, %3
+ ldr %3, [%1], #4
+ adcs %0, %0, %3
+ ldr %3, [%1], #4
+ adcs %0, %0, %3
+1: ldr %3, [%1], #4
+ adcs %0, %0, %3
+ tst %2, #15
+ subne %2, %2, #1
+ bne 1b
+ adc %0, %0, #0
+ adds %0, %0, %0, lsl #16
+ addcs %0, %0, #0x10000
+ mvn %0, %0
+ mov %0, %0, lsr #16
+ "
+ : "=&r" (sum), "=&r" (iph), "=&r" (ihl), "=&r" (tmp1)
+ : "1" (iph), "2" (ihl));
+ return(sum);
+}
+
+/*
+ * Fold a partial checksum without adding pseudo headers
+ */
+static inline unsigned int
+csum_fold(unsigned int sum)
+{
+ __asm__("
+ adds %0, %0, %0, lsl #16
+ addcs %0, %0, #0x10000"
+ : "=r" (sum)
+ : "0" (sum));
+ return (~sum) >> 16;
+}
+
+static inline unsigned long
+csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr, unsigned short len,
+ unsigned short proto, unsigned int sum)
+{
+ __asm__("
+ adds %0, %0, %1
+ adcs %0, %0, %2
+ adcs %0, %0, %3
+ adc %0, %0, #0"
+ : "=&r"(sum)
+ : "r" (daddr), "r" (saddr), "r" ((ntohs(len)<<16)+proto*256), "0" (sum));
+ return sum;
+}
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented
+ */
+static inline unsigned short int
+csum_tcpudp_magic(unsigned long saddr, unsigned long daddr, unsigned short len,
+ unsigned short proto, unsigned int sum)
+{
+ return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
+}
+
+
+/*
+ * this routine is used for miscellaneous IP-like checksums, mainly
+ * in icmp.c
+ */
+static inline unsigned short
+ip_compute_csum(unsigned char * buff, int len)
+{
+ return csum_fold(csum_partial(buff, len, 0));
+}
+
+#define _HAVE_ARCH_IPV6_CSUM
+extern unsigned long
+__csum_ipv6_magic(struct in6_addr *saddr, struct in6_addr *daddr, __u32 len,
+ __u32 proto, unsigned int sum);
+
+extern __inline__ unsigned short int
+csum_ipv6_magic(struct in6_addr *saddr, struct in6_addr *daddr, __u16 len,
+ unsigned short proto, unsigned int sum)
+{
+ return csum_fold(__csum_ipv6_magic(saddr, daddr, htonl((__u32)len),
+ htonl(proto), sum));
+}
+#endif
diff --git a/pfinet/linux-src/include/asm-i386/checksum.h b/pfinet/linux-src/include/asm-i386/checksum.h
new file mode 100644
index 00000000..add89590
--- /dev/null
+++ b/pfinet/linux-src/include/asm-i386/checksum.h
@@ -0,0 +1,211 @@
+#ifndef _I386_CHECKSUM_H
+#define _I386_CHECKSUM_H
+
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+asmlinkage unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum);
+
+/*
+ * the same as csum_partial, but copies from src while it
+ * checksums, and handles user-space pointer exceptions correctly, when needed.
+ *
+ * here even more important to align src and dst on a 32-bit (or even
+ * better 64-bit) boundary
+ */
+
+asmlinkage unsigned int csum_partial_copy_generic( const char *src, char *dst, int len, int sum,
+ int *src_err_ptr, int *dst_err_ptr);
+
+/*
+ * Note: when you get a NULL pointer exception here this means someone
+ * passed in an incorrect kernel address to one of these functions.
+ *
+ * If you use these functions directly please don't forget the
+ * verify_area().
+ */
+extern __inline__
+unsigned int csum_partial_copy_nocheck ( const char *src, char *dst,
+ int len, int sum)
+{
+ return csum_partial_copy_generic ( src, dst, len, sum, NULL, NULL);
+}
+
+extern __inline__
+unsigned int csum_partial_copy_from_user ( const char *src, char *dst,
+ int len, int sum, int *err_ptr)
+{
+ return csum_partial_copy_generic ( src, dst, len, sum, err_ptr, NULL);
+}
+
+#if 0
+
+/* Not used at the moment. It is difficult to imagine for what purpose
+ it can be used :-) Please, do not forget to verify_area before it --ANK
+ */
+
+/*
+ * This combination is currently not used, but possible:
+ */
+
+extern __inline__
+unsigned int csum_partial_copy_to_user ( const char *src, char *dst,
+ int len, int sum, int *err_ptr)
+{
+ return csum_partial_copy_generic ( src, dst, len, sum, NULL, err_ptr);
+}
+#endif
+
+/*
+ * These are the old (and unsafe) way of doing checksums, a warning message will be
+ * printed if they are used and an exeption occurs.
+ *
+ * these functions should go away after some time.
+ */
+
+#define csum_partial_copy_fromuser csum_partial_copy
+unsigned int csum_partial_copy( const char *src, char *dst, int len, int sum);
+
+/*
+ * This is a version of ip_compute_csum() optimized for IP headers,
+ * which always checksum on 4 octet boundaries.
+ *
+ * By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by
+ * Arnt Gulbrandsen.
+ */
+static inline unsigned short ip_fast_csum(unsigned char * iph,
+ unsigned int ihl) {
+ unsigned int sum;
+
+ __asm__ __volatile__(
+" movl (%1), %0\n"
+" subl $4, %2\n"
+" jbe 2f\n"
+" addl 4(%1), %0\n"
+" adcl 8(%1), %0\n"
+" adcl 12(%1), %0\n"
+"1: adcl 16(%1), %0\n"
+" lea 4(%1), %1\n"
+" decl %2\n"
+" jne 1b\n"
+" adcl $0, %0\n"
+" movl %0, %2\n"
+" shrl $16, %0\n"
+" addw %w2, %w0\n"
+" adcl $0, %0\n"
+" notl %0\n"
+"2:"
+ /* Since the input registers which are loaded with iph and ipl
+ are modified, we must also specify them as outputs, or gcc
+ will assume they contain their original values. */
+ : "=r" (sum), "=r" (iph), "=r" (ihl)
+ : "1" (iph), "2" (ihl)
+ : "memory");
+ return(sum);
+}
+
+/*
+ * Fold a partial checksum
+ */
+
+static inline unsigned int csum_fold(unsigned int sum)
+{
+ __asm__("addl %1, %0\n"
+ "adcl $0xffff, %0\n"
+ : "=r" (sum)
+ : "r" (sum << 16), "0" (sum & 0xffff0000)
+ );
+ return (~sum) >> 16;
+}
+
+static inline unsigned long csum_tcpudp_nofold(unsigned long saddr,
+ unsigned long daddr,
+ unsigned short len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ __asm__(
+ "addl %1, %0\n"
+ "adcl %2, %0\n"
+ "adcl %3, %0\n"
+ "adcl $0, %0\n"
+ : "=r" (sum)
+ : "g" (daddr), "g"(saddr), "g"((ntohs(len)<<16)+proto*256), "0"(sum));
+ return sum;
+}
+
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented
+ */
+static inline unsigned short int csum_tcpudp_magic(unsigned long saddr,
+ unsigned long daddr,
+ unsigned short len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
+}
+
+/*
+ * this routine is used for miscellaneous IP-like checksums, mainly
+ * in icmp.c
+ */
+
+static inline unsigned short ip_compute_csum(unsigned char * buff, int len) {
+ return csum_fold (csum_partial(buff, len, 0));
+}
+
+#define _HAVE_ARCH_IPV6_CSUM
+static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
+ struct in6_addr *daddr,
+ __u16 len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ __asm__("addl 0(%1), %0\n"
+ "adcl 4(%1), %0\n"
+ "adcl 8(%1), %0\n"
+ "adcl 12(%1), %0\n"
+ "adcl 0(%2), %0\n"
+ "adcl 4(%2), %0\n"
+ "adcl 8(%2), %0\n"
+ "adcl 12(%2), %0\n"
+ "adcl %3, %0\n"
+ "adcl %4, %0\n"
+ "adcl $0, %0\n"
+ : "=&r" (sum)
+ : "r" (saddr), "r" (daddr),
+ "r"(htonl((__u32) (len))), "r"(htonl(proto)), "0"(sum)
+ : "memory");
+
+ return csum_fold(sum);
+}
+
+/*
+ * Copy and checksum to user
+ */
+#define HAVE_CSUM_COPY_USER
+static __inline__ unsigned int csum_and_copy_to_user (const char *src, char *dst,
+ int len, int sum, int *err_ptr)
+{
+ if (access_ok(VERIFY_WRITE, dst, len))
+ return csum_partial_copy_generic(src, dst, len, sum, NULL, err_ptr);
+
+ if (len)
+ *err_ptr = -EFAULT;
+
+ return -1; /* invalid checksum */
+}
+
+#endif
diff --git a/pfinet/linux-src/include/asm-m68k/checksum.h b/pfinet/linux-src/include/asm-m68k/checksum.h
new file mode 100644
index 00000000..f5236490
--- /dev/null
+++ b/pfinet/linux-src/include/asm-m68k/checksum.h
@@ -0,0 +1,154 @@
+#ifndef _M68K_CHECKSUM_H
+#define _M68K_CHECKSUM_H
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum);
+
+/*
+ * the same as csum_partial, but copies from src while it
+ * checksums
+ *
+ * here even more important to align src and dst on a 32-bit (or even
+ * better 64-bit) boundary
+ */
+
+unsigned int csum_partial_copy(const char *src, char *dst, int len, int sum);
+
+
+/*
+ * the same as csum_partial_copy, but copies from user space.
+ *
+ * here even more important to align src and dst on a 32-bit (or even
+ * better 64-bit) boundary
+ */
+
+extern unsigned int csum_partial_copy_from_user(const char *src, char *dst,
+ int len, int sum, int *csum_err);
+
+#define csum_partial_copy_nocheck(src, dst, len, sum) \
+ csum_partial_copy((src), (dst), (len), (sum))
+
+/*
+ * This is a version of ip_compute_csum() optimized for IP headers,
+ * which always checksum on 4 octet boundaries.
+ *
+ */
+static inline unsigned short
+ip_fast_csum(unsigned char *iph, unsigned int ihl)
+{
+ unsigned int sum = 0;
+
+ __asm__ ("subqw #1,%2\n"
+ "1:\t"
+ "movel %1@+,%/d0\n\t"
+ "addxl %/d0,%0\n\t"
+ "dbra %2,1b\n\t"
+ "movel %0,%/d0\n\t"
+ "swap %/d0\n\t"
+ "addxw %/d0,%0\n\t"
+ "clrw %/d0\n\t"
+ "addxw %/d0,%0\n\t"
+ : "=d" (sum), "=a" (iph), "=d" (ihl)
+ : "0" (sum), "1" (iph), "2" (ihl)
+ : "d0");
+ return ~sum;
+}
+
+/*
+ * Fold a partial checksum
+ */
+
+static inline unsigned int csum_fold(unsigned int sum)
+{
+ unsigned int tmp = sum;
+ __asm__("swap %1\n\t"
+ "addw %1, %0\n\t"
+ "clrw %1\n\t"
+ "addxw %1, %0"
+ : "=&d" (sum), "=&d" (tmp)
+ : "0" (sum), "1" (sum));
+ return ~sum;
+}
+
+
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented
+ */
+
+static inline unsigned int
+csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr, unsigned short len,
+ unsigned short proto, unsigned int sum)
+{
+ __asm__ ("addl %1,%0\n\t"
+ "addxl %4,%0\n\t"
+ "addxl %5,%0\n\t"
+ "clrl %1\n\t"
+ "addxl %1,%0"
+ : "=&d" (sum), "=&d" (saddr)
+ : "0" (daddr), "1" (saddr), "d" (len + proto),
+ "d"(sum));
+ return sum;
+}
+
+static inline unsigned short int
+csum_tcpudp_magic(unsigned long saddr, unsigned long daddr, unsigned short len,
+ unsigned short proto, unsigned int sum)
+{
+ return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
+}
+
+/*
+ * this routine is used for miscellaneous IP-like checksums, mainly
+ * in icmp.c
+ */
+
+static inline unsigned short
+ip_compute_csum(unsigned char * buff, int len)
+{
+ return csum_fold (csum_partial(buff, len, 0));
+}
+
+#define _HAVE_ARCH_IPV6_CSUM
+static __inline__ unsigned short int
+csum_ipv6_magic(struct in6_addr *saddr, struct in6_addr *daddr,
+ __u16 len, unsigned short proto, unsigned int sum)
+{
+ register unsigned long tmp;
+ __asm__("addl %2@,%0\n\t"
+ "movel %2@(4),%1\n\t"
+ "addxl %1,%0\n\t"
+ "movel %2@(8),%1\n\t"
+ "addxl %1,%0\n\t"
+ "movel %2@(12),%1\n\t"
+ "addxl %1,%0\n\t"
+ "movel %3@,%1\n\t"
+ "addxl %1,%0\n\t"
+ "movel %3@(4),%1\n\t"
+ "addxl %1,%0\n\t"
+ "movel %3@(8),%1\n\t"
+ "addxl %1,%0\n\t"
+ "movel %3@(12),%1\n\t"
+ "addxl %1,%0\n\t"
+ "addxl %4,%0\n\t"
+ "clrl %1\n\t"
+ "addxl %1,%0"
+ : "=&d" (sum), "=&d" (tmp)
+ : "a" (saddr), "a" (daddr), "d" ((__u32) len + proto),
+ "0" (sum));
+
+ return csum_fold(sum);
+}
+
+#endif /* _M68K_CHECKSUM_H */
diff --git a/pfinet/linux-src/include/asm-mips/checksum.h b/pfinet/linux-src/include/asm-mips/checksum.h
new file mode 100644
index 00000000..6d3700f0
--- /dev/null
+++ b/pfinet/linux-src/include/asm-mips/checksum.h
@@ -0,0 +1,255 @@
+/* $Id: checksum.h,v 1.6 1998/09/19 19:19:36 ralf Exp $
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1995, 1996, 1997, 1998 by Ralf Baechle
+ */
+#ifndef __ASM_MIPS_CHECKSUM_H
+#define __ASM_MIPS_CHECKSUM_H
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum);
+
+/*
+ * this is a new version of the above that records errors it finds in *errp,
+ * but continues and zeros the rest of the buffer.
+ */
+#define csum_partial_copy_nocheck csum_partial_copy
+
+/*
+ * this is a new version of the above that records errors it finds in *errp,
+ * but continues and zeros the rest of the buffer.
+ */
+unsigned int csum_partial_copy_from_user(const char *src, char *dst, int len,
+ unsigned int sum, int *errp);
+
+#define HAVE_CSUM_COPY_USER
+unsigned int csum_and_copy_to_user (const char *src, char *dst,
+ int len, int sum, int *err_ptr);
+
+/*
+ * the same as csum_partial, but copies from user space (but on MIPS
+ * we have just one address space, so this is identical to the above)
+ *
+ * this is obsolete and will go away.
+ */
+#define csum_partial_copy_fromuser csum_partial_copy
+unsigned int csum_partial_copy(const char *src, char *dst, int len, unsigned int sum);
+
+/*
+ * Fold a partial checksum without adding pseudo headers
+ */
+static inline unsigned short int csum_fold(unsigned int sum)
+{
+ __asm__("
+ .set noat
+ sll $1,%0,16
+ addu %0,$1
+ sltu $1,%0,$1
+ srl %0,%0,16
+ addu %0,$1
+ xori %0,0xffff
+ .set at"
+ : "=r" (sum)
+ : "0" (sum)
+ : "$1");
+
+ return sum;
+}
+
+/*
+ * This is a version of ip_compute_csum() optimized for IP headers,
+ * which always checksum on 4 octet boundaries.
+ *
+ * By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by
+ * Arnt Gulbrandsen.
+ */
+static inline unsigned short ip_fast_csum(unsigned char * iph,
+ unsigned int ihl)
+{
+ unsigned int sum;
+ unsigned long dummy;
+
+ /*
+ * This is for 32-bit MIPS processors.
+ */
+ __asm__ __volatile__("
+ .set noreorder
+ .set noat
+ lw %0,(%1)
+ subu %2,4
+ #blez %2,2f
+ sll %2,2 # delay slot
+
+ lw %3,4(%1)
+ addu %2,%1 # delay slot
+ addu %0,%3
+ sltu $1,%0,%3
+ lw %3,8(%1)
+ addu %0,$1
+ addu %0,%3
+ sltu $1,%0,%3
+ lw %3,12(%1)
+ addu %0,$1
+ addu %0,%3
+ sltu $1,%0,%3
+ addu %0,$1
+
+1: lw %3,16(%1)
+ addiu %1,4
+ addu %0,%3
+ sltu $1,%0,%3
+ bne %2,%1,1b
+ addu %0,$1 # delay slot
+
+2: .set at
+ .set reorder"
+ : "=&r" (sum), "=&r" (iph), "=&r" (ihl), "=&r" (dummy)
+ : "1" (iph), "2" (ihl)
+ : "$1");
+
+ return csum_fold(sum);
+}
+
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented
+ */
+static inline unsigned long csum_tcpudp_nofold(unsigned long saddr,
+ unsigned long daddr,
+ unsigned short len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ __asm__("
+ .set noat
+ addu %0,%2
+ sltu $1,%0,%2
+ addu %0,$1
+
+ addu %0,%3
+ sltu $1,%0,%3
+ addu %0,$1
+
+ addu %0,%4
+ sltu $1,%0,%4
+ addu %0,$1
+ .set at"
+ : "=r" (sum)
+ : "0" (daddr), "r"(saddr),
+#ifdef __MIPSEL__
+ "r" ((ntohs(len)<<16)+proto*256),
+#else
+ "r" (((proto)<<16)+len),
+#endif
+ "r"(sum)
+ : "$1");
+
+ return sum;
+}
+
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented
+ */
+static inline unsigned short int csum_tcpudp_magic(unsigned long saddr,
+ unsigned long daddr,
+ unsigned short len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
+}
+
+/*
+ * this routine is used for miscellaneous IP-like checksums, mainly
+ * in icmp.c
+ */
+static inline unsigned short ip_compute_csum(unsigned char * buff, int len)
+{
+ return csum_fold(csum_partial(buff, len, 0));
+}
+
+#define _HAVE_ARCH_IPV6_CSUM
+static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
+ struct in6_addr *daddr,
+ __u16 len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ __asm__("
+ .set noreorder
+ .set noat
+ addu %0,%5 # proto (long in network byte order)
+ sltu $1,%0,%5
+ addu %0,$1
+
+ addu %0,%6 # csum
+ sltu $1,%0,%6
+ lw %1,0(%2) # four words source address
+ addu %0,$1
+ addu %0,%1
+ sltu $1,%0,$1
+
+ lw %1,4(%2)
+ addu %0,$1
+ addu %0,%1
+ sltu $1,%0,$1
+
+ lw %1,8(%2)
+ addu %0,$1
+ addu %0,%1
+ sltu $1,%0,$1
+
+ lw %1,12(%2)
+ addu %0,$1
+ addu %0,%1
+ sltu $1,%0,$1
+
+ lw %1,0(%3)
+ addu %0,$1
+ addu %0,%1
+ sltu $1,%0,$1
+
+ lw %1,4(%3)
+ addu %0,$1
+ addu %0,%1
+ sltu $1,%0,$1
+
+ lw %1,8(%3)
+ addu %0,$1
+ addu %0,%1
+ sltu $1,%0,$1
+
+ lw %1,12(%3)
+ addu %0,$1
+ addu %0,%1
+ sltu $1,%0,$1
+ .set noat
+ .set noreorder"
+ : "=r" (sum),
+ "=r" (proto)
+ : "r" (saddr),
+ "r" (daddr),
+ "0" (htonl((__u32) (len))),
+ "1" (htonl(proto)),
+ "r"(sum)
+ : "$1");
+
+ return csum_fold(sum);
+}
+
+#endif /* __ASM_MIPS_CHECKSUM_H */
diff --git a/pfinet/linux-src/include/asm-ppc/checksum.h b/pfinet/linux-src/include/asm-ppc/checksum.h
new file mode 100644
index 00000000..e635ff59
--- /dev/null
+++ b/pfinet/linux-src/include/asm-ppc/checksum.h
@@ -0,0 +1,113 @@
+#ifndef _PPC_CHECKSUM_H
+#define _PPC_CHECKSUM_H
+
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+extern unsigned int csum_partial(const unsigned char * buff, int len,
+ unsigned int sum);
+
+/*
+ * Computes the checksum of a memory block at src, length len,
+ * and adds in "sum" (32-bit), while copying the block to dst.
+ * If an access exception occurs on src or dst, it stores -EFAULT
+ * to *src_err or *dst_err respectively (if that pointer is not
+ * NULL), and, for an error on src, zeroes the rest of dst.
+ *
+ * Like csum_partial, this must be called with even lengths,
+ * except for the last fragment.
+ */
+extern unsigned int csum_partial_copy_generic(const char *src, char *dst,
+ int len, unsigned int sum,
+ int *src_err, int *dst_err);
+
+#define csum_partial_copy_from_user(src, dst, len, sum, errp) \
+ csum_partial_copy_generic((src), (dst), (len), (sum), (errp), 0)
+
+/* FIXME: this needs to be written to really do no check -- Cort */
+#define csum_partial_copy_nocheck(src, dst, len, sum) \
+ csum_partial_copy_generic((src), (dst), (len), (sum), 0, 0)
+/*
+ * Old versions which ignore errors.
+ */
+#define csum_partial_copy(src, dst, len, sum) \
+ csum_partial_copy_generic((src), (dst), (len), (sum), 0, 0)
+#define csum_partial_copy_fromuser(src, dst, len, sum) \
+ csum_partial_copy_generic((src), (dst), (len), (sum), 0, 0)
+
+
+/*
+ * turns a 32-bit partial checksum (e.g. from csum_partial) into a
+ * 1's complement 16-bit checksum.
+ */
+static inline unsigned int csum_fold(unsigned int sum)
+{
+ unsigned int tmp;
+
+ /* swap the two 16-bit halves of sum */
+ __asm__("rlwinm %0,%1,16,0,31" : "=r" (tmp) : "r" (sum));
+ /* if there is a carry from adding the two 16-bit halves,
+ it will carry from the lower half into the upper half,
+ giving us the correct sum in the upper half. */
+ sum = ~(sum + tmp) >> 16;
+ return sum;
+}
+
+/*
+ * this routine is used for miscellaneous IP-like checksums, mainly
+ * in icmp.c
+ */
+static inline unsigned short ip_compute_csum(unsigned char * buff, int len)
+{
+ return csum_fold(csum_partial(buff, len, 0));
+}
+
+/*
+ * FIXME: I swiped this one from the sparc and made minor modifications.
+ * It may not be correct. -- Cort
+ */
+static inline unsigned long csum_tcpudp_nofold(unsigned long saddr,
+ unsigned long daddr,
+ unsigned short len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ __asm__("
+ addc %0,%0,%1
+ adde %0,%0,%2
+ adde %0,%0,%3
+ addze %0,%0
+ "
+ : "=r" (sum)
+ : "r" (daddr), "r"(saddr), "r"((proto<<16)+len), "0"(sum));
+ return sum;
+}
+
+/*
+ * This is a version of ip_compute_csum() optimized for IP headers,
+ * which always checksum on 4 octet boundaries. ihl is the number
+ * of 32-bit words and is always >= 5.
+ */
+extern unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl);
+
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented
+ */
+extern unsigned short csum_tcpudp_magic(unsigned long saddr,
+ unsigned long daddr,
+ unsigned short len,
+ unsigned short proto,
+ unsigned int sum);
+
+#endif
diff --git a/pfinet/linux-src/include/asm-s390/checksum.h b/pfinet/linux-src/include/asm-s390/checksum.h
new file mode 100644
index 00000000..c0f07d90
--- /dev/null
+++ b/pfinet/linux-src/include/asm-s390/checksum.h
@@ -0,0 +1,186 @@
+#ifndef _S390_CHECKSUM_H
+#define _S390_CHECKSUM_H
+
+/*
+ * include/asm-s390/checksum.h
+ * S390 fast network checksum routines
+ * see also arch/S390/lib/checksum.c
+ *
+ * S390 version
+ * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Ulrich Hild (first version)
+ * Martin Schwidefsky (heavily optimized CKSM version)
+ * D.J. Barrow (third attempt)
+ */
+
+#include <asm/uaccess.h>
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+unsigned int
+csum_partial(const unsigned char * buff, int len, unsigned int sum);
+
+/*
+ * csum_partial as an inline function
+ */
+extern inline unsigned int
+csum_partial_inline(const unsigned char * buff, int len, unsigned int sum)
+{
+ __asm__ __volatile__ (
+ " lr 2,%1\n" /* address in gpr 2 */
+ " lr 3,%2\n" /* length in gpr 3 */
+ "0: cksm %0,2\n" /* do checksum on longs */
+ " jo 0b\n"
+ : "+&d" (sum)
+ : "d" (buff), "d" (len)
+ : "cc", "2", "3" );
+ return sum;
+}
+
+/*
+ * the same as csum_partial, but copies from src while it
+ * checksums
+ *
+ * here even more important to align src and dst on a 32-bit (or even
+ * better 64-bit) boundary
+ */
+
+extern inline unsigned int
+csum_partial_copy(const char *src, char *dst, int len,unsigned int sum)
+{
+ memcpy(dst,src,len);
+ return csum_partial_inline(dst, len, sum);
+}
+
+/*
+ * the same as csum_partial_copy, but copies from user space.
+ *
+ * here even more important to align src and dst on a 32-bit (or even
+ * better 64-bit) boundary
+ */
+
+extern inline unsigned int
+csum_partial_copy_from_user(const char *src, char *dst,
+ int len, unsigned int sum, int *errp)
+{
+ if (copy_from_user(dst, src, len)) {
+ *errp = -EFAULT;
+ memset(dst, 0, len);
+ return sum;
+ }
+ return csum_partial(dst, len, sum);
+}
+
+extern inline unsigned int
+csum_partial_copy_nocheck (const char *src, char *dst, int len, unsigned int sum)
+{
+ memcpy(dst,src,len);
+ return csum_partial_inline(dst, len, sum);
+}
+
+/*
+ * Fold a partial checksum without adding pseudo headers
+ */
+#if 1
+unsigned short csum_fold(unsigned int sum);
+#else
+extern inline unsigned short
+csum_fold(unsigned int sum)
+{
+ __asm__ __volatile__ (
+ " sr 3,3\n" /* %0 = H*65536 + L */
+ " lr 2,%0\n" /* %0 = H L, R2/R3 = H L / 0 0 */
+ " srdl 2,16\n" /* %0 = H L, R2/R3 = 0 H / L 0 */
+ " alr 2,3\n" /* %0 = H L, R2/R3 = L H / L 0 */
+ " alr %0,2\n" /* %0 = H+L+C L+H */
+ " srl %0,16\n" /* %0 = H+L+C */
+ : "+&d" (sum) : : "cc", "2", "3");
+ return ((unsigned short) ~sum);
+}
+#endif
+
+/*
+ * This is a version of ip_compute_csum() optimized for IP headers,
+ * which always checksum on 4 octet boundaries.
+ *
+ */
+extern inline unsigned short
+ip_fast_csum(unsigned char *iph, unsigned int ihl)
+{
+ unsigned long sum;
+
+ __asm__ __volatile__ (
+ " sr %0,%0\n" /* set sum to zero */
+ " lr 2,%1\n" /* address in gpr 2 */
+ " lr 3,%2\n" /* length in gpr 3 */
+ "0: cksm %0,2\n" /* do checksum on longs */
+ " jo 0b\n"
+ : "=&d" (sum)
+ : "d" (iph), "d" (ihl*4)
+ : "cc", "2", "3" );
+ return csum_fold(sum);
+}
+
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 32-bit checksum
+ */
+extern inline unsigned int
+csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr,
+ unsigned short len, unsigned short proto,
+ unsigned int sum)
+{
+ __asm__ __volatile__ (
+ " sll %3,16\n"
+ " or %3,%4\n" /* newproto=proto<<16 in hiword, len in lowword */
+ " alr %1,%2\n" /* saddr+=daddr */
+ " brc 12,0f\n"
+ " ahi %1,1\n" /* add carry */
+ "0: alr %1,%3\n" /* add saddr+=newproto */
+ " brc 12,1f\n"
+ " ahi %1,1\n" /* add carry again */
+ "1: alr %0,%1\n" /* sum+=saddr */
+ " brc 12,2f\n"
+ " ahi %0,1\n" /* add carry again */
+ "2:"
+ : "+&d" (sum)
+ : "d" (saddr), "d" (daddr), "d" (proto), "d" (len)
+ : "cc" );
+ return sum;
+}
+
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented
+ */
+
+extern inline unsigned short int
+csum_tcpudp_magic(unsigned long saddr, unsigned long daddr,
+ unsigned short len, unsigned short proto,
+ unsigned int sum)
+{
+ return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
+}
+
+/*
+ * this routine is used for miscellaneous IP-like checksums, mainly
+ * in icmp.c
+ */
+
+extern inline unsigned short
+ip_compute_csum(unsigned char * buff, int len)
+{
+ return csum_fold(csum_partial(buff, len, 0));
+}
+
+#endif /* _S390_CHECKSUM_H */
diff --git a/pfinet/linux-src/include/asm-sparc/checksum.h b/pfinet/linux-src/include/asm-sparc/checksum.h
new file mode 100644
index 00000000..e552cfc6
--- /dev/null
+++ b/pfinet/linux-src/include/asm-sparc/checksum.h
@@ -0,0 +1,252 @@
+/* $Id: checksum.h,v 1.29 1999/03/21 05:22:07 davem Exp $ */
+#ifndef __SPARC_CHECKSUM_H
+#define __SPARC_CHECKSUM_H
+
+/* checksum.h: IP/UDP/TCP checksum routines on the Sparc.
+ *
+ * Copyright(C) 1995 Linus Torvalds
+ * Copyright(C) 1995 Miguel de Icaza
+ * Copyright(C) 1996 David S. Miller
+ * Copyright(C) 1996 Eddie C. Dost
+ * Copyright(C) 1997 Jakub Jelinek
+ *
+ * derived from:
+ * Alpha checksum c-code
+ * ix86 inline assembly
+ * RFC1071 Computing the Internet Checksum
+ */
+
+#include <asm/uaccess.h>
+#include <asm/cprefix.h>
+
+/* computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+extern unsigned int csum_partial(unsigned char * buff, int len, unsigned int sum);
+
+/* the same as csum_partial, but copies from fs:src while it
+ * checksums
+ *
+ * here even more important to align src and dst on a 32-bit (or even
+ * better 64-bit) boundary
+ */
+
+/* FIXME: Remove these two macros ASAP */
+#define csum_partial_copy(src, dst, len, sum) \
+ csum_partial_copy_nocheck(src,dst,len,sum)
+#define csum_partial_copy_fromuser(s, d, l, w) \
+ csum_partial_copy((char *) (s), (d), (l), (w))
+
+extern unsigned int __csum_partial_copy_sparc_generic (const char *, char *);
+
+extern __inline__ unsigned int
+csum_partial_copy_nocheck (const char *src, char *dst, int len,
+ unsigned int sum)
+{
+ register unsigned int ret asm("o0") = (unsigned int)src;
+ register char *d asm("o1") = dst;
+ register int l asm("g1") = len;
+
+ __asm__ __volatile__ ("
+ call " C_LABEL_STR(__csum_partial_copy_sparc_generic) "
+ mov %4, %%g7
+ " : "=r" (ret) : "0" (ret), "r" (d), "r" (l), "r" (sum) :
+ "o1", "o2", "o3", "o4", "o5", "o7", "g1", "g2", "g3", "g4", "g5", "g7");
+ return ret;
+}
+
+extern __inline__ unsigned int
+csum_partial_copy_from_user(const char *src, char *dst, int len,
+ unsigned int sum, int *err)
+ {
+ if (!access_ok (VERIFY_READ, src, len)) {
+ *err = -EFAULT;
+ memset (dst, 0, len);
+ return sum;
+ } else {
+ register unsigned int ret asm("o0") = (unsigned int)src;
+ register char *d asm("o1") = dst;
+ register int l asm("g1") = len;
+ register unsigned int s asm("g7") = sum;
+
+ __asm__ __volatile__ ("
+ .section __ex_table,#alloc
+ .align 4
+ .word 1f,2
+ .previous
+1:
+ call " C_LABEL_STR(__csum_partial_copy_sparc_generic) "
+ st %5, [%%sp + 64]
+ " : "=r" (ret) : "0" (ret), "r" (d), "r" (l), "r" (s), "r" (err) :
+ "o1", "o2", "o3", "o4", "o5", "o7", "g1", "g2", "g3", "g4", "g5", "g7");
+ return ret;
+ }
+ }
+
+extern __inline__ unsigned int
+csum_partial_copy_to_user(const char *src, char *dst, int len,
+ unsigned int sum, int *err)
+{
+ if (!access_ok (VERIFY_WRITE, dst, len)) {
+ *err = -EFAULT;
+ return sum;
+ } else {
+ register unsigned int ret asm("o0") = (unsigned int)src;
+ register char *d asm("o1") = dst;
+ register int l asm("g1") = len;
+ register unsigned int s asm("g7") = sum;
+
+ __asm__ __volatile__ ("
+ .section __ex_table,#alloc
+ .align 4
+ .word 1f,1
+ .previous
+1:
+ call " C_LABEL_STR(__csum_partial_copy_sparc_generic) "
+ st %5, [%%sp + 64]
+ " : "=r" (ret) : "0" (ret), "r" (d), "r" (l), "r" (s), "r" (err) :
+ "o1", "o2", "o3", "o4", "o5", "o7", "g1", "g2", "g3", "g4", "g5", "g7");
+ return ret;
+ }
+}
+
+#define HAVE_CSUM_COPY_USER
+#define csum_and_copy_to_user csum_partial_copy_to_user
+
+/* ihl is always 5 or greater, almost always is 5, and iph is word aligned
+ * the majority of the time.
+ */
+extern __inline__ unsigned short ip_fast_csum(__const__ unsigned char *iph,
+ unsigned int ihl)
+{
+ unsigned short sum;
+
+ /* Note: We must read %2 before we touch %0 for the first time,
+ * because GCC can legitimately use the same register for
+ * both operands.
+ */
+ __asm__ __volatile__("sub\t%2, 4, %%g4\n\t"
+ "ld\t[%1 + 0x00], %0\n\t"
+ "ld\t[%1 + 0x04], %%g2\n\t"
+ "ld\t[%1 + 0x08], %%g3\n\t"
+ "addcc\t%%g2, %0, %0\n\t"
+ "addxcc\t%%g3, %0, %0\n\t"
+ "ld\t[%1 + 0x0c], %%g2\n\t"
+ "ld\t[%1 + 0x10], %%g3\n\t"
+ "addxcc\t%%g2, %0, %0\n\t"
+ "addx\t%0, %%g0, %0\n"
+ "1:\taddcc\t%%g3, %0, %0\n\t"
+ "add\t%1, 4, %1\n\t"
+ "addxcc\t%0, %%g0, %0\n\t"
+ "subcc\t%%g4, 1, %%g4\n\t"
+ "be,a\t2f\n\t"
+ "sll\t%0, 16, %%g2\n\t"
+ "b\t1b\n\t"
+ "ld\t[%1 + 0x10], %%g3\n"
+ "2:\taddcc\t%0, %%g2, %%g2\n\t"
+ "srl\t%%g2, 16, %0\n\t"
+ "addx\t%0, %%g0, %0\n\t"
+ "xnor\t%%g0, %0, %0"
+ : "=r" (sum), "=&r" (iph)
+ : "r" (ihl), "1" (iph)
+ : "g2", "g3", "g4", "cc");
+ return sum;
+}
+
+/* Fold a partial checksum without adding pseudo headers. */
+extern __inline__ unsigned int csum_fold(unsigned int sum)
+{
+ unsigned int tmp;
+
+ __asm__ __volatile__("addcc\t%0, %1, %1\n\t"
+ "srl\t%1, 16, %1\n\t"
+ "addx\t%1, %%g0, %1\n\t"
+ "xnor\t%%g0, %1, %0"
+ : "=&r" (sum), "=r" (tmp)
+ : "0" (sum), "1" (sum<<16)
+ : "cc");
+ return sum;
+}
+
+extern __inline__ unsigned long csum_tcpudp_nofold(unsigned long saddr,
+ unsigned long daddr,
+ unsigned int len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ __asm__ __volatile__("addcc\t%1, %0, %0\n\t"
+ "addxcc\t%2, %0, %0\n\t"
+ "addxcc\t%3, %0, %0\n\t"
+ "addx\t%0, %%g0, %0\n\t"
+ : "=r" (sum), "=r" (saddr)
+ : "r" (daddr), "r" ((proto<<16)+len), "0" (sum),
+ "1" (saddr)
+ : "cc");
+ return sum;
+}
+
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented
+ */
+static inline unsigned short int csum_tcpudp_magic(unsigned long saddr,
+ unsigned long daddr,
+ unsigned short len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
+}
+
+#define _HAVE_ARCH_IPV6_CSUM
+
+static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
+ struct in6_addr *daddr,
+ __u16 len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ __asm__ __volatile__ ("
+ addcc %3, %4, %%g4
+ addxcc %5, %%g4, %%g4
+ ld [%2 + 0x0c], %%g2
+ ld [%2 + 0x08], %%g3
+ addxcc %%g2, %%g4, %%g4
+ ld [%2 + 0x04], %%g2
+ addxcc %%g3, %%g4, %%g4
+ ld [%2 + 0x00], %%g3
+ addxcc %%g2, %%g4, %%g4
+ ld [%1 + 0x0c], %%g2
+ addxcc %%g3, %%g4, %%g4
+ ld [%1 + 0x08], %%g3
+ addxcc %%g2, %%g4, %%g4
+ ld [%1 + 0x04], %%g2
+ addxcc %%g3, %%g4, %%g4
+ ld [%1 + 0x00], %%g3
+ addxcc %%g2, %%g4, %%g4
+ addxcc %%g3, %%g4, %0
+ addx 0, %0, %0
+ "
+ : "=&r" (sum)
+ : "r" (saddr), "r" (daddr),
+ "r"(htonl((__u32) (len))), "r"(htonl(proto)), "r"(sum)
+ : "g2", "g3", "g4", "cc");
+
+ return csum_fold(sum);
+}
+
+/* this routine is used for miscellaneous IP-like checksums, mainly in icmp.c */
+extern __inline__ unsigned short ip_compute_csum(unsigned char * buff, int len)
+{
+ return csum_fold(csum_partial(buff, len, 0));
+}
+
+#endif /* !(__SPARC_CHECKSUM_H) */
diff --git a/pfinet/linux-src/include/asm-sparc64/checksum.h b/pfinet/linux-src/include/asm-sparc64/checksum.h
new file mode 100644
index 00000000..e3da2651
--- /dev/null
+++ b/pfinet/linux-src/include/asm-sparc64/checksum.h
@@ -0,0 +1,207 @@
+/* $Id: checksum.h,v 1.11 1998/04/17 02:37:22 davem Exp $ */
+#ifndef __SPARC64_CHECKSUM_H
+#define __SPARC64_CHECKSUM_H
+
+/* checksum.h: IP/UDP/TCP checksum routines on the V9.
+ *
+ * Copyright(C) 1995 Linus Torvalds
+ * Copyright(C) 1995 Miguel de Icaza
+ * Copyright(C) 1996 David S. Miller
+ * Copyright(C) 1996 Eddie C. Dost
+ * Copyright(C) 1997 Jakub Jelinek
+ *
+ * derived from:
+ * Alpha checksum c-code
+ * ix86 inline assembly
+ * RFC1071 Computing the Internet Checksum
+ */
+
+#include <asm/uaccess.h>
+
+/* computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+extern unsigned int csum_partial(unsigned char * buff, int len, unsigned int sum);
+
+/* the same as csum_partial, but copies from user space while it
+ * checksums
+ *
+ * here even more important to align src and dst on a 32-bit (or even
+ * better 64-bit) boundary
+ */
+/* FIXME: Remove these macros ASAP */
+#define csum_partial_copy(src, dst, len, sum) \
+ csum_partial_copy_nocheck(src,dst,len,sum)
+#define csum_partial_copy_fromuser(s, d, l, w) \
+ csum_partial_copy_from_user((char *) (s), (d), (l), (w), NULL)
+
+extern unsigned int csum_partial_copy_sparc64(const char *src, char *dst, int len, unsigned int sum);
+
+extern __inline__ unsigned int
+csum_partial_copy_nocheck (const char *src, char *dst, int len,
+ unsigned int sum)
+{
+ __asm__ __volatile__ ("wr %%g0, %0, %%asi" : : "i" (ASI_P));
+ return csum_partial_copy_sparc64(src, dst, len, sum);
+}
+
+extern __inline__ unsigned int
+csum_partial_copy_from_user(const char *src, char *dst, int len,
+ unsigned int sum, int *err)
+{
+ __asm__ __volatile__ ("wr %%g0, %0, %%asi
+ stx %1, [%%sp + 0x7ff + 128]
+ " : : "i" (ASI_S), "r" (err));
+ return csum_partial_copy_sparc64(src, dst, len, sum);
+}
+
+#if 0
+/* Not implemented, but nobody uses it yet... */
+extern __inline__ unsigned int
+csum_partial_copy_to_user(const char *src, char *dst, int len,
+ unsigned int sum, int *err)
+{
+ return 0;
+}
+#endif
+
+/* ihl is always 5 or greater, almost always is 5, and iph is word aligned
+ * the majority of the time.
+ */
+extern __inline__ unsigned short ip_fast_csum(__const__ unsigned char *iph,
+ unsigned int ihl)
+{
+ unsigned short sum;
+
+ /* Note: We must read %2 before we touch %0 for the first time,
+ * because GCC can legitimately use the same register for
+ * both operands.
+ */
+ __asm__ __volatile__("
+ sub %2, 4, %%g7 ! IEU0
+ lduw [%1 + 0x00], %0 ! Load Group
+ lduw [%1 + 0x04], %%g2 ! Load Group
+ lduw [%1 + 0x08], %%g3 ! Load Group
+ addcc %%g2, %0, %0 ! IEU1 1 Load Bubble + Group
+ lduw [%1 + 0x0c], %%g2 ! Load
+ addccc %%g3, %0, %0 ! Sngle Group no Bubble
+ lduw [%1 + 0x10], %%g3 ! Load Group
+ addccc %%g2, %0, %0 ! Sngle Group no Bubble
+ addc %0, %%g0, %0 ! Sngle Group
+1: addcc %%g3, %0, %0 ! IEU1 Group no Bubble
+ add %1, 4, %1 ! IEU0
+ addccc %0, %%g0, %0 ! Sngle Group no Bubble
+ subcc %%g7, 1, %%g7 ! IEU1 Group
+ be,a,pt %%icc, 2f ! CTI
+ sll %0, 16, %%g2 ! IEU0
+ lduw [%1 + 0x10], %%g3 ! Load Group
+ ba,pt %%xcc, 1b ! CTI
+ nop ! IEU0
+2: addcc %0, %%g2, %%g2 ! IEU1 Group
+ srl %%g2, 16, %0 ! IEU0 Group regdep XXX Scheisse!
+ addc %0, %%g0, %0 ! Sngle Group
+ xnor %%g0, %0, %0 ! IEU0 Group
+ srl %0, 0, %0 ! IEU0 Group XXX Scheisse!
+" : "=r" (sum), "=&r" (iph)
+ : "r" (ihl), "1" (iph)
+ : "g2", "g3", "g7", "cc");
+ return sum;
+}
+
+/* Fold a partial checksum without adding pseudo headers. */
+extern __inline__ unsigned short csum_fold(unsigned int sum)
+{
+ unsigned int tmp;
+
+ __asm__ __volatile__("
+ addcc %0, %1, %1
+ srl %1, 16, %1
+ addc %1, %%g0, %1
+ xnor %%g0, %1, %0
+" : "=&r" (sum), "=r" (tmp)
+ : "0" (sum), "1" (sum<<16)
+ : "cc");
+ return (sum & 0xffff);
+}
+
+extern __inline__ unsigned long csum_tcpudp_nofold(unsigned long saddr,
+ unsigned long daddr,
+ unsigned int len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ __asm__ __volatile__("
+ addcc %1, %0, %0
+ addccc %2, %0, %0
+ addccc %3, %0, %0
+ addc %0, %%g0, %0
+" : "=r" (sum), "=r" (saddr)
+ : "r" (daddr), "r" ((proto<<16)+len), "0" (sum), "1" (saddr)
+ : "cc");
+ return sum;
+}
+
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented
+ */
+static inline unsigned short int csum_tcpudp_magic(unsigned long saddr,
+ unsigned long daddr,
+ unsigned short len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
+}
+
+#define _HAVE_ARCH_IPV6_CSUM
+
+static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
+ struct in6_addr *daddr,
+ __u16 len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ __asm__ __volatile__ ("
+ addcc %3, %4, %%g7
+ addccc %5, %%g7, %%g7
+ lduw [%2 + 0x0c], %%g2
+ lduw [%2 + 0x08], %%g3
+ addccc %%g2, %%g7, %%g7
+ lduw [%2 + 0x04], %%g2
+ addccc %%g3, %%g7, %%g7
+ lduw [%2 + 0x00], %%g3
+ addccc %%g2, %%g7, %%g7
+ lduw [%1 + 0x0c], %%g2
+ addccc %%g3, %%g7, %%g7
+ lduw [%1 + 0x08], %%g3
+ addccc %%g2, %%g7, %%g7
+ lduw [%1 + 0x04], %%g2
+ addccc %%g3, %%g7, %%g7
+ lduw [%1 + 0x00], %%g3
+ addccc %%g2, %%g7, %%g7
+ addccc %%g3, %%g7, %0
+ addc 0, %0, %0
+" : "=&r" (sum)
+ : "r" (saddr), "r" (daddr), "r"(htonl((__u32) (len))),
+ "r"(htonl(proto)), "r"(sum)
+ : "g2", "g3", "g7", "cc");
+
+ return csum_fold(sum);
+}
+
+/* this routine is used for miscellaneous IP-like checksums, mainly in icmp.c */
+extern __inline__ unsigned short ip_compute_csum(unsigned char * buff, int len)
+{
+ return csum_fold(csum_partial(buff, len, 0));
+}
+
+#endif /* !(__SPARC64_CHECKSUM_H) */
diff --git a/pfinet/linux-src/include/linux/a.out.h b/pfinet/linux-src/include/linux/a.out.h
new file mode 100644
index 00000000..af8a1dfa
--- /dev/null
+++ b/pfinet/linux-src/include/linux/a.out.h
@@ -0,0 +1,268 @@
+#ifndef __A_OUT_GNU_H__
+#define __A_OUT_GNU_H__
+
+#define __GNU_EXEC_MACROS__
+
+#ifndef __STRUCT_EXEC_OVERRIDE__
+
+#include <asm/a.out.h>
+
+#endif /* __STRUCT_EXEC_OVERRIDE__ */
+
+/* these go in the N_MACHTYPE field */
+enum machine_type {
+#if defined (M_OLDSUN2)
+ M__OLDSUN2 = M_OLDSUN2,
+#else
+ M_OLDSUN2 = 0,
+#endif
+#if defined (M_68010)
+ M__68010 = M_68010,
+#else
+ M_68010 = 1,
+#endif
+#if defined (M_68020)
+ M__68020 = M_68020,
+#else
+ M_68020 = 2,
+#endif
+#if defined (M_SPARC)
+ M__SPARC = M_SPARC,
+#else
+ M_SPARC = 3,
+#endif
+ /* skip a bunch so we don't run into any of sun's numbers */
+ M_386 = 100,
+ M_MIPS1 = 151, /* MIPS R3000/R3000 binary */
+ M_MIPS2 = 152 /* MIPS R6000/R4000 binary */
+};
+
+#if !defined (N_MAGIC)
+#define N_MAGIC(exec) ((exec).a_info & 0xffff)
+#endif
+#define N_MACHTYPE(exec) ((enum machine_type)(((exec).a_info >> 16) & 0xff))
+#define N_FLAGS(exec) (((exec).a_info >> 24) & 0xff)
+#define N_SET_INFO(exec, magic, type, flags) \
+ ((exec).a_info = ((magic) & 0xffff) \
+ | (((int)(type) & 0xff) << 16) \
+ | (((flags) & 0xff) << 24))
+#define N_SET_MAGIC(exec, magic) \
+ ((exec).a_info = (((exec).a_info & 0xffff0000) | ((magic) & 0xffff)))
+
+#define N_SET_MACHTYPE(exec, machtype) \
+ ((exec).a_info = \
+ ((exec).a_info&0xff00ffff) | ((((int)(machtype))&0xff) << 16))
+
+#define N_SET_FLAGS(exec, flags) \
+ ((exec).a_info = \
+ ((exec).a_info&0x00ffffff) | (((flags) & 0xff) << 24))
+
+/* Code indicating object file or impure executable. */
+#define OMAGIC 0407
+/* Code indicating pure executable. */
+#define NMAGIC 0410
+/* Code indicating demand-paged executable. */
+#define ZMAGIC 0413
+/* This indicates a demand-paged executable with the header in the text.
+ The first page is unmapped to help trap NULL pointer references */
+#define QMAGIC 0314
+
+/* Code indicating core file. */
+#define CMAGIC 0421
+
+#if !defined (N_BADMAG)
+#define N_BADMAG(x) (N_MAGIC(x) != OMAGIC \
+ && N_MAGIC(x) != NMAGIC \
+ && N_MAGIC(x) != ZMAGIC \
+ && N_MAGIC(x) != QMAGIC)
+#endif
+
+#define _N_HDROFF(x) (1024 - sizeof (struct exec))
+
+#if !defined (N_TXTOFF)
+#define N_TXTOFF(x) \
+ (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : \
+ (N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec)))
+#endif
+
+#if !defined (N_DATOFF)
+#define N_DATOFF(x) (N_TXTOFF(x) + (x).a_text)
+#endif
+
+#if !defined (N_TRELOFF)
+#define N_TRELOFF(x) (N_DATOFF(x) + (x).a_data)
+#endif
+
+#if !defined (N_DRELOFF)
+#define N_DRELOFF(x) (N_TRELOFF(x) + N_TRSIZE(x))
+#endif
+
+#if !defined (N_SYMOFF)
+#define N_SYMOFF(x) (N_DRELOFF(x) + N_DRSIZE(x))
+#endif
+
+#if !defined (N_STROFF)
+#define N_STROFF(x) (N_SYMOFF(x) + N_SYMSIZE(x))
+#endif
+
+/* Address of text segment in memory after it is loaded. */
+#if !defined (N_TXTADDR)
+#define N_TXTADDR(x) (N_MAGIC(x) == QMAGIC ? PAGE_SIZE : 0)
+#endif
+
+/* Address of data segment in memory after it is loaded.
+ Note that it is up to you to define SEGMENT_SIZE
+ on machines not listed here. */
+#if defined(vax) || defined(hp300) || defined(pyr)
+#define SEGMENT_SIZE page_size
+#endif
+#ifdef sony
+#define SEGMENT_SIZE 0x2000
+#endif /* Sony. */
+#ifdef is68k
+#define SEGMENT_SIZE 0x20000
+#endif
+#if defined(m68k) && defined(PORTAR)
+#define PAGE_SIZE 0x400
+#define SEGMENT_SIZE PAGE_SIZE
+#endif
+
+#ifdef linux
+#include <asm/page.h>
+#if defined(__i386__) || defined(__mc68000__)
+#define SEGMENT_SIZE 1024
+#else
+#ifndef SEGMENT_SIZE
+#define SEGMENT_SIZE PAGE_SIZE
+#endif
+#endif
+#endif
+
+#define _N_SEGMENT_ROUND(x) (((x) + SEGMENT_SIZE - 1) & ~(SEGMENT_SIZE - 1))
+
+#define _N_TXTENDADDR(x) (N_TXTADDR(x)+(x).a_text)
+
+#ifndef N_DATADDR
+#define N_DATADDR(x) \
+ (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x)) \
+ : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x))))
+#endif
+
+/* Address of bss segment in memory after it is loaded. */
+#if !defined (N_BSSADDR)
+#define N_BSSADDR(x) (N_DATADDR(x) + (x).a_data)
+#endif
+
+#if !defined (N_NLIST_DECLARED)
+struct nlist {
+ union {
+ char *n_name;
+ struct nlist *n_next;
+ long n_strx;
+ } n_un;
+ unsigned char n_type;
+ char n_other;
+ short n_desc;
+ unsigned long n_value;
+};
+#endif /* no N_NLIST_DECLARED. */
+
+#if !defined (N_UNDF)
+#define N_UNDF 0
+#endif
+#if !defined (N_ABS)
+#define N_ABS 2
+#endif
+#if !defined (N_TEXT)
+#define N_TEXT 4
+#endif
+#if !defined (N_DATA)
+#define N_DATA 6
+#endif
+#if !defined (N_BSS)
+#define N_BSS 8
+#endif
+#if !defined (N_FN)
+#define N_FN 15
+#endif
+
+#if !defined (N_EXT)
+#define N_EXT 1
+#endif
+#if !defined (N_TYPE)
+#define N_TYPE 036
+#endif
+#if !defined (N_STAB)
+#define N_STAB 0340
+#endif
+
+/* The following type indicates the definition of a symbol as being
+ an indirect reference to another symbol. The other symbol
+ appears as an undefined reference, immediately following this symbol.
+
+ Indirection is asymmetrical. The other symbol's value will be used
+ to satisfy requests for the indirect symbol, but not vice versa.
+ If the other symbol does not have a definition, libraries will
+ be searched to find a definition. */
+#define N_INDR 0xa
+
+/* The following symbols refer to set elements.
+ All the N_SET[ATDB] symbols with the same name form one set.
+ Space is allocated for the set in the text section, and each set
+ element's value is stored into one word of the space.
+ The first word of the space is the length of the set (number of elements).
+
+ The address of the set is made into an N_SETV symbol
+ whose name is the same as the name of the set.
+ This symbol acts like a N_DATA global symbol
+ in that it can satisfy undefined external references. */
+
+/* These appear as input to LD, in a .o file. */
+#define N_SETA 0x14 /* Absolute set element symbol */
+#define N_SETT 0x16 /* Text set element symbol */
+#define N_SETD 0x18 /* Data set element symbol */
+#define N_SETB 0x1A /* Bss set element symbol */
+
+/* This is output from LD. */
+#define N_SETV 0x1C /* Pointer to set vector in data area. */
+
+#if !defined (N_RELOCATION_INFO_DECLARED)
+/* This structure describes a single relocation to be performed.
+ The text-relocation section of the file is a vector of these structures,
+ all of which apply to the text section.
+ Likewise, the data-relocation section applies to the data section. */
+
+struct relocation_info
+{
+ /* Address (within segment) to be relocated. */
+ int r_address;
+ /* The meaning of r_symbolnum depends on r_extern. */
+ unsigned int r_symbolnum:24;
+ /* Nonzero means value is a pc-relative offset
+ and it should be relocated for changes in its own address
+ as well as for changes in the symbol or section specified. */
+ unsigned int r_pcrel:1;
+ /* Length (as exponent of 2) of the field to be relocated.
+ Thus, a value of 2 indicates 1<<2 bytes. */
+ unsigned int r_length:2;
+ /* 1 => relocate with value of symbol.
+ r_symbolnum is the index of the symbol
+ in file's the symbol table.
+ 0 => relocate with the address of a segment.
+ r_symbolnum is N_TEXT, N_DATA, N_BSS or N_ABS
+ (the N_EXT bit may be set also, but signifies nothing). */
+ unsigned int r_extern:1;
+ /* Four bits that aren't used, but when writing an object file
+ it is desirable to clear them. */
+#ifdef NS32K
+ unsigned r_bsr:1;
+ unsigned r_disp:1;
+ unsigned r_pad:2;
+#else
+ unsigned int r_pad:4;
+#endif
+};
+#endif /* no N_RELOCATION_INFO_DECLARED. */
+
+
+#endif /* __A_OUT_GNU_H__ */
diff --git a/pfinet/linux-src/include/linux/acct.h b/pfinet/linux-src/include/linux/acct.h
new file mode 100644
index 00000000..e20b1839
--- /dev/null
+++ b/pfinet/linux-src/include/linux/acct.h
@@ -0,0 +1,88 @@
+/*
+ * BSD Process Accounting for Linux - Definitions
+ *
+ * Author: Marco van Wieringen (mvw@planets.elm.net)
+ *
+ * This header file contains the definitions needed to implement
+ * BSD-style process accounting. The kernel accounting code and all
+ * user-level programs that try to do something useful with the
+ * process accounting log must include this file.
+ *
+ * Copyright (C) 1995 - 1997 Marco van Wieringen - ELM Consultancy B.V.
+ *
+ */
+
+#ifndef _LINUX_ACCT_H
+#define _LINUX_ACCT_H
+
+#include <linux/types.h>
+
+/*
+ * comp_t is a 16-bit "floating" point number with a 3-bit base 8
+ * exponent and a 13-bit fraction. See linux/kernel/acct.c for the
+ * specific encoding system used.
+ */
+
+typedef __u16 comp_t;
+
+/*
+ * accounting file record
+ *
+ * This structure contains all of the information written out to the
+ * process accounting file whenever a process exits.
+ */
+
+#define ACCT_COMM 16
+
+struct acct
+{
+ char ac_flag; /* Accounting Flags */
+/*
+ * No binary format break with 2.0 - but when we hit 32bit uid we'll
+ * have to bite one
+ */
+ __u16 ac_uid; /* Accounting Real User ID */
+ __u16 ac_gid; /* Accounting Real Group ID */
+ __u16 ac_tty; /* Accounting Control Terminal */
+ __u32 ac_btime; /* Accounting Process Creation Time */
+ comp_t ac_utime; /* Accounting User Time */
+ comp_t ac_stime; /* Accounting System Time */
+ comp_t ac_etime; /* Accounting Elapsed Time */
+ comp_t ac_mem; /* Accounting Average Memory Usage */
+ comp_t ac_io; /* Accounting Chars Transferred */
+ comp_t ac_rw; /* Accounting Blocks Read or Written */
+ comp_t ac_minflt; /* Accounting Minor Pagefaults */
+ comp_t ac_majflt; /* Accounting Major Pagefaults */
+ comp_t ac_swaps; /* Accounting Number of Swaps */
+ __u32 ac_exitcode; /* Accounting Exitcode */
+ char ac_comm[ACCT_COMM + 1]; /* Accounting Command Name */
+ char ac_pad[10]; /* Accounting Padding Bytes */
+};
+
+/*
+ * accounting flags
+ */
+ /* bit set when the process ... */
+#define AFORK 0x01 /* ... executed fork, but did not exec */
+#define ASU 0x02 /* ... used super-user privileges */
+#define ACOMPAT 0x04 /* ... used compatibility mode (VAX only not used) */
+#define ACORE 0x08 /* ... dumped core */
+#define AXSIG 0x10 /* ... was killed by a signal */
+
+#define AHZ 100
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+
+#ifdef CONFIG_BSD_PROCESS_ACCT
+extern void acct_auto_close(kdev_t dev);
+extern int acct_process(long exitcode);
+#else
+#define acct_auto_close(x) do { } while (0)
+#define acct_process(x) do { } while (0)
+#endif
+
+#endif /* __KERNEL */
+
+#endif /* _LINUX_ACCT_H */
diff --git a/pfinet/linux-src/include/linux/adfs_fs.h b/pfinet/linux-src/include/linux/adfs_fs.h
new file mode 100644
index 00000000..f6ed2d3b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/adfs_fs.h
@@ -0,0 +1,175 @@
+#ifndef _ADFS_FS_H
+#define _ADFS_FS_H
+
+#include <linux/types.h>
+/*
+ * Structures of data on the disk
+ */
+
+/*
+ * Disc Record at disc address 0xc00
+ */
+struct adfs_discrecord {
+ unsigned char log2secsize;
+ unsigned char secspertrack;
+ unsigned char heads;
+ unsigned char density;
+ unsigned char idlen;
+ unsigned char log2bpmb;
+ unsigned char skew;
+ unsigned char bootoption;
+ unsigned char lowsector;
+ unsigned char nzones;
+ unsigned short zone_spare;
+ unsigned long root;
+ unsigned long disc_size;
+ unsigned short disc_id;
+ unsigned char disc_name[10];
+ unsigned long disc_type;
+ unsigned long disc_size_high;
+ unsigned char log2sharesize:4;
+ unsigned char unused:4;
+ unsigned char big_flag:1;
+};
+
+#define ADFS_DISCRECORD (0xc00)
+#define ADFS_DR_OFFSET (0x1c0)
+#define ADFS_DR_SIZE 60
+#define ADFS_SUPER_MAGIC 0xadf5
+#define ADFS_FREE_FRAG 0
+#define ADFS_BAD_FRAG 1
+#define ADFS_ROOT_FRAG 2
+
+/*
+ * Directory header
+ */
+struct adfs_dirheader {
+ unsigned char startmasseq;
+ unsigned char startname[4];
+};
+
+#define ADFS_NEWDIR_SIZE 2048
+#define ADFS_OLDDIR_SIZE 1024
+#define ADFS_NUM_DIR_ENTRIES 77
+
+/*
+ * Directory entries
+ */
+struct adfs_direntry {
+ char dirobname[10];
+#define ADFS_NAME_LEN 10
+ __u8 dirload[4];
+ __u8 direxec[4];
+ __u8 dirlen[4];
+ __u8 dirinddiscadd[3];
+ __u8 newdiratts;
+#define ADFS_NDA_OWNER_READ (1 << 0)
+#define ADFS_NDA_OWNER_WRITE (1 << 1)
+#define ADFS_NDA_LOCKED (1 << 2)
+#define ADFS_NDA_DIRECTORY (1 << 3)
+#define ADFS_NDA_EXECUTE (1 << 4)
+#define ADFS_NDA_PUBLIC_READ (1 << 5)
+#define ADFS_NDA_PUBLIC_WRITE (1 << 6)
+};
+
+#define ADFS_MAX_NAME_LEN 255
+struct adfs_idir_entry {
+ __u32 inode_no; /* Address */
+ __u32 file_id; /* file id */
+ __u32 name_len; /* name length */
+ __u32 size; /* size */
+ __u32 mtime; /* modification time */
+ __u32 filetype; /* RiscOS file type */
+ __u8 mode; /* internal mode */
+ char name[ADFS_MAX_NAME_LEN]; /* file name */
+};
+
+/*
+ * Directory tail
+ */
+union adfs_dirtail {
+ struct {
+ unsigned char dirlastmask;
+ char dirname[10];
+ unsigned char dirparent[3];
+ char dirtitle[19];
+ unsigned char reserved[14];
+ unsigned char endmasseq;
+ unsigned char endname[4];
+ unsigned char dircheckbyte;
+ } old;
+ struct {
+ unsigned char dirlastmask;
+ unsigned char reserved[2];
+ unsigned char dirparent[3];
+ char dirtitle[19];
+ char dirname[10];
+ unsigned char endmasseq;
+ unsigned char endname[4];
+ unsigned char dircheckbyte;
+ } new;
+};
+
+#ifdef __KERNEL__
+/*
+ * Calculate the boot block checksum on an ADFS drive. Note that this will
+ * appear to be correct if the sector contains all zeros, so also check that
+ * the disk size is non-zero!!!
+ */
+extern inline int adfs_checkbblk(unsigned char *ptr)
+{
+ unsigned int result = 0;
+ unsigned char *p = ptr + 511;
+
+ do {
+ result = (result & 0xff) + (result >> 8);
+ result = result + *--p;
+ } while (p != ptr);
+
+ return (result & 0xff) != ptr[511];
+}
+
+/* dir.c */
+extern unsigned int adfs_val (unsigned char *p, int len);
+extern int adfs_dir_read_parent (struct inode *inode, struct buffer_head **bhp);
+extern int adfs_dir_read (struct inode *inode, struct buffer_head **bhp);
+extern int adfs_dir_check (struct inode *inode, struct buffer_head **bhp,
+ int buffers, union adfs_dirtail *dtp);
+extern void adfs_dir_free (struct buffer_head **bhp, int buffers);
+extern int adfs_dir_get (struct super_block *sb, struct buffer_head **bhp,
+ int buffers, int pos, unsigned long parent_object_id,
+ struct adfs_idir_entry *ide);
+extern int adfs_dir_find_entry (struct super_block *sb, struct buffer_head **bhp,
+ int buffers, unsigned int index,
+ struct adfs_idir_entry *ide);
+
+/* inode.c */
+extern int adfs_inode_validate (struct inode *inode);
+extern unsigned long adfs_inode_generate (unsigned long parent_id, int diridx);
+extern unsigned long adfs_inode_objid (struct inode *inode);
+extern unsigned int adfs_parent_bmap (struct inode *inode, int block);
+extern int adfs_bmap (struct inode *inode, int block);
+extern void adfs_read_inode (struct inode *inode);
+
+/* map.c */
+extern int adfs_map_lookup (struct super_block *sb, int frag_id, int offset);
+
+/* namei.c */
+extern struct dentry *adfs_lookup (struct inode *dir, struct dentry *dentry);
+
+/* super.c */
+extern int init_adfs_fs (void);
+extern void adfs_error (struct super_block *, const char *, const char *, ...);
+
+/*
+ * Inodes and file operations
+ */
+
+/* dir.c */
+extern struct inode_operations adfs_dir_inode_operations;
+
+/* file.c */
+extern struct inode_operations adfs_file_inode_operations;
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/adfs_fs_i.h b/pfinet/linux-src/include/linux/adfs_fs_i.h
new file mode 100644
index 00000000..83157516
--- /dev/null
+++ b/pfinet/linux-src/include/linux/adfs_fs_i.h
@@ -0,0 +1,17 @@
+/*
+ * linux/include/linux/adfs_fs_i.h
+ *
+ * Copyright (C) 1997 Russell King
+ */
+
+#ifndef _ADFS_FS_I
+#define _ADFS_FS_I
+
+/*
+ * adfs file system inode data in memory
+ */
+struct adfs_inode_info {
+ unsigned long file_id; /* id of fragments containing actual data */
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/adfs_fs_sb.h b/pfinet/linux-src/include/linux/adfs_fs_sb.h
new file mode 100644
index 00000000..649b61e4
--- /dev/null
+++ b/pfinet/linux-src/include/linux/adfs_fs_sb.h
@@ -0,0 +1,33 @@
+/*
+ * linux/include/linux/adfs_fs_sb.h
+ *
+ * Copyright (C) 1997 Russell King
+ */
+
+#ifndef _ADFS_FS_SB
+#define _ADFS_FS_SB
+
+#include <linux/adfs_fs.h>
+
+/*
+ * adfs file system superblock data in memory
+ */
+struct adfs_sb_info {
+ struct buffer_head *s_sbh; /* buffer head containing disc record */
+ struct adfs_discrecord *s_dr; /* pointer to disc record in s_sbh */
+ uid_t s_uid; /* owner uid */
+ gid_t s_gid; /* owner gid */
+ int s_owner_mask; /* ADFS Owner perm -> unix perm */
+ int s_other_mask; /* ADFS Other perm -> unix perm */
+ __u16 s_zone_size; /* size of a map zone in bits */
+ __u16 s_ids_per_zone; /* max. no ids in one zone */
+ __u32 s_idlen; /* length of ID in map */
+ __u32 s_map_size; /* size of a map */
+ __u32 s_zonesize; /* zone size (in map bits) */
+ __u32 s_map_block; /* block address of map */
+ struct buffer_head **s_map; /* bh list containing map */
+ __u32 s_root; /* root disc address */
+ __s8 s_map2blk; /* shift left by this for map->sector */
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/affs_fs.h b/pfinet/linux-src/include/linux/affs_fs.h
new file mode 100644
index 00000000..342ac264
--- /dev/null
+++ b/pfinet/linux-src/include/linux/affs_fs.h
@@ -0,0 +1,115 @@
+#ifndef _AFFS_FS_H
+#define _AFFS_FS_H
+/*
+ * The affs filesystem constants/structures
+ */
+
+#include <linux/types.h>
+
+#define AFFS_SUPER_MAGIC 0xadff
+
+/* Get the filesystem block size given an inode. */
+#define AFFS_I2BSIZE(inode) ((inode)->i_sb->s_blocksize)
+
+/* Get the filesystem hash table size given an inode. */
+#define AFFS_I2HSIZE(inode) ((inode)->i_sb->u.affs_sb.s_hashsize)
+
+/* Get the block number bits given an inode */
+#define AFFS_I2BITS(inode) ((inode)->i_sb->s_blocksize_bits)
+
+/* Get the fs type given an inode */
+#define AFFS_I2FSTYPE(inode) ((inode)->i_sb->u.affs_sb.s_flags & SF_INTL)
+
+struct DateStamp
+{
+ u32 ds_Days;
+ u32 ds_Minute;
+ u32 ds_Tick;
+};
+
+/* --- Prototypes ----------------------------------------------------------------------------- */
+
+/* amigaffs.c */
+
+extern int affs_get_key_entry(int bsize, void *data, int entry_pos);
+extern int affs_get_file_name(int bsize, void *fh_data, unsigned char **name);
+extern u32 affs_checksum_block(int bsize, void *data, s32 *ptype, s32 *stype);
+extern void affs_fix_checksum(int bsize, void *data, int cspos);
+extern void secs_to_datestamp(time_t secs, struct DateStamp *ds);
+extern int prot_to_mode(unsigned int prot);
+extern u32 mode_to_prot(int mode);
+extern int affs_insert_hash(unsigned long dir_ino, struct buffer_head *header,
+ struct inode *inode);
+extern int affs_remove_hash(struct buffer_head *bh, struct inode *inode);
+extern int affs_remove_link(struct buffer_head *bh, struct inode *inode);
+extern int affs_remove_header(struct buffer_head *bh, struct inode *inode);
+extern void affs_error(struct super_block *sb, const char *function, const char *fmt, ...);
+extern void affs_warning(struct super_block *sb, const char *function, const char *fmt, ...);
+extern int affs_check_name(const unsigned char *name, int len);
+extern int affs_copy_name(unsigned char *bstr, const unsigned char *name);
+
+/* bitmap. c */
+
+extern int affs_count_free_blocks(struct super_block *s);
+extern int affs_count_free_bits(int blocksize, const char *data);
+extern void affs_free_block(struct super_block *sb, s32 block);
+extern s32 affs_new_header(struct inode *inode);
+extern s32 affs_new_data(struct inode *inode);
+extern void affs_make_zones(struct super_block *sb);
+
+/* namei.c */
+
+extern int affs_hash_name(const unsigned char *name, int len, int intl, int hashsize);
+extern struct dentry *affs_lookup(struct inode *dir, struct dentry *dentry);
+extern int affs_unlink(struct inode *dir, struct dentry *dentry);
+extern int affs_create(struct inode *dir, struct dentry *dentry, int mode);
+extern int affs_mkdir(struct inode *dir, struct dentry *dentry, int mode);
+extern int affs_rmdir(struct inode *dir, struct dentry *dentry);
+extern int affs_link(struct dentry *olddentry, struct inode *dir,
+ struct dentry *dentry);
+extern int affs_symlink(struct inode *dir, struct dentry *dentry,
+ const char *symname);
+extern int affs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry);
+
+/* inode.c */
+
+extern struct buffer_head *affs_bread(kdev_t dev, int block, int size);
+extern void affs_brelse(struct buffer_head *buf);
+extern unsigned long affs_parent_ino(struct inode *dir);
+extern struct inode *affs_new_inode(const struct inode *dir);
+extern int affs_notify_change(struct dentry *dentry, struct iattr *attr);
+extern int affs_add_entry(struct inode *dir, struct inode *link,
+ struct inode *inode, struct dentry *dentry, s32 type);
+extern void affs_put_inode(struct inode *inode);
+extern void affs_delete_inode(struct inode *inode);
+extern void affs_read_inode(struct inode *inode);
+extern void affs_write_inode(struct inode *inode);
+
+/* super.c */
+
+extern int affs_fs(void);
+extern int init_affs_fs(void);
+
+/* file.c */
+
+void affs_free_prealloc(struct inode *inode);
+extern void affs_truncate(struct inode *);
+
+/* dir.c */
+
+extern void affs_dir_truncate(struct inode *);
+
+/* jump tables */
+
+extern struct inode_operations affs_file_inode_operations;
+extern struct inode_operations affs_file_inode_operations_ofs;
+extern struct inode_operations affs_dir_inode_operations;
+extern struct inode_operations affs_symlink_inode_operations;
+extern struct inode_operations affs_chrdev_inode_operations;
+extern struct inode_operations affs_blkdev_inode_operations;
+
+extern struct dentry_operations affs_dentry_operations;
+extern struct dentry_operations affs_dentry_operations_intl;
+
+#endif
diff --git a/pfinet/linux-src/include/linux/affs_fs_i.h b/pfinet/linux-src/include/linux/affs_fs_i.h
new file mode 100644
index 00000000..beeabb0d
--- /dev/null
+++ b/pfinet/linux-src/include/linux/affs_fs_i.h
@@ -0,0 +1,47 @@
+#ifndef _AFFS_FS_I
+#define _AFFS_FS_I
+
+#include <linux/a.out.h>
+#include <linux/time.h>
+
+#define AFFS_MAX_PREALLOC 16 /* MUST be a power of 2 */
+#define AFFS_KCSIZE 73 /* Allows for 1 extension block at 512 byte-blocks */
+
+struct key_cache {
+ struct timeval kc_lru_time; /* Last time this cache was used */
+ s32 kc_first; /* First cached key */
+ s32 kc_last; /* Last cached key */
+ s32 kc_this_key; /* Key of extension block this data block keys are from */
+ int kc_this_seq; /* Sequence number of this extension block */
+ s32 kc_next_key; /* Key of next extension block */
+ s32 kc_keys[AFFS_KCSIZE]; /* Key cache */
+};
+
+#define EC_SIZE (PAGE_SIZE - 4 * sizeof(struct key_cache) - 4) / 4
+
+struct ext_cache {
+ struct key_cache kc[4]; /* The 4 key caches */
+ s32 ec[EC_SIZE]; /* Keys of assorted extension blocks */
+ int max_ext; /* Index of last known extension block */
+};
+
+/*
+ * affs fs inode data in memory
+ */
+struct affs_inode_info {
+ u32 i_protect; /* unused attribute bits */
+ s32 i_parent; /* parent ino */
+ s32 i_original; /* if != 0, this is the key of the original */
+ s32 i_data[AFFS_MAX_PREALLOC]; /* preallocated blocks */
+ struct ext_cache *i_ec; /* Cache gets allocated dynamically */
+ int i_cache_users; /* Cache cannot be freed while > 0 */
+ int i_lastblock; /* last allocated block */
+ short i_pa_cnt; /* number of preallocated blocks */
+ short i_pa_next; /* Index of next block in i_data[] */
+ short i_pa_last; /* Index of next free slot in i_data[] */
+ short i_zone; /* write zone */
+ unsigned char i_hlink; /* This is a fake */
+ unsigned char i_pad;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/affs_fs_sb.h b/pfinet/linux-src/include/linux/affs_fs_sb.h
new file mode 100644
index 00000000..a066aee7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/affs_fs_sb.h
@@ -0,0 +1,74 @@
+#ifndef _AFFS_FS_SB
+#define _AFFS_FS_SB
+
+/*
+ * super-block data in memory
+ *
+ * Block numbers are adjusted for their actual size
+ *
+ */
+
+#define MAX_ZONES 8
+#define AFFS_DATA_MIN_FREE 512 /* Number of free blocks in zone for data blocks */
+#define AFFS_HDR_MIN_FREE 128 /* Same for header blocks */
+#define AFFS_ZONE_SIZE 1024 /* Blocks per alloc zone, must be multiple of 32 */
+
+struct affs_bm_info {
+ struct buffer_head *bm_bh; /* Buffer head if loaded (bm_count > 0) */
+ s32 bm_firstblk; /* Block number of first bit in this map */
+ s32 bm_key; /* Disk block number */
+ int bm_count; /* Usage counter */
+};
+
+struct affs_alloc_zone {
+ short az_size; /* Size of this allocation zone in double words */
+ short az_count; /* Number of users */
+ int az_free; /* Free blocks in here (no. of bits) */
+};
+
+struct affs_zone {
+ unsigned long z_ino; /* Associated inode number */
+ struct affs_bm_info *z_bm; /* Zone lies in this bitmap */
+ int z_start; /* Index of first word in bitmap */
+ int z_end; /* Index of last word in zone + 1 */
+ int z_az_no; /* Zone number */
+ unsigned long z_lru_time; /* Time of last usage */
+};
+
+struct affs_sb_info {
+ int s_partition_size; /* Partition size in blocks. */
+ int s_blksize; /* Initial device blksize */
+ s32 s_root_block; /* FFS root block number. */
+ int s_hashsize; /* Size of hash table. */
+ unsigned long s_flags; /* See below. */
+ s16 s_uid; /* uid to override */
+ s16 s_gid; /* gid to override */
+ umode_t s_mode; /* mode to override */
+ int s_reserved; /* Number of reserved blocks. */
+ struct buffer_head *s_root_bh; /* Cached root block. */
+ struct affs_bm_info *s_bitmap; /* Bitmap infos. */
+ int s_bm_count; /* Number of bitmap blocks. */
+ int s_nextzone; /* Next zone to look for free blocks. */
+ int s_num_az; /* Total number of alloc zones. */
+ struct affs_zone *s_zones; /* The zones themselves. */
+ struct affs_alloc_zone *s_alloc;/* The allocation zones. */
+ char *s_zonemap; /* Bitmap for allocation zones. */
+ char *s_prefix; /* Prefix for volumes and assigns. */
+ int s_prefix_len; /* Length of prefix. */
+ char s_volume[32]; /* Volume prefix for absolute symlinks. */
+};
+
+#define SF_INTL 0x0001 /* International filesystem. */
+#define SF_BM_VALID 0x0002 /* Bitmap is valid. */
+#define SF_IMMUTABLE 0x0004 /* Protection bits cannot be changed */
+#define SF_QUIET 0x0008 /* chmod errors will be not reported */
+#define SF_SETUID 0x0010 /* Ignore Amiga uid */
+#define SF_SETGID 0x0020 /* Ignore Amiga gid */
+#define SF_SETMODE 0x0040 /* Ignore Amiga protection bits */
+#define SF_MUFS 0x0100 /* Use MUFS uid/gid mapping */
+#define SF_OFS 0x0200 /* Old filesystem */
+#define SF_PREFIX 0x0400 /* Buffer for prefix is allocated */
+#define SF_VERBOSE 0x0800 /* Talk about fs when mounting */
+#define SF_READONLY 0x1000 /* Don't allow to remount rw */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/affs_hardblocks.h b/pfinet/linux-src/include/linux/affs_hardblocks.h
new file mode 100644
index 00000000..ae893e02
--- /dev/null
+++ b/pfinet/linux-src/include/linux/affs_hardblocks.h
@@ -0,0 +1,66 @@
+#ifndef AFFS_HARDBLOCKS_H
+#define AFFS_HARDBLOCKS_H
+
+/* Just the needed definitions for the RDB of an Amiga HD. */
+
+struct RigidDiskBlock {
+ u32 rdb_ID;
+ u32 rdb_SummedLongs;
+ s32 rdb_ChkSum;
+ u32 rdb_HostID;
+ u32 rdb_BlockBytes;
+ u32 rdb_Flags;
+ u32 rdb_BadBlockList;
+ u32 rdb_PartitionList;
+ u32 rdb_FileSysHeaderList;
+ u32 rdb_DriveInit;
+ u32 rdb_Reserved1[6];
+ u32 rdb_Cylinders;
+ u32 rdb_Sectors;
+ u32 rdb_Heads;
+ u32 rdb_Interleave;
+ u32 rdb_Park;
+ u32 rdb_Reserved2[3];
+ u32 rdb_WritePreComp;
+ u32 rdb_ReducedWrite;
+ u32 rdb_StepRate;
+ u32 rdb_Reserved3[5];
+ u32 rdb_RDBBlocksLo;
+ u32 rdb_RDBBlocksHi;
+ u32 rdb_LoCylinder;
+ u32 rdb_HiCylinder;
+ u32 rdb_CylBlocks;
+ u32 rdb_AutoParkSeconds;
+ u32 rdb_HighRDSKBlock;
+ u32 rdb_Reserved4;
+ char rdb_DiskVendor[8];
+ char rdb_DiskProduct[16];
+ char rdb_DiskRevision[4];
+ char rdb_ControllerVendor[8];
+ char rdb_ControllerProduct[16];
+ char rdb_ControllerRevision[4];
+ u32 rdb_Reserved5[10];
+};
+
+#define IDNAME_RIGIDDISK 0x5244534B /* "RDSK" */
+
+struct PartitionBlock {
+ u32 pb_ID;
+ u32 pb_SummedLongs;
+ s32 pb_ChkSum;
+ u32 pb_HostID;
+ u32 pb_Next;
+ u32 pb_Flags;
+ u32 pb_Reserved1[2];
+ u32 pb_DevFlags;
+ u8 pb_DriveName[32];
+ u32 pb_Reserved2[15];
+ u32 pb_Environment[17];
+ u32 pb_EReserved[15];
+};
+
+#define IDNAME_PARTITION 0x50415254 /* "PART" */
+
+#define RDB_ALLOCATION_LIMIT 16
+
+#endif /* AFFS_HARDBLOCKS_H */
diff --git a/pfinet/linux-src/include/linux/amifd.h b/pfinet/linux-src/include/linux/amifd.h
new file mode 100644
index 00000000..491bdd86
--- /dev/null
+++ b/pfinet/linux-src/include/linux/amifd.h
@@ -0,0 +1,61 @@
+#ifndef _AMIFD_H
+#define _AMIFD_H
+
+/* Definitions for the Amiga floppy driver */
+
+#include <linux/fd.h>
+
+#define FD_MAX_UNITS 4 /* Max. Number of drives */
+#define FLOPPY_MAX_SECTORS 22 /* Max. Number of sectors per track */
+
+#ifndef ASSEMBLER
+
+struct fd_data_type {
+ char *name; /* description of data type */
+ int sects; /* sectors per track */
+#ifdef __STDC__
+ int (*read_fkt)(int);
+ void (*write_fkt)(int);
+#else
+ int (*read_fkt)(); /* read whole track */
+ void (*write_fkt)(); /* write whole track */
+#endif
+};
+
+/*
+** Floppy type descriptions
+*/
+
+struct fd_drive_type {
+ unsigned long code; /* code returned from drive */
+ char *name; /* description of drive */
+ unsigned int tracks; /* number of tracks */
+ unsigned int heads; /* number of heads */
+ unsigned int read_size; /* raw read size for one track */
+ unsigned int write_size; /* raw write size for one track */
+ unsigned int sect_mult; /* sectors and gap multiplier (HD = 2) */
+ unsigned int precomp1; /* start track for precomp 1 */
+ unsigned int precomp2; /* start track for precomp 2 */
+ unsigned int step_delay; /* time (in ms) for delay after step */
+ unsigned int settle_time; /* time to settle after dir change */
+ unsigned int side_time; /* time needed to change sides */
+};
+
+struct amiga_floppy_struct {
+ struct fd_drive_type *type; /* type of floppy for this unit */
+ struct fd_data_type *dtype; /* type of floppy for this unit */
+ int track; /* current track (-1 == unknown) */
+ unsigned char *trackbuf; /* current track (kmaloc()'d */
+
+ int blocks; /* total # blocks on disk */
+
+ int changed; /* true when not known */
+ int disk; /* disk in drive (-1 == unknown) */
+ int motor; /* true when motor is at speed */
+ int busy; /* true when drive is active */
+ int dirty; /* true when trackbuf is not on disk */
+ int status; /* current error code for unit */
+};
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/amifdreg.h b/pfinet/linux-src/include/linux/amifdreg.h
new file mode 100644
index 00000000..76188bf4
--- /dev/null
+++ b/pfinet/linux-src/include/linux/amifdreg.h
@@ -0,0 +1,81 @@
+#ifndef _LINUX_AMIFDREG_H
+#define _LINUX_AMIFDREG_H
+
+/*
+** CIAAPRA bits (read only)
+*/
+
+#define DSKRDY (0x1<<5) /* disk ready when low */
+#define DSKTRACK0 (0x1<<4) /* head at track zero when low */
+#define DSKPROT (0x1<<3) /* disk protected when low */
+#define DSKCHANGE (0x1<<2) /* low when disk removed */
+
+/*
+** CIAAPRB bits (read/write)
+*/
+
+#define DSKMOTOR (0x1<<7) /* motor on when low */
+#define DSKSEL3 (0x1<<6) /* select drive 3 when low */
+#define DSKSEL2 (0x1<<5) /* select drive 2 when low */
+#define DSKSEL1 (0x1<<4) /* select drive 1 when low */
+#define DSKSEL0 (0x1<<3) /* select drive 0 when low */
+#define DSKSIDE (0x1<<2) /* side selection: 0 = upper, 1 = lower */
+#define DSKDIREC (0x1<<1) /* step direction: 0=in, 1=out (to trk 0) */
+#define DSKSTEP (0x1) /* pulse low to step head 1 track */
+
+/*
+** DSKBYTR bits (read only)
+*/
+
+#define DSKBYT (1<<15) /* register contains valid byte when set */
+#define DMAON (1<<14) /* disk DMA enabled */
+#define DISKWRITE (1<<13) /* disk write bit in DSKLEN enabled */
+#define WORDEQUAL (1<<12) /* DSKSYNC register match when true */
+/* bits 7-0 are data */
+
+/*
+** ADKCON/ADKCONR bits
+*/
+
+#ifndef SETCLR
+#define ADK_SETCLR (1<<15) /* control bit */
+#endif
+#define ADK_PRECOMP1 (1<<14) /* precompensation selection */
+#define ADK_PRECOMP0 (1<<13) /* 00=none, 01=140ns, 10=280ns, 11=500ns */
+#define ADK_MFMPREC (1<<12) /* 0=GCR precomp., 1=MFM precomp. */
+#define ADK_WORDSYNC (1<<10) /* enable DSKSYNC auto DMA */
+#define ADK_MSBSYNC (1<<9) /* when 1, enable sync on MSbit (for GCR) */
+#define ADK_FAST (1<<8) /* bit cell: 0=2us (GCR), 1=1us (MFM) */
+
+/*
+** DSKLEN bits
+*/
+
+#define DSKLEN_DMAEN (1<<15)
+#define DSKLEN_WRITE (1<<14)
+
+/*
+** INTENA/INTREQ bits
+*/
+
+#define DSKINDEX (0x1<<4) /* DSKINDEX bit */
+
+/*
+** Misc
+*/
+
+#define MFM_SYNC 0x4489 /* standard MFM sync value */
+
+/* Values for FD_COMMAND */
+#define FD_RECALIBRATE 0x07 /* move to track 0 */
+#define FD_SEEK 0x0F /* seek track */
+#define FD_READ 0xE6 /* read with MT, MFM, SKip deleted */
+#define FD_WRITE 0xC5 /* write with MT, MFM */
+#define FD_SENSEI 0x08 /* Sense Interrupt Status */
+#define FD_SPECIFY 0x03 /* specify HUT etc */
+#define FD_FORMAT 0x4D /* format one track */
+#define FD_VERSION 0x10 /* get version code */
+#define FD_CONFIGURE 0x13 /* configure FIFO operation */
+#define FD_PERPENDICULAR 0x12 /* perpendicular r/w mode */
+
+#endif /* _LINUX_AMIFDREG_H */
diff --git a/pfinet/linux-src/include/linux/amigaffs.h b/pfinet/linux-src/include/linux/amigaffs.h
new file mode 100644
index 00000000..a6b16e06
--- /dev/null
+++ b/pfinet/linux-src/include/linux/amigaffs.h
@@ -0,0 +1,228 @@
+#ifndef AMIGAFFS_H
+#define AMIGAFFS_H
+
+#include <asm/byteorder.h>
+#include <linux/types.h>
+
+/* AmigaOS allows file names with up to 30 characters length.
+ * Names longer than that will be silently truncated. If you
+ * want to disallow this, comment out the following #define.
+ * Creating filesystem objects with longer names will then
+ * result in an error (ENAMETOOLONG).
+ */
+/*#define AFFS_NO_TRUNCATE */
+
+/* Ugly macros make the code more pretty. */
+
+#define GET_END_PTR(st,p,sz) ((st *)((char *)(p)+((sz)-sizeof(st))))
+#define AFFS_GET_HASHENTRY(data,hashkey) be32_to_cpu(((struct dir_front *)data)->hashtable[hashkey])
+#define AFFS_BLOCK(data,ino,blk) ((struct file_front *)data)->blocks[AFFS_I2HSIZE(ino)-1-(blk)]
+
+#define FILE_END(p,i) GET_END_PTR(struct file_end,p,AFFS_I2BSIZE(i))
+#define ROOT_END(p,i) GET_END_PTR(struct root_end,p,AFFS_I2BSIZE(i))
+#define DIR_END(p,i) GET_END_PTR(struct dir_end,p,AFFS_I2BSIZE(i))
+#define LINK_END(p,i) GET_END_PTR(struct hlink_end,p,AFFS_I2BSIZE(i))
+#define ROOT_END_S(p,s) GET_END_PTR(struct root_end,p,(s)->s_blocksize)
+#define DATA_FRONT(bh) ((struct data_front *)(bh)->b_data)
+#define DIR_FRONT(bh) ((struct dir_front *)(bh)->b_data)
+
+/* Only for easier debugging if need be */
+#define affs_bread bread
+#define affs_brelse brelse
+
+#ifdef __LITTLE_ENDIAN
+#define BO_EXBITS 0x18UL
+#elif defined(__BIG_ENDIAN)
+#define BO_EXBITS 0x00UL
+#else
+#error Endianness must be known for affs to work.
+#endif
+
+#define FS_OFS 0x444F5300
+#define FS_FFS 0x444F5301
+#define FS_INTLOFS 0x444F5302
+#define FS_INTLFFS 0x444F5303
+#define FS_DCOFS 0x444F5304
+#define FS_DCFFS 0x444F5305
+#define MUFS_FS 0x6d754653 /* 'muFS' */
+#define MUFS_OFS 0x6d754600 /* 'muF\0' */
+#define MUFS_FFS 0x6d754601 /* 'muF\1' */
+#define MUFS_INTLOFS 0x6d754602 /* 'muF\2' */
+#define MUFS_INTLFFS 0x6d754603 /* 'muF\3' */
+#define MUFS_DCOFS 0x6d754604 /* 'muF\4' */
+#define MUFS_DCFFS 0x6d754605 /* 'muF\5' */
+
+#define T_SHORT 2
+#define T_LIST 16
+#define T_DATA 8
+
+#define ST_LINKFILE -4
+#define ST_FILE -3
+#define ST_ROOT 1
+#define ST_USERDIR 2
+#define ST_SOFTLINK 3
+#define ST_LINKDIR 4
+
+struct root_front
+{
+ s32 primary_type;
+ s32 spare1[2];
+ s32 hash_size;
+ s32 spare2;
+ u32 checksum;
+ s32 hashtable[0];
+};
+
+struct root_end
+{
+ s32 bm_flag;
+ s32 bm_keys[25];
+ s32 bm_extend;
+ struct DateStamp dir_altered;
+ u8 disk_name[40];
+ struct DateStamp disk_altered;
+ struct DateStamp disk_made;
+ s32 spare1[3];
+ s32 secondary_type;
+};
+
+struct dir_front
+{
+ s32 primary_type;
+ s32 own_key;
+ s32 spare1[3];
+ u32 checksum;
+ s32 hashtable[0];
+};
+
+struct dir_end
+{
+ s32 spare1;
+ s16 owner_uid;
+ s16 owner_gid;
+ u32 protect;
+ s32 spare2;
+ u8 comment[92];
+ struct DateStamp created;
+ u8 dir_name[32];
+ s32 spare3[2];
+ s32 link_chain;
+ s32 spare4[5];
+ s32 hash_chain;
+ s32 parent;
+ s32 spare5;
+ s32 secondary_type;
+};
+
+struct file_front
+{
+ s32 primary_type;
+ s32 own_key;
+ s32 block_count;
+ s32 unknown1;
+ s32 first_data;
+ u32 checksum;
+ s32 blocks[0];
+};
+
+struct file_end
+{
+ s32 spare1;
+ s16 owner_uid;
+ s16 owner_gid;
+ u32 protect;
+ s32 byte_size;
+ u8 comment[92];
+ struct DateStamp created;
+ u8 file_name[32];
+ s32 spare2;
+ s32 original; /* not really in file_end */
+ s32 link_chain;
+ s32 spare3[5];
+ s32 hash_chain;
+ s32 parent;
+ s32 extension;
+ s32 secondary_type;
+};
+
+struct hlink_front
+{
+ s32 primary_type;
+ s32 own_key;
+ s32 spare1[3];
+ u32 checksum;
+};
+
+struct hlink_end
+{
+ s32 spare1;
+ s16 owner_uid;
+ s16 owner_gid;
+ u32 protect;
+ u8 comment[92];
+ struct DateStamp created;
+ u8 link_name[32];
+ s32 spare2;
+ s32 original;
+ s32 link_chain;
+ s32 spare3[5];
+ s32 hash_chain;
+ s32 parent;
+ s32 spare4;
+ s32 secondary_type;
+};
+
+struct slink_front
+{
+ s32 primary_type;
+ s32 own_key;
+ s32 spare1[3];
+ s32 checksum;
+ u8 symname[288]; /* depends on block size */
+};
+
+struct data_front
+{
+ s32 primary_type;
+ s32 header_key;
+ s32 sequence_number;
+ s32 data_size;
+ s32 next_data;
+ s32 checksum;
+ u8 data[488]; /* depends on block size */
+};
+
+/* Permission bits */
+
+#define FIBF_OTR_READ 0x8000
+#define FIBF_OTR_WRITE 0x4000
+#define FIBF_OTR_EXECUTE 0x2000
+#define FIBF_OTR_DELETE 0x1000
+#define FIBF_GRP_READ 0x0800
+#define FIBF_GRP_WRITE 0x0400
+#define FIBF_GRP_EXECUTE 0x0200
+#define FIBF_GRP_DELETE 0x0100
+
+#define FIBF_SCRIPT 0x0040
+#define FIBF_PURE 0x0020 /* no use under linux */
+#define FIBF_ARCHIVE 0x0010 /* never set, always cleared on write */
+#define FIBF_READ 0x0008 /* 0 means allowed */
+#define FIBF_WRITE 0x0004 /* 0 means allowed */
+#define FIBF_EXECUTE 0x0002 /* 0 means allowed, ignored under linux */
+#define FIBF_DELETE 0x0001 /* 0 means allowed */
+
+#define FIBF_OWNER 0x000F /* Bits pertaining to owner */
+
+#define AFFS_UMAYWRITE(prot) (((prot) & (FIBF_WRITE|FIBF_DELETE)) == (FIBF_WRITE|FIBF_DELETE))
+#define AFFS_UMAYREAD(prot) ((prot) & FIBF_READ)
+#define AFFS_UMAYEXECUTE(prot) ((prot) & FIBF_EXECUTE)
+#define AFFS_GMAYWRITE(prot) (((prot)&(FIBF_GRP_WRITE|FIBF_GRP_DELETE))==\
+ (FIBF_GRP_WRITE|FIBF_GRP_DELETE))
+#define AFFS_GMAYREAD(prot) ((prot) & FIBF_GRP_READ)
+#define AFFS_GMAYEXECUTE(prot) ((prot) & FIBF_EXECUTE)
+#define AFFS_OMAYWRITE(prot) (((prot)&(FIBF_OTR_WRITE|FIBF_OTR_DELETE))==\
+ (FIBF_OTR_WRITE|FIBF_OTR_DELETE))
+#define AFFS_OMAYREAD(prot) ((prot) & FIBF_OTR_READ)
+#define AFFS_OMAYEXECUTE(prot) ((prot) & FIBF_EXECUTE)
+
+#endif
diff --git a/pfinet/linux-src/include/linux/apm_bios.h b/pfinet/linux-src/include/linux/apm_bios.h
new file mode 100644
index 00000000..caf0b4b7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/apm_bios.h
@@ -0,0 +1,144 @@
+#ifndef _LINUX_APM_H
+#define _LINUX_APM_H
+
+/*
+ * Include file for the interface to an APM BIOS
+ * Copyright 1994-1999 Stephen Rothwell (sfr@linuxcare.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+typedef unsigned short apm_event_t;
+typedef unsigned short apm_eventinfo_t;
+
+#ifdef __KERNEL__
+
+#define APM_40 0x40
+#define APM_CS (APM_40 + 8)
+#define APM_CS_16 (APM_CS + 8)
+#define APM_DS (APM_CS_16 + 8)
+
+struct apm_bios_info {
+ unsigned short version;
+ unsigned short cseg;
+ unsigned long offset;
+ unsigned short cseg_16;
+ unsigned short dseg;
+ unsigned short flags;
+ unsigned short cseg_len;
+ unsigned short cseg_16_len;
+ unsigned short dseg_len;
+};
+
+ /* Results of APM Installation Check */
+#define APM_16_BIT_SUPPORT 0x0001
+#define APM_32_BIT_SUPPORT 0x0002
+#define APM_IDLE_SLOWS_CLOCK 0x0004
+#define APM_BIOS_DISABLED 0x0008
+#define APM_BIOS_DISENGAGED 0x0010
+
+/*
+ * Maximum number of events stored
+ */
+#define APM_MAX_EVENTS 20
+
+/*
+ * The per-file APM data
+ */
+struct apm_bios_struct {
+ int magic;
+ struct apm_bios_struct * next;
+ int suser;
+ int suspends_pending;
+ int standbys_pending;
+ int suspends_read;
+ int standbys_read;
+ int event_head;
+ int event_tail;
+ apm_event_t events[APM_MAX_EVENTS];
+};
+
+/*
+ * The magic number in apm_bios_struct
+ */
+#define APM_BIOS_MAGIC 0x4101
+
+/*
+ * in init/main.c
+ */
+extern struct apm_bios_info apm_bios_info;
+
+extern void apm_bios_init(void);
+extern void apm_setup(char *, int *);
+
+extern int apm_register_callback(int (*callback)(apm_event_t));
+extern void apm_unregister_callback(int (*callback)(apm_event_t));
+
+extern void apm_power_off(void);
+extern int apm_display_blank(void);
+extern int apm_display_unblank(void);
+
+#endif /* __KERNEL__ */
+
+/*
+ * Power states
+ */
+#define APM_STATE_READY 0x0000
+#define APM_STATE_STANDBY 0x0001
+#define APM_STATE_SUSPEND 0x0002
+#define APM_STATE_OFF 0x0003
+#define APM_STATE_BUSY 0x0004
+#define APM_STATE_REJECT 0x0005
+
+/*
+ * Events (results of Get PM Event)
+ */
+#define APM_SYS_STANDBY 0x0001
+#define APM_SYS_SUSPEND 0x0002
+#define APM_NORMAL_RESUME 0x0003
+#define APM_CRITICAL_RESUME 0x0004
+#define APM_LOW_BATTERY 0x0005
+#define APM_POWER_STATUS_CHANGE 0x0006
+#define APM_UPDATE_TIME 0x0007
+#define APM_CRITICAL_SUSPEND 0x0008
+#define APM_USER_STANDBY 0x0009
+#define APM_USER_SUSPEND 0x000a
+#define APM_STANDBY_RESUME 0x000b
+#define APM_CAPABILITY_CHANGE 0x000c
+
+/*
+ * Error codes
+ */
+#define APM_SUCCESS 0x00
+#define APM_DISABLED 0x01
+#define APM_CONNECTED 0x02
+#define APM_NOT_CONNECTED 0x03
+#define APM_16_CONNECTED 0x05
+#define APM_16_UNSUPPORTED 0x06
+#define APM_32_CONNECTED 0x07
+#define APM_32_UNSUPPORTED 0x08
+#define APM_BAD_DEVICE 0x09
+#define APM_BAD_PARAM 0x0a
+#define APM_NOT_ENGAGED 0x0b
+#define APM_BAD_FUNCTION 0x0c
+#define APM_RESUME_DISABLED 0x0d
+#define APM_NO_ERROR 0x53
+#define APM_BAD_STATE 0x60
+#define APM_NO_EVENTS 0x80
+#define APM_NOT_PRESENT 0x86
+
+/* ioctl operations */
+#include <linux/ioctl.h>
+
+#define APM_IOC_STANDBY _IO('A', 1)
+#define APM_IOC_SUSPEND _IO('A', 2)
+
+#endif /* LINUX_APM_H */
diff --git a/pfinet/linux-src/include/linux/arcdevice.h b/pfinet/linux-src/include/linux/arcdevice.h
new file mode 100644
index 00000000..b4df083e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/arcdevice.h
@@ -0,0 +1,354 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. NET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the ARCnet handlers.
+ *
+ * Version: $Id: arcdevice.h,v 1.3 1997/11/09 11:05:05 mj Exp $
+ *
+ * Authors: Avery Pennarun <apenwarr@bond.net>
+ * David Woodhouse <dwmw2@cam.ac.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+#ifndef _LINUX_ARCDEVICE_H
+#define _LINUX_ARCDEVICE_H
+
+#include <linux/config.h>
+#include <linux/if_arcnet.h>
+
+#ifdef __KERNEL__
+
+#define ARC_20020 1
+#define ARC_RIM_I 2
+#define ARC_90xx 3
+#define ARC_90xx_IO 4
+
+#define MAX_ARCNET_DEVS 8
+
+
+/* The card sends the reconfiguration signal when it loses the connection to
+ * the rest of its network. It is a 'Hello, is anybody there?' cry. This
+ * usually happens when a new computer on the network is powered on or when
+ * the cable is broken.
+ *
+ * Define DETECT_RECONFIGS if you want to detect network reconfigurations.
+ * Recons may be a real nuisance on a larger ARCnet network; if you are a
+ * network administrator you probably would like to count them.
+ * Reconfigurations will be recorded in stats.tx_carrier_errors (the last
+ * field of the /proc/net/dev file).
+ *
+ * Define SHOW_RECONFIGS if you really want to see a log message whenever
+ * a RECON occurs.
+ */
+#define DETECT_RECONFIGS
+#undef SHOW_RECONFIGS
+
+
+/* RECON_THRESHOLD is the maximum number of RECON messages to receive within
+ * one minute before printing a "cabling problem" warning. You must have
+ * DETECT_RECONFIGS enabled if you want to use this. The default value
+ * should be fine.
+ *
+ * After that, a "cabling restored" message will be printed on the next IRQ
+ * if no RECON messages have been received for 10 seconds.
+ *
+ * Do not define RECON_THRESHOLD at all if you want to disable this feature.
+ */
+#define RECON_THRESHOLD 30
+
+
+/* Define this to the minimum "timeout" value. If a transmit takes longer
+ * than TX_TIMEOUT jiffies, Linux will abort the TX and retry. On a large
+ * network, or one with heavy network traffic, this timeout may need to be
+ * increased. The larger it is, though, the longer it will be between
+ * necessary transmits - don't set this too large.
+ */
+#define TX_TIMEOUT (20*HZ/100)
+
+
+/* Display warnings about the driver being an ALPHA version.
+ */
+#undef ALPHA_WARNING
+
+
+/* New debugging bitflags: each option can be enabled individually.
+ *
+ * These can be set while the driver is running by typing:
+ * ifconfig arc0 down metric 1xxx HOSTNAME
+ * where 1xxx is 1000 + the debug level you want
+ * and HOSTNAME is your hostname/ip address
+ * and then resetting your routes.
+ *
+ * An ioctl() should be used for this instead, someday.
+ *
+ * Note: only debug flags included in the ARCNET_DEBUG_MAX define will
+ * actually be available. GCC will (at least, GCC 2.7.0 will) notice
+ * lines using a BUGLVL not in ARCNET_DEBUG_MAX and automatically optimize
+ * them out.
+ */
+#define D_NORMAL 1 /* important operational info */
+#define D_EXTRA 2 /* useful, but non-vital information */
+#define D_INIT 4 /* show init/probe messages */
+#define D_INIT_REASONS 8 /* show reasons for discarding probes */
+/* debug levels below give LOTS of output during normal operation! */
+#define D_DURING 16 /* trace operations (including irq's) */
+#define D_TX 32 /* show tx packets */
+#define D_RX 64 /* show rx packets */
+#define D_SKB 128 /* show skb's */
+
+#ifndef ARCNET_DEBUG_MAX
+#define ARCNET_DEBUG_MAX (~0) /* enable ALL debug messages */
+#endif
+
+#ifndef ARCNET_DEBUG
+#define ARCNET_DEBUG (D_NORMAL|D_EXTRA)
+#endif
+extern int arcnet_debug;
+
+/* macros to simplify debug checking */
+#define BUGLVL(x) if ((ARCNET_DEBUG_MAX)&arcnet_debug&(x))
+#define BUGMSG2(x,msg,args...) do { BUGLVL(x) printk(msg, ## args); } while (0)
+#define BUGMSG(x,msg,args...) \
+ BUGMSG2(x,"%s%6s: " msg, \
+ x==D_NORMAL ? KERN_WARNING : \
+ x<=D_INIT_REASONS ? KERN_INFO : KERN_DEBUG , \
+ dev->name , ## args)
+
+
+#define SETMASK AINTMASK(lp->intmask)
+
+ /* Time needed to resetthe card - in jiffies. This works on my SMC
+ * PC100. I can't find a reference that tells me just how long I
+ * should wait.
+ */
+#define RESETtime (HZ * 3 / 10) /* reset */
+
+ /* these are the max/min lengths of packet data. (including
+ * ClientData header)
+ * note: packet sizes 250, 251, 252 are impossible (God knows why)
+ * so exception packets become necessary.
+ *
+ * These numbers are compared with the length of the full packet,
+ * including ClientData header.
+ */
+#define MTU 253 /* normal packet max size */
+#define MinTU 257 /* extended packet min size */
+#define XMTU 508 /* extended packet max size */
+
+ /* status/interrupt mask bit fields */
+#define TXFREEflag 0x01 /* transmitter available */
+#define TXACKflag 0x02 /* transmitted msg. ackd */
+#define RECONflag 0x04 /* system reconfigured */
+#define TESTflag 0x08 /* test flag */
+#define RESETflag 0x10 /* power-on-reset */
+#define RES1flag 0x20 /* reserved - usually set by jumper */
+#define RES2flag 0x40 /* reserved - usually set by jumper */
+#define NORXflag 0x80 /* receiver inhibited */
+
+ /* Flags used for IO-mapped memory operations */
+#define AUTOINCflag 0x40 /* Increase location with each access */
+#define IOMAPflag 0x02 /* (for 90xx) Use IO mapped memory, not mmap */
+#define ENABLE16flag 0x80 /* (for 90xx) Enable 16-bit mode */
+
+ /* in the command register, the following bits have these meanings:
+ * 0-2 command
+ * 3-4 page number (for enable rcv/xmt command)
+ * 7 receive broadcasts
+ */
+#define NOTXcmd 0x01 /* disable transmitter */
+#define NORXcmd 0x02 /* disable receiver */
+#define TXcmd 0x03 /* enable transmitter */
+#define RXcmd 0x04 /* enable receiver */
+#define CONFIGcmd 0x05 /* define configuration */
+#define CFLAGScmd 0x06 /* clear flags */
+#define TESTcmd 0x07 /* load test flags */
+
+ /* flags for "clear flags" command */
+#define RESETclear 0x08 /* power-on-reset */
+#define CONFIGclear 0x10 /* system reconfigured */
+
+ /* flags for "load test flags" command */
+#define TESTload 0x08 /* test flag (diagnostic) */
+
+ /* byte deposited into first address of buffers on reset */
+#define TESTvalue 0321 /* that's octal for 0xD1 :) */
+
+ /* for "enable receiver" command */
+#define RXbcasts 0x80 /* receive broadcasts */
+
+ /* flags for "define configuration" command */
+#define NORMALconf 0x00 /* 1-249 byte packets */
+#define EXTconf 0x08 /* 250-504 byte packets */
+
+ /* Starts receiving packets into recbuf.
+ */
+#define EnableReceiver() ACOMMAND(RXcmd|(recbuf<<3)|RXbcasts)
+
+
+
+#define JIFFER(time) for (delayval=jiffies+time; time_before(jiffies,delayval);) ;
+
+ /* a complete ARCnet packet */
+union ArcPacket
+{
+ struct archdr hardheader; /* the hardware header */
+ u_char raw[512]; /* raw packet info, incl ClientData */
+};
+
+
+ /* the "client data" header - RFC1201 information
+ * notice that this screws up if it's not an even number of bytes
+ * <sigh>
+ */
+struct ClientData
+{
+ /* data that's NOT part of real packet - we MUST get rid of it before
+ * actually sending!!
+ */
+ u_char saddr, /* Source address - needed for IPX */
+ daddr; /* Destination address */
+
+ /* data that IS part of real packet */
+ u_char protocol_id, /* ARC_P_IP, ARC_P_ARP, etc */
+ split_flag; /* for use with split packets */
+ u_short sequence; /* sequence number */
+};
+#define EXTRA_CLIENTDATA (sizeof(struct ClientData)-4)
+
+
+ /* the "client data" header - RFC1051 information
+ * this also screws up if it's not an even number of bytes
+ * <sigh again>
+ */
+struct S_ClientData
+{
+ /* data that's NOT part of real packet - we MUST get rid of it before
+ * actually sending!!
+ */
+ u_char saddr, /* Source address - needed for IPX */
+ daddr, /* Destination address */
+ junk; /* padding to make an even length */
+
+ /* data that IS part of real packet */
+ u_char protocol_id; /* ARC_P_IP, ARC_P_ARP, etc */
+};
+#define S_EXTRA_CLIENTDATA (sizeof(struct S_ClientData)-1)
+
+
+/* "Incoming" is information needed for each address that could be sending
+ * to us. Mostly for partially-received split packets.
+ */
+struct Incoming
+{
+ struct sk_buff *skb; /* packet data buffer */
+ unsigned char lastpacket, /* number of last packet (from 1) */
+ numpackets; /* number of packets in split */
+ u_short sequence; /* sequence number of assembly */
+};
+
+struct Outgoing
+{
+ struct sk_buff *skb; /* buffer from upper levels */
+ struct ClientData *hdr; /* clientdata of last packet */
+ u_char *data; /* pointer to data in packet */
+ short length, /* bytes total */
+ dataleft, /* bytes left */
+ segnum, /* segment being sent */
+ numsegs, /* number of segments */
+ seglen; /* length of segment */
+};
+
+
+struct arcnet_local {
+ struct net_device_stats stats;
+ u_short sequence; /* sequence number (incs with each packet) */
+ u_short aborted_seq;
+ u_char stationid, /* our 8-bit station address */
+ recbuf, /* receive buffer # (0 or 1) */
+ txbuf, /* transmit buffer # (2 or 3) */
+ txready, /* buffer where a packet is ready to send */
+ config, /* current value of CONFIG register */
+ timeout, /* Extended timeout for COM20020 */
+ backplane, /* Backplane flag for COM20020 */
+ setup, /* Contents of setup register */
+ intmask; /* current value of INTMASK register */
+ short intx, /* in TX routine? */
+ in_txhandler, /* in TX_IRQ handler? */
+ sending, /* transmit in progress? */
+ lastload_dest, /* can last loaded packet be acked? */
+ lasttrans_dest; /* can last TX'd packet be acked? */
+
+#if defined(DETECT_RECONFIGS) && defined(RECON_THRESHOLD)
+ time_t first_recon, /* time of "first" RECON message to count */
+ last_recon; /* time of most recent RECON */
+ int num_recons, /* number of RECONs between first and last. */
+ network_down; /* do we think the network is down? */
+#endif
+
+ struct timer_list timer; /* the timer interrupt struct */
+ struct Incoming incoming[256]; /* one from each address */
+ struct Outgoing outgoing; /* packet currently being sent */
+
+ int card_type;
+ char *card_type_str;
+
+ void (*inthandler) (struct device *dev);
+ int (*arcnet_reset) (struct device *dev, int reset_delay);
+ void (*asetmask) (struct device *dev, u_char mask);
+ void (*acommand) (struct device *dev, u_char command);
+ u_char (*astatus) (struct device *dev);
+ void (*en_dis_able_TX) (struct device *dev, int enable);
+ void (*prepare_tx)(struct device *dev,u_char *hdr,int hdrlen,
+ char *data,int length,int daddr,int exceptA, int offset);
+ void (*openclose_device)(int open);
+
+ struct device *adev; /* RFC1201 protocol device */
+
+ /* These are last to ensure that the chipset drivers don't depend on the
+ * CONFIG_ARCNET_ETH and CONFIG_ARCNET_1051 options.
+ */
+
+#ifdef CONFIG_ARCNET_ETH
+ struct device *edev; /* Ethernet-Encap device */
+#endif
+
+#ifdef CONFIG_ARCNET_1051
+ struct device *sdev; /* RFC1051 protocol device */
+#endif
+};
+
+/* Functions exported by arcnet.c
+ */
+
+#if ARCNET_DEBUG_MAX & D_SKB
+extern void arcnet_dump_skb(struct device *dev,struct sk_buff *skb,
+ char *desc);
+#else
+#define arcnet_dump_skb(dev,skb,desc) ;
+#endif
+
+#if (ARCNET_DEBUG_MAX & D_RX) || (ARCNET_DEBUG_MAX & D_TX)
+extern void arcnet_dump_packet(struct device *dev,u_char *buffer,int ext,
+ char *desc);
+#else
+#define arcnet_dump_packet(dev,buffer,ext,desc) ;
+#endif
+
+extern void arcnet_tx_done(struct device *dev, struct arcnet_local *lp);
+extern void arcnet_makename(char *device);
+extern void arcnet_interrupt(int irq,void *dev_id,struct pt_regs *regs);
+extern void arcnet_setup(struct device *dev);
+extern int arcnet_go_tx(struct device *dev,int enable_irq);
+extern void arcnetA_continue_tx(struct device *dev);
+extern void arcnet_rx(struct arcnet_local *lp, u_char *arcsoft, short length, int saddr, int daddr);
+extern void arcnet_use_count(int open);
+
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_ARCDEVICE_H */
diff --git a/pfinet/linux-src/include/linux/atalk.h b/pfinet/linux-src/include/linux/atalk.h
new file mode 100644
index 00000000..e9d20979
--- /dev/null
+++ b/pfinet/linux-src/include/linux/atalk.h
@@ -0,0 +1,180 @@
+/*
+ * AppleTalk networking structures
+ *
+ * The following are directly referenced from the University Of Michigan
+ * netatalk for compatibility reasons.
+ */
+
+#ifndef __LINUX_ATALK_H__
+#define __LINUX_ATALK_H__
+
+#define ATPORT_FIRST 1
+#define ATPORT_RESERVED 128
+#define ATPORT_LAST 254 /* 254 is only legal on localtalk */
+#define ATADDR_ANYNET (__u16)0
+#define ATADDR_ANYNODE (__u8)0
+#define ATADDR_ANYPORT (__u8)0
+#define ATADDR_BCAST (__u8)255
+#define DDP_MAXSZ 587
+#define DDP_MAXHOPS 15 /* 4 bits of hop counter */
+
+#define SIOCATALKDIFADDR (SIOCPROTOPRIVATE + 0)
+
+struct at_addr
+{
+ __u16 s_net;
+ __u8 s_node;
+};
+
+struct sockaddr_at
+{
+ sa_family_t sat_family;
+ __u8 sat_port;
+ struct at_addr sat_addr;
+ char sat_zero[ 8 ];
+};
+
+struct netrange
+{
+ __u8 nr_phase;
+ __u16 nr_firstnet;
+ __u16 nr_lastnet;
+};
+
+struct atalk_route
+{
+ struct device *dev;
+ struct at_addr target;
+ struct at_addr gateway;
+ int flags;
+ struct atalk_route *next;
+};
+
+struct atalk_iface
+{
+ struct device *dev;
+ struct at_addr address; /* Our address */
+ int status; /* What are we doing? */
+#define ATIF_PROBE 1 /* Probing for an address */
+#define ATIF_PROBE_FAIL 2 /* Probe collided */
+ struct netrange nets; /* Associated direct netrange */
+ struct atalk_iface *next;
+};
+
+struct atalk_sock
+{
+ unsigned short dest_net;
+ unsigned short src_net;
+ unsigned char dest_node;
+ unsigned char src_node;
+ unsigned char dest_port;
+ unsigned char src_port;
+};
+
+#ifdef __KERNEL__
+
+#include <asm/byteorder.h>
+
+struct ddpehdr
+{
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ __u16 deh_len:10, deh_hops:4, deh_pad:2;
+#else
+ __u16 deh_pad:2, deh_hops:4, deh_len:10;
+#endif
+ __u16 deh_sum;
+ __u16 deh_dnet;
+ __u16 deh_snet;
+ __u8 deh_dnode;
+ __u8 deh_snode;
+ __u8 deh_dport;
+ __u8 deh_sport;
+ /* And netatalk apps expect to stick the type in themselves */
+};
+
+/*
+ * Don't drop the struct into the struct above. You'll get some
+ * surprise padding.
+ */
+
+struct ddpebits
+{
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ __u16 deh_len:10, deh_hops:4, deh_pad:2;
+#else
+ __u16 deh_pad:2, deh_hops:4, deh_len:10;
+#endif
+};
+
+/*
+ * Short form header
+ */
+
+struct ddpshdr
+{
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ __u16 dsh_len:10, dsh_pad:6;
+#else
+ __u16 dsh_pad:6, dsh_len:10;
+#endif
+ __u8 dsh_dport;
+ __u8 dsh_sport;
+ /* And netatalk apps expect to stick the type in themselves */
+};
+
+/* AppleTalk AARP headers */
+
+struct elapaarp
+{
+ __u16 hw_type;
+#define AARP_HW_TYPE_ETHERNET 1
+#define AARP_HW_TYPE_TOKENRING 2
+ __u16 pa_type;
+ __u8 hw_len;
+ __u8 pa_len;
+#define AARP_PA_ALEN 4
+ __u16 function;
+#define AARP_REQUEST 1
+#define AARP_REPLY 2
+#define AARP_PROBE 3
+ __u8 hw_src[ETH_ALEN] __attribute__ ((packed));
+ __u8 pa_src_zero __attribute__ ((packed));
+ __u16 pa_src_net __attribute__ ((packed));
+ __u8 pa_src_node __attribute__ ((packed));
+ __u8 hw_dst[ETH_ALEN] __attribute__ ((packed));
+ __u8 pa_dst_zero __attribute__ ((packed));
+ __u16 pa_dst_net __attribute__ ((packed));
+ __u8 pa_dst_node __attribute__ ((packed));
+};
+
+#define AARP_EXPIRY_TIME (5*60*HZ) /* Not specified - how long till we drop a resolved entry */
+#define AARP_HASH_SIZE 16 /* Size of hash table */
+#define AARP_TICK_TIME (HZ/5) /* Fast retransmission timer when resolving */
+#define AARP_RETRANSMIT_LIMIT 10 /* Send 10 requests then give up (2 seconds) */
+#define AARP_RESOLVE_TIME (10*HZ) /* Some value bigger than total retransmit time + a bit for last reply to appear and to stop continual requests */
+
+extern struct datalink_proto *ddp_dl, *aarp_dl;
+extern void aarp_proto_init(void);
+/* Inter module exports */
+
+/*
+ * Give a device find its atif control structure
+ */
+
+extern __inline__ struct atalk_iface *atalk_find_dev(struct device *dev)
+{
+ return dev->atalk_ptr;
+}
+
+extern struct at_addr *atalk_find_dev_addr(struct device *dev);
+extern struct device *atrtr_get_dev(struct at_addr *sa);
+extern int aarp_send_ddp(struct device *dev,struct sk_buff *skb, struct at_addr *sa, void *hwaddr);
+extern void aarp_send_probe(struct device *dev, struct at_addr *addr);
+extern void aarp_device_down(struct device *dev);
+
+#ifdef MODULE
+extern void aarp_cleanup_module(void);
+#endif /* MODULE */
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_ATALK_H__ */
diff --git a/pfinet/linux-src/include/linux/atari_rootsec.h b/pfinet/linux-src/include/linux/atari_rootsec.h
new file mode 100644
index 00000000..09745f7a
--- /dev/null
+++ b/pfinet/linux-src/include/linux/atari_rootsec.h
@@ -0,0 +1,34 @@
+#ifndef _LINUX_ATARI_ROOTSEC_H
+#define _LINUX_ATARI_ROOTSEC_H
+
+/*
+ * linux/include/linux/atari_rootsec.h
+ * definitions for Atari Rootsector layout
+ * by Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de)
+ *
+ * modified for ICD/Supra partitioning scheme restricted to at most 12
+ * partitions
+ * by Guenther Kelleter (guenther@pool.informatik.rwth-aachen.de)
+ */
+
+struct partition_info
+{
+ u_char flg; /* bit 0: active; bit 7: bootable */
+ char id[3]; /* "GEM", "BGM", "XGM", or other */
+ u32 st; /* start of partition */
+ u32 siz; /* length of partition */
+};
+
+struct rootsector
+{
+ char unused[0x156]; /* room for boot code */
+ struct partition_info icdpart[8]; /* info for ICD-partitions 5..12 */
+ char unused2[0xc];
+ u32 hd_siz; /* size of disk in blocks */
+ struct partition_info part[4];
+ u32 bsl_st; /* start of bad sector list */
+ u32 bsl_cnt; /* length of bad sector list */
+ u16 checksum; /* checksum for bootable disks */
+} __attribute__ ((__packed__));
+
+#endif /* _LINUX_ATARI_ROOTSEC_H */
diff --git a/pfinet/linux-src/include/linux/auto_fs.h b/pfinet/linux-src/include/linux/auto_fs.h
new file mode 100644
index 00000000..9a0ddd6c
--- /dev/null
+++ b/pfinet/linux-src/include/linux/auto_fs.h
@@ -0,0 +1,83 @@
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * linux/include/linux/auto_fs.h
+ *
+ * Copyright 1997 Transmeta Corporation - All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+
+#ifndef _LINUX_AUTO_FS_H
+#define _LINUX_AUTO_FS_H
+
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/limits.h>
+#include <linux/ioctl.h>
+#include <asm/types.h>
+
+#define AUTOFS_PROTO_VERSION 3
+
+/*
+ * Architectures where both 32- and 64-bit binaries can be executed
+ * on 64-bit kernels need this. This keeps the structure format
+ * uniform, and makes sure the wait_queue_token isn't too big to be
+ * passed back down to the kernel.
+ *
+ * This assumes that on these architectures:
+ * mode 32 bit 64 bit
+ * -------------------------
+ * int 32 bit 32 bit
+ * long 32 bit 64 bit
+ *
+ * If so, 32-bit user-space code should be backwards compatible.
+ */
+
+#if defined(__sparc__) || defined(__mips__)
+typedef unsigned int autofs_wqt_t;
+#else
+typedef unsigned long autofs_wqt_t;
+#endif
+
+enum autofs_packet_type {
+ autofs_ptype_missing, /* Missing entry (mount request) */
+ autofs_ptype_expire, /* Expire entry (umount request) */
+};
+
+struct autofs_packet_hdr {
+ int proto_version; /* Protocol version */
+ enum autofs_packet_type type; /* Type of packet */
+};
+
+struct autofs_packet_missing {
+ struct autofs_packet_hdr hdr;
+ autofs_wqt_t wait_queue_token;
+ int len;
+ char name[NAME_MAX+1];
+};
+
+struct autofs_packet_expire {
+ struct autofs_packet_hdr hdr;
+ int len;
+ char name[NAME_MAX+1];
+};
+
+#define AUTOFS_IOC_READY _IO(0x93,0x60)
+#define AUTOFS_IOC_FAIL _IO(0x93,0x61)
+#define AUTOFS_IOC_CATATONIC _IO(0x93,0x62)
+#define AUTOFS_IOC_PROTOVER _IOR(0x93,0x63,int)
+#define AUTOFS_IOC_SETTIMEOUT _IOWR(0x93,0x64,unsigned long)
+#define AUTOFS_IOC_EXPIRE _IOR(0x93,0x65,struct autofs_packet_expire)
+
+#ifdef __KERNEL__
+
+/* Init function */
+int init_autofs_fs(void);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_AUTO_FS_H */
diff --git a/pfinet/linux-src/include/linux/awe_voice.h b/pfinet/linux-src/include/linux/awe_voice.h
new file mode 100644
index 00000000..aed60f5c
--- /dev/null
+++ b/pfinet/linux-src/include/linux/awe_voice.h
@@ -0,0 +1,524 @@
+/*
+ * sound/awe_voice.h
+ *
+ * Voice information definitions for the low level driver for the
+ * AWE32/SB32/AWE64 wave table synth.
+ * version 0.4.3; Feb. 1, 1999
+ *
+ * Copyright (C) 1996-1999 Takashi Iwai
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef AWE_VOICE_H
+#define AWE_VOICE_H
+
+#ifndef SAMPLE_TYPE_AWE32
+#define SAMPLE_TYPE_AWE32 0x20
+#endif
+
+#ifndef _PATCHKEY
+#define _PATCHKEY(id) ((id<<8)|0xfd)
+#endif
+
+/*----------------------------------------------------------------
+ * patch information record
+ *----------------------------------------------------------------*/
+
+/* patch interface header: 16 bytes */
+typedef struct awe_patch_info {
+ short key; /* use AWE_PATCH here */
+#define AWE_PATCH _PATCHKEY(0x07)
+
+ short device_no; /* synthesizer number */
+ unsigned short sf_id; /* file id (should be zero) */
+ short optarg; /* optional argument */
+ int len; /* data length (without this header) */
+
+ short type; /* patch operation type */
+#define AWE_LOAD_INFO 0 /* awe_voice_rec */
+#define AWE_LOAD_DATA 1 /* awe_sample_info */
+#define AWE_OPEN_PATCH 2 /* awe_open_parm */
+#define AWE_CLOSE_PATCH 3 /* none */
+#define AWE_UNLOAD_PATCH 4 /* none */
+#define AWE_REPLACE_DATA 5 /* awe_sample_info (optarg=#channels)*/
+#define AWE_MAP_PRESET 6 /* awe_voice_map */
+/*#define AWE_PROBE_INFO 7*/ /* awe_voice_map (pat only) */
+#define AWE_PROBE_DATA 8 /* optarg=sample */
+#define AWE_LOAD_CHORUS_FX 0x10 /* awe_chorus_fx_rec (optarg=mode) */
+#define AWE_LOAD_REVERB_FX 0x11 /* awe_reverb_fx_rec (optarg=mode) */
+
+ short reserved; /* word alignment data */
+
+ /* the actual patch data begins after this */
+#if defined(AWE_COMPAT_030) && AWE_COMPAT_030
+ char data[0];
+#endif
+} awe_patch_info;
+
+/*#define AWE_PATCH_INFO_SIZE 16*/
+#define AWE_PATCH_INFO_SIZE sizeof(awe_patch_info)
+
+
+/*----------------------------------------------------------------
+ * open patch
+ *----------------------------------------------------------------*/
+
+#define AWE_PATCH_NAME_LEN 32
+
+typedef struct _awe_open_parm {
+ unsigned short type; /* sample type */
+#define AWE_PAT_TYPE_MISC 0
+#define AWE_PAT_TYPE_GM 1
+#define AWE_PAT_TYPE_GS 2
+#define AWE_PAT_TYPE_MT32 3
+#define AWE_PAT_TYPE_XG 4
+#define AWE_PAT_TYPE_SFX 5
+#define AWE_PAT_TYPE_GUS 6
+#define AWE_PAT_TYPE_MAP 7
+
+#define AWE_PAT_LOCKED 0x100 /* lock the samples */
+#define AWE_PAT_SHARED 0x200 /* sample is shared */
+
+ short reserved;
+ char name[AWE_PATCH_NAME_LEN];
+} awe_open_parm;
+
+/*#define AWE_OPEN_PARM_SIZE 28*/
+#define AWE_OPEN_PARM_SIZE sizeof(awe_open_parm)
+
+
+/*----------------------------------------------------------------
+ * raw voice information record
+ *----------------------------------------------------------------*/
+
+/* wave table envelope & effect parameters to control EMU8000 */
+typedef struct _awe_voice_parm {
+ unsigned short moddelay; /* modulation delay (0x8000) */
+ unsigned short modatkhld; /* modulation attack & hold time (0x7f7f) */
+ unsigned short moddcysus; /* modulation decay & sustain (0x7f7f) */
+ unsigned short modrelease; /* modulation release time (0x807f) */
+ short modkeyhold, modkeydecay; /* envelope change per key (not used) */
+ unsigned short voldelay; /* volume delay (0x8000) */
+ unsigned short volatkhld; /* volume attack & hold time (0x7f7f) */
+ unsigned short voldcysus; /* volume decay & sustain (0x7f7f) */
+ unsigned short volrelease; /* volume release time (0x807f) */
+ short volkeyhold, volkeydecay; /* envelope change per key (not used) */
+ unsigned short lfo1delay; /* LFO1 delay (0x8000) */
+ unsigned short lfo2delay; /* LFO2 delay (0x8000) */
+ unsigned short pefe; /* modulation pitch & cutoff (0x0000) */
+ unsigned short fmmod; /* LFO1 pitch & cutoff (0x0000) */
+ unsigned short tremfrq; /* LFO1 volume & freq (0x0000) */
+ unsigned short fm2frq2; /* LFO2 pitch & freq (0x0000) */
+ unsigned char cutoff; /* initial cutoff (0xff) */
+ unsigned char filterQ; /* initial filter Q [0-15] (0x0) */
+ unsigned char chorus; /* chorus send (0x00) */
+ unsigned char reverb; /* reverb send (0x00) */
+ unsigned short reserved[4]; /* not used */
+} awe_voice_parm;
+
+typedef struct _awe_voice_parm_block {
+ unsigned short moddelay; /* modulation delay (0x8000) */
+ unsigned char modatk, modhld;
+ unsigned char moddcy, modsus;
+ unsigned char modrel, moddummy;
+ short modkeyhold, modkeydecay; /* envelope change per key (not used) */
+ unsigned short voldelay; /* volume delay (0x8000) */
+ unsigned char volatk, volhld;
+ unsigned char voldcy, volsus;
+ unsigned char volrel, voldummy;
+ short volkeyhold, volkeydecay; /* envelope change per key (not used) */
+ unsigned short lfo1delay; /* LFO1 delay (0x8000) */
+ unsigned short lfo2delay; /* LFO2 delay (0x8000) */
+ unsigned char env1fc, env1pit;
+ unsigned char lfo1fc, lfo1pit;
+ unsigned char lfo1freq, lfo1vol;
+ unsigned char lfo2freq, lfo2pit;
+ unsigned char cutoff; /* initial cutoff (0xff) */
+ unsigned char filterQ; /* initial filter Q [0-15] (0x0) */
+ unsigned char chorus; /* chorus send (0x00) */
+ unsigned char reverb; /* reverb send (0x00) */
+ unsigned short reserved[4]; /* not used */
+} awe_voice_parm_block;
+
+#define AWE_VOICE_PARM_SIZE 48
+
+
+/* wave table parameters: 92 bytes */
+typedef struct _awe_voice_info {
+ unsigned short sf_id; /* file id (should be zero) */
+ unsigned short sample; /* sample id */
+ int start, end; /* sample offset correction */
+ int loopstart, loopend; /* loop offset correction */
+ short rate_offset; /* sample rate pitch offset */
+ unsigned short mode; /* sample mode */
+#define AWE_MODE_ROMSOUND 0x8000
+#define AWE_MODE_STEREO 1
+#define AWE_MODE_LOOPING 2
+#define AWE_MODE_NORELEASE 4 /* obsolete */
+#define AWE_MODE_INIT_PARM 8
+
+ short root; /* midi root key */
+ short tune; /* pitch tuning (in cents) */
+ char low, high; /* key note range */
+ char vellow, velhigh; /* velocity range */
+ char fixkey, fixvel; /* fixed key, velocity */
+ char pan, fixpan; /* panning, fixed panning */
+ short exclusiveClass; /* exclusive class (0 = none) */
+ unsigned char amplitude; /* sample volume (127 max) */
+ unsigned char attenuation; /* attenuation (0.375dB) */
+ short scaleTuning; /* pitch scale tuning(%), normally 100 */
+ awe_voice_parm parm; /* voice envelope parameters */
+ short index; /* internal index (set by driver) */
+} awe_voice_info;
+
+/*#define AWE_VOICE_INFO_SIZE 92*/
+#define AWE_VOICE_INFO_SIZE sizeof(awe_voice_info)
+
+/*----------------------------------------------------------------*/
+
+/* The info entry of awe_voice_rec is changed from 0 to 1
+ * for some compilers refusing zero size array.
+ * Due to this change, sizeof(awe_voice_rec) becomes different
+ * from older versions.
+ * Use AWE_VOICE_REC_SIZE instead.
+ */
+
+/* instrument info header: 4 bytes */
+typedef struct _awe_voice_rec_hdr {
+ unsigned char bank; /* midi bank number */
+ unsigned char instr; /* midi preset number */
+ char nvoices; /* number of voices */
+ char write_mode; /* write mode; normally 0 */
+#define AWE_WR_APPEND 0 /* append anyway */
+#define AWE_WR_EXCLUSIVE 1 /* skip if already exists */
+#define AWE_WR_REPLACE 2 /* replace if already exists */
+} awe_voice_rec_hdr;
+
+/*#define AWE_VOICE_REC_SIZE 4*/
+#define AWE_VOICE_REC_SIZE sizeof(awe_voice_rec_hdr)
+
+/* the standard patch structure for one sample */
+typedef struct _awe_voice_rec_patch {
+ awe_patch_info patch;
+ awe_voice_rec_hdr hdr;
+ awe_voice_info info;
+} awe_voice_rec_patch;
+
+
+/* obsolete data type */
+#if defined(AWE_COMPAT_030) && AWE_COMPAT_030
+#define AWE_INFOARRAY_SIZE 0
+#else
+#define AWE_INFOARRAY_SIZE 1
+#endif
+
+typedef struct _awe_voice_rec {
+ unsigned char bank; /* midi bank number */
+ unsigned char instr; /* midi preset number */
+ short nvoices; /* number of voices */
+ /* voice information follows here */
+ awe_voice_info info[AWE_INFOARRAY_SIZE];
+} awe_voice_rec;
+
+
+/*----------------------------------------------------------------
+ * sample wave information
+ *----------------------------------------------------------------*/
+
+/* wave table sample header: 32 bytes */
+typedef struct awe_sample_info {
+ unsigned short sf_id; /* file id (should be zero) */
+ unsigned short sample; /* sample id */
+ int start, end; /* start & end offset */
+ int loopstart, loopend; /* loop start & end offset */
+ int size; /* size (0 = ROM) */
+ short checksum_flag; /* use check sum = 1 */
+ unsigned short mode_flags; /* mode flags */
+#define AWE_SAMPLE_8BITS 1 /* wave data is 8bits */
+#define AWE_SAMPLE_UNSIGNED 2 /* wave data is unsigned */
+#define AWE_SAMPLE_NO_BLANK 4 /* no blank loop is attached */
+#define AWE_SAMPLE_SINGLESHOT 8 /* single-shot w/o loop */
+#define AWE_SAMPLE_BIDIR_LOOP 16 /* bidirectional looping */
+#define AWE_SAMPLE_STEREO_LEFT 32 /* stereo left sound */
+#define AWE_SAMPLE_STEREO_RIGHT 64 /* stereo right sound */
+#define AWE_SAMPLE_REVERSE_LOOP 128 /* reverse looping */
+ unsigned int checksum; /* check sum */
+#if defined(AWE_COMPAT_030) && AWE_COMPAT_030
+ unsigned short data[0]; /* sample data follows here */
+#endif
+} awe_sample_info;
+
+/*#define AWE_SAMPLE_INFO_SIZE 32*/
+#define AWE_SAMPLE_INFO_SIZE sizeof(awe_sample_info)
+
+
+/*----------------------------------------------------------------
+ * voice preset mapping
+ *----------------------------------------------------------------*/
+
+typedef struct awe_voice_map {
+ int map_bank, map_instr, map_key; /* key = -1 means all keys */
+ int src_bank, src_instr, src_key;
+} awe_voice_map;
+
+#define AWE_VOICE_MAP_SIZE sizeof(awe_voice_map)
+
+
+/*----------------------------------------------------------------
+ * awe hardware controls
+ *----------------------------------------------------------------*/
+
+#define _AWE_DEBUG_MODE 0x00
+#define _AWE_REVERB_MODE 0x01
+#define _AWE_CHORUS_MODE 0x02
+#define _AWE_REMOVE_LAST_SAMPLES 0x03
+#define _AWE_INITIALIZE_CHIP 0x04
+#define _AWE_SEND_EFFECT 0x05
+#define _AWE_TERMINATE_CHANNEL 0x06
+#define _AWE_TERMINATE_ALL 0x07
+#define _AWE_INITIAL_VOLUME 0x08
+#define _AWE_INITIAL_ATTEN _AWE_INITIAL_VOLUME
+#define _AWE_RESET_CHANNEL 0x09
+#define _AWE_CHANNEL_MODE 0x0a
+#define _AWE_DRUM_CHANNELS 0x0b
+#define _AWE_MISC_MODE 0x0c
+#define _AWE_RELEASE_ALL 0x0d
+#define _AWE_NOTEOFF_ALL 0x0e
+#define _AWE_CHN_PRESSURE 0x0f
+/*#define _AWE_GET_CURRENT_MODE 0x10*/
+#define _AWE_EQUALIZER 0x11
+/*#define _AWE_GET_MISC_MODE 0x12*/
+/*#define _AWE_GET_FONTINFO 0x13*/
+
+#define _AWE_MODE_FLAG 0x80
+#define _AWE_COOKED_FLAG 0x40 /* not supported */
+#define _AWE_MODE_VALUE_MASK 0x3F
+
+/*----------------------------------------------------------------*/
+
+#define _AWE_SET_CMD(p,dev,voice,cmd,p1,p2) \
+{((char*)(p))[0] = SEQ_PRIVATE;\
+ ((char*)(p))[1] = dev;\
+ ((char*)(p))[2] = _AWE_MODE_FLAG|(cmd);\
+ ((char*)(p))[3] = voice;\
+ ((unsigned short*)(p))[2] = p1;\
+ ((unsigned short*)(p))[3] = p2;}
+
+/* buffered access */
+#define _AWE_CMD(dev, voice, cmd, p1, p2) \
+{_SEQ_NEEDBUF(8);\
+ _AWE_SET_CMD(_seqbuf + _seqbufptr, dev, voice, cmd, p1, p2);\
+ _SEQ_ADVBUF(8);}
+
+/* direct access */
+#define _AWE_CMD_NOW(seqfd,dev,voice,cmd,p1,p2) \
+{struct seq_event_rec tmp;\
+ _AWE_SET_CMD(&tmp, dev, voice, cmd, p1, p2);\
+ ioctl(seqfd, SNDCTL_SEQ_OUTOFBAND, &tmp);}
+
+/*----------------------------------------------------------------*/
+
+/* set debugging mode */
+#define AWE_DEBUG_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_DEBUG_MODE, p1, 0)
+/* set reverb mode; from 0 to 7 */
+#define AWE_REVERB_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_REVERB_MODE, p1, 0)
+/* set chorus mode; from 0 to 7 */
+#define AWE_CHORUS_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_CHORUS_MODE, p1, 0)
+
+/* reset channel */
+#define AWE_RESET_CHANNEL(dev,ch) _AWE_CMD(dev, ch, _AWE_RESET_CHANNEL, 0, 0)
+#define AWE_RESET_CONTROL(dev,ch) _AWE_CMD(dev, ch, _AWE_RESET_CHANNEL, 1, 0)
+
+/* send an effect to all layers */
+#define AWE_SEND_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,type,value)
+#define AWE_ADD_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((type)|0x80),value)
+#define AWE_UNSET_EFFECT(dev,voice,type) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((type)|0x40),0)
+/* send an effect to a layer */
+#define AWE_SEND_LAYER_EFFECT(dev,voice,layer,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)),value)
+#define AWE_ADD_LAYER_EFFECT(dev,voice,layer,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)|0x80),value)
+#define AWE_UNSET_LAYER_EFFECT(dev,voice,layer,type) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)|0x40),0)
+
+/* terminate sound on the channel/voice */
+#define AWE_TERMINATE_CHANNEL(dev,voice) _AWE_CMD(dev,voice,_AWE_TERMINATE_CHANNEL,0,0)
+/* terminate all sounds */
+#define AWE_TERMINATE_ALL(dev) _AWE_CMD(dev, 0, _AWE_TERMINATE_ALL, 0, 0)
+/* release all sounds (w/o sustain effect) */
+#define AWE_RELEASE_ALL(dev) _AWE_CMD(dev, 0, _AWE_RELEASE_ALL, 0, 0)
+/* note off all sounds (w sustain effect) */
+#define AWE_NOTEOFF_ALL(dev) _AWE_CMD(dev, 0, _AWE_NOTEOFF_ALL, 0, 0)
+
+/* set initial attenuation */
+#define AWE_INITIAL_VOLUME(dev,atten) _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 0)
+#define AWE_INITIAL_ATTEN AWE_INITIAL_VOLUME
+/* relative attenuation */
+#define AWE_SET_ATTEN(dev,atten) _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 1)
+
+/* set channel playing mode; mode=0/1/2 */
+#define AWE_SET_CHANNEL_MODE(dev,mode) _AWE_CMD(dev, 0, _AWE_CHANNEL_MODE, mode, 0)
+#define AWE_PLAY_INDIRECT 0 /* indirect voice mode (default) */
+#define AWE_PLAY_MULTI 1 /* multi note voice mode */
+#define AWE_PLAY_DIRECT 2 /* direct single voice mode */
+#define AWE_PLAY_MULTI2 3 /* sequencer2 mode; used internally */
+
+/* set drum channel mask; channels is 32bit long value */
+#define AWE_DRUM_CHANNELS(dev,channels) _AWE_CMD(dev, 0, _AWE_DRUM_CHANNELS, ((channels) & 0xffff), ((channels) >> 16))
+
+/* set bass and treble control; values are from 0 to 11 */
+#define AWE_EQUALIZER(dev,bass,treble) _AWE_CMD(dev, 0, _AWE_EQUALIZER, bass, treble)
+
+/* remove last loaded samples */
+#define AWE_REMOVE_LAST_SAMPLES(seqfd,dev) _AWE_CMD_NOW(seqfd, dev, 0, _AWE_REMOVE_LAST_SAMPLES, 0, 0)
+/* initialize emu8000 chip */
+#define AWE_INITIALIZE_CHIP(seqfd,dev) _AWE_CMD_NOW(seqfd, dev, 0, _AWE_INITIALIZE_CHIP, 0, 0)
+
+/* set miscellaneous modes; meta command */
+#define AWE_MISC_MODE(dev,mode,value) _AWE_CMD(dev, 0, _AWE_MISC_MODE, mode, value)
+/* exclusive sound off; 1=off */
+#define AWE_EXCLUSIVE_SOUND(dev,mode) AWE_MISC_MODE(dev,AWE_MD_EXCLUSIVE_SOUND,mode)
+/* default GUS bank number */
+#define AWE_SET_GUS_BANK(dev,bank) AWE_MISC_MODE(dev,AWE_MD_GUS_BANK,bank)
+/* change panning position in realtime; 0=don't 1=do */
+#define AWE_REALTIME_PAN(dev,mode) AWE_MISC_MODE(dev,AWE_MD_REALTIME_PAN,mode)
+
+/* extended pressure controls; not portable with other sound drivers */
+#define AWE_KEY_PRESSURE(dev,ch,note,vel) SEQ_START_NOTE(dev,ch,(note)+128,vel)
+#define AWE_CHN_PRESSURE(dev,ch,vel) _AWE_CMD(dev,ch,_AWE_CHN_PRESSURE,vel,0)
+
+/*----------------------------------------------------------------*/
+
+/* reverb mode parameters */
+#define AWE_REVERB_ROOM1 0
+#define AWE_REVERB_ROOM2 1
+#define AWE_REVERB_ROOM3 2
+#define AWE_REVERB_HALL1 3
+#define AWE_REVERB_HALL2 4
+#define AWE_REVERB_PLATE 5
+#define AWE_REVERB_DELAY 6
+#define AWE_REVERB_PANNINGDELAY 7
+#define AWE_REVERB_PREDEFINED 8
+/* user can define reverb modes up to 32 */
+#define AWE_REVERB_NUMBERS 32
+
+typedef struct awe_reverb_fx_rec {
+ unsigned short parms[28];
+} awe_reverb_fx_rec;
+
+/*----------------------------------------------------------------*/
+
+/* chorus mode parameters */
+#define AWE_CHORUS_1 0
+#define AWE_CHORUS_2 1
+#define AWE_CHORUS_3 2
+#define AWE_CHORUS_4 3
+#define AWE_CHORUS_FEEDBACK 4
+#define AWE_CHORUS_FLANGER 5
+#define AWE_CHORUS_SHORTDELAY 6
+#define AWE_CHORUS_SHORTDELAY2 7
+#define AWE_CHORUS_PREDEFINED 8
+/* user can define chorus modes up to 32 */
+#define AWE_CHORUS_NUMBERS 32
+
+typedef struct awe_chorus_fx_rec {
+ unsigned short feedback; /* feedback level (0xE600-0xE6FF) */
+ unsigned short delay_offset; /* delay (0-0x0DA3) [1/44100 sec] */
+ unsigned short lfo_depth; /* LFO depth (0xBC00-0xBCFF) */
+ unsigned int delay; /* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */
+ unsigned int lfo_freq; /* LFO freq LFO freq (0-0xFFFFFFFF) */
+} awe_chorus_fx_rec;
+
+/*----------------------------------------------------------------*/
+
+/* misc mode types */
+enum {
+/* 0*/ AWE_MD_EXCLUSIVE_OFF, /* obsolete */
+/* 1*/ AWE_MD_EXCLUSIVE_ON, /* obsolete */
+/* 2*/ AWE_MD_VERSION, /* read only */
+/* 3*/ AWE_MD_EXCLUSIVE_SOUND, /* 0/1: exclusive note on (default=1) */
+/* 4*/ AWE_MD_REALTIME_PAN, /* 0/1: do realtime pan change (default=1) */
+/* 5*/ AWE_MD_GUS_BANK, /* bank number for GUS patches (default=0) */
+/* 6*/ AWE_MD_KEEP_EFFECT, /* 0/1: keep effect values, (default=0) */
+/* 7*/ AWE_MD_ZERO_ATTEN, /* attenuation of max volume (default=32) */
+/* 8*/ AWE_MD_CHN_PRIOR, /* 0/1: set MIDI channel priority mode (default=1) */
+/* 9*/ AWE_MD_MOD_SENSE, /* integer: modwheel sensitivity (def=18) */
+/*10*/ AWE_MD_DEF_PRESET, /* integer: default preset number (def=0) */
+/*11*/ AWE_MD_DEF_BANK, /* integer: default bank number (def=0) */
+/*12*/ AWE_MD_DEF_DRUM, /* integer: default drumset number (def=0) */
+/*13*/ AWE_MD_TOGGLE_DRUM_BANK, /* 0/1: toggle drum flag with bank# (def=0) */
+/*14*/ AWE_MD_NEW_VOLUME_CALC, /* 0/1: volume calculation mode (def=1) */
+/*15*/ AWE_MD_CHORUS_MODE, /* integer: chorus mode (def=2) */
+/*16*/ AWE_MD_REVERB_MODE, /* integer: chorus mode (def=4) */
+/*17*/ AWE_MD_BASS_LEVEL, /* integer: bass level (def=5) */
+/*18*/ AWE_MD_TREBLE_LEVEL, /* integer: treble level (def=9) */
+/*19*/ AWE_MD_DEBUG_MODE, /* integer: debug level (def=0) */
+/*20*/ AWE_MD_PAN_EXCHANGE, /* 0/1: exchange panning direction (def=0) */
+ AWE_MD_END,
+};
+
+/*----------------------------------------------------------------*/
+
+/* effect parameters */
+enum {
+
+/* modulation envelope parameters */
+/* 0*/ AWE_FX_ENV1_DELAY, /* WORD: ENVVAL */
+/* 1*/ AWE_FX_ENV1_ATTACK, /* BYTE: up ATKHLD */
+/* 2*/ AWE_FX_ENV1_HOLD, /* BYTE: lw ATKHLD */
+/* 3*/ AWE_FX_ENV1_DECAY, /* BYTE: lw DCYSUS */
+/* 4*/ AWE_FX_ENV1_RELEASE, /* BYTE: lw DCYSUS */
+/* 5*/ AWE_FX_ENV1_SUSTAIN, /* BYTE: up DCYSUS */
+/* 6*/ AWE_FX_ENV1_PITCH, /* BYTE: up PEFE */
+/* 7*/ AWE_FX_ENV1_CUTOFF, /* BYTE: lw PEFE */
+
+/* volume envelope parameters */
+/* 8*/ AWE_FX_ENV2_DELAY, /* WORD: ENVVOL */
+/* 9*/ AWE_FX_ENV2_ATTACK, /* BYTE: up ATKHLDV */
+/*10*/ AWE_FX_ENV2_HOLD, /* BYTE: lw ATKHLDV */
+/*11*/ AWE_FX_ENV2_DECAY, /* BYTE: lw DCYSUSV */
+/*12*/ AWE_FX_ENV2_RELEASE, /* BYTE: lw DCYSUSV */
+/*13*/ AWE_FX_ENV2_SUSTAIN, /* BYTE: up DCYSUSV */
+
+/* LFO1 (tremolo & vibrato) parameters */
+/*14*/ AWE_FX_LFO1_DELAY, /* WORD: LFO1VAL */
+/*15*/ AWE_FX_LFO1_FREQ, /* BYTE: lo TREMFRQ */
+/*16*/ AWE_FX_LFO1_VOLUME, /* BYTE: up TREMFRQ */
+/*17*/ AWE_FX_LFO1_PITCH, /* BYTE: up FMMOD */
+/*18*/ AWE_FX_LFO1_CUTOFF, /* BYTE: lo FMMOD */
+
+/* LFO2 (vibrato) parameters */
+/*19*/ AWE_FX_LFO2_DELAY, /* WORD: LFO2VAL */
+/*20*/ AWE_FX_LFO2_FREQ, /* BYTE: lo FM2FRQ2 */
+/*21*/ AWE_FX_LFO2_PITCH, /* BYTE: up FM2FRQ2 */
+
+/* Other overall effect parameters */
+/*22*/ AWE_FX_INIT_PITCH, /* SHORT: pitch offset */
+/*23*/ AWE_FX_CHORUS, /* BYTE: chorus effects send (0-255) */
+/*24*/ AWE_FX_REVERB, /* BYTE: reverb effects send (0-255) */
+/*25*/ AWE_FX_CUTOFF, /* BYTE: up IFATN */
+/*26*/ AWE_FX_FILTERQ, /* BYTE: up CCCA */
+
+/* Sample / loop offset changes */
+/*27*/ AWE_FX_SAMPLE_START, /* SHORT: offset */
+/*28*/ AWE_FX_LOOP_START, /* SHORT: offset */
+/*29*/ AWE_FX_LOOP_END, /* SHORT: offset */
+/*30*/ AWE_FX_COARSE_SAMPLE_START, /* SHORT: upper word offset */
+/*31*/ AWE_FX_COARSE_LOOP_START, /* SHORT: upper word offset */
+/*32*/ AWE_FX_COARSE_LOOP_END, /* SHORT: upper word offset */
+/*33*/ AWE_FX_ATTEN, /* BYTE: lo IFATN */
+
+ AWE_FX_END,
+};
+
+#endif /* AWE_VOICE_H */
diff --git a/pfinet/linux-src/include/linux/ax25.h b/pfinet/linux-src/include/linux/ax25.h
new file mode 100644
index 00000000..1e492c8f
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ax25.h
@@ -0,0 +1,98 @@
+/*
+ * These are the public elements of the Linux kernel AX.25 code. A similar
+ * file netrom.h exists for the NET/ROM protocol.
+ */
+
+#ifndef AX25_KERNEL_H
+#define AX25_KERNEL_H
+
+#define AX25_MTU 256
+#define AX25_MAX_DIGIS 8
+
+#define AX25_WINDOW 1
+#define AX25_T1 2
+#define AX25_N2 3
+#define AX25_T3 4
+#define AX25_T2 5
+#define AX25_BACKOFF 6
+#define AX25_EXTSEQ 7
+#define AX25_PIDINCL 8
+#define AX25_IDLE 9
+#define AX25_PACLEN 10
+#define AX25_IAMDIGI 12
+
+#define AX25_KILL 99
+
+#define SIOCAX25GETUID (SIOCPROTOPRIVATE+0)
+#define SIOCAX25ADDUID (SIOCPROTOPRIVATE+1)
+#define SIOCAX25DELUID (SIOCPROTOPRIVATE+2)
+#define SIOCAX25NOUID (SIOCPROTOPRIVATE+3)
+#define SIOCAX25OPTRT (SIOCPROTOPRIVATE+7)
+#define SIOCAX25CTLCON (SIOCPROTOPRIVATE+8)
+#define SIOCAX25GETINFO (SIOCPROTOPRIVATE+9)
+#define SIOCAX25ADDFWD (SIOCPROTOPRIVATE+10)
+#define SIOCAX25DELFWD (SIOCPROTOPRIVATE+11)
+
+#define AX25_SET_RT_IPMODE 2
+
+#define AX25_NOUID_DEFAULT 0
+#define AX25_NOUID_BLOCK 1
+
+typedef struct {
+ char ax25_call[7]; /* 6 call + SSID (shifted ascii!) */
+} ax25_address;
+
+struct sockaddr_ax25 {
+ sa_family_t sax25_family;
+ ax25_address sax25_call;
+ int sax25_ndigis;
+ /* Digipeater ax25_address sets follow */
+};
+
+#define sax25_uid sax25_ndigis
+
+struct full_sockaddr_ax25 {
+ struct sockaddr_ax25 fsa_ax25;
+ ax25_address fsa_digipeater[AX25_MAX_DIGIS];
+};
+
+struct ax25_routes_struct {
+ ax25_address port_addr;
+ ax25_address dest_addr;
+ unsigned char digi_count;
+ ax25_address digi_addr[AX25_MAX_DIGIS];
+};
+
+struct ax25_route_opt_struct {
+ ax25_address port_addr;
+ ax25_address dest_addr;
+ int cmd;
+ int arg;
+};
+
+struct ax25_ctl_struct {
+ ax25_address port_addr;
+ ax25_address source_addr;
+ ax25_address dest_addr;
+ unsigned int cmd;
+ unsigned long arg;
+ unsigned char digi_count;
+ ax25_address digi_addr[AX25_MAX_DIGIS];
+};
+
+struct ax25_info_struct {
+ unsigned int n2, n2count;
+ unsigned int t1, t1timer;
+ unsigned int t2, t2timer;
+ unsigned int t3, t3timer;
+ unsigned int idle, idletimer;
+ unsigned int state;
+ unsigned int rcv_q, snd_q;
+};
+
+struct ax25_fwd_struct {
+ ax25_address port_from;
+ ax25_address port_to;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/b1lli.h b/pfinet/linux-src/include/linux/b1lli.h
new file mode 100644
index 00000000..388ff80c
--- /dev/null
+++ b/pfinet/linux-src/include/linux/b1lli.h
@@ -0,0 +1,136 @@
+/*
+ * $Id: b1lli.h,v 1.8 1999/07/01 15:26:54 calle Exp $
+ *
+ * ISDN lowlevel-module for AVM B1-card.
+ *
+ * Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
+ *
+ * $Log: b1lli.h,v $
+ * Revision 1.8 1999/07/01 15:26:54 calle
+ * complete new version (I love it):
+ * + new hardware independed "capi_driver" interface that will make it easy to:
+ * - support other controllers with CAPI-2.0 (i.e. USB Controller)
+ * - write a CAPI-2.0 for the passive cards
+ * - support serial link CAPI-2.0 boxes.
+ * + wrote "capi_driver" for all supported cards.
+ * + "capi_driver" (supported cards) now have to be configured with
+ * make menuconfig, in the past all supported cards where included
+ * at once.
+ * + new and better informations in /proc/capi/
+ * + new ioctl to switch trace of capi messages per controller
+ * using "avmcapictrl trace [contr] on|off|...."
+ * + complete testcircle with all supported cards and also the
+ * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done.
+ *
+ * Revision 1.7 1999/06/21 15:24:25 calle
+ * extend information in /proc.
+ *
+ * Revision 1.6 1999/04/15 19:49:36 calle
+ * fix fuer die B1-PCI. Jetzt geht z.B. auch IRQ 17 ...
+ *
+ * Revision 1.5 1998/10/25 14:50:28 fritz
+ * Backported from MIPS (Cobalt).
+ *
+ * Revision 1.4 1998/03/29 16:05:02 calle
+ * changes from 2.0 tree merged.
+ *
+ * Revision 1.1.2.9 1998/03/20 14:30:02 calle
+ * added cardnr to detect if you try to add same T1 to different io address.
+ * change number of nccis depending on number of channels.
+ *
+ * Revision 1.1.2.8 1998/03/04 17:32:33 calle
+ * Changes for T1.
+ *
+ * Revision 1.1.2.7 1998/02/27 15:38:29 calle
+ * T1 running with slow link.
+ *
+ * Revision 1.1.2.6 1998/02/24 17:57:36 calle
+ * changes for T1.
+ *
+ * Revision 1.3 1998/01/31 10:54:37 calle
+ * include changes for PCMCIA cards from 2.0 version
+ *
+ * Revision 1.2 1997/12/10 19:38:42 calle
+ * get changes from 2.0 tree
+ *
+ * Revision 1.1.2.2 1997/11/26 16:57:26 calle
+ * more changes for B1/M1/T1.
+ *
+ * Revision 1.1.2.1 1997/11/26 10:47:01 calle
+ * prepared for M1 (Mobile) and T1 (PMX) cards.
+ * prepared to set configuration after load to support other D-channel
+ * protocols, point-to-point and leased lines.
+ *
+ * Revision 1.1 1997/03/04 21:27:32 calle
+ * First version in isdn4linux
+ *
+ * Revision 2.2 1997/02/12 09:31:39 calle
+ * new version
+ *
+ * Revision 1.1 1997/01/31 10:32:20 calle
+ * Initial revision
+ *
+ */
+
+#ifndef _B1LLI_H_
+#define _B1LLI_H_
+/*
+ * struct for loading t4 file
+ */
+typedef struct avmb1_t4file {
+ int len;
+ unsigned char *data;
+} avmb1_t4file;
+
+typedef struct avmb1_loaddef {
+ int contr;
+ avmb1_t4file t4file;
+} avmb1_loaddef;
+
+typedef struct avmb1_loadandconfigdef {
+ int contr;
+ avmb1_t4file t4file;
+ avmb1_t4file t4config;
+} avmb1_loadandconfigdef;
+
+typedef struct avmb1_resetdef {
+ int contr;
+} avmb1_resetdef;
+
+typedef struct avmb1_getdef {
+ int contr;
+ int cardtype;
+ int cardstate;
+} avmb1_getdef;
+
+/*
+ * struct for adding new cards
+ */
+typedef struct avmb1_carddef {
+ int port;
+ int irq;
+} avmb1_carddef;
+
+#define AVM_CARDTYPE_B1 0
+#define AVM_CARDTYPE_T1 1
+#define AVM_CARDTYPE_M1 2
+#define AVM_CARDTYPE_M2 3
+
+typedef struct avmb1_extcarddef {
+ int port;
+ int irq;
+ int cardtype;
+ int cardnr; /* for HEMA/T1 */
+} avmb1_extcarddef;
+
+#define AVMB1_LOAD 0 /* load image to card */
+#define AVMB1_ADDCARD 1 /* add a new card */
+#define AVMB1_RESETCARD 2 /* reset a card */
+#define AVMB1_LOAD_AND_CONFIG 3 /* load image and config to card */
+#define AVMB1_ADDCARD_WITH_TYPE 4 /* add a new card, with cardtype */
+#define AVMB1_GET_CARDINFO 5 /* get cardtype */
+#define AVMB1_REMOVECARD 6 /* remove a card (useful for T1) */
+
+#define AVMB1_REGISTERCARD_IS_OBSOLETE
+
+#endif /* _B1LLI_H_ */
diff --git a/pfinet/linux-src/include/linux/b1pcmcia.h b/pfinet/linux-src/include/linux/b1pcmcia.h
new file mode 100644
index 00000000..e13307ba
--- /dev/null
+++ b/pfinet/linux-src/include/linux/b1pcmcia.h
@@ -0,0 +1,36 @@
+/*
+ * $Id: b1pcmcia.h,v 1.1 1999/07/01 15:26:56 calle Exp $
+ *
+ * Exported functions of module b1pcmcia to be called by
+ * avm_cs card services module.
+ *
+ * Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
+ *
+ * $Log: b1pcmcia.h,v $
+ * Revision 1.1 1999/07/01 15:26:56 calle
+ * complete new version (I love it):
+ * + new hardware independed "capi_driver" interface that will make it easy to:
+ * - support other controllers with CAPI-2.0 (i.e. USB Controller)
+ * - write a CAPI-2.0 for the passive cards
+ * - support serial link CAPI-2.0 boxes.
+ * + wrote "capi_driver" for all supported cards.
+ * + "capi_driver" (supported cards) now have to be configured with
+ * make menuconfig, in the past all supported cards where included
+ * at once.
+ * + new and better informations in /proc/capi/
+ * + new ioctl to switch trace of capi messages per controller
+ * using "avmcapictrl trace [contr] on|off|...."
+ * + complete testcircle with all supported cards and also the
+ * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done.
+ *
+ */
+
+#ifndef _B1PCMCIA_H_
+#define _B1PCMCIA_H_
+
+int b1pcmcia_addcard_b1(unsigned int port, unsigned irq);
+int b1pcmcia_addcard_m1(unsigned int port, unsigned irq);
+int b1pcmcia_addcard_m2(unsigned int port, unsigned irq);
+int b1pcmcia_delcard(unsigned int port, unsigned irq);
+
+#endif /* _B1PCMCIA_H_ */
diff --git a/pfinet/linux-src/include/linux/baycom.h b/pfinet/linux-src/include/linux/baycom.h
new file mode 100644
index 00000000..81249e02
--- /dev/null
+++ b/pfinet/linux-src/include/linux/baycom.h
@@ -0,0 +1,39 @@
+/*
+ * The Linux BAYCOM driver for the Baycom serial 1200 baud modem
+ * and the parallel 9600 baud modem
+ * (C) 1997-1998 by Thomas Sailer, HB9JNX/AE4WA
+ */
+
+#ifndef _BAYCOM_H
+#define _BAYCOM_H
+
+/* -------------------------------------------------------------------- */
+/*
+ * structs for the IOCTL commands
+ */
+
+struct baycom_debug_data {
+ unsigned long debug1;
+ unsigned long debug2;
+ long debug3;
+};
+
+struct baycom_ioctl {
+ int cmd;
+ union {
+ struct baycom_debug_data dbg;
+ } data;
+};
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * ioctl values change for baycom
+ */
+#define BAYCOMCTL_GETDEBUG 0x92
+
+/* -------------------------------------------------------------------- */
+
+#endif /* _BAYCOM_H */
+
+/* --------------------------------------------------------------------- */
diff --git a/pfinet/linux-src/include/linux/binfmts.h b/pfinet/linux-src/include/linux/binfmts.h
new file mode 100644
index 00000000..0d34d35b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/binfmts.h
@@ -0,0 +1,77 @@
+#ifndef _LINUX_BINFMTS_H
+#define _LINUX_BINFMTS_H
+
+#include <linux/ptrace.h>
+#include <linux/capability.h>
+
+/*
+ * MAX_ARG_PAGES defines the number of pages allocated for arguments
+ * and envelope for the new program. 32 should suffice, this gives
+ * a maximum env+arg of 128kB w/4KB pages!
+ */
+#define MAX_ARG_PAGES 32
+
+#ifdef __KERNEL__
+
+/*
+ * This structure is used to hold the arguments that are used when loading binaries.
+ */
+struct linux_binprm{
+ char buf[128];
+ unsigned long page[MAX_ARG_PAGES];
+ unsigned long p;
+ int sh_bang;
+ int java; /* Java binary, prevent recursive invocation */
+ struct dentry * dentry;
+ int e_uid, e_gid;
+ kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
+ int argc, envc;
+ char * filename; /* Name of binary */
+ unsigned long loader, exec;
+};
+
+/*
+ * This structure defines the functions that are used to load the binary formats that
+ * linux accepts.
+ */
+struct linux_binfmt {
+ struct linux_binfmt * next;
+ struct module *module;
+ int (*load_binary)(struct linux_binprm *, struct pt_regs * regs);
+ int (*load_shlib)(int fd);
+ int (*core_dump)(long signr, struct pt_regs * regs);
+};
+
+extern int register_binfmt(struct linux_binfmt *);
+extern int unregister_binfmt(struct linux_binfmt *);
+
+extern int read_exec(struct dentry *, unsigned long offset,
+ char * addr, unsigned long count, int to_kmem);
+
+extern int open_dentry(struct dentry *, int mode);
+
+extern int init_elf_binfmt(void);
+extern int init_elf32_binfmt(void);
+extern int init_irix_binfmt(void);
+extern int init_aout_binfmt(void);
+extern int init_aout32_binfmt(void);
+extern int init_script_binfmt(void);
+extern int init_java_binfmt(void);
+extern int init_em86_binfmt(void);
+extern int init_misc_binfmt(void);
+
+extern int prepare_binprm(struct linux_binprm *);
+extern void remove_arg_zero(struct linux_binprm *);
+extern int search_binary_handler(struct linux_binprm *,struct pt_regs *);
+extern int flush_old_exec(struct linux_binprm * bprm);
+extern unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm);
+extern unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
+ unsigned long p, int from_kmem);
+
+extern void compute_creds(struct linux_binprm *binprm);
+
+/* this eventually goes away */
+#define change_ldt(a,b) setup_arg_pages(a,b)
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_BINFMTS_H */
diff --git a/pfinet/linux-src/include/linux/bios32.h b/pfinet/linux-src/include/linux/bios32.h
new file mode 100644
index 00000000..2f2c14b7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/bios32.h
@@ -0,0 +1,34 @@
+/*
+ * This is only a stub file to make drivers not yet converted to the new
+ * PCI probing mechanism work. [mj]
+ */
+
+#ifndef BIOS32_H
+#define BIOS32_H
+
+#include <linux/pci.h>
+
+#warning This driver uses the old PCI interface, please fix it (see Documentation/pci.txt)
+
+extern inline int __pcibios_read_irq(unsigned char bus, unsigned char dev_fn, unsigned char *to)
+{
+ struct pci_dev *pdev = pci_find_slot(bus, dev_fn);
+ if (!pdev) {
+ *to = 0;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ } else {
+ *to = pdev->irq;
+ return PCIBIOS_SUCCESSFUL;
+ }
+}
+
+extern inline int __pcibios_read_config_byte(unsigned char bus,
+ unsigned char dev_fn, unsigned char where, unsigned char *to)
+{
+ return pcibios_read_config_byte(bus, dev_fn, where, to);
+}
+
+#define pcibios_read_config_byte(b,d,w,p) \
+ (((w) == PCI_INTERRUPT_LINE) ? __pcibios_read_irq(b,d,p) : __pcibios_read_config_byte(b,d,w,p))
+
+#endif
diff --git a/pfinet/linux-src/include/linux/bitops.h b/pfinet/linux-src/include/linux/bitops.h
new file mode 100644
index 00000000..ddb84dd6
--- /dev/null
+++ b/pfinet/linux-src/include/linux/bitops.h
@@ -0,0 +1,72 @@
+#ifndef _LINUX_BITOPS_H
+#define _LINUX_BITOPS_H
+
+
+/*
+ * ffs: find first bit set. This is defined the same way as
+ * the libc and compiler builtin ffs routines, therefore
+ * differs in spirit from the above ffz (man ffs).
+ */
+
+extern __inline__ int generic_ffs(int x)
+{
+ int r = 1;
+
+ if (!x)
+ return 0;
+ if (!(x & 0xffff)) {
+ x >>= 16;
+ r += 16;
+ }
+ if (!(x & 0xff)) {
+ x >>= 8;
+ r += 8;
+ }
+ if (!(x & 0xf)) {
+ x >>= 4;
+ r += 4;
+ }
+ if (!(x & 3)) {
+ x >>= 2;
+ r += 2;
+ }
+ if (!(x & 1)) {
+ x >>= 1;
+ r += 1;
+ }
+ return r;
+}
+
+/*
+ * hweightN: returns the hamming weight (i.e. the number
+ * of bits set) of a N-bit word
+ */
+
+extern __inline__ unsigned int generic_hweight32(unsigned int w)
+{
+ unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
+ res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+ res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
+ res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
+ return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
+}
+
+extern __inline__ unsigned int generic_hweight16(unsigned int w)
+{
+ unsigned int res = (w & 0x5555) + ((w >> 1) & 0x5555);
+ res = (res & 0x3333) + ((res >> 2) & 0x3333);
+ res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
+ return (res & 0x00FF) + ((res >> 8) & 0x00FF);
+}
+
+extern __inline__ unsigned int generic_hweight8(unsigned int w)
+{
+ unsigned int res = (w & 0x55) + ((w >> 1) & 0x55);
+ res = (res & 0x33) + ((res >> 2) & 0x33);
+ return (res & 0x0F) + ((res >> 4) & 0x0F);
+}
+
+#include <asm/bitops.h>
+
+
+#endif
diff --git a/pfinet/linux-src/include/linux/blk.h b/pfinet/linux-src/include/linux/blk.h
new file mode 100644
index 00000000..44e05c7f
--- /dev/null
+++ b/pfinet/linux-src/include/linux/blk.h
@@ -0,0 +1,489 @@
+#ifndef _BLK_H
+#define _BLK_H
+
+#include <linux/blkdev.h>
+#include <linux/locks.h>
+#include <linux/config.h>
+
+#include <asm/spinlock.h>
+
+/*
+ * Spinlock for protecting the request queue which
+ * is mucked around with in interrupts on potentially
+ * multiple CPU's..
+ */
+extern spinlock_t io_request_lock;
+
+/*
+ * NR_REQUEST is the number of entries in the request-queue.
+ * NOTE that writes may use only the low 2/3 of these: reads
+ * take precedence.
+ */
+#define NR_REQUEST 128
+
+/*
+ * This is used in the elevator algorithm. We don't prioritise reads
+ * over writes any more --- although reads are more time-critical than
+ * writes, by treating them equally we increase filesystem throughput.
+ * This turns out to give better overall performance. -- sct
+ */
+#define IN_ORDER(s1,s2) \
+((s1)->rq_dev < (s2)->rq_dev || (((s1)->rq_dev == (s2)->rq_dev && \
+(s1)->sector < (s2)->sector)))
+
+/*
+ * Initialization functions.
+ */
+extern int isp16_init(void);
+extern int cdu31a_init(void);
+extern int acsi_init(void);
+extern int mcd_init(void);
+extern int mcdx_init(void);
+extern int sbpcd_init(void);
+extern int aztcd_init(void);
+extern int sony535_init(void);
+extern int gscd_init(void);
+extern int cm206_init(void);
+extern int optcd_init(void);
+extern int sjcd_init(void);
+extern int cdi_init(void);
+extern int hd_init(void);
+extern int ide_init(void);
+extern int xd_init(void);
+extern int mfm_init(void);
+extern int loop_init(void);
+extern int md_init(void);
+extern int ap_init(void);
+extern int ddv_init(void);
+extern int z2_init(void);
+extern int swim3_init(void);
+extern int amiga_floppy_init(void);
+extern int atari_floppy_init(void);
+extern int nbd_init(void);
+extern int ez_init(void);
+extern int bpcd_init(void);
+extern int ps2esdi_init(void);
+
+#ifdef CONFIG_ARCH_S390
+extern int mdisk_init(void);
+extern int dasd_init(void);
+#endif /* CONFIG_ARCH_S390 */
+
+extern void set_device_ro(kdev_t dev,int flag);
+void add_blkdev_randomness(int major);
+
+extern int floppy_init(void);
+extern void rd_load(void);
+extern int rd_init(void);
+extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */
+extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */
+extern int rd_image_start; /* starting block # of image */
+
+#ifdef CONFIG_BLK_DEV_INITRD
+
+#define INITRD_MINOR 250 /* shouldn't collide with /dev/ram* too soon ... */
+
+extern unsigned long initrd_start,initrd_end;
+extern int mount_initrd; /* zero if initrd should not be mounted */
+extern int initrd_below_start_ok; /* 1 if it is not an error if initrd_start < memory_start */
+void initrd_init(void);
+
+#endif
+
+#define RO_IOCTLS(dev,where) \
+ case BLKROSET: { int __val; if (!capable(CAP_SYS_ADMIN)) return -EACCES; \
+ if (get_user(__val, (int *)(where))) return -EFAULT; \
+ set_device_ro((dev),__val); return 0; } \
+ case BLKROGET: { int __val = (is_read_only(dev) != 0) ; \
+ return put_user(__val,(int *) (where)); }
+
+/*
+ * end_request() and friends. Must be called with the request queue spinlock
+ * acquired. All functions called within end_request() _must_be_ atomic.
+ *
+ * Several drivers define their own end_request and call
+ * end_that_request_first() and end_that_request_last()
+ * for parts of the original function. This prevents
+ * code duplication in drivers.
+ */
+
+int end_that_request_first(struct request *req, int uptodate, char *name);
+void end_that_request_last(struct request *req);
+
+#if defined(MAJOR_NR) || defined(IDE_DRIVER)
+
+/*
+ * Add entries as needed.
+ */
+
+#ifdef IDE_DRIVER
+
+#define DEVICE_NR(device) (MINOR(device) >> PARTN_BITS)
+#define DEVICE_ON(device) /* nothing */
+#define DEVICE_OFF(device) /* nothing */
+#define DEVICE_NAME "ide"
+
+#elif (MAJOR_NR == RAMDISK_MAJOR)
+
+/* ram disk */
+#define DEVICE_NAME "ramdisk"
+#define DEVICE_REQUEST rd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+#define DEVICE_NO_RANDOM
+
+#elif (MAJOR_NR == Z2RAM_MAJOR)
+
+/* Zorro II Ram */
+#define DEVICE_NAME "Z2RAM"
+#define DEVICE_REQUEST do_z2_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == FLOPPY_MAJOR)
+
+static void floppy_off(unsigned int nr);
+
+#define DEVICE_NAME "floppy"
+#define DEVICE_INTR do_floppy
+#define DEVICE_REQUEST do_fd_request
+#define DEVICE_NR(device) ( (MINOR(device) & 3) | ((MINOR(device) & 0x80 ) >> 5 ))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
+
+#elif (MAJOR_NR == HD_MAJOR)
+
+/* Hard disk: timeout is 6 seconds. */
+#define DEVICE_NAME "hard disk"
+#define DEVICE_INTR do_hd
+#define DEVICE_TIMEOUT HD_TIMER
+#define TIMEOUT_VALUE (6*HZ)
+#define DEVICE_REQUEST do_hd_request
+#define DEVICE_NR(device) (MINOR(device)>>6)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (SCSI_DISK_MAJOR(MAJOR_NR))
+
+#define DEVICE_NAME "scsidisk"
+#define DEVICE_INTR do_sd
+#define TIMEOUT_VALUE (2*HZ)
+#define DEVICE_REQUEST do_sd_request
+#define DEVICE_NR(device) (((MAJOR(device) & SD_MAJOR_MASK) << (8 - 4)) + (MINOR(device) >> 4))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+/* Kludge to use the same number for both char and block major numbers */
+#elif (MAJOR_NR == MD_MAJOR) && defined(MD_DRIVER)
+
+#define DEVICE_NAME "Multiple devices driver"
+#define DEVICE_REQUEST do_md_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == SCSI_TAPE_MAJOR)
+
+#define DEVICE_NAME "scsitape"
+#define DEVICE_INTR do_st
+#define DEVICE_NR(device) (MINOR(device) & 0x7f)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == SCSI_CDROM_MAJOR)
+
+#define DEVICE_NAME "CD-ROM"
+#define DEVICE_INTR do_sr
+#define DEVICE_REQUEST do_sr_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == XT_DISK_MAJOR)
+
+#define DEVICE_NAME "xt disk"
+#define DEVICE_REQUEST do_xd_request
+#define DEVICE_NR(device) (MINOR(device) >> 6)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == PS2ESDI_MAJOR)
+
+#define DEVICE_NAME "PS/2 ESDI"
+#define DEVICE_REQUEST do_ps2esdi_request
+#define DEVICE_NR(device) (MINOR(device) >> 6)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == CDU31A_CDROM_MAJOR)
+
+#define DEVICE_NAME "CDU31A"
+#define DEVICE_REQUEST do_cdu31a_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == ACSI_MAJOR) && (defined(CONFIG_ATARI_ACSI) || defined(CONFIG_ATARI_ACSI_MODULE))
+
+#define DEVICE_NAME "ACSI"
+#define DEVICE_INTR do_acsi
+#define DEVICE_REQUEST do_acsi_request
+#define DEVICE_NR(device) (MINOR(device) >> 4)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MITSUMI_CDROM_MAJOR)
+
+#define DEVICE_NAME "Mitsumi CD-ROM"
+/* #define DEVICE_INTR do_mcd */
+#define DEVICE_REQUEST do_mcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MITSUMI_X_CDROM_MAJOR)
+
+#define DEVICE_NAME "Mitsumi CD-ROM"
+/* #define DEVICE_INTR do_mcdx */
+#define DEVICE_REQUEST do_mcdx_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MATSUSHITA_CDROM_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM controller #1"
+#define DEVICE_REQUEST do_sbpcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MATSUSHITA_CDROM2_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM controller #2"
+#define DEVICE_REQUEST do_sbpcd2_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MATSUSHITA_CDROM3_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM controller #3"
+#define DEVICE_REQUEST do_sbpcd3_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MATSUSHITA_CDROM4_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM controller #4"
+#define DEVICE_REQUEST do_sbpcd4_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == AZTECH_CDROM_MAJOR)
+
+#define DEVICE_NAME "Aztech CD-ROM"
+#define DEVICE_REQUEST do_aztcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == CDU535_CDROM_MAJOR)
+
+#define DEVICE_NAME "SONY-CDU535"
+#define DEVICE_INTR do_cdu535
+#define DEVICE_REQUEST do_cdu535_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == GOLDSTAR_CDROM_MAJOR)
+
+#define DEVICE_NAME "Goldstar R420"
+#define DEVICE_REQUEST do_gscd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == CM206_CDROM_MAJOR)
+#define DEVICE_NAME "Philips/LMS CD-ROM cm206"
+#define DEVICE_REQUEST do_cm206_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == OPTICS_CDROM_MAJOR)
+
+#define DEVICE_NAME "DOLPHIN 8000AT CD-ROM"
+#define DEVICE_REQUEST do_optcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == SANYO_CDROM_MAJOR)
+
+#define DEVICE_NAME "Sanyo H94A CD-ROM"
+#define DEVICE_REQUEST do_sjcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == APBLOCK_MAJOR)
+
+#define DEVICE_NAME "apblock"
+#define DEVICE_REQUEST ap_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == DDV_MAJOR)
+
+#define DEVICE_NAME "ddv"
+#define DEVICE_REQUEST ddv_request
+#define DEVICE_NR(device) (MINOR(device)>>PARTN_BITS)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MFM_ACORN_MAJOR)
+
+#define DEVICE_NAME "mfm disk"
+#define DEVICE_INTR do_mfm
+#define DEVICE_REQUEST do_mfm_request
+#define DEVICE_NR(device) (MINOR(device) >> 6)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MFM_ACORN_MAJOR)
+
+#define DEVICE_NAME "mfm disk"
+#define DEVICE_INTR do_mfm
+#define DEVICE_REQUEST do_mfm_request
+#define DEVICE_NR(device) (MINOR(device) >> 6)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == NBD_MAJOR)
+
+#define DEVICE_NAME "nbd"
+#define DEVICE_REQUEST do_nbd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+
+#elif (MAJOR_NR == MDISK_MAJOR)
+
+#define DEVICE_NAME "mdisk"
+#define DEVICE_REQUEST mdisk_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == DASD_MAJOR)
+
+#define DEVICE_NAME "dasd"
+#define DEVICE_REQUEST do_dasd_request
+#define DEVICE_NR(device) (MINOR(device) >> PARTN_BITS)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == COMPAQ_SMART2_MAJOR)
+
+#define DEVICE_NAME "ida"
+#define TIMEOUT_VALUE (25*HZ)
+#define DEVICE_REQUEST do_ida_request0
+#define DEVICE_NR(device) (MINOR(device) >> 4)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#endif /* MAJOR_NR == whatever */
+
+#if (MAJOR_NR != SCSI_TAPE_MAJOR)
+#if !defined(IDE_DRIVER)
+
+#ifndef CURRENT
+#define CURRENT (blk_dev[MAJOR_NR].current_request)
+#endif
+
+#ifndef DEVICE_NAME
+#define DEVICE_NAME "unknown"
+#endif
+
+#define CURRENT_DEV DEVICE_NR(CURRENT->rq_dev)
+
+#ifdef DEVICE_INTR
+static void (*DEVICE_INTR)(void) = NULL;
+#endif
+
+#ifdef DEVICE_TIMEOUT
+
+#define SET_TIMER \
+((timer_table[DEVICE_TIMEOUT].expires = jiffies + TIMEOUT_VALUE), \
+(timer_active |= 1<<DEVICE_TIMEOUT))
+
+#define CLEAR_TIMER \
+timer_active &= ~(1<<DEVICE_TIMEOUT)
+
+#define SET_INTR(x) \
+if ((DEVICE_INTR = (x)) != NULL) \
+ SET_TIMER; \
+else \
+ CLEAR_TIMER;
+
+#else
+
+#define SET_INTR(x) (DEVICE_INTR = (x))
+
+#endif /* DEVICE_TIMEOUT */
+
+static void (DEVICE_REQUEST)(void);
+
+#ifdef DEVICE_INTR
+#define CLEAR_INTR SET_INTR(NULL)
+#else
+#define CLEAR_INTR
+#endif
+
+#define INIT_REQUEST \
+ if (!CURRENT) {\
+ CLEAR_INTR; \
+ return; \
+ } \
+ if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) \
+ panic(DEVICE_NAME ": request list destroyed"); \
+ if (CURRENT->bh) { \
+ if (!buffer_locked(CURRENT->bh)) \
+ panic(DEVICE_NAME ": block not locked"); \
+ }
+
+#endif /* !defined(IDE_DRIVER) */
+
+#ifndef LOCAL_END_REQUEST /* If we have our own end_request, we do not want to include this mess */
+
+#if ! SCSI_BLK_MAJOR(MAJOR_NR) && (MAJOR_NR != COMPAQ_SMART2_MAJOR)
+
+static void end_request(int uptodate) {
+ struct request *req = CURRENT;
+
+ if (end_that_request_first(req, uptodate, DEVICE_NAME))
+ return;
+
+#ifndef DEVICE_NO_RANDOM
+ add_blkdev_randomness(MAJOR(req->rq_dev));
+#endif
+ DEVICE_OFF(req->rq_dev);
+ CURRENT = req->next;
+ end_that_request_last(req);
+}
+
+#endif /* ! SCSI_BLK_MAJOR(MAJOR_NR) */
+#endif /* LOCAL_END_REQUEST */
+
+#endif /* (MAJOR_NR != SCSI_TAPE_MAJOR) */
+#endif /* defined(MAJOR_NR) || defined(IDE_DRIVER) */
+
+#endif /* _BLK_H */
diff --git a/pfinet/linux-src/include/linux/blkdev.h b/pfinet/linux-src/include/linux/blkdev.h
new file mode 100644
index 00000000..87a9092b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/blkdev.h
@@ -0,0 +1,94 @@
+#ifndef _LINUX_BLKDEV_H
+#define _LINUX_BLKDEV_H
+
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/genhd.h>
+#include <linux/tqueue.h>
+
+/*
+ * Ok, this is an expanded form so that we can use the same
+ * request for paging requests when that is implemented. In
+ * paging, 'bh' is NULL, and the semaphore is used to wait
+ * for read/write completion.
+ */
+struct request {
+ volatile int rq_status; /* should split this into a few status bits */
+#define RQ_INACTIVE (-1)
+#define RQ_ACTIVE 1
+#define RQ_SCSI_BUSY 0xffff
+#define RQ_SCSI_DONE 0xfffe
+#define RQ_SCSI_DISCONNECTING 0xffe0
+
+ kdev_t rq_dev;
+ int cmd; /* READ or WRITE */
+ int errors;
+ unsigned long sector;
+ unsigned long nr_sectors;
+ unsigned long nr_segments;
+ unsigned long current_nr_sectors;
+ char * buffer;
+ struct semaphore * sem;
+ struct buffer_head * bh;
+ struct buffer_head * bhtail;
+ struct request * next;
+};
+
+typedef void (request_fn_proc) (void);
+typedef struct request ** (queue_proc) (kdev_t dev);
+
+struct blk_dev_struct {
+ request_fn_proc *request_fn;
+ /*
+ * queue_proc has to be atomic
+ */
+ queue_proc *queue;
+ void *data;
+ struct request *current_request;
+ struct request plug;
+ struct tq_struct plug_tq;
+};
+
+struct sec_size {
+ unsigned block_size;
+ unsigned block_size_bits;
+};
+
+extern struct sec_size * blk_sec[MAX_BLKDEV];
+extern struct blk_dev_struct blk_dev[MAX_BLKDEV];
+extern struct wait_queue * wait_for_request;
+extern void resetup_one_dev(struct gendisk *dev, int drive);
+extern void unplug_device(void * data);
+extern void make_request(int major,int rw, struct buffer_head * bh);
+
+/* md needs this function to remap requests */
+extern int md_map (int minor, kdev_t *rdev, unsigned long *rsector, unsigned long size);
+extern int md_make_request (int minor, int rw, struct buffer_head * bh);
+extern int md_error (kdev_t mddev, kdev_t rdev);
+
+extern int * blk_size[MAX_BLKDEV];
+
+extern int * blksize_size[MAX_BLKDEV];
+
+extern int * hardsect_size[MAX_BLKDEV];
+
+extern int * max_readahead[MAX_BLKDEV];
+
+extern int * max_sectors[MAX_BLKDEV];
+
+extern int * max_segments[MAX_BLKDEV];
+
+#define MAX_SECTORS 128
+
+#define MAX_SEGMENTS MAX_SECTORS
+
+#define PageAlignSize(size) (((size) + PAGE_SIZE -1) & PAGE_MASK)
+#if 0 /* small readahead */
+#define MAX_READAHEAD PageAlignSize(4096*7)
+#define MIN_READAHEAD PageAlignSize(4096*2)
+#else /* large readahead */
+#define MAX_READAHEAD PageAlignSize(4096*31)
+#define MIN_READAHEAD PageAlignSize(4096*3)
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/bpqether.h b/pfinet/linux-src/include/linux/bpqether.h
new file mode 100644
index 00000000..a6c35e1a
--- /dev/null
+++ b/pfinet/linux-src/include/linux/bpqether.h
@@ -0,0 +1,41 @@
+#ifndef __BPQETHER_H
+#define __BPQETHER_H
+
+/*
+ * Defines for the BPQETHER pseudo device driver
+ */
+
+#ifndef __LINUX_IF_ETHER_H
+#include <linux/if_ether.h>
+#endif
+
+#define SIOCSBPQETHOPT (SIOCDEVPRIVATE+0) /* reserved */
+#define SIOCSBPQETHADDR (SIOCDEVPRIVATE+1)
+
+struct bpq_ethaddr {
+ unsigned char destination[ETH_ALEN];
+ unsigned char accept[ETH_ALEN];
+};
+
+/*
+ * For SIOCSBPQETHOPT - this is compatible with PI2/PacketTwin card drivers,
+ * currently not implemented, though. If someone wants to hook a radio
+ * to his Ethernet card he may find this useful. ;-)
+ */
+
+#define SIOCGBPQETHPARAM 0x5000 /* get Level 1 parameters */
+#define SIOCSBPQETHPARAM 0x5001 /* set */
+
+struct bpq_req {
+ int cmd;
+ int speed; /* unused */
+ int clockmode; /* unused */
+ int txdelay;
+ unsigned char persist; /* unused */
+ int slotime; /* unused */
+ int squeldelay;
+ int dmachan; /* unused */
+ int irq; /* unused */
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/busmouse.h b/pfinet/linux-src/include/linux/busmouse.h
new file mode 100644
index 00000000..eaa7b6be
--- /dev/null
+++ b/pfinet/linux-src/include/linux/busmouse.h
@@ -0,0 +1,103 @@
+#ifndef _LINUX_BUSMOUSE_H
+#define _LINUX_BUSMOUSE_H
+
+/*
+ * linux/include/linux/busmouse.h: header file for Logitech Bus Mouse driver
+ * by James Banks
+ *
+ * based on information gleamed from various mouse drivers on the net
+ *
+ * Heavily modified by David giller (rafetmad@oxy.edu)
+ *
+ * Minor modifications for Linux 0.96c-pl1 by Nathan Laredo
+ * gt7080a@prism.gatech.edu (13JUL92)
+ *
+ * Microsoft BusMouse support by Teemu Rantanen (tvr@cs.hut.fi) (02AUG92)
+ *
+ * Microsoft Bus Mouse support modified by Derrick Cole (cole@concert.net)
+ * 8/28/92
+ *
+ * Microsoft Bus Mouse support folded into 0.97pl4 code
+ * by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92)
+ * Changes: Logitech and Microsoft support in the same kernel.
+ * Defined new constants in busmouse.h for MS mice.
+ * Added int mse_busmouse_type to distinguish busmouse types
+ * Added a couple of new functions to handle differences in using
+ * MS vs. Logitech (where the int variable wasn't appropriate).
+ *
+ */
+
+#define MOUSE_IRQ 5
+#define LOGITECH_BUSMOUSE 0 /* Minor device # for Logitech */
+#define MICROSOFT_BUSMOUSE 2 /* Minor device # for Microsoft */
+
+/*--------- LOGITECH BUSMOUSE ITEMS -------------*/
+
+#define LOGIBM_BASE 0x23c
+#define MSE_DATA_PORT 0x23c
+#define MSE_SIGNATURE_PORT 0x23d
+#define MSE_CONTROL_PORT 0x23e
+#define MSE_INTERRUPT_PORT 0x23e
+#define MSE_CONFIG_PORT 0x23f
+#define LOGIBM_EXTENT 0x4
+
+#define MSE_ENABLE_INTERRUPTS 0x00
+#define MSE_DISABLE_INTERRUPTS 0x10
+
+#define MSE_READ_X_LOW 0x80
+#define MSE_READ_X_HIGH 0xa0
+#define MSE_READ_Y_LOW 0xc0
+#define MSE_READ_Y_HIGH 0xe0
+
+/* Magic number used to check if the mouse exists */
+#define MSE_CONFIG_BYTE 0x91
+#define MSE_DEFAULT_MODE 0x90
+#define MSE_SIGNATURE_BYTE 0xa5
+
+/* useful Logitech Mouse macros */
+
+#define MSE_INT_OFF() outb(MSE_DISABLE_INTERRUPTS, MSE_CONTROL_PORT)
+#define MSE_INT_ON() outb(MSE_ENABLE_INTERRUPTS, MSE_CONTROL_PORT)
+
+/*--------- MICROSOFT BUSMOUSE ITEMS -------------*/
+
+#define MSBM_BASE 0x23d
+#define MS_MSE_DATA_PORT 0x23d
+#define MS_MSE_SIGNATURE_PORT 0x23e
+#define MS_MSE_CONTROL_PORT 0x23c
+#define MS_MSE_CONFIG_PORT 0x23f
+#define MSBM_EXTENT 0x3
+
+#define MS_MSE_ENABLE_INTERRUPTS 0x11
+#define MS_MSE_DISABLE_INTERRUPTS 0x10
+
+#define MS_MSE_READ_BUTTONS 0x00
+#define MS_MSE_READ_X 0x01
+#define MS_MSE_READ_Y 0x02
+
+#define MS_MSE_START 0x80
+#define MS_MSE_COMMAND_MODE 0x07
+
+/* useful microsoft busmouse macros */
+
+#define MS_MSE_INT_OFF() {outb(MS_MSE_COMMAND_MODE, MS_MSE_CONTROL_PORT); \
+ outb(MS_MSE_DISABLE_INTERRUPTS, MS_MSE_DATA_PORT);}
+#define MS_MSE_INT_ON() {outb(MS_MSE_COMMAND_MODE, MS_MSE_CONTROL_PORT); \
+ outb(MS_MSE_ENABLE_INTERRUPTS, MS_MSE_DATA_PORT);}
+
+
+struct mouse_status {
+ unsigned char buttons;
+ unsigned char latch_buttons;
+ int dx;
+ int dy;
+ int present;
+ int ready;
+ int active;
+ struct wait_queue *wait;
+ struct fasync_struct *fasyncptr;
+};
+
+/* Function Prototypes */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/capability.h b/pfinet/linux-src/include/linux/capability.h
new file mode 100644
index 00000000..03c93dad
--- /dev/null
+++ b/pfinet/linux-src/include/linux/capability.h
@@ -0,0 +1,337 @@
+/*
+ * This is <linux/capability.h>
+ *
+ * Andrew G. Morgan <morgan@transmeta.com>
+ * Alexander Kjeldaas <astor@guardian.no>
+ * with help from Aleph1, Roland Buresund and Andrew Main.
+ */
+
+#ifndef _LINUX_CAPABILITY_H
+#define _LINUX_CAPABILITY_H
+
+#include <linux/types.h>
+#include <linux/fs.h>
+
+/* User-level do most of the mapping between kernel and user
+ capabilities based on the version tag given by the kernel. The
+ kernel might be somewhat backwards compatible, but don't bet on
+ it. */
+
+/* XXX - Note, cap_t, is defined by POSIX to be an "opaque" pointer to
+ a set of three capability sets. The transposition of 3*the
+ following structure to such a composite is better handled in a user
+ library since the draft standard requires the use of malloc/free
+ etc.. */
+
+#define _LINUX_CAPABILITY_VERSION 0x19980330
+
+typedef struct __user_cap_header_struct {
+ __u32 version;
+ int pid;
+} *cap_user_header_t;
+
+typedef struct __user_cap_data_struct {
+ __u32 effective;
+ __u32 permitted;
+ __u32 inheritable;
+} *cap_user_data_t;
+
+#ifdef __KERNEL__
+
+/* #define STRICT_CAP_T_TYPECHECKS */
+
+#ifdef STRICT_CAP_T_TYPECHECKS
+
+typedef struct kernel_cap_struct {
+ __u32 cap;
+} kernel_cap_t;
+
+#else
+
+typedef __u32 kernel_cap_t;
+
+#endif
+
+#define _USER_CAP_HEADER_SIZE (2*sizeof(__u32))
+#define _KERNEL_CAP_T_SIZE (sizeof(kernel_cap_t))
+
+#endif
+
+
+/**
+ ** POSIX-draft defined capabilities.
+ **/
+
+/* In a system with the [_POSIX_CHOWN_RESTRICTED] option defined, this
+ overrides the restriction of changing file ownership and group
+ ownership. */
+
+#define CAP_CHOWN 0
+
+/* Override all DAC access, including ACL execute access if
+ [_POSIX_ACL] is defined. Excluding DAC access covered by
+ CAP_LINUX_IMMUTABLE. */
+
+#define CAP_DAC_OVERRIDE 1
+
+/* Overrides all DAC restrictions regarding read and search on files
+ and directories, including ACL restrictions if [_POSIX_ACL] is
+ defined. Excluding DAC access covered by CAP_LINUX_IMMUTABLE. */
+
+#define CAP_DAC_READ_SEARCH 2
+
+/* Overrides all restrictions about allowed operations on files, where
+ file owner ID must be equal to the user ID, except where CAP_FSETID
+ is applicable. It doesn't override MAC and DAC restrictions. */
+
+#define CAP_FOWNER 3
+
+/* Overrides the following restrictions that the effective user ID
+ shall match the file owner ID when setting the S_ISUID and S_ISGID
+ bits on that file; that the effective group ID (or one of the
+ supplementary group IDs) shall match the file owner ID when setting
+ the S_ISGID bit on that file; that the S_ISUID and S_ISGID bits are
+ cleared on successful return from chown(2) (not implemented). */
+
+#define CAP_FSETID 4
+
+/* Used to decide between falling back on the old suser() or fsuser(). */
+
+#define CAP_FS_MASK 0x1f
+
+/* Overrides the restriction that the real or effective user ID of a
+ process sending a signal must match the real or effective user ID
+ of the process receiving the signal. */
+
+#define CAP_KILL 5
+
+/* Allows setgid(2) manipulation */
+/* Allows setgroups(2) */
+/* Allows forged gids on socket credentials passing. */
+
+#define CAP_SETGID 6
+
+/* Allows set*uid(2) manipulation (including fsuid). */
+/* Allows forged pids on socket credentials passing. */
+
+#define CAP_SETUID 7
+
+
+/**
+ ** Linux-specific capabilities
+ **/
+
+/* Transfer any capability in your permitted set to any pid,
+ remove any capability in your permitted set from any pid */
+
+#define CAP_SETPCAP 8
+
+/* Allow modification of S_IMMUTABLE and S_APPEND file attributes */
+
+#define CAP_LINUX_IMMUTABLE 9
+
+/* Allows binding to TCP/UDP sockets below 1024 */
+
+#define CAP_NET_BIND_SERVICE 10
+
+/* Allow broadcasting, listen to multicast */
+
+#define CAP_NET_BROADCAST 11
+
+/* Allow interface configuration */
+/* Allow administration of IP firewall, masquerading and accounting */
+/* Allow setting debug option on sockets */
+/* Allow modification of routing tables */
+/* Allow setting arbitrary process / process group ownership on
+ sockets */
+/* Allow binding to any address for transparent proxying */
+/* Allow setting TOS (type of service) */
+/* Allow setting promiscuous mode */
+/* Allow clearing driver statistics */
+/* Allow multicasting */
+/* Allow read/write of device-specific registers */
+
+#define CAP_NET_ADMIN 12
+
+/* Allow use of RAW sockets */
+/* Allow use of PACKET sockets */
+
+#define CAP_NET_RAW 13
+
+/* Allow locking of shared memory segments */
+/* Allow mlock and mlockall (which doesn't really have anything to do
+ with IPC) */
+
+#define CAP_IPC_LOCK 14
+
+/* Override IPC ownership checks */
+
+#define CAP_IPC_OWNER 15
+
+/* Insert and remove kernel modules */
+
+#define CAP_SYS_MODULE 16
+
+/* Allow ioperm/iopl access */
+
+#define CAP_SYS_RAWIO 17
+
+/* Allow use of chroot() */
+
+#define CAP_SYS_CHROOT 18
+
+/* Allow ptrace() of any process */
+
+#define CAP_SYS_PTRACE 19
+
+/* Allow configuration of process accounting */
+
+#define CAP_SYS_PACCT 20
+
+/* Allow configuration of the secure attention key */
+/* Allow administration of the random device */
+/* Allow device administration (mknod)*/
+/* Allow examination and configuration of disk quotas */
+/* Allow configuring the kernel's syslog (printk behaviour) */
+/* Allow setting the domainname */
+/* Allow setting the hostname */
+/* Allow calling bdflush() */
+/* Allow mount() and umount(), setting up new smb connection */
+/* Allow some autofs root ioctls */
+/* Allow nfsservctl */
+/* Allow VM86_REQUEST_IRQ */
+/* Allow to read/write pci config on alpha */
+/* Allow irix_prctl on mips (setstacksize) */
+/* Allow flushing all cache on m68k (sys_cacheflush) */
+/* Allow removing semaphores */
+/* Used instead of CAP_CHOWN to "chown" IPC message queues, semaphores
+ and shared memory */
+/* Allow locking/unlocking of shared memory segment */
+/* Allow turning swap on/off */
+/* Allow forged pids on socket credentials passing */
+/* Allow setting readahead and flushing buffers on block devices */
+/* Allow setting geometry in floppy driver */
+/* Allow turning DMA on/off in xd driver */
+/* Allow administration of md devices (mostly the above, but some
+ extra ioctls) */
+/* Allow tuning the ide driver */
+/* Allow access to the nvram device */
+/* Allow administration of apm_bios, serial and bttv (TV) device */
+/* Allow manufacturer commands in isdn CAPI support driver */
+/* Allow reading non-standardized portions of pci configuration space */
+/* Allow DDI debug ioctl on sbpcd driver */
+/* Allow setting up serial ports */
+/* Allow sending raw qic-117 commands */
+/* Allow enabling/disabling tagged queuing on SCSI controllers and sending
+ arbitrary SCSI commands */
+/* Allow setting encryption key on loopback filesystem */
+
+#define CAP_SYS_ADMIN 21
+
+/* Allow use of reboot() */
+
+#define CAP_SYS_BOOT 22
+
+/* Allow raising priority and setting priority on other (different
+ UID) processes */
+/* Allow use of FIFO and round-robin (realtime) scheduling on own
+ processes and setting the scheduling algorithm used by another
+ process. */
+
+#define CAP_SYS_NICE 23
+
+/* Override resource limits. Set resource limits. */
+/* Override quota limits. */
+/* Override reserved space on ext2 filesystem */
+/* NOTE: ext2 honors fsuid when checking for resource overrides, so
+ you can override using fsuid too */
+/* Override size restrictions on IPC message queues */
+/* Allow more than 64hz interrupts from the real-time clock */
+/* Override max number of consoles on console allocation */
+/* Override max number of keymaps */
+
+#define CAP_SYS_RESOURCE 24
+
+/* Allow manipulation of system clock */
+/* Allow irix_stime on mips */
+/* Allow setting the real-time clock */
+
+#define CAP_SYS_TIME 25
+
+/* Allow configuration of tty devices */
+/* Allow vhangup() of tty */
+
+#define CAP_SYS_TTY_CONFIG 26
+
+#ifdef __KERNEL__
+/*
+ * Bounding set
+ */
+extern kernel_cap_t cap_bset;
+
+/*
+ * Internal kernel functions only
+ */
+
+#ifdef STRICT_CAP_T_TYPECHECKS
+
+#define to_cap_t(x) { x }
+#define cap_t(x) (x).cap
+
+#else
+
+#define to_cap_t(x) (x)
+#define cap_t(x) (x)
+
+#endif
+
+#define CAP_EMPTY_SET to_cap_t(0)
+#define CAP_FULL_SET to_cap_t(~0)
+#define CAP_INIT_EFF_SET to_cap_t(~0 & ~CAP_TO_MASK(CAP_SETPCAP))
+#define CAP_INIT_INH_SET to_cap_t(~0 & ~CAP_TO_MASK(CAP_SETPCAP))
+
+#define CAP_TO_MASK(x) (1 << (x))
+#define cap_raise(c, flag) (cap_t(c) |= CAP_TO_MASK(flag))
+#define cap_lower(c, flag) (cap_t(c) &= ~CAP_TO_MASK(flag))
+#define cap_raised(c, flag) (cap_t(c) & CAP_TO_MASK(flag) & cap_bset)
+
+static inline kernel_cap_t cap_combine(kernel_cap_t a, kernel_cap_t b)
+{
+ kernel_cap_t dest;
+ cap_t(dest) = cap_t(a) | cap_t(b);
+ return dest;
+}
+
+static inline kernel_cap_t cap_intersect(kernel_cap_t a, kernel_cap_t b)
+{
+ kernel_cap_t dest;
+ cap_t(dest) = cap_t(a) & cap_t(b);
+ return dest;
+}
+
+static inline kernel_cap_t cap_drop(kernel_cap_t a, kernel_cap_t drop)
+{
+ kernel_cap_t dest;
+ cap_t(dest) = cap_t(a) & ~cap_t(drop);
+ return dest;
+}
+
+static inline kernel_cap_t cap_invert(kernel_cap_t c)
+{
+ kernel_cap_t dest;
+ cap_t(dest) = ~cap_t(c);
+ return dest;
+}
+
+#define cap_isclear(c) (!cap_t(c))
+#define cap_issubset(a,set) (!(cap_t(a) & ~cap_t(set)))
+
+#define cap_clear(c) do { cap_t(c) = 0; } while(0)
+#define cap_set_full(c) do { cap_t(c) = ~0; } while(0)
+#define cap_mask(c,mask) do { cap_t(c) &= cap_t(mask); } while(0)
+
+#define cap_is_fs_cap(c) (CAP_TO_MASK(c) & CAP_FS_MASK)
+
+#endif /* __KERNEL__ */
+
+#endif /* !_LINUX_CAPABILITY_H */
diff --git a/pfinet/linux-src/include/linux/capi.h b/pfinet/linux-src/include/linux/capi.h
new file mode 100644
index 00000000..9876da08
--- /dev/null
+++ b/pfinet/linux-src/include/linux/capi.h
@@ -0,0 +1,127 @@
+/*
+ * $Id: capi.h,v 1.1 1997/03/04 21:27:33 calle Exp $
+ *
+ * CAPI 2.0 Interface for Linux
+ *
+ * Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
+ *
+ * $Log: capi.h,v $
+ * Revision 1.1 1997/03/04 21:27:33 calle
+ * First version in isdn4linux
+ *
+ * Revision 2.2 1997/02/12 09:31:39 calle
+ * new version
+ *
+ * Revision 1.1 1997/01/31 10:32:20 calle
+ * Initial revision
+ *
+ *
+ */
+
+#ifndef __LINUX_CAPI_H__
+#define __LINUX_CAPI_H__
+
+#include <asm/types.h>
+#include <linux/ioctl.h>
+#ifndef __KERNEL__
+#include <linux/kernelcapi.h>
+#endif
+
+/*
+ * CAPI_REGISTER
+ */
+
+typedef struct capi_register_params { /* CAPI_REGISTER */
+ __u32 level3cnt; /* No. of simulatneous user data connections */
+ __u32 datablkcnt; /* No. of buffered data messages */
+ __u32 datablklen; /* Size of buffered data messages */
+} capi_register_params;
+
+#define CAPI_REGISTER _IOW('C',0x01,struct capi_register_params)
+
+/*
+ * CAPI_GET_MANUFACTURER
+ */
+
+#define CAPI_MANUFACTURER_LEN 64
+
+#define CAPI_GET_MANUFACTURER _IOWR('C',0x06,CAPI_MANUFACTURER_LEN)
+
+/*
+ * CAPI_GET_VERSION
+ */
+
+typedef struct capi_version {
+ __u32 majorversion;
+ __u32 minorversion;
+ __u32 majormanuversion;
+ __u32 minormanuversion;
+} capi_version;
+
+#define CAPI_GET_VERSION _IOWR('C',0x07,struct capi_version)
+
+/*
+ * CAPI_GET_SERIAL
+ */
+
+#define CAPI_SERIAL_LEN 8
+#define CAPI_GET_SERIAL _IOWR('C',0x08, CAPI_SERIAL_LEN)
+
+/*
+ * CAPI_GET_PROFILE
+ */
+
+typedef struct capi_profile {
+ __u16 ncontroller; /* number of installed controller */
+ __u16 nbchannel; /* number of B-Channels */
+ __u32 goptions; /* global options */
+ __u32 support1; /* B1 protocols support */
+ __u32 support2; /* B2 protocols support */
+ __u32 support3; /* B3 protocols support */
+ __u32 reserved[6]; /* reserved */
+ __u32 manu[5]; /* manufacturer specific information */
+} capi_profile;
+
+#define CAPI_GET_PROFILE _IOWR('C',0x09,struct capi_profile)
+
+typedef struct capi_manufacturer_cmd {
+ unsigned long cmd;
+ void *data;
+} capi_manufacturer_cmd;
+
+/*
+ * CAPI_MANUFACTURER_CMD
+ */
+
+#define CAPI_MANUFACTURER_CMD _IOWR('C',0x20, struct capi_manufacturer_cmd)
+
+/*
+ * CAPI_GET_ERRCODE
+ * capi errcode is set, * if read, write, or ioctl returns EIO,
+ * ioctl returns errcode directly, and in arg, if != 0
+ */
+
+#define CAPI_GET_ERRCODE _IOR('C',0x21, __u16)
+
+/*
+ * CAPI_INSTALLED
+ */
+#define CAPI_INSTALLED _IOR('C',0x22, __u16)
+
+/*
+ * member contr is input for
+ * CAPI_GET_MANUFACTURER, CAPI_VERSION, CAPI_GET_SERIAL
+ * and CAPI_GET_PROFILE
+ */
+typedef union capi_ioctl_struct {
+ __u32 contr;
+ capi_register_params rparams;
+ __u8 manufacturer[CAPI_MANUFACTURER_LEN];
+ capi_version version;
+ __u8 serial[CAPI_SERIAL_LEN];
+ capi_profile profile;
+ capi_manufacturer_cmd cmd;
+ __u16 errcode;
+} capi_ioctl_struct;
+
+#endif /* __LINUX_CAPI_H__ */
diff --git a/pfinet/linux-src/include/linux/cd1400.h b/pfinet/linux-src/include/linux/cd1400.h
new file mode 100644
index 00000000..d07d1e61
--- /dev/null
+++ b/pfinet/linux-src/include/linux/cd1400.h
@@ -0,0 +1,292 @@
+/*****************************************************************************/
+
+/*
+ * cd1400.h -- cd1400 UART hardware info.
+ *
+ * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au).
+ * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*****************************************************************************/
+#ifndef _CD1400_H
+#define _CD1400_H
+/*****************************************************************************/
+
+/*
+ * Define the number of async ports per cd1400 uart chip.
+ */
+#define CD1400_PORTS 4
+
+/*
+ * Define the cd1400 uarts internal FIFO sizes.
+ */
+#define CD1400_TXFIFOSIZE 12
+#define CD1400_RXFIFOSIZE 12
+
+/*
+ * Local RX FIFO thresh hold level. Also define the RTS thresh hold
+ * based on the RX thresh hold.
+ */
+#define FIFO_RXTHRESHOLD 6
+#define FIFO_RTSTHRESHOLD 7
+
+/*****************************************************************************/
+
+/*
+ * Define the cd1400 register addresses. These are all the valid
+ * registers with the cd1400. Some are global, some virtual, some
+ * per port.
+ */
+#define GFRCR 0x40
+#define CAR 0x68
+#define GCR 0x4b
+#define SVRR 0x67
+#define RICR 0x44
+#define TICR 0x45
+#define MICR 0x46
+#define RIR 0x6b
+#define TIR 0x6a
+#define MIR 0x69
+#define PPR 0x7e
+
+#define RIVR 0x43
+#define TIVR 0x42
+#define MIVR 0x41
+#define TDR 0x63
+#define RDSR 0x62
+#define MISR 0x4c
+#define EOSRR 0x60
+
+#define LIVR 0x18
+#define CCR 0x05
+#define SRER 0x06
+#define COR1 0x08
+#define COR2 0x09
+#define COR3 0x0a
+#define COR4 0x1e
+#define COR5 0x1f
+#define CCSR 0x0b
+#define RDCR 0x0e
+#define SCHR1 0x1a
+#define SCHR2 0x1b
+#define SCHR3 0x1c
+#define SCHR4 0x1d
+#define SCRL 0x22
+#define SCRH 0x23
+#define LNC 0x24
+#define MCOR1 0x15
+#define MCOR2 0x16
+#define RTPR 0x21
+#define MSVR1 0x6c
+#define MSVR2 0x6d
+#define PSVR 0x6f
+#define RBPR 0x78
+#define RCOR 0x7c
+#define TBPR 0x72
+#define TCOR 0x76
+
+/*****************************************************************************/
+
+/*
+ * Define the set of baud rate clock divisors.
+ */
+#define CD1400_CLK0 8
+#define CD1400_CLK1 32
+#define CD1400_CLK2 128
+#define CD1400_CLK3 512
+#define CD1400_CLK4 2048
+
+#define CD1400_NUMCLKS 5
+
+/*****************************************************************************/
+
+/*
+ * Define the clock pre-scalar value to be a 5 ms clock. This should be
+ * OK for now. It would probably be better to make it 10 ms, but we
+ * can't fit that divisor into 8 bits!
+ */
+#define PPR_SCALAR 244
+
+/*****************************************************************************/
+
+/*
+ * Define values used to set character size options.
+ */
+#define COR1_CHL5 0x00
+#define COR1_CHL6 0x01
+#define COR1_CHL7 0x02
+#define COR1_CHL8 0x03
+
+/*
+ * Define values used to set the number of stop bits.
+ */
+#define COR1_STOP1 0x00
+#define COR1_STOP15 0x04
+#define COR1_STOP2 0x08
+
+/*
+ * Define values used to set the parity scheme in use.
+ */
+#define COR1_PARNONE 0x00
+#define COR1_PARFORCE 0x20
+#define COR1_PARENB 0x40
+#define COR1_PARIGNORE 0x10
+
+#define COR1_PARODD 0x80
+#define COR1_PAREVEN 0x00
+
+#define COR2_IXM 0x80
+#define COR2_TXIBE 0x40
+#define COR2_ETC 0x20
+#define COR2_LLM 0x10
+#define COR2_RLM 0x08
+#define COR2_RTSAO 0x04
+#define COR2_CTSAE 0x02
+
+#define COR3_SCDRNG 0x80
+#define COR3_SCD34 0x40
+#define COR3_FCT 0x20
+#define COR3_SCD12 0x10
+
+/*
+ * Define values used by COR4.
+ */
+#define COR4_BRKINT 0x08
+#define COR4_IGNBRK 0x18
+
+/*****************************************************************************/
+
+/*
+ * Define the modem control register values.
+ * Note that the actual hardware is a little different to the conventional
+ * pin names on the cd1400.
+ */
+#define MSVR1_DTR 0x01
+#define MSVR1_DSR 0x10
+#define MSVR1_RI 0x20
+#define MSVR1_CTS 0x40
+#define MSVR1_DCD 0x80
+
+#define MSVR2_RTS 0x02
+#define MSVR2_DSR 0x10
+#define MSVR2_RI 0x20
+#define MSVR2_CTS 0x40
+#define MSVR2_DCD 0x80
+
+#define MCOR1_DCD 0x80
+#define MCOR1_CTS 0x40
+#define MCOR1_RI 0x20
+#define MCOR1_DSR 0x10
+
+#define MCOR2_DCD 0x80
+#define MCOR2_CTS 0x40
+#define MCOR2_RI 0x20
+#define MCOR2_DSR 0x10
+
+/*****************************************************************************/
+
+/*
+ * Define the bits used with the service (interrupt) enable register.
+ */
+#define SRER_NNDT 0x01
+#define SRER_TXEMPTY 0x02
+#define SRER_TXDATA 0x04
+#define SRER_RXDATA 0x10
+#define SRER_MODEM 0x80
+
+/*****************************************************************************/
+
+/*
+ * Define operational commands for the command register.
+ */
+#define CCR_RESET 0x80
+#define CCR_CORCHANGE 0x4e
+#define CCR_SENDCH 0x20
+#define CCR_CHANCTRL 0x10
+
+#define CCR_TXENABLE (CCR_CHANCTRL | 0x08)
+#define CCR_TXDISABLE (CCR_CHANCTRL | 0x04)
+#define CCR_RXENABLE (CCR_CHANCTRL | 0x02)
+#define CCR_RXDISABLE (CCR_CHANCTRL | 0x01)
+
+#define CCR_SENDSCHR1 (CCR_SENDCH | 0x01)
+#define CCR_SENDSCHR2 (CCR_SENDCH | 0x02)
+#define CCR_SENDSCHR3 (CCR_SENDCH | 0x03)
+#define CCR_SENDSCHR4 (CCR_SENDCH | 0x04)
+
+#define CCR_RESETCHAN (CCR_RESET | 0x00)
+#define CCR_RESETFULL (CCR_RESET | 0x01)
+#define CCR_TXFLUSHFIFO (CCR_RESET | 0x02)
+
+#define CCR_MAXWAIT 10000
+
+/*****************************************************************************/
+
+/*
+ * Define the valid acknowledgement types (for hw ack cycle).
+ */
+#define ACK_TYPMASK 0x07
+#define ACK_TYPTX 0x02
+#define ACK_TYPMDM 0x01
+#define ACK_TYPRXGOOD 0x03
+#define ACK_TYPRXBAD 0x07
+
+#define SVRR_RX 0x01
+#define SVRR_TX 0x02
+#define SVRR_MDM 0x04
+
+#define ST_OVERRUN 0x01
+#define ST_FRAMING 0x02
+#define ST_PARITY 0x04
+#define ST_BREAK 0x08
+#define ST_SCHAR1 0x10
+#define ST_SCHAR2 0x20
+#define ST_SCHAR3 0x30
+#define ST_SCHAR4 0x40
+#define ST_RANGE 0x70
+#define ST_SCHARMASK 0x70
+#define ST_TIMEOUT 0x80
+
+#define MISR_DCD 0x80
+#define MISR_CTS 0x40
+#define MISR_RI 0x20
+#define MISR_DSR 0x10
+
+/*****************************************************************************/
+
+/*
+ * Defines for the CCSR status register.
+ */
+#define CCSR_RXENABLED 0x80
+#define CCSR_RXFLOWON 0x40
+#define CCSR_RXFLOWOFF 0x20
+#define CCSR_TXENABLED 0x08
+#define CCSR_TXFLOWON 0x04
+#define CCSR_TXFLOWOFF 0x02
+
+/*****************************************************************************/
+
+/*
+ * Define the embedded commands.
+ */
+#define ETC_CMD 0x00
+#define ETC_STARTBREAK 0x81
+#define ETC_DELAY 0x82
+#define ETC_STOPBREAK 0x83
+
+/*****************************************************************************/
+#endif
diff --git a/pfinet/linux-src/include/linux/cdk.h b/pfinet/linux-src/include/linux/cdk.h
new file mode 100644
index 00000000..2fab8949
--- /dev/null
+++ b/pfinet/linux-src/include/linux/cdk.h
@@ -0,0 +1,486 @@
+/*****************************************************************************/
+
+/*
+ * cdk.h -- CDK interface definitions.
+ *
+ * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au).
+ * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*****************************************************************************/
+#ifndef _CDK_H
+#define _CDK_H
+/*****************************************************************************/
+
+#pragma pack(2)
+
+/*
+ * The following set of definitions is used to communicate with the
+ * shared memory interface of the Stallion intelligent multiport serial
+ * boards. The definitions in this file are taken directly from the
+ * document titled "Generic Stackable Interface, Downloader and
+ * Communications Development Kit".
+ */
+
+/*
+ * Define the set of important shared memory addresses. These are
+ * required to initialize the board and get things started. All of these
+ * addresses are relative to the start of the shared memory.
+ */
+#define CDK_SIGADDR 0x200
+#define CDK_FEATADDR 0x280
+#define CDK_CDKADDR 0x300
+#define CDK_RDYADDR 0x262
+
+#define CDK_ALIVEMARKER 13
+
+/*
+ * On hardware power up the ROMs located on the EasyConnection 8/64 will
+ * fill out the following signature information into shared memory. This
+ * way the host system can quickly determine that the board is present
+ * and is operational.
+ */
+typedef struct cdkecpsig {
+ unsigned long magic;
+ unsigned short romver;
+ unsigned short cputype;
+ unsigned char panelid[8];
+} cdkecpsig_t;
+
+#define ECP_MAGIC 0x21504345
+
+/*
+ * On hardware power up the ROMs located on the ONboard, Stallion and
+ * Brumbys will fill out the following signature information into shared
+ * memory. This way the host system can quickly determine that the board
+ * is present and is operational.
+ */
+typedef struct cdkonbsig {
+ unsigned short magic0;
+ unsigned short magic1;
+ unsigned short magic2;
+ unsigned short magic3;
+ unsigned short romver;
+ unsigned short memoff;
+ unsigned short memseg;
+ unsigned short amask0;
+ unsigned short pic;
+ unsigned short status;
+ unsigned short btype;
+ unsigned short clkticks;
+ unsigned short clkspeed;
+ unsigned short amask1;
+ unsigned short amask2;
+} cdkonbsig_t;
+
+#define ONB_MAGIC0 0xf2a7
+#define ONB_MAGIC1 0xa149
+#define ONB_MAGIC2 0x6352
+#define ONB_MAGIC3 0xf121
+
+/*
+ * Define the feature area structure. The feature area is the set of
+ * startup parameters used by the slave image when it starts executing.
+ * They allow for the specification of buffer sizes, debug trace, etc.
+ */
+typedef struct cdkfeature {
+ unsigned long debug;
+ unsigned long banner;
+ unsigned long etype;
+ unsigned long nrdevs;
+ unsigned long brdspec;
+ unsigned long txrqsize;
+ unsigned long rxrqsize;
+ unsigned long flags;
+} cdkfeature_t;
+
+#define ETYP_DDK 0
+#define ETYP_CDK 1
+
+/*
+ * Define the CDK header structure. This is the info that the slave
+ * environment sets up after it has been downloaded and started. It
+ * essentially provides a memory map for the shared memory interface.
+ */
+typedef struct cdkhdr {
+ unsigned short command;
+ unsigned short status;
+ unsigned short port;
+ unsigned short mode;
+ unsigned long cmd_buf[14];
+ unsigned short alive_cnt;
+ unsigned short intrpt_mode;
+ unsigned char intrpt_id[8];
+ unsigned char ver_release;
+ unsigned char ver_modification;
+ unsigned char ver_fix;
+ unsigned char deadman_restart;
+ unsigned short deadman;
+ unsigned short nrdevs;
+ unsigned long memp;
+ unsigned long hostp;
+ unsigned long slavep;
+ unsigned char hostreq;
+ unsigned char slavereq;
+ unsigned char cmd_reserved[30];
+} cdkhdr_t;
+
+#define MODE_DDK 0
+#define MODE_CDK 1
+
+#define IMD_INTR 0x0
+#define IMD_PPINTR 0x1
+#define IMD_POLL 0xff
+
+/*
+ * Define the memory mapping structure. This structure is pointed to by
+ * the memp field in the stlcdkhdr struct. As many as these structures
+ * as required are laid out in shared memory to define how the rest of
+ * shared memory is divided up. There will be one for each port.
+ */
+typedef struct cdkmem {
+ unsigned short dtype;
+ unsigned long offset;
+} cdkmem_t;
+
+#define TYP_UNDEFINED 0x0
+#define TYP_ASYNCTRL 0x1
+#define TYP_ASYNC 0x20
+#define TYP_PARALLEL 0x40
+#define TYP_SYNCX21 0x60
+
+/*****************************************************************************/
+
+/*
+ * Following is a set of defines and structures used to actually deal
+ * with the serial ports on the board. Firstly is the set of commands
+ * that can be applied to ports.
+ */
+#define ASYCMD (((unsigned long) 'a') << 8)
+
+#define A_NULL (ASYCMD | 0)
+#define A_FLUSH (ASYCMD | 1)
+#define A_BREAK (ASYCMD | 2)
+#define A_GETPORT (ASYCMD | 3)
+#define A_SETPORT (ASYCMD | 4)
+#define A_SETPORTF (ASYCMD | 5)
+#define A_SETPORTFTX (ASYCMD | 6)
+#define A_SETPORTFRX (ASYCMD | 7)
+#define A_GETSIGNALS (ASYCMD | 8)
+#define A_SETSIGNALS (ASYCMD | 9)
+#define A_SETSIGNALSF (ASYCMD | 10)
+#define A_SETSIGNALSFTX (ASYCMD | 11)
+#define A_SETSIGNALSFRX (ASYCMD | 12)
+#define A_GETNOTIFY (ASYCMD | 13)
+#define A_SETNOTIFY (ASYCMD | 14)
+#define A_NOTIFY (ASYCMD | 15)
+#define A_PORTCTRL (ASYCMD | 16)
+#define A_GETSTATS (ASYCMD | 17)
+#define A_RQSTATE (ASYCMD | 18)
+#define A_FLOWSTATE (ASYCMD | 19)
+#define A_CLEARSTATS (ASYCMD | 20)
+
+/*
+ * Define those arguments used for simple commands.
+ */
+#define FLUSHRX 0x1
+#define FLUSHTX 0x2
+
+#define BREAKON -1
+#define BREAKOFF -2
+
+/*
+ * Define the port setting structure, and all those defines that go along
+ * with it. Basically this structure defines the characteristics of this
+ * port: baud rate, chars, parity, input/output char cooking etc.
+ */
+typedef struct asyport {
+ unsigned long baudout;
+ unsigned long baudin;
+ unsigned long iflag;
+ unsigned long oflag;
+ unsigned long lflag;
+ unsigned long pflag;
+ unsigned long flow;
+ unsigned long spare1;
+ unsigned short vtime;
+ unsigned short vmin;
+ unsigned short txlo;
+ unsigned short txhi;
+ unsigned short rxlo;
+ unsigned short rxhi;
+ unsigned short rxhog;
+ unsigned short spare2;
+ unsigned char csize;
+ unsigned char stopbs;
+ unsigned char parity;
+ unsigned char stopin;
+ unsigned char startin;
+ unsigned char stopout;
+ unsigned char startout;
+ unsigned char parmark;
+ unsigned char brkmark;
+ unsigned char cc[11];
+} asyport_t;
+
+#define PT_STOP1 0x0
+#define PT_STOP15 0x1
+#define PT_STOP2 0x2
+
+#define PT_NOPARITY 0x0
+#define PT_ODDPARITY 0x1
+#define PT_EVENPARITY 0x2
+#define PT_MARKPARITY 0x3
+#define PT_SPACEPARITY 0x4
+
+#define F_NONE 0x0
+#define F_IXON 0x1
+#define F_IXOFF 0x2
+#define F_IXANY 0x4
+#define F_IOXANY 0x8
+#define F_RTSFLOW 0x10
+#define F_CTSFLOW 0x20
+#define F_DTRFLOW 0x40
+#define F_DCDFLOW 0x80
+#define F_DSROFLOW 0x100
+#define F_DSRIFLOW 0x200
+
+#define FI_NORX 0x1
+#define FI_RAW 0x2
+#define FI_ISTRIP 0x4
+#define FI_UCLC 0x8
+#define FI_INLCR 0x10
+#define FI_ICRNL 0x20
+#define FI_IGNCR 0x40
+#define FI_IGNBREAK 0x80
+#define FI_DSCRDBREAK 0x100
+#define FI_1MARKBREAK 0x200
+#define FI_2MARKBREAK 0x400
+#define FI_XCHNGBREAK 0x800
+#define FI_IGNRXERRS 0x1000
+#define FI_DSCDRXERRS 0x2000
+#define FI_1MARKRXERRS 0x4000
+#define FI_2MARKRXERRS 0x8000
+#define FI_XCHNGRXERRS 0x10000
+#define FI_DSCRDNULL 0x20000
+
+#define FO_OLCUC 0x1
+#define FO_ONLCR 0x2
+#define FO_OOCRNL 0x4
+#define FO_ONOCR 0x8
+#define FO_ONLRET 0x10
+#define FO_ONL 0x20
+#define FO_OBS 0x40
+#define FO_OVT 0x80
+#define FO_OFF 0x100
+#define FO_OTAB1 0x200
+#define FO_OTAB2 0x400
+#define FO_OTAB3 0x800
+#define FO_OCR1 0x1000
+#define FO_OCR2 0x2000
+#define FO_OCR3 0x4000
+#define FO_OFILL 0x8000
+#define FO_ODELL 0x10000
+
+#define P_RTSLOCK 0x1
+#define P_CTSLOCK 0x2
+#define P_MAPRTS 0x4
+#define P_MAPCTS 0x8
+#define P_LOOPBACK 0x10
+#define P_DTRFOLLOW 0x20
+#define P_FAKEDCD 0x40
+
+#define P_RXIMIN 0x10000
+#define P_RXITIME 0x20000
+#define P_RXTHOLD 0x40000
+
+/*
+ * Define a structure to communicate serial port signal and data state
+ * information.
+ */
+typedef struct asysigs {
+ unsigned long data;
+ unsigned long signal;
+ unsigned long sigvalue;
+} asysigs_t;
+
+#define DT_TXBUSY 0x1
+#define DT_TXEMPTY 0x2
+#define DT_TXLOW 0x4
+#define DT_TXHIGH 0x8
+#define DT_TXFULL 0x10
+#define DT_TXHOG 0x20
+#define DT_TXFLOWED 0x40
+#define DT_TXBREAK 0x80
+
+#define DT_RXBUSY 0x100
+#define DT_RXEMPTY 0x200
+#define DT_RXLOW 0x400
+#define DT_RXHIGH 0x800
+#define DT_RXFULL 0x1000
+#define DT_RXHOG 0x2000
+#define DT_RXFLOWED 0x4000
+#define DT_RXBREAK 0x8000
+
+#define SG_DTR 0x1
+#define SG_DCD 0x2
+#define SG_RTS 0x4
+#define SG_CTS 0x8
+#define SG_DSR 0x10
+#define SG_RI 0x20
+
+/*
+ * Define the notification setting structure. This is used to tell the
+ * port what events we want to be informed about. Fields here use the
+ * same defines as for the asysigs structure above.
+ */
+typedef struct asynotify {
+ unsigned long ctrl;
+ unsigned long data;
+ unsigned long signal;
+ unsigned long sigvalue;
+} asynotify_t;
+
+/*
+ * Define the port control structure. It is used to do fine grain
+ * control operations on the port.
+ */
+typedef struct {
+ unsigned long rxctrl;
+ unsigned long txctrl;
+ char rximdch;
+ char tximdch;
+ char spare1;
+ char spare2;
+} asyctrl_t;
+
+#define CT_ENABLE 0x1
+#define CT_DISABLE 0x2
+#define CT_STOP 0x4
+#define CT_START 0x8
+#define CT_STARTFLOW 0x10
+#define CT_STOPFLOW 0x20
+#define CT_SENDCHR 0x40
+
+/*
+ * Define the stats structure kept for each port. This is a useful set
+ * of data collected for each port on the slave. The A_GETSTATS command
+ * is used to retrieve this data from the slave.
+ */
+typedef struct asystats {
+ unsigned long opens;
+ unsigned long txchars;
+ unsigned long rxchars;
+ unsigned long txringq;
+ unsigned long rxringq;
+ unsigned long txmsgs;
+ unsigned long rxmsgs;
+ unsigned long txflushes;
+ unsigned long rxflushes;
+ unsigned long overruns;
+ unsigned long framing;
+ unsigned long parity;
+ unsigned long ringover;
+ unsigned long lost;
+ unsigned long rxstart;
+ unsigned long rxstop;
+ unsigned long txstart;
+ unsigned long txstop;
+ unsigned long dcdcnt;
+ unsigned long dtrcnt;
+ unsigned long ctscnt;
+ unsigned long rtscnt;
+ unsigned long dsrcnt;
+ unsigned long ricnt;
+ unsigned long txbreaks;
+ unsigned long rxbreaks;
+ unsigned long signals;
+ unsigned long state;
+ unsigned long hwid;
+} asystats_t;
+
+/*****************************************************************************/
+
+/*
+ * All command and control communication with a device on the slave is
+ * via a control block in shared memory. Each device has its own control
+ * block, defined by the following structure. The control block allows
+ * the host to open, close and control the device on the slave.
+ */
+typedef struct cdkctrl {
+ unsigned char open;
+ unsigned char close;
+ unsigned long openarg;
+ unsigned long closearg;
+ unsigned long cmd;
+ unsigned long status;
+ unsigned long args[32];
+} cdkctrl_t;
+
+/*
+ * Each device on the slave passes data to and from the host via a ring
+ * queue in shared memory. Define a ring queue structure to hold the
+ * vital information about each ring queue. Two ring queues will be
+ * allocated for each port, one for receive data and one for transmit
+ * data.
+ */
+typedef struct cdkasyrq {
+ unsigned long offset;
+ unsigned short size;
+ unsigned short head;
+ unsigned short tail;
+} cdkasyrq_t;
+
+/*
+ * Each asynchronous port is defined in shared memory by the following
+ * structure. It contains a control block to command a device, and also
+ * the necessary data channel information as well.
+ */
+typedef struct cdkasy {
+ cdkctrl_t ctrl;
+ unsigned short notify;
+ asynotify_t changed;
+ unsigned short receive;
+ cdkasyrq_t rxq;
+ unsigned short transmit;
+ cdkasyrq_t txq;
+} cdkasy_t;
+
+#pragma pack()
+
+/*****************************************************************************/
+
+/*
+ * Define the set of ioctls used by the driver to do special things
+ * to the board. These include interrupting it, and initializing
+ * the driver after board startup and shutdown.
+ */
+#include <linux/ioctl.h>
+
+#define STL_BINTR _IO('s',20)
+#define STL_BSTART _IO('s',21)
+#define STL_BSTOP _IO('s',22)
+#define STL_BRESET _IO('s',23)
+
+/*
+ * Define a set of ioctl extensions, used to get at special stuff.
+ */
+#define STL_GETPFLAG _IO('s',80)
+#define STL_SETPFLAG _IO('s',81)
+
+/*****************************************************************************/
+#endif
diff --git a/pfinet/linux-src/include/linux/cdrom.h b/pfinet/linux-src/include/linux/cdrom.h
new file mode 100644
index 00000000..4bbdbd44
--- /dev/null
+++ b/pfinet/linux-src/include/linux/cdrom.h
@@ -0,0 +1,433 @@
+/*
+ * -- <linux/cdrom.h>
+ * General header file for linux CD-ROM drivers
+ * Copyright (C) 1992 David Giller, rafetmad@oxy.edu
+ * 1994, 1995 Eberhard Moenkeberg, emoenke@gwdg.de
+ * 1996 David van Leeuwen, david@tm.tno.nl
+ * 1997, 1998 Erik Andersen, andersee@debian.org
+ * 1998, 1999 Jens Axboe, axboe@image.dk
+ */
+
+#ifndef _LINUX_CDROM_H
+#define _LINUX_CDROM_H
+
+/*******************************************************
+ * As of Linux 2.1.x, all Linux CD-ROM application programs will use this
+ * (and only this) include file. It is my hope to provide Linux with
+ * a uniform interface between software accessing CD-ROMs and the various
+ * device drivers that actually talk to the drives. There may still be
+ * 23 different kinds of strange CD-ROM drives, but at least there will
+ * now be one, and only one, Linux CD-ROM interface.
+ *
+ * Additionally, as of Linux 2.1.x, all Linux application programs
+ * should use the O_NONBLOCK option when opening a CD-ROM device
+ * for subsequent ioctl commands. This allows for neat system errors
+ * like "No medium found" or "Wrong medium type" upon attempting to
+ * mount or play an empty slot, mount an audio disc, or play a data disc.
+ * Generally, changing an application program to support O_NONBLOCK
+ * is as easy as the following:
+ * - drive = open("/dev/cdrom", O_RDONLY);
+ * + drive = open("/dev/cdrom", O_RDONLY | O_NONBLOCK);
+ * It is worth the small change.
+ *
+ * Patches for many common CD programs (provided by David A. van Leeuwen)
+ * can be found at: ftp://ftp.gwdg.de/pub/linux/cdrom/drivers/cm206/
+ *
+ *******************************************************/
+
+/* When a driver supports a certain function, but the cdrom drive we are
+ * using doesn't, we will return the error EDRIVE_CANT_DO_THIS. We will
+ * borrow the "Operation not supported" error from the network folks to
+ * accomplish this. Maybe someday we will get a more targeted error code,
+ * but this will do for now... */
+#define EDRIVE_CANT_DO_THIS EOPNOTSUPP
+
+/*******************************************************
+ * The CD-ROM IOCTL commands -- these should be supported by
+ * all the various cdrom drivers. For the CD-ROM ioctls, we
+ * will commandeer byte 0x53, or 'S'.
+ *******************************************************/
+#define CDROMPAUSE 0x5301 /* Pause Audio Operation */
+#define CDROMRESUME 0x5302 /* Resume paused Audio Operation */
+#define CDROMPLAYMSF 0x5303 /* Play Audio MSF (struct cdrom_msf) */
+#define CDROMPLAYTRKIND 0x5304 /* Play Audio Track/index
+ (struct cdrom_ti) */
+#define CDROMREADTOCHDR 0x5305 /* Read TOC header
+ (struct cdrom_tochdr) */
+#define CDROMREADTOCENTRY 0x5306 /* Read TOC entry
+ (struct cdrom_tocentry) */
+#define CDROMSTOP 0x5307 /* Stop the cdrom drive */
+#define CDROMSTART 0x5308 /* Start the cdrom drive */
+#define CDROMEJECT 0x5309 /* Ejects the cdrom media */
+#define CDROMVOLCTRL 0x530a /* Control output volume
+ (struct cdrom_volctrl) */
+#define CDROMSUBCHNL 0x530b /* Read subchannel data
+ (struct cdrom_subchnl) */
+#define CDROMREADMODE2 0x530c /* Read CDROM mode 2 data (2336 Bytes)
+ (struct cdrom_read) */
+#define CDROMREADMODE1 0x530d /* Read CDROM mode 1 data (2048 Bytes)
+ (struct cdrom_read) */
+#define CDROMREADAUDIO 0x530e /* (struct cdrom_read_audio) */
+#define CDROMEJECT_SW 0x530f /* enable(1)/disable(0) auto-ejecting */
+#define CDROMMULTISESSION 0x5310 /* Obtain the start-of-last-session
+ address of multi session disks
+ (struct cdrom_multisession) */
+#define CDROM_GET_MCN 0x5311 /* Obtain the "Universal Product Code"
+ if available (struct cdrom_mcn) */
+#define CDROM_GET_UPC CDROM_GET_MCN /* This one is depricated,
+ but here anyway for compatibility */
+#define CDROMRESET 0x5312 /* hard-reset the drive */
+#define CDROMVOLREAD 0x5313 /* Get the drive's volume setting
+ (struct cdrom_volctrl) */
+#define CDROMREADRAW 0x5314 /* read data in raw mode (2352 Bytes)
+ (struct cdrom_read) */
+/*
+ * These ioctls are used only used in aztcd.c and optcd.c
+ */
+#define CDROMREADCOOKED 0x5315 /* read data in cooked mode */
+#define CDROMSEEK 0x5316 /* seek msf address */
+
+/*
+ * This ioctl is only used by the scsi-cd driver.
+ It is for playing audio in logical block addressing mode.
+ */
+#define CDROMPLAYBLK 0x5317 /* (struct cdrom_blk) */
+
+/*
+ * These ioctls are used only used in optcd.c
+ */
+#define CDROMREADALL 0x5318 /* read all 2646 bytes */
+#define CDROMCLOSETRAY 0x5319 /* pendant of CDROMEJECT */
+
+/*
+ * These ioctls are implemented through the uniform CD-ROM driver
+ * They _will_ be adopted by all CD-ROM drivers, when all the CD-ROM
+ * drivers are eventually ported to the uniform CD-ROM driver interface.
+ */
+#define CDROM_SET_OPTIONS 0x5320 /* Set behavior options */
+#define CDROM_CLEAR_OPTIONS 0x5321 /* Clear behavior options */
+#define CDROM_SELECT_SPEED 0x5322 /* Set the CD-ROM speed */
+#define CDROM_SELECT_DISC 0x5323 /* Select disc (for juke-boxes) */
+#define CDROM_MEDIA_CHANGED 0x5325 /* Check is media changed */
+#define CDROM_DRIVE_STATUS 0x5326 /* Get tray position, etc. */
+#define CDROM_DISC_STATUS 0x5327 /* Get disc type, etc. */
+#define CDROM_CHANGER_NSLOTS 0x5328 /* Get number of slots */
+#define CDROM_LOCKDOOR 0x5329 /* lock or unlock door */
+#define CDROM_DEBUG 0x5330 /* Turn debug messages on/off */
+#define CDROM_GET_CAPABILITY 0x5331 /* get capabilities */
+
+/* This ioctl is only used by sbpcd at the moment */
+#define CDROMAUDIOBUFSIZ 0x5382 /* set the audio buffer size */
+
+/*******************************************************
+ * CDROM IOCTL structures
+ *******************************************************/
+
+/* Address in MSF format */
+struct cdrom_msf0
+{
+ u_char minute;
+ u_char second;
+ u_char frame;
+};
+
+/* Address in either MSF or logical format */
+union cdrom_addr
+{
+ struct cdrom_msf0 msf;
+ int lba;
+};
+
+/* This struct is used by the CDROMPLAYMSF ioctl */
+struct cdrom_msf
+{
+ u_char cdmsf_min0; /* start minute */
+ u_char cdmsf_sec0; /* start second */
+ u_char cdmsf_frame0; /* start frame */
+ u_char cdmsf_min1; /* end minute */
+ u_char cdmsf_sec1; /* end second */
+ u_char cdmsf_frame1; /* end frame */
+};
+
+/* This struct is used by the CDROMPLAYTRKIND ioctl */
+struct cdrom_ti
+{
+ u_char cdti_trk0; /* start track */
+ u_char cdti_ind0; /* start index */
+ u_char cdti_trk1; /* end track */
+ u_char cdti_ind1; /* end index */
+};
+
+/* This struct is used by the CDROMREADTOCHDR ioctl */
+struct cdrom_tochdr
+{
+ u_char cdth_trk0; /* start track */
+ u_char cdth_trk1; /* end track */
+};
+
+/* This struct is used by the CDROMVOLCTRL and CDROMVOLREAD ioctls */
+struct cdrom_volctrl
+{
+ u_char channel0;
+ u_char channel1;
+ u_char channel2;
+ u_char channel3;
+};
+
+/* This struct is used by the CDROMSUBCHNL ioctl */
+struct cdrom_subchnl
+{
+ u_char cdsc_format;
+ u_char cdsc_audiostatus;
+ u_char cdsc_adr: 4;
+ u_char cdsc_ctrl: 4;
+ u_char cdsc_trk;
+ u_char cdsc_ind;
+ union cdrom_addr cdsc_absaddr;
+ union cdrom_addr cdsc_reladdr;
+};
+
+
+/* This struct is used by the CDROMREADTOCENTRY ioctl */
+struct cdrom_tocentry
+{
+ u_char cdte_track;
+ u_char cdte_adr :4;
+ u_char cdte_ctrl :4;
+ u_char cdte_format;
+ union cdrom_addr cdte_addr;
+ u_char cdte_datamode;
+};
+
+/* This struct is used by the CDROMREADMODE1, and CDROMREADMODE2 ioctls */
+struct cdrom_read
+{
+ int cdread_lba;
+ caddr_t cdread_bufaddr;
+ int cdread_buflen;
+};
+
+/* This struct is used by the CDROMREADAUDIO ioctl */
+struct cdrom_read_audio
+{
+ union cdrom_addr addr; /* frame address */
+ u_char addr_format; /* CDROM_LBA or CDROM_MSF */
+ int nframes; /* number of 2352-byte-frames to read at once */
+ u_char *buf; /* frame buffer (size: nframes*2352 bytes) */
+};
+
+/* This struct is used with the CDROMMULTISESSION ioctl */
+struct cdrom_multisession
+{
+ union cdrom_addr addr; /* frame address: start-of-last-session
+ (not the new "frame 16"!). Only valid
+ if the "xa_flag" is true. */
+ u_char xa_flag; /* 1: "is XA disk" */
+ u_char addr_format; /* CDROM_LBA or CDROM_MSF */
+};
+
+/* This struct is used with the CDROM_GET_MCN ioctl.
+ * Very few audio discs actually have Universal Product Code information,
+ * which should just be the Medium Catalog Number on the box. Also note
+ * that the way the codeis written on CD is _not_ uniform across all discs!
+ */
+struct cdrom_mcn
+{
+ u_char medium_catalog_number[14]; /* 13 ASCII digits, null-terminated */
+};
+
+/* This is used by the CDROMPLAYBLK ioctl */
+struct cdrom_blk
+{
+ unsigned from;
+ unsigned short len;
+};
+
+
+/*
+ * A CD-ROM physical sector size is 2048, 2052, 2056, 2324, 2332, 2336,
+ * 2340, or 2352 bytes long.
+
+* Sector types of the standard CD-ROM data formats:
+ *
+ * format sector type user data size (bytes)
+ * -----------------------------------------------------------------------------
+ * 1 (Red Book) CD-DA 2352 (CD_FRAMESIZE_RAW)
+ * 2 (Yellow Book) Mode1 Form1 2048 (CD_FRAMESIZE)
+ * 3 (Yellow Book) Mode1 Form2 2336 (CD_FRAMESIZE_RAW0)
+ * 4 (Green Book) Mode2 Form1 2048 (CD_FRAMESIZE)
+ * 5 (Green Book) Mode2 Form2 2328 (2324+4 spare bytes)
+ *
+ *
+ * The layout of the standard CD-ROM data formats:
+ * -----------------------------------------------------------------------------
+ * - audio (red): | audio_sample_bytes |
+ * | 2352 |
+ *
+ * - data (yellow, mode1): | sync - head - data - EDC - zero - ECC |
+ * | 12 - 4 - 2048 - 4 - 8 - 276 |
+ *
+ * - data (yellow, mode2): | sync - head - data |
+ * | 12 - 4 - 2336 |
+ *
+ * - XA data (green, mode2 form1): | sync - head - sub - data - EDC - ECC |
+ * | 12 - 4 - 8 - 2048 - 4 - 276 |
+ *
+ * - XA data (green, mode2 form2): | sync - head - sub - data - Spare |
+ * | 12 - 4 - 8 - 2324 - 4 |
+ *
+ */
+
+/* Some generally useful CD-ROM information -- mostly based on the above */
+#define CD_MINS 74 /* max. minutes per CD, not really a limit */
+#define CD_SECS 60 /* seconds per minute */
+#define CD_FRAMES 75 /* frames per second */
+#define CD_SYNC_SIZE 12 /* 12 sync bytes per raw data frame */
+#define CD_MSF_OFFSET 150 /* MSF numbering offset of first frame */
+#define CD_CHUNK_SIZE 24 /* lowest-level "data bytes piece" */
+#define CD_NUM_OF_CHUNKS 98 /* chunks per frame */
+#define CD_FRAMESIZE_SUB 96 /* subchannel data "frame" size */
+#define CD_HEAD_SIZE 4 /* header (address) bytes per raw data frame */
+#define CD_SUBHEAD_SIZE 8 /* subheader bytes per raw XA data frame */
+#define CD_EDC_SIZE 4 /* bytes EDC per most raw data frame types */
+#define CD_ZERO_SIZE 8 /* bytes zero per yellow book mode 1 frame */
+#define CD_ECC_SIZE 276 /* bytes ECC per most raw data frame types */
+#define CD_FRAMESIZE 2048 /* bytes per frame, "cooked" mode */
+#define CD_FRAMESIZE_RAW 2352 /* bytes per frame, "raw" mode */
+#define CD_FRAMESIZE_RAWER 2646 /* The maximum possible returned bytes */
+/* most drives don't deliver everything: */
+#define CD_FRAMESIZE_RAW1 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE) /*2340*/
+#define CD_FRAMESIZE_RAW0 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE-CD_HEAD_SIZE) /*2336*/
+
+#define CD_XA_HEAD (CD_HEAD_SIZE+CD_SUBHEAD_SIZE) /* "before data" part of raw XA frame */
+#define CD_XA_TAIL (CD_EDC_SIZE+CD_ECC_SIZE) /* "after data" part of raw XA frame */
+#define CD_XA_SYNC_HEAD (CD_SYNC_SIZE+CD_XA_HEAD) /* sync bytes + header of XA frame */
+
+/* CD-ROM address types (cdrom_tocentry.cdte_format) */
+#define CDROM_LBA 0x01 /* "logical block": first frame is #0 */
+#define CDROM_MSF 0x02 /* "minute-second-frame": binary, not bcd here! */
+
+/* bit to tell whether track is data or audio (cdrom_tocentry.cdte_ctrl) */
+#define CDROM_DATA_TRACK 0x04
+
+/* The leadout track is always 0xAA, regardless of # of tracks on disc */
+#define CDROM_LEADOUT 0xAA
+
+/* audio states (from SCSI-2, but seen with other drives, too) */
+#define CDROM_AUDIO_INVALID 0x00 /* audio status not supported */
+#define CDROM_AUDIO_PLAY 0x11 /* audio play operation in progress */
+#define CDROM_AUDIO_PAUSED 0x12 /* audio play operation paused */
+#define CDROM_AUDIO_COMPLETED 0x13 /* audio play successfully completed */
+#define CDROM_AUDIO_ERROR 0x14 /* audio play stopped due to error */
+#define CDROM_AUDIO_NO_STATUS 0x15 /* no current audio status to return */
+
+/* CD-ROM-specific SCSI command opcodes */
+#define SCMD_READ_TOC 0x43 /* read table of contents */
+#define SCMD_PLAYAUDIO_MSF 0x47 /* play data at time offset */
+#define SCMD_PLAYAUDIO_TI 0x48 /* play data at track/index */
+#define SCMD_PAUSE_RESUME 0x4B /* pause/resume audio */
+#define SCMD_READ_SUBCHANNEL 0x42 /* read SC info on playing disc */
+#define SCMD_PLAYAUDIO10 0x45 /* play data at logical block */
+
+/* capability flags used with the uniform CD-ROM driver */
+#define CDC_CLOSE_TRAY 0x1 /* caddy systems _can't_ close */
+#define CDC_OPEN_TRAY 0x2 /* but _can_ eject. */
+#define CDC_LOCK 0x4 /* disable manual eject */
+#define CDC_SELECT_SPEED 0x8 /* programmable speed */
+#define CDC_SELECT_DISC 0x10 /* select disc from juke-box */
+#define CDC_MULTI_SESSION 0x20 /* read sessions>1 */
+#define CDC_MCN 0x40 /* Medium Catalog Number */
+#define CDC_MEDIA_CHANGED 0x80 /* media changed */
+#define CDC_PLAY_AUDIO 0x100 /* audio functions */
+#define CDC_RESET 0x200 /* hard reset device */
+#define CDC_IOCTLS 0x400 /* driver has non-standard ioctls */
+#define CDC_DRIVE_STATUS 0x800 /* driver implements drive status */
+
+/* drive status possibilities returned by CDROM_DRIVE_STATUS ioctl */
+#define CDS_NO_INFO 0 /* if not implemented */
+#define CDS_NO_DISC 1
+#define CDS_TRAY_OPEN 2
+#define CDS_DRIVE_NOT_READY 3
+#define CDS_DISC_OK 4
+
+/* return values for the CDROM_DISC_STATUS ioctl */
+/* can also return CDS_NO_[INFO|DISC], from above */
+#define CDS_AUDIO 100
+#define CDS_DATA_1 101
+#define CDS_DATA_2 102
+#define CDS_XA_2_1 103
+#define CDS_XA_2_2 104
+#define CDS_MIXED 105
+
+/* User-configurable behavior options for the uniform CD-ROM driver */
+#define CDO_AUTO_CLOSE 0x1 /* close tray on first open() */
+#define CDO_AUTO_EJECT 0x2 /* open tray on last release() */
+#define CDO_USE_FFLAGS 0x4 /* use O_NONBLOCK information on open */
+#define CDO_LOCK 0x8 /* lock tray on open files */
+#define CDO_CHECK_TYPE 0x10 /* check type on open for data */
+
+/* Special codes used when specifying changer slots. */
+#define CDSL_NONE ((int) (~0U>>1)-1)
+#define CDSL_CURRENT ((int) (~0U>>1))
+
+#ifdef __KERNEL__
+/* Uniform cdrom data structures for cdrom.c */
+struct cdrom_device_info {
+ struct cdrom_device_ops *ops; /* link to device_ops */
+ struct cdrom_device_info *next; /* next device_info for this major */
+ void *handle; /* driver-dependent data */
+/* specifications */
+ kdev_t dev; /* device number */
+ int mask; /* mask of capability: disables them */
+ int speed; /* maximum speed for reading data */
+ int capacity; /* number of discs in jukebox */
+/* device-related storage */
+ int options : 30; /* options flags */
+ unsigned mc_flags : 2; /* media change buffer flags */
+ int use_count; /* number of times device opened */
+ char name[20]; /* name of the device type */
+
+};
+
+struct cdrom_device_ops {
+/* routines */
+ int (*open) (struct cdrom_device_info *, int);
+ void (*release) (struct cdrom_device_info *);
+ int (*drive_status) (struct cdrom_device_info *, int);
+ int (*media_changed) (struct cdrom_device_info *, int);
+ int (*tray_move) (struct cdrom_device_info *, int);
+ int (*lock_door) (struct cdrom_device_info *, int);
+ int (*select_speed) (struct cdrom_device_info *, int);
+ int (*select_disc) (struct cdrom_device_info *, int);
+ int (*get_last_session) (struct cdrom_device_info *,
+ struct cdrom_multisession *);
+ int (*get_mcn) (struct cdrom_device_info *,
+ struct cdrom_mcn *);
+ /* hard reset device */
+ int (*reset) (struct cdrom_device_info *);
+ /* play stuff */
+ int (*audio_ioctl) (struct cdrom_device_info *,unsigned int, void *);
+ /* dev-specific */
+ int (*dev_ioctl) (struct cdrom_device_info *,
+ unsigned int, unsigned long);
+/* driver specifications */
+ const int capability; /* capability flags */
+ int n_minors; /* number of active minor devices */
+};
+
+/* the general file operations structure: */
+extern struct file_operations cdrom_fops;
+
+extern int register_cdrom(struct cdrom_device_info *cdi);
+extern int unregister_cdrom(struct cdrom_device_info *cdi);
+typedef struct {
+ int data;
+ int audio;
+ int cdi;
+ int xa;
+ long error;
+} tracktype;
+extern void cdrom_count_tracks(struct cdrom_device_info *cdi,tracktype* tracks);
+#endif /* End of kernel only stuff */
+
+#endif /* _LINUX_CDROM_H */
diff --git a/pfinet/linux-src/include/linux/coda.h b/pfinet/linux-src/include/linux/coda.h
new file mode 100644
index 00000000..c194988a
--- /dev/null
+++ b/pfinet/linux-src/include/linux/coda.h
@@ -0,0 +1,799 @@
+/*
+ You may distribute this file under either of the two licenses that
+ follow at your discretion.
+*/
+
+/* BLURB lgpl
+
+ Coda File System
+ Release 5
+
+ Copyright (c) 1987-1999 Carnegie Mellon University
+ Additional copyrights listed below
+
+This code is distributed "AS IS" without warranty of any kind under
+the terms of the GNU Library General Public Licence Version 2, as
+shown in the file LICENSE, or under the license shown below. The
+technical and financial contributors to Coda are listed in the file
+CREDITS.
+
+ Additional copyrights
+*/
+
+/*
+
+ Coda: an Experimental Distributed File System
+ Release 4.0
+
+ Copyright (c) 1987-1999 Carnegie Mellon University
+ All Rights Reserved
+
+Permission to use, copy, modify and distribute this software and its
+documentation is hereby granted, provided that both the copyright
+notice and this permission notice appear in all copies of the
+software, derivative works or modified versions, and any portions
+thereof, and that both notices appear in supporting documentation, and
+that credit is given to Carnegie Mellon University in all documents
+and publicity pertaining to direct or indirect use of this code or its
+derivatives.
+
+CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS KNOWN TO HAVE BUGS,
+SOME OF WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON ALLOWS
+FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION. CARNEGIE MELLON
+DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
+RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE OR OF
+ANY DERIVATIVE WORK.
+
+Carnegie Mellon encourages users of this software to return any
+improvements or extensions that they make, and to grant Carnegie
+Mellon the rights to redistribute these changes without encumbrance.
+*/
+
+/*
+ *
+ * Based on cfs.h from Mach, but revamped for increased simplicity.
+ * Linux modifications by
+ * Peter Braam, Aug 1996
+ */
+
+#ifndef _CODA_HEADER_
+#define _CODA_HEADER_
+
+
+
+/* Catch new _KERNEL defn for NetBSD */
+#ifdef __NetBSD__
+#include <sys/types.h>
+#endif
+
+#ifndef CODA_MAXSYMLINKS
+#define CODA_MAXSYMLINKS 10
+#endif
+
+#if defined(DJGPP) || defined(__CYGWIN32__)
+#ifdef KERNEL
+typedef unsigned long u_long;
+typedef unsigned int u_int;
+typedef unsigned short u_short;
+typedef u_long ino_t;
+typedef u_long dev_t;
+typedef void * caddr_t;
+#ifdef DOS
+typedef unsigned __int64 u_quad_t;
+#else
+typedef unsigned long long u_quad_t;
+#endif
+
+#define inline
+
+struct timespec {
+ long ts_sec;
+ long ts_nsec;
+};
+#else /* DJGPP but not KERNEL */
+#include <sys/types.h>
+#include <sys/time.h>
+typedef unsigned long long u_quad_t;
+#endif /* !KERNEL */
+#endif /* !DJGPP */
+
+
+#if defined(__linux__)
+#define cdev_t u_quad_t
+#ifndef __KERNEL__
+#if !defined(_UQUAD_T_) && (!defined(__GLIBC__) || __GLIBC__ < 2)
+#define _UQUAD_T_ 1
+typedef unsigned long long u_quad_t;
+#endif
+#else /*__KERNEL__ */
+typedef unsigned long long u_quad_t;
+#endif /* __KERNEL__ */
+#else
+#define cdev_t dev_t
+#endif
+
+#ifdef __CYGWIN32__
+struct timespec {
+ time_t tv_sec; /* seconds */
+ long tv_nsec; /* nanoseconds */
+};
+#endif
+
+#ifndef __BIT_TYPES_DEFINED__
+#define __BIT_TYPES_DEFINED__
+typedef signed char int8_t;
+typedef unsigned char u_int8_t;
+typedef short int16_t;
+typedef unsigned short u_int16_t;
+typedef int int32_t;
+typedef unsigned int u_int32_t;
+#endif
+
+
+/*
+ * Cfs constants
+ */
+#define CODA_MAXNAMLEN 255
+#define CODA_MAXPATHLEN 1024
+#define CODA_MAXSYMLINK 10
+
+/* these are Coda's version of O_RDONLY etc combinations
+ * to deal with VFS open modes
+ */
+#define C_O_READ 0x001
+#define C_O_WRITE 0x002
+#define C_O_TRUNC 0x010
+#define C_O_EXCL 0x100
+#define C_O_CREAT 0x200
+
+/* these are to find mode bits in Venus */
+#define C_M_READ 00400
+#define C_M_WRITE 00200
+
+/* for access Venus will use */
+#define C_A_C_OK 8 /* Test for writing upon create. */
+#define C_A_R_OK 4 /* Test for read permission. */
+#define C_A_W_OK 2 /* Test for write permission. */
+#define C_A_X_OK 1 /* Test for execute permission. */
+#define C_A_F_OK 0 /* Test for existence. */
+
+
+
+#ifndef _VENUS_DIRENT_T_
+#define _VENUS_DIRENT_T_ 1
+struct venus_dirent {
+ unsigned long d_fileno; /* file number of entry */
+ unsigned short d_reclen; /* length of this record */
+ unsigned char d_type; /* file type, see below */
+ unsigned char d_namlen; /* length of string in d_name */
+ char d_name[CODA_MAXNAMLEN + 1];/* name must be no longer than this */
+};
+#undef DIRSIZ
+#define DIRSIZ(dp) ((sizeof (struct venus_dirent) - (CODA_MAXNAMLEN+1)) + \
+ (((dp)->d_namlen+1 + 3) &~ 3))
+
+/*
+ * File types
+ */
+#define CDT_UNKNOWN 0
+#define CDT_FIFO 1
+#define CDT_CHR 2
+#define CDT_DIR 4
+#define CDT_BLK 6
+#define CDT_REG 8
+#define CDT_LNK 10
+#define CDT_SOCK 12
+#define CDT_WHT 14
+
+/*
+ * Convert between stat structure types and directory types.
+ */
+#define IFTOCDT(mode) (((mode) & 0170000) >> 12)
+#define CDTTOIF(dirtype) ((dirtype) << 12)
+
+#endif
+
+#ifndef _FID_T_
+#define _FID_T_ 1
+typedef u_long VolumeId;
+typedef u_long VnodeId;
+typedef u_long Unique_t;
+typedef u_long FileVersion;
+#endif
+
+#ifndef _VICEFID_T_
+#define _VICEFID_T_ 1
+typedef struct ViceFid {
+ VolumeId Volume;
+ VnodeId Vnode;
+ Unique_t Unique;
+} ViceFid;
+#endif /* VICEFID */
+
+
+#ifdef __linux__
+static __inline__ ino_t coda_f2i(struct ViceFid *fid)
+{
+ if ( ! fid )
+ return 0;
+ if (fid->Vnode == 0xfffffffe || fid->Vnode == 0xffffffff)
+ return ((fid->Volume << 20) | (fid->Unique & 0xfffff));
+ else
+ return (fid->Unique + (fid->Vnode<<10) + (fid->Volume<<20));
+}
+
+#else
+#define coda_f2i(fid)\
+ ((fid) ? ((fid)->Unique + ((fid)->Vnode<<10) + ((fid)->Volume<<20)) : 0)
+#endif
+
+
+#ifndef _VUID_T_
+#define _VUID_T_
+typedef u_int32_t vuid_t;
+typedef u_int32_t vgid_t;
+#endif /*_VUID_T_ */
+
+#ifndef _CODACRED_T_
+#define _CODACRED_T_
+struct coda_cred {
+ vuid_t cr_uid, cr_euid, cr_suid, cr_fsuid; /* Real, efftve, set, fs uid*/
+ vgid_t cr_groupid, cr_egid, cr_sgid, cr_fsgid; /* same for groups */
+};
+#endif
+
+#ifndef _VENUS_VATTR_T_
+#define _VENUS_VATTR_T_
+/*
+ * Vnode types. VNON means no type.
+ */
+enum coda_vtype { C_VNON, C_VREG, C_VDIR, C_VBLK, C_VCHR, C_VLNK, C_VSOCK, C_VFIFO, C_VBAD };
+
+struct coda_vattr {
+ long va_type; /* vnode type (for create) */
+ u_short va_mode; /* files access mode and type */
+ short va_nlink; /* number of references to file */
+ vuid_t va_uid; /* owner user id */
+ vgid_t va_gid; /* owner group id */
+ long va_fileid; /* file id */
+ u_quad_t va_size; /* file size in bytes */
+ long va_blocksize; /* blocksize preferred for i/o */
+ struct timespec va_atime; /* time of last access */
+ struct timespec va_mtime; /* time of last modification */
+ struct timespec va_ctime; /* time file changed */
+ u_long va_gen; /* generation number of file */
+ u_long va_flags; /* flags defined for file */
+ cdev_t va_rdev; /* device special file represents */
+ u_quad_t va_bytes; /* bytes of disk space held by file */
+ u_quad_t va_filerev; /* file modification number */
+};
+
+#endif
+
+/* structure used by CODA_STATFS for getting cache information from venus */
+struct coda_statfs {
+ int32_t f_blocks;
+ int32_t f_bfree;
+ int32_t f_bavail;
+ int32_t f_files;
+ int32_t f_ffree;
+};
+
+/*
+ * Kernel <--> Venus communications.
+ */
+
+#define CODA_ROOT 2
+#define CODA_SYNC 3
+#define CODA_OPEN 4
+#define CODA_CLOSE 5
+#define CODA_IOCTL 6
+#define CODA_GETATTR 7
+#define CODA_SETATTR 8
+#define CODA_ACCESS 9
+#define CODA_LOOKUP 10
+#define CODA_CREATE 11
+#define CODA_REMOVE 12
+#define CODA_LINK 13
+#define CODA_RENAME 14
+#define CODA_MKDIR 15
+#define CODA_RMDIR 16
+#define CODA_READDIR 17
+#define CODA_SYMLINK 18
+#define CODA_READLINK 19
+#define CODA_FSYNC 20
+#define CODA_INACTIVE 21
+#define CODA_VGET 22
+#define CODA_SIGNAL 23
+#define CODA_REPLACE 24
+#define CODA_FLUSH 25
+#define CODA_PURGEUSER 26
+#define CODA_ZAPFILE 27
+#define CODA_ZAPDIR 28
+#define CODA_PURGEFID 30
+#define CODA_OPEN_BY_PATH 31
+#define CODA_RESOLVE 32
+#define CODA_REINTEGRATE 33
+#define CODA_STATFS 34
+#define CODA_NCALLS 35
+
+#define DOWNCALL(opcode) (opcode >= CODA_REPLACE && opcode <= CODA_PURGEFID)
+
+#define VC_MAXDATASIZE 8192
+#define VC_MAXMSGSIZE sizeof(union inputArgs)+sizeof(union outputArgs) +\
+ VC_MAXDATASIZE
+
+#define CIOC_KERNEL_VERSION _IOWR('c', 10, sizeof (int))
+#if 0
+ /* don't care about kernel version number */
+#define CODA_KERNEL_VERSION 0
+ /* The old venus 4.6 compatible interface */
+#define CODA_KERNEL_VERSION 1
+#endif
+ /* venus_lookup gets an extra parameter to aid windows.*/
+#define CODA_KERNEL_VERSION 2
+
+/*
+ * Venus <-> Coda RPC arguments
+ */
+struct coda_in_hdr {
+ unsigned long opcode;
+ unsigned long unique; /* Keep multiple outstanding msgs distinct */
+ u_short pid; /* Common to all */
+ u_short pgid; /* Common to all */
+ u_short sid; /* Common to all */
+ struct coda_cred cred; /* Common to all */
+};
+
+/* Really important that opcode and unique are 1st two fields! */
+struct coda_out_hdr {
+ unsigned long opcode;
+ unsigned long unique;
+ unsigned long result;
+};
+
+/* coda_root: NO_IN */
+struct coda_root_out {
+ struct coda_out_hdr oh;
+ ViceFid VFid;
+};
+
+struct coda_root_in {
+ struct coda_in_hdr in;
+};
+
+/* coda_sync: */
+/* Nothing needed for coda_sync */
+
+/* coda_open: */
+struct coda_open_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+ int flags;
+};
+
+struct coda_open_out {
+ struct coda_out_hdr oh;
+ cdev_t dev;
+ ino_t inode;
+};
+
+
+/* coda_close: */
+struct coda_close_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+ int flags;
+};
+
+struct coda_close_out {
+ struct coda_out_hdr out;
+};
+
+/* coda_ioctl: */
+struct coda_ioctl_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+ int cmd;
+ int len;
+ int rwflag;
+ char *data; /* Place holder for data. */
+};
+
+struct coda_ioctl_out {
+ struct coda_out_hdr oh;
+ int len;
+ caddr_t data; /* Place holder for data. */
+};
+
+
+/* coda_getattr: */
+struct coda_getattr_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+};
+
+struct coda_getattr_out {
+ struct coda_out_hdr oh;
+ struct coda_vattr attr;
+};
+
+
+/* coda_setattr: NO_OUT */
+struct coda_setattr_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+ struct coda_vattr attr;
+};
+
+struct coda_setattr_out {
+ struct coda_out_hdr out;
+};
+
+/* coda_access: NO_OUT */
+struct coda_access_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+ int flags;
+};
+
+struct coda_access_out {
+ struct coda_out_hdr out;
+};
+
+
+/* lookup flags */
+#define CLU_CASE_SENSITIVE 0x01
+#define CLU_CASE_INSENSITIVE 0x02
+
+/* coda_lookup: */
+struct coda_lookup_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+ int name; /* Place holder for data. */
+ int flags;
+};
+
+struct coda_lookup_out {
+ struct coda_out_hdr oh;
+ ViceFid VFid;
+ int vtype;
+};
+
+
+/* coda_create: */
+struct coda_create_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+ struct coda_vattr attr;
+ int excl;
+ int mode;
+ int name; /* Place holder for data. */
+};
+
+struct coda_create_out {
+ struct coda_out_hdr oh;
+ ViceFid VFid;
+ struct coda_vattr attr;
+};
+
+
+/* coda_remove: NO_OUT */
+struct coda_remove_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+ int name; /* Place holder for data. */
+};
+
+struct coda_remove_out {
+ struct coda_out_hdr out;
+};
+
+/* coda_link: NO_OUT */
+struct coda_link_in {
+ struct coda_in_hdr ih;
+ ViceFid sourceFid; /* cnode to link *to* */
+ ViceFid destFid; /* Directory in which to place link */
+ int tname; /* Place holder for data. */
+};
+
+struct coda_link_out {
+ struct coda_out_hdr out;
+};
+
+
+/* coda_rename: NO_OUT */
+struct coda_rename_in {
+ struct coda_in_hdr ih;
+ ViceFid sourceFid;
+ int srcname;
+ ViceFid destFid;
+ int destname;
+};
+
+struct coda_rename_out {
+ struct coda_out_hdr out;
+};
+
+/* coda_mkdir: */
+struct coda_mkdir_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+ struct coda_vattr attr;
+ int name; /* Place holder for data. */
+};
+
+struct coda_mkdir_out {
+ struct coda_out_hdr oh;
+ ViceFid VFid;
+ struct coda_vattr attr;
+};
+
+
+/* coda_rmdir: NO_OUT */
+struct coda_rmdir_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+ int name; /* Place holder for data. */
+};
+
+struct coda_rmdir_out {
+ struct coda_out_hdr out;
+};
+
+/* coda_readdir: */
+struct coda_readdir_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+ int count;
+ int offset;
+};
+
+struct coda_readdir_out {
+ struct coda_out_hdr oh;
+ int size;
+ caddr_t data; /* Place holder for data. */
+};
+
+/* coda_symlink: NO_OUT */
+struct coda_symlink_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid; /* Directory to put symlink in */
+ int srcname;
+ struct coda_vattr attr;
+ int tname;
+};
+
+struct coda_symlink_out {
+ struct coda_out_hdr out;
+};
+
+/* coda_readlink: */
+struct coda_readlink_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+};
+
+struct coda_readlink_out {
+ struct coda_out_hdr oh;
+ int count;
+ caddr_t data; /* Place holder for data. */
+};
+
+
+/* coda_fsync: NO_OUT */
+struct coda_fsync_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+};
+
+struct coda_fsync_out {
+ struct coda_out_hdr out;
+};
+
+/* coda_inactive: NO_OUT */
+struct coda_inactive_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+};
+
+/* coda_vget: */
+struct coda_vget_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+};
+
+struct coda_vget_out {
+ struct coda_out_hdr oh;
+ ViceFid VFid;
+ int vtype;
+};
+
+
+/* CODA_SIGNAL is out-of-band, doesn't need data. */
+/* CODA_INVALIDATE is a venus->kernel call */
+/* CODA_FLUSH is a venus->kernel call */
+
+/* coda_purgeuser: */
+/* CODA_PURGEUSER is a venus->kernel call */
+struct coda_purgeuser_out {
+ struct coda_out_hdr oh;
+ struct coda_cred cred;
+};
+
+/* coda_zapfile: */
+/* CODA_ZAPFILE is a venus->kernel call */
+struct coda_zapfile_out {
+ struct coda_out_hdr oh;
+ ViceFid CodaFid;
+};
+
+/* coda_zapdir: */
+/* CODA_ZAPDIR is a venus->kernel call */
+struct coda_zapdir_out {
+ struct coda_out_hdr oh;
+ ViceFid CodaFid;
+};
+
+/* coda_zapnode: */
+/* CODA_ZAPVNODE is a venus->kernel call */
+struct coda_zapvnode_out {
+ struct coda_out_hdr oh;
+ struct coda_cred cred;
+ ViceFid VFid;
+};
+
+/* coda_purgefid: */
+/* CODA_PURGEFID is a venus->kernel call */
+struct coda_purgefid_out {
+ struct coda_out_hdr oh;
+ ViceFid CodaFid;
+};
+
+/* coda_rdwr: */
+struct coda_rdwr_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+ int rwflag;
+ int count;
+ int offset;
+ int ioflag;
+ caddr_t data; /* Place holder for data. */
+};
+
+struct coda_rdwr_out {
+ struct coda_out_hdr oh;
+ int rwflag;
+ int count;
+ caddr_t data; /* Place holder for data. */
+};
+
+
+/* coda_replace: */
+/* CODA_REPLACE is a venus->kernel call */
+struct coda_replace_out { /* coda_replace is a venus->kernel call */
+ struct coda_out_hdr oh;
+ ViceFid NewFid;
+ ViceFid OldFid;
+};
+
+/* coda_open_by_path: */
+struct coda_open_by_path_in {
+ struct coda_in_hdr ih;
+ ViceFid VFid;
+ int flags;
+};
+
+struct coda_open_by_path_out {
+ struct coda_out_hdr oh;
+ int path;
+};
+
+/* coda_statfs: NO_IN */
+struct coda_statfs_in {
+ struct coda_in_hdr in;
+};
+
+struct coda_statfs_out {
+ struct coda_out_hdr oh;
+ struct coda_statfs stat;
+};
+
+/*
+ * Occasionally, we don't cache the fid returned by CODA_LOOKUP.
+ * For instance, if the fid is inconsistent.
+ * This case is handled by setting the top bit of the type result parameter.
+ */
+#define CODA_NOCACHE 0x80000000
+
+union inputArgs {
+ struct coda_in_hdr ih; /* NB: every struct below begins with an ih */
+ struct coda_open_in coda_open;
+ struct coda_close_in coda_close;
+ struct coda_ioctl_in coda_ioctl;
+ struct coda_getattr_in coda_getattr;
+ struct coda_setattr_in coda_setattr;
+ struct coda_access_in coda_access;
+ struct coda_lookup_in coda_lookup;
+ struct coda_create_in coda_create;
+ struct coda_remove_in coda_remove;
+ struct coda_link_in coda_link;
+ struct coda_rename_in coda_rename;
+ struct coda_mkdir_in coda_mkdir;
+ struct coda_rmdir_in coda_rmdir;
+ struct coda_readdir_in coda_readdir;
+ struct coda_symlink_in coda_symlink;
+ struct coda_readlink_in coda_readlink;
+ struct coda_fsync_in coda_fsync;
+ struct coda_inactive_in coda_inactive;
+ struct coda_vget_in coda_vget;
+ struct coda_rdwr_in coda_rdwr;
+ struct coda_open_by_path_in coda_open_by_path;
+ struct coda_statfs_in coda_statfs;
+};
+
+union outputArgs {
+ struct coda_out_hdr oh; /* NB: every struct below begins with an oh */
+ struct coda_root_out coda_root;
+ struct coda_open_out coda_open;
+ struct coda_ioctl_out coda_ioctl;
+ struct coda_getattr_out coda_getattr;
+ struct coda_lookup_out coda_lookup;
+ struct coda_create_out coda_create;
+ struct coda_mkdir_out coda_mkdir;
+ struct coda_readdir_out coda_readdir;
+ struct coda_readlink_out coda_readlink;
+ struct coda_vget_out coda_vget;
+ struct coda_purgeuser_out coda_purgeuser;
+ struct coda_zapfile_out coda_zapfile;
+ struct coda_zapdir_out coda_zapdir;
+ struct coda_zapvnode_out coda_zapvnode;
+ struct coda_purgefid_out coda_purgefid;
+ struct coda_rdwr_out coda_rdwr;
+ struct coda_replace_out coda_replace;
+ struct coda_open_by_path_out coda_open_by_path;
+ struct coda_statfs_out coda_statfs;
+};
+
+union coda_downcalls {
+ /* CODA_INVALIDATE is a venus->kernel call */
+ /* CODA_FLUSH is a venus->kernel call */
+ struct coda_purgeuser_out purgeuser;
+ struct coda_zapfile_out zapfile;
+ struct coda_zapdir_out zapdir;
+ struct coda_zapvnode_out zapvnode;
+ struct coda_purgefid_out purgefid;
+ struct coda_replace_out replace;
+};
+
+
+/*
+ * Used for identifying usage of "Control" and pioctls
+ */
+
+#define PIOCPARM_MASK 0x0000ffff
+struct ViceIoctl {
+ caddr_t in, out; /* Data to be transferred in, or out */
+ short in_size; /* Size of input buffer <= 2K */
+ short out_size; /* Maximum size of output buffer, <= 2K */
+};
+
+struct PioctlData {
+ const char *path;
+ int follow;
+ struct ViceIoctl vi;
+};
+
+#define CODA_CONTROL ".CONTROL"
+#define CODA_CONTROLLEN 8
+#define CTL_VOL -1
+#define CTL_VNO -1
+#define CTL_UNI -1
+#define CTL_INO -1
+#define CTL_FILE "/coda/.CONTROL"
+
+
+#define IS_CTL_FID(fidp) ((fidp)->Volume == CTL_VOL &&\
+ (fidp)->Vnode == CTL_VNO &&\
+ (fidp)->Unique == CTL_UNI)
+#endif
diff --git a/pfinet/linux-src/include/linux/coda_cache.h b/pfinet/linux-src/include/linux/coda_cache.h
new file mode 100644
index 00000000..e549b02e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/coda_cache.h
@@ -0,0 +1,77 @@
+/* Coda filesystem -- Linux Minicache
+ *
+ * Copyright (C) 1989 - 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon University encourages users of this software to
+ * contribute improvements to the Coda project. Contact Peter Braam
+ * <coda@cs.cmu.edu>
+ */
+
+#ifndef _CFSNC_HEADER_
+#define _CFSNC_HEADER_
+
+/*
+ * Structure for an element in the Coda Credential Cache.
+ */
+
+struct coda_cache {
+ struct list_head cc_cclist; /* list of all cache entries */
+ struct list_head cc_cnlist; /* list of cache entries/cnode */
+ int cc_mask;
+ struct coda_cred cc_cred;
+};
+
+/* credential cache */
+void coda_cache_enter(struct inode *inode, int mask);
+void coda_cache_clear_inode(struct inode *);
+void coda_cache_clear_all(struct super_block *sb);
+void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred);
+int coda_cache_check(struct inode *inode, int mask);
+
+/* for downcalls and attributes and lookups */
+void coda_flag_inode(struct inode *inode, int flag);
+void coda_flag_inode_children(struct inode *inode, int flag);
+
+
+/*
+ * Structure to contain statistics on the cache usage
+ */
+
+struct cfsnc_statistics {
+ unsigned hits;
+ unsigned misses;
+ unsigned enters;
+ unsigned dbl_enters;
+ unsigned long_name_enters;
+ unsigned long_name_lookups;
+ unsigned long_remove;
+ unsigned lru_rm;
+ unsigned zapPfids;
+ unsigned zapFids;
+ unsigned zapFile;
+ unsigned zapUsers;
+ unsigned Flushes;
+ unsigned Sum_bucket_len;
+ unsigned Sum2_bucket_len;
+ unsigned Max_bucket_len;
+ unsigned Num_zero_len;
+ unsigned Search_len;
+};
+
+
+#define CFSNC_FIND ((u_long) 1)
+#define CFSNC_REMOVE ((u_long) 2)
+#define CFSNC_INIT ((u_long) 3)
+#define CFSNC_ENTER ((u_long) 4)
+#define CFSNC_LOOKUP ((u_long) 5)
+#define CFSNC_ZAPPFID ((u_long) 6)
+#define CFSNC_ZAPFID ((u_long) 7)
+#define CFSNC_ZAPVNODE ((u_long) 8)
+#define CFSNC_ZAPFILE ((u_long) 9)
+#define CFSNC_PURGEUSER ((u_long) 10)
+#define CFSNC_FLUSH ((u_long) 11)
+#define CFSNC_PRINTCFSNC ((u_long) 12)
+#define CFSNC_PRINTSTATS ((u_long) 13)
+#define CFSNC_REPLACE ((u_long) 14)
+
+#endif _CFSNC_HEADER_
diff --git a/pfinet/linux-src/include/linux/coda_fs_i.h b/pfinet/linux-src/include/linux/coda_fs_i.h
new file mode 100644
index 00000000..8a55e8e3
--- /dev/null
+++ b/pfinet/linux-src/include/linux/coda_fs_i.h
@@ -0,0 +1,56 @@
+/*
+ * coda_fs_i.h
+ *
+ * Copyright (C) 1998 Carnegie Mellon University
+ *
+ */
+
+#ifndef _LINUX_CODA_FS_I
+#define _LINUX_CODA_FS_I
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/coda.h>
+
+
+
+#define CODA_CNODE_MAGIC 0x47114711
+/*
+ * coda fs inode data
+ */
+struct coda_inode_info {
+ /*
+ * This is a place holder so named pipes work (more or less
+ * correctly). This must be first in the struct because the
+ * data is really accessed via inode->u.pipe_i.
+ */
+ struct pipe_inode_info pipeinfo;
+
+ struct ViceFid c_fid; /* Coda identifier */
+ u_short c_flags; /* flags (see below) */
+ u_short c_ocount; /* count of openers */
+ u_short c_owrite; /* count of open for write */
+ u_short c_mmcount; /* count of mmappers */
+ struct inode *c_ovp; /* open inode pointer */
+ struct list_head c_cnhead; /* head of cache entries */
+ struct list_head c_volrootlist; /* list of volroot cnoddes */
+ struct inode *c_vnode; /* inode associated with cnode */
+ int c_magic; /* to verify the data structure */
+};
+
+/* flags */
+#define C_VATTR 0x1 /* Validity of vattr in inode */
+#define C_PURGE 0x8
+#define C_ZAPDIR 0x10
+#define C_DYING 0x4 /* from venus (which died) */
+#define C_INITED 0x20
+#define C_FLUSH 0x2 /* used after a flush */
+
+int coda_cnode_make(struct inode **, struct ViceFid *, struct super_block *);
+int coda_cnode_makectl(struct inode **inode, struct super_block *sb);
+struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb);
+void coda_replace_fid(struct inode *, ViceFid *, ViceFid *);
+
+#endif
+#endif
diff --git a/pfinet/linux-src/include/linux/coda_linux.h b/pfinet/linux-src/include/linux/coda_linux.h
new file mode 100644
index 00000000..cd0e46ce
--- /dev/null
+++ b/pfinet/linux-src/include/linux/coda_linux.h
@@ -0,0 +1,143 @@
+/*
+ * Coda File System, Linux Kernel module
+ *
+ * Original version, adapted from cfs_mach.c, (C) Carnegie Mellon University
+ * Linux modifications (C) 1996, Peter J. Braam
+ * Rewritten for Linux 2.1 (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon University encourages users of this software to
+ * contribute improvements to the Coda project.
+ */
+
+#ifndef _LINUX_CODA_FS
+#define _LINUX_CODA_FS
+
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/malloc.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+
+/* operations */
+extern struct inode_operations coda_dir_inode_operations;
+extern struct inode_operations coda_file_inode_operations;
+extern struct inode_operations coda_ioctl_inode_operations;
+extern struct inode_operations coda_symlink_inode_operations;
+
+extern struct file_operations coda_dir_operations;
+extern struct file_operations coda_file_operations;
+extern struct file_operations coda_ioctl_operations;
+
+/* operations shared over more than one file */
+int coda_open(struct inode *i, struct file *f);
+int coda_release(struct inode *i, struct file *f);
+int coda_permission(struct inode *inode, int mask);
+int coda_revalidate_inode(struct dentry *);
+
+/* global variables */
+extern int coda_debug;
+extern int coda_print_entry;
+extern int coda_access_cache;
+
+/* this file: heloers */
+static __inline__ struct ViceFid *coda_i2f(struct inode *);
+char *coda_f2s(ViceFid *f);
+char *coda_f2s2(ViceFid *f);
+int coda_isroot(struct inode *i);
+int coda_fid_is_volroot(struct ViceFid *);
+int coda_fid_is_weird(struct ViceFid *fid);
+int coda_iscontrol(const char *name, size_t length);
+
+void coda_load_creds(struct coda_cred *cred);
+int coda_mycred(struct coda_cred *);
+void coda_vattr_to_iattr(struct inode *, struct coda_vattr *);
+void coda_iattr_to_vattr(struct iattr *, struct coda_vattr *);
+unsigned short coda_flags_to_cflags(unsigned short);
+void print_vattr( struct coda_vattr *attr );
+int coda_cred_ok(struct coda_cred *cred);
+int coda_cred_eq(struct coda_cred *cred1, struct coda_cred *cred2);
+
+/* defined in file.c */
+void coda_prepare_openfile(struct inode *coda_inode, struct file *coda_file,
+ struct inode *open_inode, struct file *open_file,
+ struct dentry *open_dentry);
+void coda_restore_codafile(struct inode *coda_inode, struct file *coda_file,
+ struct inode *open_inode, struct file *open_file);
+int coda_inode_grab(dev_t dev, ino_t ino, struct inode **ind);
+
+#define NB_SFS_SIZ 0x895440
+
+/* cache.c */
+void coda_purge_children(struct inode *, int);
+void coda_purge_dentries(struct inode *);
+
+/* sysctl.h */
+void coda_sysctl_init(void);
+void coda_sysctl_clean(void);
+
+
+/* debugging masks */
+#define D_SUPER 1 /* print results returned by Venus */
+#define D_INODE 2 /* print entry and exit into procedure */
+#define D_FILE 4
+#define D_CACHE 8 /* cache debugging */
+#define D_MALLOC 16 /* print malloc, de-alloc information */
+#define D_CNODE 32
+#define D_UPCALL 64 /* up and downcall debugging */
+#define D_PSDEV 128
+#define D_PIOCTL 256
+#define D_SPECIAL 512
+#define D_TIMING 1024
+#define D_DOWNCALL 2048
+
+#define CDEBUG(mask, format, a...) \
+ do { \
+ if (coda_debug & mask) { \
+ printk("(%s,l. %d): ", __FUNCTION__, __LINE__); \
+ printk(format, ## a); } \
+} while (0) ;
+
+#define ENTRY \
+ if(coda_print_entry) printk("Process %d entered %s\n",current->pid,__FUNCTION__)
+
+#define EXIT \
+ if(coda_print_entry) printk("Process %d leaving %s\n",current->pid,__FUNCTION__)
+
+#define CHECK_CNODE(c) do { } while (0);
+
+#define CODA_ALLOC(ptr, cast, size) \
+do { \
+ if (size < 3000) { \
+ ptr = (cast)kmalloc((unsigned long) size, GFP_KERNEL); \
+ CDEBUG(D_MALLOC, "kmalloced: %lx at %p.\n", (long)size, ptr);\
+ } else { \
+ ptr = (cast)vmalloc((unsigned long) size); \
+ CDEBUG(D_MALLOC, "vmalloced: %lx at %p .\n", (long)size, ptr);}\
+ if (ptr == 0) { \
+ printk("kernel malloc returns 0 at %s:%d\n", __FILE__, __LINE__); \
+ } \
+ memset( ptr, 0, size ); \
+} while (0)
+
+
+#define CODA_FREE(ptr,size) do {if (size < 3000) { kfree_s((ptr), (size)); CDEBUG(D_MALLOC, "kfreed: %lx at %p.\n", (long) size, ptr); } else { vfree((ptr)); CDEBUG(D_MALLOC, "vfreed: %lx at %p.\n", (long) size, ptr);} } while (0)
+
+/* inode to cnode */
+
+static __inline__ struct ViceFid *coda_i2f(struct inode *inode)
+{
+ return &(inode->u.coda_i.c_fid);
+}
+
+#define ITOC(inode) (&((inode)->u.coda_i))
+
+
+
+
+
+
+#endif
diff --git a/pfinet/linux-src/include/linux/coda_opstats.h b/pfinet/linux-src/include/linux/coda_opstats.h
new file mode 100644
index 00000000..167490d8
--- /dev/null
+++ b/pfinet/linux-src/include/linux/coda_opstats.h
@@ -0,0 +1,94 @@
+
+/*
+ * Operation statistics for Coda.
+ * Copyright (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon University encourages users of this software
+ * to contribute improvements to the Coda project. Contact Peter Braam
+ * <coda@coda.cs.cmu.edu>.
+ */
+
+
+
+#define CFS_MOUNT_STATS 0
+#define CFS_UMOUNT_STATS 1
+#define CFS_ROOT_STATS 2
+#define CFS_STATFS_STATS 3
+#define CFS_SYNC_STATS 4
+#define CFS_VGET_STATS 5
+#define CFS_VFSOPS_SIZE 6
+
+/* vnodeops:
+ * open: all to venus
+ * close: all to venus
+ * rdrw: bogus. Maybe redirected to UFS.
+ * May call open/close for internal opens/closes
+ * (Does exec not call open?)
+ * ioctl: causes a lookupname
+ * passes through
+ * select: can't get there from here.
+ * getattr: can be satsified by cache
+ * setattr: all go through
+ * access: can be satisfied by cache
+ * readlink: can be satisfied by cache
+ * fsync: passes through
+ * inactive: passes through
+ * lookup: can be satisfied by cache
+ * create: passes through
+ * remove: passes through
+ * link: passes through
+ * rename: passes through
+ * mkdir: passes through
+ * rmdir: passes through
+ * symlink: passes through
+ * readdir: may be redirected to UFS
+ * may cause an "internal" open/close
+ */
+
+#define CFS_OPEN_STATS 0
+#define CFS_CLOSE_STATS 1
+#define CFS_RDWR_STATS 2
+#define CFS_IOCTL_STATS 3
+#define CFS_SELECT_STATS 4
+#define CFS_GETATTR_STATS 5
+#define CFS_SETATTR_STATS 6
+#define CFS_ACCESS_STATS 7
+#define CFS_READLINK_STATS 8
+#define CFS_FSYNC_STATS 9
+#define CFS_INACTIVE_STATS 10
+#define CFS_LOOKUP_STATS 11
+#define CFS_CREATE_STATS 12
+#define CFS_REMOVE_STATS 13
+#define CFS_LINK_STATS 14
+#define CFS_RENAME_STATS 15
+#define CFS_MKDIR_STATS 16
+#define CFS_RMDIR_STATS 17
+#define CFS_SYMLINK_STATS 18
+#define CFS_READDIR_STATS 19
+#define CFS_VNODEOPS_SIZE 20
+
+
+/*
+ * I propose the following structres:
+ */
+
+
+struct cfs_op_stats {
+ int opcode; /* vfs opcode */
+ long entries; /* number of times call attempted */
+ long sat_intrn; /* number of times call satisfied by cache */
+ long unsat_intrn; /* number of times call failed in cache, but
+ was not bounced to venus proper. */
+ long gen_intrn; /* number of times call generated internally */
+ /* (do we need that?) */
+};
+
+
+/*
+ * With each call to the minicache, we'll bump the counters whenever
+ * a call is satisfied internally (through the cache or through a
+ * redirect), and whenever an operation is caused internally.
+ * Then, we can add the total operations caught by the minicache
+ * to the world-wide totals, and leave a caveat for the specific
+ * graphs later.
+ */
diff --git a/pfinet/linux-src/include/linux/coda_proc.h b/pfinet/linux-src/include/linux/coda_proc.h
new file mode 100644
index 00000000..7c4ca8a0
--- /dev/null
+++ b/pfinet/linux-src/include/linux/coda_proc.h
@@ -0,0 +1,145 @@
+/*
+ * coda_statis.h
+ *
+ * CODA operation statistics
+ *
+ * (c) March, 1998
+ * by Michihiro Kuramochi, Zhenyu Xia and Zhanyong Wan
+ * zhanyong.wan@yale.edu
+ *
+ */
+
+#ifndef _CODA_PROC_H
+#define _CODA_PROC_H
+
+void coda_sysctl_init(void);
+void coda_sysctl_clean(void);
+void coda_upcall_stats(int opcode, unsigned long jiffies);
+
+#include <linux/sysctl.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda.h>
+
+/* these four files are presented to show the result of the statistics:
+ *
+ * /proc/fs/coda/vfs_stats
+ * upcall_stats
+ * permission_stats
+ * cache_inv_stats
+ *
+ * these four files are presented to reset the statistics to 0:
+ *
+ * /proc/sys/coda/vfs_stats
+ * upcall_stats
+ * permission_stats
+ * cache_inv_stats
+ */
+
+/* VFS operation statistics */
+struct coda_vfs_stats
+{
+ /* file operations */
+ int file_read;
+ int file_write;
+ int file_mmap;
+ int open;
+ int release;
+ int fsync;
+
+ /* dir operations */
+ int readdir;
+
+ /* inode operations */
+ int create;
+ int lookup;
+ int link;
+ int unlink;
+ int symlink;
+ int mkdir;
+ int rmdir;
+ int rename;
+ int permission;
+ int readpage;
+
+ /* symlink operatoins*/
+ int follow_link;
+ int readlink;
+};
+
+struct coda_upcall_stats_entry
+{
+ int count;
+ unsigned long time_sum;
+ unsigned long time_squared_sum;
+};
+
+
+
+/* cache hits for permissions statistics */
+struct coda_permission_stats
+{
+ int count;
+ int hit_count;
+};
+
+/* cache invalidation statistics */
+struct coda_cache_inv_stats
+{
+ int flush;
+ int purge_user;
+ int zap_dir;
+ int zap_file;
+ int zap_vnode;
+ int purge_fid;
+ int replace;
+};
+
+/* these global variables hold the actual statistics data */
+extern struct coda_vfs_stats coda_vfs_stat;
+extern struct coda_permission_stats coda_permission_stat;
+extern struct coda_cache_inv_stats coda_cache_inv_stat;
+extern int coda_upcall_timestamping;
+
+/* reset statistics to 0 */
+void reset_coda_vfs_stats( void );
+void reset_coda_upcall_stats( void );
+void reset_coda_permission_stats( void );
+void reset_coda_cache_inv_stats( void );
+
+/* some utitlities to make it easier for you to do statistics for time */
+void do_time_stats( struct coda_upcall_stats_entry * pentry,
+ unsigned long jiffy );
+/*
+double get_time_average( const struct coda_upcall_stats_entry * pentry );
+double get_time_std_deviation( const struct coda_upcall_stats_entry * pentry );
+*/
+unsigned long get_time_average( const struct coda_upcall_stats_entry * pentry );
+unsigned long get_time_std_deviation( const struct coda_upcall_stats_entry * pentry );
+
+/* like coda_dointvec, these functions are to be registered in the ctl_table
+ * data structure for /proc/sys/... files
+ */
+int do_reset_coda_vfs_stats( ctl_table * table, int write, struct file * filp,
+ void * buffer, size_t * lenp );
+int do_reset_coda_upcall_stats( ctl_table * table, int write,
+ struct file * filp, void * buffer,
+ size_t * lenp );
+int do_reset_coda_permission_stats( ctl_table * table, int write,
+ struct file * filp, void * buffer,
+ size_t * lenp );
+int do_reset_coda_cache_inv_stats( ctl_table * table, int write,
+ struct file * filp, void * buffer,
+ size_t * lenp );
+
+/* these functions are called to form the content of /proc/fs/coda/... files */
+int coda_vfs_stats_get_info( char * buffer, char ** start, off_t offset,
+ int length, int dummy );
+int coda_upcall_stats_get_info( char * buffer, char ** start, off_t offset,
+ int length, int dummy );
+int coda_permission_stats_get_info( char * buffer, char ** start, off_t offset,
+ int length, int dummy );
+int coda_cache_inv_stats_get_info( char * buffer, char ** start, off_t offset,
+ int length, int dummy );
+
+
+#endif /* _CODA_PROC_H */
diff --git a/pfinet/linux-src/include/linux/coda_psdev.h b/pfinet/linux-src/include/linux/coda_psdev.h
new file mode 100644
index 00000000..8c564f9a
--- /dev/null
+++ b/pfinet/linux-src/include/linux/coda_psdev.h
@@ -0,0 +1,131 @@
+#ifndef __CODA_PSDEV_H
+#define __CODA_PSDEV_H
+
+#define CODA_PSDEV_MAJOR 67
+#define MAX_CODADEVS 5 /* how many do we allow */
+
+extern struct venus_comm coda_upc_comm;
+extern struct coda_sb_info coda_super_info;
+#define CODA_SUPER_MAGIC 0x73757245
+
+struct coda_sb_info
+{
+ struct inode * sbi_psdev; /* /dev/cfs? Venus/kernel device */
+ struct inode * sbi_ctlcp; /* control magic file */
+ int sbi_refct;
+ struct venus_comm * sbi_vcomm;
+ struct inode * sbi_root;
+ struct super_block *sbi_sb;
+ struct list_head sbi_cchead;
+ struct list_head sbi_volroothead;
+};
+
+/* communication pending/processing queues */
+struct venus_comm {
+ u_long vc_seq;
+ struct wait_queue *vc_waitq; /* Venus wait queue */
+ struct list_head vc_pending;
+ struct list_head vc_processing;
+ int vc_inuse;
+ pid_t vc_pid; /* Venus pid */
+};
+
+
+static inline struct coda_sb_info *coda_sbp(struct super_block *sb)
+{
+ return ((struct coda_sb_info *)((sb)->u.generic_sbp));
+}
+
+
+
+extern void coda_psdev_detach(int unit);
+extern int init_coda_psdev(void);
+
+
+/* upcalls */
+int venus_rootfid(struct super_block *sb, ViceFid *fidp);
+int venus_getattr(struct super_block *sb, struct ViceFid *fid,
+ struct coda_vattr *attr);
+int venus_setattr(struct super_block *, struct ViceFid *,
+ struct coda_vattr *);
+int venus_lookup(struct super_block *sb, struct ViceFid *fid,
+ const char *name, int length, int *type,
+ struct ViceFid *resfid);
+int venus_release(struct super_block *sb, struct ViceFid *fid, int flags,
+ struct coda_cred *);
+int venus_open(struct super_block *sb, struct ViceFid *fid,
+ int flags, ino_t *ino, dev_t *dev);
+int venus_mkdir(struct super_block *sb, struct ViceFid *dirfid,
+ const char *name, int length,
+ struct ViceFid *newfid, struct coda_vattr *attrs);
+int venus_create(struct super_block *sb, struct ViceFid *dirfid,
+ const char *name, int length, int excl, int mode, int rdev,
+ struct ViceFid *newfid, struct coda_vattr *attrs) ;
+int venus_rmdir(struct super_block *sb, struct ViceFid *dirfid,
+ const char *name, int length);
+int venus_remove(struct super_block *sb, struct ViceFid *dirfid,
+ const char *name, int length);
+int venus_readlink(struct super_block *sb, struct ViceFid *fid,
+ char *buffer, int *length);
+int venus_rename(struct super_block *, struct ViceFid *new_fid,
+ struct ViceFid *old_fid, size_t old_length,
+ size_t new_length, const char *old_name,
+ const char *new_name);
+int venus_link(struct super_block *sb, struct ViceFid *fid,
+ struct ViceFid *dirfid, const char *name, int len );
+int venus_symlink(struct super_block *sb, struct ViceFid *fid,
+ const char *name, int len, const char *symname, int symlen);
+int venus_access(struct super_block *sb, struct ViceFid *fid, int mask);
+int venus_pioctl(struct super_block *sb, struct ViceFid *fid,
+ unsigned int cmd, struct PioctlData *data);
+int coda_downcall(int opcode, union outputArgs *out, struct super_block *sb);
+int venus_fsync(struct super_block *sb, struct ViceFid *fid);
+int venus_statfs(struct super_block *sb, struct statfs *sfs);
+
+
+/* messages between coda filesystem in kernel and Venus */
+extern int coda_hard;
+extern unsigned long coda_timeout;
+struct upc_req {
+ struct list_head uc_chain;
+ caddr_t uc_data;
+ u_short uc_flags;
+ u_short uc_inSize; /* Size is at most 5000 bytes */
+ u_short uc_outSize;
+ u_short uc_opcode; /* copied from data to save lookup */
+ int uc_unique;
+ struct wait_queue *uc_sleep; /* process' wait queue */
+ unsigned long uc_posttime;
+};
+
+#define REQ_ASYNC 0x1
+#define REQ_READ 0x2
+#define REQ_WRITE 0x4
+
+
+/*
+ * Statistics
+ */
+struct coda_upcallstats {
+ int ncalls; /* client requests */
+ int nbadcalls; /* upcall failures */
+ int reqs[CODA_NCALLS]; /* count of each request */
+} ;
+
+extern struct coda_upcallstats coda_callstats;
+
+static inline void clstats(int opcode)
+{
+ coda_callstats.ncalls++;
+ if ( (0 <= opcode) && (opcode <= CODA_NCALLS) )
+ coda_callstats.reqs[opcode]++;
+ else
+ printk("clstats called with bad opcode %d\n", opcode);
+}
+
+static inline void badclstats(void)
+{
+ coda_callstats.nbadcalls++;
+}
+
+#endif
diff --git a/pfinet/linux-src/include/linux/coff.h b/pfinet/linux-src/include/linux/coff.h
new file mode 100644
index 00000000..a1ba4666
--- /dev/null
+++ b/pfinet/linux-src/include/linux/coff.h
@@ -0,0 +1,351 @@
+/* This file is derived from the GAS 2.1.4 assembler control file.
+ The GAS product is under the GNU Public License, version 2 or later.
+ As such, this file is also under that license.
+
+ If the file format changes in the COFF object, this file should be
+ subsequently updated to reflect the changes.
+
+ The actual loader module only uses a few of these structures. The full
+ set is documented here because I received the full set. If you wish
+ more information about COFF, then O'Reilly has a very excellent book.
+*/
+
+#define E_SYMNMLEN 8 /* Number of characters in a symbol name */
+#define E_FILNMLEN 14 /* Number of characters in a file name */
+#define E_DIMNUM 4 /* Number of array dimensions in auxiliary entry */
+
+/*
+ * These defines are byte order independent. There is no alignment of fields
+ * permitted in the structures. Therefore they are declared as characters
+ * and the values loaded from the character positions. It also makes it
+ * nice to have it "endian" independent.
+ */
+
+/* Load a short int from the following tables with little-endian formats */
+#define COFF_SHORT_L(ps) ((short)(((unsigned short)((unsigned char)ps[1])<<8)|\
+ ((unsigned short)((unsigned char)ps[0]))))
+
+/* Load a long int from the following tables with little-endian formats */
+#define COFF_LONG_L(ps) (((long)(((unsigned long)((unsigned char)ps[3])<<24) |\
+ ((unsigned long)((unsigned char)ps[2])<<16) |\
+ ((unsigned long)((unsigned char)ps[1])<<8) |\
+ ((unsigned long)((unsigned char)ps[0])))))
+
+/* Load a short int from the following tables with big-endian formats */
+#define COFF_SHORT_H(ps) ((short)(((unsigned short)((unsigned char)ps[0])<<8)|\
+ ((unsigned short)((unsigned char)ps[1]))))
+
+/* Load a long int from the following tables with big-endian formats */
+#define COFF_LONG_H(ps) (((long)(((unsigned long)((unsigned char)ps[0])<<24) |\
+ ((unsigned long)((unsigned char)ps[1])<<16) |\
+ ((unsigned long)((unsigned char)ps[2])<<8) |\
+ ((unsigned long)((unsigned char)ps[3])))))
+
+/* These may be overridden later by brain dead implementations which generate
+ a big-endian header with little-endian data. In that case, generate a
+ replacement macro which tests a flag and uses either of the two above
+ as appropriate. */
+
+#define COFF_LONG(v) COFF_LONG_L(v)
+#define COFF_SHORT(v) COFF_SHORT_L(v)
+
+/*** coff information for Intel 386/486. */
+
+/********************** FILE HEADER **********************/
+
+struct COFF_filehdr {
+ char f_magic[2]; /* magic number */
+ char f_nscns[2]; /* number of sections */
+ char f_timdat[4]; /* time & date stamp */
+ char f_symptr[4]; /* file pointer to symtab */
+ char f_nsyms[4]; /* number of symtab entries */
+ char f_opthdr[2]; /* sizeof(optional hdr) */
+ char f_flags[2]; /* flags */
+};
+
+/*
+ * Bits for f_flags:
+ *
+ * F_RELFLG relocation info stripped from file
+ * F_EXEC file is executable (i.e. no unresolved external
+ * references)
+ * F_LNNO line numbers stripped from file
+ * F_LSYMS local symbols stripped from file
+ * F_MINMAL this is a minimal object file (".m") output of fextract
+ * F_UPDATE this is a fully bound update file, output of ogen
+ * F_SWABD this file has had its bytes swabbed (in names)
+ * F_AR16WR this file has the byte ordering of an AR16WR
+ * (e.g. 11/70) machine
+ * F_AR32WR this file has the byte ordering of an AR32WR machine
+ * (e.g. vax and iNTEL 386)
+ * F_AR32W this file has the byte ordering of an AR32W machine
+ * (e.g. 3b,maxi)
+ * F_PATCH file contains "patch" list in optional header
+ * F_NODF (minimal file only) no decision functions for
+ * replaced functions
+ */
+
+#define COFF_F_RELFLG 0000001
+#define COFF_F_EXEC 0000002
+#define COFF_F_LNNO 0000004
+#define COFF_F_LSYMS 0000010
+#define COFF_F_MINMAL 0000020
+#define COFF_F_UPDATE 0000040
+#define COFF_F_SWABD 0000100
+#define COFF_F_AR16WR 0000200
+#define COFF_F_AR32WR 0000400
+#define COFF_F_AR32W 0001000
+#define COFF_F_PATCH 0002000
+#define COFF_F_NODF 0002000
+
+#define COFF_I386MAGIC 0x14c /* Linux's system */
+
+#if 0 /* Perhaps, someday, these formats may be used. */
+#define COFF_I386PTXMAGIC 0x154
+#define COFF_I386AIXMAGIC 0x175 /* IBM's AIX system */
+#define COFF_I386BADMAG(x) ((COFF_SHORT((x).f_magic) != COFF_I386MAGIC) \
+ && COFF_SHORT((x).f_magic) != COFF_I386PTXMAGIC \
+ && COFF_SHORT((x).f_magic) != COFF_I386AIXMAGIC)
+#else
+#define COFF_I386BADMAG(x) (COFF_SHORT((x).f_magic) != COFF_I386MAGIC)
+#endif
+
+#define COFF_FILHDR struct COFF_filehdr
+#define COFF_FILHSZ sizeof(COFF_FILHDR)
+
+/********************** AOUT "OPTIONAL HEADER" **********************/
+
+/* Linux COFF must have this "optional" header. Standard COFF has no entry
+ location for the "entry" point. They normally would start with the first
+ location of the .text section. This is not a good idea for linux. So,
+ the use of this "optional" header is not optional. It is required.
+
+ Do not be tempted to assume that the size of the optional header is
+ a constant and simply index the next byte by the size of this structure.
+ Use the 'f_opthdr' field in the main coff header for the size of the
+ structure actually written to the file!!
+*/
+
+typedef struct
+{
+ char magic[2]; /* type of file */
+ char vstamp[2]; /* version stamp */
+ char tsize[4]; /* text size in bytes, padded to FW bdry */
+ char dsize[4]; /* initialized data " " */
+ char bsize[4]; /* uninitialized data " " */
+ char entry[4]; /* entry pt. */
+ char text_start[4]; /* base of text used for this file */
+ char data_start[4]; /* base of data used for this file */
+}
+COFF_AOUTHDR;
+
+#define COFF_AOUTSZ (sizeof(COFF_AOUTHDR))
+
+#define COFF_STMAGIC 0401
+#define COFF_OMAGIC 0404
+#define COFF_JMAGIC 0407 /* dirty text and data image, can't share */
+#define COFF_DMAGIC 0410 /* dirty text segment, data aligned */
+#define COFF_ZMAGIC 0413 /* The proper magic number for executables */
+#define COFF_SHMAGIC 0443 /* shared library header */
+
+/********************** SECTION HEADER **********************/
+
+struct COFF_scnhdr {
+ char s_name[8]; /* section name */
+ char s_paddr[4]; /* physical address, aliased s_nlib */
+ char s_vaddr[4]; /* virtual address */
+ char s_size[4]; /* section size */
+ char s_scnptr[4]; /* file ptr to raw data for section */
+ char s_relptr[4]; /* file ptr to relocation */
+ char s_lnnoptr[4]; /* file ptr to line numbers */
+ char s_nreloc[2]; /* number of relocation entries */
+ char s_nlnno[2]; /* number of line number entries */
+ char s_flags[4]; /* flags */
+};
+
+#define COFF_SCNHDR struct COFF_scnhdr
+#define COFF_SCNHSZ sizeof(COFF_SCNHDR)
+
+/*
+ * names of "special" sections
+ */
+
+#define COFF_TEXT ".text"
+#define COFF_DATA ".data"
+#define COFF_BSS ".bss"
+#define COFF_COMMENT ".comment"
+#define COFF_LIB ".lib"
+
+#define COFF_SECT_TEXT 0 /* Section for instruction code */
+#define COFF_SECT_DATA 1 /* Section for initialized globals */
+#define COFF_SECT_BSS 2 /* Section for un-initialized globals */
+#define COFF_SECT_REQD 3 /* Minimum number of sections for good file */
+
+#define COFF_STYP_REG 0x00 /* regular segment */
+#define COFF_STYP_DSECT 0x01 /* dummy segment */
+#define COFF_STYP_NOLOAD 0x02 /* no-load segment */
+#define COFF_STYP_GROUP 0x04 /* group segment */
+#define COFF_STYP_PAD 0x08 /* .pad segment */
+#define COFF_STYP_COPY 0x10 /* copy section */
+#define COFF_STYP_TEXT 0x20 /* .text segment */
+#define COFF_STYP_DATA 0x40 /* .data segment */
+#define COFF_STYP_BSS 0x80 /* .bss segment */
+#define COFF_STYP_INFO 0x200 /* .comment section */
+#define COFF_STYP_OVER 0x400 /* overlay section */
+#define COFF_STYP_LIB 0x800 /* library section */
+
+/*
+ * Shared libraries have the following section header in the data field for
+ * each library.
+ */
+
+struct COFF_slib {
+ char sl_entsz[4]; /* Size of this entry */
+ char sl_pathndx[4]; /* size of the header field */
+};
+
+#define COFF_SLIBHD struct COFF_slib
+#define COFF_SLIBSZ sizeof(COFF_SLIBHD)
+
+/********************** LINE NUMBERS **********************/
+
+/* 1 line number entry for every "breakpointable" source line in a section.
+ * Line numbers are grouped on a per function basis; first entry in a function
+ * grouping will have l_lnno = 0 and in place of physical address will be the
+ * symbol table index of the function name.
+ */
+
+struct COFF_lineno {
+ union {
+ char l_symndx[4]; /* function name symbol index, iff l_lnno == 0*/
+ char l_paddr[4]; /* (physical) address of line number */
+ } l_addr;
+ char l_lnno[2]; /* line number */
+};
+
+#define COFF_LINENO struct COFF_lineno
+#define COFF_LINESZ 6
+
+/********************** SYMBOLS **********************/
+
+#define COFF_E_SYMNMLEN 8 /* # characters in a short symbol name */
+#define COFF_E_FILNMLEN 14 /* # characters in a file name */
+#define COFF_E_DIMNUM 4 /* # array dimensions in auxiliary entry */
+
+/*
+ * All symbols and sections have the following definition
+ */
+
+struct COFF_syment
+{
+ union {
+ char e_name[E_SYMNMLEN]; /* Symbol name (first 8 characters) */
+ struct {
+ char e_zeroes[4]; /* Leading zeros */
+ char e_offset[4]; /* Offset if this is a header section */
+ } e;
+ } e;
+
+ char e_value[4]; /* Value (address) of the segment */
+ char e_scnum[2]; /* Section number */
+ char e_type[2]; /* Type of section */
+ char e_sclass[1]; /* Loader class */
+ char e_numaux[1]; /* Number of auxiliary entries which follow */
+};
+
+#define COFF_N_BTMASK (0xf) /* Mask for important class bits */
+#define COFF_N_TMASK (0x30) /* Mask for important type bits */
+#define COFF_N_BTSHFT (4) /* # bits to shift class field */
+#define COFF_N_TSHIFT (2) /* # bits to shift type field */
+
+/*
+ * Auxiliary entries because the main table is too limiting.
+ */
+
+union COFF_auxent {
+
+/*
+ * Debugger information
+ */
+
+ struct {
+ char x_tagndx[4]; /* str, un, or enum tag indx */
+ union {
+ struct {
+ char x_lnno[2]; /* declaration line number */
+ char x_size[2]; /* str/union/array size */
+ } x_lnsz;
+ char x_fsize[4]; /* size of function */
+ } x_misc;
+
+ union {
+ struct { /* if ISFCN, tag, or .bb */
+ char x_lnnoptr[4]; /* ptr to fcn line # */
+ char x_endndx[4]; /* entry ndx past block end */
+ } x_fcn;
+
+ struct { /* if ISARY, up to 4 dimen. */
+ char x_dimen[E_DIMNUM][2];
+ } x_ary;
+ } x_fcnary;
+
+ char x_tvndx[2]; /* tv index */
+ } x_sym;
+
+/*
+ * Source file names (debugger information)
+ */
+
+ union {
+ char x_fname[E_FILNMLEN];
+ struct {
+ char x_zeroes[4];
+ char x_offset[4];
+ } x_n;
+ } x_file;
+
+/*
+ * Section information
+ */
+
+ struct {
+ char x_scnlen[4]; /* section length */
+ char x_nreloc[2]; /* # relocation entries */
+ char x_nlinno[2]; /* # line numbers */
+ } x_scn;
+
+/*
+ * Transfer vector (branch table)
+ */
+
+ struct {
+ char x_tvfill[4]; /* tv fill value */
+ char x_tvlen[2]; /* length of .tv */
+ char x_tvran[2][2]; /* tv range */
+ } x_tv; /* info about .tv section (in auxent of symbol .tv)) */
+};
+
+#define COFF_SYMENT struct COFF_syment
+#define COFF_SYMESZ 18
+#define COFF_AUXENT union COFF_auxent
+#define COFF_AUXESZ 18
+
+#define COFF_ETEXT "etext"
+
+/********************** RELOCATION DIRECTIVES **********************/
+
+struct COFF_reloc {
+ char r_vaddr[4]; /* Virtual address of item */
+ char r_symndx[4]; /* Symbol index in the symtab */
+ char r_type[2]; /* Relocation type */
+};
+
+#define COFF_RELOC struct COFF_reloc
+#define COFF_RELSZ 10
+
+#define COFF_DEF_DATA_SECTION_ALIGNMENT 4
+#define COFF_DEF_BSS_SECTION_ALIGNMENT 4
+#define COFF_DEF_TEXT_SECTION_ALIGNMENT 4
+
+/* For new sections we haven't heard of before */
+#define COFF_DEF_SECTION_ALIGNMENT 4
diff --git a/pfinet/linux-src/include/linux/comstats.h b/pfinet/linux-src/include/linux/comstats.h
new file mode 100644
index 00000000..06688859
--- /dev/null
+++ b/pfinet/linux-src/include/linux/comstats.h
@@ -0,0 +1,119 @@
+/*****************************************************************************/
+
+/*
+ * comstats.h -- Serial Port Stats.
+ *
+ * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au).
+ * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*****************************************************************************/
+#ifndef _COMSTATS_H
+#define _COMSTATS_H
+/*****************************************************************************/
+
+/*
+ * Serial port stats structure. The structure itself is UART
+ * independent, but some fields may be UART/driver specific (for
+ * example state).
+ */
+
+typedef struct {
+ unsigned long brd;
+ unsigned long panel;
+ unsigned long port;
+ unsigned long hwid;
+ unsigned long type;
+ unsigned long txtotal;
+ unsigned long rxtotal;
+ unsigned long txbuffered;
+ unsigned long rxbuffered;
+ unsigned long rxoverrun;
+ unsigned long rxparity;
+ unsigned long rxframing;
+ unsigned long rxlost;
+ unsigned long txbreaks;
+ unsigned long rxbreaks;
+ unsigned long txxon;
+ unsigned long txxoff;
+ unsigned long rxxon;
+ unsigned long rxxoff;
+ unsigned long txctson;
+ unsigned long txctsoff;
+ unsigned long rxrtson;
+ unsigned long rxrtsoff;
+ unsigned long modem;
+ unsigned long state;
+ unsigned long flags;
+ unsigned long ttystate;
+ unsigned long cflags;
+ unsigned long iflags;
+ unsigned long oflags;
+ unsigned long lflags;
+ unsigned long signals;
+} comstats_t;
+
+
+/*
+ * Board stats structure. Returns useful info about the board.
+ */
+
+#define COM_MAXPANELS 8
+
+typedef struct {
+ unsigned long panel;
+ unsigned long type;
+ unsigned long hwid;
+ unsigned long nrports;
+} companel_t;
+
+typedef struct {
+ unsigned long brd;
+ unsigned long type;
+ unsigned long hwid;
+ unsigned long state;
+ unsigned long ioaddr;
+ unsigned long ioaddr2;
+ unsigned long memaddr;
+ unsigned long irq;
+ unsigned long nrpanels;
+ unsigned long nrports;
+ companel_t panels[COM_MAXPANELS];
+} combrd_t;
+
+
+/*
+ * Define the ioctl operations for stats stuff.
+ */
+#include <linux/ioctl.h>
+
+#define COM_GETPORTSTATS _IO('c',30)
+#define COM_CLRPORTSTATS _IO('c',31)
+#define COM_GETBRDSTATS _IO('c',32)
+
+
+/*
+ * Define the set of ioctls that give user level access to the
+ * private port, panel and board structures. The argument required
+ * will be driver dependent!
+ */
+#define COM_READPORT _IO('c',40)
+#define COM_READBOARD _IO('c',41)
+#define COM_READPANEL _IO('c',42)
+
+/*****************************************************************************/
+#endif
diff --git a/pfinet/linux-src/include/linux/concap.h b/pfinet/linux-src/include/linux/concap.h
new file mode 100644
index 00000000..b8e72dc6
--- /dev/null
+++ b/pfinet/linux-src/include/linux/concap.h
@@ -0,0 +1,109 @@
+/* $Id: concap.h,v 1.2 1999/08/23 15:54:21 keil Exp $
+*/
+#ifndef _LINUX_CONCAP_H
+#define _LINUX_CONCAP_H
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+/* Stuff to support encapsulation protocols genericly. The encapsulation
+ protocol is processed at the uppermost layer of the network interface.
+
+ (c) 1997 by Henner Eisen <eis@baty.hanse.de>
+ This software is subject to the GNU General Public License.
+
+ Based on a ideas developed in a 'synchronous device' thread in the
+ linux-x25 mailing list contributed by Alan Cox, Thomasz Motylewski
+ and Jonathan Naylor.
+
+ For more documetation on this refer to Documentation/isdn/README.concap
+ */
+
+struct concap_proto_ops;
+struct concap_device_ops;
+
+/* this manages all data needed by the encapsulation protocol
+ */
+struct concap_proto{
+ struct device *net_dev; /* net device using our service */
+ struct concap_device_ops *dops; /* callbacks provided by device */
+ struct concap_proto_ops *pops; /* callbacks provided by us */
+ int flags;
+ void *proto_data; /* protocol specific private data, to
+ be accessed via *pops methods only*/
+ /*
+ :
+ whatever
+ :
+ */
+};
+
+/* Operations to be supported by the net device. Called by the encapsulation
+ * protocol entity. No receive method is offered because the encapsulation
+ * protocol directly calls netif_rx().
+ */
+struct concap_device_ops{
+
+ /* to request data is submitted by device*/
+ int (*data_req)(struct concap_proto *, struct sk_buff *);
+
+ /* Control methods must be set to NULL by devices which do not
+ support connection control.*/
+ /* to request a connection is set up */
+ int (*connect_req)(struct concap_proto *);
+
+ /* to request a connection is released */
+ int (*disconn_req)(struct concap_proto *);
+};
+
+/* Operations to be supported by the encapsulation protocol. Called by
+ * device driver.
+ */
+struct concap_proto_ops{
+
+ /* create a new encapsulation protocol instance of same type */
+ struct concap_proto * (*proto_new) (void);
+
+ /* delete encapsulation protocol instance and free all its resources.
+ cprot may no loger be referenced after calling this */
+ void (*proto_del)(struct concap_proto *cprot);
+
+ /* initialize the protocol's data. To be called at interface startup
+ or when the device driver resets the interface. All services of the
+ encapsulation protocol may be used after this*/
+ int (*restart)(struct concap_proto *cprot,
+ struct device *ndev,
+ struct concap_device_ops *dops);
+
+ /* inactivate an encapsulation protocol instance. The encapsulation
+ protocol may not call any *dops methods after this. */
+ int (*close)(struct concap_proto *cprot);
+
+ /* process a frame handed down to us by upper layer */
+ int (*encap_and_xmit)(struct concap_proto *cprot, struct sk_buff *skb);
+
+ /* to be called for each data entity received from lower layer*/
+ int (*data_ind)(struct concap_proto *cprot, struct sk_buff *skb);
+
+ /* to be called when a connection was set up/down.
+ Protocols that don't process these primitives might fill in
+ dummy methods here */
+ int (*connect_ind)(struct concap_proto *cprot);
+ int (*disconn_ind)(struct concap_proto *cprot);
+ /*
+ Some network device support functions, like net_header(), rebuild_header(),
+ and others, that depend solely on the encapsulation protocol, might
+ be provided here, too. The net device would just fill them in its
+ corresponding fields when it is opened.
+ */
+};
+
+/* dummy restart/close/connect/reset/disconn methods
+ */
+extern int concap_nop(struct concap_proto *cprot);
+
+/* dummy submit method
+ */
+extern int concap_drop_skb(struct concap_proto *cprot, struct sk_buff *skb);
+#endif
+#endif
diff --git a/pfinet/linux-src/include/linux/config.h b/pfinet/linux-src/include/linux/config.h
new file mode 100644
index 00000000..9d1c14f7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/config.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_CONFIG_H
+#define _LINUX_CONFIG_H
+
+#include <linux/autoconf.h>
+
+#endif
diff --git a/pfinet/linux-src/include/linux/console.h b/pfinet/linux-src/include/linux/console.h
new file mode 100644
index 00000000..efb0003e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/console.h
@@ -0,0 +1,115 @@
+/*
+ * linux/include/linux/console.h
+ *
+ * Copyright (C) 1993 Hamish Macdonald
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ * Changed:
+ * 10-Mar-94: Arno Griffioen: Conversion for vt100 emulator port from PC LINUX
+ */
+
+#ifndef _LINUX_CONSOLE_H_
+#define _LINUX_CONSOLE_H_ 1
+
+struct vc_data;
+struct console_font_op;
+
+/*
+ * this is what the terminal answers to a ESC-Z or csi0c query.
+ */
+#define VT100ID "\033[?1;2c"
+#define VT102ID "\033[?6c"
+
+struct consw {
+ const char *(*con_startup)(void);
+ void (*con_init)(struct vc_data *, int);
+ void (*con_deinit)(struct vc_data *);
+ void (*con_clear)(struct vc_data *, int, int, int, int);
+ void (*con_putc)(struct vc_data *, int, int, int);
+ void (*con_putcs)(struct vc_data *, const unsigned short *, int, int, int);
+ void (*con_cursor)(struct vc_data *, int);
+ int (*con_scroll)(struct vc_data *, int, int, int, int);
+ void (*con_bmove)(struct vc_data *, int, int, int, int, int, int);
+ int (*con_switch)(struct vc_data *);
+ int (*con_blank)(struct vc_data *, int);
+ int (*con_font_op)(struct vc_data *, struct console_font_op *);
+ int (*con_set_palette)(struct vc_data *, unsigned char *);
+ int (*con_scrolldelta)(struct vc_data *, int);
+ int (*con_set_origin)(struct vc_data *);
+ void (*con_save_screen)(struct vc_data *);
+ u8 (*con_build_attr)(struct vc_data *, u8, u8, u8, u8, u8);
+ void (*con_invert_region)(struct vc_data *, u16 *, int);
+ u16 *(*con_screen_pos)(struct vc_data *, int);
+ unsigned long (*con_getxy)(struct vc_data *, unsigned long, int *, int *);
+};
+
+extern struct consw *conswitchp;
+
+extern struct consw dummy_con; /* dummy console buffer */
+extern struct consw fb_con; /* frame buffer based console */
+extern struct consw vga_con; /* VGA text console */
+extern struct consw newport_con; /* SGI Newport console */
+extern struct consw prom_con; /* SPARC PROM console */
+
+void take_over_console(struct consw *sw, int first, int last, int deflt);
+void give_up_console(struct consw *sw);
+
+/* scroll */
+#define SM_UP (1)
+#define SM_DOWN (2)
+
+/* cursor */
+#define CM_DRAW (1)
+#define CM_ERASE (2)
+#define CM_MOVE (3)
+
+/*
+ * Array of consoles built from command line options (console=)
+ */
+struct console_cmdline
+{
+ char name[8]; /* Name of the driver */
+ int index; /* Minor dev. to use */
+ char *options; /* Options for the driver */
+};
+#define MAX_CMDLINECONSOLES 8
+extern struct console_cmdline console_list[MAX_CMDLINECONSOLES];
+
+/*
+ * The interface for a console, or any other device that
+ * wants to capture console messages (printer driver?)
+ */
+
+#define CON_PRINTBUFFER (1)
+#define CON_CONSDEV (2) /* Last on the command line */
+#define CON_ENABLED (4)
+
+struct console
+{
+ char name[8];
+ void (*write)(struct console *, const char *, unsigned);
+ int (*read)(struct console *, const char *, unsigned);
+ kdev_t (*device)(struct console *);
+ int (*wait_key)(struct console *);
+ void (*unblank)(void);
+ int (*setup)(struct console *, char *);
+ short flags;
+ short index;
+ int cflag;
+ struct console *next;
+};
+
+extern void register_console(struct console *);
+extern int unregister_console(struct console *);
+extern struct console *console_drivers;
+
+/* VESA Blanking Levels */
+#define VESA_NO_BLANKING 0
+#define VESA_VSYNC_SUSPEND 1
+#define VESA_HSYNC_SUSPEND 2
+#define VESA_POWERDOWN 3
+
+#endif /* _LINUX_CONSOLE_H */
diff --git a/pfinet/linux-src/include/linux/console_struct.h b/pfinet/linux-src/include/linux/console_struct.h
new file mode 100644
index 00000000..0f664086
--- /dev/null
+++ b/pfinet/linux-src/include/linux/console_struct.h
@@ -0,0 +1,109 @@
+/*
+ * console_struct.h
+ *
+ * Data structure describing single virtual console except for data
+ * used by vt.c.
+ *
+ * Fields marked with [#] must be set by the low-level driver.
+ * Fields marked with [!] can be changed by the low-level driver
+ * to achieve effects such as fast scrolling by changing the origin.
+ */
+
+#define NPAR 16
+
+struct vc_data {
+ unsigned short vc_num; /* Console number */
+ unsigned int vc_cols; /* [#] Console size */
+ unsigned int vc_rows;
+ unsigned int vc_size_row; /* Bytes per row */
+ struct consw *vc_sw;
+ unsigned short *vc_screenbuf; /* In-memory character/attribute buffer */
+ unsigned int vc_screenbuf_size;
+ unsigned char vc_attr; /* Current attributes */
+ unsigned char vc_def_color; /* Default colors */
+ unsigned char vc_color; /* Foreground & background */
+ unsigned char vc_s_color; /* Saved foreground & background */
+ unsigned char vc_ulcolor; /* Color for underline mode */
+ unsigned char vc_halfcolor; /* Color for half intensity mode */
+ unsigned short vc_complement_mask; /* [#] Xor mask for mouse pointer */
+ unsigned short vc_hi_font_mask; /* [#] Attribute set for upper 256 chars of font or 0 if not supported */
+ unsigned short vc_video_erase_char; /* Background erase character */
+ unsigned short vc_s_complement_mask; /* Saved mouse pointer mask */
+ unsigned int vc_x, vc_y; /* Cursor position */
+ unsigned int vc_top, vc_bottom; /* Scrolling region */
+ unsigned int vc_state; /* Escape sequence parser state */
+ unsigned int vc_npar,vc_par[NPAR]; /* Parameters of current escape sequence */
+ unsigned long vc_origin; /* [!] Start of real screen */
+ unsigned long vc_scr_end; /* [!] End of real screen */
+ unsigned long vc_visible_origin; /* [!] Top of visible window */
+ unsigned long vc_pos; /* Cursor address */
+ unsigned int vc_saved_x;
+ unsigned int vc_saved_y;
+ /* mode flags */
+ unsigned int vc_charset : 1; /* Character set G0 / G1 */
+ unsigned int vc_s_charset : 1; /* Saved character set */
+ unsigned int vc_disp_ctrl : 1; /* Display chars < 32? */
+ unsigned int vc_toggle_meta : 1; /* Toggle high bit? */
+ unsigned int vc_decscnm : 1; /* Screen Mode */
+ unsigned int vc_decom : 1; /* Origin Mode */
+ unsigned int vc_decawm : 1; /* Autowrap Mode */
+ unsigned int vc_deccm : 1; /* Cursor Visible */
+ unsigned int vc_decim : 1; /* Insert Mode */
+ unsigned int vc_deccolm : 1; /* 80/132 Column Mode */
+ /* attribute flags */
+ unsigned int vc_intensity : 2; /* 0=half-bright, 1=normal, 2=bold */
+ unsigned int vc_underline : 1;
+ unsigned int vc_blink : 1;
+ unsigned int vc_reverse : 1;
+ unsigned int vc_s_intensity : 2; /* saved rendition */
+ unsigned int vc_s_underline : 1;
+ unsigned int vc_s_blink : 1;
+ unsigned int vc_s_reverse : 1;
+ /* misc */
+ unsigned int vc_ques : 1;
+ unsigned int vc_need_wrap : 1;
+ unsigned int vc_can_do_color : 1;
+ unsigned int vc_report_mouse : 2;
+ unsigned int vc_kmalloced : 1;
+ unsigned char vc_utf : 1; /* Unicode UTF-8 encoding */
+ unsigned char vc_utf_count;
+ int vc_utf_char;
+ unsigned int vc_tab_stop[5]; /* Tab stops. 160 columns. */
+ unsigned char vc_palette[16*3]; /* Colour palette for VGA+ */
+ unsigned short * vc_translate;
+ unsigned char vc_G0_charset;
+ unsigned char vc_G1_charset;
+ unsigned char vc_saved_G0;
+ unsigned char vc_saved_G1;
+ unsigned int vc_bell_pitch; /* Console bell pitch */
+ unsigned int vc_bell_duration; /* Console bell duration */
+ unsigned int vc_cursor_type;
+ struct vc_data **vc_display_fg; /* [!] Ptr to var holding fg console for this display */
+ unsigned long vc_uni_pagedir;
+ unsigned long *vc_uni_pagedir_loc; /* [!] Location of uni_pagedir variable for this console */
+ /* additional information is in vt_kern.h */
+};
+
+struct vc {
+ struct vc_data *d;
+
+ /* might add scrmem, vt_struct, kbd at some time,
+ to have everything in one place - the disadvantage
+ would be that vc_cons etc can no longer be static */
+};
+
+extern struct vc vc_cons [MAX_NR_CONSOLES];
+
+#define CUR_DEF 0
+#define CUR_NONE 1
+#define CUR_UNDERLINE 2
+#define CUR_LOWER_THIRD 3
+#define CUR_LOWER_HALF 4
+#define CUR_TWO_THIRDS 5
+#define CUR_BLOCK 6
+#define CUR_HWMASK 0x0f
+#define CUR_SWMASK 0xfff0
+
+#define CUR_DEFAULT CUR_UNDERLINE
+
+#define CON_IS_VISIBLE(conp) (*conp->vc_display_fg == conp)
diff --git a/pfinet/linux-src/include/linux/consolemap.h b/pfinet/linux-src/include/linux/consolemap.h
new file mode 100644
index 00000000..dee4b654
--- /dev/null
+++ b/pfinet/linux-src/include/linux/consolemap.h
@@ -0,0 +1,15 @@
+/*
+ * consolemap.h
+ *
+ * Interface between console.c, selection.c and consolemap.c
+ */
+#define LAT1_MAP 0
+#define GRAF_MAP 1
+#define IBMPC_MAP 2
+#define USER_MAP 3
+
+struct vc_data;
+
+extern unsigned char inverse_translate(struct vc_data *conp, int glyph);
+extern unsigned short *set_translate(int m,int currcons);
+extern int conv_uni_to_pc(struct vc_data *conp, long ucs);
diff --git a/pfinet/linux-src/include/linux/ctype.h b/pfinet/linux-src/include/linux/ctype.h
new file mode 100644
index 00000000..afa36392
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ctype.h
@@ -0,0 +1,54 @@
+#ifndef _LINUX_CTYPE_H
+#define _LINUX_CTYPE_H
+
+/*
+ * NOTE! This ctype does not handle EOF like the standard C
+ * library is required to.
+ */
+
+#define _U 0x01 /* upper */
+#define _L 0x02 /* lower */
+#define _D 0x04 /* digit */
+#define _C 0x08 /* cntrl */
+#define _P 0x10 /* punct */
+#define _S 0x20 /* white space (space/lf/tab) */
+#define _X 0x40 /* hex digit */
+#define _SP 0x80 /* hard space (0x20) */
+
+extern unsigned char _ctype[];
+
+#define __ismask(x) (_ctype[(int)(unsigned char)(x)])
+
+#define isalnum(c) ((__ismask(c)&(_U|_L|_D)) != 0)
+#define isalpha(c) ((__ismask(c)&(_U|_L)) != 0)
+#define iscntrl(c) ((__ismask(c)&(_C)) != 0)
+#define isdigit(c) ((__ismask(c)&(_D)) != 0)
+#define isgraph(c) ((__ismask(c)&(_P|_U|_L|_D)) != 0)
+#define islower(c) ((__ismask(c)&(_L)) != 0)
+#define isprint(c) ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0)
+#define ispunct(c) ((__ismask(c)&(_P)) != 0)
+#define isspace(c) ((__ismask(c)&(_S)) != 0)
+#define isupper(c) ((__ismask(c)&(_U)) != 0)
+#define isxdigit(c) ((__ismask(c)&(_D|_X)) != 0)
+
+#define isascii(c) (((unsigned char)(c))<=0x7f)
+#define toascii(c) (((unsigned char)(c))&0x7f)
+
+static inline unsigned char __tolower(unsigned char c)
+{
+ if (isupper(c))
+ c -= 'A'-'a';
+ return c;
+}
+
+static inline unsigned char __toupper(unsigned char c)
+{
+ if (islower(c))
+ c -= 'a'-'A';
+ return c;
+}
+
+#define tolower(c) __tolower(c)
+#define toupper(c) __toupper(c)
+
+#endif
diff --git a/pfinet/linux-src/include/linux/cyclades.h b/pfinet/linux-src/include/linux/cyclades.h
new file mode 100644
index 00000000..4e8333c1
--- /dev/null
+++ b/pfinet/linux-src/include/linux/cyclades.h
@@ -0,0 +1,814 @@
+/* $Revision: 2.6 $$Date: 1998/08/10 16:57:01 $
+ * linux/include/linux/cyclades.h
+ *
+ * This file was initially written by
+ * Randolph Bentson <bentson@grieg.seaslug.org> and is maintained by
+ * Ivan Passos <ivan@cyclades.com>.
+ *
+ * This file contains the general definitions for the cyclades.c driver
+ *$Log: cyclades.h,v $
+ *Revision 2.5 1998/08/03 16:57:01 ivan
+ *added cyclades_idle_stats structure;
+ *
+ *Revision 2.4 1998/06/01 12:09:53 ivan
+ *removed closing_wait2 from cyclades_port structure;
+ *
+ *Revision 2.3 1998/03/16 18:01:12 ivan
+ *changes in the cyclades_port structure to get it closer to the
+ *standard serial port structure;
+ *added constants for new ioctls;
+ *
+ *Revision 2.2 1998/02/17 16:50:00 ivan
+ *changes in the cyclades_port structure (addition of shutdown_wait and
+ *chip_rev variables);
+ *added constants for new ioctls and for CD1400 rev. numbers.
+ *
+ *Revision 2.1 1997/10/24 16:03:00 ivan
+ *added rflow (which allows enabling the CD1400 special flow control
+ *feature) and rtsdtr_inv (which allows DTR/RTS pin inversion) to
+ *cyclades_port structure;
+ *added Alpha support
+ *
+ *Revision 2.0 1997/06/30 10:30:00 ivan
+ *added some new doorbell command constants related to IOCTLW and
+ *UART error signaling
+ *
+ *Revision 1.8 1997/06/03 15:30:00 ivan
+ *added constant ZFIRM_HLT
+ *added constant CyPCI_Ze_win ( = 2 * Cy_PCI_Zwin)
+ *
+ *Revision 1.7 1997/03/26 10:30:00 daniel
+ *new entries at the end of cyclades_port struct to reallocate
+ *variables illegally allocated within card memory.
+ *
+ *Revision 1.6 1996/09/09 18:35:30 bentson
+ *fold in changes for Cyclom-Z -- including structures for
+ *communicating with board as well modest changes to original
+ *structures to support new features.
+ *
+ *Revision 1.5 1995/11/13 21:13:31 bentson
+ *changes suggested by Michael Chastain <mec@duracef.shout.net>
+ *to support use of this file in non-kernel applications
+ *
+ *
+ */
+
+#ifndef _LINUX_CYCLADES_H
+#define _LINUX_CYCLADES_H
+
+struct cyclades_monitor {
+ unsigned long int_count;
+ unsigned long char_count;
+ unsigned long char_max;
+ unsigned long char_last;
+};
+
+/*
+ * These stats all reflect activity since the device was last initialized.
+ * (i.e., since the port was opened with no other processes already having it
+ * open)
+ */
+struct cyclades_idle_stats {
+ time_t in_use; /* Time device has been in use (secs) */
+ time_t recv_idle; /* Time since last char received (secs) */
+ time_t xmit_idle; /* Time since last char transmitted (secs) */
+ unsigned long recv_bytes; /* Bytes received */
+ unsigned long xmit_bytes; /* Bytes transmitted */
+ unsigned long overruns; /* Input overruns */
+ unsigned long frame_errs; /* Input framing errors */
+ unsigned long parity_errs; /* Input parity errors */
+};
+
+#define CYCLADES_MAGIC 0x4359
+
+#define CYGETMON 0x435901
+#define CYGETTHRESH 0x435902
+#define CYSETTHRESH 0x435903
+#define CYGETDEFTHRESH 0x435904
+#define CYSETDEFTHRESH 0x435905
+#define CYGETTIMEOUT 0x435906
+#define CYSETTIMEOUT 0x435907
+#define CYGETDEFTIMEOUT 0x435908
+#define CYSETDEFTIMEOUT 0x435909
+#define CYSETRFLOW 0x43590a
+#define CYGETRFLOW 0x43590b
+#define CYSETRTSDTR_INV 0x43590c
+#define CYGETRTSDTR_INV 0x43590d
+#define CYZSETPOLLCYCLE 0x43590e
+#define CYZGETPOLLCYCLE 0x43590f
+#define CYGETCD1400VER 0x435910
+#define CYGETCARDINFO 0x435911
+#define CYSETWAIT 0x435912
+#define CYGETWAIT 0x435913
+
+/*************** CYCLOM-Z ADDITIONS ***************/
+
+#define CZIOC ('M' << 8)
+#define CZ_NBOARDS (CZIOC|0xfa)
+#define CZ_BOOT_START (CZIOC|0xfb)
+#define CZ_BOOT_DATA (CZIOC|0xfc)
+#define CZ_BOOT_END (CZIOC|0xfd)
+#define CZ_TEST (CZIOC|0xfe)
+
+#define CZ_DEF_POLL (HZ/25)
+
+#define MAX_BOARD 4 /* Max number of boards */
+#define MAX_DEV 256 /* Max number of ports total */
+#define CYZ_MAX_SPEED 921600
+
+#define CYZ_FIFO_SIZE 16
+
+#define CYZ_BOOT_NWORDS 0x100
+struct CYZ_BOOT_CTRL {
+ unsigned short nboard;
+ int status[MAX_BOARD];
+ int nchannel[MAX_BOARD];
+ int fw_rev[MAX_BOARD];
+ unsigned long offset;
+ unsigned long data[CYZ_BOOT_NWORDS];
+};
+
+
+#ifndef DP_WINDOW_SIZE
+/* #include "cyclomz.h" */
+/****************** ****************** *******************/
+/*
+ * The data types defined below are used in all ZFIRM interface
+ * data structures. They accommodate differences between HW
+ * architectures and compilers.
+ */
+
+#if defined(__alpha__)
+typedef unsigned long ucdouble; /* 64 bits, unsigned */
+typedef unsigned int uclong; /* 32 bits, unsigned */
+#else
+typedef unsigned long uclong; /* 32 bits, unsigned */
+#endif
+typedef unsigned short ucshort; /* 16 bits, unsigned */
+typedef unsigned char ucchar; /* 8 bits, unsigned */
+
+/*
+ * Memory Window Sizes
+ */
+
+#define DP_WINDOW_SIZE (0x00080000) /* window size 512 Kb */
+#define ZE_DP_WINDOW_SIZE (0x00100000) /* window size 1 Mb (Ze and
+ 8Zo V.2 */
+#define CTRL_WINDOW_SIZE (0x00000080) /* runtime regs 128 bytes */
+
+/*
+ * CUSTOM_REG - Cyclom-Z/PCI Custom Registers Set. The driver
+ * normally will access only interested on the fpga_id, fpga_version,
+ * start_cpu and stop_cpu.
+ */
+
+struct CUSTOM_REG {
+ uclong fpga_id; /* FPGA Identification Register */
+ uclong fpga_version; /* FPGA Version Number Register */
+ uclong cpu_start; /* CPU start Register (write) */
+ uclong cpu_stop; /* CPU stop Register (write) */
+ uclong misc_reg; /* Miscellaneous Register */
+ uclong idt_mode; /* IDT mode Register */
+ uclong uart_irq_status; /* UART IRQ status Register */
+ uclong clear_timer0_irq; /* Clear timer interrupt Register */
+ uclong clear_timer1_irq; /* Clear timer interrupt Register */
+ uclong clear_timer2_irq; /* Clear timer interrupt Register */
+ uclong test_register; /* Test Register */
+ uclong test_count; /* Test Count Register */
+ uclong timer_select; /* Timer select register */
+ uclong pr_uart_irq_status; /* Prioritized UART IRQ stat Reg */
+ uclong ram_wait_state; /* RAM wait-state Register */
+ uclong uart_wait_state; /* UART wait-state Register */
+ uclong timer_wait_state; /* timer wait-state Register */
+ uclong ack_wait_state; /* ACK wait State Register */
+};
+
+/*
+ * RUNTIME_9060 - PLX PCI9060ES local configuration and shared runtime
+ * registers. This structure can be used to access the 9060 registers
+ * (memory mapped).
+ */
+
+struct RUNTIME_9060 {
+ uclong loc_addr_range; /* 00h - Local Address Range */
+ uclong loc_addr_base; /* 04h - Local Address Base */
+ uclong loc_arbitr; /* 08h - Local Arbitration */
+ uclong endian_descr; /* 0Ch - Big/Little Endian Descriptor */
+ uclong loc_rom_range; /* 10h - Local ROM Range */
+ uclong loc_rom_base; /* 14h - Local ROM Base */
+ uclong loc_bus_descr; /* 18h - Local Bus descriptor */
+ uclong loc_range_mst; /* 1Ch - Local Range for Master to PCI */
+ uclong loc_base_mst; /* 20h - Local Base for Master PCI */
+ uclong loc_range_io; /* 24h - Local Range for Master IO */
+ uclong pci_base_mst; /* 28h - PCI Base for Master PCI */
+ uclong pci_conf_io; /* 2Ch - PCI configuration for Master IO */
+ uclong filler1; /* 30h */
+ uclong filler2; /* 34h */
+ uclong filler3; /* 38h */
+ uclong filler4; /* 3Ch */
+ uclong mail_box_0; /* 40h - Mail Box 0 */
+ uclong mail_box_1; /* 44h - Mail Box 1 */
+ uclong mail_box_2; /* 48h - Mail Box 2 */
+ uclong mail_box_3; /* 4Ch - Mail Box 3 */
+ uclong filler5; /* 50h */
+ uclong filler6; /* 54h */
+ uclong filler7; /* 58h */
+ uclong filler8; /* 5Ch */
+ uclong pci_doorbell; /* 60h - PCI to Local Doorbell */
+ uclong loc_doorbell; /* 64h - Local to PCI Doorbell */
+ uclong intr_ctrl_stat; /* 68h - Interrupt Control/Status */
+ uclong init_ctrl; /* 6Ch - EEPROM control, Init Control, etc */
+};
+
+/* Values for the Local Base Address re-map register */
+
+#define WIN_RAM 0x00000001L /* set the sliding window to RAM */
+#define WIN_CREG 0x14000001L /* set the window to custom Registers */
+
+/* Values timer select registers */
+
+#define TIMER_BY_1M 0x00 /* clock divided by 1M */
+#define TIMER_BY_256K 0x01 /* clock divided by 256k */
+#define TIMER_BY_128K 0x02 /* clock divided by 128k */
+#define TIMER_BY_32K 0x03 /* clock divided by 32k */
+
+/****************** ****************** *******************/
+#endif
+
+#ifndef ZFIRM_ID
+/* #include "zfwint.h" */
+/****************** ****************** *******************/
+/*
+ * This file contains the definitions for interfacing with the
+ * Cyclom-Z ZFIRM Firmware.
+ */
+
+/* General Constant definitions */
+
+#define MAX_CHAN 64 /* max number of channels per board */
+
+/* firmware id structure (set after boot) */
+
+#define ID_ADDRESS 0x00000180L /* signature/pointer address */
+#define ZFIRM_ID 0x5557465AL /* ZFIRM/U signature */
+#define ZFIRM_HLT 0x59505B5CL /* ZFIRM needs external power supply */
+#define ZFIRM_RST 0x56040674L /* RST signal (due to FW reset) */
+
+#define ZF_TINACT_DEF 1000 /* default inactivity timeout
+ (1000 ms) */
+#define ZF_TINACT ZF_TINACT_DEF
+
+struct FIRM_ID {
+ uclong signature; /* ZFIRM/U signature */
+ uclong zfwctrl_addr; /* pointer to ZFW_CTRL structure */
+};
+
+/* Op. System id */
+
+#define C_OS_LINUX 0x00000030 /* generic Linux system */
+
+/* channel op_mode */
+
+#define C_CH_DISABLE 0x00000000 /* channel is disabled */
+#define C_CH_TXENABLE 0x00000001 /* channel Tx enabled */
+#define C_CH_RXENABLE 0x00000002 /* channel Rx enabled */
+#define C_CH_ENABLE 0x00000003 /* channel Tx/Rx enabled */
+#define C_CH_LOOPBACK 0x00000004 /* Loopback mode */
+
+/* comm_parity - parity */
+
+#define C_PR_NONE 0x00000000 /* None */
+#define C_PR_ODD 0x00000001 /* Odd */
+#define C_PR_EVEN 0x00000002 /* Even */
+#define C_PR_MARK 0x00000004 /* Mark */
+#define C_PR_SPACE 0x00000008 /* Space */
+#define C_PR_PARITY 0x000000ff
+
+#define C_PR_DISCARD 0x00000100 /* discard char with frame/par error */
+#define C_PR_IGNORE 0x00000200 /* ignore frame/par error */
+
+/* comm_data_l - data length and stop bits */
+
+#define C_DL_CS5 0x00000001
+#define C_DL_CS6 0x00000002
+#define C_DL_CS7 0x00000004
+#define C_DL_CS8 0x00000008
+#define C_DL_CS 0x0000000f
+#define C_DL_1STOP 0x00000010
+#define C_DL_15STOP 0x00000020
+#define C_DL_2STOP 0x00000040
+#define C_DL_STOP 0x000000f0
+
+/* interrupt enabling/status */
+
+#define C_IN_DISABLE 0x00000000 /* zero, disable interrupts */
+#define C_IN_TXBEMPTY 0x00000001 /* tx buffer empty */
+#define C_IN_TXLOWWM 0x00000002 /* tx buffer below LWM */
+#define C_IN_RXHIWM 0x00000010 /* rx buffer above HWM */
+#define C_IN_RXNNDT 0x00000020 /* rx no new data timeout */
+#define C_IN_MDCD 0x00000100 /* modem DCD change */
+#define C_IN_MDSR 0x00000200 /* modem DSR change */
+#define C_IN_MRI 0x00000400 /* modem RI change */
+#define C_IN_MCTS 0x00000800 /* modem CTS change */
+#define C_IN_RXBRK 0x00001000 /* Break received */
+#define C_IN_PR_ERROR 0x00002000 /* parity error */
+#define C_IN_FR_ERROR 0x00004000 /* frame error */
+#define C_IN_OVR_ERROR 0x00008000 /* overrun error */
+#define C_IN_RXOFL 0x00010000 /* RX buffer overflow */
+#define C_IN_IOCTLW 0x00020000 /* I/O control w/ wait */
+#define C_IN_MRTS 0x00040000 /* modem RTS drop */
+#define C_IN_ICHAR 0x00080000
+
+/* flow control */
+
+#define C_FL_OXX 0x00000001 /* output Xon/Xoff flow control */
+#define C_FL_IXX 0x00000002 /* output Xon/Xoff flow control */
+#define C_FL_OIXANY 0x00000004 /* output Xon/Xoff (any xon) */
+#define C_FL_SWFLOW 0x0000000f
+
+/* flow status */
+
+#define C_FS_TXIDLE 0x00000000 /* no Tx data in the buffer or UART */
+#define C_FS_SENDING 0x00000001 /* UART is sending data */
+#define C_FS_SWFLOW 0x00000002 /* Tx is stopped by received Xoff */
+
+/* rs_control/rs_status RS-232 signals */
+
+#define C_RS_PARAM 0x80000000 /* Indicates presence of parameter in
+ IOCTLM command */
+#define C_RS_RTS 0x00000001 /* RTS */
+#define C_RS_DTR 0x00000004 /* DTR */
+#define C_RS_DCD 0x00000100 /* CD */
+#define C_RS_DSR 0x00000200 /* DSR */
+#define C_RS_RI 0x00000400 /* RI */
+#define C_RS_CTS 0x00000800 /* CTS */
+
+/* commands Host <-> Board */
+
+#define C_CM_RESET 0x01 /* reset/flush buffers */
+#define C_CM_IOCTL 0x02 /* re-read CH_CTRL */
+#define C_CM_IOCTLW 0x03 /* re-read CH_CTRL, intr when done */
+#define C_CM_IOCTLM 0x04 /* RS-232 outputs change */
+#define C_CM_SENDXOFF 0x10 /* send Xoff */
+#define C_CM_SENDXON 0x11 /* send Xon */
+#define C_CM_CLFLOW 0x12 /* Clear flow control (resume) */
+#define C_CM_SENDBRK 0x41 /* send break */
+#define C_CM_INTBACK 0x42 /* Interrupt back */
+#define C_CM_SET_BREAK 0x43 /* Tx break on */
+#define C_CM_CLR_BREAK 0x44 /* Tx break off */
+#define C_CM_CMD_DONE 0x45 /* Previous command done */
+#define C_CM_INTBACK2 0x46 /* Alternate Interrupt back */
+#define C_CM_TINACT 0x51 /* set inactivity detection */
+#define C_CM_IRQ_ENBL 0x52 /* enable generation of interrupts */
+#define C_CM_IRQ_DSBL 0x53 /* disable generation of interrupts */
+#define C_CM_ACK_ENBL 0x54 /* enable acknowledged interrupt mode */
+#define C_CM_ACK_DSBL 0x55 /* disable acknowledged intr mode */
+#define C_CM_FLUSH_RX 0x56 /* flushes Rx buffer */
+#define C_CM_FLUSH_TX 0x57 /* flushes Tx buffer */
+#define C_CM_Q_ENABLE 0x58 /* enables queue access from the
+ driver */
+#define C_CM_Q_DISABLE 0x59 /* disables queue access from the
+ driver */
+
+#define C_CM_TXBEMPTY 0x60 /* Tx buffer is empty */
+#define C_CM_TXLOWWM 0x61 /* Tx buffer low water mark */
+#define C_CM_RXHIWM 0x62 /* Rx buffer high water mark */
+#define C_CM_RXNNDT 0x63 /* rx no new data timeout */
+#define C_CM_TXFEMPTY 0x64
+#define C_CM_ICHAR 0x65
+#define C_CM_MDCD 0x70 /* modem DCD change */
+#define C_CM_MDSR 0x71 /* modem DSR change */
+#define C_CM_MRI 0x72 /* modem RI change */
+#define C_CM_MCTS 0x73 /* modem CTS change */
+#define C_CM_MRTS 0x74 /* modem RTS drop */
+#define C_CM_RXBRK 0x84 /* Break received */
+#define C_CM_PR_ERROR 0x85 /* Parity error */
+#define C_CM_FR_ERROR 0x86 /* Frame error */
+#define C_CM_OVR_ERROR 0x87 /* Overrun error */
+#define C_CM_RXOFL 0x88 /* RX buffer overflow */
+#define C_CM_CMDERROR 0x90 /* command error */
+#define C_CM_FATAL 0x91 /* fatal error */
+#define C_CM_HW_RESET 0x92 /* reset board */
+
+/*
+ * CH_CTRL - This per port structure contains all parameters
+ * that control an specific port. It can be seen as the
+ * configuration registers of a "super-serial-controller".
+ */
+
+struct CH_CTRL {
+ uclong op_mode; /* operation mode */
+ uclong intr_enable; /* interrupt masking */
+ uclong sw_flow; /* SW flow control */
+ uclong flow_status; /* output flow status */
+ uclong comm_baud; /* baud rate - numerically specified */
+ uclong comm_parity; /* parity */
+ uclong comm_data_l; /* data length/stop */
+ uclong comm_flags; /* other flags */
+ uclong hw_flow; /* HW flow control */
+ uclong rs_control; /* RS-232 outputs */
+ uclong rs_status; /* RS-232 inputs */
+ uclong flow_xon; /* xon char */
+ uclong flow_xoff; /* xoff char */
+ uclong hw_overflow; /* hw overflow counter */
+ uclong sw_overflow; /* sw overflow counter */
+ uclong comm_error; /* frame/parity error counter */
+ uclong ichar;
+ uclong filler[7];
+};
+
+
+/*
+ * BUF_CTRL - This per channel structure contains
+ * all Tx and Rx buffer control for a given channel.
+ */
+
+struct BUF_CTRL {
+ uclong flag_dma; /* buffers are in Host memory */
+ uclong tx_bufaddr; /* address of the tx buffer */
+ uclong tx_bufsize; /* tx buffer size */
+ uclong tx_threshold; /* tx low water mark */
+ uclong tx_get; /* tail index tx buf */
+ uclong tx_put; /* head index tx buf */
+ uclong rx_bufaddr; /* address of the rx buffer */
+ uclong rx_bufsize; /* rx buffer size */
+ uclong rx_threshold; /* rx high water mark */
+ uclong rx_get; /* tail index rx buf */
+ uclong rx_put; /* head index rx buf */
+ uclong filler[5]; /* filler to align structures */
+};
+
+/*
+ * BOARD_CTRL - This per board structure contains all global
+ * control fields related to the board.
+ */
+
+struct BOARD_CTRL {
+
+ /* static info provided by the on-board CPU */
+ uclong n_channel; /* number of channels */
+ uclong fw_version; /* firmware version */
+
+ /* static info provided by the driver */
+ uclong op_system; /* op_system id */
+ uclong dr_version; /* driver version */
+
+ /* board control area */
+ uclong inactivity; /* inactivity control */
+
+ /* host to FW commands */
+ uclong hcmd_channel; /* channel number */
+ uclong hcmd_param; /* pointer to parameters */
+
+ /* FW to Host commands */
+ uclong fwcmd_channel; /* channel number */
+ uclong fwcmd_param; /* pointer to parameters */
+ uclong zf_int_queue_addr; /* offset for INT_QUEUE structure */
+
+ /* filler so the structures are aligned */
+ uclong filler[6];
+};
+
+/* Host Interrupt Queue */
+
+#define QUEUE_SIZE (10*MAX_CHAN)
+
+struct INT_QUEUE {
+ unsigned char intr_code[QUEUE_SIZE];
+ unsigned long channel[QUEUE_SIZE];
+ unsigned long param[QUEUE_SIZE];
+ unsigned long put;
+ unsigned long get;
+};
+
+/*
+ * ZFW_CTRL - This is the data structure that includes all other
+ * data structures used by the Firmware.
+ */
+
+struct ZFW_CTRL {
+ struct BOARD_CTRL board_ctrl;
+ struct CH_CTRL ch_ctrl[MAX_CHAN];
+ struct BUF_CTRL buf_ctrl[MAX_CHAN];
+};
+
+/****************** ****************** *******************/
+#endif
+
+/* Per card data structure */
+struct cyclades_card {
+ long base_addr;
+ long ctl_addr;
+ int irq;
+ int num_chips; /* 0 if card absent, -1 if Z/PCI, else Y */
+ int first_line; /* minor number of first channel on card */
+ int bus_index; /* address shift - 0 for ISA, 1 for PCI */
+ int intr_enabled; /* FW Interrupt flag - 0 disabled, 1 enabled */
+#ifdef __KERNEL__
+ spinlock_t card_lock;
+#else
+ uclong filler;
+#endif
+};
+
+struct cyclades_chip {
+ int filler;
+};
+
+
+#ifdef __KERNEL__
+
+/***************************************
+ * Memory access functions/macros *
+ * (required to support Alpha systems) *
+ ***************************************/
+
+#define cy_writeb(port,val) {writeb((ucchar)(val),(ulong)(port)); mb();}
+#define cy_writew(port,val) {writew((ushort)(val),(ulong)(port)); mb();}
+#define cy_writel(port,val) {writel((uclong)(val),(ulong)(port)); mb();}
+
+#define cy_readb(port) readb(port)
+#define cy_readw(port) readw(port)
+#define cy_readl(port) readl(port)
+
+/*
+ * Statistics counters
+ */
+struct cyclades_icount {
+ __u32 cts, dsr, rng, dcd, tx, rx;
+ __u32 frame, parity, overrun, brk;
+ __u32 buf_overrun;
+};
+
+/*
+ * This is our internal structure for each serial port's state.
+ *
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+struct cyclades_port {
+ int magic;
+ int card;
+ int line;
+ int flags; /* defined in tty.h */
+ int type; /* UART type */
+ struct tty_struct *tty;
+ int read_status_mask;
+ int ignore_status_mask;
+ int timeout;
+ int xmit_fifo_size;
+ int cor1,cor2,cor3,cor4,cor5;
+ int tbpr,tco,rbpr,rco;
+ int baud;
+ int rflow;
+ int rtsdtr_inv;
+ int chip_rev;
+ int custom_divisor;
+ int x_char; /* to be pushed out ASAP */
+ int close_delay;
+ unsigned short closing_wait;
+ unsigned long event;
+ unsigned long last_active;
+ int count; /* # of fd on device */
+ int breakon;
+ int breakoff;
+ int blocked_open; /* # of blocked opens */
+ long session; /* Session of opening process */
+ long pgrp; /* pgrp of opening process */
+ unsigned char *xmit_buf;
+ int xmit_head;
+ int xmit_tail;
+ int xmit_cnt;
+ int default_threshold;
+ int default_timeout;
+ unsigned long jiffies[3];
+ unsigned long rflush_count;
+ struct termios normal_termios;
+ struct termios callout_termios;
+ struct cyclades_monitor mon;
+ struct cyclades_idle_stats idle_stats;
+ struct cyclades_icount icount;
+ struct tq_struct tqueue;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+ struct wait_queue *shutdown_wait;
+ struct wait_queue *delta_msr_wait;
+};
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at cy interrupt time.
+ */
+#define Cy_EVENT_READ_PROCESS 0
+#define Cy_EVENT_WRITE_WAKEUP 1
+#define Cy_EVENT_HANGUP 2
+#define Cy_EVENT_BREAK 3
+#define Cy_EVENT_OPEN_WAKEUP 4
+#define Cy_EVENT_SHUTDOWN_WAKEUP 5
+#define Cy_EVENT_DELTA_WAKEUP 6
+#define Cy_EVENT_Z_RX_FULL 7
+
+#define CLOSING_WAIT_DELAY 30*HZ
+#define CY_CLOSING_WAIT_NONE 65535
+#define CY_CLOSING_WAIT_INF 0
+
+
+#define CyMAX_CHIPS_PER_CARD 8
+#define CyMAX_CHAR_FIFO 12
+#define CyPORTS_PER_CHIP 4
+#define CD1400_MAX_SPEED 115200
+
+#define CyISA_Ywin 0x2000
+
+#define CyPCI_Ywin 0x4000
+#define CyPCI_Yctl 0x80
+#define CyPCI_Zctl CTRL_WINDOW_SIZE
+#define CyPCI_Zwin 0x80000
+#define CyPCI_Ze_win (2 * CyPCI_Zwin)
+
+#define PCI_DEVICE_ID_MASK 0x06
+
+/**** CD1400 registers ****/
+
+#define CD1400_REV_G 0x46
+#define CD1400_REV_J 0x48
+
+#define CyRegSize 0x0400
+#define Cy_HwReset 0x1400
+#define Cy_ClrIntr 0x1800
+#define Cy_EpldRev 0x1e00
+
+/* Global Registers */
+
+#define CyGFRCR (0x40*2)
+#define CyRevE (44)
+#define CyCAR (0x68*2)
+#define CyCHAN_0 (0x00)
+#define CyCHAN_1 (0x01)
+#define CyCHAN_2 (0x02)
+#define CyCHAN_3 (0x03)
+#define CyGCR (0x4B*2)
+#define CyCH0_SERIAL (0x00)
+#define CyCH0_PARALLEL (0x80)
+#define CySVRR (0x67*2)
+#define CySRModem (0x04)
+#define CySRTransmit (0x02)
+#define CySRReceive (0x01)
+#define CyRICR (0x44*2)
+#define CyTICR (0x45*2)
+#define CyMICR (0x46*2)
+#define CyICR0 (0x00)
+#define CyICR1 (0x01)
+#define CyICR2 (0x02)
+#define CyICR3 (0x03)
+#define CyRIR (0x6B*2)
+#define CyTIR (0x6A*2)
+#define CyMIR (0x69*2)
+#define CyIRDirEq (0x80)
+#define CyIRBusy (0x40)
+#define CyIRUnfair (0x20)
+#define CyIRContext (0x1C)
+#define CyIRChannel (0x03)
+#define CyPPR (0x7E*2)
+#define CyCLOCK_20_1MS (0x27)
+#define CyCLOCK_25_1MS (0x31)
+#define CyCLOCK_25_5MS (0xf4)
+#define CyCLOCK_60_1MS (0x75)
+#define CyCLOCK_60_2MS (0xea)
+
+/* Virtual Registers */
+
+#define CyRIVR (0x43*2)
+#define CyTIVR (0x42*2)
+#define CyMIVR (0x41*2)
+#define CyIVRMask (0x07)
+#define CyIVRRxEx (0x07)
+#define CyIVRRxOK (0x03)
+#define CyIVRTxOK (0x02)
+#define CyIVRMdmOK (0x01)
+#define CyTDR (0x63*2)
+#define CyRDSR (0x62*2)
+#define CyTIMEOUT (0x80)
+#define CySPECHAR (0x70)
+#define CyBREAK (0x08)
+#define CyPARITY (0x04)
+#define CyFRAME (0x02)
+#define CyOVERRUN (0x01)
+#define CyMISR (0x4C*2)
+/* see CyMCOR_ and CyMSVR_ for bits*/
+#define CyEOSRR (0x60*2)
+
+/* Channel Registers */
+
+#define CyLIVR (0x18*2)
+#define CyMscsr (0x01)
+#define CyTdsr (0x02)
+#define CyRgdsr (0x03)
+#define CyRedsr (0x07)
+#define CyCCR (0x05*2)
+/* Format 1 */
+#define CyCHAN_RESET (0x80)
+#define CyCHIP_RESET (0x81)
+#define CyFlushTransFIFO (0x82)
+/* Format 2 */
+#define CyCOR_CHANGE (0x40)
+#define CyCOR1ch (0x02)
+#define CyCOR2ch (0x04)
+#define CyCOR3ch (0x08)
+/* Format 3 */
+#define CySEND_SPEC_1 (0x21)
+#define CySEND_SPEC_2 (0x22)
+#define CySEND_SPEC_3 (0x23)
+#define CySEND_SPEC_4 (0x24)
+/* Format 4 */
+#define CyCHAN_CTL (0x10)
+#define CyDIS_RCVR (0x01)
+#define CyENB_RCVR (0x02)
+#define CyDIS_XMTR (0x04)
+#define CyENB_XMTR (0x08)
+#define CySRER (0x06*2)
+#define CyMdmCh (0x80)
+#define CyRxData (0x10)
+#define CyTxRdy (0x04)
+#define CyTxMpty (0x02)
+#define CyNNDT (0x01)
+#define CyCOR1 (0x08*2)
+#define CyPARITY_NONE (0x00)
+#define CyPARITY_0 (0x20)
+#define CyPARITY_1 (0xA0)
+#define CyPARITY_E (0x40)
+#define CyPARITY_O (0xC0)
+#define Cy_1_STOP (0x00)
+#define Cy_1_5_STOP (0x04)
+#define Cy_2_STOP (0x08)
+#define Cy_5_BITS (0x00)
+#define Cy_6_BITS (0x01)
+#define Cy_7_BITS (0x02)
+#define Cy_8_BITS (0x03)
+#define CyCOR2 (0x09*2)
+#define CyIXM (0x80)
+#define CyTxIBE (0x40)
+#define CyETC (0x20)
+#define CyAUTO_TXFL (0x60)
+#define CyLLM (0x10)
+#define CyRLM (0x08)
+#define CyRtsAO (0x04)
+#define CyCtsAE (0x02)
+#define CyDsrAE (0x01)
+#define CyCOR3 (0x0A*2)
+#define CySPL_CH_DRANGE (0x80) /* special character detect range */
+#define CySPL_CH_DET1 (0x40) /* enable special character detection
+ on SCHR4-SCHR3 */
+#define CyFL_CTRL_TRNSP (0x20) /* Flow Control Transparency */
+#define CySPL_CH_DET2 (0x10) /* Enable special character detection
+ on SCHR2-SCHR1 */
+#define CyREC_FIFO (0x0F) /* Receive FIFO threshold */
+#define CyCOR4 (0x1E*2)
+#define CyCOR5 (0x1F*2)
+#define CyCCSR (0x0B*2)
+#define CyRxEN (0x80)
+#define CyRxFloff (0x40)
+#define CyRxFlon (0x20)
+#define CyTxEN (0x08)
+#define CyTxFloff (0x04)
+#define CyTxFlon (0x02)
+#define CyRDCR (0x0E*2)
+#define CySCHR1 (0x1A*2)
+#define CySCHR2 (0x1B*2)
+#define CySCHR3 (0x1C*2)
+#define CySCHR4 (0x1D*2)
+#define CySCRL (0x22*2)
+#define CySCRH (0x23*2)
+#define CyLNC (0x24*2)
+#define CyMCOR1 (0x15*2)
+#define CyMCOR2 (0x16*2)
+#define CyRTPR (0x21*2)
+#define CyMSVR1 (0x6C*2)
+#define CyMSVR2 (0x6D*2)
+#define CyANY_DELTA (0xF0)
+#define CyDSR (0x80)
+#define CyCTS (0x40)
+#define CyRI (0x20)
+#define CyDCD (0x10)
+#define CyDTR (0x02)
+#define CyRTS (0x01)
+#define CyPVSR (0x6F*2)
+#define CyRBPR (0x78*2)
+#define CyRCOR (0x7C*2)
+#define CyTBPR (0x72*2)
+#define CyTCOR (0x76*2)
+
+/* Custom Registers */
+
+#define CyPLX_VER (0x3400)
+#define PLX_9050 0x0b
+#define PLX_9060 0x0c
+#define PLX_9080 0x0d
+
+/***************************************************************************/
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_CYCLADES_H */
diff --git a/pfinet/linux-src/include/linux/dcache.h b/pfinet/linux-src/include/linux/dcache.h
new file mode 100644
index 00000000..9c1f8b5f
--- /dev/null
+++ b/pfinet/linux-src/include/linux/dcache.h
@@ -0,0 +1,196 @@
+#ifndef __LINUX_DCACHE_H
+#define __LINUX_DCACHE_H
+
+#ifdef __KERNEL__
+
+/*
+ * linux/include/linux/dcache.h
+ *
+ * Dirent cache data structures
+ *
+ * (C) Copyright 1997 Thomas Schoebel-Theuer,
+ * with heavy changes by Linus Torvalds
+ */
+
+#define D_MAXLEN 1024
+
+#define IS_ROOT(x) ((x) == (x)->d_parent)
+
+/*
+ * "quick string" -- eases parameter passing, but more importantly
+ * saves "metadata" about the string (ie length and the hash).
+ */
+struct qstr {
+ const unsigned char * name;
+ unsigned int len;
+ unsigned int hash;
+};
+
+/* Name hashing routines. Initial hash value */
+#define init_name_hash() 0
+
+/* partial hash update function. Assume roughly 4 bits per character */
+static __inline__ unsigned long partial_name_hash(unsigned long c, unsigned long prevhash)
+{
+ prevhash = (prevhash << 4) | (prevhash >> (8*sizeof(unsigned long)-4));
+ return prevhash ^ c;
+}
+
+/* Finally: cut down the number of bits to a int value (and try to avoid losing bits) */
+static __inline__ unsigned long end_name_hash(unsigned long hash)
+{
+ if (sizeof(hash) > sizeof(unsigned int))
+ hash += hash >> 4*sizeof(hash);
+ return (unsigned int) hash;
+}
+
+/* Compute the hash for a name string. */
+static __inline__ unsigned int full_name_hash(const unsigned char * name, unsigned int len)
+{
+ unsigned long hash = init_name_hash();
+ while (len--)
+ hash = partial_name_hash(*name++, hash);
+ return end_name_hash(hash);
+}
+
+#define DNAME_INLINE_LEN 16
+
+struct dentry {
+ int d_count;
+ unsigned int d_flags;
+ struct inode * d_inode; /* Where the name belongs to - NULL is negative */
+ struct dentry * d_parent; /* parent directory */
+ struct dentry * d_mounts; /* mount information */
+ struct dentry * d_covers;
+ struct list_head d_hash; /* lookup hash list */
+ struct list_head d_lru; /* d_count = 0 LRU list */
+ struct list_head d_child; /* child of parent list */
+ struct list_head d_subdirs; /* our children */
+ struct list_head d_alias; /* inode alias list */
+ struct qstr d_name;
+ unsigned long d_time; /* used by d_revalidate */
+ struct dentry_operations *d_op;
+ struct super_block * d_sb; /* The root of the dentry tree */
+ unsigned long d_reftime; /* last time referenced */
+ void * d_fsdata; /* fs-specific data */
+ unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */
+};
+
+struct dentry_operations {
+ int (*d_revalidate)(struct dentry *, int);
+ int (*d_hash) (struct dentry *, struct qstr *);
+ int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
+ void (*d_delete)(struct dentry *);
+ void (*d_release)(struct dentry *);
+ void (*d_iput)(struct dentry *, struct inode *);
+};
+
+/* the dentry parameter passed to d_hash and d_compare is the parent
+ * directory of the entries to be compared. It is used in case these
+ * functions need any directory specific information for determining
+ * equivalency classes. Using the dentry itself might not work, as it
+ * might be a negative dentry which has no information associated with
+ * it */
+
+
+
+/* d_flags entries */
+#define DCACHE_AUTOFS_PENDING 0x0001 /* autofs: "under construction" */
+#define DCACHE_NFSFS_RENAMED 0x0002 /* this dentry has been "silly
+ * renamed" and has to be
+ * deleted on the last dput()
+ */
+
+/*
+ * d_drop() unhashes the entry from the parent
+ * dentry hashes, so that it won't be found through
+ * a VFS lookup any more. Note that this is different
+ * from deleting the dentry - d_delete will try to
+ * mark the dentry negative if possible, giving a
+ * successful _negative_ lookup, while d_drop will
+ * just make the cache lookup fail.
+ *
+ * d_drop() is used mainly for stuff that wants
+ * to invalidate a dentry for some reason (NFS
+ * timeouts or autofs deletes).
+ */
+static __inline__ void d_drop(struct dentry * dentry)
+{
+ list_del(&dentry->d_hash);
+ INIT_LIST_HEAD(&dentry->d_hash);
+}
+
+static __inline__ int dname_external(struct dentry *d)
+{
+ return d->d_name.name != d->d_iname;
+}
+
+/*
+ * These are the low-level FS interfaces to the dcache..
+ */
+extern void d_instantiate(struct dentry *, struct inode *);
+extern void d_delete(struct dentry *);
+
+/* allocate/de-allocate */
+extern struct dentry * d_alloc(struct dentry * parent, const struct qstr *name);
+extern int prune_dcache(int, int);
+extern void shrink_dcache_sb(struct super_block *);
+extern void shrink_dcache_parent(struct dentry *);
+extern int d_invalidate(struct dentry *);
+
+#define shrink_dcache() prune_dcache(0, -1)
+
+/* dcache memory management */
+extern void shrink_dcache_memory(int, unsigned int);
+extern void check_dcache_memory(void);
+extern void free_inode_memory(int); /* defined in fs/inode.c */
+
+/* only used at mount-time */
+extern struct dentry * d_alloc_root(struct inode * root_inode, struct dentry * old_root);
+
+/* test whether root is busy without destroying dcache */
+extern int is_root_busy(struct dentry *);
+
+/* test whether we have any submounts in a subdir tree */
+extern int have_submounts(struct dentry *);
+
+/*
+ * This adds the entry to the hash queues.
+ */
+extern void d_rehash(struct dentry * entry);
+/*
+ * This adds the entry to the hash queues and initializes "d_inode".
+ * The entry was actually filled in earlier during "d_alloc()"
+ */
+static __inline__ void d_add(struct dentry * entry, struct inode * inode)
+{
+ d_rehash(entry);
+ d_instantiate(entry, inode);
+}
+
+/* used for rename() and baskets */
+extern void d_move(struct dentry * entry, struct dentry * newdentry);
+
+/* appendix may either be NULL or be used for transname suffixes */
+extern struct dentry * d_lookup(struct dentry * dir, struct qstr * name);
+
+/* validate "insecure" dentry pointer */
+extern int d_validate(struct dentry *dentry, struct dentry *dparent,
+ unsigned int hash, unsigned int len);
+
+/* write full pathname into buffer and return start of pathname */
+extern char * d_path(struct dentry * entry, char * buf, int buflen);
+
+/* Allocation counts.. */
+static __inline__ struct dentry * dget(struct dentry *dentry)
+{
+ if (dentry)
+ dentry->d_count++;
+ return dentry;
+}
+
+extern void dput(struct dentry *);
+
+#endif /* __KERNEL__ */
+
+#endif /* __LINUX_DCACHE_H */
diff --git a/pfinet/linux-src/include/linux/delay.h b/pfinet/linux-src/include/linux/delay.h
new file mode 100644
index 00000000..e1cf03d6
--- /dev/null
+++ b/pfinet/linux-src/include/linux/delay.h
@@ -0,0 +1,37 @@
+#ifndef _LINUX_DELAY_H
+#define _LINUX_DELAY_H
+
+/*
+ * Copyright (C) 1993 Linus Torvalds
+ *
+ * Delay routines, using a pre-computed "loops_per_second" value.
+ */
+
+extern unsigned long loops_per_sec;
+
+#include <asm/delay.h>
+
+/*
+ * Using udelay() for intervals greater than a few milliseconds can
+ * risk overflow for high loops_per_sec (high bogomips) machines. The
+ * mdelay() provides a wrapper to prevent this. For delays greater
+ * than MAX_UDELAY_MS milliseconds, the wrapper is used. Architecture
+ * specific values can be defined in asm-???/delay.h as an override.
+ * The 2nd mdelay() definition ensures GCC will optimize away the
+ * while loop for the common cases where n <= MAX_UDELAY_MS -- Paul G.
+ */
+
+#ifndef MAX_UDELAY_MS
+#define MAX_UDELAY_MS 5
+#endif
+
+#ifdef notdef
+#define mdelay(n) (\
+ {unsigned long msec=(n); while (msec--) udelay(1000);})
+#else
+#define mdelay(n) (\
+ (__builtin_constant_p(n) && (n)<=MAX_UDELAY_MS) ? udelay((n)*1000) : \
+ ({unsigned long msec=(n); while (msec--) udelay(1000);}))
+#endif
+
+#endif /* defined(_LINUX_DELAY_H) */
diff --git a/pfinet/linux-src/include/linux/devpts_fs.h b/pfinet/linux-src/include/linux/devpts_fs.h
new file mode 100644
index 00000000..d9dfbb44
--- /dev/null
+++ b/pfinet/linux-src/include/linux/devpts_fs.h
@@ -0,0 +1,74 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/include/linux/devpts_fs.h
+ *
+ * Copyright 1998 H. Peter Anvin -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+/*
+ * Prototypes for the pty driver <-> devpts filesystem interface. Most
+ * of this is really just a hack so we can exclude it or build it as a
+ * module, and probably should go away eventually.
+ */
+
+#ifndef _LINUX_DEVPTS_FS_H
+#define _LINUX_DEVPTS_FS_H 1
+
+#include <linux/config.h>
+#include <linux/kdev_t.h>
+#include <linux/tty.h>
+
+#ifdef CONFIG_DEVPTS_FS
+
+void devpts_pty_new(int, kdev_t);
+void devpts_pty_kill(int);
+#define unix98_max_ptys NR_PTYS * UNIX98_NR_MAJORS;
+
+#elif defined(CONFIG_DEVPTS_FS_MODULE)
+
+#ifdef BUILDING_PTY_C
+void (*devpts_upcall_new)(int,kdev_t) = NULL;
+void (*devpts_upcall_kill)(int) = NULL;
+unsigned int unix98_max_ptys = NR_PTYS * UNIX98_NR_MAJORS;
+
+EXPORT_SYMBOL(devpts_upcall_new);
+EXPORT_SYMBOL(devpts_upcall_kill);
+EXPORT_SYMBOL(unix98_max_ptys);
+#else
+extern void (*devpts_upcall_new)(int,kdev_t);
+extern void (*devpts_upcall_kill)(int);
+extern unsigned int unix98_max_ptys;
+#endif
+
+#ifndef BUILDING_DEVPTS
+extern inline void
+devpts_pty_new(int line, kdev_t device)
+{
+ if ( devpts_upcall_new )
+ return devpts_upcall_new(line,device);
+}
+
+extern inline void
+devpts_pty_kill(int line)
+{
+ if ( devpts_upcall_kill )
+ return devpts_upcall_kill(line);
+}
+#endif
+
+#else /* No /dev/pts filesystem at all */
+
+extern inline void
+devpts_pty_new(int line, kdev_t device) { }
+
+extern inline void
+devpts_pty_kill(int line) { }
+
+#endif
+
+#endif /* _LINUX_DEVPTS_FS_H */
diff --git a/pfinet/linux-src/include/linux/digi1.h b/pfinet/linux-src/include/linux/digi1.h
new file mode 100644
index 00000000..184378d2
--- /dev/null
+++ b/pfinet/linux-src/include/linux/digi1.h
@@ -0,0 +1,100 @@
+/* Definitions for DigiBoard ditty(1) command. */
+
+#if !defined(TIOCMODG)
+#define TIOCMODG ('d'<<8) | 250 /* get modem ctrl state */
+#define TIOCMODS ('d'<<8) | 251 /* set modem ctrl state */
+#endif
+
+#if !defined(TIOCMSET)
+#define TIOCMSET ('d'<<8) | 252 /* set modem ctrl state */
+#define TIOCMGET ('d'<<8) | 253 /* set modem ctrl state */
+#endif
+
+#if !defined(TIOCMBIC)
+#define TIOCMBIC ('d'<<8) | 254 /* set modem ctrl state */
+#define TIOCMBIS ('d'<<8) | 255 /* set modem ctrl state */
+#endif
+
+#if !defined(TIOCSDTR)
+#define TIOCSDTR ('e'<<8) | 0 /* set DTR */
+#define TIOCCDTR ('e'<<8) | 1 /* clear DTR */
+#endif
+
+/************************************************************************
+ * Ioctl command arguments for DIGI parameters.
+ ************************************************************************/
+#define DIGI_GETA ('e'<<8) | 94 /* Read params */
+
+#define DIGI_SETA ('e'<<8) | 95 /* Set params */
+#define DIGI_SETAW ('e'<<8) | 96 /* Drain & set params */
+#define DIGI_SETAF ('e'<<8) | 97 /* Drain, flush & set params */
+
+#define DIGI_GETFLOW ('e'<<8) | 99 /* Get startc/stopc flow */
+ /* control characters */
+#define DIGI_SETFLOW ('e'<<8) | 100 /* Set startc/stopc flow */
+ /* control characters */
+#define DIGI_GETAFLOW ('e'<<8) | 101 /* Get Aux. startc/stopc */
+ /* flow control chars */
+#define DIGI_SETAFLOW ('e'<<8) | 102 /* Set Aux. startc/stopc */
+ /* flow control chars */
+
+#define DIGI_GETINFO ('e'<<8) | 103 /* Fill in digi_info */
+#define DIGI_POLLER ('e'<<8) | 104 /* Turn on/off poller */
+#define DIGI_INIT ('e'<<8) | 105 /* Allow things to run. */
+
+struct digiflow_struct
+{
+ unsigned char startc; /* flow cntl start char */
+ unsigned char stopc; /* flow cntl stop char */
+};
+
+typedef struct digiflow_struct digiflow_t;
+
+
+/************************************************************************
+ * Values for digi_flags
+ ************************************************************************/
+#define DIGI_IXON 0x0001 /* Handle IXON in the FEP */
+#define DIGI_FAST 0x0002 /* Fast baud rates */
+#define RTSPACE 0x0004 /* RTS input flow control */
+#define CTSPACE 0x0008 /* CTS output flow control */
+#define DSRPACE 0x0010 /* DSR output flow control */
+#define DCDPACE 0x0020 /* DCD output flow control */
+#define DTRPACE 0x0040 /* DTR input flow control */
+#define DIGI_FORCEDCD 0x0100 /* Force carrier */
+#define DIGI_ALTPIN 0x0200 /* Alternate RJ-45 pin config */
+#define DIGI_AIXON 0x0400 /* Aux flow control in fep */
+
+
+/************************************************************************
+ * Values for digiDload
+ ************************************************************************/
+#define NORMAL 0
+#define PCI_CTL 1
+
+#define SIZE8 0
+#define SIZE16 1
+#define SIZE32 2
+
+/************************************************************************
+ * Structure used with ioctl commands for DIGI parameters.
+ ************************************************************************/
+struct digi_struct
+{
+ unsigned short digi_flags; /* Flags (see above) */
+};
+
+typedef struct digi_struct digi_t;
+
+struct digi_info
+{
+ unsigned long board; /* Which board is this ? */
+ unsigned char status; /* Alive or dead */
+ unsigned char type; /* see epca.h */
+ unsigned char subtype; /* For future XEM, XR, etc ... */
+ unsigned short numports; /* Number of ports configured */
+ unsigned char *port; /* I/O Address */
+ unsigned char *membase; /* DPR Address */
+ unsigned char *version; /* For future ... */
+ unsigned short windowData; /* For future ... */
+} ;
diff --git a/pfinet/linux-src/include/linux/digiFep1.h b/pfinet/linux-src/include/linux/digiFep1.h
new file mode 100644
index 00000000..c47d7fcb
--- /dev/null
+++ b/pfinet/linux-src/include/linux/digiFep1.h
@@ -0,0 +1,136 @@
+
+#define CSTART 0x400L
+#define CMAX 0x800L
+#define ISTART 0x800L
+#define IMAX 0xC00L
+#define CIN 0xD10L
+#define GLOBAL 0xD10L
+#define EIN 0xD18L
+#define FEPSTAT 0xD20L
+#define CHANSTRUCT 0x1000L
+#define RXTXBUF 0x4000L
+
+
+struct global_data
+{
+ volatile ushort cin;
+ volatile ushort cout;
+ volatile ushort cstart;
+ volatile ushort cmax;
+ volatile ushort ein;
+ volatile ushort eout;
+ volatile ushort istart;
+ volatile ushort imax;
+};
+
+
+struct board_chan
+{
+ int filler1;
+ int filler2;
+ volatile ushort tseg;
+ volatile ushort tin;
+ volatile ushort tout;
+ volatile ushort tmax;
+
+ volatile ushort rseg;
+ volatile ushort rin;
+ volatile ushort rout;
+ volatile ushort rmax;
+
+ volatile ushort tlow;
+ volatile ushort rlow;
+ volatile ushort rhigh;
+ volatile ushort incr;
+
+ volatile ushort etime;
+ volatile ushort edelay;
+ volatile unchar *dev;
+
+ volatile ushort iflag;
+ volatile ushort oflag;
+ volatile ushort cflag;
+ volatile ushort gmask;
+
+ volatile ushort col;
+ volatile ushort delay;
+ volatile ushort imask;
+ volatile ushort tflush;
+
+ int filler3;
+ int filler4;
+ int filler5;
+ int filler6;
+
+ volatile unchar num;
+ volatile unchar ract;
+ volatile unchar bstat;
+ volatile unchar tbusy;
+ volatile unchar iempty;
+ volatile unchar ilow;
+ volatile unchar idata;
+ volatile unchar eflag;
+
+ volatile unchar tflag;
+ volatile unchar rflag;
+ volatile unchar xmask;
+ volatile unchar xval;
+ volatile unchar mstat;
+ volatile unchar mchange;
+ volatile unchar mint;
+ volatile unchar lstat;
+
+ volatile unchar mtran;
+ volatile unchar orun;
+ volatile unchar startca;
+ volatile unchar stopca;
+ volatile unchar startc;
+ volatile unchar stopc;
+ volatile unchar vnext;
+ volatile unchar hflow;
+
+ volatile unchar fillc;
+ volatile unchar ochar;
+ volatile unchar omask;
+
+ unchar filler7;
+ unchar filler8[28];
+};
+
+
+#define SRXLWATER 0xE0
+#define SRXHWATER 0xE1
+#define STOUT 0xE2
+#define PAUSETX 0xE3
+#define RESUMETX 0xE4
+#define SAUXONOFFC 0xE6
+#define SENDBREAK 0xE8
+#define SETMODEM 0xE9
+#define SETIFLAGS 0xEA
+#define SONOFFC 0xEB
+#define STXLWATER 0xEC
+#define PAUSERX 0xEE
+#define RESUMERX 0xEF
+#define SETBUFFER 0xF2
+#define SETCOOKED 0xF3
+#define SETHFLOW 0xF4
+#define SETCTRLFLAGS 0xF5
+#define SETVNEXT 0xF6
+
+
+
+#define BREAK_IND 0x01
+#define LOWTX_IND 0x02
+#define EMPTYTX_IND 0x04
+#define DATA_IND 0x08
+#define MODEMCHG_IND 0x20
+
+#define FEP_HUPCL 0002000
+#if 0
+#define RTS 0x02
+#define CD 0x08
+#define DSR 0x10
+#define CTS 0x20
+#define RI 0x40
+#define DTR 0x80
+#endif
diff --git a/pfinet/linux-src/include/linux/digiPCI.h b/pfinet/linux-src/include/linux/digiPCI.h
new file mode 100644
index 00000000..834c7036
--- /dev/null
+++ b/pfinet/linux-src/include/linux/digiPCI.h
@@ -0,0 +1,37 @@
+/*************************************************************************
+ * Defines and structure definitions for PCI BIOS Interface
+ *************************************************************************/
+#define PCIMAX 32 /* maximum number of PCI boards */
+
+
+#define PCI_VENDOR_DIGI 0x114F
+#define PCI_DEVICE_EPC 0x0002
+#define PCI_DEVICE_RIGHTSWITCH 0x0003 /* For testing */
+#define PCI_DEVICE_XEM 0x0004
+#define PCI_DEVICE_XR 0x0005
+#define PCI_DEVICE_CX 0x0006
+#define PCI_DEVICE_XRJ 0x0009 /* Jupiter boards with */
+#define PCI_DEVICE_EPCJ 0x000a /* PLX 9060 chip for PCI */
+
+
+/*
+ * On the PCI boards, there is no IO space allocated
+ * The I/O registers will be in the first 3 bytes of the
+ * upper 2MB of the 4MB memory space. The board memory
+ * will be mapped into the low 2MB of the 4MB memory space
+ */
+
+/* Potential location of PCI Bios from E0000 to FFFFF*/
+#define PCI_BIOS_SIZE 0x00020000
+
+/* Size of Memory and I/O for PCI (4MB) */
+#define PCI_RAM_SIZE 0x00400000
+
+/* Size of Memory (2MB) */
+#define PCI_MEM_SIZE 0x00200000
+
+/* Offset of I/0 in Memory (2MB) */
+#define PCI_IO_OFFSET 0x00200000
+
+#define MEMOUTB(basemem, pnum, setmemval) *(caddr_t)((basemem) + ( PCI_IO_OFFSET | pnum << 4 | pnum )) = (setmemval)
+#define MEMINB(basemem, pnum) *(caddr_t)((basemem) + (PCI_IO_OFFSET | pnum << 4 | pnum )) /* for PCI I/O */
diff --git a/pfinet/linux-src/include/linux/dio.h b/pfinet/linux-src/include/linux/dio.h
new file mode 100644
index 00000000..087b06ec
--- /dev/null
+++ b/pfinet/linux-src/include/linux/dio.h
@@ -0,0 +1,204 @@
+/* header file for DIO boards for the HP300 architecture.
+ * Maybe this should handle DIO-II later?
+ * The general structure of this is vaguely based on how
+ * the Amiga port handles Zorro boards.
+ * Copyright (C) Peter Maydell 05/1998 <pmaydell@chiark.greenend.org.uk>
+ *
+ * The board IDs are from the NetBSD kernel, which for once provided
+ * helpful comments...
+ *
+ * This goes with arch/m68k/hp300/dio.c
+ */
+
+#ifndef _LINUX_DIO_H
+#define _LINUX_DIO_H
+
+/* The DIO boards in a system are distinguished by 'select codes' which
+ * range from 0-63 (DIO) and 132-255 (DIO-II).
+ * The DIO board with select code sc is located at physical address
+ * 0x600000 + sc * 0x10000
+ * So DIO cards cover [0x600000-0x800000); the areas [0x200000-0x400000) and
+ * [0x800000-0x1000000) are for additional space required by things
+ * like framebuffers. [0x400000-0x600000) is for miscellaneous internal I/O.
+ * On Linux, this is currently all mapped into the virtual address space
+ * at 0xf0000000 on bootup.
+ * DIO-II boards are at 0x1000000 + (sc - 132) * 0x400000
+ * which is address range [0x1000000-0x20000000) -- too big to map completely,
+ * so currently we just don't handle DIO-II boards. It wouldn't be hard to
+ * do with ioremap() though.
+ */
+#ifdef __KERNEL__
+/* DIO/DIO-II boards all have the following 8bit registers.
+ * These are offsets from the base of the device.
+ */
+#define DIO_IDOFF 0x01 /* primary device ID */
+#define DIO_IPLOFF 0x03 /* interrupt priority level */
+#define DIO_SECIDOFF 0x15 /* secondary device ID */
+#define DIOII_SIZEOFF 0x101 /* device size, DIO-II only */
+
+/* The internal HPIB device is special; this is its physaddr; its select code is 7.
+ * The reason why we have to treat it specially is because apparently it's broken:
+ * the device ID isn't consistent/reliable. *sigh*
+ */
+#define DIO_IHPIBADDR 0x47800
+#define DIO_IHPIBSCODE 7
+
+/* If we don't have the internal HPIB defined, then treat select code 7 like
+ * any other. If we *do* have internal HPIB, then we just have to assume that
+ * select code 7 is the internal HPIB regardless of the ID register :-<
+ */
+#define CONFIG_IHPIB /* hack hack : not yet a proper config option */
+#ifdef CONFIG_IHPIB
+#define DIO_ISIHPIB(scode) ((scode) == DIO_IHPIBSCODE)
+#else
+#define DIO_ISIHPIB(scode) 0
+#endif
+
+#define DIO_VIRADDRBASE 0xf0000000 /* vir addr where IOspace is mapped */
+
+#define DIO_BASE 0x600000 /* start of DIO space */
+#define DIO_END 0x1000000 /* end of DIO space */
+#define DIO_DEVSIZE 0x10000 /* size of a DIO device */
+
+#define DIOII_BASE 0x01000000 /* start of DIO-II space */
+#define DIOII_END 0x20000000 /* end of DIO-II space */
+#define DIOII_DEVSIZE 0x00400000 /* size of a DIO-II device */
+
+/* Highest valid select code. If we add DIO-II support this should become
+ * 256 for everything except HP320, which only has DIO.
+ */
+#define DIO_SCMAX 32
+#define DIOII_SCBASE 132 /* lowest DIO-II select code */
+#define DIO_SCINHOLE(scode) (((scode) >= 32) && ((scode) < DIOII_SCBASE))
+
+/* macros to read device IDs, given base address */
+#define DIO_ID(baseaddr) readb((baseaddr) + DIO_IDOFF)
+#define DIO_SECID(baseaddr) readb((baseaddr) + DIO_SECIDOFF)
+
+/* extract the interrupt level */
+#define DIO_IPL(baseaddr) (((readb((baseaddr) + DIO_IPLOFF) >> 4) & 0x03) + 3)
+
+/* find the size of a DIO-II board's address space.
+ * DIO boards are all fixed length.
+ */
+#define DIOII_SIZE(baseaddr) ((readb((baseaddr) + DIOII_SIZEOFF) + 1) * 0x100000)
+
+/* general purpose macro for both DIO and DIO-II */
+#define DIO_SIZE(scode, base) (DIO_ISDIOII((scode)) ? DIOII_SIZE((base)) : DIO_DEVSIZE)
+
+/* The hardware has primary and secondary IDs; we encode these in a single
+ * int as PRIMARY ID & (SECONDARY ID << 8).
+ * In practice this is only important for framebuffers,
+ * and everybody else just sets ID fields equal to the DIO_ID_FOO value.
+ */
+#define DIO_ENCODE_ID(pr,sec) ((((int)sec & 0xff) << 8) & ((int)pr & 0xff))
+/* macro to determine whether a given primary ID requires a secondary ID byte */
+#define DIO_NEEDSSECID(id) ((id) == DIO_ID_FBUFFER)
+
+/* Now a whole slew of macros giving device IDs and descriptive strings: */
+#define DIO_ID_DCA0 0x02 /* 98644A serial */
+#define DIO_DESC_DCA0 "98644A DCA0 serial"
+#define DIO_ID_DCA0REM 0x82 /* 98644A serial */
+#define DIO_DESC_DCA0REM "98644A DCA0REM serial"
+#define DIO_ID_DCA1 0x42 /* 98644A serial */
+#define DIO_DESC_DCA1 "98644A DCA1 serial"
+#define DIO_ID_DCA1REM 0xc2 /* 98644A serial */
+#define DIO_DESC_DCA1REM "98644A DCA1REM serial"
+#define DIO_ID_DCM 0x05 /* 98642A serial MUX */
+#define DIO_DESC_DCM "98642A DCM serial MUX"
+#define DIO_ID_DCMREM 0x85 /* 98642A serial MUX */
+#define DIO_DESC_DCMREM "98642A DCMREM serial MUX"
+#define DIO_ID_LAN 0x15 /* 98643A LAN */
+#define DIO_DESC_LAN "98643A LAN"
+#define DIO_ID_FHPIB 0x08 /* 98625A/98625B fast HP-IB */
+#define DIO_DESC_FHPIB "98625A/98625B fast HPIB"
+#define DIO_ID_NHPIB 0x80 /* 98624A HP-IB (normal ie slow) */
+#define DIO_DESC_NHPIB "98624A HPIB"
+#define DIO_ID_IHPIB 0x00 /* internal HPIB (not its real ID, it hasn't got one! */
+#define DIO_DESC_IHPIB "internal HPIB"
+#define DIO_ID_SCSI0 0x07 /* 98625A SCSI */
+#define DIO_DESC_SCSI0 "98625A SCSI0"
+#define DIO_ID_SCSI1 0x27 /* ditto */
+#define DIO_DESC_SCSI1 "98625A SCSI1"
+#define DIO_ID_SCSI2 0x47 /* ditto */
+#define DIO_DESC_SCSI2 "98625A SCSI2"
+#define DIO_ID_SCSI3 0x67 /* ditto */
+#define DIO_DESC_SCSI3 "98625A SCSI3"
+#define DIO_ID_FBUFFER 0x39 /* framebuffer: flavour is distinguished by secondary ID */
+#define DIO_DESC_FBUFFER "bitmapped display"
+/* the NetBSD kernel source is a bit unsure as to what these next IDs actually do :-> */
+#define DIO_ID_MISC0 0x03 /* 98622A */
+#define DIO_DESC_MISC0 "98622A"
+#define DIO_ID_MISC1 0x04 /* 98623A */
+#define DIO_DESC_MISC1 "98623A"
+#define DIO_ID_PARALLEL 0x06 /* internal parallel */
+#define DIO_DESC_PARALLEL "internal parallel"
+#define DIO_ID_MISC2 0x09 /* 98287A keyboard */
+#define DIO_DESC_MISC2 "98287A keyboard"
+#define DIO_ID_MISC3 0x0a /* HP98635A FP accelerator */
+#define DIO_DESC_MISC3 "HP98635A FP accelerator"
+#define DIO_ID_MISC4 0x0b /* timer */
+#define DIO_DESC_MISC4 "timer"
+#define DIO_ID_MISC5 0x12 /* 98640A */
+#define DIO_DESC_MISC5 "98640A"
+#define DIO_ID_MISC6 0x16 /* 98659A */
+#define DIO_DESC_MISC6 "98659A"
+#define DIO_ID_MISC7 0x19 /* 237 display */
+#define DIO_DESC_MISC7 "237 display"
+#define DIO_ID_MISC8 0x1a /* quad-wide card */
+#define DIO_DESC_MISC8 "quad-wide card"
+#define DIO_ID_MISC9 0x1b /* 98253A */
+#define DIO_DESC_MISC9 "98253A"
+#define DIO_ID_MISC10 0x1c /* 98627A */
+#define DIO_DESC_MISC10 "98253A"
+#define DIO_ID_MISC11 0x1d /* 98633A */
+#define DIO_DESC_MISC11 "98633A"
+#define DIO_ID_MISC12 0x1e /* 98259A */
+#define DIO_DESC_MISC12 "98259A"
+#define DIO_ID_MISC13 0x1f /* 8741 */
+#define DIO_DESC_MISC13 "8741"
+#define DIO_ID_VME 0x31 /* 98577A VME adapter */
+#define DIO_DESC_VME "98577A VME adapter"
+#define DIO_ID_DCL 0x34 /* 98628A serial */
+#define DIO_DESC_DCL "98628A DCL serial"
+#define DIO_ID_DCLREM 0xb4 /* 98628A serial */
+#define DIO_DESC_DCLREM "98628A DCLREM serial"
+/* These are the secondary IDs for the framebuffers */
+#define DIO_ID2_GATORBOX 0x01 /* 98700/98710 "gatorbox" */
+#define DIO_DESC2_GATORBOX "98700/98710 \"gatorbox\" display"
+#define DIO_ID2_TOPCAT 0x02 /* 98544/98545/98547 "topcat" */
+#define DIO_DESC2_TOPCAT "98544/98545/98547 \"topcat\" display"
+#define DIO_ID2_RENAISSANCE 0x04 /* 98720/98721 "renaissance" */
+#define DIO_DESC2_RENAISSANCE "98720/98721 \"renaissance\" display"
+#define DIO_ID2_LRCATSEYE 0x05 /* lowres "catseye" */
+#define DIO_DESC2_LRCATSEYE "low-res catseye display"
+#define DIO_ID2_HRCCATSEYE 0x06 /* highres colour "catseye" */
+#define DIO_DESC2_HRCCATSEYE "high-res color catseye display"
+#define DIO_ID2_HRMCATSEYE 0x07 /* highres mono "catseye" */
+#define DIO_DESC2_HRMCATSEYE "high-res mono catseye display"
+#define DIO_ID2_DAVINCI 0x08 /* 98730/98731 "davinci" */
+#define DIO_DESC2_DAVINCI "98730/98731 \"davinci\" display"
+#define DIO_ID2_XXXCATSEYE 0x09 /* "catseye" */
+#define DIO_DESC2_XXXCATSEYE "catseye display"
+#define DIO_ID2_HYPERION 0x0e /* A1096A "hyperion" */
+#define DIO_DESC2_HYPERION "A1096A \"hyperion\" display"
+#define DIO_ID2_XGENESIS 0x0b /* "x-genesis"; no NetBSD support */
+#define DIO_DESC2_XGENESIS "\"x-genesis\" display"
+#define DIO_ID2_TIGER 0x0c /* "tiger"; no NetBSD support */
+#define DIO_DESC2_TIGER "\"tiger\" display"
+#define DIO_ID2_YGENESIS 0x0d /* "y-genesis"; no NetBSD support */
+#define DIO_DESC2_YGENESIS "\"y-genesis\" display"
+/* if you add new IDs then you should tell dio.c about them so it can
+ * identify them...
+ */
+
+extern void dio_init(void);
+extern int dio_find(int deviceid);
+extern void *dio_scodetoviraddr(int scode);
+extern int dio_scodetoipl(int scode);
+extern void dio_config_board(int scode);
+extern void dio_unconfig_board(int scode);
+
+
+#endif /* __KERNEL__ */
+#endif /* ndef _LINUX_DIO_H */
diff --git a/pfinet/linux-src/include/linux/dirent.h b/pfinet/linux-src/include/linux/dirent.h
new file mode 100644
index 00000000..a18f7e46
--- /dev/null
+++ b/pfinet/linux-src/include/linux/dirent.h
@@ -0,0 +1,11 @@
+#ifndef _LINUX_DIRENT_H
+#define _LINUX_DIRENT_H
+
+struct dirent {
+ long d_ino;
+ __kernel_off_t d_off;
+ unsigned short d_reclen;
+ char d_name[256]; /* We must not include limits.h! */
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/dlists.h b/pfinet/linux-src/include/linux/dlists.h
new file mode 100644
index 00000000..f92485e4
--- /dev/null
+++ b/pfinet/linux-src/include/linux/dlists.h
@@ -0,0 +1,108 @@
+#ifndef DLISTS_H
+#define DLISTS_H
+/*
+ * include/linux/dlists.h - macros for double linked lists
+ *
+ * Copyright (C) 1997, Thomas Schoebel-Theuer,
+ * <schoebel@informatik.uni-stuttgart.de>.
+ */
+
+/* dlists are cyclic ringlists, so the last element cannot be tested
+ * for NULL. Use the following construct for traversing cyclic lists:
+ * ptr = anchor;
+ * if(ptr) do {
+ * ...
+ * ptr = ptr->{something}_{next,prev};
+ * } while(ptr != anchor);
+ * The effort here is paid off with much simpler inserts/removes.
+ * Examples for usage of these macros can be found in fs/ninode.c.
+ * To access the last element in constant time, simply use
+ * anchor->{something}_prev.
+ */
+
+#define DEF_GENERIC_INSERT(CHANGE,PREFIX,NAME,TYPE,NEXT,PREV) \
+static inline void PREFIX##NAME(TYPE ** anchor, TYPE * elem)\
+{\
+ TYPE * oldfirst = *anchor;\
+ if(!oldfirst) {\
+ elem->NEXT = elem->PREV = *anchor = elem;\
+ } else {\
+ elem->PREV = oldfirst->PREV;\
+ elem->NEXT = oldfirst;\
+ oldfirst->PREV->NEXT = elem;\
+ oldfirst->PREV = elem;\
+ if(CHANGE)\
+ *anchor = elem;\
+ }\
+}
+
+/* insert_* is always at the first position */
+#define DEF_INSERT(NAME,TYPE,NEXT,PREV) \
+ DEF_GENERIC_INSERT(1,insert_,NAME,TYPE,NEXT,PREV)
+
+/* append_* is always at the tail */
+#define DEF_APPEND(NAME,TYPE,NEXT,PREV) \
+ DEF_GENERIC_INSERT(0,append_,NAME,TYPE,NEXT,PREV)
+
+/* use this to insert _before_ oldelem somewhere in the middle of the list.
+ * the list must not be empty, and oldelem must be already a member.*/
+#define DEF_INSERT_MIDDLE(NAME,TYPE) \
+static inline void insert_middle_##NAME(TYPE ** anchor, TYPE * oldelem, TYPE * elem)\
+{\
+ int status = (oldelem == *anchor);\
+ insert_##NAME(&oldelem, elem);\
+ if(status)\
+ *anchor = oldelem;\
+}
+
+/* remove can be done with any element in the list */
+#define DEF_REMOVE(NAME,TYPE,NEXT,PREV) \
+static inline void remove_##NAME(TYPE ** anchor, TYPE * elem)\
+{\
+ TYPE * next = elem->NEXT;\
+ if(next == elem) {\
+ *anchor = NULL;\
+ } else {\
+ TYPE * prev = elem->PREV;\
+ prev->NEXT = next;\
+ next->PREV = prev;\
+ elem->NEXT = elem->PREV = NULL;/*leave this during debugging*/\
+ if(*anchor == elem)\
+ *anchor = next;\
+ }\
+}
+
+
+/* According to ideas from David S. Miller, here is a slightly
+ * more efficient plug-in compatible version using non-cyclic lists,
+ * but allowing neither backward traversals nor constant time access
+ * to the last element.
+ * Note that although the interface is the same, the PPREV pointer must be
+ * declared doubly indirect and the test for end-of-list is different. */
+
+/* as above, this inserts always at the head */
+#define DEF_LIN_INSERT(NAME,TYPE,NEXT,PPREV) \
+static inline void insert_##NAME(TYPE ** anchor, TYPE * elem)\
+{\
+ TYPE * first;\
+ if((elem->NEXT = first = *anchor))\
+ first->PPREV = &elem->NEXT;\
+ *anchor = elem;\
+ elem->PPREV = anchor;\
+}
+
+/* as above, this works with any list element */
+#define DEF_LIN_REMOVE(NAME,TYPE,NEXT,PPREV) \
+static inline void remove_##NAME(TYPE ** anchor, TYPE * elem)\
+{\
+ TYPE * pprev;\
+ if((pprev = elem->PPREV)) {\
+ TYPE * next;\
+ if((next = elem->NEXT))\
+ next->PPREV = pprev;\
+ *pprev = next;\
+ elem->PPREV = elem->NEXT = NULL; /*leave this for debugging*/\
+ }\
+}
+
+#endif
diff --git a/pfinet/linux-src/include/linux/dmascc.h b/pfinet/linux-src/include/linux/dmascc.h
new file mode 100644
index 00000000..a00ef803
--- /dev/null
+++ b/pfinet/linux-src/include/linux/dmascc.h
@@ -0,0 +1,42 @@
+/*
+ * $Id: dmascc.h,v 1.1 1997/12/01 10:44:55 oe1kib Exp $
+ *
+ * Driver for high-speed SCC boards (those with DMA support)
+ * Copyright (C) 1997 Klaus Kudielka
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Ioctls */
+#define SIOCGSCCPARAM SIOCDEVPRIVATE
+#define SIOCSSCCPARAM (SIOCDEVPRIVATE+1)
+
+/* Frequency of timer 0 */
+#define TMR_0_HZ 25600
+
+/* Configurable parameters */
+struct scc_param {
+ int pclk_hz; /* frequency of BRG input (read-only - don't change) */
+ int brg_tc; /* baud rate generator terminal count - BRG disabled if < 0 */
+ int nrzi; /* 0 (nrz), 1 (nrzi) */
+ int clocks; /* see documentation */
+ int txdelay; /* [1/TMR_0_HZ] */
+ int txtime; /* [1/HZ] */
+ int sqdelay; /* [1/TMR_0_HZ] */
+ int waittime; /* [1/TMR_0_HZ] */
+ int slottime; /* [1/TMR_0_HZ] */
+ int persist; /* 0 ... 255 */
+ int dma; /* 1, 3 */
+};
diff --git a/pfinet/linux-src/include/linux/dtlk.h b/pfinet/linux-src/include/linux/dtlk.h
new file mode 100644
index 00000000..07a6b82b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/dtlk.h
@@ -0,0 +1,104 @@
+#if 0
+
+#define TRACE_TXT(text) \
+ { \
+ if(dtlk_trace) \
+ { \
+ console_print(text); \
+ console_print("\n"); \
+ } \
+ }
+
+#define TRACE_CHR(chr) \
+ { \
+ if(dtlk_trace) \
+ console_print(chr); \
+ } \
+
+#endif
+
+#define DTLK_MINOR 0
+#define DTLK_IO_EXTENT 0x02
+
+ /* ioctl's use magic number of 0xa3 */
+#define DTLK_INTERROGATE 0xa390 /* get settings from the DoubleTalk */
+#define DTLK_STATUS 0xa391 /* get status from the DoubleTalk */
+
+
+#define DTLK_CLEAR 0x18 /* stops speech */
+
+#define DTLK_MAX_RETRIES (loops_per_sec/10000)
+
+ /* TTS Port Status Flags */
+#define TTS_READABLE 0x80 /* mask for bit which is nonzero if a
+ byte can be read from the TTS port */
+#define TTS_SPEAKING 0x40 /* mask for SYNC bit, which is nonzero
+ while DoubleTalk is producing
+ output with TTS, PCM or CVSD
+ synthesizers or tone generators
+ (that is, all but LPC) */
+#define TTS_SPEAKING2 0x20 /* mask for SYNC2 bit,
+ which falls to zero up to 0.4 sec
+ before speech stops */
+#define TTS_WRITABLE 0x10 /* mask for RDY bit, which when set to
+ 1, indicates the TTS port is ready
+ to accept a byte of data. The RDY
+ bit goes zero 2-3 usec after
+ writing, and goes 1 again 180-190
+ usec later. */
+#define TTS_ALMOST_FULL 0x08 /* mask for AF bit: When set to 1,
+ indicates that less than 300 free
+ bytes are available in the TTS
+ input buffer. AF is always 0 in the
+ PCM, TGN and CVSD modes. */
+#define TTS_ALMOST_EMPTY 0x04 /* mask for AE bit: When set to 1,
+ indicates that less than 300 bytes
+ of data remain in DoubleTalk's
+ input (TTS or PCM) buffer. AE is
+ always 1 in the TGN and CVSD
+ modes. */
+
+ /* LPC speak commands */
+#define LPC_5220_NORMAL 0x60 /* 5220 format decoding table, normal rate */
+#define LPC_5220_FAST 0x64 /* 5220 format decoding table, fast rate */
+#define LPC_D6_NORMAL 0x20 /* D6 format decoding table, normal rate */
+#define LPC_D6_FAST 0x24 /* D6 format decoding table, fast rate */
+
+ /* LPC Port Status Flags (valid only after one of the LPC
+ speak commands) */
+#define LPC_SPEAKING 0x80 /* mask for TS bit: When set to 1,
+ indicates the LPC synthesizer is
+ producing speech.*/
+#define LPC_BUFFER_LOW 0x40 /* mask for BL bit: When set to 1,
+ indicates that the hardware LPC
+ data buffer has less than 30 bytes
+ remaining. (Total internal buffer
+ size = 4096 bytes.) */
+#define LPC_BUFFER_EMPTY 0x20 /* mask for BE bit: When set to 1,
+ indicates that the LPC data buffer
+ ran out of data (error condition if
+ TS is also 1). */
+
+ /* data returned by Interrogate command */
+struct dtlk_settings
+{
+ unsigned short serial_number; /* 0-7Fh:0-7Fh */
+ unsigned char rom_version[24]; /* null terminated string */
+ unsigned char mode; /* 0=Character; 1=Phoneme; 2=Text */
+ unsigned char punc_level; /* nB; 0-7 */
+ unsigned char formant_freq; /* nF; 0-9 */
+ unsigned char pitch; /* nP; 0-99 */
+ unsigned char speed; /* nS; 0-9 */
+ unsigned char volume; /* nV; 0-9 */
+ unsigned char tone; /* nX; 0-2 */
+ unsigned char expression; /* nE; 0-9 */
+ unsigned char ext_dict_loaded; /* 1=exception dictionary loaded */
+ unsigned char ext_dict_status; /* 1=exception dictionary enabled */
+ unsigned char free_ram; /* # pages (truncated) remaining for
+ text buffer */
+ unsigned char articulation; /* nA; 0-9 */
+ unsigned char reverb; /* nR; 0-9 */
+ unsigned char eob; /* 7Fh value indicating end of
+ parameter block */
+ unsigned char has_indexing; /* nonzero if indexing is implemented */
+};
diff --git a/pfinet/linux-src/include/linux/efs_dir.h b/pfinet/linux-src/include/linux/efs_dir.h
new file mode 100644
index 00000000..2123a4e2
--- /dev/null
+++ b/pfinet/linux-src/include/linux/efs_dir.h
@@ -0,0 +1,41 @@
+/*
+ * efs_dir.h
+ *
+ * Copyright (c) 1999 Al Smith
+ */
+
+#ifndef __EFS_DIR_H__
+#define __EFS_DIR_H__
+
+#define EFS_DIRBSIZE_BITS EFS_BLOCKSIZE_BITS
+#define EFS_DIRBSIZE (1 << EFS_DIRBSIZE_BITS)
+
+struct efs_dentry {
+ unsigned int inode;
+ unsigned char namelen;
+ char name[3];
+};
+
+#define EFS_DENTSIZE (sizeof(struct efs_dentry) - 3 + 1)
+#define EFS_MAXNAMELEN ((1 << (sizeof(char) * 8)) - 1)
+
+#define EFS_DIRBLK_HEADERSIZE 4
+#define EFS_DIRBLK_MAGIC 0xbeef /* moo */
+
+struct efs_dir {
+ unsigned short magic;
+ unsigned char firstused;
+ unsigned char slots;
+
+ unsigned char space[EFS_DIRBSIZE - EFS_DIRBLK_HEADERSIZE];
+};
+
+#define EFS_MAXENTS \
+ ((EFS_DIRBSIZE - EFS_DIRBLK_HEADERSIZE) / \
+ (EFS_DENTSIZE + sizeof(char)))
+
+#define EFS_SLOTAT(dir, slot) EFS_REALOFF((dir)->space[slot])
+
+#define EFS_REALOFF(offset) ((offset << 1))
+
+#endif /* __EFS_DIR_H__ */
diff --git a/pfinet/linux-src/include/linux/efs_fs.h b/pfinet/linux-src/include/linux/efs_fs.h
new file mode 100644
index 00000000..fef6cb99
--- /dev/null
+++ b/pfinet/linux-src/include/linux/efs_fs.h
@@ -0,0 +1,66 @@
+/*
+ * efs_fs.h
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from work (c) 1995,1996 Christian Vogelgsang.
+ */
+
+#ifndef __EFS_FS_H__
+#define __EFS_FS_H__
+
+#define EFS_VERSION "1.0b"
+
+static const char cprt[] = "EFS: "EFS_VERSION" - (c) 1999 Al Smith <Al.Smith@aeschi.ch.eu.org>";
+
+#include <asm/uaccess.h>
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#if LINUX_VERSION_CODE < 0x20200
+#error This code is only for linux-2.2 and later.
+#endif
+
+/* 1 block is 512 bytes */
+#define EFS_BLOCKSIZE_BITS 9
+#define EFS_BLOCKSIZE (1 << EFS_BLOCKSIZE_BITS)
+
+#include <linux/efs_fs_i.h>
+#include <linux/efs_dir.h>
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifdef _EFS_USE_GENERIC
+#define INODE_INFO(i) (struct efs_inode_info *) &((i)->u.generic_ip)
+#define SUPER_INFO(s) (struct efs_sb_info *) &((s)->u.generic_sbp)
+#else
+#define INODE_INFO(i) &((i)->u.efs_i)
+#define SUPER_INFO(s) &((s)->u.efs_sb)
+#endif
+
+extern struct inode_operations efs_dir_inode_operations;
+extern struct inode_operations efs_file_inode_operations;
+extern struct inode_operations efs_symlink_inode_operations;
+
+extern int init_module(void);
+extern void cleanup_module(void);
+extern struct super_block *efs_read_super(struct super_block *, void *, int);
+extern void efs_put_super(struct super_block *);
+extern int efs_statfs(struct super_block *, struct statfs *, int);
+
+extern void efs_read_inode(struct inode *);
+extern efs_block_t efs_map_block(struct inode *, efs_block_t);
+
+extern struct dentry *efs_lookup(struct inode *, struct dentry *);
+extern int efs_bmap(struct inode *, int);
+
+extern int init_efs_fs(void);
+
+#endif /* __EFS_FS_H__ */
diff --git a/pfinet/linux-src/include/linux/efs_fs_i.h b/pfinet/linux-src/include/linux/efs_fs_i.h
new file mode 100644
index 00000000..e8e40703
--- /dev/null
+++ b/pfinet/linux-src/include/linux/efs_fs_i.h
@@ -0,0 +1,67 @@
+/*
+ * efs_fs_i.h
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from IRIX header files (c) 1988 Silicon Graphics
+ */
+
+#ifndef __EFS_FS_I_H__
+#define __EFS_FS_I_H__
+
+typedef int32_t efs_block_t;
+typedef uint32_t efs_ino_t;
+
+#define EFS_DIRECTEXTENTS 12
+
+/*
+ * layout of an extent, in memory and on disk. 8 bytes exactly.
+ */
+typedef union extent_u {
+ unsigned char raw[8];
+ struct extent_s {
+ unsigned int ex_magic:8; /* magic # (zero) */
+ unsigned int ex_bn:24; /* basic block */
+ unsigned int ex_length:8; /* numblocks in this extent */
+ unsigned int ex_offset:24; /* logical offset into file */
+ } cooked;
+} efs_extent;
+
+typedef struct edevs {
+ short odev;
+ short dev_filler; /* force ndev to start */
+ unsigned int ndev; /* on a 32-bit boundary */
+} efs_devs;
+
+/*
+ * extent based filesystem inode as it appears on disk. The efs inode
+ * is exactly 128 bytes long.
+ */
+struct efs_dinode {
+ u_short di_mode; /* mode and type of file */
+ short di_nlink; /* number of links to file */
+ u_short di_uid; /* owner's user id */
+ u_short di_gid; /* owner's group id */
+ int32_t di_size; /* number of bytes in file */
+ int32_t di_atime; /* time last accessed */
+ int32_t di_mtime; /* time last modified */
+ int32_t di_ctime; /* time created */
+ uint32_t di_gen; /* generation number */
+ short di_numextents; /* # of extents */
+ u_char di_version; /* version of inode */
+ u_char di_spare; /* spare - used by AFS */
+ union di_addr {
+ efs_extent di_extents[EFS_DIRECTEXTENTS];
+ efs_devs di_dev; /* device for IFCHR/IFBLK */
+ } di_u;
+};
+
+/* efs inode storage in memory */
+struct efs_inode_info {
+ int numextents;
+ int lastextent;
+
+ efs_extent extents[EFS_DIRECTEXTENTS];
+};
+
+#endif /* __EFS_FS_I_H__ */
diff --git a/pfinet/linux-src/include/linux/efs_fs_sb.h b/pfinet/linux-src/include/linux/efs_fs_sb.h
new file mode 100644
index 00000000..cddc3bc0
--- /dev/null
+++ b/pfinet/linux-src/include/linux/efs_fs_sb.h
@@ -0,0 +1,62 @@
+/*
+ * efs_fs_sb.h
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from IRIX header files (c) 1988 Silicon Graphics
+ */
+
+#ifndef __EFS_FS_SB_H__
+#define __EFS_FS_SB_H__
+
+/* statfs() magic number for EFS */
+#define EFS_SUPER_MAGIC 0x414A53
+
+/* EFS superblock magic numbers */
+#define EFS_MAGIC 0x072959
+#define EFS_NEWMAGIC 0x07295a
+
+#define IS_EFS_MAGIC(x) ((x == EFS_MAGIC) || (x == EFS_NEWMAGIC))
+
+#define EFS_SUPER 1
+#define EFS_ROOTINODE 2
+
+/* efs superblock on disk */
+struct efs_super {
+ int32_t fs_size; /* size of filesystem, in sectors */
+ int32_t fs_firstcg; /* bb offset to first cg */
+ int32_t fs_cgfsize; /* size of cylinder group in bb's */
+ short fs_cgisize; /* bb's of inodes per cylinder group */
+ short fs_sectors; /* sectors per track */
+ short fs_heads; /* heads per cylinder */
+ short fs_ncg; /* # of cylinder groups in filesystem */
+ short fs_dirty; /* fs needs to be fsck'd */
+ short fs_filler; /* force fs_time to start 2bytes later*/
+ int32_t fs_time; /* last super-block update */
+ int32_t fs_magic; /* magic number */
+ char fs_fname[6]; /* file system name */
+ char fs_fpack[6]; /* file system pack name */
+ int32_t fs_bmsize; /* size of bitmap in bytes */
+ int32_t fs_tfree; /* total free data blocks */
+ int32_t fs_tinode; /* total free inodes */
+ int32_t fs_bmblock; /* bitmap location. */
+ int32_t fs_replsb; /* Location of replicated superblock. */
+ int32_t fs_lastialloc; /* last allocated inode */
+ char fs_spare[20]; /* space for expansion - MUST BE ZERO */
+ int32_t fs_checksum; /* checksum of volume portion of fs */
+};
+
+/* efs superblock information in memory */
+struct efs_sb_info {
+ int32_t fs_magic; /* superblock magic number */
+ int32_t fs_start; /* first block of filesystem */
+ int32_t first_block; /* first data block in filesystem */
+ int32_t total_blocks; /* total number of blocks in filesystem */
+ int32_t group_size; /* # of blocks a group consists of */
+ int32_t data_free; /* # of free data blocks */
+ int32_t inode_free; /* # of free inodes */
+ short inode_blocks; /* # of blocks used for inodes in every grp */
+ short total_groups; /* # of groups */
+};
+
+#endif /* __EFS_FS_SB_H__ */
diff --git a/pfinet/linux-src/include/linux/efs_vh.h b/pfinet/linux-src/include/linux/efs_vh.h
new file mode 100644
index 00000000..6b22ac5a
--- /dev/null
+++ b/pfinet/linux-src/include/linux/efs_vh.h
@@ -0,0 +1,69 @@
+/*
+ * efs_vh.h
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from IRIX header files (c) 1985 MIPS Computer Systems, Inc.
+ */
+
+#ifndef __EFS_VH_H__
+#define __EFS_VH_H__
+
+#define VHMAGIC 0xbe5a941 /* volume header magic number */
+#define NPARTAB 16 /* 16 unix partitions */
+#define NVDIR 15 /* max of 15 directory entries */
+#define BFNAMESIZE 16 /* max 16 chars in boot file name */
+#define VDNAMESIZE 8
+
+struct volume_directory {
+ char vd_name[VDNAMESIZE]; /* name */
+ int vd_lbn; /* logical block number */
+ int vd_nbytes; /* file length in bytes */
+};
+
+struct partition_table { /* one per logical partition */
+ int pt_nblks; /* # of logical blks in partition */
+ int pt_firstlbn; /* first lbn of partition */
+ int pt_type; /* use of partition */
+};
+
+struct volume_header {
+ int vh_magic; /* identifies volume header */
+ short vh_rootpt; /* root partition number */
+ short vh_swappt; /* swap partition number */
+ char vh_bootfile[BFNAMESIZE]; /* name of file to boot */
+ char pad[48]; /* device param space */
+ struct volume_directory vh_vd[NVDIR]; /* other vol hdr contents */
+ struct partition_table vh_pt[NPARTAB]; /* device partition layout */
+ int vh_csum; /* volume header checksum */
+ int vh_fill; /* fill out to 512 bytes */
+};
+
+/* partition type sysv is used for EFS format CD-ROM partitions */
+#define SGI_SYSV 0x05
+#define SGI_EFS 0x07
+#define IS_EFS(x) (((x) == SGI_EFS) || ((x) == SGI_SYSV))
+
+struct pt_types {
+ int pt_type;
+ char *pt_name;
+} sgi_pt_types[] = {
+ {0x00, "SGI vh"},
+ {0x01, "SGI trkrepl"},
+ {0x02, "SGI secrepl"},
+ {0x03, "SGI raw"},
+ {0x04, "SGI bsd"},
+ {SGI_SYSV, "SGI sysv"},
+ {0x06, "SGI vol"},
+ {SGI_EFS, "SGI efs"},
+ {0x08, "SGI lv"},
+ {0x09, "SGI rlv"},
+ {0x0A, "SGI xfs"},
+ {0x0B, "SGI xfslog"},
+ {0x0C, "SGI xlv"},
+ {0x82, "Linux swap"},
+ {0x83, "Linux native"},
+ {0, NULL}
+};
+
+#endif /* __EFS_VH_H__ */
diff --git a/pfinet/linux-src/include/linux/elf.h b/pfinet/linux-src/include/linux/elf.h
new file mode 100644
index 00000000..f1b9c912
--- /dev/null
+++ b/pfinet/linux-src/include/linux/elf.h
@@ -0,0 +1,601 @@
+#ifndef _LINUX_ELF_H
+#define _LINUX_ELF_H
+
+#include <linux/types.h>
+#include <asm/elf.h>
+
+/* 32-bit ELF base types. */
+typedef __u32 Elf32_Addr;
+typedef __u16 Elf32_Half;
+typedef __u32 Elf32_Off;
+typedef __s32 Elf32_Sword;
+typedef __u32 Elf32_Word;
+
+/* 64-bit ELF base types. */
+typedef __u64 Elf64_Addr;
+typedef __u16 Elf64_Half;
+typedef __s16 Elf64_SHalf;
+typedef __u64 Elf64_Off;
+typedef __s64 Elf64_Sword;
+typedef __u64 Elf64_Word;
+
+/* These constants are for the segment types stored in the image headers */
+#define PT_NULL 0
+#define PT_LOAD 1
+#define PT_DYNAMIC 2
+#define PT_INTERP 3
+#define PT_NOTE 4
+#define PT_SHLIB 5
+#define PT_PHDR 6
+#define PT_LOPROC 0x70000000
+#define PT_HIPROC 0x7fffffff
+#define PT_MIPS_REGINFO 0x70000000
+
+/* Flags in the e_flags field of the header */
+#define EF_MIPS_NOREORDER 0x00000001
+#define EF_MIPS_PIC 0x00000002
+#define EF_MIPS_CPIC 0x00000004
+#define EF_MIPS_ARCH 0xf0000000
+
+/* These constants define the different elf file types */
+#define ET_NONE 0
+#define ET_REL 1
+#define ET_EXEC 2
+#define ET_DYN 3
+#define ET_CORE 4
+#define ET_LOPROC 0xff00
+#define ET_HIPROC 0xffff
+
+/* These constants define the various ELF target machines */
+#define EM_NONE 0
+#define EM_M32 1
+#define EM_SPARC 2
+#define EM_386 3
+#define EM_68K 4
+#define EM_88K 5
+#define EM_486 6 /* Perhaps disused */
+#define EM_860 7
+
+#define EM_MIPS 8 /* MIPS R3000 (officially, big-endian only) */
+
+#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */
+
+#define EM_PARISC 15 /* HPPA */
+
+#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */
+
+#define EM_PPC 20 /* PowerPC */
+
+#define EM_SPARCV9 43 /* SPARC v9 64-bit */
+
+/*
+ * This is an interim value that we will use until the committee comes
+ * up with a final number.
+ */
+#define EM_ALPHA 0x9026
+
+/*
+ * This is an interim value for S390 architecture
+ */
+#define EM_S390 0xA390
+
+/* This is the info that is needed to parse the dynamic section of the file */
+#define DT_NULL 0
+#define DT_NEEDED 1
+#define DT_PLTRELSZ 2
+#define DT_PLTGOT 3
+#define DT_HASH 4
+#define DT_STRTAB 5
+#define DT_SYMTAB 6
+#define DT_RELA 7
+#define DT_RELASZ 8
+#define DT_RELAENT 9
+#define DT_STRSZ 10
+#define DT_SYMENT 11
+#define DT_INIT 12
+#define DT_FINI 13
+#define DT_SONAME 14
+#define DT_RPATH 15
+#define DT_SYMBOLIC 16
+#define DT_REL 17
+#define DT_RELSZ 18
+#define DT_RELENT 19
+#define DT_PLTREL 20
+#define DT_DEBUG 21
+#define DT_TEXTREL 22
+#define DT_JMPREL 23
+#define DT_LOPROC 0x70000000
+#define DT_HIPROC 0x7fffffff
+#define DT_MIPS_RLD_VERSION 0x70000001
+#define DT_MIPS_TIME_STAMP 0x70000002
+#define DT_MIPS_ICHECKSUM 0x70000003
+#define DT_MIPS_IVERSION 0x70000004
+#define DT_MIPS_FLAGS 0x70000005
+ #define RHF_NONE 0
+ #define RHF_HARDWAY 1
+ #define RHF_NOTPOT 2
+#define DT_MIPS_BASE_ADDRESS 0x70000006
+#define DT_MIPS_CONFLICT 0x70000008
+#define DT_MIPS_LIBLIST 0x70000009
+#define DT_MIPS_LOCAL_GOTNO 0x7000000a
+#define DT_MIPS_CONFLICTNO 0x7000000b
+#define DT_MIPS_LIBLISTNO 0x70000010
+#define DT_MIPS_SYMTABNO 0x70000011
+#define DT_MIPS_UNREFEXTNO 0x70000012
+#define DT_MIPS_GOTSYM 0x70000013
+#define DT_MIPS_HIPAGENO 0x70000014
+#define DT_MIPS_RLD_MAP 0x70000016
+
+/* This info is needed when parsing the symbol table */
+#define STB_LOCAL 0
+#define STB_GLOBAL 1
+#define STB_WEAK 2
+
+#define STT_NOTYPE 0
+#define STT_OBJECT 1
+#define STT_FUNC 2
+#define STT_SECTION 3
+#define STT_FILE 4
+
+#define ELF32_ST_BIND(x) ((x) >> 4)
+#define ELF32_ST_TYPE(x) (((unsigned int) x) & 0xf)
+
+/* Symbolic values for the entries in the auxiliary table
+ put on the initial stack */
+#define AT_NULL 0 /* end of vector */
+#define AT_IGNORE 1 /* entry should be ignored */
+#define AT_EXECFD 2 /* file descriptor of program */
+#define AT_PHDR 3 /* program headers for program */
+#define AT_PHENT 4 /* size of program header entry */
+#define AT_PHNUM 5 /* number of program headers */
+#define AT_PAGESZ 6 /* system page size */
+#define AT_BASE 7 /* base address of interpreter */
+#define AT_FLAGS 8 /* flags */
+#define AT_ENTRY 9 /* entry point of program */
+#define AT_NOTELF 10 /* program is not ELF */
+#define AT_UID 11 /* real uid */
+#define AT_EUID 12 /* effective uid */
+#define AT_GID 13 /* real gid */
+#define AT_EGID 14 /* effective gid */
+#define AT_PLATFORM 15 /* string identifying CPU for optimizations */
+#define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */
+
+typedef struct dynamic{
+ Elf32_Sword d_tag;
+ union{
+ Elf32_Sword d_val;
+ Elf32_Addr d_ptr;
+ } d_un;
+} Elf32_Dyn;
+
+typedef struct {
+ Elf64_Word d_tag; /* entry tag value */
+ union {
+ Elf64_Word d_val;
+ Elf64_Word d_ptr;
+ } d_un;
+} Elf64_Dyn;
+
+/* The following are used with relocations */
+#define ELF32_R_SYM(x) ((x) >> 8)
+#define ELF32_R_TYPE(x) ((x) & 0xff)
+
+#define R_386_NONE 0
+#define R_386_32 1
+#define R_386_PC32 2
+#define R_386_GOT32 3
+#define R_386_PLT32 4
+#define R_386_COPY 5
+#define R_386_GLOB_DAT 6
+#define R_386_JMP_SLOT 7
+#define R_386_RELATIVE 8
+#define R_386_GOTOFF 9
+#define R_386_GOTPC 10
+#define R_386_NUM 11
+
+#define R_MIPS_NONE 0
+#define R_MIPS_16 1
+#define R_MIPS_32 2
+#define R_MIPS_REL32 3
+#define R_MIPS_26 4
+#define R_MIPS_HI16 5
+#define R_MIPS_LO16 6
+#define R_MIPS_GPREL16 7
+#define R_MIPS_LITERAL 8
+#define R_MIPS_GOT16 9
+#define R_MIPS_PC16 10
+#define R_MIPS_CALL16 11
+#define R_MIPS_GPREL32 12
+/* The remaining relocs are defined on Irix, although they are not
+ in the MIPS ELF ABI. */
+#define R_MIPS_UNUSED1 13
+#define R_MIPS_UNUSED2 14
+#define R_MIPS_UNUSED3 15
+#define R_MIPS_SHIFT5 16
+#define R_MIPS_SHIFT6 17
+#define R_MIPS_64 18
+#define R_MIPS_GOT_DISP 19
+#define R_MIPS_GOT_PAGE 20
+#define R_MIPS_GOT_OFST 21
+/*
+ * The following two relocation types are specified in the the MIPS ABI
+ * conformance guide version 1.2 but not yet in the psABI.
+ */
+#define R_MIPS_GOTHI16 22
+#define R_MIPS_GOTLO16 23
+#define R_MIPS_SUB 24
+#define R_MIPS_INSERT_A 25
+#define R_MIPS_INSERT_B 26
+#define R_MIPS_DELETE 27
+#define R_MIPS_HIGHER 28
+#define R_MIPS_HIGHEST 29
+/*
+ * The following two relocation types are specified in the the MIPS ABI
+ * conformance guide version 1.2 but not yet in the psABI.
+ */
+#define R_MIPS_CALLHI16 30
+#define R_MIPS_CALLLO16 31
+/*
+ * This range is reserved for vendor specific relocations.
+ */
+#define R_MIPS_LOVENDOR 100
+#define R_MIPS_HIVENDOR 127
+
+
+/*
+ * Sparc ELF relocation types
+ */
+#define R_SPARC_NONE 0
+#define R_SPARC_8 1
+#define R_SPARC_16 2
+#define R_SPARC_32 3
+#define R_SPARC_DISP8 4
+#define R_SPARC_DISP16 5
+#define R_SPARC_DISP32 6
+#define R_SPARC_WDISP30 7
+#define R_SPARC_WDISP22 8
+#define R_SPARC_HI22 9
+#define R_SPARC_22 10
+#define R_SPARC_13 11
+#define R_SPARC_LO10 12
+#define R_SPARC_GOT10 13
+#define R_SPARC_GOT13 14
+#define R_SPARC_GOT22 15
+#define R_SPARC_PC10 16
+#define R_SPARC_PC22 17
+#define R_SPARC_WPLT30 18
+#define R_SPARC_COPY 19
+#define R_SPARC_GLOB_DAT 20
+#define R_SPARC_JMP_SLOT 21
+#define R_SPARC_RELATIVE 22
+#define R_SPARC_UA32 23
+#define R_SPARC_PLT32 24
+#define R_SPARC_HIPLT22 25
+#define R_SPARC_LOPLT10 26
+#define R_SPARC_PCPLT32 27
+#define R_SPARC_PCPLT22 28
+#define R_SPARC_PCPLT10 29
+#define R_SPARC_10 30
+#define R_SPARC_11 31
+#define R_SPARC_WDISP16 40
+#define R_SPARC_WDISP19 41
+#define R_SPARC_7 43
+#define R_SPARC_5 44
+#define R_SPARC_6 45
+
+/* Bits present in AT_HWCAP, primarily for Sparc32. */
+
+#define HWCAP_SPARC_FLUSH 1 /* CPU supports flush instruction. */
+#define HWCAP_SPARC_STBAR 2
+#define HWCAP_SPARC_SWAP 4
+#define HWCAP_SPARC_MULDIV 8
+#define HWCAP_SPARC_V9 16
+
+
+/*
+ * 68k ELF relocation types
+ */
+#define R_68K_NONE 0
+#define R_68K_32 1
+#define R_68K_16 2
+#define R_68K_8 3
+#define R_68K_PC32 4
+#define R_68K_PC16 5
+#define R_68K_PC8 6
+#define R_68K_GOT32 7
+#define R_68K_GOT16 8
+#define R_68K_GOT8 9
+#define R_68K_GOT32O 10
+#define R_68K_GOT16O 11
+#define R_68K_GOT8O 12
+#define R_68K_PLT32 13
+#define R_68K_PLT16 14
+#define R_68K_PLT8 15
+#define R_68K_PLT32O 16
+#define R_68K_PLT16O 17
+#define R_68K_PLT8O 18
+#define R_68K_COPY 19
+#define R_68K_GLOB_DAT 20
+#define R_68K_JMP_SLOT 21
+#define R_68K_RELATIVE 22
+
+/*
+ * Alpha ELF relocation types
+ */
+#define R_ALPHA_NONE 0 /* No reloc */
+#define R_ALPHA_REFLONG 1 /* Direct 32 bit */
+#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */
+#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */
+#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */
+#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */
+#define R_ALPHA_GPDISP 6 /* Add displacement to GP */
+#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */
+#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */
+#define R_ALPHA_SREL16 9 /* PC relative 16 bit */
+#define R_ALPHA_SREL32 10 /* PC relative 32 bit */
+#define R_ALPHA_SREL64 11 /* PC relative 64 bit */
+#define R_ALPHA_OP_PUSH 12 /* OP stack push */
+#define R_ALPHA_OP_STORE 13 /* OP stack pop and store */
+#define R_ALPHA_OP_PSUB 14 /* OP stack subtract */
+#define R_ALPHA_OP_PRSHIFT 15 /* OP stack right shift */
+#define R_ALPHA_GPVALUE 16
+#define R_ALPHA_GPRELHIGH 17
+#define R_ALPHA_GPRELLOW 18
+#define R_ALPHA_IMMED_GP_16 19
+#define R_ALPHA_IMMED_GP_HI32 20
+#define R_ALPHA_IMMED_SCN_HI32 21
+#define R_ALPHA_IMMED_BR_HI32 22
+#define R_ALPHA_IMMED_LO32 23
+#define R_ALPHA_COPY 24 /* Copy symbol at runtime */
+#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */
+#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */
+#define R_ALPHA_RELATIVE 27 /* Adjust by program base */
+
+/* Legal values for e_flags field of Elf64_Ehdr. */
+
+#define EF_ALPHA_32BIT 1 /* All addresses are below 2GB */
+
+
+typedef struct elf32_rel {
+ Elf32_Addr r_offset;
+ Elf32_Word r_info;
+} Elf32_Rel;
+
+typedef struct elf64_rel {
+ Elf64_Addr r_offset; /* Location at which to apply the action */
+ Elf64_Word r_info; /* index and type of relocation */
+} Elf64_Rel;
+
+typedef struct elf32_rela{
+ Elf32_Addr r_offset;
+ Elf32_Word r_info;
+ Elf32_Sword r_addend;
+} Elf32_Rela;
+
+typedef struct elf64_rela {
+ Elf64_Addr r_offset; /* Location at which to apply the action */
+ Elf64_Word r_info; /* index and type of relocation */
+ Elf64_Word r_addend; /* Constant addend used to compute value */
+} Elf64_Rela;
+
+typedef struct elf32_sym{
+ Elf32_Word st_name;
+ Elf32_Addr st_value;
+ Elf32_Word st_size;
+ unsigned char st_info;
+ unsigned char st_other;
+ Elf32_Half st_shndx;
+} Elf32_Sym;
+
+typedef struct elf64_sym {
+ Elf32_Word st_name; /* Symbol name, index in string tbl (yes, Elf32) */
+ unsigned char st_info; /* Type and binding attributes */
+ unsigned char st_other; /* No defined meaning, 0 */
+ Elf64_Half st_shndx; /* Associated section index */
+ Elf64_Addr st_value; /* Value of the symbol */
+ Elf64_Word st_size; /* Associated symbol size */
+} Elf64_Sym;
+
+
+#define EI_NIDENT 16
+
+typedef struct elf32_hdr{
+ unsigned char e_ident[EI_NIDENT];
+ Elf32_Half e_type;
+ Elf32_Half e_machine;
+ Elf32_Word e_version;
+ Elf32_Addr e_entry; /* Entry point */
+ Elf32_Off e_phoff;
+ Elf32_Off e_shoff;
+ Elf32_Word e_flags;
+ Elf32_Half e_ehsize;
+ Elf32_Half e_phentsize;
+ Elf32_Half e_phnum;
+ Elf32_Half e_shentsize;
+ Elf32_Half e_shnum;
+ Elf32_Half e_shstrndx;
+} Elf32_Ehdr;
+
+typedef struct elf64_hdr {
+ unsigned char e_ident[16]; /* ELF "magic number" */
+ Elf64_SHalf e_type;
+ Elf64_Half e_machine;
+ __s32 e_version;
+ Elf64_Addr e_entry; /* Entry point virtual address */
+ Elf64_Off e_phoff; /* Program header table file offset */
+ Elf64_Off e_shoff; /* Section header table file offset */
+ __s32 e_flags;
+ Elf64_SHalf e_ehsize;
+ Elf64_SHalf e_phentsize;
+ Elf64_SHalf e_phnum;
+ Elf64_SHalf e_shentsize;
+ Elf64_SHalf e_shnum;
+ Elf64_SHalf e_shstrndx;
+} Elf64_Ehdr;
+
+/* These constants define the permissions on sections in the program
+ header, p_flags. */
+#define PF_R 0x4
+#define PF_W 0x2
+#define PF_X 0x1
+
+typedef struct elf32_phdr{
+ Elf32_Word p_type;
+ Elf32_Off p_offset;
+ Elf32_Addr p_vaddr;
+ Elf32_Addr p_paddr;
+ Elf32_Word p_filesz;
+ Elf32_Word p_memsz;
+ Elf32_Word p_flags;
+ Elf32_Word p_align;
+} Elf32_Phdr;
+
+typedef struct elf64_phdr {
+ __s32 p_type;
+ __s32 p_flags;
+ Elf64_Off p_offset; /* Segment file offset */
+ Elf64_Addr p_vaddr; /* Segment virtual address */
+ Elf64_Addr p_paddr; /* Segment physical address */
+ Elf64_Word p_filesz; /* Segment size in file */
+ Elf64_Word p_memsz; /* Segment size in memory */
+ Elf64_Word p_align; /* Segment alignment, file & memory */
+} Elf64_Phdr;
+
+/* sh_type */
+#define SHT_NULL 0
+#define SHT_PROGBITS 1
+#define SHT_SYMTAB 2
+#define SHT_STRTAB 3
+#define SHT_RELA 4
+#define SHT_HASH 5
+#define SHT_DYNAMIC 6
+#define SHT_NOTE 7
+#define SHT_NOBITS 8
+#define SHT_REL 9
+#define SHT_SHLIB 10
+#define SHT_DYNSYM 11
+#define SHT_NUM 12
+#define SHT_LOPROC 0x70000000
+#define SHT_HIPROC 0x7fffffff
+#define SHT_LOUSER 0x80000000
+#define SHT_HIUSER 0xffffffff
+#define SHT_MIPS_LIST 0x70000000
+#define SHT_MIPS_CONFLICT 0x70000002
+#define SHT_MIPS_GPTAB 0x70000003
+#define SHT_MIPS_UCODE 0x70000004
+
+/* sh_flags */
+#define SHF_WRITE 0x1
+#define SHF_ALLOC 0x2
+#define SHF_EXECINSTR 0x4
+#define SHF_MASKPROC 0xf0000000
+#define SHF_MIPS_GPREL 0x10000000
+
+/* special section indexes */
+#define SHN_UNDEF 0
+#define SHN_LORESERVE 0xff00
+#define SHN_LOPROC 0xff00
+#define SHN_HIPROC 0xff1f
+#define SHN_ABS 0xfff1
+#define SHN_COMMON 0xfff2
+#define SHN_HIRESERVE 0xffff
+#define SHN_MIPS_ACCOMON 0xff00
+
+typedef struct {
+ Elf32_Word sh_name;
+ Elf32_Word sh_type;
+ Elf32_Word sh_flags;
+ Elf32_Addr sh_addr;
+ Elf32_Off sh_offset;
+ Elf32_Word sh_size;
+ Elf32_Word sh_link;
+ Elf32_Word sh_info;
+ Elf32_Word sh_addralign;
+ Elf32_Word sh_entsize;
+} Elf32_Shdr;
+
+typedef struct elf64_shdr {
+ Elf32_Word sh_name; /* Section name, index in string tbl (yes Elf32) */
+ Elf32_Word sh_type; /* Type of section (yes Elf32) */
+ Elf64_Word sh_flags; /* Miscellaneous section attributes */
+ Elf64_Addr sh_addr; /* Section virtual addr at execution */
+ Elf64_Off sh_offset; /* Section file offset */
+ Elf64_Word sh_size; /* Size of section in bytes */
+ Elf32_Word sh_link; /* Index of another section (yes Elf32) */
+ Elf32_Word sh_info; /* Additional section information (yes Elf32) */
+ Elf64_Word sh_addralign; /* Section alignment */
+ Elf64_Word sh_entsize; /* Entry size if section holds table */
+} Elf64_Shdr;
+
+#define EI_MAG0 0 /* e_ident[] indexes */
+#define EI_MAG1 1
+#define EI_MAG2 2
+#define EI_MAG3 3
+#define EI_CLASS 4
+#define EI_DATA 5
+#define EI_VERSION 6
+#define EI_PAD 7
+
+#define ELFMAG0 0x7f /* EI_MAG */
+#define ELFMAG1 'E'
+#define ELFMAG2 'L'
+#define ELFMAG3 'F'
+#define ELFMAG "\177ELF"
+#define SELFMAG 4
+
+#define ELFCLASSNONE 0 /* EI_CLASS */
+#define ELFCLASS32 1
+#define ELFCLASS64 2
+#define ELFCLASSNUM 3
+
+#define ELFDATANONE 0 /* e_ident[EI_DATA] */
+#define ELFDATA2LSB 1
+#define ELFDATA2MSB 2
+
+#define EV_NONE 0 /* e_version, EI_VERSION */
+#define EV_CURRENT 1
+#define EV_NUM 2
+
+/* Notes used in ET_CORE */
+#define NT_PRSTATUS 1
+#define NT_PRFPREG 2
+#define NT_PRPSINFO 3
+#define NT_TASKSTRUCT 4
+
+/* Note header in a PT_NOTE section */
+typedef struct elf32_note {
+ Elf32_Word n_namesz; /* Name size */
+ Elf32_Word n_descsz; /* Content size */
+ Elf32_Word n_type; /* Content type */
+} Elf32_Nhdr;
+
+/* Note header in a PT_NOTE section */
+/*
+ * For now we use the 32 bit version of the structure until we figure
+ * out whether we need anything better. Note - on the Alpha, "unsigned int"
+ * is only 32 bits.
+ */
+typedef struct elf64_note {
+ Elf32_Word n_namesz; /* Name size */
+ Elf32_Word n_descsz; /* Content size */
+ Elf32_Word n_type; /* Content type */
+} Elf64_Nhdr;
+
+#if ELF_CLASS == ELFCLASS32
+
+extern Elf32_Dyn _DYNAMIC [];
+#define elfhdr elf32_hdr
+#define elf_phdr elf32_phdr
+#define elf_note elf32_note
+
+#else
+
+extern Elf64_Dyn _DYNAMIC [];
+#define elfhdr elf64_hdr
+#define elf_phdr elf64_phdr
+#define elf_note elf64_note
+
+#endif
+
+
+#endif /* _LINUX_ELF_H */
diff --git a/pfinet/linux-src/include/linux/elfcore.h b/pfinet/linux-src/include/linux/elfcore.h
new file mode 100644
index 00000000..5c1cb05b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/elfcore.h
@@ -0,0 +1,88 @@
+#ifndef _LINUX_ELFCORE_H
+#define _LINUX_ELFCORE_H
+
+#include <linux/types.h>
+#include <linux/signal.h>
+#include <linux/time.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+
+struct elf_siginfo
+{
+ int si_signo; /* signal number */
+ int si_code; /* extra code */
+ int si_errno; /* errno */
+};
+
+#include <asm/elf.h>
+
+#ifndef __KERNEL__
+typedef elf_greg_t greg_t;
+typedef elf_gregset_t gregset_t;
+typedef elf_fpregset_t fpregset_t;
+#define NGREG ELF_NGREG
+#endif
+
+/*
+ * Definitions to generate Intel SVR4-like core files.
+ * These mostly have the same names as the SVR4 types with "elf_"
+ * tacked on the front to prevent clashes with linux definitions,
+ * and the typedef forms have been avoided. This is mostly like
+ * the SVR4 structure, but more Linuxy, with things that Linux does
+ * not support and which gdb doesn't really use excluded.
+ * Fields present but not used are marked with "XXX".
+ */
+struct elf_prstatus
+{
+#if 0
+ long pr_flags; /* XXX Process flags */
+ short pr_why; /* XXX Reason for process halt */
+ short pr_what; /* XXX More detailed reason */
+#endif
+ struct elf_siginfo pr_info; /* Info associated with signal */
+ short pr_cursig; /* Current signal */
+ unsigned long pr_sigpend; /* Set of pending signals */
+ unsigned long pr_sighold; /* Set of held signals */
+#if 0
+ struct sigaltstack pr_altstack; /* Alternate stack info */
+ struct sigaction pr_action; /* Signal action for current sig */
+#endif
+ pid_t pr_pid;
+ pid_t pr_ppid;
+ pid_t pr_pgrp;
+ pid_t pr_sid;
+ struct timeval pr_utime; /* User time */
+ struct timeval pr_stime; /* System time */
+ struct timeval pr_cutime; /* Cumulative user time */
+ struct timeval pr_cstime; /* Cumulative system time */
+#if 0
+ long pr_instr; /* Current instruction */
+#endif
+ elf_gregset_t pr_reg; /* GP registers */
+ int pr_fpvalid; /* True if math co-processor being used. */
+};
+
+#define ELF_PRARGSZ (80) /* Number of chars for args */
+
+struct elf_prpsinfo
+{
+ char pr_state; /* numeric process state */
+ char pr_sname; /* char for pr_state */
+ char pr_zomb; /* zombie */
+ char pr_nice; /* nice val */
+ unsigned long pr_flag; /* flags */
+ uid_t pr_uid;
+ gid_t pr_gid;
+ pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid;
+ /* Lots missing */
+ char pr_fname[16]; /* filename of executable */
+ char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */
+};
+
+#ifndef __KERNEL__
+typedef struct elf_prstatus prstatus_t;
+typedef struct elf_prpsinfo prpsinfo_t;
+#define PRARGSZ ELF_PRARGSZ
+#endif
+
+#endif /* _LINUX_ELFCORE_H */
diff --git a/pfinet/linux-src/include/linux/epca.h b/pfinet/linux-src/include/linux/epca.h
new file mode 100644
index 00000000..dbd72ad8
--- /dev/null
+++ b/pfinet/linux-src/include/linux/epca.h
@@ -0,0 +1,169 @@
+#define XEMPORTS 0xC02
+#define XEPORTS 0xC22
+
+#define MAX_ALLOC 0x100
+
+#define MAXBOARDS 12
+#define FEPCODESEG 0x0200L
+#define FEPCODE 0x2000L
+#define BIOSCODE 0xf800L
+
+#define MISCGLOBAL 0x0C00L
+#define NPORT 0x0C22L
+#define MBOX 0x0C40L
+#define PORTBASE 0x0C90L
+
+/* Begin code defines used for epca_setup */
+
+#define INVALID_BOARD_TYPE 0x1
+#define INVALID_NUM_PORTS 0x2
+#define INVALID_MEM_BASE 0x4
+#define INVALID_PORT_BASE 0x8
+#define INVALID_BOARD_STATUS 0x10
+#define INVALID_ALTPIN 0x20
+
+/* End code defines used for epca_setup */
+
+
+#define FEPCLR 0x00
+#define FEPMEM 0x02
+#define FEPRST 0x04
+#define FEPINT 0x08
+#define FEPMASK 0x0e
+#define FEPWIN 0x80
+
+#define PCXE 0
+#define PCXEVE 1
+#define PCXEM 2
+#define EISAXEM 3
+#define PC64XE 4
+#define PCXI 5
+#define PCIXEM 7
+#define PCICX 8
+#define PCIXR 9
+#define PCIXRJ 10
+#define EPCA_NUM_TYPES 6
+
+
+static char *board_desc[] =
+{
+ "PC/Xe",
+ "PC/Xeve",
+ "PC/Xem",
+ "EISA/Xem",
+ "PC/64Xe",
+ "PC/Xi",
+ "unknown",
+ "PCI/Xem",
+ "PCI/CX",
+ "PCI/Xr",
+ "PCI/Xrj",
+};
+
+#define STARTC 021
+#define STOPC 023
+#define IAIXON 0x2000
+
+
+#define TXSTOPPED 0x1
+#define LOWWAIT 0x2
+#define EMPTYWAIT 0x4
+#define RXSTOPPED 0x8
+#define TXBUSY 0x10
+
+#define DISABLED 0
+#define ENABLED 1
+#define OFF 0
+#define ON 1
+
+#define FEPTIMEOUT 200000
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2
+#define SERIAL_TYPE_INFO 3
+#define EPCA_EVENT_HANGUP 1
+#define EPCA_MAGIC 0x5c6df104L
+
+struct channel
+{
+ long magic;
+ unchar boardnum;
+ unchar channelnum;
+ unchar omodem; /* FEP output modem status */
+ unchar imodem; /* FEP input modem status */
+ unchar modemfake; /* Modem values to be forced */
+ unchar modem; /* Force values */
+ unchar hflow;
+ unchar dsr;
+ unchar dcd;
+ unchar m_rts ; /* The bits used in whatever FEP */
+ unchar m_dcd ; /* is indiginous to this board to */
+ unchar m_dsr ; /* represent each of the physical */
+ unchar m_cts ; /* handshake lines */
+ unchar m_ri ;
+ unchar m_dtr ;
+ unchar stopc;
+ unchar startc;
+ unchar stopca;
+ unchar startca;
+ unchar fepstopc;
+ unchar fepstartc;
+ unchar fepstopca;
+ unchar fepstartca;
+ unchar txwin;
+ unchar rxwin;
+ ushort fepiflag;
+ ushort fepcflag;
+ ushort fepoflag;
+ ushort txbufhead;
+ ushort txbufsize;
+ ushort rxbufhead;
+ ushort rxbufsize;
+ int close_delay;
+ int count;
+ int blocked_open;
+ int event;
+ int asyncflags;
+ uint dev;
+ long session;
+ long pgrp;
+ ulong statusflags;
+ ulong c_iflag;
+ ulong c_cflag;
+ ulong c_lflag;
+ ulong c_oflag;
+ unchar *txptr;
+ unchar *rxptr;
+ unchar *tmp_buf;
+ struct board_info *board;
+ volatile struct board_chan *brdchan;
+ struct digi_struct digiext;
+ struct tty_struct *tty;
+ struct termios normal_termios;
+ struct termios callout_termios;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+ struct tq_struct tqueue;
+ volatile struct global_data *mailbox;
+};
+
+struct board_info
+{
+ unchar status;
+ unchar type;
+ unchar altpin;
+ ushort numports;
+ unchar *port;
+ unchar *membase;
+ unchar *re_map_port;
+ unchar *re_map_membase;
+ ulong memory_seg;
+ void ( * memwinon ) (struct board_info *, unsigned int) ;
+ void ( * memwinoff ) (struct board_info *, unsigned int) ;
+ void ( * globalwinon ) (struct channel *) ;
+ void ( * txwinon ) (struct channel *) ;
+ void ( * rxwinon ) (struct channel *) ;
+ void ( * memoff ) (struct channel *) ;
+ void ( * assertgwinon ) (struct channel *) ;
+ void ( * assertmemoff ) (struct channel *) ;
+ unchar poller_inhibited ;
+};
diff --git a/pfinet/linux-src/include/linux/epcaconfig.h b/pfinet/linux-src/include/linux/epcaconfig.h
new file mode 100644
index 00000000..c840c673
--- /dev/null
+++ b/pfinet/linux-src/include/linux/epcaconfig.h
@@ -0,0 +1,8 @@
+#define NUMCARDS 1
+#define NBDEVS 2
+
+struct board_info static_boards[NUMCARDS]={
+ { ENABLED, 0, OFF, 2, (unchar*) 0x320, (unchar*) 0xd0000 },
+};
+
+/* DO NOT HAND EDIT THIS FILE! */
diff --git a/pfinet/linux-src/include/linux/errno.h b/pfinet/linux-src/include/linux/errno.h
new file mode 100644
index 00000000..ac212844
--- /dev/null
+++ b/pfinet/linux-src/include/linux/errno.h
@@ -0,0 +1,16 @@
+#ifndef _LINUX_ERRNO_H
+#define _LINUX_ERRNO_H
+
+#include <asm/errno.h>
+
+#ifdef __KERNEL__
+
+/* Should never be seen by user programs */
+#define ERESTARTSYS 512
+#define ERESTARTNOINTR 513
+#define ERESTARTNOHAND 514 /* restart if no handler.. */
+#define ENOIOCTLCMD 515 /* No ioctl command */
+
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/errqueue.h b/pfinet/linux-src/include/linux/errqueue.h
new file mode 100644
index 00000000..48d79561
--- /dev/null
+++ b/pfinet/linux-src/include/linux/errqueue.h
@@ -0,0 +1,44 @@
+#ifndef _LINUX_ERRQUEUE_H
+#define _LINUX_ERRQUEUE_H 1
+
+#ifdef __KERNEL__
+#include <linux/config.h>
+#endif
+
+struct sock_extended_err
+{
+ __u32 ee_errno;
+ __u8 ee_origin;
+ __u8 ee_type;
+ __u8 ee_code;
+ __u8 ee_pad;
+ __u32 ee_info;
+ __u32 ee_data;
+};
+
+#define SO_EE_ORIGIN_NONE 0
+#define SO_EE_ORIGIN_LOCAL 1
+#define SO_EE_ORIGIN_ICMP 2
+#define SO_EE_ORIGIN_ICMP6 3
+
+#define SO_EE_OFFENDER(ee) ((struct sockaddr*)((ee)+1))
+
+#ifdef __KERNEL__
+#define SKB_EXT_ERR(skb) ((struct sock_exterr_skb *) ((skb)->cb))
+
+struct sock_exterr_skb
+{
+ union {
+ struct inet_skb_parm h4;
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+ struct inet6_skb_parm h6;
+#endif
+ } header;
+ struct sock_extended_err ee;
+ u16 addr_offset;
+ u16 port;
+};
+
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/etherdevice.h b/pfinet/linux-src/include/linux/etherdevice.h
new file mode 100644
index 00000000..9101855c
--- /dev/null
+++ b/pfinet/linux-src/include/linux/etherdevice.h
@@ -0,0 +1,56 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. NET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the Ethernet handlers.
+ *
+ * Version: @(#)eth.h 1.0.4 05/13/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * Relocated to include/linux where it belongs by Alan Cox
+ * <gw4pts@gw4pts.ampr.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * WARNING: This move may well be temporary. This file will get merged with others RSN.
+ *
+ */
+#ifndef _LINUX_ETHERDEVICE_H
+#define _LINUX_ETHERDEVICE_H
+
+#include <linux/config.h>
+#include <linux/if_ether.h>
+
+#ifdef __KERNEL__
+extern int eth_header(struct sk_buff *skb, struct device *dev,
+ unsigned short type, void *daddr,
+ void *saddr, unsigned len);
+extern int eth_rebuild_header(struct sk_buff *skb);
+extern unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev);
+extern void eth_header_cache_update(struct hh_cache *hh, struct device *dev,
+ unsigned char * haddr);
+extern int eth_header_cache(struct neighbour *neigh,
+ struct hh_cache *hh);
+extern int eth_header_parse(struct sk_buff *skb,
+ unsigned char *haddr);
+extern struct device * init_etherdev(struct device *, int);
+
+#ifdef CONFIG_IP_ROUTER
+static __inline__ void eth_copy_and_sum (struct sk_buff *dest, unsigned char *src, int len, int base)
+{
+ memcpy (dest->data, src, len);
+}
+#else
+extern void eth_copy_and_sum(struct sk_buff *dest,
+ unsigned char *src, int length, int base);
+#endif
+
+#endif
+
+#endif /* _LINUX_ETHERDEVICE_H */
diff --git a/pfinet/linux-src/include/linux/ext2_fs.h b/pfinet/linux-src/include/linux/ext2_fs.h
new file mode 100644
index 00000000..840e7cad
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ext2_fs.h
@@ -0,0 +1,624 @@
+/*
+ * linux/include/linux/ext2_fs.h
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/include/linux/minix_fs.h
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#ifndef _LINUX_EXT2_FS_H
+#define _LINUX_EXT2_FS_H
+
+#include <linux/types.h>
+
+/*
+ * The second extended filesystem constants/structures
+ */
+
+/*
+ * Define EXT2FS_DEBUG to produce debug messages
+ */
+#undef EXT2FS_DEBUG
+
+/*
+ * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files
+ */
+#define EXT2_PREALLOCATE
+#define EXT2_DEFAULT_PREALLOC_BLOCKS 8
+
+/*
+ * The second extended file system version
+ */
+#define EXT2FS_DATE "95/08/09"
+#define EXT2FS_VERSION "0.5b"
+
+/*
+ * Debug code
+ */
+#ifdef EXT2FS_DEBUG
+# define ext2_debug(f, a...) { \
+ printk ("EXT2-fs DEBUG (%s, %d): %s:", \
+ __FILE__, __LINE__, __FUNCTION__); \
+ printk (f, ## a); \
+ }
+#else
+# define ext2_debug(f, a...) /**/
+#endif
+
+/*
+ * Special inodes numbers
+ */
+#define EXT2_BAD_INO 1 /* Bad blocks inode */
+#define EXT2_ROOT_INO 2 /* Root inode */
+#define EXT2_ACL_IDX_INO 3 /* ACL inode */
+#define EXT2_ACL_DATA_INO 4 /* ACL inode */
+#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
+#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */
+
+/* First non-reserved inode for old ext2 filesystems */
+#define EXT2_GOOD_OLD_FIRST_INO 11
+
+/*
+ * The second extended file system magic number
+ */
+#define EXT2_SUPER_MAGIC 0xEF53
+
+/*
+ * Maximal count of links to a file
+ */
+#define EXT2_LINK_MAX 32000
+
+/*
+ * Macro-instructions used to manage several block sizes
+ */
+#define EXT2_MIN_BLOCK_SIZE 1024
+#define EXT2_MAX_BLOCK_SIZE 4096
+#define EXT2_MIN_BLOCK_LOG_SIZE 10
+#ifdef __KERNEL__
+# define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize)
+#else
+# define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
+#endif
+#define EXT2_ACLE_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_entry))
+#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32))
+#ifdef __KERNEL__
+# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits)
+#else
+# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10)
+#endif
+#ifdef __KERNEL__
+#define EXT2_ADDR_PER_BLOCK_BITS(s) ((s)->u.ext2_sb.s_addr_per_block_bits)
+#define EXT2_INODE_SIZE(s) ((s)->u.ext2_sb.s_inode_size)
+#define EXT2_FIRST_INO(s) ((s)->u.ext2_sb.s_first_ino)
+#else
+#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+ EXT2_GOOD_OLD_INODE_SIZE : \
+ (s)->s_inode_size)
+#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+ EXT2_GOOD_OLD_FIRST_INO : \
+ (s)->s_first_ino)
+#endif
+
+/*
+ * Macro-instructions used to manage fragments
+ */
+#define EXT2_MIN_FRAG_SIZE 1024
+#define EXT2_MAX_FRAG_SIZE 4096
+#define EXT2_MIN_FRAG_LOG_SIZE 10
+#ifdef __KERNEL__
+# define EXT2_FRAG_SIZE(s) ((s)->u.ext2_sb.s_frag_size)
+# define EXT2_FRAGS_PER_BLOCK(s) ((s)->u.ext2_sb.s_frags_per_block)
+#else
+# define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size)
+# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s))
+#endif
+
+/*
+ * ACL structures
+ */
+struct ext2_acl_header /* Header of Access Control Lists */
+{
+ __u32 aclh_size;
+ __u32 aclh_file_count;
+ __u32 aclh_acle_count;
+ __u32 aclh_first_acle;
+};
+
+struct ext2_acl_entry /* Access Control List Entry */
+{
+ __u32 acle_size;
+ __u16 acle_perms; /* Access permissions */
+ __u16 acle_type; /* Type of entry */
+ __u16 acle_tag; /* User or group identity */
+ __u16 acle_pad1;
+ __u32 acle_next; /* Pointer on next entry for the */
+ /* same inode or on next free entry */
+};
+
+/*
+ * Structure of a blocks group descriptor
+ */
+struct ext2_group_desc
+{
+ __u32 bg_block_bitmap; /* Blocks bitmap block */
+ __u32 bg_inode_bitmap; /* Inodes bitmap block */
+ __u32 bg_inode_table; /* Inodes table block */
+ __u16 bg_free_blocks_count; /* Free blocks count */
+ __u16 bg_free_inodes_count; /* Free inodes count */
+ __u16 bg_used_dirs_count; /* Directories count */
+ __u16 bg_pad;
+ __u32 bg_reserved[3];
+};
+
+/*
+ * Macro-instructions used to manage group descriptors
+ */
+#ifdef __KERNEL__
+# define EXT2_BLOCKS_PER_GROUP(s) ((s)->u.ext2_sb.s_blocks_per_group)
+# define EXT2_DESC_PER_BLOCK(s) ((s)->u.ext2_sb.s_desc_per_block)
+# define EXT2_INODES_PER_GROUP(s) ((s)->u.ext2_sb.s_inodes_per_group)
+# define EXT2_DESC_PER_BLOCK_BITS(s) ((s)->u.ext2_sb.s_desc_per_block_bits)
+#else
+# define EXT2_BLOCKS_PER_GROUP(s) ((s)->s_blocks_per_group)
+# define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc))
+# define EXT2_INODES_PER_GROUP(s) ((s)->s_inodes_per_group)
+#endif
+
+/*
+ * Constants relative to the data blocks
+ */
+#define EXT2_NDIR_BLOCKS 12
+#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
+#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
+#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
+#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
+
+/*
+ * Inode flags
+ */
+#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */
+#define EXT2_UNRM_FL 0x00000002 /* Undelete */
+#define EXT2_COMPR_FL 0x00000004 /* Compress file */
+#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */
+#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */
+#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */
+#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */
+#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */
+/* Reserved for compression usage... */
+#define EXT2_DIRTY_FL 0x00000100
+#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */
+#define EXT2_NOCOMP_FL 0x00000400 /* Don't compress */
+#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */
+/* End compression flags --- maybe not all used */
+#define EXT2_BTREE_FL 0x00001000 /* btree format dir */
+#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */
+
+#define EXT2_FL_USER_VISIBLE 0x00001FFF /* User visible flags */
+#define EXT2_FL_USER_MODIFIABLE 0x000000FF /* User modifiable flags */
+
+/*
+ * ioctl commands
+ */
+#define EXT2_IOC_GETFLAGS _IOR('f', 1, long)
+#define EXT2_IOC_SETFLAGS _IOW('f', 2, long)
+#define EXT2_IOC_GETVERSION _IOR('v', 1, long)
+#define EXT2_IOC_SETVERSION _IOW('v', 2, long)
+
+/*
+ * Structure of an inode on the disk
+ */
+struct ext2_inode {
+ __u16 i_mode; /* File mode */
+ __u16 i_uid; /* Owner Uid */
+ __u32 i_size; /* Size in bytes */
+ __u32 i_atime; /* Access time */
+ __u32 i_ctime; /* Creation time */
+ __u32 i_mtime; /* Modification time */
+ __u32 i_dtime; /* Deletion Time */
+ __u16 i_gid; /* Group Id */
+ __u16 i_links_count; /* Links count */
+ __u32 i_blocks; /* Blocks count */
+ __u32 i_flags; /* File flags */
+ union {
+ struct {
+ __u32 l_i_reserved1;
+ } linux1;
+ struct {
+ __u32 h_i_translator;
+ } hurd1;
+ struct {
+ __u32 m_i_reserved1;
+ } masix1;
+ } osd1; /* OS dependent 1 */
+ __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+ __u32 i_version; /* File version (for NFS) */
+ __u32 i_file_acl; /* File ACL */
+ __u32 i_dir_acl; /* Directory ACL */
+ __u32 i_faddr; /* Fragment address */
+ union {
+ struct {
+ __u8 l_i_frag; /* Fragment number */
+ __u8 l_i_fsize; /* Fragment size */
+ __u16 i_pad1;
+ __u32 l_i_reserved2[2];
+ } linux2;
+ struct {
+ __u8 h_i_frag; /* Fragment number */
+ __u8 h_i_fsize; /* Fragment size */
+ __u16 h_i_mode_high;
+ __u16 h_i_uid_high;
+ __u16 h_i_gid_high;
+ __u32 h_i_author;
+ } hurd2;
+ struct {
+ __u8 m_i_frag; /* Fragment number */
+ __u8 m_i_fsize; /* Fragment size */
+ __u16 m_pad1;
+ __u32 m_i_reserved2[2];
+ } masix2;
+ } osd2; /* OS dependent 2 */
+};
+
+#define i_size_high i_dir_acl
+
+#if defined(__KERNEL__) || defined(__linux__)
+#define i_reserved1 osd1.linux1.l_i_reserved1
+#define i_frag osd2.linux2.l_i_frag
+#define i_fsize osd2.linux2.l_i_fsize
+#define i_reserved2 osd2.linux2.l_i_reserved2
+#endif
+
+#ifdef __hurd__
+#define i_translator osd1.hurd1.h_i_translator
+#define i_frag osd2.hurd2.h_i_frag;
+#define i_fsize osd2.hurd2.h_i_fsize;
+#define i_uid_high osd2.hurd2.h_i_uid_high
+#define i_gid_high osd2.hurd2.h_i_gid_high
+#define i_author osd2.hurd2.h_i_author
+#endif
+
+#ifdef __masix__
+#define i_reserved1 osd1.masix1.m_i_reserved1
+#define i_frag osd2.masix2.m_i_frag
+#define i_fsize osd2.masix2.m_i_fsize
+#define i_reserved2 osd2.masix2.m_i_reserved2
+#endif
+
+/*
+ * File system states
+ */
+#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */
+#define EXT2_ERROR_FS 0x0002 /* Errors detected */
+
+/*
+ * Mount flags
+ */
+#define EXT2_MOUNT_CHECK_NORMAL 0x0001 /* Do some more checks */
+#define EXT2_MOUNT_CHECK_STRICT 0x0002 /* Do again more checks */
+#define EXT2_MOUNT_CHECK (EXT2_MOUNT_CHECK_NORMAL | \
+ EXT2_MOUNT_CHECK_STRICT)
+#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */
+#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */
+#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */
+#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */
+#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */
+#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */
+
+#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt
+#define set_opt(o, opt) o |= EXT2_MOUNT_##opt
+#define test_opt(sb, opt) ((sb)->u.ext2_sb.s_mount_opt & \
+ EXT2_MOUNT_##opt)
+/*
+ * Maximal mount counts between two filesystem checks
+ */
+#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */
+#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */
+
+/*
+ * Behaviour when detecting errors
+ */
+#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */
+#define EXT2_ERRORS_RO 2 /* Remount fs read-only */
+#define EXT2_ERRORS_PANIC 3 /* Panic */
+#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE
+
+/*
+ * Structure of the super block
+ */
+struct ext2_super_block {
+ __u32 s_inodes_count; /* Inodes count */
+ __u32 s_blocks_count; /* Blocks count */
+ __u32 s_r_blocks_count; /* Reserved blocks count */
+ __u32 s_free_blocks_count; /* Free blocks count */
+ __u32 s_free_inodes_count; /* Free inodes count */
+ __u32 s_first_data_block; /* First Data Block */
+ __u32 s_log_block_size; /* Block size */
+ __s32 s_log_frag_size; /* Fragment size */
+ __u32 s_blocks_per_group; /* # Blocks per group */
+ __u32 s_frags_per_group; /* # Fragments per group */
+ __u32 s_inodes_per_group; /* # Inodes per group */
+ __u32 s_mtime; /* Mount time */
+ __u32 s_wtime; /* Write time */
+ __u16 s_mnt_count; /* Mount count */
+ __s16 s_max_mnt_count; /* Maximal mount count */
+ __u16 s_magic; /* Magic signature */
+ __u16 s_state; /* File system state */
+ __u16 s_errors; /* Behaviour when detecting errors */
+ __u16 s_minor_rev_level; /* minor revision level */
+ __u32 s_lastcheck; /* time of last check */
+ __u32 s_checkinterval; /* max. time between checks */
+ __u32 s_creator_os; /* OS */
+ __u32 s_rev_level; /* Revision level */
+ __u16 s_def_resuid; /* Default uid for reserved blocks */
+ __u16 s_def_resgid; /* Default gid for reserved blocks */
+ /*
+ * These fields are for EXT2_DYNAMIC_REV superblocks only.
+ *
+ * Note: the difference between the compatible feature set and
+ * the incompatible feature set is that if there is a bit set
+ * in the incompatible feature set that the kernel doesn't
+ * know about, it should refuse to mount the filesystem.
+ *
+ * e2fsck's requirements are more strict; if it doesn't know
+ * about a feature in either the compatible or incompatible
+ * feature set, it must abort and not try to meddle with
+ * things it doesn't understand...
+ */
+ __u32 s_first_ino; /* First non-reserved inode */
+ __u16 s_inode_size; /* size of inode structure */
+ __u16 s_block_group_nr; /* block group # of this superblock */
+ __u32 s_feature_compat; /* compatible feature set */
+ __u32 s_feature_incompat; /* incompatible feature set */
+ __u32 s_feature_ro_compat; /* readonly-compatible feature set */
+ __u8 s_uuid[16]; /* 128-bit uuid for volume */
+ char s_volume_name[16]; /* volume name */
+ char s_last_mounted[64]; /* directory where last mounted */
+ __u32 s_algorithm_usage_bitmap; /* For compression */
+ /*
+ * Performance hints. Directory preallocation should only
+ * happen if the EXT2_COMPAT_PREALLOC flag is on.
+ */
+ __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
+ __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
+ __u16 s_padding1;
+ __u32 s_reserved[204]; /* Padding to the end of the block */
+};
+
+#ifdef __KERNEL__
+#define EXT2_SB(sb) (&((sb)->u.ext2_sb))
+#else
+/* Assume that user mode programs are passing in an ext2fs superblock, not
+ * a kernel struct super_block. This will allow us to call the feature-test
+ * macros from user land. */
+#define EXT2_SB(sb) (sb)
+#endif
+
+/*
+ * Codes for operating systems
+ */
+#define EXT2_OS_LINUX 0
+#define EXT2_OS_HURD 1
+#define EXT2_OS_MASIX 2
+#define EXT2_OS_FREEBSD 3
+#define EXT2_OS_LITES 4
+
+/*
+ * Revision levels
+ */
+#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */
+#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */
+
+#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV
+#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV
+
+#define EXT2_GOOD_OLD_INODE_SIZE 128
+
+/*
+ * Feature set definitions
+ */
+
+#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \
+ ( EXT2_SB(sb)->s_feature_compat & (mask) )
+#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \
+ ( EXT2_SB(sb)->s_feature_ro_compat & (mask) )
+#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \
+ ( EXT2_SB(sb)->s_feature_incompat & (mask) )
+
+#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001
+
+#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
+#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
+#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004
+
+#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
+#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
+
+#define EXT2_FEATURE_COMPAT_SUPP 0
+#define EXT2_FEATURE_INCOMPAT_SUPP EXT2_FEATURE_INCOMPAT_FILETYPE
+#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+ EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+
+/*
+ * Default values for user and/or group using reserved blocks
+ */
+#define EXT2_DEF_RESUID 0
+#define EXT2_DEF_RESGID 0
+
+/*
+ * Structure of a directory entry
+ */
+#define EXT2_NAME_LEN 255
+
+struct ext2_dir_entry {
+ __u32 inode; /* Inode number */
+ __u16 rec_len; /* Directory entry length */
+ __u16 name_len; /* Name length */
+ char name[EXT2_NAME_LEN]; /* File name */
+};
+
+/*
+ * The new version of the directory entry. Since EXT2 structures are
+ * stored in intel byte order, and the name_len field could never be
+ * bigger than 255 chars, it's safe to reclaim the extra byte for the
+ * file_type field.
+ */
+struct ext2_dir_entry_2 {
+ __u32 inode; /* Inode number */
+ __u16 rec_len; /* Directory entry length */
+ __u8 name_len; /* Name length */
+ __u8 file_type;
+ char name[EXT2_NAME_LEN]; /* File name */
+};
+
+/*
+ * Ext2 directory file types. Only the low 3 bits are used. The
+ * other bits are reserved for now.
+ */
+#define EXT2_FT_UNKNOWN 0
+#define EXT2_FT_REG_FILE 1
+#define EXT2_FT_DIR 2
+#define EXT2_FT_CHRDEV 3
+#define EXT2_FT_BLKDEV 4
+#define EXT2_FT_FIFO 5
+#define EXT2_FT_SOCK 6
+#define EXT2_FT_SYMLINK 7
+
+#define EXT2_FT_MAX 8
+
+/*
+ * EXT2_DIR_PAD defines the directory entries boundaries
+ *
+ * NOTE: It must be a multiple of 4
+ */
+#define EXT2_DIR_PAD 4
+#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
+#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
+ ~EXT2_DIR_ROUND)
+
+#ifdef __KERNEL__
+
+/* Filesize hard limits for 64-bit file offsets */
+extern long long ext2_max_sizes[];
+
+/*
+ * Function prototypes
+ */
+
+/*
+ * Ok, these declarations are also in <linux/kernel.h> but none of the
+ * ext2 source programs needs to include it so they are duplicated here.
+ */
+# define NORET_TYPE /**/
+# define ATTRIB_NORET __attribute__((noreturn))
+# define NORET_AND noreturn,
+
+/* acl.c */
+extern int ext2_permission (struct inode *, int);
+
+/* balloc.c */
+extern int ext2_group_sparse(int group);
+extern int ext2_new_block (const struct inode *, unsigned long,
+ __u32 *, __u32 *, int *);
+extern void ext2_free_blocks (const struct inode *, unsigned long,
+ unsigned long);
+extern unsigned long ext2_count_free_blocks (struct super_block *);
+extern void ext2_check_blocks_bitmap (struct super_block *);
+extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
+ unsigned int block_group,
+ struct buffer_head ** bh);
+
+/* bitmap.c */
+extern unsigned long ext2_count_free (struct buffer_head *, unsigned);
+
+/* dir.c */
+extern int ext2_check_dir_entry (const char *, struct inode *,
+ struct ext2_dir_entry_2 *, struct buffer_head *,
+ unsigned long);
+
+/* file.c */
+extern int ext2_read (struct inode *, struct file *, char *, int);
+extern int ext2_write (struct inode *, struct file *, char *, int);
+
+/* fsync.c */
+extern int ext2_sync_file (struct file *, struct dentry *);
+
+/* ialloc.c */
+extern struct inode * ext2_new_inode (const struct inode *, int, int *);
+extern void ext2_free_inode (struct inode *);
+extern unsigned long ext2_count_free_inodes (struct super_block *);
+extern void ext2_check_inodes_bitmap (struct super_block *);
+
+/* inode.c */
+extern int ext2_bmap (struct inode *, int);
+
+extern struct buffer_head * ext2_getblk (struct inode *, long, int, int *);
+extern struct buffer_head * ext2_bread (struct inode *, int, int, int *);
+
+extern int ext2_getcluster (struct inode * inode, long block);
+extern void ext2_read_inode (struct inode *);
+extern void ext2_write_inode (struct inode *);
+extern void ext2_put_inode (struct inode *);
+extern void ext2_delete_inode (struct inode *);
+extern int ext2_sync_inode (struct inode *);
+extern int ext2_notify_change(struct dentry *, struct iattr *);
+extern void ext2_discard_prealloc (struct inode *);
+
+/* ioctl.c */
+extern int ext2_ioctl (struct inode *, struct file *, unsigned int,
+ unsigned long);
+
+/* namei.c */
+extern void ext2_release (struct inode *, struct file *);
+extern struct dentry *ext2_lookup (struct inode *, struct dentry *);
+extern int ext2_create (struct inode *,struct dentry *,int);
+extern int ext2_mkdir (struct inode *,struct dentry *,int);
+extern int ext2_rmdir (struct inode *,struct dentry *);
+extern int ext2_unlink (struct inode *,struct dentry *);
+extern int ext2_symlink (struct inode *,struct dentry *,const char *);
+extern int ext2_link (struct dentry *, struct inode *, struct dentry *);
+extern int ext2_mknod (struct inode *, struct dentry *, int, int);
+extern int ext2_rename (struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
+
+/* super.c */
+extern void ext2_error (struct super_block *, const char *, const char *, ...)
+ __attribute__ ((format (printf, 3, 4)));
+extern NORET_TYPE void ext2_panic (struct super_block *, const char *,
+ const char *, ...)
+ __attribute__ ((NORET_AND format (printf, 3, 4)));
+extern void ext2_warning (struct super_block *, const char *, const char *, ...)
+ __attribute__ ((format (printf, 3, 4)));
+extern void ext2_put_super (struct super_block *);
+extern void ext2_write_super (struct super_block *);
+extern int ext2_remount (struct super_block *, int *, char *);
+extern struct super_block * ext2_read_super (struct super_block *,void *,int);
+extern int init_ext2_fs(void);
+extern int ext2_statfs (struct super_block *, struct statfs *, int);
+
+/* truncate.c */
+extern void ext2_truncate (struct inode *);
+
+/*
+ * Inodes and files operations
+ */
+
+/* dir.c */
+extern struct inode_operations ext2_dir_inode_operations;
+
+/* file.c */
+extern struct inode_operations ext2_file_inode_operations;
+
+/* symlink.c */
+extern struct inode_operations ext2_symlink_inode_operations;
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_EXT2_FS_H */
diff --git a/pfinet/linux-src/include/linux/ext2_fs_i.h b/pfinet/linux-src/include/linux/ext2_fs_i.h
new file mode 100644
index 00000000..8f01f8ad
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ext2_fs_i.h
@@ -0,0 +1,42 @@
+/*
+ * linux/include/linux/ext2_fs_i.h
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/include/linux/minix_fs_i.h
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#ifndef _LINUX_EXT2_FS_I
+#define _LINUX_EXT2_FS_I
+
+/*
+ * second extended file system inode data in memory
+ */
+struct ext2_inode_info {
+ __u32 i_data[15];
+ __u32 i_flags;
+ __u32 i_faddr;
+ __u8 i_frag_no;
+ __u8 i_frag_size;
+ __u16 i_osync;
+ __u32 i_file_acl;
+ __u32 i_dir_acl;
+ __u32 i_dtime;
+ __u32 i_version;
+ __u32 i_block_group;
+ __u32 i_next_alloc_block;
+ __u32 i_next_alloc_goal;
+ __u32 i_prealloc_block;
+ __u32 i_prealloc_count;
+ __u32 i_high_size;
+ int i_new_inode:1; /* Is a freshly allocated inode */
+};
+
+#endif /* _LINUX_EXT2_FS_I */
diff --git a/pfinet/linux-src/include/linux/ext2_fs_sb.h b/pfinet/linux-src/include/linux/ext2_fs_sb.h
new file mode 100644
index 00000000..20721632
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ext2_fs_sb.h
@@ -0,0 +1,66 @@
+/*
+ * linux/include/linux/ext2_fs_sb.h
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/include/linux/minix_fs_sb.h
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#ifndef _LINUX_EXT2_FS_SB
+#define _LINUX_EXT2_FS_SB
+
+#include <linux/ext2_fs.h>
+
+/*
+ * The following is not needed anymore since the descriptors buffer
+ * heads are now dynamically allocated
+ */
+/* #define EXT2_MAX_GROUP_DESC 8 */
+
+#define EXT2_MAX_GROUP_LOADED 8
+
+/*
+ * second extended-fs super-block data in memory
+ */
+struct ext2_sb_info {
+ unsigned long s_frag_size; /* Size of a fragment in bytes */
+ unsigned long s_frags_per_block;/* Number of fragments per block */
+ unsigned long s_inodes_per_block;/* Number of inodes per block */
+ unsigned long s_frags_per_group;/* Number of fragments in a group */
+ unsigned long s_blocks_per_group;/* Number of blocks in a group */
+ unsigned long s_inodes_per_group;/* Number of inodes in a group */
+ unsigned long s_itb_per_group; /* Number of inode table blocks per group */
+ unsigned long s_db_per_group; /* Number of descriptor blocks per group */
+ unsigned long s_desc_per_block; /* Number of group descriptors per block */
+ unsigned long s_groups_count; /* Number of groups in the fs */
+ struct buffer_head * s_sbh; /* Buffer containing the super block */
+ struct ext2_super_block * s_es; /* Pointer to the super block in the buffer */
+ struct buffer_head ** s_group_desc;
+ unsigned short s_loaded_inode_bitmaps;
+ unsigned short s_loaded_block_bitmaps;
+ unsigned long s_inode_bitmap_number[EXT2_MAX_GROUP_LOADED];
+ struct buffer_head * s_inode_bitmap[EXT2_MAX_GROUP_LOADED];
+ unsigned long s_block_bitmap_number[EXT2_MAX_GROUP_LOADED];
+ struct buffer_head * s_block_bitmap[EXT2_MAX_GROUP_LOADED];
+ unsigned long s_mount_opt;
+ unsigned short s_resuid;
+ unsigned short s_resgid;
+ unsigned short s_mount_state;
+ unsigned short s_pad;
+ int s_addr_per_block_bits;
+ int s_desc_per_block_bits;
+ int s_inode_size;
+ int s_first_ino;
+ int s_feature_compat;
+ int s_feature_incompat;
+ int s_feature_ro_compat;
+};
+
+#endif /* _LINUX_EXT2_FS_SB */
diff --git a/pfinet/linux-src/include/linux/fat_cvf.h b/pfinet/linux-src/include/linux/fat_cvf.h
new file mode 100644
index 00000000..1c4df58f
--- /dev/null
+++ b/pfinet/linux-src/include/linux/fat_cvf.h
@@ -0,0 +1,49 @@
+#ifndef _FAT_CVF
+#define _FAT_CVF
+
+#define CVF_USE_READPAGE 0x0001
+
+struct cvf_format
+{ int cvf_version;
+ char* cvf_version_text;
+ unsigned long flags;
+ int (*detect_cvf) (struct super_block*sb);
+ int (*mount_cvf) (struct super_block*sb,char*options);
+ int (*unmount_cvf) (struct super_block*sb);
+ struct buffer_head* (*cvf_bread) (struct super_block*sb,int block);
+ struct buffer_head* (*cvf_getblk) (struct super_block*sb,int block);
+ void (*cvf_brelse) (struct super_block *sb,struct buffer_head *bh);
+ void (*cvf_mark_buffer_dirty) (struct super_block *sb,
+ struct buffer_head *bh,
+ int dirty_val);
+ void (*cvf_set_uptodate) (struct super_block *sb,
+ struct buffer_head *bh,
+ int val);
+ int (*cvf_is_uptodate) (struct super_block *sb,struct buffer_head *bh);
+ void (*cvf_ll_rw_block) (struct super_block *sb,
+ int opr,
+ int nbreq,
+ struct buffer_head *bh[32]);
+ int (*fat_access) (struct super_block *sb,int nr,int new_value);
+ int (*cvf_statfs) (struct super_block *sb,struct statfs *buf, int bufsiz);
+ int (*cvf_bmap) (struct inode *inode,int block);
+ int (*cvf_smap) (struct inode *inode,int sector);
+ ssize_t (*cvf_file_read) ( struct file *, char *, size_t, loff_t *);
+ ssize_t (*cvf_file_write) ( struct file *, const char *, size_t, loff_t *);
+ int (*cvf_mmap) (struct file *, struct vm_area_struct *);
+ int (*cvf_readpage) (struct inode *, struct page *);
+ int (*cvf_writepage) (struct inode *, struct page *);
+ int (*cvf_dir_ioctl) (struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long arg);
+ void (*zero_out_cluster) (struct inode*, int clusternr);
+};
+
+int register_cvf_format(struct cvf_format*cvf_format);
+int unregister_cvf_format(struct cvf_format*cvf_format);
+void dec_cvf_format_use_count_by_version(int version);
+int detect_cvf(struct super_block*sb,char*force);
+
+extern struct cvf_format *cvf_formats[];
+extern int cvf_format_use_count[];
+
+#endif
diff --git a/pfinet/linux-src/include/linux/fb.h b/pfinet/linux-src/include/linux/fb.h
new file mode 100644
index 00000000..4170e030
--- /dev/null
+++ b/pfinet/linux-src/include/linux/fb.h
@@ -0,0 +1,494 @@
+#ifndef _LINUX_FB_H
+#define _LINUX_FB_H
+
+#include <asm/types.h>
+
+/* Definitions of frame buffers */
+
+#define FB_MAJOR 29
+
+#define FB_MODES_SHIFT 5 /* 32 modes per framebuffer */
+#define FB_NUM_MINORS 256 /* 256 Minors */
+#define FB_MAX (FB_NUM_MINORS / (1 << FB_MODES_SHIFT))
+#define GET_FB_IDX(node) (MINOR(node) >> FB_MODES_SHIFT)
+
+/* ioctls
+ 0x46 is 'F' */
+#define FBIOGET_VSCREENINFO 0x4600
+#define FBIOPUT_VSCREENINFO 0x4601
+#define FBIOGET_FSCREENINFO 0x4602
+#define FBIOGETCMAP 0x4604
+#define FBIOPUTCMAP 0x4605
+#define FBIOPAN_DISPLAY 0x4606
+/* 0x4607-0x460B are defined below */
+/* #define FBIOGET_MONITORSPEC 0x460C */
+/* #define FBIOPUT_MONITORSPEC 0x460D */
+/* #define FBIOSWITCH_MONIBIT 0x460E */
+#define FBIOGET_CON2FBMAP 0x460F
+#define FBIOPUT_CON2FBMAP 0x4610
+#define FBIOBLANK 0x4611 /* arg: 0 or vesa level + 1 */
+
+#define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */
+#define FB_TYPE_PLANES 1 /* Non interleaved planes */
+#define FB_TYPE_INTERLEAVED_PLANES 2 /* Interleaved planes */
+#define FB_TYPE_TEXT 3 /* Text/attributes */
+#define FB_TYPE_VGA_PLANES 4 /* EGA/VGA planes */
+
+#define FB_AUX_TEXT_MDA 0 /* Monochrome text */
+#define FB_AUX_TEXT_CGA 1 /* CGA/EGA/VGA Color text */
+#define FB_AUX_TEXT_S3_MMIO 2 /* S3 MMIO fasttext */
+#define FB_AUX_TEXT_MGA_STEP16 3 /* MGA Millenium I: text, attr, 14 reserved bytes */
+#define FB_AUX_TEXT_MGA_STEP8 4 /* other MGAs: text, attr, 6 reserved bytes */
+
+#define FB_VISUAL_MONO01 0 /* Monochr. 1=Black 0=White */
+#define FB_VISUAL_MONO10 1 /* Monochr. 1=White 0=Black */
+#define FB_VISUAL_TRUECOLOR 2 /* True color */
+#define FB_VISUAL_PSEUDOCOLOR 3 /* Pseudo color (like atari) */
+#define FB_VISUAL_DIRECTCOLOR 4 /* Direct color */
+#define FB_VISUAL_STATIC_PSEUDOCOLOR 5 /* Pseudo color readonly */
+
+#define FB_ACCEL_NONE 0 /* no hardware accelerator */
+#define FB_ACCEL_ATARIBLITT 1 /* Atari Blitter */
+#define FB_ACCEL_AMIGABLITT 2 /* Amiga Blitter */
+#define FB_ACCEL_S3_TRIO64 3 /* Cybervision64 (S3 Trio64) */
+#define FB_ACCEL_NCR_77C32BLT 4 /* RetinaZ3 (NCR 77C32BLT) */
+#define FB_ACCEL_S3_VIRGE 5 /* Cybervision64/3D (S3 ViRGE) */
+#define FB_ACCEL_ATI_MACH64GX 6 /* ATI Mach 64GX family */
+#define FB_ACCEL_DEC_TGA 7 /* DEC 21030 TGA */
+#define FB_ACCEL_ATI_MACH64CT 8 /* ATI Mach 64CT family */
+#define FB_ACCEL_ATI_MACH64VT 9 /* ATI Mach 64CT family VT class */
+#define FB_ACCEL_ATI_MACH64GT 10 /* ATI Mach 64CT family GT class */
+#define FB_ACCEL_SUN_CREATOR 11 /* Sun Creator/Creator3D */
+#define FB_ACCEL_SUN_CGSIX 12 /* Sun cg6 */
+#define FB_ACCEL_SUN_LEO 13 /* Sun leo/zx */
+#define FB_ACCEL_IMS_TWINTURBO 14 /* IMS Twin Turbo */
+#define FB_ACCEL_3DLABS_PERMEDIA2 15 /* 3Dlabs Permedia 2 */
+#define FB_ACCEL_MATROX_MGA2064W 16 /* Matrox MGA2064W (Millenium) */
+#define FB_ACCEL_MATROX_MGA1064SG 17 /* Matrox MGA1064SG (Mystique) */
+#define FB_ACCEL_MATROX_MGA2164W 18 /* Matrox MGA2164W (Millenium II) */
+#define FB_ACCEL_MATROX_MGA2164W_AGP 19 /* Matrox MGA2164W (Millenium II) */
+#define FB_ACCEL_MATROX_MGAG100 20 /* Matrox G100 (Productiva G100) */
+#define FB_ACCEL_MATROX_MGAG200 21 /* Matrox G200 (Myst, Mill, ...) */
+#define FB_ACCEL_SUN_CG14 22 /* Sun cgfourteen */
+#define FB_ACCEL_SUN_BWTWO 23 /* Sun bwtwo */
+#define FB_ACCEL_SUN_CGTHREE 24 /* Sun cgthree */
+#define FB_ACCEL_SUN_TCX 25 /* Sun tcx */
+#define FB_ACCEL_MATROX_MGAG400 26 /* Matrox G400 */
+
+struct fb_fix_screeninfo {
+ char id[16]; /* identification string eg "TT Builtin" */
+ char *smem_start; /* Start of frame buffer mem */
+ /* (physical address) */
+ __u32 smem_len; /* Length of frame buffer mem */
+ __u32 type; /* see FB_TYPE_* */
+ __u32 type_aux; /* Interleave for interleaved Planes */
+ __u32 visual; /* see FB_VISUAL_* */
+ __u16 xpanstep; /* zero if no hardware panning */
+ __u16 ypanstep; /* zero if no hardware panning */
+ __u16 ywrapstep; /* zero if no hardware ywrap */
+ __u32 line_length; /* length of a line in bytes */
+ char *mmio_start; /* Start of Memory Mapped I/O */
+ /* (physical address) */
+ __u32 mmio_len; /* Length of Memory Mapped I/O */
+ __u32 accel; /* Type of acceleration available */
+ __u16 reserved[3]; /* Reserved for future compatibility */
+};
+
+/* Interpretation of offset for color fields: All offsets are from the right,
+ * inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you
+ * can use the offset as right argument to <<). A pixel afterwards is a bit
+ * stream and is written to video memory as that unmodified. This implies
+ * big-endian byte order if bits_per_pixel is greater than 8.
+ */
+struct fb_bitfield {
+ __u32 offset; /* beginning of bitfield */
+ __u32 length; /* length of bitfield */
+ __u32 msb_right; /* != 0 : Most significant bit is */
+ /* right */
+};
+
+#define FB_NONSTD_HAM 1 /* Hold-And-Modify (HAM) */
+
+#define FB_ACTIVATE_NOW 0 /* set values immediately (or vbl)*/
+#define FB_ACTIVATE_NXTOPEN 1 /* activate on next open */
+#define FB_ACTIVATE_TEST 2 /* don't set, round up impossible */
+#define FB_ACTIVATE_MASK 15
+ /* values */
+#define FB_ACTIVATE_VBL 16 /* activate values on next vbl */
+#define FB_CHANGE_CMAP_VBL 32 /* change colormap on vbl */
+#define FB_ACTIVATE_ALL 64 /* change all VCs on this fb */
+
+#define FB_ACCELF_TEXT 1 /* text mode acceleration */
+
+#define FB_SYNC_HOR_HIGH_ACT 1 /* horizontal sync high active */
+#define FB_SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */
+#define FB_SYNC_EXT 4 /* external sync */
+#define FB_SYNC_COMP_HIGH_ACT 8 /* composite sync high active */
+#define FB_SYNC_BROADCAST 16 /* broadcast video timings */
+ /* vtotal = 144d/288n/576i => PAL */
+ /* vtotal = 121d/242n/484i => NTSC */
+#define FB_SYNC_ON_GREEN 32 /* sync on green */
+
+#define FB_VMODE_NONINTERLACED 0 /* non interlaced */
+#define FB_VMODE_INTERLACED 1 /* interlaced */
+#define FB_VMODE_DOUBLE 2 /* double scan */
+#define FB_VMODE_MASK 255
+
+#define FB_VMODE_YWRAP 256 /* ywrap instead of panning */
+#define FB_VMODE_SMOOTH_XPAN 512 /* smooth xpan possible (internally used) */
+#define FB_VMODE_CONUPDATE 512 /* don't update x/yoffset */
+
+struct fb_var_screeninfo {
+ __u32 xres; /* visible resolution */
+ __u32 yres;
+ __u32 xres_virtual; /* virtual resolution */
+ __u32 yres_virtual;
+ __u32 xoffset; /* offset from virtual to visible */
+ __u32 yoffset; /* resolution */
+
+ __u32 bits_per_pixel; /* guess what */
+ __u32 grayscale; /* != 0 Graylevels instead of colors */
+
+ struct fb_bitfield red; /* bitfield in fb mem if true color, */
+ struct fb_bitfield green; /* else only length is significant */
+ struct fb_bitfield blue;
+ struct fb_bitfield transp; /* transparency */
+
+ __u32 nonstd; /* != 0 Non standard pixel format */
+
+ __u32 activate; /* see FB_ACTIVATE_* */
+
+ __u32 height; /* height of picture in mm */
+ __u32 width; /* width of picture in mm */
+
+ __u32 accel_flags; /* acceleration flags (hints) */
+
+ /* Timing: All values in pixclocks, except pixclock (of course) */
+ __u32 pixclock; /* pixel clock in ps (pico seconds) */
+ __u32 left_margin; /* time from sync to picture */
+ __u32 right_margin; /* time from picture to sync */
+ __u32 upper_margin; /* time from sync to picture */
+ __u32 lower_margin;
+ __u32 hsync_len; /* length of horizontal sync */
+ __u32 vsync_len; /* length of vertical sync */
+ __u32 sync; /* see FB_SYNC_* */
+ __u32 vmode; /* see FB_VMODE_* */
+ __u32 reserved[6]; /* Reserved for future compatibility */
+};
+
+struct fb_cmap {
+ __u32 start; /* First entry */
+ __u32 len; /* Number of entries */
+ __u16 *red; /* Red values */
+ __u16 *green;
+ __u16 *blue;
+ __u16 *transp; /* transparency, can be NULL */
+};
+
+struct fb_con2fbmap {
+ __u32 console;
+ __u32 framebuffer;
+};
+
+struct fb_monspecs {
+ __u32 hfmin; /* hfreq lower limit (Hz) */
+ __u32 hfmax; /* hfreq upper limit (Hz) */
+ __u16 vfmin; /* vfreq lower limit (Hz) */
+ __u16 vfmax; /* vfreq upper limit (Hz) */
+ unsigned dpms : 1; /* supports DPMS */
+};
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+
+
+struct fb_info;
+struct fb_info_gen;
+struct vm_area_struct;
+struct file;
+
+ /*
+ * Frame buffer operations
+ */
+
+struct fb_ops {
+ /* open/release and usage marking */
+ int (*fb_open)(struct fb_info *info, int user);
+ int (*fb_release)(struct fb_info *info, int user);
+ /* get non settable parameters */
+ int (*fb_get_fix)(struct fb_fix_screeninfo *fix, int con,
+ struct fb_info *info);
+ /* get settable parameters */
+ int (*fb_get_var)(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info);
+ /* set settable parameters */
+ int (*fb_set_var)(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info);
+ /* get colormap */
+ int (*fb_get_cmap)(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info);
+ /* set colormap */
+ int (*fb_set_cmap)(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info);
+ /* pan display */
+ int (*fb_pan_display)(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info);
+ /* perform fb specific ioctl */
+ int (*fb_ioctl)(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg, int con, struct fb_info *info);
+ /* perform fb specific mmap */
+ int (*fb_mmap)(struct fb_info *info, struct file *file, struct vm_area_struct *vma);
+ /* switch to/from raster image mode */
+ int (*fb_rasterimg)(struct fb_info *info, int start);
+};
+
+
+ /*
+ * This is the interface between the low-level console driver and the
+ * low-level frame buffer device
+ */
+
+struct display {
+ /* Filled in by the frame buffer device */
+
+ struct fb_var_screeninfo var; /* variable infos. yoffset and vmode */
+ /* are updated by fbcon.c */
+ struct fb_cmap cmap; /* colormap */
+ char *screen_base; /* pointer to top of virtual screen */
+ /* (virtual address) */
+ int visual;
+ int type; /* see FB_TYPE_* */
+ int type_aux; /* Interleave for interleaved Planes */
+ u_short ypanstep; /* zero if no hardware ypan */
+ u_short ywrapstep; /* zero if no hardware ywrap */
+ u_long line_length; /* length of a line in bytes */
+ u_short can_soft_blank; /* zero if no hardware blanking */
+ u_short inverse; /* != 0 text black on white as default */
+ struct display_switch *dispsw; /* low level operations */
+ void *dispsw_data; /* optional dispsw helper data */
+
+#if 0
+ struct fb_fix_cursorinfo fcrsr;
+ struct fb_var_cursorinfo *vcrsr;
+ struct fb_cursorstate crsrstate;
+#endif
+
+ /* Filled in by the low-level console driver */
+
+ struct vc_data *conp; /* pointer to console data */
+ struct fb_info *fb_info; /* frame buffer for this console */
+ int vrows; /* number of virtual rows */
+ unsigned short cursor_x; /* current cursor position */
+ unsigned short cursor_y;
+ int fgcol; /* text colors */
+ int bgcol;
+ u_long next_line; /* offset to one line below */
+ u_long next_plane; /* offset to next plane */
+ u_char *fontdata; /* Font associated to this display */
+ unsigned short _fontheightlog;
+ unsigned short _fontwidthlog;
+ unsigned short _fontheight;
+ unsigned short _fontwidth;
+ int userfont; /* != 0 if fontdata kmalloc()ed */
+ u_short scrollmode; /* Scroll Method */
+ short yscroll; /* Hardware scrolling */
+ unsigned char fgshift, bgshift;
+ unsigned short charmask; /* 0xff or 0x1ff */
+};
+
+
+struct fb_info {
+ char modename[40]; /* default video mode */
+ kdev_t node;
+ int flags;
+#define FBINFO_FLAG_MODULE 1 /* Low-level driver is a module */
+ struct fb_ops *fbops;
+ struct fb_monspecs monspecs;
+ struct display *disp; /* initial display variable */
+ struct vc_data *display_fg; /* Console visible on this display */
+ char fontname[40]; /* default font name */
+ int (*changevar)(int); /* tell console var has changed */
+ int (*switch_con)(int, struct fb_info*);
+ /* tell fb to switch consoles */
+ int (*updatevar)(int, struct fb_info*);
+ /* tell fb to update the vars */
+ void (*blank)(int, struct fb_info*); /* tell fb to (un)blank the screen */
+ /* arg = 0: unblank */
+ /* arg > 0: VESA level (arg-1) */
+
+ /* From here on everything is device dependent */
+};
+
+#ifdef MODULE
+#define FBINFO_FLAG_DEFAULT FBINFO_FLAG_MODULE
+#else
+#define FBINFO_FLAG_DEFAULT 0
+#endif
+
+ /*
+ * This structure abstracts from the underlying hardware. It is not
+ * mandatory but used by the `generic' frame buffer operations.
+ * Read drivers/video/skeletonfb.c for more information.
+ */
+
+struct fbgen_hwswitch {
+ void (*detect)(void);
+ int (*encode_fix)(struct fb_fix_screeninfo *fix, const void *par,
+ struct fb_info_gen *info);
+ int (*decode_var)(const struct fb_var_screeninfo *var, void *par,
+ struct fb_info_gen *info);
+ int (*encode_var)(struct fb_var_screeninfo *var, const void *par,
+ struct fb_info_gen *info);
+ void (*get_par)(void *par, struct fb_info_gen *info);
+ void (*set_par)(const void *par, struct fb_info_gen *info);
+ int (*getcolreg)(unsigned regno, unsigned *red, unsigned *green,
+ unsigned *blue, unsigned *transp, struct fb_info *info);
+ int (*setcolreg)(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp, struct fb_info *info);
+ int (*pan_display)(const struct fb_var_screeninfo *var,
+ struct fb_info_gen *info);
+ int (*blank)(int blank_mode, struct fb_info_gen *info);
+ void (*set_disp)(const void *par, struct display *disp,
+ struct fb_info_gen *info);
+};
+
+struct fb_info_gen {
+ struct fb_info info;
+
+ /* Entries for a generic frame buffer device */
+ /* Yes, this starts looking like C++ */
+ u_int parsize;
+ struct fbgen_hwswitch *fbhw;
+
+ /* From here on everything is device dependent */
+};
+
+ /*
+ * `Generic' versions of the frame buffer device operations
+ */
+
+extern int fbgen_get_fix(struct fb_fix_screeninfo *fix, int con,
+ struct fb_info *info);
+extern int fbgen_get_var(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info);
+extern int fbgen_set_var(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info);
+extern int fbgen_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info);
+extern int fbgen_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info);
+extern int fbgen_pan_display(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info);
+extern int fbgen_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg, int con,
+ struct fb_info *info);
+
+ /*
+ * Helper functions
+ */
+
+extern int fbgen_do_set_var(struct fb_var_screeninfo *var, int isactive,
+ struct fb_info_gen *info);
+extern void fbgen_set_disp(int con, struct fb_info_gen *info);
+extern void fbgen_install_cmap(int con, struct fb_info_gen *info);
+extern int fbgen_update_var(int con, struct fb_info *info);
+extern int fbgen_switch(int con, struct fb_info *info);
+extern void fbgen_blank(int blank, struct fb_info *info);
+
+
+struct fb_videomode {
+ const char *name;
+ struct fb_var_screeninfo var;
+};
+
+
+/* drivers/char/fbmem.c */
+extern int register_framebuffer(struct fb_info *fb_info);
+extern int unregister_framebuffer(const struct fb_info *fb_info);
+extern int fbmon_valid_timings(u_int pixclock, u_int htotal, u_int vtotal,
+ const struct fb_info *fb_info);
+extern int fbmon_dpms(const struct fb_info *fb_info);
+
+
+extern int num_registered_fb;
+extern struct fb_info *registered_fb[FB_MAX];
+extern char con2fb_map[MAX_NR_CONSOLES];
+
+/* drivers/video/fbcon.c */
+extern struct display fb_display[MAX_NR_CONSOLES];
+
+/* drivers/video/fbcmap.c */
+extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp);
+extern void fb_copy_cmap(struct fb_cmap *from, struct fb_cmap *to,
+ int fsfromto);
+extern int fb_get_cmap(struct fb_cmap *cmap, int kspc,
+ int (*getcolreg)(u_int, u_int *, u_int *, u_int *,
+ u_int *, struct fb_info *),
+ struct fb_info *fb_info);
+extern int fb_set_cmap(struct fb_cmap *cmap, int kspc,
+ int (*setcolreg)(u_int, u_int, u_int, u_int, u_int,
+ struct fb_info *),
+ struct fb_info *fb_info);
+extern struct fb_cmap *fb_default_cmap(int len);
+extern void fb_invert_cmaps(void);
+
+/* VESA Blanking Levels */
+#define VESA_NO_BLANKING 0
+#define VESA_VSYNC_SUSPEND 1
+#define VESA_HSYNC_SUSPEND 2
+#define VESA_POWERDOWN 3
+
+#endif /* __KERNEL__ */
+
+#if 1
+
+#define FBCMD_GET_CURRENTPAR 0xDEAD0005
+#define FBCMD_SET_CURRENTPAR 0xDEAD8005
+
+#endif
+
+
+#if 1 /* Preliminary */
+
+ /*
+ * Hardware Cursor
+ */
+
+#define FBIOGET_FCURSORINFO 0x4607
+#define FBIOGET_VCURSORINFO 0x4608
+#define FBIOPUT_VCURSORINFO 0x4609
+#define FBIOGET_CURSORSTATE 0x460A
+#define FBIOPUT_CURSORSTATE 0x460B
+
+
+struct fb_fix_cursorinfo {
+ __u16 crsr_width; /* width and height of the cursor in */
+ __u16 crsr_height; /* pixels (zero if no cursor) */
+ __u16 crsr_xsize; /* cursor size in display pixels */
+ __u16 crsr_ysize;
+ __u16 crsr_color1; /* colormap entry for cursor color1 */
+ __u16 crsr_color2; /* colormap entry for cursor color2 */
+};
+
+struct fb_var_cursorinfo {
+ __u16 width;
+ __u16 height;
+ __u16 xspot;
+ __u16 yspot;
+ __u8 data[1]; /* field with [height][width] */
+};
+
+struct fb_cursorstate {
+ __s16 xoffset;
+ __s16 yoffset;
+ __u16 mode;
+};
+
+#define FB_CURSOR_OFF 0
+#define FB_CURSOR_ON 1
+#define FB_CURSOR_FLASH 2
+
+#endif /* Preliminary */
+
+#endif /* _LINUX_FB_H */
diff --git a/pfinet/linux-src/include/linux/fcdevice.h b/pfinet/linux-src/include/linux/fcdevice.h
new file mode 100644
index 00000000..22a1e454
--- /dev/null
+++ b/pfinet/linux-src/include/linux/fcdevice.h
@@ -0,0 +1,40 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. NET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the Fibre Channel handlers.
+ *
+ * Version: @(#)fcdevice.h 1.0.0 09/26/98
+ *
+ * Authors: Vineet Abraham <vma@iol.unh.edu>
+ *
+ * Relocated to include/linux where it belongs by Alan Cox
+ * <gw4pts@gw4pts.ampr.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * WARNING: This move may well be temporary. This file will get merged with others RSN.
+ *
+ */
+#ifndef _LINUX_FCDEVICE_H
+#define _LINUX_FCDEVICE_H
+
+
+#include <linux/if_fc.h>
+
+#ifdef __KERNEL__
+extern int fc_header(struct sk_buff *skb, struct device *dev,
+ unsigned short type, void *daddr,
+ void *saddr, unsigned len);
+extern int fc_rebuild_header(struct sk_buff *skb);
+//extern unsigned short fc_type_trans(struct sk_buff *skb, struct device *dev);
+
+extern struct device * init_fcdev(struct device *, int);
+
+#endif
+
+#endif /* _LINUX_FCDEVICE_H */
diff --git a/pfinet/linux-src/include/linux/fcntl.h b/pfinet/linux-src/include/linux/fcntl.h
new file mode 100644
index 00000000..9de3512e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/fcntl.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_FCNTL_H
+#define _LINUX_FCNTL_H
+
+#include <asm/fcntl.h>
+
+#endif
diff --git a/pfinet/linux-src/include/linux/fd.h b/pfinet/linux-src/include/linux/fd.h
new file mode 100644
index 00000000..c0ed2792
--- /dev/null
+++ b/pfinet/linux-src/include/linux/fd.h
@@ -0,0 +1,378 @@
+#ifndef _LINUX_FD_H
+#define _LINUX_FD_H
+
+#include <linux/ioctl.h>
+
+/* New file layout: Now the ioctl definitions immediately follow the
+ * definitions of the structures that they use */
+
+/*
+ * Geometry
+ */
+struct floppy_struct {
+ unsigned int size, /* nr of sectors total */
+ sect, /* sectors per track */
+ head, /* nr of heads */
+ track, /* nr of tracks */
+ stretch; /* !=0 means double track steps */
+#define FD_STRETCH 1
+#define FD_SWAPSIDES 2
+
+ unsigned char gap, /* gap1 size */
+
+ rate, /* data rate. |= 0x40 for perpendicular */
+#define FD_2M 0x4
+#define FD_SIZECODEMASK 0x38
+#define FD_SIZECODE(floppy) (((((floppy)->rate&FD_SIZECODEMASK)>> 3)+ 2) %8)
+#define FD_SECTSIZE(floppy) ( (floppy)->rate & FD_2M ? \
+ 512 : 128 << FD_SIZECODE(floppy) )
+#define FD_PERP 0x40
+
+ spec1, /* stepping rate, head unload time */
+ fmt_gap; /* gap2 size */
+ const char * name; /* used only for predefined formats */
+};
+
+
+/* commands needing write access have 0x40 set */
+/* commands needing super user access have 0x80 set */
+
+#define FDCLRPRM _IO(2, 0x41)
+/* clear user-defined parameters */
+
+#define FDSETPRM _IOW(2, 0x42, struct floppy_struct)
+#define FDSETMEDIAPRM FDSETPRM
+/* set user-defined parameters for current media */
+
+#define FDDEFPRM _IOW(2, 0x43, struct floppy_struct)
+#define FDGETPRM _IOR(2, 0x04, struct floppy_struct)
+#define FDDEFMEDIAPRM FDDEFPRM
+#define FDGETMEDIAPRM FDGETPRM
+/* set/get disk parameters */
+
+
+#define FDMSGON _IO(2,0x45)
+#define FDMSGOFF _IO(2,0x46)
+/* issue/don't issue kernel messages on media type change */
+
+
+/*
+ * Formatting (obsolete)
+ */
+#define FD_FILL_BYTE 0xF6 /* format fill byte. */
+
+struct format_descr {
+ unsigned int device,head,track;
+};
+
+#define FDFMTBEG _IO(2,0x47)
+/* begin formatting a disk */
+#define FDFMTTRK _IOW(2,0x48, struct format_descr)
+/* format the specified track */
+#define FDFMTEND _IO(2,0x49)
+/* end formatting a disk */
+
+
+/*
+ * Error thresholds
+ */
+struct floppy_max_errors {
+ unsigned int
+ abort, /* number of errors to be reached before aborting */
+ read_track, /* maximal number of errors permitted to read an
+ * entire track at once */
+ reset, /* maximal number of errors before a reset is tried */
+ recal, /* maximal number of errors before a recalibrate is
+ * tried */
+
+ /*
+ * Threshold for reporting FDC errors to the console.
+ * Setting this to zero may flood your screen when using
+ * ultra cheap floppies ;-)
+ */
+ reporting;
+
+};
+
+#define FDSETEMSGTRESH _IO(2,0x4a)
+/* set fdc error reporting threshold */
+
+#define FDFLUSH _IO(2,0x4b)
+/* flush buffers for media; either for verifying media, or for
+ * handling a media change without closing the file descriptor */
+
+#define FDSETMAXERRS _IOW(2, 0x4c, struct floppy_max_errors)
+#define FDGETMAXERRS _IOR(2, 0x0e, struct floppy_max_errors)
+/* set/get abortion and read_track threshold. See also floppy_drive_params
+ * structure */
+
+
+typedef char floppy_drive_name[16];
+#define FDGETDRVTYP _IOR(2, 0x0f, floppy_drive_name)
+/* get drive type: 5 1/4 or 3 1/2 */
+
+
+/*
+ * Drive parameters (user modifiable)
+ */
+struct floppy_drive_params {
+ signed char cmos; /* CMOS type */
+
+ /* Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms
+ * etc) and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA).
+ */
+ unsigned long max_dtr; /* Step rate, usec */
+ unsigned long hlt; /* Head load/settle time, msec */
+ unsigned long hut; /* Head unload time (remnant of
+ * 8" drives) */
+ unsigned long srt; /* Step rate, usec */
+
+ unsigned long spinup; /* time needed for spinup (expressed
+ * in jiffies) */
+ unsigned long spindown; /* timeout needed for spindown */
+ unsigned char spindown_offset; /* decides in which position the disk
+ * will stop */
+ unsigned char select_delay; /* delay to wait after select */
+ unsigned char rps; /* rotations per second */
+ unsigned char tracks; /* maximum number of tracks */
+ unsigned long timeout; /* timeout for interrupt requests */
+
+ unsigned char interleave_sect; /* if there are more sectors, use
+ * interleave */
+
+ struct floppy_max_errors max_errors;
+
+ char flags; /* various flags, including ftd_msg */
+/*
+ * Announce successful media type detection and media information loss after
+ * disk changes.
+ * Also used to enable/disable printing of overrun warnings.
+ */
+
+#define FTD_MSG 0x10
+#define FD_BROKEN_DCL 0x20
+#define FD_DEBUG 0x02
+#define FD_SILENT_DCL_CLEAR 0x4
+#define FD_INVERTED_DCL 0x80 /* must be 0x80, because of hardware
+ considerations */
+
+ char read_track; /* use readtrack during probing? */
+
+/*
+ * Auto-detection. Each drive type has eight formats which are
+ * used in succession to try to read the disk. If the FDC cannot lock onto
+ * the disk, the next format is tried. This uses the variable 'probing'.
+ */
+ short autodetect[8]; /* autodetected formats */
+
+ int checkfreq; /* how often should the drive be checked for disk
+ * changes */
+ int native_format; /* native format of this drive */
+};
+
+enum {
+ FD_NEED_TWADDLE_BIT, /* more magic */
+ FD_VERIFY_BIT, /* inquire for write protection */
+ FD_DISK_NEWCHANGE_BIT, /* change detected, and no action undertaken yet
+ * to clear media change status */
+ FD_UNUSED_BIT,
+ FD_DISK_CHANGED_BIT, /* disk has been changed since last i/o */
+ FD_DISK_WRITABLE_BIT /* disk is writable */
+};
+
+#define FDSETDRVPRM _IOW(2, 0x90, struct floppy_drive_params)
+#define FDGETDRVPRM _IOR(2, 0x11, struct floppy_drive_params)
+/* set/get drive parameters */
+
+
+/*
+ * Current drive state (not directly modifiable by user, readonly)
+ */
+struct floppy_drive_struct {
+ unsigned long flags;
+/* values for these flags */
+#define FD_NEED_TWADDLE (1 << FD_NEED_TWADDLE_BIT)
+#define FD_VERIFY (1 << FD_VERIFY_BIT)
+#define FD_DISK_NEWCHANGE (1 << FD_DISK_NEWCHANGE_BIT)
+#define FD_DISK_CHANGED (1 << FD_DISK_CHANGED_BIT)
+#define FD_DISK_WRITABLE (1 << FD_DISK_WRITABLE_BIT)
+
+ unsigned long spinup_date;
+ unsigned long select_date;
+ unsigned long first_read_date;
+ short probed_format;
+ short track; /* current track */
+ short maxblock; /* id of highest block read */
+ short maxtrack; /* id of highest half track read */
+ int generation; /* how many diskchanges? */
+
+/*
+ * (User-provided) media information is _not_ discarded after a media change
+ * if the corresponding keep_data flag is non-zero. Positive values are
+ * decremented after each probe.
+ */
+ int keep_data;
+
+ /* Prevent "aliased" accesses. */
+ int fd_ref;
+ int fd_device;
+ unsigned long last_checked; /* when was the drive last checked for a disk
+ * change? */
+
+ char *dmabuf;
+ int bufblocks;
+};
+
+#define FDGETDRVSTAT _IOR(2, 0x12, struct floppy_drive_struct)
+#define FDPOLLDRVSTAT _IOR(2, 0x13, struct floppy_drive_struct)
+/* get drive state: GET returns the cached state, POLL polls for new state */
+
+
+/*
+ * reset FDC
+ */
+enum reset_mode {
+ FD_RESET_IF_NEEDED, /* reset only if the reset flags is set */
+ FD_RESET_IF_RAWCMD, /* obsolete */
+ FD_RESET_ALWAYS /* reset always */
+};
+#define FDRESET _IO(2, 0x54)
+
+
+/*
+ * FDC state
+ */
+struct floppy_fdc_state {
+ int spec1; /* spec1 value last used */
+ int spec2; /* spec2 value last used */
+ int dtr;
+ unsigned char version; /* FDC version code */
+ unsigned char dor;
+ unsigned long address; /* io address */
+ unsigned int rawcmd:2;
+ unsigned int reset:1;
+ unsigned int need_configure:1;
+ unsigned int perp_mode:2;
+ unsigned int has_fifo:1;
+ unsigned int driver_version; /* version code for floppy driver */
+#define FD_DRIVER_VERSION 0x100
+/* user programs using the floppy API should use floppy_fdc_state to
+ * get the version number of the floppy driver that they are running
+ * on. If this version number is bigger than the one compiled into the
+ * user program (the FD_DRIVER_VERSION define), it should be prepared
+ * to bigger structures
+ */
+
+ unsigned char track[4];
+ /* Position of the heads of the 4 units attached to this FDC,
+ * as stored on the FDC. In the future, the position as stored
+ * on the FDC might not agree with the actual physical
+ * position of these drive heads. By allowing such
+ * disagreement, it will be possible to reset the FDC without
+ * incurring the expensive cost of repositioning all heads.
+ * Right now, these positions are hard wired to 0. */
+
+};
+
+#define FDGETFDCSTAT _IOR(2, 0x15, struct floppy_fdc_state)
+
+
+/*
+ * Asynchronous Write error tracking
+ */
+struct floppy_write_errors {
+ /* Write error logging.
+ *
+ * These fields can be cleared with the FDWERRORCLR ioctl.
+ * Only writes that were attempted but failed due to a physical media
+ * error are logged. write(2) calls that fail and return an error code
+ * to the user process are not counted.
+ */
+
+ unsigned int write_errors; /* number of physical write errors
+ * encountered */
+
+ /* position of first and last write errors */
+ unsigned long first_error_sector;
+ int first_error_generation;
+ unsigned long last_error_sector;
+ int last_error_generation;
+
+ unsigned int badness; /* highest retry count for a read or write
+ * operation */
+};
+
+#define FDWERRORCLR _IO(2, 0x56)
+/* clear write error and badness information */
+#define FDWERRORGET _IOR(2, 0x17, struct floppy_write_errors)
+/* get write error and badness information */
+
+
+/*
+ * Raw commands
+ */
+/* new interface flag: now we can do them in batches */
+#define FDHAVEBATCHEDRAWCMD
+
+struct floppy_raw_cmd {
+ unsigned int flags;
+#define FD_RAW_READ 1
+#define FD_RAW_WRITE 2
+#define FD_RAW_NO_MOTOR 4
+#define FD_RAW_DISK_CHANGE 4 /* out: disk change flag was set */
+#define FD_RAW_INTR 8 /* wait for an interrupt */
+#define FD_RAW_SPIN 0x10 /* spin up the disk for this command */
+#define FD_RAW_NO_MOTOR_AFTER 0x20 /* switch the motor off after command
+ * completion */
+#define FD_RAW_NEED_DISK 0x40 /* this command needs a disk to be present */
+#define FD_RAW_NEED_SEEK 0x80 /* this command uses an implied seek (soft) */
+
+/* more "in" flags */
+#define FD_RAW_MORE 0x100 /* more records follow */
+#define FD_RAW_STOP_IF_FAILURE 0x200 /* stop if we encounter a failure */
+#define FD_RAW_STOP_IF_SUCCESS 0x400 /* stop if command successful */
+#define FD_RAW_SOFTFAILURE 0x800 /* consider the return value for failure
+ * detection too */
+
+/* more "out" flags */
+#define FD_RAW_FAILURE 0x10000 /* command sent to fdc, fdc returned error */
+#define FD_RAW_HARDFAILURE 0x20000 /* fdc had to be reset, or timed out */
+
+ void *data;
+ char *kernel_data; /* location of data buffer in the kernel */
+ struct floppy_raw_cmd *next; /* used for chaining of raw cmd's
+ * within the kernel */
+ long length; /* in: length of dma transfer. out: remaining bytes */
+ long phys_length; /* physical length, if different from dma length */
+ int buffer_length; /* length of allocated buffer */
+
+ unsigned char rate;
+ unsigned char cmd_count;
+ unsigned char cmd[16];
+ unsigned char reply_count;
+ unsigned char reply[16];
+ int track;
+ int resultcode;
+
+ int reserved1;
+ int reserved2;
+};
+
+#define FDRAWCMD _IO(2, 0x58)
+/* send a raw command to the fdc. Structure size not included, because of
+ * batches */
+
+#define FDTWADDLE _IO(2, 0x59)
+/* flicker motor-on bit before reading a sector. Experimental */
+
+
+#define FDEJECT _IO(2, 0x5a)
+/* eject the disk */
+
+
+#ifdef __KERNEL__
+/* eject the boot floppy (if we need the drive for a different root floppy) */
+void floppy_eject(void);
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/fddidevice.h b/pfinet/linux-src/include/linux/fddidevice.h
new file mode 100644
index 00000000..a093ccf7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/fddidevice.h
@@ -0,0 +1,39 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the FDDI handlers.
+ *
+ * Version: @(#)fddidevice.h 1.0.0 08/12/96
+ *
+ * Author: Lawrence V. Stefani, <stefani@lkg.dec.com>
+ *
+ * fddidevice.h is based on previous trdevice.h work by
+ * Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_FDDIDEVICE_H
+#define _LINUX_FDDIDEVICE_H
+
+#include <linux/if_fddi.h>
+
+#ifdef __KERNEL__
+extern int fddi_header(struct sk_buff *skb,
+ struct device *dev,
+ unsigned short type,
+ void *daddr,
+ void *saddr,
+ unsigned len);
+extern int fddi_rebuild_header(struct sk_buff *skb);
+extern unsigned short fddi_type_trans(struct sk_buff *skb,
+ struct device *dev);
+#endif
+
+#endif /* _LINUX_FDDIDEVICE_H */
diff --git a/pfinet/linux-src/include/linux/fdreg.h b/pfinet/linux-src/include/linux/fdreg.h
new file mode 100644
index 00000000..1d9026ee
--- /dev/null
+++ b/pfinet/linux-src/include/linux/fdreg.h
@@ -0,0 +1,143 @@
+#ifndef _LINUX_FDREG_H
+#define _LINUX_FDREG_H
+/*
+ * This file contains some defines for the floppy disk controller.
+ * Various sources. Mostly "IBM Microcomputers: A Programmers
+ * Handbook", Sanches and Canton.
+ */
+
+#ifdef FDPATCHES
+
+#define FD_IOPORT fdc_state[fdc].address
+
+/* Fd controller regs. S&C, about page 340 */
+#define FD_STATUS (4 + FD_IOPORT )
+#define FD_DATA (5 + FD_IOPORT )
+
+/* Digital Output Register */
+#define FD_DOR (2 + FD_IOPORT )
+
+/* Digital Input Register (read) */
+#define FD_DIR (7 + FD_IOPORT )
+
+/* Diskette Control Register (write)*/
+#define FD_DCR (7 + FD_IOPORT )
+
+#else
+
+#define FD_STATUS 0x3f4
+#define FD_DATA 0x3f5
+#define FD_DOR 0x3f2 /* Digital Output Register */
+#define FD_DIR 0x3f7 /* Digital Input Register (read) */
+#define FD_DCR 0x3f7 /* Diskette Control Register (write)*/
+
+#endif
+
+/* Bits of main status register */
+#define STATUS_BUSYMASK 0x0F /* drive busy mask */
+#define STATUS_BUSY 0x10 /* FDC busy */
+#define STATUS_DMA 0x20 /* 0- DMA mode */
+#define STATUS_DIR 0x40 /* 0- cpu->fdc */
+#define STATUS_READY 0x80 /* Data reg ready */
+
+/* Bits of FD_ST0 */
+#define ST0_DS 0x03 /* drive select mask */
+#define ST0_HA 0x04 /* Head (Address) */
+#define ST0_NR 0x08 /* Not Ready */
+#define ST0_ECE 0x10 /* Equipment check error */
+#define ST0_SE 0x20 /* Seek end */
+#define ST0_INTR 0xC0 /* Interrupt code mask */
+
+/* Bits of FD_ST1 */
+#define ST1_MAM 0x01 /* Missing Address Mark */
+#define ST1_WP 0x02 /* Write Protect */
+#define ST1_ND 0x04 /* No Data - unreadable */
+#define ST1_OR 0x10 /* OverRun */
+#define ST1_CRC 0x20 /* CRC error in data or addr */
+#define ST1_EOC 0x80 /* End Of Cylinder */
+
+/* Bits of FD_ST2 */
+#define ST2_MAM 0x01 /* Missing Address Mark (again) */
+#define ST2_BC 0x02 /* Bad Cylinder */
+#define ST2_SNS 0x04 /* Scan Not Satisfied */
+#define ST2_SEH 0x08 /* Scan Equal Hit */
+#define ST2_WC 0x10 /* Wrong Cylinder */
+#define ST2_CRC 0x20 /* CRC error in data field */
+#define ST2_CM 0x40 /* Control Mark = deleted */
+
+/* Bits of FD_ST3 */
+#define ST3_HA 0x04 /* Head (Address) */
+#define ST3_DS 0x08 /* drive is double-sided */
+#define ST3_TZ 0x10 /* Track Zero signal (1=track 0) */
+#define ST3_RY 0x20 /* drive is ready */
+#define ST3_WP 0x40 /* Write Protect */
+#define ST3_FT 0x80 /* Drive Fault */
+
+/* Values for FD_COMMAND */
+#define FD_RECALIBRATE 0x07 /* move to track 0 */
+#define FD_SEEK 0x0F /* seek track */
+#define FD_READ 0xE6 /* read with MT, MFM, SKip deleted */
+#define FD_WRITE 0xC5 /* write with MT, MFM */
+#define FD_SENSEI 0x08 /* Sense Interrupt Status */
+#define FD_SPECIFY 0x03 /* specify HUT etc */
+#define FD_FORMAT 0x4D /* format one track */
+#define FD_VERSION 0x10 /* get version code */
+#define FD_CONFIGURE 0x13 /* configure FIFO operation */
+#define FD_PERPENDICULAR 0x12 /* perpendicular r/w mode */
+#define FD_GETSTATUS 0x04 /* read ST3 */
+#define FD_DUMPREGS 0x0E /* dump the contents of the fdc regs */
+#define FD_READID 0xEA /* prints the header of a sector */
+#define FD_UNLOCK 0x14 /* Fifo config unlock */
+#define FD_LOCK 0x94 /* Fifo config lock */
+#define FD_RSEEK_OUT 0x8f /* seek out (i.e. to lower tracks) */
+#define FD_RSEEK_IN 0xcf /* seek in (i.e. to higher tracks) */
+
+/* the following commands are new in the 82078. They are not used in the
+ * floppy driver, except the first three. These commands may be useful for apps
+ * which use the FDRAWCMD interface. For doc, get the 82078 spec sheets at
+ * http://www-techdoc.intel.com/docs/periph/fd_contr/datasheets/ */
+
+#define FD_PARTID 0x18 /* part id ("extended" version cmd) */
+#define FD_SAVE 0x2e /* save fdc regs for later restore */
+#define FD_DRIVESPEC 0x8e /* drive specification: Access to the
+ * 2 Mbps data transfer rate for tape
+ * drives */
+
+#define FD_RESTORE 0x4e /* later restore */
+#define FD_POWERDOWN 0x27 /* configure FDC's powersave features */
+#define FD_FORMAT_N_WRITE 0xef /* format and write in one go. */
+#define FD_OPTION 0x33 /* ISO format (which is a clean way to
+ * pack more sectors on a track) */
+
+/* DMA commands */
+#define DMA_READ 0x46
+#define DMA_WRITE 0x4A
+
+/* FDC version return types */
+#define FDC_NONE 0x00
+#define FDC_UNKNOWN 0x10 /* DO NOT USE THIS TYPE EXCEPT IF IDENTIFICATION
+ FAILS EARLY */
+#define FDC_8272A 0x20 /* Intel 8272a, NEC 765 */
+#define FDC_765ED 0x30 /* Non-Intel 1MB-compatible FDC, can't detect */
+#define FDC_82072 0x40 /* Intel 82072; 8272a + FIFO + DUMPREGS */
+#define FDC_82072A 0x45 /* 82072A (on Sparcs) */
+#define FDC_82077_ORIG 0x51 /* Original version of 82077AA, sans LOCK */
+#define FDC_82077 0x52 /* 82077AA-1 */
+#define FDC_82078_UNKN 0x5f /* Unknown 82078 variant */
+#define FDC_82078 0x60 /* 44pin 82078 or 64pin 82078SL */
+#define FDC_82078_1 0x61 /* 82078-1 (2Mbps fdc) */
+#define FDC_S82078B 0x62 /* S82078B (first seen on Adaptec AVA-2825 VLB
+ * SCSI/EIDE/Floppy controller) */
+#define FDC_87306 0x63 /* National Semiconductor PC 87306 */
+
+/*
+ * Beware: the fdc type list is roughly sorted by increasing features.
+ * Presence of features is tested by comparing the FDC version id with the
+ * "oldest" version that has the needed feature.
+ * If during FDC detection, an obscure test fails late in the sequence, don't
+ * assign FDC_UNKNOWN. Else the FDC will be treated as a dumb 8272a, or worse.
+ * This is especially true if the tests are unneeded.
+ */
+
+#define FD_RESET_DELAY 20
+#endif
diff --git a/pfinet/linux-src/include/linux/file.h b/pfinet/linux-src/include/linux/file.h
new file mode 100644
index 00000000..05f388f0
--- /dev/null
+++ b/pfinet/linux-src/include/linux/file.h
@@ -0,0 +1,71 @@
+/*
+ * Wrapper functions for accessing the file_struct fd array.
+ */
+
+#ifndef __LINUX_FILE_H
+#define __LINUX_FILE_H
+
+extern void __fput(struct file *);
+
+/*
+ * Check whether the specified task has the fd open. Since the task
+ * may not have a files_struct, we must test for p->files != NULL.
+ */
+extern inline struct file * fcheck_task(struct task_struct *p, unsigned int fd)
+{
+ struct file * file = NULL;
+
+ if (p->files && fd < p->files->max_fds)
+ file = p->files->fd[fd];
+ return file;
+}
+
+/*
+ * Check whether the specified fd has an open file.
+ */
+extern inline struct file * fcheck(unsigned int fd)
+{
+ struct file * file = NULL;
+
+ if (fd < current->files->max_fds)
+ file = current->files->fd[fd];
+ return file;
+}
+
+extern inline struct file * fget(unsigned int fd)
+{
+ struct file * file = fcheck(fd);
+
+ if (file)
+ file->f_count++;
+ return file;
+}
+
+/*
+ * Install a file pointer in the fd array.
+ */
+extern inline void fd_install(unsigned int fd, struct file *file)
+{
+ current->files->fd[fd] = file;
+}
+
+/*
+ * 23/12/1998 Marcin Dalecki <dalecki@cs.net.pl>:
+ *
+ * Since those functions where calling other functions, it was compleatly
+ * bogous to make them all "extern inline".
+ *
+ * The removal of this pseudo optimization saved me scandaleous:
+ *
+ * 3756 (i386 arch)
+ *
+ * precious bytes from my kernel, even without counting all the code compiled
+ * as module!
+ *
+ * I suspect there are many other similar "optimizations" across the
+ * kernel...
+ */
+extern void fput(struct file *file);
+extern void put_filp(struct file *file);
+
+#endif
diff --git a/pfinet/linux-src/include/linux/filter.h b/pfinet/linux-src/include/linux/filter.h
new file mode 100644
index 00000000..58ad8b1b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/filter.h
@@ -0,0 +1,140 @@
+/*
+ * Linux Socket Filter Data Structures
+ */
+
+#ifndef __LINUX_FILTER_H__
+#define __LINUX_FILTER_H__
+
+/*
+ * Current version of the filter code architecture.
+ */
+#define BPF_MAJOR_VERSION 1
+#define BPF_MINOR_VERSION 1
+
+/*
+ * Try and keep these values and structures similar to BSD, especially
+ * the BPF code definitions which need to match so you can share filters
+ */
+
+struct sock_filter /* Filter block */
+{
+ __u16 code; /* Actual filter code */
+ __u8 jt; /* Jump true */
+ __u8 jf; /* Jump false */
+ __u32 k; /* Generic multiuse field */
+};
+
+struct sock_fprog /* Required for SO_ATTACH_FILTER. */
+{
+ unsigned short len; /* Number of filter blocks */
+ struct sock_filter *filter;
+};
+
+#ifdef __KERNEL__
+struct sk_filter
+{
+ atomic_t refcnt;
+ unsigned int len; /* Number of filter blocks */
+ struct sock_filter insns[0];
+};
+
+extern __inline__ unsigned int sk_filter_len(struct sk_filter *fp)
+{
+ return fp->len*sizeof(struct sock_filter) + sizeof(*fp);
+}
+#endif
+
+/*
+ * Instruction classes
+ */
+
+#define BPF_CLASS(code) ((code) & 0x07)
+#define BPF_LD 0x00
+#define BPF_LDX 0x01
+#define BPF_ST 0x02
+#define BPF_STX 0x03
+#define BPF_ALU 0x04
+#define BPF_JMP 0x05
+#define BPF_RET 0x06
+#define BPF_MISC 0x07
+
+/* ld/ldx fields */
+#define BPF_SIZE(code) ((code) & 0x18)
+#define BPF_W 0x00
+#define BPF_H 0x08
+#define BPF_B 0x10
+#define BPF_MODE(code) ((code) & 0xe0)
+#define BPF_IMM 0x00
+#define BPF_ABS 0x20
+#define BPF_IND 0x40
+#define BPF_MEM 0x60
+#define BPF_LEN 0x80
+#define BPF_MSH 0xa0
+
+/* alu/jmp fields */
+#define BPF_OP(code) ((code) & 0xf0)
+#define BPF_ADD 0x00
+#define BPF_SUB 0x10
+#define BPF_MUL 0x20
+#define BPF_DIV 0x30
+#define BPF_OR 0x40
+#define BPF_AND 0x50
+#define BPF_LSH 0x60
+#define BPF_RSH 0x70
+#define BPF_NEG 0x80
+#define BPF_JA 0x00
+#define BPF_JEQ 0x10
+#define BPF_JGT 0x20
+#define BPF_JGE 0x30
+#define BPF_JSET 0x40
+#define BPF_SRC(code) ((code) & 0x08)
+#define BPF_K 0x00
+#define BPF_X 0x08
+
+/* ret - BPF_K and BPF_X also apply */
+#define BPF_RVAL(code) ((code) & 0x18)
+#define BPF_A 0x10
+
+/* misc */
+#define BPF_MISCOP(code) ((code) & 0xf8)
+#define BPF_TAX 0x00
+#define BPF_TXA 0x80
+
+#ifndef BPF_MAXINSNS
+#define BPF_MAXINSNS 4096
+#endif
+
+/*
+ * Macros for filter block array initializers.
+ */
+#ifndef BPF_STMT
+#define BPF_STMT(code, k) { (unsigned short)(code), 0, 0, k }
+#endif
+#ifndef BPF_JUMP
+#define BPF_JUMP(code, k, jt, jf) { (unsigned short)(code), jt, jf, k }
+#endif
+
+/*
+ * Number of scratch memory words for: BPF_ST and BPF_STX
+ */
+#define BPF_MEMWORDS 16
+
+/* RATIONALE. Negative offsets are invalid in BPF.
+ We use them to reference ancillary data.
+ Unlike introduction new instructions, it does not break
+ existing compilers/optimizers.
+ */
+#define SKF_AD_OFF (-0x1000)
+#define SKF_AD_PROTOCOL 0
+#define SKF_AD_PKTTYPE 4
+#define SKF_AD_IFINDEX 8
+#define SKF_AD_MAX 12
+#define SKF_NET_OFF (-0x100000)
+#define SKF_LL_OFF (-0x200000)
+
+#ifdef __KERNEL__
+extern int sk_run_filter(struct sk_buff *skb, struct sock_filter *filter, int flen);
+extern int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
+#endif /* __KERNEL__ */
+
+#endif /* __LINUX_FILTER_H__ */
diff --git a/pfinet/linux-src/include/linux/firewall.h b/pfinet/linux-src/include/linux/firewall.h
new file mode 100644
index 00000000..1747f381
--- /dev/null
+++ b/pfinet/linux-src/include/linux/firewall.h
@@ -0,0 +1,61 @@
+#ifndef __LINUX_FIREWALL_H
+#define __LINUX_FIREWALL_H
+
+#include <linux/config.h>
+
+/*
+ * Definitions for loadable firewall modules
+ */
+
+#define FW_QUEUE 0
+#define FW_BLOCK 1
+#define FW_ACCEPT 2
+#define FW_REJECT (-1)
+#define FW_REDIRECT 3
+#define FW_MASQUERADE 4
+#define FW_SKIP 5
+
+struct firewall_ops
+{
+ struct firewall_ops *next;
+ int (*fw_forward)(struct firewall_ops *this, int pf,
+ struct device *dev, void *phdr, void *arg, struct sk_buff **pskb);
+ int (*fw_input)(struct firewall_ops *this, int pf,
+ struct device *dev, void *phdr, void *arg, struct sk_buff **pskb);
+ int (*fw_output)(struct firewall_ops *this, int pf,
+ struct device *dev, void *phdr, void *arg, struct sk_buff **pskb);
+ /* Data falling in the second 486 cache line isn't used directly
+ during a firewall call and scan, only by insert/delete and other
+ unusual cases
+ */
+ int fw_pf; /* Protocol family */
+ int fw_priority; /* Priority of chosen firewalls */
+};
+
+#ifdef __KERNEL__
+extern int register_firewall(int pf, struct firewall_ops *fw);
+extern int unregister_firewall(int pf, struct firewall_ops *fw);
+extern void fwchain_init(void);
+#ifdef CONFIG_FIREWALL
+extern int call_fw_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **pskb);
+extern int call_in_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **pskb);
+extern int call_out_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **pskb);
+#else
+extern __inline__ int call_fw_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **skb)
+{
+ return FW_ACCEPT;
+}
+
+extern __inline__ int call_in_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **skb)
+{
+ return FW_ACCEPT;
+}
+
+extern __inline__ int call_out_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **skb)
+{
+ return FW_ACCEPT;
+}
+
+#endif
+#endif
+#endif
diff --git a/pfinet/linux-src/include/linux/fs.h b/pfinet/linux-src/include/linux/fs.h
new file mode 100644
index 00000000..548b9d74
--- /dev/null
+++ b/pfinet/linux-src/include/linux/fs.h
@@ -0,0 +1,910 @@
+#ifndef _LINUX_FS_H
+#define _LINUX_FS_H
+
+/*
+ * This file has definitions for some important file table
+ * structures etc.
+ */
+
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <linux/limits.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <linux/vfs.h>
+#include <linux/net.h>
+#include <linux/kdev_t.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/dcache.h>
+#include <linux/stat.h>
+
+#include <asm/atomic.h>
+#include <linux/bitops.h>
+#include <asm/cache.h>
+#include <linux/stddef.h> /* just in case the #define NULL previously in here was needed */
+
+struct poll_table_struct;
+
+
+/*
+ * It's silly to have NR_OPEN bigger than NR_FILE, but you can change
+ * the file limit at runtime and only root can increase the per-process
+ * nr_file rlimit, so it's safe to set up a ridiculously high absolute
+ * upper limit on files-per-process.
+ *
+ * Some programs (notably those using select()) may have to be
+ * recompiled to take full advantage of the new limits..
+ */
+
+/* Fixed constants first: */
+#undef NR_OPEN
+#define NR_OPEN (1024*1024) /* Absolute upper limit on fd num */
+#define INR_OPEN 1024 /* Initial setting for nfile rlimits */
+
+#define BLOCK_SIZE_BITS 10
+#define BLOCK_SIZE (1<<BLOCK_SIZE_BITS)
+
+/* And dynamically-tunable limits and defaults: */
+extern int max_inodes;
+extern int max_files, nr_files, nr_free_files;
+extern int max_super_blocks, nr_super_blocks;
+
+#define NR_FILE 4096 /* this can well be larger on a larger system */
+#define NR_RESERVED_FILES 10 /* reserved for root */
+#define NR_SUPER 256
+
+#define MAY_EXEC 1
+#define MAY_WRITE 2
+#define MAY_READ 4
+
+#define FMODE_READ 1
+#define FMODE_WRITE 2
+
+#define READ 0
+#define WRITE 1
+#define READA 2 /* read-ahead - don't block if no resources */
+#define WRITEA 3 /* write-ahead - don't block if no resources */
+
+#define NIL_FILP ((struct file *)0)
+#define SEL_IN 1
+#define SEL_OUT 2
+#define SEL_EX 4
+
+/* public flags for file_system_type */
+#define FS_REQUIRES_DEV 1
+#define FS_NO_DCACHE 2 /* Only dcache the necessary things. */
+#define FS_NO_PRELIM 4 /* prevent preloading of dentries, even if
+ * FS_NO_DCACHE is not set.
+ */
+#define FS_IBASKET 8 /* FS does callback to free_ibasket() if space gets low. */
+
+/*
+ * These are the fs-independent mount-flags: up to 16 flags are supported
+ */
+#define MS_RDONLY 1 /* Mount read-only */
+#define MS_NOSUID 2 /* Ignore suid and sgid bits */
+#define MS_NODEV 4 /* Disallow access to device special files */
+#define MS_NOEXEC 8 /* Disallow program execution */
+#define MS_SYNCHRONOUS 16 /* Writes are synced at once */
+#define MS_REMOUNT 32 /* Alter flags of a mounted FS */
+#define MS_MANDLOCK 64 /* Allow mandatory locks on an FS */
+#define S_QUOTA 128 /* Quota initialized for file/directory/symlink */
+#define S_APPEND 256 /* Append-only file */
+#define S_IMMUTABLE 512 /* Immutable file */
+#define MS_NOATIME 1024 /* Do not update access times. */
+#define MS_NODIRATIME 2048 /* Do not update directory access times */
+
+#define MS_ODD_RENAME 32768 /* Temporary stuff; will go away as soon
+ * as nfs_rename() will be cleaned up
+ */
+
+/*
+ * Flags that can be altered by MS_REMOUNT
+ */
+#define MS_RMT_MASK (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS|MS_MANDLOCK|MS_NOATIME|MS_NODIRATIME)
+
+/*
+ * Magic mount flag number. Has to be or-ed to the flag values.
+ */
+#define MS_MGC_VAL 0xC0ED0000 /* magic flag number to indicate "new" flags */
+#define MS_MGC_MSK 0xffff0000 /* magic flag number mask */
+
+/*
+ * Note that read-only etc flags are inode-specific: setting some file-system
+ * flags just means all the inodes inherit those flags by default. It might be
+ * possible to override it selectively if you really wanted to with some
+ * ioctl() that is not currently implemented.
+ *
+ * Exception: MS_RDONLY is always applied to the entire file system.
+ *
+ * Unfortunately, it is possible to change a filesystems flags with it mounted
+ * with files in use. This means that all of the inodes will not have their
+ * i_flags updated. Hence, i_flags no longer inherit the superblock mount
+ * flags, so these have to be checked separately. -- rmk@arm.uk.linux.org
+ */
+#define __IS_FLG(inode,flg) (((inode)->i_sb && (inode)->i_sb->s_flags & (flg)) \
+ || (inode)->i_flags & (flg))
+
+#define IS_RDONLY(inode) (((inode)->i_sb) && ((inode)->i_sb->s_flags & MS_RDONLY))
+#define IS_NOSUID(inode) __IS_FLG(inode, MS_NOSUID)
+#define IS_NODEV(inode) __IS_FLG(inode, MS_NODEV)
+#define IS_NOEXEC(inode) __IS_FLG(inode, MS_NOEXEC)
+#define IS_SYNC(inode) __IS_FLG(inode, MS_SYNCHRONOUS)
+#define IS_MANDLOCK(inode) __IS_FLG(inode, MS_MANDLOCK)
+
+#define IS_QUOTAINIT(inode) ((inode)->i_flags & S_QUOTA)
+#define IS_APPEND(inode) ((inode)->i_flags & S_APPEND)
+#define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE)
+#define IS_NOATIME(inode) __IS_FLG(inode, MS_NOATIME)
+#define IS_NODIRATIME(inode) __IS_FLG(inode, MS_NODIRATIME)
+
+/* the read-only stuff doesn't really belong here, but any other place is
+ probably as bad and I don't want to create yet another include file. */
+
+#define BLKROSET _IO(0x12,93) /* set device read-only (0 = read-write) */
+#define BLKROGET _IO(0x12,94) /* get read-only status (0 = read_write) */
+#define BLKRRPART _IO(0x12,95) /* re-read partition table */
+#define BLKGETSIZE _IO(0x12,96) /* return device size */
+#define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */
+#define BLKRASET _IO(0x12,98) /* Set read ahead for block device */
+#define BLKRAGET _IO(0x12,99) /* get current read ahead setting */
+#define BLKFRASET _IO(0x12,100)/* set filesystem (mm/filemap.c) read-ahead */
+#define BLKFRAGET _IO(0x12,101)/* get filesystem (mm/filemap.c) read-ahead */
+#define BLKSECTSET _IO(0x12,102)/* set max sectors per request (ll_rw_blk.c) */
+#define BLKSECTGET _IO(0x12,103)/* get max sectors per request (ll_rw_blk.c) */
+#define BLKSSZGET _IO(0x12,104) /* get block device sector size */
+
+#define BMAP_IOCTL 1 /* obsolete - kept for compatibility */
+#define FIBMAP _IO(0x00,1) /* bmap access */
+#define FIGETBSZ _IO(0x00,2) /* get the block size used for bmap */
+
+#ifdef __KERNEL__
+
+#include <asm/semaphore.h>
+#include <asm/byteorder.h>
+#include <asm/bitops.h>
+
+extern void update_atime (struct inode *inode);
+#define UPDATE_ATIME(inode) update_atime (inode)
+
+extern void buffer_init(unsigned long);
+extern void inode_init(void);
+extern void file_table_init(void);
+extern void dcache_init(void);
+
+typedef char buffer_block[BLOCK_SIZE];
+
+/* bh state bits */
+#define BH_Uptodate 0 /* 1 if the buffer contains valid data */
+#define BH_Dirty 1 /* 1 if the buffer is dirty */
+#define BH_Lock 2 /* 1 if the buffer is locked */
+#define BH_Req 3 /* 0 if the buffer has been invalidated */
+#define BH_Protected 6 /* 1 if the buffer is protected */
+
+/*
+ * Try to keep the most commonly used fields in single cache lines (16
+ * bytes) to improve performance. This ordering should be
+ * particularly beneficial on 32-bit processors.
+ *
+ * We use the first 16 bytes for the data which is used in searches
+ * over the block hash lists (ie. getblk(), find_buffer() and
+ * friends).
+ *
+ * The second 16 bytes we use for lru buffer scans, as used by
+ * sync_buffers() and refill_freelist(). -- sct
+ */
+struct buffer_head {
+ /* First cache line: */
+ struct buffer_head * b_next; /* Hash queue list */
+ unsigned long b_blocknr; /* block number */
+ unsigned long b_size; /* block size */
+ kdev_t b_dev; /* device (B_FREE = free) */
+ kdev_t b_rdev; /* Real device */
+ unsigned long b_rsector; /* Real buffer location on disk */
+ struct buffer_head * b_this_page; /* circular list of buffers in one page */
+ unsigned long b_state; /* buffer state bitmap (see above) */
+ struct buffer_head * b_next_free;
+ unsigned int b_count; /* users using this block */
+
+ /* Non-performance-critical data follows. */
+ char * b_data; /* pointer to data block (1024 bytes) */
+ unsigned int b_list; /* List that this buffer appears */
+ unsigned long b_flushtime; /* Time when this (dirty) buffer
+ * should be written */
+ struct wait_queue * b_wait;
+ struct buffer_head ** b_pprev; /* doubly linked list of hash-queue */
+ struct buffer_head * b_prev_free; /* doubly linked list of buffers */
+ struct buffer_head * b_reqnext; /* request queue */
+
+ /*
+ * I/O completion
+ */
+ void (*b_end_io)(struct buffer_head *bh, int uptodate);
+ void *b_dev_id;
+};
+
+typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate);
+void init_buffer(struct buffer_head *bh, kdev_t dev, int block,
+ bh_end_io_t *handler, void *dev_id);
+
+static inline int buffer_uptodate(struct buffer_head * bh)
+{
+ return test_bit(BH_Uptodate, &bh->b_state);
+}
+
+static inline int buffer_dirty(struct buffer_head * bh)
+{
+ return test_bit(BH_Dirty, &bh->b_state);
+}
+
+static inline int buffer_locked(struct buffer_head * bh)
+{
+ return test_bit(BH_Lock, &bh->b_state);
+}
+
+static inline int buffer_req(struct buffer_head * bh)
+{
+ return test_bit(BH_Req, &bh->b_state);
+}
+
+static inline int buffer_protected(struct buffer_head * bh)
+{
+ return test_bit(BH_Protected, &bh->b_state);
+}
+
+#define buffer_page(bh) (mem_map + MAP_NR((bh)->b_data))
+#define touch_buffer(bh) set_bit(PG_referenced, &buffer_page(bh)->flags)
+
+#include <linux/pipe_fs_i.h>
+#include <linux/minix_fs_i.h>
+#include <linux/ext2_fs_i.h>
+#include <linux/hpfs_fs_i.h>
+#include <linux/ntfs_fs_i.h>
+#include <linux/msdos_fs_i.h>
+#include <linux/umsdos_fs_i.h>
+#include <linux/iso_fs_i.h>
+#include <linux/nfs_fs_i.h>
+#include <linux/sysv_fs_i.h>
+#include <linux/affs_fs_i.h>
+#include <linux/ufs_fs_i.h>
+#include <linux/efs_fs_i.h>
+#include <linux/coda_fs_i.h>
+#include <linux/romfs_fs_i.h>
+#include <linux/smb_fs_i.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/adfs_fs_i.h>
+#include <linux/qnx4_fs_i.h>
+
+/*
+ * Attribute flags. These should be or-ed together to figure out what
+ * has been changed!
+ */
+#define ATTR_MODE 1
+#define ATTR_UID 2
+#define ATTR_GID 4
+#define ATTR_SIZE 8
+#define ATTR_ATIME 16
+#define ATTR_MTIME 32
+#define ATTR_CTIME 64
+#define ATTR_ATIME_SET 128
+#define ATTR_MTIME_SET 256
+#define ATTR_FORCE 512 /* Not a change, but a change it */
+#define ATTR_ATTR_FLAG 1024
+
+/*
+ * This is the Inode Attributes structure, used for notify_change(). It
+ * uses the above definitions as flags, to know which values have changed.
+ * Also, in this manner, a Filesystem can look at only the values it cares
+ * about. Basically, these are the attributes that the VFS layer can
+ * request to change from the FS layer.
+ *
+ * Derek Atkins <warlord@MIT.EDU> 94-10-20
+ */
+struct iattr {
+ unsigned int ia_valid;
+ umode_t ia_mode;
+ uid_t ia_uid;
+ gid_t ia_gid;
+ off_t ia_size;
+ time_t ia_atime;
+ time_t ia_mtime;
+ time_t ia_ctime;
+ unsigned int ia_attr_flags;
+};
+
+/*
+ * This is the inode attributes flag definitions
+ */
+#define ATTR_FLAG_SYNCRONOUS 1 /* Syncronous write */
+#define ATTR_FLAG_NOATIME 2 /* Don't update atime */
+#define ATTR_FLAG_APPEND 4 /* Append-only file */
+#define ATTR_FLAG_IMMUTABLE 8 /* Immutable file */
+#define ATTR_FLAG_NODIRATIME 16 /* Don't update atime for directory */
+
+/*
+ * Includes for diskquotas and mount structures.
+ */
+#include <linux/quota.h>
+#include <linux/mount.h>
+
+struct inode {
+ struct list_head i_hash;
+ struct list_head i_list;
+ struct list_head i_dentry;
+
+ unsigned long i_ino;
+ unsigned int i_count;
+ kdev_t i_dev;
+ umode_t i_mode;
+ nlink_t i_nlink;
+ uid_t i_uid;
+ gid_t i_gid;
+ kdev_t i_rdev;
+ off_t i_size;
+ time_t i_atime;
+ time_t i_mtime;
+ time_t i_ctime;
+ unsigned long i_blksize;
+ unsigned long i_blocks;
+ unsigned long i_version;
+ unsigned long i_nrpages;
+ struct semaphore i_sem;
+ struct semaphore i_atomic_write;
+ struct inode_operations *i_op;
+ struct super_block *i_sb;
+ struct wait_queue *i_wait;
+ struct file_lock *i_flock;
+ struct vm_area_struct *i_mmap;
+ struct page *i_pages;
+ struct dquot *i_dquot[MAXQUOTAS];
+
+ unsigned long i_state;
+
+ unsigned int i_flags;
+ unsigned char i_pipe;
+ unsigned char i_sock;
+
+ int i_writecount;
+ unsigned int i_attr_flags;
+ __u32 i_generation;
+ union {
+ struct pipe_inode_info pipe_i;
+ struct minix_inode_info minix_i;
+ struct ext2_inode_info ext2_i;
+ struct hpfs_inode_info hpfs_i;
+ struct ntfs_inode_info ntfs_i;
+ struct msdos_inode_info msdos_i;
+ struct umsdos_inode_info umsdos_i;
+ struct iso_inode_info isofs_i;
+ struct nfs_inode_info nfs_i;
+ struct sysv_inode_info sysv_i;
+ struct affs_inode_info affs_i;
+ struct ufs_inode_info ufs_i;
+ struct efs_inode_info efs_i;
+ struct romfs_inode_info romfs_i;
+ struct coda_inode_info coda_i;
+ struct smb_inode_info smbfs_i;
+ struct hfs_inode_info hfs_i;
+ struct adfs_inode_info adfs_i;
+ struct qnx4_inode_info qnx4_i;
+ struct socket socket_i;
+ void *generic_ip;
+ } u;
+};
+
+/* Inode state bits.. */
+#define I_DIRTY 1
+#define I_LOCK 2
+#define I_FREEING 4
+
+extern void __mark_inode_dirty(struct inode *);
+static inline void mark_inode_dirty(struct inode *inode)
+{
+ if (!(inode->i_state & I_DIRTY))
+ __mark_inode_dirty(inode);
+}
+
+struct fown_struct {
+ int pid; /* pid or -pgrp where SIGIO should be sent */
+ uid_t uid, euid; /* uid/euid of process setting the owner */
+ int signum; /* posix.1b rt signal to be delivered on IO */
+};
+
+struct file {
+ struct file *f_next, **f_pprev;
+ struct dentry *f_dentry;
+ struct file_operations *f_op;
+ mode_t f_mode;
+ loff_t f_pos;
+ unsigned int f_count, f_flags;
+ unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;
+ struct fown_struct f_owner;
+ unsigned int f_uid, f_gid;
+ int f_error;
+
+ unsigned long f_version;
+
+ /* needed for tty driver, and maybe others */
+ void *private_data;
+};
+
+extern int init_private_file(struct file *, struct dentry *, int);
+
+#define FL_POSIX 1
+#define FL_FLOCK 2
+#define FL_BROKEN 4 /* broken flock() emulation */
+#define FL_ACCESS 8 /* for processes suspended by mandatory locking */
+#define FL_LOCKD 16 /* lock held by rpc.lockd */
+
+/*
+ * The POSIX file lock owner is determined by
+ * the "struct files_struct" in the thread group
+ * (or NULL for no owner - BSD locks).
+ *
+ * Lockd stuffs a "host" pointer into this.
+ */
+typedef struct files_struct *fl_owner_t;
+
+struct file_lock {
+ struct file_lock *fl_next; /* singly linked list for this inode */
+ struct file_lock *fl_nextlink; /* doubly linked list of all locks */
+ struct file_lock *fl_prevlink; /* used to simplify lock removal */
+ struct file_lock *fl_nextblock; /* circular list of blocked processes */
+ struct file_lock *fl_prevblock;
+ fl_owner_t fl_owner;
+ unsigned int fl_pid;
+ struct wait_queue *fl_wait;
+ struct file *fl_file;
+ unsigned char fl_flags;
+ unsigned char fl_type;
+ off_t fl_start;
+ off_t fl_end;
+
+ void (*fl_notify)(struct file_lock *); /* unblock callback */
+
+ union {
+ struct nfs_lock_info nfs_fl;
+ } fl_u;
+};
+
+extern struct file_lock *file_lock_table;
+
+#include <linux/fcntl.h>
+
+extern int fcntl_getlk(unsigned int fd, struct flock *l);
+extern int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l);
+
+/* fs/locks.c */
+extern void locks_remove_posix(struct file *, fl_owner_t id);
+extern void locks_remove_flock(struct file *);
+extern struct file_lock *posix_test_lock(struct file *, struct file_lock *);
+extern int posix_lock_file(struct file *, struct file_lock *, unsigned int);
+extern void posix_block_lock(struct file_lock *, struct file_lock *);
+extern void posix_unblock_lock(struct file_lock *);
+
+struct fasync_struct {
+ int magic;
+ int fa_fd;
+ struct fasync_struct *fa_next; /* singly linked list */
+ struct file *fa_file;
+};
+
+#define FASYNC_MAGIC 0x4601
+
+extern int fasync_helper(int, struct file *, int, struct fasync_struct **);
+
+#include <linux/minix_fs_sb.h>
+#include <linux/ext2_fs_sb.h>
+#include <linux/hpfs_fs_sb.h>
+#include <linux/ntfs_fs_sb.h>
+#include <linux/msdos_fs_sb.h>
+#include <linux/iso_fs_sb.h>
+#include <linux/nfs_fs_sb.h>
+#include <linux/sysv_fs_sb.h>
+#include <linux/affs_fs_sb.h>
+#include <linux/ufs_fs_sb.h>
+#include <linux/efs_fs_sb.h>
+#include <linux/romfs_fs_sb.h>
+#include <linux/smb_fs_sb.h>
+#include <linux/hfs_fs_sb.h>
+#include <linux/adfs_fs_sb.h>
+#include <linux/qnx4_fs_sb.h>
+
+extern struct list_head super_blocks;
+
+#define sb_entry(list) list_entry((list), struct super_block, s_list)
+struct super_block {
+ struct list_head s_list; /* Keep this first */
+ kdev_t s_dev;
+ unsigned long s_blocksize;
+ unsigned char s_blocksize_bits;
+ unsigned char s_lock;
+ unsigned char s_rd_only;
+ unsigned char s_dirt;
+ struct file_system_type *s_type;
+ struct super_operations *s_op;
+ struct dquot_operations *dq_op;
+ unsigned long s_flags;
+ unsigned long s_magic;
+ unsigned long s_time;
+ struct dentry *s_root;
+ struct wait_queue *s_wait;
+
+ struct inode *s_ibasket;
+ short int s_ibasket_count;
+ short int s_ibasket_max;
+ struct list_head s_dirty; /* dirty inodes */
+
+ union {
+ struct minix_sb_info minix_sb;
+ struct ext2_sb_info ext2_sb;
+ struct hpfs_sb_info hpfs_sb;
+ struct ntfs_sb_info ntfs_sb;
+ struct msdos_sb_info msdos_sb;
+ struct isofs_sb_info isofs_sb;
+ struct nfs_sb_info nfs_sb;
+ struct sysv_sb_info sysv_sb;
+ struct affs_sb_info affs_sb;
+ struct ufs_sb_info ufs_sb;
+ struct efs_sb_info efs_sb;
+ struct romfs_sb_info romfs_sb;
+ struct smb_sb_info smbfs_sb;
+ struct hfs_sb_info hfs_sb;
+ struct adfs_sb_info adfs_sb;
+ struct qnx4_sb_info qnx4_sb;
+ void *generic_sbp;
+ } u;
+ /*
+ * The next field is for VFS *only*. No filesystems have any business
+ * even looking at it. You had been warned.
+ */
+ struct semaphore s_vfs_rename_sem; /* Kludge */
+};
+
+/*
+ * VFS helper functions..
+ */
+extern int vfs_rmdir(struct inode *, struct dentry *);
+extern int vfs_unlink(struct inode *, struct dentry *);
+extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+
+/*
+ * This is the "filldir" function type, used by readdir() to let
+ * the kernel specify what kind of dirent layout it wants to have.
+ * This allows the kernel to read directories into kernel space or
+ * to have different dirent layouts depending on the binary type.
+ */
+typedef int (*filldir_t)(void *, const char *, int, off_t, ino_t);
+
+struct file_operations {
+ loff_t (*llseek) (struct file *, loff_t, int);
+ ssize_t (*read) (struct file *, char *, size_t, loff_t *);
+ ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
+ int (*readdir) (struct file *, void *, filldir_t);
+ unsigned int (*poll) (struct file *, struct poll_table_struct *);
+ int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
+ int (*mmap) (struct file *, struct vm_area_struct *);
+ int (*open) (struct inode *, struct file *);
+ int (*flush) (struct file *);
+ int (*release) (struct inode *, struct file *);
+ int (*fsync) (struct file *, struct dentry *);
+ int (*fasync) (int, struct file *, int);
+ int (*check_media_change) (kdev_t dev);
+ int (*revalidate) (kdev_t dev);
+ int (*lock) (struct file *, int, struct file_lock *);
+};
+
+struct inode_operations {
+ struct file_operations * default_file_ops;
+ int (*create) (struct inode *,struct dentry *,int);
+ struct dentry * (*lookup) (struct inode *,struct dentry *);
+ int (*link) (struct dentry *,struct inode *,struct dentry *);
+ int (*unlink) (struct inode *,struct dentry *);
+ int (*symlink) (struct inode *,struct dentry *,const char *);
+ int (*mkdir) (struct inode *,struct dentry *,int);
+ int (*rmdir) (struct inode *,struct dentry *);
+ int (*mknod) (struct inode *,struct dentry *,int,int);
+ int (*rename) (struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
+ int (*readlink) (struct dentry *, char *,int);
+ struct dentry * (*follow_link) (struct dentry *, struct dentry *, unsigned int);
+ int (*readpage) (struct file *, struct page *);
+ int (*writepage) (struct file *, struct page *);
+ int (*bmap) (struct inode *,int);
+ void (*truncate) (struct inode *);
+ int (*permission) (struct inode *, int);
+ int (*smap) (struct inode *,int);
+ int (*updatepage) (struct file *, struct page *, unsigned long, unsigned int, int);
+ int (*revalidate) (struct dentry *);
+};
+
+struct super_operations {
+ void (*read_inode) (struct inode *);
+ void (*write_inode) (struct inode *);
+ void (*put_inode) (struct inode *);
+ void (*delete_inode) (struct inode *);
+ int (*notify_change) (struct dentry *, struct iattr *);
+ void (*put_super) (struct super_block *);
+ void (*write_super) (struct super_block *);
+ int (*statfs) (struct super_block *, struct statfs *, int);
+ int (*remount_fs) (struct super_block *, int *, char *);
+ void (*clear_inode) (struct inode *);
+ void (*umount_begin) (struct super_block *);
+};
+
+struct dquot_operations {
+ void (*initialize) (struct inode *, short);
+ void (*drop) (struct inode *);
+ int (*alloc_block) (const struct inode *, unsigned long, uid_t, char);
+ int (*alloc_inode) (const struct inode *, unsigned long, uid_t);
+ void (*free_block) (const struct inode *, unsigned long);
+ void (*free_inode) (const struct inode *, unsigned long);
+ int (*transfer) (struct dentry *, struct iattr *, uid_t);
+};
+
+struct file_system_type {
+ const char *name;
+ int fs_flags;
+ struct super_block *(*read_super) (struct super_block *, void *, int);
+ struct file_system_type * next;
+};
+
+extern int register_filesystem(struct file_system_type *);
+extern int unregister_filesystem(struct file_system_type *);
+
+/* Return value for VFS lock functions - tells locks.c to lock conventionally
+ * REALLY kosha for root NFS and nfs_lock
+ */
+#define LOCK_USE_CLNT 1
+
+#define FLOCK_VERIFY_READ 1
+#define FLOCK_VERIFY_WRITE 2
+
+extern int locks_mandatory_locked(struct inode *inode);
+extern int locks_mandatory_area(int read_write, struct inode *inode,
+ struct file *filp, loff_t offset,
+ size_t count);
+
+extern inline int locks_verify_locked(struct inode *inode)
+{
+ /* Candidates for mandatory locking have the setgid bit set
+ * but no group execute bit - an otherwise meaningless combination.
+ */
+ if (IS_MANDLOCK(inode) &&
+ (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+ return (locks_mandatory_locked(inode));
+ return (0);
+}
+
+extern inline int locks_verify_area(int read_write, struct inode *inode,
+ struct file *filp, loff_t offset,
+ size_t count)
+{
+ /* Candidates for mandatory locking have the setgid bit set
+ * but no group execute bit - an otherwise meaningless combination.
+ */
+ if (IS_MANDLOCK(inode) &&
+ (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+ return (locks_mandatory_area(read_write, inode, filp, offset,
+ count));
+ return (0);
+}
+
+
+/* fs/open.c */
+
+asmlinkage int sys_open(const char *, int, int);
+asmlinkage int sys_close(unsigned int); /* yes, it's really unsigned */
+extern int do_truncate(struct dentry *, unsigned long);
+extern int get_unused_fd(void);
+extern void put_unused_fd(unsigned int);
+
+extern struct file *filp_open(const char *, int, int);
+extern int filp_close(struct file *, fl_owner_t id);
+
+extern char * getname(const char * filename);
+#define __getname() ((char *) __get_free_page(GFP_KERNEL))
+#define putname(name) free_page((unsigned long)(name))
+
+extern void kill_fasync(struct fasync_struct *fa, int sig);
+extern int register_blkdev(unsigned int, const char *, struct file_operations *);
+extern int unregister_blkdev(unsigned int major, const char * name);
+extern int blkdev_open(struct inode * inode, struct file * filp);
+extern int blkdev_release (struct inode * inode);
+extern struct file_operations def_blk_fops;
+extern struct inode_operations blkdev_inode_operations;
+
+/* fs/devices.c */
+extern int register_chrdev(unsigned int, const char *, struct file_operations *);
+extern int unregister_chrdev(unsigned int major, const char * name);
+extern int chrdev_open(struct inode * inode, struct file * filp);
+extern struct file_operations def_chr_fops;
+extern struct inode_operations chrdev_inode_operations;
+extern char * bdevname(kdev_t dev);
+extern char * cdevname(kdev_t dev);
+extern char * kdevname(kdev_t dev);
+
+
+extern void init_fifo(struct inode * inode);
+extern struct inode_operations fifo_inode_operations;
+
+/* Invalid inode operations -- fs/bad_inode.c */
+extern void make_bad_inode(struct inode * inode);
+extern int is_bad_inode(struct inode * inode);
+
+extern struct file_operations connecting_fifo_fops;
+extern struct file_operations read_fifo_fops;
+extern struct file_operations write_fifo_fops;
+extern struct file_operations rdwr_fifo_fops;
+extern struct file_operations read_pipe_fops;
+extern struct file_operations write_pipe_fops;
+extern struct file_operations rdwr_pipe_fops;
+
+extern struct file_system_type *get_fs_type(const char *name);
+
+extern int fs_may_remount_ro(struct super_block *);
+extern int fs_may_mount(kdev_t dev);
+
+extern struct file *inuse_filps;
+
+extern void refile_buffer(struct buffer_head * buf);
+extern void set_writetime(struct buffer_head * buf, int flag);
+extern int try_to_free_buffers(struct page *);
+
+extern int nr_buffers;
+extern long buffermem;
+extern int nr_buffer_heads;
+
+#define BUF_CLEAN 0
+#define BUF_LOCKED 1 /* Buffers scheduled for write */
+#define BUF_DIRTY 2 /* Dirty buffers, not yet scheduled for write */
+#define NR_LIST 3
+
+void mark_buffer_uptodate(struct buffer_head * bh, int on);
+
+extern inline void mark_buffer_clean(struct buffer_head * bh)
+{
+ if (test_and_clear_bit(BH_Dirty, &bh->b_state)) {
+ if (bh->b_list == BUF_DIRTY)
+ refile_buffer(bh);
+ }
+}
+
+extern inline void mark_buffer_dirty(struct buffer_head * bh, int flag)
+{
+ if (!test_and_set_bit(BH_Dirty, &bh->b_state)) {
+ set_writetime(bh, flag);
+ if (bh->b_list != BUF_DIRTY)
+ refile_buffer(bh);
+ }
+}
+
+extern int check_disk_change(kdev_t dev);
+extern int invalidate_inodes(struct super_block * sb);
+extern void invalidate_inode_pages(struct inode *);
+#define invalidate_buffers(dev) __invalidate_buffers((dev), 0)
+#define destroy_buffers(dev) __invalidate_buffers((dev), 1)
+extern void __invalidate_buffers(kdev_t dev, int);
+extern int floppy_is_wp(int minor);
+extern void sync_inodes(kdev_t dev);
+extern void write_inode_now(struct inode *inode);
+extern void sync_dev(kdev_t dev);
+extern int fsync_dev(kdev_t dev);
+extern void sync_supers(kdev_t dev);
+extern int bmap(struct inode * inode,int block);
+extern int notify_change(struct dentry *, struct iattr *);
+extern int permission(struct inode * inode,int mask);
+extern int get_write_access(struct inode *inode);
+extern void put_write_access(struct inode *inode);
+extern struct dentry * open_namei(const char * pathname, int flag, int mode);
+extern struct dentry * do_mknod(const char * filename, int mode, dev_t dev);
+extern int do_pipe(int *);
+
+/* fs/dcache.c -- generic fs support functions */
+extern int is_subdir(struct dentry *, struct dentry *);
+extern ino_t find_inode_number(struct dentry *, struct qstr *);
+
+/*
+ * Kernel pointers have redundant information, so we can use a
+ * scheme where we can return either an error code or a dentry
+ * pointer with the same return value.
+ *
+ * This should be a per-architecture thing, to allow different
+ * error and pointer decisions.
+ */
+#define ERR_PTR(err) ((void *)((long)(err)))
+#define PTR_ERR(ptr) ((long)(ptr))
+#define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000))
+
+/*
+ * The bitmask for a lookup event:
+ * - follow links at the end
+ * - require a directory
+ * - ending slashes ok even for nonexistent files
+ * - internal "there are more path compnents" flag
+ */
+#define LOOKUP_FOLLOW (1)
+#define LOOKUP_DIRECTORY (2)
+#define LOOKUP_SLASHOK (4)
+#define LOOKUP_CONTINUE (8)
+
+extern struct dentry * lookup_dentry(const char *, struct dentry *, unsigned int);
+extern struct dentry * __namei(const char *, unsigned int);
+
+#define namei(pathname) __namei(pathname, 1)
+#define lnamei(pathname) __namei(pathname, 0)
+
+extern void iput(struct inode *);
+extern struct inode * igrab(struct inode *inode);
+extern ino_t iunique(struct super_block *, ino_t);
+extern struct inode * iget(struct super_block *, unsigned long);
+extern struct inode * iget_in_use (struct super_block *, unsigned long);
+extern void clear_inode(struct inode *);
+extern struct inode * get_empty_inode(void);
+
+extern void insert_inode_hash(struct inode *);
+extern void remove_inode_hash(struct inode *);
+extern struct file * get_empty_filp(void);
+extern struct buffer_head * get_hash_table(kdev_t, int, int);
+extern struct buffer_head * getblk(kdev_t, int, int);
+extern struct buffer_head * find_buffer(kdev_t dev, int block, int size);
+extern void ll_rw_block(int, int, struct buffer_head * bh[]);
+extern int is_read_only(kdev_t);
+extern void __brelse(struct buffer_head *);
+extern inline void brelse(struct buffer_head *buf)
+{
+ if (buf)
+ __brelse(buf);
+}
+extern void __bforget(struct buffer_head *buf);
+extern inline void bforget(struct buffer_head *buf)
+{
+ if (buf)
+ __bforget(buf);
+}
+extern void set_blocksize(kdev_t dev, int size);
+extern unsigned int get_hardblocksize(kdev_t dev);
+extern struct buffer_head * bread(kdev_t dev, int block, int size);
+extern struct buffer_head * breada(kdev_t dev,int block, int size,
+ unsigned int pos, unsigned int filesize);
+
+extern int brw_page(int, struct page *, kdev_t, int [], int, int);
+
+extern int generic_readpage(struct file *, struct page *);
+extern int generic_file_mmap(struct file *, struct vm_area_struct *);
+extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
+extern ssize_t generic_file_write(struct file *, const char*, size_t, loff_t*);
+
+extern struct super_block *get_super(kdev_t dev);
+extern void put_super(kdev_t dev);
+unsigned long generate_cluster(kdev_t dev, int b[], int size);
+unsigned long generate_cluster_swab32(kdev_t dev, int b[], int size);
+extern kdev_t ROOT_DEV;
+
+extern void show_buffers(void);
+extern void mount_root(void);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+extern kdev_t real_root_dev;
+extern int change_root(kdev_t new_root_dev,const char *put_old);
+#endif
+
+extern ssize_t char_read(struct file *, char *, size_t, loff_t *);
+extern ssize_t block_read(struct file *, char *, size_t, loff_t *);
+extern int read_ahead[];
+
+extern ssize_t char_write(struct file *, const char *, size_t, loff_t *);
+extern ssize_t block_write(struct file *, const char *, size_t, loff_t *);
+
+extern int block_fsync(struct file *, struct dentry *dir);
+extern int file_fsync(struct file *, struct dentry *dir);
+
+extern int inode_change_ok(struct inode *, struct iattr *);
+extern void inode_setattr(struct inode *, struct iattr *);
+
+extern __u32 inode_generation_count;
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/ftape-header-segment.h b/pfinet/linux-src/include/linux/ftape-header-segment.h
new file mode 100644
index 00000000..4732218f
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ftape-header-segment.h
@@ -0,0 +1,122 @@
+#ifndef _FTAPE_HEADER_SEGMENT_H
+#define _FTAPE_HEADER_SEGMENT_H
+
+/*
+ * Copyright (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/include/linux/ftape-header-segment.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:28 $
+ *
+ * This file defines some offsets into the header segment of a
+ * floppy tape cartridge. For use with the QIC-40/80/3010/3020
+ * floppy-tape driver "ftape" for Linux.
+ */
+
+#define FT_SIGNATURE 0 /* must be 0xaa55aa55 */
+#define FT_FMT_CODE 4
+#define FT_REV_LEVEL 5 /* only for QIC-80 since. Rev. L (== 0x0c) */
+#define FT_HSEG_1 6 /* first header segment, except for format code 6 */
+#define FT_HSEG_2 8 /* second header segment, except for format code 6 */
+#define FT_FRST_SEG 10 /* first data segment, except for format code 6 */
+#define FT_LAST_SEG 12 /* last data segment, except for format code 6 */
+#define FT_FMT_DATE 14 /* date and time of most recent format, see below */
+#define FT_WR_DATE 18 /* date and time of most recent write or format */
+#define FT_SPT 24 /* segments per track */
+#define FT_TPC 26 /* tracks per cartridge */
+#define FT_FHM 27 /* floppy drive head (maximum of it) */
+#define FT_FTM 28 /* floppy track max. */
+#define FT_FSM 29 /* floppy sector max. (128) */
+#define FT_LABEL 30 /* floppy tape label */
+#define FT_LABEL_DATE 74 /* date and time the tape label was written */
+#define FT_LABEL_SZ (FT_LABEL_DATE - FT_LABEL)
+#define FT_CMAP_START 78 /* starting segment of compression map */
+#define FT_FMT_ERROR 128 /* must be set to 0xff if remainder gets lost during
+ * tape format
+ */
+#define FT_SEG_CNT 130 /* number of seg. written, formatted or verified
+ * through lifetime of tape (why not read?)
+ */
+#define FT_INIT_DATE 138 /* date and time of initial tape format */
+#define FT_FMT_CNT 142 /* number of times tape has been formatted */
+#define FT_FSL_CNT 144 /* number of segments in failed sector log */
+#define FT_MK_CODE 146 /* id string of tape manufacturer */
+#define FT_LOT_CODE 190 /* tape manufacturer lot code */
+#define FT_6_HSEG_1 234 /* first header segment for format code 6 */
+#define FT_6_HSEG_2 238 /* second header segment for format code 6 */
+#define FT_6_FRST_SEG 242 /* first data segment for format code 6 */
+#define FT_6_LAST_SEG 246 /* last data segment for format code 6 */
+
+#define FT_FSL 256
+#define FT_HEADER_END 256 /* space beyond this point:
+ * format codes 2, 3 and 5:
+ * - failed sector log until byte 2047
+ * - bad sector map in the reamining part of segment
+ * format codes 4 and 6:
+ * - bad sector map starts hear
+ */
+
+
+/* value to be stored at the FT_SIGNATURE offset
+ */
+#define FT_HSEG_MAGIC 0xaa55aa55
+#define FT_D2G_MAGIC 0x82288228 /* Ditto 2GB */
+
+/* data and time encoding: */
+#define FT_YEAR_SHIFT 25
+#define FT_YEAR_MASK 0xfe000000
+#define FT_YEAR_0 1970
+#define FT_YEAR_MAX 127
+#define FT_YEAR(year) ((((year)-FT_YEAR_0)<<FT_YEAR_SHIFT)&FT_YEAR_MASK)
+
+#define FT_TIME_SHIFT 0
+#define FT_TIME_MASK 0x01FFFFFF
+#define FT_TIME_MAX 0x01ea6dff /* last second of a year */
+#define FT_TIME(mo,d,h,m,s) \
+ ((((s)+60*((m)+60*((h)+24*((d)+31*(mo))))) & FT_TIME_MASK))
+
+#define FT_TIME_STAMP(y,mo,d,h,m,s) (FT_YEAR(y) | FT_TIME(mo,d,h,m,s))
+
+/* values for the format code field */
+typedef enum {
+ fmt_normal = 2, /* QIC-80 post Rev. B 205Ft or 307Ft tape */
+ fmt_1100ft = 3, /* QIC-80 post Rev. B 1100Ft tape */
+ fmt_var = 4, /* QIC-80 post Rev. B variabel length format */
+ fmt_425ft = 5, /* QIC-80 post Rev. B 425Ft tape */
+ fmt_big = 6 /* QIC-3010/3020 variable length tape with more
+ * than 2^16 segments per tape
+ */
+} ft_format_type;
+
+/* definitions for the failed sector log */
+#define FT_FSL_SIZE (2 * FT_SECTOR_SIZE - FT_HEADER_END)
+#define FT_FSL_MAX_ENTRIES (FT_FSL_SIZE/sizeof(__u32))
+
+typedef struct ft_fsl_entry {
+ __u16 segment;
+ __u16 date;
+} __attribute__ ((packed)) ft_fsl_entry;
+
+
+/* date encoding for the failed sector log
+ * month: 1..12, day: 1..31, year: 1970..2097
+ */
+#define FT_FSL_TIME_STAMP(y,m,d) \
+ (((((y) - FT_YEAR_0)<<9)&0xfe00) | (((m)<<5)&0x01e0) | ((d)&0x001f))
+
+#endif /* _FTAPE_HEADER_SEGMENT_H */
diff --git a/pfinet/linux-src/include/linux/ftape-vendors.h b/pfinet/linux-src/include/linux/ftape-vendors.h
new file mode 100644
index 00000000..ec1a81f0
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ftape-vendors.h
@@ -0,0 +1,137 @@
+#ifndef _FTAPE_VENDORS_H
+#define _FTAPE_VENDORS_H
+
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/include/linux/ftape-vendors.h,v $
+ * $Revision: 1.6 $
+ * $Date: 1997/10/09 15:38:11 $
+ *
+ * This file contains the supported drive types with their
+ * QIC-117 spec. vendor code and drive dependent configuration
+ * information.
+ */
+
+typedef enum {
+ unknown_wake_up = 0,
+ no_wake_up,
+ wake_up_colorado,
+ wake_up_mountain,
+ wake_up_insight,
+} wake_up_types;
+
+typedef struct {
+ wake_up_types wake_up; /* see wake_up_types */
+ char *name; /* Text describing the drive */
+} wakeup_method;
+
+/* Note: order of entries in WAKEUP_METHODS must be so that a variable
+ * of type wake_up_types can be used as an index in the array.
+ */
+#define WAKEUP_METHODS { \
+ { unknown_wake_up, "Unknown" }, \
+ { no_wake_up, "None" }, \
+ { wake_up_colorado, "Colorado" }, \
+ { wake_up_mountain, "Mountain" }, \
+ { wake_up_insight, "Motor-on" }, \
+}
+
+typedef struct {
+ unsigned int vendor_id; /* vendor id from drive */
+ int speed; /* maximum tape transport speed (ips) */
+ wake_up_types wake_up; /* see wake_up_types */
+ char *name; /* Text describing the drive */
+} vendor_struct;
+
+#define UNKNOWN_VENDOR (-1)
+
+#define QIC117_VENDORS { \
+/* see _vendor_struct */ \
+ { 0x00000, 82, wake_up_colorado, "Colorado DJ-10 (old)" }, \
+ { 0x00047, 90, wake_up_colorado, "Colorado DJ-10/DJ-20" }, \
+ { 0x011c2, 84, wake_up_colorado, "Colorado 700" }, \
+ { 0x011c3, 90, wake_up_colorado, "Colorado 1400" }, \
+ { 0x011c4, 84, wake_up_colorado, "Colorado DJ-10/DJ-20 (new)" }, \
+ { 0x011c5, 84, wake_up_colorado, "HP Colorado T1000" }, \
+ { 0x011c6, 90, wake_up_colorado, "HP Colorado T3000" }, \
+ { 0x00005, 45, wake_up_mountain, "Archive 5580i" }, \
+ { 0x10005, 50, wake_up_insight, "Insight 80Mb, Irwin 80SX" }, \
+ { 0x00140, 74, wake_up_mountain, "Archive S.Hornet [Identity/Escom]" }, \
+ { 0x00146, 72, wake_up_mountain, "Archive 31250Q [Escom]" }, \
+ { 0x0014a, 100, wake_up_mountain, "Archive XL9250i [Conner/Escom]" }, \
+ { 0x0014c, 98, wake_up_mountain, "Conner C250MQT" }, \
+ { 0x0014e, 80, wake_up_mountain, "Conner C250MQ" }, \
+ { 0x00150, 80, wake_up_mountain, "Conner TSM420R/TST800R" }, \
+ { 0x00152, 80, wake_up_mountain, "Conner TSM850R" }, \
+ { 0x00156, 80, wake_up_mountain, "Conner TSM850R/1700R/TST3200R" }, \
+ { 0x00180, 0, wake_up_mountain, "Summit SE 150" }, \
+ { 0x00181, 85, wake_up_mountain, "Summit SE 250, Mountain FS8000" }, \
+ { 0x001c1, 82, no_wake_up, "Wangtek 3040F" }, \
+ { 0x001c8, 64, no_wake_up, "Wangtek 3080F" }, \
+ { 0x001c8, 64, wake_up_colorado, "Wangtek 3080F" }, \
+ { 0x001ca, 67, no_wake_up, "Wangtek 3080F (new)" }, \
+ { 0x001cc, 77, wake_up_colorado, "Wangtek 3200 / Teac 700" }, \
+ { 0x001cd, 75, wake_up_colorado, "Reveal TB1400" }, \
+ { 0x00380, 85, wake_up_colorado, "Exabyte Eagle-96" }, \
+ { 0x00381, 85, wake_up_colorado, "Exabyte Eagle TR-3" }, \
+ { 0x00382, 85, wake_up_colorado, "Exabyte Eagle TR-3" }, \
+ { 0x003ce, 77, wake_up_colorado, "Teac 800" }, \
+ { 0x003cf, 0, wake_up_colorado, "Teac FT3010TR" }, \
+ { 0x08880, 64, no_wake_up, "Iomega 250, Ditto 800" }, \
+ { 0x08880, 64, wake_up_colorado, "Iomega 250, Ditto 800" }, \
+ { 0x08880, 64, wake_up_insight, "Iomega 250, Ditto 800" }, \
+ { 0x08881, 80, wake_up_colorado, "Iomega 700" }, \
+ { 0x08882, 80, wake_up_colorado, "Iomega 3200" }, \
+ { 0x08883, 80, wake_up_colorado, "Iomega DITTO 2GB" }, \
+ { 0x00021, 70, no_wake_up, "AIWA CT-803" }, \
+ { 0x004c0, 80, no_wake_up, "AIWA TD-S1600" }, \
+ { 0x00021, 0, wake_up_mountain, "COREtape QIC80" }, \
+ { 0x00441, 0, wake_up_mountain, "ComByte DoublePlay" }, \
+ { 0x00481, 127, wake_up_mountain, "PERTEC MyTape 800" }, \
+ { 0x00483, 130, wake_up_mountain, "PERTEC MyTape 3200" }, \
+ { UNKNOWN_VENDOR, 0, no_wake_up, "unknown" } \
+}
+
+#define QIC117_MAKE_CODES { \
+ { 0, "Unassigned" }, \
+ { 1, "Alloy Computer Products" }, \
+ { 2, "3M" }, \
+ { 3, "Tandberg Data" }, \
+ { 4, "Colorado" }, \
+ { 5, "Archive/Conner" }, \
+ { 6, "Mountain/Summit Memory Systems" }, \
+ { 7, "Wangtek/Rexon/Tecmar" }, \
+ { 8, "Sony" }, \
+ { 9, "Cipher Data Products" }, \
+ { 10, "Irwin Magnetic Systems" }, \
+ { 11, "Braemar" }, \
+ { 12, "Verbatim" }, \
+ { 13, "Core International" }, \
+ { 14, "Exabyte" }, \
+ { 15, "Teac" }, \
+ { 16, "Gigatek" }, \
+ { 17, "ComByte" }, \
+ { 18, "PERTEC Memories" }, \
+ { 19, "Aiwa" }, \
+ { 71, "Colorado" }, \
+ { 546, "Iomega Inc" }, \
+}
+
+#endif /* _FTAPE_VENDORS_H */
diff --git a/pfinet/linux-src/include/linux/ftape.h b/pfinet/linux-src/include/linux/ftape.h
new file mode 100644
index 00000000..a26f2e51
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ftape.h
@@ -0,0 +1,212 @@
+#ifndef _FTAPE_H
+#define _FTAPE_H
+
+/*
+ * Copyright (C) 1994-1996 Bas Laarhoven,
+ * (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/include/linux/ftape.h,v $
+ * $Revision: 1.17.6.4 $
+ * $Date: 1997/11/25 01:52:54 $
+ *
+ * This file contains global definitions, typedefs and macro's
+ * for the QIC-40/80/3010/3020 floppy-tape driver for Linux.
+ */
+
+#define FTAPE_VERSION "ftape v3.04d 25/11/97"
+
+/* this makes the Kernel version numbers readable */
+#define KERNEL_VER(major,minor,sublvl) (((major)<<16)+((minor)<<8)+(sublvl))
+
+#ifdef __KERNEL__
+#include <linux/sched.h>
+#include <linux/mm.h>
+#endif
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/config.h>
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13)
+typedef daddr_t __kernel_daddr_t; /* needed for mtio.h */
+#endif
+#include <linux/mtio.h>
+
+#define FT_SECTOR(x) (x+1) /* sector offset into real sector */
+#define FT_SECTOR_SIZE 1024
+#define FT_SECTORS_PER_SEGMENT 32
+#define FT_ECC_SECTORS 3
+#define FT_SEGMENT_SIZE ((FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS) * FT_SECTOR_SIZE)
+#define FT_BUFF_SIZE (FT_SECTORS_PER_SEGMENT * FT_SECTOR_SIZE)
+
+/*
+ * bits of the minor device number that define drive selection
+ * methods. Could be used one day to access multiple tape
+ * drives on the same controller.
+ */
+#define FTAPE_SEL_A 0
+#define FTAPE_SEL_B 1
+#define FTAPE_SEL_C 2
+#define FTAPE_SEL_D 3
+#define FTAPE_SEL_MASK 3
+#define FTAPE_SEL(unit) ((unit) & FTAPE_SEL_MASK)
+#define FTAPE_NO_REWIND 4 /* mask for minor nr */
+
+/* the following two may be reported when MTIOCGET is requested ... */
+typedef union {
+ struct {
+ __u8 error;
+ __u8 command;
+ } error;
+ long space;
+} ft_drive_error;
+typedef union {
+ struct {
+ __u8 drive_status;
+ __u8 drive_config;
+ __u8 tape_status;
+ } status;
+ long space;
+} ft_drive_status;
+
+#ifdef __KERNEL__
+
+#define FT_RQM_DELAY 12
+#define FT_MILLISECOND 1
+#define FT_SECOND 1000
+#define FT_FOREVER -1
+#ifndef HZ
+#error "HZ undefined."
+#endif
+#define FT_USPT (1000000/HZ) /* microseconds per tick */
+
+/* This defines the number of retries that the driver will allow
+ * before giving up (and letting a higher level handle the error).
+ */
+#ifdef TESTING
+#define FT_SOFT_RETRIES 1 /* number of low level retries */
+#define FT_RETRIES_ON_ECC_ERROR 3 /* ecc error when correcting segment */
+#else
+#define FT_SOFT_RETRIES 6 /* number of low level retries (triple) */
+#define FT_RETRIES_ON_ECC_ERROR 3 /* ecc error when correcting segment */
+#endif
+
+#ifndef THE_FTAPE_MAINTAINER
+#define THE_FTAPE_MAINTAINER "the ftape maintainer"
+#endif
+
+/* Initialize missing configuration parameters.
+ */
+#ifndef CONFIG_FT_NR_BUFFERS
+# define CONFIG_FT_NR_BUFFERS 3
+#endif
+#ifndef CONFIG_FT_FDC_THR
+# define CONFIG_FT_FDC_THR 8
+#endif
+#ifndef CONFIG_FT_FDC_MAX_RATE
+# define CONFIG_FT_FDC_MAX_RATE 2000
+#endif
+#ifndef CONFIG_FT_FDC_BASE
+# define CONFIG_FT_FDC_BASE 0
+#endif
+#ifndef CONFIG_FT_FDC_IRQ
+# define CONFIG_FT_FDC_IRQ 0
+#endif
+#ifndef CONFIG_FT_FDC_DMA
+# define CONFIG_FT_FDC_DMA 0
+#endif
+
+/* Turn some booleans into numbers.
+ */
+#ifdef CONFIG_FT_PROBE_FC10
+# undef CONFIG_FT_PROBE_FC10
+# define CONFIG_FT_PROBE_FC10 1
+#else
+# define CONFIG_FT_PROBE_FC10 0
+#endif
+#ifdef CONFIG_FT_MACH2
+# undef CONFIG_FT_MACH2
+# define CONFIG_FT_MACH2 1
+#else
+# define CONFIG_FT_MACH2 0
+#endif
+
+/* Insert default settings
+ */
+#if CONFIG_FT_PROBE_FC10 == 1
+# if CONFIG_FT_FDC_BASE == 0
+# undef CONFIG_FT_FDC_BASE
+# define CONFIG_FT_FDC_BASE 0x180
+# endif
+# if CONFIG_FT_FDC_IRQ == 0
+# undef CONFIG_FT_FDC_IRQ
+# define CONFIG_FT_FDC_IRQ 9
+# endif
+# if CONFIG_FT_FDC_DMA == 0
+# undef CONFIG_FT_FDC_DMA
+# define CONFIG_FT_FDC_DMA 3
+# endif
+#elif CONFIG_FT_MACH2 == 1 /* CONFIG_FT_PROBE_FC10 == 1 */
+# if CONFIG_FT_FDC_BASE == 0
+# undef CONFIG_FT_FDC_BASE
+# define CONFIG_FT_FDC_BASE 0x1E0
+# endif
+# if CONFIG_FT_FDC_IRQ == 0
+# undef CONFIG_FT_FDC_IRQ
+# define CONFIG_FT_FDC_IRQ 6
+# endif
+# if CONFIG_FT_FDC_DMA == 0
+# undef CONFIG_FT_FDC_DMA
+# define CONFIG_FT_FDC_DMA 2
+# endif
+#elif CONFIG_FT_ALT_FDC == 1 /* CONFIG_FT_MACH2 */
+# if CONFIG_FT_FDC_BASE == 0
+# undef CONFIG_FT_FDC_BASE
+# define CONFIG_FT_FDC_BASE 0x370
+# endif
+# if CONFIG_FT_FDC_IRQ == 0
+# undef CONFIG_FT_FDC_IRQ
+# define CONFIG_FT_FDC_IRQ 6
+# endif
+# if CONFIG_FT_FDC_DMA == 0
+# undef CONFIG_FT_FDC_DMA
+# define CONFIG_FT_FDC_DMA 2
+# endif
+#else /* CONFIG_FT_ALT_FDC */
+# if CONFIG_FT_FDC_BASE == 0
+# undef CONFIG_FT_FDC_BASE
+# define CONFIG_FT_FDC_BASE 0x3f0
+# endif
+# if CONFIG_FT_FDC_IRQ == 0
+# undef CONFIG_FT_FDC_IRQ
+# define CONFIG_FT_FDC_IRQ 6
+# endif
+# if CONFIG_FT_FDC_DMA == 0
+# undef CONFIG_FT_FDC_DMA
+# define CONFIG_FT_FDC_DMA 2
+# endif
+#endif /* standard FDC */
+
+/* some useful macro's
+ */
+#define ABS(a) ((a) < 0 ? -(a) : (a))
+#define NR_ITEMS(x) (int)(sizeof(x)/ sizeof(*x))
+
+extern int ftape_init(void);
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/genhd.h b/pfinet/linux-src/include/linux/genhd.h
new file mode 100644
index 00000000..1b625319
--- /dev/null
+++ b/pfinet/linux-src/include/linux/genhd.h
@@ -0,0 +1,267 @@
+#ifndef _LINUX_GENHD_H
+#define _LINUX_GENHD_H
+
+/*
+ * genhd.h Copyright (C) 1992 Drew Eckhardt
+ * Generic hard disk header file by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <asm/unaligned.h>
+
+#define CONFIG_MSDOS_PARTITION 1
+
+#ifdef __alpha__
+#define CONFIG_OSF_PARTITION 1
+#endif
+
+#if defined(__sparc__) || defined(CONFIG_SMD_DISKLABEL)
+#define CONFIG_SUN_PARTITION 1
+#endif
+
+#if defined(CONFIG_SGI)
+#define CONFIG_SGI_PARTITION 1
+#endif
+
+/* These three have identical behaviour; use the second one if DOS fdisk gets
+ confused about extended/logical partitions starting past cylinder 1023. */
+#define DOS_EXTENDED_PARTITION 5
+#define LINUX_EXTENDED_PARTITION 0x85
+#define WIN98_EXTENDED_PARTITION 0x0f
+
+#define LINUX_SWAP_PARTITION 0x82
+#define LINUX_RAID_PARTITION 0xfd /* autodetect RAID partition */
+#define LINUX_OLD_RAID_PARTITION 0x86
+
+#ifdef CONFIG_SOLARIS_X86_PARTITION
+#define SOLARIS_X86_PARTITION LINUX_SWAP_PARTITION
+#endif
+
+#define DM6_PARTITION 0x54 /* has DDO: use xlated geom & offset */
+#define EZD_PARTITION 0x55 /* EZ-DRIVE: remap sector 1 to 0 */
+#define DM6_AUX1PARTITION 0x51 /* no DDO: use xlated geom */
+#define DM6_AUX3PARTITION 0x53 /* no DDO: use xlated geom */
+
+
+struct partition {
+ unsigned char boot_ind; /* 0x80 - active */
+ unsigned char head; /* starting head */
+ unsigned char sector; /* starting sector */
+ unsigned char cyl; /* starting cylinder */
+ unsigned char sys_ind; /* What partition type */
+ unsigned char end_head; /* end head */
+ unsigned char end_sector; /* end sector */
+ unsigned char end_cyl; /* end cylinder */
+ unsigned int start_sect; /* starting sector counting from 0 */
+ unsigned int nr_sects; /* nr of sectors in partition */
+} __attribute__((packed));
+
+struct hd_struct {
+ long start_sect;
+ long nr_sects;
+ int type; /* RAID or normal */
+};
+
+/*
+ * partition types Linux cares about.
+ *
+ * currently there are 'normal' and RAID types.
+ */
+
+static inline unsigned int ptype (unsigned char raw_type)
+{
+ switch (raw_type) {
+ case LINUX_OLD_RAID_PARTITION:
+ return LINUX_OLD_RAID_PARTITION;
+ case LINUX_RAID_PARTITION:
+ return LINUX_RAID_PARTITION;
+ default:
+ }
+ return 0;
+}
+
+/*
+ * the maximum length a given partition name can take (eg. "scd11")
+ */
+#define MAX_DISKNAME_LEN 32
+
+struct gendisk {
+ int major; /* major number of driver */
+ const char *major_name; /* name of major driver */
+ int minor_shift; /* number of times minor is shifted to
+ get real minor */
+ int max_p; /* maximum partitions per device */
+ int max_nr; /* maximum number of real devices */
+
+ void (*init)(struct gendisk *); /* Initialization called before we do our thing */
+ struct hd_struct *part; /* partition table */
+ int *sizes; /* device size in blocks, copied to blk_size[] */
+ int nr_real; /* number of real devices */
+
+ void *real_devices; /* internal use */
+ struct gendisk *next;
+};
+
+#ifdef CONFIG_SOLARIS_X86_PARTITION
+
+#define SOLARIS_X86_NUMSLICE 8
+#define SOLARIS_X86_VTOC_SANE (0x600DDEEEUL)
+
+struct solaris_x86_slice {
+ ushort s_tag; /* ID tag of partition */
+ ushort s_flag; /* permision flags */
+ daddr_t s_start; /* start sector no of partition */
+ long s_size; /* # of blocks in partition */
+};
+
+struct solaris_x86_vtoc {
+ unsigned long v_bootinfo[3]; /* info needed by mboot (unsupported) */
+ unsigned long v_sanity; /* to verify vtoc sanity */
+ unsigned long v_version; /* layout version */
+ char v_volume[8]; /* volume name */
+ ushort v_sectorsz; /* sector size in bytes */
+ ushort v_nparts; /* number of partitions */
+ unsigned long v_reserved[10]; /* free space */
+ struct solaris_x86_slice
+ v_slice[SOLARIS_X86_NUMSLICE]; /* slice headers */
+ time_t timestamp[SOLARIS_X86_NUMSLICE]; /* timestamp (unsupported) */
+ char v_asciilabel[128]; /* for compatibility */
+};
+
+#endif /* CONFIG_SOLARIS_X86_PARTITION */
+
+#ifdef CONFIG_BSD_DISKLABEL
+/*
+ * BSD disklabel support by Yossi Gottlieb <yogo@math.tau.ac.il>
+ * updated by Marc Espie <Marc.Espie@openbsd.org>
+ */
+#define FREEBSD_PARTITION 0xa5 /* FreeBSD Partition ID */
+#define OPENBSD_PARTITION 0xa6 /* OpenBSD Partition ID */
+#define NETBSD_PARTITION 0xa9 /* NetBSD Partition ID */
+#define BSDI_PARTITION 0xb7 /* BSDI Partition ID */
+
+/* Ours is not to wonder why.. */
+#define BSD_PARTITION FREEBSD_PARTITION
+
+/* check against BSD src/sys/sys/disklabel.h for consistency */
+
+#define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */
+#define BSD_MAXPARTITIONS 8
+#define OPENBSD_MAXPARTITIONS 16
+#define BSD_FS_UNUSED 0 /* disklabel unused partition entry ID */
+struct bsd_disklabel {
+ __u32 d_magic; /* the magic number */
+ __s16 d_type; /* drive type */
+ __s16 d_subtype; /* controller/d_type specific */
+ char d_typename[16]; /* type name, e.g. "eagle" */
+ char d_packname[16]; /* pack identifier */
+ __u32 d_secsize; /* # of bytes per sector */
+ __u32 d_nsectors; /* # of data sectors per track */
+ __u32 d_ntracks; /* # of tracks per cylinder */
+ __u32 d_ncylinders; /* # of data cylinders per unit */
+ __u32 d_secpercyl; /* # of data sectors per cylinder */
+ __u32 d_secperunit; /* # of data sectors per unit */
+ __u16 d_sparespertrack; /* # of spare sectors per track */
+ __u16 d_sparespercyl; /* # of spare sectors per cylinder */
+ __u32 d_acylinders; /* # of alt. cylinders per unit */
+ __u16 d_rpm; /* rotational speed */
+ __u16 d_interleave; /* hardware sector interleave */
+ __u16 d_trackskew; /* sector 0 skew, per track */
+ __u16 d_cylskew; /* sector 0 skew, per cylinder */
+ __u32 d_headswitch; /* head switch time, usec */
+ __u32 d_trkseek; /* track-to-track seek, usec */
+ __u32 d_flags; /* generic flags */
+#define NDDATA 5
+ __u32 d_drivedata[NDDATA]; /* drive-type specific information */
+#define NSPARE 5
+ __u32 d_spare[NSPARE]; /* reserved for future use */
+ __u32 d_magic2; /* the magic number (again) */
+ __u16 d_checksum; /* xor of data incl. partitions */
+
+ /* filesystem and partition information: */
+ __u16 d_npartitions; /* number of partitions in following */
+ __u32 d_bbsize; /* size of boot area at sn0, bytes */
+ __u32 d_sbsize; /* max size of fs superblock, bytes */
+ struct bsd_partition { /* the partition table */
+ __u32 p_size; /* number of sectors in partition */
+ __u32 p_offset; /* starting sector */
+ __u32 p_fsize; /* filesystem basic fragment size */
+ __u8 p_fstype; /* filesystem type, see below */
+ __u8 p_frag; /* filesystem fragments per block */
+ __u16 p_cpg; /* filesystem cylinders per group */
+ } d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */
+};
+
+#endif /* CONFIG_BSD_DISKLABEL */
+
+#ifdef CONFIG_UNIXWARE_DISKLABEL
+/*
+ * Unixware slices support by Andrzej Krzysztofowicz <ankry@mif.pg.gda.pl>
+ * and Krzysztof G. Baranowski <kgb@knm.org.pl>
+ */
+
+#define UNIXWARE_PARTITION 0x63 /* Partition ID, same as */
+ /* GNU_HURD and SCO Unix */
+#define UNIXWARE_DISKMAGIC (0xCA5E600DUL) /* The disk magic number */
+#define UNIXWARE_DISKMAGIC2 (0x600DDEEEUL) /* The slice table magic nr */
+#define UNIXWARE_NUMSLICE 16
+#define UNIXWARE_FS_UNUSED 0 /* Unused slice entry ID */
+
+struct unixware_slice {
+ __u16 s_label; /* label */
+ __u16 s_flags; /* permission flags */
+ __u32 start_sect; /* starting sector */
+ __u32 nr_sects; /* number of sectors in slice */
+};
+
+struct unixware_disklabel {
+ __u32 d_type; /* drive type */
+ __u32 d_magic; /* the magic number */
+ __u32 d_version; /* version number */
+ char d_serial[12]; /* serial number of the device */
+ __u32 d_ncylinders; /* # of data cylinders per device */
+ __u32 d_ntracks; /* # of tracks per cylinder */
+ __u32 d_nsectors; /* # of data sectors per track */
+ __u32 d_secsize; /* # of bytes per sector */
+ __u32 d_part_start; /* # of first sector of this partition */
+ __u32 d_unknown1[12]; /* ? */
+ __u32 d_alt_tbl; /* byte offset of alternate table */
+ __u32 d_alt_len; /* byte length of alternate table */
+ __u32 d_phys_cyl; /* # of physical cylinders per device */
+ __u32 d_phys_trk; /* # of physical tracks per cylinder */
+ __u32 d_phys_sec; /* # of physical sectors per track */
+ __u32 d_phys_bytes; /* # of physical bytes per sector */
+ __u32 d_unknown2; /* ? */
+ __u32 d_unknown3; /* ? */
+ __u32 d_pad[8]; /* pad */
+
+ struct unixware_vtoc {
+ __u32 v_magic; /* the magic number */
+ __u32 v_version; /* version number */
+ char v_name[8]; /* volume name */
+ __u16 v_nslices; /* # of slices */
+ __u16 v_unknown1; /* ? */
+ __u32 v_reserved[10]; /* reserved */
+ struct unixware_slice
+ v_slice[UNIXWARE_NUMSLICE]; /* slice headers */
+ } vtoc;
+
+}; /* 408 */
+
+#endif /* CONFIG_UNIXWARE_DISKLABEL */
+
+extern struct gendisk *gendisk_head; /* linked list of disks */
+
+/*
+ * disk_name() is used by genhd.c and md.c.
+ * It formats the devicename of the indicated disk
+ * into the supplied buffer, and returns a pointer
+ * to that same buffer (for convenience).
+ */
+char *disk_name (struct gendisk *hd, int minor, char *buf);
+
+#endif
diff --git a/pfinet/linux-src/include/linux/ghash.h b/pfinet/linux-src/include/linux/ghash.h
new file mode 100644
index 00000000..278f6c2f
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ghash.h
@@ -0,0 +1,218 @@
+/*
+ * include/linux/ghash.h -- generic hashing with fuzzy retrieval
+ *
+ * (C) 1997 Thomas Schoebel-Theuer
+ *
+ * The algorithms implemented here seem to be a completely new invention,
+ * and I'll publish the fundamentals in a paper.
+ */
+
+#ifndef _GHASH_H
+#define _GHASH_H
+/* HASHSIZE _must_ be a power of two!!! */
+
+
+#define DEF_HASH_FUZZY_STRUCTS(NAME,HASHSIZE,TYPE) \
+\
+struct NAME##_table {\
+ TYPE * hashtable[HASHSIZE];\
+ TYPE * sorted_list;\
+ int nr_entries;\
+};\
+\
+struct NAME##_ptrs {\
+ TYPE * next_hash;\
+ TYPE * prev_hash;\
+ TYPE * next_sorted;\
+ TYPE * prev_sorted;\
+};
+
+#define DEF_HASH_FUZZY(LINKAGE,NAME,HASHSIZE,TYPE,PTRS,KEYTYPE,KEY,KEYCMP,KEYEQ,HASHFN)\
+\
+LINKAGE void insert_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\
+{\
+ int ix = HASHFN(elem->KEY);\
+ TYPE ** base = &tbl->hashtable[ix];\
+ TYPE * ptr = *base;\
+ TYPE * prev = NULL;\
+\
+ tbl->nr_entries++;\
+ while(ptr && KEYCMP(ptr->KEY, elem->KEY)) {\
+ base = &ptr->PTRS.next_hash;\
+ prev = ptr;\
+ ptr = *base;\
+ }\
+ elem->PTRS.next_hash = ptr;\
+ elem->PTRS.prev_hash = prev;\
+ if(ptr) {\
+ ptr->PTRS.prev_hash = elem;\
+ }\
+ *base = elem;\
+\
+ ptr = prev;\
+ if(!ptr) {\
+ ptr = tbl->sorted_list;\
+ prev = NULL;\
+ } else {\
+ prev = ptr->PTRS.prev_sorted;\
+ }\
+ while(ptr) {\
+ TYPE * next = ptr->PTRS.next_hash;\
+ if(next && KEYCMP(next->KEY, elem->KEY)) {\
+ prev = ptr;\
+ ptr = next;\
+ } else if(KEYCMP(ptr->KEY, elem->KEY)) {\
+ prev = ptr;\
+ ptr = ptr->PTRS.next_sorted;\
+ } else\
+ break;\
+ }\
+ elem->PTRS.next_sorted = ptr;\
+ elem->PTRS.prev_sorted = prev;\
+ if(ptr) {\
+ ptr->PTRS.prev_sorted = elem;\
+ }\
+ if(prev) {\
+ prev->PTRS.next_sorted = elem;\
+ } else {\
+ tbl->sorted_list = elem;\
+ }\
+}\
+\
+LINKAGE void remove_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\
+{\
+ TYPE * next = elem->PTRS.next_hash;\
+ TYPE * prev = elem->PTRS.prev_hash;\
+\
+ tbl->nr_entries--;\
+ if(next)\
+ next->PTRS.prev_hash = prev;\
+ if(prev)\
+ prev->PTRS.next_hash = next;\
+ else {\
+ int ix = HASHFN(elem->KEY);\
+ tbl->hashtable[ix] = next;\
+ }\
+\
+ next = elem->PTRS.next_sorted;\
+ prev = elem->PTRS.prev_sorted;\
+ if(next)\
+ next->PTRS.prev_sorted = prev;\
+ if(prev)\
+ prev->PTRS.next_sorted = next;\
+ else\
+ tbl->sorted_list = next;\
+}\
+\
+LINKAGE TYPE * find_##NAME##_hash(struct NAME##_table * tbl, KEYTYPE pos)\
+{\
+ int ix = hashfn(pos);\
+ TYPE * ptr = tbl->hashtable[ix];\
+ while(ptr && KEYCMP(ptr->KEY, pos))\
+ ptr = ptr->PTRS.next_hash;\
+ if(ptr && !KEYEQ(ptr->KEY, pos))\
+ ptr = NULL;\
+ return ptr;\
+}\
+\
+LINKAGE TYPE * find_##NAME##_hash_fuzzy(struct NAME##_table * tbl, KEYTYPE pos)\
+{\
+ int ix;\
+ int offset;\
+ TYPE * ptr;\
+ TYPE * next;\
+\
+ ptr = tbl->sorted_list;\
+ if(!ptr || KEYCMP(pos, ptr->KEY))\
+ return NULL;\
+ ix = HASHFN(pos);\
+ offset = HASHSIZE;\
+ do {\
+ offset >>= 1;\
+ next = tbl->hashtable[(ix+offset) & ((HASHSIZE)-1)];\
+ if(next && (KEYCMP(next->KEY, pos) || KEYEQ(next->KEY, pos))\
+ && KEYCMP(ptr->KEY, next->KEY))\
+ ptr = next;\
+ } while(offset);\
+\
+ for(;;) {\
+ next = ptr->PTRS.next_hash;\
+ if(next) {\
+ if(KEYCMP(next->KEY, pos)) {\
+ ptr = next;\
+ continue;\
+ }\
+ }\
+ next = ptr->PTRS.next_sorted;\
+ if(next && KEYCMP(next->KEY, pos)) {\
+ ptr = next;\
+ continue;\
+ }\
+ return ptr;\
+ }\
+ return NULL;\
+}
+
+#define DEF_HASH_STRUCTS(NAME,HASHSIZE,TYPE) \
+\
+struct NAME##_table {\
+ TYPE * hashtable[HASHSIZE];\
+ int nr_entries;\
+};\
+\
+struct NAME##_ptrs {\
+ TYPE * next_hash;\
+ TYPE * prev_hash;\
+};
+
+#define DEF_HASH(LINKAGE,NAME,HASHSIZE,TYPE,PTRS,KEYTYPE,KEY,KEYCMP,KEYEQ,HASHFN)\
+\
+LINKAGE void insert_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\
+{\
+ int ix = HASHFN(elem->KEY);\
+ TYPE ** base = &tbl->hashtable[ix];\
+ TYPE * ptr = *base;\
+ TYPE * prev = NULL;\
+\
+ tbl->nr_entries++;\
+ while(ptr && KEYCMP(ptr->KEY, elem->KEY)) {\
+ base = &ptr->PTRS.next_hash;\
+ prev = ptr;\
+ ptr = *base;\
+ }\
+ elem->PTRS.next_hash = ptr;\
+ elem->PTRS.prev_hash = prev;\
+ if(ptr) {\
+ ptr->PTRS.prev_hash = elem;\
+ }\
+ *base = elem;\
+}\
+\
+LINKAGE void remove_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\
+{\
+ TYPE * next = elem->PTRS.next_hash;\
+ TYPE * prev = elem->PTRS.prev_hash;\
+\
+ tbl->nr_entries--;\
+ if(next)\
+ next->PTRS.prev_hash = prev;\
+ if(prev)\
+ prev->PTRS.next_hash = next;\
+ else {\
+ int ix = HASHFN(elem->KEY);\
+ tbl->hashtable[ix] = next;\
+ }\
+}\
+\
+LINKAGE TYPE * find_##NAME##_hash(struct NAME##_table * tbl, KEYTYPE pos)\
+{\
+ int ix = hashfn(pos);\
+ TYPE * ptr = tbl->hashtable[ix];\
+ while(ptr && KEYCMP(ptr->KEY, pos))\
+ ptr = ptr->PTRS.next_hash;\
+ if(ptr && !KEYEQ(ptr->KEY, pos))\
+ ptr = NULL;\
+ return ptr;\
+}
+
+#endif
diff --git a/pfinet/linux-src/include/linux/hayesesp.h b/pfinet/linux-src/include/linux/hayesesp.h
new file mode 100644
index 00000000..5641ef91
--- /dev/null
+++ b/pfinet/linux-src/include/linux/hayesesp.h
@@ -0,0 +1,126 @@
+#ifndef HAYESESP_H
+#define HAYESESP_H
+
+struct hayes_esp_config {
+ short flow_on;
+ short flow_off;
+ short rx_trigger;
+ short tx_trigger;
+ short pio_threshold;
+ unsigned char rx_timeout;
+ char dma_channel;
+};
+
+#ifdef __KERNEL__
+
+#define ESP_DMA_CHANNEL 0
+#define ESP_RX_TRIGGER 768
+#define ESP_TX_TRIGGER 768
+#define ESP_FLOW_OFF 1016
+#define ESP_FLOW_ON 944
+#define ESP_RX_TMOUT 128
+#define ESP_PIO_THRESHOLD 32
+
+#define ESP_IN_MAJOR 57 /* major dev # for dial in */
+#define ESP_OUT_MAJOR 58 /* major dev # for dial out */
+#define ESPC_SCALE 3
+#define UART_ESI_BASE 0x00
+#define UART_ESI_SID 0x01
+#define UART_ESI_RX 0x02
+#define UART_ESI_TX 0x02
+#define UART_ESI_CMD1 0x04
+#define UART_ESI_CMD2 0x05
+#define UART_ESI_STAT1 0x04
+#define UART_ESI_STAT2 0x05
+#define UART_ESI_RWS 0x07
+
+#define UART_IER_DMA_TMOUT 0x80
+#define UART_IER_DMA_TC 0x08
+
+#define ESI_SET_IRQ 0x04
+#define ESI_SET_DMA_TMOUT 0x05
+#define ESI_SET_SRV_MASK 0x06
+#define ESI_SET_ERR_MASK 0x07
+#define ESI_SET_FLOW_CNTL 0x08
+#define ESI_SET_FLOW_CHARS 0x09
+#define ESI_SET_FLOW_LVL 0x0a
+#define ESI_SET_TRIGGER 0x0b
+#define ESI_SET_RX_TIMEOUT 0x0c
+#define ESI_SET_FLOW_TMOUT 0x0d
+#define ESI_WRITE_UART 0x0e
+#define ESI_READ_UART 0x0f
+#define ESI_SET_MODE 0x10
+#define ESI_GET_ERR_STAT 0x12
+#define ESI_GET_UART_STAT 0x13
+#define ESI_GET_RX_AVAIL 0x14
+#define ESI_GET_TX_AVAIL 0x15
+#define ESI_START_DMA_RX 0x16
+#define ESI_START_DMA_TX 0x17
+#define ESI_ISSUE_BREAK 0x1a
+#define ESI_FLUSH_RX 0x1b
+#define ESI_FLUSH_TX 0x1c
+#define ESI_SET_BAUD 0x1d
+#define ESI_SET_ENH_IRQ 0x1f
+#define ESI_SET_REINTR 0x20
+#define ESI_SET_PRESCALAR 0x23
+#define ESI_NO_COMMAND 0xff
+
+#define ESP_STAT_RX_TIMEOUT 0x01
+#define ESP_STAT_DMA_RX 0x02
+#define ESP_STAT_DMA_TX 0x04
+#define ESP_STAT_NEVER_DMA 0x08
+#define ESP_STAT_USE_PIO 0x10
+
+#define ESP_EVENT_WRITE_WAKEUP 0
+#define ESP_MAGIC 0x53ee
+#define ESP_XMIT_SIZE 4096
+
+struct esp_struct {
+ int magic;
+ int port;
+ int irq;
+ int flags; /* defined in tty.h */
+ struct tty_struct *tty;
+ int read_status_mask;
+ int ignore_status_mask;
+ int timeout;
+ int stat_flags;
+ int custom_divisor;
+ int close_delay;
+ unsigned short closing_wait;
+ unsigned short closing_wait2;
+ int IER; /* Interrupt Enable Register */
+ int MCR; /* Modem control register */
+ unsigned long event;
+ unsigned long last_active;
+ int line;
+ int count; /* # of fd on device */
+ int blocked_open; /* # of blocked opens */
+ long session; /* Session of opening process */
+ long pgrp; /* pgrp of opening process */
+ unsigned char *xmit_buf;
+ int xmit_head;
+ int xmit_tail;
+ int xmit_cnt;
+ struct tq_struct tqueue;
+ struct tq_struct tqueue_hangup;
+ struct termios normal_termios;
+ struct termios callout_termios;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+ struct wait_queue *delta_msr_wait;
+ struct wait_queue *break_wait;
+ struct async_icount icount; /* kernel counters for the 4 input interrupts */
+ struct hayes_esp_config config; /* port configuration */
+ struct esp_struct *next_port; /* For the linked list */
+};
+
+struct esp_pio_buffer {
+ unsigned char data[1024];
+ struct esp_pio_buffer *next;
+};
+
+#endif /* __KERNEL__ */
+
+
+#endif /* ESP_H */
diff --git a/pfinet/linux-src/include/linux/hdlcdrv.h b/pfinet/linux-src/include/linux/hdlcdrv.h
new file mode 100644
index 00000000..11cd8c6a
--- /dev/null
+++ b/pfinet/linux-src/include/linux/hdlcdrv.h
@@ -0,0 +1,383 @@
+/*
+ * hdlcdrv.h -- HDLC packet radio network driver.
+ * The Linux soundcard driver for 1200 baud and 9600 baud packet radio
+ * (C) 1996-1998 by Thomas Sailer, HB9JNX/AE4WA
+ */
+
+#ifndef _HDLCDRV_H
+#define _HDLCDRV_H
+
+/* -------------------------------------------------------------------- */
+/*
+ * structs for the IOCTL commands
+ */
+
+struct hdlcdrv_params {
+ int iobase;
+ int irq;
+ int dma;
+ int dma2;
+ int seriobase;
+ int pariobase;
+ int midiiobase;
+};
+
+struct hdlcdrv_channel_params {
+ int tx_delay; /* the transmitter keyup delay in 10ms units */
+ int tx_tail; /* the transmitter keyoff delay in 10ms units */
+ int slottime; /* the slottime in 10ms; usually 10 = 100ms */
+ int ppersist; /* the p-persistence 0..255 */
+ int fulldup; /* some driver do not support full duplex, setting */
+ /* this just makes them send even if DCD is on */
+};
+
+struct hdlcdrv_old_channel_state {
+ int ptt;
+ int dcd;
+ int ptt_keyed;
+};
+
+struct hdlcdrv_channel_state {
+ int ptt;
+ int dcd;
+ int ptt_keyed;
+ unsigned long tx_packets;
+ unsigned long tx_errors;
+ unsigned long rx_packets;
+ unsigned long rx_errors;
+};
+
+struct hdlcdrv_ioctl {
+ int cmd;
+ union {
+ struct hdlcdrv_params mp;
+ struct hdlcdrv_channel_params cp;
+ struct hdlcdrv_channel_state cs;
+ struct hdlcdrv_old_channel_state ocs;
+ unsigned int calibrate;
+ unsigned char bits;
+ char modename[128];
+ char drivername[32];
+ } data;
+};
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * ioctl values
+ */
+#define HDLCDRVCTL_GETMODEMPAR 0
+#define HDLCDRVCTL_SETMODEMPAR 1
+#define HDLCDRVCTL_MODEMPARMASK 2 /* not handled by hdlcdrv */
+#define HDLCDRVCTL_GETCHANNELPAR 10
+#define HDLCDRVCTL_SETCHANNELPAR 11
+#define HDLCDRVCTL_OLDGETSTAT 20
+#define HDLCDRVCTL_CALIBRATE 21
+#define HDLCDRVCTL_GETSTAT 22
+
+/*
+ * these are mainly for debugging purposes
+ */
+#define HDLCDRVCTL_GETSAMPLES 30
+#define HDLCDRVCTL_GETBITS 31
+
+/*
+ * not handled by hdlcdrv, but by its depending drivers
+ */
+#define HDLCDRVCTL_GETMODE 40
+#define HDLCDRVCTL_SETMODE 41
+#define HDLCDRVCTL_MODELIST 42
+#define HDLCDRVCTL_DRIVERNAME 43
+
+/*
+ * mask of needed modem parameters, returned by HDLCDRVCTL_MODEMPARMASK
+ */
+#define HDLCDRV_PARMASK_IOBASE (1<<0)
+#define HDLCDRV_PARMASK_IRQ (1<<1)
+#define HDLCDRV_PARMASK_DMA (1<<2)
+#define HDLCDRV_PARMASK_DMA2 (1<<3)
+#define HDLCDRV_PARMASK_SERIOBASE (1<<4)
+#define HDLCDRV_PARMASK_PARIOBASE (1<<5)
+#define HDLCDRV_PARMASK_MIDIIOBASE (1<<6)
+
+/* -------------------------------------------------------------------- */
+
+#ifdef __KERNEL__
+
+#include <linux/netdevice.h>
+#include <linux/if.h>
+#include <asm/spinlock.h>
+
+#define HDLCDRV_MAGIC 0x5ac6e778
+#define HDLCDRV_IFNAMELEN 6
+#define HDLCDRV_HDLCBUFFER 32 /* should be a power of 2 for speed reasons */
+#define HDLCDRV_BITBUFFER 256 /* should be a power of 2 for speed reasons */
+#undef HDLCDRV_LOOPBACK /* define for HDLC debugging purposes */
+#define HDLCDRV_DEBUG
+
+/* maximum packet length, excluding CRC */
+#define HDLCDRV_MAXFLEN 400
+
+
+struct hdlcdrv_hdlcbuffer {
+ spinlock_t lock;
+ unsigned rd, wr;
+ unsigned short buf[HDLCDRV_HDLCBUFFER];
+};
+
+#ifdef HDLCDRV_DEBUG
+struct hdlcdrv_bitbuffer {
+ unsigned int rd;
+ unsigned int wr;
+ unsigned int shreg;
+ unsigned char buffer[HDLCDRV_BITBUFFER];
+};
+
+extern inline void hdlcdrv_add_bitbuffer(struct hdlcdrv_bitbuffer *buf,
+ unsigned int bit)
+{
+ unsigned char new;
+
+ new = buf->shreg & 1;
+ buf->shreg >>= 1;
+ buf->shreg |= (!!bit) << 7;
+ if (new) {
+ buf->buffer[buf->wr] = buf->shreg;
+ buf->wr = (buf->wr+1) % sizeof(buf->buffer);
+ buf->shreg = 0x80;
+ }
+}
+
+extern inline void hdlcdrv_add_bitbuffer_word(struct hdlcdrv_bitbuffer *buf,
+ unsigned int bits)
+{
+ buf->buffer[buf->wr] = bits & 0xff;
+ buf->wr = (buf->wr+1) % sizeof(buf->buffer);
+ buf->buffer[buf->wr] = (bits >> 8) & 0xff;
+ buf->wr = (buf->wr+1) % sizeof(buf->buffer);
+
+}
+#endif /* HDLCDRV_DEBUG */
+
+/* -------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each driver.
+ */
+
+struct hdlcdrv_ops {
+ /*
+ * first some informations needed by the hdlcdrv routines
+ */
+ const char *drvname;
+ const char *drvinfo;
+ /*
+ * the routines called by the hdlcdrv routines
+ */
+ int (*open)(struct device *);
+ int (*close)(struct device *);
+ int (*ioctl)(struct device *, struct ifreq *,
+ struct hdlcdrv_ioctl *, int);
+};
+
+struct hdlcdrv_state {
+ int magic;
+
+ char ifname[HDLCDRV_IFNAMELEN];
+
+ const struct hdlcdrv_ops *ops;
+
+ struct {
+ int bitrate;
+ } par;
+
+ struct hdlcdrv_pttoutput {
+ int dma2;
+ int seriobase;
+ int pariobase;
+ int midiiobase;
+ unsigned int flags;
+ } ptt_out;
+
+ struct hdlcdrv_channel_params ch_params;
+
+ struct hdlcdrv_hdlcrx {
+ struct hdlcdrv_hdlcbuffer hbuf;
+ int in_hdlc_rx;
+ /* 0 = sync hunt, != 0 receiving */
+ int rx_state;
+ unsigned int bitstream;
+ unsigned int bitbuf;
+ int numbits;
+ unsigned char dcd;
+
+ int len;
+ unsigned char *bp;
+ unsigned char buffer[HDLCDRV_MAXFLEN+2];
+ } hdlcrx;
+
+ struct hdlcdrv_hdlctx {
+ struct hdlcdrv_hdlcbuffer hbuf;
+ int in_hdlc_tx;
+ /*
+ * 0 = send flags
+ * 1 = send txtail (flags)
+ * 2 = send packet
+ */
+ int tx_state;
+ int numflags;
+ unsigned int bitstream;
+ unsigned char ptt;
+ int calibrate;
+ int slotcnt;
+
+ unsigned int bitbuf;
+ int numbits;
+
+ int len;
+ unsigned char *bp;
+ unsigned char buffer[HDLCDRV_MAXFLEN+2];
+ } hdlctx;
+
+#ifdef HDLCDRV_DEBUG
+ struct hdlcdrv_bitbuffer bitbuf_channel;
+ struct hdlcdrv_bitbuffer bitbuf_hdlc;
+#endif /* HDLCDRV_DEBUG */
+
+#if LINUX_VERSION_CODE < 0x20119
+ struct enet_statistics stats;
+#else
+ struct net_device_stats stats;
+#endif
+ int ptt_keyed;
+
+ struct sk_buff_head send_queue; /* Packets awaiting transmission */
+};
+
+
+/* -------------------------------------------------------------------- */
+
+extern inline int hdlcdrv_hbuf_full(struct hdlcdrv_hdlcbuffer *hb)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&hb->lock, flags);
+ ret = !((HDLCDRV_HDLCBUFFER - 1 + hb->rd - hb->wr) % HDLCDRV_HDLCBUFFER);
+ spin_unlock_irqrestore(&hb->lock, flags);
+ return ret;
+}
+
+/* -------------------------------------------------------------------- */
+
+extern inline int hdlcdrv_hbuf_empty(struct hdlcdrv_hdlcbuffer *hb)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&hb->lock, flags);
+ ret = (hb->rd == hb->wr);
+ spin_unlock_irqrestore(&hb->lock, flags);
+ return ret;
+}
+
+/* -------------------------------------------------------------------- */
+
+extern inline unsigned short hdlcdrv_hbuf_get(struct hdlcdrv_hdlcbuffer *hb)
+{
+ unsigned long flags;
+ unsigned short val;
+ unsigned newr;
+
+ spin_lock_irqsave(&hb->lock, flags);
+ if (hb->rd == hb->wr)
+ val = 0;
+ else {
+ newr = (hb->rd+1) % HDLCDRV_HDLCBUFFER;
+ val = hb->buf[hb->rd];
+ hb->rd = newr;
+ }
+ spin_unlock_irqrestore(&hb->lock, flags);
+ return val;
+}
+
+/* -------------------------------------------------------------------- */
+
+extern inline void hdlcdrv_hbuf_put(struct hdlcdrv_hdlcbuffer *hb,
+ unsigned short val)
+{
+ unsigned newp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hb->lock, flags);
+ newp = (hb->wr+1) % HDLCDRV_HDLCBUFFER;
+ if (newp != hb->rd) {
+ hb->buf[hb->wr] = val & 0xffff;
+ hb->wr = newp;
+ }
+ spin_unlock_irqrestore(&hb->lock, flags);
+}
+
+/* -------------------------------------------------------------------- */
+
+extern inline void hdlcdrv_putbits(struct hdlcdrv_state *s, unsigned int bits)
+{
+ hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, bits);
+}
+
+extern inline unsigned int hdlcdrv_getbits(struct hdlcdrv_state *s)
+{
+ unsigned int ret;
+
+ if (hdlcdrv_hbuf_empty(&s->hdlctx.hbuf)) {
+ if (s->hdlctx.calibrate > 0)
+ s->hdlctx.calibrate--;
+ else
+ s->hdlctx.ptt = 0;
+ ret = 0;
+ } else
+ ret = hdlcdrv_hbuf_get(&s->hdlctx.hbuf);
+#ifdef HDLCDRV_LOOPBACK
+ hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, ret);
+#endif /* HDLCDRV_LOOPBACK */
+ return ret;
+}
+
+extern inline void hdlcdrv_channelbit(struct hdlcdrv_state *s, unsigned int bit)
+{
+#ifdef HDLCDRV_DEBUG
+ hdlcdrv_add_bitbuffer(&s->bitbuf_channel, bit);
+#endif /* HDLCDRV_DEBUG */
+}
+
+extern inline void hdlcdrv_setdcd(struct hdlcdrv_state *s, int dcd)
+{
+ s->hdlcrx.dcd = !!dcd;
+}
+
+extern inline int hdlcdrv_ptt(struct hdlcdrv_state *s)
+{
+ return s->hdlctx.ptt || (s->hdlctx.calibrate > 0);
+}
+
+/* -------------------------------------------------------------------- */
+
+void hdlcdrv_receiver(struct device *, struct hdlcdrv_state *);
+void hdlcdrv_transmitter(struct device *, struct hdlcdrv_state *);
+void hdlcdrv_arbitrate(struct device *, struct hdlcdrv_state *);
+int hdlcdrv_register_hdlcdrv(struct device *dev, const struct hdlcdrv_ops *ops,
+ unsigned int privsize, char *ifname,
+ unsigned int baseaddr, unsigned int irq,
+ unsigned int dma);
+int hdlcdrv_unregister_hdlcdrv(struct device *dev);
+
+/* -------------------------------------------------------------------- */
+
+
+
+#endif /* __KERNEL__ */
+
+/* -------------------------------------------------------------------- */
+
+#endif /* _HDLCDRV_H */
+
+/* -------------------------------------------------------------------- */
diff --git a/pfinet/linux-src/include/linux/hdreg.h b/pfinet/linux-src/include/linux/hdreg.h
new file mode 100644
index 00000000..e467ae9b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/hdreg.h
@@ -0,0 +1,282 @@
+#ifndef _LINUX_HDREG_H
+#define _LINUX_HDREG_H
+
+/*
+ * This file contains some defines for the AT-hd-controller.
+ * Various sources.
+ */
+
+#define HD_IRQ 14 /* the standard disk interrupt */
+
+/* ide.c has its own port definitions in "ide.h" */
+
+/* Hd controller regs. Ref: IBM AT Bios-listing */
+#define HD_DATA 0x1f0 /* _CTL when writing */
+#define HD_ERROR 0x1f1 /* see err-bits */
+#define HD_NSECTOR 0x1f2 /* nr of sectors to read/write */
+#define HD_SECTOR 0x1f3 /* starting sector */
+#define HD_LCYL 0x1f4 /* starting cylinder */
+#define HD_HCYL 0x1f5 /* high byte of starting cyl */
+#define HD_CURRENT 0x1f6 /* 101dhhhh , d=drive, hhhh=head */
+#define HD_STATUS 0x1f7 /* see status-bits */
+#define HD_FEATURE HD_ERROR /* same io address, read=error, write=feature */
+#define HD_PRECOMP HD_FEATURE /* obsolete use of this port - predates IDE */
+#define HD_COMMAND HD_STATUS /* same io address, read=status, write=cmd */
+
+#define HD_CMD 0x3f6 /* used for resets */
+#define HD_ALTSTATUS 0x3f6 /* same as HD_STATUS but doesn't clear irq */
+
+/* remainder is shared between hd.c, ide.c, ide-cd.c, and the hdparm utility */
+
+/* Bits of HD_STATUS */
+#define ERR_STAT 0x01
+#define INDEX_STAT 0x02
+#define ECC_STAT 0x04 /* Corrected error */
+#define DRQ_STAT 0x08
+#define SEEK_STAT 0x10
+#define WRERR_STAT 0x20
+#define READY_STAT 0x40
+#define BUSY_STAT 0x80
+
+/* Values for HD_COMMAND */
+#define WIN_RESTORE 0x10
+#define WIN_READ 0x20
+#define WIN_WRITE 0x30
+#define WIN_WRITE_VERIFY 0x3C
+#define WIN_VERIFY 0x40
+#define WIN_FORMAT 0x50
+#define WIN_INIT 0x60
+#define WIN_SEEK 0x70
+#define WIN_DIAGNOSE 0x90
+#define WIN_SPECIFY 0x91 /* set drive geometry translation */
+#define WIN_SETIDLE1 0xE3
+#define WIN_SETIDLE2 0x97
+
+#define WIN_STANDBYNOW1 0xE0
+#define WIN_STANDBYNOW2 0x94
+#define WIN_SLEEPNOW1 0xE6
+#define WIN_SLEEPNOW2 0x99
+#define WIN_CHECKPOWERMODE1 0xE5
+#define WIN_CHECKPOWERMODE2 0x98
+
+#define WIN_DOORLOCK 0xde /* lock door on removable drives */
+#define WIN_DOORUNLOCK 0xdf /* unlock door on removable drives */
+
+#define WIN_MULTREAD 0xC4 /* read sectors using multiple mode */
+#define WIN_MULTWRITE 0xC5 /* write sectors using multiple mode */
+#define WIN_SETMULT 0xC6 /* enable/disable multiple mode */
+#define WIN_IDENTIFY 0xEC /* ask drive to identify itself */
+#define WIN_IDENTIFY_DMA 0xEE /* same as WIN_IDENTIFY, but DMA */
+#define WIN_SETFEATURES 0xEF /* set special drive features */
+#define WIN_READDMA 0xc8 /* read sectors using DMA transfers */
+#define WIN_WRITEDMA 0xca /* write sectors using DMA transfers */
+
+#define WIN_READ_BUFFER 0xE4 /* force read only 1 sector */
+#define WIN_WRITE_BUFFER 0xE8 /* force write only 1 sector */
+
+#define WIN_SMART 0xb0 /* self-monitoring and reporting */
+
+/* Additional drive command codes used by ATAPI devices. */
+#define WIN_PIDENTIFY 0xA1 /* identify ATAPI device */
+#define WIN_SRST 0x08 /* ATAPI soft reset command */
+#define WIN_PACKETCMD 0xa0 /* Send a packet command. */
+
+/* WIN_SMART sub-commands */
+
+#define SMART_READ_VALUES 0xd0
+#define SMART_READ_THRESHOLDS 0xd1
+#define SMART_AUTOSAVE 0xd2
+#define SMART_SAVE 0xd3
+#define SMART_IMMEDIATE_OFFLINE 0xd4
+#define SMART_ENABLE 0xd8
+#define SMART_DISABLE 0xd9
+#define SMART_STATUS 0xda
+#define SMART_AUTO_OFFLINE 0xdb
+
+/* WIN_SECURITY sub-commands */
+#define SECURITY_SET_PASSWORD 0xBA /* 0xF1 */
+#define SECURITY_UNLOCK 0xBB /* 0xF2 */
+#define SECURITY_ERASE_PREPARE 0xBC /* 0xF3 */
+#define SECURITY_ERASE_UNIT 0xBD /* 0xF4 */
+#define SECURITY_FREEZE_LOCK 0xBE /* 0xF5 */
+#define SECURITY_DISABLE_PASSWORD 0xBF /* 0xF6 */
+
+/* Bits for HD_ERROR */
+#define MARK_ERR 0x01 /* Bad address mark */
+#define TRK0_ERR 0x02 /* couldn't find track 0 */
+#define ABRT_ERR 0x04 /* Command aborted */
+#define MCR_ERR 0x08 /* media change request */
+#define ID_ERR 0x10 /* ID field not found */
+#define ECC_ERR 0x40 /* Uncorrectable ECC error */
+#define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */
+#define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */
+
+struct hd_geometry {
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned short cylinders;
+ unsigned long start;
+};
+
+/* hd/ide ctl's that pass (arg) ptrs to user space are numbered 0x030n/0x031n */
+#define HDIO_GETGEO 0x0301 /* get device geometry */
+#define HDIO_GET_UNMASKINTR 0x0302 /* get current unmask setting */
+#define HDIO_GET_MULTCOUNT 0x0304 /* get current IDE blockmode setting */
+#define HDIO_OBSOLETE_IDENTITY 0x0307 /* OBSOLETE, DO NOT USE: returns 142 bytes */
+#define HDIO_GET_KEEPSETTINGS 0x0308 /* get keep-settings-on-reset flag */
+#define HDIO_GET_32BIT 0x0309 /* get current io_32bit setting */
+#define HDIO_GET_NOWERR 0x030a /* get ignore-write-error flag */
+#define HDIO_GET_DMA 0x030b /* get use-dma flag */
+#define HDIO_GET_NICE 0x030c /* get nice flags */
+#define HDIO_GET_IDENTITY 0x030d /* get IDE identification info */
+#define HDIO_DRIVE_CMD 0x031f /* execute a special drive command */
+
+/* hd/ide ctl's that pass (arg) non-ptr values are numbered 0x032n/0x033n */
+#define HDIO_SET_MULTCOUNT 0x0321 /* change IDE blockmode */
+#define HDIO_SET_UNMASKINTR 0x0322 /* permit other irqs during I/O */
+#define HDIO_SET_KEEPSETTINGS 0x0323 /* keep ioctl settings on reset */
+#define HDIO_SET_32BIT 0x0324 /* change io_32bit flags */
+#define HDIO_SET_NOWERR 0x0325 /* change ignore-write-error flag */
+#define HDIO_SET_DMA 0x0326 /* change use-dma flag */
+#define HDIO_SET_PIO_MODE 0x0327 /* reconfig interface to new speed */
+#define HDIO_SCAN_HWIF 0x0328 /* register and (re)scan interface */
+#define HDIO_SET_NICE 0x0329 /* set nice flags */
+#define HDIO_UNREGISTER_HWIF 0x032a /* unregister interface */
+
+/* structure returned by HDIO_GET_IDENTITY, as per ANSI ATA2 rev.2f spec */
+struct hd_driveid {
+ unsigned short config; /* lots of obsolete bit flags */
+ unsigned short cyls; /* "physical" cyls */
+ unsigned short reserved2; /* reserved (word 2) */
+ unsigned short heads; /* "physical" heads */
+ unsigned short track_bytes; /* unformatted bytes per track */
+ unsigned short sector_bytes; /* unformatted bytes per sector */
+ unsigned short sectors; /* "physical" sectors per track */
+ unsigned short vendor0; /* vendor unique */
+ unsigned short vendor1; /* vendor unique */
+ unsigned short vendor2; /* vendor unique */
+ unsigned char serial_no[20]; /* 0 = not_specified */
+ unsigned short buf_type;
+ unsigned short buf_size; /* 512 byte increments; 0 = not_specified */
+ unsigned short ecc_bytes; /* for r/w long cmds; 0 = not_specified */
+ unsigned char fw_rev[8]; /* 0 = not_specified */
+ unsigned char model[40]; /* 0 = not_specified */
+ unsigned char max_multsect; /* 0=not_implemented */
+ unsigned char vendor3; /* vendor unique */
+ unsigned short dword_io; /* 0=not_implemented; 1=implemented */
+ unsigned char vendor4; /* vendor unique */
+ unsigned char capability; /* bits 0:DMA 1:LBA 2:IORDYsw 3:IORDYsup*/
+ unsigned short reserved50; /* reserved (word 50) */
+ unsigned char vendor5; /* vendor unique */
+ unsigned char tPIO; /* 0=slow, 1=medium, 2=fast */
+ unsigned char vendor6; /* vendor unique */
+ unsigned char tDMA; /* 0=slow, 1=medium, 2=fast */
+ unsigned short field_valid; /* bits 0:cur_ok 1:eide_ok */
+ unsigned short cur_cyls; /* logical cylinders */
+ unsigned short cur_heads; /* logical heads */
+ unsigned short cur_sectors; /* logical sectors per track */
+ unsigned short cur_capacity0; /* logical total sectors on drive */
+ unsigned short cur_capacity1; /* (2 words, misaligned int) */
+ unsigned char multsect; /* current multiple sector count */
+ unsigned char multsect_valid; /* when (bit0==1) multsect is ok */
+ unsigned int lba_capacity; /* total number of sectors */
+ unsigned short dma_1word; /* single-word dma info */
+ unsigned short dma_mword; /* multiple-word dma info */
+ unsigned short eide_pio_modes; /* bits 0:mode3 1:mode4 */
+ unsigned short eide_dma_min; /* min mword dma cycle time (ns) */
+ unsigned short eide_dma_time; /* recommended mword dma cycle time (ns) */
+ unsigned short eide_pio; /* min cycle time (ns), no IORDY */
+ unsigned short eide_pio_iordy; /* min cycle time (ns), with IORDY */
+ unsigned short word69;
+ unsigned short word70;
+ /* HDIO_GET_IDENTITY currently returns only words 0 through 70 */
+ unsigned short word71;
+ unsigned short word72;
+ unsigned short word73;
+ unsigned short word74;
+ unsigned short word75;
+ unsigned short word76;
+ unsigned short word77;
+ unsigned short word78;
+ unsigned short word79;
+ unsigned short word80;
+ unsigned short word81;
+ unsigned short command_sets; /* bits 0:Smart 1:Security 2:Removable 3:PM */
+ unsigned short word83; /* bits 14:Smart Enabled 13:0 zero */
+ unsigned short word84;
+ unsigned short word85;
+ unsigned short word86;
+ unsigned short word87;
+ unsigned short dma_ultra;
+ unsigned short word89; /* reserved (word 89) */
+ unsigned short word90; /* reserved (word 90) */
+ unsigned short word91; /* reserved (word 91) */
+ unsigned short word92; /* reserved (word 92) */
+ unsigned short word93; /* reserved (word 93) */
+ unsigned short word94; /* reserved (word 94) */
+ unsigned short word95; /* reserved (word 95) */
+ unsigned short word96; /* reserved (word 96) */
+ unsigned short word97; /* reserved (word 97) */
+ unsigned short word98; /* reserved (word 98) */
+ unsigned short word99; /* reserved (word 99) */
+ unsigned short word100; /* reserved (word 100) */
+ unsigned short word101; /* reserved (word 101) */
+ unsigned short word102; /* reserved (word 102) */
+ unsigned short word103; /* reserved (word 103) */
+ unsigned short word104; /* reserved (word 104) */
+ unsigned short word105; /* reserved (word 105) */
+ unsigned short word106; /* reserved (word 106) */
+ unsigned short word107; /* reserved (word 107) */
+ unsigned short word108; /* reserved (word 108) */
+ unsigned short word109; /* reserved (word 109) */
+ unsigned short word110; /* reserved (word 110) */
+ unsigned short word111; /* reserved (word 111) */
+ unsigned short word112; /* reserved (word 112) */
+ unsigned short word113; /* reserved (word 113) */
+ unsigned short word114; /* reserved (word 114) */
+ unsigned short word115; /* reserved (word 115) */
+ unsigned short word116; /* reserved (word 116) */
+ unsigned short word117; /* reserved (word 117) */
+ unsigned short word118; /* reserved (word 118) */
+ unsigned short word119; /* reserved (word 119) */
+ unsigned short word120; /* reserved (word 120) */
+ unsigned short word121; /* reserved (word 121) */
+ unsigned short word122; /* reserved (word 122) */
+ unsigned short word123; /* reserved (word 123) */
+ unsigned short word124; /* reserved (word 124) */
+ unsigned short word125; /* reserved (word 125) */
+ unsigned short word126; /* reserved (word 126) */
+ unsigned short word127; /* reserved (word 127) */
+ unsigned short security; /* bits 0:support 1:enabled 2:locked 3:frozen */
+ unsigned short reserved[127];
+};
+
+/*
+ * IDE "nice" flags. These are used on a per drive basis to determine
+ * when to be nice and give more bandwidth to the other devices which
+ * share the same IDE bus.
+ */
+#define IDE_NICE_DSC_OVERLAP (0) /* per the DSC overlap protocol */
+#define IDE_NICE_ATAPI_OVERLAP (1) /* not supported yet */
+#define IDE_NICE_0 (2) /* when sure that it won't affect us */
+#define IDE_NICE_1 (3) /* when probably won't affect us much */
+#define IDE_NICE_2 (4) /* when we know it's on our expense */
+
+#ifdef __KERNEL__
+/*
+ * These routines are used for kernel command line parameters from main.c:
+ */
+#include <linux/config.h>
+
+#ifdef CONFIG_BLK_DEV_HD
+void hd_setup(char *, int *);
+#endif /* CONFIG_BLK_DEV_HD */
+
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+int ide_register(int io_port, int ctl_port, int irq);
+void ide_unregister(unsigned int);
+#endif /* CONFIG_BLK_DEV_IDE || CONFIG_BLK_DEV_IDE_MODULE */
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_HDREG_H */
diff --git a/pfinet/linux-src/include/linux/hfmodem.h b/pfinet/linux-src/include/linux/hfmodem.h
new file mode 100644
index 00000000..7b35f116
--- /dev/null
+++ b/pfinet/linux-src/include/linux/hfmodem.h
@@ -0,0 +1,256 @@
+/*****************************************************************************/
+
+/*
+ * hfmodem.h -- Linux soundcard HF FSK driver.
+ *
+ * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * Swiss Federal Institute of Technology (ETH), Electronics Lab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * This is the Linux realtime sound output driver
+ */
+
+/*****************************************************************************/
+
+#ifndef _HFMODEM_H
+#define _HFMODEM_H
+/* --------------------------------------------------------------------- */
+
+#include <linux/version.h>
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#if LINUX_VERSION_CODE >= 0x20100
+#include <linux/poll.h>
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#define HFMODEM_MINOR 145
+
+#define HFMODEM_SRATE 8000
+#define HFMODEM_MAXBITS 4800 /* required for GTOR 300 baud mode */
+#define HFMODEM_MINBAUD 40
+#define HFMODEM_MAXBAUD 400
+#define HFMODEM_MAXCORRLEN ((HFMODEM_SRATE+HFMODEM_MINBAUD-1)/HFMODEM_MINBAUD)
+
+/* --------------------------------------------------------------------- */
+
+typedef unsigned long hfmodem_time_t;
+typedef int hfmodem_soft_t;
+typedef unsigned long hfmodem_id_t;
+
+/* --------------------------------------------------------------------- */
+
+struct hfmodem_ioctl_fsk_tx_request {
+ hfmodem_time_t tstart;
+ hfmodem_time_t tinc;
+ int inv;
+ hfmodem_id_t id;
+ unsigned int nbits;
+ unsigned char *data;
+ unsigned int freq[2];
+};
+
+struct hfmodem_ioctl_fsk_rx_request {
+ hfmodem_time_t tstart;
+ hfmodem_time_t tinc;
+ unsigned int baud;
+ hfmodem_id_t id;
+ unsigned int nbits;
+ hfmodem_soft_t *data;
+ unsigned int freq[2];
+};
+
+struct hfmodem_ioctl_mixer_params {
+ int src;
+ int igain;
+ int ogain;
+};
+
+struct hfmodem_ioctl_sample_params {
+ __s16 *data;
+ int len;
+};
+
+#define HFMODEM_IOCTL_FSKTXREQUEST _IOW('H', 0, struct hfmodem_ioctl_fsk_tx_request)
+#define HFMODEM_IOCTL_FSKRXREQUEST _IOW('H', 1, struct hfmodem_ioctl_fsk_rx_request)
+#define HFMODEM_IOCTL_CLEARRQ _IO('H', 3)
+#define HFMODEM_IOCTL_GETCURTIME _IOR('H', 4, hfmodem_time_t)
+#define HFMODEM_IOCTL_WAITRQ _IOR('H', 5, hfmodem_id_t)
+#define HFMODEM_IOCTL_MIXERPARAMS _IOW('H', 6, struct hfmodem_ioctl_mixer_params)
+#define HFMODEM_IOCTL_SAMPLESTART _IOW('H', 7, struct hfmodem_ioctl_sample_params)
+#define HFMODEM_IOCTL_SAMPLEFINISHED _IO('H', 8)
+
+/* --------------------------------------------------------------------- */
+#ifdef __KERNEL__
+
+#include <linux/parport.h>
+
+#define DMA_MODE_AUTOINIT 0x10
+
+#define NR_DEVICE 1
+
+#define HFMODEM_FRAGSAMPLES (HFMODEM_SRATE/100)
+#define HFMODEM_FRAGSIZE (HFMODEM_FRAGSAMPLES*2)
+#define HFMODEM_NUMFRAGS 8
+#define HFMODEM_EXCESSFRAGS 3
+
+#define HFMODEM_NUMRXSLOTS 20
+#define HFMODEM_NUMTXSLOTS 4
+
+#define HFMODEM_CORRELATOR_CACHE 8
+
+enum slot_st { ss_unused = 0, ss_ready, ss_oper, ss_retired };
+typedef int hfmodem_conv_t;
+
+struct hfmodem_state {
+ const struct hfmodem_scops *scops;
+
+ /* io params */
+ struct {
+ unsigned int base_addr;
+ unsigned int dma;
+ unsigned int irq;
+ } io;
+
+ struct {
+ unsigned int seriobase;
+ unsigned int pariobase;
+ unsigned int midiiobase;
+ unsigned int flags;
+ struct pardevice *pardev;
+ } ptt_out;
+
+ struct {
+ __s16 *buf;
+ unsigned int lastfrag;
+ unsigned int fragptr;
+ unsigned int last_dmaptr;
+ int ptt_frames;
+ } dma;
+
+ struct {
+ unsigned int last_tvusec;
+ unsigned long long time_cnt;
+ hfmodem_time_t lasttime;
+#ifdef __i386__
+ unsigned int starttime_lo, starttime_hi;
+#endif /* __i386__ */
+ } clk;
+
+ int active;
+ struct wait_queue *wait;
+
+ struct {
+ __s16 *kbuf;
+ __s16 *ubuf;
+ __s16 *kptr;
+ unsigned int size;
+ int rem;
+ } sbuf;
+
+ struct {
+ hfmodem_time_t last_time;
+ unsigned int tx_phase;
+
+ struct hfmodem_l1_rxslot {
+ enum slot_st state;
+ hfmodem_time_t tstart, tinc;
+ hfmodem_soft_t *data;
+ hfmodem_soft_t *userdata;
+ unsigned int nbits;
+ unsigned int cntbits;
+ hfmodem_id_t id;
+ unsigned int corrlen;
+ hfmodem_conv_t scale;
+ unsigned int corr_cache;
+ } rxslots[HFMODEM_NUMRXSLOTS];
+
+ struct hfmodem_l1_txslot {
+ enum slot_st state;
+ hfmodem_time_t tstart, tinc;
+ unsigned char *data;
+ unsigned int nbits;
+ unsigned int cntbits;
+ hfmodem_id_t id;
+ unsigned char inv;
+ unsigned int phinc;
+ unsigned int phase_incs[2];
+ } txslots[HFMODEM_NUMTXSLOTS];
+ } l1;
+};
+
+struct hfmodem_correlator_cache {
+ int refcnt;
+ int lru;
+ unsigned short phase_incs[2];
+ hfmodem_conv_t correlator[2][2][HFMODEM_MAXCORRLEN];
+};
+
+struct hfmodem_scops {
+ unsigned int extent;
+
+ void (*init)(struct hfmodem_state *dev);
+ void (*prepare_input)(struct hfmodem_state *dev);
+ void (*trigger_input)(struct hfmodem_state *dev);
+ void (*prepare_output)(struct hfmodem_state *dev);
+ void (*trigger_output)(struct hfmodem_state *dev);
+ void (*stop)(struct hfmodem_state *dev);
+ unsigned int (*intack)(struct hfmodem_state *dev);
+ void (*mixer)(struct hfmodem_state *dev, int src, int igain, int ogain);
+};
+
+/* --------------------------------------------------------------------- */
+
+extern int hfmodem_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+#if LINUX_VERSION_CODE >= 0x20100
+extern unsigned int hfmodem_poll(struct file *file, poll_table *wait);
+#else
+extern int hfmodem_select(struct inode *inode, struct file *file, int sel_type, select_table *wait);
+#endif
+
+extern void hfmodem_clear_rq(struct hfmodem_state *dev);
+extern void hfmodem_input_samples(struct hfmodem_state *dev, hfmodem_time_t tstart,
+ hfmodem_time_t tinc, __s16 *samples);
+extern int hfmodem_output_samples(struct hfmodem_state *dev, hfmodem_time_t tstart,
+ hfmodem_time_t tinc, __s16 *samples);
+extern long hfmodem_next_tx_event(struct hfmodem_state *dev, hfmodem_time_t curr);
+extern void hfmodem_finish_pending_rx_requests(struct hfmodem_state *dev);
+extern void hfmodem_wakeup(struct hfmodem_state *dev);
+
+
+extern int hfmodem_sbcprobe(struct hfmodem_state *dev);
+extern int hfmodem_wssprobe(struct hfmodem_state *dev);
+
+extern void hfmodem_refclock_probe(void);
+extern void hfmodem_refclock_init(struct hfmodem_state *dev);
+extern hfmodem_time_t hfmodem_refclock_current(struct hfmodem_state *dev, hfmodem_time_t expected, int exp_valid);
+
+/* --------------------------------------------------------------------- */
+
+extern const char hfmodem_drvname[];
+extern const char hfmodem_drvinfo[];
+
+extern struct hfmodem_state hfmodem_state[NR_DEVICE];
+extern struct hfmodem_correlator_cache hfmodem_correlator_cache[HFMODEM_CORRELATOR_CACHE];
+
+/* --------------------------------------------------------------------- */
+#endif /* __KERNEL__ */
+/* --------------------------------------------------------------------- */
+#endif /* _HFMODEM_H */
diff --git a/pfinet/linux-src/include/linux/hfs_fs.h b/pfinet/linux-src/include/linux/hfs_fs.h
new file mode 100644
index 00000000..851bcb0d
--- /dev/null
+++ b/pfinet/linux-src/include/linux/hfs_fs.h
@@ -0,0 +1,337 @@
+/*
+ * linux/include/linux/hfs_fs.h
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * The source code distribution of the Columbia AppleTalk Package for
+ * UNIX, version 6.0, (CAP) was used as a specification of the
+ * location and format of files used by CAP's Aufs. No code from CAP
+ * appears in hfs_fs. hfs_fs is not a work ``derived'' from CAP in
+ * the sense of intellectual property law.
+ *
+ * The source code distributions of Netatalk, versions 1.3.3b2 and
+ * 1.4b2, were used as a specification of the location and format of
+ * files used by Netatalk's afpd. No code from Netatalk appears in
+ * hfs_fs. hfs_fs is not a work ``derived'' from Netatalk in the
+ * sense of intellectual property law.
+ */
+
+#ifndef _LINUX_HFS_FS_H
+#define _LINUX_HFS_FS_H
+
+#include <linux/hfs_sysdep.h>
+
+/* magic numbers for Apple Double header files */
+#define HFS_DBL_MAGIC 0x00051607
+#define HFS_SNGL_MAGIC 0x00051600
+#define HFS_HDR_VERSION_1 0x00010000
+#define HFS_HDR_VERSION_2 0x00020000
+
+/* magic numbers for various internal structures */
+#define HFS_INO_MAGIC 0x4821
+#define HFS_SB_MAGIC 0x4822
+
+/* The space used for the AppleDouble or AppleSingle headers */
+#define HFS_DBL_HDR_LEN 1024
+
+/* The space used for the Netatalk header */
+#define HFS_NAT_HDR_LEN 1024 /* 589 for an exact match */
+
+/* Macros to extract CNID and file "type" from the Linux inode number */
+#define HFS_CNID(X) ((X) & 0x3FFFFFFF)
+#define HFS_ITYPE(X) ((X) & 0xC0000000)
+
+/* Macros to enumerate types */
+#define HFS_ITYPE_TO_INT(X) ((X) >> 30)
+#define HFS_INT_TO_ITYPE(X) ((X) << 30)
+
+/* generic ITYPEs */
+#define HFS_ITYPE_0 0x00000000
+#define HFS_ITYPE_1 0x40000000
+#define HFS_ITYPE_2 0x80000000
+#define HFS_ITYPE_3 0xC0000000
+#define HFS_ITYPE_NORM HFS_ITYPE_0 /* "normal" directory or file */
+
+/* ITYPEs for CAP */
+#define HFS_CAP_NORM HFS_ITYPE_0 /* data fork or normal directory */
+#define HFS_CAP_DATA HFS_ITYPE_0 /* data fork of file */
+#define HFS_CAP_NDIR HFS_ITYPE_0 /* normal directory */
+#define HFS_CAP_FNDR HFS_ITYPE_1 /* finder info for file or dir */
+#define HFS_CAP_RSRC HFS_ITYPE_2 /* resource fork of file */
+#define HFS_CAP_RDIR HFS_ITYPE_2 /* .resource directory */
+#define HFS_CAP_FDIR HFS_ITYPE_3 /* .finderinfo directory */
+
+/* ITYPEs for Apple Double */
+#define HFS_DBL_NORM HFS_ITYPE_0 /* data fork or directory */
+#define HFS_DBL_DATA HFS_ITYPE_0 /* data fork of file */
+#define HFS_DBL_DIR HFS_ITYPE_0 /* directory */
+#define HFS_DBL_HDR HFS_ITYPE_1 /* AD header of file or dir */
+
+/* ITYPEs for netatalk */
+#define HFS_NAT_NORM HFS_ITYPE_0 /* data fork or directory */
+#define HFS_NAT_DATA HFS_ITYPE_0 /* data fork of file */
+#define HFS_NAT_NDIR HFS_ITYPE_0 /* normal directory */
+#define HFS_NAT_HDR HFS_ITYPE_1 /* AD header of file or dir */
+#define HFS_NAT_HDIR HFS_ITYPE_2 /* directory holding AD headers */
+
+/* ITYPEs for Apple Single */
+#define HFS_SGL_NORM HFS_ITYPE_0 /* AppleSingle file or directory */
+#define HFS_SGL_SNGL HFS_ITYPE_0 /* AppleSingle file */
+#define HFS_SGL_DIR HFS_ITYPE_0 /* directory */
+#define HFS_SGL_DINF HFS_ITYPE_1 /* %DirInfo for directory */
+
+/* IDs for elements of an AppleDouble or AppleSingle header */
+#define HFS_HDR_DATA 1 /* data fork */
+#define HFS_HDR_RSRC 2 /* resource fork */
+#define HFS_HDR_FNAME 3 /* full (31-character) name */
+#define HFS_HDR_COMNT 4 /* comment */
+#define HFS_HDR_BWICN 5 /* b/w icon */
+#define HFS_HDR_CICON 6 /* color icon info */
+#define HFS_HDR_OLDI 7 /* old file info */
+#define HFS_HDR_DATES 8 /* file dates info */
+#define HFS_HDR_FINFO 9 /* Finder info */
+#define HFS_HDR_MACI 10 /* Macintosh info */
+#define HFS_HDR_PRODOSI 11 /* ProDOS info */
+#define HFS_HDR_MSDOSI 12 /* MSDOS info */
+#define HFS_HDR_SNAME 13 /* short name */
+#define HFS_HDR_AFPI 14 /* AFP file info */
+#define HFS_HDR_DID 15 /* directory id */
+#define HFS_HDR_MAX 16
+
+/*
+ * There are three time systems. All three are based on seconds since
+ * a particular time/date.
+ * Unix: unsigned lil-endian since 00:00 GMT, Jan. 1, 1970
+ * mac: unsigned big-endian since 00:00 GMT, Jan. 1, 1904
+ * header: SIGNED big-endian since 00:00 GMT, Jan. 1, 2000
+ *
+ */
+#define hfs_h_to_mtime(ARG) htonl((hfs_s32)ntohl(ARG)+3029529600U)
+#define hfs_m_to_htime(ARG) ((hfs_s32)htonl(ntohl(ARG)-3029529600U))
+#define hfs_h_to_utime(ARG) ((hfs_s32)hfs_to_utc(ntohl(ARG)+946684800U))
+#define hfs_u_to_htime(ARG) ((hfs_s32)htonl(hfs_from_utc(ARG)-946684800U))
+#define hfs_u_to_mtime(ARG) htonl(hfs_from_utc(ARG)+2082844800U)
+#define hfs_m_to_utime(ARG) (hfs_to_utc(ntohl(ARG)-2082844800U))
+
+/*======== Data structures kept in memory ========*/
+
+/*
+ * A descriptor for a single entry within the header of an
+ * AppleDouble or AppleSingle header file.
+ * An array of these make up a table of contents for the file.
+ */
+struct hfs_hdr_descr {
+ hfs_u32 id; /* The Apple assigned ID for the entry type */
+ hfs_u32 offset; /* The offset to reach the entry */
+ hfs_u32 length; /* The length of the entry */
+};
+
+/*
+ * The info needed to reconstruct a given header layout
+ */
+struct hfs_hdr_layout {
+ hfs_u32 magic; /* AppleSingle or AppleDouble */
+ hfs_u32 version; /* 0x00010000 or 0x00020000 */
+ hfs_u16 entries; /* How many entries used */
+ struct hfs_hdr_descr
+ descr[HFS_HDR_MAX]; /* Descriptors */
+ struct hfs_hdr_descr
+ *order[HFS_HDR_MAX]; /* 'descr' ordered by offset */
+};
+
+/* header layout for netatalk's v1 appledouble file format */
+struct hfs_nat_hdr {
+ hfs_lword_t magic;
+ hfs_lword_t version;
+ hfs_byte_t homefs[16];
+ hfs_word_t entries;
+ hfs_byte_t descrs[12*5];
+ hfs_byte_t real_name[255]; /* id=3 */
+ hfs_byte_t comment[200]; /* id=4 XXX: not yet implemented */
+ hfs_byte_t old_info[16]; /* id=7 */
+ hfs_u8 finderinfo[32]; /* id=9 */
+};
+
+/*
+ * Default header layout for Netatalk and AppleDouble
+ */
+struct hfs_dbl_hdr {
+ hfs_lword_t magic;
+ hfs_lword_t version;
+ hfs_byte_t filler[16];
+ hfs_word_t entries;
+ hfs_byte_t descrs[12*HFS_HDR_MAX];
+ hfs_byte_t real_name[255]; /* id=3 */
+ hfs_byte_t comment[200]; /* id=4 XXX: not yet implemented */
+ hfs_u32 create_time; /* \ */
+ hfs_u32 modify_time; /* | id=8 (or 7) */
+ hfs_u32 backup_time; /* | */
+ hfs_u32 access_time; /* / (attributes with id=7) */
+ hfs_u8 finderinfo[32]; /* id=9 */
+ hfs_u32 fileinfo; /* id=10 */
+ hfs_u32 cnid; /* id=15 */
+ hfs_u8 short_name[12]; /* id=13 */
+ hfs_u8 prodosi[8]; /* id=11 */
+};
+
+/* finder metadata for CAP */
+struct hfs_cap_info {
+ hfs_byte_t fi_fndr[32]; /* Finder's info */
+ hfs_word_t fi_attr; /* AFP attributes (f=file/d=dir) */
+#define HFS_AFP_INV 0x001 /* Invisible bit (f/d) */
+#define HFS_AFP_EXPFOLDER 0x002 /* exported folder (d) */
+#define HFS_AFP_MULTI 0x002 /* Multiuser bit (f) */
+#define HFS_AFP_SYS 0x004 /* System bit (f/d) */
+#define HFS_AFP_DOPEN 0x008 /* data fork already open (f) */
+#define HFS_AFP_MOUNTED 0x008 /* mounted folder (d) */
+#define HFS_AFP_ROPEN 0x010 /* resource fork already open (f) */
+#define HFS_AFP_INEXPFOLDER 0x010 /* folder in shared area (d) */
+#define HFS_AFP_WRI 0x020 /* Write inhibit bit (readonly) (f) */
+#define HFS_AFP_BACKUP 0x040 /* backup needed bit (f/d) */
+#define HFS_AFP_RNI 0x080 /* Rename inhibit bit (f/d) */
+#define HFS_AFP_DEI 0x100 /* Delete inhibit bit (f/d) */
+#define HFS_AFP_NOCOPY 0x400 /* Copy protect bit (f) */
+#define HFS_AFP_RDONLY ( HFS_AFP_WRI|HFS_AFP_RNI|HFS_AFP_DEI)
+ hfs_byte_t fi_magic1; /* Magic number: */
+#define HFS_CAP_MAGIC1 0xFF
+ hfs_byte_t fi_version; /* Version of this structure: */
+#define HFS_CAP_VERSION 0x10
+ hfs_byte_t fi_magic; /* Another magic number: */
+#define HFS_CAP_MAGIC 0xDA
+ hfs_byte_t fi_bitmap; /* Bitmap of which names are valid: */
+#define HFS_CAP_SHORTNAME 0x01
+#define HFS_CAP_LONGNAME 0x02
+ hfs_byte_t fi_shortfilename[12+1]; /* "short name" (unused) */
+ hfs_byte_t fi_macfilename[32+1]; /* Original (Macintosh) name */
+ hfs_byte_t fi_comln; /* Length of comment (always 0) */
+ hfs_byte_t fi_comnt[200]; /* Finder comment (unused) */
+ /* optional: used by aufs only if compiled with USE_MAC_DATES */
+ hfs_byte_t fi_datemagic; /* Magic number for dates extension: */
+#define HFS_CAP_DMAGIC 0xDA
+ hfs_byte_t fi_datevalid; /* Bitmap of which dates are valid: */
+#define HFS_CAP_MDATE 0x01
+#define HFS_CAP_CDATE 0x02
+ hfs_lword_t fi_ctime; /* Creation date (in AFP format) */
+ hfs_lword_t fi_mtime; /* Modify date (in AFP format) */
+ hfs_lword_t fi_utime; /* Un*x time of last mtime change */
+ hfs_byte_t pad;
+};
+
+#ifdef __KERNEL__
+
+typedef ssize_t hfs_rwret_t;
+typedef size_t hfs_rwarg_t;
+
+#include <asm/uaccess.h>
+
+/* Some forward declarations */
+struct hfs_fork;
+struct hfs_cat_key;
+struct hfs_cat_entry;
+extern struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *,
+ const struct hfs_cat_key *);
+
+/* dir.c */
+extern hfs_rwret_t hfs_dir_read(struct file *, char *, hfs_rwarg_t,
+ loff_t *);
+extern int hfs_create(struct inode *, struct dentry *, int);
+extern int hfs_mkdir(struct inode *, struct dentry *, int);
+extern int hfs_mknod(struct inode *, struct dentry *, int, int);
+extern int hfs_unlink(struct inode *, struct dentry *);
+extern int hfs_rmdir(struct inode *, struct dentry *);
+extern int hfs_rename(struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
+
+/* dir_cap.c */
+extern const struct hfs_name hfs_cap_reserved1[];
+extern const struct hfs_name hfs_cap_reserved2[];
+extern struct inode_operations hfs_cap_ndir_inode_operations;
+extern struct inode_operations hfs_cap_fdir_inode_operations;
+extern struct inode_operations hfs_cap_rdir_inode_operations;
+extern void hfs_cap_drop_dentry(struct dentry *, const ino_t);
+
+/* dir_dbl.c */
+extern const struct hfs_name hfs_dbl_reserved1[];
+extern const struct hfs_name hfs_dbl_reserved2[];
+extern struct inode_operations hfs_dbl_dir_inode_operations;
+extern void hfs_dbl_drop_dentry(struct dentry *, const ino_t);
+
+/* dir_nat.c */
+extern const struct hfs_name hfs_nat_reserved1[];
+extern const struct hfs_name hfs_nat_reserved2[];
+extern struct inode_operations hfs_nat_ndir_inode_operations;
+extern struct inode_operations hfs_nat_hdir_inode_operations;
+extern void hfs_nat_drop_dentry(struct dentry *, const ino_t);
+
+/* dir_sngl.c */
+extern const struct hfs_name hfs_sngl_reserved1[];
+extern const struct hfs_name hfs_sngl_reserved2[];
+extern struct inode_operations hfs_sngl_dir_inode_operations;
+
+/* file.c */
+extern hfs_s32 hfs_do_read(struct inode *, struct hfs_fork *, hfs_u32,
+ char *, hfs_u32, int);
+extern hfs_s32 hfs_do_write(struct inode *, struct hfs_fork *, hfs_u32,
+ const char *, hfs_u32);
+extern void hfs_file_fix_mode(struct hfs_cat_entry *entry);
+extern struct inode_operations hfs_file_inode_operations;
+
+/* file_cap.c */
+extern struct inode_operations hfs_cap_info_inode_operations;
+
+/* file_hdr.c */
+extern struct inode_operations hfs_hdr_inode_operations;
+extern const struct hfs_hdr_layout hfs_dbl_fil_hdr_layout;
+extern const struct hfs_hdr_layout hfs_dbl_dir_hdr_layout;
+extern const struct hfs_hdr_layout hfs_nat_hdr_layout;
+extern const struct hfs_hdr_layout hfs_nat2_hdr_layout;
+extern const struct hfs_hdr_layout hfs_sngl_hdr_layout;
+
+/* inode.c */
+extern void hfs_put_inode(struct inode *);
+extern int hfs_notify_change(struct dentry *, struct iattr *);
+extern struct inode *hfs_iget(struct hfs_cat_entry *, ino_t, struct dentry *);
+
+extern void hfs_cap_ifill(struct inode *, ino_t, const int);
+extern void hfs_dbl_ifill(struct inode *, ino_t, const int);
+extern void hfs_nat_ifill(struct inode *, ino_t, const int);
+extern void hfs_sngl_ifill(struct inode *, ino_t, const int);
+
+/* super.c */
+extern struct super_block *hfs_read_super(struct super_block *,void *,int);
+extern int init_hfs_fs(void);
+
+/* trans.c */
+extern void hfs_colon2mac(struct hfs_name *, const char *, int);
+extern void hfs_prcnt2mac(struct hfs_name *, const char *, int);
+extern void hfs_triv2mac(struct hfs_name *, const char *, int);
+extern void hfs_latin2mac(struct hfs_name *, const char *, int);
+extern int hfs_mac2cap(char *, const struct hfs_name *);
+extern int hfs_mac2nat(char *, const struct hfs_name *);
+extern int hfs_mac2latin(char *, const struct hfs_name *);
+extern int hfs_mac2seven(char *, const struct hfs_name *);
+extern int hfs_mac2eight(char *, const struct hfs_name *);
+extern int hfs_mac2alpha(char *, const struct hfs_name *);
+extern int hfs_mac2triv(char *, const struct hfs_name *);
+extern void hfs_tolower(unsigned char *, int);
+
+#define HFS_I(X) (&((X)->u.hfs_i))
+#define HFS_SB(X) (&((X)->u.hfs_sb))
+
+extern __inline__ void hfs_nameout(struct inode *dir, struct hfs_name *out,
+ const char *in, int len) {
+ HFS_SB(dir->i_sb)->s_nameout(out, in, len);
+}
+
+extern __inline__ int hfs_namein(struct inode *dir, char *out,
+ const struct hfs_name *in) {
+ int len = HFS_SB(dir->i_sb)->s_namein(out, in);
+ if (HFS_SB(dir->i_sb)->s_lowercase) {
+ hfs_tolower(out, len);
+ }
+ return len;
+}
+
+#endif /* __KERNEL__ */
+#endif
diff --git a/pfinet/linux-src/include/linux/hfs_fs_i.h b/pfinet/linux-src/include/linux/hfs_fs_i.h
new file mode 100644
index 00000000..03585a08
--- /dev/null
+++ b/pfinet/linux-src/include/linux/hfs_fs_i.h
@@ -0,0 +1,43 @@
+/*
+ * linux/include/linux/hfs_fs_i.h
+ *
+ * Copyright (C) 1995, 1996 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file defines the type (struct hfs_inode_info) and the two
+ * subordinate types hfs_extent and hfs_file.
+ */
+
+#ifndef _LINUX_HFS_FS_I_H
+#define _LINUX_HFS_FS_I_H
+
+/*
+ * struct hfs_inode_info
+ *
+ * The HFS-specific part of a Linux (struct inode)
+ */
+struct hfs_inode_info {
+ int magic; /* A magic number */
+
+ struct hfs_cat_entry *entry;
+
+ /* For a regular or header file */
+ struct hfs_fork *fork;
+ int convert;
+
+ /* For a directory */
+ ino_t file_type;
+ char dir_size;
+
+ /* For header files */
+ const struct hfs_hdr_layout *default_layout;
+ struct hfs_hdr_layout *layout;
+
+ /* to deal with localtime ugliness */
+ int tz_secondswest;
+
+ /* for dentry cleanup */
+ void (*d_drop_op)(struct dentry *, const ino_t);
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/hfs_fs_sb.h b/pfinet/linux-src/include/linux/hfs_fs_sb.h
new file mode 100644
index 00000000..826f388c
--- /dev/null
+++ b/pfinet/linux-src/include/linux/hfs_fs_sb.h
@@ -0,0 +1,53 @@
+/*
+ * linux/include/linux/hfs_fs_sb.h
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file defines the type (struct hfs_sb_info) which contains the
+ * HFS-specific information in the in-core superblock.
+ */
+
+#ifndef _LINUX_HFS_FS_SB_H
+#define _LINUX_HFS_FS_SB_H
+
+/* forward declaration: */
+struct hfs_name;
+
+typedef int (*hfs_namein_fn) (char *, const struct hfs_name *);
+typedef void (*hfs_nameout_fn) (struct hfs_name *, const char *, int);
+typedef void (*hfs_ifill_fn) (struct inode *, ino_t, const int);
+
+/*
+ * struct hfs_sb_info
+ *
+ * The HFS-specific part of a Linux (struct super_block)
+ */
+struct hfs_sb_info {
+ int magic; /* A magic number */
+ struct hfs_mdb *s_mdb; /* The HFS MDB */
+ int s_quiet; /* Silent failure when
+ changing owner or mode? */
+ int s_lowercase; /* Map names to lowercase? */
+ int s_afpd; /* AFPD compatible mode? */
+ int s_version; /* version info */
+ hfs_namein_fn s_namein; /* The function used to
+ map Mac filenames to
+ Linux filenames */
+ hfs_nameout_fn s_nameout; /* The function used to
+ map Linux filenames
+ to Mac filenames */
+ hfs_ifill_fn s_ifill; /* The function used
+ to fill in inode fields */
+ const struct hfs_name *s_reserved1; /* Reserved names */
+ const struct hfs_name *s_reserved2; /* Reserved names */
+ __u32 s_type; /* Type for new files */
+ __u32 s_creator; /* Creator for new files */
+ umode_t s_umask; /* The umask applied to the
+ permissions on all files */
+ uid_t s_uid; /* The uid of all files */
+ gid_t s_gid; /* The gid of all files */
+ char s_conv; /* Type of text conversion */
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/hfs_sysdep.h b/pfinet/linux-src/include/linux/hfs_sysdep.h
new file mode 100644
index 00000000..00cc6400
--- /dev/null
+++ b/pfinet/linux-src/include/linux/hfs_sysdep.h
@@ -0,0 +1,245 @@
+/*
+ * linux/include/linux/hfs_sysdep.h
+ *
+ * Copyright (C) 1996-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains constants, types and inline
+ * functions for various system dependent things.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#ifndef _HFS_SYSDEP_H
+#define _HFS_SYSDEP_H
+
+#include <linux/malloc.h>
+#include <linux/types.h>
+#include <linux/locks.h>
+#include <linux/fs.h>
+
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+extern struct timezone sys_tz;
+
+#undef offsetof
+#define offsetof(TYPE, MEMB) ((size_t) &((TYPE *)0)->MEMB)
+
+/* Typedefs for integer types by size and signedness */
+typedef __u8 hfs_u8;
+typedef __u16 hfs_u16;
+typedef __u32 hfs_u32;
+typedef __s8 hfs_s8;
+typedef __s16 hfs_s16;
+typedef __s32 hfs_s32;
+
+/* Typedefs for unaligned integer types */
+typedef unsigned char hfs_byte_t;
+typedef unsigned char hfs_word_t[2];
+typedef unsigned char hfs_lword_t[4];
+
+/* these funny looking things are GCC variable argument macros */
+#define hfs_warn(format, args...) printk(KERN_WARNING format , ## args)
+#define hfs_error(format, args...) printk(KERN_ERR format , ## args)
+
+
+#if defined(DEBUG_ALL) || defined(DEBUG_MEM)
+extern long int hfs_alloc;
+#endif
+
+extern inline void *hfs_malloc(unsigned int size) {
+#if defined(DEBUG_ALL) || defined(DEBUG_MEM)
+ hfs_warn("%ld bytes allocation at %s:%u\n",
+ (hfs_alloc += size), __FILE__, __LINE__);
+#endif
+ return kmalloc(size, GFP_KERNEL);
+}
+
+extern inline void hfs_free(void *ptr, unsigned int size) {
+ kfree_s(ptr, size);
+#if defined(DEBUG_ALL) || defined(DEBUG_MEM)
+ hfs_warn("%ld bytes allocation at %s:%u\n",
+ (hfs_alloc -= ptr ? size : 0), __FILE__, __LINE__);
+#endif
+}
+
+
+/* handle conversion between times.
+ *
+ * NOTE: hfs+ doesn't need this. also, we don't use tz_dsttime as that's
+ * not a good thing to do. instead, we depend upon tz_minuteswest
+ * having the correct daylight savings correction.
+ */
+extern inline hfs_u32 hfs_from_utc(hfs_s32 time)
+{
+ return time - sys_tz.tz_minuteswest*60;
+}
+
+extern inline hfs_s32 hfs_to_utc(hfs_u32 time)
+{
+ return time + sys_tz.tz_minuteswest*60;
+}
+
+extern inline hfs_u32 hfs_time(void) {
+ return htonl(hfs_from_utc(CURRENT_TIME)+2082844800U);
+}
+
+
+/*
+ * hfs_wait_queue
+ */
+typedef struct wait_queue *hfs_wait_queue;
+
+extern inline void hfs_init_waitqueue(hfs_wait_queue *queue) {
+ init_waitqueue(queue);
+}
+
+extern inline void hfs_sleep_on(hfs_wait_queue *queue) {
+ sleep_on(queue);
+}
+
+extern inline void hfs_wake_up(hfs_wait_queue *queue) {
+ wake_up(queue);
+}
+
+extern inline void hfs_relinquish(void) {
+ schedule();
+}
+
+
+/*
+ * hfs_sysmdb
+ */
+typedef struct super_block *hfs_sysmdb;
+
+extern inline void hfs_mdb_dirty(hfs_sysmdb sys_mdb) {
+ sys_mdb->s_dirt = 1;
+}
+
+extern inline char *hfs_mdb_name(hfs_sysmdb sys_mdb) {
+ return kdevname(sys_mdb->s_dev);
+}
+
+
+/*
+ * hfs_sysentry
+ */
+typedef struct dentry *hfs_sysentry[4];
+
+/*
+ * hfs_buffer
+ */
+typedef struct buffer_head *hfs_buffer;
+
+#define HFS_BAD_BUFFER NULL
+
+/* In sysdep.c, since it needs HFS_SECTOR_SIZE */
+extern hfs_buffer hfs_buffer_get(hfs_sysmdb, int, int);
+
+extern inline int hfs_buffer_ok(hfs_buffer buffer) {
+ return (buffer != NULL);
+}
+
+extern inline void hfs_buffer_put(hfs_buffer buffer) {
+ brelse(buffer);
+}
+
+extern inline void hfs_buffer_dirty(hfs_buffer buffer) {
+ mark_buffer_dirty(buffer, 1);
+}
+
+extern inline void hfs_buffer_sync(hfs_buffer buffer) {
+ while (buffer_locked(buffer)) {
+ wait_on_buffer(buffer);
+ }
+ if (buffer_dirty(buffer)) {
+ ll_rw_block(WRITE, 1, &buffer);
+ wait_on_buffer(buffer);
+ }
+}
+
+extern inline void *hfs_buffer_data(const hfs_buffer buffer) {
+ return buffer->b_data;
+}
+
+
+/*
+ * bit operations
+ */
+
+#undef BITNR
+#if defined(__BIG_ENDIAN)
+# define BITNR(X) ((X)^31)
+# if !defined(__constant_htonl)
+# define __constant_htonl(x) (x)
+# endif
+# if !defined(__constant_htons)
+# define __constant_htons(x) (x)
+# endif
+#elif defined(__LITTLE_ENDIAN)
+# define BITNR(X) ((X)^7)
+# if !defined(__constant_htonl)
+# define __constant_htonl(x) \
+ ((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \
+ (((unsigned long int)(x) & 0x0000ff00U) << 8) | \
+ (((unsigned long int)(x) & 0x00ff0000U) >> 8) | \
+ (((unsigned long int)(x) & 0xff000000U) >> 24)))
+# endif
+# if !defined(__constant_htons)
+# define __constant_htons(x) \
+ ((unsigned short int)((((unsigned short int)(x) & 0x00ff) << 8) | \
+ (((unsigned short int)(x) & 0xff00) >> 8)))
+# endif
+#else
+# error "Don't know if bytes are big- or little-endian!"
+#endif
+
+extern inline int hfs_clear_bit(int bitnr, hfs_u32 *lword) {
+ return test_and_clear_bit(BITNR(bitnr), lword);
+}
+
+extern inline int hfs_set_bit(int bitnr, hfs_u32 *lword) {
+ return test_and_set_bit(BITNR(bitnr), lword);
+}
+
+extern inline int hfs_test_bit(int bitnr, const hfs_u32 *lword) {
+ /* the kernel should declare the second arg of test_bit as const */
+ return test_bit(BITNR(bitnr), (void *)lword);
+}
+
+#undef BITNR
+
+/*
+ * HFS structures have fields aligned to 16-bit boundaries.
+ * So, 16-bit get/put are easy while 32-bit get/put need
+ * some care on architectures like the DEC Alpha.
+ *
+ * In what follows:
+ * ns = 16-bit integer in network byte-order w/ 16-bit alignment
+ * hs = 16-bit integer in host byte-order w/ 16-bit alignment
+ * nl = 32-bit integer in network byte-order w/ unknown alignment
+ * hl = 32-bit integer in host byte-order w/ unknown alignment
+ * anl = 32-bit integer in network byte-order w/ 32-bit alignment
+ * ahl = 32-bit integer in host byte-order w/ 32-bit alignment
+ * Example: hfs_get_hl() gets an unaligned 32-bit integer converting
+ * it to host byte-order.
+ */
+#define hfs_get_hs(addr) ntohs(*((hfs_u16 *)(addr)))
+#define hfs_get_ns(addr) (*((hfs_u16 *)(addr)))
+#define hfs_get_hl(addr) ntohl(get_unaligned((hfs_u32 *)(addr)))
+#define hfs_get_nl(addr) get_unaligned((hfs_u32 *)(addr))
+#define hfs_get_ahl(addr) ntohl(*((hfs_u32 *)(addr)))
+#define hfs_get_anl(addr) (*((hfs_u32 *)(addr)))
+#define hfs_put_hs(val, addr) ((void)(*((hfs_u16 *)(addr)) = ntohs(val)))
+#define hfs_put_ns(val, addr) ((void)(*((hfs_u16 *)(addr)) = (val)))
+#define hfs_put_hl(val, addr) put_unaligned(htonl(val), (hfs_u32 *)(addr))
+#define hfs_put_nl(val, addr) put_unaligned((val), (hfs_u32 *)(addr))
+#define hfs_put_ahl(val, addr) ((void)(*((hfs_u32 *)(addr)) = ntohl(val)))
+#define hfs_put_anl(val, addr) ((void)(*((hfs_u32 *)(addr)) = (val)))
+
+#endif
diff --git a/pfinet/linux-src/include/linux/hippidevice.h b/pfinet/linux-src/include/linux/hippidevice.h
new file mode 100644
index 00000000..e345a9d2
--- /dev/null
+++ b/pfinet/linux-src/include/linux/hippidevice.h
@@ -0,0 +1,58 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the HIPPI handlers.
+ *
+ * Version: @(#)hippidevice.h 1.0.0 05/26/97
+ *
+ * Author: Jes Sorensen, <Jes.Sorensen@cern.ch>
+ *
+ * hippidevice.h is based on previous fddidevice.h work by
+ * Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ * Lawrence V. Stefani, <stefani@lkg.dec.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_HIPPIDEVICE_H
+#define _LINUX_HIPPIDEVICE_H
+
+#include <linux/if_hippi.h>
+
+#ifdef __KERNEL__
+extern int hippi_header(struct sk_buff *skb,
+ struct device *dev,
+ unsigned short type,
+ void *daddr,
+ void *saddr,
+ unsigned len);
+
+extern int hippi_rebuild_header(struct sk_buff *skb);
+
+extern unsigned short hippi_type_trans(struct sk_buff *skb,
+ struct device *dev);
+
+extern void hippi_header_cache_bind(struct hh_cache ** hhp,
+ struct device *dev,
+ unsigned short htype,
+ __u32 daddr);
+
+extern void hippi_header_cache_update(struct hh_cache *hh,
+ struct device *dev,
+ unsigned char * haddr);
+extern int hippi_header_parse(struct sk_buff *skb, unsigned char *haddr);
+
+extern void hippi_net_init(void);
+void hippi_setup(struct device *dev);
+
+extern struct device *init_hippi_dev(struct device *, int);
+extern void unregister_hipdev(struct device *dev);
+#endif
+
+#endif /* _LINUX_HIPPIDEVICE_H */
diff --git a/pfinet/linux-src/include/linux/hpfs_fs.h b/pfinet/linux-src/include/linux/hpfs_fs.h
new file mode 100644
index 00000000..2b7926ab
--- /dev/null
+++ b/pfinet/linux-src/include/linux/hpfs_fs.h
@@ -0,0 +1,13 @@
+#ifndef _LINUX_HPFS_FS_H
+#define _LINUX_HPFS_FS_H
+
+/* HPFS magic number (word 0 of block 16) */
+
+#define HPFS_SUPER_MAGIC 0xf995e849
+
+/* The entry point for a VFS */
+
+extern struct super_block *hpfs_read_super (struct super_block *, void *, int);
+extern int init_hpfs_fs(void);
+
+#endif
diff --git a/pfinet/linux-src/include/linux/hpfs_fs_i.h b/pfinet/linux-src/include/linux/hpfs_fs_i.h
new file mode 100644
index 00000000..d9aa8f34
--- /dev/null
+++ b/pfinet/linux-src/include/linux/hpfs_fs_i.h
@@ -0,0 +1,24 @@
+#ifndef _HPFS_FS_I
+#define _HPFS_FS_I
+
+struct hpfs_inode_info {
+ ino_t i_parent_dir; /* (directories) gives fnode of parent dir */
+ unsigned i_dno; /* (directories) root dnode */
+ unsigned i_dpos; /* (directories) temp for readdir */
+ unsigned i_dsubdno; /* (directories) temp for readdir */
+ unsigned i_file_sec; /* (files) minimalist cache of alloc info */
+ unsigned i_disk_sec; /* (files) minimalist cache of alloc info */
+ unsigned i_n_secs; /* (files) minimalist cache of alloc info */
+ unsigned i_conv : 2; /* (files) crlf->newline hackery */
+};
+
+#define i_hpfs_dno u.hpfs_i.i_dno
+#define i_hpfs_parent_dir u.hpfs_i.i_parent_dir
+#define i_hpfs_n_secs u.hpfs_i.i_n_secs
+#define i_hpfs_file_sec u.hpfs_i.i_file_sec
+#define i_hpfs_disk_sec u.hpfs_i.i_disk_sec
+#define i_hpfs_dpos u.hpfs_i.i_dpos
+#define i_hpfs_dsubdno u.hpfs_i.i_dsubdno
+#define i_hpfs_conv u.hpfs_i.i_conv
+
+#endif
diff --git a/pfinet/linux-src/include/linux/hpfs_fs_sb.h b/pfinet/linux-src/include/linux/hpfs_fs_sb.h
new file mode 100644
index 00000000..a383e16b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/hpfs_fs_sb.h
@@ -0,0 +1,32 @@
+#ifndef _HPFS_FS_SB
+#define _HPFS_FS_SB
+
+struct hpfs_sb_info {
+ ino_t sb_root; /* inode number of root dir */
+ unsigned sb_fs_size; /* file system size, sectors */
+ unsigned sb_bitmaps; /* sector number of bitmap list */
+ unsigned sb_dirband_size; /* directory band size, dnodes */
+ unsigned sb_dmap; /* sector number of dnode bit map */
+ unsigned sb_n_free; /* free blocks for statfs, or -1 */
+ unsigned sb_n_free_dnodes; /* free dnodes for statfs, or -1 */
+ uid_t sb_uid; /* uid from mount options */
+ gid_t sb_gid; /* gid from mount options */
+ umode_t sb_mode; /* mode from mount options */
+ unsigned sb_lowercase : 1; /* downcase filenames hackery */
+ unsigned sb_conv : 2; /* crlf->newline hackery */
+};
+
+#define s_hpfs_root u.hpfs_sb.sb_root
+#define s_hpfs_fs_size u.hpfs_sb.sb_fs_size
+#define s_hpfs_bitmaps u.hpfs_sb.sb_bitmaps
+#define s_hpfs_dirband_size u.hpfs_sb.sb_dirband_size
+#define s_hpfs_dmap u.hpfs_sb.sb_dmap
+#define s_hpfs_uid u.hpfs_sb.sb_uid
+#define s_hpfs_gid u.hpfs_sb.sb_gid
+#define s_hpfs_mode u.hpfs_sb.sb_mode
+#define s_hpfs_n_free u.hpfs_sb.sb_n_free
+#define s_hpfs_n_free_dnodes u.hpfs_sb.sb_n_free_dnodes
+#define s_hpfs_lowercase u.hpfs_sb.sb_lowercase
+#define s_hpfs_conv u.hpfs_sb.sb_conv
+
+#endif
diff --git a/pfinet/linux-src/include/linux/i2c.h b/pfinet/linux-src/include/linux/i2c.h
new file mode 100644
index 00000000..8b355d9e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/i2c.h
@@ -0,0 +1,190 @@
+#ifndef I2C_H
+#define I2C_H
+
+/*
+ * linux i2c interface. Works a little bit like the scsi subsystem.
+ * There are:
+ *
+ * i2c the basic control module (like scsi_mod)
+ * bus driver a driver with a i2c bus (hostadapter driver)
+ * chip driver a driver for a chip connected
+ * to a i2c bus (cdrom/hd driver)
+ *
+ * A device will be attached to one bus and one chip driver. Every chip
+ * driver gets a unique ID.
+ *
+ * A chip driver can provide a ioctl-like callback for the
+ * communication with other parts of the kernel (not every i2c chip is
+ * useful without other devices, a TV card tuner for example).
+ *
+ * "i2c internal" parts of the structs: only the i2c module is allowed to
+ * write to them, for others they are read-only.
+ *
+ */
+
+#define I2C_BUS_MAX 4 /* max # of bus drivers */
+#define I2C_DRIVER_MAX 8 /* max # of chip drivers */
+#define I2C_DEVICE_MAX 8 /* max # if devices per bus/driver */
+
+struct i2c_bus;
+struct i2c_driver;
+struct i2c_device;
+
+#define I2C_DRIVERID_MSP3400 1
+#define I2C_DRIVERID_TUNER 2
+#define I2C_DRIVERID_VIDEOTEXT 3
+#define I2C_DRIVERID_VIDEODECODER 4
+#define I2C_DRIVERID_VIDEOENCODER 5
+
+#define I2C_BUSID_BT848 1 /* I2C bus on a BT848 */
+ /* 2 is used in 2.3.x */
+#define I2C_BUSID_BUZ 3 /* I2C bus on a BUZ */
+#define I2C_BUSID_ZORAN 4 /* I2C bus on a Zoran */
+#define I2C_BUSID_SGIVWFB 5 /* Moved to be unique */
+
+/*
+ * struct for a driver for a i2c chip (tuner, soundprocessor,
+ * videotext, ... ).
+ *
+ * a driver will register within the i2c module. The i2c module will
+ * callback the driver (i2c_attach) for every device it finds on a i2c
+ * bus at the specified address. If the driver decides to "accept"
+ * the, device, it must return a struct i2c_device, and NULL
+ * otherwise.
+ *
+ * i2c_detach = i2c_attach ** -1
+ *
+ * i2c_command will be used to pass commands to the driver in a
+ * ioctl-line manner.
+ *
+ */
+
+struct i2c_driver
+{
+ char name[32]; /* some useful label */
+ int id; /* device type ID */
+ unsigned char addr_l, addr_h; /* address range of the chip */
+
+ int (*attach)(struct i2c_device *device);
+ int (*detach)(struct i2c_device *device);
+ int (*command)(struct i2c_device *device,unsigned int cmd, void *arg);
+
+ /* i2c internal */
+ struct i2c_device *devices[I2C_DEVICE_MAX];
+ int devcount;
+};
+
+
+/*
+ * this holds the informations about a i2c bus available in the system.
+ *
+ * a chip with a i2c bus interface (like bt848) registers the bus within
+ * the i2c module. This struct provides functions to access the i2c bus.
+ *
+ * One must hold the spinlock to access the i2c bus (XXX: is the irqsave
+ * required? Maybe better use a semaphore?).
+ * [-AC-] having a spinlock_irqsave is only needed if we have drivers wishing
+ * to bang their i2c bus from an interrupt.
+ *
+ * attach/detach_inform is a callback to inform the bus driver about
+ * attached chip drivers.
+ *
+ */
+
+/* needed: unsigned long flags */
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x020100
+# if 0
+# define LOCK_FLAGS unsigned long flags;
+# define LOCK_I2C_BUS(bus) spin_lock_irqsave(&(bus->bus_lock),flags);
+# define UNLOCK_I2C_BUS(bus) spin_unlock_irqrestore(&(bus->bus_lock),flags);
+# else
+# define LOCK_FLAGS
+# define LOCK_I2C_BUS(bus) spin_lock(&(bus->bus_lock));
+# define UNLOCK_I2C_BUS(bus) spin_unlock(&(bus->bus_lock));
+# endif
+#else
+# define LOCK_FLAGS unsigned long flags;
+# define LOCK_I2C_BUS(bus) { save_flags(flags); cli(); }
+# define UNLOCK_I2C_BUS(bus) { restore_flags(flags); }
+#endif
+
+struct i2c_bus
+{
+ char name[32]; /* some useful label */
+ int id;
+ void *data; /* free for use by the bus driver */
+
+#if LINUX_VERSION_CODE >= 0x020100
+ spinlock_t bus_lock;
+#endif
+
+ /* attach/detach inform callbacks */
+ void (*attach_inform)(struct i2c_bus *bus, int id);
+ void (*detach_inform)(struct i2c_bus *bus, int id);
+
+ /* Software I2C */
+ void (*i2c_setlines)(struct i2c_bus *bus, int ctrl, int data);
+ int (*i2c_getdataline)(struct i2c_bus *bus);
+
+ /* Hardware I2C */
+ int (*i2c_read)(struct i2c_bus *bus, unsigned char addr);
+ int (*i2c_write)(struct i2c_bus *bus, unsigned char addr,
+ unsigned char b1, unsigned char b2, int both);
+
+ /* internal data for i2c module */
+ struct i2c_device *devices[I2C_DEVICE_MAX];
+ int devcount;
+};
+
+
+/*
+ * This holds per-device data for a i2c device
+ */
+
+struct i2c_device
+{
+ char name[32]; /* some useful label */
+ void *data; /* free for use by the chip driver */
+ unsigned char addr; /* chip addr */
+
+ /* i2c internal */
+ struct i2c_bus *bus;
+ struct i2c_driver *driver;
+};
+
+
+/* ------------------------------------------------------------------- */
+/* i2c module functions */
+
+/* register/unregister a i2c bus */
+int i2c_register_bus(struct i2c_bus *bus);
+int i2c_unregister_bus(struct i2c_bus *bus);
+
+/* register/unregister a chip driver */
+int i2c_register_driver(struct i2c_driver *driver);
+int i2c_unregister_driver(struct i2c_driver *driver);
+
+/* send a command to a chip using the ioctl-like callback interface */
+int i2c_control_device(struct i2c_bus *bus, int id,
+ unsigned int cmd, void *arg);
+
+/* i2c bus access functions */
+void i2c_start(struct i2c_bus *bus);
+void i2c_stop(struct i2c_bus *bus);
+void i2c_one(struct i2c_bus *bus);
+void i2c_zero(struct i2c_bus *bus);
+int i2c_ack(struct i2c_bus *bus);
+
+int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack);
+unsigned char i2c_readbyte(struct i2c_bus *bus,int last);
+
+/* i2c (maybe) hardware functions */
+int i2c_read(struct i2c_bus *bus, unsigned char addr);
+int i2c_write(struct i2c_bus *bus, unsigned char addr,
+ unsigned char b1, unsigned char b2, int both);
+
+int i2c_init(void);
+#endif /* I2C_H */
diff --git a/pfinet/linux-src/include/linux/icmp.h b/pfinet/linux-src/include/linux/icmp.h
new file mode 100644
index 00000000..fb4ed8b9
--- /dev/null
+++ b/pfinet/linux-src/include/linux/icmp.h
@@ -0,0 +1,102 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the ICMP protocol.
+ *
+ * Version: @(#)icmp.h 1.0.3 04/28/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_ICMP_H
+#define _LINUX_ICMP_H
+
+#define ICMP_ECHOREPLY 0 /* Echo Reply */
+#define ICMP_DEST_UNREACH 3 /* Destination Unreachable */
+#define ICMP_SOURCE_QUENCH 4 /* Source Quench */
+#define ICMP_REDIRECT 5 /* Redirect (change route) */
+#define ICMP_ECHO 8 /* Echo Request */
+#define ICMP_TIME_EXCEEDED 11 /* Time Exceeded */
+#define ICMP_PARAMETERPROB 12 /* Parameter Problem */
+#define ICMP_TIMESTAMP 13 /* Timestamp Request */
+#define ICMP_TIMESTAMPREPLY 14 /* Timestamp Reply */
+#define ICMP_INFO_REQUEST 15 /* Information Request */
+#define ICMP_INFO_REPLY 16 /* Information Reply */
+#define ICMP_ADDRESS 17 /* Address Mask Request */
+#define ICMP_ADDRESSREPLY 18 /* Address Mask Reply */
+#define NR_ICMP_TYPES 18
+
+
+/* Codes for UNREACH. */
+#define ICMP_NET_UNREACH 0 /* Network Unreachable */
+#define ICMP_HOST_UNREACH 1 /* Host Unreachable */
+#define ICMP_PROT_UNREACH 2 /* Protocol Unreachable */
+#define ICMP_PORT_UNREACH 3 /* Port Unreachable */
+#define ICMP_FRAG_NEEDED 4 /* Fragmentation Needed/DF set */
+#define ICMP_SR_FAILED 5 /* Source Route failed */
+#define ICMP_NET_UNKNOWN 6
+#define ICMP_HOST_UNKNOWN 7
+#define ICMP_HOST_ISOLATED 8
+#define ICMP_NET_ANO 9
+#define ICMP_HOST_ANO 10
+#define ICMP_NET_UNR_TOS 11
+#define ICMP_HOST_UNR_TOS 12
+#define ICMP_PKT_FILTERED 13 /* Packet filtered */
+#define ICMP_PREC_VIOLATION 14 /* Precedence violation */
+#define ICMP_PREC_CUTOFF 15 /* Precedence cut off */
+#define NR_ICMP_UNREACH 15 /* instead of hardcoding immediate value */
+
+/* Codes for REDIRECT. */
+#define ICMP_REDIR_NET 0 /* Redirect Net */
+#define ICMP_REDIR_HOST 1 /* Redirect Host */
+#define ICMP_REDIR_NETTOS 2 /* Redirect Net for TOS */
+#define ICMP_REDIR_HOSTTOS 3 /* Redirect Host for TOS */
+
+/* Codes for TIME_EXCEEDED. */
+#define ICMP_EXC_TTL 0 /* TTL count exceeded */
+#define ICMP_EXC_FRAGTIME 1 /* Fragment Reass time exceeded */
+
+
+struct icmphdr {
+ __u8 type;
+ __u8 code;
+ __u16 checksum;
+ union {
+ struct {
+ __u16 id;
+ __u16 sequence;
+ } echo;
+ __u32 gateway;
+ struct {
+ __u16 __unused;
+ __u16 mtu;
+ } frag;
+ } un;
+};
+
+#ifdef __KERNEL__
+
+struct icmp_err {
+ int errno;
+ unsigned fatal:1;
+};
+#endif
+
+/*
+ * constants for (set|get)sockopt
+ */
+
+#define ICMP_FILTER 1
+
+struct icmp_filter {
+ __u32 data;
+};
+
+
+#endif /* _LINUX_ICMP_H */
diff --git a/pfinet/linux-src/include/linux/icmpv6.h b/pfinet/linux-src/include/linux/icmpv6.h
new file mode 100644
index 00000000..fcd6fce2
--- /dev/null
+++ b/pfinet/linux-src/include/linux/icmpv6.h
@@ -0,0 +1,149 @@
+#ifndef _LINUX_ICMPV6_H
+#define _LINUX_ICMPV6_H
+
+#include <asm/byteorder.h>
+
+struct icmp6hdr {
+
+ __u8 icmp6_type;
+ __u8 icmp6_code;
+ __u16 icmp6_cksum;
+
+
+ union {
+ __u32 un_data32[1];
+ __u16 un_data16[2];
+ __u8 un_data8[4];
+
+ struct icmpv6_echo {
+ __u16 identifier;
+ __u16 sequence;
+ } u_echo;
+
+ struct icmpv6_nd_advt {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u32 reserved:5,
+ override:1,
+ solicited:1,
+ router:1,
+ reserved2:24;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u32 router:1,
+ solicited:1,
+ override:1,
+ reserved:29;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ } u_nd_advt;
+
+ struct icmpv6_nd_ra {
+ __u8 hop_limit;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 reserved:6,
+ other:1,
+ managed:1;
+
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u8 managed:1,
+ other:1,
+ reserved:6;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ __u16 rt_lifetime;
+ } u_nd_ra;
+
+ } icmp6_dataun;
+
+#define icmp6_identifier icmp6_dataun.u_echo.identifier
+#define icmp6_sequence icmp6_dataun.u_echo.sequence
+#define icmp6_pointer icmp6_dataun.un_data32[0]
+#define icmp6_mtu icmp6_dataun.un_data32[0]
+#define icmp6_unused icmp6_dataun.un_data32[0]
+#define icmp6_maxdelay icmp6_dataun.un_data16[0]
+#define icmp6_router icmp6_dataun.u_nd_advt.router
+#define icmp6_solicited icmp6_dataun.u_nd_advt.solicited
+#define icmp6_override icmp6_dataun.u_nd_advt.override
+#define icmp6_ndiscreserved icmp6_dataun.u_nd_advt.reserved
+#define icmp6_hop_limit icmp6_dataun.u_nd_ra.hop_limit
+#define icmp6_addrconf_managed icmp6_dataun.u_nd_ra.managed
+#define icmp6_addrconf_other icmp6_dataun.u_nd_ra.other
+#define icmp6_rt_lifetime icmp6_dataun.u_nd_ra.rt_lifetime
+};
+
+
+#define ICMPV6_DEST_UNREACH 1
+#define ICMPV6_PKT_TOOBIG 2
+#define ICMPV6_TIME_EXCEED 3
+#define ICMPV6_PARAMPROB 4
+
+#define ICMPV6_INFOMSG_MASK 0x80
+
+#define ICMPV6_ECHO_REQUEST 128
+#define ICMPV6_ECHO_REPLY 129
+#define ICMPV6_MGM_QUERY 130
+#define ICMPV6_MGM_REPORT 131
+#define ICMPV6_MGM_REDUCTION 132
+
+/*
+ * Codes for Destination Unreachable
+ */
+#define ICMPV6_NOROUTE 0
+#define ICMPV6_ADM_PROHIBITED 1
+#define ICMPV6_NOT_NEIGHBOUR 2
+#define ICMPV6_ADDR_UNREACH 3
+#define ICMPV6_PORT_UNREACH 4
+
+/*
+ * Codes for Time Exceeded
+ */
+#define ICMPV6_EXC_HOPLIMIT 0
+#define ICMPV6_EXC_FRAGTIME 1
+
+/*
+ * Codes for Parameter Problem
+ */
+#define ICMPV6_HDR_FIELD 0
+#define ICMPV6_UNK_NEXTHDR 1
+#define ICMPV6_UNK_OPTION 2
+
+/*
+ * constants for (set|get)sockopt
+ */
+
+#define ICMPV6_FILTER 1
+
+/*
+ * ICMPV6 filter
+ */
+
+#define ICMPV6_FILTER_BLOCK 1
+#define ICMPV6_FILTER_PASS 2
+#define ICMPV6_FILTER_BLOCKOTHERS 3
+#define ICMPV6_FILTER_PASSONLY 4
+
+struct icmp6_filter {
+ __u32 data[8];
+};
+
+#ifdef __KERNEL__
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+
+
+extern void icmpv6_send(struct sk_buff *skb,
+ int type, int code,
+ __u32 info,
+ struct device *dev);
+
+extern int icmpv6_init(struct net_proto_family *ops);
+extern int icmpv6_err_convert(int type, int code,
+ int *err);
+extern void icmpv6_cleanup(void);
+extern void icmpv6_param_prob(struct sk_buff *skb,
+ int code, void *pos);
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/if.h b/pfinet/linux-src/include/linux/if.h
new file mode 100644
index 00000000..c95f372c
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if.h
@@ -0,0 +1,138 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the INET interface module.
+ *
+ * Version: @(#)if.h 1.0.2 04/18/93
+ *
+ * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1982-1988
+ * Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IF_H
+#define _LINUX_IF_H
+
+#include <linux/types.h> /* for "__kernel_caddr_t" et al */
+#include <linux/socket.h> /* for "struct sockaddr" et al */
+
+/* Standard interface flags. */
+#define IFF_UP 0x1 /* interface is up */
+#define IFF_BROADCAST 0x2 /* broadcast address valid */
+#define IFF_DEBUG 0x4 /* turn on debugging */
+#define IFF_LOOPBACK 0x8 /* is a loopback net */
+#define IFF_POINTOPOINT 0x10 /* interface is has p-p link */
+#define IFF_NOTRAILERS 0x20 /* avoid use of trailers */
+#define IFF_RUNNING 0x40 /* resources allocated */
+#define IFF_NOARP 0x80 /* no ARP protocol */
+#define IFF_PROMISC 0x100 /* receive all packets */
+#define IFF_ALLMULTI 0x200 /* receive all multicast packets*/
+
+#define IFF_MASTER 0x400 /* master of a load balancer */
+#define IFF_SLAVE 0x800 /* slave of a load balancer */
+
+#define IFF_MULTICAST 0x1000 /* Supports multicast */
+
+#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ALLMULTI)
+
+#define IFF_PORTSEL 0x2000 /* can set media type */
+#define IFF_AUTOMEDIA 0x4000 /* auto media select active */
+#define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses*/
+
+/*
+ * Device mapping structure. I'd just gone off and designed a
+ * beautiful scheme using only loadable modules with arguments
+ * for driver options and along come the PCMCIA people 8)
+ *
+ * Ah well. The get() side of this is good for WDSETUP, and it'll
+ * be handy for debugging things. The set side is fine for now and
+ * being very small might be worth keeping for clean configuration.
+ */
+
+struct ifmap
+{
+ unsigned long mem_start;
+ unsigned long mem_end;
+ unsigned short base_addr;
+ unsigned char irq;
+ unsigned char dma;
+ unsigned char port;
+ /* 3 bytes spare */
+};
+
+/*
+ * Interface request structure used for socket
+ * ioctl's. All interface ioctl's must have parameter
+ * definitions which begin with ifr_name. The
+ * remainder may be interface specific.
+ */
+
+struct ifreq
+{
+#define IFHWADDRLEN 6
+#define IFNAMSIZ 16
+ union
+ {
+ char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ } ifr_ifrn;
+
+ union {
+ struct sockaddr ifru_addr;
+ struct sockaddr ifru_dstaddr;
+ struct sockaddr ifru_broadaddr;
+ struct sockaddr ifru_netmask;
+ struct sockaddr ifru_hwaddr;
+ short ifru_flags;
+ int ifru_ivalue;
+ int ifru_mtu;
+ struct ifmap ifru_map;
+ char ifru_slave[IFNAMSIZ]; /* Just fits the size */
+ char ifru_newname[IFNAMSIZ];
+ char * ifru_data;
+ } ifr_ifru;
+};
+
+#define ifr_name ifr_ifrn.ifrn_name /* interface name */
+#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */
+#define ifr_addr ifr_ifru.ifru_addr /* address */
+#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */
+#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
+#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */
+#define ifr_flags ifr_ifru.ifru_flags /* flags */
+#define ifr_metric ifr_ifru.ifru_ivalue /* metric */
+#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */
+#define ifr_map ifr_ifru.ifru_map /* device map */
+#define ifr_slave ifr_ifru.ifru_slave /* slave device */
+#define ifr_data ifr_ifru.ifru_data /* for use by interface */
+#define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */
+#define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */
+#define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */
+#define ifr_newname ifr_ifru.ifru_newname /* New name */
+
+/*
+ * Structure used in SIOCGIFCONF request.
+ * Used to retrieve interface configuration
+ * for machine (useful for programs which
+ * must know all networks accessible).
+ */
+
+struct ifconf
+{
+ int ifc_len; /* size of buffer */
+ union
+ {
+ char * ifcu_buf;
+ struct ifreq *ifcu_req;
+ } ifc_ifcu;
+};
+#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */
+#define ifc_req ifc_ifcu.ifcu_req /* array of structures */
+
+
+#endif /* _LINUX_IF_H */
diff --git a/pfinet/linux-src/include/linux/if_arcnet.h b/pfinet/linux-src/include/linux/if_arcnet.h
new file mode 100644
index 00000000..8b2ba0aa
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_arcnet.h
@@ -0,0 +1,63 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the ARCnet interface.
+ *
+ * Version: $Id: if_arcnet.h,v 1.2 1997/09/05 08:57:54 mj Exp $
+ *
+ * Author: David Woodhouse <dwmw2@cam.ac.uk>
+ * Avery Pennarun <apenwarr@bond.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_IF_ARCNET_H
+#define _LINUX_IF_ARCNET_H
+
+
+/*
+ * These are the defined ARCnet Protocol ID's.
+ */
+
+ /* RFC1201 Protocol ID's */
+#define ARC_P_IP 212 /* 0xD4 */
+#define ARC_P_ARP 213 /* 0xD5 */
+#define ARC_P_RARP 214 /* 0xD6 */
+#define ARC_P_IPX 250 /* 0xFA */
+#define ARC_P_NOVELL_EC 236 /* 0xEC */
+
+ /* Old RFC1051 Protocol ID's */
+#define ARC_P_IP_RFC1051 240 /* 0xF0 */
+#define ARC_P_ARP_RFC1051 241 /* 0xF1 */
+
+ /* MS LanMan/WfWg protocol */
+#define ARC_P_ETHER 0xE8
+
+ /* Unsupported/indirectly supported protocols */
+#define ARC_P_DATAPOINT_BOOT 0 /* very old Datapoint equipment */
+#define ARC_P_DATAPOINT_MOUNT 1
+#define ARC_P_POWERLAN_BEACON 8 /* Probably ATA-Netbios related */
+#define ARC_P_POWERLAN_BEACON2 243
+#define ARC_P_LANSOFT 251 /* 0xFB - what is this? */
+#define ARC_P_ATALK 0xDD
+
+
+/*
+ * This is an ARCnet frame header.
+ */
+
+struct archdr /* was struct HardHeader */
+{
+ u_char source, /* source ARCnet - filled in automagically */
+ destination, /* destination ARCnet - 0 for broadcast */
+ offset1, /* offset of ClientData (256-byte packets) */
+ offset2; /* offset of ClientData (512-byte packets) */
+
+};
+
+#endif /* _LINUX_IF_ARCNET_H */
diff --git a/pfinet/linux-src/include/linux/if_arp.h b/pfinet/linux-src/include/linux/if_arp.h
new file mode 100644
index 00000000..39007c8b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_arp.h
@@ -0,0 +1,133 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the ARP (RFC 826) protocol.
+ *
+ * Version: @(#)if_arp.h 1.0.1 04/16/93
+ *
+ * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1986-1988
+ * Portions taken from the KA9Q/NOS (v2.00m PA0GRI) source.
+ * Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Florian La Roche,
+ * Jonathan Layes <layes@loran.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IF_ARP_H
+#define _LINUX_IF_ARP_H
+
+#include <linux/netdevice.h>
+
+/* ARP protocol HARDWARE identifiers. */
+#define ARPHRD_NETROM 0 /* from KA9Q: NET/ROM pseudo */
+#define ARPHRD_ETHER 1 /* Ethernet 10Mbps */
+#define ARPHRD_EETHER 2 /* Experimental Ethernet */
+#define ARPHRD_AX25 3 /* AX.25 Level 2 */
+#define ARPHRD_PRONET 4 /* PROnet token ring */
+#define ARPHRD_CHAOS 5 /* Chaosnet */
+#define ARPHRD_IEEE802 6 /* IEEE 802.2 Ethernet/TR/TB */
+#define ARPHRD_ARCNET 7 /* ARCnet */
+#define ARPHRD_APPLETLK 8 /* APPLEtalk */
+#define ARPHRD_DLCI 15 /* Frame Relay DLCI */
+#define ARPHRD_METRICOM 23 /* Metricom STRIP (new IANA id) */
+
+/* Dummy types for non ARP hardware */
+#define ARPHRD_SLIP 256
+#define ARPHRD_CSLIP 257
+#define ARPHRD_SLIP6 258
+#define ARPHRD_CSLIP6 259
+#define ARPHRD_RSRVD 260 /* Notional KISS type */
+#define ARPHRD_ADAPT 264
+#define ARPHRD_ROSE 270
+#define ARPHRD_X25 271 /* CCITT X.25 */
+#define ARPHRD_PPP 512
+#define ARPHRD_HDLC 513 /* (Cisco) HDLC */
+#define ARPHRD_LAPB 516 /* LAPB */
+
+#define ARPHRD_TUNNEL 768 /* IPIP tunnel */
+#define ARPHRD_TUNNEL6 769 /* IPIP6 tunnel */
+#define ARPHRD_FRAD 770 /* Frame Relay Access Device */
+#define ARPHRD_SKIP 771 /* SKIP vif */
+#define ARPHRD_LOOPBACK 772 /* Loopback device */
+#define ARPHRD_LOCALTLK 773 /* Localtalk device */
+#define ARPHRD_FDDI 774 /* Fiber Distributed Data Interface */
+#define ARPHRD_BIF 775 /* AP1000 BIF */
+#define ARPHRD_SIT 776 /* sit0 device - IPv6-in-IPv4 */
+#define ARPHRD_IPDDP 777 /* IP over DDP tunneller */
+#define ARPHRD_IPGRE 778 /* GRE over IP */
+#define ARPHRD_PIMREG 779 /* PIMSM register interface */
+#define ARPHRD_HIPPI 780 /* High Performance Parallel Interface */
+#define ARPHRD_ASH 781 /* Nexus 64Mbps Ash */
+#define ARPHRD_ECONET 782 /* Acorn Econet */
+#define ARPHRD_IRDA 783 /* Linux/IR */
+/* ARP works differently on different FC media .. so */
+#define ARPHRD_FCPP 784 /* Point to point fibrechanel */
+#define ARPHRD_FCAL 785 /* Fibrechannel arbitrated loop */
+#define ARPHRD_FCPL 786 /* Fibrechannel public loop */
+#define ARPHRD_FCFABRIC 787 /* Fibrechannel fabric */
+ /* 787->799 reserved for fibrechannel media types */
+
+
+/* ARP protocol opcodes. */
+#define ARPOP_REQUEST 1 /* ARP request */
+#define ARPOP_REPLY 2 /* ARP reply */
+#define ARPOP_RREQUEST 3 /* RARP request */
+#define ARPOP_RREPLY 4 /* RARP reply */
+
+
+/* ARP ioctl request. */
+struct arpreq {
+ struct sockaddr arp_pa; /* protocol address */
+ struct sockaddr arp_ha; /* hardware address */
+ int arp_flags; /* flags */
+ struct sockaddr arp_netmask; /* netmask (only for proxy arps) */
+ char arp_dev[16];
+};
+
+struct arpreq_old {
+ struct sockaddr arp_pa; /* protocol address */
+ struct sockaddr arp_ha; /* hardware address */
+ int arp_flags; /* flags */
+ struct sockaddr arp_netmask; /* netmask (only for proxy arps) */
+};
+
+/* ARP Flag values. */
+#define ATF_COM 0x02 /* completed entry (ha valid) */
+#define ATF_PERM 0x04 /* permanent entry */
+#define ATF_PUBL 0x08 /* publish entry */
+#define ATF_USETRAILERS 0x10 /* has requested trailers */
+#define ATF_NETMASK 0x20 /* want to use a netmask (only
+ for proxy entries) */
+#define ATF_DONTPUB 0x40 /* don't answer this addresses */
+
+/*
+ * This structure defines an ethernet arp header.
+ */
+
+struct arphdr
+{
+ unsigned short ar_hrd; /* format of hardware address */
+ unsigned short ar_pro; /* format of protocol address */
+ unsigned char ar_hln; /* length of hardware address */
+ unsigned char ar_pln; /* length of protocol address */
+ unsigned short ar_op; /* ARP opcode (command) */
+
+#if 0
+ /*
+ * Ethernet looks like this : This bit is variable sized however...
+ */
+ unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
+ unsigned char ar_sip[4]; /* sender IP address */
+ unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
+ unsigned char ar_tip[4]; /* target IP address */
+#endif
+
+};
+
+#endif /* _LINUX_IF_ARP_H */
diff --git a/pfinet/linux-src/include/linux/if_cablemodem.h b/pfinet/linux-src/include/linux/if_cablemodem.h
new file mode 100644
index 00000000..9ca1007e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_cablemodem.h
@@ -0,0 +1,22 @@
+#ifndef _LINUX_CABLEMODEM_H_
+#define _LINUX_CABLEMODEM_H_
+/*
+ * Author: Franco Venturi <fventuri@mediaone.net>
+ * Copyright 1998 Franco Venturi
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+/* some useful defines for sb1000.c e cmconfig.c - fv */
+#define SIOCGCMSTATS SIOCDEVPRIVATE+0 /* get cable modem stats */
+#define SIOCGCMFIRMWARE SIOCDEVPRIVATE+1 /* get cm firmware version */
+#define SIOCGCMFREQUENCY SIOCDEVPRIVATE+2 /* get cable modem frequency */
+#define SIOCSCMFREQUENCY SIOCDEVPRIVATE+3 /* set cable modem frequency */
+#define SIOCGCMPIDS SIOCDEVPRIVATE+4 /* get cable modem PIDs */
+#define SIOCSCMPIDS SIOCDEVPRIVATE+5 /* set cable modem PIDs */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/if_ec.h b/pfinet/linux-src/include/linux/if_ec.h
new file mode 100644
index 00000000..4883f16a
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_ec.h
@@ -0,0 +1,47 @@
+/* Definitions for Econet sockets. */
+
+#ifndef __LINUX_IF_EC
+#define __LINUX_IF_EC
+
+/* User visible stuff. Glibc provides its own but libc5 folk will use these */
+
+struct ec_addr
+{
+ unsigned char station; /* Station number. */
+ unsigned char net; /* Network number. */
+};
+
+struct sockaddr_ec
+{
+ unsigned short sec_family;
+ unsigned char port; /* Port number. */
+ unsigned char cb; /* Control/flag byte. */
+ unsigned char type; /* Type of message. */
+ struct ec_addr addr;
+ unsigned long cookie;
+};
+
+#define ECTYPE_PACKET_RECEIVED 0 /* Packet received */
+#define ECTYPE_TRANSMIT_STATUS 0x10 /* Transmit completed,
+ low nibble holds status */
+
+#define ECTYPE_TRANSMIT_OK 1
+#define ECTYPE_TRANSMIT_NOT_LISTENING 2
+#define ECTYPE_TRANSMIT_NET_ERROR 3
+#define ECTYPE_TRANSMIT_NO_CLOCK 4
+#define ECTYPE_TRANSMIT_LINE_JAMMED 5
+#define ECTYPE_TRANSMIT_NOT_PRESENT 6
+
+#ifdef __KERNEL__
+
+struct econet_opt
+{
+ unsigned char cb;
+ unsigned char port;
+ unsigned char station;
+ unsigned char net;
+};
+
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/if_eql.h b/pfinet/linux-src/include/linux/if_eql.h
new file mode 100644
index 00000000..320dbebf
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_eql.h
@@ -0,0 +1,81 @@
+/*
+ * Equalizer Load-balancer for serial network interfaces.
+ *
+ * (c) Copyright 1995 Simon "Guru Aleph-Null" Janes
+ * NCM: Network and Communications Management, Inc.
+ *
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * The author may be reached as simon@ncm.com, or C/O
+ * NCM
+ * Attn: Simon Janes
+ * 6803 Whittier Ave
+ * McLean VA 22101
+ * Phone: 1-703-847-0040 ext 103
+ */
+
+#ifndef _LINUX_IF_EQL_H
+#define _LINUX_IF_EQL_H
+
+#include <linux/timer.h>
+
+#define EQL_DEFAULT_SLAVE_PRIORITY 28800
+#define EQL_DEFAULT_MAX_SLAVES 4
+#define EQL_DEFAULT_MTU 576
+#define EQL_DEFAULT_RESCHED_IVAL 100
+
+#define EQL_ENSLAVE (SIOCDEVPRIVATE)
+#define EQL_EMANCIPATE (SIOCDEVPRIVATE + 1)
+
+#define EQL_GETSLAVECFG (SIOCDEVPRIVATE + 2)
+#define EQL_SETSLAVECFG (SIOCDEVPRIVATE + 3)
+
+#define EQL_GETMASTRCFG (SIOCDEVPRIVATE + 4)
+#define EQL_SETMASTRCFG (SIOCDEVPRIVATE + 5)
+
+typedef struct slave {
+ struct device *dev;
+ long priority;
+ long priority_bps;
+ long priority_Bps;
+ long bytes_queued;
+ struct slave *next;
+} slave_t;
+
+typedef struct slave_queue {
+ slave_t *head;
+ slave_t *best_slave;
+ int num_slaves;
+ struct device *master_dev;
+ char lock;
+} slave_queue_t;
+
+typedef struct equalizer {
+ slave_queue_t *queue;
+ int min_slaves;
+ int max_slaves;
+ struct enet_statistics *stats;
+ struct timer_list timer;
+ char timer_on;
+} equalizer_t;
+
+typedef struct master_config {
+ char master_name[16];
+ int max_slaves;
+ int min_slaves;
+} master_config_t;
+
+typedef struct slave_config {
+ char slave_name[16];
+ long priority;
+} slave_config_t;
+
+typedef struct slaving_request {
+ char slave_name[16];
+ long priority;
+} slaving_request_t;
+
+
+#endif /* _LINUX_EQL_H */
diff --git a/pfinet/linux-src/include/linux/if_ether.h b/pfinet/linux-src/include/linux/if_ether.h
new file mode 100644
index 00000000..99bb97fa
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_ether.h
@@ -0,0 +1,98 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the Ethernet IEEE 802.3 interface.
+ *
+ * Version: @(#)if_ether.h 1.0.1a 02/08/94
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Donald Becker, <becker@super.org>
+ * Alan Cox, <alan@redhat.com>
+ * Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_IF_ETHER_H
+#define _LINUX_IF_ETHER_H
+
+/*
+ * IEEE 802.3 Ethernet magic constants. The frame sizes omit the preamble
+ * and FCS/CRC (frame check sequence).
+ */
+
+#define ETH_ALEN 6 /* Octets in one ethernet addr */
+#define ETH_HLEN 14 /* Total octets in header. */
+#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */
+#define ETH_DATA_LEN 1500 /* Max. octets in payload */
+#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */
+
+/*
+ * These are the defined Ethernet Protocol ID's.
+ */
+
+#define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */
+#define ETH_P_ECHO 0x0200 /* Ethernet Echo packet */
+#define ETH_P_PUP 0x0400 /* Xerox PUP packet */
+#define ETH_P_IP 0x0800 /* Internet Protocol packet */
+#define ETH_P_X25 0x0805 /* CCITT X.25 */
+#define ETH_P_ARP 0x0806 /* Address Resolution packet */
+#define ETH_P_BPQ 0x08FF /* G8BPQ AX.25 Ethernet Packet [ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_DEC 0x6000 /* DEC Assigned proto */
+#define ETH_P_DNA_DL 0x6001 /* DEC DNA Dump/Load */
+#define ETH_P_DNA_RC 0x6002 /* DEC DNA Remote Console */
+#define ETH_P_DNA_RT 0x6003 /* DEC DNA Routing */
+#define ETH_P_LAT 0x6004 /* DEC LAT */
+#define ETH_P_DIAG 0x6005 /* DEC Diagnostics */
+#define ETH_P_CUST 0x6006 /* DEC Customer use */
+#define ETH_P_SCA 0x6007 /* DEC Systems Comms Arch */
+#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */
+#define ETH_P_ATALK 0x809B /* Appletalk DDP */
+#define ETH_P_AARP 0x80F3 /* Appletalk AARP */
+#define ETH_P_IPX 0x8137 /* IPX over DIX */
+#define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */
+
+/*
+ * Non DIX types. Won't clash for 1500 types.
+ */
+
+#define ETH_P_802_3 0x0001 /* Dummy type for 802.3 frames */
+#define ETH_P_AX25 0x0002 /* Dummy protocol id for AX.25 */
+#define ETH_P_ALL 0x0003 /* Every packet (be careful!!!) */
+#define ETH_P_802_2 0x0004 /* 802.2 frames */
+#define ETH_P_SNAP 0x0005 /* Internal only */
+#define ETH_P_DDCMP 0x0006 /* DEC DDCMP: Internal only */
+#define ETH_P_WAN_PPP 0x0007 /* Dummy type for WAN PPP frames*/
+#define ETH_P_PPP_MP 0x0008 /* Dummy type for PPP MP frames */
+#define ETH_P_LOCALTALK 0x0009 /* Localtalk pseudo type */
+#define ETH_P_PPPTALK 0x0010 /* Dummy type for Atalk over PPP*/
+#define ETH_P_TR_802_2 0x0011 /* 802.2 frames */
+#define ETH_P_MOBITEX 0x0015 /* Mobitex (kaz@cafe.net) */
+#define ETH_P_CONTROL 0x0016 /* Card specific control frames */
+#define ETH_P_IRDA 0x0017 /* Linux/IR */
+
+/*
+ * This is an Ethernet frame header.
+ */
+
+struct ethhdr
+{
+ unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
+ unsigned char h_source[ETH_ALEN]; /* source ether addr */
+ unsigned short h_proto; /* packet type ID field */
+};
+
+/*
+ * We Have changed the ethernet statistics collection data. This
+ * is just for partial compatibility for now.
+ */
+
+
+#define enet_statistics net_device_stats
+
+#endif /* _LINUX_IF_ETHER_H */
diff --git a/pfinet/linux-src/include/linux/if_fc.h b/pfinet/linux-src/include/linux/if_fc.h
new file mode 100644
index 00000000..33330b45
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_fc.h
@@ -0,0 +1,50 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for Fibre Channel.
+ *
+ * Version: @(#)if_fc.h 0.0 11/20/98
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Donald Becker, <becker@super.org>
+ * Peter De Schrijver, <stud11@cc4.kuleuven.ac.be>
+ * Vineet Abraham, <vma@iol.unh.edu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IF_FC_H
+#define _LINUX_IF_FC_H
+
+
+#define FC_ALEN 6 /* Octets in one ethernet addr */
+#define FC_HLEN (sizeof(struct fch_hdr)+sizeof(struct fcllc))
+#define FC_ID_LEN 3 /* Octets in a Fibre Channel Address */
+
+/* LLC and SNAP constants */
+#define EXTENDED_SAP 0xAA
+#define UI_CMD 0x03
+
+/* This is NOT the Fibre Channel frame header. The FC frame header is
+ * constructed in the driver as the Tachyon needs certain fields in
+ * certains positions. So, it can't be generalized here.*/
+
+struct fch_hdr {
+ __u8 daddr[FC_ALEN]; /* destination address */
+ __u8 saddr[FC_ALEN]; /* source address */
+};
+
+/* This is a Fibre Channel LLC structure */
+struct fcllc {
+ __u8 dsap; /* destination SAP */
+ __u8 ssap; /* source SAP */
+ __u8 llc; /* LLC control field */
+ __u8 protid[3]; /* protocol id */
+ __u16 ethertype; /* ether type field */
+};
+
+#endif /* _LINUX_IF_FC_H */
diff --git a/pfinet/linux-src/include/linux/if_fddi.h b/pfinet/linux-src/include/linux/if_fddi.h
new file mode 100644
index 00000000..22179636
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_fddi.h
@@ -0,0 +1,223 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the ANSI FDDI interface.
+ *
+ * Version: @(#)if_fddi.h 1.0.1 09/16/96
+ *
+ * Author: Lawrence V. Stefani, <stefani@lkg.dec.com>
+ *
+ * if_fddi.h is based on previous if_ether.h and if_tr.h work by
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Donald Becker, <becker@super.org>
+ * Alan Cox, <alan@redhat.com>
+ * Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk>
+ * Peter De Schrijver, <stud11@cc4.kuleuven.ac.be>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IF_FDDI_H
+#define _LINUX_IF_FDDI_H
+
+/*
+ * Define max and min legal sizes. The frame sizes do not include
+ * 4 byte FCS/CRC (frame check sequence).
+ */
+#define FDDI_K_ALEN 6 /* Octets in one FDDI address */
+#define FDDI_K_8022_HLEN 16 /* Total octets in 802.2 header */
+#define FDDI_K_SNAP_HLEN 21 /* Total octets in 802.2 SNAP header */
+#define FDDI_K_8022_ZLEN 16 /* Min octets in 802.2 frame sans FCS */
+#define FDDI_K_SNAP_ZLEN 21 /* Min octets in 802.2 SNAP frame sans FCS */
+#define FDDI_K_8022_DLEN 4475 /* Max octets in 802.2 payload */
+#define FDDI_K_SNAP_DLEN 4470 /* Max octets in 802.2 SNAP payload */
+#define FDDI_K_LLC_ZLEN 13 /* Min octets in LLC frame sans FCS */
+#define FDDI_K_LLC_LEN 4491 /* Max octets in LLC frame sans FCS */
+
+/* Define FDDI Frame Control (FC) Byte values */
+#define FDDI_FC_K_VOID 0x00
+#define FDDI_FC_K_NON_RESTRICTED_TOKEN 0x80
+#define FDDI_FC_K_RESTRICTED_TOKEN 0xC0
+#define FDDI_FC_K_SMT_MIN 0x41
+#define FDDI_FC_K_SMT_MAX 0x4F
+#define FDDI_FC_K_MAC_MIN 0xC1
+#define FDDI_FC_K_MAC_MAX 0xCF
+#define FDDI_FC_K_ASYNC_LLC_MIN 0x50
+#define FDDI_FC_K_ASYNC_LLC_DEF 0x54
+#define FDDI_FC_K_ASYNC_LLC_MAX 0x5F
+#define FDDI_FC_K_SYNC_LLC_MIN 0xD0
+#define FDDI_FC_K_SYNC_LLC_MAX 0xD7
+#define FDDI_FC_K_IMPLEMENTOR_MIN 0x60
+#define FDDI_FC_K_IMPLEMENTOR_MAX 0x6F
+#define FDDI_FC_K_RESERVED_MIN 0x70
+#define FDDI_FC_K_RESERVED_MAX 0x7F
+
+/* Define LLC and SNAP constants */
+#define FDDI_EXTENDED_SAP 0xAA
+#define FDDI_UI_CMD 0x03
+
+/* Define 802.2 Type 1 header */
+struct fddi_8022_1_hdr
+ {
+ __u8 dsap; /* destination service access point */
+ __u8 ssap; /* source service access point */
+ __u8 ctrl; /* control byte #1 */
+ } __attribute__ ((packed));
+
+/* Define 802.2 Type 2 header */
+struct fddi_8022_2_hdr
+ {
+ __u8 dsap; /* destination service access point */
+ __u8 ssap; /* source service access point */
+ __u8 ctrl_1; /* control byte #1 */
+ __u8 ctrl_2; /* control byte #2 */
+ } __attribute__ ((packed));
+
+/* Define 802.2 SNAP header */
+#define FDDI_K_OUI_LEN 3
+struct fddi_snap_hdr
+ {
+ __u8 dsap; /* always 0xAA */
+ __u8 ssap; /* always 0xAA */
+ __u8 ctrl; /* always 0x03 */
+ __u8 oui[FDDI_K_OUI_LEN]; /* organizational universal id */
+ __u16 ethertype; /* packet type ID field */
+ } __attribute__ ((packed));
+
+/* Define FDDI LLC frame header */
+struct fddihdr
+ {
+ __u8 fc; /* frame control */
+ __u8 daddr[FDDI_K_ALEN]; /* destination address */
+ __u8 saddr[FDDI_K_ALEN]; /* source address */
+ union
+ {
+ struct fddi_8022_1_hdr llc_8022_1;
+ struct fddi_8022_2_hdr llc_8022_2;
+ struct fddi_snap_hdr llc_snap;
+ } hdr;
+ } __attribute__ ((packed));
+
+/* Define FDDI statistics structure */
+struct fddi_statistics
+ {
+ __u32 rx_packets; /* total packets received */
+ __u32 tx_packets; /* total packets transmitted */
+ __u32 rx_bytes; /* total bytes received */
+ __u32 tx_bytes; /* total bytes transmitted */
+ __u32 rx_errors; /* bad packets received */
+ __u32 tx_errors; /* packet transmit problems */
+ __u32 rx_dropped; /* no space in linux buffers */
+ __u32 tx_dropped; /* no space available in linux */
+ __u32 multicast; /* multicast packets received */
+ __u32 transmit_collision; /* always 0 for FDDI */
+
+ /* detailed rx_errors */
+ __u32 rx_length_errors;
+ __u32 rx_over_errors; /* receiver ring buff overflow */
+ __u32 rx_crc_errors; /* recved pkt with crc error */
+ __u32 rx_frame_errors; /* recv'd frame alignment error */
+ __u32 rx_fifo_errors; /* recv'r fifo overrun */
+ __u32 rx_missed_errors; /* receiver missed packet */
+
+ /* detailed tx_errors */
+ __u32 tx_aborted_errors;
+ __u32 tx_carrier_errors;
+ __u32 tx_fifo_errors;
+ __u32 tx_heartbeat_errors;
+ __u32 tx_window_errors;
+
+ /* for cslip etc */
+ __u32 rx_compressed;
+ __u32 tx_compressed;
+
+ /* Detailed FDDI statistics. Adopted from RFC 1512 */
+
+ __u8 smt_station_id[8];
+ __u32 smt_op_version_id;
+ __u32 smt_hi_version_id;
+ __u32 smt_lo_version_id;
+ __u8 smt_user_data[32];
+ __u32 smt_mib_version_id;
+ __u32 smt_mac_cts;
+ __u32 smt_non_master_cts;
+ __u32 smt_master_cts;
+ __u32 smt_available_paths;
+ __u32 smt_config_capabilities;
+ __u32 smt_config_policy;
+ __u32 smt_connection_policy;
+ __u32 smt_t_notify;
+ __u32 smt_stat_rpt_policy;
+ __u32 smt_trace_max_expiration;
+ __u32 smt_bypass_present;
+ __u32 smt_ecm_state;
+ __u32 smt_cf_state;
+ __u32 smt_remote_disconnect_flag;
+ __u32 smt_station_status;
+ __u32 smt_peer_wrap_flag;
+ __u32 smt_time_stamp;
+ __u32 smt_transition_time_stamp;
+ __u32 mac_frame_status_functions;
+ __u32 mac_t_max_capability;
+ __u32 mac_tvx_capability;
+ __u32 mac_available_paths;
+ __u32 mac_current_path;
+ __u8 mac_upstream_nbr[FDDI_K_ALEN];
+ __u8 mac_downstream_nbr[FDDI_K_ALEN];
+ __u8 mac_old_upstream_nbr[FDDI_K_ALEN];
+ __u8 mac_old_downstream_nbr[FDDI_K_ALEN];
+ __u32 mac_dup_address_test;
+ __u32 mac_requested_paths;
+ __u32 mac_downstream_port_type;
+ __u8 mac_smt_address[FDDI_K_ALEN];
+ __u32 mac_t_req;
+ __u32 mac_t_neg;
+ __u32 mac_t_max;
+ __u32 mac_tvx_value;
+ __u32 mac_frame_cts;
+ __u32 mac_copied_cts;
+ __u32 mac_transmit_cts;
+ __u32 mac_error_cts;
+ __u32 mac_lost_cts;
+ __u32 mac_frame_error_threshold;
+ __u32 mac_frame_error_ratio;
+ __u32 mac_rmt_state;
+ __u32 mac_da_flag;
+ __u32 mac_una_da_flag;
+ __u32 mac_frame_error_flag;
+ __u32 mac_ma_unitdata_available;
+ __u32 mac_hardware_present;
+ __u32 mac_ma_unitdata_enable;
+ __u32 path_tvx_lower_bound;
+ __u32 path_t_max_lower_bound;
+ __u32 path_max_t_req;
+ __u32 path_configuration[8];
+ __u32 port_my_type[2];
+ __u32 port_neighbor_type[2];
+ __u32 port_connection_policies[2];
+ __u32 port_mac_indicated[2];
+ __u32 port_current_path[2];
+ __u8 port_requested_paths[3*2];
+ __u32 port_mac_placement[2];
+ __u32 port_available_paths[2];
+ __u32 port_pmd_class[2];
+ __u32 port_connection_capabilities[2];
+ __u32 port_bs_flag[2];
+ __u32 port_lct_fail_cts[2];
+ __u32 port_ler_estimate[2];
+ __u32 port_lem_reject_cts[2];
+ __u32 port_lem_cts[2];
+ __u32 port_ler_cutoff[2];
+ __u32 port_ler_alarm[2];
+ __u32 port_connect_state[2];
+ __u32 port_pcm_state[2];
+ __u32 port_pc_withhold[2];
+ __u32 port_ler_flag[2];
+ __u32 port_hardware_present[2];
+ };
+
+#endif /* _LINUX_IF_FDDI_H */
diff --git a/pfinet/linux-src/include/linux/if_frad.h b/pfinet/linux-src/include/linux/if_frad.h
new file mode 100644
index 00000000..4f1a1e2f
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_frad.h
@@ -0,0 +1,201 @@
+/*
+ * DLCI/FRAD Definitions for Frame Relay Access Devices. DLCI devices are
+ * created for each DLCI associated with a FRAD. The FRAD driver
+ * is not truly a network device, but the lower level device
+ * handler. This allows other FRAD manufacturers to use the DLCI
+ * code, including its RFC1490 encapsulation alongside the current
+ * implementation for the Sangoma cards.
+ *
+ * Version: @(#)if_ifrad.h 0.15 31 Mar 96
+ *
+ * Author: Mike McLagan <mike.mclagan@linux.org>
+ *
+ * Changes:
+ * 0.15 Mike McLagan changed structure defs (packed)
+ * re-arranged flags
+ * added DLCI_RET vars
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _FRAD_H_
+#define _FRAD_H_
+
+#include <linux/config.h>
+#include <linux/if.h>
+
+#if defined(CONFIG_DLCI) || defined(CONFIG_DLCI_MODULE)
+
+/* Structures and constants associated with the DLCI device driver */
+
+struct dlci_add
+{
+ char devname[IFNAMSIZ];
+ short dlci;
+};
+
+#define DLCI_GET_CONF (SIOCDEVPRIVATE + 2)
+#define DLCI_SET_CONF (SIOCDEVPRIVATE + 3)
+
+/*
+ * These are related to the Sangoma SDLA and should remain in order.
+ * Code within the SDLA module is based on the specifics of this
+ * structure. Change at your own peril.
+ */
+struct dlci_conf {
+ short flags;
+ short CIR_fwd;
+ short Bc_fwd;
+ short Be_fwd;
+ short CIR_bwd;
+ short Bc_bwd;
+ short Be_bwd;
+
+/* these are part of the status read */
+ short Tc_fwd;
+ short Tc_bwd;
+ short Tf_max;
+ short Tb_max;
+
+/* add any new fields here above is a mirror of sdla_dlci_conf */
+};
+
+#define DLCI_GET_SLAVE (SIOCDEVPRIVATE + 4)
+
+/* configuration flags for DLCI */
+#define DLCI_IGNORE_CIR_OUT 0x0001
+#define DLCI_ACCOUNT_CIR_IN 0x0002
+#define DLCI_BUFFER_IF 0x0008
+
+#define DLCI_VALID_FLAGS 0x000B
+
+/* FRAD driver uses these to indicate what it did with packet */
+#define DLCI_RET_OK 0x00
+#define DLCI_RET_ERR 0x01
+#define DLCI_RET_DROP 0x02
+
+/* defines for the actual Frame Relay hardware */
+#define FRAD_GET_CONF (SIOCDEVPRIVATE)
+#define FRAD_SET_CONF (SIOCDEVPRIVATE + 1)
+
+#define FRAD_LAST_IOCTL FRAD_SET_CONF
+
+/*
+ * Based on the setup for the Sangoma SDLA. If changes are
+ * necessary to this structure, a routine will need to be
+ * added to that module to copy fields.
+ */
+struct frad_conf
+{
+ short station;
+ short flags;
+ short kbaud;
+ short clocking;
+ short mtu;
+ short T391;
+ short T392;
+ short N391;
+ short N392;
+ short N393;
+ short CIR_fwd;
+ short Bc_fwd;
+ short Be_fwd;
+ short CIR_bwd;
+ short Bc_bwd;
+ short Be_bwd;
+
+/* Add new fields here, above is a mirror of the sdla_conf */
+
+};
+
+#define FRAD_STATION_CPE 0x0000
+#define FRAD_STATION_NODE 0x0001
+
+#define FRAD_TX_IGNORE_CIR 0x0001
+#define FRAD_RX_ACCOUNT_CIR 0x0002
+#define FRAD_DROP_ABORTED 0x0004
+#define FRAD_BUFFERIF 0x0008
+#define FRAD_STATS 0x0010
+#define FRAD_MCI 0x0100
+#define FRAD_AUTODLCI 0x8000
+#define FRAD_VALID_FLAGS 0x811F
+
+#define FRAD_CLOCK_INT 0x0001
+#define FRAD_CLOCK_EXT 0x0000
+
+#ifdef __KERNEL__
+
+/* these are the fields of an RFC 1490 header */
+struct frhdr
+{
+ unsigned char control __attribute__((packed));
+
+ /* for IP packets, this can be the NLPID */
+ unsigned char pad __attribute__((packed));
+
+ unsigned char NLPID __attribute__((packed));
+ unsigned char OUI[3] __attribute__((packed));
+ unsigned short PID __attribute__((packed));
+
+#define IP_NLPID pad
+};
+
+/* see RFC 1490 for the definition of the following */
+#define FRAD_I_UI 0x03
+
+#define FRAD_P_PADDING 0x00
+#define FRAD_P_Q933 0x08
+#define FRAD_P_SNAP 0x80
+#define FRAD_P_CLNP 0x81
+#define FRAD_P_IP 0xCC
+
+struct dlci_local
+{
+ struct enet_statistics stats;
+ struct device *slave;
+ struct dlci_conf config;
+ int configured;
+
+ /* callback function */
+ void (*receive)(struct sk_buff *skb, struct device *);
+};
+
+struct frad_local
+{
+ struct enet_statistics stats;
+
+ /* devices which this FRAD is slaved to */
+ struct device *master[CONFIG_DLCI_MAX];
+ short dlci[CONFIG_DLCI_MAX];
+
+ struct frad_conf config;
+ int configured; /* has this device been configured */
+ int initialized; /* mem_start, port, irq set ? */
+
+ /* callback functions */
+ int (*activate)(struct device *, struct device *);
+ int (*deactivate)(struct device *, struct device *);
+ int (*assoc)(struct device *, struct device *);
+ int (*deassoc)(struct device *, struct device *);
+ int (*dlci_conf)(struct device *, struct device *, int get);
+
+ /* fields that are used by the Sangoma SDLA cards */
+ struct timer_list timer;
+ int type; /* adapter type */
+ int state; /* state of the S502/8 control latch */
+ int buffer; /* current buffer for S508 firmware */
+};
+
+int register_frad(const char *name);
+int unregister_frad(const char *name);
+
+int (*dlci_ioctl_hook)(unsigned int, void *);
+
+#endif __KERNEL__
+
+#endif /* CONFIG_DLCI || CONFIG_DLCI_MODULE */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/if_hippi.h b/pfinet/linux-src/include/linux/if_hippi.h
new file mode 100644
index 00000000..52c4a80b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_hippi.h
@@ -0,0 +1,157 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the HIPPI interface.
+ *
+ * Version: @(#)if_hippi.h 1.0.0 05/26/97
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Donald Becker, <becker@super.org>
+ * Alan Cox, <alan@redhat.com>
+ * Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk>
+ * Jes Sorensen, <Jes.Sorensen@cern.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_IF_HIPPI_H
+#define _LINUX_IF_HIPPI_H
+
+#include <asm/byteorder.h>
+
+/*
+ * HIPPI magic constants.
+ */
+
+#define HIPPI_ALEN 6 /* Bytes in one HIPPI hw-addr */
+#define HIPPI_HLEN sizeof(struct hippi_hdr)
+#define HIPPI_ZLEN 0 /* Min. bytes in frame without FCS */
+#define HIPPI_DATA_LEN 65280 /* Max. bytes in payload */
+#define HIPPI_FRAME_LEN (HIPPI_DATA_LEN + HIPPI_HLEN)
+ /* Max. bytes in frame without FCS */
+
+/*
+ * Define LLC and SNAP constants.
+ */
+#define HIPPI_EXTENDED_SAP 0xAA
+#define HIPPI_UI_CMD 0x03
+
+
+/*
+ * Do we need to list some sort of ID's here?
+ */
+
+/*
+ * HIPPI statistics collection data.
+ */
+
+struct hipnet_statistics
+{
+ int rx_packets; /* total packets received */
+ int tx_packets; /* total packets transmitted */
+ int rx_errors; /* bad packets received */
+ int tx_errors; /* packet transmit problems */
+ int rx_dropped; /* no space in linux buffers */
+ int tx_dropped; /* no space available in linux */
+
+ /* detailed rx_errors: */
+ int rx_length_errors;
+ int rx_over_errors; /* receiver ring buff overflow */
+ int rx_crc_errors; /* recved pkt with crc error */
+ int rx_frame_errors; /* recv'd frame alignment error */
+ int rx_fifo_errors; /* recv'r fifo overrun */
+ int rx_missed_errors; /* receiver missed packet */
+
+ /* detailed tx_errors */
+ int tx_aborted_errors;
+ int tx_carrier_errors;
+ int tx_fifo_errors;
+ int tx_heartbeat_errors;
+ int tx_window_errors;
+};
+
+
+struct hippi_fp_hdr
+{
+#if 0
+ __u8 ulp; /* must contain 4 */
+#if defined (__BIG_ENDIAN_BITFIELD)
+ __u8 d1_data_present:1; /* must be 1 */
+ __u8 start_d2_burst_boundary:1; /* must be zero */
+ __u8 reserved:6; /* must be zero */
+#if 0
+ __u16 reserved1:5;
+ __u16 d1_area_size:8; /* must be 3 */
+ __u16 d2_offset:3; /* must be zero */
+#endif
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 reserved:6; /* must be zero */
+ __u8 start_d2_burst_boundary:1; /* must be zero */
+ __u8 d1_data_present:1; /* must be 1 */
+#if 0
+ __u16 d2_offset:3; /* must be zero */
+ __u16 d1_area_size:8; /* must be 3 */
+ __u16 reserved1:5; /* must be zero */
+#endif
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+#else
+ __u32 fixed;
+#endif
+ __u32 d2_size;
+} __attribute__ ((packed));
+
+struct hippi_le_hdr
+{
+#if defined (__BIG_ENDIAN_BITFIELD)
+ unsigned long fc:3;
+ unsigned long double_wide:1;
+ unsigned long message_type:4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned long message_type:4;
+ unsigned long double_wide:1;
+ unsigned long fc:3;
+#endif
+ __u8 dest_switch_addr[3];
+#if defined (__BIG_ENDIAN_BITFIELD)
+ __u8 dest_addr_type:4,
+ src_addr_type:4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 src_addr_type:4,
+ dest_addr_type:4;
+#endif
+ __u8 src_switch_addr[3];
+ __u16 reserved;
+ __u8 daddr[HIPPI_ALEN];
+ __u16 locally_administered;
+ __u8 saddr[HIPPI_ALEN];
+} __attribute__ ((packed));
+
+#define HIPPI_OUI_LEN 3
+/*
+ * Looks like the dsap and ssap fields have been swapped by mistake in
+ * RFC 2067 "IP over HIPPI".
+ */
+struct hippi_snap_hdr
+{
+ __u8 dsap; /* always 0xAA */
+ __u8 ssap; /* always 0xAA */
+ __u8 ctrl; /* always 0x03 */
+ __u8 oui[HIPPI_OUI_LEN]; /* organizational universal id (zero)*/
+ __u16 ethertype; /* packet type ID field */
+} __attribute__ ((packed));
+
+struct hippi_hdr
+{
+ struct hippi_fp_hdr fp;
+ struct hippi_le_hdr le;
+ struct hippi_snap_hdr snap;
+} __attribute__ ((packed));
+
+#endif /* _LINUX_IF_HIPPI_H */
diff --git a/pfinet/linux-src/include/linux/if_ltalk.h b/pfinet/linux-src/include/linux/if_ltalk.h
new file mode 100644
index 00000000..d7b17d32
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_ltalk.h
@@ -0,0 +1,12 @@
+#ifndef __LINUX_LTALK_H
+#define __LINUX_LTALK_H
+
+#define LTALK_HLEN 1
+#define LTALK_MTU 600
+#define LTALK_ALEN 1
+
+#ifdef __KERNEL__
+extern void ltalk_setup(struct device *);
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/if_packet.h b/pfinet/linux-src/include/linux/if_packet.h
new file mode 100644
index 00000000..ad5655cf
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_packet.h
@@ -0,0 +1,50 @@
+#ifndef __LINUX_IF_PACKET_H
+#define __LINUX_IF_PACKET_H
+
+struct sockaddr_pkt
+{
+ unsigned short spkt_family;
+ unsigned char spkt_device[14];
+ unsigned short spkt_protocol;
+};
+
+struct sockaddr_ll
+{
+ unsigned short sll_family;
+ unsigned short sll_protocol;
+ int sll_ifindex;
+ unsigned short sll_hatype;
+ unsigned char sll_pkttype;
+ unsigned char sll_halen;
+ unsigned char sll_addr[8];
+};
+
+/* Packet types */
+
+#define PACKET_HOST 0 /* To us */
+#define PACKET_BROADCAST 1 /* To all */
+#define PACKET_MULTICAST 2 /* To group */
+#define PACKET_OTHERHOST 3 /* To someone else */
+#define PACKET_OUTGOING 4 /* Outgoing of any type */
+/* These ones are invisible by user level */
+#define PACKET_LOOPBACK 5 /* MC/BRD frame looped back */
+#define PACKET_FASTROUTE 6 /* Fastrouted frame */
+
+/* Packet socket options */
+
+#define PACKET_ADD_MEMBERSHIP 1
+#define PACKET_DROP_MEMBERSHIP 2
+
+struct packet_mreq
+{
+ int mr_ifindex;
+ unsigned short mr_type;
+ unsigned short mr_alen;
+ unsigned char mr_address[8];
+};
+
+#define PACKET_MR_MULTICAST 0
+#define PACKET_MR_PROMISC 1
+#define PACKET_MR_ALLMULTI 2
+
+#endif
diff --git a/pfinet/linux-src/include/linux/if_plip.h b/pfinet/linux-src/include/linux/if_plip.h
new file mode 100644
index 00000000..153a6499
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_plip.h
@@ -0,0 +1,28 @@
+/*
+ * NET3 PLIP tuning facilities for the new Niibe PLIP.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef _LINUX_IF_PLIP_H
+#define _LINUX_IF_PLIP_H
+
+#include <linux/sockios.h>
+
+#define SIOCDEVPLIP SIOCDEVPRIVATE
+
+struct plipconf
+{
+ unsigned short pcmd;
+ unsigned long nibble;
+ unsigned long trigger;
+};
+
+#define PLIP_GET_TIMEOUT 0x1
+#define PLIP_SET_TIMEOUT 0x2
+
+#endif
diff --git a/pfinet/linux-src/include/linux/if_ppp.h b/pfinet/linux-src/include/linux/if_ppp.h
new file mode 100644
index 00000000..1d103bdc
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_ppp.h
@@ -0,0 +1,143 @@
+/* $Id: if_ppp.h,v 1.19 1999/03/31 06:07:57 paulus Exp $ */
+
+/*
+ * if_ppp.h - Point-to-Point Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+/*
+ * ==FILEVERSION 990331==
+ *
+ * NOTE TO MAINTAINERS:
+ * If you modify this file at all, please set the above date.
+ * if_ppp.h is shipped with a PPP distribution as well as with the kernel;
+ * if everyone increases the FILEVERSION number above, then scripts
+ * can do the right thing when deciding whether to install a new if_ppp.h
+ * file. Don't change the format of that line otherwise, so the
+ * installation script can recognize it.
+ */
+
+#ifndef _IF_PPP_H_
+#define _IF_PPP_H_
+
+/*
+ * Packet sizes
+ */
+
+#define PPP_MTU 1500 /* Default MTU (size of Info field) */
+#define PPP_MAXMRU 65000 /* Largest MRU we allow */
+#define PPP_VERSION "2.3.7"
+#define PPP_MAGIC 0x5002 /* Magic value for the ppp structure */
+#define PROTO_IPX 0x002b /* protocol numbers */
+#define PROTO_DNA_RT 0x0027 /* DNA Routing */
+
+
+/*
+ * Bit definitions for flags.
+ */
+
+#define SC_COMP_PROT 0x00000001 /* protocol compression (output) */
+#define SC_COMP_AC 0x00000002 /* header compression (output) */
+#define SC_COMP_TCP 0x00000004 /* TCP (VJ) compression (output) */
+#define SC_NO_TCP_CCID 0x00000008 /* disable VJ connection-id comp. */
+#define SC_REJ_COMP_AC 0x00000010 /* reject adrs/ctrl comp. on input */
+#define SC_REJ_COMP_TCP 0x00000020 /* reject TCP (VJ) comp. on input */
+#define SC_CCP_OPEN 0x00000040 /* Look at CCP packets */
+#define SC_CCP_UP 0x00000080 /* May send/recv compressed packets */
+#define SC_ENABLE_IP 0x00000100 /* IP packets may be exchanged */
+#define SC_COMP_RUN 0x00001000 /* compressor has been inited */
+#define SC_DECOMP_RUN 0x00002000 /* decompressor has been inited */
+#define SC_DEBUG 0x00010000 /* enable debug messages */
+#define SC_LOG_INPKT 0x00020000 /* log contents of good pkts recvd */
+#define SC_LOG_OUTPKT 0x00040000 /* log contents of pkts sent */
+#define SC_LOG_RAWIN 0x00080000 /* log all chars received */
+#define SC_LOG_FLUSH 0x00100000 /* log all chars flushed */
+#define SC_SYNC 0x00200000 /* synchronous serial mode */
+#define SC_MASK 0x0f2000ff /* bits that user can change */
+
+/* state bits */
+#define SC_XMIT_BUSY 0x10000000 /* (used by isdn_ppp?) */
+#define SC_RCV_ODDP 0x08000000 /* have rcvd char with odd parity */
+#define SC_RCV_EVNP 0x04000000 /* have rcvd char with even parity */
+#define SC_RCV_B7_1 0x02000000 /* have rcvd char with bit 7 = 1 */
+#define SC_RCV_B7_0 0x01000000 /* have rcvd char with bit 7 = 0 */
+#define SC_DC_FERROR 0x00800000 /* fatal decomp error detected */
+#define SC_DC_ERROR 0x00400000 /* non-fatal decomp error detected */
+
+/*
+ * Ioctl definitions.
+ */
+
+struct npioctl {
+ int protocol; /* PPP protocol, e.g. PPP_IP */
+ enum NPmode mode;
+};
+
+/* Structure describing a CCP configuration option, for PPPIOCSCOMPRESS */
+struct ppp_option_data {
+ __u8 *ptr;
+ __u32 length;
+ int transmit;
+};
+
+struct ifpppstatsreq {
+ struct ifreq b;
+ struct ppp_stats stats; /* statistic information */
+};
+
+struct ifpppcstatsreq {
+ struct ifreq b;
+ struct ppp_comp_stats stats;
+};
+
+#define ifr__name b.ifr_ifrn.ifrn_name
+#define stats_ptr b.ifr_ifru.ifru_data
+
+/*
+ * Ioctl definitions.
+ */
+
+#define PPPIOCGFLAGS _IOR('t', 90, int) /* get configuration flags */
+#define PPPIOCSFLAGS _IOW('t', 89, int) /* set configuration flags */
+#define PPPIOCGASYNCMAP _IOR('t', 88, int) /* get async map */
+#define PPPIOCSASYNCMAP _IOW('t', 87, int) /* set async map */
+#define PPPIOCGUNIT _IOR('t', 86, int) /* get ppp unit number */
+#define PPPIOCGRASYNCMAP _IOR('t', 85, int) /* get receive async map */
+#define PPPIOCSRASYNCMAP _IOW('t', 84, int) /* set receive async map */
+#define PPPIOCGMRU _IOR('t', 83, int) /* get max receive unit */
+#define PPPIOCSMRU _IOW('t', 82, int) /* set max receive unit */
+#define PPPIOCSMAXCID _IOW('t', 81, int) /* set VJ max slot ID */
+#define PPPIOCGXASYNCMAP _IOR('t', 80, ext_accm) /* get extended ACCM */
+#define PPPIOCSXASYNCMAP _IOW('t', 79, ext_accm) /* set extended ACCM */
+#define PPPIOCXFERUNIT _IO('t', 78) /* transfer PPP unit */
+#define PPPIOCSCOMPRESS _IOW('t', 77, struct ppp_option_data)
+#define PPPIOCGNPMODE _IOWR('t', 76, struct npioctl) /* get NP mode */
+#define PPPIOCSNPMODE _IOW('t', 75, struct npioctl) /* set NP mode */
+#define PPPIOCGDEBUG _IOR('t', 65, int) /* Read debug level */
+#define PPPIOCSDEBUG _IOW('t', 64, int) /* Set debug level */
+#define PPPIOCGIDLE _IOR('t', 63, struct ppp_idle) /* get idle time */
+
+#define SIOCGPPPSTATS (SIOCDEVPRIVATE + 0)
+#define SIOCGPPPVER (SIOCDEVPRIVATE + 1) /* NEVER change this!! */
+#define SIOCGPPPCSTATS (SIOCDEVPRIVATE + 2)
+
+#if !defined(ifr_mtu)
+#define ifr_mtu ifr_ifru.ifru_metric
+#endif
+
+#endif /* _IF_PPP_H_ */
diff --git a/pfinet/linux-src/include/linux/if_pppvar.h b/pfinet/linux-src/include/linux/if_pppvar.h
new file mode 100644
index 00000000..d6cd0c25
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_pppvar.h
@@ -0,0 +1,135 @@
+/* From: if_pppvar.h,v 1.2 1995/06/12 11:36:51 paulus Exp */
+/*
+ * if_pppvar.h - private structures and declarations for PPP.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * ==FILEVERSION 990325==
+ *
+ * NOTE TO MAINTAINERS:
+ * If you modify this file at all, please set the above date.
+ * if_pppvar.h is shipped with a PPP distribution as well as with the kernel;
+ * if everyone increases the FILEVERSION number above, then scripts
+ * can do the right thing when deciding whether to install a new if_pppvar.h
+ * file. Don't change the format of that line otherwise, so the
+ * installation script can recognize it.
+ */
+
+/*
+ * Supported network protocols. These values are used for
+ * indexing sc_npmode.
+ */
+
+#define NP_IP 0 /* Internet Protocol */
+#define NP_IPX 1 /* IPX protocol */
+#define NP_AT 2 /* Appletalk protocol */
+#define NP_IPV6 3 /* Internet Protocol */
+#define NUM_NP 4 /* Number of NPs. */
+
+#define OBUFSIZE 256 /* # chars of output buffering */
+
+/*
+ * Structure describing each ppp unit.
+ */
+
+struct ppp {
+ int magic; /* magic value for structure */
+ struct ppp *next; /* unit with next index */
+ unsigned long inuse; /* are we allocated? */
+ int line; /* network interface unit # */
+ __u32 flags; /* miscellaneous control flags */
+ int mtu; /* maximum xmit frame size */
+ int mru; /* maximum receive frame size */
+ struct slcompress *slcomp; /* for TCP header compression */
+ struct sk_buff_head xmt_q; /* frames to send from pppd */
+ struct sk_buff_head rcv_q; /* frames for pppd to read */
+ unsigned long xmit_busy; /* bit 0 set when xmitter busy */
+
+ /* Information specific to using ppp on async serial lines. */
+ struct tty_struct *tty; /* ptr to TTY structure */
+ struct tty_struct *backup_tty; /* TTY to use if tty gets closed */
+ __u8 escape; /* 0x20 if prev char was PPP_ESC */
+ __u8 toss; /* toss this frame */
+ volatile __u8 tty_pushing; /* internal state flag */
+ volatile __u8 woke_up; /* internal state flag */
+ __u32 xmit_async_map[8]; /* 1 bit means that given control
+ character is quoted on output*/
+ __u32 recv_async_map; /* 1 bit means that given control
+ character is ignored on input*/
+ __u32 bytes_sent; /* Bytes sent on frame */
+ __u32 bytes_rcvd; /* Bytes recvd on frame */
+
+ /* Async transmission information */
+ struct sk_buff *tpkt; /* frame currently being sent */
+ int tpkt_pos; /* how much of it we've done */
+ __u16 tfcs; /* FCS so far for it */
+ unsigned char *optr; /* where we're up to in sending */
+ unsigned char *olim; /* points past last valid char */
+
+ /* Async reception information */
+ struct sk_buff *rpkt; /* frame currently being rcvd */
+ __u16 rfcs; /* FCS so far of rpkt */
+
+ /* Queues for select() functionality */
+ struct wait_queue *read_wait; /* queue for reading processes */
+
+ /* info for detecting idle channels */
+ unsigned long last_xmit; /* time of last transmission */
+ unsigned long last_recv; /* time last packet received */
+
+ /* Statistic information */
+ struct pppstat stats; /* statistic information */
+
+ /* PPP compression protocol information */
+ struct compressor *sc_xcomp; /* transmit compressor */
+ void *sc_xc_state; /* transmit compressor state */
+ struct compressor *sc_rcomp; /* receive decompressor */
+ void *sc_rc_state; /* receive decompressor state */
+
+ enum NPmode sc_npmode[NUM_NP]; /* what to do with each NP */
+ int sc_xfer; /* PID of reserved PPP table */
+ char name[8]; /* space for unit name */
+ struct device dev; /* net device structure */
+ struct enet_statistics estats; /* more detailed stats */
+
+ /* tty output buffer */
+ unsigned char obuf[OBUFSIZE]; /* buffer for characters to send */
+};
diff --git a/pfinet/linux-src/include/linux/if_shaper.h b/pfinet/linux-src/include/linux/if_shaper.h
new file mode 100644
index 00000000..e2e97c30
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_shaper.h
@@ -0,0 +1,64 @@
+#ifndef __LINUX_SHAPER_H
+#define __LINUX_SHAPER_H
+
+#ifdef __KERNEL__
+
+#define SHAPER_QLEN 10
+/*
+ * This is a bit speed dependent (read it shouldn't be a constant!)
+ *
+ * 5 is about right for 28.8 upwards. Below that double for every
+ * halving of speed or so. - ie about 20 for 9600 baud.
+ */
+#define SHAPER_LATENCY (5*HZ)
+#define SHAPER_MAXSLIP 2
+#define SHAPER_BURST (HZ/50) /* Good for >128K then */
+
+struct shaper
+{
+ struct sk_buff_head sendq;
+ __u32 bytespertick;
+ __u32 bitspersec;
+ __u32 shapelatency;
+ __u32 shapeclock;
+ __u32 recovery; /* Time we can next clock a packet out on
+ an empty queue */
+ unsigned long locked;
+ struct net_device_stats stats;
+ struct device *dev;
+ int (*hard_start_xmit) (struct sk_buff *skb,
+ struct device *dev);
+ int (*hard_header) (struct sk_buff *skb,
+ struct device *dev,
+ unsigned short type,
+ void *daddr,
+ void *saddr,
+ unsigned len);
+ int (*rebuild_header)(struct sk_buff *skb);
+ int (*hard_header_cache)(struct neighbour *neigh, struct hh_cache *hh);
+ void (*header_cache_update)(struct hh_cache *hh, struct device *dev, unsigned char * haddr);
+ struct net_device_stats* (*get_stats)(struct device *dev);
+ struct wait_queue *wait_queue;
+ struct timer_list timer;
+};
+
+#endif
+
+#define SHAPER_SET_DEV 0x0001
+#define SHAPER_SET_SPEED 0x0002
+#define SHAPER_GET_DEV 0x0003
+#define SHAPER_GET_SPEED 0x0004
+
+struct shaperconf
+{
+ __u16 ss_cmd;
+ union
+ {
+ char ssu_name[14];
+ __u32 ssu_speed;
+ } ss_u;
+#define ss_speed ss_u.ssu_speed
+#define ss_name ss_u.ssu_name
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/if_slip.h b/pfinet/linux-src/include/linux/if_slip.h
new file mode 100644
index 00000000..1eb4e3a8
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_slip.h
@@ -0,0 +1,30 @@
+/*
+ * Swansea University Computer Society NET3
+ *
+ * This file declares the constants of special use with the SLIP/CSLIP/
+ * KISS TNC driver.
+ */
+
+#ifndef __LINUX_SLIP_H
+#define __LINUX_SLIP_H
+
+#define SL_MODE_SLIP 0
+#define SL_MODE_CSLIP 1
+#define SL_MODE_KISS 4
+
+#define SL_OPT_SIXBIT 2
+#define SL_OPT_ADAPTIVE 8
+
+/*
+ * VSV = ioctl for keepalive & outfill in SLIP driver
+ */
+
+#define SIOCSKEEPALIVE (SIOCDEVPRIVATE) /* Set keepalive timeout in sec */
+#define SIOCGKEEPALIVE (SIOCDEVPRIVATE+1) /* Get keepalive timeout */
+#define SIOCSOUTFILL (SIOCDEVPRIVATE+2) /* Set outfill timeout */
+#define SIOCGOUTFILL (SIOCDEVPRIVATE+3) /* Get outfill timeout */
+#define SIOCSLEASE (SIOCDEVPRIVATE+4) /* Set "leased" line type */
+#define SIOCGLEASE (SIOCDEVPRIVATE+5) /* Get line type */
+
+
+#endif
diff --git a/pfinet/linux-src/include/linux/if_strip.h b/pfinet/linux-src/include/linux/if_strip.h
new file mode 100644
index 00000000..fb5c5c98
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_strip.h
@@ -0,0 +1,25 @@
+/*
+ * if_strip.h --
+ *
+ * Definitions for the STRIP interface
+ *
+ * Copyright 1996 The Board of Trustees of The Leland Stanford
+ * Junior University. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies. Stanford University
+ * makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without
+ * express or implied warranty.
+ */
+
+#ifndef __LINUX_STRIP_H
+#define __LINUX_STRIP_H
+
+typedef struct {
+ __u8 c[6];
+} MetricomAddress;
+
+#endif
diff --git a/pfinet/linux-src/include/linux/if_tr.h b/pfinet/linux-src/include/linux/if_tr.h
new file mode 100644
index 00000000..f7c97eeb
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_tr.h
@@ -0,0 +1,100 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the Token-Ring IEEE 802.5 interface.
+ *
+ * Version: @(#)if_tr.h 0.0 07/11/94
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Donald Becker, <becker@super.org>
+ * Peter De Schrijver, <stud11@cc4.kuleuven.ac.be>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IF_TR_H
+#define _LINUX_IF_TR_H
+
+
+/* IEEE 802.5 Token-Ring magic constants. The frame sizes omit the preamble
+ and FCS/CRC (frame check sequence). */
+#define TR_ALEN 6 /* Octets in one ethernet addr */
+#define TR_HLEN (sizeof(struct trh_hdr)+sizeof(struct trllc))
+#define AC 0x10
+#define LLC_FRAME 0x40
+#if 0
+#define ETH_HLEN 14 /* Total octets in header. */
+#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */
+#define ETH_DATA_LEN 1500 /* Max. octets in payload */
+#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */
+#endif
+
+
+/* LLC and SNAP constants */
+#define EXTENDED_SAP 0xAA
+#define UI_CMD 0x03
+
+/* This is an Token-Ring frame header. */
+struct trh_hdr {
+ __u8 ac; /* access control field */
+ __u8 fc; /* frame control field */
+ __u8 daddr[TR_ALEN]; /* destination address */
+ __u8 saddr[TR_ALEN]; /* source address */
+ __u16 rcf; /* route control field */
+ __u16 rseg[8]; /* routing registers */
+};
+
+/* This is an Token-Ring LLC structure */
+struct trllc {
+ __u8 dsap; /* destination SAP */
+ __u8 ssap; /* source SAP */
+ __u8 llc; /* LLC control field */
+ __u8 protid[3]; /* protocol id */
+ __u16 ethertype; /* ether type field */
+};
+
+/* Token-Ring statistics collection data. */
+struct tr_statistics {
+ unsigned long rx_packets; /* total packets received */
+ unsigned long tx_packets; /* total packets transmitted */
+ unsigned long rx_bytes; /* total bytes received */
+ unsigned long tx_bytes; /* total bytes transmitted */
+ unsigned long rx_errors; /* bad packets received */
+ unsigned long tx_errors; /* packet transmit problems */
+ unsigned long rx_dropped; /* no space in linux buffers */
+ unsigned long tx_dropped; /* no space available in linux */
+ unsigned long multicast; /* multicast packets received */
+ unsigned long transmit_collision;
+
+ /* detailed Token-Ring errors. See IBM Token-Ring Network
+ Architecture for more info */
+
+ unsigned long line_errors;
+ unsigned long internal_errors;
+ unsigned long burst_errors;
+ unsigned long A_C_errors;
+ unsigned long abort_delimiters;
+ unsigned long lost_frames;
+ unsigned long recv_congest_count;
+ unsigned long frame_copied_errors;
+ unsigned long frequency_errors;
+ unsigned long token_errors;
+ unsigned long dummy1;
+};
+
+/* source routing stuff */
+
+#define TR_RII 0x80
+#define TR_RCF_DIR_BIT 0x80
+#define TR_RCF_LEN_MASK 0x1f00
+#define TR_RCF_BROADCAST 0x8000 /* all-routes broadcast */
+#define TR_RCF_LIMITED_BROADCAST 0xC000 /* single-route broadcast */
+#define TR_RCF_FRAME2K 0x20
+#define TR_RCF_BROADCAST_MASK 0xC000
+#define TR_MAXRIFLEN 18
+
+#endif /* _LINUX_IF_TR_H */
diff --git a/pfinet/linux-src/include/linux/if_tunnel.h b/pfinet/linux-src/include/linux/if_tunnel.h
new file mode 100644
index 00000000..bef9f8fd
--- /dev/null
+++ b/pfinet/linux-src/include/linux/if_tunnel.h
@@ -0,0 +1,29 @@
+#ifndef _IF_TUNNEL_H_
+#define _IF_TUNNEL_H_
+
+#define SIOCGETTUNNEL (SIOCDEVPRIVATE + 0)
+#define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1)
+#define SIOCDELTUNNEL (SIOCDEVPRIVATE + 2)
+#define SIOCCHGTUNNEL (SIOCDEVPRIVATE + 3)
+
+#define GRE_CSUM __constant_htons(0x8000)
+#define GRE_ROUTING __constant_htons(0x4000)
+#define GRE_KEY __constant_htons(0x2000)
+#define GRE_SEQ __constant_htons(0x1000)
+#define GRE_STRICT __constant_htons(0x0800)
+#define GRE_REC __constant_htons(0x0700)
+#define GRE_FLAGS __constant_htons(0x00F8)
+#define GRE_VERSION __constant_htons(0x0007)
+
+struct ip_tunnel_parm
+{
+ char name[IFNAMSIZ];
+ int link;
+ __u16 i_flags;
+ __u16 o_flags;
+ __u32 i_key;
+ __u32 o_key;
+ struct iphdr iph;
+};
+
+#endif /* _IF_TUNNEL_H_ */
diff --git a/pfinet/linux-src/include/linux/igmp.h b/pfinet/linux-src/include/linux/igmp.h
new file mode 100644
index 00000000..c13afde2
--- /dev/null
+++ b/pfinet/linux-src/include/linux/igmp.h
@@ -0,0 +1,129 @@
+/*
+ * Linux NET3: Internet Group Management Protocol [IGMP]
+ *
+ * Authors:
+ * Alan Cox <Alan.Cox@linux.org>
+ *
+ * Extended to talk the BSD extended IGMP protocol of mrouted 3.6
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_IGMP_H
+#define _LINUX_IGMP_H
+
+/*
+ * IGMP protocol structures
+ */
+
+/*
+ * Header in on cable format
+ */
+
+struct igmphdr
+{
+ __u8 type;
+ __u8 code; /* For newer IGMP */
+ __u16 csum;
+ __u32 group;
+};
+
+#define IGMP_HOST_MEMBERSHIP_QUERY 0x11 /* From RFC1112 */
+#define IGMP_HOST_MEMBERSHIP_REPORT 0x12 /* Ditto */
+#define IGMP_DVMRP 0x13 /* DVMRP routing */
+#define IGMP_PIM 0x14 /* PIM routing */
+#define IGMP_TRACE 0x15
+#define IGMP_HOST_NEW_MEMBERSHIP_REPORT 0x16 /* New version of 0x11 */
+#define IGMP_HOST_LEAVE_MESSAGE 0x17
+
+#define IGMP_MTRACE_RESP 0x1e
+#define IGMP_MTRACE 0x1f
+
+
+/*
+ * Use the BSD names for these for compatibility
+ */
+
+#define IGMP_DELAYING_MEMBER 0x01
+#define IGMP_IDLE_MEMBER 0x02
+#define IGMP_LAZY_MEMBER 0x03
+#define IGMP_SLEEPING_MEMBER 0x04
+#define IGMP_AWAKENING_MEMBER 0x05
+
+#define IGMP_MINLEN 8
+
+#define IGMP_MAX_HOST_REPORT_DELAY 10 /* max delay for response to */
+ /* query (in seconds) */
+
+#define IGMP_TIMER_SCALE 10 /* denotes that the igmphdr->timer field */
+ /* specifies time in 10th of seconds */
+
+#define IGMP_AGE_THRESHOLD 400 /* If this host don't hear any IGMP V1 */
+ /* message in this period of time, */
+ /* revert to IGMP v2 router. */
+
+#define IGMP_ALL_HOSTS htonl(0xE0000001L)
+#define IGMP_ALL_ROUTER htonl(0xE0000002L)
+#define IGMP_LOCAL_GROUP htonl(0xE0000000L)
+#define IGMP_LOCAL_GROUP_MASK htonl(0xFFFFFF00L)
+
+/*
+ * struct for keeping the multicast list in
+ */
+
+#ifdef __KERNEL__
+
+/* ip_mc_socklist is real list now. Speed is not argument;
+ this list never used in fast path code
+ */
+
+struct ip_mc_socklist
+{
+ struct ip_mc_socklist *next;
+ int count;
+ struct ip_mreqn multi;
+};
+
+struct ip_mc_list
+{
+ struct in_device *interface;
+ unsigned long multiaddr;
+ struct ip_mc_list *next;
+ struct timer_list timer;
+ int users;
+ char tm_running;
+ char reporter;
+ char unsolicit_count;
+ char loaded;
+};
+
+extern __inline__ int ip_check_mc(struct device *dev, u32 mc_addr)
+{
+ struct in_device *in_dev = dev->ip_ptr;
+ struct ip_mc_list *im;
+
+ if (in_dev) {
+ for (im=in_dev->mc_list; im; im=im->next)
+ if (im->multiaddr == mc_addr)
+ return 1;
+ }
+ return 0;
+}
+
+extern int igmp_rcv(struct sk_buff *, unsigned short);
+extern int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr);
+extern int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr);
+extern void ip_mc_drop_socket(struct sock *sk);
+extern void ip_mr_init(void);
+extern void ip_mc_init_dev(struct in_device *);
+extern void ip_mc_destroy_dev(struct in_device *);
+extern void ip_mc_up(struct in_device *);
+extern void ip_mc_down(struct in_device *);
+extern int ip_mc_dec_group(struct in_device *in_dev, u32 addr);
+extern void ip_mc_inc_group(struct in_device *in_dev, u32 addr);
+#endif
+#endif
diff --git a/pfinet/linux-src/include/linux/in.h b/pfinet/linux-src/include/linux/in.h
new file mode 100644
index 00000000..37db22a9
--- /dev/null
+++ b/pfinet/linux-src/include/linux/in.h
@@ -0,0 +1,189 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions of the Internet Protocol.
+ *
+ * Version: @(#)in.h 1.0.1 04/21/93
+ *
+ * Authors: Original taken from the GNU Project <netinet/in.h> file.
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IN_H
+#define _LINUX_IN_H
+
+#include <linux/types.h>
+
+/* Standard well-defined IP protocols. */
+enum {
+ IPPROTO_IP = 0, /* Dummy protocol for TCP */
+ IPPROTO_ICMP = 1, /* Internet Control Message Protocol */
+ IPPROTO_IGMP = 2, /* Internet Group Management Protocol */
+ IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94) */
+ IPPROTO_TCP = 6, /* Transmission Control Protocol */
+ IPPROTO_EGP = 8, /* Exterior Gateway Protocol */
+ IPPROTO_PUP = 12, /* PUP protocol */
+ IPPROTO_UDP = 17, /* User Datagram Protocol */
+ IPPROTO_IDP = 22, /* XNS IDP protocol */
+ IPPROTO_RSVP = 46, /* RSVP protocol */
+ IPPROTO_GRE = 47, /* Cisco GRE tunnels (rfc 1701,1702) */
+
+ IPPROTO_IPV6 = 41, /* IPv6-in-IPv4 tunnelling */
+
+ IPPROTO_PIM = 103, /* Protocol Independent Multicast */
+
+ IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */
+ IPPROTO_AH = 51, /* Authentication Header protocol */
+ IPPROTO_COMP = 108, /* Compression Header protocol */
+
+ IPPROTO_RAW = 255, /* Raw IP packets */
+ IPPROTO_MAX
+};
+
+
+/* Internet address. */
+struct in_addr {
+ __u32 s_addr;
+};
+
+#define IP_TOS 1
+#define IP_TTL 2
+#define IP_HDRINCL 3
+#define IP_OPTIONS 4
+#define IP_ROUTER_ALERT 5
+#define IP_RECVOPTS 6
+#define IP_RETOPTS 7
+#define IP_PKTINFO 8
+#define IP_PKTOPTIONS 9
+#define IP_MTU_DISCOVER 10
+#define IP_RECVERR 11
+#define IP_RECVTTL 12
+#define IP_RECVTOS 13
+#define IP_MTU 14
+
+/* BSD compatibility */
+#define IP_RECVRETOPTS IP_RETOPTS
+
+/* IP_MTU_DISCOVER values */
+#define IP_PMTUDISC_DONT 0 /* Never send DF frames */
+#define IP_PMTUDISC_WANT 1 /* Use per route hints */
+#define IP_PMTUDISC_DO 2 /* Always DF */
+
+#define IP_MULTICAST_IF 32
+#define IP_MULTICAST_TTL 33
+#define IP_MULTICAST_LOOP 34
+#define IP_ADD_MEMBERSHIP 35
+#define IP_DROP_MEMBERSHIP 36
+
+/* These need to appear somewhere around here */
+#define IP_DEFAULT_MULTICAST_TTL 1
+#define IP_DEFAULT_MULTICAST_LOOP 1
+
+/* Request struct for multicast socket ops */
+
+struct ip_mreq
+{
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_interface; /* local IP address of interface */
+};
+
+struct ip_mreqn
+{
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_address; /* local IP address of interface */
+ int imr_ifindex; /* Interface index */
+};
+
+struct in_pktinfo
+{
+ int ipi_ifindex;
+ struct in_addr ipi_spec_dst;
+ struct in_addr ipi_addr;
+};
+
+/* Structure describing an Internet (IP) socket address. */
+#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
+struct sockaddr_in {
+ sa_family_t sin_family; /* Address family */
+ unsigned short int sin_port; /* Port number */
+ struct in_addr sin_addr; /* Internet address */
+
+ /* Pad to size of `struct sockaddr'. */
+ unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
+ sizeof(unsigned short int) - sizeof(struct in_addr)];
+};
+#define sin_zero __pad /* for BSD UNIX comp. -FvK */
+
+
+/*
+ * Definitions of the bits in an Internet address integer.
+ * On subnets, host and network parts are found according
+ * to the subnet mask, not these masks.
+ */
+#define IN_CLASSA(a) ((((long int) (a)) & 0x80000000) == 0)
+#define IN_CLASSA_NET 0xff000000
+#define IN_CLASSA_NSHIFT 24
+#define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET)
+#define IN_CLASSA_MAX 128
+
+#define IN_CLASSB(a) ((((long int) (a)) & 0xc0000000) == 0x80000000)
+#define IN_CLASSB_NET 0xffff0000
+#define IN_CLASSB_NSHIFT 16
+#define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET)
+#define IN_CLASSB_MAX 65536
+
+#define IN_CLASSC(a) ((((long int) (a)) & 0xe0000000) == 0xc0000000)
+#define IN_CLASSC_NET 0xffffff00
+#define IN_CLASSC_NSHIFT 8
+#define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET)
+
+#define IN_CLASSD(a) ((((long int) (a)) & 0xf0000000) == 0xe0000000)
+#define IN_MULTICAST(a) IN_CLASSD(a)
+#define IN_MULTICAST_NET 0xF0000000
+
+#define IN_EXPERIMENTAL(a) ((((long int) (a)) & 0xf0000000) == 0xf0000000)
+#define IN_BADCLASS(a) IN_EXPERIMENTAL((a))
+
+/* Address to accept any incoming messages. */
+#define INADDR_ANY ((unsigned long int) 0x00000000)
+
+/* Address to send to all hosts. */
+#define INADDR_BROADCAST ((unsigned long int) 0xffffffff)
+
+/* Address indicating an error return. */
+#define INADDR_NONE ((unsigned long int) 0xffffffff)
+
+/* Network number for local host loopback. */
+#define IN_LOOPBACKNET 127
+
+/* Address to loopback in software to local host. */
+#define INADDR_LOOPBACK 0x7f000001 /* 127.0.0.1 */
+#define IN_LOOPBACK(a) ((((long int) (a)) & 0xff000000) == 0x7f000000)
+
+/* Defines for Multicast INADDR */
+#define INADDR_UNSPEC_GROUP 0xe0000000U /* 224.0.0.0 */
+#define INADDR_ALLHOSTS_GROUP 0xe0000001U /* 224.0.0.1 */
+#define INADDR_ALLRTRS_GROUP 0xe0000002U /* 224.0.0.2 */
+#define INADDR_MAX_LOCAL_GROUP 0xe00000ffU /* 224.0.0.255 */
+
+
+/* <asm/byteorder.h> contains the htonl type stuff.. */
+#include <asm/byteorder.h>
+
+#ifdef __KERNEL__
+/* Some random defines to make it easier in the kernel.. */
+#define LOOPBACK(x) (((x) & htonl(0xff000000)) == htonl(0x7f000000))
+#define MULTICAST(x) (((x) & htonl(0xf0000000)) == htonl(0xe0000000))
+#define BADCLASS(x) (((x) & htonl(0xf0000000)) == htonl(0xf0000000))
+#define ZERONET(x) (((x) & htonl(0xff000000)) == htonl(0x00000000))
+#define LOCAL_MCAST(x) (((x) & htonl(0xFFFFFF00)) == htonl(0xE0000000))
+
+#endif
+
+#endif /* _LINUX_IN_H */
diff --git a/pfinet/linux-src/include/linux/in6.h b/pfinet/linux-src/include/linux/in6.h
new file mode 100644
index 00000000..35e018c7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/in6.h
@@ -0,0 +1,194 @@
+/*
+ * Types and definitions for AF_INET6
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * Sources:
+ * IPv6 Program Interfaces for BSD Systems
+ * <draft-ietf-ipngwg-bsd-api-05.txt>
+ *
+ * Advanced Sockets API for IPv6
+ * <draft-stevens-advanced-api-00.txt>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_IN6_H
+#define _LINUX_IN6_H
+
+#include <linux/types.h>
+
+/*
+ * IPv6 address structure
+ */
+
+struct in6_addr
+{
+ union
+ {
+ __u8 u6_addr8[16];
+ __u16 u6_addr16[8];
+ __u32 u6_addr32[4];
+#if (~0UL) > 0xffffffff
+#ifndef __RELAX_IN6_ADDR_ALIGNMENT
+ /* Alas, protocols do not respect 64bit alignmnet.
+ rsvp/pim/... are broken. However, it is good
+ idea to force correct alignment always, when
+ it is possible.
+ */
+ __u64 u6_addr64[2];
+#endif
+#endif
+ } in6_u;
+#define s6_addr in6_u.u6_addr8
+#define s6_addr16 in6_u.u6_addr16
+#define s6_addr32 in6_u.u6_addr32
+#define s6_addr64 in6_u.u6_addr64
+};
+
+struct sockaddr_in6 {
+ unsigned short int sin6_family; /* AF_INET6 */
+ __u16 sin6_port; /* Transport layer port # */
+ __u32 sin6_flowinfo; /* IPv6 flow information */
+ struct in6_addr sin6_addr; /* IPv6 address */
+};
+
+
+struct ipv6_mreq {
+ /* IPv6 multicast address of group */
+ struct in6_addr ipv6mr_multiaddr;
+
+ /* local IPv6 address of interface */
+ int ipv6mr_ifindex;
+};
+
+struct in6_flowlabel_req
+{
+ struct in6_addr flr_dst;
+ __u32 flr_label;
+ __u8 flr_action;
+ __u8 flr_share;
+ __u16 flr_flags;
+ __u16 flr_expires;
+ __u16 flr_linger;
+ __u32 __flr_pad;
+ /* Options in format of IPV6_PKTOPTIONS */
+};
+
+#define IPV6_FL_A_GET 0
+#define IPV6_FL_A_PUT 1
+#define IPV6_FL_A_RENEW 2
+
+#define IPV6_FL_F_CREATE 1
+#define IPV6_FL_F_EXCL 2
+
+#define IPV6_FL_S_NONE 0
+#define IPV6_FL_S_EXCL 1
+#define IPV6_FL_S_PROCESS 2
+#define IPV6_FL_S_USER 3
+#define IPV6_FL_S_ANY 255
+
+
+/*
+ * Bitmask constant declarations to help applications select out the
+ * flow label and priority fields.
+ *
+ * Note that this are in host byte order while the flowinfo field of
+ * sockaddr_in6 is in network byte order.
+ */
+
+#define IPV6_FLOWINFO_FLOWLABEL 0x000fffff
+#define IPV6_FLOWINFO_PRIORITY 0x0ff00000
+
+/* These defintions are obsolete */
+#define IPV6_PRIORITY_UNCHARACTERIZED 0x0000
+#define IPV6_PRIORITY_FILLER 0x0100
+#define IPV6_PRIORITY_UNATTENDED 0x0200
+#define IPV6_PRIORITY_RESERVED1 0x0300
+#define IPV6_PRIORITY_BULK 0x0400
+#define IPV6_PRIORITY_RESERVED2 0x0500
+#define IPV6_PRIORITY_INTERACTIVE 0x0600
+#define IPV6_PRIORITY_CONTROL 0x0700
+#define IPV6_PRIORITY_8 0x0800
+#define IPV6_PRIORITY_9 0x0900
+#define IPV6_PRIORITY_10 0x0a00
+#define IPV6_PRIORITY_11 0x0b00
+#define IPV6_PRIORITY_12 0x0c00
+#define IPV6_PRIORITY_13 0x0d00
+#define IPV6_PRIORITY_14 0x0e00
+#define IPV6_PRIORITY_15 0x0f00
+
+/*
+ * IPV6 extension headers
+ */
+#define IPPROTO_HOPOPTS 0 /* IPv6 hop-by-hop options */
+#define IPPROTO_ROUTING 43 /* IPv6 routing header */
+#define IPPROTO_FRAGMENT 44 /* IPv6 fragmentation header */
+#define IPPROTO_ICMPV6 58 /* ICMPv6 */
+#define IPPROTO_NONE 59 /* IPv6 no next header */
+#define IPPROTO_DSTOPTS 60 /* IPv6 destination options */
+
+/*
+ * IPv6 TLV options.
+ */
+#define IPV6_TLV_PAD0 0
+#define IPV6_TLV_PADN 1
+#define IPV6_TLV_ROUTERALERT 20
+#define IPV6_TLV_JUMBO 194
+
+/*
+ * IPV6 socket options
+ */
+
+#define IPV6_ADDRFORM 1
+#define IPV6_PKTINFO 2
+#define IPV6_HOPOPTS 3
+#define IPV6_DSTOPTS 4
+#define IPV6_RTHDR 5
+#define IPV6_PKTOPTIONS 6
+#define IPV6_CHECKSUM 7
+#define IPV6_HOPLIMIT 8
+#define IPV6_NEXTHOP 9
+#define IPV6_AUTHHDR 10
+#define IPV6_FLOWINFO 11
+
+#if 0
+/* Aliases for obsolete names */
+#define IPV6_RXHOPOPTS IPV6_HOPOPTS
+#define IPV6_RXDSTOPTS IPV6_DSTOPTS
+#define IPV6_RXSRCRT IPV6_RTHDR
+#endif
+
+/*
+ * Alternative names
+ */
+#define SCM_SRCRT IPV6_RXSRCRT
+
+#define IPV6_UNICAST_HOPS 16
+#define IPV6_MULTICAST_IF 17
+#define IPV6_MULTICAST_HOPS 18
+#define IPV6_MULTICAST_LOOP 19
+#define IPV6_ADD_MEMBERSHIP 20
+#define IPV6_DROP_MEMBERSHIP 21
+#define IPV6_ROUTER_ALERT 22
+#define IPV6_MTU_DISCOVER 23
+#define IPV6_MTU 24
+#define IPV6_RECVERR 25
+#define IPV6_V6ONLY 26
+
+/* IPV6_MTU_DISCOVER values */
+#define IPV6_PMTUDISC_DONT 0
+#define IPV6_PMTUDISC_WANT 1
+#define IPV6_PMTUDISC_DO 2
+
+/* Flowlabel */
+#define IPV6_FLOWLABEL_MGR 32
+#define IPV6_FLOWINFO_SEND 33
+
+
+#endif
diff --git a/pfinet/linux-src/include/linux/in_route.h b/pfinet/linux-src/include/linux/in_route.h
new file mode 100644
index 00000000..61f25c30
--- /dev/null
+++ b/pfinet/linux-src/include/linux/in_route.h
@@ -0,0 +1,32 @@
+#ifndef _LINUX_IN_ROUTE_H
+#define _LINUX_IN_ROUTE_H
+
+/* IPv4 routing cache flags */
+
+#define RTCF_DEAD RTNH_F_DEAD
+#define RTCF_ONLINK RTNH_F_ONLINK
+
+/* Obsolete flag. About to be deleted */
+#define RTCF_NOPMTUDISC RTM_F_NOPMTUDISC
+
+#define RTCF_NOTIFY 0x00010000
+#define RTCF_DIRECTDST 0x00020000
+#define RTCF_REDIRECTED 0x00040000
+#define RTCF_TPROXY 0x00080000
+
+#define RTCF_FAST 0x00200000
+#define RTCF_MASQ 0x00400000
+#define RTCF_SNAT 0x00800000
+#define RTCF_DOREDIRECT 0x01000000
+#define RTCF_DIRECTSRC 0x04000000
+#define RTCF_DNAT 0x08000000
+#define RTCF_BROADCAST 0x10000000
+#define RTCF_MULTICAST 0x20000000
+#define RTCF_REJECT 0x40000000
+#define RTCF_LOCAL 0x80000000
+
+#define RTCF_NAT (RTCF_DNAT|RTCF_SNAT)
+
+#define RT_TOS(tos) ((tos)&IPTOS_TOS_MASK)
+
+#endif /* _LINUX_IN_ROUTE_H */
diff --git a/pfinet/linux-src/include/linux/in_systm.h b/pfinet/linux-src/include/linux/in_systm.h
new file mode 100644
index 00000000..eac9a588
--- /dev/null
+++ b/pfinet/linux-src/include/linux/in_systm.h
@@ -0,0 +1,32 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Miscellaneous internetwork definitions for kernel.
+ *
+ * Version: @(#)in_systm.h 1.0.0 12/17/93
+ *
+ * Authors: Original taken from Berkeley BSD UNIX 4.3-RENO.
+ * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IN_SYSTM_H
+#define _LINUX_IN_SYSTM_H
+
+/*
+ * Network types.
+ * The n_ types are network-order variants of their natural
+ * equivalents. The Linux kernel NET-2 code does not use
+ * them (yet), but it might in the future. This is mostly
+ * there for compatibility with BSD user-level programs.
+ */
+typedef u_short n_short; /* short as received from the net */
+typedef u_long n_long; /* long as received from the net */
+typedef u_long n_time; /* ms since 00:00 GMT, byte rev */
+
+#endif /* _LINUX_IN_SYSTM_H */
diff --git a/pfinet/linux-src/include/linux/inet.h b/pfinet/linux-src/include/linux/inet.h
new file mode 100644
index 00000000..acb93765
--- /dev/null
+++ b/pfinet/linux-src/include/linux/inet.h
@@ -0,0 +1,52 @@
+/*
+ * Swansea University Computer Society NET3
+ *
+ * This work is derived from NET2Debugged, which is in turn derived
+ * from NET2D which was written by:
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This work was derived from Ross Biro's inspirational work
+ * for the LINUX operating system. His version numbers were:
+ *
+ * $Id: Space.c,v 0.8.4.5 1992/12/12 19:25:04 bir7 Exp $
+ * $Id: arp.c,v 0.8.4.6 1993/01/28 22:30:00 bir7 Exp $
+ * $Id: arp.h,v 0.8.4.6 1993/01/28 22:30:00 bir7 Exp $
+ * $Id: dev.c,v 0.8.4.13 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: dev.h,v 0.8.4.7 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: eth.c,v 0.8.4.4 1993/01/22 23:21:38 bir7 Exp $
+ * $Id: eth.h,v 0.8.4.1 1992/11/10 00:17:18 bir7 Exp $
+ * $Id: icmp.c,v 0.8.4.9 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: icmp.h,v 0.8.4.2 1992/11/15 14:55:30 bir7 Exp $
+ * $Id: ip.c,v 0.8.4.8 1992/12/12 19:25:04 bir7 Exp $
+ * $Id: ip.h,v 0.8.4.2 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: loopback.c,v 0.8.4.8 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: packet.c,v 0.8.4.7 1993/01/26 22:04:00 bir7 Exp $
+ * $Id: protocols.c,v 0.8.4.3 1992/11/15 14:55:30 bir7 Exp $
+ * $Id: raw.c,v 0.8.4.12 1993/01/26 22:04:00 bir7 Exp $
+ * $Id: sock.c,v 0.8.4.6 1993/01/28 22:30:00 bir7 Exp $
+ * $Id: sock.h,v 0.8.4.7 1993/01/26 22:04:00 bir7 Exp $
+ * $Id: tcp.c,v 0.8.4.16 1993/01/26 22:04:00 bir7 Exp $
+ * $Id: tcp.h,v 0.8.4.7 1993/01/22 22:58:08 bir7 Exp $
+ * $Id: timer.c,v 0.8.4.8 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: timer.h,v 0.8.4.2 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: udp.c,v 0.8.4.12 1993/01/26 22:04:00 bir7 Exp $
+ * $Id: udp.h,v 0.8.4.1 1992/11/10 00:17:18 bir7 Exp $
+ * $Id: we.c,v 0.8.4.10 1993/01/23 18:00:11 bir7 Exp $
+ * $Id: wereg.h,v 0.8.4.1 1992/11/10 00:17:18 bir7 Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_INET_H
+#define _LINUX_INET_H
+
+#ifdef __KERNEL__
+
+extern void inet_proto_init(struct net_proto *pro);
+extern char *in_ntoa(__u32 in);
+extern __u32 in_aton(const char *str);
+
+#endif
+#endif /* _LINUX_INET_H */
diff --git a/pfinet/linux-src/include/linux/inetdevice.h b/pfinet/linux-src/include/linux/inetdevice.h
new file mode 100644
index 00000000..323a305c
--- /dev/null
+++ b/pfinet/linux-src/include/linux/inetdevice.h
@@ -0,0 +1,128 @@
+#ifndef _LINUX_INETDEVICE_H
+#define _LINUX_INETDEVICE_H
+
+#ifdef __KERNEL__
+
+struct ipv4_devconf
+{
+ int accept_redirects;
+ int send_redirects;
+ int secure_redirects;
+ int shared_media;
+ int accept_source_route;
+ int rp_filter;
+ int proxy_arp;
+ int bootp_relay;
+ int log_martians;
+ int forwarding;
+ int mc_forwarding;
+ int hidden;
+ void *sysctl;
+};
+
+extern struct ipv4_devconf ipv4_devconf;
+
+struct in_device
+{
+ struct device *dev;
+ struct in_ifaddr *ifa_list; /* IP ifaddr chain */
+ struct ip_mc_list *mc_list; /* IP multicast filter chain */
+ unsigned long mr_v1_seen;
+ unsigned flags;
+ struct neigh_parms *arp_parms;
+ struct ipv4_devconf cnf;
+};
+
+#define IN_DEV_FORWARD(in_dev) ((in_dev)->cnf.forwarding)
+#define IN_DEV_MFORWARD(in_dev) (ipv4_devconf.mc_forwarding && (in_dev)->cnf.mc_forwarding)
+#define IN_DEV_RPFILTER(in_dev) (ipv4_devconf.rp_filter && (in_dev)->cnf.rp_filter)
+#define IN_DEV_SOURCE_ROUTE(in_dev) (ipv4_devconf.accept_source_route && (in_dev)->cnf.accept_source_route)
+#define IN_DEV_BOOTP_RELAY(in_dev) (ipv4_devconf.bootp_relay && (in_dev)->cnf.bootp_relay)
+
+#define IN_DEV_LOG_MARTIANS(in_dev) (ipv4_devconf.log_martians || (in_dev)->cnf.log_martians)
+#define IN_DEV_PROXY_ARP(in_dev) (ipv4_devconf.proxy_arp || (in_dev)->cnf.proxy_arp)
+#define IN_DEV_HIDDEN(in_dev) ((in_dev)->cnf.hidden && ipv4_devconf.hidden)
+#define IN_DEV_SHARED_MEDIA(in_dev) (ipv4_devconf.shared_media || (in_dev)->cnf.shared_media)
+#define IN_DEV_TX_REDIRECTS(in_dev) (ipv4_devconf.send_redirects || (in_dev)->cnf.send_redirects)
+#define IN_DEV_SEC_REDIRECTS(in_dev) (ipv4_devconf.secure_redirects || (in_dev)->cnf.secure_redirects)
+
+#define IN_DEV_RX_REDIRECTS(in_dev) \
+ ((IN_DEV_FORWARD(in_dev) && \
+ (ipv4_devconf.accept_redirects && (in_dev)->cnf.accept_redirects)) \
+ || (!IN_DEV_FORWARD(in_dev) && \
+ (ipv4_devconf.accept_redirects || (in_dev)->cnf.accept_redirects)))
+
+struct in_ifaddr
+{
+ struct in_ifaddr *ifa_next;
+ struct in_device *ifa_dev;
+ u32 ifa_local;
+ u32 ifa_address;
+ u32 ifa_mask;
+ u32 ifa_broadcast;
+ u32 ifa_anycast;
+ unsigned char ifa_scope;
+ unsigned char ifa_flags;
+ unsigned char ifa_prefixlen;
+ char ifa_label[IFNAMSIZ];
+};
+
+extern int register_inetaddr_notifier(struct notifier_block *nb);
+extern int unregister_inetaddr_notifier(struct notifier_block *nb);
+
+extern struct device *ip_dev_find(u32 addr);
+extern struct in_ifaddr *inet_addr_onlink(struct in_device *in_dev, u32 a, u32 b);
+extern int devinet_ioctl(unsigned int cmd, void *);
+extern void devinet_init(void);
+extern struct in_device *inetdev_init(struct device *dev);
+extern struct in_device *inetdev_by_index(int);
+extern u32 inet_select_addr(struct device *dev, u32 dst, int scope);
+extern struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, u32 prefix, u32 mask);
+extern void inet_forward_change(void);
+
+extern __inline__ int inet_ifa_match(u32 addr, struct in_ifaddr *ifa)
+{
+ return !((addr^ifa->ifa_address)&ifa->ifa_mask);
+}
+
+/*
+ * Check if a mask is acceptable.
+ */
+
+extern __inline__ int bad_mask(u32 mask, u32 addr)
+{
+ if (addr & (mask = ~mask))
+ return 1;
+ mask = ntohl(mask);
+ if (mask & (mask+1))
+ return 1;
+ return 0;
+}
+
+#define for_primary_ifa(in_dev) { struct in_ifaddr *ifa; \
+ for (ifa = (in_dev)->ifa_list; ifa && !(ifa->ifa_flags&IFA_F_SECONDARY); ifa = ifa->ifa_next)
+
+#define for_ifa(in_dev) { struct in_ifaddr *ifa; \
+ for (ifa = (in_dev)->ifa_list; ifa; ifa = ifa->ifa_next)
+
+
+#define endfor_ifa(in_dev) }
+
+#endif /* __KERNEL__ */
+
+extern __inline__ __u32 inet_make_mask(int logmask)
+{
+ if (logmask)
+ return htonl(~((1<<(32-logmask))-1));
+ return 0;
+}
+
+extern __inline__ int inet_mask_len(__u32 mask)
+{
+ if (!(mask = ntohl(mask)))
+ return 0;
+ return 32 - ffz(~mask);
+}
+
+
+#endif /* _LINUX_INETDEVICE_H */
diff --git a/pfinet/linux-src/include/linux/init.h b/pfinet/linux-src/include/linux/init.h
new file mode 100644
index 00000000..4465f38b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/init.h
@@ -0,0 +1,68 @@
+#ifndef _LINUX_INIT_H
+#define _LINUX_INIT_H
+
+/* These macros are used to mark some functions or
+ * initialized data (doesn't apply to uninitialized data)
+ * as `initialization' functions. The kernel can take this
+ * as hint that the function is used only during the initialization
+ * phase and free up used memory resources after
+ *
+ * Usage:
+ * For functions:
+ *
+ * You should add __init immediately before the function name, like:
+ *
+ * static void __init initme(int x, int y)
+ * {
+ * extern int z; z = x * y;
+ * }
+ *
+ * Depricated: you can surround the whole function declaration
+ * just before function body into __initfunc() macro, like:
+ *
+ * __initfunc (static void initme(int x, int y))
+ * {
+ * extern int z; z = x * y;
+ * }
+ *
+ * If the function has a prototype somewhere, you can also add
+ * __init between closing brace of the prototype and semicolon:
+ *
+ * extern int initialize_foobar_device(int, int, int) __init;
+ *
+ * For initialized data:
+ * You should insert __initdata between the variable name and equal
+ * sign followed by value, e.g.:
+ *
+ * static int init_variable __initdata = 0;
+ * static char linux_logo[] __initdata = { 0x32, 0x36, ... };
+ *
+ * For initialized data not at file scope, i.e. within a function,
+ * you should use __initlocaldata instead, due to a bug in GCC 2.7.
+ */
+
+/*
+ * Disable the __initfunc macros if a file that is a part of a
+ * module attempts to use them. We do not want to interfere
+ * with module linking.
+ */
+
+#ifndef MODULE
+#include <asm/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(__arginit) __arginit
+/* For assembly routines */
+#define __INIT
+#define __FINIT
+#define __INITDATA
+#endif
+
+#if __GNUC__ >= 2 && __GNUC_MINOR__ >= 8
+#define __initlocaldata __initdata
+#else
+#define __initlocaldata
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/interrupt.h b/pfinet/linux-src/include/linux/interrupt.h
new file mode 100644
index 00000000..3e3edd8b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/interrupt.h
@@ -0,0 +1,84 @@
+/* interrupt.h */
+#ifndef _LINUX_INTERRUPT_H
+#define _LINUX_INTERRUPT_H
+
+#include <linux/kernel.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
+
+struct irqaction {
+ void (*handler)(int, void *, struct pt_regs *);
+ unsigned long flags;
+ unsigned long mask;
+ const char *name;
+ void *dev_id;
+ struct irqaction *next;
+};
+
+extern volatile unsigned char bh_running;
+
+extern atomic_t bh_mask_count[32];
+extern unsigned long bh_active;
+extern unsigned long bh_mask;
+extern void (*bh_base[32])(void);
+
+asmlinkage void do_bottom_half(void);
+
+/* Who gets which entry in bh_base. Things which will occur most often
+ should come first - in which case NET should be up the top with SERIAL/TQUEUE! */
+
+enum {
+ TIMER_BH = 0,
+ CONSOLE_BH,
+ TQUEUE_BH,
+ DIGI_BH,
+ SERIAL_BH,
+ RISCOM8_BH,
+ SPECIALIX_BH,
+ AURORA_BH,
+ ESP_BH,
+ NET_BH,
+ SCSI_BH,
+ IMMEDIATE_BH,
+ KEYBOARD_BH,
+ CYCLADES_BH,
+ CM206_BH,
+ JS_BH,
+ MACSERIAL_BH,
+ ISICOM_BH
+};
+
+#include <asm/hardirq.h>
+#include <asm/softirq.h>
+
+/*
+ * Autoprobing for irqs:
+ *
+ * probe_irq_on() and probe_irq_off() provide robust primitives
+ * for accurate IRQ probing during kernel initialization. They are
+ * reasonably simple to use, are not "fooled" by spurious interrupts,
+ * and, unlike other attempts at IRQ probing, they do not get hung on
+ * stuck interrupts (such as unused PS2 mouse interfaces on ASUS boards).
+ *
+ * For reasonably foolproof probing, use them as follows:
+ *
+ * 1. clear and/or mask the device's internal interrupt.
+ * 2. sti();
+ * 3. irqs = probe_irq_on(); // "take over" all unassigned idle IRQs
+ * 4. enable the device and cause it to trigger an interrupt.
+ * 5. wait for the device to interrupt, using non-intrusive polling or a delay.
+ * 6. irq = probe_irq_off(irqs); // get IRQ number, 0=none, negative=multiple
+ * 7. service the device to clear its pending interrupt.
+ * 8. loop again if paranoia is required.
+ *
+ * probe_irq_on() returns a mask of allocated irq's.
+ *
+ * probe_irq_off() takes the mask as a parameter,
+ * and returns the irq number which occurred,
+ * or zero if none occurred, or a negative irq number
+ * if more than one irq occurred.
+ */
+extern unsigned long probe_irq_on(void); /* returns 0 on failure */
+extern int probe_irq_off(unsigned long); /* returns 0 or negative on failure */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/ioctl.h b/pfinet/linux-src/include/linux/ioctl.h
new file mode 100644
index 00000000..7e55c361
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ioctl.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_IOCTL_H
+#define _LINUX_IOCTL_H
+
+#include <asm/ioctl.h>
+
+#endif /* _LINUX_IOCTL_H */
diff --git a/pfinet/linux-src/include/linux/ioport.h b/pfinet/linux-src/include/linux/ioport.h
new file mode 100644
index 00000000..b5eef44d
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ioport.h
@@ -0,0 +1,35 @@
+/*
+ * portio.h Definitions of routines for detecting, reserving and
+ * allocating system resources.
+ *
+ * Version: 0.01 8/30/93
+ *
+ * Author: Donald Becker (becker@super.org)
+ */
+
+#ifndef _LINUX_PORTIO_H
+#define _LINUX_PORTIO_H
+
+#define HAVE_PORTRESERVE
+/*
+ * Call check_region() before probing for your hardware.
+ * Once you have found you hardware, register it with request_region().
+ * If you unload the driver, use release_region to free ports.
+ */
+extern void reserve_setup(char *str, int *ints);
+extern int check_region(unsigned long from, unsigned long extent);
+extern void request_region(unsigned long from, unsigned long extent,const char *name);
+extern void release_region(unsigned long from, unsigned long extent);
+extern int get_ioport_list(char *);
+
+#ifdef __sparc__
+extern unsigned long occupy_region(unsigned long base, unsigned long end,
+ unsigned long num, unsigned int align,
+ const char *name);
+#endif
+
+#define HAVE_AUTOIRQ
+extern void autoirq_setup(int waittime);
+extern int autoirq_report(int waittime);
+
+#endif /* _LINUX_PORTIO_H */
diff --git a/pfinet/linux-src/include/linux/ip.h b/pfinet/linux-src/include/linux/ip.h
new file mode 100644
index 00000000..7b642728
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ip.h
@@ -0,0 +1,138 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the IP protocol.
+ *
+ * Version: @(#)ip.h 1.0.2 04/28/93
+ *
+ * Authors: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IP_H
+#define _LINUX_IP_H
+#include <asm/byteorder.h>
+
+/* SOL_IP socket options */
+
+#define IPTOS_TOS_MASK 0x1E
+#define IPTOS_TOS(tos) ((tos)&IPTOS_TOS_MASK)
+#define IPTOS_LOWDELAY 0x10
+#define IPTOS_THROUGHPUT 0x08
+#define IPTOS_RELIABILITY 0x04
+#define IPTOS_MINCOST 0x02
+
+#define IPTOS_PREC_MASK 0xE0
+#define IPTOS_PREC(tos) ((tos)&IPTOS_PREC_MASK)
+#define IPTOS_PREC_NETCONTROL 0xe0
+#define IPTOS_PREC_INTERNETCONTROL 0xc0
+#define IPTOS_PREC_CRITIC_ECP 0xa0
+#define IPTOS_PREC_FLASHOVERRIDE 0x80
+#define IPTOS_PREC_FLASH 0x60
+#define IPTOS_PREC_IMMEDIATE 0x40
+#define IPTOS_PREC_PRIORITY 0x20
+#define IPTOS_PREC_ROUTINE 0x00
+
+
+/* IP options */
+#define IPOPT_COPY 0x80
+#define IPOPT_CLASS_MASK 0x60
+#define IPOPT_NUMBER_MASK 0x1f
+
+#define IPOPT_COPIED(o) ((o)&IPOPT_COPY)
+#define IPOPT_CLASS(o) ((o)&IPOPT_CLASS_MASK)
+#define IPOPT_NUMBER(o) ((o)&IPOPT_NUMBER_MASK)
+
+#define IPOPT_CONTROL 0x00
+#define IPOPT_RESERVED1 0x20
+#define IPOPT_MEASUREMENT 0x40
+#define IPOPT_RESERVED2 0x60
+
+#define IPOPT_END (0 |IPOPT_CONTROL)
+#define IPOPT_NOOP (1 |IPOPT_CONTROL)
+#define IPOPT_SEC (2 |IPOPT_CONTROL|IPOPT_COPY)
+#define IPOPT_LSRR (3 |IPOPT_CONTROL|IPOPT_COPY)
+#define IPOPT_TIMESTAMP (4 |IPOPT_MEASUREMENT)
+#define IPOPT_RR (7 |IPOPT_CONTROL)
+#define IPOPT_SID (8 |IPOPT_CONTROL|IPOPT_COPY)
+#define IPOPT_SSRR (9 |IPOPT_CONTROL|IPOPT_COPY)
+#define IPOPT_RA (20|IPOPT_CONTROL|IPOPT_COPY)
+
+#define IPVERSION 4
+#define MAXTTL 255
+#define IPDEFTTL 64
+
+/* struct timestamp, struct route and MAX_ROUTES are removed.
+
+ REASONS: it is clear that nobody used them because:
+ - MAX_ROUTES value was wrong.
+ - "struct route" was wrong.
+ - "struct timestamp" had fatally misaligned bitfields and was completely unusable.
+ */
+
+#define IPOPT_OPTVAL 0
+#define IPOPT_OLEN 1
+#define IPOPT_OFFSET 2
+#define IPOPT_MINOFF 4
+#define MAX_IPOPTLEN 40
+#define IPOPT_NOP IPOPT_NOOP
+#define IPOPT_EOL IPOPT_END
+#define IPOPT_TS IPOPT_TIMESTAMP
+
+#define IPOPT_TS_TSONLY 0 /* timestamps only */
+#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */
+#define IPOPT_TS_PRESPEC 3 /* specified modules only */
+
+#ifdef __KERNEL__
+
+struct ip_options {
+ __u32 faddr; /* Saved first hop address */
+ unsigned char optlen;
+ unsigned char srr;
+ unsigned char rr;
+ unsigned char ts;
+ unsigned char is_setbyuser:1, /* Set by setsockopt? */
+ is_data:1, /* Options in __data, rather than skb */
+ is_strictroute:1, /* Strict source route */
+ srr_is_hit:1, /* Packet destination addr was our one */
+ is_changed:1, /* IP checksum more not valid */
+ rr_needaddr:1, /* Need to record addr of outgoing dev */
+ ts_needtime:1, /* Need to record timestamp */
+ ts_needaddr:1; /* Need to record addr of outgoing dev */
+ unsigned char router_alert;
+ unsigned char __pad1;
+ unsigned char __pad2;
+ unsigned char __data[0];
+};
+
+#define optlength(opt) (sizeof(struct ip_options) + opt->optlen)
+#endif
+
+struct iphdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 ihl:4,
+ version:4;
+#elif defined (__BIG_ENDIAN_BITFIELD)
+ __u8 version:4,
+ ihl:4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ __u8 tos;
+ __u16 tot_len;
+ __u16 id;
+ __u16 frag_off;
+ __u8 ttl;
+ __u8 protocol;
+ __u16 check;
+ __u32 saddr;
+ __u32 daddr;
+ /*The options start here. */
+};
+
+#endif /* _LINUX_IP_H */
diff --git a/pfinet/linux-src/include/linux/ip_fw.h b/pfinet/linux-src/include/linux/ip_fw.h
new file mode 100644
index 00000000..f36ec7e6
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ip_fw.h
@@ -0,0 +1,193 @@
+/*
+ * This code is heavily based on the code in ip_fw.h; see that file for
+ * copyrights and attributions. This code is basically GPL.
+ *
+ * 15-Feb-1997: Major changes to allow graphs for firewall rules.
+ * Paul Russell <Paul.Russell@rustcorp.com.au> and
+ * Michael Neuling <Michael.Neuling@rustcorp.com.au>
+ * 2-Nov-1997: Changed types to __u16, etc.
+ * Removed IP_FW_F_TCPACK & IP_FW_F_BIDIR.
+ * Added inverse flags field.
+ * Removed multiple port specs.
+ */
+
+/*
+ * Format of an IP firewall descriptor
+ *
+ * src, dst, src_mask, dst_mask are always stored in network byte order.
+ * flags are stored in host byte order (of course).
+ * Port numbers are stored in HOST byte order.
+ */
+
+#ifndef _IP_FWCHAINS_H
+#define _IP_FWCHAINS_H
+
+#ifdef __KERNEL__
+#include <linux/icmp.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#endif /* __KERNEL__ */
+#define IP_FW_MAX_LABEL_LENGTH 8
+typedef char ip_chainlabel[IP_FW_MAX_LABEL_LENGTH+1];
+
+struct ip_fw
+{
+ struct in_addr fw_src, fw_dst; /* Source and destination IP addr */
+ struct in_addr fw_smsk, fw_dmsk; /* Mask for src and dest IP addr */
+ __u32 fw_mark; /* ID to stamp on packet */
+ __u16 fw_proto; /* Protocol, 0 = ANY */
+ __u16 fw_flg; /* Flags word */
+ __u16 fw_invflg; /* Inverse flags */
+ __u16 fw_spts[2]; /* Source port range. */
+ __u16 fw_dpts[2]; /* Destination port range. */
+ __u16 fw_redirpt; /* Port to redirect to. */
+ __u16 fw_outputsize; /* Max amount to output to
+ NETLINK */
+ char fw_vianame[IFNAMSIZ]; /* name of interface "via" */
+ __u8 fw_tosand, fw_tosxor; /* Revised packet priority */
+};
+
+struct ip_fwuser
+{
+ struct ip_fw ipfw;
+ ip_chainlabel label;
+};
+
+/* Values for "fw_flg" field . */
+#define IP_FW_F_PRN 0x0001 /* Print packet if it matches */
+#define IP_FW_F_TCPSYN 0x0002 /* For tcp packets-check SYN only */
+#define IP_FW_F_FRAG 0x0004 /* Set if rule is a fragment rule */
+#define IP_FW_F_MARKABS 0x0008 /* Set the mark to fw_mark, not add. */
+#define IP_FW_F_WILDIF 0x0010 /* Need only match start of interface name. */
+#define IP_FW_F_NETLINK 0x0020 /* Redirect to netlink: 2.1.x only */
+#define IP_FW_F_MASK 0x003F /* All possible flag bits mask */
+
+/* Values for "fw_invflg" field. */
+#define IP_FW_INV_SRCIP 0x0001 /* Invert the sense of fw_src. */
+#define IP_FW_INV_DSTIP 0x0002 /* Invert the sense of fw_dst. */
+#define IP_FW_INV_PROTO 0x0004 /* Invert the sense of fw_proto. */
+#define IP_FW_INV_SRCPT 0x0008 /* Invert the sense of source ports. */
+#define IP_FW_INV_DSTPT 0x0010 /* Invert the sense of destination ports. */
+#define IP_FW_INV_VIA 0x0020 /* Invert the sense of fw_vianame. */
+#define IP_FW_INV_SYN 0x0040 /* Invert the sense of IP_FW_F_TCPSYN. */
+#define IP_FW_INV_FRAG 0x0080 /* Invert the sense of IP_FW_F_FRAG. */
+
+/*
+ * New IP firewall options for [gs]etsockopt at the RAW IP level.
+ * Unlike BSD Linux inherits IP options so you don't have to use
+ * a raw socket for this. Instead we check rights in the calls. */
+
+#define IP_FW_BASE_CTL 64 /* base for firewall socket options */
+
+#define IP_FW_APPEND (IP_FW_BASE_CTL) /* Takes ip_fwchange */
+#define IP_FW_REPLACE (IP_FW_BASE_CTL+1) /* Takes ip_fwnew */
+#define IP_FW_DELETE_NUM (IP_FW_BASE_CTL+2) /* Takes ip_fwdelnum */
+#define IP_FW_DELETE (IP_FW_BASE_CTL+3) /* Takes ip_fwchange */
+#define IP_FW_INSERT (IP_FW_BASE_CTL+4) /* Takes ip_fwnew */
+#define IP_FW_FLUSH (IP_FW_BASE_CTL+5) /* Takes ip_chainlabel */
+#define IP_FW_ZERO (IP_FW_BASE_CTL+6) /* Takes ip_chainlabel */
+#define IP_FW_CHECK (IP_FW_BASE_CTL+7) /* Takes ip_fwtest */
+#define IP_FW_MASQ_TIMEOUTS (IP_FW_BASE_CTL+8) /* Takes 3 ints */
+#define IP_FW_CREATECHAIN (IP_FW_BASE_CTL+9) /* Takes ip_chainlabel */
+#define IP_FW_DELETECHAIN (IP_FW_BASE_CTL+10) /* Takes ip_chainlabel */
+#define IP_FW_POLICY (IP_FW_BASE_CTL+11) /* Takes ip_fwpolicy */
+/* Masquerade control, only 1 optname */
+
+#define IP_FW_MASQ_CTL (IP_FW_BASE_CTL+12) /* General ip_masq ctl */
+
+/* Builtin chain labels */
+#define IP_FW_LABEL_FORWARD "forward"
+#define IP_FW_LABEL_INPUT "input"
+#define IP_FW_LABEL_OUTPUT "output"
+
+/* Special targets */
+#define IP_FW_LABEL_MASQUERADE "MASQ"
+#define IP_FW_LABEL_REDIRECT "REDIRECT"
+#define IP_FW_LABEL_ACCEPT "ACCEPT"
+#define IP_FW_LABEL_BLOCK "DENY"
+#define IP_FW_LABEL_REJECT "REJECT"
+#define IP_FW_LABEL_RETURN "RETURN"
+#define IP_FW_LABEL_QUEUE "QUEUE"
+
+/* Files in /proc/net */
+#define IP_FW_PROC_CHAINS "ip_fwchains"
+#define IP_FW_PROC_CHAIN_NAMES "ip_fwnames"
+
+
+struct ip_fwpkt
+{
+ struct iphdr fwp_iph; /* IP header */
+ union {
+ struct tcphdr fwp_tcph; /* TCP header or */
+ struct udphdr fwp_udph; /* UDP header */
+ struct icmphdr fwp_icmph; /* ICMP header */
+ } fwp_protoh;
+ struct in_addr fwp_via; /* interface address */
+ char fwp_vianame[IFNAMSIZ]; /* interface name */
+};
+
+/* The argument to IP_FW_DELETE and IP_FW_APPEND */
+struct ip_fwchange
+{
+ struct ip_fwuser fwc_rule;
+ ip_chainlabel fwc_label;
+};
+
+/* The argument to IP_FW_CHECK. */
+struct ip_fwtest
+{
+ struct ip_fwpkt fwt_packet; /* Packet to be tested */
+ ip_chainlabel fwt_label; /* Block to start test in */
+};
+
+/* The argument to IP_FW_DELETE_NUM */
+struct ip_fwdelnum
+{
+ __u32 fwd_rulenum;
+ ip_chainlabel fwd_label;
+};
+
+/* The argument to IP_FW_REPLACE and IP_FW_INSERT */
+struct ip_fwnew
+{
+ __u32 fwn_rulenum;
+ struct ip_fwuser fwn_rule;
+ ip_chainlabel fwn_label;
+};
+
+/* The argument to IP_FW_POLICY */
+struct ip_fwpolicy
+{
+ ip_chainlabel fwp_policy;
+ ip_chainlabel fwp_label;
+};
+/*
+ * timeouts for ip masquerading
+ */
+
+extern int ip_fw_masq_timeouts(void *, int);
+
+
+/*
+ * Main firewall chains definitions and global var's definitions.
+ */
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+#include <linux/init.h>
+extern void ip_fw_init(void) __init;
+#else /* 2.0.x */
+extern void ip_fw_init(void);
+#endif /* 2.1.x */
+extern int ip_fw_ctl(int, void *, int);
+#ifdef CONFIG_IP_MASQUERADE
+extern int ip_masq_uctl(int, char *, int);
+#endif
+#endif /* KERNEL */
+
+#endif /* _IP_FWCHAINS_H */
diff --git a/pfinet/linux-src/include/linux/ip_masq.h b/pfinet/linux-src/include/linux/ip_masq.h
new file mode 100644
index 00000000..ba893138
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ip_masq.h
@@ -0,0 +1,140 @@
+/*
+ * IP_MASQ user space control interface
+ * $Id: ip_masq.h,v 1.2 1998/12/08 05:41:48 davem Exp $
+ */
+
+#ifndef _LINUX_IP_MASQ_H
+#define _LINUX_IP_MASQ_H
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/stddef.h>
+#else
+#include <sys/types.h>
+#include <stddef.h>
+#endif
+
+struct ip_masq_user {
+ int protocol;
+ u_int16_t sport, dport, mport;
+ u_int32_t saddr, daddr, maddr;
+ u_int32_t rt_daddr; /* dst address to use for rt query */
+ u_int32_t rt_saddr;
+ u_int32_t ip_tos; /* TOS */
+ unsigned timeout; /* in ticks (HZ per sec) */
+ unsigned flags;
+ int fd; /* NOT IMPL: attach tunnel to this fd */
+ int state; /* NOT IMPL: return conn state */
+};
+
+#define IP_MASQ_USER_F_LISTEN 0x01 /* put entry to LISTEN state */
+#define IP_MASQ_USER_F_DEAD 0x02 /* mark as DEAD */
+#define IP_MASQ_USER_F_FORCE 0x04 /* force operation */
+
+struct ip_masq_timeout {
+ int protocol;
+ union {
+ struct {
+ unsigned established;
+ unsigned syn_sent;
+ unsigned syn_recv;
+ unsigned fin_wait;
+ unsigned time_wait;
+ unsigned close;
+ unsigned close_wait;
+ unsigned last_ack;
+ unsigned listen;
+ } tcp;
+ unsigned udp;
+ unsigned icmp;
+ } u;
+};
+
+/*
+ * AUTOFW stuff
+ */
+#define IP_FWD_RANGE 1
+#define IP_FWD_PORT 2
+#define IP_FWD_DIRECT 3
+
+#define IP_AUTOFW_ACTIVE 1
+#define IP_AUTOFW_USETIME 2
+#define IP_AUTOFW_SECURE 4
+
+
+/* WARNING: bitwise equal to ip_autofw in net/ip_autofw.h */
+struct ip_autofw_user {
+ void * next;
+ u_int16_t type;
+ u_int16_t low;
+ u_int16_t hidden;
+ u_int16_t high;
+ u_int16_t visible;
+ u_int16_t protocol;
+ u_int32_t lastcontact;
+ u_int32_t where;
+ u_int16_t ctlproto;
+ u_int16_t ctlport;
+ u_int16_t flags;
+ /* struct timer_list timer; */
+};
+
+/*
+ * PORTFW stuff
+ */
+struct ip_portfw_user {
+ u_int16_t protocol; /* Which protocol are we talking? */
+ u_int32_t laddr, raddr; /* Remote address */
+ u_int16_t lport, rport; /* Local and remote port */
+ int pref; /* Preference value */
+};
+
+/*
+ * MFW stuff
+ */
+struct ip_mfw_user {
+ u_int32_t fwmark; /* Firewalling mark */
+ u_int32_t raddr; /* remote port */
+ u_int16_t rport; /* remote port */
+ u_int16_t dummy; /* Make up to multiple of 4 */
+ int pref; /* Preference value */
+ unsigned flags; /* misc flags */
+};
+
+#define IP_MASQ_MFW_SCHED 0x01
+
+#define IP_FW_MASQCTL_MAX 256
+#define IP_MASQ_TNAME_MAX 32
+
+struct ip_masq_ctl {
+ int m_target;
+ int m_cmd;
+ char m_tname[IP_MASQ_TNAME_MAX];
+ union {
+ struct ip_portfw_user portfw_user;
+ struct ip_autofw_user autofw_user;
+ struct ip_mfw_user mfw_user;
+ struct ip_masq_user user;
+ unsigned char m_raw[IP_FW_MASQCTL_MAX];
+ } u;
+};
+
+#define IP_MASQ_CTL_BSIZE (offsetof (struct ip_masq_ctl,u))
+
+#define IP_MASQ_TARGET_CORE 1
+#define IP_MASQ_TARGET_MOD 2 /* masq_mod is selected by "name" */
+#define IP_MASQ_TARGET_USER 3
+#define IP_MASQ_TARGET_LAST 4
+
+#define IP_MASQ_CMD_NONE 0 /* just peek */
+#define IP_MASQ_CMD_INSERT 1
+#define IP_MASQ_CMD_ADD 2
+#define IP_MASQ_CMD_SET 3
+#define IP_MASQ_CMD_DEL 4
+#define IP_MASQ_CMD_GET 5
+#define IP_MASQ_CMD_FLUSH 6
+#define IP_MASQ_CMD_LIST 7 /* actually fake: done via /proc */
+#define IP_MASQ_CMD_ENABLE 8
+#define IP_MASQ_CMD_DISABLE 9
+
+#endif /* _LINUX_IP_MASQ_H */
diff --git a/pfinet/linux-src/include/linux/ipc.h b/pfinet/linux-src/include/linux/ipc.h
new file mode 100644
index 00000000..35e4f2b2
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ipc.h
@@ -0,0 +1,47 @@
+#ifndef _LINUX_IPC_H
+#define _LINUX_IPC_H
+
+#include <linux/types.h>
+
+#define IPC_PRIVATE ((__kernel_key_t) 0)
+
+struct ipc_perm
+{
+ __kernel_key_t key;
+ __kernel_uid_t uid;
+ __kernel_gid_t gid;
+ __kernel_uid_t cuid;
+ __kernel_gid_t cgid;
+ __kernel_mode_t mode;
+ unsigned short seq;
+};
+
+/* resource get request flags */
+#define IPC_CREAT 00001000 /* create if key is nonexistent */
+#define IPC_EXCL 00002000 /* fail if key exists */
+#define IPC_NOWAIT 00004000 /* return error on wait */
+
+/* these fields are used by the DIPC package so the kernel as standard
+ should avoid using them if possible */
+
+#define IPC_DIPC 00010000 /* make it distributed */
+#define IPC_OWN 00020000 /* this machine is the DIPC owner */
+
+/*
+ * Control commands used with semctl, msgctl and shmctl
+ * see also specific commands in sem.h, msg.h and shm.h
+ */
+#define IPC_RMID 0 /* remove resource */
+#define IPC_SET 1 /* set ipc_perm options */
+#define IPC_STAT 2 /* get ipc_perm options */
+#define IPC_INFO 3 /* see ipcs */
+
+#ifdef __KERNEL__
+
+/* special shmsegs[id], msgque[id] or semary[id] values */
+#define IPC_UNUSED ((void *) -1)
+#define IPC_NOID ((void *) -2) /* being allocated/destroyed */
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_IPC_H */
diff --git a/pfinet/linux-src/include/linux/ipsec.h b/pfinet/linux-src/include/linux/ipsec.h
new file mode 100644
index 00000000..b9d7bcc6
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ipsec.h
@@ -0,0 +1,69 @@
+/*
+ * Definitions for the SECurity layer
+ *
+ * Author:
+ * Robert Muchsel <muchsel@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_IPSEC_H
+#define _LINUX_IPSEC_H
+
+#include <linux/config.h>
+#include <linux/socket.h>
+#include <net/sock.h>
+#include <linux/skbuff.h>
+
+/* Values for the set/getsockopt calls */
+
+/* These defines are compatible with NRL IPv6, however their semantics
+ is different */
+
+#define IPSEC_LEVEL_NONE -1 /* send plaintext, accept any */
+#define IPSEC_LEVEL_DEFAULT 0 /* encrypt/authenticate if possible */
+ /* the default MUST be 0, because a */
+ /* socket is initialized with 0's */
+#define IPSEC_LEVEL_USE 1 /* use outbound, don't require inbound */
+#define IPSEC_LEVEL_REQUIRE 2 /* require both directions */
+#define IPSEC_LEVEL_UNIQUE 2 /* for compatibility only */
+
+#ifdef __KERNEL__
+
+/* skb bit flags set on packet input processing */
+
+#define RCV_SEC 0x0f /* options on receive */
+#define RCV_AUTH 0x01 /* was authenticated */
+#define RCV_CRYPT 0x02 /* was encrypted */
+#define RCV_TUNNEL 0x04 /* was tunneled */
+#define SND_SEC 0xf0 /* options on send, these are */
+#define SND_AUTH 0x10 /* currently unused */
+#define SND_CRYPT 0x20
+#define SND_TUNNEL 0x40
+
+/*
+ * FIXME: ignores network encryption for now..
+ */
+
+#ifdef CONFIG_NET_SECURITY
+extern __inline__ int ipsec_sk_policy(struct sock *sk, struct sk_buff *skb)
+{
+ return ((sk->authentication < IPSEC_LEVEL_REQUIRE) ||
+ (skb->security & RCV_AUTH)) &&
+ ((sk->encryption < IPSEC_LEVEL_REQUIRE) ||
+ (skb->security & RCV_CRYPT));
+}
+
+#else
+
+extern __inline__ int ipsec_sk_policy(struct sock *sk, struct sk_buff *skb)
+{
+ return 1;
+}
+#endif /* CONFIG */
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_IPSEC_H */
diff --git a/pfinet/linux-src/include/linux/ipv6.h b/pfinet/linux-src/include/linux/ipv6.h
new file mode 100644
index 00000000..299db1a5
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ipv6.h
@@ -0,0 +1,132 @@
+#ifndef _IPV6_H
+#define _IPV6_H
+
+#include <linux/config.h>
+#include <linux/in6.h>
+#include <asm/byteorder.h>
+
+/* The latest drafts declared increase in minimal mtu up to 1280. */
+
+#define IPV6_MIN_MTU 1280
+
+/*
+ * Advanced API
+ * source interface/address selection, source routing, etc...
+ * *under construction*
+ */
+
+
+struct in6_pktinfo {
+ struct in6_addr ipi6_addr;
+ int ipi6_ifindex;
+};
+
+
+struct in6_ifreq {
+ struct in6_addr ifr6_addr;
+ __u32 ifr6_prefixlen;
+ int ifr6_ifindex;
+};
+
+#define IPV6_SRCRT_STRICT 0x01 /* this hop must be a neighbor */
+#define IPV6_SRCRT_TYPE_0 0 /* IPv6 type 0 Routing Header */
+
+/*
+ * routing header
+ */
+struct ipv6_rt_hdr {
+ __u8 nexthdr;
+ __u8 hdrlen;
+ __u8 type;
+ __u8 segments_left;
+
+ /*
+ * type specific data
+ * variable length field
+ */
+};
+
+
+struct ipv6_opt_hdr {
+ __u8 nexthdr;
+ __u8 hdrlen;
+ /*
+ * TLV encoded option data follows.
+ */
+};
+
+#define ipv6_destopt_hdr ipv6_opt_hdr
+#define ipv6_hopopt_hdr ipv6_opt_hdr
+
+#ifdef __KERNEL__
+#define ipv6_optlen(p) (((p)->hdrlen+1) << 3)
+#endif
+
+/*
+ * routing header type 0 (used in cmsghdr struct)
+ */
+
+struct rt0_hdr {
+ struct ipv6_rt_hdr rt_hdr;
+ __u32 bitmap; /* strict/loose bit map */
+ struct in6_addr addr[0];
+
+#define rt0_type rt_hdr.type;
+};
+
+/*
+ * IPv6 fixed header
+ *
+ * BEWARE, it is incorrect. The first 4 bits of flow_lbl
+ * are glued to priority now, forming "class".
+ */
+
+struct ipv6hdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 priority:4,
+ version:4;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u8 version:4,
+ priority:4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ __u8 flow_lbl[3];
+
+ __u16 payload_len;
+ __u8 nexthdr;
+ __u8 hop_limit;
+
+ struct in6_addr saddr;
+ struct in6_addr daddr;
+};
+
+#ifdef __KERNEL__
+
+/*
+ This structure contains results of exthdrs parsing
+ as offsets from skb->nh.
+ */
+
+struct inet6_skb_parm
+{
+ int iif;
+ __u16 ra;
+ __u16 hop;
+ __u16 auth;
+ __u16 dst0;
+ __u16 srcrt;
+ __u16 dst1;
+};
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#define __ipv6_only_sock(sk) (sk->net_pinfo.af_inet6.ipv6only)
+#define ipv6_only_sock(sk) ((sk)->family == PF_INET6 && __ipv6_only_sock(sk))
+#else
+#define __ipv6_only_sock(sk) 0
+#define ipv6_only_sock(sk) 0
+#endif
+
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/ipv6_route.h b/pfinet/linux-src/include/linux/ipv6_route.h
new file mode 100644
index 00000000..a4861d05
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ipv6_route.h
@@ -0,0 +1,56 @@
+/*
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_IPV6_ROUTE_H
+#define _LINUX_IPV6_ROUTE_H
+
+enum
+{
+ RTA_IPV6_UNSPEC,
+ RTA_IPV6_HOPLIMIT,
+};
+
+#define RTA_IPV6_MAX RTA_IPV6_HOPLIMIT
+
+
+#define RTF_DEFAULT 0x00010000 /* default - learned via ND */
+#define RTF_ALLONLINK 0x00020000 /* fallback, no routers on link */
+#define RTF_ADDRCONF 0x00040000 /* addrconf route - RA */
+
+#define RTF_NONEXTHOP 0x00200000 /* route with no nexthop */
+#define RTF_EXPIRES 0x00400000
+
+#define RTF_CACHE 0x01000000 /* cache entry */
+#define RTF_FLOW 0x02000000 /* flow significant route */
+#define RTF_POLICY 0x04000000 /* policy route */
+
+#define RTF_LOCAL 0x80000000
+
+struct in6_rtmsg {
+ struct in6_addr rtmsg_dst;
+ struct in6_addr rtmsg_src;
+ struct in6_addr rtmsg_gateway;
+ __u32 rtmsg_type;
+ __u16 rtmsg_dst_len;
+ __u16 rtmsg_src_len;
+ __u32 rtmsg_metric;
+ unsigned long rtmsg_info;
+ __u32 rtmsg_flags;
+ int rtmsg_ifindex;
+};
+
+#define RTMSG_NEWDEVICE 0x11
+#define RTMSG_DELDEVICE 0x12
+#define RTMSG_NEWROUTE 0x21
+#define RTMSG_DELROUTE 0x22
+
+#endif
diff --git a/pfinet/linux-src/include/linux/ipx.h b/pfinet/linux-src/include/linux/ipx.h
new file mode 100644
index 00000000..8b9d6bb1
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ipx.h
@@ -0,0 +1,89 @@
+#ifndef _IPX_H_
+#define _IPX_H_
+#include <linux/sockios.h>
+#include <linux/socket.h>
+#define IPX_NODE_LEN 6
+#define IPX_MTU 576
+
+struct sockaddr_ipx
+{
+ sa_family_t sipx_family;
+ __u16 sipx_port;
+ __u32 sipx_network;
+ unsigned char sipx_node[IPX_NODE_LEN];
+ __u8 sipx_type;
+ unsigned char sipx_zero; /* 16 byte fill */
+};
+
+/*
+ * So we can fit the extra info for SIOCSIFADDR into the address nicely
+ */
+
+#define sipx_special sipx_port
+#define sipx_action sipx_zero
+#define IPX_DLTITF 0
+#define IPX_CRTITF 1
+
+typedef struct ipx_route_definition
+{
+ __u32 ipx_network;
+ __u32 ipx_router_network;
+ unsigned char ipx_router_node[IPX_NODE_LEN];
+} ipx_route_definition;
+
+typedef struct ipx_interface_definition
+{
+ __u32 ipx_network;
+ unsigned char ipx_device[16];
+ unsigned char ipx_dlink_type;
+#define IPX_FRAME_NONE 0
+#define IPX_FRAME_SNAP 1
+#define IPX_FRAME_8022 2
+#define IPX_FRAME_ETHERII 3
+#define IPX_FRAME_8023 4
+#define IPX_FRAME_TR_8022 5 /* obsolete */
+ unsigned char ipx_special;
+#define IPX_SPECIAL_NONE 0
+#define IPX_PRIMARY 1
+#define IPX_INTERNAL 2
+ unsigned char ipx_node[IPX_NODE_LEN];
+} ipx_interface_definition;
+
+typedef struct ipx_config_data
+{
+ unsigned char ipxcfg_auto_select_primary;
+ unsigned char ipxcfg_auto_create_interfaces;
+} ipx_config_data;
+
+/*
+ * OLD Route Definition for backward compatibility.
+ */
+
+struct ipx_route_def
+{
+ __u32 ipx_network;
+ __u32 ipx_router_network;
+#define IPX_ROUTE_NO_ROUTER 0
+ unsigned char ipx_router_node[IPX_NODE_LEN];
+ unsigned char ipx_device[16];
+ unsigned short ipx_flags;
+#define IPX_RT_SNAP 8
+#define IPX_RT_8022 4
+#define IPX_RT_BLUEBOOK 2
+#define IPX_RT_ROUTED 1
+};
+
+#define SIOCAIPXITFCRT (SIOCPROTOPRIVATE)
+#define SIOCAIPXPRISLT (SIOCPROTOPRIVATE+1)
+#define SIOCIPXCFGDATA (SIOCPROTOPRIVATE+2)
+#define SIOCIPXNCPCONN (SIOCPROTOPRIVATE+3)
+
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+
+extern int ipxrtr_route_skb(struct sk_buff *);
+extern int ipx_if_offset(unsigned long ipx_net_number);
+extern void ipx_remove_socket(struct sock *sk);
+#endif /* def __KERNEL__ */
+
+#endif /* def _IPX_H_ */
diff --git a/pfinet/linux-src/include/linux/irda.h b/pfinet/linux-src/include/linux/irda.h
new file mode 100644
index 00000000..e0eedbc9
--- /dev/null
+++ b/pfinet/linux-src/include/linux/irda.h
@@ -0,0 +1,116 @@
+/*********************************************************************
+ *
+ * Filename: irda.h
+ * Version:
+ * Description:
+ * Status: Experimental.
+ * Author: Dag Brattli <dagb@cs.uit.no>
+ * Created at: Mon Mar 8 14:06:12 1999
+ * Modified at: Mon Mar 22 14:14:54 1999
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ *
+ * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * Neither Dag Brattli nor University of Tromsø admit liability nor
+ * provide warranty for any of this software. This material is
+ * provided "AS-IS" and at no charge.
+ *
+ ********************************************************************/
+
+#ifndef KERNEL_IRDA_H
+#define KERNEL_IRDA_H
+
+/* Hint bit positions for first hint byte */
+#define HINT_PNP 0x01
+#define HINT_PDA 0x02
+#define HINT_COMPUTER 0x04
+#define HINT_PRINTER 0x08
+#define HINT_MODEM 0x10
+#define HINT_FAX 0x20
+#define HINT_LAN 0x40
+#define HINT_EXTENSION 0x80
+
+/* Hint bit positions for second hint byte (first extension byte) */
+#define HINT_TELEPHONY 0x01
+#define HINT_FILE_SERVER 0x02
+#define HINT_COMM 0x04
+#define HINT_MESSAGE 0x08
+#define HINT_HTTP 0x10
+#define HINT_OBEX 0x20
+
+/* IrLMP character code values */
+#define CS_ASCII 0x00
+#define CS_ISO_8859_1 0x01
+#define CS_ISO_8859_2 0x02
+#define CS_ISO_8859_3 0x03
+#define CS_ISO_8859_4 0x04
+#define CS_ISO_8859_5 0x05
+#define CS_ISO_8859_6 0x06
+#define CS_ISO_8859_7 0x07
+#define CS_ISO_8859_8 0x08
+#define CS_ISO_8859_9 0x09
+#define CS_UNICODE 0xff
+
+#define SOL_IRLMP 266 /* Same as SOL_IRDA for now */
+#define SOL_IRTTP 266 /* Same as SOL_IRDA for now */
+
+#define IRLMP_ENUMDEVICES 1
+#define IRLMP_IAS_SET 2
+#define IRLMP_IAS_QUERY 3
+#define IRLMP_DISCOVERY_MASK_SET 4
+
+#define IRTTP_QOS_SET 5
+#define IRTTP_QOS_GET 6
+#define IRTTP_MAX_SDU_SIZE 7
+
+#define IAS_MAX_STRING 256
+#define IAS_MAX_OCTET_STRING 1024
+#define IAS_MAX_CLASSNAME 64
+#define IAS_MAX_ATTRIBNAME 256
+
+#define LSAP_ANY 0xff
+
+struct sockaddr_irda {
+ sa_family_t sir_family; /* AF_IRDA */
+ unsigned char sir_lsap_sel; /* LSAP/TSAP selector */
+ unsigned int sir_addr; /* Device address */
+ char sir_name[25]; /* Usually <service>:IrDA:TinyTP */
+};
+
+struct irda_device_info {
+ unsigned int saddr; /* Address of remote device */
+ unsigned int daddr; /* Link where it was discovered */
+ char info[22]; /* Description */
+ unsigned char charset; /* Charset used for description */
+ unsigned char hints[2]; /* Hint bits */
+};
+
+struct irda_device_list {
+ unsigned int len;
+ struct irda_device_info dev[0];
+};
+
+struct irda_ias_set {
+ char irda_class_name[IAS_MAX_CLASSNAME];
+ char irda_attrib_name[IAS_MAX_ATTRIBNAME];
+ unsigned int irda_attrib_type;
+ union {
+ unsigned int irda_attrib_int;
+ struct {
+ unsigned short len;
+ u_char OctetSeq[IAS_MAX_OCTET_STRING];
+ } irda_attrib_octet_seq;
+ struct {
+ unsigned char len;
+ unsigned char charset;
+ unsigned char string[IAS_MAX_STRING];
+ } irda_attrib_string;
+ } attribute;
+};
+
+#endif /* KERNEL_IRDA_H */
diff --git a/pfinet/linux-src/include/linux/isdn.h b/pfinet/linux-src/include/linux/isdn.h
new file mode 100644
index 00000000..742fe14b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/isdn.h
@@ -0,0 +1,936 @@
+/* $Id: isdn.h,v 1.81 1999/10/27 21:21:18 detabc Exp $
+ *
+ * Main header for the Linux ISDN subsystem (linklevel).
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: isdn.h,v $
+ * Revision 1.81 1999/10/27 21:21:18 detabc
+ * Added support for building logically-bind-group's per interface.
+ * useful for outgoing call's with more then one isdn-card.
+ *
+ * Switchable support to dont reset the hangup-timeout for
+ * receive frames. Most part's of the timru-rules for receiving frames
+ * are now obsolete. If the input- or forwarding-firewall deny
+ * the frame, the line will be not hold open.
+ *
+ * Revision 1.80 1999/10/26 21:09:29 armin
+ * New bufferlen for phonenumber only with kernel 2.3.x
+ *
+ * Revision 1.79 1999/10/16 17:52:38 keil
+ * Changing the MSN length need new data versions
+ *
+ * Revision 1.78 1999/10/08 18:59:33 armin
+ * Bugfix of too small MSN buffer and checking phone number
+ * in isdn_tty_getdial()
+ *
+ * Revision 1.77 1999/09/23 22:22:42 detabc
+ * added tcp-keepalive-detect with local response (ipv4 only)
+ * added host-only-interface support
+ * (source ipaddr == interface ipaddr) (ipv4 only)
+ * ok with kernel 2.3.18 and 2.2.12
+ *
+ * Revision 1.76 1999/09/14 10:16:21 keil
+ * change ABC include
+ *
+ * Revision 1.75 1999/09/13 23:25:17 he
+ * serialized xmitting frames from isdn_ppp and BSENT statcallb
+ *
+ * Revision 1.74 1999/09/12 16:19:39 detabc
+ * added abc features
+ * low cost routing for net-interfaces (only the HL side).
+ * need more implementation in the isdnlog-utility
+ * udp info support (first part).
+ * different EAZ on outgoing call's.
+ * more checks on D-Channel callbacks (double use of channels).
+ * tested and running with kernel 2.3.17
+ *
+ * Revision 1.73 1999/09/06 07:29:36 fritz
+ * Changed my mail-address.
+ *
+ * Revision 1.72 1999/09/04 22:20:19 detabc
+ *
+ * Revision 1.71 1999/08/23 15:54:22 keil
+ * more backported changes from kernel 2.3.14
+ *
+ * Revision 1.70 1999/07/31 12:59:58 armin
+ * Added tty fax capabilities.
+ *
+ * Revision 1.69 1999/07/13 20:47:53 werner
+ * added channel bit ISDN_USAGE_DISABLED for limiting b-channel access.
+ *
+ * Revision 1.68 1999/07/11 17:07:37 armin
+ * Added tty modem register S23.
+ * Added new layer 2 and 3 protocols for Fax and DSP functions.
+ *
+ * Revision 1.67 1999/07/07 10:17:24 detabc
+ * remove unused messages
+ *
+ * Revision 1.66 1999/07/01 08:35:37 keil
+ * compatibility to 2.3
+ *
+ * Revision 1.65 1999/06/10 11:51:27 paul
+ * fixed comment for NET_DV
+ *
+ * Revision 1.64 1999/04/18 14:57:14 fritz
+ * Removed TIMRU stuff
+ *
+ * Revision 1.63 1999/04/18 14:07:18 fritz
+ * Removed TIMRU stuff.
+ *
+ * Revision 1.62 1999/04/12 13:16:54 fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.61 1999/03/02 11:43:21 armin
+ * Added variable to store connect-message of Modem.
+ * Added Timer-define for RegS7 (Wait for Carrier).
+ *
+ * Revision 1.60 1998/10/25 14:50:29 fritz
+ * Backported from MIPS (Cobalt).
+ *
+ * Revision 1.59 1998/10/23 10:18:55 paul
+ * Implementation of "dialmode" (successor of "status")
+ * You also need current isdnctrl for this!
+ *
+ * Revision 1.58 1998/10/23 10:10:06 fritz
+ * Test-Checkin
+ *
+ * Revision 1.57 1998/08/31 21:10:01 he
+ * new ioctl IIOCNETGPN for /dev/isdninfo (get network interface'
+ * peer phone number)
+ *
+ * Revision 1.56 1998/07/26 18:46:52 armin
+ * Added silence detection in voice receive mode.
+ *
+ * Revision 1.55 1998/06/26 15:13:17 fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 1.54 1998/06/18 23:32:01 fritz
+ * Replaced cli()/restore_flags() in isdn_tty_write() by locking.
+ * Removed direct-senddown feature in isdn_tty_write because it will
+ * never succeed with locking and is useless anyway.
+ *
+ * Revision 1.53 1998/06/17 19:51:51 he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.46 1998/04/14 16:28:59 he
+ * Fixed user space access with interrupts off and remaining
+ * copy_{to,from}_user() -> -EFAULT return codes
+ *
+ * Revision 1.45 1998/03/24 16:33:12 hipp
+ * More CCP changes. BSD compression now "works" on a local loopback link.
+ * Moved some isdn_ppp stuff from isdn.h to isdn_ppp.h
+ *
+ * Revision 1.44 1998/03/22 18:50:56 hipp
+ * Added BSD Compression for syncPPP .. UNTESTED at the moment
+ *
+ * Revision 1.43 1998/03/09 17:46:44 he
+ * merged in 2.1.89 changes
+ *
+ *
+ * Revision 1.40 1998/03/08 01:08:29 fritz
+ * Increased NET_DV because of TIMRU
+ *
+ * Revision 1.39 1998/03/07 22:42:49 fritz
+ * Starting generic module support (Nothing usable yet).
+ *
+ * Revision 1.38 1998/03/07 18:21:29 cal
+ * Dynamic Timeout-Rule-Handling vs. 971110 included
+ *
+ * Revision 1.37 1998/02/22 19:45:24 fritz
+ * Some changes regarding V.110
+ *
+ * Revision 1.36 1998/02/20 17:35:55 fritz
+ * Added V.110 stuff.
+ *
+ * Revision 1.35 1998/01/31 22:14:14 keil
+ * changes for 2.1.82
+ *
+ * Revision 1.34 1997/10/09 21:28:11 fritz
+ * New HL<->LL interface:
+ * New BSENT callback with nr. of bytes included.
+ * Sending without ACK.
+ * New L1 error status (not yet in use).
+ * Cleaned up obsolete structures.
+ * Implemented Cisco-SLARP.
+ * Changed local net-interface data to be dynamically allocated.
+ * Removed old 2.0 compatibility stuff.
+ *
+ * Revision 1.33 1997/08/21 14:44:22 fritz
+ * Moved triggercps to end of struct for backwards-compatibility.
+ *
+ * Revision 1.32 1997/08/21 09:49:46 fritz
+ * Increased NET_DV
+ *
+ * Revision 1.31 1997/06/22 11:57:07 fritz
+ * Added ability to adjust slave triggerlevel.
+ *
+ * Revision 1.30 1997/06/17 13:07:23 hipp
+ * compression changes , MP changes
+ *
+ * Revision 1.29 1997/05/27 15:18:02 fritz
+ * Added changes for recent 2.1.x kernels:
+ * changed return type of isdn_close
+ * queue_task_* -> queue_task
+ * clear/set_bit -> test_and_... where appropriate.
+ * changed type of hard_header_cache parameter.
+ *
+ * Revision 1.28 1997/03/07 01:33:01 fritz
+ * Added proper ifdef's for CONFIG_ISDN_AUDIO
+ *
+ * Revision 1.27 1997/03/05 21:11:49 fritz
+ * Minor fixes.
+ *
+ * Revision 1.26 1997/02/28 02:37:53 fritz
+ * Added some comments.
+ *
+ * Revision 1.25 1997/02/23 16:54:23 hipp
+ * some initial changes for future PPP compresion
+ *
+ * Revision 1.24 1997/02/18 09:42:45 fritz
+ * Bugfix: Increased ISDN_MODEM_ANZREG.
+ * Increased TTY_DV.
+ *
+ * Revision 1.23 1997/02/10 22:07:13 fritz
+ * Added 2 modem registers for numbering plan and screening info.
+ *
+ * Revision 1.22 1997/02/03 23:42:08 fritz
+ * Added ISDN_TIMER_RINGING
+ * Misc. changes for Kernel 2.1.X compatibility
+ *
+ * Revision 1.21 1997/01/17 01:19:10 fritz
+ * Applied chargeint patch.
+ *
+ * Revision 1.20 1997/01/17 00:41:19 fritz
+ * Increased TTY_DV.
+ *
+ * Revision 1.19 1997/01/14 01:41:07 fritz
+ * Added ATI2 related variables.
+ * Added variables for audio support in skbuffs.
+ *
+ * Revision 1.18 1996/11/06 17:37:50 keil
+ * more changes for 2.1.X
+ *
+ * Revision 1.17 1996/09/07 12:53:57 hipp
+ * moved a few isdn_ppp.c specific defines to drives/isdn/isdn_ppp.h
+ *
+ * Revision 1.16 1996/08/12 16:20:56 hipp
+ * renamed ppp_minor to ppp_slot
+ *
+ * Revision 1.15 1996/06/15 14:56:57 fritz
+ * Added version signatures for data structures used
+ * by userlevel programs.
+ *
+ * Revision 1.14 1996/06/06 21:24:23 fritz
+ * Started adding support for suspend/resume.
+ *
+ * Revision 1.13 1996/06/05 02:18:20 fritz
+ * Added DTMF decoding stuff.
+ *
+ * Revision 1.12 1996/06/03 19:55:08 fritz
+ * Fixed typos.
+ *
+ * Revision 1.11 1996/05/31 01:37:47 fritz
+ * Minor changes, due to changes in isdn_tty.c
+ *
+ * Revision 1.10 1996/05/18 01:37:18 fritz
+ * Added spelling corrections and some minor changes
+ * to stay in sync with kernel.
+ *
+ * Revision 1.9 1996/05/17 03:58:20 fritz
+ * Added flags for DLE handling.
+ *
+ * Revision 1.8 1996/05/11 21:49:55 fritz
+ * Removed queue management variables.
+ * Changed queue management to use sk_buffs.
+ *
+ * Revision 1.7 1996/05/07 09:10:06 fritz
+ * Reorganized tty-related structs.
+ *
+ * Revision 1.6 1996/05/06 11:38:27 hipp
+ * minor change in ippp struct
+ *
+ * Revision 1.5 1996/04/30 11:03:16 fritz
+ * Added Michael's ippp-bind patch.
+ *
+ * Revision 1.4 1996/04/29 23:00:02 fritz
+ * Added variables for voice-support.
+ *
+ * Revision 1.3 1996/04/20 16:54:58 fritz
+ * Increased maximum number of channels.
+ * Added some flags for isdn_net to handle callback more reliable.
+ * Fixed delay-definitions to be more accurate.
+ * Misc. typos
+ *
+ * Revision 1.2 1996/02/11 02:10:02 fritz
+ * Changed IOCTL-names
+ * Added rx_netdev, st_netdev, first_skb, org_hcb, and org_hcu to
+ * Netdevice-local struct.
+ *
+ * Revision 1.1 1996/01/10 20:55:07 fritz
+ * Initial revision
+ *
+ */
+
+#ifndef isdn_h
+#define isdn_h
+
+#include <linux/config.h>
+#include <linux/ioctl.h>
+
+#define ISDN_TTY_MAJOR 43
+#define ISDN_TTYAUX_MAJOR 44
+#define ISDN_MAJOR 45
+
+/* The minor-devicenumbers for Channel 0 and 1 are used as arguments for
+ * physical Channel-Mapping, so they MUST NOT be changed without changing
+ * the correspondent code in isdn.c
+ */
+
+#ifdef CONFIG_COBALT_MICRO_SERVER
+/* Save memory */
+#define ISDN_MAX_DRIVERS 2
+#define ISDN_MAX_CHANNELS 8
+#else
+#define ISDN_MAX_DRIVERS 32
+#define ISDN_MAX_CHANNELS 64
+#endif
+#define ISDN_MINOR_B 0
+#define ISDN_MINOR_BMAX (ISDN_MAX_CHANNELS-1)
+#define ISDN_MINOR_CTRL 64
+#define ISDN_MINOR_CTRLMAX (64 + (ISDN_MAX_CHANNELS-1))
+#define ISDN_MINOR_PPP 128
+#define ISDN_MINOR_PPPMAX (128 + (ISDN_MAX_CHANNELS-1))
+#define ISDN_MINOR_STATUS 255
+
+#undef CONFIG_ISDN_WITH_ABC_CALLB
+#undef CONFIG_ISDN_WITH_ABC_UDP_CHECK
+#undef CONFIG_ISDN_WITH_ABC_UDP_CHECK_HANGUP
+#undef CONFIG_ISDN_WITH_ABC_UDP_CHECK_DIAL
+#undef CONFIG_ISDN_WITH_ABC_OUTGOING_EAZ
+#undef CONFIG_ISDN_WITH_ABC_LCR_SUPPORT
+#undef CONFIG_ISDN_WITH_ABC_IPV4_TCP_KEEPALIVE
+#undef CONFIG_ISDN_WITH_ABC_IPV4_DYNADDR
+#undef CONFIG_ISDN_WITH_ABC_RCV_NO_HUPTIMER
+#undef CONFIG_ISDN_WITH_ABC_ICALL_BIND
+
+
+/* New ioctl-codes */
+#define IIOCNETAIF _IO('I',1)
+#define IIOCNETDIF _IO('I',2)
+#define IIOCNETSCF _IO('I',3)
+#define IIOCNETGCF _IO('I',4)
+#define IIOCNETANM _IO('I',5)
+#define IIOCNETDNM _IO('I',6)
+#define IIOCNETGNM _IO('I',7)
+#define IIOCGETSET _IO('I',8) /* no longer supported */
+#define IIOCSETSET _IO('I',9) /* no longer supported */
+#define IIOCSETVER _IO('I',10)
+#define IIOCNETHUP _IO('I',11)
+#define IIOCSETGST _IO('I',12)
+#define IIOCSETBRJ _IO('I',13)
+#define IIOCSIGPRF _IO('I',14)
+#define IIOCGETPRF _IO('I',15)
+#define IIOCSETPRF _IO('I',16)
+#define IIOCGETMAP _IO('I',17)
+#define IIOCSETMAP _IO('I',18)
+#define IIOCNETASL _IO('I',19)
+#define IIOCNETDIL _IO('I',20)
+#define IIOCGETCPS _IO('I',21)
+#define IIOCGETDVR _IO('I',22)
+#define IIOCNETLCR _IO('I',23) /* dwabc ioctl for LCR from isdnlog */
+
+#define IIOCNETALN _IO('I',32)
+#define IIOCNETDLN _IO('I',33)
+
+#define IIOCNETGPN _IO('I',34)
+
+#define IIOCDBGVAR _IO('I',127)
+
+#define IIOCDRVCTL _IO('I',128)
+
+/* Packet encapsulations for net-interfaces */
+#define ISDN_NET_ENCAP_ETHER 0
+#define ISDN_NET_ENCAP_RAWIP 1
+#define ISDN_NET_ENCAP_IPTYP 2
+#define ISDN_NET_ENCAP_CISCOHDLC 3 /* Without SLARP and keepalive */
+#define ISDN_NET_ENCAP_SYNCPPP 4
+#define ISDN_NET_ENCAP_UIHDLC 5
+#define ISDN_NET_ENCAP_CISCOHDLCK 6 /* With SLARP and keepalive */
+#define ISDN_NET_ENCAP_X25IFACE 7 /* Documentation/networking/x25-iface.txt*/
+#define ISDN_NET_ENCAP_MAX_ENCAP ISDN_NET_ENCAP_X25IFACE
+/* Facility which currently uses an ISDN-channel */
+#define ISDN_USAGE_NONE 0
+#define ISDN_USAGE_RAW 1
+#define ISDN_USAGE_MODEM 2
+#define ISDN_USAGE_NET 3
+#define ISDN_USAGE_VOICE 4
+#define ISDN_USAGE_FAX 5
+#define ISDN_USAGE_MASK 7 /* Mask to get plain usage */
+#define ISDN_USAGE_DISABLED 32 /* This bit is set, if channel is disabled */
+#define ISDN_USAGE_EXCLUSIVE 64 /* This bit is set, if channel is exclusive */
+#define ISDN_USAGE_OUTGOING 128 /* This bit is set, if channel is outgoing */
+
+#define ISDN_MODEM_ANZREG 24 /* Number of Modem-Registers */
+#define ISDN_LMSNLEN 255 /* Length of tty's Listen-MSN string */
+#define ISDN_CMSGLEN 50 /* Length of CONNECT-Message to add for Modem */
+
+#define ISDN_MSNLEN 20
+#define NET_DV 0x05 /* Data version for isdn_net_ioctl_cfg */
+#define TTY_DV 0x05 /* Data version for iprofd etc. */
+
+#define INF_DV 0x01 /* Data version for /dev/isdninfo */
+
+typedef struct {
+ char drvid[25];
+ unsigned long arg;
+} isdn_ioctl_struct;
+
+typedef struct {
+ unsigned long isdndev;
+ unsigned long atmodem[ISDN_MAX_CHANNELS];
+ unsigned long info[ISDN_MAX_CHANNELS];
+} debugvar_addr;
+
+typedef struct {
+ char name[10];
+ char phone[ISDN_MSNLEN];
+ int outgoing;
+} isdn_net_ioctl_phone;
+
+typedef struct {
+ char name[10]; /* Name of interface */
+ char master[10]; /* Name of Master for Bundling */
+ char slave[10]; /* Name of Slave for Bundling */
+ char eaz[256]; /* EAZ/MSN */
+ char drvid[25]; /* DriverId for Bindings */
+ int onhtime; /* Hangup-Timeout */
+ int charge; /* Charge-Units */
+ int l2_proto; /* Layer-2 protocol */
+ int l3_proto; /* Layer-3 protocol */
+ int p_encap; /* Encapsulation */
+ int exclusive; /* Channel, if bound exclusive */
+ int dialmax; /* Dial Retry-Counter */
+ int slavedelay; /* Delay until slave starts up */
+ int cbdelay; /* Delay before Callback */
+ int chargehup; /* Flag: Charge-Hangup */
+ int ihup; /* Flag: Hangup-Timeout on incoming line */
+ int secure; /* Flag: Secure */
+ int callback; /* Flag: Callback */
+ int cbhup; /* Flag: Reject Call before Callback */
+ int pppbind; /* ippp device for bindings */
+ int chargeint; /* Use fixed charge interval length */
+ int triggercps; /* BogoCPS needed for triggering slave */
+ int dialtimeout; /* Dial-Timeout */
+ int dialwait; /* Time to wait after failed dial */
+ int dialmode; /* Flag: off / on / auto */
+} isdn_net_ioctl_cfg;
+
+#define ISDN_NET_DIALMODE_MASK 0xC0 /* bits for status */
+#define ISDN_NET_DM_OFF 0x00 /* this interface is stopped */
+#define ISDN_NET_DM_MANUAL 0x40 /* this interface is on (manual) */
+#define ISDN_NET_DM_AUTO 0x80 /* this interface is autodial */
+#define ISDN_NET_DIALMODE(x) ((&(x))->flags & ISDN_NET_DIALMODE_MASK)
+
+#ifdef __KERNEL__
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/malloc.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_reg.h>
+#include <linux/fcntl.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/tcp.h>
+
+#ifdef CONFIG_ISDN_PPP
+
+#ifdef CONFIG_ISDN_PPP_VJ
+# include <net/slhc_vj.h>
+#endif
+
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#include <linux/if_pppvar.h>
+
+#include <linux/isdn_ppp.h>
+#endif
+
+#ifdef CONFIG_ISDN_X25
+# include <linux/concap.h>
+#endif
+
+#include <linux/isdnif.h>
+
+#define ISDN_DRVIOCTL_MASK 0x7f /* Mask for Device-ioctl */
+
+/* Until now unused */
+#define ISDN_SERVICE_VOICE 1
+#define ISDN_SERVICE_AB 1<<1
+#define ISDN_SERVICE_X21 1<<2
+#define ISDN_SERVICE_G4 1<<3
+#define ISDN_SERVICE_BTX 1<<4
+#define ISDN_SERVICE_DFUE 1<<5
+#define ISDN_SERVICE_X25 1<<6
+#define ISDN_SERVICE_TTX 1<<7
+#define ISDN_SERVICE_MIXED 1<<8
+#define ISDN_SERVICE_FW 1<<9
+#define ISDN_SERVICE_GTEL 1<<10
+#define ISDN_SERVICE_BTXN 1<<11
+#define ISDN_SERVICE_BTEL 1<<12
+
+/* Macros checking plain usage */
+#define USG_NONE(x) ((x & ISDN_USAGE_MASK)==ISDN_USAGE_NONE)
+#define USG_RAW(x) ((x & ISDN_USAGE_MASK)==ISDN_USAGE_RAW)
+#define USG_MODEM(x) ((x & ISDN_USAGE_MASK)==ISDN_USAGE_MODEM)
+#define USG_VOICE(x) ((x & ISDN_USAGE_MASK)==ISDN_USAGE_VOICE)
+#define USG_NET(x) ((x & ISDN_USAGE_MASK)==ISDN_USAGE_NET)
+#define USG_FAX(x) ((x & ISDN_USAGE_MASK)==ISDN_USAGE_FAX)
+#define USG_OUTGOING(x) ((x & ISDN_USAGE_OUTGOING)==ISDN_USAGE_OUTGOING)
+#define USG_MODEMORVOICE(x) (((x & ISDN_USAGE_MASK)==ISDN_USAGE_MODEM) || \
+ ((x & ISDN_USAGE_MASK)==ISDN_USAGE_VOICE) )
+
+/* Timer-delays and scheduling-flags */
+#define ISDN_TIMER_RES 3 /* Main Timer-Resolution */
+#define ISDN_TIMER_02SEC (HZ/(ISDN_TIMER_RES+1)/5) /* Slow-Timer1 .2 sec */
+#define ISDN_TIMER_1SEC (HZ/(ISDN_TIMER_RES+1)) /* Slow-Timer2 1 sec */
+#define ISDN_TIMER_RINGING 5 /* tty RINGs = ISDN_TIMER_1SEC * this factor */
+#define ISDN_TIMER_KEEPINT 10 /* Cisco-Keepalive = ISDN_TIMER_1SEC * this factor */
+#define ISDN_TIMER_MODEMREAD 1
+#define ISDN_TIMER_MODEMPLUS 2
+#define ISDN_TIMER_MODEMRING 4
+#define ISDN_TIMER_MODEMXMIT 8
+#define ISDN_TIMER_NETDIAL 16
+#define ISDN_TIMER_NETHANGUP 32
+#define ISDN_TIMER_IPPP 64
+#define ISDN_TIMER_KEEPALIVE 128 /* Cisco-Keepalive */
+#define ISDN_TIMER_CARRIER 256 /* Wait for Carrier */
+#define ISDN_TIMER_FAST (ISDN_TIMER_MODEMREAD | ISDN_TIMER_MODEMPLUS | \
+ ISDN_TIMER_MODEMXMIT)
+#define ISDN_TIMER_SLOW (ISDN_TIMER_MODEMRING | ISDN_TIMER_NETHANGUP | \
+ ISDN_TIMER_NETDIAL | ISDN_TIMER_KEEPALIVE | \
+ ISDN_TIMER_CARRIER)
+
+/* Timeout-Values for isdn_net_dial() */
+#define ISDN_TIMER_DTIMEOUT10 (10*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1)))
+#define ISDN_TIMER_DTIMEOUT15 (15*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1)))
+#define ISDN_TIMER_DTIMEOUT60 (60*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1)))
+
+/* GLOBAL_FLAGS */
+#define ISDN_GLOBAL_STOPPED 1
+
+/*=================== Start of ip-over-ISDN stuff =========================*/
+
+/* Feature- and status-flags for a net-interface */
+#define ISDN_NET_CONNECTED 0x01 /* Bound to ISDN-Channel */
+#define ISDN_NET_SECURE 0x02 /* Accept calls from phonelist only */
+#define ISDN_NET_CALLBACK 0x04 /* activate callback */
+#define ISDN_NET_CBHUP 0x08 /* hangup before callback */
+#define ISDN_NET_CBOUT 0x10 /* remote machine does callback */
+
+#define ISDN_NET_MAGIC 0x49344C02 /* for paranoia-checking */
+
+/* Phone-list-element */
+typedef struct {
+ void *next;
+ char num[ISDN_MSNLEN];
+} isdn_net_phone;
+
+/*
+ Principles when extending structures for generic encapsulation protocol
+ ("concap") support:
+ - Stuff which is hardware specific (here i4l-specific) goes in
+ the netdev -> local structure (here: isdn_net_local)
+ - Stuff which is encapsulation protocol specific goes in the structure
+ which holds the linux device structure (here: isdn_net_device)
+*/
+
+/* Local interface-data */
+typedef struct isdn_net_local_s {
+ ulong magic;
+ char name[10]; /* Name of device */
+ struct enet_statistics stats; /* Ethernet Statistics */
+ int isdn_device; /* Index to isdn-device */
+ int isdn_channel; /* Index to isdn-channel */
+ int ppp_slot; /* PPPD device slot number */
+ int pre_device; /* Preselected isdn-device */
+ int pre_channel; /* Preselected isdn-channel */
+ int exclusive; /* If non-zero idx to reserved chan.*/
+ int flags; /* Connection-flags */
+ int dialretry; /* Counter for Dialout-retries */
+ int dialmax; /* Max. Number of Dial-retries */
+ int cbdelay; /* Delay before Callback starts */
+ int dtimer; /* Timeout-counter for dialing */
+ char msn[ISDN_MSNLEN]; /* MSNs/EAZs for this interface */
+ u_char cbhup; /* Flag: Reject Call before Callback*/
+ u_char dialstate; /* State for dialing */
+ u_char p_encap; /* Packet encapsulation */
+ /* 0 = Ethernet over ISDN */
+ /* 1 = RAW-IP */
+ /* 2 = IP with type field */
+ u_char l2_proto; /* Layer-2-protocol */
+ /* See ISDN_PROTO_L2..-constants in */
+ /* isdnif.h */
+ /* 0 = X75/LAPB with I-Frames */
+ /* 1 = X75/LAPB with UI-Frames */
+ /* 2 = X75/LAPB with BUI-Frames */
+ /* 3 = HDLC */
+ u_char l3_proto; /* Layer-3-protocol */
+ /* See ISDN_PROTO_L3..-constants in */
+ /* isdnif.h */
+ /* 0 = Transparent */
+ int huptimer; /* Timeout-counter for auto-hangup */
+ int charge; /* Counter for charging units */
+ int chargetime; /* Timer for Charging info */
+ int hupflags; /* Flags for charge-unit-hangup: */
+ /* bit0: chargeint is invalid */
+ /* bit1: Getting charge-interval */
+ /* bit2: Do charge-unit-hangup */
+ /* bit3: Do hangup even on incoming */
+ int outgoing; /* Flag: outgoing call */
+ int onhtime; /* Time to keep link up */
+ int chargeint; /* Interval between charge-infos */
+ int onum; /* Flag: at least 1 outgoing number */
+ int cps; /* current speed of this interface */
+ int transcount; /* byte-counter for cps-calculation */
+ int sqfull; /* Flag: netdev-queue overloaded */
+ ulong sqfull_stamp; /* Start-Time of overload */
+ ulong slavedelay; /* Dynamic bundling delaytime */
+ int triggercps; /* BogoCPS needed for trigger slave */
+ struct device *srobin; /* Ptr to Master device for slaves */
+ isdn_net_phone *phone[2]; /* List of remote-phonenumbers */
+ /* phone[0] = Incoming Numbers */
+ /* phone[1] = Outgoing Numbers */
+ isdn_net_phone *dial; /* Pointer to dialed number */
+ struct device *master; /* Ptr to Master device for slaves */
+ struct device *slave; /* Ptr to Slave device for masters */
+ struct isdn_net_local_s *next; /* Ptr to next link in bundle */
+ struct isdn_net_local_s *last; /* Ptr to last link in bundle */
+ struct isdn_net_dev_s *netdev; /* Ptr to netdev */
+ struct sk_buff *first_skb; /* Ptr to skb that triggers dialing */
+ struct sk_buff *volatile sav_skb; /* Ptr to skb, rejected by LL-driver*/
+ /* Ptr to orig. hard_header_cache */
+ int (*org_hhc)(
+ struct neighbour *neigh,
+ struct hh_cache *hh);
+ /* Ptr to orig. header_cache_update */
+ void (*org_hcu)(struct hh_cache *,
+ struct device *,
+ unsigned char *);
+ int pppbind; /* ippp device for bindings */
+ int dialtimeout; /* How long shall we try on dialing? (jiffies) */
+ int dialwait; /* How long shall we wait after failed attempt? (jiffies) */
+ ulong dialstarted; /* jiffies of first dialing-attempt */
+ ulong dialwait_timer; /* jiffies of earliest next dialing-attempt */
+ int huptimeout; /* How long will the connection be up? (seconds) */
+#ifdef CONFIG_ISDN_X25
+ struct concap_device_ops *dops; /* callbacks used by encapsulator */
+#endif
+ int cisco_loop; /* Loop counter for Cisco-SLARP */
+ ulong cisco_myseq; /* Local keepalive seq. for Cisco */
+ ulong cisco_yourseq; /* Remote keepalive seq. for Cisco */
+} isdn_net_local;
+
+/* the interface itself */
+typedef struct isdn_net_dev_s {
+ isdn_net_local *local;
+ isdn_net_local *queue;
+ void *next; /* Pointer to next isdn-interface */
+ struct device dev; /* interface to upper levels */
+#ifdef CONFIG_ISDN_PPP
+ struct mpqueue *mp_last;
+ struct ippp_bundle ib;
+#endif
+#ifdef CONFIG_ISDN_X25
+ struct concap_proto *cprot; /* connection oriented encapsulation protocol */
+#endif
+
+} isdn_net_dev;
+
+/*===================== End of ip-over-ISDN stuff ===========================*/
+
+/*======================= Start of ISDN-tty stuff ===========================*/
+
+#define ISDN_ASYNC_MAGIC 0x49344C01 /* for paranoia-checking */
+#define ISDN_ASYNC_INITIALIZED 0x80000000 /* port was initialized */
+#define ISDN_ASYNC_CALLOUT_ACTIVE 0x40000000 /* Call out device active */
+#define ISDN_ASYNC_NORMAL_ACTIVE 0x20000000 /* Normal device active */
+#define ISDN_ASYNC_CLOSING 0x08000000 /* Serial port is closing */
+#define ISDN_ASYNC_CTS_FLOW 0x04000000 /* Do CTS flow control */
+#define ISDN_ASYNC_CHECK_CD 0x02000000 /* i.e., CLOCAL */
+#define ISDN_ASYNC_HUP_NOTIFY 0x0001 /* Notify tty on hangups/closes */
+#define ISDN_ASYNC_SESSION_LOCKOUT 0x0100 /* Lock cua opens on session */
+#define ISDN_ASYNC_PGRP_LOCKOUT 0x0200 /* Lock cua opens on pgrp */
+#define ISDN_ASYNC_CALLOUT_NOHUP 0x0400 /* No hangup for cui */
+#define ISDN_ASYNC_SPLIT_TERMIOS 0x0008 /* Sep. termios for dialin/out */
+#define ISDN_SERIAL_XMIT_SIZE 1024 /* Default bufsize for write */
+#define ISDN_SERIAL_XMIT_MAX 4000 /* Maximum bufsize for write */
+#define ISDN_SERIAL_TYPE_NORMAL 1
+#define ISDN_SERIAL_TYPE_CALLOUT 2
+
+#ifdef CONFIG_ISDN_AUDIO
+/* For using sk_buffs with audio we need some private variables
+ * within each sk_buff. For this purpose, we declare a struct here,
+ * and put it always at skb->head. A few macros help accessing the
+ * variables. Of course, we need to check skb_headroom prior to
+ * any access.
+ */
+typedef struct isdn_audio_skb {
+ unsigned short dle_count;
+ unsigned char lock;
+} isdn_audio_skb;
+
+#define ISDN_AUDIO_SKB_DLECOUNT(skb) (((isdn_audio_skb*)skb->head)->dle_count)
+#define ISDN_AUDIO_SKB_LOCK(skb) (((isdn_audio_skb*)skb->head)->lock)
+#endif
+
+/* Private data of AT-command-interpreter */
+typedef struct atemu {
+ u_char profile[ISDN_MODEM_ANZREG]; /* Modem-Regs. Profile 0 */
+ u_char mdmreg[ISDN_MODEM_ANZREG]; /* Modem-Registers */
+ char pmsn[ISDN_MSNLEN]; /* EAZ/MSNs Profile 0 */
+ char msn[ISDN_MSNLEN]; /* EAZ/MSN */
+ char plmsn[ISDN_LMSNLEN]; /* Listening MSNs Profile 0 */
+ char lmsn[ISDN_LMSNLEN]; /* Listening MSNs */
+ char cpn[ISDN_MSNLEN]; /* CalledPartyNumber on incoming call */
+ char connmsg[ISDN_CMSGLEN]; /* CONNECT-Msg from HL-Driver */
+#ifdef CONFIG_ISDN_AUDIO
+ u_char vpar[10]; /* Voice-parameters */
+ int lastDLE; /* Flag for voice-coding: DLE seen */
+#endif
+ int mdmcmdl; /* Length of Modem-Commandbuffer */
+ int pluscount; /* Counter for +++ sequence */
+ int lastplus; /* Timestamp of last + */
+ int carrierwait; /* Seconds of carrier waiting */
+ char mdmcmd[255]; /* Modem-Commandbuffer */
+ unsigned int charge; /* Charge units of current connection */
+} atemu;
+
+/* Private data (similar to async_struct in <linux/serial.h>) */
+typedef struct modem_info {
+ int magic;
+ int flags; /* defined in tty.h */
+ int x_char; /* xon/xoff character */
+ int mcr; /* Modem control register */
+ int msr; /* Modem status register */
+ int lsr; /* Line status register */
+ int line;
+ int count; /* # of fd on device */
+ int blocked_open; /* # of blocked opens */
+ long session; /* Session of opening process */
+ long pgrp; /* pgrp of opening process */
+ int online; /* 1 = B-Channel is up, drop data */
+ /* 2 = B-Channel is up, deliver d.*/
+ int dialing; /* Dial in progress or ATA */
+ int rcvsched; /* Receive needs schedule */
+ int isdn_driver; /* Index to isdn-driver */
+ int isdn_channel; /* Index to isdn-channel */
+ int drv_index; /* Index to dev->usage */
+ int ncarrier; /* Flag: schedule NO CARRIER */
+ unsigned char last_cause[8]; /* Last cause message */
+ unsigned char last_num[ISDN_MSNLEN];
+ /* Last phone-number */
+ unsigned char last_l2; /* Last layer-2 protocol */
+ unsigned char last_si; /* Last service */
+ unsigned char last_lhup; /* Last hangup local? */
+ unsigned char last_dir; /* Last direction (in or out) */
+ struct timer_list nc_timer; /* Timer for delayed NO CARRIER */
+ int send_outstanding;/* # of outstanding send-requests */
+ int xmit_size; /* max. # of chars in xmit_buf */
+ int xmit_count; /* # of chars in xmit_buf */
+ unsigned char *xmit_buf; /* transmit buffer */
+ struct sk_buff_head xmit_queue; /* transmit queue */
+ atomic_t xmit_lock; /* Semaphore for isdn_tty_write */
+#ifdef CONFIG_ISDN_AUDIO
+ int vonline; /* Voice-channel status */
+ /* Bit 0 = recording */
+ /* Bit 1 = playback */
+ /* Bit 2 = playback, DLE-ETX seen */
+ struct sk_buff_head dtmf_queue; /* queue for dtmf results */
+ void *adpcms; /* state for adpcm decompression */
+ void *adpcmr; /* state for adpcm compression */
+ void *dtmf_state; /* state for dtmf decoder */
+ void *silence_state; /* state for silence detection */
+#endif
+#ifdef CONFIG_ISDN_TTY_FAX
+ struct T30_s *fax; /* T30 Fax Group 3 data/interface */
+ int faxonline; /* Fax-channel status */
+#endif
+ struct tty_struct *tty; /* Pointer to corresponding tty */
+ atemu emu; /* AT-emulator data */
+ struct termios normal_termios; /* For saving termios structs */
+ struct termios callout_termios;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+ struct semaphore write_sem;
+} modem_info;
+
+#define ISDN_MODEM_WINSIZE 8
+
+/* Description of one ISDN-tty */
+typedef struct {
+ int refcount; /* Number of opens */
+ struct tty_driver tty_modem; /* tty-device */
+ struct tty_driver cua_modem; /* cua-device */
+ struct tty_struct *modem_table[ISDN_MAX_CHANNELS]; /* ?? copied from Orig */
+ struct termios *modem_termios[ISDN_MAX_CHANNELS];
+ struct termios *modem_termios_locked[ISDN_MAX_CHANNELS];
+ modem_info info[ISDN_MAX_CHANNELS]; /* Private data */
+} modem;
+
+/*======================= End of ISDN-tty stuff ============================*/
+
+/*======================== Start of V.110 stuff ============================*/
+#define V110_BUFSIZE 1024
+
+typedef struct {
+ int nbytes; /* 1 Matrixbyte -> nbytes in stream */
+ int nbits; /* Number of used bits in streambyte */
+ unsigned char key; /* Bitmask in stream eg. 11 (nbits=2) */
+ int decodelen; /* Amount of data in decodebuf */
+ int SyncInit; /* Number of sync frames to send */
+ unsigned char *OnlineFrame; /* Precalculated V110 idle frame */
+ unsigned char *OfflineFrame; /* Precalculated V110 sync Frame */
+ int framelen; /* Length of frames */
+ int skbuser; /* Number of unacked userdata skbs */
+ int skbidle; /* Number of unacked idle/sync skbs */
+ int introducer; /* Local vars for decoder */
+ int dbit;
+ unsigned char b;
+ int skbres; /* space to reserve in outgoing skb */
+ int maxsize; /* maxbufsize of lowlevel driver */
+ unsigned char *encodebuf; /* temporary buffer for encoding */
+ unsigned char decodebuf[V110_BUFSIZE]; /* incomplete V110 matrices */
+} isdn_v110_stream;
+
+/*========================= End of V.110 stuff =============================*/
+
+/*======================= Start of general stuff ===========================*/
+
+typedef struct {
+ char *next;
+ char *private;
+} infostruct;
+
+typedef struct isdn_module {
+ struct isdn_module *prev;
+ struct isdn_module *next;
+ char *name;
+ int (*get_free_channel)(int, int, int, int, int);
+ int (*free_channel)(int, int, int);
+ int (*status_callback)(isdn_ctrl *);
+ int (*command)(isdn_ctrl *);
+ int (*receive_callback)(int, int, struct sk_buff *);
+ int (*writebuf_skb)(int, int, int, struct sk_buff *);
+ int (*net_start_xmit)(struct sk_buff *, struct device *);
+ int (*net_receive)(struct device *, struct sk_buff *);
+ int (*net_open)(struct device *);
+ int (*net_close)(struct device *);
+ int priority;
+} isdn_module;
+
+#define DRV_FLAG_RUNNING 1
+#define DRV_FLAG_REJBUS 2
+#define DRV_FLAG_LOADED 4
+
+/* Description of hardware-level-driver */
+typedef struct {
+ ulong online; /* Channel-Online flags */
+ ulong flags; /* Misc driver Flags */
+ int locks; /* Number of locks for this driver */
+ int channels; /* Number of channels */
+ struct wait_queue *st_waitq; /* Wait-Queue for status-read's */
+ int maxbufsize; /* Maximum Buffersize supported */
+ unsigned long pktcount; /* Until now: unused */
+ int stavail; /* Chars avail on Status-device */
+ isdn_if *interface; /* Interface to driver */
+ int *rcverr; /* Error-counters for B-Ch.-receive */
+ int *rcvcount; /* Byte-counters for B-Ch.-receive */
+#ifdef CONFIG_ISDN_AUDIO
+ unsigned long DLEflag; /* Flags: Insert DLE at next read */
+#endif
+ struct sk_buff_head *rpqueue; /* Pointers to start of Rcv-Queue */
+ struct wait_queue **rcv_waitq; /* Wait-Queues for B-Channel-Reads */
+ struct wait_queue **snd_waitq; /* Wait-Queue for B-Channel-Send's */
+ char msn2eaz[10][ISDN_MSNLEN]; /* Mapping-Table MSN->EAZ */
+} driver;
+
+/* Main driver-data */
+typedef struct isdn_devt {
+ unsigned short flags; /* Bitmapped Flags: */
+ /* */
+ int drivers; /* Current number of drivers */
+ int channels; /* Current number of channels */
+ int net_verbose; /* Verbose-Flag */
+ int modempoll; /* Flag: tty-read active */
+ int tflags; /* Timer-Flags: */
+ /* see ISDN_TIMER_..defines */
+ int global_flags;
+ infostruct *infochain; /* List of open info-devs. */
+ struct wait_queue *info_waitq; /* Wait-Queue for isdninfo */
+ struct timer_list timer; /* Misc.-function Timer */
+ int chanmap[ISDN_MAX_CHANNELS];/* Map minor->device-channel */
+ int drvmap[ISDN_MAX_CHANNELS]; /* Map minor->driver-index */
+ int usage[ISDN_MAX_CHANNELS]; /* Used by tty/ip/voice */
+ char num[ISDN_MAX_CHANNELS][ISDN_MSNLEN];
+ /* Remote number of active ch.*/
+ int m_idx[ISDN_MAX_CHANNELS]; /* Index for mdm.... */
+ driver *drv[ISDN_MAX_DRIVERS]; /* Array of drivers */
+ isdn_net_dev *netdev; /* Linked list of net-if's */
+ char drvid[ISDN_MAX_DRIVERS][20];/* Driver-ID */
+ struct task_struct *profd; /* For iprofd */
+ modem mdm; /* tty-driver-data */
+ isdn_net_dev *rx_netdev[ISDN_MAX_CHANNELS]; /* rx netdev-pointers */
+ isdn_net_dev *st_netdev[ISDN_MAX_CHANNELS]; /* stat netdev-pointers */
+ ulong ibytes[ISDN_MAX_CHANNELS]; /* Statistics incoming bytes */
+ ulong obytes[ISDN_MAX_CHANNELS]; /* Statistics outgoing bytes */
+ int v110emu[ISDN_MAX_CHANNELS];/* V.110 emulator-mode 0=none */
+ atomic_t v110use[ISDN_MAX_CHANNELS];/* Usage-Semaphore for stream */
+ isdn_v110_stream *v110[ISDN_MAX_CHANNELS]; /* V.110 private data */
+ struct semaphore sem; /* serialize list access*/
+ isdn_module *modules;
+} isdn_dev;
+
+extern isdn_dev *dev;
+
+
+
+/* Utility-Macros */
+#define MIN(a,b) ((a<b)?a:b)
+#define MAX(a,b) ((a>b)?a:b)
+#endif /* __KERNEL__ */
+#endif /* isdn_h */
diff --git a/pfinet/linux-src/include/linux/isdn_divertif.h b/pfinet/linux-src/include/linux/isdn_divertif.h
new file mode 100644
index 00000000..2892d021
--- /dev/null
+++ b/pfinet/linux-src/include/linux/isdn_divertif.h
@@ -0,0 +1,62 @@
+/*
+ * $Id: isdn_divertif.h,v 1.3 1999/07/05 20:22:00 werner Exp $
+ *
+ * Header for the diversion supplementary interface for i4l.
+ *
+ * Copyright 1998 by Werner Cornelius (werner@isdn4linux.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: isdn_divertif.h,v $
+ * Revision 1.3 1999/07/05 20:22:00 werner
+ * changes to use diversion sources for all kernel versions.
+ * removed static device, only proc filesystem used
+ *
+ * Revision 1.2 1999/07/04 21:38:38 werner
+ * ported from kernel version 2.0
+ *
+ *
+ */
+
+
+/***********************************************************/
+/* magic value is also used to control version information */
+/***********************************************************/
+#define DIVERT_IF_MAGIC 0x25873401
+#define DIVERT_CMD_REG 0x00 /* register command */
+#define DIVERT_CMD_REL 0x01 /* release command */
+#define DIVERT_NO_ERR 0x00 /* return value no error */
+#define DIVERT_CMD_ERR 0x01 /* invalid cmd */
+#define DIVERT_VER_ERR 0x02 /* magic/version invalid */
+#define DIVERT_REG_ERR 0x03 /* module already registered */
+#define DIVERT_REL_ERR 0x04 /* module not registered */
+#define DIVERT_REG_NAME isdn_register_divert
+
+/***************************************************************/
+/* structure exchanging data between isdn hl and divert module */
+/***************************************************************/
+typedef struct
+ { ulong if_magic; /* magic info and version */
+ int cmd; /* command */
+ int (*stat_callback)(isdn_ctrl *); /* supplied by divert module when calling */
+ int (*ll_cmd)(isdn_ctrl *); /* supplied by hl on return */
+ char * (*drv_to_name)(int); /* map a driver id to name, supplied by hl */
+ int (*name_to_drv)(char *); /* map a driver id to name, supplied by hl */
+ } isdn_divert_if;
+
+/*********************/
+/* function register */
+/*********************/
+extern int DIVERT_REG_NAME(isdn_divert_if *);
diff --git a/pfinet/linux-src/include/linux/isdn_ppp.h b/pfinet/linux-src/include/linux/isdn_ppp.h
new file mode 100644
index 00000000..e7682fb3
--- /dev/null
+++ b/pfinet/linux-src/include/linux/isdn_ppp.h
@@ -0,0 +1,236 @@
+/* -*- mode: c; c-basic-offset: 2 -*- */
+
+#ifndef _LINUX_ISDN_PPP_H
+#define _LINUX_ISDN_PPP_H
+
+#include <linux/config.h>
+
+#define CALLTYPE_INCOMING 0x1
+#define CALLTYPE_OUTGOING 0x2
+#define CALLTYPE_CALLBACK 0x4
+
+#define IPPP_VERSION "2.2.0"
+
+struct pppcallinfo
+{
+ int calltype;
+ unsigned char local_num[64];
+ unsigned char remote_num[64];
+ int charge_units;
+};
+
+#define PPPIOCGCALLINFO _IOWR('t',128,struct pppcallinfo)
+#define PPPIOCBUNDLE _IOW('t',129,int)
+#define PPPIOCGMPFLAGS _IOR('t',130,int)
+#define PPPIOCSMPFLAGS _IOW('t',131,int)
+#define PPPIOCSMPMTU _IOW('t',132,int)
+#define PPPIOCSMPMRU _IOW('t',133,int)
+#define PPPIOCGCOMPRESSORS _IOR('t',134,unsigned long [8])
+#define PPPIOCSCOMPRESSOR _IOW('t',135,int)
+#define PPPIOCGIFNAME _IOR('t',136, char [IFNAMSIZ] )
+
+#define PPP_MP 0x003d
+#define PPP_LINK_COMP 0x00fb
+#define PPP_LINK_CCP 0x80fb
+
+#define SC_MP_PROT 0x00000200
+#define SC_REJ_MP_PROT 0x00000400
+#define SC_OUT_SHORT_SEQ 0x00000800
+#define SC_IN_SHORT_SEQ 0x00004000
+
+#define SC_DECOMP_ON 0x01
+#define SC_COMP_ON 0x02
+#define SC_DECOMP_DISCARD 0x04
+#define SC_COMP_DISCARD 0x08
+#define SC_LINK_DECOMP_ON 0x10
+#define SC_LINK_COMP_ON 0x20
+#define SC_LINK_DECOMP_DISCARD 0x40
+#define SC_LINK_COMP_DISCARD 0x80
+
+#define DECOMP_ERR_NOMEM (-10)
+
+#define MP_END_FRAG 0x40
+#define MP_BEGIN_FRAG 0x80
+
+#define ISDN_PPP_COMP_MAX_OPTIONS 16
+
+#define IPPP_COMP_FLAG_XMIT 0x1
+#define IPPP_COMP_FLAG_LINK 0x2
+
+struct isdn_ppp_comp_data {
+ int num;
+ unsigned char options[ISDN_PPP_COMP_MAX_OPTIONS];
+ int optlen;
+ int flags;
+};
+
+#ifdef __KERNEL__
+
+/*
+ * We need a way for the decompressor to influence the generation of CCP
+ * Reset-Requests in a variety of ways. The decompressor is already returning
+ * a lot of information (generated skb length, error conditions) so we use
+ * another parameter. This parameter is a pointer to a structure which is
+ * to be marked valid by the decompressor and only in this case is ever used.
+ * Furthermore, the only case where this data is used is when the decom-
+ * pressor returns DECOMP_ERROR.
+ *
+ * We use this same struct for the reset entry of the compressor to commu-
+ * nicate to its caller how to deal with sending of a Reset Ack. In this
+ * case, expra is not used, but other options still apply (suppressing
+ * sending with rsend, appending arbitrary data, etc).
+ */
+
+#define IPPP_RESET_MAXDATABYTES 32
+
+struct isdn_ppp_resetparams {
+ unsigned char valid:1; /* rw Is this structure filled at all ? */
+ unsigned char rsend:1; /* rw Should we send one at all ? */
+ unsigned char idval:1; /* rw Is the id field valid ? */
+ unsigned char dtval:1; /* rw Is the data field valid ? */
+ unsigned char expra:1; /* rw Is an Ack expected for this Req ? */
+ unsigned char id; /* wo Send CCP ResetReq with this id */
+ unsigned short maxdlen; /* ro Max bytes to be stored in data field */
+ unsigned short dlen; /* rw Bytes stored in data field */
+ unsigned char *data; /* wo Data for ResetReq info field */
+};
+
+/*
+ * this is an 'old friend' from ppp-comp.h under a new name
+ * check the original include for more information
+ */
+struct isdn_ppp_compressor {
+ struct isdn_ppp_compressor *next, *prev;
+ int num; /* CCP compression protocol number */
+
+ void *(*alloc) (struct isdn_ppp_comp_data *);
+ void (*free) (void *state);
+ int (*init) (void *state, struct isdn_ppp_comp_data *,
+ int unit,int debug);
+
+ /* The reset entry needs to get more exact information about the
+ ResetReq or ResetAck it was called with. The parameters are
+ obvious. If reset is called without a Req or Ack frame which
+ could be handed into it, code MUST be set to 0. Using rsparm,
+ the reset entry can control if and how a ResetAck is returned. */
+
+ void (*reset) (void *state, unsigned char code, unsigned char id,
+ unsigned char *data, unsigned len,
+ struct isdn_ppp_resetparams *rsparm);
+
+ int (*compress) (void *state, struct sk_buff *in,
+ struct sk_buff *skb_out, int proto);
+
+ int (*decompress) (void *state,struct sk_buff *in,
+ struct sk_buff *skb_out,
+ struct isdn_ppp_resetparams *rsparm);
+
+ void (*incomp) (void *state, struct sk_buff *in,int proto);
+ void (*stat) (void *state, struct compstat *stats);
+};
+
+extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *);
+extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *);
+extern int isdn_ppp_dial_slave(char *);
+extern int isdn_ppp_hangup_slave(char *);
+
+struct ippp_bundle {
+ int mp_mrru; /* unused */
+ struct mpqueue *last; /* currently defined in isdn_net_dev */
+ int min; /* currently calculated 'on the fly' */
+ long next_num; /* we wanna see this seq.-number next */
+ struct sqqueue *sq;
+ int modify:1; /* set to 1 while modifying sqqueue */
+ int bundled:1; /* bundle active ? */
+};
+
+#define NUM_RCV_BUFFS 64
+
+struct sqqueue {
+ struct sqqueue *next;
+ long sqno_start;
+ long sqno_end;
+ struct sk_buff *skb;
+ long timer;
+};
+
+struct mpqueue {
+ struct mpqueue *next;
+ struct mpqueue *last;
+ long sqno;
+ struct sk_buff *skb;
+ int BEbyte;
+ unsigned long time;
+};
+
+struct ippp_buf_queue {
+ struct ippp_buf_queue *next;
+ struct ippp_buf_queue *last;
+ char *buf; /* NULL here indicates end of queue */
+ int len;
+};
+
+/* The data structure for one CCP reset transaction */
+enum ippp_ccp_reset_states {
+ CCPResetIdle,
+ CCPResetSentReq,
+ CCPResetRcvdReq,
+ CCPResetSentAck,
+ CCPResetRcvdAck
+};
+
+struct ippp_ccp_reset_state {
+ enum ippp_ccp_reset_states state; /* State of this transaction */
+ struct ippp_struct *is; /* Backlink to device stuff */
+ unsigned char id; /* Backlink id index */
+ unsigned char ta:1; /* The timer is active (flag) */
+ unsigned char expra:1; /* We expect a ResetAck at all */
+ int dlen; /* Databytes stored in data */
+ struct timer_list timer; /* For timeouts/retries */
+ /* This is a hack but seems sufficient for the moment. We do not want
+ to have this be yet another allocation for some bytes, it is more
+ memory management overhead than the whole mess is worth. */
+ unsigned char data[IPPP_RESET_MAXDATABYTES];
+};
+
+/* The data structure keeping track of the currently outstanding CCP Reset
+ transactions. */
+struct ippp_ccp_reset {
+ struct ippp_ccp_reset_state *rs[256]; /* One per possible id */
+ unsigned char lastid; /* Last id allocated by the engine */
+};
+
+struct ippp_struct {
+ struct ippp_struct *next_link;
+ int state;
+ struct ippp_buf_queue rq[NUM_RCV_BUFFS]; /* packet queue for isdn_ppp_read() */
+ struct ippp_buf_queue *first; /* pointer to (current) first packet */
+ struct ippp_buf_queue *last; /* pointer to (current) last used packet in queue */
+ struct wait_queue *wq;
+ struct task_struct *tk;
+ unsigned int mpppcfg;
+ unsigned int pppcfg;
+ unsigned int mru;
+ unsigned int mpmru;
+ unsigned int mpmtu;
+ unsigned int maxcid;
+ struct isdn_net_local_s *lp;
+ int unit;
+ int minor;
+ long last_link_seqno;
+ long mp_seqno;
+ long range;
+#ifdef CONFIG_ISDN_PPP_VJ
+ unsigned char *cbuf;
+ struct slcompress *slcomp;
+#endif
+ unsigned long debug;
+ struct isdn_ppp_compressor *compressor,*decompressor;
+ struct isdn_ppp_compressor *link_compressor,*link_decompressor;
+ void *decomp_stat,*comp_stat,*link_decomp_stat,*link_comp_stat;
+ struct ippp_ccp_reset *reset; /* Allocated on demand, may never be needed */
+ unsigned long compflags;
+};
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_ISDN_PPP_H */
diff --git a/pfinet/linux-src/include/linux/isdnif.h b/pfinet/linux-src/include/linux/isdnif.h
new file mode 100644
index 00000000..7380b326
--- /dev/null
+++ b/pfinet/linux-src/include/linux/isdnif.h
@@ -0,0 +1,631 @@
+/* $Id: isdnif.h,v 1.32 1999/10/11 22:03:00 keil Exp $
+ *
+ * Linux ISDN subsystem
+ *
+ * Definition of the interface between the subsystem and its low-level drivers.
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96 Thinking Objects Software GmbH Wuerzburg
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: isdnif.h,v $
+ * Revision 1.32 1999/10/11 22:03:00 keil
+ * COMPAT_NEED_UACCESS (no include in isdn_compat.h)
+ *
+ * Revision 1.31 1999/09/06 07:29:36 fritz
+ * Changed my mail-address.
+ *
+ * Revision 1.30 1999/08/23 15:54:29 keil
+ * more backported changes from kernel 2.3.14
+ *
+ * Revision 1.29 1999/07/31 13:00:02 armin
+ * Added tty fax capabilities.
+ *
+ * Revision 1.28 1999/07/13 20:57:48 werner
+ * added callback ISDN_STAT_DISCH for limiting b-channel resources.
+ *
+ * Revision 1.27 1999/07/11 17:07:39 armin
+ * Added tty modem register S23.
+ * Added new layer 2 and 3 protocols for Fax and DSP functions.
+ *
+ * Revision 1.26 1999/07/01 08:35:44 keil
+ * compatibility to 2.3
+ *
+ * Revision 1.25 1998/06/17 19:51:55 he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.24 1998/03/19 13:18:57 keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
+ * Revision 1.23 1998/02/20 17:36:52 fritz
+ * Added L2-protocols for V.110, changed FEATURE-Flag-constants.
+ *
+ * Revision 1.22 1998/01/31 22:14:12 keil
+ * changes for 2.1.82
+ *
+ * Revision 1.21 1997/10/09 21:28:13 fritz
+ * New HL<->LL interface:
+ * New BSENT callback with nr. of bytes included.
+ * Sending without ACK.
+ * New L1 error status (not yet in use).
+ * Cleaned up obsolete structures.
+ * Implemented Cisco-SLARP.
+ * Changed local net-interface data to be dynamically allocated.
+ * Removed old 2.0 compatibility stuff.
+ *
+ * Revision 1.20 1997/05/27 15:18:06 fritz
+ * Added changes for recent 2.1.x kernels:
+ * changed return type of isdn_close
+ * queue_task_* -> queue_task
+ * clear/set_bit -> test_and_... where appropriate.
+ * changed type of hard_header_cache parameter.
+ *
+ * Revision 1.19 1997/03/25 23:13:56 keil
+ * NI-1 US protocol
+ *
+ * Revision 1.18 1997/03/04 22:09:18 calle
+ * Change macros copy_from_user and copy_to_user in inline function.
+ * These are now correct replacements of the functions for 2.1.xx
+ *
+ * Revision 1.17 1997/02/10 21:12:53 fritz
+ * More setup-interface changes.
+ *
+ * Revision 1.16 1997/02/10 19:42:57 fritz
+ * New interface for reporting incoming calls.
+ *
+ * Revision 1.15 1997/02/09 00:18:42 keil
+ * leased line support
+ *
+ * Revision 1.14 1997/02/03 23:43:00 fritz
+ * Misc changes for Kernel 2.1.X compatibility.
+ *
+ * Revision 1.13 1996/11/13 02:39:59 fritz
+ * More compatibility changes.
+ *
+ * Revision 1.12 1996/11/06 17:38:48 keil
+ * more changes for 2.1.X
+ *
+ * Revision 1.11 1996/10/23 11:59:42 fritz
+ * More compatibility changes.
+ *
+ * Revision 1.10 1996/10/22 23:14:19 fritz
+ * Changes for compatibility to 2.0.X and 2.1.X kernels.
+ *
+ * Revision 1.9 1996/06/06 21:24:24 fritz
+ * Started adding support for suspend/resume.
+ *
+ * Revision 1.8 1996/05/18 01:45:37 fritz
+ * More spelling corrections.
+ *
+ * Revision 1.7 1996/05/18 01:37:19 fritz
+ * Added spelling corrections and some minor changes
+ * to stay in sync with kernel.
+ *
+ * Revision 1.6 1996/05/17 03:59:28 fritz
+ * Marked rcvcallb and writebuf obsolete.
+ *
+ * Revision 1.5 1996/05/01 11:43:54 fritz
+ * Removed STANDALONE
+ *
+ * Revision 1.4 1996/05/01 11:38:40 fritz
+ * Added ISDN_FEATURE_L2_TRANS
+ *
+ * Revision 1.3 1996/04/29 22:57:54 fritz
+ * Added driverId and channel parameters to
+ * writecmd() and readstat().
+ * Added constant for voice-support.
+ *
+ * Revision 1.2 1996/04/20 17:02:40 fritz
+ * Changes to support skbuffs for Lowlevel-Drivers.
+ * Misc. typos
+ *
+ * Revision 1.1 1996/01/09 05:50:51 fritz
+ * Initial revision
+ *
+ */
+
+#ifndef isdnif_h
+#define isdnif_h
+
+#include <linux/config.h>
+
+/*
+ * Values for general protocol-selection
+ */
+#define ISDN_PTYPE_UNKNOWN 0 /* Protocol undefined */
+#define ISDN_PTYPE_1TR6 1 /* german 1TR6-protocol */
+#define ISDN_PTYPE_EURO 2 /* EDSS1-protocol */
+#define ISDN_PTYPE_LEASED 3 /* for leased lines */
+#define ISDN_PTYPE_NI1 4 /* US NI-1 protocol */
+#define ISDN_PTYPE_MAX 7 /* Max. 8 Protocols */
+
+/*
+ * Values for Layer-2-protocol-selection
+ */
+#define ISDN_PROTO_L2_X75I 0 /* X75/LAPB with I-Frames */
+#define ISDN_PROTO_L2_X75UI 1 /* X75/LAPB with UI-Frames */
+#define ISDN_PROTO_L2_X75BUI 2 /* X75/LAPB with UI-Frames */
+#define ISDN_PROTO_L2_HDLC 3 /* HDLC */
+#define ISDN_PROTO_L2_TRANS 4 /* Transparent (Voice) */
+#define ISDN_PROTO_L2_X25DTE 5 /* X25/LAPB DTE mode */
+#define ISDN_PROTO_L2_X25DCE 6 /* X25/LAPB DCE mode */
+#define ISDN_PROTO_L2_V11096 7 /* V.110 bitrate adaption 9600 Baud */
+#define ISDN_PROTO_L2_V11019 8 /* V.110 bitrate adaption 19200 Baud */
+#define ISDN_PROTO_L2_V11038 9 /* V.110 bitrate adaption 38400 Baud */
+#define ISDN_PROTO_L2_MODEM 10 /* Analog Modem on Board */
+#define ISDN_PROTO_L2_FAX 11 /* Fax Group 2/3 */
+#define ISDN_PROTO_L2_MAX 15 /* Max. 16 Protocols */
+
+/*
+ * Values for Layer-3-protocol-selection
+ */
+#define ISDN_PROTO_L3_TRANS 0 /* Transparent */
+#define ISDN_PROTO_L3_TRANSDSP 1 /* Transparent with DSP */
+#define ISDN_PROTO_L3_FAX 2 /* Fax Group 2/3 */
+#define ISDN_PROTO_L3_MAX 7 /* Max. 8 Protocols */
+
+#ifdef __KERNEL__
+
+#include <linux/skbuff.h>
+
+/***************************************************************************/
+/* Extensions made by Werner Cornelius (werner@ikt.de) */
+/* */
+/* The proceed command holds a incoming call in a state to leave processes */
+/* enough time to check whether ist should be accepted. */
+/* The PROT_IO Command extends the interface to make protocol dependent */
+/* features available (call diversion, call waiting...). */
+/* */
+/* The PROT_IO Command is executed with the desired driver id and the arg */
+/* parameter coded as follows: */
+/* The lower 8 bits of arg contain the desired protocol from ISDN_PTYPE */
+/* definitions. The upper 24 bits represent the protocol specific cmd/stat.*/
+/* Any additional data is protocol and command specific. */
+/* This mechanism also applies to the statcallb callback STAT_PROT. */
+/* */
+/* This suggested extension permits an easy expansion of protocol specific */
+/* handling. Extensions may be added at any time without changing the HL */
+/* driver code and not getting conflicts without certifications. */
+/* The well known CAPI 2.0 interface handles such extensions in a similar */
+/* way. Perhaps a protocol specific module may be added and separately */
+/* loaded and linked to the basic isdn module for handling. */
+/***************************************************************************/
+
+/*****************/
+/* DSS1 commands */
+/*****************/
+#define DSS1_CMD_INVOKE ((0x00 << 8) | ISDN_PTYPE_EURO) /* invoke a supplementary service */
+#define DSS1_CMD_INVOKE_ABORT ((0x01 << 8) | ISDN_PTYPE_EURO) /* abort a invoke cmd */
+
+/*******************************/
+/* DSS1 Status callback values */
+/*******************************/
+#define DSS1_STAT_INVOKE_RES ((0x80 << 8) | ISDN_PTYPE_EURO) /* Result for invocation */
+#define DSS1_STAT_INVOKE_ERR ((0x81 << 8) | ISDN_PTYPE_EURO) /* Error Return for invocation */
+#define DSS1_STAT_INVOKE_BRD ((0x82 << 8) | ISDN_PTYPE_EURO) /* Deliver invoke broadcast info */
+
+
+/*********************************************************************/
+/* structures for DSS1 commands and callback */
+/* */
+/* An action is invoked by sending a DSS1_CMD_INVOKE. The ll_id, proc*/
+/* timeout, datalen and data fields must be set before calling. */
+/* */
+/* The return value is a positive hl_id value also delivered in the */
+/* hl_id field. A value of zero signals no more left hl_id capacitys.*/
+/* A negative return value signals errors in LL. So if the return */
+/* value is <= 0 no action in LL will be taken -> request ignored */
+/* */
+/* The timeout field must be filled with a positive value specifying */
+/* the amount of time the INVOKED process waits for a reaction from */
+/* the network. */
+/* If a response (either error or result) is received during this */
+/* intervall, a reporting callback is initiated and the process will */
+/* be deleted, the hl identifier will be freed. */
+/* If no response is received during the specified intervall, a error*/
+/* callback is initiated with timeout set to -1 and a datalen set */
+/* to 0. */
+/* If timeout is set to a value <= 0 during INVOCATION the process is*/
+/* immediately deleted after sending the data. No callback occurs ! */
+/* */
+/* A currently waiting process may be aborted with INVOKE_ABORT. No */
+/* callback will occur when a process has been aborted. */
+/* */
+/* Broadcast invoke frames from the network are reported via the */
+/* STAT_INVOKE_BRD callback. The ll_id is set to 0, the other fields */
+/* are supplied by the network and not by the HL. */
+/*********************************************************************/
+typedef struct
+ { ulong ll_id; /* ID supplied by LL when executing */
+ /* a command and returned by HL for */
+ /* INVOKE_RES and INVOKE_ERR */
+ int hl_id; /* ID supplied by HL when called */
+ /* for executing a cmd and delivered */
+ /* for results and errors */
+ /* must be supplied by LL when aborting*/
+ int proc; /* invoke procedure used by CMD_INVOKE */
+ /* returned by callback and broadcast */
+ int timeout; /* timeout for INVOKE CMD in ms */
+ /* -1 in stat callback when timed out */
+ /* error value when error callback */
+ int datalen; /* length of cmd or stat data */
+ u_char *data;/* pointer to data delivered or send */
+ } dss1_cmd_stat;
+
+/*
+ * Commands from linklevel to lowlevel
+ *
+ */
+#define ISDN_CMD_IOCTL 0 /* Perform ioctl */
+#define ISDN_CMD_DIAL 1 /* Dial out */
+#define ISDN_CMD_ACCEPTD 2 /* Accept an incoming call on D-Chan. */
+#define ISDN_CMD_ACCEPTB 3 /* Request B-Channel connect. */
+#define ISDN_CMD_HANGUP 4 /* Hangup */
+#define ISDN_CMD_CLREAZ 5 /* Clear EAZ(s) of channel */
+#define ISDN_CMD_SETEAZ 6 /* Set EAZ(s) of channel */
+#define ISDN_CMD_GETEAZ 7 /* Get EAZ(s) of channel */
+#define ISDN_CMD_SETSIL 8 /* Set Service-Indicator-List of channel */
+#define ISDN_CMD_GETSIL 9 /* Get Service-Indicator-List of channel */
+#define ISDN_CMD_SETL2 10 /* Set B-Chan. Layer2-Parameter */
+#define ISDN_CMD_GETL2 11 /* Get B-Chan. Layer2-Parameter */
+#define ISDN_CMD_SETL3 12 /* Set B-Chan. Layer3-Parameter */
+#define ISDN_CMD_GETL3 13 /* Get B-Chan. Layer3-Parameter */
+#define ISDN_CMD_LOCK 14 /* Signal usage by upper levels */
+#define ISDN_CMD_UNLOCK 15 /* Release usage-lock */
+#define ISDN_CMD_SUSPEND 16 /* Suspend connection */
+#define ISDN_CMD_RESUME 17 /* Resume connection */
+#define ISDN_CMD_PROCEED 18 /* Proceed with call establishment */
+#define ISDN_CMD_ALERT 19 /* Alert after Proceeding */
+#define ISDN_CMD_REDIR 20 /* Redir a incoming call */
+#define ISDN_CMD_PROT_IO 21 /* Protocol specific commands */
+#define CAPI_PUT_MESSAGE 22 /* CAPI message send down or up */
+#define ISDN_CMD_FAXCMD 23 /* FAX commands to HL-driver */
+#define ISDN_CMD_AUDIO 24 /* DSP, DTMF, ... settings */
+
+/*
+ * Status-Values delivered from lowlevel to linklevel via
+ * statcallb().
+ *
+ */
+#define ISDN_STAT_STAVAIL 256 /* Raw status-data available */
+#define ISDN_STAT_ICALL 257 /* Incoming call detected */
+#define ISDN_STAT_RUN 258 /* Signal protocol-code is running */
+#define ISDN_STAT_STOP 259 /* Signal halt of protocol-code */
+#define ISDN_STAT_DCONN 260 /* Signal D-Channel connect */
+#define ISDN_STAT_BCONN 261 /* Signal B-Channel connect */
+#define ISDN_STAT_DHUP 262 /* Signal D-Channel disconnect */
+#define ISDN_STAT_BHUP 263 /* Signal B-Channel disconnect */
+#define ISDN_STAT_CINF 264 /* Charge-Info */
+#define ISDN_STAT_LOAD 265 /* Signal new lowlevel-driver is loaded */
+#define ISDN_STAT_UNLOAD 266 /* Signal unload of lowlevel-driver */
+#define ISDN_STAT_BSENT 267 /* Signal packet sent */
+#define ISDN_STAT_NODCH 268 /* Signal no D-Channel */
+#define ISDN_STAT_ADDCH 269 /* Add more Channels */
+#define ISDN_STAT_CAUSE 270 /* Cause-Message */
+#define ISDN_STAT_ICALLW 271 /* Incoming call without B-chan waiting */
+#define ISDN_STAT_REDIR 272 /* Redir result */
+#define ISDN_STAT_PROT 273 /* protocol IO specific callback */
+#define ISDN_STAT_DISPLAY 274 /* deliver a received display message */
+#define ISDN_STAT_L1ERR 275 /* Signal Layer-1 Error */
+#define ISDN_STAT_FAXIND 276 /* FAX indications from HL-driver */
+#define ISDN_STAT_AUDIO 277 /* DTMF, DSP indications */
+#define ISDN_STAT_DISCH 278 /* Disable/Enable channel usage */
+
+/*
+ * Audio commands
+ */
+#define ISDN_AUDIO_SETDD 0 /* Set DTMF detection */
+#define ISDN_AUDIO_DTMF 1 /* Rx/Tx DTMF */
+
+/*
+ * Values for errcode field
+ */
+#define ISDN_STAT_L1ERR_SEND 1
+#define ISDN_STAT_L1ERR_RECV 2
+
+/*
+ * Values for feature-field of interface-struct.
+ */
+/* Layer 2 */
+#define ISDN_FEATURE_L2_X75I (0x0001 << ISDN_PROTO_L2_X75I)
+#define ISDN_FEATURE_L2_X75UI (0x0001 << ISDN_PROTO_L2_X75UI)
+#define ISDN_FEATURE_L2_X75BUI (0x0001 << ISDN_PROTO_L2_X75BUI)
+#define ISDN_FEATURE_L2_HDLC (0x0001 << ISDN_PROTO_L2_HDLC)
+#define ISDN_FEATURE_L2_TRANS (0x0001 << ISDN_PROTO_L2_TRANS)
+#define ISDN_FEATURE_L2_X25DTE (0x0001 << ISDN_PROTO_L2_X25DTE)
+#define ISDN_FEATURE_L2_X25DCE (0x0001 << ISDN_PROTO_L2_X25DCE)
+#define ISDN_FEATURE_L2_V11096 (0x0001 << ISDN_PROTO_L2_V11096)
+#define ISDN_FEATURE_L2_V11019 (0x0001 << ISDN_PROTO_L2_V11019)
+#define ISDN_FEATURE_L2_V11038 (0x0001 << ISDN_PROTO_L2_V11038)
+#define ISDN_FEATURE_L2_MODEM (0x0001 << ISDN_PROTO_L2_MODEM)
+#define ISDN_FEATURE_L2_FAX (0x0001 << ISDN_PROTO_L2_FAX)
+
+#define ISDN_FEATURE_L2_MASK (0x0FFFF) /* Max. 16 protocols */
+#define ISDN_FEATURE_L2_SHIFT (0)
+
+/* Layer 3 */
+#define ISDN_FEATURE_L3_TRANS (0x10000 << ISDN_PROTO_L3_TRANS)
+#define ISDN_FEATURE_L3_TRANSDSP (0x10000 << ISDN_PROTO_L3_TRANSDSP)
+#define ISDN_FEATURE_L3_FAX (0x10000 << ISDN_PROTO_L3_FAX)
+
+#define ISDN_FEATURE_L3_MASK (0x0FF0000) /* Max. 8 Protocols */
+#define ISDN_FEATURE_L3_SHIFT (16)
+
+/* Signaling */
+#define ISDN_FEATURE_P_UNKNOWN (0x1000000 << ISDN_PTYPE_UNKNOWN)
+#define ISDN_FEATURE_P_1TR6 (0x1000000 << ISDN_PTYPE_1TR6)
+#define ISDN_FEATURE_P_EURO (0x1000000 << ISDN_PTYPE_EURO)
+#define ISDN_FEATURE_P_NI1 (0x1000000 << ISDN_PTYPE_NI1)
+
+#define ISDN_FEATURE_P_MASK (0x0FF000000) /* Max. 8 Protocols */
+#define ISDN_FEATURE_P_SHIFT (24)
+
+typedef struct setup_parm {
+ unsigned char phone[32]; /* Remote Phone-Number */
+ unsigned char eazmsn[32]; /* Local EAZ or MSN */
+ unsigned char si1; /* Service Indicator 1 */
+ unsigned char si2; /* Service Indicator 2 */
+ unsigned char plan; /* Numbering plan */
+ unsigned char screen; /* Screening info */
+} setup_parm;
+
+
+#ifdef CONFIG_ISDN_TTY_FAX
+/* T.30 Fax G3 */
+
+#define FAXIDLEN 21
+
+typedef struct T30_s {
+ /* session parameters */
+ __u8 resolution __attribute__ ((packed));
+ __u8 rate __attribute__ ((packed));
+ __u8 width __attribute__ ((packed));
+ __u8 length __attribute__ ((packed));
+ __u8 compression __attribute__ ((packed));
+ __u8 ecm __attribute__ ((packed));
+ __u8 binary __attribute__ ((packed));
+ __u8 scantime __attribute__ ((packed));
+ __u8 id[FAXIDLEN] __attribute__ ((packed));
+ /* additional parameters */
+ __u8 phase __attribute__ ((packed));
+ __u8 direction __attribute__ ((packed));
+ __u8 code __attribute__ ((packed));
+ __u8 badlin __attribute__ ((packed));
+ __u8 badmul __attribute__ ((packed));
+ __u8 bor __attribute__ ((packed));
+ __u8 fet __attribute__ ((packed));
+ __u8 pollid[FAXIDLEN] __attribute__ ((packed));
+ __u8 cq __attribute__ ((packed));
+ __u8 cr __attribute__ ((packed));
+ __u8 ctcrty __attribute__ ((packed));
+ __u8 minsp __attribute__ ((packed));
+ __u8 phcto __attribute__ ((packed));
+ __u8 rel __attribute__ ((packed));
+ __u8 nbc __attribute__ ((packed));
+ /* remote station parameters */
+ __u8 r_resolution __attribute__ ((packed));
+ __u8 r_rate __attribute__ ((packed));
+ __u8 r_width __attribute__ ((packed));
+ __u8 r_length __attribute__ ((packed));
+ __u8 r_compression __attribute__ ((packed));
+ __u8 r_ecm __attribute__ ((packed));
+ __u8 r_binary __attribute__ ((packed));
+ __u8 r_scantime __attribute__ ((packed));
+ __u8 r_id[FAXIDLEN] __attribute__ ((packed));
+ __u8 r_code __attribute__ ((packed));
+} T30_s;
+
+#define ISDN_TTY_FAX_CONN_IN 0
+#define ISDN_TTY_FAX_CONN_OUT 1
+
+#define ISDN_TTY_FAX_FCON 0
+#define ISDN_TTY_FAX_DIS 1
+#define ISDN_TTY_FAX_FTT 2
+#define ISDN_TTY_FAX_MCF 3
+#define ISDN_TTY_FAX_DCS 4
+#define ISDN_TTY_FAX_TRAIN_OK 5
+#define ISDN_TTY_FAX_EOP 6
+#define ISDN_TTY_FAX_EOM 7
+#define ISDN_TTY_FAX_MPS 8
+#define ISDN_TTY_FAX_DTC 9
+#define ISDN_TTY_FAX_RID 10
+#define ISDN_TTY_FAX_HNG 11
+#define ISDN_TTY_FAX_DT 12
+#define ISDN_TTY_FAX_FCON_I 13
+#define ISDN_TTY_FAX_DR 14
+#define ISDN_TTY_FAX_ET 15
+#define ISDN_TTY_FAX_CFR 16
+#define ISDN_TTY_FAX_PTS 17
+#define ISDN_TTY_FAX_SENT 18
+
+#define ISDN_FAX_PHASE_IDLE 0
+#define ISDN_FAX_PHASE_A 1
+#define ISDN_FAX_PHASE_B 2
+#define ISDN_FAX_PHASE_C 3
+#define ISDN_FAX_PHASE_D 4
+#define ISDN_FAX_PHASE_E 5
+
+#endif /* TTY_FAX */
+
+/* CAPI structs */
+
+/* this is compatible to the old union size */
+#define MAX_CAPI_PARA_LEN 50
+
+typedef struct {
+ /* Header */
+ __u16 Length;
+ __u16 ApplId;
+ __u8 Command;
+ __u8 Subcommand;
+ __u16 Messagenumber;
+
+ /* Parameter */
+ union {
+ __u32 Controller;
+ __u32 PLCI;
+ __u32 NCCI;
+ } adr;
+ __u8 para[MAX_CAPI_PARA_LEN];
+} capi_msg;
+
+/*
+ * Structure for exchanging above infos
+ *
+ */
+typedef struct {
+ int driver; /* Lowlevel-Driver-ID */
+ int command; /* Command or Status (see above) */
+ ulong arg; /* Additional Data */
+ union {
+ ulong errcode; /* Type of error with STAT_L1ERR */
+ int length; /* Amount of bytes sent with STAT_BSENT */
+ u_char num[50];/* Additional Data */
+ setup_parm setup;/* For SETUP msg */
+ capi_msg cmsg; /* For CAPI like messages */
+ char display[85];/* display message data */
+ dss1_cmd_stat dss1_io; /* DSS1 IO-parameter/result */
+#ifdef CONFIG_ISDN_TTY_FAX
+ T30_s *fax; /* Pointer to ttys fax struct */
+#endif
+ } parm;
+} isdn_ctrl;
+
+/*
+ * The interface-struct itself (initialized at load-time of lowlevel-driver)
+ *
+ * See Documentation/isdn/INTERFACE for a description, how the communication
+ * between the ISDN subsystem and its drivers is done.
+ *
+ */
+typedef struct {
+ /* Number of channels supported by this driver
+ */
+ int channels;
+
+ /*
+ * Maximum Size of transmit/receive-buffer this driver supports.
+ */
+ int maxbufsize;
+
+ /* Feature-Flags for this driver.
+ * See defines ISDN_FEATURE_... for Values
+ */
+ unsigned long features;
+
+ /*
+ * Needed for calculating
+ * dev->hard_header_len = linklayer header + hl_hdrlen;
+ * Drivers, not supporting sk_buff's should set this to 0.
+ */
+ unsigned short hl_hdrlen;
+
+ /*
+ * Receive-Callback using sk_buff's
+ * Parameters:
+ * int Driver-ID
+ * int local channel-number (0 ...)
+ * struct sk_buff *skb received Data
+ */
+ void (*rcvcallb_skb)(int, int, struct sk_buff *);
+
+ /* Status-Callback
+ * Parameters:
+ * isdn_ctrl*
+ * driver = Driver ID.
+ * command = One of above ISDN_STAT_... constants.
+ * arg = depending on status-type.
+ * num = depending on status-type.
+ */
+ int (*statcallb)(isdn_ctrl*);
+
+ /* Send command
+ * Parameters:
+ * isdn_ctrl*
+ * driver = Driver ID.
+ * command = One of above ISDN_CMD_... constants.
+ * arg = depending on command.
+ * num = depending on command.
+ */
+ int (*command)(isdn_ctrl*);
+
+ /*
+ * Send data using sk_buff's
+ * Parameters:
+ * int driverId
+ * int local channel-number (0...)
+ * int Flag: Need ACK for this packet.
+ * struct sk_buff *skb Data to send
+ */
+ int (*writebuf_skb) (int, int, int, struct sk_buff *);
+
+ /* Send raw D-Channel-Commands
+ * Parameters:
+ * u_char pointer data
+ * int length of data
+ * int Flag: 0 = Call form Kernel-Space (use memcpy,
+ * no schedule allowed)
+ * 1 = Data is in User-Space (use memcpy_fromfs,
+ * may schedule)
+ * int driverId
+ * int local channel-number (0 ...)
+ */
+ int (*writecmd)(const u_char*, int, int, int, int);
+
+ /* Read raw Status replies
+ * u_char pointer data (volatile)
+ * int length of buffer
+ * int Flag: 0 = Call form Kernel-Space (use memcpy,
+ * no schedule allowed)
+ * 1 = Data is in User-Space (use memcpy_fromfs,
+ * may schedule)
+ * int driverId
+ * int local channel-number (0 ...)
+ */
+ int (*readstat)(u_char*, int, int, int, int);
+
+ char id[20];
+} isdn_if;
+
+/*
+ * Function which must be called by lowlevel-driver at loadtime with
+ * the following fields of above struct set:
+ *
+ * channels Number of channels that will be supported.
+ * hl_hdrlen Space to preserve in sk_buff's when sending. Drivers, not
+ * supporting sk_buff's should set this to 0.
+ * command Address of Command-Handler.
+ * features Bitwise coded Features of this driver. (use ISDN_FEATURE_...)
+ * writebuf_skb Address of Skbuff-Send-Handler.
+ * writecmd " " D-Channel " which accepts raw D-Ch-Commands.
+ * readstat " " D-Channel " which delivers raw Status-Data.
+ *
+ * The linklevel-driver fills the following fields:
+ *
+ * channels Driver-ID assigned to this driver. (Must be used on all
+ * subsequent callbacks.
+ * rcvcallb_skb Address of handler for received Skbuff's.
+ * statcallb " " " for status-changes.
+ *
+ */
+extern int register_isdn(isdn_if*);
+#include <asm/uaccess.h>
+
+#endif /* __KERNEL__ */
+#endif /* isdnif_h */
diff --git a/pfinet/linux-src/include/linux/isicom.h b/pfinet/linux-src/include/linux/isicom.h
new file mode 100644
index 00000000..a7dc2f80
--- /dev/null
+++ b/pfinet/linux-src/include/linux/isicom.h
@@ -0,0 +1,309 @@
+#ifndef _LINUX_ISICOM_H
+#define _LINUX_ISICOM_H
+
+/*#define ISICOM_DEBUG*/
+/*#define ISICOM_DEBUG_DTR_RTS*/
+
+
+/*
+ * Firmware Loader definitions ...
+ */
+
+#define __MultiTech ('M'<<8)
+#define MIOCTL_LOAD_FIRMWARE (__MultiTech | 0x01)
+#define MIOCTL_READ_FIRMWARE (__MultiTech | 0x02)
+#define MIOCTL_XFER_CTRL (__MultiTech | 0x03)
+#define MIOCTL_RESET_CARD (__MultiTech | 0x04)
+
+#define DATA_SIZE 16
+
+typedef struct {
+ unsigned short exec_segment;
+ unsigned short exec_addr;
+} exec_record;
+
+typedef struct {
+ int board; /* Board to load */
+ unsigned short addr;
+ unsigned short count;
+} bin_header;
+
+typedef struct {
+ int board; /* Board to load */
+ unsigned short addr;
+ unsigned short count;
+ unsigned short segment;
+ unsigned char bin_data[DATA_SIZE];
+} bin_frame;
+
+#ifdef __KERNEL__
+
+#define YES 1
+#define NO 0
+
+#define ISILOAD_MISC_MINOR 155 /* /dev/isctl */
+#define ISILOAD_NAME "ISILoad"
+
+/*
+ * ISICOM Driver definitions ...
+ *
+ */
+
+#define ISICOM_NAME "ISICom"
+
+/*
+ * PCI definitions
+ */
+
+ #define DEVID_COUNT 9
+ #define VENDOR_ID 0x10b5
+
+/*
+ * These are now officially allocated numbers
+ */
+
+#define ISICOM_NMAJOR 112 /* normal */
+#define ISICOM_CMAJOR 113 /* callout */
+#define ISICOM_MAGIC (('M' << 8) | 'T')
+
+#define WAKEUP_CHARS 256 /* hard coded for now */
+#define TX_SIZE 254
+
+#define BOARD_COUNT 4
+#define PORT_COUNT (BOARD_COUNT*16)
+
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2
+
+/* character sizes */
+
+#define ISICOM_CS5 0x0000
+#define ISICOM_CS6 0x0001
+#define ISICOM_CS7 0x0002
+#define ISICOM_CS8 0x0003
+
+/* stop bits */
+
+#define ISICOM_1SB 0x0000
+#define ISICOM_2SB 0x0004
+
+/* parity */
+
+#define ISICOM_NOPAR 0x0000
+#define ISICOM_ODPAR 0x0008
+#define ISICOM_EVPAR 0x0018
+
+/* flow control */
+
+#define ISICOM_CTSRTS 0x03
+#define ISICOM_INITIATE_XONXOFF 0x04
+#define ISICOM_RESPOND_XONXOFF 0x08
+
+#define InterruptTheCard(base) (outw(0,(base)+0xc))
+#define ClearInterrupt(base) (inw((base)+0x0a))
+
+#define BOARD(line) (((line) >> 4) & 0x3)
+#define MIN(a, b) ( (a) < (b) ? (a) : (b) )
+
+ /* isi kill queue bitmap */
+
+#define ISICOM_KILLTX 0x01
+#define ISICOM_KILLRX 0x02
+
+ /* isi_board status bitmap */
+
+#define FIRMWARE_LOADED 0x0001
+#define BOARD_ACTIVE 0x0002
+
+ /* isi_port status bitmap */
+
+#define ISI_CTS 0x1000
+#define ISI_DSR 0x2000
+#define ISI_RI 0x4000
+#define ISI_DCD 0x8000
+#define ISI_DTR 0x0100
+#define ISI_RTS 0x0200
+
+
+#define ISI_TXOK 0x0001
+
+struct isi_board {
+ unsigned short base;
+ unsigned char irq;
+ unsigned char port_count;
+ unsigned short status;
+ unsigned short port_status; /* each bit represents a single port */
+ unsigned short shift_count;
+ struct isi_port * ports;
+ signed char count;
+ unsigned char isa;
+};
+
+struct isi_port {
+ unsigned short magic;
+ unsigned int flags;
+ int count;
+ int blocked_open;
+ int close_delay;
+ unsigned short channel;
+ unsigned short status;
+ unsigned short closing_wait;
+ long session;
+ long pgrp;
+ struct isi_board * card;
+ struct tty_struct * tty;
+ struct wait_queue * close_wait;
+ struct wait_queue * open_wait;
+ struct tq_struct hangup_tq;
+ struct tq_struct bh_tqueue;
+ unsigned char * xmit_buf;
+ int xmit_head;
+ int xmit_tail;
+ int xmit_cnt;
+ struct termios normal_termios;
+ struct termios callout_termios;
+};
+
+
+/*
+ * ISI Card specific ops ...
+ */
+
+extern inline void raise_dtr(struct isi_port * port)
+{
+ struct isi_board * card = port->card;
+ unsigned short base = card->base;
+ unsigned char channel = port->channel;
+ short wait=400;
+ while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+ if (wait <= 0) {
+ printk(KERN_WARNING "ISICOM: Card found busy in raise_dtr.\n");
+ return;
+ }
+#ifdef ISICOM_DEBUG_DTR_RTS
+ printk(KERN_DEBUG "ISICOM: raise_dtr.\n");
+#endif
+ outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+ outw(0x0504, base);
+ InterruptTheCard(base);
+ port->status |= ISI_DTR;
+}
+
+extern inline void drop_dtr(struct isi_port * port)
+{
+ struct isi_board * card = port->card;
+ unsigned short base = card->base;
+ unsigned char channel = port->channel;
+ short wait=400;
+ while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+ if (wait <= 0) {
+ printk(KERN_WARNING "ISICOM: Card found busy in drop_dtr.\n");
+ return;
+ }
+#ifdef ISICOM_DEBUG_DTR_RTS
+ printk(KERN_DEBUG "ISICOM: drop_dtr.\n");
+#endif
+ outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+ outw(0x0404, base);
+ InterruptTheCard(base);
+ port->status &= ~ISI_DTR;
+}
+extern inline void raise_rts(struct isi_port * port)
+{
+ struct isi_board * card = port->card;
+ unsigned short base = card->base;
+ unsigned char channel = port->channel;
+ short wait=400;
+ while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+ if (wait <= 0) {
+ printk(KERN_WARNING "ISICOM: Card found busy in raise_rts.\n");
+ return;
+ }
+#ifdef ISICOM_DEBUG_DTR_RTS
+ printk(KERN_DEBUG "ISICOM: raise_rts.\n");
+#endif
+ outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+ outw(0x0a04, base);
+ InterruptTheCard(base);
+ port->status |= ISI_RTS;
+}
+extern inline void drop_rts(struct isi_port * port)
+{
+ struct isi_board * card = port->card;
+ unsigned short base = card->base;
+ unsigned char channel = port->channel;
+ short wait=400;
+ while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+ if (wait <= 0) {
+ printk(KERN_WARNING "ISICOM: Card found busy in drop_rts.\n");
+ return;
+ }
+#ifdef ISICOM_DEBUG_DTR_RTS
+ printk(KERN_DEBUG "ISICOM: drop_rts.\n");
+#endif
+ outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+ outw(0x0804, base);
+ InterruptTheCard(base);
+ port->status &= ~ISI_RTS;
+}
+extern inline void raise_dtr_rts(struct isi_port * port)
+{
+ struct isi_board * card = port->card;
+ unsigned short base = card->base;
+ unsigned char channel = port->channel;
+ short wait=400;
+ while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+ if (wait <= 0) {
+ printk(KERN_WARNING "ISICOM: Card found busy in raise_dtr_rts.\n");
+ return;
+ }
+#ifdef ISICOM_DEBUG_DTR_RTS
+ printk(KERN_DEBUG "ISICOM: raise_dtr_rts.\n");
+#endif
+ outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+ outw(0x0f04, base);
+ InterruptTheCard(base);
+ port->status |= (ISI_DTR | ISI_RTS);
+}
+extern inline void drop_dtr_rts(struct isi_port * port)
+{
+ struct isi_board * card = port->card;
+ unsigned short base = card->base;
+ unsigned char channel = port->channel;
+ short wait=400;
+ while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+ if (wait <= 0) {
+ printk(KERN_WARNING "ISICOM: Card found busy in drop_dtr_rts.\n");
+ return;
+ }
+#ifdef ISICOM_DEBUG_DTR_RTS
+ printk(KERN_DEBUG "ISICOM: drop_dtr_rts.\n");
+#endif
+ outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+ outw(0x0c04, base);
+ InterruptTheCard(base);
+ port->status &= ~(ISI_RTS | ISI_DTR);
+}
+
+extern inline void kill_queue(struct isi_port * port, short queue)
+{
+ struct isi_board * card = port->card;
+ unsigned short base = card->base;
+ unsigned char channel = port->channel;
+ short wait=400;
+ while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+ if (wait <= 0) {
+ printk(KERN_WARNING "ISICOM: Card found busy in kill_queue.\n");
+ return;
+ }
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: kill_queue 0x%x.\n", queue);
+#endif
+ outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+ outw((queue << 8) | 0x06, base);
+ InterruptTheCard(base);
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* ISICOM_H */
diff --git a/pfinet/linux-src/include/linux/iso_fs.h b/pfinet/linux-src/include/linux/iso_fs.h
new file mode 100644
index 00000000..0fcb4b82
--- /dev/null
+++ b/pfinet/linux-src/include/linux/iso_fs.h
@@ -0,0 +1,230 @@
+
+#ifndef _ISOFS_FS_H
+#define _ISOFS_FS_H
+
+#include <linux/types.h>
+/*
+ * The isofs filesystem constants/structures
+ */
+
+/* This part borrowed from the bsd386 isofs */
+#define ISODCL(from, to) (to - from + 1)
+
+struct iso_volume_descriptor {
+ char type[ISODCL(1,1)]; /* 711 */
+ char id[ISODCL(2,6)];
+ char version[ISODCL(7,7)];
+ char data[ISODCL(8,2048)];
+};
+
+/* volume descriptor types */
+#define ISO_VD_PRIMARY 1
+#define ISO_VD_SUPPLEMENTARY 2
+#define ISO_VD_END 255
+
+#define ISO_STANDARD_ID "CD001"
+
+struct iso_primary_descriptor {
+ char type [ISODCL ( 1, 1)]; /* 711 */
+ char id [ISODCL ( 2, 6)];
+ char version [ISODCL ( 7, 7)]; /* 711 */
+ char unused1 [ISODCL ( 8, 8)];
+ char system_id [ISODCL ( 9, 40)]; /* achars */
+ char volume_id [ISODCL ( 41, 72)]; /* dchars */
+ char unused2 [ISODCL ( 73, 80)];
+ char volume_space_size [ISODCL ( 81, 88)]; /* 733 */
+ char unused3 [ISODCL ( 89, 120)];
+ char volume_set_size [ISODCL (121, 124)]; /* 723 */
+ char volume_sequence_number [ISODCL (125, 128)]; /* 723 */
+ char logical_block_size [ISODCL (129, 132)]; /* 723 */
+ char path_table_size [ISODCL (133, 140)]; /* 733 */
+ char type_l_path_table [ISODCL (141, 144)]; /* 731 */
+ char opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */
+ char type_m_path_table [ISODCL (149, 152)]; /* 732 */
+ char opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */
+ char root_directory_record [ISODCL (157, 190)]; /* 9.1 */
+ char volume_set_id [ISODCL (191, 318)]; /* dchars */
+ char publisher_id [ISODCL (319, 446)]; /* achars */
+ char preparer_id [ISODCL (447, 574)]; /* achars */
+ char application_id [ISODCL (575, 702)]; /* achars */
+ char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */
+ char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */
+ char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */
+ char creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */
+ char modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */
+ char expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */
+ char effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */
+ char file_structure_version [ISODCL (882, 882)]; /* 711 */
+ char unused4 [ISODCL (883, 883)];
+ char application_data [ISODCL (884, 1395)];
+ char unused5 [ISODCL (1396, 2048)];
+};
+
+/* Almost the same as the primary descriptor but two fields are specified */
+struct iso_supplementary_descriptor {
+ char type [ISODCL ( 1, 1)]; /* 711 */
+ char id [ISODCL ( 2, 6)];
+ char version [ISODCL ( 7, 7)]; /* 711 */
+ char flags [ISODCL ( 8, 8)]; /* 853 */
+ char system_id [ISODCL ( 9, 40)]; /* achars */
+ char volume_id [ISODCL ( 41, 72)]; /* dchars */
+ char unused2 [ISODCL ( 73, 80)];
+ char volume_space_size [ISODCL ( 81, 88)]; /* 733 */
+ char escape [ISODCL ( 89, 120)]; /* 856 */
+ char volume_set_size [ISODCL (121, 124)]; /* 723 */
+ char volume_sequence_number [ISODCL (125, 128)]; /* 723 */
+ char logical_block_size [ISODCL (129, 132)]; /* 723 */
+ char path_table_size [ISODCL (133, 140)]; /* 733 */
+ char type_l_path_table [ISODCL (141, 144)]; /* 731 */
+ char opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */
+ char type_m_path_table [ISODCL (149, 152)]; /* 732 */
+ char opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */
+ char root_directory_record [ISODCL (157, 190)]; /* 9.1 */
+ char volume_set_id [ISODCL (191, 318)]; /* dchars */
+ char publisher_id [ISODCL (319, 446)]; /* achars */
+ char preparer_id [ISODCL (447, 574)]; /* achars */
+ char application_id [ISODCL (575, 702)]; /* achars */
+ char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */
+ char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */
+ char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */
+ char creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */
+ char modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */
+ char expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */
+ char effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */
+ char file_structure_version [ISODCL (882, 882)]; /* 711 */
+ char unused4 [ISODCL (883, 883)];
+ char application_data [ISODCL (884, 1395)];
+ char unused5 [ISODCL (1396, 2048)];
+};
+
+
+#define HS_STANDARD_ID "CDROM"
+
+struct hs_volume_descriptor {
+ char foo [ISODCL ( 1, 8)]; /* 733 */
+ char type [ISODCL ( 9, 9)]; /* 711 */
+ char id [ISODCL ( 10, 14)];
+ char version [ISODCL ( 15, 15)]; /* 711 */
+ char data[ISODCL(16,2048)];
+};
+
+
+struct hs_primary_descriptor {
+ char foo [ISODCL ( 1, 8)]; /* 733 */
+ char type [ISODCL ( 9, 9)]; /* 711 */
+ char id [ISODCL ( 10, 14)];
+ char version [ISODCL ( 15, 15)]; /* 711 */
+ char unused1 [ISODCL ( 16, 16)]; /* 711 */
+ char system_id [ISODCL ( 17, 48)]; /* achars */
+ char volume_id [ISODCL ( 49, 80)]; /* dchars */
+ char unused2 [ISODCL ( 81, 88)]; /* 733 */
+ char volume_space_size [ISODCL ( 89, 96)]; /* 733 */
+ char unused3 [ISODCL ( 97, 128)]; /* 733 */
+ char volume_set_size [ISODCL (129, 132)]; /* 723 */
+ char volume_sequence_number [ISODCL (133, 136)]; /* 723 */
+ char logical_block_size [ISODCL (137, 140)]; /* 723 */
+ char path_table_size [ISODCL (141, 148)]; /* 733 */
+ char type_l_path_table [ISODCL (149, 152)]; /* 731 */
+ char unused4 [ISODCL (153, 180)]; /* 733 */
+ char root_directory_record [ISODCL (181, 214)]; /* 9.1 */
+};
+
+/* We use this to help us look up the parent inode numbers. */
+
+struct iso_path_table{
+ unsigned char name_len[2]; /* 721 */
+ char extent[4]; /* 731 */
+ char parent[2]; /* 721 */
+ char name[0];
+};
+
+/* high sierra is identical to iso, except that the date is only 6 bytes, and
+ there is an extra reserved byte after the flags */
+
+struct iso_directory_record {
+ char length [ISODCL (1, 1)]; /* 711 */
+ char ext_attr_length [ISODCL (2, 2)]; /* 711 */
+ char extent [ISODCL (3, 10)]; /* 733 */
+ char size [ISODCL (11, 18)]; /* 733 */
+ char date [ISODCL (19, 25)]; /* 7 by 711 */
+ char flags [ISODCL (26, 26)];
+ char file_unit_size [ISODCL (27, 27)]; /* 711 */
+ char interleave [ISODCL (28, 28)]; /* 711 */
+ char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
+ unsigned char name_len [ISODCL (33, 33)]; /* 711 */
+ char name [0];
+};
+
+#define ISOFS_BLOCK_BITS 11
+#define ISOFS_BLOCK_SIZE 2048
+
+#define ISOFS_BUFFER_SIZE(INODE) ((INODE)->i_sb->s_blocksize)
+#define ISOFS_BUFFER_BITS(INODE) ((INODE)->i_sb->s_blocksize_bits)
+#define ISOFS_ZONE_BITS(INODE) ((INODE)->i_sb->u.isofs_sb.s_log_zone_size)
+
+#define ISOFS_SUPER_MAGIC 0x9660
+
+#ifdef __KERNEL__
+extern int isonum_711(char *);
+extern int isonum_712(char *);
+extern int isonum_721(char *);
+extern int isonum_722(char *);
+extern int isonum_723(char *);
+extern int isonum_731(char *);
+extern int isonum_732(char *);
+extern int isonum_733(char *);
+extern int iso_date(char *, int);
+
+extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *);
+extern int get_rock_ridge_filename(struct iso_directory_record *, char *, struct inode *);
+
+extern char * get_rock_ridge_symlink(struct inode *);
+extern int find_rock_ridge_relocation(struct iso_directory_record *, struct inode *);
+
+int get_joliet_filename(struct iso_directory_record *, struct inode *, unsigned char *);
+int get_acorn_filename(struct iso_directory_record *, char *, struct inode *);
+
+/* The stuff that follows may be totally unneeded. I have not checked to see
+ which prototypes we are still using. */
+
+extern int isofs_open(struct inode * inode, struct file * filp);
+extern void isofs_release(struct inode * inode, struct file * filp);
+extern struct dentry *isofs_lookup(struct inode * dir, struct dentry *);
+extern unsigned long isofs_count_free_inodes(struct super_block *sb);
+extern int isofs_new_block(int dev);
+extern int isofs_free_block(int dev, int block);
+extern int isofs_bmap(struct inode *,int);
+
+extern void isofs_put_super(struct super_block *);
+extern struct super_block *isofs_read_super(struct super_block *,void *,int);
+extern int init_iso9660_fs(void);
+extern void isofs_read_inode(struct inode *);
+extern void isofs_put_inode(struct inode *);
+extern int isofs_statfs(struct super_block *, struct statfs *, int);
+
+extern int isofs_lseek(struct inode *, struct file *, off_t, int);
+extern int isofs_read(struct inode *, struct file *, char *, int);
+extern int isofs_lookup_grandparent(struct inode *, int);
+
+extern struct inode_operations isofs_file_inode_operations;
+extern struct inode_operations isofs_dir_inode_operations;
+extern struct inode_operations isofs_symlink_inode_operations;
+extern struct inode_operations isofs_chrdev_inode_operations;
+extern struct inode_operations isofs_blkdev_inode_operations;
+extern struct inode_operations isofs_fifo_inode_operations;
+
+/* The following macros are used to check for memory leaks. */
+#ifdef LEAK_CHECK
+#define free_s leak_check_free_s
+#define malloc leak_check_malloc
+#define bread leak_check_bread
+#define brelse leak_check_brelse
+extern void * leak_check_malloc(unsigned int size);
+extern void leak_check_free_s(void * obj, int size);
+extern struct buffer_head * leak_check_bread(int dev, int block, int size);
+extern void leak_check_brelse(struct buffer_head * bh);
+#endif /* LEAK_CHECK */
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/iso_fs_i.h b/pfinet/linux-src/include/linux/iso_fs_i.h
new file mode 100644
index 00000000..02f17e07
--- /dev/null
+++ b/pfinet/linux-src/include/linux/iso_fs_i.h
@@ -0,0 +1,14 @@
+#ifndef _ISO_FS_I
+#define _ISO_FS_I
+
+/*
+ * iso fs inode data in memory
+ */
+struct iso_inode_info {
+ unsigned int i_first_extent;
+ unsigned char i_file_format;
+ unsigned long i_next_section_ino;
+ off_t i_section_size;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/iso_fs_sb.h b/pfinet/linux-src/include/linux/iso_fs_sb.h
new file mode 100644
index 00000000..7bb55a50
--- /dev/null
+++ b/pfinet/linux-src/include/linux/iso_fs_sb.h
@@ -0,0 +1,31 @@
+#ifndef _ISOFS_FS_SB
+#define _ISOFS_FS_SB
+
+/*
+ * iso9660 super-block data in memory
+ */
+struct isofs_sb_info {
+ unsigned long s_ninodes;
+ unsigned long s_nzones;
+ unsigned long s_firstdatazone;
+ unsigned long s_log_zone_size;
+ unsigned long s_max_size;
+
+ unsigned char s_high_sierra; /* A simple flag */
+ unsigned char s_mapping;
+ unsigned char s_rock;
+ unsigned char s_joliet_level;
+ unsigned char s_utf8;
+ unsigned char s_cruft; /* Broken disks with high
+ byte of length containing
+ junk */
+ unsigned char s_unhide;
+ unsigned char s_nosuid;
+ unsigned char s_nodev;
+ mode_t s_mode;
+ gid_t s_gid;
+ uid_t s_uid;
+ struct nls_table *s_nls_iocharset; /* Native language support table */
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/istallion.h b/pfinet/linux-src/include/linux/istallion.h
new file mode 100644
index 00000000..269ef88b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/istallion.h
@@ -0,0 +1,134 @@
+/*****************************************************************************/
+
+/*
+ * istallion.h -- stallion intelligent multiport serial driver.
+ *
+ * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au).
+ * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*****************************************************************************/
+#ifndef _ISTALLION_H
+#define _ISTALLION_H
+/*****************************************************************************/
+
+/*
+ * Define important driver constants here.
+ */
+#define STL_MAXBRDS 4
+#define STL_MAXPANELS 4
+#define STL_MAXPORTS 64
+#define STL_MAXCHANS (STL_MAXPORTS + 1)
+#define STL_MAXDEVS (STL_MAXBRDS * STL_MAXPORTS)
+
+
+/*
+ * Define a set of structures to hold all the board/panel/port info
+ * for our ports. These will be dynamically allocated as required at
+ * driver initialization time.
+ */
+
+/*
+ * Port and board structures to hold status info about each object.
+ * The board structure contains pointers to structures for each port
+ * connected to it. Panels are not distinguished here, since
+ * communication with the slave board will always be on a per port
+ * basis.
+ */
+typedef struct {
+ unsigned long magic;
+ int portnr;
+ int panelnr;
+ int brdnr;
+ unsigned long state;
+ int devnr;
+ int flags;
+ int baud_base;
+ int custom_divisor;
+ int close_delay;
+ int closing_wait;
+ int refcount;
+ int openwaitcnt;
+ int rc;
+ int argsize;
+ void *argp;
+ long session;
+ long pgrp;
+ unsigned int rxmarkmsk;
+ struct tty_struct *tty;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+ struct wait_queue *raw_wait;
+ struct tq_struct tqhangup;
+ struct termios normaltermios;
+ struct termios callouttermios;
+ asysigs_t asig;
+ unsigned long addr;
+ unsigned long rxoffset;
+ unsigned long txoffset;
+ unsigned long sigs;
+ unsigned long pflag;
+ unsigned int rxsize;
+ unsigned int txsize;
+ unsigned char reqbit;
+ unsigned char portidx;
+ unsigned char portbit;
+} stliport_t;
+
+/*
+ * Use a structure of function pointers to do board level operations.
+ * These include, enable/disable, paging shared memory, interrupting, etc.
+ */
+typedef struct stlibrd {
+ unsigned long magic;
+ int brdnr;
+ int brdtype;
+ int state;
+ int nrpanels;
+ int nrports;
+ int nrdevs;
+ unsigned int iobase;
+ int iosize;
+ unsigned long memaddr;
+ void *membase;
+ int memsize;
+ int pagesize;
+ int hostoffset;
+ int slaveoffset;
+ int bitsize;
+ int enabval;
+ int panels[STL_MAXPANELS];
+ int panelids[STL_MAXPANELS];
+ void (*init)(struct stlibrd *brdp);
+ void (*enable)(struct stlibrd *brdp);
+ void (*reenable)(struct stlibrd *brdp);
+ void (*disable)(struct stlibrd *brdp);
+ char *(*getmemptr)(struct stlibrd *brdp, unsigned long offset, int line);
+ void (*intr)(struct stlibrd *brdp);
+ void (*reset)(struct stlibrd *brdp);
+ stliport_t *ports[STL_MAXPORTS];
+} stlibrd_t;
+
+
+/*
+ * Define MAGIC numbers used for above structures.
+ */
+#define STLI_PORTMAGIC 0xe671c7a1
+#define STLI_BOARDMAGIC 0x4bc6c825
+
+/*****************************************************************************/
+#endif
diff --git a/pfinet/linux-src/include/linux/ixjuser.h b/pfinet/linux-src/include/linux/ixjuser.h
new file mode 100644
index 00000000..1ee0ee91
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ixjuser.h
@@ -0,0 +1,629 @@
+/*
+ * ixjuser.h
+ *
+ * User-space include file for the Internet PhoneJACK and
+ * Internet LineJACK Telephony Cards.
+ *
+ * (c) Copyright 1999 Quicknet Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Author: Ed Okerson, <eokerson@quicknet.net>
+ *
+ * Contributors: Greg Herlein, <gherlein@quicknet.net>
+ * David W. Erhart, <derhart@quicknet.net>
+ * John Sellers, <jsellers@quicknet.net>
+ * Mike Preston, <mpreston@quicknet.net>
+ *
+ * More information about the hardware related to this driver can be found
+ * at our website: http://www.quicknet.net
+ *
+ * Fixes:
+ */
+
+static char ixjuser_h_rcsid[] = "$Id: ixjuser.h,v 3.4 1999/12/16 22:18:36 root Exp root $";
+
+#include <linux/telephony.h>
+
+/***************************************************************************
+
+ If you use the IXJCTL_TESTRAM command, the card must be power
+ cycled to reset the SRAM values before further use.
+
+***************************************************************************/
+#define IXJCTL_DSP_RESET _IO ('q', 0xC0)
+
+#define IXJCTL_RING PHONE_RING
+#define IXJCTL_HOOKSTATE PHONE_HOOKSTATE
+#define IXJCTL_MAXRINGS PHONE_MAXRINGS
+#define IXJCTL_RING_CADENCE PHONE_RING_CADENCE
+#define IXJCTL_RING_START PHONE_RING_START
+#define IXJCTL_RING_STOP PHONE_RING_STOP
+
+#define IXJCTL_CARDTYPE _IOR ('q', 0xC1, int)
+#define IXJCTL_SERIAL _IOR ('q', 0xC2, int)
+#define IXJCTL_DSP_TYPE _IOR ('q', 0xC3, int)
+#define IXJCTL_DSP_VERSION _IOR ('q', 0xC4, int)
+#define IXJCTL_DSP_IDLE _IO ('q', 0xC5)
+#define IXJCTL_TESTRAM _IO ('q', 0xC6)
+
+/******************************************************************************
+*
+* This group of IOCTLs deal with the record settings of the DSP
+*
+* The IXJCTL_REC_DEPTH command sets the internal buffer depth of the DSP.
+* Setting a lower depth reduces latency, but increases the demand of the
+* application to service the driver without frame loss. The DSP has 480
+* bytes of physical buffer memory for the record channel so the true
+* maximum limit is determined by how many frames will fit in the buffer.
+*
+* 1 uncompressed (480 byte) 16-bit linear frame.
+* 2 uncompressed (240 byte) 8-bit A-law/mu-law frames.
+* 15 TrueSpeech 8.5 frames.
+* 20 TrueSpeech 6.3,5.3,4.8 or 4.1 frames.
+*
+* The default in the driver is currently set to 2 frames.
+*
+* The IXJCTL_REC_VOLUME and IXJCTL_PLAY_VOLUME commands both use a Q8
+* number as a parameter, 0x100 scales the signal by 1.0, 0x200 scales the
+* signal by 2.0, 0x80 scales the signal by 0.5. No protection is given
+* against over-scaling, if the multiplication factor times the input
+* signal exceeds 16 bits, overflow distortion will occur. The default
+* setting is 0x100 (1.0).
+*
+* The IXJCTL_REC_LEVEL returns the average signal level (not r.m.s.) on
+* the most recently recorded frame as a 16 bit value.
+******************************************************************************/
+
+#define IXJCTL_REC_CODEC PHONE_REC_CODEC
+#define IXJCTL_REC_START PHONE_REC_START
+#define IXJCTL_REC_STOP PHONE_REC_STOP
+#define IXJCTL_REC_DEPTH PHONE_REC_DEPTH
+#define IXJCTL_FRAME PHONE_FRAME
+#define IXJCTL_REC_VOLUME PHONE_REC_VOLUME
+#define IXJCTL_REC_LEVEL PHONE_REC_LEVEL
+
+typedef enum {
+ f300_640 = 4, f300_500, f1100, f350, f400, f480, f440, f620, f20_50,
+ f133_200, f300, f300_420, f330, f300_425, f330_440, f340, f350_400,
+ f350_440, f350_450, f360, f380_420, f392, f400_425, f400_440, f400_450,
+ f420, f425, f425_450, f425_475, f435, f440_450, f440_480, f445, f450,
+ f452, f475, f480_620, f494, f500, f520, f523, f525, f540_660, f587,
+ f590, f600, f660, f700, f740, f750, f750_1450, f770, f800, f816, f850,
+ f857_1645, f900, f900_1300, f935_1215, f941_1477, f942, f950, f950_1400,
+ f975, f1000, f1020, f1050, f1100_1750, f1140, f1200, f1209, f1330, f1336,
+ lf1366, f1380, f1400, f1477, f1600, f1633_1638, f1800, f1860
+} IXJ_FILTER_FREQ;
+
+typedef struct {
+ unsigned int filter;
+ IXJ_FILTER_FREQ freq;
+ char enable;
+} IXJ_FILTER;
+
+#define IXJCTL_SET_FILTER _IOW ('q', 0xC7, IXJ_FILTER *)
+#define IXJCTL_GET_FILTER_HIST _IOW ('q', 0xC8, int)
+/******************************************************************************
+*
+* This IOCTL allows you to reassign values in the tone index table. The
+* tone table has 32 entries (0 - 31), but the driver only allows entries
+* 13 - 27 to be modified, entry 0 is reserved for silence and 1 - 12 are
+* the standard DTMF digits and 28 - 31 are the DTMF tones for A, B, C & D.
+* The positions used internally for Call Progress Tones are as follows:
+* Dial Tone - 25
+* Ring Back - 26
+* Busy Signal - 27
+*
+* The freq values are calculated as:
+* freq = cos(2 * PI * frequency / 8000)
+*
+* The most commonly needed values are already calculated and listed in the
+* enum IXJ_TONE_FREQ. Each tone index can have two frequencies with
+* different gains, if you are only using a single frequency set the unused
+* one to 0.
+*
+* The gain values range from 0 to 15 indicating +6dB to -24dB in 2dB
+* increments.
+*
+******************************************************************************/
+
+typedef enum {
+ hz20 = 0x7ffa,
+ hz50 = 0x7fe5,
+ hz133 = 0x7f4c,
+ hz200 = 0x7e6b,
+ hz261 = 0x7d50, /* .63 C1 */
+ hz277 = 0x7cfa, /* .18 CS1 */
+ hz293 = 0x7c9f, /* .66 D1 */
+ hz300 = 0x7c75,
+ hz311 = 0x7c32, /* .13 DS1 */
+ hz329 = 0x7bbf, /* .63 E1 */
+ hz330 = 0x7bb8,
+ hz340 = 0x7b75,
+ hz349 = 0x7b37, /* .23 F1 */
+ hz350 = 0x7b30,
+ hz360 = 0x7ae9,
+ hz369 = 0x7aa8, /* .99 FS1 */
+ hz380 = 0x7a56,
+ hz392 = 0x79fa, /* .00 G1 */
+ hz400 = 0x79bb,
+ hz415 = 0x7941, /* .30 GS1 */
+ hz420 = 0x7918,
+ hz425 = 0x78ee,
+ hz435 = 0x7899,
+ hz440 = 0x786d, /* .00 A1 */
+ hz445 = 0x7842,
+ hz450 = 0x7815,
+ hz452 = 0x7803,
+ hz466 = 0x7784, /* .16 AS1 */
+ hz475 = 0x7731,
+ hz480 = 0x7701,
+ hz493 = 0x7685, /* .88 B1 */
+ hz494 = 0x767b,
+ hz500 = 0x7640,
+ hz520 = 0x7578,
+ hz523 = 0x7559, /* .25 C2 */
+ hz525 = 0x7544,
+ hz540 = 0x74a7,
+ hz554 = 0x7411, /* .37 CS2 */
+ hz587 = 0x72a1, /* .33 D2 */
+ hz590 = 0x727f,
+ hz600 = 0x720b,
+ hz620 = 0x711e,
+ hz622 = 0x7106, /* .25 DS2 */
+ hz659 = 0x6f3b, /* .26 E2 */
+ hz660 = 0x6f2e,
+ hz698 = 0x6d3d, /* .46 F2 */
+ hz700 = 0x6d22,
+ hz739 = 0x6b09, /* .99 FS2 */
+ hz740 = 0x6afa,
+ hz750 = 0x6a6c,
+ hz770 = 0x694b,
+ hz783 = 0x688b, /* .99 G2 */
+ hz800 = 0x678d,
+ hz816 = 0x6698,
+ hz830 = 0x65bf, /* .61 GS2 */
+ hz850 = 0x6484,
+ hz857 = 0x6414,
+ hz880 = 0x629f, /* .00 A2 */
+ hz900 = 0x6154,
+ hz932 = 0x5f35, /* .33 AS2 */
+ hz935 = 0x5f01,
+ hz941 = 0x5e9a,
+ hz942 = 0x5e88,
+ hz950 = 0x5dfd,
+ hz975 = 0x5c44,
+ hz1000 = 0x5a81,
+ hz1020 = 0x5912,
+ hz1050 = 0x56e2,
+ hz1100 = 0x5320,
+ hz1140 = 0x5007,
+ hz1200 = 0x4b3b,
+ hz1209 = 0x4a80,
+ hz1215 = 0x4a02,
+ hz1250 = 0x471c,
+ hz1300 = 0x42e0,
+ hz1330 = 0x4049,
+ hz1336 = 0x3fc4,
+ hz1366 = 0x3d22,
+ hz1380 = 0x3be4,
+ hz1400 = 0x3a1b,
+ hz1450 = 0x3596,
+ hz1477 = 0x331c,
+ hz1500 = 0x30fb,
+ hz1600 = 0x278d,
+ hz1633 = 0x2462,
+ hz1638 = 0x23e7,
+ hz1645 = 0x233a,
+ hz1750 = 0x18f8,
+ hz1800 = 0x1405,
+ hz1860 = 0xe0b,
+ hz2100 = 0xf5f6,
+ hz2450 = 0xd3b3
+} IXJ_FREQ;
+
+typedef enum {
+ C1 = hz261,
+ CS1 = hz277,
+ D1 = hz293,
+ DS1 = hz311,
+ E1 = hz329,
+ F1 = hz349,
+ FS1 = hz369,
+ G1 = hz392,
+ GS1 = hz415,
+ A1 = hz440,
+ AS1 = hz466,
+ B1 = hz493,
+ C2 = hz523,
+ CS2 = hz554,
+ D2 = hz587,
+ DS2 = hz622,
+ E2 = hz659,
+ F2 = hz698,
+ FS2 = hz739,
+ G2 = hz783,
+ GS2 = hz830,
+ A2 = hz880,
+ AS2 = hz932,
+} IXJ_NOTE;
+
+typedef struct {
+ int tone_index;
+ int freq0;
+ int gain0;
+ int freq1;
+ int gain1;
+} IXJ_TONE;
+
+#define IXJCTL_INIT_TONE _IOW ('q', 0xC9, IXJ_TONE *)
+
+/******************************************************************************
+*
+* The IXJCTL_TONE_CADENCE ioctl defines tone sequences used for various
+* Call Progress Tones (CPT). This is accomplished by setting up an array of
+* IXJ_CADENCE_ELEMENT structures that sequentially define the states of
+* the tone sequence. The tone_on_time and tone_off time are in
+* 250 microsecond intervals. A pointer to this array is passed to the
+* driver as the ce element of an IXJ_CADENCE structure. The elements_used
+* must be set to the number of IXJ_CADENCE_ELEMENTS in the array. The
+* termination variable defines what to do at the end of a cadence, the
+* options are to play the cadence once and stop, to repeat the last
+* element of the cadence indefinatly, or to repeat the entire cadence
+* indefinatly. The ce variable is a pointer to the array of IXJ_TONE
+* structures. If the freq0 variable is non-zero, the tone table contents
+* for the tone_index are updated to the frequencies and gains defined. It
+* should be noted that DTMF tones cannot be reassigned, so if DTMF tone
+* table indexs are used in a cadence the frequency and gain variables will
+* be ignored.
+*
+* If the array elements contain frequency parameters the driver will
+* initialize the needed tone table elements and begin playing the tone,
+* there is no preset limit on the number of elements in the cadence. If
+* there is more than one frequency used in the cadence, sequential elements
+* of different frequencies MUST use different tone table indexes. Only one
+* cadence can be played at a time. It is possible to build complex
+* cadences with multiple frequencies using 2 tone table indexes by
+* alternating between them.
+*
+******************************************************************************/
+
+typedef struct {
+ int index;
+ int tone_on_time;
+ int tone_off_time;
+ int freq0;
+ int gain0;
+ int freq1;
+ int gain1;
+} IXJ_CADENCE_ELEMENT;
+
+typedef enum {
+ PLAY_ONCE,
+ REPEAT_LAST_ELEMENT,
+ REPEAT_ALL
+} IXJ_CADENCE_TERM;
+
+typedef struct {
+ int elements_used;
+ IXJ_CADENCE_TERM termination;
+ IXJ_CADENCE_ELEMENT *ce;
+} IXJ_CADENCE;
+
+#define IXJCTL_TONE_CADENCE _IOW ('q', 0xCA, IXJ_CADENCE *)
+/******************************************************************************
+*
+* This group of IOCTLs deal with the playback settings of the DSP
+*
+******************************************************************************/
+
+#define IXJCTL_PLAY_CODEC PHONE_PLAY_CODEC
+#define IXJCTL_PLAY_START PHONE_PLAY_START
+#define IXJCTL_PLAY_STOP PHONE_PLAY_STOP
+#define IXJCTL_PLAY_DEPTH PHONE_PLAY_DEPTH
+#define IXJCTL_PLAY_VOLUME PHONE_PLAY_VOLUME
+#define IXJCTL_PLAY_LEVEL PHONE_PLAY_LEVEL
+
+/******************************************************************************
+*
+* This group of IOCTLs deal with the Acoustic Echo Cancellation settings
+* of the DSP
+*
+* Issuing the IXJCTL_AEC_START command with a value of AEC_OFF has the
+* same effect as IXJCTL_AEC_STOP. This is to simplify slider bar
+* controls. IXJCTL_AEC_GET_LEVEL returns the current setting of the AEC.
+******************************************************************************/
+#define IXJCTL_AEC_START _IOW ('q', 0xCB, int)
+#define IXJCTL_AEC_STOP _IO ('q', 0xCC)
+#define IXJCTL_AEC_GET_LEVEL _IO ('q', 0xCD)
+
+#define AEC_OFF 0
+#define AEC_LOW 1
+#define AEC_MED 2
+#define AEC_HIGH 3
+/******************************************************************************
+*
+* Call Progress Tones, DTMF, etc.
+* IXJCTL_DTMF_OOB determines if dtmf signaling is sent as Out-Of-Band
+* only. If you pass a 1, dtmf is suppressed from the audio stream.
+* Tone on and off times are in 250 microsecond intervals so
+* ioctl(ixj1, IXJCTL_SET_TONE_ON_TIME, 360);
+* will set the tone on time of board ixj1 to 360 * 250us = 90ms
+* the default values of tone on and off times is 840 or 210ms
+******************************************************************************/
+
+#define IXJCTL_DTMF_READY PHONE_DTMF_READY
+#define IXJCTL_GET_DTMF PHONE_GET_DTMF
+#define IXJCTL_GET_DTMF_ASCII PHONE_GET_DTMF_ASCII
+#define IXJCTL_DTMF_OOB PHONE_DTMF_OOB
+#define IXJCTL_EXCEPTION PHONE_EXCEPTION
+#define IXJCTL_PLAY_TONE PHONE_PLAY_TONE
+#define IXJCTL_SET_TONE_ON_TIME PHONE_SET_TONE_ON_TIME
+#define IXJCTL_SET_TONE_OFF_TIME PHONE_SET_TONE_OFF_TIME
+#define IXJCTL_GET_TONE_ON_TIME PHONE_GET_TONE_ON_TIME
+#define IXJCTL_GET_TONE_OFF_TIME PHONE_GET_TONE_OFF_TIME
+#define IXJCTL_GET_TONE_STATE PHONE_GET_TONE_STATE
+#define IXJCTL_BUSY PHONE_BUSY
+#define IXJCTL_RINGBACK PHONE_RINGBACK
+#define IXJCTL_DIALTONE PHONE_DIALTONE
+#define IXJCTL_CPT_STOP PHONE_CPT_STOP
+
+/******************************************************************************
+* LineJack specific IOCTLs
+*
+* The lsb 4 bits of the LED argument represent the state of each of the 4
+* LED's on the LineJack
+******************************************************************************/
+
+#define IXJCTL_SET_LED _IOW ('q', 0xCE, int)
+#define IXJCTL_MIXER _IOW ('q', 0xCF, int)
+
+/******************************************************************************
+*
+* The master volume controls use attenuation with 32 levels from 0 to -62dB
+* with steps of 2dB each, the defines should be OR'ed together then sent
+* as the parameter to the mixer command to change the mixer settings.
+*
+******************************************************************************/
+#define MIXER_MASTER_L 0x0100
+#define MIXER_MASTER_R 0x0200
+#define ATT00DB 0x00
+#define ATT02DB 0x01
+#define ATT04DB 0x02
+#define ATT06DB 0x03
+#define ATT08DB 0x04
+#define ATT10DB 0x05
+#define ATT12DB 0x06
+#define ATT14DB 0x07
+#define ATT16DB 0x08
+#define ATT18DB 0x09
+#define ATT20DB 0x0A
+#define ATT22DB 0x0B
+#define ATT24DB 0x0C
+#define ATT26DB 0x0D
+#define ATT28DB 0x0E
+#define ATT30DB 0x0F
+#define ATT32DB 0x10
+#define ATT34DB 0x11
+#define ATT36DB 0x12
+#define ATT38DB 0x13
+#define ATT40DB 0x14
+#define ATT42DB 0x15
+#define ATT44DB 0x16
+#define ATT46DB 0x17
+#define ATT48DB 0x18
+#define ATT50DB 0x19
+#define ATT52DB 0x1A
+#define ATT54DB 0x1B
+#define ATT56DB 0x1C
+#define ATT58DB 0x1D
+#define ATT60DB 0x1E
+#define ATT62DB 0x1F
+#define MASTER_MUTE 0x80
+
+/******************************************************************************
+*
+* The input volume controls use gain with 32 levels from +12dB to -50dB
+* with steps of 2dB each, the defines should be OR'ed together then sent
+* as the parameter to the mixer command to change the mixer settings.
+*
+******************************************************************************/
+#define MIXER_PORT_CD_L 0x0600
+#define MIXER_PORT_CD_R 0x0700
+#define MIXER_PORT_LINE_IN_L 0x0800
+#define MIXER_PORT_LINE_IN_R 0x0900
+#define MIXER_PORT_POTS_REC 0x0C00
+#define MIXER_PORT_MIC 0x0E00
+
+#define GAIN12DB 0x00
+#define GAIN10DB 0x01
+#define GAIN08DB 0x02
+#define GAIN06DB 0x03
+#define GAIN04DB 0x04
+#define GAIN02DB 0x05
+#define GAIN00DB 0x06
+#define GAIN_02DB 0x07
+#define GAIN_04DB 0x08
+#define GAIN_06DB 0x09
+#define GAIN_08DB 0x0A
+#define GAIN_10DB 0x0B
+#define GAIN_12DB 0x0C
+#define GAIN_14DB 0x0D
+#define GAIN_16DB 0x0E
+#define GAIN_18DB 0x0F
+#define GAIN_20DB 0x10
+#define GAIN_22DB 0x11
+#define GAIN_24DB 0x12
+#define GAIN_26DB 0x13
+#define GAIN_28DB 0x14
+#define GAIN_30DB 0x15
+#define GAIN_32DB 0x16
+#define GAIN_34DB 0x17
+#define GAIN_36DB 0x18
+#define GAIN_38DB 0x19
+#define GAIN_40DB 0x1A
+#define GAIN_42DB 0x1B
+#define GAIN_44DB 0x1C
+#define GAIN_46DB 0x1D
+#define GAIN_48DB 0x1E
+#define GAIN_50DB 0x1F
+#define INPUT_MUTE 0x80
+
+/******************************************************************************
+*
+* The POTS volume control use attenuation with 8 levels from 0dB to -28dB
+* with steps of 4dB each, the defines should be OR'ed together then sent
+* as the parameter to the mixer command to change the mixer settings.
+*
+******************************************************************************/
+#define MIXER_PORT_POTS_PLAY 0x0F00
+
+#define POTS_ATT_00DB 0x00
+#define POTS_ATT_04DB 0x01
+#define POTS_ATT_08DB 0x02
+#define POTS_ATT_12DB 0x03
+#define POTS_ATT_16DB 0x04
+#define POTS_ATT_20DB 0x05
+#define POTS_ATT_24DB 0x06
+#define POTS_ATT_28DB 0x07
+#define POTS_MUTE 0x80
+
+/******************************************************************************
+*
+* The DAA controls the interface to the PSTN port. The driver loads the
+* US coefficients by default, so if you live in a different country you
+* need to load the set for your countries phone system.
+*
+******************************************************************************/
+#define IXJCTL_DAA_COEFF_SET _IOW ('q', 0xD0, int)
+
+#define DAA_US 1 //PITA 8kHz
+#define DAA_UK 2 //ISAR34 8kHz
+#define DAA_FRANCE 3 //
+#define DAA_GERMANY 4
+#define DAA_AUSTRALIA 5
+#define DAA_JAPAN 6
+
+/******************************************************************************
+*
+* Use IXJCTL_PORT to set or query the port the card is set to. If the
+* argument is set to PORT_QUERY, the return value of the ioctl will
+* indicate which port is currently in use, otherwise it will change the
+* port.
+*
+******************************************************************************/
+#define IXJCTL_PORT _IOW ('q', 0xD1, int)
+
+#define PORT_QUERY 0
+#define PORT_POTS 1
+#define PORT_PSTN 2
+#define PORT_SPEAKER 3
+#define PORT_HANDSET 4
+
+#define IXJCTL_PSTN_SET_STATE PHONE_PSTN_SET_STATE
+#define IXJCTL_PSTN_GET_STATE PHONE_PSTN_GET_STATE
+
+#define PSTN_ON_HOOK 0
+#define PSTN_RINGING 1
+#define PSTN_OFF_HOOK 2
+#define PSTN_PULSE_DIAL 3
+
+/******************************************************************************
+*
+* The DAA Analog GAIN sets 2 parameters at one time, the receive gain (AGRR),
+* and the transmit gain (AGX). OR together the components and pass them
+* as the parameter to IXJCTL_DAA_AGAIN. The default setting is both at 0dB.
+*
+******************************************************************************/
+#define IXJCTL_DAA_AGAIN _IOW ('q', 0xD2, int)
+
+#define AGRR00DB 0x00 // Analog gain in receive direction 0dB
+#define AGRR3_5DB 0x10 // Analog gain in receive direction 3.5dB
+#define AGRR06DB 0x30 // Analog gain in receive direction 6dB
+
+#define AGX00DB 0x00 // Analog gain in transmit direction 0dB
+#define AGX_6DB 0x04 // Analog gain in transmit direction -6dB
+#define AGX3_5DB 0x08 // Analog gain in transmit direction 3.5dB
+#define AGX_2_5B 0x0C // Analog gain in transmit direction -2.5dB
+
+#define IXJCTL_PSTN_LINETEST _IO ('q', 0xD3)
+
+typedef struct {
+ char month[3];
+ char day[3];
+ char hour[3];
+ char min[3];
+ int numlen;
+ char number[11];
+ int namelen;
+ char name[80];
+} IXJ_CID;
+
+#define IXJCTL_CID _IOR ('q', 0xD4, IXJ_CID *)
+/******************************************************************************
+*
+* The wink duration is tunable with this ioctl. The default wink duration
+* is 320ms. You do not need to use this ioctl if you do not require a
+* different wink duration.
+*
+******************************************************************************/
+#define IXJCTL_WINK_DURATION PHONE_WINK_DURATION
+
+/******************************************************************************
+*
+* This ioctl will connect the POTS port to the PSTN port on the LineJACK
+* In order for this to work properly the port selection should be set to
+* the PSTN port with IXJCTL_PORT prior to calling this ioctl. This will
+* enable conference calls between PSTN callers and network callers.
+* Passing a 1 to this ioctl enables the POTS<->PSTN connection while
+* passing a 0 turns it back off.
+*
+******************************************************************************/
+#define IXJCTL_POTS_PSTN _IOW ('q', 0xD5, int)
+
+/******************************************************************************
+*
+* IOCTLs added by request.
+*
+* IXJCTL_HZ sets the value your Linux kernel uses for HZ as defined in
+* /usr/include/asm/param.h, this determines the fundamental
+* frequency of the clock ticks on your Linux system. The kernel
+* must be rebuilt if you change this value, also all modules you
+* use (except this one) must be recompiled. The default value
+* is 100, and you only need to use this IOCTL if you use some
+* other value.
+*
+*
+* IXJCTL_RATE sets the number of times per second that the driver polls
+* the DSP. This value cannot be larger than HZ. By
+* increasing both of these values, you may be able to reduce
+* latency because the max hang time that can exist between the
+* driver and the DSP will be reduced.
+*
+******************************************************************************/
+
+#define IXJCTL_HZ _IOW ('q', 0xE0, int)
+#define IXJCTL_RATE _IOW ('q', 0xE1, int)
+#define IXJCTL_FRAMES_READ _IOR ('q', 0xE2, unsigned long)
+#define IXJCTL_FRAMES_WRITTEN _IOR ('q', 0xE3, unsigned long)
+#define IXJCTL_READ_WAIT _IOR ('q', 0xE4, unsigned long)
+#define IXJCTL_WRITE_WAIT _IOR ('q', 0xE5, unsigned long)
+#define IXJCTL_DRYBUFFER_READ _IOR ('q', 0xE6, unsigned long)
+#define IXJCTL_DRYBUFFER_CLEAR _IO ('q', 0xE7)
+
+/******************************************************************************
+*
+* The intercom IOCTL's short the output from one card to the input of the
+* other and vice versa (actually done in the DSP read function). It is only
+* necessary to execute the IOCTL on one card, but it is necessary to have
+* both devices open to be able to detect hook switch changes. The record
+* codec and rate of each card must match the playback codec and rate of
+* the other card for this to work properly.
+*
+******************************************************************************/
+
+#define IXJCTL_INTERCOM_START _IOW ('q', 0xFD, int)
+#define IXJCTL_INTERCOM_STOP _IOW ('q', 0xFE, int)
diff --git a/pfinet/linux-src/include/linux/joystick.h b/pfinet/linux-src/include/linux/joystick.h
new file mode 100644
index 00000000..b5b39d0f
--- /dev/null
+++ b/pfinet/linux-src/include/linux/joystick.h
@@ -0,0 +1,280 @@
+#ifndef _LINUX_JOYSTICK_H
+#define _LINUX_JOYSTICK_H
+
+/*
+ * /usr/include/linux/joystick.h Version 1.2
+ *
+ * Copyright (C) 1996-1999 Vojtech Pavlik
+ *
+ * Sponsored by SuSE
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
+ */
+
+#include <asm/types.h>
+#include <linux/module.h>
+
+/*
+ * Version
+ */
+
+#define JS_VERSION 0x01020f
+
+/*
+ * Types and constants for reading from /dev/js
+ */
+
+#define JS_EVENT_BUTTON 0x01 /* button pressed/released */
+#define JS_EVENT_AXIS 0x02 /* joystick moved */
+#define JS_EVENT_INIT 0x80 /* initial state of device */
+
+struct js_event {
+ __u32 time; /* event timestamp in miliseconds */
+ __s16 value; /* value */
+ __u8 type; /* event type */
+ __u8 number; /* axis/button number */
+};
+
+/*
+ * IOCTL commands for joystick driver
+ */
+
+#define JSIOCGVERSION _IOR('j', 0x01, __u32) /* get driver version */
+
+#define JSIOCGAXES _IOR('j', 0x11, __u8) /* get number of axes */
+#define JSIOCGBUTTONS _IOR('j', 0x12, __u8) /* get number of buttons */
+#define JSIOCGNAME(len) _IOC(_IOC_READ, 'j', 0x13, len) /* get identifier string */
+
+#define JSIOCSCORR _IOW('j', 0x21, struct js_corr) /* set correction values */
+#define JSIOCGCORR _IOR('j', 0x22, struct js_corr) /* get correction values */
+
+/*
+ * Types and constants for get/set correction
+ */
+
+#define JS_CORR_NONE 0x00 /* returns raw values */
+#define JS_CORR_BROKEN 0x01 /* broken line */
+
+struct js_corr {
+ __s32 coef[8];
+ __s16 prec;
+ __u16 type;
+};
+
+/*
+ * v0.x compatibility definitions
+ */
+
+#define JS_RETURN sizeof(struct JS_DATA_TYPE)
+#define JS_TRUE 1
+#define JS_FALSE 0
+#define JS_X_0 0x01
+#define JS_Y_0 0x02
+#define JS_X_1 0x04
+#define JS_Y_1 0x08
+#define JS_MAX 2
+
+#define JS_DEF_TIMEOUT 0x1300
+#define JS_DEF_CORR 0
+#define JS_DEF_TIMELIMIT 10L
+
+#define JS_SET_CAL 1
+#define JS_GET_CAL 2
+#define JS_SET_TIMEOUT 3
+#define JS_GET_TIMEOUT 4
+#define JS_SET_TIMELIMIT 5
+#define JS_GET_TIMELIMIT 6
+#define JS_GET_ALL 7
+#define JS_SET_ALL 8
+
+struct JS_DATA_TYPE {
+ int buttons;
+ int x;
+ int y;
+};
+
+struct JS_DATA_SAVE_TYPE {
+ int JS_TIMEOUT;
+ int BUSY;
+ long JS_EXPIRETIME;
+ long JS_TIMELIMIT;
+ struct JS_DATA_TYPE JS_SAVE;
+ struct JS_DATA_TYPE JS_CORR;
+};
+
+/*
+ * Internal definitions
+ */
+
+#ifdef __KERNEL__
+
+#define JS_BUFF_SIZE 64 /* output buffer size */
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
+#error "You need to use at least v2.2 Linux kernel."
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+#include <asm/spinlock.h>
+typedef struct wait_queue *wait_queue_head_t;
+#define __setup(a,b)
+#define BASE_ADDRESS(x,i) ((x)->base_address[i])
+#define DECLARE_WAITQUEUE(x,y) struct wait_queue x = { y, NULL }
+#define init_waitqueue_head(x) do { *(x) = NULL; } while (0)
+#define __set_current_state(x) current->state = x
+#define SETUP_PARAM char *str, int *ints
+#define SETUP_PARSE(x) do {} while (0)
+#else
+#include <linux/spinlock.h>
+#define BASE_ADDRESS(x,i) ((x)->resource[i].start)
+#define SETUP_PARAM char *str
+#define SETUP_PARSE(x) int ints[x]; get_options(str, x, ints)
+#endif
+
+#define PCI_VENDOR_ID_AUREAL 0x12eb
+
+/*
+ * Parport stuff
+ */
+
+#include <linux/parport.h>
+
+#define JS_PAR_STATUS_INVERT (0x80)
+#define JS_PAR_CTRL_INVERT (0x04)
+#define JS_PAR_DATA_IN(y) parport_read_data(y->port)
+#define JS_PAR_DATA_OUT(x,y) parport_write_data(y->port, x)
+#define JS_PAR_STATUS(y) parport_read_status(y->port)
+
+#ifndef PARPORT_NEED_GENERIC_OPS
+#define JS_PAR_CTRL_IN(y) parport_read_control(y->port)
+#else
+#define JS_PAR_CTRL_IN(y) inb(y->port->base+2)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+#define JS_PAR_CTRL_OUT(x,y) parport_write_control(y->port, x)
+#define JS_PAR_ECTRL_OUT(x,y) parport_write_econtrol(y->port, x)
+#else
+#define JS_PAR_CTRL_OUT(x,y) \
+ do { \
+ if ((x) & 0x20) parport_data_reverse(y->port); \
+ else parport_data_forward(y->port); \
+ parport_write_control(y->port, (x) & ~0x20); \
+ } while (0)
+#define JS_PAR_ECTRL_OUT(x,y) /*parport sets PS/2 mode on ECR chips */
+#define PARPORT_MODE_PCPS2 PARPORT_MODE_TRISTATE
+#define PARPORT_MODE_PCECPPS2 PARPORT_MODE_TRISTATE
+#endif
+
+/*
+ * Internal types
+ */
+
+struct js_dev;
+
+typedef int (*js_read_func)(void *info, int **axes, int **buttons);
+typedef int (*js_ops_func)(struct js_dev *dev);
+
+struct js_data {
+ int *axes;
+ int *buttons;
+};
+
+struct js_dev {
+ struct js_dev *next;
+ struct js_list *list;
+ struct js_port *port;
+ wait_queue_head_t wait;
+ struct js_data cur;
+ struct js_data new;
+ struct js_corr *corr;
+ struct js_event buff[JS_BUFF_SIZE];
+ js_ops_func open;
+ js_ops_func close;
+ int ahead;
+ int bhead;
+ int tail;
+ int num_axes;
+ int num_buttons;
+ char *name;
+};
+
+struct js_list {
+ struct js_list *next;
+ struct js_dev *dev;
+ int tail;
+ int startup;
+};
+
+struct js_port {
+ struct js_port *next;
+ struct js_port *prev;
+ js_read_func read;
+ struct js_dev **devs;
+ int **axes;
+ int **buttons;
+ struct js_corr **corr;
+ void *info;
+ int ndevs;
+ int fail;
+ int total;
+};
+
+/*
+ * Sub-module interface
+ */
+
+extern struct js_port *js_register_port(struct js_port *port, void *info,
+ int devs, int infos, js_read_func read);
+extern struct js_port *js_unregister_port(struct js_port *port);
+
+extern int js_register_device(struct js_port *port, int number, int axes,
+ int buttons, char *name, js_ops_func open, js_ops_func close);
+extern void js_unregister_device(struct js_dev *dev);
+
+/*
+ * Kernel interface
+ */
+
+extern int js_init(void);
+extern int js_am_init(void);
+extern int js_an_init(void);
+extern int js_as_init(void);
+extern int js_console_init(void);
+extern int js_cr_init(void);
+extern int js_db9_init(void);
+extern int js_gr_init(void);
+extern int js_l4_init(void);
+extern int js_lt_init(void);
+extern int js_mag_init(void);
+extern int js_pci_init(void);
+extern int js_sw_init(void);
+extern int js_sball_init(void);
+extern int js_orb_init(void);
+extern int js_tm_init(void);
+extern int js_tg_init(void);
+extern int js_war_init(void);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_JOYSTICK_H */
diff --git a/pfinet/linux-src/include/linux/kbd_diacr.h b/pfinet/linux-src/include/linux/kbd_diacr.h
new file mode 100644
index 00000000..1c1a3ff0
--- /dev/null
+++ b/pfinet/linux-src/include/linux/kbd_diacr.h
@@ -0,0 +1,8 @@
+#ifndef _DIACR_H
+#define _DIACR_H
+#include <linux/kd.h>
+
+extern struct kbdiacr accent_table[];
+extern unsigned int accent_table_size;
+
+#endif /* _DIACR_H */
diff --git a/pfinet/linux-src/include/linux/kbd_kern.h b/pfinet/linux-src/include/linux/kbd_kern.h
new file mode 100644
index 00000000..f5ae9ed9
--- /dev/null
+++ b/pfinet/linux-src/include/linux/kbd_kern.h
@@ -0,0 +1,164 @@
+#ifndef _KBD_KERN_H
+#define _KBD_KERN_H
+
+#include <linux/interrupt.h>
+#include <linux/keyboard.h>
+
+extern int shift_state;
+
+extern char *func_table[MAX_NR_FUNC];
+extern char func_buf[];
+extern char *funcbufptr;
+extern int funcbufsize, funcbufleft;
+
+/*
+ * kbd->xxx contains the VC-local things (flag settings etc..)
+ *
+ * Note: externally visible are LED_SCR, LED_NUM, LED_CAP defined in kd.h
+ * The code in KDGETLED / KDSETLED depends on the internal and
+ * external order being the same.
+ *
+ * Note: lockstate is used as index in the array key_map.
+ */
+struct kbd_struct {
+
+ unsigned char lockstate;
+/* 8 modifiers - the names do not have any meaning at all;
+ they can be associated to arbitrarily chosen keys */
+#define VC_SHIFTLOCK KG_SHIFT /* shift lock mode */
+#define VC_ALTGRLOCK KG_ALTGR /* altgr lock mode */
+#define VC_CTRLLOCK KG_CTRL /* control lock mode */
+#define VC_ALTLOCK KG_ALT /* alt lock mode */
+#define VC_SHIFTLLOCK KG_SHIFTL /* shiftl lock mode */
+#define VC_SHIFTRLOCK KG_SHIFTR /* shiftr lock mode */
+#define VC_CTRLLLOCK KG_CTRLL /* ctrll lock mode */
+#define VC_CTRLRLOCK KG_CTRLR /* ctrlr lock mode */
+ unsigned char slockstate; /* for `sticky' Shift, Ctrl, etc. */
+
+ unsigned char ledmode:2; /* one 2-bit value */
+#define LED_SHOW_FLAGS 0 /* traditional state */
+#define LED_SHOW_IOCTL 1 /* only change leds upon ioctl */
+#define LED_SHOW_MEM 2 /* `heartbeat': peek into memory */
+
+ unsigned char ledflagstate:3; /* flags, not lights */
+ unsigned char default_ledflagstate:3;
+#define VC_SCROLLOCK 0 /* scroll-lock mode */
+#define VC_NUMLOCK 1 /* numeric lock mode */
+#define VC_CAPSLOCK 2 /* capslock mode */
+
+ unsigned char kbdmode:2; /* one 2-bit value */
+#define VC_XLATE 0 /* translate keycodes using keymap */
+#define VC_MEDIUMRAW 1 /* medium raw (keycode) mode */
+#define VC_RAW 2 /* raw (scancode) mode */
+#define VC_UNICODE 3 /* Unicode mode */
+
+ unsigned char modeflags:5;
+#define VC_APPLIC 0 /* application key mode */
+#define VC_CKMODE 1 /* cursor key mode */
+#define VC_REPEAT 2 /* keyboard repeat */
+#define VC_CRLF 3 /* 0 - enter sends CR, 1 - enter sends CRLF */
+#define VC_META 4 /* 0 - meta, 1 - meta=prefix with ESC */
+};
+
+extern struct kbd_struct kbd_table[];
+
+extern int kbd_init(void);
+
+extern unsigned char getledstate(void);
+extern void setledstate(struct kbd_struct *kbd, unsigned int led);
+
+extern int do_poke_blanked_console;
+
+extern inline void show_console(void)
+{
+ do_poke_blanked_console = 1;
+ mark_bh(CONSOLE_BH);
+}
+
+extern inline void set_console(int nr)
+{
+ want_console = nr;
+ mark_bh(CONSOLE_BH);
+}
+
+extern inline void set_leds(void)
+{
+ mark_bh(KEYBOARD_BH);
+}
+
+extern inline int vc_kbd_mode(struct kbd_struct * kbd, int flag)
+{
+ return ((kbd->modeflags >> flag) & 1);
+}
+
+extern inline int vc_kbd_led(struct kbd_struct * kbd, int flag)
+{
+ return ((kbd->ledflagstate >> flag) & 1);
+}
+
+extern inline void set_vc_kbd_mode(struct kbd_struct * kbd, int flag)
+{
+ kbd->modeflags |= 1 << flag;
+}
+
+extern inline void set_vc_kbd_led(struct kbd_struct * kbd, int flag)
+{
+ kbd->ledflagstate |= 1 << flag;
+}
+
+extern inline void clr_vc_kbd_mode(struct kbd_struct * kbd, int flag)
+{
+ kbd->modeflags &= ~(1 << flag);
+}
+
+extern inline void clr_vc_kbd_led(struct kbd_struct * kbd, int flag)
+{
+ kbd->ledflagstate &= ~(1 << flag);
+}
+
+extern inline void chg_vc_kbd_lock(struct kbd_struct * kbd, int flag)
+{
+ kbd->lockstate ^= 1 << flag;
+}
+
+extern inline void chg_vc_kbd_slock(struct kbd_struct * kbd, int flag)
+{
+ kbd->slockstate ^= 1 << flag;
+}
+
+extern inline void chg_vc_kbd_mode(struct kbd_struct * kbd, int flag)
+{
+ kbd->modeflags ^= 1 << flag;
+}
+
+extern inline void chg_vc_kbd_led(struct kbd_struct * kbd, int flag)
+{
+ kbd->ledflagstate ^= 1 << flag;
+}
+
+#define U(x) ((x) ^ 0xf000)
+
+/* keyboard.c */
+
+struct console;
+
+int getkeycode(unsigned int scancode);
+int setkeycode(unsigned int scancode, unsigned int keycode);
+void compute_shiftstate(void);
+int keyboard_wait_for_keypress(struct console *);
+
+/* defkeymap.c */
+
+extern unsigned int keymap_count;
+
+/* console.c */
+
+extern task_queue con_task_queue;
+
+extern inline void con_schedule_flip(struct tty_struct *t)
+{
+ queue_task(&t->flip.tqueue, &con_task_queue);
+ mark_bh(CONSOLE_BH);
+}
+
+#endif
diff --git a/pfinet/linux-src/include/linux/kbd_ll.h b/pfinet/linux-src/include/linux/kbd_ll.h
new file mode 100644
index 00000000..02ebf8ec
--- /dev/null
+++ b/pfinet/linux-src/include/linux/kbd_ll.h
@@ -0,0 +1,12 @@
+/*
+ * Interface between the low-level keyboard driver and the keymapper
+ */
+
+#ifndef _KBD_LL_H
+#define _KBD_LL_H
+
+extern struct pt_regs *kbd_pt_regs;
+
+void handle_scancode(unsigned char scancode, int down);
+
+#endif /* _KBD_LL_H */
diff --git a/pfinet/linux-src/include/linux/kd.h b/pfinet/linux-src/include/linux/kd.h
new file mode 100644
index 00000000..c717c198
--- /dev/null
+++ b/pfinet/linux-src/include/linux/kd.h
@@ -0,0 +1,180 @@
+#ifndef _LINUX_KD_H
+#define _LINUX_KD_H
+#include <linux/types.h>
+
+/* 0x4B is 'K', to avoid collision with termios and vt */
+
+#define GIO_FONT 0x4B60 /* gets font in expanded form */
+#define PIO_FONT 0x4B61 /* use font in expanded form */
+
+#define GIO_FONTX 0x4B6B /* get font using struct consolefontdesc */
+#define PIO_FONTX 0x4B6C /* set font using struct consolefontdesc */
+struct consolefontdesc {
+ unsigned short charcount; /* characters in font (256 or 512) */
+ unsigned short charheight; /* scan lines per character (1-32) */
+ char *chardata; /* font data in expanded form */
+};
+
+#define PIO_FONTRESET 0x4B6D /* reset to default font */
+
+#define GIO_CMAP 0x4B70 /* gets colour palette on VGA+ */
+#define PIO_CMAP 0x4B71 /* sets colour palette on VGA+ */
+
+#define KIOCSOUND 0x4B2F /* start sound generation (0 for off) */
+#define KDMKTONE 0x4B30 /* generate tone */
+
+#define KDGETLED 0x4B31 /* return current led state */
+#define KDSETLED 0x4B32 /* set led state [lights, not flags] */
+#define LED_SCR 0x01 /* scroll lock led */
+#define LED_CAP 0x04 /* caps lock led */
+#define LED_NUM 0x02 /* num lock led */
+
+#define KDGKBTYPE 0x4B33 /* get keyboard type */
+#define KB_84 0x01
+#define KB_101 0x02 /* this is what we always answer */
+#define KB_OTHER 0x03
+
+#define KDADDIO 0x4B34 /* add i/o port as valid */
+#define KDDELIO 0x4B35 /* del i/o port as valid */
+#define KDENABIO 0x4B36 /* enable i/o to video board */
+#define KDDISABIO 0x4B37 /* disable i/o to video board */
+
+#define KDSETMODE 0x4B3A /* set text/graphics mode */
+#define KD_TEXT 0x00
+#define KD_GRAPHICS 0x01
+#define KD_TEXT0 0x02 /* obsolete */
+#define KD_TEXT1 0x03 /* obsolete */
+#define KDGETMODE 0x4B3B /* get current mode */
+
+#define KDMAPDISP 0x4B3C /* map display into address space */
+#define KDUNMAPDISP 0x4B3D /* unmap display from address space */
+
+typedef char scrnmap_t;
+#define E_TABSZ 256
+#define GIO_SCRNMAP 0x4B40 /* get screen mapping from kernel */
+#define PIO_SCRNMAP 0x4B41 /* put screen mapping table in kernel */
+#define GIO_UNISCRNMAP 0x4B69 /* get full Unicode screen mapping */
+#define PIO_UNISCRNMAP 0x4B6A /* set full Unicode screen mapping */
+
+#define GIO_UNIMAP 0x4B66 /* get unicode-to-font mapping from kernel */
+struct unipair {
+ unsigned short unicode;
+ unsigned short fontpos;
+};
+struct unimapdesc {
+ unsigned short entry_ct;
+ struct unipair *entries;
+};
+#define PIO_UNIMAP 0x4B67 /* put unicode-to-font mapping in kernel */
+#define PIO_UNIMAPCLR 0x4B68 /* clear table, possibly advise hash algorithm */
+struct unimapinit {
+ unsigned short advised_hashsize; /* 0 if no opinion */
+ unsigned short advised_hashstep; /* 0 if no opinion */
+ unsigned short advised_hashlevel; /* 0 if no opinion */
+};
+
+#define UNI_DIRECT_BASE 0xF000 /* start of Direct Font Region */
+#define UNI_DIRECT_MASK 0x01FF /* Direct Font Region bitmask */
+
+#define K_RAW 0x00
+#define K_XLATE 0x01
+#define K_MEDIUMRAW 0x02
+#define K_UNICODE 0x03
+#define KDGKBMODE 0x4B44 /* gets current keyboard mode */
+#define KDSKBMODE 0x4B45 /* sets current keyboard mode */
+
+#define K_METABIT 0x03
+#define K_ESCPREFIX 0x04
+#define KDGKBMETA 0x4B62 /* gets meta key handling mode */
+#define KDSKBMETA 0x4B63 /* sets meta key handling mode */
+
+#define K_SCROLLLOCK 0x01
+#define K_CAPSLOCK 0x02
+#define K_NUMLOCK 0x04
+#define KDGKBLED 0x4B64 /* get led flags (not lights) */
+#define KDSKBLED 0x4B65 /* set led flags (not lights) */
+
+struct kbentry {
+ unsigned char kb_table;
+ unsigned char kb_index;
+ unsigned short kb_value;
+};
+#define K_NORMTAB 0x00
+#define K_SHIFTTAB 0x01
+#define K_ALTTAB 0x02
+#define K_ALTSHIFTTAB 0x03
+
+#define KDGKBENT 0x4B46 /* gets one entry in translation table */
+#define KDSKBENT 0x4B47 /* sets one entry in translation table */
+
+struct kbsentry {
+ unsigned char kb_func;
+ unsigned char kb_string[512];
+};
+#define KDGKBSENT 0x4B48 /* gets one function key string entry */
+#define KDSKBSENT 0x4B49 /* sets one function key string entry */
+
+struct kbdiacr {
+ unsigned char diacr, base, result;
+};
+struct kbdiacrs {
+ unsigned int kb_cnt; /* number of entries in following array */
+ struct kbdiacr kbdiacr[256]; /* MAX_DIACR from keyboard.h */
+};
+#define KDGKBDIACR 0x4B4A /* read kernel accent table */
+#define KDSKBDIACR 0x4B4B /* write kernel accent table */
+
+struct kbkeycode {
+ unsigned int scancode, keycode;
+};
+#define KDGETKEYCODE 0x4B4C /* read kernel keycode table entry */
+#define KDSETKEYCODE 0x4B4D /* write kernel keycode table entry */
+
+#define KDSIGACCEPT 0x4B4E /* accept kbd generated signals */
+
+struct hwclk_time {
+ unsigned sec; /* 0..59 */
+ unsigned min; /* 0..59 */
+ unsigned hour; /* 0..23 */
+ unsigned day; /* 1..31 */
+ unsigned mon; /* 0..11 */
+ unsigned year; /* 70... */
+ int wday; /* 0..6, 0 is Sunday, -1 means unknown/don't set */
+};
+
+#define KDGHWCLK 0x4B50 /* get hardware clock */
+#define KDSHWCLK 0x4B51 /* set hardware clock */
+
+struct kbd_repeat {
+ int delay; /* in msec; <= 0: don't change */
+ int rate; /* in msec; <= 0: don't change */
+};
+
+#define KDKBDREP 0x4B52 /* set keyboard delay/repeat rate;
+ * actually used values are returned */
+
+#define KDFONTOP 0x4B72 /* font operations */
+
+struct console_font_op {
+ unsigned int op; /* operation code KD_FONT_OP_* */
+ unsigned int flags; /* KD_FONT_FLAG_* */
+ unsigned int width, height; /* font size */
+ unsigned int charcount;
+ unsigned char *data; /* font data with height fixed to 32 */
+};
+
+#define KD_FONT_OP_SET 0 /* Set font */
+#define KD_FONT_OP_GET 1 /* Get font */
+#define KD_FONT_OP_SET_DEFAULT 2 /* Set font to default, data points to name / NULL */
+#define KD_FONT_OP_COPY 3 /* Copy from another console */
+
+#define KD_FONT_FLAG_DONT_RECALC 1 /* Don't recalculate hw charcell size [compat] */
+#ifdef __KERNEL__
+#define KD_FONT_FLAG_OLD 0x80000000 /* Invoked via old interface [compat] */
+#endif
+
+/* note: 0x4B00-0x4B4E all have had a value at some time;
+ don't reuse for the time being */
+/* note: 0x4B60-0x4B6D, 0x4B70-0x4B72 used above */
+
+#endif /* _LINUX_KD_H */
diff --git a/pfinet/linux-src/include/linux/kdev_t.h b/pfinet/linux-src/include/linux/kdev_t.h
new file mode 100644
index 00000000..a06cdb9e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/kdev_t.h
@@ -0,0 +1,114 @@
+#ifndef _LINUX_KDEV_T_H
+#define _LINUX_KDEV_T_H
+#ifdef __KERNEL__
+/*
+As a preparation for the introduction of larger device numbers,
+we introduce a type kdev_t to hold them. No information about
+this type is known outside of this include file.
+
+Objects of type kdev_t designate a device. Outside of the kernel
+the corresponding things are objects of type dev_t - usually an
+integral type with the device major and minor in the high and low
+bits, respectively. Conversion is done by
+
+extern kdev_t to_kdev_t(int);
+
+It is up to the various file systems to decide how objects of type
+dev_t are stored on disk.
+The only other point of contact between kernel and outside world
+are the system calls stat and mknod, new versions of which will
+eventually have to be used in libc.
+
+[Unfortunately, the floppy control ioctls fail to hide the internal
+kernel structures, and the fd_device field of a struct floppy_drive_struct
+is user-visible. So, it remains a dev_t for the moment, with some ugly
+conversions in floppy.c.]
+
+Inside the kernel, we aim for a kdev_t type that is a pointer
+to a structure with information about the device (like major,
+minor, size, blocksize, sectorsize, name, read-only flag,
+struct file_operations etc.).
+
+However, for the time being we let kdev_t be almost the same as dev_t:
+
+typedef struct { unsigned short major, minor; } kdev_t;
+
+Admissible operations on an object of type kdev_t:
+- passing it along
+- comparing it for equality with another such object
+- storing it in ROOT_DEV, inode->i_dev, inode->i_rdev, sb->s_dev,
+ bh->b_dev, req->rq_dev, de->dc_dev, tty->device
+- using its bit pattern as argument in a hash function
+- finding its major and minor
+- complaining about it
+
+An object of type kdev_t is created only by the function MKDEV(),
+with the single exception of the constant 0 (no device).
+
+Right now the other information mentioned above is usually found
+in static arrays indexed by major or major,minor.
+
+An obstacle to immediately using
+ typedef struct { ... (* lots of information *) } *kdev_t
+is the case of mknod used to create a block device that the
+kernel doesn't know about at present (but first learns about
+when some module is inserted).
+
+aeb - 950811
+*/
+
+/* Since MINOR(dev) is used as index in static arrays,
+ the kernel is not quite ready yet for larger minors.
+ However, everything runs fine with an arbitrary kdev_t type. */
+
+#define MINORBITS 8
+#define MINORMASK ((1U << MINORBITS) - 1)
+
+typedef unsigned short kdev_t;
+
+#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
+#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
+#define HASHDEV(dev) ((unsigned int) (dev))
+#define NODEV 0
+#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
+#define B_FREE 0xffff /* yuk */
+
+extern char * kdevname(kdev_t); /* note: returns pointer to static data! */
+
+/*
+As long as device numbers in the outside world have 16 bits only,
+we use these conversions.
+*/
+
+static inline unsigned int kdev_t_to_nr(kdev_t dev) {
+ return (MAJOR(dev)<<8) | MINOR(dev);
+}
+
+static inline kdev_t to_kdev_t(int dev)
+{
+ int major, minor;
+#if 0
+ major = (dev >> 16);
+ if (!major) {
+ major = (dev >> 8);
+ minor = (dev & 0xff);
+ } else
+ minor = (dev & 0xffff);
+#else
+ major = (dev >> 8);
+ minor = (dev & 0xff);
+#endif
+ return MKDEV(major, minor);
+}
+
+#else /* __KERNEL__ */
+
+/*
+Some programs want their definitions of MAJOR and MINOR and MKDEV
+from the kernel sources. These must be the externally visible ones.
+*/
+#define MAJOR(dev) ((dev)>>8)
+#define MINOR(dev) ((dev) & 0xff)
+#define MKDEV(ma,mi) ((ma)<<8 | (mi))
+#endif /* __KERNEL__ */
+#endif
diff --git a/pfinet/linux-src/include/linux/kernel.h b/pfinet/linux-src/include/linux/kernel.h
new file mode 100644
index 00000000..73a0a689
--- /dev/null
+++ b/pfinet/linux-src/include/linux/kernel.h
@@ -0,0 +1,96 @@
+#ifndef _LINUX_KERNEL_H
+#define _LINUX_KERNEL_H
+
+/*
+ * 'kernel.h' contains some often-used function prototypes etc
+ */
+
+#ifdef __KERNEL__
+
+#include <stdarg.h>
+#include <linux/linkage.h>
+
+/* Optimization barrier */
+/* The "volatile" is due to gcc bugs */
+#define barrier() __asm__ __volatile__("": : :"memory")
+
+#define INT_MAX ((int)(~0U>>1))
+#define UINT_MAX (~0U)
+#define LONG_MAX ((long)(~0UL>>1))
+#define ULONG_MAX (~0UL)
+
+#define STACK_MAGIC 0xdeadbeef
+
+#define KERN_EMERG "<0>" /* system is unusable */
+#define KERN_ALERT "<1>" /* action must be taken immediately */
+#define KERN_CRIT "<2>" /* critical conditions */
+#define KERN_ERR "<3>" /* error conditions */
+#define KERN_WARNING "<4>" /* warning conditions */
+#define KERN_NOTICE "<5>" /* normal but significant condition */
+#define KERN_INFO "<6>" /* informational */
+#define KERN_DEBUG "<7>" /* debug-level messages */
+
+# define NORET_TYPE /**/
+# define ATTRIB_NORET __attribute__((noreturn))
+# define NORET_AND noreturn,
+
+#ifdef __i386__
+#define FASTCALL(x) x __attribute__((regparm(3)))
+#else
+#define FASTCALL(x) x
+#endif
+
+extern void math_error(void);
+extern struct notifier_block *panic_notifier_list;
+NORET_TYPE void panic(const char * fmt, ...)
+ __attribute__ ((NORET_AND format (printf, 1, 2)));
+NORET_TYPE void do_exit(long error_code)
+ ATTRIB_NORET;
+extern unsigned long simple_strtoul(const char *,char **,unsigned int);
+extern long simple_strtol(const char *,char **,unsigned int);
+extern int sprintf(char * buf, const char * fmt, ...);
+extern int vsprintf(char *buf, const char *, va_list);
+
+extern int session_of_pgrp(int pgrp);
+
+asmlinkage int printk(const char * fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+
+#if DEBUG
+#define pr_debug(fmt,arg...) \
+ printk(KERN_DEBUG fmt,##arg)
+#else
+#define pr_debug(fmt,arg...) \
+ do { } while (0)
+#endif
+
+#define pr_info(fmt,arg...) \
+ printk(KERN_INFO fmt,##arg)
+
+/*
+ * Display an IP address in readable format.
+ */
+
+#define NIPQUAD(addr) \
+ ((unsigned char *)&addr)[0], \
+ ((unsigned char *)&addr)[1], \
+ ((unsigned char *)&addr)[2], \
+ ((unsigned char *)&addr)[3]
+
+#endif /* __KERNEL__ */
+
+#define SI_LOAD_SHIFT 16
+struct sysinfo {
+ long uptime; /* Seconds since boot */
+ unsigned long loads[3]; /* 1, 5, and 15 minute load averages */
+ unsigned long totalram; /* Total usable main memory size */
+ unsigned long freeram; /* Available memory size */
+ unsigned long sharedram; /* Amount of shared memory */
+ unsigned long bufferram; /* Memory used by buffers */
+ unsigned long totalswap; /* Total swap space size */
+ unsigned long freeswap; /* swap space still available */
+ unsigned short procs; /* Number of current processes */
+ char _f[22]; /* Pads structure to 64 bytes */
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/kernel_stat.h b/pfinet/linux-src/include/linux/kernel_stat.h
new file mode 100644
index 00000000..cd584c1a
--- /dev/null
+++ b/pfinet/linux-src/include/linux/kernel_stat.h
@@ -0,0 +1,50 @@
+#ifndef _LINUX_KERNEL_STAT_H
+#define _LINUX_KERNEL_STAT_H
+
+#include <asm/irq.h>
+#include <linux/smp.h>
+#include <linux/tasks.h>
+
+/*
+ * 'kernel_stat.h' contains the definitions needed for doing
+ * some kernel statistics (CPU usage, context switches ...),
+ * used by rstatd/perfmeter
+ */
+
+#define DK_NDRIVE 4
+
+struct kernel_stat {
+ unsigned int cpu_user, cpu_nice, cpu_system;
+ unsigned int per_cpu_user[NR_CPUS],
+ per_cpu_nice[NR_CPUS],
+ per_cpu_system[NR_CPUS];
+ unsigned int dk_drive[DK_NDRIVE];
+ unsigned int dk_drive_rio[DK_NDRIVE];
+ unsigned int dk_drive_wio[DK_NDRIVE];
+ unsigned int dk_drive_rblk[DK_NDRIVE];
+ unsigned int dk_drive_wblk[DK_NDRIVE];
+ unsigned int pgpgin, pgpgout;
+ unsigned int pswpin, pswpout;
+ unsigned int irqs[NR_CPUS][NR_IRQS];
+ unsigned int ipackets, opackets;
+ unsigned int ierrors, oerrors;
+ unsigned int collisions;
+ unsigned int context_swtch;
+};
+
+extern struct kernel_stat kstat;
+
+/*
+ * Number of interrupts per specific IRQ source, since bootup
+ */
+extern inline int kstat_irqs (int irq)
+{
+ int i, sum=0;
+
+ for (i = 0 ; i < smp_num_cpus ; i++)
+ sum += kstat.irqs[cpu_logical_map(i)][irq];
+
+ return sum;
+}
+
+#endif /* _LINUX_KERNEL_STAT_H */
diff --git a/pfinet/linux-src/include/linux/kernelcapi.h b/pfinet/linux-src/include/linux/kernelcapi.h
new file mode 100644
index 00000000..5778d2eb
--- /dev/null
+++ b/pfinet/linux-src/include/linux/kernelcapi.h
@@ -0,0 +1,140 @@
+/*
+ * $Id: kernelcapi.h,v 1.4 1999/09/10 17:24:19 calle Exp $
+ *
+ * Kernel CAPI 2.0 Interface for Linux
+ *
+ * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
+ *
+ * $Log: kernelcapi.h,v $
+ * Revision 1.4 1999/09/10 17:24:19 calle
+ * Changes for proposed standard for CAPI2.0:
+ * - AK148 "Linux Exention"
+ *
+ * Revision 1.3 1999/07/01 15:26:56 calle
+ * complete new version (I love it):
+ * + new hardware independed "capi_driver" interface that will make it easy to:
+ * - support other controllers with CAPI-2.0 (i.e. USB Controller)
+ * - write a CAPI-2.0 for the passive cards
+ * - support serial link CAPI-2.0 boxes.
+ * + wrote "capi_driver" for all supported cards.
+ * + "capi_driver" (supported cards) now have to be configured with
+ * make menuconfig, in the past all supported cards where included
+ * at once.
+ * + new and better informations in /proc/capi/
+ * + new ioctl to switch trace of capi messages per controller
+ * using "avmcapictrl trace [contr] on|off|...."
+ * + complete testcircle with all supported cards and also the
+ * PCMCIA cards (now patch for pcmcia-cs-3.0.13 needed) done.
+ *
+ * Revision 1.2 1999/06/21 15:24:26 calle
+ * extend information in /proc.
+ *
+ * Revision 1.1 1997/03/04 21:27:33 calle
+ * First version in isdn4linux
+ *
+ * Revision 2.2 1997/02/12 09:31:39 calle
+ * new version
+ *
+ * Revision 1.1 1997/01/31 10:32:20 calle
+ * Initial revision
+ *
+ *
+ */
+#ifndef __KERNELCAPI_H__
+#define __KERNELCAPI_H__
+
+#define CAPI_MAXAPPL 20 /*
+ * maximum number of applications
+ */
+#define CAPI_MAXCONTR 10 /*
+ * maximum number of controller
+ */
+#define CAPI_MAXDATAWINDOW 8
+
+
+typedef struct kcapi_flagdef {
+ int contr;
+ int flag;
+} kcapi_flagdef;
+
+/* new ioctls >= 10 */
+#define KCAPI_CMD_TRACE 10
+
+/*
+ * flag > 2 => trace also data
+ * flag & 1 => show trace
+ */
+#define KCAPI_TRACE_OFF 0
+#define KCAPI_TRACE_SHORT_NO_DATA 1
+#define KCAPI_TRACE_FULL_NO_DATA 2
+#define KCAPI_TRACE_SHORT 3
+#define KCAPI_TRACE_FULL 4
+
+
+#ifdef __KERNEL__
+
+struct capi_interface {
+ __u16 (*capi_isinstalled) (void);
+
+ __u16 (*capi_register) (capi_register_params * rparam, __u16 * applidp);
+ __u16 (*capi_release) (__u16 applid);
+ __u16 (*capi_put_message) (__u16 applid, struct sk_buff * msg);
+ __u16 (*capi_get_message) (__u16 applid, struct sk_buff ** msgp);
+ __u16 (*capi_set_signal) (__u16 applid,
+ void (*signal) (__u16 applid, __u32 param),
+ __u32 param);
+ __u16 (*capi_get_manufacturer) (__u32 contr, __u8 buf[CAPI_MANUFACTURER_LEN]);
+ __u16 (*capi_get_version) (__u32 contr, struct capi_version * verp);
+ __u16(*capi_get_serial) (__u32 contr, __u8 serial[CAPI_SERIAL_LEN]);
+ __u16(*capi_get_profile) (__u32 contr, struct capi_profile * profp);
+
+ /*
+ * to init controllers, data is always in user memory
+ */
+ int (*capi_manufacturer) (unsigned int cmd, void *data);
+
+};
+
+#define KCI_CONTRUP 0
+#define KCI_CONTRDOWN 1
+
+struct capi_interface_user {
+ char name[20];
+ void (*callback) (unsigned int cmd, __u32 contr, void *data);
+ /* internal */
+ struct capi_interface_user *next;
+};
+
+struct capi_interface *attach_capi_interface(struct capi_interface_user *);
+int detach_capi_interface(struct capi_interface_user *);
+
+
+#define CAPI_NOERROR 0x0000
+
+#define CAPI_TOOMANYAPPLS 0x1001
+#define CAPI_LOGBLKSIZETOSMALL 0x1002
+#define CAPI_BUFFEXECEEDS64K 0x1003
+#define CAPI_MSGBUFSIZETOOSMALL 0x1004
+#define CAPI_ANZLOGCONNNOTSUPPORTED 0x1005
+#define CAPI_REGRESERVED 0x1006
+#define CAPI_REGBUSY 0x1007
+#define CAPI_REGOSRESOURCEERR 0x1008
+#define CAPI_REGNOTINSTALLED 0x1009
+#define CAPI_REGCTRLERNOTSUPPORTEXTEQUIP 0x100a
+#define CAPI_REGCTRLERONLYSUPPORTEXTEQUIP 0x100b
+
+#define CAPI_ILLAPPNR 0x1101
+#define CAPI_ILLCMDORSUBCMDORMSGTOSMALL 0x1102
+#define CAPI_SENDQUEUEFULL 0x1103
+#define CAPI_RECEIVEQUEUEEMPTY 0x1104
+#define CAPI_RECEIVEOVERFLOW 0x1105
+#define CAPI_UNKNOWNNOTPAR 0x1106
+#define CAPI_MSGBUSY 0x1107
+#define CAPI_MSGOSRESOURCEERR 0x1108
+#define CAPI_MSGNOTINSTALLED 0x1109
+#define CAPI_MSGCTRLERNOTSUPPORTEXTEQUIP 0x110a
+#define CAPI_MSGCTRLERONLYSUPPORTEXTEQUIP 0x110b
+
+#endif /* __KERNEL__ */
+
+#endif /* __KERNELCAPI_H__ */
diff --git a/pfinet/linux-src/include/linux/keyboard.h b/pfinet/linux-src/include/linux/keyboard.h
new file mode 100644
index 00000000..6829c653
--- /dev/null
+++ b/pfinet/linux-src/include/linux/keyboard.h
@@ -0,0 +1,430 @@
+#ifndef __LINUX_KEYBOARD_H
+#define __LINUX_KEYBOARD_H
+
+#define KG_SHIFT 0
+#define KG_CTRL 2
+#define KG_ALT 3
+#define KG_ALTGR 1
+#define KG_SHIFTL 4
+#define KG_SHIFTR 5
+#define KG_CTRLL 6
+#define KG_CTRLR 7
+#define KG_CAPSSHIFT 8
+
+#define NR_SHIFT 9
+
+#define NR_KEYS 128
+#define MAX_NR_KEYMAPS 256
+/* This means 64Kb if all keymaps are allocated. Only the superuser
+ may increase the number of keymaps beyond MAX_NR_OF_USER_KEYMAPS. */
+#define MAX_NR_OF_USER_KEYMAPS 256 /* should be at least 7 */
+
+#ifdef __KERNEL__
+extern const int NR_TYPES;
+extern const int max_vals[];
+extern unsigned short *key_maps[MAX_NR_KEYMAPS];
+extern unsigned short plain_map[NR_KEYS];
+extern struct wait_queue * keypress_wait;
+extern unsigned char keyboard_type;
+#endif
+
+#define MAX_NR_FUNC 256 /* max nr of strings assigned to keys */
+
+#define KT_LATIN 0 /* we depend on this being zero */
+#define KT_LETTER 11 /* symbol that can be acted upon by CapsLock */
+#define KT_FN 1
+#define KT_SPEC 2
+#define KT_PAD 3
+#define KT_DEAD 4
+#define KT_CONS 5
+#define KT_CUR 6
+#define KT_SHIFT 7
+#define KT_META 8
+#define KT_ASCII 9
+#define KT_LOCK 10
+#define KT_SLOCK 12
+
+#define K(t,v) (((t)<<8)|(v))
+#define KTYP(x) ((x) >> 8)
+#define KVAL(x) ((x) & 0xff)
+
+#define K_F1 K(KT_FN,0)
+#define K_F2 K(KT_FN,1)
+#define K_F3 K(KT_FN,2)
+#define K_F4 K(KT_FN,3)
+#define K_F5 K(KT_FN,4)
+#define K_F6 K(KT_FN,5)
+#define K_F7 K(KT_FN,6)
+#define K_F8 K(KT_FN,7)
+#define K_F9 K(KT_FN,8)
+#define K_F10 K(KT_FN,9)
+#define K_F11 K(KT_FN,10)
+#define K_F12 K(KT_FN,11)
+#define K_F13 K(KT_FN,12)
+#define K_F14 K(KT_FN,13)
+#define K_F15 K(KT_FN,14)
+#define K_F16 K(KT_FN,15)
+#define K_F17 K(KT_FN,16)
+#define K_F18 K(KT_FN,17)
+#define K_F19 K(KT_FN,18)
+#define K_F20 K(KT_FN,19)
+#define K_FIND K(KT_FN,20)
+#define K_INSERT K(KT_FN,21)
+#define K_REMOVE K(KT_FN,22)
+#define K_SELECT K(KT_FN,23)
+#define K_PGUP K(KT_FN,24) /* PGUP is a synonym for PRIOR */
+#define K_PGDN K(KT_FN,25) /* PGDN is a synonym for NEXT */
+#define K_MACRO K(KT_FN,26)
+#define K_HELP K(KT_FN,27)
+#define K_DO K(KT_FN,28)
+#define K_PAUSE K(KT_FN,29)
+#define K_F21 K(KT_FN,30)
+#define K_F22 K(KT_FN,31)
+#define K_F23 K(KT_FN,32)
+#define K_F24 K(KT_FN,33)
+#define K_F25 K(KT_FN,34)
+#define K_F26 K(KT_FN,35)
+#define K_F27 K(KT_FN,36)
+#define K_F28 K(KT_FN,37)
+#define K_F29 K(KT_FN,38)
+#define K_F30 K(KT_FN,39)
+#define K_F31 K(KT_FN,40)
+#define K_F32 K(KT_FN,41)
+#define K_F33 K(KT_FN,42)
+#define K_F34 K(KT_FN,43)
+#define K_F35 K(KT_FN,44)
+#define K_F36 K(KT_FN,45)
+#define K_F37 K(KT_FN,46)
+#define K_F38 K(KT_FN,47)
+#define K_F39 K(KT_FN,48)
+#define K_F40 K(KT_FN,49)
+#define K_F41 K(KT_FN,50)
+#define K_F42 K(KT_FN,51)
+#define K_F43 K(KT_FN,52)
+#define K_F44 K(KT_FN,53)
+#define K_F45 K(KT_FN,54)
+#define K_F46 K(KT_FN,55)
+#define K_F47 K(KT_FN,56)
+#define K_F48 K(KT_FN,57)
+#define K_F49 K(KT_FN,58)
+#define K_F50 K(KT_FN,59)
+#define K_F51 K(KT_FN,60)
+#define K_F52 K(KT_FN,61)
+#define K_F53 K(KT_FN,62)
+#define K_F54 K(KT_FN,63)
+#define K_F55 K(KT_FN,64)
+#define K_F56 K(KT_FN,65)
+#define K_F57 K(KT_FN,66)
+#define K_F58 K(KT_FN,67)
+#define K_F59 K(KT_FN,68)
+#define K_F60 K(KT_FN,69)
+#define K_F61 K(KT_FN,70)
+#define K_F62 K(KT_FN,71)
+#define K_F63 K(KT_FN,72)
+#define K_F64 K(KT_FN,73)
+#define K_F65 K(KT_FN,74)
+#define K_F66 K(KT_FN,75)
+#define K_F67 K(KT_FN,76)
+#define K_F68 K(KT_FN,77)
+#define K_F69 K(KT_FN,78)
+#define K_F70 K(KT_FN,79)
+#define K_F71 K(KT_FN,80)
+#define K_F72 K(KT_FN,81)
+#define K_F73 K(KT_FN,82)
+#define K_F74 K(KT_FN,83)
+#define K_F75 K(KT_FN,84)
+#define K_F76 K(KT_FN,85)
+#define K_F77 K(KT_FN,86)
+#define K_F78 K(KT_FN,87)
+#define K_F79 K(KT_FN,88)
+#define K_F80 K(KT_FN,89)
+#define K_F81 K(KT_FN,90)
+#define K_F82 K(KT_FN,91)
+#define K_F83 K(KT_FN,92)
+#define K_F84 K(KT_FN,93)
+#define K_F85 K(KT_FN,94)
+#define K_F86 K(KT_FN,95)
+#define K_F87 K(KT_FN,96)
+#define K_F88 K(KT_FN,97)
+#define K_F89 K(KT_FN,98)
+#define K_F90 K(KT_FN,99)
+#define K_F91 K(KT_FN,100)
+#define K_F92 K(KT_FN,101)
+#define K_F93 K(KT_FN,102)
+#define K_F94 K(KT_FN,103)
+#define K_F95 K(KT_FN,104)
+#define K_F96 K(KT_FN,105)
+#define K_F97 K(KT_FN,106)
+#define K_F98 K(KT_FN,107)
+#define K_F99 K(KT_FN,108)
+#define K_F100 K(KT_FN,109)
+#define K_F101 K(KT_FN,110)
+#define K_F102 K(KT_FN,111)
+#define K_F103 K(KT_FN,112)
+#define K_F104 K(KT_FN,113)
+#define K_F105 K(KT_FN,114)
+#define K_F106 K(KT_FN,115)
+#define K_F107 K(KT_FN,116)
+#define K_F108 K(KT_FN,117)
+#define K_F109 K(KT_FN,118)
+#define K_F110 K(KT_FN,119)
+#define K_F111 K(KT_FN,120)
+#define K_F112 K(KT_FN,121)
+#define K_F113 K(KT_FN,122)
+#define K_F114 K(KT_FN,123)
+#define K_F115 K(KT_FN,124)
+#define K_F116 K(KT_FN,125)
+#define K_F117 K(KT_FN,126)
+#define K_F118 K(KT_FN,127)
+#define K_F119 K(KT_FN,128)
+#define K_F120 K(KT_FN,129)
+#define K_F121 K(KT_FN,130)
+#define K_F122 K(KT_FN,131)
+#define K_F123 K(KT_FN,132)
+#define K_F124 K(KT_FN,133)
+#define K_F125 K(KT_FN,134)
+#define K_F126 K(KT_FN,135)
+#define K_F127 K(KT_FN,136)
+#define K_F128 K(KT_FN,137)
+#define K_F129 K(KT_FN,138)
+#define K_F130 K(KT_FN,139)
+#define K_F131 K(KT_FN,140)
+#define K_F132 K(KT_FN,141)
+#define K_F133 K(KT_FN,142)
+#define K_F134 K(KT_FN,143)
+#define K_F135 K(KT_FN,144)
+#define K_F136 K(KT_FN,145)
+#define K_F137 K(KT_FN,146)
+#define K_F138 K(KT_FN,147)
+#define K_F139 K(KT_FN,148)
+#define K_F140 K(KT_FN,149)
+#define K_F141 K(KT_FN,150)
+#define K_F142 K(KT_FN,151)
+#define K_F143 K(KT_FN,152)
+#define K_F144 K(KT_FN,153)
+#define K_F145 K(KT_FN,154)
+#define K_F146 K(KT_FN,155)
+#define K_F147 K(KT_FN,156)
+#define K_F148 K(KT_FN,157)
+#define K_F149 K(KT_FN,158)
+#define K_F150 K(KT_FN,159)
+#define K_F151 K(KT_FN,160)
+#define K_F152 K(KT_FN,161)
+#define K_F153 K(KT_FN,162)
+#define K_F154 K(KT_FN,163)
+#define K_F155 K(KT_FN,164)
+#define K_F156 K(KT_FN,165)
+#define K_F157 K(KT_FN,166)
+#define K_F158 K(KT_FN,167)
+#define K_F159 K(KT_FN,168)
+#define K_F160 K(KT_FN,169)
+#define K_F161 K(KT_FN,170)
+#define K_F162 K(KT_FN,171)
+#define K_F163 K(KT_FN,172)
+#define K_F164 K(KT_FN,173)
+#define K_F165 K(KT_FN,174)
+#define K_F166 K(KT_FN,175)
+#define K_F167 K(KT_FN,176)
+#define K_F168 K(KT_FN,177)
+#define K_F169 K(KT_FN,178)
+#define K_F170 K(KT_FN,179)
+#define K_F171 K(KT_FN,180)
+#define K_F172 K(KT_FN,181)
+#define K_F173 K(KT_FN,182)
+#define K_F174 K(KT_FN,183)
+#define K_F175 K(KT_FN,184)
+#define K_F176 K(KT_FN,185)
+#define K_F177 K(KT_FN,186)
+#define K_F178 K(KT_FN,187)
+#define K_F179 K(KT_FN,188)
+#define K_F180 K(KT_FN,189)
+#define K_F181 K(KT_FN,190)
+#define K_F182 K(KT_FN,191)
+#define K_F183 K(KT_FN,192)
+#define K_F184 K(KT_FN,193)
+#define K_F185 K(KT_FN,194)
+#define K_F186 K(KT_FN,195)
+#define K_F187 K(KT_FN,196)
+#define K_F188 K(KT_FN,197)
+#define K_F189 K(KT_FN,198)
+#define K_F190 K(KT_FN,199)
+#define K_F191 K(KT_FN,200)
+#define K_F192 K(KT_FN,201)
+#define K_F193 K(KT_FN,202)
+#define K_F194 K(KT_FN,203)
+#define K_F195 K(KT_FN,204)
+#define K_F196 K(KT_FN,205)
+#define K_F197 K(KT_FN,206)
+#define K_F198 K(KT_FN,207)
+#define K_F199 K(KT_FN,208)
+#define K_F200 K(KT_FN,209)
+#define K_F201 K(KT_FN,210)
+#define K_F202 K(KT_FN,211)
+#define K_F203 K(KT_FN,212)
+#define K_F204 K(KT_FN,213)
+#define K_F205 K(KT_FN,214)
+#define K_F206 K(KT_FN,215)
+#define K_F207 K(KT_FN,216)
+#define K_F208 K(KT_FN,217)
+#define K_F209 K(KT_FN,218)
+#define K_F210 K(KT_FN,219)
+#define K_F211 K(KT_FN,220)
+#define K_F212 K(KT_FN,221)
+#define K_F213 K(KT_FN,222)
+#define K_F214 K(KT_FN,223)
+#define K_F215 K(KT_FN,224)
+#define K_F216 K(KT_FN,225)
+#define K_F217 K(KT_FN,226)
+#define K_F218 K(KT_FN,227)
+#define K_F219 K(KT_FN,228)
+#define K_F220 K(KT_FN,229)
+#define K_F221 K(KT_FN,230)
+#define K_F222 K(KT_FN,231)
+#define K_F223 K(KT_FN,232)
+#define K_F224 K(KT_FN,233)
+#define K_F225 K(KT_FN,234)
+#define K_F226 K(KT_FN,235)
+#define K_F227 K(KT_FN,236)
+#define K_F228 K(KT_FN,237)
+#define K_F229 K(KT_FN,238)
+#define K_F230 K(KT_FN,239)
+#define K_F231 K(KT_FN,240)
+#define K_F232 K(KT_FN,241)
+#define K_F233 K(KT_FN,242)
+#define K_F234 K(KT_FN,243)
+#define K_F235 K(KT_FN,244)
+#define K_F236 K(KT_FN,245)
+#define K_F237 K(KT_FN,246)
+#define K_F238 K(KT_FN,247)
+#define K_F239 K(KT_FN,248)
+#define K_F240 K(KT_FN,249)
+#define K_F241 K(KT_FN,250)
+#define K_F242 K(KT_FN,251)
+#define K_F243 K(KT_FN,252)
+#define K_F244 K(KT_FN,253)
+#define K_F245 K(KT_FN,254)
+#define K_UNDO K(KT_FN,255)
+
+
+#define K_HOLE K(KT_SPEC,0)
+#define K_ENTER K(KT_SPEC,1)
+#define K_SH_REGS K(KT_SPEC,2)
+#define K_SH_MEM K(KT_SPEC,3)
+#define K_SH_STAT K(KT_SPEC,4)
+#define K_BREAK K(KT_SPEC,5)
+#define K_CONS K(KT_SPEC,6)
+#define K_CAPS K(KT_SPEC,7)
+#define K_NUM K(KT_SPEC,8)
+#define K_HOLD K(KT_SPEC,9)
+#define K_SCROLLFORW K(KT_SPEC,10)
+#define K_SCROLLBACK K(KT_SPEC,11)
+#define K_BOOT K(KT_SPEC,12)
+#define K_CAPSON K(KT_SPEC,13)
+#define K_COMPOSE K(KT_SPEC,14)
+#define K_SAK K(KT_SPEC,15)
+#define K_DECRCONSOLE K(KT_SPEC,16)
+#define K_INCRCONSOLE K(KT_SPEC,17)
+#define K_SPAWNCONSOLE K(KT_SPEC,18)
+#define K_BARENUMLOCK K(KT_SPEC,19)
+
+#define K_ALLOCATED K(KT_SPEC,126) /* dynamically allocated keymap */
+#define K_NOSUCHMAP K(KT_SPEC,127) /* returned by KDGKBENT */
+
+#define K_P0 K(KT_PAD,0)
+#define K_P1 K(KT_PAD,1)
+#define K_P2 K(KT_PAD,2)
+#define K_P3 K(KT_PAD,3)
+#define K_P4 K(KT_PAD,4)
+#define K_P5 K(KT_PAD,5)
+#define K_P6 K(KT_PAD,6)
+#define K_P7 K(KT_PAD,7)
+#define K_P8 K(KT_PAD,8)
+#define K_P9 K(KT_PAD,9)
+#define K_PPLUS K(KT_PAD,10) /* key-pad plus */
+#define K_PMINUS K(KT_PAD,11) /* key-pad minus */
+#define K_PSTAR K(KT_PAD,12) /* key-pad asterisk (star) */
+#define K_PSLASH K(KT_PAD,13) /* key-pad slash */
+#define K_PENTER K(KT_PAD,14) /* key-pad enter */
+#define K_PCOMMA K(KT_PAD,15) /* key-pad comma: kludge... */
+#define K_PDOT K(KT_PAD,16) /* key-pad dot (period): kludge... */
+#define K_PPLUSMINUS K(KT_PAD,17) /* key-pad plus/minus */
+#define K_PPARENL K(KT_PAD,18) /* key-pad left parenthesis */
+#define K_PPARENR K(KT_PAD,19) /* key-pad right parenthesis */
+
+#define NR_PAD 20
+
+#define K_DGRAVE K(KT_DEAD,0)
+#define K_DACUTE K(KT_DEAD,1)
+#define K_DCIRCM K(KT_DEAD,2)
+#define K_DTILDE K(KT_DEAD,3)
+#define K_DDIERE K(KT_DEAD,4)
+#define K_DCEDIL K(KT_DEAD,5)
+
+#define NR_DEAD 6
+
+#define K_DOWN K(KT_CUR,0)
+#define K_LEFT K(KT_CUR,1)
+#define K_RIGHT K(KT_CUR,2)
+#define K_UP K(KT_CUR,3)
+
+#define K_SHIFT K(KT_SHIFT,KG_SHIFT)
+#define K_CTRL K(KT_SHIFT,KG_CTRL)
+#define K_ALT K(KT_SHIFT,KG_ALT)
+#define K_ALTGR K(KT_SHIFT,KG_ALTGR)
+#define K_SHIFTL K(KT_SHIFT,KG_SHIFTL)
+#define K_SHIFTR K(KT_SHIFT,KG_SHIFTR)
+#define K_CTRLL K(KT_SHIFT,KG_CTRLL)
+#define K_CTRLR K(KT_SHIFT,KG_CTRLR)
+#define K_CAPSSHIFT K(KT_SHIFT,KG_CAPSSHIFT)
+
+#define K_ASC0 K(KT_ASCII,0)
+#define K_ASC1 K(KT_ASCII,1)
+#define K_ASC2 K(KT_ASCII,2)
+#define K_ASC3 K(KT_ASCII,3)
+#define K_ASC4 K(KT_ASCII,4)
+#define K_ASC5 K(KT_ASCII,5)
+#define K_ASC6 K(KT_ASCII,6)
+#define K_ASC7 K(KT_ASCII,7)
+#define K_ASC8 K(KT_ASCII,8)
+#define K_ASC9 K(KT_ASCII,9)
+#define K_HEX0 K(KT_ASCII,10)
+#define K_HEX1 K(KT_ASCII,11)
+#define K_HEX2 K(KT_ASCII,12)
+#define K_HEX3 K(KT_ASCII,13)
+#define K_HEX4 K(KT_ASCII,14)
+#define K_HEX5 K(KT_ASCII,15)
+#define K_HEX6 K(KT_ASCII,16)
+#define K_HEX7 K(KT_ASCII,17)
+#define K_HEX8 K(KT_ASCII,18)
+#define K_HEX9 K(KT_ASCII,19)
+#define K_HEXa K(KT_ASCII,20)
+#define K_HEXb K(KT_ASCII,21)
+#define K_HEXc K(KT_ASCII,22)
+#define K_HEXd K(KT_ASCII,23)
+#define K_HEXe K(KT_ASCII,24)
+#define K_HEXf K(KT_ASCII,25)
+
+#define NR_ASCII 26
+
+#define K_SHIFTLOCK K(KT_LOCK,KG_SHIFT)
+#define K_CTRLLOCK K(KT_LOCK,KG_CTRL)
+#define K_ALTLOCK K(KT_LOCK,KG_ALT)
+#define K_ALTGRLOCK K(KT_LOCK,KG_ALTGR)
+#define K_SHIFTLLOCK K(KT_LOCK,KG_SHIFTL)
+#define K_SHIFTRLOCK K(KT_LOCK,KG_SHIFTR)
+#define K_CTRLLLOCK K(KT_LOCK,KG_CTRLL)
+#define K_CTRLRLOCK K(KT_LOCK,KG_CTRLR)
+
+#define K_SHIFT_SLOCK K(KT_SLOCK,KG_SHIFT)
+#define K_CTRL_SLOCK K(KT_SLOCK,KG_CTRL)
+#define K_ALT_SLOCK K(KT_SLOCK,KG_ALT)
+#define K_ALTGR_SLOCK K(KT_SLOCK,KG_ALTGR)
+#define K_SHIFTL_SLOCK K(KT_SLOCK,KG_SHIFTL)
+#define K_SHIFTR_SLOCK K(KT_SLOCK,KG_SHIFTR)
+#define K_CTRLL_SLOCK K(KT_SLOCK,KG_CTRLL)
+#define K_CTRLR_SLOCK K(KT_SLOCK,KG_CTRLR)
+
+#define NR_LOCK 8
+
+#define MAX_DIACR 256
+#endif
diff --git a/pfinet/linux-src/include/linux/kmod.h b/pfinet/linux-src/include/linux/kmod.h
new file mode 100644
index 00000000..49683949
--- /dev/null
+++ b/pfinet/linux-src/include/linux/kmod.h
@@ -0,0 +1,11 @@
+/*
+ kmod header
+*/
+
+#include <linux/config.h>
+
+#ifdef CONFIG_KMOD
+extern int request_module(const char * name);
+#else
+#define request_module(x) do {} while(0)
+#endif
diff --git a/pfinet/linux-src/include/linux/lapb.h b/pfinet/linux-src/include/linux/lapb.h
new file mode 100644
index 00000000..bf1825a6
--- /dev/null
+++ b/pfinet/linux-src/include/linux/lapb.h
@@ -0,0 +1,56 @@
+/*
+ * These are the public elements of the Linux LAPB module.
+ */
+
+#ifndef LAPB_KERNEL_H
+#define LAPB_KERNEL_H
+
+#define LAPB_OK 0
+#define LAPB_BADTOKEN 1
+#define LAPB_INVALUE 2
+#define LAPB_CONNECTED 3
+#define LAPB_NOTCONNECTED 4
+#define LAPB_REFUSED 5
+#define LAPB_TIMEDOUT 6
+#define LAPB_NOMEM 7
+
+#define LAPB_STANDARD 0x00
+#define LAPB_EXTENDED 0x01
+
+#define LAPB_SLP 0x00
+#define LAPB_MLP 0x02
+
+#define LAPB_DTE 0x00
+#define LAPB_DCE 0x04
+
+struct lapb_register_struct {
+ void (*connect_confirmation)(void *token, int reason);
+ void (*connect_indication)(void *token, int reason);
+ void (*disconnect_confirmation)(void *token, int reason);
+ void (*disconnect_indication)(void *token, int reason);
+ void (*data_indication)(void *token, struct sk_buff *skb);
+ void (*data_transmit)(void *token, struct sk_buff *skb);
+};
+
+struct lapb_parms_struct {
+ unsigned int t1;
+ unsigned int t1timer;
+ unsigned int t2;
+ unsigned int t2timer;
+ unsigned int n2;
+ unsigned int n2count;
+ unsigned int window;
+ unsigned int state;
+ unsigned int mode;
+};
+
+extern int lapb_register(void *token, struct lapb_register_struct *callbacks);
+extern int lapb_unregister(void *token);
+extern int lapb_getparms(void *token, struct lapb_parms_struct *parms);
+extern int lapb_setparms(void *token, struct lapb_parms_struct *parms);
+extern int lapb_connect_request(void *token);
+extern int lapb_disconnect_request(void *token);
+extern int lapb_data_request(void *token, struct sk_buff *skb);
+extern int lapb_data_received(void *token, struct sk_buff *skb);
+
+#endif
diff --git a/pfinet/linux-src/include/linux/limits.h b/pfinet/linux-src/include/linux/limits.h
new file mode 100644
index 00000000..5848688e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/limits.h
@@ -0,0 +1,19 @@
+#ifndef _LINUX_LIMITS_H
+#define _LINUX_LIMITS_H
+
+#define NR_OPEN 1024
+
+#define NGROUPS_MAX 32 /* supplemental group IDs are available */
+#define ARG_MAX 131072 /* # bytes of args + environ for exec() */
+#define CHILD_MAX 999 /* no limit :-) */
+#define OPEN_MAX 256 /* # open files a process may have */
+#define LINK_MAX 127 /* # links a file may have */
+#define MAX_CANON 255 /* size of the canonical input queue */
+#define MAX_INPUT 255 /* size of the type-ahead buffer */
+#define NAME_MAX 255 /* # chars in a file name */
+#define PATH_MAX 4095 /* # chars in a path name */
+#define PIPE_BUF 4096 /* # bytes in atomic write to a pipe */
+
+#define RTSIG_MAX 32
+
+#endif
diff --git a/pfinet/linux-src/include/linux/linkage.h b/pfinet/linux-src/include/linux/linkage.h
new file mode 100644
index 00000000..190202f0
--- /dev/null
+++ b/pfinet/linux-src/include/linux/linkage.h
@@ -0,0 +1,54 @@
+#ifndef _LINUX_LINKAGE_H
+#define _LINUX_LINKAGE_H
+
+#ifdef __cplusplus
+#define CPP_ASMLINKAGE extern "C"
+#else
+#define CPP_ASMLINKAGE
+#endif
+
+#if defined __i386__ && (__GNUC__ > 2 || __GNUC_MINOR__ > 7)
+#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
+#else
+#define asmlinkage CPP_ASMLINKAGE
+#endif
+
+#define SYMBOL_NAME_STR(X) #X
+#define SYMBOL_NAME(X) X
+#ifdef __STDC__
+#define SYMBOL_NAME_LABEL(X) X##:
+#else
+#define SYMBOL_NAME_LABEL(X) X/**/:
+#endif
+
+#ifdef __arm__
+#define __ALIGN .align 0
+#define __ALIGN_STR ".align 0"
+#else
+#ifdef __mc68000__
+#define __ALIGN .align 4
+#define __ALIGN_STR ".align 4"
+#else
+#if !defined(__i486__) && !defined(__i586__)
+#define __ALIGN .align 4,0x90
+#define __ALIGN_STR ".align 4,0x90"
+#else /* __i486__/__i586__ */
+#define __ALIGN .align 16,0x90
+#define __ALIGN_STR ".align 16,0x90"
+#endif /* __i486__/__i586__ */
+#endif /* __mc68000__ */
+#endif /* __arm__ */
+
+#ifdef __ASSEMBLY__
+
+#define ALIGN __ALIGN
+#define ALIGN_STR __ALIGN_STR
+
+#define ENTRY(name) \
+ .globl SYMBOL_NAME(name); \
+ ALIGN; \
+ SYMBOL_NAME_LABEL(name)
+
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/linux_logo.h b/pfinet/linux-src/include/linux/linux_logo.h
new file mode 100644
index 00000000..9aa712eb
--- /dev/null
+++ b/pfinet/linux-src/include/linux/linux_logo.h
@@ -0,0 +1,1445 @@
+/* $Id: linux_logo.h,v 1.5 1998/07/30 16:30:58 jj Exp $
+ * include/linux/linux_logo.h: This is a linux logo
+ * to be displayed on boot.
+ *
+ * Copyright (C) 1996 Larry Ewing (lewing@isc.tamu.edu)
+ * Copyright (C) 1996,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *
+ * You can put anything here, but:
+ * LINUX_LOGO_COLORS has to be less than 224
+ * image size has to be 80x80
+ * values have to start from 0x20
+ * (i.e. RGB(linux_logo_red[0],
+ * linux_logo_green[0],
+ * linux_logo_blue[0]) is color 0x20)
+ * BW image has to be 80x80 as well, with MS bit
+ * on the left
+ * Serial_console ascii image can be any size,
+ * but should contain %s to display the version
+ */
+
+#if LINUX_LOGO_COLORS == 214
+
+unsigned char linux_logo_red[] __initdata = {
+ 0x02, 0x9E, 0xE9, 0xC4, 0x50, 0xC9, 0xC4, 0xE9,
+ 0x65, 0xE3, 0xC2, 0x25, 0xA4, 0xEC, 0x90, 0xA6,
+ 0xC4, 0x6A, 0xD1, 0xF3, 0x12, 0xED, 0xA0, 0xC2,
+ 0xB8, 0xD5, 0xDB, 0xD2, 0x3E, 0x16, 0xEB, 0x54,
+ 0xA9, 0xCD, 0xF5, 0x0A, 0xBA, 0xB3, 0xDC, 0x74,
+ 0xCE, 0xF6, 0xD3, 0xC5, 0xEA, 0xB8, 0xED, 0x5E,
+ 0xE5, 0x26, 0xF4, 0xA9, 0x82, 0x94, 0xE6, 0x38,
+ 0xF2, 0x0F, 0x7F, 0x49, 0xE5, 0xF4, 0xD3, 0xC3,
+ 0xC2, 0x1E, 0xD5, 0xC6, 0xA4, 0xFA, 0x0A, 0xBA,
+ 0xD4, 0xEB, 0xEA, 0xEC, 0xA8, 0xBC, 0xB4, 0xDC,
+ 0x84, 0xE4, 0xCE, 0xEC, 0x92, 0xCD, 0xDC, 0x8B,
+ 0xCC, 0x1E, 0xF6, 0xB2, 0x60, 0x2A, 0x96, 0x52,
+ 0x0F, 0xBD, 0xFA, 0xCC, 0xB8, 0x7A, 0x4C, 0xD2,
+ 0x06, 0xEF, 0x44, 0x64, 0xF4, 0xBA, 0xCE, 0xE6,
+ 0x8A, 0x6F, 0x3C, 0x70, 0x7C, 0x9C, 0xBA, 0xDF,
+ 0x2C, 0x4D, 0x3B, 0xCA, 0xDE, 0xCE, 0xEE, 0x46,
+ 0x6A, 0xAC, 0x96, 0xE5, 0x96, 0x7A, 0xBA, 0xB6,
+ 0xE2, 0x7E, 0xAA, 0xC5, 0x96, 0x9E, 0xC2, 0xAA,
+ 0xDA, 0x35, 0xB6, 0x82, 0x88, 0xBE, 0xC2, 0x9E,
+ 0xB4, 0xD5, 0xDA, 0x9C, 0xA0, 0xD0, 0xA8, 0xC7,
+ 0x72, 0xF2, 0xDB, 0x76, 0xDC, 0xBE, 0xAA, 0xF4,
+ 0x87, 0x2F, 0x53, 0x8E, 0x36, 0xCE, 0xE6, 0xCA,
+ 0xCB, 0xE4, 0xD6, 0xAA, 0x42, 0x5D, 0xB4, 0x59,
+ 0x1C, 0xC8, 0x96, 0x6C, 0xDA, 0xCE, 0xE6, 0xCB,
+ 0x96, 0x16, 0xFA, 0xBE, 0xAE, 0xFE, 0x6E, 0xD6,
+ 0xCE, 0xB6, 0xE5, 0xED, 0xDB, 0xDC, 0xF4, 0x72,
+ 0x1F, 0xAE, 0xE6, 0xC2, 0xCA, 0xC4
+};
+
+unsigned char linux_logo_green[] __initdata = {
+ 0x02, 0x88, 0xC4, 0x85, 0x44, 0xA2, 0xA8, 0xE5,
+ 0x65, 0xA6, 0xC2, 0x24, 0xA4, 0xB4, 0x62, 0x86,
+ 0x94, 0x44, 0xD2, 0xB6, 0x12, 0xD4, 0x73, 0x96,
+ 0x92, 0x95, 0xB2, 0xC2, 0x36, 0x0E, 0xBC, 0x54,
+ 0x75, 0xA5, 0xF5, 0x0A, 0xB2, 0x83, 0xC2, 0x74,
+ 0x9B, 0xBD, 0xA2, 0xCA, 0xDA, 0x8C, 0xCB, 0x42,
+ 0xAC, 0x12, 0xDA, 0x7B, 0x54, 0x94, 0xD2, 0x24,
+ 0xBE, 0x06, 0x65, 0x33, 0xBB, 0xBC, 0xAB, 0x8C,
+ 0x92, 0x1E, 0x9B, 0xB6, 0x6E, 0xFB, 0x04, 0xA2,
+ 0xC8, 0xBD, 0xAD, 0xEC, 0x92, 0xBC, 0x7B, 0x9D,
+ 0x84, 0xC4, 0xC4, 0xB4, 0x6C, 0x93, 0xA3, 0x5E,
+ 0x8D, 0x13, 0xD6, 0x82, 0x4C, 0x2A, 0x7A, 0x5A,
+ 0x0D, 0x82, 0xBB, 0xCC, 0x8B, 0x6A, 0x3C, 0xBE,
+ 0x06, 0xC4, 0x44, 0x45, 0xDB, 0x96, 0xB6, 0xDE,
+ 0x8A, 0x4D, 0x3C, 0x5A, 0x7C, 0x9C, 0xAA, 0xCB,
+ 0x1C, 0x4D, 0x2E, 0xB2, 0xBE, 0xAA, 0xDE, 0x3E,
+ 0x6A, 0xAC, 0x82, 0xE5, 0x72, 0x62, 0x92, 0x9E,
+ 0xCA, 0x4A, 0x8E, 0xBE, 0x86, 0x6B, 0xAA, 0x9A,
+ 0xBE, 0x34, 0xAB, 0x76, 0x6E, 0x9A, 0x9E, 0x62,
+ 0x76, 0xCE, 0xD3, 0x92, 0x7C, 0xB8, 0x7E, 0xC6,
+ 0x5E, 0xE2, 0xC3, 0x54, 0xAA, 0x9E, 0x8A, 0xCA,
+ 0x63, 0x2D, 0x3B, 0x8E, 0x1A, 0x9E, 0xC2, 0xA6,
+ 0xCB, 0xDC, 0xD6, 0x8E, 0x26, 0x5C, 0xB4, 0x45,
+ 0x1C, 0xB8, 0x6E, 0x4C, 0xBC, 0xAE, 0xD6, 0x92,
+ 0x63, 0x16, 0xF6, 0x8C, 0x7A, 0xFE, 0x6E, 0xBA,
+ 0xC6, 0x86, 0xAA, 0xAE, 0xDB, 0xA4, 0xD4, 0x56,
+ 0x0E, 0x6E, 0xB6, 0xB2, 0xBE, 0xBE
+};
+
+unsigned char linux_logo_blue[] __initdata = {
+ 0x04, 0x28, 0x10, 0x0B, 0x14, 0x14, 0x74, 0xC7,
+ 0x64, 0x0E, 0xC3, 0x24, 0xA4, 0x0C, 0x10, 0x20,
+ 0x0D, 0x04, 0xD1, 0x0D, 0x13, 0x22, 0x0A, 0x40,
+ 0x14, 0x0C, 0x11, 0x94, 0x0C, 0x08, 0x0B, 0x56,
+ 0x09, 0x47, 0xF4, 0x0B, 0x9C, 0x07, 0x54, 0x74,
+ 0x0F, 0x0C, 0x0F, 0xC7, 0x6C, 0x14, 0x14, 0x11,
+ 0x0B, 0x04, 0x12, 0x0C, 0x05, 0x94, 0x94, 0x0A,
+ 0x34, 0x09, 0x14, 0x08, 0x2F, 0x15, 0x19, 0x11,
+ 0x28, 0x0C, 0x0B, 0x94, 0x08, 0xFA, 0x08, 0x7C,
+ 0xBC, 0x15, 0x0A, 0xEC, 0x64, 0xBB, 0x0A, 0x0C,
+ 0x84, 0x2C, 0xA0, 0x15, 0x10, 0x0D, 0x0B, 0x0E,
+ 0x0A, 0x07, 0x10, 0x3C, 0x24, 0x2C, 0x28, 0x5C,
+ 0x0A, 0x0D, 0x0A, 0xC1, 0x22, 0x4C, 0x10, 0x94,
+ 0x04, 0x0F, 0x45, 0x08, 0x31, 0x54, 0x3C, 0xBC,
+ 0x8C, 0x09, 0x3C, 0x18, 0x7C, 0x9C, 0x7C, 0x91,
+ 0x0C, 0x4D, 0x17, 0x74, 0x0C, 0x48, 0x9C, 0x3C,
+ 0x6A, 0xAC, 0x5C, 0xE3, 0x29, 0x3C, 0x2C, 0x7C,
+ 0x6C, 0x04, 0x14, 0xA9, 0x74, 0x07, 0x2C, 0x74,
+ 0x4C, 0x34, 0x97, 0x5C, 0x38, 0x0C, 0x5C, 0x04,
+ 0x0C, 0xBA, 0xBC, 0x78, 0x18, 0x88, 0x24, 0xC2,
+ 0x3C, 0xB4, 0x87, 0x0C, 0x14, 0x4C, 0x3C, 0x10,
+ 0x17, 0x2C, 0x0A, 0x8C, 0x04, 0x1C, 0x44, 0x2C,
+ 0xCD, 0xD8, 0xD4, 0x34, 0x0C, 0x5B, 0xB4, 0x1E,
+ 0x1D, 0xAC, 0x24, 0x18, 0x20, 0x5C, 0xB4, 0x1C,
+ 0x09, 0x14, 0xFC, 0x0C, 0x10, 0xFC, 0x6C, 0x7C,
+ 0xB4, 0x1C, 0x15, 0x17, 0xDB, 0x18, 0x21, 0x24,
+ 0x04, 0x04, 0x44, 0x8C, 0x8C, 0xB7
+};
+
+unsigned char linux_logo[] __initdata = {
+ 0xBF, 0x95, 0x90, 0xCB, 0x95, 0xA1, 0x2C, 0x2C,
+ 0x95, 0x55, 0xCB, 0x90, 0xCB, 0x95, 0x2C, 0x95,
+ 0xCB, 0x47, 0x94, 0x95, 0xA1, 0xD6, 0xD6, 0x2C,
+ 0x90, 0x47, 0x70, 0x2C, 0x6D, 0x2A, 0x6D, 0xD6,
+ 0xA1, 0x2C, 0x55, 0x95, 0x2C, 0x2C, 0x55, 0x55,
+ 0x95, 0xA1, 0xA1, 0xA1, 0x6D, 0xBF, 0x2A, 0x2A,
+ 0xBF, 0x83, 0xBF, 0x95, 0x90, 0xCB, 0x95, 0xA1,
+ 0x2C, 0x2C, 0x95, 0x55, 0xCB, 0x90, 0xCB, 0x95,
+ 0x2C, 0x95, 0xCB, 0x47, 0x94, 0x95, 0xA1, 0xD6,
+ 0xD6, 0x2C, 0x90, 0x47, 0x70, 0x2C, 0x6D, 0x2A,
+ 0x95, 0x47, 0x47, 0x90, 0x2C, 0x2C, 0x2C, 0x95,
+ 0x55, 0x55, 0xCB, 0x90, 0xCB, 0x55, 0x55, 0xCB,
+ 0x47, 0xE6, 0x70, 0x95, 0xD6, 0xD6, 0xA1, 0x2C,
+ 0x55, 0x55, 0x95, 0xD6, 0x6D, 0xD6, 0xA1, 0x2C,
+ 0x2C, 0x95, 0x55, 0x95, 0x95, 0x95, 0x2C, 0x2C,
+ 0xA1, 0xA1, 0x2C, 0x2C, 0xA1, 0xD6, 0xD6, 0xD6,
+ 0xD6, 0xD6, 0x95, 0x47, 0x47, 0x90, 0x2C, 0x2C,
+ 0x2C, 0x95, 0x55, 0x55, 0xCB, 0x90, 0xCB, 0x55,
+ 0x55, 0xCB, 0x47, 0xE6, 0x70, 0x95, 0xD6, 0xD6,
+ 0xA1, 0x2C, 0x55, 0x55, 0x95, 0xD6, 0x6D, 0xD6,
+ 0x90, 0x47, 0x47, 0x70, 0x2C, 0xA1, 0x2C, 0x95,
+ 0x55, 0x55, 0x90, 0xCB, 0x55, 0x55, 0x55, 0x70,
+ 0x94, 0x70, 0x95, 0xA1, 0xD6, 0xD6, 0xA1, 0x2C,
+ 0x95, 0x95, 0x2C, 0xA1, 0xD6, 0xA1, 0x2C, 0x2C,
+ 0x95, 0x55, 0xCB, 0x95, 0xD6, 0xA1, 0x2C, 0x95,
+ 0xA1, 0xD6, 0xD6, 0xA1, 0xA1, 0xD6, 0xA1, 0xA1,
+ 0xA1, 0x2C, 0x90, 0x47, 0x47, 0x70, 0x2C, 0xA1,
+ 0x2C, 0x95, 0x55, 0x55, 0x90, 0xCB, 0x55, 0x55,
+ 0x55, 0x70, 0x94, 0x70, 0x95, 0xA1, 0xD6, 0xD6,
+ 0xA1, 0x2C, 0x95, 0x95, 0x2C, 0xD6, 0xD6, 0xA1,
+ 0x94, 0xA0, 0x47, 0x55, 0x2C, 0xD6, 0xA1, 0x95,
+ 0x55, 0x55, 0xCB, 0xCB, 0x55, 0x55, 0xCB, 0xCB,
+ 0x55, 0x95, 0x2C, 0xA1, 0xD6, 0xD6, 0xA1, 0x2C,
+ 0x95, 0x95, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x95,
+ 0x55, 0x55, 0x2C, 0x3F, 0x80, 0x20, 0x88, 0x88,
+ 0x88, 0x20, 0x88, 0xB1, 0x2C, 0xA1, 0x2C, 0x2C,
+ 0x95, 0xCB, 0x94, 0xA0, 0x47, 0x55, 0x2C, 0xD6,
+ 0xA1, 0x95, 0x55, 0x55, 0xCB, 0xCB, 0x55, 0x55,
+ 0xCB, 0xCB, 0x55, 0x95, 0x2C, 0xA1, 0xD6, 0xD6,
+ 0xA1, 0x2C, 0x95, 0x95, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x94, 0x94, 0x70, 0x2C, 0xA1, 0xD6, 0xA1, 0x2C,
+ 0x55, 0x55, 0xCB, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x95, 0x2C, 0xD6, 0xD6, 0xD6, 0xA1, 0x2C, 0x95,
+ 0x55, 0x55, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x2C, 0x94, 0x80, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x88, 0x92, 0xA1, 0x95,
+ 0x55, 0x90, 0x94, 0x94, 0x70, 0x2C, 0xA1, 0xD6,
+ 0xA1, 0x2C, 0x55, 0x55, 0xCB, 0x55, 0x55, 0x55,
+ 0x55, 0x55, 0x95, 0x2C, 0xD6, 0xD6, 0xD6, 0xA1,
+ 0x2C, 0x95, 0x55, 0x55, 0x55, 0x95, 0x95, 0x95,
+ 0x70, 0x70, 0x55, 0x2C, 0xD6, 0xD6, 0xA1, 0x95,
+ 0x55, 0x90, 0xCB, 0xCB, 0x55, 0x55, 0x2C, 0x2C,
+ 0xA1, 0xD6, 0xA1, 0xA1, 0x2C, 0x2C, 0x95, 0x55,
+ 0x55, 0x55, 0x95, 0x95, 0x2C, 0x95, 0x95, 0xD6,
+ 0xB1, 0x88, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x80, 0x34, 0x88, 0x43, 0x47,
+ 0x95, 0xCB, 0x70, 0x70, 0x55, 0x2C, 0xD6, 0xD6,
+ 0xA1, 0x95, 0x55, 0x90, 0xCB, 0xCB, 0x55, 0x55,
+ 0x2C, 0x2C, 0xA1, 0xD6, 0xA1, 0xA1, 0xA1, 0x2C,
+ 0x55, 0x55, 0x55, 0x55, 0x2C, 0x95, 0x2C, 0x2C,
+ 0x55, 0x55, 0x95, 0x2C, 0xA1, 0xA1, 0x2C, 0x55,
+ 0x90, 0x70, 0x90, 0x55, 0x95, 0x95, 0xA1, 0xA1,
+ 0xA1, 0xA1, 0xA1, 0xA1, 0x2C, 0x95, 0x95, 0x95,
+ 0x95, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0xD5,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x88, 0x7D, 0x3F, 0xB1, 0x80, 0x20,
+ 0x99, 0x2C, 0x55, 0x55, 0x95, 0x2C, 0xA1, 0xA1,
+ 0x2C, 0x55, 0x90, 0x70, 0x90, 0x55, 0x95, 0x95,
+ 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0x2C, 0x2C, 0x2C,
+ 0x95, 0x55, 0x95, 0x95, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x95, 0x90, 0x55, 0x2C, 0xA1, 0xA1, 0x95, 0xCB,
+ 0x70, 0x94, 0x90, 0x55, 0x95, 0xA1, 0xA1, 0xA1,
+ 0x2C, 0x2C, 0x2C, 0x2C, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0xA1, 0x88,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0xB1, 0x47, 0xD5, 0x7D, 0x43,
+ 0x20, 0x70, 0x95, 0x90, 0x55, 0x2C, 0xA1, 0xA1,
+ 0x95, 0xCB, 0x70, 0x94, 0x90, 0x55, 0x95, 0xA1,
+ 0xA1, 0xA1, 0x2C, 0x95, 0x2C, 0x2C, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x2C, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x90, 0x55, 0x2C, 0xD6, 0xD6, 0x2C, 0x90,
+ 0x94, 0x70, 0x55, 0x95, 0x2C, 0xD6, 0xD6, 0xA1,
+ 0x95, 0x95, 0x95, 0x2C, 0x2C, 0x95, 0x55, 0x55,
+ 0xCB, 0xCB, 0xCB, 0x55, 0xCB, 0x55, 0x47, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x88, 0xB1, 0x3F, 0x92, 0x2B, 0x80,
+ 0x20, 0x80, 0xD6, 0x70, 0x55, 0x2C, 0xD6, 0xD6,
+ 0x2C, 0x90, 0x94, 0x70, 0x55, 0x95, 0x2C, 0xD6,
+ 0xD6, 0xA1, 0x2C, 0x95, 0x95, 0x2C, 0x2C, 0x95,
+ 0x95, 0x55, 0x90, 0xCB, 0xCB, 0xCB, 0xCB, 0x55,
+ 0xD6, 0x55, 0x95, 0xA1, 0xD6, 0xA1, 0x55, 0x70,
+ 0x94, 0x55, 0x95, 0xA1, 0xA1, 0xA1, 0xA1, 0x95,
+ 0x55, 0x55, 0x55, 0x95, 0x55, 0x55, 0xCB, 0x90,
+ 0x70, 0x90, 0xCB, 0x55, 0x55, 0xA1, 0xD8, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x88, 0xD8, 0xE1, 0x88, 0x20, 0x20,
+ 0x88, 0x88, 0xE6, 0x55, 0x2C, 0xA1, 0xD6, 0xA1,
+ 0x55, 0x70, 0x94, 0x55, 0x95, 0xA1, 0xA1, 0xA1,
+ 0xA1, 0x95, 0x55, 0x55, 0x95, 0x95, 0x55, 0x55,
+ 0x90, 0x90, 0x90, 0x90, 0xCB, 0x55, 0x55, 0x55,
+ 0xD6, 0x2C, 0xA1, 0xD6, 0xD6, 0xA1, 0xCB, 0x70,
+ 0x70, 0x95, 0x2C, 0xA1, 0xA1, 0x2C, 0x2C, 0x55,
+ 0xCB, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x55, 0x95, 0x2C, 0x95, 0x2C, 0xD6, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x80, 0xD6, 0xA1, 0xD6, 0xD6, 0xA1,
+ 0xCB, 0x70, 0x70, 0x95, 0x2C, 0xA1, 0xA1, 0x2C,
+ 0x2C, 0x55, 0xCB, 0xCB, 0x55, 0x55, 0x55, 0x55,
+ 0x55, 0x55, 0x55, 0x95, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0xD6, 0xA1, 0xA1, 0xA1, 0xA1, 0x55, 0x70, 0x94,
+ 0xCB, 0x95, 0xA1, 0xA1, 0x2C, 0x95, 0xCB, 0x55,
+ 0x90, 0xCB, 0x55, 0x55, 0x55, 0x55, 0x95, 0xA1,
+ 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0x95, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x88, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x88, 0x95, 0xA1, 0xA1, 0xA1, 0x55,
+ 0x70, 0x94, 0xCB, 0x95, 0xA1, 0xA1, 0x2C, 0x95,
+ 0xCB, 0xCB, 0x90, 0xCB, 0x55, 0x55, 0x55, 0x55,
+ 0x95, 0x2C, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0x2C, 0x2C, 0x2C, 0x95, 0x95, 0xCB, 0x70, 0x70,
+ 0x95, 0x2C, 0x2C, 0x95, 0xCB, 0x70, 0x90, 0xCB,
+ 0xCB, 0x55, 0x55, 0xCB, 0x55, 0x55, 0x2C, 0xD6,
+ 0xD6, 0xD6, 0xD6, 0xA1, 0x2C, 0x70, 0x20, 0x20,
+ 0x88, 0x43, 0xD8, 0x43, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x88, 0x88, 0x43, 0x2B, 0xD8, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x3F, 0x2C, 0x95, 0x95, 0xCB,
+ 0x70, 0x70, 0x95, 0x2C, 0x2C, 0x95, 0xCB, 0x90,
+ 0x90, 0xCB, 0x55, 0xCB, 0x55, 0xCB, 0x55, 0x95,
+ 0x2C, 0xD6, 0xD6, 0xD6, 0xD6, 0xA1, 0x2C, 0x2C,
+ 0xA1, 0x95, 0x95, 0x55, 0xCB, 0x70, 0x90, 0x55,
+ 0x2C, 0x2C, 0x2C, 0x55, 0x70, 0x70, 0x55, 0x95,
+ 0x95, 0xCB, 0x90, 0x90, 0x90, 0x95, 0x2C, 0xA1,
+ 0xD6, 0xD6, 0x2C, 0x2C, 0x95, 0x70, 0x20, 0x20,
+ 0x80, 0x2B, 0x34, 0x2B, 0x88, 0x20, 0x20, 0x20,
+ 0x88, 0xB1, 0x28, 0x28, 0x2B, 0x7D, 0x80, 0x20,
+ 0x20, 0x20, 0x20, 0x92, 0x95, 0x55, 0xCB, 0x70,
+ 0x90, 0x55, 0x2C, 0x2C, 0x2C, 0x55, 0x70, 0x70,
+ 0x55, 0x95, 0x55, 0x55, 0x90, 0x90, 0x90, 0x55,
+ 0x2C, 0xA1, 0xD6, 0xA1, 0x2C, 0x2C, 0x95, 0x95,
+ 0xA1, 0x95, 0x55, 0xCB, 0x90, 0x70, 0xCB, 0x95,
+ 0xA1, 0x95, 0x95, 0xCB, 0x90, 0xCB, 0x95, 0x2C,
+ 0x95, 0x70, 0x70, 0x90, 0x55, 0x2C, 0xA1, 0xA1,
+ 0x2C, 0x2C, 0x55, 0xCB, 0x55, 0x90, 0x20, 0x34,
+ 0x90, 0x6D, 0x70, 0xD8, 0x43, 0x20, 0x20, 0x88,
+ 0x3F, 0x55, 0xA1, 0x2A, 0xD6, 0x7D, 0x43, 0x20,
+ 0x20, 0x20, 0x88, 0x7D, 0x55, 0xCB, 0x90, 0x70,
+ 0xCB, 0x95, 0xA1, 0x95, 0x95, 0xCB, 0x70, 0xCB,
+ 0x95, 0xA1, 0x95, 0x70, 0x70, 0xCB, 0x55, 0x2C,
+ 0xA1, 0xA1, 0xA1, 0x95, 0x55, 0x55, 0x55, 0x95,
+ 0x2C, 0x55, 0x90, 0x70, 0x94, 0x90, 0x95, 0x2C,
+ 0x2C, 0x95, 0xCB, 0x90, 0x55, 0x95, 0xA1, 0xA1,
+ 0x95, 0x90, 0x90, 0x95, 0xA1, 0xD6, 0xD6, 0x6D,
+ 0xA1, 0x95, 0x55, 0xCB, 0x55, 0xCB, 0x20, 0x99,
+ 0xBF, 0xA3, 0xA3, 0x90, 0x20, 0x20, 0x20, 0x92,
+ 0x83, 0x6B, 0x6B, 0x6B, 0xA3, 0x70, 0x88, 0x20,
+ 0x20, 0x20, 0x20, 0x2B, 0x90, 0x70, 0x94, 0x90,
+ 0x95, 0x2C, 0x2C, 0x95, 0xCB, 0x90, 0x55, 0x95,
+ 0xA1, 0x2C, 0x55, 0x90, 0x90, 0x95, 0xA1, 0xD6,
+ 0xD6, 0x6D, 0xA1, 0x95, 0x55, 0xCB, 0x55, 0x55,
+ 0x2C, 0x55, 0x70, 0x70, 0x94, 0x90, 0x95, 0x2C,
+ 0x2C, 0x55, 0xCB, 0xCB, 0x95, 0x2C, 0x2C, 0x2C,
+ 0x55, 0x55, 0x95, 0xA1, 0x6D, 0xBF, 0x6D, 0xD6,
+ 0x95, 0x55, 0x90, 0xCB, 0x55, 0x95, 0x88, 0x95,
+ 0x2C, 0x3F, 0x6D, 0x6B, 0x34, 0x20, 0x20, 0x47,
+ 0x65, 0xD6, 0xE1, 0x3F, 0x2A, 0x6B, 0x2B, 0x20,
+ 0x20, 0x20, 0x20, 0x43, 0x70, 0x70, 0x94, 0x90,
+ 0x95, 0x2C, 0x2C, 0x55, 0x55, 0x55, 0x95, 0x2C,
+ 0xA1, 0x2C, 0x55, 0xCB, 0x95, 0xA1, 0x6D, 0xBF,
+ 0x6D, 0xD6, 0x2C, 0x55, 0x90, 0xCB, 0x95, 0x95,
+ 0x95, 0x55, 0x70, 0x94, 0x70, 0x55, 0x2C, 0xA1,
+ 0x2C, 0x55, 0xCB, 0x55, 0x2C, 0x95, 0x2C, 0x95,
+ 0x95, 0x95, 0xA1, 0x6D, 0xBF, 0x2A, 0xD6, 0x95,
+ 0x70, 0x94, 0x94, 0x70, 0x55, 0x55, 0x20, 0xBF,
+ 0xC9, 0xB1, 0x99, 0x42, 0xB1, 0x61, 0x7D, 0x94,
+ 0x65, 0xB1, 0x88, 0x99, 0xD5, 0xE5, 0x7F, 0x20,
+ 0x20, 0x20, 0x20, 0x43, 0x70, 0x94, 0x70, 0x55,
+ 0x2C, 0xA1, 0x2C, 0x55, 0x90, 0x55, 0x2C, 0x95,
+ 0x2C, 0x95, 0x95, 0x2C, 0xA1, 0x6D, 0xBF, 0xBF,
+ 0xD6, 0x55, 0x70, 0x94, 0x94, 0x70, 0xCB, 0x55,
+ 0x55, 0xCB, 0x70, 0x94, 0x70, 0x95, 0xA1, 0xA1,
+ 0x95, 0x55, 0x55, 0x95, 0x2C, 0x95, 0x95, 0x95,
+ 0x95, 0xA1, 0x6D, 0x2A, 0x2A, 0xD6, 0x55, 0x94,
+ 0xE6, 0xE6, 0x47, 0x70, 0x55, 0x95, 0x20, 0x2A,
+ 0xD8, 0x43, 0xC9, 0x83, 0x98, 0x79, 0x34, 0x9F,
+ 0x6B, 0x43, 0x20, 0x88, 0x2B, 0x65, 0xA0, 0x20,
+ 0x20, 0x20, 0x20, 0xE1, 0x70, 0x94, 0x70, 0x95,
+ 0xA1, 0xA1, 0x95, 0x55, 0x55, 0x95, 0x2C, 0x95,
+ 0x95, 0x95, 0x95, 0xA1, 0x6D, 0xBF, 0x2A, 0xD6,
+ 0x55, 0x94, 0xE6, 0xE6, 0x47, 0x70, 0x55, 0x55,
+ 0x94, 0x70, 0x94, 0x47, 0x70, 0x95, 0x2C, 0x2C,
+ 0x95, 0xCB, 0x95, 0x2C, 0x2C, 0xA1, 0x2C, 0x2C,
+ 0xA1, 0xD6, 0x6D, 0x6D, 0xA1, 0xCB, 0x47, 0x28,
+ 0xE6, 0x47, 0x70, 0x55, 0x95, 0xA1, 0x20, 0x2C,
+ 0x7F, 0x88, 0xF0, 0xC6, 0x25, 0x5E, 0xCF, 0x2F,
+ 0xE7, 0x9A, 0x20, 0x88, 0x99, 0x65, 0x3F, 0x20,
+ 0x20, 0x20, 0x20, 0x34, 0x94, 0x47, 0x70, 0x95,
+ 0xA1, 0x2C, 0x55, 0xCB, 0x95, 0x2C, 0x2C, 0xA1,
+ 0x2C, 0x2C, 0xA1, 0xD6, 0x6D, 0x6D, 0xA1, 0xCB,
+ 0x94, 0x28, 0xA0, 0x47, 0x70, 0x55, 0x95, 0x95,
+ 0x47, 0x70, 0x90, 0x94, 0x70, 0x95, 0xA1, 0x2C,
+ 0x55, 0x55, 0x2C, 0xA1, 0xA1, 0xA1, 0xA1, 0x2C,
+ 0xA1, 0x6D, 0x2A, 0xD6, 0x55, 0x47, 0x28, 0x28,
+ 0x47, 0x70, 0x55, 0x95, 0x2C, 0xA1, 0x20, 0x28,
+ 0xEC, 0x86, 0xBE, 0x48, 0x3E, 0x3E, 0x3A, 0x25,
+ 0x4E, 0xAE, 0x93, 0xD7, 0xEC, 0xD1, 0x34, 0x20,
+ 0x20, 0x20, 0x20, 0x43, 0x55, 0x94, 0x70, 0x95,
+ 0xA1, 0xA1, 0x55, 0xCB, 0x2C, 0xA1, 0xA1, 0xA1,
+ 0xA1, 0x2C, 0xA1, 0x6D, 0x6D, 0xD6, 0x55, 0x47,
+ 0x28, 0x28, 0x47, 0x70, 0x55, 0x95, 0x2C, 0x2C,
+ 0x95, 0x95, 0x55, 0x90, 0xCB, 0x2C, 0xA1, 0xA1,
+ 0x55, 0x55, 0x2C, 0xD6, 0xD6, 0xA1, 0xA1, 0x2C,
+ 0xD6, 0x6D, 0x6D, 0xA1, 0x70, 0x28, 0xD5, 0xE6,
+ 0x70, 0x55, 0x95, 0x2C, 0xA1, 0xD6, 0x20, 0xE1,
+ 0x26, 0x84, 0x76, 0x73, 0x9C, 0x22, 0x4E, 0x35,
+ 0x8C, 0x7A, 0x4E, 0xDC, 0x8E, 0x7E, 0x3D, 0x88,
+ 0x20, 0x20, 0x20, 0x88, 0x2C, 0x90, 0x90, 0x95,
+ 0xA1, 0x2C, 0x55, 0x55, 0x2C, 0xD6, 0xD6, 0xD6,
+ 0x2C, 0x2C, 0xD6, 0x2A, 0x6D, 0x2C, 0x70, 0x28,
+ 0xD5, 0xE6, 0x70, 0x55, 0x95, 0xA1, 0x2C, 0xA1,
+ 0xBF, 0xA1, 0x95, 0xCB, 0xCB, 0x2C, 0xA1, 0xA1,
+ 0x95, 0x95, 0xA1, 0xD6, 0xD6, 0xA1, 0x2C, 0x95,
+ 0xD6, 0x6D, 0xD6, 0x95, 0x94, 0x28, 0xE6, 0x70,
+ 0x55, 0x95, 0xA1, 0xA1, 0xA1, 0xD6, 0x20, 0x57,
+ 0xE4, 0xDF, 0x50, 0x3E, 0x22, 0x4E, 0x35, 0x8C,
+ 0x8C, 0x52, 0x52, 0x7A, 0x4E, 0x58, 0xD7, 0x20,
+ 0x20, 0x20, 0x20, 0x88, 0x2C, 0xCB, 0x55, 0x2C,
+ 0xA1, 0xA1, 0x95, 0x95, 0xA1, 0xD6, 0xD6, 0xA1,
+ 0x2C, 0x95, 0xA1, 0x6D, 0x6D, 0x95, 0x47, 0xA0,
+ 0xE6, 0x70, 0x55, 0x95, 0x2C, 0xA1, 0xA1, 0xA1,
+ 0xD2, 0x95, 0x55, 0x90, 0x55, 0x2C, 0xD6, 0xA1,
+ 0x95, 0x95, 0xA1, 0xD6, 0xD6, 0x2C, 0x95, 0x2C,
+ 0xA1, 0x6D, 0xA1, 0x55, 0x94, 0x47, 0x94, 0xCB,
+ 0x55, 0x95, 0x2C, 0xA1, 0xD6, 0xD6, 0x59, 0xC8,
+ 0xE3, 0x76, 0x2D, 0x3E, 0x22, 0x4E, 0x8C, 0x35,
+ 0x52, 0x52, 0xEE, 0x3A, 0x4D, 0xED, 0x24, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x28, 0xCB, 0x55, 0x2C,
+ 0xD6, 0xA1, 0x95, 0x95, 0xA1, 0xD6, 0xA1, 0x2C,
+ 0x95, 0x2C, 0xD6, 0x6D, 0xA1, 0x55, 0x94, 0xE6,
+ 0x70, 0xCB, 0x55, 0x95, 0xA1, 0xD6, 0xD6, 0xA1,
+ 0xD0, 0x94, 0x94, 0x90, 0x55, 0x2C, 0xA1, 0xA1,
+ 0x55, 0x95, 0xA1, 0xA1, 0xA1, 0x2C, 0x95, 0x2C,
+ 0xA1, 0xD6, 0x2C, 0x70, 0x94, 0x94, 0x94, 0x94,
+ 0x70, 0x55, 0xA1, 0xD6, 0xA1, 0xD6, 0x88, 0x77,
+ 0x38, 0xC4, 0x3E, 0x69, 0x4E, 0x35, 0x8C, 0xEE,
+ 0x35, 0x89, 0x30, 0x30, 0x4A, 0x48, 0x3C, 0x20,
+ 0x20, 0x88, 0x20, 0x20, 0xD8, 0x2C, 0x55, 0x2C,
+ 0xD6, 0xA1, 0x95, 0x95, 0x2C, 0xD6, 0xA1, 0x2C,
+ 0x95, 0x2C, 0xA1, 0xD6, 0x2C, 0x90, 0x94, 0x47,
+ 0x94, 0x94, 0x70, 0x55, 0x2C, 0xD6, 0xA1, 0x95,
+ 0x95, 0x28, 0x47, 0x90, 0x95, 0x2C, 0xA1, 0x2C,
+ 0x95, 0x55, 0x95, 0xA1, 0xD6, 0xA1, 0x2C, 0x2C,
+ 0xA1, 0xA1, 0x55, 0x70, 0x94, 0x47, 0x94, 0x94,
+ 0x70, 0x2C, 0xD6, 0xD6, 0x2C, 0xA1, 0x43, 0x98,
+ 0x54, 0x48, 0x3E, 0x22, 0x35, 0xEE, 0xEE, 0x9C,
+ 0x4D, 0x45, 0x75, 0x4A, 0xDF, 0x7B, 0x3D, 0x20,
+ 0xD8, 0x28, 0x2B, 0x88, 0x20, 0x95, 0x95, 0x2C,
+ 0xA1, 0x2C, 0x55, 0x55, 0x2C, 0xA1, 0xD6, 0xA1,
+ 0x2C, 0x95, 0xA1, 0x2C, 0x55, 0x70, 0x94, 0x94,
+ 0x94, 0x94, 0x70, 0x95, 0xD6, 0xD6, 0x2C, 0x95,
+ 0x70, 0x28, 0x47, 0x55, 0x95, 0x2C, 0x2C, 0x2C,
+ 0x95, 0x95, 0x95, 0xA1, 0xA1, 0xA1, 0x95, 0x55,
+ 0x95, 0x95, 0x55, 0x70, 0x70, 0x70, 0x94, 0x70,
+ 0x55, 0xD6, 0x6D, 0xD6, 0x95, 0x2C, 0x20, 0x43,
+ 0xBB, 0xC8, 0x36, 0x30, 0x30, 0x38, 0x45, 0x6E,
+ 0xE3, 0x75, 0x78, 0x37, 0xBD, 0xD9, 0x3F, 0x20,
+ 0x88, 0xD5, 0x70, 0xB1, 0x88, 0xA0, 0x95, 0x2C,
+ 0x2C, 0xA1, 0x95, 0x55, 0x95, 0xA1, 0xA1, 0xA1,
+ 0x2C, 0x55, 0x95, 0x2C, 0x55, 0x70, 0x70, 0x70,
+ 0x94, 0x70, 0x55, 0xD6, 0x6D, 0x6D, 0x95, 0x55,
+ 0x94, 0x47, 0x70, 0x95, 0x2C, 0x2C, 0x2C, 0xA1,
+ 0x2C, 0x95, 0x2C, 0xA1, 0xD6, 0xA1, 0x2C, 0x55,
+ 0x55, 0x95, 0x95, 0x55, 0x55, 0x55, 0x55, 0x95,
+ 0xA1, 0x6D, 0x4B, 0xD6, 0x55, 0xD6, 0x20, 0xD8,
+ 0xD6, 0x67, 0xDA, 0x4D, 0xED, 0x62, 0x78, 0x78,
+ 0x23, 0x84, 0x67, 0xF5, 0x4B, 0xBF, 0x90, 0x88,
+ 0x88, 0x2B, 0x47, 0x99, 0x20, 0x43, 0xD6, 0x2C,
+ 0x2C, 0xA1, 0x2C, 0x95, 0x2C, 0xA1, 0xD6, 0xA1,
+ 0x95, 0x95, 0x55, 0x95, 0x55, 0x55, 0x55, 0x55,
+ 0x55, 0x95, 0xD6, 0x6D, 0xBF, 0xD6, 0x55, 0xCB,
+ 0x55, 0x55, 0x55, 0x2C, 0x2C, 0x2C, 0x2C, 0xA1,
+ 0x2C, 0x2C, 0x2C, 0xA1, 0xA1, 0x2C, 0x2C, 0x95,
+ 0x55, 0x95, 0x95, 0x2C, 0x2C, 0x2C, 0x2C, 0xA1,
+ 0x6D, 0x2A, 0x2A, 0xA1, 0x55, 0x55, 0x20, 0xD8,
+ 0x6D, 0xAB, 0x96, 0x7E, 0x64, 0x53, 0x36, 0x36,
+ 0xC6, 0x63, 0x6D, 0xD0, 0x6B, 0xE5, 0xA3, 0x7D,
+ 0x20, 0x88, 0x80, 0x88, 0x20, 0x20, 0xC9, 0xA1,
+ 0x2C, 0xA1, 0xA1, 0x2C, 0x2C, 0xA1, 0xA1, 0xA1,
+ 0x95, 0x95, 0x55, 0x95, 0x95, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0xA1, 0x6D, 0xBF, 0x6D, 0xA1, 0x55, 0x55,
+ 0x95, 0x95, 0x95, 0x95, 0x2C, 0x2C, 0x2C, 0xA1,
+ 0xA1, 0x95, 0x95, 0x2C, 0x2C, 0x2C, 0x2C, 0x95,
+ 0x55, 0x55, 0x2C, 0x2C, 0xA1, 0xA1, 0xD6, 0xD6,
+ 0x6D, 0x6D, 0xA1, 0x55, 0x2C, 0xD8, 0x20, 0xB1,
+ 0xA3, 0x4B, 0x6D, 0xD9, 0xA7, 0x6C, 0xAF, 0xB2,
+ 0x6D, 0x2A, 0x83, 0x42, 0xE5, 0xE5, 0x65, 0x2C,
+ 0x20, 0x20, 0x88, 0x20, 0x20, 0x20, 0x88, 0x95,
+ 0x2C, 0xA1, 0x2C, 0x95, 0x95, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x95, 0x55, 0x55, 0x2C, 0x2C, 0xA1, 0xA1,
+ 0xD6, 0xD6, 0x6D, 0x6D, 0xA1, 0x55, 0xCB, 0x55,
+ 0x95, 0x55, 0x95, 0x95, 0x2C, 0x2C, 0x95, 0x2C,
+ 0x2C, 0x95, 0x95, 0x95, 0x95, 0x95, 0x2C, 0x95,
+ 0x55, 0x95, 0x2C, 0x2C, 0xA1, 0xA1, 0xD6, 0xA1,
+ 0xA1, 0x2C, 0x55, 0x55, 0x28, 0x88, 0x43, 0x2A,
+ 0xE5, 0xA3, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D,
+ 0xBF, 0xA3, 0x42, 0xE5, 0xE5, 0xE5, 0xE5, 0x65,
+ 0xB1, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xD8,
+ 0xD6, 0x2C, 0x2C, 0x95, 0x95, 0x95, 0x95, 0x2C,
+ 0x95, 0x95, 0x55, 0x95, 0x2C, 0x2C, 0xA1, 0xA1,
+ 0xA1, 0xA1, 0xA1, 0x2C, 0x95, 0x90, 0x90, 0x55,
+ 0x90, 0xCB, 0x55, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x2C, 0x2C, 0x95, 0x55, 0x95, 0x95, 0x95, 0x55,
+ 0x55, 0xCB, 0x55, 0x2C, 0x95, 0x95, 0x95, 0x95,
+ 0x55, 0x90, 0x90, 0x90, 0xE1, 0x43, 0x28, 0xE5,
+ 0xE5, 0x65, 0xD0, 0x6D, 0x6D, 0x6D, 0x2A, 0xD2,
+ 0x42, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xD6, 0x20, 0x20, 0x20, 0x20, 0x20, 0x88, 0x88,
+ 0xD5, 0x2C, 0x2C, 0x2C, 0x95, 0x55, 0x95, 0x95,
+ 0x95, 0x55, 0x55, 0xCB, 0x55, 0x95, 0x2C, 0x95,
+ 0x95, 0x95, 0x55, 0x90, 0x70, 0x70, 0x70, 0x90,
+ 0x70, 0x70, 0xCB, 0x55, 0x55, 0x95, 0x95, 0x95,
+ 0x2C, 0x95, 0x95, 0x55, 0x55, 0x55, 0x55, 0xCB,
+ 0x70, 0x70, 0x70, 0xCB, 0x90, 0x90, 0x70, 0x94,
+ 0x94, 0x94, 0x2C, 0x80, 0x20, 0xE1, 0xA3, 0xE5,
+ 0xE5, 0xE5, 0x42, 0xEC, 0xD0, 0x83, 0xA3, 0x65,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0x65, 0x7D, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x88, 0x2C, 0x95, 0x95, 0x95, 0x55, 0x55, 0x55,
+ 0x55, 0xCB, 0x70, 0x70, 0x90, 0x90, 0x90, 0x90,
+ 0x70, 0x94, 0x94, 0x94, 0x70, 0x70, 0x70, 0x70,
+ 0x70, 0x55, 0x55, 0x55, 0x95, 0x95, 0x95, 0x95,
+ 0x2C, 0x2C, 0x95, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x90, 0x70, 0x90, 0x55, 0x55, 0xCB, 0x70, 0x94,
+ 0x94, 0x95, 0xD8, 0x20, 0x88, 0x70, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0x65, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0x47, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0xE1, 0x6D, 0x2C, 0x95, 0x55, 0x55, 0x55,
+ 0x55, 0x55, 0x90, 0x70, 0x70, 0x55, 0x55, 0xCB,
+ 0x70, 0x94, 0x94, 0x94, 0x70, 0x90, 0x70, 0x94,
+ 0x55, 0x2C, 0x2C, 0x2C, 0x95, 0x2C, 0x95, 0x95,
+ 0x2C, 0x2C, 0x2C, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0xCB, 0xCB, 0x95, 0x2C, 0x2C, 0x95, 0x55, 0x90,
+ 0x55, 0x99, 0x20, 0x20, 0xE1, 0xA3, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xD6, 0x88, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x2B, 0x6D, 0x95, 0x95, 0x55, 0x55,
+ 0x55, 0x55, 0xCB, 0x55, 0x95, 0x2C, 0x2C, 0x95,
+ 0x55, 0x90, 0xCB, 0xCB, 0xCB, 0xCB, 0x90, 0x70,
+ 0x2C, 0xD6, 0xD6, 0x2C, 0x2C, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x2C, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x2C, 0xA1, 0x2C, 0x95, 0x55, 0x95,
+ 0xE6, 0x88, 0x20, 0x20, 0x3F, 0xA3, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0x42, 0xA3, 0x88, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x88, 0x2B, 0xD6, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x2C, 0xA1, 0x2C, 0x95,
+ 0x55, 0x55, 0x95, 0x95, 0x95, 0x55, 0x55, 0x55,
+ 0xA1, 0xD6, 0xD6, 0xA1, 0x2C, 0x2C, 0x95, 0x2C,
+ 0x2C, 0x2C, 0x95, 0x2C, 0x95, 0x95, 0x55, 0x95,
+ 0x95, 0x2C, 0x2C, 0x2C, 0x95, 0xCB, 0xCB, 0x94,
+ 0x20, 0x20, 0x20, 0x20, 0xE6, 0x83, 0x65, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0x42, 0x6B, 0x6B, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0x42, 0x6B, 0x6B, 0xA3, 0xD2,
+ 0xD2, 0x6B, 0xC9, 0x20, 0x20, 0x88, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x88, 0x8A, 0xA1, 0x95, 0x95,
+ 0x95, 0x55, 0x95, 0x2C, 0xA1, 0x2C, 0x95, 0xCB,
+ 0xCB, 0x55, 0x95, 0x95, 0x95, 0x55, 0x55, 0x95,
+ 0x6D, 0x6D, 0x6D, 0xD6, 0xA1, 0x2C, 0x2C, 0x95,
+ 0x2C, 0x95, 0x2C, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x55, 0x70, 0x70, 0x2C, 0x80,
+ 0x88, 0x20, 0x20, 0x80, 0x94, 0xD6, 0x32, 0x6B,
+ 0xE5, 0xE5, 0xE5, 0x42, 0x6B, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xA3, 0xD2, 0xD0, 0xBF, 0x2A,
+ 0x2A, 0xD0, 0x6D, 0x34, 0x20, 0xE1, 0x88, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x88, 0xA1, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x55, 0x70, 0x70,
+ 0x70, 0x90, 0xCB, 0xCB, 0xCB, 0x95, 0x95, 0x2C,
+ 0xD0, 0x6D, 0xD6, 0xD6, 0xA1, 0xA1, 0xA1, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x95, 0x55, 0x55, 0x55, 0x95,
+ 0x95, 0x2C, 0x95, 0x55, 0xCB, 0xCB, 0x95, 0x88,
+ 0x20, 0x20, 0x88, 0xD8, 0x2C, 0xD1, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0x65, 0x65, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0x42, 0x6B, 0xEC,
+ 0xBF, 0x2A, 0xEC, 0x95, 0x20, 0x34, 0x2B, 0xE1,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x99, 0x95, 0x55,
+ 0x55, 0x55, 0x95, 0x95, 0x95, 0x55, 0xCB, 0xCB,
+ 0x55, 0x55, 0xCB, 0xCB, 0xCB, 0x55, 0x95, 0x95,
+ 0x32, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0x2C, 0x2C,
+ 0xA1, 0x95, 0x95, 0x95, 0x55, 0xCB, 0xCB, 0x55,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x55, 0x99, 0x20,
+ 0xE1, 0xE1, 0x43, 0x47, 0x6B, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0x42, 0xEC, 0xBF, 0xA3, 0x8A, 0x20, 0x88, 0xD8,
+ 0x2B, 0x20, 0x20, 0x20, 0x88, 0x88, 0x2C, 0xCB,
+ 0xCB, 0x95, 0x95, 0x2C, 0x95, 0x95, 0x55, 0x95,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x95, 0x55, 0x95,
+ 0x6D, 0x55, 0x55, 0x55, 0x95, 0x95, 0x2C, 0x95,
+ 0x2C, 0x95, 0x95, 0x55, 0x55, 0x55, 0x55, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0xA1, 0x34, 0x20,
+ 0xC9, 0x20, 0xE1, 0xA3, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xA3, 0x83, 0x6D, 0x20, 0x88, 0x88,
+ 0x2B, 0x34, 0x20, 0x20, 0x20, 0x88, 0xD5, 0x55,
+ 0x55, 0x55, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x55, 0x55, 0x95, 0x95,
+ 0x2C, 0x55, 0xCB, 0x55, 0xCB, 0x55, 0x55, 0x95,
+ 0x95, 0x95, 0x95, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x95, 0x95, 0x55, 0x95, 0x2C, 0x20, 0xD8,
+ 0xE1, 0x20, 0x70, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0x65, 0xA3, 0x92, 0x43, 0x7D,
+ 0xD8, 0xC9, 0x88, 0x20, 0x20, 0x20, 0x43, 0xD6,
+ 0x2C, 0x2C, 0x95, 0x95, 0x95, 0x55, 0x95, 0x2C,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x2C, 0x95, 0x2C,
+ 0xA1, 0x55, 0x55, 0x55, 0x55, 0x95, 0x95, 0x55,
+ 0x55, 0x55, 0x95, 0x95, 0x2C, 0x2C, 0xA1, 0x2C,
+ 0xA1, 0x2C, 0x2C, 0x95, 0x2C, 0x99, 0x88, 0xB1,
+ 0x20, 0xD8, 0x42, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xCB, 0x34, 0x8A,
+ 0xC9, 0x34, 0x2B, 0x20, 0x20, 0x20, 0x20, 0x90,
+ 0xA1, 0xA1, 0xA1, 0x2C, 0x2C, 0x95, 0x95, 0x2C,
+ 0x2C, 0x95, 0x95, 0x95, 0x95, 0x2C, 0x2C, 0x2C,
+ 0xD6, 0x2C, 0x55, 0x55, 0x95, 0x2C, 0x2C, 0x2C,
+ 0x55, 0xCB, 0x55, 0x2C, 0x2C, 0xA1, 0x2C, 0xA1,
+ 0xA1, 0xA1, 0x2C, 0x2C, 0x6D, 0x43, 0xD8, 0x80,
+ 0x88, 0xCB, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0x32, 0x80, 0xE1,
+ 0x80, 0x20, 0xB1, 0x20, 0x20, 0x20, 0x20, 0xC9,
+ 0xD6, 0xA1, 0xA1, 0xA1, 0x2C, 0xA1, 0x2C, 0x2C,
+ 0x2C, 0x55, 0x55, 0x55, 0x95, 0x95, 0x95, 0x55,
+ 0xD6, 0x95, 0x95, 0x95, 0x2C, 0xA1, 0x2C, 0x2C,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x2C, 0x95, 0x2C,
+ 0x2C, 0x2C, 0x2C, 0x95, 0xCB, 0x20, 0xC9, 0x20,
+ 0xE1, 0xA3, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0x42, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xD8, 0x20,
+ 0x20, 0x20, 0x2B, 0x43, 0x20, 0x20, 0x20, 0x88,
+ 0xD6, 0x2C, 0x2C, 0x2C, 0x95, 0x95, 0x95, 0x55,
+ 0x95, 0x55, 0x55, 0xCB, 0x55, 0xCB, 0xCB, 0x55,
+ 0x2C, 0x55, 0x55, 0x95, 0x2C, 0x2C, 0xA1, 0x95,
+ 0x55, 0x95, 0x55, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x55, 0xCB, 0x70, 0xCB, 0xC9, 0x80, 0x2B, 0x20,
+ 0xA0, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0x42, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0x92, 0x20,
+ 0x20, 0x20, 0xE1, 0xD8, 0x20, 0x20, 0x20, 0x20,
+ 0x95, 0x95, 0x55, 0xCB, 0x90, 0x90, 0x70, 0x90,
+ 0x90, 0x90, 0xCB, 0xCB, 0xCB, 0xCB, 0x55, 0x95,
+ 0x95, 0x55, 0x55, 0x95, 0x95, 0x2C, 0x2C, 0x2C,
+ 0x95, 0x95, 0x55, 0x55, 0x55, 0x95, 0x95, 0x55,
+ 0x90, 0x47, 0xA0, 0x55, 0x20, 0x2B, 0x43, 0x88,
+ 0x6D, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0x6B, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0x28, 0x20,
+ 0x20, 0x20, 0xE1, 0xE1, 0x20, 0x20, 0x20, 0x20,
+ 0x28, 0x55, 0x90, 0x47, 0xA0, 0x47, 0x94, 0x70,
+ 0x55, 0x95, 0x95, 0x55, 0xCB, 0x55, 0x55, 0x2C,
+ 0x2C, 0x2C, 0x95, 0x95, 0x95, 0x2C, 0x2C, 0x2C,
+ 0x95, 0x2C, 0x95, 0x95, 0x95, 0x95, 0x95, 0x55,
+ 0x94, 0xE6, 0x70, 0x2B, 0x88, 0x2B, 0x88, 0xE1,
+ 0x65, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0x6B, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0x47, 0x20,
+ 0x20, 0x20, 0xE1, 0x34, 0x20, 0x20, 0x20, 0x20,
+ 0xB1, 0x95, 0x94, 0xE6, 0xA0, 0x47, 0x70, 0x55,
+ 0x2C, 0xA1, 0x2C, 0x55, 0x90, 0xCB, 0x2C, 0xD6,
+ 0x6D, 0xA1, 0x2C, 0x95, 0x95, 0xA1, 0x2C, 0xA1,
+ 0x2C, 0x2C, 0x95, 0x95, 0x95, 0x95, 0x95, 0x55,
+ 0x70, 0xE6, 0x70, 0x20, 0x20, 0x7D, 0x20, 0x8A,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0x65, 0xA3, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0x94, 0x20,
+ 0x20, 0x20, 0xD8, 0x88, 0x20, 0x20, 0x20, 0x20,
+ 0xD8, 0x2C, 0x94, 0x47, 0x47, 0x90, 0x95, 0x95,
+ 0xA1, 0x6D, 0xA1, 0x90, 0x94, 0x55, 0x2C, 0xD6,
+ 0xD0, 0xA1, 0x95, 0x95, 0x2C, 0x2C, 0xA1, 0x2C,
+ 0x95, 0x95, 0x55, 0x55, 0x55, 0x95, 0x2C, 0x2C,
+ 0xCB, 0x95, 0xD8, 0x20, 0x20, 0xB1, 0x88, 0x28,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE2, 0xA3, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xCB, 0x20,
+ 0x20, 0x20, 0x2B, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x88, 0xD6, 0x55, 0x47, 0x94, 0x55, 0x2C, 0xA1,
+ 0xA1, 0xD6, 0x95, 0x94, 0x94, 0x55, 0xD6, 0x6D,
+ 0xBF, 0x95, 0x90, 0xCB, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x55, 0x95, 0xCB, 0x90, 0x90, 0x95, 0x2C, 0x95,
+ 0x90, 0x70, 0x20, 0x20, 0x34, 0x8A, 0x20, 0x94,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0x65, 0x6B, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xCB, 0x20,
+ 0x20, 0x88, 0x2B, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x88, 0xD6, 0xCB, 0x47, 0x94, 0x55, 0xA1, 0xD6,
+ 0xD6, 0x2C, 0xCB, 0x47, 0x70, 0xA1, 0x6D, 0x2A,
+ 0x95, 0x47, 0x47, 0x70, 0x95, 0xA1, 0x2C, 0x95,
+ 0x55, 0x55, 0x90, 0x90, 0x55, 0x55, 0x55, 0x90,
+ 0x47, 0xD5, 0x20, 0x20, 0x80, 0xD5, 0x43, 0xCB,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0x42, 0x6B, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xCB, 0x20,
+ 0x20, 0x80, 0x34, 0x20, 0x20, 0x20, 0x88, 0x20,
+ 0x20, 0x2C, 0x47, 0xE6, 0x70, 0x2C, 0xD6, 0xD6,
+ 0xA1, 0x2C, 0x55, 0xCB, 0x95, 0xA1, 0x6D, 0xD6,
+ 0x90, 0x47, 0x47, 0x90, 0x2C, 0xA1, 0x2C, 0x95,
+ 0x55, 0x55, 0x90, 0x90, 0x55, 0x55, 0x55, 0x70,
+ 0x94, 0x8A, 0x20, 0x88, 0x88, 0xE1, 0xD8, 0x95,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE2, 0x42, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0x47, 0x20,
+ 0x43, 0x7D, 0x43, 0x80, 0x88, 0x20, 0x20, 0x20,
+ 0x88, 0xCB, 0x94, 0x70, 0x55, 0xA1, 0xD6, 0xD6,
+ 0xA1, 0x2C, 0x2C, 0x95, 0xA1, 0xA1, 0xD6, 0xA1,
+ 0x94, 0xE6, 0x47, 0x55, 0x2C, 0xD6, 0xA1, 0x95,
+ 0x55, 0x55, 0xCB, 0xCB, 0x55, 0x55, 0xCB, 0xCB,
+ 0x55, 0xA0, 0x43, 0x86, 0x86, 0x43, 0xD8, 0xCB,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0x65, 0x6B, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0x3F, 0x80,
+ 0xD8, 0x80, 0x88, 0x34, 0xD8, 0x2B, 0xD8, 0x20,
+ 0x99, 0x90, 0x55, 0x95, 0x2C, 0xA1, 0xD6, 0xD6,
+ 0xA1, 0x95, 0x95, 0x95, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x94, 0x94, 0x70, 0x2C, 0xA1, 0xD6, 0xA1, 0x2C,
+ 0x55, 0x55, 0xCB, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x95, 0x44, 0xBC, 0x3E, 0x5D, 0xD3, 0x79, 0x92,
+ 0xA3, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0x65, 0x42, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0x65, 0x9A, 0x34,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x43, 0x99, 0xE1,
+ 0x70, 0x55, 0x95, 0xA1, 0xD6, 0xD6, 0xD6, 0xA1,
+ 0x2C, 0x95, 0x55, 0x55, 0x95, 0x95, 0x95, 0x95,
+ 0x70, 0x70, 0x55, 0x2C, 0xD6, 0xD6, 0xA1, 0x95,
+ 0x55, 0x90, 0xCB, 0xCB, 0x55, 0x55, 0x2C, 0x2C,
+ 0x32, 0x9D, 0xEB, 0x5D, 0x69, 0x49, 0x84, 0xF0,
+ 0xB1, 0xEC, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0x42, 0x6B, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xC1, 0x4E, 0x21, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x34, 0xC9, 0xD8,
+ 0xBB, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0x2C, 0x2C,
+ 0x95, 0x55, 0x55, 0x55, 0x95, 0x95, 0x2C, 0x2C,
+ 0x55, 0xCB, 0x95, 0x2C, 0xA1, 0xA1, 0x2C, 0x55,
+ 0x90, 0x70, 0x90, 0x55, 0x95, 0x95, 0x6D, 0xD0,
+ 0xC2, 0x48, 0x6A, 0x49, 0x69, 0x82, 0x5D, 0x2F,
+ 0x59, 0x7D, 0xBF, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0x65, 0x6B, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xEA, 0xC7, 0x7E, 0x66,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x34, 0x43, 0x5A,
+ 0x46, 0x27, 0xA1, 0xA1, 0xA1, 0xA1, 0x2C, 0x95,
+ 0x95, 0x55, 0x95, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x95, 0x90, 0x55, 0x2C, 0xA1, 0xA1, 0x95, 0x55,
+ 0x94, 0x94, 0x2C, 0x2A, 0x72, 0x3B, 0x56, 0xDD,
+ 0xDF, 0x29, 0x5D, 0x49, 0x89, 0x5D, 0x3E, 0x69,
+ 0x93, 0x66, 0x34, 0xA1, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0x65, 0x42, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xEA, 0x3E, 0x5A, 0x66,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x5B, 0x73,
+ 0x89, 0x4C, 0xBF, 0x2C, 0x95, 0x2C, 0x2C, 0x95,
+ 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+ 0x2C, 0x70, 0x55, 0x2C, 0xD6, 0xD6, 0x2C, 0xCB,
+ 0x70, 0x55, 0xE7, 0x60, 0x4A, 0x48, 0xCD, 0x4A,
+ 0x29, 0x73, 0x5D, 0x82, 0x49, 0x49, 0x49, 0x49,
+ 0x3A, 0x57, 0x88, 0x88, 0x70, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0x42, 0x73, 0x50, 0xBE, 0x79,
+ 0x20, 0x20, 0x20, 0x20, 0x66, 0xCC, 0x37, 0x9C,
+ 0x3E, 0xCE, 0xBF, 0x95, 0x95, 0x95, 0x2C, 0x95,
+ 0x95, 0x55, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0x55,
+ 0xA1, 0x55, 0x95, 0xA1, 0xD6, 0xA1, 0x55, 0x94,
+ 0x94, 0xE8, 0x60, 0xC4, 0x3E, 0x2D, 0x2D, 0x2D,
+ 0x33, 0x5D, 0x82, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x89, 0xAA, 0x59, 0x20, 0x20, 0x28, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xEC, 0x4A, 0x2D, 0x50, 0x78, 0x2E,
+ 0x57, 0x51, 0xF0, 0x57, 0x31, 0x4D, 0x50, 0x2D,
+ 0x5D, 0xF2, 0xA1, 0x2C, 0x95, 0x95, 0x55, 0x55,
+ 0x90, 0x90, 0x70, 0x90, 0xCB, 0x55, 0x55, 0x55,
+ 0x6D, 0x2C, 0xA1, 0xD6, 0xD6, 0xA1, 0x55, 0x94,
+ 0x70, 0xB9, 0x75, 0x50, 0x3E, 0x49, 0x49, 0x49,
+ 0x5D, 0x82, 0x49, 0x49, 0x82, 0x49, 0x49, 0x49,
+ 0x89, 0x69, 0x4F, 0x20, 0x20, 0x20, 0x8A, 0x42,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0x83, 0x4A, 0x3A, 0x50, 0x62, 0x23,
+ 0x81, 0xB8, 0xB8, 0xE9, 0x5F, 0x29, 0x33, 0x5D,
+ 0x5D, 0x73, 0xE8, 0xCB, 0x55, 0x55, 0x55, 0x55,
+ 0x55, 0x55, 0x95, 0x95, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0xD6, 0xA1, 0xA1, 0xA1, 0xA1, 0x55, 0x70, 0x70,
+ 0xCB, 0x68, 0x75, 0x50, 0x82, 0x49, 0x49, 0x49,
+ 0x5D, 0x49, 0x49, 0x5D, 0x49, 0x49, 0x5D, 0x82,
+ 0x69, 0x5D, 0x25, 0xF0, 0x20, 0x20, 0x20, 0xE1,
+ 0x2A, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0x4B, 0xF4, 0xDF, 0x50, 0x73, 0x76, 0x48,
+ 0x75, 0xDF, 0x75, 0x62, 0xC4, 0x33, 0x82, 0x49,
+ 0x5D, 0x5D, 0xA8, 0xF5, 0x55, 0x55, 0x55, 0x55,
+ 0x2C, 0x2C, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0x2C, 0x2C, 0x2C, 0x95, 0x95, 0xCB, 0x70, 0x70,
+ 0x95, 0x83, 0x5F, 0xEA, 0x2D, 0x49, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x5D, 0x49, 0x22, 0x5A, 0x79, 0x20, 0x20, 0x20,
+ 0x80, 0xD2, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0x65, 0xD0, 0x63, 0x5F, 0x29, 0x2D, 0x2D, 0xEA,
+ 0x29, 0x29, 0x76, 0x50, 0x2D, 0x82, 0x49, 0x49,
+ 0x3E, 0x49, 0x5C, 0xB0, 0xBA, 0x95, 0x55, 0x55,
+ 0x2C, 0xA1, 0xD6, 0xD6, 0xD6, 0xA1, 0x2C, 0x2C,
+ 0xA1, 0x95, 0x95, 0x55, 0xCB, 0x70, 0x70, 0x55,
+ 0x2C, 0x83, 0x60, 0x76, 0x5D, 0x49, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x49, 0x5D, 0x89, 0xDC, 0x8B, 0x20, 0x20, 0x20,
+ 0x20, 0x95, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE2, 0x32, 0x85, 0xE3, 0x29, 0x2D, 0x33, 0x2D,
+ 0x2D, 0x2D, 0x6A, 0x2D, 0x33, 0x5D, 0x49, 0x82,
+ 0x49, 0x49, 0x82, 0x73, 0x5C, 0x9E, 0x2C, 0x55,
+ 0x2C, 0xA1, 0xD6, 0xA1, 0x2C, 0x2C, 0x95, 0x95,
+ 0x2C, 0x95, 0x55, 0xCB, 0x90, 0x90, 0xCB, 0x95,
+ 0x2C, 0x6D, 0x41, 0x6F, 0x3E, 0x49, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x49, 0x82, 0x3E, 0x4E, 0x38, 0xCA, 0x20, 0x20,
+ 0x20, 0x55, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0x65,
+ 0x42, 0xA0, 0xD4, 0xE3, 0x29, 0x2D, 0x82, 0x5D,
+ 0x5D, 0x82, 0x82, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x49, 0x3E, 0x49, 0x49, 0x49, 0x5C, 0x56, 0xD6,
+ 0xA1, 0xA1, 0xA1, 0x95, 0x55, 0x55, 0x55, 0x95,
+ 0xA1, 0x55, 0x90, 0x70, 0x94, 0x70, 0x95, 0x2C,
+ 0x2C, 0xD6, 0xDD, 0x6F, 0x33, 0x49, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x5D, 0x5D, 0x82, 0x69, 0x22, 0x62, 0x80, 0x34,
+ 0x94, 0x6B, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0x65, 0xE5, 0x65, 0x6B,
+ 0xD5, 0x88, 0x5B, 0xE3, 0x29, 0x5D, 0x5D, 0x5D,
+ 0x5D, 0x5D, 0x5D, 0x5D, 0x49, 0x49, 0x49, 0x82,
+ 0x49, 0x49, 0x89, 0x49, 0x82, 0x49, 0x71, 0xBA,
+ 0x6D, 0x6D, 0xA1, 0x95, 0x55, 0xCB, 0x55, 0x55,
+ 0x2C, 0x55, 0x70, 0x70, 0x70, 0x90, 0x95, 0xA1,
+ 0x2C, 0xA1, 0x41, 0x76, 0x5D, 0x5D, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x49, 0x5D, 0x82, 0x5D, 0x89, 0x5E, 0x96, 0x65,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0x65, 0x65, 0xEC, 0xB1,
+ 0x20, 0x20, 0xCA, 0x23, 0x29, 0x33, 0x49, 0x5D,
+ 0x49, 0x82, 0x49, 0x49, 0x49, 0x49, 0x49, 0x82,
+ 0x49, 0x82, 0x5D, 0x5D, 0x5D, 0x2D, 0x5C, 0x8F,
+ 0x6D, 0xD6, 0x2C, 0x55, 0x90, 0xCB, 0x95, 0x95,
+ 0x95, 0x55, 0x70, 0x94, 0x70, 0x55, 0x2C, 0xA1,
+ 0x95, 0xE8, 0x5F, 0x76, 0x33, 0x5D, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x3E, 0x9C, 0x2F, 0x68,
+ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+ 0x65, 0xE5, 0x65, 0xE5, 0x6B, 0x90, 0x80, 0x20,
+ 0x20, 0x20, 0x4F, 0x81, 0x50, 0x3E, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x69, 0x69, 0x49, 0x5D, 0x2D, 0xC4, 0x46, 0xA3,
+ 0xD6, 0x55, 0x70, 0x94, 0x94, 0x70, 0xCB, 0x55,
+ 0x55, 0xCB, 0x70, 0x47, 0x70, 0x95, 0xA1, 0xA1,
+ 0x95, 0xBD, 0x75, 0x2D, 0x33, 0x49, 0x49, 0x49,
+ 0x49, 0x49, 0x5D, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x5D, 0x2D, 0xB5, 0xDB,
+ 0xD6, 0x65, 0xE5, 0x65, 0xE5, 0xE5, 0x65, 0xE5,
+ 0x65, 0x65, 0x6B, 0x95, 0x2B, 0x88, 0x20, 0x20,
+ 0x20, 0x20, 0x8B, 0x81, 0x29, 0x33, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x49, 0x3E, 0x3E, 0x5E, 0x41, 0x97, 0x27, 0xD6,
+ 0x55, 0x94, 0xE6, 0xE6, 0x47, 0x70, 0x55, 0x55,
+ 0x94, 0x70, 0x94, 0x94, 0x70, 0x55, 0xA1, 0x2C,
+ 0x6D, 0xC5, 0x39, 0x6A, 0x5D, 0x5D, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x3E, 0xEA, 0x30, 0x77,
+ 0xE1, 0xC9, 0x94, 0x2C, 0xD6, 0xD6, 0xA1, 0x55,
+ 0x47, 0x9F, 0x43, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x80, 0x91, 0x81, 0x6A, 0x2D, 0x49, 0x49,
+ 0x49, 0x5D, 0x5D, 0x49, 0x49, 0x5D, 0x5D, 0x82,
+ 0xEB, 0x4A, 0x41, 0xC2, 0x8F, 0xF5, 0xA1, 0x55,
+ 0x94, 0x28, 0xA0, 0x47, 0x70, 0x55, 0x95, 0x95,
+ 0x47, 0x70, 0x70, 0x94, 0x90, 0x95, 0xA1, 0x2C,
+ 0xE8, 0xA6, 0x39, 0x76, 0x50, 0x50, 0x2D, 0x2D,
+ 0x3E, 0x3E, 0x5D, 0x3E, 0x5D, 0x5D, 0x49, 0x82,
+ 0x49, 0x49, 0x49, 0x82, 0x82, 0x50, 0x75, 0xE0,
+ 0x57, 0x20, 0x88, 0x88, 0x20, 0x20, 0x88, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x79, 0x91, 0x81, 0x76, 0x33, 0x49, 0x49,
+ 0x5D, 0x82, 0x49, 0x49, 0x3E, 0x6A, 0xEA, 0x29,
+ 0xDF, 0x97, 0xBF, 0x6D, 0x6D, 0xD6, 0x55, 0x47,
+ 0x28, 0x28, 0x47, 0x70, 0x55, 0x95, 0x2C, 0x2C,
+ 0x95, 0x95, 0x55, 0x90, 0x90, 0x95, 0xA1, 0xA1,
+ 0xD6, 0x26, 0x45, 0x81, 0x5F, 0x30, 0x48, 0x6F,
+ 0x6F, 0x29, 0x29, 0x6A, 0x2D, 0x2D, 0x5D, 0x49,
+ 0x49, 0x49, 0x49, 0x49, 0x2D, 0x76, 0x6E, 0x77,
+ 0x5B, 0x66, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x79, 0xA9, 0xB8, 0x39, 0x50, 0x5D, 0x5D,
+ 0x5D, 0x5D, 0x3E, 0x2D, 0x29, 0x76, 0xCD, 0x37,
+ 0xB9, 0xA1, 0xA1, 0x6D, 0x6D, 0x2C, 0x94, 0x28,
+ 0xD5, 0xE6, 0x70, 0x55, 0x95, 0xA1, 0x2C, 0xA1,
+ 0xBF, 0xA1, 0x95, 0xCB, 0x55, 0x95, 0xA1, 0x2C,
+ 0x95, 0x83, 0xDE, 0x87, 0xB6, 0xBE, 0x40, 0x6E,
+ 0x81, 0x81, 0x78, 0x78, 0x39, 0x6F, 0xEA, 0x2D,
+ 0x2D, 0x33, 0x33, 0x33, 0x76, 0x30, 0x64, 0x54,
+ 0x5B, 0x66, 0x20, 0x20, 0x66, 0x20, 0x88, 0x20,
+ 0x20, 0x20, 0x88, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x88, 0x34, 0x8B, 0xF1, 0x23, 0x6F, 0x50, 0x2D,
+ 0x2D, 0x6A, 0x29, 0x6F, 0x78, 0x84, 0x9B, 0xD2,
+ 0x2C, 0x2C, 0xD6, 0x6D, 0x6D, 0x2C, 0x47, 0xA0,
+ 0xE6, 0x70, 0x55, 0x95, 0x2C, 0xA1, 0xA1, 0xA1,
+ 0xD2, 0x95, 0x55, 0xCB, 0x55, 0x2C, 0xD6, 0xA1,
+ 0x95, 0x95, 0xA1, 0xD6, 0x6D, 0x6D, 0xBA, 0xF3,
+ 0x8D, 0x36, 0x74, 0x36, 0xF1, 0xB8, 0x23, 0x78,
+ 0x62, 0x4A, 0x29, 0x62, 0x23, 0xF1, 0x54, 0x31,
+ 0x57, 0x2B, 0x90, 0x95, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0xA1, 0xA1, 0xA1, 0xA1, 0x2C, 0x2C, 0x2C, 0xCB,
+ 0xE6, 0x7D, 0xCA, 0xB7, 0xB8, 0x75, 0x6F, 0x6F,
+ 0x76, 0x6F, 0x78, 0x81, 0x53, 0xBD, 0x6D, 0x2C,
+ 0x95, 0x95, 0xA1, 0x6D, 0xA1, 0x55, 0x94, 0xE6,
+ 0x70, 0xCB, 0x55, 0x95, 0xA1, 0xD6, 0xD6, 0xA1,
+ 0xD0, 0x94, 0x94, 0x90, 0x95, 0x2C, 0xD6, 0xA1,
+ 0x95, 0x55, 0x2C, 0xA1, 0xD6, 0xA1, 0x95, 0x2C,
+ 0xD6, 0x68, 0xAB, 0x6C, 0xA4, 0x77, 0x77, 0xAD,
+ 0x40, 0x53, 0x6E, 0x40, 0xB7, 0x54, 0x31, 0xD7,
+ 0xAC, 0xD6, 0x55, 0x55, 0x95, 0x95, 0x95, 0x55,
+ 0x95, 0x2C, 0x2C, 0xA1, 0x95, 0x95, 0x2C, 0xA1,
+ 0x6D, 0xD2, 0x7C, 0x54, 0xAD, 0x40, 0x6E, 0x81,
+ 0x81, 0x6E, 0x36, 0xDA, 0xE8, 0xD6, 0xD6, 0x2C,
+ 0x2C, 0x2C, 0xA1, 0xD6, 0x95, 0x90, 0x94, 0x47,
+ 0x94, 0x94, 0x70, 0x55, 0x2C, 0xD6, 0xA1, 0x95,
+ 0x95, 0x28, 0x47, 0x90, 0x95, 0x2C, 0xA1, 0x2C,
+ 0x55, 0x95, 0x2C, 0xA1, 0xA1, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0xA1, 0x55, 0x70, 0x95, 0x2C, 0xB2, 0xB4,
+ 0xC3, 0xC3, 0x54, 0x54, 0xA9, 0x31, 0xCA, 0x2A,
+ 0x95, 0x90, 0x55, 0x95, 0x2C, 0xA1, 0x2C, 0x95,
+ 0x95, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0xD6,
+ 0x6D, 0x2A, 0xB2, 0x4F, 0x31, 0x2E, 0xE0, 0xAD,
+ 0xB7, 0xC8, 0xB4, 0xF5, 0x2C, 0xA1, 0xA1, 0xA1,
+ 0x95, 0x2C, 0xA1, 0x2C, 0x95, 0x70, 0x94, 0x94,
+ 0x94, 0x94, 0x70, 0x95, 0xD6, 0xD6, 0x2C, 0x95,
+ 0x94, 0x28, 0x47, 0xCB, 0x95, 0x2C, 0xA1, 0xA1,
+ 0x95, 0x55, 0x2C, 0xA1, 0xD6, 0xA1, 0x95, 0x95,
+ 0x95, 0x2C, 0x55, 0x70, 0x70, 0x70, 0x94, 0x2C,
+ 0x63, 0xBB, 0xA5, 0xD7, 0xCA, 0xB3, 0x6D, 0x2C,
+ 0x55, 0x55, 0x95, 0x2C, 0x2C, 0x2C, 0x95, 0x95,
+ 0x95, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0xA1,
+ 0xD6, 0x2C, 0x70, 0x95, 0xAC, 0xC0, 0xDB, 0xEF,
+ 0xEF, 0xA2, 0xE8, 0x95, 0x95, 0xA1, 0xD6, 0xA1,
+ 0x95, 0x55, 0x2C, 0x95, 0x55, 0x70, 0x70, 0x70,
+ 0x94, 0x70, 0x55, 0xD6, 0x6D, 0x6D, 0x95, 0x55,
+ 0x70, 0x47, 0x70, 0x95, 0x2C, 0x2C, 0x2C, 0xA1,
+ 0x2C, 0x95, 0x2C, 0xA1, 0xD6, 0xA1, 0x95, 0x55,
+ 0x55, 0x95, 0x55, 0x55, 0x55, 0x55, 0x55, 0x95,
+ 0xA1, 0xF5, 0xBF, 0xBF, 0xA1, 0x95, 0x95, 0x95,
+ 0x95, 0x55, 0x2C, 0x2C, 0x95, 0x55, 0x55, 0x95,
+ 0x95, 0x95, 0xA1, 0xA1, 0xA1, 0xA1, 0x2C, 0xA1,
+ 0x2C, 0x55, 0x70, 0x94, 0x90, 0x2C, 0x6D, 0x6D,
+ 0x6D, 0xA1, 0x2C, 0x95, 0x2C, 0xA1, 0xD6, 0xA1,
+ 0x2C, 0x55, 0x55, 0x95, 0x55, 0x55, 0x55, 0x55,
+ 0x55, 0x95, 0xD6, 0x6D, 0xBF, 0xD6, 0x55, 0xCB,
+ 0x55, 0x55, 0x55, 0x2C, 0x2C, 0x2C, 0x2C, 0xA1,
+ 0xA1, 0x95, 0x2C, 0xA1, 0xA1, 0xA1, 0x2C, 0x95,
+ 0x55, 0x95, 0x95, 0x2C, 0x2C, 0x2C, 0x2C, 0xA1,
+ 0x6D, 0xBF, 0x6D, 0x2C, 0x55, 0x55, 0x95, 0x95,
+ 0xCB, 0xCB, 0x55, 0x55, 0xCB, 0x55, 0x55, 0x95,
+ 0x95, 0x2C, 0x2C, 0xA1, 0xA1, 0xA1, 0x2C, 0x2C,
+ 0xA1, 0x95, 0xCB, 0xCB, 0x95, 0x95, 0x2C, 0x2C,
+ 0x2C, 0xA1, 0x2C, 0x2C, 0x2C, 0xA1, 0xA1, 0x2C,
+ 0x2C, 0x95, 0x55, 0x95, 0x95, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0xA1, 0x6D, 0xBF, 0x6D, 0xA1, 0x55, 0x55,
+ 0x95, 0x95, 0x95, 0x95, 0x2C, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0x95, 0x95, 0x95, 0x2C, 0x2C, 0x2C, 0x95,
+ 0x55, 0x95, 0x2C, 0x2C, 0xA1, 0xA1, 0xD6, 0xD6,
+ 0x6D, 0x6D, 0xA1, 0x95, 0xCB, 0x55, 0x95, 0x55,
+ 0x90, 0x70, 0xCB, 0xCB, 0x90, 0xCB, 0x95, 0x95,
+ 0x2C, 0x2C, 0xA1, 0xD6, 0xA1, 0xA1, 0xA1, 0xA1,
+ 0xA1, 0xA1, 0x2C, 0x95, 0x95, 0x2C, 0x2C, 0x2C,
+ 0x2C, 0xA1, 0x2C, 0x95, 0x95, 0x95, 0x2C, 0x2C,
+ 0x2C, 0x95, 0x55, 0x55, 0x2C, 0x2C, 0xA1, 0xA1,
+ 0xD6, 0xD6, 0x6D, 0x6D, 0xA1, 0x55, 0xCB, 0x55
+};
+
+#endif
+
+#ifdef INCLUDE_LINUX_LOGOBW
+
+unsigned char linux_logo_bw[] __initdata = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x3F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F,
+ 0xFE, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFE, 0x3F, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xC7, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xC3,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF,
+ 0xFB, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFD, 0xFF, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xF1,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF,
+ 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF9, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xF9, 0xCF, 0xC3, 0xF8, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x87, 0x81, 0xF9,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xA7,
+ 0x99, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF9, 0xF3, 0xBC, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xF9, 0xE3, 0xBC, 0xF9, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xB0, 0x3C, 0xF9,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0xB0,
+ 0x19, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF9, 0xC0, 0x03, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xF9, 0x80, 0x01, 0xF8, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x80, 0x01, 0xF8,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x80,
+ 0x01, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xF9, 0xC0, 0x21, 0xD8, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xF9, 0xB1, 0x80, 0xEC, 0xC0, 0x1F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x90, 0x00, 0xE4,
+ 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x8C,
+ 0xC0, 0x7C, 0x04, 0x81, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xE3, 0x80, 0x00, 0x7C, 0x40, 0x11, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xE3, 0x80, 0x00, 0x7F, 0xD2, 0x29,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0x00, 0x00, 0x3F,
+ 0x80, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0x0E, 0x00,
+ 0x00, 0x3F, 0x80, 0x19, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x1E, 0x00, 0x00, 0x1F, 0x80, 0x19, 0xFF, 0xFF,
+ 0xFF, 0xFE, 0x1C, 0x00, 0x00, 0x1E, 0x80, 0x19,
+ 0xFF, 0xFF, 0xFF, 0xFE, 0x3C, 0x00, 0x00, 0x1E,
+ 0x80, 0x11, 0xFF, 0xFF, 0xFF, 0xFC, 0x7C, 0x00,
+ 0x00, 0x0F, 0x80, 0x11, 0xFF, 0xFF, 0xFF, 0xFC,
+ 0xF8, 0x00, 0x00, 0x0E, 0x80, 0x11, 0xFF, 0xFF,
+ 0xFF, 0xFC, 0xF8, 0x00, 0x00, 0x06, 0x00, 0x11,
+ 0xFF, 0xFF, 0xFF, 0xF8, 0xF8, 0x00, 0x00, 0x06,
+ 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xF9, 0xF0, 0x00,
+ 0x00, 0x02, 0x00, 0x09, 0xFF, 0xFF, 0xFF, 0xF1,
+ 0xF0, 0x00, 0x00, 0x02, 0x80, 0x10, 0xFF, 0xFF,
+ 0xFF, 0xF1, 0xE0, 0x00, 0x00, 0x00, 0x97, 0x10,
+ 0xFF, 0xFF, 0xFF, 0xE3, 0xE0, 0x00, 0x00, 0x00,
+ 0xDF, 0xF0, 0xFF, 0xFF, 0xFF, 0xE3, 0xC0, 0x00,
+ 0x00, 0x00, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xC7,
+ 0xC0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0xFF, 0xFF,
+ 0xFF, 0xC7, 0x80, 0x00, 0x00, 0x01, 0xFF, 0xF8,
+ 0xFF, 0xFF, 0xFF, 0x8F, 0x80, 0x00, 0x00, 0x01,
+ 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0x8F, 0x80, 0x00,
+ 0x00, 0x01, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0x9F,
+ 0x80, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0xFF, 0xFF,
+ 0xFF, 0x9F, 0x80, 0x00, 0x00, 0x01, 0x80, 0x18,
+ 0xFF, 0xFF, 0xFF, 0x9E, 0x80, 0x00, 0x00, 0x03,
+ 0xA8, 0x11, 0xFF, 0xFF, 0xFF, 0x9F, 0x80, 0x00,
+ 0x00, 0x02, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x99,
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x09, 0xFF, 0xFF,
+ 0xFF, 0x00, 0x80, 0x00, 0x00, 0x01, 0xC0, 0x01,
+ 0xFF, 0xFF, 0xFE, 0x20, 0x60, 0x00, 0x00, 0x00,
+ 0xFF, 0xC3, 0xFF, 0xFF, 0xF8, 0x00, 0x30, 0x00,
+ 0x00, 0x00, 0xFF, 0x0F, 0xFF, 0xFF, 0xC0, 0x40,
+ 0x38, 0x00, 0x00, 0x00, 0xFE, 0x47, 0xFF, 0xFF,
+ 0x81, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xFC, 0x23,
+ 0xFF, 0xFF, 0x90, 0x00, 0x1E, 0x00, 0x00, 0x00,
+ 0x78, 0x11, 0xFF, 0xFF, 0x80, 0x00, 0x0F, 0x80,
+ 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00,
+ 0x07, 0xC0, 0x00, 0x00, 0x00, 0x08, 0xFF, 0xFF,
+ 0xC0, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x04,
+ 0x7F, 0xFF, 0x80, 0x00, 0x03, 0xC0, 0x00, 0x10,
+ 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x01, 0x80,
+ 0x00, 0x30, 0x00, 0x00, 0x0F, 0xFF, 0x80, 0x00,
+ 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, 0x4F, 0xFF,
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00,
+ 0x0F, 0xFF, 0xC0, 0x00, 0x00, 0x80, 0x03, 0xF0,
+ 0x00, 0x00, 0x8F, 0xFF, 0x80, 0x00, 0x00, 0x40,
+ 0x0F, 0xF0, 0x00, 0x04, 0x1F, 0xFF, 0x80, 0x00,
+ 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x10, 0x1F, 0xFF,
+ 0xC0, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x40,
+ 0xFF, 0xFF, 0x98, 0x00, 0x00, 0xFF, 0xFF, 0xF0,
+ 0x00, 0x83, 0xFF, 0xFF, 0x81, 0xE0, 0x01, 0xFF,
+ 0xFF, 0xF8, 0x02, 0x07, 0xFF, 0xFF, 0x80, 0x3F,
+ 0x07, 0xE0, 0x00, 0x1C, 0x0C, 0x1F, 0xFF, 0xFF,
+ 0xF8, 0x03, 0xFF, 0x80, 0x00, 0x1F, 0x78, 0x1F,
+ 0xFF, 0xFF, 0xFF, 0x80, 0x7F, 0x00, 0x07, 0x0F,
+ 0xF0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFE, 0x0C, 0x07,
+ 0xFF, 0x83, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x1F, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x07, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+#endif
+
+#ifdef INCLUDE_LINUX_LOGO16
+
+unsigned char linux_logo16_red[] __initdata = {
+ 0x00, 0x90, 0xb0, 0x9c, 0xf7, 0x35, 0x83, 0xa5,
+ 0x65, 0x8f, 0x98, 0xc9, 0xdb, 0xe1, 0xe7, 0xf8
+};
+
+unsigned char linux_logo16_green[] __initdata = {
+ 0x00, 0x90, 0xb0, 0x9c, 0xf7, 0x2e, 0x83, 0xa5,
+ 0x65, 0x6e, 0x98, 0x89, 0xbf, 0xac, 0xda, 0xf8
+};
+
+unsigned char linux_logo16_blue[] __initdata = {
+ 0x00, 0x90, 0xaf, 0x9c, 0xf7, 0x2b, 0x82, 0xa5,
+ 0x65, 0x41, 0x97, 0x1e, 0x60, 0x29, 0xa5, 0xf8
+};
+
+unsigned char linux_logo16[] __initdata = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa1, 0x11, 0x11,
+ 0x61, 0x16, 0x66, 0x66, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x1a, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0xa8, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x87, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x77, 0x73, 0x33, 0x33, 0x3a, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xa3, 0x33, 0x33, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x77, 0x77,
+ 0x77, 0x27, 0x77, 0x77, 0x77, 0x33, 0x3a, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xa3, 0x33, 0x33, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x55, 0x50, 0x08, 0x33, 0x77, 0x77,
+ 0x77, 0x72, 0x72, 0x27, 0x77, 0x77, 0x33, 0x33,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xa3, 0x33, 0x33, 0x77, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x58, 0x85, 0x00, 0x11, 0x11, 0xaa,
+ 0xa3, 0x37, 0x77, 0x72, 0x22, 0x22, 0x77, 0x73,
+ 0x33, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa3,
+ 0x33, 0x37, 0x77, 0x33, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x56, 0x85, 0x00, 0x06, 0x66, 0x11,
+ 0x11, 0x1a, 0xa3, 0x37, 0x77, 0x72, 0x22, 0x77,
+ 0x73, 0x33, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x33,
+ 0x33, 0x33, 0x33, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x55, 0x00, 0x00, 0x06, 0x66, 0x66,
+ 0x66, 0x66, 0x11, 0x1a, 0xa3, 0x77, 0x72, 0x22,
+ 0x77, 0x73, 0x3a, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0x33,
+ 0x33, 0x33, 0x33, 0xa0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11,
+ 0x66, 0x66, 0x66, 0x66, 0x11, 0xa3, 0x77, 0x22,
+ 0x22, 0x77, 0x33, 0x33, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0x33, 0x33,
+ 0x33, 0x3a, 0xa1, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x33,
+ 0xaa, 0x11, 0x16, 0x66, 0x66, 0x61, 0x1a, 0x37,
+ 0x22, 0x22, 0x77, 0x33, 0x3a, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xa3, 0x33, 0x33, 0x33,
+ 0x3a, 0xa1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x22,
+ 0x22, 0x77, 0x3a, 0x11, 0x66, 0x66, 0x66, 0x1a,
+ 0x37, 0x22, 0x22, 0x77, 0x33, 0x3a, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0x33, 0x33, 0x3a,
+ 0xa1, 0x11, 0x11, 0x10, 0x00, 0x00, 0x50, 0x00,
+ 0x00, 0x05, 0x80, 0x50, 0x00, 0x00, 0x07, 0x72,
+ 0x22, 0x22, 0x22, 0x73, 0xa1, 0x66, 0x66, 0x61,
+ 0x1a, 0x77, 0x22, 0x27, 0x73, 0x33, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0x33, 0x3a, 0xaa,
+ 0x11, 0x11, 0x1a, 0xa0, 0x08, 0x71, 0x05, 0x00,
+ 0x00, 0x12, 0x22, 0x50, 0x00, 0x00, 0x07, 0x77,
+ 0x77, 0x72, 0x22, 0x22, 0x27, 0x31, 0x16, 0x66,
+ 0x61, 0x13, 0x77, 0x22, 0x77, 0x33, 0x3a, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xa3, 0x33, 0x33, 0xaa, 0xa1,
+ 0x11, 0x1a, 0x33, 0x70, 0x07, 0x2e, 0x70, 0x00,
+ 0x01, 0x44, 0x42, 0x60, 0x00, 0x00, 0x02, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x27, 0x31, 0x66,
+ 0x66, 0x61, 0xa3, 0x72, 0x22, 0x77, 0x33, 0xaa,
+ 0xaa, 0xaa, 0xa3, 0x33, 0x33, 0xaa, 0xaa, 0x11,
+ 0x1a, 0x33, 0x77, 0x30, 0x04, 0x82, 0x40, 0x00,
+ 0x54, 0x48, 0x54, 0x40, 0x00, 0x00, 0x01, 0xaa,
+ 0x32, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x31,
+ 0x66, 0x66, 0x11, 0x37, 0x22, 0x27, 0x73, 0x3a,
+ 0xaa, 0xaa, 0xa3, 0x33, 0x3a, 0xaa, 0xaa, 0xaa,
+ 0xa3, 0x77, 0xaa, 0x10, 0x50, 0x08, 0x46, 0x05,
+ 0x54, 0x80, 0x50, 0x42, 0x00, 0x00, 0x08, 0x66,
+ 0x66, 0x1a, 0x32, 0x22, 0x22, 0x22, 0x22, 0x27,
+ 0x31, 0x66, 0x66, 0x13, 0x72, 0x22, 0x77, 0x33,
+ 0xaa, 0xaa, 0xaa, 0x33, 0xaa, 0xa1, 0xaa, 0xa3,
+ 0x37, 0xa1, 0x1a, 0x30, 0x50, 0x06, 0x26, 0x00,
+ 0x54, 0x00, 0x00, 0x44, 0x00, 0x00, 0x08, 0xe2,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0x22, 0x22,
+ 0x27, 0xa6, 0x66, 0x61, 0xa7, 0x72, 0x27, 0x73,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x33,
+ 0x31, 0x11, 0x37, 0x70, 0x02, 0x00, 0xab, 0xbb,
+ 0xb6, 0x00, 0x00, 0xf4, 0x00, 0x00, 0xee, 0xee,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0x22,
+ 0x22, 0x23, 0x16, 0x66, 0x1a, 0x37, 0x22, 0x77,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa3, 0x3a,
+ 0x11, 0xa7, 0x33, 0x10, 0x04, 0x09, 0xbd, 0xdd,
+ 0xbd, 0xd0, 0x04, 0x45, 0x00, 0x0e, 0xee, 0xee,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0x22,
+ 0x22, 0x22, 0x71, 0x66, 0x66, 0x13, 0x72, 0x27,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x33, 0x11,
+ 0xa3, 0x73, 0xa1, 0x60, 0x08, 0xbd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdb, 0x90, 0x00, 0x02, 0xec, 0xee,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xce, 0x22,
+ 0x22, 0x22, 0x27, 0xa6, 0x66, 0x61, 0x37, 0x27,
+ 0x1a, 0xaa, 0xaa, 0xaa, 0xaa, 0xa3, 0xa1, 0x1a,
+ 0x33, 0xa1, 0x16, 0x60, 0x0b, 0xbd, 0xdd, 0xdd,
+ 0xcd, 0xdd, 0xdd, 0xd9, 0x00, 0x00, 0xec, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0xa2,
+ 0x22, 0x22, 0x22, 0x7a, 0x66, 0x66, 0x13, 0x77,
+ 0x1a, 0xaa, 0xaa, 0xaa, 0xaa, 0x3a, 0x11, 0x33,
+ 0xaa, 0x11, 0x66, 0x60, 0x9b, 0xdd, 0xdd, 0xdd,
+ 0xcd, 0xdd, 0xdb, 0xb9, 0x00, 0x00, 0xec, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xee, 0x61,
+ 0x72, 0x22, 0x22, 0x22, 0xa1, 0x66, 0x61, 0x37,
+ 0x1a, 0xaa, 0xaa, 0xaa, 0xa3, 0xa1, 0x13, 0x3a,
+ 0x11, 0x11, 0x11, 0x10, 0x5b, 0xdd, 0xdd, 0xdc,
+ 0xdd, 0xdd, 0xbd, 0xd9, 0x00, 0x00, 0xec, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xee, 0x86,
+ 0x17, 0x22, 0x22, 0x22, 0x23, 0x16, 0x66, 0xaa,
+ 0xaa, 0xa3, 0x3a, 0xaa, 0xaa, 0x1a, 0x3a, 0xa1,
+ 0x11, 0x11, 0x1a, 0x70, 0x05, 0xbd, 0xdd, 0xdd,
+ 0xdb, 0x5b, 0xdd, 0xb0, 0x00, 0x60, 0x2e, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xe6, 0x88,
+ 0x66, 0x32, 0x22, 0x22, 0x22, 0x36, 0x66, 0x11,
+ 0x33, 0x33, 0x3a, 0xaa, 0x11, 0xaa, 0xaa, 0xa1,
+ 0x11, 0x1a, 0x3a, 0x60, 0x02, 0x99, 0xbb, 0xb9,
+ 0x9b, 0xbb, 0xbc, 0x22, 0x00, 0x86, 0x5e, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xe1, 0x68,
+ 0x86, 0x63, 0x22, 0x22, 0x22, 0x2a, 0x66, 0x66,
+ 0x33, 0x33, 0xaa, 0xaa, 0x1a, 0xaa, 0xaa, 0x11,
+ 0x1a, 0xa7, 0x68, 0x80, 0x02, 0x2b, 0xbd, 0xbb,
+ 0xbb, 0xb9, 0x22, 0x22, 0x00, 0x06, 0x6e, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xc7, 0xa6,
+ 0x88, 0x86, 0x32, 0x22, 0x22, 0x27, 0xa6, 0x66,
+ 0x33, 0x3a, 0xaa, 0xa1, 0xaa, 0xaa, 0xa1, 0x11,
+ 0xa3, 0xa6, 0x88, 0x80, 0x02, 0x22, 0x9b, 0xbb,
+ 0xbb, 0x22, 0x24, 0xf4, 0x60, 0x00, 0x0c, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xc2, 0x21,
+ 0x68, 0x88, 0x63, 0x22, 0x22, 0x22, 0x71, 0x66,
+ 0x33, 0x3a, 0x11, 0x11, 0xaa, 0xaa, 0x11, 0xaa,
+ 0x71, 0x88, 0x88, 0x00, 0x02, 0xe2, 0x26, 0x99,
+ 0x22, 0x22, 0x4f, 0xf4, 0x40, 0x00, 0x0c, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x22, 0x22,
+ 0x16, 0x88, 0x86, 0xa2, 0x22, 0x22, 0x27, 0x11,
+ 0x33, 0xa1, 0x11, 0x11, 0xaa, 0x31, 0x1a, 0xa3,
+ 0x68, 0x88, 0x81, 0x00, 0x54, 0x42, 0x22, 0x22,
+ 0x22, 0x44, 0xff, 0xff, 0x48, 0x00, 0x00, 0x99,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x22, 0x22,
+ 0x21, 0x88, 0x88, 0x6a, 0x22, 0x22, 0x22, 0x31,
+ 0x3a, 0xa1, 0x11, 0x1a, 0xa3, 0x11, 0x33, 0x36,
+ 0x88, 0x86, 0x30, 0x00, 0x4f, 0x44, 0x22, 0x22,
+ 0x24, 0xff, 0xff, 0xff, 0x44, 0x00, 0x00, 0x99,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x95, 0x22, 0x72,
+ 0x22, 0x18, 0x88, 0x86, 0x32, 0x22, 0x22, 0x27,
+ 0xaa, 0x11, 0x11, 0x1a, 0x31, 0x13, 0x33, 0x68,
+ 0x88, 0x6a, 0x00, 0x02, 0x4f, 0x4f, 0x42, 0x24,
+ 0x4f, 0xff, 0xff, 0xff, 0xf4, 0x50, 0x00, 0x99,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x22, 0x73,
+ 0x72, 0x26, 0x88, 0x88, 0x63, 0x22, 0x22, 0x22,
+ 0x11, 0x11, 0x11, 0xa3, 0xa1, 0x73, 0xa6, 0x88,
+ 0x81, 0xa5, 0x00, 0x04, 0x4f, 0x4f, 0x44, 0x4f,
+ 0xff, 0xff, 0xff, 0xff, 0xf4, 0x40, 0x00, 0x99,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x12, 0x27,
+ 0xaa, 0x22, 0x68, 0x55, 0x86, 0x72, 0x22, 0x22,
+ 0x11, 0x11, 0x1a, 0x33, 0x13, 0x3a, 0x18, 0x88,
+ 0x1a, 0x10, 0x00, 0x44, 0x4f, 0x4f, 0xff, 0x4f,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x99,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x61, 0x22,
+ 0x3a, 0xa2, 0x26, 0x85, 0x58, 0x67, 0x22, 0x22,
+ 0x61, 0x61, 0x1a, 0x7a, 0x37, 0x31, 0x88, 0x81,
+ 0x11, 0x00, 0x05, 0xe4, 0x44, 0xff, 0xff, 0xff,
+ 0x4f, 0xf4, 0x44, 0xff, 0xff, 0xf5, 0x00, 0x99,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x88, 0x12,
+ 0x2a, 0xaa, 0x72, 0x68, 0x55, 0x81, 0x22, 0x22,
+ 0x66, 0x61, 0xa3, 0x33, 0x73, 0x16, 0x88, 0x11,
+ 0x10, 0x00, 0x08, 0x74, 0x44, 0x4f, 0x44, 0x44,
+ 0xf4, 0xf4, 0x44, 0x44, 0xe2, 0x44, 0x00, 0x99,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x88, 0x81,
+ 0x22, 0xaa, 0xa7, 0x26, 0x85, 0x88, 0x12, 0x22,
+ 0x66, 0x61, 0x37, 0xa7, 0x3a, 0x66, 0x66, 0x11,
+ 0x80, 0x00, 0x0a, 0x72, 0x44, 0x4f, 0x44, 0x4f,
+ 0xff, 0x44, 0x44, 0x22, 0x22, 0x24, 0x00, 0x99,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0x85, 0x88,
+ 0x12, 0x2a, 0xaa, 0x22, 0x68, 0x58, 0x63, 0x22,
+ 0x66, 0x1a, 0x73, 0x77, 0x31, 0x66, 0x61, 0x11,
+ 0x00, 0x00, 0x07, 0x44, 0xff, 0x4f, 0xf4, 0x4f,
+ 0xff, 0x4f, 0x44, 0xf4, 0x42, 0x22, 0x40, 0x9b,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb9, 0x85, 0x55,
+ 0x81, 0x27, 0xaa, 0xa2, 0x78, 0x88, 0x86, 0x72,
+ 0x66, 0x13, 0x77, 0x73, 0x11, 0x66, 0x61, 0x76,
+ 0x00, 0x50, 0x84, 0xf4, 0xff, 0x4f, 0xf4, 0xff,
+ 0xff, 0x4f, 0x44, 0xff, 0x4f, 0x42, 0x40, 0x9b,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb9, 0x68, 0x55,
+ 0x58, 0x12, 0x3a, 0xaa, 0x23, 0x88, 0x88, 0xa7,
+ 0x66, 0xa7, 0x77, 0x7a, 0x16, 0x66, 0x1a, 0x15,
+ 0x05, 0x00, 0x4f, 0xf4, 0xff, 0x4f, 0xf4, 0xff,
+ 0xff, 0x4f, 0x44, 0xff, 0x4f, 0x44, 0x24, 0x9b,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xb9, 0x26, 0x55,
+ 0x55, 0x81, 0x23, 0xaa, 0x32, 0x18, 0x88, 0x6a,
+ 0x61, 0x37, 0x77, 0x31, 0x66, 0x66, 0x17, 0x60,
+ 0x05, 0x08, 0x4f, 0xf4, 0xff, 0x4f, 0xf4, 0xff,
+ 0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x4e, 0x99,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x99, 0xa2, 0x65,
+ 0x55, 0x58, 0xa2, 0x7a, 0xa2, 0x26, 0x88, 0x61,
+ 0x61, 0x32, 0x27, 0xa1, 0x66, 0x61, 0x31, 0x60,
+ 0x00, 0x04, 0x4f, 0xf4, 0xff, 0x44, 0x44, 0xff,
+ 0xff, 0x4f, 0x44, 0xff, 0x4f, 0x44, 0xf4, 0x99,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x9b, 0xaa, 0x26,
+ 0x55, 0x55, 0x87, 0x27, 0x33, 0x27, 0x68, 0x61,
+ 0x1a, 0x72, 0x27, 0xa6, 0x66, 0x6a, 0x71, 0x00,
+ 0x80, 0x84, 0xff, 0xf4, 0xff, 0x44, 0x44, 0xff,
+ 0xff, 0x4f, 0x44, 0xff, 0x4f, 0x44, 0xf4, 0x99,
+ 0x9b, 0x9b, 0x99, 0xb9, 0xb9, 0x99, 0xaa, 0xa2,
+ 0x85, 0x55, 0x56, 0x22, 0x27, 0x22, 0x36, 0x66,
+ 0x13, 0x22, 0x23, 0x16, 0x86, 0x63, 0x73, 0x00,
+ 0x00, 0x44, 0xf4, 0xf4, 0xff, 0x44, 0x44, 0xff,
+ 0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x4f, 0x99,
+ 0x9b, 0x99, 0x99, 0x99, 0xb9, 0x99, 0xaa, 0xaa,
+ 0x28, 0x55, 0x58, 0x12, 0x22, 0x22, 0x21, 0x11,
+ 0xa3, 0x27, 0x7a, 0x66, 0x86, 0x17, 0x75, 0x05,
+ 0x05, 0xff, 0xf4, 0xf4, 0xff, 0x44, 0x44, 0xff,
+ 0xff, 0x4f, 0x44, 0x4f, 0x4f, 0x44, 0x4f, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x3a, 0xaa,
+ 0xa2, 0x85, 0x58, 0x67, 0x72, 0x22, 0x27, 0xa1,
+ 0x37, 0x27, 0x7a, 0x68, 0x86, 0xa2, 0x70, 0x00,
+ 0x02, 0xff, 0xf4, 0xf4, 0xff, 0x44, 0x44, 0x4f,
+ 0xff, 0x4f, 0x44, 0xf4, 0xf4, 0xf4, 0xf4, 0x99,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x23, 0xaa,
+ 0xa7, 0x78, 0x88, 0x81, 0x77, 0x22, 0x27, 0x3a,
+ 0x72, 0x73, 0x71, 0x68, 0x66, 0x32, 0x50, 0x00,
+ 0x04, 0x4f, 0xf4, 0xf4, 0xff, 0x44, 0x44, 0x4f,
+ 0xff, 0x4f, 0x44, 0xf4, 0xf4, 0xf4, 0x44, 0x95,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x55, 0x12, 0x3a,
+ 0xaa, 0x21, 0x88, 0x81, 0x77, 0x27, 0x73, 0x73,
+ 0x72, 0x33, 0x36, 0x86, 0x61, 0x72, 0x00, 0x00,
+ 0x04, 0x44, 0xf4, 0xf4, 0xf4, 0x44, 0x44, 0x4f,
+ 0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x44, 0x55,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x8a, 0x27,
+ 0xaa, 0x77, 0x68, 0x61, 0x23, 0x71, 0x11, 0x3a,
+ 0x27, 0xa3, 0x36, 0x86, 0x61, 0x20, 0x00, 0x00,
+ 0x04, 0xf4, 0xf4, 0xf4, 0xf4, 0x44, 0x44, 0x4f,
+ 0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x41, 0x59,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x95, 0x58, 0x77,
+ 0x27, 0x32, 0x36, 0x63, 0x23, 0x71, 0x66, 0x11,
+ 0x27, 0x13, 0xa6, 0x86, 0x6a, 0x20, 0x00, 0x50,
+ 0x04, 0x4f, 0x4f, 0x4f, 0x4f, 0x44, 0x44, 0x4f,
+ 0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x41, 0x99,
+ 0x9b, 0xbb, 0xbb, 0xbb, 0xb9, 0x99, 0x68, 0x13,
+ 0x32, 0x22, 0x73, 0xa7, 0x2a, 0x31, 0x88, 0x66,
+ 0x7a, 0x13, 0x18, 0x66, 0x63, 0x20, 0x00, 0x06,
+ 0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x44, 0x44, 0x4f,
+ 0xff, 0x4f, 0x44, 0xff, 0x4f, 0x4f, 0x49, 0x95,
+ 0xa9, 0xa9, 0x99, 0x97, 0x92, 0x99, 0x65, 0x6a,
+ 0x17, 0x22, 0x23, 0x72, 0x27, 0xaa, 0x88, 0x88,
+ 0xa1, 0x17, 0x68, 0x66, 0x67, 0x70, 0x00, 0x05,
+ 0x0f, 0x4f, 0x4f, 0x4f, 0x4f, 0x44, 0x44, 0x4f,
+ 0xff, 0x4f, 0x44, 0xff, 0xf4, 0xf4, 0x49, 0x9c,
+ 0x2e, 0xee, 0xee, 0xee, 0xee, 0xa9, 0x65, 0x8a,
+ 0x1a, 0xaa, 0x37, 0x72, 0x27, 0x37, 0x88, 0x88,
+ 0x11, 0x17, 0x68, 0x66, 0x67, 0x10, 0x9d, 0xd0,
+ 0x84, 0x44, 0xff, 0x4f, 0x4f, 0x44, 0xf4, 0x4f,
+ 0xff, 0x4f, 0x44, 0xff, 0xf4, 0xf4, 0x4f, 0x69,
+ 0xcc, 0xee, 0xee, 0xee, 0xec, 0x99, 0x88, 0x63,
+ 0x61, 0x68, 0x61, 0x72, 0x22, 0x7a, 0x68, 0x88,
+ 0x11, 0x17, 0x88, 0x66, 0x12, 0x1b, 0xdd, 0xdd,
+ 0x02, 0x44, 0x4f, 0x4f, 0x4f, 0x44, 0x44, 0x4f,
+ 0xff, 0x4f, 0x44, 0xff, 0xff, 0x4f, 0x4c, 0xc5,
+ 0x0c, 0xc1, 0x11, 0x1c, 0xc0, 0x26, 0x66, 0x17,
+ 0x66, 0x88, 0x88, 0x12, 0x22, 0x23, 0xa8, 0x88,
+ 0x11, 0x13, 0x88, 0x66, 0x17, 0xbb, 0xdd, 0xdd,
+ 0xd0, 0x8f, 0xff, 0xf4, 0xf4, 0x44, 0xf4, 0x4f,
+ 0xff, 0x4f, 0x44, 0xf4, 0x4f, 0x44, 0xdd, 0xdd,
+ 0x00, 0x00, 0x00, 0x05, 0x9d, 0x21, 0x66, 0x27,
+ 0xa6, 0x65, 0x58, 0x67, 0x22, 0x27, 0x28, 0x88,
+ 0x11, 0xaa, 0x86, 0x68, 0x1a, 0xbb, 0xdd, 0xdd,
+ 0xdb, 0x05, 0xf4, 0xf4, 0xf4, 0xf4, 0x44, 0x4f,
+ 0xff, 0x4f, 0x44, 0xf4, 0xf4, 0xf4, 0xdd, 0xdb,
+ 0x00, 0x00, 0x00, 0x00, 0xdd, 0xda, 0x66, 0x22,
+ 0x71, 0x15, 0x55, 0x81, 0x22, 0x22, 0x76, 0x88,
+ 0x11, 0x31, 0x88, 0x88, 0xab, 0xbd, 0xdd, 0xdd,
+ 0xdd, 0x00, 0x04, 0x44, 0xff, 0xff, 0x4f, 0x4f,
+ 0xff, 0x4f, 0x44, 0xf4, 0xf4, 0x44, 0xdd, 0xdb,
+ 0x00, 0x00, 0x00, 0x0b, 0xdd, 0xda, 0x11, 0x22,
+ 0x23, 0x68, 0x55, 0x86, 0x22, 0x22, 0x7a, 0x88,
+ 0x1a, 0x71, 0x88, 0x89, 0xbb, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xd0, 0x00, 0x4f, 0x44, 0xff, 0x4f, 0x4f,
+ 0xff, 0x4f, 0x44, 0xf4, 0xff, 0xe2, 0xdd, 0xdb,
+ 0x90, 0x00, 0x05, 0xbd, 0xdd, 0xb8, 0x63, 0x22,
+ 0x27, 0xa6, 0x55, 0x88, 0x77, 0x22, 0x22, 0x88,
+ 0x1a, 0x28, 0xbd, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdb, 0x00, 0x07, 0x44, 0x4f, 0x4f, 0x4f,
+ 0xff, 0x4f, 0x44, 0x4f, 0x4f, 0x22, 0xdd, 0xdb,
+ 0xbb, 0x9b, 0xbb, 0xbd, 0xdd, 0xd5, 0x86, 0x22,
+ 0x22, 0x77, 0x85, 0x88, 0x17, 0x22, 0x22, 0x88,
+ 0xaa, 0x2b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0x00, 0x00, 0x54, 0x4f, 0x4f, 0x4f,
+ 0xff, 0x4f, 0x44, 0xf4, 0x44, 0x22, 0xbd, 0xdd,
+ 0xbb, 0xbb, 0xbb, 0xdd, 0xdd, 0xdd, 0x88, 0x72,
+ 0x27, 0x22, 0x88, 0x88, 0x67, 0x72, 0x22, 0x18,
+ 0x33, 0x2d, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xd0, 0x00, 0x05, 0x4f, 0x4f, 0x4f,
+ 0xff, 0x4f, 0x44, 0x44, 0x4f, 0x22, 0xbd, 0xdd,
+ 0xdb, 0xbb, 0xdd, 0xdd, 0xdd, 0xdd, 0x88, 0x17,
+ 0x27, 0x72, 0x68, 0x88, 0x87, 0x32, 0x22, 0x36,
+ 0x37, 0x2d, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xd5, 0x00, 0x00, 0x4f, 0x4f, 0x4f,
+ 0xff, 0xf4, 0xf4, 0xf4, 0xf4, 0x22, 0xbb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd8, 0x67,
+ 0x72, 0x77, 0x38, 0x88, 0x83, 0x37, 0x22, 0x26,
+ 0x72, 0x2b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0x00, 0x00, 0x4f, 0x4f, 0x4f,
+ 0xff, 0xf4, 0xf4, 0xf4, 0x44, 0x25, 0xbb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd3,
+ 0x32, 0x73, 0x76, 0x88, 0x81, 0x33, 0x22, 0x2a,
+ 0x22, 0x2b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xb0, 0x54, 0x4f, 0x4f, 0x4f,
+ 0xff, 0xf4, 0xf4, 0xff, 0x44, 0x00, 0xbb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xa7, 0x73, 0x26, 0x88, 0x86, 0x7a, 0x72, 0x27,
+ 0x22, 0x2b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdb, 0x44, 0xff, 0x4f, 0x4f,
+ 0xff, 0xf4, 0xf4, 0x44, 0x40, 0x05, 0xbb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0x13, 0x23, 0x21, 0x68, 0x86, 0x17, 0x72, 0x22,
+ 0x22, 0x2b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdb, 0x44, 0x4f, 0x4f, 0x4f,
+ 0xff, 0xff, 0x44, 0x42, 0x00, 0x05, 0xbd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0x87, 0x27, 0x27, 0x16, 0x66, 0x67, 0x22, 0x22,
+ 0x72, 0x7b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0x94, 0x44, 0x44, 0x44,
+ 0x44, 0x44, 0x44, 0x00, 0x00, 0x05, 0xbb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xb8,
+ 0x86, 0x22, 0x22, 0x7a, 0x68, 0x81, 0x22, 0x22,
+ 0x37, 0x7b, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdb, 0xb5, 0x44, 0x44, 0x44,
+ 0x44, 0x47, 0x00, 0x00, 0x00, 0x05, 0xbd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xd8, 0x68,
+ 0x58, 0x72, 0x22, 0x27, 0x18, 0x86, 0x72, 0x22,
+ 0x1a, 0xbb, 0xbd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdb, 0xb5, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xbb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xb9, 0x18, 0x85,
+ 0x58, 0x12, 0x22, 0x36, 0x18, 0x88, 0x32, 0x22,
+ 0x61, 0x3b, 0xbb, 0xbb, 0xbd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdb, 0xb9, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xbb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xb9, 0x7a, 0x68, 0x85,
+ 0x88, 0x62, 0x27, 0x16, 0x18, 0x88, 0x12, 0x27,
+ 0x86, 0x18, 0x9b, 0xbb, 0xbb, 0xbb, 0xbb, 0xbd,
+ 0xdd, 0xdd, 0xdd, 0xbb, 0xb5, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xbb, 0xbd,
+ 0xdd, 0xdd, 0xdb, 0xbb, 0x87, 0x31, 0x68, 0x65,
+ 0x88, 0x82, 0x23, 0x16, 0x18, 0x88, 0x12, 0x23,
+ 0x88, 0x67, 0x27, 0xa8, 0x9b, 0xbb, 0xbb, 0xbb,
+ 0xbd, 0xdd, 0xbb, 0xbb, 0x95, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x9b, 0xbb,
+ 0xbb, 0xbb, 0xbb, 0x96, 0x87, 0x16, 0x68, 0x18,
+ 0x88, 0x62, 0x31, 0x66, 0x18, 0x88, 0x62, 0x73,
+ 0x88, 0x63, 0x27, 0x33, 0x65, 0x55, 0x99, 0x9b,
+ 0xbb, 0xbb, 0xbb, 0x99, 0x55, 0x0a, 0xa1, 0x86,
+ 0x81, 0x68, 0x88, 0x55, 0x58, 0x85, 0x9b, 0xbb,
+ 0xbb, 0xbb, 0x95, 0x88, 0x83, 0x66, 0x66, 0x18,
+ 0x66, 0x82, 0xa1, 0x66, 0x18, 0x88, 0x62, 0x33,
+ 0x88, 0x81, 0x27, 0x7a, 0x18, 0x58, 0x86, 0x85,
+ 0x99, 0x99, 0x99, 0x95, 0x53, 0x2a, 0xaa, 0x88,
+ 0x67, 0x31, 0x68, 0x55, 0x58, 0x85, 0x59, 0xbb,
+ 0xbb, 0xb9, 0x58, 0x68, 0x83, 0x66, 0x61, 0x16,
+ 0x66, 0x62, 0x16, 0x66, 0x68, 0x88, 0x62, 0xaa,
+ 0x88, 0x86, 0x27, 0x77, 0x78, 0x55, 0x88, 0x22,
+ 0x25, 0x55, 0x95, 0x55, 0x6a, 0xa2, 0x2a, 0x88,
+ 0x62, 0x27, 0x37, 0x38, 0x88, 0x87, 0x55, 0x59,
+ 0x95, 0x58, 0x16, 0x88, 0x8a, 0x66, 0x63, 0x68,
+ 0x86, 0x67, 0x66, 0x66, 0x68, 0x88, 0x12, 0x11,
+ 0x88, 0x88, 0x72, 0x77, 0x78, 0x85, 0x58, 0x17,
+ 0x23, 0x32, 0x55, 0x55, 0x81, 0x13, 0x73, 0x66,
+ 0x62, 0x7a, 0xaa, 0x38, 0x88, 0x58, 0x27, 0x55,
+ 0x58, 0x32, 0x38, 0x88, 0x81, 0x66, 0xa2, 0x88,
+ 0x86, 0x61, 0x66, 0x61, 0x66, 0x68, 0x13, 0x11,
+ 0x88, 0x88, 0x12, 0x22, 0x71, 0x85, 0x58, 0x62,
+ 0x23, 0xa2, 0x68, 0x88, 0x81, 0x66, 0x88, 0x88,
+ 0x63, 0x2a, 0xaa, 0x28, 0x88, 0x55, 0x86, 0x61,
+ 0x66, 0x66, 0x68, 0x88, 0x66, 0x66, 0x77, 0x88,
+ 0x68, 0x16, 0x66, 0x62, 0x66, 0x68, 0xa1, 0x61,
+ 0x88, 0x88, 0x62, 0x22, 0x22, 0x85, 0x55, 0x83,
+ 0x72, 0x37, 0xa8, 0x88, 0x61, 0x66, 0x85, 0x55,
+ 0x86, 0x23, 0xaa, 0x71, 0x88, 0x85, 0x88, 0x66,
+ 0x88, 0x86, 0x88, 0x88, 0x16, 0x61, 0x21, 0x88,
+ 0x66, 0xa6, 0x86, 0x17, 0x66, 0x66, 0x31, 0x61,
+ 0x88, 0x88, 0x87, 0x72, 0x22, 0x68, 0x55, 0x86,
+ 0x77, 0x77, 0x36, 0x88, 0x13, 0x68, 0x85, 0x55,
+ 0x58, 0x12, 0x73, 0x72, 0x76, 0x88, 0x88, 0x68,
+ 0x88, 0x88, 0x88, 0x66, 0x36, 0x63, 0x26, 0x86,
+ 0x86, 0x36, 0x86, 0x11, 0x66, 0x66, 0x76, 0x61,
+ 0x88, 0x88, 0x81, 0x22, 0x22, 0x38, 0x85, 0x58,
+ 0x37, 0x22, 0x21, 0x68, 0xa2, 0x31, 0x68, 0x55,
+ 0x55, 0x81, 0x22, 0x22, 0xa8, 0x88, 0x88, 0x68,
+ 0x86, 0x88, 0x68, 0x81, 0x36, 0x17, 0x21, 0x68,
+ 0x86, 0x16, 0x66, 0x26, 0x66, 0x61, 0x36, 0x66,
+ 0x68, 0x88, 0x86, 0x27, 0x22, 0x28, 0x88, 0x88,
+ 0x17, 0x72, 0x2a, 0x66, 0xa2, 0x22, 0x36, 0x55,
+ 0x55, 0x58, 0x37, 0x3a, 0x16, 0x66, 0x66, 0x66,
+ 0x66, 0x18, 0x88, 0x67, 0x16, 0x12, 0x71, 0x68,
+ 0x81, 0x68, 0x61, 0x76, 0x66, 0x6a, 0x16, 0x66,
+ 0x88, 0x88, 0x86, 0x77, 0x22, 0x26, 0x88, 0x88,
+ 0x13, 0x37, 0x71, 0x66, 0xa2, 0x33, 0x2a, 0x85,
+ 0x55, 0x55, 0x17, 0x73, 0x16, 0x66, 0x66, 0x68,
+ 0x63, 0x88, 0x88, 0xa2, 0x66, 0xa2, 0xa6, 0x88,
+ 0x61, 0x68, 0x6a, 0x76, 0x66, 0x6a, 0x66, 0x6a
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/list.h b/pfinet/linux-src/include/linux/list.h
new file mode 100644
index 00000000..e77559a6
--- /dev/null
+++ b/pfinet/linux-src/include/linux/list.h
@@ -0,0 +1,99 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#ifdef __KERNEL__
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD(name) \
+ struct list_head name = { &name, &name }
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_add(struct list_head * new,
+ struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/*
+ * Insert a new entry after the specified head..
+ */
+static __inline__ void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+static __inline__ void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+static __inline__ int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+/*
+ * Splice in "list" into "head"
+ */
+static __inline__ void list_splice(struct list_head *list, struct list_head *head)
+{
+ struct list_head *first = list->next;
+
+ if (first != list) {
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+ }
+}
+
+#define list_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/lists.h b/pfinet/linux-src/include/linux/lists.h
new file mode 100644
index 00000000..6a2240a2
--- /dev/null
+++ b/pfinet/linux-src/include/linux/lists.h
@@ -0,0 +1,62 @@
+/*
+ * lists.h: Simple list macros for Linux
+ */
+
+#define DLNODE(ptype) \
+ struct { \
+ ptype * dl_prev; \
+ ptype * dl_next; \
+ }
+
+#define DNODE_SINGLE(node) {(node),(node)}
+#define DNODE_NULL {0,0}
+
+#define DLIST_INIT(listnam) \
+ (listnam).dl_prev = &(listnam); \
+ (listnam).dl_next = &(listnam);
+
+#define DLIST_NEXT(listnam) listnam.dl_next
+#define DLIST_PREV(listnam) listnam.dl_prev
+
+#define DLIST_INSERT_AFTER(node, new, listnam) do { \
+ (new)->listnam.dl_prev = (node); \
+ (new)->listnam.dl_next = (node)->listnam.dl_next; \
+ (node)->listnam.dl_next->listnam.dl_prev = (new); \
+ (node)->listnam.dl_next = (new); \
+ } while (0)
+
+#define DLIST_INSERT_BEFORE(node, new, listnam) do { \
+ (new)->listnam.dl_next = (node); \
+ (new)->listnam.dl_prev = (node)->listnam.dl_prev; \
+ (node)->listnam.dl_prev->listnam.dl_next = (new); \
+ (node)->listnam.dl_prev = (new); \
+ } while (0)
+
+#define DLIST_DELETE(node, listnam) do { \
+ node->listnam.dl_prev->listnam.dl_next = \
+ node->listnam.dl_next; \
+ node->listnam.dl_next->listnam.dl_prev = \
+ node->listnam.dl_prev; \
+ } while (0)
+
+/*
+ * queue-style operations, which have a head and tail
+ */
+
+#define QUEUE_INIT(head, listnam, ptype) \
+ (head)->listnam.dl_prev = (head)->listnam.dl_next = (ptype)(head);
+
+#define QUEUE_FIRST(head, listnam) (head)->DLIST_NEXT(listnam)
+#define QUEUE_LAST(head, listnam) (head)->DLIST_PREV(listnam)
+#define QUEUE_EMPTY(head, listnam) \
+ ((QUEUE_FIRST(head, listnam) == QUEUE_LAST(head, listnam)) && \
+ ((u_long)QUEUE_FIRST(head, listnam) == (u_long)head))
+
+#define QUEUE_ENTER(head, new, listnam, ptype) do { \
+ (new)->listnam.dl_prev = (ptype)(head); \
+ (new)->listnam.dl_next = (head)->listnam.dl_next; \
+ (head)->listnam.dl_next->listnam.dl_prev = (new); \
+ (head)->listnam.dl_next = (new); \
+ } while (0)
+
+#define QUEUE_REMOVE(head, node, listnam) DLIST_DELETE(node, listnam)
diff --git a/pfinet/linux-src/include/linux/locks.h b/pfinet/linux-src/include/linux/locks.h
new file mode 100644
index 00000000..07ac4fa4
--- /dev/null
+++ b/pfinet/linux-src/include/linux/locks.h
@@ -0,0 +1,61 @@
+#ifndef _LINUX_LOCKS_H
+#define _LINUX_LOCKS_H
+
+#ifndef _LINUX_MM_H
+#include <linux/mm.h>
+#endif
+#ifndef _LINUX_PAGEMAP_H
+#include <linux/pagemap.h>
+#endif
+
+/*
+ * Buffer cache locking - note that interrupts may only unlock, not
+ * lock buffers.
+ */
+extern void __wait_on_buffer(struct buffer_head *);
+
+extern inline void wait_on_buffer(struct buffer_head * bh)
+{
+ if (test_bit(BH_Lock, &bh->b_state))
+ __wait_on_buffer(bh);
+}
+
+extern inline void lock_buffer(struct buffer_head * bh)
+{
+ while (test_and_set_bit(BH_Lock, &bh->b_state))
+ __wait_on_buffer(bh);
+}
+
+extern inline void unlock_buffer(struct buffer_head *bh)
+{
+ clear_bit(BH_Lock, &bh->b_state);
+ wake_up(&bh->b_wait);
+}
+
+/*
+ * super-block locking. Again, interrupts may only unlock
+ * a super-block (although even this isn't done right now.
+ * nfs may need it).
+ */
+extern void __wait_on_super(struct super_block *);
+
+extern inline void wait_on_super(struct super_block * sb)
+{
+ if (sb->s_lock)
+ __wait_on_super(sb);
+}
+
+extern inline void lock_super(struct super_block * sb)
+{
+ if (sb->s_lock)
+ __wait_on_super(sb);
+ sb->s_lock = 1;
+}
+
+extern inline void unlock_super(struct super_block * sb)
+{
+ sb->s_lock = 0;
+ wake_up(&sb->s_wait);
+}
+
+#endif /* _LINUX_LOCKS_H */
diff --git a/pfinet/linux-src/include/linux/loop.h b/pfinet/linux-src/include/linux/loop.h
new file mode 100644
index 00000000..43b3bd69
--- /dev/null
+++ b/pfinet/linux-src/include/linux/loop.h
@@ -0,0 +1,130 @@
+#ifndef _LINUX_LOOP_H
+#define _LINUX_LOOP_H
+
+#include <linux/kdev_t.h>
+
+/*
+ * include/linux/loop.h
+ *
+ * Written by Theodore Ts'o, 3/29/93.
+ *
+ * Copyright 1993 by Theodore Ts'o. Redistribution of this file is
+ * permitted under the GNU Public License.
+ */
+
+#define LO_NAME_SIZE 64
+#define LO_KEY_SIZE 32
+
+#ifdef __KERNEL__
+
+struct loop_device {
+ int lo_number;
+ struct dentry *lo_dentry;
+ int lo_refcnt;
+ kdev_t lo_device;
+ int lo_offset;
+ int lo_encrypt_type;
+ int lo_encrypt_key_size;
+ int lo_flags;
+ int (*transfer)(struct loop_device *, int cmd,
+ char *raw_buf, char *loop_buf, int size,
+ int real_block);
+ char lo_name[LO_NAME_SIZE];
+ char lo_encrypt_key[LO_KEY_SIZE];
+ __u32 lo_init[2];
+ uid_t lo_key_owner; /* Who set the key */
+ int (*ioctl)(struct loop_device *, int cmd,
+ unsigned long arg);
+
+ struct file * lo_backing_file;
+ void *key_data;
+ char key_reserved[48]; /* for use by the filter modules */
+};
+
+typedef int (* transfer_proc_t)(struct loop_device *, int cmd,
+ char *raw_buf, char *loop_buf, int size,
+ int real_block);
+
+#endif /* __KERNEL__ */
+
+/*
+ * Loop flags
+ */
+#define LO_FLAGS_DO_BMAP 0x00000001
+#define LO_FLAGS_READ_ONLY 0x00000002
+
+/*
+ * Note that this structure gets the wrong offsets when directly used
+ * from a glibc program, because glibc has a 32bit dev_t.
+ * Prevent people from shooting in their own foot.
+ */
+#if __GLIBC__ >= 2 && !defined(dev_t)
+#error "Wrong dev_t in loop.h"
+#endif
+
+/*
+ * This uses kdev_t because glibc currently has no appropriate
+ * conversion version for the loop ioctls.
+ * The situation is very unpleasant
+ */
+
+struct loop_info {
+ int lo_number; /* ioctl r/o */
+ dev_t lo_device; /* ioctl r/o */
+ unsigned long lo_inode; /* ioctl r/o */
+ dev_t lo_rdevice; /* ioctl r/o */
+ int lo_offset;
+ int lo_encrypt_type;
+ int lo_encrypt_key_size; /* ioctl w/o */
+ int lo_flags; /* ioctl r/o */
+ char lo_name[LO_NAME_SIZE];
+ unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */
+ unsigned long lo_init[2];
+ char reserved[4];
+};
+
+/*
+ * Loop filter types
+ */
+
+#define LO_CRYPT_NONE 0
+#define LO_CRYPT_XOR 1
+#define LO_CRYPT_DES 2
+#define LO_CRYPT_FISH2 3 /* Brand new Twofish encryption */
+#define LO_CRYPT_BLOW 4
+#define LO_CRYPT_CAST128 5
+#define LO_CRYPT_IDEA 6
+#define LO_CRYPT_DUMMY 9
+#define LO_CRYPT_SKIPJACK 10
+#define MAX_LO_CRYPT 20
+
+#ifdef __KERNEL__
+/* Support for loadable transfer modules */
+struct loop_func_table {
+ int number; /* filter type */
+ int (*transfer)(struct loop_device *lo, int cmd,
+ char *raw_buf, char *loop_buf, int size,
+ int real_block);
+ int (*init)(struct loop_device *, struct loop_info *);
+ /* release is called from loop_unregister_transfer or clr_fd */
+ int (*release)(struct loop_device *);
+ int (*ioctl)(struct loop_device *, int cmd, unsigned long arg);
+ /* lock and unlock manage the module use counts */
+ void (*lock)(struct loop_device *);
+ void (*unlock)(struct loop_device *);
+};
+
+int loop_register_transfer(struct loop_func_table *funcs);
+int loop_unregister_transfer(int number);
+
+#endif
+/*
+ * IOCTL commands --- we will commandeer 0x4C ('L')
+ */
+
+#define LOOP_SET_FD 0x4C00
+#define LOOP_CLR_FD 0x4C01
+#define LOOP_SET_STATUS 0x4C02
+#define LOOP_GET_STATUS 0x4C03
+
+#endif
diff --git a/pfinet/linux-src/include/linux/lp.h b/pfinet/linux-src/include/linux/lp.h
new file mode 100644
index 00000000..139ad843
--- /dev/null
+++ b/pfinet/linux-src/include/linux/lp.h
@@ -0,0 +1,188 @@
+#ifndef _LINUX_LP_H
+#define _LINUX_LP_H
+
+/*
+ * usr/include/linux/lp.h c.1991-1992 James Wiegand
+ * many modifications copyright (C) 1992 Michael K. Johnson
+ * Interrupt support added 1993 Nigel Gamble
+ */
+
+/*
+ * Per POSIX guidelines, this module reserves the LP and lp prefixes
+ * These are the lp_table[minor].flags flags...
+ */
+#define LP_EXIST 0x0001
+#define LP_SELEC 0x0002
+#define LP_BUSY 0x0004
+#define LP_BUSY_BIT_POS 2
+#define LP_OFFL 0x0008
+#define LP_NOPA 0x0010
+#define LP_ERR 0x0020
+#define LP_ABORT 0x0040
+#define LP_CAREFUL 0x0080
+#define LP_ABORTOPEN 0x0100
+#define LP_TRUST_IRQ 0x0200
+
+/* timeout for each character. This is relative to bus cycles -- it
+ * is the count in a busy loop. THIS IS THE VALUE TO CHANGE if you
+ * have extremely slow printing, or if the machine seems to slow down
+ * a lot when you print. If you have slow printing, increase this
+ * number and recompile, and if your system gets bogged down, decrease
+ * this number. This can be changed with the tunelp(8) command as well.
+ */
+
+#define LP_INIT_CHAR 1000
+
+/* The parallel port specs apparently say that there needs to be
+ * a .5usec wait before and after the strobe.
+ */
+
+#define LP_INIT_WAIT 1
+
+/* This is the amount of time that the driver waits for the printer to
+ * catch up when the printer's buffer appears to be filled. If you
+ * want to tune this and have a fast printer (i.e. HPIIIP), decrease
+ * this number, and if you have a slow printer, increase this number.
+ * This is in hundredths of a second, the default 2 being .05 second.
+ * Or use the tunelp(8) command, which is especially nice if you want
+ * change back and forth between character and graphics printing, which
+ * are wildly different...
+ */
+
+#define LP_INIT_TIME 2
+
+/* IOCTL numbers */
+#define LPCHAR 0x0601 /* corresponds to LP_INIT_CHAR */
+#define LPTIME 0x0602 /* corresponds to LP_INIT_TIME */
+#define LPABORT 0x0604 /* call with TRUE arg to abort on error,
+ FALSE to retry. Default is retry. */
+#define LPSETIRQ 0x0605 /* call with new IRQ number,
+ or 0 for polling (no IRQ) */
+#define LPGETIRQ 0x0606 /* get the current IRQ number */
+#define LPWAIT 0x0608 /* corresponds to LP_INIT_WAIT */
+/* NOTE: LPCAREFUL is obsoleted and it' s always the default right now -arca */
+#define LPCAREFUL 0x0609 /* call with TRUE arg to require out-of-paper, off-
+ line, and error indicators good on all writes,
+ FALSE to ignore them. Default is ignore. */
+#define LPABORTOPEN 0x060a /* call with TRUE arg to abort open() on error,
+ FALSE to ignore error. Default is ignore. */
+#define LPGETSTATUS 0x060b /* return LP_S(minor) */
+#define LPRESET 0x060c /* reset printer */
+#ifdef LP_STATS
+#define LPGETSTATS 0x060d /* get statistics (struct lp_stats) */
+#endif
+#define LPGETFLAGS 0x060e /* get status flags */
+#define LPTRUSTIRQ 0x060f /* set/unset the LP_TRUST_IRQ flag */
+
+/* timeout for printk'ing a timeout, in jiffies (100ths of a second).
+ This is also used for re-checking error conditions if LP_ABORT is
+ not set. This is the default behavior. */
+
+#define LP_TIMEOUT_INTERRUPT (60 * HZ)
+#define LP_TIMEOUT_POLLED (10 * HZ)
+
+#ifdef __KERNEL__
+
+/* Magic numbers for defining port-device mappings */
+#define LP_PARPORT_UNSPEC -4
+#define LP_PARPORT_AUTO -3
+#define LP_PARPORT_OFF -2
+#define LP_PARPORT_NONE -1
+
+#define LP_F(minor) lp_table[(minor)].flags /* flags for busy, etc. */
+#define LP_CHAR(minor) lp_table[(minor)].chars /* busy timeout */
+#define LP_TIME(minor) lp_table[(minor)].time /* wait time */
+#define LP_WAIT(minor) lp_table[(minor)].wait /* strobe wait */
+#define LP_IRQ(minor) lp_table[(minor)].dev->port->irq /* interrupt # */
+ /* PARPORT_IRQ_NONE means polled */
+#ifdef LP_STATS
+#define LP_STAT(minor) lp_table[(minor)].stats /* statistics area */
+#endif
+#define LP_BUFFER_SIZE 256
+
+#define LP_BASE(x) lp_table[(x)].dev->port->base
+
+#ifdef LP_STATS
+struct lp_stats {
+ unsigned long chars;
+ unsigned long sleeps;
+ unsigned int maxrun;
+ unsigned int maxwait;
+ unsigned int meanwait;
+ unsigned int mdev;
+};
+#endif
+
+struct lp_struct {
+ struct pardevice *dev;
+ unsigned long flags;
+ unsigned int chars;
+ unsigned int time;
+ unsigned int wait;
+ char *lp_buffer;
+#ifdef LP_STATS
+ unsigned int lastcall;
+ unsigned int runchars;
+ struct lp_stats stats;
+#endif
+ struct wait_queue *wait_q;
+ unsigned int last_error;
+ volatile unsigned int irq_detected:1;
+ volatile unsigned int irq_missed:1;
+};
+
+/*
+ * The following constants describe the various signals of the printer port
+ * hardware. Note that the hardware inverts some signals and that some
+ * signals are active low. An example is LP_STROBE, which must be programmed
+ * with 1 for being active and 0 for being inactive, because the strobe signal
+ * gets inverted, but it is also active low.
+ */
+
+/*
+ * bit defines for 8255 status port
+ * base + 1
+ * accessed with LP_S(minor), which gets the byte...
+ */
+#define LP_PBUSY 0x80 /* inverted input, active high */
+#define LP_PACK 0x40 /* unchanged input, active low */
+#define LP_POUTPA 0x20 /* unchanged input, active high */
+#define LP_PSELECD 0x10 /* unchanged input, active high */
+#define LP_PERRORP 0x08 /* unchanged input, active low */
+
+/*
+ * defines for 8255 control port
+ * base + 2
+ * accessed with LP_C(minor)
+ */
+#define LP_PINTEN 0x10 /* high to read data in or-ed with data out */
+#define LP_PSELECP 0x08 /* inverted output, active low */
+#define LP_PINITP 0x04 /* unchanged output, active low */
+#define LP_PAUTOLF 0x02 /* inverted output, active low */
+#define LP_PSTROBE 0x01 /* short high output on raising edge */
+
+/*
+ * the value written to ports to test existence. PC-style ports will
+ * return the value written. AT-style ports will return 0. so why not
+ * make them the same ?
+ */
+#define LP_DUMMY 0x00
+
+/*
+ * This is the port delay time, in microseconds.
+ * It is used only in the lp_init() and lp_reset() routine.
+ */
+#define LP_DELAY 50
+
+#define LP_POLLED(minor) (lp_table[(minor)].dev->port->irq == PARPORT_IRQ_NONE)
+#define LP_PREEMPTED(minor) (lp_table[(minor)].dev->port->waithead != NULL)
+
+/*
+ * function prototypes
+ */
+
+extern int lp_init(void);
+
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/lp_intern.h b/pfinet/linux-src/include/linux/lp_intern.h
new file mode 100644
index 00000000..56ac21d7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/lp_intern.h
@@ -0,0 +1,21 @@
+#ifndef _LINUX_LP_INTERN_H_
+#define _LINUX_LP_INTERN_H_
+
+/*
+ * split in two parts by Joerg Dorchain
+ * usr/include/linux/lp.h modified for Amiga by Michael Rausch
+ * modified for Atari by Andreas Schwab
+ * bug fixed by Jes Sorensen 18/8-94:
+ * It was not possible to compile the kernel only for Atari or Amiga.
+ *
+ * linux i386 version c.1991-1992 James Wiegand
+ * many modifications copyright (C) 1992 Michael K. Johnson
+ * Interrupt support added 1993 Nigel Gamble
+ */
+
+#include <linux/types.h>
+#include <linux/lp_m68k.h>
+
+int lp_internal_init(void);
+
+#endif
diff --git a/pfinet/linux-src/include/linux/lp_m68k.h b/pfinet/linux-src/include/linux/lp_m68k.h
new file mode 100644
index 00000000..cddecc98
--- /dev/null
+++ b/pfinet/linux-src/include/linux/lp_m68k.h
@@ -0,0 +1,135 @@
+#ifndef _LINUX_LP_H
+#define _LINUX_LP_H
+
+/*
+ * split in two parts by Joerg Dorchain
+ * usr/include/linux/lp.h modified for Amiga by Michael Rausch
+ * modified for Atari by Andreas Schwab
+ * bug fixed by Jes Sorensen 18/8-94:
+ * It was not possible to compile the kernel only for Atari or Amiga.
+ *
+ * linux i386 version c.1991-1992 James Wiegand
+ * many modifications copyright (C) 1992 Michael K. Johnson
+ * Interrupt support added 1993 Nigel Gamble
+ */
+
+/*
+ * many many printers are we going to support? currently, this is the
+ * hardcoded limit
+ */
+#define MAX_LP 5
+
+/*
+ * Per POSIX guidelines, this module reserves the LP and lp prefixes
+ * These are the lp_table[minor].flags flags...
+ */
+#define LP_EXIST 0x0001
+#define LP_BUSY 0x0004
+#define LP_ABORT 0x0040
+#define LP_CAREFUL 0x0080
+#define LP_ABORTOPEN 0x0100
+
+/* timeout for each character. This is relative to bus cycles -- it
+ * is the count in a busy loop. THIS IS THE VALUE TO CHANGE if you
+ * have extremely slow printing, or if the machine seems to slow down
+ * a lot when you print. If you have slow printing, increase this
+ * number and recompile, and if your system gets bogged down, decrease
+ * this number. This can be changed with the tunelp(8) command as well.
+ */
+
+#define LP_INIT_CHAR 1000
+
+/* The parallel port specs apparently say that there needs to be
+ * a .5usec wait before and after the strobe. Since there are wildly
+ * different computers running linux, I can't come up with a perfect
+ * value, but since it worked well on most printers before without,
+ * I'll initialize it to 0.
+ */
+
+#define LP_INIT_WAIT 0
+
+/* This is the amount of time that the driver waits for the printer to
+ * catch up when the printer's buffer appears to be filled. If you
+ * want to tune this and have a fast printer (i.e. HPIIIP), decrease
+ * this number, and if you have a slow printer, increase this number.
+ * This is in hundredths of a second, the default 2 being .05 second.
+ * Or use the tunelp(8) command, which is especially nice if you want
+ * change back and forth between character and graphics printing, which
+ * are wildly different...
+ */
+
+#define LP_INIT_TIME 40
+
+/* IOCTL numbers */
+#define LPCHAR 0x0601 /* corresponds to LP_INIT_CHAR */
+#define LPTIME 0x0602 /* corresponds to LP_INIT_TIME */
+#define LPABORT 0x0604 /* call with TRUE arg to abort on error,
+ FALSE to retry. Default is retry. */
+#define LPSETIRQ 0x0605 /* call with new IRQ number,
+ or 0 for polling (no IRQ) */
+#define LPGETIRQ 0x0606 /* get the current IRQ number */
+#define LPWAIT 0x0608 /* corresponds to LP_INIT_WAIT */
+#define LPCAREFUL 0x0609 /* call with TRUE arg to require out-of-paper, off-
+ line, and error indicators good on all writes,
+ FALSE to ignore them. Default is ignore. */
+#define LPABORTOPEN 0x060a /* call with TRUE arg to abort open() on error,
+ FALSE to ignore error. Default is ignore. */
+#define LPGETSTATUS 0x060b /* return LP_S(minor) */
+#define LPRESET 0x060c /* reset printer */
+
+/* timeout for printk'ing a timeout, in jiffies (100ths of a second).
+ This is also used for re-checking error conditions if LP_ABORT is
+ not set. This is the default behavior. */
+
+#define LP_TIMEOUT_INTERRUPT (60 * HZ)
+#define LP_TIMEOUT_POLLED (10 * HZ)
+
+
+#define LP_BUFFER_SIZE 1024 /*256*/
+
+enum lp_type {
+LP_UNKNOWN = 0,
+LP_AMIGA = 1,
+LP_ATARI = 2,
+LP_MFC = 3,
+LP_IOEXT = 4,
+LP_MVME167 = 5,
+LP_BVME6000 = 6
+};
+
+/*
+ * warning: this structure is in kernel space and has to fit in one page,
+ * i.e. must not be larger than 4k
+ */
+struct lp_struct {
+ char *name;
+ unsigned int irq;
+ void (*lp_out)(int,int); /*output char function*/
+ int (*lp_is_busy)(int);
+ int (*lp_has_pout)(int);
+ int (*lp_is_online)(int);
+ int (*lp_dummy)(int);
+ int (*lp_ioctl)(int, unsigned int, unsigned long);
+ int (*lp_open)(int); /* for module use counter */
+ void (*lp_release)(int); /* for module use counter */
+ int flags; /*for BUSY... */
+ unsigned int chars; /*busy timeout */
+ unsigned int time; /*wait time */
+ unsigned int wait;
+ struct wait_queue *lp_wait_q; /*strobe wait */
+ void *base; /* hardware drivers internal use*/
+ enum lp_type type;
+ char lp_buffer[LP_BUFFER_SIZE];
+ int do_print;
+ unsigned long copy_size,bytes_written;
+};
+
+extern struct lp_struct *lp_table[MAX_LP];
+extern unsigned int lp_irq;
+
+void lp_interrupt(int dev);
+int lp_m68k_init(void);
+int register_parallel(struct lp_struct *, int);
+void unregister_parallel(int);
+
+#endif
diff --git a/pfinet/linux-src/include/linux/lp_mfc.h b/pfinet/linux-src/include/linux/lp_mfc.h
new file mode 100644
index 00000000..02f735e1
--- /dev/null
+++ b/pfinet/linux-src/include/linux/lp_mfc.h
@@ -0,0 +1,13 @@
+#ifndef _LINUX_LP_MFC_H_
+#define _LINUX_LP_MFC_H_
+
+/*
+ * created 6.11.95 Joerg Dorchain
+ */
+
+#include <linux/types.h>
+#include <linux/lp_m68k.h>
+
+int lp_mfc_init(void);
+
+#endif
diff --git a/pfinet/linux-src/include/linux/major.h b/pfinet/linux-src/include/linux/major.h
new file mode 100644
index 00000000..2db0a0af
--- /dev/null
+++ b/pfinet/linux-src/include/linux/major.h
@@ -0,0 +1,135 @@
+#ifndef _LINUX_MAJOR_H
+#define _LINUX_MAJOR_H
+
+/*
+ * This file has definitions for major device numbers.
+ * For the device number assignments, see Documentation/devices.txt.
+ */
+
+/* limits */
+
+/*
+ * Important: Don't change this to 256. Major number 255 is and must be
+ * reserved for future expansion into a larger dev_t space.
+ */
+#define MAX_CHRDEV 255
+#define MAX_BLKDEV 255
+
+#define UNNAMED_MAJOR 0
+#define MEM_MAJOR 1
+#define RAMDISK_MAJOR 1
+#define FLOPPY_MAJOR 2
+#define PTY_MASTER_MAJOR 2
+#define IDE0_MAJOR 3
+#define PTY_SLAVE_MAJOR 3
+#define HD_MAJOR IDE0_MAJOR
+#define TTY_MAJOR 4
+#define TTYAUX_MAJOR 5
+#define LP_MAJOR 6
+#define VCS_MAJOR 7
+#define LOOP_MAJOR 7
+#define SCSI_DISK0_MAJOR 8
+#define SCSI_TAPE_MAJOR 9
+#define MD_MAJOR 9
+#define MISC_MAJOR 10
+#define SCSI_CDROM_MAJOR 11
+#define QIC02_TAPE_MAJOR 12
+#define XT_DISK_MAJOR 13
+#define SOUND_MAJOR 14
+#define CDU31A_CDROM_MAJOR 15
+#define JOYSTICK_MAJOR 15
+#define GOLDSTAR_CDROM_MAJOR 16
+#define OPTICS_CDROM_MAJOR 17
+#define SANYO_CDROM_MAJOR 18
+#define CYCLADES_MAJOR 19
+#define CYCLADESAUX_MAJOR 20
+#define MITSUMI_X_CDROM_MAJOR 20
+#define MFM_ACORN_MAJOR 21 /* ARM Linux /dev/mfm */
+#define SCSI_GENERIC_MAJOR 21
+#define Z8530_MAJOR 34
+#define DIGI_MAJOR 23
+#define IDE1_MAJOR 22
+#define DIGICU_MAJOR 22
+#define MITSUMI_CDROM_MAJOR 23
+#define CDU535_CDROM_MAJOR 24
+#define STL_SERIALMAJOR 24
+#define MATSUSHITA_CDROM_MAJOR 25
+#define STL_CALLOUTMAJOR 25
+#define MATSUSHITA_CDROM2_MAJOR 26
+#define QIC117_TAPE_MAJOR 27
+#define MATSUSHITA_CDROM3_MAJOR 27
+#define MATSUSHITA_CDROM4_MAJOR 28
+#define STL_SIOMEMMAJOR 28
+#define ACSI_MAJOR 28
+#define AZTECH_CDROM_MAJOR 29
+#define GRAPHDEV_MAJOR 29 /* SparcLinux & Linux/68k /dev/fb */
+#define SHMIQ_MAJOR 85 /* Linux/MIPS, SGI /dev/shmiq */
+#define CM206_CDROM_MAJOR 32
+#define IDE2_MAJOR 33
+#define IDE3_MAJOR 34
+#define NETLINK_MAJOR 36
+#define PS2ESDI_MAJOR 36
+#define IDETAPE_MAJOR 37
+#define Z2RAM_MAJOR 37
+#define APBLOCK_MAJOR 38 /* AP1000 Block device */
+#define DDV_MAJOR 39 /* AP1000 DDV block device */
+#define NBD_MAJOR 43 /* Network block device */
+#define RISCOM8_NORMAL_MAJOR 48
+#define DAC960_MAJOR 48 /* 48..55 */
+#define RISCOM8_CALLOUT_MAJOR 49
+#define MKISS_MAJOR 55
+#define DSP56K_MAJOR 55 /* DSP56001 processor device */
+
+#define IDE4_MAJOR 56
+#define IDE5_MAJOR 57
+
+#define SCSI_DISK1_MAJOR 65
+#define SCSI_DISK2_MAJOR 66
+#define SCSI_DISK3_MAJOR 67
+#define SCSI_DISK4_MAJOR 68
+#define SCSI_DISK5_MAJOR 69
+#define SCSI_DISK6_MAJOR 70
+#define SCSI_DISK7_MAJOR 71
+
+#define LVM_BLK_MAJOR 58 /* Logical Volume Manager */
+
+#define COMPAQ_SMART2_MAJOR 72
+#define COMPAQ_SMART2_MAJOR1 73
+#define COMPAQ_SMART2_MAJOR2 74
+#define COMPAQ_SMART2_MAJOR3 75
+#define COMPAQ_SMART2_MAJOR4 76
+#define COMPAQ_SMART2_MAJOR5 77
+#define COMPAQ_SMART2_MAJOR6 78
+#define COMPAQ_SMART2_MAJOR7 79
+
+#define SPECIALIX_NORMAL_MAJOR 75
+#define SPECIALIX_CALLOUT_MAJOR 76
+
+#define DASD_MAJOR 94 /* Official assignations from Peter */
+
+#define LVM_CHAR_MAJOR 109 /* Logical Volume Manager */
+
+#define MDISK_MAJOR 95 /* Official assignations from Peter */
+
+#define AURORA_MAJOR 79
+
+#define UNIX98_PTY_MASTER_MAJOR 128
+#define UNIX98_PTY_MAJOR_COUNT 8
+#define UNIX98_PTY_SLAVE_MAJOR (UNIX98_PTY_MASTER_MAJOR+UNIX98_PTY_MAJOR_COUNT)
+
+/*
+ * Tests for SCSI devices.
+ */
+
+#define SCSI_DISK_MAJOR(M) ((M) == SCSI_DISK0_MAJOR || \
+ ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR))
+
+#define SCSI_BLK_MAJOR(M) \
+ (SCSI_DISK_MAJOR(M) \
+ || (M) == SCSI_CDROM_MAJOR)
+
+static __inline__ int scsi_blk_major(int m) {
+ return SCSI_BLK_MAJOR(m);
+}
+
+#endif
diff --git a/pfinet/linux-src/include/linux/malloc.h b/pfinet/linux-src/include/linux/malloc.h
new file mode 100644
index 00000000..f3ebf185
--- /dev/null
+++ b/pfinet/linux-src/include/linux/malloc.h
@@ -0,0 +1,5 @@
+#ifndef _LINUX_MALLOC_H
+#define _LINUX_MALLOC_H
+
+#include <linux/slab.h>
+#endif /* _LINUX_MALLOC_H */
diff --git a/pfinet/linux-src/include/linux/mc146818rtc.h b/pfinet/linux-src/include/linux/mc146818rtc.h
new file mode 100644
index 00000000..0a2efb6e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/mc146818rtc.h
@@ -0,0 +1,149 @@
+/* mc146818rtc.h - register definitions for the Real-Time-Clock / CMOS RAM
+ * Copyright Torsten Duwe <duwe@informatik.uni-erlangen.de> 1993
+ * derived from Data Sheet, Copyright Motorola 1984 (!).
+ * It was written to be part of the Linux operating system.
+ */
+/* permission is hereby granted to copy, modify and redistribute this code
+ * in terms of the GNU Library General Public License, Version 2 or later,
+ * at your option.
+ */
+
+#ifndef _MC146818RTC_H
+#define _MC146818RTC_H
+#include <asm/io.h>
+
+#ifndef RTC_PORT
+#define RTC_PORT(x) (0x70 + (x))
+#define RTC_ALWAYS_BCD 1
+#endif
+
+#define CMOS_READ(addr) ({ \
+outb_p((addr),RTC_PORT(0)); \
+inb_p(RTC_PORT(1)); \
+})
+#define CMOS_WRITE(val, addr) ({ \
+outb_p((addr),RTC_PORT(0)); \
+outb_p((val),RTC_PORT(1)); \
+})
+
+/**********************************************************************
+ * register summary
+ **********************************************************************/
+#define RTC_SECONDS 0
+#define RTC_SECONDS_ALARM 1
+#define RTC_MINUTES 2
+#define RTC_MINUTES_ALARM 3
+#define RTC_HOURS 4
+#define RTC_HOURS_ALARM 5
+/* RTC_*_alarm is always true if 2 MSBs are set */
+# define RTC_ALARM_DONT_CARE 0xC0
+
+#define RTC_DAY_OF_WEEK 6
+#define RTC_DAY_OF_MONTH 7
+#define RTC_MONTH 8
+#define RTC_YEAR 9
+
+/* control registers - Moto names
+ */
+#define RTC_REG_A 10
+#define RTC_REG_B 11
+#define RTC_REG_C 12
+#define RTC_REG_D 13
+
+/**********************************************************************
+ * register details
+ **********************************************************************/
+#define RTC_FREQ_SELECT RTC_REG_A
+
+/* update-in-progress - set to "1" 244 microsecs before RTC goes off the bus,
+ * reset after update (may take 1.984ms @ 32768Hz RefClock) is complete,
+ * totalling to a max high interval of 2.228 ms.
+ */
+# define RTC_UIP 0x80
+# define RTC_DIV_CTL 0x70
+ /* divider control: refclock values 4.194 / 1.049 MHz / 32.768 kHz */
+# define RTC_REF_CLCK_4MHZ 0x00
+# define RTC_REF_CLCK_1MHZ 0x10
+# define RTC_REF_CLCK_32KHZ 0x20
+ /* 2 values for divider stage reset, others for "testing purposes only" */
+# define RTC_DIV_RESET1 0x60
+# define RTC_DIV_RESET2 0x70
+ /* Periodic intr. / Square wave rate select. 0=none, 1=32.8kHz,... 15=2Hz */
+# define RTC_RATE_SELECT 0x0F
+
+/**********************************************************************/
+#define RTC_CONTROL RTC_REG_B
+# define RTC_SET 0x80 /* disable updates for clock setting */
+# define RTC_PIE 0x40 /* periodic interrupt enable */
+# define RTC_AIE 0x20 /* alarm interrupt enable */
+# define RTC_UIE 0x10 /* update-finished interrupt enable */
+# define RTC_SQWE 0x08 /* enable square-wave output */
+# define RTC_DM_BINARY 0x04 /* all time/date values are BCD if clear */
+# define RTC_24H 0x02 /* 24 hour mode - else hours bit 7 means pm */
+# define RTC_DST_EN 0x01 /* auto switch DST - works f. USA only */
+
+/**********************************************************************/
+#define RTC_INTR_FLAGS RTC_REG_C
+/* caution - cleared by read */
+# define RTC_IRQF 0x80 /* any of the following 3 is active */
+# define RTC_PF 0x40
+# define RTC_AF 0x20
+# define RTC_UF 0x10
+
+/**********************************************************************/
+#define RTC_VALID RTC_REG_D
+# define RTC_VRT 0x80 /* valid RAM and time */
+/**********************************************************************/
+
+/* example: !(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY)
+ * determines if the following two #defines are needed
+ */
+#ifndef BCD_TO_BIN
+#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
+#endif
+
+#ifndef BIN_TO_BCD
+#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10)
+#endif
+
+/*
+ * The struct used to pass data via the following ioctl. Similar to the
+ * struct tm in <time.h>, but it needs to be here so that the kernel
+ * source is self contained, allowing cross-compiles, etc. etc.
+ */
+
+struct rtc_time {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+ int tm_wday;
+ int tm_yday;
+ int tm_isdst;
+};
+
+/*
+ * ioctl calls that are permitted to the /dev/rtc interface, if
+ * CONFIG_RTC was enabled.
+ */
+
+#define RTC_AIE_ON _IO('p', 0x01) /* Alarm int. enable on */
+#define RTC_AIE_OFF _IO('p', 0x02) /* ... off */
+#define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on */
+#define RTC_UIE_OFF _IO('p', 0x04) /* ... off */
+#define RTC_PIE_ON _IO('p', 0x05) /* Periodic int. enable on */
+#define RTC_PIE_OFF _IO('p', 0x06) /* ... off */
+
+#define RTC_ALM_SET _IOW('p', 0x07, struct rtc_time) /* Set alarm time */
+#define RTC_ALM_READ _IOR('p', 0x08, struct rtc_time) /* Read alarm time */
+#define RTC_RD_TIME _IOR('p', 0x09, struct rtc_time) /* Read RTC time */
+#define RTC_SET_TIME _IOW('p', 0x0a, struct rtc_time) /* Set RTC time */
+#define RTC_IRQP_READ _IOR('p', 0x0b, unsigned long) /* Read IRQ rate */
+#define RTC_IRQP_SET _IOW('p', 0x0c, unsigned long) /* Set IRQ rate */
+#define RTC_EPOCH_READ _IOR('p', 0x0d, unsigned long) /* Read epoch */
+#define RTC_EPOCH_SET _IOW('p', 0x0e, unsigned long) /* Set epoch */
+
+
+#endif /* _MC146818RTC_H */
diff --git a/pfinet/linux-src/include/linux/mca.h b/pfinet/linux-src/include/linux/mca.h
new file mode 100644
index 00000000..5b6a6ebf
--- /dev/null
+++ b/pfinet/linux-src/include/linux/mca.h
@@ -0,0 +1,100 @@
+/*
+ * Header for Microchannel Architecture Bus
+ * Written by Martin Kolinek, February 1996
+*/
+
+#ifndef _LINUX_MCA_H
+#define _LINUX_MCA_H
+
+/* The detection of MCA bus is done in the real mode (using BIOS).
+ * The information is exported to the protected code, where this
+ * variable is set to one in case MCA bus was detected.
+*/
+extern int MCA_bus;
+
+/* maximal number of MCA slots - actually, some machines have less, but
+they all have sufficient number of POS registers to cover 8. */
+#define MCA_MAX_SLOT_NR 8
+
+/* MCA_NOTFOUND is an error condition. The other two indicate
+ motherboard POS registers contain the adapter. They might be
+ returned by the mca_find_adapter() function, and can be used as
+ arguments to mca_read_stored_pos(). I'm not going to allow direct
+ access to the motherboard registers until we run across an adapter
+ that requires it. We don't know enough about them to know if it's
+ safe.
+
+ See Documentation/mca.txt or one of the existing drivers for
+ more information.
+*/
+#define MCA_NOTFOUND (-1)
+#define MCA_INTEGSCSI (MCA_MAX_SLOT_NR)
+#define MCA_INTEGVIDEO (MCA_MAX_SLOT_NR+1)
+
+/* max number of adapters, including both slots and various integrated
+things. */
+#define MCA_NUMADAPTERS (MCA_MAX_SLOT_NR+2)
+
+/* returns the slot of the first enabled adapter matching id. User can
+specify a starting slot beyond zero, to deal with detecting multiple
+devices. Returns MCA_NOTFOUND if id not found. Also checks the
+integrated adapters. */
+extern int mca_find_adapter(int id, int start);
+extern int mca_find_unused_adapter(int id, int start);
+
+/* adapter state info - returns 0 if no */
+extern int mca_isadapter(int slot);
+extern int mca_isenabled(int slot);
+
+extern int mca_is_adapter_used(int slot);
+extern int mca_mark_as_used(int slot);
+extern void mca_mark_as_unused(int slot);
+
+/* gets a byte out of POS register (stored in memory) */
+extern unsigned char mca_read_stored_pos(int slot, int reg);
+
+/*
+ This can be expanded later. Right now, it gives us a way of
+ getting meaningful information into the MCA_info structure,
+ so we can have a more interesting /proc/mca.
+*/
+extern void mca_set_adapter_name(int slot, char* name);
+extern char* mca_get_adapter_name(int slot);
+
+/*
+ This sets up an information callback for /proc/mca/slot?. The
+ function is called with the buffer, slot, and device pointer (or
+ some equally informative context information, or nothing, if you
+ prefer), and is expected to put useful information into the
+ buffer. The adapter name, id, and POS registers get printed
+ before this is called though, so don't do it again.
+
+ This should be called with a NULL procfn when a module
+ unregisters, thus preventing kernel crashes and other such
+ nastiness.
+*/
+typedef int (*MCA_ProcFn)(char* buf, int slot, void* dev);
+extern void mca_set_adapter_procfn(int slot, MCA_ProcFn, void* dev);
+
+/* These routines actually mess with the hardware POS registers. They
+temporarily disable the device (and interrupts), so make sure you know
+what you're doing if you use them. Furthermore, writing to a POS may
+result in two devices trying to share a resource, which in turn can
+result in multiple devices sharing memory spaces, IRQs, or even trashing
+hardware. YOU HAVE BEEN WARNED.
+
+You can only access slots with this. Motherboard registers are off
+limits.
+*/
+
+/* read a byte from the specified POS register. */
+extern unsigned char mca_read_pos(int slot, int reg);
+
+/* write a byte to the specified POS register. */
+extern void mca_write_pos(int slot, int reg, unsigned char byte);
+
+/* Should only be called by the NMI interrupt handler, this will do some
+fancy stuff to figure out what might have generated a NMI. */
+extern void mca_handle_nmi(void);
+
+#endif /* _LINUX_MCA_H */
diff --git a/pfinet/linux-src/include/linux/md.h b/pfinet/linux-src/include/linux/md.h
new file mode 100644
index 00000000..f4f4f548
--- /dev/null
+++ b/pfinet/linux-src/include/linux/md.h
@@ -0,0 +1,300 @@
+/*
+ md.h : Multiple Devices driver for Linux
+ Copyright (C) 1994-96 Marc ZYNGIER
+ <zyngier@ufr-info-p7.ibp.fr> or
+ <maz@gloups.fdn.fr>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ You should have received a copy of the GNU General Public License
+ (for example /usr/src/linux/COPYING); if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _MD_H
+#define _MD_H
+
+#include <linux/major.h>
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/*
+ * Different major versions are not compatible.
+ * Different minor versions are only downward compatible.
+ * Different patchlevel versions are downward and upward compatible.
+ */
+#define MD_MAJOR_VERSION 0
+#define MD_MINOR_VERSION 36
+#define MD_PATCHLEVEL_VERSION 6
+
+#define MD_DEFAULT_DISK_READAHEAD (256 * 1024)
+
+/* ioctls */
+#define REGISTER_DEV _IO (MD_MAJOR, 1)
+#define START_MD _IO (MD_MAJOR, 2)
+#define STOP_MD _IO (MD_MAJOR, 3)
+#define REGISTER_DEV_NEW _IO (MD_MAJOR, 4)
+
+/*
+ personalities :
+ Byte 0 : Chunk size factor
+ Byte 1 : Fault tolerance count for each physical device
+ ( 0 means no fault tolerance,
+ 0xFF means always tolerate faults), not used by now.
+ Byte 2 : Personality
+ Byte 3 : Reserved.
+ */
+
+#define FAULT_SHIFT 8
+#define PERSONALITY_SHIFT 16
+
+#define FACTOR_MASK 0x000000FFUL
+#define FAULT_MASK 0x0000FF00UL
+#define PERSONALITY_MASK 0x00FF0000UL
+
+#define MD_RESERVED 0 /* Not used by now */
+#define LINEAR (1UL << PERSONALITY_SHIFT)
+#define STRIPED (2UL << PERSONALITY_SHIFT)
+#define RAID0 STRIPED
+#define RAID1 (3UL << PERSONALITY_SHIFT)
+#define RAID5 (4UL << PERSONALITY_SHIFT)
+#define MAX_PERSONALITY 5
+
+/*
+ * MD superblock.
+ *
+ * The MD superblock maintains some statistics on each MD configuration.
+ * Each real device in the MD set contains it near the end of the device.
+ * Some of the ideas are copied from the ext2fs implementation.
+ *
+ * We currently use 4096 bytes as follows:
+ *
+ * word offset function
+ *
+ * 0 - 31 Constant generic MD device information.
+ * 32 - 63 Generic state information.
+ * 64 - 127 Personality specific information.
+ * 128 - 511 12 32-words descriptors of the disks in the raid set.
+ * 512 - 911 Reserved.
+ * 912 - 1023 Disk specific descriptor.
+ */
+
+/*
+ * If x is the real device size in bytes, we return an apparent size of:
+ *
+ * y = (x & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES
+ *
+ * and place the 4kB superblock at offset y.
+ */
+#define MD_RESERVED_BYTES (64 * 1024)
+#define MD_RESERVED_SECTORS (MD_RESERVED_BYTES / 512)
+#define MD_RESERVED_BLOCKS (MD_RESERVED_BYTES / BLOCK_SIZE)
+
+#define MD_NEW_SIZE_SECTORS(x) ((x & ~(MD_RESERVED_SECTORS - 1)) - MD_RESERVED_SECTORS)
+#define MD_NEW_SIZE_BLOCKS(x) ((x & ~(MD_RESERVED_BLOCKS - 1)) - MD_RESERVED_BLOCKS)
+
+#define MD_SB_BYTES 4096
+#define MD_SB_WORDS (MD_SB_BYTES / 4)
+#define MD_SB_BLOCKS (MD_SB_BYTES / BLOCK_SIZE)
+#define MD_SB_SECTORS (MD_SB_BYTES / 512)
+
+/*
+ * The following are counted in 32-bit words
+ */
+#define MD_SB_GENERIC_OFFSET 0
+#define MD_SB_PERSONALITY_OFFSET 64
+#define MD_SB_DISKS_OFFSET 128
+#define MD_SB_DESCRIPTOR_OFFSET 992
+
+#define MD_SB_GENERIC_CONSTANT_WORDS 32
+#define MD_SB_GENERIC_STATE_WORDS 32
+#define MD_SB_GENERIC_WORDS (MD_SB_GENERIC_CONSTANT_WORDS + MD_SB_GENERIC_STATE_WORDS)
+#define MD_SB_PERSONALITY_WORDS 64
+#define MD_SB_DISKS_WORDS 384
+#define MD_SB_DESCRIPTOR_WORDS 32
+#define MD_SB_RESERVED_WORDS (1024 - MD_SB_GENERIC_WORDS - MD_SB_PERSONALITY_WORDS - MD_SB_DISKS_WORDS - MD_SB_DESCRIPTOR_WORDS)
+#define MD_SB_EQUAL_WORDS (MD_SB_GENERIC_WORDS + MD_SB_PERSONALITY_WORDS + MD_SB_DISKS_WORDS)
+#define MD_SB_DISKS (MD_SB_DISKS_WORDS / MD_SB_DESCRIPTOR_WORDS)
+
+/*
+ * Device "operational" state bits
+ */
+#define MD_FAULTY_DEVICE 0 /* Device is faulty / operational */
+#define MD_ACTIVE_DEVICE 1 /* Device is a part or the raid set / spare disk */
+#define MD_SYNC_DEVICE 2 /* Device is in sync with the raid set */
+
+typedef struct md_device_descriptor_s {
+ __u32 number; /* 0 Device number in the entire set */
+ __u32 major; /* 1 Device major number */
+ __u32 minor; /* 2 Device minor number */
+ __u32 raid_disk; /* 3 The role of the device in the raid set */
+ __u32 state; /* 4 Operational state */
+ __u32 reserved[MD_SB_DESCRIPTOR_WORDS - 5];
+} md_descriptor_t;
+
+#define MD_SB_MAGIC 0xa92b4efc
+
+/*
+ * Superblock state bits
+ */
+#define MD_SB_CLEAN 0
+#define MD_SB_ERRORS 1
+
+typedef struct md_superblock_s {
+
+ /*
+ * Constant generic information
+ */
+ __u32 md_magic; /* 0 MD identifier */
+ __u32 major_version; /* 1 major version to which the set conforms */
+ __u32 minor_version; /* 2 minor version to which the set conforms */
+ __u32 patch_version; /* 3 patchlevel version to which the set conforms */
+ __u32 gvalid_words; /* 4 Number of non-reserved words in this section */
+ __u32 set_magic; /* 5 Raid set identifier */
+ __u32 ctime; /* 6 Creation time */
+ __u32 level; /* 7 Raid personality (mirroring, raid5, ...) */
+ __u32 size; /* 8 Apparent size of each individual disk, in kB */
+ __u32 nr_disks; /* 9 Number of total disks in the raid set */
+ __u32 raid_disks; /* 10 Number of disks in a fully functional raid set */
+ __u32 gstate_creserved[MD_SB_GENERIC_CONSTANT_WORDS - 11];
+
+ /*
+ * Generic state information
+ */
+ __u32 utime; /* 0 Superblock update time */
+ __u32 state; /* 1 State bits (clean, ...) */
+ __u32 active_disks; /* 2 Number of currently active disks (some non-faulty disks might not be in sync) */
+ __u32 working_disks; /* 3 Number of working disks */
+ __u32 failed_disks; /* 4 Number of failed disks */
+ __u32 spare_disks; /* 5 Number of spare disks */
+ __u32 gstate_sreserved[MD_SB_GENERIC_STATE_WORDS - 6];
+
+ /*
+ * Personality information
+ */
+ __u32 parity_algorithm;
+ __u32 chunk_size;
+ __u32 pstate_reserved[MD_SB_PERSONALITY_WORDS - 2];
+
+ /*
+ * Disks information
+ */
+ md_descriptor_t disks[MD_SB_DISKS];
+
+ /*
+ * Reserved
+ */
+ __u32 reserved[MD_SB_RESERVED_WORDS];
+
+ /*
+ * Active descriptor
+ */
+ md_descriptor_t descriptor;
+} md_superblock_t;
+
+#ifdef __KERNEL__
+
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <asm/semaphore.h>
+
+/*
+ * Kernel-based reconstruction is mostly working, but still requires
+ * some additional work.
+ */
+#define SUPPORT_RECONSTRUCTION 0
+
+#define MAX_REAL 8 /* Max number of physical dev per md dev */
+#define MAX_MD_DEV 4 /* Max number of md dev */
+
+#define FACTOR(a) ((a)->repartition & FACTOR_MASK)
+#define MAX_FAULT(a) (((a)->repartition & FAULT_MASK)>>8)
+#define PERSONALITY(a) ((a)->repartition & PERSONALITY_MASK)
+
+#define FACTOR_SHIFT(a) (PAGE_SHIFT + (a) - 10)
+
+struct real_dev
+{
+ kdev_t dev; /* Device number */
+ int size; /* Device size (in blocks) */
+ int offset; /* Real device offset (in blocks) in md dev
+ (only used in linear mode) */
+ struct inode *inode; /* Lock inode */
+ md_superblock_t *sb;
+ u32 sb_offset;
+};
+
+struct md_dev;
+
+#define SPARE_INACTIVE 0
+#define SPARE_WRITE 1
+#define SPARE_ACTIVE 2
+
+struct md_personality
+{
+ char *name;
+ int (*map)(struct md_dev *mddev, kdev_t *rdev,
+ unsigned long *rsector, unsigned long size);
+ int (*make_request)(struct md_dev *mddev, int rw, struct buffer_head * bh);
+ void (*end_request)(struct buffer_head * bh, int uptodate);
+ int (*run)(int minor, struct md_dev *mddev);
+ int (*stop)(int minor, struct md_dev *mddev);
+ int (*status)(char *page, int minor, struct md_dev *mddev);
+ int (*ioctl)(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+ int max_invalid_dev;
+ int (*error_handler)(struct md_dev *mddev, kdev_t dev);
+
+/*
+ * Some personalities (RAID-1, RAID-5) can get disks hot-added and
+ * hot-removed. Hot removal is different from failure. (failure marks
+ * a disk inactive, but the disk is still part of the array)
+ */
+ int (*hot_add_disk) (struct md_dev *mddev, kdev_t dev);
+ int (*hot_remove_disk) (struct md_dev *mddev, kdev_t dev);
+ int (*mark_spare) (struct md_dev *mddev, md_descriptor_t *descriptor, int state);
+};
+
+struct md_dev
+{
+ struct real_dev devices[MAX_REAL];
+ struct md_personality *pers;
+ md_superblock_t *sb;
+ int sb_dirty;
+ int repartition;
+ int busy;
+ int nb_dev;
+ void *private;
+};
+
+struct md_thread {
+ void (*run) (void *data);
+ void *data;
+ struct wait_queue *wqueue;
+ unsigned long flags;
+ struct semaphore *sem;
+ struct task_struct *tsk;
+};
+
+#define THREAD_WAKEUP 0
+
+extern struct md_dev md_dev[MAX_MD_DEV];
+extern int md_size[MAX_MD_DEV];
+extern int md_maxreadahead[MAX_MD_DEV];
+
+extern char *partition_name (kdev_t dev);
+
+extern int register_md_personality (int p_num, struct md_personality *p);
+extern int unregister_md_personality (int p_num);
+extern struct md_thread *md_register_thread (void (*run) (void *data), void *data);
+extern void md_unregister_thread (struct md_thread *thread);
+extern void md_wakeup_thread(struct md_thread *thread);
+extern int md_update_sb (int minor);
+extern int md_do_sync(struct md_dev *mddev);
+
+#endif __KERNEL__
+#endif _MD_H
diff --git a/pfinet/linux-src/include/linux/minix_fs.h b/pfinet/linux-src/include/linux/minix_fs.h
new file mode 100644
index 00000000..4682ee56
--- /dev/null
+++ b/pfinet/linux-src/include/linux/minix_fs.h
@@ -0,0 +1,127 @@
+#ifndef _LINUX_MINIX_FS_H
+#define _LINUX_MINIX_FS_H
+
+/*
+ * The minix filesystem constants/structures
+ */
+
+/*
+ * Thanks to Kees J Bot for sending me the definitions of the new
+ * minix filesystem (aka V2) with bigger inodes and 32-bit block
+ * pointers.
+ */
+
+#define MINIX_ROOT_INO 1
+
+/* Not the same as the bogus LINK_MAX in <linux/limits.h>. Oh well. */
+#define MINIX_LINK_MAX 250
+#define MINIX2_LINK_MAX 65530
+
+#define MINIX_I_MAP_SLOTS 8
+#define MINIX_Z_MAP_SLOTS 64
+#define MINIX_SUPER_MAGIC 0x137F /* original minix fs */
+#define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */
+#define MINIX2_SUPER_MAGIC 0x2468 /* minix V2 fs */
+#define MINIX2_SUPER_MAGIC2 0x2478 /* minix V2 fs, 30 char names */
+#define MINIX_VALID_FS 0x0001 /* Clean fs. */
+#define MINIX_ERROR_FS 0x0002 /* fs has errors. */
+
+#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode)))
+#define MINIX2_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix2_inode)))
+
+#define MINIX_V1 0x0001 /* original minix fs */
+#define MINIX_V2 0x0002 /* minix V2 fs */
+
+#define INODE_VERSION(inode) inode->i_sb->u.minix_sb.s_version
+
+/*
+ * This is the original minix inode layout on disk.
+ * Note the 8-bit gid and atime and ctime.
+ */
+struct minix_inode {
+ __u16 i_mode;
+ __u16 i_uid;
+ __u32 i_size;
+ __u32 i_time;
+ __u8 i_gid;
+ __u8 i_nlinks;
+ __u16 i_zone[9];
+};
+
+/*
+ * The new minix inode has all the time entries, as well as
+ * long block numbers and a third indirect block (7+1+1+1
+ * instead of 7+1+1). Also, some previously 8-bit values are
+ * now 16-bit. The inode is now 64 bytes instead of 32.
+ */
+struct minix2_inode {
+ __u16 i_mode;
+ __u16 i_nlinks;
+ __u16 i_uid;
+ __u16 i_gid;
+ __u32 i_size;
+ __u32 i_atime;
+ __u32 i_mtime;
+ __u32 i_ctime;
+ __u32 i_zone[10];
+};
+
+/*
+ * minix super-block data on disk
+ */
+struct minix_super_block {
+ __u16 s_ninodes;
+ __u16 s_nzones;
+ __u16 s_imap_blocks;
+ __u16 s_zmap_blocks;
+ __u16 s_firstdatazone;
+ __u16 s_log_zone_size;
+ __u32 s_max_size;
+ __u16 s_magic;
+ __u16 s_state;
+ __u32 s_zones;
+};
+
+struct minix_dir_entry {
+ __u16 inode;
+ char name[0];
+};
+
+#ifdef __KERNEL__
+
+extern struct dentry *minix_lookup(struct inode * dir, struct dentry *dentry);
+extern int minix_create(struct inode * dir, struct dentry *dentry, int mode);
+extern int minix_mkdir(struct inode * dir, struct dentry *dentry, int mode);
+extern int minix_rmdir(struct inode * dir, struct dentry *dentry);
+extern int minix_unlink(struct inode * dir, struct dentry *dentry);
+extern int minix_symlink(struct inode * inode, struct dentry *dentry,
+ const char * symname);
+extern int minix_link(struct dentry * old_dentry, struct inode * dir, struct dentry *dentry);
+extern int minix_mknod(struct inode * dir, struct dentry *dentry, int mode, int rdev);
+extern int minix_rename(struct inode * old_dir, struct dentry *old_dentry,
+ struct inode * new_dir, struct dentry *new_dentry);
+extern struct inode * minix_new_inode(const struct inode * dir);
+extern void minix_free_inode(struct inode * inode);
+extern unsigned long minix_count_free_inodes(struct super_block *sb);
+extern int minix_new_block(struct super_block * sb);
+extern void minix_free_block(struct super_block * sb, int block);
+extern unsigned long minix_count_free_blocks(struct super_block *sb);
+
+extern int minix_bmap(struct inode *,int);
+
+extern struct buffer_head * minix_getblk(struct inode *, int, int);
+extern struct buffer_head * minix_bread(struct inode *, int, int);
+
+extern void minix_truncate(struct inode *);
+extern int init_minix_fs(void);
+extern int minix_sync_inode(struct inode *);
+extern int minix_sync_file(struct file *, struct dentry *);
+
+extern struct inode_operations minix_file_inode_operations;
+extern struct inode_operations minix_dir_inode_operations;
+extern struct inode_operations minix_symlink_inode_operations;
+extern struct dentry_operations minix_dentry_operations;
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/minix_fs_i.h b/pfinet/linux-src/include/linux/minix_fs_i.h
new file mode 100644
index 00000000..c3711e52
--- /dev/null
+++ b/pfinet/linux-src/include/linux/minix_fs_i.h
@@ -0,0 +1,14 @@
+#ifndef _MINIX_FS_I
+#define _MINIX_FS_I
+
+/*
+ * minix fs inode data in memory
+ */
+struct minix_inode_info {
+ union {
+ __u16 i1_data[16];
+ __u32 i2_data[16];
+ } u;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/minix_fs_sb.h b/pfinet/linux-src/include/linux/minix_fs_sb.h
new file mode 100644
index 00000000..54c82af9
--- /dev/null
+++ b/pfinet/linux-src/include/linux/minix_fs_sb.h
@@ -0,0 +1,26 @@
+#ifndef _MINIX_FS_SB
+#define _MINIX_FS_SB
+
+/*
+ * minix super-block data in memory
+ */
+struct minix_sb_info {
+ unsigned long s_ninodes;
+ unsigned long s_nzones;
+ unsigned long s_imap_blocks;
+ unsigned long s_zmap_blocks;
+ unsigned long s_firstdatazone;
+ unsigned long s_log_zone_size;
+ unsigned long s_max_size;
+ int s_dirsize;
+ int s_namelen;
+ int s_link_max;
+ struct buffer_head ** s_imap;
+ struct buffer_head ** s_zmap;
+ struct buffer_head * s_sbh;
+ struct minix_super_block * s_ms;
+ unsigned short s_mount_state;
+ unsigned short s_version;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/miscdevice.h b/pfinet/linux-src/include/linux/miscdevice.h
new file mode 100644
index 00000000..e04dc1b4
--- /dev/null
+++ b/pfinet/linux-src/include/linux/miscdevice.h
@@ -0,0 +1,43 @@
+#ifndef _LINUX_MISCDEVICE_H
+#define _LINUX_MISCDEVICE_H
+
+#define BUSMOUSE_MINOR 0
+#define PSMOUSE_MINOR 1
+#define MS_BUSMOUSE_MINOR 2
+#define ATIXL_BUSMOUSE_MINOR 3
+#define AMIGAMOUSE_MINOR 4
+#define ATARIMOUSE_MINOR 5
+#define SUN_MOUSE_MINOR 6
+#define APOLLO_MOUSE_MINOR 7
+#define PC110PAD_MINOR 9
+#define MAC_MOUSE_MINOR 10
+#define WATCHDOG_MINOR 130 /* Watchdog timer */
+#define TEMP_MINOR 131 /* Temperature Sensor */
+#define RTC_MINOR 135
+#define SUN_OPENPROM_MINOR 139
+#define NVRAM_MINOR 144
+#define I2O_MINOR 166
+#define MISC_DYNAMIC_MINOR 255
+
+#define SGI_GRAPHICS_MINOR 146
+#define SGI_OPENGL_MINOR 147
+#define SGI_GFX_MINOR 148
+#define SGI_STREAMS_MOUSE 149
+#define SGI_STREAMS_KEYBOARD 150
+/* drivers/sgi/char/usema.c */
+#define SGI_USEMACLONE 151
+
+extern int misc_init(void);
+
+struct miscdevice
+{
+ int minor;
+ const char *name;
+ struct file_operations *fops;
+ struct miscdevice * next, * prev;
+};
+
+extern int misc_register(struct miscdevice * misc);
+extern int misc_deregister(struct miscdevice * misc);
+
+#endif
diff --git a/pfinet/linux-src/include/linux/mm.h b/pfinet/linux-src/include/linux/mm.h
new file mode 100644
index 00000000..232c53dc
--- /dev/null
+++ b/pfinet/linux-src/include/linux/mm.h
@@ -0,0 +1,392 @@
+#ifndef _LINUX_MM_H
+#define _LINUX_MM_H
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+
+#ifdef __KERNEL__
+
+#include <linux/string.h>
+
+extern unsigned long max_mapnr;
+extern unsigned long num_physpages;
+extern void * high_memory;
+extern int page_cluster;
+
+#include <asm/page.h>
+#include <asm/atomic.h>
+
+/*
+ * Linux kernel virtual memory manager primitives.
+ * The idea being to have a "virtual" mm in the same way
+ * we have a virtual fs - giving a cleaner interface to the
+ * mm details, and allowing different kinds of memory mappings
+ * (from shared memory to executable loading to arbitrary
+ * mmap() functions).
+ */
+
+/*
+ * This struct defines a memory VMM memory area. There is one of these
+ * per VM-area/task. A VM area is any part of the process virtual memory
+ * space that has a special rule for the page-fault handlers (ie a shared
+ * library, the executable area etc).
+ */
+struct vm_area_struct {
+ struct mm_struct * vm_mm; /* VM area parameters */
+ unsigned long vm_start;
+ unsigned long vm_end;
+
+ /* linked list of VM areas per task, sorted by address */
+ struct vm_area_struct *vm_next;
+
+ pgprot_t vm_page_prot;
+ unsigned short vm_flags;
+
+ /* AVL tree of VM areas per task, sorted by address */
+ short vm_avl_height;
+ struct vm_area_struct * vm_avl_left;
+ struct vm_area_struct * vm_avl_right;
+
+ /* For areas with inode, the list inode->i_mmap, for shm areas,
+ * the list of attaches, otherwise unused.
+ */
+ struct vm_area_struct *vm_next_share;
+ struct vm_area_struct **vm_pprev_share;
+
+ struct vm_operations_struct * vm_ops;
+ unsigned long vm_offset;
+ struct file * vm_file;
+ unsigned long vm_pte; /* shared mem */
+};
+
+/*
+ * vm_flags..
+ */
+#define VM_READ 0x0001 /* currently active flags */
+#define VM_WRITE 0x0002
+#define VM_EXEC 0x0004
+#define VM_SHARED 0x0008
+
+#define VM_MAYREAD 0x0010 /* limits for mprotect() etc */
+#define VM_MAYWRITE 0x0020
+#define VM_MAYEXEC 0x0040
+#define VM_MAYSHARE 0x0080
+
+#define VM_GROWSDOWN 0x0100 /* general info on the segment */
+#define VM_GROWSUP 0x0200
+#define VM_SHM 0x0400 /* shared memory area, don't swap out */
+#define VM_DENYWRITE 0x0800 /* ETXTBSY on write attempts.. */
+
+#define VM_EXECUTABLE 0x1000
+#define VM_LOCKED 0x2000
+#define VM_IO 0x4000 /* Memory mapped I/O or similar */
+
+#define VM_STACK_FLAGS 0x0177
+
+/*
+ * mapping from the currently active vm_flags protection bits (the
+ * low four bits) to a page protection mask..
+ */
+extern pgprot_t protection_map[16];
+
+
+/*
+ * These are the virtual MM functions - opening of an area, closing and
+ * unmapping it (needed to keep files on disk up-to-date etc), pointer
+ * to the functions called when a no-page or a wp-page exception occurs.
+ */
+struct vm_operations_struct {
+ void (*open)(struct vm_area_struct * area);
+ void (*close)(struct vm_area_struct * area);
+ void (*unmap)(struct vm_area_struct *area, unsigned long, size_t);
+ void (*protect)(struct vm_area_struct *area, unsigned long, size_t, unsigned int newprot);
+ int (*sync)(struct vm_area_struct *area, unsigned long, size_t, unsigned int flags);
+ void (*advise)(struct vm_area_struct *area, unsigned long, size_t, unsigned int advise);
+ unsigned long (*nopage)(struct vm_area_struct * area, unsigned long address, int write_access);
+ unsigned long (*wppage)(struct vm_area_struct * area, unsigned long address,
+ unsigned long page);
+ int (*swapout)(struct vm_area_struct *, struct page *);
+ pte_t (*swapin)(struct vm_area_struct *, unsigned long, unsigned long);
+};
+
+/*
+ * Try to keep the most commonly accessed fields in single cache lines
+ * here (16 bytes or greater). This ordering should be particularly
+ * beneficial on 32-bit processors.
+ *
+ * The first line is data used in page cache lookup, the second line
+ * is used for linear searches (eg. clock algorithm scans).
+ */
+typedef struct page {
+ /* these must be first (free area handling) */
+ struct page *next;
+ struct page *prev;
+ struct inode *inode;
+ unsigned long offset;
+ struct page *next_hash;
+ atomic_t count;
+ unsigned long flags; /* atomic flags, some possibly updated asynchronously */
+ struct wait_queue *wait;
+ struct page **pprev_hash;
+ struct buffer_head * buffers;
+} mem_map_t;
+
+/* Page flag bit values */
+#define PG_locked 0
+#define PG_error 1
+#define PG_referenced 2
+#define PG_dirty 3
+#define PG_uptodate 4
+#define PG_free_after 5
+#define PG_decr_after 6
+#define PG_swap_unlock_after 7
+#define PG_DMA 8
+#define PG_Slab 9
+#define PG_swap_cache 10
+#define PG_skip 11
+#define PG_reserved 31
+
+/* Make it prettier to test the above... */
+#define PageLocked(page) (test_bit(PG_locked, &(page)->flags))
+#define PageError(page) (test_bit(PG_error, &(page)->flags))
+#define PageReferenced(page) (test_bit(PG_referenced, &(page)->flags))
+#define PageDirty(page) (test_bit(PG_dirty, &(page)->flags))
+#define PageUptodate(page) (test_bit(PG_uptodate, &(page)->flags))
+#define PageFreeAfter(page) (test_bit(PG_free_after, &(page)->flags))
+#define PageDecrAfter(page) (test_bit(PG_decr_after, &(page)->flags))
+#define PageSwapUnlockAfter(page) (test_bit(PG_swap_unlock_after, &(page)->flags))
+#define PageDMA(page) (test_bit(PG_DMA, &(page)->flags))
+#define PageSlab(page) (test_bit(PG_Slab, &(page)->flags))
+#define PageSwapCache(page) (test_bit(PG_swap_cache, &(page)->flags))
+#define PageReserved(page) (test_bit(PG_reserved, &(page)->flags))
+
+#define PageSetSlab(page) (set_bit(PG_Slab, &(page)->flags))
+#define PageSetSwapCache(page) (set_bit(PG_swap_cache, &(page)->flags))
+
+#define PageTestandSetDirty(page) \
+ (test_and_set_bit(PG_dirty, &(page)->flags))
+#define PageTestandSetSwapCache(page) \
+ (test_and_set_bit(PG_swap_cache, &(page)->flags))
+
+#define PageClearSlab(page) (clear_bit(PG_Slab, &(page)->flags))
+#define PageClearSwapCache(page)(clear_bit(PG_swap_cache, &(page)->flags))
+
+#define PageTestandClearDirty(page) \
+ (test_and_clear_bit(PG_dirty, &(page)->flags))
+#define PageTestandClearSwapCache(page) \
+ (test_and_clear_bit(PG_swap_cache, &(page)->flags))
+
+/*
+ * Various page->flags bits:
+ *
+ * PG_reserved is set for a page which must never be accessed (which
+ * may not even be present).
+ *
+ * PG_DMA is set for those pages which lie in the range of
+ * physical addresses capable of carrying DMA transfers.
+ *
+ * Multiple processes may "see" the same page. E.g. for untouched
+ * mappings of /dev/null, all processes see the same page full of
+ * zeroes, and text pages of executables and shared libraries have
+ * only one copy in memory, at most, normally.
+ *
+ * For the non-reserved pages, page->count denotes a reference count.
+ * page->count == 0 means the page is free.
+ * page->count == 1 means the page is used for exactly one purpose
+ * (e.g. a private data page of one process).
+ *
+ * A page may be used for kmalloc() or anyone else who does a
+ * get_free_page(). In this case the page->count is at least 1, and
+ * all other fields are unused but should be 0 or NULL. The
+ * management of this page is the responsibility of the one who uses
+ * it.
+ *
+ * The other pages (we may call them "process pages") are completely
+ * managed by the Linux memory manager: I/O, buffers, swapping etc.
+ * The following discussion applies only to them.
+ *
+ * A page may belong to an inode's memory mapping. In this case,
+ * page->inode is the pointer to the inode, and page->offset is the
+ * file offset of the page (not necessarily a multiple of PAGE_SIZE).
+ *
+ * A page may have buffers allocated to it. In this case,
+ * page->buffers is a circular list of these buffer heads. Else,
+ * page->buffers == NULL.
+ *
+ * For pages belonging to inodes, the page->count is the number of
+ * attaches, plus 1 if buffers are allocated to the page.
+ *
+ * All pages belonging to an inode make up a doubly linked list
+ * inode->i_pages, using the fields page->next and page->prev. (These
+ * fields are also used for freelist management when page->count==0.)
+ * There is also a hash table mapping (inode,offset) to the page
+ * in memory if present. The lists for this hash table use the fields
+ * page->next_hash and page->pprev_hash.
+ *
+ * All process pages can do I/O:
+ * - inode pages may need to be read from disk,
+ * - inode pages which have been modified and are MAP_SHARED may need
+ * to be written to disk,
+ * - private pages which have been modified may need to be swapped out
+ * to swap space and (later) to be read back into memory.
+ * During disk I/O, PG_locked is used. This bit is set before I/O
+ * and reset when I/O completes. page->wait is a wait queue of all
+ * tasks waiting for the I/O on this page to complete.
+ * PG_uptodate tells whether the page's contents is valid.
+ * When a read completes, the page becomes uptodate, unless a disk I/O
+ * error happened.
+ * When a write completes, and PG_free_after is set, the page is
+ * freed without any further delay.
+ *
+ * For choosing which pages to swap out, inode pages carry a
+ * PG_referenced bit, which is set any time the system accesses
+ * that page through the (inode,offset) hash table.
+ *
+ * PG_skip is used on sparc/sparc64 architectures to "skip" certain
+ * parts of the address space.
+ *
+ * PG_error is set to indicate that an I/O error occurred on this page.
+ */
+
+extern mem_map_t * mem_map;
+
+/*
+ * This is timing-critical - most of the time in getting a new page
+ * goes to clearing the page. If you want a page without the clearing
+ * overhead, just use __get_free_page() directly..
+ */
+#define __get_free_page(gfp_mask) __get_free_pages((gfp_mask),0)
+#define __get_dma_pages(gfp_mask, order) __get_free_pages((gfp_mask) | GFP_DMA,(order))
+extern unsigned long FASTCALL(__get_free_pages(int gfp_mask, unsigned long gfp_order));
+
+extern inline unsigned long get_free_page(int gfp_mask)
+{
+ unsigned long page;
+
+ page = __get_free_page(gfp_mask);
+ if (page)
+ clear_page(page);
+ return page;
+}
+
+extern int low_on_memory;
+
+/* memory.c & swap.c*/
+
+#define free_page(addr) free_pages((addr),0)
+extern void FASTCALL(free_pages(unsigned long addr, unsigned long order));
+extern void FASTCALL(__free_page(struct page *));
+
+extern void show_free_areas(void);
+extern unsigned long put_dirty_page(struct task_struct * tsk,unsigned long page,
+ unsigned long address);
+
+extern void free_page_tables(struct mm_struct * mm);
+extern void clear_page_tables(struct mm_struct *, unsigned long, int);
+extern int new_page_tables(struct task_struct * tsk);
+
+extern void zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size);
+extern int copy_page_range(struct mm_struct *dst, struct mm_struct *src, struct vm_area_struct *vma);
+extern int remap_page_range(unsigned long from, unsigned long to, unsigned long size, pgprot_t prot);
+extern int zeromap_page_range(unsigned long from, unsigned long size, pgprot_t prot);
+
+extern void vmtruncate(struct inode * inode, unsigned long offset);
+extern int handle_mm_fault(struct task_struct *tsk,struct vm_area_struct *vma, unsigned long address, int write_access);
+extern int make_pages_present(unsigned long addr, unsigned long end);
+
+extern int pgt_cache_water[2];
+extern int check_pgt_cache(void);
+
+extern unsigned long paging_init(unsigned long start_mem, unsigned long end_mem);
+extern void mem_init(unsigned long start_mem, unsigned long end_mem);
+extern void show_mem(void);
+extern void si_meminfo(struct sysinfo * val);
+
+/* mmap.c */
+extern void vma_init(void);
+extern void merge_segments(struct mm_struct *, unsigned long, unsigned long);
+extern void insert_vm_struct(struct mm_struct *, struct vm_area_struct *);
+extern void build_mmap_avl(struct mm_struct *);
+extern void exit_mmap(struct mm_struct *);
+extern unsigned long get_unmapped_area(unsigned long, unsigned long);
+
+extern unsigned long do_mmap(struct file *, unsigned long, unsigned long,
+ unsigned long, unsigned long, unsigned long);
+extern int do_munmap(unsigned long, size_t);
+
+/* filemap.c */
+extern void remove_inode_page(struct page *);
+extern unsigned long page_unuse(struct page *);
+extern int shrink_mmap(int, int);
+extern void truncate_inode_pages(struct inode *, unsigned long);
+extern unsigned long get_cached_page(struct inode *, unsigned long, int);
+extern void put_cached_page(unsigned long);
+
+/*
+ * GFP bitmasks..
+ */
+#define __GFP_WAIT 0x01
+#define __GFP_LOW 0x02
+#define __GFP_MED 0x04
+#define __GFP_HIGH 0x08
+#define __GFP_IO 0x10
+#define __GFP_SWAP 0x20
+
+#define __GFP_DMA 0x80
+
+#define GFP_BUFFER (__GFP_LOW | __GFP_WAIT)
+#define GFP_ATOMIC (__GFP_HIGH)
+#define GFP_USER (__GFP_LOW | __GFP_WAIT | __GFP_IO)
+#define GFP_KERNEL (__GFP_MED | __GFP_WAIT | __GFP_IO)
+#define GFP_NFS (__GFP_HIGH | __GFP_WAIT | __GFP_IO)
+#define GFP_KSWAPD (__GFP_IO | __GFP_SWAP)
+
+/* Flag - indicates that the buffer will be suitable for DMA. Ignored on some
+ platforms, used as appropriate on others */
+
+#define GFP_DMA __GFP_DMA
+
+/* vma is the first one with address < vma->vm_end,
+ * and even address < vma->vm_start. Have to extend vma. */
+static inline int expand_stack(struct vm_area_struct * vma, unsigned long address)
+{
+ unsigned long grow;
+
+ address &= PAGE_MASK;
+ grow = vma->vm_start - address;
+ if ((vma->vm_end - address
+ > current->rlim[RLIMIT_STACK].rlim_cur) ||
+ ((current->rlim[RLIMIT_AS].rlim_cur < RLIM_INFINITY) &&
+ ((vma->vm_mm->total_vm << PAGE_SHIFT) + grow
+ > current->rlim[RLIMIT_AS].rlim_cur)))
+ return -ENOMEM;
+ vma->vm_start = address;
+ vma->vm_offset -= grow;
+ vma->vm_mm->total_vm += grow >> PAGE_SHIFT;
+ if (vma->vm_flags & VM_LOCKED)
+ vma->vm_mm->locked_vm += grow >> PAGE_SHIFT;
+ return 0;
+}
+
+/* Look up the first VMA which satisfies addr < vm_end, NULL if none. */
+extern struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr);
+
+/* Look up the first VMA which intersects the interval start_addr..end_addr-1,
+ NULL if none. Assume start_addr < end_addr. */
+static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr)
+{
+ struct vm_area_struct * vma = find_vma(mm,start_addr);
+
+ if (vma && end_addr <= vma->vm_start)
+ vma = NULL;
+ return vma;
+}
+
+#define buffer_under_min() ((buffermem >> PAGE_SHIFT) * 100 < \
+ buffer_mem.min_percent * num_physpages)
+#define pgcache_under_min() (page_cache_size * 100 < \
+ page_cache.min_percent * num_physpages)
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/mman.h b/pfinet/linux-src/include/linux/mman.h
new file mode 100644
index 00000000..3bc0430b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/mman.h
@@ -0,0 +1,8 @@
+#ifndef _LINUX_MMAN_H
+#define _LINUX_MMAN_H
+
+#include <asm/mman.h>
+
+#define MREMAP_MAYMOVE 1
+
+#endif /* _LINUX_MMAN_H */
diff --git a/pfinet/linux-src/include/linux/modsetver.h b/pfinet/linux-src/include/linux/modsetver.h
new file mode 100644
index 00000000..7d0b9d37
--- /dev/null
+++ b/pfinet/linux-src/include/linux/modsetver.h
@@ -0,0 +1,10 @@
+/* Symbol versioning nastiness. */
+
+#define __SYMBOL_VERSION(x) __ver_ ## x
+#define __VERSIONED_SYMBOL2(x,v) x ## _R ## v
+#define __VERSIONED_SYMBOL1(x,v) __VERSIONED_SYMBOL2(x,v)
+#define __VERSIONED_SYMBOL(x) __VERSIONED_SYMBOL1(x,__SYMBOL_VERSION(x))
+
+#ifndef _set_ver
+#define _set_ver(x) __VERSIONED_SYMBOL(x)
+#endif
diff --git a/pfinet/linux-src/include/linux/module.h b/pfinet/linux-src/include/linux/module.h
new file mode 100644
index 00000000..585a8d1d
--- /dev/null
+++ b/pfinet/linux-src/include/linux/module.h
@@ -0,0 +1,287 @@
+/*
+ * Dynamic loading of modules into the kernel.
+ *
+ * Rewritten by Richard Henderson <rth@tamu.edu> Dec 1996
+ */
+
+#ifndef _LINUX_MODULE_H
+#define _LINUX_MODULE_H
+
+#include <linux/config.h>
+
+#ifdef __GENKSYMS__
+# define _set_ver(sym) sym
+# undef MODVERSIONS
+# define MODVERSIONS
+#else /* ! __GENKSYMS__ */
+# if !defined(MODVERSIONS) && defined(EXPORT_SYMTAB)
+# define _set_ver(sym) sym
+# include <linux/modversions.h>
+# endif
+#endif /* __GENKSYMS__ */
+
+#include <asm/atomic.h>
+
+/* Don't need to bring in all of uaccess.h just for this decl. */
+struct exception_table_entry;
+
+/* Used by get_kernel_syms, which is obsolete. */
+struct kernel_sym
+{
+ unsigned long value;
+ char name[60]; /* should have been 64-sizeof(long); oh well */
+};
+
+struct module_symbol
+{
+ unsigned long value;
+ const char *name;
+};
+
+struct module_ref
+{
+ struct module *dep; /* "parent" pointer */
+ struct module *ref; /* "child" pointer */
+ struct module_ref *next_ref;
+};
+
+/* TBD */
+struct module_persist;
+
+struct module
+{
+ unsigned long size_of_struct; /* == sizeof(module) */
+ struct module *next;
+ const char *name;
+ unsigned long size;
+
+ union
+ {
+ atomic_t usecount;
+ long pad;
+ } uc; /* Needs to keep its size - so says rth */
+
+ unsigned long flags; /* AUTOCLEAN et al */
+
+ unsigned nsyms;
+ unsigned ndeps;
+
+ struct module_symbol *syms;
+ struct module_ref *deps;
+ struct module_ref *refs;
+ int (*init)(void);
+ void (*cleanup)(void);
+ const struct exception_table_entry *ex_table_start;
+ const struct exception_table_entry *ex_table_end;
+#ifdef __alpha__
+ unsigned long gp;
+#endif
+ /* Members past this point are extensions to the basic
+ module support and are optional. Use mod_opt_member()
+ to examine them. */
+ const struct module_persist *persist_start;
+ const struct module_persist *persist_end;
+ int (*can_unload)(void);
+};
+
+struct module_info
+{
+ unsigned long addr;
+ unsigned long size;
+ unsigned long flags;
+ long usecount;
+};
+
+/* Bits of module.flags. */
+
+#define MOD_UNINITIALIZED 0
+#define MOD_RUNNING 1
+#define MOD_DELETED 2
+#define MOD_AUTOCLEAN 4
+#define MOD_VISITED 8
+#define MOD_USED_ONCE 16
+#define MOD_JUST_FREED 32
+
+/* Values for query_module's which. */
+
+#define QM_MODULES 1
+#define QM_DEPS 2
+#define QM_REFS 3
+#define QM_SYMBOLS 4
+#define QM_INFO 5
+
+/* When struct module is extended, we must test whether the new member
+ is present in the header received from insmod before we can use it.
+ This function returns true if the member is present. */
+
+#define mod_member_present(mod,member) \
+ ((unsigned long)(&((struct module *)0L)->member + 1) \
+ <= (mod)->size_of_struct)
+
+/* Backwards compatibility definition. */
+
+#define GET_USE_COUNT(module) (atomic_read(&(module)->uc.usecount))
+
+/* Poke the use count of a module. */
+
+#define __MOD_INC_USE_COUNT(mod) \
+ (atomic_inc(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED|MOD_USED_ONCE)
+#define __MOD_DEC_USE_COUNT(mod) \
+ (atomic_dec(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED)
+#define __MOD_IN_USE(mod) \
+ (mod_member_present((mod), can_unload) && (mod)->can_unload \
+ ? (mod)->can_unload() : atomic_read(&(mod)->uc.usecount))
+
+/* Indirect stringification. */
+
+#define __MODULE_STRING_1(x) #x
+#define __MODULE_STRING(x) __MODULE_STRING_1(x)
+
+/* Find a symbol exported by the kernel or another module */
+extern unsigned long get_module_symbol(char *, char *);
+
+#if defined(MODULE) && !defined(__GENKSYMS__)
+
+/* Embedded module documentation macros. */
+
+/* For documentation purposes only. */
+
+#define MODULE_AUTHOR(name) \
+const char __module_author[] __attribute__((section(".modinfo"))) = \
+"author=" name
+
+#define MODULE_DESCRIPTION(desc) \
+const char __module_description[] __attribute__((section(".modinfo"))) = \
+"description=" desc
+
+/* Could potentially be used by kmod... */
+
+#define MODULE_SUPPORTED_DEVICE(dev) \
+const char __module_device[] __attribute__((section(".modinfo"))) = \
+"device=" dev
+
+/* Used to verify parameters given to the module. The TYPE arg should
+ be a string in the following format:
+ [min[-max]]{b,h,i,l,s}
+ The MIN and MAX specifiers delimit the length of the array. If MAX
+ is omitted, it defaults to MIN; if both are omitted, the default is 1.
+ The final character is a type specifier:
+ b byte
+ h short
+ i int
+ l long
+ s string
+*/
+
+#define MODULE_PARM(var,type) \
+const char __module_parm_##var[] \
+__attribute__((section(".modinfo"))) = \
+"parm_" __MODULE_STRING(var) "=" type
+
+#define MODULE_PARM_DESC(var,desc) \
+const char __module_parm_desc_##var[] \
+__attribute__((section(".modinfo"))) = \
+"parm_desc_" __MODULE_STRING(var) "=" desc
+
+/* The attributes of a section are set the first time the section is
+ seen; we want .modinfo to not be allocated. */
+
+__asm__(".section .modinfo\n\t.previous");
+
+/* Define the module variable, and usage macros. */
+extern struct module __this_module;
+
+#define MOD_INC_USE_COUNT __MOD_INC_USE_COUNT(&__this_module)
+#define MOD_DEC_USE_COUNT __MOD_DEC_USE_COUNT(&__this_module)
+#define MOD_IN_USE __MOD_IN_USE(&__this_module)
+
+#ifndef __NO_VERSION__
+#include <linux/version.h>
+const char __module_kernel_version[] __attribute__((section(".modinfo"))) =
+"kernel_version=" UTS_RELEASE;
+#ifdef MODVERSIONS
+const char __module_using_checksums[] __attribute__((section(".modinfo"))) =
+"using_checksums=1";
+#endif
+#endif
+
+#else /* MODULE */
+
+#define MODULE_AUTHOR(name)
+#define MODULE_DESCRIPTION(desc)
+#define MODULE_SUPPORTED_DEVICE(name)
+#define MODULE_PARM(var,type)
+#define MODULE_PARM_DESC(var,desc)
+
+#ifndef __GENKSYMS__
+
+#define MOD_INC_USE_COUNT do { } while (0)
+#define MOD_DEC_USE_COUNT do { } while (0)
+#define MOD_IN_USE 1
+
+extern struct module *module_list;
+
+#endif /* !__GENKSYMS__ */
+
+#endif /* MODULE */
+
+/* Export a symbol either from the kernel or a module.
+
+ In the kernel, the symbol is added to the kernel's global symbol table.
+
+ In a module, it controls which variables are exported. If no
+ variables are explicitly exported, the action is controlled by the
+ insmod -[xX] flags. Otherwise, only the variables listed are exported.
+ This obviates the need for the old register_symtab() function. */
+
+#if defined(__GENKSYMS__)
+
+/* We want the EXPORT_SYMBOL tag left intact for recognition. */
+
+#elif !defined(AUTOCONF_INCLUDED)
+
+#define __EXPORT_SYMBOL(sym,str) error config_must_be_included_before_module
+#define EXPORT_SYMBOL(var) error config_must_be_included_before_module
+#define EXPORT_SYMBOL_NOVERS(var) error config_must_be_included_before_module
+
+#elif !defined(CONFIG_MODULES)
+
+#define __EXPORT_SYMBOL(sym,str)
+#define EXPORT_SYMBOL(var)
+#define EXPORT_SYMBOL_NOVERS(var)
+
+#elif !defined(EXPORT_SYMTAB)
+
+/* If things weren't set up in the Makefiles to get EXPORT_SYMTAB defined,
+ then they weren't set up to run genksyms properly so MODVERSIONS breaks. */
+#define __EXPORT_SYMBOL(sym,str) error EXPORT_SYMTAB_not_defined
+#define EXPORT_SYMBOL(var) error EXPORT_SYMTAB_not_defined
+#define EXPORT_SYMBOL_NOVERS(var) error EXPORT_SYMTAB_not_defined
+
+#else
+
+#define __EXPORT_SYMBOL(sym, str) \
+const char __kstrtab_##sym[] \
+__attribute__((section(".kstrtab"))) = str; \
+const struct module_symbol __ksymtab_##sym \
+__attribute__((section("__ksymtab"))) = \
+{ (unsigned long)&sym, __kstrtab_##sym }
+
+#if defined(MODVERSIONS) || !defined(CONFIG_MODVERSIONS)
+#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var))
+#else
+#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(__VERSIONED_SYMBOL(var)))
+#endif
+
+#define EXPORT_SYMBOL_NOVERS(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var))
+
+#endif /* __GENKSYMS__ */
+
+#ifdef MODULE
+/* Force a module to export no symbols. */
+#define EXPORT_NO_SYMBOLS __asm__(".section __ksymtab\n.previous")
+#else
+#define EXPORT_NO_SYMBOLS
+#endif /* MODULE */
+
+#endif /* _LINUX_MODULE_H */
diff --git a/pfinet/linux-src/include/linux/mount.h b/pfinet/linux-src/include/linux/mount.h
new file mode 100644
index 00000000..1e697071
--- /dev/null
+++ b/pfinet/linux-src/include/linux/mount.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Definitions for mount interface. This describes the in the kernel build
+ * linkedlist with mounted filesystems.
+ *
+ * Author: Marco van Wieringen <mvw@planets.elm.net>
+ *
+ * Version: $Id: mount.h,v 2.0 1996/11/17 16:48:14 mvw Exp mvw $
+ *
+ */
+#ifndef _LINUX_MOUNT_H
+#define _LINUX_MOUNT_H
+
+#define DQUOT_USR_ENABLED 0x01 /* User diskquotas enabled */
+#define DQUOT_GRP_ENABLED 0x02 /* Group diskquotas enabled */
+
+struct quota_mount_options
+{
+ unsigned int flags; /* Flags for diskquotas on this device */
+ struct semaphore dqio_sem; /* lock device while I/O in progress */
+ struct semaphore dqoff_sem; /* serialize quota_off() and quota_on() on device */
+ struct file *files[MAXQUOTAS]; /* fp's to quotafiles */
+ time_t inode_expire[MAXQUOTAS]; /* expiretime for inode-quota */
+ time_t block_expire[MAXQUOTAS]; /* expiretime for block-quota */
+ char rsquash[MAXQUOTAS]; /* for quotas treat root as any other user */
+};
+
+struct vfsmount
+{
+ kdev_t mnt_dev; /* Device this applies to */
+ char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */
+ char *mnt_dirname; /* Name of directory mounted on */
+ unsigned int mnt_flags; /* Flags of this device */
+ struct super_block *mnt_sb; /* pointer to superblock */
+ struct quota_mount_options mnt_dquot; /* Diskquota specific mount options */
+ struct vfsmount *mnt_next; /* pointer to next in linkedlist */
+};
+
+struct vfsmount *lookup_vfsmnt(kdev_t dev);
+
+/*
+ * Umount options
+ */
+
+#define MNT_FORCE 0x00000001 /* Attempt to forcibily umount */
+
+#endif /* _LINUX_MOUNT_H */
diff --git a/pfinet/linux-src/include/linux/mpp.h b/pfinet/linux-src/include/linux/mpp.h
new file mode 100644
index 00000000..2dd02ff4
--- /dev/null
+++ b/pfinet/linux-src/include/linux/mpp.h
@@ -0,0 +1,18 @@
+#ifndef _LINUX_MPP_H
+#define _LINUX_MPP_H
+
+/*
+ * Definitions related to Massively Parallel Processing support.
+ */
+
+/* All mpp implementations must supply these functions */
+
+extern void mpp_init(void);
+extern void mpp_hw_init(void);
+extern void mpp_procfs_init(void);
+
+extern int mpp_num_cells(void);
+extern int mpp_cid(void);
+extern int get_mppinfo(char *buffer);
+
+#endif
diff --git a/pfinet/linux-src/include/linux/mroute.h b/pfinet/linux-src/include/linux/mroute.h
new file mode 100644
index 00000000..b57519b7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/mroute.h
@@ -0,0 +1,223 @@
+#ifndef __LINUX_MROUTE_H
+#define __LINUX_MROUTE_H
+
+#include <linux/sockios.h>
+#include <linux/in.h>
+
+/*
+ * Based on the MROUTING 3.5 defines primarily to keep
+ * source compatibility with BSD.
+ *
+ * See the mrouted code for the original history.
+ *
+ * Protocol Independent Multicast (PIM) data structures included
+ * Carlos Picoto (cap@di.fc.ul.pt)
+ *
+ */
+
+#define MRT_BASE 200
+#define MRT_INIT (MRT_BASE) /* Activate the kernel mroute code */
+#define MRT_DONE (MRT_BASE+1) /* Shutdown the kernel mroute */
+#define MRT_ADD_VIF (MRT_BASE+2) /* Add a virtual interface */
+#define MRT_DEL_VIF (MRT_BASE+3) /* Delete a virtual interface */
+#define MRT_ADD_MFC (MRT_BASE+4) /* Add a multicast forwarding entry */
+#define MRT_DEL_MFC (MRT_BASE+5) /* Delete a multicast forwarding entry */
+#define MRT_VERSION (MRT_BASE+6) /* Get the kernel multicast version */
+#define MRT_ASSERT (MRT_BASE+7) /* Activate PIM assert mode */
+#define MRT_PIM (MRT_BASE+8) /* enable PIM code */
+
+#define SIOCGETVIFCNT SIOCPROTOPRIVATE /* IP protocol privates */
+#define SIOCGETSGCNT (SIOCPROTOPRIVATE+1)
+#define SIOCGETRPF (SIOCPROTOPRIVATE+2)
+
+#define MAXVIFS 32
+typedef unsigned long vifbitmap_t; /* User mode code depends on this lot */
+typedef unsigned short vifi_t;
+#define ALL_VIFS ((vifi_t)(-1))
+
+/*
+ * Same idea as select
+ */
+
+#define VIFM_SET(n,m) ((m)|=(1<<(n)))
+#define VIFM_CLR(n,m) ((m)&=~(1<<(n)))
+#define VIFM_ISSET(n,m) ((m)&(1<<(n)))
+#define VIFM_CLRALL(m) ((m)=0)
+#define VIFM_COPY(mfrom,mto) ((mto)=(mfrom))
+#define VIFM_SAME(m1,m2) ((m1)==(m2))
+
+/*
+ * Passed by mrouted for an MRT_ADD_VIF - again we use the
+ * mrouted 3.6 structures for compatibility
+ */
+
+struct vifctl {
+ vifi_t vifc_vifi; /* Index of VIF */
+ unsigned char vifc_flags; /* VIFF_ flags */
+ unsigned char vifc_threshold; /* ttl limit */
+ unsigned int vifc_rate_limit; /* Rate limiter values (NI) */
+ struct in_addr vifc_lcl_addr; /* Our address */
+ struct in_addr vifc_rmt_addr; /* IPIP tunnel addr */
+};
+
+#define VIFF_TUNNEL 0x1 /* IPIP tunnel */
+#define VIFF_SRCRT 0x2 /* NI */
+#define VIFF_REGISTER 0x4 /* register vif */
+
+/*
+ * Cache manipulation structures for mrouted and PIMd
+ */
+
+struct mfcctl
+{
+ struct in_addr mfcc_origin; /* Origin of mcast */
+ struct in_addr mfcc_mcastgrp; /* Group in question */
+ vifi_t mfcc_parent; /* Where it arrived */
+ unsigned char mfcc_ttls[MAXVIFS]; /* Where it is going */
+ unsigned int mfcc_pkt_cnt; /* pkt count for src-grp */
+ unsigned int mfcc_byte_cnt;
+ unsigned int mfcc_wrong_if;
+ int mfcc_expire;
+};
+
+/*
+ * Group count retrieval for mrouted
+ */
+
+struct sioc_sg_req
+{
+ struct in_addr src;
+ struct in_addr grp;
+ unsigned long pktcnt;
+ unsigned long bytecnt;
+ unsigned long wrong_if;
+};
+
+/*
+ * To get vif packet counts
+ */
+
+struct sioc_vif_req
+{
+ vifi_t vifi; /* Which iface */
+ unsigned long icount; /* In packets */
+ unsigned long ocount; /* Out packets */
+ unsigned long ibytes; /* In bytes */
+ unsigned long obytes; /* Out bytes */
+};
+
+/*
+ * This is the format the mroute daemon expects to see IGMP control
+ * data. Magically happens to be like an IP packet as per the original
+ */
+
+struct igmpmsg
+{
+ __u32 unused1,unused2;
+ unsigned char im_msgtype; /* What is this */
+ unsigned char im_mbz; /* Must be zero */
+ unsigned char im_vif; /* Interface (this ought to be a vifi_t!) */
+ unsigned char unused3;
+ struct in_addr im_src,im_dst;
+};
+
+/*
+ * That's all usermode folks
+ */
+
+#ifdef __KERNEL__
+extern struct sock *mroute_socket;
+extern int ip_mroute_setsockopt(struct sock *, int, char *, int);
+extern int ip_mroute_getsockopt(struct sock *, int, char *, int *);
+extern int ipmr_ioctl(struct sock *sk, int cmd, unsigned long arg);
+extern void mroute_close(struct sock *sk);
+extern void ipmr_forward(struct sk_buff *skb, int is_frag);
+extern int ip_mr_find_tunnel(__u32, __u32);
+extern void ip_mr_init(void);
+
+
+struct vif_device
+{
+ struct device *dev; /* Device we are using */
+ unsigned long bytes_in,bytes_out;
+ unsigned long pkt_in,pkt_out; /* Statistics */
+ unsigned long rate_limit; /* Traffic shaping (NI) */
+ unsigned char threshold; /* TTL threshold */
+ unsigned short flags; /* Control flags */
+ __u32 local,remote; /* Addresses(remote for tunnels)*/
+ int link; /* Physical interface index */
+};
+
+struct mfc_cache
+{
+ struct mfc_cache *next; /* Next entry on cache line */
+ __u32 mfc_mcastgrp; /* Group the entry belongs to */
+ __u32 mfc_origin; /* Source of packet */
+ vifi_t mfc_parent; /* Source interface */
+ struct timer_list mfc_timer; /* Expiry timer */
+ int mfc_flags; /* Flags on line */
+ struct sk_buff_head mfc_unresolved; /* Unresolved buffers */
+ int mfc_queuelen; /* Unresolved buffer counter */
+ unsigned long mfc_last_assert;
+ int mfc_minvif;
+ int mfc_maxvif;
+ unsigned long mfc_bytes;
+ unsigned long mfc_pkt;
+ unsigned long mfc_wrong_if;
+ unsigned char mfc_ttls[MAXVIFS]; /* TTL thresholds */
+};
+
+#define MFC_QUEUED 1
+#define MFC_RESOLVED 2
+#define MFC_NOTIFY 4
+
+
+#define MFC_LINES 64
+
+#ifdef __BIG_ENDIAN
+#define MFC_HASH(a,b) ((((a)>>24)^((b)>>26))&(MFC_LINES-1))
+#else
+#define MFC_HASH(a,b) (((a)^((b)>>2))&(MFC_LINES-1))
+#endif
+
+#endif
+
+
+#define MFC_ASSERT_THRESH (3*HZ) /* Maximal freq. of asserts */
+
+/*
+ * Pseudo messages used by mrouted
+ */
+
+#define IGMPMSG_NOCACHE 1 /* Kern cache fill request to mrouted */
+#define IGMPMSG_WRONGVIF 2 /* For PIM assert processing (unused) */
+#define IGMPMSG_WHOLEPKT 3 /* For PIM Register processing */
+
+#ifdef __KERNEL__
+
+#define PIM_V1_VERSION __constant_htonl(0x10000000)
+#define PIM_V1_REGISTER 1
+
+#define PIM_VERSION 2
+#define PIM_REGISTER 1
+
+#define PIM_NULL_REGISTER __constant_htonl(0x40000000)
+
+/* PIMv2 register message header layout (ietf-draft-idmr-pimvsm-v2-00.ps */
+
+struct pimreghdr
+{
+ __u8 type;
+ __u8 reserved;
+ __u16 csum;
+ __u32 flags;
+};
+
+extern int pim_rcv(struct sk_buff * , unsigned short);
+extern int pim_rcv_v1(struct sk_buff * , unsigned short len);
+
+struct rtmsg;
+extern int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait);
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/msdos_fs.h b/pfinet/linux-src/include/linux/msdos_fs.h
new file mode 100644
index 00000000..f9ef19e8
--- /dev/null
+++ b/pfinet/linux-src/include/linux/msdos_fs.h
@@ -0,0 +1,336 @@
+#ifndef _LINUX_MSDOS_FS_H
+#define _LINUX_MSDOS_FS_H
+
+/*
+ * The MS-DOS filesystem constants/structures
+ */
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/fd.h>
+
+#include <asm/byteorder.h>
+
+#define MSDOS_ROOT_INO 1 /* == MINIX_ROOT_INO */
+#define SECTOR_SIZE 512 /* sector size (bytes) */
+#define SECTOR_BITS 9 /* log2(SECTOR_SIZE) */
+#define MSDOS_DPB (MSDOS_DPS) /* dir entries per block */
+#define MSDOS_DPB_BITS 4 /* log2(MSDOS_DPB) */
+#define MSDOS_DPS (SECTOR_SIZE/sizeof(struct msdos_dir_entry))
+#define MSDOS_DPS_BITS 4 /* log2(MSDOS_DPS) */
+#define MSDOS_DIR_BITS 5 /* log2(sizeof(struct msdos_dir_entry)) */
+
+#define MSDOS_SUPER_MAGIC 0x4d44 /* MD */
+
+#define FAT_CACHE 8 /* FAT cache size */
+
+#define MSDOS_MAX_EXTRA 3 /* tolerate up to that number of clusters which are
+ inaccessible because the FAT is too short */
+
+#define ATTR_RO 1 /* read-only */
+#define ATTR_HIDDEN 2 /* hidden */
+#define ATTR_SYS 4 /* system */
+#define ATTR_VOLUME 8 /* volume label */
+#define ATTR_DIR 16 /* directory */
+#define ATTR_ARCH 32 /* archived */
+
+#define ATTR_NONE 0 /* no attribute bits */
+#define ATTR_UNUSED (ATTR_VOLUME | ATTR_ARCH | ATTR_SYS | ATTR_HIDDEN)
+ /* attribute bits that are copied "as is" */
+#define ATTR_EXT (ATTR_RO | ATTR_HIDDEN | ATTR_SYS | ATTR_VOLUME)
+ /* bits that are used by the Windows 95/Windows NT extended FAT */
+
+#define ATTR_DIR_READ_BOTH 512 /* read both short and long names from the
+ * vfat filesystem. This is used by Samba
+ * to export the vfat filesystem with correct
+ * shortnames. */
+#define ATTR_DIR_READ_SHORT 1024
+
+#define CASE_LOWER_BASE 8 /* base is lower case */
+#define CASE_LOWER_EXT 16 /* extension is lower case */
+
+#define SCAN_ANY 0 /* either hidden or not */
+#define SCAN_HID 1 /* only hidden */
+#define SCAN_NOTHID 2 /* only not hidden */
+#define SCAN_NOTANY 3 /* test name, then use SCAN_HID or SCAN_NOTHID */
+
+#define DELETED_FLAG 0xe5 /* marks file as deleted when in name[0] */
+#define IS_FREE(n) (!*(n) || *(const unsigned char *) (n) == DELETED_FLAG || \
+ *(const unsigned char *) (n) == FD_FILL_BYTE)
+
+#define MSDOS_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)
+ /* valid file mode bits */
+
+#define MSDOS_SB(s) (&((s)->u.msdos_sb))
+#define MSDOS_I(i) (&((i)->u.msdos_i))
+
+#define MSDOS_NAME 11 /* maximum name length */
+#define MSDOS_LONGNAME 256 /* maximum name length */
+#define MSDOS_SLOTS 21 /* max # of slots needed for short and long names */
+#define MSDOS_DOT ". " /* ".", padded to MSDOS_NAME chars */
+#define MSDOS_DOTDOT ".. " /* "..", padded to MSDOS_NAME chars */
+
+#define MSDOS_FAT12 4078 /* maximum number of clusters in a 12 bit FAT */
+
+#define EOF_FAT12 0xFF8 /* standard EOF */
+#define EOF_FAT16 0xFFF8
+#define EOF_FAT32 0xFFFFFF8
+#define EOF_FAT(s) (MSDOS_SB(s)->fat_bits == 32 ? EOF_FAT32 : \
+ MSDOS_SB(s)->fat_bits == 16 ? EOF_FAT16 : EOF_FAT12)
+
+/*
+ * Inode flags
+ */
+#define FAT_BINARY_FL 0x00000001 /* File contains binary data */
+
+/*
+ * ioctl commands
+ */
+#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct dirent [2])
+#define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct dirent [2])
+
+/*
+ * Conversion from and to little-endian byte order. (no-op on i386/i486)
+ *
+ * Naming: Ca_b_c, where a: F = from, T = to, b: LE = little-endian,
+ * BE = big-endian, c: W = word (16 bits), L = longword (32 bits)
+ */
+
+#define CF_LE_W(v) le16_to_cpu(v)
+#define CF_LE_L(v) le32_to_cpu(v)
+#define CT_LE_W(v) cpu_to_le16(v)
+#define CT_LE_L(v) cpu_to_le32(v)
+
+struct fat_boot_sector {
+ __s8 ignored[3]; /* Boot strap short or near jump */
+ __s8 system_id[8]; /* Name - can be used to special case
+ partition manager volumes */
+ __u8 sector_size[2]; /* bytes per logical sector */
+ __u8 cluster_size; /* sectors/cluster */
+ __u16 reserved; /* reserved sectors */
+ __u8 fats; /* number of FATs */
+ __u8 dir_entries[2]; /* root directory entries */
+ __u8 sectors[2]; /* number of sectors */
+ __u8 media; /* media code (unused) */
+ __u16 fat_length; /* sectors/FAT */
+ __u16 secs_track; /* sectors per track */
+ __u16 heads; /* number of heads */
+ __u32 hidden; /* hidden sectors (unused) */
+ __u32 total_sect; /* number of sectors (if sectors == 0) */
+
+ /* The following fields are only used by FAT32 */
+ __u32 fat32_length; /* sectors/FAT */
+ __u16 flags; /* bit 8: fat mirroring, low 4: active fat */
+ __u8 version[2]; /* major, minor filesystem version */
+ __u32 root_cluster; /* first cluster in root directory */
+ __u16 info_sector; /* filesystem info sector */
+ __u16 backup_boot; /* backup boot sector */
+ __u16 reserved2[6]; /* Unused */
+};
+
+struct fat_boot_fsinfo {
+ __u32 reserved1; /* Nothing as far as I can tell */
+ __u32 signature; /* 0x61417272L */
+ __u32 free_clusters; /* Free cluster count. -1 if unknown */
+ __u32 next_cluster; /* Most recently allocated cluster.
+ * Unused under Linux. */
+ __u32 reserved2[4];
+};
+
+struct msdos_dir_entry {
+ __s8 name[8],ext[3]; /* name and extension */
+ __u8 attr; /* attribute bits */
+ __u8 lcase; /* Case for base and extension */
+ __u8 ctime_ms; /* Creation time, milliseconds */
+ __u16 ctime; /* Creation time */
+ __u16 cdate; /* Creation date */
+ __u16 adate; /* Last access date */
+ __u16 starthi; /* High 16 bits of cluster in FAT32 */
+ __u16 time,date,start;/* time, date and first cluster */
+ __u32 size; /* file size (in bytes) */
+};
+
+/* Up to 13 characters of the name */
+struct msdos_dir_slot {
+ __u8 id; /* sequence number for slot */
+ __u8 name0_4[10]; /* first 5 characters in name */
+ __u8 attr; /* attribute byte */
+ __u8 reserved; /* always 0 */
+ __u8 alias_checksum; /* checksum for 8.3 alias */
+ __u8 name5_10[12]; /* 6 more characters in name */
+ __u16 start; /* starting cluster number, 0 in long slots */
+ __u8 name11_12[4]; /* last 2 characters in name */
+};
+
+struct vfat_slot_info {
+ int is_long; /* was the found entry long */
+ int long_slots; /* number of long slots in filename */
+ int total_slots; /* total slots (long and short) */
+ loff_t longname_offset; /* dir offset for longname start */
+ loff_t shortname_offset; /* dir offset for shortname start */
+ int ino; /* ino for the file */
+};
+
+/* Determine whether this FS has kB-aligned data. */
+#define MSDOS_CAN_BMAP(mib) (!(((mib)->cluster_size & 1) || \
+ ((mib)->data_start & 1)))
+
+/* Convert attribute bits and a mask to the UNIX mode. */
+#define MSDOS_MKMODE(a,m) (m & (a & ATTR_RO ? S_IRUGO|S_IXUGO : S_IRWXUGO))
+
+/* Convert the UNIX mode to MS-DOS attribute bits. */
+#define MSDOS_MKATTR(m) ((m & S_IWUGO) ? ATTR_NONE : ATTR_RO)
+
+
+#ifdef __KERNEL__
+
+struct fat_cache {
+ kdev_t device; /* device number. 0 means unused. */
+ int start_cluster; /* first cluster of the chain. */
+ int file_cluster; /* cluster number in the file. */
+ int disk_cluster; /* cluster number on disk. */
+ struct fat_cache *next; /* next cache entry */
+};
+
+/* misc.c */
+extern int fat_is_binary(char conversion,char *extension);
+extern void lock_fat(struct super_block *sb);
+extern void unlock_fat(struct super_block *sb);
+extern int fat_add_cluster(struct inode *inode);
+extern struct buffer_head *fat_add_cluster1(struct inode *inode);
+extern int date_dos2unix(__u16 time, __u16 date);
+extern void fat_fs_panic(struct super_block *s,const char *msg);
+extern void fat_lock_creation(void);
+extern void fat_unlock_creation(void);
+extern void fat_date_unix2dos(int unix_date,__u16 *time, __u16 *date);
+extern int fat__get_entry(struct inode *dir,loff_t *pos,struct buffer_head **bh,
+ struct msdos_dir_entry **de,int *ino);
+static __inline__ int fat_get_entry(struct inode *dir,loff_t *pos,
+ struct buffer_head **bh,struct msdos_dir_entry **de,int *ino)
+{
+ /* Fast stuff first */
+ if (*bh && *de &&
+ (*de - (struct msdos_dir_entry *)(*bh)->b_data) < MSDOS_DPB-1) {
+ *pos += sizeof(struct msdos_dir_entry);
+ (*de)++;
+ (*ino)++;
+ return 0;
+ }
+ return fat__get_entry(dir,pos,bh,de,ino);
+}
+extern int fat_scan(struct inode *dir,const char *name,struct buffer_head **res_bh,
+ struct msdos_dir_entry **res_de,int *ino);
+extern int fat_parent_ino(struct inode *dir,int locked);
+extern int fat_subdirs(struct inode *dir);
+void fat_clusters_flush(struct super_block *sb);
+
+/* fat.c */
+extern int fat_access(struct super_block *sb,int nr,int new_value);
+extern int fat_smap(struct inode *inode,int sector);
+extern int fat_free(struct inode *inode,int skip);
+void fat_cache_inval_inode(struct inode *inode);
+void fat_cache_inval_dev(kdev_t device);
+extern void fat_cache_init(void);
+void fat_cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu);
+void fat_cache_add(struct inode *inode,int f_clu,int d_clu);
+int fat_get_cluster(struct inode *inode,int cluster);
+
+/* inode.c */
+extern void fat_hash_init(void);
+extern int fat_bmap(struct inode *inode,int block);
+extern int fat_notify_change(struct dentry *, struct iattr *);
+extern void fat_clear_inode(struct inode *inode);
+extern void fat_delete_inode(struct inode *inode);
+extern void fat_put_super(struct super_block *sb);
+extern void fat_attach(struct inode *inode, int ino);
+extern void fat_detach(struct inode *inode);
+extern struct inode *fat_iget(struct super_block*,int);
+extern struct inode *fat_build_inode(struct super_block*,struct msdos_dir_entry*,int,int*);
+extern struct super_block *fat_read_super(struct super_block *s, void *data, int silent, struct inode_operations *dir_ops);
+extern void msdos_put_super(struct super_block *sb);
+extern int fat_statfs(struct super_block *sb,struct statfs *buf, int);
+extern void fat_write_inode(struct inode *inode);
+
+/* dir.c */
+extern struct file_operations fat_dir_operations;
+extern int fat_search_long(struct inode *dir, const char *name, int len,
+ int anycase, loff_t *spos, loff_t *lpos);
+extern int fat_readdir(struct file *filp,
+ void *dirent, filldir_t);
+extern int fat_dir_ioctl(struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long arg);
+int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh,
+ struct msdos_dir_entry **de, int *ino);
+int fat_dir_empty(struct inode *dir);
+
+/* file.c */
+extern struct inode_operations fat_file_inode_operations;
+extern struct inode_operations fat_file_inode_operations_1024;
+extern struct inode_operations fat_file_inode_operations_readpage;
+extern ssize_t fat_file_read(struct file *, char *, size_t, loff_t *);
+extern ssize_t fat_file_write(struct file *, const char *, size_t, loff_t *);
+extern void fat_truncate(struct inode *inode);
+
+/* mmap.c */
+extern int fat_mmap(struct file *, struct vm_area_struct *);
+extern int fat_readpage(struct file *, struct page *);
+
+
+/* vfat.c */
+extern int init_vfat_fs(void);
+
+
+/* msdosfs_syms.c */
+extern int init_msdos_fs(void);
+extern struct file_system_type msdos_fs_type;
+
+/* msdos.c */
+extern struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent);
+
+/* msdos.c - these are for Umsdos */
+extern void msdos_read_inode(struct inode *inode);
+extern struct dentry *msdos_lookup(struct inode *dir,struct dentry *);
+extern int msdos_create(struct inode *dir,struct dentry *dentry,int mode);
+extern int msdos_rmdir(struct inode *dir,struct dentry *dentry);
+extern int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode);
+extern int msdos_unlink(struct inode *dir,struct dentry *dentry);
+extern int msdos_rename(struct inode *old_dir,struct dentry *old_dentry,
+ struct inode *new_dir,struct dentry *new_dentry);
+
+/* nls.c */
+extern int init_fat_nls(void);
+extern struct fat_nls_table *fat_load_nls(int codepage);
+
+/* tables.c */
+extern unsigned char fat_uni2esc[];
+extern unsigned char fat_esc2uni[];
+
+/* fatfs_syms.c */
+extern int init_fat_fs(void);
+extern void cleanup_fat_fs(void);
+
+/* nls.c */
+extern int fat_register_nls(struct fat_nls_table * fmt);
+extern int fat_unregister_nls(struct fat_nls_table * fmt);
+extern struct fat_nls_table *fat_find_nls(int codepage);
+extern struct fat_nls_table *fat_load_nls(int codepage);
+extern void fat_unload_nls(int codepage);
+extern int init_fat_nls(void);
+
+/* vfat/namei.c - these are for dmsdos */
+extern int vfat_create(struct inode *dir,struct dentry *dentry,int mode);
+extern int vfat_unlink(struct inode *dir,struct dentry *dentry);
+extern int vfat_mkdir(struct inode *dir,struct dentry *dentry,int mode);
+extern int vfat_rmdir(struct inode *dir,struct dentry *dentry);
+extern int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
+ struct inode *new_dir,struct dentry *new_dentry);
+extern struct super_block *vfat_read_super(struct super_block *sb,void *data,
+ int silent);
+extern void vfat_read_inode(struct inode *inode);
+extern struct dentry *vfat_lookup(struct inode *dir,struct dentry *);
+
+/* vfat/vfatfs_syms.c */
+extern struct file_system_type vfat_fs_type;
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/msdos_fs_i.h b/pfinet/linux-src/include/linux/msdos_fs_i.h
new file mode 100644
index 00000000..929de3db
--- /dev/null
+++ b/pfinet/linux-src/include/linux/msdos_fs_i.h
@@ -0,0 +1,39 @@
+#ifndef _MSDOS_FS_I
+#define _MSDOS_FS_I
+
+#ifndef _LINUX_PIPE_FS_I_H
+#include <linux/pipe_fs_i.h>
+#endif
+
+/*
+ * MS-DOS file system inode data in memory
+ */
+
+struct msdos_inode_info {
+ /*
+ UMSDOS manage special file and fifo as normal empty
+ msdos file. fifo inode processing conflict with msdos
+ processing. So I insert the pipe_inode_info so the
+ information does not overlap. This increases the size of
+ the msdos_inode_info, but the clear winner here is
+ the ext2_inode_info. So it does not change anything to
+ the total size of a struct inode.
+
+ I have not put it conditional. With the advent of loadable
+ file system drivers, it would be very easy to compile
+ a MS-DOS FS driver unaware of UMSDOS and then later to
+ load a (then incompatible) UMSDOS FS driver.
+ */
+ struct pipe_inode_info reserved;
+ int i_start; /* first cluster or 0 */
+ int i_logstart; /* logical first cluster */
+ int i_attrs; /* unused attribute bits */
+ int i_ctime_ms; /* unused change time in milliseconds */
+ int i_binary; /* file contains non-text data */
+ int i_location; /* on-disk position of directory entry or 0 */
+ struct inode *i_fat_inode; /* struct inode of this one */
+ struct list_head i_fat_hash; /* hash by i_location */
+ off_t i_last_pos;/* position of last lookup */
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/msdos_fs_sb.h b/pfinet/linux-src/include/linux/msdos_fs_sb.h
new file mode 100644
index 00000000..ae86a980
--- /dev/null
+++ b/pfinet/linux-src/include/linux/msdos_fs_sb.h
@@ -0,0 +1,59 @@
+#ifndef _MSDOS_FS_SB
+#define _MSDOS_FS_SB
+#include<linux/fat_cvf.h>
+
+/*
+ * MS-DOS file system in-core superblock data
+ */
+
+struct fat_mount_options {
+ uid_t fs_uid;
+ gid_t fs_gid;
+ unsigned short fs_umask;
+ unsigned short codepage; /* Codepage for shortname conversions */
+ char *iocharset; /* Charset used for filename input/display */
+ unsigned char name_check; /* r = relaxed, n = normal, s = strict */
+ unsigned char conversion; /* b = binary, t = text, a = auto */
+ unsigned quiet:1, /* set = fake successful chmods and chowns */
+ showexec:1, /* set = only set x bit for com/exe/bat */
+ sys_immutable:1, /* set = system files are immutable */
+ dotsOK:1, /* set = hidden and system files are named '.filename' */
+ isvfat:1, /* 0=no vfat long filename support, 1=vfat support */
+ utf8:1, /* Use of UTF8 character set (Default) */
+ unicode_xlate:1, /* create escape sequences for unhandled Unicode */
+ posixfs:1, /* Allow names like makefile and Makefile to coexist */
+ numtail:1, /* Does first alias have a numeric '~1' type tail? */
+ atari:1, /* Use Atari GEMDOS variation of MS-DOS fs */
+ fat32:1; /* Is this a FAT32 partition? */
+};
+
+struct vfat_unicode {
+ unsigned char uni1;
+ unsigned char uni2;
+};
+
+struct msdos_sb_info {
+ unsigned short cluster_size; /* sectors/cluster */
+ unsigned char fats,fat_bits; /* number of FATs, FAT bits (12 or 16) */
+ unsigned short fat_start;
+ unsigned long fat_length; /* FAT start & length (sec.) */
+ unsigned long dir_start;
+ unsigned short dir_entries; /* root dir start & entries */
+ unsigned long data_start; /* first data sector */
+ unsigned long clusters; /* number of clusters */
+ unsigned long root_cluster; /* first cluster of the root directory */
+ unsigned long fsinfo_offset; /* FAT32 fsinfo offset from start of disk */
+ struct wait_queue *fat_wait;
+ int fat_lock;
+ int prev_free; /* previously returned free cluster number */
+ int free_clusters; /* -1 if undefined */
+ struct fat_mount_options options;
+ struct nls_table *nls_disk; /* Codepage used on disk */
+ struct nls_table *nls_io; /* Charset used for input and display */
+ struct cvf_format* cvf_format;
+ void *dir_ops; /* Opaque; default directory operations */
+ void (*put_super_callback)(struct super_block *);
+ void *private_data;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/msg.h b/pfinet/linux-src/include/linux/msg.h
new file mode 100644
index 00000000..0312b5de
--- /dev/null
+++ b/pfinet/linux-src/include/linux/msg.h
@@ -0,0 +1,81 @@
+#ifndef _LINUX_MSG_H
+#define _LINUX_MSG_H
+
+#include <linux/ipc.h>
+
+/* ipcs ctl commands */
+#define MSG_STAT 11
+#define MSG_INFO 12
+
+/* msgrcv options */
+#define MSG_NOERROR 010000 /* no error if message is too big */
+#define MSG_EXCEPT 020000 /* recv any msg except of specified type.*/
+
+/* one msqid structure for each queue on the system */
+struct msqid_ds {
+ struct ipc_perm msg_perm;
+ struct msg *msg_first; /* first message on queue */
+ struct msg *msg_last; /* last message in queue */
+ __kernel_time_t msg_stime; /* last msgsnd time */
+ __kernel_time_t msg_rtime; /* last msgrcv time */
+ __kernel_time_t msg_ctime; /* last change time */
+ struct wait_queue *wwait;
+ struct wait_queue *rwait;
+ unsigned short msg_cbytes; /* current number of bytes on queue */
+ unsigned short msg_qnum; /* number of messages in queue */
+ unsigned short msg_qbytes; /* max number of bytes on queue */
+ __kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
+ __kernel_ipc_pid_t msg_lrpid; /* last receive pid */
+};
+
+/* message buffer for msgsnd and msgrcv calls */
+struct msgbuf {
+ long mtype; /* type of message */
+ char mtext[1]; /* message text */
+};
+
+/* buffer for msgctl calls IPC_INFO, MSG_INFO */
+struct msginfo {
+ int msgpool;
+ int msgmap;
+ int msgmax;
+ int msgmnb;
+ int msgmni;
+ int msgssz;
+ int msgtql;
+ unsigned short msgseg;
+};
+
+#define MSGMNI 128 /* <= 1K */ /* max # of msg queue identifiers */
+#define MSGMAX 4056 /* <= 4056 */ /* max size of message (bytes) */
+#define MSGMNB 16384 /* ? */ /* default max size of a message queue */
+#define MSGQNUM 1024 /* <=65535 */ /* Max messages in flight / queue */
+
+/* unused */
+#define MSGPOOL (MSGMNI*MSGMNB/1024) /* size in kilobytes of message pool */
+#define MSGTQL MSGMNB /* number of system message headers */
+#define MSGMAP MSGMNB /* number of entries in message map */
+#define MSGSSZ 16 /* message segment size */
+#define __MSGSEG ((MSGPOOL*1024)/ MSGSSZ) /* max no. of segments */
+#define MSGSEG (__MSGSEG <= 0xffff ? __MSGSEG : 0xffff)
+
+#ifdef __KERNEL__
+
+/* one msg structure for each message */
+struct msg {
+ struct msg *msg_next; /* next message on queue */
+ long msg_type;
+ char *msg_spot; /* message text address */
+ time_t msg_stime; /* msgsnd time */
+ short msg_ts; /* message text size */
+};
+
+asmlinkage int sys_msgget (key_t key, int msgflg);
+asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);
+asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp,
+ int msgflg);
+asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_MSG_H */
diff --git a/pfinet/linux-src/include/linux/mtio.h b/pfinet/linux-src/include/linux/mtio.h
new file mode 100644
index 00000000..c794ed89
--- /dev/null
+++ b/pfinet/linux-src/include/linux/mtio.h
@@ -0,0 +1,373 @@
+/*
+ * linux/mtio.h header file for Linux. Written by H. Bergman
+ *
+ * Modified for special ioctls provided by zftape in September 1997
+ * by C.-J. Heine.
+ */
+
+#ifndef _LINUX_MTIO_H
+#define _LINUX_MTIO_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/qic117.h>
+
+/*
+ * Structures and definitions for mag tape io control commands
+ */
+
+/* structure for MTIOCTOP - mag tape op command */
+struct mtop {
+ short mt_op; /* operations defined below */
+ int mt_count; /* how many of them */
+};
+
+/* Magnetic Tape operations [Not all operations supported by all drivers]: */
+#define MTRESET 0 /* +reset drive in case of problems */
+#define MTFSF 1 /* forward space over FileMark,
+ * position at first record of next file
+ */
+#define MTBSF 2 /* backward space FileMark (position before FM) */
+#define MTFSR 3 /* forward space record */
+#define MTBSR 4 /* backward space record */
+#define MTWEOF 5 /* write an end-of-file record (mark) */
+#define MTREW 6 /* rewind */
+#define MTOFFL 7 /* rewind and put the drive offline (eject?) */
+#define MTNOP 8 /* no op, set status only (read with MTIOCGET) */
+#define MTRETEN 9 /* retension tape */
+#define MTBSFM 10 /* +backward space FileMark, position at FM */
+#define MTFSFM 11 /* +forward space FileMark, position at FM */
+#define MTEOM 12 /* goto end of recorded media (for appending files).
+ * MTEOM positions after the last FM, ready for
+ * appending another file.
+ */
+#define MTERASE 13 /* erase tape -- be careful! */
+
+#define MTRAS1 14 /* run self test 1 (nondestructive) */
+#define MTRAS2 15 /* run self test 2 (destructive) */
+#define MTRAS3 16 /* reserved for self test 3 */
+
+#define MTSETBLK 20 /* set block length (SCSI) */
+#define MTSETDENSITY 21 /* set tape density (SCSI) */
+#define MTSEEK 22 /* seek to block (Tandberg, etc.) */
+#define MTTELL 23 /* tell block (Tandberg, etc.) */
+#define MTSETDRVBUFFER 24 /* set the drive buffering according to SCSI-2 */
+ /* ordinary buffered operation with code 1 */
+#define MTFSS 25 /* space forward over setmarks */
+#define MTBSS 26 /* space backward over setmarks */
+#define MTWSM 27 /* write setmarks */
+
+#define MTLOCK 28 /* lock the drive door */
+#define MTUNLOCK 29 /* unlock the drive door */
+#define MTLOAD 30 /* execute the SCSI load command */
+#define MTUNLOAD 31 /* execute the SCSI unload command */
+#define MTCOMPRESSION 32/* control compression with SCSI mode page 15 */
+#define MTSETPART 33 /* Change the active tape partition */
+#define MTMKPART 34 /* Format the tape with one or two partitions */
+
+/* structure for MTIOCGET - mag tape get status command */
+
+struct mtget {
+ long mt_type; /* type of magtape device */
+ long mt_resid; /* residual count: (not sure)
+ * number of bytes ignored, or
+ * number of files not skipped, or
+ * number of records not skipped.
+ */
+ /* the following registers are device dependent */
+ long mt_dsreg; /* status register */
+ long mt_gstat; /* generic (device independent) status */
+ long mt_erreg; /* error register */
+ /* The next two fields are not always used */
+ __kernel_daddr_t mt_fileno; /* number of current file on tape */
+ __kernel_daddr_t mt_blkno; /* current block number */
+};
+
+
+
+/*
+ * Constants for mt_type. Not all of these are supported,
+ * and these are not all of the ones that are supported.
+ */
+#define MT_ISUNKNOWN 0x01
+#define MT_ISQIC02 0x02 /* Generic QIC-02 tape streamer */
+#define MT_ISWT5150 0x03 /* Wangtek 5150EQ, QIC-150, QIC-02 */
+#define MT_ISARCHIVE_5945L2 0x04 /* Archive 5945L-2, QIC-24, QIC-02? */
+#define MT_ISCMSJ500 0x05 /* CMS Jumbo 500 (QIC-02?) */
+#define MT_ISTDC3610 0x06 /* Tandberg 6310, QIC-24 */
+#define MT_ISARCHIVE_VP60I 0x07 /* Archive VP60i, QIC-02 */
+#define MT_ISARCHIVE_2150L 0x08 /* Archive Viper 2150L */
+#define MT_ISARCHIVE_2060L 0x09 /* Archive Viper 2060L */
+#define MT_ISARCHIVESC499 0x0A /* Archive SC-499 QIC-36 controller */
+#define MT_ISQIC02_ALL_FEATURES 0x0F /* Generic QIC-02 with all features */
+#define MT_ISWT5099EEN24 0x11 /* Wangtek 5099-een24, 60MB, QIC-24 */
+#define MT_ISTEAC_MT2ST 0x12 /* Teac MT-2ST 155mb drive, Teac DC-1 card (Wangtek type) */
+#define MT_ISEVEREX_FT40A 0x32 /* Everex FT40A (QIC-40) */
+#define MT_ISDDS1 0x51 /* DDS device without partitions */
+#define MT_ISDDS2 0x52 /* DDS device with partitions */
+#define MT_ISSCSI1 0x71 /* Generic ANSI SCSI-1 tape unit */
+#define MT_ISSCSI2 0x72 /* Generic ANSI SCSI-2 tape unit */
+
+/* QIC-40/80/3010/3020 ftape supported drives.
+ * 20bit vendor ID + 0x800000 (see ftape-vendors.h)
+ */
+#define MT_ISFTAPE_UNKNOWN 0x800000 /* obsolete */
+#define MT_ISFTAPE_FLAG 0x800000
+
+struct mt_tape_info {
+ long t_type; /* device type id (mt_type) */
+ char *t_name; /* descriptive name */
+};
+
+#define MT_TAPE_INFO { \
+ {MT_ISUNKNOWN, "Unknown type of tape device"}, \
+ {MT_ISQIC02, "Generic QIC-02 tape streamer"}, \
+ {MT_ISWT5150, "Wangtek 5150, QIC-150"}, \
+ {MT_ISARCHIVE_5945L2, "Archive 5945L-2"}, \
+ {MT_ISCMSJ500, "CMS Jumbo 500"}, \
+ {MT_ISTDC3610, "Tandberg TDC 3610, QIC-24"}, \
+ {MT_ISARCHIVE_VP60I, "Archive VP60i, QIC-02"}, \
+ {MT_ISARCHIVE_2150L, "Archive Viper 2150L"}, \
+ {MT_ISARCHIVE_2060L, "Archive Viper 2060L"}, \
+ {MT_ISARCHIVESC499, "Archive SC-499 QIC-36 controller"}, \
+ {MT_ISQIC02_ALL_FEATURES, "Generic QIC-02 tape, all features"}, \
+ {MT_ISWT5099EEN24, "Wangtek 5099-een24, 60MB"}, \
+ {MT_ISTEAC_MT2ST, "Teac MT-2ST 155mb data cassette drive"}, \
+ {MT_ISEVEREX_FT40A, "Everex FT40A, QIC-40"}, \
+ {MT_ISSCSI1, "Generic SCSI-1 tape"}, \
+ {MT_ISSCSI2, "Generic SCSI-2 tape"}, \
+ {0, NULL} \
+}
+
+
+/* structure for MTIOCPOS - mag tape get position command */
+
+struct mtpos {
+ long mt_blkno; /* current block number */
+};
+
+
+/* structure for MTIOCGETCONFIG/MTIOCSETCONFIG primarily intended
+ * as an interim solution for QIC-02 until DDI is fully implemented.
+ */
+struct mtconfiginfo {
+ long mt_type; /* drive type */
+ long ifc_type; /* interface card type */
+ unsigned short irqnr; /* IRQ number to use */
+ unsigned short dmanr; /* DMA channel to use */
+ unsigned short port; /* IO port base address */
+
+ unsigned long debug; /* debugging flags */
+
+ unsigned have_dens:1;
+ unsigned have_bsf:1;
+ unsigned have_fsr:1;
+ unsigned have_bsr:1;
+ unsigned have_eod:1;
+ unsigned have_seek:1;
+ unsigned have_tell:1;
+ unsigned have_ras1:1;
+ unsigned have_ras2:1;
+ unsigned have_ras3:1;
+ unsigned have_qfa:1;
+
+ unsigned pad1:5;
+ char reserved[10];
+};
+
+/* structure for MTIOCVOLINFO, query information about the volume
+ * currently positioned at (zftape)
+ */
+struct mtvolinfo {
+ unsigned int mt_volno; /* vol-number */
+ unsigned int mt_blksz; /* blocksize used when recording */
+ unsigned int mt_rawsize; /* raw tape space consumed, in kb */
+ unsigned int mt_size; /* volume size after decompression, in kb */
+ unsigned int mt_cmpr:1; /* this volume has been compressed */
+};
+
+/* raw access to a floppy drive, read and write an arbitrary segment.
+ * For ftape/zftape to support formatting etc.
+ */
+#define MT_FT_RD_SINGLE 0
+#define MT_FT_RD_AHEAD 1
+#define MT_FT_WR_ASYNC 0 /* start tape only when all buffers are full */
+#define MT_FT_WR_MULTI 1 /* start tape, continue until buffers are empty */
+#define MT_FT_WR_SINGLE 2 /* write a single segment and stop afterwards */
+#define MT_FT_WR_DELETE 3 /* write deleted data marks, one segment at time */
+
+struct mtftseg
+{
+ unsigned mt_segno; /* the segment to read or write */
+ unsigned mt_mode; /* modes for read/write (sync/async etc.) */
+ int mt_result; /* result of r/w request, not of the ioctl */
+ void *mt_data; /* User space buffer: must be 29kb */
+};
+
+/* get tape capacity (ftape/zftape)
+ */
+struct mttapesize {
+ unsigned long mt_capacity; /* entire, uncompressed capacity
+ * of a cartridge
+ */
+ unsigned long mt_used; /* what has been used so far, raw
+ * uncompressed amount
+ */
+};
+
+/* possible values of the ftfmt_op field
+ */
+#define FTFMT_SET_PARMS 1 /* set software parms */
+#define FTFMT_GET_PARMS 2 /* get software parms */
+#define FTFMT_FORMAT_TRACK 3 /* start formatting a tape track */
+#define FTFMT_STATUS 4 /* monitor formatting a tape track */
+#define FTFMT_VERIFY 5 /* verify the given segment */
+
+struct ftfmtparms {
+ unsigned char ft_qicstd; /* QIC-40/QIC-80/QIC-3010/QIC-3020 */
+ unsigned char ft_fmtcode; /* Refer to the QIC specs */
+ unsigned char ft_fhm; /* floppy head max */
+ unsigned char ft_ftm; /* floppy track max */
+ unsigned short ft_spt; /* segments per track */
+ unsigned short ft_tpc; /* tracks per cartridge */
+};
+
+struct ftfmttrack {
+ unsigned int ft_track; /* track to format */
+ unsigned char ft_gap3; /* size of gap3, for FORMAT_TRK */
+};
+
+struct ftfmtstatus {
+ unsigned int ft_segment; /* segment currently being formatted */
+};
+
+struct ftfmtverify {
+ unsigned int ft_segment; /* segment to verify */
+ unsigned long ft_bsm; /* bsm as result of VERIFY cmd */
+};
+
+struct mtftformat {
+ unsigned int fmt_op; /* operation to perform */
+ union fmt_arg {
+ struct ftfmtparms fmt_parms; /* format parameters */
+ struct ftfmttrack fmt_track; /* ctrl while formatting */
+ struct ftfmtstatus fmt_status;
+ struct ftfmtverify fmt_verify; /* for verifying */
+ } fmt_arg;
+};
+
+struct mtftcmd {
+ unsigned int ft_wait_before; /* timeout to wait for drive to get ready
+ * before command is sent. Milliseconds
+ */
+ qic117_cmd_t ft_cmd; /* command to send */
+ unsigned char ft_parm_cnt; /* zero: no parm is sent. */
+ unsigned char ft_parms[3]; /* parameter(s) to send to
+ * the drive. The parms are nibbles
+ * driver sends cmd + 2 step pulses */
+ unsigned int ft_result_bits; /* if non zero, number of bits
+ * returned by the tape drive
+ */
+ unsigned int ft_result; /* the result returned by the tape drive*/
+ unsigned int ft_wait_after; /* timeout to wait for drive to get ready
+ * after command is sent. 0: don't wait */
+ int ft_status; /* status returned by ready wait
+ * undefined if timeout was 0.
+ */
+ int ft_error; /* error code if error status was set by
+ * command
+ */
+};
+
+/* mag tape io control commands */
+#define MTIOCTOP _IOW('m', 1, struct mtop) /* do a mag tape op */
+#define MTIOCGET _IOR('m', 2, struct mtget) /* get tape status */
+#define MTIOCPOS _IOR('m', 3, struct mtpos) /* get tape position */
+
+/* The next two are used by the QIC-02 driver for runtime reconfiguration.
+ * See tpqic02.h for struct mtconfiginfo.
+ */
+#define MTIOCGETCONFIG _IOR('m', 4, struct mtconfiginfo) /* get tape config */
+#define MTIOCSETCONFIG _IOW('m', 5, struct mtconfiginfo) /* set tape config */
+
+/* the next six are used by the floppy ftape drivers and its frontends
+ * sorry, but MTIOCTOP commands are write only.
+ */
+#define MTIOCRDFTSEG _IOWR('m', 6, struct mtftseg) /* read a segment */
+#define MTIOCWRFTSEG _IOWR('m', 7, struct mtftseg) /* write a segment */
+#define MTIOCVOLINFO _IOR('m', 8, struct mtvolinfo) /* info about volume */
+#define MTIOCGETSIZE _IOR('m', 9, struct mttapesize)/* get cartridge size*/
+#define MTIOCFTFORMAT _IOWR('m', 10, struct mtftformat) /* format ftape */
+#define MTIOCFTCMD _IOWR('m', 11, struct mtftcmd) /* send QIC-117 cmd */
+
+/* Generic Mag Tape (device independent) status macros for examining
+ * mt_gstat -- HP-UX compatible.
+ * There is room for more generic status bits here, but I don't
+ * know which of them are reserved. At least three or so should
+ * be added to make this really useful.
+ */
+#define GMT_EOF(x) ((x) & 0x80000000)
+#define GMT_BOT(x) ((x) & 0x40000000)
+#define GMT_EOT(x) ((x) & 0x20000000)
+#define GMT_SM(x) ((x) & 0x10000000) /* DDS setmark */
+#define GMT_EOD(x) ((x) & 0x08000000) /* DDS EOD */
+#define GMT_WR_PROT(x) ((x) & 0x04000000)
+/* #define GMT_ ? ((x) & 0x02000000) */
+#define GMT_ONLINE(x) ((x) & 0x01000000)
+#define GMT_D_6250(x) ((x) & 0x00800000)
+#define GMT_D_1600(x) ((x) & 0x00400000)
+#define GMT_D_800(x) ((x) & 0x00200000)
+/* #define GMT_ ? ((x) & 0x00100000) */
+/* #define GMT_ ? ((x) & 0x00080000) */
+#define GMT_DR_OPEN(x) ((x) & 0x00040000) /* door open (no tape) */
+/* #define GMT_ ? ((x) & 0x00020000) */
+#define GMT_IM_REP_EN(x) ((x) & 0x00010000) /* immediate report mode */
+/* 16 generic status bits unused */
+
+
+/* SCSI-tape specific definitions */
+/* Bitfield shifts in the status */
+#define MT_ST_BLKSIZE_SHIFT 0
+#define MT_ST_BLKSIZE_MASK 0xffffff
+#define MT_ST_DENSITY_SHIFT 24
+#define MT_ST_DENSITY_MASK 0xff000000
+
+#define MT_ST_SOFTERR_SHIFT 0
+#define MT_ST_SOFTERR_MASK 0xffff
+
+/* Bitfields for the MTSETDRVBUFFER ioctl */
+#define MT_ST_OPTIONS 0xf0000000
+#define MT_ST_BOOLEANS 0x10000000
+#define MT_ST_SETBOOLEANS 0x30000000
+#define MT_ST_CLEARBOOLEANS 0x40000000
+#define MT_ST_WRITE_THRESHOLD 0x20000000
+#define MT_ST_DEF_BLKSIZE 0x50000000
+#define MT_ST_DEF_OPTIONS 0x60000000
+#define MT_ST_TIMEOUTS 0x70000000
+#define MT_ST_SET_TIMEOUT (MT_ST_TIMEOUTS | 0x000000)
+#define MT_ST_SET_LONG_TIMEOUT (MT_ST_TIMEOUTS | 0x100000)
+
+#define MT_ST_BUFFER_WRITES 0x1
+#define MT_ST_ASYNC_WRITES 0x2
+#define MT_ST_READ_AHEAD 0x4
+#define MT_ST_DEBUGGING 0x8
+#define MT_ST_TWO_FM 0x10
+#define MT_ST_FAST_MTEOM 0x20
+#define MT_ST_AUTO_LOCK 0x40
+#define MT_ST_DEF_WRITES 0x80
+#define MT_ST_CAN_BSR 0x100
+#define MT_ST_NO_BLKLIMS 0x200
+#define MT_ST_CAN_PARTITIONS 0x400
+#define MT_ST_SCSI2LOGICAL 0x800
+#define MT_ST_SYSV 0x1000
+
+/* The mode parameters to be controlled. Parameter chosen with bits 20-28 */
+#define MT_ST_CLEAR_DEFAULT 0xfffff
+#define MT_ST_DEF_DENSITY (MT_ST_DEF_OPTIONS | 0x100000)
+#define MT_ST_DEF_COMPRESSION (MT_ST_DEF_OPTIONS | 0x200000)
+#define MT_ST_DEF_DRVBUFFER (MT_ST_DEF_OPTIONS | 0x300000)
+
+/* The offset for the arguments for the special HP changer load command. */
+#define MT_ST_HPLOADER_OFFSET 10000
+
+#endif /* _LINUX_MTIO_H */
diff --git a/pfinet/linux-src/include/linux/nbd.h b/pfinet/linux-src/include/linux/nbd.h
new file mode 100644
index 00000000..c2c0431f
--- /dev/null
+++ b/pfinet/linux-src/include/linux/nbd.h
@@ -0,0 +1,85 @@
+#ifndef LINUX_NBD_H
+#define LINUX_NBD_H
+
+#define NBD_SET_SOCK _IO( 0xab, 0 )
+#define NBD_SET_BLKSIZE _IO( 0xab, 1 )
+#define NBD_SET_SIZE _IO( 0xab, 2 )
+#define NBD_DO_IT _IO( 0xab, 3 )
+#define NBD_CLEAR_SOCK _IO( 0xab, 4 )
+#define NBD_CLEAR_QUE _IO( 0xab, 5 )
+#define NBD_PRINT_DEBUG _IO( 0xab, 6 )
+#define NBD_SET_SIZE_BLOCKS _IO( 0xab, 7 )
+
+#ifdef MAJOR_NR
+
+#include <linux/locks.h>
+#include <asm/semaphore.h>
+
+#define LOCAL_END_REQUEST
+
+#include <linux/blk.h>
+
+#ifdef PARANOIA
+extern int requests_in;
+extern int requests_out;
+#endif
+
+static void
+nbd_end_request(struct request *req)
+{
+#ifdef PARANOIA
+ requests_out++;
+#endif
+ if (end_that_request_first( req, !req->errors, "nbd" ))
+ return;
+ end_that_request_last( req );
+}
+
+#define MAX_NBD 128
+
+struct nbd_device {
+ int refcnt;
+ int flags;
+ int harderror; /* Code of hard error */
+#define NBD_READ_ONLY 0x0001
+#define NBD_WRITE_NOCHK 0x0002
+#define NBD_INITIALISED 0x0004
+ struct socket * sock;
+ struct file * file; /* If == NULL, device is not ready, yet */
+ int magic; /* FIXME: not if debugging is off */
+ struct request *head; /* Requests are added here... */
+ struct request *tail;
+ struct semaphore queue_lock;
+};
+#endif
+
+/* This now IS in some kind of include file... */
+
+/* These are send over network in request/reply magic field */
+
+#define NBD_REQUEST_MAGIC 0x25609513
+#define NBD_REPLY_MAGIC 0x67446698
+/* Do *not* use magics: 0x12560953 0x96744668. */
+
+/*
+ * This is packet used for communication between client and
+ * server. All data are in network byte order.
+ */
+struct nbd_request {
+ u32 magic;
+ u32 type; /* == READ || == WRITE */
+ char handle[8];
+ u64 from;
+ u32 len;
+}
+#ifdef __GNUC__
+ __attribute__ ((packed))
+#endif
+;
+
+struct nbd_reply {
+ u32 magic;
+ u32 error; /* 0 = ok, else error */
+ char handle[8]; /* handle you got from request */
+};
+#endif
diff --git a/pfinet/linux-src/include/linux/ncp.h b/pfinet/linux-src/include/linux/ncp.h
new file mode 100644
index 00000000..229618db
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ncp.h
@@ -0,0 +1,204 @@
+/*
+ * ncp.h
+ *
+ * Copyright (C) 1995 by Volker Lendecke
+ * Modified for sparc by J.F. Chadima
+ * Modified for __constant_ntoh by Frank A. Vorstenbosch
+ *
+ */
+
+#ifndef _LINUX_NCP_H
+#define _LINUX_NCP_H
+
+#include <linux/types.h>
+#include <linux/ipx.h>
+
+#define NCP_PTYPE (0x11)
+#define NCP_PORT (0x0451)
+
+#define NCP_ALLOC_SLOT_REQUEST (0x1111)
+#define NCP_REQUEST (0x2222)
+#define NCP_DEALLOC_SLOT_REQUEST (0x5555)
+
+struct ncp_request_header {
+ __u16 type __attribute__((packed));
+ __u8 sequence __attribute__((packed));
+ __u8 conn_low __attribute__((packed));
+ __u8 task __attribute__((packed));
+ __u8 conn_high __attribute__((packed));
+ __u8 function __attribute__((packed));
+ __u8 data[0] __attribute__((packed));
+};
+
+#define NCP_REPLY (0x3333)
+#define NCP_POSITIVE_ACK (0x9999)
+
+struct ncp_reply_header {
+ __u16 type __attribute__((packed));
+ __u8 sequence __attribute__((packed));
+ __u8 conn_low __attribute__((packed));
+ __u8 task __attribute__((packed));
+ __u8 conn_high __attribute__((packed));
+ __u8 completion_code __attribute__((packed));
+ __u8 connection_state __attribute__((packed));
+ __u8 data[0] __attribute__((packed));
+};
+
+#define NCP_VOLNAME_LEN (16)
+#define NCP_NUMBER_OF_VOLUMES (64)
+struct ncp_volume_info {
+ __u32 total_blocks;
+ __u32 free_blocks;
+ __u32 purgeable_blocks;
+ __u32 not_yet_purgeable_blocks;
+ __u32 total_dir_entries;
+ __u32 available_dir_entries;
+ __u8 sectors_per_block;
+ char volume_name[NCP_VOLNAME_LEN + 1];
+};
+
+/* these define the attribute byte as seen by NCP */
+#define aRONLY (ntohl(0x01000000))
+#define aHIDDEN (__constant_ntohl(0x02000000))
+#define aSYSTEM (__constant_ntohl(0x04000000))
+#define aEXECUTE (ntohl(0x08000000))
+#define aDIR (ntohl(0x10000000))
+#define aARCH (ntohl(0x20000000))
+#define aSHARED (ntohl(0x80000000))
+#define aDONTSUBALLOCATE (ntohl(1L<<(11+8)))
+#define aTRANSACTIONAL (ntohl(1L<<(12+8)))
+#define aPURGE (ntohl(1L<<(16-8)))
+#define aRENAMEINHIBIT (ntohl(1L<<(17-8)))
+#define aDELETEINHIBIT (ntohl(1L<<(18-8)))
+#define aDONTCOMPRESS (nothl(1L<<(27-24)))
+
+#define AR_READ (ntohs(0x0100))
+#define AR_WRITE (ntohs(0x0200))
+#define AR_EXCLUSIVE (ntohs(0x2000))
+
+#define NCP_FILE_ID_LEN 6
+
+/* Defines for Name Spaces */
+#define NW_NS_DOS 0
+#define NW_NS_MAC 1
+#define NW_NS_NFS 2
+#define NW_NS_FTAM 3
+#define NW_NS_OS2 4
+
+/* Defines for ReturnInformationMask */
+#define RIM_NAME (ntohl(0x01000000L))
+#define RIM_SPACE_ALLOCATED (ntohl(0x02000000L))
+#define RIM_ATTRIBUTES (ntohl(0x04000000L))
+#define RIM_DATA_SIZE (ntohl(0x08000000L))
+#define RIM_TOTAL_SIZE (ntohl(0x10000000L))
+#define RIM_EXT_ATTR_INFO (ntohl(0x20000000L))
+#define RIM_ARCHIVE (ntohl(0x40000000L))
+#define RIM_MODIFY (ntohl(0x80000000L))
+#define RIM_CREATION (ntohl(0x00010000L))
+#define RIM_OWNING_NAMESPACE (ntohl(0x00020000L))
+#define RIM_DIRECTORY (ntohl(0x00040000L))
+#define RIM_RIGHTS (ntohl(0x00080000L))
+#define RIM_ALL (ntohl(0xFF0F0000L))
+#define RIM_COMPRESSED_INFO (ntohl(0x00000080L))
+
+/* open/create modes */
+#define OC_MODE_OPEN 0x01
+#define OC_MODE_TRUNCATE 0x02
+#define OC_MODE_REPLACE 0x02
+#define OC_MODE_CREATE 0x08
+
+/* open/create results */
+#define OC_ACTION_NONE 0x00
+#define OC_ACTION_OPEN 0x01
+#define OC_ACTION_CREATE 0x02
+#define OC_ACTION_TRUNCATE 0x04
+#define OC_ACTION_REPLACE 0x04
+
+/* access rights attributes */
+#ifndef AR_READ_ONLY
+#define AR_READ_ONLY 0x0001
+#define AR_WRITE_ONLY 0x0002
+#define AR_DENY_READ 0x0004
+#define AR_DENY_WRITE 0x0008
+#define AR_COMPATIBILITY 0x0010
+#define AR_WRITE_THROUGH 0x0040
+#define AR_OPEN_COMPRESSED 0x0100
+#endif
+
+struct nw_info_struct {
+ __u32 spaceAlloc __attribute__((packed));
+ __u32 attributes __attribute__((packed));
+ __u16 flags __attribute__((packed));
+ __u32 dataStreamSize __attribute__((packed));
+ __u32 totalStreamSize __attribute__((packed));
+ __u16 numberOfStreams __attribute__((packed));
+ __u16 creationTime __attribute__((packed));
+ __u16 creationDate __attribute__((packed));
+ __u32 creatorID __attribute__((packed));
+ __u16 modifyTime __attribute__((packed));
+ __u16 modifyDate __attribute__((packed));
+ __u32 modifierID __attribute__((packed));
+ __u16 lastAccessDate __attribute__((packed));
+ __u16 archiveTime __attribute__((packed));
+ __u16 archiveDate __attribute__((packed));
+ __u32 archiverID __attribute__((packed));
+ __u16 inheritedRightsMask __attribute__((packed));
+ __u32 dirEntNum __attribute__((packed));
+ __u32 DosDirNum __attribute__((packed));
+ __u32 volNumber __attribute__((packed));
+ __u32 EADataSize __attribute__((packed));
+ __u32 EAKeyCount __attribute__((packed));
+ __u32 EAKeySize __attribute__((packed));
+ __u32 NSCreator __attribute__((packed));
+ __u8 nameLen __attribute__((packed));
+ __u8 entryName[256] __attribute__((packed));
+};
+
+/* modify mask - use with MODIFY_DOS_INFO structure */
+#define DM_ATTRIBUTES (ntohl(0x02000000L))
+#define DM_CREATE_DATE (ntohl(0x04000000L))
+#define DM_CREATE_TIME (ntohl(0x08000000L))
+#define DM_CREATOR_ID (ntohl(0x10000000L))
+#define DM_ARCHIVE_DATE (ntohl(0x20000000L))
+#define DM_ARCHIVE_TIME (ntohl(0x40000000L))
+#define DM_ARCHIVER_ID (ntohl(0x80000000L))
+#define DM_MODIFY_DATE (ntohl(0x00010000L))
+#define DM_MODIFY_TIME (ntohl(0x00020000L))
+#define DM_MODIFIER_ID (ntohl(0x00040000L))
+#define DM_LAST_ACCESS_DATE (ntohl(0x00080000L))
+#define DM_INHERITED_RIGHTS_MASK (ntohl(0x00100000L))
+#define DM_MAXIMUM_SPACE (ntohl(0x00200000L))
+
+struct nw_modify_dos_info {
+ __u32 attributes __attribute__((packed));
+ __u16 creationDate __attribute__((packed));
+ __u16 creationTime __attribute__((packed));
+ __u32 creatorID __attribute__((packed));
+ __u16 modifyDate __attribute__((packed));
+ __u16 modifyTime __attribute__((packed));
+ __u32 modifierID __attribute__((packed));
+ __u16 archiveDate __attribute__((packed));
+ __u16 archiveTime __attribute__((packed));
+ __u32 archiverID __attribute__((packed));
+ __u16 lastAccessDate __attribute__((packed));
+ __u16 inheritanceGrantMask __attribute__((packed));
+ __u16 inheritanceRevokeMask __attribute__((packed));
+ __u32 maximumSpace __attribute__((packed));
+};
+
+struct nw_file_info {
+ struct nw_info_struct i;
+ int opened;
+ int access;
+ __u32 server_file_handle __attribute__((packed));
+ __u8 open_create_action __attribute__((packed));
+ __u8 file_handle[6] __attribute__((packed));
+};
+
+struct nw_search_sequence {
+ __u8 volNumber __attribute__((packed));
+ __u32 dirBase __attribute__((packed));
+ __u32 sequence __attribute__((packed));
+};
+
+#endif /* _LINUX_NCP_H */
diff --git a/pfinet/linux-src/include/linux/ncp_fs.h b/pfinet/linux-src/include/linux/ncp_fs.h
new file mode 100644
index 00000000..9c5df534
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ncp_fs.h
@@ -0,0 +1,331 @@
+/*
+ * ncp_fs.h
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_NCP_FS_H
+#define _LINUX_NCP_FS_H
+
+#include <linux/fs.h>
+#include <linux/in.h>
+#include <linux/types.h>
+
+#include <linux/ncp_mount.h>
+
+/* NLS charsets by ioctl */
+#define NCP_IOCSNAME_LEN 20
+struct ncp_nls_ioctl
+{
+ unsigned char codepage[NCP_IOCSNAME_LEN+1];
+ unsigned char iocharset[NCP_IOCSNAME_LEN+1];
+};
+
+#include <linux/ncp_fs_sb.h>
+#include <linux/ncp_fs_i.h>
+
+/*
+ * ioctl commands
+ */
+
+struct ncp_ioctl_request {
+ unsigned int function;
+ unsigned int size;
+ char *data;
+};
+
+struct ncp_fs_info {
+ int version;
+ struct sockaddr_ipx addr;
+ __kernel_uid_t mounted_uid;
+ int connection; /* Connection number the server assigned us */
+ int buffer_size; /* The negotiated buffer size, to be
+ used for read/write requests! */
+
+ int volume_number;
+ __u32 directory_id;
+};
+
+struct ncp_sign_init
+{
+ char sign_root[8];
+ char sign_last[16];
+};
+
+struct ncp_lock_ioctl
+{
+#define NCP_LOCK_LOG 0
+#define NCP_LOCK_SH 1
+#define NCP_LOCK_EX 2
+#define NCP_LOCK_CLEAR 256
+ int cmd;
+ int origin;
+ unsigned int offset;
+ unsigned int length;
+#define NCP_LOCK_DEFAULT_TIMEOUT 18
+#define NCP_LOCK_MAX_TIMEOUT 180
+ int timeout;
+};
+
+struct ncp_setroot_ioctl
+{
+ int volNumber;
+ int namespace;
+ __u32 dirEntNum;
+};
+
+struct ncp_objectname_ioctl
+{
+#define NCP_AUTH_NONE 0x00
+#define NCP_AUTH_BIND 0x31
+#define NCP_AUTH_NDS 0x32
+ int auth_type;
+ size_t object_name_len;
+ void* object_name; /* an userspace data, in most cases user name */
+};
+
+struct ncp_privatedata_ioctl
+{
+ size_t len;
+ void* data; /* ~1000 for NDS */
+};
+
+#define NCP_IOC_NCPREQUEST _IOR('n', 1, struct ncp_ioctl_request)
+#define NCP_IOC_GETMOUNTUID _IOW('n', 2, __kernel_uid_t)
+
+#if 1
+#ifdef __KERNEL__
+/* remove after ncpfs-2.0.13 gets released or at the beginning of kernel-2.1. codefreeze */
+#define NCP_IOC_GETMOUNTUID_INT _IOW('n', 2, unsigned int)
+#endif
+#endif
+
+#define NCP_IOC_CONN_LOGGED_IN _IO('n', 3)
+
+#define NCP_GET_FS_INFO_VERSION (1)
+#define NCP_IOC_GET_FS_INFO _IOWR('n', 4, struct ncp_fs_info)
+
+#define NCP_IOC_SIGN_INIT _IOR('n', 5, struct ncp_sign_init)
+#define NCP_IOC_SIGN_WANTED _IOR('n', 6, int)
+#define NCP_IOC_SET_SIGN_WANTED _IOW('n', 6, int)
+
+#define NCP_IOC_LOCKUNLOCK _IOR('n', 7, struct ncp_lock_ioctl)
+
+#define NCP_IOC_GETROOT _IOW('n', 8, struct ncp_setroot_ioctl)
+#define NCP_IOC_SETROOT _IOR('n', 8, struct ncp_setroot_ioctl)
+
+#define NCP_IOC_GETOBJECTNAME _IOWR('n', 9, struct ncp_objectname_ioctl)
+#define NCP_IOC_SETOBJECTNAME _IOR('n', 9, struct ncp_objectname_ioctl)
+#define NCP_IOC_GETPRIVATEDATA _IOWR('n', 10, struct ncp_privatedata_ioctl)
+#define NCP_IOC_SETPRIVATEDATA _IOR('n', 10, struct ncp_privatedata_ioctl)
+
+#define NCP_IOC_GETCHARSETS _IOWR('n', 11, struct ncp_nls_ioctl)
+#define NCP_IOC_SETCHARSETS _IOR('n', 11, struct ncp_nls_ioctl)
+
+/*
+ * The packet size to allocate. One page should be enough.
+ */
+#define NCP_PACKET_SIZE 4070
+
+#define NCP_MAXPATHLEN 255
+#define NCP_MAXNAMELEN 14
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+
+#undef NCPFS_PARANOIA
+#ifndef DEBUG_NCP
+#define DEBUG_NCP 0
+#endif
+#if DEBUG_NCP > 0
+#define DPRINTK(format, args...) printk(format , ## args)
+#else
+#define DPRINTK(format, args...)
+#endif
+
+#if DEBUG_NCP > 1
+#define DDPRINTK(format, args...) printk(format , ## args)
+#else
+#define DDPRINTK(format, args...)
+#endif
+
+/* The readdir cache size controls how many directory entries are
+ * cached.
+ */
+#define NCP_READDIR_CACHE_SIZE 64
+
+#define NCP_MAX_RPC_TIMEOUT (6*HZ)
+
+/*
+ * This is the ncpfs part of the inode structure. This must contain
+ * all the information we need to work with an inode after creation.
+ * (Move to ncp_fs_i.h once it stabilizes, and add a union in fs.h)
+ */
+struct ncpfs_i {
+ __u32 dirEntNum __attribute__((packed));
+ __u32 DosDirNum __attribute__((packed));
+ __u32 volNumber __attribute__((packed));
+#ifdef CONFIG_NCPFS_SMALLDOS
+ __u32 origNS;
+#endif
+#ifdef CONFIG_NCPFS_STRONG
+ __u32 nwattr;
+#endif
+ int opened;
+ int access;
+ __u32 server_file_handle __attribute__((packed));
+ __u8 open_create_action __attribute__((packed));
+ __u8 file_handle[6] __attribute__((packed));
+};
+
+/*
+ * This is an extension of the nw_file_info structure with
+ * the additional information we need to create an inode.
+ */
+struct ncpfs_inode_info {
+ ino_t ino; /* dummy inode number */
+ struct nw_file_info nw_info;
+};
+
+/* Guess, what 0x564c is :-) */
+#define NCP_SUPER_MAGIC 0x564c
+
+
+#define NCP_SBP(sb) ((struct ncp_server *)((sb)->u.generic_sbp))
+
+#define NCP_SERVER(inode) NCP_SBP((inode)->i_sb)
+/* We don't have an ncpfs union yet, so use smbfs ... */
+#define NCP_FINFO(inode) ((struct ncpfs_i *)&((inode)->u.smbfs_i))
+
+#ifdef DEBUG_NCP_MALLOC
+
+#include <linux/malloc.h>
+
+extern int ncp_malloced;
+extern int ncp_current_malloced;
+
+static inline void *
+ ncp_kmalloc(unsigned int size, int priority)
+{
+ ncp_malloced += 1;
+ ncp_current_malloced += 1;
+ return kmalloc(size, priority);
+}
+
+static inline void ncp_kfree_s(void *obj, int size)
+{
+ ncp_current_malloced -= 1;
+ kfree_s(obj, size);
+}
+
+#else /* DEBUG_NCP_MALLOC */
+
+#define ncp_kmalloc(s,p) kmalloc(s,p)
+#define ncp_kfree_s(o,s) kfree_s(o,s)
+
+#endif /* DEBUG_NCP_MALLOC */
+
+/* linux/fs/ncpfs/inode.c */
+int ncp_notify_change(struct dentry *, struct iattr *attr);
+struct super_block *ncp_read_super(struct super_block *, void *, int);
+struct inode *ncp_iget(struct super_block *, struct ncpfs_inode_info *);
+void ncp_update_inode(struct inode *, struct nw_file_info *);
+void ncp_update_inode2(struct inode *, struct nw_file_info *);
+extern int init_ncp_fs(void);
+
+/* linux/fs/ncpfs/dir.c */
+extern struct inode_operations ncp_dir_inode_operations;
+int ncp_conn_logged_in(struct ncp_server *);
+void ncp_init_dir_cache(void);
+void ncp_invalid_dir_cache(struct inode *);
+void ncp_free_dir_cache(void);
+int ncp_date_dos2unix(__u16 time, __u16 date);
+void ncp_date_unix2dos(int unix_date, __u16 * time, __u16 * date);
+
+/* linux/fs/ncpfs/ioctl.c */
+int ncp_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+/* linux/fs/ncpfs/sock.c */
+int ncp_request2(struct ncp_server *server, int function,
+ void* reply, int max_reply_size);
+static int inline ncp_request(struct ncp_server *server, int function) {
+ return ncp_request2(server, function, server->packet, server->packet_size);
+}
+int ncp_connect(struct ncp_server *server);
+int ncp_disconnect(struct ncp_server *server);
+void ncp_lock_server(struct ncp_server *server);
+void ncp_unlock_server(struct ncp_server *server);
+
+/* linux/fs/ncpfs/file.c */
+extern struct inode_operations ncp_file_inode_operations;
+int ncp_make_open(struct inode *, int);
+
+/* linux/fs/ncpfs/mmap.c */
+int ncp_mmap(struct file *, struct vm_area_struct *);
+
+/* linux/fs/ncpfs/ncplib_kernel.c */
+int ncp_make_closed(struct inode *);
+
+static inline void str_upper(char *name)
+{
+ while (*name) {
+ if (*name >= 'a' && *name <= 'z') {
+ *name -= ('a' - 'A');
+ }
+ name++;
+ }
+}
+
+static inline void str_lower(char *name)
+{
+ while (*name) {
+ if (*name >= 'A' && *name <= 'Z') {
+ *name += ('a' - 'A');
+ }
+ name++;
+ }
+}
+
+static inline int ncp_namespace(struct inode *inode)
+{
+ struct ncp_server *server = NCP_SERVER(inode);
+ return server->name_space[NCP_FINFO(inode)->volNumber];
+}
+
+static inline int ncp_preserve_entry_case(struct inode *i, __u32 nscreator) {
+#if defined(CONFIG_NCPFS_NFS_NS) || defined(CONFIG_NCPFS_OS2_NS)
+ int ns = ncp_namespace(i);
+#endif
+#if defined(CONFIG_NCPFS_SMALLDOS) && defined(CONFIG_NCPFS_OS2_NS)
+ if ((ns == NW_NS_OS2) && (nscreator == NW_NS_DOS))
+ return 0;
+#endif
+ return
+#ifdef CONFIG_NCPFS_OS2_NS
+ (ns == NW_NS_OS2) ||
+#endif /* CONFIG_NCPFS_OS2_NS */
+#ifdef CONFIG_NCPFS_NFS_NS
+ (ns == NW_NS_NFS) ||
+#endif /* CONFIG_NCPFS_NFS_NS */
+ 0;
+}
+
+static inline int ncp_preserve_case(struct inode *i)
+{
+ return ncp_preserve_entry_case(i, NW_NS_OS2);
+}
+
+static inline int ncp_case_sensitive(struct inode *i)
+{
+#ifdef CONFIG_NCPFS_NFS_NS
+ return ncp_namespace(i) == NW_NS_NFS;
+#else
+ return 0;
+#endif /* CONFIG_NCPFS_NFS_NS */
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_NCP_FS_H */
diff --git a/pfinet/linux-src/include/linux/ncp_fs_i.h b/pfinet/linux-src/include/linux/ncp_fs_i.h
new file mode 100644
index 00000000..3df38b28
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ncp_fs_i.h
@@ -0,0 +1,36 @@
+/*
+ * ncp_fs_i.h
+ *
+ * Copyright (C) 1995 Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_NCP_FS_I
+#define _LINUX_NCP_FS_I
+
+#include <linux/ncp.h>
+
+#ifdef __KERNEL__
+
+enum ncp_inode_state {
+ NCP_INODE_VALID = 19, /* Inode currently in use */
+ NCP_INODE_LOOKED_UP, /* directly before iget */
+ NCP_INODE_CACHED, /* in a path to an inode which is in use */
+ NCP_INODE_INVALID
+};
+
+/*
+ * ncp fs inode data (in memory only)
+ */
+struct ncp_inode_info {
+ enum ncp_inode_state state;
+ int nused; /* for directories:
+ number of references in memory */
+ struct ncp_inode_info *dir;
+ struct ncp_inode_info *next, *prev;
+ struct inode *inode;
+ struct nw_file_info finfo;
+};
+
+#endif
+#endif
diff --git a/pfinet/linux-src/include/linux/ncp_fs_sb.h b/pfinet/linux-src/include/linux/ncp_fs_sb.h
new file mode 100644
index 00000000..1f8b85ff
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ncp_fs_sb.h
@@ -0,0 +1,95 @@
+/*
+ * ncp_fs_sb.h
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#ifndef _NCP_FS_SB
+#define _NCP_FS_SB
+
+#include <asm/semaphore.h>
+#include <linux/ncp_mount.h>
+#include <linux/types.h>
+
+#ifdef __KERNEL__
+
+#define NCP_DEFAULT_BUFSIZE 1024
+#define NCP_DEFAULT_OPTIONS 0 /* 2 for packet signatures */
+
+struct ncp_server {
+
+ struct ncp_mount_data m; /* Nearly all of the mount data is of
+ interest for us later, so we store
+ it completely. */
+
+ __u8 name_space[NCP_NUMBER_OF_VOLUMES + 2];
+
+ struct file *ncp_filp; /* File pointer to ncp socket */
+
+ u8 sequence;
+ u8 task;
+ u16 connection; /* Remote connection number */
+
+ u8 completion; /* Status message from server */
+ u8 conn_status; /* Bit 4 = 1 ==> Server going down, no
+ requests allowed anymore.
+ Bit 0 = 1 ==> Server is down. */
+
+ int buffer_size; /* Negotiated bufsize */
+
+ int reply_size; /* Size of last reply */
+
+ int packet_size;
+ unsigned char *packet; /* Here we prepare requests and
+ receive replies */
+
+ int lock; /* To prevent mismatch in protocols. */
+ struct semaphore sem;
+
+ int current_size; /* for packet preparation */
+ int has_subfunction;
+ int ncp_reply_size;
+
+ struct ncp_inode_info root;
+ struct dentry* root_dentry;
+
+ int root_setuped;
+
+/* info for packet signing */
+ int sign_wanted; /* 1=Server needs signed packets */
+ int sign_active; /* 0=don't do signing, 1=do */
+ char sign_root[8]; /* generated from password and encr. key */
+ char sign_last[16];
+
+ /* Authentication info: NDS or BINDERY, username */
+ struct {
+ int auth_type;
+ size_t object_name_len;
+ void* object_name;
+ int object_type;
+ } auth;
+ /* Password info */
+ struct {
+ size_t len;
+ void* data;
+ } priv;
+
+ struct ncp_nls_ioctl nls_charsets; /* NLS user data */
+ struct nls_table *nls_vol; /* codepage used on volume */
+ struct nls_table *nls_io; /* charset used for input and display */
+};
+
+static inline int ncp_conn_valid(struct ncp_server *server)
+{
+ return ((server->conn_status & 0x11) == 0);
+}
+
+static inline void ncp_invalidate_conn(struct ncp_server *server)
+{
+ server->conn_status |= 0x01;
+}
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/ncp_mount.h b/pfinet/linux-src/include/linux/ncp_mount.h
new file mode 100644
index 00000000..a214372a
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ncp_mount.h
@@ -0,0 +1,45 @@
+/*
+ * ncp_mount.h
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_NCP_MOUNT_H
+#define _LINUX_NCP_MOUNT_H
+
+#include <linux/types.h>
+#include <linux/ipx.h>
+#include <linux/ncp.h>
+#include <linux/ncp_fs_i.h>
+
+#define NCP_MOUNT_VERSION 3
+
+/* Values for flags */
+#define NCP_MOUNT_SOFT 0x0001
+#define NCP_MOUNT_INTR 0x0002
+#define NCP_MOUNT_STRONG 0x0004 /* enable delete/rename of r/o files */
+#define NCP_MOUNT_NO_OS2 0x0008 /* do not use OS/2 (LONG) namespace */
+#define NCP_MOUNT_NO_NFS 0x0010 /* do not use NFS namespace */
+#define NCP_MOUNT_EXTRAS 0x0020
+#define NCP_MOUNT_SYMLINKS 0x0040 /* enable symlinks */
+
+struct ncp_mount_data {
+ int version;
+ unsigned int ncp_fd; /* The socket to the ncp port */
+ __kernel_uid_t mounted_uid; /* Who may umount() this filesystem? */
+ __kernel_pid_t wdog_pid; /* Who cares for our watchdog packets? */
+
+ unsigned char mounted_vol[NCP_VOLNAME_LEN + 1];
+ unsigned int time_out; /* How long should I wait after
+ sending a NCP request? */
+ unsigned int retry_count; /* And how often should I retry? */
+ unsigned int flags;
+
+ __kernel_uid_t uid;
+ __kernel_gid_t gid;
+ __kernel_mode_t file_mode;
+ __kernel_mode_t dir_mode;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/net.h b/pfinet/linux-src/include/linux/net.h
new file mode 100644
index 00000000..b224c49e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/net.h
@@ -0,0 +1,152 @@
+/*
+ * NET An implementation of the SOCKET network access protocol.
+ * This is the master header file for the Linux NET layer,
+ * or, in plain English: the networking handling part of the
+ * kernel.
+ *
+ * Version: @(#)net.h 1.0.3 05/25/93
+ *
+ * Authors: Orest Zborowski, <obz@Kodak.COM>
+ * Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_NET_H
+#define _LINUX_NET_H
+
+#include <linux/socket.h>
+
+struct poll_table_struct;
+
+#define NPROTO 32 /* should be enough for now.. */
+
+
+#define SYS_SOCKET 1 /* sys_socket(2) */
+#define SYS_BIND 2 /* sys_bind(2) */
+#define SYS_CONNECT 3 /* sys_connect(2) */
+#define SYS_LISTEN 4 /* sys_listen(2) */
+#define SYS_ACCEPT 5 /* sys_accept(2) */
+#define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */
+#define SYS_GETPEERNAME 7 /* sys_getpeername(2) */
+#define SYS_SOCKETPAIR 8 /* sys_socketpair(2) */
+#define SYS_SEND 9 /* sys_send(2) */
+#define SYS_RECV 10 /* sys_recv(2) */
+#define SYS_SENDTO 11 /* sys_sendto(2) */
+#define SYS_RECVFROM 12 /* sys_recvfrom(2) */
+#define SYS_SHUTDOWN 13 /* sys_shutdown(2) */
+#define SYS_SETSOCKOPT 14 /* sys_setsockopt(2) */
+#define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */
+#define SYS_SENDMSG 16 /* sys_sendmsg(2) */
+#define SYS_RECVMSG 17 /* sys_recvmsg(2) */
+
+
+typedef enum {
+ SS_FREE = 0, /* not allocated */
+ SS_UNCONNECTED, /* unconnected to any socket */
+ SS_CONNECTING, /* in process of connecting */
+ SS_CONNECTED, /* connected to socket */
+ SS_DISCONNECTING /* in process of disconnecting */
+} socket_state;
+
+#define SO_ACCEPTCON (1<<16) /* performed a listen */
+#define SO_WAITDATA (1<<17) /* wait data to read */
+#define SO_NOSPACE (1<<18) /* no space to write */
+
+#ifdef __KERNEL__
+
+struct file; /* forward decl magic */
+struct socket
+{
+ socket_state state;
+
+ unsigned long flags;
+ struct proto_ops *ops;
+ struct inode *inode;
+#ifdef _HURD_
+ uint_fast32_t refcnt; /* # of sock_user's pointing to this */
+ mach_port_t identity; /* for io_identity */
+ ino_t st_ino;
+#else
+ struct fasync_struct *fasync_list; /* Asynchronous wake up list */
+ struct file *file; /* File back pointer for gc */
+#endif
+ struct sock *sk;
+ struct wait_queue *wait;
+
+ short type;
+ unsigned char passcred;
+ unsigned char tli;
+};
+
+#define SOCK_INODE(S) ((S)->inode)
+
+struct scm_cookie;
+
+struct proto_ops {
+ int family;
+
+ int (*dup) (struct socket *newsock, struct socket *oldsock);
+ int (*release) (struct socket *sock, struct socket *peer);
+ int (*bind) (struct socket *sock, struct sockaddr *umyaddr,
+ int sockaddr_len);
+ int (*connect) (struct socket *sock, struct sockaddr *uservaddr,
+ int sockaddr_len, int flags);
+ int (*socketpair) (struct socket *sock1, struct socket *sock2);
+ int (*accept) (struct socket *sock, struct socket *newsock,
+ int flags);
+ int (*getname) (struct socket *sock, struct sockaddr *uaddr,
+ int *usockaddr_len, int peer);
+ unsigned int (*poll) (struct file *file, struct socket *sock, struct poll_table_struct *wait);
+ int (*ioctl) (struct socket *sock, unsigned int cmd,
+ unsigned long arg);
+ int (*listen) (struct socket *sock, int len);
+ int (*shutdown) (struct socket *sock, int flags);
+ int (*setsockopt) (struct socket *sock, int level, int optname,
+ char *optval, int optlen);
+ int (*getsockopt) (struct socket *sock, int level, int optname,
+ char *optval, int *optlen);
+ int (*fcntl) (struct socket *sock, unsigned int cmd,
+ unsigned long arg);
+ int (*sendmsg) (struct socket *sock, struct msghdr *m, int total_len, struct scm_cookie *scm);
+ int (*recvmsg) (struct socket *sock, struct msghdr *m, int total_len, int flags, struct scm_cookie *scm);
+};
+
+struct net_proto_family
+{
+ int family;
+ int (*create)(struct socket *sock, int protocol);
+ /* These are counters for the number of different methods of
+ each we support */
+ short authentication;
+ short encryption;
+ short encrypt_net;
+};
+
+struct net_proto
+{
+ const char *name; /* Protocol name */
+ void (*init_func)(struct net_proto *); /* Bootstrap */
+};
+
+extern struct net_proto_family *net_families[];
+extern int sock_wake_async(struct socket *sk, int how);
+extern int sock_register(struct net_proto_family *fam);
+extern int sock_unregister(int family);
+extern struct socket *sock_alloc(void);
+extern int sock_create(int family, int type, int proto, struct socket **);
+extern void sock_release(struct socket *);
+extern int sock_sendmsg(struct socket *, struct msghdr *m, int len);
+extern int sock_recvmsg(struct socket *, struct msghdr *m, int len, int flags);
+extern int sock_readv_writev(int type, struct inode * inode, struct file * file,
+ const struct iovec * iov, long count, long size);
+
+extern int net_ratelimit(void);
+extern unsigned long net_random(void);
+extern void net_srandom(unsigned long);
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_NET_H */
diff --git a/pfinet/linux-src/include/linux/netbeui.h b/pfinet/linux-src/include/linux/netbeui.h
new file mode 100644
index 00000000..2fb2f71b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/netbeui.h
@@ -0,0 +1,16 @@
+#ifndef _LINUX_NETBEUI_H
+#define _LINUX_NETBEUI_H
+
+#include <linux/if.h>
+
+#define NB_NAME_LEN 20 /* Set this properly from the full docs when
+ I get them */
+
+struct sockaddr_netbeui
+{
+ sa_family snb_family;
+ char snb_name[NB_NAME_LEN];
+ char snb_devhint[IFNAMSIZ];
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/netdevice.h b/pfinet/linux-src/include/linux/netdevice.h
new file mode 100644
index 00000000..0b6af203
--- /dev/null
+++ b/pfinet/linux-src/include/linux/netdevice.h
@@ -0,0 +1,468 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the Interfaces handler.
+ *
+ * Version: @(#)dev.h 1.0.10 08/12/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Corey Minyard <wf-rch!minyard@relay.EU.net>
+ * Donald J. Becker, <becker@cesdis.gsfc.nasa.gov>
+ * Alan Cox, <Alan.Cox@linux.org>
+ * Bjorn Ekwall. <bj0rn@blox.se>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Moved to /usr/include/linux for NET3
+ */
+#ifndef _LINUX_NETDEVICE_H
+#define _LINUX_NETDEVICE_H
+
+#ifdef __KERNEL__
+#include <linux/config.h>
+#endif
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+
+#include <asm/atomic.h>
+
+#ifdef __KERNEL__
+#ifdef CONFIG_NET_PROFILE
+#include <net/profile.h>
+#endif
+#endif
+
+/*
+ * For future expansion when we will have different priorities.
+ */
+
+#define MAX_ADDR_LEN 7 /* Largest hardware address length */
+
+/*
+ * Compute the worst case header length according to the protocols
+ * used.
+ */
+
+#if !defined(CONFIG_AX25) && !defined(CONFIG_AX25_MODULE) && !defined(CONFIG_TR)
+#define LL_MAX_HEADER 32
+#else
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#define LL_MAX_HEADER 96
+#else
+#define LL_MAX_HEADER 48
+#endif
+#endif
+
+#if !defined(CONFIG_NET_IPIP) && \
+ !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE)
+#define MAX_HEADER LL_MAX_HEADER
+#else
+#define MAX_HEADER (LL_MAX_HEADER + 48)
+#endif
+
+/*
+ * Network device statistics. Akin to the 2.0 ether stats but
+ * with byte counters.
+ */
+
+struct net_device_stats
+{
+ unsigned long rx_packets; /* total packets received */
+ unsigned long tx_packets; /* total packets transmitted */
+ unsigned long rx_bytes; /* total bytes received */
+ unsigned long tx_bytes; /* total bytes transmitted */
+ unsigned long rx_errors; /* bad packets received */
+ unsigned long tx_errors; /* packet transmit problems */
+ unsigned long rx_dropped; /* no space in linux buffers */
+ unsigned long tx_dropped; /* no space available in linux */
+ unsigned long multicast; /* multicast packets received */
+ unsigned long collisions;
+
+ /* detailed rx_errors: */
+ unsigned long rx_length_errors;
+ unsigned long rx_over_errors; /* receiver ring buff overflow */
+ unsigned long rx_crc_errors; /* recved pkt with crc error */
+ unsigned long rx_frame_errors; /* recv'd frame alignment error */
+ unsigned long rx_fifo_errors; /* recv'r fifo overrun */
+ unsigned long rx_missed_errors; /* receiver missed packet */
+
+ /* detailed tx_errors */
+ unsigned long tx_aborted_errors;
+ unsigned long tx_carrier_errors;
+ unsigned long tx_fifo_errors;
+ unsigned long tx_heartbeat_errors;
+ unsigned long tx_window_errors;
+
+ /* for cslip etc */
+ unsigned long rx_compressed;
+ unsigned long tx_compressed;
+};
+
+#ifdef CONFIG_NET_FASTROUTE
+struct net_fastroute_stats
+{
+ int hits;
+ int succeed;
+ int deferred;
+ int latency_reduction;
+};
+#endif
+
+/* Media selection options. */
+enum {
+ IF_PORT_UNKNOWN = 0,
+ IF_PORT_10BASE2,
+ IF_PORT_10BASET,
+ IF_PORT_AUI,
+ IF_PORT_100BASET,
+ IF_PORT_100BASETX,
+ IF_PORT_100BASEFX
+};
+
+#ifdef __KERNEL__
+
+extern const char *if_port_text[];
+
+#include <linux/skbuff.h>
+
+struct neighbour;
+struct neigh_parms;
+struct sk_buff;
+
+/*
+ * We tag multicasts with these structures.
+ */
+
+struct dev_mc_list
+{
+ struct dev_mc_list *next;
+ __u8 dmi_addr[MAX_ADDR_LEN];
+ unsigned char dmi_addrlen;
+ int dmi_users;
+ int dmi_gusers;
+};
+
+struct hh_cache
+{
+ struct hh_cache *hh_next; /* Next entry */
+ atomic_t hh_refcnt; /* number of users */
+ unsigned short hh_type; /* protocol identifier, f.e ETH_P_IP */
+ int (*hh_output)(struct sk_buff *skb);
+ rwlock_t hh_lock;
+ /* cached hardware header; allow for machine alignment needs. */
+ unsigned long hh_data[16/sizeof(unsigned long)];
+};
+
+
+/*
+ * The DEVICE structure.
+ * Actually, this whole structure is a big mistake. It mixes I/O
+ * data with strictly "high-level" data, and it has to know about
+ * almost every data structure used in the INET module.
+ *
+ * FIXME: cleanup struct device such that network protocol info
+ * moves out.
+ */
+
+struct device
+{
+
+ /*
+ * This is the first field of the "visible" part of this structure
+ * (i.e. as seen by users in the "Space.c" file). It is the name
+ * the interface.
+ */
+ char *name;
+
+ /*
+ * I/O specific fields
+ * FIXME: Merge these and struct ifmap into one
+ */
+ unsigned long rmem_end; /* shmem "recv" end */
+ unsigned long rmem_start; /* shmem "recv" start */
+ unsigned long mem_end; /* shared mem end */
+ unsigned long mem_start; /* shared mem start */
+ unsigned long base_addr; /* device I/O address */
+ unsigned int irq; /* device IRQ number */
+
+ /* Low-level status flags. */
+ volatile unsigned char start; /* start an operation */
+ /*
+ * These two are just single-bit flags, but due to atomicity
+ * reasons they have to be inside a "unsigned long". However,
+ * they should be inside the SAME unsigned long instead of
+ * this wasteful use of memory..
+ */
+ unsigned long interrupt; /* bitops.. */
+ unsigned long tbusy; /* transmitter busy */
+
+ struct device *next;
+
+ /* The device initialization function. Called only once. */
+ int (*init)(struct device *dev);
+ void (*destructor)(struct device *dev);
+
+ /* Interface index. Unique device identifier */
+ int ifindex;
+ int iflink;
+
+ /*
+ * Some hardware also needs these fields, but they are not
+ * part of the usual set specified in Space.c.
+ */
+
+ unsigned char if_port; /* Selectable AUI, TP,..*/
+ unsigned char dma; /* DMA channel */
+
+ struct net_device_stats* (*get_stats)(struct device *dev);
+ struct iw_statistics* (*get_wireless_stats)(struct device *dev);
+
+ /*
+ * This marks the end of the "visible" part of the structure. All
+ * fields hereafter are internal to the system, and may change at
+ * will (read: may be cleaned up at will).
+ */
+
+ /* These may be needed for future network-power-down code. */
+ unsigned long trans_start; /* Time (in jiffies) of last Tx */
+ unsigned long last_rx; /* Time of last Rx */
+
+ unsigned short flags; /* interface flags (a la BSD) */
+ unsigned short gflags;
+ unsigned mtu; /* interface MTU value */
+ unsigned short type; /* interface hardware type */
+ unsigned short hard_header_len; /* hardware hdr length */
+ void *priv; /* pointer to private data */
+
+ /* Interface address info. */
+ unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */
+ unsigned char pad; /* make dev_addr aligned to 8 bytes */
+ unsigned char dev_addr[MAX_ADDR_LEN]; /* hw address */
+ unsigned char addr_len; /* hardware address length */
+
+ struct dev_mc_list *mc_list; /* Multicast mac addresses */
+ int mc_count; /* Number of installed mcasts */
+ int promiscuity;
+ int allmulti;
+
+ /* For load balancing driver pair support */
+
+ unsigned long pkt_queue; /* Packets queued */
+ struct device *slave; /* Slave device */
+
+ /* Protocol specific pointers */
+
+ void *atalk_ptr; /* AppleTalk link */
+ void *ip_ptr; /* IPv4 specific data */
+ void *dn_ptr; /* DECnet specific data */
+
+ struct Qdisc *qdisc;
+ struct Qdisc *qdisc_sleeping;
+ struct Qdisc *qdisc_list;
+ unsigned long tx_queue_len; /* Max frames per queue allowed */
+
+ /* Bridge stuff */
+ int bridge_port_id;
+
+ /* Pointers to interface service routines. */
+ int (*open)(struct device *dev);
+ int (*stop)(struct device *dev);
+ int (*hard_start_xmit) (struct sk_buff *skb,
+ struct device *dev);
+ int (*hard_header) (struct sk_buff *skb,
+ struct device *dev,
+ unsigned short type,
+ void *daddr,
+ void *saddr,
+ unsigned len);
+ int (*rebuild_header)(struct sk_buff *skb);
+#define HAVE_MULTICAST
+ void (*set_multicast_list)(struct device *dev);
+#define HAVE_SET_MAC_ADDR
+ int (*set_mac_address)(struct device *dev,
+ void *addr);
+#define HAVE_PRIVATE_IOCTL
+ int (*do_ioctl)(struct device *dev,
+ struct ifreq *ifr, int cmd);
+#define HAVE_SET_CONFIG
+ int (*set_config)(struct device *dev,
+ struct ifmap *map);
+#define HAVE_HEADER_CACHE
+ int (*hard_header_cache)(struct neighbour *neigh,
+ struct hh_cache *hh);
+ void (*header_cache_update)(struct hh_cache *hh,
+ struct device *dev,
+ unsigned char * haddr);
+#define HAVE_CHANGE_MTU
+ int (*change_mtu)(struct device *dev, int new_mtu);
+
+ int (*hard_header_parse)(struct sk_buff *skb,
+ unsigned char *haddr);
+ int (*neigh_setup)(struct device *dev, struct neigh_parms *);
+ int (*accept_fastpath)(struct device *, struct dst_entry*);
+
+#ifdef CONFIG_NET_FASTROUTE
+ /* Really, this semaphore may be necessary and for not fastroute code;
+ f.e. SMP??
+ */
+ int tx_semaphore;
+#define NETDEV_FASTROUTE_HMASK 0xF
+ /* Semi-private data. Keep it at the end of device struct. */
+ struct dst_entry *fastpath[NETDEV_FASTROUTE_HMASK+1];
+#endif
+
+ int (*change_flags)(struct device *dev, short flags);
+};
+
+
+struct packet_type
+{
+ unsigned short type; /* This is really htons(ether_type). */
+ struct device *dev; /* NULL is wildcarded here */
+ int (*func) (struct sk_buff *, struct device *,
+ struct packet_type *);
+ void *data; /* Private to the packet type */
+ struct packet_type *next;
+};
+
+
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+
+extern struct device loopback_dev; /* The loopback */
+extern struct device *dev_base; /* All devices */
+extern struct packet_type *ptype_base[16]; /* Hashed types */
+extern int netdev_dropping;
+extern int net_cpu_congestion;
+
+extern struct device *dev_getbyhwaddr(unsigned short type, char *hwaddr);
+extern void dev_add_pack(struct packet_type *pt);
+extern void dev_remove_pack(struct packet_type *pt);
+extern struct device *dev_get(const char *name);
+extern struct device *dev_alloc(const char *name, int *err);
+extern int dev_alloc_name(struct device *dev, const char *name);
+extern int dev_open(struct device *dev);
+extern int dev_close(struct device *dev);
+extern int dev_queue_xmit(struct sk_buff *skb);
+extern void dev_loopback_xmit(struct sk_buff *skb);
+extern int register_netdevice(struct device *dev);
+extern int unregister_netdevice(struct device *dev);
+extern int register_netdevice_notifier(struct notifier_block *nb);
+extern int unregister_netdevice_notifier(struct notifier_block *nb);
+extern int dev_new_index(void);
+extern struct device *dev_get_by_index(int ifindex);
+extern int dev_restart(struct device *dev);
+
+typedef int gifconf_func_t(struct device * dev, char * bufptr, int len);
+extern int register_gifconf(unsigned int family, gifconf_func_t * gifconf);
+extern __inline__ int unregister_gifconf(unsigned int family)
+{
+ return register_gifconf(family, 0);
+}
+
+#define HAVE_NETIF_RX 1
+extern void netif_rx(struct sk_buff *skb);
+extern void net_bh(void);
+extern int dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
+extern int dev_ioctl(unsigned int cmd, void *);
+extern int dev_change_flags(struct device *, unsigned);
+extern void dev_queue_xmit_nit(struct sk_buff *skb, struct device *dev);
+
+extern void dev_init(void);
+
+extern int netdev_nit;
+
+/* Locking protection for page faults during outputs to devices unloaded during the fault */
+
+extern atomic_t dev_lockct;
+
+/*
+ * These two don't currently need to be atomic
+ * but they may do soon. Do it properly anyway.
+ */
+
+extern __inline__ void dev_lock_list(void)
+{
+ atomic_inc(&dev_lockct);
+}
+
+extern __inline__ void dev_unlock_list(void)
+{
+ atomic_dec(&dev_lockct);
+}
+
+/*
+ * This almost never occurs, isn't in performance critical paths
+ * and we can thus be relaxed about it.
+ *
+ * FIXME: What if this is being run as a real time process ??
+ * Linus: We need a way to force a yield here ?
+ *
+ * FIXME: Though dev_lockct is atomic varible, locking procedure
+ * is not atomic.
+ */
+
+extern __inline__ void dev_lock_wait(void)
+{
+ while (atomic_read(&dev_lockct)) {
+ current->policy |= SCHED_YIELD;
+ schedule();
+ }
+}
+
+extern __inline__ void dev_init_buffers(struct device *dev)
+{
+ /* DO NOTHING */
+}
+
+/* These functions live elsewhere (drivers/net/net_init.c, but related) */
+
+extern void ether_setup(struct device *dev);
+extern void fddi_setup(struct device *dev);
+extern void tr_setup(struct device *dev);
+extern void fc_setup(struct device *dev);
+extern void tr_freedev(struct device *dev);
+extern void fc_freedev(struct device *dev);
+extern int ether_config(struct device *dev, struct ifmap *map);
+/* Support for loadable net-drivers */
+extern int register_netdev(struct device *dev);
+extern void unregister_netdev(struct device *dev);
+extern int register_trdev(struct device *dev);
+extern void unregister_trdev(struct device *dev);
+extern int register_fcdev(struct device *dev);
+extern void unregister_fcdev(struct device *dev);
+/* Functions used for multicast support */
+extern void dev_mc_upload(struct device *dev);
+extern int dev_mc_delete(struct device *dev, void *addr, int alen, int all);
+extern int dev_mc_add(struct device *dev, void *addr, int alen, int newonly);
+extern void dev_mc_discard(struct device *dev);
+extern void dev_set_promiscuity(struct device *dev, int inc);
+extern void dev_set_allmulti(struct device *dev, int inc);
+extern void netdev_state_change(struct device *dev);
+/* Load a device via the kmod */
+extern void dev_load(const char *name);
+extern void dev_mcast_init(void);
+extern int netdev_register_fc(struct device *dev, void (*stimul)(struct device *dev));
+extern void netdev_unregister_fc(int bit);
+extern int netdev_dropping;
+extern int netdev_max_backlog;
+extern atomic_t netdev_rx_dropped;
+extern unsigned long netdev_fc_xoff;
+#ifdef CONFIG_NET_FASTROUTE
+extern int netdev_fastroute;
+extern int netdev_fastroute_obstacles;
+extern void dev_clear_fastroute(struct device *dev);
+extern struct net_fastroute_stats dev_fastroute_stat;
+#endif
+
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_DEV_H */
diff --git a/pfinet/linux-src/include/linux/netlink.h b/pfinet/linux-src/include/linux/netlink.h
new file mode 100644
index 00000000..59075b07
--- /dev/null
+++ b/pfinet/linux-src/include/linux/netlink.h
@@ -0,0 +1,158 @@
+#ifndef __LINUX_NETLINK_H
+#define __LINUX_NETLINK_H
+
+#define NETLINK_ROUTE 0 /* Routing/device hook */
+#define NETLINK_SKIP 1 /* Reserved for ENskip */
+#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
+#define NETLINK_FIREWALL 3 /* Firewalling hook */
+#define NETLINK_ARPD 8
+#define NETLINK_ROUTE6 11 /* af_inet6 route comm channel */
+#define NETLINK_IP6_FW 13
+#define NETLINK_TAPBASE 16 /* 16 to 31 are ethertap */
+
+#define MAX_LINKS 32
+
+struct sockaddr_nl
+{
+ sa_family_t nl_family; /* AF_NETLINK */
+ unsigned short nl_pad; /* zero */
+ __u32 nl_pid; /* process pid */
+ __u32 nl_groups; /* multicast groups mask */
+};
+
+struct nlmsghdr
+{
+ __u32 nlmsg_len; /* Length of message including header */
+ __u16 nlmsg_type; /* Message content */
+ __u16 nlmsg_flags; /* Additional flags */
+ __u32 nlmsg_seq; /* Sequence number */
+ __u32 nlmsg_pid; /* Sending process PID */
+};
+
+/* Flags values */
+
+#define NLM_F_REQUEST 1 /* It is request message. */
+#define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */
+#define NLM_F_ACK 4 /* If succeed, reply with ack */
+#define NLM_F_ECHO 8 /* Echo this request */
+
+/* Modifiers to GET request */
+#define NLM_F_ROOT 0x100 /* specify tree root */
+#define NLM_F_MATCH 0x200 /* return all matching */
+#define NLM_F_ATOMIC 0x400 /* atomic GET */
+#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
+
+/* Modifiers to NEW request */
+#define NLM_F_REPLACE 0x100 /* Override existing */
+#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */
+#define NLM_F_CREATE 0x400 /* Create, if it does not exist */
+#define NLM_F_APPEND 0x800 /* Add to end of list */
+
+/*
+ 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL
+ 4.4BSD CHANGE NLM_F_REPLACE
+
+ True CHANGE NLM_F_CREATE|NLM_F_REPLACE
+ Append NLM_F_CREATE
+ Check NLM_F_EXCL
+ */
+
+#define NLMSG_ALIGNTO 4
+#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
+#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
+#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
+#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
+ (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
+#define NLMSG_OK(nlh,len) ((len) > 0 && (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
+ (nlh)->nlmsg_len <= (len))
+#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
+
+#define NLMSG_NOOP 0x1 /* Nothing. */
+#define NLMSG_ERROR 0x2 /* Error */
+#define NLMSG_DONE 0x3 /* End of a dump */
+#define NLMSG_OVERRUN 0x4 /* Data lost */
+
+struct nlmsgerr
+{
+ int error;
+ struct nlmsghdr msg;
+};
+
+#define NET_MAJOR 36 /* Major 36 is reserved for networking */
+
+#ifdef __KERNEL__
+
+struct netlink_skb_parms
+{
+ struct ucred creds; /* Skb credentials */
+ __u32 pid;
+ __u32 groups;
+ __u32 dst_pid;
+ __u32 dst_groups;
+ kernel_cap_t eff_cap;
+};
+
+#define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)->cb))
+#define NETLINK_CREDS(skb) (&NETLINK_CB((skb)).creds)
+
+
+extern int netlink_attach(int unit, int (*function)(int,struct sk_buff *skb));
+extern void netlink_detach(int unit);
+extern int netlink_post(int unit, struct sk_buff *skb);
+extern int init_netlink(void);
+extern struct sock *netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len));
+extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err);
+extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock);
+extern void netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 pid,
+ __u32 group, int allocation);
+extern void netlink_set_err(struct sock *ssk, __u32 pid, __u32 group, int code);
+
+/*
+ * skb should fit one page. This choice is good for headerless malloc.
+ *
+ * FIXME: What is the best size for SLAB???? --ANK
+ */
+#define NLMSG_GOODSIZE (PAGE_SIZE - ((sizeof(struct sk_buff)+0xF)&~0xF))
+
+
+struct netlink_callback
+{
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ int (*dump)(struct sk_buff * skb, struct netlink_callback *cb);
+ int (*done)(struct netlink_callback *cb);
+ int family;
+ long args[4];
+};
+
+extern __inline__ struct nlmsghdr *
+__nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len)
+{
+ struct nlmsghdr *nlh;
+ int size = NLMSG_LENGTH(len);
+
+ nlh = (struct nlmsghdr*)skb_put(skb, NLMSG_ALIGN(size));
+ nlh->nlmsg_type = type;
+ nlh->nlmsg_len = size;
+ nlh->nlmsg_flags = 0;
+ nlh->nlmsg_pid = pid;
+ nlh->nlmsg_seq = seq;
+ return nlh;
+}
+
+#define NLMSG_PUT(skb, pid, seq, type, len) \
+({ if (skb_tailroom(skb) < (int)NLMSG_SPACE(len)) goto nlmsg_failure; \
+ __nlmsg_put(skb, pid, seq, type, len); })
+
+extern int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
+ struct nlmsghdr *nlh,
+ int (*dump)(struct sk_buff *skb, struct netlink_callback*),
+ int (*done)(struct netlink_callback*));
+
+
+extern void netlink_proto_init(struct net_proto *pro);
+
+#endif /* __KERNEL__ */
+
+#endif /* __LINUX_NETLINK_H */
diff --git a/pfinet/linux-src/include/linux/netrom.h b/pfinet/linux-src/include/linux/netrom.h
new file mode 100644
index 00000000..6939b32f
--- /dev/null
+++ b/pfinet/linux-src/include/linux/netrom.h
@@ -0,0 +1,34 @@
+/*
+ * These are the public elements of the Linux kernel NET/ROM implementation.
+ * For kernel AX.25 see the file ax25.h. This file requires ax25.h for the
+ * definition of the ax25_address structure.
+ */
+
+#ifndef NETROM_KERNEL_H
+#define NETROM_KERNEL_H
+
+#define NETROM_MTU 236
+
+#define NETROM_T1 1
+#define NETROM_T2 2
+#define NETROM_N2 3
+#define NETROM_T4 6
+#define NETROM_IDLE 7
+
+#define SIOCNRDECOBS (SIOCPROTOPRIVATE+2)
+
+struct nr_route_struct {
+#define NETROM_NEIGH 0
+#define NETROM_NODE 1
+ int type;
+ ax25_address callsign;
+ char device[16];
+ unsigned int quality;
+ char mnemonic[7];
+ ax25_address neighbour;
+ unsigned int obs_count;
+ unsigned int ndigis;
+ ax25_address digipeaters[AX25_MAX_DIGIS];
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/nfs.h b/pfinet/linux-src/include/linux/nfs.h
new file mode 100644
index 00000000..7936d5a7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/nfs.h
@@ -0,0 +1,226 @@
+/*
+ * NFS protocol definitions
+ */
+#ifndef _LINUX_NFS_H
+#define _LINUX_NFS_H
+
+#include <linux/sunrpc/msg_prot.h>
+
+#define NFS_PORT 2049
+#define NFS_MAXDATA 8192
+#define NFS_MAXPATHLEN 1024
+#define NFS_MAXNAMLEN 255
+#define NFS_MAXGROUPS 16
+#define NFS_FHSIZE 32
+#define NFS_COOKIESIZE 4
+#define NFS_FIFO_DEV (-1)
+#define NFSMODE_FMT 0170000
+#define NFSMODE_DIR 0040000
+#define NFSMODE_CHR 0020000
+#define NFSMODE_BLK 0060000
+#define NFSMODE_REG 0100000
+#define NFSMODE_LNK 0120000
+#define NFSMODE_SOCK 0140000
+#define NFSMODE_FIFO 0010000
+
+
+enum nfs_stat {
+ NFS_OK = 0,
+ NFSERR_PERM = 1,
+ NFSERR_NOENT = 2,
+ NFSERR_IO = 5,
+ NFSERR_NXIO = 6,
+ NFSERR_EAGAIN = 11,
+ NFSERR_ACCES = 13,
+ NFSERR_EXIST = 17,
+ NFSERR_XDEV = 18,
+ NFSERR_NODEV = 19,
+ NFSERR_NOTDIR = 20,
+ NFSERR_ISDIR = 21,
+ NFSERR_INVAL = 22, /* that Sun forgot */
+ NFSERR_FBIG = 27,
+ NFSERR_NOSPC = 28,
+ NFSERR_ROFS = 30,
+ NFSERR_OPNOTSUPP = 45,
+ NFSERR_NAMETOOLONG = 63,
+ NFSERR_NOTEMPTY = 66,
+ NFSERR_DQUOT = 69,
+ NFSERR_STALE = 70,
+ NFSERR_WFLUSH = 99
+};
+
+enum nfs_ftype {
+ NFNON = 0,
+ NFREG = 1,
+ NFDIR = 2,
+ NFBLK = 3,
+ NFCHR = 4,
+ NFLNK = 5,
+ NFSOCK = 6,
+ NFBAD = 7,
+ NFFIFO = 8
+};
+
+struct nfs_fh {
+ char data[NFS_FHSIZE];
+};
+
+#define NFS_PROGRAM 100003
+#define NFS_VERSION 2
+#define NFSPROC_NULL 0
+#define NFSPROC_GETATTR 1
+#define NFSPROC_SETATTR 2
+#define NFSPROC_ROOT 3
+#define NFSPROC_LOOKUP 4
+#define NFSPROC_READLINK 5
+#define NFSPROC_READ 6
+#define NFSPROC_WRITECACHE 7
+#define NFSPROC_WRITE 8
+#define NFSPROC_CREATE 9
+#define NFSPROC_REMOVE 10
+#define NFSPROC_RENAME 11
+#define NFSPROC_LINK 12
+#define NFSPROC_SYMLINK 13
+#define NFSPROC_MKDIR 14
+#define NFSPROC_RMDIR 15
+#define NFSPROC_READDIR 16
+#define NFSPROC_STATFS 17
+
+/* Mount support for NFSroot */
+#ifdef __KERNEL__
+#define NFS_MNT_PROGRAM 100005
+#define NFS_MNT_VERSION 1
+#define NFS_MNT_PORT 627
+#define NFS_MNTPROC_MNT 1
+#define NFS_MNTPROC_UMNT 3
+#endif
+
+#if defined(__KERNEL__) || defined(NFS_NEED_KERNEL_TYPES)
+
+extern struct rpc_program nfs_program;
+extern struct rpc_stat nfs_rpcstat;
+
+struct nfs_time {
+ __u32 seconds;
+ __u32 useconds;
+};
+
+struct nfs_fattr {
+ enum nfs_ftype type;
+ __u32 mode;
+ __u32 nlink;
+ __u32 uid;
+ __u32 gid;
+ __u32 size;
+ __u32 blocksize;
+ __u32 rdev;
+ __u32 blocks;
+ __u32 fsid;
+ __u32 fileid;
+ struct nfs_time atime;
+ struct nfs_time mtime;
+ struct nfs_time ctime;
+};
+
+struct nfs_sattr {
+ __u32 mode;
+ __u32 uid;
+ __u32 gid;
+ __u32 size;
+ struct nfs_time atime;
+ struct nfs_time mtime;
+};
+
+struct nfs_fsinfo {
+ __u32 tsize;
+ __u32 bsize;
+ __u32 blocks;
+ __u32 bfree;
+ __u32 bavail;
+};
+
+struct nfs_writeargs {
+ struct nfs_fh * fh;
+ __u32 offset;
+ __u32 count;
+ const void * buffer;
+};
+
+#ifdef NFS_NEED_XDR_TYPES
+
+struct nfs_sattrargs {
+ struct nfs_fh * fh;
+ struct nfs_sattr * sattr;
+};
+
+struct nfs_diropargs {
+ struct nfs_fh * fh;
+ const char * name;
+};
+
+struct nfs_readargs {
+ struct nfs_fh * fh;
+ __u32 offset;
+ __u32 count;
+ void * buffer;
+};
+
+struct nfs_createargs {
+ struct nfs_fh * fh;
+ const char * name;
+ struct nfs_sattr * sattr;
+};
+
+struct nfs_renameargs {
+ struct nfs_fh * fromfh;
+ const char * fromname;
+ struct nfs_fh * tofh;
+ const char * toname;
+};
+
+struct nfs_linkargs {
+ struct nfs_fh * fromfh;
+ struct nfs_fh * tofh;
+ const char * toname;
+};
+
+struct nfs_symlinkargs {
+ struct nfs_fh * fromfh;
+ const char * fromname;
+ const char * topath;
+ struct nfs_sattr * sattr;
+};
+
+struct nfs_readdirargs {
+ struct nfs_fh * fh;
+ __u32 cookie;
+ void * buffer;
+ unsigned int bufsiz;
+};
+
+struct nfs_diropok {
+ struct nfs_fh * fh;
+ struct nfs_fattr * fattr;
+};
+
+struct nfs_readres {
+ struct nfs_fattr * fattr;
+ unsigned int count;
+};
+
+struct nfs_readlinkres {
+ char ** string;
+ unsigned int * lenp;
+ unsigned int maxlen;
+ void * buffer;
+};
+
+struct nfs_readdirres {
+ void * buffer;
+ unsigned int bufsiz;
+};
+
+#endif /* NFS_NEED_XDR_TYPES */
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/nfs3.h b/pfinet/linux-src/include/linux/nfs3.h
new file mode 100644
index 00000000..94ec44b6
--- /dev/null
+++ b/pfinet/linux-src/include/linux/nfs3.h
@@ -0,0 +1,252 @@
+/*
+ * NFSv3 protocol definitions
+ */
+#ifndef _LINUX_NFS3_H
+#define _LINUX_NFS3_H
+
+#include <linux/sunrpc/msg_prot.h>
+#include <linux/nfs.h>
+
+#define NFS3_PORT 2049
+#define NFS3_MAXDATA 8192
+#define NFS3_MAXPATHLEN PATH_MAX
+#define NFS3_MAXNAMLEN NAME_MAX
+#define NFS3_MAXGROUPS 16
+#define NFS3_FHSIZE NFS_FHSIZE
+#define NFS3_COOKIESIZE 4
+#define NFS3_FIFO_DEV (-1)
+#define NFS3MODE_FMT 0170000
+#define NFS3MODE_DIR 0040000
+#define NFS3MODE_CHR 0020000
+#define NFS3MODE_BLK 0060000
+#define NFS3MODE_REG 0100000
+#define NFS3MODE_LNK 0120000
+#define NFS3MODE_SOCK 0140000
+#define NFS3MODE_FIFO 0010000
+
+
+enum nfs3_stat {
+ NFS3_OK = 0,
+ NFS3ERR_PERM = 1,
+ NFS3ERR_NOENT = 2,
+ NFS3ERR_IO = 5,
+ NFS3ERR_NXIO = 6,
+ NFS3ERR_EAGAIN = 11,
+ NFS3ERR_ACCES = 13,
+ NFS3ERR_EXIST = 17,
+ NFS3ERR_XDEV = 18, /* new in NFSv3 */
+ NFS3ERR_NODEV = 19,
+ NFS3ERR_NOTDIR = 20,
+ NFS3ERR_ISDIR = 21,
+ NFS3ERR_INVAL = 22, /* new in NFSv3 */
+ NFS3ERR_FBIG = 27,
+ NFS3ERR_NOSPC = 28,
+ NFS3ERR_ROFS = 30,
+ NFS3ERR_MLINK = 31, /* new in NFSv3 */
+ NFS3ERR_NAMETOOLONG = 63,
+ NFS3ERR_NOTEMPTY = 66,
+ NFS3ERR_DQUOT = 69,
+ NFS3ERR_STALE = 70,
+ NFS3ERR_REMOTE = 71, /* new in NFSv3 */
+ NFS3ERR_BADHANDLE = 10001,/* ditto */
+ NFS3ERR_NOT_SYNC = 10002,/* ditto */
+ NFS3ERR_BAD_COOKIE = 10003,/* ditto */
+ NFS3ERR_NOTSUPP = 10004,/* ditto */
+ NFS3ERR_TOOSMALL = 10005,/* ditto */
+ NFS3ERR_SERVERFAULT = 10006,/* ditto */
+ NFS3ERR_BADTYPE = 10007,/* ditto */
+ NFS3ERR_JUKEBOX = 10008,/* ditto */
+};
+
+enum nfs3_ftype {
+ NF3NON = 0,
+ NF3REG = 1,
+ NF3DIR = 2,
+ NF3BLK = 3,
+ NF3CHR = 4,
+ NF3LNK = 5,
+ NF3SOCK = 6,
+ NF3FIFO = 7, /* changed from NFSv2 (was 8) */
+ NF3BAD = 8
+};
+
+#define NFS3_VERSION 3
+#define NFSPROC_NULL 0
+#define NFSPROC_GETATTR 1
+#define NFSPROC_SETATTR 2
+#define NFSPROC_ROOT 3
+#define NFSPROC_LOOKUP 4
+#define NFSPROC_READLINK 5
+#define NFSPROC_READ 6
+#define NFSPROC_WRITECACHE 7
+#define NFSPROC_WRITE 8
+#define NFSPROC_CREATE 9
+#define NFSPROC_REMOVE 10
+#define NFSPROC_RENAME 11
+#define NFSPROC_LINK 12
+#define NFSPROC_SYMLINK 13
+#define NFSPROC_MKDIR 14
+#define NFSPROC_RMDIR 15
+#define NFSPROC_READDIR 16
+#define NFSPROC_STATFS 17
+
+#if defined(__KERNEL__) || defined(NFS_NEED_KERNEL_TYPES)
+
+struct nfs3_fh {
+ __u32 size;
+ __u8 data[NFS3_FHSIZE];
+};
+
+struct nfs3_fattr {
+ enum nfs3_ftype type;
+ __u32 mode;
+ __u32 nlink;
+ __u32 uid;
+ __u32 gid;
+ __u64 size;
+ __u64 used;
+ __u32 rdev_maj;
+ __u32 rdev_min;
+ __u32 fsid;
+ __u32 fileid;
+ struct nfs_time atime;
+ struct nfs_time mtime;
+ struct nfs_time ctime;
+};
+
+struct nfs3_wcc_attr {
+ __u64 size;
+ struct nfs_time mtime;
+ struct nfs_time ctime;
+};
+
+struct nfs3_wcc_data {
+ struct nfs3_wcc_attr before;
+ struct nfs3_wcc_attr after;
+};
+
+struct nfs3_sattr {
+ __u32 valid;
+ __u32 mode;
+ __u32 uid;
+ __u32 gid;
+ __u64 size;
+ struct nfs_time atime;
+ struct nfs_time mtime;
+};
+
+struct nfs3_entry {
+ __u32 fileid;
+ char * name;
+ unsigned int length;
+ __u32 cookie;
+ __u32 eof;
+};
+
+struct nfs3_fsinfo {
+ __u32 tsize;
+ __u32 bsize;
+ __u32 blocks;
+ __u32 bfree;
+ __u32 bavail;
+};
+
+#ifdef NFS_NEED_XDR_TYPES
+
+struct nfs3_sattrargs {
+ struct nfs_fh * fh;
+ struct nfs_sattr * sattr;
+};
+
+struct nfs3_diropargs {
+ struct nfs_fh * fh;
+ const char * name;
+};
+
+struct nfs3_readargs {
+ struct nfs_fh * fh;
+ __u32 offset;
+ __u32 count;
+ void * buffer;
+};
+
+struct nfs3_writeargs {
+ struct nfs_fh * fh;
+ __u32 offset;
+ __u32 count;
+ const void * buffer;
+};
+
+struct nfs3_createargs {
+ struct nfs_fh * fh;
+ const char * name;
+ struct nfs_sattr * sattr;
+};
+
+struct nfs3_renameargs {
+ struct nfs_fh * fromfh;
+ const char * fromname;
+ struct nfs_fh * tofh;
+ const char * toname;
+};
+
+struct nfs3_linkargs {
+ struct nfs_fh * fromfh;
+ struct nfs_fh * tofh;
+ const char * toname;
+};
+
+struct nfs3_symlinkargs {
+ struct nfs_fh * fromfh;
+ const char * fromname;
+ const char * topath;
+ struct nfs_sattr * sattr;
+};
+
+struct nfs3_readdirargs {
+ struct nfs_fh * fh;
+ __u32 cookie;
+ void * buffer;
+ unsigned int bufsiz;
+};
+
+struct nfs3_diropok {
+ struct nfs_fh * fh;
+ struct nfs_fattr * fattr;
+};
+
+struct nfs3_readres {
+ struct nfs_fattr * fattr;
+ unsigned int count;
+};
+
+struct nfs3_readlinkres {
+ char ** string;
+ unsigned int * lenp;
+ unsigned int maxlen;
+ void * buffer;
+};
+
+struct nfs3_readdirres {
+ void * buffer;
+ unsigned int bufsiz;
+};
+
+/*
+ * The following are for NFSv3
+ */
+struct nfs3_fh {
+ __u32 size;
+ __u8 data[NFS3_FHSIZE]
+};
+
+struct nfs3_wcc_attr {
+ __u64 size;
+ struct nfs_time mtime;
+ struct nfs_time ctime;
+};
+
+#endif /* NFS_NEED_XDR_TYPES */
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/nfs_fs.h b/pfinet/linux-src/include/linux/nfs_fs.h
new file mode 100644
index 00000000..af961506
--- /dev/null
+++ b/pfinet/linux-src/include/linux/nfs_fs.h
@@ -0,0 +1,284 @@
+/*
+ * linux/include/linux/nfs_fs.h
+ *
+ * Copyright (C) 1992 Rick Sladkey
+ *
+ * OS-specific nfs filesystem definitions and declarations
+ */
+
+#ifndef _LINUX_NFS_FS_H
+#define _LINUX_NFS_FS_H
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+
+#include <linux/sunrpc/sched.h>
+#include <linux/nfs.h>
+#include <linux/nfs_mount.h>
+
+/*
+ * Enable debugging support for nfs client.
+ * Requires RPC_DEBUG.
+ */
+#ifdef RPC_DEBUG
+# define NFS_DEBUG
+#endif
+
+/*
+ * NFS_MAX_DIRCACHE controls the number of simultaneously cached
+ * directory chunks. Each chunk holds the list of nfs_entry's returned
+ * in a single readdir call in a memory region of size PAGE_SIZE.
+ *
+ * Note that at most server->rsize bytes of the cache memory are used.
+ */
+#define NFS_MAX_DIRCACHE 16
+
+#define NFS_MAX_FILE_IO_BUFFER_SIZE 16384
+#define NFS_DEF_FILE_IO_BUFFER_SIZE 4096
+
+/*
+ * The upper limit on timeouts for the exponential backoff algorithm.
+ */
+#define NFS_MAX_RPC_TIMEOUT (6*HZ)
+
+/*
+ * Size of the lookup cache in units of number of entries cached.
+ * It is better not to make this too large although the optimum
+ * depends on a usage and environment.
+ */
+#define NFS_LOOKUP_CACHE_SIZE 64
+
+/*
+ * superblock magic number for NFS
+ */
+#define NFS_SUPER_MAGIC 0x6969
+
+#define NFS_FH(dentry) ((struct nfs_fh *) ((dentry)->d_fsdata))
+#define NFS_DSERVER(dentry) (&(dentry)->d_sb->u.nfs_sb.s_server)
+#define NFS_SERVER(inode) (&(inode)->i_sb->u.nfs_sb.s_server)
+#define NFS_CLIENT(inode) (NFS_SERVER(inode)->client)
+#define NFS_ADDR(inode) (RPC_PEERADDR(NFS_CLIENT(inode)))
+#define NFS_CONGESTED(inode) (RPC_CONGESTED(NFS_CLIENT(inode)))
+
+#define NFS_READTIME(inode) ((inode)->u.nfs_i.read_cache_jiffies)
+#define NFS_OLDMTIME(inode) ((inode)->u.nfs_i.read_cache_mtime)
+#define NFS_CACHEINV(inode) \
+do { \
+ NFS_READTIME(inode) = jiffies - 1000000; \
+ NFS_OLDMTIME(inode) = 0; \
+} while (0)
+#define NFS_ATTRTIMEO(inode) ((inode)->u.nfs_i.attrtimeo)
+#define NFS_MINATTRTIMEO(inode) \
+ (S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmin \
+ : NFS_SERVER(inode)->acregmin)
+#define NFS_MAXATTRTIMEO(inode) \
+ (S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmax \
+ : NFS_SERVER(inode)->acregmax)
+
+#define NFS_FLAGS(inode) ((inode)->u.nfs_i.flags)
+#define NFS_REVALIDATING(inode) (NFS_FLAGS(inode) & NFS_INO_REVALIDATE)
+#define NFS_WRITEBACK(inode) ((inode)->u.nfs_i.writeback)
+
+/*
+ * These are the default flags for swap requests
+ */
+#define NFS_RPC_SWAPFLAGS (RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS)
+
+/* Flags in the RPC client structure */
+#define NFS_CLNTF_BUFSIZE 0x0001 /* readdir buffer in longwords */
+
+#ifdef __KERNEL__
+
+/*
+ * This struct describes a file region to be written.
+ * It's kind of a pity we have to keep all these lists ourselves, rather
+ * than sticking an extra pointer into struct page.
+ */
+struct nfs_wreq {
+ struct rpc_listitem wb_list; /* linked list of req's */
+ struct rpc_task wb_task; /* RPC task */
+ struct file * wb_file; /* dentry referenced */
+ struct page * wb_page; /* page to be written */
+ struct wait_queue * wb_wait; /* wait for completion */
+ unsigned int wb_offset; /* offset within page */
+ unsigned int wb_bytes; /* dirty range */
+ unsigned int wb_count; /* user count */
+ int wb_status;
+ pid_t wb_pid; /* owner process */
+ unsigned short wb_flags; /* status flags */
+
+ struct nfs_writeargs wb_args; /* NFS RPC stuff */
+ struct nfs_fattr wb_fattr; /* file attributes */
+};
+
+#define WB_NEXT(req) ((struct nfs_wreq *) ((req)->wb_list.next))
+
+/*
+ * Various flags for wb_flags
+ */
+#define NFS_WRITE_CANCELLED 0x0004 /* has been cancelled */
+#define NFS_WRITE_UNCOMMITTED 0x0008 /* written but uncommitted (NFSv3) */
+#define NFS_WRITE_INVALIDATE 0x0010 /* invalidate after write */
+#define NFS_WRITE_INPROGRESS 0x0100 /* RPC call in progress */
+#define NFS_WRITE_COMPLETE 0x0200 /* RPC call completed */
+
+#define WB_CANCELLED(req) ((req)->wb_flags & NFS_WRITE_CANCELLED)
+#define WB_UNCOMMITTED(req) ((req)->wb_flags & NFS_WRITE_UNCOMMITTED)
+#define WB_INVALIDATE(req) ((req)->wb_flags & NFS_WRITE_INVALIDATE)
+#define WB_INPROGRESS(req) ((req)->wb_flags & NFS_WRITE_INPROGRESS)
+#define WB_COMPLETE(req) ((req)->wb_flags & NFS_WRITE_COMPLETE)
+
+/*
+ * linux/fs/nfs/proc.c
+ */
+extern int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr);
+extern int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_sattr *sattr, struct nfs_fattr *fattr);
+extern int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir,
+ const char *name, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr);
+extern int nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
+ void **p0, char **string, unsigned int *len,
+ unsigned int maxlen);
+extern int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle,
+ int swap, unsigned long offset, unsigned int count,
+ void *buffer, struct nfs_fattr *fattr);
+extern int nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle,
+ int swap, unsigned long offset, unsigned int count,
+ const void *buffer, struct nfs_fattr *fattr);
+extern int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
+ const char *name, struct nfs_sattr *sattr,
+ struct nfs_fh *fhandle, struct nfs_fattr *fattr);
+extern int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir,
+ const char *name);
+extern int nfs_proc_rename(struct nfs_server *server,
+ struct nfs_fh *old_dir, const char *old_name,
+ struct nfs_fh *new_dir, const char *new_name);
+extern int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fh *dir, const char *name);
+extern int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
+ const char *name, const char *path,
+ struct nfs_sattr *sattr);
+extern int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
+ const char *name, struct nfs_sattr *sattr,
+ struct nfs_fh *fhandle, struct nfs_fattr *fattr);
+extern int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir,
+ const char *name);
+extern int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
+ u32 cookie, unsigned int size, __u32 *entry);
+extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fsinfo *res);
+
+
+/*
+ * linux/fs/nfs/inode.c
+ */
+extern struct super_block *nfs_read_super(struct super_block *, void *, int);
+extern int init_nfs_fs(void);
+extern struct inode *nfs_fhget(struct dentry *, struct nfs_fh *,
+ struct nfs_fattr *);
+extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
+extern int nfs_revalidate(struct dentry *);
+extern int nfs_open(struct inode *, struct file *);
+extern int nfs_release(struct inode *, struct file *);
+extern int _nfs_revalidate_inode(struct nfs_server *, struct dentry *);
+
+/*
+ * linux/fs/nfs/file.c
+ */
+extern struct inode_operations nfs_file_inode_operations;
+
+/*
+ * linux/fs/nfs/dir.c
+ */
+extern struct inode_operations nfs_dir_inode_operations;
+extern struct dentry_operations nfs_dentry_operations;
+extern void nfs_free_dircache(void);
+extern void nfs_invalidate_dircache(struct inode *);
+extern void nfs_invalidate_dircache_sb(struct super_block *);
+
+/*
+ * linux/fs/nfs/symlink.c
+ */
+extern struct inode_operations nfs_symlink_inode_operations;
+
+/*
+ * linux/fs/nfs/locks.c
+ */
+extern int nfs_lock(struct file *, int, struct file_lock *);
+
+/*
+ * linux/fs/nfs/write.c
+ */
+extern int nfs_writepage(struct file *, struct page *);
+extern int nfs_check_failed_request(struct inode *);
+
+/*
+ * Try to write back everything synchronously (but check the
+ * return value!)
+ */
+extern int nfs_wb_all(struct inode *);
+extern int nfs_wb_page(struct inode *, struct page *);
+extern int nfs_wb_file(struct inode *, struct file *);
+
+/*
+ * Invalidate write-backs, possibly trying to write them
+ * back first..
+ */
+extern void nfs_inval(struct inode *);
+extern int nfs_updatepage(struct file *, struct page *, unsigned long, unsigned int, int);
+
+/*
+ * linux/fs/nfs/read.c
+ */
+extern int nfs_readpage(struct file *, struct page *);
+
+/*
+ * linux/fs/mount_clnt.c
+ * (Used only by nfsroot module)
+ */
+extern int nfs_mount(struct sockaddr_in *, char *, struct nfs_fh *);
+
+/*
+ * inline functions
+ */
+static inline int
+nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
+ return 0;
+ return _nfs_revalidate_inode(server, dentry);
+}
+
+/* NFS root */
+
+extern int nfs_root_mount(struct super_block *sb);
+
+#endif /* __KERNEL__ */
+
+/*
+ * NFS debug flags
+ */
+#define NFSDBG_VFS 0x0001
+#define NFSDBG_DIRCACHE 0x0002
+#define NFSDBG_LOOKUPCACHE 0x0004
+#define NFSDBG_PAGECACHE 0x0008
+#define NFSDBG_PROC 0x0010
+#define NFSDBG_XDR 0x0020
+#define NFSDBG_FILE 0x0040
+#define NFSDBG_ROOT 0x0080
+#define NFSDBG_ALL 0xFFFF
+
+#ifdef __KERNEL__
+# undef ifdebug
+# ifdef NFS_DEBUG
+# define ifdebug(fac) if (nfs_debug & NFSDBG_##fac)
+# else
+# define ifdebug(fac) if (0)
+# endif
+#endif /* __KERNEL */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/nfs_fs_i.h b/pfinet/linux-src/include/linux/nfs_fs_i.h
new file mode 100644
index 00000000..885f21f0
--- /dev/null
+++ b/pfinet/linux-src/include/linux/nfs_fs_i.h
@@ -0,0 +1,71 @@
+#ifndef _NFS_FS_I
+#define _NFS_FS_I
+
+#include <linux/nfs.h>
+#include <linux/pipe_fs_i.h>
+
+/*
+ * nfs fs inode data in memory
+ */
+struct nfs_inode_info {
+ /*
+ * This is a place holder so named pipes on NFS filesystems
+ * work (more or less correctly). This must be first in the
+ * struct because the data is really accessed via inode->u.pipe_i.
+ */
+ struct pipe_inode_info pipeinfo;
+
+ /*
+ * Various flags
+ */
+ unsigned short flags;
+
+ /*
+ * read_cache_jiffies is when we started read-caching this inode,
+ * and read_cache_mtime is the mtime of the inode at that time.
+ * attrtimeo is for how long the cached information is assumed
+ * to be valid. A successful attribute revalidation doubles
+ * attrtimeo (up to acregmax/acdirmax), a failure resets it to
+ * acregmin/acdirmin.
+ *
+ * We need to revalidate the cached attrs for this inode if
+ *
+ * jiffies - read_cache_jiffies > attrtimeo
+ *
+ * and invalidate any cached data/flush out any dirty pages if
+ * we find that
+ *
+ * mtime != read_cache_mtime
+ */
+ unsigned long read_cache_jiffies;
+ unsigned long read_cache_mtime;
+ unsigned long attrtimeo;
+
+ /*
+ * This is the list of dirty unwritten pages.
+ * NFSv3 will want to add a list for written but uncommitted
+ * pages.
+ */
+ struct nfs_wreq * writeback;
+};
+
+/*
+ * Legal inode flag values
+ */
+#define NFS_INO_REVALIDATE 0x0001 /* revalidating attrs */
+#define NFS_IS_SNAPSHOT 0x0010 /* a snapshot file */
+
+/*
+ * NFS lock info
+ */
+struct nfs_lock_info {
+ u32 state;
+ u32 flags;
+};
+
+/*
+ * Lock flag values
+ */
+#define NFS_LCK_GRANTED 0x0001 /* lock has been granted */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/nfs_fs_sb.h b/pfinet/linux-src/include/linux/nfs_fs_sb.h
new file mode 100644
index 00000000..343455ec
--- /dev/null
+++ b/pfinet/linux-src/include/linux/nfs_fs_sb.h
@@ -0,0 +1,31 @@
+#ifndef _NFS_FS_SB
+#define _NFS_FS_SB
+
+#include <linux/nfs.h>
+#include <linux/in.h>
+
+/*
+ * NFS client parameters stored in the superblock.
+ */
+struct nfs_server {
+ struct rpc_clnt * client; /* RPC client handle */
+ int flags; /* various flags */
+ int rsize; /* read size */
+ int wsize; /* write size */
+ unsigned int bsize; /* server block size */
+ unsigned int acregmin; /* attr cache timeouts */
+ unsigned int acregmax;
+ unsigned int acdirmin;
+ unsigned int acdirmax;
+ char * hostname; /* remote hostname */
+};
+
+/*
+ * nfs super-block data in memory
+ */
+struct nfs_sb_info {
+ struct nfs_server s_server;
+ struct nfs_fh s_root;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/nfs_mount.h b/pfinet/linux-src/include/linux/nfs_mount.h
new file mode 100644
index 00000000..60493b15
--- /dev/null
+++ b/pfinet/linux-src/include/linux/nfs_mount.h
@@ -0,0 +1,53 @@
+#ifndef _LINUX_NFS_MOUNT_H
+#define _LINUX_NFS_MOUNT_H
+
+/*
+ * linux/include/linux/nfs_mount.h
+ *
+ * Copyright (C) 1992 Rick Sladkey
+ *
+ * structure passed from user-space to kernel-space during an nfs mount
+ */
+
+/*
+ * WARNING! Do not delete or change the order of these fields. If
+ * a new field is required then add it to the end. The version field
+ * tracks which fields are present. This will ensure some measure of
+ * mount-to-kernel version compatibility. Some of these aren't used yet
+ * but here they are anyway.
+ */
+#define NFS_MOUNT_VERSION 3
+
+struct nfs_mount_data {
+ int version; /* 1 */
+ int fd; /* 1 */
+ struct nfs_fh root; /* 1 */
+ int flags; /* 1 */
+ int rsize; /* 1 */
+ int wsize; /* 1 */
+ int timeo; /* 1 */
+ int retrans; /* 1 */
+ int acregmin; /* 1 */
+ int acregmax; /* 1 */
+ int acdirmin; /* 1 */
+ int acdirmax; /* 1 */
+ struct sockaddr_in addr; /* 1 */
+ char hostname[256]; /* 1 */
+ int namlen; /* 2 */
+ unsigned int bsize; /* 3 */
+};
+
+/* bits in the flags field */
+
+#define NFS_MOUNT_SOFT 0x0001 /* 1 */
+#define NFS_MOUNT_INTR 0x0002 /* 1 */
+#define NFS_MOUNT_SECURE 0x0004 /* 1 */
+#define NFS_MOUNT_POSIX 0x0008 /* 1 */
+#define NFS_MOUNT_NOCTO 0x0010 /* 1 */
+#define NFS_MOUNT_NOAC 0x0020 /* 1 */
+#define NFS_MOUNT_TCP 0x0040 /* 2 */
+#define NFS_MOUNT_VER3 0x0080 /* 3 */
+#define NFS_MOUNT_KERBEROS 0x0100 /* 3 */
+#define NFS_MOUNT_NONLM 0x0200 /* 3 */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/nfsiod.h b/pfinet/linux-src/include/linux/nfsiod.h
new file mode 100644
index 00000000..fdd07a2d
--- /dev/null
+++ b/pfinet/linux-src/include/linux/nfsiod.h
@@ -0,0 +1,52 @@
+/*
+ * linux/include/linux/nfsiod.h
+ *
+ * Declarations for asynchronous NFS RPC calls.
+ *
+ */
+
+#ifndef _LINUX_NFSIOD_H
+#define _LINUX_NFSIOD_H
+
+#include <linux/rpcsock.h>
+#include <linux/nfs_fs.h>
+
+#ifdef __KERNEL__
+
+/*
+ * This is the callback handler for nfsiod requests.
+ * Note that the callback procedure must NOT sleep.
+ */
+struct nfsiod_req;
+typedef int (*nfsiod_callback_t)(int result, struct nfsiod_req *);
+
+/*
+ * This is the nfsiod request struct.
+ */
+struct nfsiod_req {
+ struct nfsiod_req * rq_next;
+ struct nfsiod_req * rq_prev;
+ struct wait_queue * rq_wait;
+ struct rpc_ioreq rq_rpcreq;
+ nfsiod_callback_t rq_callback;
+ struct nfs_server * rq_server;
+ struct inode * rq_inode;
+ struct page * rq_page;
+
+ /* user creds */
+ uid_t rq_fsuid;
+ gid_t rq_fsgid;
+ int rq_groups[NGROUPS];
+
+ /* retry handling */
+ int rq_retries;
+};
+
+struct nfsiod_req * nfsiod_reserve(struct nfs_server *);
+void nfsiod_release(struct nfsiod_req *);
+void nfsiod_enqueue(struct nfsiod_req *);
+int nfsiod(void);
+
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_NFSIOD_H */
diff --git a/pfinet/linux-src/include/linux/nls.h b/pfinet/linux-src/include/linux/nls.h
new file mode 100644
index 00000000..efcd5892
--- /dev/null
+++ b/pfinet/linux-src/include/linux/nls.h
@@ -0,0 +1,56 @@
+struct nls_unicode {
+ unsigned char uni1;
+ unsigned char uni2;
+};
+
+struct nls_table {
+ char *charset;
+ unsigned char **page_uni2charset;
+ struct nls_unicode *charset2uni;
+
+ void (*inc_use_count) (void);
+ void (*dec_use_count) (void);
+ struct nls_table *next;
+};
+
+/* nls.c */
+extern int init_nls(void);
+extern int register_nls(struct nls_table *);
+extern int unregister_nls(struct nls_table *);
+extern struct nls_table *find_nls(char *);
+extern struct nls_table *load_nls(char *);
+extern void unload_nls(struct nls_table *);
+extern struct nls_table *load_nls_default(void);
+
+extern int utf8_mbtowc(__u16 *, const __u8 *, int);
+extern int utf8_mbstowcs(__u16 *, const __u8 *, int);
+extern int utf8_wctomb(__u8 *, __u16, int);
+extern int utf8_wcstombs(__u8 *, const __u16 *, int);
+
+extern int init_nls_iso8859_1(void);
+extern int init_nls_iso8859_2(void);
+extern int init_nls_iso8859_3(void);
+extern int init_nls_iso8859_4(void);
+extern int init_nls_iso8859_5(void);
+extern int init_nls_iso8859_6(void);
+extern int init_nls_iso8859_7(void);
+extern int init_nls_iso8859_8(void);
+extern int init_nls_iso8859_9(void);
+extern int init_nls_iso8859_15(void);
+extern int init_nls_cp437(void);
+extern int init_nls_cp737(void);
+extern int init_nls_cp775(void);
+extern int init_nls_cp850(void);
+extern int init_nls_cp852(void);
+extern int init_nls_cp855(void);
+extern int init_nls_cp857(void);
+extern int init_nls_cp860(void);
+extern int init_nls_cp861(void);
+extern int init_nls_cp862(void);
+extern int init_nls_cp863(void);
+extern int init_nls_cp864(void);
+extern int init_nls_cp865(void);
+extern int init_nls_cp866(void);
+extern int init_nls_cp869(void);
+extern int init_nls_cp874(void);
+extern int init_nls_koi8_r(void);
diff --git a/pfinet/linux-src/include/linux/notifier.h b/pfinet/linux-src/include/linux/notifier.h
new file mode 100644
index 00000000..1e8bf707
--- /dev/null
+++ b/pfinet/linux-src/include/linux/notifier.h
@@ -0,0 +1,115 @@
+/*
+ * Routines to manage notifier chains for passing status changes to any
+ * interested routines. We need this instead of hard coded call lists so
+ * that modules can poke their nose into the innards. The network devices
+ * needed them so here they are for the rest of you.
+ *
+ * Alan Cox <Alan.Cox@linux.org>
+ */
+
+#ifndef _LINUX_NOTIFIER_H
+#define _LINUX_NOTIFIER_H
+#include <linux/errno.h>
+
+struct notifier_block
+{
+ int (*notifier_call)(struct notifier_block *self, unsigned long, void *);
+ struct notifier_block *next;
+ int priority;
+};
+
+
+#ifdef __KERNEL__
+
+#define NOTIFY_DONE 0x0000 /* Don't care */
+#define NOTIFY_OK 0x0001 /* Suits me */
+#define NOTIFY_STOP_MASK 0x8000 /* Don't call further */
+#define NOTIFY_BAD (NOTIFY_STOP_MASK|0x0002) /* Bad/Veto action */
+
+extern __inline__ int notifier_chain_register(struct notifier_block **list, struct notifier_block *n)
+{
+ while(*list)
+ {
+ if(n->priority > (*list)->priority)
+ break;
+ list= &((*list)->next);
+ }
+ n->next = *list;
+ *list=n;
+ return 0;
+}
+
+/*
+ * Warning to any non GPL module writers out there.. these functions are
+ * GPL'd
+ */
+
+extern __inline__ int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n)
+{
+ while((*nl)!=NULL)
+ {
+ if((*nl)==n)
+ {
+ *nl=n->next;
+ return 0;
+ }
+ nl=&((*nl)->next);
+ }
+ return -ENOENT;
+}
+
+/*
+ * This is one of these things that is generally shorter inline
+ */
+
+extern __inline__ int notifier_call_chain(struct notifier_block **n, unsigned long val, void *v)
+{
+ int ret=NOTIFY_DONE;
+ struct notifier_block *nb = *n;
+ while(nb)
+ {
+ ret=nb->notifier_call(nb,val,v);
+ if(ret&NOTIFY_STOP_MASK)
+ return ret;
+ nb=nb->next;
+ }
+ return ret;
+}
+
+
+/*
+ * Declared notifiers so far. I can imagine quite a few more chains
+ * over time (eg laptop power reset chains, reboot chain (to clean
+ * device units up), device [un]mount chain, module load/unload chain,
+ * low memory chain, screenblank chain (for plug in modular screenblankers)
+ * VC switch chains (for loadable kernel svgalib VC switch helpers) etc...
+ */
+
+/* netdevice notifier chain */
+#define NETDEV_UP 0x0001 /* For now you can't veto a device up/down */
+#define NETDEV_DOWN 0x0002
+#define NETDEV_REBOOT 0x0003 /* Tell a protocol stack a network interface
+ detected a hardware crash and restarted
+ - we can use this eg to kick tcp sessions
+ once done */
+#define NETDEV_CHANGE 0x0004 /* Notify device state change */
+#define NETDEV_REGISTER 0x0005
+#define NETDEV_UNREGISTER 0x0006
+#define NETDEV_CHANGEMTU 0x0007
+#define NETDEV_CHANGEADDR 0x0008
+#define NETDEV_GOING_DOWN 0x0009
+#define NETDEV_CHANGENAME 0x000A
+
+#define SYS_DOWN 0x0001 /* Notify of system down */
+#define SYS_RESTART SYS_DOWN
+#define SYS_HALT 0x0002 /* Notify of system halt */
+#define SYS_POWER_OFF 0x0003 /* Notify of system power off */
+
+/*
+ * Publicly visible notifier objects
+ */
+
+extern struct notifier_block *boot_notifier_list;
+
+#endif
+#endif
diff --git a/pfinet/linux-src/include/linux/ntfs_fs.h b/pfinet/linux-src/include/linux/ntfs_fs.h
new file mode 100644
index 00000000..aafdb78d
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ntfs_fs.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_NTFS_FS_H
+#define _LINUX_NTFS_FS_H
+
+int init_ntfs_fs(void);
+
+#endif
diff --git a/pfinet/linux-src/include/linux/ntfs_fs_i.h b/pfinet/linux-src/include/linux/ntfs_fs_i.h
new file mode 100644
index 00000000..cab27748
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ntfs_fs_i.h
@@ -0,0 +1,83 @@
+#ifndef _LINUX_NTFS_FS_I_H
+#define _LINUX_NTFS_FS_I_H
+
+/* Forward declarations, to keep number of mutual includes low */
+struct ntfs_attribute;
+struct ntfs_sb_info;
+
+/* Duplicate definitions from ntfs/ntfstypes.h */
+#ifndef NTFS_INTEGRAL_TYPES
+#define NTFS_INTEGRAL_TYPES
+typedef u8 ntfs_u8;
+typedef u16 ntfs_u16;
+typedef u32 ntfs_u32;
+typedef u64 ntfs_u64;
+typedef s8 ntfs_s8;
+typedef s16 ntfs_s16;
+typedef s32 ntfs_s32;
+typedef s64 ntfs_s64;
+#endif
+
+#ifndef NTMODE_T
+#define NTMODE_T
+typedef __kernel_mode_t ntmode_t;
+#endif
+#ifndef NTFS_UID_T
+#define NTFS_UID_T
+typedef __kernel_uid_t ntfs_uid_t;
+#endif
+#ifndef NTFS_GID_T
+#define NTFS_GID_T
+typedef __kernel_gid_t ntfs_gid_t;
+#endif
+#ifndef NTFS_SIZE_T
+#define NTFS_SIZE_T
+typedef __kernel_size_t ntfs_size_t;
+#endif
+#ifndef NTFS_TIME_T
+#define NTFS_TIME_T
+typedef __kernel_time_t ntfs_time_t;
+#endif
+
+/* unicode character type */
+#ifndef NTFS_WCHAR_T
+#define NTFS_WCHAR_T
+typedef unsigned short ntfs_wchar_t;
+#endif
+/* file offset */
+#ifndef NTFS_OFFSET_T
+#define NTFS_OFFSET_T
+typedef unsigned long long ntfs_offset_t;
+#endif
+/* UTC */
+#ifndef NTFS_TIME64_T
+#define NTFS_TIME64_T
+typedef unsigned long long ntfs_time64_t;
+#endif
+/* This is really unsigned long long. So we support only volumes up to 2 TB */
+#ifndef NTFS_CLUSTER_T
+#define NTFS_CLUSTER_T
+typedef unsigned int ntfs_cluster_t;
+#endif
+
+/* Definition of NTFS in-memory inode structure */
+struct ntfs_inode_info{
+ struct ntfs_sb_info *vol;
+ int i_number; /* should be really 48 bits */
+ unsigned sequence_number;
+ unsigned char* attr; /* array of the attributes */
+ int attr_count; /* size of attrs[] */
+ struct ntfs_attribute *attrs;
+ int record_count; /* size of records[] */
+ /* array of the record numbers of the MFT
+ whose attributes have been inserted in the inode */
+ int *records;
+ union{
+ struct{
+ int recordsize;
+ int clusters_per_record;
+ }index;
+ } u;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/ntfs_fs_sb.h b/pfinet/linux-src/include/linux/ntfs_fs_sb.h
new file mode 100644
index 00000000..898ef710
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ntfs_fs_sb.h
@@ -0,0 +1,44 @@
+#ifndef _LINUX_NTFS_FS_SB_H
+#define _LINUX_NTFS_FS_SB_H
+
+struct ntfs_sb_info{
+ /* Configuration provided by user at mount time */
+ ntfs_uid_t uid;
+ ntfs_gid_t gid;
+ ntmode_t umask;
+ unsigned int nct;
+ void *nls_map;
+ unsigned int ngt;
+ /* Configuration provided by user with ntfstools */
+ ntfs_size_t partition_bias; /* for access to underlying device */
+ /* Attribute definitions */
+ ntfs_u32 at_standard_information;
+ ntfs_u32 at_attribute_list;
+ ntfs_u32 at_file_name;
+ ntfs_u32 at_security_descriptor;
+ ntfs_u32 at_data;
+ ntfs_u32 at_index_root;
+ ntfs_u32 at_index_allocation;
+ ntfs_u32 at_bitmap;
+ ntfs_u32 at_symlink; /* aka SYMBOLIC_LINK or REPARSE_POINT */
+ /* Data read from the boot file */
+ int blocksize;
+ int clusterfactor;
+ int clustersize;
+ int mft_recordsize;
+ int mft_clusters_per_record;
+ int index_recordsize;
+ int index_clusters_per_record;
+ int mft_cluster;
+ /* data read from special files */
+ unsigned char *mft;
+ unsigned short *upcase;
+ unsigned int upcase_length;
+ /* inodes we always hold onto */
+ struct ntfs_inode_info *mft_ino;
+ struct ntfs_inode_info *mftmirr;
+ struct ntfs_inode_info *bitmap;
+ struct super_block *sb;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/nubus.h b/pfinet/linux-src/include/linux/nubus.h
new file mode 100644
index 00000000..73aafeb4
--- /dev/null
+++ b/pfinet/linux-src/include/linux/nubus.h
@@ -0,0 +1,95 @@
+
+struct nubus_slot
+{
+ int slot_flags;
+#define NUBUS_DEVICE_PRESENT 1
+#define NUBUS_DEVICE_ACTIVE 2
+#define NUBUS_DEVICE_IRQ 4
+ __u32 slot_directory;
+ __u32 slot_dlength;
+ __u32 slot_crc;
+ __u8 slot_rev;
+ __u8 slot_format;
+ __u8 slot_lanes;
+ /*
+ * Stuff we pulled from the directory
+ */
+ __u32 slot_dirbase;
+ __u32 slot_thisdir;
+ char slot_vendor[64];
+ char slot_cardname[64];
+};
+
+struct nbnamevec
+{
+ char *name;
+ int id;
+};
+
+struct nubus_dir
+{
+ unsigned char *base;
+ int length;
+ int count;
+ int mask;
+};
+
+struct nubus_dirent
+{
+ unsigned char type;
+ int value; /* Actually 24bits used */
+ int mask;
+ int base; /* For dirptr function */
+};
+
+struct nubus_type
+{
+ __u16 category;
+ __u16 type;
+ __u16 DrHW;
+ __u16 DrSW;
+};
+
+#define NUBUS_CAT_BOARD 0x0001
+#define NUBUS_CAT_DISPLAY 0x0003
+#define NUBUS_CAT_NETWORK 0x0004
+#define NUBUS_CAT_COMMUNICATIONS 0x0006
+#define NUBUS_CAT_FONT 0x0009
+#define NUBUS_CAT_CPU 0x000A
+
+#define RES_ID_TYPE 0x0001
+#define RES_ID_NAME 0x0002
+#define RES_ID_BOARD_DIR 0x0001
+#define RES_ID_FLAGS 0x0007
+
+struct nubus_device_specifier
+{
+ int (*setup)(struct nubus_device_specifier *, int slot, struct nubus_type *);
+ struct nubus_device_specifier *next;
+};
+
+
+extern void register_nubus_device(struct nubus_device_specifier *nb);
+extern void unregister_nubus_device(struct nubus_device_specifier *nb);
+
+extern struct nubus_dir *nubus_openrootdir(int slot);
+extern struct nubus_dir *nubus_opensubdir(struct nubus_dirent *d);
+extern void nubus_closedir(struct nubus_dir *);
+extern struct nubus_dirent *nubus_readdir(struct nubus_dir *);
+extern unsigned char *nubus_dirptr(struct nubus_dirent *d);
+extern void nubus_strncpy(int slot, void *to, unsigned char *p, int len);
+extern void nubus_memcpy(int slot, void *to, unsigned char *p, int len);
+extern void nubus_init(void);
+extern void nubus_sweep_video(void);
+extern int nubus_ethernet_addr(int slot, unsigned char *addr);
+
+extern __inline void *nubus_slot_addr(int slot)
+{
+ return (void *)(0xF0000000|(slot<<24));
+}
+
+extern int nubus_hwreg_present(volatile void *ptr);
+
+extern void nubus_init_via(void);
+extern int nubus_free_irq(int slot);
+extern int nubus_request_irq(int slot, void *dev_id, void (*handler)(int,void *,struct pt_regs *));
diff --git a/pfinet/linux-src/include/linux/nvram.h b/pfinet/linux-src/include/linux/nvram.h
new file mode 100644
index 00000000..9e05db19
--- /dev/null
+++ b/pfinet/linux-src/include/linux/nvram.h
@@ -0,0 +1,18 @@
+#ifndef _LINUX_NVRAM_H
+#define _LINUX_NVRAM_H
+
+#include <linux/ioctl.h>
+
+/* /dev/nvram ioctls */
+#define NVRAM_INIT _IO('p', 0x40) /* initialize NVRAM and set checksum */
+#define NVRAM_SETCKS _IO('p', 0x41) /* recalculate checksum */
+
+#ifdef __KERNEL__
+extern unsigned char nvram_read_byte( int i );
+extern void nvram_write_byte( unsigned char c, int i );
+extern int nvram_check_checksum( void );
+extern void nvram_set_checksum( void );
+extern int nvram_init( void );
+#endif
+
+#endif /* _LINUX_NVRAM_H */
diff --git a/pfinet/linux-src/include/linux/openpic.h b/pfinet/linux-src/include/linux/openpic.h
new file mode 100644
index 00000000..68102042
--- /dev/null
+++ b/pfinet/linux-src/include/linux/openpic.h
@@ -0,0 +1,362 @@
+/*
+ * linux/openpic.h -- OpenPIC definitions
+ *
+ * Copyright (C) 1997 Geert Uytterhoeven
+ *
+ * This file is based on the following documentation:
+ *
+ * The Open Programmable Interrupt Controller (PIC)
+ * Register Interface Specification Revision 1.2
+ *
+ * Issue Date: October 1995
+ *
+ * Issued jointly by Advanced Micro Devices and Cyrix Corporation
+ *
+ * AMD is a registered trademark of Advanced Micro Devices, Inc.
+ * Copyright (C) 1995, Advanced Micro Devices, Inc. and Cyrix, Inc.
+ * All Rights Reserved.
+ *
+ * To receive a copy of this documentation, send an email to openpic@amd.com.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _LINUX_OPENPIC_H
+#define _LINUX_OPENPIC_H
+
+#if !defined(__powerpc__) && !defined(__i386__)
+#error Unsupported OpenPIC platform
+#endif
+
+
+#ifdef __KERNEL__
+
+ /*
+ * OpenPIC supports up to 2048 interrupt sources and up to 32 processors
+ */
+
+#define OPENPIC_MAX_SOURCES 2048
+#define OPENPIC_MAX_PROCESSORS 32
+
+#define OPENPIC_NUM_TIMERS 4
+#define OPENPIC_NUM_IPI 4
+#define OPENPIC_NUM_PRI 16
+#define OPENPIC_NUM_VECTORS 256
+
+
+ /*
+ * Vector numbers
+ */
+
+#define OPENPIC_VEC_TIMER 64 /* and up */
+#define OPENPIC_VEC_IPI 70 /* and up */
+#define OPENPIC_VEC_SPURIOUS 127
+
+
+ /*
+ * OpenPIC Registers are 32 bits and aligned on 128 bit boundaries
+ */
+
+typedef struct _OpenPIC_Reg {
+ u_int Reg; /* Little endian! */
+ char Pad[0xc];
+} OpenPIC_Reg;
+
+
+ /*
+ * Per Processor Registers
+ */
+
+typedef struct _OpenPIC_Processor {
+ /*
+ * Private Shadow Registers (for SLiC backwards compatibility)
+ */
+ u_int IPI0_Dispatch_Shadow; /* Write Only */
+ char Pad1[0x4];
+ u_int IPI0_Vector_Priority_Shadow; /* Read/Write */
+ char Pad2[0x34];
+ /*
+ * Interprocessor Interrupt Command Ports
+ */
+ OpenPIC_Reg _IPI_Dispatch[OPENPIC_NUM_IPI]; /* Write Only */
+ /*
+ * Current Task Priority Register
+ */
+ OpenPIC_Reg _Current_Task_Priority; /* Read/Write */
+#ifndef __powerpc__
+ /*
+ * Who Am I Register
+ */
+ OpenPIC_Reg _Who_Am_I; /* Read Only */
+#else
+ char Pad3[0x10];
+#endif
+#ifndef __i386__
+ /*
+ * Interrupt Acknowledge Register
+ */
+ OpenPIC_Reg _Interrupt_Acknowledge; /* Read Only */
+#else
+ char Pad4[0x10];
+#endif
+ /*
+ * End of Interrupt (EOI) Register
+ */
+ OpenPIC_Reg _EOI; /* Read/Write */
+ char Pad5[0xf40];
+} OpenPIC_Processor;
+
+
+ /*
+ * Timer Registers
+ */
+
+typedef struct _OpenPIC_Timer {
+ OpenPIC_Reg _Current_Count; /* Read Only */
+ OpenPIC_Reg _Base_Count; /* Read/Write */
+ OpenPIC_Reg _Vector_Priority; /* Read/Write */
+ OpenPIC_Reg _Destination; /* Read/Write */
+} OpenPIC_Timer;
+
+
+ /*
+ * Global Registers
+ */
+
+typedef struct _OpenPIC_Global {
+ /*
+ * Feature Reporting Registers
+ */
+ OpenPIC_Reg _Feature_Reporting0; /* Read Only */
+ OpenPIC_Reg _Feature_Reporting1; /* Future Expansion */
+ /*
+ * Global Configuration Registers
+ */
+ OpenPIC_Reg _Global_Configuration0; /* Read/Write */
+ OpenPIC_Reg _Global_Configuration1; /* Future Expansion */
+ /*
+ * Vendor Specific Registers
+ */
+ OpenPIC_Reg _Vendor_Specific[4];
+ /*
+ * Vendor Identification Register
+ */
+ OpenPIC_Reg _Vendor_Identification; /* Read Only */
+ /*
+ * Processor Initialization Register
+ */
+ OpenPIC_Reg _Processor_Initialization; /* Read/Write */
+ /*
+ * IPI Vector/Priority Registers
+ */
+ OpenPIC_Reg _IPI_Vector_Priority[OPENPIC_NUM_IPI]; /* Read/Write */
+ /*
+ * Spurious Vector Register
+ */
+ OpenPIC_Reg _Spurious_Vector; /* Read/Write */
+ /*
+ * Global Timer Registers
+ */
+ OpenPIC_Reg _Timer_Frequency; /* Read/Write */
+ OpenPIC_Timer Timer[OPENPIC_NUM_TIMERS];
+ char Pad1[0xee00];
+} OpenPIC_Global;
+
+
+ /*
+ * Interrupt Source Registers
+ */
+
+typedef struct _OpenPIC_Source {
+ OpenPIC_Reg _Vector_Priority; /* Read/Write */
+ OpenPIC_Reg _Destination; /* Read/Write */
+} OpenPIC_Source;
+
+
+ /*
+ * OpenPIC Register Map
+ */
+
+struct OpenPIC {
+#ifndef __powerpc__
+ /*
+ * Per Processor Registers --- Private Access
+ */
+ OpenPIC_Processor Private;
+#else
+ char Pad1[0x1000];
+#endif
+ /*
+ * Global Registers
+ */
+ OpenPIC_Global Global;
+ /*
+ * Interrupt Source Configuration Registers
+ */
+ OpenPIC_Source Source[OPENPIC_MAX_SOURCES];
+ /*
+ * Per Processor Registers
+ */
+ OpenPIC_Processor Processor[OPENPIC_MAX_PROCESSORS];
+};
+
+extern volatile struct OpenPIC *OpenPIC;
+extern u_int OpenPIC_NumInitSenses;
+extern u_char *OpenPIC_InitSenses;
+
+
+ /*
+ * Current Task Priority Register
+ */
+
+#define OPENPIC_CURRENT_TASK_PRIORITY_MASK 0x0000000f
+
+ /*
+ * Who Am I Register
+ */
+
+#define OPENPIC_WHO_AM_I_ID_MASK 0x0000001f
+
+ /*
+ * Feature Reporting Register 0
+ */
+
+#define OPENPIC_FEATURE_LAST_SOURCE_MASK 0x07ff0000
+#define OPENPIC_FEATURE_LAST_SOURCE_SHIFT 16
+#define OPENPIC_FEATURE_LAST_PROCESSOR_MASK 0x00001f00
+#define OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT 8
+#define OPENPIC_FEATURE_VERSION_MASK 0x000000ff
+
+ /*
+ * Global Configuration Register 0
+ */
+
+#define OPENPIC_CONFIG_RESET 0x80000000
+#define OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE 0x20000000
+#define OPENPIC_CONFIG_BASE_MASK 0x000fffff
+
+ /*
+ * Vendor Identification Register
+ */
+
+#define OPENPIC_VENDOR_ID_STEPPING_MASK 0x00ff0000
+#define OPENPIC_VENDOR_ID_STEPPING_SHIFT 16
+#define OPENPIC_VENDOR_ID_DEVICE_ID_MASK 0x0000ff00
+#define OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT 8
+#define OPENPIC_VENDOR_ID_VENDOR_ID_MASK 0x000000ff
+
+ /*
+ * Vector/Priority Registers
+ */
+
+#define OPENPIC_MASK 0x80000000
+#define OPENPIC_ACTIVITY 0x40000000 /* Read Only */
+#define OPENPIC_PRIORITY_MASK 0x000f0000
+#define OPENPIC_PRIORITY_SHIFT 16
+#define OPENPIC_VECTOR_MASK 0x000000ff
+
+
+ /*
+ * Interrupt Source Registers
+ */
+
+#define OPENPIC_SENSE_POLARITY 0x00800000 /* Undoc'd */
+#define OPENPIC_SENSE_LEVEL 0x00400000
+
+
+ /*
+ * Timer Registers
+ */
+
+#define OPENPIC_COUNT_MASK 0x7fffffff
+#define OPENPIC_TIMER_TOGGLE 0x80000000
+#define OPENPIC_TIMER_COUNT_INHIBIT 0x80000000
+
+
+ /*
+ * Aliases to make life simpler
+ */
+
+/* Per Processor Registers */
+#define IPI_Dispatch(i) _IPI_Dispatch[i].Reg
+#define Current_Task_Priority _Current_Task_Priority.Reg
+#ifndef __powerpc__
+#define Who_Am_I _Who_Am_I.Reg
+#endif
+#ifndef __i386__
+#define Interrupt_Acknowledge _Interrupt_Acknowledge.Reg
+#endif
+#define EOI _EOI.Reg
+
+/* Global Registers */
+#define Feature_Reporting0 _Feature_Reporting0.Reg
+#define Feature_Reporting1 _Feature_Reporting1.Reg
+#define Global_Configuration0 _Global_Configuration0.Reg
+#define Global_Configuration1 _Global_Configuration1.Reg
+#define Vendor_Specific(i) _Vendor_Specific[i].Reg
+#define Vendor_Identification _Vendor_Identification.Reg
+#define Processor_Initialization _Processor_Initialization.Reg
+#define IPI_Vector_Priority(i) _IPI_Vector_Priority[i].Reg
+#define Spurious_Vector _Spurious_Vector.Reg
+#define Timer_Frequency _Timer_Frequency.Reg
+
+/* Timer Registers */
+#define Current_Count _Current_Count.Reg
+#define Base_Count _Base_Count.Reg
+#define Vector_Priority _Vector_Priority.Reg
+#define Destination _Destination.Reg
+
+/* Interrupt Source Registers */
+#define Vector_Priority _Vector_Priority.Reg
+#define Destination _Destination.Reg
+
+ /*
+ * OpenPIC Operations
+ */
+
+/* Global Operations */
+extern void openpic_init(int);
+extern void openpic_reset(void);
+extern void openpic_enable_8259_pass_through(void);
+extern void openpic_disable_8259_pass_through(void);
+#ifndef __i386__
+extern u_int openpic_irq(u_int cpu);
+#endif
+#ifndef __powerpc__
+extern void openpic_eoi(void);
+extern u_int openpic_get_priority(void);
+extern void openpic_set_priority(u_int pri);
+#else
+extern void openpic_eoi(u_int cpu);
+extern u_int openpic_get_priority(u_int cpu);
+extern void openpic_set_priority(u_int cpu, u_int pri);
+#endif
+extern u_int openpic_get_spurious(void);
+extern void openpic_set_spurious(u_int vector);
+extern void openpic_init_processor(u_int cpumask);
+
+/* Interprocessor Interrupts */
+extern void openpic_initipi(u_int ipi, u_int pri, u_int vector);
+#ifndef __powerpc__
+extern void openpic_cause_IPI(u_int ipi, u_int cpumask);
+#else
+extern void openpic_cause_IPI(u_int cpu, u_int ipi, u_int cpumask);
+#endif
+
+/* Timer Interrupts */
+extern void openpic_inittimer(u_int timer, u_int pri, u_int vector);
+extern void openpic_maptimer(u_int timer, u_int cpumask);
+
+/* Interrupt Sources */
+extern void openpic_enable_irq(u_int irq);
+extern void openpic_disable_irq(u_int irq);
+extern void openpic_initirq(u_int irq, u_int pri, u_int vector, int polarity,
+ int is_level);
+extern void openpic_mapirq(u_int irq, u_int cpumask);
+extern void openpic_set_sense(u_int irq, int sense);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_OPENPIC_H */
diff --git a/pfinet/linux-src/include/linux/pagemap.h b/pfinet/linux-src/include/linux/pagemap.h
new file mode 100644
index 00000000..9bf5efd8
--- /dev/null
+++ b/pfinet/linux-src/include/linux/pagemap.h
@@ -0,0 +1,156 @@
+#ifndef _LINUX_PAGEMAP_H
+#define _LINUX_PAGEMAP_H
+
+#include <asm/system.h>
+
+/*
+ * Page-mapping primitive inline functions
+ *
+ * Copyright 1995 Linus Torvalds
+ */
+
+#include <linux/mm.h>
+#include <linux/fs.h>
+
+static inline unsigned long page_address(struct page * page)
+{
+ return PAGE_OFFSET + PAGE_SIZE * (page - mem_map);
+}
+
+/*
+ * The page cache can done in larger chunks than
+ * one page, because it allows for more efficient
+ * throughput (it can then be mapped into user
+ * space in smaller chunks for same flexibility).
+ *
+ * Or rather, it _will_ be done in larger chunks.
+ */
+#define PAGE_CACHE_SHIFT PAGE_SHIFT
+#define PAGE_CACHE_SIZE PAGE_SIZE
+#define PAGE_CACHE_MASK PAGE_MASK
+
+#define page_cache_alloc() __get_free_page(GFP_USER)
+#define page_cache_free(x) free_page(x)
+#define page_cache_release(x) __free_page(x)
+
+/*
+ * From a kernel address, get the "struct page *"
+ */
+#define page_cache_entry(x) (mem_map + MAP_NR(x))
+
+#define PAGE_HASH_BITS page_hash_bits
+#define PAGE_HASH_MASK page_hash_mask
+
+extern unsigned long page_cache_size; /* # of pages currently in the hash table */
+extern unsigned int page_hash_bits;
+extern unsigned int page_hash_mask;
+extern struct page **page_hash_table;
+
+extern void page_cache_init(unsigned long);
+
+/*
+ * We use a power-of-two hash table to avoid a modulus,
+ * and get a reasonable hash by knowing roughly how the
+ * inode pointer and offsets are distributed (ie, we
+ * roughly know which bits are "significant")
+ */
+static inline unsigned long _page_hashfn(struct inode * inode, unsigned long offset)
+{
+#define i (((unsigned long) inode)/(sizeof(struct inode) & ~ (sizeof(struct inode) - 1)))
+#define o ((offset >> PAGE_SHIFT) + (offset & ~PAGE_MASK))
+ return ((i+o) & PAGE_HASH_MASK);
+#undef i
+#undef o
+}
+
+#define page_hash(inode,offset) (page_hash_table+_page_hashfn(inode,offset))
+
+static inline struct page * __find_page(struct inode * inode, unsigned long offset, struct page *page)
+{
+ goto inside;
+ for (;;) {
+ page = page->next_hash;
+inside:
+ if (!page)
+ goto not_found;
+ if (page->inode != inode)
+ continue;
+ if (page->offset == offset)
+ break;
+ }
+ /* Found the page. */
+ atomic_inc(&page->count);
+ set_bit(PG_referenced, &page->flags);
+not_found:
+ return page;
+}
+
+static inline struct page *find_page(struct inode * inode, unsigned long offset)
+{
+ return __find_page(inode, offset, *page_hash(inode, offset));
+}
+
+static inline void remove_page_from_hash_queue(struct page * page)
+{
+ if(page->pprev_hash) {
+ if(page->next_hash)
+ page->next_hash->pprev_hash = page->pprev_hash;
+ *page->pprev_hash = page->next_hash;
+ page->pprev_hash = NULL;
+ }
+ page_cache_size--;
+}
+
+static inline void __add_page_to_hash_queue(struct page * page, struct page **p)
+{
+ page_cache_size++;
+ if((page->next_hash = *p) != NULL)
+ (*p)->pprev_hash = &page->next_hash;
+ *p = page;
+ page->pprev_hash = p;
+}
+
+static inline void add_page_to_hash_queue(struct page * page, struct inode * inode, unsigned long offset)
+{
+ __add_page_to_hash_queue(page, page_hash(inode,offset));
+}
+
+static inline void remove_page_from_inode_queue(struct page * page)
+{
+ struct inode * inode = page->inode;
+
+ page->inode = NULL;
+ inode->i_nrpages--;
+ if (inode->i_pages == page)
+ inode->i_pages = page->next;
+ if (page->next)
+ page->next->prev = page->prev;
+ if (page->prev)
+ page->prev->next = page->next;
+ page->next = NULL;
+ page->prev = NULL;
+}
+
+static inline void add_page_to_inode_queue(struct inode * inode, struct page * page)
+{
+ struct page **p = &inode->i_pages;
+
+ inode->i_nrpages++;
+ page->inode = inode;
+ page->prev = NULL;
+ if ((page->next = *p) != NULL)
+ page->next->prev = page;
+ *p = page;
+}
+
+extern void __wait_on_page(struct page *);
+static inline void wait_on_page(struct page * page)
+{
+ if (PageLocked(page))
+ __wait_on_page(page);
+}
+
+extern void update_vm_cache_conditional(struct inode *, unsigned long, const char *, int, unsigned long);
+extern void update_vm_cache(struct inode *, unsigned long, const char *, int);
+
+#endif
diff --git a/pfinet/linux-src/include/linux/param.h b/pfinet/linux-src/include/linux/param.h
new file mode 100644
index 00000000..092e92f6
--- /dev/null
+++ b/pfinet/linux-src/include/linux/param.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_PARAM_H
+#define _LINUX_PARAM_H
+
+#include <asm/param.h>
+
+#endif
diff --git a/pfinet/linux-src/include/linux/parport.h b/pfinet/linux-src/include/linux/parport.h
new file mode 100644
index 00000000..856fc016
--- /dev/null
+++ b/pfinet/linux-src/include/linux/parport.h
@@ -0,0 +1,396 @@
+/* $Id: parport.h,v 1.1 1998/05/17 10:57:52 andrea Exp andrea $ */
+
+#ifndef _PARPORT_H_
+#define _PARPORT_H_
+
+/* Start off with user-visible constants */
+
+/* Maximum of 8 ports per machine */
+#define PARPORT_MAX 8
+
+/* Magic numbers */
+#define PARPORT_IRQ_NONE -1
+#define PARPORT_DMA_NONE -1
+#define PARPORT_IRQ_AUTO -2
+#define PARPORT_DMA_AUTO -2
+#define PARPORT_DISABLE -2
+#define PARPORT_IRQ_PROBEONLY -3
+
+#define PARPORT_CONTROL_STROBE 0x1
+#define PARPORT_CONTROL_AUTOFD 0x2
+#define PARPORT_CONTROL_INIT 0x4
+#define PARPORT_CONTROL_SELECT 0x8
+#define PARPORT_CONTROL_INTEN 0x10
+#define PARPORT_CONTROL_DIRECTION 0x20
+
+#define PARPORT_STATUS_ERROR 0x8
+#define PARPORT_STATUS_SELECT 0x10
+#define PARPORT_STATUS_PAPEROUT 0x20
+#define PARPORT_STATUS_ACK 0x40
+#define PARPORT_STATUS_BUSY 0x80
+
+/* Type classes for Plug-and-Play probe. */
+typedef enum {
+ PARPORT_CLASS_LEGACY = 0, /* Non-IEEE1284 device */
+ PARPORT_CLASS_PRINTER,
+ PARPORT_CLASS_MODEM,
+ PARPORT_CLASS_NET,
+ PARPORT_CLASS_HDC, /* Hard disk controller */
+ PARPORT_CLASS_PCMCIA,
+ PARPORT_CLASS_MEDIA, /* Multimedia device */
+ PARPORT_CLASS_FDC, /* Floppy disk controller */
+ PARPORT_CLASS_PORTS,
+ PARPORT_CLASS_SCANNER,
+ PARPORT_CLASS_DIGCAM,
+ PARPORT_CLASS_OTHER, /* Anything else */
+ PARPORT_CLASS_UNSPEC /* No CLS field in ID */
+} parport_device_class;
+
+/* The "modes" entry in parport is a bit field representing the following
+ * modes.
+ * Note that PARPORT_MODE_PCECPEPP is for the SMC EPP+ECP mode which is NOT
+ * 100% compatible with EPP.
+ */
+#define PARPORT_MODE_PCSPP 0x0001
+#define PARPORT_MODE_PCPS2 0x0002
+#define PARPORT_MODE_PCEPP 0x0004
+#define PARPORT_MODE_PCECP 0x0008
+#define PARPORT_MODE_PCECPEPP 0x0010
+#define PARPORT_MODE_PCECR 0x0020 /* ECR Register Exists */
+#define PARPORT_MODE_PCECPPS2 0x0040
+
+/* The rest is for the kernel only */
+#ifdef __KERNEL__
+
+#include <asm/system.h>
+#include <asm/ptrace.h>
+#include <asm/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/config.h>
+
+#define PARPORT_NEED_GENERIC_OPS
+
+/* Define this later. */
+struct parport;
+
+struct pc_parport_state {
+ unsigned int ctr;
+ unsigned int ecr;
+};
+
+struct parport_state {
+ union {
+ struct pc_parport_state pc;
+ /* ARC has no state. */
+ /* AX uses same state information as PC */
+ void *misc;
+ } u;
+};
+
+struct parport_operations {
+ void (*write_data)(struct parport *, unsigned char);
+ unsigned char (*read_data)(struct parport *);
+ void (*write_control)(struct parport *, unsigned char);
+ unsigned char (*read_control)(struct parport *);
+ unsigned char (*frob_control)(struct parport *, unsigned char mask, unsigned char val);
+ void (*write_econtrol)(struct parport *, unsigned char);
+ unsigned char (*read_econtrol)(struct parport *);
+ unsigned char (*frob_econtrol)(struct parport *, unsigned char mask, unsigned char val);
+ void (*write_status)(struct parport *, unsigned char);
+ unsigned char (*read_status)(struct parport *);
+ void (*write_fifo)(struct parport *, unsigned char);
+ unsigned char (*read_fifo)(struct parport *);
+
+ void (*change_mode)(struct parport *, int);
+
+ void (*release_resources)(struct parport *);
+ int (*claim_resources)(struct parport *);
+
+ void (*epp_write_data)(struct parport *, unsigned char);
+ unsigned char (*epp_read_data)(struct parport *);
+ void (*epp_write_addr)(struct parport *, unsigned char);
+ unsigned char (*epp_read_addr)(struct parport *);
+ int (*epp_check_timeout)(struct parport *);
+ size_t (*epp_write_block)(struct parport *, void *, size_t);
+ size_t (*epp_read_block)(struct parport *, void *, size_t);
+
+ int (*ecp_write_block)(struct parport *, void *, size_t, void (*fn)(struct parport *, void *, size_t), void *);
+ int (*ecp_read_block)(struct parport *, void *, size_t, void (*fn)(struct parport *, void *, size_t), void *);
+
+ void (*init_state)(struct parport_state *);
+ void (*save_state)(struct parport *, struct parport_state *);
+ void (*restore_state)(struct parport *, struct parport_state *);
+
+ void (*enable_irq)(struct parport *);
+ void (*disable_irq)(struct parport *);
+ void (*interrupt)(int, void *, struct pt_regs *);
+
+ void (*inc_use_count)(void);
+ void (*dec_use_count)(void);
+ void (*fill_inode)(struct inode *inode, int fill);
+};
+
+struct parport_device_info {
+ parport_device_class class;
+ const char *class_name;
+ const char *mfr;
+ const char *model;
+ const char *cmdset;
+ const char *description;
+};
+
+/* Each device can have two callback functions:
+ * 1) a preemption function, called by the resource manager to request
+ * that the driver relinquish control of the port. The driver should
+ * return zero if it agrees to release the port, and nonzero if it
+ * refuses. Do not call parport_release() - the kernel will do this
+ * implicitly.
+ *
+ * 2) a wake-up function, called by the resource manager to tell drivers
+ * that the port is available to be claimed. If a driver wants to use
+ * the port, it should call parport_claim() here.
+ */
+
+/* A parallel port device */
+struct pardevice {
+ const char *name;
+ struct parport *port;
+ int (*preempt)(void *);
+ void (*wakeup)(void *);
+ void *private;
+ void (*irq_func)(int, void *, struct pt_regs *);
+ unsigned int flags;
+ struct pardevice *next;
+ struct pardevice *prev;
+ struct parport_state *state; /* saved status over preemption */
+ struct wait_queue *wait_q;
+ unsigned long int time;
+ unsigned long int timeslice;
+ unsigned int waiting;
+ struct pardevice *waitprev;
+ struct pardevice *waitnext;
+};
+
+/* Directory information for the /proc interface */
+struct parport_dir {
+ struct proc_dir_entry *entry; /* Directory /proc/parport/X */
+ struct proc_dir_entry *irq; /* .../irq */
+ struct proc_dir_entry *devices; /* .../devices */
+ struct proc_dir_entry *hardware; /* .../hardware */
+ struct proc_dir_entry *probe; /* .../autoprobe */
+ char name[4];
+};
+
+/* A parallel port */
+struct parport {
+ unsigned long base; /* base address */
+ unsigned int size; /* IO extent */
+ const char *name;
+ int irq; /* interrupt (or -1 for none) */
+ int dma;
+ unsigned int modes;
+
+ struct pardevice *devices;
+ struct pardevice *cad; /* port owner */
+
+ struct pardevice *waithead;
+ struct pardevice *waittail;
+
+ struct parport *next;
+ unsigned int flags;
+
+ struct parport_dir pdir;
+ struct parport_device_info probe_info;
+
+ struct parport_operations *ops;
+ void *private_data; /* for lowlevel driver */
+
+ int number; /* port index - the `n' in `parportn' */
+ spinlock_t pardevice_lock;
+ spinlock_t waitlist_lock;
+ rwlock_t cad_lock;
+
+ /* PCI parallel I/O card support. */
+ unsigned long base_hi; /* base address (hi - ECR) */
+};
+
+/* parport_register_port registers a new parallel port at the given address (if
+ * one does not already exist) and returns a pointer to it. This entails
+ * claiming the I/O region, IRQ and DMA.
+ * NULL is returned if initialisation fails.
+ */
+struct parport *parport_register_port(unsigned long base, int irq, int dma,
+ struct parport_operations *ops);
+
+/* Unregister a port. */
+extern void parport_unregister_port(struct parport *port);
+
+/* parport_in_use returns nonzero if there are devices attached to a port. */
+#define parport_in_use(x) ((x)->devices != NULL)
+
+/* Put a parallel port to sleep; release its hardware resources. Only possible
+ * if no devices are registered. */
+extern void parport_quiesce(struct parport *);
+
+/* parport_enumerate returns a pointer to the linked list of all the ports
+ * in this machine.
+ */
+struct parport *parport_enumerate(void);
+
+/* parport_register_device declares that a device is connected to a port, and
+ * tells the kernel all it needs to know.
+ * pf is the preemption function (may be NULL for no callback)
+ * kf is the wake-up function (may be NULL for no callback)
+ * irq_func is the interrupt handler (may be NULL for no interrupts)
+ * handle is a user pointer that gets handed to callback functions.
+ */
+struct pardevice *parport_register_device(struct parport *port,
+ const char *name,
+ int (*pf)(void *), void (*kf)(void *),
+ void (*irq_func)(int, void *, struct pt_regs *),
+ int flags, void *handle);
+
+/* parport_unregister unlinks a device from the chain. */
+extern void parport_unregister_device(struct pardevice *dev);
+
+/* parport_claim tries to gain ownership of the port for a particular driver.
+ * This may fail (return non-zero) if another driver is busy. If this
+ * driver has registered an interrupt handler, it will be enabled.
+ */
+extern int parport_claim(struct pardevice *dev);
+
+/* parport_claim_or_block is the same, but sleeps if the port cannot be
+ claimed. Return value is 1 if it slept, 0 normally and -errno on error. */
+extern int parport_claim_or_block(struct pardevice *dev);
+
+/* parport_release reverses a previous parport_claim. This can never fail,
+ * though the effects are undefined (except that they are bad) if you didn't
+ * previously own the port. Once you have released the port you should make
+ * sure that neither your code nor the hardware on the port tries to initiate
+ * any communication without first re-claiming the port.
+ * If you mess with the port state (enabling ECP for example) you should
+ * clean up before releasing the port.
+ */
+
+extern void parport_release(struct pardevice *dev);
+
+/* parport_yield relinquishes the port if it would be helpful to other
+ * drivers. The return value is the same as for parport_claim.
+ */
+extern __inline__ int parport_yield(struct pardevice *dev)
+{
+ unsigned long int timeslip = (jiffies - dev->time);
+ if ((dev->port->waithead == NULL) || (timeslip < dev->timeslice))
+ return 0;
+ parport_release(dev);
+ return parport_claim(dev);
+}
+
+/* parport_yield_blocking is the same but uses parport_claim_or_block
+ * instead of parport_claim.
+ */
+extern __inline__ int parport_yield_blocking(struct pardevice *dev)
+{
+ unsigned long int timeslip = (jiffies - dev->time);
+ if ((dev->port->waithead == NULL) || (timeslip < dev->timeslice))
+ return 0;
+ parport_release(dev);
+ return parport_claim_or_block(dev);
+}
+
+/*
+ * Lowlevel drivers _can_ call this support function to handle irqs.
+ */
+extern __inline__ void parport_generic_irq(int irq, struct parport *port,
+ struct pt_regs *regs)
+{
+ read_lock(&port->cad_lock);
+ if (!port->cad)
+ goto out_unlock;
+ if (port->cad->irq_func)
+ port->cad->irq_func(irq, port->cad->private, regs);
+ else
+ printk(KERN_ERR "%s: irq%d happened with irq_func NULL "
+ "with %s as cad!\n", port->name, irq, port->cad->name);
+ out_unlock:
+ read_unlock(&port->cad_lock);
+}
+
+/* Flags used to identify what a device does. */
+#define PARPORT_DEV_TRAN 0 /* WARNING !! DEPRECATED !! */
+#define PARPORT_DEV_LURK (1<<0) /* WARNING !! DEPRECATED !! */
+#define PARPORT_DEV_EXCL (1<<1) /* Need exclusive access. */
+
+#define PARPORT_FLAG_COMA (1<<0)
+#define PARPORT_FLAG_EXCL (1<<1) /* EXCL driver registered. */
+
+extern void parport_parse_irqs(int, const char *[], int irqval[]);
+extern int parport_ieee1284_nibble_mode_ok(struct parport *, unsigned char);
+extern int parport_wait_peripheral(struct parport *, unsigned char, unsigned
+ char);
+
+/* Prototypes from parport_procfs */
+extern int parport_proc_init(void);
+extern void parport_proc_cleanup(void);
+extern int parport_proc_register(struct parport *pp);
+extern int parport_proc_unregister(struct parport *pp);
+
+extern void dec_parport_count(void);
+extern void inc_parport_count(void);
+
+extern int parport_probe(struct parport *port, char *buffer, int len);
+extern void parport_probe_one(struct parport *port);
+extern void (*parport_probe_hook)(struct parport *port);
+
+/* If PC hardware is the only type supported, we can optimise a bit. */
+#if (defined(CONFIG_PARPORT_PC) || defined(CONFIG_PARPORT_PC_MODULE)) && !(defined(CONFIG_PARPORT_AX) || defined(CONFIG_PARPORT_AX_MODULE)) && !(defined(CONFIG_PARPORT_ARC) || defined(CONFIG_PARPORT_ARC_MODULE)) && !defined(CONFIG_PARPORT_OTHER)
+#undef PARPORT_NEED_GENERIC_OPS
+#include <linux/parport_pc.h>
+#define parport_write_data(p,x) parport_pc_write_data(p,x)
+#define parport_read_data(p) parport_pc_read_data(p)
+#define parport_write_control(p,x) parport_pc_write_control(p,x)
+#define parport_read_control(p) parport_pc_read_control(p)
+#define parport_frob_control(p,m,v) parport_pc_frob_control(p,m,v)
+#define parport_write_econtrol(p,x) parport_pc_write_econtrol(p,x)
+#define parport_read_econtrol(p) parport_pc_read_econtrol(p)
+#define parport_frob_econtrol(p,m,v) parport_pc_frob_econtrol(p,m,v)
+#define parport_write_status(p,v) parport_pc_write_status(p,v)
+#define parport_read_status(p) parport_pc_read_status(p)
+#define parport_write_fifo(p,v) parport_pc_write_fifo(p,v)
+#define parport_read_fifo(p) parport_pc_read_fifo(p)
+#define parport_change_mode(p,m) parport_pc_change_mode(p,m)
+#define parport_release_resources(p) parport_pc_release_resources(p)
+#define parport_claim_resources(p) parport_pc_claim_resources(p)
+#define parport_epp_write_data(p,x) parport_pc_write_epp(p,x)
+#define parport_epp_read_data(p) parport_pc_read_epp(p)
+#define parport_epp_write_addr(p,x) parport_pc_write_epp_addr(p,x)
+#define parport_epp_read_addr(p) parport_pc_read_epp_addr(p)
+#define parport_epp_check_timeout(p) parport_pc_check_epp_timeout(p)
+#endif
+
+#ifdef PARPORT_NEED_GENERIC_OPS
+/* Generic operations vector through the dispatch table. */
+#define parport_write_data(p,x) (p)->ops->write_data(p,x)
+#define parport_read_data(p) (p)->ops->read_data(p)
+#define parport_write_control(p,x) (p)->ops->write_control(p,x)
+#define parport_read_control(p) (p)->ops->read_control(p)
+#define parport_frob_control(p,m,v) (p)->ops->frob_control(p,m,v)
+#define parport_write_econtrol(p,x) (p)->ops->write_econtrol(p,x)
+#define parport_read_econtrol(p) (p)->ops->read_econtrol(p)
+#define parport_frob_econtrol(p,m,v) (p)->ops->frob_econtrol(p,m,v)
+#define parport_write_status(p,v) (p)->ops->write_status(p,v)
+#define parport_read_status(p) (p)->ops->read_status(p)
+#define parport_write_fifo(p,v) (p)->ops->write_fifo(p,v)
+#define parport_read_fifo(p) (p)->ops->read_fifo(p)
+#define parport_change_mode(p,m) (p)->ops->change_mode(p,m)
+#define parport_release_resources(p) (p)->ops->release_resources(p)
+#define parport_claim_resources(p) (p)->ops->claim_resources(p)
+#define parport_epp_write_data(p,x) (p)->ops->epp_write_data(p,x)
+#define parport_epp_read_data(p) (p)->ops->epp_read_data(p)
+#define parport_epp_write_addr(p,x) (p)->ops->epp_write_addr(p,x)
+#define parport_epp_read_addr(p) (p)->ops->epp_read_addr(p)
+#define parport_epp_check_timeout(p) (p)->ops->epp_check_timeout(p)
+#endif
+
+#endif /* __KERNEL__ */
+#endif /* _PARPORT_H_ */
diff --git a/pfinet/linux-src/include/linux/parport_pc.h b/pfinet/linux-src/include/linux/parport_pc.h
new file mode 100644
index 00000000..f7ef3406
--- /dev/null
+++ b/pfinet/linux-src/include/linux/parport_pc.h
@@ -0,0 +1,152 @@
+#ifndef __LINUX_PARPORT_PC_H
+#define __LINUX_PARPORT_PC_H
+
+#include <asm/io.h>
+
+/* --- register definitions ------------------------------- */
+
+#define ECONTROL(p) ((p)->base_hi + 0x2)
+#define CONFIGB(p) ((p)->base_hi + 0x1)
+#define CONFIGA(p) ((p)->base_hi + 0x0)
+#define FIFO(p) ((p)->base_hi + 0x0)
+#define EPPDATA(p) ((p)->base + 0x4)
+#define EPPADDR(p) ((p)->base + 0x3)
+#define CONTROL(p) ((p)->base + 0x2)
+#define STATUS(p) ((p)->base + 0x1)
+#define DATA(p) ((p)->base + 0x0)
+
+/* Private data for PC low-level driver. */
+struct parport_pc_private {
+ /* Contents of CTR. */
+ unsigned char ctr;
+};
+
+extern int parport_pc_epp_clear_timeout(struct parport *pb);
+
+extern volatile unsigned char parport_pc_ctr;
+
+extern __inline__ void parport_pc_write_epp(struct parport *p, unsigned char d)
+{
+ outb(d, EPPDATA(p));
+}
+
+extern __inline__ unsigned char parport_pc_read_epp(struct parport *p)
+{
+ return inb(EPPDATA(p));
+}
+
+extern __inline__ void parport_pc_write_epp_addr(struct parport *p, unsigned char d)
+{
+ outb(d, EPPADDR(p));
+}
+
+extern __inline__ unsigned char parport_pc_read_epp_addr(struct parport *p)
+{
+ return inb(EPPADDR(p));
+}
+
+extern __inline__ int parport_pc_check_epp_timeout(struct parport *p)
+{
+ if (!(inb(STATUS(p)) & 1))
+ return 0;
+ parport_pc_epp_clear_timeout(p);
+ return 1;
+}
+
+extern __inline__ unsigned char parport_pc_read_configb(struct parport *p)
+{
+ return inb(CONFIGB(p));
+}
+
+extern __inline__ void parport_pc_write_data(struct parport *p, unsigned char d)
+{
+ outb(d, DATA(p));
+}
+
+extern __inline__ unsigned char parport_pc_read_data(struct parport *p)
+{
+ return inb(DATA(p));
+}
+
+extern __inline__ void parport_pc_write_control(struct parport *p, unsigned char d)
+{
+ struct parport_pc_private *priv = p->private_data;
+ priv->ctr = d;/* update soft copy */
+ outb(d, CONTROL(p));
+}
+
+extern __inline__ unsigned char parport_pc_read_control(struct parport *p)
+{
+ struct parport_pc_private *priv = p->private_data;
+ return priv->ctr;
+}
+
+extern __inline__ unsigned char parport_pc_frob_control(struct parport *p, unsigned char mask, unsigned char val)
+{
+ struct parport_pc_private *priv = p->private_data;
+ unsigned char ctr = priv->ctr;
+ ctr = (ctr & ~mask) ^ val;
+ outb (ctr, CONTROL(p));
+ return priv->ctr = ctr; /* update soft copy */
+}
+
+extern __inline__ void parport_pc_write_status(struct parport *p, unsigned char d)
+{
+ outb(d, STATUS(p));
+}
+
+extern __inline__ unsigned char parport_pc_read_status(struct parport *p)
+{
+ return inb(STATUS(p));
+}
+
+extern __inline__ void parport_pc_write_econtrol(struct parport *p, unsigned char d)
+{
+ outb(d, ECONTROL(p));
+}
+
+extern __inline__ unsigned char parport_pc_read_econtrol(struct parport *p)
+{
+ return inb(ECONTROL(p));
+}
+
+extern __inline__ unsigned char parport_pc_frob_econtrol(struct parport *p, unsigned char mask, unsigned char val)
+{
+ unsigned char old = inb(ECONTROL(p));
+ outb(((old & ~mask) ^ val), ECONTROL(p));
+ return old;
+}
+
+extern void parport_pc_change_mode(struct parport *p, int m);
+
+extern void parport_pc_write_fifo(struct parport *p, unsigned char v);
+
+extern unsigned char parport_pc_read_fifo(struct parport *p);
+
+extern void parport_pc_disable_irq(struct parport *p);
+
+extern void parport_pc_enable_irq(struct parport *p);
+
+extern void parport_pc_release_resources(struct parport *p);
+
+extern int parport_pc_claim_resources(struct parport *p);
+
+extern void parport_pc_init_state(struct parport_state *s);
+
+extern void parport_pc_save_state(struct parport *p, struct parport_state *s);
+
+extern void parport_pc_restore_state(struct parport *p, struct parport_state *s);
+
+extern size_t parport_pc_epp_read_block(struct parport *p, void *buf, size_t length);
+
+extern size_t parport_pc_epp_write_block(struct parport *p, void *buf, size_t length);
+
+extern int parport_pc_ecp_read_block(struct parport *p, void *buf, size_t length, void (*fn)(struct parport *, void *, size_t), void *handle);
+
+extern int parport_pc_ecp_write_block(struct parport *p, void *buf, size_t length, void (*fn)(struct parport *, void *, size_t), void *handle);
+
+extern void parport_pc_inc_use_count(void);
+
+extern void parport_pc_dec_use_count(void);
+
+#endif
diff --git a/pfinet/linux-src/include/linux/pc_keyb.h b/pfinet/linux-src/include/linux/pc_keyb.h
new file mode 100644
index 00000000..29ccd395
--- /dev/null
+++ b/pfinet/linux-src/include/linux/pc_keyb.h
@@ -0,0 +1,130 @@
+/*
+ * include/linux/pc_keyb.h
+ *
+ * PC Keyboard And Keyboard Controller
+ *
+ * (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+/*
+ * Configuration Switches
+ */
+
+#undef KBD_REPORT_ERR /* Report keyboard errors */
+#define KBD_REPORT_UNKN /* Report unknown scan codes */
+#define KBD_REPORT_TIMEOUTS /* Report keyboard timeouts */
+#undef KBD_IS_FOCUS_9000 /* We have the brain-damaged FOCUS-9000 keyboard */
+#undef INITIALIZE_MOUSE /* Define if your PS/2 mouse needs initialization. */
+
+
+
+#define KBD_INIT_TIMEOUT 1000 /* Timeout in ms for initializing the keyboard */
+#define KBC_TIMEOUT 250 /* Timeout in ms for sending to keyboard controller */
+#define KBD_TIMEOUT 1000 /* Timeout in ms for keyboard command acknowledge */
+
+/*
+ * Internal variables of the driver
+ */
+
+extern unsigned char pckbd_read_mask;
+extern unsigned char aux_device_present;
+
+/*
+ * Keyboard Controller Registers on normal PCs.
+ */
+
+#define KBD_STATUS_REG 0x64 /* Status register (R) */
+#define KBD_CNTL_REG 0x64 /* Controller command register (W) */
+#define KBD_DATA_REG 0x60 /* Keyboard data register (R/W) */
+
+/*
+ * Keyboard Controller Commands
+ */
+
+#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */
+#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */
+#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */
+#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */
+#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */
+#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */
+#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */
+#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */
+#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */
+#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */
+#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if
+ initiated by the auxiliary device */
+#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */
+
+/*
+ * Keyboard Commands
+ */
+
+#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */
+#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */
+#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */
+#define KBD_CMD_DISABLE 0xF5 /* Disable scanning */
+#define KBD_CMD_RESET 0xFF /* Reset */
+
+/*
+ * Keyboard Replies
+ */
+
+#define KBD_REPLY_POR 0xAA /* Power on reset */
+#define KBD_REPLY_ACK 0xFA /* Command ACK */
+#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */
+
+/*
+ * Status Register Bits
+ */
+
+#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
+#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
+#define KBD_STAT_SELFTEST 0x04 /* Self test successful */
+#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */
+#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */
+#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
+#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */
+#define KBD_STAT_PERR 0x80 /* Parity error */
+
+#define AUX_STAT_OBF (KBD_STAT_OBF | KBD_STAT_MOUSE_OBF)
+
+/*
+ * Controller Mode Register Bits
+ */
+
+#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */
+#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */
+#define KBD_MODE_SYS 0x04 /* The system flag (?) */
+#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
+#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
+#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */
+#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
+#define KBD_MODE_RFU 0x80
+
+/*
+ * Mouse Commands
+ */
+
+#define AUX_SET_RES 0xE8 /* Set resolution */
+#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */
+#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */
+#define AUX_GET_SCALE 0xE9 /* Get scaling factor */
+#define AUX_SET_STREAM 0xEA /* Set stream mode */
+#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */
+#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */
+#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */
+#define AUX_RESET 0xFF /* Reset aux device */
+#define AUX_ACK 0xFA /* Command byte ACK. */
+
+#define AUX_BUF_SIZE 2048 /* This might be better divisible by
+ three to make overruns stay in sync
+ but then the read function would need
+ a lock etc - ick */
+
+struct aux_queue {
+ unsigned long head;
+ unsigned long tail;
+ struct wait_queue *proc_list;
+ struct fasync_struct *fasync;
+ unsigned char buf[AUX_BUF_SIZE];
+};
diff --git a/pfinet/linux-src/include/linux/pci.h b/pfinet/linux-src/include/linux/pci.h
new file mode 100644
index 00000000..6ad71fa4
--- /dev/null
+++ b/pfinet/linux-src/include/linux/pci.h
@@ -0,0 +1,1359 @@
+/*
+ * $Id: pci.h,v 1.87 1998/10/11 15:13:12 mj Exp $
+ *
+ * PCI defines and function prototypes
+ * Copyright 1994, Drew Eckhardt
+ * Copyright 1997--1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ * For more information, please consult the following manuals (look at
+ * http://www.pcisig.com/ for how to get them):
+ *
+ * PCI BIOS Specification
+ * PCI Local Bus Specification
+ * PCI to PCI Bridge Specification
+ * PCI System Design Guide
+ */
+
+#ifndef LINUX_PCI_H
+#define LINUX_PCI_H
+
+/*
+ * Under PCI, each device has 256 bytes of configuration address space,
+ * of which the first 64 bytes are standardized as follows:
+ */
+#define PCI_VENDOR_ID 0x00 /* 16 bits */
+#define PCI_DEVICE_ID 0x02 /* 16 bits */
+#define PCI_COMMAND 0x04 /* 16 bits */
+#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */
+#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */
+#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */
+#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */
+#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */
+#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */
+#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */
+#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */
+#define PCI_COMMAND_SERR 0x100 /* Enable SERR */
+#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */
+
+#define PCI_STATUS 0x06 /* 16 bits */
+#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */
+#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */
+#define PCI_STATUS_UDF 0x40 /* Support User Definable Features */
+#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */
+#define PCI_STATUS_PARITY 0x100 /* Detected parity error */
+#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */
+#define PCI_STATUS_DEVSEL_FAST 0x000
+#define PCI_STATUS_DEVSEL_MEDIUM 0x200
+#define PCI_STATUS_DEVSEL_SLOW 0x400
+#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */
+#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */
+#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */
+#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */
+#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */
+
+#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8
+ revision */
+#define PCI_REVISION_ID 0x08 /* Revision ID */
+#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */
+#define PCI_CLASS_DEVICE 0x0a /* Device class */
+
+#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */
+#define PCI_LATENCY_TIMER 0x0d /* 8 bits */
+#define PCI_HEADER_TYPE 0x0e /* 8 bits */
+#define PCI_HEADER_TYPE_NORMAL 0
+#define PCI_HEADER_TYPE_BRIDGE 1
+#define PCI_HEADER_TYPE_CARDBUS 2
+
+#define PCI_BIST 0x0f /* 8 bits */
+#define PCI_BIST_CODE_MASK 0x0f /* Return result */
+#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */
+#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */
+
+/*
+ * Base addresses specify locations in memory or I/O space.
+ * Decoded size can be determined by writing a value of
+ * 0xffffffff to the register, and reading it back. Only
+ * 1 bits are decoded.
+ */
+#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */
+#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */
+#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */
+#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */
+#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */
+#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */
+#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */
+#define PCI_BASE_ADDRESS_SPACE_IO 0x01
+#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00
+#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06
+#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */
+#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M */
+#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */
+#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */
+#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL)
+#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL)
+/* bit 1 is reserved if address_space = 1 */
+
+/* Header type 0 (normal devices) */
+#define PCI_CARDBUS_CIS 0x28
+#define PCI_SUBSYSTEM_VENDOR_ID 0x2c
+#define PCI_SUBSYSTEM_ID 0x2e
+#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */
+#define PCI_ROM_ADDRESS_ENABLE 0x01
+#define PCI_ROM_ADDRESS_MASK (~0x7ffUL)
+
+#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */
+
+/* 0x35-0x3b are reserved */
+#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */
+#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */
+#define PCI_MIN_GNT 0x3e /* 8 bits */
+#define PCI_MAX_LAT 0x3f /* 8 bits */
+
+/* Header type 1 (PCI-to-PCI bridges) */
+#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */
+#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */
+#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */
+#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */
+#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */
+#define PCI_IO_LIMIT 0x1d
+#define PCI_IO_RANGE_TYPE_MASK 0x0f /* I/O bridging type */
+#define PCI_IO_RANGE_TYPE_16 0x00
+#define PCI_IO_RANGE_TYPE_32 0x01
+#define PCI_IO_RANGE_MASK ~0x0f
+#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */
+#define PCI_MEMORY_BASE 0x20 /* Memory range behind */
+#define PCI_MEMORY_LIMIT 0x22
+#define PCI_MEMORY_RANGE_TYPE_MASK 0x0f
+#define PCI_MEMORY_RANGE_MASK ~0x0f
+#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */
+#define PCI_PREF_MEMORY_LIMIT 0x26
+#define PCI_PREF_RANGE_TYPE_MASK 0x0f
+#define PCI_PREF_RANGE_TYPE_32 0x00
+#define PCI_PREF_RANGE_TYPE_64 0x01
+#define PCI_PREF_RANGE_MASK ~0x0f
+#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */
+#define PCI_PREF_LIMIT_UPPER32 0x2c
+#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */
+#define PCI_IO_LIMIT_UPPER16 0x32
+/* 0x34-0x3b is reserved */
+#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */
+/* 0x3c-0x3d are same as for htype 0 */
+#define PCI_BRIDGE_CONTROL 0x3e
+#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */
+#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */
+#define PCI_BRIDGE_CTL_NO_ISA 0x04 /* Disable bridging of ISA ports */
+#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */
+#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */
+#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */
+#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */
+
+/* Header type 2 (CardBus bridges) */
+/* 0x14-0x15 reserved */
+#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */
+#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */
+#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */
+#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */
+#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */
+#define PCI_CB_MEMORY_BASE_0 0x1c
+#define PCI_CB_MEMORY_LIMIT_0 0x20
+#define PCI_CB_MEMORY_BASE_1 0x24
+#define PCI_CB_MEMORY_LIMIT_1 0x28
+#define PCI_CB_IO_BASE_0 0x2c
+#define PCI_CB_IO_BASE_0_HI 0x2e
+#define PCI_CB_IO_LIMIT_0 0x30
+#define PCI_CB_IO_LIMIT_0_HI 0x32
+#define PCI_CB_IO_BASE_1 0x34
+#define PCI_CB_IO_BASE_1_HI 0x36
+#define PCI_CB_IO_LIMIT_1 0x38
+#define PCI_CB_IO_LIMIT_1_HI 0x3a
+#define PCI_CB_IO_RANGE_MASK ~0x03
+/* 0x3c-0x3d are same as for htype 0 */
+#define PCI_CB_BRIDGE_CONTROL 0x3e
+#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */
+#define PCI_CB_BRIDGE_CTL_SERR 0x02
+#define PCI_CB_BRIDGE_CTL_ISA 0x04
+#define PCI_CB_BRIDGE_CTL_VGA 0x08
+#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20
+#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */
+#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */
+#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */
+#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200
+#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400
+#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40
+#define PCI_CB_SUBSYSTEM_ID 0x42
+#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */
+/* 0x48-0x7f reserved */
+
+/* Capability lists */
+#define PCI_CAP_LIST_ID 0 /* Capability ID */
+#define PCI_CAP_ID_PM 0x01 /* Power Management */
+#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */
+#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */
+
+/* Device classes and subclasses */
+
+#define PCI_CLASS_NOT_DEFINED 0x0000
+#define PCI_CLASS_NOT_DEFINED_VGA 0x0001
+
+#define PCI_BASE_CLASS_STORAGE 0x01
+#define PCI_CLASS_STORAGE_SCSI 0x0100
+#define PCI_CLASS_STORAGE_IDE 0x0101
+#define PCI_CLASS_STORAGE_FLOPPY 0x0102
+#define PCI_CLASS_STORAGE_IPI 0x0103
+#define PCI_CLASS_STORAGE_RAID 0x0104
+#define PCI_CLASS_STORAGE_OTHER 0x0180
+
+#define PCI_BASE_CLASS_NETWORK 0x02
+#define PCI_CLASS_NETWORK_ETHERNET 0x0200
+#define PCI_CLASS_NETWORK_TOKEN_RING 0x0201
+#define PCI_CLASS_NETWORK_FDDI 0x0202
+#define PCI_CLASS_NETWORK_ATM 0x0203
+#define PCI_CLASS_NETWORK_OTHER 0x0280
+
+#define PCI_BASE_CLASS_DISPLAY 0x03
+#define PCI_CLASS_DISPLAY_VGA 0x0300
+#define PCI_CLASS_DISPLAY_XGA 0x0301
+#define PCI_CLASS_DISPLAY_OTHER 0x0380
+
+#define PCI_BASE_CLASS_MULTIMEDIA 0x04
+#define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400
+#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401
+#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480
+
+#define PCI_BASE_CLASS_MEMORY 0x05
+#define PCI_CLASS_MEMORY_RAM 0x0500
+#define PCI_CLASS_MEMORY_FLASH 0x0501
+#define PCI_CLASS_MEMORY_OTHER 0x0580
+
+#define PCI_BASE_CLASS_BRIDGE 0x06
+#define PCI_CLASS_BRIDGE_HOST 0x0600
+#define PCI_CLASS_BRIDGE_ISA 0x0601
+#define PCI_CLASS_BRIDGE_EISA 0x0602
+#define PCI_CLASS_BRIDGE_MC 0x0603
+#define PCI_CLASS_BRIDGE_PCI 0x0604
+#define PCI_CLASS_BRIDGE_PCMCIA 0x0605
+#define PCI_CLASS_BRIDGE_NUBUS 0x0606
+#define PCI_CLASS_BRIDGE_CARDBUS 0x0607
+#define PCI_CLASS_BRIDGE_OTHER 0x0680
+
+#define PCI_BASE_CLASS_COMMUNICATION 0x07
+#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700
+#define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701
+#define PCI_CLASS_COMMUNICATION_OTHER 0x0780
+
+#define PCI_BASE_CLASS_SYSTEM 0x08
+#define PCI_CLASS_SYSTEM_PIC 0x0800
+#define PCI_CLASS_SYSTEM_DMA 0x0801
+#define PCI_CLASS_SYSTEM_TIMER 0x0802
+#define PCI_CLASS_SYSTEM_RTC 0x0803
+#define PCI_CLASS_SYSTEM_OTHER 0x0880
+
+#define PCI_BASE_CLASS_INPUT 0x09
+#define PCI_CLASS_INPUT_KEYBOARD 0x0900
+#define PCI_CLASS_INPUT_PEN 0x0901
+#define PCI_CLASS_INPUT_MOUSE 0x0902
+#define PCI_CLASS_INPUT_OTHER 0x0980
+
+#define PCI_BASE_CLASS_DOCKING 0x0a
+#define PCI_CLASS_DOCKING_GENERIC 0x0a00
+#define PCI_CLASS_DOCKING_OTHER 0x0a01
+
+#define PCI_BASE_CLASS_PROCESSOR 0x0b
+#define PCI_CLASS_PROCESSOR_386 0x0b00
+#define PCI_CLASS_PROCESSOR_486 0x0b01
+#define PCI_CLASS_PROCESSOR_PENTIUM 0x0b02
+#define PCI_CLASS_PROCESSOR_ALPHA 0x0b10
+#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20
+#define PCI_CLASS_PROCESSOR_CO 0x0b40
+
+#define PCI_BASE_CLASS_SERIAL 0x0c
+#define PCI_CLASS_SERIAL_FIREWIRE 0x0c00
+#define PCI_CLASS_SERIAL_ACCESS 0x0c01
+#define PCI_CLASS_SERIAL_SSA 0x0c02
+#define PCI_CLASS_SERIAL_USB 0x0c03
+#define PCI_CLASS_SERIAL_FIBER 0x0c04
+
+#define PCI_CLASS_HOT_SWAP_CONTROLLER 0xff00
+
+#define PCI_CLASS_OTHERS 0xff
+
+/*
+ * Vendor and card ID's: sort these numerically according to vendor
+ * (and according to card ID within vendor). Send all updates to
+ * <linux-pcisupport@cck.uni-kl.de>.
+ */
+#define PCI_VENDOR_ID_COMPAQ 0x0e11
+#define PCI_DEVICE_ID_COMPAQ_TOKENRING 0x0508
+#define PCI_DEVICE_ID_COMPAQ_1280 0x3033
+#define PCI_DEVICE_ID_COMPAQ_TRIFLEX 0x4000
+#define PCI_DEVICE_ID_COMPAQ_SMART2P 0xae10
+#define PCI_DEVICE_ID_COMPAQ_NETEL100 0xae32
+#define PCI_DEVICE_ID_COMPAQ_NETEL10 0xae34
+#define PCI_DEVICE_ID_COMPAQ_NETFLEX3I 0xae35
+#define PCI_DEVICE_ID_COMPAQ_NETEL100D 0xae40
+#define PCI_DEVICE_ID_COMPAQ_NETEL100PI 0xae43
+#define PCI_DEVICE_ID_COMPAQ_NETEL100I 0xb011
+#define PCI_DEVICE_ID_COMPAQ_THUNDER 0xf130
+#define PCI_DEVICE_ID_COMPAQ_NETFLEX3B 0xf150
+
+#define PCI_VENDOR_ID_NCR 0x1000
+#define PCI_DEVICE_ID_NCR_53C810 0x0001
+#define PCI_DEVICE_ID_NCR_53C820 0x0002
+#define PCI_DEVICE_ID_NCR_53C825 0x0003
+#define PCI_DEVICE_ID_NCR_53C815 0x0004
+#define PCI_DEVICE_ID_NCR_53C860 0x0006
+#define PCI_DEVICE_ID_NCR_53C1510D 0x000a
+#define PCI_DEVICE_ID_NCR_53C896 0x000b
+#define PCI_DEVICE_ID_NCR_53C895 0x000c
+#define PCI_DEVICE_ID_NCR_53C885 0x000d
+#define PCI_DEVICE_ID_NCR_53C875 0x000f
+#define PCI_DEVICE_ID_NCR_53C1510 0x0010
+#define PCI_DEVICE_ID_NCR_53C875J 0x008f
+
+#define PCI_VENDOR_ID_ATI 0x1002
+#define PCI_DEVICE_ID_ATI_68800 0x4158
+#define PCI_DEVICE_ID_ATI_215CT222 0x4354
+#define PCI_DEVICE_ID_ATI_210888CX 0x4358
+#define PCI_DEVICE_ID_ATI_215GB 0x4742
+#define PCI_DEVICE_ID_ATI_215GD 0x4744
+#define PCI_DEVICE_ID_ATI_215GI 0x4749
+#define PCI_DEVICE_ID_ATI_215GP 0x4750
+#define PCI_DEVICE_ID_ATI_215GQ 0x4751
+#define PCI_DEVICE_ID_ATI_215GT 0x4754
+#define PCI_DEVICE_ID_ATI_215GTB 0x4755
+#define PCI_DEVICE_ID_ATI_210888GX 0x4758
+#define PCI_DEVICE_ID_ATI_215LG 0x4c47
+#define PCI_DEVICE_ID_ATI_264LT 0x4c54
+#define PCI_DEVICE_ID_ATI_264VT 0x5654
+
+#define PCI_VENDOR_ID_VLSI 0x1004
+#define PCI_DEVICE_ID_VLSI_82C592 0x0005
+#define PCI_DEVICE_ID_VLSI_82C593 0x0006
+#define PCI_DEVICE_ID_VLSI_82C594 0x0007
+#define PCI_DEVICE_ID_VLSI_82C597 0x0009
+#define PCI_DEVICE_ID_VLSI_82C541 0x000c
+#define PCI_DEVICE_ID_VLSI_82C543 0x000d
+#define PCI_DEVICE_ID_VLSI_82C532 0x0101
+#define PCI_DEVICE_ID_VLSI_82C534 0x0102
+#define PCI_DEVICE_ID_VLSI_82C535 0x0104
+#define PCI_DEVICE_ID_VLSI_82C147 0x0105
+#define PCI_DEVICE_ID_VLSI_VAS96011 0x0702
+
+#define PCI_VENDOR_ID_ADL 0x1005
+#define PCI_DEVICE_ID_ADL_2301 0x2301
+
+#define PCI_VENDOR_ID_NS 0x100b
+#define PCI_DEVICE_ID_NS_87415 0x0002
+#define PCI_DEVICE_ID_NS_87410 0xd001
+
+#define PCI_VENDOR_ID_TSENG 0x100c
+#define PCI_DEVICE_ID_TSENG_W32P_2 0x3202
+#define PCI_DEVICE_ID_TSENG_W32P_b 0x3205
+#define PCI_DEVICE_ID_TSENG_W32P_c 0x3206
+#define PCI_DEVICE_ID_TSENG_W32P_d 0x3207
+#define PCI_DEVICE_ID_TSENG_ET6000 0x3208
+
+#define PCI_VENDOR_ID_WEITEK 0x100e
+#define PCI_DEVICE_ID_WEITEK_P9000 0x9001
+#define PCI_DEVICE_ID_WEITEK_P9100 0x9100
+
+#define PCI_VENDOR_ID_DEC 0x1011
+#define PCI_DEVICE_ID_DEC_BRD 0x0001
+#define PCI_DEVICE_ID_DEC_TULIP 0x0002
+#define PCI_DEVICE_ID_DEC_TGA 0x0004
+#define PCI_DEVICE_ID_DEC_TULIP_FAST 0x0009
+#define PCI_DEVICE_ID_DEC_TGA2 0x000D
+#define PCI_DEVICE_ID_DEC_FDDI 0x000F
+#define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014
+#define PCI_DEVICE_ID_DEC_21142 0x0019
+#define PCI_DEVICE_ID_DEC_21052 0x0021
+#define PCI_DEVICE_ID_DEC_21150 0x0022
+#define PCI_DEVICE_ID_DEC_21152 0x0024
+#define PCI_DEVICE_ID_DEC_21153 0x0025
+#define PCI_DEVICE_ID_DEC_21154 0x0026
+#define PCI_DEVICE_ID_DEC_21285 0x1065
+#define PCI_DEVICE_ID_COMPAQ_42XX 0x0046
+
+#define PCI_VENDOR_ID_CIRRUS 0x1013
+#define PCI_DEVICE_ID_CIRRUS_7548 0x0038
+#define PCI_DEVICE_ID_CIRRUS_5430 0x00a0
+#define PCI_DEVICE_ID_CIRRUS_5434_4 0x00a4
+#define PCI_DEVICE_ID_CIRRUS_5434_8 0x00a8
+#define PCI_DEVICE_ID_CIRRUS_5436 0x00ac
+#define PCI_DEVICE_ID_CIRRUS_5446 0x00b8
+#define PCI_DEVICE_ID_CIRRUS_5480 0x00bc
+#define PCI_DEVICE_ID_CIRRUS_5464 0x00d4
+#define PCI_DEVICE_ID_CIRRUS_5465 0x00d6
+#define PCI_DEVICE_ID_CIRRUS_6729 0x1100
+#define PCI_DEVICE_ID_CIRRUS_6832 0x1110
+#define PCI_DEVICE_ID_CIRRUS_7542 0x1200
+#define PCI_DEVICE_ID_CIRRUS_7543 0x1202
+#define PCI_DEVICE_ID_CIRRUS_7541 0x1204
+
+#define PCI_VENDOR_ID_IBM 0x1014
+#define PCI_DEVICE_ID_IBM_FIRE_CORAL 0x000a
+#define PCI_DEVICE_ID_IBM_TR 0x0018
+#define PCI_DEVICE_ID_IBM_82G2675 0x001d
+#define PCI_DEVICE_ID_IBM_MCA 0x0020
+#define PCI_DEVICE_ID_IBM_82351 0x0022
+#define PCI_DEVICE_ID_IBM_PYTHON 0x002d
+#define PCI_DEVICE_ID_IBM_SERVERAID 0x002e
+#define PCI_DEVICE_ID_IBM_TR_WAKE 0x003e
+#define PCI_DEVICE_ID_IBM_MPIC 0x0046
+#define PCI_DEVICE_ID_IBM_3780IDSP 0x007d
+#define PCI_DEVICE_ID_IBM_MPIC_2 0xffff
+
+#define PCI_VENDOR_ID_WD 0x101c
+#define PCI_DEVICE_ID_WD_7197 0x3296
+
+#define PCI_VENDOR_ID_AMD 0x1022
+#define PCI_DEVICE_ID_AMD_LANCE 0x2000
+#define PCI_DEVICE_ID_AMD_LANCE_HOME 0x2001
+#define PCI_DEVICE_ID_AMD_SCSI 0x2020
+
+#define PCI_VENDOR_ID_TRIDENT 0x1023
+#define PCI_DEVICE_ID_TRIDENT_9397 0x9397
+#define PCI_DEVICE_ID_TRIDENT_9420 0x9420
+#define PCI_DEVICE_ID_TRIDENT_9440 0x9440
+#define PCI_DEVICE_ID_TRIDENT_9660 0x9660
+#define PCI_DEVICE_ID_TRIDENT_9750 0x9750
+
+#define PCI_VENDOR_ID_AI 0x1025
+#define PCI_DEVICE_ID_AI_M1435 0x1435
+
+#define PCI_VENDOR_ID_MATROX 0x102B
+#define PCI_DEVICE_ID_MATROX_MGA_2 0x0518
+#define PCI_DEVICE_ID_MATROX_MIL 0x0519
+#define PCI_DEVICE_ID_MATROX_MYS 0x051A
+#define PCI_DEVICE_ID_MATROX_MIL_2 0x051b
+#define PCI_DEVICE_ID_MATROX_MIL_2_AGP 0x051f
+#define PCI_DEVICE_ID_MATROX_G200_PCI 0x0520
+#define PCI_DEVICE_ID_MATROX_G200_AGP 0x0521
+#define PCI_DEVICE_ID_MATROX_MGA_IMP 0x0d10
+#define PCI_DEVICE_ID_MATROX_G100_MM 0x1000
+#define PCI_DEVICE_ID_MATROX_G100_AGP 0x1001
+
+#define PCI_VENDOR_ID_CT 0x102c
+#define PCI_DEVICE_ID_CT_65545 0x00d8
+#define PCI_DEVICE_ID_CT_65548 0x00dc
+#define PCI_DEVICE_ID_CT_65550 0x00e0
+#define PCI_DEVICE_ID_CT_65554 0x00e4
+#define PCI_DEVICE_ID_CT_65555 0x00e5
+
+#define PCI_VENDOR_ID_MIRO 0x1031
+#define PCI_DEVICE_ID_MIRO_36050 0x5601
+
+#define PCI_VENDOR_ID_NEC 0x1033
+#define PCI_DEVICE_ID_NEC_PCX2 0x0046
+
+#define PCI_VENDOR_ID_FD 0x1036
+#define PCI_DEVICE_ID_FD_36C70 0x0000
+
+#define PCI_VENDOR_ID_SI 0x1039
+#define PCI_DEVICE_ID_SI_5591_AGP 0x0001
+#define PCI_DEVICE_ID_SI_6202 0x0002
+#define PCI_DEVICE_ID_SI_503 0x0008
+#define PCI_DEVICE_ID_SI_ACPI 0x0009
+#define PCI_DEVICE_ID_SI_5597_VGA 0x0200
+#define PCI_DEVICE_ID_SI_6205 0x0205
+#define PCI_DEVICE_ID_SI_501 0x0406
+#define PCI_DEVICE_ID_SI_496 0x0496
+#define PCI_DEVICE_ID_SI_601 0x0601
+#define PCI_DEVICE_ID_SI_5107 0x5107
+#define PCI_DEVICE_ID_SI_5511 0x5511
+#define PCI_DEVICE_ID_SI_5513 0x5513
+#define PCI_DEVICE_ID_SI_5571 0x5571
+#define PCI_DEVICE_ID_SI_5591 0x5591
+#define PCI_DEVICE_ID_SI_5597 0x5597
+#define PCI_DEVICE_ID_SI_7001 0x7001
+
+#define PCI_VENDOR_ID_HP 0x103c
+#define PCI_DEVICE_ID_HP_J2585A 0x1030
+#define PCI_DEVICE_ID_HP_J2585B 0x1031
+
+#define PCI_VENDOR_ID_PCTECH 0x1042
+#define PCI_DEVICE_ID_PCTECH_RZ1000 0x1000
+#define PCI_DEVICE_ID_PCTECH_RZ1001 0x1001
+#define PCI_DEVICE_ID_PCTECH_SAMURAI_0 0x3000
+#define PCI_DEVICE_ID_PCTECH_SAMURAI_1 0x3010
+#define PCI_DEVICE_ID_PCTECH_SAMURAI_IDE 0x3020
+
+#define PCI_VENDOR_ID_DPT 0x1044
+#define PCI_DEVICE_ID_DPT 0xa400
+
+#define PCI_VENDOR_ID_OPTI 0x1045
+#define PCI_DEVICE_ID_OPTI_92C178 0xc178
+#define PCI_DEVICE_ID_OPTI_82C557 0xc557
+#define PCI_DEVICE_ID_OPTI_82C558 0xc558
+#define PCI_DEVICE_ID_OPTI_82C621 0xc621
+#define PCI_DEVICE_ID_OPTI_82C700 0xc700
+#define PCI_DEVICE_ID_OPTI_82C701 0xc701
+#define PCI_DEVICE_ID_OPTI_82C814 0xc814
+#define PCI_DEVICE_ID_OPTI_82C822 0xc822
+#define PCI_DEVICE_ID_OPTI_82C861 0xc861
+#define PCI_DEVICE_ID_OPTI_82C825 0xd568
+
+#define PCI_VENDOR_ID_SGS 0x104a
+#define PCI_DEVICE_ID_SGS_2000 0x0008
+#define PCI_DEVICE_ID_SGS_1764 0x0009
+
+#define PCI_VENDOR_ID_BUSLOGIC 0x104B
+#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC 0x0140
+#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER 0x1040
+#define PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT 0x8130
+
+#define PCI_VENDOR_ID_TI 0x104c
+#define PCI_DEVICE_ID_TI_TVP4010 0x3d04
+#define PCI_DEVICE_ID_TI_TVP4020 0x3d07
+#define PCI_DEVICE_ID_TI_PCI1130 0xac12
+#define PCI_DEVICE_ID_TI_PCI1031 0xac13
+#define PCI_DEVICE_ID_TI_PCI1131 0xac15
+#define PCI_DEVICE_ID_TI_PCI1250 0xac16
+#define PCI_DEVICE_ID_TI_PCI1220 0xac17
+
+#define PCI_VENDOR_ID_OAK 0x104e
+#define PCI_DEVICE_ID_OAK_OTI107 0x0107
+
+/* Winbond have two vendor IDs! See 0x10ad as well */
+#define PCI_VENDOR_ID_WINBOND2 0x1050
+#define PCI_DEVICE_ID_WINBOND2_89C940 0x0940
+
+#define PCI_VENDOR_ID_MOTOROLA 0x1057
+#define PCI_VENDOR_ID_MOTOROLA_OOPS 0x1507
+#define PCI_DEVICE_ID_MOTOROLA_MPC105 0x0001
+#define PCI_DEVICE_ID_MOTOROLA_MPC106 0x0002
+#define PCI_DEVICE_ID_MOTOROLA_RAVEN 0x4801
+#define PCI_DEVICE_ID_MOTOROLA_FALCON 0x4802
+#define PCI_DEVICE_ID_MOTOROLA_CPX8216 0x4806
+
+#define PCI_VENDOR_ID_PROMISE 0x105a
+#define PCI_DEVICE_ID_PROMISE_20246 0x4d33
+#define PCI_DEVICE_ID_PROMISE_5300 0x5300
+
+#define PCI_VENDOR_ID_N9 0x105d
+#define PCI_DEVICE_ID_N9_I128 0x2309
+#define PCI_DEVICE_ID_N9_I128_2 0x2339
+#define PCI_DEVICE_ID_N9_I128_T2R 0x493d
+
+#define PCI_VENDOR_ID_UMC 0x1060
+#define PCI_DEVICE_ID_UMC_UM8673F 0x0101
+#define PCI_DEVICE_ID_UMC_UM8891A 0x0891
+#define PCI_DEVICE_ID_UMC_UM8886BF 0x673a
+#define PCI_DEVICE_ID_UMC_UM8886A 0x886a
+#define PCI_DEVICE_ID_UMC_UM8881F 0x8881
+#define PCI_DEVICE_ID_UMC_UM8886F 0x8886
+#define PCI_DEVICE_ID_UMC_UM9017F 0x9017
+#define PCI_DEVICE_ID_UMC_UM8886N 0xe886
+#define PCI_DEVICE_ID_UMC_UM8891N 0xe891
+
+#define PCI_VENDOR_ID_X 0x1061
+#define PCI_DEVICE_ID_X_AGX016 0x0001
+
+#define PCI_VENDOR_ID_PICOP 0x1066
+#define PCI_DEVICE_ID_PICOP_PT86C52X 0x0001
+#define PCI_DEVICE_ID_PICOP_PT80C524 0x8002
+
+#define PCI_VENDOR_ID_MYLEX 0x1069
+#define PCI_DEVICE_ID_MYLEX_DAC960P_V2 0x0001
+#define PCI_DEVICE_ID_MYLEX_DAC960P_V3 0x0002
+#define PCI_DEVICE_ID_MYLEX_DAC960P_V4 0x0010
+#define PCI_DEVICE_ID_MYLEX_DAC960P_V5 0x0020
+
+#define PCI_VENDOR_ID_APPLE 0x106b
+#define PCI_DEVICE_ID_APPLE_BANDIT 0x0001
+#define PCI_DEVICE_ID_APPLE_GC 0x0002
+#define PCI_DEVICE_ID_APPLE_HYDRA 0x000e
+
+#define PCI_VENDOR_ID_NEXGEN 0x1074
+#define PCI_DEVICE_ID_NEXGEN_82C501 0x4e78
+
+#define PCI_VENDOR_ID_QLOGIC 0x1077
+#define PCI_DEVICE_ID_QLOGIC_ISP1020 0x1020
+#define PCI_DEVICE_ID_QLOGIC_ISP1022 0x1022
+#define PCI_DEVICE_ID_QLOGIC_ISP2100 0x2100
+#define PCI_DEVICE_ID_QLOGIC_ISP2200 0x2200
+
+#define PCI_VENDOR_ID_CYRIX 0x1078
+#define PCI_DEVICE_ID_CYRIX_5510 0x0000
+#define PCI_DEVICE_ID_CYRIX_PCI_MASTER 0x0001
+#define PCI_DEVICE_ID_CYRIX_5520 0x0002
+#define PCI_DEVICE_ID_CYRIX_5530_LEGACY 0x0100
+#define PCI_DEVICE_ID_CYRIX_5530_SMI 0x0101
+#define PCI_DEVICE_ID_CYRIX_5530_IDE 0x0102
+#define PCI_DEVICE_ID_CYRIX_5530_AUDIO 0x0103
+#define PCI_DEVICE_ID_CYRIX_5530_VIDEO 0x0104
+
+#define PCI_VENDOR_ID_LEADTEK 0x107d
+#define PCI_DEVICE_ID_LEADTEK_805 0x0000
+
+#define PCI_VENDOR_ID_CONTAQ 0x1080
+#define PCI_DEVICE_ID_CONTAQ_82C599 0x0600
+#define PCI_DEVICE_ID_CONTAQ_82C693 0xc693
+
+#define PCI_VENDOR_ID_FOREX 0x1083
+
+#define PCI_VENDOR_ID_OLICOM 0x108d
+#define PCI_DEVICE_ID_OLICOM_OC3136 0x0001
+#define PCI_DEVICE_ID_OLICOM_OC2315 0x0011
+#define PCI_DEVICE_ID_OLICOM_OC2325 0x0012
+#define PCI_DEVICE_ID_OLICOM_OC2183 0x0013
+#define PCI_DEVICE_ID_OLICOM_OC2326 0x0014
+#define PCI_DEVICE_ID_OLICOM_OC6151 0x0021
+
+#define PCI_VENDOR_ID_SUN 0x108e
+#define PCI_DEVICE_ID_SUN_EBUS 0x1000
+#define PCI_DEVICE_ID_SUN_HAPPYMEAL 0x1001
+#define PCI_DEVICE_ID_SUN_SIMBA 0x5000
+#define PCI_DEVICE_ID_SUN_PBM 0x8000
+#define PCI_DEVICE_ID_SUN_SABRE 0xa000
+
+#define PCI_VENDOR_ID_CMD 0x1095
+#define PCI_DEVICE_ID_CMD_640 0x0640
+#define PCI_DEVICE_ID_CMD_643 0x0643
+#define PCI_DEVICE_ID_CMD_646 0x0646
+#define PCI_DEVICE_ID_CMD_647 0x0647
+#define PCI_DEVICE_ID_CMD_670 0x0670
+
+#define PCI_VENDOR_ID_VISION 0x1098
+#define PCI_DEVICE_ID_VISION_QD8500 0x0001
+#define PCI_DEVICE_ID_VISION_QD8580 0x0002
+
+#define PCI_VENDOR_ID_BROOKTREE 0x109e
+#define PCI_DEVICE_ID_BROOKTREE_848 0x0350
+#define PCI_DEVICE_ID_BROOKTREE_849A 0x0351
+#define PCI_DEVICE_ID_BROOKTREE_878_1 0x036e
+#define PCI_DEVICE_ID_BROOKTREE_878 0x0878
+#define PCI_DEVICE_ID_BROOKTREE_8474 0x8474
+
+#define PCI_VENDOR_ID_SIERRA 0x10a8
+#define PCI_DEVICE_ID_SIERRA_STB 0x0000
+
+#define PCI_VENDOR_ID_ACC 0x10aa
+#define PCI_DEVICE_ID_ACC_2056 0x0000
+
+#define PCI_VENDOR_ID_WINBOND 0x10ad
+#define PCI_DEVICE_ID_WINBOND_83769 0x0001
+#define PCI_DEVICE_ID_WINBOND_82C105 0x0105
+#define PCI_DEVICE_ID_WINBOND_83C553 0x0565
+
+#define PCI_VENDOR_ID_DATABOOK 0x10b3
+#define PCI_DEVICE_ID_DATABOOK_87144 0xb106
+
+#define PCI_VENDOR_ID_PLX 0x10b5
+#define PCI_DEVICE_ID_PLX_9050 0x9050
+#define PCI_DEVICE_ID_PLX_9060 0x9060
+#define PCI_DEVICE_ID_PLX_9060ES 0x906E
+#define PCI_DEVICE_ID_PLX_9060SD 0x906D
+#define PCI_DEVICE_ID_PLX_9080 0x9080
+
+#define PCI_VENDOR_ID_MADGE 0x10b6
+#define PCI_DEVICE_ID_MADGE_MK2 0x0002
+#define PCI_DEVICE_ID_MADGE_C155S 0x1001
+
+#define PCI_VENDOR_ID_3COM 0x10b7
+#define PCI_DEVICE_ID_3COM_3C985 0x0001
+#define PCI_DEVICE_ID_3COM_3C339 0x3390
+#define PCI_DEVICE_ID_3COM_3C590 0x5900
+#define PCI_DEVICE_ID_3COM_3C595TX 0x5950
+#define PCI_DEVICE_ID_3COM_3C595T4 0x5951
+#define PCI_DEVICE_ID_3COM_3C595MII 0x5952
+#define PCI_DEVICE_ID_3COM_3C900TPO 0x9000
+#define PCI_DEVICE_ID_3COM_3C900COMBO 0x9001
+#define PCI_DEVICE_ID_3COM_3C905TX 0x9050
+#define PCI_DEVICE_ID_3COM_3C905T4 0x9051
+#define PCI_DEVICE_ID_3COM_3C905B_TX 0x9055
+
+#define PCI_VENDOR_ID_SMC 0x10b8
+#define PCI_DEVICE_ID_SMC_EPIC100 0x0005
+
+#define PCI_VENDOR_ID_AL 0x10b9
+#define PCI_DEVICE_ID_AL_M1445 0x1445
+#define PCI_DEVICE_ID_AL_M1449 0x1449
+#define PCI_DEVICE_ID_AL_M1451 0x1451
+#define PCI_DEVICE_ID_AL_M1461 0x1461
+#define PCI_DEVICE_ID_AL_M1489 0x1489
+#define PCI_DEVICE_ID_AL_M1511 0x1511
+#define PCI_DEVICE_ID_AL_M1513 0x1513
+#define PCI_DEVICE_ID_AL_M1521 0x1521
+#define PCI_DEVICE_ID_AL_M1523 0x1523
+#define PCI_DEVICE_ID_AL_M1531 0x1531
+#define PCI_DEVICE_ID_AL_M1533 0x1533
+#define PCI_DEVICE_ID_AL_M3307 0x3307
+#define PCI_DEVICE_ID_AL_M4803 0x5215
+#define PCI_DEVICE_ID_AL_M5219 0x5219
+#define PCI_DEVICE_ID_AL_M5229 0x5229
+#define PCI_DEVICE_ID_AL_M5237 0x5237
+#define PCI_DEVICE_ID_AL_M7101 0x7101
+
+#define PCI_VENDOR_ID_MITSUBISHI 0x10ba
+
+#define PCI_VENDOR_ID_SURECOM 0x10bd
+#define PCI_DEVICE_ID_SURECOM_NE34 0x0e34
+
+#define PCI_VENDOR_ID_NEOMAGIC 0x10c8
+#define PCI_DEVICE_ID_NEOMAGIC_MAGICGRAPH_NM2070 0x0001
+#define PCI_DEVICE_ID_NEOMAGIC_MAGICGRAPH_128V 0x0002
+#define PCI_DEVICE_ID_NEOMAGIC_MAGICGRAPH_128ZV 0x0003
+#define PCI_DEVICE_ID_NEOMAGIC_MAGICGRAPH_NM2160 0x0004
+#define PCI_DEVICE_ID_NEOMAGIC_MAGICMEDIA_256AV 0x0005
+#define PCI_DEVICE_ID_NEOMAGIC_MAGICGRAPH_128ZVPLUS 0x0083
+
+#define PCI_VENDOR_ID_ASP 0x10cd
+#define PCI_DEVICE_ID_ASP_ABP940 0x1200
+#define PCI_DEVICE_ID_ASP_ABP940U 0x1300
+#define PCI_DEVICE_ID_ASP_ABP940UW 0x2300
+
+#define PCI_VENDOR_ID_MACRONIX 0x10d9
+#define PCI_DEVICE_ID_MACRONIX_MX98713 0x0512
+#define PCI_DEVICE_ID_MACRONIX_MX987x5 0x0531
+
+#define PCI_VENDOR_ID_CERN 0x10dc
+#define PCI_DEVICE_ID_CERN_SPSB_PMC 0x0001
+#define PCI_DEVICE_ID_CERN_SPSB_PCI 0x0002
+#define PCI_DEVICE_ID_CERN_HIPPI_DST 0x0021
+#define PCI_DEVICE_ID_CERN_HIPPI_SRC 0x0022
+
+#define PCI_VENDOR_ID_NVIDIA 0x10de
+
+#define PCI_VENDOR_ID_IMS 0x10e0
+#define PCI_DEVICE_ID_IMS_8849 0x8849
+
+#define PCI_VENDOR_ID_TEKRAM2 0x10e1
+#define PCI_DEVICE_ID_TEKRAM2_690c 0x690c
+
+#define PCI_VENDOR_ID_TUNDRA 0x10e3
+#define PCI_DEVICE_ID_TUNDRA_CA91C042 0x0000
+
+#define PCI_VENDOR_ID_AMCC 0x10e8
+#define PCI_DEVICE_ID_AMCC_MYRINET 0x8043
+#define PCI_DEVICE_ID_AMCC_PARASTATION 0x8062
+#define PCI_DEVICE_ID_AMCC_S5933 0x807d
+#define PCI_DEVICE_ID_AMCC_S5933_HEPC3 0x809c
+
+#define PCI_VENDOR_ID_INTERG 0x10ea
+#define PCI_DEVICE_ID_INTERG_1680 0x1680
+#define PCI_DEVICE_ID_INTERG_1682 0x1682
+
+#define PCI_VENDOR_ID_REALTEK 0x10ec
+#define PCI_DEVICE_ID_REALTEK_8029 0x8029
+#define PCI_DEVICE_ID_REALTEK_8129 0x8129
+#define PCI_DEVICE_ID_REALTEK_8139 0x8139
+
+#define PCI_VENDOR_ID_TRUEVISION 0x10fa
+#define PCI_DEVICE_ID_TRUEVISION_T1000 0x000c
+
+#define PCI_VENDOR_ID_INIT 0x1101
+#define PCI_DEVICE_ID_INIT_320P 0x9100
+#define PCI_DEVICE_ID_INIT_360P 0x9500
+
+#define PCI_VENDOR_ID_TTI 0x1103
+#define PCI_DEVICE_ID_TTI_HPT343 0x0003
+
+#define PCI_VENDOR_ID_VIA 0x1106
+#define PCI_DEVICE_ID_VIA_82C505 0x0505
+#define PCI_DEVICE_ID_VIA_82C561 0x0561
+#define PCI_DEVICE_ID_VIA_82C586_1 0x0571
+#define PCI_DEVICE_ID_VIA_82C576 0x0576
+#define PCI_DEVICE_ID_VIA_82C585 0x0585
+#define PCI_DEVICE_ID_VIA_82C586_0 0x0586
+#define PCI_DEVICE_ID_VIA_82C595 0x0595
+#define PCI_DEVICE_ID_VIA_82C596_0 0x0596
+#define PCI_DEVICE_ID_VIA_82C597_0 0x0597
+#define PCI_DEVICE_ID_VIA_82C598_0 0x0598
+#define PCI_DEVICE_ID_VIA_82C926 0x0926
+#define PCI_DEVICE_ID_VIA_82C416 0x1571
+#define PCI_DEVICE_ID_VIA_82C595_97 0x1595
+#define PCI_DEVICE_ID_VIA_82C586_2 0x3038
+#define PCI_DEVICE_ID_VIA_82C586_3 0x3040
+#define PCI_DEVICE_ID_VIA_82C686_5 0x3058
+#define PCI_DEVICE_ID_VIA_86C100A 0x6100
+#define PCI_DEVICE_ID_VIA_82C597_1 0x8597
+#define PCI_DEVICE_ID_VIA_82C598_1 0x8598
+
+#define PCI_VENDOR_ID_SMC2 0x1113
+#define PCI_DEVICE_ID_SMC2_1211TX 0x1211
+
+#define PCI_VENDOR_ID_VORTEX 0x1119
+#define PCI_DEVICE_ID_VORTEX_GDT60x0 0x0000
+#define PCI_DEVICE_ID_VORTEX_GDT6000B 0x0001
+#define PCI_DEVICE_ID_VORTEX_GDT6x10 0x0002
+#define PCI_DEVICE_ID_VORTEX_GDT6x20 0x0003
+#define PCI_DEVICE_ID_VORTEX_GDT6530 0x0004
+#define PCI_DEVICE_ID_VORTEX_GDT6550 0x0005
+#define PCI_DEVICE_ID_VORTEX_GDT6x17 0x0006
+#define PCI_DEVICE_ID_VORTEX_GDT6x27 0x0007
+#define PCI_DEVICE_ID_VORTEX_GDT6537 0x0008
+#define PCI_DEVICE_ID_VORTEX_GDT6557 0x0009
+#define PCI_DEVICE_ID_VORTEX_GDT6x15 0x000a
+#define PCI_DEVICE_ID_VORTEX_GDT6x25 0x000b
+#define PCI_DEVICE_ID_VORTEX_GDT6535 0x000c
+#define PCI_DEVICE_ID_VORTEX_GDT6555 0x000d
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP 0x0100
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP 0x0101
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP 0x0102
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP 0x0103
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP 0x0104
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP 0x0105
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP1 0x0110
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP1 0x0111
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP1 0x0112
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP1 0x0113
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP1 0x0114
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP1 0x0115
+#define PCI_DEVICE_ID_VORTEX_GDT6x17RP2 0x0120
+#define PCI_DEVICE_ID_VORTEX_GDT6x27RP2 0x0121
+#define PCI_DEVICE_ID_VORTEX_GDT6537RP2 0x0122
+#define PCI_DEVICE_ID_VORTEX_GDT6557RP2 0x0123
+#define PCI_DEVICE_ID_VORTEX_GDT6x11RP2 0x0124
+#define PCI_DEVICE_ID_VORTEX_GDT6x21RP2 0x0125
+
+#define PCI_VENDOR_ID_EF 0x111a
+#define PCI_DEVICE_ID_EF_ATM_FPGA 0x0000
+#define PCI_DEVICE_ID_EF_ATM_ASIC 0x0002
+
+#define PCI_VENDOR_ID_FORE 0x1127
+#define PCI_DEVICE_ID_FORE_PCA200PC 0x0210
+#define PCI_DEVICE_ID_FORE_PCA200E 0x0300
+
+#define PCI_VENDOR_ID_IMAGINGTECH 0x112f
+#define PCI_DEVICE_ID_IMAGINGTECH_ICPCI 0x0000
+
+#define PCI_VENDOR_ID_PHILIPS 0x1131
+#define PCI_DEVICE_ID_PHILIPS_SAA7145 0x7145
+#define PCI_DEVICE_ID_PHILIPS_SAA7146 0x7146
+
+#define PCI_VENDOR_ID_CYCLONE 0x113c
+#define PCI_DEVICE_ID_CYCLONE_SDK 0x0001
+
+#define PCI_VENDOR_ID_ALLIANCE 0x1142
+#define PCI_DEVICE_ID_ALLIANCE_PROMOTIO 0x3210
+#define PCI_DEVICE_ID_ALLIANCE_PROVIDEO 0x6422
+#define PCI_DEVICE_ID_ALLIANCE_AT24 0x6424
+#define PCI_DEVICE_ID_ALLIANCE_AT3D 0x643d
+
+#define PCI_VENDOR_ID_SYSKONNECT 0x1148
+#define PCI_DEVICE_ID_SYSKONNECT_FP 0x4000
+#define PCI_DEVICE_ID_SYSKONNECT_TR 0x4200
+#define PCI_DEVICE_ID_SYSKONNECT_GE 0x4300
+
+#define PCI_VENDOR_ID_VMIC 0x114a
+#define PCI_DEVICE_ID_VMIC_VME 0x7587
+
+#define PCI_VENDOR_ID_DIGI 0x114f
+#define PCI_DEVICE_ID_DIGI_EPC 0x0002
+#define PCI_DEVICE_ID_DIGI_RIGHTSWITCH 0x0003
+#define PCI_DEVICE_ID_DIGI_XEM 0x0004
+#define PCI_DEVICE_ID_DIGI_XR 0x0005
+#define PCI_DEVICE_ID_DIGI_CX 0x0006
+#define PCI_DEVICE_ID_DIGI_XRJ 0x0009
+#define PCI_DEVICE_ID_DIGI_EPCJ 0x000a
+#define PCI_DEVICE_ID_DIGI_XR_920 0x0027
+
+#define PCI_VENDOR_ID_MUTECH 0x1159
+#define PCI_DEVICE_ID_MUTECH_MV1000 0x0001
+
+#define PCI_VENDOR_ID_RENDITION 0x1163
+#define PCI_DEVICE_ID_RENDITION_VERITE 0x0001
+#define PCI_DEVICE_ID_RENDITION_VERITE2100 0x2000
+
+#define PCI_VENDOR_ID_TOSHIBA 0x1179
+#define PCI_DEVICE_ID_TOSHIBA_601 0x0601
+#define PCI_DEVICE_ID_TOSHIBA_TOPIC95 0x060a
+#define PCI_DEVICE_ID_TOSHIBA_TOPIC97 0x060f
+
+#define PCI_VENDOR_ID_RICOH 0x1180
+#define PCI_DEVICE_ID_RICOH_RL5C465 0x0465
+#define PCI_DEVICE_ID_RICOH_RL5C466 0x0466
+#define PCI_DEVICE_ID_RICOH_RL5C475 0x0475
+#define PCI_DEVICE_ID_RICOH_RL5C478 0x0478
+
+#define PCI_VENDOR_ID_ARTOP 0x1191
+#define PCI_DEVICE_ID_ARTOP_ATP8400 0x0004
+#define PCI_DEVICE_ID_ARTOP_ATP850UF 0x0005
+
+#define PCI_VENDOR_ID_ZEITNET 0x1193
+#define PCI_DEVICE_ID_ZEITNET_1221 0x0001
+#define PCI_DEVICE_ID_ZEITNET_1225 0x0002
+
+#define PCI_VENDOR_ID_OMEGA 0x119b
+#define PCI_DEVICE_ID_OMEGA_82C092G 0x1221
+
+#define PCI_VENDOR_ID_GALILEO 0x11ab
+#define PCI_DEVICE_ID_GALILEO_GT64011 0x4146
+
+#define PCI_VENDOR_ID_LITEON 0x11ad
+#define PCI_DEVICE_ID_LITEON_LNE100TX 0x0002
+
+#define PCI_VENDOR_ID_NP 0x11bc
+#define PCI_DEVICE_ID_NP_PCI_FDDI 0x0001
+
+#define PCI_VENDOR_ID_ATT 0x11c1
+#define PCI_DEVICE_ID_ATT_L56XMF 0x0440
+
+#define PCI_VENDOR_ID_SPECIALIX 0x11cb
+#define PCI_DEVICE_ID_SPECIALIX_IO8 0x2000
+#define PCI_DEVICE_ID_SPECIALIX_XIO 0x4000
+#define PCI_DEVICE_ID_SPECIALIX_RIO 0x8000
+
+#define PCI_VENDOR_ID_AURAVISION 0x11d1
+#define PCI_DEVICE_ID_AURAVISION_VXP524 0x01f7
+
+#define PCI_VENDOR_ID_IKON 0x11d5
+#define PCI_DEVICE_ID_IKON_10115 0x0115
+#define PCI_DEVICE_ID_IKON_10117 0x0117
+
+#define PCI_VENDOR_ID_ZORAN 0x11de
+#define PCI_DEVICE_ID_ZORAN_36057 0x6057
+#define PCI_DEVICE_ID_ZORAN_36120 0x6120
+
+#define PCI_VENDOR_ID_KINETIC 0x11f4
+#define PCI_DEVICE_ID_KINETIC_2915 0x2915
+
+#define PCI_VENDOR_ID_COMPEX 0x11f6
+#define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112
+#define PCI_DEVICE_ID_COMPEX_RL2000 0x1401
+
+#define PCI_VENDOR_ID_RP 0x11fe
+#define PCI_DEVICE_ID_RP32INTF 0x0001
+#define PCI_DEVICE_ID_RP8INTF 0x0002
+#define PCI_DEVICE_ID_RP16INTF 0x0003
+#define PCI_DEVICE_ID_RP4QUAD 0x0004
+#define PCI_DEVICE_ID_RP8OCTA 0x0005
+#define PCI_DEVICE_ID_RP8J 0x0006
+#define PCI_DEVICE_ID_RPP4 0x000A
+#define PCI_DEVICE_ID_RPP8 0x000B
+#define PCI_DEVICE_ID_RP8M 0x000C
+
+#define PCI_VENDOR_ID_CYCLADES 0x120e
+#define PCI_DEVICE_ID_CYCLOM_Y_Lo 0x0100
+#define PCI_DEVICE_ID_CYCLOM_Y_Hi 0x0101
+#define PCI_DEVICE_ID_CYCLOM_4Y_Lo 0x0102
+#define PCI_DEVICE_ID_CYCLOM_4Y_Hi 0x0103
+#define PCI_DEVICE_ID_CYCLOM_8Y_Lo 0x0104
+#define PCI_DEVICE_ID_CYCLOM_8Y_Hi 0x0105
+#define PCI_DEVICE_ID_CYCLOM_Z_Lo 0x0200
+#define PCI_DEVICE_ID_CYCLOM_Z_Hi 0x0201
+
+#define PCI_VENDOR_ID_ESSENTIAL 0x120f
+#define PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER 0x0001
+
+#define PCI_VENDOR_ID_O2 0x1217
+#define PCI_DEVICE_ID_O2_6729 0x6729
+#define PCI_DEVICE_ID_O2_6730 0x673a
+#define PCI_DEVICE_ID_O2_6832 0x6832
+#define PCI_DEVICE_ID_O2_6836 0x6836
+
+#define PCI_VENDOR_ID_3DFX 0x121a
+#define PCI_DEVICE_ID_3DFX_VOODOO 0x0001
+#define PCI_DEVICE_ID_3DFX_VOODOO2 0x0002
+#define PCI_DEVICE_ID_3DFX_BANSHEE 0x0003
+
+#define PCI_VENDOR_ID_SIGMADES 0x1236
+#define PCI_DEVICE_ID_SIGMADES_6425 0x6401
+
+#define PCI_VENDOR_ID_CCUBE 0x123f
+
+#define PCI_VENDOR_ID_AVM 0x1244
+#define PCI_DEVICE_ID_AVM_A1 0x0a00
+
+#define PCI_VENDOR_ID_DIPIX 0x1246
+
+#define PCI_VENDOR_ID_STALLION 0x124d
+#define PCI_DEVICE_ID_STALLION_ECHPCI832 0x0000
+#define PCI_DEVICE_ID_STALLION_ECHPCI864 0x0002
+#define PCI_DEVICE_ID_STALLION_EIOPCI 0x0003
+
+#define PCI_VENDOR_ID_OPTIBASE 0x1255
+#define PCI_DEVICE_ID_OPTIBASE_FORGE 0x1110
+#define PCI_DEVICE_ID_OPTIBASE_FUSION 0x1210
+#define PCI_DEVICE_ID_OPTIBASE_VPLEX 0x2110
+#define PCI_DEVICE_ID_OPTIBASE_VPLEXCC 0x2120
+#define PCI_DEVICE_ID_OPTIBASE_VQUEST 0x2130
+
+#define PCI_VENDOR_ID_SATSAGEM 0x1267
+#define PCI_DEVICE_ID_SATSAGEM_PCR2101 0x5352
+#define PCI_DEVICE_ID_SATSAGEM_TELSATTURBO 0x5a4b
+
+#define PCI_VENDOR_ID_HUGHES 0x1273
+#define PCI_DEVICE_ID_HUGHES_DIRECPC 0x0002
+
+#define PCI_VENDOR_ID_ENSONIQ 0x1274
+#define PCI_DEVICE_ID_ENSONIQ_AUDIOPCI 0x5000
+#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371
+
+#define PCI_VENDOR_ID_ALTEON 0x12ae
+#define PCI_DEVICE_ID_ALTEON_ACENIC 0x0001
+
+#define PCI_VENDOR_ID_PICTUREL 0x12c5
+#define PCI_DEVICE_ID_PICTUREL_PCIVST 0x0081
+
+#define PCI_VENDOR_ID_NVIDIA_SGS 0x12d2
+#define PCI_DEVICE_ID_NVIDIA_SGS_RIVA128 0x0018
+
+#define PCI_VENDOR_ID_CBOARDS 0x1307
+#define PCI_DEVICE_ID_CBOARDS_DAS1602_16 0x0001
+
+#define PCI_VENDOR_ID_SIIG 0x131f
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_550 0x1010
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_650 0x1011
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_850 0x1012
+#define PCI_DEVICE_ID_SIIG_1P_10x 0x1020
+#define PCI_DEVICE_ID_SIIG_2P_10x 0x1021
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_550 0x1034
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_650 0x1035
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_850 0x1036
+#define PCI_DEVICE_ID_SIIG_1P_20x 0x2020
+#define PCI_DEVICE_ID_SIIG_2P_20x 0x2021
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_550 0x2040
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_650 0x2041
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_850 0x2042
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_550 0x2010
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_650 0x2011
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_850 0x2012
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_550 0x2060
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_650 0x2061
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_850 0x2062
+
+#define PCI_VENDOR_ID_NETGEAR 0x1385
+#define PCI_DEVICE_ID_NETGEAR_GA620 0x620a
+
+#define PCI_VENDOR_ID_LAVA 0x1407
+#define PCI_DEVICE_ID_LAVA_PARALLEL 0x8000
+#define PCI_DEVICE_ID_LAVA_DUAL_PAR_A 0x8002 /* The Lava Dual Parallel is */
+#define PCI_DEVICE_ID_LAVA_DUAL_PAR_B 0x8003 /* two PCI devices on a card */
+
+#define PCI_VENDOR_ID_SYMPHONY 0x1c1c
+#define PCI_DEVICE_ID_SYMPHONY_101 0x0001
+
+#define PCI_VENDOR_ID_TEKRAM 0x1de1
+#define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29
+
+#define PCI_VENDOR_ID_3DLABS 0x3d3d
+#define PCI_DEVICE_ID_3DLABS_300SX 0x0001
+#define PCI_DEVICE_ID_3DLABS_500TX 0x0002
+#define PCI_DEVICE_ID_3DLABS_DELTA 0x0003
+#define PCI_DEVICE_ID_3DLABS_PERMEDIA 0x0004
+#define PCI_DEVICE_ID_3DLABS_MX 0x0006
+#define PCI_DEVICE_ID_3DLABS_PERMEDIA2 0x0007
+#define PCI_DEVICE_ID_3DLABS_GAMMA 0x0008
+#define PCI_DEVICE_ID_3DLABS_PERMEDIA2V 0x0009
+
+#define PCI_VENDOR_ID_AVANCE 0x4005
+#define PCI_DEVICE_ID_AVANCE_ALG2064 0x2064
+#define PCI_DEVICE_ID_AVANCE_2302 0x2302
+
+#define PCI_VENDOR_ID_NETVIN 0x4a14
+#define PCI_DEVICE_ID_NETVIN_NV5000SC 0x5000
+
+#define PCI_VENDOR_ID_S3 0x5333
+#define PCI_DEVICE_ID_S3_PLATO_PXS 0x0551
+#define PCI_DEVICE_ID_S3_ViRGE 0x5631
+#define PCI_DEVICE_ID_S3_TRIO 0x8811
+#define PCI_DEVICE_ID_S3_AURORA64VP 0x8812
+#define PCI_DEVICE_ID_S3_TRIO64UVP 0x8814
+#define PCI_DEVICE_ID_S3_ViRGE_VX 0x883d
+#define PCI_DEVICE_ID_S3_868 0x8880
+#define PCI_DEVICE_ID_S3_928 0x88b0
+#define PCI_DEVICE_ID_S3_864_1 0x88c0
+#define PCI_DEVICE_ID_S3_864_2 0x88c1
+#define PCI_DEVICE_ID_S3_964_1 0x88d0
+#define PCI_DEVICE_ID_S3_964_2 0x88d1
+#define PCI_DEVICE_ID_S3_968 0x88f0
+#define PCI_DEVICE_ID_S3_TRIO64V2 0x8901
+#define PCI_DEVICE_ID_S3_PLATO_PXG 0x8902
+#define PCI_DEVICE_ID_S3_ViRGE_DXGX 0x8a01
+#define PCI_DEVICE_ID_S3_ViRGE_GX2 0x8a10
+#define PCI_DEVICE_ID_S3_ViRGE_MX 0x8c01
+#define PCI_DEVICE_ID_S3_ViRGE_MXP 0x8c02
+#define PCI_DEVICE_ID_S3_ViRGE_MXPMV 0x8c03
+#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00
+
+#define PCI_VENDOR_ID_DCI 0x6666
+#define PCI_DEVICE_ID_DCI_PCCOM4 0x0001
+
+#define PCI_VENDOR_ID_GENROCO 0x5555
+#define PCI_DEVICE_ID_GENROCO_HFP832 0x0003
+
+#define PCI_VENDOR_ID_INTEL 0x8086
+#define PCI_DEVICE_ID_INTEL_21145 0x0039
+#define PCI_DEVICE_ID_INTEL_82375 0x0482
+#define PCI_DEVICE_ID_INTEL_82424 0x0483
+#define PCI_DEVICE_ID_INTEL_82378 0x0484
+#define PCI_DEVICE_ID_INTEL_82430 0x0486
+#define PCI_DEVICE_ID_INTEL_82434 0x04a3
+#define PCI_DEVICE_ID_INTEL_I960 0x0960
+#define PCI_DEVICE_ID_INTEL_82092AA_0 0x1221
+#define PCI_DEVICE_ID_INTEL_82092AA_1 0x1222
+#define PCI_DEVICE_ID_INTEL_7116 0x1223
+#define PCI_DEVICE_ID_INTEL_82596 0x1226
+#define PCI_DEVICE_ID_INTEL_82865 0x1227
+#define PCI_DEVICE_ID_INTEL_82557 0x1229
+#define PCI_DEVICE_ID_INTEL_82437 0x122d
+#define PCI_DEVICE_ID_INTEL_82371FB_0 0x122e
+#define PCI_DEVICE_ID_INTEL_82371FB_1 0x1230
+#define PCI_DEVICE_ID_INTEL_82371MX 0x1234
+#define PCI_DEVICE_ID_INTEL_82437MX 0x1235
+#define PCI_DEVICE_ID_INTEL_82441 0x1237
+#define PCI_DEVICE_ID_INTEL_82380FB 0x124b
+#define PCI_DEVICE_ID_INTEL_82439 0x1250
+#define PCI_DEVICE_ID_INTEL_MEGARAID 0x1960
+#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000
+#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010
+#define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020
+#define PCI_DEVICE_ID_INTEL_82437VX 0x7030
+#define PCI_DEVICE_ID_INTEL_82439TX 0x7100
+#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110
+#define PCI_DEVICE_ID_INTEL_82371AB 0x7111
+#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112
+#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113
+#define PCI_DEVICE_ID_INTEL_82443LX_0 0x7180
+#define PCI_DEVICE_ID_INTEL_82443LX_1 0x7181
+#define PCI_DEVICE_ID_INTEL_82443BX_0 0x7190
+#define PCI_DEVICE_ID_INTEL_82443BX_1 0x7191
+#define PCI_DEVICE_ID_INTEL_82443BX_2 0x7192
+#define PCI_DEVICE_ID_INTEL_P6 0x84c4
+#define PCI_DEVICE_ID_INTEL_82450GX 0x84c4
+#define PCI_DEVICE_ID_INTEL_82453GX 0x84c5
+#define PCI_DEVICE_ID_INTEL_82451NX 0x84ca
+#define PCI_DEVICE_ID_INTEL_82454NX 0x84cb
+
+#define PCI_VENDOR_ID_COMPUTONE 0x8e0e
+#define PCI_DEVICE_ID_COMPUTONE_IP2EX 0x0291
+
+#define PCI_VENDOR_ID_KTI 0x8e2e
+#define PCI_DEVICE_ID_KTI_ET32P2 0x3000
+
+#define PCI_VENDOR_ID_ADAPTEC 0x9004
+#define PCI_DEVICE_ID_ADAPTEC_7810 0x1078
+#define PCI_DEVICE_ID_ADAPTEC_7821 0x2178
+#define PCI_DEVICE_ID_ADAPTEC_38602 0x3860
+#define PCI_DEVICE_ID_ADAPTEC_7850 0x5078
+#define PCI_DEVICE_ID_ADAPTEC_7855 0x5578
+#define PCI_DEVICE_ID_ADAPTEC_5800 0x5800
+#define PCI_DEVICE_ID_ADAPTEC_3860 0x6038
+#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075
+#define PCI_DEVICE_ID_ADAPTEC_7860 0x6078
+#define PCI_DEVICE_ID_ADAPTEC_7861 0x6178
+#define PCI_DEVICE_ID_ADAPTEC_7870 0x7078
+#define PCI_DEVICE_ID_ADAPTEC_7871 0x7178
+#define PCI_DEVICE_ID_ADAPTEC_7872 0x7278
+#define PCI_DEVICE_ID_ADAPTEC_7873 0x7378
+#define PCI_DEVICE_ID_ADAPTEC_7874 0x7478
+#define PCI_DEVICE_ID_ADAPTEC_7895 0x7895
+#define PCI_DEVICE_ID_ADAPTEC_7880 0x8078
+#define PCI_DEVICE_ID_ADAPTEC_7881 0x8178
+#define PCI_DEVICE_ID_ADAPTEC_7882 0x8278
+#define PCI_DEVICE_ID_ADAPTEC_7883 0x8378
+#define PCI_DEVICE_ID_ADAPTEC_7884 0x8478
+#define PCI_DEVICE_ID_ADAPTEC_7885 0x8578
+#define PCI_DEVICE_ID_ADAPTEC_7886 0x8678
+#define PCI_DEVICE_ID_ADAPTEC_7887 0x8778
+#define PCI_DEVICE_ID_ADAPTEC_7888 0x8878
+#define PCI_DEVICE_ID_ADAPTEC_1030 0x8b78
+
+#define PCI_VENDOR_ID_ADAPTEC2 0x9005
+#define PCI_DEVICE_ID_ADAPTEC2_2940U2 0x0010
+#define PCI_DEVICE_ID_ADAPTEC2_2930U2 0x0011
+#define PCI_DEVICE_ID_ADAPTEC2_7890B 0x0013
+#define PCI_DEVICE_ID_ADAPTEC2_7890 0x001f
+#define PCI_DEVICE_ID_ADAPTEC2_3940U2 0x0050
+#define PCI_DEVICE_ID_ADAPTEC2_3950U2D 0x0051
+#define PCI_DEVICE_ID_ADAPTEC2_7896 0x005f
+#define PCI_DEVICE_ID_ADAPTEC2_7892A 0x0080
+#define PCI_DEVICE_ID_ADAPTEC2_7892B 0x0081
+#define PCI_DEVICE_ID_ADAPTEC2_7892D 0x0083
+#define PCI_DEVICE_ID_ADAPTEC2_7892P 0x008f
+#define PCI_DEVICE_ID_ADAPTEC2_7899A 0x00c0
+#define PCI_DEVICE_ID_ADAPTEC2_7899B 0x00c1
+#define PCI_DEVICE_ID_ADAPTEC2_7899D 0x00c3
+#define PCI_DEVICE_ID_ADAPTEC2_7899P 0x00cf
+
+#define PCI_VENDOR_ID_ATRONICS 0x907f
+#define PCI_DEVICE_ID_ATRONICS_2015 0x2015
+
+#define PCI_VENDOR_ID_HOLTEK 0x9412
+#define PCI_DEVICE_ID_HOLTEK_6565 0x6565
+
+#define PCI_VENDOR_ID_TIGERJET 0xe159
+#define PCI_DEVICE_ID_TIGERJET_300 0x0001
+
+#define PCI_VENDOR_ID_ARK 0xedd8
+#define PCI_DEVICE_ID_ARK_STING 0xa091
+#define PCI_DEVICE_ID_ARK_STINGARK 0xa099
+#define PCI_DEVICE_ID_ARK_2000MT 0xa0a1
+
+#define PCI_VENDOR_ID_INTERPHASE 0x107e
+#define PCI_DEVICE_ID_INTERPHASE_5526 0x0004
+#define PCI_DEVICE_ID_INTERPHASE_55x6 0x0005
+
+/*
+ * The PCI interface treats multi-function devices as independent
+ * devices. The slot/function address of each device is encoded
+ * in a single byte as follows:
+ *
+ * 7:3 = slot
+ * 2:0 = function
+ */
+#define PCI_DEVFN(slot,func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
+#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
+#define PCI_FUNC(devfn) ((devfn) & 0x07)
+
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include <linux/config.h>
+
+/*
+ * There is one pci_dev structure for each slot-number/function-number
+ * combination:
+ */
+struct pci_dev {
+ struct pci_bus *bus; /* bus this device is on */
+ struct pci_dev *sibling; /* next device on this bus */
+ struct pci_dev *next; /* chain of all devices */
+
+ void *sysdata; /* hook for sys-specific extension */
+ struct proc_dir_entry *procent; /* device entry in /proc/bus/pci */
+
+ unsigned int devfn; /* encoded device & function index */
+ unsigned short vendor;
+ unsigned short device;
+ unsigned int class; /* 3 bytes: (base,sub,prog-if) */
+ unsigned int hdr_type; /* PCI header type */
+ unsigned int master : 1; /* set if device is master capable */
+ /*
+ * In theory, the irq level can be read from configuration
+ * space and all would be fine. However, old PCI chips don't
+ * support these registers and return 0 instead. For example,
+ * the Vision864-P rev 0 chip can uses INTA, but returns 0 in
+ * the interrupt line and pin registers. pci_init()
+ * initializes this field with the value at PCI_INTERRUPT_LINE
+ * and it is the job of pcibios_fixup() to change it if
+ * necessary. The field must not be 0 unless the device
+ * cannot generate interrupts at all.
+ */
+ unsigned int irq; /* irq generated by this device */
+
+ /* Base registers for this device, can be adjusted by
+ * pcibios_fixup() as necessary.
+ */
+ unsigned long base_address[6];
+ unsigned long rom_address;
+};
+
+struct pci_bus {
+ struct pci_bus *parent; /* parent bus this bridge is on */
+ struct pci_bus *children; /* chain of P2P bridges on this bus */
+ struct pci_bus *next; /* chain of all PCI buses */
+
+ struct pci_dev *self; /* bridge device as seen by parent */
+ struct pci_dev *devices; /* devices behind this bridge */
+
+ void *sysdata; /* hook for sys-specific extension */
+ struct proc_dir_entry *procdir; /* directory entry in /proc/bus/pci */
+
+ unsigned char number; /* bus number */
+ unsigned char primary; /* number of primary bridge */
+ unsigned char secondary; /* number of secondary bridge */
+ unsigned char subordinate; /* max number of subordinate buses */
+};
+
+extern struct pci_bus pci_root; /* root bus */
+extern struct pci_dev *pci_devices; /* list of all devices */
+
+/*
+ * Error values that may be returned by the PCI bios.
+ */
+#define PCIBIOS_SUCCESSFUL 0x00
+#define PCIBIOS_FUNC_NOT_SUPPORTED 0x81
+#define PCIBIOS_BAD_VENDOR_ID 0x83
+#define PCIBIOS_DEVICE_NOT_FOUND 0x86
+#define PCIBIOS_BAD_REGISTER_NUMBER 0x87
+#define PCIBIOS_SET_FAILED 0x88
+#define PCIBIOS_BUFFER_TOO_SMALL 0x89
+
+/* Low-level architecture-dependent routines */
+
+int pcibios_present (void);
+void pcibios_init(void);
+void pcibios_fixup(void);
+void pcibios_fixup_bus(struct pci_bus *);
+char *pcibios_setup (char *str);
+int pcibios_read_config_byte (unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned char *val);
+int pcibios_read_config_word (unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned short *val);
+int pcibios_read_config_dword (unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned int *val);
+int pcibios_write_config_byte (unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned char val);
+int pcibios_write_config_word (unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned short val);
+int pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned int val);
+
+/* Don't use these in new code, use pci_find_... instead */
+
+int pcibios_find_class (unsigned int class_code, unsigned short index, unsigned char *bus, unsigned char *dev_fn);
+int pcibios_find_device (unsigned short vendor, unsigned short dev_id,
+ unsigned short index, unsigned char *bus,
+ unsigned char *dev_fn);
+
+/* Generic PCI interface functions */
+
+void pci_init(void);
+void pci_setup(char *str, int *ints);
+void pci_quirks_init(void);
+unsigned int pci_scan_bus(struct pci_bus *bus);
+struct pci_bus *pci_scan_peer_bridge(int bus);
+void pci_proc_init(void);
+void proc_old_pci_init(void);
+int get_pci_list(char *buf);
+int pci_proc_attach_device(struct pci_dev *dev);
+int pci_proc_detach_device(struct pci_dev *dev);
+
+struct pci_dev *pci_find_device (unsigned int vendor, unsigned int device, struct pci_dev *from);
+struct pci_dev *pci_find_class (unsigned int class, struct pci_dev *from);
+struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn);
+
+#define pci_present pcibios_present
+int pci_read_config_byte(struct pci_dev *dev, u8 where, u8 *val);
+int pci_read_config_word(struct pci_dev *dev, u8 where, u16 *val);
+int pci_read_config_dword(struct pci_dev *dev, u8 where, u32 *val);
+int pci_write_config_byte(struct pci_dev *dev, u8 where, u8 val);
+int pci_write_config_word(struct pci_dev *dev, u8 where, u16 val);
+int pci_write_config_dword(struct pci_dev *dev, u8 where, u32 val);
+void pci_set_master(struct pci_dev *dev);
+
+#ifndef CONFIG_PCI
+/* If the system does not have PCI, clearly these return errors. Define
+ these as simple inline functions to avoid hair in drivers. */
+extern inline int pcibios_present(void) { return 0; }
+
+#define _PCI_NOP(o,s,t) \
+ extern inline int pcibios_##o##_config_##s## (u8 bus, u8 dfn, u8 where, t val) \
+ { return PCIBIOS_FUNC_NOT_SUPPORTED; } \
+ extern inline int pci_##o##_config_##s## (struct pci_dev *dev, u8 where, t val) \
+ { return PCIBIOS_FUNC_NOT_SUPPORTED; }
+#define _PCI_NOP_ALL(o,x) _PCI_NOP(o,byte,u8 x) \
+ _PCI_NOP(o,word,u16 x) \
+ _PCI_NOP(o,dword,u32 x)
+_PCI_NOP_ALL(read, *)
+_PCI_NOP_ALL(write,)
+
+extern inline struct pci_dev *pci_find_device(unsigned int vendor, unsigned int device, struct pci_dev *from)
+{ return NULL; }
+
+extern inline struct pci_dev *pci_find_class(unsigned int class, struct pci_dev *from)
+{ return NULL; }
+
+extern inline struct pci_dev *pci_find_slot(unsigned int bus, unsigned int devfn)
+{ return NULL; }
+
+extern inline void pci_set_master(struct pci_dev *dev)
+{ return; }
+
+#endif /* !CONFIG_PCI */
+
+#endif /* __KERNEL__ */
+#endif /* LINUX_PCI_H */
diff --git a/pfinet/linux-src/include/linux/personality.h b/pfinet/linux-src/include/linux/personality.h
new file mode 100644
index 00000000..a927b9e7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/personality.h
@@ -0,0 +1,57 @@
+#ifndef _PERSONALITY_H
+#define _PERSONALITY_H
+
+#include <linux/linkage.h>
+#include <linux/ptrace.h>
+
+
+/* Flags for bug emulation. These occupy the top three bytes. */
+#define STICKY_TIMEOUTS 0x4000000
+#define WHOLE_SECONDS 0x2000000
+#define ADDR_LIMIT_32BIT 0x0800000
+
+/* Personality types. These go in the low byte. Avoid using the top bit,
+ * it will conflict with error returns.
+ */
+#define PER_MASK (0x00ff)
+#define PER_LINUX (0x0000)
+#define PER_LINUX_32BIT (0x0000 | ADDR_LIMIT_32BIT)
+#define PER_SVR4 (0x0001 | STICKY_TIMEOUTS)
+#define PER_SVR3 (0x0002 | STICKY_TIMEOUTS)
+#define PER_SCOSVR3 (0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS)
+#define PER_WYSEV386 (0x0004 | STICKY_TIMEOUTS)
+#define PER_ISCR4 (0x0005 | STICKY_TIMEOUTS)
+#define PER_BSD (0x0006)
+#define PER_XENIX (0x0007 | STICKY_TIMEOUTS)
+#define PER_LINUX32 (0x0008)
+#define PER_IRIX32 (0x0009 | STICKY_TIMEOUTS) /* IRIX5 32-bit */
+#define PER_IRIXN32 (0x000a | STICKY_TIMEOUTS) /* IRIX6 new 32-bit */
+#define PER_IRIX64 (0x000b | STICKY_TIMEOUTS) /* IRIX6 64-bit */
+
+/* Prototype for an lcall7 syscall handler. */
+typedef void (*lcall7_func)(struct pt_regs *);
+
+
+/* Description of an execution domain - personality range supported,
+ * lcall7 syscall handler, start up / shut down functions etc.
+ * N.B. The name and lcall7 handler must be where they are since the
+ * offset of the handler is hard coded in kernel/sys_call.S.
+ */
+struct exec_domain {
+ const char *name;
+ lcall7_func handler;
+ unsigned char pers_low, pers_high;
+ unsigned long * signal_map;
+ unsigned long * signal_invmap;
+ struct module * module;
+ struct exec_domain *next;
+};
+
+extern struct exec_domain default_exec_domain;
+
+extern struct exec_domain *lookup_exec_domain(unsigned long personality);
+extern int register_exec_domain(struct exec_domain *it);
+extern int unregister_exec_domain(struct exec_domain *it);
+asmlinkage int sys_personality(unsigned long personality);
+
+#endif /* _PERSONALITY_H */
diff --git a/pfinet/linux-src/include/linux/pg.h b/pfinet/linux-src/include/linux/pg.h
new file mode 100644
index 00000000..c752a97c
--- /dev/null
+++ b/pfinet/linux-src/include/linux/pg.h
@@ -0,0 +1,63 @@
+/* pg.h (c) 1998 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license
+
+
+ pg.h defines the user interface to the generic ATAPI packet
+ command driver for parallel port ATAPI devices (pg). The
+ driver is loosely modelled after the generic SCSI driver, sg,
+ although the actual interface is different.
+
+ The pg driver provides a simple character device interface for
+ sending ATAPI commands to a device. With the exception of the
+ ATAPI reset operation, all operations are performed by a pair
+ of read and write operations to the appropriate /dev/pgN device.
+ A write operation delivers a command and any outbound data in
+ a single buffer. Normally, the write will succeed unless the
+ device is offline or malfunctioning, or there is already another
+ command pending. If the write succeeds, it should be followed
+ immediately by a read operation, to obtain any returned data and
+ status information. A read will fail if there is no operation
+ in progress.
+
+ As a special case, the device can be reset with a write operation,
+ and in this case, no following read is expected, or permitted.
+
+ There are no ioctl() operations. Any single operation
+ may transfer at most PG_MAX_DATA bytes. Note that the driver must
+ copy the data through an internal buffer. In keeping with all
+ current ATAPI devices, command packets are assumed to be exactly
+ 12 bytes in length.
+
+ To permit future changes to this interface, the headers in the
+ read and write buffers contain a single character "magic" flag.
+ Currently this flag must be the character "P".
+
+*/
+
+#define PG_MAGIC 'P'
+#define PG_RESET 'Z'
+#define PG_COMMAND 'C'
+
+#define PG_MAX_DATA 32768
+
+struct pg_write_hdr {
+
+ char magic; /* == PG_MAGIC */
+ char func; /* PG_RESET or PG_COMMAND */
+ int dlen; /* number of bytes expected to transfer */
+ int timeout; /* number of seconds before timeout */
+ char packet[12]; /* packet command */
+
+};
+
+struct pg_read_hdr {
+
+ char magic; /* == PG_MAGIC */
+ char scsi; /* "scsi" status == sense key */
+ int dlen; /* size of device transfer request */
+ int duration; /* time in seconds command took */
+ char pad[12]; /* not used */
+
+};
+
+/* end of pg.h */
diff --git a/pfinet/linux-src/include/linux/phonedev.h b/pfinet/linux-src/include/linux/phonedev.h
new file mode 100644
index 00000000..d54049ee
--- /dev/null
+++ b/pfinet/linux-src/include/linux/phonedev.h
@@ -0,0 +1,26 @@
+#ifndef __LINUX_PHONEDEV_H
+#define __LINUX_PHONEDEV_H
+
+#include <linux/types.h>
+#include <linux/version.h>
+
+#ifdef __KERNEL__
+
+#include <linux/poll.h>
+
+struct phone_device {
+ struct phone_device *next;
+ struct file_operations *f_op;
+ int (*open) (struct phone_device *, struct file *);
+ int board; /* Device private index */
+ int minor;
+};
+
+extern int phonedev_init(void);
+#define PHONE_MAJOR 100
+extern int phone_register_device(struct phone_device *, int unit);
+#define PHONE_UNIT_ANY -1
+extern void phone_unregister_device(struct phone_device *);
+
+#endif
+#endif
diff --git a/pfinet/linux-src/include/linux/pipe_fs_i.h b/pfinet/linux-src/include/linux/pipe_fs_i.h
new file mode 100644
index 00000000..3a847d72
--- /dev/null
+++ b/pfinet/linux-src/include/linux/pipe_fs_i.h
@@ -0,0 +1,34 @@
+#ifndef _LINUX_PIPE_FS_I_H
+#define _LINUX_PIPE_FS_I_H
+
+struct pipe_inode_info {
+ struct wait_queue * wait;
+ char * base;
+ unsigned int start;
+ unsigned int lock;
+ unsigned int rd_openers;
+ unsigned int wr_openers;
+ unsigned int readers;
+ unsigned int writers;
+};
+
+#define PIPE_WAIT(inode) ((inode).u.pipe_i.wait)
+#define PIPE_BASE(inode) ((inode).u.pipe_i.base)
+#define PIPE_START(inode) ((inode).u.pipe_i.start)
+#define PIPE_LEN(inode) ((inode).i_size)
+#define PIPE_RD_OPENERS(inode) ((inode).u.pipe_i.rd_openers)
+#define PIPE_WR_OPENERS(inode) ((inode).u.pipe_i.wr_openers)
+#define PIPE_READERS(inode) ((inode).u.pipe_i.readers)
+#define PIPE_WRITERS(inode) ((inode).u.pipe_i.writers)
+#define PIPE_LOCK(inode) ((inode).u.pipe_i.lock)
+#define PIPE_SIZE(inode) PIPE_LEN(inode)
+
+#define PIPE_EMPTY(inode) (PIPE_SIZE(inode)==0)
+#define PIPE_FULL(inode) (PIPE_SIZE(inode)==PIPE_BUF)
+#define PIPE_FREE(inode) (PIPE_BUF - PIPE_LEN(inode))
+#define PIPE_END(inode) ((PIPE_START(inode)+PIPE_LEN(inode))&\
+ (PIPE_BUF-1))
+#define PIPE_MAX_RCHUNK(inode) (PIPE_BUF - PIPE_START(inode))
+#define PIPE_MAX_WCHUNK(inode) (PIPE_BUF - PIPE_END(inode))
+
+#endif
diff --git a/pfinet/linux-src/include/linux/pkt_cls.h b/pfinet/linux-src/include/linux/pkt_cls.h
new file mode 100644
index 00000000..36935ed3
--- /dev/null
+++ b/pfinet/linux-src/include/linux/pkt_cls.h
@@ -0,0 +1,146 @@
+#ifndef __LINUX_PKT_CLS_H
+#define __LINUX_PKT_CLS_H
+
+struct tc_police
+{
+ __u32 index;
+ int action;
+#define TC_POLICE_UNSPEC (-1)
+#define TC_POLICE_OK 0
+#define TC_POLICE_RECLASSIFY 1
+#define TC_POLICE_SHOT 2
+
+ __u32 limit;
+ __u32 burst;
+ __u32 mtu;
+ struct tc_ratespec rate;
+ struct tc_ratespec peakrate;
+};
+
+enum
+{
+ TCA_POLICE_UNSPEC,
+ TCA_POLICE_TBF,
+ TCA_POLICE_RATE,
+ TCA_POLICE_PEAKRATE,
+ TCA_POLICE_AVRATE,
+ TCA_POLICE_RESULT
+#define TCA_POLICE_RESULT TCA_POLICE_RESULT
+};
+
+#define TCA_POLICE_MAX TCA_POLICE_RESULT
+
+/* U32 filters */
+
+#define TC_U32_HTID(h) ((h)&0xFFF00000)
+#define TC_U32_USERHTID(h) (TC_U32_HTID(h)>>20)
+#define TC_U32_HASH(h) (((h)>>12)&0xFF)
+#define TC_U32_NODE(h) ((h)&0xFFF)
+#define TC_U32_KEY(h) ((h)&0xFFFFF)
+#define TC_U32_UNSPEC 0
+#define TC_U32_ROOT (0xFFF00000)
+
+enum
+{
+ TCA_U32_UNSPEC,
+ TCA_U32_CLASSID,
+ TCA_U32_HASH,
+ TCA_U32_LINK,
+ TCA_U32_DIVISOR,
+ TCA_U32_SEL,
+ TCA_U32_POLICE,
+};
+
+#define TCA_U32_MAX TCA_U32_POLICE
+
+struct tc_u32_key
+{
+ __u32 mask;
+ __u32 val;
+ int off;
+ int offmask;
+};
+
+struct tc_u32_sel
+{
+ unsigned char flags;
+ unsigned char offshift;
+ unsigned char nkeys;
+
+ __u16 offmask;
+ __u16 off;
+ short offoff;
+
+ short hoff;
+ __u32 hmask;
+
+ struct tc_u32_key keys[0];
+};
+
+/* Flags */
+
+#define TC_U32_TERMINAL 1
+#define TC_U32_OFFSET 2
+#define TC_U32_VAROFFSET 4
+#define TC_U32_EAT 8
+
+#define TC_U32_MAXDEPTH 8
+
+
+/* RSVP filter */
+
+enum
+{
+ TCA_RSVP_UNSPEC,
+ TCA_RSVP_CLASSID,
+ TCA_RSVP_DST,
+ TCA_RSVP_SRC,
+ TCA_RSVP_PINFO,
+ TCA_RSVP_POLICE,
+};
+
+#define TCA_RSVP_MAX TCA_RSVP_POLICE
+
+struct tc_rsvp_gpi
+{
+ __u32 key;
+ __u32 mask;
+ int offset;
+};
+
+struct tc_rsvp_pinfo
+{
+ struct tc_rsvp_gpi dpi;
+ struct tc_rsvp_gpi spi;
+ __u8 protocol;
+ __u8 tunnelid;
+ __u8 tunnelhdr;
+};
+
+/* ROUTE filter */
+
+enum
+{
+ TCA_ROUTE4_UNSPEC,
+ TCA_ROUTE4_CLASSID,
+ TCA_ROUTE4_TO,
+ TCA_ROUTE4_FROM,
+ TCA_ROUTE4_IIF,
+ TCA_ROUTE4_POLICE,
+};
+
+#define TCA_ROUTE4_MAX TCA_ROUTE4_POLICE
+
+
+/* FW filter */
+
+enum
+{
+ TCA_FW_UNSPEC,
+ TCA_FW_CLASSID,
+ TCA_FW_POLICE,
+};
+
+#define TCA_FW_MAX TCA_FW_POLICE
+
+#endif
diff --git a/pfinet/linux-src/include/linux/pkt_sched.h b/pfinet/linux-src/include/linux/pkt_sched.h
new file mode 100644
index 00000000..4ec170db
--- /dev/null
+++ b/pfinet/linux-src/include/linux/pkt_sched.h
@@ -0,0 +1,277 @@
+#ifndef __LINUX_PKT_SCHED_H
+#define __LINUX_PKT_SCHED_H
+
+/* Logical priority bands not depending on specific packet scheduler.
+ Every scheduler will map them to real traffic classes, if it has
+ no more precise mechanism to classify packets.
+
+ These numbers have no special meaning, though their coincidence
+ with obsolete IPv6 values is not occasional :-). New IPv6 drafts
+ preferred full anarchy inspired by diffserv group.
+
+ Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy
+ class, actually, as rule it will be handled with more care than
+ filler or even bulk.
+ */
+
+#define TC_PRIO_BESTEFFORT 0
+#define TC_PRIO_FILLER 1
+#define TC_PRIO_BULK 2
+#define TC_PRIO_INTERACTIVE_BULK 4
+#define TC_PRIO_INTERACTIVE 6
+#define TC_PRIO_CONTROL 7
+
+#define TC_PRIO_MAX 15
+
+/* Generic queue statistics, available for all the elements.
+ Particular schedulers may have also their private records.
+ */
+
+struct tc_stats
+{
+ __u64 bytes; /* NUmber of enqueues bytes */
+ __u32 packets; /* Number of enqueued packets */
+ __u32 drops; /* Packets dropped because of lack of resources */
+ __u32 overlimits; /* Number of throttle events when this
+ * flow goes out of allocated bandwidth */
+ __u32 bps; /* Current flow byte rate */
+ __u32 pps; /* Current flow packet rate */
+ __u32 qlen;
+ __u32 backlog;
+};
+
+struct tc_estimator
+{
+ char interval;
+ unsigned char ewma_log;
+};
+
+/* "Handles"
+ ---------
+
+ All the traffic control objects have 32bit identifiers, or "handles".
+
+ They can be considered as opaque numbers from user API viewpoint,
+ but actually they always consist of two fields: major and
+ minor numbers, which are interpreted by kernel specially,
+ that may be used by applications, though not recommended.
+
+ F.e. qdisc handles always have minor number equal to zero,
+ classes (or flows) have major equal to parent qdisc major, and
+ minor uniquely identifying class inside qdisc.
+
+ Macros to manipulate handles:
+ */
+
+#define TC_H_MAJ_MASK (0xFFFF0000U)
+#define TC_H_MIN_MASK (0x0000FFFFU)
+#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK)
+#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK)
+#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK))
+
+#define TC_H_UNSPEC (0U)
+#define TC_H_ROOT (0xFFFFFFFFU)
+
+struct tc_ratespec
+{
+ unsigned char cell_log;
+ unsigned char __reserved;
+ unsigned short feature;
+ short addend;
+ unsigned short mpu;
+ __u32 rate;
+};
+
+/* FIFO section */
+
+struct tc_fifo_qopt
+{
+ __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */
+};
+
+/* PRIO section */
+
+#define TCQ_PRIO_BANDS 16
+
+struct tc_prio_qopt
+{
+ int bands; /* Number of bands */
+ __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */
+};
+
+/* CSZ section */
+
+struct tc_csz_qopt
+{
+ int flows; /* Maximal number of guaranteed flows */
+ unsigned char R_log; /* Fixed point position for round number */
+ unsigned char delta_log; /* Log of maximal managed time interval */
+ __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> CSZ band */
+};
+
+struct tc_csz_copt
+{
+ struct tc_ratespec slice;
+ struct tc_ratespec rate;
+ struct tc_ratespec peakrate;
+ __u32 limit;
+ __u32 buffer;
+ __u32 mtu;
+};
+
+enum
+{
+ TCA_CSZ_UNSPEC,
+ TCA_CSZ_PARMS,
+ TCA_CSZ_RTAB,
+ TCA_CSZ_PTAB,
+};
+
+/* TBF section */
+
+struct tc_tbf_qopt
+{
+ struct tc_ratespec rate;
+ struct tc_ratespec peakrate;
+ __u32 limit;
+ __u32 buffer;
+ __u32 mtu;
+};
+
+enum
+{
+ TCA_TBF_UNSPEC,
+ TCA_TBF_PARMS,
+ TCA_TBF_RTAB,
+ TCA_TBF_PTAB,
+};
+
+
+/* TEQL section */
+
+/* TEQL does not require any parameters */
+
+/* SFQ section */
+
+struct tc_sfq_qopt
+{
+ unsigned quantum; /* Bytes per round allocated to flow */
+ int perturb_period; /* Period of hash perturbation */
+ __u32 limit; /* Maximal packets in queue */
+ unsigned divisor; /* Hash divisor */
+ unsigned flows; /* Maximal number of flows */
+};
+
+/*
+ * NOTE: limit, divisor and flows are hardwired to code at the moment.
+ *
+ * limit=flows=128, divisor=1024;
+ *
+ * The only reason for this is efficiency, it is possible
+ * to change these parameters in compile time.
+ */
+
+/* RED section */
+
+enum
+{
+ TCA_RED_UNSPEC,
+ TCA_RED_PARMS,
+ TCA_RED_STAB,
+};
+
+struct tc_red_qopt
+{
+ __u32 limit; /* HARD maximal queue length (bytes) */
+ __u32 qth_min; /* Min average length threshold (bytes) */
+ __u32 qth_max; /* Max average length threshold (bytes) */
+ unsigned char Wlog; /* log(W) */
+ unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */
+ unsigned char Scell_log; /* cell size for idle damping */
+};
+
+/* CBQ section */
+
+#define TC_CBQ_MAXPRIO 8
+#define TC_CBQ_MAXLEVEL 8
+#define TC_CBQ_DEF_EWMA 5
+
+struct tc_cbq_lssopt
+{
+ unsigned char change;
+ unsigned char flags;
+#define TCF_CBQ_LSS_BOUNDED 1
+#define TCF_CBQ_LSS_ISOLATED 2
+ unsigned char ewma_log;
+ unsigned char level;
+#define TCF_CBQ_LSS_FLAGS 1
+#define TCF_CBQ_LSS_EWMA 2
+#define TCF_CBQ_LSS_MAXIDLE 4
+#define TCF_CBQ_LSS_MINIDLE 8
+#define TCF_CBQ_LSS_OFFTIME 0x10
+#define TCF_CBQ_LSS_AVPKT 0x20
+ __u32 maxidle;
+ __u32 minidle;
+ __u32 offtime;
+ __u32 avpkt;
+};
+
+struct tc_cbq_wrropt
+{
+ unsigned char flags;
+ unsigned char priority;
+ unsigned char cpriority;
+ unsigned char __reserved;
+ __u32 allot;
+ __u32 weight;
+};
+
+struct tc_cbq_ovl
+{
+ unsigned char strategy;
+#define TC_CBQ_OVL_CLASSIC 0
+#define TC_CBQ_OVL_DELAY 1
+#define TC_CBQ_OVL_LOWPRIO 2
+#define TC_CBQ_OVL_DROP 3
+#define TC_CBQ_OVL_RCLASSIC 4
+ unsigned char priority2;
+ __u32 penalty;
+};
+
+struct tc_cbq_police
+{
+ unsigned char police;
+ unsigned char __res1;
+ unsigned short __res2;
+};
+
+struct tc_cbq_fopt
+{
+ __u32 split;
+ __u32 defmap;
+ __u32 defchange;
+};
+
+struct tc_cbq_xstats
+{
+ __u32 borrows;
+ __u32 overactions;
+ __s32 avgidle;
+ __s32 undertime;
+};
+
+enum
+{
+ TCA_CBQ_UNSPEC,
+ TCA_CBQ_LSSOPT,
+ TCA_CBQ_WRROPT,
+ TCA_CBQ_FOPT,
+ TCA_CBQ_OVL_STRATEGY,
+ TCA_CBQ_RATE,
+ TCA_CBQ_RTAB,
+ TCA_CBQ_POLICE,
+};
+
+#define TCA_CBQ_MAX TCA_CBQ_POLICE
+
+#endif
diff --git a/pfinet/linux-src/include/linux/poll.h b/pfinet/linux-src/include/linux/poll.h
new file mode 100644
index 00000000..991204f1
--- /dev/null
+++ b/pfinet/linux-src/include/linux/poll.h
@@ -0,0 +1,107 @@
+#ifndef _LINUX_POLL_H
+#define _LINUX_POLL_H
+
+#include <asm/poll.h>
+
+#ifdef __KERNEL__
+
+#include <linux/wait.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+
+
+struct poll_table_entry {
+ struct file * filp;
+ struct wait_queue wait;
+ struct wait_queue ** wait_address;
+};
+
+typedef struct poll_table_struct {
+ struct poll_table_struct * next;
+ unsigned int nr;
+ struct poll_table_entry * entry;
+} poll_table;
+
+#define __MAX_POLL_TABLE_ENTRIES ((PAGE_SIZE - sizeof (poll_table)) / sizeof (struct poll_table_entry))
+
+extern void __pollwait(struct file * filp, struct wait_queue ** wait_address, poll_table *p);
+
+extern inline void poll_wait(struct file * filp, struct wait_queue ** wait_address, poll_table *p)
+{
+ if (p && wait_address)
+ __pollwait(filp, wait_address, p);
+}
+
+/*
+ * For the kernel fd_set we use a fixed set-size for allocation purposes.
+ * This set-size doesn't necessarily bear any relation to the size the user
+ * uses, but should preferably obviously be larger than any possible user
+ * size (NR_OPEN bits).
+ *
+ * We need 6 bitmaps (in/out/ex for both incoming and outgoing), and we
+ * allocate one page for all the bitmaps. Thus we have 8*PAGE_SIZE bits,
+ * to be divided by 6. And we'd better make sure we round to a full
+ * long-word (in fact, we'll round to 64 bytes).
+ */
+
+
+#define KFDS_64BLOCK ((PAGE_SIZE/(6*64))*64)
+#define KFDS_NR (KFDS_64BLOCK*8 > NR_OPEN ? NR_OPEN : KFDS_64BLOCK*8)
+typedef unsigned long kernel_fd_set[KFDS_NR/__NFDBITS];
+
+/*
+ * Scalable version of the fd_set.
+ */
+
+typedef struct {
+ unsigned long *in, *out, *ex;
+ unsigned long *res_in, *res_out, *res_ex;
+} fd_set_bits;
+
+/*
+ * How many longwords for "nr" bits?
+ */
+#define FDS_BITPERLONG (8*sizeof(long))
+#define FDS_LONGS(nr) (((nr)+FDS_BITPERLONG-1)/FDS_BITPERLONG)
+#define FDS_BYTES(nr) (FDS_LONGS(nr)*sizeof(long))
+
+/*
+ * We do a VERIFY_WRITE here even though we are only reading this time:
+ * we'll write to it eventually..
+ *
+ * Use "unsigned long" accesses to let user-mode fd_set's be long-aligned.
+ */
+static inline
+int get_fd_set(unsigned long nr, void *ufdset, unsigned long *fdset)
+{
+ nr = FDS_BYTES(nr);
+ if (ufdset) {
+ int error;
+ error = verify_area(VERIFY_WRITE, ufdset, nr);
+ if (!error && __copy_from_user(fdset, ufdset, nr))
+ error = -EFAULT;
+ return error;
+ }
+ memset(fdset, 0, nr);
+ return 0;
+}
+
+static inline
+void set_fd_set(unsigned long nr, void *ufdset, unsigned long *fdset)
+{
+ if (ufdset)
+ __copy_to_user(ufdset, fdset, FDS_BYTES(nr));
+}
+
+static inline
+void zero_fd_set(unsigned long nr, unsigned long *fdset)
+{
+ memset(fdset, 0, FDS_BYTES(nr));
+}
+
+extern int do_select(int n, fd_set_bits *fds, long *timeout);
+
+#endif /* KERNEL */
+
+#endif /* _LINUX_POLL_H */
diff --git a/pfinet/linux-src/include/linux/posix_types.h b/pfinet/linux-src/include/linux/posix_types.h
new file mode 100644
index 00000000..3ee2ed9d
--- /dev/null
+++ b/pfinet/linux-src/include/linux/posix_types.h
@@ -0,0 +1,48 @@
+#ifndef _LINUX_POSIX_TYPES_H
+#define _LINUX_POSIX_TYPES_H
+
+#include <linux/stddef.h>
+
+/*
+ * This allows for 1024 file descriptors: if NR_OPEN is ever grown
+ * beyond that you'll have to change this too. But 1024 fd's seem to be
+ * enough even for such "real" unices like OSF/1, so hopefully this is
+ * one limit that doesn't have to be changed [again].
+ *
+ * Note that POSIX wants the FD_CLEAR(fd,fdsetp) defines to be in
+ * <sys/time.h> (and thus <linux/time.h>) - but this is a more logical
+ * place for them. Solved by having dummy defines in <sys/time.h>.
+ */
+
+/*
+ * Those macros may have been defined in <gnu/types.h>. But we always
+ * use the ones here.
+ */
+#undef __NFDBITS
+#define __NFDBITS (8 * sizeof(unsigned long))
+
+#undef __FD_SETSIZE
+#define __FD_SETSIZE 1024
+
+#undef __FDSET_LONGS
+#define __FDSET_LONGS (__FD_SETSIZE/__NFDBITS)
+
+#undef __FDELT
+#define __FDELT(d) ((d) / __NFDBITS)
+
+#undef __FDMASK
+#define __FDMASK(d) (1UL << ((d) % __NFDBITS))
+
+typedef struct {
+ unsigned long fds_bits [__FDSET_LONGS];
+} __kernel_fd_set;
+
+/* Type of a signal handler. */
+typedef void (*__kernel_sighandler_t)(int);
+
+/* Type of a SYSV IPC key. */
+typedef int __kernel_key_t;
+
+#include <asm/posix_types.h>
+
+#endif /* _LINUX_POSIX_TYPES_H */
diff --git a/pfinet/linux-src/include/linux/ppp-comp.h b/pfinet/linux-src/include/linux/ppp-comp.h
new file mode 100644
index 00000000..3a5d5865
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ppp-comp.h
@@ -0,0 +1,198 @@
+/*
+ * ppp-comp.h - Definitions for doing PPP packet compression.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id: ppp-comp.h,v 1.6 1997/11/27 06:04:44 paulus Exp $
+ */
+
+/*
+ * ==FILEVERSION 980319==
+ *
+ * NOTE TO MAINTAINERS:
+ * If you modify this file at all, please set the above date.
+ * ppp-comp.h is shipped with a PPP distribution as well as with the kernel;
+ * if everyone increases the FILEVERSION number above, then scripts
+ * can do the right thing when deciding whether to install a new ppp-comp.h
+ * file. Don't change the format of that line otherwise, so the
+ * installation script can recognize it.
+ */
+
+#ifndef _NET_PPP_COMP_H
+#define _NET_PPP_COMP_H
+
+/*
+ * The following symbols control whether we include code for
+ * various compression methods.
+ */
+
+#ifndef DO_BSD_COMPRESS
+#define DO_BSD_COMPRESS 1 /* by default, include BSD-Compress */
+#endif
+#ifndef DO_DEFLATE
+#define DO_DEFLATE 1 /* by default, include Deflate */
+#endif
+#define DO_PREDICTOR_1 0
+#define DO_PREDICTOR_2 0
+
+/*
+ * Structure giving methods for compression/decompression.
+ */
+
+struct compressor {
+ int compress_proto; /* CCP compression protocol number */
+
+ /* Allocate space for a compressor (transmit side) */
+ void *(*comp_alloc) (unsigned char *options, int opt_len);
+
+ /* Free space used by a compressor */
+ void (*comp_free) (void *state);
+
+ /* Initialize a compressor */
+ int (*comp_init) (void *state, unsigned char *options,
+ int opt_len, int unit, int opthdr, int debug);
+
+ /* Reset a compressor */
+ void (*comp_reset) (void *state);
+
+ /* Compress a packet */
+ int (*compress) (void *state, unsigned char *rptr,
+ unsigned char *obuf, int isize, int osize);
+
+ /* Return compression statistics */
+ void (*comp_stat) (void *state, struct compstat *stats);
+
+ /* Allocate space for a decompressor (receive side) */
+ void *(*decomp_alloc) (unsigned char *options, int opt_len);
+
+ /* Free space used by a decompressor */
+ void (*decomp_free) (void *state);
+
+ /* Initialize a decompressor */
+ int (*decomp_init) (void *state, unsigned char *options,
+ int opt_len, int unit, int opthdr, int mru,
+ int debug);
+
+ /* Reset a decompressor */
+ void (*decomp_reset) (void *state);
+
+ /* Decompress a packet. */
+ int (*decompress) (void *state, unsigned char *ibuf, int isize,
+ unsigned char *obuf, int osize);
+
+ /* Update state for an incompressible packet received */
+ void (*incomp) (void *state, unsigned char *ibuf, int icnt);
+
+ /* Return decompression statistics */
+ void (*decomp_stat) (void *state, struct compstat *stats);
+};
+
+/*
+ * The return value from decompress routine is the length of the
+ * decompressed packet if successful, otherwise DECOMP_ERROR
+ * or DECOMP_FATALERROR if an error occurred.
+ *
+ * We need to make this distinction so that we can disable certain
+ * useful functionality, namely sending a CCP reset-request as a result
+ * of an error detected after decompression. This is to avoid infringing
+ * a patent held by Motorola.
+ * Don't you just lurve software patents.
+ */
+
+#define DECOMP_ERROR -1 /* error detected before decomp. */
+#define DECOMP_FATALERROR -2 /* error detected after decomp. */
+
+/*
+ * CCP codes.
+ */
+
+#define CCP_CONFREQ 1
+#define CCP_CONFACK 2
+#define CCP_TERMREQ 5
+#define CCP_TERMACK 6
+#define CCP_RESETREQ 14
+#define CCP_RESETACK 15
+
+/*
+ * Max # bytes for a CCP option
+ */
+
+#define CCP_MAX_OPTION_LENGTH 32
+
+/*
+ * Parts of a CCP packet.
+ */
+
+#define CCP_CODE(dp) ((dp)[0])
+#define CCP_ID(dp) ((dp)[1])
+#define CCP_LENGTH(dp) (((dp)[2] << 8) + (dp)[3])
+#define CCP_HDRLEN 4
+
+#define CCP_OPT_CODE(dp) ((dp)[0])
+#define CCP_OPT_LENGTH(dp) ((dp)[1])
+#define CCP_OPT_MINLEN 2
+
+/*
+ * Definitions for BSD-Compress.
+ */
+
+#define CI_BSD_COMPRESS 21 /* config. option for BSD-Compress */
+#define CILEN_BSD_COMPRESS 3 /* length of config. option */
+
+/* Macros for handling the 3rd byte of the BSD-Compress config option. */
+#define BSD_NBITS(x) ((x) & 0x1F) /* number of bits requested */
+#define BSD_VERSION(x) ((x) >> 5) /* version of option format */
+#define BSD_CURRENT_VERSION 1 /* current version number */
+#define BSD_MAKE_OPT(v, n) (((v) << 5) | (n))
+
+#define BSD_MIN_BITS 9 /* smallest code size supported */
+#define BSD_MAX_BITS 15 /* largest code size supported */
+
+/*
+ * Definitions for Deflate.
+ */
+
+#define CI_DEFLATE 26 /* config option for Deflate */
+#define CI_DEFLATE_DRAFT 24 /* value used in original draft RFC */
+#define CILEN_DEFLATE 4 /* length of its config option */
+
+#define DEFLATE_MIN_SIZE 8
+#define DEFLATE_MAX_SIZE 15
+#define DEFLATE_METHOD_VAL 8
+#define DEFLATE_SIZE(x) (((x) >> 4) + DEFLATE_MIN_SIZE)
+#define DEFLATE_METHOD(x) ((x) & 0x0F)
+#define DEFLATE_MAKE_OPT(w) ((((w) - DEFLATE_MIN_SIZE) << 4) \
+ + DEFLATE_METHOD_VAL)
+#define DEFLATE_CHK_SEQUENCE 0
+
+/*
+ * Definitions for other, as yet unsupported, compression methods.
+ */
+
+#define CI_PREDICTOR_1 1 /* config option for Predictor-1 */
+#define CILEN_PREDICTOR_1 2 /* length of its config option */
+#define CI_PREDICTOR_2 2 /* config option for Predictor-2 */
+#define CILEN_PREDICTOR_2 2 /* length of its config option */
+
+#endif /* _NET_PPP_COMP_H */
diff --git a/pfinet/linux-src/include/linux/ppp.h b/pfinet/linux-src/include/linux/ppp.h
new file mode 100644
index 00000000..233f961e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ppp.h
@@ -0,0 +1,4 @@
+/*
+ * Back compatibility for a while.
+ */
+#include <linux/if_ppp.h>
diff --git a/pfinet/linux-src/include/linux/ppp_defs.h b/pfinet/linux-src/include/linux/ppp_defs.h
new file mode 100644
index 00000000..c506c90a
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ppp_defs.h
@@ -0,0 +1,182 @@
+/* $Id: ppp_defs.h,v 1.2 1994/09/21 01:31:06 paulus Exp $ */
+
+/*
+ * ppp_defs.h - PPP definitions.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+/*
+ * ==FILEVERSION 990114==
+ *
+ * NOTE TO MAINTAINERS:
+ * If you modify this file at all, please set the above date.
+ * ppp_defs.h is shipped with a PPP distribution as well as with the kernel;
+ * if everyone increases the FILEVERSION number above, then scripts
+ * can do the right thing when deciding whether to install a new ppp_defs.h
+ * file. Don't change the format of that line otherwise, so the
+ * installation script can recognize it.
+ */
+
+#ifndef _PPP_DEFS_H_
+#define _PPP_DEFS_H_
+
+/*
+ * The basic PPP frame.
+ */
+#define PPP_HDRLEN 4 /* octets for standard ppp header */
+#define PPP_FCSLEN 2 /* octets for FCS */
+#define PPP_MRU 1500 /* default MRU = max length of info field */
+
+#define PPP_ADDRESS(p) (((__u8 *)(p))[0])
+#define PPP_CONTROL(p) (((__u8 *)(p))[1])
+#define PPP_PROTOCOL(p) ((((__u8 *)(p))[2] << 8) + ((__u8 *)(p))[3])
+
+/*
+ * Significant octet values.
+ */
+#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */
+#define PPP_UI 0x03 /* Unnumbered Information */
+#define PPP_FLAG 0x7e /* Flag Sequence */
+#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */
+#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */
+
+/*
+ * Protocol field values.
+ */
+#define PPP_IP 0x21 /* Internet Protocol */
+#define PPP_AT 0x29 /* AppleTalk Protocol */
+#define PPP_IPX 0x2b /* IPX protocol */
+#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */
+#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */
+#define PPP_IPV6 0x57 /* Internet Protocol Version 6 */
+#define PPP_COMP 0xfd /* compressed packet */
+#define PPP_IPCP 0x8021 /* IP Control Protocol */
+#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */
+#define PPP_IPXCP 0x802b /* IPX Control Protocol */
+#define PPP_IPV6CP 0x8057 /* IPv6 Control Protocol */
+#define PPP_CCP 0x80fd /* Compression Control Protocol */
+#define PPP_LCP 0xc021 /* Link Control Protocol */
+#define PPP_PAP 0xc023 /* Password Authentication Protocol */
+#define PPP_LQR 0xc025 /* Link Quality Report protocol */
+#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */
+#define PPP_CBCP 0xc029 /* Callback Control Protocol */
+
+/*
+ * Values for FCS calculations.
+ */
+
+#define PPP_INITFCS 0xffff /* Initial FCS value */
+#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */
+#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
+
+/*
+ * Extended asyncmap - allows any character to be escaped.
+ */
+
+typedef __u32 ext_accm[8];
+
+/*
+ * What to do with network protocol (NP) packets.
+ */
+enum NPmode {
+ NPMODE_PASS, /* pass the packet through */
+ NPMODE_DROP, /* silently drop the packet */
+ NPMODE_ERROR, /* return an error */
+ NPMODE_QUEUE /* save it up for later. */
+};
+
+/*
+ * Statistics for LQRP and pppstats
+ */
+struct pppstat {
+ __u32 ppp_discards; /* # frames discarded */
+
+ __u32 ppp_ibytes; /* bytes received */
+ __u32 ppp_ioctects; /* bytes received not in error */
+ __u32 ppp_ipackets; /* packets received */
+ __u32 ppp_ierrors; /* receive errors */
+ __u32 ppp_ilqrs; /* # LQR frames received */
+
+ __u32 ppp_obytes; /* raw bytes sent */
+ __u32 ppp_ooctects; /* frame bytes sent */
+ __u32 ppp_opackets; /* packets sent */
+ __u32 ppp_oerrors; /* transmit errors */
+ __u32 ppp_olqrs; /* # LQR frames sent */
+};
+
+struct vjstat {
+ __u32 vjs_packets; /* outbound packets */
+ __u32 vjs_compressed; /* outbound compressed packets */
+ __u32 vjs_searches; /* searches for connection state */
+ __u32 vjs_misses; /* times couldn't find conn. state */
+ __u32 vjs_uncompressedin; /* inbound uncompressed packets */
+ __u32 vjs_compressedin; /* inbound compressed packets */
+ __u32 vjs_errorin; /* inbound unknown type packets */
+ __u32 vjs_tossed; /* inbound packets tossed because of error */
+};
+
+struct compstat {
+ __u32 unc_bytes; /* total uncompressed bytes */
+ __u32 unc_packets; /* total uncompressed packets */
+ __u32 comp_bytes; /* compressed bytes */
+ __u32 comp_packets; /* compressed packets */
+ __u32 inc_bytes; /* incompressible bytes */
+ __u32 inc_packets; /* incompressible packets */
+
+ /* the compression ratio is defined as in_count / bytes_out */
+ __u32 in_count; /* Bytes received */
+ __u32 bytes_out; /* Bytes transmitted */
+
+ double ratio; /* not computed in kernel. */
+};
+
+struct ppp_stats {
+ struct pppstat p; /* basic PPP statistics */
+ struct vjstat vj; /* VJ header compression statistics */
+};
+
+struct ppp_comp_stats {
+ struct compstat c; /* packet compression statistics */
+ struct compstat d; /* packet decompression statistics */
+};
+
+/*
+ * The following structure records the time in seconds since
+ * the last NP packet was sent or received.
+ */
+struct ppp_idle {
+ time_t xmit_idle; /* time since last NP packet sent */
+ time_t recv_idle; /* time since last NP packet received */
+};
+
+#ifndef __P
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+#endif
+
+#endif /* _PPP_DEFS_H_ */
diff --git a/pfinet/linux-src/include/linux/prctl.h b/pfinet/linux-src/include/linux/prctl.h
new file mode 100644
index 00000000..f08f3c36
--- /dev/null
+++ b/pfinet/linux-src/include/linux/prctl.h
@@ -0,0 +1,9 @@
+#ifndef _LINUX_PRCTL_H
+#define _LINUX_PRCTL_H
+
+/* Values to pass as first argument to prctl() */
+
+#define PR_SET_PDEATHSIG 1 /* Second arg is a signal */
+
+
+#endif /* _LINUX_PRCTL_H */
diff --git a/pfinet/linux-src/include/linux/proc_fs.h b/pfinet/linux-src/include/linux/proc_fs.h
new file mode 100644
index 00000000..77d7d741
--- /dev/null
+++ b/pfinet/linux-src/include/linux/proc_fs.h
@@ -0,0 +1,472 @@
+#ifndef _LINUX_PROC_FS_H
+#define _LINUX_PROC_FS_H
+
+#include <linux/config.h>
+#include <linux/malloc.h>
+
+/*
+ * The proc filesystem constants/structures
+ */
+
+/*
+ * We always define these enumerators
+ */
+
+enum root_directory_inos {
+ PROC_ROOT_INO = 1,
+ PROC_LOADAVG,
+ PROC_UPTIME,
+ PROC_MEMINFO,
+ PROC_KMSG,
+ PROC_VERSION,
+ PROC_CPUINFO,
+ PROC_PCI,
+ PROC_MCA,
+ PROC_NUBUS,
+ PROC_SELF, /* will change inode # */
+ PROC_NET,
+ PROC_SCSI,
+ PROC_MALLOC,
+ PROC_KCORE,
+ PROC_MODULES,
+ PROC_STAT,
+ PROC_DEVICES,
+ PROC_PARTITIONS,
+ PROC_INTERRUPTS,
+ PROC_FILESYSTEMS,
+ PROC_KSYMS,
+ PROC_DMA,
+ PROC_IOPORTS,
+ PROC_PROFILE, /* whether enabled or not */
+ PROC_CMDLINE,
+ PROC_SYS,
+ PROC_MTAB,
+ PROC_SWAP,
+ PROC_MD,
+ PROC_RTC,
+ PROC_LOCKS,
+ PROC_HARDWARE,
+ PROC_SLABINFO,
+ PROC_PARPORT,
+ PROC_PPC_HTAB,
+ PROC_STRAM,
+ PROC_SOUND,
+ PROC_MTRR, /* whether enabled or not */
+ PROC_FS
+};
+
+enum pid_directory_inos {
+ PROC_PID_INO = 2,
+ PROC_PID_STATUS,
+ PROC_PID_MEM,
+ PROC_PID_CWD,
+ PROC_PID_ROOT,
+ PROC_PID_EXE,
+ PROC_PID_FD,
+ PROC_PID_ENVIRON,
+ PROC_PID_CMDLINE,
+ PROC_PID_STAT,
+ PROC_PID_STATM,
+ PROC_PID_MAPS,
+#if CONFIG_AP1000
+ PROC_PID_RINGBUF,
+#endif
+ PROC_PID_CPU,
+};
+
+enum pid_subdirectory_inos {
+ PROC_PID_FD_DIR = 0x8000, /* 0x8000-0xffff */
+};
+
+enum net_directory_inos {
+ PROC_NET_UNIX = 128,
+ PROC_NET_ARP,
+ PROC_NET_ROUTE,
+ PROC_NET_DEV,
+ PROC_NET_RAW,
+ PROC_NET_RAW6,
+ PROC_NET_TCP,
+ PROC_NET_TCP6,
+ PROC_NET_UDP,
+ PROC_NET_UDP6,
+ PROC_NET_SNMP,
+ PROC_NET_RARP,
+ PROC_NET_IGMP,
+ PROC_NET_IPMR_VIF,
+ PROC_NET_IPMR_MFC,
+ PROC_NET_IPFWFWD,
+ PROC_NET_IPFWIN,
+ PROC_NET_IPFWOUT,
+ PROC_NET_IPACCT,
+ PROC_NET_IPMSQHST,
+ PROC_NET_WIRELESS,
+ PROC_NET_IPX_INTERFACE,
+ PROC_NET_IPX_ROUTE,
+ PROC_NET_IPX,
+ PROC_NET_ATALK,
+ PROC_NET_AT_ROUTE,
+ PROC_NET_ATIF,
+ PROC_NET_AX25_ROUTE,
+ PROC_NET_AX25,
+ PROC_NET_AX25_CALLS,
+ PROC_NET_BMAC,
+ PROC_NET_NR_NODES,
+ PROC_NET_NR_NEIGH,
+ PROC_NET_NR,
+ PROC_NET_SOCKSTAT,
+ PROC_NET_SOCKSTAT6,
+ PROC_NET_RTCACHE,
+ PROC_NET_AX25_BPQETHER,
+ PROC_NET_IP_MASQ_APP,
+ PROC_NET_RT6,
+ PROC_NET_SNMP6,
+ PROC_NET_RT6_STATS,
+ PROC_NET_NDISC,
+ PROC_NET_STRIP_STATUS,
+ PROC_NET_STRIP_TRACE,
+ PROC_NET_Z8530,
+ PROC_NET_RS_NODES,
+ PROC_NET_RS_NEIGH,
+ PROC_NET_RS_ROUTES,
+ PROC_NET_RS,
+ PROC_NET_CL2LLC,
+ PROC_NET_X25_ROUTES,
+ PROC_NET_X25,
+ PROC_NET_TR_RIF,
+ PROC_NET_DN_DEV,
+ PROC_NET_DN_ADJ,
+ PROC_NET_DN_L1,
+ PROC_NET_DN_L2,
+ PROC_NET_DN_CACHE,
+ PROC_NET_DN_SKT,
+ PROC_NET_DN_FW,
+ PROC_NET_DN_RAW,
+ PROC_NET_NETSTAT,
+ PROC_NET_IPFW_CHAINS,
+ PROC_NET_IPFW_CHAIN_NAMES,
+ PROC_NET_AT_AARP,
+ PROC_NET_BRIDGE,
+ PROC_NET_LAST
+};
+
+enum scsi_directory_inos {
+ PROC_SCSI_SCSI = 256,
+ PROC_SCSI_ADVANSYS,
+ PROC_SCSI_PCI2000,
+ PROC_SCSI_PCI2220I,
+ PROC_SCSI_PSI240I,
+ PROC_SCSI_EATA,
+ PROC_SCSI_EATA_PIO,
+ PROC_SCSI_AHA152X,
+ PROC_SCSI_AHA1542,
+ PROC_SCSI_AHA1740,
+ PROC_SCSI_AIC7XXX,
+ PROC_SCSI_BUSLOGIC,
+ PROC_SCSI_U14_34F,
+ PROC_SCSI_FDOMAIN,
+ PROC_SCSI_GDTH,
+ PROC_SCSI_GENERIC_NCR5380,
+ PROC_SCSI_IN2000,
+ PROC_SCSI_PAS16,
+ PROC_SCSI_QLOGICFAS,
+ PROC_SCSI_QLOGICISP,
+ PROC_SCSI_QLOGICFC,
+ PROC_SCSI_SEAGATE,
+ PROC_SCSI_T128,
+ PROC_SCSI_NCR53C7xx,
+ PROC_SCSI_SYM53C8XX,
+ PROC_SCSI_NCR53C8XX,
+ PROC_SCSI_ULTRASTOR,
+ PROC_SCSI_7000FASST,
+ PROC_SCSI_IBMMCA,
+ PROC_SCSI_FD_MCS,
+ PROC_SCSI_EATA2X,
+ PROC_SCSI_DC390T,
+ PROC_SCSI_AM53C974,
+ PROC_SCSI_SSC,
+ PROC_SCSI_NCR53C406A,
+ PROC_SCSI_SYM53C416,
+ PROC_SCSI_MEGARAID,
+ PROC_SCSI_PPA,
+ PROC_SCSI_ATP870U,
+ PROC_SCSI_ESP,
+ PROC_SCSI_QLOGICPTI,
+ PROC_SCSI_AMIGA7XX,
+ PROC_SCSI_MVME16x,
+ PROC_SCSI_BVME6000,
+ PROC_SCSI_SIM710,
+ PROC_SCSI_A3000,
+ PROC_SCSI_A2091,
+ PROC_SCSI_GVP11,
+ PROC_SCSI_ATARI,
+ PROC_SCSI_MAC,
+ PROC_SCSI_IDESCSI,
+ PROC_SCSI_SGIWD93,
+ PROC_SCSI_MESH,
+ PROC_SCSI_53C94,
+ PROC_SCSI_PLUTO,
+ PROC_SCSI_INI9100U,
+ PROC_SCSI_INIA100,
+ PROC_SCSI_IPH5526_FC,
+ PROC_SCSI_FCAL,
+ PROC_SCSI_I2O,
+ PROC_SCSI_USB_SCSI,
+ PROC_SCSI_SCSI_DEBUG,
+ PROC_SCSI_NOT_PRESENT,
+ PROC_SCSI_FILE, /* I'm assuming here that we */
+ PROC_SCSI_LAST = (PROC_SCSI_FILE + 16) /* won't ever see more than */
+}; /* 16 HBAs in one machine */
+
+enum mca_directory_inos {
+ PROC_MCA_MACHINE = (PROC_SCSI_LAST+1),
+ PROC_MCA_REGISTERS,
+ PROC_MCA_VIDEO,
+ PROC_MCA_SCSI,
+ PROC_MCA_SLOT, /* the 8 adapter slots */
+ PROC_MCA_LAST = (PROC_MCA_SLOT + 8)
+};
+
+enum bus_directory_inos {
+ PROC_BUS_PCI = PROC_MCA_LAST,
+ PROC_BUS_PCI_DEVICES,
+ PROC_BUS_ZORRO,
+ PROC_BUS_ZORRO_DEVICES,
+ PROC_BUS_LAST
+};
+
+enum fs_directory_inos {
+ PROC_FS_CODA = PROC_BUS_LAST,
+ PROC_FS_LAST
+};
+
+enum fs_coda_directory_inos {
+ PROC_VFS_STATS = PROC_FS_LAST,
+ PROC_UPCALL_STATS,
+ PROC_PERMISSION_STATS,
+ PROC_CACHE_INV_STATS,
+ PROC_CODA_FS_LAST
+};
+
+/* Finally, the dynamically allocatable proc entries are reserved: */
+
+#define PROC_DYNAMIC_FIRST 4096
+#define PROC_NDYNAMIC 4096
+#define PROC_OPENPROM_FIRST (PROC_DYNAMIC_FIRST+PROC_NDYNAMIC)
+#define PROC_OPENPROM PROC_OPENPROM_FIRST
+#define PROC_NOPENPROM 4096
+#define PROC_OPENPROMD_FIRST (PROC_OPENPROM_FIRST+PROC_NOPENPROM)
+#define PROC_NOPENPROMD 4096
+
+#define PROC_SUPER_MAGIC 0x9fa0
+
+/*
+ * This is not completely implemented yet. The idea is to
+ * create an in-memory tree (like the actual /proc filesystem
+ * tree) of these proc_dir_entries, so that we can dynamically
+ * add new files to /proc.
+ *
+ * The "next" pointer creates a linked list of one /proc directory,
+ * while parent/subdir create the directory structure (every
+ * /proc file has a parent, but "subdir" is NULL for all
+ * non-directory entries).
+ *
+ * "get_info" is called at "read", while "fill_inode" is used to
+ * fill in file type/protection/owner information specific to the
+ * particular /proc file.
+ */
+struct proc_dir_entry {
+ unsigned short low_ino;
+ unsigned short namelen;
+ const char *name;
+ mode_t mode;
+ nlink_t nlink;
+ uid_t uid;
+ gid_t gid;
+ unsigned long size;
+ struct inode_operations * ops;
+ int (*get_info)(char *, char **, off_t, int, int);
+ void (*fill_inode)(struct inode *, int);
+ struct proc_dir_entry *next, *parent, *subdir;
+ void *data;
+ int (*read_proc)(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+ int (*write_proc)(struct file *file, const char *buffer,
+ unsigned long count, void *data);
+ int (*readlink_proc)(struct proc_dir_entry *de, char *page);
+ unsigned int count; /* use count */
+ int deleted; /* delete flag */
+};
+
+typedef int (read_proc_t)(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+typedef int (write_proc_t)(struct file *file, const char *buffer,
+ unsigned long count, void *data);
+
+extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
+ off_t offset, int length, int inout);
+
+#ifdef CONFIG_PROC_FS
+
+extern struct proc_dir_entry proc_root;
+extern struct proc_dir_entry proc_root_fs;
+extern struct proc_dir_entry *proc_net;
+extern struct proc_dir_entry *proc_scsi;
+extern struct proc_dir_entry proc_sys;
+extern struct proc_dir_entry proc_openprom;
+extern struct proc_dir_entry proc_pid;
+extern struct proc_dir_entry proc_pid_fd;
+extern struct proc_dir_entry proc_mca;
+extern struct proc_dir_entry *proc_bus;
+
+extern struct inode_operations proc_scsi_inode_operations;
+
+extern void proc_root_init(void);
+extern void proc_base_init(void);
+
+extern int proc_register(struct proc_dir_entry *, struct proc_dir_entry *);
+extern int proc_unregister(struct proc_dir_entry *, int);
+
+static inline int proc_net_register(struct proc_dir_entry * x)
+{
+ return proc_register(proc_net, x);
+}
+
+static inline int proc_net_unregister(int x)
+{
+ return proc_unregister(proc_net, x);
+}
+
+static inline int proc_scsi_register(struct proc_dir_entry *driver,
+ struct proc_dir_entry *x)
+{
+ x->ops = &proc_scsi_inode_operations;
+ if(x->low_ino < PROC_SCSI_FILE){
+ return(proc_register(proc_scsi, x));
+ }else{
+ return(proc_register(driver, x));
+ }
+}
+
+static inline int proc_scsi_unregister(struct proc_dir_entry *driver, int x)
+{
+ extern void scsi_init_free(char *ptr, unsigned int size);
+
+ if(x < PROC_SCSI_FILE)
+ return(proc_unregister(proc_scsi, x));
+ else {
+ struct proc_dir_entry **p = &driver->subdir, *dp;
+ int ret;
+
+ while ((dp = *p) != NULL) {
+ if (dp->low_ino == x)
+ break;
+ p = &dp->next;
+ }
+ ret = proc_unregister(driver, x);
+ scsi_init_free((char *) dp, sizeof(struct proc_dir_entry) + 4);
+ return(ret);
+ }
+}
+
+extern struct dentry_operations proc_dentry_operations;
+extern struct super_block *proc_read_super(struct super_block *,void *,int);
+extern int init_proc_fs(void);
+extern struct inode * proc_get_inode(struct super_block *, int, struct proc_dir_entry *);
+extern int proc_statfs(struct super_block *, struct statfs *, int);
+extern void proc_read_inode(struct inode *);
+extern void proc_write_inode(struct inode *);
+extern int proc_permission(struct inode *, int);
+
+extern int proc_match(int, const char *,struct proc_dir_entry *);
+
+/*
+ * These are generic /proc routines that use the internal
+ * "struct proc_dir_entry" tree to traverse the filesystem.
+ *
+ * The /proc root directory has extended versions to take care
+ * of the /proc/<pid> subdirectories.
+ */
+extern int proc_readdir(struct file *, void *, filldir_t);
+extern struct dentry *proc_lookup(struct inode *, struct dentry *);
+
+struct openpromfs_dev {
+ struct openpromfs_dev *next;
+ u32 node;
+ ino_t inode;
+ kdev_t rdev;
+ mode_t mode;
+ char name[32];
+};
+extern struct inode_operations *
+proc_openprom_register(int (*readdir)(struct file *, void *, filldir_t),
+ struct dentry * (*lookup)(struct inode *, struct dentry *),
+ void (*use)(struct inode *, int),
+ struct openpromfs_dev ***);
+extern void proc_openprom_deregister(void);
+extern void (*proc_openprom_use)(struct inode *,int);
+extern int proc_openprom_regdev(struct openpromfs_dev *);
+extern int proc_openprom_unregdev(struct openpromfs_dev *);
+
+extern struct inode_operations proc_dir_inode_operations;
+extern struct inode_operations proc_file_inode_operations;
+extern struct inode_operations proc_net_inode_operations;
+extern struct inode_operations proc_netdir_inode_operations;
+extern struct inode_operations proc_openprom_inode_operations;
+extern struct inode_operations proc_mem_inode_operations;
+extern struct inode_operations proc_sys_inode_operations;
+extern struct inode_operations proc_array_inode_operations;
+extern struct inode_operations proc_arraylong_inode_operations;
+extern struct inode_operations proc_kcore_inode_operations;
+extern struct inode_operations proc_profile_inode_operations;
+extern struct inode_operations proc_kmsg_inode_operations;
+extern struct inode_operations proc_link_inode_operations;
+extern struct inode_operations proc_fd_inode_operations;
+#if CONFIG_AP1000
+extern struct inode_operations proc_ringbuf_inode_operations;
+#endif
+extern struct inode_operations proc_omirr_inode_operations;
+extern struct inode_operations proc_ppc_htab_inode_operations;
+
+/*
+ * generic.c
+ */
+struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
+ struct proc_dir_entry *parent);
+void remove_proc_entry(const char *name, struct proc_dir_entry *parent);
+
+/*
+ * proc_tty.c
+ */
+extern void proc_tty_init(void);
+extern void proc_tty_register_driver(struct tty_driver *driver);
+extern void proc_tty_unregister_driver(struct tty_driver *driver);
+
+/*
+ * proc_devtree.c
+ */
+extern void proc_device_tree_init(void);
+
+#else
+
+extern inline int proc_register(struct proc_dir_entry *a, struct proc_dir_entry *b) { return 0; };
+extern inline int proc_unregister(struct proc_dir_entry *a, int b) { return 0; };
+extern inline int proc_net_register(struct proc_dir_entry *a) { return 0; };
+extern inline int proc_net_unregister(int x) { return 0; };
+extern inline int proc_scsi_register(struct proc_dir_entry *b, struct proc_dir_entry *c) { return 0; };
+extern inline int proc_scsi_unregister(struct proc_dir_entry *a, int x) { return 0; };
+
+extern inline struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
+ struct proc_dir_entry *parent)
+{
+ return NULL;
+}
+
+extern inline void remove_proc_entry(const char *name, struct proc_dir_entry *parent) {};
+
+extern inline void proc_tty_register_driver(struct tty_driver *driver) {};
+extern inline void proc_tty_unregister_driver(struct tty_driver *driver) {};
+
+extern struct proc_dir_entry proc_root;
+
+#endif /* CONFIG_PROC_FS */
+#endif /* _LINUX_PROC_FS_H */
diff --git a/pfinet/linux-src/include/linux/ps2esdi.h b/pfinet/linux-src/include/linux/ps2esdi.h
new file mode 100644
index 00000000..c0e050b1
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ps2esdi.h
@@ -0,0 +1,98 @@
+#ifndef _PS2ESDI_H_
+#define _PS2ESDI_H_
+
+#define NRML_ESDI_ID 0xddff
+#define INTG_ESDI_ID 0xdf9f
+
+#define PRIMARY_IO_BASE 0x3510
+#define ALT_IO_BASE 0x3518
+
+#define ESDI_CMD_INT (io_base+0)
+#define ESDI_STT_INT (io_base+0)
+#define ESDI_CONTROL (io_base+2)
+#define ESDI_STATUS (io_base+2)
+#define ESDI_ATTN (io_base+3)
+#define ESDI_INTRPT (io_base+3)
+
+#define STATUS_ENABLED 0x01
+#define STATUS_ALTERNATE 0x02
+#define STATUS_BUSY 0x10
+#define STATUS_STAT_AVAIL 0x08
+#define STATUS_INTR 0x01
+#define STATUS_RESET_FAIL 0xea
+#define STATUS_CMD_INF 0x04
+
+#define CTRL_SOFT_RESET 0xe4
+#define CTRL_HARD_RESET 0x80
+#define CTRL_EOI 0xe2
+#define CTRL_ENABLE_DMA 0x02
+#define CTRL_ENABLE_INTR 0x01
+#define CTRL_DISABLE_INTR 0x00
+
+#define ATT_EOI 0x02
+
+/* bits of word 0 of configuration status block. more info see p.38 of tech ref */
+#define CONFIG_IS 0x10 /* Invalid Secondary */
+#define CONFIG_ZD 0x08 /* Zero Defect */
+#define CONFIG_SF 0x04 /* Skewed Format */
+#define CONFIG_FR 0x02 /* Removable */
+#define CONFIG_RT 0x01 /* Retries */
+
+#define PORT_SYS_A 0x92
+#define PORT_DMA_FN 0x18
+#define PORT_DMA_EX 0x1a
+
+#define ON (unsigned char)0x40
+#define OFF (unsigned char)~ON
+#define LITE_ON outb(inb(PORT_SYS_A) | ON,PORT_SYS_A)
+#define LITE_OFF outb((inb(PORT_SYS_A) & OFF),PORT_SYS_A)
+
+#define FAIL 0
+#define SUCCES 1
+
+#define INT_CMD_COMPLETE 0x01
+#define INT_CMD_ECC 0x03
+#define INT_CMD_RETRY 0x05
+#define INT_CMD_FORMAT 0x06
+#define INT_CMD_ECC_RETRY 0x07
+#define INT_CMD_WARNING 0x08
+#define INT_CMD_ABORT 0x09
+#define INT_RESET 0x0A
+#define INT_TRANSFER_REQ 0x0B
+#define INT_CMD_FAILED 0x0C
+#define INT_DMA_ERR 0x0D
+#define INT_CMD_BLK_ERR 0x0E
+#define INT_ATTN_ERROR 0x0F
+
+#define DMA_MASK_CHAN 0x90
+#define DMA_UNMASK_CHAN 0xA0
+#define DMA_WRITE_ADDR 0x20
+#define DMA_WRITE_TC 0x40
+#define DMA_WRITE_MODE 0x70
+
+#define CMD_GET_DEV_CONFIG 0x09
+#define CMD_READ 0x4601
+#define CMD_WRITE 0x4602
+#define DMA_READ_16 0x4C
+#define DMA_WRITE_16 0x44
+
+
+#define MB 1024*1024
+#define SECT_SIZE 512
+
+#define ERROR 1
+#define OK 0
+
+#define HDIO_GETGEO 0x0301
+
+#define FALSE 0
+#define TRUE !FALSE
+
+struct ps2esdi_geometry {
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned short cylinders;
+ unsigned long start;
+};
+
+#endif /* _PS2ESDI_H_ */
diff --git a/pfinet/linux-src/include/linux/ptrace.h b/pfinet/linux-src/include/linux/ptrace.h
new file mode 100644
index 00000000..0a02879d
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ptrace.h
@@ -0,0 +1,26 @@
+#ifndef _LINUX_PTRACE_H
+#define _LINUX_PTRACE_H
+/* ptrace.h */
+/* structs and defines to help the user use the ptrace system call. */
+
+/* has the defines to get at the registers. */
+
+#define PTRACE_TRACEME 0
+#define PTRACE_PEEKTEXT 1
+#define PTRACE_PEEKDATA 2
+#define PTRACE_PEEKUSR 3
+#define PTRACE_POKETEXT 4
+#define PTRACE_POKEDATA 5
+#define PTRACE_POKEUSR 6
+#define PTRACE_CONT 7
+#define PTRACE_KILL 8
+#define PTRACE_SINGLESTEP 9
+
+#define PTRACE_ATTACH 0x10
+#define PTRACE_DETACH 0x11
+
+#define PTRACE_SYSCALL 24
+
+#include <asm/ptrace.h>
+
+#endif
diff --git a/pfinet/linux-src/include/linux/qic117.h b/pfinet/linux-src/include/linux/qic117.h
new file mode 100644
index 00000000..07b537e5
--- /dev/null
+++ b/pfinet/linux-src/include/linux/qic117.h
@@ -0,0 +1,290 @@
+#ifndef _QIC117_H
+#define _QIC117_H
+
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ * (C) 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/include/linux/qic117.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:32 $
+ *
+ * This file contains QIC-117 spec. related definitions for the
+ * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ *
+ * These data were taken from the Quarter-Inch Cartridge
+ * Drive Standards, Inc. document titled:
+ * `Common Command Set Interface Specification for Flexible
+ * Disk Controller Based Minicartridge Tape Drives'
+ * document QIC-117 Revision J, 28 Aug 96.
+ * For more information, contact:
+ * Quarter-Inch Cartridge Drive Standards, Inc.
+ * 311 East Carrillo Street
+ * Santa Barbara, California 93101
+ * Telephone (805) 963-3853
+ * Fax (805) 962-1541
+ * WWW http://www.qic.org
+ *
+ * Current QIC standard revisions (of interest) are:
+ * QIC-40-MC, Rev. M, 2 Sep 92.
+ * QIC-80-MC, Rev. N, 20 Mar 96.
+ * QIC-80-MC, Rev. K, 15 Dec 94.
+ * QIC-113, Rev. G, 15 Jun 95.
+ * QIC-117, Rev. J, 28 Aug 96.
+ * QIC-122, Rev. B, 6 Mar 91.
+ * QIC-130, Rev. C, 2 Sep 92.
+ * QIC-3010-MC, Rev. F, 14 Jun 95.
+ * QIC-3020-MC, Rev. G, 31 Aug 95.
+ * QIC-CRF3, Rev. B, 15 Jun 95.
+ * */
+
+/*
+ * QIC-117 common command set rev. J.
+ * These commands are sent to the tape unit
+ * as number of pulses over the step line.
+ */
+
+typedef enum {
+ QIC_NO_COMMAND = 0,
+ QIC_RESET = 1,
+ QIC_REPORT_NEXT_BIT = 2,
+ QIC_PAUSE = 3,
+ QIC_MICRO_STEP_PAUSE = 4,
+ QIC_ALTERNATE_TIMEOUT = 5,
+ QIC_REPORT_DRIVE_STATUS = 6,
+ QIC_REPORT_ERROR_CODE = 7,
+ QIC_REPORT_DRIVE_CONFIGURATION = 8,
+ QIC_REPORT_ROM_VERSION = 9,
+ QIC_LOGICAL_FORWARD = 10,
+ QIC_PHYSICAL_REVERSE = 11,
+ QIC_PHYSICAL_FORWARD = 12,
+ QIC_SEEK_HEAD_TO_TRACK = 13,
+ QIC_SEEK_LOAD_POINT = 14,
+ QIC_ENTER_FORMAT_MODE = 15,
+ QIC_WRITE_REFERENCE_BURST = 16,
+ QIC_ENTER_VERIFY_MODE = 17,
+ QIC_STOP_TAPE = 18,
+/* commands 19-20: reserved */
+ QIC_MICRO_STEP_HEAD_UP = 21,
+ QIC_MICRO_STEP_HEAD_DOWN = 22,
+ QIC_SOFT_SELECT = 23,
+ QIC_SOFT_DESELECT = 24,
+ QIC_SKIP_REVERSE = 25,
+ QIC_SKIP_FORWARD = 26,
+ QIC_SELECT_RATE = 27,
+/* command 27, in ccs2: Select Rate or Format */
+ QIC_ENTER_DIAGNOSTIC_1 = 28,
+ QIC_ENTER_DIAGNOSTIC_2 = 29,
+ QIC_ENTER_PRIMARY_MODE = 30,
+/* command 31: vendor unique */
+ QIC_REPORT_VENDOR_ID = 32,
+ QIC_REPORT_TAPE_STATUS = 33,
+ QIC_SKIP_EXTENDED_REVERSE = 34,
+ QIC_SKIP_EXTENDED_FORWARD = 35,
+ QIC_CALIBRATE_TAPE_LENGTH = 36,
+ QIC_REPORT_FORMAT_SEGMENTS = 37,
+ QIC_SET_FORMAT_SEGMENTS = 38,
+/* commands 39-45: reserved */
+ QIC_PHANTOM_SELECT = 46,
+ QIC_PHANTOM_DESELECT = 47
+} qic117_cmd_t;
+
+typedef enum {
+ discretional = 0, required, ccs1, ccs2
+} qic_compatibility;
+
+typedef enum {
+ unused, mode, motion, report
+} command_types;
+
+struct qic117_command_table {
+ char *name;
+ __u8 mask;
+ __u8 state;
+ __u8 cmd_type;
+ __u8 non_intr;
+ __u8 level;
+};
+
+#define QIC117_COMMANDS {\
+/* command mask state cmd_type */\
+/* | name | | | non_intr */\
+/* | | | | | | level */\
+/* 0*/ {NULL, 0x00, 0x00, mode, 0, discretional},\
+/* 1*/ {"soft reset", 0x00, 0x00, motion, 1, required},\
+/* 2*/ {"report next bit", 0x00, 0x00, report, 0, required},\
+/* 3*/ {"pause", 0x36, 0x24, motion, 1, required},\
+/* 4*/ {"micro step pause", 0x36, 0x24, motion, 1, required},\
+/* 5*/ {"alternate command timeout", 0x00, 0x00, mode, 0, required},\
+/* 6*/ {"report drive status", 0x00, 0x00, report, 0, required},\
+/* 7*/ {"report error code", 0x01, 0x01, report, 0, required},\
+/* 8*/ {"report drive configuration",0x00, 0x00, report, 0, required},\
+/* 9*/ {"report rom version", 0x00, 0x00, report, 0, required},\
+/*10*/ {"logical forward", 0x37, 0x25, motion, 0, required},\
+/*11*/ {"physical reverse", 0x17, 0x05, motion, 0, required},\
+/*12*/ {"physical forward", 0x17, 0x05, motion, 0, required},\
+/*13*/ {"seek head to track", 0x37, 0x25, motion, 0, required},\
+/*14*/ {"seek load point", 0x17, 0x05, motion, 1, required},\
+/*15*/ {"enter format mode", 0x1f, 0x05, mode, 0, required},\
+/*16*/ {"write reference burst", 0x1f, 0x05, motion, 1, required},\
+/*17*/ {"enter verify mode", 0x37, 0x25, mode, 0, required},\
+/*18*/ {"stop tape", 0x00, 0x00, motion, 1, required},\
+/*19*/ {"reserved (19)", 0x00, 0x00, unused, 0, discretional},\
+/*20*/ {"reserved (20)", 0x00, 0x00, unused, 0, discretional},\
+/*21*/ {"micro step head up", 0x02, 0x00, motion, 0, required},\
+/*22*/ {"micro step head down", 0x02, 0x00, motion, 0, required},\
+/*23*/ {"soft select", 0x00, 0x00, mode, 0, discretional},\
+/*24*/ {"soft deselect", 0x00, 0x00, mode, 0, discretional},\
+/*25*/ {"skip segments reverse", 0x36, 0x24, motion, 1, required},\
+/*26*/ {"skip segments forward", 0x36, 0x24, motion, 1, required},\
+/*27*/ {"select rate or format", 0x03, 0x01, mode, 0, required /* [ccs2] */},\
+/*28*/ {"enter diag mode 1", 0x00, 0x00, mode, 0, discretional},\
+/*29*/ {"enter diag mode 2", 0x00, 0x00, mode, 0, discretional},\
+/*30*/ {"enter primary mode", 0x00, 0x00, mode, 0, required},\
+/*31*/ {"vendor unique (31)", 0x00, 0x00, unused, 0, discretional},\
+/*32*/ {"report vendor id", 0x00, 0x00, report, 0, required},\
+/*33*/ {"report tape status", 0x04, 0x04, report, 0, ccs1},\
+/*34*/ {"skip extended reverse", 0x36, 0x24, motion, 1, ccs1},\
+/*35*/ {"skip extended forward", 0x36, 0x24, motion, 1, ccs1},\
+/*36*/ {"calibrate tape length", 0x17, 0x05, motion, 1, ccs2},\
+/*37*/ {"report format segments", 0x17, 0x05, report, 0, ccs2},\
+/*38*/ {"set format segments", 0x17, 0x05, mode, 0, ccs2},\
+/*39*/ {"reserved (39)", 0x00, 0x00, unused, 0, discretional},\
+/*40*/ {"vendor unique (40)", 0x00, 0x00, unused, 0, discretional},\
+/*41*/ {"vendor unique (41)", 0x00, 0x00, unused, 0, discretional},\
+/*42*/ {"vendor unique (42)", 0x00, 0x00, unused, 0, discretional},\
+/*43*/ {"vendor unique (43)", 0x00, 0x00, unused, 0, discretional},\
+/*44*/ {"vendor unique (44)", 0x00, 0x00, unused, 0, discretional},\
+/*45*/ {"vendor unique (45)", 0x00, 0x00, unused, 0, discretional},\
+/*46*/ {"phantom select", 0x00, 0x00, mode, 0, discretional},\
+/*47*/ {"phantom deselect", 0x00, 0x00, mode, 0, discretional},\
+}
+
+/*
+ * Status bits returned by QIC_REPORT_DRIVE_STATUS
+ */
+
+#define QIC_STATUS_READY 0x01 /* Drive is ready or idle. */
+#define QIC_STATUS_ERROR 0x02 /* Error detected, must read
+ error code to clear this */
+#define QIC_STATUS_CARTRIDGE_PRESENT 0x04 /* Tape is present */
+#define QIC_STATUS_WRITE_PROTECT 0x08 /* Tape is write protected */
+#define QIC_STATUS_NEW_CARTRIDGE 0x10 /* New cartridge inserted, must
+ read error status to clear. */
+#define QIC_STATUS_REFERENCED 0x20 /* Cartridge appears to have been
+ formatted. */
+#define QIC_STATUS_AT_BOT 0x40 /* Cartridge is at physical
+ beginning of tape. */
+#define QIC_STATUS_AT_EOT 0x80 /* Cartridge is at physical end
+ of tape. */
+/*
+ * Status bits returned by QIC_REPORT_DRIVE_CONFIGURATION
+ */
+
+#define QIC_CONFIG_RATE_MASK 0x18
+#define QIC_CONFIG_RATE_SHIFT 3
+#define QIC_CONFIG_RATE_250 0
+#define QIC_CONFIG_RATE_500 2
+#define QIC_CONFIG_RATE_1000 3
+#define QIC_CONFIG_RATE_2000 1
+#define QIC_CONFIG_RATE_4000 0 /* since QIC-117 Rev. J */
+
+#define QIC_CONFIG_LONG 0x40 /* Extra Length Tape Detected */
+#define QIC_CONFIG_80 0x80 /* QIC-80 detected. */
+
+/*
+ * Status bits returned by QIC_REPORT_TAPE_STATUS
+ */
+
+#define QIC_TAPE_STD_MASK 0x0f
+#define QIC_TAPE_QIC40 0x01
+#define QIC_TAPE_QIC80 0x02
+#define QIC_TAPE_QIC3020 0x03
+#define QIC_TAPE_QIC3010 0x04
+
+#define QIC_TAPE_LEN_MASK 0x70
+#define QIC_TAPE_205FT 0x10
+#define QIC_TAPE_307FT 0x20
+#define QIC_TAPE_VARIABLE 0x30
+#define QIC_TAPE_1100FT 0x40
+#define QIC_TAPE_FLEX 0x60
+
+#define QIC_TAPE_WIDE 0x80
+
+/* Define a value (in feet) slightly higher than
+ * the possible maximum tape length.
+ */
+#define QIC_TOP_TAPE_LEN 1500
+
+/*
+ * Errors: List of error codes, and their severity.
+ */
+
+typedef struct {
+ char *message; /* Text describing the error. */
+ unsigned int fatal:1; /* Non-zero if the error is fatal. */
+} ftape_error;
+
+#define QIC117_ERRORS {\
+ /* 0*/ { "No error", 0, },\
+ /* 1*/ { "Command Received while Drive Not Ready", 0, },\
+ /* 2*/ { "Cartridge Not Present or Removed", 1, },\
+ /* 3*/ { "Motor Speed Error (not within 1%)", 1, },\
+ /* 4*/ { "Motor Speed Fault (jammed, or gross speed error", 1, },\
+ /* 5*/ { "Cartridge Write Protected", 1, },\
+ /* 6*/ { "Undefined or Reserved Command Code", 1, },\
+ /* 7*/ { "Illegal Track Address Specified for Seek", 1, },\
+ /* 8*/ { "Illegal Command in Report Subcontext", 0, },\
+ /* 9*/ { "Illegal Entry into a Diagnostic Mode", 1, },\
+ /*10*/ { "Broken Tape Detected (based on hole sensor)", 1, },\
+ /*11*/ { "Warning--Read Gain Setting Error", 1, },\
+ /*12*/ { "Command Received While Error Status Pending (obs)", 1, },\
+ /*13*/ { "Command Received While New Cartridge Pending", 1, },\
+ /*14*/ { "Command Illegal or Undefined in Primary Mode", 1, },\
+ /*15*/ { "Command Illegal or Undefined in Format Mode", 1, },\
+ /*16*/ { "Command Illegal or Undefined in Verify Mode", 1, },\
+ /*17*/ { "Logical Forward Not at Logical BOT or no Format Segments in Format Mode", 1, },\
+ /*18*/ { "Logical EOT Before All Segments generated", 1, },\
+ /*19*/ { "Command Illegal When Cartridge Not Referenced", 1, },\
+ /*20*/ { "Self-Diagnostic Failed (cannot be cleared)", 1, },\
+ /*21*/ { "Warning EEPROM Not Initialized, Defaults Set", 1, },\
+ /*22*/ { "EEPROM Corrupted or Hardware Failure", 1, },\
+ /*23*/ { "Motion Time-out Error", 1, },\
+ /*24*/ { "Data Segment Too Long -- Logical Forward or Pause", 1, },\
+ /*25*/ { "Transmit Overrun (obs)", 1, },\
+ /*26*/ { "Power On Reset Occurred", 0, },\
+ /*27*/ { "Software Reset Occurred", 0, },\
+ /*28*/ { "Diagnostic Mode 1 Error", 1, },\
+ /*29*/ { "Diagnostic Mode 2 Error", 1, },\
+ /*30*/ { "Command Received During Non-Interruptible Process", 1, },\
+ /*31*/ { "Rate or Format Selection Error", 1, },\
+ /*32*/ { "Illegal Command While in High Speed Mode", 1, },\
+ /*33*/ { "Illegal Seek Segment Value", 1, },\
+ /*34*/ { "Invalid Media", 1, },\
+ /*35*/ { "Head Positioning Failure", 1, },\
+ /*36*/ { "Write Reference Burst Failure", 1, },\
+ /*37*/ { "Prom Code Missing", 1, },\
+ /*38*/ { "Invalid Format", 1, },\
+ /*39*/ { "EOT/BOT System Failure", 1, },\
+ /*40*/ { "Prom A Checksum Error", 1, },\
+ /*41*/ { "Drive Wakeup Reset Occurred", 1, },\
+ /*42*/ { "Prom B Checksum Error", 1, },\
+ /*43*/ { "Illegal Entry into Format Mode", 1, },\
+}
+
+#endif /* _QIC117_H */
diff --git a/pfinet/linux-src/include/linux/qnx4_fs.h b/pfinet/linux-src/include/linux/qnx4_fs.h
new file mode 100644
index 00000000..c5a01039
--- /dev/null
+++ b/pfinet/linux-src/include/linux/qnx4_fs.h
@@ -0,0 +1,123 @@
+/*
+ * Name : qnx4_fs.h
+ * Author : Richard Frowijn
+ * Function : qnx4 global filesystem definitions
+ * Version : 1.0
+ * Last modified : 23-03-1998
+ *
+ * History : 23-03-1998 created
+ */
+#ifndef _LINUX_QNX4_FS_H
+#define _LINUX_QNX4_FS_H
+
+#include <linux/qnxtypes.h>
+
+#define QNX4_ROOT_INO 1
+
+#define _MAX_XTNTS_PER_XBLK 60
+/* for di_status */
+#define QNX4_FILE_USED 0x01
+#define QNX4_FILE_MODIFIED 0x02
+#define QNX4_FILE_BUSY 0x04
+#define QNX4_FILE_LINK 0x08
+#define QNX4_FILE_INODE 0x10
+#define QNX4_FILE_FSYSCLEAN 0x20
+
+#define QNX4_I_MAP_SLOTS 8
+#define QNX4_Z_MAP_SLOTS 64
+#define QNX4_SUPER_MAGIC 0x002f /* qnx4 fs detection */
+#define QNX4_VALID_FS 0x0001 /* Clean fs. */
+#define QNX4_ERROR_FS 0x0002 /* fs has errors. */
+#define QNX4_BLOCK_SIZE 0x200 /* blocksize of 512 bytes */
+#define QNX4_DIR_ENTRY_SIZE 0x040 /* dir entry size */
+#define QNX4_XBLK_ENTRY_SIZE 0x200 /* xblk entry size */
+#define QNX4_INODES_PER_BLOCK 0x08 /* 512 / 64 */
+
+/* for filenames */
+#define _SHORT_NAME_MAX 16
+#define QNX4_NAME_MAX 48
+
+/*
+ * This is the original qnx4 inode layout on disk.
+ */
+struct qnx4_inode_entry {
+ char di_fname[16];
+ off_t di_size;
+ _xtnt_t di_first_xtnt;
+ long di_xblk;
+ time_t di_ftime;
+ time_t di_mtime;
+ time_t di_atime;
+ time_t di_ctime;
+ _nxtnt_t di_num_xtnts;
+ mode_t di_mode;
+ muid_t di_uid;
+ mgid_t di_gid;
+ nlink_t di_nlink;
+ char di_zero[4];
+ _ftype_t di_type;
+ unsigned char di_status;
+};
+
+struct qnx4_link_info {
+ char dl_fname[QNX4_NAME_MAX];
+ long dl_inode_blk;
+ unsigned char dl_inode_ndx;
+ unsigned char dl_spare[10];
+ unsigned char dl_status;
+};
+
+struct qnx4_xblk {
+ long xblk_next_xblk;
+ long xblk_prev_xblk;
+ unsigned char xblk_num_xtnts;
+ char xblk_spare[3];
+ long xblk_num_blocks;
+ _xtnt_t xblk_xnts[_MAX_XTNTS_PER_XBLK];
+ char xblk_signature[8];
+ _xtnt_t xblk_first_xtnt;
+};
+
+struct qnx4_super_block {
+ struct qnx4_inode_entry RootDir;
+ struct qnx4_inode_entry Inode;
+ struct qnx4_inode_entry Boot;
+ struct qnx4_inode_entry AltBoot;
+};
+
+#ifdef __KERNEL__
+
+#define QNX4_DEBUG 0
+
+#if QNX4_DEBUG
+#define QNX4DEBUG(X) printk X
+#else
+#define QNX4DEBUG(X) (void) 0
+#endif
+
+extern struct dentry *qnx4_lookup(struct inode *dir, struct dentry *dentry);
+extern unsigned long qnx4_count_free_inodes(struct super_block *sb);
+extern unsigned long qnx4_count_free_blocks(struct super_block *sb);
+
+extern struct buffer_head *qnx4_getblk(struct inode *, int, int);
+extern struct buffer_head *qnx4_bread(struct inode *, int, int);
+
+extern int init_qnx4_fs(void);
+extern int qnx4_create(struct inode *dir, struct dentry *dentry, int mode);
+extern struct inode_operations qnx4_file_inode_operations;
+extern struct inode_operations qnx4_dir_inode_operations;
+extern struct inode_operations qnx4_symlink_inode_operations;
+extern int qnx4_is_free(struct super_block *sb, int block);
+extern int qnx4_set_bitmap(struct super_block *sb, int block, int busy);
+extern int qnx4_create(struct inode *inode, struct dentry *dentry, int mode);
+extern void qnx4_truncate(struct inode *inode);
+extern void qnx4_free_inode(struct inode *inode);
+extern int qnx4_unlink(struct inode *dir, struct dentry *dentry);
+extern int qnx4_rmdir(struct inode *dir, struct dentry *dentry);
+extern int qnx4_sync_file(struct file *file, struct dentry *dentry);
+extern int qnx4_sync_inode(struct inode *inode);
+extern int qnx4_bmap(struct inode *inode, int block);
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/qnx4_fs_i.h b/pfinet/linux-src/include/linux/qnx4_fs_i.h
new file mode 100644
index 00000000..83d53e52
--- /dev/null
+++ b/pfinet/linux-src/include/linux/qnx4_fs_i.h
@@ -0,0 +1,38 @@
+/*
+ * Name : qnx4_fs_i.h
+ * Author : Richard Frowijn
+ * Function : qnx4 inode definitions
+ * Version : 1.0
+ * Last modified : 25-05-1998
+ *
+ * History : 23-03-1998 created
+ *
+ */
+#ifndef _QNX4_FS_I
+#define _QNX4_FS_I
+
+#include <linux/qnxtypes.h>
+
+/*
+ * qnx4 fs inode entry
+ */
+struct qnx4_inode_info {
+ char i_reserved[16]; /* 16 */
+ off_t i_size; /* 4 */
+ _xtnt_t i_first_xtnt; /* 8 */
+ long i_xblk; /* 4 */
+ time_t i_ftime; /* 4 */
+ time_t i_mtime; /* 4 */
+ time_t i_atime; /* 4 */
+ time_t i_ctime; /* 4 */
+ _nxtnt_t i_num_xtnts; /* 2 */
+ mode_t i_mode; /* 2 */
+ muid_t i_uid; /* 2 */
+ mgid_t i_gid; /* 2 */
+ nlink_t i_nlink; /* 2 */
+ char i_zero[4]; /* 4 */
+ _ftype_t i_type; /* 1 */
+ unsigned char i_status; /* 1 */
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/qnx4_fs_sb.h b/pfinet/linux-src/include/linux/qnx4_fs_sb.h
new file mode 100644
index 00000000..9f28d3cb
--- /dev/null
+++ b/pfinet/linux-src/include/linux/qnx4_fs_sb.h
@@ -0,0 +1,27 @@
+/*
+ * Name : qnx4_fs_sb.h
+ * Author : Richard Frowijn
+ * Function : qnx4 superblock definitions
+ * Version : 1.0
+ * Last modified : 20-05-1998
+ *
+ * History : 23-03-1998 created
+ *
+ */
+#ifndef _QNX4_FS_SB
+#define _QNX4_FS_SB
+
+#include <linux/qnxtypes.h>
+
+/*
+ * qnx4 super-block data in memory
+ */
+
+struct qnx4_sb_info {
+ struct buffer_head *sb_buf; /* superblock buffer */
+ struct qnx4_super_block *sb; /* our superblock */
+ unsigned int Version; /* may be useful */
+ struct qnx4_inode_entry *BitMap; /* useful */
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/qnxtypes.h b/pfinet/linux-src/include/linux/qnxtypes.h
new file mode 100644
index 00000000..054da0d1
--- /dev/null
+++ b/pfinet/linux-src/include/linux/qnxtypes.h
@@ -0,0 +1,28 @@
+/*
+ * Name : qnxtypes.h
+ * Author : Richard Frowijn
+ * Function : standard qnx types
+ * Version : 1.0
+ * Last modified : 22-03-1998
+ *
+ * History : 22-03-1998 created
+ *
+ */
+
+#ifndef _QNX4TYPES_H
+#define _QNX4TYPES_H
+
+typedef unsigned short _nxtnt_t;
+typedef unsigned char _ftype_t;
+
+typedef struct {
+ long xtnt_blk;
+ long xtnt_size;
+} _xtnt_t;
+
+typedef unsigned short muid_t;
+typedef unsigned short mgid_t;
+typedef unsigned long qnx_off_t;
+typedef unsigned short qnx_nlink_t;
+
+#endif
diff --git a/pfinet/linux-src/include/linux/quota.h b/pfinet/linux-src/include/linux/quota.h
new file mode 100644
index 00000000..2c4a5bce
--- /dev/null
+++ b/pfinet/linux-src/include/linux/quota.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Version: $Id: quota.h,v 2.0 1996/11/17 16:48:14 mvw Exp mvw $
+ */
+
+#ifndef _LINUX_QUOTA_
+#define _LINUX_QUOTA_
+
+#include <linux/errno.h>
+
+/*
+ * Convert diskblocks to blocks and the other way around.
+ */
+#define dbtob(num) (num << 10)
+#define btodb(num) (num >> 10)
+
+/*
+ * Convert count of filesystem blocks to diskquota blocks, meant
+ * for filesystems where i_blksize != BLOCK_SIZE
+ */
+#define fs_to_dq_blocks(num, blksize) (((num) * (blksize)) / BLOCK_SIZE)
+
+/*
+ * Definitions for disk quotas imposed on the average user
+ * (big brother finally hits Linux).
+ *
+ * The following constants define the amount of time given a user
+ * before the soft limits are treated as hard limits (usually resulting
+ * in an allocation failure). The timer is started when the user crosses
+ * their soft limit, it is reset when they go below their soft limit.
+ */
+#define MAX_IQ_TIME 604800 /* (7*24*60*60) 1 week */
+#define MAX_DQ_TIME 604800 /* (7*24*60*60) 1 week */
+
+#define MAXQUOTAS 2
+#define USRQUOTA 0 /* element used for user quotas */
+#define GRPQUOTA 1 /* element used for group quotas */
+
+/*
+ * Definitions for the default names of the quotas files.
+ */
+#define INITQFNAMES { \
+ "user", /* USRQUOTA */ \
+ "group", /* GRPQUOTA */ \
+ "undefined", \
+};
+
+#define QUOTAFILENAME "quota"
+#define QUOTAGROUP "staff"
+
+extern int nr_dquots, nr_free_dquots;
+extern int max_dquots;
+extern int dquot_root_squash;
+
+#define NR_DQHASH 43 /* Just an arbitrary number */
+#define NR_DQUOTS 1024 /* Maximum number of quotas active at one time (Configurable from /proc/sys/fs) */
+
+/*
+ * Command definitions for the 'quotactl' system call.
+ * The commands are broken into a main command defined below
+ * and a subcommand that is used to convey the type of
+ * quota that is being manipulated (see above).
+ */
+#define SUBCMDMASK 0x00ff
+#define SUBCMDSHIFT 8
+#define QCMD(cmd, type) (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK))
+
+#define Q_QUOTAON 0x0100 /* enable quotas */
+#define Q_QUOTAOFF 0x0200 /* disable quotas */
+#define Q_GETQUOTA 0x0300 /* get limits and usage */
+#define Q_SETQUOTA 0x0400 /* set limits and usage */
+#define Q_SETUSE 0x0500 /* set usage */
+#define Q_SYNC 0x0600 /* sync disk copy of a filesystems quotas */
+#define Q_SETQLIM 0x0700 /* set limits */
+#define Q_GETSTATS 0x0800 /* get collected stats */
+#define Q_RSQUASH 0x1000 /* set root_squash option */
+
+/*
+ * The following structure defines the format of the disk quota file
+ * (as it appears on disk) - the file is an array of these structures
+ * indexed by user or group number.
+ */
+struct dqblk {
+ __u32 dqb_bhardlimit; /* absolute limit on disk blks alloc */
+ __u32 dqb_bsoftlimit; /* preferred limit on disk blks */
+ __u32 dqb_curblocks; /* current block count */
+ __u32 dqb_ihardlimit; /* absolute limit on allocated inodes */
+ __u32 dqb_isoftlimit; /* preferred inode limit */
+ __u32 dqb_curinodes; /* current # allocated inodes */
+ time_t dqb_btime; /* time limit for excessive disk use */
+ time_t dqb_itime; /* time limit for excessive inode use */
+};
+
+/*
+ * Shorthand notation.
+ */
+#define dq_bhardlimit dq_dqb.dqb_bhardlimit
+#define dq_bsoftlimit dq_dqb.dqb_bsoftlimit
+#define dq_curblocks dq_dqb.dqb_curblocks
+#define dq_ihardlimit dq_dqb.dqb_ihardlimit
+#define dq_isoftlimit dq_dqb.dqb_isoftlimit
+#define dq_curinodes dq_dqb.dqb_curinodes
+#define dq_btime dq_dqb.dqb_btime
+#define dq_itime dq_dqb.dqb_itime
+
+#define dqoff(UID) ((loff_t)((UID) * sizeof (struct dqblk)))
+
+struct dqstats {
+ __u32 lookups;
+ __u32 drops;
+ __u32 reads;
+ __u32 writes;
+ __u32 cache_hits;
+ __u32 allocated_dquots;
+ __u32 free_dquots;
+ __u32 syncs;
+};
+
+#ifdef __KERNEL__
+
+/*
+ * Maximum length of a message generated in the quota system,
+ * that needs to be kicked onto the tty.
+ */
+#define MAX_QUOTA_MESSAGE 75
+
+#define DQ_LOCKED 0x01 /* locked for update */
+#define DQ_WANT 0x02 /* wanted for update */
+#define DQ_MOD 0x04 /* dquot modified since read */
+#define DQ_BLKS 0x10 /* uid/gid has been warned about blk limit */
+#define DQ_INODES 0x20 /* uid/gid has been warned about inode limit */
+#define DQ_FAKE 0x40 /* no limits only usage */
+
+struct dquot {
+ struct dquot *dq_next; /* Pointer to next dquot */
+ struct dquot **dq_pprev;
+ struct list_head dq_free; /* free list element */
+ struct dquot *dq_hash_next; /* Pointer to next in dquot_hash */
+ struct dquot **dq_hash_pprev; /* Pointer to previous in dquot_hash */
+ struct wait_queue *dq_wait; /* Pointer to waitqueue */
+ int dq_count; /* Reference count */
+
+ /* fields after this point are cleared when invalidating */
+ struct vfsmount *dq_mnt; /* VFS_mount_point this applies to */
+ unsigned int dq_id; /* ID this applies to (uid, gid) */
+ kdev_t dq_dev; /* Device this applies to */
+ short dq_type; /* Type of quota */
+ short dq_flags; /* See DQ_* */
+ unsigned long dq_referenced; /* Number of times this dquot was
+ referenced during its lifetime */
+ struct dqblk dq_dqb; /* Diskquota usage */
+};
+
+#define NODQUOT (struct dquot *)NULL
+
+/*
+ * Flags used for set_dqblk.
+ */
+#define QUOTA_SYSCALL 0x01
+#define SET_QUOTA 0x02
+#define SET_USE 0x04
+#define SET_QLIMIT 0x08
+
+#define QUOTA_OK 0
+#define NO_QUOTA 1
+
+#else
+
+# /* nodep */ include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int quotactl __P ((int, const char *, int, caddr_t));
+__END_DECLS
+
+#endif /* __KERNEL__ */
+#endif /* _QUOTA_ */
diff --git a/pfinet/linux-src/include/linux/quotaops.h b/pfinet/linux-src/include/linux/quotaops.h
new file mode 100644
index 00000000..bb76e715
--- /dev/null
+++ b/pfinet/linux-src/include/linux/quotaops.h
@@ -0,0 +1,134 @@
+/*
+ * Definitions for diskquota-operations. When diskquota is configured these
+ * macros expand to the right source-code.
+ *
+ * Author: Marco van Wieringen <mvw@planets.elm.net>
+ *
+ * Version: $Id: quotaops.h,v 1.2 1998/01/15 16:22:26 ecd Exp $
+ *
+ */
+#ifndef _LINUX_QUOTAOPS_
+#define _LINUX_QUOTAOPS_
+
+#include <linux/config.h>
+
+#if defined(CONFIG_QUOTA)
+
+/*
+ * declaration of quota_function calls in kernel.
+ */
+extern void dquot_initialize(struct inode *inode, short type);
+extern void dquot_drop(struct inode *inode);
+extern void invalidate_dquots(kdev_t dev, short type);
+extern int quota_off(kdev_t dev, short type);
+extern int sync_dquots(kdev_t dev, short type);
+
+extern int dquot_alloc_block(const struct inode *inode, unsigned long number,
+ uid_t initiator, char warn);
+extern int dquot_alloc_inode(const struct inode *inode, unsigned long number,
+ uid_t initiator);
+
+extern void dquot_free_block(const struct inode *inode, unsigned long number);
+extern void dquot_free_inode(const struct inode *inode, unsigned long number);
+
+extern int dquot_transfer(struct dentry *dentry, struct iattr *iattr,
+ uid_t initiator);
+
+/*
+ * Operations supported for diskquotas.
+ */
+extern __inline__ void DQUOT_INIT(struct inode *inode)
+{
+ if (inode->i_sb && inode->i_sb->dq_op)
+ inode->i_sb->dq_op->initialize(inode, -1);
+}
+
+extern __inline__ void DQUOT_DROP(struct inode *inode)
+{
+ if (IS_QUOTAINIT(inode)) {
+ if (inode->i_sb && inode->i_sb->dq_op)
+ inode->i_sb->dq_op->drop(inode);
+ }
+}
+
+extern __inline__ int DQUOT_PREALLOC_BLOCK(struct super_block *sb, const struct inode *inode, int nr)
+{
+ if (sb->dq_op) {
+ if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, sb->s_blocksize),
+ current->fsuid, 0) == NO_QUOTA)
+ return 1;
+ }
+ return 0;
+}
+
+extern __inline__ int DQUOT_ALLOC_BLOCK(struct super_block *sb, const struct inode *inode, int nr)
+{
+ if (sb->dq_op) {
+ if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, sb->s_blocksize),
+ current->fsuid, 1) == NO_QUOTA)
+ return 1;
+ }
+ return 0;
+}
+
+extern __inline__ int DQUOT_ALLOC_INODE(struct super_block *sb, struct inode *inode)
+{
+ if (sb->dq_op) {
+ sb->dq_op->initialize (inode, -1);
+ if (sb->dq_op->alloc_inode (inode, 1, current->fsuid))
+ return 1;
+ }
+ inode->i_flags |= S_QUOTA;
+ return 0;
+}
+
+extern __inline__ void DQUOT_FREE_BLOCK(struct super_block *sb, const struct inode *inode, int nr)
+{
+ if (sb->dq_op)
+ sb->dq_op->free_block(inode, fs_to_dq_blocks(nr, sb->s_blocksize));
+}
+
+extern __inline__ void DQUOT_FREE_INODE(struct super_block *sb, struct inode *inode)
+{
+ if (sb->dq_op)
+ sb->dq_op->free_inode(inode, 1);
+}
+
+extern __inline__ int DQUOT_TRANSFER(struct dentry *dentry, struct iattr *iattr)
+{
+ int error = -EDQUOT;
+
+ if (dentry->d_inode->i_sb->dq_op) {
+ dentry->d_inode->i_sb->dq_op->initialize(dentry->d_inode, -1);
+ error = dentry->d_inode->i_sb->dq_op->transfer(dentry, iattr, current->fsuid);
+ } else {
+ error = notify_change(dentry, iattr);
+ }
+ return error;
+}
+
+#define DQUOT_SYNC(dev) sync_dquots(dev, -1)
+#define DQUOT_OFF(dev) quota_off(dev, -1)
+
+#else
+
+/*
+ * NO-OP when quota not configured.
+ */
+#define DQUOT_INIT(inode) do { } while(0)
+#define DQUOT_DROP(inode) do { } while(0)
+#define DQUOT_PREALLOC_BLOCK(sb, inode, nr) (0)
+#define DQUOT_ALLOC_BLOCK(sb, inode, nr) (0)
+#define DQUOT_ALLOC_INODE(sb, inode) (0)
+#define DQUOT_FREE_BLOCK(sb, inode, nr) do { } while(0)
+#define DQUOT_FREE_INODE(sb, inode) do { } while(0)
+#define DQUOT_SYNC(dev) do { } while(0)
+#define DQUOT_OFF(dev) do { } while(0)
+
+/*
+ * Special case expands to a simple notify_change.
+ */
+#define DQUOT_TRANSFER(dentry, iattr) notify_change(dentry, iattr)
+
+#endif /* CONFIG_QUOTA */
+#endif /* _LINUX_QUOTAOPS_ */
diff --git a/pfinet/linux-src/include/linux/raid0.h b/pfinet/linux-src/include/linux/raid0.h
new file mode 100644
index 00000000..e1ae51c0
--- /dev/null
+++ b/pfinet/linux-src/include/linux/raid0.h
@@ -0,0 +1,27 @@
+#ifndef _RAID0_H
+#define _RAID0_H
+
+struct strip_zone
+{
+ int zone_offset; /* Zone offset in md_dev */
+ int dev_offset; /* Zone offset in real dev */
+ int size; /* Zone size */
+ int nb_dev; /* Number of devices attached to the zone */
+ struct real_dev *dev[MAX_REAL]; /* Devices attached to the zone */
+};
+
+struct raid0_hash
+{
+ struct strip_zone *zone0, *zone1;
+};
+
+struct raid0_data
+{
+ struct raid0_hash *hash_table; /* Dynamically allocated */
+ struct strip_zone *strip_zone; /* This one too */
+ int nr_strip_zones;
+ struct strip_zone *smallest;
+ int nr_zones;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/raid1.h b/pfinet/linux-src/include/linux/raid1.h
new file mode 100644
index 00000000..4b031e68
--- /dev/null
+++ b/pfinet/linux-src/include/linux/raid1.h
@@ -0,0 +1,49 @@
+#ifndef _RAID1_H
+#define _RAID1_H
+
+#include <linux/md.h>
+
+struct mirror_info {
+ int number;
+ int raid_disk;
+ kdev_t dev;
+ int next;
+ int sect_limit;
+
+ /*
+ * State bits:
+ */
+ int operational;
+ int write_only;
+ int spare;
+};
+
+struct raid1_data {
+ struct md_dev *mddev;
+ struct mirror_info mirrors[MD_SB_DISKS]; /* RAID1 devices, 2 to MD_SB_DISKS */
+ int raid_disks;
+ int working_disks; /* Number of working disks */
+ int last_used;
+ unsigned long next_sect;
+ int sect_count;
+ int resync_running;
+};
+
+/*
+ * this is our 'private' 'collective' RAID1 buffer head.
+ * it contains information about what kind of IO operations were started
+ * for this RAID5 operation, and about their status:
+ */
+
+struct raid1_bh {
+ unsigned int remaining;
+ int cmd;
+ unsigned long state;
+ struct md_dev *mddev;
+ struct buffer_head *master_bh;
+ struct buffer_head *mirror_bh [MD_SB_DISKS];
+ struct buffer_head bh_req;
+ struct buffer_head *next_retry;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/raid5.h b/pfinet/linux-src/include/linux/raid5.h
new file mode 100644
index 00000000..5efd211a
--- /dev/null
+++ b/pfinet/linux-src/include/linux/raid5.h
@@ -0,0 +1,110 @@
+#ifndef _RAID5_H
+#define _RAID5_H
+
+#ifdef __KERNEL__
+#include <linux/md.h>
+#include <asm/atomic.h>
+
+struct disk_info {
+ kdev_t dev;
+ int operational;
+ int number;
+ int raid_disk;
+ int write_only;
+ int spare;
+};
+
+struct stripe_head {
+ struct stripe_head *hash_next, **hash_pprev; /* hash pointers */
+ struct stripe_head *free_next; /* pool of free sh's */
+ struct buffer_head *buffer_pool; /* pool of free buffers */
+ struct buffer_head *bh_pool; /* pool of free bh's */
+ struct raid5_data *raid_conf;
+ struct buffer_head *bh_old[MD_SB_DISKS]; /* disk image */
+ struct buffer_head *bh_new[MD_SB_DISKS]; /* buffers of the MD device (present in buffer cache) */
+ struct buffer_head *bh_copy[MD_SB_DISKS]; /* copy on write of bh_new (bh_new can change from under us) */
+ struct buffer_head *bh_req[MD_SB_DISKS]; /* copy of bh_new (only the buffer heads), queued to the lower levels */
+ int cmd_new[MD_SB_DISKS]; /* READ/WRITE for new */
+ int new[MD_SB_DISKS]; /* buffer added since the last handle_stripe() */
+ unsigned long sector; /* sector of this row */
+ int size; /* buffers size */
+ int pd_idx; /* parity disk index */
+ int nr_pending; /* nr of pending cmds */
+ unsigned long state; /* state flags */
+ int cmd; /* stripe cmd */
+ int count; /* nr of waiters */
+ int write_method; /* reconstruct-write / read-modify-write */
+ int phase; /* PHASE_BEGIN, ..., PHASE_COMPLETE */
+ struct wait_queue *wait; /* processes waiting for this stripe */
+};
+
+/*
+ * Phase
+ */
+#define PHASE_BEGIN 0
+#define PHASE_READ_OLD 1
+#define PHASE_WRITE 2
+#define PHASE_READ 3
+#define PHASE_COMPLETE 4
+
+/*
+ * Write method
+ */
+#define METHOD_NONE 0
+#define RECONSTRUCT_WRITE 1
+#define READ_MODIFY_WRITE 2
+
+/*
+ * Stripe state
+ */
+#define STRIPE_LOCKED 0
+#define STRIPE_ERROR 1
+
+/*
+ * Stripe commands
+ */
+#define STRIPE_NONE 0
+#define STRIPE_WRITE 1
+#define STRIPE_READ 2
+
+struct raid5_data {
+ struct stripe_head **stripe_hashtbl;
+ struct md_dev *mddev;
+ struct md_thread *thread, *resync_thread;
+ struct disk_info disks[MD_SB_DISKS];
+ struct disk_info *spare;
+ int buffer_size;
+ int chunk_size, level, algorithm;
+ int raid_disks, working_disks, failed_disks;
+ int sector_count;
+ unsigned long next_sector;
+ atomic_t nr_handle;
+ struct stripe_head *next_free_stripe;
+ int nr_stripes;
+ int resync_parity;
+ int max_nr_stripes;
+ int clock;
+ int nr_hashed_stripes;
+ int nr_locked_stripes;
+ int nr_pending_stripes;
+ int nr_cached_stripes;
+
+ /*
+ * Free stripes pool
+ */
+ int nr_free_sh;
+ struct stripe_head *free_sh_list;
+ struct wait_queue *wait_for_stripe;
+};
+
+#endif
+
+/*
+ * Our supported algorithms
+ */
+#define ALGORITHM_LEFT_ASYMMETRIC 0
+#define ALGORITHM_RIGHT_ASYMMETRIC 1
+#define ALGORITHM_LEFT_SYMMETRIC 2
+#define ALGORITHM_RIGHT_SYMMETRIC 3
+
+#endif
diff --git a/pfinet/linux-src/include/linux/random.h b/pfinet/linux-src/include/linux/random.h
new file mode 100644
index 00000000..58c93b9b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/random.h
@@ -0,0 +1,73 @@
+/*
+ * include/linux/random.h
+ *
+ * Include file for the random number generator.
+ */
+
+#ifndef _LINUX_RANDOM_H
+#define _LINUX_RANDOM_H
+
+#include <linux/ioctl.h>
+
+/* ioctl()'s for the random number generator */
+
+/* Get the entropy count. */
+#define RNDGETENTCNT _IOR( 'R', 0x00, int )
+
+/* Add to (or subtract from) the entropy count. (Superuser only.) */
+#define RNDADDTOENTCNT _IOW( 'R', 0x01, int )
+
+/* Get the contents of the entropy pool. (Superuser only.) */
+#define RNDGETPOOL _IOR( 'R', 0x02, int [2] )
+
+/*
+ * Write bytes into the entropy pool and add to the entropy count.
+ * (Superuser only.)
+ */
+#define RNDADDENTROPY _IOW( 'R', 0x03, int [2] )
+
+/* Clear entropy count to 0. (Superuser only.) */
+#define RNDZAPENTCNT _IO( 'R', 0x04 )
+
+/* Clear the entropy pool and associated counters. (Superuser only.) */
+#define RNDCLEARPOOL _IO( 'R', 0x06 )
+
+struct rand_pool_info {
+ int entropy_count;
+ int buf_size;
+ __u32 buf[0];
+};
+
+/* Exported functions */
+
+#ifdef __KERNEL__
+
+extern void rand_initialize(void);
+extern void rand_initialize_irq(int irq);
+extern void rand_initialize_blkdev(int irq, int mode);
+
+extern void add_keyboard_randomness(unsigned char scancode);
+extern void add_mouse_randomness(__u32 mouse_data);
+extern void add_interrupt_randomness(int irq);
+extern void add_blkdev_randomness(int major);
+
+extern void get_random_bytes(void *buf, int nbytes);
+
+extern __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
+ __u16 sport, __u16 dport);
+extern __u32 secure_tcp_syn_cookie(__u32 saddr, __u32 daddr,
+ __u16 sport, __u16 dport,
+ __u32 sseq, __u32 count,
+ __u32 data);
+extern __u32 check_tcp_syn_cookie(__u32 cookie, __u32 saddr,
+ __u32 daddr, __u16 sport,
+ __u16 dport, __u32 sseq,
+ __u32 count, __u32 maxdiff);
+
+#ifndef MODULE
+extern struct file_operations random_fops, urandom_fops;
+#endif
+
+#endif /* __KERNEL___ */
+
+#endif /* _LINUX_RANDOM_H */
diff --git a/pfinet/linux-src/include/linux/reboot.h b/pfinet/linux-src/include/linux/reboot.h
new file mode 100644
index 00000000..463350f5
--- /dev/null
+++ b/pfinet/linux-src/include/linux/reboot.h
@@ -0,0 +1,52 @@
+#ifndef _LINUX_REBOOT_H
+#define _LINUX_REBOOT_H
+
+/*
+ * Magic values required to use _reboot() system call.
+ */
+
+#define LINUX_REBOOT_MAGIC1 0xfee1dead
+#define LINUX_REBOOT_MAGIC2 672274793
+#define LINUX_REBOOT_MAGIC2A 85072278
+#define LINUX_REBOOT_MAGIC2B 369367448
+
+
+/*
+ * Commands accepted by the _reboot() system call.
+ *
+ * RESTART Restart system using default command and mode.
+ * HALT Stop OS and give system control to ROM monitor, if any.
+ * CAD_ON Ctrl-Alt-Del sequence causes RESTART command.
+ * CAD_OFF Ctrl-Alt-Del sequence sends SIGINT to init task.
+ * POWER_OFF Stop OS and remove all power from system, if possible.
+ * RESTART2 Restart system using given command string.
+ */
+
+#define LINUX_REBOOT_CMD_RESTART 0x01234567
+#define LINUX_REBOOT_CMD_HALT 0xCDEF0123
+#define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF
+#define LINUX_REBOOT_CMD_CAD_OFF 0x00000000
+#define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC
+#define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4
+
+
+#ifdef __KERNEL__
+
+#include <linux/notifier.h>
+
+extern struct notifier_block *reboot_notifier_list;
+extern int register_reboot_notifier(struct notifier_block *);
+extern int unregister_reboot_notifier(struct notifier_block *);
+
+
+/*
+ * Architecture-specific implementations of sys_reboot commands.
+ */
+
+extern void machine_restart(char *cmd);
+extern void machine_halt(void);
+extern void machine_power_off(void);
+
+#endif
+
+#endif /* _LINUX_REBOOT_H */
diff --git a/pfinet/linux-src/include/linux/resource.h b/pfinet/linux-src/include/linux/resource.h
new file mode 100644
index 00000000..f3bffbd7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/resource.h
@@ -0,0 +1,60 @@
+#ifndef _LINUX_RESOURCE_H
+#define _LINUX_RESOURCE_H
+
+#include <linux/time.h>
+
+/*
+ * Resource control/accounting header file for linux
+ */
+
+/*
+ * Definition of struct rusage taken from BSD 4.3 Reno
+ *
+ * We don't support all of these yet, but we might as well have them....
+ * Otherwise, each time we add new items, programs which depend on this
+ * structure will lose. This reduces the chances of that happening.
+ */
+#define RUSAGE_SELF 0
+#define RUSAGE_CHILDREN (-1)
+#define RUSAGE_BOTH (-2) /* sys_wait4() uses this */
+
+struct rusage {
+ struct timeval ru_utime; /* user time used */
+ struct timeval ru_stime; /* system time used */
+ long ru_maxrss; /* maximum resident set size */
+ long ru_ixrss; /* integral shared memory size */
+ long ru_idrss; /* integral unshared data size */
+ long ru_isrss; /* integral unshared stack size */
+ long ru_minflt; /* page reclaims */
+ long ru_majflt; /* page faults */
+ long ru_nswap; /* swaps */
+ long ru_inblock; /* block input operations */
+ long ru_oublock; /* block output operations */
+ long ru_msgsnd; /* messages sent */
+ long ru_msgrcv; /* messages received */
+ long ru_nsignals; /* signals received */
+ long ru_nvcsw; /* voluntary context switches */
+ long ru_nivcsw; /* involuntary " */
+};
+
+#define RLIM_INFINITY ((long)(~0UL>>1))
+
+struct rlimit {
+ long rlim_cur;
+ long rlim_max;
+};
+
+#define PRIO_MIN (-20)
+#define PRIO_MAX 20
+
+#define PRIO_PROCESS 0
+#define PRIO_PGRP 1
+#define PRIO_USER 2
+
+/*
+ * Due to binary compatibility, the actual resource numbers
+ * may be different for different linux versions..
+ */
+#include <asm/resource.h>
+
+#endif
diff --git a/pfinet/linux-src/include/linux/rocket.h b/pfinet/linux-src/include/linux/rocket.h
new file mode 100644
index 00000000..2019d4d3
--- /dev/null
+++ b/pfinet/linux-src/include/linux/rocket.h
@@ -0,0 +1,55 @@
+/*
+ * This file contains the exported interface of the rocket driver to
+ * its configuration program.
+ */
+
+struct rocket_config {
+ int line;
+ int flags;
+ int closing_wait;
+ int close_delay;
+ int port;
+ int reserved[32];
+};
+
+struct rocket_ports {
+ int tty_major;
+ int callout_major;
+ int port_bitmap[4];
+ int reserved[32];
+};
+
+/*
+ * Rocketport flags
+ */
+#define ROCKET_CALLOUT_NOHUP 0x00000001
+#define ROCKET_FORCE_CD 0x00000002
+#define ROCKET_HUP_NOTIFY 0x00000004
+#define ROCKET_SPLIT_TERMIOS 0x00000008
+#define ROCKET_SPD_MASK 0x00000070
+#define ROCKET_SPD_HI 0x00000010 /* Use 56000 instead of 38400 bps */
+#define ROCKET_SPD_VHI 0x00000020 /* Use 115200 instead of 38400 bps*/
+#define ROCKET_SPD_SHI 0x00000030 /* Use 230400 instead of 38400 bps*/
+#define ROCKET_SPD_WARP 0x00000040 /* Use 460800 instead of 38400 bps*/
+#define ROCKET_SAK 0x00000080
+#define ROCKET_SESSION_LOCKOUT 0x00000100
+#define ROCKET_PGRP_LOCKOUT 0x00000200
+
+#define ROCKET_FLAGS 0x000003FF
+
+#define ROCKET_USR_MASK 0x0071 /* Legal flags that non-privileged
+ * users can set or reset */
+
+/*
+ * For closing_wait and closing_wait2
+ */
+#define ROCKET_CLOSING_WAIT_NONE 65535
+#define ROCKET_CLOSING_WAIT_INF 0
+
+/*
+ * Rocketport ioctls -- "RP"
+ */
+#define RCKP_GET_STRUCT 0x00525001
+#define RCKP_GET_CONFIG 0x00525002
+#define RCKP_SET_CONFIG 0x00525003
+#define RCKP_GET_PORTS 0x00525004
diff --git a/pfinet/linux-src/include/linux/romfs_fs.h b/pfinet/linux-src/include/linux/romfs_fs.h
new file mode 100644
index 00000000..844e22f9
--- /dev/null
+++ b/pfinet/linux-src/include/linux/romfs_fs.h
@@ -0,0 +1,62 @@
+#ifndef __LINUX_ROMFS_FS_H
+#define __LINUX_ROMFS_FS_H
+
+/* The basic structures of the romfs filesystem */
+
+#define ROMBSIZE BLOCK_SIZE
+#define ROMBSBITS BLOCK_SIZE_BITS
+#define ROMBMASK (ROMBSIZE-1)
+#define ROMFS_MAGIC 0x7275
+
+#define ROMFS_MAXFN 128
+
+#define __mkw(h,l) (((h)&0x00ff)<< 8|((l)&0x00ff))
+#define __mkl(h,l) (((h)&0xffff)<<16|((l)&0xffff))
+#define __mk4(a,b,c,d) htonl(__mkl(__mkw(a,b),__mkw(c,d)))
+#define ROMSB_WORD0 __mk4('-','r','o','m')
+#define ROMSB_WORD1 __mk4('1','f','s','-')
+
+/* On-disk "super block" */
+
+struct romfs_super_block {
+ __u32 word0;
+ __u32 word1;
+ __u32 size;
+ __u32 checksum;
+ char name[0]; /* volume name */
+};
+
+/* On disk inode */
+
+struct romfs_inode {
+ __u32 next; /* low 4 bits see ROMFH_ */
+ __u32 spec;
+ __u32 size;
+ __u32 checksum;
+ char name[0];
+};
+
+#define ROMFH_TYPE 7
+#define ROMFH_HRD 0
+#define ROMFH_DIR 1
+#define ROMFH_REG 2
+#define ROMFH_SYM 3
+#define ROMFH_BLK 4
+#define ROMFH_CHR 5
+#define ROMFH_SCK 6
+#define ROMFH_FIF 7
+#define ROMFH_EXEC 8
+
+/* Alignment */
+
+#define ROMFH_SIZE 16
+#define ROMFH_PAD (ROMFH_SIZE-1)
+#define ROMFH_MASK (~ROMFH_PAD)
+
+#ifdef __KERNEL__
+
+/* Not much now */
+extern int init_romfs_fs(void);
+
+#endif /* __KERNEL__ */
+#endif
diff --git a/pfinet/linux-src/include/linux/romfs_fs_i.h b/pfinet/linux-src/include/linux/romfs_fs_i.h
new file mode 100644
index 00000000..1ffe4b7d
--- /dev/null
+++ b/pfinet/linux-src/include/linux/romfs_fs_i.h
@@ -0,0 +1,11 @@
+#ifndef __ROMFS_FS_I
+#define __ROMFS_FS_I
+
+/* inode in-kernel data */
+
+struct romfs_inode_info {
+ unsigned long i_metasize; /* size of non-data area */
+ unsigned long i_dataoffset; /* from the start of fs */
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/romfs_fs_sb.h b/pfinet/linux-src/include/linux/romfs_fs_sb.h
new file mode 100644
index 00000000..02da2280
--- /dev/null
+++ b/pfinet/linux-src/include/linux/romfs_fs_sb.h
@@ -0,0 +1,10 @@
+#ifndef __ROMFS_FS_SB
+#define __ROMFS_FS_SB
+
+/* romfs superblock in-core data */
+
+struct romfs_sb_info {
+ unsigned long s_maxsize;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/rose.h b/pfinet/linux-src/include/linux/rose.h
new file mode 100644
index 00000000..c7b4b184
--- /dev/null
+++ b/pfinet/linux-src/include/linux/rose.h
@@ -0,0 +1,87 @@
+/*
+ * These are the public elements of the Linux kernel Rose implementation.
+ * For kernel AX.25 see the file ax25.h. This file requires ax25.h for the
+ * definition of the ax25_address structure.
+ */
+
+#ifndef ROSE_KERNEL_H
+#define ROSE_KERNEL_H
+
+#define ROSE_MTU 251
+
+#define ROSE_MAX_DIGIS 6
+
+#define ROSE_DEFER 1
+#define ROSE_T1 2
+#define ROSE_T2 3
+#define ROSE_T3 4
+#define ROSE_IDLE 5
+#define ROSE_QBITINCL 6
+#define ROSE_HOLDBACK 7
+
+#define SIOCRSGCAUSE (SIOCPROTOPRIVATE+0)
+#define SIOCRSSCAUSE (SIOCPROTOPRIVATE+1)
+#define SIOCRSL2CALL (SIOCPROTOPRIVATE+2)
+#define SIOCRSSL2CALL (SIOCPROTOPRIVATE+2)
+#define SIOCRSACCEPT (SIOCPROTOPRIVATE+3)
+#define SIOCRSCLRRT (SIOCPROTOPRIVATE+4)
+#define SIOCRSGL2CALL (SIOCPROTOPRIVATE+5)
+#define SIOCRSGFACILITIES (SIOCPROTOPRIVATE+6)
+
+#define ROSE_DTE_ORIGINATED 0x00
+#define ROSE_NUMBER_BUSY 0x01
+#define ROSE_INVALID_FACILITY 0x03
+#define ROSE_NETWORK_CONGESTION 0x05
+#define ROSE_OUT_OF_ORDER 0x09
+#define ROSE_ACCESS_BARRED 0x0B
+#define ROSE_NOT_OBTAINABLE 0x0D
+#define ROSE_REMOTE_PROCEDURE 0x11
+#define ROSE_LOCAL_PROCEDURE 0x13
+#define ROSE_SHIP_ABSENT 0x39
+
+typedef struct {
+ char rose_addr[5];
+} rose_address;
+
+struct sockaddr_rose {
+ sa_family_t srose_family;
+ rose_address srose_addr;
+ ax25_address srose_call;
+ int srose_ndigis;
+ ax25_address srose_digi;
+};
+
+struct full_sockaddr_rose {
+ sa_family_t srose_family;
+ rose_address srose_addr;
+ ax25_address srose_call;
+ unsigned int srose_ndigis;
+ ax25_address srose_digis[ROSE_MAX_DIGIS];
+};
+
+struct rose_route_struct {
+ rose_address address;
+ unsigned short mask;
+ ax25_address neighbour;
+ char device[16];
+ unsigned char ndigis;
+ ax25_address digipeaters[AX25_MAX_DIGIS];
+};
+
+struct rose_cause_struct {
+ unsigned char cause;
+ unsigned char diagnostic;
+};
+
+struct rose_facilities_struct {
+ rose_address source_addr, dest_addr;
+ ax25_address source_call, dest_call;
+ unsigned char source_ndigis, dest_ndigis;
+ ax25_address source_digis[ROSE_MAX_DIGIS];
+ ax25_address dest_digis[ROSE_MAX_DIGIS];
+ unsigned int rand;
+ rose_address fail_addr;
+ ax25_address fail_call;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/route.h b/pfinet/linux-src/include/linux/route.h
new file mode 100644
index 00000000..6d1b8e39
--- /dev/null
+++ b/pfinet/linux-src/include/linux/route.h
@@ -0,0 +1,69 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the IP router interface.
+ *
+ * Version: @(#)route.h 1.0.3 05/27/93
+ *
+ * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1986-1988
+ * for the purposes of compatibility only.
+ *
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * Changes:
+ * Mike McLagan : Routing by source
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_ROUTE_H
+#define _LINUX_ROUTE_H
+
+#include <linux/if.h>
+
+
+/* This structure gets passed by the SIOCADDRT and SIOCDELRT calls. */
+struct rtentry
+{
+ unsigned long rt_pad1;
+ struct sockaddr rt_dst; /* target address */
+ struct sockaddr rt_gateway; /* gateway addr (RTF_GATEWAY) */
+ struct sockaddr rt_genmask; /* target network mask (IP) */
+ unsigned short rt_flags;
+ short rt_pad2;
+ unsigned long rt_pad3;
+ void *rt_pad4;
+ short rt_metric; /* +1 for binary compatibility! */
+ char *rt_dev; /* forcing the device at add */
+ unsigned long rt_mtu; /* per route MTU/Window */
+#ifndef __KERNEL__
+#define rt_mss rt_mtu /* Compatibility :-( */
+#endif
+ unsigned long rt_window; /* Window clamping */
+ unsigned short rt_irtt; /* Initial RTT */
+};
+
+
+#define RTF_UP 0x0001 /* route usable */
+#define RTF_GATEWAY 0x0002 /* destination is a gateway */
+#define RTF_HOST 0x0004 /* host entry (net otherwise) */
+#define RTF_REINSTATE 0x0008 /* reinstate route after tmout */
+#define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */
+#define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */
+#define RTF_MTU 0x0040 /* specific MTU for this route */
+#define RTF_MSS RTF_MTU /* Compatibility :-( */
+#define RTF_WINDOW 0x0080 /* per route window clamping */
+#define RTF_IRTT 0x0100 /* Initial round trip time */
+#define RTF_REJECT 0x0200 /* Reject route */
+
+/*
+ * <linux/ipv6_route.h> uses RTF values >= 64k
+ */
+
+
+
+#endif /* _LINUX_ROUTE_H */
diff --git a/pfinet/linux-src/include/linux/rpcsock.h b/pfinet/linux-src/include/linux/rpcsock.h
new file mode 100644
index 00000000..80f1fc4c
--- /dev/null
+++ b/pfinet/linux-src/include/linux/rpcsock.h
@@ -0,0 +1,121 @@
+/*
+ * rpcsock.h Declarations for the RPC call interface.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+
+#ifndef _LINUX_RPCSOCK_H
+#define _LINUX_RPCSOCK_H
+
+/*
+ * The rpcsock code maintains an estimate on the maximum number of out-
+ * standing RPC requests, using the congestion avoidance implemented in
+ * 44BSD. This is basically the Van Jacobson slow start algorithm: If a
+ * retransmit occurs, the congestion window is halved; otherwise, it is
+ * incremented by 1/cwnd when a reply is received and a full number of
+ * requests are outstanding.
+ *
+ * Upper procedures may check whether a request would block waiting for
+ * a free RPC slot by using the RPC_CONGESTED() macro.
+ *
+ * Note: on machines with low memory we should probably use a smaller
+ * MAXREQS value: At 32 outstanding reqs with 8 megs of RAM, fragment
+ * reassembly will frequently run out of memory.
+ */
+#define RPC_MAXREQS 32
+#define RPC_CWNDSCALE 256
+#define RPC_MAXCWND (RPC_MAXREQS * RPC_CWNDSCALE)
+/* #define RPC_INITCWND (RPC_MAXCWND / 2) */
+#define RPC_INITCWND RPC_CWNDSCALE
+#define RPC_CONGESTED(rsock) ((rsock)->cong >= (rsock)->cwnd)
+
+/* RPC reply header size: xid, direction, status, accept_status (verifier
+ * size computed separately)
+ */
+#define RPC_HDRSIZE (4 * 4)
+
+/*
+ * This describes a timeout strategy
+ */
+struct rpc_timeout {
+ unsigned long to_initval,
+ to_maxval,
+ to_increment;
+ int to_retries;
+ char to_exponential;
+};
+
+/*
+ * This describes a complete RPC request
+ */
+struct rpc_ioreq {
+ struct rpc_wait * rq_slot;
+ struct sockaddr * rq_addr;
+ int rq_alen;
+ struct iovec rq_svec[UIO_FASTIOV];
+ unsigned int rq_snr;
+ unsigned long rq_slen;
+ struct iovec rq_rvec[UIO_FASTIOV];
+ unsigned int rq_rnr;
+ unsigned long rq_rlen;
+};
+
+/*
+ * This is the callback handler for async RPC.
+ */
+struct rpc_wait;
+typedef void (*rpc_callback_fn_t)(int, struct rpc_wait *, void *);
+
+/*
+ * Wait information. This struct defines all the state of an RPC
+ * request currently in flight.
+ */
+struct rpc_wait {
+ struct rpc_sock * w_sock;
+ struct rpc_wait * w_prev;
+ struct rpc_wait * w_next;
+ struct rpc_ioreq * w_req;
+ int w_result;
+ struct wait_queue * w_wait;
+ rpc_callback_fn_t w_handler;
+ void * w_cdata;
+ char w_queued;
+ char w_gotit;
+ __u32 w_xid;
+};
+
+struct rpc_sock {
+ struct file * file;
+ struct socket * sock;
+ struct sock * inet;
+ struct rpc_wait waiting[RPC_MAXREQS];
+ unsigned long cong;
+ unsigned long cwnd;
+ struct rpc_wait * pending;
+ struct rpc_wait * free;
+ struct wait_queue * backlog;
+ struct wait_queue * shutwait;
+ int shutdown;
+};
+
+#ifdef __KERNEL__
+
+/* rpc_call: Call synchronously */
+int rpc_call(struct rpc_sock *, struct rpc_ioreq *,
+ struct rpc_timeout *);
+/* These implement asynch calls for nfsiod: Process calls rpc_reserve and
+ * rpc_transmits, then passes the request to nfsiod, which collects the
+ * results via rpc_doio
+ */
+int rpc_reserve(struct rpc_sock *, struct rpc_ioreq *, int);
+void rpc_release(struct rpc_sock *, struct rpc_ioreq *);
+int rpc_transmit(struct rpc_sock *, struct rpc_ioreq *);
+int rpc_doio(struct rpc_sock *, struct rpc_ioreq *,
+ struct rpc_timeout *, int);
+struct rpc_sock * rpc_makesock(struct file *);
+int rpc_closesock(struct rpc_sock *);
+
+#endif /* __KERNEL__*/
+
+#endif /* _LINUX_RPCSOCK_H */
diff --git a/pfinet/linux-src/include/linux/rtnetlink.h b/pfinet/linux-src/include/linux/rtnetlink.h
new file mode 100644
index 00000000..548a9b14
--- /dev/null
+++ b/pfinet/linux-src/include/linux/rtnetlink.h
@@ -0,0 +1,673 @@
+#ifndef __LINUX_RTNETLINK_H
+#define __LINUX_RTNETLINK_H
+
+#ifdef __KERNEL__
+#include <linux/config.h>
+#endif
+#include <linux/netlink.h>
+
+#define RTNL_DEBUG 1
+
+
+/****
+ * Routing/neighbour discovery messages.
+ ****/
+
+/* Types of messages */
+
+#define RTM_BASE 0x10
+
+#define RTM_NEWLINK (RTM_BASE+0)
+#define RTM_DELLINK (RTM_BASE+1)
+#define RTM_GETLINK (RTM_BASE+2)
+
+#define RTM_NEWADDR (RTM_BASE+4)
+#define RTM_DELADDR (RTM_BASE+5)
+#define RTM_GETADDR (RTM_BASE+6)
+
+#define RTM_NEWROUTE (RTM_BASE+8)
+#define RTM_DELROUTE (RTM_BASE+9)
+#define RTM_GETROUTE (RTM_BASE+10)
+
+#define RTM_NEWNEIGH (RTM_BASE+12)
+#define RTM_DELNEIGH (RTM_BASE+13)
+#define RTM_GETNEIGH (RTM_BASE+14)
+
+#define RTM_NEWRULE (RTM_BASE+16)
+#define RTM_DELRULE (RTM_BASE+17)
+#define RTM_GETRULE (RTM_BASE+18)
+
+#define RTM_NEWQDISC (RTM_BASE+20)
+#define RTM_DELQDISC (RTM_BASE+21)
+#define RTM_GETQDISC (RTM_BASE+22)
+
+#define RTM_NEWTCLASS (RTM_BASE+24)
+#define RTM_DELTCLASS (RTM_BASE+25)
+#define RTM_GETTCLASS (RTM_BASE+26)
+
+#define RTM_NEWTFILTER (RTM_BASE+28)
+#define RTM_DELTFILTER (RTM_BASE+29)
+#define RTM_GETTFILTER (RTM_BASE+30)
+
+#define RTM_MAX (RTM_BASE+31)
+
+/*
+ Generic structure for encapsulation optional route information.
+ It is reminiscent of sockaddr, but with sa_family replaced
+ with attribute type.
+ */
+
+struct rtattr
+{
+ unsigned short rta_len;
+ unsigned short rta_type;
+};
+
+/* Macros to handle rtattributes */
+
+#define RTA_ALIGNTO 4
+#define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) )
+#define RTA_OK(rta,len) ((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \
+ (rta)->rta_len <= (len))
+#define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \
+ (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len)))
+#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
+#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len))
+#define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0)))
+#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
+
+
+
+
+/******************************************************************************
+ * Definitions used in routing table administation.
+ ****/
+
+struct rtmsg
+{
+ unsigned char rtm_family;
+ unsigned char rtm_dst_len;
+ unsigned char rtm_src_len;
+ unsigned char rtm_tos;
+
+ unsigned char rtm_table; /* Routing table id */
+ unsigned char rtm_protocol; /* Routing protocol; see below */
+ unsigned char rtm_scope; /* See below */
+ unsigned char rtm_type; /* See below */
+
+ unsigned rtm_flags;
+};
+
+/* rtm_type */
+
+enum
+{
+ RTN_UNSPEC,
+ RTN_UNICAST, /* Gateway or direct route */
+ RTN_LOCAL, /* Accept locally */
+ RTN_BROADCAST, /* Accept locally as broadcast,
+ send as broadcast */
+ RTN_ANYCAST, /* Accept locally as broadcast,
+ but send as unicast */
+ RTN_MULTICAST, /* Multicast route */
+ RTN_BLACKHOLE, /* Drop */
+ RTN_UNREACHABLE, /* Destination is unreachable */
+ RTN_PROHIBIT, /* Administratively prohibited */
+ RTN_THROW, /* Not in this table */
+ RTN_NAT, /* Translate this address */
+ RTN_XRESOLVE, /* Use external resolver */
+};
+
+#define RTN_MAX RTN_XRESOLVE
+
+
+/* rtm_protocol */
+
+#define RTPROT_UNSPEC 0
+#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects;
+ not used by current IPv4 */
+#define RTPROT_KERNEL 2 /* Route installed by kernel */
+#define RTPROT_BOOT 3 /* Route installed during boot */
+#define RTPROT_STATIC 4 /* Route installed by administrator */
+
+/* Values of protocol >= RTPROT_STATIC are not interpreted by kernel;
+ they just passed from user and back as is.
+ It will be used by hypothetical multiple routing daemons.
+ Note that protocol values should be standardized in order to
+ avoid conflicts.
+ */
+
+#define RTPROT_GATED 8 /* Apparently, GateD */
+#define RTPROT_RA 9 /* RDISC/ND router advertisements */
+#define RTPROT_MRT 10 /* Merit MRT */
+#define RTPROT_ZEBRA 11 /* Zebra */
+#define RTPROT_BIRD 12 /* BIRD */
+
+/* rtm_scope
+
+ Really it is not scope, but sort of distance to the destination.
+ NOWHERE are reserved for not existing destinations, HOST is our
+ local addresses, LINK are destinations, located on directly attached
+ link and UNIVERSE is everywhere in the Universe.
+
+ Intermediate values are also possible f.e. interior routes
+ could be assigned a value between UNIVERSE and LINK.
+*/
+
+enum rt_scope_t
+{
+ RT_SCOPE_UNIVERSE=0,
+/* User defined values */
+ RT_SCOPE_SITE=200,
+ RT_SCOPE_LINK=253,
+ RT_SCOPE_HOST=254,
+ RT_SCOPE_NOWHERE=255
+};
+
+/* rtm_flags */
+
+#define RTM_F_NOTIFY 0x100 /* Notify user of route change */
+#define RTM_F_CLONED 0x200 /* This route is cloned */
+#define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */
+
+/* Reserved table identifiers */
+
+enum rt_class_t
+{
+ RT_TABLE_UNSPEC=0,
+/* User defined values */
+ RT_TABLE_DEFAULT=253,
+ RT_TABLE_MAIN=254,
+ RT_TABLE_LOCAL=255
+};
+#define RT_TABLE_MAX RT_TABLE_LOCAL
+
+
+
+/* Routing message attributes */
+
+enum rtattr_type_t
+{
+ RTA_UNSPEC,
+ RTA_DST,
+ RTA_SRC,
+ RTA_IIF,
+ RTA_OIF,
+ RTA_GATEWAY,
+ RTA_PRIORITY,
+ RTA_PREFSRC,
+ RTA_METRICS,
+ RTA_MULTIPATH,
+ RTA_PROTOINFO,
+ RTA_FLOW,
+ RTA_CACHEINFO
+};
+
+#define RTA_MAX RTA_CACHEINFO
+
+#define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg))))
+#define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg))
+
+/* RTM_MULTIPATH --- array of struct rtnexthop.
+ *
+ * "struct rtnexthop" describres all necessary nexthop information,
+ * i.e. parameters of path to a destination via this nextop.
+ *
+ * At the moment it is impossible to set different prefsrc, mtu, window
+ * and rtt for different paths from multipath.
+ */
+
+struct rtnexthop
+{
+ unsigned short rtnh_len;
+ unsigned char rtnh_flags;
+ unsigned char rtnh_hops;
+ int rtnh_ifindex;
+};
+
+/* rtnh_flags */
+
+#define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */
+#define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */
+#define RTNH_F_ONLINK 4 /* Gateway is forced on link */
+
+/* Macros to handle hexthops */
+
+#define RTNH_ALIGNTO 4
+#define RTNH_ALIGN(len) ( ((len)+RTNH_ALIGNTO-1) & ~(RTNH_ALIGNTO-1) )
+#define RTNH_OK(rtnh,len) ((rtnh)->rtnh_len >= sizeof(struct rtnexthop) && \
+ ((int)(rtnh)->rtnh_len) <= (len))
+#define RTNH_NEXT(rtnh) ((struct rtnexthop*)(((char*)(rtnh)) + RTNH_ALIGN((rtnh)->rtnh_len)))
+#define RTNH_LENGTH(len) (RTNH_ALIGN(sizeof(struct rtnexthop)) + (len))
+#define RTNH_SPACE(len) RTNH_ALIGN(RTNH_LENGTH(len))
+#define RTNH_DATA(rtnh) ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0)))
+
+/* RTM_CACHEINFO */
+
+struct rta_cacheinfo
+{
+ __u32 rta_clntref;
+ __u32 rta_lastuse;
+ __s32 rta_expires;
+ __u32 rta_error;
+ __u32 rta_used;
+};
+
+/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */
+
+enum
+{
+ RTAX_UNSPEC,
+ RTAX_LOCK,
+ RTAX_MTU,
+ RTAX_WINDOW,
+ RTAX_RTT,
+ RTAX_HOPS,
+ RTAX_SSTHRESH,
+ RTAX_CWND,
+};
+
+#define RTAX_MAX RTAX_CWND
+
+
+
+/*********************************************************
+ * Interface address.
+ ****/
+
+struct ifaddrmsg
+{
+ unsigned char ifa_family;
+ unsigned char ifa_prefixlen; /* The prefix length */
+ unsigned char ifa_flags; /* Flags */
+ unsigned char ifa_scope; /* See above */
+ int ifa_index; /* Link index */
+};
+
+enum
+{
+ IFA_UNSPEC,
+ IFA_ADDRESS,
+ IFA_LOCAL,
+ IFA_LABEL,
+ IFA_BROADCAST,
+ IFA_ANYCAST,
+ IFA_CACHEINFO
+};
+
+#define IFA_MAX IFA_CACHEINFO
+
+/* ifa_flags */
+
+#define IFA_F_SECONDARY 0x01
+
+#define IFA_F_DEPRECATED 0x20
+#define IFA_F_TENTATIVE 0x40
+#define IFA_F_PERMANENT 0x80
+
+struct ifa_cacheinfo
+{
+ __s32 ifa_prefered;
+ __s32 ifa_valid;
+};
+
+
+#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
+
+/*
+ Important comment:
+ IFA_ADDRESS is prefix address, rather than local interface address.
+ It makes no difference for normally configured broadcast interfaces,
+ but for point-to-point IFA_ADDRESS is DESTINATION address,
+ local address is supplied in IFA_LOCAL attribute.
+ */
+
+/**************************************************************
+ * Neighbour discovery.
+ ****/
+
+struct ndmsg
+{
+ unsigned char ndm_family;
+ unsigned char ndm_pad1;
+ unsigned short ndm_pad2;
+ int ndm_ifindex; /* Link index */
+ __u16 ndm_state;
+ __u8 ndm_flags;
+ __u8 ndm_type;
+};
+
+enum
+{
+ NDA_UNSPEC,
+ NDA_DST,
+ NDA_LLADDR,
+ NDA_CACHEINFO
+};
+
+#define NDA_MAX NDA_CACHEINFO
+
+#define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
+#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
+
+/*
+ * Neighbor Cache Entry Flags
+ */
+
+#define NTF_PROXY 0x08 /* == ATF_PUBL */
+#define NTF_ROUTER 0x80
+
+/*
+ * Neighbor Cache Entry States.
+ */
+
+#define NUD_INCOMPLETE 0x01
+#define NUD_REACHABLE 0x02
+#define NUD_STALE 0x04
+#define NUD_DELAY 0x08
+#define NUD_PROBE 0x10
+#define NUD_FAILED 0x20
+
+/* Dummy states */
+#define NUD_NOARP 0x40
+#define NUD_PERMANENT 0x80
+#define NUD_NONE 0x00
+
+
+struct nda_cacheinfo
+{
+ __u32 ndm_confirmed;
+ __u32 ndm_used;
+ __u32 ndm_updated;
+ __u32 ndm_refcnt;
+};
+
+/****
+ * General form of address family dependent message.
+ ****/
+
+struct rtgenmsg
+{
+ unsigned char rtgen_family;
+};
+
+/*****************************************************************
+ * Link layer specific messages.
+ ****/
+
+/* struct ifinfomsg
+ * passes link level specific information, not dependent
+ * on network protocol.
+ */
+
+struct ifinfomsg
+{
+ unsigned char ifi_family;
+ unsigned char __ifi_pad;
+ unsigned short ifi_type; /* ARPHRD_* */
+ int ifi_index; /* Link index */
+ unsigned ifi_flags; /* IFF_* flags */
+ unsigned ifi_change; /* IFF_* change mask */
+};
+
+enum
+{
+ IFLA_UNSPEC,
+ IFLA_ADDRESS,
+ IFLA_BROADCAST,
+ IFLA_IFNAME,
+ IFLA_MTU,
+ IFLA_LINK,
+ IFLA_QDISC,
+ IFLA_STATS
+};
+
+
+#define IFLA_MAX IFLA_STATS
+
+#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
+
+/* ifi_flags.
+
+ IFF_* flags.
+
+ The only change is:
+ IFF_LOOPBACK, IFF_BROADCAST and IFF_POINTOPOINT are
+ more not changeable by user. They describe link media
+ characteristics and set by device driver.
+
+ Comments:
+ - Combination IFF_BROADCAST|IFF_POINTOPOINT is invalid
+ - If neiher of these three flags are set;
+ the interface is NBMA.
+
+ - IFF_MULTICAST does not mean anything special:
+ multicasts can be used on all not-NBMA links.
+ IFF_MULTICAST means that this media uses special encapsulation
+ for multicast frames. Apparently, all IFF_POINTOPOINT and
+ IFF_BROADCAST devices are able to use multicasts too.
+ */
+
+/* ifi_link.
+ For usual devices it is equal ifi_index.
+ If it is a "virtual interface" (f.e. tunnel), ifi_link
+ can point to real physical interface (f.e. for bandwidth calculations),
+ or maybe 0, what means, that real media is unknown (usual
+ for IPIP tunnels, when route to endpoint is allowed to change)
+ */
+
+/*****************************************************************
+ * Traffic control messages.
+ ****/
+
+struct tcmsg
+{
+ unsigned char tcm_family;
+ unsigned char tcm__pad1;
+ unsigned short tcm__pad2;
+ int tcm_ifindex;
+ __u32 tcm_handle;
+ __u32 tcm_parent;
+ __u32 tcm_info;
+};
+
+enum
+{
+ TCA_UNSPEC,
+ TCA_KIND,
+ TCA_OPTIONS,
+ TCA_STATS,
+ TCA_XSTATS,
+ TCA_RATE,
+};
+
+#define TCA_MAX TCA_RATE
+
+#define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg))))
+#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg))
+
+
+/* SUMMARY: maximal rtattr understood by kernel */
+
+#define RTATTR_MAX RTA_MAX
+
+/* RTnetlink multicast groups */
+
+#define RTMGRP_LINK 1
+#define RTMGRP_NOTIFY 2
+#define RTMGRP_NEIGH 4
+#define RTMGRP_TC 8
+
+#define RTMGRP_IPV4_IFADDR 0x10
+#define RTMGRP_IPV4_MROUTE 0x20
+#define RTMGRP_IPV4_ROUTE 0x40
+
+#define RTMGRP_IPV6_IFADDR 0x100
+#define RTMGRP_IPV6_MROUTE 0x200
+#define RTMGRP_IPV6_ROUTE 0x400
+
+/* End of information exported to user level */
+
+#ifdef __KERNEL__
+
+extern atomic_t rtnl_rlockct;
+extern struct wait_queue *rtnl_wait;
+
+extern __inline__ int rtattr_strcmp(struct rtattr *rta, char *str)
+{
+ int len = strlen(str) + 1;
+ return len > rta->rta_len || memcmp(RTA_DATA(rta), str, len);
+}
+
+extern int rtattr_parse(struct rtattr *tb[], int maxattr, struct rtattr *rta, int len);
+
+#ifdef CONFIG_RTNETLINK
+extern struct sock *rtnl;
+
+struct rtnetlink_link
+{
+ int (*doit)(struct sk_buff *, struct nlmsghdr*, void *attr);
+ int (*dumpit)(struct sk_buff *, struct netlink_callback *cb);
+};
+
+extern struct rtnetlink_link * rtnetlink_links[NPROTO];
+extern int rtnetlink_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb);
+extern int rtnetlink_send(struct sk_buff *skb, u32 pid, u32 group, int echo);
+
+extern void __rta_fill(struct sk_buff *skb, int attrtype, int attrlen, const void *data);
+
+#define RTA_PUT(skb, attrtype, attrlen, data) \
+({ if (skb_tailroom(skb) < (int)RTA_SPACE(attrlen)) goto rtattr_failure; \
+ __rta_fill(skb, attrtype, attrlen, data); })
+
+extern unsigned long rtnl_wlockct;
+
+/* NOTE: these locks are not interrupt safe, are not SMP safe,
+ * they are even not atomic. 8)8)8) ... and it is not a bug.
+ * Really, if these locks will be programmed correctly,
+ * all the addressing/routing machine would become SMP safe,
+ * but is absolutely useless at the moment, because all the kernel
+ * is not reenterable in any case. --ANK
+ *
+ * Well, atomic_* and set_bit provide the only thing here:
+ * gcc is confused not to overoptimize them, that's all.
+ * I remember as gcc splitted ++ operation, but cannot reproduce
+ * it with gcc-2.7.*. --ANK
+ *
+ * One more note: rwlock facility should be written and put
+ * to a kernel wide location: f.e. current implementation of semaphores
+ * (especially, for x86) looks like a wonder. It would be good
+ * to have something similar for rwlock. Recursive lock could be also
+ * useful thing. --ANK
+ */
+
+extern __inline__ int rtnl_shlock_nowait(void)
+{
+ atomic_inc(&rtnl_rlockct);
+ if (test_bit(0, &rtnl_wlockct)) {
+ atomic_dec(&rtnl_rlockct);
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+extern __inline__ void rtnl_shlock(void)
+{
+ while (rtnl_shlock_nowait())
+ sleep_on(&rtnl_wait);
+}
+
+/* Check for possibility to PROMOTE shared lock to exclusive.
+ Shared lock must be already grabbed with rtnl_shlock*().
+ */
+
+extern __inline__ int rtnl_exlock_nowait(void)
+{
+ if (atomic_read(&rtnl_rlockct) > 1)
+ return -EAGAIN;
+ if (test_and_set_bit(0, &rtnl_wlockct))
+ return -EAGAIN;
+ return 0;
+}
+
+extern __inline__ void rtnl_exlock(void)
+{
+ while (rtnl_exlock_nowait())
+ sleep_on(&rtnl_wait);
+}
+
+#if 0
+extern __inline__ void rtnl_shunlock(void)
+{
+ atomic_dec(&rtnl_rlockct);
+ if (atomic_read(&rtnl_rlockct) <= 1) {
+ wake_up(&rtnl_wait);
+ if (rtnl && rtnl->receive_queue.qlen)
+ rtnl->data_ready(rtnl, 0);
+ }
+}
+#else
+
+/* The problem: inline requires to include <net/sock.h> and, hence,
+ almost all of net includes :-(
+ */
+
+#define rtnl_shunlock() ({ \
+ atomic_dec(&rtnl_rlockct); \
+ if (atomic_read(&rtnl_rlockct) <= 1) { \
+ wake_up(&rtnl_wait); \
+ if (rtnl && rtnl->receive_queue.qlen) \
+ rtnl->data_ready(rtnl, 0); \
+ } \
+})
+#endif
+
+/* Release exclusive lock. Note, that we do not wake up rtnetlink socket,
+ * it will be done later after releasing shared lock.
+ */
+
+extern __inline__ void rtnl_exunlock(void)
+{
+ clear_bit(0, &rtnl_wlockct);
+ wake_up(&rtnl_wait);
+}
+
+#else
+
+extern __inline__ void rtnl_shlock(void)
+{
+#ifndef _HURD_
+ while (atomic_read(&rtnl_rlockct))
+ sleep_on(&rtnl_wait);
+ atomic_inc(&rtnl_rlockct);
+#endif
+}
+
+extern __inline__ void rtnl_shunlock(void)
+{
+#ifndef _HURD_
+ if (atomic_dec_and_test(&rtnl_rlockct))
+ wake_up(&rtnl_wait);
+#endif
+}
+
+extern __inline__ void rtnl_exlock(void)
+{
+}
+
+extern __inline__ void rtnl_exunlock(void)
+{
+}
+
+#endif
+
+extern void rtnl_lock(void);
+extern void rtnl_unlock(void);
+extern void rtnetlink_init(void);
+
+#endif /* __KERNEL__ */
+
+
+#endif /* __LINUX_RTNETLINK_H */
diff --git a/pfinet/linux-src/include/linux/sc26198.h b/pfinet/linux-src/include/linux/sc26198.h
new file mode 100644
index 00000000..38685e07
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sc26198.h
@@ -0,0 +1,533 @@
+/*****************************************************************************/
+
+/*
+ * sc26198.h -- SC26198 UART hardware info.
+ *
+ * Copyright (C) 1995-1998 Stallion Technologies (support@stallion.oz.au).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*****************************************************************************/
+#ifndef _SC26198_H
+#define _SC26198_H
+/*****************************************************************************/
+
+/*
+ * Define the number of async ports per sc26198 uart device.
+ */
+#define SC26198_PORTS 8
+
+/*
+ * Baud rate timing clocks. All derived from a master 14.7456 MHz clock.
+ */
+#define SC26198_MASTERCLOCK 14745600L
+#define SC26198_DCLK (SC26198_MASTERCLOCK)
+#define SC26198_CCLK (SC26198_MASTERCLOCK / 2)
+#define SC26198_BCLK (SC26198_MASTERCLOCK / 4)
+
+/*
+ * Define internal FIFO sizes for the 26198 ports.
+ */
+#define SC26198_TXFIFOSIZE 16
+#define SC26198_RXFIFOSIZE 16
+
+/*****************************************************************************/
+
+/*
+ * Global register definitions. These registers are global to each 26198
+ * device, not specific ports on it.
+ */
+#define TSTR 0x0d
+#define GCCR 0x0f
+#define ICR 0x1b
+#define WDTRCR 0x1d
+#define IVR 0x1f
+#define BRGTRUA 0x84
+#define GPOSR 0x87
+#define GPOC 0x8b
+#define UCIR 0x8c
+#define CIR 0x8c
+#define BRGTRUB 0x8d
+#define GRXFIFO 0x8e
+#define GTXFIFO 0x8e
+#define GCCR2 0x8f
+#define BRGTRLA 0x94
+#define GPOR 0x97
+#define GPOD 0x9b
+#define BRGTCR 0x9c
+#define GICR 0x9c
+#define BRGTRLB 0x9d
+#define GIBCR 0x9d
+#define GITR 0x9f
+
+/*
+ * Per port channel registers. These are the register offsets within
+ * the port address space, so need to have the port address (0 to 7)
+ * inserted in bit positions 4:6.
+ */
+#define MR0 0x00
+#define MR1 0x01
+#define IOPCR 0x02
+#define BCRBRK 0x03
+#define BCRCOS 0x04
+#define BCRX 0x06
+#define BCRA 0x07
+#define XONCR 0x08
+#define XOFFCR 0x09
+#define ARCR 0x0a
+#define RXCSR 0x0c
+#define TXCSR 0x0e
+#define MR2 0x80
+#define SR 0x81
+#define SCCR 0x81
+#define ISR 0x82
+#define IMR 0x82
+#define TXFIFO 0x83
+#define RXFIFO 0x83
+#define IPR 0x84
+#define IOPIOR 0x85
+#define XISR 0x86
+
+/*
+ * For any given port calculate the address to use to access a specified
+ * register. This is only used for unusual access, mostly this is done
+ * through the assembler access routines.
+ */
+#define SC26198_PORTREG(port,reg) ((((port) & 0x07) << 4) | (reg))
+
+/*****************************************************************************/
+
+/*
+ * Global configuration control register bit definitions.
+ */
+#define GCCR_NOACK 0x00
+#define GCCR_IVRACK 0x02
+#define GCCR_IVRCHANACK 0x04
+#define GCCR_IVRTYPCHANACK 0x06
+#define GCCR_ASYNCCYCLE 0x00
+#define GCCR_SYNCCYCLE 0x40
+
+/*****************************************************************************/
+
+/*
+ * Mode register 0 bit definitions.
+ */
+#define MR0_ADDRNONE 0x00
+#define MR0_AUTOWAKE 0x01
+#define MR0_AUTODOZE 0x02
+#define MR0_AUTOWAKEDOZE 0x03
+#define MR0_SWFNONE 0x00
+#define MR0_SWFTX 0x04
+#define MR0_SWFRX 0x08
+#define MR0_SWFRXTX 0x0c
+#define MR0_TXMASK 0x30
+#define MR0_TXEMPTY 0x00
+#define MR0_TXHIGH 0x10
+#define MR0_TXHALF 0x20
+#define MR0_TXRDY 0x00
+#define MR0_ADDRNT 0x00
+#define MR0_ADDRT 0x40
+#define MR0_SWFNT 0x00
+#define MR0_SWFT 0x80
+
+/*
+ * Mode register 1 bit definitions.
+ */
+#define MR1_CS5 0x00
+#define MR1_CS6 0x01
+#define MR1_CS7 0x02
+#define MR1_CS8 0x03
+#define MR1_PAREVEN 0x00
+#define MR1_PARODD 0x04
+#define MR1_PARENB 0x00
+#define MR1_PARFORCE 0x08
+#define MR1_PARNONE 0x10
+#define MR1_PARSPECIAL 0x18
+#define MR1_ERRCHAR 0x00
+#define MR1_ERRBLOCK 0x20
+#define MR1_ISRUNMASKED 0x00
+#define MR1_ISRMASKED 0x40
+#define MR1_AUTORTS 0x80
+
+/*
+ * Mode register 2 bit definitions.
+ */
+#define MR2_STOP1 0x00
+#define MR2_STOP15 0x01
+#define MR2_STOP2 0x02
+#define MR2_STOP916 0x03
+#define MR2_RXFIFORDY 0x00
+#define MR2_RXFIFOHALF 0x04
+#define MR2_RXFIFOHIGH 0x08
+#define MR2_RXFIFOFULL 0x0c
+#define MR2_AUTOCTS 0x10
+#define MR2_TXRTS 0x20
+#define MR2_MODENORM 0x00
+#define MR2_MODEAUTOECHO 0x40
+#define MR2_MODELOOP 0x80
+#define MR2_MODEREMECHO 0xc0
+
+/*****************************************************************************/
+
+/*
+ * Baud Rate Generator (BRG) selector values.
+ */
+#define BRG_50 0x00
+#define BRG_75 0x01
+#define BRG_150 0x02
+#define BRG_200 0x03
+#define BRG_300 0x04
+#define BRG_450 0x05
+#define BRG_600 0x06
+#define BRG_900 0x07
+#define BRG_1200 0x08
+#define BRG_1800 0x09
+#define BRG_2400 0x0a
+#define BRG_3600 0x0b
+#define BRG_4800 0x0c
+#define BRG_7200 0x0d
+#define BRG_9600 0x0e
+#define BRG_14400 0x0f
+#define BRG_19200 0x10
+#define BRG_28200 0x11
+#define BRG_38400 0x12
+#define BRG_57600 0x13
+#define BRG_115200 0x14
+#define BRG_230400 0x15
+#define BRG_GIN0 0x16
+#define BRG_GIN1 0x17
+#define BRG_CT0 0x18
+#define BRG_CT1 0x19
+#define BRG_RX2TX316 0x1b
+#define BRG_RX2TX31 0x1c
+
+#define SC26198_MAXBAUD 921600
+
+/*****************************************************************************/
+
+/*
+ * Command register command definitions.
+ */
+#define CR_NULL 0x04
+#define CR_ADDRNORMAL 0x0c
+#define CR_RXRESET 0x14
+#define CR_TXRESET 0x1c
+#define CR_CLEARRXERR 0x24
+#define CR_BREAKRESET 0x2c
+#define CR_TXSTARTBREAK 0x34
+#define CR_TXSTOPBREAK 0x3c
+#define CR_RTSON 0x44
+#define CR_RTSOFF 0x4c
+#define CR_ADDRINIT 0x5c
+#define CR_RXERRBLOCK 0x6c
+#define CR_TXSENDXON 0x84
+#define CR_TXSENDXOFF 0x8c
+#define CR_GANGXONSET 0x94
+#define CR_GANGXOFFSET 0x9c
+#define CR_GANGXONINIT 0xa4
+#define CR_GANGXOFFINIT 0xac
+#define CR_HOSTXON 0xb4
+#define CR_HOSTXOFF 0xbc
+#define CR_CANCELXOFF 0xc4
+#define CR_ADDRRESET 0xdc
+#define CR_RESETALLPORTS 0xf4
+#define CR_RESETALL 0xfc
+
+#define CR_RXENABLE 0x01
+#define CR_TXENABLE 0x02
+
+/*****************************************************************************/
+
+/*
+ * Channel status register.
+ */
+#define SR_RXRDY 0x01
+#define SR_RXFULL 0x02
+#define SR_TXRDY 0x04
+#define SR_TXEMPTY 0x08
+#define SR_RXOVERRUN 0x10
+#define SR_RXPARITY 0x20
+#define SR_RXFRAMING 0x40
+#define SR_RXBREAK 0x80
+
+#define SR_RXERRS (SR_RXPARITY | SR_RXFRAMING | SR_RXOVERRUN)
+
+/*****************************************************************************/
+
+/*
+ * Interrupt status register and interrupt mask register bit definitions.
+ */
+#define IR_TXRDY 0x01
+#define IR_RXRDY 0x02
+#define IR_RXBREAK 0x04
+#define IR_XONXOFF 0x10
+#define IR_ADDRRECOG 0x20
+#define IR_RXWATCHDOG 0x40
+#define IR_IOPORT 0x80
+
+/*****************************************************************************/
+
+/*
+ * Interrupt vector register field definitions.
+ */
+#define IVR_CHANMASK 0x07
+#define IVR_TYPEMASK 0x18
+#define IVR_CONSTMASK 0xc0
+
+#define IVR_RXDATA 0x10
+#define IVR_RXBADDATA 0x18
+#define IVR_TXDATA 0x08
+#define IVR_OTHER 0x00
+
+/*****************************************************************************/
+
+/*
+ * BRG timer control register bit definitions.
+ */
+#define BRGCTCR_DISABCLK0 0x00
+#define BRGCTCR_ENABCLK0 0x08
+#define BRGCTCR_DISABCLK1 0x00
+#define BRGCTCR_ENABCLK1 0x80
+
+#define BRGCTCR_0SCLK16 0x00
+#define BRGCTCR_0SCLK32 0x01
+#define BRGCTCR_0SCLK64 0x02
+#define BRGCTCR_0SCLK128 0x03
+#define BRGCTCR_0X1 0x04
+#define BRGCTCR_0X12 0x05
+#define BRGCTCR_0IO1A 0x06
+#define BRGCTCR_0GIN0 0x07
+
+#define BRGCTCR_1SCLK16 0x00
+#define BRGCTCR_1SCLK32 0x10
+#define BRGCTCR_1SCLK64 0x20
+#define BRGCTCR_1SCLK128 0x30
+#define BRGCTCR_1X1 0x40
+#define BRGCTCR_1X12 0x50
+#define BRGCTCR_1IO1B 0x60
+#define BRGCTCR_1GIN1 0x70
+
+/*****************************************************************************/
+
+/*
+ * Watch dog timer enable register.
+ */
+#define WDTRCR_ENABALL 0xff
+
+/*****************************************************************************/
+
+/*
+ * XON/XOFF interrupt status register.
+ */
+#define XISR_TXCHARMASK 0x03
+#define XISR_TXCHARNORMAL 0x00
+#define XISR_TXWAIT 0x01
+#define XISR_TXXOFFPEND 0x02
+#define XISR_TXXONPEND 0x03
+
+#define XISR_TXFLOWMASK 0x0c
+#define XISR_TXNORMAL 0x00
+#define XISR_TXSTOPPEND 0x04
+#define XISR_TXSTARTED 0x08
+#define XISR_TXSTOPPED 0x0c
+
+#define XISR_RXFLOWMASK 0x30
+#define XISR_RXFLOWNONE 0x00
+#define XISR_RXXONSENT 0x10
+#define XISR_RXXOFFSENT 0x20
+
+#define XISR_RXXONGOT 0x40
+#define XISR_RXXOFFGOT 0x80
+
+/*****************************************************************************/
+
+/*
+ * Current interrupt register.
+ */
+#define CIR_TYPEMASK 0xc0
+#define CIR_TYPEOTHER 0x00
+#define CIR_TYPETX 0x40
+#define CIR_TYPERXGOOD 0x80
+#define CIR_TYPERXBAD 0xc0
+
+#define CIR_RXDATA 0x80
+#define CIR_RXBADDATA 0x40
+#define CIR_TXDATA 0x40
+
+#define CIR_CHANMASK 0x07
+#define CIR_CNTMASK 0x38
+
+#define CIR_SUBTYPEMASK 0x38
+#define CIR_SUBNONE 0x00
+#define CIR_SUBCOS 0x08
+#define CIR_SUBADDR 0x10
+#define CIR_SUBXONXOFF 0x18
+#define CIR_SUBBREAK 0x28
+
+/*****************************************************************************/
+
+/*
+ * Global interrupting channel register.
+ */
+#define GICR_CHANMASK 0x07
+
+/*****************************************************************************/
+
+/*
+ * Global interrupting byte count register.
+ */
+#define GICR_COUNTMASK 0x0f
+
+/*****************************************************************************/
+
+/*
+ * Global interrupting type register.
+ */
+#define GITR_RXMASK 0xc0
+#define GITR_RXNONE 0x00
+#define GITR_RXBADDATA 0x80
+#define GITR_RXGOODDATA 0xc0
+#define GITR_TXDATA 0x20
+
+#define GITR_SUBTYPEMASK 0x07
+#define GITR_SUBNONE 0x00
+#define GITR_SUBCOS 0x01
+#define GITR_SUBADDR 0x02
+#define GITR_SUBXONXOFF 0x03
+#define GITR_SUBBREAK 0x05
+
+/*****************************************************************************/
+
+/*
+ * Input port change register.
+ */
+#define IPR_CTS 0x01
+#define IPR_DTR 0x02
+#define IPR_RTS 0x04
+#define IPR_DCD 0x08
+#define IPR_CTSCHANGE 0x10
+#define IPR_DTRCHANGE 0x20
+#define IPR_RTSCHANGE 0x40
+#define IPR_DCDCHANGE 0x80
+
+#define IPR_CHANGEMASK 0xf0
+
+/*****************************************************************************/
+
+/*
+ * IO port interrupt and output register.
+ */
+#define IOPR_CTS 0x01
+#define IOPR_DTR 0x02
+#define IOPR_RTS 0x04
+#define IOPR_DCD 0x08
+#define IOPR_CTSCOS 0x10
+#define IOPR_DTRCOS 0x20
+#define IOPR_RTSCOS 0x40
+#define IOPR_DCDCOS 0x80
+
+/*****************************************************************************/
+
+/*
+ * IO port configuration register.
+ */
+#define IOPCR_SETCTS 0x00
+#define IOPCR_SETDTR 0x04
+#define IOPCR_SETRTS 0x10
+#define IOPCR_SETDCD 0x00
+
+#define IOPCR_SETSIGS (IOPCR_SETRTS | IOPCR_SETRTS | IOPCR_SETDTR | IOPCR_SETDCD)
+
+/*****************************************************************************/
+
+/*
+ * General purpose output select register.
+ */
+#define GPORS_TXC1XA 0x08
+#define GPORS_TXC16XA 0x09
+#define GPORS_RXC16XA 0x0a
+#define GPORS_TXC16XB 0x0b
+#define GPORS_GPOR3 0x0c
+#define GPORS_GPOR2 0x0d
+#define GPORS_GPOR1 0x0e
+#define GPORS_GPOR0 0x0f
+
+/*****************************************************************************/
+
+/*
+ * General purpose output register.
+ */
+#define GPOR_0 0x01
+#define GPOR_1 0x02
+#define GPOR_2 0x04
+#define GPOR_3 0x08
+
+/*****************************************************************************/
+
+/*
+ * General purpose output clock register.
+ */
+#define GPORC_0NONE 0x00
+#define GPORC_0GIN0 0x01
+#define GPORC_0GIN1 0x02
+#define GPORC_0IO3A 0x02
+
+#define GPORC_1NONE 0x00
+#define GPORC_1GIN0 0x04
+#define GPORC_1GIN1 0x08
+#define GPORC_1IO3C 0x0c
+
+#define GPORC_2NONE 0x00
+#define GPORC_2GIN0 0x10
+#define GPORC_2GIN1 0x20
+#define GPORC_2IO3E 0x20
+
+#define GPORC_3NONE 0x00
+#define GPORC_3GIN0 0x40
+#define GPORC_3GIN1 0x80
+#define GPORC_3IO3G 0xc0
+
+/*****************************************************************************/
+
+/*
+ * General purpose output data register.
+ */
+#define GPOD_0MASK 0x03
+#define GPOD_0SET1 0x00
+#define GPOD_0SET0 0x01
+#define GPOD_0SETR0 0x02
+#define GPOD_0SETIO3B 0x03
+
+#define GPOD_1MASK 0x0c
+#define GPOD_1SET1 0x00
+#define GPOD_1SET0 0x04
+#define GPOD_1SETR0 0x08
+#define GPOD_1SETIO3D 0x0c
+
+#define GPOD_2MASK 0x30
+#define GPOD_2SET1 0x00
+#define GPOD_2SET0 0x10
+#define GPOD_2SETR0 0x20
+#define GPOD_2SETIO3F 0x30
+
+#define GPOD_3MASK 0xc0
+#define GPOD_3SET1 0x00
+#define GPOD_3SET0 0x40
+#define GPOD_3SETR0 0x80
+#define GPOD_3SETIO3H 0xc0
+
+/*****************************************************************************/
+#endif
diff --git a/pfinet/linux-src/include/linux/scc.h b/pfinet/linux-src/include/linux/scc.h
new file mode 100644
index 00000000..7ad72e10
--- /dev/null
+++ b/pfinet/linux-src/include/linux/scc.h
@@ -0,0 +1,258 @@
+/* $Id: scc.h,v 1.29 1997/04/02 14:56:45 jreuter Exp jreuter $ */
+
+#ifndef _SCC_H
+#define _SCC_H
+
+#include <linux/config.h>
+
+/* selection of hardware types */
+
+#define PA0HZP 0x00 /* hardware type for PA0HZP SCC card and compatible */
+#define EAGLE 0x01 /* hardware type for EAGLE card */
+#define PC100 0x02 /* hardware type for PC100 card */
+#define PRIMUS 0x04 /* hardware type for PRIMUS-PC (DG9BL) card */
+#define DRSI 0x08 /* hardware type for DRSI PC*Packet card */
+#define BAYCOM 0x10 /* hardware type for BayCom (U)SCC */
+
+/* DEV ioctl() commands */
+
+enum SCC_ioctl_cmds {
+ SIOCSCCRESERVED = SIOCDEVPRIVATE,
+ SIOCSCCCFG,
+ SIOCSCCINI,
+ SIOCSCCCHANINI,
+ SIOCSCCSMEM,
+ SIOCSCCGKISS,
+ SIOCSCCSKISS,
+ SIOCSCCGSTAT,
+ SIOCSCCCAL
+};
+
+/* magic number */
+
+#define SCC_MAGIC 0x8530 /* ;-) */
+
+/* Device parameter control (from WAMPES) */
+
+enum L1_params {
+ PARAM_DATA,
+ PARAM_TXDELAY,
+ PARAM_PERSIST,
+ PARAM_SLOTTIME,
+ PARAM_TXTAIL,
+ PARAM_FULLDUP,
+ PARAM_SOFTDCD, /* was: PARAM_HW */
+ PARAM_MUTE, /* ??? */
+ PARAM_DTR,
+ PARAM_RTS,
+ PARAM_SPEED,
+ PARAM_ENDDELAY, /* ??? */
+ PARAM_GROUP,
+ PARAM_IDLE,
+ PARAM_MIN,
+ PARAM_MAXKEY,
+ PARAM_WAIT,
+ PARAM_MAXDEFER,
+ PARAM_TX,
+ PARAM_HWEVENT = 31,
+ PARAM_RETURN = 255 /* reset kiss mode */
+};
+
+/* fulldup parameter */
+
+enum FULLDUP_modes {
+ KISS_DUPLEX_HALF, /* normal CSMA operation */
+ KISS_DUPLEX_FULL, /* fullduplex, key down trx after transmission */
+ KISS_DUPLEX_LINK, /* fullduplex, key down trx after 'idletime' sec */
+ KISS_DUPLEX_OPTIMA /* fullduplex, let the protocol layer control the hw */
+};
+
+/* misc. parameters */
+
+#define TIMER_OFF 65535U /* to switch off timers */
+#define NO_SUCH_PARAM 65534U /* param not implemented */
+
+/* HWEVENT parameter */
+
+enum HWEVENT_opts {
+ HWEV_DCD_ON,
+ HWEV_DCD_OFF,
+ HWEV_ALL_SENT
+};
+
+/* channel grouping */
+
+#define RXGROUP 0100 /* if set, only tx when all channels clear */
+#define TXGROUP 0200 /* if set, don't transmit simultaneously */
+
+/* Tx/Rx clock sources */
+
+enum CLOCK_sources {
+ CLK_DPLL, /* normal halfduplex operation */
+ CLK_EXTERNAL, /* external clocking (G3RUH/DF9IC modems) */
+ CLK_DIVIDER, /* Rx = DPLL, Tx = divider (fullduplex with */
+ /* modems without clock regeneration */
+ CLK_BRG /* experimental fullduplex mode with DPLL/BRG for */
+ /* MODEMs without clock recovery */
+};
+
+/* Tx state */
+
+enum TX_state {
+ TXS_IDLE, /* Transmitter off, no data pending */
+ TXS_BUSY, /* waiting for permission to send / tailtime */
+ TXS_ACTIVE, /* Transmitter on, sending data */
+ TXS_NEWFRAME, /* reset CRC and send (next) frame */
+ TXS_IDLE2, /* Transmitter on, no data pending */
+ TXS_WAIT, /* Waiting for Mintime to expire */
+ TXS_TIMEOUT /* We had a transmission timeout */
+};
+
+typedef unsigned long io_port; /* type definition for an 'io port address' */
+
+/* SCC statistical information */
+
+struct scc_stat {
+ long rxints; /* Receiver interrupts */
+ long txints; /* Transmitter interrupts */
+ long exints; /* External/status interrupts */
+ long spints; /* Special receiver interrupts */
+
+ long txframes; /* Packets sent */
+ long rxframes; /* Number of Frames Actually Received */
+ long rxerrs; /* CRC Errors */
+ long txerrs; /* KISS errors */
+
+ unsigned int nospace; /* "Out of buffers" */
+ unsigned int rx_over; /* Receiver Overruns */
+ unsigned int tx_under; /* Transmitter Underruns */
+
+ unsigned int tx_state; /* Transmitter state */
+ int tx_queued; /* tx frames enqueued */
+
+ unsigned int maxqueue; /* allocated tx_buffers */
+ unsigned int bufsize; /* used buffersize */
+};
+
+struct scc_modem {
+ long speed; /* Line speed, bps */
+ char clocksrc; /* 0 = DPLL, 1 = external, 2 = divider */
+ char nrz; /* NRZ instead of NRZI */
+};
+
+struct scc_kiss_cmd {
+ int command; /* one of the KISS-Commands defined above */
+ unsigned param; /* KISS-Param */
+};
+
+struct scc_hw_config {
+ io_port data_a; /* data port channel A */
+ io_port ctrl_a; /* control port channel A */
+ io_port data_b; /* data port channel B */
+ io_port ctrl_b; /* control port channel B */
+ io_port vector_latch; /* INTACK-Latch (#) */
+ io_port special; /* special function port */
+
+ int irq; /* irq */
+ long clock; /* clock */
+ char option; /* command for function port */
+
+ char brand; /* hardware type */
+ char escc; /* use ext. features of a 8580/85180/85280 */
+};
+
+/* (#) only one INTACK latch allowed. */
+
+
+struct scc_mem_config {
+ unsigned int dummy;
+ unsigned int bufsize;
+};
+
+struct scc_calibrate {
+ unsigned int time;
+ unsigned char pattern;
+};
+
+#ifdef __KERNEL__
+
+enum {TX_OFF, TX_ON}; /* command for scc_key_trx() */
+
+/* Vector masks in RR2B */
+
+#define VECTOR_MASK 0x06
+#define TXINT 0x00
+#define EXINT 0x02
+#define RXINT 0x04
+#define SPINT 0x06
+
+#ifdef CONFIG_SCC_DELAY
+#define Inb(port) inb_p(port)
+#define Outb(port, val) outb_p(val, port)
+#else
+#define Inb(port) inb(port)
+#define Outb(port, val) outb(val, port)
+#endif
+
+/* SCC channel control structure for KISS */
+
+struct scc_kiss {
+ unsigned char txdelay; /* Transmit Delay 10 ms/cnt */
+ unsigned char persist; /* Persistence (0-255) as a % */
+ unsigned char slottime; /* Delay to wait on persistence hit */
+ unsigned char tailtime; /* Delay after last byte written */
+ unsigned char fulldup; /* Full Duplex mode 0=CSMA 1=DUP 2=ALWAYS KEYED */
+ unsigned char waittime; /* Waittime before any transmit attempt */
+ unsigned int maxkeyup; /* Maximum time to transmit (seconds) */
+ unsigned char mintime; /* Minimal offtime after MAXKEYUP timeout (seconds) */
+ unsigned int idletime; /* Maximum idle time in ALWAYS KEYED mode (seconds) */
+ unsigned int maxdefer; /* Timer for CSMA channel busy limit */
+ unsigned char tx_inhibit; /* Transmit is not allowed when set */
+ unsigned char group; /* Group ID for AX.25 TX interlocking */
+ unsigned char mode; /* 'normal' or 'hwctrl' mode (unused) */
+ unsigned char softdcd; /* Use DPLL instead of DCD pin for carrier detect */
+};
+
+
+/* SCC channel structure */
+
+struct scc_channel {
+ int magic; /* magic word */
+
+ int init; /* channel exists? */
+
+ struct device *dev; /* link to device control structure */
+ struct net_device_stats dev_stat;/* device statistics */
+
+ char brand; /* manufacturer of the board */
+ long clock; /* used clock */
+
+ io_port ctrl; /* I/O address of CONTROL register */
+ io_port data; /* I/O address of DATA register */
+ io_port special; /* I/O address of special function port */
+ int irq; /* Number of Interrupt */
+
+ char option;
+ char enhanced; /* Enhanced SCC support */
+
+ unsigned char wreg[16]; /* Copy of last written value in WRx */
+ unsigned char status; /* Copy of R0 at last external interrupt */
+ unsigned char dcd; /* DCD status */
+
+ struct scc_kiss kiss; /* control structure for KISS params */
+ struct scc_stat stat; /* statistical information */
+ struct scc_modem modem; /* modem information */
+
+ struct sk_buff_head tx_queue; /* next tx buffer */
+ struct sk_buff *rx_buff; /* pointer to frame currently received */
+ struct sk_buff *tx_buff; /* pointer to frame currently transmitted */
+
+ /* Timer */
+
+ struct timer_list tx_t; /* tx timer for this channel */
+ struct timer_list tx_wdog; /* tx watchdogs */
+};
+
+int scc_init(void);
+#endif /* defined(__KERNEL__) */
+#endif /* defined(_SCC_H) */
diff --git a/pfinet/linux-src/include/linux/sched.h b/pfinet/linux-src/include/linux/sched.h
new file mode 100644
index 00000000..d983c17e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sched.h
@@ -0,0 +1,813 @@
+#ifndef _LINUX_SCHED_H
+#define _LINUX_SCHED_H
+
+#include <asm/param.h> /* for HZ */
+
+extern unsigned long global_event;
+
+#include <linux/binfmts.h>
+#include <linux/personality.h>
+#include <linux/tasks.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/times.h>
+#include <linux/timex.h>
+
+#include <asm/system.h>
+#include <asm/semaphore.h>
+#include <asm/page.h>
+
+#include <linux/smp.h>
+#include <linux/tty.h>
+#include <linux/sem.h>
+#include <linux/signal.h>
+#include <linux/securebits.h>
+
+/*
+ * cloning flags:
+ */
+#define CSIGNAL 0x000000ff /* signal mask to be sent at exit */
+#define CLONE_VM 0x00000100 /* set if VM shared between processes */
+#define CLONE_FS 0x00000200 /* set if fs info shared between processes */
+#define CLONE_FILES 0x00000400 /* set if open files shared between processes */
+#define CLONE_SIGHAND 0x00000800 /* set if signal handlers shared */
+#define CLONE_PID 0x00001000 /* set if pid shared */
+#define CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */
+#define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */
+
+/*
+ * These are the constant used to fake the fixed-point load-average
+ * counting. Some notes:
+ * - 11 bit fractions expand to 22 bits by the multiplies: this gives
+ * a load-average precision of 10 bits integer + 11 bits fractional
+ * - if you want to count load-averages more often, you need more
+ * precision, or rounding will get you. With 2-second counting freq,
+ * the EXP_n values would be 1981, 2034 and 2043 if still using only
+ * 11 bit fractions.
+ */
+extern unsigned long avenrun[]; /* Load averages */
+
+#define FSHIFT 11 /* nr of bits of precision */
+#define FIXED_1 (1<<FSHIFT) /* 1.0 as fixed-point */
+#define LOAD_FREQ (5*HZ) /* 5 sec intervals */
+#define EXP_1 1884 /* 1/exp(5sec/1min) as fixed-point */
+#define EXP_5 2014 /* 1/exp(5sec/5min) */
+#define EXP_15 2037 /* 1/exp(5sec/15min) */
+
+#define CALC_LOAD(load,exp,n) \
+ load *= exp; \
+ load += n*(FIXED_1-exp); \
+ load >>= FSHIFT;
+
+#define CT_TO_SECS(x) ((x) / HZ)
+#define CT_TO_USECS(x) (((x) % HZ) * 1000000/HZ)
+
+extern int nr_running, nr_tasks;
+extern int last_pid;
+
+#include <linux/fs.h>
+#include <linux/time.h>
+#include <linux/param.h>
+#include <linux/resource.h>
+#include <linux/timer.h>
+
+#include <asm/processor.h>
+
+#define TASK_RUNNING 0
+#define TASK_INTERRUPTIBLE 1
+#define TASK_UNINTERRUPTIBLE 2
+#define TASK_ZOMBIE 4
+#define TASK_STOPPED 8
+#define TASK_SWAPPING 16
+
+/*
+ * Scheduling policies
+ */
+#define SCHED_OTHER 0
+#define SCHED_FIFO 1
+#define SCHED_RR 2
+
+/*
+ * This is an additional bit set when we want to
+ * yield the CPU for one re-schedule..
+ */
+#define SCHED_YIELD 0x10
+
+struct sched_param {
+ int sched_priority;
+};
+
+#ifdef __KERNEL__
+
+#include <asm/spinlock.h>
+
+/*
+ * This serializes "schedule()" and also protects
+ * the run-queue from deletions/modifications (but
+ * _adding_ to the beginning of the run-queue has
+ * a separate lock).
+ */
+extern rwlock_t tasklist_lock;
+extern spinlock_t runqueue_lock;
+
+extern void sched_init(void);
+extern void init_idle(void);
+extern void show_state(void);
+extern void trap_init(void);
+
+#define MAX_SCHEDULE_TIMEOUT LONG_MAX
+extern signed long FASTCALL(schedule_timeout(signed long timeout));
+asmlinkage void schedule(void);
+
+/*
+ * The default fd array needs to be at least BITS_PER_LONG,
+ * as this is the granularity returned by copy_fdset().
+ */
+#define NR_OPEN_DEFAULT BITS_PER_LONG
+
+/*
+ * Open file table structure
+ */
+struct files_struct {
+ atomic_t count;
+ int max_fds;
+ int max_fdset;
+ int next_fd;
+ struct file ** fd; /* current fd array */
+ fd_set *close_on_exec;
+ fd_set *open_fds;
+ fd_set close_on_exec_init;
+ fd_set open_fds_init;
+ struct file * fd_array[NR_OPEN_DEFAULT];
+};
+
+#define INIT_FILES { \
+ ATOMIC_INIT(1), \
+ NR_OPEN_DEFAULT, \
+ __FD_SETSIZE, \
+ 0, \
+ &init_files.fd_array[0], \
+ &init_files.close_on_exec_init, \
+ &init_files.open_fds_init, \
+ { { 0, } }, \
+ { { 0, } }, \
+ { NULL, } \
+}
+
+struct fs_struct {
+ atomic_t count;
+ int umask;
+ struct dentry * root, * pwd;
+};
+
+#define INIT_FS { \
+ ATOMIC_INIT(1), \
+ 0022, \
+ NULL, NULL \
+}
+
+/* Maximum number of active map areas.. This is a random (large) number */
+#define MAX_MAP_COUNT (65536)
+
+/* Number of map areas at which the AVL tree is activated. This is arbitrary. */
+#define AVL_MIN_MAP_COUNT 32
+
+struct mm_struct {
+ struct vm_area_struct *mmap; /* list of VMAs */
+ struct vm_area_struct *mmap_avl; /* tree of VMAs */
+ struct vm_area_struct *mmap_cache; /* last find_vma result */
+ pgd_t * pgd;
+ atomic_t count;
+ int map_count; /* number of VMAs */
+ struct semaphore mmap_sem;
+ unsigned long context;
+ unsigned long start_code, end_code, start_data, end_data;
+ unsigned long start_brk, brk, start_stack;
+ unsigned long arg_start, arg_end, env_start, env_end;
+ unsigned long rss, total_vm, locked_vm;
+ unsigned long def_flags;
+ unsigned long cpu_vm_mask;
+ unsigned long swap_cnt; /* number of pages to swap on next pass */
+ unsigned long swap_address;
+ /*
+ * This is an architecture-specific pointer: the portable
+ * part of Linux does not know about any segments.
+ */
+ void * segments;
+};
+
+#define INIT_MM { \
+ &init_mmap, NULL, NULL, \
+ swapper_pg_dir, \
+ ATOMIC_INIT(1), 1, \
+ MUTEX, \
+ 0, \
+ 0, 0, 0, 0, \
+ 0, 0, 0, \
+ 0, 0, 0, 0, \
+ 0, 0, 0, \
+ 0, 0, 0, 0, NULL }
+
+struct signal_struct {
+ atomic_t count;
+ struct k_sigaction action[_NSIG];
+ spinlock_t siglock;
+};
+
+
+#define INIT_SIGNALS { \
+ ATOMIC_INIT(1), \
+ { {{0,}}, }, \
+ SPIN_LOCK_UNLOCKED }
+
+/*
+ * Some day this will be a full-fledged user tracking system..
+ * Right now it is only used to track how many processes a
+ * user has, but it has the potential to track memory usage etc.
+ */
+struct user_struct;
+
+struct task_struct {
+/* these are hardcoded - don't touch */
+ volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
+ unsigned long flags; /* per process flags, defined below */
+ int sigpending;
+ mm_segment_t addr_limit; /* thread address space:
+ 0-0xBFFFFFFF for user-thead
+ 0-0xFFFFFFFF for kernel-thread
+ */
+ struct exec_domain *exec_domain;
+ long need_resched;
+
+/* various fields */
+ long counter;
+ long priority;
+ cycles_t avg_slice;
+/* SMP and runqueue state */
+ int has_cpu;
+ int processor;
+ int last_processor;
+ int lock_depth; /* Lock depth. We can context switch in and out of holding a syscall kernel lock... */
+ struct task_struct *next_task, *prev_task;
+ struct task_struct *next_run, *prev_run;
+
+/* task state */
+ struct linux_binfmt *binfmt;
+ int exit_code, exit_signal;
+ int pdeath_signal; /* The signal sent when the parent dies */
+ /* ??? */
+ unsigned long personality;
+ int dumpable:1;
+ int did_exec:1;
+ pid_t pid;
+ pid_t pgrp;
+ pid_t tty_old_pgrp;
+ pid_t session;
+ /* boolean value for session group leader */
+ int leader;
+ /*
+ * pointers to (original) parent process, youngest child, younger sibling,
+ * older sibling, respectively. (p->father can be replaced with
+ * p->p_pptr->pid)
+ */
+ struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
+
+ /* PID hash table linkage. */
+ struct task_struct *pidhash_next;
+ struct task_struct **pidhash_pprev;
+
+ /* Pointer to task[] array linkage. */
+ struct task_struct **tarray_ptr;
+
+ struct wait_queue *wait_chldexit; /* for wait4() */
+ struct semaphore *vfork_sem; /* for vfork() */
+ unsigned long policy, rt_priority;
+ unsigned long it_real_value, it_prof_value, it_virt_value;
+ unsigned long it_real_incr, it_prof_incr, it_virt_incr;
+ struct timer_list real_timer;
+ struct tms times;
+ unsigned long start_time;
+ long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];
+/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
+ unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
+ int swappable:1;
+/* process credentials */
+ uid_t uid,euid,suid,fsuid;
+ gid_t gid,egid,sgid,fsgid;
+ int ngroups;
+ gid_t groups[NGROUPS];
+ kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
+ struct user_struct *user;
+/* limits */
+ struct rlimit rlim[RLIM_NLIMITS];
+ unsigned short used_math;
+ char comm[16];
+/* file system info */
+ int link_count;
+ struct tty_struct *tty; /* NULL if no tty */
+/* ipc stuff */
+ struct sem_undo *semundo;
+ struct sem_queue *semsleeping;
+/* tss for this task */
+ struct thread_struct tss;
+/* filesystem information */
+ struct fs_struct *fs;
+/* open file information */
+ struct files_struct *files;
+/* memory management info */
+ struct mm_struct *mm;
+
+/* signal handlers */
+ spinlock_t sigmask_lock; /* Protects signal and blocked */
+ struct signal_struct *sig;
+ sigset_t signal, blocked;
+ struct signal_queue *sigqueue, **sigqueue_tail;
+ unsigned long sas_ss_sp;
+ size_t sas_ss_size;
+
+/* Thread group tracking */
+ u32 parent_exec_id;
+ u32 self_exec_id;
+};
+
+/*
+ * Per process flags
+ */
+#define PF_ALIGNWARN 0x00000001 /* Print alignment warning msgs */
+ /* Not implemented yet, only for 486*/
+#define PF_STARTING 0x00000002 /* being created */
+#define PF_EXITING 0x00000004 /* getting shut down */
+#define PF_PTRACED 0x00000010 /* set if ptrace (0) has been called */
+#define PF_TRACESYS 0x00000020 /* tracing system calls */
+#define PF_FORKNOEXEC 0x00000040 /* forked but didn't exec */
+#define PF_SUPERPRIV 0x00000100 /* used super-user privileges */
+#define PF_DUMPCORE 0x00000200 /* dumped core */
+#define PF_SIGNALED 0x00000400 /* killed by a signal */
+#define PF_MEMALLOC 0x00000800 /* Allocating memory */
+#define PF_VFORK 0x00001000 /* Wake up parent in mm_release */
+
+#define PF_USEDFPU 0x00100000 /* task used FPU this quantum (SMP) */
+#define PF_DTRACE 0x00200000 /* delayed trace (used on m68k, i386) */
+
+/*
+ * Limit the stack by to some sane default: root can always
+ * increase this limit if needed.. 8MB seems reasonable.
+ */
+#define _STK_LIM (8*1024*1024)
+
+#define DEF_PRIORITY (20*HZ/100) /* 210 ms time slices */
+
+/*
+ * INIT_TASK is used to set up the first task table, touch at
+ * your own risk!. Base=0, limit=0x1fffff (=2MB)
+ */
+#define INIT_TASK \
+/* state etc */ { 0,0,0,KERNEL_DS,&default_exec_domain,0, \
+/* counter */ DEF_PRIORITY,DEF_PRIORITY,0, \
+/* SMP */ 0,0,0,-1, \
+/* schedlink */ &init_task,&init_task, &init_task, &init_task, \
+/* binfmt */ NULL, \
+/* ec,brk... */ 0,0,0,0,0,0, \
+/* pid etc.. */ 0,0,0,0,0, \
+/* proc links*/ &init_task,&init_task,NULL,NULL,NULL, \
+/* pidhash */ NULL, NULL, \
+/* tarray */ &task[0], \
+/* chld wait */ NULL, NULL, \
+/* timeout */ SCHED_OTHER,0,0,0,0,0,0,0, \
+/* timer */ { NULL, NULL, 0, 0, it_real_fn }, \
+/* utime */ {0,0,0,0},0, \
+/* per CPU times */ {0, }, {0, }, \
+/* flt */ 0,0,0,0,0,0, \
+/* swp */ 0, \
+/* process credentials */ \
+/* uid etc */ 0,0,0,0,0,0,0,0, \
+/* suppl grps*/ 0, {0,}, \
+/* caps */ CAP_INIT_EFF_SET,CAP_INIT_INH_SET,CAP_FULL_SET, \
+/* user */ NULL, \
+/* rlimits */ INIT_RLIMITS, \
+/* math */ 0, \
+/* comm */ "swapper", \
+/* fs info */ 0,NULL, \
+/* ipc */ NULL, NULL, \
+/* tss */ INIT_TSS, \
+/* fs */ &init_fs, \
+/* files */ &init_files, \
+/* mm */ &init_mm, \
+/* signals */ SPIN_LOCK_UNLOCKED, &init_signals, {{0}}, {{0}}, NULL, &init_task.sigqueue, 0, 0, \
+/* exec cts */ 0,0, \
+}
+
+union task_union {
+ struct task_struct task;
+ unsigned long stack[2048];
+};
+
+extern union task_union init_task_union;
+
+extern struct mm_struct init_mm;
+extern struct task_struct *task[NR_TASKS];
+
+extern struct task_struct **tarray_freelist;
+extern spinlock_t taskslot_lock;
+
+extern __inline__ void add_free_taskslot(struct task_struct **t)
+{
+ spin_lock(&taskslot_lock);
+ *t = (struct task_struct *) tarray_freelist;
+ tarray_freelist = t;
+ spin_unlock(&taskslot_lock);
+}
+
+extern __inline__ struct task_struct **get_free_taskslot(void)
+{
+ struct task_struct **tslot;
+
+ spin_lock(&taskslot_lock);
+ if((tslot = tarray_freelist) != NULL)
+ tarray_freelist = (struct task_struct **) *tslot;
+ spin_unlock(&taskslot_lock);
+
+ return tslot;
+}
+
+/* PID hashing. */
+#define PIDHASH_SZ (NR_TASKS >> 2)
+extern struct task_struct *pidhash[PIDHASH_SZ];
+
+#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
+
+extern __inline__ void hash_pid(struct task_struct *p)
+{
+ struct task_struct **htable = &pidhash[pid_hashfn(p->pid)];
+
+ if((p->pidhash_next = *htable) != NULL)
+ (*htable)->pidhash_pprev = &p->pidhash_next;
+ *htable = p;
+ p->pidhash_pprev = htable;
+}
+
+extern __inline__ void unhash_pid(struct task_struct *p)
+{
+ if(p->pidhash_next)
+ p->pidhash_next->pidhash_pprev = p->pidhash_pprev;
+ *p->pidhash_pprev = p->pidhash_next;
+}
+
+extern __inline__ struct task_struct *find_task_by_pid(int pid)
+{
+ struct task_struct *p, **htable = &pidhash[pid_hashfn(pid)];
+
+ for(p = *htable; p && p->pid != pid; p = p->pidhash_next)
+ ;
+
+ return p;
+}
+
+/* per-UID process charging. */
+extern int alloc_uid(struct task_struct *p);
+void free_uid(struct task_struct *p);
+
+#include <asm/current.h>
+
+extern unsigned long volatile jiffies;
+extern unsigned long itimer_ticks;
+extern unsigned long itimer_next;
+extern struct timeval xtime;
+extern void do_timer(struct pt_regs *);
+
+extern unsigned int * prof_buffer;
+extern unsigned long prof_len;
+extern unsigned long prof_shift;
+
+#define CURRENT_TIME (xtime.tv_sec)
+
+extern void FASTCALL(__wake_up(struct wait_queue ** p, unsigned int mode));
+extern void FASTCALL(sleep_on(struct wait_queue ** p));
+extern long FASTCALL(sleep_on_timeout(struct wait_queue ** p,
+ signed long timeout));
+extern void FASTCALL(interruptible_sleep_on(struct wait_queue ** p));
+extern long FASTCALL(interruptible_sleep_on_timeout(struct wait_queue ** p,
+ signed long timeout));
+extern void FASTCALL(wake_up_process(struct task_struct * tsk));
+
+#define wake_up(x) __wake_up((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE)
+#define wake_up_interruptible(x) __wake_up((x),TASK_INTERRUPTIBLE)
+
+extern int in_group_p(gid_t grp);
+
+extern void flush_signals(struct task_struct *);
+extern void flush_signal_handlers(struct task_struct *);
+extern int dequeue_signal(sigset_t *block, siginfo_t *);
+extern int send_sig_info(int, struct siginfo *info, struct task_struct *);
+extern int force_sig_info(int, struct siginfo *info, struct task_struct *);
+extern int kill_pg_info(int, struct siginfo *info, pid_t);
+extern int kill_sl_info(int, struct siginfo *info, pid_t);
+extern int kill_proc_info(int, struct siginfo *info, pid_t);
+extern int kill_something_info(int, struct siginfo *info, int);
+extern void notify_parent(struct task_struct * tsk, int);
+extern void force_sig(int sig, struct task_struct * p);
+extern int send_sig(int sig, struct task_struct * p, int priv);
+extern int kill_pg(pid_t, int, int);
+extern int kill_sl(pid_t, int, int);
+extern int kill_proc(pid_t, int, int);
+extern int do_sigaction(int sig, const struct k_sigaction *act,
+ struct k_sigaction *oact);
+extern int do_sigaltstack(const stack_t *ss, stack_t *oss, unsigned long sp);
+
+extern inline int signal_pending(struct task_struct *p)
+{
+ return (p->sigpending != 0);
+}
+
+/* Reevaluate whether the task has signals pending delivery.
+ This is required every time the blocked sigset_t changes.
+ All callers should have t->sigmask_lock. */
+
+static inline void recalc_sigpending(struct task_struct *t)
+{
+ unsigned long ready;
+ long i;
+
+ switch (_NSIG_WORDS) {
+ default:
+ for (i = _NSIG_WORDS, ready = 0; --i >= 0 ;)
+ ready |= t->signal.sig[i] &~ t->blocked.sig[i];
+ break;
+
+ case 4: ready = t->signal.sig[3] &~ t->blocked.sig[3];
+ ready |= t->signal.sig[2] &~ t->blocked.sig[2];
+ ready |= t->signal.sig[1] &~ t->blocked.sig[1];
+ ready |= t->signal.sig[0] &~ t->blocked.sig[0];
+ break;
+
+ case 2: ready = t->signal.sig[1] &~ t->blocked.sig[1];
+ ready |= t->signal.sig[0] &~ t->blocked.sig[0];
+ break;
+
+ case 1: ready = t->signal.sig[0] &~ t->blocked.sig[0];
+ }
+
+ t->sigpending = (ready != 0);
+}
+
+/* True if we are on the alternate signal stack. */
+
+static inline int on_sig_stack(unsigned long sp)
+{
+ return (sp >= current->sas_ss_sp
+ && sp < current->sas_ss_sp + current->sas_ss_size);
+}
+
+static inline int sas_ss_flags(unsigned long sp)
+{
+ return (current->sas_ss_size == 0 ? SS_DISABLE
+ : on_sig_stack(sp) ? SS_ONSTACK : 0);
+}
+
+extern int request_irq(unsigned int irq,
+ void (*handler)(int, void *, struct pt_regs *),
+ unsigned long flags,
+ const char *device,
+ void *dev_id);
+extern void free_irq(unsigned int irq, void *dev_id);
+
+/*
+ * This has now become a routine instead of a macro, it sets a flag if
+ * it returns true (to do BSD-style accounting where the process is flagged
+ * if it uses root privs). The implication of this is that you should do
+ * normal permissions checks first, and check suser() last.
+ *
+ * [Dec 1997 -- Chris Evans]
+ * For correctness, the above considerations need to be extended to
+ * fsuser(). This is done, along with moving fsuser() checks to be
+ * last.
+ *
+ * These will be removed, but in the mean time, when the SECURE_NOROOT
+ * flag is set, uids don't grant privilege.
+ */
+extern inline int suser(void)
+{
+ if (!issecure(SECURE_NOROOT) && current->euid == 0) {
+ current->flags |= PF_SUPERPRIV;
+ return 1;
+ }
+ return 0;
+}
+
+extern inline int fsuser(void)
+{
+ if (!issecure(SECURE_NOROOT) && current->fsuid == 0) {
+ current->flags |= PF_SUPERPRIV;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * capable() checks for a particular capability.
+ * New privilege checks should use this interface, rather than suser() or
+ * fsuser(). See include/linux/capability.h for defined capabilities.
+ */
+
+extern inline int capable(int cap)
+{
+#if 1 /* ok now */
+ if (cap_raised(current->cap_effective, cap))
+#else
+ if (cap_is_fs_cap(cap) ? current->fsuid == 0 : current->euid == 0)
+#endif
+ {
+ current->flags |= PF_SUPERPRIV;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Routines for handling mm_structs
+ */
+extern struct mm_struct * mm_alloc(void);
+static inline void mmget(struct mm_struct * mm)
+{
+ atomic_inc(&mm->count);
+}
+extern void mmput(struct mm_struct *);
+/* Remove the current tasks stale references to the old mm_struct */
+extern void mm_release(void);
+
+/*
+ * Routines for handling the fd arrays
+ */
+extern struct file ** alloc_fd_array(int);
+extern int expand_fd_array(struct files_struct *, int nr);
+extern void free_fd_array(struct file **, int);
+
+extern fd_set *alloc_fdset(int);
+extern int expand_fdset(struct files_struct *, int nr);
+extern void free_fdset(fd_set *, int);
+
+/* Expand files. Return <0 on error; 0 nothing done; 1 files expanded,
+ * we may have blocked. */
+static inline int expand_files(struct files_struct *files, int nr)
+{
+ int err, expand = 0;
+#ifdef FDSET_DEBUG
+ printk (KERN_ERR __FUNCTION__ " %d: nr = %d\n", current->pid, nr);
+#endif
+
+ if (nr >= files->max_fdset) {
+ expand = 1;
+ if ((err = expand_fdset(files, nr + 1)))
+ goto out;
+ }
+ if (nr >= files->max_fds) {
+ expand = 1;
+ if ((err = expand_fd_array(files, nr + 1)))
+ goto out;
+ }
+ err = expand;
+ out:
+#ifdef FDSET_DEBUG
+ if (err)
+ printk (KERN_ERR __FUNCTION__ " %d: return %d\n", current->pid, err);
+#endif
+ return err;
+}
+
+extern int copy_thread(int, unsigned long, unsigned long, struct task_struct *, struct pt_regs *);
+extern void flush_thread(void);
+extern void exit_thread(void);
+
+extern void exit_mm(struct task_struct *);
+extern void exit_fs(struct task_struct *);
+extern void exit_files(struct task_struct *);
+extern void exit_sighand(struct task_struct *);
+
+extern int do_execve(char *, char **, char **, struct pt_regs *);
+extern int do_fork(unsigned long, unsigned long, struct pt_regs *);
+
+/*
+ * The wait-queues are circular lists, and you have to be *very* sure
+ * to keep them correct. Use only these two functions to add/remove
+ * entries in the queues.
+ */
+extern inline void __add_wait_queue(struct wait_queue ** p, struct wait_queue * wait)
+{
+ wait->next = *p ? : WAIT_QUEUE_HEAD(p);
+ *p = wait;
+}
+
+extern rwlock_t waitqueue_lock;
+
+extern inline void add_wait_queue(struct wait_queue ** p, struct wait_queue * wait)
+{
+ unsigned long flags;
+
+ write_lock_irqsave(&waitqueue_lock, flags);
+ __add_wait_queue(p, wait);
+ write_unlock_irqrestore(&waitqueue_lock, flags);
+}
+
+extern inline void __remove_wait_queue(struct wait_queue ** p, struct wait_queue * wait)
+{
+ struct wait_queue * next = wait->next;
+ struct wait_queue * head = next;
+ struct wait_queue * tmp;
+
+ while ((tmp = head->next) != wait) {
+ head = tmp;
+ }
+ head->next = next;
+}
+
+extern inline void remove_wait_queue(struct wait_queue ** p, struct wait_queue * wait)
+{
+ unsigned long flags;
+
+ write_lock_irqsave(&waitqueue_lock, flags);
+ __remove_wait_queue(p, wait);
+ write_unlock_irqrestore(&waitqueue_lock, flags);
+}
+
+#define __wait_event(wq, condition) \
+do { \
+ struct wait_queue __wait; \
+ \
+ __wait.task = current; \
+ add_wait_queue(&wq, &__wait); \
+ for (;;) { \
+ current->state = TASK_UNINTERRUPTIBLE; \
+ mb(); \
+ if (condition) \
+ break; \
+ schedule(); \
+ } \
+ current->state = TASK_RUNNING; \
+ remove_wait_queue(&wq, &__wait); \
+} while (0)
+
+#define wait_event(wq, condition) \
+do { \
+ if (condition) \
+ break; \
+ __wait_event(wq, condition); \
+} while (0)
+
+#define __wait_event_interruptible(wq, condition, ret) \
+do { \
+ struct wait_queue __wait; \
+ \
+ __wait.task = current; \
+ add_wait_queue(&wq, &__wait); \
+ for (;;) { \
+ current->state = TASK_INTERRUPTIBLE; \
+ mb(); \
+ if (condition) \
+ break; \
+ if (!signal_pending(current)) { \
+ schedule(); \
+ continue; \
+ } \
+ ret = -ERESTARTSYS; \
+ break; \
+ } \
+ current->state = TASK_RUNNING; \
+ remove_wait_queue(&wq, &__wait); \
+} while (0)
+
+#define wait_event_interruptible(wq, condition) \
+({ \
+ int __ret = 0; \
+ if (!(condition)) \
+ __wait_event_interruptible(wq, condition, __ret); \
+ __ret; \
+})
+
+#define REMOVE_LINKS(p) do { \
+ (p)->next_task->prev_task = (p)->prev_task; \
+ (p)->prev_task->next_task = (p)->next_task; \
+ if ((p)->p_osptr) \
+ (p)->p_osptr->p_ysptr = (p)->p_ysptr; \
+ if ((p)->p_ysptr) \
+ (p)->p_ysptr->p_osptr = (p)->p_osptr; \
+ else \
+ (p)->p_pptr->p_cptr = (p)->p_osptr; \
+ } while (0)
+
+#define SET_LINKS(p) do { \
+ (p)->next_task = &init_task; \
+ (p)->prev_task = init_task.prev_task; \
+ init_task.prev_task->next_task = (p); \
+ init_task.prev_task = (p); \
+ (p)->p_ysptr = NULL; \
+ if (((p)->p_osptr = (p)->p_pptr->p_cptr) != NULL) \
+ (p)->p_osptr->p_ysptr = p; \
+ (p)->p_pptr->p_cptr = p; \
+ } while (0)
+
+#define for_each_task(p) \
+ for (p = &init_task ; (p = p->next_task) != &init_task ; )
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/sdla.h b/pfinet/linux-src/include/linux/sdla.h
new file mode 100644
index 00000000..44ae55b1
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sdla.h
@@ -0,0 +1,339 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the Frame relay interface.
+ *
+ * Version: @(#)if_ifrad.h 0.20 13 Apr 96
+ *
+ * Author: Mike McLagan <mike.mclagan@linux.org>
+ *
+ * Changes:
+ * 0.15 Mike McLagan Structure packing
+ *
+ * 0.20 Mike McLagan New flags for S508 buffer handling
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef SDLA_H
+#define SDLA_H
+
+/* adapter type */
+#define SDLA_TYPES
+#define SDLA_S502A 5020
+#define SDLA_S502E 5021
+#define SDLA_S503 5030
+#define SDLA_S507 5070
+#define SDLA_S508 5080
+#define SDLA_S509 5090
+#define SDLA_UNKNOWN -1
+
+/* port selection flags for the S508 */
+#define SDLA_S508_PORT_V35 0x00
+#define SDLA_S508_PORT_RS232 0x02
+
+/* Z80 CPU speeds */
+#define SDLA_CPU_3M 0x00
+#define SDLA_CPU_5M 0x01
+#define SDLA_CPU_7M 0x02
+#define SDLA_CPU_8M 0x03
+#define SDLA_CPU_10M 0x04
+#define SDLA_CPU_16M 0x05
+#define SDLA_CPU_12M 0x06
+
+/* some private IOCTLs */
+#define SDLA_IDENTIFY (FRAD_LAST_IOCTL + 1)
+#define SDLA_CPUSPEED (FRAD_LAST_IOCTL + 2)
+#define SDLA_PROTOCOL (FRAD_LAST_IOCTL + 3)
+
+#define SDLA_CLEARMEM (FRAD_LAST_IOCTL + 4)
+#define SDLA_WRITEMEM (FRAD_LAST_IOCTL + 5)
+#define SDLA_READMEM (FRAD_LAST_IOCTL + 6)
+
+struct sdla_mem {
+ int addr;
+ int len;
+ void *data;
+};
+
+#define SDLA_START (FRAD_LAST_IOCTL + 7)
+#define SDLA_STOP (FRAD_LAST_IOCTL + 8)
+
+/* some offsets in the Z80's memory space */
+#define SDLA_NMIADDR 0x0000
+#define SDLA_CONF_ADDR 0x0010
+#define SDLA_S502A_NMIADDR 0x0066
+#define SDLA_CODE_BASEADDR 0x0100
+#define SDLA_WINDOW_SIZE 0x2000
+#define SDLA_ADDR_MASK 0x1FFF
+
+/* largest handleable block of data */
+#define SDLA_MAX_DATA 4080
+#define SDLA_MAX_MTU 4072 /* MAX_DATA - sizeof(fradhdr) */
+#define SDLA_MAX_DLCI 24
+
+/* this should be the same as frad_conf */
+struct sdla_conf {
+ short station;
+ short config;
+ short kbaud;
+ short clocking;
+ short max_frm;
+ short T391;
+ short T392;
+ short N391;
+ short N392;
+ short N393;
+ short CIR_fwd;
+ short Bc_fwd;
+ short Be_fwd;
+ short CIR_bwd;
+ short Bc_bwd;
+ short Be_bwd;
+};
+
+/* this should be the same as dlci_conf */
+struct sdla_dlci_conf {
+ short config;
+ short CIR_fwd;
+ short Bc_fwd;
+ short Be_fwd;
+ short CIR_bwd;
+ short Bc_bwd;
+ short Be_bwd;
+ short Tc_fwd;
+ short Tc_bwd;
+ short Tf_max;
+ short Tb_max;
+};
+
+#ifndef __KERNEL__
+
+void sdla(void *cfg_info, char *dev, struct frad_conf *conf, int quiet);
+
+#else
+
+/* important Z80 window addresses */
+#define SDLA_CONTROL_WND 0xE000
+
+#define SDLA_502_CMD_BUF 0xEF60
+#define SDLA_502_RCV_BUF 0xA900
+#define SDLA_502_TXN_AVAIL 0xFFF1
+#define SDLA_502_RCV_AVAIL 0xFFF2
+#define SDLA_502_EVENT_FLAGS 0xFFF3
+#define SDLA_502_MDM_STATUS 0xFFF4
+#define SDLA_502_IRQ_INTERFACE 0xFFFD
+#define SDLA_502_IRQ_PERMISSION 0xFFFE
+#define SDLA_502_DATA_OFS 0x0010
+
+#define SDLA_508_CMD_BUF 0xE000
+#define SDLA_508_TXBUF_INFO 0xF100
+#define SDLA_508_RXBUF_INFO 0xF120
+#define SDLA_508_EVENT_FLAGS 0xF003
+#define SDLA_508_MDM_STATUS 0xF004
+#define SDLA_508_IRQ_INTERFACE 0xF010
+#define SDLA_508_IRQ_PERMISSION 0xF011
+#define SDLA_508_TSE_OFFSET 0xF012
+
+/* Event flags */
+#define SDLA_EVENT_STATUS 0x01
+#define SDLA_EVENT_DLCI_STATUS 0x02
+#define SDLA_EVENT_BAD_DLCI 0x04
+#define SDLA_EVENT_LINK_DOWN 0x40
+
+/* IRQ Trigger flags */
+#define SDLA_INTR_RX 0x01
+#define SDLA_INTR_TX 0x02
+#define SDLA_INTR_MODEM 0x04
+#define SDLA_INTR_COMPLETE 0x08
+#define SDLA_INTR_STATUS 0x10
+#define SDLA_INTR_TIMER 0x20
+
+/* DLCI status bits */
+#define SDLA_DLCI_DELETED 0x01
+#define SDLA_DLCI_ACTIVE 0x02
+#define SDLA_DLCI_WAITING 0x04
+#define SDLA_DLCI_NEW 0x08
+#define SDLA_DLCI_INCLUDED 0x40
+
+/* valid command codes */
+#define SDLA_INFORMATION_WRITE 0x01
+#define SDLA_INFORMATION_READ 0x02
+#define SDLA_ISSUE_IN_CHANNEL_SIGNAL 0x03
+#define SDLA_SET_DLCI_CONFIGURATION 0x10
+#define SDLA_READ_DLCI_CONFIGURATION 0x11
+#define SDLA_DISABLE_COMMUNICATIONS 0x12
+#define SDLA_ENABLE_COMMUNICATIONS 0x13
+#define SDLA_READ_DLC_STATUS 0x14
+#define SDLA_READ_DLC_STATISTICS 0x15
+#define SDLA_FLUSH_DLC_STATISTICS 0x16
+#define SDLA_LIST_ACTIVE_DLCI 0x17
+#define SDLA_FLUSH_INFORMATION_BUFFERS 0x18
+#define SDLA_ADD_DLCI 0x20
+#define SDLA_DELETE_DLCI 0x21
+#define SDLA_ACTIVATE_DLCI 0x22
+#define SDLA_DEACTIVATE_DLCI 0x23
+#define SDLA_READ_MODEM_STATUS 0x30
+#define SDLA_SET_MODEM_STATUS 0x31
+#define SDLA_READ_COMMS_ERR_STATS 0x32
+#define SDLA_FLUSH_COMMS_ERR_STATS 0x33
+#define SDLA_READ_CODE_VERSION 0x40
+#define SDLA_SET_IRQ_TRIGGER 0x50
+#define SDLA_GET_IRQ_TRIGGER 0x51
+
+/* In channel signal types */
+#define SDLA_ICS_LINK_VERIFY 0x02
+#define SDLA_ICS_STATUS_ENQ 0x03
+
+/* modem status flags */
+#define SDLA_MODEM_DTR_HIGH 0x01
+#define SDLA_MODEM_RTS_HIGH 0x02
+#define SDLA_MODEM_DCD_HIGH 0x08
+#define SDLA_MODEM_CTS_HIGH 0x20
+
+/* used for RET_MODEM interpretation */
+#define SDLA_MODEM_DCD_LOW 0x01
+#define SDLA_MODEM_CTS_LOW 0x02
+
+/* return codes */
+#define SDLA_RET_OK 0x00
+#define SDLA_RET_COMMUNICATIONS 0x01
+#define SDLA_RET_CHANNEL_INACTIVE 0x02
+#define SDLA_RET_DLCI_INACTIVE 0x03
+#define SDLA_RET_DLCI_CONFIG 0x04
+#define SDLA_RET_BUF_TOO_BIG 0x05
+#define SDLA_RET_NO_DATA 0x05
+#define SDLA_RET_BUF_OVERSIZE 0x06
+#define SDLA_RET_CIR_OVERFLOW 0x07
+#define SDLA_RET_NO_BUFS 0x08
+#define SDLA_RET_TIMEOUT 0x0A
+#define SDLA_RET_MODEM 0x10
+#define SDLA_RET_CHANNEL_OFF 0x11
+#define SDLA_RET_CHANNEL_ON 0x12
+#define SDLA_RET_DLCI_STATUS 0x13
+#define SDLA_RET_DLCI_UNKNOWN 0x14
+#define SDLA_RET_COMMAND_INVALID 0x1F
+
+/* Configuration flags */
+#define SDLA_DIRECT_RECV 0x0080
+#define SDLA_TX_NO_EXCEPT 0x0020
+#define SDLA_NO_ICF_MSGS 0x1000
+#define SDLA_TX50_RX50 0x0000
+#define SDLA_TX70_RX30 0x2000
+#define SDLA_TX30_RX70 0x4000
+
+/* IRQ selection flags */
+#define SDLA_IRQ_RECEIVE 0x01
+#define SDLA_IRQ_TRANSMIT 0x02
+#define SDLA_IRQ_MODEM_STAT 0x04
+#define SDLA_IRQ_COMMAND 0x08
+#define SDLA_IRQ_CHANNEL 0x10
+#define SDLA_IRQ_TIMER 0x20
+
+/* definitions for PC memory mapping */
+#define SDLA_8K_WINDOW 0x01
+#define SDLA_S502_SEG_A 0x10
+#define SDLA_S502_SEG_C 0x20
+#define SDLA_S502_SEG_D 0x00
+#define SDLA_S502_SEG_E 0x30
+#define SDLA_S507_SEG_A 0x00
+#define SDLA_S507_SEG_B 0x40
+#define SDLA_S507_SEG_C 0x80
+#define SDLA_S507_SEG_E 0xC0
+#define SDLA_S508_SEG_A 0x00
+#define SDLA_S508_SEG_C 0x10
+#define SDLA_S508_SEG_D 0x08
+#define SDLA_S508_SEG_E 0x18
+
+/* SDLA adapter port constants */
+#define SDLA_IO_EXTENTS 0x04
+
+#define SDLA_REG_CONTROL 0x00
+#define SDLA_REG_PC_WINDOW 0x01 /* offset for PC window select latch */
+#define SDLA_REG_Z80_WINDOW 0x02 /* offset for Z80 window select latch */
+#define SDLA_REG_Z80_CONTROL 0x03 /* offset for Z80 control latch */
+
+#define SDLA_S502_STS 0x00 /* status reg for 502, 502E, 507 */
+#define SDLA_S508_GNRL 0x00 /* general purp. reg for 508 */
+#define SDLA_S508_STS 0x01 /* status reg for 508 */
+#define SDLA_S508_IDR 0x02 /* ID reg for 508 */
+
+/* control register flags */
+#define SDLA_S502A_START 0x00 /* start the CPU */
+#define SDLA_S502A_INTREQ 0x02
+#define SDLA_S502A_INTEN 0x04
+#define SDLA_S502A_HALT 0x08 /* halt the CPU */
+#define SDLA_S502A_NMI 0x10 /* issue an NMI to the CPU */
+
+#define SDLA_S502E_CPUEN 0x01
+#define SDLA_S502E_ENABLE 0x02
+#define SDLA_S502E_INTACK 0x04
+
+#define SDLA_S507_ENABLE 0x01
+#define SDLA_S507_IRQ3 0x00
+#define SDLA_S507_IRQ4 0x20
+#define SDLA_S507_IRQ5 0x40
+#define SDLA_S507_IRQ7 0x60
+#define SDLA_S507_IRQ10 0x80
+#define SDLA_S507_IRQ11 0xA0
+#define SDLA_S507_IRQ12 0xC0
+#define SDLA_S507_IRQ15 0xE0
+
+#define SDLA_HALT 0x00
+#define SDLA_CPUEN 0x02
+#define SDLA_MEMEN 0x04
+#define SDLA_S507_EPROMWR 0x08
+#define SDLA_S507_EPROMCLK 0x10
+#define SDLA_S508_INTRQ 0x08
+#define SDLA_S508_INTEN 0x10
+
+struct sdla_cmd {
+ char opp_flag __attribute__((packed));
+ char cmd __attribute__((packed));
+ short length __attribute__((packed));
+ char retval __attribute__((packed));
+ short dlci __attribute__((packed));
+ char flags __attribute__((packed));
+ short rxlost_int __attribute__((packed));
+ long rxlost_app __attribute__((packed));
+ char reserve[2] __attribute__((packed));
+ char data[SDLA_MAX_DATA] __attribute__((packed)); /* transfer data buffer */
+};
+
+struct intr_info {
+ char flags __attribute__((packed));
+ short txlen __attribute__((packed));
+ char irq __attribute__((packed));
+ char flags2 __attribute__((packed));
+ short timeout __attribute__((packed));
+};
+
+/* found in the 508's control window at RXBUF_INFO */
+struct buf_info {
+ unsigned short rse_num __attribute__((packed));
+ unsigned long rse_base __attribute__((packed));
+ unsigned long rse_next __attribute__((packed));
+ unsigned long buf_base __attribute__((packed));
+ unsigned short reserved __attribute__((packed));
+ unsigned long buf_top __attribute__((packed));
+};
+
+/* structure pointed to by rse_base in RXBUF_INFO struct */
+struct buf_entry {
+ char opp_flag __attribute__((packed));
+ short length __attribute__((packed));
+ short dlci __attribute__((packed));
+ char flags __attribute__((packed));
+ short timestamp __attribute__((packed));
+ short reserved[2] __attribute__((packed));
+ long buf_addr __attribute__((packed));
+};
+
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/sdla_chdlc.h b/pfinet/linux-src/include/linux/sdla_chdlc.h
new file mode 100644
index 00000000..c82fe14b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sdla_chdlc.h
@@ -0,0 +1,808 @@
+/*************************************************************************
+ sdla_chdlc.h Sangoma Cisco HDLC firmware API definitions
+
+ Author: Gideon Hack
+ Nenad Corbic <ncorbic@sangoma.com>
+
+ Copyright: (c) 1995-1999 Sangoma Technologies Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the term of the GNU General Public License
+ as published by the Free Software Foundation; either version
+ 2 of the License, or (at your option) any later version.
+
+===========================================================================
+ Oct 04, 1999 Nenad Corbic Updated API support
+ Jun 02, 1999 Gideon Hack Changes for S514 usage.
+ Oct 28, 1998 Jaspreet Singh Made changes for Dual Port CHDLC.
+ Jun 11, 1998 David Fong Initial version.
+===========================================================================
+
+ Organization
+ - Compatibility notes
+ - Constants defining the shared memory control block (mailbox)
+ - Interface commands
+ - Return code from interface commands
+ - Constants for the commands (structures for casting data)
+ - UDP Management constants and structures
+
+*************************************************************************/
+
+#ifndef _SDLA_CHDLC_H
+# define _SDLC_CHDLC_H
+
+/*------------------------------------------------------------------------
+ Notes:
+
+ All structres defined in this file are byte-aligned.
+
+ Compiler Platform
+ ------------------------
+ GNU C Linux
+
+------------------------------------------------------------------------*/
+
+#ifndef PACKED
+#define PACKED __attribute__((packed))
+#endif /* PACKED */
+
+
+/* ----------------------------------------------------------------------------
+ * Constants defining the shared memory control block (mailbox)
+ * --------------------------------------------------------------------------*/
+
+#define PRI_BASE_ADDR_MB_STRUCT 0xE000 /* the base address of the mailbox structure on the adapter */
+#define SEC_BASE_ADDR_MB_STRUCT 0xE800 /* the base address of the mailbox structure on the adapter */
+#define SIZEOF_MB_DATA_BFR 2032 /* the size of the actual mailbox data area */
+#define NUMBER_MB_RESERVED_BYTES 0x0B /* the number of reserved bytes in the mailbox header area */
+
+
+#define MIN_LGTH_CHDLC_DATA_CFG 300 /* min length of the CHDLC data field (for configuration purposes) */
+#define PRI_MAX_NO_DATA_BYTES_IN_FRAME 15354 /* PRIMARY - max length of the CHDLC data field */
+
+typedef struct {
+ unsigned char opp_flag PACKED; /* the opp flag */
+ unsigned char command PACKED; /* the user command */
+ unsigned short buffer_length PACKED; /* the data length */
+ unsigned char return_code PACKED; /* the return code */
+ unsigned char MB_reserved[NUMBER_MB_RESERVED_BYTES] PACKED; /* reserved for later */
+ unsigned char data[SIZEOF_MB_DATA_BFR] PACKED; /* the data area */
+} CHDLC_MAILBOX_STRUCT;
+
+typedef struct {
+ pid_t pid_num PACKED;
+ CHDLC_MAILBOX_STRUCT cmdarea PACKED;
+
+} CMDBLOCK_STRUCT;
+
+
+
+
+/* ----------------------------------------------------------------------------
+ * Interface commands
+ * --------------------------------------------------------------------------*/
+
+/* global interface commands */
+#define READ_GLOBAL_EXCEPTION_CONDITION 0x01
+#define SET_GLOBAL_CONFIGURATION 0x02
+#define READ_GLOBAL_CONFIGURATION 0x03
+#define READ_GLOBAL_STATISTICS 0x04
+#define FLUSH_GLOBAL_STATISTICS 0x05
+#define SET_MODEM_STATUS 0x06 /* set status of DTR or RTS */
+#define READ_MODEM_STATUS 0x07 /* read status of CTS and DCD */
+#define READ_COMMS_ERROR_STATS 0x08
+#define FLUSH_COMMS_ERROR_STATS 0x09
+#define SET_TRACE_CONFIGURATION 0x0A /* set the line trace config */
+#define READ_TRACE_CONFIGURATION 0x0B /* read the line trace config */
+#define READ_TRACE_STATISTICS 0x0C /* read the trace statistics */
+#define FLUSH_TRACE_STATISTICS 0x0D /* flush the trace statistics */
+#define FT1_MONITOR_STATUS_CTRL 0x1C /* set the status of the S508/FT1 monitoring */
+#define SET_FT1_CONFIGURATION 0x18 /* set the FT1 configuration */
+#define READ_FT1_CONFIGURATION 0x19 /* read the FT1 configuration */
+#define TRANSMIT_ASYNC_DATA_TO_FT1 0x1A /* output asynchronous data to the FT1 */
+#define RECEIVE_ASYNC_DATA_FROM_FT1 0x1B /* receive asynchronous data from the FT1 */
+#define FT1_MONITOR_STATUS_CTRL 0x1C /* set the status of the FT1 monitoring */
+
+#define READ_FT1_OPERATIONAL_STATS 0x1D /* read the S508/FT1 operational statistics */
+#define SET_FT1_MODE 0x1E /* set the operational mode of the S508/FT1 module */
+
+/* CHDLC-level interface commands */
+#define READ_CHDLC_CODE_VERSION 0x20
+#define READ_CHDLC_EXCEPTION_CONDITION 0x21 /* read exception condition from the adapter */
+#define SET_CHDLC_CONFIGURATION 0x22
+#define READ_CHDLC_CONFIGURATION 0x23
+#define ENABLE_CHDLC_COMMUNICATIONS 0x24
+#define DISABLE_CHDLC_COMMUNICATIONS 0x25
+#define READ_CHDLC_LINK_STATUS 0x26
+#define READ_CHDLC_OPERATIONAL_STATS 0x27
+#define FLUSH_CHDLC_OPERATIONAL_STATS 0x28
+#define SET_CHDLC_INTERRUPT_TRIGGERS 0x30 /* set application interrupt triggers */
+#define READ_CHDLC_INTERRUPT_TRIGGERS 0x31 /* read application interrupt trigger configuration */
+
+/* Special UDP drivers management commands */
+#define CPIPE_ENABLE_TRACING 0x50
+#define CPIPE_DISABLE_TRACING 0x51
+#define CPIPE_GET_TRACE_INFO 0x52
+#define CPIPE_GET_IBA_DATA 0x53
+#define CPIPE_FT1_READ_STATUS 0x54
+#define CPIPE_DRIVER_STAT_IFSEND 0x55
+#define CPIPE_DRIVER_STAT_INTR 0x56
+#define CPIPE_DRIVER_STAT_GEN 0x57
+#define CPIPE_FLUSH_DRIVER_STATS 0x58
+#define CPIPE_ROUTER_UP_TIME 0x59
+
+/* Driver specific commands for API */
+#define CHDLC_READ_TRACE_DATA 0xE4 /* read trace data */
+#define TRACE_ALL 0x00
+#define TRACE_PROT 0x01
+#define TRACE_DATA 0x02
+
+/* ----------------------------------------------------------------------------
+ * Return codes from interface commands
+ * --------------------------------------------------------------------------*/
+
+#define COMMAND_OK 0x00
+
+/* return codes from global interface commands */
+#define NO_GLOBAL_EXCEP_COND_TO_REPORT 0x01 /* there is no CHDLC exception condition to report */
+#define LGTH_GLOBAL_CFG_DATA_INVALID 0x01 /* the length of the passed global configuration data is invalid */
+#define LGTH_TRACE_CFG_DATA_INVALID 0x01 /* the length of the passed trace configuration data is invalid */
+#define IRQ_TIMEOUT_VALUE_INVALID 0x02 /* an invalid application IRQ timeout value was selected */
+#define TRACE_CONFIG_INVALID 0x02 /* the passed line trace configuration is invalid */
+#define ADAPTER_OPERATING_FREQ_INVALID 0x03 /* an invalid adapter operating frequency was selected */
+#define TRC_DEAC_TMR_INVALID 0x03 /* the trace deactivation timer is invalid */
+#define S508_FT1_ADPTR_NOT_PRESENT 0x0C /* the S508/FT1 adapter is not present */
+#define INVALID_FT1_STATUS_SELECTION 0x0D /* the S508/FT1 status selection is invalid */
+#define FT1_OP_STATS_NOT_ENABLED 0x0D /* the FT1 operational statistics have not been enabled */
+#define FT1_OP_STATS_NOT_AVAILABLE 0x0E /* the FT1 operational statistics are not currently available */
+#define S508_FT1_MODE_SELECTION_BUSY 0x0E /* the S508/FT1 adapter is busy selecting the operational mode */
+
+/* return codes from command READ_GLOBAL_EXCEPTION_CONDITION */
+#define EXCEP_MODEM_STATUS_CHANGE 0x10 /* a modem status change occurred */
+#define EXCEP_TRC_DISABLED 0x11 /* the trace has been disabled */
+#define EXCEP_IRQ_TIMEOUT 0x12 /* IRQ timeout */
+
+/* return codes from CHDLC-level interface commands */
+#define NO_CHDLC_EXCEP_COND_TO_REPORT 0x21 /* there is no CHDLC exception condition to report */
+#define CHDLC_COMMS_DISABLED 0x21 /* communications are not currently enabled */
+#define CHDLC_COMMS_ENABLED 0x21 /* communications are currently enabled */
+#define DISABLE_CHDLC_COMMS_BEFORE_CFG 0x21 /* CHDLC communications must be disabled before setting the configuration */
+#define ENABLE_CHDLC_COMMS_BEFORE_CONN 0x21 /* communications must be enabled before using the CHDLC_CONNECT conmmand */
+#define CHDLC_CFG_BEFORE_COMMS_ENABLED 0x22 /* perform a SET_CHDLC_CONFIGURATION before enabling comms */
+#define LGTH_CHDLC_CFG_DATA_INVALID 0x22 /* the length of the passed CHDLC configuration data is invalid */
+#define LGTH_INT_TRIGGERS_DATA_INVALID 0x22 /* the length of the passed interrupt trigger data is invalid */
+#define INVALID_IRQ_SELECTED 0x23 /* in invalid IRQ was selected in the SET_CHDLC_INTERRUPT_TRIGGERS */
+#define INVALID_CHDLC_CFG_DATA 0x23 /* the passed CHDLC configuration data is invalid */
+#define IRQ_TMR_VALUE_INVALID 0x24 /* an invalid application IRQ timer value was selected */
+#define LARGER_PERCENT_TX_BFR_REQUIRED 0x24 /* a larger Tx buffer percentage is required */
+#define LARGER_PERCENT_RX_BFR_REQUIRED 0x25 /* a larger Rx buffer percentage is required */
+#define S514_BOTH_PORTS_SAME_CLK_MODE 0x26 /* S514 - both ports must have same clock mode */
+#define INVALID_CMND_HDLC_STREAM_MODE 0x4E /* the CHDLC interface command is invalid for HDLC streaming mode */
+#define INVALID_CHDLC_COMMAND 0x4F /* the defined CHDLC interface command is invalid */
+
+/* return codes from command READ_CHDLC_EXCEPTION_CONDITION */
+#define EXCEP_LINK_ACTIVE 0x30 /* the CHDLC link has become active */
+#define EXCEP_LINK_INACTIVE_MODEM 0x31 /* the CHDLC link has become inactive (modem status) */
+#define EXCEP_LINK_INACTIVE_KPALV 0x32 /* the CHDLC link has become inactive (keepalive status) */
+#define EXCEP_IP_ADDRESS_DISCOVERED 0x33 /* the IP address has been discovered */
+#define EXCEP_LOOPBACK_CONDITION 0x34 /* a loopback condition has occurred */
+
+
+/* return code from command CHDLC_SEND_WAIT and CHDLC_SEND_NO_WAIT */
+#define LINK_DISCONNECTED 0x21
+#define NO_TX_BFRS_AVAIL 0x24
+
+
+/* ----------------------------------------------------------------------------
+ * Constants for the SET_GLOBAL_CONFIGURATION/READ_GLOBAL_CONFIGURATION commands
+ * --------------------------------------------------------------------------*/
+
+/* the global configuration structure */
+typedef struct {
+ unsigned short adapter_config_options PACKED; /* adapter config options */
+ unsigned short app_IRQ_timeout PACKED; /* application IRQ timeout */
+ unsigned long adapter_operating_frequency PACKED; /* adapter operating frequency */
+} GLOBAL_CONFIGURATION_STRUCT;
+
+/* settings for the 'app_IRQ_timeout' */
+#define MAX_APP_IRQ_TIMEOUT_VALUE 5000 /* the maximum permitted IRQ timeout */
+
+
+
+/* ----------------------------------------------------------------------------
+ * Constants for the READ_GLOBAL_STATISTICS command
+ * --------------------------------------------------------------------------*/
+
+/* the global statistics structure */
+typedef struct {
+ unsigned short app_IRQ_timeout_count PACKED;
+} GLOBAL_STATS_STRUCT;
+
+
+
+/* ----------------------------------------------------------------------------
+ * Constants for the READ_COMMS_ERROR_STATS command
+ * --------------------------------------------------------------------------*/
+
+/* the communications error statistics structure */
+typedef struct {
+ unsigned short Rx_overrun_err_count PACKED;
+ unsigned short CRC_err_count PACKED; /* receiver CRC error count */
+ unsigned short Rx_abort_count PACKED; /* abort frames recvd count */
+ unsigned short Rx_dis_pri_bfrs_full_count PACKED;/* receiver disabled */
+ unsigned short comms_err_stat_reserved_1 PACKED;/* reserved for later */
+ unsigned short sec_Tx_abort_msd_Tx_int_count PACKED; /* secondary - abort frames transmitted count (missed Tx interrupt) */
+ unsigned short missed_Tx_und_int_count PACKED; /* missed tx underrun interrupt count */
+ unsigned short sec_Tx_abort_count PACKED; /*secondary-abort frames tx count */
+ unsigned short DCD_state_change_count PACKED; /* DCD state change */
+ unsigned short CTS_state_change_count PACKED; /* CTS state change */
+} COMMS_ERROR_STATS_STRUCT;
+
+
+
+/* ----------------------------------------------------------------------------
+ * Constants used for line tracing
+ * --------------------------------------------------------------------------*/
+
+/* the trace configuration structure (SET_TRACE_CONFIGURATION/READ_TRACE_CONFIGURATION commands) */
+typedef struct {
+ unsigned char trace_config PACKED; /* trace configuration */
+ unsigned short trace_deactivation_timer PACKED; /* trace deactivation timer */
+ unsigned long ptr_trace_stat_el_cfg_struct PACKED; /* a pointer to the line trace element configuration structure */
+} LINE_TRACE_CONFIG_STRUCT;
+
+/* 'trace_config' bit settings */
+#define TRACE_INACTIVE 0x00 /* trace is inactive */
+#define TRACE_ACTIVE 0x01 /* trace is active */
+#define TRACE_DELAY_MODE 0x04 /* operate the trace in delay mode */
+#define TRACE_DATA_FRAMES 0x08 /* trace Data frames */
+#define TRACE_SLARP_FRAMES 0x10 /* trace SLARP frames */
+#define TRACE_CDP_FRAMES 0x20 /* trace CDP frames */
+
+/* the line trace status element configuration structure */
+typedef struct {
+ unsigned short number_trace_status_elements PACKED; /* number of line trace elements */
+ unsigned long base_addr_trace_status_elements PACKED; /* base address of the trace element list */
+ unsigned long next_trace_element_to_use PACKED; /* pointer to the next trace element to be used */
+ unsigned long base_addr_trace_buffer PACKED; /* base address of the trace data buffer */
+ unsigned long end_addr_trace_buffer PACKED; /* end address of the trace data buffer */
+} TRACE_STATUS_EL_CFG_STRUCT;
+
+/* the line trace status element structure */
+typedef struct {
+ unsigned char opp_flag PACKED; /* opp flag */
+ unsigned short trace_length PACKED; /* trace length */
+ unsigned char trace_type PACKED; /* trace type */
+ unsigned short trace_time_stamp PACKED; /* time stamp */
+ unsigned short trace_reserved_1 PACKED; /* reserved for later use */
+ unsigned long trace_reserved_2 PACKED; /* reserved for later use */
+ unsigned long ptr_data_bfr PACKED; /* ptr to the trace data buffer */
+} TRACE_STATUS_ELEMENT_STRUCT;
+
+/* "trace_type" bit settings */
+#define TRACE_INCOMING 0x00
+#define TRACE_OUTGOINGING 0x01
+#define TRACE_INCOMING_ABORTED 0x10
+#define TRACE_INCOMING_CRC_ERROR 0x20
+#define TRACE_INCOMING_OVERRUN_ERROR 0x40
+
+
+
+/* the line trace statistics structure */
+typedef struct {
+ unsigned long frames_traced_count PACKED; /* number of frames traced */
+ unsigned long trc_frms_not_recorded_count PACKED; /* number of trace frames discarded */
+} LINE_TRACE_STATS_STRUCT;
+
+
+/* ----------------------------------------------------------------------------
+ * Constants for the FT1_MONITOR_STATUS_CTRL command
+ * --------------------------------------------------------------------------*/
+
+#define DISABLE_FT1_STATUS_STATISTICS 0x00 /* disable the FT1 status and statistics monitoring */
+#define ENABLE_READ_FT1_STATUS 0x01 /* read the FT1 operational status */
+#define ENABLE_READ_FT1_OP_STATS 0x02 /* read the FT1 operational statistics */
+#define FLUSH_FT1_OP_STATS 0x04 /* flush the FT1 operational statistics */
+
+
+
+
+/* ----------------------------------------------------------------------------
+ * Constants for the SET_CHDLC_CONFIGURATION command
+ * --------------------------------------------------------------------------*/
+
+/* the CHDLC configuration structure */
+typedef struct {
+ unsigned long baud_rate PACKED; /* the baud rate */
+ unsigned short line_config_options PACKED; /* line configuration options */
+ unsigned short modem_config_options PACKED; /* modem configration options */
+ unsigned short modem_status_timer PACKED; /* timer for monitoring modem status changes */
+ unsigned short CHDLC_API_options PACKED; /* CHDLC API options */
+ unsigned short CHDLC_protocol_options PACKED; /* CHDLC protocol options */
+ unsigned short percent_data_buffer_for_Tx PACKED; /* percentage data buffering used for Tx */
+ unsigned short CHDLC_statistics_options PACKED; /* CHDLC operational statistics options */
+ unsigned short max_CHDLC_data_field_length PACKED; /* the maximum length of the CHDLC Data field */
+ unsigned short transmit_keepalive_timer PACKED; /* the transmit keepalive timer */
+ unsigned short receive_keepalive_timer PACKED; /* the receive keepalive timer */
+ unsigned short keepalive_error_tolerance PACKED; /* the receive keepalive error tolerance */
+ unsigned short SLARP_request_timer PACKED; /* the SLARP request timer */
+ unsigned long IP_address PACKED; /* the IP address */
+ unsigned long IP_netmask PACKED; /* the IP netmask */
+ unsigned long ptr_shared_mem_info_struct PACKED; /* a pointer to the shared memory area information structure */
+ unsigned long ptr_CHDLC_Tx_stat_el_cfg_struct PACKED; /* a pointer to the transmit status element configuration structure */
+ unsigned long ptr_CHDLC_Rx_stat_el_cfg_struct PACKED; /* a pointer to the receive status element configuration structure */
+} CHDLC_CONFIGURATION_STRUCT;
+
+/* settings for the 'line_config_options' */
+#define INTERFACE_LEVEL_V35 0x0000 /* V.35 interface level */
+#define INTERFACE_LEVEL_RS232 0x0001 /* RS-232 interface level */
+
+/* settings for the 'modem_config_options' */
+
+#define DONT_RAISE_DTR_RTS_ON_EN_COMMS 0x0001
+/* don't automatically raise DTR and RTS when performing an
+ ENABLE_CHDLC_COMMUNICATIONS command */
+
+#define DONT_REPORT_CHG_IN_MODEM_STAT 0x0002
+/* don't report changes in modem status to the application */
+
+
+/* bit settings for the 'CHDLC_protocol_options' byte */
+
+#define IGNORE_DCD_FOR_LINK_STAT 0x0001
+/* ignore DCD in determining the CHDLC link status */
+
+#define IGNORE_CTS_FOR_LINK_STAT 0x0002
+/* ignore CTS in determining the CHDLC link status */
+
+#define IGNORE_KPALV_FOR_LINK_STAT 0x0004
+/* ignore keepalive frames in determining the CHDLC link status */
+
+#define HDLC_STREAMING_MODE 0x8000
+
+/* settings for the 'CHDLC_statistics_options' */
+
+#define CHDLC_TX_DATA_BYTE_COUNT_STAT 0x0001
+/* record the number of Data bytes transmitted */
+
+#define CHDLC_RX_DATA_BYTE_COUNT_STAT 0x0002
+/* record the number of Data bytes received */
+
+#define CHDLC_TX_THROUGHPUT_STAT 0x0004
+/* compute the Data frame transmit throughput */
+
+#define CHDLC_RX_THROUGHPUT_STAT 0x0008
+/* compute the Data frame receive throughput */
+
+
+/* permitted minimum and maximum values for setting the CHDLC configuration */
+#define PRI_MAX_BAUD_RATE_S508 2666666 /* PRIMARY - maximum baud rate (S508) */
+#define SEC_MAX_BAUD_RATE_S508 258064 /* SECONDARY - maximum baud rate (S508) */
+#define PRI_MAX_BAUD_RATE_S514 2750000 /* PRIMARY - maximum baud rate (S508) */
+#define SEC_MAX_BAUD_RATE_S514 515625 /* SECONDARY - maximum baud rate (S508) */
+
+#define MIN_MODEM_TIMER 0 /* minimum modem status timer */
+#define MAX_MODEM_TIMER 5000 /* maximum modem status timer */
+
+#define SEC_MAX_NO_DATA_BYTES_IN_FRAME 2048 /* SECONDARY - max length of the CHDLC data field */
+
+#define MIN_Tx_KPALV_TIMER 0 /* minimum transmit keepalive timer */
+#define MAX_Tx_KPALV_TIMER 60000 /* maximum transmit keepalive timer */
+#define DEFAULT_Tx_KPALV_TIMER 10000 /* default transmit keepalive timer */
+
+#define MIN_Rx_KPALV_TIMER 10 /* minimum receive keepalive timer */
+#define MAX_Rx_KPALV_TIMER 60000 /* maximum receive keepalive timer */
+#define DEFAULT_Rx_KPALV_TIMER 10000 /* default receive keepalive timer */
+
+#define MIN_KPALV_ERR_TOL 1 /* min kpalv error tolerance count */
+#define MAX_KPALV_ERR_TOL 20 /* max kpalv error tolerance count */
+#define DEFAULT_KPALV_ERR_TOL 3 /* default value */
+
+#define MIN_SLARP_REQ_TIMER 0 /* min transmit SLARP Request timer */
+#define MAX_SLARP_REQ_TIMER 60000 /* max transmit SLARP Request timer */
+#define DEFAULT_SLARP_REQ_TIMER 0 /* default value -- no SLARP */
+
+
+
+/* ----------------------------------------------------------------------------
+ * Constants for the READ_CHDLC_LINK_STATUS command
+ * --------------------------------------------------------------------------*/
+
+/* the CHDLC status structure */
+typedef struct {
+ unsigned char CHDLC_link_status PACKED; /* CHDLC link status */
+ unsigned char no_Data_frms_for_app PACKED; /* number of Data frames available for the application */
+ unsigned char receiver_status PACKED; /* enabled/disabled */
+ unsigned char SLARP_state PACKED; /* internal SLARP state */
+} CHDLC_LINK_STATUS_STRUCT;
+
+/* settings for the 'CHDLC_link_status' variable */
+#define CHDLC_LINK_INACTIVE 0x00 /* the CHDLC link is inactive */
+#define CHDLC_LINK_ACTIVE 0x01 /* the CHDLC link is active */
+
+
+
+/* ----------------------------------------------------------------------------
+ * Constants for the READ_CHDLC_OPERATIONAL_STATS command
+ * --------------------------------------------------------------------------*/
+
+/* the CHDLC operational statistics structure */
+typedef struct {
+
+ /* Data frame transmission statistics */
+ unsigned long Data_frames_Tx_count PACKED; /* # of frames transmitted */
+ unsigned long Data_bytes_Tx_count PACKED; /* # of bytes transmitted */
+ unsigned long Data_Tx_throughput PACKED; /* transmit throughput */
+ unsigned long no_ms_for_Data_Tx_thruput_comp PACKED; /* millisecond time used for the Tx throughput computation */
+ unsigned long Tx_Data_discard_lgth_err_count PACKED; /* number of Data frames discarded (length error) */
+ unsigned long reserved_Data_frm_Tx_stat1 PACKED; /* reserved for later */
+ unsigned long reserved_Data_frm_Tx_stat2 PACKED; /* reserved for later */
+ unsigned long reserved_Data_frm_Tx_stat3 PACKED; /* reserved for later */
+
+ /* Data frame reception statistics */
+ unsigned long Data_frames_Rx_count PACKED; /* number of frames received */
+ unsigned long Data_bytes_Rx_count PACKED; /* number of bytes received */
+ unsigned long Data_Rx_throughput PACKED; /* receive throughput */
+ unsigned long no_ms_for_Data_Rx_thruput_comp PACKED; /* millisecond time used for the Rx throughput computation */
+ unsigned long Rx_Data_discard_short_count PACKED; /* received Data frames discarded (too short) */
+ unsigned long Rx_Data_discard_long_count PACKED; /* received Data frames discarded (too long) */
+ unsigned long Rx_Data_discard_inactive_count PACKED; /* received Data frames discarded (link inactive) */
+ unsigned long reserved_Data_frm_Rx_stat1 PACKED; /* reserved for later */
+
+ /* SLARP frame transmission/reception statistics */
+ unsigned long CHDLC_SLARP_REQ_Tx_count PACKED; /* number of SLARP Request frames transmitted */
+ unsigned long CHDLC_SLARP_REQ_Rx_count PACKED; /* number of SLARP Request frames received */
+ unsigned long CHDLC_SLARP_REPLY_Tx_count PACKED; /* number of SLARP Reply frames transmitted */
+ unsigned long CHDLC_SLARP_REPLY_Rx_count PACKED; /* number of SLARP Reply frames received */
+ unsigned long CHDLC_SLARP_KPALV_Tx_count PACKED; /* number of SLARP keepalive frames transmitted */
+ unsigned long CHDLC_SLARP_KPALV_Rx_count PACKED; /* number of SLARP keepalive frames received */
+ unsigned long reserved_SLARP_stat1 PACKED; /* reserved for later */
+ unsigned long reserved_SLARP_stat2 PACKED; /* reserved for later */
+
+ /* CDP frame transmission/reception statistics */
+ unsigned long CHDLC_CDP_Tx_count PACKED; /* number of CDP frames transmitted */
+ unsigned long CHDLC_CDP_Rx_count PACKED; /* number of CDP frames received */
+ unsigned long reserved_CDP_stat1 PACKED; /* reserved for later */
+ unsigned long reserved_CDP_stat2 PACKED; /* reserved for later */
+ unsigned long reserved_CDP_stat3 PACKED; /* reserved for later */
+ unsigned long reserved_CDP_stat4 PACKED; /* reserved for later */
+ unsigned long reserved_CDP_stat5 PACKED; /* reserved for later */
+ unsigned long reserved_CDP_stat6 PACKED; /* reserved for later */
+
+ /* Incomming frames with a format error statistics */
+ unsigned short Rx_frm_incomp_CHDLC_hdr_count PACKED; /* frames received of with incomplete Cisco HDLC header */
+ unsigned short Rx_frms_too_long_count PACKED; /* frames received of excessive length count */
+ unsigned short Rx_invalid_CHDLC_addr_count PACKED; /* frames received with an invalid CHDLC address count */
+ unsigned short Rx_invalid_CHDLC_ctrl_count PACKED; /* frames received with an invalid CHDLC control field count */
+ unsigned short Rx_invalid_CHDLC_type_count PACKED; /* frames received of an invalid CHDLC frame type count */
+ unsigned short Rx_SLARP_invalid_code_count PACKED; /* SLARP frame received with an invalid packet code */
+ unsigned short Rx_SLARP_Reply_bad_IP_addr PACKED; /* SLARP Reply received - bad IP address */
+ unsigned short Rx_SLARP_Reply_bad_netmask PACKED; /* SLARP Reply received - bad netmask */
+ unsigned long reserved_frm_format_err1 PACKED; /* reserved for later */
+ unsigned long reserved_frm_format_err2 PACKED; /* reserved for later */
+ unsigned long reserved_frm_format_err3 PACKED; /* reserved for later */
+ unsigned long reserved_frm_format_err4 PACKED; /* reserved for later */
+
+ /* CHDLC timeout/retry statistics */
+ unsigned short SLARP_Rx_keepalive_TO_count PACKED; /* timeout count for incomming SLARP frames */
+ unsigned short SLARP_Request_TO_count PACKED; /* timeout count for SLARP Request frames */
+ unsigned long To_retry_reserved_stat1 PACKED; /* reserved for later */
+ unsigned long To_retry_reserved_stat2 PACKED; /* reserved for later */
+ unsigned long To_retry_reserved_stat3 PACKED; /* reserved for later */
+
+ /* CHDLC link active/inactive and loopback statistics */
+ unsigned short link_active_count PACKED; /* number of times that the link went active */
+ unsigned short link_inactive_modem_count PACKED; /* number of times that the link went inactive (modem failure) */
+ unsigned short link_inactive_keepalive_count PACKED; /* number of times that the link went inactive (keepalive failure) */
+ unsigned short link_looped_count PACKED; /* link looped count */
+ unsigned long link_status_reserved_stat1 PACKED; /* reserved for later use */
+ unsigned long link_status_reserved_stat2 PACKED; /* reserved for later use */
+
+ /* miscellaneous statistics */
+ unsigned long reserved_misc_stat1 PACKED; /* reserved for later */
+ unsigned long reserved_misc_stat2 PACKED; /* reserved for later */
+ unsigned long reserved_misc_stat3 PACKED; /* reserved for later */
+ unsigned long reserved_misc_stat4 PACKED; /* reserved for later */
+
+} CHDLC_OPERATIONAL_STATS_STRUCT;
+
+
+
+/* ----------------------------------------------------------------------------
+ * Constants for using application interrupts
+ * --------------------------------------------------------------------------*/
+
+/* the structure used for the SET_CHDLC_INTERRUPT_TRIGGERS/READ_CHDLC_INTERRUPT_TRIGGERS command */
+typedef struct {
+ unsigned char CHDLC_interrupt_triggers PACKED; /* CHDLC interrupt trigger configuration */
+ unsigned char IRQ PACKED; /* IRQ to be used */
+ unsigned short interrupt_timer PACKED; /* interrupt timer */
+ unsigned short misc_interrupt_bits PACKED; /* miscellaneous bits */
+} CHDLC_INT_TRIGGERS_STRUCT;
+
+/* 'CHDLC_interrupt_triggers' bit settings */
+#define APP_INT_ON_RX_FRAME 0x01 /* interrupt on Data frame reception */
+#define APP_INT_ON_TX_FRAME 0x02 /* interrupt when an Data frame may be transmitted */
+#define APP_INT_ON_COMMAND_COMPLETE 0x04 /* interrupt when an interface command is complete */
+#define APP_INT_ON_TIMER 0x08 /* interrupt on a defined millisecond timeout */
+#define APP_INT_ON_GLOBAL_EXCEP_COND 0x10 /* interrupt on a global exception condition */
+#define APP_INT_ON_CHDLC_EXCEP_COND 0x20 /* interrupt on an CHDLC exception condition */
+#define APP_INT_ON_TRACE_DATA_AVAIL 0x80 /* interrupt when trace data is available */
+
+/* interrupt types indicated at 'interrupt_type' byte of the INTERRUPT_INFORMATION_STRUCT */
+#define NO_APP_INTS_PEND 0x00 /* no interrups are pending */
+#define RX_APP_INT_PEND 0x01 /* a receive interrupt is pending */
+#define TX_APP_INT_PEND 0x02 /* a transmit interrupt is pending */
+#define COMMAND_COMPLETE_APP_INT_PEND 0x04 /* a 'command complete' interrupt is pending */
+#define TIMER_APP_INT_PEND 0x08 /* a timer interrupt is pending */
+#define GLOBAL_EXCEP_COND_APP_INT_PEND 0x10 /* a global exception condition interrupt is pending */
+#define CHDLC_EXCEP_COND_APP_INT_PEND 0x20 /* an CHDLC exception condition interrupt is pending */
+#define TRACE_DATA_AVAIL_APP_INT_PEND 0x80 /* a trace data available interrupt is pending */
+
+
+/* modem status changes */
+#define DCD_HIGH 0x08
+#define CTS_HIGH 0x20
+
+
+/* ----------------------------------------------------------------------------
+ * Constants for Data frame transmission
+ * --------------------------------------------------------------------------*/
+
+/* the Data frame transmit status element configuration structure */
+typedef struct {
+ unsigned short number_Tx_status_elements PACKED; /* number of transmit status elements */
+ unsigned long base_addr_Tx_status_elements PACKED; /* base address of the transmit element list */
+ unsigned long next_Tx_status_element_to_use PACKED; /* pointer to the next transmit element to be used */
+} CHDLC_TX_STATUS_EL_CFG_STRUCT;
+
+/* the Data frame transmit status element structure */
+typedef struct {
+ unsigned char opp_flag PACKED; /* opp flag */
+ unsigned short frame_length PACKED; /* length of the frame to be transmitted */
+ unsigned char reserved_1 PACKED; /* reserved for internal use */
+ unsigned long reserved_2 PACKED; /* reserved for internal use */
+ unsigned long reserved_3 PACKED; /* reserved for internal use */
+ unsigned long ptr_data_bfr PACKED; /* pointer to the data area */
+} CHDLC_DATA_TX_STATUS_EL_STRUCT;
+
+
+
+/* ----------------------------------------------------------------------------
+ * Constants for Data frame reception
+ * --------------------------------------------------------------------------*/
+
+/* the Data frame receive status element configuration structure */
+typedef struct {
+ unsigned short number_Rx_status_elements PACKED; /* number of receive status elements */
+ unsigned long base_addr_Rx_status_elements PACKED; /* base address of the receive element list */
+ unsigned long next_Rx_status_element_to_use PACKED; /* pointer to the next receive element to be used */
+ unsigned long base_addr_Rx_buffer PACKED; /* base address of the receive data buffer */
+ unsigned long end_addr_Rx_buffer PACKED; /* end address of the receive data buffer */
+} CHDLC_RX_STATUS_EL_CFG_STRUCT;
+
+/* the Data frame receive status element structure */
+typedef struct {
+ unsigned char opp_flag PACKED; /* opp flag */
+ unsigned short frame_length PACKED; /* length of the received frame */
+ unsigned char error_flag PACKED; /* frame errors (HDLC_STREAMING_MODE)*/
+ unsigned short time_stamp PACKED; /* receive time stamp (HDLC_STREAMING_MODE) */
+ unsigned long reserved_1 PACKED; /* reserved for internal use */
+ unsigned short reserved_2 PACKED; /* reserved for internal use */
+ unsigned long ptr_data_bfr PACKED; /* pointer to the data area */
+} CHDLC_DATA_RX_STATUS_EL_STRUCT;
+
+
+
+/* ----------------------------------------------------------------------------
+ * Constants defining the shared memory information area
+ * --------------------------------------------------------------------------*/
+
+/* the global information structure */
+typedef struct {
+ unsigned char global_status PACKED; /* global status */
+ unsigned char modem_status PACKED; /* current modem status */
+ unsigned char global_excep_conditions PACKED; /* global exception conditions */
+ unsigned char glob_info_reserved[5] PACKED; /* reserved */
+ unsigned char codename[4] PACKED; /* Firmware name */
+ unsigned char codeversion[4] PACKED; /* Firmware version */
+} GLOBAL_INFORMATION_STRUCT;
+
+/* the CHDLC information structure */
+typedef struct {
+ unsigned char CHDLC_status PACKED; /* CHDLC status */
+ unsigned char CHDLC_excep_conditions PACKED; /* CHDLC exception conditions */
+ unsigned char CHDLC_info_reserved[14] PACKED; /* reserved */
+} CHDLC_INFORMATION_STRUCT;
+
+/* the interrupt information structure */
+typedef struct {
+ unsigned char interrupt_type PACKED; /* type of interrupt triggered */
+ unsigned char interrupt_permission PACKED; /* interrupt permission mask */
+ unsigned char int_info_reserved[14] PACKED; /* reserved */
+} INTERRUPT_INFORMATION_STRUCT;
+
+/* the S508/FT1 information structure */
+typedef struct {
+ unsigned char parallel_port_A_input PACKED; /* input - parallel port A */
+ unsigned char parallel_port_B_input PACKED; /* input - parallel port B */
+ unsigned char FT1_info_reserved[14] PACKED; /* reserved */
+} FT1_INFORMATION_STRUCT;
+
+/* the shared memory area information structure */
+typedef struct {
+ GLOBAL_INFORMATION_STRUCT global_info_struct PACKED; /* the global information structure */
+ CHDLC_INFORMATION_STRUCT CHDLC_info_struct PACKED; /* the CHDLC information structure */
+ INTERRUPT_INFORMATION_STRUCT interrupt_info_struct PACKED; /* the interrupt information structure */
+ FT1_INFORMATION_STRUCT FT1_info_struct PACKED; /* the S508/FT1 information structure */
+} SHARED_MEMORY_INFO_STRUCT;
+
+/* ----------------------------------------------------------------------------
+ * UDP Management constants and structures
+ * --------------------------------------------------------------------------*/
+
+/* The embedded control block for UDP mgmt
+ This is essentially a mailbox structure, without the large data field */
+
+typedef struct {
+ unsigned char opp_flag PACKED; /* the opp flag */
+ unsigned char command PACKED; /* the user command */
+ unsigned short buffer_length PACKED; /* the data length */
+ unsigned char return_code PACKED; /* the return code */
+ unsigned char MB_reserved[NUMBER_MB_RESERVED_BYTES] PACKED; /* reserved for later */
+} cblock_t;
+
+
+/* UDP management packet layout (data area of ip packet) */
+/*
+typedef struct {
+ unsigned char signature[8] PACKED;
+ unsigned char request_reply PACKED;
+ unsigned char id PACKED;
+ unsigned char reserved[6] PACKED;
+ cblock_t cblock PACKED;
+ unsigned char num_frames PACKED;
+ unsigned char ismoredata PACKED;
+ unsigned char data[SIZEOF_MB_DATA_BFR] PACKED;
+} udp_management_packet_t;
+
+*/
+
+typedef struct {
+ unsigned char num_frames PACKED;
+ unsigned char ismoredata PACKED;
+} trace_info_t;
+
+typedef struct {
+ ip_pkt_t ip_pkt PACKED;
+ udp_pkt_t udp_pkt PACKED;
+ wp_mgmt_t wp_mgmt PACKED;
+ cblock_t cblock PACKED;
+ trace_info_t trace_info PACKED;
+ unsigned char data[SIZEOF_MB_DATA_BFR] PACKED;
+} chdlc_udp_pkt_t;
+
+typedef struct ft1_exec_cmd{
+ unsigned char command PACKED; /* the user command */
+ unsigned short buffer_length PACKED; /* the data length */
+ unsigned char return_code PACKED; /* the return code */
+ unsigned char MB_reserved[NUMBER_MB_RESERVED_BYTES] PACKED;
+} ft1_exec_cmd_t;
+
+typedef struct {
+ unsigned char opp_flag PACKED;
+ ft1_exec_cmd_t cmd PACKED;
+ unsigned char data[SIZEOF_MB_DATA_BFR] PACKED;
+} ft1_exec_t;
+
+#define UDPMGMT_SIGNATURE "CTPIPEAB"
+
+
+/* UDP/IP packet (for UDP management) layout */
+/*
+typedef struct {
+ unsigned char reserved[2] PACKED;
+ unsigned short ip_length PACKED;
+ unsigned char reserved2[4] PACKED;
+ unsigned char ip_ttl PACKED;
+ unsigned char ip_protocol PACKED;
+ unsigned short ip_checksum PACKED;
+ unsigned long ip_src_address PACKED;
+ unsigned long ip_dst_address PACKED;
+ unsigned short udp_src_port PACKED;
+ unsigned short udp_dst_port PACKED;
+ unsigned short udp_length PACKED;
+ unsigned short udp_checksum PACKED;
+ udp_management_packet_t um_packet PACKED;
+} ip_packet_t;
+*/
+
+/* valid ip_protocol for UDP management */
+#define UDPMGMT_UDP_PROTOCOL 0x11
+
+
+typedef struct {
+ unsigned char status PACKED;
+ unsigned char data_avail PACKED;
+ unsigned short real_length PACKED;
+ unsigned short time_stamp PACKED;
+ unsigned char data[1] PACKED;
+} trace_pkt_t;
+
+typedef struct {
+ unsigned char error_flag PACKED;
+ unsigned short time_stamp PACKED;
+ unsigned char reserved[13] PACKED;
+} api_rx_hdr_t;
+
+typedef struct {
+ api_rx_hdr_t api_rx_hdr PACKED;
+ void * data PACKED;
+} api_rx_element_t;
+
+typedef struct {
+ unsigned char attr PACKED;
+ unsigned char reserved[15] PACKED;
+} api_tx_hdr_t;
+
+typedef struct {
+ api_tx_hdr_t api_tx_hdr PACKED;
+ void * data PACKED;
+} api_tx_element_t;
+
+/* ----------------------------------------------------------------------------
+ * Constants for the SET_FT1_CONFIGURATION/READ_FT1_CONFIGURATION command
+ * --------------------------------------------------------------------------*/
+
+/* the FT1 configuration structure */
+typedef struct {
+ unsigned short framing_mode;
+ unsigned short encoding_mode;
+ unsigned short line_build_out;
+ unsigned short channel_base;
+ unsigned short baud_rate_kbps; /* the baud rate (in kbps) */
+ unsigned short clock_mode;
+} ft1_config_t;
+
+/* settings for the 'framing_mode' */
+#define ESF_FRAMING 0x00 /* ESF framing */
+#define D4_FRAMING 0x01 /* D4 framing */
+
+/* settings for the 'encoding_mode' */
+#define B8ZS_ENCODING 0x00 /* B8ZS encoding */
+#define AMI_ENCODING 0x01 /* AMI encoding */
+
+/* settings for the 'line_build_out' */
+#define LN_BLD_CSU_0dB_DSX1_0_to_133 0x00 /* set build out to CSU (0db) or DSX-1 (0-133ft) */
+#define LN_BLD_DSX1_133_to_266 0x01 /* set build out DSX-1 (133-266ft) */
+#define LN_BLD_DSX1_266_to_399 0x02 /* set build out DSX-1 (266-399ft) */
+#define LN_BLD_DSX1_399_to_533 0x03 /* set build out DSX-1 (399-533ft) */
+#define LN_BLD_DSX1_533_to_655 0x04 /* set build out DSX-1 (533-655ft) */
+#define LN_BLD_CSU_NEG_7dB 0x05 /* set build out to CSU (-7.5db) */
+#define LN_BLD_CSU_NEG_15dB 0x06 /* set build out to CSU (-15db) */
+#define LN_BLD_CSU_NEG_22dB 0x07 /* set build out to CSU (-22.5db) */
+
+/* settings for the 'channel_base' */
+#define MIN_CHANNEL_BASE_VALUE 1 /* the minimum permitted channel base value */
+#define MAX_CHANNEL_BASE_VALUE 24 /* the maximum permitted channel base value */
+
+/* settings for the 'baud_rate_kbps' */
+#define MIN_BAUD_RATE_KBPS 0 /* the minimum permitted baud rate (kbps) */
+#define MAX_BAUD_RATE_KBPS 1536 /* the maximum permitted baud rate (kbps) */
+#define BAUD_RATE_FT1_AUTO_CONFIG 0xFFFF /* the baud rate used to trigger an automatic FT1 configuration */
+
+/* settings for the 'clock_mode' */
+#define CLOCK_MODE_NORMAL 0x00 /* clock mode set to normal (slave) */
+#define CLOCK_MODE_MASTER 0x01 /* clock mode set to master */
+
+
+#define BAUD_RATE_FT1_AUTO_CONFIG 0xFFFF
+#define AUTO_FT1_CONFIG_NOT_COMPLETE 0x08
+#define AUTO_FT1_CFG_FAIL_OP_MODE 0x0C
+#define AUTO_FT1_CFG_FAIL_INVALID_LINE 0x0D
+
+
+#ifdef _MSC_
+# pragma pack()
+#endif
+#endif /* _SDLA_CHDLC_H */
diff --git a/pfinet/linux-src/include/linux/sdla_fr.h b/pfinet/linux-src/include/linux/sdla_fr.h
new file mode 100644
index 00000000..1c45c61d
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sdla_fr.h
@@ -0,0 +1,637 @@
+/*****************************************************************************
+* sdla_fr.h Sangoma frame relay firmware API definitions.
+*
+* Author: Gideon Hack
+* Nenad Corbic <ncorbic@sangoma.com>
+*
+* Copyright: (c) 1995-1999 Sangoma Technologies Inc.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version
+* 2 of the License, or (at your option) any later version.
+* ============================================================================
+* Oct 04, 1999 Gideon Hack Updated API structures
+* Jun 02, 1999 Gideon Hack Modifications for S514 support
+* Oct 12, 1997 Jaspreet Singh Added FR_READ_DLCI_IB_MAPPING
+* Jul 21, 1997 Jaspreet Singh Changed FRRES_TOO_LONG and FRRES_TOO_MANY to
+* 0x05 and 0x06 respectively.
+* Dec 23, 1996 Gene Kozin v2.0
+* Apr 29, 1996 Gene Kozin v1.0 (merged version S502 & S508 definitions).
+* Sep 26, 1995 Gene Kozin Initial version.
+*****************************************************************************/
+#ifndef _SDLA_FR_H
+#define _SDLA_FR_H
+
+/*----------------------------------------------------------------------------
+ * Notes:
+ * ------
+ * 1. All structures defined in this file are byte-alined.
+ *
+ * Compiler Platform
+ * -------- --------
+ * GNU C Linux
+ */
+
+#ifndef PACKED
+# define PACKED __attribute__((packed))
+#endif /* PACKED */
+
+/* Adapter memory layout */
+#define FR_MB_VECTOR 0xE000 /* mailbox window vector */
+#define FR502_RX_VECTOR 0xA000 /* S502 direct receive window vector */
+#define FR502_MBOX_OFFS 0xF60 /* S502 mailbox offset */
+#define FR508_MBOX_OFFS 0 /* S508 mailbox offset */
+#define FR502_FLAG_OFFS 0x1FF0 /* S502 status flags offset */
+#define FR508_FLAG_OFFS 0x1000 /* S508 status flags offset */
+#define FR502_RXMB_OFFS 0x900 /* S502 direct receive mailbox offset */
+#define FR508_TXBC_OFFS 0x1100 /* S508 Tx buffer info offset */
+#define FR508_RXBC_OFFS 0x1120 /* S508 Rx buffer info offset */
+
+/* Important constants */
+#define FR502_MAX_DATA 4096 /* maximum data buffer length */
+#define FR508_MAX_DATA 4080 /* maximum data buffer length */
+#define MIN_LGTH_FR_DATA_CFG 300 /* min Information frame length
+(for configuration purposes) */
+#define FR_MAX_NO_DATA_BYTES_IN_FRAME 15354 /* max Information frame length */
+
+#define HIGHEST_VALID_DLCI 991
+
+/****** Data Structures *****************************************************/
+
+/*----------------------------------------------------------------------------
+ * Frame relay command block.
+ */
+typedef struct fr_cmd
+{
+ unsigned char command PACKED; /* command code */
+ unsigned short length PACKED; /* length of data buffer */
+ unsigned char result PACKED; /* return code */
+ unsigned short dlci PACKED; /* DLCI number */
+ unsigned char attr PACKED; /* FECN, BECN, DE and C/R bits */
+ unsigned short rxlost1 PACKED; /* frames discarded at int. level */
+ unsigned long rxlost2 PACKED; /* frames discarded at app. level */
+ unsigned char rsrv[2] PACKED; /* reserved for future use */
+} fr_cmd_t;
+
+/* 'command' field defines */
+#define FR_WRITE 0x01
+#define FR_READ 0x02
+#define FR_ISSUE_IS_FRAME 0x03
+#define FR_SET_CONFIG 0x10
+#define FR_READ_CONFIG 0x11
+#define FR_COMM_DISABLE 0x12
+#define FR_COMM_ENABLE 0x13
+#define FR_READ_STATUS 0x14
+#define FR_READ_STATISTICS 0x15
+#define FR_FLUSH_STATISTICS 0x16
+#define FR_LIST_ACTIVE_DLCI 0x17
+#define FR_FLUSH_DATA_BUFFERS 0x18
+#define FR_READ_ADD_DLC_STATS 0x19
+#define FR_ADD_DLCI 0x20
+#define FR_DELETE_DLCI 0x21
+#define FR_ACTIVATE_DLCI 0x22
+#define FR_DEACTIVATE_DLCI 0x22
+#define FR_READ_MODEM_STATUS 0x30
+#define FR_SET_MODEM_STATUS 0x31
+#define FR_READ_ERROR_STATS 0x32
+#define FR_FLUSH_ERROR_STATS 0x33
+#define FR_READ_DLCI_IB_MAPPING 0x34
+#define FR_READ_CODE_VERSION 0x40
+#define FR_SET_INTR_MODE 0x50
+#define FR_READ_INTR_MODE 0x51
+#define FR_SET_TRACE_CONFIG 0x60
+#define FR_FT1_STATUS_CTRL 0x80
+#define FR_SET_FT1_MODE 0x81
+
+/* Special UDP drivers management commands */
+#define FPIPE_ENABLE_TRACING 0x41
+#define FPIPE_DISABLE_TRACING 0x42
+#define FPIPE_GET_TRACE_INFO 0x43
+#define FPIPE_FT1_READ_STATUS 0x44
+#define FPIPE_DRIVER_STAT_IFSEND 0x45
+#define FPIPE_DRIVER_STAT_INTR 0x46
+#define FPIPE_DRIVER_STAT_GEN 0x47
+#define FPIPE_FLUSH_DRIVER_STATS 0x48
+#define FPIPE_ROUTER_UP_TIME 0x49
+
+/* 'result' field defines */
+#define FRRES_OK 0x00 /* command executed successfully */
+#define FRRES_DISABLED 0x01 /* communications not enabled */
+#define FRRES_INOPERATIVE 0x02 /* channel inoperative */
+#define FRRES_DLCI_INACTIVE 0x03 /* DLCI is inactive */
+#define FRRES_DLCI_INVALID 0x04 /* DLCI is not configured */
+#define FRRES_TOO_LONG 0x05
+#define FRRES_TOO_MANY 0x06
+#define FRRES_CIR_OVERFLOW 0x07 /* Tx throughput has exceeded CIR */
+#define FRRES_BUFFER_OVERFLOW 0x08
+#define FRRES_MODEM_FAILURE 0x10 /* DCD and/or CTS dropped */
+#define FRRES_CHANNEL_DOWN 0x11 /* channel became inoperative */
+#define FRRES_CHANNEL_UP 0x12 /* channel became operative */
+#define FRRES_DLCI_CHANGE 0x13 /* DLCI status (or number) changed */
+#define FRRES_DLCI_MISMATCH 0x14
+#define FRRES_INVALID_CMD 0x1F /* invalid command */
+
+/* 'attr' field defines */
+#define FRATTR_
+
+/*----------------------------------------------------------------------------
+ * Frame relay mailbox.
+ * This structure is located at offset FR50?_MBOX_OFFS into FR_MB_VECTOR.
+ * For S502 it is also located at offset FR502_RXMB_OFFS into
+ * FR502_RX_VECTOR.
+ */
+typedef struct fr_mbox
+{
+ unsigned char opflag PACKED; /* 00h: execution flag */
+ fr_cmd_t cmd PACKED; /* 01h: command block */
+ unsigned char data[1] PACKED; /* 10h: variable length data buffer */
+} fr_mbox_t;
+
+/*----------------------------------------------------------------------------
+ * S502 frame relay status flags.
+ * This structure is located at offset FR502_FLAG_OFFS into FR_MB_VECTOR.
+ */
+typedef struct fr502_flags
+{
+ unsigned char rsrv1[1] PACKED; /* 00h: */
+ unsigned char tx_ready PACKED; /* 01h: Tx buffer available */
+ unsigned char rx_ready PACKED; /* 02h: Rx frame available */
+ unsigned char event PACKED; /* 03h: asynchronous event */
+ unsigned char mstatus PACKED; /* 04h: modem status */
+ unsigned char rsrv2[8] PACKED; /* 05h: */
+ unsigned char iflag PACKED; /* 0Dh: interrupt flag */
+ unsigned char imask PACKED; /* 0Eh: interrupt mask */
+} fr502_flags_t;
+
+/*----------------------------------------------------------------------------
+ * S508 frame relay status flags.
+ * This structure is located at offset FR508_FLAG_OFFS into FR_MB_VECTOR.
+ */
+typedef struct fr508_flags
+{
+ unsigned char rsrv1[3] PACKED; /* 00h: reserved */
+ unsigned char event PACKED; /* 03h: asynchronous event */
+ unsigned char mstatus PACKED; /* 04h: modem status */
+ unsigned char rsrv2[11] PACKED; /* 05h: reserved */
+ unsigned char iflag PACKED; /* 10h: interrupt flag */
+ unsigned char imask PACKED; /* 11h: interrupt mask */
+ unsigned long tse_offs PACKED; /* 12h: Tx status element */
+ unsigned short dlci PACKED; /* 16h: DLCI NUMBER */
+} fr508_flags_t;
+
+/* 'event' field defines */
+#define FR_EVENT_STATUS 0x01 /* channel status change */
+#define FR_EVENT_DLC_STATUS 0x02 /* DLC status change */
+#define FR_EVENT_BAD_DLCI 0x04 /* FSR included wrong DLCI */
+#define FR_EVENT_LINK_DOWN 0x40 /* DCD or CTS low */
+
+/* 'mstatus' field defines */
+#define FR_MDM_DCD 0x08 /* mdm_status: DCD */
+#define FR_MDM_CTS 0x20 /* mdm_status: CTS */
+
+/* 'iflag' & 'imask' fields defines */
+#define FR_INTR_RXRDY 0x01 /* Rx ready */
+#define FR_INTR_TXRDY 0x02 /* Tx ready */
+#define FR_INTR_MODEM 0x04 /* modem status change (DCD, CTS) */
+#define FR_INTR_READY 0x08 /* interface command completed */
+#define FR_INTR_DLC 0x10 /* DLC status change */
+#define FR_INTR_TIMER 0x20 /* millisecond timer */
+#define FR_INTR_TX_MULT_DLCIs 0x80 /* Tx interrupt on multiple DLCIs */
+
+
+/*----------------------------------------------------------------------------
+ * Receive Buffer Configuration Info. S508 only!
+ * This structure is located at offset FR508_RXBC_OFFS into FR_MB_VECTOR.
+ */
+typedef struct fr_buf_info
+{
+ unsigned short rse_num PACKED; /* 00h: number of status elements */
+ unsigned long rse_base PACKED; /* 02h: receive status array base */
+ unsigned long rse_next PACKED; /* 06h: next status element */
+ unsigned long buf_base PACKED; /* 0Ah: rotational buffer base */
+ unsigned short reserved PACKED; /* 0Eh: */
+ unsigned long buf_top PACKED; /* 10h: rotational buffer top */
+} fr_buf_info_t;
+
+/*----------------------------------------------------------------------------
+ * Buffer Status Element. S508 only!
+ * Array of structures of this type is located at offset defined by the
+ * 'rse_base' field of the frBufInfo_t structure into absolute adapter
+ * memory address space.
+ */
+typedef struct fr_rx_buf_ctl
+{
+ unsigned char flag PACKED; /* 00h: ready flag */
+ unsigned short length PACKED; /* 01h: frame length */
+ unsigned short dlci PACKED; /* 03h: DLCI */
+ unsigned char attr PACKED; /* 05h: FECN/BECN/DE/CR */
+ unsigned short tmstamp PACKED; /* 06h: time stamp */
+ unsigned short rsrv[2] PACKED; /* 08h: */
+ unsigned long offset PACKED; /* 0Ch: buffer absolute address */
+} fr_rx_buf_ctl_t;
+
+typedef struct fr_tx_buf_ctl
+{
+ unsigned char flag PACKED; /* 00h: ready flag */
+ unsigned short rsrv0[2] PACKED; /* 01h: */
+ unsigned short length PACKED; /* 05h: frame length */
+ unsigned short dlci PACKED; /* 07h: DLCI */
+ unsigned char attr PACKED; /* 09h: FECN/BECN/DE/CR */
+ unsigned short rsrv1 PACKED; /* 0Ah: */
+ unsigned long offset PACKED; /* 0Ch: buffer absolute address */
+} fr_tx_buf_ctl_t;
+
+/*----------------------------------------------------------------------------
+ * Global Configuration Block. Passed to FR_SET_CONFIG command when dlci == 0.
+ */
+typedef struct fr_conf
+{
+ unsigned short station PACKED; /* 00h: CPE/Node */
+ unsigned short options PACKED; /* 02h: configuration options */
+ unsigned short kbps PACKED; /* 04h: baud rate in kbps */
+ unsigned short port PACKED; /* 06h: RS-232/V.35 */
+ unsigned short mtu PACKED; /* 08h: max. transmit length */
+ unsigned short t391 PACKED; /* 0Ah: */
+ unsigned short t392 PACKED; /* 0Ch: */
+ unsigned short n391 PACKED; /* 0Eh: */
+ unsigned short n392 PACKED; /* 10h: */
+ unsigned short n393 PACKED; /* 12h: */
+ unsigned short cir_fwd PACKED; /* 14h: */
+ unsigned short bc_fwd PACKED; /* 16h: */
+ unsigned short be_fwd PACKED; /* 18h: */
+ unsigned short cir_bwd PACKED; /* 1Ah: */
+ unsigned short bc_bwd PACKED; /* 1Ch: */
+ unsigned short be_bwd PACKED; /* 1Eh: */
+ unsigned short dlci[0] PACKED; /* 20h: */
+} fr_conf_t;
+
+/* 'station_type' defines */
+#define FRCFG_STATION_CPE 0
+#define FRCFG_STATION_NODE 1
+
+/* 'conf_flags' defines */
+#define FRCFG_IGNORE_TX_CIR 0x0001
+#define FRCFG_IGNORE_RX_CIR 0x0002
+#define FRCFG_DONT_RETRANSMIT 0x0004
+#define FRCFG_IGNORE_CBS 0x0008
+#define FRCFG_THROUGHPUT 0x0010 /* enable throughput calculation */
+#define FRCFG_DIRECT_RX 0x0080 /* enable direct receive buffer */
+#define FRCFG_AUTO_CONFIG 0x8000 /* enable auto DLCI configuration */
+
+/* 'baud_rate' defines */
+#define FRCFG_BAUD_1200 12
+#define FRCFG_BAUD_2400 24
+#define FRCFG_BAUD_4800 48
+#define FRCFG_BAUD_9600 96
+#define FRCFG_BAUD_19200 19
+#define FRCFG_BAUD_38400 38
+#define FRCFG_BAUD_56000 56
+#define FRCFG_BAUD_64000 64
+#define FRCFG_BAUD_128000 128
+
+/* 'port_mode' defines */
+#define FRCFG_MODE_EXT_CLK 0x0000
+#define FRCFG_MODE_INT_CLK 0x0001
+#define FRCFG_MODE_V35 0x0000 /* S508 only */
+#define FRCFG_MODE_RS232 0x0002 /* S508 only */
+
+/* defines for line tracing */
+
+/* the line trace status element presented by the frame relay code */
+typedef struct {
+ unsigned char flag PACKED; /* ready flag */
+ unsigned short length PACKED; /* trace length */
+ unsigned char rsrv0[2] PACKED; /* reserved */
+ unsigned char attr PACKED; /* trace attributes */
+ unsigned short tmstamp PACKED; /* time stamp */
+ unsigned char rsrv1[4] PACKED; /* reserved */
+ unsigned long offset PACKED; /* buffer absolute address */
+} fr_trc_el_t;
+
+typedef struct {
+ unsigned char status PACKED; /* status flag */
+ unsigned char data_passed PACKED; /* 0 if no data passed, 1 if */
+ /* data passed */
+ unsigned short length PACKED; /* frame length */
+ unsigned short tmstamp PACKED; /* time stamp */
+} fpipemon_trc_hdr_t;
+
+typedef struct {
+ fpipemon_trc_hdr_t fpipemon_trc_hdr PACKED;
+ unsigned char data[FR_MAX_NO_DATA_BYTES_IN_FRAME] PACKED;
+} fpipemon_trc_t;
+
+/* bit settings for the 'status' byte - note that bits 1, 2 and 3 are used */
+/* for returning the number of frames being passed to fpipemon */
+#define TRC_OUTGOING_FRM 0x01
+#define TRC_ABORT_ERROR 0x10
+#define TRC_CRC_ERROR 0x20
+#define TRC_OVERRUN_ERROR 0x40
+#define MORE_TRC_DATA 0x80
+
+#define MAX_FRMS_TRACED 0x07
+
+#define NO_TRC_ELEMENTS_OFF 0x9000
+#define BASE_TRC_ELEMENTS_OFF 0x9002
+#define TRC_ACTIVE 0x01
+#define FLUSH_TRC_BUFFERS 0x02
+#define FLUSH_TRC_STATISTICS 0x04
+#define TRC_SIGNALLING_FRMS 0x10
+#define TRC_INFO_FRMS 0x20
+#define ACTIVATE_TRC (TRC_ACTIVE | TRC_SIGNALLING_FRMS | TRC_INFO_FRMS)
+#define RESET_TRC (FLUSH_TRC_BUFFERS | FLUSH_TRC_STATISTICS)
+
+/*----------------------------------------------------------------------------
+ * Channel configuration.
+ * This structure is passed to the FR_SET_CONFIG command when dlci != 0.
+ */
+typedef struct fr_dlc_conf
+{
+ unsigned short conf_flags PACKED; /* 00h: configuration bits */
+ unsigned short cir_fwd PACKED; /* 02h: */
+ unsigned short bc_fwd PACKED; /* 04h: */
+ unsigned short be_fwd PACKED; /* 06h: */
+ unsigned short cir_bwd PACKED; /* 08h: */
+ unsigned short bc_bwd PACKED; /* 0Ah: */
+ unsigned short be_bwd PACKED; /* 0Ch: */
+} fr_dlc_conf_t;
+
+/*----------------------------------------------------------------------------
+ * S502 interrupt mode control block.
+ * This structure is passed to the FR_SET_INTR_FLAGS and returned by the
+ * FR_READ_INTR_FLAGS commands.
+ */
+typedef struct fr502_intr_ctl
+{
+ unsigned char mode PACKED; /* 00h: interrupt enable flags */
+ unsigned short tx_len PACKED; /* 01h: required Tx buffer size */
+} fr502_intr_ctl_t;
+
+/*----------------------------------------------------------------------------
+ * S508 interrupt mode control block.
+ * This structure is passed to the FR_SET_INTR_FLAGS and returned by the
+ * FR_READ_INTR_FLAGS commands.
+ */
+typedef struct fr508_intr_ctl
+{
+ unsigned char mode PACKED; /* 00h: interrupt enable flags */
+ unsigned short tx_len PACKED; /* 01h: required Tx buffer size */
+ unsigned char irq PACKED; /* 03h: IRQ level to activate */
+ unsigned char flags PACKED; /* 04h: ?? */
+ unsigned short timeout PACKED; /* 05h: ms, for timer interrupt */
+} fr508_intr_ctl_t;
+
+/*----------------------------------------------------------------------------
+ * Channel status.
+ * This structure is returned by the FR_READ_STATUS command.
+ */
+typedef struct fr_dlc_Status
+{
+ unsigned char status PACKED; /* 00h: link/DLCI status */
+ struct
+ {
+ unsigned short dlci PACKED; /* 01h: DLCI number */
+ unsigned char status PACKED; /* 03h: DLCI status */
+ } circuit[1] PACKED;
+} fr_dlc_status_t;
+
+/* 'status' defines */
+#define FR_LINK_INOPER 0x00 /* for global status (DLCI == 0) */
+#define FR_LINK_OPER 0x01
+#define FR_DLCI_DELETED 0x01 /* for circuit status (DLCI != 0) */
+#define FR_DLCI_ACTIVE 0x02
+#define FR_DLCI_WAITING 0x04
+#define FR_DLCI_NEW 0x08
+#define FR_DLCI_REPORT 0x40
+
+/*----------------------------------------------------------------------------
+ * Global Statistics Block.
+ * This structure is returned by the FR_READ_STATISTICS command when
+ * dcli == 0.
+ */
+typedef struct fr_link_stat
+{
+ unsigned short rx_too_long PACKED; /* 00h: */
+ unsigned short rx_dropped PACKED; /* 02h: */
+ unsigned short rx_dropped2 PACKED; /* 04h: */
+ unsigned short rx_bad_dlci PACKED; /* 06h: */
+ unsigned short rx_bad_format PACKED; /* 08h: */
+ unsigned short retransmitted PACKED; /* 0Ah: */
+ unsigned short cpe_tx_FSE PACKED; /* 0Ch: */
+ unsigned short cpe_tx_LIV PACKED; /* 0Eh: */
+ unsigned short cpe_rx_FSR PACKED; /* 10h: */
+ unsigned short cpe_rx_LIV PACKED; /* 12h: */
+ unsigned short node_rx_FSE PACKED; /* 14h: */
+ unsigned short node_rx_LIV PACKED; /* 16h: */
+ unsigned short node_tx_FSR PACKED; /* 18h: */
+ unsigned short node_tx_LIV PACKED; /* 1Ah: */
+ unsigned short rx_ISF_err PACKED; /* 1Ch: */
+ unsigned short rx_unsolicited PACKED; /* 1Eh: */
+ unsigned short rx_SSN_err PACKED; /* 20h: */
+ unsigned short rx_RSN_err PACKED; /* 22h: */
+ unsigned short T391_timeouts PACKED; /* 24h: */
+ unsigned short T392_timeouts PACKED; /* 26h: */
+ unsigned short N392_reached PACKED; /* 28h: */
+ unsigned short cpe_SSN_RSN PACKED; /* 2Ah: */
+ unsigned short current_SSN PACKED; /* 2Ch: */
+ unsigned short current_RSN PACKED; /* 2Eh: */
+ unsigned short curreny_T391 PACKED; /* 30h: */
+ unsigned short current_T392 PACKED; /* 32h: */
+ unsigned short current_N392 PACKED; /* 34h: */
+ unsigned short current_N393 PACKED; /* 36h: */
+} fr_link_stat_t;
+
+/*----------------------------------------------------------------------------
+ * DLCI statistics.
+ * This structure is returned by the FR_READ_STATISTICS command when
+ * dlci != 0.
+ */
+typedef struct fr_dlci_stat
+{
+ unsigned long tx_frames PACKED; /* 00h: */
+ unsigned long tx_bytes PACKED; /* 04h: */
+ unsigned long rx_frames PACKED; /* 08h: */
+ unsigned long rx_bytes PACKED; /* 0Ch: */
+ unsigned long rx_dropped PACKED; /* 10h: */
+ unsigned long rx_inactive PACKED; /* 14h: */
+ unsigned long rx_exceed_CIR PACKED; /* 18h: */
+ unsigned long rx_DE_set PACKED; /* 1Ch: */
+ unsigned long tx_throughput PACKED; /* 20h: */
+ unsigned long tx_calc_timer PACKED; /* 24h: */
+ unsigned long rx_throughput PACKED; /* 28h: */
+ unsigned long rx_calc_timer PACKED; /* 2Ch: */
+} fr_dlci_stat_t;
+
+/*----------------------------------------------------------------------------
+ * Communications error statistics.
+ * This structure is returned by the FR_READ_ERROR_STATS command.
+ */
+typedef struct fr_comm_stat
+{
+ unsigned char rx_overruns PACKED; /* 00h: */
+ unsigned char rx_bad_crc PACKED; /* 01h: */
+ unsigned char rx_aborts PACKED; /* 02h: */
+ unsigned char rx_too_long PACKED; /* 03h: */
+ unsigned char tx_aborts PACKED; /* 04h: */
+ unsigned char tx_underruns PACKED; /* 05h: */
+ unsigned char tx_missed_undr PACKED; /* 06h: */
+ unsigned char dcd_dropped PACKED; /* 07h: */
+ unsigned char cts_dropped PACKED; /* 08h: */
+} fr_comm_stat_t;
+
+/*----------------------------------------------------------------------------
+ * Defines for the FR_ISSUE_IS_FRAME command.
+ */
+#define FR_ISF_LVE 2 /* issue Link Verification Enquiry */
+#define FR_ISF_FSE 3 /* issue Full Status Enquiry */
+
+/*----------------------------------------------------------------------------
+ * Frame Relay ARP Header -- Used for Dynamic route creation with InvARP
+ */
+
+typedef struct arphdr_fr
+ {
+ unsigned short ar_hrd PACKED; /* format of hardware addr */
+ unsigned short ar_pro PACKED; /* format of protocol addr */
+ unsigned char ar_hln PACKED; /* length of hardware addr */
+ unsigned char ar_pln PACKED; /* length of protocol addr */
+ unsigned short ar_op PACKED; /* ARP opcode */
+ unsigned short ar_sha PACKED; /* Sender DLCI addr 2 bytes */
+ unsigned long ar_sip PACKED; /* Sender IP addr 4 bytes */
+ unsigned short ar_tha PACKED; /* Target DLCI addr 2 bytes */
+ unsigned long ar_tip PACKED; /* Target IP addr 4 bytes */
+ } arphdr_fr_t;
+
+/*----------------------------------------------------------------------------
+ * Frame Relay RFC 1490 SNAP Header -- Used to check for ARP packets
+ */
+typedef struct arphdr_1490
+ {
+ unsigned char control PACKED; /* UI, etc... */
+ unsigned char pad PACKED; /* Pad */
+ unsigned char NLPID PACKED; /* SNAP */
+ unsigned char OUI[3] PACKED; /* Ethertype, etc... */
+ unsigned short PID PACKED; /* ARP, IP, etc... */
+ } arphdr_1490_t;
+
+/* UDP/IP packet (for UDP management) layout */
+
+/* The embedded control block for UDP mgmt
+ This is essentially a mailbox structure, without the large data field */
+
+typedef struct {
+ unsigned char opp_flag PACKED; /* the opp flag */
+ unsigned char command PACKED; /* command code */
+ unsigned short length PACKED; /* length of data buffer */
+ unsigned char result PACKED; /* return code */
+ unsigned short dlci PACKED; /* DLCI number */
+ unsigned char attr PACKED; /* FECN, BECN, DE and C/R bits */
+ unsigned short rxlost1 PACKED; /* frames discarded at int. level */
+ unsigned long rxlost2 PACKED; /* frames discarded at app. level */
+ unsigned char rsrv[2] PACKED; /* reserved for future use */
+} cblock_t;
+
+
+/* UDP management packet layout (data area of ip packet) */
+
+typedef struct {
+ unsigned char control PACKED;
+ unsigned char NLPID PACKED;
+} fr_encap_hdr_t;
+
+typedef struct {
+ fr_encap_hdr_t fr_encap_hdr PACKED;
+ ip_pkt_t ip_pkt PACKED;
+ udp_pkt_t udp_pkt PACKED;
+ wp_mgmt_t wp_mgmt PACKED;
+ cblock_t cblock PACKED;
+ unsigned char data[4080] PACKED;
+} fr_udp_pkt_t;
+
+
+/* valid ip_protocol for UDP management */
+#define UDPMGMT_UDP_PROTOCOL 0x11
+
+#define UDPMGMT_FPIPE_SIGNATURE "FPIPE8ND"
+#define UDPMGMT_DRVRSTATS_SIGNATURE "DRVSTATS"
+
+/* values for request/reply byte */
+#define UDPMGMT_REQUEST 0x01
+#define UDPMGMT_REPLY 0x02
+#define UDP_OFFSET 12
+
+typedef struct {
+ unsigned long if_send_entry;
+ unsigned long if_send_skb_null;
+ unsigned long if_send_broadcast;
+ unsigned long if_send_multicast;
+ unsigned long if_send_critical_ISR;
+ unsigned long if_send_critical_non_ISR;
+ unsigned long if_send_busy;
+ unsigned long if_send_busy_timeout;
+ unsigned long if_send_DRVSTATS_request;
+ unsigned long if_send_FPIPE_request;
+ unsigned long if_send_wan_disconnected;
+ unsigned long if_send_dlci_disconnected;
+ unsigned long if_send_no_bfrs;
+ unsigned long if_send_adptr_bfrs_full;
+ unsigned long if_send_bfrs_passed_to_adptr;
+ unsigned long if_send_consec_send_fail;
+} drvstats_if_send_t;
+
+typedef struct {
+ unsigned long rx_intr_no_socket;
+ unsigned long rx_intr_dev_not_started;
+ unsigned long rx_intr_DRVSTATS_request;
+ unsigned long rx_intr_FPIPE_request;
+ unsigned long rx_intr_bfr_not_passed_to_stack;
+ unsigned long rx_intr_bfr_passed_to_stack;
+ } drvstats_rx_intr_t;
+
+typedef struct {
+ unsigned long UDP_FPIPE_mgmt_kmalloc_err;
+ unsigned long UDP_FPIPE_mgmt_direction_err;
+ unsigned long UDP_FPIPE_mgmt_adptr_type_err;
+ unsigned long UDP_FPIPE_mgmt_adptr_cmnd_OK;
+ unsigned long UDP_FPIPE_mgmt_adptr_cmnd_timeout;
+ unsigned long UDP_FPIPE_mgmt_adptr_send_passed;
+ unsigned long UDP_FPIPE_mgmt_adptr_send_failed;
+ unsigned long UDP_FPIPE_mgmt_not_passed_to_stack;
+ unsigned long UDP_FPIPE_mgmt_passed_to_stack;
+ unsigned long UDP_FPIPE_mgmt_no_socket;
+ unsigned long UDP_DRVSTATS_mgmt_kmalloc_err;
+ unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_OK;
+ unsigned long UDP_DRVSTATS_mgmt_adptr_cmnd_timeout;
+ unsigned long UDP_DRVSTATS_mgmt_adptr_send_passed;
+ unsigned long UDP_DRVSTATS_mgmt_adptr_send_failed;
+ unsigned long UDP_DRVSTATS_mgmt_not_passed_to_stack;
+ unsigned long UDP_DRVSTATS_mgmt_passed_to_stack;
+ unsigned long UDP_DRVSTATS_mgmt_no_socket;
+} drvstats_gen_t;
+
+typedef struct {
+ unsigned char attr PACKED;
+ unsigned short time_stamp PACKED;
+ unsigned char reserved[13] PACKED;
+} api_rx_hdr_t;
+
+typedef struct {
+ api_rx_hdr_t api_rx_hdr PACKED;
+ void * data PACKED;
+} api_rx_element_t;
+
+typedef struct {
+ unsigned char attr PACKED;
+ unsigned char reserved[15] PACKED;
+} api_tx_hdr_t;
+
+typedef struct {
+ api_tx_hdr_t api_tx_hdr PACKED;
+ void * data PACKED;
+} api_tx_element_t;
+
+#ifdef _MSC_
+# pragma pack()
+#endif
+#endif /* _SDLA_FR_H */
diff --git a/pfinet/linux-src/include/linux/sdla_ppp.h b/pfinet/linux-src/include/linux/sdla_ppp.h
new file mode 100644
index 00000000..95d5c0fe
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sdla_ppp.h
@@ -0,0 +1,573 @@
+/*****************************************************************************
+* sdla_ppp.h Sangoma PPP firmware API definitions.
+*
+* Author: Gene Kozin <74604.152@compuserve.com>
+*
+* Copyright: (c) 1995-1997 Sangoma Technologies Inc.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version
+* 2 of the License, or (at your option) any later version.
+* ============================================================================
+* Jan 06, 1997 Gene Kozin v2.0
+* Apr 11, 1996 Gene Kozin Initial version.
+*****************************************************************************/
+#ifndef _SDLA_PPP_H
+#define _SDLA_PPP_H
+
+/*----------------------------------------------------------------------------
+ * Notes:
+ * ------
+ * 1. All structures defined in this file are byte-alined.
+ *
+ * Compiler Platform
+ * -------- --------
+ * GNU C Linux
+ */
+
+#ifndef PACKED
+# define PACKED __attribute__((packed))
+#endif /* PACKED */
+
+/* Adapter memory layout and important constants */
+#define PPP508_MB_VECT 0xE000 /* mailbox window vector */
+#define PPP508_MB_OFFS 0 /* mailbox offset */
+#define PPP508_FLG_OFFS 0x1000 /* status flags offset */
+#define PPP508_BUF_OFFS 0x1100 /* buffer info block offset */
+#define PPP514_MB_OFFS 0xE000 /* mailbox offset */
+#define PPP514_FLG_OFFS 0xF000 /* status flags offset */
+#define PPP514_BUF_OFFS 0xF100 /* buffer info block offset */
+
+#define PPP_MAX_DATA 1008 /* command block data buffer length */
+
+/****** Data Structures *****************************************************/
+
+/*----------------------------------------------------------------------------
+ * PPP Command Block.
+ */
+typedef struct ppp_cmd{
+ unsigned char command PACKED; /* command code */
+ unsigned short length PACKED; /* length of data buffer */
+ unsigned char result PACKED; /* return code */
+ unsigned char rsrv[11] PACKED; /* reserved for future use */
+} ppp_cmd_t;
+
+typedef struct cblock{
+ unsigned char opp_flag PACKED;
+ unsigned char command PACKED; /* command code */
+ unsigned short length PACKED; /* length of data buffer */
+ unsigned char result PACKED; /* return code */
+ unsigned char rsrv[11] PACKED; /* reserved for future use */
+} cblock_t;
+
+typedef struct ppp_udp_pkt{
+ ip_pkt_t ip_pkt PACKED;
+ udp_pkt_t udp_pkt PACKED;
+ wp_mgmt_t wp_mgmt PACKED;
+ cblock_t cblock PACKED;
+ unsigned char data[MAX_LGTH_UDP_MGNT_PKT] PACKED;
+} ppp_udp_pkt_t;
+
+typedef struct {
+ unsigned char status PACKED;
+ unsigned char data_avail PACKED;
+ unsigned short real_length PACKED;
+ unsigned short time_stamp PACKED;
+ unsigned char data[1] PACKED;
+} trace_pkt_t;
+
+
+typedef struct {
+ unsigned char opp_flag PACKED;
+ unsigned char trace_type PACKED;
+ unsigned short trace_length PACKED;
+ unsigned short trace_data_ptr PACKED;
+ unsigned short trace_time_stamp PACKED;
+} trace_element_t;
+
+/* 'command' field defines */
+#define PPP_READ_CODE_VERSION 0x10 /* configuration commands */
+#define PPP_SET_CONFIG 0x05
+#define PPP_READ_CONFIG 0x06
+#define PPP_SET_INTR_FLAGS 0x20
+#define PPP_READ_INTR_FLAGS 0x21
+#define PPP_SET_INBOUND_AUTH 0x30
+#define PPP_SET_OUTBOUND_AUTH 0x31
+#define PPP_GET_CONNECTION_INFO 0x32
+
+#define PPP_COMM_ENABLE 0x03 /* operational commands */
+#define PPP_COMM_DISABLE 0x04
+#define PPP_SEND_SIGN_FRAME 0x23
+#define PPP_READ_SIGN_RESPONSE 0x24
+#define PPP_DATALINE_MONITOR 0x33
+
+#define PPP_READ_STATISTICS 0x07 /* statistics commands */
+#define PPP_FLUSH_STATISTICS 0x08
+#define PPP_READ_ERROR_STATS 0x09
+#define PPP_FLUSH_ERROR_STATS 0x0A
+#define PPP_READ_PACKET_STATS 0x12
+#define PPP_FLUSH_PACKET_STATS 0x13
+#define PPP_READ_LCP_STATS 0x14
+#define PPP_FLUSH_LCP_STATS 0x15
+#define PPP_READ_LPBK_STATS 0x16
+#define PPP_FLUSH_LPBK_STATS 0x17
+#define PPP_READ_IPCP_STATS 0x18
+#define PPP_FLUSH_IPCP_STATS 0x19
+#define PPP_READ_IPXCP_STATS 0x1A
+#define PPP_FLUSH_IPXCP_STATS 0x1B
+#define PPP_READ_PAP_STATS 0x1C
+#define PPP_FLUSH_PAP_STATS 0x1D
+#define PPP_READ_CHAP_STATS 0x1E
+#define PPP_FLUSH_CHAP_STATS 0x1F
+
+/* 'result' field defines */
+#define PPPRES_OK 0x00 /* command executed successfully */
+#define PPPRES_INVALID_STATE 0x09 /* invalid command in this context */
+
+/*----------------------------------------------------------------------------
+ * PPP Mailbox.
+ * This structure is located at offset PPP???_MB_OFFS into PPP???_MB_VECT
+ */
+typedef struct ppp_mbox
+{
+ unsigned char flag PACKED; /* 00h: command execution flag */
+ ppp_cmd_t cmd PACKED; /* 01h: command block */
+ unsigned char data[1] PACKED; /* 10h: variable length data buffer */
+} ppp_mbox_t;
+
+/*----------------------------------------------------------------------------
+ * PPP Status Flags.
+ * This structure is located at offset PPP???_FLG_OFFS into
+ * PPP???_MB_VECT.
+ */
+typedef struct ppp_flags
+{
+ unsigned char iflag PACKED; /* 00: interrupt flag */
+ unsigned char imask PACKED; /* 01: interrupt mask */
+ unsigned char resrv PACKED;
+ unsigned char mstatus PACKED; /* 03: modem status */
+ unsigned char lcp_state PACKED; /* 04: LCP state */
+ unsigned char ppp_phase PACKED; /* 05: PPP phase */
+ unsigned char ip_state PACKED; /* 06: IPCP state */
+ unsigned char ipx_state PACKED; /* 07: IPXCP state */
+ unsigned char pap_state PACKED; /* 08: PAP state */
+ unsigned char chap_state PACKED; /* 09: CHAP state */
+ unsigned short disc_cause PACKED; /* 0A: disconnection cause */
+} ppp_flags_t;
+
+/* 'iflag' defines */
+#define PPP_INTR_RXRDY 0x01 /* Rx ready */
+#define PPP_INTR_TXRDY 0x02 /* Tx ready */
+#define PPP_INTR_MODEM 0x04 /* modem status change (DCD, CTS) */
+#define PPP_INTR_CMD 0x08 /* interface command completed */
+#define PPP_INTR_DISC 0x10 /* data link disconnected */
+#define PPP_INTR_OPEN 0x20 /* data link open */
+#define PPP_INTR_DROP_DTR 0x40 /* DTR drop timeout expired */
+#define PPP_INTR_TIMER 0x80 /* timer interrupt */
+
+
+/* 'mstatus' defines */
+#define PPP_MDM_DCD 0x08 /* mdm_status: DCD */
+#define PPP_MDM_CTS 0x20 /* mdm_status: CTS */
+
+/* 'disc_cause' defines */
+#define PPP_LOCAL_TERMINATION 0x0001 /* Local Request by PPP termination phase */
+#define PPP_DCD_CTS_DROP 0x0002 /* DCD and/or CTS dropped. Link down */
+#define PPP_REMOTE_TERMINATION 0x0800 /* Remote Request by PPP termination phase */
+
+/* 'misc_config_bits' defines */
+#define DONT_RE_TX_ABORTED_I_FRAMES 0x01
+#define TX_FRM_BYTE_COUNT_STATS 0x02
+#define RX_FRM_BYTE_COUNT_STATS 0x04
+#define TIME_STAMP_IN_RX_FRAMES 0x08
+#define NON_STD_ADPTR_FREQ 0x10
+#define INTERFACE_LEVEL_RS232 0x20
+#define AUTO_LINK_RECOVERY 0x100
+#define DONT_TERMINATE_LNK_MAX_CONFIG 0x200
+
+/* 'authentication options' defines */
+#define NO_AUTHENTICATION 0x00
+#define INBOUND_AUTH 0x80
+#define PAP_AUTH 0x01
+#define CHAP_AUTH 0x02
+
+/* 'ip options' defines */
+#define L_AND_R_IP_NO_ASSIG 0x00
+#define L_IP_LOCAL_ASSIG 0x01
+#define L_IP_REMOTE_ASSIG 0x02
+#define R_IP_LOCAL_ASSIG 0x04
+#define R_IP_REMOTE_ASSIG 0x08
+#define ENABLE_IP 0x80
+
+/* 'ipx options' defines */
+#define ROUTING_PROT_DEFAULT 0x20
+#define ENABLE_IPX 0x80
+#define DISABLE_IPX 0x00
+
+/*----------------------------------------------------------------------------
+ * PPP Buffer Info.
+ * This structure is located at offset PPP508_BUF_OFFS into
+ * PPP508_MB_VECT.
+ */
+typedef struct ppp508_buf_info
+{
+ unsigned short txb_num PACKED; /* 00: number of transmit buffers */
+ unsigned long txb_ptr PACKED; /* 02: pointer to the buffer ctl. */
+ unsigned char rsrv1[26] PACKED;
+ unsigned short rxb_num PACKED; /* 20: number of receive buffers */
+ unsigned long rxb_ptr PACKED; /* 22: pointer to the buffer ctl. */
+ unsigned long rxb1_ptr PACKED; /* 26: pointer to the first buf.ctl. */
+ unsigned long rxb_base PACKED; /* 2A: pointer to the buffer base */
+ unsigned char rsrv2[2] PACKED;
+ unsigned long rxb_end PACKED; /* 30: pointer to the buffer end */
+} ppp508_buf_info_t;
+
+/*----------------------------------------------------------------------------
+ * Transmit/Receive Buffer Control Block.
+ */
+typedef struct ppp_buf_ctl
+{
+ unsigned char flag PACKED; /* 00: 'buffer ready' flag */
+ unsigned short length PACKED; /* 01: length of data */
+ unsigned char reserved1[1] PACKED; /* 03: */
+ unsigned char proto PACKED; /* 04: protocol */
+ unsigned short timestamp PACKED; /* 05: time stamp (Rx only) */
+ unsigned char reserved2[5] PACKED; /* 07: */
+ union
+ {
+ unsigned short o_p[2]; /* 1C: buffer offset & page (S502) */
+ unsigned long ptr; /* 1C: buffer pointer (S508) */
+ } buf PACKED;
+} ppp_buf_ctl_t;
+
+/*----------------------------------------------------------------------------
+ * S508 Adapter Configuration Block (passed to the PPP_SET_CONFIG command).
+ */
+typedef struct ppp508_conf
+{
+ unsigned long line_speed PACKED; /* 00: baud rate, bps */
+ unsigned short txbuf_percent PACKED; /* 04: % of Tx buffer */
+ unsigned short conf_flags PACKED; /* 06: configuration bits */
+ unsigned short mtu_local PACKED; /* 08: local MTU */
+ unsigned short mtu_remote PACKED; /* 0A: remote MTU */
+ unsigned short restart_tmr PACKED; /* 0C: restart timer */
+ unsigned short auth_rsrt_tmr PACKED; /* 0E: authentication timer */
+ unsigned short auth_wait_tmr PACKED; /* 10: authentication timer */
+ unsigned short mdm_fail_tmr PACKED; /* 12: modem failure timer */
+ unsigned short dtr_drop_tmr PACKED; /* 14: DTR drop timer */
+ unsigned short connect_tmout PACKED; /* 16: connection timeout */
+ unsigned short conf_retry PACKED; /* 18: max. retry */
+ unsigned short term_retry PACKED; /* 1A: max. retry */
+ unsigned short fail_retry PACKED; /* 1C: max. retry */
+ unsigned short auth_retry PACKED; /* 1E: max. retry */
+ unsigned char auth_options PACKED; /* 20: authentication opt. */
+ unsigned char ip_options PACKED; /* 21: IP options */
+ unsigned long ip_local PACKED; /* 22: local IP address */
+ unsigned long ip_remote PACKED; /* 26: remote IP address */
+ unsigned char ipx_options PACKED; /* 2A: IPX options */
+ unsigned char ipx_netno[4] PACKED; /* 2B: IPX net number */
+ unsigned char ipx_local[6] PACKED; /* 2F: local IPX node number*/
+ unsigned char ipx_remote[6] PACKED; /* 35: remote IPX node num.*/
+ unsigned char ipx_router[48] PACKED; /* 3B: IPX router name*/
+ unsigned long alt_cpu_clock PACKED; /* 6B: */
+} ppp508_conf_t;
+
+/*----------------------------------------------------------------------------
+ * S508 Adapter Read Connection Information Block
+ * Returned by the PPP_GET_CONNECTION_INFO command
+ */
+typedef struct ppp508_connect_info
+{
+ unsigned short mru PACKED; /* 00-01 Remote Max Rec' Unit */
+ unsigned char ip_options PACKED; /* 02: Negotiated ip options */
+ unsigned long ip_local PACKED; /* 03-06: local IP address */
+ unsigned long ip_remote PACKED; /* 07-0A: remote IP address */
+ unsigned char ipx_options PACKED; /* 0B: Negotiated ipx options */
+ unsigned char ipx_netno[4] PACKED; /* 0C-0F: IPX net number */
+ unsigned char ipx_local[6] PACKED; /* 10-1F: local IPX node # */
+ unsigned char ipx_remote[6] PACKED; /* 16-1B: remote IPX node # */
+ unsigned char ipx_router[48] PACKED; /* 1C-4B: IPX router name */
+ unsigned char auth_status PACKED; /* 4C: Authentication Status */
+ unsigned char inbd_auth_peerID[1] PACKED; /* 4D: variable length inbound authenticated peer ID */
+} ppp508_connect_info_t;
+
+/* 'line_speed' field */
+#define PPP_BITRATE_1200 0x01
+#define PPP_BITRATE_2400 0x02
+#define PPP_BITRATE_4800 0x03
+#define PPP_BITRATE_9600 0x04
+#define PPP_BITRATE_19200 0x05
+#define PPP_BITRATE_38400 0x06
+#define PPP_BITRATE_45000 0x07
+#define PPP_BITRATE_56000 0x08
+#define PPP_BITRATE_64000 0x09
+#define PPP_BITRATE_74000 0x0A
+#define PPP_BITRATE_112000 0x0B
+#define PPP_BITRATE_128000 0x0C
+#define PPP_BITRATE_156000 0x0D
+
+/* Defines for the 'conf_flags' field */
+#define PPP_IGNORE_TX_ABORT 0x01 /* don't re-transmit aborted frames */
+#define PPP_ENABLE_TX_STATS 0x02 /* enable Tx statistics */
+#define PPP_ENABLE_RX_STATS 0x04 /* enable Rx statistics */
+#define PPP_ENABLE_TIMESTAMP 0x08 /* enable timestamp */
+
+/* 'ip_options' defines */
+#define PPP_LOCAL_IP_LOCAL 0x01
+#define PPP_LOCAL_IP_REMOTE 0x02
+#define PPP_REMOTE_IP_LOCAL 0x04
+#define PPP_REMOTE_IP_REMOTE 0x08
+
+/* 'ipx_options' defines */
+#define PPP_REMOTE_IPX_NETNO 0x01
+#define PPP_REMOTE_IPX_LOCAL 0x02
+#define PPP_REMOTE_IPX_REMOTE 0x04
+#define PPP_IPX_ROUTE_RIP_SAP 0x08
+#define PPP_IPX_ROUTE_NLSP 0x10
+#define PPP_IPX_ROUTE_DEFAULT 0x20
+#define PPP_IPX_CONF_COMPLETE 0x40
+#define PPP_IPX_ENABLE 0x80
+
+/*----------------------------------------------------------------------------
+ * S508 Adapter Configuration Block (returned by the PPP_READ_CONFIG command).
+ */
+typedef struct ppp508_get_conf
+{
+ unsigned long bps PACKED; /* 00: baud rate, bps */
+ ppp508_conf_t conf PACKED; /* 04: requested config. */
+ unsigned short txb_num PACKED; /* 6F: number of Tx buffers */
+ unsigned short rxb_num PACKED; /* 71: number of Rx buffers */
+} ppp508_get_conf_t;
+
+/*----------------------------------------------------------------------------
+ * S508 Operational Statistics (returned by the PPP_READ_STATISTIC command).
+ */
+typedef struct ppp508_stats
+{
+ unsigned short reserved1 PACKED; /* 00: */
+ unsigned short rx_bad_len PACKED; /* 02: */
+ unsigned short reserved2 PACKED; /* 04: */
+ unsigned long tx_frames PACKED; /* 06: */
+ unsigned long tx_bytes PACKED; /* 0A: */
+ unsigned long rx_frames PACKED; /* 0E: */
+ unsigned long rx_bytes PACKED; /* 12: */
+} ppp508_stats_t;
+
+/*----------------------------------------------------------------------------
+ * Adapter Error Statistics (returned by the PPP_READ_ERROR_STATS command).
+ */
+typedef struct ppp_err_stats
+{
+ unsigned char rx_overrun PACKED; /* 00: Rx overrun errors */
+ unsigned char rx_bad_crc PACKED; /* 01: Rx CRC errors */
+ unsigned char rx_abort PACKED; /* 02: Rx aborted frames */
+ unsigned char rx_lost PACKED; /* 03: Rx frames lost */
+ unsigned char tx_abort PACKED; /* 04: Tx aborted frames */
+ unsigned char tx_underrun PACKED; /* 05: Tx underrun errors */
+ unsigned char tx_missed_intr PACKED; /* 06: Tx underruns missed */
+ unsigned char reserved PACKED; /* 07: Tx underruns missed */
+ unsigned char dcd_trans PACKED; /* 08: DCD transitions */
+ unsigned char cts_trans PACKED; /* 09: CTS transitions */
+} ppp_err_stats_t;
+
+/*----------------------------------------------------------------------------
+ * Packet Statistics (returned by the PPP_READ_PACKET_STATS command).
+ */
+typedef struct ppp_pkt_stats
+{
+ unsigned short rx_bad_header PACKED; /* 00: */
+ unsigned short rx_prot_unknwn PACKED; /* 02: */
+ unsigned short rx_too_large PACKED; /* 04: */
+ unsigned short rx_lcp PACKED; /* 06: */
+ unsigned short tx_lcp PACKED; /* 08: */
+ unsigned short rx_ipcp PACKED; /* 0A: */
+ unsigned short tx_ipcp PACKED; /* 0C: */
+ unsigned short rx_ipxcp PACKED; /* 0E: */
+ unsigned short tx_ipxcp PACKED; /* 10: */
+ unsigned short rx_pap PACKED; /* 12: */
+ unsigned short tx_pap PACKED; /* 14: */
+ unsigned short rx_chap PACKED; /* 16: */
+ unsigned short tx_chap PACKED; /* 18: */
+ unsigned short rx_lqr PACKED; /* 1A: */
+ unsigned short tx_lqr PACKED; /* 1C: */
+ unsigned short rx_ip PACKED; /* 1E: */
+ unsigned short tx_ip PACKED; /* 20: */
+ unsigned short rx_ipx PACKED; /* 22: */
+ unsigned short tx_ipx PACKED; /* 24: */
+} ppp_pkt_stats_t;
+
+/*----------------------------------------------------------------------------
+ * LCP Statistics (returned by the PPP_READ_LCP_STATS command).
+ */
+typedef struct ppp_lcp_stats
+{
+ unsigned short rx_unknown PACKED; /* 00: unknown LCP type */
+ unsigned short rx_conf_rqst PACKED; /* 02: Configure-Request */
+ unsigned short rx_conf_ack PACKED; /* 04: Configure-Ack */
+ unsigned short rx_conf_nak PACKED; /* 06: Configure-Nak */
+ unsigned short rx_conf_rej PACKED; /* 08: Configure-Reject */
+ unsigned short rx_term_rqst PACKED; /* 0A: Terminate-Request */
+ unsigned short rx_term_ack PACKED; /* 0C: Terminate-Ack */
+ unsigned short rx_code_rej PACKED; /* 0E: Code-Reject */
+ unsigned short rx_proto_rej PACKED; /* 10: Protocol-Reject */
+ unsigned short rx_echo_rqst PACKED; /* 12: Echo-Request */
+ unsigned short rx_echo_reply PACKED; /* 14: Echo-Reply */
+ unsigned short rx_disc_rqst PACKED; /* 16: Discard-Request */
+ unsigned short tx_conf_rqst PACKED; /* 18: Configure-Request */
+ unsigned short tx_conf_ack PACKED; /* 1A: Configure-Ack */
+ unsigned short tx_conf_nak PACKED; /* 1C: Configure-Nak */
+ unsigned short tx_conf_rej PACKED; /* 1E: Configure-Reject */
+ unsigned short tx_term_rqst PACKED; /* 20: Terminate-Request */
+ unsigned short tx_term_ack PACKED; /* 22: Terminate-Ack */
+ unsigned short tx_code_rej PACKED; /* 24: Code-Reject */
+ unsigned short tx_proto_rej PACKED; /* 26: Protocol-Reject */
+ unsigned short tx_echo_rqst PACKED; /* 28: Echo-Request */
+ unsigned short tx_echo_reply PACKED; /* 2A: Echo-Reply */
+ unsigned short tx_disc_rqst PACKED; /* 2E: Discard-Request */
+ unsigned short rx_too_large PACKED; /* 30: packets too large */
+ unsigned short rx_ack_inval PACKED; /* 32: invalid Conf-Ack */
+ unsigned short rx_rej_inval PACKED; /* 34: invalid Conf-Reject */
+ unsigned short rx_rej_badid PACKED; /* 36: Conf-Reject w/bad ID */
+} ppp_lcp_stats_t;
+
+/*----------------------------------------------------------------------------
+ * Loopback Error Statistics (returned by the PPP_READ_LPBK_STATS command).
+ */
+typedef struct ppp_lpbk_stats
+{
+ unsigned short conf_magic PACKED; /* 00: */
+ unsigned short loc_echo_rqst PACKED; /* 02: */
+ unsigned short rem_echo_rqst PACKED; /* 04: */
+ unsigned short loc_echo_reply PACKED; /* 06: */
+ unsigned short rem_echo_reply PACKED; /* 08: */
+ unsigned short loc_disc_rqst PACKED; /* 0A: */
+ unsigned short rem_disc_rqst PACKED; /* 0C: */
+ unsigned short echo_tx_collsn PACKED; /* 0E: */
+ unsigned short echo_rx_collsn PACKED; /* 10: */
+} ppp_lpbk_stats_t;
+
+/*----------------------------------------------------------------------------
+ * Protocol Statistics (returned by the PPP_READ_IPCP_STATS and
+ * PPP_READ_IPXCP_STATS commands).
+ */
+typedef struct ppp_prot_stats
+{
+ unsigned short rx_unknown PACKED; /* 00: unknown type */
+ unsigned short rx_conf_rqst PACKED; /* 02: Configure-Request */
+ unsigned short rx_conf_ack PACKED; /* 04: Configure-Ack */
+ unsigned short rx_conf_nak PACKED; /* 06: Configure-Nak */
+ unsigned short rx_conf_rej PACKED; /* 08: Configure-Reject */
+ unsigned short rx_term_rqst PACKED; /* 0A: Terminate-Request */
+ unsigned short rx_term_ack PACKED; /* 0C: Terminate-Ack */
+ unsigned short rx_code_rej PACKED; /* 0E: Code-Reject */
+ unsigned short reserved PACKED; /* 10: */
+ unsigned short tx_conf_rqst PACKED; /* 12: Configure-Request */
+ unsigned short tx_conf_ack PACKED; /* 14: Configure-Ack */
+ unsigned short tx_conf_nak PACKED; /* 16: Configure-Nak */
+ unsigned short tx_conf_rej PACKED; /* 18: Configure-Reject */
+ unsigned short tx_term_rqst PACKED; /* 1A: Terminate-Request */
+ unsigned short tx_term_ack PACKED; /* 1C: Terminate-Ack */
+ unsigned short tx_code_rej PACKED; /* 1E: Code-Reject */
+ unsigned short rx_too_large PACKED; /* 20: packets too large */
+ unsigned short rx_ack_inval PACKED; /* 22: invalid Conf-Ack */
+ unsigned short rx_rej_inval PACKED; /* 24: invalid Conf-Reject */
+ unsigned short rx_rej_badid PACKED; /* 26: Conf-Reject w/bad ID */
+} ppp_prot_stats_t;
+
+/*----------------------------------------------------------------------------
+ * PAP Statistics (returned by the PPP_READ_PAP_STATS command).
+ */
+typedef struct ppp_pap_stats
+{
+ unsigned short rx_unknown PACKED; /* 00: unknown type */
+ unsigned short rx_auth_rqst PACKED; /* 02: Authenticate-Request */
+ unsigned short rx_auth_ack PACKED; /* 04: Authenticate-Ack */
+ unsigned short rx_auth_nak PACKED; /* 06: Authenticate-Nak */
+ unsigned short reserved PACKED; /* 08: */
+ unsigned short tx_auth_rqst PACKED; /* 0A: Authenticate-Request */
+ unsigned short tx_auth_ack PACKED; /* 0C: Authenticate-Ack */
+ unsigned short tx_auth_nak PACKED; /* 0E: Authenticate-Nak */
+ unsigned short rx_too_large PACKED; /* 10: packets too large */
+ unsigned short rx_bad_peerid PACKED; /* 12: invalid peer ID */
+ unsigned short rx_bad_passwd PACKED; /* 14: invalid password */
+} ppp_pap_stats_t;
+
+/*----------------------------------------------------------------------------
+ * CHAP Statistics (returned by the PPP_READ_CHAP_STATS command).
+ */
+typedef struct ppp_chap_stats
+{
+ unsigned short rx_unknown PACKED; /* 00: unknown type */
+ unsigned short rx_challenge PACKED; /* 02: Authenticate-Request */
+ unsigned short rx_response PACKED; /* 04: Authenticate-Ack */
+ unsigned short rx_success PACKED; /* 06: Authenticate-Nak */
+ unsigned short rx_failure PACKED; /* 08: Authenticate-Nak */
+ unsigned short reserved PACKED; /* 0A: */
+ unsigned short tx_challenge PACKED; /* 0C: Authenticate-Request */
+ unsigned short tx_response PACKED; /* 0E: Authenticate-Ack */
+ unsigned short tx_success PACKED; /* 10: Authenticate-Nak */
+ unsigned short tx_failure PACKED; /* 12: Authenticate-Nak */
+ unsigned short rx_too_large PACKED; /* 14: packets too large */
+ unsigned short rx_bad_peerid PACKED; /* 16: invalid peer ID */
+ unsigned short rx_bad_passwd PACKED; /* 18: invalid password */
+ unsigned short rx_bad_md5 PACKED; /* 1A: invalid MD5 format */
+ unsigned short rx_bad_resp PACKED; /* 1C: invalid response */
+} ppp_chap_stats_t;
+
+/*----------------------------------------------------------------------------
+ * Connection Information (returned by the PPP_GET_CONNECTION_INFO command).
+ */
+typedef struct ppp_conn_info
+{
+ unsigned short remote_mru PACKED; /* 00: */
+ unsigned char ip_options PACKED; /* 02: */
+ unsigned char ip_local[4] PACKED; /* 03: */
+ unsigned char ip_remote[4] PACKED; /* 07: */
+ unsigned char ipx_options PACKED; /* 0B: */
+ unsigned char ipx_network[4] PACKED; /* 0C: */
+ unsigned char ipx_local[6] PACKED; /* 10: */
+ unsigned char ipx_remote[6] PACKED; /* 16: */
+ unsigned char ipx_router[48] PACKED; /* 1C: */
+ unsigned char auth_status PACKED; /* 4C: */
+ unsigned char peer_id[0] PACKED; /* 4D: */
+} ppp_conn_info_t;
+
+/* Data structure for SET_TRIGGER_INTR command
+ */
+
+typedef struct ppp_intr_info{
+ unsigned char i_enable PACKED; /* 0 Interrupt enable bits */
+ unsigned char irq PACKED; /* 1 Irq number */
+ unsigned short timer_len PACKED; /* 2 Timer delay */
+} ppp_intr_info_t;
+
+
+#define FT1_MONITOR_STATUS_CTRL 0x80
+#define SET_FT1_MODE 0x81
+
+
+
+/* Special UDP drivers management commands */
+#define PPIPE_ENABLE_TRACING 0x20
+#define PPIPE_DISABLE_TRACING 0x21
+#define PPIPE_GET_TRACE_INFO 0x22
+#define PPIPE_GET_IBA_DATA 0x23
+#define PPIPE_KILL_BOARD 0x24
+#define PPIPE_FT1_READ_STATUS 0x25
+#define PPIPE_DRIVER_STAT_IFSEND 0x26
+#define PPIPE_DRIVER_STAT_INTR 0x27
+#define PPIPE_DRIVER_STAT_GEN 0x28
+#define PPIPE_FLUSH_DRIVER_STATS 0x29
+#define PPIPE_ROUTER_UP_TIME 0x30
+
+#define DISABLE_TRACING 0x00
+#define TRACE_SIGNALLING_FRAMES 0x01
+#define TRACE_DATA_FRAMES 0x02
+
+
+
+#ifdef _MSC_
+# pragma pack()
+#endif
+#endif /* _SDLA_PPP_H */
diff --git a/pfinet/linux-src/include/linux/sdla_x25.h b/pfinet/linux-src/include/linux/sdla_x25.h
new file mode 100644
index 00000000..58214a08
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sdla_x25.h
@@ -0,0 +1,625 @@
+/*****************************************************************************
+* sdla_x25.h Sangoma X.25 firmware API definitions.
+*
+* Author: Gene Kozin <74604.152@compuserve.com>
+*
+* Copyright: (c) 1995-1996 Sangoma Technologies Inc.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version
+* 2 of the License, or (at your option) any later version.
+* ============================================================================
+* Dec 13, 1996 Gene Kozin Initial version
+*****************************************************************************/
+#ifndef _SDLA_X25_H
+#define _SDLA_X25_H
+
+/*----------------------------------------------------------------------------
+ * Notes:
+ * ------
+ * 1. All structures defined in this file are byte-aligned. To ensure
+ * portability of this code between different platforms and compilers, one
+ * of the following defines must be defined before including this file:
+ *
+ * Compiler Platform Define Use option
+ * -------- -------- ------ ----------
+ * GNU C Linux _GNUC_ -
+ * Microsoft C DOS/Windows _MSC_ -
+ *
+ */
+
+#ifdef _GNUC_
+# ifndef PACKED
+# define PACKED __attribute__((packed))
+# endif /* PACKED */
+#else
+# define PACKED
+#endif
+#ifdef _MSC_
+# pragma pack(1)
+#endif
+
+/****** CONSTANTS DEFINITIONS ***********************************************/
+
+#define X25_MAX_CHAN 255 /* max number of open X.25 circuits */
+#define X25_MAX_DATA 1024 /* max length of X.25 data buffer */
+
+/*
+ * X.25 shared memory layout.
+ */
+#define X25_MBOX_OFFS 0x16B0 /* general mailbox block */
+#define X25_RXMBOX_OFFS 0x1AD0 /* receive mailbox */
+#define X25_STATUS_OFFS 0x1EF0 /* X.25 status structure */
+
+/****** DATA STRUCTURES *****************************************************/
+
+/*----------------------------------------------------------------------------
+ * X.25 Command Block.
+ */
+typedef struct X25Cmd
+{
+ unsigned char command PACKED; /* command code */
+ unsigned short length PACKED; /* transfer data length */
+ unsigned char result PACKED; /* return code */
+ unsigned char pf PACKED; /* P/F bit */
+ unsigned short lcn PACKED; /* logical channel */
+ unsigned char qdm PACKED; /* Q/D/M bits */
+ unsigned char cause PACKED; /* cause field */
+ unsigned char diagn PACKED; /* diagnostics */
+ unsigned char pktType PACKED; /* packet type */
+ unsigned char resrv[4] PACKED; /* reserved */
+} TX25Cmd;
+
+/*
+ * Defines for the 'command' field.
+ */
+/*----- General commands --------------*/
+#define X25_SET_GLOBAL_VARS 0x0B /* set global variables */
+#define X25_READ_MODEM_STATUS 0x0C /* read modem status */
+#define X25_READ_CODE_VERSION 0x15 /* read firmware version number */
+#define X25_TRACE_CONFIGURE 0x14 /* configure trace facility */
+#define X25_READ_TRACE_DATA 0x16 /* read trace data */
+#define X25_SET_INTERRUPT_MODE 0x17 /* set interrupt generation mode */
+#define X25_READ_INTERRUPT_MODE 0x18 /* read interrupt generation mode */
+/*----- HDLC-level commands -----------*/
+#define X25_HDLC_LINK_CONFIGURE 0x01 /* configure HDLC link level */
+#define X25_HDLC_LINK_OPEN 0x02 /* open HDLC link */
+#define X25_HDLC_LINK_CLOSE 0x03 /* close HDLC link */
+#define X25_HDLC_LINK_SETUP 0x04 /* set up HDLC link */
+#define X25_HDLC_LINK_DISC 0x05 /* disconnect DHLC link */
+#define X25_HDLC_LINK_STATUS 0x06 /* read DHLC link status */
+#define X25_HDLC_READ_STATS 0x07 /* read operational statistics */
+#define X25_HDLC_FLUSH_STATS 0x08 /* flush operational statistics */
+#define X25_HDLC_READ_COMM_ERR 0x09 /* read error statistics */
+#define X25_HDLC_FLUSH_COMM_ERR 0x0A /* flush error statistics */
+#define X25_HDLC_FLUSH_BUFFERS 0x0D /* flush HDLC-level data buffers */
+#define X25_HDLC_SPRVS_CNT_STAT 0x0F /* read surervisory count status */
+#define X25_HDLC_SEND_UI_FRAME 0x10 /* send unnumbered information frame */
+#define X25_HDLC_WRITE 0x11 /* send HDLC information frame */
+#define X25_HDLC_READ 0x21 /* read HDLC information frame */
+#define X25_HDLC_READ_CONFIG 0x12 /* read HDLC configuration */
+#define X25_HDLC_SET_CONFIG 0x13 /* set HDLC configuration */
+/*----- X.25-level commands -----------*/
+#define X25_READ 0x22 /* read X.25 packet */
+#define X25_WRITE 0x23 /* send X.25 packet */
+#define X25_PLACE_CALL 0x30 /* place a call on SVC */
+#define X25_ACCEPT_CALL 0x31 /* accept incoming call */
+#define X25_CLEAR_CALL 0x32 /* clear call */
+#define X25_CLEAR_CONFRM 0x33 /* send clear confirmation packet */
+#define X25_RESET 0x34 /* send reset request packet */
+#define X25_RESET_CONFRM 0x35 /* send reset confirmation packet */
+#define X25_RESTART 0x36 /* send restart request packet */
+#define X25_RESTART_CONFRM 0x37 /* send restart confirmation packet */
+#define X25_INTERRUPT 0x38 /* send interrupt request packet */
+#define X25_INTERRUPT_CONFRM 0x39 /* send interrupt confirmation pkt */
+#define X25_REGISTRATION_RQST 0x3A /* send registration request packet */
+#define X25_REGISTRATION_CONFRM 0x3B /* send registration confirmation */
+#define X25_IS_DATA_AVAILABLE 0x40 /* querry receive queue */
+#define X25_INCOMING_CALL_CTL 0x41 /* select incoming call options */
+#define X25_CONFIGURE_PVC 0x42 /* configure PVC */
+#define X25_GET_ACTIVE_CHANNELS 0x43 /* get a list of active circuits */
+#define X25_READ_CHANNEL_CONFIG 0x44 /* read virt. circuit configuration */
+#define X25_FLUSH_DATA_BUFFERS 0x45 /* flush X.25-level data buffers */
+#define X25_READ_HISTORY_TABLE 0x46 /* read asynchronous event log */
+#define X25_HISTORY_TABLE_CTL 0x47 /* control asynchronous event log */
+#define X25_GET_TX_D_BIT_STATUS 0x48 /* is packet with D-bit acknowledged */
+#define X25_READ_STATISTICS 0x49 /* read X.25-level statistics */
+#define X25_FLUSH_STATISTICS 0x4A /* flush X.25-level statistics */
+#define X25_READ_CONFIGURATION 0x50 /* read HDLC & X.25 configuration */
+#define X25_SET_CONFIGURATION 0x51 /* set HDLC & X.25 configuration */
+
+/*
+ * Defines for the 'result' field.
+ */
+/*----- General results ---------------*/
+#define X25RES_OK 0x00
+#define X25RES_ERROR 0x01
+#define X25RES_LINK_NOT_IN_ABM 0x02 /* link is not in ABM mode */
+#define X25RES_LINK_CLOSED 0x03
+#define X25RES_INVAL_LENGTH 0x04
+#define X25RES_INVAL_CMD 0x05
+#define X25RES_UNNUMBERED_FRAME 0x06 /* unnumbered frame received */
+#define X25RES_FRM_REJECT_MODE 0x07 /* link is in Frame Reject mode */
+#define X25RES_MODEM_FAILURE 0x08 /* DCD and/or CTS dropped */
+#define X25RES_N2_RETRY_LIMIT 0x09 /* N2 retry limit has been exceeded */
+#define X25RES_INVAL_LCN 0x30 /* invalid logical channel number */
+#define X25RES_INVAL_STATE 0x31 /* channel is not in data xfer mode */
+#define X25RES_INVAL_DATA_LEN 0x32 /* invalid data length */
+#define X25RES_NOT_READY 0x33 /* no data available / buffers full */
+#define X25RES_NETWORK_DOWN 0x34
+#define X25RES_CHANNEL_IN_USE 0x35 /* there is data queued on this LCN */
+#define X25RES_REGST_NOT_SUPPRT 0x36 /* registration not supported */
+#define X25RES_INVAL_FORMAT 0x37 /* invalid packet format */
+#define X25RES_D_BIT_NOT_SUPPRT 0x38 /* D-bit pragmatics not supported */
+#define X25RES_FACIL_NOT_SUPPRT 0x39 /* Call facility not supported */
+#define X25RES_INVAL_CALL_ARG 0x3A /* erroneous call arguments */
+#define X25RES_INVAL_CALL_DATA 0x3B /* erroneous call user data */
+#define X25RES_ASYNC_PACKET 0x40 /* asynchronous packet received */
+#define X25RES_PROTO_VIOLATION 0x41 /* protocol violation occurred */
+#define X25RES_PKT_TIMEOUT 0x42 /* X.25 packet time out */
+#define X25RES_PKT_RETRY_LIMIT 0x43 /* X.25 packet retry limit exceeded */
+/*----- Command-dependent results -----*/
+#define X25RES_LINK_DISC 0x00 /* HDLC_LINK_STATUS */
+#define X25RES_LINK_IN_ABM 0x01 /* HDLC_LINK_STATUS */
+#define X25RES_NO_DATA 0x01 /* HDLC_READ/READ_TRACE_DATA*/
+#define X25RES_TRACE_INACTIVE 0x02 /* READ_TRACE_DATA */
+#define X25RES_LINK_IS_OPEN 0x01 /* HDLC_LINK_OPEN */
+#define X25RES_LINK_IS_DISC 0x02 /* HDLC_LINK_DISC */
+#define X25RES_LINK_IS_CLOSED 0x03 /* HDLC_LINK_CLOSE */
+#define X25RES_INVAL_PARAM 0x31 /* INCOMING_CALL_CTL */
+#define X25RES_INVAL_CONFIG 0x35 /* REGISTR_RQST/CONFRM */
+
+/*
+ * Defines for the 'qdm_bits' field.
+ */
+#define X25CMD_Q_BIT_MASK 0x04
+#define X25CMD_D_BIT_MASK 0x02
+#define X25CMD_M_BIT_MASK 0x01
+
+/*
+ * Defines for the 'pkt_type' field.
+ */
+/*----- Asynchronous events ------*/
+#define ASE_CLEAR_RQST 0x02
+#define ASE_RESET_RQST 0x04
+#define ASE_RESTART_RQST 0x08
+#define ASE_INTERRUPT 0x10
+#define ASE_DTE_REGISTR_RQST 0x20
+#define ASE_CALL_RQST 0x30
+#define ASE_CALL_ACCEPTED 0x31
+#define ASE_CLEAR_CONFRM 0x32
+#define ASE_RESET_CONFRM 0x33
+#define ASE_RESTART_CONFRM 0x34
+#define ASE_INTERRUPT_CONFRM 0x35
+#define ASE_DCE_REGISTR_CONFRM 0x36
+#define ASE_DIAGNOSTIC 0x37
+#define ASE_CALL_AUTO_CLEAR 0x38
+#define AUTO_RESPONSE_FLAG 0x80
+/*----- Time-Out events ----------*/
+#define TOE_RESTART_RQST 0x03
+#define TOE_CALL_RQST 0x05
+#define TOE_CLEAR_RQST 0x08
+#define TOE_RESET_RQST 0x0A
+/*----- Protocol Violation events */
+#define PVE_CLEAR_RQST 0x32
+#define PVE_RESET_RQST 0x33
+#define PVE_RESTART_RQST 0x34
+#define PVE_DIAGNOSTIC 0x37
+
+/*----------------------------------------------------------------------------
+ * X.25 Mailbox.
+ * This structure is located at offsets X25_MBOX_OFFS and X25_RXMBOX_OFFS
+ * into shared memory window.
+ */
+typedef struct X25Mbox
+{
+ unsigned char opflag PACKED; /* 00h: execution flag */
+ TX25Cmd cmd PACKED; /* 01h: command block */
+ unsigned char data[1] PACKED; /* 10h: data buffer */
+} TX25Mbox;
+
+/*----------------------------------------------------------------------------
+ * X.25 Time Stamp Structure.
+ */
+typedef struct X25TimeStamp
+{
+ unsigned char month PACKED;
+ unsigned char date PACKED;
+ unsigned char sec PACKED;
+ unsigned char min PACKED;
+ unsigned char hour PACKED;
+} TX25TimeStamp;
+
+/*----------------------------------------------------------------------------
+ * X.25 Status Block.
+ * This structure is located at offset X25_STATUS_OFF into shared memory
+ * window.
+ */
+typedef struct X25Status
+{
+ unsigned short pvc_map PACKED; /* 00h: PVC map */
+ unsigned short icc_map PACKED; /* 02h: Incoming Chan. map */
+ unsigned short twc_map PACKED; /* 04h: Two-way Cnan. map */
+ unsigned short ogc_map PACKED; /* 06h: Outgoing Chan. map */
+ TX25TimeStamp tstamp PACKED; /* 08h: timestamp (BCD) */
+ unsigned char iflags PACKED; /* 0Dh: interrupt flags */
+ unsigned char imask PACKED; /* 0Eh: interrupt mask */
+ unsigned char resrv PACKED; /* 0Eh: */
+ unsigned char gflags PACKED; /* 10h: misc. HDLC/X25 flags */
+ unsigned char cflags[X25_MAX_CHAN] PACKED; /* channel status bytes */
+} TX25Status;
+
+/*
+ * Bitmasks for the 'iflags' field.
+ */
+#define X25_RX_INTR 0x01 /* receive interrupt */
+#define X25_TX_INTR 0x02 /* transmit interrupt */
+#define X25_MODEM_INTR 0x04 /* modem status interrupt (CTS/DCD) */
+#define X25_EVENT_INTR 0x10 /* asynchronous event encountered */
+#define X25_CMD_INTR 0x08 /* interface command complete */
+
+/*
+ * Bitmasks for the 'gflags' field.
+ */
+#define X25_HDLC_ABM 0x01 /* HDLC is in ABM mode */
+#define X25_RX_READY 0x02 /* X.25 data available */
+#define X25_TRACE_READY 0x08 /* trace data available */
+#define X25_EVENT_IND 0x20 /* asynchronous event indicator */
+#define X25_TX_READY 0x40 /* space is available in Tx buf.*/
+
+/*
+ * Bitmasks for the 'cflags' field.
+ */
+#define X25_XFER_MODE 0x80 /* channel is in data transfer mode */
+#define X25_TXWIN_OPEN 0x40 /* transmit window open */
+#define X25_RXBUF_MASK 0x3F /* number of data buffers available */
+
+/*****************************************************************************
+ * Following definitions structurize contents of the TX25Mbox.data field for
+ * different X.25 interface commands.
+ ****************************************************************************/
+
+/* ---------------------------------------------------------------------------
+ * X25_SET_GLOBAL_VARS Command.
+ */
+typedef struct X25GlobalVars
+{
+ unsigned char resrv PACKED; /* 00h: reserved */
+ unsigned char dtrCtl PACKED; /* 01h: DTR control code */
+ unsigned char resErr PACKED; /* 01h: '1' - reset modem error */
+} TX25GlobalVars;
+
+/*
+ * Defines for the 'dtrCtl' field.
+ */
+#define X25_RAISE_DTR 0x01
+#define X25_DROP_DTR 0x02
+
+/* ---------------------------------------------------------------------------
+ * X25_READ_MODEM_STATUS Command.
+ */
+typedef struct X25ModemStatus
+{
+ unsigned char status PACKED; /* 00h: modem status */
+} TX25ModemStatus;
+
+/*
+ * Defines for the 'status' field.
+ */
+#define X25_CTS_MASK 0x20
+#define X25_DCD_MASK 0x08
+
+/* ---------------------------------------------------------------------------
+ * X25_HDLC_LINK_STATUS Command.
+ */
+typedef struct X25LinkStatus
+{
+ unsigned char txQueued PACKED; /* 00h: queued Tx I-frames*/
+ unsigned char rxQueued PACKED; /* 01h: queued Rx I-frames*/
+ unsigned char station PACKED; /* 02h: DTE/DCE config. */
+ unsigned char reserved PACKED; /* 03h: reserved */
+ unsigned char sfTally PACKED; /* 04h: supervisory frame tally */
+} TX25LinkStatus;
+
+/*
+ * Defines for the 'station' field.
+ */
+#define X25_STATION_DTE 0x01 /* station configured as DTE */
+#define X25_STATION_DCE 0x02 /* station configured as DCE */
+
+/* ---------------------------------------------------------------------------
+ * X25_HDLC_READ_STATS Command.
+ */
+typedef struct HdlcStats
+{ /* a number of ... */
+ unsigned short rxIFrames PACKED; /* 00h: ready Rx I-frames */
+ unsigned short rxNoseq PACKED; /* 02h: frms out-of-sequence */
+ unsigned short rxNodata PACKED; /* 04h: I-frms without data */
+ unsigned short rxDiscarded PACKED; /* 06h: discarded frames */
+ unsigned short rxTooLong PACKED; /* 08h: frames too long */
+ unsigned short rxBadAddr PACKED; /* 0Ah: frms with inval.addr*/
+ unsigned short txAcked PACKED; /* 0Ch: acknowledged I-frms */
+ unsigned short txRetransm PACKED; /* 0Eh: re-transmit. I-frms */
+ unsigned short t1Timeout PACKED; /* 10h: T1 timeouts */
+ unsigned short rxSABM PACKED; /* 12h: received SABM frames */
+ unsigned short rxDISC PACKED; /* 14h: received DISC frames */
+ unsigned short rxDM PACKED; /* 16h: received DM frames */
+ unsigned short rxFRMR PACKED; /* 18h: FRMR frames received */
+ unsigned short txSABM PACKED; /* 1Ah: transm. SABM frames*/
+ unsigned short txDISC PACKED; /* 1Ch: transm. DISC frames*/
+ unsigned short txDM PACKED; /* 1Eh: transm. DM frames */
+ unsigned short txFRMR PACKED; /* 20h: transm. FRMR frames*/
+} THdlcStats;
+
+/* ---------------------------------------------------------------------------
+ * X25_HDLC_READ_COMM_ERR Command.
+ */
+typedef struct HdlcCommErr
+{ /* a number of ... */
+ unsigned char rxOverrun PACKED; /* 00h: Rx overrun errors */
+ unsigned char rxBadCrc PACKED; /* 01h: Rx CRC errors */
+ unsigned char rxAborted PACKED; /* 02h: Rx aborted frames */
+ unsigned char rxDropped PACKED; /* 03h: frames lost */
+ unsigned char txAborted PACKED; /* 04h: Tx aborted frames */
+ unsigned char txUnderrun PACKED; /* 05h: Tx underrun errors */
+ unsigned char txMissIntr PACKED; /* 06h: missed underrun ints */
+ unsigned char reserved PACKED; /* 07h: reserved */
+ unsigned char droppedDCD PACKED; /* 08h: times DCD dropped */
+ unsigned char droppedCTS PACKED; /* 09h: times CTS dropped */
+} THdlcCommErr;
+
+/* ---------------------------------------------------------------------------
+ * X25_SET_CONFIGURATION & X25_READ_CONFIGURATION Commands.
+ */
+typedef struct X25Config
+{
+ unsigned char baudRate PACKED; /* 00h: */
+ unsigned char t1 PACKED; /* 01h: */
+ unsigned char t2 PACKED; /* 02h: */
+ unsigned char n2 PACKED; /* 03h: */
+ unsigned short hdlcMTU PACKED; /* 04h: */
+ unsigned char hdlcWindow PACKED; /* 06h: */
+ unsigned char t4 PACKED; /* 07h: */
+ unsigned char autoModem PACKED; /* 08h: */
+ unsigned char autoHdlc PACKED; /* 09h: */
+ unsigned char hdlcOptions PACKED; /* 0Ah: */
+ unsigned char station PACKED; /* 0Bh: */
+ unsigned char pktWindow PACKED; /* 0Ch: */
+ unsigned short defPktSize PACKED; /* 0Dh: */
+ unsigned short pktMTU PACKED; /* 0Fh: */
+ unsigned short loPVC PACKED; /* 11h: */
+ unsigned short hiPVC PACKED; /* 13h: */
+ unsigned short loIncomingSVC PACKED; /* 15h: */
+ unsigned short hiIncomingSVC PACKED; /* 17h: */
+ unsigned short loTwoWaySVC PACKED; /* 19h: */
+ unsigned short hiTwoWaySVC PACKED; /* 1Bh: */
+ unsigned short loOutgoingSVC PACKED; /* 1Dh: */
+ unsigned short hiOutgoingSVC PACKED; /* 1Fh: */
+ unsigned short options PACKED; /* 21h: */
+ unsigned char responseOpt PACKED; /* 23h: */
+ unsigned short facil1 PACKED; /* 24h: */
+ unsigned short facil2 PACKED; /* 26h: */
+ unsigned short ccittFacil PACKED; /* 28h: */
+ unsigned short otherFacil PACKED; /* 2Ah: */
+ unsigned short ccittCompat PACKED; /* 2Ch: */
+ unsigned char t10t20 PACKED; /* 2Eh: */
+ unsigned char t11t21 PACKED; /* 2Fh: */
+ unsigned char t12t22 PACKED; /* 30h: */
+ unsigned char t13t23 PACKED; /* 31h: */
+ unsigned char t16t26 PACKED; /* 32H: */
+ unsigned char t28 PACKED; /* 33h: */
+ unsigned char r10r20 PACKED; /* 34h: */
+ unsigned char r12r22 PACKED; /* 35h: */
+ unsigned char r13r23 PACKED; /* 36h: */
+} TX25Config;
+
+/* ---------------------------------------------------------------------------
+ * X25_READ_CHANNEL_CONFIG Command.
+ */
+typedef struct X25ChanAlloc /*----- Channel allocation -*/
+{
+ unsigned short loPVC PACKED; /* 00h: lowest PVC number */
+ unsigned short hiPVC PACKED; /* 02h: highest PVC number */
+ unsigned short loIncomingSVC PACKED; /* 04h: lowest incoming SVC */
+ unsigned short hiIncomingSVC PACKED; /* 06h: highest incoming SVC */
+ unsigned short loTwoWaySVC PACKED; /* 08h: lowest two-way SVC */
+ unsigned short hiTwoWaySVC PACKED; /* 0Ah: highest two-way SVC */
+ unsigned short loOutgoingSVC PACKED; /* 0Ch: lowest outgoing SVC */
+ unsigned short hiOutgoingSVC PACKED; /* 0Eh: highest outgoing SVC */
+} TX25ChanAlloc;
+
+typedef struct X25ChanCfg /*------ Channel configuration -----*/
+{
+ unsigned char type PACKED; /* 00h: channel type */
+ unsigned char txConf PACKED; /* 01h: Tx packet and window sizes */
+ unsigned char rxConf PACKED; /* 01h: Rx packet and window sizes */
+} TX25ChanCfg;
+
+/*
+ * Defines for the 'type' field.
+ */
+#define X25_PVC 0x01 /* PVC */
+#define X25_SVC_IN 0x03 /* Incoming SVC */
+#define X25_SVC_TWOWAY 0x07 /* Two-way SVC */
+#define X25_SVC_OUT 0x0B /* Outgoing SVC */
+
+/*----------------------------------------------------------------------------
+ * X25_READ_STATISTICS Command.
+ */
+typedef struct X25Stats
+{ /* number of packets Tx/Rx'ed */
+ unsigned short txRestartRqst PACKED; /* 00h: Restart Request */
+ unsigned short rxRestartRqst PACKED; /* 02h: Restart Request */
+ unsigned short txRestartConf PACKED; /* 04h: Restart Confirmation */
+ unsigned short rxRestartConf PACKED; /* 06h: Restart Confirmation */
+ unsigned short txResetRqst PACKED; /* 08h: Reset Request */
+ unsigned short rxResetRqst PACKED; /* 0Ah: Reset Request */
+ unsigned short txResetConf PACKED; /* 0Ch: Reset Confirmation */
+ unsigned short rxResetConf PACKED; /* 0Eh: Reset Confirmation */
+ unsigned short txCallRequest PACKED; /* 10h: Call Request */
+ unsigned short rxCallRequest PACKED; /* 12h: Call Request */
+ unsigned short txCallAccept PACKED; /* 14h: Call Accept */
+ unsigned short rxCallAccept PACKED; /* 16h: Call Accept */
+ unsigned short txClearRqst PACKED; /* 18h: Clear Request */
+ unsigned short rxClearRqst PACKED; /* 1Ah: Clear Request */
+ unsigned short txClearConf PACKED; /* 1Ch: Clear Confirmation */
+ unsigned short rxClearConf PACKED; /* 1Eh: Clear Confirmation */
+ unsigned short txDiagnostic PACKED; /* 20h: Diagnostic */
+ unsigned short rxDiagnostic PACKED; /* 22h: Diagnostic */
+ unsigned short txRegRqst PACKED; /* 24h: Registration Request */
+ unsigned short rxRegRqst PACKED; /* 26h: Registration Request */
+ unsigned short txRegConf PACKED; /* 28h: Registration Confirm.*/
+ unsigned short rxRegConf PACKED; /* 2Ah: Registration Confirm.*/
+ unsigned short txInterrupt PACKED; /* 2Ch: Interrupt */
+ unsigned short rxInterrupt PACKED; /* 2Eh: Interrupt */
+ unsigned short txIntrConf PACKED; /* 30h: Interrupt Confirm. */
+ unsigned short rxIntrConf PACKED; /* 32h: Interrupt Confirm. */
+ unsigned short txData PACKED; /* 34h: Data */
+ unsigned short rxData PACKED; /* 36h: Data */
+ unsigned short txRR PACKED; /* 38h: RR */
+ unsigned short rxRR PACKED; /* 3Ah: RR */
+ unsigned short txRNR PACKED; /* 3Ch: RNR */
+ unsigned short rxRNR PACKED; /* 3Eh: RNR */
+} TX25Stats;
+
+/*----------------------------------------------------------------------------
+ * X25_READ_HISTORY_TABLE Command.
+ */
+typedef struct X25EventLog
+{
+ unsigned char type PACKED; /* 00h: transaction type */
+ unsigned short lcn PACKED; /* 01h: logical channel num */
+ unsigned char packet PACKED; /* 03h: async packet type */
+ unsigned char cause PACKED; /* 04h: X.25 cause field */
+ unsigned char diag PACKED; /* 05h: X.25 diag field */
+ TX25TimeStamp ts PACKED; /* 06h: time stamp */
+} TX25EventLog;
+
+/*
+ * Defines for the 'type' field.
+ */
+#define X25LOG_INCOMING 0x00
+#define X25LOG_APPLICATION 0x01
+#define X25LOG_AUTOMATIC 0x02
+#define X25LOG_ERROR 0x04
+#define X25LOG_TIMEOUT 0x08
+#define X25LOG_RECOVERY 0x10
+
+/*
+ * Defines for the 'packet' field.
+ */
+#define X25LOG_CALL_RQST 0x0B
+#define X25LOG_CALL_ACCEPTED 0x0F
+#define X25LOG_CLEAR_RQST 0x13
+#define X25LOG_CLEAR_CONFRM 0x17
+#define X25LOG_RESET_RQST 0x1B
+#define X25LOG_RESET_CONFRM 0x1F
+#define X25LOG_RESTART_RQST 0xFB
+#define X25LOG_RESTART_COMFRM 0xFF
+#define X25LOG_DIAGNOSTIC 0xF1
+#define X25LOG_DTE_REG_RQST 0xF3
+#define X25LOG_DTE_REG_COMFRM 0xF7
+
+/* ---------------------------------------------------------------------------
+ * X25_TRACE_CONFIGURE Command.
+ */
+typedef struct X25TraceCfg
+{
+ unsigned char flags PACKED; /* 00h: trace configuration flags */
+ unsigned char timeout PACKED; /* 01h: timeout for trace delay mode*/
+} TX25TraceCfg;
+
+/*
+ * Defines for the 'flags' field.
+ */
+#define X25_TRC_ENABLE 0x01 /* bit0: '1' - trace enabled */
+#define X25_TRC_TIMESTAMP 0x02 /* bit1: '1' - time stamping enabled*/
+#define X25_TRC_DELAY 0x04 /* bit2: '1' - trace delay enabled */
+#define X25_TRC_DATA 0x08 /* bit3: '1' - trace data packets */
+#define X25_TRC_SUPERVISORY 0x10 /* bit4: '1' - trace suprvisory pkts*/
+#define X25_TRC_ASYNCHRONOUS 0x20 /* bit5: '1' - trace asynch. packets*/
+#define X25_TRC_HDLC 0x40 /* bit6: '1' - trace all packets */
+#define X25_TRC_READ 0x80 /* bit7: '1' - get current config. */
+
+/* ---------------------------------------------------------------------------
+ * X25_READ_TRACE_DATA Command.
+ */
+typedef struct X25Trace /*----- Trace data structure -------*/
+{
+ unsigned short length PACKED; /* 00h: trace data length */
+ unsigned char type PACKED; /* 02h: trace type */
+ unsigned char lost_cnt PACKED; /* 03h: N of traces lost */
+ TX25TimeStamp tstamp PACKED; /* 04h: mon/date/sec/min/hour */
+ unsigned short millisec PACKED; /* 09h: ms time stamp */
+ unsigned char data[0] PACKED; /* 0Bh: traced frame */
+} TX25Trace;
+
+/*
+ * Defines for the 'type' field.
+ */
+#define X25_TRC_TYPE_MASK 0x0F /* bits 0..3: trace type */
+#define X25_TRC_TYPE_RX_FRAME 0x00 /* received frame trace */
+#define X25_TRC_TYPE_TX_FRAME 0x01 /* transmitted frame */
+#define X25_TRC_TYPE_ERR_FRAME 0x02 /* error frame */
+
+#define X25_TRC_ERROR_MASK 0xF0 /* bits 4..7: error code */
+#define X25_TRCERR_RX_ABORT 0x10 /* receive abort error */
+#define X25_TRCERR_RX_BADCRC 0x20 /* receive CRC error */
+#define X25_TRCERR_RX_OVERRUN 0x30 /* receiver overrun error */
+#define X25_TRCERR_RX_TOO_LONG 0x40 /* excessive frame length error */
+#define X25_TRCERR_TX_ABORT 0x70 /* aborted frame transmission error */
+#define X25_TRCERR_TX_UNDERRUN 0x80 /* transmit underrun error */
+
+/*****************************************************************************
+ * Following definitions describe HDLC frame and X.25 packet formats.
+ ****************************************************************************/
+
+typedef struct HDLCFrame /*----- DHLC Frame Format ----------*/
+{
+ unsigned char addr PACKED; /* address field */
+ unsigned char cntl PACKED; /* control field */
+ unsigned char data[0] PACKED;
+} THDLCFrame;
+
+typedef struct X25Pkt /*----- X.25 Packet Format ----------*/
+{
+ unsigned char lcn_hi PACKED; /* 4 MSB of Logical Channel Number */
+ unsigned char lcn_lo PACKED; /* 8 LSB of Logical Channel Number */
+ unsigned char type PACKED;
+ unsigned char data[0] PACKED;
+} TX25Pkt;
+
+/*
+ * Defines for the 'lcn_hi' field.
+ */
+#define X25_Q_BIT_MASK 0x80 /* Data Qualifier Bit mask */
+#define X25_D_BIT_MASK 0x40 /* Delivery Confirmation Bit mask */
+#define X25_M_BITS_MASK 0x30 /* Modulo Bits mask */
+#define X25_LCN_MSB_MASK 0x0F /* LCN most significant bits mask */
+
+/*
+ * Defines for the 'type' field.
+ */
+#define X25PKT_DATA 0x01 /* Data packet mask */
+#define X25PKT_SUPERVISORY 0x02 /* Supervisory packet mask */
+#define X25PKT_CALL_RQST 0x0B /* Call Request/Incoming */
+#define X25PKT_CALL_ACCEPTED 0x0F /* Call Accepted/Connected */
+#define X25PKT_CLEAR_RQST 0x13 /* Clear Request/Indication */
+#define X25PKT_CLEAR_CONFRM 0x17 /* Clear Confirmation */
+#define X25PKT_RESET_RQST 0x1B /* Reset Request/Indication */
+#define X25PKT_RESET_CONFRM 0x1F /* Reset Confirmation */
+#define X25PKT_RESTART_RQST 0xFB /* Restart Request/Indication */
+#define X25PKT_RESTART_CONFRM 0xFF /* Restart Confirmation */
+#define X25PKT_INTERRUPT 0x23 /* Interrupt */
+#define X25PKT_INTERRUPT_CONFRM 0x27 /* Interrupt Confirmation */
+#define X25PKT_DIAGNOSTIC 0xF1 /* Diagnostic */
+#define X25PKT_REGISTR_RQST 0xF3 /* Registration Request */
+#define X25PKT_REGISTR_CONFRM 0xF7 /* Registration Confirmation */
+#define X25PKT_RR_MASKED 0x01 /* Receive Ready packet after masking */
+#define X25PKT_RNR_MASKED 0x05 /* Receive Not Ready after masking */
+
+#ifdef _MSC_
+# pragma pack()
+#endif
+#endif /* _SDLA_X25_H */
diff --git a/pfinet/linux-src/include/linux/sdladrv.h b/pfinet/linux-src/include/linux/sdladrv.h
new file mode 100644
index 00000000..724fd6c7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sdladrv.h
@@ -0,0 +1,77 @@
+/*****************************************************************************
+* sdladrv.h SDLA Support Module. Kernel API Definitions.
+*
+* Author: Gideon Hack
+*
+* Copyright: (c) 1995-1999 Sangoma Technologies Inc.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version
+* 2 of the License, or (at your option) any later version.
+* ============================================================================
+* Jun 02, 1999 Gideon Hack Added support for the S514 PCI adapter.
+* Dec 11, 1996 Gene Kozin Complete overhaul.
+* Oct 17, 1996 Gene Kozin Minor bug fixes.
+* Jun 12, 1996 Gene Kozin Added support for S503 card.
+* Dec 06, 1995 Gene Kozin Initial version.
+*****************************************************************************/
+#ifndef _SDLADRV_H
+#define _SDLADRV_H
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= 0x020100
+#define LINUX_2_1
+#endif
+
+#define SDLA_MAXIORANGE 4 /* maximum I/O port range */
+#define SDLA_WINDOWSIZE 0x2000 /* default dual-port memory window size */
+
+/****** Data Structures *****************************************************/
+
+/*----------------------------------------------------------------------------
+ * Adapter hardware configuration. Pointer to this structure is passed to all
+ * APIs.
+ */
+typedef struct sdlahw
+{
+ unsigned type; /* adapter type */
+ unsigned fwid; /* firmware ID */
+ unsigned port; /* adapter I/O port base */
+ int irq; /* interrupt request level */
+ char S514_cpu_no[1]; /* PCI CPU Number */
+ unsigned char S514_slot_no; /* PCI Slot Number */
+#ifdef LINUX_2_1
+ struct pci_dev *pci_dev; /* PCI device */
+#else
+ unsigned char pci_bus; /* PCI bus number */
+ unsigned char pci_dev_func; /* PCI device/function number */
+#endif
+ void * dpmbase; /* dual-port memory base */
+ unsigned dpmsize; /* dual-port memory size */
+ unsigned pclk; /* CPU clock rate, kHz */
+ unsigned long memory; /* memory size */
+ unsigned long vector; /* local offset of the DPM window */
+ unsigned io_range; /* I/O port range */
+ unsigned char regs[SDLA_MAXIORANGE]; /* was written to registers */
+ unsigned reserved[5];
+} sdlahw_t;
+
+/****** Function Prototypes *************************************************/
+
+extern int sdla_setup (sdlahw_t* hw, void* sfm, unsigned len);
+extern int sdla_down (sdlahw_t* hw);
+extern int sdla_inten (sdlahw_t* hw);
+extern int sdla_intde (sdlahw_t* hw);
+extern int sdla_intack (sdlahw_t* hw);
+extern void S514_intack (sdlahw_t* hw, u32 int_status);
+extern void read_S514_int_stat (sdlahw_t* hw, u32* int_status);
+extern int sdla_intr (sdlahw_t* hw);
+extern int sdla_mapmem (sdlahw_t* hw, unsigned long addr);
+extern int sdla_peek (sdlahw_t* hw, unsigned long addr, void* buf,
+ unsigned len);
+extern int sdla_poke (sdlahw_t* hw, unsigned long addr, void* buf,
+ unsigned len);
+extern int sdla_exec (void* opflag);
+
+#endif /* _SDLADRV_H */
diff --git a/pfinet/linux-src/include/linux/sdlapci.h b/pfinet/linux-src/include/linux/sdlapci.h
new file mode 100644
index 00000000..e65783da
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sdlapci.h
@@ -0,0 +1,67 @@
+/*****************************************************************************
+* sdlapci.h WANPIPE(tm) Multiprotocol WAN Link Driver.
+* Definitions for the SDLA PCI adapter.
+*
+* Author: Gideon Hack <ghack@sangoma.com>
+*
+* Copyright: (c) 1999 Sangoma Technologies Inc.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version
+* 2 of the License, or (at your option) any later version.
+* ============================================================================
+* Jun 02, 1999 Gideon Hack Initial version.
+*****************************************************************************/
+#ifndef _SDLAPCI_H
+#define _SDLAPCI_H
+
+/****** Defines *************************************************************/
+
+/* Definitions for identifying and finding S514 PCI adapters */
+#define V3_VENDOR_ID 0x11B0 /* V3 vendor ID number */
+#define V3_DEVICE_ID 0x0002 /* V3 device ID number */
+#define SANGOMA_SUBSYS_VENDOR 0x4753 /* ID for Sangoma */
+#define PCI_DEV_SLOT_MASK 0x1F /* mask for slot numbering */
+#define PCI_IRQ_NOT_ALLOCATED 0xFF /* interrupt line for no IRQ */
+
+/* Local PCI register offsets */
+#define PCI_VENDOR_ID_WORD 0x00 /* vendor ID */
+#define PCI_IO_BASE_DWORD 0x10 /* IO base */
+#define PCI_MEM_BASE0_DWORD 0x14 /* memory base - apperture 0 */
+#define PCI_MEM_BASE1_DWORD 0x18 /* memory base - apperture 1 */
+#define PCI_SUBSYS_VENDOR_WORD 0x2C /* subsystem vendor ID */
+#define PCI_INT_LINE_BYTE 0x3C /* interrupt line */
+#define PCI_INT_PIN_BYTE 0x3D /* interrupt pin */
+#define PCI_MAP0_DWORD 0x40 /* PCI to local bus address 0 */
+#define PCI_MAP1_DWORD 0x44 /* PCI to local bus address 1 */
+#define PCI_INT_STATUS 0x48 /* interrupt status */
+#define PCI_INT_CONFIG 0x4C /* interrupt configuration */
+
+/* Local PCI register usage */
+#define PCI_MEMORY_ENABLE 0x00000003 /* enable PCI memory */
+#define PCI_CPU_A_MEM_DISABLE 0x00000002 /* disable CPU A memory */
+#define PCI_CPU_B_MEM_DISABLE 0x00100002 /* disable CPU B memory */
+#define PCI_ENABLE_IRQ_CPU_A 0x005A0004 /* enable IRQ for CPU A */
+#define PCI_ENABLE_IRQ_CPU_B 0x005A0008 /* enable IRQ for CPU B */
+#define PCI_DISABLE_IRQ_CPU_A 0x00000004 /* disable IRQ for CPU A */
+#define PCI_DISABLE_IRQ_CPU_B 0x00000008 /* disable IRQ for CPU B */
+
+/* Setting for the Interrupt Status register */
+#define IRQ_CPU_A 0x04 /* IRQ for CPU A */
+#define IRQ_CPU_B 0x08 /* IRQ for CPU B */
+
+/* The maximum size of the S514 memory */
+#define MAX_SIZEOF_S514_MEMORY (256 * 1024)
+
+/* S514 control register offsets within the memory address space */
+#define S514_CTRL_REG_BYTE 0x80000
+
+/* S514 adapter control bytes */
+#define S514_CPU_HALT 0x00
+#define S514_CPU_START 0x01
+
+/* The maximum number of S514 adapters supported */
+#define MAX_S514_CARDS 8
+
+#endif /* _SDLAPCI_H */
diff --git a/pfinet/linux-src/include/linux/sdlasfm.h b/pfinet/linux-src/include/linux/sdlasfm.h
new file mode 100644
index 00000000..c82d31a5
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sdlasfm.h
@@ -0,0 +1,103 @@
+/*****************************************************************************
+* sdlasfm.h WANPIPE(tm) Multiprotocol WAN Link Driver.
+* Definitions for the SDLA Firmware Module (SFM).
+*
+* Author: Gideon Hack
+*
+* Copyright: (c) 1995-1999 Sangoma Technologies Inc.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version
+* 2 of the License, or (at your option) any later version.
+* ============================================================================
+* Jun 02, 1999 Gideon Hack Added support for the S514 adapter.
+* Dec 11, 1996 Gene Kozin Cosmetic changes
+* Apr 16, 1996 Gene Kozin Changed adapter & firmware IDs. Version 2
+* Dec 15, 1995 Gene Kozin Structures chaned
+* Nov 09, 1995 Gene Kozin Initial version.
+*****************************************************************************/
+#ifndef _SDLASFM_H
+#define _SDLASFM_H
+
+/****** Defines *************************************************************/
+
+#define SFM_VERSION 2
+#define SFM_SIGNATURE "SFM - Sangoma SDLA Firmware Module"
+
+/* min/max */
+#define SFM_IMAGE_SIZE 0x8000 /* max size of SDLA code image file */
+#define SFM_DESCR_LEN 256 /* max length of description string */
+#define SFM_MAX_SDLA 16 /* max number of compatible adapters */
+
+/* Adapter types */
+#define SDLA_S502A 5020
+#define SDLA_S502E 5021
+#define SDLA_S503 5030
+#define SDLA_S508 5080
+#define SDLA_S507 5070
+#define SDLA_S509 5090
+#define SDLA_S514 5140
+
+/* S514 PCI adapter CPU numbers */
+#define S514_CPU_A 'A'
+#define S514_CPU_B 'B'
+
+
+/* Firmware identification numbers:
+ * 0 .. 999 Test & Diagnostics
+ * 1000 .. 1999 Streaming HDLC
+ * 2000 .. 2999 Bisync
+ * 3000 .. 3999 SDLC
+ * 4000 .. 4999 HDLC
+ * 5000 .. 5999 X.25
+ * 6000 .. 6999 Frame Relay
+ * 7000 .. 7999 PPP
+ * 8000 .. 8999 Cisco HDLC
+ */
+#define SFID_CALIB502 200
+#define SFID_STRM502 1200
+#define SFID_STRM508 1800
+#define SFID_BSC502 2200
+#define SFID_SDLC502 3200
+#define SFID_HDLC502 4200
+#define SFID_HDLC508 4800
+#define SFID_X25_502 5200
+#define SFID_X25_508 5800
+#define SFID_FR502 6200
+#define SFID_FR508 6800
+#define SFID_PPP502 7200
+#define SFID_PPP508 7800
+#define SFID_PPP514 7140
+#define SFID_CHDLC508 8800
+#define SFID_CHDLC514 8140
+
+/****** Data Types **********************************************************/
+
+typedef struct sfm_info /* firmware module information */
+{
+ unsigned short codeid; /* firmware ID */
+ unsigned short version; /* firmaware version number */
+ unsigned short adapter[SFM_MAX_SDLA]; /* compatible adapter types */
+ unsigned long memsize; /* minimum memory size */
+ unsigned short reserved[2]; /* reserved */
+ unsigned short startoffs; /* entry point offset */
+ unsigned short winoffs; /* dual-port memory window offset */
+ unsigned short codeoffs; /* code load offset */
+ unsigned short codesize; /* code size */
+ unsigned short dataoffs; /* configuration data load offset */
+ unsigned short datasize; /* configuration data size */
+} sfm_info_t;
+
+typedef struct sfm /* SDLA firmware file structire */
+{
+ char signature[80]; /* SFM file signature */
+ unsigned short version; /* file format version */
+ unsigned short checksum; /* info + image */
+ unsigned short reserved[6]; /* reserved */
+ char descr[SFM_DESCR_LEN]; /* description string */
+ sfm_info_t info; /* firmware module info */
+ unsigned char image[1]; /* code image (variable size) */
+} sfm_t;
+
+#endif /* _SDLASFM_H */
diff --git a/pfinet/linux-src/include/linux/securebits.h b/pfinet/linux-src/include/linux/securebits.h
new file mode 100644
index 00000000..1e10badc
--- /dev/null
+++ b/pfinet/linux-src/include/linux/securebits.h
@@ -0,0 +1,30 @@
+#ifndef _LINUX_SECUREBITS_H
+#define _LINUX_SECUREBITS_H 1
+
+#define SECUREBITS_DEFAULT 0x00000000
+
+extern unsigned securebits;
+
+/* When set UID 0 has no special privileges. When unset, we support
+ inheritance of root-permissions and suid-root executablew under
+ compatibility mode. We raise the effective and inheritable bitmasks
+ *of the executable file* if the effective uid of the new process is
+ 0. If the real uid is 0, we raise the inheritable bitmask of the
+ executable file. */
+#define SECURE_NOROOT 0
+
+/* When set, setuid to/from uid 0 does not trigger capability-"fixes"
+ to be compatible with old programs relying on set*uid to loose
+ privileges. When unset, setuid doesn't change privileges. */
+#define SECURE_NO_SETUID_FIXUP 2
+
+/* Each securesetting is implemented using two bits. One bit specify
+ whether the setting is on or off. The other bit specify whether the
+ setting is fixed or not. A setting which is fixed cannot be changed
+ from user-level. */
+
+#define issecure(X) ( (1 << (X+1)) & SECUREBITS_DEFAULT ? \
+ (1 << (X)) & SECUREBITS_DEFAULT : \
+ (1 << (X)) & securebits )
+
+#endif /* !_LINUX_SECUREBITS_H */
diff --git a/pfinet/linux-src/include/linux/selection.h b/pfinet/linux-src/include/linux/selection.h
new file mode 100644
index 00000000..74d1b24d
--- /dev/null
+++ b/pfinet/linux-src/include/linux/selection.h
@@ -0,0 +1,44 @@
+/*
+ * selection.h
+ *
+ * Interface between console.c, tty_io.c, vt.c, vc_screen.c and selection.c
+ */
+
+#ifndef _LINUX_SELECTION_H_
+#define _LINUX_SELECTION_H_
+
+#include <linux/vt_buffer.h>
+
+extern int sel_cons;
+
+extern void clear_selection(void);
+extern int set_selection(const unsigned long arg, struct tty_struct *tty, int user);
+extern int paste_selection(struct tty_struct *tty);
+extern int sel_loadlut(const unsigned long arg);
+extern int mouse_reporting(void);
+extern void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry);
+
+#define video_num_columns (vc_cons[currcons].d->vc_cols)
+#define video_num_lines (vc_cons[currcons].d->vc_rows)
+#define video_size_row (vc_cons[currcons].d->vc_size_row)
+#define can_do_color (vc_cons[currcons].d->vc_can_do_color)
+
+extern int console_blanked;
+
+extern unsigned char color_table[];
+extern int default_red[];
+extern int default_grn[];
+extern int default_blu[];
+
+extern unsigned short *screen_pos(int currcons, int w_offset, int viewed);
+extern u16 screen_glyph(int currcons, int offset);
+extern void complement_pos(int currcons, int offset);
+extern void invert_screen(int currcons, int offset, int count, int shift);
+
+extern void getconsxy(int currcons, char *p);
+extern void putconsxy(int currcons, char *p);
+
+extern u16 vcs_scr_readw(int currcons, const u16 *org);
+extern void vcs_scr_writew(int currcons, u16 val, u16 *org);
+
+#endif
diff --git a/pfinet/linux-src/include/linux/sem.h b/pfinet/linux-src/include/linux/sem.h
new file mode 100644
index 00000000..13c9a8ac
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sem.h
@@ -0,0 +1,114 @@
+#ifndef _LINUX_SEM_H
+#define _LINUX_SEM_H
+
+#include <linux/ipc.h>
+
+/* semop flags */
+#define SEM_UNDO 0x1000 /* undo the operation on exit */
+
+/* semctl Command Definitions. */
+#define GETPID 11 /* get sempid */
+#define GETVAL 12 /* get semval */
+#define GETALL 13 /* get all semval's */
+#define GETNCNT 14 /* get semncnt */
+#define GETZCNT 15 /* get semzcnt */
+#define SETVAL 16 /* set semval */
+#define SETALL 17 /* set all semval's */
+
+/* ipcs ctl cmds */
+#define SEM_STAT 18
+#define SEM_INFO 19
+
+/* One semid data structure for each set of semaphores in the system. */
+struct semid_ds {
+ struct ipc_perm sem_perm; /* permissions .. see ipc.h */
+ __kernel_time_t sem_otime; /* last semop time */
+ __kernel_time_t sem_ctime; /* last change time */
+ struct sem *sem_base; /* ptr to first semaphore in array */
+ struct sem_queue *sem_pending; /* pending operations to be processed */
+ struct sem_queue **sem_pending_last; /* last pending operation */
+ struct sem_undo *undo; /* undo requests on this array */
+ unsigned short sem_nsems; /* no. of semaphores in array */
+};
+
+/* semop system calls takes an array of these. */
+struct sembuf {
+ unsigned short sem_num; /* semaphore index in array */
+ short sem_op; /* semaphore operation */
+ short sem_flg; /* operation flags */
+};
+
+/* arg for semctl system calls. */
+union semun {
+ int val; /* value for SETVAL */
+ struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
+ unsigned short *array; /* array for GETALL & SETALL */
+ struct seminfo *__buf; /* buffer for IPC_INFO */
+ void *__pad;
+};
+
+struct seminfo {
+ int semmap;
+ int semmni;
+ int semmns;
+ int semmnu;
+ int semmsl;
+ int semopm;
+ int semume;
+ int semusz;
+ int semvmx;
+ int semaem;
+};
+
+#define SEMMNI 128 /* ? max # of semaphore identifiers */
+#define SEMMSL 250 /* <= 512 max num of semaphores per id */
+#define SEMMNS (SEMMNI*SEMMSL) /* ? max # of semaphores in system */
+#define SEMOPM 32 /* ~ 100 max num of ops per semop call */
+#define SEMVMX 32767 /* semaphore maximum value */
+
+/* unused */
+#define SEMUME SEMOPM /* max num of undo entries per process */
+#define SEMMNU SEMMNS /* num of undo structures system wide */
+#define SEMAEM (SEMVMX >> 1) /* adjust on exit max value */
+#define SEMMAP SEMMNS /* # of entries in semaphore map */
+#define SEMUSZ 20 /* sizeof struct sem_undo */
+
+#ifdef __KERNEL__
+
+/* One semaphore structure for each semaphore in the system. */
+struct sem {
+ int semval; /* current value */
+ int sempid; /* pid of last operation */
+};
+
+/* One queue for each semaphore set in the system. */
+struct sem_queue {
+ struct sem_queue * next; /* next entry in the queue */
+ struct sem_queue ** prev; /* previous entry in the queue, *(q->prev) == q */
+ struct wait_queue * sleeper; /* sleeping process */
+ struct sem_undo * undo; /* undo structure */
+ int pid; /* process id of requesting process */
+ int status; /* completion status of operation */
+ struct semid_ds * sma; /* semaphore array for operations */
+ struct sembuf * sops; /* array of pending operations */
+ int nsops; /* number of operations */
+ int alter; /* operation will alter semaphore */
+};
+
+/* Each task has a list of undo requests. They are executed automatically
+ * when the process exits.
+ */
+struct sem_undo {
+ struct sem_undo * proc_next; /* next entry on this process */
+ struct sem_undo * id_next; /* next entry on this semaphore set */
+ int semid; /* semaphore set identifier */
+ short * semadj; /* array of adjustments, one per semaphore */
+};
+
+asmlinkage int sys_semget (key_t key, int nsems, int semflg);
+asmlinkage int sys_semop (int semid, struct sembuf *sops, unsigned nsops);
+asmlinkage int sys_semctl (int semid, int semnum, int cmd, union semun arg);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SEM_H */
diff --git a/pfinet/linux-src/include/linux/serial.h b/pfinet/linux-src/include/linux/serial.h
new file mode 100644
index 00000000..929618dd
--- /dev/null
+++ b/pfinet/linux-src/include/linux/serial.h
@@ -0,0 +1,142 @@
+/*
+ * include/linux/serial.h
+ *
+ * Copyright (C) 1992 by Theodore Ts'o.
+ *
+ * Redistribution of this file is permitted under the terms of the GNU
+ * Public License (GPL)
+ */
+
+#ifndef _LINUX_SERIAL_H
+#define _LINUX_SERIAL_H
+
+struct serial_struct {
+ int type;
+ int line;
+ int port;
+ int irq;
+ int flags;
+ int xmit_fifo_size;
+ int custom_divisor;
+ int baud_base;
+ unsigned short close_delay;
+ char reserved_char[2];
+ int hub6;
+ unsigned short closing_wait; /* time to wait before closing */
+ unsigned short closing_wait2; /* no longer used... */
+ int reserved[4];
+};
+
+/*
+ * For the close wait times, 0 means wait forever for serial port to
+ * flush its output. 65535 means don't wait at all.
+ */
+#define ASYNC_CLOSING_WAIT_INF 0
+#define ASYNC_CLOSING_WAIT_NONE 65535
+
+/*
+ * These are the supported serial types.
+ */
+#define PORT_UNKNOWN 0
+#define PORT_8250 1
+#define PORT_16450 2
+#define PORT_16550 3
+#define PORT_16550A 4
+#define PORT_CIRRUS 5 /* usurped by cyclades.c */
+#define PORT_16650 6
+#define PORT_16650V2 7
+#define PORT_16750 8
+#define PORT_STARTECH 9 /* usurped by cyclades.c */
+#define PORT_MAX 9
+
+struct serial_uart_config {
+ char *name;
+ int dfl_xmit_fifo_size;
+ int flags;
+};
+
+#define UART_CLEAR_FIFO 0x01
+#define UART_USE_FIFO 0x02
+#define UART_STARTECH 0x04
+
+/*
+ * Definitions for async_struct (and serial_struct) flags field
+ */
+#define ASYNC_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes
+ on the callout port */
+#define ASYNC_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
+#define ASYNC_SAK 0x0004 /* Secure Attention Key (Orange book) */
+#define ASYNC_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
+
+#define ASYNC_SPD_MASK 0x1030
+#define ASYNC_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */
+
+#define ASYNC_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */
+#define ASYNC_SPD_CUST 0x0030 /* Use user-specified divisor */
+
+#define ASYNC_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
+#define ASYNC_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
+#define ASYNC_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
+#define ASYNC_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
+#define ASYNC_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */
+
+#define ASYNC_HARDPPS_CD 0x0800 /* Call hardpps when CD goes high */
+
+#define ASYNC_SPD_SHI 0x1000 /* Use 230400 instead of 38400 bps */
+#define ASYNC_SPD_WARP 0x1010 /* Use 460800 instead of 38400 bps */
+
+#define ASYNC_LOW_LATENCY 0x2000 /* Request low latency behaviour */
+
+#define ASYNC_FLAGS 0x3FFF /* Possible legal async flags */
+#define ASYNC_USR_MASK 0x3430 /* Legal flags that non-privileged
+ * users can set or reset */
+
+/* Internal flags used only by kernel/chr_drv/serial.c */
+#define ASYNC_INITIALIZED 0x80000000 /* Serial port was initialized */
+#define ASYNC_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
+#define ASYNC_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
+#define ASYNC_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
+#define ASYNC_CLOSING 0x08000000 /* Serial port is closing */
+#define ASYNC_CTS_FLOW 0x04000000 /* Do CTS flow control */
+#define ASYNC_CHECK_CD 0x02000000 /* i.e., CLOCAL */
+#define ASYNC_SHARE_IRQ 0x01000000 /* for multifunction cards */
+
+#define ASYNC_INTERNAL_FLAGS 0xFF000000 /* Internal flags */
+
+/*
+ * Multiport serial configuration structure --- external structure
+ */
+struct serial_multiport_struct {
+ int irq;
+ int port1;
+ unsigned char mask1, match1;
+ int port2;
+ unsigned char mask2, match2;
+ int port3;
+ unsigned char mask3, match3;
+ int port4;
+ unsigned char mask4, match4;
+ int port_monitor;
+ int reserved[32];
+};
+
+/*
+ * Serial input interrupt line counters -- external structure
+ * Four lines can interrupt: CTS, DSR, RI, DCD
+ */
+struct serial_icounter_struct {
+ int cts, dsr, rng, dcd;
+ int rx, tx;
+ int frame, overrun, parity, brk;
+ int buf_overrun;
+ int reserved[9];
+};
+
+
+#ifdef __KERNEL__
+/* Export to allow PCMCIA to use this - Dave Hinds */
+extern int register_serial(struct serial_struct *req);
+extern void unregister_serial(int line);
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_SERIAL_H */
diff --git a/pfinet/linux-src/include/linux/serial167.h b/pfinet/linux-src/include/linux/serial167.h
new file mode 100644
index 00000000..9f01f361
--- /dev/null
+++ b/pfinet/linux-src/include/linux/serial167.h
@@ -0,0 +1,175 @@
+/*
+ * serial167.h
+ *
+ * Richard Hirst [richard@sleepie.demon.co.uk]
+ *
+ * Based on cyclades.h
+ */
+
+struct cyclades_monitor {
+ unsigned long int_count;
+ unsigned long char_count;
+ unsigned long char_max;
+ unsigned long char_last;
+};
+
+/*
+ * This is our internal structure for each serial port's state.
+ *
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+struct cyclades_port {
+ int magic;
+ int type;
+ int card;
+ int line;
+ int flags; /* defined in tty.h */
+ struct tty_struct *tty;
+ int read_status_mask;
+ int timeout;
+ int xmit_fifo_size;
+ int cor1,cor2,cor3,cor4,cor5,cor6,cor7;
+ int tbpr,tco,rbpr,rco;
+ int ignore_status_mask;
+ int close_delay;
+ int IER; /* Interrupt Enable Register */
+ int event;
+ unsigned long last_active;
+ int count; /* # of fd on device */
+ int x_char; /* to be pushed out ASAP */
+ int x_break;
+ int blocked_open; /* # of blocked opens */
+ long session; /* Session of opening process */
+ long pgrp; /* pgrp of opening process */
+ unsigned char *xmit_buf;
+ int xmit_head;
+ int xmit_tail;
+ int xmit_cnt;
+ int default_threshold;
+ int default_timeout;
+ struct tq_struct tqueue;
+ struct termios normal_termios;
+ struct termios callout_termios;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+ struct cyclades_monitor mon;
+};
+
+#define CYCLADES_MAGIC 0x4359
+
+#define CYGETMON 0x435901
+#define CYGETTHRESH 0x435902
+#define CYSETTHRESH 0x435903
+#define CYGETDEFTHRESH 0x435904
+#define CYSETDEFTHRESH 0x435905
+#define CYGETTIMEOUT 0x435906
+#define CYSETTIMEOUT 0x435907
+#define CYGETDEFTIMEOUT 0x435908
+#define CYSETDEFTIMEOUT 0x435909
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at cy interrupt time.
+ */
+#define Cy_EVENT_READ_PROCESS 0
+#define Cy_EVENT_WRITE_WAKEUP 1
+#define Cy_EVENT_HANGUP 2
+#define Cy_EVENT_BREAK 3
+#define Cy_EVENT_OPEN_WAKEUP 4
+
+
+
+#define CyMaxChipsPerCard 1
+
+/**** cd2401 registers ****/
+
+#define CyGFRCR (0x81)
+#define CyCCR (0x13)
+#define CyCLR_CHAN (0x40)
+#define CyINIT_CHAN (0x20)
+#define CyCHIP_RESET (0x10)
+#define CyENB_XMTR (0x08)
+#define CyDIS_XMTR (0x04)
+#define CyENB_RCVR (0x02)
+#define CyDIS_RCVR (0x01)
+#define CyCAR (0xee)
+#define CyIER (0x11)
+#define CyMdmCh (0x80)
+#define CyRxExc (0x20)
+#define CyRxData (0x08)
+#define CyTxMpty (0x02)
+#define CyTxRdy (0x01)
+#define CyLICR (0x26)
+#define CyRISR (0x89)
+#define CyTIMEOUT (0x80)
+#define CySPECHAR (0x70)
+#define CyOVERRUN (0x08)
+#define CyPARITY (0x04)
+#define CyFRAME (0x02)
+#define CyBREAK (0x01)
+#define CyREOIR (0x84)
+#define CyTEOIR (0x85)
+#define CyMEOIR (0x86)
+#define CyNOTRANS (0x08)
+#define CyRFOC (0x30)
+#define CyRDR (0xf8)
+#define CyTDR (0xf8)
+#define CyMISR (0x8b)
+#define CyRISR (0x89)
+#define CyTISR (0x8a)
+#define CyMSVR1 (0xde)
+#define CyMSVR2 (0xdf)
+#define CyDSR (0x80)
+#define CyDCD (0x40)
+#define CyCTS (0x20)
+#define CyDTR (0x02)
+#define CyRTS (0x01)
+#define CyRTPRL (0x25)
+#define CyRTPRH (0x24)
+#define CyCOR1 (0x10)
+#define CyPARITY_NONE (0x00)
+#define CyPARITY_E (0x40)
+#define CyPARITY_O (0xC0)
+#define Cy_5_BITS (0x04)
+#define Cy_6_BITS (0x05)
+#define Cy_7_BITS (0x06)
+#define Cy_8_BITS (0x07)
+#define CyCOR2 (0x17)
+#define CyETC (0x20)
+#define CyCtsAE (0x02)
+#define CyCOR3 (0x16)
+#define Cy_1_STOP (0x02)
+#define Cy_2_STOP (0x04)
+#define CyCOR4 (0x15)
+#define CyREC_FIFO (0x0F) /* Receive FIFO threshold */
+#define CyCOR5 (0x14)
+#define CyCOR6 (0x18)
+#define CyCOR7 (0x07)
+#define CyRBPR (0xcb)
+#define CyRCOR (0xc8)
+#define CyTBPR (0xc3)
+#define CyTCOR (0xc0)
+#define CySCHR1 (0x1f)
+#define CySCHR2 (0x1e)
+#define CyTPR (0xda)
+#define CyPILR1 (0xe3)
+#define CyPILR2 (0xe0)
+#define CyPILR3 (0xe1)
+#define CyCMR (0x1b)
+#define CyASYNC (0x02)
+#define CyLICR (0x26)
+#define CyLIVR (0x09)
+#define CySCRL (0x23)
+#define CySCRH (0x22)
+#define CyTFTC (0x80)
+
+
+/* max number of chars in the FIFO */
+
+#define CyMAX_CHAR_FIFO 12
+
+/***************************************************************************/
diff --git a/pfinet/linux-src/include/linux/serialP.h b/pfinet/linux-src/include/linux/serialP.h
new file mode 100644
index 00000000..6bf0746e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/serialP.h
@@ -0,0 +1,119 @@
+/*
+ * Private header file for the (dumb) serial driver
+ *
+ * Copyright (C) 1997 by Theodore Ts'o.
+ *
+ * Redistribution of this file is permitted under the terms of the GNU
+ * Public License (GPL)
+ */
+
+#ifndef _LINUX_SERIALP_H
+#define _LINUX_SERIALP_H
+
+/*
+ * This is our internal structure for each serial port's state.
+ *
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+#include <linux/termios.h>
+#include <linux/tqueue.h>
+
+/*
+ * Counters of the input lines (CTS, DSR, RI, CD) interrupts
+ */
+struct async_icount {
+ __u32 cts, dsr, rng, dcd, tx, rx;
+ __u32 frame, parity, overrun, brk;
+ __u32 buf_overrun;
+};
+
+struct serial_state {
+ int magic;
+ int baud_base;
+ int port;
+ int irq;
+ int flags;
+ int hub6;
+ int type;
+ int line;
+ int xmit_fifo_size;
+ int custom_divisor;
+ int count;
+ unsigned short close_delay;
+ unsigned short closing_wait; /* time to wait before closing */
+ struct async_icount icount;
+ struct termios normal_termios;
+ struct termios callout_termios;
+ struct async_struct *info;
+};
+
+struct async_struct {
+ int magic;
+ int port;
+ int hub6;
+ int flags;
+ int xmit_fifo_size;
+ struct serial_state *state;
+ struct tty_struct *tty;
+ int read_status_mask;
+ int ignore_status_mask;
+ int timeout;
+ int quot;
+ int x_char; /* xon/xoff character */
+ int close_delay;
+ unsigned short closing_wait;
+ unsigned short closing_wait2;
+ int IER; /* Interrupt Enable Register */
+ int MCR; /* Modem control register */
+ unsigned long event;
+ unsigned long last_active;
+ int line;
+ int blocked_open; /* # of blocked opens */
+ long session; /* Session of opening process */
+ long pgrp; /* pgrp of opening process */
+ unsigned char *xmit_buf;
+ int xmit_head;
+ int xmit_tail;
+ int xmit_cnt;
+ struct tq_struct tqueue;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+ struct wait_queue *delta_msr_wait;
+ struct async_struct *next_port; /* For the linked list */
+ struct async_struct *prev_port;
+};
+
+#define SERIAL_MAGIC 0x5301
+#define SSTATE_MAGIC 0x5302
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define SERIAL_XMIT_SIZE 4096
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP 0
+
+/*
+ * Multiport serial configuration structure --- internal structure
+ */
+struct rs_multiport_struct {
+ int port1;
+ unsigned char mask1, match1;
+ int port2;
+ unsigned char mask2, match2;
+ int port3;
+ unsigned char mask3, match3;
+ int port4;
+ unsigned char mask4, match4;
+ int port_monitor;
+};
+
+#endif /* _LINUX_SERIAL_H */
diff --git a/pfinet/linux-src/include/linux/serial_reg.h b/pfinet/linux-src/include/linux/serial_reg.h
new file mode 100644
index 00000000..20ab5746
--- /dev/null
+++ b/pfinet/linux-src/include/linux/serial_reg.h
@@ -0,0 +1,143 @@
+/*
+ * include/linux/serial_reg.h
+ *
+ * Copyright (C) 1992, 1994 by Theodore Ts'o.
+ *
+ * Redistribution of this file is permitted under the terms of the GNU
+ * Public License (GPL)
+ *
+ * These are the UART port assignments, expressed as offsets from the base
+ * register. These assignments should hold for any serial port based on
+ * a 8250, 16450, or 16550(A).
+ */
+
+#ifndef _LINUX_SERIAL_REG_H
+#define _LINUX_SERIAL_REG_H
+
+#define UART_RX 0 /* In: Receive buffer (DLAB=0) */
+#define UART_TX 0 /* Out: Transmit buffer (DLAB=0) */
+#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */
+#define UART_DLM 1 /* Out: Divisor Latch High (DLAB=1) */
+#define UART_IER 1 /* Out: Interrupt Enable Register */
+#define UART_IIR 2 /* In: Interrupt ID Register */
+#define UART_FCR 2 /* Out: FIFO Control Register */
+#define UART_EFR 2 /* I/O: Extended Features Register */
+ /* (DLAB=1, 16C660 only) */
+#define UART_LCR 3 /* Out: Line Control Register */
+#define UART_MCR 4 /* Out: Modem Control Register */
+#define UART_LSR 5 /* In: Line Status Register */
+#define UART_MSR 6 /* In: Modem Status Register */
+#define UART_SCR 7 /* I/O: Scratch Register */
+
+/*
+ * These are the definitions for the FIFO Control Register
+ * (16650 only)
+ */
+#define UART_FCR_ENABLE_FIFO 0x01 /* Enable the FIFO */
+#define UART_FCR_CLEAR_RCVR 0x02 /* Clear the RCVR FIFO */
+#define UART_FCR_CLEAR_XMIT 0x04 /* Clear the XMIT FIFO */
+#define UART_FCR_DMA_SELECT 0x08 /* For DMA applications */
+#define UART_FCR_TRIGGER_MASK 0xC0 /* Mask for the FIFO trigger range */
+#define UART_FCR_TRIGGER_1 0x00 /* Mask for trigger set at 1 */
+#define UART_FCR_TRIGGER_4 0x40 /* Mask for trigger set at 4 */
+#define UART_FCR_TRIGGER_8 0x80 /* Mask for trigger set at 8 */
+#define UART_FCR_TRIGGER_14 0xC0 /* Mask for trigger set at 14 */
+/* 16650 redefinitions */
+#define UART_FCR6_R_TRIGGER_8 0x00 /* Mask for receive trigger set at 1 */
+#define UART_FCR6_R_TRIGGER_16 0x40 /* Mask for receive trigger set at 4 */
+#define UART_FCR6_R_TRIGGER_24 0x80 /* Mask for receive trigger set at 8 */
+#define UART_FCR6_R_TRIGGER_28 0xC0 /* Mask for receive trigger set at 14 */
+#define UART_FCR6_T_TRIGGER_16 0x00 /* Mask for transmit trigger set at 16 */
+#define UART_FCR6_T_TRIGGER_8 0x10 /* Mask for transmit trigger set at 8 */
+#define UART_FCR6_T_TRIGGER_24 0x20 /* Mask for transmit trigger set at 24 */
+#define UART_FCR6_T_TRIGGER_30 0x30 /* Mask for transmit trigger set at 30 */
+/* TI 16750 definitions */
+#define UART_FCR7_64BYTE 0x20 /* Go into 64 byte mode */
+
+/*
+ * These are the definitions for the Line Control Register
+ *
+ * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting
+ * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits.
+ */
+#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */
+#define UART_LCR_SBC 0x40 /* Set break control */
+#define UART_LCR_SPAR 0x20 /* Stick parity (?) */
+#define UART_LCR_EPAR 0x10 /* Even parity select */
+#define UART_LCR_PARITY 0x08 /* Parity Enable */
+#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 stop bit, 1= 2 stop bits */
+#define UART_LCR_WLEN5 0x00 /* Wordlength: 5 bits */
+#define UART_LCR_WLEN6 0x01 /* Wordlength: 6 bits */
+#define UART_LCR_WLEN7 0x02 /* Wordlength: 7 bits */
+#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */
+
+/*
+ * These are the definitions for the Line Status Register
+ */
+#define UART_LSR_TEMT 0x40 /* Transmitter empty */
+#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
+#define UART_LSR_BI 0x10 /* Break interrupt indicator */
+#define UART_LSR_FE 0x08 /* Frame error indicator */
+#define UART_LSR_PE 0x04 /* Parity error indicator */
+#define UART_LSR_OE 0x02 /* Overrun error indicator */
+#define UART_LSR_DR 0x01 /* Receiver data ready */
+
+/*
+ * These are the definitions for the Interrupt Identification Register
+ */
+#define UART_IIR_NO_INT 0x01 /* No interrupts pending */
+#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */
+
+#define UART_IIR_MSI 0x00 /* Modem status interrupt */
+#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */
+#define UART_IIR_RDI 0x04 /* Receiver data interrupt */
+#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */
+
+/*
+ * These are the definitions for the Interrupt Enable Register
+ */
+#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */
+#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */
+#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */
+#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */
+/*
+ * Sleep mode for ST16650 and TI16750.
+ * Note that for 16650, EFR-bit 4 must be selected as well.
+ */
+#define UART_IERX_SLEEP 0x10 /* Enable sleep mode */
+
+/*
+ * These are the definitions for the Modem Control Register
+ */
+#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */
+#define UART_MCR_OUT2 0x08 /* Out2 complement */
+#define UART_MCR_OUT1 0x04 /* Out1 complement */
+#define UART_MCR_RTS 0x02 /* RTS complement */
+#define UART_MCR_DTR 0x01 /* DTR complement */
+
+/*
+ * These are the definitions for the Modem Status Register
+ */
+#define UART_MSR_DCD 0x80 /* Data Carrier Detect */
+#define UART_MSR_RI 0x40 /* Ring Indicator */
+#define UART_MSR_DSR 0x20 /* Data Set Ready */
+#define UART_MSR_CTS 0x10 /* Clear to Send */
+#define UART_MSR_DDCD 0x08 /* Delta DCD */
+#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */
+#define UART_MSR_DDSR 0x02 /* Delta DSR */
+#define UART_MSR_DCTS 0x01 /* Delta CTS */
+#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */
+
+/*
+ * These are the definitions for the Extended Features Register
+ * (StarTech 16C660 only, when DLAB=1)
+ */
+#define UART_EFR_CTS 0x80 /* CTS flow control */
+#define UART_EFR_RTS 0x40 /* RTS flow control */
+#define UART_EFR_SCD 0x20 /* Special character detect */
+#define UART_EFR_ECB 0x10 /* Enhanced control bit */
+/*
+ * the low four bits control software flow control
+ */
+
+#endif /* _LINUX_SERIAL_REG_H */
diff --git a/pfinet/linux-src/include/linux/shm.h b/pfinet/linux-src/include/linux/shm.h
new file mode 100644
index 00000000..a6d13e8d
--- /dev/null
+++ b/pfinet/linux-src/include/linux/shm.h
@@ -0,0 +1,79 @@
+#ifndef _LINUX_SHM_H_
+#define _LINUX_SHM_H_
+
+#include <linux/ipc.h>
+
+#include <asm/shmparam.h>
+
+struct shmid_ds {
+ struct ipc_perm shm_perm; /* operation perms */
+ int shm_segsz; /* size of segment (bytes) */
+ __kernel_time_t shm_atime; /* last attach time */
+ __kernel_time_t shm_dtime; /* last detach time */
+ __kernel_time_t shm_ctime; /* last change time */
+ __kernel_ipc_pid_t shm_cpid; /* pid of creator */
+ __kernel_ipc_pid_t shm_lpid; /* pid of last operator */
+ unsigned short shm_nattch; /* no. of current attaches */
+ unsigned short shm_unused; /* compatibility */
+ void *shm_unused2; /* ditto - used by DIPC */
+ void *shm_unused3; /* unused */
+};
+
+struct shmid_kernel
+{
+ struct shmid_ds u;
+ /* the following are private */
+ unsigned long shm_npages; /* size of segment (pages) */
+ unsigned long *shm_pages; /* array of ptrs to frames -> SHMMAX */
+ struct vm_area_struct *attaches; /* descriptors for attaches */
+};
+
+/* permission flag for shmget */
+#define SHM_R 0400 /* or S_IRUGO from <linux/stat.h> */
+#define SHM_W 0200 /* or S_IWUGO from <linux/stat.h> */
+
+/* mode for attach */
+#define SHM_RDONLY 010000 /* read-only access */
+#define SHM_RND 020000 /* round attach address to SHMLBA boundary */
+#define SHM_REMAP 040000 /* take-over region on attach */
+
+/* super user shmctl commands */
+#define SHM_LOCK 11
+#define SHM_UNLOCK 12
+
+/* ipcs ctl commands */
+#define SHM_STAT 13
+#define SHM_INFO 14
+
+struct shminfo {
+ int shmmax;
+ int shmmin;
+ int shmmni;
+ int shmseg;
+ int shmall;
+};
+
+struct shm_info {
+ int used_ids;
+ unsigned long shm_tot; /* total allocated shm */
+ unsigned long shm_rss; /* total resident shm */
+ unsigned long shm_swp; /* total swapped shm */
+ unsigned long swap_attempts;
+ unsigned long swap_successes;
+};
+
+#ifdef __KERNEL__
+
+/* shm_mode upper byte flags */
+#define SHM_DEST 01000 /* segment will be destroyed on last detach */
+#define SHM_LOCKED 02000 /* segment will not be swapped */
+
+asmlinkage int sys_shmget (key_t key, int size, int flag);
+asmlinkage int sys_shmat (int shmid, char *shmaddr, int shmflg, unsigned long *addr);
+asmlinkage int sys_shmdt (char *shmaddr);
+asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf);
+extern void shm_unuse(unsigned long entry, unsigned long page);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SHM_H_ */
diff --git a/pfinet/linux-src/include/linux/signal.h b/pfinet/linux-src/include/linux/signal.h
new file mode 100644
index 00000000..d6e82ae0
--- /dev/null
+++ b/pfinet/linux-src/include/linux/signal.h
@@ -0,0 +1,212 @@
+#ifndef _LINUX_SIGNAL_H
+#define _LINUX_SIGNAL_H
+
+#include <asm/signal.h>
+#include <asm/siginfo.h>
+
+#ifdef __KERNEL__
+/*
+ * Real Time signals may be queued.
+ */
+
+struct signal_queue
+{
+ struct signal_queue *next;
+ siginfo_t info;
+};
+
+/*
+ * Define some primitives to manipulate sigset_t.
+ */
+
+#ifndef __HAVE_ARCH_SIG_BITOPS
+#include <asm/bitops.h>
+
+/* We don't use <asm/bitops.h> for these because there is no need to
+ be atomic. */
+extern inline void sigaddset(sigset_t *set, int _sig)
+{
+ unsigned long sig = _sig - 1;
+ if (_NSIG_WORDS == 1)
+ set->sig[0] |= 1UL << sig;
+ else
+ set->sig[sig / _NSIG_BPW] |= 1UL << (sig % _NSIG_BPW);
+}
+
+extern inline void sigdelset(sigset_t *set, int _sig)
+{
+ unsigned long sig = _sig - 1;
+ if (_NSIG_WORDS == 1)
+ set->sig[0] &= ~(1UL << sig);
+ else
+ set->sig[sig / _NSIG_BPW] &= ~(1UL << (sig % _NSIG_BPW));
+}
+
+extern inline int sigismember(sigset_t *set, int _sig)
+{
+ unsigned long sig = _sig - 1;
+ if (_NSIG_WORDS == 1)
+ return 1 & (set->sig[0] >> sig);
+ else
+ return 1 & (set->sig[sig / _NSIG_BPW] >> (sig % _NSIG_BPW));
+}
+
+extern inline int sigfindinword(unsigned long word)
+{
+ return ffz(~word);
+}
+
+#define sigmask(sig) (1UL << ((sig) - 1))
+
+#endif /* __HAVE_ARCH_SIG_BITOPS */
+
+#ifndef __HAVE_ARCH_SIG_SETOPS
+#include <linux/string.h>
+
+#define _SIG_SET_BINOP(name, op) \
+extern inline void name(sigset_t *r, const sigset_t *a, const sigset_t *b) \
+{ \
+ unsigned long a0, a1, a2, a3, b0, b1, b2, b3; \
+ unsigned long i; \
+ \
+ for (i = 0; i < _NSIG_WORDS/4; ++i) { \
+ a0 = a->sig[4*i+0]; a1 = a->sig[4*i+1]; \
+ a2 = a->sig[4*i+2]; a3 = a->sig[4*i+3]; \
+ b0 = b->sig[4*i+0]; b1 = b->sig[4*i+1]; \
+ b2 = b->sig[4*i+2]; b3 = b->sig[4*i+3]; \
+ r->sig[4*i+0] = op(a0, b0); \
+ r->sig[4*i+1] = op(a1, b1); \
+ r->sig[4*i+2] = op(a2, b2); \
+ r->sig[4*i+3] = op(a3, b3); \
+ } \
+ switch (_NSIG_WORDS % 4) { \
+ case 3: \
+ a0 = a->sig[4*i+0]; a1 = a->sig[4*i+1]; a2 = a->sig[4*i+2]; \
+ b0 = b->sig[4*i+0]; b1 = b->sig[4*i+1]; b2 = b->sig[4*i+2]; \
+ r->sig[4*i+0] = op(a0, b0); \
+ r->sig[4*i+1] = op(a1, b1); \
+ r->sig[4*i+2] = op(a2, b2); \
+ break; \
+ case 2: \
+ a0 = a->sig[4*i+0]; a1 = a->sig[4*i+1]; \
+ b0 = b->sig[4*i+0]; b1 = b->sig[4*i+1]; \
+ r->sig[4*i+0] = op(a0, b0); \
+ r->sig[4*i+1] = op(a1, b1); \
+ break; \
+ case 1: \
+ a0 = a->sig[4*i+0]; b0 = b->sig[4*i+0]; \
+ r->sig[4*i+0] = op(a0, b0); \
+ break; \
+ } \
+}
+
+#define _sig_or(x,y) ((x) | (y))
+_SIG_SET_BINOP(sigorsets, _sig_or)
+
+#define _sig_and(x,y) ((x) & (y))
+_SIG_SET_BINOP(sigandsets, _sig_and)
+
+#define _sig_nand(x,y) ((x) & ~(y))
+_SIG_SET_BINOP(signandsets, _sig_nand)
+
+#undef _SIG_SET_BINOP
+#undef _sig_or
+#undef _sig_and
+#undef _sig_nand
+
+#define _SIG_SET_OP(name, op) \
+extern inline void name(sigset_t *set) \
+{ \
+ unsigned long i; \
+ \
+ for (i = 0; i < _NSIG_WORDS/4; ++i) { \
+ set->sig[4*i+0] = op(set->sig[4*i+0]); \
+ set->sig[4*i+1] = op(set->sig[4*i+1]); \
+ set->sig[4*i+2] = op(set->sig[4*i+2]); \
+ set->sig[4*i+3] = op(set->sig[4*i+3]); \
+ } \
+ switch (_NSIG_WORDS % 4) { \
+ case 3: set->sig[4*i+2] = op(set->sig[4*i+2]); \
+ case 2: set->sig[4*i+1] = op(set->sig[4*i+1]); \
+ case 1: set->sig[4*i+0] = op(set->sig[4*i+0]); \
+ } \
+}
+
+#define _sig_not(x) (~(x))
+_SIG_SET_OP(signotset, _sig_not)
+
+#undef _SIG_SET_OP
+#undef _sig_not
+
+extern inline void sigemptyset(sigset_t *set)
+{
+ switch (_NSIG_WORDS) {
+ default:
+ memset(set, 0, sizeof(sigset_t));
+ break;
+ case 2: set->sig[1] = 0;
+ case 1: set->sig[0] = 0;
+ break;
+ }
+}
+
+extern inline void sigfillset(sigset_t *set)
+{
+ switch (_NSIG_WORDS) {
+ default:
+ memset(set, -1, sizeof(sigset_t));
+ break;
+ case 2: set->sig[1] = -1;
+ case 1: set->sig[0] = -1;
+ break;
+ }
+}
+
+extern char * render_sigset_t(sigset_t *set, char *buffer);
+
+/* Some extensions for manipulating the low 32 signals in particular. */
+
+extern inline void sigaddsetmask(sigset_t *set, unsigned long mask)
+{
+ set->sig[0] |= mask;
+}
+
+extern inline void sigdelsetmask(sigset_t *set, unsigned long mask)
+{
+ set->sig[0] &= ~mask;
+}
+
+extern inline int sigtestsetmask(sigset_t *set, unsigned long mask)
+{
+ return (set->sig[0] & mask) != 0;
+}
+
+extern inline void siginitset(sigset_t *set, unsigned long mask)
+{
+ set->sig[0] = mask;
+ switch (_NSIG_WORDS) {
+ default:
+ memset(&set->sig[1], 0, sizeof(long)*(_NSIG_WORDS-1));
+ break;
+ case 2: set->sig[1] = 0;
+ case 1:
+ }
+}
+
+extern inline void siginitsetinv(sigset_t *set, unsigned long mask)
+{
+ set->sig[0] = ~mask;
+ switch (_NSIG_WORDS) {
+ default:
+ memset(&set->sig[1], -1, sizeof(long)*(_NSIG_WORDS-1));
+ break;
+ case 2: set->sig[1] = -1;
+ case 1:
+ }
+}
+
+#endif /* __HAVE_ARCH_SIG_SETOPS */
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SIGNAL_H */
diff --git a/pfinet/linux-src/include/linux/skbuff.h b/pfinet/linux-src/include/linux/skbuff.h
new file mode 100644
index 00000000..82d5da6e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/skbuff.h
@@ -0,0 +1,584 @@
+/*
+ * Definitions for the 'struct sk_buff' memory handlers.
+ *
+ * Authors:
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ * Florian La Roche, <rzsfl@rz.uni-sb.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_SKBUFF_H
+#define _LINUX_SKBUFF_H
+
+#include <linux/config.h>
+#include <linux/time.h>
+
+#include <asm/atomic.h>
+#include <asm/types.h>
+#include <asm/spinlock.h>
+
+#define HAVE_ALLOC_SKB /* For the drivers to know */
+#define HAVE_ALIGNABLE_SKB /* Ditto 8) */
+#define SLAB_SKB /* Slabified skbuffs */
+
+#define CHECKSUM_NONE 0
+#define CHECKSUM_HW 1
+#define CHECKSUM_UNNECESSARY 2
+
+struct sk_buff_head {
+ struct sk_buff * next;
+ struct sk_buff * prev;
+ __u32 qlen; /* Must be same length as a pointer
+ for using debugging */
+};
+
+struct sk_buff {
+ struct sk_buff * next; /* Next buffer in list */
+ struct sk_buff * prev; /* Previous buffer in list */
+ struct sk_buff_head * list; /* List we are on */
+ struct sock *sk; /* Socket we are owned by */
+ struct timeval stamp; /* Time we arrived */
+ struct device *dev; /* Device we arrived on/are leaving by */
+
+ /* Transport layer header */
+ union
+ {
+ struct tcphdr *th;
+ struct udphdr *uh;
+ struct icmphdr *icmph;
+ struct igmphdr *igmph;
+ struct iphdr *ipiph;
+ struct spxhdr *spxh;
+ unsigned char *raw;
+ } h;
+
+ /* Network layer header */
+ union
+ {
+ struct iphdr *iph;
+ struct ipv6hdr *ipv6h;
+ struct arphdr *arph;
+ struct ipxhdr *ipxh;
+ unsigned char *raw;
+ } nh;
+
+ /* Link layer header */
+ union
+ {
+ struct ethhdr *ethernet;
+ unsigned char *raw;
+ } mac;
+
+ struct dst_entry *dst;
+
+ char cb[48];
+
+ unsigned int len; /* Length of actual data */
+ unsigned int csum; /* Checksum */
+ volatile char used; /* Data moved to user and not MSG_PEEK */
+ unsigned char is_clone, /* We are a clone */
+ cloned, /* head may be cloned (check refcnt to be sure). */
+ pkt_type, /* Packet class */
+ pkt_bridged, /* Tracker for bridging */
+ ip_summed; /* Driver fed us an IP checksum */
+ __u32 priority; /* Packet queueing priority */
+ atomic_t users; /* User count - see datagram.c,tcp.c */
+ unsigned short protocol; /* Packet protocol from driver. */
+ unsigned short security; /* Security level of packet */
+ unsigned int truesize; /* Buffer size */
+
+ unsigned char *head; /* Head of buffer */
+ unsigned char *data; /* Data head pointer */
+ unsigned char *tail; /* Tail pointer */
+ unsigned char *end; /* End pointer */
+ void (*destructor)(struct sk_buff *); /* Destruct function */
+#ifdef CONFIG_IP_FIREWALL
+ __u32 fwmark; /* Label made by fwchains, used by pktsched */
+#endif
+#if defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE)
+ __u32 shapelatency; /* Latency on frame */
+ __u32 shapeclock; /* Time it should go out */
+ __u32 shapelen; /* Frame length in clocks */
+ __u32 shapestamp; /* Stamp for shaper */
+ __u16 shapepend; /* Pending */
+#endif
+
+#if defined(CONFIG_HIPPI)
+ union{
+ __u32 ifield;
+ } private;
+#endif
+};
+
+/* These are just the default values. This is run time configurable.
+ * FIXME: Probably the config option should go away. -- erics
+ */
+#ifdef CONFIG_SKB_LARGE
+#define SK_WMEM_MAX 65535
+#define SK_RMEM_MAX 65535
+#else
+#define SK_WMEM_MAX 32767
+#define SK_RMEM_MAX 32767
+#endif
+
+#ifdef __KERNEL__
+/*
+ * Handling routines are only of interest to the kernel
+ */
+#include <linux/malloc.h>
+
+#include <asm/system.h>
+
+extern void __kfree_skb(struct sk_buff *skb);
+extern void skb_queue_head_init(struct sk_buff_head *list);
+extern void skb_queue_head(struct sk_buff_head *list,struct sk_buff *buf);
+extern void skb_queue_tail(struct sk_buff_head *list,struct sk_buff *buf);
+extern struct sk_buff * skb_dequeue(struct sk_buff_head *list);
+extern void skb_insert(struct sk_buff *old,struct sk_buff *newsk);
+extern void skb_append(struct sk_buff *old,struct sk_buff *newsk);
+extern void skb_unlink(struct sk_buff *buf);
+extern __u32 skb_queue_len(struct sk_buff_head *list);
+extern struct sk_buff * skb_peek_copy(struct sk_buff_head *list);
+extern struct sk_buff * alloc_skb(unsigned int size, int priority);
+extern struct sk_buff * dev_alloc_skb(unsigned int size);
+extern void kfree_skbmem(struct sk_buff *skb);
+extern struct sk_buff * skb_clone(struct sk_buff *skb, int priority);
+extern struct sk_buff * skb_copy(struct sk_buff *skb, int priority);
+extern struct sk_buff * skb_realloc_headroom(struct sk_buff *skb, int newheadroom);
+#define dev_kfree_skb(a) kfree_skb(a)
+extern unsigned char * skb_put(struct sk_buff *skb, unsigned int len);
+extern unsigned char * skb_push(struct sk_buff *skb, unsigned int len);
+extern unsigned char * skb_pull(struct sk_buff *skb, unsigned int len);
+extern int skb_headroom(struct sk_buff *skb);
+extern int skb_tailroom(struct sk_buff *skb);
+extern void skb_reserve(struct sk_buff *skb, unsigned int len);
+extern void skb_trim(struct sk_buff *skb, unsigned int len);
+extern void skb_over_panic(struct sk_buff *skb, int len, void *here);
+extern void skb_under_panic(struct sk_buff *skb, int len, void *here);
+
+/* Internal */
+extern __inline__ atomic_t *skb_datarefp(struct sk_buff *skb)
+{
+ return (atomic_t *)(skb->end);
+}
+
+extern __inline__ int skb_queue_empty(struct sk_buff_head *list)
+{
+ return (list->next == (struct sk_buff *) list);
+}
+
+extern __inline__ void kfree_skb(struct sk_buff *skb)
+{
+ if (atomic_dec_and_test(&skb->users))
+ __kfree_skb(skb);
+}
+
+/* Use this if you didn't touch the skb state [for fast switching] */
+extern __inline__ void kfree_skb_fast(struct sk_buff *skb)
+{
+ if (atomic_dec_and_test(&skb->users))
+ kfree_skbmem(skb);
+}
+
+extern __inline__ int skb_cloned(struct sk_buff *skb)
+{
+ return skb->cloned && atomic_read(skb_datarefp(skb)) != 1;
+}
+
+extern __inline__ int skb_shared(struct sk_buff *skb)
+{
+ return (atomic_read(&skb->users) != 1);
+}
+
+/*
+ * Copy shared buffers into a new sk_buff. We effectively do COW on
+ * packets to handle cases where we have a local reader and forward
+ * and a couple of other messy ones. The normal one is tcpdumping
+ * a packet thats being forwarded.
+ */
+
+extern __inline__ struct sk_buff *skb_unshare(struct sk_buff *skb, int pri)
+{
+ struct sk_buff *nskb;
+ if(!skb_cloned(skb))
+ return skb;
+ nskb=skb_copy(skb, pri);
+ kfree_skb(skb); /* Free our shared copy */
+ return nskb;
+}
+
+/*
+ * Peek an sk_buff. Unlike most other operations you _MUST_
+ * be careful with this one. A peek leaves the buffer on the
+ * list and someone else may run off with it. For an interrupt
+ * type system cli() peek the buffer copy the data and sti();
+ */
+
+extern __inline__ struct sk_buff *skb_peek(struct sk_buff_head *list_)
+{
+ struct sk_buff *list = ((struct sk_buff *)list_)->next;
+ if (list == (struct sk_buff *)list_)
+ list = NULL;
+ return list;
+}
+
+extern __inline__ struct sk_buff *skb_peek_tail(struct sk_buff_head *list_)
+{
+ struct sk_buff *list = ((struct sk_buff *)list_)->prev;
+ if (list == (struct sk_buff *)list_)
+ list = NULL;
+ return list;
+}
+
+/*
+ * Return the length of an sk_buff queue
+ */
+
+extern __inline__ __u32 skb_queue_len(struct sk_buff_head *list_)
+{
+ return(list_->qlen);
+}
+
+extern __inline__ void skb_queue_head_init(struct sk_buff_head *list)
+{
+ list->prev = (struct sk_buff *)list;
+ list->next = (struct sk_buff *)list;
+ list->qlen = 0;
+}
+
+/*
+ * Insert an sk_buff at the start of a list.
+ *
+ * The "__skb_xxxx()" functions are the non-atomic ones that
+ * can only be called with interrupts disabled.
+ */
+
+extern __inline__ void __skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
+{
+ struct sk_buff *prev, *next;
+
+ newsk->list = list;
+ list->qlen++;
+ prev = (struct sk_buff *)list;
+ next = prev->next;
+ newsk->next = next;
+ newsk->prev = prev;
+ next->prev = newsk;
+ prev->next = newsk;
+}
+
+extern spinlock_t skb_queue_lock;
+
+extern __inline__ void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&skb_queue_lock, flags);
+ __skb_queue_head(list, newsk);
+ spin_unlock_irqrestore(&skb_queue_lock, flags);
+}
+
+/*
+ * Insert an sk_buff at the end of a list.
+ */
+
+extern __inline__ void __skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
+{
+ struct sk_buff *prev, *next;
+
+ newsk->list = list;
+ list->qlen++;
+ next = (struct sk_buff *)list;
+ prev = next->prev;
+ newsk->next = next;
+ newsk->prev = prev;
+ next->prev = newsk;
+ prev->next = newsk;
+}
+
+extern __inline__ void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&skb_queue_lock, flags);
+ __skb_queue_tail(list, newsk);
+ spin_unlock_irqrestore(&skb_queue_lock, flags);
+}
+
+/*
+ * Remove an sk_buff from a list.
+ */
+
+extern __inline__ struct sk_buff *__skb_dequeue(struct sk_buff_head *list)
+{
+ struct sk_buff *next, *prev, *result;
+
+ prev = (struct sk_buff *) list;
+ next = prev->next;
+ result = NULL;
+ if (next != prev) {
+ result = next;
+ next = next->next;
+ list->qlen--;
+ next->prev = prev;
+ prev->next = next;
+ result->next = NULL;
+ result->prev = NULL;
+ result->list = NULL;
+ }
+ return result;
+}
+
+extern __inline__ struct sk_buff *skb_dequeue(struct sk_buff_head *list)
+{
+ long flags;
+ struct sk_buff *result;
+
+ spin_lock_irqsave(&skb_queue_lock, flags);
+ result = __skb_dequeue(list);
+ spin_unlock_irqrestore(&skb_queue_lock, flags);
+ return result;
+}
+
+/*
+ * Insert a packet on a list.
+ */
+
+extern __inline__ void __skb_insert(struct sk_buff *newsk,
+ struct sk_buff * prev, struct sk_buff *next,
+ struct sk_buff_head * list)
+{
+ newsk->next = next;
+ newsk->prev = prev;
+ next->prev = newsk;
+ prev->next = newsk;
+ newsk->list = list;
+ list->qlen++;
+}
+
+/*
+ * Place a packet before a given packet in a list
+ */
+extern __inline__ void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&skb_queue_lock, flags);
+ __skb_insert(newsk, old->prev, old, old->list);
+ spin_unlock_irqrestore(&skb_queue_lock, flags);
+}
+
+/*
+ * Place a packet after a given packet in a list.
+ */
+
+extern __inline__ void __skb_append(struct sk_buff *old, struct sk_buff *newsk)
+{
+ __skb_insert(newsk, old, old->next, old->list);
+}
+
+extern __inline__ void skb_append(struct sk_buff *old, struct sk_buff *newsk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&skb_queue_lock, flags);
+ __skb_append(old, newsk);
+ spin_unlock_irqrestore(&skb_queue_lock, flags);
+}
+
+/*
+ * remove sk_buff from list. _Must_ be called atomically, and with
+ * the list known..
+ */
+extern __inline__ void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list)
+{
+ struct sk_buff * next, * prev;
+
+ list->qlen--;
+ next = skb->next;
+ prev = skb->prev;
+ skb->next = NULL;
+ skb->prev = NULL;
+ skb->list = NULL;
+ next->prev = prev;
+ prev->next = next;
+}
+
+/*
+ * Remove an sk_buff from its list. Works even without knowing the list it
+ * is sitting on, which can be handy at times. It also means that THE LIST
+ * MUST EXIST when you unlink. Thus a list must have its contents unlinked
+ * _FIRST_.
+ */
+
+extern __inline__ void skb_unlink(struct sk_buff *skb)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&skb_queue_lock, flags);
+ if(skb->list)
+ __skb_unlink(skb, skb->list);
+ spin_unlock_irqrestore(&skb_queue_lock, flags);
+}
+
+/* XXX: more streamlined implementation */
+extern __inline__ struct sk_buff *__skb_dequeue_tail(struct sk_buff_head *list)
+{
+ struct sk_buff *skb = skb_peek_tail(list);
+ if (skb)
+ __skb_unlink(skb, list);
+ return skb;
+}
+
+extern __inline__ struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list)
+{
+ long flags;
+ struct sk_buff *result;
+
+ spin_lock_irqsave(&skb_queue_lock, flags);
+ result = __skb_dequeue_tail(list);
+ spin_unlock_irqrestore(&skb_queue_lock, flags);
+ return result;
+}
+
+/*
+ * Add data to an sk_buff
+ */
+
+extern __inline__ unsigned char *__skb_put(struct sk_buff *skb, unsigned int len)
+{
+ unsigned char *tmp=skb->tail;
+ skb->tail+=len;
+ skb->len+=len;
+ return tmp;
+}
+
+extern __inline__ unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
+{
+ unsigned char *tmp=skb->tail;
+ skb->tail+=len;
+ skb->len+=len;
+ if(skb->tail>skb->end)
+ {
+ __label__ here;
+ skb_over_panic(skb, len, &&here);
+here: ;
+ }
+ return tmp;
+}
+
+extern __inline__ unsigned char *__skb_push(struct sk_buff *skb, unsigned int len)
+{
+ skb->data-=len;
+ skb->len+=len;
+ return skb->data;
+}
+
+extern __inline__ unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
+{
+ skb->data-=len;
+ skb->len+=len;
+ if(skb->data<skb->head)
+ {
+ __label__ here;
+ skb_under_panic(skb, len, &&here);
+here: ;
+ }
+ return skb->data;
+}
+
+extern __inline__ char *__skb_pull(struct sk_buff *skb, unsigned int len)
+{
+ skb->len-=len;
+ return skb->data+=len;
+}
+
+extern __inline__ unsigned char * skb_pull(struct sk_buff *skb, unsigned int len)
+{
+ if (len > skb->len)
+ return NULL;
+ return __skb_pull(skb,len);
+}
+
+extern __inline__ int skb_headroom(struct sk_buff *skb)
+{
+ return skb->data-skb->head;
+}
+
+extern __inline__ int skb_tailroom(struct sk_buff *skb)
+{
+ return skb->end-skb->tail;
+}
+
+extern __inline__ void skb_reserve(struct sk_buff *skb, unsigned int len)
+{
+ skb->data+=len;
+ skb->tail+=len;
+}
+
+extern __inline__ void __skb_trim(struct sk_buff *skb, unsigned int len)
+{
+ skb->len = len;
+ skb->tail = skb->data+len;
+}
+
+extern __inline__ void skb_trim(struct sk_buff *skb, unsigned int len)
+{
+ if (skb->len > len) {
+ __skb_trim(skb, len);
+ }
+}
+
+extern __inline__ void skb_orphan(struct sk_buff *skb)
+{
+ if (skb->destructor)
+ skb->destructor(skb);
+ skb->destructor = NULL;
+ skb->sk = NULL;
+}
+
+extern __inline__ void skb_queue_purge(struct sk_buff_head *list)
+{
+ struct sk_buff *skb;
+ while ((skb=skb_dequeue(list))!=NULL)
+ kfree_skb(skb);
+}
+
+extern __inline__ struct sk_buff *dev_alloc_skb(unsigned int length)
+{
+ struct sk_buff *skb;
+
+ skb = alloc_skb(length+16, GFP_ATOMIC);
+ if (skb)
+ skb_reserve(skb,16);
+ return skb;
+}
+
+extern __inline__ struct sk_buff *
+skb_cow(struct sk_buff *skb, unsigned int headroom)
+{
+ headroom = (headroom+15)&~15;
+
+ if ((unsigned)skb_headroom(skb) < headroom || skb_cloned(skb)) {
+ struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom);
+ kfree_skb(skb);
+ skb = skb2;
+ }
+ return skb;
+}
+
+extern struct sk_buff * skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err);
+extern unsigned int datagram_poll(struct file *file, struct socket *sock, struct poll_table_struct *wait);
+extern int skb_copy_datagram(struct sk_buff *from, int offset, char *to,int size);
+extern int skb_copy_datagram_iovec(struct sk_buff *from, int offset, struct iovec *to,int size);
+extern void skb_free_datagram(struct sock * sk, struct sk_buff *skb);
+
+extern void skb_init(void);
+extern void skb_add_mtu(int mtu);
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_SKBUFF_H */
diff --git a/pfinet/linux-src/include/linux/slab.h b/pfinet/linux-src/include/linux/slab.h
new file mode 100644
index 00000000..0b46d3b7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/slab.h
@@ -0,0 +1,71 @@
+/*
+ * linux/mm/slab.h
+ * Written by Mark Hemment, 1996.
+ * (markhe@nextd.demon.co.uk)
+ */
+
+#if !defined(_LINUX_SLAB_H)
+#define _LINUX_SLAB_H
+
+#if defined(__KERNEL__)
+
+typedef struct kmem_cache_s kmem_cache_t;
+
+#include <linux/mm.h>
+#include <asm/cache.h>
+
+/* flags for kmem_cache_alloc() */
+#define SLAB_BUFFER GFP_BUFFER
+#define SLAB_ATOMIC GFP_ATOMIC
+#define SLAB_USER GFP_USER
+#define SLAB_KERNEL GFP_KERNEL
+#define SLAB_NFS GFP_NFS
+#define SLAB_DMA GFP_DMA
+
+#define SLAB_LEVEL_MASK 0x0000007fUL
+#define SLAB_NO_GROW 0x00001000UL /* don't grow a cache */
+
+/* flags to pass to kmem_cache_create().
+ * The first 3 are only valid when the allocator as been build
+ * SLAB_DEBUG_SUPPORT.
+ */
+#define SLAB_DEBUG_FREE 0x00000100UL /* Peform (expensive) checks on free */
+#define SLAB_DEBUG_INITIAL 0x00000200UL /* Call constructor (as verifier) */
+#define SLAB_RED_ZONE 0x00000400UL /* Red zone objs in a cache */
+#define SLAB_POISON 0x00000800UL /* Poison objects */
+#define SLAB_NO_REAP 0x00001000UL /* never reap from the cache */
+#define SLAB_HWCACHE_ALIGN 0x00002000UL /* align objs on a h/w cache lines */
+#if 0
+#define SLAB_HIGH_PACK 0x00004000UL /* XXX */
+#endif
+
+/* flags passed to a constructor func */
+#define SLAB_CTOR_CONSTRUCTOR 0x001UL /* if not set, then deconstructor */
+#define SLAB_CTOR_ATOMIC 0x002UL /* tell constructor it can't sleep */
+#define SLAB_CTOR_VERIFY 0x004UL /* tell constructor it's a verify call */
+
+/* prototypes */
+extern long kmem_cache_init(long, long);
+extern void kmem_cache_sizes_init(void);
+extern kmem_cache_t *kmem_find_general_cachep(size_t);
+extern kmem_cache_t *kmem_cache_create(const char *, size_t, size_t, unsigned long,
+ void (*)(void *, kmem_cache_t *, unsigned long),
+ void (*)(void *, kmem_cache_t *, unsigned long));
+extern int kmem_cache_shrink(kmem_cache_t *);
+extern void *kmem_cache_alloc(kmem_cache_t *, int);
+extern void kmem_cache_free(kmem_cache_t *, void *);
+
+extern void *kmalloc(size_t, int);
+extern void kfree(const void *);
+extern void kfree_s(const void *, size_t);
+
+extern void kmem_cache_reap(int);
+extern int get_slabinfo(char *);
+
+/* System wide caches */
+extern kmem_cache_t *vm_area_cachep;
+extern kmem_cache_t *mm_cachep;
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SLAB_H */
diff --git a/pfinet/linux-src/include/linux/smb.h b/pfinet/linux-src/include/linux/smb.h
new file mode 100644
index 00000000..852d5b0d
--- /dev/null
+++ b/pfinet/linux-src/include/linux/smb.h
@@ -0,0 +1,123 @@
+/*
+ * smb.h
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_SMB_H
+#define _LINUX_SMB_H
+
+#include <linux/types.h>
+
+enum smb_protocol {
+ SMB_PROTOCOL_NONE,
+ SMB_PROTOCOL_CORE,
+ SMB_PROTOCOL_COREPLUS,
+ SMB_PROTOCOL_LANMAN1,
+ SMB_PROTOCOL_LANMAN2,
+ SMB_PROTOCOL_NT1
+};
+
+enum smb_case_hndl {
+ SMB_CASE_DEFAULT,
+ SMB_CASE_LOWER,
+ SMB_CASE_UPPER
+};
+
+struct smb_dskattr {
+ __u16 total;
+ __u16 allocblocks;
+ __u16 blocksize;
+ __u16 free;
+};
+
+struct smb_conn_opt {
+
+ /* The socket */
+ unsigned int fd;
+
+ enum smb_protocol protocol;
+ enum smb_case_hndl case_handling;
+
+ /* Connection-Options */
+
+ __u32 max_xmit;
+ __u16 server_uid;
+ __u16 tid;
+
+ /* The following are LANMAN 1.0 options */
+ __u16 secmode;
+ __u16 maxmux;
+ __u16 maxvcs;
+ __u16 rawmode;
+ __u32 sesskey;
+
+ /* The following are NT LM 0.12 options */
+ __u32 maxraw;
+ __u32 capabilities;
+ __s16 serverzone;
+};
+
+#ifdef __KERNEL__
+
+#define SMB_MAXNAMELEN 255
+#define SMB_MAXPATHLEN 1024
+
+/*
+ * Contains all relevant data on a SMB networked file.
+ */
+struct smb_fattr {
+
+ __u16 attr;
+
+ unsigned long f_ino;
+ umode_t f_mode;
+ nlink_t f_nlink;
+ uid_t f_uid;
+ gid_t f_gid;
+ kdev_t f_rdev;
+ off_t f_size;
+ time_t f_atime;
+ time_t f_mtime;
+ time_t f_ctime;
+ unsigned long f_blksize;
+ unsigned long f_blocks;
+};
+
+struct smb_dirent {
+ struct smb_fattr attr;
+
+ int f_pos;
+ int len;
+ __u8 name[SMB_MAXNAMELEN];
+};
+
+enum smb_conn_state {
+ CONN_VALID, /* everything's fine */
+ CONN_INVALID, /* Something went wrong, but did not
+ try to reconnect yet. */
+ CONN_RETRIED /* Tried a reconnection, but was refused */
+};
+
+/*
+ * The readdir cache size controls how many directory entries are cached.
+ */
+#define SMB_READDIR_CACHE_SIZE 64
+
+#define SMB_SUPER_MAGIC 0x517B
+
+#define SMB_SERVER(inode) (&(inode->i_sb->u.smbfs_sb))
+#define SMB_INOP(inode) (&(inode->u.smbfs_i))
+
+#define SMB_HEADER_LEN 37 /* includes everything up to, but not
+ * including smb_bcc */
+#define SMB_DEF_MAX_XMIT 32768
+#define SMB_INITIAL_PACKET_SIZE 4000
+
+/* Allocate max. 1 page */
+#define TRANS2_MAX_TRANSFER (4096-17)
+
+#endif
+#endif
diff --git a/pfinet/linux-src/include/linux/smb_fs.h b/pfinet/linux-src/include/linux/smb_fs.h
new file mode 100644
index 00000000..11dfce7d
--- /dev/null
+++ b/pfinet/linux-src/include/linux/smb_fs.h
@@ -0,0 +1,240 @@
+/*
+ * smb_fs.h
+ *
+ * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_SMB_FS_H
+#define _LINUX_SMB_FS_H
+
+#include <linux/smb.h>
+
+/*
+ * ioctl commands
+ */
+#define SMB_IOC_GETMOUNTUID _IOR('u', 1, __kernel_uid_t)
+#define SMB_IOC_NEWCONN _IOW('u', 2, struct smb_conn_opt)
+
+#ifdef __KERNEL__
+
+#include <asm/unaligned.h>
+
+#define WVAL(buf,pos) \
+(le16_to_cpu(get_unaligned((__u16 *)((__u8 *)(buf) + (pos)))))
+#define DVAL(buf,pos) \
+(le32_to_cpu(get_unaligned((__u32 *)((__u8 *)(buf) + (pos)))))
+#define WSET(buf,pos,val) \
+put_unaligned(cpu_to_le16((__u16)(val)), (__u16 *)((__u8 *)(buf) + (pos)))
+#define DSET(buf,pos,val) \
+put_unaligned(cpu_to_le32((__u32)(val)), (__u32 *)((__u8 *)(buf) + (pos)))
+
+/* where to find the base of the SMB packet proper */
+#define smb_base(buf) ((__u8 *)(((__u8 *)(buf))+4))
+
+#include <linux/vmalloc.h>
+
+#ifdef DEBUG_SMB_MALLOC
+
+extern int smb_malloced;
+extern int smb_current_vmalloced;
+
+static inline void *
+smb_vmalloc(unsigned int size)
+{
+ smb_malloced += 1;
+ smb_current_vmalloced += 1;
+ return vmalloc(size);
+}
+
+static inline void
+smb_vfree(void *obj)
+{
+ smb_current_vmalloced -= 1;
+ vfree(obj);
+}
+
+#else /* DEBUG_SMB_MALLOC */
+
+#define smb_kmalloc(s,p) kmalloc(s,p)
+#define smb_kfree_s(o,s) kfree_s(o,s)
+#define smb_vmalloc(s) vmalloc(s)
+#define smb_vfree(o) vfree(o)
+
+#endif /* DEBUG_SMB_MALLOC */
+
+/*
+ * Flags for the in-memory inode
+ */
+#define SMB_F_CACHEVALID 0x01 /* directory cache valid */
+#define SMB_F_LOCALWRITE 0x02 /* file modified locally */
+
+/*
+ * Bug fix flags
+ */
+#define SMB_FIX_WIN95 0x0001 /* Win 95 server */
+#define SMB_FIX_OLDATTR 0x0002 /* Use core getattr (Win 95 speedup) */
+#define SMB_FIX_DIRATTR 0x0004 /* Use find_first for getattr */
+
+
+/* NT1 protocol capability bits */
+#define SMB_CAP_RAW_MODE 0x0001
+#define SMB_CAP_MPX_MODE 0x0002
+#define SMB_CAP_UNICODE 0x0004
+#define SMB_CAP_LARGE_FILES 0x0008
+#define SMB_CAP_NT_SMBS 0x0010
+#define SMB_CAP_RPC_REMOTE_APIS 0x0020
+#define SMB_CAP_STATUS32 0x0040
+#define SMB_CAP_LEVEL_II_OPLOCKS 0x0080
+#define SMB_CAP_LOCK_AND_READ 0x0100
+#define SMB_CAP_NT_FIND 0x0200
+#define SMB_CAP_DFS 0x1000
+#define SMB_CAP_LARGE_READX 0x4000
+
+
+/* linux/fs/smbfs/mmap.c */
+int smb_mmap(struct file *, struct vm_area_struct *);
+
+/* linux/fs/smbfs/file.c */
+extern struct inode_operations smb_file_inode_operations;
+
+/* linux/fs/smbfs/dir.c */
+extern struct inode_operations smb_dir_inode_operations;
+void smb_renew_times(struct dentry *);
+
+/* linux/fs/smbfs/ioctl.c */
+int smb_ioctl (struct inode *, struct file *, unsigned int, unsigned long);
+
+/* linux/fs/smbfs/inode.c */
+struct super_block *smb_read_super(struct super_block *, void *, int);
+void smb_get_inode_attr(struct inode *, struct smb_fattr *);
+void smb_invalidate_inodes(struct smb_sb_info *);
+int smb_revalidate_inode(struct dentry *);
+int smb_notify_change(struct dentry *, struct iattr *);
+unsigned long smb_invent_inos(unsigned long);
+struct inode *smb_iget(struct super_block *, struct smb_fattr *);
+extern int init_smb_fs(void);
+
+/* linux/fs/smbfs/proc.c */
+__u32 smb_len(unsigned char *);
+__u8 *smb_encode_smb_length(__u8 *, __u32);
+__u8 *smb_setup_header(struct smb_sb_info *, __u8, __u16, __u16);
+int smb_get_rsize(struct smb_sb_info *);
+int smb_get_wsize(struct smb_sb_info *);
+int smb_newconn(struct smb_sb_info *, struct smb_conn_opt *);
+int smb_errno(struct smb_sb_info *);
+int smb_close(struct inode *);
+void smb_close_dentry(struct dentry *);
+int smb_close_fileid(struct dentry *, __u16);
+int smb_open(struct dentry *, int);
+int smb_proc_read(struct dentry *, off_t, int, char *);
+int smb_proc_write(struct dentry *, off_t, int, const char *);
+int smb_proc_create(struct dentry *, __u16, time_t, __u16 *);
+int smb_proc_mv(struct dentry *, struct dentry *);
+int smb_proc_mkdir(struct dentry *);
+int smb_proc_rmdir(struct dentry *);
+int smb_proc_unlink(struct dentry *);
+int smb_proc_readdir(struct dentry *, int, void *);
+int smb_proc_getattr(struct dentry *, struct smb_fattr *);
+int smb_proc_setattr(struct dentry *, struct smb_fattr *);
+int smb_proc_settime(struct dentry *, struct smb_fattr *);
+int smb_proc_dskattr(struct super_block *, struct statfs *);
+int smb_proc_reconnect(struct smb_sb_info *);
+int smb_proc_connect(struct smb_sb_info *);
+int smb_proc_disconnect(struct smb_sb_info *);
+int smb_proc_trunc(struct smb_sb_info *, __u16, __u32);
+void smb_init_root_dirent(struct smb_sb_info *, struct smb_fattr *);
+
+static inline int
+smb_is_open(struct inode *i)
+{
+ return (i->u.smbfs_i.open == SMB_SERVER(i)->generation);
+}
+
+/* linux/fs/smbfs/sock.c */
+int smb_round_length(int);
+int smb_valid_socket(struct inode *);
+void smb_close_socket(struct smb_sb_info *);
+int smb_release(struct smb_sb_info *server);
+int smb_connect(struct smb_sb_info *server);
+int smb_request(struct smb_sb_info *server);
+int smb_request_read_raw(struct smb_sb_info *, unsigned char *, int);
+int smb_request_write_raw(struct smb_sb_info *, unsigned const char *, int);
+int smb_catch_keepalive(struct smb_sb_info *server);
+int smb_dont_catch_keepalive(struct smb_sb_info *server);
+int smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command,
+ int ldata, unsigned char *data,
+ int lparam, unsigned char *param,
+ int *lrdata, unsigned char **rdata,
+ int *lrparam, unsigned char **rparam);
+
+/* fs/smbfs/cache.c */
+
+/*
+ * The cache index describes the pages mapped starting
+ * at offset PAGE_SIZE. We keep only a minimal amount
+ * of information here.
+ */
+struct cache_index {
+ unsigned short num_entries;
+ unsigned short space;
+ struct cache_block * block;
+};
+
+#define NINDEX (PAGE_SIZE-64)/sizeof(struct cache_index)
+/*
+ * The cache head is mapped as the page at offset 0.
+ */
+struct cache_head {
+ int valid;
+ int status; /* error code or 0 */
+ int entries; /* total entries */
+ int pages; /* number of data pages */
+ int idx; /* index of current data page */
+ struct cache_index index[NINDEX];
+};
+
+/*
+ * An array of cache_entry structures holds information
+ * for each object in the cache_block.
+ */
+struct cache_entry {
+ ino_t ino;
+ unsigned short namelen;
+ unsigned short offset;
+};
+
+/*
+ * The cache blocks hold the actual data. The entry table grows up
+ * while the names grow down, and we have space until they meet.
+ */
+struct cache_block {
+ union {
+ struct cache_entry table[1];
+ char names[PAGE_SIZE];
+ } cb_data;
+};
+
+/*
+ * To return an entry, we can pass a reference to the
+ * name instead of having to copy it.
+ */
+struct cache_dirent {
+ ino_t ino;
+ unsigned long pos;
+ int len;
+ char * name;
+};
+
+struct cache_head * smb_get_dircache(struct dentry *);
+void smb_init_dircache(struct cache_head *);
+void smb_free_dircache(struct cache_head *);
+int smb_refill_dircache(struct cache_head *, struct dentry *);
+void smb_add_to_cache(struct cache_head *, struct cache_dirent *, off_t);
+int smb_find_in_cache(struct cache_head *, off_t, struct cache_dirent *);
+void smb_invalid_dir_cache(struct inode *);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SMB_FS_H */
diff --git a/pfinet/linux-src/include/linux/smb_fs_i.h b/pfinet/linux-src/include/linux/smb_fs_i.h
new file mode 100644
index 00000000..4aea02c3
--- /dev/null
+++ b/pfinet/linux-src/include/linux/smb_fs_i.h
@@ -0,0 +1,35 @@
+/*
+ * smb_fs_i.h
+ *
+ * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_SMB_FS_I
+#define _LINUX_SMB_FS_I
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+
+/*
+ * smb fs inode data (in memory only)
+ */
+struct smb_inode_info {
+
+ /*
+ * file handles are local to a connection. A file is open if
+ * (open == generation).
+ */
+ unsigned int open; /* open generation */
+ __u16 fileid; /* What id to handle a file with? */
+ __u16 attr; /* Attribute fields, DOS value */
+
+ __u16 access; /* Access mode */
+ __u16 cache_valid; /* dircache valid? */
+ unsigned long oldmtime; /* last time refreshed */
+ unsigned long closed; /* timestamp when closed */
+};
+
+#endif
+#endif
diff --git a/pfinet/linux-src/include/linux/smb_fs_sb.h b/pfinet/linux-src/include/linux/smb_fs_sb.h
new file mode 100644
index 00000000..cedbb5ab
--- /dev/null
+++ b/pfinet/linux-src/include/linux/smb_fs_sb.h
@@ -0,0 +1,50 @@
+/*
+ * smb_fs_sb.h
+ *
+ * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ */
+
+#ifndef _SMB_FS_SB
+#define _SMB_FS_SB
+
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include <linux/smb.h>
+
+/* Get the server for the specified dentry */
+#define server_from_dentry(dentry) &dentry->d_sb->u.smbfs_sb
+#define SB_of(server) ((struct super_block *) ((char *)(server) - \
+ (unsigned long)(&((struct super_block *)0)->u.smbfs_sb)))
+
+struct smb_sb_info {
+ enum smb_conn_state state;
+ struct file * sock_file;
+
+ struct smb_mount_data *mnt;
+ unsigned char *temp_buf;
+
+ /* Connections are counted. Each time a new socket arrives,
+ * generation is incremented.
+ */
+ unsigned int generation;
+ pid_t conn_pid;
+ struct smb_conn_opt opt;
+
+ struct semaphore sem;
+ struct wait_queue * wait;
+
+ __u32 packet_size;
+ unsigned char * packet;
+ unsigned short rcls; /* The error codes we received */
+ unsigned short err;
+
+ /* We use our on data_ready callback, but need the original one */
+ void *data_ready;
+};
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/smb_mount.h b/pfinet/linux-src/include/linux/smb_mount.h
new file mode 100644
index 00000000..886d945d
--- /dev/null
+++ b/pfinet/linux-src/include/linux/smb_mount.h
@@ -0,0 +1,25 @@
+/*
+ * smb_mount.h
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ */
+
+#ifndef _LINUX_SMB_MOUNT_H
+#define _LINUX_SMB_MOUNT_H
+
+#include <linux/types.h>
+
+#define SMB_MOUNT_VERSION 6
+
+struct smb_mount_data {
+ int version;
+ __kernel_uid_t mounted_uid; /* Who may umount() this filesystem? */
+ __kernel_uid_t uid;
+ __kernel_gid_t gid;
+ __kernel_mode_t file_mode;
+ __kernel_mode_t dir_mode;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/smbno.h b/pfinet/linux-src/include/linux/smbno.h
new file mode 100644
index 00000000..0f25e026
--- /dev/null
+++ b/pfinet/linux-src/include/linux/smbno.h
@@ -0,0 +1,278 @@
+#ifndef _SMBNO_H_
+#define _SMBNO_H_
+
+/* these define the attribute byte as seen by DOS */
+#define aRONLY (1L<<0)
+#define aHIDDEN (1L<<1)
+#define aSYSTEM (1L<<2)
+#define aVOLID (1L<<3)
+#define aDIR (1L<<4)
+#define aARCH (1L<<5)
+
+/* error classes */
+#define SUCCESS 0 /* The request was successful. */
+#define ERRDOS 0x01 /* Error is from the core DOS operating system set. */
+#define ERRSRV 0x02 /* Error is generated by the server network file manager.*/
+#define ERRHRD 0x03 /* Error is an hardware error. */
+#define ERRCMD 0xFF /* Command was not in the "SMB" format. */
+
+/* SMB X/Open error codes for the ERRdos error class */
+
+#define ERRbadfunc 1 /* Invalid function (or system call) */
+#define ERRbadfile 2 /* File not found (pathname error) */
+#define ERRbadpath 3 /* Directory not found */
+#define ERRnofids 4 /* Too many open files */
+#define ERRnoaccess 5 /* Access denied */
+#define ERRbadfid 6 /* Invalid fid */
+#define ERRbadmcb 7 /* Memory control blocks destroyed */
+#define ERRnomem 8 /* Out of memory */
+#define ERRbadmem 9 /* Invalid memory block address */
+#define ERRbadenv 10 /* Invalid environment */
+#define ERRbadformat 11 /* Invalid format */
+#define ERRbadaccess 12 /* Invalid open mode */
+#define ERRbaddata 13 /* Invalid data (only from ioctl call) */
+#define ERRres 14 /* reserved */
+#define ERRbaddrive 15 /* Invalid drive */
+#define ERRremcd 16 /* Attempt to delete current directory */
+#define ERRdiffdevice 17 /* rename/move across different filesystems */
+#define ERRnofiles 18 /* no more files found in file search */
+#define ERRbadshare 32 /* Share mode on file conflict with open mode */
+#define ERRlock 33 /* Lock request conflicts with existing lock */
+#define ERRfilexists 80 /* File in operation already exists */
+#define ERRundocumented1 123 /* Invalid name?? e.g. .tmp* */
+#define ERRbadpipe 230 /* Named pipe invalid */
+#define ERRpipebusy 231 /* All instances of pipe are busy */
+#define ERRpipeclosing 232 /* named pipe close in progress */
+#define ERRnotconnected 233 /* No process on other end of named pipe */
+#define ERRmoredata 234 /* More data to be returned */
+#define ERROR_EAS_DIDNT_FIT 275 /* Extended attributes didn't fit */
+#define ERROR_EAS_NOT_SUPPORTED 282 /* Extended attributes not supported */
+
+/* Error codes for the ERRSRV class */
+
+#define ERRerror 1 /* Non specific error code */
+#define ERRbadpw 2 /* Bad password */
+#define ERRbadtype 3 /* reserved */
+#define ERRaccess 4 /* No permissions to do the requested operation */
+#define ERRinvnid 5 /* tid invalid */
+#define ERRinvnetname 6 /* Invalid servername */
+#define ERRinvdevice 7 /* Invalid device */
+#define ERRqfull 49 /* Print queue full */
+#define ERRqtoobig 50 /* Queued item too big */
+#define ERRinvpfid 52 /* Invalid print file in smb_fid */
+#define ERRsmbcmd 64 /* Unrecognised command */
+#define ERRsrverror 65 /* smb server internal error */
+#define ERRfilespecs 67 /* fid and pathname invalid combination */
+#define ERRbadlink 68 /* reserved */
+#define ERRbadpermits 69 /* Access specified for a file is not valid */
+#define ERRbadpid 70 /* reserved */
+#define ERRsetattrmode 71 /* attribute mode invalid */
+#define ERRpaused 81 /* Message server paused */
+#define ERRmsgoff 82 /* Not receiving messages */
+#define ERRnoroom 83 /* No room for message */
+#define ERRrmuns 87 /* too many remote usernames */
+#define ERRtimeout 88 /* operation timed out */
+#define ERRnoresource 89 /* No resources currently available for request. */
+#define ERRtoomanyuids 90 /* too many userids */
+#define ERRbaduid 91 /* bad userid */
+#define ERRuseMPX 250 /* temporarily unable to use raw mode, use MPX mode */
+#define ERRuseSTD 251 /* temporarily unable to use raw mode, use std.mode */
+#define ERRcontMPX 252 /* resume MPX mode */
+#define ERRbadPW /* reserved */
+#define ERRnosupport 0xFFFF
+
+/* Error codes for the ERRHRD class */
+
+#define ERRnowrite 19 /* read only media */
+#define ERRbadunit 20 /* Unknown device */
+#define ERRnotready 21 /* Drive not ready */
+#define ERRbadcmd 22 /* Unknown command */
+#define ERRdata 23 /* Data (CRC) error */
+#define ERRbadreq 24 /* Bad request structure length */
+#define ERRseek 25
+#define ERRbadmedia 26
+#define ERRbadsector 27
+#define ERRnopaper 28
+#define ERRwrite 29 /* write fault */
+#define ERRread 30 /* read fault */
+#define ERRgeneral 31 /* General hardware failure */
+#define ERRwrongdisk 34
+#define ERRFCBunavail 35
+#define ERRsharebufexc 36 /* share buffer exceeded */
+#define ERRdiskfull 39
+
+/*
+ * Access modes when opening a file
+ */
+#define SMB_ACCMASK 0x0003
+#define SMB_O_RDONLY 0x0000
+#define SMB_O_WRONLY 0x0001
+#define SMB_O_RDWR 0x0002
+
+/* offsets into message for common items */
+#define smb_com 8
+#define smb_rcls 9
+#define smb_reh 10
+#define smb_err 11
+#define smb_flg 13
+#define smb_flg2 14
+#define smb_reb 13
+#define smb_tid 28
+#define smb_pid 30
+#define smb_uid 32
+#define smb_mid 34
+#define smb_wct 36
+#define smb_vwv 37
+#define smb_vwv0 37
+#define smb_vwv1 39
+#define smb_vwv2 41
+#define smb_vwv3 43
+#define smb_vwv4 45
+#define smb_vwv5 47
+#define smb_vwv6 49
+#define smb_vwv7 51
+#define smb_vwv8 53
+#define smb_vwv9 55
+#define smb_vwv10 57
+#define smb_vwv11 59
+#define smb_vwv12 61
+#define smb_vwv13 63
+#define smb_vwv14 65
+
+/* these are the trans2 sub fields for primary requests */
+#define smb_tpscnt smb_vwv0
+#define smb_tdscnt smb_vwv1
+#define smb_mprcnt smb_vwv2
+#define smb_mdrcnt smb_vwv3
+#define smb_msrcnt smb_vwv4
+#define smb_flags smb_vwv5
+#define smb_timeout smb_vwv6
+#define smb_pscnt smb_vwv9
+#define smb_psoff smb_vwv10
+#define smb_dscnt smb_vwv11
+#define smb_dsoff smb_vwv12
+#define smb_suwcnt smb_vwv13
+#define smb_setup smb_vwv14
+#define smb_setup0 smb_setup
+#define smb_setup1 (smb_setup+2)
+#define smb_setup2 (smb_setup+4)
+
+/* these are for the secondary requests */
+#define smb_spscnt smb_vwv2
+#define smb_spsoff smb_vwv3
+#define smb_spsdisp smb_vwv4
+#define smb_sdscnt smb_vwv5
+#define smb_sdsoff smb_vwv6
+#define smb_sdsdisp smb_vwv7
+#define smb_sfid smb_vwv8
+
+/* and these for responses */
+#define smb_tprcnt smb_vwv0
+#define smb_tdrcnt smb_vwv1
+#define smb_prcnt smb_vwv3
+#define smb_proff smb_vwv4
+#define smb_prdisp smb_vwv5
+#define smb_drcnt smb_vwv6
+#define smb_droff smb_vwv7
+#define smb_drdisp smb_vwv8
+
+/* the complete */
+#define SMBmkdir 0x00 /* create directory */
+#define SMBrmdir 0x01 /* delete directory */
+#define SMBopen 0x02 /* open file */
+#define SMBcreate 0x03 /* create file */
+#define SMBclose 0x04 /* close file */
+#define SMBflush 0x05 /* flush file */
+#define SMBunlink 0x06 /* delete file */
+#define SMBmv 0x07 /* rename file */
+#define SMBgetatr 0x08 /* get file attributes */
+#define SMBsetatr 0x09 /* set file attributes */
+#define SMBread 0x0A /* read from file */
+#define SMBwrite 0x0B /* write to file */
+#define SMBlock 0x0C /* lock byte range */
+#define SMBunlock 0x0D /* unlock byte range */
+#define SMBctemp 0x0E /* create temporary file */
+#define SMBmknew 0x0F /* make new file */
+#define SMBchkpth 0x10 /* check directory path */
+#define SMBexit 0x11 /* process exit */
+#define SMBlseek 0x12 /* seek */
+#define SMBtcon 0x70 /* tree connect */
+#define SMBtconX 0x75 /* tree connect and X*/
+#define SMBtdis 0x71 /* tree disconnect */
+#define SMBnegprot 0x72 /* negotiate protocol */
+#define SMBdskattr 0x80 /* get disk attributes */
+#define SMBsearch 0x81 /* search directory */
+#define SMBsplopen 0xC0 /* open print spool file */
+#define SMBsplwr 0xC1 /* write to print spool file */
+#define SMBsplclose 0xC2 /* close print spool file */
+#define SMBsplretq 0xC3 /* return print queue */
+#define SMBsends 0xD0 /* send single block message */
+#define SMBsendb 0xD1 /* send broadcast message */
+#define SMBfwdname 0xD2 /* forward user name */
+#define SMBcancelf 0xD3 /* cancel forward */
+#define SMBgetmac 0xD4 /* get machine name */
+#define SMBsendstrt 0xD5 /* send start of multi-block message */
+#define SMBsendend 0xD6 /* send end of multi-block message */
+#define SMBsendtxt 0xD7 /* send text of multi-block message */
+
+/* Core+ protocol */
+#define SMBlockread 0x13 /* Lock a range and read */
+#define SMBwriteunlock 0x14 /* Unlock a range then write */
+#define SMBreadbraw 0x1a /* read a block of data with no smb header */
+#define SMBwritebraw 0x1d /* write a block of data with no smb header */
+#define SMBwritec 0x20 /* secondary write request */
+#define SMBwriteclose 0x2c /* write a file then close it */
+
+/* dos extended protocol */
+#define SMBreadBraw 0x1A /* read block raw */
+#define SMBreadBmpx 0x1B /* read block multiplexed */
+#define SMBreadBs 0x1C /* read block (secondary response) */
+#define SMBwriteBraw 0x1D /* write block raw */
+#define SMBwriteBmpx 0x1E /* write block multiplexed */
+#define SMBwriteBs 0x1F /* write block (secondary request) */
+#define SMBwriteC 0x20 /* write complete response */
+#define SMBsetattrE 0x22 /* set file attributes expanded */
+#define SMBgetattrE 0x23 /* get file attributes expanded */
+#define SMBlockingX 0x24 /* lock/unlock byte ranges and X */
+#define SMBtrans 0x25 /* transaction - name, bytes in/out */
+#define SMBtranss 0x26 /* transaction (secondary request/response) */
+#define SMBioctl 0x27 /* IOCTL */
+#define SMBioctls 0x28 /* IOCTL (secondary request/response) */
+#define SMBcopy 0x29 /* copy */
+#define SMBmove 0x2A /* move */
+#define SMBecho 0x2B /* echo */
+#define SMBopenX 0x2D /* open and X */
+#define SMBreadX 0x2E /* read and X */
+#define SMBwriteX 0x2F /* write and X */
+#define SMBsesssetupX 0x73 /* Session Set Up & X (including User Logon) */
+#define SMBtconX 0x75 /* tree connect and X */
+#define SMBffirst 0x82 /* find first */
+#define SMBfunique 0x83 /* find unique */
+#define SMBfclose 0x84 /* find close */
+#define SMBinvalid 0xFE /* invalid command */
+
+
+/* Extended 2.0 protocol */
+#define SMBtrans2 0x32 /* TRANS2 protocol set */
+#define SMBtranss2 0x33 /* TRANS2 protocol set, secondary command */
+#define SMBfindclose 0x34 /* Terminate a TRANSACT2_FINDFIRST */
+#define SMBfindnclose 0x35 /* Terminate a TRANSACT2_FINDNOTIFYFIRST */
+#define SMBulogoffX 0x74 /* user logoff */
+
+/* these are the TRANS2 sub commands */
+#define TRANSACT2_OPEN 0
+#define TRANSACT2_FINDFIRST 1
+#define TRANSACT2_FINDNEXT 2
+#define TRANSACT2_QFSINFO 3
+#define TRANSACT2_SETFSINFO 4
+#define TRANSACT2_QPATHINFO 5
+#define TRANSACT2_SETPATHINFO 6
+#define TRANSACT2_QFILEINFO 7
+#define TRANSACT2_SETFILEINFO 8
+#define TRANSACT2_FSCTL 9
+#define TRANSACT2_IOCTL 10
+#define TRANSACT2_FINDNOTIFYFIRST 11
+#define TRANSACT2_FINDNOTIFYNEXT 12
+#define TRANSACT2_MKDIR 13
+
+#endif /* _SMBNO_H_ */
diff --git a/pfinet/linux-src/include/linux/smp.h b/pfinet/linux-src/include/linux/smp.h
new file mode 100644
index 00000000..eae12907
--- /dev/null
+++ b/pfinet/linux-src/include/linux/smp.h
@@ -0,0 +1,86 @@
+#ifndef __LINUX_SMP_H
+#define __LINUX_SMP_H
+
+/*
+ * Generic SMP support
+ * Alan Cox. <alan@redhat.com>
+ */
+
+#ifdef __SMP__
+
+#include <asm/smp.h>
+
+/*
+ * main cross-CPU interfaces, handles INIT, TLB flush, STOP, etc.
+ * (defined in asm header):
+ */
+
+/*
+ * stops all CPUs but the current one:
+ */
+extern void smp_send_stop(void);
+
+/*
+ * sends a 'reschedule' event to another CPU:
+ */
+extern void FASTCALL(smp_send_reschedule(int cpu));
+
+
+/*
+ * Boot processor call to load the other CPU's
+ */
+extern void smp_boot_cpus(void);
+
+/*
+ * Processor call in. Must hold processors until ..
+ */
+extern void smp_callin(void);
+
+/*
+ * Multiprocessors may now schedule
+ */
+extern void smp_commence(void);
+
+/*
+ * Call a function on all other processors
+ */
+extern int smp_call_function (void (*func) (void *info), void *info,
+ int retry, int wait);
+
+/*
+ * True once the per process idle is forked
+ */
+extern int smp_threads_ready;
+
+extern int smp_num_cpus;
+
+extern volatile unsigned long smp_msg_data;
+extern volatile int smp_src_cpu;
+extern volatile int smp_msg_id;
+
+#define MSG_ALL_BUT_SELF 0x8000 /* Assume <32768 CPU's */
+#define MSG_ALL 0x8001
+
+#define MSG_INVALIDATE_TLB 0x0001 /* Remote processor TLB invalidate */
+#define MSG_STOP_CPU 0x0002 /* Sent to shut down slave CPU's
+ * when rebooting
+ */
+#define MSG_RESCHEDULE 0x0003 /* Reschedule request from master CPU*/
+#define MSG_CALL_FUNCTION 0x0004 /* Call function on all other CPUs */
+
+#else
+
+/*
+ * These macros fold the SMP functionality into a single CPU system
+ */
+
+#define smp_num_cpus 1
+#define smp_processor_id() 0
+#define hard_smp_processor_id() 0
+#define smp_threads_ready 1
+#define kernel_lock()
+#define cpu_logical_map(cpu) 0
+#define smp_call_function(func,info,retry,wait)
+
+#endif
+#endif
diff --git a/pfinet/linux-src/include/linux/smp_lock.h b/pfinet/linux-src/include/linux/smp_lock.h
new file mode 100644
index 00000000..4583e2f5
--- /dev/null
+++ b/pfinet/linux-src/include/linux/smp_lock.h
@@ -0,0 +1,17 @@
+#ifndef __LINUX_SMPLOCK_H
+#define __LINUX_SMPLOCK_H
+
+#ifndef __SMP__
+
+#define lock_kernel() do { } while(0)
+#define unlock_kernel() do { } while(0)
+#define release_kernel_lock(task, cpu) do { } while(0)
+#define reacquire_kernel_lock(task) do { } while(0)
+
+#else
+
+#include <asm/smplock.h>
+
+#endif /* __SMP__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/socket.h b/pfinet/linux-src/include/linux/socket.h
new file mode 100644
index 00000000..b427f992
--- /dev/null
+++ b/pfinet/linux-src/include/linux/socket.h
@@ -0,0 +1,280 @@
+#ifndef _LINUX_SOCKET_H
+#define _LINUX_SOCKET_H
+
+#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
+
+#include <asm/socket.h> /* arch-dependent defines */
+#include <linux/sockios.h> /* the SIOCxxx I/O controls */
+#include <linux/uio.h> /* iovec support */
+#include <linux/types.h> /* pid_t */
+
+typedef unsigned short sa_family_t;
+
+/*
+ * 1003.1g requires sa_family_t and that sa_data is char.
+ */
+
+struct sockaddr {
+ sa_family_t sa_family; /* address family, AF_xxx */
+ char sa_data[14]; /* 14 bytes of protocol address */
+};
+
+struct linger {
+ int l_onoff; /* Linger active */
+ int l_linger; /* How long to linger for */
+};
+
+/*
+ * As we do 4.4BSD message passing we use a 4.4BSD message passing
+ * system, not 4.3. Thus msg_accrights(len) are now missing. They
+ * belong in an obscure libc emulation or the bin.
+ */
+
+struct msghdr {
+ void * msg_name; /* Socket name */
+ int msg_namelen; /* Length of name */
+ struct iovec * msg_iov; /* Data blocks */
+ __kernel_size_t msg_iovlen; /* Number of blocks */
+ void * msg_control; /* Per protocol magic (eg BSD file descriptor passing) */
+ __kernel_size_t msg_controllen; /* Length of cmsg list */
+ unsigned msg_flags;
+};
+
+/*
+ * POSIX 1003.1g - ancillary data object information
+ * Ancillary data consits of a sequence of pairs of
+ * (cmsghdr, cmsg_data[])
+ */
+
+struct cmsghdr {
+ __kernel_size_t cmsg_len; /* data byte count, including hdr */
+ int cmsg_level; /* originating protocol */
+ int cmsg_type; /* protocol-specific type */
+};
+
+/*
+ * Ancillary data object information MACROS
+ * Table 5-14 of POSIX 1003.1g
+ */
+
+#define __CMSG_NXTHDR(ctl, len, cmsg) __cmsg_nxthdr((ctl),(len),(cmsg))
+#define CMSG_NXTHDR(mhdr, cmsg) cmsg_nxthdr((mhdr), (cmsg))
+
+#define CMSG_ALIGN(len) ( ((len)+sizeof(long)-1) & ~(sizeof(long)-1) )
+
+#define CMSG_DATA(cmsg) ((void *)((char *)(cmsg) + CMSG_ALIGN(sizeof(struct cmsghdr))))
+#define CMSG_SPACE(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(len))
+#define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (len))
+
+#define __CMSG_FIRSTHDR(ctl,len) ((len) >= sizeof(struct cmsghdr) ? \
+ (struct cmsghdr *)(ctl) : \
+ (struct cmsghdr *)NULL)
+#define CMSG_FIRSTHDR(msg) __CMSG_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen)
+
+/*
+ * This mess will go away with glibc
+ */
+
+#ifdef __KERNEL__
+#define __KINLINE extern __inline__
+#elif defined(__GNUC__)
+#define __KINLINE static __inline__
+#elif defined(__cplusplus)
+#define __KINLINE static inline
+#else
+#define __KINLINE static
+#endif
+
+
+/*
+ * Get the next cmsg header
+ *
+ * PLEASE, do not touch this function. If you think, that it is
+ * incorrect, grep kernel sources and think about consequences
+ * before trying to improve it.
+ *
+ * Now it always returns valid, not truncated ancillary object
+ * HEADER. But caller still MUST check, that cmsg->cmsg_len is
+ * inside range, given by msg->msg_controllen before using
+ * ansillary object DATA. --ANK (980731)
+ */
+
+__KINLINE struct cmsghdr * __cmsg_nxthdr(void *__ctl, __kernel_size_t __size,
+ struct cmsghdr *__cmsg)
+{
+ struct cmsghdr * __ptr;
+
+ __ptr = (struct cmsghdr*)(((unsigned char *) __cmsg) + CMSG_ALIGN(__cmsg->cmsg_len));
+ if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size)
+ return (struct cmsghdr*)0;
+
+ return __ptr;
+}
+
+__KINLINE struct cmsghdr * cmsg_nxthdr (struct msghdr *__msg, struct cmsghdr *__cmsg)
+{
+ return __cmsg_nxthdr(__msg->msg_control, __msg->msg_controllen, __cmsg);
+}
+
+/* "Socket"-level control message types: */
+
+#define SCM_RIGHTS 0x01 /* rw: access rights (array of int) */
+#define SCM_CREDENTIALS 0x02 /* rw: struct ucred */
+#define SCM_CONNECT 0x03 /* rw: struct scm_connect */
+
+struct ucred {
+ __u32 pid;
+ __u32 uid;
+ __u32 gid;
+};
+
+
+/* Supported address families. */
+#define AF_UNSPEC 0
+#define AF_UNIX 1 /* Unix domain sockets */
+#define AF_LOCAL 1 /* POSIX name for AF_UNIX */
+#define AF_INET 2 /* Internet IP Protocol */
+#define AF_AX25 3 /* Amateur Radio AX.25 */
+#define AF_IPX 4 /* Novell IPX */
+#define AF_APPLETALK 5 /* AppleTalk DDP */
+#define AF_NETROM 6 /* Amateur Radio NET/ROM */
+#define AF_BRIDGE 7 /* Multiprotocol bridge */
+#define AF_ATMPVC 8 /* ATM PVCs */
+#define AF_X25 9 /* Reserved for X.25 project */
+#define AF_INET6 10 /* IP version 6 */
+#define AF_ROSE 11 /* Amateur Radio X.25 PLP */
+#define AF_DECnet 12 /* Reserved for DECnet project */
+#define AF_NETBEUI 13 /* Reserved for 802.2LLC project*/
+#define AF_SECURITY 14 /* Security callback pseudo AF */
+#define AF_KEY 15 /* PF_KEY key management API */
+#define AF_NETLINK 16
+#define AF_ROUTE AF_NETLINK /* Alias to emulate 4.4BSD */
+#define AF_PACKET 17 /* Packet family */
+#define AF_ASH 18 /* Ash */
+#define AF_ECONET 19 /* Acorn Econet */
+#define AF_ATMSVC 20 /* ATM SVCs */
+#define AF_SNA 22 /* Linux SNA Project (nutters!) */
+#define AF_IRDA 23 /* IRDA sockets */
+#define AF_MAX 32 /* For now.. */
+
+/* Protocol families, same as address families. */
+#define PF_UNSPEC AF_UNSPEC
+#define PF_UNIX AF_UNIX
+#define PF_LOCAL AF_LOCAL
+#define PF_INET AF_INET
+#define PF_AX25 AF_AX25
+#define PF_IPX AF_IPX
+#define PF_APPLETALK AF_APPLETALK
+#define PF_NETROM AF_NETROM
+#define PF_BRIDGE AF_BRIDGE
+#define PF_ATMPVC AF_ATMPVC
+#define PF_X25 AF_X25
+#define PF_INET6 AF_INET6
+#define PF_ROSE AF_ROSE
+#define PF_DECnet AF_DECnet
+#define PF_NETBEUI AF_NETBEUI
+#define PF_SECURITY AF_SECURITY
+#define PF_KEY AF_KEY
+#define PF_NETLINK AF_NETLINK
+#define PF_ROUTE AF_ROUTE
+#define PF_PACKET AF_PACKET
+#define PF_ASH AF_ASH
+#define PF_ECONET AF_ECONET
+#define PF_ATMSVC AF_ATMSVC
+#define PF_SNA AF_SNA
+#define PF_IRDA AF_IRDA
+
+#define PF_MAX AF_MAX
+
+/* Maximum queue length specifiable by listen. */
+#define SOMAXCONN 128
+
+/* Flags we can use with send/ and recv.
+ Added those for 1003.1g not all are supported yet
+ */
+
+#define MSG_OOB 1
+#define MSG_PEEK 2
+#define MSG_DONTROUTE 4
+#define MSG_TRYHARD 4 /* Synonym for MSG_DONTROUTE for DECnet */
+#define MSG_CTRUNC 8
+#define MSG_PROXY 0x10 /* Supply or ask second address. */
+#define MSG_TRUNC 0x20
+#define MSG_DONTWAIT 0x40 /* Nonblocking io */
+#define MSG_EOR 0x80 /* End of record */
+#define MSG_WAITALL 0x100 /* Wait for a full request */
+#define MSG_FIN 0x200
+#define MSG_SYN 0x400
+#define MSG_URG 0x800
+#define MSG_RST 0x1000
+#define MSG_ERRQUEUE 0x2000
+#define MSG_NOSIGNAL 0x4000
+
+#define MSG_CTLIGNORE 0x80000000
+
+#define MSG_EOF MSG_FIN
+#define MSG_CTLFLAGS (MSG_OOB|MSG_URG|MSG_FIN|MSG_SYN|MSG_RST)
+
+
+/* Setsockoptions(2) level. Thanks to BSD these must match IPPROTO_xxx */
+#define SOL_IP 0
+/* #define SOL_ICMP 1 No-no-no! Due to Linux :-) we cannot use SOL_ICMP=1 */
+#define SOL_TCP 6
+#define SOL_UDP 17
+#define SOL_IPV6 41
+#define SOL_ICMPV6 58
+#define SOL_RAW 255
+#define SOL_IPX 256
+#define SOL_AX25 257
+#define SOL_ATALK 258
+#define SOL_NETROM 259
+#define SOL_ROSE 260
+#define SOL_DECNET 261
+#define SOL_X25 262
+#define SOL_PACKET 263
+#define SOL_ATM 264 /* ATM layer (cell level) */
+#define SOL_AAL 265 /* ATM Adaption Layer (packet level) */
+#define SOL_IRDA 266
+
+/* IPX options */
+#define IPX_TYPE 1
+
+/* TCP options - this way around because someone left a set in the c library includes */
+#define TCP_NODELAY 1
+#define TCP_MAXSEG 2
+#define TCP_CORK 3 /* Linux specific (for use with sendfile) */
+
+#ifdef __KERNEL__
+extern int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len);
+extern int memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov,
+ int offset, int len);
+extern int csum_partial_copy_fromiovecend(unsigned char *kdata,
+ struct iovec *iov,
+ int offset,
+ unsigned int len, int *csump);
+
+extern int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode);
+extern int memcpy_toiovec(struct iovec *v, unsigned char *kdata, int len);
+extern void memcpy_tokerneliovec(struct iovec *iov, unsigned char *kdata, int len);
+extern int move_addr_to_user(void *kaddr, int klen, void *uaddr, int *ulen);
+extern int move_addr_to_kernel(void *uaddr, int ulen, void *kaddr);
+extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data);
+#endif
+#endif /* not kernel and not glibc */
+
+#if !defined(__KERNEL__) && (!defined(__GLIBC__) || (__GLIBC__ < 2))
+
+/* Socket types for libc5 compatibility -- KTK */
+
+#define SOCK_STREAM 1 /* stream (connection) socket */
+#define SOCK_DGRAM 2 /* datagram (conn.less) socket */
+#define SOCK_RAW 3 /* raw socket */
+#define SOCK_RDM 4 /* reliably-delivered message */
+#define SOCK_SEQPACKET 5 /* sequential packet socket */
+#define SOCK_PACKET 10 /* linux specific way of */
+ /* getting packets at the dev */
+ /* level. For writing rarp and */
+ /* other similar things on the */
+ /* user level. */
+#endif /* libc<=5 && !kernel */
+#endif /* _LINUX_SOCKET_H */
diff --git a/pfinet/linux-src/include/linux/sockios.h b/pfinet/linux-src/include/linux/sockios.h
new file mode 100644
index 00000000..995e43e9
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sockios.h
@@ -0,0 +1,109 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions of the socket-level I/O control calls.
+ *
+ * Version: @(#)sockios.h 1.0.2 03/09/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_SOCKIOS_H
+#define _LINUX_SOCKIOS_H
+
+#include <asm/sockios.h>
+
+/* Routing table calls. */
+#define SIOCADDRT 0x890B /* add routing table entry */
+#define SIOCDELRT 0x890C /* delete routing table entry */
+#define SIOCRTMSG 0x890D /* call to routing system */
+
+/* Socket configuration controls. */
+#define SIOCGIFNAME 0x8910 /* get iface name */
+#define SIOCSIFLINK 0x8911 /* set iface channel */
+#define SIOCGIFCONF 0x8912 /* get iface list */
+#define SIOCGIFFLAGS 0x8913 /* get flags */
+#define SIOCSIFFLAGS 0x8914 /* set flags */
+#define SIOCGIFADDR 0x8915 /* get PA address */
+#define SIOCSIFADDR 0x8916 /* set PA address */
+#define SIOCGIFDSTADDR 0x8917 /* get remote PA address */
+#define SIOCSIFDSTADDR 0x8918 /* set remote PA address */
+#define SIOCGIFBRDADDR 0x8919 /* get broadcast PA address */
+#define SIOCSIFBRDADDR 0x891a /* set broadcast PA address */
+#define SIOCGIFNETMASK 0x891b /* get network PA mask */
+#define SIOCSIFNETMASK 0x891c /* set network PA mask */
+#define SIOCGIFMETRIC 0x891d /* get metric */
+#define SIOCSIFMETRIC 0x891e /* set metric */
+#define SIOCGIFMEM 0x891f /* get memory address (BSD) */
+#define SIOCSIFMEM 0x8920 /* set memory address (BSD) */
+#define SIOCGIFMTU 0x8921 /* get MTU size */
+#define SIOCSIFMTU 0x8922 /* set MTU size */
+#define SIOCSIFNAME 0x8923 /* set interface name */
+#define SIOCSIFHWADDR 0x8924 /* set hardware address */
+#define SIOCGIFENCAP 0x8925 /* get/set encapsulations */
+#define SIOCSIFENCAP 0x8926
+#define SIOCGIFHWADDR 0x8927 /* Get hardware address */
+#define SIOCGIFSLAVE 0x8929 /* Driver slaving support */
+#define SIOCSIFSLAVE 0x8930
+#define SIOCADDMULTI 0x8931 /* Multicast address lists */
+#define SIOCDELMULTI 0x8932
+#define SIOCGIFINDEX 0x8933 /* name -> if_index mapping */
+#define SIOGIFINDEX SIOCGIFINDEX /* misprint compatibility :-) */
+#define SIOCSIFPFLAGS 0x8934 /* set/get extended flags set */
+#define SIOCGIFPFLAGS 0x8935
+#define SIOCDIFADDR 0x8936 /* delete PA address */
+#define SIOCSIFHWBROADCAST 0x8937 /* set hardware broadcast addr */
+#define SIOCGIFCOUNT 0x8938 /* get number of devices */
+
+#define SIOCGIFBR 0x8940 /* Bridging support */
+#define SIOCSIFBR 0x8941 /* Set bridging options */
+
+#define SIOCGIFTXQLEN 0x8942 /* Get the tx queue length */
+#define SIOCSIFTXQLEN 0x8943 /* Set the tx queue length */
+
+
+/* ARP cache control calls. */
+ /* 0x8950 - 0x8952 * obsolete calls, don't re-use */
+#define SIOCDARP 0x8953 /* delete ARP table entry */
+#define SIOCGARP 0x8954 /* get ARP table entry */
+#define SIOCSARP 0x8955 /* set ARP table entry */
+
+/* RARP cache control calls. */
+#define SIOCDRARP 0x8960 /* delete RARP table entry */
+#define SIOCGRARP 0x8961 /* get RARP table entry */
+#define SIOCSRARP 0x8962 /* set RARP table entry */
+
+/* Driver configuration calls */
+
+#define SIOCGIFMAP 0x8970 /* Get device parameters */
+#define SIOCSIFMAP 0x8971 /* Set device parameters */
+
+/* DLCI configuration calls */
+
+#define SIOCADDDLCI 0x8980 /* Create new DLCI device */
+#define SIOCDELDLCI 0x8981 /* Delete DLCI device */
+
+/* Device private ioctl calls */
+
+/*
+ * These 16 ioctls are available to devices via the do_ioctl() device
+ * vector. Each device should include this file and redefine these names
+ * as their own. Because these are device dependent it is a good idea
+ * _NOT_ to issue them to random objects and hope.
+ */
+
+#define SIOCDEVPRIVATE 0x89F0 /* to 89FF */
+
+/*
+ * These 16 ioctl calls are protocol private
+ */
+
+#define SIOCPROTOPRIVATE 0x89E0 /* to 89EF */
+#endif /* _LINUX_SOCKIOS_H */
diff --git a/pfinet/linux-src/include/linux/sound.h b/pfinet/linux-src/include/linux/sound.h
new file mode 100644
index 00000000..4921b90e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sound.h
@@ -0,0 +1,15 @@
+/*
+ * Sound core interface functions
+ */
+
+extern int register_sound_special(struct file_operations *fops, int unit);
+extern int register_sound_mixer(struct file_operations *fops, int dev);
+extern int register_sound_midi(struct file_operations *fops, int dev);
+extern int register_sound_dsp(struct file_operations *fops, int dev);
+extern int register_sound_synth(struct file_operations *fops, int dev);
+
+extern void unregister_sound_special(int unit);
+extern void unregister_sound_mixer(int unit);
+extern void unregister_sound_midi(int unit);
+extern void unregister_sound_dsp(int unit);
+extern void unregister_sound_synth(int unit);
diff --git a/pfinet/linux-src/include/linux/soundcard.h b/pfinet/linux-src/include/linux/soundcard.h
new file mode 100644
index 00000000..2041b4d4
--- /dev/null
+++ b/pfinet/linux-src/include/linux/soundcard.h
@@ -0,0 +1,1270 @@
+#ifndef SOUNDCARD_H
+#define SOUNDCARD_H
+/*
+ * Copyright by Hannu Savolainen 1993-1997
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+/*
+ * OSS interface version. With versions earlier than 3.6 this value is
+ * an integer with value less than 361. In versions 3.6 and later
+ * it's a six digit hexadecimal value. For example value
+ * of 0x030600 represents OSS version 3.6.0.
+ * Use ioctl(fd, OSS_GETVERSION, &int) to get the version number of
+ * the currently active driver.
+ */
+#define SOUND_VERSION 0x030802
+#define OPEN_SOUND_SYSTEM
+
+/* In Linux we need to be prepared for cross compiling */
+#include <linux/ioctl.h>
+
+/*
+ * Supported card ID numbers (Should be somewhere else?)
+ */
+
+#define SNDCARD_ADLIB 1
+#define SNDCARD_SB 2
+#define SNDCARD_PAS 3
+#define SNDCARD_GUS 4
+#define SNDCARD_MPU401 5
+#define SNDCARD_SB16 6
+#define SNDCARD_SB16MIDI 7
+#define SNDCARD_UART6850 8
+#define SNDCARD_GUS16 9
+#define SNDCARD_MSS 10
+#define SNDCARD_PSS 11
+#define SNDCARD_SSCAPE 12
+#define SNDCARD_PSS_MPU 13
+#define SNDCARD_PSS_MSS 14
+#define SNDCARD_SSCAPE_MSS 15
+#define SNDCARD_TRXPRO 16
+#define SNDCARD_TRXPRO_SB 17
+#define SNDCARD_TRXPRO_MPU 18
+#define SNDCARD_MAD16 19
+#define SNDCARD_MAD16_MPU 20
+#define SNDCARD_CS4232 21
+#define SNDCARD_CS4232_MPU 22
+#define SNDCARD_MAUI 23
+#define SNDCARD_PSEUDO_MSS 24
+#define SNDCARD_GUSPNP 25
+#define SNDCARD_UART401 26
+/* Sound card numbers 27 to N are reserved. Don't add more numbers here. */
+
+/***********************************
+ * IOCTL Commands for /dev/sequencer
+ */
+
+#ifndef _SIOWR
+#if defined(_IOWR) && (defined(_AIX) || (!defined(sun) && !defined(sparc) && !defined(__INCioctlh) && !defined(__Lynx__)))
+/* Use already defined ioctl defines if they exist (except with Sun) */
+#define SIOCPARM_MASK IOCPARM_MASK
+#define SIOC_VOID IOC_VOID
+#define SIOC_OUT IOC_OUT
+#define SIOC_IN IOC_IN
+#define SIOC_INOUT IOC_INOUT
+#define _SIOC_SIZE _IOC_SIZE
+#define _SIOC_DIR _IOC_DIR
+#define _SIOC_NONE _IOC_NONE
+#define _SIOC_READ _IOC_READ
+#define _SIOC_WRITE _IOC_WRITE
+#define _SIO _IO
+#define _SIOR _IOR
+#define _SIOW _IOW
+#define _SIOWR _IOWR
+#else
+
+/* Ioctl's have the command encoded in the lower word,
+ * and the size of any in or out parameters in the upper
+ * word. The high 2 bits of the upper word are used
+ * to encode the in/out status of the parameter; for now
+ * we restrict parameters to at most 8191 bytes.
+ */
+/* #define SIOCTYPE (0xff<<8) */
+#define SIOCPARM_MASK 0x1fff /* parameters must be < 8192 bytes */
+#define SIOC_VOID 0x00000000 /* no parameters */
+#define SIOC_OUT 0x20000000 /* copy out parameters */
+#define SIOC_IN 0x40000000 /* copy in parameters */
+#define SIOC_INOUT (SIOC_IN|SIOC_OUT)
+/* the 0x20000000 is so we can distinguish new ioctl's from old */
+#define _SIO(x,y) ((int)(SIOC_VOID|(x<<8)|y))
+#define _SIOR(x,y,t) ((int)(SIOC_OUT|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y))
+#define _SIOW(x,y,t) ((int)(SIOC_IN|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y))
+/* this should be _SIORW, but stdio got there first */
+#define _SIOWR(x,y,t) ((int)(SIOC_INOUT|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y))
+#define _SIOC_SIZE(x) ((x>>16)&SIOCPARM_MASK)
+#define _SIOC_DIR(x) (x & 0xf0000000)
+#define _SIOC_NONE SIOC_VOID
+#define _SIOC_READ SIOC_OUT
+#define _SIOC_WRITE SIOC_IN
+# endif /* _IOWR */
+#endif /* !_SIOWR */
+
+#define SNDCTL_SEQ_RESET _SIO ('Q', 0)
+#define SNDCTL_SEQ_SYNC _SIO ('Q', 1)
+#define SNDCTL_SYNTH_INFO _SIOWR('Q', 2, struct synth_info)
+#define SNDCTL_SEQ_CTRLRATE _SIOWR('Q', 3, int) /* Set/get timer resolution (HZ) */
+#define SNDCTL_SEQ_GETOUTCOUNT _SIOR ('Q', 4, int)
+#define SNDCTL_SEQ_GETINCOUNT _SIOR ('Q', 5, int)
+#define SNDCTL_SEQ_PERCMODE _SIOW ('Q', 6, int)
+#define SNDCTL_FM_LOAD_INSTR _SIOW ('Q', 7, struct sbi_instrument) /* Obsolete. Don't use!!!!!! */
+#define SNDCTL_SEQ_TESTMIDI _SIOW ('Q', 8, int)
+#define SNDCTL_SEQ_RESETSAMPLES _SIOW ('Q', 9, int)
+#define SNDCTL_SEQ_NRSYNTHS _SIOR ('Q',10, int)
+#define SNDCTL_SEQ_NRMIDIS _SIOR ('Q',11, int)
+#define SNDCTL_MIDI_INFO _SIOWR('Q',12, struct midi_info)
+#define SNDCTL_SEQ_THRESHOLD _SIOW ('Q',13, int)
+#define SNDCTL_SYNTH_MEMAVL _SIOWR('Q',14, int) /* in=dev#, out=memsize */
+#define SNDCTL_FM_4OP_ENABLE _SIOW ('Q',15, int) /* in=dev# */
+#define SNDCTL_SEQ_PANIC _SIO ('Q',17)
+#define SNDCTL_SEQ_OUTOFBAND _SIOW ('Q',18, struct seq_event_rec)
+#define SNDCTL_SEQ_GETTIME _SIOR ('Q',19, int)
+#define SNDCTL_SYNTH_ID _SIOWR('Q',20, struct synth_info)
+#define SNDCTL_SYNTH_CONTROL _SIOWR('Q',21, struct synth_control)
+#define SNDCTL_SYNTH_REMOVESAMPLE _SIOWR('Q',22, struct remove_sample)
+
+typedef struct synth_control
+{
+ int devno; /* Synthesizer # */
+ char data[4000]; /* Device spesific command/data record */
+}synth_control;
+
+typedef struct remove_sample
+{
+ int devno; /* Synthesizer # */
+ int bankno; /* MIDI bank # (0=General MIDI) */
+ int instrno; /* MIDI instrument number */
+} remove_sample;
+
+typedef struct seq_event_rec {
+ unsigned char arr[8];
+} seq_event_rec;
+
+#define SNDCTL_TMR_TIMEBASE _SIOWR('T', 1, int)
+#define SNDCTL_TMR_START _SIO ('T', 2)
+#define SNDCTL_TMR_STOP _SIO ('T', 3)
+#define SNDCTL_TMR_CONTINUE _SIO ('T', 4)
+#define SNDCTL_TMR_TEMPO _SIOWR('T', 5, int)
+#define SNDCTL_TMR_SOURCE _SIOWR('T', 6, int)
+# define TMR_INTERNAL 0x00000001
+# define TMR_EXTERNAL 0x00000002
+# define TMR_MODE_MIDI 0x00000010
+# define TMR_MODE_FSK 0x00000020
+# define TMR_MODE_CLS 0x00000040
+# define TMR_MODE_SMPTE 0x00000080
+#define SNDCTL_TMR_METRONOME _SIOW ('T', 7, int)
+#define SNDCTL_TMR_SELECT _SIOW ('T', 8, int)
+
+/*
+ * Some big endian/little endian handling macros
+ */
+
+#if defined(_AIX) || defined(AIX) || defined(sparc) || defined(HPPA) || defined(PPC)
+/* Big endian machines */
+# define _PATCHKEY(id) (0xfd00|id)
+# define AFMT_S16_NE AFMT_S16_BE
+#else
+# define _PATCHKEY(id) ((id<<8)|0xfd)
+# define AFMT_S16_NE AFMT_S16_LE
+#endif
+
+/*
+ * Sample loading mechanism for internal synthesizers (/dev/sequencer)
+ * The following patch_info structure has been designed to support
+ * Gravis UltraSound. It tries to be universal format for uploading
+ * sample based patches but is probably too limited.
+ *
+ * (PBD) As Hannu guessed, the GUS structure is too limited for
+ * the WaveFront, but this is the right place for a constant definition.
+ */
+
+struct patch_info {
+ unsigned short key; /* Use WAVE_PATCH here */
+#define WAVE_PATCH _PATCHKEY(0x04)
+#define GUS_PATCH WAVE_PATCH
+#define WAVEFRONT_PATCH _PATCHKEY(0x06)
+
+ short device_no; /* Synthesizer number */
+ short instr_no; /* Midi pgm# */
+
+ unsigned int mode;
+/*
+ * The least significant byte has the same format than the GUS .PAT
+ * files
+ */
+#define WAVE_16_BITS 0x01 /* bit 0 = 8 or 16 bit wave data. */
+#define WAVE_UNSIGNED 0x02 /* bit 1 = Signed - Unsigned data. */
+#define WAVE_LOOPING 0x04 /* bit 2 = looping enabled-1. */
+#define WAVE_BIDIR_LOOP 0x08 /* bit 3 = Set is bidirectional looping. */
+#define WAVE_LOOP_BACK 0x10 /* bit 4 = Set is looping backward. */
+#define WAVE_SUSTAIN_ON 0x20 /* bit 5 = Turn sustaining on. (Env. pts. 3)*/
+#define WAVE_ENVELOPES 0x40 /* bit 6 = Enable envelopes - 1 */
+#define WAVE_FAST_RELEASE 0x80 /* bit 7 = Shut off immediately after note off */
+ /* (use the env_rate/env_offs fields). */
+/* Linux specific bits */
+#define WAVE_VIBRATO 0x00010000 /* The vibrato info is valid */
+#define WAVE_TREMOLO 0x00020000 /* The tremolo info is valid */
+#define WAVE_SCALE 0x00040000 /* The scaling info is valid */
+#define WAVE_FRACTIONS 0x00080000 /* Fraction information is valid */
+/* Reserved bits */
+#define WAVE_ROM 0x40000000 /* For future use */
+#define WAVE_MULAW 0x20000000 /* For future use */
+/* Other bits must be zeroed */
+
+ int len; /* Size of the wave data in bytes */
+ int loop_start, loop_end; /* Byte offsets from the beginning */
+
+/*
+ * The base_freq and base_note fields are used when computing the
+ * playback speed for a note. The base_note defines the tone frequency
+ * which is heard if the sample is played using the base_freq as the
+ * playback speed.
+ *
+ * The low_note and high_note fields define the minimum and maximum note
+ * frequencies for which this sample is valid. It is possible to define
+ * more than one samples for an instrument number at the same time. The
+ * low_note and high_note fields are used to select the most suitable one.
+ *
+ * The fields base_note, high_note and low_note should contain
+ * the note frequency multiplied by 1000. For example value for the
+ * middle A is 440*1000.
+ */
+
+ unsigned int base_freq;
+ unsigned int base_note;
+ unsigned int high_note;
+ unsigned int low_note;
+ int panning; /* -128=left, 127=right */
+ int detuning;
+
+/* New fields introduced in version 1.99.5 */
+
+ /* Envelope. Enabled by mode bit WAVE_ENVELOPES */
+ unsigned char env_rate[ 6 ]; /* GUS HW ramping rate */
+ unsigned char env_offset[ 6 ]; /* 255 == 100% */
+
+ /*
+ * The tremolo, vibrato and scale info are not supported yet.
+ * Enable by setting the mode bits WAVE_TREMOLO, WAVE_VIBRATO or
+ * WAVE_SCALE
+ */
+
+ unsigned char tremolo_sweep;
+ unsigned char tremolo_rate;
+ unsigned char tremolo_depth;
+
+ unsigned char vibrato_sweep;
+ unsigned char vibrato_rate;
+ unsigned char vibrato_depth;
+
+ int scale_frequency;
+ unsigned int scale_factor; /* from 0 to 2048 or 0 to 2 */
+
+ int volume;
+ int fractions;
+ int reserved1;
+ int spare[2];
+ char data[1]; /* The waveform data starts here */
+ };
+
+struct sysex_info {
+ short key; /* Use SYSEX_PATCH or MAUI_PATCH here */
+#define SYSEX_PATCH _PATCHKEY(0x05)
+#define MAUI_PATCH _PATCHKEY(0x06)
+ short device_no; /* Synthesizer number */
+ int len; /* Size of the sysex data in bytes */
+ unsigned char data[1]; /* Sysex data starts here */
+ };
+
+/*
+ * /dev/sequencer input events.
+ *
+ * The data written to the /dev/sequencer is a stream of events. Events
+ * are records of 4 or 8 bytes. The first byte defines the size.
+ * Any number of events can be written with a write call. There
+ * is a set of macros for sending these events. Use these macros if you
+ * want to maximize portability of your program.
+ *
+ * Events SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO. Are also input events.
+ * (All input events are currently 4 bytes long. Be prepared to support
+ * 8 byte events also. If you receive any event having first byte >= 128,
+ * it's a 8 byte event.
+ *
+ * The events are documented at the end of this file.
+ *
+ * Normal events (4 bytes)
+ * There is also a 8 byte version of most of the 4 byte events. The
+ * 8 byte one is recommended.
+ */
+#define SEQ_NOTEOFF 0
+#define SEQ_FMNOTEOFF SEQ_NOTEOFF /* Just old name */
+#define SEQ_NOTEON 1
+#define SEQ_FMNOTEON SEQ_NOTEON
+#define SEQ_WAIT TMR_WAIT_ABS
+#define SEQ_PGMCHANGE 3
+#define SEQ_FMPGMCHANGE SEQ_PGMCHANGE
+#define SEQ_SYNCTIMER TMR_START
+#define SEQ_MIDIPUTC 5
+#define SEQ_DRUMON 6 /*** OBSOLETE ***/
+#define SEQ_DRUMOFF 7 /*** OBSOLETE ***/
+#define SEQ_ECHO TMR_ECHO /* For synching programs with output */
+#define SEQ_AFTERTOUCH 9
+#define SEQ_CONTROLLER 10
+
+/*******************************************
+ * Midi controller numbers
+ *******************************************
+ * Controllers 0 to 31 (0x00 to 0x1f) and
+ * 32 to 63 (0x20 to 0x3f) are continuous
+ * controllers.
+ * In the MIDI 1.0 these controllers are sent using
+ * two messages. Controller numbers 0 to 31 are used
+ * to send the MSB and the controller numbers 32 to 63
+ * are for the LSB. Note that just 7 bits are used in MIDI bytes.
+ */
+
+#define CTL_BANK_SELECT 0x00
+#define CTL_MODWHEEL 0x01
+#define CTL_BREATH 0x02
+/* undefined 0x03 */
+#define CTL_FOOT 0x04
+#define CTL_PORTAMENTO_TIME 0x05
+#define CTL_DATA_ENTRY 0x06
+#define CTL_MAIN_VOLUME 0x07
+#define CTL_BALANCE 0x08
+/* undefined 0x09 */
+#define CTL_PAN 0x0a
+#define CTL_EXPRESSION 0x0b
+/* undefined 0x0c */
+/* undefined 0x0d */
+/* undefined 0x0e */
+/* undefined 0x0f */
+#define CTL_GENERAL_PURPOSE1 0x10
+#define CTL_GENERAL_PURPOSE2 0x11
+#define CTL_GENERAL_PURPOSE3 0x12
+#define CTL_GENERAL_PURPOSE4 0x13
+/* undefined 0x14 - 0x1f */
+
+/* undefined 0x20 */
+/* The controller numbers 0x21 to 0x3f are reserved for the */
+/* least significant bytes of the controllers 0x00 to 0x1f. */
+/* These controllers are not recognised by the driver. */
+
+/* Controllers 64 to 69 (0x40 to 0x45) are on/off switches. */
+/* 0=OFF and 127=ON (intermediate values are possible) */
+#define CTL_DAMPER_PEDAL 0x40
+#define CTL_SUSTAIN 0x40 /* Alias */
+#define CTL_HOLD 0x40 /* Alias */
+#define CTL_PORTAMENTO 0x41
+#define CTL_SOSTENUTO 0x42
+#define CTL_SOFT_PEDAL 0x43
+/* undefined 0x44 */
+#define CTL_HOLD2 0x45
+/* undefined 0x46 - 0x4f */
+
+#define CTL_GENERAL_PURPOSE5 0x50
+#define CTL_GENERAL_PURPOSE6 0x51
+#define CTL_GENERAL_PURPOSE7 0x52
+#define CTL_GENERAL_PURPOSE8 0x53
+/* undefined 0x54 - 0x5a */
+#define CTL_EXT_EFF_DEPTH 0x5b
+#define CTL_TREMOLO_DEPTH 0x5c
+#define CTL_CHORUS_DEPTH 0x5d
+#define CTL_DETUNE_DEPTH 0x5e
+#define CTL_CELESTE_DEPTH 0x5e /* Alias for the above one */
+#define CTL_PHASER_DEPTH 0x5f
+#define CTL_DATA_INCREMENT 0x60
+#define CTL_DATA_DECREMENT 0x61
+#define CTL_NONREG_PARM_NUM_LSB 0x62
+#define CTL_NONREG_PARM_NUM_MSB 0x63
+#define CTL_REGIST_PARM_NUM_LSB 0x64
+#define CTL_REGIST_PARM_NUM_MSB 0x65
+/* undefined 0x66 - 0x78 */
+/* reserved 0x79 - 0x7f */
+
+/* Pseudo controllers (not midi compatible) */
+#define CTRL_PITCH_BENDER 255
+#define CTRL_PITCH_BENDER_RANGE 254
+#define CTRL_EXPRESSION 253 /* Obsolete */
+#define CTRL_MAIN_VOLUME 252 /* Obsolete */
+#define SEQ_BALANCE 11
+#define SEQ_VOLMODE 12
+
+/*
+ * Volume mode decides how volumes are used
+ */
+
+#define VOL_METHOD_ADAGIO 1
+#define VOL_METHOD_LINEAR 2
+
+/*
+ * Note! SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO are used also as
+ * input events.
+ */
+
+/*
+ * Event codes 0xf0 to 0xfc are reserved for future extensions.
+ */
+
+#define SEQ_FULLSIZE 0xfd /* Long events */
+/*
+ * SEQ_FULLSIZE events are used for loading patches/samples to the
+ * synthesizer devices. These events are passed directly to the driver
+ * of the associated synthesizer device. There is no limit to the size
+ * of the extended events. These events are not queued but executed
+ * immediately when the write() is called (execution can take several
+ * seconds of time).
+ *
+ * When a SEQ_FULLSIZE message is written to the device, it must
+ * be written using exactly one write() call. Other events cannot
+ * be mixed to the same write.
+ *
+ * For FM synths (YM3812/OPL3) use struct sbi_instrument and write it to the
+ * /dev/sequencer. Don't write other data together with the instrument structure
+ * Set the key field of the structure to FM_PATCH. The device field is used to
+ * route the patch to the corresponding device.
+ *
+ * For wave table use struct patch_info. Initialize the key field
+ * to WAVE_PATCH.
+ */
+#define SEQ_PRIVATE 0xfe /* Low level HW dependent events (8 bytes) */
+#define SEQ_EXTENDED 0xff /* Extended events (8 bytes) OBSOLETE */
+
+/*
+ * Record for FM patches
+ */
+
+typedef unsigned char sbi_instr_data[32];
+
+struct sbi_instrument {
+ unsigned short key; /* FM_PATCH or OPL3_PATCH */
+#define FM_PATCH _PATCHKEY(0x01)
+#define OPL3_PATCH _PATCHKEY(0x03)
+ short device; /* Synth# (0-4) */
+ int channel; /* Program# to be initialized */
+ sbi_instr_data operators; /* Register settings for operator cells (.SBI format) */
+ };
+
+struct synth_info { /* Read only */
+ char name[30];
+ int device; /* 0-N. INITIALIZE BEFORE CALLING */
+ int synth_type;
+#define SYNTH_TYPE_FM 0
+#define SYNTH_TYPE_SAMPLE 1
+#define SYNTH_TYPE_MIDI 2 /* Midi interface */
+
+ int synth_subtype;
+#define FM_TYPE_ADLIB 0x00
+#define FM_TYPE_OPL3 0x01
+#define MIDI_TYPE_MPU401 0x401
+
+#define SAMPLE_TYPE_BASIC 0x10
+#define SAMPLE_TYPE_GUS SAMPLE_TYPE_BASIC
+#define SAMPLE_TYPE_WAVEFRONT 0x11
+
+ int perc_mode; /* No longer supported */
+ int nr_voices;
+ int nr_drums; /* Obsolete field */
+ int instr_bank_size;
+ unsigned int capabilities;
+#define SYNTH_CAP_PERCMODE 0x00000001 /* No longer used */
+#define SYNTH_CAP_OPL3 0x00000002 /* Set if OPL3 supported */
+#define SYNTH_CAP_INPUT 0x00000004 /* Input (MIDI) device */
+ int dummies[19]; /* Reserve space */
+ };
+
+struct sound_timer_info {
+ char name[32];
+ int caps;
+ };
+
+#define MIDI_CAP_MPU401 1 /* MPU-401 intelligent mode */
+
+struct midi_info {
+ char name[30];
+ int device; /* 0-N. INITIALIZE BEFORE CALLING */
+ unsigned int capabilities; /* To be defined later */
+ int dev_type;
+ int dummies[18]; /* Reserve space */
+ };
+
+/********************************************
+ * ioctl commands for the /dev/midi##
+ */
+typedef struct {
+ unsigned char cmd;
+ char nr_args, nr_returns;
+ unsigned char data[30];
+ } mpu_command_rec;
+
+#define SNDCTL_MIDI_PRETIME _SIOWR('m', 0, int)
+#define SNDCTL_MIDI_MPUMODE _SIOWR('m', 1, int)
+#define SNDCTL_MIDI_MPUCMD _SIOWR('m', 2, mpu_command_rec)
+
+/********************************************
+ * IOCTL commands for /dev/dsp and /dev/audio
+ */
+
+#define SNDCTL_DSP_RESET _SIO ('P', 0)
+#define SNDCTL_DSP_SYNC _SIO ('P', 1)
+#define SNDCTL_DSP_SPEED _SIOWR('P', 2, int)
+#define SNDCTL_DSP_STEREO _SIOWR('P', 3, int)
+#define SNDCTL_DSP_GETBLKSIZE _SIOWR('P', 4, int)
+#define SNDCTL_DSP_SAMPLESIZE SNDCTL_DSP_SETFMT
+#define SNDCTL_DSP_CHANNELS _SIOWR('P', 6, int)
+#define SOUND_PCM_WRITE_CHANNELS SNDCTL_DSP_CHANNELS
+#define SOUND_PCM_WRITE_FILTER _SIOWR('P', 7, int)
+#define SNDCTL_DSP_POST _SIO ('P', 8)
+#define SNDCTL_DSP_SUBDIVIDE _SIOWR('P', 9, int)
+#define SNDCTL_DSP_SETFRAGMENT _SIOWR('P',10, int)
+
+/* Audio data formats (Note! U8=8 and S16_LE=16 for compatibility) */
+#define SNDCTL_DSP_GETFMTS _SIOR ('P',11, int) /* Returns a mask */
+#define SNDCTL_DSP_SETFMT _SIOWR('P',5, int) /* Selects ONE fmt*/
+# define AFMT_QUERY 0x00000000 /* Return current fmt */
+# define AFMT_MU_LAW 0x00000001
+# define AFMT_A_LAW 0x00000002
+# define AFMT_IMA_ADPCM 0x00000004
+# define AFMT_U8 0x00000008
+# define AFMT_S16_LE 0x00000010 /* Little endian signed 16*/
+# define AFMT_S16_BE 0x00000020 /* Big endian signed 16 */
+# define AFMT_S8 0x00000040
+# define AFMT_U16_LE 0x00000080 /* Little endian U16 */
+# define AFMT_U16_BE 0x00000100 /* Big endian U16 */
+# define AFMT_MPEG 0x00000200 /* MPEG (2) audio */
+
+/*
+ * Buffer status queries.
+ */
+typedef struct audio_buf_info {
+ int fragments; /* # of available fragments (partially usend ones not counted) */
+ int fragstotal; /* Total # of fragments allocated */
+ int fragsize; /* Size of a fragment in bytes */
+
+ int bytes; /* Available space in bytes (includes partially used fragments) */
+ /* Note! 'bytes' could be more than fragments*fragsize */
+ } audio_buf_info;
+
+#define SNDCTL_DSP_GETOSPACE _SIOR ('P',12, audio_buf_info)
+#define SNDCTL_DSP_GETISPACE _SIOR ('P',13, audio_buf_info)
+#define SNDCTL_DSP_NONBLOCK _SIO ('P',14)
+#define SNDCTL_DSP_GETCAPS _SIOR ('P',15, int)
+# define DSP_CAP_REVISION 0x000000ff /* Bits for revision level (0 to 255) */
+# define DSP_CAP_DUPLEX 0x00000100 /* Full duplex record/playback */
+# define DSP_CAP_REALTIME 0x00000200 /* Real time capability */
+# define DSP_CAP_BATCH 0x00000400 /* Device has some kind of */
+ /* internal buffers which may */
+ /* cause some delays and */
+ /* decrease precision of timing */
+# define DSP_CAP_COPROC 0x00000800 /* Has a coprocessor */
+ /* Sometimes it's a DSP */
+ /* but usually not */
+# define DSP_CAP_TRIGGER 0x00001000 /* Supports SETTRIGGER */
+# define DSP_CAP_MMAP 0x00002000 /* Supports mmap() */
+
+#define SNDCTL_DSP_GETTRIGGER _SIOR ('P',16, int)
+#define SNDCTL_DSP_SETTRIGGER _SIOW ('P',16, int)
+# define PCM_ENABLE_INPUT 0x00000001
+# define PCM_ENABLE_OUTPUT 0x00000002
+
+typedef struct count_info {
+ int bytes; /* Total # of bytes processed */
+ int blocks; /* # of fragment transitions since last time */
+ int ptr; /* Current DMA pointer value */
+ } count_info;
+
+#define SNDCTL_DSP_GETIPTR _SIOR ('P',17, count_info)
+#define SNDCTL_DSP_GETOPTR _SIOR ('P',18, count_info)
+
+typedef struct buffmem_desc {
+ unsigned *buffer;
+ int size;
+ } buffmem_desc;
+#define SNDCTL_DSP_MAPINBUF _SIOR ('P', 19, buffmem_desc)
+#define SNDCTL_DSP_MAPOUTBUF _SIOR ('P', 20, buffmem_desc)
+#define SNDCTL_DSP_SETSYNCRO _SIO ('P', 21)
+#define SNDCTL_DSP_SETDUPLEX _SIO ('P', 22)
+#define SNDCTL_DSP_GETODELAY _SIOR ('P', 23, int)
+
+/*
+ * Application's profile defines the way how playback underrun situations should be handled.
+ *
+ * APF_NORMAL (the default) and APF_NETWORK make the driver to cleanup the
+ * playback buffer whenever an underrun occurs. This consumes some time
+ * prevents looping the existing buffer.
+ * APF_CPUINTENS is intended to be set by CPU intensive applications which
+ * are likely to run out of time occasionally. In this mode the buffer cleanup is
+ * disabled which saves CPU time but also let's the previous buffer content to
+ * be played during the "pause" after the underrun.
+ */
+#define SNDCTL_DSP_PROFILE _SIOW ('P', 23, int)
+#define APF_NORMAL 0 /* Normal applications */
+#define APF_NETWORK 1 /* Underruns probably caused by an "external" delay */
+#define APF_CPUINTENS 2 /* Underruns probably caused by "overheating" the CPU */
+
+#define SOUND_PCM_READ_RATE _SIOR ('P', 2, int)
+#define SOUND_PCM_READ_CHANNELS _SIOR ('P', 6, int)
+#define SOUND_PCM_READ_BITS _SIOR ('P', 5, int)
+#define SOUND_PCM_READ_FILTER _SIOR ('P', 7, int)
+
+/* Some alias names */
+#define SOUND_PCM_WRITE_BITS SNDCTL_DSP_SETFMT
+#define SOUND_PCM_WRITE_RATE SNDCTL_DSP_SPEED
+#define SOUND_PCM_POST SNDCTL_DSP_POST
+#define SOUND_PCM_RESET SNDCTL_DSP_RESET
+#define SOUND_PCM_SYNC SNDCTL_DSP_SYNC
+#define SOUND_PCM_SUBDIVIDE SNDCTL_DSP_SUBDIVIDE
+#define SOUND_PCM_SETFRAGMENT SNDCTL_DSP_SETFRAGMENT
+#define SOUND_PCM_GETFMTS SNDCTL_DSP_GETFMTS
+#define SOUND_PCM_SETFMT SNDCTL_DSP_SETFMT
+#define SOUND_PCM_GETOSPACE SNDCTL_DSP_GETOSPACE
+#define SOUND_PCM_GETISPACE SNDCTL_DSP_GETISPACE
+#define SOUND_PCM_NONBLOCK SNDCTL_DSP_NONBLOCK
+#define SOUND_PCM_GETCAPS SNDCTL_DSP_GETCAPS
+#define SOUND_PCM_GETTRIGGER SNDCTL_DSP_GETTRIGGER
+#define SOUND_PCM_SETTRIGGER SNDCTL_DSP_SETTRIGGER
+#define SOUND_PCM_SETSYNCRO SNDCTL_DSP_SETSYNCRO
+#define SOUND_PCM_GETIPTR SNDCTL_DSP_GETIPTR
+#define SOUND_PCM_GETOPTR SNDCTL_DSP_GETOPTR
+#define SOUND_PCM_MAPINBUF SNDCTL_DSP_MAPINBUF
+#define SOUND_PCM_MAPOUTBUF SNDCTL_DSP_MAPOUTBUF
+
+/*
+ * ioctl calls to be used in communication with coprocessors and
+ * DSP chips.
+ */
+
+typedef struct copr_buffer {
+ int command; /* Set to 0 if not used */
+ int flags;
+#define CPF_NONE 0x0000
+#define CPF_FIRST 0x0001 /* First block */
+#define CPF_LAST 0x0002 /* Last block */
+ int len;
+ int offs; /* If required by the device (0 if not used) */
+
+ unsigned char data[4000]; /* NOTE! 4000 is not 4k */
+ } copr_buffer;
+
+typedef struct copr_debug_buf {
+ int command; /* Used internally. Set to 0 */
+ int parm1;
+ int parm2;
+ int flags;
+ int len; /* Length of data in bytes */
+ } copr_debug_buf;
+
+typedef struct copr_msg {
+ int len;
+ unsigned char data[4000];
+ } copr_msg;
+
+#define SNDCTL_COPR_RESET _SIO ('C', 0)
+#define SNDCTL_COPR_LOAD _SIOWR('C', 1, copr_buffer)
+#define SNDCTL_COPR_RDATA _SIOWR('C', 2, copr_debug_buf)
+#define SNDCTL_COPR_RCODE _SIOWR('C', 3, copr_debug_buf)
+#define SNDCTL_COPR_WDATA _SIOW ('C', 4, copr_debug_buf)
+#define SNDCTL_COPR_WCODE _SIOW ('C', 5, copr_debug_buf)
+#define SNDCTL_COPR_RUN _SIOWR('C', 6, copr_debug_buf)
+#define SNDCTL_COPR_HALT _SIOWR('C', 7, copr_debug_buf)
+#define SNDCTL_COPR_SENDMSG _SIOWR('C', 8, copr_msg)
+#define SNDCTL_COPR_RCVMSG _SIOR ('C', 9, copr_msg)
+
+/*********************************************
+ * IOCTL commands for /dev/mixer
+ */
+
+/*
+ * Mixer devices
+ *
+ * There can be up to 20 different analog mixer channels. The
+ * SOUND_MIXER_NRDEVICES gives the currently supported maximum.
+ * The SOUND_MIXER_READ_DEVMASK returns a bitmask which tells
+ * the devices supported by the particular mixer.
+ */
+
+#define SOUND_MIXER_NRDEVICES 25
+#define SOUND_MIXER_VOLUME 0
+#define SOUND_MIXER_BASS 1
+#define SOUND_MIXER_TREBLE 2
+#define SOUND_MIXER_SYNTH 3
+#define SOUND_MIXER_PCM 4
+#define SOUND_MIXER_SPEAKER 5
+#define SOUND_MIXER_LINE 6
+#define SOUND_MIXER_MIC 7
+#define SOUND_MIXER_CD 8
+#define SOUND_MIXER_IMIX 9 /* Recording monitor */
+#define SOUND_MIXER_ALTPCM 10
+#define SOUND_MIXER_RECLEV 11 /* Recording level */
+#define SOUND_MIXER_IGAIN 12 /* Input gain */
+#define SOUND_MIXER_OGAIN 13 /* Output gain */
+/*
+ * The AD1848 codec and compatibles have three line level inputs
+ * (line, aux1 and aux2). Since each card manufacturer have assigned
+ * different meanings to these inputs, it's inpractical to assign
+ * specific meanings (line, cd, synth etc.) to them.
+ */
+#define SOUND_MIXER_LINE1 14 /* Input source 1 (aux1) */
+#define SOUND_MIXER_LINE2 15 /* Input source 2 (aux2) */
+#define SOUND_MIXER_LINE3 16 /* Input source 3 (line) */
+#define SOUND_MIXER_DIGITAL1 17 /* Digital (input) 1 */
+#define SOUND_MIXER_DIGITAL2 18 /* Digital (input) 2 */
+#define SOUND_MIXER_DIGITAL3 19 /* Digital (input) 3 */
+#define SOUND_MIXER_PHONEIN 20 /* Phone input */
+#define SOUND_MIXER_PHONEOUT 21 /* Phone output */
+#define SOUND_MIXER_VIDEO 22 /* Video/TV (audio) in */
+#define SOUND_MIXER_RADIO 23 /* Radio in */
+#define SOUND_MIXER_MONITOR 24 /* Monitor (usually mic) volume */
+
+/* Some on/off settings (SOUND_SPECIAL_MIN - SOUND_SPECIAL_MAX) */
+/* Not counted to SOUND_MIXER_NRDEVICES, but use the same number space */
+#define SOUND_ONOFF_MIN 28
+#define SOUND_ONOFF_MAX 30
+
+/* Note! Number 31 cannot be used since the sign bit is reserved */
+#define SOUND_MIXER_NONE 31
+
+/*
+ * The following unsupported macros are no longer functional.
+ * Use SOUND_MIXER_PRIVATE# macros in future.
+ */
+#define SOUND_MIXER_ENHANCE SOUND_MIXER_NONE
+#define SOUND_MIXER_MUTE SOUND_MIXER_NONE
+#define SOUND_MIXER_LOUD SOUND_MIXER_NONE
+
+
+#define SOUND_DEVICE_LABELS {"Vol ", "Bass ", "Trebl", "Synth", "Pcm ", "Spkr ", "Line ", \
+ "Mic ", "CD ", "Mix ", "Pcm2 ", "Rec ", "IGain", "OGain", \
+ "Line1", "Line2", "Line3", "Digital1", "Digital2", "Digital3", \
+ "PhoneIn", "PhoneOut", "Video", "Radio", "Monitor"}
+
+#define SOUND_DEVICE_NAMES {"vol", "bass", "treble", "synth", "pcm", "speaker", "line", \
+ "mic", "cd", "mix", "pcm2", "rec", "igain", "ogain", \
+ "line1", "line2", "line3", "dig1", "dig2", "dig3", \
+ "phin", "phout", "video", "radio", "monitor"}
+
+/* Device bitmask identifiers */
+
+#define SOUND_MIXER_RECSRC 0xff /* Arg contains a bit for each recording source */
+#define SOUND_MIXER_DEVMASK 0xfe /* Arg contains a bit for each supported device */
+#define SOUND_MIXER_RECMASK 0xfd /* Arg contains a bit for each supported recording source */
+#define SOUND_MIXER_CAPS 0xfc
+# define SOUND_CAP_EXCL_INPUT 0x00000001 /* Only one recording source at a time */
+#define SOUND_MIXER_STEREODEVS 0xfb /* Mixer channels supporting stereo */
+#define SOUND_MIXER_OUTSRC 0xfa /* Arg contains a bit for each input source to output */
+#define SOUND_MIXER_OUTMASK 0xf9 /* Arg contains a bit for each supported input source to output */
+
+/* Device mask bits */
+
+#define SOUND_MASK_VOLUME (1 << SOUND_MIXER_VOLUME)
+#define SOUND_MASK_BASS (1 << SOUND_MIXER_BASS)
+#define SOUND_MASK_TREBLE (1 << SOUND_MIXER_TREBLE)
+#define SOUND_MASK_SYNTH (1 << SOUND_MIXER_SYNTH)
+#define SOUND_MASK_PCM (1 << SOUND_MIXER_PCM)
+#define SOUND_MASK_SPEAKER (1 << SOUND_MIXER_SPEAKER)
+#define SOUND_MASK_LINE (1 << SOUND_MIXER_LINE)
+#define SOUND_MASK_MIC (1 << SOUND_MIXER_MIC)
+#define SOUND_MASK_CD (1 << SOUND_MIXER_CD)
+#define SOUND_MASK_IMIX (1 << SOUND_MIXER_IMIX)
+#define SOUND_MASK_ALTPCM (1 << SOUND_MIXER_ALTPCM)
+#define SOUND_MASK_RECLEV (1 << SOUND_MIXER_RECLEV)
+#define SOUND_MASK_IGAIN (1 << SOUND_MIXER_IGAIN)
+#define SOUND_MASK_OGAIN (1 << SOUND_MIXER_OGAIN)
+#define SOUND_MASK_LINE1 (1 << SOUND_MIXER_LINE1)
+#define SOUND_MASK_LINE2 (1 << SOUND_MIXER_LINE2)
+#define SOUND_MASK_LINE3 (1 << SOUND_MIXER_LINE3)
+#define SOUND_MASK_DIGITAL1 (1 << SOUND_MIXER_DIGITAL1)
+#define SOUND_MASK_DIGITAL2 (1 << SOUND_MIXER_DIGITAL2)
+#define SOUND_MASK_DIGITAL3 (1 << SOUND_MIXER_DIGITAL3)
+#define SOUND_MASK_PHONEIN (1 << SOUND_MIXER_PHONEIN)
+#define SOUND_MASK_PHONEOUT (1 << SOUND_MIXER_PHONEOUT)
+#define SOUND_MASK_RADIO (1 << SOUND_MIXER_RADIO)
+#define SOUND_MASK_VIDEO (1 << SOUND_MIXER_VIDEO)
+#define SOUND_MASK_MONITOR (1 << SOUND_MIXER_MONITOR)
+
+/* Obsolete macros */
+#define SOUND_MASK_MUTE (1 << SOUND_MIXER_MUTE)
+#define SOUND_MASK_ENHANCE (1 << SOUND_MIXER_ENHANCE)
+#define SOUND_MASK_LOUD (1 << SOUND_MIXER_LOUD)
+
+#define MIXER_READ(dev) _SIOR('M', dev, int)
+#define SOUND_MIXER_READ_VOLUME MIXER_READ(SOUND_MIXER_VOLUME)
+#define SOUND_MIXER_READ_BASS MIXER_READ(SOUND_MIXER_BASS)
+#define SOUND_MIXER_READ_TREBLE MIXER_READ(SOUND_MIXER_TREBLE)
+#define SOUND_MIXER_READ_SYNTH MIXER_READ(SOUND_MIXER_SYNTH)
+#define SOUND_MIXER_READ_PCM MIXER_READ(SOUND_MIXER_PCM)
+#define SOUND_MIXER_READ_SPEAKER MIXER_READ(SOUND_MIXER_SPEAKER)
+#define SOUND_MIXER_READ_LINE MIXER_READ(SOUND_MIXER_LINE)
+#define SOUND_MIXER_READ_MIC MIXER_READ(SOUND_MIXER_MIC)
+#define SOUND_MIXER_READ_CD MIXER_READ(SOUND_MIXER_CD)
+#define SOUND_MIXER_READ_IMIX MIXER_READ(SOUND_MIXER_IMIX)
+#define SOUND_MIXER_READ_ALTPCM MIXER_READ(SOUND_MIXER_ALTPCM)
+#define SOUND_MIXER_READ_RECLEV MIXER_READ(SOUND_MIXER_RECLEV)
+#define SOUND_MIXER_READ_IGAIN MIXER_READ(SOUND_MIXER_IGAIN)
+#define SOUND_MIXER_READ_OGAIN MIXER_READ(SOUND_MIXER_OGAIN)
+#define SOUND_MIXER_READ_LINE1 MIXER_READ(SOUND_MIXER_LINE1)
+#define SOUND_MIXER_READ_LINE2 MIXER_READ(SOUND_MIXER_LINE2)
+#define SOUND_MIXER_READ_LINE3 MIXER_READ(SOUND_MIXER_LINE3)
+
+/* Obsolete macros */
+#define SOUND_MIXER_READ_MUTE MIXER_READ(SOUND_MIXER_MUTE)
+#define SOUND_MIXER_READ_ENHANCE MIXER_READ(SOUND_MIXER_ENHANCE)
+#define SOUND_MIXER_READ_LOUD MIXER_READ(SOUND_MIXER_LOUD)
+
+#define SOUND_MIXER_READ_RECSRC MIXER_READ(SOUND_MIXER_RECSRC)
+#define SOUND_MIXER_READ_DEVMASK MIXER_READ(SOUND_MIXER_DEVMASK)
+#define SOUND_MIXER_READ_RECMASK MIXER_READ(SOUND_MIXER_RECMASK)
+#define SOUND_MIXER_READ_STEREODEVS MIXER_READ(SOUND_MIXER_STEREODEVS)
+#define SOUND_MIXER_READ_CAPS MIXER_READ(SOUND_MIXER_CAPS)
+
+#define MIXER_WRITE(dev) _SIOWR('M', dev, int)
+#define SOUND_MIXER_WRITE_VOLUME MIXER_WRITE(SOUND_MIXER_VOLUME)
+#define SOUND_MIXER_WRITE_BASS MIXER_WRITE(SOUND_MIXER_BASS)
+#define SOUND_MIXER_WRITE_TREBLE MIXER_WRITE(SOUND_MIXER_TREBLE)
+#define SOUND_MIXER_WRITE_SYNTH MIXER_WRITE(SOUND_MIXER_SYNTH)
+#define SOUND_MIXER_WRITE_PCM MIXER_WRITE(SOUND_MIXER_PCM)
+#define SOUND_MIXER_WRITE_SPEAKER MIXER_WRITE(SOUND_MIXER_SPEAKER)
+#define SOUND_MIXER_WRITE_LINE MIXER_WRITE(SOUND_MIXER_LINE)
+#define SOUND_MIXER_WRITE_MIC MIXER_WRITE(SOUND_MIXER_MIC)
+#define SOUND_MIXER_WRITE_CD MIXER_WRITE(SOUND_MIXER_CD)
+#define SOUND_MIXER_WRITE_IMIX MIXER_WRITE(SOUND_MIXER_IMIX)
+#define SOUND_MIXER_WRITE_ALTPCM MIXER_WRITE(SOUND_MIXER_ALTPCM)
+#define SOUND_MIXER_WRITE_RECLEV MIXER_WRITE(SOUND_MIXER_RECLEV)
+#define SOUND_MIXER_WRITE_IGAIN MIXER_WRITE(SOUND_MIXER_IGAIN)
+#define SOUND_MIXER_WRITE_OGAIN MIXER_WRITE(SOUND_MIXER_OGAIN)
+#define SOUND_MIXER_WRITE_LINE1 MIXER_WRITE(SOUND_MIXER_LINE1)
+#define SOUND_MIXER_WRITE_LINE2 MIXER_WRITE(SOUND_MIXER_LINE2)
+#define SOUND_MIXER_WRITE_LINE3 MIXER_WRITE(SOUND_MIXER_LINE3)
+
+/* Obsolete macros */
+#define SOUND_MIXER_WRITE_MUTE MIXER_WRITE(SOUND_MIXER_MUTE)
+#define SOUND_MIXER_WRITE_ENHANCE MIXER_WRITE(SOUND_MIXER_ENHANCE)
+#define SOUND_MIXER_WRITE_LOUD MIXER_WRITE(SOUND_MIXER_LOUD)
+
+#define SOUND_MIXER_WRITE_RECSRC MIXER_WRITE(SOUND_MIXER_RECSRC)
+
+typedef struct mixer_info
+{
+ char id[16];
+ char name[32];
+ int modify_counter;
+ int fillers[10];
+} mixer_info;
+
+typedef struct _old_mixer_info /* Obsolete */
+{
+ char id[16];
+ char name[32];
+} _old_mixer_info;
+
+#define SOUND_MIXER_INFO _SIOR ('M', 101, mixer_info)
+#define SOUND_OLD_MIXER_INFO _SIOR ('M', 101, _old_mixer_info)
+
+/*
+ * A mechanism for accessing "proprietary" mixer features. This method
+ * permits passing 128 bytes of arbitrary data between a mixer application
+ * and the mixer driver. Interpretation of the record is defined by
+ * the particular mixer driver.
+ */
+typedef unsigned char mixer_record[128];
+
+#define SOUND_MIXER_ACCESS _SIOWR('M', 102, mixer_record)
+
+/*
+ * Two ioctls for special souncard function
+ */
+#define SOUND_MIXER_AGC _SIOWR('M', 103, int)
+#define SOUND_MIXER_3DSE _SIOWR('M', 104, int)
+
+/*
+ * The SOUND_MIXER_PRIVATE# commands can be redefined by low level drivers.
+ * These features can be used when accessing device specific features.
+ */
+#define SOUND_MIXER_PRIVATE1 _SIOWR('M', 111, int)
+#define SOUND_MIXER_PRIVATE2 _SIOWR('M', 112, int)
+#define SOUND_MIXER_PRIVATE3 _SIOWR('M', 113, int)
+#define SOUND_MIXER_PRIVATE4 _SIOWR('M', 114, int)
+#define SOUND_MIXER_PRIVATE5 _SIOWR('M', 115, int)
+
+/*
+ * SOUND_MIXER_GETLEVELS and SOUND_MIXER_SETLEVELS calls can be used
+ * for querying current mixer settings from the driver and for loading
+ * default volume settings _prior_ activating the mixer (loading
+ * doesn't affect current state of the mixer hardware). These calls
+ * are for internal use only.
+ */
+
+typedef struct mixer_vol_table {
+ int num; /* Index to volume table */
+ char name[32];
+ int levels[32];
+} mixer_vol_table;
+
+#define SOUND_MIXER_GETLEVELS _SIOWR('M', 116, mixer_vol_table)
+#define SOUND_MIXER_SETLEVELS _SIOWR('M', 117, mixer_vol_table)
+
+/*
+ * An ioctl for identifying the driver version. It will return value
+ * of the SOUND_VERSION macro used when compiling the driver.
+ * This call was introduced in OSS version 3.6 and it will not work
+ * with earlier versions (returns EINVAL).
+ */
+#define OSS_GETVERSION _SIOR ('M', 118, int)
+
+/*
+ * Level 2 event types for /dev/sequencer
+ */
+
+/*
+ * The 4 most significant bits of byte 0 specify the class of
+ * the event:
+ *
+ * 0x8X = system level events,
+ * 0x9X = device/port specific events, event[1] = device/port,
+ * The last 4 bits give the subtype:
+ * 0x02 = Channel event (event[3] = chn).
+ * 0x01 = note event (event[4] = note).
+ * (0x01 is not used alone but always with bit 0x02).
+ * event[2] = MIDI message code (0x80=note off etc.)
+ *
+ */
+
+#define EV_SEQ_LOCAL 0x80
+#define EV_TIMING 0x81
+#define EV_CHN_COMMON 0x92
+#define EV_CHN_VOICE 0x93
+#define EV_SYSEX 0x94
+/*
+ * Event types 200 to 220 are reserved for application use.
+ * These numbers will not be used by the driver.
+ */
+
+/*
+ * Events for event type EV_CHN_VOICE
+ */
+
+#define MIDI_NOTEOFF 0x80
+#define MIDI_NOTEON 0x90
+#define MIDI_KEY_PRESSURE 0xA0
+
+/*
+ * Events for event type EV_CHN_COMMON
+ */
+
+#define MIDI_CTL_CHANGE 0xB0
+#define MIDI_PGM_CHANGE 0xC0
+#define MIDI_CHN_PRESSURE 0xD0
+#define MIDI_PITCH_BEND 0xE0
+
+#define MIDI_SYSTEM_PREFIX 0xF0
+
+/*
+ * Timer event types
+ */
+#define TMR_WAIT_REL 1 /* Time relative to the prev time */
+#define TMR_WAIT_ABS 2 /* Absolute time since TMR_START */
+#define TMR_STOP 3
+#define TMR_START 4
+#define TMR_CONTINUE 5
+#define TMR_TEMPO 6
+#define TMR_ECHO 8
+#define TMR_CLOCK 9 /* MIDI clock */
+#define TMR_SPP 10 /* Song position pointer */
+#define TMR_TIMESIG 11 /* Time signature */
+
+/*
+ * Local event types
+ */
+#define LOCL_STARTAUDIO 1
+
+#if (!defined(__KERNEL__) && !defined(KERNEL) && !defined(INKERNEL) && !defined(_KERNEL)) || defined(USE_SEQ_MACROS)
+/*
+ * Some convenience macros to simplify programming of the
+ * /dev/sequencer interface
+ *
+ * These macros define the API which should be used when possible.
+ */
+#define SEQ_DECLAREBUF() SEQ_USE_EXTBUF()
+
+void seqbuf_dump(void); /* This function must be provided by programs */
+
+extern int OSS_init(int seqfd, int buflen);
+extern void OSS_seqbuf_dump(int fd, unsigned char *buf, int buflen);
+extern void OSS_seq_advbuf(int len, int fd, unsigned char *buf, int buflen);
+extern void OSS_seq_needbuf(int len, int fd, unsigned char *buf, int buflen);
+extern void OSS_patch_caching(int dev, int chn, int patch,
+ int fd, unsigned char *buf, int buflen);
+extern void OSS_drum_caching(int dev, int chn, int patch,
+ int fd, unsigned char *buf, int buflen);
+extern void OSS_write_patch(int fd, unsigned char *buf, int len);
+extern int OSS_write_patch2(int fd, unsigned char *buf, int len);
+
+#define SEQ_PM_DEFINES int __foo_bar___
+#ifdef OSSLIB
+# define SEQ_USE_EXTBUF() \
+ extern unsigned char *_seqbuf; \
+ extern int _seqbuflen;extern int _seqbufptr
+# define SEQ_DEFINEBUF(len) SEQ_USE_EXTBUF();static int _requested_seqbuflen=len
+# define _SEQ_ADVBUF(len) OSS_seq_advbuf(len, seqfd, _seqbuf, _seqbuflen)
+# define _SEQ_NEEDBUF(len) OSS_seq_needbuf(len, seqfd, _seqbuf, _seqbuflen)
+# define SEQ_DUMPBUF() OSS_seqbuf_dump(seqfd, _seqbuf, _seqbuflen)
+
+# define SEQ_LOAD_GMINSTR(dev, instr) \
+ OSS_patch_caching(dev, -1, instr, seqfd, _seqbuf, _seqbuflen)
+# define SEQ_LOAD_GMDRUM(dev, drum) \
+ OSS_drum_caching(dev, -1, drum, seqfd, _seqbuf, _seqbuflen)
+#else /* !OSSLIB */
+
+# define SEQ_LOAD_GMINSTR(dev, instr)
+# define SEQ_LOAD_GMDRUM(dev, drum)
+
+# define SEQ_USE_EXTBUF() \
+ extern unsigned char _seqbuf[]; \
+ extern int _seqbuflen;extern int _seqbufptr
+
+#ifndef USE_SIMPLE_MACROS
+/* Sample seqbuf_dump() implementation:
+ *
+ * SEQ_DEFINEBUF (2048); -- Defines a buffer for 2048 bytes
+ *
+ * int seqfd; -- The file descriptor for /dev/sequencer.
+ *
+ * void
+ * seqbuf_dump ()
+ * {
+ * if (_seqbufptr)
+ * if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ * {
+ * perror ("write /dev/sequencer");
+ * exit (-1);
+ * }
+ * _seqbufptr = 0;
+ * }
+ */
+
+#define SEQ_DEFINEBUF(len) unsigned char _seqbuf[len]; int _seqbuflen = len;int _seqbufptr = 0
+#define _SEQ_NEEDBUF(len) if ((_seqbufptr+(len)) > _seqbuflen) seqbuf_dump()
+#define _SEQ_ADVBUF(len) _seqbufptr += len
+#define SEQ_DUMPBUF seqbuf_dump
+#else
+/*
+ * This variation of the sequencer macros is used just to format one event
+ * using fixed buffer.
+ *
+ * The program using the macro library must define the following macros before
+ * using this library.
+ *
+ * #define _seqbuf name of the buffer (unsigned char[])
+ * #define _SEQ_ADVBUF(len) If the applic needs to know the exact
+ * size of the event, this macro can be used.
+ * Otherwise this must be defined as empty.
+ * #define _seqbufptr Define the name of index variable or 0 if
+ * not required.
+ */
+#define _SEQ_NEEDBUF(len) /* empty */
+#endif
+#endif /* !OSSLIB */
+
+#define SEQ_VOLUME_MODE(dev, mode) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_VOLMODE;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (mode);\
+ _seqbuf[_seqbufptr+4] = 0;\
+ _seqbuf[_seqbufptr+5] = 0;\
+ _seqbuf[_seqbufptr+6] = 0;\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+/*
+ * Midi voice messages
+ */
+
+#define _CHN_VOICE(dev, event, chn, note, parm) \
+ {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = EV_CHN_VOICE;\
+ _seqbuf[_seqbufptr+1] = (dev);\
+ _seqbuf[_seqbufptr+2] = (event);\
+ _seqbuf[_seqbufptr+3] = (chn);\
+ _seqbuf[_seqbufptr+4] = (note);\
+ _seqbuf[_seqbufptr+5] = (parm);\
+ _seqbuf[_seqbufptr+6] = (0);\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_START_NOTE(dev, chn, note, vol) \
+ _CHN_VOICE(dev, MIDI_NOTEON, chn, note, vol)
+
+#define SEQ_STOP_NOTE(dev, chn, note, vol) \
+ _CHN_VOICE(dev, MIDI_NOTEOFF, chn, note, vol)
+
+#define SEQ_KEY_PRESSURE(dev, chn, note, pressure) \
+ _CHN_VOICE(dev, MIDI_KEY_PRESSURE, chn, note, pressure)
+
+/*
+ * Midi channel messages
+ */
+
+#define _CHN_COMMON(dev, event, chn, p1, p2, w14) \
+ {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = EV_CHN_COMMON;\
+ _seqbuf[_seqbufptr+1] = (dev);\
+ _seqbuf[_seqbufptr+2] = (event);\
+ _seqbuf[_seqbufptr+3] = (chn);\
+ _seqbuf[_seqbufptr+4] = (p1);\
+ _seqbuf[_seqbufptr+5] = (p2);\
+ *(short *)&_seqbuf[_seqbufptr+6] = (w14);\
+ _SEQ_ADVBUF(8);}
+/*
+ * SEQ_SYSEX permits sending of sysex messages. (It may look that it permits
+ * sending any MIDI bytes but it's absolutely not possible. Trying to do
+ * so _will_ cause problems with MPU401 intelligent mode).
+ *
+ * Sysex messages are sent in blocks of 1 to 6 bytes. Longer messages must be
+ * sent by calling SEQ_SYSEX() several times (there must be no other events
+ * between them). First sysex fragment must have 0xf0 in the first byte
+ * and the last byte (buf[len-1] of the last fragment must be 0xf7. No byte
+ * between these sysex start and end markers cannot be larger than 0x7f. Also
+ * lengths of each fragments (except the last one) must be 6.
+ *
+ * Breaking the above rules may work with some MIDI ports but is likely to
+ * cause fatal problems with some other devices (such as MPU401).
+ */
+#define SEQ_SYSEX(dev, buf, len) \
+ {int ii, ll=(len); \
+ unsigned char *bufp=buf;\
+ if (ll>6)ll=6;\
+ _SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = EV_SYSEX;\
+ _seqbuf[_seqbufptr+1] = (dev);\
+ for(ii=0;ii<ll;ii++)\
+ _seqbuf[_seqbufptr+ii+2] = bufp[ii];\
+ for(ii=ll;ii<6;ii++)\
+ _seqbuf[_seqbufptr+ii+2] = 0xff;\
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_CHN_PRESSURE(dev, chn, pressure) \
+ _CHN_COMMON(dev, MIDI_CHN_PRESSURE, chn, pressure, 0, 0)
+
+#define SEQ_SET_PATCH SEQ_PGM_CHANGE
+#ifdef OSSLIB
+# define SEQ_PGM_CHANGE(dev, chn, patch) \
+ {OSS_patch_caching(dev, chn, patch, seqfd, _seqbuf, _seqbuflen); \
+ _CHN_COMMON(dev, MIDI_PGM_CHANGE, chn, patch, 0, 0);}
+#else
+# define SEQ_PGM_CHANGE(dev, chn, patch) \
+ _CHN_COMMON(dev, MIDI_PGM_CHANGE, chn, patch, 0, 0)
+#endif
+
+#define SEQ_CONTROL(dev, chn, controller, value) \
+ _CHN_COMMON(dev, MIDI_CTL_CHANGE, chn, controller, 0, value)
+
+#define SEQ_BENDER(dev, chn, value) \
+ _CHN_COMMON(dev, MIDI_PITCH_BEND, chn, 0, 0, value)
+
+
+#define SEQ_V2_X_CONTROL(dev, voice, controller, value) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+ _seqbuf[_seqbufptr+1] = SEQ_CONTROLLER;\
+ _seqbuf[_seqbufptr+2] = (dev);\
+ _seqbuf[_seqbufptr+3] = (voice);\
+ _seqbuf[_seqbufptr+4] = (controller);\
+ _seqbuf[_seqbufptr+5] = ((value)&0xff);\
+ _seqbuf[_seqbufptr+6] = ((value>>8)&0xff);\
+ _seqbuf[_seqbufptr+7] = 0;\
+ _SEQ_ADVBUF(8);}
+/*
+ * The following 5 macros are incorrectly implemented and obsolete.
+ * Use SEQ_BENDER and SEQ_CONTROL (with proper controller) instead.
+ */
+#define SEQ_PITCHBEND(dev, voice, value) SEQ_V2_X_CONTROL(dev, voice, CTRL_PITCH_BENDER, value)
+#define SEQ_BENDER_RANGE(dev, voice, value) SEQ_V2_X_CONTROL(dev, voice, CTRL_PITCH_BENDER_RANGE, value)
+#define SEQ_EXPRESSION(dev, voice, value) SEQ_CONTROL(dev, voice, CTL_EXPRESSION, value*128)
+#define SEQ_MAIN_VOLUME(dev, voice, value) SEQ_CONTROL(dev, voice, CTL_MAIN_VOLUME, (value*16383)/100)
+#define SEQ_PANNING(dev, voice, pos) SEQ_CONTROL(dev, voice, CTL_PAN, (pos+128) / 2)
+
+/*
+ * Timing and synchronization macros
+ */
+
+#define _TIMER_EVENT(ev, parm) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr+0] = EV_TIMING; \
+ _seqbuf[_seqbufptr+1] = (ev); \
+ _seqbuf[_seqbufptr+2] = 0;\
+ _seqbuf[_seqbufptr+3] = 0;\
+ *(unsigned int *)&_seqbuf[_seqbufptr+4] = (parm); \
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_START_TIMER() _TIMER_EVENT(TMR_START, 0)
+#define SEQ_STOP_TIMER() _TIMER_EVENT(TMR_STOP, 0)
+#define SEQ_CONTINUE_TIMER() _TIMER_EVENT(TMR_CONTINUE, 0)
+#define SEQ_WAIT_TIME(ticks) _TIMER_EVENT(TMR_WAIT_ABS, ticks)
+#define SEQ_DELTA_TIME(ticks) _TIMER_EVENT(TMR_WAIT_REL, ticks)
+#define SEQ_ECHO_BACK(key) _TIMER_EVENT(TMR_ECHO, key)
+#define SEQ_SET_TEMPO(value) _TIMER_EVENT(TMR_TEMPO, value)
+#define SEQ_SONGPOS(pos) _TIMER_EVENT(TMR_SPP, pos)
+#define SEQ_TIME_SIGNATURE(sig) _TIMER_EVENT(TMR_TIMESIG, sig)
+
+/*
+ * Local control events
+ */
+
+#define _LOCAL_EVENT(ev, parm) {_SEQ_NEEDBUF(8);\
+ _seqbuf[_seqbufptr+0] = EV_SEQ_LOCAL; \
+ _seqbuf[_seqbufptr+1] = (ev); \
+ _seqbuf[_seqbufptr+2] = 0;\
+ _seqbuf[_seqbufptr+3] = 0;\
+ *(unsigned int *)&_seqbuf[_seqbufptr+4] = (parm); \
+ _SEQ_ADVBUF(8);}
+
+#define SEQ_PLAYAUDIO(devmask) _LOCAL_EVENT(LOCL_STARTAUDIO, devmask)
+/*
+ * Events for the level 1 interface only
+ */
+
+#define SEQ_MIDIOUT(device, byte) {_SEQ_NEEDBUF(4);\
+ _seqbuf[_seqbufptr] = SEQ_MIDIPUTC;\
+ _seqbuf[_seqbufptr+1] = (byte);\
+ _seqbuf[_seqbufptr+2] = (device);\
+ _seqbuf[_seqbufptr+3] = 0;\
+ _SEQ_ADVBUF(4);}
+
+/*
+ * Patch loading.
+ */
+#ifdef OSSLIB
+# define SEQ_WRPATCH(patchx, len) \
+ OSS_write_patch(seqfd, (char*)(patchx), len)
+# define SEQ_WRPATCH2(patchx, len) \
+ OSS_write_patch2(seqfd, (char*)(patchx), len)
+#else
+# define SEQ_WRPATCH(patchx, len) \
+ {if (_seqbufptr) SEQ_DUMPBUF();\
+ if (write(seqfd, (char*)(patchx), len)==-1) \
+ perror("Write patch: /dev/sequencer");}
+# define SEQ_WRPATCH2(patchx, len) \
+ (SEQ_DUMPBUF(), write(seqfd, (char*)(patchx), len))
+#endif
+
+#endif
+#endif
diff --git a/pfinet/linux-src/include/linux/soundmodem.h b/pfinet/linux-src/include/linux/soundmodem.h
new file mode 100644
index 00000000..10d0799d
--- /dev/null
+++ b/pfinet/linux-src/include/linux/soundmodem.h
@@ -0,0 +1,90 @@
+/*
+ * The Linux soundcard driver for 1200 baud and 9600 baud packet radio
+ * (C) 1996-1998 by Thomas Sailer, HB9JNX/AE4WA
+ */
+
+#ifndef _SOUNDMODEM_H
+#define _SOUNDMODEM_H
+
+/* -------------------------------------------------------------------- */
+/*
+ * structs for the IOCTL commands
+ */
+
+struct sm_debug_data {
+ unsigned int int_rate;
+ unsigned int mod_cycles;
+ unsigned int demod_cycles;
+ unsigned int dma_residue;
+};
+
+struct sm_diag_data {
+ unsigned int mode;
+ unsigned int flags;
+ unsigned int samplesperbit;
+ unsigned int datalen;
+ short *data;
+};
+
+struct sm_mixer_data {
+ unsigned int mixer_type;
+ unsigned int sample_rate;
+ unsigned int bit_rate;
+ unsigned int reg;
+ unsigned int data;
+};
+
+struct sm_config {
+ int hardware;
+ int mode;
+};
+
+struct sm_ioctl {
+ int cmd;
+ union {
+ struct sm_config cfg;
+ struct sm_diag_data diag;
+ struct sm_mixer_data mix;
+ struct sm_debug_data dbg;
+ } data;
+};
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * diagnose modes
+ */
+#define SM_DIAGMODE_OFF 0
+#define SM_DIAGMODE_INPUT 1
+#define SM_DIAGMODE_DEMOD 2
+#define SM_DIAGMODE_CONSTELLATION 3
+
+/*
+ * diagnose flags
+ */
+#define SM_DIAGFLAG_DCDGATE (1<<0)
+#define SM_DIAGFLAG_VALID (1<<1)
+
+/*
+ * mixer types
+ */
+#define SM_MIXER_INVALID 0
+#define SM_MIXER_AD1848 0x10
+#define SM_MIXER_CRYSTAL 0x11
+#define SM_MIXER_CT1335 0x20
+#define SM_MIXER_CT1345 0x21
+#define SM_MIXER_CT1745 0x22
+
+/*
+ * ioctl values
+ */
+#define SMCTL_DIAGNOSE 0x82
+#define SMCTL_GETMIXER 0x83
+#define SMCTL_SETMIXER 0x84
+#define SMCTL_GETDEBUG 0x85
+
+/* -------------------------------------------------------------------- */
+
+#endif /* _SOUNDMODEM_H */
+
+/* --------------------------------------------------------------------- */
diff --git a/pfinet/linux-src/include/linux/stallion.h b/pfinet/linux-src/include/linux/stallion.h
new file mode 100644
index 00000000..35274488
--- /dev/null
+++ b/pfinet/linux-src/include/linux/stallion.h
@@ -0,0 +1,156 @@
+/*****************************************************************************/
+
+/*
+ * stallion.h -- stallion multiport serial driver.
+ *
+ * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au).
+ * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*****************************************************************************/
+#ifndef _STALLION_H
+#define _STALLION_H
+/*****************************************************************************/
+
+/*
+ * Define important driver constants here.
+ */
+#define STL_MAXBRDS 4
+#define STL_MAXPANELS 4
+#define STL_MAXBANKS 8
+#define STL_PORTSPERPANEL 16
+#define STL_MAXPORTS 64
+#define STL_MAXDEVS (STL_MAXBRDS * STL_MAXPORTS)
+
+
+/*
+ * Define a set of structures to hold all the board/panel/port info
+ * for our ports. These will be dynamically allocated as required.
+ */
+
+/*
+ * Define a ring queue structure for each port. This will hold the
+ * TX data waiting to be output. Characters are fed into this buffer
+ * from the line discipline (or even direct from user space!) and
+ * then fed into the UARTs during interrupts. Will use a classic ring
+ * queue here for this. The good thing about this type of ring queue
+ * is that the head and tail pointers can be updated without interrupt
+ * protection - since "write" code only needs to change the head, and
+ * interrupt code only needs to change the tail.
+ */
+typedef struct {
+ char *buf;
+ char *head;
+ char *tail;
+} stlrq_t;
+
+/*
+ * Port, panel and board structures to hold status info about each.
+ * The board structure contains pointers to structures for each panel
+ * connected to it, and in turn each panel structure contains pointers
+ * for each port structure for each port on that panel. Note that
+ * the port structure also contains the board and panel number that it
+ * is associated with, this makes it (fairly) easy to get back to the
+ * board/panel info for a port.
+ */
+typedef struct stlport {
+ unsigned long magic;
+ int portnr;
+ int panelnr;
+ int brdnr;
+ int ioaddr;
+ int uartaddr;
+ int pagenr;
+ int istate;
+ int flags;
+ int baud_base;
+ int custom_divisor;
+ int close_delay;
+ int closing_wait;
+ int refcount;
+ int openwaitcnt;
+ int brklen;
+ long session;
+ long pgrp;
+ unsigned int sigs;
+ unsigned int rxignoremsk;
+ unsigned int rxmarkmsk;
+ unsigned int imr;
+ unsigned int crenable;
+ unsigned long clk;
+ unsigned long hwid;
+ void *uartp;
+ struct tty_struct *tty;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+ struct termios normaltermios;
+ struct termios callouttermios;
+ struct tq_struct tqueue;
+ comstats_t stats;
+ stlrq_t tx;
+} stlport_t;
+
+typedef struct stlpanel {
+ unsigned long magic;
+ int panelnr;
+ int brdnr;
+ int pagenr;
+ int nrports;
+ int iobase;
+ void *uartp;
+ void (*isr)(struct stlpanel *panelp, unsigned int iobase);
+ unsigned int hwid;
+ unsigned int ackmask;
+ stlport_t *ports[STL_PORTSPERPANEL];
+} stlpanel_t;
+
+typedef struct stlbrd {
+ unsigned long magic;
+ int brdnr;
+ int brdtype;
+ int state;
+ int nrpanels;
+ int nrports;
+ int nrbnks;
+ int irq;
+ int irqtype;
+ void (*isr)(struct stlbrd *brdp);
+ unsigned int ioaddr1;
+ unsigned int ioaddr2;
+ unsigned int iosize1;
+ unsigned int iosize2;
+ unsigned int iostatus;
+ unsigned int ioctrl;
+ unsigned int ioctrlval;
+ unsigned int hwid;
+ unsigned long clk;
+ unsigned int bnkpageaddr[STL_MAXBANKS];
+ unsigned int bnkstataddr[STL_MAXBANKS];
+ stlpanel_t *bnk2panel[STL_MAXBANKS];
+ stlpanel_t *panels[STL_MAXPANELS];
+} stlbrd_t;
+
+
+/*
+ * Define MAGIC numbers used for above structures.
+ */
+#define STL_PORTMAGIC 0x5a7182c9
+#define STL_PANELMAGIC 0x7ef621a1
+#define STL_BOARDMAGIC 0xa2267f52
+
+/*****************************************************************************/
+#endif
diff --git a/pfinet/linux-src/include/linux/stat.h b/pfinet/linux-src/include/linux/stat.h
new file mode 100644
index 00000000..e43e241f
--- /dev/null
+++ b/pfinet/linux-src/include/linux/stat.h
@@ -0,0 +1,57 @@
+#ifndef _LINUX_STAT_H
+#define _LINUX_STAT_H
+
+#ifdef __KERNEL__
+
+#include <asm/stat.h>
+
+#endif
+
+#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
+
+#define S_IFMT 00170000
+#define S_IFSOCK 0140000
+#define S_IFLNK 0120000
+#define S_IFREG 0100000
+#define S_IFBLK 0060000
+#define S_IFDIR 0040000
+#define S_IFCHR 0020000
+#define S_IFIFO 0010000
+#define S_ISUID 0004000
+#define S_ISGID 0002000
+#define S_ISVTX 0001000
+
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
+#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
+#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+
+#define S_IRWXU 00700
+#define S_IRUSR 00400
+#define S_IWUSR 00200
+#define S_IXUSR 00100
+
+#define S_IRWXG 00070
+#define S_IRGRP 00040
+#define S_IWGRP 00020
+#define S_IXGRP 00010
+
+#define S_IRWXO 00007
+#define S_IROTH 00004
+#define S_IWOTH 00002
+#define S_IXOTH 00001
+
+#endif
+
+#ifdef __KERNEL__
+#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO)
+#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
+#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH)
+#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH)
+#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/stddef.h b/pfinet/linux-src/include/linux/stddef.h
new file mode 100644
index 00000000..dfa23221
--- /dev/null
+++ b/pfinet/linux-src/include/linux/stddef.h
@@ -0,0 +1,14 @@
+#ifndef _LINUX_STDDEF_H
+#define _LINUX_STDDEF_H
+
+#undef NULL
+#if defined(__cplusplus)
+#define NULL 0
+#else
+#define NULL ((void *)0)
+#endif
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+#endif
diff --git a/pfinet/linux-src/include/linux/string.h b/pfinet/linux-src/include/linux/string.h
new file mode 100644
index 00000000..9ea63cd0
--- /dev/null
+++ b/pfinet/linux-src/include/linux/string.h
@@ -0,0 +1,43 @@
+#ifndef _LINUX_STRING_H_
+#define _LINUX_STRING_H_
+
+#include <linux/types.h> /* for size_t */
+#include <linux/stddef.h> /* for NULL */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern char * ___strtok;
+extern char * strcpy(char *,const char *);
+extern char * strncpy(char *,const char *, __kernel_size_t);
+extern char * strcat(char *, const char *);
+extern char * strncat(char *, const char *, __kernel_size_t);
+extern char * strchr(const char *,int);
+extern char * strrchr(const char *,int);
+extern char * strpbrk(const char *,const char *);
+extern char * strtok(char *,const char *);
+extern char * strstr(const char *,const char *);
+extern __kernel_size_t strlen(const char *);
+extern __kernel_size_t strnlen(const char *,__kernel_size_t);
+extern __kernel_size_t strspn(const char *,const char *);
+extern int strcmp(const char *,const char *);
+extern int strncmp(const char *,const char *,__kernel_size_t);
+extern int strnicmp(const char *, const char *, __kernel_size_t);
+
+extern void * memset(void *,int,__kernel_size_t);
+extern void * memcpy(void *,const void *,__kernel_size_t);
+extern void * memmove(void *,const void *,__kernel_size_t);
+extern void * memscan(void *,int,__kernel_size_t);
+extern int memcmp(const void *,const void *,__kernel_size_t);
+
+/*
+ * Include machine specific inline routines
+ */
+#include <asm/string.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LINUX_STRING_H_ */
diff --git a/pfinet/linux-src/include/linux/swap.h b/pfinet/linux-src/include/linux/swap.h
new file mode 100644
index 00000000..0e3d2763
--- /dev/null
+++ b/pfinet/linux-src/include/linux/swap.h
@@ -0,0 +1,175 @@
+#ifndef _LINUX_SWAP_H
+#define _LINUX_SWAP_H
+
+#include <asm/page.h>
+
+#define SWAP_FLAG_PREFER 0x8000 /* set if swap priority specified */
+#define SWAP_FLAG_PRIO_MASK 0x7fff
+#define SWAP_FLAG_PRIO_SHIFT 0
+
+#define MAX_SWAPFILES 8
+
+union swap_header {
+ struct
+ {
+ char reserved[PAGE_SIZE - 10];
+ char magic[10];
+ } magic;
+ struct
+ {
+ char bootbits[1024]; /* Space for disklabel etc. */
+ unsigned int version;
+ unsigned int last_page;
+ unsigned int nr_badpages;
+ unsigned int padding[125];
+ unsigned int badpages[1];
+ } info;
+};
+
+#ifdef __KERNEL__
+
+/*
+ * Max bad pages in the new format..
+ */
+#define __swapoffset(x) ((unsigned long)&((union swap_header *)0)->x)
+#define MAX_SWAP_BADPAGES \
+ ((__swapoffset(magic.magic) - __swapoffset(info.badpages)) / sizeof(int))
+
+#undef DEBUG_SWAP
+
+#include <asm/atomic.h>
+
+#define SWP_USED 1
+#define SWP_WRITEOK 3
+
+#define SWAP_CLUSTER_MAX 32
+
+#define SWAP_MAP_MAX 0x7fff
+#define SWAP_MAP_BAD 0x8000
+
+struct swap_info_struct {
+ unsigned int flags;
+ kdev_t swap_device;
+ struct dentry * swap_file;
+ unsigned short * swap_map;
+ unsigned char * swap_lockmap;
+ unsigned int lowest_bit;
+ unsigned int highest_bit;
+ unsigned int cluster_next;
+ unsigned int cluster_nr;
+ int prio; /* swap priority */
+ int pages;
+ unsigned long max;
+ int next; /* next entry on swap list */
+};
+
+extern int nr_swap_pages;
+extern int nr_free_pages;
+extern atomic_t nr_async_pages;
+extern struct inode swapper_inode;
+extern unsigned long page_cache_size;
+extern long buffermem;
+
+/* Incomplete types for prototype declarations: */
+struct task_struct;
+struct vm_area_struct;
+struct sysinfo;
+
+/* linux/ipc/shm.c */
+extern int shm_swap (int, int);
+
+/* linux/mm/swap.c */
+extern void swap_setup (void);
+
+/* linux/mm/vmscan.c */
+extern int try_to_free_pages(unsigned int gfp_mask);
+
+/* linux/mm/page_io.c */
+extern void rw_swap_page(int, unsigned long, char *, int);
+extern void rw_swap_page_nocache(int, unsigned long, char *);
+extern void rw_swap_page_nolock(int, unsigned long, char *, int);
+extern void swap_after_unlock_page (unsigned long entry);
+
+/* linux/mm/page_alloc.c */
+extern int swap_in(struct task_struct *, struct vm_area_struct *,
+ pte_t *, unsigned long, int);
+
+
+/* linux/mm/swap_state.c */
+extern void show_swap_cache_info(void);
+extern int add_to_swap_cache(struct page *, unsigned long);
+extern int swap_duplicate(unsigned long);
+extern int swap_check_entry(unsigned long);
+struct page * lookup_swap_cache(unsigned long);
+extern struct page * read_swap_cache_async(unsigned long, int);
+#define read_swap_cache(entry) read_swap_cache_async(entry, 1);
+extern int FASTCALL(swap_count(unsigned long));
+/*
+ * Make these inline later once they are working properly.
+ */
+extern void delete_from_swap_cache(struct page *page);
+extern void free_page_and_swap_cache(unsigned long addr);
+
+/* linux/mm/swapfile.c */
+extern unsigned int nr_swapfiles;
+extern struct swap_info_struct swap_info[];
+void si_swapinfo(struct sysinfo *);
+unsigned long get_swap_page(void);
+extern void FASTCALL(swap_free(unsigned long));
+struct swap_list_t {
+ int head; /* head of priority-ordered swapfile list */
+ int next; /* swapfile to be used next */
+};
+extern struct swap_list_t swap_list;
+asmlinkage int sys_swapoff(const char *);
+asmlinkage int sys_swapon(const char *, int);
+
+/*
+ * vm_ops not present page codes for shared memory.
+ *
+ * Will go away eventually..
+ */
+#define SHM_SWP_TYPE 0x20
+
+/*
+ * swap cache stuff (in linux/mm/swap_state.c)
+ */
+
+#define SWAP_CACHE_INFO
+
+#ifdef SWAP_CACHE_INFO
+extern unsigned long swap_cache_add_total;
+extern unsigned long swap_cache_del_total;
+extern unsigned long swap_cache_find_total;
+extern unsigned long swap_cache_find_success;
+#endif
+
+extern inline unsigned long in_swap_cache(struct page *page)
+{
+ if (PageSwapCache(page))
+ return page->offset;
+ return 0;
+}
+
+/*
+ * Work out if there are any other processes sharing this page, ignoring
+ * any page reference coming from the swap cache, or from outstanding
+ * swap IO on this page. (The page cache _does_ count as another valid
+ * reference to the page, however.)
+ */
+static inline int is_page_shared(struct page *page)
+{
+ unsigned int count;
+ if (PageReserved(page))
+ return 1;
+ count = atomic_read(&page->count);
+ if (PageSwapCache(page))
+ count += swap_count(page->offset) - 2;
+ if (PageFreeAfter(page))
+ count--;
+ return count > 1;
+}
+
+#endif /* __KERNEL__*/
+
+#endif /* _LINUX_SWAP_H */
diff --git a/pfinet/linux-src/include/linux/swapctl.h b/pfinet/linux-src/include/linux/swapctl.h
new file mode 100644
index 00000000..f9f2d2ac
--- /dev/null
+++ b/pfinet/linux-src/include/linux/swapctl.h
@@ -0,0 +1,35 @@
+#ifndef _LINUX_SWAPCTL_H
+#define _LINUX_SWAPCTL_H
+
+#include <asm/page.h>
+#include <linux/fs.h>
+
+typedef struct buffer_mem_v1
+{
+ unsigned int min_percent;
+ unsigned int borrow_percent;
+ unsigned int max_percent;
+} buffer_mem_v1;
+typedef buffer_mem_v1 buffer_mem_t;
+extern buffer_mem_t buffer_mem;
+extern buffer_mem_t page_cache;
+
+typedef struct freepages_v1
+{
+ unsigned int min;
+ unsigned int low;
+ unsigned int high;
+} freepages_v1;
+typedef freepages_v1 freepages_t;
+extern freepages_t freepages;
+
+typedef struct pager_daemon_v1
+{
+ unsigned int tries_base;
+ unsigned int tries_min;
+ unsigned int swap_cluster;
+} pager_daemon_v1;
+typedef pager_daemon_v1 pager_daemon_t;
+extern pager_daemon_t pager_daemon;
+
+#endif /* _LINUX_SWAPCTL_H */
diff --git a/pfinet/linux-src/include/linux/synclink.h b/pfinet/linux-src/include/linux/synclink.h
new file mode 100644
index 00000000..450341b7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/synclink.h
@@ -0,0 +1,255 @@
+/*
+ * SyncLink Multiprotocol Serial Adapter Driver
+ *
+ * ==FILEDATE 19990810==
+ *
+ * Copyright (C) 1998 by Microgate Corporation
+ *
+ * Redistribution of this file is permitted under
+ * the terms of the GNU Public License (GPL)
+ */
+
+#ifndef _SYNCLINK_H_
+#define _SYNCLINK_H_
+
+#define BOOLEAN int
+#define TRUE 1
+#define FALSE 0
+
+#define BIT0 0x0001
+#define BIT1 0x0002
+#define BIT2 0x0004
+#define BIT3 0x0008
+#define BIT4 0x0010
+#define BIT5 0x0020
+#define BIT6 0x0040
+#define BIT7 0x0080
+#define BIT8 0x0100
+#define BIT9 0x0200
+#define BIT10 0x0400
+#define BIT11 0x0800
+#define BIT12 0x1000
+#define BIT13 0x2000
+#define BIT14 0x4000
+#define BIT15 0x8000
+#define BIT16 0x00010000
+#define BIT17 0x00020000
+#define BIT18 0x00040000
+#define BIT19 0x00080000
+#define BIT20 0x00100000
+#define BIT21 0x00200000
+#define BIT22 0x00400000
+#define BIT23 0x00800000
+#define BIT24 0x01000000
+#define BIT25 0x02000000
+#define BIT26 0x04000000
+#define BIT27 0x08000000
+#define BIT28 0x10000000
+#define BIT29 0x20000000
+#define BIT30 0x40000000
+#define BIT31 0x80000000
+
+
+#define HDLC_MAX_FRAME_SIZE 65535
+#define MAX_ASYNC_TRANSMIT 4096
+#define MAX_ASYNC_BUFFER_SIZE 4096
+
+#define ASYNC_PARITY_NONE 0
+#define ASYNC_PARITY_EVEN 1
+#define ASYNC_PARITY_ODD 2
+#define ASYNC_PARITY_SPACE 3
+
+#define HDLC_FLAG_UNDERRUN_ABORT7 0x0000
+#define HDLC_FLAG_UNDERRUN_ABORT15 0x0001
+#define HDLC_FLAG_UNDERRUN_FLAG 0x0002
+#define HDLC_FLAG_UNDERRUN_CRC 0x0004
+#define HDLC_FLAG_SHARE_ZERO 0x0010
+#define HDLC_FLAG_AUTO_CTS 0x0020
+#define HDLC_FLAG_AUTO_DCD 0x0040
+#define HDLC_FLAG_AUTO_RTS 0x0080
+#define HDLC_FLAG_RXC_DPLL 0x0100
+#define HDLC_FLAG_RXC_BRG 0x0200
+#define HDLC_FLAG_RXC_TXCPIN 0x8000
+#define HDLC_FLAG_RXC_RXCPIN 0x0000
+#define HDLC_FLAG_TXC_DPLL 0x0400
+#define HDLC_FLAG_TXC_BRG 0x0800
+#define HDLC_FLAG_TXC_TXCPIN 0x0000
+#define HDLC_FLAG_TXC_RXCPIN 0x0008
+#define HDLC_FLAG_DPLL_DIV8 0x1000
+#define HDLC_FLAG_DPLL_DIV16 0x2000
+#define HDLC_FLAG_DPLL_DIV32 0x0000
+#define HDLC_FLAG_HDLC_LOOPMODE 0x4000
+
+#define HDLC_CRC_NONE 0
+#define HDLC_CRC_16_CCITT 1
+#define HDLC_CRC_32_CCITT 2
+
+#define HDLC_TXIDLE_FLAGS 0
+#define HDLC_TXIDLE_ALT_ZEROS_ONES 1
+#define HDLC_TXIDLE_ZEROS 2
+#define HDLC_TXIDLE_ONES 3
+#define HDLC_TXIDLE_ALT_MARK_SPACE 4
+#define HDLC_TXIDLE_SPACE 5
+#define HDLC_TXIDLE_MARK 6
+
+#define HDLC_ENCODING_NRZ 0
+#define HDLC_ENCODING_NRZB 1
+#define HDLC_ENCODING_NRZI_MARK 2
+#define HDLC_ENCODING_NRZI_SPACE 3
+#define HDLC_ENCODING_NRZI HDLC_ENCODING_NRZI_SPACE
+#define HDLC_ENCODING_BIPHASE_MARK 4
+#define HDLC_ENCODING_BIPHASE_SPACE 5
+#define HDLC_ENCODING_BIPHASE_LEVEL 6
+#define HDLC_ENCODING_DIFF_BIPHASE_LEVEL 7
+
+#define HDLC_PREAMBLE_LENGTH_8BITS 0
+#define HDLC_PREAMBLE_LENGTH_16BITS 1
+#define HDLC_PREAMBLE_LENGTH_32BITS 2
+#define HDLC_PREAMBLE_LENGTH_64BITS 3
+
+#define HDLC_PREAMBLE_PATTERN_NONE 0
+#define HDLC_PREAMBLE_PATTERN_ZEROS 1
+#define HDLC_PREAMBLE_PATTERN_FLAGS 2
+#define HDLC_PREAMBLE_PATTERN_10 3
+#define HDLC_PREAMBLE_PATTERN_01 4
+#define HDLC_PREAMBLE_PATTERN_ONES 5
+
+#define MGSL_MODE_ASYNC 1
+#define MGSL_MODE_HDLC 2
+
+#define MGSL_BUS_TYPE_ISA 1
+#define MGSL_BUS_TYPE_EISA 2
+#define MGSL_BUS_TYPE_PCI 5
+
+typedef struct _MGSL_PARAMS
+{
+ /* Common */
+
+ unsigned long mode; /* Asynchronous or HDLC */
+ unsigned char loopback; /* internal loopback mode */
+
+ /* HDLC Only */
+
+ unsigned short flags;
+ unsigned char encoding; /* NRZ, NRZI, etc. */
+ unsigned long clock_speed; /* external clock speed in bits per second */
+ unsigned char addr_filter; /* receive HDLC address filter, 0xFF = disable */
+ unsigned short crc_type; /* None, CRC16-CCITT, or CRC32-CCITT */
+ unsigned char preamble_length;
+ unsigned char preamble;
+
+ /* Async Only */
+
+ unsigned long data_rate; /* bits per second */
+ unsigned char data_bits; /* 7 or 8 data bits */
+ unsigned char stop_bits; /* 1 or 2 stop bits */
+ unsigned char parity; /* none, even, or odd */
+
+} MGSL_PARAMS, *PMGSL_PARAMS;
+
+#define MICROGATE_VENDOR_ID 0x13c0
+#define SYNCLINK_DEVICE_ID 0x0010
+#define MGSL_MAX_SERIAL_NUMBER 30
+
+/*
+** device diagnostics status
+*/
+
+#define DiagStatus_OK 0
+#define DiagStatus_AddressFailure 1
+#define DiagStatus_AddressConflict 2
+#define DiagStatus_IrqFailure 3
+#define DiagStatus_IrqConflict 4
+#define DiagStatus_DmaFailure 5
+#define DiagStatus_DmaConflict 6
+#define DiagStatus_PciAdapterNotFound 7
+#define DiagStatus_CantAssignPciResources 8
+#define DiagStatus_CantAssignPciMemAddr 9
+#define DiagStatus_CantAssignPciIoAddr 10
+#define DiagStatus_CantAssignPciIrq 11
+#define DiagStatus_MemoryError 12
+
+#define SerialSignal_DCD 0x01 /* Data Carrier Detect */
+#define SerialSignal_TXD 0x02 /* Transmit Data */
+#define SerialSignal_RI 0x04 /* Ring Indicator */
+#define SerialSignal_RXD 0x08 /* Receive Data */
+#define SerialSignal_CTS 0x10 /* Clear to Send */
+#define SerialSignal_RTS 0x20 /* Request to Send */
+#define SerialSignal_DSR 0x40 /* Data Set Ready */
+#define SerialSignal_DTR 0x80 /* Data Terminal Ready */
+
+
+/*
+ * Counters of the input lines (CTS, DSR, RI, CD) interrupts
+ */
+struct mgsl_icount {
+ __u32 cts, dsr, rng, dcd, tx, rx;
+ __u32 frame, parity, overrun, brk;
+ __u32 buf_overrun;
+ __u32 txok;
+ __u32 txunder;
+ __u32 txabort;
+ __u32 txtimeout;
+ __u32 rxshort;
+ __u32 rxlong;
+ __u32 rxabort;
+ __u32 rxover;
+ __u32 rxcrc;
+ __u32 rxok;
+ __u32 exithunt;
+ __u32 rxidle;
+};
+
+
+#define DEBUG_LEVEL_DATA 1
+#define DEBUG_LEVEL_ERROR 2
+#define DEBUG_LEVEL_INFO 3
+#define DEBUG_LEVEL_BH 4
+#define DEBUG_LEVEL_ISR 5
+
+/*
+** Event bit flags for use with MgslWaitEvent
+*/
+
+#define MgslEvent_DsrActive 0x0001
+#define MgslEvent_DsrInactive 0x0002
+#define MgslEvent_Dsr 0x0003
+#define MgslEvent_CtsActive 0x0004
+#define MgslEvent_CtsInactive 0x0008
+#define MgslEvent_Cts 0x000c
+#define MgslEvent_DcdActive 0x0010
+#define MgslEvent_DcdInactive 0x0020
+#define MgslEvent_Dcd 0x0030
+#define MgslEvent_RiActive 0x0040
+#define MgslEvent_RiInactive 0x0080
+#define MgslEvent_Ri 0x00c0
+#define MgslEvent_ExitHuntMode 0x0100
+#define MgslEvent_IdleReceived 0x0200
+
+/* Private IOCTL codes:
+ *
+ * MGSL_IOCSPARAMS set MGSL_PARAMS structure values
+ * MGSL_IOCGPARAMS get current MGSL_PARAMS structure values
+ * MGSL_IOCSTXIDLE set current transmit idle mode
+ * MGSL_IOCGTXIDLE get current transmit idle mode
+ * MGSL_IOCTXENABLE enable or disable transmitter
+ * MGSL_IOCRXENABLE enable or disable receiver
+ * MGSL_IOCTXABORT abort transmitting frame (HDLC)
+ * MGSL_IOCGSTATS return current statistics
+ * MGSL_IOCWAITEVENT wait for specified event to occur
+ * MGSL_LOOPTXDONE transmit in HDLC LoopMode done
+ */
+#define MGSL_MAGIC_IOC 'm'
+#define MGSL_IOCSPARAMS _IOW(MGSL_MAGIC_IOC,0,struct _MGSL_PARAMS)
+#define MGSL_IOCGPARAMS _IOR(MGSL_MAGIC_IOC,1,struct _MGSL_PARAMS)
+#define MGSL_IOCSTXIDLE _IO(MGSL_MAGIC_IOC,2)
+#define MGSL_IOCGTXIDLE _IO(MGSL_MAGIC_IOC,3)
+#define MGSL_IOCTXENABLE _IO(MGSL_MAGIC_IOC,4)
+#define MGSL_IOCRXENABLE _IO(MGSL_MAGIC_IOC,5)
+#define MGSL_IOCTXABORT _IO(MGSL_MAGIC_IOC,6)
+#define MGSL_IOCGSTATS _IO(MGSL_MAGIC_IOC,7)
+#define MGSL_IOCWAITEVENT _IOWR(MGSL_MAGIC_IOC,8,int)
+#define MGSL_IOCCLRMODCOUNT _IO(MGSL_MAGIC_IOC,15)
+#define MGSL_IOCLOOPTXDONE _IO(MGSL_MAGIC_IOC,9)
+
+#endif /* _SYNCLINK_H_ */
diff --git a/pfinet/linux-src/include/linux/sys.h b/pfinet/linux-src/include/linux/sys.h
new file mode 100644
index 00000000..dcd32566
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sys.h
@@ -0,0 +1,30 @@
+#ifndef _LINUX_SYS_H
+#define _LINUX_SYS_H
+
+/*
+ * system call entry points ... but not all are defined
+ */
+#define NR_syscalls 256
+
+/*
+ * These are system calls that will be removed at some time
+ * due to newer versions existing..
+ * (please be careful - ibcs2 may need some of these).
+ */
+#ifdef notdef
+#define _sys_waitpid _sys_old_syscall /* _sys_wait4 */
+#define _sys_olduname _sys_old_syscall /* _sys_newuname */
+#define _sys_uname _sys_old_syscall /* _sys_newuname */
+#define _sys_stat _sys_old_syscall /* _sys_newstat */
+#define _sys_fstat _sys_old_syscall /* _sys_newfstat */
+#define _sys_lstat _sys_old_syscall /* _sys_newlstat */
+#define _sys_signal _sys_old_syscall /* _sys_sigaction */
+#define _sys_sgetmask _sys_old_syscall /* _sys_sigprocmask */
+#define _sys_ssetmask _sys_old_syscall /* _sys_sigprocmask */
+#endif
+
+/*
+ * These are system calls that haven't been implemented yet
+ * but have an entry in the table for future expansion..
+ */
+#endif
diff --git a/pfinet/linux-src/include/linux/sysctl.h b/pfinet/linux-src/include/linux/sysctl.h
new file mode 100644
index 00000000..1067bfbb
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sysctl.h
@@ -0,0 +1,560 @@
+/*
+ * sysctl.h: General linux system control interface
+ *
+ * Begun 24 March 1995, Stephen Tweedie
+ *
+ ****************************************************************
+ ****************************************************************
+ **
+ ** WARNING:
+ ** The values in this file are exported to user space via
+ ** the sysctl() binary interface. Do *NOT* change the
+ ** numbering of any existing values here, and do not change
+ ** any numbers within any one set of values. If you have
+ ** to redefine an existing interface, use a new number for it.
+ ** The kernel will then return ENOTDIR to any application using
+ ** the old binary interface.
+ **
+ ** --sct
+ **
+ ****************************************************************
+ ****************************************************************
+ */
+
+#include <linux/lists.h>
+
+#ifndef _LINUX_SYSCTL_H
+#define _LINUX_SYSCTL_H
+
+#define CTL_MAXNAME 10
+
+struct __sysctl_args {
+ int *name;
+ int nlen;
+ void *oldval;
+ size_t *oldlenp;
+ void *newval;
+ size_t newlen;
+ unsigned long __unused[4];
+};
+
+/* Define sysctl names first */
+
+/* Top-level names: */
+
+/* For internal pattern-matching use only: */
+#ifdef __KERNEL__
+#define CTL_ANY -1 /* Matches any name */
+#define CTL_NONE 0
+#endif
+
+enum
+{
+ CTL_KERN=1, /* General kernel info and control */
+ CTL_VM=2, /* VM management */
+ CTL_NET=3, /* Networking */
+ CTL_PROC=4, /* Process info */
+ CTL_FS=5, /* Filesystems */
+ CTL_DEBUG=6, /* Debugging */
+ CTL_DEV=7, /* Devices */
+ CTL_BUS=8 /* Buses */
+};
+
+/* CTL_BUS names: */
+enum
+{
+ BUS_ISA=1 /* ISA */
+};
+
+/* CTL_KERN names: */
+enum
+{
+ KERN_OSTYPE=1, /* string: system version */
+ KERN_OSRELEASE=2, /* string: system release */
+ KERN_OSREV=3, /* int: system revision */
+ KERN_VERSION=4, /* string: compile time info */
+ KERN_SECUREMASK=5, /* struct: maximum rights mask */
+ KERN_PROF=6, /* table: profiling information */
+ KERN_NODENAME=7,
+ KERN_DOMAINNAME=8,
+
+ KERN_CAP_BSET=14, /* int: capability bounding set */
+ KERN_PANIC=15, /* int: panic timeout */
+ KERN_REALROOTDEV=16, /* real root device to mount after initrd */
+
+ KERN_JAVA_INTERPRETER=19, /* path to Java(tm) interpreter */
+ KERN_JAVA_APPLETVIEWER=20, /* path to Java(tm) appletviewer */
+ KERN_SPARC_REBOOT=21, /* reboot command on Sparc */
+ KERN_CTLALTDEL=22, /* int: allow ctl-alt-del to reboot */
+ KERN_PRINTK=23, /* struct: control printk logging parameters */
+ KERN_NAMETRANS=24, /* Name translation */
+ KERN_PPC_HTABRECLAIM=25, /* turn htab reclaimation on/off on PPC */
+ KERN_PPC_ZEROPAGED=26, /* turn idle page zeroing on/off on PPC */
+ KERN_PPC_POWERSAVE_NAP=27, /* use nap mode for power saving */
+ KERN_MODPROBE=28,
+ KERN_SG_BIG_BUFF=29,
+ KERN_ACCT=30, /* BSD process accounting parameters */
+ KERN_PPC_L2CR=31, /* l2cr register on PPC */
+
+ KERN_RTSIGNR=32, /* Number of rt sigs queued */
+ KERN_RTSIGMAX=33, /* Max queuable */
+
+ KERN_SHMMAX=34, /* int: Maximum shared memory segment */
+ KERN_MSGMAX=35, /* int: Maximum size of a messege */
+ KERN_MSGMNB=36, /* int: Maximum message queue size */
+ KERN_MSGPOOL=37, /* int: Maximum system message pool size */
+ KERN_SYSRQ=38, /* int: Sysreq enable */
+ KERN_SHMALL=41, /* int: maximum size of shared memory */
+ KERN_SPARC_STOP_A=44, /* int: Sparc Stop-A enable */
+};
+
+
+/* CTL_VM names: */
+enum
+{
+ VM_SWAPCTL=1, /* struct: Set vm swapping control */
+ VM_SWAPOUT=2, /* int: Background pageout interval */
+ VM_FREEPG=3, /* struct: Set free page thresholds */
+ VM_BDFLUSH=4, /* struct: Control buffer cache flushing */
+ VM_OVERCOMMIT_MEMORY=5, /* Turn off the virtual memory safety limit */
+ VM_BUFFERMEM=6, /* struct: Set buffer memory thresholds */
+ VM_PAGECACHE=7, /* struct: Set cache memory thresholds */
+ VM_PAGERDAEMON=8, /* struct: Control kswapd behaviour */
+ VM_PGT_CACHE=9, /* struct: Set page table cache parameters */
+ VM_PAGE_CLUSTER=10 /* int: set number of pages to swap together */
+};
+
+
+/* CTL_NET names: */
+enum
+{
+ NET_CORE=1,
+ NET_ETHER=2,
+ NET_802=3,
+ NET_UNIX=4,
+ NET_IPV4=5,
+ NET_IPX=6,
+ NET_ATALK=7,
+ NET_NETROM=8,
+ NET_AX25=9,
+ NET_BRIDGE=10,
+ NET_ROSE=11,
+ NET_IPV6=12,
+ NET_X25=13,
+ NET_TR=14,
+ NET_DECNET=15,
+ NET_ECONET=16
+};
+
+/* /proc/sys/bus/isa */
+enum
+{
+ BUS_ISA_MEM_BASE=1,
+ BUS_ISA_PORT_BASE=2,
+ BUS_ISA_PORT_SHIFT=3
+};
+
+/* /proc/sys/net/core */
+enum
+{
+ NET_CORE_WMEM_MAX=1,
+ NET_CORE_RMEM_MAX=2,
+ NET_CORE_WMEM_DEFAULT=3,
+ NET_CORE_RMEM_DEFAULT=4,
+/* was NET_CORE_DESTROY_DELAY */
+ NET_CORE_MAX_BACKLOG=6,
+ NET_CORE_FASTROUTE=7,
+ NET_CORE_MSG_COST=8,
+ NET_CORE_MSG_BURST=9,
+ NET_CORE_OPTMEM_MAX=10
+};
+
+/* /proc/sys/net/ethernet */
+
+/* /proc/sys/net/802 */
+
+/* /proc/sys/net/unix */
+
+enum
+{
+ NET_UNIX_DESTROY_DELAY=1,
+ NET_UNIX_DELETE_DELAY=2,
+ NET_UNIX_MAX_DGRAM_QLEN=3,
+};
+
+/* /proc/sys/net/ipv4 */
+enum
+{
+ /* v2.0 compatibile variables */
+ NET_IPV4_FORWARD=8,
+ NET_IPV4_DYNADDR=9,
+
+ NET_IPV4_CONF=16,
+ NET_IPV4_NEIGH=17,
+ NET_IPV4_ROUTE=18,
+ NET_IPV4_FIB_HASH=19,
+
+ NET_IPV4_TCP_TIMESTAMPS=33,
+ NET_IPV4_TCP_WINDOW_SCALING=34,
+ NET_IPV4_TCP_SACK=35,
+ NET_IPV4_TCP_RETRANS_COLLAPSE=36,
+ NET_IPV4_DEFAULT_TTL=37,
+ NET_IPV4_AUTOCONFIG=38,
+ NET_IPV4_NO_PMTU_DISC=39,
+ NET_IPV4_TCP_SYN_RETRIES=40,
+ NET_IPV4_IPFRAG_HIGH_THRESH=41,
+ NET_IPV4_IPFRAG_LOW_THRESH=42,
+ NET_IPV4_IPFRAG_TIME=43,
+ NET_IPV4_TCP_MAX_KA_PROBES=44,
+ NET_IPV4_TCP_KEEPALIVE_TIME=45,
+ NET_IPV4_TCP_KEEPALIVE_PROBES=46,
+ NET_IPV4_TCP_RETRIES1=47,
+ NET_IPV4_TCP_RETRIES2=48,
+ NET_IPV4_TCP_FIN_TIMEOUT=49,
+ NET_IPV4_IP_MASQ_DEBUG=50,
+ NET_TCP_SYNCOOKIES=51,
+ NET_TCP_STDURG=52,
+ NET_TCP_RFC1337=53,
+ NET_TCP_SYN_TAILDROP=54,
+ NET_TCP_MAX_SYN_BACKLOG=55,
+ NET_IPV4_LOCAL_PORT_RANGE=56,
+ NET_IPV4_ICMP_ECHO_IGNORE_ALL=57,
+ NET_IPV4_ICMP_ECHO_IGNORE_BROADCASTS=58,
+ NET_IPV4_ICMP_SOURCEQUENCH_RATE=59,
+ NET_IPV4_ICMP_DESTUNREACH_RATE=60,
+ NET_IPV4_ICMP_TIMEEXCEED_RATE=61,
+ NET_IPV4_ICMP_PARAMPROB_RATE=62,
+ NET_IPV4_ICMP_ECHOREPLY_RATE=63,
+ NET_IPV4_ICMP_IGNORE_BOGUS_ERROR_RESPONSES=64,
+ NET_IPV4_IGMP_MAX_MEMBERSHIPS=65,
+ NET_IPV4_ALWAYS_DEFRAG=67
+};
+
+enum {
+ NET_IPV4_ROUTE_FLUSH=1,
+ NET_IPV4_ROUTE_MIN_DELAY=2,
+ NET_IPV4_ROUTE_MAX_DELAY=3,
+ NET_IPV4_ROUTE_GC_THRESH=4,
+ NET_IPV4_ROUTE_MAX_SIZE=5,
+ NET_IPV4_ROUTE_GC_MIN_INTERVAL=6,
+ NET_IPV4_ROUTE_GC_TIMEOUT=7,
+ NET_IPV4_ROUTE_GC_INTERVAL=8,
+ NET_IPV4_ROUTE_REDIRECT_LOAD=9,
+ NET_IPV4_ROUTE_REDIRECT_NUMBER=10,
+ NET_IPV4_ROUTE_REDIRECT_SILENCE=11,
+ NET_IPV4_ROUTE_ERROR_COST=12,
+ NET_IPV4_ROUTE_ERROR_BURST=13,
+ NET_IPV4_ROUTE_GC_ELASTICITY=14,
+ NET_IPV4_ROUTE_MTU_EXPIRES=15
+};
+
+enum
+{
+ NET_PROTO_CONF_ALL=-2,
+ NET_PROTO_CONF_DEFAULT=-3
+
+ /* And device ifindices ... */
+};
+
+enum
+{
+ NET_IPV4_CONF_FORWARDING=1,
+ NET_IPV4_CONF_MC_FORWARDING=2,
+ NET_IPV4_CONF_PROXY_ARP=3,
+ NET_IPV4_CONF_ACCEPT_REDIRECTS=4,
+ NET_IPV4_CONF_SECURE_REDIRECTS=5,
+ NET_IPV4_CONF_SEND_REDIRECTS=6,
+ NET_IPV4_CONF_SHARED_MEDIA=7,
+ NET_IPV4_CONF_RP_FILTER=8,
+ NET_IPV4_CONF_ACCEPT_SOURCE_ROUTE=9,
+ NET_IPV4_CONF_BOOTP_RELAY=10,
+ NET_IPV4_CONF_LOG_MARTIANS=11,
+ NET_IPV4_CONF_HIDDEN=12
+};
+
+/* /proc/sys/net/ipv6 */
+enum {
+ NET_IPV6_CONF=16,
+ NET_IPV6_NEIGH=17,
+ NET_IPV6_ROUTE=18,
+ NET_IPV6_BINDV6ONLY=20,
+};
+
+enum {
+ NET_IPV6_ROUTE_FLUSH=1,
+ NET_IPV6_ROUTE_GC_THRESH=2,
+ NET_IPV6_ROUTE_MAX_SIZE=3,
+ NET_IPV6_ROUTE_GC_MIN_INTERVAL=4,
+ NET_IPV6_ROUTE_GC_TIMEOUT=5,
+ NET_IPV6_ROUTE_GC_INTERVAL=6,
+ NET_IPV6_ROUTE_GC_ELASTICITY=7,
+ NET_IPV6_ROUTE_MTU_EXPIRES=8
+};
+
+enum {
+ NET_IPV6_FORWARDING=1,
+ NET_IPV6_HOP_LIMIT=2,
+ NET_IPV6_MTU=3,
+ NET_IPV6_ACCEPT_RA=4,
+ NET_IPV6_ACCEPT_REDIRECTS=5,
+ NET_IPV6_AUTOCONF=6,
+ NET_IPV6_DAD_TRANSMITS=7,
+ NET_IPV6_RTR_SOLICITS=8,
+ NET_IPV6_RTR_SOLICIT_INTERVAL=9,
+ NET_IPV6_RTR_SOLICIT_DELAY=10
+};
+
+/* /proc/sys/net/<protocol>/neigh/<dev> */
+enum {
+ NET_NEIGH_MCAST_SOLICIT=1,
+ NET_NEIGH_UCAST_SOLICIT=2,
+ NET_NEIGH_APP_SOLICIT=3,
+ NET_NEIGH_RETRANS_TIME=4,
+ NET_NEIGH_REACHABLE_TIME=5,
+ NET_NEIGH_DELAY_PROBE_TIME=6,
+ NET_NEIGH_GC_STALE_TIME=7,
+ NET_NEIGH_UNRES_QLEN=8,
+ NET_NEIGH_PROXY_QLEN=9,
+ NET_NEIGH_ANYCAST_DELAY=10,
+ NET_NEIGH_PROXY_DELAY=11,
+ NET_NEIGH_LOCKTIME=12,
+ NET_NEIGH_GC_INTERVAL=13,
+ NET_NEIGH_GC_THRESH1=14,
+ NET_NEIGH_GC_THRESH2=15,
+ NET_NEIGH_GC_THRESH3=16
+};
+
+/* /proc/sys/net/ipx */
+
+
+/* /proc/sys/net/appletalk */
+enum {
+ NET_ATALK_AARP_EXPIRY_TIME=1,
+ NET_ATALK_AARP_TICK_TIME=2,
+ NET_ATALK_AARP_RETRANSMIT_LIMIT=3,
+ NET_ATALK_AARP_RESOLVE_TIME=4
+};
+
+
+/* /proc/sys/net/netrom */
+enum {
+ NET_NETROM_DEFAULT_PATH_QUALITY=1,
+ NET_NETROM_OBSOLESCENCE_COUNT_INITIALISER=2,
+ NET_NETROM_NETWORK_TTL_INITIALISER=3,
+ NET_NETROM_TRANSPORT_TIMEOUT=4,
+ NET_NETROM_TRANSPORT_MAXIMUM_TRIES=5,
+ NET_NETROM_TRANSPORT_ACKNOWLEDGE_DELAY=6,
+ NET_NETROM_TRANSPORT_BUSY_DELAY=7,
+ NET_NETROM_TRANSPORT_REQUESTED_WINDOW_SIZE=8,
+ NET_NETROM_TRANSPORT_NO_ACTIVITY_TIMEOUT=9,
+ NET_NETROM_ROUTING_CONTROL=10,
+ NET_NETROM_LINK_FAILS_COUNT=11
+};
+
+/* /proc/sys/net/ax25 */
+enum {
+ NET_AX25_IP_DEFAULT_MODE=1,
+ NET_AX25_DEFAULT_MODE=2,
+ NET_AX25_BACKOFF_TYPE=3,
+ NET_AX25_CONNECT_MODE=4,
+ NET_AX25_STANDARD_WINDOW=5,
+ NET_AX25_EXTENDED_WINDOW=6,
+ NET_AX25_T1_TIMEOUT=7,
+ NET_AX25_T2_TIMEOUT=8,
+ NET_AX25_T3_TIMEOUT=9,
+ NET_AX25_IDLE_TIMEOUT=10,
+ NET_AX25_N2=11,
+ NET_AX25_PACLEN=12,
+ NET_AX25_PROTOCOL=13,
+ NET_AX25_DAMA_SLAVE_TIMEOUT=14
+};
+
+/* /proc/sys/net/rose */
+enum {
+ NET_ROSE_RESTART_REQUEST_TIMEOUT=1,
+ NET_ROSE_CALL_REQUEST_TIMEOUT=2,
+ NET_ROSE_RESET_REQUEST_TIMEOUT=3,
+ NET_ROSE_CLEAR_REQUEST_TIMEOUT=4,
+ NET_ROSE_ACK_HOLD_BACK_TIMEOUT=5,
+ NET_ROSE_ROUTING_CONTROL=6,
+ NET_ROSE_LINK_FAIL_TIMEOUT=7,
+ NET_ROSE_MAX_VCS=8,
+ NET_ROSE_WINDOW_SIZE=9,
+ NET_ROSE_NO_ACTIVITY_TIMEOUT=10
+};
+
+/* /proc/sys/net/x25 */
+enum {
+ NET_X25_RESTART_REQUEST_TIMEOUT=1,
+ NET_X25_CALL_REQUEST_TIMEOUT=2,
+ NET_X25_RESET_REQUEST_TIMEOUT=3,
+ NET_X25_CLEAR_REQUEST_TIMEOUT=4,
+ NET_X25_ACK_HOLD_BACK_TIMEOUT=5
+};
+
+/* /proc/sys/net/token-ring */
+enum
+{
+ NET_TR_RIF_TIMEOUT=1
+};
+
+/* /proc/sys/net/decnet */
+enum {
+ NET_DECNET_DEF_T3_BROADCAST=1,
+ NET_DECNET_DEF_T3_POINTTOPOINT=2,
+ NET_DECNET_DEF_T1=3,
+ NET_DECNET_DEF_BCT1=4,
+ NET_DECNET_CACHETIMEOUT=5,
+ NET_DECNET_DEBUG_LEVEL=6
+};
+
+/* CTL_PROC names: */
+
+/* CTL_FS names: */
+enum
+{
+ FS_NRINODE=1, /* int:current number of allocated inodes */
+ FS_STATINODE=2,
+ FS_MAXINODE=3, /* int:maximum number of inodes that can be allocated */
+ FS_NRDQUOT=4, /* int:current number of allocated dquots */
+ FS_MAXDQUOT=5, /* int:maximum number of dquots that can be allocated */
+ FS_NRFILE=6, /* int:current number of allocated filedescriptors */
+ FS_MAXFILE=7, /* int:maximum number of filedescriptors that can be allocated */
+ FS_DENTRY=8,
+ FS_NRSUPER=9, /* int:current number of allocated super_blocks */
+ FS_MAXSUPER=10 /* int:maximum number of super_blocks that can be allocated */
+};
+
+/* CTL_DEBUG names: */
+
+/* CTL_DEV names: */
+enum {
+ DEV_CDROM=1,
+ DEV_HWMON=2
+};
+
+/* /proc/sys/dev/cdrom */
+enum {
+ DEV_CDROM_INFO=1
+};
+
+#ifdef __KERNEL__
+
+extern asmlinkage int sys_sysctl(struct __sysctl_args *);
+extern void sysctl_init(void);
+
+typedef struct ctl_table ctl_table;
+
+typedef int ctl_handler (ctl_table *table, int *name, int nlen,
+ void *oldval, size_t *oldlenp,
+ void *newval, size_t newlen,
+ void **context);
+
+typedef int proc_handler (ctl_table *ctl, int write, struct file * filp,
+ void *buffer, size_t *lenp);
+
+extern int proc_dostring(ctl_table *, int, struct file *,
+ void *, size_t *);
+extern int proc_dointvec(ctl_table *, int, struct file *,
+ void *, size_t *);
+extern int proc_dointvec_bset(ctl_table *, int, struct file *,
+ void *, size_t *);
+extern int proc_dointvec_minmax(ctl_table *, int, struct file *,
+ void *, size_t *);
+extern int proc_dointvec_jiffies(ctl_table *, int, struct file *,
+ void *, size_t *);
+
+extern int do_sysctl (int *name, int nlen,
+ void *oldval, size_t *oldlenp,
+ void *newval, size_t newlen);
+
+extern int do_sysctl_strategy (ctl_table *table,
+ int *name, int nlen,
+ void *oldval, size_t *oldlenp,
+ void *newval, size_t newlen, void ** context);
+
+extern ctl_handler sysctl_string;
+extern ctl_handler sysctl_intvec;
+extern ctl_handler sysctl_jiffies;
+
+extern int do_string (
+ void *oldval, size_t *oldlenp, void *newval, size_t newlen,
+ int rdwr, char *data, size_t max);
+extern int do_int (
+ void *oldval, size_t *oldlenp, void *newval, size_t newlen,
+ int rdwr, int *data);
+extern int do_struct (
+ void *oldval, size_t *oldlenp, void *newval, size_t newlen,
+ int rdwr, void *data, size_t len);
+
+
+/*
+ * Register a set of sysctl names by calling register_sysctl_table
+ * with an initialised array of ctl_table's. An entry with zero
+ * ctl_name terminates the table. table->de will be set up by the
+ * registration and need not be initialised in advance.
+ *
+ * sysctl names can be mirrored automatically under /proc/sys. The
+ * procname supplied controls /proc naming.
+ *
+ * The table's mode will be honoured both for sys_sysctl(2) and
+ * proc-fs access.
+ *
+ * Leaf nodes in the sysctl tree will be represented by a single file
+ * under /proc; non-leaf nodes will be represented by directories. A
+ * null procname disables /proc mirroring at this node.
+ *
+ * sysctl(2) can automatically manage read and write requests through
+ * the sysctl table. The data and maxlen fields of the ctl_table
+ * struct enable minimal validation of the values being written to be
+ * performed, and the mode field allows minimal authentication.
+ *
+ * More sophisticated management can be enabled by the provision of a
+ * strategy routine with the table entry. This will be called before
+ * any automatic read or write of the data is performed.
+ *
+ * The strategy routine may return:
+ * <0: Error occurred (error is passed to user process)
+ * 0: OK - proceed with automatic read or write.
+ * >0: OK - read or write has been done by the strategy routine, so
+ * return immediately.
+ *
+ * There must be a proc_handler routine for any terminal nodes
+ * mirrored under /proc/sys (non-terminals are handled by a built-in
+ * directory handler). Several default handlers are available to
+ * cover common cases.
+ */
+
+/* A sysctl table is an array of struct ctl_table: */
+struct ctl_table
+{
+ int ctl_name; /* Binary ID */
+ const char *procname; /* Text ID for /proc/sys, or zero */
+ void *data;
+ int maxlen;
+ mode_t mode;
+ ctl_table *child;
+ proc_handler *proc_handler; /* Callback for text formatting */
+ ctl_handler *strategy; /* Callback function for all r/w */
+ struct proc_dir_entry *de; /* /proc control block */
+ void *extra1;
+ void *extra2;
+};
+
+/* struct ctl_table_header is used to maintain dynamic lists of
+ ctl_table trees. */
+struct ctl_table_header
+{
+ ctl_table *ctl_table;
+ DLNODE(struct ctl_table_header) ctl_entry;
+};
+
+struct ctl_table_header * register_sysctl_table(ctl_table * table,
+ int insert_at_head);
+void unregister_sysctl_table(struct ctl_table_header * table);
+
+#else /* __KERNEL__ */
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SYSCTL_H */
diff --git a/pfinet/linux-src/include/linux/sysrq.h b/pfinet/linux-src/include/linux/sysrq.h
new file mode 100644
index 00000000..6c080adb
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sysrq.h
@@ -0,0 +1,40 @@
+/* -*- linux-c -*-
+ *
+ * $Id: sysrq.h,v 1.3 1997/07/17 11:54:33 mj Exp $
+ *
+ * Linux Magic System Request Key Hacks
+ *
+ * (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#include <linux/config.h>
+
+struct pt_regs;
+struct kbd_struct;
+struct tty_struct;
+
+/* Generic SysRq interface -- you may call it from any device driver, supplying
+ * ASCII code of the key, pointer to registers and kbd/tty structs (if they
+ * are available -- else NULL's).
+ */
+
+void handle_sysrq(int, struct pt_regs *, struct kbd_struct *, struct tty_struct *);
+
+/* Deferred actions */
+
+extern int emergency_sync_scheduled;
+
+#define EMERG_SYNC 1
+#define EMERG_REMOUNT 2
+
+void do_emergency_sync(void);
+
+#ifdef CONFIG_MAGIC_SYSRQ
+#define CHECK_EMERGENCY_SYNC \
+ if (emergency_sync_scheduled) \
+ do_emergency_sync();
+#else
+#define CHECK_EMERGENCY_SYNC
+#endif
+
+extern int sysrq_enabled;
diff --git a/pfinet/linux-src/include/linux/sysv_fs.h b/pfinet/linux-src/include/linux/sysv_fs.h
new file mode 100644
index 00000000..b34296c1
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sysv_fs.h
@@ -0,0 +1,413 @@
+#ifndef _LINUX_SYSV_FS_H
+#define _LINUX_SYSV_FS_H
+
+/*
+ * The SystemV/Coherent filesystem constants/structures/macros
+ */
+
+
+/* This code assumes
+ - a little endian processor like 386,
+ - sizeof(short) = 2, sizeof(int) = 4, sizeof(long) = 4,
+ - alignof(short) = 2, alignof(long) = 4.
+*/
+
+#ifdef __GNUC__
+#define __packed2__ __attribute__ ((packed, aligned(2)))
+#else
+#error I want gcc!
+#endif
+
+#include <linux/stat.h> /* declares S_IFLNK etc. */
+#include <linux/sched.h> /* declares wake_up() */
+#include <linux/sysv_fs_sb.h> /* defines the sv_... shortcuts */
+
+
+/* Layout on disk */
+/* ============== */
+
+
+/* The block size is sb->sv_block_size which may be smaller than BLOCK_SIZE. */
+
+/* zones (= data allocation units) are blocks */
+
+/* On Coherent FS, 32 bit quantities are stored using (I quote the Coherent
+ manual) a "canonical byte ordering". This is the PDP-11 byte ordering:
+ x = 2^24 * byte3 + 2^16 * byte2 + 2^8 * byte1 + byte0 is stored
+ as { byte2, byte3, byte0, byte1 }. We need conversions.
+*/
+
+typedef u32 coh_ulong;
+
+static inline coh_ulong to_coh_ulong (u32 x)
+{
+ return ((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16);
+}
+
+static inline u32 from_coh_ulong (coh_ulong x)
+{
+ return ((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16);
+}
+
+/* inode numbers are 16 bit */
+
+typedef u16 sysv_ino_t;
+
+/* Block numbers are 24 bit, sometimes stored in 32 bit.
+ On Coherent FS, they are always stored in PDP-11 manner: the least
+ significant 16 bits come last.
+*/
+
+typedef u32 sysv_zone_t;
+
+/* Among the blocks ... */
+/* Xenix FS, Coherent FS: block 0 is the boot block, block 1 the super-block.
+ SystemV FS: block 0 contains both the boot sector and the super-block. */
+/* The first inode zone is sb->sv_firstinodezone (1 or 2). */
+
+/* Among the inodes ... */
+/* 0 is non-existent */
+#define SYSV_BADBL_INO 1 /* inode of bad blocks file */
+#define SYSV_ROOT_INO 2 /* inode of root directory */
+
+
+/* Xenix super-block data on disk */
+#define XENIX_NICINOD 100 /* number of inode cache entries */
+#define XENIX_NICFREE 100 /* number of free block list chunk entries */
+struct xenix_super_block {
+ u16 s_isize; /* index of first data zone */
+ u32 s_fsize __packed2__; /* total number of zones of this fs */
+ /* the start of the free block list: */
+ u16 s_nfree; /* number of free blocks in s_free, <= XENIX_NICFREE */
+ u32 s_free[XENIX_NICFREE]; /* first free block list chunk */
+ /* the cache of free inodes: */
+ u16 s_ninode; /* number of free inodes in s_inode, <= XENIX_NICINOD */
+ sysv_ino_t s_inode[XENIX_NICINOD]; /* some free inodes */
+ /* locks, not used by Linux: */
+ char s_flock; /* lock during free block list manipulation */
+ char s_ilock; /* lock during inode cache manipulation */
+ char s_fmod; /* super-block modified flag */
+ char s_ronly; /* flag whether fs is mounted read-only */
+ u32 s_time __packed2__; /* time of last super block update */
+ u32 s_tfree __packed2__; /* total number of free zones */
+ u16 s_tinode; /* total number of free inodes */
+ s16 s_dinfo[4]; /* device information ?? */
+ char s_fname[6]; /* file system volume name */
+ char s_fpack[6]; /* file system pack name */
+ char s_clean; /* set to 0x46 when filesystem is properly unmounted */
+ char s_fill[371];
+ s32 s_magic; /* version of file system */
+ s32 s_type; /* type of file system: 1 for 512 byte blocks
+ 2 for 1024 byte blocks
+ 3 for 2048 byte blocks */
+
+};
+
+/* Xenix free list block on disk */
+struct xenix_freelist_chunk {
+ u16 fl_nfree; /* number of free blocks in fl_free, <= XENIX_NICFREE] */
+ u32 fl_free[XENIX_NICFREE] __packed2__;
+};
+
+/* SystemV FS comes in two variants:
+ * sysv2: System V Release 2 (e.g. Microport), structure elements aligned(2).
+ * sysv4: System V Release 4 (e.g. Consensys), structure elements aligned(4).
+ */
+#define SYSV_NICINOD 100 /* number of inode cache entries */
+#define SYSV_NICFREE 50 /* number of free block list chunk entries */
+
+/* SystemV4 super-block data on disk */
+struct sysv4_super_block {
+ u16 s_isize; /* index of first data zone */
+ u16 s_pad0;
+ u32 s_fsize; /* total number of zones of this fs */
+ /* the start of the free block list: */
+ u16 s_nfree; /* number of free blocks in s_free, <= SYSV_NICFREE */
+ u16 s_pad1;
+ u32 s_free[SYSV_NICFREE]; /* first free block list chunk */
+ /* the cache of free inodes: */
+ u16 s_ninode; /* number of free inodes in s_inode, <= SYSV_NICINOD */
+ u16 s_pad2;
+ sysv_ino_t s_inode[SYSV_NICINOD]; /* some free inodes */
+ /* locks, not used by Linux: */
+ char s_flock; /* lock during free block list manipulation */
+ char s_ilock; /* lock during inode cache manipulation */
+ char s_fmod; /* super-block modified flag */
+ char s_ronly; /* flag whether fs is mounted read-only */
+ u32 s_time; /* time of last super block update */
+ s16 s_dinfo[4]; /* device information ?? */
+ u32 s_tfree; /* total number of free zones */
+ u16 s_tinode; /* total number of free inodes */
+ u16 s_pad3;
+ char s_fname[6]; /* file system volume name */
+ char s_fpack[6]; /* file system pack name */
+ s32 s_fill[12];
+ s32 s_state; /* file system state: 0x7c269d38-s_time means clean */
+ s32 s_magic; /* version of file system */
+ s32 s_type; /* type of file system: 1 for 512 byte blocks
+ 2 for 1024 byte blocks */
+};
+
+/* SystemV4 free list block on disk */
+struct sysv4_freelist_chunk {
+ u16 fl_nfree; /* number of free blocks in fl_free, <= SYSV_NICFREE] */
+ u32 fl_free[SYSV_NICFREE];
+};
+
+/* SystemV2 super-block data on disk */
+struct sysv2_super_block {
+ u16 s_isize; /* index of first data zone */
+ u32 s_fsize __packed2__; /* total number of zones of this fs */
+ /* the start of the free block list: */
+ u16 s_nfree; /* number of free blocks in s_free, <= SYSV_NICFREE */
+ u32 s_free[SYSV_NICFREE]; /* first free block list chunk */
+ /* the cache of free inodes: */
+ u16 s_ninode; /* number of free inodes in s_inode, <= SYSV_NICINOD */
+ sysv_ino_t s_inode[SYSV_NICINOD]; /* some free inodes */
+ /* locks, not used by Linux: */
+ char s_flock; /* lock during free block list manipulation */
+ char s_ilock; /* lock during inode cache manipulation */
+ char s_fmod; /* super-block modified flag */
+ char s_ronly; /* flag whether fs is mounted read-only */
+ u32 s_time __packed2__; /* time of last super block update */
+ s16 s_dinfo[4]; /* device information ?? */
+ u32 s_tfree __packed2__; /* total number of free zones */
+ u16 s_tinode; /* total number of free inodes */
+ char s_fname[6]; /* file system volume name */
+ char s_fpack[6]; /* file system pack name */
+ s32 s_fill[14];
+ s32 s_state; /* file system state: 0xcb096f43 means clean */
+ s32 s_magic; /* version of file system */
+ s32 s_type; /* type of file system: 1 for 512 byte blocks
+ 2 for 1024 byte blocks */
+};
+
+/* SystemV2 free list block on disk */
+struct sysv2_freelist_chunk {
+ u16 fl_nfree; /* number of free blocks in fl_free, <= SYSV_NICFREE] */
+ u32 fl_free[SYSV_NICFREE] __packed2__;
+};
+
+/* Coherent super-block data on disk */
+#define COH_NICINOD 100 /* number of inode cache entries */
+#define COH_NICFREE 64 /* number of free block list chunk entries */
+struct coh_super_block {
+ u16 s_isize; /* index of first data zone */
+ coh_ulong s_fsize __packed2__; /* total number of zones of this fs */
+ /* the start of the free block list: */
+ u16 s_nfree; /* number of free blocks in s_free, <= COH_NICFREE */
+ coh_ulong s_free[COH_NICFREE] __packed2__; /* first free block list chunk */
+ /* the cache of free inodes: */
+ u16 s_ninode; /* number of free inodes in s_inode, <= COH_NICINOD */
+ sysv_ino_t s_inode[COH_NICINOD]; /* some free inodes */
+ /* locks, not used by Linux: */
+ char s_flock; /* lock during free block list manipulation */
+ char s_ilock; /* lock during inode cache manipulation */
+ char s_fmod; /* super-block modified flag */
+ char s_ronly; /* flag whether fs is mounted read-only */
+ coh_ulong s_time __packed2__; /* time of last super block update */
+ coh_ulong s_tfree __packed2__; /* total number of free zones */
+ u16 s_tinode; /* total number of free inodes */
+ u16 s_interleave_m; /* interleave factor */
+ u16 s_interleave_n;
+ char s_fname[6]; /* file system volume name */
+ char s_fpack[6]; /* file system pack name */
+ u32 s_unique; /* zero, not used */
+};
+
+/* Coherent free list block on disk */
+struct coh_freelist_chunk {
+ u16 fl_nfree; /* number of free blocks in fl_free, <= COH_NICFREE] */
+ u32 fl_free[COH_NICFREE] __packed2__;
+};
+
+
+/* SystemV/Coherent inode data on disk */
+
+struct sysv_inode {
+ u16 i_mode;
+ u16 i_nlink;
+ u16 i_uid;
+ u16 i_gid;
+ u32 i_size;
+ union { /* directories, regular files, ... */
+ unsigned char i_addb[3*(10+1+1+1)+1]; /* zone numbers: max. 10 data blocks,
+ * then 1 indirection block,
+ * then 1 double indirection block,
+ * then 1 triple indirection block.
+ * Then maybe a "file generation number" ??
+ */
+ /* devices */
+ dev_t i_rdev;
+ /* named pipes on Coherent */
+ struct {
+ char p_addp[30];
+ s16 p_pnc;
+ s16 p_prx;
+ s16 p_pwx;
+ } i_p;
+ } i_a;
+ u32 i_atime; /* time of last access */
+ u32 i_mtime; /* time of last modification */
+ u32 i_ctime; /* time of creation */
+};
+
+/* The admissible values for i_mode are listed in <linux/stat.h> :
+ * #define S_IFMT 00170000 mask for type
+ * #define S_IFREG 0100000 type = regular file
+ * #define S_IFBLK 0060000 type = block device
+ * #define S_IFDIR 0040000 type = directory
+ * #define S_IFCHR 0020000 type = character device
+ * #define S_IFIFO 0010000 type = named pipe
+ * #define S_ISUID 0004000 set user id
+ * #define S_ISGID 0002000 set group id
+ * #define S_ISVTX 0001000 save swapped text even after use
+ * Additionally for SystemV:
+ * #define S_IFLNK 0120000 type = symbolic link
+ * #define S_IFNAM 0050000 type = XENIX special named file ??
+ * Additionally for Coherent:
+ * #define S_IFMPB 0070000 type = multiplexed block device ??
+ * #define S_IFMPC 0030000 type = multiplexed character device ??
+ *
+ * Since Coherent doesn't know about symbolic links, we use a kludgey
+ * implementation of symbolic links: i_mode = COH_KLUDGE_SYMLINK_MODE
+ * denotes a symbolic link. When a regular file should get this mode by
+ * accident, it is automatically converted to COH_KLUDGE_NOT_SYMLINK.
+ * We use S_IFREG because only regular files (and Coherent pipes...) can have
+ * data blocks with arbitrary contents associated with them, and S_ISVTX
+ * ("save swapped text after use") because it is unused on both Linux and
+ * Coherent: Linux does much more intelligent paging, and Coherent hasn't
+ * virtual memory at all.
+ * Same trick for Xenix.
+ */
+#define COH_KLUDGE_SYMLINK_MODE (S_IFREG | S_ISVTX)
+#define COH_KLUDGE_NOT_SYMLINK (S_IFREG | S_ISVTX | S_IRUSR) /* force read access */
+extern inline mode_t from_coh_imode(unsigned short mode)
+{
+ if (mode == COH_KLUDGE_SYMLINK_MODE)
+ return (S_IFLNK | 0777);
+ else
+ return mode;
+}
+extern inline unsigned short to_coh_imode(mode_t mode)
+{
+ if (S_ISLNK(mode))
+ return COH_KLUDGE_SYMLINK_MODE;
+ else if (mode == COH_KLUDGE_SYMLINK_MODE)
+ return COH_KLUDGE_NOT_SYMLINK;
+ else
+ return mode;
+}
+
+/* Admissible values for i_nlink: 0.._LINK_MAX */
+#define XENIX_LINK_MAX 126 /* ?? */
+#define SYSV_LINK_MAX 126 /* 127? 251? */
+#define COH_LINK_MAX 10000 /* max number of hard links to an inode */
+
+/* The number of inodes per block is
+ sb->sv_inodes_per_block = block_size / sizeof(struct sysv_inode) */
+/* The number of indirect pointers per block is
+ sb->sv_ind_per_block = block_size / sizeof(u32) */
+
+
+/* SystemV/Coherent directory entry on disk */
+
+#define SYSV_NAMELEN 14 /* max size of name in struct sysv_dir_entry */
+
+struct sysv_dir_entry {
+ sysv_ino_t inode;
+ char name[SYSV_NAMELEN]; /* up to 14 characters, the rest are zeroes */
+};
+
+#define SYSV_DIRSIZE sizeof(struct sysv_dir_entry) /* size of every directory entry */
+
+
+/* Operations */
+/* ========== */
+
+
+/* identify the FS in memory */
+#define FSTYPE_XENIX 1
+#define FSTYPE_SYSV4 2
+#define FSTYPE_SYSV2 3
+#define FSTYPE_COH 4
+
+#define SYSV_MAGIC_BASE 0x012FF7B3
+
+#define XENIX_SUPER_MAGIC (SYSV_MAGIC_BASE+FSTYPE_XENIX)
+#define SYSV4_SUPER_MAGIC (SYSV_MAGIC_BASE+FSTYPE_SYSV4)
+#define SYSV2_SUPER_MAGIC (SYSV_MAGIC_BASE+FSTYPE_SYSV2)
+#define COH_SUPER_MAGIC (SYSV_MAGIC_BASE+FSTYPE_COH)
+
+#ifdef __KERNEL__
+
+/* sv_get_hash_table(sb,dev,block) is equivalent to get_hash_table(dev,block,block_size) */
+static inline struct buffer_head *
+sv_get_hash_table (struct super_block *sb, kdev_t dev, unsigned int block)
+{
+ return get_hash_table (dev, block + sb->sv_block_base, sb->sv_block_size);
+}
+
+/* sv_getblk(sb,dev,block) is equivalent to getblk(dev,block,block_size) */
+static inline struct buffer_head *
+sv_getblk (struct super_block *sb, kdev_t dev, unsigned int block)
+{
+ return getblk (dev, block + sb->sv_block_base, sb->sv_block_size);
+}
+
+/* sv_bread(sb,dev,block) is equivalent to bread(dev,block,block_size) */
+static inline struct buffer_head *
+sv_bread (struct super_block *sb, kdev_t dev, unsigned int block)
+{
+ return bread (dev, block + sb->sv_block_base, sb->sv_block_size);
+}
+
+
+/*
+ * Function prototypes
+ */
+
+extern struct dentry *sysv_lookup(struct inode * dir, struct dentry * dentry);
+extern int sysv_create(struct inode * dir, struct dentry * dentry, int mode);
+extern int sysv_mkdir(struct inode * dir, struct dentry * dentry, int mode);
+extern int sysv_rmdir(struct inode * dir, struct dentry * dentry);
+extern int sysv_unlink(struct inode * dir, struct dentry * dentry);
+extern int sysv_symlink(struct inode * inode, struct dentry * dentry, const char * symname);
+extern int sysv_link(struct dentry * old_dentry, struct inode * dir, struct dentry * dentry);
+extern int sysv_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev);
+extern int sysv_rename(struct inode * old_dir, struct dentry * old_dentry,
+ struct inode * new_dir, struct dentry * new_dentry);
+extern struct inode * sysv_new_inode(const struct inode * dir);
+extern void sysv_free_inode(struct inode * inode);
+extern unsigned long sysv_count_free_inodes(struct super_block *sb);
+extern int sysv_new_block(struct super_block * sb);
+extern void sysv_free_block(struct super_block * sb, unsigned int block);
+extern unsigned long sysv_count_free_blocks(struct super_block *sb);
+
+extern int sysv_bmap(struct inode *,int);
+
+extern struct buffer_head * sysv_getblk(struct inode *, unsigned int, int);
+extern struct buffer_head * sysv_file_bread(struct inode *, int, int);
+extern ssize_t sysv_file_read(struct file *, char *, size_t, loff_t *);
+
+extern void sysv_truncate(struct inode *);
+extern void sysv_put_super(struct super_block *);
+extern struct super_block *sysv_read_super(struct super_block *,void *,int);
+extern int init_sysv_fs(void);
+extern void sysv_write_super(struct super_block *);
+extern void sysv_read_inode(struct inode *);
+extern int sysv_notify_change(struct dentry *, struct iattr *);
+extern void sysv_write_inode(struct inode *);
+extern int sysv_statfs(struct super_block *, struct statfs *, int);
+extern int sysv_sync_inode(struct inode *);
+extern int sysv_sync_file(struct file *, struct dentry *);
+extern int sysv_mmap(struct file *, struct vm_area_struct *);
+
+extern struct inode_operations sysv_file_inode_operations;
+extern struct inode_operations sysv_file_inode_operations_with_bmap;
+extern struct inode_operations sysv_dir_inode_operations;
+extern struct inode_operations sysv_symlink_inode_operations;
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/sysv_fs_i.h b/pfinet/linux-src/include/linux/sysv_fs_i.h
new file mode 100644
index 00000000..c2b578af
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sysv_fs_i.h
@@ -0,0 +1,15 @@
+#ifndef _SYSV_FS_I
+#define _SYSV_FS_I
+
+/*
+ * SystemV/Coherent FS inode data in memory
+ */
+struct sysv_inode_info {
+ u32 i_data[10+1+1+1]; /* zone numbers: max. 10 data blocks,
+ * then 1 indirection block,
+ * then 1 double indirection block,
+ * then 1 triple indirection block.
+ */
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/sysv_fs_sb.h b/pfinet/linux-src/include/linux/sysv_fs_sb.h
new file mode 100644
index 00000000..61af1591
--- /dev/null
+++ b/pfinet/linux-src/include/linux/sysv_fs_sb.h
@@ -0,0 +1,121 @@
+#ifndef _SYSV_FS_SB
+#define _SYSV_FS_SB
+
+/*
+ * SystemV/Coherent super-block data in memory
+ * The SystemV/Coherent superblock contains dynamic data (it gets modified
+ * while the system is running). This is in contrast to the Minix and Berkeley
+ * filesystems (where the superblock is never modified). This affects the
+ * sync() operation: we must keep the superblock in a disk buffer and use this
+ * one as our "working copy".
+ */
+
+struct sysv_sb_info {
+ int s_type; /* file system type: FSTYPE_{XENIX|SYSV|COH} */
+ unsigned int s_block_size; /* zone size, = 512 or = 1024 */
+ unsigned int s_block_size_1; /* block_size - 1 */
+ unsigned int s_block_size_bits; /* log2(block_size) */
+ unsigned int s_block_size_inc_bits; /* log2(block_size/BLOCK_SIZE) if >0 */
+ unsigned int s_block_size_dec_bits; /* log2(BLOCK_SIZE/block_size) if >0 */
+ char s_convert; /* flag whether byte ordering requires conversion */
+ char s_kludge_symlinks; /* flag whether symlinks have a kludgey mode */
+ char s_truncate; /* if 1: names > SYSV_NAMELEN chars are truncated */
+ /* if 0: they are disallowed (ENAMETOOLONG) */
+ nlink_t s_link_max; /* max number of hard links to a file */
+ unsigned int s_inodes_per_block; /* number of inodes per block */
+ unsigned int s_inodes_per_block_1; /* inodes_per_block - 1 */
+ unsigned int s_inodes_per_block_bits; /* log2(inodes_per_block) */
+ unsigned int s_ind_per_block; /* number of indirections per block */
+ unsigned int s_ind_per_block_1; /* ind_per_block - 1 */
+ unsigned int s_ind_per_block_bits; /* log2(ind_per_block) */
+ unsigned int s_ind_per_block_2; /* ind_per_block ^ 2 */
+ unsigned int s_ind_per_block_2_1; /* ind_per_block ^ 2 - 1 */
+ unsigned int s_ind_per_block_2_bits; /* log2(ind_per_block^2) */
+ unsigned int s_ind_per_block_3; /* ind_per_block ^ 3 */
+ unsigned int s_ind_per_block_block_size_1; /* ind_per_block*block_size - 1 */
+ unsigned int s_ind_per_block_block_size_bits; /* log2(ind_per_block*block_size) */
+ unsigned int s_ind_per_block_2_block_size_1; /* ind_per_block^2 * block_size - 1 */
+ unsigned int s_ind_per_block_2_block_size_bits; /* log2(ind_per_block^2 * block_size) */
+ unsigned int s_ind0_size; /* 10 * block_size */
+ unsigned int s_ind1_size; /* (10 + ipb) * block_size */
+ unsigned int s_ind2_size; /* (10 + ipb + ipb^2) * block_size */
+ unsigned int s_toobig_block; /* 10 + ipb + ipb^2 + ipb^3 */
+ unsigned int s_block_base; /* physical block number of block 0 */
+ unsigned short s_fic_size; /* free inode cache size, NICINOD */
+ unsigned short s_flc_size; /* free block list chunk size, NICFREE */
+ /* The superblock is kept in one or two disk buffers: */
+ struct buffer_head *s_bh1;
+ struct buffer_head *s_bh2;
+ /* These are pointers into the disk buffer, to compensate for
+ different superblock layout. */
+ char * s_sbd1; /* entire superblock data, for part 1 */
+ char * s_sbd2; /* entire superblock data, for part 2 */
+ u16 *s_sb_fic_count; /* pointer to s_sbd->s_ninode */
+ u16 *s_sb_fic_inodes; /* pointer to s_sbd->s_inode */
+ u16 *s_sb_total_free_inodes; /* pointer to s_sbd->s_tinode */
+ u16 *s_sb_flc_count; /* pointer to s_sbd->s_nfree */
+ u32 *s_sb_flc_blocks; /* pointer to s_sbd->s_free */
+ u32 *s_sb_total_free_blocks;/* pointer to s_sbd->s_tfree */
+ u32 *s_sb_time; /* pointer to s_sbd->s_time */
+ u32 *s_sb_state; /* pointer to s_sbd->s_state, only FSTYPE_SYSV */
+ /* We keep those superblock entities that don't change here;
+ this saves us an indirection and perhaps a conversion. */
+ u32 s_firstinodezone; /* index of first inode zone */
+ u32 s_firstdatazone; /* same as s_sbd->s_isize */
+ u32 s_ninodes; /* total number of inodes */
+ u32 s_ndatazones; /* total number of data zones */
+ u32 s_nzones; /* same as s_sbd->s_fsize */
+};
+/* The fields s_ind_per_block_2_1, s_toobig_block are currently unused. */
+
+/* sv_ == u.sysv_sb.s_ */
+#define sv_type u.sysv_sb.s_type
+#define sv_block_size u.sysv_sb.s_block_size
+#define sv_block_size_1 u.sysv_sb.s_block_size_1
+#define sv_block_size_bits u.sysv_sb.s_block_size_bits
+#define sv_block_size_inc_bits u.sysv_sb.s_block_size_inc_bits
+#define sv_block_size_dec_bits u.sysv_sb.s_block_size_dec_bits
+#define sv_convert u.sysv_sb.s_convert
+#define sv_kludge_symlinks u.sysv_sb.s_kludge_symlinks
+#define sv_truncate u.sysv_sb.s_truncate
+#define sv_link_max u.sysv_sb.s_link_max
+#define sv_inodes_per_block u.sysv_sb.s_inodes_per_block
+#define sv_inodes_per_block_1 u.sysv_sb.s_inodes_per_block_1
+#define sv_inodes_per_block_bits u.sysv_sb.s_inodes_per_block_bits
+#define sv_ind_per_block u.sysv_sb.s_ind_per_block
+#define sv_ind_per_block_1 u.sysv_sb.s_ind_per_block_1
+#define sv_ind_per_block_bits u.sysv_sb.s_ind_per_block_bits
+#define sv_ind_per_block_2 u.sysv_sb.s_ind_per_block_2
+#define sv_ind_per_block_2_1 u.sysv_sb.s_ind_per_block_2_1
+#define sv_ind_per_block_2_bits u.sysv_sb.s_ind_per_block_2_bits
+#define sv_ind_per_block_3 u.sysv_sb.s_ind_per_block_3
+#define sv_ind_per_block_block_size_1 u.sysv_sb.s_ind_per_block_block_size_1
+#define sv_ind_per_block_block_size_bits u.sysv_sb.s_ind_per_block_block_size_bits
+#define sv_ind_per_block_2_block_size_1 u.sysv_sb.s_ind_per_block_2_block_size_1
+#define sv_ind_per_block_2_block_size_bits u.sysv_sb.s_ind_per_block_2_block_size_bits
+#define sv_ind0_size u.sysv_sb.s_ind0_size
+#define sv_ind1_size u.sysv_sb.s_ind1_size
+#define sv_ind2_size u.sysv_sb.s_ind2_size
+#define sv_toobig_block u.sysv_sb.s_toobig_block
+#define sv_block_base u.sysv_sb.s_block_base
+#define sv_fic_size u.sysv_sb.s_fic_size
+#define sv_flc_size u.sysv_sb.s_flc_size
+#define sv_bh1 u.sysv_sb.s_bh1
+#define sv_bh2 u.sysv_sb.s_bh2
+#define sv_sbd1 u.sysv_sb.s_sbd1
+#define sv_sbd2 u.sysv_sb.s_sbd2
+#define sv_sb_fic_count u.sysv_sb.s_sb_fic_count
+#define sv_sb_fic_inodes u.sysv_sb.s_sb_fic_inodes
+#define sv_sb_total_free_inodes u.sysv_sb.s_sb_total_free_inodes
+#define sv_sb_flc_count u.sysv_sb.s_sb_flc_count
+#define sv_sb_flc_blocks u.sysv_sb.s_sb_flc_blocks
+#define sv_sb_total_free_blocks u.sysv_sb.s_sb_total_free_blocks
+#define sv_sb_time u.sysv_sb.s_sb_time
+#define sv_sb_state u.sysv_sb.s_sb_state
+#define sv_firstinodezone u.sysv_sb.s_firstinodezone
+#define sv_firstdatazone u.sysv_sb.s_firstdatazone
+#define sv_ninodes u.sysv_sb.s_ninodes
+#define sv_ndatazones u.sysv_sb.s_ndatazones
+#define sv_nzones u.sysv_sb.s_nzones
+
+#endif
diff --git a/pfinet/linux-src/include/linux/tasks.h b/pfinet/linux-src/include/linux/tasks.h
new file mode 100644
index 00000000..91b758f4
--- /dev/null
+++ b/pfinet/linux-src/include/linux/tasks.h
@@ -0,0 +1,25 @@
+#ifndef _LINUX_TASKS_H
+#define _LINUX_TASKS_H
+
+/*
+ * This is the maximum nr of tasks - change it if you need to
+ */
+
+#ifdef __SMP__
+#define NR_CPUS 32 /* Max processors that can be running in SMP */
+#else
+#define NR_CPUS 1
+#endif
+
+#define NR_TASKS 512 /* On x86 Max 4092, or 4090 w/APM configured. */
+
+#define MAX_TASKS_PER_USER (NR_TASKS/2)
+#define MIN_TASKS_LEFT_FOR_ROOT 4
+
+
+/*
+ * This controls the maximum pid allocated to a process
+ */
+#define PID_MAX 0x8000
+
+#endif
diff --git a/pfinet/linux-src/include/linux/tcp.h b/pfinet/linux-src/include/linux/tcp.h
new file mode 100644
index 00000000..9ee71810
--- /dev/null
+++ b/pfinet/linux-src/include/linux/tcp.h
@@ -0,0 +1,90 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the TCP protocol.
+ *
+ * Version: @(#)tcp.h 1.0.2 04/28/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_TCP_H
+#define _LINUX_TCP_H
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+struct tcphdr {
+ __u16 source;
+ __u16 dest;
+ __u32 seq;
+ __u32 ack_seq;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u16 res1:4,
+ doff:4,
+ fin:1,
+ syn:1,
+ rst:1,
+ psh:1,
+ ack:1,
+ urg:1,
+ res2:2;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u16 doff:4,
+ res1:4,
+ res2:2,
+ urg:1,
+ ack:1,
+ psh:1,
+ rst:1,
+ syn:1,
+ fin:1;
+#else
+#error "Adjust your <asm/byteorder.h> defines"
+#endif
+ __u16 window;
+ __u16 check;
+ __u16 urg_ptr;
+};
+
+
+enum {
+ TCP_ESTABLISHED = 1,
+ TCP_SYN_SENT,
+ TCP_SYN_RECV,
+ TCP_FIN_WAIT1,
+ TCP_FIN_WAIT2,
+ TCP_TIME_WAIT,
+ TCP_CLOSE,
+ TCP_CLOSE_WAIT,
+ TCP_LAST_ACK,
+ TCP_LISTEN,
+ TCP_CLOSING, /* now a valid state */
+
+ TCP_MAX_STATES /* Leave at the end! */
+};
+
+#define TCP_STATE_MASK 0xF
+#define TCP_ACTION_FIN (1 << 7)
+
+enum {
+ TCPF_ESTABLISHED = (1 << 1),
+ TCPF_SYN_SENT = (1 << 2),
+ TCPF_SYN_RECV = (1 << 3),
+ TCPF_FIN_WAIT1 = (1 << 4),
+ TCPF_FIN_WAIT2 = (1 << 5),
+ TCPF_TIME_WAIT = (1 << 6),
+ TCPF_CLOSE = (1 << 7),
+ TCPF_CLOSE_WAIT = (1 << 8),
+ TCPF_LAST_ACK = (1 << 9),
+ TCPF_LISTEN = (1 << 10),
+ TCPF_CLOSING = (1 << 11)
+};
+
+#endif /* _LINUX_TCP_H */
diff --git a/pfinet/linux-src/include/linux/telephony.h b/pfinet/linux-src/include/linux/telephony.h
new file mode 100644
index 00000000..0e4a95a4
--- /dev/null
+++ b/pfinet/linux-src/include/linux/telephony.h
@@ -0,0 +1,200 @@
+/*
+ * telephony.h
+ *
+ * Basic Linux Telephony Interface
+ *
+ * (c) Copyright 1999 Quicknet Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Ed Okerson, <eokerson@quicknet.net>
+ * Greg Herlein, <gherlein@quicknet.net>
+ *
+ * Contributors: Alan Cox, <acox@redhat.com>
+ * David Erhart, <derhart@quicknet.net>
+ *
+ * Version: 0.1.0 - December 19, 1999
+ *
+ * Fixes:
+ */
+
+#ifndef TELEPHONY_H
+#define TELEPHONY_H
+
+/* vendor identification numbers */
+#define PHONE_VENDOR_IXJ 1
+#define PHONE_VENDOR_QUICKNET PHONE_IXJ
+#define PHONE_VENDOR_VOICETRONIX 2
+#define PHONE_VENDOR_ACULAB 3
+#define PHONE_VENDOR_DIGI 4
+#define PHONE_VENDOR_FRANKLIN 5
+
+/******************************************************************************
+ * Vendor Summary Information Area
+ *
+ * Quicknet Technologies, Inc. - makes low density analog telephony cards
+ * with audio compression, POTS and PSTN interfaces (www.quicknet.net)
+ *
+ * (other vendors following this API shuld add a short description of
+ * the telephony products they support under Linux)
+ *
+ *****************************************************************************/
+
+
+/******************************************************************************
+*
+* The capabilities ioctls can inform you of the capabilities of each phone
+* device installed in your system. The PHONECTL_CAPABILITIES ioctl
+* returns an integer value indicating the number of capabilities the
+* device has. The PHONECTL_CAPABILITIES_LIST will fill an array of
+* capability structs with all of it's capabilities. The
+* PHONECTL_CAPABILITIES_CHECK takes a single capability struct and returns
+* a TRUE if the device has that capability, otherwise it returns false.
+*
+******************************************************************************/
+typedef enum {
+ vendor = 0,
+ device,
+ port,
+ codec,
+ dsp
+} phone_cap;
+
+struct phone_capability {
+ char desc[80];
+ phone_cap captype;
+ int cap;
+ int handle;
+};
+
+typedef enum {
+ pots = 0,
+ pstn,
+ handset,
+ speaker
+} phone_ports;
+
+#define PHONE_CAPABILITIES _IO ('q', 0x80)
+#define PHONE_CAPABILITIES_LIST _IOR ('q', 0x81, struct phone_capability *)
+#define PHONE_CAPABILITIES_CHECK _IOW ('q', 0x82, struct phone_capability *)
+
+#define PHONE_RING _IO ('q', 0x83)
+#define PHONE_HOOKSTATE _IO ('q', 0x84)
+#define PHONE_MAXRINGS _IOW ('q', 0x85, char)
+#define PHONE_RING_CADENCE _IOW ('q', 0x86, short)
+#define PHONE_RING_START _IO ('q', 0x87)
+#define PHONE_RING_STOP _IO ('q', 0x88)
+
+#define USA_RING_CADENCE 0xC0C0
+
+#define PHONE_REC_CODEC _IOW ('q', 0x89, int)
+#define PHONE_REC_START _IO ('q', 0x8A)
+#define PHONE_REC_STOP _IO ('q', 0x8B)
+#define PHONE_REC_DEPTH _IOW ('q', 0x8C, int)
+#define PHONE_FRAME _IOW ('q', 0x8D, int)
+#define PHONE_REC_VOLUME _IOW ('q', 0x8E, int)
+#define PHONE_REC_LEVEL _IO ('q', 0x8F)
+
+#define PHONE_PLAY_CODEC _IOW ('q', 0x90, int)
+#define PHONE_PLAY_START _IO ('q', 0x91)
+#define PHONE_PLAY_STOP _IO ('q', 0x92)
+#define PHONE_PLAY_DEPTH _IOW ('q', 0x93, int)
+#define PHONE_PLAY_VOLUME _IOW ('q', 0x94, int)
+#define PHONE_PLAY_LEVEL _IO ('q', 0x95)
+#define PHONE_DTMF_READY _IOR ('q', 0x96, int)
+#define PHONE_GET_DTMF _IOR ('q', 0x97, int)
+#define PHONE_GET_DTMF_ASCII _IOR ('q', 0x98, int)
+#define PHONE_DTMF_OOB _IOW ('q', 0x99, int)
+#define PHONE_EXCEPTION _IOR ('q', 0x9A, int)
+#define PHONE_PLAY_TONE _IOW ('q', 0x9B, char)
+#define PHONE_SET_TONE_ON_TIME _IOW ('q', 0x9C, int)
+#define PHONE_SET_TONE_OFF_TIME _IOW ('q', 0x9D, int)
+#define PHONE_GET_TONE_ON_TIME _IO ('q', 0x9E)
+#define PHONE_GET_TONE_OFF_TIME _IO ('q', 0x9F)
+#define PHONE_GET_TONE_STATE _IO ('q', 0xA0)
+#define PHONE_BUSY _IO ('q', 0xA1)
+#define PHONE_RINGBACK _IO ('q', 0xA2)
+#define PHONE_DIALTONE _IO ('q', 0xA3)
+#define PHONE_CPT_STOP _IO ('q', 0xA4)
+
+#define PHONE_PSTN_SET_STATE _IOW ('q', 0xA4, int)
+#define PHONE_PSTN_GET_STATE _IO ('q', 0xA5)
+
+#define PSTN_ON_HOOK 0
+#define PSTN_RINGING 1
+#define PSTN_OFF_HOOK 2
+#define PSTN_PULSE_DIAL 3
+
+/******************************************************************************
+*
+* The wink duration is tunable with this ioctl. The default wink duration
+* is 320ms. You do not need to use this ioctl if you do not require a
+* different wink duration.
+*
+******************************************************************************/
+#define PHONE_WINK_DURATION _IOW ('q', 0xA6, int)
+
+
+/******************************************************************************
+*
+* Codec Definitions
+*
+******************************************************************************/
+typedef enum {
+ G723_63 = 1,
+ G723_53 = 2,
+ TS85 = 3,
+ TS48 = 4,
+ TS41 = 5,
+ G728 = 6,
+ G729 = 7,
+ ULAW = 8,
+ ALAW = 9,
+ LINEAR16 = 10,
+ LINEAR8 = 11,
+ WSS = 12
+} phone_codec;
+
+/******************************************************************************
+*
+* The exception structure allows us to multiplex multiple events onto the
+* select() exception set. If any of these flags are set select() will
+* return with a positive indication on the exception set. The dtmf_ready
+* bit indicates if there is data waiting in the DTMF buffer. The
+* hookstate bit is set if there is a change in hookstate status, it does not
+* indicate the current state of the hookswitch. The pstn_ring bit
+* indicates that the DAA on a LineJACK card has detected ring voltage on
+* the PSTN port. The caller_id bit indicates that caller_id data has been
+* received and is available. The pstn_wink bit indicates that the DAA on
+* the LineJACK has received a wink from the telco switch. The f0, f1, f2
+* and f3 bits indicate that the filter has been triggered by detecting the
+* frequency programmed into that filter.
+*
+* The remaining bits should be set to zero. They will become defined over time
+* for other interface cards and their needs.
+*
+******************************************************************************/
+struct phone_except
+{
+ unsigned int dtmf_ready:1;
+ unsigned int hookstate:1;
+ unsigned int pstn_ring:1;
+ unsigned int caller_id:1;
+ unsigned int pstn_wink:1;
+ unsigned int f0:1;
+ unsigned int f1:1;
+ unsigned int f2:1;
+ unsigned int f3:1;
+ unsigned int reserved:23;
+};
+
+union telephony_exception {
+ struct phone_except bits;
+ unsigned int bytes;
+};
+
+
+#endif /* TELEPHONY_H */
diff --git a/pfinet/linux-src/include/linux/termios.h b/pfinet/linux-src/include/linux/termios.h
new file mode 100644
index 00000000..47866288
--- /dev/null
+++ b/pfinet/linux-src/include/linux/termios.h
@@ -0,0 +1,7 @@
+#ifndef _LINUX_TERMIOS_H
+#define _LINUX_TERMIOS_H
+
+#include <linux/types.h>
+#include <asm/termios.h>
+
+#endif
diff --git a/pfinet/linux-src/include/linux/time.h b/pfinet/linux-src/include/linux/time.h
new file mode 100644
index 00000000..53a125a0
--- /dev/null
+++ b/pfinet/linux-src/include/linux/time.h
@@ -0,0 +1,92 @@
+#ifndef _LINUX_TIME_H
+#define _LINUX_TIME_H
+
+#include <asm/param.h>
+#include <linux/types.h>
+
+#ifndef _STRUCT_TIMESPEC
+#define _STRUCT_TIMESPEC
+struct timespec {
+ time_t tv_sec; /* seconds */
+ long tv_nsec; /* nanoseconds */
+};
+#endif /* _STRUCT_TIMESPEC */
+
+/*
+ * Change timeval to jiffies, trying to avoid the
+ * most obvious overflows..
+ *
+ * And some not so obvious.
+ *
+ * Note that we don't want to return MAX_LONG, because
+ * for various timeout reasons we often end up having
+ * to wait "jiffies+1" in order to guarantee that we wait
+ * at _least_ "jiffies" - so "jiffies+1" had better still
+ * be positive.
+ */
+#define MAX_JIFFY_OFFSET ((~0UL >> 1)-1)
+
+static __inline__ unsigned long
+timespec_to_jiffies(struct timespec *value)
+{
+ unsigned long sec = value->tv_sec;
+ long nsec = value->tv_nsec;
+
+ if (sec >= (MAX_JIFFY_OFFSET / HZ))
+ return MAX_JIFFY_OFFSET;
+ nsec += 1000000000L / HZ - 1;
+ nsec /= 1000000000L / HZ;
+ return HZ * sec + nsec;
+}
+
+static __inline__ void
+jiffies_to_timespec(unsigned long jiffies, struct timespec *value)
+{
+ value->tv_nsec = (jiffies % HZ) * (1000000000L / HZ);
+ value->tv_sec = jiffies / HZ;
+}
+
+struct timeval {
+ time_t tv_sec; /* seconds */
+ suseconds_t tv_usec; /* microseconds */
+};
+
+struct timezone {
+ int tz_minuteswest; /* minutes west of Greenwich */
+ int tz_dsttime; /* type of dst correction */
+};
+
+#define NFDBITS __NFDBITS
+
+#ifdef __KERNEL__
+extern void do_gettimeofday(struct timeval *tv);
+extern void do_settimeofday(struct timeval *tv);
+extern void get_fast_time(struct timeval *tv);
+extern void (*do_get_fast_time)(struct timeval *);
+#endif
+
+#define FD_SETSIZE __FD_SETSIZE
+#define FD_SET(fd,fdsetp) __FD_SET(fd,fdsetp)
+#define FD_CLR(fd,fdsetp) __FD_CLR(fd,fdsetp)
+#define FD_ISSET(fd,fdsetp) __FD_ISSET(fd,fdsetp)
+#define FD_ZERO(fdsetp) __FD_ZERO(fdsetp)
+
+/*
+ * Names of the interval timers, and structure
+ * defining a timer setting.
+ */
+#define ITIMER_REAL 0
+#define ITIMER_VIRTUAL 1
+#define ITIMER_PROF 2
+
+struct itimerspec {
+ struct timespec it_interval; /* timer period */
+ struct timespec it_value; /* timer expiration */
+};
+
+struct itimerval {
+ struct timeval it_interval; /* timer interval */
+ struct timeval it_value; /* current value */
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/timer.h b/pfinet/linux-src/include/linux/timer.h
new file mode 100644
index 00000000..56f39893
--- /dev/null
+++ b/pfinet/linux-src/include/linux/timer.h
@@ -0,0 +1,96 @@
+#ifndef _LINUX_TIMER_H
+#define _LINUX_TIMER_H
+
+/*
+ * Old-style timers. Please don't use for any new code.
+ *
+ * Numbering of these timers should be consecutive to minimize
+ * processing delays. [MJ]
+ */
+
+#define BLANK_TIMER 0 /* Console screen-saver */
+#define BEEP_TIMER 1 /* Console beep */
+#define RS_TIMER 2 /* RS-232 ports */
+#define SWAP_TIMER 3 /* Background pageout */
+#define BACKGR_TIMER 4 /* io_request background I/O */
+#define HD_TIMER 5 /* Old IDE driver */
+#define FLOPPY_TIMER 6 /* Floppy */
+#define QIC02_TAPE_TIMER 7 /* QIC 02 tape */
+#define MCD_TIMER 8 /* Mitsumi CDROM */
+#define GSCD_TIMER 9 /* Goldstar CDROM */
+#define COMTROL_TIMER 10 /* Comtrol serial */
+#define DIGI_TIMER 11 /* Digi serial */
+#define GDTH_TIMER 12 /* Ugh - gdth scsi driver */
+
+#define COPRO_TIMER 31 /* 387 timeout for buggy hardware (boot only) */
+
+struct timer_struct {
+ unsigned long expires;
+ void (*fn)(void);
+};
+
+extern unsigned long timer_active;
+extern struct timer_struct timer_table[32];
+
+/*
+ * This is completely separate from the above, and is the
+ * "new and improved" way of handling timers more dynamically.
+ * Hopefully efficient and general enough for most things.
+ *
+ * The "hardcoded" timers above are still useful for well-
+ * defined problems, but the timer-list is probably better
+ * when you need multiple outstanding timers or similar.
+ *
+ * The "data" field is in case you want to use the same
+ * timeout function for several timeouts. You can use this
+ * to distinguish between the different invocations.
+ */
+struct timer_list {
+ struct timer_list *next; /* MUST be first element */
+ struct timer_list *prev;
+ unsigned long expires;
+ unsigned long data;
+ void (*function)(unsigned long);
+};
+
+extern void add_timer(struct timer_list * timer);
+extern int del_timer(struct timer_list * timer);
+
+/*
+ * mod_timer is a more efficient way to update the expire field of an
+ * active timer (if the timer is inactive it will be activated)
+ * mod_timer(a,b) is equivalent to del_timer(a); a->expires = b; add_timer(a)
+ */
+void mod_timer(struct timer_list *timer, unsigned long expires);
+
+extern void it_real_fn(unsigned long);
+
+extern inline void init_timer(struct timer_list * timer)
+{
+ timer->next = NULL;
+ timer->prev = NULL;
+}
+
+extern inline int timer_pending(struct timer_list * timer)
+{
+ return timer->prev != NULL;
+}
+
+/*
+ * These inlines deal with timer wrapping correctly. You are
+ * strongly encouraged to use them
+ * 1. Because people otherwise forget
+ * 2. Because if the timer wrap changes in future you wont have to
+ * alter your driver code.
+ *
+ * Do this with "<0" and ">=0" to only test the sign of the result. A
+ * good compiler would generate better code (and a really good compiler
+ * wouldn't care). Gcc is currently neither.
+ */
+#define time_after(a,b) ((long)(b) - (long)(a) < 0)
+#define time_before(a,b) time_after(b,a)
+
+#define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0)
+#define time_before_eq(a,b) time_after_eq(b,a)
+
+#endif
diff --git a/pfinet/linux-src/include/linux/times.h b/pfinet/linux-src/include/linux/times.h
new file mode 100644
index 00000000..569349ef
--- /dev/null
+++ b/pfinet/linux-src/include/linux/times.h
@@ -0,0 +1,11 @@
+#ifndef _LINUX_TIMES_H
+#define _LINUX_TIMES_H
+
+struct tms {
+ clock_t tms_utime;
+ clock_t tms_stime;
+ clock_t tms_cutime;
+ clock_t tms_cstime;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/timex.h b/pfinet/linux-src/include/linux/timex.h
new file mode 100644
index 00000000..653009ad
--- /dev/null
+++ b/pfinet/linux-src/include/linux/timex.h
@@ -0,0 +1,277 @@
+/*****************************************************************************
+ * *
+ * Copyright (c) David L. Mills 1993 *
+ * *
+ * Permission to use, copy, modify, and distribute this software and its *
+ * documentation for any purpose and without fee is hereby granted, provided *
+ * that the above copyright notice appears in all copies and that both the *
+ * copyright notice and this permission notice appear in supporting *
+ * documentation, and that the name University of Delaware not be used in *
+ * advertising or publicity pertaining to distribution of the software *
+ * without specific, written prior permission. The University of Delaware *
+ * makes no representations about the suitability this software for any *
+ * purpose. It is provided "as is" without express or implied warranty. *
+ * *
+ *****************************************************************************/
+
+/*
+ * Modification history timex.h
+ *
+ * 29 Dec 97 Russell King
+ * Moved CLOCK_TICK_RATE, CLOCK_TICK_FACTOR and FINETUNE to asm/timex.h
+ * for ARM machines
+ *
+ * 9 Jan 97 Adrian Sun
+ * Shifted LATCH define to allow access to alpha machines.
+ *
+ * 26 Sep 94 David L. Mills
+ * Added defines for hybrid phase/frequency-lock loop.
+ *
+ * 19 Mar 94 David L. Mills
+ * Moved defines from kernel routines to header file and added new
+ * defines for PPS phase-lock loop.
+ *
+ * 20 Feb 94 David L. Mills
+ * Revised status codes and structures for external clock and PPS
+ * signal discipline.
+ *
+ * 28 Nov 93 David L. Mills
+ * Adjusted parameters to improve stability and increase poll
+ * interval.
+ *
+ * 17 Sep 93 David L. Mills
+ * Created file $NTP/include/sys/timex.h
+ * 07 Oct 93 Torsten Duwe
+ * Derived linux/timex.h
+ * 1995-08-13 Torsten Duwe
+ * kernel PLL updated to 1994-12-13 specs (rfc-1589)
+ * 1997-08-30 Ulrich Windl
+ * Added new constant NTP_PHASE_LIMIT
+ */
+#ifndef _LINUX_TIMEX_H
+#define _LINUX_TIMEX_H
+
+/*
+ * The following defines establish the engineering parameters of the PLL
+ * model. The HZ variable establishes the timer interrupt frequency, 100 Hz
+ * for the SunOS kernel, 256 Hz for the Ultrix kernel and 1024 Hz for the
+ * OSF/1 kernel. The SHIFT_HZ define expresses the same value as the
+ * nearest power of two in order to avoid hardware multiply operations.
+ */
+#ifdef __alpha__
+# define SHIFT_HZ 10 /* log2(HZ) */
+#else
+# define SHIFT_HZ 7 /* log2(HZ) */
+#endif
+
+/*
+ * SHIFT_KG and SHIFT_KF establish the damping of the PLL and are chosen
+ * for a slightly underdamped convergence characteristic. SHIFT_KH
+ * establishes the damping of the FLL and is chosen by wisdom and black
+ * art.
+ *
+ * MAXTC establishes the maximum time constant of the PLL. With the
+ * SHIFT_KG and SHIFT_KF values given and a time constant range from
+ * zero to MAXTC, the PLL will converge in 15 minutes to 16 hours,
+ * respectively.
+ */
+#define SHIFT_KG 6 /* phase factor (shift) */
+#define SHIFT_KF 16 /* PLL frequency factor (shift) */
+#define SHIFT_KH 2 /* FLL frequency factor (shift) */
+#define MAXTC 6 /* maximum time constant (shift) */
+
+/*
+ * The SHIFT_SCALE define establishes the decimal point of the time_phase
+ * variable which serves as an extension to the low-order bits of the
+ * system clock variable. The SHIFT_UPDATE define establishes the decimal
+ * point of the time_offset variable which represents the current offset
+ * with respect to standard time. The FINEUSEC define represents 1 usec in
+ * scaled units.
+ *
+ * SHIFT_USEC defines the scaling (shift) of the time_freq and
+ * time_tolerance variables, which represent the current frequency
+ * offset and maximum frequency tolerance.
+ *
+ * FINEUSEC is 1 us in SHIFT_UPDATE units of the time_phase variable.
+ */
+#define SHIFT_SCALE 22 /* phase scale (shift) */
+#define SHIFT_UPDATE (SHIFT_KG + MAXTC) /* time offset scale (shift) */
+#define SHIFT_USEC 16 /* frequency offset scale (shift) */
+#define FINEUSEC (1L << SHIFT_SCALE) /* 1 us in phase units */
+
+#define MAXPHASE 512000L /* max phase error (us) */
+#define MAXFREQ (512L << SHIFT_USEC) /* max frequency error (ppm) */
+#define MAXTIME (200L << PPS_AVG) /* max PPS error (jitter) (200 us) */
+#define MINSEC 16L /* min interval between updates (s) */
+#define MAXSEC 1200L /* max interval between updates (s) */
+#define NTP_PHASE_LIMIT (MAXPHASE << 5) /* beyond max. dispersion */
+
+/*
+ * The following defines are used only if a pulse-per-second (PPS)
+ * signal is available and connected via a modem control lead, such as
+ * produced by the optional ppsclock feature incorporated in the Sun
+ * asynch driver. They establish the design parameters of the frequency-
+ * lock loop used to discipline the CPU clock oscillator to the PPS
+ * signal.
+ *
+ * PPS_AVG is the averaging factor for the frequency loop, as well as
+ * the time and frequency dispersion.
+ *
+ * PPS_SHIFT and PPS_SHIFTMAX specify the minimum and maximum
+ * calibration intervals, respectively, in seconds as a power of two.
+ *
+ * PPS_VALID is the maximum interval before the PPS signal is considered
+ * invalid and protocol updates used directly instead.
+ *
+ * MAXGLITCH is the maximum interval before a time offset of more than
+ * MAXTIME is believed.
+ */
+#define PPS_AVG 2 /* pps averaging constant (shift) */
+#define PPS_SHIFT 2 /* min interval duration (s) (shift) */
+#define PPS_SHIFTMAX 8 /* max interval duration (s) (shift) */
+#define PPS_VALID 120 /* pps signal watchdog max (s) */
+#define MAXGLITCH 30 /* pps signal glitch max (s) */
+
+/*
+ * Pick up the architecture specific timex specifications
+ */
+#include <asm/timex.h>
+
+/* LATCH is used in the interval timer and ftape setup. */
+#define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */
+
+/*
+ * syscall interface - used (mainly by NTP daemon)
+ * to discipline kernel clock oscillator
+ */
+struct timex {
+ unsigned int modes; /* mode selector */
+ long offset; /* time offset (usec) */
+ long freq; /* frequency offset (scaled ppm) */
+ long maxerror; /* maximum error (usec) */
+ long esterror; /* estimated error (usec) */
+ int status; /* clock command/status */
+ long constant; /* pll time constant */
+ long precision; /* clock precision (usec) (read only) */
+ long tolerance; /* clock frequency tolerance (ppm)
+ * (read only)
+ */
+ struct timeval time; /* (read only) */
+ long tick; /* (modified) usecs between clock ticks */
+
+ long ppsfreq; /* pps frequency (scaled ppm) (ro) */
+ long jitter; /* pps jitter (us) (ro) */
+ int shift; /* interval duration (s) (shift) (ro) */
+ long stabil; /* pps stability (scaled ppm) (ro) */
+ long jitcnt; /* jitter limit exceeded (ro) */
+ long calcnt; /* calibration intervals (ro) */
+ long errcnt; /* calibration errors (ro) */
+ long stbcnt; /* stability limit exceeded (ro) */
+
+ int :32; int :32; int :32; int :32;
+ int :32; int :32; int :32; int :32;
+ int :32; int :32; int :32; int :32;
+};
+
+/*
+ * Mode codes (timex.mode)
+ */
+#define ADJ_OFFSET 0x0001 /* time offset */
+#define ADJ_FREQUENCY 0x0002 /* frequency offset */
+#define ADJ_MAXERROR 0x0004 /* maximum time error */
+#define ADJ_ESTERROR 0x0008 /* estimated time error */
+#define ADJ_STATUS 0x0010 /* clock status */
+#define ADJ_TIMECONST 0x0020 /* pll time constant */
+#define ADJ_TICK 0x4000 /* tick value */
+#define ADJ_OFFSET_SINGLESHOT 0x8001 /* old-fashioned adjtime */
+
+/* xntp 3.4 compatibility names */
+#define MOD_OFFSET ADJ_OFFSET
+#define MOD_FREQUENCY ADJ_FREQUENCY
+#define MOD_MAXERROR ADJ_MAXERROR
+#define MOD_ESTERROR ADJ_ESTERROR
+#define MOD_STATUS ADJ_STATUS
+#define MOD_TIMECONST ADJ_TIMECONST
+#define MOD_CLKB ADJ_TICK
+#define MOD_CLKA ADJ_OFFSET_SINGLESHOT /* 0x8000 in original */
+
+
+/*
+ * Status codes (timex.status)
+ */
+#define STA_PLL 0x0001 /* enable PLL updates (rw) */
+#define STA_PPSFREQ 0x0002 /* enable PPS freq discipline (rw) */
+#define STA_PPSTIME 0x0004 /* enable PPS time discipline (rw) */
+#define STA_FLL 0x0008 /* select frequency-lock mode (rw) */
+
+#define STA_INS 0x0010 /* insert leap (rw) */
+#define STA_DEL 0x0020 /* delete leap (rw) */
+#define STA_UNSYNC 0x0040 /* clock unsynchronized (rw) */
+#define STA_FREQHOLD 0x0080 /* hold frequency (rw) */
+
+#define STA_PPSSIGNAL 0x0100 /* PPS signal present (ro) */
+#define STA_PPSJITTER 0x0200 /* PPS signal jitter exceeded (ro) */
+#define STA_PPSWANDER 0x0400 /* PPS signal wander exceeded (ro) */
+#define STA_PPSERROR 0x0800 /* PPS signal calibration error (ro) */
+
+#define STA_CLOCKERR 0x1000 /* clock hardware fault (ro) */
+
+#define STA_RONLY (STA_PPSSIGNAL | STA_PPSJITTER | STA_PPSWANDER | \
+ STA_PPSERROR | STA_CLOCKERR) /* read-only bits */
+
+/*
+ * Clock states (time_state)
+ */
+#define TIME_OK 0 /* clock synchronized, no leap second */
+#define TIME_INS 1 /* insert leap second */
+#define TIME_DEL 2 /* delete leap second */
+#define TIME_OOP 3 /* leap second in progress */
+#define TIME_WAIT 4 /* leap second has occurred */
+#define TIME_ERROR 5 /* clock not synchronized */
+#define TIME_BAD TIME_ERROR /* bw compat */
+
+#ifdef __KERNEL__
+/*
+ * kernel variables
+ * Note: maximum error = NTP synch distance = dispersion + delay / 2;
+ * estimated error = NTP dispersion.
+ */
+extern long tick; /* timer interrupt period */
+extern int tickadj; /* amount of adjustment per tick */
+
+/*
+ * phase-lock loop variables
+ */
+extern int time_state; /* clock status */
+extern int time_status; /* clock synchronization status bits */
+extern long time_offset; /* time adjustment (us) */
+extern long time_constant; /* pll time constant */
+extern long time_tolerance; /* frequency tolerance (ppm) */
+extern long time_precision; /* clock precision (us) */
+extern long time_maxerror; /* maximum error */
+extern long time_esterror; /* estimated error */
+
+extern long time_phase; /* phase offset (scaled us) */
+extern long time_freq; /* frequency offset (scaled ppm) */
+extern long time_adj; /* tick adjust (scaled 1 / HZ) */
+extern long time_reftime; /* time at last adjustment (s) */
+
+extern long time_adjust; /* The amount of adjtime left */
+
+/* interface variables pps->timer interrupt */
+extern long pps_offset; /* pps time offset (us) */
+extern long pps_jitter; /* time dispersion (jitter) (us) */
+extern long pps_freq; /* frequency offset (scaled ppm) */
+extern long pps_stabil; /* frequency dispersion (scaled ppm) */
+extern long pps_valid; /* pps signal watchdog counter */
+
+/* interface variables pps->adjtimex */
+extern int pps_shift; /* interval duration (s) (shift) */
+extern long pps_jitcnt; /* jitter limit exceeded */
+extern long pps_calcnt; /* calibration intervals */
+extern long pps_errcnt; /* calibration errors */
+extern long pps_stbcnt; /* stability limit exceeded */
+
+#endif /* KERNEL */
+
+#endif /* LINUX_TIMEX_H */
diff --git a/pfinet/linux-src/include/linux/tpqic02.h b/pfinet/linux-src/include/linux/tpqic02.h
new file mode 100644
index 00000000..8817f110
--- /dev/null
+++ b/pfinet/linux-src/include/linux/tpqic02.h
@@ -0,0 +1,738 @@
+/* $Id: tpqic02.h,v 1.5 1996/12/14 23:01:38 root Exp root $
+ *
+ * Include file for QIC-02 driver for Linux.
+ *
+ * Copyright (c) 1992--1995 by H. H. Bergman. All rights reserved.
+ *
+ * ******* USER CONFIG SECTION BELOW (Near line 70) *******
+ */
+
+#ifndef _LINUX_TPQIC02_H
+#define _LINUX_TPQIC02_H
+
+#include <linux/config.h>
+
+#if CONFIG_QIC02_TAPE || CONFIG_QIC02_TAPE_MODULE
+
+/* need to have QIC02_TAPE_DRIVE and QIC02_TAPE_IFC expand to something */
+#include <linux/mtio.h>
+
+
+/* Make QIC02_TAPE_IFC expand to something.
+ *
+ * The only difference between WANGTEK and EVEREX is in the
+ * handling of the DMA channel 3.
+ * Note that the driver maps EVEREX to WANGTEK internally for speed
+ * reasons. Externally WANGTEK==1, EVEREX==2, ARCHIVE==3.
+ * These must correspond to the values used in qic02config(1).
+ *
+ * Support for Mountain controllers was added by Erik Jacobson
+ * and severely hacked by me. -- hhb
+ *
+ * Support for Emerald controllers by Alan Bain <afrb2@chiark.chu.cam.ac.uk>
+ * with more hacks by me. -- hhb
+ */
+#define WANGTEK 1 /* don't know about Wangtek QIC-36 */
+#define EVEREX (WANGTEK+1) /* I heard *some* of these are identical */
+#define EVEREX_811V EVEREX /* With TEAC MT 2ST 45D */
+#define EVEREX_831V EVEREX
+#define ARCHIVE 3
+#define ARCHIVE_SC400 ARCHIVE /* rumoured to be from the pre-SMD-age */
+#define ARCHIVE_SC402 ARCHIVE /* don't know much about SC400 */
+#define ARCHIVE_SC499 ARCHIVE /* SC402 and SC499R should be identical */
+
+#define MOUNTAIN 5 /* Mountain Computer Interface */
+#define EMERALD 6 /* Emerald Interface card */
+
+
+
+#define QIC02_TAPE_PORT_RANGE 8 /* number of IO locations to reserve */
+
+
+/*********** START OF USER CONFIGURABLE SECTION ************/
+
+/* Tape configuration: Select DRIVE, IFC, PORT, IRQ and DMA below.
+ * Runtime (re)configuration is not supported yet.
+ *
+ * Tape drive configuration: (MT_IS* constants are defined in mtio.h)
+ *
+ * QIC02_TAPE_DRIVE = MT_ISWT5150
+ * - Wangtek 5150, format: up to QIC-150.
+ * QIC02_TAPE_DRIVE = MT_ISQIC02_ALL_FEATURES
+ * - Enables some optional QIC02 commands that some drives may lack.
+ * It is provided so you can check which are supported by your drive.
+ * Refer to tpqic02.h for others.
+ *
+ * Supported interface cards: QIC02_TAPE_IFC =
+ * WANGTEK,
+ * ARCHIVE_SC402, ARCHIVE_SC499. (both same programming interface)
+ *
+ * Make sure you have the I/O ports/DMA channels
+ * and IRQ stuff configured properly!
+ * NOTE: There may be other device drivers using the same major
+ * number. This must be avoided. Check for timer.h conflicts too.
+ *
+ * If you have an EVEREX EV-831 card and you are using DMA channel 3,
+ * you will probably have to ``#define QIC02_TAPE_DMA3_FIX'' below.
+ */
+
+/* CONFIG_QIC02_DYNCONF can be defined in autoconf.h, by `make config' */
+
+/*** #undef CONFIG_QIC02_DYNCONF ***/
+
+#ifndef CONFIG_QIC02_DYNCONF
+
+#define QIC02_TAPE_DRIVE MT_ISQIC02_ALL_FEATURES /* drive type */
+/* #define QIC02_TAPE_DRIVE MT_ISWT5150 */
+/* #define QIC02_TAPE_DRIVE MT_ISARCHIVE_5945L2 */
+/* #define QIC02_TAPE_DRIVE MT_ISTEAC_MT2ST */
+/* #define QIC02_TAPE_DRIVE MT_ISARCHIVE_2150L */
+/* #define QIC02_TAPE_DRIVE MT_ISARCHIVESC499 */
+
+/* Either WANGTEK, ARCHIVE or MOUNTAIN. Not EVEREX.
+ * If you have an EVEREX, use WANGTEK and try the DMA3_FIX below.
+ */
+#define QIC02_TAPE_IFC WANGTEK /* interface card type */
+/* #define QIC02_TAPE_IFC ARCHIVE */
+/* #define QIC02_TAPE_IFC MOUNTAIN */
+
+#define QIC02_TAPE_PORT 0x300 /* controller port address */
+#define QIC02_TAPE_IRQ 5 /* For IRQ2, use 9 here, others normal. */
+#define QIC02_TAPE_DMA 1 /* either 1 or 3, because 2 is used by the floppy */
+
+/* If DMA3 doesn't work, but DMA1 does, and you have a
+ * Wangtek/Everex card, you can try #define-ing the flag
+ * below. Note that you should also change the DACK jumper
+ * for Wangtek/Everex cards when changing the DMA channel.
+ */
+#undef QIC02_TAPE_DMA3_FIX
+
+/************ END OF USER CONFIGURABLE SECTION *************/
+
+/* I put the stuff above in config.in, but a few recompiles, to
+ * verify different configurations, and several days later I decided
+ * to change it back again.
+ */
+
+
+
+/* NOTE: TP_HAVE_DENS should distinguish between available densities (?)
+ * NOTE: Drive select is not implemented -- I have only one tape streamer,
+ * so I'm unable and unmotivated to test and implement that. ;-) ;-)
+ */
+#if QIC02_TAPE_DRIVE == MT_ISWT5150
+#define TP_HAVE_DENS 1
+#define TP_HAVE_BSF 0 /* nope */
+#define TP_HAVE_FSR 0 /* nope */
+#define TP_HAVE_BSR 0 /* nope */
+#define TP_HAVE_EOD 0 /* most of the time */
+#define TP_HAVE_SEEK 0
+#define TP_HAVE_TELL 0
+#define TP_HAVE_RAS1 1
+#define TP_HAVE_RAS2 1
+
+#elif QIC02_TAPE_DRIVE == MT_ISARCHIVESC499 /* Archive SC-499 QIC-36 controller */
+#define TP_HAVE_DENS 1 /* can do set density (QIC-11 / QIC-24) */
+#define TP_HAVE_BSF 0
+#define TP_HAVE_FSR 1 /* can skip one block forwards */
+#define TP_HAVE_BSR 1 /* can skip one block backwards */
+#define TP_HAVE_EOD 1 /* can seek to end of recorded data */
+#define TP_HAVE_SEEK 0
+#define TP_HAVE_TELL 0
+#define TP_HAVE_RAS1 1 /* can run selftest 1 */
+#define TP_HAVE_RAS2 1 /* can run selftest 2 */
+/* These last two selftests shouldn't be used yet! */
+
+#elif (QIC02_TAPE_DRIVE == MT_ISARCHIVE_2060L) || (QIC02_TAPE_DRIVE == MT_ISARCHIVE_2150L)
+#define TP_HAVE_DENS 1 /* can do set density (QIC-24 / QIC-120 / QIC-150) */
+#define TP_HAVE_BSF 0
+#define TP_HAVE_FSR 1 /* can skip one block forwards */
+#define TP_HAVE_BSR 1 /* can skip one block backwards */
+#define TP_HAVE_EOD 1 /* can seek to end of recorded data */
+#define TP_HAVE_TELL 1 /* can read current block address */
+#define TP_HAVE_SEEK 1 /* can seek to block */
+#define TP_HAVE_RAS1 1 /* can run selftest 1 */
+#define TP_HAVE_RAS2 1 /* can run selftest 2 */
+/* These last two selftests shouldn't be used yet! */
+
+#elif QIC02_TAPE_DRIVE == MT_ISARCHIVE_5945L2
+/* can anyone verify this entry?? */
+#define TP_HAVE_DENS 1 /* can do set density?? (QIC-24??) */
+#define TP_HAVE_BSF 0
+#define TP_HAVE_FSR 1 /* can skip one block forwards */
+#define TP_HAVE_BSR 1 /* can skip one block backwards */
+#define TP_HAVE_EOD 1 /* can seek to end of recorded data */
+#define TP_HAVE_TELL 1 /* can read current block address */
+#define TP_HAVE_SEEK 1 /* can seek to block */
+#define TP_HAVE_RAS1 1 /* can run selftest 1 */
+#define TP_HAVE_RAS2 1 /* can run selftest 2 */
+/* These last two selftests shouldn't be used yet! */
+
+#elif QIC02_TAPE_DRIVE == MT_ISTEAC_MT2ST
+/* can anyone verify this entry?? */
+#define TP_HAVE_DENS 0 /* cannot do set density?? (QIC-150?) */
+#define TP_HAVE_BSF 0
+#define TP_HAVE_FSR 1 /* can skip one block forwards */
+#define TP_HAVE_BSR 1 /* can skip one block backwards */
+#define TP_HAVE_EOD 1 /* can seek to end of recorded data */
+#define TP_HAVE_SEEK 1 /* can seek to block */
+#define TP_HAVE_TELL 1 /* can read current block address */
+#define TP_HAVE_RAS1 1 /* can run selftest 1 */
+#define TP_HAVE_RAS2 1 /* can run selftest 2 */
+/* These last two selftests shouldn't be used yet! */
+
+#elif QIC02_TAPE_DRIVE == MT_ISQIC02_ALL_FEATURES
+#define TP_HAVE_DENS 1 /* can do set density */
+#define TP_HAVE_BSF 1 /* can search filemark backwards */
+#define TP_HAVE_FSR 1 /* can skip one block forwards */
+#define TP_HAVE_BSR 1 /* can skip one block backwards */
+#define TP_HAVE_EOD 1 /* can seek to end of recorded data */
+#define TP_HAVE_SEEK 1 /* seek to block address */
+#define TP_HAVE_TELL 1 /* tell current block address */
+#define TP_HAVE_RAS1 1 /* can run selftest 1 */
+#define TP_HAVE_RAS2 1 /* can run selftest 2 */
+/* These last two selftests shouldn't be used yet! */
+
+
+#else
+#error No QIC-02 tape drive type defined!
+/* If your drive is not listed above, first try the 'ALL_FEATURES',
+ * to see what commands are supported, then create your own entry in
+ * the list above. You may want to mail it to me, so that I can include
+ * it in the next release.
+ */
+#endif
+
+#endif /* !CONFIG_QIC02_DYNCONF */
+
+
+/* WANGTEK interface card specifics */
+#define WT_QIC02_STAT_PORT (QIC02_TAPE_PORT)
+#define WT_QIC02_CTL_PORT (QIC02_TAPE_PORT)
+#define WT_QIC02_CMD_PORT (QIC02_TAPE_PORT+1)
+#define WT_QIC02_DATA_PORT (QIC02_TAPE_PORT+1)
+
+/* status register bits (Active LOW!) */
+#define WT_QIC02_STAT_POLARITY 0
+#define WT_QIC02_STAT_READY 0x01
+#define WT_QIC02_STAT_EXCEPTION 0x02
+#define WT_QIC02_STAT_MASK (WT_QIC02_STAT_READY|WT_QIC02_STAT_EXCEPTION)
+
+#define WT_QIC02_STAT_RESETMASK 0x07
+#define WT_QIC02_STAT_RESETVAL (WT_QIC02_STAT_RESETMASK & ~WT_QIC02_STAT_EXCEPTION)
+
+/* controller register (QIC02_CTL_PORT) bits */
+#define WT_QIC02_CTL_RESET 0x02
+#define WT_QIC02_CTL_REQUEST 0x04
+#define WT_CTL_ONLINE 0x01
+#define WT_CTL_CMDOFF 0xC0
+
+#define WT_CTL_DMA3 0x10 /* enable dma chan3 */
+#define WT_CTL_DMA1 0x08 /* enable dma chan1 or chan2 */
+
+/* EMERALD interface card specifics
+ * Much like Wangtek, only different polarity and bit locations
+ */
+#define EMR_QIC02_STAT_PORT (QIC02_TAPE_PORT)
+#define EMR_QIC02_CTL_PORT (QIC02_TAPE_PORT)
+#define EMR_QIC02_CMD_PORT (QIC02_TAPE_PORT+1)
+#define EMR_QIC02_DATA_PORT (QIC02_TAPE_PORT+1)
+
+/* status register bits (Active High!) */
+#define EMR_QIC02_STAT_POLARITY 1
+#define EMR_QIC02_STAT_READY 0x01
+#define EMR_QIC02_STAT_EXCEPTION 0x02
+#define EMR_QIC02_STAT_MASK (EMR_QIC02_STAT_READY|EMR_QIC02_STAT_EXCEPTION)
+
+#define EMR_QIC02_STAT_RESETMASK 0x07
+#define EMR_QIC02_STAT_RESETVAL (EMR_QIC02_STAT_RESETMASK & ~EMR_QIC02_STAT_EXCEPTION)
+
+/* controller register (QIC02_CTL_PORT) bits */
+#define EMR_QIC02_CTL_RESET 0x02
+#define EMR_QIC02_CTL_REQUEST 0x04
+#define EMR_CTL_ONLINE 0x01
+#define EMR_CTL_CMDOFF 0xC0
+
+#define EMR_CTL_DMA3 0x10 /* enable dma chan3 */
+#define EMR_CTL_DMA1 0x08 /* enable dma chan1 or chan2 */
+
+
+
+/* ARCHIVE interface card specifics */
+#define AR_QIC02_STAT_PORT (QIC02_TAPE_PORT+1)
+#define AR_QIC02_CTL_PORT (QIC02_TAPE_PORT+1)
+#define AR_QIC02_CMD_PORT (QIC02_TAPE_PORT)
+#define AR_QIC02_DATA_PORT (QIC02_TAPE_PORT)
+
+#define AR_START_DMA_PORT (QIC02_TAPE_PORT+2)
+#define AR_RESET_DMA_PORT (QIC02_TAPE_PORT+3)
+
+/* STAT port bits */
+#define AR_QIC02_STAT_POLARITY 0
+#define AR_STAT_IRQF 0x80 /* active high, interrupt request flag */
+#define AR_QIC02_STAT_READY 0x40 /* active low */
+#define AR_QIC02_STAT_EXCEPTION 0x20 /* active low */
+#define AR_QIC02_STAT_MASK (AR_QIC02_STAT_READY|AR_QIC02_STAT_EXCEPTION)
+#define AR_STAT_DMADONE 0x10 /* active high, DMA done */
+#define AR_STAT_DIRC 0x08 /* active high, direction */
+
+#define AR_QIC02_STAT_RESETMASK 0x70 /* check RDY,EXC,DMADONE */
+#define AR_QIC02_STAT_RESETVAL ((AR_QIC02_STAT_RESETMASK & ~AR_STAT_IRQF & ~AR_QIC02_STAT_EXCEPTION) | AR_STAT_DMADONE)
+
+/* CTL port bits */
+#define AR_QIC02_CTL_RESET 0x80 /* drive reset */
+#define AR_QIC02_CTL_REQUEST 0x40 /* notify of new command */
+#define AR_CTL_IEN 0x20 /* interrupt enable */
+#define AR_CTL_DNIEN 0x10 /* done-interrupt enable */
+ /* Note: All of these bits are cleared automatically when writing to
+ * AR_RESET_DMA_PORT. So AR_CTL_IEN and AR_CTL_DNIEN must be
+ * reprogrammed before the write to AR_START_DMA_PORT.
+ */
+
+
+/* MOUNTAIN interface specifics */
+#define MTN_QIC02_STAT_PORT (QIC02_TAPE_PORT+1)
+#define MTN_QIC02_CTL_PORT (QIC02_TAPE_PORT+1)
+#define MTN_QIC02_CMD_PORT (QIC02_TAPE_PORT)
+#define MTN_QIC02_DATA_PORT (QIC02_TAPE_PORT)
+
+#define MTN_W_SELECT_DMA_PORT (QIC02_TAPE_PORT+2)
+#define MTN_R_DESELECT_DMA_PORT (QIC02_TAPE_PORT+2)
+#define MTN_W_DMA_WRITE_PORT (QIC02_TAPE_PORT+3)
+
+/* STAT port bits */
+#define MTN_QIC02_STAT_POLARITY 0
+#define MTN_QIC02_STAT_READY 0x02 /* active low */
+#define MTN_QIC02_STAT_EXCEPTION 0x04 /* active low */
+#define MTN_QIC02_STAT_MASK (MTN_QIC02_STAT_READY|MTN_QIC02_STAT_EXCEPTION)
+#define MTN_STAT_DMADONE 0x01 /* active high, DMA done */
+
+#define MTN_QIC02_STAT_RESETMASK 0x07 /* check RDY,EXC,DMADONE */
+#define MTN_QIC02_STAT_RESETVAL ((MTN_QIC02_STAT_RESETMASK & ~MTN_QIC02_STAT_EXCEPTION) | MTN_STAT_DMADONE)
+
+/* CTL port bits */
+#define MTN_QIC02_CTL_RESET_NOT 0x80 /* drive reset, active low */
+#define MTN_QIC02_CTL_RESET 0x80 /* Fodder #definition to keep gcc happy */
+
+#define MTN_QIC02_CTL_ONLINE 0x40 /* Put drive on line */
+#define MTN_QIC02_CTL_REQUEST 0x20 /* notify of new command */
+#define MTN_QIC02_CTL_IRQ_DRIVER 0x10 /* Enable IRQ tristate driver */
+#define MTN_QIC02_CTL_DMA_DRIVER 0x08 /* Enable DMA tristate driver */
+#define MTN_CTL_EXC_IEN 0x04 /* Exception interrupt enable */
+#define MTN_CTL_RDY_IEN 0x02 /* Ready interrupt enable */
+#define MTN_CTL_DNIEN 0x01 /* done-interrupt enable */
+
+#define MTN_CTL_ONLINE (MTN_QIC02_CTL_RESET_NOT | MTN_QIC02_CTL_IRQ_DRIVER | MTN_QIC02_CTL_DMA_DRIVER)
+
+
+#ifndef CONFIG_QIC02_DYNCONF
+
+# define QIC02_TAPE_DEBUG (qic02_tape_debug)
+
+# if QIC02_TAPE_IFC == WANGTEK
+# define QIC02_STAT_POLARITY WT_QIC02_STAT_POLARITY
+# define QIC02_STAT_PORT WT_QIC02_STAT_PORT
+# define QIC02_CTL_PORT WT_QIC02_CTL_PORT
+# define QIC02_CMD_PORT WT_QIC02_CMD_PORT
+# define QIC02_DATA_PORT WT_QIC02_DATA_PORT
+
+# define QIC02_STAT_READY WT_QIC02_STAT_READY
+# define QIC02_STAT_EXCEPTION WT_QIC02_STAT_EXCEPTION
+# define QIC02_STAT_MASK WT_QIC02_STAT_MASK
+# define QIC02_STAT_RESETMASK WT_QIC02_STAT_RESETMASK
+# define QIC02_STAT_RESETVAL WT_QIC02_STAT_RESETVAL
+
+# define QIC02_CTL_RESET WT_QIC02_CTL_RESET
+# define QIC02_CTL_REQUEST WT_QIC02_CTL_REQUEST
+
+# if QIC02_TAPE_DMA == 3
+# ifdef QIC02_TAPE_DMA3_FIX
+# define WT_CTL_DMA WT_CTL_DMA1
+# else
+# define WT_CTL_DMA WT_CTL_DMA3
+# endif
+# elif QIC02_TAPE_DMA == 1
+# define WT_CTL_DMA WT_CTL_DMA1
+# else
+# error Unsupported or incorrect DMA configuration.
+# endif
+
+# elif QIC02_TAPE_IFC == EMERALD
+# define QIC02_STAT_POLARITY EMR_QIC02_STAT_POLARITY
+# define QIC02_STAT_PORT EMR_QIC02_STAT_PORT
+# define QIC02_CTL_PORT EMR_QIC02_CTL_PORT
+# define QIC02_CMD_PORT EMR_QIC02_CMD_PORT
+# define QIC02_DATA_PORT EMR_QIC02_DATA_PORT
+
+# define QIC02_STAT_READY EMR_QIC02_STAT_READY
+# define QIC02_STAT_EXCEPTION EMR_QIC02_STAT_EXCEPTION
+# define QIC02_STAT_MASK EMR_QIC02_STAT_MASK
+# define QIC02_STAT_RESETMASK EMR_QIC02_STAT_RESETMASK
+# define QIC02_STAT_RESETVAL EMR_QIC02_STAT_RESETVAL
+
+# define QIC02_CTL_RESET EMR_QIC02_CTL_RESET
+# define QIC02_CTL_REQUEST EMR_QIC02_CTL_REQUEST
+
+# if QIC02_TAPE_DMA == 3
+# ifdef QIC02_TAPE_DMA3_FIX
+# define EMR_CTL_DMA EMR_CTL_DMA1
+# else
+# define EMR_CTL_DMA EMR_CTL_DMA3
+# endif
+# elif QIC02_TAPE_DMA == 1
+# define EMR_CTL_DMA EMR_CTL_DMA1
+# else
+# error Unsupported or incorrect DMA configuration.
+# endif
+
+# elif QIC02_TAPE_IFC == ARCHIVE
+# define QIC02_STAT_POLARITY AR_QIC02_STAT_POLARITY
+# define QIC02_STAT_PORT AR_QIC02_STAT_PORT
+# define QIC02_CTL_PORT AR_QIC02_CTL_PORT
+# define QIC02_CMD_PORT AR_QIC02_CMD_PORT
+# define QIC02_DATA_PORT AR_QIC02_DATA_PORT
+
+# define QIC02_STAT_READY AR_QIC02_STAT_READY
+# define QIC02_STAT_EXCEPTION AR_QIC02_STAT_EXCEPTION
+# define QIC02_STAT_MASK AR_QIC02_STAT_MASK
+# define QIC02_STAT_RESETMASK AR_QIC02_STAT_RESETMASK
+# define QIC02_STAT_RESETVAL AR_QIC02_STAT_RESETVAL
+
+# define QIC02_CTL_RESET AR_QIC02_CTL_RESET
+# define QIC02_CTL_REQUEST AR_QIC02_CTL_REQUEST
+
+# if QIC02_TAPE_DMA > 3 /* channel 2 is used by the floppy driver */
+# error DMA channels other than 1 and 3 are not supported.
+# endif
+
+# elif QIC02_TAPE_IFC == MOUNTAIN
+# define QIC02_STAT_POLARITY MTN_QIC02_STAT_POLARITY
+# define QIC02_STAT_PORT MTN_QIC02_STAT_PORT
+# define QIC02_CTL_PORT MTN_QIC02_CTL_PORT
+# define QIC02_CMD_PORT MTN_QIC02_CMD_PORT
+# define QIC02_DATA_PORT MTN_QIC02_DATA_PORT
+
+# define QIC02_STAT_READY MTN_QIC02_STAT_READY
+# define QIC02_STAT_EXCEPTION MTN_QIC02_STAT_EXCEPTION
+# define QIC02_STAT_MASK MTN_QIC02_STAT_MASK
+# define QIC02_STAT_RESETMASK MTN_QIC02_STAT_RESETMASK
+# define QIC02_STAT_RESETVAL MTN_QIC02_STAT_RESETVAL
+
+# define QIC02_CTL_RESET MTN_QIC02_CTL_RESET
+# define QIC02_CTL_REQUEST MTN_QIC02_CTL_REQUEST
+
+# if QIC02_TAPE_DMA > 3 /* channel 2 is used by the floppy driver */
+# error DMA channels other than 1 and 3 are not supported.
+# endif
+
+# else
+# error No valid interface card specified!
+# endif /* QIC02_TAPE_IFC */
+
+
+ /* An ugly hack to make sure WT_CTL_DMA is defined even for the
+ * static, non-Wangtek case. The alternative was even worse.
+ */
+# ifndef WT_CTL_DMA
+# define WT_CTL_DMA WT_CTL_DMA1
+# endif
+
+/*******************/
+
+#else /* !CONFIG_QIC02_DYNCONF */
+
+/* Now the runtime config version, using variables instead of constants.
+ *
+ * qic02_tape_dynconf is R/O for the kernel, set from userspace.
+ * qic02_tape_ccb is private to the driver, R/W.
+ */
+
+# define QIC02_TAPE_DRIVE (qic02_tape_dynconf.mt_type)
+# define QIC02_TAPE_IFC (qic02_tape_ccb.ifc_type)
+# define QIC02_TAPE_IRQ (qic02_tape_dynconf.irqnr)
+# define QIC02_TAPE_DMA (qic02_tape_dynconf.dmanr)
+# define QIC02_TAPE_PORT (qic02_tape_dynconf.port)
+# define WT_CTL_DMA (qic02_tape_ccb.dma_enable_value)
+# define QIC02_TAPE_DEBUG (qic02_tape_dynconf.debug)
+
+# define QIC02_STAT_PORT (qic02_tape_ccb.port_stat)
+# define QIC02_CTL_PORT (qic02_tape_ccb.port_ctl)
+# define QIC02_CMD_PORT (qic02_tape_ccb.port_cmd)
+# define QIC02_DATA_PORT (qic02_tape_ccb.port_data)
+
+# define QIC02_STAT_POLARITY (qic02_tape_ccb.stat_polarity)
+# define QIC02_STAT_READY (qic02_tape_ccb.stat_ready)
+# define QIC02_STAT_EXCEPTION (qic02_tape_ccb.stat_exception)
+# define QIC02_STAT_MASK (qic02_tape_ccb.stat_mask)
+
+# define QIC02_STAT_RESETMASK (qic02_tape_ccb.stat_resetmask)
+# define QIC02_STAT_RESETVAL (qic02_tape_ccb.stat_resetval)
+
+# define QIC02_CTL_RESET (qic02_tape_ccb.ctl_reset)
+# define QIC02_CTL_REQUEST (qic02_tape_ccb.ctl_request)
+
+# define TP_HAVE_DENS (qic02_tape_dynconf.have_dens)
+# define TP_HAVE_BSF (qic02_tape_dynconf.have_bsf)
+# define TP_HAVE_FSR (qic02_tape_dynconf.have_fsr)
+# define TP_HAVE_BSR (qic02_tape_dynconf.have_bsr)
+# define TP_HAVE_EOD (qic02_tape_dynconf.have_eod)
+# define TP_HAVE_SEEK (qic02_tape_dynconf.have_seek)
+# define TP_HAVE_TELL (qic02_tape_dynconf.have_tell)
+# define TP_HAVE_RAS1 (qic02_tape_dynconf.have_ras1)
+# define TP_HAVE_RAS2 (qic02_tape_dynconf.have_ras2)
+
+#endif /* CONFIG_QIC02_DYNCONF */
+
+
+/* "Vendor Unique" codes */
+/* Archive seek & tell stuff */
+#define AR_QCMDV_TELL_BLK 0xAE /* read current block address */
+#define AR_QCMDV_SEEK_BLK 0xAD /* seek to specific block */
+#define AR_SEEK_BUF_SIZE 3 /* address is 3 bytes */
+
+
+
+/*
+ * Misc common stuff
+ */
+
+/* Standard QIC-02 commands -- rev F. All QIC-02 drives must support these */
+#define QCMD_SEL_1 0x01 /* select drive 1 */
+#define QCMD_SEL_2 0x02 /* select drive 2 */
+#define QCMD_SEL_3 0x04 /* select drive 3 */
+#define QCMD_SEL_4 0x08 /* select drive 4 */
+#define QCMD_REWIND 0x21 /* rewind tape */
+#define QCMD_ERASE 0x22 /* erase tape */
+#define QCMD_RETEN 0x24 /* retension tape */
+#define QCMD_WRT_DATA 0x40 /* write data */
+#define QCMD_WRT_FM 0x60 /* write file mark */
+#define QCMD_RD_DATA 0x80 /* read data */
+#define QCMD_RD_FM 0xA0 /* read file mark (forward direction) */
+#define QCMD_RD_STAT 0xC0 /* read status */
+
+/* Other (optional/vendor unique) commands */
+ /* Density commands are only valid when TP_BOM is set! */
+#define QCMD_DENS_11 0x26 /* QIC-11 */
+#define QCMD_DENS_24 0x27 /* QIC-24: 9 track 60MB */
+#define QCMD_DENS_120 0x28 /* QIC-120: 15 track 120MB */
+#define QCMD_DENS_150 0x29 /* QIC-150: 18 track 150MB */
+#define QCMD_DENS_300 0x2A /* QIC-300/QIC-2100 */
+#define QCMD_DENS_600 0x2B /* QIC-600/QIC-2200 */
+/* don't know about QIC-1000 and QIC-1350 */
+
+#define QCMD_WRTNU_DATA 0x40 /* write data, no underruns, insert filler. */
+#define QCMD_SPACE_FWD 0x81 /* skip next block */
+#define QCMD_SPACE_BCK 0x89 /* move tape head one block back -- very useful! */
+#define QCMD_RD_FM_BCK 0xA8 /* read filemark (backwards) */
+#define QCMD_SEEK_EOD 0xA3 /* skip to EOD */
+#define QCMD_RD_STAT_X1 0xC1 /* read extended status 1 */
+#define QCMD_RD_STAT_X2 0xC4 /* read extended status 2 */
+#define QCMD_RD_STAT_X3 0xE0 /* read extended status 3 */
+#define QCMD_SELF_TST1 0xC2 /* run self test 1 (nondestructive) */
+#define QCMD_SELF_TST2 0xCA /* run self test 2 (destructive) */
+
+
+
+/* Optional, QFA (Quick File Access) commands.
+ * Not all drives support this, but those that do could use these commands
+ * to implement semi-non-sequential access. `mt fsf` would benefit from this.
+ * QFA divides the tape into 2 partitions, a data and a directory partition,
+ * causing some incompatibility problems wrt std QIC-02 data exchange.
+ * It would be useful to cache the directory info, but that might be tricky
+ * to do in kernel-space. [Size constraints.]
+ * Refer to the QIC-02 specs, appendix A for more information.
+ * I have no idea how other *nix variants implement QFA.
+ * I have no idea which drives support QFA and which don't.
+ */
+#define QFA_ENABLE 0x2D /* enter QFA mode, give @ BOT only */
+#define QFA_DATA 0x20 /* select data partition */
+#define QFA_DIR 0x23 /* select directory partition */
+#define QFA_RD_POS 0xCF /* read position+status bytes */
+#define QFA_SEEK_EOD 0xA1 /* seek EOD within current partition */
+#define QFA_SEEK_BLK 0xAF /* seek to a block within current partition */
+
+
+
+
+/*
+ * Debugging flags
+ */
+#define TPQD_SENSE_TEXT 0x0001
+#define TPQD_SENSE_CNTS 0x0002
+#define TPQD_REWIND 0x0004
+#define TPQD_TERM_CYCLE 0x0008
+#define TPQD_IOCTLS 0x0010
+#define TPQD_DMAX 0x0020
+#define TPQD_BLKSZ 0x0040
+#define TPQD_MISC 0x0080
+
+#define TPQD_DEBUG 0x0100
+
+#define TPQD_DIAGS 0x1000
+
+#define TPQD_ALWAYS 0x8000
+
+#define TPQD_DEFAULT_FLAGS 0x00fc
+
+
+#define TPQDBG(f) ((QIC02_TAPE_DEBUG) & (TPQD_##f))
+
+
+/* Minor device codes for tapes:
+ * |7|6|5|4|3|2|1|0|
+ * | \ | / \ | / |_____ 1=rewind on close, 0=no rewind on close
+ * | \|/ |_________ Density: 000=none, 001=QIC-11, 010=24, 011=120,
+ * | | 100=QIC-150, 101..111 reserved.
+ * | |_______________ Reserved for unit numbers.
+ * |___________________ Reserved for diagnostics during debugging.
+ */
+
+#define TP_REWCLOSE(d) ((MINOR(d)&0x01) == 1) /* rewind bit */
+ /* rewind is only done if data has been transferred */
+#define TP_DENS(dev) ((MINOR(dev) >> 1) & 0x07) /* tape density */
+#define TP_UNIT(dev) ((MINOR(dev) >> 4) & 0x07) /* unit number */
+
+/* print excessive diagnostics */
+#define TP_DIAGS(dev) (QIC02_TAPE_DEBUG & TPQD_DIAGS)
+
+/* status codes returned by a WTS_RDSTAT call */
+struct tpstatus { /* sizeof(short)==2), LSB first */
+ unsigned short exs; /* Drive exception flags */
+ unsigned short dec; /* data error count: nr of blocks rewritten/soft read errors */
+ unsigned short urc; /* underrun count: nr of times streaming was interrupted */
+};
+#define TPSTATSIZE sizeof(struct tpstatus)
+
+
+/* defines for tpstatus.exs -- taken from 386BSD wt driver */
+#define TP_POR 0x100 /* Power on or reset occurred */
+#define TP_EOR 0x200 /* REServed for end of RECORDED media */
+#define TP_PAR 0x400 /* REServed for bus parity */
+#define TP_BOM 0x800 /* Beginning of media */
+#define TP_MBD 0x1000 /* Marginal block detected */
+#define TP_NDT 0x2000 /* No data detected */
+#define TP_ILL 0x4000 /* Illegal command */
+#define TP_ST1 0x8000 /* Status byte 1 flag */
+#define TP_FIL 0x01 /* File mark detected */
+#define TP_BNL 0x02 /* Bad block not located */
+#define TP_UDA 0x04 /* Unrecoverable data error */
+#define TP_EOM 0x08 /* End of media */
+#define TP_WRP 0x10 /* Write protected cartridge */
+#define TP_USL 0x20 /* Unselected drive */
+#define TP_CNI 0x40 /* Cartridge not in place */
+#define TP_ST0 0x80 /* Status byte 0 flag */
+
+#define REPORT_ERR0 (TP_CNI|TP_USL|TP_WRP|TP_EOM|TP_UDA|TP_BNL|TP_FIL)
+#define REPORT_ERR1 (TP_ILL|TP_NDT|TP_MBD|TP_PAR)
+
+
+/* exception numbers */
+#define EXC_UNKNOWN 0 /* (extra) Unknown exception code */
+#define EXC_NDRV 1 /* No drive */
+#define EXC_NCART 2 /* No cartridge */
+#define EXC_WP 3 /* Write protected */
+#define EXC_EOM 4 /* EOM */
+#define EXC_RWA 5 /* read/write abort */
+#define EXC_XBAD 6 /* read error, bad block transferred */
+#define EXC_XFILLER 7 /* read error, filler block transferred */
+#define EXC_NDT 8 /* read error, no data */
+#define EXC_NDTEOM 9 /* read error, no data & EOM */
+#define EXC_NDTBOM 10 /* read error, no data & BOM */
+#define EXC_FM 11 /* Read a filemark */
+#define EXC_ILL 12 /* Illegal command */
+#define EXC_POR 13 /* Power on/reset */
+#define EXC_MARGINAL 14 /* Marginal block detected */
+#define EXC_EOR 15 /* (extra, for SEEKEOD) End Of Recorded data reached */
+#define EXC_BOM 16 /* (extra) BOM reached */
+
+
+#define TAPE_NOTIFY_TIMEOUT 1000000
+
+/* internal function return codes */
+#define TE_OK 0 /* everything is fine */
+#define TE_EX 1 /* exception detected */
+#define TE_ERR 2 /* some error */
+#define TE_NS 3 /* can't read status */
+#define TE_TIM 4 /* timed out */
+#define TE_DEAD 5 /* tape drive doesn't respond */
+#define TE_END 6 /******** Archive hack *****/
+
+/* timeout timer values -- check these! */
+#define TIM_S (4*HZ) /* 4 seconds (normal cmds) */
+#define TIM_M (30*HZ) /* 30 seconds (write FM) */
+#define TIM_R (8*60*HZ) /* 8 minutes (retensioning) */
+#define TIM_F (2*3600*HZ) /* est. 1.2hr for full tape read/write+2 retens */
+
+#define TIMERON(t) timer_table[QIC02_TAPE_TIMER].expires = jiffies + (t); \
+ timer_active |= (1<<QIC02_TAPE_TIMER)
+#define TIMEROFF timer_active &= ~(1<<QIC02_TAPE_TIMER)
+#define TIMERCONT timer_active |= (1<<QIC02_TAPE_TIMER)
+
+
+typedef char flag;
+#define NO 0 /* NO must be 0 */
+#define YES 1 /* YES must be != 0 */
+
+
+#ifdef TDEBUG
+# define TPQDEB(s) s
+# define TPQPUTS(s) tpqputs(s)
+#else
+# define TPQDEB(s)
+# define TPQPUTS(s)
+#endif
+
+
+/* NR_BLK_BUF is a `tuneable parameter'. If you're really low on
+ * kernel space, you could decrease it to 1, or if you got a very
+ * slow machine, you could increase it up to 127 blocks. Less kernel
+ * buffer blocks result in more context-switching.
+ */
+#define NR_BLK_BUF 20 /* max 127 blocks */
+#define TAPE_BLKSIZE 512 /* streamer tape block size (fixed) */
+#define TPQBUF_SIZE (TAPE_BLKSIZE*NR_BLK_BUF) /* buffer size */
+
+
+#define BLOCKS_BEYOND_EW 2 /* nr of blocks after Early Warning hole */
+#define BOGUS_IRQ 32009
+
+
+/* This is internal data, filled in based on the ifc_type field given
+ * by the user. Everex is mapped to Wangtek with a different
+ * `dma_enable_value', if dmanr==3.
+ */
+struct qic02_ccb {
+ long ifc_type;
+
+ unsigned short port_stat; /* Status port address */
+ unsigned short port_ctl; /* Control port address */
+ unsigned short port_cmd; /* Command port address */
+ unsigned short port_data; /* Data port address */
+
+ /* status register bits */
+ unsigned short stat_polarity; /* invert status bits or not */
+ unsigned short stat_ready; /* drive ready */
+ unsigned short stat_exception; /* drive signals exception */
+ unsigned short stat_mask;
+ unsigned short stat_resetmask;
+ unsigned short stat_resetval;
+
+ /* control register bits */
+ unsigned short ctl_reset; /* reset drive */
+ unsigned short ctl_request; /* latch command */
+
+ /* This is used to change the DMA3 behaviour */
+ unsigned short dma_enable_value;
+};
+
+#if MODULE
+static int qic02_tape_init(void);
+#else
+extern int qic02_tape_init(void); /* for mem.c */
+#endif
+
+
+
+#endif /* CONFIG_QIC02_TAPE */
+
+#endif /* _LINUX_TPQIC02_H */
diff --git a/pfinet/linux-src/include/linux/tqueue.h b/pfinet/linux-src/include/linux/tqueue.h
new file mode 100644
index 00000000..d886f753
--- /dev/null
+++ b/pfinet/linux-src/include/linux/tqueue.h
@@ -0,0 +1,124 @@
+/*
+ * tqueue.h --- task queue handling for Linux.
+ *
+ * Mostly based on a proposed bottom-half replacement code written by
+ * Kai Petzke, wpp@marie.physik.tu-berlin.de.
+ *
+ * Modified for use in the Linux kernel by Theodore Ts'o,
+ * tytso@mit.edu. Any bugs are my fault, not Kai's.
+ *
+ * The original comment follows below.
+ */
+
+#ifndef _LINUX_TQUEUE_H
+#define _LINUX_TQUEUE_H
+
+#include <asm/bitops.h>
+#include <asm/system.h>
+#include <asm/spinlock.h>
+
+/*
+ * New proposed "bottom half" handlers:
+ * (C) 1994 Kai Petzke, wpp@marie.physik.tu-berlin.de
+ *
+ * Advantages:
+ * - Bottom halfs are implemented as a linked list. You can have as many
+ * of them, as you want.
+ * - No more scanning of a bit field is required upon call of a bottom half.
+ * - Support for chained bottom half lists. The run_task_queue() function can be
+ * used as a bottom half handler. This is for example useful for bottom
+ * halfs, which want to be delayed until the next clock tick.
+ *
+ * Problems:
+ * - The queue_task_irq() inline function is only atomic with respect to itself.
+ * Problems can occur, when queue_task_irq() is called from a normal system
+ * call, and an interrupt comes in. No problems occur, when queue_task_irq()
+ * is called from an interrupt or bottom half, and interrupted, as run_task_queue()
+ * will not be executed/continued before the last interrupt returns. If in
+ * doubt, use queue_task(), not queue_task_irq().
+ * - Bottom halfs are called in the reverse order that they were linked into
+ * the list.
+ */
+
+struct tq_struct {
+ struct tq_struct *next; /* linked list of active bh's */
+ unsigned long sync; /* must be initialized to zero */
+ void (*routine)(void *); /* function to call */
+ void *data; /* argument to function */
+};
+
+typedef struct tq_struct * task_queue;
+
+#define DECLARE_TASK_QUEUE(q) task_queue q = NULL
+
+extern task_queue tq_timer, tq_immediate, tq_scheduler, tq_disk;
+
+/*
+ * To implement your own list of active bottom halfs, use the following
+ * two definitions:
+ *
+ * struct tq_struct *my_bh = NULL;
+ * struct tq_struct run_my_bh = {
+ * 0, 0, (void (*)(void *)) run_task_queue, &my_bh
+ * };
+ *
+ * To activate a bottom half on your list, use:
+ *
+ * queue_task(tq_pointer, &my_bh);
+ *
+ * To run the bottom halfs on your list put them on the immediate list by:
+ *
+ * queue_task(&run_my_bh, &tq_immediate);
+ *
+ * This allows you to do deferred procession. For example, you could
+ * have a bottom half list tq_timer, which is marked active by the timer
+ * interrupt.
+ */
+
+extern spinlock_t tqueue_lock;
+
+/*
+ * queue_task
+ */
+extern __inline__ void queue_task(struct tq_struct *bh_pointer,
+ task_queue *bh_list)
+{
+ if (!test_and_set_bit(0,&bh_pointer->sync)) {
+ unsigned long flags;
+ spin_lock_irqsave(&tqueue_lock, flags);
+ bh_pointer->next = *bh_list;
+ *bh_list = bh_pointer;
+ spin_unlock_irqrestore(&tqueue_lock, flags);
+ }
+}
+
+/*
+ * Call all "bottom halfs" on a given list.
+ */
+extern __inline__ void run_task_queue(task_queue *list)
+{
+ if (*list) {
+ unsigned long flags;
+ struct tq_struct *p;
+
+ spin_lock_irqsave(&tqueue_lock, flags);
+ p = *list;
+ *list = NULL;
+ spin_unlock_irqrestore(&tqueue_lock, flags);
+
+ while (p) {
+ void *arg;
+ void (*f) (void *);
+ struct tq_struct *save_p;
+ arg = p -> data;
+ f = p -> routine;
+ save_p = p;
+ p = p -> next;
+ mb();
+ save_p -> sync = 0;
+ (*f)(arg);
+ }
+ }
+}
+
+#endif /* _LINUX_TQUEUE_H */
diff --git a/pfinet/linux-src/include/linux/trdevice.h b/pfinet/linux-src/include/linux/trdevice.h
new file mode 100644
index 00000000..8689db32
--- /dev/null
+++ b/pfinet/linux-src/include/linux/trdevice.h
@@ -0,0 +1,40 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. NET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the Token-ring handlers.
+ *
+ * Version: @(#)eth.h 1.0.4 05/13/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * Relocated to include/linux where it belongs by Alan Cox
+ * <gw4pts@gw4pts.ampr.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * WARNING: This move may well be temporary. This file will get merged with others RSN.
+ *
+ */
+#ifndef _LINUX_TRDEVICE_H
+#define _LINUX_TRDEVICE_H
+
+
+#include <linux/if_tr.h>
+
+#ifdef __KERNEL__
+extern int tr_header(struct sk_buff *skb, struct device *dev,
+ unsigned short type, void *daddr,
+ void *saddr, unsigned len);
+extern int tr_rebuild_header(struct sk_buff *skb);
+extern unsigned short tr_type_trans(struct sk_buff *skb, struct device *dev);
+extern struct device * init_trdev(struct device *, int);
+
+#endif
+
+#endif /* _LINUX_TRDEVICE_H */
diff --git a/pfinet/linux-src/include/linux/tty.h b/pfinet/linux-src/include/linux/tty.h
new file mode 100644
index 00000000..3112c349
--- /dev/null
+++ b/pfinet/linux-src/include/linux/tty.h
@@ -0,0 +1,417 @@
+#ifndef _LINUX_TTY_H
+#define _LINUX_TTY_H
+
+/*
+ * 'tty.h' defines some structures used by tty_io.c and some defines.
+ */
+
+/*
+ * These constants are also useful for user-level apps (e.g., VC
+ * resizing).
+ */
+#define MIN_NR_CONSOLES 1 /* must be at least 1 */
+#define MAX_NR_CONSOLES 63 /* serial lines start at 64 */
+#define MAX_NR_USER_CONSOLES 63 /* must be root to allocate above this */
+ /* Note: the ioctl VT_GETSTATE does not work for
+ consoles 16 and higher (since it returns a short) */
+
+#ifdef __KERNEL__
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/termios.h>
+#include <linux/tqueue.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_ldisc.h>
+#include <linux/serialP.h>
+
+#include <asm/system.h>
+
+
+/*
+ * Note: don't mess with NR_PTYS until you understand the tty minor
+ * number allocation game...
+ * (Note: the *_driver.minor_start values 1, 64, 128, 192 are
+ * hardcoded at present.)
+ */
+#define NR_PTYS 256 /* ptys/major */
+#define NR_LDISCS 16
+
+/*
+ * Unix98 PTY's can be defined as any multiple of NR_PTYS up to
+ * UNIX98_PTY_MAJOR_COUNT; this section defines what we need from the
+ * config options
+ */
+#ifdef CONFIG_UNIX98_PTYS
+# define UNIX98_NR_MAJORS ((CONFIG_UNIX98_PTY_COUNT+NR_PTYS-1)/NR_PTYS)
+# if UNIX98_NR_MAJORS <= 0
+# undef CONFIG_UNIX98_PTYS
+# elif UNIX98_NR_MAJORS > UNIX98_PTY_MAJOR_COUNT
+# error Too many Unix98 ptys defined
+# undef UNIX98_NR_MAJORS
+# define UNIX98_NR_MAJORS UNIX98_PTY_MAJOR_COUNT
+# endif
+#endif
+
+/*
+ * These are set up by the setup-routine at boot-time:
+ */
+
+struct screen_info {
+ unsigned char orig_x; /* 0x00 */
+ unsigned char orig_y; /* 0x01 */
+ unsigned short dontuse1; /* 0x02 -- EXT_MEM_K sits here */
+ unsigned short orig_video_page; /* 0x04 */
+ unsigned char orig_video_mode; /* 0x06 */
+ unsigned char orig_video_cols; /* 0x07 */
+ unsigned short unused2; /* 0x08 */
+ unsigned short orig_video_ega_bx; /* 0x0a */
+ unsigned short unused3; /* 0x0c */
+ unsigned char orig_video_lines; /* 0x0e */
+ unsigned char orig_video_isVGA; /* 0x0f */
+ unsigned short orig_video_points; /* 0x10 */
+
+ /* VESA graphic mode -- linear frame buffer */
+ unsigned short lfb_width; /* 0x12 */
+ unsigned short lfb_height; /* 0x14 */
+ unsigned short lfb_depth; /* 0x16 */
+ unsigned long lfb_base; /* 0x18 */
+ unsigned long lfb_size; /* 0x1c */
+ unsigned short dontuse2, dontuse3; /* 0x20 -- CL_MAGIC and CL_OFFSET here */
+ unsigned short lfb_linelength; /* 0x24 */
+ unsigned char red_size; /* 0x26 */
+ unsigned char red_pos; /* 0x27 */
+ unsigned char green_size; /* 0x28 */
+ unsigned char green_pos; /* 0x29 */
+ unsigned char blue_size; /* 0x2a */
+ unsigned char blue_pos; /* 0x2b */
+ unsigned char rsvd_size; /* 0x2c */
+ unsigned char rsvd_pos; /* 0x2d */
+ unsigned short vesapm_seg; /* 0x2e */
+ unsigned short vesapm_off; /* 0x30 */
+ unsigned short pages; /* 0x32 */
+ /* 0x34 -- 0x3f reserved for future expansion */
+};
+
+extern struct screen_info screen_info;
+
+#define ORIG_X (screen_info.orig_x)
+#define ORIG_Y (screen_info.orig_y)
+#define ORIG_VIDEO_MODE (screen_info.orig_video_mode)
+#define ORIG_VIDEO_COLS (screen_info.orig_video_cols)
+#define ORIG_VIDEO_EGA_BX (screen_info.orig_video_ega_bx)
+#define ORIG_VIDEO_LINES (screen_info.orig_video_lines)
+#define ORIG_VIDEO_ISVGA (screen_info.orig_video_isVGA)
+#define ORIG_VIDEO_POINTS (screen_info.orig_video_points)
+
+#define VIDEO_TYPE_MDA 0x10 /* Monochrome Text Display */
+#define VIDEO_TYPE_CGA 0x11 /* CGA Display */
+#define VIDEO_TYPE_EGAM 0x20 /* EGA/VGA in Monochrome Mode */
+#define VIDEO_TYPE_EGAC 0x21 /* EGA in Color Mode */
+#define VIDEO_TYPE_VGAC 0x22 /* VGA+ in Color Mode */
+#define VIDEO_TYPE_VLFB 0x23 /* VESA VGA in graphic mode */
+
+#define VIDEO_TYPE_TGAC 0x40 /* DEC TGA */
+
+#define VIDEO_TYPE_SUN 0x50 /* Sun frame buffer. */
+#define VIDEO_TYPE_SUNPCI 0x51 /* Sun PCI based frame buffer. */
+
+#define VIDEO_TYPE_PMAC 0x60 /* PowerMacintosh frame buffer. */
+
+#define VIDEO_TYPE_SGI 0x70 /* Various SGI graphics hardware */
+#define VIDEO_TYPE_MIPS_G364 0x71 /* MIPS Magnum 4000 G364 video */
+
+/*
+ * This character is the same as _POSIX_VDISABLE: it cannot be used as
+ * a c_cc[] character, but indicates that a particular special character
+ * isn't in use (eg VINTR has no character etc)
+ */
+#define __DISABLED_CHAR '\0'
+
+/*
+ * This is the flip buffer used for the tty driver. The buffer is
+ * located in the tty structure, and is used as a high speed interface
+ * between the tty driver and the tty line discipline.
+ */
+#define TTY_FLIPBUF_SIZE 512
+
+struct tty_flip_buffer {
+ struct tq_struct tqueue;
+ struct semaphore pty_sem;
+ char *char_buf_ptr;
+ unsigned char *flag_buf_ptr;
+ int count;
+ int buf_num;
+ unsigned char char_buf[2*TTY_FLIPBUF_SIZE];
+ char flag_buf[2*TTY_FLIPBUF_SIZE];
+ unsigned char slop[4]; /* N.B. bug overwrites buffer by 1 */
+};
+/*
+ * The pty uses char_buf and flag_buf as a contiguous buffer
+ */
+#define PTY_BUF_SIZE 4*TTY_FLIPBUF_SIZE
+
+/*
+ * When a break, frame error, or parity error happens, these codes are
+ * stuffed into the flags buffer.
+ */
+#define TTY_NORMAL 0
+#define TTY_BREAK 1
+#define TTY_FRAME 2
+#define TTY_PARITY 3
+#define TTY_OVERRUN 4
+
+#define INTR_CHAR(tty) ((tty)->termios->c_cc[VINTR])
+#define QUIT_CHAR(tty) ((tty)->termios->c_cc[VQUIT])
+#define ERASE_CHAR(tty) ((tty)->termios->c_cc[VERASE])
+#define KILL_CHAR(tty) ((tty)->termios->c_cc[VKILL])
+#define EOF_CHAR(tty) ((tty)->termios->c_cc[VEOF])
+#define TIME_CHAR(tty) ((tty)->termios->c_cc[VTIME])
+#define MIN_CHAR(tty) ((tty)->termios->c_cc[VMIN])
+#define SWTC_CHAR(tty) ((tty)->termios->c_cc[VSWTC])
+#define START_CHAR(tty) ((tty)->termios->c_cc[VSTART])
+#define STOP_CHAR(tty) ((tty)->termios->c_cc[VSTOP])
+#define SUSP_CHAR(tty) ((tty)->termios->c_cc[VSUSP])
+#define EOL_CHAR(tty) ((tty)->termios->c_cc[VEOL])
+#define REPRINT_CHAR(tty) ((tty)->termios->c_cc[VREPRINT])
+#define DISCARD_CHAR(tty) ((tty)->termios->c_cc[VDISCARD])
+#define WERASE_CHAR(tty) ((tty)->termios->c_cc[VWERASE])
+#define LNEXT_CHAR(tty) ((tty)->termios->c_cc[VLNEXT])
+#define EOL2_CHAR(tty) ((tty)->termios->c_cc[VEOL2])
+
+#define _I_FLAG(tty,f) ((tty)->termios->c_iflag & (f))
+#define _O_FLAG(tty,f) ((tty)->termios->c_oflag & (f))
+#define _C_FLAG(tty,f) ((tty)->termios->c_cflag & (f))
+#define _L_FLAG(tty,f) ((tty)->termios->c_lflag & (f))
+
+#define I_IGNBRK(tty) _I_FLAG((tty),IGNBRK)
+#define I_BRKINT(tty) _I_FLAG((tty),BRKINT)
+#define I_IGNPAR(tty) _I_FLAG((tty),IGNPAR)
+#define I_PARMRK(tty) _I_FLAG((tty),PARMRK)
+#define I_INPCK(tty) _I_FLAG((tty),INPCK)
+#define I_ISTRIP(tty) _I_FLAG((tty),ISTRIP)
+#define I_INLCR(tty) _I_FLAG((tty),INLCR)
+#define I_IGNCR(tty) _I_FLAG((tty),IGNCR)
+#define I_ICRNL(tty) _I_FLAG((tty),ICRNL)
+#define I_IUCLC(tty) _I_FLAG((tty),IUCLC)
+#define I_IXON(tty) _I_FLAG((tty),IXON)
+#define I_IXANY(tty) _I_FLAG((tty),IXANY)
+#define I_IXOFF(tty) _I_FLAG((tty),IXOFF)
+#define I_IMAXBEL(tty) _I_FLAG((tty),IMAXBEL)
+
+#define O_OPOST(tty) _O_FLAG((tty),OPOST)
+#define O_OLCUC(tty) _O_FLAG((tty),OLCUC)
+#define O_ONLCR(tty) _O_FLAG((tty),ONLCR)
+#define O_OCRNL(tty) _O_FLAG((tty),OCRNL)
+#define O_ONOCR(tty) _O_FLAG((tty),ONOCR)
+#define O_ONLRET(tty) _O_FLAG((tty),ONLRET)
+#define O_OFILL(tty) _O_FLAG((tty),OFILL)
+#define O_OFDEL(tty) _O_FLAG((tty),OFDEL)
+#define O_NLDLY(tty) _O_FLAG((tty),NLDLY)
+#define O_CRDLY(tty) _O_FLAG((tty),CRDLY)
+#define O_TABDLY(tty) _O_FLAG((tty),TABDLY)
+#define O_BSDLY(tty) _O_FLAG((tty),BSDLY)
+#define O_VTDLY(tty) _O_FLAG((tty),VTDLY)
+#define O_FFDLY(tty) _O_FLAG((tty),FFDLY)
+
+#define C_BAUD(tty) _C_FLAG((tty),CBAUD)
+#define C_CSIZE(tty) _C_FLAG((tty),CSIZE)
+#define C_CSTOPB(tty) _C_FLAG((tty),CSTOPB)
+#define C_CREAD(tty) _C_FLAG((tty),CREAD)
+#define C_PARENB(tty) _C_FLAG((tty),PARENB)
+#define C_PARODD(tty) _C_FLAG((tty),PARODD)
+#define C_HUPCL(tty) _C_FLAG((tty),HUPCL)
+#define C_CLOCAL(tty) _C_FLAG((tty),CLOCAL)
+#define C_CIBAUD(tty) _C_FLAG((tty),CIBAUD)
+#define C_CRTSCTS(tty) _C_FLAG((tty),CRTSCTS)
+
+#define L_ISIG(tty) _L_FLAG((tty),ISIG)
+#define L_ICANON(tty) _L_FLAG((tty),ICANON)
+#define L_XCASE(tty) _L_FLAG((tty),XCASE)
+#define L_ECHO(tty) _L_FLAG((tty),ECHO)
+#define L_ECHOE(tty) _L_FLAG((tty),ECHOE)
+#define L_ECHOK(tty) _L_FLAG((tty),ECHOK)
+#define L_ECHONL(tty) _L_FLAG((tty),ECHONL)
+#define L_NOFLSH(tty) _L_FLAG((tty),NOFLSH)
+#define L_TOSTOP(tty) _L_FLAG((tty),TOSTOP)
+#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL)
+#define L_ECHOPRT(tty) _L_FLAG((tty),ECHOPRT)
+#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE)
+#define L_FLUSHO(tty) _L_FLAG((tty),FLUSHO)
+#define L_PENDIN(tty) _L_FLAG((tty),PENDIN)
+#define L_IEXTEN(tty) _L_FLAG((tty),IEXTEN)
+
+/*
+ * Where all of the state associated with a tty is kept while the tty
+ * is open. Since the termios state should be kept even if the tty
+ * has been closed --- for things like the baud rate, etc --- it is
+ * not stored here, but rather a pointer to the real state is stored
+ * here. Possible the winsize structure should have the same
+ * treatment, but (1) the default 80x24 is usually right and (2) it's
+ * most often used by a windowing system, which will set the correct
+ * size each time the window is created or resized anyway.
+ * IMPORTANT: since this structure is dynamically allocated, it must
+ * be no larger than 4096 bytes. Changing TTY_FLIPBUF_SIZE will change
+ * the size of this structure, and it needs to be done with care.
+ * - TYT, 9/14/92
+ */
+struct tty_struct {
+ int magic;
+ struct tty_driver driver;
+ struct tty_ldisc ldisc;
+ struct termios *termios, *termios_locked;
+ int pgrp;
+ int session;
+ kdev_t device;
+ unsigned long flags;
+ int count;
+ struct winsize winsize;
+ unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1;
+ unsigned char low_latency:1, warned:1;
+ unsigned char ctrl_status;
+
+ struct tty_struct *link;
+ struct fasync_struct *fasync;
+ struct tty_flip_buffer flip;
+ int max_flip_cnt;
+ int alt_speed; /* For magic substitution of 38400 bps */
+ struct wait_queue *write_wait;
+ struct wait_queue *read_wait;
+ struct tq_struct tq_hangup;
+ void *disc_data;
+ void *driver_data;
+
+#define N_TTY_BUF_SIZE 4096
+
+ /*
+ * The following is data for the N_TTY line discipline. For
+ * historical reasons, this is included in the tty structure.
+ */
+ unsigned int column;
+ unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
+ unsigned char closing:1;
+ unsigned short minimum_to_wake;
+ unsigned overrun_time;
+ int num_overrun;
+ unsigned long process_char_map[256/(8*sizeof(unsigned long))];
+ char *read_buf;
+ int read_head;
+ int read_tail;
+ int read_cnt;
+ unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];
+ int canon_data;
+ unsigned long canon_head;
+ unsigned int canon_column;
+ struct semaphore atomic_read;
+ struct semaphore atomic_write;
+};
+
+/* tty magic number */
+#define TTY_MAGIC 0x5401
+
+/*
+ * These bits are used in the flags field of the tty structure.
+ *
+ * So that interrupts won't be able to mess up the queues,
+ * copy_to_cooked must be atomic with respect to itself, as must
+ * tty->write. Thus, you must use the inline functions set_bit() and
+ * clear_bit() to make things atomic.
+ */
+#define TTY_THROTTLED 0
+#define TTY_IO_ERROR 1
+#define TTY_OTHER_CLOSED 2
+#define TTY_EXCLUSIVE 3
+#define TTY_DEBUG 4
+#define TTY_DO_WRITE_WAKEUP 5
+#define TTY_PUSH 6
+#define TTY_CLOSING 7
+#define TTY_DONT_FLIP 8
+#define TTY_HW_COOK_OUT 14
+#define TTY_HW_COOK_IN 15
+#define TTY_PTY_LOCK 16
+#define TTY_NO_WRITE_SPLIT 17
+
+#define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))
+
+extern void tty_write_flush(struct tty_struct *);
+
+extern struct termios tty_std_termios;
+extern struct tty_struct * redirect;
+extern struct tty_ldisc ldiscs[];
+extern int fg_console, last_console, want_console;
+
+extern int kmsg_redirect;
+
+extern unsigned long con_init(unsigned long);
+
+extern int rs_init(void);
+extern int lp_init(void);
+extern int pty_init(void);
+extern int tty_init(void);
+extern int mxser_init(void);
+extern int moxa_init(void);
+extern int ip2_init(void);
+extern int pcxe_init(void);
+extern int pc_init(void);
+extern int vcs_init(void);
+extern int rp_init(void);
+extern int cy_init(void);
+extern int stl_init(void);
+extern int stli_init(void);
+extern int riscom8_init(void);
+extern int specialix_init(void);
+extern int espserial_init(void);
+extern int macserial_init(void);
+
+extern int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
+ const char *routine);
+extern char *tty_name(struct tty_struct *tty, char *buf);
+extern void tty_wait_until_sent(struct tty_struct * tty, long timeout);
+extern int tty_check_change(struct tty_struct * tty);
+extern void stop_tty(struct tty_struct * tty);
+extern void start_tty(struct tty_struct * tty);
+extern int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc);
+extern int tty_register_driver(struct tty_driver *driver);
+extern int tty_unregister_driver(struct tty_driver *driver);
+extern int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp,
+ int buflen);
+extern void tty_write_message(struct tty_struct *tty, char *msg);
+
+extern int is_orphaned_pgrp(int pgrp);
+extern int is_ignored(int sig);
+extern int tty_signal(int sig, struct tty_struct *tty);
+extern void tty_hangup(struct tty_struct * tty);
+extern void tty_vhangup(struct tty_struct * tty);
+extern void tty_unhangup(struct file *filp);
+extern int tty_hung_up_p(struct file * filp);
+extern void do_SAK(struct tty_struct *tty);
+extern void disassociate_ctty(int priv);
+extern void tty_flip_buffer_push(struct tty_struct *tty);
+extern int tty_get_baud_rate(struct tty_struct *tty);
+
+/* n_tty.c */
+extern struct tty_ldisc tty_ldisc_N_TTY;
+
+/* tty_ioctl.c */
+extern int n_tty_ioctl(struct tty_struct * tty, struct file * file,
+ unsigned int cmd, unsigned long arg);
+
+/* serial.c */
+
+extern long serial_console_init(long kmem_start, long kmem_end);
+
+/* pcxx.c */
+
+extern int pcxe_open(struct tty_struct *tty, struct file *filp);
+
+/* printk.c */
+
+extern void console_print(const char *);
+
+/* vt.c */
+
+extern int vt_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg);
+
+#endif /* __KERNEL__ */
+#endif
diff --git a/pfinet/linux-src/include/linux/tty_driver.h b/pfinet/linux-src/include/linux/tty_driver.h
new file mode 100644
index 00000000..46ace283
--- /dev/null
+++ b/pfinet/linux-src/include/linux/tty_driver.h
@@ -0,0 +1,227 @@
+#ifndef _LINUX_TTY_DRIVER_H
+#define _LINUX_TTY_DRIVER_H
+
+/*
+ * This structure defines the interface between the low-level tty
+ * driver and the tty routines. The following routines can be
+ * defined; unless noted otherwise, they are optional, and can be
+ * filled in with a null pointer.
+ *
+ * int (*open)(struct tty_struct * tty, struct file * filp);
+ *
+ * This routine is called when a particular tty device is opened.
+ * This routine is mandatory; if this routine is not filled in,
+ * the attempted open will fail with ENODEV.
+ *
+ * void (*close)(struct tty_struct * tty, struct file * filp);
+ *
+ * This routine is called when a particular tty device is closed.
+ *
+ * int (*write)(struct tty_struct * tty, int from_user,
+ * const unsigned char *buf, int count);
+ *
+ * This routine is called by the kernel to write a series of
+ * characters to the tty device. The characters may come from
+ * user space or kernel space. This routine will return the
+ * number of characters actually accepted for writing. This
+ * routine is mandatory.
+ *
+ * void (*put_char)(struct tty_struct *tty, unsigned char ch);
+ *
+ * This routine is called by the kernel to write a single
+ * character to the tty device. If the kernel uses this routine,
+ * it must call the flush_chars() routine (if defined) when it is
+ * done stuffing characters into the driver. If there is no room
+ * in the queue, the character is ignored.
+ *
+ * void (*flush_chars)(struct tty_struct *tty);
+ *
+ * This routine is called by the kernel after it has written a
+ * series of characters to the tty device using put_char().
+ *
+ * int (*write_room)(struct tty_struct *tty);
+ *
+ * This routine returns the numbers of characters the tty driver
+ * will accept for queuing to be written. This number is subject
+ * to change as output buffers get emptied, or if the output flow
+ * control is acted.
+ *
+ * int (*ioctl)(struct tty_struct *tty, struct file * file,
+ * unsigned int cmd, unsigned long arg);
+ *
+ * This routine allows the tty driver to implement
+ * device-specific ioctl's. If the ioctl number passed in cmd
+ * is not recognized by the driver, it should return ENOIOCTLCMD.
+ *
+ * void (*set_termios)(struct tty_struct *tty, struct termios * old);
+ *
+ * This routine allows the tty driver to be notified when
+ * device's termios settings have changed. Note that a
+ * well-designed tty driver should be prepared to accept the case
+ * where old == NULL, and try to do something rational.
+ *
+ * void (*set_ldisc)(struct tty_struct *tty);
+ *
+ * This routine allows the tty driver to be notified when the
+ * device's termios settings have changed.
+ *
+ * void (*throttle)(struct tty_struct * tty);
+ *
+ * This routine notifies the tty driver that input buffers for
+ * the line discipline are close to full, and it should somehow
+ * signal that no more characters should be sent to the tty.
+ *
+ * void (*unthrottle)(struct tty_struct * tty);
+ *
+ * This routine notifies the tty drivers that it should signals
+ * that characters can now be sent to the tty without fear of
+ * overrunning the input buffers of the line disciplines.
+ *
+ * void (*stop)(struct tty_struct *tty);
+ *
+ * This routine notifies the tty driver that it should stop
+ * outputting characters to the tty device.
+ *
+ * void (*start)(struct tty_struct *tty);
+ *
+ * This routine notifies the tty driver that it resume sending
+ * characters to the tty device.
+ *
+ * void (*hangup)(struct tty_struct *tty);
+ *
+ * This routine notifies the tty driver that it should hangup the
+ * tty device.
+ *
+ * void (*break_ctl)(struct tty_stuct *tty, int state);
+ *
+ * This optional routine requests the tty driver to turn on or
+ * off BREAK status on the RS-232 port. If state is -1,
+ * then the BREAK status should be turned on; if state is 0, then
+ * BREAK should be turned off.
+ *
+ * If this routine is implemented, the high-level tty driver will
+ * handle the following ioctls: TCSBRK, TCSBRKP, TIOCSBRK,
+ * TIOCCBRK. Otherwise, these ioctls will be passed down to the
+ * driver to handle.
+ *
+ * void (*wait_until_sent)(struct tty_struct *tty, int timeout);
+ *
+ * This routine waits until the device has written out all of the
+ * characters in its transmitter FIFO.
+ *
+ * void (*send_xchar)(struct tty_struct *tty, char ch);
+ *
+ * This routine is used to send a high-priority XON/XOFF
+ * character to the device.
+ */
+
+#include <linux/fs.h>
+
+struct tty_driver {
+ int magic; /* magic number for this structure */
+ const char *driver_name;
+ const char *name;
+ int name_base; /* offset of printed name */
+ short major; /* major device number */
+ short minor_start; /* start of minor device number*/
+ short num; /* number of devices */
+ short type; /* type of tty driver */
+ short subtype; /* subtype of tty driver */
+ struct termios init_termios; /* Initial termios */
+ int flags; /* tty driver flags */
+ int *refcount; /* for loadable tty drivers */
+ struct proc_dir_entry *proc_entry; /* /proc fs entry */
+ struct tty_driver *other; /* only used for the PTY driver */
+
+ /*
+ * Pointer to the tty data structures
+ */
+ struct tty_struct **table;
+ struct termios **termios;
+ struct termios **termios_locked;
+ void *driver_state; /* only used for the PTY driver */
+
+ /*
+ * Interface routines from the upper tty layer to the tty
+ * driver.
+ */
+ int (*open)(struct tty_struct * tty, struct file * filp);
+ void (*close)(struct tty_struct * tty, struct file * filp);
+ int (*write)(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count);
+ void (*put_char)(struct tty_struct *tty, unsigned char ch);
+ void (*flush_chars)(struct tty_struct *tty);
+ int (*write_room)(struct tty_struct *tty);
+ int (*chars_in_buffer)(struct tty_struct *tty);
+ int (*ioctl)(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg);
+ void (*set_termios)(struct tty_struct *tty, struct termios * old);
+ void (*throttle)(struct tty_struct * tty);
+ void (*unthrottle)(struct tty_struct * tty);
+ void (*stop)(struct tty_struct *tty);
+ void (*start)(struct tty_struct *tty);
+ void (*hangup)(struct tty_struct *tty);
+ void (*break_ctl)(struct tty_struct *tty, int state);
+ void (*flush_buffer)(struct tty_struct *tty);
+ void (*set_ldisc)(struct tty_struct *tty);
+ void (*wait_until_sent)(struct tty_struct *tty, int timeout);
+ void (*send_xchar)(struct tty_struct *tty, char ch);
+ int (*read_proc)(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+ int (*write_proc)(struct file *file, const char *buffer,
+ unsigned long count, void *data);
+
+ /*
+ * linked list pointers
+ */
+ struct tty_driver *next;
+ struct tty_driver *prev;
+};
+
+/* tty driver magic number */
+#define TTY_DRIVER_MAGIC 0x5402
+
+/*
+ * tty driver flags
+ *
+ * TTY_DRIVER_RESET_TERMIOS --- requests the tty layer to reset the
+ * termios setting when the last process has closed the device.
+ * Used for PTY's, in particular.
+ *
+ * TTY_DRIVER_REAL_RAW --- if set, indicates that the driver will
+ * guarantee never not to set any special character handling
+ * flags if ((IGNBRK || (!BRKINT && !PARMRK)) && (IGNPAR ||
+ * !INPCK)). That is, if there is no reason for the driver to
+ * send notifications of parity and break characters up to the
+ * line driver, it won't do so. This allows the line driver to
+ * optimize for this case if this flag is set. (Note that there
+ * is also a promise, if the above case is true, not to signal
+ * overruns, either.)
+ */
+#define TTY_DRIVER_INSTALLED 0x0001
+#define TTY_DRIVER_RESET_TERMIOS 0x0002
+#define TTY_DRIVER_REAL_RAW 0x0004
+
+/* tty driver types */
+#define TTY_DRIVER_TYPE_SYSTEM 0x0001
+#define TTY_DRIVER_TYPE_CONSOLE 0x0002
+#define TTY_DRIVER_TYPE_SERIAL 0x0003
+#define TTY_DRIVER_TYPE_PTY 0x0004
+#define TTY_DRIVER_TYPE_SCC 0x0005 /* scc driver */
+#define TTY_DRIVER_TYPE_SYSCONS 0x0006
+
+/* system subtypes (magic, used by tty_io.c) */
+#define SYSTEM_TYPE_TTY 0x0001
+#define SYSTEM_TYPE_CONSOLE 0x0002
+#define SYSTEM_TYPE_SYSCONS 0x0003
+#define SYSTEM_TYPE_SYSPTMX 0x0004
+
+/* pty subtypes (magic, used by tty_io.c) */
+#define PTY_TYPE_MASTER 0x0001
+#define PTY_TYPE_SLAVE 0x0002
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2
+
+#endif /* #ifdef _LINUX_TTY_DRIVER_H */
diff --git a/pfinet/linux-src/include/linux/tty_flip.h b/pfinet/linux-src/include/linux/tty_flip.h
new file mode 100644
index 00000000..daea800e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/tty_flip.h
@@ -0,0 +1,28 @@
+#ifndef _LINUX_TTY_FLIP_H
+#define _LINUX_TTY_FLIP_H
+
+#ifdef INCLUDE_INLINE_FUNCS
+#define _INLINE_ extern
+#else
+#define _INLINE_ extern __inline__
+#endif
+
+_INLINE_ void tty_insert_flip_char(struct tty_struct *tty,
+ unsigned char ch, char flag)
+{
+ if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+ tty->flip.count++;
+ *tty->flip.flag_buf_ptr++ = flag;
+ *tty->flip.char_buf_ptr++ = ch;
+ }
+}
+
+_INLINE_ void tty_schedule_flip(struct tty_struct *tty)
+{
+ queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+#undef _INLINE_
+
+
+#endif /* _LINUX_TTY_FLIP_H */
diff --git a/pfinet/linux-src/include/linux/tty_ldisc.h b/pfinet/linux-src/include/linux/tty_ldisc.h
new file mode 100644
index 00000000..4e904f83
--- /dev/null
+++ b/pfinet/linux-src/include/linux/tty_ldisc.h
@@ -0,0 +1,138 @@
+#ifndef _LINUX_TTY_LDISC_H
+#define _LINUX_TTY_LDISC_H
+
+/*
+ * This structure defines the interface between the tty line discpline
+ * implementation and the tty routines. The following routines can be
+ * defined; unless noted otherwise, they are optional, and can be
+ * filled in with a null pointer.
+ *
+ * int (*open)(struct tty_struct *);
+ *
+ * This function is called when the line discpline is associated
+ * with the tty. The line discpline can use this as an
+ * opportunity to initialize any state needed by the ldisc routines.
+ *
+ * void (*close)(struct tty_struct *);
+ *
+ * This function is called when the line discpline is being
+ * shutdown, either because the tty is being closed or because
+ * the tty is being changed to use a new line discpline
+ *
+ * void (*flush_buffer)(struct tty_struct *tty);
+ *
+ * This function instructs the line discipline to clear its
+ * buffers of any input characters it may have queued to be
+ * delivered to the user mode process.
+ *
+ * ssize_t (*chars_in_buffer)(struct tty_struct *tty);
+ *
+ * This function returns the number of input characters the line
+ * iscpline may have queued up to be delivered to the user mode
+ * process.
+ *
+ * ssize_t (*read)(struct tty_struct * tty, struct file * file,
+ * unsigned char * buf, size_t nr);
+ *
+ * This function is called when the user requests to read from
+ * the tty. The line discpline will return whatever characters
+ * it has buffered up for the user. If this function is not
+ * defined, the user will receive an EIO error.
+ *
+ * ssize_t (*write)(struct tty_struct * tty, struct file * file,
+ * const unsigned char * buf, size_t nr);
+ *
+ * This function is called when the user requests to write to the
+ * tty. The line discpline will deliver the characters to the
+ * low-level tty device for transmission, optionally performing
+ * some processing on the characters first. If this function is
+ * not defined, the user will receive an EIO error.
+ *
+ * int (*ioctl)(struct tty_struct * tty, struct file * file,
+ * unsigned int cmd, unsigned long arg);
+ *
+ * This function is called when the user requests an ioctl which
+ * is not handled by the tty layer or the low-level tty driver.
+ * It is intended for ioctls which affect line discpline
+ * operation. Not that the search order for ioctls is (1) tty
+ * layer, (2) tty low-level driver, (3) line discpline. So a
+ * low-level driver can "grab" an ioctl request before the line
+ * discpline has a chance to see it.
+ *
+ * void (*set_termios)(struct tty_struct *tty, struct termios * old);
+ *
+ * This function notifies the line discpline that a change has
+ * been made to the termios structure.
+ *
+ * int (*poll)(struct tty_struct * tty, struct file * file,
+ * poll_table *wait);
+ *
+ * This function is called when a user attempts to select/poll on a
+ * tty device. It is solely the responsibility of the line
+ * discipline to handle poll requests.
+ *
+ * void (*receive_buf)(struct tty_struct *, const unsigned char *cp,
+ * char *fp, int count);
+ *
+ * This function is called by the low-level tty driver to send
+ * characters received by the hardware to the line discpline for
+ * processing. <cp> is a pointer to the buffer of input
+ * character received by the device. <fp> is a pointer to a
+ * pointer of flag bytes which indicate whether a character was
+ * received with a parity error, etc.
+ *
+ * int (*receive_room)(struct tty_struct *);
+ *
+ * This function is called by the low-level tty driver to
+ * determine how many characters the line discpline can accept.
+ * The low-level driver must not send more characters than was
+ * indicated by receive_room, or the line discpline may drop
+ * those characters.
+ *
+ * void (*write_wakeup)(struct tty_struct *);
+ *
+ * This function is called by the low-level tty driver to signal
+ * that line discpline should try to send more characters to the
+ * low-level driver for transmission. If the line discpline does
+ * not have any more data to send, it can just return.
+ */
+
+#include <linux/fs.h>
+#include <linux/wait.h>
+
+struct tty_ldisc {
+ int magic;
+ char *name;
+ int num;
+ int flags;
+ /*
+ * The following routines are called from above.
+ */
+ int (*open)(struct tty_struct *);
+ void (*close)(struct tty_struct *);
+ void (*flush_buffer)(struct tty_struct *tty);
+ ssize_t (*chars_in_buffer)(struct tty_struct *tty);
+ ssize_t (*read)(struct tty_struct * tty, struct file * file,
+ unsigned char * buf, size_t nr);
+ ssize_t (*write)(struct tty_struct * tty, struct file * file,
+ const unsigned char * buf, size_t nr);
+ int (*ioctl)(struct tty_struct * tty, struct file * file,
+ unsigned int cmd, unsigned long arg);
+ void (*set_termios)(struct tty_struct *tty, struct termios * old);
+ unsigned int (*poll)(struct tty_struct *, struct file *,
+ struct poll_table_struct *);
+
+ /*
+ * The following routines are called from below.
+ */
+ void (*receive_buf)(struct tty_struct *, const unsigned char *cp,
+ char *fp, int count);
+ int (*receive_room)(struct tty_struct *);
+ void (*write_wakeup)(struct tty_struct *);
+};
+
+#define TTY_LDISC_MAGIC 0x5403
+
+#define LDISC_FLAG_DEFINED 0x00000001
+
+#endif /* _LINUX_TTY_LDISC_H */
diff --git a/pfinet/linux-src/include/linux/types.h b/pfinet/linux-src/include/linux/types.h
new file mode 100644
index 00000000..a53a4ccc
--- /dev/null
+++ b/pfinet/linux-src/include/linux/types.h
@@ -0,0 +1,108 @@
+#ifndef _LINUX_TYPES_H
+#define _LINUX_TYPES_H
+
+#include <linux/posix_types.h>
+#include <asm/types.h>
+
+#ifndef __KERNEL_STRICT_NAMES
+
+typedef __kernel_fd_set fd_set;
+typedef __kernel_dev_t dev_t;
+typedef __kernel_ino_t ino_t;
+typedef __kernel_mode_t mode_t;
+typedef __kernel_nlink_t nlink_t;
+typedef __kernel_off_t off_t;
+typedef __kernel_pid_t pid_t;
+typedef __kernel_uid_t uid_t;
+typedef __kernel_gid_t gid_t;
+typedef __kernel_daddr_t daddr_t;
+typedef __kernel_key_t key_t;
+typedef __kernel_suseconds_t suseconds_t;
+
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+typedef __kernel_loff_t loff_t;
+#endif
+
+/*
+ * The following typedefs are also protected by individual ifdefs for
+ * historical reasons:
+ */
+#ifndef _SIZE_T
+#define _SIZE_T
+typedef __kernel_size_t size_t;
+#endif
+
+#ifndef _SSIZE_T
+#define _SSIZE_T
+typedef __kernel_ssize_t ssize_t;
+#endif
+
+#ifndef _PTRDIFF_T
+#define _PTRDIFF_T
+typedef __kernel_ptrdiff_t ptrdiff_t;
+#endif
+
+#ifndef _TIME_T
+#define _TIME_T
+typedef __kernel_time_t time_t;
+#endif
+
+#ifndef _CLOCK_T
+#define _CLOCK_T
+typedef __kernel_clock_t clock_t;
+#endif
+
+#ifndef _CADDR_T
+#define _CADDR_T
+typedef __kernel_caddr_t caddr_t;
+#endif
+
+/* bsd */
+typedef unsigned char u_char;
+typedef unsigned short u_short;
+typedef unsigned int u_int;
+typedef unsigned long u_long;
+
+/* sysv */
+typedef unsigned char unchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+
+#ifndef __BIT_TYPES_DEFINED__
+#define __BIT_TYPES_DEFINED__
+
+typedef __u8 u_int8_t;
+typedef __s8 int8_t;
+typedef __u16 u_int16_t;
+typedef __s16 int16_t;
+typedef __u32 u_int32_t;
+typedef __s32 int32_t;
+
+#endif /* !(__BIT_TYPES_DEFINED__) */
+
+typedef __u8 uint8_t;
+typedef __u16 uint16_t;
+typedef __u32 uint32_t;
+
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+typedef __u64 uint64_t;
+typedef __u64 u_int64_t;
+typedef __s64 int64_t;
+#endif
+
+#endif /* __KERNEL_STRICT_NAMES */
+
+/*
+ * Below are truly Linux-specific types that should never collide with
+ * any application/library that wants linux/types.h.
+ */
+
+struct ustat {
+ __kernel_daddr_t f_tfree;
+ __kernel_ino_t f_tinode;
+ char f_fname[6];
+ char f_fpack[6];
+};
+
+#endif /* _LINUX_TYPES_H */
diff --git a/pfinet/linux-src/include/linux/udp.h b/pfinet/linux-src/include/linux/udp.h
new file mode 100644
index 00000000..ab75a1e8
--- /dev/null
+++ b/pfinet/linux-src/include/linux/udp.h
@@ -0,0 +1,29 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the UDP protocol.
+ *
+ * Version: @(#)udp.h 1.0.2 04/28/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_UDP_H
+#define _LINUX_UDP_H
+
+
+struct udphdr {
+ __u16 source;
+ __u16 dest;
+ __u16 len;
+ __u16 check;
+};
+
+
+#endif /* _LINUX_UDP_H */
diff --git a/pfinet/linux-src/include/linux/ufs_fs.h b/pfinet/linux-src/include/linux/ufs_fs.h
new file mode 100644
index 00000000..38604163
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ufs_fs.h
@@ -0,0 +1,571 @@
+/*
+ * linux/include/linux/ufs_fs.h
+ *
+ * Copyright (C) 1996
+ * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
+ * Laboratory for Computer Science Research Computing Facility
+ * Rutgers, The State University of New Jersey
+ *
+ * Clean swab support by Fare <fare@tunes.org>
+ * just hope no one is using NNUUXXI on __?64 structure elements
+ * 64-bit clean thanks to Maciej W. Rozycki <macro@ds2.pg.gda.pl>
+ *
+ * 4.4BSD (FreeBSD) support added on February 1st 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
+ * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
+ *
+ * NeXTstep support added on February 5th 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk>.
+ *
+ * Write support by Daniel Pirkl <daniel.pirkl@email.cz>
+ */
+
+#ifndef __LINUX_UFS_FS_H
+#define __LINUX_UFS_FS_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/stat.h>
+
+#define UFS_BBLOCK 0
+#define UFS_BBSIZE 8192
+#define UFS_SBLOCK 8192
+#define UFS_SBSIZE 8192
+
+#define UFS_SECTOR_SIZE 512
+#define UFS_SECTOR_BITS 9
+#define UFS_MAGIC 0x00011954
+#define UFS_CIGAM 0x54190100 /* byteswapped MAGIC */
+
+#define UFS_BSIZE 8192
+#define UFS_MINBSIZE 4096
+#define UFS_FSIZE 1024
+#define UFS_MAXFRAG (UFS_BSIZE / UFS_FSIZE)
+
+#define UFS_NDADDR 12
+#define UFS_NINDIR 3
+
+#define UFS_IND_BLOCK (UFS_NDADDR + 0)
+#define UFS_DIND_BLOCK (UFS_NDADDR + 1)
+#define UFS_TIND_BLOCK (UFS_NDADDR + 2)
+
+#define UFS_NDIR_FRAGMENT (UFS_NDADDR << uspi->s_fpbshift)
+#define UFS_IND_FRAGMENT (UFS_IND_BLOCK << uspi->s_fpbshift)
+#define UFS_DIND_FRAGMENT (UFS_DIND_BLOCK << uspi->s_fpbshift)
+#define UFS_TIND_FRAGMENT (UFS_TIND_BLOCK << uspi->s_fpbshift)
+
+#define UFS_ROOTINO 2
+#define UFS_FIRST_INO (UFS_ROOTINO + 1)
+
+#define UFS_USEEFT ((__u16)65535)
+
+#define UFS_FSOK 0x7c269d38
+#define UFS_FSACTIVE ((char)0x00)
+#define UFS_FSCLEAN ((char)0x01)
+#define UFS_FSSTABLE ((char)0x02)
+#define UFS_FSOSF1 ((char)0x03) /* is this correct for DEC OSF/1? */
+#define UFS_FSBAD ((char)0xff)
+
+/* From here to next blank line, s_flags for ufs_sb_info */
+/* endianness */
+#define UFS_BYTESEX 0x00000001 /* mask; leave room to 0xF */
+#if defined(__LITTLE_ENDIAN) || defined(__BIG_ENDIAN)
+/* these are for sane architectures */
+#define UFS_NATIVE_ENDIAN 0x00000000
+#define UFS_SWABBED_ENDIAN 0x00000001
+#else
+/* these are for pervert architectures */
+#define UFS_LITTLE_ENDIAN 0x00000000
+#define UFS_BIG_ENDIAN 0x00000001
+#endif
+/* directory entry encoding */
+#define UFS_DE_MASK 0x00000010 /* mask for the following */
+#define UFS_DE_OLD 0x00000000
+#define UFS_DE_44BSD 0x00000010
+/* uid encoding */
+#define UFS_UID_MASK 0x00000060 /* mask for the following */
+#define UFS_UID_OLD 0x00000000
+#define UFS_UID_44BSD 0x00000020
+#define UFS_UID_EFT 0x00000040
+/* superblock state encoding */
+#define UFS_ST_MASK 0x00000700 /* mask for the following */
+#define UFS_ST_OLD 0x00000000
+#define UFS_ST_44BSD 0x00000100
+#define UFS_ST_SUN 0x00000200
+#define UFS_ST_SUNx86 0x00000400
+/*cylinder group encoding */
+#define UFS_CG_MASK 0x00003000 /* mask for the following */
+#define UFS_CG_OLD 0x00000000
+#define UFS_CG_44BSD 0x00002000
+#define UFS_CG_SUN 0x00001000
+
+/* fs_inodefmt options */
+#define UFS_42INODEFMT -1
+#define UFS_44INODEFMT 2
+
+/* mount options */
+#define UFS_MOUNT_ONERROR 0x0000000F
+#define UFS_MOUNT_ONERROR_PANIC 0x00000001
+#define UFS_MOUNT_ONERROR_LOCK 0x00000002
+#define UFS_MOUNT_ONERROR_UMOUNT 0x00000004
+#define UFS_MOUNT_ONERROR_REPAIR 0x00000008
+
+#define UFS_MOUNT_UFSTYPE 0x000007F0
+#define UFS_MOUNT_UFSTYPE_OLD 0x00000010
+#define UFS_MOUNT_UFSTYPE_44BSD 0x00000020
+#define UFS_MOUNT_UFSTYPE_SUN 0x00000040
+#define UFS_MOUNT_UFSTYPE_NEXTSTEP 0x00000080
+#define UFS_MOUNT_UFSTYPE_NEXTSTEP_CD 0x00000100
+#define UFS_MOUNT_UFSTYPE_OPENSTEP 0x00000200
+#define UFS_MOUNT_UFSTYPE_SUNx86 0x00000400
+
+#define ufs_clear_opt(o,opt) o &= ~UFS_MOUNT_##opt
+#define ufs_set_opt(o,opt) o |= UFS_MOUNT_##opt
+#define ufs_test_opt(o,opt) ((o) & UFS_MOUNT_##opt)
+
+/*
+ * MINFREE gives the minimum acceptable percentage of file system
+ * blocks which may be free. If the freelist drops below this level
+ * only the superuser may continue to allocate blocks. This may
+ * be set to 0 if no reserve of free blocks is deemed necessary,
+ * however throughput drops by fifty percent if the file system
+ * is run at between 95% and 100% full; thus the minimum default
+ * value of fs_minfree is 5%. However, to get good clustering
+ * performance, 10% is a better choice. hence we use 10% as our
+ * default value. With 10% free space, fragmentation is not a
+ * problem, so we choose to optimize for time.
+ */
+#define UFS_MINFREE 5
+#define UFS_DEFAULTOPT UFS_OPTTIME
+
+/*
+ * Turn file system block numbers into disk block addresses.
+ * This maps file system blocks to device size blocks.
+ */
+#define ufs_fsbtodb(uspi, b) ((b) << (uspi)->s_fsbtodb)
+#define ufs_dbtofsb(uspi, b) ((b) >> (uspi)->s_fsbtodb)
+
+/*
+ * Cylinder group macros to locate things in cylinder groups.
+ * They calc file system addresses of cylinder group data structures.
+ */
+#define ufs_cgbase(c) (uspi->s_fpg * (c))
+#define ufs_cgstart(c) (ufs_cgbase(c) + uspi->s_cgoffset * ((c) & ~uspi->s_cgmask))
+#define ufs_cgsblock(c) (ufs_cgstart(c) + uspi->s_sblkno) /* super blk */
+#define ufs_cgcmin(c) (ufs_cgstart(c) + uspi->s_cblkno) /* cg block */
+#define ufs_cgimin(c) (ufs_cgstart(c) + uspi->s_iblkno) /* inode blk */
+#define ufs_cgdmin(c) (ufs_cgstart(c) + uspi->s_dblkno) /* 1st data */
+
+/*
+ * Macros for handling inode numbers:
+ * inode number to file system block offset.
+ * inode number to cylinder group number.
+ * inode number to file system block address.
+ */
+#define ufs_inotocg(x) ((x) / uspi->s_ipg)
+#define ufs_inotocgoff(x) ((x) % uspi->s_ipg)
+#define ufs_inotofsba(x) (ufs_cgimin(ufs_inotocg(x)) + ufs_inotocgoff(x) / uspi->s_inopf)
+#define ufs_inotofsbo(x) ((x) % uspi->s_inopf)
+
+/*
+ * Give cylinder group number for a file system block.
+ * Give cylinder group block number for a file system block.
+ */
+#define ufs_dtog(d) ((d) / uspi->s_fpg)
+#define ufs_dtogd(d) ((d) % uspi->s_fpg)
+
+/*
+ * Compute the cylinder and rotational position of a cyl block addr.
+ */
+#define ufs_cbtocylno(bno) \
+ ((bno) * uspi->s_nspf / uspi->s_spc)
+#define ufs_cbtorpos(bno) \
+ ((((bno) * uspi->s_nspf % uspi->s_spc / uspi->s_nsect \
+ * uspi->s_trackskew + (bno) * uspi->s_nspf % uspi->s_spc \
+ % uspi->s_nsect * uspi->s_interleave) % uspi->s_nsect \
+ * uspi->s_nrpos) / uspi->s_npsect)
+
+/*
+ * The following macros optimize certain frequently calculated
+ * quantities by using shifts and masks in place of divisions
+ * modulos and multiplications.
+ */
+#define ufs_blkoff(loc) ((loc) & uspi->s_qbmask)
+#define ufs_fragoff(loc) ((loc) & uspi->s_qfmask)
+#define ufs_lblktosize(blk) ((blk) << uspi->s_bshift)
+#define ufs_lblkno(loc) ((loc) >> uspi->s_bshift)
+#define ufs_numfrags(loc) ((loc) >> uspi->s_fshift)
+#define ufs_blkroundup(size) (((size) + uspi->s_qbmask) & uspi->s_bmask)
+#define ufs_fragroundup(size) (((size) + uspi->s_qfmask) & uspi->s_fmask)
+#define ufs_fragstoblks(frags) ((frags) >> uspi->s_fpbshift)
+#define ufs_blkstofrags(blks) ((blks) << uspi->s_fpbshift)
+#define ufs_fragnum(fsb) ((fsb) & uspi->s_fpbmask)
+#define ufs_blknum(fsb) ((fsb) & ~uspi->s_fpbmask)
+
+#define UFS_MAXNAMLEN 255
+#define UFS_MAXMNTLEN 512
+#define UFS_MAXCSBUFS 31
+#define UFS_LINK_MAX 32000
+
+/*
+ * UFS_DIR_PAD defines the directory entries boundaries
+ * (must be a multiple of 4)
+ */
+#define UFS_DIR_PAD 4
+#define UFS_DIR_ROUND (UFS_DIR_PAD - 1)
+#define UFS_DIR_REC_LEN(name_len) (((name_len) + 1 + 8 + UFS_DIR_ROUND) & ~UFS_DIR_ROUND)
+
+struct ufs_timeval {
+ __s32 tv_sec;
+ __s32 tv_usec;
+};
+
+/*
+ * File types
+ */
+#define DT_UNKNOWN 0
+#define DT_FIFO 1
+#define DT_CHR 2
+#define DT_DIR 4
+#define DT_BLK 6
+#define DT_REG 8
+#define DT_LNK 10
+#define DT_SOCK 12
+#define DT_WHT 14
+
+struct ufs_dir_entry {
+ __u32 d_ino; /* inode number of this entry */
+ __u16 d_reclen; /* length of this entry */
+ union {
+ __u16 d_namlen; /* actual length of d_name */
+ struct {
+ __u8 d_type; /* file type */
+ __u8 d_namlen; /* length of string in d_name */
+ } d_44;
+ } d_u;
+ __u8 d_name[UFS_MAXNAMLEN + 1]; /* file name */
+};
+
+struct ufs_csum {
+ __u32 cs_ndir; /* number of directories */
+ __u32 cs_nbfree; /* number of free blocks */
+ __u32 cs_nifree; /* number of free inodes */
+ __u32 cs_nffree; /* number of free frags */
+};
+
+/*
+ * This is the actual superblock, as it is laid out on the disk.
+ */
+struct ufs_super_block {
+ __u32 fs_link; /* UNUSED */
+ __u32 fs_rlink; /* UNUSED */
+ __u32 fs_sblkno; /* addr of super-block in filesys */
+ __u32 fs_cblkno; /* offset of cyl-block in filesys */
+ __u32 fs_iblkno; /* offset of inode-blocks in filesys */
+ __u32 fs_dblkno; /* offset of first data after cg */
+ __u32 fs_cgoffset; /* cylinder group offset in cylinder */
+ __u32 fs_cgmask; /* used to calc mod fs_ntrak */
+ __u32 fs_time; /* last time written -- time_t */
+ __u32 fs_size; /* number of blocks in fs */
+ __u32 fs_dsize; /* number of data blocks in fs */
+ __u32 fs_ncg; /* number of cylinder groups */
+ __u32 fs_bsize; /* size of basic blocks in fs */
+ __u32 fs_fsize; /* size of frag blocks in fs */
+ __u32 fs_frag; /* number of frags in a block in fs */
+/* these are configuration parameters */
+ __u32 fs_minfree; /* minimum percentage of free blocks */
+ __u32 fs_rotdelay; /* num of ms for optimal next block */
+ __u32 fs_rps; /* disk revolutions per second */
+/* these fields can be computed from the others */
+ __u32 fs_bmask; /* ``blkoff'' calc of blk offsets */
+ __u32 fs_fmask; /* ``fragoff'' calc of frag offsets */
+ __u32 fs_bshift; /* ``lblkno'' calc of logical blkno */
+ __u32 fs_fshift; /* ``numfrags'' calc number of frags */
+/* these are configuration parameters */
+ __u32 fs_maxcontig; /* max number of contiguous blks */
+ __u32 fs_maxbpg; /* max number of blks per cyl group */
+/* these fields can be computed from the others */
+ __u32 fs_fragshift; /* block to frag shift */
+ __u32 fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */
+ __u32 fs_sbsize; /* actual size of super block */
+ __u32 fs_csmask; /* csum block offset */
+ __u32 fs_csshift; /* csum block number */
+ __u32 fs_nindir; /* value of NINDIR */
+ __u32 fs_inopb; /* value of INOPB */
+ __u32 fs_nspf; /* value of NSPF */
+/* yet another configuration parameter */
+ __u32 fs_optim; /* optimization preference, see below */
+/* these fields are derived from the hardware */
+ union {
+ struct {
+ __u32 fs_npsect; /* # sectors/track including spares */
+ } fs_sun;
+ struct {
+ __s32 fs_state; /* file system state time stamp */
+ } fs_sunx86;
+ } fs_u1;
+ __u32 fs_interleave; /* hardware sector interleave */
+ __u32 fs_trackskew; /* sector 0 skew, per track */
+/* a unique id for this filesystem (currently unused and unmaintained) */
+/* In 4.3 Tahoe this space is used by fs_headswitch and fs_trkseek */
+/* Neither of those fields is used in the Tahoe code right now but */
+/* there could be problems if they are. */
+ __u32 fs_id[2]; /* file system id */
+/* sizes determined by number of cylinder groups and their sizes */
+ __u32 fs_csaddr; /* blk addr of cyl grp summary area */
+ __u32 fs_cssize; /* size of cyl grp summary area */
+ __u32 fs_cgsize; /* cylinder group size */
+/* these fields are derived from the hardware */
+ __u32 fs_ntrak; /* tracks per cylinder */
+ __u32 fs_nsect; /* sectors per track */
+ __u32 fs_spc; /* sectors per cylinder */
+/* this comes from the disk driver partitioning */
+ __u32 fs_ncyl; /* cylinders in file system */
+/* these fields can be computed from the others */
+ __u32 fs_cpg; /* cylinders per group */
+ __u32 fs_ipg; /* inodes per group */
+ __u32 fs_fpg; /* blocks per group * fs_frag */
+/* this data must be re-computed after crashes */
+ struct ufs_csum fs_cstotal; /* cylinder summary information */
+/* these fields are cleared at mount time */
+ __s8 fs_fmod; /* super block modified flag */
+ __s8 fs_clean; /* file system is clean flag */
+ __s8 fs_ronly; /* mounted read-only flag */
+ __s8 fs_flags; /* currently unused flag */
+ __s8 fs_fsmnt[UFS_MAXMNTLEN]; /* name mounted on */
+/* these fields retain the current block allocation info */
+ __u32 fs_cgrotor; /* last cg searched */
+ __u32 fs_csp[UFS_MAXCSBUFS]; /* list of fs_cs info buffers */
+ __u32 fs_maxcluster;
+ __u32 fs_cpc; /* cyl per cycle in postbl */
+ __u16 fs_opostbl[16][8]; /* old rotation block list head */
+ union {
+ struct {
+ __s32 fs_sparecon[53];/* reserved for future constants */
+ __s32 fs_reclaim;
+ __s32 fs_sparecon2[1];
+ __s32 fs_state; /* file system state time stamp */
+ __u32 fs_qbmask[2]; /* ~usb_bmask */
+ __u32 fs_qfmask[2]; /* ~usb_fmask */
+ } fs_sun;
+ struct {
+ __s32 fs_sparecon[53];/* reserved for future constants */
+ __s32 fs_reclaim;
+ __s32 fs_sparecon2[1];
+ __u32 fs_npsect; /* # sectors/track including spares */
+ __u32 fs_qbmask[2]; /* ~usb_bmask */
+ __u32 fs_qfmask[2]; /* ~usb_fmask */
+ } fs_sunx86;
+ struct {
+ __s32 fs_sparecon[50];/* reserved for future constants */
+ __s32 fs_contigsumsize;/* size of cluster summary array */
+ __s32 fs_maxsymlinklen;/* max length of an internal symlink */
+ __s32 fs_inodefmt; /* format of on-disk inodes */
+ __u32 fs_maxfilesize[2]; /* max representable file size */
+ __u32 fs_qbmask[2]; /* ~usb_bmask */
+ __u32 fs_qfmask[2]; /* ~usb_fmask */
+ __s32 fs_state; /* file system state time stamp */
+ } fs_44;
+ } fs_u2;
+ __s32 fs_postblformat; /* format of positional layout tables */
+ __s32 fs_nrpos; /* number of rotational positions */
+ __s32 fs_postbloff; /* (__s16) rotation block list head */
+ __s32 fs_rotbloff; /* (__u8) blocks for each rotation */
+ __s32 fs_magic; /* magic number */
+ __u8 fs_space[1]; /* list of blocks for each rotation */
+};
+
+/*
+ * Preference for optimization.
+ */
+#define UFS_OPTTIME 0 /* minimize allocation time */
+#define UFS_OPTSPACE 1 /* minimize disk fragmentation */
+
+/*
+ * Rotational layout table format types
+ */
+#define UFS_42POSTBLFMT -1 /* 4.2BSD rotational table format */
+#define UFS_DYNAMICPOSTBLFMT 1 /* dynamic rotational table format */
+
+/*
+ * Convert cylinder group to base address of its global summary info.
+ */
+#define fs_cs(indx) \
+ u.ufs_sb.s_csp[(indx) >> uspi->s_csshift][(indx) & ~uspi->s_csmask]
+
+/*
+ * Cylinder group block for a file system.
+ *
+ * Writable fields in the cylinder group are protected by the associated
+ * super block lock fs->fs_lock.
+ */
+#define CG_MAGIC 0x090255
+#define ufs_cg_chkmagic(ucg) (SWAB32((ucg)->cg_magic) == CG_MAGIC)
+
+/*
+ * size of this structure is 172 B
+ */
+struct ufs_cylinder_group {
+ __u32 cg_link; /* linked list of cyl groups */
+ __u32 cg_magic; /* magic number */
+ __u32 cg_time; /* time last written */
+ __u32 cg_cgx; /* we are the cgx'th cylinder group */
+ __u16 cg_ncyl; /* number of cyl's this cg */
+ __u16 cg_niblk; /* number of inode blocks this cg */
+ __u32 cg_ndblk; /* number of data blocks this cg */
+ struct ufs_csum cg_cs; /* cylinder summary information */
+ __u32 cg_rotor; /* position of last used block */
+ __u32 cg_frotor; /* position of last used frag */
+ __u32 cg_irotor; /* position of last used inode */
+ __u32 cg_frsum[UFS_MAXFRAG]; /* counts of available frags */
+ __u32 cg_btotoff; /* (__u32) block totals per cylinder */
+ __u32 cg_boff; /* (short) free block positions */
+ __u32 cg_iusedoff; /* (char) used inode map */
+ __u32 cg_freeoff; /* (u_char) free block map */
+ __u32 cg_nextfreeoff; /* (u_char) next available space */
+ union {
+ struct {
+ __u32 cg_clustersumoff; /* (u_int32) counts of avail clusters */
+ __u32 cg_clusteroff; /* (u_int8) free cluster map */
+ __u32 cg_nclusterblks; /* number of clusters this cg */
+ __u32 cg_sparecon[13]; /* reserved for future use */
+ } cg_44;
+ __u32 cg_sparecon[16]; /* reserved for future use */
+ } cg_u;
+ __u8 cg_space[1]; /* space for cylinder group maps */
+/* actually longer */
+};
+
+/*
+ * structure of an on-disk inode
+ */
+struct ufs_inode {
+ __u16 ui_mode; /* 0x0 */
+ __u16 ui_nlink; /* 0x2 */
+ union {
+ struct {
+ __u16 ui_suid; /* 0x4 */
+ __u16 ui_sgid; /* 0x6 */
+ } oldids;
+ __u32 ui_inumber; /* 0x4 lsf: inode number */
+ __u32 ui_author; /* 0x4 GNU HURD: author */
+ } ui_u1;
+ __u64 ui_size; /* 0x8 */
+ struct ufs_timeval ui_atime; /* 0x10 access */
+ struct ufs_timeval ui_mtime; /* 0x18 modification */
+ struct ufs_timeval ui_ctime; /* 0x20 creation */
+ union {
+ struct {
+ __u32 ui_db[UFS_NDADDR];/* 0x28 data blocks */
+ __u32 ui_ib[UFS_NINDIR];/* 0x58 indirect blocks */
+ } ui_addr;
+ __u8 ui_symlink[4*(UFS_NDADDR+UFS_NINDIR)];/* 0x28 fast symlink */
+ } ui_u2;
+ __u32 ui_flags; /* 0x64 immutable, append-only... */
+ __u32 ui_blocks; /* 0x68 blocks in use */
+ __u32 ui_gen; /* 0x6c like ext2 i_version, for NFS support */
+ union {
+ struct {
+ __u32 ui_shadow; /* 0x70 shadow inode with security data */
+ __u32 ui_uid; /* 0x74 long EFT version of uid */
+ __u32 ui_gid; /* 0x78 long EFT version of gid */
+ __u32 ui_oeftflag; /* 0x7c reserved */
+ } ui_sun;
+ struct {
+ __u32 ui_uid; /* 0x70 File owner */
+ __u32 ui_gid; /* 0x74 File group */
+ __s32 ui_spare[2]; /* 0x78 reserved */
+ } ui_44;
+ struct {
+ __u32 ui_uid; /* 0x70 */
+ __u32 ui_gid; /* 0x74 */
+ __u16 ui_modeh; /* 0x78 mode high bits */
+ __u16 ui_spare; /* 0x7A unused */
+ __u32 ui_trans; /* 0x7c filesystem translator */
+ } ui_hurd;
+ } ui_u3;
+};
+
+/* FreeBSD has these in sys/stat.h */
+/* ui_flags that can be set by a file owner */
+#define UFS_UF_SETTABLE 0x0000ffff
+#define UFS_UF_NODUMP 0x00000001 /* do not dump */
+#define UFS_UF_IMMUTABLE 0x00000002 /* immutable (can't "change") */
+#define UFS_UF_APPEND 0x00000004 /* append-only */
+#define UFS_UF_OPAQUE 0x00000008 /* directory is opaque (unionfs) */
+#define UFS_UF_NOUNLINK 0x00000010 /* can't be removed or renamed */
+/* ui_flags that only root can set */
+#define UFS_SF_SETTABLE 0xffff0000
+#define UFS_SF_ARCHIVED 0x00010000 /* archived */
+#define UFS_SF_IMMUTABLE 0x00020000 /* immutable (can't "change") */
+#define UFS_SF_APPEND 0x00040000 /* append-only */
+#define UFS_SF_NOUNLINK 0x00100000 /* can't be removed or renamed */
+
+#ifdef __KERNEL__
+
+/* acl.c */
+extern int ufs_permission (struct inode *, int);
+
+/* balloc.c */
+extern void ufs_free_fragments (struct inode *, unsigned, unsigned);
+extern void ufs_free_blocks (struct inode *, unsigned, unsigned);
+extern unsigned ufs_new_fragments (struct inode *, u32 *, unsigned, unsigned, unsigned, int *);
+
+/* cylinder.c */
+extern struct ufs_cg_private_info * ufs_load_cylinder (struct super_block *, unsigned);
+extern void ufs_put_cylinder (struct super_block *, unsigned);
+
+/* dir.c */
+extern struct inode_operations ufs_dir_inode_operations;
+extern struct file_operations ufs_dir_operations;
+extern int ufs_check_dir_entry (const char *, struct inode *, struct ufs_dir_entry *, struct buffer_head *, unsigned long);
+
+/* file.c */
+extern struct inode_operations ufs_file_inode_operations;
+extern struct file_operations ufs_file_operations;
+
+/* ialloc.c */
+extern void ufs_free_inode (struct inode *inode);
+extern struct inode * ufs_new_inode (const struct inode *, int, int *);
+
+/* inode.c */
+extern int ufs_bmap (struct inode *, int);
+extern void ufs_read_inode (struct inode *);
+extern void ufs_put_inode (struct inode *);
+extern void ufs_write_inode (struct inode *);
+extern int ufs_sync_inode (struct inode *);
+extern void ufs_write_inode (struct inode *);
+extern void ufs_delete_inode (struct inode *);
+extern struct buffer_head * ufs_getfrag (struct inode *, unsigned, int, int *);
+extern struct buffer_head * ufs_bread (struct inode *, unsigned, int, int *);
+
+/* namei.c */
+extern struct dentry *ufs_lookup (struct inode *, struct dentry *);
+extern int ufs_mkdir(struct inode *, struct dentry *, int);
+extern int ufs_rmdir (struct inode *, struct dentry *);
+extern int ufs_unlink (struct inode *, struct dentry *);
+extern int ufs_create (struct inode *, struct dentry *, int);
+extern int ufs_rename (struct inode *, struct dentry *, struct inode *, struct dentry *);
+extern int ufs_mknod (struct inode *, struct dentry *, int, int);
+extern int ufs_symlink (struct inode *, struct dentry *, const char *);
+extern int ufs_link (struct dentry *, struct inode *, struct dentry *);
+
+/* super.c */
+extern struct super_operations ufs_super_ops;
+extern struct file_system_type ufs_fs_type;
+extern void ufs_warning (struct super_block *, const char *, const char *, ...) __attribute__ ((format (printf, 3, 4)));
+extern void ufs_error (struct super_block *, const char *, const char *, ...) __attribute__ ((format (printf, 3, 4)));
+extern void ufs_panic (struct super_block *, const char *, const char *, ...) __attribute__ ((format (printf, 3, 4)));
+extern int init_ufs_fs(void);
+extern void ufs_write_super (struct super_block *);
+
+/* symlink.c */
+extern struct inode_operations ufs_symlink_inode_operations;
+
+/* truncate.c */
+extern void ufs_truncate (struct inode *);
+
+#endif /* __KERNEL__ */
+
+#endif /* __LINUX_UFS_FS_H */
diff --git a/pfinet/linux-src/include/linux/ufs_fs_i.h b/pfinet/linux-src/include/linux/ufs_fs_i.h
new file mode 100644
index 00000000..df4c7575
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ufs_fs_i.h
@@ -0,0 +1,32 @@
+/*
+ * linux/include/linux/ufs_fs_i.h
+ *
+ * Copyright (C) 1996
+ * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
+ * Laboratory for Computer Science Research Computing Facility
+ * Rutgers, The State University of New Jersey
+ *
+ * NeXTstep support added on February 5th 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk>.
+ */
+
+#ifndef _LINUX_UFS_FS_I_H
+#define _LINUX_UFS_FS_I_H
+
+struct ufs_inode_info {
+ union {
+ __u32 i_data[15];
+ __u8 i_symlink[4*15];
+ } i_u1;
+ __u64 i_size;
+ __u32 i_flags;
+ __u32 i_gen;
+ __u32 i_shadow;
+ __u32 i_uid;
+ __u32 i_gid;
+ __u32 i_oeftflag;
+ __u16 i_osync;
+ __u32 i_lastfrag;
+};
+
+#endif /* _LINUX_UFS_FS_I_H */
diff --git a/pfinet/linux-src/include/linux/ufs_fs_sb.h b/pfinet/linux-src/include/linux/ufs_fs_sb.h
new file mode 100644
index 00000000..2fc49e99
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ufs_fs_sb.h
@@ -0,0 +1,245 @@
+/*
+ * linux/include/linux/ufs_fs_sb.h
+ *
+ * Copyright (C) 1996
+ * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
+ * Laboratory for Computer Science Research Computing Facility
+ * Rutgers, The State University of New Jersey
+ *
+ * $Id: ufs_fs_sb.h,v 1.8 1998/05/06 12:04:40 jj Exp $
+ *
+ * Write support by Daniel Pirkl <daniel.pirkl@email.cz>
+ */
+
+#ifndef __LINUX_UFS_FS_SB_H
+#define __LINUX_UFS_FS_SB_H
+
+#include <linux/ufs_fs.h>
+
+/*
+ * This structure is used for reading disk structures larger
+ * than the size of fragment.
+ */
+struct ufs_buffer_head {
+ unsigned fragment; /* first fragment */
+ unsigned count; /* number of fragments */
+ struct buffer_head * bh[UFS_MAXFRAG]; /* buffers */
+};
+
+struct ufs_cg_private_info {
+ struct ufs_cylinder_group ucg;
+ __u32 c_cgx; /* number of cylidner group */
+ __u16 c_ncyl; /* number of cyl's this cg */
+ __u16 c_niblk; /* number of inode blocks this cg */
+ __u32 c_ndblk; /* number of data blocks this cg */
+ __u32 c_rotor; /* position of last used block */
+ __u32 c_frotor; /* position of last used frag */
+ __u32 c_irotor; /* position of last used inode */
+ __u32 c_btotoff; /* (__u32) block totals per cylinder */
+ __u32 c_boff; /* (short) free block positions */
+ __u32 c_iusedoff; /* (char) used inode map */
+ __u32 c_freeoff; /* (u_char) free block map */
+ __u32 c_nextfreeoff; /* (u_char) next available space */
+ __u32 c_clustersumoff;/* (u_int32) counts of avail clusters */
+ __u32 c_clusteroff; /* (u_int8) free cluster map */
+ __u32 c_nclusterblks; /* number of clusters this cg */
+};
+
+struct ufs_sb_private_info {
+ struct ufs_buffer_head s_ubh; /* buffer containing super block */
+ __u32 s_sblkno; /* offset of super-blocks in filesys */
+ __u32 s_cblkno; /* offset of cg-block in filesys */
+ __u32 s_iblkno; /* offset of inode-blocks in filesys */
+ __u32 s_dblkno; /* offset of first data after cg */
+ __u32 s_cgoffset; /* cylinder group offset in cylinder */
+ __u32 s_cgmask; /* used to calc mod fs_ntrak */
+ __u32 s_size; /* number of blocks (fragments) in fs */
+ __u32 s_dsize; /* number of data blocks in fs */
+ __u32 s_ncg; /* number of cylinder groups */
+ __u32 s_bsize; /* size of basic blocks */
+ __u32 s_fsize; /* size of fragments */
+ __u32 s_fpb; /* fragments per block */
+ __u32 s_minfree; /* minimum percentage of free blocks */
+ __u32 s_bmask; /* `blkoff'' calc of blk offsets */
+ __u32 s_fmask; /* s_fsize mask */
+ __u32 s_bshift; /* `lblkno'' calc of logical blkno */
+ __u32 s_fshift; /* s_fsize shift */
+ __u32 s_fpbshift; /* fragments per block shift */
+ __u32 s_fsbtodb; /* fsbtodb and dbtofsb shift constant */
+ __u32 s_sbsize; /* actual size of super block */
+ __u32 s_csmask; /* csum block offset */
+ __u32 s_csshift; /* csum block number */
+ __u32 s_nindir; /* value of NINDIR */
+ __u32 s_inopb; /* value of INOPB */
+ __u32 s_nspf; /* value of NSPF */
+ __u32 s_npsect; /* # sectors/track including spares */
+ __u32 s_interleave; /* hardware sector interleave */
+ __u32 s_trackskew; /* sector 0 skew, per track */
+ __u32 s_csaddr; /* blk addr of cyl grp summary area */
+ __u32 s_cssize; /* size of cyl grp summary area */
+ __u32 s_cgsize; /* cylinder group size */
+ __u32 s_ntrak; /* tracks per cylinder */
+ __u32 s_nsect; /* sectors per track */
+ __u32 s_spc; /* sectors per cylinder */
+ __u32 s_ipg; /* inodes per group */
+ __u32 s_fpg; /* fragments per group */
+ __u32 s_cpc; /* cyl per cycle in postbl */
+ __s32 s_contigsumsize;/* size of cluster summary array, 44bsd */
+ __s64 s_qbmask; /* ~usb_bmask */
+ __s64 s_qfmask; /* ~usb_fmask */
+ __s32 s_postblformat; /* format of positional layout tables */
+ __s32 s_nrpos; /* number of rotational positions */
+ __s32 s_postbloff; /* (__s16) rotation block list head */
+ __s32 s_rotbloff; /* (__u8) blocks for each rotation */
+
+ __u32 s_fpbmask; /* fragments per block mask */
+ __u32 s_apb; /* address per block */
+ __u32 s_2apb; /* address per block^2 */
+ __u32 s_3apb; /* address per block^3 */
+ __u32 s_apbmask; /* address per block mask */
+ __u32 s_apbshift; /* address per block shift */
+ __u32 s_2apbshift; /* address per block shift * 2 */
+ __u32 s_3apbshift; /* address per block shift * 3 */
+ __u32 s_nspfshift; /* number of sector per fragment shift */
+ __u32 s_nspb; /* number of sector per block */
+ __u32 s_inopf; /* inodes per fragment */
+ __u32 s_sbbase; /* offset of NeXTstep superblock */
+ __u32 s_bpf; /* bits per fragment */
+ __u32 s_bpfshift; /* bits per fragment shift*/
+ __u32 s_bpfmask; /* bits per fragment mask */
+};
+
+
+#define UFS_MAX_GROUP_LOADED 8
+#define UFS_CGNO_EMPTY ((unsigned)-1)
+
+struct ufs_sb_info {
+ struct ufs_sb_private_info * s_uspi;
+ struct ufs_csum * s_csp[UFS_MAXCSBUFS];
+ unsigned s_swab;
+ unsigned s_flags;
+ struct buffer_head ** s_ucg;
+ struct ufs_cg_private_info * s_ucpi[UFS_MAX_GROUP_LOADED];
+ unsigned s_cgno[UFS_MAX_GROUP_LOADED];
+ unsigned short s_cg_loaded;
+ unsigned s_mount_opt;
+};
+
+/*
+ * Sizes of this structures are:
+ * ufs_super_block_first 512
+ * ufs_super_block_second 512
+ * ufs_super_block_third 356
+ */
+struct ufs_super_block_first {
+ __u32 fs_link;
+ __u32 fs_rlink;
+ __u32 fs_sblkno;
+ __u32 fs_cblkno;
+ __u32 fs_iblkno;
+ __u32 fs_dblkno;
+ __u32 fs_cgoffset;
+ __u32 fs_cgmask;
+ __u32 fs_time;
+ __u32 fs_size;
+ __u32 fs_dsize;
+ __u32 fs_ncg;
+ __u32 fs_bsize;
+ __u32 fs_fsize;
+ __u32 fs_frag;
+ __u32 fs_minfree;
+ __u32 fs_rotdelay;
+ __u32 fs_rps;
+ __u32 fs_bmask;
+ __u32 fs_fmask;
+ __u32 fs_bshift;
+ __u32 fs_fshift;
+ __u32 fs_maxcontig;
+ __u32 fs_maxbpg;
+ __u32 fs_fragshift;
+ __u32 fs_fsbtodb;
+ __u32 fs_sbsize;
+ __u32 fs_csmask;
+ __u32 fs_csshift;
+ __u32 fs_nindir;
+ __u32 fs_inopb;
+ __u32 fs_nspf;
+ __u32 fs_optim;
+ union {
+ struct {
+ __u32 fs_npsect;
+ } fs_sun;
+ struct {
+ __s32 fs_state;
+ } fs_sunx86;
+ } fs_u1;
+ __u32 fs_interleave;
+ __u32 fs_trackskew;
+ __u32 fs_id[2];
+ __u32 fs_csaddr;
+ __u32 fs_cssize;
+ __u32 fs_cgsize;
+ __u32 fs_ntrak;
+ __u32 fs_nsect;
+ __u32 fs_spc;
+ __u32 fs_ncyl;
+ __u32 fs_cpg;
+ __u32 fs_ipg;
+ __u32 fs_fpg;
+ struct ufs_csum fs_cstotal;
+ __s8 fs_fmod;
+ __s8 fs_clean;
+ __s8 fs_ronly;
+ __s8 fs_flags;
+ __s8 fs_fsmnt[UFS_MAXMNTLEN - 212];
+
+};
+
+struct ufs_super_block_second {
+ __s8 fs_fsmnt[212];
+ __u32 fs_cgrotor;
+ __u32 fs_csp[UFS_MAXCSBUFS];
+ __u32 fs_maxcluster;
+ __u32 fs_cpc;
+ __u16 fs_opostbl[82];
+};
+
+struct ufs_super_block_third {
+ __u16 fs_opostbl[46];
+ union {
+ struct {
+ __s32 fs_sparecon[53];/* reserved for future constants */
+ __s32 fs_reclaim;
+ __s32 fs_sparecon2[1];
+ __s32 fs_state; /* file system state time stamp */
+ __u32 fs_qbmask[2]; /* ~usb_bmask */
+ __u32 fs_qfmask[2]; /* ~usb_fmask */
+ } fs_sun;
+ struct {
+ __s32 fs_sparecon[53];/* reserved for future constants */
+ __s32 fs_reclaim;
+ __s32 fs_sparecon2[1];
+ __u32 fs_npsect; /* # sectors/track including spares */
+ __u32 fs_qbmask[2]; /* ~usb_bmask */
+ __u32 fs_qfmask[2]; /* ~usb_fmask */
+ } fs_sunx86;
+ struct {
+ __s32 fs_sparecon[50];/* reserved for future constants */
+ __s32 fs_contigsumsize;/* size of cluster summary array */
+ __s32 fs_maxsymlinklen;/* max length of an internal symlink */
+ __s32 fs_inodefmt; /* format of on-disk inodes */
+ __u32 fs_maxfilesize[2]; /* max representable file size */
+ __u32 fs_qbmask[2]; /* ~usb_bmask */
+ __u32 fs_qfmask[2]; /* ~usb_fmask */
+ __s32 fs_state; /* file system state time stamp */
+ } fs_44;
+ } fs_u2;
+ __s32 fs_postblformat;
+ __s32 fs_nrpos;
+ __s32 fs_postbloff;
+ __s32 fs_rotbloff;
+ __s32 fs_magic;
+ __u8 fs_space[1];
+};
+
+#endif /* __LINUX_UFS_FS_SB_H */
diff --git a/pfinet/linux-src/include/linux/uio.h b/pfinet/linux-src/include/linux/uio.h
new file mode 100644
index 00000000..beaafffd
--- /dev/null
+++ b/pfinet/linux-src/include/linux/uio.h
@@ -0,0 +1,37 @@
+#ifndef __LINUX_UIO_H
+#define __LINUX_UIO_H
+
+#include <linux/types.h>
+
+/*
+ * Berkeley style UIO structures - Alan Cox 1994.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+/* A word of warning: Our uio structure will clash with the C library one (which is now obsolete). Remove the C
+ library one from sys/uio.h if you have a very old library set */
+
+struct iovec
+{
+ void *iov_base; /* BSD uses caddr_t (1003.1g requires void *) */
+ __kernel_size_t iov_len; /* Must be size_t (1003.1g) */
+};
+
+/*
+ * UIO_MAXIOV shall be at least 16 1003.1g (5.4.1.1)
+ */
+
+#define UIO_FASTIOV 8
+#define UIO_MAXIOV 1024
+#if 0
+#define UIO_MAXIOV 16 /* Maximum iovec's in one operation
+ 16 matches BSD */
+ /* Beg pardon: BSD has 1024 --ANK */
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/ultrasound.h b/pfinet/linux-src/include/linux/ultrasound.h
new file mode 100644
index 00000000..6b7703e7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/ultrasound.h
@@ -0,0 +1,103 @@
+#ifndef _ULTRASOUND_H_
+#define _ULTRASOUND_H_
+/*
+ * ultrasound.h - Macros for programming the Gravis Ultrasound
+ * These macros are extremely device dependent
+ * and not portable.
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
+ *
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+
+
+/*
+ * Private events for Gravis Ultrasound (GUS)
+ *
+ * Format:
+ * byte 0 - SEQ_PRIVATE (0xfe)
+ * byte 1 - Synthesizer device number (0-N)
+ * byte 2 - Command (see below)
+ * byte 3 - Voice number (0-31)
+ * bytes 4 and 5 - parameter P1 (unsigned short)
+ * bytes 6 and 7 - parameter P2 (unsigned short)
+ *
+ * Commands:
+ * Each command affects one voice defined in byte 3.
+ * Unused parameters (P1 and/or P2 *MUST* be initialized to zero).
+ * _GUS_NUMVOICES - Sets max. number of concurrent voices (P1=14-31, default 16)
+ * _GUS_VOICESAMPLE- ************ OBSOLETE *************
+ * _GUS_VOICEON - Starts voice (P1=voice mode)
+ * _GUS_VOICEOFF - Stops voice (no parameters)
+ * _GUS_VOICEFADE - Stops the voice smoothly.
+ * _GUS_VOICEMODE - Alters the voice mode, don't start or stop voice (P1=voice mode)
+ * _GUS_VOICEBALA - Sets voice balence (P1, 0=left, 7=middle and 15=right, default 7)
+ * _GUS_VOICEFREQ - Sets voice (sample) playback frequency (P1=Hz)
+ * _GUS_VOICEVOL - Sets voice volume (P1=volume, 0xfff=max, 0xeff=half, 0x000=off)
+ * _GUS_VOICEVOL2 - Sets voice volume (P1=volume, 0xfff=max, 0xeff=half, 0x000=off)
+ * (Like GUS_VOICEVOL but doesn't change the hw
+ * volume. It just updates volume in the voice table).
+ *
+ * _GUS_RAMPRANGE - Sets limits for volume ramping (P1=low volume, P2=high volume)
+ * _GUS_RAMPRATE - Sets the speed for volume ramping (P1=scale, P2=rate)
+ * _GUS_RAMPMODE - Sets the volume ramping mode (P1=ramping mode)
+ * _GUS_RAMPON - Starts volume ramping (no parameters)
+ * _GUS_RAMPOFF - Stops volume ramping (no parameters)
+ * _GUS_VOLUME_SCALE - Changes the volume calculation constants
+ * for all voices.
+ */
+
+#define _GUS_NUMVOICES 0x00
+#define _GUS_VOICESAMPLE 0x01 /* OBSOLETE */
+#define _GUS_VOICEON 0x02
+#define _GUS_VOICEOFF 0x03
+#define _GUS_VOICEMODE 0x04
+#define _GUS_VOICEBALA 0x05
+#define _GUS_VOICEFREQ 0x06
+#define _GUS_VOICEVOL 0x07
+#define _GUS_RAMPRANGE 0x08
+#define _GUS_RAMPRATE 0x09
+#define _GUS_RAMPMODE 0x0a
+#define _GUS_RAMPON 0x0b
+#define _GUS_RAMPOFF 0x0c
+#define _GUS_VOICEFADE 0x0d
+#define _GUS_VOLUME_SCALE 0x0e
+#define _GUS_VOICEVOL2 0x0f
+#define _GUS_VOICE_POS 0x10
+
+/*
+ * GUS API macros
+ */
+
+#define _GUS_CMD(chn, voice, cmd, p1, p2) \
+ {_SEQ_NEEDBUF(8); _seqbuf[_seqbufptr] = SEQ_PRIVATE;\
+ _seqbuf[_seqbufptr+1] = (chn); _seqbuf[_seqbufptr+2] = cmd;\
+ _seqbuf[_seqbufptr+3] = voice;\
+ *(unsigned short*)&_seqbuf[_seqbufptr+4] = p1;\
+ *(unsigned short*)&_seqbuf[_seqbufptr+6] = p2;\
+ _SEQ_ADVBUF(8);}
+
+#define GUS_NUMVOICES(chn, p1) _GUS_CMD(chn, 0, _GUS_NUMVOICES, (p1), 0)
+#define GUS_VOICESAMPLE(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICESAMPLE, (p1), 0) /* OBSOLETE */
+#define GUS_VOICEON(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICEON, (p1), 0)
+#define GUS_VOICEOFF(chn, voice) _GUS_CMD(chn, voice, _GUS_VOICEOFF, 0, 0)
+#define GUS_VOICEFADE(chn, voice) _GUS_CMD(chn, voice, _GUS_VOICEFADE, 0, 0)
+#define GUS_VOICEMODE(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICEMODE, (p1), 0)
+#define GUS_VOICEBALA(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICEBALA, (p1), 0)
+#define GUS_VOICEFREQ(chn, voice, p) _GUS_CMD(chn, voice, _GUS_VOICEFREQ, \
+ (p) & 0xffff, ((p) >> 16) & 0xffff)
+#define GUS_VOICEVOL(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICEVOL, (p1), 0)
+#define GUS_VOICEVOL2(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_VOICEVOL2, (p1), 0)
+#define GUS_RAMPRANGE(chn, voice, low, high) _GUS_CMD(chn, voice, _GUS_RAMPRANGE, (low), (high))
+#define GUS_RAMPRATE(chn, voice, p1, p2) _GUS_CMD(chn, voice, _GUS_RAMPRATE, (p1), (p2))
+#define GUS_RAMPMODE(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_RAMPMODE, (p1), 0)
+#define GUS_RAMPON(chn, voice, p1) _GUS_CMD(chn, voice, _GUS_RAMPON, (p1), 0)
+#define GUS_RAMPOFF(chn, voice) _GUS_CMD(chn, voice, _GUS_RAMPOFF, 0, 0)
+#define GUS_VOLUME_SCALE(chn, voice, p1, p2) _GUS_CMD(chn, voice, _GUS_VOLUME_SCALE, (p1), (p2))
+#define GUS_VOICE_POS(chn, voice, p) _GUS_CMD(chn, voice, _GUS_VOICE_POS, \
+ (p) & 0xffff, ((p) >> 16) & 0xffff)
+
+#endif
diff --git a/pfinet/linux-src/include/linux/umsdos_fs.h b/pfinet/linux-src/include/linux/umsdos_fs.h
new file mode 100644
index 00000000..14bdb829
--- /dev/null
+++ b/pfinet/linux-src/include/linux/umsdos_fs.h
@@ -0,0 +1,188 @@
+#ifndef LINUX_UMSDOS_FS_H
+#define LINUX_UMSDOS_FS_H
+
+
+#define UMS_DEBUG 1 /* define for check_* functions */
+/*#define UMSDOS_DEBUG 1*/
+#define UMSDOS_PARANOIA 1
+
+#define UMSDOS_VERSION 0
+#define UMSDOS_RELEASE 4
+
+#define UMSDOS_ROOT_INO 1
+
+/* This is the file acting as a directory extension */
+#define UMSDOS_EMD_FILE "--linux-.---"
+#define UMSDOS_EMD_NAMELEN 12
+#define UMSDOS_PSDROOT_NAME "linux"
+#define UMSDOS_PSDROOT_LEN 5
+
+#ifndef _LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+#ifndef _LINUX_LIMITS_H
+#include <linux/limits.h>
+#endif
+#ifndef _LINUX_DIRENT_H
+#include <linux/dirent.h>
+#endif
+#ifndef _LINUX_IOCTL_H
+#include <linux/ioctl.h>
+#endif
+
+
+#ifdef __KERNEL__
+/* #Specification: convention / PRINTK Printk and printk
+ * Here is the convention for the use of printk inside fs/umsdos
+ *
+ * printk carry important message (error or status).
+ * Printk is for debugging (it is a macro defined at the beginning of
+ * most source.
+ * PRINTK is a nulled Printk macro.
+ *
+ * This convention makes the source easier to read, and Printk easier
+ * to shut off.
+ */
+# define PRINTK(x)
+# ifdef UMSDOS_DEBUG
+# define Printk(x) printk x
+# else
+# define Printk(x)
+# endif
+#endif
+
+
+struct umsdos_fake_info {
+ char fname[13];
+ int len;
+};
+
+#define UMSDOS_MAXNAME 220
+/* This structure is 256 bytes large, depending on the name, only part */
+/* of it is written to disk */
+/* nice though it would be, I can't change this and preserve backward compatibility */
+struct umsdos_dirent {
+ unsigned char name_len; /* if == 0, then this entry is not used */
+ unsigned char flags; /* UMSDOS_xxxx */
+ unsigned short nlink; /* How many hard links point to this entry */
+ uid_t uid; /* Owner user id */
+ gid_t gid; /* Group id */
+ time_t atime; /* Access time */
+ time_t mtime; /* Last modification time */
+ time_t ctime; /* Creation time */
+ dev_t rdev; /* major and minor number of a device */
+ /* special file */
+ umode_t mode; /* Standard UNIX permissions bits + type of */
+ char spare[12]; /* unused bytes for future extensions */
+ /* file, see linux/stat.h */
+ char name[UMSDOS_MAXNAME]; /* Not '\0' terminated */
+ /* but '\0' padded, so it will allow */
+ /* for adding news fields in this record */
+ /* by reducing the size of name[] */
+};
+
+#define UMSDOS_HIDDEN 1 /* Never show this entry in directory search */
+#define UMSDOS_HLINK 2 /* It is a (pseudo) hard link */
+
+/* #Specification: EMD file / record size
+ * Entry are 64 bytes wide in the EMD file. It allows for a 30 characters
+ * name. If a name is longer, contiguous entries are allocated. So a
+ * umsdos_dirent may span multiple records.
+ */
+
+#define UMSDOS_REC_SIZE 64
+
+/* Translation between MSDOS name and UMSDOS name */
+
+struct umsdos_info {
+ int msdos_reject; /* Tell if the file name is invalid for MSDOS */
+ /* See umsdos_parse */
+ struct umsdos_fake_info fake;
+ struct umsdos_dirent entry;
+ off_t f_pos; /* offset of the entry in the EMD file
+ * or offset where the entry may be store
+ * if it is a new entry
+ */
+ int recsize; /* Record size needed to store entry */
+};
+
+/* Definitions for ioctl (number randomly chosen)
+ * The next ioctl commands operate only on the DOS directory
+ * The file umsdos_progs/umsdosio.c contain a string table
+ * based on the order of those definition. Keep it in sync
+ */
+#define UMSDOS_READDIR_DOS _IO(0x04,210) /* Do a readdir of the DOS directory */
+#define UMSDOS_UNLINK_DOS _IO(0x04,211) /* Erase in the DOS directory only */
+#define UMSDOS_RMDIR_DOS _IO(0x04,212) /* rmdir in the DOS directory only */
+#define UMSDOS_STAT_DOS _IO(0x04,213) /* Get info about a file */
+
+/* The next ioctl commands operate only on the EMD file */
+#define UMSDOS_CREAT_EMD _IO(0x04,214) /* Create a file */
+#define UMSDOS_UNLINK_EMD _IO(0x04,215) /* unlink (rmdir) a file */
+#define UMSDOS_READDIR_EMD _IO(0x04,216) /* read the EMD file only. */
+#define UMSDOS_GETVERSION _IO(0x04,217) /* Get the release number of UMSDOS */
+#define UMSDOS_INIT_EMD _IO(0x04,218) /* Create the EMD file if not there */
+#define UMSDOS_DOS_SETUP _IO(0x04,219) /* Set the defaults of the MS-DOS driver. */
+
+#define UMSDOS_RENAME_DOS _IO(0x04,220) /* rename a file/directory in the DOS
+ * directory only */
+struct umsdos_ioctl {
+ struct dirent dos_dirent;
+ struct umsdos_dirent umsdos_dirent;
+ /* The following structure is used to exchange some data
+ * with utilities (umsdos_progs/util/umsdosio.c). The first
+ * releases were using struct stat from "sys/stat.h". This was
+ * causing some problem for cross compilation of the kernel
+ * Since I am not really using the structure stat, but only some field
+ * of it, I have decided to replicate the structure here
+ * for compatibility with the binaries out there
+ * FIXME PTW 1998, this has probably changed
+ */
+
+ struct {
+ dev_t st_dev;
+ unsigned short __pad1;
+ ino_t st_ino;
+ umode_t st_mode;
+ nlink_t st_nlink;
+ uid_t st_uid;
+ gid_t st_gid;
+ dev_t st_rdev;
+ unsigned short __pad2;
+ off_t st_size;
+ unsigned long st_blksize;
+ unsigned long st_blocks;
+ time_t st_atime;
+ unsigned long __unused1;
+ time_t st_mtime;
+ unsigned long __unused2;
+ time_t st_ctime;
+ unsigned long __unused3;
+ unsigned long __unused4;
+ unsigned long __unused5;
+ } stat;
+ char version, release;
+};
+
+/* Different macros to access struct umsdos_dirent */
+#define EDM_ENTRY_ISUSED(e) ((e)->name_len!=0)
+
+#ifdef __KERNEL__
+
+#ifndef LINUX_FS_H
+#include <linux/fs.h>
+#endif
+
+extern struct inode_operations umsdos_dir_inode_operations;
+extern struct file_operations umsdos_file_operations;
+extern struct inode_operations umsdos_file_inode_operations;
+extern struct inode_operations umsdos_file_inode_operations_no_bmap;
+extern struct inode_operations umsdos_file_inode_operations_readpage;
+extern struct inode_operations umsdos_symlink_inode_operations;
+extern int init_umsdos_fs (void);
+
+#include <linux/umsdos_fs.p>
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/umsdos_fs.p b/pfinet/linux-src/include/linux/umsdos_fs.p
new file mode 100644
index 00000000..de436f0e
--- /dev/null
+++ b/pfinet/linux-src/include/linux/umsdos_fs.p
@@ -0,0 +1,118 @@
+/* check.c 23/01/95 03.38.30 */
+void check_page_tables (void);
+
+/* dir.c 22/06/95 00.22.12 */
+int dummy_dir_read ( struct file *filp,
+ char *buf,
+ size_t size,
+ loff_t *count);
+char * umsdos_d_path(struct dentry *, char *, int);
+void umsdos_lookup_patch_new(struct dentry *, struct umsdos_info *);
+int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry);
+struct dentry *umsdos_lookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo);
+struct dentry *UMSDOS_lookup(struct inode *, struct dentry *);
+struct dentry *umsdos_lookup_dentry(struct dentry *, char *, int, int);
+struct dentry *umsdos_covered(struct dentry *, char *, int);
+
+struct dentry *umsdos_solve_hlink (struct dentry *hlink);
+
+/* emd.c 22/06/95 00.22.04 */
+ssize_t umsdos_file_write_kmem_real (struct file *filp,
+ const char *buf,
+ size_t count);
+
+ssize_t umsdos_file_read_kmem (struct file *filp,
+ char *buf,
+ size_t count);
+ssize_t umsdos_file_write_kmem (struct file *filp,
+ const char *buf,
+ size_t count);
+ssize_t umsdos_emd_dir_write (struct file *filp,
+ char *buf,
+ size_t count);
+ssize_t umsdos_emd_dir_read (struct file *filp,
+ char *buf,
+ size_t count);
+struct dentry *umsdos_get_emd_dentry(struct dentry *);
+int umsdos_have_emd(struct dentry *);
+int umsdos_make_emd(struct dentry *);
+int umsdos_emd_dir_readentry (struct file *, struct umsdos_dirent *);
+int umsdos_newentry (struct dentry *, struct umsdos_info *);
+int umsdos_newhidden (struct dentry *, struct umsdos_info *);
+int umsdos_delentry (struct dentry *, struct umsdos_info *, int);
+int umsdos_findentry (struct dentry *, struct umsdos_info *, int);
+int umsdos_isempty (struct dentry *);
+
+/* file.c 25/01/95 02.25.38 */
+
+/* inode.c 12/06/95 09.49.40 */
+void fill_new_filp (struct file *filp, struct dentry *dentry);
+void UMSDOS_read_inode (struct inode *);
+void UMSDOS_write_inode (struct inode *);
+int UMSDOS_notify_change (struct dentry *, struct iattr *attr);
+int umsdos_notify_change_locked(struct dentry *, struct iattr *attr);
+void UMSDOS_put_inode (struct inode *);
+int UMSDOS_statfs (struct super_block *, struct statfs *, int);
+struct super_block *UMSDOS_read_super (struct super_block *, void *, int);
+void UMSDOS_put_super (struct super_block *);
+
+void umsdos_setup_dir(struct dentry *);
+void umsdos_set_dirinfo_new(struct dentry *, off_t);
+void umsdos_patch_dentry_inode (struct dentry *, off_t);
+int umsdos_get_dirowner (struct inode *inode, struct inode **result);
+
+/* ioctl.c 22/06/95 00.22.08 */
+int UMSDOS_ioctl_dir (struct inode *dir,
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long data);
+
+/* mangle.c 25/01/95 02.25.38 */
+void umsdos_manglename (struct umsdos_info *info);
+int umsdos_evalrecsize (int len);
+int umsdos_parse (const char *name,int len, struct umsdos_info *info);
+
+/* namei.c 25/01/95 02.25.38 */
+void umsdos_lockcreate (struct inode *dir);
+void umsdos_startlookup (struct inode *dir);
+void umsdos_unlockcreate (struct inode *dir);
+void umsdos_endlookup (struct inode *dir);
+
+int umsdos_readlink_x ( struct dentry *dentry,
+ char *buffer,
+ int bufsiz);
+int UMSDOS_symlink (struct inode *dir,
+ struct dentry *dentry,
+ const char *symname);
+int UMSDOS_link (struct dentry *olddentry,
+ struct inode *dir,
+ struct dentry *dentry);
+int UMSDOS_create (struct inode *dir,
+ struct dentry *dentry,
+ int mode);
+
+int UMSDOS_mkdir (struct inode *dir,
+ struct dentry *dentry,
+ int mode);
+int UMSDOS_mknod (struct inode *dir,
+ struct dentry *dentry,
+ int mode,
+ int rdev);
+int UMSDOS_rmdir (struct inode *dir,struct dentry *dentry);
+int UMSDOS_unlink (struct inode *dir, struct dentry *dentry);
+int UMSDOS_rename (struct inode *old_dir,
+ struct dentry *old_dentry,
+ struct inode *new_dir,
+ struct dentry *new_dentry);
+
+/* rdir.c 22/03/95 03.31.42 */
+struct dentry *umsdos_rlookup_x (struct inode *dir, struct dentry *dentry, int nopseudo);
+struct dentry *UMSDOS_rlookup (struct inode *dir, struct dentry *dentry);
+
+/* symlink.c 23/01/95 03.38.30 */
+
+/* check.c */
+void checkd_inode (struct inode *inode);
+void check_inode (struct inode *inode);
+void check_dentry (struct dentry *dentry);
+void check_dentry_path (struct dentry *dentry, const char *desc);
diff --git a/pfinet/linux-src/include/linux/umsdos_fs_i.h b/pfinet/linux-src/include/linux/umsdos_fs_i.h
new file mode 100644
index 00000000..111fd913
--- /dev/null
+++ b/pfinet/linux-src/include/linux/umsdos_fs_i.h
@@ -0,0 +1,75 @@
+#ifndef UMSDOS_FS_I_H
+#define UMSDOS_FS_I_H
+
+#ifndef _LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+
+#include <linux/msdos_fs_i.h>
+#include <linux/pipe_fs_i.h>
+
+/* #Specification: strategy / in memory inode
+ * Here is the information specific to the inode of the UMSDOS file
+ * system. This information is added to the end of the standard struct
+ * inode. Each file system has its own extension to struct inode,
+ * so do the umsdos file system.
+ *
+ * The strategy is to have the umsdos_inode_info as a superset of
+ * the msdos_inode_info, since most of the time the job is done
+ * by the msdos fs code.
+ *
+ * So we duplicate the msdos_inode_info, and add our own info at the
+ * end.
+ *
+ * For all file type (and directory) the inode has a reference to:
+ * the directory which hold this entry: i_dir_owner
+ * The EMD file of i_dir_owner: i_emd_owner
+ * The offset in this EMD file of the entry: pos
+ *
+ * For directory, we also have a reference to the inode of its
+ * own EMD file. Also, we have dir_locking_info to help synchronise
+ * file creation and file lookup. This data is sharing space with
+ * the pipe_inode_info not used by directory. See also msdos_fs_i.h
+ * for more information about pipe_inode_info and msdos_inode_info.
+ *
+ * Special file and fifo do have an inode which correspond to an
+ * empty MSDOS file.
+ *
+ * symlink are processed mostly like regular file. The content is the
+ * link.
+ *
+ * fifos add there own extension to the inode. I have reserved some
+ * space for fifos side by side with msdos_inode_info. This is just
+ * to for the show, because msdos_inode_info already include the
+ * pipe_inode_info.
+ *
+ * The UMSDOS specific extension is placed after the union.
+ */
+
+struct dir_locking_info {
+ struct wait_queue *p;
+ short int looking; /* How many process doing a lookup */
+ short int creating; /* Is there any creation going on here
+ * Only one at a time, although one
+ * may recursively lock, so it is a counter
+ */
+ long pid; /* pid of the process owning the creation */
+ /* lock */
+};
+
+struct umsdos_inode_info {
+ union {
+ struct msdos_inode_info msdos_info;
+ struct pipe_inode_info pipe_info;
+ struct dir_locking_info dir_info;
+ } u;
+ int i_patched; /* Inode has been patched */
+ int i_is_hlink; /* Resolved hardlink inode? */
+ unsigned long i_emd_owner; /* Is this the EMD file inode? */
+ off_t pos; /* Entry offset in the emd_owner file */
+ /* The rest is used only if this inode describes a directory */
+ struct dentry *i_emd_dentry; /* EMD dentry for this directory */
+ unsigned long i_emd_dir; /* Inode of the EMD file */
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/un.h b/pfinet/linux-src/include/linux/un.h
new file mode 100644
index 00000000..45561c56
--- /dev/null
+++ b/pfinet/linux-src/include/linux/un.h
@@ -0,0 +1,11 @@
+#ifndef _LINUX_UN_H
+#define _LINUX_UN_H
+
+#define UNIX_PATH_MAX 108
+
+struct sockaddr_un {
+ sa_family_t sun_family; /* AF_UNIX */
+ char sun_path[UNIX_PATH_MAX]; /* pathname */
+};
+
+#endif /* _LINUX_UN_H */
diff --git a/pfinet/linux-src/include/linux/unistd.h b/pfinet/linux-src/include/linux/unistd.h
new file mode 100644
index 00000000..10ed9834
--- /dev/null
+++ b/pfinet/linux-src/include/linux/unistd.h
@@ -0,0 +1,11 @@
+#ifndef _LINUX_UNISTD_H_
+#define _LINUX_UNISTD_H_
+
+extern int errno;
+
+/*
+ * Include machine specific syscallX macros
+ */
+#include <asm/unistd.h>
+
+#endif /* _LINUX_UNISTD_H_ */
diff --git a/pfinet/linux-src/include/linux/user.h b/pfinet/linux-src/include/linux/user.h
new file mode 100644
index 00000000..68daf840
--- /dev/null
+++ b/pfinet/linux-src/include/linux/user.h
@@ -0,0 +1 @@
+#include <asm/user.h>
diff --git a/pfinet/linux-src/include/linux/utime.h b/pfinet/linux-src/include/linux/utime.h
new file mode 100644
index 00000000..c6bf27b7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/utime.h
@@ -0,0 +1,9 @@
+#ifndef _LINUX_UTIME_H
+#define _LINUX_UTIME_H
+
+struct utimbuf {
+ time_t actime;
+ time_t modtime;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/uts.h b/pfinet/linux-src/include/linux/uts.h
new file mode 100644
index 00000000..39e09ae1
--- /dev/null
+++ b/pfinet/linux-src/include/linux/uts.h
@@ -0,0 +1,23 @@
+#ifndef _LINUX_UTS_H
+#define _LINUX_UTS_H
+
+/*
+ * Defines for what uname() should return
+ */
+#ifndef UTS_SYSNAME
+#define UTS_SYSNAME "Linux"
+#endif
+
+#ifndef UTS_MACHINE
+#define UTS_MACHINE "unknown"
+#endif
+
+#ifndef UTS_NODENAME
+#define UTS_NODENAME "(none)" /* set by sethostname() */
+#endif
+
+#ifndef UTS_DOMAINNAME
+#define UTS_DOMAINNAME "(none)" /* set by setdomainname() */
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/utsname.h b/pfinet/linux-src/include/linux/utsname.h
new file mode 100644
index 00000000..a83503f9
--- /dev/null
+++ b/pfinet/linux-src/include/linux/utsname.h
@@ -0,0 +1,36 @@
+#ifndef _LINUX_UTSNAME_H
+#define _LINUX_UTSNAME_H
+
+#define __OLD_UTS_LEN 8
+
+struct oldold_utsname {
+ char sysname[9];
+ char nodename[9];
+ char release[9];
+ char version[9];
+ char machine[9];
+};
+
+#define __NEW_UTS_LEN 64
+
+struct old_utsname {
+ char sysname[65];
+ char nodename[65];
+ char release[65];
+ char version[65];
+ char machine[65];
+};
+
+struct new_utsname {
+ char sysname[65];
+ char nodename[65];
+ char release[65];
+ char version[65];
+ char machine[65];
+ char domainname[65];
+};
+
+extern struct new_utsname system_utsname;
+
+extern struct semaphore uts_sem;
+#endif
diff --git a/pfinet/linux-src/include/linux/vfs.h b/pfinet/linux-src/include/linux/vfs.h
new file mode 100644
index 00000000..b3a58657
--- /dev/null
+++ b/pfinet/linux-src/include/linux/vfs.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_VFS_H
+#define _LINUX_VFS_H
+
+#include <asm/statfs.h>
+
+#endif
diff --git a/pfinet/linux-src/include/linux/video_decoder.h b/pfinet/linux-src/include/linux/video_decoder.h
new file mode 100644
index 00000000..1302c7f4
--- /dev/null
+++ b/pfinet/linux-src/include/linux/video_decoder.h
@@ -0,0 +1,37 @@
+#ifndef _LINUX_VIDEO_DECODER_H
+#define _LINUX_VIDEO_DECODER_H
+
+struct video_decoder_capability { /* this name is too long */
+ __u32 flags;
+#define VIDEO_DECODER_PAL 1 /* can decode PAL signal */
+#define VIDEO_DECODER_NTSC 2 /* can decode NTSC */
+#define VIDEO_DECODER_SECAM 4 /* can decode SECAM */
+#define VIDEO_DECODER_AUTO 8 /* can autosense norm */
+#define VIDEO_DECODER_CCIR 16 /* CCIR-601 pixel rate (720 pixels per line) instead of square pixel rate */
+ int inputs; /* number of inputs */
+ int outputs; /* number of outputs */
+};
+
+/*
+DECODER_GET_STATUS returns the following flags. The only one you need is
+DECODER_STATUS_GOOD, the others are just nice things to know.
+*/
+#define DECODER_STATUS_GOOD 1 /* receiving acceptable input */
+#define DECODER_STATUS_COLOR 2 /* receiving color information */
+#define DECODER_STATUS_PAL 4 /* auto detected */
+#define DECODER_STATUS_NTSC 8 /* auto detected */
+#define DECODER_STATUS_SECAM 16 /* auto detected */
+
+
+#define DECODER_GET_CAPABILITIES _IOR('d', 1, struct video_decoder_capability)
+#define DECODER_GET_STATUS _IOR('d', 2, int)
+#define DECODER_SET_NORM _IOW('d', 3, int)
+#define DECODER_SET_INPUT _IOW('d', 4, int) /* 0 <= input < #inputs */
+#define DECODER_SET_OUTPUT _IOW('d', 5, int) /* 0 <= output < #outputs */
+#define DECODER_ENABLE_OUTPUT _IOW('d', 6, int) /* boolean output enable control */
+#define DECODER_SET_PICTURE _IOW('d', 7, struct video_picture)
+
+#define DECODER_DUMP _IO('d', 192) /* debug hook */
+
+
+#endif
diff --git a/pfinet/linux-src/include/linux/video_encoder.h b/pfinet/linux-src/include/linux/video_encoder.h
new file mode 100644
index 00000000..4b0e6907
--- /dev/null
+++ b/pfinet/linux-src/include/linux/video_encoder.h
@@ -0,0 +1,21 @@
+#ifndef _LINUX_VIDEO_ENCODER_H
+#define _LINUX_VIDEO_ENCODER_H
+
+struct video_encoder_capability { /* this name is too long */
+ __u32 flags;
+#define VIDEO_ENCODER_PAL 1 /* can encode PAL signal */
+#define VIDEO_ENCODER_NTSC 2 /* can encode NTSC */
+#define VIDEO_ENCODER_SECAM 4 /* can encode SECAM */
+#define VIDEO_ENCODER_CCIR 16 /* CCIR-601 pixel rate (720 pixels per line) instead of square pixel rate */
+ int inputs; /* number of inputs */
+ int outputs; /* number of outputs */
+};
+
+#define ENCODER_GET_CAPABILITIES _IOR('e', 1, struct video_encoder_capability)
+#define ENCODER_SET_NORM _IOW('e', 2, int)
+#define ENCODER_SET_INPUT _IOW('e', 3, int) /* 0 <= input < #inputs */
+#define ENCODER_SET_OUTPUT _IOW('e', 4, int) /* 0 <= output < #outputs */
+#define ENCODER_ENABLE_OUTPUT _IOW('e', 5, int) /* boolean output enable control */
+
+
+#endif
diff --git a/pfinet/linux-src/include/linux/videodev.h b/pfinet/linux-src/include/linux/videodev.h
new file mode 100644
index 00000000..b3427409
--- /dev/null
+++ b/pfinet/linux-src/include/linux/videodev.h
@@ -0,0 +1,293 @@
+#ifndef __LINUX_VIDEODEV_H
+#define __LINUX_VIDEODEV_H
+
+#include <linux/types.h>
+#include <linux/version.h>
+
+#ifdef __KERNEL__
+
+#if LINUX_VERSION_CODE >= 0x020100
+#include <linux/poll.h>
+#endif
+
+struct video_device
+{
+ char name[32];
+ int type;
+ int hardware;
+
+ int (*open)(struct video_device *, int mode);
+ void (*close)(struct video_device *);
+ long (*read)(struct video_device *, char *, unsigned long, int noblock);
+ /* Do we need a write method ? */
+ long (*write)(struct video_device *, const char *, unsigned long, int noblock);
+#if LINUX_VERSION_CODE >= 0x020100
+ unsigned int (*poll)(struct video_device *, struct file *, poll_table *);
+#endif
+ int (*ioctl)(struct video_device *, unsigned int , void *);
+ int (*mmap)(struct video_device *, const char *, unsigned long);
+ int (*initialize)(struct video_device *);
+ void *priv; /* Used to be 'private' but that upsets C++ */
+ int busy;
+ int minor;
+};
+
+extern int videodev_init(void);
+#define VIDEO_MAJOR 81
+extern int video_register_device(struct video_device *, int type);
+
+#define VFL_TYPE_GRABBER 0
+#define VFL_TYPE_VBI 1
+#define VFL_TYPE_RADIO 2
+#define VFL_TYPE_VTX 3
+
+extern void video_unregister_device(struct video_device *);
+#endif
+
+
+#define VID_TYPE_CAPTURE 1 /* Can capture */
+#define VID_TYPE_TUNER 2 /* Can tune */
+#define VID_TYPE_TELETEXT 4 /* Does teletext */
+#define VID_TYPE_OVERLAY 8 /* Overlay onto frame buffer */
+#define VID_TYPE_CHROMAKEY 16 /* Overlay by chromakey */
+#define VID_TYPE_CLIPPING 32 /* Can clip */
+#define VID_TYPE_FRAMERAM 64 /* Uses the frame buffer memory */
+#define VID_TYPE_SCALES 128 /* Scalable */
+#define VID_TYPE_MONOCHROME 256 /* Monochrome only */
+#define VID_TYPE_SUBCAPTURE 512 /* Can capture subareas of the image */
+
+struct video_capability
+{
+ char name[32];
+ int type;
+ int channels; /* Num channels */
+ int audios; /* Num audio devices */
+ int maxwidth; /* Supported width */
+ int maxheight; /* And height */
+ int minwidth; /* Supported width */
+ int minheight; /* And height */
+};
+
+
+struct video_channel
+{
+ int channel;
+ char name[32];
+ int tuners;
+ __u32 flags;
+#define VIDEO_VC_TUNER 1 /* Channel has a tuner */
+#define VIDEO_VC_AUDIO 2 /* Channel has audio */
+ __u16 type;
+#define VIDEO_TYPE_TV 1
+#define VIDEO_TYPE_CAMERA 2
+ __u16 norm; /* Norm set by channel */
+};
+
+struct video_tuner
+{
+ int tuner;
+ char name[32];
+ ulong rangelow, rangehigh; /* Tuner range */
+ __u32 flags;
+#define VIDEO_TUNER_PAL 1
+#define VIDEO_TUNER_NTSC 2
+#define VIDEO_TUNER_SECAM 4
+#define VIDEO_TUNER_LOW 8 /* Uses KHz not MHz */
+#define VIDEO_TUNER_NORM 16 /* Tuner can set norm */
+#define VIDEO_TUNER_STEREO_ON 128 /* Tuner is seeing stereo */
+#define VIDEO_TUNER_RDS_ON 256 /* Tuner is seeing an RDS datastream */
+#define VIDEO_TUNER_MBS_ON 512 /* Tuner is seeing an MBS datastream */
+ __u16 mode; /* PAL/NTSC/SECAM/OTHER */
+#define VIDEO_MODE_PAL 0
+#define VIDEO_MODE_NTSC 1
+#define VIDEO_MODE_SECAM 2
+#define VIDEO_MODE_AUTO 3
+ __u16 signal; /* Signal strength 16bit scale */
+};
+
+struct video_picture
+{
+ __u16 brightness;
+ __u16 hue;
+ __u16 colour;
+ __u16 contrast;
+ __u16 whiteness; /* Black and white only */
+ __u16 depth; /* Capture depth */
+ __u16 palette; /* Palette in use */
+#define VIDEO_PALETTE_GREY 1 /* Linear greyscale */
+#define VIDEO_PALETTE_HI240 2 /* High 240 cube (BT848) */
+#define VIDEO_PALETTE_RGB565 3 /* 565 16 bit RGB */
+#define VIDEO_PALETTE_RGB24 4 /* 24bit RGB */
+#define VIDEO_PALETTE_RGB32 5 /* 32bit RGB */
+#define VIDEO_PALETTE_RGB555 6 /* 555 15bit RGB */
+#define VIDEO_PALETTE_YUV422 7 /* YUV422 capture */
+#define VIDEO_PALETTE_YUYV 8
+#define VIDEO_PALETTE_UYVY 9 /* The great thing about standards is ... */
+#define VIDEO_PALETTE_YUV420 10
+#define VIDEO_PALETTE_YUV411 11 /* YUV411 capture */
+#define VIDEO_PALETTE_RAW 12 /* RAW capture (BT848) */
+#define VIDEO_PALETTE_YUV422P 13 /* YUV 4:2:2 Planar */
+#define VIDEO_PALETTE_YUV411P 14 /* YUV 4:1:1 Planar */
+#define VIDEO_PALETTE_YUV420P 15 /* YUV 4:2:0 Planar */
+#define VIDEO_PALETTE_YUV410P 16 /* YUV 4:1:0 Planar */
+#define VIDEO_PALETTE_PLANAR 13 /* start of planar entries */
+#define VIDEO_PALETTE_COMPONENT 7 /* start of component entries */
+};
+
+struct video_audio
+{
+ int audio; /* Audio channel */
+ __u16 volume; /* If settable */
+ __u16 bass, treble;
+ __u32 flags;
+#define VIDEO_AUDIO_MUTE 1
+#define VIDEO_AUDIO_MUTABLE 2
+#define VIDEO_AUDIO_VOLUME 4
+#define VIDEO_AUDIO_BASS 8
+#define VIDEO_AUDIO_TREBLE 16
+ char name[16];
+#define VIDEO_SOUND_MONO 1
+#define VIDEO_SOUND_STEREO 2
+#define VIDEO_SOUND_LANG1 4
+#define VIDEO_SOUND_LANG2 8
+ __u16 mode;
+ __u16 balance; /* Stereo balance */
+ __u16 step; /* Step actual volume uses */
+};
+
+struct video_clip
+{
+ __s32 x,y;
+ __s32 width, height;
+ struct video_clip *next; /* For user use/driver use only */
+};
+
+struct video_window
+{
+ __u32 x,y; /* Position of window */
+ __u32 width,height; /* Its size */
+ __u32 chromakey;
+ __u32 flags;
+ struct video_clip *clips; /* Set only */
+ int clipcount;
+#define VIDEO_WINDOW_INTERLACE 1
+#define VIDEO_CLIP_BITMAP -1
+/* bitmap is 1024x625, a '1' bit represents a clipped pixel */
+#define VIDEO_CLIPMAP_SIZE (128 * 625)
+};
+
+struct video_capture
+{
+ __u32 x,y; /* Offsets into image */
+ __u32 width, height; /* Area to capture */
+ __u16 decimation; /* Decimation divder */
+ __u16 flags; /* Flags for capture */
+#define VIDEO_CAPTURE_ODD 0 /* Temporal */
+#define VIDEO_CAPTURE_EVEN 1
+};
+
+struct video_buffer
+{
+ void *base;
+ int height,width;
+ int depth;
+ int bytesperline;
+};
+
+struct video_mmap
+{
+ unsigned int frame; /* Frame (0 - n) for double buffer */
+ int height,width;
+ unsigned int format; /* should be VIDEO_PALETTE_* */
+};
+
+struct video_key
+{
+ __u8 key[8];
+ __u32 flags;
+};
+
+
+#define VIDEO_MAX_FRAME 32
+
+struct video_mbuf
+{
+ int size; /* Total memory to map */
+ int frames; /* Frames */
+ int offsets[VIDEO_MAX_FRAME];
+};
+
+
+#define VIDEO_NO_UNIT (-1)
+
+
+struct video_unit
+{
+ int video; /* Video minor */
+ int vbi; /* VBI minor */
+ int radio; /* Radio minor */
+ int audio; /* Audio minor */
+ int teletext; /* Teletext minor */
+};
+
+#define VIDIOCGCAP _IOR('v',1,struct video_capability) /* Get capabilities */
+#define VIDIOCGCHAN _IOWR('v',2,struct video_channel) /* Get channel info (sources) */
+#define VIDIOCSCHAN _IOW('v',3,struct video_channel) /* Set channel */
+#define VIDIOCGTUNER _IOWR('v',4,struct video_tuner) /* Get tuner abilities */
+#define VIDIOCSTUNER _IOW('v',5,struct video_tuner) /* Tune the tuner for the current channel */
+#define VIDIOCGPICT _IOR('v',6,struct video_picture) /* Get picture properties */
+#define VIDIOCSPICT _IOW('v',7,struct video_picture) /* Set picture properties */
+#define VIDIOCCAPTURE _IOW('v',8,int) /* Start, end capture */
+#define VIDIOCGWIN _IOR('v',9, struct video_window) /* Set the video overlay window */
+#define VIDIOCSWIN _IOW('v',10, struct video_window) /* Set the video overlay window - passes clip list for hardware smarts , chromakey etc */
+#define VIDIOCGFBUF _IOR('v',11, struct video_buffer) /* Get frame buffer */
+#define VIDIOCSFBUF _IOW('v',12, struct video_buffer) /* Set frame buffer - root only */
+#define VIDIOCKEY _IOR('v',13, struct video_key) /* Video key event - to dev 255 is to all - cuts capture on all DMA windows with this key (0xFFFFFFFF == all) */
+#define VIDIOCGFREQ _IOR('v',14, unsigned long) /* Set tuner */
+#define VIDIOCSFREQ _IOW('v',15, unsigned long) /* Set tuner */
+#define VIDIOCGAUDIO _IOR('v',16, struct video_audio) /* Get audio info */
+#define VIDIOCSAUDIO _IOW('v',17, struct video_audio) /* Audio source, mute etc */
+#define VIDIOCSYNC _IOW('v',18, int) /* Sync with mmap grabbing */
+#define VIDIOCMCAPTURE _IOW('v',19, struct video_mmap) /* Grab frames */
+#define VIDIOCGMBUF _IOR('v', 20, struct video_mbuf) /* Memory map buffer info */
+#define VIDIOCGUNIT _IOR('v', 21, struct video_unit) /* Get attached units */
+#define VIDIOCGCAPTURE _IOR('v',22, struct video_capture) /* Get frame buffer */
+#define VIDIOCSCAPTURE _IOW('v',23, struct video_capture) /* Set frame buffer - root only */
+
+#define BASE_VIDIOCPRIVATE 192 /* 192-255 are private */
+
+
+#define VID_HARDWARE_BT848 1
+#define VID_HARDWARE_QCAM_BW 2
+#define VID_HARDWARE_PMS 3
+#define VID_HARDWARE_QCAM_C 4
+#define VID_HARDWARE_PSEUDO 5
+#define VID_HARDWARE_SAA5249 6
+#define VID_HARDWARE_AZTECH 7
+#define VID_HARDWARE_SF16MI 8
+#define VID_HARDWARE_RTRACK 9
+#define VID_HARDWARE_ZOLTRIX 10
+#define VID_HARDWARE_SAA7146 11
+#define VID_HARDWARE_VIDEUM 12 /* Reserved for Winnov videum */
+#define VID_HARDWARE_RTRACK2 13
+#define VID_HARDWARE_PERMEDIA2 14 /* Reserved for Permedia2 */
+#define VID_HARDWARE_RIVA128 15 /* Reserved for RIVA 128 */
+#define VID_HARDWARE_PLANB 16 /* PowerMac motherboard video-in */
+#define VID_HARDWARE_BROADWAY 17 /* Broadway project */
+#define VID_HARDWARE_GEMTEK 18
+#define VID_HARDWARE_TYPHOON 19
+#define VID_HARDWARE_VINO 20 /* Reserved for SGI Indy Vino */
+#define VID_HARDWARE_CADET 21 /* Cadet radio */
+#define VID_HARDWARE_TRUST 22 /* Trust FM Radio */
+
+/*
+ * Initialiser list
+ */
+
+struct video_init
+{
+ char *name;
+ int (*init)(struct video_init *);
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/videotext.h b/pfinet/linux-src/include/linux/videotext.h
new file mode 100644
index 00000000..78faf6af
--- /dev/null
+++ b/pfinet/linux-src/include/linux/videotext.h
@@ -0,0 +1,144 @@
+#ifndef _VTX_H
+#define _VTX_H
+
+/* $Id: videotext.h,v 1.1 1998/03/30 22:26:39 alan Exp $
+ *
+ * Copyright (c) 1994-97 Martin Buck <martin-2.buck@student.uni-ulm.de>
+ * Read COPYING for more information
+ *
+ */
+
+
+/*
+ * Videotext ioctls
+ */
+#define VTXIOCGETINFO 0x7101 /* get version of driver & capabilities of vtx-chipset */
+#define VTXIOCCLRPAGE 0x7102 /* clear page-buffer */
+#define VTXIOCCLRFOUND 0x7103 /* clear bits indicating that page was found */
+#define VTXIOCPAGEREQ 0x7104 /* search for page */
+#define VTXIOCGETSTAT 0x7105 /* get status of page-buffer */
+#define VTXIOCGETPAGE 0x7106 /* get contents of page-buffer */
+#define VTXIOCSTOPDAU 0x7107 /* stop data acquisition unit */
+#define VTXIOCPUTPAGE 0x7108 /* display page on TV-screen */
+#define VTXIOCSETDISP 0x7109 /* set TV-mode */
+#define VTXIOCPUTSTAT 0x710a /* set status of TV-output-buffer */
+#define VTXIOCCLRCACHE 0x710b /* clear cache on VTX-interface (if avail.) */
+#define VTXIOCSETVIRT 0x710c /* turn on virtual mode (this disables TV-display) */
+
+
+/*
+ * Definitions for VTXIOCGETINFO
+ */
+
+#define SAA5243 0
+#define SAA5246 1
+#define SAA5249 2
+#define SAA5248 3
+#define XSTV5346 4
+
+typedef struct {
+ int version_major, version_minor; /* version of driver; if version_major changes, driver */
+ /* is not backward compatible!!! CHECK THIS!!! */
+ int numpages; /* number of page-buffers of vtx-chipset */
+ int cct_type; /* type of vtx-chipset (SAA5243, SAA5246, SAA5248 or
+ * SAA5249) */
+}
+vtx_info_t;
+
+
+/*
+ * Definitions for VTXIOC{CLRPAGE,CLRFOUND,PAGEREQ,GETSTAT,GETPAGE,STOPDAU,PUTPAGE,SETDISP}
+ */
+
+#define MIN_UNIT (1<<0)
+#define MIN_TEN (1<<1)
+#define HR_UNIT (1<<2)
+#define HR_TEN (1<<3)
+#define PG_UNIT (1<<4)
+#define PG_TEN (1<<5)
+#define PG_HUND (1<<6)
+#define PGMASK_MAX (1<<7)
+#define PGMASK_PAGE (PG_HUND | PG_TEN | PG_UNIT)
+#define PGMASK_HOUR (HR_TEN | HR_UNIT)
+#define PGMASK_MINUTE (MIN_TEN | MIN_UNIT)
+
+typedef struct
+{
+ int page; /* number of requested page (hexadecimal) */
+ int hour; /* requested hour (hexadecimal) */
+ int minute; /* requested minute (hexadecimal) */
+ int pagemask; /* mask defining which values of the above are set */
+ int pgbuf; /* buffer where page will be stored */
+ int start; /* start of requested part of page */
+ int end; /* end of requested part of page */
+ void *buffer; /* pointer to beginning of destination buffer */
+}
+vtx_pagereq_t;
+
+
+/*
+ * Definitions for VTXIOC{GETSTAT,PUTSTAT}
+ */
+
+#define VTX_PAGESIZE (40 * 24)
+#define VTX_VIRTUALSIZE (40 * 49)
+
+typedef struct
+{
+ int pagenum; /* number of page (hexadecimal) */
+ int hour; /* hour (hexadecimal) */
+ int minute; /* minute (hexadecimal) */
+ int charset; /* national charset */
+ unsigned delete : 1; /* delete page (C4) */
+ unsigned headline : 1; /* insert headline (C5) */
+ unsigned subtitle : 1; /* insert subtitle (C6) */
+ unsigned supp_header : 1; /* suppress header (C7) */
+ unsigned update : 1; /* update page (C8) */
+ unsigned inter_seq : 1; /* interrupted sequence (C9) */
+ unsigned dis_disp : 1; /* disable/suppress display (C10) */
+ unsigned serial : 1; /* serial mode (C11) */
+ unsigned notfound : 1; /* /FOUND */
+ unsigned pblf : 1; /* PBLF */
+ unsigned hamming : 1; /* hamming-error occurred */
+}
+vtx_pageinfo_t;
+
+
+/*
+ * Definitions for VTXIOCSETDISP
+ */
+
+typedef enum {
+ DISPOFF, DISPNORM, DISPTRANS, DISPINS, INTERLACE_OFFSET
+} vtxdisp_t;
+
+
+
+/*
+ * Tuner ioctls
+ */
+
+#define TUNIOCGETINFO 0x7201 /* get version of driver & capabilities of tuner */
+#define TUNIOCRESET 0x7202 /* reset tuner */
+#define TUNIOCSETFREQ 0x7203 /* set tuning frequency (unit: kHz) */
+#define TUNIOCGETFREQ 0x7204 /* get tuning frequency (unit: kHz) */
+#define TUNIOCSETCHAN 0x7205 /* set tuning channel */
+#define TUNIOCGETCHAN 0x7206 /* get tuning channel */
+
+
+typedef struct
+{
+ int version_major, version_minor; /* version of driver; if version_major changes, driver */
+ /* is not backward compatible!!! CHECK THIS!!! */
+ unsigned freq : 1; /* tuner can be set to given frequency */
+ unsigned chan : 1; /* tuner stores several channels */
+ unsigned scan : 1; /* tuner supports scanning */
+ unsigned autoscan : 1; /* tuner supports scanning with automatic stop */
+ unsigned afc : 1; /* tuner supports AFC */
+ unsigned dummy1, dummy2, dummy3, dummy4, dummy5, dummy6, dummy7, dummy8, dummy9, dummy10,
+ dummy11 : 1;
+ int dummy12, dummy13, dummy14, dummy15, dummy16, dummy17, dummy18, dummy19;
+} tuner_info_t;
+
+
+#endif /* _VTX_H */
diff --git a/pfinet/linux-src/include/linux/vmalloc.h b/pfinet/linux-src/include/linux/vmalloc.h
new file mode 100644
index 00000000..ae59dc01
--- /dev/null
+++ b/pfinet/linux-src/include/linux/vmalloc.h
@@ -0,0 +1,23 @@
+#ifndef __LINUX_VMALLOC_H
+#define __LINUX_VMALLOC_H
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+#include <asm/pgtable.h>
+
+struct vm_struct {
+ unsigned long flags;
+ void * addr;
+ unsigned long size;
+ struct vm_struct * next;
+};
+
+struct vm_struct * get_vm_area(unsigned long size);
+void vfree(void * addr);
+void * vmalloc(unsigned long size);
+long vread(char *buf, char *addr, unsigned long count);
+void vmfree_area_pages(unsigned long address, unsigned long size);
+int vmalloc_area_pages(unsigned long address, unsigned long size);
+
+#endif
diff --git a/pfinet/linux-src/include/linux/vt.h b/pfinet/linux-src/include/linux/vt.h
new file mode 100644
index 00000000..9f95b0be
--- /dev/null
+++ b/pfinet/linux-src/include/linux/vt.h
@@ -0,0 +1,54 @@
+#ifndef _LINUX_VT_H
+#define _LINUX_VT_H
+
+/* 0x56 is 'V', to avoid collision with termios and kd */
+
+#define VT_OPENQRY 0x5600 /* find available vt */
+
+struct vt_mode {
+ char mode; /* vt mode */
+ char waitv; /* if set, hang on writes if not active */
+ short relsig; /* signal to raise on release req */
+ short acqsig; /* signal to raise on acquisition */
+ short frsig; /* unused (set to 0) */
+};
+#define VT_GETMODE 0x5601 /* get mode of active vt */
+#define VT_SETMODE 0x5602 /* set mode of active vt */
+#define VT_AUTO 0x00 /* auto vt switching */
+#define VT_PROCESS 0x01 /* process controls switching */
+#define VT_ACKACQ 0x02 /* acknowledge switch */
+
+struct vt_stat {
+ unsigned short v_active; /* active vt */
+ unsigned short v_signal; /* signal to send */
+ unsigned short v_state; /* vt bitmask */
+};
+#define VT_GETSTATE 0x5603 /* get global vt state info */
+#define VT_SENDSIG 0x5604 /* signal to send to bitmask of vts */
+
+#define VT_RELDISP 0x5605 /* release display */
+
+#define VT_ACTIVATE 0x5606 /* make vt active */
+#define VT_WAITACTIVE 0x5607 /* wait for vt active */
+#define VT_DISALLOCATE 0x5608 /* free memory associated to vt */
+
+struct vt_sizes {
+ unsigned short v_rows; /* number of rows */
+ unsigned short v_cols; /* number of columns */
+ unsigned short v_scrollsize; /* number of lines of scrollback */
+};
+#define VT_RESIZE 0x5609 /* set kernel's idea of screensize */
+
+struct vt_consize {
+ unsigned short v_rows; /* number of rows */
+ unsigned short v_cols; /* number of columns */
+ unsigned short v_vlin; /* number of pixel rows on screen */
+ unsigned short v_clin; /* number of pixel rows per character */
+ unsigned short v_vcol; /* number of pixel columns on screen */
+ unsigned short v_ccol; /* number of pixel columns per character */
+};
+#define VT_RESIZEX 0x560A /* set kernel's idea of screensize + more */
+#define VT_LOCKSWITCH 0x560B /* disallow vt switching */
+#define VT_UNLOCKSWITCH 0x560C /* allow vt switching */
+
+#endif /* _LINUX_VT_H */
diff --git a/pfinet/linux-src/include/linux/vt_buffer.h b/pfinet/linux-src/include/linux/vt_buffer.h
new file mode 100644
index 00000000..ca1ec519
--- /dev/null
+++ b/pfinet/linux-src/include/linux/vt_buffer.h
@@ -0,0 +1,83 @@
+/*
+ * include/linux/vt_buffer.h -- Access to VT screen buffer
+ *
+ * (c) 1998 Martin Mares <mj@ucw.cz>
+ *
+ * This is a set of macros and functions which are used in the
+ * console driver and related code to access the screen buffer.
+ * In most cases the console works with simple in-memory buffer,
+ * but when handling hardware text mode consoles, we store
+ * the foreground console directly in video memory.
+ */
+
+#ifndef _LINUX_VT_BUFFER_H_
+#define _LINUX_VT_BUFFER_H_
+
+#include <linux/config.h>
+
+#ifdef CONFIG_VGA_CONSOLE
+#include <asm/vga.h>
+#endif
+
+#ifndef VT_BUF_HAVE_RW
+#define scr_writew(val, addr) (*(addr) = (val))
+#define scr_readw(addr) (*(addr))
+#define scr_memcpyw(d, s, c) memcpy(d, s, c)
+#define scr_memmovew(d, s, c) memmove(d, s, c)
+#define VT_BUF_HAVE_MEMCPYW
+#define VT_BUF_HAVE_MEMMOVEW
+#define scr_memcpyw_from(d, s, c) memcpy(d, s, c)
+#define scr_memcpyw_to(d, s, c) memcpy(d, s, c)
+#define VT_BUF_HAVE_MEMCPYF
+#endif
+
+#ifndef VT_BUF_HAVE_MEMSETW
+extern inline void scr_memsetw(u16 *s, u16 c, unsigned int count)
+{
+ count /= 2;
+ while (count--)
+ scr_writew(c, s++);
+}
+#endif
+
+#ifndef VT_BUF_HAVE_MEMCPYW
+extern inline void scr_memcpyw(u16 *d, const u16 *s, unsigned int count)
+{
+ count /= 2;
+ while (count--)
+ scr_writew(scr_readw(s++), d++);
+}
+#endif
+
+#ifndef VT_BUF_HAVE_MEMMOVEW
+extern inline void scr_memmovew(u16 *d, const u16 *s, unsigned int count)
+{
+ if (d < s)
+ scr_memcpyw(d, s, count);
+ else {
+ count /= 2;
+ d += count;
+ s += count;
+ while (count--)
+ scr_writew(scr_readw(--s), --d);
+ }
+}
+#endif
+
+#ifndef VT_BUF_HAVE_MEMCPYF
+extern inline void scr_memcpyw_from(u16 *d, const u16 *s, unsigned int count)
+{
+ count /= 2;
+ while (count--)
+ *d++ = scr_readw(s++);
+}
+
+extern inline void scr_memcpyw_to(u16 *d, const u16 *s, unsigned int count)
+{
+ count /= 2;
+ while (count--)
+ scr_writew(*s++, d++);
+}
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/vt_kern.h b/pfinet/linux-src/include/linux/vt_kern.h
new file mode 100644
index 00000000..e1b72768
--- /dev/null
+++ b/pfinet/linux-src/include/linux/vt_kern.h
@@ -0,0 +1,94 @@
+#ifndef _VT_KERN_H
+#define _VT_KERN_H
+
+/*
+ * this really is an extension of the vc_cons structure in console.c, but
+ * with information needed by the vt package
+ */
+
+#include <linux/config.h>
+#include <linux/vt.h>
+
+/*
+ * Presently, a lot of graphics programs do not restore the contents of
+ * the higher font pages. Defining this flag will avoid use of them, but
+ * will lose support for PIO_FONTRESET. Note that many font operations are
+ * not likely to work with these programs anyway; they need to be
+ * fixed. The linux/Documentation directory includes a code snippet
+ * to save and restore the text font.
+ */
+#ifdef CONFIG_VGA_CONSOLE
+#define BROKEN_GRAPHICS_PROGRAMS 1
+#endif
+
+extern struct vt_struct {
+ int vc_num; /* The console number */
+ unsigned char vc_mode; /* KD_TEXT, ... */
+ struct vt_mode vt_mode;
+ int vt_pid;
+ int vt_newvt;
+ struct wait_queue *paste_wait;
+} *vt_cons[MAX_NR_CONSOLES];
+
+void (*kd_mksound)(unsigned int hz, unsigned int ticks);
+
+/* console.c */
+
+struct console_font_op;
+struct consw;
+
+int vc_allocate(unsigned int console);
+int vc_cons_allocated(unsigned int console);
+int vc_resize(unsigned int lines, unsigned int cols,
+ unsigned int first, unsigned int last);
+#define vc_resize_all(l, c) vc_resize(l, c, 0, MAX_NR_CONSOLES-1)
+#define vc_resize_con(l, c, x) vc_resize(l, c, x, x)
+void vc_disallocate(unsigned int console);
+void reset_palette(int currcons);
+void set_palette(int currcons);
+void do_blank_screen(int gfx_mode);
+void unblank_screen(void);
+void poke_blanked_console(void);
+int con_font_op(int currcons, struct console_font_op *op);
+int con_set_cmap(unsigned char *cmap);
+int con_get_cmap(unsigned char *cmap);
+void scrollback(int);
+void scrollfront(int);
+void update_region(int currcons, unsigned long start, int count);
+void redraw_screen(int new_console, int is_switch);
+#define update_screen(x) redraw_screen(x, 0)
+#define switch_screen(x) redraw_screen(x, 1)
+
+struct tty_struct;
+int tioclinux(struct tty_struct *tty, unsigned long arg);
+
+/* consolemap.c */
+
+struct unimapinit;
+struct unipair;
+
+int con_set_trans_old(unsigned char * table);
+int con_get_trans_old(unsigned char * table);
+int con_set_trans_new(unsigned short * table);
+int con_get_trans_new(unsigned short * table);
+int con_clear_unimap(int currcons, struct unimapinit *ui);
+int con_set_unimap(int currcons, ushort ct, struct unipair *list);
+int con_get_unimap(int currcons, ushort ct, ushort *uct, struct unipair *list);
+int con_set_default_unimap(int currcons);
+void con_free_unimap(int currcons);
+void con_protect_unimap(int currcons, int rdonly);
+int con_copy_unimap(int dstcons, int srccons);
+
+/* vt.c */
+
+extern unsigned int video_font_height;
+extern unsigned int default_font_height;
+extern unsigned int video_scan_lines;
+
+void complete_change_console(unsigned int new_console);
+int vt_waitactive(int vt);
+void change_console(unsigned int);
+void reset_vc(unsigned int new_console);
+int vt_waitactive(int vt);
+
+#endif /* _VT_KERN_H */
diff --git a/pfinet/linux-src/include/linux/wait.h b/pfinet/linux-src/include/linux/wait.h
new file mode 100644
index 00000000..6514693c
--- /dev/null
+++ b/pfinet/linux-src/include/linux/wait.h
@@ -0,0 +1,33 @@
+#ifndef _LINUX_WAIT_H
+#define _LINUX_WAIT_H
+
+#define WNOHANG 0x00000001
+#define WUNTRACED 0x00000002
+
+#define __WCLONE 0x80000000
+
+#ifdef __KERNEL__
+
+#include <asm/page.h>
+
+struct wait_queue {
+ struct task_struct * task;
+ struct wait_queue * next;
+};
+
+#define WAIT_QUEUE_HEAD(x) ((struct wait_queue *)((x)-1))
+
+static inline void init_waitqueue(struct wait_queue **q)
+{
+ *q = WAIT_QUEUE_HEAD(q);
+}
+
+static inline int waitqueue_active(struct wait_queue **q)
+{
+ struct wait_queue *head = *q;
+ return head && head != WAIT_QUEUE_HEAD(q);
+}
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/pfinet/linux-src/include/linux/wanpipe.h b/pfinet/linux-src/include/linux/wanpipe.h
new file mode 100644
index 00000000..d3b61f4a
--- /dev/null
+++ b/pfinet/linux-src/include/linux/wanpipe.h
@@ -0,0 +1,371 @@
+/*****************************************************************************
+* wanpipe.h WANPIPE(tm) Multiprotocol WAN Link Driver.
+* User-level API definitions.
+*
+* Author: Nenad Corbic <ncorbic@sangoma.com>
+* Gideon Hack
+*
+* Copyright: (c) 1995-1999 Sangoma Technologies Inc.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version
+* 2 of the License, or (at your option) any later version.
+* ============================================================================
+* Oct 04, 1999 Nenad Corbic New CHDLC and FRAME RELAY code, SMP support
+* Jun 02, 1999 Gideon Hack Added 'update_call_count' for Cisco HDLC
+* support
+* Jun 26, 1998 David Fong Added 'ip_mode' in sdla_t.u.p for dynamic IP
+* routing mode configuration
+* Jun 12, 1998 David Fong Added Cisco HDLC union member in sdla_t
+* Dec 08, 1997 Jaspreet Singh Added 'authenticator' in union of 'sdla_t'
+* Nov 26, 1997 Jaspreet Singh Added 'load_sharing' structure. Also added
+* 'devs_struct','dev_to_devtint_next' to 'sdla_t'
+* Nov 24, 1997 Jaspreet Singh Added 'irq_dis_if_send_count',
+* 'irq_dis_poll_count' to 'sdla_t'.
+* Nov 06, 1997 Jaspreet Singh Added a define called 'INTR_TEST_MODE'
+* Oct 20, 1997 Jaspreet Singh Added 'buff_intr_mode_unbusy' and
+* 'dlci_intr_mode_unbusy' to 'sdla_t'
+* Oct 18, 1997 Jaspreet Singh Added structure to maintain global driver
+* statistics.
+* Jan 15, 1997 Gene Kozin Version 3.1.0
+* o added UDP management stuff
+* Jan 02, 1997 Gene Kozin Version 3.0.0
+*****************************************************************************/
+#ifndef _WANPIPE_H
+#define _WANPIPE_H
+
+#ifdef __SMP__
+#include <asm/spinlock.h> /* Support for SMP Locking */
+#endif
+
+#include <linux/wanrouter.h>
+
+/* Defines */
+
+#ifndef PACKED
+#define PACKED __attribute__((packed))
+#endif
+
+#define WANPIPE_MAGIC 0x414C4453L /* signatire: 'SDLA' reversed */
+
+/* IOCTL numbers (up to 16) */
+#define WANPIPE_DUMP (ROUTER_USER+0) /* dump adapter's memory */
+#define WANPIPE_EXEC (ROUTER_USER+1) /* execute firmware command */
+
+#define TRACE_ALL 0x00
+#define TRACE_PROT 0x01
+#define TRACE_DATA 0x02
+
+/* values for request/reply byte */
+#define UDPMGMT_REQUEST 0x01
+#define UDPMGMT_REPLY 0x02
+#define UDP_OFFSET 12
+
+
+/*
+ * Data structures for IOCTL calls.
+ */
+
+typedef struct sdla_dump /* WANPIPE_DUMP */
+{
+ unsigned long magic; /* for verification */
+ unsigned long offset; /* absolute adapter memory address */
+ unsigned long length; /* block length */
+ void* ptr; /* -> buffer */
+} sdla_dump_t;
+
+typedef struct sdla_exec /* WANPIPE_EXEC */
+{
+ unsigned long magic; /* for verification */
+ void* cmd; /* -> command structure */
+ void* data; /* -> data buffer */
+} sdla_exec_t;
+
+/* UDP management stuff */
+
+typedef struct wum_header
+{
+ unsigned char signature[8]; /* 00h: signature */
+ unsigned char type; /* 08h: request/reply */
+ unsigned char command; /* 09h: commnand */
+ unsigned char reserved[6]; /* 0Ah: reserved */
+} wum_header_t;
+
+/*************************************************************************
+ Data Structure for global statistics
+*************************************************************************/
+
+typedef struct global_stats
+{
+ unsigned long isr_entry;
+ unsigned long isr_already_critical;
+ unsigned long isr_rx;
+ unsigned long isr_tx;
+ unsigned long isr_intr_test;
+ unsigned long isr_spurious;
+ unsigned long isr_enable_tx_int;
+ unsigned long rx_intr_corrupt_rx_bfr;
+ unsigned long rx_intr_on_orphaned_DLCI;
+ unsigned long rx_intr_dev_not_started;
+ unsigned long tx_intr_dev_not_started;
+ unsigned long poll_entry;
+ unsigned long poll_already_critical;
+ unsigned long poll_processed;
+ unsigned long poll_tbusy_bad_status;
+ unsigned long poll_host_disable_irq;
+ unsigned long poll_host_enable_irq;
+
+} global_stats_t;
+
+
+typedef struct{
+ unsigned short udp_src_port PACKED;
+ unsigned short udp_dst_port PACKED;
+ unsigned short udp_length PACKED;
+ unsigned short udp_checksum PACKED;
+} udp_pkt_t;
+
+
+typedef struct {
+ unsigned char ver_inet_hdr_length PACKED;
+ unsigned char service_type PACKED;
+ unsigned short total_length PACKED;
+ unsigned short identifier PACKED;
+ unsigned short flags_frag_offset PACKED;
+ unsigned char ttl PACKED;
+ unsigned char protocol PACKED;
+ unsigned short hdr_checksum PACKED;
+ unsigned long ip_src_address PACKED;
+ unsigned long ip_dst_address PACKED;
+} ip_pkt_t;
+
+
+typedef struct {
+ unsigned char signature[8] PACKED;
+ unsigned char request_reply PACKED;
+ unsigned char id PACKED;
+ unsigned char reserved[6] PACKED;
+} wp_mgmt_t;
+
+/*************************************************************************
+ Data Structure for if_send statistics
+*************************************************************************/
+typedef struct if_send_stat{
+ unsigned long if_send_entry;
+ unsigned long if_send_skb_null;
+ unsigned long if_send_broadcast;
+ unsigned long if_send_multicast;
+ unsigned long if_send_critical_ISR;
+ unsigned long if_send_critical_non_ISR;
+ unsigned long if_send_tbusy;
+ unsigned long if_send_tbusy_timeout;
+ unsigned long if_send_PIPE_request;
+ unsigned long if_send_wan_disconnected;
+ unsigned long if_send_dlci_disconnected;
+ unsigned long if_send_no_bfrs;
+ unsigned long if_send_adptr_bfrs_full;
+ unsigned long if_send_bfr_passed_to_adptr;
+ unsigned long if_send_protocol_error;
+ unsigned long if_send_bfr_not_passed_to_adptr;
+ unsigned long if_send_tx_int_enabled;
+ unsigned long if_send_consec_send_fail;
+} if_send_stat_t;
+
+typedef struct rx_intr_stat{
+ unsigned long rx_intr_no_socket;
+ unsigned long rx_intr_dev_not_started;
+ unsigned long rx_intr_PIPE_request;
+ unsigned long rx_intr_bfr_not_passed_to_stack;
+ unsigned long rx_intr_bfr_passed_to_stack;
+} rx_intr_stat_t;
+
+typedef struct pipe_mgmt_stat{
+ unsigned long UDP_PIPE_mgmt_kmalloc_err;
+ unsigned long UDP_PIPE_mgmt_direction_err;
+ unsigned long UDP_PIPE_mgmt_adptr_type_err;
+ unsigned long UDP_PIPE_mgmt_adptr_cmnd_OK;
+ unsigned long UDP_PIPE_mgmt_adptr_cmnd_timeout;
+ unsigned long UDP_PIPE_mgmt_adptr_send_passed;
+ unsigned long UDP_PIPE_mgmt_adptr_send_failed;
+ unsigned long UDP_PIPE_mgmt_not_passed_to_stack;
+ unsigned long UDP_PIPE_mgmt_passed_to_stack;
+ unsigned long UDP_PIPE_mgmt_no_socket;
+ unsigned long UDP_PIPE_mgmt_passed_to_adptr;
+} pipe_mgmt_stat_t;
+
+
+
+#define MAX_LGTH_UDP_MGNT_PKT 2000
+
+
+/* This is used for interrupt testing */
+#define INTR_TEST_MODE 0x02
+
+#define WUM_SIGNATURE_L 0x50495046
+#define WUM_SIGNATURE_H 0x444E3845
+
+#define WUM_KILL 0x50
+#define WUM_EXEC 0x51
+
+#ifdef __KERNEL__
+/****** Kernel Interface ****************************************************/
+
+#include <linux/sdladrv.h> /* SDLA support module API definitions */
+#include <linux/sdlasfm.h> /* SDLA firmware module definitions */
+
+#ifndef min
+#define min(a,b) (((a)<(b))?(a):(b))
+#endif
+#ifndef max
+#define max(a,b) (((a)>(b))?(a):(b))
+#endif
+
+#define is_digit(ch) (((ch)>=(unsigned)'0'&&(ch)<=(unsigned)'9')?1:0)
+#define is_alpha(ch) ((((ch)>=(unsigned)'a'&&(ch)<=(unsigned)'z')||\
+ ((ch)>=(unsigned)'A'&&(ch)<=(unsigned)'Z'))?1:0)
+#define is_hex_digit(ch) ((((ch)>=(unsigned)'0'&&(ch)<=(unsigned)'9')||\
+ ((ch)>=(unsigned)'a'&&(ch)<=(unsigned)'f')||\
+ ((ch)>=(unsigned)'A'&&(ch)<=(unsigned)'F'))?1:0)
+
+/****** Data Structures *****************************************************/
+
+/* Adapter Data Space.
+ * This structure is needed because we handle multiple cards, otherwise
+ * static data would do it.
+ */
+typedef struct sdla
+{
+ char devname[WAN_DRVNAME_SZ+1]; /* card name */
+ sdlahw_t hw; /* hardware configuration */
+ wan_device_t wandev; /* WAN device data space */
+ unsigned open_cnt; /* number of open interfaces */
+ unsigned long state_tick; /* link state timestamp */
+ unsigned intr_mode; /* Type of Interrupt Mode */
+ char in_isr; /* interrupt-in-service flag */
+ char buff_int_mode_unbusy; /* flag for carrying out dev_tint */
+ char dlci_int_mode_unbusy; /* flag for carrying out dev_tint */
+ char configured; /* flag for previous configurations */
+ unsigned short irq_dis_if_send_count; /* Disabling irqs in if_send*/
+ unsigned short irq_dis_poll_count; /* Disabling irqs in poll routine*/
+ unsigned short force_enable_irq;
+ char TracingEnabled; /* flag for enabling trace */
+ global_stats_t statistics; /* global statistics */
+#ifdef __SMP__
+ spinlock_t lock; /* Support for SMP Locking */
+#endif
+ void* mbox; /* -> mailbox */
+ void* rxmb; /* -> receive mailbox */
+ void* flags; /* -> adapter status flags */
+ void (*isr)(struct sdla* card); /* interrupt service routine */
+ void (*poll)(struct sdla* card); /* polling routine */
+ int (*exec)(struct sdla* card, void* u_cmd, void* u_data);
+
+ struct sdla *next; /* Secondary Port Device: Piggibacking */
+ union
+ {
+ struct
+ { /****** X.25 specific data **********/
+ unsigned lo_pvc;
+ unsigned hi_pvc;
+ unsigned lo_svc;
+ unsigned hi_svc;
+ } x;
+ struct
+ { /****** frame relay specific data ***/
+ void* rxmb_base; /* -> first Rx buffer */
+ void* rxmb_last; /* -> last Rx buffer */
+ unsigned rx_base; /* S508 receive buffer base */
+ unsigned rx_top; /* S508 receive buffer end */
+ unsigned short node_dlci[100];
+ unsigned short dlci_num;
+ struct device *dlci_to_dev_map[991 + 1];
+ unsigned tx_interrupts_pending;
+ unsigned short timer_int_enabled;
+ unsigned short udp_pkt_lgth;
+ int udp_type;
+ char udp_pkt_src;
+ unsigned udp_dlci;
+ char udp_pkt_data[MAX_LGTH_UDP_MGNT_PKT];
+ void* trc_el_base; /* first trace element */
+ void* trc_el_last; /* last trace element */
+ void *curr_trc_el; /* current trace element */
+ unsigned short trc_bfr_space; /* trace buffer space */
+ unsigned char update_comms_stats;
+ } f;
+ struct /****** PPP-specific data ***********/
+ {
+ char if_name[WAN_IFNAME_SZ+1]; /* interface name */
+ void* txbuf; /* -> current Tx buffer */
+ void* txbuf_base; /* -> first Tx buffer */
+ void* txbuf_last; /* -> last Tx buffer */
+ void* rxbuf_base; /* -> first Rx buffer */
+ void* rxbuf_last; /* -> last Rx buffer */
+ unsigned rx_base; /* S508 receive buffer base */
+ unsigned rx_top; /* S508 receive buffer end */
+ char ip_mode; /* STATIC/HOST/PEER IP Mode */
+ char authenticator; /* Authenticator for PAP/CHAP */
+ } p;
+ struct /* Cisco HDLC-specific data */
+ {
+ char if_name[WAN_IFNAME_SZ+1]; /* interface name */
+ unsigned char comm_port;/* Communication Port O or 1 */
+ unsigned char usedby; /* Used by WANPIPE or API */
+ void* rxmb; /* Receive mail box */
+ void* flags; /* flags */
+ void* tx_status; /* Tx status element */
+ void* rx_status; /* Rx status element */
+ void* txbuf; /* -> current Tx buffer */
+ void* txbuf_base; /* -> first Tx buffer */
+ void* txbuf_last; /* -> last Tx buffer */
+ void* rxbuf_base; /* -> first Rx buffer */
+ void* rxbuf_last; /* -> last Rx buffer */
+ unsigned rx_base; /* S508 receive buffer base */
+ unsigned rx_top; /* S508 receive buffer end */
+ unsigned short protocol_options;
+ unsigned short kpalv_tx; /* Tx kpalv timer */
+ unsigned short kpalv_rx; /* Rx kpalv timer */
+ unsigned short kpalv_err; /* Error tolerance */
+ unsigned short slarp_timer; /* SLARP req timer */
+ unsigned state; /* state of the link */
+ unsigned char api_status;
+ unsigned char update_call_count;
+ } c;
+ struct
+ {
+ void* tx_status; /* Tx status element */
+ void* rx_status; /* Rx status element */
+ void* trace_status; /* Trace status element */
+ void* txbuf; /* -> current Tx buffer */
+ void* txbuf_base; /* -> first Tx buffer */
+ void* txbuf_last; /* -> last Tx buffer */
+ void* rxbuf_base; /* -> first Rx buffer */
+ void* rxbuf_last; /* -> last Rx buffer */
+ void* tracebuf; /* -> current Trace buffer */
+ void* tracebuf_base; /* -> current Trace buffer */
+ void* tracebuf_last; /* -> current Trace buffer */
+ unsigned rx_base; /* receive buffer base */
+ unsigned rx_end; /* receive buffer end */
+ unsigned trace_base; /* trace buffer base */
+ unsigned trace_end; /* trace buffer end */
+
+ } h;
+ } u;
+} sdla_t;
+
+/****** Public Functions ****************************************************/
+
+void wanpipe_open (sdla_t* card); /* wpmain.c */
+void wanpipe_close (sdla_t* card); /* wpmain.c */
+void wanpipe_set_state (sdla_t* card, int state); /* wpmain.c */
+
+int wpx_init (sdla_t* card, wandev_conf_t* conf); /* wpx.c */
+int wpf_init (sdla_t* card, wandev_conf_t* conf); /* wpf.c */
+int wpp_init (sdla_t* card, wandev_conf_t* conf); /* wpp.c */
+int wpc_init (sdla_t* card, wandev_conf_t* conf); /* Cisco HDLC */
+int bsc_init (sdla_t* card, wandev_conf_t* conf); /* BSC streaming */
+int hdlc_init(sdla_t* card, wandev_conf_t* conf); /* HDLC support */
+int wpft1_init (sdla_t* card, wandev_conf_t* conf); /* FT1 Config support */
+
+#endif /* __KERNEL__ */
+#endif /* _WANPIPE_H */
diff --git a/pfinet/linux-src/include/linux/wanrouter.h b/pfinet/linux-src/include/linux/wanrouter.h
new file mode 100644
index 00000000..530783f7
--- /dev/null
+++ b/pfinet/linux-src/include/linux/wanrouter.h
@@ -0,0 +1,476 @@
+/*****************************************************************************
+* wanrouter.h Definitions for the WAN Multiprotocol Router Module.
+* This module provides API and common services for WAN Link
+* Drivers and is completely hardware-independent.
+*
+* Author: Nenad Corbic <ncorbic@sangoma.com>
+* Gideon Hack
+*
+* Copyright: (c) 1995-1999 Sangoma Technologies Inc.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version
+* 2 of the License, or (at your option) any later version.
+* ============================================================================
+* Oct 04, 1999 Nenad Corbic Updated for 2.1.0 release
+* Jun 02, 1999 Gideon Hack Added support for the S514 adapter.
+* Jul 20, 1998 David Fong Added Inverse ARP options to 'wanif_conf_t'
+* Jun 12, 1998 David Fong Added Cisco HDLC support.
+* Dec 16, 1997 Jaspreet Singh Moved 'enable_IPX' and 'network_number' to
+* 'wanif_conf_t'
+* Dec 05, 1997 Jaspreet Singh Added 'pap', 'chap' to 'wanif_conf_t'
+* Added 'authenticator' to 'wan_ppp_conf_t'
+* Nov 06, 1997 Jaspreet Singh Changed Router Driver version to 1.1 from 1.0
+* Oct 20, 1997 Jaspreet Singh Added 'cir','bc','be' and 'mc' to 'wanif_conf_t'
+* Added 'enable_IPX' and 'network_number' to
+* 'wan_device_t'. Also added defines for
+* UDP PACKET TYPE, Interrupt test, critical values
+* for RACE conditions.
+* Oct 05, 1997 Jaspreet Singh Added 'dlci_num' and 'dlci[100]' to
+* 'wan_fr_conf_t' to configure a list of dlci(s)
+* for a NODE
+* Jul 07, 1997 Jaspreet Singh Added 'ttl' to 'wandev_conf_t' & 'wan_device_t'
+* May 29, 1997 Jaspreet Singh Added 'tx_int_enabled' to 'wan_device_t'
+* May 21, 1997 Jaspreet Singh Added 'udp_port' to 'wan_device_t'
+* Apr 25, 1997 Farhan Thawar Added 'udp_port' to 'wandev_conf_t'
+* Jan 16, 1997 Gene Kozin router_devlist made public
+* Jan 02, 1997 Gene Kozin Initial version (based on wanpipe.h).
+*****************************************************************************/
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x020100
+#define LINUX_2_1
+#endif
+
+#ifndef _ROUTER_H
+#define _ROUTER_H
+
+#define ROUTER_NAME "wanrouter" /* in case we ever change it */
+#define ROUTER_VERSION 1 /* version number */
+#define ROUTER_RELEASE 1 /* release (minor version) number */
+#define ROUTER_IOCTL 'W' /* for IOCTL calls */
+#define ROUTER_MAGIC 0x524D4157L /* signature: 'WANR' reversed */
+
+/* IOCTL codes for /proc/router/<device> entries (up to 255) */
+enum router_ioctls
+{
+ ROUTER_SETUP = ROUTER_IOCTL<<8, /* configure device */
+ ROUTER_DOWN, /* shut down device */
+ ROUTER_STAT, /* get device status */
+ ROUTER_IFNEW, /* add interface */
+ ROUTER_IFDEL, /* delete interface */
+ ROUTER_IFSTAT, /* get interface status */
+ ROUTER_USER = (ROUTER_IOCTL<<8)+16, /* driver-specific calls */
+ ROUTER_USER_MAX = (ROUTER_IOCTL<<8)+31
+};
+
+/* identifiers for displaying proc file data for dual port adapters */
+#define PROC_DATA_PORT_0 0x8000 /* the data is for port 0 */
+#define PROC_DATA_PORT_1 0x8001 /* the data is for port 1 */
+
+/* NLPID for packet encapsulation (ISO/IEC TR 9577) */
+#define NLPID_IP 0xCC /* Internet Protocol Datagram */
+#define NLPID_SNAP 0x80 /* IEEE Subnetwork Access Protocol */
+#define NLPID_CLNP 0x81 /* ISO/IEC 8473 */
+#define NLPID_ESIS 0x82 /* ISO/IEC 9542 */
+#define NLPID_ISIS 0x83 /* ISO/IEC ISIS */
+#define NLPID_Q933 0x08 /* CCITT Q.933 */
+
+/* Miscellaneous */
+#define WAN_IFNAME_SZ 15 /* max length of the interface name */
+#define WAN_DRVNAME_SZ 15 /* max length of the link driver name */
+#define WAN_ADDRESS_SZ 31 /* max length of the WAN media address */
+#define USED_BY_FIELD 8 /* max length of the used by field */
+
+/* Defines for UDP PACKET TYPE */
+#define UDP_PTPIPE_TYPE 0x01
+#define UDP_FPIPE_TYPE 0x02
+#define UDP_CPIPE_TYPE 0x03
+#define UDP_DRVSTATS_TYPE 0x04
+#define UDP_INVALID_TYPE 0x05
+
+/* Command return code */
+#define CMD_OK 0 /* normal firmware return code */
+#define CMD_TIMEOUT 0xFF /* firmware command timed out */
+
+/* UDP Packet Management */
+#define UDP_PKT_FRM_STACK 0x00
+#define UDP_PKT_FRM_NETWORK 0x01
+
+/* Maximum interrupt test counter */
+#define MAX_INTR_TEST_COUNTER 100
+
+/* Critical Values for RACE conditions*/
+#define CRITICAL_IN_ISR 0xA1
+#define CRITICAL_INTR_HANDLED 0xB1
+
+/****** Data Types **********************************************************/
+
+/*----------------------------------------------------------------------------
+ * X.25-specific link-level configuration.
+ */
+typedef struct wan_x25_conf
+{
+ unsigned lo_pvc; /* lowest permanent circuit number */
+ unsigned hi_pvc; /* highest permanent circuit number */
+ unsigned lo_svc; /* lowest switched circuit number */
+ unsigned hi_svc; /* highest switched circuit number */
+ unsigned hdlc_window; /* HDLC window size (1..7) */
+ unsigned pkt_window; /* X.25 packet window size (1..7) */
+ unsigned t1; /* HDLC timer T1, sec (1..30) */
+ unsigned t2; /* HDLC timer T2, sec (0..29) */
+ unsigned t4; /* HDLC supervisory frame timer = T4 * T1 */
+ unsigned n2; /* HDLC retransmission limit (1..30) */
+ unsigned t10_t20; /* X.25 RESTART timeout, sec (1..255) */
+ unsigned t11_t21; /* X.25 CALL timeout, sec (1..255) */
+ unsigned t12_t22; /* X.25 RESET timeout, sec (1..255) */
+ unsigned t13_t23; /* X.25 CLEAR timeout, sec (1..255) */
+ unsigned t16_t26; /* X.25 INTERRUPT timeout, sec (1..255) */
+ unsigned t28; /* X.25 REGISTRATION timeout, sec (1..255) */
+ unsigned r10_r20; /* RESTART retransmission limit (0..250) */
+ unsigned r12_r22; /* RESET retransmission limit (0..250) */
+ unsigned r13_r23; /* CLEAR retransmission limit (0..250) */
+ unsigned ccitt_compat; /* compatibility mode: 1988/1984/1980 */
+} wan_x25_conf_t;
+
+/*----------------------------------------------------------------------------
+ * Frame relay specific link-level configuration.
+ */
+typedef struct wan_fr_conf
+{
+ unsigned signalling; /* local in-channel signalling type */
+ unsigned t391; /* link integrity verification timer */
+ unsigned t392; /* polling verification timer */
+ unsigned n391; /* full status polling cycle counter */
+ unsigned n392; /* error threshold counter */
+ unsigned n393; /* monitored events counter */
+ unsigned dlci_num; /* number of DLCs (access node) */
+ unsigned dlci[100]; /* List of all DLCIs */
+} wan_fr_conf_t;
+
+/*----------------------------------------------------------------------------
+ * PPP-specific link-level configuration.
+ */
+typedef struct wan_ppp_conf
+{
+ unsigned restart_tmr; /* restart timer */
+ unsigned auth_rsrt_tmr; /* authentication timer */
+ unsigned auth_wait_tmr; /* authentication timer */
+ unsigned mdm_fail_tmr; /* modem failure timer */
+ unsigned dtr_drop_tmr; /* DTR drop timer */
+ unsigned connect_tmout; /* connection timeout */
+ unsigned conf_retry; /* max. retry */
+ unsigned term_retry; /* max. retry */
+ unsigned fail_retry; /* max. retry */
+ unsigned auth_retry; /* max. retry */
+ unsigned auth_options; /* authentication opt. */
+ unsigned ip_options; /* IP options */
+ char authenticator; /* AUTHENTICATOR or not */
+ char ip_mode; /* Static/Host/Peer */
+} wan_ppp_conf_t;
+
+/*----------------------------------------------------------------------------
+ * CHDLC-specific link-level configuration.
+ */
+typedef struct wan_chdlc_conf
+{
+ unsigned char ignore_dcd; /* Protocol options: */
+ unsigned char ignore_cts; /* Ignore these to determine */
+ unsigned char ignore_keepalive; /* link status (Yes or No) */
+ unsigned char hdlc_streaming; /* hdlc_streaming mode (Y/N) */
+ unsigned keepalive_tx_tmr; /* transmit keepalive timer */
+ unsigned keepalive_rx_tmr; /* receive keepalive timer */
+ unsigned keepalive_err_margin; /* keepalive_error_tolerance */
+ unsigned slarp_timer; /* SLARP request timer */
+} wan_chdlc_conf_t;
+
+
+/*----------------------------------------------------------------------------
+ * WAN device configuration. Passed to ROUTER_SETUP IOCTL.
+ */
+typedef struct wandev_conf
+{
+ unsigned magic; /* magic number (for verification) */
+ unsigned config_id; /* configuration structure identifier */
+ /****** hardware configuration ******/
+ unsigned ioport; /* adapter I/O port base */
+ unsigned long maddr; /* dual-port memory address */
+ unsigned msize; /* dual-port memory size */
+ int irq; /* interrupt request level */
+ int dma; /* DMA request level */
+ char S514_CPU_no[1]; /* S514 PCI adapter CPU number ('A' or 'B') */
+ unsigned PCI_slot_no; /* S514 PCI adapter slot number */
+ char comm_port; /* Communication Port (PRI=0, SEC=1) */
+ unsigned bps; /* data transfer rate */
+ unsigned mtu; /* maximum transmit unit size */
+ unsigned udp_port; /* UDP port for management */
+ unsigned char ttl; /* Time To Live for UDP security */
+ unsigned char ft1; /* FT1 Configurator Option */
+ char interface; /* RS-232/V.35, etc. */
+ char clocking; /* external/internal */
+ char line_coding; /* NRZ/NRZI/FM0/FM1, etc. */
+ char station; /* DTE/DCE, primary/secondary, etc. */
+ char connection; /* permanent/switched/on-demand */
+ char read_mode; /* read mode: Polling or interrupt */
+ unsigned hw_opt[4]; /* other hardware options */
+ unsigned reserved[4];
+ /****** arbitrary data ***************/
+ unsigned data_size; /* data buffer size */
+ void* data; /* data buffer, e.g. firmware */
+ union /****** protocol-specific ************/
+ {
+ wan_x25_conf_t x25; /* X.25 configuration */
+ wan_ppp_conf_t ppp; /* PPP configuration */
+ wan_fr_conf_t fr; /* frame relay configuration */
+ wan_chdlc_conf_t chdlc; /* Cisco HDLC configuration */
+ } u;
+} wandev_conf_t;
+
+/* 'config_id' definitions */
+#define WANCONFIG_X25 101 /* X.25 link */
+#define WANCONFIG_FR 102 /* frame relay link */
+#define WANCONFIG_PPP 103 /* synchronous PPP link */
+#define WANCONFIG_CHDLC 104 /* Cisco HDLC Link */
+#define WANCONFIG_BSC 105 /* BiSync Streaming */
+#define WANCONFIG_HDLC 106 /* HDLC Support */
+
+/*
+ * Configuration options defines.
+ */
+/* general options */
+#define WANOPT_OFF 0
+#define WANOPT_ON 1
+#define WANOPT_NO 0
+#define WANOPT_YES 1
+
+/* intercace options */
+#define WANOPT_RS232 0
+#define WANOPT_V35 1
+
+/* data encoding options */
+#define WANOPT_NRZ 0
+#define WANOPT_NRZI 1
+#define WANOPT_FM0 2
+#define WANOPT_FM1 3
+
+/* link type options */
+#define WANOPT_POINTTOPOINT 0 /* RTS always active */
+#define WANOPT_MULTIDROP 1 /* RTS is active when transmitting */
+
+/* clocking options */
+#define WANOPT_EXTERNAL 0
+#define WANOPT_INTERNAL 1
+
+/* station options */
+#define WANOPT_DTE 0
+#define WANOPT_DCE 1
+#define WANOPT_CPE 0
+#define WANOPT_NODE 1
+#define WANOPT_SECONDARY 0
+#define WANOPT_PRIMARY 1
+
+/* connection options */
+#define WANOPT_PERMANENT 0 /* DTR always active */
+#define WANOPT_SWITCHED 1 /* use DTR to setup link (dial-up) */
+#define WANOPT_ONDEMAND 2 /* activate DTR only before sending */
+
+/* frame relay in-channel signalling */
+#define WANOPT_FR_ANSI 1 /* ANSI T1.617 Annex D */
+#define WANOPT_FR_Q933 2 /* ITU Q.933A */
+#define WANOPT_FR_LMI 3 /* LMI */
+
+/* PPP IP Mode Options */
+#define WANOPT_PPP_STATIC 0
+#define WANOPT_PPP_HOST 1
+#define WANOPT_PPP_PEER 2
+
+/* CHDLC Protocol Options */
+/* DF Commmented out for now.
+
+#define WANOPT_CHDLC_NO_DCD IGNORE_DCD_FOR_LINK_STAT
+#define WANOPT_CHDLC_NO_CTS IGNORE_CTS_FOR_LINK_STAT
+#define WANOPT_CHDLC_NO_KEEPALIVE IGNORE_KPALV_FOR_LINK_STAT
+*/
+
+/* Port options */
+#define WANOPT_PRI 0
+#define WANOPT_SEC 1
+/* read mode */
+#define WANOPT_INTR 0
+#define WANOPT_POLL 1
+
+/*----------------------------------------------------------------------------
+ * WAN Link Status Info (for ROUTER_STAT IOCTL).
+ */
+typedef struct wandev_stat
+{
+ unsigned state; /* link state */
+ unsigned ndev; /* number of configured interfaces */
+
+ /* link/interface configuration */
+ unsigned connection; /* permanent/switched/on-demand */
+ unsigned media_type; /* Frame relay/PPP/X.25/SDLC, etc. */
+ unsigned mtu; /* max. transmit unit for this device */
+
+ /* physical level statistics */
+ unsigned modem_status; /* modem status */
+ unsigned rx_frames; /* received frames count */
+ unsigned rx_overruns; /* receiver overrun error count */
+ unsigned rx_crc_err; /* receive CRC error count */
+ unsigned rx_aborts; /* received aborted frames count */
+ unsigned rx_bad_length; /* unexpetedly long/short frames count */
+ unsigned rx_dropped; /* frames discarded at device level */
+ unsigned tx_frames; /* transmitted frames count */
+ unsigned tx_underruns; /* aborted transmissions (underruns) count */
+ unsigned tx_timeouts; /* transmission timeouts */
+ unsigned tx_rejects; /* other transmit errors */
+
+ /* media level statistics */
+ unsigned rx_bad_format; /* frames with invalid format */
+ unsigned rx_bad_addr; /* frames with invalid media address */
+ unsigned tx_retries; /* frames re-transmitted */
+ unsigned reserved[16]; /* reserved for future use */
+} wandev_stat_t;
+
+/* 'state' defines */
+enum wan_states
+{
+ WAN_UNCONFIGURED, /* link/channel is not configured */
+ WAN_DISCONNECTED, /* link/channel is disconnected */
+ WAN_CONNECTING, /* connection is in progress */
+ WAN_CONNECTED, /* link/channel is operational */
+ WAN_LIMIT, /* for verification only */
+ WAN_DUALPORT /* for Dual Port cards */
+};
+
+/* 'modem_status' masks */
+#define WAN_MODEM_CTS 0x0001 /* CTS line active */
+#define WAN_MODEM_DCD 0x0002 /* DCD line active */
+#define WAN_MODEM_DTR 0x0010 /* DTR line active */
+#define WAN_MODEM_RTS 0x0020 /* RTS line active */
+
+/*----------------------------------------------------------------------------
+ * WAN interface (logical channel) configuration (for ROUTER_IFNEW IOCTL).
+ */
+typedef struct wanif_conf
+{
+ unsigned magic; /* magic number */
+ unsigned config_id; /* configuration identifier */
+ char name[WAN_IFNAME_SZ+1]; /* interface name, ASCIIZ */
+ char addr[WAN_ADDRESS_SZ+1]; /* media address, ASCIIZ */
+ char usedby[USED_BY_FIELD]; /* used by API or WANPIPE */
+ unsigned idle_timeout; /* sec, before disconnecting */
+ unsigned hold_timeout; /* sec, before re-connecting */
+ unsigned cir; /* Committed Information Rate fwd,bwd*/
+ unsigned bc; /* Committed Burst Size fwd, bwd */
+ unsigned be; /* Excess Burst Size fwd, bwd */
+ unsigned char enable_IPX; /* Enable or Disable IPX */
+ unsigned char inarp; /* Send Inverse ARP requests Y/N */
+ unsigned inarp_interval; /* sec, between InARP requests */
+ unsigned long network_number; /* Network Number for IPX */
+ char mc; /* Multicast on or off */
+ char pap; /* PAP enabled or disabled */
+ char chap; /* CHAP enabled or disabled */
+ unsigned char userid[511]; /* List of User Id */
+ unsigned char passwd[511]; /* List of passwords */
+ unsigned char sysname[31]; /* Name of the system */
+ unsigned char ignore_dcd; /* Protocol options: */
+ unsigned char ignore_cts; /* Ignore these to determine */
+ unsigned char ignore_keepalive; /* link status (Yes or No) */
+ unsigned char hdlc_streaming; /* Hdlc streaming mode (Y/N) */
+ unsigned keepalive_tx_tmr; /* transmit keepalive timer */
+ unsigned keepalive_rx_tmr; /* receive keepalive timer */
+ unsigned keepalive_err_margin; /* keepalive_error_tolerance */
+ unsigned slarp_timer; /* SLARP request timer */
+ unsigned char ttl; /* Time To Live for UDP security */
+ char interface; /* RS-232/V.35, etc. */
+ char clocking; /* external/internal */
+ unsigned bps; /* data transfer rate */
+ unsigned mtu; /* maximum transmit unit size */
+} wanif_conf_t;
+
+#ifdef __KERNEL__
+/****** Kernel Interface ****************************************************/
+
+#include <linux/fs.h> /* support for device drivers */
+#include <linux/proc_fs.h> /* proc filesystem pragmatics */
+#include <linux/inet.h> /* in_aton(), in_ntoa() prototypes */
+#include <linux/netdevice.h> /* support for network drivers */
+/*----------------------------------------------------------------------------
+ * WAN device data space.
+ */
+typedef struct wan_device
+{
+ unsigned magic; /* magic number */
+ char* name; /* -> WAN device name (ASCIIZ) */
+ void* private; /* -> driver private data */
+ unsigned config_id; /* Configuration ID */
+ /****** hardware configuration ******/
+ unsigned ioport; /* adapter I/O port base #1 */
+ char S514_cpu_no[1]; /* PCI CPU Number */
+ unsigned char S514_slot_no; /* PCI Slot Number */
+ unsigned long maddr; /* dual-port memory address */
+ unsigned msize; /* dual-port memory size */
+ int irq; /* interrupt request level */
+ int dma; /* DMA request level */
+ unsigned bps; /* data transfer rate */
+ unsigned mtu; /* max physical transmit unit size */
+ unsigned udp_port; /* UDP port for management */
+ unsigned char ttl; /* Time To Live for UDP security */
+ unsigned enable_tx_int; /* Transmit Interrupt enabled or not */
+ char interface; /* RS-232/V.35, etc. */
+ char clocking; /* external/internal */
+ char line_coding; /* NRZ/NRZI/FM0/FM1, etc. */
+ char station; /* DTE/DCE, primary/secondary, etc. */
+ char connection; /* permanent/switched/on-demand */
+ char signalling; /* Signalling RS232 or V35 */
+ char read_mode; /* read mode: Polling or interrupt */
+ char new_if_cnt; /* Number of interfaces per wanpipe */
+ char del_if_cnt; /* Number of times del_if() gets called */
+ unsigned char piggyback; /* Piggibacking a port */
+ unsigned hw_opt[4]; /* other hardware options */
+ /****** status and statistics *******/
+ char state; /* device state */
+ char api_status; /* device api status */
+#ifdef LINUX_2_1
+ struct net_device_stats stats; /* interface statistics */
+#else
+ struct enet_statistics stats; /* interface statistics */
+#endif
+ unsigned reserved[16]; /* reserved for future use */
+ unsigned critical; /* critical section flag */
+ /****** device management methods ***/
+ int (*setup) (struct wan_device *wandev, wandev_conf_t *conf);
+ int (*shutdown) (struct wan_device *wandev);
+ int (*update) (struct wan_device *wandev);
+ int (*ioctl) (struct wan_device *wandev, unsigned cmd,
+ unsigned long arg);
+ int (*new_if) (struct wan_device *wandev, struct device *dev,
+ wanif_conf_t *conf);
+ int (*del_if) (struct wan_device *wandev, struct device *dev);
+ /****** maintained by the router ****/
+ struct wan_device* next; /* -> next device */
+ struct device* dev; /* list of network interfaces */
+ unsigned ndev; /* number of interfaces */
+ struct proc_dir_entry dent; /* proc filesystem entry */
+} wan_device_t;
+
+/* Public functions available for device drivers */
+extern int register_wan_device(wan_device_t *wandev);
+extern int unregister_wan_device(char *name);
+unsigned short wanrouter_type_trans(struct sk_buff *skb, struct device *dev);
+int wanrouter_encapsulate(struct sk_buff *skb, struct device *dev);
+
+/* Proc interface functions. These must not be called by the drivers! */
+extern int wanrouter_proc_init(void);
+extern void wanrouter_proc_cleanup(void);
+extern int wanrouter_proc_add(wan_device_t *wandev);
+extern int wanrouter_proc_delete(wan_device_t *wandev);
+extern int wanrouter_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+
+/* Public Data */
+extern wan_device_t *router_devlist; /* list of registered devices */
+
+#endif /* __KERNEL__ */
+#endif /* _ROUTER_H */
diff --git a/pfinet/linux-src/include/linux/watchdog.h b/pfinet/linux-src/include/linux/watchdog.h
new file mode 100644
index 00000000..3f358258
--- /dev/null
+++ b/pfinet/linux-src/include/linux/watchdog.h
@@ -0,0 +1,40 @@
+/*
+ * Generic watchdog defines. Derived from..
+ *
+ * Berkshire PC Watchdog Defines
+ * by Ken Hollis <khollis@bitgate.com>
+ *
+ */
+
+#include <linux/ioctl.h>
+
+#define WATCHDOG_IOCTL_BASE 'W'
+
+struct watchdog_info {
+ u32 options; /* Options the card/driver supports */
+ u32 firmware_version; /* Firmware version of the card */
+ u8 identity[32]; /* Identity of the board */
+};
+
+#define WDIOC_GETSUPPORT _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
+#define WDIOC_GETSTATUS _IOR(WATCHDOG_IOCTL_BASE, 1, int)
+#define WDIOC_GETBOOTSTATUS _IOR(WATCHDOG_IOCTL_BASE, 2, int)
+#define WDIOC_GETTEMP _IOR(WATCHDOG_IOCTL_BASE, 3, int)
+#define WDIOC_SETOPTIONS _IOR(WATCHDOG_IOCTL_BASE, 4, int)
+#define WDIOC_KEEPALIVE _IOR(WATCHDOG_IOCTL_BASE, 5, int)
+
+#define WDIOF_UNKNOWN -1 /* Unknown flag error */
+#define WDIOS_UNKNOWN -1 /* Unknown status error */
+
+#define WDIOF_OVERHEAT 0x0001 /* Reset due to CPU overheat */
+#define WDIOF_FANFAULT 0x0002 /* Fan failed */
+#define WDIOF_EXTERN1 0x0004 /* External relay 1 */
+#define WDIOF_EXTERN2 0x0008 /* External relay 2 */
+#define WDIOF_POWERUNDER 0x0010 /* Power bad/power fault */
+#define WDIOF_CARDRESET 0x0020 /* Card previously reset the CPU */
+#define WDIOF_POWEROVER 0x0040 /* Power over voltage */
+#define WDIOF_KEEPALIVEPING 0x8000 /* Keep alive ping reply */
+
+#define WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer */
+#define WDIOS_ENABLECARD 0x0002 /* Turn on the watchdog timer */
+#define WDIOS_TEMPPANIC 0x0004 /* Kernel panic on temperature trip */
diff --git a/pfinet/linux-src/include/linux/wavefront.h b/pfinet/linux-src/include/linux/wavefront.h
new file mode 100644
index 00000000..f816f940
--- /dev/null
+++ b/pfinet/linux-src/include/linux/wavefront.h
@@ -0,0 +1,675 @@
+#ifndef __wavefront_h__
+#define __wavefront_h__
+
+/* WaveFront header file.
+ *
+ * Copyright (C) by Paul Barton-Davis 1998
+ *
+ * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ */
+
+#if (!defined(__GNUC__) && !defined(__GNUG__))
+
+ You will not be able to compile this file correctly without gcc, because
+ it is necessary to pack the "wavefront_alias" structure to a size
+ of 22 bytes, corresponding to 16-bit alignment (as would have been
+ the case on the original platform, MS-DOS). If this is not done,
+ then WavePatch-format files cannot be read/written correctly.
+ The method used to do this here ("__attribute__((packed)") is
+ completely compiler dependent.
+
+ All other wavefront_* types end up aligned to 32 bit values and
+ still have the same (correct) size.
+
+#else
+
+ /* However, note that as of G++ 2.7.3.2, g++ was unable to
+ correctly parse *type* __attribute__ tags. It will do the
+ right thing if we use the "packed" attribute on each struct
+ member, which has the same semantics anyway.
+ */
+
+#endif __GNUC__
+
+/***************************** WARNING ********************************
+ PLEASE DO NOT MODIFY THIS FILE IN ANY WAY THAT AFFECTS ITS ABILITY TO
+ BE USED WITH EITHER C *OR* C++.
+ **********************************************************************/
+
+#ifndef NUM_MIDIKEYS
+#define NUM_MIDIKEYS 128
+#endif NUM_MIDIKEYS
+
+#ifndef NUM_MIDICHANNELS
+#define NUM_MIDICHANNELS 16
+#endif NUM_MIDICHANNELS
+
+/* These are very useful/important. the original wavefront interface
+ was developed on a 16 bit system, where sizeof(int) = 2
+ bytes. Defining things like this makes the code much more portable, and
+ easier to understand without having to toggle back and forth
+ between a 16-bit view of the world and a 32-bit one.
+ */
+
+typedef short INT16;
+typedef unsigned short UINT16;
+typedef int INT32;
+typedef unsigned int UINT32;
+typedef char CHAR8;
+typedef unsigned char UCHAR8;
+
+/* Pseudo-commands not part of the WaveFront command set.
+ These are used for various driver controls and direct
+ hardware control.
+ */
+
+#define WFC_DEBUG_DRIVER 0
+#define WFC_FX_IOCTL 1
+#define WFC_PATCH_STATUS 2
+#define WFC_PROGRAM_STATUS 3
+#define WFC_SAMPLE_STATUS 4
+#define WFC_DISABLE_INTERRUPTS 5
+#define WFC_ENABLE_INTERRUPTS 6
+#define WFC_INTERRUPT_STATUS 7
+#define WFC_ROMSAMPLES_RDONLY 8
+#define WFC_IDENTIFY_SLOT_TYPE 9
+
+/* Wavefront synth commands
+ */
+
+#define WFC_DOWNLOAD_SAMPLE 0x80
+#define WFC_DOWNLOAD_BLOCK 0x81
+#define WFC_DOWNLOAD_MULTISAMPLE 0x82
+#define WFC_DOWNLOAD_SAMPLE_ALIAS 0x83
+#define WFC_DELETE_SAMPLE 0x84
+#define WFC_REPORT_FREE_MEMORY 0x85
+#define WFC_DOWNLOAD_PATCH 0x86
+#define WFC_DOWNLOAD_PROGRAM 0x87
+#define WFC_SET_SYNTHVOL 0x89
+#define WFC_SET_NVOICES 0x8B
+#define WFC_DOWNLOAD_DRUM 0x90
+#define WFC_GET_SYNTHVOL 0x92
+#define WFC_GET_NVOICES 0x94
+#define WFC_DISABLE_CHANNEL 0x9A
+#define WFC_ENABLE_CHANNEL 0x9B
+#define WFC_MISYNTH_OFF 0x9D
+#define WFC_MISYNTH_ON 0x9E
+#define WFC_FIRMWARE_VERSION 0x9F
+#define WFC_GET_NSAMPLES 0xA0
+#define WFC_DISABLE_DRUM_PROGRAM 0xA2
+#define WFC_UPLOAD_PATCH 0xA3
+#define WFC_UPLOAD_PROGRAM 0xA4
+#define WFC_SET_TUNING 0xA6
+#define WFC_GET_TUNING 0xA7
+#define WFC_VMIDI_ON 0xA8
+#define WFC_VMIDI_OFF 0xA9
+#define WFC_MIDI_STATUS 0xAA
+#define WFC_GET_CHANNEL_STATUS 0xAB
+#define WFC_DOWNLOAD_SAMPLE_HEADER 0xAC
+#define WFC_UPLOAD_SAMPLE_HEADER 0xAD
+#define WFC_UPLOAD_MULTISAMPLE 0xAE
+#define WFC_UPLOAD_SAMPLE_ALIAS 0xAF
+#define WFC_IDENTIFY_SAMPLE_TYPE 0xB0
+#define WFC_DOWNLOAD_EDRUM_PROGRAM 0xB1
+#define WFC_UPLOAD_EDRUM_PROGRAM 0xB2
+#define WFC_SET_EDRUM_CHANNEL 0xB3
+#define WFC_INSTOUT_LEVELS 0xB4
+#define WFC_PEAKOUT_LEVELS 0xB5
+#define WFC_REPORT_CHANNEL_PROGRAMS 0xB6
+#define WFC_HARDWARE_VERSION 0xCF
+#define WFC_UPLOAD_SAMPLE_PARAMS 0xD7
+#define WFC_DOWNLOAD_OS 0xF1
+#define WFC_NOOP 0xFF
+
+#define WF_MAX_SAMPLE 512
+#define WF_MAX_PATCH 256
+#define WF_MAX_PROGRAM 128
+
+#define WF_SECTION_MAX 44 /* longest OS section length */
+
+/* # of bytes we send to the board when sending it various kinds of
+ substantive data, such as samples, patches and programs.
+*/
+
+#define WF_PROGRAM_BYTES 32
+#define WF_PATCH_BYTES 132
+#define WF_SAMPLE_BYTES 27
+#define WF_SAMPLE_HDR_BYTES 25
+#define WF_ALIAS_BYTES 25
+#define WF_DRUM_BYTES 9
+#define WF_MSAMPLE_BYTES 259 /* (MIDI_KEYS * 2) + 3 */
+
+#define WF_ACK 0x80
+#define WF_DMA_ACK 0x81
+
+/* OR-values for MIDI status bits */
+
+#define WF_MIDI_VIRTUAL_ENABLED 0x1
+#define WF_MIDI_VIRTUAL_IS_EXTERNAL 0x2
+#define WF_MIDI_IN_TO_SYNTH_DISABLED 0x4
+
+/* slot indexes for struct address_info: makes code a little more mnemonic */
+
+#define WF_SYNTH_SLOT 0
+#define WF_INTERNAL_MIDI_SLOT 1
+#define WF_EXTERNAL_MIDI_SLOT 2
+
+/* Magic MIDI bytes used to switch I/O streams on the ICS2115 MPU401
+ emulation. Note these NEVER show up in output from the device and
+ should NEVER be used in input unless Virtual MIDI mode has been
+ disabled. If they do show up as input, the results are unpredictable.
+*/
+
+#define WF_EXTERNAL_SWITCH 0xFD
+#define WF_INTERNAL_SWITCH 0xF9
+
+/* Debugging flags */
+
+#define WF_DEBUG_CMD 0x1
+#define WF_DEBUG_DATA 0x2
+#define WF_DEBUG_LOAD_PATCH 0x4
+#define WF_DEBUG_IO 0x8
+
+/* WavePatch file format stuff */
+
+#define WF_WAVEPATCH_VERSION 120; /* Current version number (1.2) */
+#define WF_MAX_COMMENT 64 /* Comment length */
+#define WF_NUM_LAYERS 4
+#define WF_NAME_LENGTH 32
+#define WF_SOURCE_LENGTH 260
+
+#define BankFileID "Bank"
+#define DrumkitFileID "DrumKit"
+#define ProgramFileID "Program"
+
+struct wf_envelope
+{
+ UCHAR8 attack_time:7;
+ UCHAR8 Unused1:1;
+
+ UCHAR8 decay1_time:7;
+ UCHAR8 Unused2:1;
+
+ UCHAR8 decay2_time:7;
+ UCHAR8 Unused3:1;
+
+ UCHAR8 sustain_time:7;
+ UCHAR8 Unused4:1;
+
+ UCHAR8 release_time:7;
+ UCHAR8 Unused5:1;
+
+ UCHAR8 release2_time:7;
+ UCHAR8 Unused6:1;
+
+ CHAR8 attack_level;
+ CHAR8 decay1_level;
+ CHAR8 decay2_level;
+ CHAR8 sustain_level;
+ CHAR8 release_level;
+
+ UCHAR8 attack_velocity:7;
+ UCHAR8 Unused7:1;
+
+ UCHAR8 volume_velocity:7;
+ UCHAR8 Unused8:1;
+
+ UCHAR8 keyboard_scaling:7;
+ UCHAR8 Unused9:1;
+};
+typedef struct wf_envelope wavefront_envelope;
+
+struct wf_lfo
+{
+ UCHAR8 sample_number;
+
+ UCHAR8 frequency:7;
+ UCHAR8 Unused1:1;
+
+ UCHAR8 am_src:4;
+ UCHAR8 fm_src:4;
+
+ CHAR8 fm_amount;
+ CHAR8 am_amount;
+ CHAR8 start_level;
+ CHAR8 end_level;
+
+ UCHAR8 ramp_delay:7;
+ UCHAR8 wave_restart:1; /* for LFO2 only */
+
+ UCHAR8 ramp_time:7;
+ UCHAR8 Unused2:1;
+};
+typedef struct wf_lfo wavefront_lfo;
+
+struct wf_patch
+{
+ INT16 frequency_bias; /* ** THIS IS IN MOTOROLA FORMAT!! ** */
+
+ UCHAR8 amplitude_bias:7;
+ UCHAR8 Unused1:1;
+
+ UCHAR8 portamento:7;
+ UCHAR8 Unused2:1;
+
+ UCHAR8 sample_number;
+
+ UCHAR8 pitch_bend:4;
+ UCHAR8 sample_msb:1;
+ UCHAR8 Unused3:3;
+
+ UCHAR8 mono:1;
+ UCHAR8 retrigger:1;
+ UCHAR8 nohold:1;
+ UCHAR8 restart:1;
+ UCHAR8 filterconfig:2; /* SDK says "not used" */
+ UCHAR8 reuse:1;
+ UCHAR8 reset_lfo:1;
+
+ UCHAR8 fm_src2:4;
+ UCHAR8 fm_src1:4;
+
+ CHAR8 fm_amount1;
+ CHAR8 fm_amount2;
+
+ UCHAR8 am_src:4;
+ UCHAR8 Unused4:4;
+
+ CHAR8 am_amount;
+
+ UCHAR8 fc1_mode:4;
+ UCHAR8 fc2_mode:4;
+
+ CHAR8 fc1_mod_amount;
+ CHAR8 fc1_keyboard_scaling;
+ CHAR8 fc1_bias;
+ CHAR8 fc2_mod_amount;
+ CHAR8 fc2_keyboard_scaling;
+ CHAR8 fc2_bias;
+
+ UCHAR8 randomizer:7;
+ UCHAR8 Unused5:1;
+
+ struct wf_envelope envelope1;
+ struct wf_envelope envelope2;
+ struct wf_lfo lfo1;
+ struct wf_lfo lfo2;
+};
+typedef struct wf_patch wavefront_patch;
+
+struct wf_layer
+{
+ UCHAR8 patch_number;
+
+ UCHAR8 mix_level:7;
+ UCHAR8 mute:1;
+
+ UCHAR8 split_point:7;
+ UCHAR8 play_below:1;
+
+ UCHAR8 pan_mod_src:2;
+ UCHAR8 pan_or_mod:1;
+ UCHAR8 pan:4;
+ UCHAR8 split_type:1;
+};
+typedef struct wf_layer wavefront_layer;
+
+struct wf_program
+{
+ struct wf_layer layer[WF_NUM_LAYERS];
+};
+typedef struct wf_program wavefront_program;
+
+struct wf_sample_offset
+{
+ INT32 Fraction:4;
+ INT32 Integer:20;
+ INT32 Unused:8;
+};
+typedef struct wf_sample_offset wavefront_sample_offset;
+
+/* Sample slot types */
+
+#define WF_ST_SAMPLE 0
+#define WF_ST_MULTISAMPLE 1
+#define WF_ST_ALIAS 2
+#define WF_ST_EMPTY 3
+
+/* pseudo's */
+
+#define WF_ST_DRUM 4
+#define WF_ST_PROGRAM 5
+#define WF_ST_PATCH 6
+#define WF_ST_SAMPLEHDR 7
+
+#define WF_ST_MASK 0xf
+
+/* Flags for slot status. These occupy the upper bits of the same byte
+ as a sample type.
+*/
+
+#define WF_SLOT_USED 0x80 /* XXX don't rely on this being accurate */
+#define WF_SLOT_FILLED 0x40
+#define WF_SLOT_ROM 0x20
+
+#define WF_SLOT_MASK 0xf0
+
+/* channel constants */
+
+#define WF_CH_MONO 0
+#define WF_CH_LEFT 1
+#define WF_CH_RIGHT 2
+
+/* Sample formats */
+
+#define LINEAR_16BIT 0
+#define WHITE_NOISE 1
+#define LINEAR_8BIT 2
+#define MULAW_8BIT 3
+
+#define WF_SAMPLE_IS_8BIT(smpl) ((smpl)->SampleResolution&2)
+
+
+/*
+
+ Because most/all of the sample data we pass in via pointers has
+ never been copied (just mmap-ed into user space straight from the
+ disk), it would be nice to allow handling of multi-channel sample
+ data without forcing user-level extraction of the relevant bytes.
+
+ So, we need a way of specifying which channel to use (the WaveFront
+ only handles mono samples in a given slot), and the only way to do
+ this without using some struct other than wavefront_sample as the
+ interface is the awful hack of using the unused bits in a
+ wavefront_sample:
+
+ Val Meaning
+ --- -------
+ 0 no channel selection (use channel 1, sample is MONO)
+ 1 use first channel, and skip one
+ 2 use second channel, and skip one
+ 3 use third channel, and skip two
+ 4 use fourth channel, skip three
+ 5 use fifth channel, skip four
+ 6 use six channel, skip five
+
+
+ This can handle up to 4 channels, and anyone downloading >4 channels
+ of sample data just to select one of them needs to find some tools
+ like sox ...
+
+ NOTE: values 0, 1 and 2 correspond to WF_CH_* above. This is
+ important.
+
+*/
+
+#define WF_SET_CHANNEL(samp,chn) \
+ (samp)->Unused1 = chn & 0x1; \
+ (samp)->Unused2 = chn & 0x2; \
+ (samp)->Unused3 = chn & 0x4
+
+#define WF_GET_CHANNEL(samp) \
+ (((samp)->Unused3 << 2)|((samp)->Unused2<<1)|(samp)->Unused1)
+
+typedef struct wf_sample {
+ struct wf_sample_offset sampleStartOffset;
+ struct wf_sample_offset loopStartOffset;
+ struct wf_sample_offset loopEndOffset;
+ struct wf_sample_offset sampleEndOffset;
+ INT16 FrequencyBias;
+ UCHAR8 SampleResolution:2; /* sample_format */
+ UCHAR8 Unused1:1;
+ UCHAR8 Loop:1;
+ UCHAR8 Bidirectional:1;
+ UCHAR8 Unused2:1;
+ UCHAR8 Reverse:1;
+ UCHAR8 Unused3:1;
+} wavefront_sample;
+
+typedef struct wf_multisample {
+ INT16 NumberOfSamples; /* log2 of the number of samples */
+ INT16 SampleNumber[NUM_MIDIKEYS];
+} wavefront_multisample;
+
+typedef struct wf_alias {
+ INT16 OriginalSample __attribute__ ((packed));
+
+ struct wf_sample_offset sampleStartOffset __attribute__ ((packed));
+ struct wf_sample_offset loopStartOffset __attribute__ ((packed));
+ struct wf_sample_offset sampleEndOffset __attribute__ ((packed));
+ struct wf_sample_offset loopEndOffset __attribute__ ((packed));
+
+ INT16 FrequencyBias __attribute__ ((packed));
+
+ UCHAR8 SampleResolution:2 __attribute__ ((packed));
+ UCHAR8 Unused1:1 __attribute__ ((packed));
+ UCHAR8 Loop:1 __attribute__ ((packed));
+ UCHAR8 Bidirectional:1 __attribute__ ((packed));
+ UCHAR8 Unused2:1 __attribute__ ((packed));
+ UCHAR8 Reverse:1 __attribute__ ((packed));
+ UCHAR8 Unused3:1 __attribute__ ((packed));
+
+ /* This structure is meant to be padded only to 16 bits on their
+ original. Of course, whoever wrote their documentation didn't
+ realize that sizeof(struct) can be >=
+ sum(sizeof(struct-fields)) and so thought that giving a C level
+ description of the structs used in WavePatch files was
+ sufficient. I suppose it was, as long as you remember the
+ standard 16->32 bit issues.
+ */
+
+ UCHAR8 sixteen_bit_padding __attribute__ ((packed));
+} wavefront_alias;
+
+typedef struct wf_drum {
+ UCHAR8 PatchNumber;
+ UCHAR8 MixLevel:7;
+ UCHAR8 Unmute:1;
+ UCHAR8 Group:4;
+ UCHAR8 Unused1:4;
+ UCHAR8 PanModSource:2;
+ UCHAR8 PanModulated:1;
+ UCHAR8 PanAmount:4;
+ UCHAR8 Unused2:1;
+} wavefront_drum;
+
+typedef struct wf_drumkit {
+ struct wf_drum drum[NUM_MIDIKEYS];
+} wavefront_drumkit;
+
+typedef struct wf_channel_programs {
+ UCHAR8 Program[NUM_MIDICHANNELS];
+} wavefront_channel_programs;
+
+/* How to get MIDI channel status from the data returned by
+ a WFC_GET_CHANNEL_STATUS command (a struct wf_channel_programs)
+*/
+
+#define WF_CHANNEL_STATUS(ch,wcp) (wcp)[(ch/7)] & (1<<((ch)%7))
+
+typedef union wf_any {
+ wavefront_sample s;
+ wavefront_multisample ms;
+ wavefront_alias a;
+ wavefront_program pr;
+ wavefront_patch p;
+ wavefront_drum d;
+} wavefront_any;
+
+/* Hannu Solvainen hoped that his "patch_info" struct in soundcard.h
+ might work for other wave-table based patch loading situations.
+ Alas, his fears were correct. The WaveFront doesn't even come with
+ just "patches", but several different kind of structures that
+ control the sound generation process.
+ */
+
+typedef struct wf_patch_info {
+
+ /* the first two fields are used by the OSS "patch loading" interface
+ only, and are unused by the current user-level library.
+ */
+
+ INT16 key; /* Use WAVEFRONT_PATCH here */
+ UINT16 devno; /* fill in when sending */
+ UCHAR8 subkey; /* WF_ST_{SAMPLE,ALIAS,etc.} */
+
+#define WAVEFRONT_FIND_FREE_SAMPLE_SLOT 999
+
+ UINT16 number; /* patch/sample/prog number */
+
+ UINT32 size; /* size of any data included in
+ one of the fields in `hdrptr', or
+ as `dataptr'.
+
+ NOTE: for actual samples, this is
+ the size of the *SELECTED CHANNEL*
+ even if more data is actually available.
+
+ So, a stereo sample (2 channels) of
+ 6000 bytes total has `size' = 3000.
+
+ See the macros and comments for
+ WF_{GET,SET}_CHANNEL above.
+
+ */
+ wavefront_any *hdrptr; /* user-space ptr to hdr bytes */
+ UINT16 *dataptr; /* actual sample data */
+
+ wavefront_any hdr; /* kernel-space copy of hdr bytes */
+} wavefront_patch_info;
+
+/* The maximum number of bytes we will ever move to or from user space
+ in response to a WFC_* command. This obviously doesn't cover
+ actual sample data.
+*/
+
+#define WF_MAX_READ sizeof(wavefront_multisample)
+#define WF_MAX_WRITE sizeof(wavefront_multisample)
+
+/*
+ This allows us to execute any WF command except the download/upload
+ ones, which are handled differently due to copyin/copyout issues as
+ well as data-nybbling to/from the card.
+ */
+
+typedef struct wavefront_control {
+ int cmd; /* WFC_* */
+ char status; /* return status to user-space */
+ unsigned char rbuf[WF_MAX_READ]; /* bytes read from card */
+ unsigned char wbuf[WF_MAX_WRITE]; /* bytes written to card */
+} wavefront_control;
+
+#define WFCTL_WFCMD 0x1
+#define WFCTL_LOAD_SPP 0x2
+
+/* Modulator table */
+
+#define WF_MOD_LFO1 0
+#define WF_MOD_LFO2 1
+#define WF_MOD_ENV1 2
+#define WF_MOD_ENV2 3
+#define WF_MOD_KEYBOARD 4
+#define WF_MOD_LOGKEY 5
+#define WF_MOD_VELOCITY 6
+#define WF_MOD_LOGVEL 7
+#define WF_MOD_RANDOM 8
+#define WF_MOD_PRESSURE 9
+#define WF_MOD_MOD_WHEEL 10
+#define WF_MOD_1 WF_MOD_MOD_WHEEL
+#define WF_MOD_BREATH 11
+#define WF_MOD_2 WF_MOD_BREATH
+#define WF_MOD_FOOT 12
+#define WF_MOD_4 WF_MOD_FOOT
+#define WF_MOD_VOLUME 13
+#define WF_MOD_7 WF_MOD_VOLUME
+#define WF_MOD_PAN 14
+#define WF_MOD_10 WF_MOD_PAN
+#define WF_MOD_EXPR 15
+#define WF_MOD_11 WF_MOD_EXPR
+
+/* FX-related material */
+
+typedef struct wf_fx_info {
+ int request; /* see list below */
+ int data[4]; /* we don't need much */
+} wavefront_fx_info;
+
+/* support for each of these will be forthcoming once I or someone
+ else has figured out which of the addresses on page 6 and page 7 of
+ the YSS225 control each parameter. Incidentally, these come from
+ the Windows driver interface, but again, Turtle Beach didn't
+ document the API to use them.
+*/
+
+#define WFFX_SETOUTGAIN 0
+#define WFFX_SETSTEREOOUTGAIN 1
+#define WFFX_SETREVERBIN1GAIN 2
+#define WFFX_SETREVERBIN2GAIN 3
+#define WFFX_SETREVERBIN3GAIN 4
+#define WFFX_SETCHORUSINPORT 5
+#define WFFX_SETREVERBIN1PORT 6
+#define WFFX_SETREVERBIN2PORT 7
+#define WFFX_SETREVERBIN3PORT 8
+#define WFFX_SETEFFECTPORT 9
+#define WFFX_SETAUXPORT 10
+#define WFFX_SETREVERBTYPE 11
+#define WFFX_SETREVERBDELAY 12
+#define WFFX_SETCHORUSLFO 13
+#define WFFX_SETCHORUSPMD 14
+#define WFFX_SETCHORUSAMD 15
+#define WFFX_SETEFFECT 16
+#define WFFX_SETBASEALL 17
+#define WFFX_SETREVERBALL 18
+#define WFFX_SETCHORUSALL 20
+#define WFFX_SETREVERBDEF 22
+#define WFFX_SETCHORUSDEF 23
+#define WFFX_DELAYSETINGAIN 24
+#define WFFX_DELAYSETFBGAIN 25
+#define WFFX_DELAYSETFBLPF 26
+#define WFFX_DELAYSETGAIN 27
+#define WFFX_DELAYSETTIME 28
+#define WFFX_DELAYSETFBTIME 29
+#define WFFX_DELAYSETALL 30
+#define WFFX_DELAYSETDEF 32
+#define WFFX_SDELAYSETINGAIN 33
+#define WFFX_SDELAYSETFBGAIN 34
+#define WFFX_SDELAYSETFBLPF 35
+#define WFFX_SDELAYSETGAIN 36
+#define WFFX_SDELAYSETTIME 37
+#define WFFX_SDELAYSETFBTIME 38
+#define WFFX_SDELAYSETALL 39
+#define WFFX_SDELAYSETDEF 41
+#define WFFX_DEQSETINGAIN 42
+#define WFFX_DEQSETFILTER 43
+#define WFFX_DEQSETALL 44
+#define WFFX_DEQSETDEF 46
+#define WFFX_MUTE 47
+#define WFFX_FLANGESETBALANCE 48
+#define WFFX_FLANGESETDELAY 49
+#define WFFX_FLANGESETDWFFX_TH 50
+#define WFFX_FLANGESETFBGAIN 51
+#define WFFX_FLANGESETINGAIN 52
+#define WFFX_FLANGESETLFO 53
+#define WFFX_FLANGESETALL 54
+#define WFFX_FLANGESETDEF 56
+#define WFFX_PITCHSETSHIFT 57
+#define WFFX_PITCHSETBALANCE 58
+#define WFFX_PITCHSETALL 59
+#define WFFX_PITCHSETDEF 61
+#define WFFX_SRSSETINGAIN 62
+#define WFFX_SRSSETSPACE 63
+#define WFFX_SRSSETCENTER 64
+#define WFFX_SRSSETGAIN 65
+#define WFFX_SRSSETMODE 66
+#define WFFX_SRSSETDEF 68
+
+/* Allow direct user-space control over FX memory/coefficient data.
+ In theory this could be used to download the FX microprogram,
+ but it would be a little slower, and involve some weird code.
+ */
+
+#define WFFX_MEMSET 69
+
+#endif __wavefront_h__
diff --git a/pfinet/linux-src/include/linux/wireless.h b/pfinet/linux-src/include/linux/wireless.h
new file mode 100644
index 00000000..61f85152
--- /dev/null
+++ b/pfinet/linux-src/include/linux/wireless.h
@@ -0,0 +1,448 @@
+/*
+ * This file define a set of standard wireless extensions
+ *
+ * Version : 9 16.10.99
+ *
+ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ */
+
+#ifndef _LINUX_WIRELESS_H
+#define _LINUX_WIRELESS_H
+
+/************************** DOCUMENTATION **************************/
+/*
+ * Basically, the wireless extensions are for now a set of standard ioctl
+ * call + /proc/net/wireless
+ *
+ * The entry /proc/net/wireless give statistics and information on the
+ * driver.
+ * This is better than having each driver having its entry because
+ * its centralised and we may remove the driver module safely.
+ *
+ * Ioctl are used to configure the driver and issue commands. This is
+ * better than command line options of insmod because we may want to
+ * change dynamically (while the driver is running) some parameters.
+ *
+ * The ioctl mechanimsm are copied from standard devices ioctl.
+ * We have the list of command plus a structure descibing the
+ * data exchanged...
+ * Note that to add these ioctl, I was obliged to modify :
+ * net/core/dev.c (two place + add include)
+ * net/ipv4/af_inet.c (one place + add include)
+ *
+ * /proc/net/wireless is a copy of /proc/net/dev.
+ * We have a structure for data passed from the driver to /proc/net/wireless
+ * Too add this, I've modified :
+ * net/core/dev.c (two other places)
+ * include/linux/netdevice.h (one place)
+ * include/linux/proc_fs.h (one place)
+ *
+ * Do not add here things that are redundant with other mechanisms
+ * (drivers init, ifconfig, /proc/net/dev, ...) and with are not
+ * wireless specific.
+ *
+ * These wireless extensions are not magic : each driver has to provide
+ * support for them...
+ *
+ * IMPORTANT NOTE : As everything in the kernel, this is very much a
+ * work in progress. Contact me if you have ideas of improvements...
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include <linux/types.h> /* for "caddr_t" et al */
+#include <linux/socket.h> /* for "struct sockaddr" et al */
+#include <linux/if.h> /* for IFNAMSIZ and co... */
+
+/**************************** CONSTANTS ****************************/
+
+/* --------------------------- VERSION --------------------------- */
+/*
+ * This constant is used to know the availability of the wireless
+ * extensions and to know which version of wireless extensions it is
+ * (there is some stuff that will be added in the future...)
+ * I just plan to increment with each new version.
+ */
+#define WIRELESS_EXT 9
+
+/*
+ * Changes :
+ *
+ * V2 to V3
+ * --------
+ * Alan Cox start some incompatibles changes. I've integrated a bit more.
+ * - Encryption renamed to Encode to avoid US regulation problems
+ * - Frequency changed from float to struct to avoid problems on old 386
+ *
+ * V3 to V4
+ * --------
+ * - Add sensitivity
+ *
+ * V4 to V5
+ * --------
+ * - Missing encoding definitions in range
+ * - Access points stuff
+ *
+ * V5 to V6
+ * --------
+ * - 802.11 support (ESSID ioctls)
+ *
+ * V6 to V7
+ * --------
+ * - define IW_ESSID_MAX_SIZE and IW_MAX_AP
+ *
+ * V7 to V8
+ * --------
+ * - Changed my e-mail address
+ * - More 802.11 support (nickname, rate, rts, frag)
+ * - List index in frequencies
+ *
+ * V8 to V9
+ * --------
+ * - Support for 'mode of operation' (ad-hoc, managed...)
+ * - Support for unicast and multicast power saving
+ * - Change encoding to support larger tokens (>64 bits)
+ * - Updated iw_params (disable, flags) and use it for NWID
+ * - Extracted iw_point from iwreq for clarity
+ */
+
+/* -------------------------- IOCTL LIST -------------------------- */
+
+/* Basic operations */
+#define SIOCSIWNAME 0x8B00 /* Unused ??? */
+#define SIOCGIWNAME 0x8B01 /* get name */
+#define SIOCSIWNWID 0x8B02 /* set network id (the cell) */
+#define SIOCGIWNWID 0x8B03 /* get network id */
+#define SIOCSIWFREQ 0x8B04 /* set channel/frequency */
+#define SIOCGIWFREQ 0x8B05 /* get channel/frequency */
+#define SIOCSIWMODE 0x8B06 /* set operation mode */
+#define SIOCGIWMODE 0x8B07 /* get operation mode */
+#define SIOCSIWSENS 0x8B08 /* set sensitivity */
+#define SIOCGIWSENS 0x8B09 /* get sensitivity */
+
+/* Informative stuff */
+#define SIOCSIWRANGE 0x8B0A /* Unused ??? */
+#define SIOCGIWRANGE 0x8B0B /* Get range of parameters */
+#define SIOCSIWPRIV 0x8B0C /* Unused ??? */
+#define SIOCGIWPRIV 0x8B0D /* get private ioctl interface info */
+
+/* Mobile IP support */
+#define SIOCSIWSPY 0x8B10 /* set spy addresses */
+#define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */
+
+/* Access Point manipulation */
+#define SIOCSIWAP 0x8B14 /* set access point MAC addresses */
+#define SIOCGIWAP 0x8B15 /* get access point MAC addresses */
+#define SIOCGIWAPLIST 0x8B17 /* get list of access point in range */
+
+/* 802.11 specific support */
+#define SIOCSIWESSID 0x8B1A /* set ESSID (network name) */
+#define SIOCGIWESSID 0x8B1B /* get ESSID */
+#define SIOCSIWNICKN 0x8B1C /* set node name/nickname */
+#define SIOCGIWNICKN 0x8B1D /* get node name/nickname */
+/* As the ESSID and NICKN are strings up to 32 bytes long, it doesn't fit
+ * within the 'iwreq' structure, so we need to use the 'data' member to
+ * point to a string in user space, like it is done for RANGE...
+ * The "flags" member indicate if the ESSID is active or not (promiscuous).
+ */
+
+/* Other parameters useful in 802.11 and some other devices */
+#define SIOCSIWRATE 0x8B20 /* set default bit rate (bps) */
+#define SIOCGIWRATE 0x8B21 /* get default bit rate (bps) */
+#define SIOCSIWRTS 0x8B22 /* set RTS/CTS threshold (bytes) */
+#define SIOCGIWRTS 0x8B23 /* get RTS/CTS threshold (bytes) */
+#define SIOCSIWFRAG 0x8B24 /* set fragmentation thr (bytes) */
+#define SIOCGIWFRAG 0x8B25 /* get fragmentation thr (bytes) */
+
+/* Encoding stuff (scrambling, hardware security, WEP...) */
+#define SIOCSIWENCODE 0x8B2A /* set encoding token & mode */
+#define SIOCGIWENCODE 0x8B2B /* get encoding token & mode */
+/* Power saving stuff (power management, unicast and multicast) */
+#define SIOCSIWPOWER 0x8B2C /* set Power Management settings */
+#define SIOCGIWPOWER 0x8B2D /* get Power Management settings */
+
+/* ------------------------- IOCTL STUFF ------------------------- */
+
+/* The first and the last (range) */
+#define SIOCIWFIRST 0x8B00
+#define SIOCIWLAST 0x8B30
+
+/* Even : get (world access), odd : set (root access) */
+#define IW_IS_SET(cmd) (!((cmd) & 0x1))
+#define IW_IS_GET(cmd) ((cmd) & 0x1)
+
+/* ------------------------- PRIVATE INFO ------------------------- */
+/*
+ * The following is used with SIOCGIWPRIV. It allow a driver to define
+ * the interface (name, type of data) for its private ioctl.
+ * Privates ioctl are SIOCDEVPRIVATE -> SIOCDEVPRIVATE + 0xF
+ */
+
+#define IW_PRIV_TYPE_MASK 0x7000 /* Type of arguments */
+#define IW_PRIV_TYPE_NONE 0x0000
+#define IW_PRIV_TYPE_BYTE 0x1000 /* Char as number */
+#define IW_PRIV_TYPE_CHAR 0x2000 /* Char as character */
+#define IW_PRIV_TYPE_INT 0x4000 /* 32 bits int */
+#define IW_PRIV_TYPE_FLOAT 0x5000
+
+#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed nuber of args */
+
+#define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */
+
+/*
+ * Note : if the number of args is fixed and the size < 16 octets,
+ * instead of passing a pointer we will put args in the iwreq struct...
+ */
+
+/* ----------------------- OTHER CONSTANTS ----------------------- */
+
+/* Maximum frequencies in the range struct */
+#define IW_MAX_FREQUENCIES 16
+/* Note : if you have something like 80 frequencies,
+ * don't increase this constant and don't fill the frequency list.
+ * The user will be able to set by channel anyway... */
+
+/* Maximum bit rates in the range struct */
+#define IW_MAX_BITRATES 8
+
+/* Maximum of address that you may set with SPY */
+#define IW_MAX_SPY 8
+
+/* Maximum of address that you may get in the
+ list of access points in range */
+#define IW_MAX_AP 8
+
+/* Maximum size of the ESSID and NICKN strings */
+#define IW_ESSID_MAX_SIZE 32
+
+/* Modes of operation */
+#define IW_MODE_AUTO 0 /* Let the driver decides */
+#define IW_MODE_ADHOC 1 /* Single cell network */
+#define IW_MODE_INFRA 2 /* Multi cell network, roaming, ... */
+#define IW_MODE_MASTER 3 /* Synchronisation master or Access Point */
+#define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */
+#define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */
+
+/* Maximum number of size of encoding token available
+ * they are listed in the range structure */
+#define IW_MAX_ENCODING_SIZES 8
+
+/* Maximum size of the encoding token in bytes */
+#define IW_ENCODING_TOKEN_MAX 32 /* 256 bits (for now) */
+
+/* Flags for encoding (along with the token) */
+#define IW_ENCODE_INDEX 0x00FF /* Token index (if needed) */
+#define IW_ENCODE_FLAGS 0xF000 /* Flags defined below */
+#define IW_ENCODE_DISABLED 0x8000 /* Encoding disabled */
+#define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */
+#define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */
+#define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */
+
+/* Power management flags available (along with the value, if any) */
+#define IW_POWER_ON 0x0000 /* No details... */
+#define IW_POWER_TYPE 0xF000 /* Type of parameter */
+#define IW_POWER_PERIOD 0x1000 /* Value is a period/duration of */
+#define IW_POWER_TIMEOUT 0x2000 /* Value is a timeout (to go asleep) */
+#define IW_POWER_MODE 0x0F00 /* Power Management mode */
+#define IW_POWER_UNICAST_R 0x0100 /* Receive only unicast messages */
+#define IW_POWER_MULTICAST_R 0x0200 /* Receive only multicast messages */
+#define IW_POWER_ALL_R 0x0300 /* Receive all messages though PM */
+#define IW_POWER_FORCE_S 0x0400 /* Force PM procedure for sending unicast */
+#define IW_POWER_REPEATER 0x0800 /* Repeat broadcast messages in PM period */
+
+/****************************** TYPES ******************************/
+
+/* --------------------------- SUBTYPES --------------------------- */
+/*
+ * Generic format for most parameters that fit in an int
+ */
+struct iw_param
+{
+ __s32 value; /* The value of the parameter itself */
+ __u8 fixed; /* Hardware should not use auto select */
+ __u8 disabled; /* Disable the feature */
+ __u16 flags; /* Various specifc flags (if any) */
+};
+
+/*
+ * For all data larger than 16 octets, we need to use a
+ * pointer to memory alocated in user space.
+ */
+struct iw_point
+{
+ caddr_t pointer; /* Pointer to the data (in user space) */
+ __u16 length; /* number of fields or size in bytes */
+ __u16 flags; /* Optional params */
+};
+
+/*
+ * A frequency
+ * For numbers lower than 10^9, we encode the number in 'm' and
+ * set 'e' to 0
+ * For number greater than 10^9, we divide it by the lowest power
+ * of 10 to get 'm' lower than 10^9, with 'm'= f / (10^'e')...
+ * The power of 10 is in 'e', the result of the division is in 'm'.
+ */
+struct iw_freq
+{
+ __u32 m; /* Mantissa */
+ __u16 e; /* Exponent */
+ __u8 i; /* List index (when in range struct) */
+};
+
+/*
+ * Quality of the link
+ */
+struct iw_quality
+{
+ __u8 qual; /* link quality (%retries, SNR or better...) */
+ __u8 level; /* signal level */
+ __u8 noise; /* noise level */
+ __u8 updated; /* Flags to know if updated */
+};
+
+/*
+ * Packet discarded in the wireless adapter due to
+ * "wireless" specific problems...
+ */
+struct iw_discarded
+{
+ __u32 nwid; /* Wrong nwid */
+ __u32 code; /* Unable to code/decode */
+ __u32 misc; /* Others cases */
+};
+
+/* ------------------------ WIRELESS STATS ------------------------ */
+/*
+ * Wireless statistics (used for /proc/net/wireless)
+ */
+struct iw_statistics
+{
+ __u16 status; /* Status
+ * - device dependent for now */
+
+ struct iw_quality qual; /* Quality of the link
+ * (instant/mean/max) */
+ struct iw_discarded discard; /* Packet discarded counts */
+};
+
+/* ------------------------ IOCTL REQUEST ------------------------ */
+/*
+ * The structure to exchange data for ioctl.
+ * This structure is the same as 'struct ifreq', but (re)defined for
+ * convenience...
+ *
+ * Note that it should fit on the same memory footprint !
+ * You should check this when increasing the above structures (16 octets)
+ * 16 octets = 128 bits. Warning, pointers might be 64 bits wide...
+ */
+struct iwreq
+{
+ union
+ {
+ char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */
+ } ifr_ifrn;
+
+ /* Data part */
+ union
+ {
+ /* Config - generic */
+ char name[IFNAMSIZ];
+ /* Name : used to verify the presence of wireless extensions.
+ * Name of the protocol/provider... */
+
+ struct iw_point essid; /* Extended network name */
+ struct iw_param nwid; /* network id (or domain - the cell) */
+ struct iw_freq freq; /* frequency or channel :
+ * 0-1000 = channel
+ * > 1000 = frequency in Hz */
+
+ struct iw_param sens; /* signal level threshold */
+ struct iw_param bitrate; /* default bit rate */
+ struct iw_param rts; /* RTS threshold threshold */
+ struct iw_param frag; /* Fragmentation threshold */
+ __u32 mode; /* Operation mode */
+
+ struct iw_point encoding; /* Encoding stuff : tokens */
+ struct iw_param power; /* PM duration/timeout */
+
+ struct sockaddr ap_addr; /* Access point address */
+
+ struct iw_point data; /* Other large parameters */
+ } u;
+};
+
+/* -------------------------- IOCTL DATA -------------------------- */
+/*
+ * For those ioctl which want to exchange mode data that what could
+ * fit in the above structure...
+ */
+
+/*
+ * Range of parameters
+ */
+
+struct iw_range
+{
+ /* Informative stuff (to choose between different interface) */
+ __u32 throughput; /* To give an idea... */
+ /* In theory this value should be the maximum benchmarked
+ * TCP/IP throughput, because with most of these devices the
+ * bit rate is meaningless (overhead an co) to estimate how
+ * fast the connection will go and pick the fastest one.
+ * I suggest people to play with Netperf or any benchmark...
+ */
+
+ /* NWID (or domain id) */
+ __u32 min_nwid; /* Minimal NWID we are able to set */
+ __u32 max_nwid; /* Maximal NWID we are able to set */
+
+ /* Frequency */
+ __u16 num_channels; /* Number of channels [0; num - 1] */
+ __u8 num_frequency; /* Number of entry in the list */
+ struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */
+ /* Note : this frequency list doesn't need to fit channel numbers */
+
+ /* signal level threshold range */
+ __s32 sensitivity;
+
+ /* Quality of link & SNR stuff */
+ struct iw_quality max_qual; /* Quality of the link */
+
+ /* Rates */
+ __u8 num_bitrates; /* Number of entries in the list */
+ __s32 bitrate[IW_MAX_BITRATES]; /* list, in bps */
+
+ /* RTS threshold */
+ __s32 min_rts; /* Minimal RTS threshold */
+ __s32 max_rts; /* Maximal RTS threshold */
+
+ /* Frag threshold */
+ __s32 min_frag; /* Minimal frag threshold */
+ __s32 max_frag; /* Maximal frag threshold */
+
+ /* Power Management duration & timeout */
+ __s32 min_pmd; /* Minimal PM duration */
+ __s32 max_pmd; /* Maximal PM duration */
+ __s32 min_pmt; /* Minimal PM timeout */
+ __s32 max_pmt; /* Maximal PM timeout */
+
+ /* Encoder stuff */
+ __u16 encoding_size[IW_MAX_ENCODING_SIZES]; /* Different token sizes */
+ __u8 num_encoding_sizes; /* Number of entry in the list */
+ __u8 max_encoding_tokens; /* Max number of tokens */
+};
+
+/*
+ * Private ioctl interface information
+ */
+
+struct iw_priv_args
+{
+ __u32 cmd; /* Number of the ioctl to issue */
+ __u16 set_args; /* Type and number of args */
+ __u16 get_args; /* Type and number of args */
+ char name[IFNAMSIZ]; /* Name of the extension */
+};
+
+#endif /* _LINUX_WIRELESS_H */
diff --git a/pfinet/linux-src/include/linux/wrapper.h b/pfinet/linux-src/include/linux/wrapper.h
new file mode 100644
index 00000000..d8a73117
--- /dev/null
+++ b/pfinet/linux-src/include/linux/wrapper.h
@@ -0,0 +1,40 @@
+#ifndef _WRAPPER_H_
+#define _WRAPPER_H_
+#define wait_handle struct wait_queue
+#define file_handle struct file
+#define inode_handle struct inode
+#define select_table_handle select_table
+#define vm_area_handle struct vm_area_struct
+#define file_operation_handle file_operations
+
+#define connect_wrapper(x) 0
+#define current_got_fatal_signal() (signal_pending(current))
+#define current_set_timeout(val) current->timeout = val
+
+#define module_interruptible_sleep_on interruptible_sleep_on
+#define module_wake_up wake_up
+#define module_select_wait select_wait
+#define module_register_chrdev register_chrdev
+#define module_unregister_chrdev unregister_chrdev
+#define module_register_blkdev register_blkdev
+#define module_unregister_blkdev unregister_blkdev
+
+#define inode_get_rdev(i) i->i_rdev
+#define inode_get_count(i) i->i_count
+#define inode_inc_count(i) i->i_count++
+#define inode_dec_count(i) i->i_count--
+
+#define file_get_flags(f) f->f_flags
+
+#define vma_set_inode(v,i) v->vm_inode = i
+#define vma_get_flags(v) v->vm_flags
+#define vma_get_offset(v) v->vm_offset
+#define vma_get_start(v) v->vm_start
+#define vma_get_end(v) v->vm_end
+#define vma_get_page_prot(v) v->vm_page_prot
+
+#define mem_map_reserve(p) set_bit(PG_reserved, &mem_map[p].flags)
+#define mem_map_unreserve(p) clear_bit(PG_reserved, &mem_map[p].flags)
+#define mem_map_inc_count(p) atomic_inc(&(mem_map[p].count))
+#define mem_map_dec_count(p) atomic_dec(&(mem_map[p].count))
+#endif
diff --git a/pfinet/linux-src/include/linux/x25.h b/pfinet/linux-src/include/linux/x25.h
new file mode 100644
index 00000000..6f3f300b
--- /dev/null
+++ b/pfinet/linux-src/include/linux/x25.h
@@ -0,0 +1,93 @@
+/*
+ * These are the public elements of the Linux kernel X.25 implementation.
+ */
+
+#ifndef X25_KERNEL_H
+#define X25_KERNEL_H
+
+#define SIOCX25GSUBSCRIP (SIOCPROTOPRIVATE + 0)
+#define SIOCX25SSUBSCRIP (SIOCPROTOPRIVATE + 1)
+#define SIOCX25GFACILITIES (SIOCPROTOPRIVATE + 2)
+#define SIOCX25SFACILITIES (SIOCPROTOPRIVATE + 3)
+#define SIOCX25GCALLUSERDATA (SIOCPROTOPRIVATE + 4)
+#define SIOCX25SCALLUSERDATA (SIOCPROTOPRIVATE + 5)
+#define SIOCX25GCAUSEDIAG (SIOCPROTOPRIVATE + 6)
+
+/*
+ * Values for {get,set}sockopt.
+ */
+#define X25_QBITINCL 1
+
+/*
+ * X.25 Packet Size values.
+ */
+#define X25_PS16 4
+#define X25_PS32 5
+#define X25_PS64 6
+#define X25_PS128 7
+#define X25_PS256 8
+#define X25_PS512 9
+#define X25_PS1024 10
+#define X25_PS2048 11
+#define X25_PS4096 12
+
+/*
+ * An X.121 address, it is held as ASCII text, null terminated, up to 15
+ * digits and a null terminator.
+ */
+typedef struct {
+ char x25_addr[16];
+} x25_address;
+
+/*
+ * Linux X.25 Address structure, used for bind, and connect mostly.
+ */
+struct sockaddr_x25 {
+ sa_family_t sx25_family; /* Must be AF_X25 */
+ x25_address sx25_addr; /* X.121 Address */
+};
+
+/*
+ * DTE/DCE subscription options.
+ */
+struct x25_subscrip_struct {
+ char device[200];
+ unsigned int extended;
+};
+
+/*
+ * Routing table control structure.
+ */
+struct x25_route_struct {
+ x25_address address;
+ unsigned int sigdigits;
+ char device[200];
+};
+
+/*
+ * Facilities structure.
+ */
+struct x25_facilities {
+ unsigned int winsize_in, winsize_out;
+ unsigned int pacsize_in, pacsize_out;
+ unsigned int throughput;
+ unsigned int reverse;
+};
+
+/*
+ * Call User Data structure.
+ */
+struct x25_calluserdata {
+ unsigned int cudlength;
+ unsigned char cuddata[128];
+};
+
+/*
+ * Call clearing Cause and Diagnostic structure.
+ */
+struct x25_causediag {
+ unsigned char cause;
+ unsigned char diagnostic;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/linux/yam.h b/pfinet/linux-src/include/linux/yam.h
new file mode 100644
index 00000000..7fe28228
--- /dev/null
+++ b/pfinet/linux-src/include/linux/yam.h
@@ -0,0 +1,82 @@
+/*****************************************************************************/
+
+/*
+ * yam.h -- YAM radio modem driver.
+ *
+ * Copyright (C) 1998 Frederic Rible F1OAT (frible@teaser.fr)
+ * Adapted from baycom.c driver written by Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Please note that the GPL allows you to use the driver, NOT the radio.
+ * In order to use the radio, you need a license from the communications
+ * authority of your country.
+ *
+ *
+ */
+
+/*****************************************************************************/
+
+#define SIOCYAMRESERVED (0)
+#define SIOCYAMSCFG (1) /* Set configuration */
+#define SIOCYAMGCFG (2) /* Get configuration */
+#define SIOCYAMSMCS (3) /* Set mcs data */
+
+#define YAM_IOBASE (1 << 0)
+#define YAM_IRQ (1 << 1)
+#define YAM_BITRATE (1 << 2) /* Bit rate of radio port ->57600 */
+#define YAM_MODE (1 << 3) /* 0=simplex 1=duplex 2=duplex+tempo */
+#define YAM_HOLDDLY (1 << 4) /* duplex tempo (sec) */
+#define YAM_TXDELAY (1 << 5) /* Tx Delay (ms) */
+#define YAM_TXTAIL (1 << 6) /* Tx Tail (ms) */
+#define YAM_PERSIST (1 << 7) /* Persist (ms) */
+#define YAM_SLOTTIME (1 << 8) /* Slottime (ms) */
+#define YAM_BAUDRATE (1 << 9) /* Baud rate of rs232 port ->115200 */
+
+#define YAM_MAXBITRATE 57600
+#define YAM_MAXBAUDRATE 115200
+#define YAM_MAXMODE 2
+#define YAM_MAXHOLDDLY 99
+#define YAM_MAXTXDELAY 999
+#define YAM_MAXTXTAIL 999
+#define YAM_MAXPERSIST 255
+#define YAM_MAXSLOTTIME 999
+
+#define YAM_FPGA_SIZE 5302
+
+struct yamcfg {
+ unsigned int mask; /* Mask of commands */
+ unsigned int iobase; /* IO Base of COM port */
+ unsigned int irq; /* IRQ of COM port */
+ unsigned int bitrate; /* Bit rate of radio port */
+ unsigned int baudrate; /* Baud rate of the RS232 port */
+ unsigned int txdelay; /* TxDelay */
+ unsigned int txtail; /* TxTail */
+ unsigned int persist; /* Persistence */
+ unsigned int slottime; /* Slottime */
+ unsigned int mode; /* mode 0 (simp), 1(Dupl), 2(Dupl+delay) */
+ unsigned int holddly; /* PTT delay in FullDuplex 2 mode */
+};
+
+struct yamdrv_ioctl_cfg {
+ int cmd;
+ struct yamcfg cfg;
+};
+
+struct yamdrv_ioctl_mcs {
+ int cmd;
+ int bitrate;
+ unsigned char bits[YAM_FPGA_SIZE];
+};
diff --git a/pfinet/linux-src/include/linux/zftape.h b/pfinet/linux-src/include/linux/zftape.h
new file mode 100644
index 00000000..ec5d2ffe
--- /dev/null
+++ b/pfinet/linux-src/include/linux/zftape.h
@@ -0,0 +1,87 @@
+#ifndef _ZFTAPE_H
+#define _ZFTAPE_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/include/linux/zftape.h,v $
+ * $Revision: 1.12 $
+ * $Date: 1997/10/21 11:02:37 $
+ *
+ * Special ioctl and other global info for the zftape VFS
+ * interface for the QIC-40/80/3010/3020 floppy-tape driver for
+ * Linux.
+ */
+
+#define ZFTAPE_VERSION "zftape for " FTAPE_VERSION
+
+#include <linux/ftape.h>
+
+#define ZFTAPE_LABEL "Ftape - The Linux Floppy Tape Project!"
+
+/* Bits of the minor device number that control the operation mode */
+#define ZFT_Q80_MODE (1 << 3)
+#define ZFT_ZIP_MODE (1 << 4)
+#define ZFT_RAW_MODE (1 << 5)
+#define ZFT_MINOR_OP_MASK (ZFT_Q80_MODE | \
+ ZFT_ZIP_MODE | \
+ ZFT_RAW_MODE)
+#define ZFT_MINOR_MASK (FTAPE_SEL_MASK | \
+ ZFT_MINOR_OP_MASK | \
+ FTAPE_NO_REWIND)
+
+#ifdef ZFT_OBSOLETE
+struct mtblksz {
+ unsigned int mt_blksz;
+};
+#define MTIOC_ZFTAPE_GETBLKSZ _IOR('m', 104, struct mtblksz)
+#endif
+
+#ifdef __KERNEL__
+
+extern int zft_init(void);
+
+extern inline __s64 zft_div_blksz(__s64 value, __u32 blk_sz)
+{
+ if (blk_sz == 1) {
+ return value;
+ } else {
+ return (__s64)(((__u32)(value >> 10) + (blk_sz >> 10) - 1)
+ / (blk_sz >> 10));
+ }
+}
+
+extern inline __s64 zft_mul_blksz(__s64 value, __u32 blk_sz)
+{
+ if (blk_sz == 1) {
+ return value;
+ } else {
+ /* if blk_sz != 1, then it is a multiple of 1024. In
+ * this case, `value' will also fit into 32 bits.
+ *
+ * Actually, this limits the capacity to 42
+ * bits. This is (2^32)*1024, roughly a thousand
+ * times 2GB, or 3 Terabytes. Hopefully this is enough
+ */
+ return(__s64)(((__u32)(value)*(blk_sz>>10))<<10);
+ }
+}
+
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/linux/zorro.h b/pfinet/linux-src/include/linux/zorro.h
new file mode 100644
index 00000000..9ec9ca71
--- /dev/null
+++ b/pfinet/linux-src/include/linux/zorro.h
@@ -0,0 +1,736 @@
+/*
+ * linux/zorro.h -- Amiga AutoConfig (Zorro) Bus Definitions
+ *
+ * Copyright (C) 1995-1998 Geert Uytterhoeven
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _LINUX_ZORRO_H
+#define _LINUX_ZORRO_H
+
+#ifndef __ASSEMBLY__
+
+ /*
+ * Known Zorro Boards
+ *
+ * Each Zorro board has a 32-bit ID of the form
+ *
+ * mmmmmmmmmmmmmmmmppppppppeeeeeeee
+ *
+ * with
+ *
+ * mmmmmmmmmmmmmmmm 16-bit Manufacturer ID (assigned by CBM (sigh))
+ * pppppppp 8-bit Product ID (assigned by manufacturer)
+ * eeeeeeee 8-bit Extended Product ID (currently only used
+ * for some GVP boards)
+ */
+
+
+#define ZORRO_MANUF(id) ((id) >> 16)
+#define ZORRO_PROD(id) (((id) >> 8) & 0xff)
+#define ZORRO_EPC(id) ((id) & 0xff)
+
+#define ZORRO_ID(manuf, prod, epc) \
+ ((ZORRO_MANUF_##manuf << 16) | ((prod) << 8) | (epc))
+
+typedef __u32 zorro_id;
+
+
+#define ZORRO_MANUF_PACIFIC_PERIPHERALS 0x00D3
+#define ZORRO_PROD_PACIFIC_PERIPHERALS_SE_2000_A500 ZORRO_ID(PACIFIC_PERIPHERALS, 0x00, 0)
+#define ZORRO_PROD_PACIFIC_PERIPHERALS_SCSI ZORRO_ID(PACIFIC_PERIPHERALS, 0x0A, 0)
+
+#define ZORRO_MANUF_MACROSYSTEMS_USA_2 0x0100
+#define ZORRO_PROD_MACROSYSTEMS_WARP_ENGINE ZORRO_ID(MACROSYSTEMS_USA_2, 0x13, 0)
+
+#define ZORRO_MANUF_KUPKE_1 0x00DD
+#define ZORRO_PROD_KUPKE_GOLEM_RAM_BOX_2MB ZORRO_ID(KUPKE_1, 0x00, 0)
+
+#define ZORRO_MANUF_MEMPHIS 0x0100
+#define ZORRO_PROD_MEMPHIS_STORMBRINGER ZORRO_ID(MEMPHIS, 0x00, 0)
+
+#define ZORRO_MANUF_3_STATE 0x0200
+#define ZORRO_PROD_3_STATE_MEGAMIX_2000 ZORRO_ID(3_STATE, 0x02, 0)
+
+#define ZORRO_MANUF_COMMODORE_BRAUNSCHWEIG 0x0201
+#define ZORRO_PROD_CBM_A2088_A2286 ZORRO_ID(COMMODORE_BRAUNSCHWEIG, 0x01, 0)
+#define ZORRO_PROD_CBM_A2286 ZORRO_ID(COMMODORE_BRAUNSCHWEIG, 0x02, 0)
+#define ZORRO_PROD_CBM_A4091_1 ZORRO_ID(COMMODORE_BRAUNSCHWEIG, 0x54, 0)
+#define ZORRO_PROD_CBM_A2386SX_1 ZORRO_ID(COMMODORE_BRAUNSCHWEIG, 0x67, 0)
+
+#define ZORRO_MANUF_COMMODORE_WEST_CHESTER_1 0x0202
+#define ZORRO_PROD_CBM_A2090A ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x01, 0)
+#define ZORRO_PROD_CBM_A590_A2091_1 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x02, 0)
+#define ZORRO_PROD_CBM_A590_A2091_2 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x03, 0)
+#define ZORRO_PROD_CBM_A2090B ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x04, 0)
+#define ZORRO_PROD_CBM_A2060 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x09, 0)
+#define ZORRO_PROD_CBM_A590_A2052_A2058_A2091 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x0A, 0)
+#define ZORRO_PROD_CBM_A560_RAM ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x20, 0)
+#define ZORRO_PROD_CBM_A2232_PROTOTYPE ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x45, 0)
+#define ZORRO_PROD_CBM_A2232 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x46, 0)
+#define ZORRO_PROD_CBM_A2620 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x50, 0)
+#define ZORRO_PROD_CBM_A2630 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x51, 0)
+#define ZORRO_PROD_CBM_A4091_2 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x54, 0)
+#define ZORRO_PROD_CBM_A2065_1 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x5A, 0)
+#define ZORRO_PROD_CBM_ROMULATOR ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x60, 0)
+#define ZORRO_PROD_CBM_A3000_TEST_FIXTURE ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x61, 0)
+#define ZORRO_PROD_CBM_A2386SX_2 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x67, 0)
+#define ZORRO_PROD_CBM_A2065_2 ZORRO_ID(COMMODORE_WEST_CHESTER_1, 0x70, 0)
+
+#define ZORRO_MANUF_COMMODORE_WEST_CHESTER_2 0x0203
+#define ZORRO_PROD_CBM_A2090A_CM ZORRO_ID(COMMODORE_WEST_CHESTER_2, 0x03, 0)
+
+#define ZORRO_MANUF_PROGRESSIVE_PERIPHERALS_AND_SYSTEMS_2 0x02F4
+#define ZORRO_PROD_PPS_EXP8000 ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS_2, 0x02, 0)
+
+#define ZORRO_MANUF_KOLFF_COMPUTER_SUPPLIES 0x02FF
+#define ZORRO_PROD_KCS_POWER_PC_BOARD ZORRO_ID(KOLFF_COMPUTER_SUPPLIES, 0x00, 0)
+
+#define ZORRO_MANUF_CARDCO_1 0x03EC
+#define ZORRO_PROD_CARDCO_KRONOS_2000_1 ZORRO_ID(CARDCO_1, 0x04, 0)
+#define ZORRO_PROD_CARDCO_A1000_1 ZORRO_ID(CARDCO_1, 0x0C, 0)
+#define ZORRO_PROD_CARDCO_ESCORT ZORRO_ID(CARDCO_1, 0x0E, 0)
+#define ZORRO_PROD_CARDCO_A2410 ZORRO_ID(CARDCO_1, 0xF5, 0)
+
+#define ZORRO_MANUF_A_SQUARED 0x03ED
+#define ZORRO_PROD_A_SQUARED_LIVE_2000 ZORRO_ID(A_SQUARED, 0x01, 0)
+
+#define ZORRO_MANUF_COMSPEC_COMMUNICATIONS 0x03EE
+#define ZORRO_PROD_COMSPEC_COMMUNICATIONS_AX2000 ZORRO_ID(COMSPEC_COMMUNICATIONS, 0x01, 0)
+
+#define ZORRO_MANUF_ANAKIN_RESEARCH 0x03F1
+#define ZORRO_PROD_ANAKIN_RESEARCH_EASYL ZORRO_ID(ANAKIN_RESEARCH, 0x01, 0)
+
+#define ZORRO_MANUF_MICROBOTICS 0x03F2
+#define ZORRO_PROD_MICROBOTICS_STARBOARD_II ZORRO_ID(MICROBOTICS, 0x00, 0)
+#define ZORRO_PROD_MICROBOTICS_STARDRIVE ZORRO_ID(MICROBOTICS, 0x02, 0)
+#define ZORRO_PROD_MICROBOTICS_8_UP_A ZORRO_ID(MICROBOTICS, 0x03, 0)
+#define ZORRO_PROD_MICROBOTICS_8_UP_Z ZORRO_ID(MICROBOTICS, 0x04, 0)
+#define ZORRO_PROD_MICROBOTICS_DELTA_RAM ZORRO_ID(MICROBOTICS, 0x20, 0)
+#define ZORRO_PROD_MICROBOTICS_8_STAR_RAM ZORRO_ID(MICROBOTICS, 0x40, 0)
+#define ZORRO_PROD_MICROBOTICS_8_STAR ZORRO_ID(MICROBOTICS, 0x41, 0)
+#define ZORRO_PROD_MICROBOTICS_VXL_RAM_32 ZORRO_ID(MICROBOTICS, 0x44, 0)
+#define ZORRO_PROD_MICROBOTICS_VXL_68030 ZORRO_ID(MICROBOTICS, 0x45, 0)
+#define ZORRO_PROD_MICROBOTICS_DELTA ZORRO_ID(MICROBOTICS, 0x60, 0)
+#define ZORRO_PROD_MICROBOTICS_MBX_1200_1200Z_RAM ZORRO_ID(MICROBOTICS, 0x81, 0)
+#define ZORRO_PROD_MICROBOTICS_HARDFRAME_2000_1 ZORRO_ID(MICROBOTICS, 0x96, 0)
+#define ZORRO_PROD_MICROBOTICS_HARDFRAME_2000_2 ZORRO_ID(MICROBOTICS, 0x9E, 0)
+#define ZORRO_PROD_MICROBOTICS_MBX_1200_1200Z ZORRO_ID(MICROBOTICS, 0xC1, 0)
+
+#define ZORRO_MANUF_ACCESS_ASSOCIATES_ALEGRA 0x03F4
+
+#define ZORRO_MANUF_EXPANSION_TECHNOLOGIES 0x03F6
+
+#define ZORRO_MANUF_ASDG 0x03FF
+#define ZORRO_PROD_ASDG_MEMORY_1 ZORRO_ID(ASDG, 0x01, 0)
+#define ZORRO_PROD_ASDG_MEMORY_2 ZORRO_ID(ASDG, 0x02, 0)
+#define ZORRO_PROD_ASDG_EB920_LAN_ROVER ZORRO_ID(ASDG, 0xFE, 0)
+#define ZORRO_PROD_ASDG_GPIB_DUALIEEE488_TWIN_X ZORRO_ID(ASDG, 0xFF, 0)
+
+#define ZORRO_MANUF_IMTRONICS_1 0x0404
+#define ZORRO_PROD_IMTRONICS_HURRICANE_2800_1 ZORRO_ID(IMTRONICS_1, 0x39, 0)
+#define ZORRO_PROD_IMTRONICS_HURRICANE_2800_2 ZORRO_ID(IMTRONICS_1, 0x57, 0)
+
+#define ZORRO_MANUF_CBM_UNIVERSITY_OF_LOWELL 0x0406
+#define ZORRO_PROD_CBM_A2410 ZORRO_ID(CBM_UNIVERSITY_OF_LOWELL, 0x00, 0)
+
+#define ZORRO_MANUF_AMERISTAR 0x041D
+#define ZORRO_PROD_AMERISTAR_A2065 ZORRO_ID(AMERISTAR, 0x01, 0)
+#define ZORRO_PROD_AMERISTAR_A560 ZORRO_ID(AMERISTAR, 0x09, 0)
+#define ZORRO_PROD_AMERISTAR_A4066 ZORRO_ID(AMERISTAR, 0x0A, 0)
+
+#define ZORRO_MANUF_SUPRA 0x0420
+#define ZORRO_PROD_SUPRA_SUPRADRIVE_4x4 ZORRO_ID(SUPRA, 0x01, 0)
+#define ZORRO_PROD_SUPRA_1000_RAM ZORRO_ID(SUPRA, 0x02, 0)
+#define ZORRO_PROD_SUPRA_2000_DMA ZORRO_ID(SUPRA, 0x03, 0)
+#define ZORRO_PROD_SUPRA_500 ZORRO_ID(SUPRA, 0x05, 0)
+#define ZORRO_PROD_SUPRA_500_SCSI ZORRO_ID(SUPRA, 0x08, 0)
+#define ZORRO_PROD_SUPRA_500XP_2000_RAM ZORRO_ID(SUPRA, 0x09, 0)
+#define ZORRO_PROD_SUPRA_500RX_2000_RAM ZORRO_ID(SUPRA, 0x0A, 0)
+#define ZORRO_PROD_SUPRA_2400ZI ZORRO_ID(SUPRA, 0x0B, 0)
+#define ZORRO_PROD_SUPRA_500XP_SUPRADRIVE_WORDSYNC ZORRO_ID(SUPRA, 0x0C, 0)
+#define ZORRO_PROD_SUPRA_SUPRADRIVE_WORDSYNC_II ZORRO_ID(SUPRA, 0x0D, 0)
+#define ZORRO_PROD_SUPRA_2400ZIPLUS ZORRO_ID(SUPRA, 0x10, 0)
+
+#define ZORRO_MANUF_COMPUTER_SYSTEMS_ASSOCIATES 0x0422
+#define ZORRO_PROD_CSA_MAGNUM ZORRO_ID(COMPUTER_SYSTEMS_ASSOCIATES, 0x11, 0)
+#define ZORRO_PROD_CSA_12_GAUGE ZORRO_ID(COMPUTER_SYSTEMS_ASSOCIATES, 0x15, 0)
+
+#define ZORRO_MANUF_MARC_MICHAEL_GROTH 0x0439
+
+#define ZORRO_MANUF_M_TECH 0x0502
+#define ZORRO_PROD_MTEC_AT500_1 ZORRO_ID(M_TECH, 0x03, 0)
+
+#define ZORRO_MANUF_GREAT_VALLEY_PRODUCTS_1 0x06E1
+#define ZORRO_PROD_GVP_IMPACT_SERIES_I ZORRO_ID(GREAT_VALLEY_PRODUCTS_1, 0x08, 0)
+
+#define ZORRO_MANUF_BYTEBOX 0x07DA
+#define ZORRO_PROD_BYTEBOX_A500 ZORRO_ID(BYTEBOX, 0x00, 0)
+
+#define ZORRO_MANUF_DKB_POWER_COMPUTING 0x07DC
+#define ZORRO_PROD_DKB_POWER_COMPUTING_SECUREKEY ZORRO_ID(DKB_POWER_COMPUTING, 0x09, 0)
+#define ZORRO_PROD_DKB_POWER_COMPUTING_DKM_3128 ZORRO_ID(DKB_POWER_COMPUTING, 0x0E, 0)
+#define ZORRO_PROD_DKB_POWER_COMPUTING_RAPID_FIRE ZORRO_ID(DKB_POWER_COMPUTING, 0x0F, 0)
+#define ZORRO_PROD_DKB_POWER_COMPUTING_DKM_1202 ZORRO_ID(DKB_POWER_COMPUTING, 0x10, 0)
+#define ZORRO_PROD_DKB_POWER_COMPUTING_COBRA_VIPER_II_68EC030 ZORRO_ID(DKB_POWER_COMPUTING, 0x12, 0)
+#define ZORRO_PROD_DKB_POWER_COMPUTING_WILDFIRE_060_1 ZORRO_ID(DKB_POWER_COMPUTING, 0x17, 0)
+#define ZORRO_PROD_DKB_POWER_COMPUTING_WILDFIRE_060_2 ZORRO_ID(DKB_POWER_COMPUTING, 0xFF, 0)
+
+#define ZORRO_MANUF_GREAT_VALLEY_PRODUCTS_2 0x07E1
+#define ZORRO_PROD_GVP_IMPACT_SERIES_I_4K ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x01, 0)
+#define ZORRO_PROD_GVP_IMPACT_SERIES_I_16K_2 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x02, 0)
+#define ZORRO_PROD_GVP_IMPACT_SERIES_I_16K_3 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x03, 0)
+#define ZORRO_PROD_GVP_IMPACT_3001_IDE_1 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x08, 0)
+#define ZORRO_PROD_GVP_IMPACT_3001_RAM ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x09, 0)
+#define ZORRO_PROD_GVP_IMPACT_SERIES_II_RAM_1 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0A, 0)
+#define ZORRO_PROD_GVP_EPC_BASE ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0)
+#define ZORRO_PROD_GVP_GFORCE_040_1 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x20)
+#define ZORRO_PROD_GVP_GFORCE_040_SCSI_1 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x30)
+#define ZORRO_PROD_GVP_A1291 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x40)
+#define ZORRO_PROD_GVP_COMBO_030_R4 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x60)
+#define ZORRO_PROD_GVP_COMBO_030_R4_SCSI ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x70)
+#define ZORRO_PROD_GVP_PHONEPAK ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x78)
+#define ZORRO_PROD_GVP_IO_EXTENDER ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0x98)
+#define ZORRO_PROD_GVP_GFORCE_030 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xa0)
+#define ZORRO_PROD_GVP_GFORCE_030_SCSI ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xb0)
+#define ZORRO_PROD_GVP_A530 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xc0)
+#define ZORRO_PROD_GVP_A530_SCSI ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xd0)
+#define ZORRO_PROD_GVP_COMBO_030_R3 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xe0)
+#define ZORRO_PROD_GVP_COMBO_030_R3_SCSI ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xf0)
+#define ZORRO_PROD_GVP_SERIES_II ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0B, 0xf8)
+#define ZORRO_PROD_GVP_IMPACT_3001_IDE_2 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0D, 0)
+/*#define ZORRO_PROD_GVP_A2000_030 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0D, 0)*/
+/*#define ZORRO_PROD_GVP_GFORCE_040_SCSI_2 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x0D, 0)*/
+#define ZORRO_PROD_GVP_GFORCE_040_060 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x16, 0)
+#define ZORRO_PROD_GVP_IMPACT_VISION_24 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0x20, 0)
+#define ZORRO_PROD_GVP_GFORCE_040_2 ZORRO_ID(GREAT_VALLEY_PRODUCTS_2, 0xFF, 0)
+
+#define ZORRO_MANUF_CALIFORNIA_ACCESS_SYNERGY 0x07E5
+#define ZORRO_PROD_CALIFORNIA_ACCESS_SYNERGY_MALIBU ZORRO_ID(CALIFORNIA_ACCESS_SYNERGY, 0x01, 0)
+
+#define ZORRO_MANUF_XETEC 0x07E6
+#define ZORRO_PROD_XETEC_FASTCARD ZORRO_ID(XETEC, 0x01, 0)
+#define ZORRO_PROD_XETEC_FASTCARD_RAM ZORRO_ID(XETEC, 0x02, 0)
+#define ZORRO_PROD_XETEC_FASTCARD_PLUS ZORRO_ID(XETEC, 0x03, 0)
+
+#define ZORRO_MANUF_PROGRESSIVE_PERIPHERALS_AND_SYSTEMS 0x07EA
+#define ZORRO_PROD_PPS_MERCURY ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS, 0x00, 0)
+#define ZORRO_PROD_PPS_A3000_68040 ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS, 0x01, 0)
+#define ZORRO_PROD_PPS_A2000_68040 ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS, 0x69, 0)
+#define ZORRO_PROD_PPS_ZEUS ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS, 0x96, 0)
+#define ZORRO_PROD_PPS_A500_68040 ZORRO_ID(PROGRESSIVE_PERIPHERALS_AND_SYSTEMS, 0xBB, 0)
+
+#define ZORRO_MANUF_XEBEC 0x07EC
+
+#define ZORRO_MANUF_SPIRIT_TECHNOLOGY 0x07F2
+#define ZORRO_PROD_SPIRIT_TECHNOLOGY_INSIDER_IN1000 ZORRO_ID(SPIRIT_TECHNOLOGY, 0x01, 0)
+#define ZORRO_PROD_SPIRIT_TECHNOLOGY_INSIDER_IN500 ZORRO_ID(SPIRIT_TECHNOLOGY, 0x02, 0)
+#define ZORRO_PROD_SPIRIT_TECHNOLOGY_SIN500 ZORRO_ID(SPIRIT_TECHNOLOGY, 0x03, 0)
+#define ZORRO_PROD_SPIRIT_TECHNOLOGY_HDA_506 ZORRO_ID(SPIRIT_TECHNOLOGY, 0x04, 0)
+#define ZORRO_PROD_SPIRIT_TECHNOLOGY_AX_S ZORRO_ID(SPIRIT_TECHNOLOGY, 0x05, 0)
+#define ZORRO_PROD_SPIRIT_TECHNOLOGY_OCTABYTE ZORRO_ID(SPIRIT_TECHNOLOGY, 0x06, 0)
+#define ZORRO_PROD_SPIRIT_TECHNOLOGY_INMATE ZORRO_ID(SPIRIT_TECHNOLOGY, 0x08, 0)
+
+#define ZORRO_MANUF_SPIRIT_TECHNOLOGY_2 0x07F3
+
+#define ZORRO_MANUF_BSC_ALFADATA_1 0x07FE
+#define ZORRO_PROD_BSC_ALF_3_1 ZORRO_ID(BSC_ALFADATA_1, 0x03, 0)
+
+#define ZORRO_MANUF_BSC_ALFADATA_2 0x0801
+#define ZORRO_PROD_BSC_ALF_2_1 ZORRO_ID(BSC_ALFADATA_2, 0x01, 0)
+#define ZORRO_PROD_BSC_ALF_2_2 ZORRO_ID(BSC_ALFADATA_2, 0x02, 0)
+#define ZORRO_PROD_BSC_ALF_3_2 ZORRO_ID(BSC_ALFADATA_2, 0x03, 0)
+
+#define ZORRO_MANUF_CARDCO_2 0x0802
+#define ZORRO_PROD_CARDCO_KRONOS_2000_2 ZORRO_ID(CARDCO_2, 0x04, 0)
+#define ZORRO_PROD_CARDCO_A1000_2 ZORRO_ID(CARDCO_2, 0x0C, 0)
+
+#define ZORRO_MANUF_JOCHHEIM 0x0804
+#define ZORRO_PROD_JOCHHEIM_RAM ZORRO_ID(JOCHHEIM, 0x01, 0)
+
+#define ZORRO_MANUF_CHECKPOINT_TECHNOLOGIES 0x0807
+#define ZORRO_PROD_CHECKPOINT_TECHNOLOGIES_SERIAL_SOLUTION ZORRO_ID(CHECKPOINT_TECHNOLOGIES, 0x00, 0)
+
+#define ZORRO_MANUF_EDOTRONIK 0x0810
+#define ZORRO_PROD_EDOTRONIK_IEEE_488 ZORRO_ID(EDOTRONIK, 0x01, 0)
+#define ZORRO_PROD_EDOTRONIK_8032 ZORRO_ID(EDOTRONIK, 0x02, 0)
+#define ZORRO_PROD_EDOTRONIK_MULTISERIAL ZORRO_ID(EDOTRONIK, 0x03, 0)
+#define ZORRO_PROD_EDOTRONIK_VIDEODIGITIZER ZORRO_ID(EDOTRONIK, 0x04, 0)
+#define ZORRO_PROD_EDOTRONIK_PARALLEL_IO ZORRO_ID(EDOTRONIK, 0x05, 0)
+#define ZORRO_PROD_EDOTRONIK_PIC_PROTOYPING ZORRO_ID(EDOTRONIK, 0x06, 0)
+#define ZORRO_PROD_EDOTRONIK_ADC ZORRO_ID(EDOTRONIK, 0x07, 0)
+#define ZORRO_PROD_EDOTRONIK_VME ZORRO_ID(EDOTRONIK, 0x08, 0)
+#define ZORRO_PROD_EDOTRONIK_DSP96000 ZORRO_ID(EDOTRONIK, 0x09, 0)
+
+#define ZORRO_MANUF_NES_INC 0x0813
+#define ZORRO_PROD_NES_INC_RAM ZORRO_ID(NES_INC, 0x00, 0)
+
+#define ZORRO_MANUF_ICD 0x0817
+#define ZORRO_PROD_ICD_ADVANTAGE_2000_SCSI ZORRO_ID(ICD, 0x01, 0)
+#define ZORRO_PROD_ICD_ADVANTAGE_IDE ZORRO_ID(ICD, 0x03, 0)
+#define ZORRO_PROD_ICD_ADVANTAGE_2080_RAM ZORRO_ID(ICD, 0x04, 0)
+
+#define ZORRO_MANUF_KUPKE_2 0x0819
+#define ZORRO_PROD_KUPKE_OMTI ZORRO_ID(KUPKE_2, 0x01, 0)
+#define ZORRO_PROD_KUPKE_SCSI_II ZORRO_ID(KUPKE_2, 0x02, 0)
+#define ZORRO_PROD_KUPKE_GOLEM_BOX ZORRO_ID(KUPKE_2, 0x03, 0)
+#define ZORRO_PROD_KUPKE_030_882 ZORRO_ID(KUPKE_2, 0x04, 0)
+#define ZORRO_PROD_KUPKE_SCSI_AT ZORRO_ID(KUPKE_2, 0x05, 0)
+
+#define ZORRO_MANUF_GREAT_VALLEY_PRODUCTS_3 0x081D
+#define ZORRO_PROD_GVP_A2000_RAM8 ZORRO_ID(GREAT_VALLEY_PRODUCTS_3, 0x09, 0)
+#define ZORRO_PROD_GVP_IMPACT_SERIES_II_RAM_2 ZORRO_ID(GREAT_VALLEY_PRODUCTS_3, 0x0A, 0)
+
+#define ZORRO_MANUF_INTERWORKS_NETWORK 0x081E
+
+#define ZORRO_MANUF_HARDITAL_SYNTHESIS 0x0820
+#define ZORRO_PROD_HARDITAL_SYNTHESIS_TQM_68030_68882 ZORRO_ID(HARDITAL_SYNTHESIS, 0x14, 0)
+
+#define ZORRO_MANUF_APPLIED_ENGINEERING 0x0828
+#define ZORRO_PROD_APPLIED_ENGINEERING_DL2000 ZORRO_ID(APPLIED_ENGINEERING, 0x10, 0)
+#define ZORRO_PROD_APPLIED_ENGINEERING_RAM_WORKS ZORRO_ID(APPLIED_ENGINEERING, 0xE0, 0)
+
+#define ZORRO_MANUF_BSC_ALFADATA_3 0x082C
+#define ZORRO_PROD_BSC_OKTAGON_2008 ZORRO_ID(BSC_ALFADATA_3, 0x05, 0)
+#define ZORRO_PROD_BSC_TANDEM_AT_2008_508 ZORRO_ID(BSC_ALFADATA_3, 0x06, 0)
+#define ZORRO_PROD_BSC_ALFA_RAM_1200 ZORRO_ID(BSC_ALFADATA_3, 0x07, 0)
+#define ZORRO_PROD_BSC_OKTAGON_2008_RAM ZORRO_ID(BSC_ALFADATA_3, 0x08, 0)
+#define ZORRO_PROD_BSC_MULTIFACE_I ZORRO_ID(BSC_ALFADATA_3, 0x10, 0)
+#define ZORRO_PROD_BSC_MULTIFACE_II ZORRO_ID(BSC_ALFADATA_3, 0x11, 0)
+#define ZORRO_PROD_BSC_MULTIFACE_III ZORRO_ID(BSC_ALFADATA_3, 0x12, 0)
+#define ZORRO_PROD_BSC_FRAMEMASTER_II ZORRO_ID(BSC_ALFADATA_3, 0x20, 0)
+#define ZORRO_PROD_BSC_GRAFFITI_RAM ZORRO_ID(BSC_ALFADATA_3, 0x21, 0)
+#define ZORRO_PROD_BSC_GRAFFITI_REG ZORRO_ID(BSC_ALFADATA_3, 0x22, 0)
+#define ZORRO_PROD_BSC_ISDN_MASTERCARD ZORRO_ID(BSC_ALFADATA_3, 0x40, 0)
+#define ZORRO_PROD_BSC_ISDN_MASTERCARD_II ZORRO_ID(BSC_ALFADATA_3, 0x41, 0)
+
+#define ZORRO_MANUF_PHOENIX 0x0835
+#define ZORRO_PROD_PHOENIX_ST506 ZORRO_ID(PHOENIX, 0x21, 0)
+#define ZORRO_PROD_PHOENIX_SCSI ZORRO_ID(PHOENIX, 0x22, 0)
+#define ZORRO_PROD_PHOENIX_RAM ZORRO_ID(PHOENIX, 0xBE, 0)
+
+#define ZORRO_MANUF_ADVANCED_STORAGE_SYSTEMS 0x0836
+#define ZORRO_PROD_ADVANCED_STORAGE_SYSTEMS_NEXUS ZORRO_ID(ADVANCED_STORAGE_SYSTEMS, 0x01, 0)
+#define ZORRO_PROD_ADVANCED_STORAGE_SYSTEMS_NEXUS_RAM ZORRO_ID(ADVANCED_STORAGE_SYSTEMS, 0x08, 0)
+
+#define ZORRO_MANUF_IMPULSE 0x0838
+#define ZORRO_PROD_IMPULSE_FIRECRACKER_24 ZORRO_ID(IMPULSE, 0x00, 0)
+
+#define ZORRO_MANUF_IVS 0x0840
+#define ZORRO_PROD_IVS_GRANDSLAM_PIC_2 ZORRO_ID(IVS, 0x02, 0)
+#define ZORRO_PROD_IVS_GRANDSLAM_PIC_1 ZORRO_ID(IVS, 0x04, 0)
+#define ZORRO_PROD_IVS_OVERDRIVE ZORRO_ID(IVS, 0x10, 0)
+#define ZORRO_PROD_IVS_TRUMPCARD_CLASSIC ZORRO_ID(IVS, 0x30, 0)
+#define ZORRO_PROD_IVS_TRUMPCARD_PRO_GRANDSLAM ZORRO_ID(IVS, 0x34, 0)
+#define ZORRO_PROD_IVS_META_4 ZORRO_ID(IVS, 0x40, 0)
+#define ZORRO_PROD_IVS_WAVETOOLS ZORRO_ID(IVS, 0xBF, 0)
+#define ZORRO_PROD_IVS_VECTOR_1 ZORRO_ID(IVS, 0xF3, 0)
+#define ZORRO_PROD_IVS_VECTOR_2 ZORRO_ID(IVS, 0xF4, 0)
+
+#define ZORRO_MANUF_VECTOR_1 0x0841
+#define ZORRO_PROD_VECTOR_CONNECTION_1 ZORRO_ID(VECTOR_1, 0xE3, 0)
+
+#define ZORRO_MANUF_XPERT_PRODEV 0x0845
+#define ZORRO_PROD_XPERT_PRODEV_VISIONA_RAM ZORRO_ID(XPERT_PRODEV, 0x01, 0)
+#define ZORRO_PROD_XPERT_PRODEV_VISIONA_REG ZORRO_ID(XPERT_PRODEV, 0x02, 0)
+#define ZORRO_PROD_XPERT_PRODEV_MERLIN_RAM ZORRO_ID(XPERT_PRODEV, 0x03, 0)
+#define ZORRO_PROD_XPERT_PRODEV_MERLIN_REG_1 ZORRO_ID(XPERT_PRODEV, 0x04, 0)
+#define ZORRO_PROD_XPERT_PRODEV_MERLIN_REG_2 ZORRO_ID(XPERT_PRODEV, 0xC9, 0)
+
+#define ZORRO_MANUF_HYDRA_SYSTEMS 0x0849
+#define ZORRO_PROD_HYDRA_SYSTEMS_AMIGANET ZORRO_ID(HYDRA_SYSTEMS, 0x01, 0)
+
+#define ZORRO_MANUF_SUNRIZE_INDUSTRIES 0x084F
+#define ZORRO_PROD_SUNRIZE_INDUSTRIES_AD1012 ZORRO_ID(SUNRIZE_INDUSTRIES, 0x01, 0)
+#define ZORRO_PROD_SUNRIZE_INDUSTRIES_AD516 ZORRO_ID(SUNRIZE_INDUSTRIES, 0x02, 0)
+#define ZORRO_PROD_SUNRIZE_INDUSTRIES_DD512 ZORRO_ID(SUNRIZE_INDUSTRIES, 0x03, 0)
+
+#define ZORRO_MANUF_TRICERATOPS 0x0850
+#define ZORRO_PROD_TRICERATOPS_MULTI_IO ZORRO_ID(TRICERATOPS, 0x01, 0)
+
+#define ZORRO_MANUF_APPLIED_MAGIC 0x0851
+#define ZORRO_PROD_APPLIED_MAGIC_DMI_RESOLVER ZORRO_ID(APPLIED_MAGIC, 0x01, 0)
+#define ZORRO_PROD_APPLIED_MAGIC_DIGITAL_BROADCASTER ZORRO_ID(APPLIED_MAGIC, 0x06, 0)
+
+#define ZORRO_MANUF_GFX_BASE 0x085E
+#define ZORRO_PROD_GFX_BASE_GDA_1_VRAM ZORRO_ID(GFX_BASE, 0x00, 0)
+#define ZORRO_PROD_GFX_BASE_GDA_1 ZORRO_ID(GFX_BASE, 0x01, 0)
+
+#define ZORRO_MANUF_ROCTEC 0x0860
+#define ZORRO_PROD_ROCTEC_RH_800C ZORRO_ID(ROCTEC, 0x01, 0)
+#define ZORRO_PROD_ROCTEC_RH_800C_RAM ZORRO_ID(ROCTEC, 0x01, 0)
+
+#define ZORRO_MANUF_KATO 0x0861
+#define ZORRO_PROD_KATO_MELODY ZORRO_ID(KATO, 0x80, 0)
+/* ID clash!! */
+#define ZORRO_MANUF_HELFRICH_1 0x0861
+#define ZORRO_PROD_HELFRICH_RAINBOW_II ZORRO_ID(HELFRICH_1, 0x20, 0)
+#define ZORRO_PROD_HELFRICH_RAINBOW_III ZORRO_ID(HELFRICH_1, 0x21, 0)
+
+#define ZORRO_MANUF_ATLANTIS 0x0862
+
+#define ZORRO_MANUF_PROTAR 0x0864
+
+#define ZORRO_MANUF_ACS 0x0865
+
+#define ZORRO_MANUF_SOFTWARE_RESULTS_ENTERPRISES 0x0866
+#define ZORRO_PROD_SOFTWARE_RESULTS_ENTERPRISES_GOLDEN_GATE_2_BUS_PLUS ZORRO_ID(SOFTWARE_RESULTS_ENTERPRISES, 0x01, 0)
+
+#define ZORRO_MANUF_MASOBOSHI 0x086D
+#define ZORRO_PROD_MASOBOSHI_MASTER_CARD_SC201 ZORRO_ID(MASOBOSHI, 0x03, 0)
+#define ZORRO_PROD_MASOBOSHI_MASTER_CARD_MC702 ZORRO_ID(MASOBOSHI, 0x04, 0)
+#define ZORRO_PROD_MASOBOSHI_MVD_819 ZORRO_ID(MASOBOSHI, 0x07, 0)
+
+#define ZORRO_MANUF_MAINHATTAN_DATA 0x086F
+#define ZORRO_PROD_MAINHATTAN_DATA_IDE ZORRO_ID(MAINHATTAN_DATA, 0x01, 0)
+
+#define ZORRO_MANUF_VILLAGE_TRONIC 0x0877
+#define ZORRO_PROD_VILLAGE_TRONIC_DOMINO_RAM ZORRO_ID(VILLAGE_TRONIC, 0x01, 0)
+#define ZORRO_PROD_VILLAGE_TRONIC_DOMINO_REG ZORRO_ID(VILLAGE_TRONIC, 0x02, 0)
+#define ZORRO_PROD_VILLAGE_TRONIC_DOMINO_16M_PROTOTYPE ZORRO_ID(VILLAGE_TRONIC, 0x03, 0)
+#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM ZORRO_ID(VILLAGE_TRONIC, 0x0B, 0)
+#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG ZORRO_ID(VILLAGE_TRONIC, 0x0C, 0)
+#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_SEGMENTED_MODE ZORRO_ID(VILLAGE_TRONIC, 0x0D, 0)
+#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_MEM1 ZORRO_ID(VILLAGE_TRONIC, 0x15, 0)
+#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_MEM2 ZORRO_ID(VILLAGE_TRONIC, 0x16, 0)
+#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_REG ZORRO_ID(VILLAGE_TRONIC, 0x17, 0)
+#define ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3 ZORRO_ID(VILLAGE_TRONIC, 0x18, 0)
+#define ZORRO_PROD_VILLAGE_TRONIC_ARIADNE ZORRO_ID(VILLAGE_TRONIC, 0xC9, 0)
+#define ZORRO_PROD_VILLAGE_TRONIC_ARIADNE2 ZORRO_ID(VILLAGE_TRONIC, 0xCA, 0)
+
+#define ZORRO_MANUF_UTILITIES_UNLIMITED 0x087B
+#define ZORRO_PROD_UTILITIES_UNLIMITED_EMPLANT_DELUXE ZORRO_ID(UTILITIES_UNLIMITED, 0x15, 0)
+#define ZORRO_PROD_UTILITIES_UNLIMITED_EMPLANT_DELUXE2 ZORRO_ID(UTILITIES_UNLIMITED, 0x20, 0)
+
+#define ZORRO_MANUF_AMITRIX 0x0880
+#define ZORRO_PROD_AMITRIX_MULTI_IO ZORRO_ID(AMITRIX, 0x01, 0)
+#define ZORRO_PROD_AMITRIX_CD_RAM ZORRO_ID(AMITRIX, 0x02, 0)
+
+#define ZORRO_MANUF_ARMAX 0x0885
+#define ZORRO_PROD_ARMAX_OMNIBUS ZORRO_ID(ARMAX, 0x00, 0)
+
+#define ZORRO_MANUF_ZEUS 0x088D
+#define ZORRO_PROD_ZEUS_SPIDER ZORRO_ID(ZEUS, 0x04, 0)
+
+#define ZORRO_MANUF_NEWTEK 0x088F
+#define ZORRO_PROD_NEWTEK_VIDEOTOASTER ZORRO_ID(NEWTEK, 0x00, 0)
+
+#define ZORRO_MANUF_M_TECH_GERMANY 0x0890
+#define ZORRO_PROD_MTEC_AT500_2 ZORRO_ID(M_TECH_GERMANY, 0x01, 0)
+#define ZORRO_PROD_MTEC_68030 ZORRO_ID(M_TECH_GERMANY, 0x03, 0)
+#define ZORRO_PROD_MTEC_68020I ZORRO_ID(M_TECH_GERMANY, 0x06, 0)
+#define ZORRO_PROD_MTEC_A1200_T68030_RTC ZORRO_ID(M_TECH_GERMANY, 0x20, 0)
+#define ZORRO_PROD_MTEC_VIPER_MK_V_E_MATRIX_530 ZORRO_ID(M_TECH_GERMANY, 0x21, 0)
+#define ZORRO_PROD_MTEC_8_MB_RAM ZORRO_ID(M_TECH_GERMANY, 0x22, 0)
+#define ZORRO_PROD_MTEC_VIPER_MK_V_E_MATRIX_530_SCSI_IDE ZORRO_ID(M_TECH_GERMANY, 0x24, 0)
+
+#define ZORRO_MANUF_GREAT_VALLEY_PRODUCTS_4 0x0891
+#define ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM ZORRO_ID(GREAT_VALLEY_PRODUCTS_4, 0x01, 0)
+#define ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG ZORRO_ID(GREAT_VALLEY_PRODUCTS_4, 0x02, 0)
+
+#define ZORRO_MANUF_APOLLO_1 0x0892
+#define ZORRO_PROD_APOLLO_A1200 ZORRO_ID(APOLLO_1, 0x01, 0)
+
+#define ZORRO_MANUF_HELFRICH_2 0x0893
+#define ZORRO_PROD_HELFRICH_PICCOLO_RAM ZORRO_ID(HELFRICH_2, 0x05, 0)
+#define ZORRO_PROD_HELFRICH_PICCOLO_REG ZORRO_ID(HELFRICH_2, 0x06, 0)
+#define ZORRO_PROD_HELFRICH_PEGGY_PLUS_MPEG ZORRO_ID(HELFRICH_2, 0x07, 0)
+#define ZORRO_PROD_HELFRICH_VIDEOCRUNCHER ZORRO_ID(HELFRICH_2, 0x08, 0)
+#define ZORRO_PROD_HELFRICH_SD64_RAM ZORRO_ID(HELFRICH_2, 0x0A, 0)
+#define ZORRO_PROD_HELFRICH_SD64_REG ZORRO_ID(HELFRICH_2, 0x0B, 0)
+
+#define ZORRO_MANUF_MACROSYSTEMS_USA 0x089B
+#define ZORRO_PROD_MACROSYSTEMS_WARP_ENGINE_40xx ZORRO_ID(MACROSYSTEMS_USA, 0x13, 0)
+
+#define ZORRO_MANUF_ELBOX_COMPUTER 0x089E
+#define ZORRO_PROD_ELBOX_COMPUTER_1200_4 ZORRO_ID(ELBOX_COMPUTER, 0x06, 0)
+
+#define ZORRO_MANUF_HARMS_PROFESSIONAL 0x0A00
+#define ZORRO_PROD_HARMS_PROFESSIONAL_030_PLUS ZORRO_ID(HARMS_PROFESSIONAL, 0x10, 0)
+#define ZORRO_PROD_HARMS_PROFESSIONAL_3500 ZORRO_ID(HARMS_PROFESSIONAL, 0xD0, 0)
+
+#define ZORRO_MANUF_MICRONIK 0x0A50
+#define ZORRO_PROD_MICRONIK_RCA_120 ZORRO_ID(MICRONIK, 0x0A, 0)
+
+#define ZORRO_MANUF_MICRONIK2 0x0F0F
+#define ZORRO_PROD_MICRONIK2_Z3I ZORRO_ID(MICRONIK2, 0x01, 0)
+
+#define ZORRO_MANUF_MEGAMICRO 0x1000
+#define ZORRO_PROD_MEGAMICRO_SCRAM_500 ZORRO_ID(MEGAMICRO, 0x03, 0)
+#define ZORRO_PROD_MEGAMICRO_SCRAM_500_RAM ZORRO_ID(MEGAMICRO, 0x04, 0)
+
+#define ZORRO_MANUF_IMTRONICS_2 0x1028
+#define ZORRO_PROD_IMTRONICS_HURRICANE_2800_3 ZORRO_ID(IMTRONICS_2, 0x39, 0)
+#define ZORRO_PROD_IMTRONICS_HURRICANE_2800_4 ZORRO_ID(IMTRONICS_2, 0x57, 0)
+
+/* unofficial ID */
+#define ZORRO_MANUF_INDIVIDUAL_COMPUTERS 0x1212
+#define ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA ZORRO_ID(INDIVIDUAL_COMPUTERS, 0x00, 0)
+#define ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL ZORRO_ID(INDIVIDUAL_COMPUTERS, 0x2A, 0)
+
+#define ZORRO_MANUF_KUPKE_3 0x1248
+#define ZORRO_PROD_KUPKE_GOLEM_HD_3000 ZORRO_ID(KUPKE_3, 0x01, 0)
+
+#define ZORRO_MANUF_ITH 0x1388
+#define ZORRO_PROD_ITH_ISDN_MASTER_II ZORRO_ID(ITH, 0x01, 0)
+
+#define ZORRO_MANUF_VMC 0x1389
+#define ZORRO_PROD_VMC_ISDN_BLASTER_Z2 ZORRO_ID(VMC, 0x01, 0)
+#define ZORRO_PROD_VMC_HYPERCOM_4 ZORRO_ID(VMC, 0x02, 0)
+
+#define ZORRO_MANUF_INFORMATION 0x157C
+#define ZORRO_PROD_INFORMATION_ISDN_ENGINE_I ZORRO_ID(INFORMATION, 0x64, 0)
+
+#define ZORRO_MANUF_VORTEX 0x2017
+#define ZORRO_PROD_VORTEX_GOLDEN_GATE_80386SX ZORRO_ID(VORTEX, 0x07, 0)
+#define ZORRO_PROD_VORTEX_GOLDEN_GATE_RAM ZORRO_ID(VORTEX, 0x08, 0)
+#define ZORRO_PROD_VORTEX_GOLDEN_GATE_80486 ZORRO_ID(VORTEX, 0x09, 0)
+
+#define ZORRO_MANUF_EXPANSION_SYSTEMS 0x2062
+#define ZORRO_PROD_EXPANSION_SYSTEMS_DATAFLYER_4000SX ZORRO_ID(EXPANSION_SYSTEMS, 0x01, 0)
+#define ZORRO_PROD_EXPANSION_SYSTEMS_DATAFLYER_4000SX_RAM ZORRO_ID(EXPANSION_SYSTEMS, 0x02, 0)
+
+#define ZORRO_MANUF_READYSOFT 0x2100
+#define ZORRO_PROD_READYSOFT_AMAX_II_IV ZORRO_ID(READYSOFT, 0x01, 0)
+
+#define ZORRO_MANUF_PHASE5 0x2140
+#define ZORRO_PROD_PHASE5_BLIZZARD_RAM ZORRO_ID(PHASE5, 0x01, 0)
+#define ZORRO_PROD_PHASE5_BLIZZARD ZORRO_ID(PHASE5, 0x02, 0)
+#define ZORRO_PROD_PHASE5_BLIZZARD_1220_IV ZORRO_ID(PHASE5, 0x06, 0)
+#define ZORRO_PROD_PHASE5_FASTLANE_Z3_RAM ZORRO_ID(PHASE5, 0x0A, 0)
+#define ZORRO_PROD_PHASE5_BLIZZARD_1230_II_FASTLANE_Z3_CYBERSCSI_CYBERSTORM060 ZORRO_ID(PHASE5, 0x0B, 0)
+#define ZORRO_PROD_PHASE5_BLIZZARD_1220_CYBERSTORM ZORRO_ID(PHASE5, 0x0C, 0)
+#define ZORRO_PROD_PHASE5_BLIZZARD_1230 ZORRO_ID(PHASE5, 0x0D, 0)
+#define ZORRO_PROD_PHASE5_BLIZZARD_1230_IV_1260 ZORRO_ID(PHASE5, 0x11, 0)
+#define ZORRO_PROD_PHASE5_BLIZZARD_2060 ZORRO_ID(PHASE5, 0x18, 0)
+#define ZORRO_PROD_PHASE5_CYBERSTORM_MK_II ZORRO_ID(PHASE5, 0x19, 0)
+#define ZORRO_PROD_PHASE5_CYBERVISION64 ZORRO_ID(PHASE5, 0x22, 0)
+#define ZORRO_PROD_PHASE5_CYBERVISION64_3D_PROTOTYPE ZORRO_ID(PHASE5, 0x32, 0)
+#define ZORRO_PROD_PHASE5_CYBERVISION64_3D ZORRO_ID(PHASE5, 0x43, 0)
+#define ZORRO_PROD_PHASE5_CYBERSTORM_MK_III ZORRO_ID(PHASE5, 0x64, 0)
+#define ZORRO_PROD_PHASE5_BLIZZARD_603E_PLUS ZORRO_ID(PHASE5, 0x6e, 0)
+
+#define ZORRO_MANUF_DPS 0x2169
+#define ZORRO_PROD_DPS_PERSONAL_ANIMATION_RECORDER ZORRO_ID(DPS, 0x01, 0)
+
+#define ZORRO_MANUF_APOLLO_2 0x2200
+#define ZORRO_PROD_APOLLO_A620_68020_1 ZORRO_ID(APOLLO_2, 0x00, 0)
+#define ZORRO_PROD_APOLLO_A620_68020_2 ZORRO_ID(APOLLO_2, 0x01, 0)
+
+#define ZORRO_MANUF_APOLLO_3 0x2222
+#define ZORRO_PROD_APOLLO_AT_APOLLO ZORRO_ID(APOLLO_3, 0x22, 0)
+#define ZORRO_PROD_APOLLO_1230_1240_1260_2030_4040_4060 ZORRO_ID(APOLLO_3, 0x23, 0)
+
+#define ZORRO_MANUF_PETSOFF_LP 0x38A5
+#define ZORRO_PROD_PETSOFF_LP_DELFINA ZORRO_ID(PETSOFF_LP, 0x00, 0)
+#define ZORRO_PROD_PETSOFF_LP_DELFINA_LITE ZORRO_ID(PETSOFF_LP, 0x01, 0)
+
+#define ZORRO_MANUF_UWE_GERLACH 0x3FF7
+#define ZORRO_PROD_UWE_GERLACH_RAM_ROM ZORRO_ID(UWE_GERLACH, 0xd4, 0)
+
+#define ZORRO_MANUF_ACT 0x4231
+#define ZORRO_PROD_ACT_PRELUDE ZORRO_ID(ACT, 0x01, 0)
+
+#define ZORRO_MANUF_MACROSYSTEMS_GERMANY 0x4754
+#define ZORRO_PROD_MACROSYSTEMS_MAESTRO ZORRO_ID(MACROSYSTEMS_GERMANY, 0x03, 0)
+#define ZORRO_PROD_MACROSYSTEMS_VLAB ZORRO_ID(MACROSYSTEMS_GERMANY, 0x04, 0)
+#define ZORRO_PROD_MACROSYSTEMS_MAESTRO_PRO ZORRO_ID(MACROSYSTEMS_GERMANY, 0x05, 0)
+#define ZORRO_PROD_MACROSYSTEMS_RETINA ZORRO_ID(MACROSYSTEMS_GERMANY, 0x06, 0)
+#define ZORRO_PROD_MACROSYSTEMS_MULTI_EVOLUTION ZORRO_ID(MACROSYSTEMS_GERMANY, 0x08, 0)
+#define ZORRO_PROD_MACROSYSTEMS_TOCCATA ZORRO_ID(MACROSYSTEMS_GERMANY, 0x0C, 0)
+#define ZORRO_PROD_MACROSYSTEMS_RETINA_Z3 ZORRO_ID(MACROSYSTEMS_GERMANY, 0x10, 0)
+#define ZORRO_PROD_MACROSYSTEMS_VLAB_MOTION ZORRO_ID(MACROSYSTEMS_GERMANY, 0x12, 0)
+#define ZORRO_PROD_MACROSYSTEMS_ALTAIS ZORRO_ID(MACROSYSTEMS_GERMANY, 0x13, 0)
+#define ZORRO_PROD_MACROSYSTEMS_FALCON_040 ZORRO_ID(MACROSYSTEMS_GERMANY, 0xFD, 0)
+
+#define ZORRO_MANUF_COMBITEC 0x6766
+
+#define ZORRO_MANUF_SKI_PERIPHERALS 0x8000
+#define ZORRO_PROD_SKI_PERIPHERALS_MAST_FIREBALL ZORRO_ID(SKI_PERIPHERALS, 0x08, 0)
+#define ZORRO_PROD_SKI_PERIPHERALS_SCSI_DUAL_SERIAL ZORRO_ID(SKI_PERIPHERALS, 0x80, 0)
+
+#define ZORRO_MANUF_REIS_WARE_2 0xA9AD
+#define ZORRO_PROD_REIS_WARE_SCAN_KING ZORRO_ID(REIS_WARE_2, 0x11, 0)
+
+#define ZORRO_MANUF_CAMERON 0xAA01
+#define ZORRO_PROD_CAMERON_PERSONAL_A4 ZORRO_ID(CAMERON, 0x10, 0)
+
+#define ZORRO_MANUF_REIS_WARE 0xAA11
+#define ZORRO_PROD_REIS_WARE_HANDYSCANNER ZORRO_ID(REIS_WARE, 0x11, 0)
+
+#define ZORRO_MANUF_PHOENIX_2 0xB5A8
+#define ZORRO_PROD_PHOENIX_ST506_2 ZORRO_ID(PHOENIX_2, 0x21, 0)
+#define ZORRO_PROD_PHOENIX_SCSI_2 ZORRO_ID(PHOENIX_2, 0x22, 0)
+#define ZORRO_PROD_PHOENIX_RAM_2 ZORRO_ID(PHOENIX_2, 0xBE, 0)
+
+#define ZORRO_MANUF_COMBITEC_2 0xC008
+#define ZORRO_PROD_COMBITEC_HD ZORRO_ID(COMBITEC_2, 0x2A, 0)
+#define ZORRO_PROD_COMBITEC_SRAM ZORRO_ID(COMBITEC_2, 0x2B, 0)
+
+
+ /*
+ * Test and illegal Manufacturer IDs.
+ */
+
+#define ZORRO_MANUF_HACKER 0x07DB
+#define ZORRO_PROD_GENERAL_PROTOTYPE ZORRO_ID(HACKER, 0x00, 0)
+#define ZORRO_PROD_HACKER_SCSI ZORRO_ID(HACKER, 0x01, 0)
+#define ZORRO_PROD_RESOURCE_MANAGEMENT_FORCE_QUICKNET_QN2000 ZORRO_ID(HACKER, 0x02, 0)
+#define ZORRO_PROD_VECTOR_CONNECTION_2 ZORRO_ID(HACKER, 0xE0, 0)
+#define ZORRO_PROD_VECTOR_CONNECTION_3 ZORRO_ID(HACKER, 0xE1, 0)
+#define ZORRO_PROD_VECTOR_CONNECTION_4 ZORRO_ID(HACKER, 0xE2, 0)
+#define ZORRO_PROD_VECTOR_CONNECTION_5 ZORRO_ID(HACKER, 0xE3, 0)
+
+
+ /*
+ * GVP identifies most of its products through the 'extended product code'
+ * (epc). The epc has to be ANDed with the GVP_PRODMASK before the
+ * identification.
+ */
+
+#define GVP_PRODMASK (0xf8)
+#define GVP_SCSICLKMASK (0x01)
+
+enum GVP_flags {
+ GVP_IO = 0x01,
+ GVP_ACCEL = 0x02,
+ GVP_SCSI = 0x04,
+ GVP_24BITDMA = 0x08,
+ GVP_25BITDMA = 0x10,
+ GVP_NOBANK = 0x20,
+ GVP_14MHZ = 0x40,
+};
+
+
+struct Node {
+ struct Node *ln_Succ; /* Pointer to next (successor) */
+ struct Node *ln_Pred; /* Pointer to previous (predecessor) */
+ __u8 ln_Type;
+ __s8 ln_Pri; /* Priority, for sorting */
+ __s8 *ln_Name; /* ID string, null terminated */
+} __attribute__ ((packed));
+
+struct ExpansionRom {
+ /* -First 16 bytes of the expansion ROM */
+ __u8 er_Type; /* Board type, size and flags */
+ __u8 er_Product; /* Product number, assigned by manufacturer */
+ __u8 er_Flags; /* Flags */
+ __u8 er_Reserved03; /* Must be zero ($ff inverted) */
+ __u16 er_Manufacturer; /* Unique ID, ASSIGNED BY COMMODORE-AMIGA! */
+ __u32 er_SerialNumber; /* Available for use by manufacturer */
+ __u16 er_InitDiagVec; /* Offset to optional "DiagArea" structure */
+ __u8 er_Reserved0c;
+ __u8 er_Reserved0d;
+ __u8 er_Reserved0e;
+ __u8 er_Reserved0f;
+} __attribute__ ((packed));
+
+/* er_Type board type bits */
+#define ERT_TYPEMASK 0xc0
+#define ERT_ZORROII 0xc0
+#define ERT_ZORROIII 0x80
+
+/* other bits defined in er_Type */
+#define ERTB_MEMLIST 5 /* Link RAM into free memory list */
+#define ERTF_MEMLIST (1<<5)
+
+struct ConfigDev {
+ struct Node cd_Node;
+ __u8 cd_Flags; /* (read/write) */
+ __u8 cd_Pad; /* reserved */
+ struct ExpansionRom cd_Rom; /* copy of board's expansion ROM */
+ void *cd_BoardAddr; /* where in memory the board was placed */
+ __u32 cd_BoardSize; /* size of board in bytes */
+ __u16 cd_SlotAddr; /* which slot number (PRIVATE) */
+ __u16 cd_SlotSize; /* number of slots (PRIVATE) */
+ void *cd_Driver; /* pointer to node of driver */
+ struct ConfigDev *cd_NextCD; /* linked list of drivers to config */
+ __u32 cd_Unused[4]; /* for whatever the driver wants */
+} __attribute__ ((packed));
+
+#else /* __ASSEMBLY__ */
+
+LN_Succ = 0
+LN_Pred = LN_Succ+4
+LN_Type = LN_Pred+4
+LN_Pri = LN_Type+1
+LN_Name = LN_Pri+1
+LN_sizeof = LN_Name+4
+
+ER_Type = 0
+ER_Product = ER_Type+1
+ER_Flags = ER_Product+1
+ER_Reserved03 = ER_Flags+1
+ER_Manufacturer = ER_Reserved03+1
+ER_SerialNumber = ER_Manufacturer+2
+ER_InitDiagVec = ER_SerialNumber+4
+ER_Reserved0c = ER_InitDiagVec+2
+ER_Reserved0d = ER_Reserved0c+1
+ER_Reserved0e = ER_Reserved0d+1
+ER_Reserved0f = ER_Reserved0e+1
+ER_sizeof = ER_Reserved0f+1
+
+CD_Node = 0
+CD_Flags = CD_Node+LN_sizeof
+CD_Pad = CD_Flags+1
+CD_Rom = CD_Pad+1
+CD_BoardAddr = CD_Rom+ER_sizeof
+CD_BoardSize = CD_BoardAddr+4
+CD_SlotAddr = CD_BoardSize+4
+CD_SlotSize = CD_SlotAddr+2
+CD_Driver = CD_SlotSize+2
+CD_NextCD = CD_Driver+4
+CD_Unused = CD_NextCD+4
+CD_sizeof = CD_Unused+(4*4)
+
+#endif /* __ASSEMBLY__ */
+
+#ifndef __ASSEMBLY__
+
+#define ZORRO_NUM_AUTO 16
+
+#ifdef __KERNEL__
+
+extern unsigned int zorro_num_autocon; /* # of autoconfig devices found */
+extern struct ConfigDev zorro_autocon[ZORRO_NUM_AUTO];
+
+
+ /*
+ * Zorro Functions
+ */
+
+extern void zorro_init(void);
+extern void zorro_proc_init(void);
+
+extern unsigned int zorro_find(zorro_id id, unsigned int part, unsigned int index);
+extern const struct ConfigDev *zorro_get_board(unsigned int key);
+extern void zorro_config_board(unsigned int key, unsigned int part);
+extern void zorro_unconfig_board(unsigned int key, unsigned int part);
+
+
+ /*
+ * Bitmask indicating portions of available Zorro II RAM that are unused
+ * by the system. Every bit represents a 64K chunk, for a maximum of 8MB
+ * (128 chunks, physical 0x00200000-0x009fffff).
+ *
+ * If you want to use (= allocate) portions of this RAM, you should clear
+ * the corresponding bits.
+ */
+
+extern __u32 zorro_unused_z2ram[4];
+
+#define Z2RAM_START (0x00200000)
+#define Z2RAM_END (0x00a00000)
+#define Z2RAM_SIZE (0x00800000)
+#define Z2RAM_CHUNKSIZE (0x00010000)
+#define Z2RAM_CHUNKMASK (0x0000ffff)
+#define Z2RAM_CHUNKSHIFT (16)
+
+
+#endif /* !__ASSEMBLY__ */
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_ZORRO_H */
diff --git a/pfinet/linux-src/include/net/addrconf.h b/pfinet/linux-src/include/net/addrconf.h
new file mode 100644
index 00000000..4b270775
--- /dev/null
+++ b/pfinet/linux-src/include/net/addrconf.h
@@ -0,0 +1,160 @@
+#ifndef _ADDRCONF_H
+#define _ADDRCONF_H
+
+#include "ipv6.h"
+
+#define RETRANS_TIMER HZ
+
+#define MAX_RTR_SOLICITATIONS 3
+#define RTR_SOLICITATION_INTERVAL (4*HZ)
+
+#define ADDR_CHECK_FREQUENCY (120*HZ)
+
+struct prefix_info {
+ __u8 type;
+ __u8 length;
+ __u8 prefix_len;
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ __u8 onlink : 1,
+ autoconf : 1,
+ reserved : 6;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 reserved : 6,
+ autoconf : 1,
+ onlink : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ __u32 valid;
+ __u32 prefered;
+ __u32 reserved2;
+
+ struct in6_addr prefix;
+};
+
+
+#ifdef __KERNEL__
+
+#include <linux/in6.h>
+#include <linux/netdevice.h>
+#include <net/if_inet6.h>
+
+#define IN6_ADDR_HSIZE 16
+
+extern void addrconf_init(void);
+extern void addrconf_cleanup(void);
+
+extern int addrconf_notify(struct notifier_block *this,
+ unsigned long event,
+ void * data);
+
+extern int addrconf_add_ifaddr(void *arg);
+extern int addrconf_del_ifaddr(void *arg);
+extern int addrconf_set_dstaddr(void *arg);
+
+extern struct inet6_ifaddr * ipv6_chk_addr(struct in6_addr *addr,
+ struct device *dev, int nd);
+extern int ipv6_get_saddr(struct dst_entry *dst,
+ struct in6_addr *daddr,
+ struct in6_addr *saddr);
+extern struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev);
+
+/*
+ * multicast prototypes (mcast.c)
+ */
+extern int ipv6_sock_mc_join(struct sock *sk,
+ int ifindex,
+ struct in6_addr *addr);
+extern int ipv6_sock_mc_drop(struct sock *sk,
+ int ifindex,
+ struct in6_addr *addr);
+extern void ipv6_sock_mc_close(struct sock *sk);
+
+extern int ipv6_dev_mc_inc(struct device *dev,
+ struct in6_addr *addr);
+extern int ipv6_dev_mc_dec(struct device *dev,
+ struct in6_addr *addr);
+extern void ipv6_mc_up(struct inet6_dev *idev);
+extern void ipv6_mc_down(struct inet6_dev *idev);
+extern void ipv6_mc_destroy_dev(struct inet6_dev *idev);
+extern void addrconf_dad_failure(struct inet6_ifaddr *ifp);
+
+extern int ipv6_chk_mcast_addr(struct device *dev,
+ struct in6_addr *addr);
+
+extern void addrconf_prefix_rcv(struct device *dev,
+ u8 *opt, int len);
+
+extern struct inet6_dev * ipv6_get_idev(struct device *dev);
+
+extern void addrconf_forwarding_on(void);
+/*
+ * Hash function taken from net_alias.c
+ */
+
+static __inline__ u8 ipv6_addr_hash(struct in6_addr *addr)
+{
+ __u32 word;
+ unsigned tmp;
+
+ /*
+ * We perform the hash function over the last 64 bits of the address
+ * This will include the IEEE address token on links that support it.
+ */
+
+ word = addr->s6_addr[2] ^ addr->s6_addr32[3];
+ tmp = word ^ (word>>16);
+ tmp ^= (tmp >> 8);
+
+ return ((tmp ^ (tmp >> 4)) & 0x0f);
+}
+
+static __inline__ int ipv6_devindex_hash(int ifindex)
+{
+ return ifindex & (IN6_ADDR_HSIZE - 1);
+}
+
+/*
+ * compute link-local solicited-node multicast address
+ */
+
+extern __inline__ void addrconf_addr_solict_mult_old(struct in6_addr *addr,
+ struct in6_addr *solicited)
+{
+ ipv6_addr_set(solicited,
+ __constant_htonl(0xFF020000), 0,
+ __constant_htonl(0x1), addr->s6_addr32[3]);
+}
+
+extern __inline__ void addrconf_addr_solict_mult_new(struct in6_addr *addr,
+ struct in6_addr *solicited)
+{
+ ipv6_addr_set(solicited,
+ __constant_htonl(0xFF020000), 0,
+ __constant_htonl(0x1),
+ __constant_htonl(0xFF000000) | addr->s6_addr32[3]);
+}
+
+
+extern __inline__ void ipv6_addr_all_nodes(struct in6_addr *addr)
+{
+ ipv6_addr_set(addr,
+ __constant_htonl(0xFF020000), 0, 0,
+ __constant_htonl(0x1));
+}
+
+extern __inline__ void ipv6_addr_all_routers(struct in6_addr *addr)
+{
+ ipv6_addr_set(addr,
+ __constant_htonl(0xFF020000), 0, 0,
+ __constant_htonl(0x2));
+}
+
+extern __inline__ int ipv6_addr_is_multicast(struct in6_addr *addr)
+{
+ return (addr->s6_addr32[0] & __constant_htonl(0xFF000000)) == __constant_htonl(0xFF000000);
+}
+
+#endif
+#endif
diff --git a/pfinet/linux-src/include/net/af_unix.h b/pfinet/linux-src/include/net/af_unix.h
new file mode 100644
index 00000000..06970ac6
--- /dev/null
+++ b/pfinet/linux-src/include/net/af_unix.h
@@ -0,0 +1,35 @@
+#ifndef __LINUX_NET_AFUNIX_H
+#define __LINUX_NET_AFUNIX_H
+extern void unix_proto_init(struct net_proto *pro);
+extern struct proto_ops unix_proto_ops;
+extern void unix_inflight(struct file *fp);
+extern void unix_notinflight(struct file *fp);
+typedef struct sock unix_socket;
+extern void unix_gc(void);
+
+#define UNIX_HASH_SIZE 16
+
+extern unix_socket *unix_socket_table[UNIX_HASH_SIZE+1];
+
+#define forall_unix_sockets(i, s) for (i=0; i<=UNIX_HASH_SIZE; i++) \
+ for (s=unix_socket_table[i]; s; s=s->next)
+
+struct unix_address
+{
+ atomic_t refcnt;
+ int len;
+ unsigned hash;
+ struct sockaddr_un name[0];
+};
+
+struct unix_skb_parms
+{
+ struct ucred creds; /* Skb credentials */
+ struct scm_fp_list *fp; /* Passed files */
+ unsigned attr; /* Special attributes */
+};
+
+#define UNIXCB(skb) (*(struct unix_skb_parms*)&((skb)->cb))
+#define UNIXCREDS(skb) (&UNIXCB((skb)).creds)
+
+#endif
diff --git a/pfinet/linux-src/include/net/arp.h b/pfinet/linux-src/include/net/arp.h
new file mode 100644
index 00000000..b672bacb
--- /dev/null
+++ b/pfinet/linux-src/include/net/arp.h
@@ -0,0 +1,24 @@
+/* linux/net/inet/arp.h */
+#ifndef _ARP_H
+#define _ARP_H
+
+#include <linux/if_arp.h>
+#include <net/neighbour.h>
+
+extern struct neigh_table arp_tbl;
+
+extern void arp_init(void);
+extern int arp_rcv(struct sk_buff *skb, struct device *dev,
+ struct packet_type *pt);
+extern int arp_find(unsigned char *haddr, struct sk_buff *skb);
+extern int arp_ioctl(unsigned int cmd, void *arg);
+extern void arp_send(int type, int ptype, u32 dest_ip,
+ struct device *dev, u32 src_ip,
+ unsigned char *dest_hw, unsigned char *src_hw, unsigned char *th);
+extern int arp_bind_neighbour(struct dst_entry *dst);
+extern int arp_mc_map(u32 addr, u8 *haddr, struct device *dev, int dir);
+extern void arp_ifdown(struct device *dev);
+
+extern struct neigh_ops arp_broken_ops;
+
+#endif /* _ARP_H */
diff --git a/pfinet/linux-src/include/net/atalkcall.h b/pfinet/linux-src/include/net/atalkcall.h
new file mode 100644
index 00000000..726e33cd
--- /dev/null
+++ b/pfinet/linux-src/include/net/atalkcall.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void atalk_proto_init(struct net_proto *pro);
diff --git a/pfinet/linux-src/include/net/ax25.h b/pfinet/linux-src/include/net/ax25.h
new file mode 100644
index 00000000..2c8d20a6
--- /dev/null
+++ b/pfinet/linux-src/include/net/ax25.h
@@ -0,0 +1,349 @@
+/*
+ * Declarations of AX.25 type objects.
+ *
+ * Alan Cox (GW4PTS) 10/11/93
+ */
+
+#ifndef _AX25_H
+#define _AX25_H
+#include <linux/config.h>
+#include <linux/ax25.h>
+
+#define AX25_T1CLAMPLO 1
+#define AX25_T1CLAMPHI (30 * HZ)
+
+#define AX25_BPQ_HEADER_LEN 16
+#define AX25_KISS_HEADER_LEN 1
+
+#define AX25_HEADER_LEN 17
+#define AX25_ADDR_LEN 7
+#define AX25_DIGI_HEADER_LEN (AX25_MAX_DIGIS * AX25_ADDR_LEN)
+#define AX25_MAX_HEADER_LEN (AX25_HEADER_LEN + AX25_DIGI_HEADER_LEN)
+
+/* AX.25 Protocol IDs */
+#define AX25_P_ROSE 0x01
+#define AX25_P_IP 0xCC
+#define AX25_P_ARP 0xCD
+#define AX25_P_TEXT 0xF0
+#define AX25_P_NETROM 0xCF
+#define AX25_P_SEGMENT 0x08
+
+/* AX.25 Segment control values */
+#define AX25_SEG_REM 0x7F
+#define AX25_SEG_FIRST 0x80
+
+#define AX25_CBIT 0x80 /* Command/Response bit */
+#define AX25_EBIT 0x01 /* HDLC Address Extension bit */
+#define AX25_HBIT 0x80 /* Has been repeated bit */
+
+#define AX25_SSSID_SPARE 0x60 /* Unused bits in SSID for standard AX.25 */
+#define AX25_ESSID_SPARE 0x20 /* Unused bits in SSID for extended AX.25 */
+#define AX25_DAMA_FLAG 0x20 /* Well, it is *NOT* unused! (dl1bke 951121 */
+
+#define AX25_COND_ACK_PENDING 0x01
+#define AX25_COND_REJECT 0x02
+#define AX25_COND_PEER_RX_BUSY 0x04
+#define AX25_COND_OWN_RX_BUSY 0x08
+#define AX25_COND_DAMA_MODE 0x10
+
+#ifndef _LINUX_NETDEVICE_H
+#include <linux/netdevice.h>
+#endif
+
+/* Upper sub-layer (LAPB) definitions */
+
+/* Control field templates */
+#define AX25_I 0x00 /* Information frames */
+#define AX25_S 0x01 /* Supervisory frames */
+#define AX25_RR 0x01 /* Receiver ready */
+#define AX25_RNR 0x05 /* Receiver not ready */
+#define AX25_REJ 0x09 /* Reject */
+#define AX25_U 0x03 /* Unnumbered frames */
+#define AX25_SABM 0x2f /* Set Asynchronous Balanced Mode */
+#define AX25_SABME 0x6f /* Set Asynchronous Balanced Mode Extended */
+#define AX25_DISC 0x43 /* Disconnect */
+#define AX25_DM 0x0f /* Disconnected mode */
+#define AX25_UA 0x63 /* Unnumbered acknowledge */
+#define AX25_FRMR 0x87 /* Frame reject */
+#define AX25_UI 0x03 /* Unnumbered information */
+
+#define AX25_PF 0x10 /* Poll/final bit for standard AX.25 */
+#define AX25_EPF 0x01 /* Poll/final bit for extended AX.25 */
+
+#define AX25_ILLEGAL 0x100 /* Impossible to be a real frame type */
+
+#define AX25_POLLOFF 0
+#define AX25_POLLON 1
+
+/* AX25 L2 C-bit */
+#define AX25_COMMAND 1
+#define AX25_RESPONSE 2
+
+/* Define Link State constants. */
+
+enum {
+ AX25_STATE_0,
+ AX25_STATE_1,
+ AX25_STATE_2,
+ AX25_STATE_3,
+ AX25_STATE_4
+};
+
+#define AX25_MODULUS 8 /* Standard AX.25 modulus */
+#define AX25_EMODULUS 128 /* Extended AX.25 modulus */
+
+enum {
+ AX25_PROTO_STD_SIMPLEX,
+ AX25_PROTO_STD_DUPLEX,
+ AX25_PROTO_DAMA_SLAVE,
+ AX25_PROTO_DAMA_MASTER
+};
+
+enum {
+ AX25_VALUES_IPDEFMODE, /* 0=DG 1=VC */
+ AX25_VALUES_AXDEFMODE, /* 0=Normal 1=Extended Seq Nos */
+ AX25_VALUES_BACKOFF, /* 0=None 1=Linear 2=Exponential */
+ AX25_VALUES_CONMODE, /* Allow connected modes - 0=No 1=no "PID text" 2=all PIDs */
+ AX25_VALUES_WINDOW, /* Default window size for standard AX.25 */
+ AX25_VALUES_EWINDOW, /* Default window size for extended AX.25 */
+ AX25_VALUES_T1, /* Default T1 timeout value */
+ AX25_VALUES_T2, /* Default T2 timeout value */
+ AX25_VALUES_T3, /* Default T3 timeout value */
+ AX25_VALUES_IDLE, /* Connected mode idle timer */
+ AX25_VALUES_N2, /* Default N2 value */
+ AX25_VALUES_PACLEN, /* AX.25 MTU */
+ AX25_VALUES_PROTOCOL, /* Std AX.25, DAMA Slave, DAMA Master */
+ AX25_VALUES_DS_TIMEOUT, /* DAMA Slave timeout */
+ AX25_MAX_VALUES /* THIS MUST REMAIN THE LAST ENTRY OF THIS LIST */
+};
+
+#define AX25_DEF_IPDEFMODE 0 /* Datagram */
+#define AX25_DEF_AXDEFMODE 0 /* Normal */
+#define AX25_DEF_BACKOFF 1 /* Linear backoff */
+#define AX25_DEF_CONMODE 2 /* Connected mode allowed */
+#define AX25_DEF_WINDOW 2 /* Window=2 */
+#define AX25_DEF_EWINDOW 32 /* Module-128 Window=32 */
+#define AX25_DEF_T1 (10 * HZ) /* T1=10s */
+#define AX25_DEF_T2 (3 * HZ) /* T2=3s */
+#define AX25_DEF_T3 (300 * HZ) /* T3=300s */
+#define AX25_DEF_N2 10 /* N2=10 */
+#define AX25_DEF_IDLE (0 * 60 * HZ) /* Idle=None */
+#define AX25_DEF_PACLEN 256 /* Paclen=256 */
+#define AX25_DEF_PROTOCOL AX25_PROTO_STD_SIMPLEX /* Standard AX.25 */
+#define AX25_DEF_DS_TIMEOUT (3 * 60 * HZ) /* DAMA timeout 3 minutes */
+
+typedef struct ax25_uid_assoc {
+ struct ax25_uid_assoc *next;
+ uid_t uid;
+ ax25_address call;
+} ax25_uid_assoc;
+
+typedef struct {
+ ax25_address calls[AX25_MAX_DIGIS];
+ unsigned char repeated[AX25_MAX_DIGIS];
+ unsigned char ndigi;
+ char lastrepeat;
+} ax25_digi;
+
+typedef struct ax25_route {
+ struct ax25_route *next;
+ ax25_address callsign;
+ struct device *dev;
+ ax25_digi *digipeat;
+ char ip_mode;
+} ax25_route;
+
+typedef struct {
+ char slave; /* slave_mode? */
+ struct timer_list slave_timer; /* timeout timer */
+ unsigned short slave_timeout; /* when? */
+} ax25_dama_info;
+
+#ifndef _LINUX_SYSCTL_H
+#include <linux/sysctl.h>
+#endif
+
+typedef struct ax25_dev {
+ struct ax25_dev *next;
+ struct device *dev;
+ struct device *forward;
+ struct ctl_table systable[AX25_MAX_VALUES+1];
+ int values[AX25_MAX_VALUES];
+#if defined(CONFIG_AX25_DAMA_SLAVE) || defined(CONFIG_AX25_DAMA_MASTER)
+ ax25_dama_info dama;
+#endif
+} ax25_dev;
+
+typedef struct ax25_cb {
+ struct ax25_cb *next;
+ ax25_address source_addr, dest_addr;
+ ax25_digi *digipeat;
+ ax25_dev *ax25_dev;
+ unsigned char iamdigi;
+ unsigned char state, modulus, pidincl;
+ unsigned short vs, vr, va;
+ unsigned char condition, backoff;
+ unsigned char n2, n2count;
+ struct timer_list t1timer, t2timer, t3timer, idletimer;
+ unsigned long t1, t2, t3, idle, rtt;
+ unsigned short paclen, fragno, fraglen;
+ struct sk_buff_head write_queue;
+ struct sk_buff_head reseq_queue;
+ struct sk_buff_head ack_queue;
+ struct sk_buff_head frag_queue;
+ unsigned char window;
+ struct timer_list timer;
+ struct sock *sk; /* Backlink to socket */
+} ax25_cb;
+
+/* af_ax25.c */
+extern ax25_cb *volatile ax25_list;
+extern void ax25_free_cb(ax25_cb *);
+extern void ax25_insert_socket(ax25_cb *);
+struct sock *ax25_find_listener(ax25_address *, int, struct device *, int);
+struct sock *ax25_find_socket(ax25_address *, ax25_address *, int);
+extern ax25_cb *ax25_find_cb(ax25_address *, ax25_address *, ax25_digi *, struct device *);
+extern struct sock *ax25_addr_match(ax25_address *);
+extern void ax25_send_to_raw(struct sock *, struct sk_buff *, int);
+extern void ax25_destroy_socket(ax25_cb *);
+extern ax25_cb *ax25_create_cb(void);
+extern void ax25_fillin_cb(ax25_cb *, ax25_dev *);
+extern int ax25_create(struct socket *, int);
+extern struct sock *ax25_make_new(struct sock *, struct ax25_dev *);
+
+/* ax25_addr.c */
+extern ax25_address null_ax25_address;
+extern char *ax2asc(ax25_address *);
+extern ax25_address *asc2ax(char *);
+extern int ax25cmp(ax25_address *, ax25_address *);
+extern int ax25digicmp(ax25_digi *, ax25_digi *);
+extern unsigned char *ax25_addr_parse(unsigned char *, int, ax25_address *, ax25_address *, ax25_digi *, int *, int *);
+extern int ax25_addr_build(unsigned char *, ax25_address *, ax25_address *, ax25_digi *, int, int);
+extern int ax25_addr_size(ax25_digi *);
+extern void ax25_digi_invert(ax25_digi *, ax25_digi *);
+
+/* ax25_dev.c */
+extern ax25_dev *ax25_dev_list;
+extern ax25_dev *ax25_dev_ax25dev(struct device *);
+extern ax25_dev *ax25_addr_ax25dev(ax25_address *);
+extern void ax25_dev_device_up(struct device *);
+extern void ax25_dev_device_down(struct device *);
+extern int ax25_fwd_ioctl(unsigned int, struct ax25_fwd_struct *);
+extern struct device *ax25_fwd_dev(struct device *);
+extern void ax25_dev_free(void);
+
+/* ax25_ds_in.c */
+extern int ax25_ds_frame_in(ax25_cb *, struct sk_buff *, int);
+
+/* ax25_ds_subr.c */
+extern void ax25_ds_nr_error_recovery(ax25_cb *);
+extern void ax25_ds_enquiry_response(ax25_cb *);
+extern void ax25_ds_establish_data_link(ax25_cb *);
+extern void ax25_dev_dama_on(ax25_dev *);
+extern void ax25_dev_dama_off(ax25_dev *);
+extern void ax25_dama_on(ax25_cb *);
+extern void ax25_dama_off(ax25_cb *);
+
+/* ax25_ds_timer.c */
+extern void ax25_ds_set_timer(ax25_dev *);
+extern void ax25_ds_del_timer(ax25_dev *);
+extern void ax25_ds_timer(ax25_cb *);
+extern void ax25_ds_t1_timeout(ax25_cb *);
+extern void ax25_ds_heartbeat_expiry(ax25_cb *);
+extern void ax25_ds_t3timer_expiry(ax25_cb *);
+extern void ax25_ds_idletimer_expiry(ax25_cb *);
+
+#include <net/ax25call.h>
+
+/* ax25_iface.c */
+extern int ax25_protocol_register(unsigned int, int (*)(struct sk_buff *, ax25_cb *));
+extern void ax25_protocol_release(unsigned int);
+extern int ax25_linkfail_register(void (*)(ax25_cb *, int));
+extern void ax25_linkfail_release(void (*)(ax25_cb *, int));
+extern int ax25_listen_register(ax25_address *, struct device *);
+extern void ax25_listen_release(ax25_address *, struct device *);
+extern int (*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *);
+extern int ax25_listen_mine(ax25_address *, struct device *);
+extern void ax25_link_failed(ax25_cb *, int);
+extern int ax25_protocol_is_registered(unsigned int);
+
+/* ax25_in.c */
+extern int ax25_rx_iframe(ax25_cb *, struct sk_buff *);
+extern int ax25_kiss_rcv(struct sk_buff *, struct device *, struct packet_type *);
+
+/* ax25_ip.c */
+extern int ax25_encapsulate(struct sk_buff *, struct device *, unsigned short, void *, void *, unsigned int);
+extern int ax25_rebuild_header(struct sk_buff *);
+
+/* ax25_out.c */
+extern ax25_cb *ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, ax25_digi *, struct device *);
+extern void ax25_output(ax25_cb *, int, struct sk_buff *);
+extern void ax25_kick(ax25_cb *);
+extern void ax25_transmit_buffer(ax25_cb *, struct sk_buff *, int);
+extern void ax25_queue_xmit(struct sk_buff *);
+extern int ax25_check_iframes_acked(ax25_cb *, unsigned short);
+
+/* ax25_route.c */
+extern void ax25_rt_device_down(struct device *);
+extern int ax25_rt_ioctl(unsigned int, void *);
+extern int ax25_rt_get_info(char *, char **, off_t, int, int);
+extern int ax25_rt_autobind(ax25_cb *, ax25_address *);
+extern ax25_route *ax25_rt_find_route(ax25_address *, struct device *);
+extern struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *);
+extern void ax25_rt_free(void);
+
+/* ax25_std_in.c */
+extern int ax25_std_frame_in(ax25_cb *, struct sk_buff *, int);
+
+/* ax25_std_subr.c */
+extern void ax25_std_nr_error_recovery(ax25_cb *);
+extern void ax25_std_establish_data_link(ax25_cb *);
+extern void ax25_std_transmit_enquiry(ax25_cb *);
+extern void ax25_std_enquiry_response(ax25_cb *);
+extern void ax25_std_timeout_response(ax25_cb *);
+
+/* ax25_std_timer.c */
+extern void ax25_std_heartbeat_expiry(ax25_cb *);
+extern void ax25_std_t1timer_expiry(ax25_cb *);
+extern void ax25_std_t2timer_expiry(ax25_cb *);
+extern void ax25_std_t3timer_expiry(ax25_cb *);
+extern void ax25_std_idletimer_expiry(ax25_cb *);
+
+/* ax25_subr.c */
+extern void ax25_clear_queues(ax25_cb *);
+extern void ax25_frames_acked(ax25_cb *, unsigned short);
+extern void ax25_requeue_frames(ax25_cb *);
+extern int ax25_validate_nr(ax25_cb *, unsigned short);
+extern int ax25_decode(ax25_cb *, struct sk_buff *, int *, int *, int *);
+extern void ax25_send_control(ax25_cb *, int, int, int);
+extern void ax25_return_dm(struct device *, ax25_address *, ax25_address *, ax25_digi *);
+extern void ax25_calculate_t1(ax25_cb *);
+extern void ax25_calculate_rtt(ax25_cb *);
+extern void ax25_disconnect(ax25_cb *, int);
+
+/* ax25_timer.c */
+extern void ax25_start_heartbeat(ax25_cb *);
+extern void ax25_start_t1timer(ax25_cb *);
+extern void ax25_start_t2timer(ax25_cb *);
+extern void ax25_start_t3timer(ax25_cb *);
+extern void ax25_start_idletimer(ax25_cb *);
+extern void ax25_stop_heartbeat(ax25_cb *);
+extern void ax25_stop_t1timer(ax25_cb *);
+extern void ax25_stop_t2timer(ax25_cb *);
+extern void ax25_stop_t3timer(ax25_cb *);
+extern void ax25_stop_idletimer(ax25_cb *);
+extern int ax25_t1timer_running(ax25_cb *);
+extern unsigned long ax25_display_timer(struct timer_list *);
+
+/* ax25_uid.c */
+extern int ax25_uid_policy;
+extern ax25_address *ax25_findbyuid(uid_t);
+extern int ax25_uid_ioctl(int, struct sockaddr_ax25 *);
+extern int ax25_uid_get_info(char *, char **, off_t, int, int);
+extern void ax25_uid_free(void);
+
+/* sysctl_net_ax25.c */
+extern void ax25_register_sysctl(void);
+extern void ax25_unregister_sysctl(void);
+
+#endif
diff --git a/pfinet/linux-src/include/net/ax25call.h b/pfinet/linux-src/include/net/ax25call.h
new file mode 100644
index 00000000..68b8a70c
--- /dev/null
+++ b/pfinet/linux-src/include/net/ax25call.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void ax25_proto_init(struct net_proto *pro);
diff --git a/pfinet/linux-src/include/net/br.h b/pfinet/linux-src/include/net/br.h
new file mode 100644
index 00000000..95effc51
--- /dev/null
+++ b/pfinet/linux-src/include/net/br.h
@@ -0,0 +1,328 @@
+/*
+ * Constants and structure definitions for the bridging code
+ */
+
+#if !defined(One)
+#define Zero 0
+#define One 1
+#endif /* !defined(One) */
+
+#if !defined(TRUE)
+#define FALSE 0
+#define TRUE 1
+#endif /* !defined(TRUE) */
+
+/** port states. **/
+#define Disabled 0 /* (4.4 5) */
+#define Listening 1 /* (4.4.2) */
+#define Learning 2 /* (4.4.3) */
+#define Forwarding 3 /* (4 4 4) */
+#define Blocking 4 /* (4.4.1) */
+
+
+/* MAG Yich! Easiest way of giving a configurable number of ports
+ * If you want more than 32, change BR_MAX_PORTS and recompile brcfg!
+ */
+#define BR_MAX_PORTS (32)
+#if CONFIG_BRIDGE_NUM_PORTS > BR_MAX_PORTS
+#undef CONFIG_BRIDGE_NUM_PORTS
+#define CONFIG_BRIDGE_NUM_PORTS BR_MAX_PORTS
+#endif
+#define No_of_ports CONFIG_BRIDGE_NUM_PORTS
+/* arbitrary choice, to allow the code below to compile */
+
+#define All_ports (No_of_ports + 1)
+
+/*
+ * We time out our entries in the FDB after this many seconds.
+ */
+#define FDB_TIMEOUT 20 /* JRP: 20s as NSC bridge code, was 300 for Linux */
+
+/*
+ * the following defines are the initial values used when the
+ * bridge is booted. These may be overridden when this bridge is
+ * not the root bridge. These are the recommended default values
+ * from the 802.1d specification.
+ */
+#define BRIDGE_MAX_AGE 20
+#define BRIDGE_HELLO_TIME 2
+#define BRIDGE_FORWARD_DELAY 15
+#define HOLD_TIME 1
+
+/* broacast/multicast storm limitation. This per source. */
+#define MAX_MCAST_PER_PERIOD 4
+#define MCAST_HOLD_TIME (10*HZ/100)
+
+#define Default_path_cost 10
+
+/*
+ * minimum increment possible to avoid underestimating age, allows for BPDU
+ * transmission time
+ */
+#define Message_age_increment 1
+
+#define No_port 0
+/*
+ * reserved value for Bridge's root port parameter indicating no root port,
+ * used when Bridge is the root - also used to indicate the source when
+ * a frame is being generated by a higher layer protocol on this host
+ */
+
+/** Configuration BPDU Parameters (4.5.1) **/
+
+typedef struct {
+ union {
+ struct {
+ unsigned short priority;
+ unsigned char ula[6];
+ } p_u;
+ unsigned int id[2];
+ } bi;
+} bridge_id_t;
+
+#define BRIDGE_PRIORITY bi.p_u.priority
+#define BRIDGE_ID_ULA bi.p_u.ula
+#define BRIDGE_ID bi.id
+
+/* JRP: on the network the flags field is between "type" and "root_id"
+ * this is unfortunated! To make the code portable to a RISC machine
+ * the pdus are now massaged a little bit for processing
+ */
+#define TOPOLOGY_CHANGE 0x01
+#define TOPOLOGY_CHANGE_ACK 0x80
+#define BRIDGE_BPDU_8021_CONFIG_SIZE 35 /* real size */
+#define BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET 4
+#define BRIDGE_BPDU_8021_PROTOCOL_ID 0
+#define BRIDGE_BPDU_8021_PROTOCOL_VERSION_ID 0
+#define BRIDGE_LLC1_HS 3
+#define BRIDGE_LLC1_DSAP 0x42
+#define BRIDGE_LLC1_SSAP 0x42
+#define BRIDGE_LLC1_CTRL 0x03
+
+typedef struct {
+ unsigned short protocol_id;
+ unsigned char protocol_version_id;
+ unsigned char type;
+ bridge_id_t root_id; /* (4.5.1.1) */
+ unsigned int root_path_cost; /* (4.5.1.2) */
+ bridge_id_t bridge_id; /* (4.5.1.3) */
+ unsigned short port_id; /* (4.5.1.4) */
+ unsigned short message_age; /* (4.5.1.5) */
+ unsigned short max_age; /* (4.5.1.6) */
+ unsigned short hello_time; /* (4.5.1.7) */
+ unsigned short forward_delay; /* (4.5.1.8) */
+ unsigned char top_change_ack;
+ unsigned char top_change;
+} Config_bpdu;
+
+#ifdef __LITTLE_ENDIAN
+#define config_bpdu_hton(config_bpdu) \
+ (config_bpdu)->root_path_cost = htonl((config_bpdu)->root_path_cost); \
+ (config_bpdu)->port_id = htons((config_bpdu)->port_id); \
+ (config_bpdu)->message_age = htons((config_bpdu)->message_age); \
+ (config_bpdu)->max_age = htons((config_bpdu)->max_age); \
+ (config_bpdu)->hello_time = htons((config_bpdu)->hello_time); \
+ (config_bpdu)->forward_delay = htons((config_bpdu)->forward_delay);
+#else
+#define config_bpdu_hton(config_bpdu)
+#endif
+#define config_bpdu_ntoh config_bpdu_hton
+
+
+/** Topology Change Notification BPDU Parameters (4.5.2) **/
+
+typedef struct {
+ unsigned short protocol_id;
+ unsigned char protocol_version_id;
+ unsigned char type;
+} Tcn_bpdu;
+
+#define BPDU_TYPE_CONFIG 0
+#define BPDU_TYPE_TOPO_CHANGE 128
+
+/** Bridge Parameters (4.5.3) **/
+typedef struct {
+ bridge_id_t designated_root; /* (4.5.3.1) */
+ unsigned int root_path_cost; /* (4.5.3.2) */
+ unsigned int root_port; /* (4.5.3.3) */
+ unsigned short max_age; /* (4.5.3.4) */
+ unsigned short hello_time; /* (4.5.3.5) */
+ unsigned short forward_delay; /* (4.5.3.6) */
+ bridge_id_t bridge_id; /* (4.5.3.7) */
+ unsigned short bridge_max_age; /* (4.5.3.8) */
+ unsigned short bridge_hello_time; /* (4.5.3.9) */
+ unsigned short bridge_forward_delay; /* (4.5.3.10) */
+ unsigned int top_change_detected; /* (4.5.3.11) */
+ unsigned int top_change; /* (4.5.3.12) */
+ unsigned short topology_change_time; /* (4.5.3.13) */
+ unsigned short hold_time; /* (4.5.3.14) */
+ unsigned int instance;
+} Bridge_data;
+
+/** Port Parameters (4.5.5) **/
+typedef struct {
+ unsigned short port_id; /* (4.5.5.1) */
+ unsigned int state; /* (4.5.5.2) */
+ unsigned int path_cost; /* (4.5.5.3) */
+ bridge_id_t designated_root; /* (4.5.5.4) */
+ unsigned int designated_cost; /* (4.5.5.5) */
+ bridge_id_t designated_bridge; /* (4.5.5.6) */
+ unsigned short designated_port; /* (4.5.5.7) */
+ unsigned int top_change_ack; /* (4.5.5.8) */
+ unsigned int config_pending; /* (4.5.5.9) */
+ bridge_id_t ifmac;
+ unsigned int admin_state;
+ char ifname[IFNAMSIZ]; /* Make life easier for brcfg */
+ struct device *dev;
+ struct fdb *fdb; /* head of per port fdb chain */
+} Port_data;
+
+
+
+/** types to support timers for this pseudo-implementation. **/
+typedef struct {
+ unsigned int active; /* timer in use. */
+ unsigned int value; /* current value of timer,
+ * counting up. */
+} Timer;
+
+struct fdb {
+ unsigned char ula[6];
+ unsigned char pad[2];
+ unsigned short port;
+ unsigned int timer;
+ unsigned short flags;
+#define FDB_ENT_VALID 0x01
+ unsigned short mcast_count;
+ unsigned int mcast_timer; /* oldest xxxxxcast */
+
+/* AVL tree of all addresses, sorted by address */
+ short fdb_avl_height;
+ struct fdb *fdb_avl_left;
+ struct fdb *fdb_avl_right;
+/* linked list of addresses for each port */
+ struct fdb *fdb_next;
+};
+
+/* data returned on BRCMD_DISPLAY_FDB */
+struct fdb_info {
+ unsigned char ula[6];
+ unsigned char port;
+ unsigned char flags;
+ unsigned int timer;
+};
+struct fdb_info_hdr {
+ int copied; /* nb of entries copied to user */
+ int not_copied; /* when user buffer is too small */
+ int cmd_time;
+};
+
+#define IS_BRIDGED 0x2e
+
+
+#define BR_MAX_PROTOCOLS 32
+#define BR_MAX_PROT_STATS BR_MAX_PROTOCOLS
+
+/* policy values for policy field */
+#define BR_ACCEPT 1
+#define BR_REJECT 0
+
+/* JRP: extra statistics for debug */
+typedef struct {
+ /* br_receive_frame counters */
+ int port_disable_up_stack;
+ int rcv_bpdu;
+ int notForwarding;
+ int forwarding_up_stack;
+ int unknown_state;
+
+ /* br_tx_frame counters */
+ int port_disable;
+ int port_not_disable;
+
+ /* br_forward counters */
+ int local_multicast;
+ int forwarded_multicast; /* up stack as well */
+ int flood_unicast;
+ int aged_flood_unicast;
+ int forwarded_unicast;
+ int forwarded_unicast_up_stack;
+ int forwarded_ip_up_stack;
+ int forwarded_ip_up_stack_lie; /* received on alternate device */
+ int arp_for_local_mac;
+ int drop_same_port;
+ int drop_same_port_aged;
+ int drop_multicast;
+} br_stats_counter;
+
+struct br_stat {
+ unsigned int flags;
+ Bridge_data bridge_data;
+ unsigned int policy;
+ unsigned int exempt_protocols;
+ unsigned short protocols[BR_MAX_PROTOCOLS];
+ unsigned short prot_id[BR_MAX_PROT_STATS]; /* Protocol encountered */
+ unsigned int prot_counter[BR_MAX_PROT_STATS]; /* How many packets ? */
+ br_stats_counter packet_cnts;
+ unsigned int num_ports;
+ Port_data port_data[BR_MAX_PORTS + 1];
+};
+
+/* defined flags for br_stat.flags */
+#define BR_UP 0x0001 /* bridging enabled */
+#define BR_DEBUG 0x0002 /* debugging enabled */
+#define BR_PROT_STATS 0x0004 /* protocol statistics enabled */
+#define BR_STP_DISABLED 0x0008 /* Spanning tree protocol disabled */
+
+struct br_cf {
+ unsigned int cmd;
+ unsigned int arg1;
+ unsigned int arg2;
+};
+
+/* defined cmds */
+#define BRCMD_BRIDGE_ENABLE 1
+#define BRCMD_BRIDGE_DISABLE 2
+#define BRCMD_PORT_ENABLE 3 /* arg1 = port */
+#define BRCMD_PORT_DISABLE 4 /* arg1 = port */
+#define BRCMD_SET_BRIDGE_PRIORITY 5 /* arg1 = priority */
+#define BRCMD_SET_PORT_PRIORITY 6 /* arg1 = port, arg2 = priority */
+#define BRCMD_SET_PATH_COST 7 /* arg1 = port, arg2 = cost */
+#define BRCMD_DISPLAY_FDB 8
+#define BRCMD_ENABLE_DEBUG 9
+#define BRCMD_DISABLE_DEBUG 10
+#define BRCMD_SET_POLICY 11 /* arg1 = default policy (1==bridge all) */
+#define BRCMD_EXEMPT_PROTOCOL 12 /* arg1 = protocol (see net/if_ether.h) */
+#define BRCMD_ENABLE_PROT_STATS 13
+#define BRCMD_DISABLE_PROT_STATS 14
+#define BRCMD_ZERO_PROT_STATS 15
+#define BRCMD_TOGGLE_STP 16
+#define BRCMD_IF_ENABLE 17 /* arg1 = if_index */
+#define BRCMD_IF_DISABLE 18 /* arg1 = if_index */
+#define BRCMD_SET_IF_PRIORITY 19 /* arg1 = if_index, arg2 = priority */
+#define BRCMD_SET_IF_PATH_COST 20 /* arg1 = if_index, arg2 = cost */
+
+/* prototypes of exported bridging functions... */
+
+#ifdef __KERNEL__
+void br_init(void);
+int br_receive_frame(struct sk_buff *skb); /* 3.5 */
+int br_tx_frame(struct sk_buff *skb);
+int brg_init(void);
+int br_ioctl(unsigned int cmd, void *arg);
+void requeue_fdb(struct fdb *node, int new_port);
+
+struct fdb *br_avl_find_addr(unsigned char addr[6]);
+struct fdb *br_avl_insert (struct fdb * new_node);
+void sprintf_avl (char **pbuffer, struct fdb * tree, off_t *pos,int* len, off_t offset, int length);
+int br_tree_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
+void br_avl_delete_by_port(int port);
+int br_call_bridge(struct sk_buff *skb, unsigned short type);
+void br_spacedevice_register(void);
+
+/* externs */
+
+extern struct br_stat br_stats;
+extern Port_data port_info[];
+
+#endif
diff --git a/pfinet/linux-src/include/net/checksum.h b/pfinet/linux-src/include/net/checksum.h
new file mode 100644
index 00000000..041d4760
--- /dev/null
+++ b/pfinet/linux-src/include/net/checksum.h
@@ -0,0 +1,110 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Checksumming functions for IP, TCP, UDP and so on
+ *
+ * Authors: Jorge Cwik, <jorge@laser.satlink.net>
+ * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ * Borrows very liberally from tcp.c and ip.c, see those
+ * files for more names.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * Fixes:
+ *
+ * Ralf Baechle : generic ipv6 checksum
+ * <ralf@waldorf-gmbh.de>
+ */
+
+#ifndef _CHECKSUM_H
+#define _CHECKSUM_H
+
+#include <asm/types.h>
+#include <asm/byteorder.h>
+#include <net/ip.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+
+#ifndef _HAVE_ARCH_IPV6_CSUM
+
+static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
+ struct in6_addr *daddr,
+ __u16 len,
+ unsigned short proto,
+ unsigned int csum)
+{
+
+ int carry;
+ __u32 ulen;
+ __u32 uproto;
+
+ csum += saddr->s6_addr32[0];
+ carry = (csum < saddr->s6_addr32[0]);
+ csum += carry;
+
+ csum += saddr->s6_addr32[1];
+ carry = (csum < saddr->s6_addr32[1]);
+ csum += carry;
+
+ csum += saddr->s6_addr32[2];
+ carry = (csum < saddr->s6_addr32[2]);
+ csum += carry;
+
+ csum += saddr->s6_addr32[3];
+ carry = (csum < saddr->s6_addr32[3]);
+ csum += carry;
+
+ csum += daddr->s6_addr32[0];
+ carry = (csum < daddr->s6_addr32[0]);
+ csum += carry;
+
+ csum += daddr->s6_addr32[1];
+ carry = (csum < daddr->s6_addr32[1]);
+ csum += carry;
+
+ csum += daddr->s6_addr32[2];
+ carry = (csum < daddr->s6_addr32[2]);
+ csum += carry;
+
+ csum += daddr->s6_addr32[3];
+ carry = (csum < daddr->s6_addr32[3]);
+ csum += carry;
+
+ ulen = htonl((__u32) len);
+ csum += ulen;
+ carry = (csum < ulen);
+ csum += carry;
+
+ uproto = htonl(proto);
+ csum += uproto;
+ carry = (csum < uproto);
+ csum += carry;
+
+ return csum_fold(csum);
+}
+
+#endif
+
+#ifndef _HAVE_ARCH_COPY_AND_CSUM_FROM_USER
+extern __inline__
+unsigned int csum_and_copy_from_user (const char *src, char *dst,
+ int len, int sum, int *err_ptr)
+{
+ if (verify_area(VERIFY_READ, src, len) == 0)
+ return csum_partial_copy_from_user(src, dst, len, sum, err_ptr);
+
+ if (len)
+ *err_ptr = -EFAULT;
+
+ return sum;
+}
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/net/datalink.h b/pfinet/linux-src/include/net/datalink.h
new file mode 100644
index 00000000..44e56990
--- /dev/null
+++ b/pfinet/linux-src/include/net/datalink.h
@@ -0,0 +1,16 @@
+#ifndef _NET_INET_DATALINK_H_
+#define _NET_INET_DATALINK_H_
+
+struct datalink_proto {
+ unsigned short type_len;
+ unsigned char type[8];
+ const char *string_name;
+ unsigned short header_length;
+ int (*rcvfunc)(struct sk_buff *, struct device *,
+ struct packet_type *);
+ void (*datalink_header)(struct datalink_proto *, struct sk_buff *,
+ unsigned char *);
+ struct datalink_proto *next;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/net/dst.h b/pfinet/linux-src/include/net/dst.h
new file mode 100644
index 00000000..baf4f414
--- /dev/null
+++ b/pfinet/linux-src/include/net/dst.h
@@ -0,0 +1,178 @@
+/*
+ * net/dst.h Protocol independent destination cache definitions.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#ifndef _NET_DST_H
+#define _NET_DST_H
+
+#include <linux/config.h>
+#include <net/neighbour.h>
+
+/*
+ * 0 - no debugging messages
+ * 1 - rare events and bugs (default)
+ * 2 - trace mode.
+ */
+#define RT_CACHE_DEBUG 0
+
+#define DST_GC_MIN (1*HZ)
+#define DST_GC_INC (5*HZ)
+#define DST_GC_MAX (120*HZ)
+
+struct sk_buff;
+
+struct dst_entry
+{
+ struct dst_entry *next;
+ atomic_t refcnt; /* tree/hash references */
+ atomic_t use; /* client references */
+ struct device *dev;
+ int obsolete;
+ unsigned long lastuse;
+ unsigned long expires;
+ unsigned mxlock;
+ unsigned pmtu;
+ unsigned window;
+ unsigned rtt;
+ unsigned long rate_last; /* rate limiting for ICMP */
+ unsigned long rate_tokens;
+
+ int error;
+
+ struct neighbour *neighbour;
+ struct hh_cache *hh;
+
+ int (*input)(struct sk_buff*);
+ int (*output)(struct sk_buff*);
+
+#ifdef CONFIG_NET_CLS_ROUTE
+ __u32 tclassid;
+#endif
+
+ struct dst_ops *ops;
+
+ char info[0];
+};
+
+
+struct dst_ops
+{
+ unsigned short family;
+ unsigned short protocol;
+ unsigned gc_thresh;
+
+ int (*gc)(void);
+ struct dst_entry * (*check)(struct dst_entry *, __u32 cookie);
+ struct dst_entry * (*reroute)(struct dst_entry *,
+ struct sk_buff *);
+ void (*destroy)(struct dst_entry *);
+ struct dst_entry * (*negative_advice)(struct dst_entry *);
+ void (*link_failure)(struct sk_buff *);
+
+ atomic_t entries;
+};
+
+#ifdef __KERNEL__
+
+extern struct dst_entry * dst_garbage_list;
+extern atomic_t dst_total;
+
+extern __inline__
+struct dst_entry * dst_clone(struct dst_entry * dst)
+{
+ if (dst)
+ atomic_inc(&dst->use);
+ return dst;
+}
+
+extern __inline__
+void dst_release(struct dst_entry * dst)
+{
+ if (dst)
+ atomic_dec(&dst->use);
+}
+
+/* The following primitive should be use if and only if
+ destination entry has just been removed from a location
+ accessed directly by hard irq.
+ */
+extern __inline__
+void dst_release_irqwait(struct dst_entry * dst)
+{
+ if (dst) {
+ synchronize_irq();
+ atomic_dec(&dst->use);
+ }
+}
+
+extern __inline__
+struct dst_entry * dst_check(struct dst_entry ** dst_p, u32 cookie)
+{
+ struct dst_entry * dst = *dst_p;
+ if (dst && dst->obsolete)
+ dst = dst->ops->check(dst, cookie);
+ return (*dst_p = dst);
+}
+
+extern __inline__
+struct dst_entry * dst_reroute(struct dst_entry ** dst_p, struct sk_buff *skb)
+{
+ struct dst_entry * dst = *dst_p;
+ if (dst && dst->obsolete)
+ dst = dst->ops->reroute(dst, skb);
+ return (*dst_p = dst);
+}
+
+
+extern void * dst_alloc(int size, struct dst_ops * ops);
+extern void __dst_free(struct dst_entry * dst);
+extern void dst_destroy(struct dst_entry * dst);
+
+extern __inline__
+void dst_free(struct dst_entry * dst)
+{
+ if (dst->obsolete > 1)
+ return;
+ if (!atomic_read(&dst->use)) {
+ dst_destroy(dst);
+ return;
+ }
+ __dst_free(dst);
+}
+
+extern __inline__ void dst_confirm(struct dst_entry *dst)
+{
+ if (dst)
+ neigh_confirm(dst->neighbour);
+}
+
+extern __inline__ void dst_negative_advice(struct dst_entry **dst_p)
+{
+ struct dst_entry * dst = *dst_p;
+ if (dst && dst->ops->negative_advice)
+ *dst_p = dst->ops->negative_advice(dst);
+}
+
+extern __inline__ void dst_link_failure(struct sk_buff *skb)
+{
+ struct dst_entry * dst = skb->dst;
+ if (dst && dst->ops && dst->ops->link_failure)
+ dst->ops->link_failure(skb);
+}
+
+extern __inline__ void dst_set_expires(struct dst_entry *dst, int timeout)
+{
+ unsigned long expires = jiffies + timeout;
+
+ if (expires == 0)
+ expires = 1;
+
+ if (dst->expires == 0 || (long)(dst->expires - expires) > 0)
+ dst->expires = expires;
+}
+#endif
+
+#endif /* _NET_DST_H */
diff --git a/pfinet/linux-src/include/net/flow.h b/pfinet/linux-src/include/net/flow.h
new file mode 100644
index 00000000..e1ce1b2a
--- /dev/null
+++ b/pfinet/linux-src/include/net/flow.h
@@ -0,0 +1,101 @@
+/*
+ *
+ * Flow based forwarding rules (usage: firewalling, etc)
+ *
+ */
+
+#ifndef _NET_FLOW_H
+#define _NET_FLOW_H
+
+struct flowi {
+ int proto; /* {TCP, UDP, ICMP} */
+
+ union {
+ struct {
+ __u32 daddr;
+ __u32 saddr;
+ } ip4_u;
+
+ struct {
+ struct in6_addr * daddr;
+ struct in6_addr * saddr;
+ __u32 flowlabel;
+ } ip6_u;
+ } nl_u;
+#define fl6_dst nl_u.ip6_u.daddr
+#define fl6_src nl_u.ip6_u.saddr
+#define fl6_flowlabel nl_u.ip6_u.flowlabel
+#define fl4_dst nl_u.ip4_u.daddr
+#define fl4_src nl_u.ip4_u.saddr
+
+ int oif;
+
+ union {
+ struct {
+ __u16 sport;
+ __u16 dport;
+ } ports;
+
+ struct {
+ __u8 type;
+ __u8 code;
+ } icmpt;
+
+ unsigned long data;
+ } uli_u;
+};
+
+#define FLOWR_NODECISION 0 /* rule not appliable to flow */
+#define FLOWR_SELECT 1 /* flow must follow this rule */
+#define FLOWR_CLEAR 2 /* priority level clears flow */
+#define FLOWR_ERROR 3
+
+struct fl_acc_args {
+ int type;
+
+
+#define FL_ARG_FORWARD 1
+#define FL_ARG_ORIGIN 2
+
+ union {
+ struct sk_buff *skb;
+ struct {
+ struct sock *sk;
+ struct flowi *flow;
+ } fl_o;
+ } fl_u;
+};
+
+
+struct pkt_filter {
+ atomic_t refcnt;
+ unsigned int offset;
+ __u32 value;
+ __u32 mask;
+ struct pkt_filter *next;
+};
+
+#define FLR_INPUT 1
+#define FLR_OUTPUT 2
+
+struct flow_filter {
+ int type;
+ union {
+ struct pkt_filter *filter;
+ struct sock *sk;
+ } u;
+};
+
+struct flow_rule {
+ struct flow_rule_ops *ops;
+ unsigned char private[0];
+};
+
+struct flow_rule_ops {
+ int (*accept)(struct rt6_info *rt,
+ struct rt6_info *rule,
+ struct fl_acc_args *args,
+ struct rt6_info **nrt);
+};
+
+#endif
diff --git a/pfinet/linux-src/include/net/icmp.h b/pfinet/linux-src/include/net/icmp.h
new file mode 100644
index 00000000..6e9c5418
--- /dev/null
+++ b/pfinet/linux-src/include/net/icmp.h
@@ -0,0 +1,42 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the ICMP module.
+ *
+ * Version: @(#)icmp.h 1.0.4 05/13/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ICMP_H
+#define _ICMP_H
+
+#include <linux/icmp.h>
+#include <linux/skbuff.h>
+
+#include <net/sock.h>
+#include <net/protocol.h>
+
+extern struct icmp_err icmp_err_convert[];
+extern struct icmp_mib icmp_statistics;
+
+extern void icmp_send(struct sk_buff *skb_in, int type, int code,
+ unsigned long info);
+extern int icmp_rcv(struct sk_buff *skb, unsigned short len);
+extern int icmp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+extern void icmp_init(struct net_proto_family *ops);
+
+/* Move into dst.h ? */
+extern int xrlim_allow(struct dst_entry *dst, int timeout);
+
+/* CONFIG_IP_TRANSPARENT_PROXY */
+extern int icmp_chkaddr(struct sk_buff *skb);
+
+#endif /* _ICMP_H */
diff --git a/pfinet/linux-src/include/net/if_inet6.h b/pfinet/linux-src/include/net/if_inet6.h
new file mode 100644
index 00000000..4e9ed978
--- /dev/null
+++ b/pfinet/linux-src/include/net/if_inet6.h
@@ -0,0 +1,134 @@
+/*
+ * inet6 interface/address list definitions
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _NET_IF_INET6_H
+#define _NET_IF_INET6_H
+
+/* These flags match corresponding IFA_F_* flags but ADDR_INVALID,
+ which is invisible externally.
+ */
+
+#define ADDR_PERMANENT 0x80
+
+#define DAD_COMPLETE 0x00
+#define DAD_INCOMPLETE 0x40
+#define DAD_STATUS 0x40
+
+#define ADDR_STATUS 0x21
+#define ADDR_DEPRECATED 0x20
+#define ADDR_INVALID 0x01
+
+
+
+#define IF_RA_RCVD 0x20
+#define IF_RS_SENT 0x10
+
+#ifdef __KERNEL__
+
+struct inet6_ifaddr
+{
+ struct in6_addr addr;
+ __u32 prefix_len;
+
+ __u32 valid_lft;
+ __u32 prefered_lft;
+ unsigned long tstamp;
+
+ __u8 probes;
+ __u8 flags;
+
+ __u16 scope;
+
+ struct timer_list timer;
+
+ struct inet6_dev *idev;
+
+ struct inet6_ifaddr *lst_next; /* next addr in addr_lst */
+ struct inet6_ifaddr *if_next; /* next addr in inet6_dev */
+};
+
+
+struct ipv6_mc_socklist
+{
+ struct in6_addr addr;
+ int ifindex;
+ struct ipv6_mc_socklist *next;
+};
+
+#define MAF_TIMER_RUNNING 0x01
+#define MAF_LAST_REPORTER 0x02
+#define MAF_LOADED 0x04
+
+struct ifmcaddr6
+{
+ struct in6_addr mca_addr;
+ struct device *dev;
+ struct ifmcaddr6 *next;
+ struct ifmcaddr6 *if_next;
+ struct timer_list mca_timer;
+ unsigned mca_flags;
+ atomic_t mca_users;
+};
+
+#define IFA_HOST IPV6_ADDR_LOOPBACK
+#define IFA_LINK IPV6_ADDR_LINKLOCAL
+#define IFA_SITE IPV6_ADDR_SITELOCAL
+#define IFA_GLOBAL 0x0000U
+
+struct ipv6_devconf
+{
+ int forwarding;
+ int hop_limit;
+ int mtu6;
+ int accept_ra;
+ int accept_redirects;
+ int autoconf;
+ int dad_transmits;
+ int rtr_solicits;
+ int rtr_solicit_interval;
+ int rtr_solicit_delay;
+
+ void *sysctl;
+};
+
+struct inet6_dev
+{
+ struct device *dev;
+
+ struct inet6_ifaddr *addr_list;
+ struct ifmcaddr6 *mc_list;
+ __u32 if_flags;
+
+ struct neigh_parms *nd_parms;
+ struct inet6_dev *next;
+ struct ipv6_devconf cnf;
+};
+
+extern struct ipv6_devconf ipv6_devconf;
+
+extern __inline__ void ipv6_eth_mc_map(struct in6_addr *addr, char *buf)
+{
+ /*
+ * +-------+-------+-------+-------+-------+-------+
+ * | 33 | 33 | DST13 | DST14 | DST15 | DST16 |
+ * +-------+-------+-------+-------+-------+-------+
+ */
+
+ buf[0]= 0x33;
+ buf[1]= 0x33;
+
+ memcpy(buf + 2, &addr->s6_addr32[3], sizeof(__u32));
+}
+#endif
+#endif
diff --git a/pfinet/linux-src/include/net/inet_common.h b/pfinet/linux-src/include/net/inet_common.h
new file mode 100644
index 00000000..80a6b343
--- /dev/null
+++ b/pfinet/linux-src/include/net/inet_common.h
@@ -0,0 +1,43 @@
+#ifndef _INET_COMMON_H
+#define _INET_COMMON_H
+
+extern struct proto_ops inet_stream_ops;
+extern struct proto_ops inet_dgram_ops;
+
+/*
+ * INET4 prototypes used by INET6
+ */
+
+extern void inet_remove_sock(struct sock *sk1);
+extern void inet_put_sock(unsigned short num,
+ struct sock *sk);
+extern int inet_release(struct socket *sock,
+ struct socket *peer);
+extern int inet_stream_connect(struct socket *sock,
+ struct sockaddr * uaddr,
+ int addr_len, int flags);
+extern int inet_dgram_connect(struct socket *sock,
+ struct sockaddr * uaddr,
+ int addr_len, int flags);
+extern int inet_accept(struct socket *sock,
+ struct socket *newsock, int flags);
+extern int inet_recvmsg(struct socket *sock,
+ struct msghdr *ubuf,
+ int size, int flags, struct scm_cookie *scm);
+extern int inet_sendmsg(struct socket *sock,
+ struct msghdr *msg,
+ int size, struct scm_cookie *scm);
+extern int inet_shutdown(struct socket *sock, int how);
+extern unsigned int inet_poll(struct file * file, struct socket *sock, struct poll_table_struct *wait);
+extern int inet_setsockopt(struct socket *sock, int level,
+ int optname, char *optval,
+ int optlen);
+extern int inet_getsockopt(struct socket *sock, int level,
+ int optname, char *optval,
+ int *optlen);
+extern int inet_fcntl(struct socket *sock,
+ unsigned int cmd,
+ unsigned long arg);
+extern int inet_listen(struct socket *sock, int backlog);
+
+#endif
diff --git a/pfinet/linux-src/include/net/ip.h b/pfinet/linux-src/include/net/ip.h
new file mode 100644
index 00000000..1a6cb610
--- /dev/null
+++ b/pfinet/linux-src/include/net/ip.h
@@ -0,0 +1,267 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the IP module.
+ *
+ * Version: @(#)ip.h 1.0.2 05/07/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ *
+ * Changes:
+ * Mike McLagan : Routing by source
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _IP_H
+#define _IP_H
+
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/ip.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/in_route.h>
+#include <net/route.h>
+#include <net/arp.h>
+
+#ifndef _SNMP_H
+#include <net/snmp.h>
+#endif
+
+#include <net/sock.h> /* struct sock */
+
+struct inet_skb_parm
+{
+ struct ip_options opt; /* Compiled IP options */
+ u16 redirport; /* Redirect port */
+ unsigned char flags;
+
+#define IPSKB_MASQUERADED 1
+#define IPSKB_TRANSLATED 2
+#define IPSKB_FORWARDED 4
+};
+
+struct ipcm_cookie
+{
+ u32 addr;
+ int oif;
+ struct ip_options *opt;
+};
+
+#define IPCB(skb) ((struct inet_skb_parm*)((skb)->cb))
+
+struct ip_ra_chain
+{
+ struct ip_ra_chain *next;
+ struct sock *sk;
+ void (*destructor)(struct sock *);
+};
+
+extern struct ip_ra_chain *ip_ra_chain;
+
+/* IP flags. */
+#define IP_CE 0x8000 /* Flag: "Congestion" */
+#define IP_DF 0x4000 /* Flag: "Don't Fragment" */
+#define IP_MF 0x2000 /* Flag: "More Fragments" */
+#define IP_OFFSET 0x1FFF /* "Fragment Offset" part */
+
+#define IP_FRAG_TIME (30 * HZ) /* fragment lifetime */
+
+extern void ip_mc_dropsocket(struct sock *);
+extern void ip_mc_dropdevice(struct device *dev);
+extern int ip_mc_procinfo(char *, char **, off_t, int, int);
+
+/*
+ * Functions provided by ip.c
+ */
+
+extern int ip_ioctl(struct sock *sk, int cmd, unsigned long arg);
+extern void ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
+ u32 saddr, u32 daddr,
+ struct ip_options *opt);
+extern int ip_rcv(struct sk_buff *skb, struct device *dev,
+ struct packet_type *pt);
+extern int ip_local_deliver(struct sk_buff *skb);
+extern int ip_mr_input(struct sk_buff *skb);
+extern int ip_output(struct sk_buff *skb);
+extern int ip_mc_output(struct sk_buff *skb);
+extern void ip_fragment(struct sk_buff *skb, int (*out)(struct sk_buff*));
+extern int ip_do_nat(struct sk_buff *skb);
+extern void ip_send_check(struct iphdr *ip);
+extern int ip_id_count;
+extern void ip_queue_xmit(struct sk_buff *skb);
+extern void ip_init(void);
+extern int ip_build_xmit(struct sock *sk,
+ int getfrag (const void *,
+ char *,
+ unsigned int,
+ unsigned int),
+ const void *frag,
+ unsigned length,
+ struct ipcm_cookie *ipc,
+ struct rtable *rt,
+ int flags);
+
+
+struct ip_reply_arg {
+ struct iovec iov[2];
+ int n_iov; /* redundant */
+ u32 csum;
+ int csumoffset; /* u16 offset of csum in iov[0].iov_base */
+ /* -1 if not needed */
+};
+
+void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *arg,
+ unsigned int len);
+
+extern int __ip_finish_output(struct sk_buff *skb);
+
+struct ipv4_config
+{
+ int log_martians;
+ int autoconfig;
+ int no_pmtu_disc;
+};
+
+extern struct ipv4_config ipv4_config;
+extern struct ip_mib ip_statistics;
+extern struct linux_mib net_statistics;
+
+extern int sysctl_local_port_range[2];
+
+extern __inline__ int ip_finish_output(struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb->dst;
+ struct device *dev = dst->dev;
+ struct hh_cache *hh = dst->hh;
+
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_IP);
+
+ if (hh) {
+ read_lock_irq(&hh->hh_lock);
+ memcpy(skb->data - 16, hh->hh_data, 16);
+ read_unlock_irq(&hh->hh_lock);
+ skb_push(skb, dev->hard_header_len);
+ return hh->hh_output(skb);
+ } else if (dst->neighbour)
+ return dst->neighbour->output(skb);
+
+ kfree_skb(skb);
+ return -EINVAL;
+}
+
+extern __inline__ void ip_send(struct sk_buff *skb)
+{
+ if (skb->len > skb->dst->pmtu)
+ ip_fragment(skb, __ip_finish_output);
+ else
+ ip_finish_output(skb);
+}
+
+extern __inline__
+int ip_decrease_ttl(struct iphdr *iph)
+{
+ u16 check = iph->check;
+ check = ntohs(check) + 0x0100;
+ if ((check & 0xFF00) == 0)
+ check++; /* carry overflow */
+ iph->check = htons(check);
+ return --iph->ttl;
+}
+
+extern __inline__
+int ip_dont_fragment(struct sock *sk, struct dst_entry *dst)
+{
+ return (sk->ip_pmtudisc == IP_PMTUDISC_DO ||
+ (sk->ip_pmtudisc == IP_PMTUDISC_WANT &&
+ !(dst->mxlock&(1<<RTAX_MTU))));
+}
+
+/*
+ * Map a multicast IP onto multicast MAC for type ethernet.
+ */
+
+extern __inline__ void ip_eth_mc_map(u32 addr, char *buf)
+{
+ addr=ntohl(addr);
+ buf[0]=0x01;
+ buf[1]=0x00;
+ buf[2]=0x5e;
+ buf[5]=addr&0xFF;
+ addr>>=8;
+ buf[4]=addr&0xFF;
+ addr>>=8;
+ buf[3]=addr&0x7F;
+}
+
+/*
+ * Map a multicast IP onto multicast MAC for type Token Ring.
+ * This conforms to RFC1469 Option 2 Multicasting i.e.
+ * using a functional address to transmit / receive
+ * multicast packets.
+ */
+
+extern __inline__ void ip_tr_mc_map(u32 addr, char *buf)
+{
+ buf[0]=0xC0;
+ buf[1]=0x00;
+ buf[2]=0x00;
+ buf[3]=0x04;
+ buf[4]=0x00;
+ buf[5]=0x00;
+}
+
+extern int ip_call_ra_chain(struct sk_buff *skb);
+
+/*
+ * Functions provided by ip_fragment.o
+ */
+
+struct sk_buff *ip_defrag(struct sk_buff *skb);
+
+/*
+ * Functions provided by ip_forward.c
+ */
+
+extern int ip_forward(struct sk_buff *skb);
+extern int ip_net_unreachable(struct sk_buff *skb);
+
+/*
+ * Functions provided by ip_options.c
+ */
+
+extern void ip_options_build(struct sk_buff *skb, struct ip_options *opt, u32 daddr, struct rtable *rt, int is_frag);
+extern int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb);
+extern void ip_options_fragment(struct sk_buff *skb);
+extern int ip_options_compile(struct ip_options *opt, struct sk_buff *skb);
+extern int ip_options_get(struct ip_options **optp, unsigned char *data, int optlen, int user);
+extern void ip_options_undo(struct ip_options * opt);
+extern void ip_forward_options(struct sk_buff *skb);
+extern int ip_options_rcv_srr(struct sk_buff *skb);
+
+/*
+ * Functions provided by ip_sockglue.c
+ */
+
+extern void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb);
+extern int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc);
+extern int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen);
+extern int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen);
+extern int ip_ra_control(struct sock *sk, unsigned char on, void (*destructor)(struct sock *));
+
+extern int ip_recv_error(struct sock *sk, struct msghdr *msg, int len);
+extern void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
+ u16 port, u32 info, u8 *payload);
+extern void ip_local_error(struct sock *sk, int err, u32 daddr, u16 dport,
+ u32 info);
+
+#endif /* _IP_H */
diff --git a/pfinet/linux-src/include/net/ip6_fib.h b/pfinet/linux-src/include/net/ip6_fib.h
new file mode 100644
index 00000000..efd652f2
--- /dev/null
+++ b/pfinet/linux-src/include/net/ip6_fib.h
@@ -0,0 +1,177 @@
+/*
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _IP6_FIB_H
+#define _IP6_FIB_H
+
+#ifdef __KERNEL__
+
+#include <linux/ipv6_route.h>
+
+#include <net/dst.h>
+#include <net/flow.h>
+#include <linux/rtnetlink.h>
+
+struct rt6_info;
+
+struct fib6_node
+{
+ struct fib6_node *parent;
+ struct fib6_node *left;
+ struct fib6_node *right;
+
+ struct fib6_node *subtree;
+
+ struct rt6_info *leaf;
+
+ __u16 fn_bit; /* bit key */
+ __u16 fn_flags;
+ __u32 fn_sernum;
+};
+
+
+/*
+ * routing information
+ *
+ */
+
+struct rt6key
+{
+ struct in6_addr addr;
+ int plen;
+};
+
+struct rt6_info
+{
+ union {
+ struct dst_entry dst;
+ struct rt6_info *next;
+ } u;
+
+#define rt6i_dev u.dst.dev
+#define rt6i_nexthop u.dst.neighbour
+#define rt6i_expires u.dst.expires
+
+ struct fib6_node *rt6i_node;
+
+ struct in6_addr rt6i_gateway;
+
+ u32 rt6i_flags;
+ u32 rt6i_metric;
+ u8 rt6i_hoplimit;
+ atomic_t rt6i_ref;
+
+ union {
+ struct flow_rule *rt6iu_flowr;
+ struct flow_filter *rt6iu_filter;
+ } flow_u;
+
+#define rt6i_flowr flow_u.rt6iu_flowr
+#define rt6i_filter flow_u.rt6iu_filter
+
+ struct rt6key rt6i_dst;
+ struct rt6key rt6i_src;
+};
+
+struct fib6_walker_t
+{
+ struct fib6_walker_t *prev, *next;
+ struct fib6_node *root, *node;
+ struct rt6_info *leaf;
+ unsigned char state;
+ unsigned char prune;
+ int (*func)(struct fib6_walker_t *);
+ void *args;
+};
+
+extern struct fib6_walker_t fib6_walker_list;
+
+extern __inline__ void fib6_walker_link(struct fib6_walker_t *w)
+{
+ w->next = fib6_walker_list.next;
+ w->prev = &fib6_walker_list;
+ w->next->prev = w;
+ w->prev->next = w;
+}
+
+extern __inline__ void fib6_walker_unlink(struct fib6_walker_t *w)
+{
+ w->next->prev = w->prev;
+ w->prev->next = w->next;
+ w->prev = w->next = w;
+}
+
+struct rt6_statistics {
+ __u32 fib_nodes;
+ __u32 fib_route_nodes;
+ __u32 fib_rt_alloc; /* permanet routes */
+ __u32 fib_rt_entries; /* rt entries in table */
+ __u32 fib_rt_cache; /* cache routes */
+};
+
+#define RTN_TL_ROOT 0x0001
+#define RTN_ROOT 0x0002 /* tree root node */
+#define RTN_RTINFO 0x0004 /* node with valid routing info */
+
+/*
+ * priority levels (or metrics)
+ *
+ */
+
+#define RTPRI_FIREWALL 8 /* Firewall control information */
+#define RTPRI_FLOW 16 /* Flow based forwarding rules */
+#define RTPRI_KERN_CTL 32 /* Kernel control routes */
+
+#define RTPRI_USER_MIN 256 /* Mimimum user priority */
+#define RTPRI_USER_MAX 1024 /* Maximum user priority */
+
+#define RTPRI_KERN_DFLT 4096 /* Kernel default routes */
+
+#define MAX_FLOW_BACKTRACE 32
+
+
+typedef void (*f_pnode)(struct fib6_node *fn, void *);
+
+extern struct fib6_node ip6_routing_table;
+
+/*
+ * exported functions
+ */
+
+extern struct fib6_node *fib6_lookup(struct fib6_node *root,
+ struct in6_addr *daddr,
+ struct in6_addr *saddr);
+
+struct fib6_node *fib6_locate(struct fib6_node *root,
+ struct in6_addr *daddr, int dst_len,
+ struct in6_addr *saddr, int src_len);
+
+extern void fib6_clean_tree(struct fib6_node *root,
+ int (*func)(struct rt6_info *, void *arg),
+ int prune, void *arg);
+
+extern int fib6_walk(struct fib6_walker_t *w);
+extern int fib6_walk_continue(struct fib6_walker_t *w);
+
+extern int fib6_add(struct fib6_node *root,
+ struct rt6_info *rt);
+
+extern int fib6_del(struct rt6_info *rt);
+
+extern void inet6_rt_notify(int event, struct rt6_info *rt);
+
+extern void fib6_run_gc(unsigned long dummy);
+
+extern void fib6_gc_cleanup(void);
+
+#endif
+#endif
diff --git a/pfinet/linux-src/include/net/ip6_fw.h b/pfinet/linux-src/include/net/ip6_fw.h
new file mode 100644
index 00000000..7866273d
--- /dev/null
+++ b/pfinet/linux-src/include/net/ip6_fw.h
@@ -0,0 +1,54 @@
+#ifndef __NET_IP6_FW_H
+#define __NET_IP6_FW_H
+
+#define IP6_FW_LISTHEAD 0x1000
+#define IP6_FW_ACCEPT 0x0001
+#define IP6_FW_REJECT 0x0002
+
+#define IP6_FW_DEBUG 2
+
+#define IP6_FW_MSG_ADD 1
+#define IP6_FW_MSG_DEL 2
+#define IP6_FW_MSG_REPORT 3
+
+
+/*
+ * Fast "hack" user interface
+ */
+struct ip6_fw_msg {
+ struct in6_addr dst;
+ struct in6_addr src;
+ int dst_len;
+ int src_len;
+ int action;
+ int policy;
+ int proto;
+ union {
+ struct {
+ __u16 sport;
+ __u16 dport;
+ } transp;
+
+ unsigned long data;
+
+ int icmp_type;
+ } u;
+
+ int msg_len;
+};
+
+#ifdef __KERNEL__
+
+#include <net/flow.h>
+
+struct ip6_fw_rule {
+ struct flow_rule flowr;
+ struct ip6_fw_rule *next;
+ struct ip6_fw_rule *prev;
+ struct flowi info;
+ unsigned long policy;
+};
+
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/net/ip6_route.h b/pfinet/linux-src/include/net/ip6_route.h
new file mode 100644
index 00000000..9311cc34
--- /dev/null
+++ b/pfinet/linux-src/include/net/ip6_route.h
@@ -0,0 +1,111 @@
+#ifndef _NET_IP6_ROUTE_H
+#define _NET_IP6_ROUTE_H
+
+#define IP6_RT_PRIO_FW 16
+#define IP6_RT_PRIO_USER 1024
+#define IP6_RT_PRIO_ADDRCONF 256
+#define IP6_RT_PRIO_KERN 512
+#define IP6_RT_FLOW_MASK 0x00ff
+
+#ifdef __KERNEL__
+
+#include <net/flow.h>
+#include <net/ip6_fib.h>
+
+struct pol_chain {
+ int type;
+ int priority;
+ struct fib6_node *rules;
+ struct pol_chain *next;
+};
+
+extern struct rt6_info ip6_null_entry;
+
+extern int ip6_rt_max_size;
+extern int ip6_rt_gc_min;
+extern int ip6_rt_gc_timeout;
+extern int ip6_rt_gc_interval;
+
+extern void ip6_route_input(struct sk_buff *skb);
+
+extern struct dst_entry * ip6_route_output(struct sock *sk,
+ struct flowi *fl);
+
+extern void ip6_route_init(void);
+extern void ip6_route_cleanup(void);
+
+extern int ipv6_route_ioctl(unsigned int cmd, void *arg);
+
+extern int ip6_route_add(struct in6_rtmsg *rtmsg);
+extern int ip6_del_rt(struct rt6_info *);
+
+extern int ip6_rt_addr_add(struct in6_addr *addr,
+ struct device *dev);
+
+extern int ip6_rt_addr_del(struct in6_addr *addr,
+ struct device *dev);
+
+extern void rt6_sndmsg(int type, struct in6_addr *dst,
+ struct in6_addr *src,
+ struct in6_addr *gw,
+ struct device *dev,
+ int dstlen, int srclen,
+ int metric, __u32 flags);
+
+extern struct rt6_info *rt6_lookup(struct in6_addr *daddr,
+ struct in6_addr *saddr,
+ int oif, int flags);
+
+/*
+ * support functions for ND
+ *
+ */
+extern struct rt6_info * rt6_get_dflt_router(struct in6_addr *addr,
+ struct device *dev);
+extern struct rt6_info * rt6_add_dflt_router(struct in6_addr *gwaddr,
+ struct device *dev);
+
+extern void rt6_purge_dflt_routers(int lst_resort);
+
+extern void rt6_redirect(struct in6_addr *dest,
+ struct in6_addr *saddr,
+ struct neighbour *neigh,
+ int on_link);
+
+extern void rt6_pmtu_discovery(struct in6_addr *daddr,
+ struct in6_addr *saddr,
+ struct device *dev,
+ u32 pmtu);
+
+struct nlmsghdr;
+struct netlink_callback;
+extern int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb);
+extern int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
+extern int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
+extern int inet6_rtm_getroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
+
+extern void rt6_ifdown(struct device *dev);
+extern void rt6_mtu_change(struct device *dev, unsigned mtu);
+
+/*
+ * Store a destination cache entry in a socket
+ * For UDP/RAW sockets this is done on udp_connect.
+ */
+
+extern __inline__ void ip6_dst_store(struct sock *sk, struct dst_entry *dst,
+ struct in6_addr *daddr)
+{
+ struct ipv6_pinfo *np;
+ struct rt6_info *rt;
+
+ np = &sk->net_pinfo.af_inet6;
+ dst_release(xchg(&sk->dst_cache,dst));
+
+ rt = (struct rt6_info *) dst;
+
+ np->daddr_cache = daddr;
+ np->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0;
+}
+
+#endif
+#endif
diff --git a/pfinet/linux-src/include/net/ip_fib.h b/pfinet/linux-src/include/net/ip_fib.h
new file mode 100644
index 00000000..76a2fbc3
--- /dev/null
+++ b/pfinet/linux-src/include/net/ip_fib.h
@@ -0,0 +1,257 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the Forwarding Information Base.
+ *
+ * Authors: A.N.Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _NET_IP_FIB_H
+#define _NET_IP_FIB_H
+
+#include <linux/config.h>
+
+struct kern_rta
+{
+ void *rta_dst;
+ void *rta_src;
+ int *rta_iif;
+ int *rta_oif;
+ void *rta_gw;
+ u32 *rta_priority;
+ void *rta_prefsrc;
+ struct rtattr *rta_mx;
+ struct rtattr *rta_mp;
+ unsigned char *rta_protoinfo;
+ unsigned char *rta_flow;
+ struct rta_cacheinfo *rta_ci;
+};
+
+struct fib_nh
+{
+ struct device *nh_dev;
+ unsigned nh_flags;
+ unsigned char nh_scope;
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ int nh_weight;
+ int nh_power;
+#endif
+#ifdef CONFIG_NET_CLS_ROUTE
+ __u32 nh_tclassid;
+#endif
+ int nh_oif;
+ u32 nh_gw;
+};
+
+/*
+ * This structure contains data shared by many of routes.
+ */
+
+struct fib_info
+{
+ struct fib_info *fib_next;
+ struct fib_info *fib_prev;
+ int fib_refcnt;
+ unsigned fib_flags;
+ int fib_protocol;
+ u32 fib_prefsrc;
+ u32 fib_priority;
+#define FIB_MAX_METRICS RTAX_RTT
+ unsigned fib_metrics[FIB_MAX_METRICS];
+#define fib_mtu fib_metrics[RTAX_MTU-1]
+#define fib_window fib_metrics[RTAX_WINDOW-1]
+#define fib_rtt fib_metrics[RTAX_RTT-1]
+ int fib_nhs;
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ int fib_power;
+#endif
+ struct fib_nh fib_nh[0];
+#define fib_dev fib_nh[0].nh_dev
+};
+
+
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+struct fib_rule;
+#endif
+
+struct fib_result
+{
+ u32 *prefix;
+ unsigned char prefixlen;
+ unsigned char nh_sel;
+ unsigned char type;
+ unsigned char scope;
+ struct fib_info *fi;
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+ struct fib_rule *r;
+#endif
+};
+
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+
+#define FIB_RES_NH(res) ((res).fi->fib_nh[(res).nh_sel])
+#define FIB_RES_RESET(res) ((res).nh_sel = 0)
+
+#else /* CONFIG_IP_ROUTE_MULTIPATH */
+
+#define FIB_RES_NH(res) ((res).fi->fib_nh[0])
+#define FIB_RES_RESET(res)
+
+#endif /* CONFIG_IP_ROUTE_MULTIPATH */
+
+#define FIB_RES_PREFSRC(res) ((res).fi->fib_prefsrc ? : __fib_res_prefsrc(&res))
+#define FIB_RES_GW(res) (FIB_RES_NH(res).nh_gw)
+#define FIB_RES_DEV(res) (FIB_RES_NH(res).nh_dev)
+#define FIB_RES_OIF(res) (FIB_RES_NH(res).nh_oif)
+
+struct fib_table
+{
+ unsigned char tb_id;
+ unsigned tb_stamp;
+ int (*tb_lookup)(struct fib_table *tb, const struct rt_key *key, struct fib_result *res);
+ int (*tb_insert)(struct fib_table *table, struct rtmsg *r,
+ struct kern_rta *rta, struct nlmsghdr *n,
+ struct netlink_skb_parms *req);
+ int (*tb_delete)(struct fib_table *table, struct rtmsg *r,
+ struct kern_rta *rta, struct nlmsghdr *n,
+ struct netlink_skb_parms *req);
+ int (*tb_dump)(struct fib_table *table, struct sk_buff *skb,
+ struct netlink_callback *cb);
+ int (*tb_flush)(struct fib_table *table);
+ int (*tb_get_info)(struct fib_table *table, char *buf,
+ int first, int count);
+ void (*tb_select_default)(struct fib_table *table,
+ const struct rt_key *key, struct fib_result *res);
+
+ unsigned char tb_data[0];
+};
+
+#ifndef CONFIG_IP_MULTIPLE_TABLES
+
+extern struct fib_table *local_table;
+extern struct fib_table *main_table;
+
+extern __inline__ struct fib_table *fib_get_table(int id)
+{
+ if (id != RT_TABLE_LOCAL)
+ return main_table;
+ return local_table;
+}
+
+extern __inline__ struct fib_table *fib_new_table(int id)
+{
+ return fib_get_table(id);
+}
+
+extern __inline__ int fib_lookup(const struct rt_key *key, struct fib_result *res)
+{
+ if (local_table->tb_lookup(local_table, key, res) &&
+ main_table->tb_lookup(main_table, key, res))
+ return -ENETUNREACH;
+ return 0;
+}
+
+extern __inline__ void fib_select_default(const struct rt_key *key, struct fib_result *res)
+{
+ if (FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK)
+ main_table->tb_select_default(main_table, key, res);
+}
+
+#else /* CONFIG_IP_MULTIPLE_TABLES */
+#define local_table (fib_tables[RT_TABLE_LOCAL])
+#define main_table (fib_tables[RT_TABLE_MAIN])
+
+extern struct fib_table * fib_tables[RT_TABLE_MAX+1];
+extern int fib_lookup(const struct rt_key *key, struct fib_result *res);
+extern struct fib_table *__fib_new_table(int id);
+
+extern __inline__ struct fib_table *fib_get_table(int id)
+{
+ if (id == 0)
+ id = RT_TABLE_MAIN;
+
+ return fib_tables[id];
+}
+
+extern __inline__ struct fib_table *fib_new_table(int id)
+{
+ if (id == 0)
+ id = RT_TABLE_MAIN;
+
+ return fib_tables[id] ? : __fib_new_table(id);
+}
+
+extern void fib_select_default(const struct rt_key *key, struct fib_result *res);
+
+#endif /* CONFIG_IP_MULTIPLE_TABLES */
+
+/* Exported by fib_frontend.c */
+extern void ip_fib_init(void);
+extern void fib_flush(void);
+extern int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
+extern int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
+extern int inet_rtm_getroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
+extern int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb);
+extern int fib_validate_source(u32 src, u32 dst, u8 tos, int oif,
+ struct device *dev, u32 *spec_dst, u32 *itag);
+extern void fib_select_multipath(const struct rt_key *key, struct fib_result *res);
+
+/* Exported by fib_semantics.c */
+extern int ip_fib_check_default(u32 gw, struct device *dev);
+extern void fib_release_info(struct fib_info *);
+extern int fib_semantic_match(int type, struct fib_info *,
+ const struct rt_key *, struct fib_result*);
+extern struct fib_info *fib_create_info(const struct rtmsg *r, struct kern_rta *rta,
+ const struct nlmsghdr *, int *err);
+extern int fib_nh_match(struct rtmsg *r, struct nlmsghdr *, struct kern_rta *rta, struct fib_info *fi);
+extern int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
+ u8 tb_id, u8 type, u8 scope, void *dst, int dst_len, u8 tos,
+ struct fib_info *fi);
+extern int fib_sync_down(u32 local, struct device *dev, int force);
+extern int fib_sync_up(struct device *dev);
+extern int fib_convert_rtentry(int cmd, struct nlmsghdr *nl, struct rtmsg *rtm,
+ struct kern_rta *rta, struct rtentry *r);
+extern void fib_node_get_info(int type, int dead, struct fib_info *fi, u32 prefix, u32 mask, char *buffer);
+extern u32 __fib_res_prefsrc(struct fib_result *res);
+
+/* Exported by fib_hash.c */
+extern struct fib_table *fib_hash_init(int id);
+
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+/* Exported by fib_rules.c */
+
+extern int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
+extern int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg);
+extern int inet_dump_rules(struct sk_buff *skb, struct netlink_callback *cb);
+extern u32 fib_rules_map_destination(u32 daddr, struct fib_result *res);
+#ifdef CONFIG_NET_CLS_ROUTE
+extern u32 fib_rules_tclass(struct fib_result *res);
+#endif
+extern u32 fib_rules_policy(u32 saddr, struct fib_result *res, unsigned *flags);
+extern void fib_rules_init(void);
+#endif
+
+extern __inline__ void fib_combine_itag(u32 *itag, struct fib_result *res)
+{
+#ifdef CONFIG_NET_CLS_ROUTE
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+ u32 rtag;
+#endif
+ *itag = FIB_RES_NH(*res).nh_tclassid<<16;
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+ rtag = fib_rules_tclass(res);
+ if (*itag == 0)
+ *itag = (rtag<<16);
+ *itag |= (rtag>>16);
+#endif
+#endif
+}
+
+#endif /* _NET_FIB_H */
diff --git a/pfinet/linux-src/include/net/ip_masq.h b/pfinet/linux-src/include/net/ip_masq.h
new file mode 100644
index 00000000..1e050359
--- /dev/null
+++ b/pfinet/linux-src/include/net/ip_masq.h
@@ -0,0 +1,362 @@
+/*
+ * IP masquerading functionality definitions
+ */
+
+#include <linux/config.h> /* for CONFIG_IP_MASQ_DEBUG */
+#ifndef _IP_MASQ_H
+#define _IP_MASQ_H
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/ip.h>
+#include <linux/skbuff.h>
+#include <linux/list.h>
+#endif /* __KERNEL__ */
+
+/*
+ * This define affects the number of ports that can be handled
+ * by each of the protocol helper modules.
+ */
+#define MAX_MASQ_APP_PORTS 12
+
+/*
+ * Linux ports don't normally get allocated above 32K.
+ * I used an extra 4K port-space
+ */
+
+#define PORT_MASQ_BEGIN 61000
+#define PORT_MASQ_END (PORT_MASQ_BEGIN+4096)
+
+#define MASQUERADE_EXPIRE_TCP 15*60*HZ
+#define MASQUERADE_EXPIRE_TCP_FIN 2*60*HZ
+#define MASQUERADE_EXPIRE_UDP 5*60*HZ
+/*
+ * ICMP can no longer be modified on the fly using an ioctl - this
+ * define is the only way to change the timeouts
+ */
+#define MASQUERADE_EXPIRE_ICMP 125*HZ
+
+#define IP_MASQ_MOD_CTL 0x00
+#define IP_MASQ_USER_CTL 0x01
+
+#ifdef __KERNEL__
+
+#define IP_MASQ_TAB_SIZE 256
+
+#define IP_MASQ_F_NO_DADDR 0x0001 /* no daddr yet */
+#define IP_MASQ_F_NO_DPORT 0x0002 /* no dport set yet */
+#define IP_MASQ_F_NO_SADDR 0x0004 /* no sport set yet */
+#define IP_MASQ_F_NO_SPORT 0x0008 /* no sport set yet */
+
+#define IP_MASQ_F_DLOOSE 0x0010 /* loose dest binding */
+#define IP_MASQ_F_NO_REPLY 0x0080 /* no reply yet from outside */
+
+#define IP_MASQ_F_HASHED 0x0100 /* hashed entry */
+#define IP_MASQ_F_OUT_SEQ 0x0200 /* must do output seq adjust */
+#define IP_MASQ_F_IN_SEQ 0x0400 /* must do input seq adjust */
+
+#define IP_MASQ_F_MPORT 0x1000 /* own mport specified */
+#define IP_MASQ_F_USER 0x2000 /* from uspace */
+#define IP_MASQ_F_SIMPLE_HASH 0x8000 /* prevent s+d and m+d hashing */
+
+/*
+ * Delta seq. info structure
+ * Each MASQ struct has 2 (output AND input seq. changes).
+ */
+
+struct ip_masq_seq {
+ __u32 init_seq; /* Add delta from this seq */
+ short delta; /* Delta in sequence numbers */
+ short previous_delta; /* Delta in sequence numbers before last resized pkt */
+};
+
+/*
+ * MASQ structure allocated for each masqueraded association
+ */
+struct ip_masq {
+ struct list_head m_list, s_list, d_list;
+ /* hashed d-linked list heads */
+ atomic_t refcnt; /* reference count */
+ struct timer_list timer; /* Expiration timer */
+ __u16 protocol; /* Which protocol are we talking? */
+ __u16 sport, dport, mport; /* src, dst & masq ports */
+ __u32 saddr, daddr, maddr; /* src, dst & masq addresses */
+ struct ip_masq_seq out_seq, in_seq;
+ struct ip_masq_app *app; /* bound ip_masq_app object */
+ void *app_data; /* Application private data */
+ struct ip_masq *control; /* Master control connection */
+ atomic_t n_control; /* Number of "controlled" masqs */
+ unsigned flags; /* status flags */
+ unsigned timeout; /* timeout */
+ unsigned state; /* state info */
+ struct ip_masq_timeout_table *timeout_table;
+};
+
+/*
+ * Timeout values
+ * ipchains holds a copy of this definition
+ */
+
+struct ip_fw_masq {
+ int tcp_timeout;
+ int tcp_fin_timeout;
+ int udp_timeout;
+};
+
+union ip_masq_tphdr {
+ unsigned char *raw;
+ struct udphdr *uh;
+ struct tcphdr *th;
+ struct icmphdr *icmph;
+ __u16 *portp;
+};
+/*
+ * [0]: UDP free_ports
+ * [1]: TCP free_ports
+ * [2]: ICMP free_ports
+ */
+
+extern atomic_t ip_masq_free_ports[3];
+
+/*
+ * ip_masq initializer (registers symbols and /proc/net entries)
+ */
+extern int ip_masq_init(void);
+
+/*
+ * functions called from ip layer
+ */
+extern int ip_fw_masquerade(struct sk_buff **, __u32 maddr);
+extern int ip_fw_masq_icmp(struct sk_buff **, __u32 maddr);
+extern int ip_fw_unmasq_icmp(struct sk_buff *);
+extern int ip_fw_demasquerade(struct sk_buff **);
+
+/*
+ * ip_masq obj creation/deletion functions.
+ */
+extern struct ip_masq *ip_masq_new(int proto, __u32 maddr, __u16 mport, __u32 saddr, __u16 sport, __u32 daddr, __u16 dport, unsigned flags);
+
+extern void ip_masq_control_add(struct ip_masq *ms, struct ip_masq* ctl_ms);
+extern void ip_masq_control_del(struct ip_masq *ms);
+extern struct ip_masq * ip_masq_control_get(struct ip_masq *ms);
+
+struct ip_masq_ctl;
+
+struct ip_masq_hook {
+ int (*ctl)(int, struct ip_masq_ctl *, int);
+ int (*info)(char *, char **, off_t, int, int);
+};
+
+extern struct list_head ip_masq_m_table[IP_MASQ_TAB_SIZE];
+extern struct list_head ip_masq_s_table[IP_MASQ_TAB_SIZE];
+extern struct list_head ip_masq_d_table[IP_MASQ_TAB_SIZE];
+extern const char * ip_masq_state_name(int state);
+extern struct ip_masq_hook *ip_masq_user_hook;
+extern u32 ip_masq_select_addr(struct device *dev, u32 dst, int scope);
+/*
+ *
+ * IP_MASQ_APP: IP application masquerading definitions
+ *
+ */
+
+struct ip_masq_app
+{
+ struct ip_masq_app *next;
+ char *name; /* name of application proxy */
+ unsigned type; /* type = proto<<16 | port (host byte order)*/
+ int n_attach;
+ int (*masq_init_1) /* ip_masq initializer */
+ (struct ip_masq_app *, struct ip_masq *);
+ int (*masq_done_1) /* ip_masq fin. */
+ (struct ip_masq_app *, struct ip_masq *);
+ int (*pkt_out) /* output (masquerading) hook */
+ (struct ip_masq_app *, struct ip_masq *, struct sk_buff **, __u32);
+ int (*pkt_in) /* input (demasq) hook */
+ (struct ip_masq_app *, struct ip_masq *, struct sk_buff **, __u32);
+};
+
+/*
+ * ip_masq_app initializer
+ */
+extern int ip_masq_app_init(void);
+
+/*
+ * ip_masq_app object registration functions (port: host byte order)
+ */
+extern int register_ip_masq_app(struct ip_masq_app *mapp, unsigned short proto, __u16 port);
+extern int unregister_ip_masq_app(struct ip_masq_app *mapp);
+
+/*
+ * get ip_masq_app obj by proto,port(net_byte_order)
+ */
+extern struct ip_masq_app * ip_masq_app_get(unsigned short proto, __u16 port);
+
+/*
+ * ip_masq TO ip_masq_app (un)binding functions.
+ */
+extern struct ip_masq_app * ip_masq_bind_app(struct ip_masq *ms);
+extern int ip_masq_unbind_app(struct ip_masq *ms);
+
+/*
+ * output and input app. masquerading hooks.
+ *
+ */
+extern int ip_masq_app_pkt_out(struct ip_masq *, struct sk_buff **skb_p, __u32 maddr);
+extern int ip_masq_app_pkt_in(struct ip_masq *, struct sk_buff **skb_p, __u32 maddr);
+
+/*
+ * service routine(s).
+ */
+
+extern struct ip_masq * ip_masq_out_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port);
+extern struct ip_masq * ip_masq_in_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port);
+
+extern int ip_masq_listen(struct ip_masq *);
+
+static __inline__ struct ip_masq * ip_masq_in_get_iph(const struct iphdr *iph)
+{
+ const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
+ return ip_masq_in_get(iph->protocol,
+ iph->saddr, portp[0],
+ iph->daddr, portp[1]);
+}
+
+static __inline__ struct ip_masq * ip_masq_out_get_iph(const struct iphdr *iph)
+{
+ const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
+ return ip_masq_out_get(iph->protocol,
+ iph->saddr, portp[0],
+ iph->daddr, portp[1]);
+}
+
+extern void ip_masq_put(struct ip_masq *ms);
+
+
+extern rwlock_t __ip_masq_lock;
+
+#ifdef __SMP__
+#define read_lock_bh(lock) do { start_bh_atomic(); read_lock(lock); \
+ } while (0)
+#define read_unlock_bh(lock) do { read_unlock(lock); end_bh_atomic(); \
+ } while (0)
+#define write_lock_bh(lock) do { start_bh_atomic(); write_lock(lock); \
+ } while (0)
+#define write_unlock_bh(lock) do { write_unlock(lock); end_bh_atomic(); \
+ } while (0)
+#else
+#define read_lock_bh(lock) start_bh_atomic()
+#define read_unlock_bh(lock) end_bh_atomic()
+#define write_lock_bh(lock) start_bh_atomic()
+#define write_unlock_bh(lock) end_bh_atomic()
+#endif
+/*
+ *
+ */
+
+/*
+ * Debugging stuff
+ */
+
+extern int ip_masq_get_debug_level(void);
+
+#ifdef CONFIG_IP_MASQ_DEBUG
+#define IP_MASQ_DEBUG(level, msg...) do { \
+ if (level <= ip_masq_get_debug_level()) \
+ printk(KERN_DEBUG "IP_MASQ:" ## msg); \
+ } while (0)
+#else /* NO DEBUGGING at ALL */
+#define IP_MASQ_DEBUG(level, msg...) do { } while (0)
+#endif
+
+#define IP_MASQ_INFO(msg...) \
+ printk(KERN_INFO "IP_MASQ:" ## msg)
+
+#define IP_MASQ_ERR(msg...) \
+ printk(KERN_ERR "IP_MASQ:" ## msg)
+
+#define IP_MASQ_WARNING(msg...) \
+ printk(KERN_WARNING "IP_MASQ:" ## msg)
+
+
+/*
+ * /proc/net entry
+ */
+extern int ip_masq_proc_register(struct proc_dir_entry *);
+extern void ip_masq_proc_unregister(struct proc_dir_entry *);
+extern int ip_masq_app_getinfo(char *buffer, char **start, off_t offset, int length, int dummy);
+
+/*
+ * skb_replace function used by "client" modules to replace
+ * a segment of skb.
+ */
+extern struct sk_buff * ip_masq_skb_replace(struct sk_buff *skb, int pri, char *o_buf, int o_len, char *n_buf, int n_len);
+
+/*
+ * masq_proto_num returns 0 for UDP, 1 for TCP, 2 for ICMP
+ */
+
+static __inline__ int masq_proto_num(unsigned proto)
+{
+ switch (proto)
+ {
+ case IPPROTO_UDP: return (0); break;
+ case IPPROTO_TCP: return (1); break;
+ case IPPROTO_ICMP: return (2); break;
+ default: return (-1); break;
+ }
+}
+
+static __inline__ const char *masq_proto_name(unsigned proto)
+{
+ static char buf[20];
+ static const char *strProt[] = {"UDP","TCP","ICMP"};
+ int msproto = masq_proto_num(proto);
+
+ if (msproto<0||msproto>2) {
+ sprintf(buf, "IP_%d", proto);
+ return buf;
+ }
+ return strProt[msproto];
+}
+
+enum {
+ IP_MASQ_S_NONE = 0,
+ IP_MASQ_S_ESTABLISHED,
+ IP_MASQ_S_SYN_SENT,
+ IP_MASQ_S_SYN_RECV,
+ IP_MASQ_S_FIN_WAIT,
+ IP_MASQ_S_TIME_WAIT,
+ IP_MASQ_S_CLOSE,
+ IP_MASQ_S_CLOSE_WAIT,
+ IP_MASQ_S_LAST_ACK,
+ IP_MASQ_S_LISTEN,
+ IP_MASQ_S_UDP,
+ IP_MASQ_S_ICMP,
+ IP_MASQ_S_LAST
+};
+
+struct ip_masq_timeout_table {
+ atomic_t refcnt;
+ int scale;
+ int timeout[IP_MASQ_S_LAST+1];
+};
+
+static __inline__ void ip_masq_timeout_attach(struct ip_masq *ms, struct ip_masq_timeout_table *mstim)
+{
+ atomic_inc (&mstim->refcnt);
+ ms->timeout_table=mstim;
+}
+
+static __inline__ void ip_masq_timeout_detach(struct ip_masq *ms)
+{
+ struct ip_masq_timeout_table *mstim = ms->timeout_table;
+
+ if (!mstim)
+ return;
+ atomic_dec(&mstim->refcnt);
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* _IP_MASQ_H */
diff --git a/pfinet/linux-src/include/net/ip_masq_mod.h b/pfinet/linux-src/include/net/ip_masq_mod.h
new file mode 100644
index 00000000..a7a67d6f
--- /dev/null
+++ b/pfinet/linux-src/include/net/ip_masq_mod.h
@@ -0,0 +1,86 @@
+/*
+ * IP Masquerading Modules Support
+ *
+ * Version: @(#)ip_masq_mod.h 0.01 97/10/30
+ *
+ * Author: Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
+ *
+ */
+
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/ip_fw.h>
+#include <linux/proc_fs.h>
+#include <net/ip_masq.h>
+
+#define IP_MASQ_MOD_NOP 0
+#define IP_MASQ_MOD_ACCEPT 1
+#define IP_MASQ_MOD_REJECT -1
+
+struct ip_masq_mod {
+ struct ip_masq_mod *next; /* next mod for addrs. lookups */
+ struct ip_masq_mod *next_reg; /* next mod for configuration ctls */
+ char *mmod_name;
+ atomic_t refcnt;
+ atomic_t mmod_nent; /* number of entries */
+ struct proc_dir_entry *mmod_proc_ent;
+ int (*mmod_ctl) (int optname, struct ip_masq_ctl *, int optlen);
+ int (*mmod_init) (void);
+ int (*mmod_done) (void);
+ int (*mmod_in_rule) (const struct sk_buff *, const struct iphdr *);
+ int (*mmod_in_update) (const struct sk_buff *, const struct iphdr *,
+ struct ip_masq *);
+ struct ip_masq * (*mmod_in_create) (const struct sk_buff *, const struct iphdr *, __u32);
+ int (*mmod_out_rule) (const struct sk_buff *, const struct iphdr *);
+ int (*mmod_out_update) (const struct sk_buff *, const struct iphdr *,
+ struct ip_masq *);
+ struct ip_masq * (*mmod_out_create) (const struct sk_buff *, const struct iphdr *, __u32);
+};
+
+/*
+ * Service routines (called from ip_masq.c)
+ */
+
+int ip_masq_mod_out_rule(const struct sk_buff *, const struct iphdr *);
+int ip_masq_mod_out_update(const struct sk_buff *, const struct iphdr *, struct ip_masq *ms);
+struct ip_masq * ip_masq_mod_out_create(const struct sk_buff *, const struct iphdr *iph, __u32 maddr);
+
+int ip_masq_mod_in_rule(const struct sk_buff *, const struct iphdr *iph);
+int ip_masq_mod_in_update(const struct sk_buff *, const struct iphdr *iph, struct ip_masq *ms);
+struct ip_masq * ip_masq_mod_in_create(const struct sk_buff *, const struct iphdr *iph, __u32 maddr);
+
+extern int ip_masq_mod_ctl(int optname, struct ip_masq_ctl *, int len);
+
+/*
+ * ip_masq_mod registration functions
+ */
+extern int register_ip_masq_mod(struct ip_masq_mod *mmod);
+extern int unregister_ip_masq_mod(struct ip_masq_mod *mmod);
+extern int ip_masq_mod_lkp_unlink(struct ip_masq_mod *mmod);
+extern int ip_masq_mod_lkp_link(struct ip_masq_mod *mmod);
+
+/*
+ * init functions protos
+ */
+extern int ip_portfw_init(void);
+extern int ip_mfw_init(void);
+extern int ip_autofw_init(void);
+
+/*
+ * Utility ...
+ */
+static __inline__ void ip_masq_mod_dec_nent(struct ip_masq_mod *mmod)
+{
+ if (atomic_dec_and_test(&mmod->mmod_nent)) {
+ ip_masq_mod_lkp_unlink(mmod);
+ }
+}
+static __inline__ void ip_masq_mod_inc_nent(struct ip_masq_mod *mmod)
+{
+ atomic_inc(&mmod->mmod_nent);
+ if (atomic_read(&mmod->mmod_nent)==1)
+ ip_masq_mod_lkp_link(mmod);
+}
+
+#endif /* __KERNEL__ */
diff --git a/pfinet/linux-src/include/net/ipconfig.h b/pfinet/linux-src/include/net/ipconfig.h
new file mode 100644
index 00000000..f9356946
--- /dev/null
+++ b/pfinet/linux-src/include/net/ipconfig.h
@@ -0,0 +1,21 @@
+/*
+ * $Id: ipconfig.h,v 1.3 1999/01/04 20:13:29 davem Exp $
+ *
+ * Copyright (C) 1997 Martin Mares
+ *
+ * Automatic IP Layer Configuration
+ */
+
+extern __u32 root_server_addr;
+extern u8 root_server_path[];
+extern u32 ic_myaddr;
+extern u32 ic_servaddr;
+extern u32 ic_gateway;
+extern u32 ic_netmask;
+extern int ic_enable;
+extern int ic_host_name_set;
+extern int ic_set_manually;
+extern int ic_proto_enabled;
+
+#define IC_BOOTP 1
+#define IC_RARP 2
diff --git a/pfinet/linux-src/include/net/ipip.h b/pfinet/linux-src/include/net/ipip.h
new file mode 100644
index 00000000..22c464c3
--- /dev/null
+++ b/pfinet/linux-src/include/net/ipip.h
@@ -0,0 +1,33 @@
+#ifndef __NET_IPIP_H
+#define __NET_IPIP_H 1
+
+#include <linux/if_tunnel.h>
+
+/* Keep error state on tunnel for 30 sec */
+#define IPTUNNEL_ERR_TIMEO (30*HZ)
+
+struct ip_tunnel
+{
+ struct ip_tunnel *next;
+ struct device *dev;
+ struct net_device_stats stat;
+
+ int recursion; /* Depth of hard_start_xmit recursion */
+ int err_count; /* Number of arrived ICMP errors */
+ unsigned long err_time; /* Time when the last ICMP error arrived */
+
+ /* These four fields used only by GRE */
+ __u32 i_seqno; /* The last seen seqno */
+ __u32 o_seqno; /* The last output seqno */
+ int hlen; /* Precalculated GRE header length */
+ int mlink;
+
+ struct ip_tunnel_parm parms;
+};
+
+extern int ipip_init(void);
+extern int ipgre_init(void);
+extern int sit_init(void);
+extern void sit_cleanup(void);
+
+#endif
diff --git a/pfinet/linux-src/include/net/ipv6.h b/pfinet/linux-src/include/net/ipv6.h
new file mode 100644
index 00000000..60ea0536
--- /dev/null
+++ b/pfinet/linux-src/include/net/ipv6.h
@@ -0,0 +1,325 @@
+/*
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: ipv6.h,v 1.16 1999/04/22 10:07:27 davem Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _NET_IPV6_H
+#define _NET_IPV6_H
+
+#include <linux/ipv6.h>
+#include <asm/hardirq.h>
+#include <net/ndisc.h>
+#include <net/flow.h>
+
+/*
+ * NextHeader field of IPv6 header
+ */
+
+#define NEXTHDR_HOP 0 /* Hop-by-hop option header. */
+#define NEXTHDR_TCP 6 /* TCP segment. */
+#define NEXTHDR_UDP 17 /* UDP message. */
+#define NEXTHDR_IPV6 41 /* IPv6 in IPv6 */
+#define NEXTHDR_ROUTING 43 /* Routing header. */
+#define NEXTHDR_FRAGMENT 44 /* Fragmentation/reassembly header. */
+#define NEXTHDR_ESP 50 /* Encapsulating security payload. */
+#define NEXTHDR_AUTH 51 /* Authentication header. */
+#define NEXTHDR_ICMP 58 /* ICMP for IPv6. */
+#define NEXTHDR_NONE 59 /* No next header */
+#define NEXTHDR_DEST 60 /* Destination options header. */
+
+#define NEXTHDR_MAX 255
+
+
+
+#define IPV6_DEFAULT_HOPLIMIT 64
+#define IPV6_DEFAULT_MCASTHOPS 1
+
+/*
+ * Addr type
+ *
+ * type - unicast | multicast | anycast
+ * scope - local | site | global
+ * v4 - compat
+ * v4mapped
+ * any
+ * loopback
+ */
+
+#define IPV6_ADDR_ANY 0x0000U
+
+#define IPV6_ADDR_UNICAST 0x0001U
+#define IPV6_ADDR_MULTICAST 0x0002U
+#define IPV6_ADDR_ANYCAST 0x0004U
+
+#define IPV6_ADDR_LOOPBACK 0x0010U
+#define IPV6_ADDR_LINKLOCAL 0x0020U
+#define IPV6_ADDR_SITELOCAL 0x0040U
+
+#define IPV6_ADDR_COMPATv4 0x0080U
+
+#define IPV6_ADDR_SCOPE_MASK 0x00f0U
+
+#define IPV6_ADDR_MAPPED 0x1000U
+#define IPV6_ADDR_RESERVED 0x2000U /* reserved address space */
+
+/*
+ * fragmentation header
+ */
+
+struct frag_hdr {
+ unsigned char nexthdr;
+ unsigned char reserved;
+ unsigned short frag_off;
+ __u32 identification;
+};
+
+#ifdef __KERNEL__
+
+#include <net/sock.h>
+
+/* sysctls */
+extern int sysctl_ipv6_bindv6only;
+
+extern struct ipv6_mib ipv6_statistics;
+extern struct icmpv6_mib icmpv6_statistics;
+extern struct udp_mib udp_stats_in6;
+
+struct ip6_ra_chain
+{
+ struct ip6_ra_chain *next;
+ struct sock *sk;
+ int sel;
+ void (*destructor)(struct sock *);
+};
+
+extern struct ip6_ra_chain *ip6_ra_chain;
+
+/*
+ This structure is prepared by protocol, when parsing
+ ancillary data and passed to IPv6.
+ */
+
+struct ipv6_txoptions
+{
+ /* Length of this structure */
+ int tot_len;
+
+ /* length of extension headers */
+
+ __u16 opt_flen; /* after fragment hdr */
+ __u16 opt_nflen; /* before fragment hdr */
+
+ struct ipv6_opt_hdr *hopopt;
+ struct ipv6_opt_hdr *dst0opt;
+ struct ipv6_rt_hdr *srcrt; /* Routing Header */
+ struct ipv6_opt_hdr *auth;
+ struct ipv6_opt_hdr *dst1opt;
+
+ /* Option buffer, as read by IPV6_PKTOPTIONS, starts here. */
+};
+
+struct ip6_flowlabel
+{
+ struct ip6_flowlabel *next;
+ u32 label;
+ struct in6_addr dst;
+ struct ipv6_txoptions *opt;
+ atomic_t users;
+ u32 linger;
+ u8 share;
+ u32 owner;
+ unsigned long lastuse;
+ unsigned long expires;
+};
+
+#define IPV6_FLOWINFO_MASK __constant_htonl(0x0FFFFFFF)
+#define IPV6_FLOWLABEL_MASK __constant_htonl(0x000FFFFF)
+
+struct ipv6_fl_socklist
+{
+ struct ipv6_fl_socklist *next;
+ struct ip6_flowlabel *fl;
+};
+
+extern struct ip6_flowlabel *fl6_sock_lookup(struct sock *sk, u32 label);
+extern struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space,
+ struct ip6_flowlabel * fl,
+ struct ipv6_txoptions * fopt);
+extern void fl6_free_socklist(struct sock *sk);
+extern int ipv6_flowlabel_opt(struct sock *sk, char *optval, int optlen);
+extern void ip6_flowlabel_init(void);
+extern void ip6_flowlabel_cleanup(void);
+
+extern __inline__ void fl6_sock_release(struct ip6_flowlabel *fl)
+{
+ if (fl)
+ atomic_dec(&fl->users);
+}
+
+extern int ip6_ra_control(struct sock *sk, int sel,
+ void (*destructor)(struct sock *));
+
+
+extern int ip6_call_ra_chain(struct sk_buff *skb, int sel);
+
+extern u8 * ipv6_reassembly(struct sk_buff **skb, u8 *nhptr);
+
+extern u8 * ipv6_parse_hopopts(struct sk_buff *skb, u8 *nhptr);
+
+extern u8 * ipv6_parse_exthdrs(struct sk_buff **skb, u8 *nhptr);
+
+extern struct ipv6_txoptions * ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt);
+
+#define IPV6_FRAG_TIMEOUT (60*HZ) /* 60 seconds */
+
+/*
+ * Function prototype for build_xmit
+ */
+
+typedef int (*inet_getfrag_t) (const void *data,
+ struct in6_addr *addr,
+ char *,
+ unsigned int, unsigned int);
+
+
+extern int ipv6_addr_type(struct in6_addr *addr);
+
+extern __inline__ int ipv6_addr_scope(struct in6_addr *addr)
+{
+ return ipv6_addr_type(addr) & IPV6_ADDR_SCOPE_MASK;
+}
+
+extern __inline__ int ipv6_addr_cmp(struct in6_addr *a1, struct in6_addr *a2)
+{
+ return memcmp((void *) a1, (void *) a2, sizeof(struct in6_addr));
+}
+
+extern __inline__ void ipv6_addr_copy(struct in6_addr *a1, struct in6_addr *a2)
+{
+ memcpy((void *) a1, (void *) a2, sizeof(struct in6_addr));
+}
+
+#ifndef __HAVE_ARCH_ADDR_SET
+extern __inline__ void ipv6_addr_set(struct in6_addr *addr,
+ __u32 w1, __u32 w2,
+ __u32 w3, __u32 w4)
+{
+ addr->s6_addr32[0] = w1;
+ addr->s6_addr32[1] = w2;
+ addr->s6_addr32[2] = w3;
+ addr->s6_addr32[3] = w4;
+}
+#endif
+
+extern __inline__ int ipv6_addr_any(struct in6_addr *a)
+{
+ return ((a->s6_addr32[0] | a->s6_addr32[1] |
+ a->s6_addr32[2] | a->s6_addr32[3] ) == 0);
+}
+
+/*
+ * Prototypes exported by ipv6
+ */
+
+/*
+ * rcv function (called from netdevice level)
+ */
+
+extern int ipv6_rcv(struct sk_buff *skb,
+ struct device *dev,
+ struct packet_type *pt);
+
+/*
+ * upper-layer output functions
+ */
+extern int ip6_xmit(struct sock *sk,
+ struct sk_buff *skb,
+ struct flowi *fl,
+ struct ipv6_txoptions *opt);
+
+extern int ip6_nd_hdr(struct sock *sk,
+ struct sk_buff *skb,
+ struct device *dev,
+ struct in6_addr *saddr,
+ struct in6_addr *daddr,
+ int proto, int len);
+
+extern int ip6_build_xmit(struct sock *sk,
+ inet_getfrag_t getfrag,
+ const void *data,
+ struct flowi *fl,
+ unsigned length,
+ struct ipv6_txoptions *opt,
+ int hlimit, int flags);
+
+/*
+ * skb processing functions
+ */
+
+extern int ip6_output(struct sk_buff *skb);
+extern int ip6_forward(struct sk_buff *skb);
+extern int ip6_input(struct sk_buff *skb);
+extern int ip6_mc_input(struct sk_buff *skb);
+
+/*
+ * Extension header (options) processing
+ */
+
+extern u8 * ipv6_build_nfrag_opts(struct sk_buff *skb,
+ u8 *prev_hdr,
+ struct ipv6_txoptions *opt,
+ struct in6_addr *daddr,
+ u32 jumbolen);
+extern u8 * ipv6_build_frag_opts(struct sk_buff *skb,
+ u8 *prev_hdr,
+ struct ipv6_txoptions *opt);
+extern void ipv6_push_nfrag_opts(struct sk_buff *skb,
+ struct ipv6_txoptions *opt,
+ u8 *proto,
+ struct in6_addr **daddr_p);
+extern void ipv6_push_frag_opts(struct sk_buff *skb,
+ struct ipv6_txoptions *opt,
+ u8 *proto);
+
+extern u8 * ipv6_skip_exthdr(struct ipv6_opt_hdr *hdr,
+ u8 *nexthdrp, int len);
+
+extern struct ipv6_txoptions * ipv6_invert_rthdr(struct sock *sk,
+ struct ipv6_rt_hdr *hdr);
+
+
+/*
+ * socket options (ipv6_sockglue.c)
+ */
+
+extern int ipv6_setsockopt(struct sock *sk, int level,
+ int optname, char *optval,
+ int optlen);
+extern int ipv6_getsockopt(struct sock *sk, int level,
+ int optname, char *optval,
+ int *optlen);
+
+extern void ipv6_packet_init(void);
+
+extern void ipv6_netdev_notif_init(void);
+
+extern void ipv6_packet_cleanup(void);
+
+extern void ipv6_netdev_notif_cleanup(void);
+
+extern int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len);
+extern void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, u16 port,
+ u32 info, u8 *payload);
+extern void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info);
+
+#endif /* __KERNEL__ */
+#endif /* _NET_IPV6_H */
diff --git a/pfinet/linux-src/include/net/ipx.h b/pfinet/linux-src/include/net/ipx.h
new file mode 100644
index 00000000..2152e388
--- /dev/null
+++ b/pfinet/linux-src/include/net/ipx.h
@@ -0,0 +1,82 @@
+/*
+ * The following information is in its entirety obtained from:
+ *
+ * Novell 'IPX Router Specification' Version 1.10
+ * Part No. 107-000029-001
+ *
+ * Which is available from ftp.novell.com
+ */
+
+#ifndef _NET_INET_IPX_H_
+#define _NET_INET_IPX_H_
+
+#include <linux/netdevice.h>
+#include <net/datalink.h>
+#include <linux/ipx.h>
+
+typedef struct
+{
+ __u32 net;
+ __u8 node[IPX_NODE_LEN];
+ __u16 sock;
+} ipx_address;
+
+#define ipx_broadcast_node "\377\377\377\377\377\377"
+#define ipx_this_node "\0\0\0\0\0\0"
+
+struct ipxhdr
+{
+ __u16 ipx_checksum __attribute__ ((packed));
+#define IPX_NO_CHECKSUM 0xFFFF
+ __u16 ipx_pktsize __attribute__ ((packed));
+ __u8 ipx_tctrl;
+ __u8 ipx_type;
+#define IPX_TYPE_UNKNOWN 0x00
+#define IPX_TYPE_RIP 0x01 /* may also be 0 */
+#define IPX_TYPE_SAP 0x04 /* may also be 0 */
+#define IPX_TYPE_SPX 0x05 /* SPX protocol */
+#define IPX_TYPE_NCP 0x11 /* $lots for docs on this (SPIT) */
+#define IPX_TYPE_PPROP 0x14 /* complicated flood fill brdcast [Not supported] */
+ ipx_address ipx_dest __attribute__ ((packed));
+ ipx_address ipx_source __attribute__ ((packed));
+};
+
+#include <net/ipxcall.h>
+
+typedef struct ipx_interface {
+ /* IPX address */
+ __u32 if_netnum;
+ unsigned char if_node[IPX_NODE_LEN];
+
+ /* physical device info */
+ struct device *if_dev;
+ struct datalink_proto *if_dlink;
+ unsigned short if_dlink_type;
+
+ /* socket support */
+ unsigned short if_sknum;
+ struct sock *if_sklist;
+
+ /* administrative overhead */
+ int if_ipx_offset;
+ unsigned char if_internal;
+ unsigned char if_primary;
+
+ struct ipx_interface *if_next;
+} ipx_interface;
+
+typedef struct ipx_route {
+ __u32 ir_net;
+ ipx_interface *ir_intrfc;
+ unsigned char ir_routed;
+ unsigned char ir_router_node[IPX_NODE_LEN];
+ struct ipx_route *ir_next;
+} ipx_route;
+
+#define IPX_MIN_EPHEMERAL_SOCKET 0x4000
+#define IPX_MAX_EPHEMERAL_SOCKET 0x7fff
+
+extern int ipx_register_spx(struct proto_ops **, struct net_proto_family *);
+extern int ipx_unregister_spx(void);
+
+#endif /* def _NET_INET_IPX_H_ */
diff --git a/pfinet/linux-src/include/net/ipxcall.h b/pfinet/linux-src/include/net/ipxcall.h
new file mode 100644
index 00000000..eb5bd2bd
--- /dev/null
+++ b/pfinet/linux-src/include/net/ipxcall.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void ipx_proto_init(struct net_proto *pro);
diff --git a/pfinet/linux-src/include/net/lapb.h b/pfinet/linux-src/include/net/lapb.h
new file mode 100644
index 00000000..7cc9b345
--- /dev/null
+++ b/pfinet/linux-src/include/net/lapb.h
@@ -0,0 +1,150 @@
+#ifndef _LAPB_H
+#define _LAPB_H
+#include <linux/lapb.h>
+
+#define LAPB_HEADER_LEN 20 /* LAPB over Ethernet + a bit more */
+
+#define LAPB_ACK_PENDING_CONDITION 0x01
+#define LAPB_REJECT_CONDITION 0x02
+#define LAPB_PEER_RX_BUSY_CONDITION 0x04
+
+/* Control field templates */
+#define LAPB_I 0x00 /* Information frames */
+#define LAPB_S 0x01 /* Supervisory frames */
+#define LAPB_U 0x03 /* Unnumbered frames */
+
+#define LAPB_RR 0x01 /* Receiver ready */
+#define LAPB_RNR 0x05 /* Receiver not ready */
+#define LAPB_REJ 0x09 /* Reject */
+
+#define LAPB_SABM 0x2F /* Set Asynchronous Balanced Mode */
+#define LAPB_SABME 0x6F /* Set Asynchronous Balanced Mode Extended */
+#define LAPB_DISC 0x43 /* Disconnect */
+#define LAPB_DM 0x0F /* Disconnected mode */
+#define LAPB_UA 0x63 /* Unnumbered acknowledge */
+#define LAPB_FRMR 0x87 /* Frame reject */
+
+#define LAPB_ILLEGAL 0x100 /* Impossible to be a real frame type */
+
+#define LAPB_SPF 0x10 /* Poll/final bit for standard LAPB */
+#define LAPB_EPF 0x01 /* Poll/final bit for extended LAPB */
+
+#define LAPB_FRMR_W 0x01 /* Control field invalid */
+#define LAPB_FRMR_X 0x02 /* I field invalid */
+#define LAPB_FRMR_Y 0x04 /* I field too long */
+#define LAPB_FRMR_Z 0x08 /* Invalid N(R) */
+
+#define LAPB_POLLOFF 0
+#define LAPB_POLLON 1
+
+/* LAPB C-bit */
+#define LAPB_COMMAND 1
+#define LAPB_RESPONSE 2
+
+#define LAPB_ADDR_A 0x03
+#define LAPB_ADDR_B 0x01
+#define LAPB_ADDR_C 0x0F
+#define LAPB_ADDR_D 0x07
+
+/* Define Link State constants. */
+enum {
+ LAPB_STATE_0, /* Disconnected State */
+ LAPB_STATE_1, /* Awaiting Connection State */
+ LAPB_STATE_2, /* Awaiting Disconnection State */
+ LAPB_STATE_3, /* Data Transfer State */
+ LAPB_STATE_4 /* Frame Reject State */
+};
+
+#define LAPB_DEFAULT_MODE (LAPB_STANDARD | LAPB_SLP | LAPB_DTE)
+#define LAPB_DEFAULT_WINDOW 7 /* Window=7 */
+#define LAPB_DEFAULT_T1 (5 * HZ) /* T1=5s */
+#define LAPB_DEFAULT_T2 (1 * HZ) /* T2=1s */
+#define LAPB_DEFAULT_N2 20 /* N2=20 */
+
+#define LAPB_SMODULUS 8
+#define LAPB_EMODULUS 128
+
+/*
+ * Information about the current frame.
+ */
+struct lapb_frame {
+ unsigned short type; /* Parsed type */
+ unsigned short nr, ns; /* N(R), N(S) */
+ unsigned char cr; /* Command/Response */
+ unsigned char pf; /* Poll/Final */
+ unsigned char control[2]; /* Original control data*/
+};
+
+/*
+ * The per LAPB connection control structure.
+ */
+typedef struct lapb_cb {
+ struct lapb_cb *next;
+ void *token;
+
+ /* Link status fields */
+ unsigned int mode;
+ unsigned char state;
+ unsigned short vs, vr, va;
+ unsigned char condition;
+ unsigned short n2, n2count;
+ unsigned short t1, t2;
+ struct timer_list t1timer, t2timer;
+
+ /* Internal control information */
+ struct sk_buff_head write_queue;
+ struct sk_buff_head ack_queue;
+ unsigned char window;
+ struct lapb_register_struct callbacks;
+
+ /* FRMR control information */
+ struct lapb_frame frmr_data;
+ unsigned char frmr_type;
+} lapb_cb;
+
+/* lapb_iface.c */
+extern void lapb_connect_confirmation(lapb_cb *, int);
+extern void lapb_connect_indication(lapb_cb *, int);
+extern void lapb_disconnect_confirmation(lapb_cb *, int);
+extern void lapb_disconnect_indication(lapb_cb *, int);
+extern int lapb_data_indication(lapb_cb *, struct sk_buff *);
+extern int lapb_data_transmit(lapb_cb *, struct sk_buff *);
+
+/* lapb_in.c */
+extern void lapb_data_input(lapb_cb *, struct sk_buff *);
+
+/* lapb_out.c */
+extern void lapb_kick(lapb_cb *);
+extern void lapb_transmit_buffer(lapb_cb *, struct sk_buff *, int);
+extern void lapb_establish_data_link(lapb_cb *);
+extern void lapb_enquiry_response(lapb_cb *);
+extern void lapb_timeout_response(lapb_cb *);
+extern void lapb_check_iframes_acked(lapb_cb *, unsigned short);
+extern void lapb_check_need_response(lapb_cb *, int, int);
+
+/* lapb_subr.c */
+extern void lapb_clear_queues(lapb_cb *);
+extern void lapb_frames_acked(lapb_cb *, unsigned short);
+extern void lapb_requeue_frames(lapb_cb *);
+extern int lapb_validate_nr(lapb_cb *, unsigned short);
+extern void lapb_decode(lapb_cb *, struct sk_buff *, struct lapb_frame *);
+extern void lapb_send_control(lapb_cb *, int, int, int);
+extern void lapb_transmit_frmr(lapb_cb *);
+
+/* lapb_timer.c */
+extern void lapb_start_t1timer(lapb_cb *);
+extern void lapb_start_t2timer(lapb_cb *);
+extern void lapb_stop_t1timer(lapb_cb *);
+extern void lapb_stop_t2timer(lapb_cb *);
+extern int lapb_t1timer_running(lapb_cb *);
+
+/*
+ * Debug levels.
+ * 0 = Off
+ * 1 = State Changes
+ * 2 = Packets I/O and State Changes
+ * 3 = Hex dumps, Packets I/O and State Changes.
+ */
+#define LAPB_DEBUG 0
+
+#endif
diff --git a/pfinet/linux-src/include/net/lapbcall.h b/pfinet/linux-src/include/net/lapbcall.h
new file mode 100644
index 00000000..825e7f2c
--- /dev/null
+++ b/pfinet/linux-src/include/net/lapbcall.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void lapb_proto_init(struct net_proto *pro);
diff --git a/pfinet/linux-src/include/net/llc.h b/pfinet/linux-src/include/net/llc.h
new file mode 100644
index 00000000..e88e8dc2
--- /dev/null
+++ b/pfinet/linux-src/include/net/llc.h
@@ -0,0 +1,134 @@
+#include <linux/skbuff.h>
+
+#define LLC_MODULE
+
+typedef struct llc_struct llc;
+typedef struct llc_struct *llcptr;
+
+/*
+ * LLC private data area structure.
+ */
+
+struct llc_struct
+{
+ char eye[4]; /* To recognize llc area in dump */
+ int retry_count; /* LLC link state variables */
+ unsigned char name[9]; /* name of this llc instance */
+ unsigned char s_flag;
+ unsigned char p_flag;
+ unsigned char f_flag;
+ unsigned char data_flag;
+ unsigned char cause_flag;
+ unsigned char vs; /* Send state variable */
+ unsigned char vr; /* Receive state variable */
+ unsigned char remote_busy;
+ unsigned char state; /* Current state of type2 llc procedure */
+ int n1; /* Maximum number of bytes in I pdu 7.8.2 */
+ int n2; /* Naximum number of retransmissions 7.8.2 */
+ unsigned char k; /* Transmit window size 7.8.4, tw in IBM doc*/
+ unsigned char rw; /* Receive window size */
+ struct
+ {
+ /*
+ * FRMR_RSP info field structure: 5.4.2.3.5 p55
+ */
+
+ unsigned char cntl1;
+ unsigned char cntl2;
+ unsigned char vs;
+ unsigned char vr_cr;
+ unsigned char xxyz;
+ } frmr_info_fld;
+
+ /*
+ * Timers in 7.8.1 page 78
+ */
+
+#define P_TIMER 0
+#define REJ_TIMER 1
+#define ACK_TIMER 2
+#define BUSY_TIMER 3
+ unsigned long timer_expire_time[4];
+ unsigned char timer_state[4]; /* The state of each timer */
+#define TIMER_IDLE 0
+#define TIMER_RUNNING 1
+#define TIMER_EXPIRED 2
+ unsigned long timer_interval[4];
+ struct timer_list tl[4];
+
+ /*
+ * Client entry point, called by the LLC.
+ */
+
+ void (*llc_event)(struct llc_struct *);
+
+ /*
+ * Mux and Demux variables
+ */
+
+ char * client_data; /* Pointer to clients context */
+ unsigned char local_sap;
+ unsigned char remote_sap ;
+ char remote_mac[MAX_ADDR_LEN]; /* MAC address of remote session partner */
+ struct device *dev; /* Device we are attached to */
+
+ unsigned char llc_mode; /* See doc 7.1 on p70 */
+#define MODE_ADM 1
+#define MODE_ABM 2
+
+ int llc_callbacks; /* Pending callbacks */
+#define LLC_CONN_INDICATION 1 /* We have to ensure the names don't */
+#define LLC_CONN_CONFIRM 2 /* mix up with the 802 state table */
+#define LLC_DATA_INDIC 4
+#define LLC_DISC_INDICATION 8
+#define LLC_RESET_INDIC_LOC 16
+#define LLC_RESET_INDIC_REM 32
+#define LLC_RST_CONFIRM 64
+#define LLC_FRMR_RECV 128
+#define LLC_FRMR_SENT 256
+#define LLC_REMOTE_BUSY 512
+#define LLC_REMOTE_NOTBUSY 1024
+#define LLC_TEST_INDICATION 2048
+#define LLC_XID_INDICATION 4096
+#define LLC_UI_DATA 8192
+
+ struct sk_buff *inc_skb; /* Saved data buffer for indications */
+
+ struct sk_buff_head rtq; /* Retransmit queue */
+ struct sk_buff_head atq; /* Await transit queue */
+
+ unsigned char xid_count;
+
+ struct llc_struct *nextllc; /* ptr to next llc struct in proto chain */
+};
+
+#define ADD_TO_RTQ(skb) skb_queue_tail(&lp->rtq,skb)
+#define ADD_TO_ATQ(skb) skb_queue_tail(&lp->atq,skb)
+
+void llc_cancel_timers(llcptr lp);
+int llc_decode_frametype(frameptr fr);
+llcptr llc_find(void);
+int llc_free_acknowledged_skbs(llcptr lp, unsigned char ack);
+void llc_handle_xid_indication( char *chsp, short int ll, char *xid_data);
+void llc_interpret_pseudo_code(llcptr lp, int pc_label, struct sk_buff *skb, char type);
+void llc_add_to_queue(struct sk_buff *skb, struct sk_buff **f, struct sk_buff **b);
+void llc_process_otype2_frame(llcptr lp, struct sk_buff *skb, char type);
+struct sk_buff *llc_pull_from_atq(llcptr lp);
+int llc_resend_ipdu(llcptr lp, unsigned char ack_nr, unsigned char type, char p);
+void llc_sendpdu(llcptr lp, char type, char pf, int data_len, char *pdu_data);
+void llc_sendipdu(llcptr lp, char type, char pf, struct sk_buff *skb);
+void llc_start_timer(llcptr lp, int t);
+void llc_stop_timer(llcptr lp, int t);
+void llc_timer_expired(llcptr lp, int t);
+int llc_validate_seq_nos(llcptr lp, frameptr fr);
+
+int llc_data_request(llcptr lp, struct sk_buff *skb);
+void llc_unit_data_request(llcptr lp, int ll, char * data);
+void llc_disconnect_request(llcptr lp);
+void llc_connect_request(llcptr lp);
+void llc_xid_request(llcptr lp, char opt, int data_len, char *pdu_data);
+void llc_test_request(llcptr lp, int data_len, char *pdu_data);
+
+int register_cl2llc_client(llcptr llc, const char *device, void (*ops)(llcptr), u8 *rmac, u8 ssap, u8 dsap);
+void unregister_cl2llc_client(llcptr lp);
+int llc_mac_data_indicate(llcptr lp, struct sk_buff *skb );
diff --git a/pfinet/linux-src/include/net/llc_frame.h b/pfinet/linux-src/include/net/llc_frame.h
new file mode 100644
index 00000000..e8fb198d
--- /dev/null
+++ b/pfinet/linux-src/include/net/llc_frame.h
@@ -0,0 +1,98 @@
+/* if_ether.h needed for definition of ETH_DATA_LEN and ETH_ALEN
+ */
+#include "linux/if_ether.h"
+
+/* frame layout based on par3.2 "LLC PDU format"
+ */
+typedef union { /* pdu layout from pages 40 & 44 */
+ struct { /* general header, all pdu types */
+ unsigned dsap : 8; /* dest service access point */
+ unsigned ssap : 8; /* source service access point */
+ unsigned f1 : 1; /* I- U- or S- format id bits */
+ unsigned f2 : 1;
+ unsigned : 6;
+ unsigned : 8;
+ } pdu_hdr;
+ struct {
+ char dummy1[2]; /* dsap + ssap */
+ char byte1;
+ char byte2;
+ } pdu_cntl; /* unformatted control bytes */
+ struct { /* header of an Information pdu */
+ unsigned char dummy2[2];
+ unsigned : 1;
+ unsigned ns : 7;
+ unsigned i_pflag : 1; /* poll/final bit */
+ unsigned nr : 7; /* N(R) */
+ unsigned char is_info[ ETH_DATA_LEN ];
+ } i_hdr;
+ struct { /* header of a Supervisory pdu */
+ unsigned char dummy3[2];
+ unsigned : 2;
+ unsigned ss : 2; /* supervisory function bits */
+ unsigned : 4;
+ unsigned s_pflag : 1; /* poll/final bit */
+ unsigned nr : 7; /* N(R) */
+ } s_hdr;
+
+/* when accessing the P/F bit or the N(R) field there's no need to distinguish
+ I pdus from S pdus i_pflag and s_pflag / i_nr and s_nr map to the same
+ physical location.
+ */
+ struct { /* header of an Unnumbered pdu */
+ unsigned char dummy4[2];
+ unsigned : 2;
+ unsigned mm1 : 2; /* modifier function part1 */
+ unsigned u_pflag : 1; /* P/F for U- pdus */
+ unsigned mm2 : 3; /* modifier function part2 */
+ unsigned char u_info[ ETH_DATA_LEN-1];
+ } u_hdr;
+ struct { /* mm field in an Unnumbered pdu */
+ unsigned char dummy5[2];
+ unsigned : 2;
+ unsigned mm : 6; /* must be masked to get ridd of P/F ! */
+ } u_mm;
+
+} frame_type, *frameptr;
+
+/* frame format test macros: */
+
+#define IS_UFRAME( fr ) ( ( (fr)->pdu_hdr.f1) & ( (fr)->pdu_hdr.f2) )
+
+#define IS_IFRAME( fr ) ( !( (fr)->pdu_hdr.f1) )
+
+#define IS_SFRAME( fr ) ( ( (fr)->pdu_hdr.f1) & !( (fr)->pdu_hdr.f2) )
+
+#define IS_RSP( fr ) ( fr->pdu_hdr.ssap & 0x01 )
+
+
+/* The transition table, the _encode tables and some tests in the
+ source code depend on the numeric order of these values.
+ Think twice before changing.
+ */
+
+/* frame names for TYPE 2 operation: */
+#define I_CMD 0
+#define RR_CMD 1
+#define RNR_CMD 2
+#define REJ_CMD 3
+#define DISC_CMD 4
+#define SABME_CMD 5
+#define I_RSP 6
+#define RR_RSP 7
+#define RNR_RSP 8
+#define REJ_RSP 9
+#define UA_RSP 10
+#define DM_RSP 11
+#define FRMR_RSP 12
+
+/* junk frame name: */
+#define BAD_FRAME 13
+#define NO_FRAME 13
+
+/* frame names for TYPE 1 operation: */
+#define UI_CMD 14
+#define XID_CMD 15
+#define TEST_CMD 16
+#define XID_RSP 17
+#define TEST_RSP 18
diff --git a/pfinet/linux-src/include/net/llc_name.h b/pfinet/linux-src/include/net/llc_name.h
new file mode 100644
index 00000000..c78d2acb
--- /dev/null
+++ b/pfinet/linux-src/include/net/llc_name.h
@@ -0,0 +1,6 @@
+char *frame_names[] =
+ {"I_CMD","RR_CMD","RNR_CMD","REJ_CMD","DISC_CMD",
+ "SABME_CMD","I_RSP","RR_RSP","RNR_RSP","REJ_RSP",
+ "UA_RSP","DM_RSP","FRMR_RSP","BAD_FRAME","UI_CMD",
+ "XID_CMD","TEST_CMD","XID_RSP","TEST_RSP"
+};
diff --git a/pfinet/linux-src/include/net/llc_state.h b/pfinet/linux-src/include/net/llc_state.h
new file mode 100644
index 00000000..bb18e9bd
--- /dev/null
+++ b/pfinet/linux-src/include/net/llc_state.h
@@ -0,0 +1,4 @@
+char *state_names[] = {
+ "ADM","CONN","RESET_WAIT","RESET_CHECK","SETUP",
+ "RESET","D_CONN","ERROR","NORMAL"
+};
diff --git a/pfinet/linux-src/include/net/llccall.h b/pfinet/linux-src/include/net/llccall.h
new file mode 100644
index 00000000..94bdfb0f
--- /dev/null
+++ b/pfinet/linux-src/include/net/llccall.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void llc_init(struct net_proto *pro);
diff --git a/pfinet/linux-src/include/net/ndisc.h b/pfinet/linux-src/include/net/ndisc.h
new file mode 100644
index 00000000..7a51f367
--- /dev/null
+++ b/pfinet/linux-src/include/net/ndisc.h
@@ -0,0 +1,123 @@
+#ifndef _NDISC_H
+#define _NDISC_H
+
+/*
+ * ICMP codes for neighbour discovery messages
+ */
+
+#define NDISC_ROUTER_SOLICITATION 133
+#define NDISC_ROUTER_ADVERTISEMENT 134
+#define NDISC_NEIGHBOUR_SOLICITATION 135
+#define NDISC_NEIGHBOUR_ADVERTISEMENT 136
+#define NDISC_REDIRECT 137
+
+/*
+ * ndisc options
+ */
+
+#define ND_OPT_SOURCE_LL_ADDR 1
+#define ND_OPT_TARGET_LL_ADDR 2
+#define ND_OPT_PREFIX_INFO 3
+#define ND_OPT_REDIRECT_HDR 4
+#define ND_OPT_MTU 5
+
+#define MAX_RTR_SOLICITATION_DELAY HZ
+
+#define ND_REACHABLE_TIME (30*HZ)
+#define ND_RETRANS_TIMER HZ
+
+#define ND_MIN_RANDOM_FACTOR (1/2)
+#define ND_MAX_RANDOM_FACTOR (3/2)
+
+#ifdef __KERNEL__
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/icmpv6.h>
+#include <net/neighbour.h>
+#include <asm/atomic.h>
+
+extern struct neigh_table nd_tbl;
+
+struct nd_msg {
+ struct icmp6hdr icmph;
+ struct in6_addr target;
+ struct {
+ __u8 opt_type;
+ __u8 opt_len;
+ __u8 link_addr[MAX_ADDR_LEN];
+ } opt;
+};
+
+struct ra_msg {
+ struct icmp6hdr icmph;
+ __u32 reachable_time;
+ __u32 retrans_timer;
+};
+
+
+extern int ndisc_init(struct net_proto_family *ops);
+
+extern void ndisc_cleanup(void);
+
+extern int ndisc_rcv(struct sk_buff *skb, unsigned long len);
+
+extern void ndisc_send_ns(struct device *dev,
+ struct neighbour *neigh,
+ struct in6_addr *solicit,
+ struct in6_addr *daddr,
+ struct in6_addr *saddr);
+
+extern void ndisc_send_rs(struct device *dev,
+ struct in6_addr *saddr,
+ struct in6_addr *daddr);
+
+extern void ndisc_forwarding_on(void);
+extern void ndisc_forwarding_off(void);
+
+extern void ndisc_send_redirect(struct sk_buff *skb,
+ struct neighbour *neigh,
+ struct in6_addr *target);
+
+extern int ndisc_mc_map(struct in6_addr *addr, char *buf, struct device *dev, int dir);
+
+
+struct rt6_info * dflt_rt_lookup(void);
+
+/*
+ * IGMP
+ */
+extern int igmp6_init(struct net_proto_family *ops);
+
+extern void igmp6_cleanup(void);
+
+extern int igmp6_event_query(struct sk_buff *skb,
+ struct icmp6hdr *hdr,
+ int len);
+
+extern int igmp6_event_report(struct sk_buff *skb,
+ struct icmp6hdr *hdr,
+ int len);
+
+extern void igmp6_cleanup(void);
+
+extern __inline__ struct neighbour * ndisc_get_neigh(struct device *dev, struct in6_addr *addr)
+{
+
+ if (dev) {
+ struct neighbour *neigh;
+
+ start_bh_atomic();
+ neigh = __neigh_lookup(&nd_tbl, addr, dev, 1);
+ end_bh_atomic();
+
+ return neigh;
+ }
+ return NULL;
+}
+
+
+#endif /* __KERNEL__ */
+
+
+#endif
diff --git a/pfinet/linux-src/include/net/neighbour.h b/pfinet/linux-src/include/net/neighbour.h
new file mode 100644
index 00000000..ab79f17c
--- /dev/null
+++ b/pfinet/linux-src/include/net/neighbour.h
@@ -0,0 +1,270 @@
+#ifndef _NET_NEIGHBOUR_H
+#define _NET_NEIGHBOUR_H
+
+/*
+ * Generic neighbour manipulation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ */
+
+/* The following flags & states are exported to user space,
+ so that they should be moved to include/linux/ directory.
+ */
+
+/*
+ * Neighbor Cache Entry Flags
+ */
+
+#define NTF_PROXY 0x08 /* == ATF_PUBL */
+#define NTF_ROUTER 0x80
+
+/*
+ * Neighbor Cache Entry States.
+ */
+
+#define NUD_INCOMPLETE 0x01
+#define NUD_REACHABLE 0x02
+#define NUD_STALE 0x04
+#define NUD_DELAY 0x08
+#define NUD_PROBE 0x10
+#define NUD_FAILED 0x20
+
+/* Dummy states */
+#define NUD_NOARP 0x40
+#define NUD_PERMANENT 0x80
+#define NUD_NONE 0x00
+
+/* NUD_NOARP & NUD_PERMANENT are pseudostates, they never change
+ and make no address resolution or NUD.
+ NUD_PERMANENT is also cannot be deleted by garbage collectors.
+ */
+
+#ifdef __KERNEL__
+
+#include <asm/atomic.h>
+#include <linux/skbuff.h>
+
+#define NUD_IN_TIMER (NUD_INCOMPLETE|NUD_DELAY|NUD_PROBE)
+#define NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY)
+#define NUD_CONNECTED (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE)
+
+struct neigh_parms
+{
+ struct neigh_parms *next;
+ int (*neigh_setup)(struct neighbour *);
+ struct neigh_table *tbl;
+ int entries;
+ void *priv;
+
+ void *sysctl_table;
+
+ int base_reachable_time;
+ int retrans_time;
+ int gc_staletime;
+ int reachable_time;
+ int delay_probe_time;
+
+ int queue_len;
+ int ucast_probes;
+ int app_probes;
+ int mcast_probes;
+ int anycast_delay;
+ int proxy_delay;
+ int proxy_qlen;
+ int locktime;
+};
+
+struct neigh_statistics
+{
+ unsigned long allocs;
+ unsigned long res_failed;
+ unsigned long rcv_probes_mcast;
+ unsigned long rcv_probes_ucast;
+};
+
+struct neighbour
+{
+ struct neighbour *next;
+ struct neigh_table *tbl;
+ struct neigh_parms *parms;
+ struct device *dev;
+ unsigned long used;
+ unsigned long confirmed;
+ unsigned long updated;
+ __u8 flags;
+ __u8 nud_state;
+ __u8 type;
+ __u8 probes;
+ unsigned char ha[MAX_ADDR_LEN];
+ struct hh_cache *hh;
+ atomic_t refcnt;
+ int (*output)(struct sk_buff *skb);
+ struct sk_buff_head arp_queue;
+ struct timer_list timer;
+ struct neigh_ops *ops;
+ u8 primary_key[0];
+};
+
+struct neigh_ops
+{
+ int family;
+ void (*destructor)(struct neighbour *);
+ void (*solicit)(struct neighbour *, struct sk_buff*);
+ void (*error_report)(struct neighbour *, struct sk_buff*);
+ int (*output)(struct sk_buff*);
+ int (*connected_output)(struct sk_buff*);
+ int (*hh_output)(struct sk_buff*);
+ int (*queue_xmit)(struct sk_buff*);
+};
+
+struct pneigh_entry
+{
+ struct pneigh_entry *next;
+ struct device *dev;
+ u8 key[0];
+};
+
+#define NEIGH_HASHMASK 0x1F
+#define PNEIGH_HASHMASK 0xF
+
+/*
+ * neighbour table manipulation
+ */
+
+
+struct neigh_table
+{
+ struct neigh_table *next;
+ int family;
+ int entry_size;
+ int key_len;
+ int (*constructor)(struct neighbour *);
+ int (*pconstructor)(struct pneigh_entry *);
+ void (*pdestructor)(struct pneigh_entry *);
+ void (*proxy_redo)(struct sk_buff *skb);
+ struct neigh_parms parms;
+ /* HACK. gc_* shoul follow parms without a gap! */
+ int gc_interval;
+ int gc_thresh1;
+ int gc_thresh2;
+ int gc_thresh3;
+ unsigned long last_flush;
+ struct timer_list gc_timer;
+ struct timer_list proxy_timer;
+ struct sk_buff_head proxy_queue;
+ int entries;
+ atomic_t lock;
+ unsigned long last_rand;
+ struct neigh_parms *parms_list;
+ struct neigh_statistics stats;
+ struct neighbour *hash_buckets[NEIGH_HASHMASK+1];
+ struct pneigh_entry *phash_buckets[PNEIGH_HASHMASK+1];
+};
+
+extern void neigh_table_init(struct neigh_table *tbl);
+extern int neigh_table_clear(struct neigh_table *tbl);
+extern struct neighbour *__neigh_lookup(struct neigh_table *tbl,
+ const void *pkey, struct device *dev,
+ int creat);
+extern void neigh_destroy(struct neighbour *neigh);
+extern int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb);
+extern int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int arp);
+extern int neigh_ifdown(struct neigh_table *tbl, struct device *dev);
+extern int neigh_resolve_output(struct sk_buff *skb);
+extern int neigh_connected_output(struct sk_buff *skb);
+extern int neigh_compat_output(struct sk_buff *skb);
+extern struct neighbour *neigh_event_ns(struct neigh_table *tbl,
+ u8 *lladdr, void *saddr,
+ struct device *dev);
+
+extern struct neigh_parms *neigh_parms_alloc(struct device *dev, struct neigh_table *tbl);
+extern void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms);
+extern unsigned long neigh_rand_reach_time(unsigned long base);
+
+extern void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
+ struct sk_buff *skb);
+extern struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl, const void *key, struct device *dev, int creat);
+extern int pneigh_delete(struct neigh_table *tbl, const void *key, struct device *dev);
+
+struct netlink_callback;
+struct nlmsghdr;
+extern int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb);
+extern int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
+extern int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
+extern void neigh_app_ns(struct neighbour *n);
+
+extern int neigh_sysctl_register(struct device *dev, struct neigh_parms *p,
+ int p_id, int pdev_id, char *p_name);
+extern void neigh_sysctl_unregister(struct neigh_parms *p);
+
+/*
+ * Neighbour references
+ *
+ * When neighbour pointers are passed to "client" code the
+ * reference count is increased. The count is 0 if the node
+ * is only referenced by the corresponding table.
+ */
+
+extern __inline__ void neigh_release(struct neighbour *neigh)
+{
+ if (atomic_dec_and_test(&neigh->refcnt) && neigh->tbl == NULL)
+ neigh_destroy(neigh);
+}
+
+extern __inline__ struct neighbour * neigh_clone(struct neighbour *neigh)
+{
+ if (neigh)
+ atomic_inc(&neigh->refcnt);
+ return neigh;
+}
+
+extern __inline__ void neigh_confirm(struct neighbour *neigh)
+{
+ if (neigh)
+ neigh->confirmed = jiffies;
+}
+
+extern __inline__ struct neighbour *
+neigh_lookup(struct neigh_table *tbl, const void *pkey, struct device *dev)
+{
+ struct neighbour *neigh;
+ start_bh_atomic();
+ neigh = __neigh_lookup(tbl, pkey, dev, 0);
+ end_bh_atomic();
+ return neigh;
+}
+
+extern __inline__ int neigh_is_connected(struct neighbour *neigh)
+{
+ return neigh->nud_state&NUD_CONNECTED;
+}
+
+extern __inline__ int neigh_is_valid(struct neighbour *neigh)
+{
+ return neigh->nud_state&NUD_VALID;
+}
+
+extern __inline__ int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
+{
+ neigh->used = jiffies;
+ if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE)))
+ return __neigh_event_send(neigh, skb);
+ return 0;
+}
+
+extern __inline__ void neigh_table_lock(struct neigh_table *tbl)
+{
+ atomic_inc(&tbl->lock);
+ synchronize_bh();
+}
+
+extern __inline__ void neigh_table_unlock(struct neigh_table *tbl)
+{
+ atomic_dec(&tbl->lock);
+}
+
+
+#endif
+#endif
diff --git a/pfinet/linux-src/include/net/netbeuicall.h b/pfinet/linux-src/include/net/netbeuicall.h
new file mode 100644
index 00000000..5176f82b
--- /dev/null
+++ b/pfinet/linux-src/include/net/netbeuicall.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void netbeui_proto_init(struct net_proto *pro);
diff --git a/pfinet/linux-src/include/net/netrom.h b/pfinet/linux-src/include/net/netrom.h
new file mode 100644
index 00000000..356a7d27
--- /dev/null
+++ b/pfinet/linux-src/include/net/netrom.h
@@ -0,0 +1,181 @@
+/*
+ * Declarations of NET/ROM type objects.
+ *
+ * Jonathan Naylor G4KLX 9/4/95
+ */
+
+#ifndef _NETROM_H
+#define _NETROM_H
+#include <linux/netrom.h>
+
+#define NR_NETWORK_LEN 15
+#define NR_TRANSPORT_LEN 5
+
+#define NR_PROTO_IP 0x0C
+
+#define NR_PROTOEXT 0x00
+#define NR_CONNREQ 0x01
+#define NR_CONNACK 0x02
+#define NR_DISCREQ 0x03
+#define NR_DISCACK 0x04
+#define NR_INFO 0x05
+#define NR_INFOACK 0x06
+
+#define NR_CHOKE_FLAG 0x80
+#define NR_NAK_FLAG 0x40
+#define NR_MORE_FLAG 0x20
+
+/* Define Link State constants. */
+enum {
+ NR_STATE_0,
+ NR_STATE_1,
+ NR_STATE_2,
+ NR_STATE_3
+};
+
+#define NR_COND_ACK_PENDING 0x01
+#define NR_COND_REJECT 0x02
+#define NR_COND_PEER_RX_BUSY 0x04
+#define NR_COND_OWN_RX_BUSY 0x08
+
+#define NR_DEFAULT_T1 (120 * HZ) /* Outstanding frames - 120 seconds */
+#define NR_DEFAULT_T2 (5 * HZ) /* Response delay - 5 seconds */
+#define NR_DEFAULT_N2 3 /* Number of Retries - 3 */
+#define NR_DEFAULT_T4 (180 * HZ) /* Busy Delay - 180 seconds */
+#define NR_DEFAULT_IDLE (0 * 60 * HZ) /* No Activity Timeout - none */
+#define NR_DEFAULT_WINDOW 4 /* Default Window Size - 4 */
+#define NR_DEFAULT_OBS 6 /* Default Obsolescence Count - 6 */
+#define NR_DEFAULT_QUAL 10 /* Default Neighbour Quality - 10 */
+#define NR_DEFAULT_TTL 16 /* Default Time To Live - 16 */
+#define NR_DEFAULT_ROUTING 1 /* Is routing enabled ? */
+#define NR_DEFAULT_FAILS 2 /* Link fails until route fails */
+
+#define NR_MODULUS 256
+#define NR_MAX_WINDOW_SIZE 127 /* Maximum Window Allowable - 127 */
+#define NR_MAX_PACKET_SIZE 236 /* Maximum Packet Length - 236 */
+
+typedef struct {
+ ax25_address user_addr, source_addr, dest_addr;
+ struct device *device;
+ unsigned char my_index, my_id;
+ unsigned char your_index, your_id;
+ unsigned char state, condition, bpqext, window;
+ unsigned short vs, vr, va, vl;
+ unsigned char n2, n2count;
+ unsigned long t1, t2, t4, idle;
+ unsigned short fraglen;
+ struct timer_list t1timer;
+ struct timer_list t2timer;
+ struct timer_list t4timer;
+ struct timer_list idletimer;
+ struct sk_buff_head ack_queue;
+ struct sk_buff_head reseq_queue;
+ struct sk_buff_head frag_queue;
+ struct sock *sk; /* Backlink to socket */
+} nr_cb;
+
+struct nr_neigh {
+ struct nr_neigh *next;
+ ax25_address callsign;
+ ax25_digi *digipeat;
+ ax25_cb *ax25;
+ struct device *dev;
+ unsigned char quality;
+ unsigned char locked;
+ unsigned short count;
+ unsigned int number;
+ unsigned char failed;
+};
+
+struct nr_route {
+ unsigned char quality;
+ unsigned char obs_count;
+ struct nr_neigh *neighbour;
+};
+
+struct nr_node {
+ struct nr_node *next;
+ ax25_address callsign;
+ char mnemonic[7];
+ unsigned char which;
+ unsigned char count;
+ struct nr_route routes[3];
+};
+
+/* af_netrom.c */
+extern int sysctl_netrom_default_path_quality;
+extern int sysctl_netrom_obsolescence_count_initialiser;
+extern int sysctl_netrom_network_ttl_initialiser;
+extern int sysctl_netrom_transport_timeout;
+extern int sysctl_netrom_transport_maximum_tries;
+extern int sysctl_netrom_transport_acknowledge_delay;
+extern int sysctl_netrom_transport_busy_delay;
+extern int sysctl_netrom_transport_requested_window_size;
+extern int sysctl_netrom_transport_no_activity_timeout;
+extern int sysctl_netrom_routing_control;
+extern int sysctl_netrom_link_fails_count;
+extern int nr_rx_frame(struct sk_buff *, struct device *);
+extern void nr_destroy_socket(struct sock *);
+
+/* nr_dev.c */
+extern int nr_rx_ip(struct sk_buff *, struct device *);
+extern int nr_init(struct device *);
+
+#include <net/nrcall.h>
+
+/* nr_in.c */
+extern int nr_process_rx_frame(struct sock *, struct sk_buff *);
+
+/* nr_loopback.c */
+extern void nr_loopback_init(void);
+extern void nr_loopback_clear(void);
+extern int nr_loopback_queue(struct sk_buff *);
+
+/* nr_out.c */
+extern void nr_output(struct sock *, struct sk_buff *);
+extern void nr_send_nak_frame(struct sock *);
+extern void nr_kick(struct sock *);
+extern void nr_transmit_buffer(struct sock *, struct sk_buff *);
+extern void nr_establish_data_link(struct sock *);
+extern void nr_enquiry_response(struct sock *);
+extern void nr_check_iframes_acked(struct sock *, unsigned short);
+
+/* nr_route.c */
+extern void nr_rt_device_down(struct device *);
+extern struct device *nr_dev_first(void);
+extern struct device *nr_dev_get(ax25_address *);
+extern int nr_rt_ioctl(unsigned int, void *);
+extern void nr_link_failed(ax25_cb *, int);
+extern int nr_route_frame(struct sk_buff *, ax25_cb *);
+extern int nr_nodes_get_info(char *, char **, off_t, int, int);
+extern int nr_neigh_get_info(char *, char **, off_t, int, int);
+extern void nr_rt_free(void);
+
+/* nr_subr.c */
+extern void nr_clear_queues(struct sock *);
+extern void nr_frames_acked(struct sock *, unsigned short);
+extern void nr_requeue_frames(struct sock *);
+extern int nr_validate_nr(struct sock *, unsigned short);
+extern int nr_in_rx_window(struct sock *, unsigned short);
+extern void nr_write_internal(struct sock *, int);
+extern void nr_transmit_refusal(struct sk_buff *, int);
+extern void nr_disconnect(struct sock *, int);
+
+/* nr_timer.c */
+extern void nr_start_heartbeat(struct sock *);
+extern void nr_start_t1timer(struct sock *);
+extern void nr_start_t2timer(struct sock *);
+extern void nr_start_t4timer(struct sock *);
+extern void nr_start_idletimer(struct sock *);
+extern void nr_stop_heartbeat(struct sock *);
+extern void nr_stop_t1timer(struct sock *);
+extern void nr_stop_t2timer(struct sock *);
+extern void nr_stop_t4timer(struct sock *);
+extern void nr_stop_idletimer(struct sock *);
+extern int nr_t1timer_running(struct sock *);
+
+/* sysctl_net_netrom.c */
+extern void nr_register_sysctl(void);
+extern void nr_unregister_sysctl(void);
+
+#endif
diff --git a/pfinet/linux-src/include/net/nrcall.h b/pfinet/linux-src/include/net/nrcall.h
new file mode 100644
index 00000000..09ee699d
--- /dev/null
+++ b/pfinet/linux-src/include/net/nrcall.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void nr_proto_init(struct net_proto *pro);
diff --git a/pfinet/linux-src/include/net/p8022.h b/pfinet/linux-src/include/net/p8022.h
new file mode 100644
index 00000000..03d7c3d6
--- /dev/null
+++ b/pfinet/linux-src/include/net/p8022.h
@@ -0,0 +1,7 @@
+#ifndef _NET_P8022_H
+#define _NET_P8022_H
+
+extern struct datalink_proto *register_8022_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *));
+extern void unregister_8022_client(unsigned char type);
+
+#endif
diff --git a/pfinet/linux-src/include/net/p8022call.h b/pfinet/linux-src/include/net/p8022call.h
new file mode 100644
index 00000000..14f0c2ce
--- /dev/null
+++ b/pfinet/linux-src/include/net/p8022call.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of Space.c simpler */
+extern void p8022_proto_init(struct net_proto *);
diff --git a/pfinet/linux-src/include/net/pkt_cls.h b/pfinet/linux-src/include/net/pkt_cls.h
new file mode 100644
index 00000000..b8547e60
--- /dev/null
+++ b/pfinet/linux-src/include/net/pkt_cls.h
@@ -0,0 +1,93 @@
+#ifndef __NET_PKT_CLS_H
+#define __NET_PKT_CLS_H
+
+
+#include <linux/pkt_cls.h>
+
+struct rtattr;
+struct tcmsg;
+
+/* Basic packet classifier frontend definitions. */
+
+struct tcf_result
+{
+ unsigned long class;
+ u32 classid;
+};
+
+struct tcf_proto
+{
+ /* Fast access part */
+ struct tcf_proto *next;
+ void *root;
+ int (*classify)(struct sk_buff*, struct tcf_proto*, struct tcf_result *);
+ u32 protocol;
+
+ /* All the rest */
+ u32 prio;
+ u32 classid;
+ struct Qdisc *q;
+ void *data;
+ struct tcf_proto_ops *ops;
+};
+
+struct tcf_walker
+{
+ int stop;
+ int skip;
+ int count;
+ int (*fn)(struct tcf_proto *, unsigned long node, struct tcf_walker *);
+};
+
+struct tcf_proto_ops
+{
+ struct tcf_proto_ops *next;
+ char kind[IFNAMSIZ];
+
+ int (*classify)(struct sk_buff*, struct tcf_proto*, struct tcf_result *);
+ int (*init)(struct tcf_proto*);
+ void (*destroy)(struct tcf_proto*);
+
+ unsigned long (*get)(struct tcf_proto*, u32 handle);
+ void (*put)(struct tcf_proto*, unsigned long);
+ int (*change)(struct tcf_proto*, unsigned long, u32 handle, struct rtattr **, unsigned long *);
+ int (*delete)(struct tcf_proto*, unsigned long);
+ void (*walk)(struct tcf_proto*, struct tcf_walker *arg);
+
+ /* rtnetlink specific */
+ int (*dump)(struct tcf_proto*, unsigned long, struct sk_buff *skb, struct tcmsg*);
+};
+
+/* Main classifier routine: scans classifier chain attached
+ to this qdisc, (optionally) tests for protocol and asks
+ specific classifiers.
+ */
+
+extern __inline__ int tc_classify(struct sk_buff *skb, struct tcf_proto *tp, struct tcf_result *res)
+{
+ int err = 0;
+ u32 protocol = skb->protocol;
+
+ for ( ; tp; tp = tp->next) {
+ if ((tp->protocol == protocol ||
+ tp->protocol == __constant_htons(ETH_P_ALL)) &&
+ (err = tp->classify(skb, tp, res)) >= 0)
+ return err;
+ }
+ return -1;
+}
+
+extern __inline__ unsigned long cls_set_class(unsigned long *clp, unsigned long cl)
+{
+ unsigned long old_cl;
+
+ old_cl = *clp;
+ *clp = cl;
+ synchronize_bh();
+ return old_cl;
+}
+
+extern int register_tcf_proto_ops(struct tcf_proto_ops *ops);
+extern int unregister_tcf_proto_ops(struct tcf_proto_ops *ops);
+
+#endif
diff --git a/pfinet/linux-src/include/net/pkt_sched.h b/pfinet/linux-src/include/net/pkt_sched.h
new file mode 100644
index 00000000..c2ef8656
--- /dev/null
+++ b/pfinet/linux-src/include/net/pkt_sched.h
@@ -0,0 +1,407 @@
+#ifndef __NET_PKT_SCHED_H
+#define __NET_PKT_SCHED_H
+
+#define PSCHED_GETTIMEOFDAY 1
+#define PSCHED_JIFFIES 2
+#define PSCHED_CPU 3
+
+#define PSCHED_CLOCK_SOURCE PSCHED_JIFFIES
+
+#include <linux/pkt_sched.h>
+#include <net/pkt_cls.h>
+
+struct rtattr;
+struct Qdisc;
+
+struct qdisc_walker
+{
+ int stop;
+ int skip;
+ int count;
+ int (*fn)(struct Qdisc *, unsigned long cl, struct qdisc_walker *);
+};
+
+struct Qdisc_class_ops
+{
+ /* Child qdisc manipulation */
+ int (*graft)(struct Qdisc *, unsigned long cl, struct Qdisc *, struct Qdisc **);
+ struct Qdisc * (*leaf)(struct Qdisc *, unsigned long cl);
+
+ /* Class manipulation routines */
+ unsigned long (*get)(struct Qdisc *, u32 classid);
+ void (*put)(struct Qdisc *, unsigned long);
+ int (*change)(struct Qdisc *, u32, u32, struct rtattr **, unsigned long *);
+ int (*delete)(struct Qdisc *, unsigned long);
+ void (*walk)(struct Qdisc *, struct qdisc_walker * arg);
+
+ /* Filter manipulation */
+ struct tcf_proto ** (*tcf_chain)(struct Qdisc *, unsigned long);
+ unsigned long (*bind_tcf)(struct Qdisc *, unsigned long, u32 classid);
+ void (*unbind_tcf)(struct Qdisc *, unsigned long);
+
+ /* rtnetlink specific */
+ int (*dump)(struct Qdisc *, unsigned long, struct sk_buff *skb, struct tcmsg*);
+};
+
+struct Qdisc_ops
+{
+ struct Qdisc_ops *next;
+ struct Qdisc_class_ops *cl_ops;
+ char id[IFNAMSIZ];
+ int priv_size;
+
+ int (*enqueue)(struct sk_buff *, struct Qdisc *);
+ struct sk_buff * (*dequeue)(struct Qdisc *);
+ int (*requeue)(struct sk_buff *, struct Qdisc *);
+ int (*drop)(struct Qdisc *);
+
+ int (*init)(struct Qdisc *, struct rtattr *arg);
+ void (*reset)(struct Qdisc *);
+ void (*destroy)(struct Qdisc *);
+ int (*change)(struct Qdisc *, struct rtattr *arg);
+
+ int (*dump)(struct Qdisc *, struct sk_buff *);
+};
+
+struct Qdisc_head
+{
+ struct Qdisc_head *forw;
+};
+
+extern struct Qdisc_head qdisc_head;
+
+struct Qdisc
+{
+ struct Qdisc_head h;
+ int (*enqueue)(struct sk_buff *skb, struct Qdisc *dev);
+ struct sk_buff * (*dequeue)(struct Qdisc *dev);
+ unsigned flags;
+#define TCQ_F_BUILTIN 1
+#define TCQ_F_THROTTLED 2
+ struct Qdisc_ops *ops;
+ struct Qdisc *next;
+ u32 handle;
+ atomic_t refcnt;
+ struct sk_buff_head q;
+ struct device *dev;
+
+ struct tc_stats stats;
+ unsigned long tx_timeo;
+ unsigned long tx_last;
+ int (*reshape_fail)(struct sk_buff *skb, struct Qdisc *q);
+
+ /* This field is deprecated, but it is still used by CBQ
+ * and it will live until better solution will be invented.
+ */
+ struct Qdisc *__parent;
+
+ char data[0];
+};
+
+struct qdisc_rate_table
+{
+ struct tc_ratespec rate;
+ u32 data[256];
+ struct qdisc_rate_table *next;
+ int refcnt;
+};
+
+
+/*
+ Timer resolution MUST BE < 10% of min_schedulable_packet_size/bandwidth
+
+ Normal IP packet size ~ 512byte, hence:
+
+ 0.5Kbyte/1Mbyte/sec = 0.5msec, so that we need 50usec timer for
+ 10Mbit ethernet.
+
+ 10msec resolution -> <50Kbit/sec.
+
+ The result: [34]86 is not good choice for QoS router :-(
+
+ The things are not so bad, because we may use artificial
+ clock evaluated by integration of network data flow
+ in the most critical places.
+
+ Note: we do not use fastgettimeofday.
+ The reason is that, when it is not the same thing as
+ gettimeofday, it returns invalid timestamp, which is
+ not updated, when net_bh is active.
+
+ So, use PSCHED_CLOCK_SOURCE = PSCHED_CPU on alpha and pentiums
+ with rtdsc. And PSCHED_JIFFIES on all other architectures, including [34]86
+ and pentiums without rtdsc.
+ You can use PSCHED_GETTIMEOFDAY on another architectures,
+ which have fast and precise clock source, but it is too expensive.
+ */
+
+/* General note about internal clock.
+
+ Any clock source returns time intervals, measured in units
+ close to 1usec. With source PSCHED_GETTIMEOFDAY it is precisely
+ microseconds, otherwise something close but different chosen to minimize
+ arithmetic cost. Ratio usec/internal untis in form nominator/denominator
+ may be read from /proc/net/psched.
+ */
+
+
+#if PSCHED_CLOCK_SOURCE == PSCHED_GETTIMEOFDAY
+
+typedef struct timeval psched_time_t;
+typedef long psched_tdiff_t;
+
+#define PSCHED_GET_TIME(stamp) do_gettimeofday(&(stamp))
+#define PSCHED_US2JIFFIE(usecs) (((usecs)+(1000000/HZ-1))/(1000000/HZ))
+
+#define PSCHED_EXPORTLIST EXPORT_SYMBOL(psched_tod_diff);
+
+#else /* PSCHED_CLOCK_SOURCE != PSCHED_GETTIMEOFDAY */
+
+#define PSCHED_EXPORTLIST PSCHED_EXPORTLIST_1 PSCHED_EXPORTLIST_2
+
+typedef u64 psched_time_t;
+typedef long psched_tdiff_t;
+
+extern psched_time_t psched_time_base;
+
+#if PSCHED_CLOCK_SOURCE == PSCHED_JIFFIES
+
+#if HZ == 100
+#define PSCHED_JSCALE 13
+#elif HZ == 1024
+#define PSCHED_JSCALE 10
+#else
+#define PSCHED_JSCALE 0
+#endif
+
+#define PSCHED_EXPORTLIST_2
+
+#if ~0UL == 0xFFFFFFFF
+
+#define PSCHED_WATCHER unsigned long
+
+extern PSCHED_WATCHER psched_time_mark;
+
+#define PSCHED_GET_TIME(stamp) ((stamp) = psched_time_base + (((unsigned long)(jiffies-psched_time_mark))<<PSCHED_JSCALE))
+
+#define PSCHED_EXPORTLIST_1 EXPORT_SYMBOL(psched_time_base); \
+ EXPORT_SYMBOL(psched_time_mark);
+
+#else
+
+#define PSCHED_GET_TIME(stamp) ((stamp) = (jiffies<<PSCHED_JSCALE))
+
+#define PSCHED_EXPORTLIST_1
+
+#endif
+
+#define PSCHED_US2JIFFIE(delay) (((delay)+(1<<PSCHED_JSCALE)-1)>>PSCHED_JSCALE)
+
+#elif PSCHED_CLOCK_SOURCE == PSCHED_CPU
+
+extern psched_tdiff_t psched_clock_per_hz;
+extern int psched_clock_scale;
+
+#define PSCHED_EXPORTLIST_2 EXPORT_SYMBOL(psched_clock_per_hz); \
+ EXPORT_SYMBOL(psched_clock_scale);
+
+#define PSCHED_US2JIFFIE(delay) (((delay)+psched_clock_per_hz-1)/psched_clock_per_hz)
+
+#if CPU == 586 || CPU == 686
+
+#define PSCHED_GET_TIME(stamp) \
+({ u64 __cur; \
+ __asm__ __volatile__ (".byte 0x0f,0x31" :"=A" (__cur)); \
+ (stamp) = __cur>>psched_clock_scale; \
+})
+
+#define PSCHED_EXPORTLIST_1
+
+#elif defined (__alpha__)
+
+#define PSCHED_WATCHER u32
+
+extern PSCHED_WATCHER psched_time_mark;
+
+#define PSCHED_GET_TIME(stamp) \
+({ u32 __res; \
+ __asm__ __volatile__ ("rpcc %0" : "r="(__res)); \
+ if (__res <= psched_time_mark) psched_time_base += 0x100000000UL; \
+ psched_time_mark = __res; \
+ (stamp) = (psched_time_base + __res)>>psched_clock_scale; \
+})
+
+#define PSCHED_EXPORTLIST_1 EXPORT_SYMBOL(psched_time_base); \
+ EXPORT_SYMBOL(psched_time_mark);
+
+#else
+
+#error PSCHED_CLOCK_SOURCE=PSCHED_CPU is not supported on this arch.
+
+#endif /* ARCH */
+
+#endif /* PSCHED_CLOCK_SOURCE == PSCHED_JIFFIES */
+
+#endif /* PSCHED_CLOCK_SOURCE == PSCHED_GETTIMEOFDAY */
+
+#if PSCHED_CLOCK_SOURCE == PSCHED_GETTIMEOFDAY
+#define PSCHED_TDIFF(tv1, tv2) \
+({ \
+ int __delta_sec = (tv1).tv_sec - (tv2).tv_sec; \
+ int __delta = (tv1).tv_usec - (tv2).tv_usec; \
+ if (__delta_sec) { \
+ switch (__delta_sec) { \
+ default: \
+ __delta = 0; \
+ case 2: \
+ __delta += 1000000; \
+ case 1: \
+ __delta += 1000000; \
+ } \
+ } \
+ __delta; \
+})
+
+extern int psched_tod_diff(int delta_sec, int bound);
+
+#define PSCHED_TDIFF_SAFE(tv1, tv2, bound, guard) \
+({ \
+ int __delta_sec = (tv1).tv_sec - (tv2).tv_sec; \
+ int __delta = (tv1).tv_usec - (tv2).tv_usec; \
+ switch (__delta_sec) { \
+ default: \
+ __delta = psched_tod_diff(__delta_sec, bound); guard; break; \
+ case 2: \
+ __delta += 1000000; \
+ case 1: \
+ __delta += 1000000; \
+ case 0: ; \
+ } \
+ __delta; \
+})
+
+#define PSCHED_TLESS(tv1, tv2) (((tv1).tv_usec < (tv2).tv_usec && \
+ (tv1).tv_sec <= (tv2).tv_sec) || \
+ (tv1).tv_sec < (tv2).tv_sec)
+
+#define PSCHED_TADD2(tv, delta, tv_res) \
+({ \
+ int __delta = (tv).tv_usec + (delta); \
+ (tv_res).tv_sec = (tv).tv_sec; \
+ if (__delta > 1000000) { (tv_res).tv_sec++; __delta -= 1000000; } \
+ (tv_res).tv_usec = __delta; \
+})
+
+#define PSCHED_TADD(tv, delta) \
+({ \
+ (tv).tv_usec += (delta); \
+ if ((tv).tv_usec > 1000000) { (tv).tv_sec++; \
+ (tv).tv_usec -= 1000000; } \
+})
+
+/* Set/check that time is in the "past perfect";
+ it depends on concrete representation of system time
+ */
+
+#define PSCHED_SET_PASTPERFECT(t) ((t).tv_sec = 0)
+#define PSCHED_IS_PASTPERFECT(t) ((t).tv_sec == 0)
+
+#define PSCHED_AUDIT_TDIFF(t) ({ if ((t) > 2000000) (t) = 2000000; })
+
+#else
+
+#define PSCHED_TDIFF(tv1, tv2) (long)((tv1) - (tv2))
+#define PSCHED_TDIFF_SAFE(tv1, tv2, bound, guard) \
+({ \
+ long __delta = (tv1) - (tv2); \
+ if ( __delta > (bound)) { __delta = (bound); guard; } \
+ __delta; \
+})
+
+
+#define PSCHED_TLESS(tv1, tv2) ((tv1) < (tv2))
+#define PSCHED_TADD2(tv, delta, tv_res) ((tv_res) = (tv) + (delta))
+#define PSCHED_TADD(tv, delta) ((tv) += (delta))
+#define PSCHED_SET_PASTPERFECT(t) ((t) = 0)
+#define PSCHED_IS_PASTPERFECT(t) ((t) == 0)
+#define PSCHED_AUDIT_TDIFF(t)
+
+#endif
+
+struct tcf_police
+{
+ struct tcf_police *next;
+ int refcnt;
+ u32 index;
+
+ int action;
+ int result;
+ u32 ewma_rate;
+ u32 burst;
+ u32 mtu;
+
+ u32 toks;
+ u32 ptoks;
+ psched_time_t t_c;
+ struct qdisc_rate_table *R_tab;
+ struct qdisc_rate_table *P_tab;
+
+ struct tc_stats stats;
+};
+
+extern void tcf_police_destroy(struct tcf_police *p);
+extern struct tcf_police * tcf_police_locate(struct rtattr *rta, struct rtattr *est);
+extern int tcf_police_dump(struct sk_buff *skb, struct tcf_police *p);
+extern int tcf_police(struct sk_buff *skb, struct tcf_police *p);
+
+extern __inline__ void tcf_police_release(struct tcf_police *p)
+{
+ if (p && --p->refcnt == 0)
+ tcf_police_destroy(p);
+}
+
+extern struct Qdisc noop_qdisc;
+extern struct Qdisc_ops noop_qdisc_ops;
+extern struct Qdisc_ops pfifo_qdisc_ops;
+extern struct Qdisc_ops bfifo_qdisc_ops;
+
+int register_qdisc(struct Qdisc_ops *qops);
+int unregister_qdisc(struct Qdisc_ops *qops);
+struct Qdisc *qdisc_lookup(struct device *dev, u32 handle);
+struct Qdisc *qdisc_lookup_class(struct device *dev, u32 handle);
+void dev_init_scheduler(struct device *dev);
+void dev_shutdown(struct device *dev);
+void dev_activate(struct device *dev);
+void dev_deactivate(struct device *dev);
+void qdisc_reset(struct Qdisc *qdisc);
+void qdisc_destroy(struct Qdisc *qdisc);
+struct Qdisc * qdisc_create_dflt(struct device *dev, struct Qdisc_ops *ops);
+int qdisc_new_estimator(struct tc_stats *stats, struct rtattr *opt);
+void qdisc_kill_estimator(struct tc_stats *stats);
+struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, struct rtattr *tab);
+void qdisc_put_rtab(struct qdisc_rate_table *tab);
+int teql_init(void);
+int tc_filter_init(void);
+int pktsched_init(void);
+
+void qdisc_run_queues(void);
+int qdisc_restart(struct device *dev);
+
+extern __inline__ void qdisc_wakeup(struct device *dev)
+{
+ if (!dev->tbusy) {
+ struct Qdisc *q = dev->qdisc;
+ if (qdisc_restart(dev) && q->h.forw == NULL) {
+ q->h.forw = qdisc_head.forw;
+ qdisc_head.forw = &q->h;
+ }
+ }
+}
+
+extern __inline__ unsigned psched_mtu(struct device *dev)
+{
+ unsigned mtu = dev->mtu;
+ return dev->hard_header ? mtu + dev->hard_header_len : mtu;
+}
+
+#endif
diff --git a/pfinet/linux-src/include/net/profile.h b/pfinet/linux-src/include/net/profile.h
new file mode 100644
index 00000000..82fad0a3
--- /dev/null
+++ b/pfinet/linux-src/include/net/profile.h
@@ -0,0 +1,311 @@
+#include <linux/config.h> /* for CONFIG_NET_PROFILE */
+#ifndef _NET_PROFILE_H_
+#define _NET_PROFILE_H_ 1
+
+#ifdef CONFIG_NET_PROFILE
+
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/kernel.h>
+#include <asm/system.h>
+
+struct net_profile_slot
+{
+ char id[16];
+ struct net_profile_slot *next;
+ struct timeval entered;
+ struct timeval accumulator;
+ struct timeval irq;
+ int hits;
+ int active;
+ int underflow;
+};
+
+extern atomic_t net_profile_active;
+extern struct timeval net_profile_adjust;
+extern void net_profile_irq_adjust(struct timeval *entered, struct timeval* leaved);
+
+#if CPU == 586 || CPU == 686
+
+
+extern __inline__ void net_profile_stamp(struct timeval *pstamp)
+{
+ __asm__ __volatile__ (".byte 0x0f,0x31"
+ :"=a" (pstamp->tv_usec),
+ "=d" (pstamp->tv_sec));
+}
+
+extern __inline__ void net_profile_accumulate(struct timeval *entered,
+ struct timeval *leaved,
+ struct timeval *acc)
+{
+ __asm__ __volatile__ ("subl %2,%0\n\t"
+ "sbbl %3,%1\n\t"
+ "addl %4,%0\n\t"
+ "adcl %5,%1\n\t"
+ "subl " SYMBOL_NAME_STR(net_profile_adjust) "+4,%0\n\t"
+ "sbbl $0,%1\n\t"
+ : "=r" (acc->tv_usec), "=r" (acc->tv_sec)
+ : "g" (entered->tv_usec), "g" (entered->tv_sec),
+ "g" (leaved->tv_usec), "g" (leaved->tv_sec),
+ "0" (acc->tv_usec), "1" (acc->tv_sec));
+}
+
+extern __inline__ void net_profile_sub(struct timeval *sub,
+ struct timeval *acc)
+{
+ __asm__ __volatile__ ("subl %2,%0\n\t"
+ "sbbl %3,%1\n\t"
+ : "=r" (acc->tv_usec), "=r" (acc->tv_sec)
+ : "g" (sub->tv_usec), "g" (sub->tv_sec),
+ "0" (acc->tv_usec), "1" (acc->tv_sec));
+}
+
+extern __inline__ void net_profile_add(struct timeval *add,
+ struct timeval *acc)
+{
+ __asm__ __volatile__ ("addl %2,%0\n\t"
+ "adcl %3,%1\n\t"
+ : "=r" (acc->tv_usec), "=r" (acc->tv_sec)
+ : "g" (add->tv_usec), "g" (add->tv_sec),
+ "0" (acc->tv_usec), "1" (acc->tv_sec));
+}
+
+
+#elif defined (__alpha__)
+
+extern __u32 alpha_lo;
+extern long alpha_hi;
+
+/* On alpha cycle counter has only 32 bits :-( :-( */
+
+extern __inline__ void net_profile_stamp(struct timeval *pstamp)
+{
+ __u32 result;
+ __asm__ __volatile__ ("rpcc %0" : "r="(result));
+ if (result <= alpha_lo)
+ alpha_hi++;
+ alpha_lo = result;
+ pstamp->tv_sec = alpha_hi;
+ pstamp->tv_usec = alpha_lo;
+}
+
+extern __inline__ void net_profile_accumulate(struct timeval *entered,
+ struct timeval *leaved,
+ struct timeval *acc)
+{
+ time_t usecs = acc->tv_usec + leaved->tv_usec - entered->tv_usec
+ - net_profile_adjust.tv_usec;
+ time_t secs = acc->tv_sec + leaved->tv_sec - entered->tv_sec;
+
+ if (usecs >= 0x100000000L) {
+ usecs -= 0x100000000L;
+ secs++;
+ } else if (usecs < -0x100000000L) {
+ usecs += 0x200000000L;
+ secs -= 2;
+ } else if (usecs < 0) {
+ usecs += 0x100000000L;
+ secs--;
+ }
+ acc->tv_sec = secs;
+ acc->tv_usec = usecs;
+}
+
+extern __inline__ void net_profile_sub(struct timeval *entered,
+ struct timeval *leaved)
+{
+ time_t usecs = leaved->tv_usec - entered->tv_usec;
+ time_t secs = leaved->tv_sec - entered->tv_sec;
+
+ if (usecs < 0) {
+ usecs += 0x100000000L;
+ secs--;
+ }
+ leaved->tv_sec = secs;
+ leaved->tv_usec = usecs;
+}
+
+extern __inline__ void net_profile_add(struct timeval *entered, struct timeval *leaved)
+{
+ time_t usecs = leaved->tv_usec + entered->tv_usec;
+ time_t secs = leaved->tv_sec + entered->tv_sec;
+
+ if (usecs >= 0x100000000L) {
+ usecs -= 0x100000000L;
+ secs++;
+ }
+ leaved->tv_sec = secs;
+ leaved->tv_usec = usecs;
+}
+
+
+#else
+
+extern __inline__ void net_profile_stamp(struct timeval *pstamp)
+{
+ /* Not "fast" counterpart! On architectures without
+ cpu clock "fast" routine is absolutely useless in this
+ situation. do_gettimeofday still says something on slow-slow-slow
+ boxes, though it eats more cpu time than the sobject of
+ investigation :-) :-)
+ */
+ do_gettimeofday(pstamp);
+}
+
+extern __inline__ void net_profile_accumulate(struct timeval *entered,
+ struct timeval *leaved,
+ struct timeval *acc)
+{
+ time_t usecs = acc->tv_usec + leaved->tv_usec - entered->tv_usec
+ - net_profile_adjust.tv_usec;
+ time_t secs = acc->tv_sec + leaved->tv_sec - entered->tv_sec;
+
+ if (usecs >= 1000000) {
+ usecs -= 1000000;
+ secs++;
+ } else if (usecs < -1000000) {
+ usecs += 2000000;
+ secs -= 2;
+ } else if (usecs < 0) {
+ usecs += 1000000;
+ secs--;
+ }
+ acc->tv_sec = secs;
+ acc->tv_usec = usecs;
+}
+
+extern __inline__ void net_profile_sub(struct timeval *entered,
+ struct timeval *leaved)
+{
+ time_t usecs = leaved->tv_usec - entered->tv_usec;
+ time_t secs = leaved->tv_sec - entered->tv_sec;
+
+ if (usecs < 0) {
+ usecs += 1000000;
+ secs--;
+ }
+ leaved->tv_sec = secs;
+ leaved->tv_usec = usecs;
+}
+
+extern __inline__ void net_profile_add(struct timeval *entered, struct timeval *leaved)
+{
+ time_t usecs = leaved->tv_usec + entered->tv_usec;
+ time_t secs = leaved->tv_sec + entered->tv_sec;
+
+ if (usecs >= 1000000) {
+ usecs -= 1000000;
+ secs++;
+ }
+ leaved->tv_sec = secs;
+ leaved->tv_usec = usecs;
+}
+
+
+
+#endif
+
+extern __inline__ void net_profile_enter(struct net_profile_slot *s)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (s->active++ == 0) {
+ net_profile_stamp(&s->entered);
+ atomic_inc(&net_profile_active);
+ }
+ restore_flags(flags);
+}
+
+extern __inline__ void net_profile_leave_irq(struct net_profile_slot *s)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (--s->active <= 0) {
+ if (s->active == 0) {
+ struct timeval curr_pstamp;
+ net_profile_stamp(&curr_pstamp);
+ net_profile_accumulate(&s->entered, &curr_pstamp, &s->accumulator);
+ if (!atomic_dec_and_test(&net_profile_active))
+ net_profile_irq_adjust(&s->entered, &curr_pstamp);
+ } else {
+ s->underflow++;
+ }
+ }
+ s->hits++;
+ restore_flags(flags);
+}
+
+extern __inline__ void net_profile_leave(struct net_profile_slot *s)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ if (--s->active <= 0) {
+ if (s->active == 0) {
+ struct timeval curr_pstamp;
+ net_profile_stamp(&curr_pstamp);
+ net_profile_accumulate(&s->entered, &curr_pstamp, &s->accumulator);
+ atomic_dec(&net_profile_active);
+ } else {
+ s->underflow++;
+ }
+ }
+ s->hits++;
+ restore_flags(flags);
+}
+
+
+#define NET_PROFILE_ENTER(slot) net_profile_enter(&net_prof_##slot)
+#define NET_PROFILE_LEAVE(slot) net_profile_leave(&net_prof_##slot)
+#define NET_PROFILE_LEAVE_IRQ(slot) net_profile_leave_irq(&net_prof_##slot)
+
+#define NET_PROFILE_SKB_CLEAR(skb) ({ \
+ skb->pstamp.tv_usec = 0; \
+})
+
+#define NET_PROFILE_SKB_INIT(skb) ({ \
+ net_profile_stamp(&skb->pstamp); \
+})
+
+#define NET_PROFILE_SKB_PASSED(skb, slot) ({ \
+ if (skb->pstamp.tv_usec) { \
+ struct timeval cur_pstamp = skb->pstamp; \
+ net_profile_stamp(&skb->pstamp); \
+ net_profile_accumulate(&cur_pstamp, &skb->pstamp, &net_prof_##slot.accumulator); \
+ net_prof_##slot.hits++; \
+ }})
+
+#define NET_PROFILE_DECL(slot) \
+ extern struct net_profile_slot net_prof_##slot;
+
+#define NET_PROFILE_DEFINE(slot) \
+ struct net_profile_slot net_prof_##slot = { #slot, };
+
+#define NET_PROFILE_REGISTER(slot) net_profile_register(&net_prof_##slot)
+#define NET_PROFILE_UNREGISTER(slot) net_profile_unregister(&net_prof_##slot)
+
+extern int net_profile_init(void);
+extern int net_profile_register(struct net_profile_slot *);
+extern int net_profile_unregister(struct net_profile_slot *);
+
+#else
+
+#define NET_PROFILE_ENTER(slot) do { /* nothing */ } while(0)
+#define NET_PROFILE_LEAVE(slot) do { /* nothing */ } while(0)
+#define NET_PROFILE_LEAVE_IRQ(slot) do { /* nothing */ } while(0)
+#define NET_PROFILE_SKB_CLEAR(skb) do { /* nothing */ } while(0)
+#define NET_PROFILE_SKB_INIT(skb) do { /* nothing */ } while(0)
+#define NET_PROFILE_SKB_PASSED(skb, slot) do { /* nothing */ } while(0)
+#define NET_PROFILE_DECL(slot)
+#define NET_PROFILE_DEFINE(slot)
+#define NET_PROFILE_REGISTER(slot) do { /* nothing */ } while(0)
+#define NET_PROFILE_UNREGISTER(slot) do { /* nothing */ } while(0)
+
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/net/protocol.h b/pfinet/linux-src/include/net/protocol.h
new file mode 100644
index 00000000..f6e947b1
--- /dev/null
+++ b/pfinet/linux-src/include/net/protocol.h
@@ -0,0 +1,82 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the protocol dispatcher.
+ *
+ * Version: @(#)protocol.h 1.0.2 05/07/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Changes:
+ * Alan Cox : Added a name field and a frag handler
+ * field for later.
+ * Alan Cox : Cleaned up, and sorted types.
+ * Pedro Roque : inet6 protocols
+ */
+
+#ifndef _PROTOCOL_H
+#define _PROTOCOL_H
+
+#include <linux/config.h>
+#include <linux/in6.h>
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+#include <linux/ipv6.h>
+#endif
+
+#define MAX_INET_PROTOS 32 /* Must be a power of 2 */
+
+
+/* This is used to register protocols. */
+struct inet_protocol
+{
+ int (*handler)(struct sk_buff *skb, unsigned short len);
+ void (*err_handler)(struct sk_buff *skb, unsigned char *dp, int len);
+ struct inet_protocol *next;
+ unsigned char protocol;
+ unsigned char copy:1;
+ void *data;
+ const char *name;
+};
+
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+struct inet6_protocol
+{
+ int (*handler)(struct sk_buff *skb,
+ unsigned long len);
+
+ void (*err_handler)(struct sk_buff *skb, struct ipv6hdr *hdr,
+ struct inet6_skb_parm *opt,
+ int type, int code, unsigned char *buff,
+ __u32 info);
+ struct inet6_protocol *next;
+ unsigned char protocol;
+ unsigned char copy:1;
+ void *data;
+ const char *name;
+};
+#endif
+
+extern struct inet_protocol *inet_protocol_base;
+extern struct inet_protocol *inet_protos[MAX_INET_PROTOS];
+
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+extern struct inet6_protocol *inet6_protocol_base;
+extern struct inet6_protocol *inet6_protos[MAX_INET_PROTOS];
+#endif
+
+extern void inet_add_protocol(struct inet_protocol *prot);
+extern int inet_del_protocol(struct inet_protocol *prot);
+
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+extern void inet6_add_protocol(struct inet6_protocol *prot);
+extern int inet6_del_protocol(struct inet6_protocol *prot);
+#endif
+
+#endif /* _PROTOCOL_H */
diff --git a/pfinet/linux-src/include/net/psnap.h b/pfinet/linux-src/include/net/psnap.h
new file mode 100644
index 00000000..49a68f7e
--- /dev/null
+++ b/pfinet/linux-src/include/net/psnap.h
@@ -0,0 +1,7 @@
+#ifndef _NET_PSNAP_H
+#define _NET_PSNAP_H
+
+extern struct datalink_proto *register_snap_client(unsigned char *desc, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *));
+extern void unregister_snap_client(unsigned char *desc);
+
+#endif
diff --git a/pfinet/linux-src/include/net/psnapcall.h b/pfinet/linux-src/include/net/psnapcall.h
new file mode 100644
index 00000000..9da5763c
--- /dev/null
+++ b/pfinet/linux-src/include/net/psnapcall.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of Space.c simpler */
+extern void snap_proto_init(struct net_proto *);
diff --git a/pfinet/linux-src/include/net/rarp.h b/pfinet/linux-src/include/net/rarp.h
new file mode 100644
index 00000000..7842d971
--- /dev/null
+++ b/pfinet/linux-src/include/net/rarp.h
@@ -0,0 +1,11 @@
+/* linux/net/inet/rarp.h */
+#ifndef _RARP_H
+#define _RARP_H
+
+extern int rarp_ioctl(unsigned int cmd, void *arg);
+extern int rarp_get_info(char *buffer,
+ char **start,
+ off_t offset,
+ int length,
+ int dummy);
+#endif /* _RARP_H */
diff --git a/pfinet/linux-src/include/net/raw.h b/pfinet/linux-src/include/net/raw.h
new file mode 100644
index 00000000..4d2e6e98
--- /dev/null
+++ b/pfinet/linux-src/include/net/raw.h
@@ -0,0 +1,38 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the RAW-IP module.
+ *
+ * Version: @(#)raw.h 1.0.2 05/07/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _RAW_H
+#define _RAW_H
+
+
+extern struct proto raw_prot;
+
+
+extern void raw_err(struct sock *, struct sk_buff *);
+extern int raw_rcv(struct sock *, struct sk_buff *);
+
+/* Note: v4 ICMP wants to get at this stuff, if you change the
+ * hashing mechanism, make sure you update icmp.c as well.
+ */
+#define RAWV4_HTABLE_SIZE MAX_INET_PROTOS
+extern struct sock *raw_v4_htable[RAWV4_HTABLE_SIZE];
+
+
+extern struct sock *raw_v4_lookup(struct sock *sk, unsigned short num,
+ unsigned long raddr, unsigned long laddr,
+ int dif);
+
+#endif /* _RAW_H */
diff --git a/pfinet/linux-src/include/net/rawv6.h b/pfinet/linux-src/include/net/rawv6.h
new file mode 100644
index 00000000..d54572d1
--- /dev/null
+++ b/pfinet/linux-src/include/net/rawv6.h
@@ -0,0 +1,27 @@
+#ifndef _NET_RAWV6_H
+#define _NET_RAWV6_H
+
+#ifdef __KERNEL__
+
+#define RAWV6_HTABLE_SIZE MAX_INET_PROTOS
+extern struct sock *raw_v6_htable[RAWV6_HTABLE_SIZE];
+
+
+extern struct sock *raw_v6_lookup(struct sock *sk, unsigned short num,
+ struct in6_addr *loc_addr, struct in6_addr *rmt_addr);
+
+extern int rawv6_rcv(struct sock *sk,
+ struct sk_buff *skb,
+ unsigned long len);
+
+
+extern void rawv6_err(struct sock *sk,
+ struct sk_buff *skb,
+ struct ipv6hdr *hdr,
+ struct inet6_skb_parm *opt,
+ int type, int code,
+ unsigned char *buff, u32 info);
+
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/net/rose.h b/pfinet/linux-src/include/net/rose.h
new file mode 100644
index 00000000..1c8684d2
--- /dev/null
+++ b/pfinet/linux-src/include/net/rose.h
@@ -0,0 +1,241 @@
+/*
+ * Declarations of Rose type objects.
+ *
+ * Jonathan Naylor G4KLX 25/8/96
+ */
+
+#ifndef _ROSE_H
+#define _ROSE_H
+#include <linux/rose.h>
+
+#define ROSE_ADDR_LEN 5
+
+#define ROSE_MIN_LEN 3
+
+#define ROSE_GFI 0x10
+#define ROSE_Q_BIT 0x80
+#define ROSE_D_BIT 0x40
+#define ROSE_M_BIT 0x10
+
+#define ROSE_CALL_REQUEST 0x0B
+#define ROSE_CALL_ACCEPTED 0x0F
+#define ROSE_CLEAR_REQUEST 0x13
+#define ROSE_CLEAR_CONFIRMATION 0x17
+#define ROSE_DATA 0x00
+#define ROSE_INTERRUPT 0x23
+#define ROSE_INTERRUPT_CONFIRMATION 0x27
+#define ROSE_RR 0x01
+#define ROSE_RNR 0x05
+#define ROSE_REJ 0x09
+#define ROSE_RESET_REQUEST 0x1B
+#define ROSE_RESET_CONFIRMATION 0x1F
+#define ROSE_REGISTRATION_REQUEST 0xF3
+#define ROSE_REGISTRATION_CONFIRMATION 0xF7
+#define ROSE_RESTART_REQUEST 0xFB
+#define ROSE_RESTART_CONFIRMATION 0xFF
+#define ROSE_DIAGNOSTIC 0xF1
+#define ROSE_ILLEGAL 0xFD
+
+/* Define Link State constants. */
+
+enum {
+ ROSE_STATE_0, /* Ready */
+ ROSE_STATE_1, /* Awaiting Call Accepted */
+ ROSE_STATE_2, /* Awaiting Clear Confirmation */
+ ROSE_STATE_3, /* Data Transfer */
+ ROSE_STATE_4, /* Awaiting Reset Confirmation */
+ ROSE_STATE_5 /* Deferred Call Acceptance */
+};
+
+#define ROSE_DEFAULT_T0 (180 * HZ) /* Default T10 T20 value */
+#define ROSE_DEFAULT_T1 (200 * HZ) /* Default T11 T21 value */
+#define ROSE_DEFAULT_T2 (180 * HZ) /* Default T12 T22 value */
+#define ROSE_DEFAULT_T3 (180 * HZ) /* Default T13 T23 value */
+#define ROSE_DEFAULT_HB (5 * HZ) /* Default Holdback value */
+#define ROSE_DEFAULT_IDLE (0 * 60 * HZ) /* No Activity Timeout - none */
+#define ROSE_DEFAULT_ROUTING 1 /* Default routing flag */
+#define ROSE_DEFAULT_FAIL_TIMEOUT (120 * HZ) /* Time until link considered usable */
+#define ROSE_DEFAULT_MAXVC 50 /* Maximum number of VCs per neighbour */
+#define ROSE_DEFAULT_WINDOW_SIZE 7 /* Default window size */
+
+#define ROSE_MODULUS 8
+#define ROSE_MAX_PACKET_SIZE 251 /* Maximum packet size */
+
+#define ROSE_COND_ACK_PENDING 0x01
+#define ROSE_COND_PEER_RX_BUSY 0x02
+#define ROSE_COND_OWN_RX_BUSY 0x04
+
+#define FAC_NATIONAL 0x00
+#define FAC_CCITT 0x0F
+
+#define FAC_NATIONAL_RAND 0x7F
+#define FAC_NATIONAL_FLAGS 0x3F
+#define FAC_NATIONAL_DEST_DIGI 0xE9
+#define FAC_NATIONAL_SRC_DIGI 0xEB
+#define FAC_NATIONAL_FAIL_CALL 0xED
+#define FAC_NATIONAL_FAIL_ADD 0xEE
+#define FAC_NATIONAL_DIGIS 0xEF
+
+#define FAC_CCITT_DEST_NSAP 0xC9
+#define FAC_CCITT_SRC_NSAP 0xCB
+
+struct rose_neigh {
+ struct rose_neigh *next;
+ ax25_address callsign;
+ ax25_digi *digipeat;
+ ax25_cb *ax25;
+ struct device *dev;
+ unsigned short count;
+ unsigned short use;
+ unsigned int number;
+ char restarted;
+ char dce_mode;
+ char loopback;
+ struct sk_buff_head queue;
+ struct timer_list t0timer;
+ struct timer_list ftimer;
+};
+
+struct rose_node {
+ struct rose_node *next;
+ rose_address address;
+ unsigned short mask;
+ unsigned char count;
+ char loopback;
+ struct rose_neigh *neighbour[3];
+};
+
+struct rose_route {
+ struct rose_route *next;
+ unsigned int lci1, lci2;
+ rose_address src_addr, dest_addr;
+ ax25_address src_call, dest_call;
+ struct rose_neigh *neigh1, *neigh2;
+ unsigned int rand;
+};
+
+typedef struct {
+ rose_address source_addr, dest_addr;
+ ax25_address source_call, dest_call;
+ unsigned char source_ndigis, dest_ndigis;
+ ax25_address source_digis[ROSE_MAX_DIGIS];
+ ax25_address dest_digis[ROSE_MAX_DIGIS];
+ struct rose_neigh *neighbour;
+ struct device *device;
+ unsigned int lci, rand;
+ unsigned char state, condition, qbitincl, defer;
+ unsigned char cause, diagnostic;
+ unsigned short vs, vr, va, vl;
+ unsigned long t1, t2, t3, hb, idle;
+#ifdef M_BIT
+ unsigned short fraglen;
+ struct sk_buff_head frag_queue;
+#endif
+ struct sk_buff_head ack_queue;
+ struct rose_facilities_struct facilities;
+ struct timer_list timer;
+ struct timer_list idletimer;
+ struct sock *sk; /* Backlink to socket */
+} rose_cb;
+
+/* af_rose.c */
+extern ax25_address rose_callsign;
+extern int sysctl_rose_restart_request_timeout;
+extern int sysctl_rose_call_request_timeout;
+extern int sysctl_rose_reset_request_timeout;
+extern int sysctl_rose_clear_request_timeout;
+extern int sysctl_rose_no_activity_timeout;
+extern int sysctl_rose_ack_hold_back_timeout;
+extern int sysctl_rose_routing_control;
+extern int sysctl_rose_link_fail_timeout;
+extern int sysctl_rose_maximum_vcs;
+extern int sysctl_rose_window_size;
+extern int rosecmp(rose_address *, rose_address *);
+extern int rosecmpm(rose_address *, rose_address *, unsigned short);
+extern char *rose2asc(rose_address *);
+extern struct sock *rose_find_socket(unsigned int, struct rose_neigh *);
+extern void rose_kill_by_neigh(struct rose_neigh *);
+extern unsigned int rose_new_lci(struct rose_neigh *);
+extern int rose_rx_call_request(struct sk_buff *, struct device *, struct rose_neigh *, unsigned int);
+extern void rose_destroy_socket(struct sock *);
+
+/* rose_dev.c */
+extern int rose_rx_ip(struct sk_buff *, struct device *);
+extern int rose_init(struct device *);
+
+#include <net/rosecall.h>
+
+/* rose_in.c */
+extern int rose_process_rx_frame(struct sock *, struct sk_buff *);
+
+/* rose_link.c */
+extern void rose_start_ftimer(struct rose_neigh *);
+extern void rose_start_t0timer(struct rose_neigh *);
+extern void rose_stop_ftimer(struct rose_neigh *);
+extern void rose_stop_t0timer(struct rose_neigh *);
+extern int rose_ftimer_running(struct rose_neigh *);
+extern int rose_t0timer_running(struct rose_neigh *);
+extern void rose_link_rx_restart(struct sk_buff *, struct rose_neigh *, unsigned short);
+extern void rose_transmit_restart_request(struct rose_neigh *);
+extern void rose_transmit_restart_confirmation(struct rose_neigh *);
+extern void rose_transmit_diagnostic(struct rose_neigh *, unsigned char);
+extern void rose_transmit_clear_request(struct rose_neigh *, unsigned int, unsigned char, unsigned char);
+extern void rose_transmit_link(struct sk_buff *, struct rose_neigh *);
+
+/* rose_loopback.c */
+extern void rose_loopback_init(void);
+extern void rose_loopback_clear(void);
+extern int rose_loopback_queue(struct sk_buff *, struct rose_neigh *);
+
+/* rose_out.c */
+extern void rose_kick(struct sock *);
+extern void rose_enquiry_response(struct sock *);
+
+/* rose_route.c */
+extern struct rose_neigh *rose_loopback_neigh;
+
+extern int rose_add_loopback_neigh(void);
+extern int rose_add_loopback_node(rose_address *);
+extern void rose_del_loopback_node(rose_address *);
+extern void rose_rt_device_down(struct device *);
+extern void rose_link_device_down(struct device *);
+extern struct device *rose_dev_first(void);
+extern struct device *rose_dev_get(rose_address *);
+extern struct rose_route *rose_route_free_lci(unsigned int, struct rose_neigh *);
+extern struct device *rose_ax25_dev_get(char *);
+extern struct rose_neigh *rose_get_neigh(rose_address *, unsigned char *, unsigned char *);
+extern int rose_rt_ioctl(unsigned int, void *);
+extern void rose_link_failed(ax25_cb *, int);
+extern int rose_route_frame(struct sk_buff *, ax25_cb *);
+extern int rose_nodes_get_info(char *, char **, off_t, int, int);
+extern int rose_neigh_get_info(char *, char **, off_t, int, int);
+extern int rose_routes_get_info(char *, char **, off_t, int, int);
+extern void rose_rt_free(void);
+
+/* rose_subr.c */
+extern void rose_clear_queues(struct sock *);
+extern void rose_frames_acked(struct sock *, unsigned short);
+extern void rose_requeue_frames(struct sock *);
+extern int rose_validate_nr(struct sock *, unsigned short);
+extern void rose_write_internal(struct sock *, int);
+extern int rose_decode(struct sk_buff *, int *, int *, int *, int *, int *);
+extern int rose_parse_facilities(unsigned char *, struct rose_facilities_struct *);
+extern int rose_create_facilities(unsigned char *, rose_cb *);
+extern void rose_disconnect(struct sock *, int, int, int);
+
+/* rose_timer.c */
+extern void rose_start_heartbeat(struct sock *);
+extern void rose_start_t1timer(struct sock *);
+extern void rose_start_t2timer(struct sock *);
+extern void rose_start_t3timer(struct sock *);
+extern void rose_start_hbtimer(struct sock *);
+extern void rose_start_idletimer(struct sock *);
+extern void rose_stop_heartbeat(struct sock *);
+extern void rose_stop_timer(struct sock *);
+extern void rose_stop_idletimer(struct sock *);
+
+/* sysctl_net_rose.c */
+extern void rose_register_sysctl(void);
+extern void rose_unregister_sysctl(void);
+
+#endif
diff --git a/pfinet/linux-src/include/net/rosecall.h b/pfinet/linux-src/include/net/rosecall.h
new file mode 100644
index 00000000..5bbe69cc
--- /dev/null
+++ b/pfinet/linux-src/include/net/rosecall.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void rose_proto_init(struct net_proto *pro);
diff --git a/pfinet/linux-src/include/net/route.h b/pfinet/linux-src/include/net/route.h
new file mode 100644
index 00000000..66df4712
--- /dev/null
+++ b/pfinet/linux-src/include/net/route.h
@@ -0,0 +1,153 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the IP router.
+ *
+ * Version: @(#)route.h 1.0.4 05/27/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Fixes:
+ * Alan Cox : Reformatted. Added ip_rt_local()
+ * Alan Cox : Support for TCP parameters.
+ * Alexey Kuznetsov: Major changes for new routing code.
+ * Mike McLagan : Routing by source
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ROUTE_H
+#define _ROUTE_H
+
+#include <linux/config.h>
+#include <net/dst.h>
+#include <linux/in_route.h>
+#include <linux/rtnetlink.h>
+#include <linux/route.h>
+
+#ifndef __KERNEL__
+#warning This file is not supposed to be used outside of kernel.
+#endif
+
+#define RT_HASH_DIVISOR 256
+
+/*
+ * Prevents LRU trashing, entries considered equivalent,
+ * if the difference between last use times is less then this number.
+ */
+#define RT_CACHE_BUBBLE_THRESHOLD (5*HZ)
+
+
+#define RTO_ONLINK 0x01
+#define RTO_TPROXY 0x80000000
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+#define RTO_CONN RTO_TPROXY
+#else
+#define RTO_CONN 0
+#endif
+
+struct rt_key
+{
+ __u32 dst;
+ __u32 src;
+ int iif;
+ int oif;
+#ifdef CONFIG_IP_ROUTE_FWMARK
+ __u32 fwmark;
+#endif
+ __u8 tos;
+ __u8 scope;
+};
+
+struct rtable
+{
+ union
+ {
+ struct dst_entry dst;
+ struct rtable *rt_next;
+ } u;
+
+ unsigned rt_flags;
+ unsigned rt_type;
+
+ __u32 rt_dst; /* Path destination */
+ __u32 rt_src; /* Path source */
+ int rt_iif;
+
+ /* Info on neighbour */
+ __u32 rt_gateway;
+
+ /* Cache lookup keys */
+ struct rt_key key;
+
+ /* Miscellaneous cached information */
+ __u32 rt_spec_dst; /* RFC1122 specific destination */
+
+#ifdef CONFIG_IP_ROUTE_NAT
+ __u32 rt_src_map;
+ __u32 rt_dst_map;
+#endif
+};
+
+extern struct rtable *rt_hash_table[RT_HASH_DIVISOR];
+
+struct ip_rt_acct
+{
+ __u32 o_bytes;
+ __u32 o_packets;
+ __u32 i_bytes;
+ __u32 i_packets;
+};
+
+extern struct ip_rt_acct ip_rt_acct[256];
+
+extern void ip_rt_init(void);
+extern void ip_rt_redirect(u32 old_gw, u32 dst, u32 new_gw,
+ u32 src, u8 tos, struct device *dev);
+extern void ip_rt_advice(struct rtable **rp, int advice);
+extern void rt_cache_flush(int how);
+extern int ip_route_output(struct rtable **, u32 dst, u32 src, u32 tos, int oif);
+extern int ip_route_input(struct sk_buff*, u32 dst, u32 src, u8 tos, struct device *devin);
+extern unsigned short ip_rt_frag_needed(struct iphdr *iph, unsigned short new_mtu);
+extern void ip_rt_update_pmtu(struct dst_entry *dst, unsigned mtu);
+extern void ip_rt_send_redirect(struct sk_buff *skb);
+
+extern unsigned inet_addr_type(u32 addr);
+extern void ip_rt_multicast_event(struct in_device *);
+extern int ip_rt_ioctl(unsigned int cmd, void *arg);
+extern void ip_rt_get_source(u8 *src, struct rtable *rt);
+extern int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb);
+
+
+extern __inline__ void ip_rt_put(struct rtable * rt)
+{
+ if (rt)
+ dst_release(&rt->u.dst);
+}
+
+extern __u8 ip_tos2prio[16];
+
+extern __inline__ char rt_tos2priority(u8 tos)
+{
+ return ip_tos2prio[IPTOS_TOS(tos)>>1];
+}
+
+extern __inline__ int ip_route_connect(struct rtable **rp, u32 dst, u32 src, u32 tos, int oif)
+{
+ int err;
+ err = ip_route_output(rp, dst, src, tos, oif);
+ if (err || (dst && src))
+ return err;
+ dst = (*rp)->rt_dst;
+ src = (*rp)->rt_src;
+ ip_rt_put(*rp);
+ *rp = NULL;
+ return ip_route_output(rp, dst, src, tos, oif);
+}
+
+#endif /* _ROUTE_H */
diff --git a/pfinet/linux-src/include/net/scm.h b/pfinet/linux-src/include/net/scm.h
new file mode 100644
index 00000000..f9feff54
--- /dev/null
+++ b/pfinet/linux-src/include/net/scm.h
@@ -0,0 +1,66 @@
+#ifndef __LINUX_NET_SCM_H
+#define __LINUX_NET_SCM_H
+
+/* Well, we should have at least one descriptor open
+ * to accept passed FDs 8)
+ */
+#define SCM_MAX_FD (OPEN_MAX-1)
+
+struct scm_fp_list
+{
+ int count;
+ struct file *fp[SCM_MAX_FD];
+};
+
+struct scm_cookie
+{
+ struct ucred creds; /* Skb credentials */
+ struct scm_fp_list *fp; /* Passed files */
+ unsigned long seq; /* Connection seqno */
+};
+
+extern void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm);
+extern int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm);
+extern void __scm_destroy(struct scm_cookie *scm);
+extern struct scm_fp_list * scm_fp_dup(struct scm_fp_list *fpl);
+
+static __inline__ void scm_destroy(struct scm_cookie *scm)
+{
+ if (scm && scm->fp)
+ __scm_destroy(scm);
+}
+
+static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
+ struct scm_cookie *scm)
+{
+ memset(scm, 0, sizeof(*scm));
+ scm->creds.uid = current->uid;
+ scm->creds.gid = current->gid;
+ scm->creds.pid = current->pid;
+ if (msg->msg_controllen <= 0)
+ return 0;
+ return __scm_send(sock, msg, scm);
+}
+
+static __inline__ void scm_recv(struct socket *sock, struct msghdr *msg,
+ struct scm_cookie *scm, int flags)
+{
+ if (!msg->msg_control)
+ {
+ if (sock->passcred || scm->fp)
+ msg->msg_flags |= MSG_CTRUNC;
+ scm_destroy(scm);
+ return;
+ }
+
+ if (sock->passcred)
+ put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(scm->creds), &scm->creds);
+
+ if (!scm->fp)
+ return;
+
+ scm_detach_fds(msg, scm);
+}
+
+
+#endif __LINUX_NET_SCM_H
diff --git a/pfinet/linux-src/include/net/slhc.h b/pfinet/linux-src/include/net/slhc.h
new file mode 100644
index 00000000..c7b39db5
--- /dev/null
+++ b/pfinet/linux-src/include/net/slhc.h
@@ -0,0 +1,6 @@
+#ifndef __NET_SLHC_H
+#define __NET_SLHC_H
+
+extern void slhc_install(void);
+
+#endif
diff --git a/pfinet/linux-src/include/net/slhc_vj.h b/pfinet/linux-src/include/net/slhc_vj.h
new file mode 100644
index 00000000..04387d8a
--- /dev/null
+++ b/pfinet/linux-src/include/net/slhc_vj.h
@@ -0,0 +1,187 @@
+#ifndef _SLHC_H
+#define _SLHC_H
+/*
+ * Definitions for tcp compression routines.
+ *
+ * $Header: slcompress.h,v 1.10 89/12/31 08:53:02 van Exp $
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ *
+ *
+ * modified for KA9Q Internet Software Package by
+ * Katie Stevens (dkstevens@ucdavis.edu)
+ * University of California, Davis
+ * Computing Services
+ * - 01-31-90 initial adaptation
+ *
+ * - Feb 1991 Bill_Simpson@um.cc.umich.edu
+ * variable number of conversation slots
+ * allow zero or one slots
+ * separate routines
+ * status display
+ */
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits). The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet. The next two octets are the TCP checksum
+ * from the original datagram. The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ *
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowledgment, sequence number and IP ID. (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.) Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0. (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type. There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows. Top
+ * three bits are actual packet type. For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* SLIP compression masks for len/vers byte */
+#define SL_TYPE_IP 0x40
+#define SL_TYPE_UNCOMPRESSED_TCP 0x70
+#define SL_TYPE_COMPRESSED_TCP 0x80
+#define SL_TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C 0x40 /* flag bits for what changed in a packet */
+#define NEW_I 0x20
+#define NEW_S 0x08
+#define NEW_A 0x04
+#define NEW_W 0x02
+#define NEW_U 0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+/*
+ * data type and sizes conversion assumptions:
+ *
+ * VJ code KA9Q style generic
+ * u_char byte_t unsigned char 8 bits
+ * u_short int16 unsigned short 16 bits
+ * u_int int16 unsigned short 16 bits
+ * u_long unsigned long unsigned long 32 bits
+ * int int32 long 32 bits
+ */
+
+typedef __u8 byte_t;
+typedef __u32 int32;
+
+/*
+ * "state" data for each active tcp conversation on the wire. This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+ byte_t cs_this; /* connection id number (xmit) */
+ struct cstate *next; /* next in ring (xmit) */
+ struct iphdr cs_ip; /* ip/tcp hdr from most recent packet */
+ struct tcphdr cs_tcp;
+ unsigned char cs_ipopt[64];
+ unsigned char cs_tcpopt[64];
+ int cs_hsize;
+};
+#define NULLSLSTATE (struct cstate *)0
+
+/*
+ * all the state data for one serial line (we need one of these per line).
+ */
+struct slcompress {
+ struct cstate *tstate; /* transmit connection states (array)*/
+ struct cstate *rstate; /* receive connection states (array)*/
+
+ byte_t tslot_limit; /* highest transmit slot id (0-l)*/
+ byte_t rslot_limit; /* highest receive slot id (0-l)*/
+
+ byte_t xmit_oldest; /* oldest xmit in ring */
+ byte_t xmit_current; /* most recent xmit id */
+ byte_t recv_current; /* most recent rcvd id */
+
+ byte_t flags;
+#define SLF_TOSS 0x01 /* tossing rcvd frames until id received */
+
+ int32 sls_o_nontcp; /* outbound non-TCP packets */
+ int32 sls_o_tcp; /* outbound TCP packets */
+ int32 sls_o_uncompressed; /* outbound uncompressed packets */
+ int32 sls_o_compressed; /* outbound compressed packets */
+ int32 sls_o_searches; /* searches for connection state */
+ int32 sls_o_misses; /* times couldn't find conn. state */
+
+ int32 sls_i_uncompressed; /* inbound uncompressed packets */
+ int32 sls_i_compressed; /* inbound compressed packets */
+ int32 sls_i_error; /* inbound error packets */
+ int32 sls_i_tossed; /* inbound packets tossed because of error */
+
+ int32 sls_i_runt;
+ int32 sls_i_badcheck;
+};
+#define NULLSLCOMPR (struct slcompress *)0
+
+#define __ARGS(x) x
+
+/* In slhc.c: */
+struct slcompress *slhc_init __ARGS((int rslots, int tslots));
+void slhc_free __ARGS((struct slcompress *comp));
+
+int slhc_compress __ARGS((struct slcompress *comp, unsigned char *icp,
+ int isize, unsigned char *ocp, unsigned char **cpp,
+ int compress_cid));
+int slhc_uncompress __ARGS((struct slcompress *comp, unsigned char *icp,
+ int isize));
+int slhc_remember __ARGS((struct slcompress *comp, unsigned char *icp,
+ int isize));
+int slhc_toss __ARGS((struct slcompress *comp));
+
+void slhc_i_status __ARGS((struct slcompress *comp));
+void slhc_o_status __ARGS((struct slcompress *comp));
+
+#endif /* _SLHC_H */
diff --git a/pfinet/linux-src/include/net/snmp.h b/pfinet/linux-src/include/net/snmp.h
new file mode 100644
index 00000000..cc8354b6
--- /dev/null
+++ b/pfinet/linux-src/include/net/snmp.h
@@ -0,0 +1,183 @@
+/*
+ *
+ * SNMP MIB entries for the IP subsystem.
+ *
+ * Alan Cox <gw4pts@gw4pts.ampr.org>
+ *
+ * We don't chose to implement SNMP in the kernel (this would
+ * be silly as SNMP is a pain in the backside in places). We do
+ * however need to collect the MIB statistics and export them
+ * out of /proc (eventually)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef _SNMP_H
+#define _SNMP_H
+
+/*
+ * We use all unsigned longs. Linux will soon be so reliable that even these
+ * will rapidly get too small 8-). Seriously consider the IpInReceives count
+ * on the 20Gb/s + networks people expect in a few years time!
+ */
+
+struct ip_mib
+{
+ unsigned long IpForwarding;
+ unsigned long IpDefaultTTL;
+ unsigned long IpInReceives;
+ unsigned long IpInHdrErrors;
+ unsigned long IpInAddrErrors;
+ unsigned long IpForwDatagrams;
+ unsigned long IpInUnknownProtos;
+ unsigned long IpInDiscards;
+ unsigned long IpInDelivers;
+ unsigned long IpOutRequests;
+ unsigned long IpOutDiscards;
+ unsigned long IpOutNoRoutes;
+ unsigned long IpReasmTimeout;
+ unsigned long IpReasmReqds;
+ unsigned long IpReasmOKs;
+ unsigned long IpReasmFails;
+ unsigned long IpFragOKs;
+ unsigned long IpFragFails;
+ unsigned long IpFragCreates;
+};
+
+struct ipv6_mib
+{
+ unsigned long Ip6InReceives;
+ unsigned long Ip6InHdrErrors;
+ unsigned long Ip6InTooBigErrors;
+ unsigned long Ip6InNoRoutes;
+ unsigned long Ip6InAddrErrors;
+ unsigned long Ip6InUnknownProtos;
+ unsigned long Ip6InTruncatedPkts;
+ unsigned long Ip6InDiscards;
+ unsigned long Ip6InDelivers;
+ unsigned long Ip6OutForwDatagrams;
+ unsigned long Ip6OutRequests;
+ unsigned long Ip6OutDiscards;
+ unsigned long Ip6OutNoRoutes;
+ unsigned long Ip6ReasmTimeout;
+ unsigned long Ip6ReasmReqds;
+ unsigned long Ip6ReasmOKs;
+ unsigned long Ip6ReasmFails;
+ unsigned long Ip6FragOKs;
+ unsigned long Ip6FragFails;
+ unsigned long Ip6FragCreates;
+ unsigned long Ip6InMcastPkts;
+ unsigned long Ip6OutMcastPkts;
+};
+
+struct icmp_mib
+{
+ unsigned long IcmpInMsgs;
+ unsigned long IcmpInErrors;
+ unsigned long IcmpInDestUnreachs;
+ unsigned long IcmpInTimeExcds;
+ unsigned long IcmpInParmProbs;
+ unsigned long IcmpInSrcQuenchs;
+ unsigned long IcmpInRedirects;
+ unsigned long IcmpInEchos;
+ unsigned long IcmpInEchoReps;
+ unsigned long IcmpInTimestamps;
+ unsigned long IcmpInTimestampReps;
+ unsigned long IcmpInAddrMasks;
+ unsigned long IcmpInAddrMaskReps;
+ unsigned long IcmpOutMsgs;
+ unsigned long IcmpOutErrors;
+ unsigned long IcmpOutDestUnreachs;
+ unsigned long IcmpOutTimeExcds;
+ unsigned long IcmpOutParmProbs;
+ unsigned long IcmpOutSrcQuenchs;
+ unsigned long IcmpOutRedirects;
+ unsigned long IcmpOutEchos;
+ unsigned long IcmpOutEchoReps;
+ unsigned long IcmpOutTimestamps;
+ unsigned long IcmpOutTimestampReps;
+ unsigned long IcmpOutAddrMasks;
+ unsigned long IcmpOutAddrMaskReps;
+};
+
+struct icmpv6_mib
+{
+ unsigned long Icmp6InMsgs;
+ unsigned long Icmp6InErrors;
+
+ unsigned long Icmp6InDestUnreachs;
+ unsigned long Icmp6InPktTooBigs;
+ unsigned long Icmp6InTimeExcds;
+ unsigned long Icmp6InParmProblems;
+
+ unsigned long Icmp6InEchos;
+ unsigned long Icmp6InEchoReplies;
+ unsigned long Icmp6InGroupMembQueries;
+ unsigned long Icmp6InGroupMembResponses;
+ unsigned long Icmp6InGroupMembReductions;
+ unsigned long Icmp6InRouterSolicits;
+ unsigned long Icmp6InRouterAdvertisements;
+ unsigned long Icmp6InNeighborSolicits;
+ unsigned long Icmp6InNeighborAdvertisements;
+ unsigned long Icmp6InRedirects;
+
+ unsigned long Icmp6OutMsgs;
+
+ unsigned long Icmp6OutDestUnreachs;
+ unsigned long Icmp6OutPktTooBigs;
+ unsigned long Icmp6OutTimeExcds;
+ unsigned long Icmp6OutParmProblems;
+
+ unsigned long Icmp6OutEchoReplies;
+ unsigned long Icmp6OutRouterSolicits;
+ unsigned long Icmp6OutNeighborSolicits;
+ unsigned long Icmp6OutNeighborAdvertisements;
+ unsigned long Icmp6OutRedirects;
+ unsigned long Icmp6OutGroupMembResponses;
+ unsigned long Icmp6OutGroupMembReductions;
+};
+
+struct tcp_mib
+{
+ unsigned long TcpRtoAlgorithm;
+ unsigned long TcpRtoMin;
+ unsigned long TcpRtoMax;
+ unsigned long TcpMaxConn;
+ unsigned long TcpActiveOpens;
+ unsigned long TcpPassiveOpens;
+ unsigned long TcpAttemptFails;
+ unsigned long TcpEstabResets;
+ unsigned long TcpCurrEstab;
+ unsigned long TcpInSegs;
+ unsigned long TcpOutSegs;
+ unsigned long TcpRetransSegs;
+ unsigned long TcpInErrs;
+ unsigned long TcpOutRsts;
+};
+
+struct udp_mib
+{
+ unsigned long UdpInDatagrams;
+ unsigned long UdpNoPorts;
+ unsigned long UdpInErrors;
+ unsigned long UdpOutDatagrams;
+};
+
+struct linux_mib
+{
+ unsigned long SyncookiesSent;
+ unsigned long SyncookiesRecv;
+ unsigned long SyncookiesFailed;
+ unsigned long EmbryonicRsts;
+ unsigned long PruneCalled;
+ unsigned long RcvPruned;
+ unsigned long OfoPruned;
+ unsigned long OutOfWindowIcmps;
+ unsigned long LockDroppedIcmps;
+};
+
+#endif
diff --git a/pfinet/linux-src/include/net/sock.h b/pfinet/linux-src/include/net/sock.h
new file mode 100644
index 00000000..96fdfe8b
--- /dev/null
+++ b/pfinet/linux-src/include/net/sock.h
@@ -0,0 +1,951 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the AF_INET socket handler.
+ *
+ * Version: @(#)sock.h 1.0.4 05/13/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Corey Minyard <wf-rch!minyard@relay.EU.net>
+ * Florian La Roche <flla@stud.uni-sb.de>
+ *
+ * Fixes:
+ * Alan Cox : Volatiles in skbuff pointers. See
+ * skbuff comments. May be overdone,
+ * better to prove they can be removed
+ * than the reverse.
+ * Alan Cox : Added a zapped field for tcp to note
+ * a socket is reset and must stay shut up
+ * Alan Cox : New fields for options
+ * Pauline Middelink : identd support
+ * Alan Cox : Eliminate low level recv/recvfrom
+ * David S. Miller : New socket lookup architecture.
+ * Steve Whitehouse: Default routines for sock_ops
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _SOCK_H
+#define _SOCK_H
+
+#include <linux/config.h>
+#include <linux/timer.h>
+#include <linux/in.h> /* struct sockaddr_in */
+
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+#include <linux/in6.h> /* struct sockaddr_in6 */
+#include <linux/ipv6.h> /* dest_cache, inet6_options */
+#include <linux/icmpv6.h>
+#include <net/if_inet6.h> /* struct ipv6_mc_socklist */
+#endif
+
+#if defined(CONFIG_INET) || defined (CONFIG_INET_MODULE)
+#include <linux/icmp.h>
+#endif
+#include <linux/tcp.h> /* struct tcphdr */
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h> /* struct sk_buff */
+#include <net/protocol.h> /* struct inet_protocol */
+#if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE)
+#include <net/x25.h>
+#endif
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <net/ax25.h>
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+#include <net/netrom.h>
+#endif
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <net/rose.h>
+#endif
+#endif
+
+#if defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE)
+#if defined(CONFIG_SPX) || defined(CONFIG_SPX_MODULE)
+#include <net/spx.h>
+#else
+#include <net/ipx.h>
+#endif /* CONFIG_SPX */
+#endif /* CONFIG_IPX */
+
+#if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE)
+#include <linux/atalk.h>
+#endif
+
+#if defined(CONFIG_DECNET) || defined(CONFIG_DECNET_MODULE)
+#include <net/dn.h>
+#endif
+
+#if defined(CONFIG_IRDA) || defined(CONFIG_IRDA_MODULE)
+#include <net/irda/irda.h>
+#endif
+
+#ifdef CONFIG_FILTER
+#include <linux/filter.h>
+#endif
+
+#include <asm/atomic.h>
+
+#define MIN_WRITE_SPACE 2048
+
+/* The AF_UNIX specific socket options */
+struct unix_opt {
+ int family;
+ char * name;
+ int locks;
+ struct unix_address *addr;
+ struct dentry * dentry;
+ struct semaphore readsem;
+ struct sock * other;
+ struct sock ** list;
+ struct sock * gc_tree;
+ int inflight;
+};
+
+#ifdef CONFIG_NETLINK
+struct netlink_callback;
+
+struct netlink_opt {
+ pid_t pid;
+ unsigned groups;
+ pid_t dst_pid;
+ unsigned dst_groups;
+ int (*handler)(int unit, struct sk_buff *skb);
+ atomic_t locks;
+ struct netlink_callback *cb;
+};
+#endif
+
+/* Once the IPX ncpd patches are in these are going into protinfo. */
+#if defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE)
+struct ipx_opt {
+ ipx_address dest_addr;
+ ipx_interface *intrfc;
+ unsigned short port;
+#ifdef CONFIG_IPX_INTERN
+ unsigned char node[IPX_NODE_LEN];
+#endif
+ unsigned short type;
+/*
+ * To handle special ncp connection-handling sockets for mars_nwe,
+ * the connection number must be stored in the socket.
+ */
+ unsigned short ipx_ncp_conn;
+};
+#endif
+
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+struct ipv6_pinfo {
+ struct in6_addr saddr;
+ struct in6_addr rcv_saddr;
+ struct in6_addr daddr;
+ struct in6_addr *daddr_cache;
+
+ __u32 flow_label;
+ __u32 frag_size;
+ int hop_limit;
+ int mcast_hops;
+ int mcast_oif;
+
+ /* pktoption flags */
+ union {
+ struct {
+ __u8 srcrt:2,
+ rxinfo:1,
+ rxhlim:1,
+ hopopts:1,
+ dstopts:1,
+ authhdr:1,
+ rxflow:1;
+ } bits;
+ __u8 all;
+ } rxopt;
+
+ /* sockopt flags */
+ __u8 mc_loop:1,
+ recverr:1,
+ sndflow:1,
+ pmtudisc:2,
+ ipv6only:1;
+
+ struct ipv6_mc_socklist *ipv6_mc_list;
+ struct ipv6_fl_socklist *ipv6_fl_list;
+ __u32 dst_cookie;
+
+ struct ipv6_txoptions *opt;
+ struct sk_buff *pktoptions;
+};
+
+struct raw6_opt {
+ __u32 checksum; /* perform checksum */
+ __u32 offset; /* checksum offset */
+
+ struct icmp6_filter filter;
+};
+
+#endif /* IPV6 */
+
+#if defined(CONFIG_INET) || defined(CONFIG_INET_MODULE)
+struct raw_opt {
+ struct icmp_filter filter;
+};
+#endif
+
+/* This defines a selective acknowledgement block. */
+struct tcp_sack_block {
+ __u32 start_seq;
+ __u32 end_seq;
+};
+
+struct tcp_opt {
+ int tcp_header_len; /* Bytes of tcp header to send */
+
+/*
+ * Header prediction flags
+ * 0x5?10 << 16 + snd_wnd in net byte order
+ */
+ __u32 pred_flags;
+
+/*
+ * RFC793 variables by their proper names. This means you can
+ * read the code and the spec side by side (and laugh ...)
+ * See RFC793 and RFC1122. The RFC writes these in capitals.
+ */
+ __u32 rcv_nxt; /* What we want to receive next */
+ __u32 snd_nxt; /* Next sequence we send */
+
+ __u32 snd_una; /* First byte we want an ack for */
+ __u32 rcv_tstamp; /* timestamp of last received packet */
+ __u32 lrcvtime; /* timestamp of last received data packet*/
+ __u32 srtt; /* smothed round trip time << 3 */
+
+ __u32 ato; /* delayed ack timeout */
+ __u32 snd_wl1; /* Sequence for window update */
+
+ __u32 snd_wl2; /* Ack sequence for update */
+ __u32 snd_wnd; /* The window we expect to receive */
+ __u32 max_window;
+ __u32 pmtu_cookie; /* Last pmtu seen by socket */
+ __u16 mss_cache; /* Cached effective mss, not including SACKS */
+ __u16 mss_clamp; /* Maximal mss, negotiated at connection setup */
+ __u16 ext_header_len; /* Dave, do you allow mw to use this hole? 8) --ANK */
+ __u8 pending; /* pending events */
+ __u8 retransmits;
+ __u32 last_ack_sent; /* last ack we sent */
+
+ __u32 backoff; /* backoff */
+ __u32 mdev; /* medium deviation */
+ __u32 snd_cwnd; /* Sending congestion window */
+ __u32 rto; /* retransmit timeout */
+
+ __u32 packets_out; /* Packets which are "in flight" */
+ __u32 fackets_out; /* Non-retrans SACK'd packets */
+ __u32 retrans_out; /* Fast-retransmitted packets out */
+ __u32 high_seq; /* snd_nxt at onset of congestion */
+/*
+ * Slow start and congestion control (see also Nagle, and Karn & Partridge)
+ */
+ __u32 snd_ssthresh; /* Slow start size threshold */
+ __u16 snd_cwnd_cnt; /* Linear increase counter */
+ __u8 dup_acks; /* Consequetive duplicate acks seen from other end */
+ __u8 delayed_acks;
+ __u16 user_mss; /* mss requested by user in ioctl */
+
+ /* Two commonly used timers in both sender and receiver paths. */
+ struct timer_list retransmit_timer; /* Resend (no ack) */
+ struct timer_list delack_timer; /* Ack delay */
+
+ struct sk_buff_head out_of_order_queue; /* Out of order segments go here */
+
+ struct tcp_func *af_specific; /* Operations which are AF_INET{4,6} specific */
+ struct sk_buff *send_head; /* Front of stuff to transmit */
+ struct sk_buff *retrans_head; /* retrans head can be
+ * different to the head of
+ * write queue if we are doing
+ * fast retransmit
+ */
+
+ __u32 rcv_wnd; /* Current receiver window */
+ __u32 rcv_wup; /* rcv_nxt on last window update sent */
+ __u32 write_seq;
+ __u32 copied_seq;
+/*
+ * Options received (usually on last packet, some only on SYN packets).
+ */
+ char tstamp_ok, /* TIMESTAMP seen on SYN packet */
+ wscale_ok, /* Wscale seen on SYN packet */
+ sack_ok; /* SACK seen on SYN packet */
+ char saw_tstamp; /* Saw TIMESTAMP on last packet */
+ __u8 snd_wscale; /* Window scaling received from sender */
+ __u8 rcv_wscale; /* Window scaling to send to receiver */
+ __u8 rexmt_done; /* Retransmitted up to send head? */
+ __u32 rcv_tsval; /* Time stamp value */
+ __u32 rcv_tsecr; /* Time stamp echo reply */
+ __u32 ts_recent; /* Time stamp to echo next */
+ __u32 ts_recent_stamp;/* Time we stored ts_recent (for aging) */
+ int num_sacks; /* Number of SACK blocks */
+ struct tcp_sack_block selective_acks[4]; /* The SACKS themselves*/
+
+ struct timer_list probe_timer; /* Probes */
+ __u32 window_clamp; /* XXX Document this... -DaveM */
+ __u32 probes_out; /* unanswered 0 window probes */
+ __u32 syn_seq;
+ __u32 fin_seq;
+ __u32 urg_seq;
+ __u32 urg_data;
+
+ __u32 last_seg_size; /* Size of last incoming segment */
+ __u32 rcv_mss; /* MSS used for delayed ACK decisions */
+ __u32 partial_writers; /* # of clients wanting at the head packet */
+
+ struct open_request *syn_wait_queue;
+ struct open_request **syn_wait_last;
+
+ int syn_backlog; /* Backlog of received SYNs */
+};
+
+
+/*
+ * This structure really needs to be cleaned up.
+ * Most of it is for TCP, and not used by any of
+ * the other protocols.
+ */
+
+/*
+ * The idea is to start moving to a newer struct gradualy
+ *
+ * IMHO the newer struct should have the following format:
+ *
+ * struct sock {
+ * sockmem [mem, proto, callbacks]
+ *
+ * union or struct {
+ * ax25;
+ * } ll_pinfo;
+ *
+ * union {
+ * ipv4;
+ * ipv6;
+ * ipx;
+ * netrom;
+ * rose;
+ * x25;
+ * } net_pinfo;
+ *
+ * union {
+ * tcp;
+ * udp;
+ * spx;
+ * netrom;
+ * } tp_pinfo;
+ *
+ * }
+ */
+
+/* Define this to get the sk->debug debugging facility. */
+#define SOCK_DEBUGGING
+#ifdef SOCK_DEBUGGING
+#define SOCK_DEBUG(sk, msg...) do { if((sk) && ((sk)->debug)) printk(KERN_DEBUG msg); } while (0)
+#else
+#define SOCK_DEBUG(sk, msg...) do { } while (0)
+#endif
+
+struct sock {
+ /* This must be first. */
+ struct sock *sklist_next;
+ struct sock *sklist_prev;
+
+ /* Local port binding hash linkage. */
+ struct sock *bind_next;
+ struct sock **bind_pprev;
+
+ /* Socket demultiplex comparisons on incoming packets. */
+ __u32 daddr; /* Foreign IPv4 addr */
+ __u32 rcv_saddr; /* Bound local IPv4 addr */
+ __u16 dport; /* Destination port */
+ unsigned short num; /* Local port */
+ int bound_dev_if; /* Bound device index if != 0 */
+
+ /* Main hash linkage for various protocol lookup tables. */
+ struct sock *next;
+ struct sock **pprev;
+
+ volatile unsigned char state, /* Connection state */
+ zapped; /* In ax25 & ipx means not linked */
+ __u16 sport; /* Source port */
+
+ unsigned short family; /* Address family */
+ unsigned char reuse, /* SO_REUSEADDR setting */
+ nonagle; /* Disable Nagle algorithm? */
+
+ atomic_t sock_readers; /* User count */
+ int rcvbuf; /* Size of receive buffer in bytes */
+
+ struct wait_queue **sleep; /* Sock wait queue */
+ struct dst_entry *dst_cache; /* Destination cache */
+ atomic_t rmem_alloc; /* Receive queue bytes committed */
+ struct sk_buff_head receive_queue; /* Incoming packets */
+ atomic_t wmem_alloc; /* Transmit queue bytes committed */
+ struct sk_buff_head write_queue; /* Packet sending queue */
+ atomic_t omem_alloc; /* "o" is "option" or "other" */
+ __u32 saddr; /* Sending source */
+ unsigned int allocation; /* Allocation mode */
+ int sndbuf; /* Size of send buffer in bytes */
+ struct sock *prev;
+
+ /* Not all are volatile, but some are, so we might as well say they all are.
+ * XXX Make this a flag word -DaveM
+ */
+ volatile char dead,
+ done,
+ urginline,
+ keepopen,
+ linger,
+ destroy,
+ no_check,
+ broadcast,
+ bsdism;
+ unsigned char debug;
+ int proc;
+ unsigned long lingertime;
+
+ int hashent;
+ struct sock *pair;
+
+ /* Error and backlog packet queues, rarely used. */
+ struct sk_buff_head back_log,
+ error_queue;
+
+ struct proto *prot;
+
+ unsigned short shutdown;
+
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+ union {
+ struct ipv6_pinfo af_inet6;
+ } net_pinfo;
+#endif
+
+ union {
+ struct tcp_opt af_tcp;
+#if defined(CONFIG_INET) || defined (CONFIG_INET_MODULE)
+ struct raw_opt tp_raw4;
+#endif
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+ struct raw6_opt tp_raw;
+#endif /* CONFIG_IPV6 */
+#if defined(CONFIG_SPX) || defined (CONFIG_SPX_MODULE)
+ struct spx_opt af_spx;
+#endif /* CONFIG_SPX */
+
+ } tp_pinfo;
+
+ int err, err_soft; /* Soft holds errors that don't
+ cause failure but are the cause
+ of a persistent failure not just
+ 'timed out' */
+ unsigned short ack_backlog;
+ unsigned short max_ack_backlog;
+ __u32 priority;
+ unsigned short type;
+ unsigned char localroute; /* Route locally only */
+ unsigned char protocol;
+ struct ucred peercred;
+
+#ifdef CONFIG_FILTER
+ /* Socket Filtering Instructions */
+ struct sk_filter *filter;
+#endif /* CONFIG_FILTER */
+
+ /* This is where all the private (optional) areas that don't
+ * overlap will eventually live.
+ */
+ union {
+ void *destruct_hook;
+ struct unix_opt af_unix;
+#if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE)
+ struct atalk_sock af_at;
+#endif
+#if defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE)
+ struct ipx_opt af_ipx;
+#endif
+#if defined (CONFIG_DECNET) || defined(CONFIG_DECNET_MODULE)
+ struct dn_scp dn;
+#endif
+#if defined (CONFIG_PACKET) || defined(CONFIG_PACKET_MODULE)
+ struct packet_opt *af_packet;
+#endif
+#if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE)
+ x25_cb *x25;
+#endif
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ ax25_cb *ax25;
+#endif
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+ nr_cb *nr;
+#endif
+#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+ rose_cb *rose;
+#endif
+#ifdef CONFIG_NETLINK
+ struct netlink_opt af_netlink;
+#endif
+#if defined(CONFIG_ECONET) || defined(CONFIG_ECONET_MODULE)
+ struct econet_opt *af_econet;
+#endif
+#if defined(CONFIG_IRDA) || defined(CONFIG_IRDA_MODULE)
+ struct irda_sock *irda;
+#endif
+ } protinfo;
+
+ /* IP 'private area' or will be eventually. */
+ int ip_ttl; /* TTL setting */
+ int ip_tos; /* TOS */
+ unsigned ip_cmsg_flags;
+ struct ip_options *opt;
+ unsigned char ip_hdrincl; /* Include headers ? */
+ __u8 ip_mc_ttl; /* Multicasting TTL */
+ __u8 ip_mc_loop; /* Loopback */
+ __u8 ip_recverr;
+ __u8 ip_pmtudisc;
+ int ip_mc_index; /* Multicast device index */
+ __u32 ip_mc_addr;
+ struct ip_mc_socklist *ip_mc_list; /* Group array */
+
+ /* This part is used for the timeout functions (timer.c). */
+ int timeout; /* What are we waiting for? */
+ struct timer_list timer; /* This is the sock cleanup timer. */
+ struct timeval stamp;
+
+ /* Identd */
+ struct socket *socket;
+
+ /* RPC layer private data */
+ void *user_data;
+
+ /* Callbacks */
+ void (*state_change)(struct sock *sk);
+ void (*data_ready)(struct sock *sk,int bytes);
+ void (*write_space)(struct sock *sk);
+ void (*error_report)(struct sock *sk);
+
+ int (*backlog_rcv) (struct sock *sk,
+ struct sk_buff *skb);
+ void (*destruct)(struct sock *sk);
+};
+
+/* IP protocol blocks we attach to sockets.
+ * socket layer -> transport layer interface
+ * transport -> network interface is defined by struct inet_proto
+ */
+struct proto {
+ /* These must be first. */
+ struct sock *sklist_next;
+ struct sock *sklist_prev;
+
+ void (*close)(struct sock *sk,
+ long timeout);
+ int (*connect)(struct sock *sk,
+ struct sockaddr *uaddr,
+ int addr_len);
+
+ struct sock * (*accept) (struct sock *sk, int flags);
+ void (*retransmit)(struct sock *sk, int all);
+ void (*write_wakeup)(struct sock *sk);
+ void (*read_wakeup)(struct sock *sk);
+
+ unsigned int (*poll)(struct file * file, struct socket *sock,
+ struct poll_table_struct *wait);
+
+ int (*ioctl)(struct sock *sk, int cmd,
+ unsigned long arg);
+ int (*init)(struct sock *sk);
+ int (*destroy)(struct sock *sk);
+ void (*shutdown)(struct sock *sk, int how);
+ int (*setsockopt)(struct sock *sk, int level,
+ int optname, char *optval, int optlen);
+ int (*getsockopt)(struct sock *sk, int level,
+ int optname, char *optval,
+ int *option);
+ int (*sendmsg)(struct sock *sk, struct msghdr *msg,
+ int len);
+ int (*recvmsg)(struct sock *sk, struct msghdr *msg,
+ int len, int noblock, int flags,
+ int *addr_len);
+ int (*bind)(struct sock *sk,
+ struct sockaddr *uaddr, int addr_len);
+
+ int (*backlog_rcv) (struct sock *sk,
+ struct sk_buff *skb);
+
+ /* Keeping track of sk's, looking them up, and port selection methods. */
+ void (*hash)(struct sock *sk);
+ void (*unhash)(struct sock *sk);
+ int (*get_port)(struct sock *sk, unsigned short snum);
+
+ unsigned short max_header;
+ unsigned long retransmits;
+ char name[32];
+ int inuse, highestinuse;
+};
+
+#define TIME_WRITE 1 /* Not yet used */
+#define TIME_RETRANS 2 /* Retransmit timer */
+#define TIME_DACK 3 /* Delayed ack timer */
+#define TIME_CLOSE 4
+#define TIME_KEEPOPEN 5
+#define TIME_DESTROY 6
+#define TIME_DONE 7 /* Used to absorb those last few packets */
+#define TIME_PROBE0 8
+
+/* About 10 seconds */
+#define SOCK_DESTROY_TIME (10*HZ)
+
+/* Sockets 0-1023 can't be bound to unless you are superuser */
+#define PROT_SOCK 1024
+
+#define SHUTDOWN_MASK 3
+#define RCV_SHUTDOWN 1
+#define SEND_SHUTDOWN 2
+
+/* Per-protocol hash table implementations use this to make sure
+ * nothing changes.
+ */
+#define SOCKHASH_LOCK() start_bh_atomic()
+#define SOCKHASH_UNLOCK() end_bh_atomic()
+
+/* Some things in the kernel just want to get at a protocols
+ * entire socket list commensurate, thus...
+ */
+static __inline__ void __add_to_prot_sklist(struct sock *sk)
+{
+ struct proto *p = sk->prot;
+
+ sk->sklist_prev = (struct sock *) p;
+ sk->sklist_next = p->sklist_next;
+ p->sklist_next->sklist_prev = sk;
+ p->sklist_next = sk;
+
+ /* Charge the protocol. */
+ sk->prot->inuse += 1;
+ if(sk->prot->highestinuse < sk->prot->inuse)
+ sk->prot->highestinuse = sk->prot->inuse;
+}
+
+static __inline__ void add_to_prot_sklist(struct sock *sk)
+{
+ SOCKHASH_LOCK();
+ if(!sk->sklist_next)
+ __add_to_prot_sklist(sk);
+ SOCKHASH_UNLOCK();
+}
+
+static __inline__ void del_from_prot_sklist(struct sock *sk)
+{
+ SOCKHASH_LOCK();
+ if(sk->sklist_next) {
+ sk->sklist_next->sklist_prev = sk->sklist_prev;
+ sk->sklist_prev->sklist_next = sk->sklist_next;
+ sk->sklist_next = NULL;
+ sk->prot->inuse--;
+ }
+ SOCKHASH_UNLOCK();
+}
+
+/*
+ * Used by processes to "lock" a socket state, so that
+ * interrupts and bottom half handlers won't change it
+ * from under us. It essentially blocks any incoming
+ * packets, so that we won't get any new data or any
+ * packets that change the state of the socket.
+ *
+ * Note the 'barrier()' calls: gcc may not move a lock
+ * "downwards" or a unlock "upwards" when optimizing.
+ */
+extern void __release_sock(struct sock *sk);
+
+static inline void lock_sock(struct sock *sk)
+{
+#if 0
+/* debugging code: the test isn't even 100% correct, but it can catch bugs */
+/* Note that a double lock is ok in theory - it's just _usually_ a bug */
+/* Actually it can easily happen with multiple writers */
+ if (atomic_read(&sk->sock_readers)) {
+ printk("double lock on socket at %p\n", gethere());
+here:
+ }
+#endif
+ atomic_inc(&sk->sock_readers);
+ synchronize_bh();
+}
+
+static inline void release_sock(struct sock *sk)
+{
+ barrier();
+ if (atomic_dec_and_test(&sk->sock_readers))
+ __release_sock(sk);
+}
+
+/*
+ * This might not be the most appropriate place for this two
+ * but since they are used by a lot of the net related code
+ * at least they get declared on a include that is common to all
+ */
+
+static __inline__ int min(unsigned int a, unsigned int b)
+{
+ if (a > b)
+ a = b;
+ return a;
+}
+
+static __inline__ int max(unsigned int a, unsigned int b)
+{
+ if (a < b)
+ a = b;
+ return a;
+}
+
+extern struct sock * sk_alloc(int family, int priority, int zero_it);
+extern void sk_free(struct sock *sk);
+extern void destroy_sock(struct sock *sk);
+
+extern struct sk_buff *sock_wmalloc(struct sock *sk,
+ unsigned long size, int force,
+ int priority);
+extern struct sk_buff *sock_rmalloc(struct sock *sk,
+ unsigned long size, int force,
+ int priority);
+extern void sock_wfree(struct sk_buff *skb);
+extern void sock_rfree(struct sk_buff *skb);
+extern unsigned long sock_rspace(struct sock *sk);
+extern unsigned long sock_wspace(struct sock *sk);
+
+extern int sock_setsockopt(struct socket *sock, int level,
+ int op, char *optval,
+ int optlen);
+
+extern int sock_getsockopt(struct socket *sock, int level,
+ int op, char *optval,
+ int *optlen);
+extern struct sk_buff *sock_alloc_send_skb(struct sock *sk,
+ unsigned long size,
+ unsigned long fallback,
+ int noblock,
+ int *errcode);
+extern void *sock_kmalloc(struct sock *sk, int size, int priority);
+extern void sock_kfree_s(struct sock *sk, void *mem, int size);
+
+
+/*
+ * Functions to fill in entries in struct proto_ops when a protocol
+ * does not implement a particular function.
+ */
+extern int sock_no_dup(struct socket *, struct socket *);
+extern int sock_no_release(struct socket *,
+ struct socket *);
+extern int sock_no_bind(struct socket *,
+ struct sockaddr *, int);
+extern int sock_no_connect(struct socket *,
+ struct sockaddr *, int, int);
+extern int sock_no_socketpair(struct socket *,
+ struct socket *);
+extern int sock_no_accept(struct socket *,
+ struct socket *, int);
+extern int sock_no_getname(struct socket *,
+ struct sockaddr *, int *, int);
+extern unsigned int sock_no_poll(struct file *, struct socket *,
+ struct poll_table_struct *);
+extern int sock_no_ioctl(struct socket *, unsigned int,
+ unsigned long);
+extern int sock_no_listen(struct socket *, int);
+extern int sock_no_shutdown(struct socket *, int);
+extern int sock_no_getsockopt(struct socket *, int , int,
+ char *, int *);
+extern int sock_no_setsockopt(struct socket *, int, int,
+ char *, int);
+extern int sock_no_fcntl(struct socket *,
+ unsigned int, unsigned long);
+extern int sock_no_sendmsg(struct socket *,
+ struct msghdr *, int,
+ struct scm_cookie *);
+extern int sock_no_recvmsg(struct socket *,
+ struct msghdr *, int,
+ struct scm_cookie *);
+
+/*
+ * Default socket callbacks and setup code
+ */
+
+extern void sock_def_callback1(struct sock *);
+extern void sock_def_callback2(struct sock *, int);
+extern void sock_def_callback3(struct sock *);
+extern void sock_def_destruct(struct sock *);
+
+/* Initialise core socket variables */
+extern void sock_init_data(struct socket *sock, struct sock *sk);
+
+extern void sklist_remove_socket(struct sock **list, struct sock *sk);
+extern void sklist_insert_socket(struct sock **list, struct sock *sk);
+extern void sklist_destroy_socket(struct sock **list, struct sock *sk);
+
+#ifdef CONFIG_FILTER
+/*
+ * Run the filter code and then cut skb->data to correct size returned by
+ * sk_run_filter. If pkt_len is 0 we toss packet. If skb->len is smaller
+ * than pkt_len we keep whole skb->data.
+ */
+extern __inline__ int sk_filter(struct sk_buff *skb, struct sk_filter *filter)
+{
+ int pkt_len;
+
+ pkt_len = sk_run_filter(skb, filter->insns, filter->len);
+ if(!pkt_len)
+ return 1; /* Toss Packet */
+ else
+ skb_trim(skb, pkt_len);
+
+ return 0;
+}
+
+extern __inline__ void sk_filter_release(struct sock *sk, struct sk_filter *fp)
+{
+ unsigned int size = sk_filter_len(fp);
+
+ atomic_sub(size, &sk->omem_alloc);
+
+ if (atomic_dec_and_test(&fp->refcnt))
+ kfree_s(fp, size);
+}
+
+extern __inline__ void sk_filter_charge(struct sock *sk, struct sk_filter *fp)
+{
+ atomic_inc(&fp->refcnt);
+ atomic_add(sk_filter_len(fp), &sk->omem_alloc);
+}
+
+#endif /* CONFIG_FILTER */
+
+/*
+ * Queue a received datagram if it will fit. Stream and sequenced
+ * protocols can't normally use this as they need to fit buffers in
+ * and play with them.
+ *
+ * Inlined as it's very short and called for pretty much every
+ * packet ever received.
+ */
+
+extern __inline__ void skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
+{
+ skb->sk = sk;
+ skb->destructor = sock_wfree;
+ atomic_add(skb->truesize, &sk->wmem_alloc);
+}
+
+extern __inline__ void skb_set_owner_r(struct sk_buff *skb, struct sock *sk)
+{
+ skb->sk = sk;
+ skb->destructor = sock_rfree;
+ atomic_add(skb->truesize, &sk->rmem_alloc);
+}
+
+
+extern __inline__ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+#ifdef CONFIG_FILTER
+ struct sk_filter *filter;
+#endif
+ /* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
+ number of warnings when compiling with -W --ANK
+ */
+ if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf)
+ return -ENOMEM;
+
+#ifdef CONFIG_FILTER
+ if ((filter = sk->filter) != NULL && sk_filter(skb, filter))
+ return -EPERM; /* Toss packet */
+#endif /* CONFIG_FILTER */
+
+ skb_set_owner_r(skb, sk);
+ skb_queue_tail(&sk->receive_queue, skb);
+ if (!sk->dead)
+ sk->data_ready(sk,skb->len);
+ return 0;
+}
+
+extern __inline__ int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb)
+{
+ /* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
+ number of warnings when compiling with -W --ANK
+ */
+ if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf)
+ return -ENOMEM;
+ skb_set_owner_r(skb, sk);
+ skb_queue_tail(&sk->error_queue,skb);
+ if (!sk->dead)
+ sk->data_ready(sk,skb->len);
+ return 0;
+}
+
+/*
+ * Recover an error report and clear atomically
+ */
+
+extern __inline__ int sock_error(struct sock *sk)
+{
+ int err=xchg(&sk->err,0);
+ return -err;
+}
+
+extern __inline__ unsigned long sock_wspace(struct sock *sk)
+{
+ int amt = 0;
+
+ if (!(sk->shutdown & SEND_SHUTDOWN)) {
+ amt = sk->sndbuf - atomic_read(&sk->wmem_alloc);
+ if (amt < 0)
+ amt = 0;
+ }
+ return amt;
+}
+
+/*
+ * Default write policy as shown to user space via poll/select/SIGIO
+ * Kernel internally doesn't use the MIN_WRITE_SPACE threshold.
+ */
+extern __inline__ int sock_writeable(struct sock *sk)
+{
+ return sock_wspace(sk) >= MIN_WRITE_SPACE;
+}
+
+/*
+ * Declarations from timer.c
+ */
+
+extern struct sock *timer_base;
+
+extern void net_delete_timer (struct sock *);
+extern void net_reset_timer (struct sock *, int, unsigned long);
+extern void net_timer (unsigned long);
+
+extern __inline__ int gfp_any(void)
+{
+ return in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
+}
+
+/*
+ * Enable debug/info messages
+ */
+
+#if 1
+#define NETDEBUG(x) do { } while (0)
+#else
+#define NETDEBUG(x) do { x; } while (0)
+#endif
+
+#endif /* _SOCK_H */
diff --git a/pfinet/linux-src/include/net/spx.h b/pfinet/linux-src/include/net/spx.h
new file mode 100644
index 00000000..a449b891
--- /dev/null
+++ b/pfinet/linux-src/include/net/spx.h
@@ -0,0 +1,93 @@
+#ifndef __NET_SPX_H
+#define __NET_SPX_H
+
+#include <net/ipx.h>
+
+struct spxhdr
+{ __u8 cctl;
+ __u8 dtype;
+#define SPX_DTYPE_ECONN 0xFE /* Finished */
+#define SPX_DTYPE_ECACK 0xFF /* Ok */
+ __u16 sconn; /* Connection ID */
+ __u16 dconn; /* Connection ID */
+ __u16 sequence;
+ __u16 ackseq;
+ __u16 allocseq;
+};
+
+struct ipxspxhdr
+{ struct ipxhdr ipx;
+ struct spxhdr spx;
+};
+
+#define SPX_SYS_PKT_LEN (sizeof(struct ipxspxhdr))
+
+#ifdef __KERNEL__
+struct spx_opt
+{ int state;
+ int sndbuf;
+ int retries; /* Number of WD retries */
+ int retransmits; /* Number of retransmits */
+ int max_retries;
+ int wd_interval;
+ void *owner;
+ __u16 dest_connid; /* Net order */
+ __u16 source_connid; /* Net order */
+ __u16 sequence; /* Host order - our current pkt # */
+ __u16 alloc; /* Host order - max seq we can rcv now */
+ __u16 rmt_ack; /* Host order - last pkt ACKd by remote */
+ __u16 rmt_seq;
+ __u16 acknowledge;
+ __u16 rmt_alloc; /* Host order - max seq remote can handle now */
+ ipx_address dest_addr;
+ ipx_address source_addr;
+ struct timer_list watchdog; /* Idle watch */
+ struct timer_list retransmit; /* Retransmit timer */
+ struct sk_buff_head rcv_queue;
+ struct sk_buff_head transmit_queue;
+ struct sk_buff_head retransmit_queue;
+};
+
+/* Packet connectino control defines */
+#define CCTL_SPXII_XHD 0x01 /* SPX2 extended header */
+#define CCTL_SPX_UNKNOWN 0x02 /* Unknown (unused ??) */
+#define CCTL_SPXII_NEG 0x04 /* Negotiate size */
+#define CCTL_SPXII 0x08 /* Set for SPX2 */
+#define CCTL_EOM 0x10 /* End of message marker */
+#define CCTL_URG 0x20 /* Urgent marker in SPP (not used in SPX?) */
+#define CCTL_ACK 0x40 /* Send me an ACK */
+#define CCTL_CTL 0x80 /* Control message */
+#define CCTL_SYS CCTL_CTL /* Spec uses CCTL_SYS */
+
+/* Connection state defines */
+#define SPX_CLOSED 7
+#define SPX_CONNECTING 8
+#define SPX_CONNECTED 9
+
+/* Packet transmit types - Internal */
+#define DATA 0 /* Data */
+#define ACK 1 /* Data ACK */
+#define WDACK 2 /* WD ACK */
+#define CONACK 3 /* Connection Request ACK */
+#define CONREQ 4 /* Connection Request */
+#define WDREQ 5 /* WD Request */
+#define DISCON 6 /* Informed Disconnect */
+#define DISACK 7 /* Informed Disconnect ACK */
+#define RETRAN 8 /* Int. Retransmit of packet */
+#define TQUEUE 9 /* Int. Transmit of a queued packet */
+
+/*
+ * These are good canidates for IOcontrol calls
+ */
+
+/* Watchdog defines */
+#define VERIFY_TIMEOUT 3 * HZ
+#define ABORT_TIMEOUT 30 * HZ
+
+/* Packet retransmit defines */
+#define RETRY_COUNT 10
+#define RETRY_TIME 1 * HZ
+#define MAX_RETRY_DELAY 5 * HZ
+
+#endif /* __KERNEL__ */
+#endif /* def __NET_SPX_H */
diff --git a/pfinet/linux-src/include/net/spxcall.h b/pfinet/linux-src/include/net/spxcall.h
new file mode 100644
index 00000000..0461fbbe
--- /dev/null
+++ b/pfinet/linux-src/include/net/spxcall.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void spx_proto_init(struct net_proto *pro);
diff --git a/pfinet/linux-src/include/net/tcp.h b/pfinet/linux-src/include/net/tcp.h
new file mode 100644
index 00000000..abb4b210
--- /dev/null
+++ b/pfinet/linux-src/include/net/tcp.h
@@ -0,0 +1,1097 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the TCP module.
+ *
+ * Version: @(#)tcp.h 1.0.5 05/23/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _TCP_H
+#define _TCP_H
+
+#include <linux/config.h>
+#include <linux/tcp.h>
+#include <linux/slab.h>
+#include <net/checksum.h>
+
+/* This is for all connections with a full identity, no wildcards.
+ * Half of the table is for TIME_WAIT, the other half is for the
+ * rest.
+ *
+ * This needs to be shared by v4 and v6 because the lookup and hashing
+ * code needs to work with different AF's yet the port space is
+ * shared.
+ */
+extern unsigned int tcp_ehash_size;
+extern struct sock **tcp_ehash;
+
+/* This is for listening sockets, thus all sockets which possess wildcards. */
+#define TCP_LHTABLE_SIZE 32 /* Yes, really, this is all you need. */
+extern struct sock *tcp_listening_hash[TCP_LHTABLE_SIZE];
+
+/* There are a few simple rules, which allow for local port reuse by
+ * an application. In essence:
+ *
+ * 1) Sockets bound to different interfaces may share a local port.
+ * Failing that, goto test 2.
+ * 2) If all sockets have sk->reuse set, and none of them are in
+ * TCP_LISTEN state, the port may be shared.
+ * Failing that, goto test 3.
+ * 3) If all sockets are bound to a specific sk->rcv_saddr local
+ * address, and none of them are the same, the port may be
+ * shared.
+ * Failing this, the port cannot be shared.
+ *
+ * The interesting point, is test #2. This is what an FTP server does
+ * all day. To optimize this case we use a specific flag bit defined
+ * below. As we add sockets to a bind bucket list, we perform a
+ * check of: (newsk->reuse && (newsk->state != TCP_LISTEN))
+ * As long as all sockets added to a bind bucket pass this test,
+ * the flag bit will be set.
+ * The resulting situation is that tcp_v[46]_verify_bind() can just check
+ * for this flag bit, if it is set and the socket trying to bind has
+ * sk->reuse set, we don't even have to walk the owners list at all,
+ * we return that it is ok to bind this socket to the requested local port.
+ *
+ * Sounds like a lot of work, but it is worth it. In a more naive
+ * implementation (ie. current FreeBSD etc.) the entire list of ports
+ * must be walked for each data port opened by an ftp server. Needless
+ * to say, this does not scale at all. With a couple thousand FTP
+ * users logged onto your box, isn't it nice to know that new data
+ * ports are created in O(1) time? I thought so. ;-) -DaveM
+ */
+struct tcp_bind_bucket {
+ unsigned short port;
+ unsigned short fastreuse;
+ struct tcp_bind_bucket *next;
+ struct sock *owners;
+ struct tcp_bind_bucket **pprev;
+};
+
+extern unsigned int tcp_bhash_size;
+extern struct tcp_bind_bucket **tcp_bhash;
+extern kmem_cache_t *tcp_bucket_cachep;
+extern struct tcp_bind_bucket *tcp_bucket_create(unsigned short snum);
+extern void tcp_bucket_unlock(struct sock *sk);
+extern int tcp_port_rover;
+
+/* Level-1 socket-demux cache. */
+#define TCP_NUM_REGS 32
+extern struct sock *tcp_regs[TCP_NUM_REGS];
+
+#define TCP_RHASH_FN(__fport) \
+ ((((__fport) >> 7) ^ (__fport)) & (TCP_NUM_REGS - 1))
+#define TCP_RHASH(__fport) tcp_regs[TCP_RHASH_FN((__fport))]
+#define TCP_SK_RHASH_FN(__sock) TCP_RHASH_FN((__sock)->dport)
+#define TCP_SK_RHASH(__sock) tcp_regs[TCP_SK_RHASH_FN((__sock))]
+
+static __inline__ void tcp_reg_zap(struct sock *sk)
+{
+ struct sock **rpp;
+
+ rpp = &(TCP_SK_RHASH(sk));
+ if(*rpp == sk)
+ *rpp = NULL;
+}
+
+/* These are AF independent. */
+static __inline__ int tcp_bhashfn(__u16 lport)
+{
+ return (lport & (tcp_bhash_size - 1));
+}
+
+/* This is a TIME_WAIT bucket. It works around the memory consumption
+ * problems of sockets in such a state on heavily loaded servers, but
+ * without violating the protocol specification.
+ */
+struct tcp_tw_bucket {
+ /* These _must_ match the beginning of struct sock precisely.
+ * XXX Yes I know this is gross, but I'd have to edit every single
+ * XXX networking file if I created a "struct sock_header". -DaveM
+ * Just don't forget -fno-strict-aliasing, but it should be really
+ * fixed -AK
+ */
+ struct sock *sklist_next;
+ struct sock *sklist_prev;
+ struct sock *bind_next;
+ struct sock **bind_pprev;
+ __u32 daddr;
+ __u32 rcv_saddr;
+ __u16 dport;
+ unsigned short num;
+ int bound_dev_if;
+ struct sock *next;
+ struct sock **pprev;
+ unsigned char state,
+ zapped;
+ __u16 sport;
+ unsigned short family;
+ unsigned char reuse,
+ nonagle;
+
+ /* And these are ours. */
+ __u32 rcv_nxt, snd_nxt;
+ __u16 window;
+ struct tcp_func *af_specific;
+ struct tcp_bind_bucket *tb;
+ struct tcp_tw_bucket *next_death;
+ struct tcp_tw_bucket **pprev_death;
+ int death_slot;
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct in6_addr v6_daddr;
+ struct in6_addr v6_rcv_saddr;
+#endif
+};
+
+extern kmem_cache_t *tcp_timewait_cachep;
+
+/* Socket demux engine toys. */
+#ifdef __BIG_ENDIAN
+#define TCP_COMBINED_PORTS(__sport, __dport) \
+ (((__u32)(__sport)<<16) | (__u32)(__dport))
+#else /* __LITTLE_ENDIAN */
+#define TCP_COMBINED_PORTS(__sport, __dport) \
+ (((__u32)(__dport)<<16) | (__u32)(__sport))
+#endif
+
+#if (BITS_PER_LONG == 64)
+#ifdef __BIG_ENDIAN
+#define TCP_V4_ADDR_COOKIE(__name, __saddr, __daddr) \
+ __u64 __name = (((__u64)(__saddr))<<32)|((__u64)(__daddr));
+#else /* __LITTLE_ENDIAN */
+#define TCP_V4_ADDR_COOKIE(__name, __saddr, __daddr) \
+ __u64 __name = (((__u64)(__daddr))<<32)|((__u64)(__saddr));
+#endif /* __BIG_ENDIAN */
+#define TCP_IPV4_MATCH(__sk, __cookie, __saddr, __daddr, __ports, __dif)\
+ (((*((__u64 *)&((__sk)->daddr)))== (__cookie)) && \
+ ((*((__u32 *)&((__sk)->dport)))== (__ports)) && \
+ (!((__sk)->bound_dev_if) || ((__sk)->bound_dev_if == (__dif))))
+#else /* 32-bit arch */
+#define TCP_V4_ADDR_COOKIE(__name, __saddr, __daddr)
+#define TCP_IPV4_MATCH(__sk, __cookie, __saddr, __daddr, __ports, __dif)\
+ (((__sk)->daddr == (__saddr)) && \
+ ((__sk)->rcv_saddr == (__daddr)) && \
+ ((*((__u32 *)&((__sk)->dport)))== (__ports)) && \
+ (!((__sk)->bound_dev_if) || ((__sk)->bound_dev_if == (__dif))))
+#endif /* 64-bit arch */
+
+#define TCP_IPV6_MATCH(__sk, __saddr, __daddr, __ports, __dif) \
+ (((*((__u32 *)&((__sk)->dport)))== (__ports)) && \
+ ((__sk)->family == AF_INET6) && \
+ !ipv6_addr_cmp(&(__sk)->net_pinfo.af_inet6.daddr, (__saddr)) && \
+ !ipv6_addr_cmp(&(__sk)->net_pinfo.af_inet6.rcv_saddr, (__daddr)) && \
+ (!((__sk)->bound_dev_if) || ((__sk)->bound_dev_if == (__dif))))
+
+/* These can have wildcards, don't try too hard. */
+static __inline__ int tcp_lhashfn(unsigned short num)
+{
+ return num & (TCP_LHTABLE_SIZE - 1);
+}
+
+static __inline__ int tcp_sk_listen_hashfn(struct sock *sk)
+{
+ return tcp_lhashfn(sk->num);
+}
+
+/* Note, that it is > than ipv6 header */
+#define NETHDR_SIZE (sizeof(struct iphdr) + 40)
+
+/*
+ * 40 is maximal IP options size
+ * 20 is the maximum TCP options size we can currently construct on a SYN.
+ * 40 is the maximum possible TCP options size.
+ */
+
+#define MAX_SYN_SIZE (NETHDR_SIZE + sizeof(struct tcphdr) + 20 + MAX_HEADER + 15)
+#define MAX_FIN_SIZE (NETHDR_SIZE + sizeof(struct tcphdr) + MAX_HEADER + 15)
+#define BASE_ACK_SIZE (NETHDR_SIZE + MAX_HEADER + 15)
+#define MAX_ACK_SIZE (NETHDR_SIZE + sizeof(struct tcphdr) + MAX_HEADER + 15)
+#define MAX_RESET_SIZE (NETHDR_SIZE + sizeof(struct tcphdr) + MAX_HEADER + 15)
+#define MAX_TCPHEADER_SIZE (NETHDR_SIZE + sizeof(struct tcphdr) + 20 + MAX_HEADER + 15)
+
+/*
+ * Never offer a window over 32767 without using window scaling. Some
+ * poor stacks do signed 16bit maths!
+ */
+#define MAX_WINDOW 32767
+#define MIN_WINDOW 2048
+#define MAX_ACK_BACKLOG 2
+#define MAX_DELAY_ACK 2
+#define TCP_WINDOW_DIFF 2048
+
+/* urg_data states */
+#define URG_VALID 0x0100
+#define URG_NOTYET 0x0200
+#define URG_READ 0x0400
+
+#define TCP_RETR1 7 /*
+ * This is how many retries it does before it
+ * tries to figure out if the gateway is
+ * down.
+ */
+
+#define TCP_RETR2 15 /*
+ * This should take at least
+ * 90 minutes to time out.
+ */
+
+#define TCP_TIMEOUT_LEN (15*60*HZ) /* should be about 15 mins */
+#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to successfully
+ * close the socket, about 60 seconds */
+#define TCP_FIN_TIMEOUT (3*60*HZ) /* BSD style FIN_WAIT2 deadlock breaker */
+
+#define TCP_ACK_TIME (3*HZ) /* time to delay before sending an ACK */
+#define TCP_DONE_TIME (5*HZ/2)/* maximum time to wait before actually
+ * destroying a socket */
+#define TCP_WRITE_TIME (30*HZ) /* initial time to wait for an ACK,
+ * after last transmit */
+#define TCP_TIMEOUT_INIT (3*HZ) /* RFC 1122 initial timeout value */
+#define TCP_SYN_RETRIES 10 /* number of times to retry opening a
+ * connection (TCP_RETR2-....) */
+#define TCP_PROBEWAIT_LEN (1*HZ)/* time to wait between probes when
+ * I've got something to write and
+ * there is no window */
+#define TCP_KEEPALIVE_TIME (120*60*HZ) /* two hours */
+#define TCP_KEEPALIVE_PROBES 9 /* Max of 9 keepalive probes */
+#define TCP_KEEPALIVE_PERIOD ((75*HZ)>>2) /* period of keepalive check */
+
+#define TCP_SYNACK_PERIOD (HZ/2) /* How often to run the synack slow timer */
+#define TCP_QUICK_TRIES 8 /* How often we try to retransmit, until
+ * we tell the link layer that it is something
+ * wrong (e.g. that it can expire redirects) */
+
+#define TCP_BUCKETGC_PERIOD (HZ)
+
+/* TIME_WAIT reaping mechanism. */
+#define TCP_TWKILL_SLOTS 8 /* Please keep this a power of 2. */
+#define TCP_TWKILL_PERIOD ((HZ*60)/TCP_TWKILL_SLOTS)
+
+/*
+ * TCP option
+ */
+
+#define TCPOPT_NOP 1 /* Padding */
+#define TCPOPT_EOL 0 /* End of options */
+#define TCPOPT_MSS 2 /* Segment size negotiating */
+#define TCPOPT_WINDOW 3 /* Window scaling */
+#define TCPOPT_SACK_PERM 4 /* SACK Permitted */
+#define TCPOPT_SACK 5 /* SACK Block */
+#define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */
+
+/*
+ * TCP option lengths
+ */
+
+#define TCPOLEN_MSS 4
+#define TCPOLEN_WINDOW 3
+#define TCPOLEN_SACK_PERM 2
+#define TCPOLEN_TIMESTAMP 10
+
+/* But this is what stacks really send out. */
+#define TCPOLEN_TSTAMP_ALIGNED 12
+#define TCPOLEN_WSCALE_ALIGNED 4
+#define TCPOLEN_SACKPERM_ALIGNED 4
+#define TCPOLEN_SACK_BASE 2
+#define TCPOLEN_SACK_BASE_ALIGNED 4
+#define TCPOLEN_SACK_PERBLOCK 8
+
+struct open_request;
+
+struct or_calltable {
+ void (*rtx_syn_ack) (struct sock *sk, struct open_request *req);
+ void (*destructor) (struct open_request *req);
+ void (*send_reset) (struct sk_buff *skb);
+};
+
+struct tcp_v4_open_req {
+ __u32 loc_addr;
+ __u32 rmt_addr;
+ struct ip_options *opt;
+};
+
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+struct tcp_v6_open_req {
+ struct in6_addr loc_addr;
+ struct in6_addr rmt_addr;
+ struct sk_buff *pktopts;
+ int iif;
+};
+#endif
+
+/* this structure is too big */
+struct open_request {
+ struct open_request *dl_next; /* Must be first member! */
+ __u32 rcv_isn;
+ __u32 snt_isn;
+ __u16 rmt_port;
+ __u16 mss;
+ __u8 retrans;
+ __u8 __pad;
+ unsigned snd_wscale : 4,
+ rcv_wscale : 4,
+ tstamp_ok : 1,
+ sack_ok : 1,
+ wscale_ok : 1;
+ /* The following two fields can be easily recomputed I think -AK */
+ __u32 window_clamp; /* window clamp at creation time */
+ __u32 rcv_wnd; /* rcv_wnd offered first time */
+ __u32 ts_recent;
+ unsigned long expires;
+ struct or_calltable *class;
+ struct sock *sk;
+ union {
+ struct tcp_v4_open_req v4_req;
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+ struct tcp_v6_open_req v6_req;
+#endif
+ } af;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ __u16 lcl_port; /* LVE */
+#endif
+};
+
+/* SLAB cache for open requests. */
+extern kmem_cache_t *tcp_openreq_cachep;
+
+#define tcp_openreq_alloc() kmem_cache_alloc(tcp_openreq_cachep, SLAB_ATOMIC)
+#define tcp_openreq_free(req) kmem_cache_free(tcp_openreq_cachep, req)
+
+/*
+ * Pointers to address related TCP functions
+ * (i.e. things that depend on the address family)
+ *
+ * BUGGG_FUTURE: all the idea behind this struct is wrong.
+ * It mixes socket frontend with transport function.
+ * With port sharing between IPv6/v4 it gives the only advantage,
+ * only poor IPv6 needs to permanently recheck, that it
+ * is still IPv6 8)8) It must be cleaned up as soon as possible.
+ * --ANK (980802)
+ */
+
+struct tcp_func {
+ void (*queue_xmit) (struct sk_buff *skb);
+
+ void (*send_check) (struct sock *sk,
+ struct tcphdr *th,
+ int len,
+ struct sk_buff *skb);
+
+ int (*rebuild_header) (struct sock *sk);
+
+ int (*conn_request) (struct sock *sk,
+ struct sk_buff *skb,
+ __u32 isn);
+
+ struct sock * (*syn_recv_sock) (struct sock *sk,
+ struct sk_buff *skb,
+ struct open_request *req,
+ struct dst_entry *dst);
+
+ struct sock * (*get_sock) (struct sk_buff *skb,
+ struct tcphdr *th);
+
+ __u16 net_header_len;
+
+
+
+ int (*setsockopt) (struct sock *sk,
+ int level,
+ int optname,
+ char *optval,
+ int optlen);
+
+ int (*getsockopt) (struct sock *sk,
+ int level,
+ int optname,
+ char *optval,
+ int *optlen);
+
+
+ void (*addr2sockaddr) (struct sock *sk,
+ struct sockaddr *);
+
+ int sockaddr_len;
+};
+
+/*
+ * The next routines deal with comparing 32 bit unsigned ints
+ * and worry about wraparound (automatic with unsigned arithmetic).
+ */
+
+extern __inline int before(__u32 seq1, __u32 seq2)
+{
+ return (__s32)(seq1-seq2) < 0;
+}
+
+extern __inline int after(__u32 seq1, __u32 seq2)
+{
+ return (__s32)(seq2-seq1) < 0;
+}
+
+
+/* is s2<=s1<=s3 ? */
+extern __inline int between(__u32 seq1, __u32 seq2, __u32 seq3)
+{
+ return seq3 - seq2 >= seq1 - seq2;
+}
+
+
+extern struct proto tcp_prot;
+extern struct tcp_mib tcp_statistics;
+
+extern void tcp_put_port(struct sock *sk);
+extern void __tcp_put_port(struct sock *sk);
+extern void tcp_inherit_port(struct sock *sk, struct sock *child);
+
+extern void tcp_v4_err(struct sk_buff *skb,
+ unsigned char *, int);
+
+extern void tcp_shutdown (struct sock *sk, int how);
+
+extern int tcp_v4_rcv(struct sk_buff *skb,
+ unsigned short len);
+
+extern int tcp_do_sendmsg(struct sock *sk, struct msghdr *msg);
+
+extern int tcp_ioctl(struct sock *sk,
+ int cmd,
+ unsigned long arg);
+
+extern int tcp_rcv_state_process(struct sock *sk,
+ struct sk_buff *skb,
+ struct tcphdr *th,
+ unsigned len);
+
+extern int tcp_rcv_established(struct sock *sk,
+ struct sk_buff *skb,
+ struct tcphdr *th,
+ unsigned len);
+
+enum tcp_tw_status {
+ TCP_TW_SUCCESS = 0,
+ TCP_TW_RST = 1,
+ TCP_TW_ACK = 2
+ };
+
+extern enum tcp_tw_status tcp_timewait_state_process(struct tcp_tw_bucket *tw,
+ struct sk_buff *skb,
+ struct tcphdr *th,
+ unsigned len);
+
+extern void tcp_close(struct sock *sk,
+ long timeout);
+extern struct sock * tcp_accept(struct sock *sk, int flags);
+extern unsigned int tcp_poll(struct file * file, struct socket *sock, struct poll_table_struct *wait);
+extern void tcp_write_space(struct sock *sk);
+
+extern int tcp_getsockopt(struct sock *sk, int level,
+ int optname, char *optval,
+ int *optlen);
+extern int tcp_setsockopt(struct sock *sk, int level,
+ int optname, char *optval,
+ int optlen);
+extern void tcp_set_keepalive(struct sock *sk, int val);
+extern int tcp_recvmsg(struct sock *sk,
+ struct msghdr *msg,
+ int len, int nonblock,
+ int flags, int *addr_len);
+
+extern void tcp_parse_options(struct sock *sk, struct tcphdr *th,
+ struct tcp_opt *tp, int no_fancy);
+
+/*
+ * TCP v4 functions exported for the inet6 API
+ */
+
+extern int tcp_v4_rebuild_header(struct sock *sk);
+
+extern int tcp_v4_build_header(struct sock *sk,
+ struct sk_buff *skb);
+
+extern void tcp_v4_send_check(struct sock *sk,
+ struct tcphdr *th, int len,
+ struct sk_buff *skb);
+
+extern int tcp_v4_conn_request(struct sock *sk,
+ struct sk_buff *skb,
+ __u32 isn);
+
+extern struct sock * tcp_create_openreq_child(struct sock *sk,
+ struct open_request *req,
+ struct sk_buff *skb);
+
+extern struct sock * tcp_v4_syn_recv_sock(struct sock *sk,
+ struct sk_buff *skb,
+ struct open_request *req,
+ struct dst_entry *dst);
+
+extern int tcp_v4_do_rcv(struct sock *sk,
+ struct sk_buff *skb);
+
+extern int tcp_v4_connect(struct sock *sk,
+ struct sockaddr *uaddr,
+ int addr_len);
+
+extern void tcp_connect(struct sock *sk,
+ struct sk_buff *skb,
+ int est_mss);
+
+extern struct sk_buff * tcp_make_synack(struct sock *sk,
+ struct dst_entry *dst,
+ struct open_request *req,
+ int mss);
+
+
+/* From syncookies.c */
+extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
+ struct ip_options *opt);
+extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb,
+ __u16 *mss);
+
+/* tcp_output.c */
+
+extern void tcp_read_wakeup(struct sock *);
+extern void tcp_write_xmit(struct sock *);
+extern void tcp_time_wait(struct sock *);
+extern int tcp_retransmit_skb(struct sock *, struct sk_buff *);
+extern void tcp_fack_retransmit(struct sock *);
+extern void tcp_xmit_retransmit_queue(struct sock *);
+extern void tcp_simple_retransmit(struct sock *);
+
+extern void tcp_send_probe0(struct sock *);
+extern void tcp_send_partial(struct sock *);
+extern void tcp_write_wakeup(struct sock *);
+extern void tcp_send_fin(struct sock *sk);
+extern void tcp_send_active_reset(struct sock *sk);
+extern int tcp_send_synack(struct sock *);
+extern void tcp_transmit_skb(struct sock *, struct sk_buff *);
+extern void tcp_send_skb(struct sock *, struct sk_buff *, int force_queue);
+extern void tcp_send_ack(struct sock *sk);
+extern void tcp_send_delayed_ack(struct tcp_opt *tp, int max_timeout);
+
+/* CONFIG_IP_TRANSPARENT_PROXY */
+extern int tcp_chkaddr(struct sk_buff *);
+
+/* tcp_timer.c */
+#define tcp_reset_msl_timer(x,y,z) net_reset_timer(x,y,z)
+extern void tcp_reset_xmit_timer(struct sock *, int, unsigned long);
+extern void tcp_init_xmit_timers(struct sock *);
+extern void tcp_clear_xmit_timers(struct sock *);
+
+extern void tcp_retransmit_timer(unsigned long);
+extern void tcp_delack_timer(unsigned long);
+extern void tcp_probe_timer(unsigned long);
+
+extern struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
+ struct open_request *req);
+
+/*
+ * TCP slow timer
+ */
+extern struct timer_list tcp_slow_timer;
+
+struct tcp_sl_timer {
+ atomic_t count;
+ unsigned long period;
+ unsigned long last;
+ void (*handler) (unsigned long);
+};
+
+#define TCP_SLT_SYNACK 0
+#define TCP_SLT_KEEPALIVE 1
+#define TCP_SLT_TWKILL 2
+#define TCP_SLT_MAX 3
+
+extern struct tcp_sl_timer tcp_slt_array[TCP_SLT_MAX];
+
+extern int tcp_sync_mss(struct sock *sk, u32 pmtu);
+
+/* Compute the current effective MSS, taking SACKs and IP options,
+ * and even PMTU discovery events into account.
+ */
+
+static __inline__ unsigned int tcp_current_mss(struct sock *sk)
+{
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+ struct dst_entry *dst = sk->dst_cache;
+ int mss_now = tp->mss_cache;
+
+ if (dst && dst->pmtu != tp->pmtu_cookie)
+ mss_now = tcp_sync_mss(sk, dst->pmtu);
+
+ if(tp->sack_ok && tp->num_sacks)
+ mss_now -= (TCPOLEN_SACK_BASE_ALIGNED +
+ (tp->num_sacks * TCPOLEN_SACK_PERBLOCK));
+ return mss_now > 8 ? mss_now : 8;
+}
+
+/* Compute the actual receive window we are currently advertising.
+ * Rcv_nxt can be after the window if our peer push more data
+ * than the offered window.
+ */
+static __inline__ u32 tcp_receive_window(struct tcp_opt *tp)
+{
+ s32 win = tp->rcv_wup + tp->rcv_wnd - tp->rcv_nxt;
+
+ if (win < 0)
+ win = 0;
+ return (u32) win;
+}
+
+/* Choose a new window, without checks for shrinking, and without
+ * scaling applied to the result. The caller does these things
+ * if necessary. This is a "raw" window selection.
+ */
+extern u32 __tcp_select_window(struct sock *sk);
+
+/* Chose a new window to advertise, update state in tcp_opt for the
+ * socket, and return result with RFC1323 scaling applied. The return
+ * value can be stuffed directly into th->window for an outgoing
+ * frame.
+ */
+extern __inline__ u16 tcp_select_window(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ u32 cur_win = tcp_receive_window(tp);
+ u32 new_win = __tcp_select_window(sk);
+
+ /* Never shrink the offered window */
+ if(new_win < cur_win) {
+ /* Danger Will Robinson!
+ * Don't update rcv_wup/rcv_wnd here or else
+ * we will not be able to advertise a zero
+ * window in time. --DaveM
+ */
+ new_win = cur_win;
+ } else {
+ tp->rcv_wnd = new_win;
+ tp->rcv_wup = tp->rcv_nxt;
+ }
+
+ /* RFC1323 scaling applied */
+ return new_win >> tp->rcv_wscale;
+}
+
+/* See if we can advertise non-zero, and if so how much we
+ * can increase our advertisement. If it becomes more than
+ * twice what we are talking about right now, return true.
+ */
+extern __inline__ int tcp_raise_window(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ u32 cur_win = tcp_receive_window(tp);
+ u32 new_win = __tcp_select_window(sk);
+
+ return (new_win && (new_win > (cur_win << 1)));
+}
+
+/* Recalculate snd_ssthresh, we want to set it to:
+ *
+ * one half the current congestion window, but no
+ * less than two segments
+ *
+ * We must take into account the current send window
+ * as well, however we keep track of that using different
+ * units so a conversion is necessary. -DaveM
+ */
+extern __inline__ __u32 tcp_recalc_ssthresh(struct tcp_opt *tp)
+{
+ __u32 snd_wnd_packets = tp->snd_wnd / max(tp->mss_cache, 1);
+
+ return max(min(snd_wnd_packets, tp->snd_cwnd) >> 1, 2);
+}
+
+/* TCP timestamps are only 32-bits, this causes a slight
+ * complication on 64-bit systems since we store a snapshot
+ * of jiffies in the buffer control blocks below. We decidedly
+ * only use of the low 32-bits of jiffies and hide the ugly
+ * casts with the following macro.
+ */
+#define tcp_time_stamp ((__u32)(jiffies))
+
+/* This is what the send packet queueing engine uses to pass
+ * TCP per-packet control information to the transmission
+ * code. We also store the host-order sequence numbers in
+ * here too. This is 36 bytes on 32-bit architectures,
+ * 40 bytes on 64-bit machines, if this grows please adjust
+ * skbuff.h:skbuff->cb[xxx] size appropriately.
+ */
+struct tcp_skb_cb {
+ union {
+ struct inet_skb_parm h4;
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+ struct inet6_skb_parm h6;
+#endif
+ } header; /* For incoming frames */
+ __u32 seq; /* Starting sequence number */
+ __u32 end_seq; /* SEQ + FIN + SYN + datalen */
+ __u32 when; /* used to compute rtt's */
+ __u8 flags; /* TCP header flags. */
+
+ /* NOTE: These must match up to the flags byte in a
+ * real TCP header.
+ */
+#define TCPCB_FLAG_FIN 0x01
+#define TCPCB_FLAG_SYN 0x02
+#define TCPCB_FLAG_RST 0x04
+#define TCPCB_FLAG_PSH 0x08
+#define TCPCB_FLAG_ACK 0x10
+#define TCPCB_FLAG_URG 0x20
+
+ __u8 sacked; /* State flags for SACK/FACK. */
+#define TCPCB_SACKED_ACKED 0x01 /* SKB ACK'd by a SACK block */
+#define TCPCB_SACKED_RETRANS 0x02 /* SKB retransmitted */
+
+ __u16 urg_ptr; /* Valid w/URG flags is set. */
+ __u32 ack_seq; /* Sequence number ACK'd */
+};
+
+#define TCP_SKB_CB(__skb) ((struct tcp_skb_cb *)&((__skb)->cb[0]))
+
+/* This determines how many packets are "in the network" to the best
+ * of our knowledge. In many cases it is conservative, but where
+ * detailed information is available from the receiver (via SACK
+ * blocks etc.) we can make more aggressive calculations.
+ *
+ * Use this for decisions involving congestion control, use just
+ * tp->packets_out to determine if the send queue is empty or not.
+ *
+ * Read this equation as:
+ *
+ * "Packets sent once on transmission queue" MINUS
+ * "Packets acknowledged by FACK information" PLUS
+ * "Packets fast retransmitted"
+ */
+static __inline__ int tcp_packets_in_flight(struct tcp_opt *tp)
+{
+ return tp->packets_out - tp->fackets_out + tp->retrans_out;
+}
+
+/* This checks if the data bearing packet SKB (usually tp->send_head)
+ * should be put on the wire right now.
+ */
+static __inline__ int tcp_snd_test(struct sock *sk, struct sk_buff *skb)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ int nagle_check = 1;
+
+ /* RFC 1122 - section 4.2.3.4
+ *
+ * We must queue if
+ *
+ * a) The right edge of this frame exceeds the window
+ * b) There are packets in flight and we have a small segment
+ * [SWS avoidance and Nagle algorithm]
+ * (part of SWS is done on packetization)
+ * c) We are retransmiting [Nagle]
+ * d) We have too many packets 'in flight'
+ *
+ * Don't use the nagle rule for urgent data (or
+ * for the final FIN -DaveM).
+ */
+ if ((sk->nonagle == 2 && (skb->len < tp->mss_cache)) ||
+ (!sk->nonagle &&
+ skb->len < (tp->mss_cache >> 1) &&
+ tp->packets_out &&
+ !(TCP_SKB_CB(skb)->flags & (TCPCB_FLAG_URG|TCPCB_FLAG_FIN))))
+ nagle_check = 0;
+
+ /* Don't be strict about the congestion window for the
+ * final FIN frame. -DaveM
+ */
+ return (nagle_check &&
+ ((tcp_packets_in_flight(tp) < tp->snd_cwnd) ||
+ (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN)) &&
+ !after(TCP_SKB_CB(skb)->end_seq, tp->snd_una + tp->snd_wnd) &&
+ tp->retransmits == 0);
+}
+
+/* Push out any pending frames which were held back due to
+ * TCP_CORK or attempt at coalescing tiny packets.
+ * The socket must be locked by the caller.
+ */
+static __inline__ void tcp_push_pending_frames(struct sock *sk, struct tcp_opt *tp)
+{
+ if(tp->send_head) {
+ if(tcp_snd_test(sk, tp->send_head))
+ tcp_write_xmit(sk);
+ else if(tp->packets_out == 0 && !tp->pending) {
+ /* We held off on this in tcp_send_skb() */
+ tp->pending = TIME_PROBE0;
+ tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto);
+ }
+ }
+}
+
+/* This tells the input processing path that an ACK should go out
+ * right now.
+ */
+#define tcp_enter_quickack_mode(__tp) ((__tp)->ato |= (1<<31))
+#define tcp_exit_quickack_mode(__tp) ((__tp)->ato &= ~(1<<31))
+#define tcp_in_quickack_mode(__tp) (((__tp)->ato & (1 << 31)) != 0)
+
+/*
+ * List all states of a TCP socket that can be viewed as a "connected"
+ * state. This now includes TCP_SYN_RECV, although I am not yet fully
+ * convinced that this is the solution for the 'getpeername(2)'
+ * problem. Thanks to Stephen A. Wood <saw@cebaf.gov> -FvK
+ */
+
+extern __inline const int tcp_connected(const int state)
+{
+ return ((1 << state) &
+ (TCPF_ESTABLISHED|TCPF_CLOSE_WAIT|TCPF_FIN_WAIT1|
+ TCPF_FIN_WAIT2|TCPF_SYN_RECV));
+}
+
+/*
+ * Calculate(/check) TCP checksum
+ */
+static __inline__ u16 tcp_v4_check(struct tcphdr *th, int len,
+ unsigned long saddr, unsigned long daddr,
+ unsigned long base)
+{
+ return csum_tcpudp_magic(saddr,daddr,len,IPPROTO_TCP,base);
+}
+
+#undef STATE_TRACE
+
+#ifdef STATE_TRACE
+static char *statename[]={
+ "Unused","Established","Syn Sent","Syn Recv",
+ "Fin Wait 1","Fin Wait 2","Time Wait", "Close",
+ "Close Wait","Last ACK","Listen","Closing"
+};
+#endif
+
+static __inline__ void tcp_set_state(struct sock *sk, int state)
+{
+ int oldstate = sk->state;
+
+ sk->state = state;
+
+#ifdef STATE_TRACE
+ SOCK_DEBUG(sk, "TCP sk=%p, State %s -> %s\n",sk, statename[oldstate],statename[state]);
+#endif
+
+ switch (state) {
+ case TCP_ESTABLISHED:
+ if (oldstate != TCP_ESTABLISHED)
+ tcp_statistics.TcpCurrEstab++;
+ break;
+
+ case TCP_CLOSE:
+ {
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+ /* Should be about 2 rtt's */
+ net_reset_timer(sk, TIME_DONE, min(tp->srtt * 2, TCP_DONE_TIME));
+ sk->prot->unhash(sk);
+ /* fall through */
+ }
+ default:
+ if (oldstate==TCP_ESTABLISHED)
+ tcp_statistics.TcpCurrEstab--;
+ }
+}
+
+static __inline__ void tcp_build_and_update_options(__u32 *ptr, struct tcp_opt *tp, __u32 tstamp)
+{
+ if (tp->tstamp_ok) {
+ *ptr++ = __constant_htonl((TCPOPT_NOP << 24) |
+ (TCPOPT_NOP << 16) |
+ (TCPOPT_TIMESTAMP << 8) |
+ TCPOLEN_TIMESTAMP);
+ *ptr++ = htonl(tstamp);
+ *ptr++ = htonl(tp->ts_recent);
+ }
+ if(tp->sack_ok && tp->num_sacks) {
+ int this_sack;
+
+ *ptr++ = __constant_htonl((TCPOPT_NOP << 24) |
+ (TCPOPT_NOP << 16) |
+ (TCPOPT_SACK << 8) |
+ (TCPOLEN_SACK_BASE +
+ (tp->num_sacks * TCPOLEN_SACK_PERBLOCK)));
+ for(this_sack = 0; this_sack < tp->num_sacks; this_sack++) {
+ *ptr++ = htonl(tp->selective_acks[this_sack].start_seq);
+ *ptr++ = htonl(tp->selective_acks[this_sack].end_seq);
+ }
+ }
+}
+
+/* Construct a tcp options header for a SYN or SYN_ACK packet.
+ * If this is every changed make sure to change the definition of
+ * MAX_SYN_SIZE to match the new maximum number of options that you
+ * can generate.
+ */
+extern __inline__ void tcp_syn_build_options(__u32 *ptr, int mss, int ts, int sack,
+ int offer_wscale, int wscale, __u32 tstamp, __u32 ts_recent)
+{
+ /* We always get an MSS option.
+ * The option bytes which will be seen in normal data
+ * packets should timestamps be used, must be in the MSS
+ * advertised. But we subtract them from sk->mss so
+ * that calculations in tcp_sendmsg are simpler etc.
+ * So account for this fact here if necessary. If we
+ * don't do this correctly, as a receiver we won't
+ * recognize data packets as being full sized when we
+ * should, and thus we won't abide by the delayed ACK
+ * rules correctly.
+ * SACKs don't matter, we never delay an ACK when we
+ * have any of those going out.
+ */
+ *ptr++ = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | mss);
+ if (ts) {
+ if(sack)
+ *ptr++ = __constant_htonl((TCPOPT_SACK_PERM << 24) | (TCPOLEN_SACK_PERM << 16) |
+ (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
+ else
+ *ptr++ = __constant_htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
+ (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
+ *ptr++ = htonl(tstamp); /* TSVAL */
+ *ptr++ = htonl(ts_recent); /* TSECR */
+ } else if(sack)
+ *ptr++ = __constant_htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
+ (TCPOPT_SACK_PERM << 8) | TCPOLEN_SACK_PERM);
+ if (offer_wscale)
+ *ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_WINDOW << 16) | (TCPOLEN_WINDOW << 8) | (wscale));
+}
+
+/* Determine a window scaling and initial window to offer.
+ * Based on the assumption that the given amount of space
+ * will be offered. Store the results in the tp structure.
+ * NOTE: for smooth operation initial space offering should
+ * be a multiple of mss if possible. We assume here that mss >= 1.
+ * This MUST be enforced by all callers.
+ */
+extern __inline__ void tcp_select_initial_window(__u32 space, __u16 mss,
+ __u32 *rcv_wnd,
+ __u32 *window_clamp,
+ int wscale_ok,
+ __u8 *rcv_wscale)
+{
+ /* If no clamp set the clamp to the max possible scaled window */
+ if (*window_clamp == 0)
+ (*window_clamp) = (65535<<14);
+ space = min(*window_clamp,space);
+
+ /* Quantize space offering to a multiple of mss if possible. */
+ if (space > mss)
+ space = (space/mss)*mss;
+
+ /* NOTE: offering an initial window larger than 32767
+ * will break some buggy TCP stacks. We try to be nice.
+ * If we are not window scaling, then this truncates
+ * our initial window offering to 32k. There should also
+ * be a sysctl option to stop being nice.
+ */
+ (*rcv_wnd) = min(space, MAX_WINDOW);
+ (*rcv_wscale) = 0;
+ if (wscale_ok) {
+ /* See RFC1323 for an explanation of the limit to 14 */
+ while (space > 65535 && (*rcv_wscale) < 14) {
+ space >>= 1;
+ (*rcv_wscale)++;
+ }
+ }
+ /* Set the clamp no higher than max representable value */
+ (*window_clamp) = min(65535<<(*rcv_wscale),*window_clamp);
+}
+
+extern __inline__ void tcp_synq_unlink(struct tcp_opt *tp, struct open_request *req, struct open_request *prev)
+{
+ if(!req->dl_next)
+ tp->syn_wait_last = (struct open_request **)prev;
+ prev->dl_next = req->dl_next;
+}
+
+extern __inline__ void tcp_synq_queue(struct tcp_opt *tp, struct open_request *req)
+{
+ req->dl_next = NULL;
+ *tp->syn_wait_last = req;
+ tp->syn_wait_last = &req->dl_next;
+}
+
+extern __inline__ void tcp_synq_init(struct tcp_opt *tp)
+{
+ tp->syn_wait_queue = NULL;
+ tp->syn_wait_last = &tp->syn_wait_queue;
+}
+
+extern void __tcp_inc_slow_timer(struct tcp_sl_timer *slt);
+extern __inline__ void tcp_inc_slow_timer(int timer)
+{
+ struct tcp_sl_timer *slt = &tcp_slt_array[timer];
+
+ if (atomic_read(&slt->count) == 0)
+ {
+ __tcp_inc_slow_timer(slt);
+ }
+
+ atomic_inc(&slt->count);
+}
+
+extern __inline__ void tcp_dec_slow_timer(int timer)
+{
+ struct tcp_sl_timer *slt = &tcp_slt_array[timer];
+
+ atomic_dec(&slt->count);
+}
+
+extern const char timer_bug_msg[];
+
+static inline void tcp_clear_xmit_timer(struct sock *sk, int what)
+{
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+ struct timer_list *timer;
+
+ switch (what) {
+ case TIME_RETRANS:
+ timer = &tp->retransmit_timer;
+ break;
+ case TIME_DACK:
+ timer = &tp->delack_timer;
+ break;
+ case TIME_PROBE0:
+ timer = &tp->probe_timer;
+ break;
+ default:
+ printk("%s", timer_bug_msg);
+ return;
+ };
+ if(timer->prev != NULL)
+ del_timer(timer);
+}
+
+static inline int tcp_timer_is_set(struct sock *sk, int what)
+{
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+
+ switch (what) {
+ case TIME_RETRANS:
+ return tp->retransmit_timer.prev != NULL;
+ break;
+ case TIME_DACK:
+ return tp->delack_timer.prev != NULL;
+ break;
+ case TIME_PROBE0:
+ return tp->probe_timer.prev != NULL;
+ break;
+ default:
+ printk("%s", timer_bug_msg);
+ };
+ return 0;
+}
+
+
+#endif /* _TCP_H */
diff --git a/pfinet/linux-src/include/net/transp_v6.h b/pfinet/linux-src/include/net/transp_v6.h
new file mode 100644
index 00000000..d49bc86e
--- /dev/null
+++ b/pfinet/linux-src/include/net/transp_v6.h
@@ -0,0 +1,46 @@
+#ifndef _TRANSP_V6_H
+#define _TRANSP_V6_H
+
+#include <net/checksum.h>
+
+/*
+ * IPv6 transport protocols
+ */
+
+#ifdef __KERNEL__
+
+extern struct proto rawv6_prot;
+extern struct proto udpv6_prot;
+extern struct proto tcpv6_prot;
+
+struct flowi;
+
+extern void rawv6_init(void);
+extern void udpv6_init(void);
+extern void tcpv6_init(void);
+
+extern int udpv6_connect(struct sock *sk,
+ struct sockaddr *uaddr,
+ int addr_len);
+
+extern int datagram_recv_ctl(struct sock *sk,
+ struct msghdr *msg,
+ struct sk_buff *skb);
+
+extern int datagram_send_ctl(struct msghdr *msg,
+ struct flowi *fl,
+ struct ipv6_txoptions *opt,
+ int *hlimit);
+
+#define LOOPBACK4_IPV6 __constant_htonl(0x7f000006)
+
+/*
+ * address family specific functions
+ */
+extern struct tcp_func ipv4_specific;
+
+extern int inet6_destroy_sock(struct sock *sk);
+
+#endif
+
+#endif
diff --git a/pfinet/linux-src/include/net/udp.h b/pfinet/linux-src/include/net/udp.h
new file mode 100644
index 00000000..f3ceadb5
--- /dev/null
+++ b/pfinet/linux-src/include/net/udp.h
@@ -0,0 +1,66 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the UDP module.
+ *
+ * Version: @(#)udp.h 1.0.2 05/07/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * Fixes:
+ * Alan Cox : Turned on udp checksums. I don't want to
+ * chase 'memory corruption' bugs that aren't!
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _UDP_H
+#define _UDP_H
+
+#include <linux/udp.h>
+#include <net/sock.h>
+
+#define UDP_HTABLE_SIZE 128
+
+/* udp.c: This needs to be shared by v4 and v6 because the lookup
+ * and hashing code needs to work with different AF's yet
+ * the port space is shared.
+ */
+extern struct sock *udp_hash[UDP_HTABLE_SIZE];
+
+#define UDP_NO_CHECK 0
+
+extern int udp_port_rover;
+
+static inline int udp_lport_inuse(u16 num)
+{
+ struct sock *sk = udp_hash[num & (UDP_HTABLE_SIZE - 1)];
+
+ for(; sk != NULL; sk = sk->next) {
+ if(sk->num == num)
+ return 1;
+ }
+ return 0;
+}
+
+extern struct proto udp_prot;
+
+
+extern void udp_err(struct sk_buff *, unsigned char *, int);
+extern int udp_connect(struct sock *sk,
+ struct sockaddr *usin, int addr_len);
+
+extern int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len);
+
+extern int udp_rcv(struct sk_buff *skb, unsigned short len);
+extern int udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+
+/* CONFIG_IP_TRANSPARENT_PROXY */
+extern int udp_chkaddr(struct sk_buff *skb);
+
+#endif /* _UDP_H */
diff --git a/pfinet/linux-src/include/net/x25.h b/pfinet/linux-src/include/net/x25.h
new file mode 100644
index 00000000..5ac507bd
--- /dev/null
+++ b/pfinet/linux-src/include/net/x25.h
@@ -0,0 +1,221 @@
+/*
+ * Declarations of X.25 Packet Layer type objects.
+ *
+ * Jonathan Naylor 17/11/96
+ */
+
+#ifndef _X25_H
+#define _X25_H
+#include <linux/x25.h>
+
+#define X25_ADDR_LEN 16
+
+#define X25_MAX_L2_LEN 18 /* 802.2 LLC */
+
+#define X25_STD_MIN_LEN 3
+#define X25_EXT_MIN_LEN 4
+
+#define X25_GFI_SEQ_MASK 0x30
+#define X25_GFI_STDSEQ 0x10
+#define X25_GFI_EXTSEQ 0x20
+
+#define X25_Q_BIT 0x80
+#define X25_D_BIT 0x40
+#define X25_STD_M_BIT 0x10
+#define X25_EXT_M_BIT 0x01
+
+#define X25_CALL_REQUEST 0x0B
+#define X25_CALL_ACCEPTED 0x0F
+#define X25_CLEAR_REQUEST 0x13
+#define X25_CLEAR_CONFIRMATION 0x17
+#define X25_DATA 0x00
+#define X25_INTERRUPT 0x23
+#define X25_INTERRUPT_CONFIRMATION 0x27
+#define X25_RR 0x01
+#define X25_RNR 0x05
+#define X25_REJ 0x09
+#define X25_RESET_REQUEST 0x1B
+#define X25_RESET_CONFIRMATION 0x1F
+#define X25_REGISTRATION_REQUEST 0xF3
+#define X25_REGISTRATION_CONFIRMATION 0xF7
+#define X25_RESTART_REQUEST 0xFB
+#define X25_RESTART_CONFIRMATION 0xFF
+#define X25_DIAGNOSTIC 0xF1
+#define X25_ILLEGAL 0xFD
+
+/* Define the various conditions that may exist */
+
+#define X25_COND_ACK_PENDING 0x01
+#define X25_COND_OWN_RX_BUSY 0x02
+#define X25_COND_PEER_RX_BUSY 0x04
+
+/* Define Link State constants. */
+enum {
+ X25_STATE_0, /* Ready */
+ X25_STATE_1, /* Awaiting Call Accepted */
+ X25_STATE_2, /* Awaiting Clear Confirmation */
+ X25_STATE_3, /* Data Transfer */
+ X25_STATE_4 /* Awaiting Reset Confirmation */
+};
+
+enum {
+ X25_LINK_STATE_0,
+ X25_LINK_STATE_1,
+ X25_LINK_STATE_2,
+ X25_LINK_STATE_3
+};
+
+#define X25_DEFAULT_T20 (180 * HZ) /* Default T20 value */
+#define X25_DEFAULT_T21 (200 * HZ) /* Default T21 value */
+#define X25_DEFAULT_T22 (180 * HZ) /* Default T22 value */
+#define X25_DEFAULT_T23 (180 * HZ) /* Default T23 value */
+#define X25_DEFAULT_T2 (3 * HZ) /* Default ack holdback value */
+
+#define X25_DEFAULT_WINDOW_SIZE 2 /* Default Window Size */
+#define X25_DEFAULT_PACKET_SIZE X25_PS128 /* Default Packet Size */
+#define X25_DEFAULT_THROUGHPUT 0x0A /* Deafult Throughput */
+#define X25_DEFAULT_REVERSE 0x00 /* Default Reverse Charging */
+
+#define X25_SMODULUS 8
+#define X25_EMODULUS 128
+
+/*
+ * X.25 Facilities constants.
+ */
+
+#define X25_FAC_CLASS_MASK 0xC0
+
+#define X25_FAC_CLASS_A 0x00
+#define X25_FAC_CLASS_B 0x40
+#define X25_FAC_CLASS_C 0x80
+#define X25_FAC_CLASS_D 0xC0
+
+#define X25_FAC_REVERSE 0x01
+#define X25_FAC_THROUGHPUT 0x02
+#define X25_FAC_PACKET_SIZE 0x42
+#define X25_FAC_WINDOW_SIZE 0x43
+
+#define X25_MAX_FAC_LEN 20 /* Plenty to spare */
+#define X25_MAX_CUD_LEN 128
+
+struct x25_route {
+ struct x25_route *next;
+ x25_address address; /* Start of address range */
+ unsigned int sigdigits; /* Number of sig digits */
+ struct device *dev; /* More than one for MLP */
+};
+
+struct x25_neigh {
+ struct x25_neigh *next;
+ struct device *dev;
+ unsigned int state;
+ unsigned int extended;
+ struct sk_buff_head queue;
+ unsigned long t20;
+ struct timer_list t20timer;
+};
+
+typedef struct {
+ x25_address source_addr, dest_addr;
+ struct x25_neigh *neighbour;
+ unsigned int lci;
+ unsigned char state, condition, qbitincl, intflag;
+ unsigned short vs, vr, va, vl;
+ unsigned long t2, t21, t22, t23;
+ unsigned short fraglen;
+ struct sk_buff_head ack_queue;
+ struct sk_buff_head fragment_queue;
+ struct sk_buff_head interrupt_in_queue;
+ struct sk_buff_head interrupt_out_queue;
+ struct sock *sk; /* Backlink to socket */
+ struct timer_list timer;
+ struct x25_causediag causediag;
+ struct x25_facilities facilities;
+ struct x25_calluserdata calluserdata;
+} x25_cb;
+
+/* af_x25.c */
+extern int sysctl_x25_restart_request_timeout;
+extern int sysctl_x25_call_request_timeout;
+extern int sysctl_x25_reset_request_timeout;
+extern int sysctl_x25_clear_request_timeout;
+extern int sysctl_x25_ack_holdback_timeout;
+
+extern int x25_addr_ntoa(unsigned char *, x25_address *, x25_address *);
+extern int x25_addr_aton(unsigned char *, x25_address *, x25_address *);
+extern unsigned int x25_new_lci(struct x25_neigh *);
+extern struct sock *x25_find_socket(unsigned int, struct x25_neigh *);
+extern void x25_destroy_socket(struct sock *);
+extern int x25_rx_call_request(struct sk_buff *, struct x25_neigh *, unsigned int);
+extern void x25_kill_by_neigh(struct x25_neigh *);
+
+#include <net/x25call.h>
+
+/* x25_dev.c */
+extern void x25_send_frame(struct sk_buff *, struct x25_neigh *);
+extern int x25_lapb_receive_frame(struct sk_buff *, struct device *, struct packet_type *);
+extern int x25_llc_receive_frame(struct sk_buff *, struct device *, struct packet_type *);
+extern void x25_establish_link(struct x25_neigh *);
+extern void x25_terminate_link(struct x25_neigh *);
+
+/* x25_facilities.c */
+extern int x25_parse_facilities(struct sk_buff *, struct x25_facilities *);
+extern int x25_create_facilities(unsigned char *, struct x25_facilities *);
+extern int x25_negotiate_facilities(struct sk_buff *, struct sock *, struct x25_facilities *);
+extern void x25_limit_facilities(struct x25_facilities *, struct x25_neigh *);
+
+/* x25_in.c */
+extern int x25_process_rx_frame(struct sock *, struct sk_buff *);
+
+/* x25_link.c */
+extern void x25_link_control(struct sk_buff *, struct x25_neigh *, unsigned short);
+extern void x25_link_device_up(struct device *);
+extern void x25_link_device_down(struct device *);
+extern void x25_link_established(struct x25_neigh *);
+extern void x25_link_terminated(struct x25_neigh *);
+extern void x25_transmit_restart_request(struct x25_neigh *);
+extern void x25_transmit_restart_confirmation(struct x25_neigh *);
+extern void x25_transmit_diagnostic(struct x25_neigh *, unsigned char);
+extern void x25_transmit_clear_request(struct x25_neigh *, unsigned int, unsigned char);
+extern void x25_transmit_link(struct sk_buff *, struct x25_neigh *);
+extern int x25_subscr_ioctl(unsigned int, void *);
+extern struct x25_neigh *x25_get_neigh(struct device *);
+extern void x25_link_free(void);
+
+/* x25_out.c */
+extern void x25_output(struct sock *, struct sk_buff *);
+extern void x25_kick(struct sock *);
+extern void x25_enquiry_response(struct sock *);
+
+/* x25_route.c */
+extern struct device *x25_get_route(x25_address *);
+extern struct device *x25_dev_get(char *);
+extern void x25_route_device_down(struct device *);
+extern int x25_route_ioctl(unsigned int, void *);
+extern int x25_routes_get_info(char *, char **, off_t, int, int);
+extern void x25_route_free(void);
+
+/* x25_subr.c */
+extern void x25_clear_queues(struct sock *);
+extern void x25_frames_acked(struct sock *, unsigned short);
+extern void x25_requeue_frames(struct sock *);
+extern int x25_validate_nr(struct sock *, unsigned short);
+extern void x25_write_internal(struct sock *, int);
+extern int x25_decode(struct sock *, struct sk_buff *, int *, int *, int *, int *, int *);
+extern void x25_disconnect(struct sock *, int, unsigned char, unsigned char);
+
+/* x25_timer.c */
+extern void x25_start_heartbeat(struct sock *);
+extern void x25_start_t2timer(struct sock *);
+extern void x25_start_t21timer(struct sock *);
+extern void x25_start_t22timer(struct sock *);
+extern void x25_start_t23timer(struct sock *);
+extern void x25_stop_heartbeat(struct sock *);
+extern void x25_stop_timer(struct sock *);
+extern unsigned long x25_display_timer(struct sock *);
+
+/* sysctl_net_x25.c */
+extern void x25_register_sysctl(void);
+extern void x25_unregister_sysctl(void);
+
+#endif
diff --git a/pfinet/linux-src/include/net/x25call.h b/pfinet/linux-src/include/net/x25call.h
new file mode 100644
index 00000000..7c478a1d
--- /dev/null
+++ b/pfinet/linux-src/include/net/x25call.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void x25_proto_init(struct net_proto *pro);
diff --git a/pfinet/linux-src/net/core/Makefile b/pfinet/linux-src/net/core/Makefile
new file mode 100644
index 00000000..5df65cd2
--- /dev/null
+++ b/pfinet/linux-src/net/core/Makefile
@@ -0,0 +1,41 @@
+#
+# Makefile for the Linux networking core.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := core.o
+
+O_OBJS := sock.o skbuff.o iovec.o datagram.o scm.o
+
+ifeq ($(CONFIG_SYSCTL),y)
+ifeq ($(CONFIG_NET),y)
+O_OBJS += sysctl_net_core.o
+endif
+endif
+
+ifdef CONFIG_FILTER
+O_OBJS += filter.o
+endif
+
+ifdef CONFIG_NET
+
+O_OBJS += dev.o dev_mcast.o dst.o neighbour.o rtnetlink.o utils.o
+
+ifdef CONFIG_FIREWALL
+OX_OBJS += firewall.o
+endif
+
+endif
+
+ifdef CONFIG_NET_PROFILE
+OX_OBJS += profile.o
+endif
+
+include $(TOPDIR)/Rules.make
+
+tar:
+ tar -cvf /dev/f1 .
diff --git a/pfinet/linux-src/net/core/datagram.c b/pfinet/linux-src/net/core/datagram.c
new file mode 100644
index 00000000..9bb68fa4
--- /dev/null
+++ b/pfinet/linux-src/net/core/datagram.c
@@ -0,0 +1,249 @@
+/*
+ * SUCS NET3:
+ *
+ * Generic datagram handling routines. These are generic for all protocols. Possibly a generic IP version on top
+ * of these would make sense. Not tonight however 8-).
+ * This is used because UDP, RAW, PACKET, DDP, IPX, AX.25 and NetROM layer all have identical poll code and mostly
+ * identical recvmsg() code. So we share it here. The poll was shared before but buried in udp.c so I moved it.
+ *
+ * Authors: Alan Cox <alan@redhat.com>. (datagram_poll() from old udp.c code)
+ *
+ * Fixes:
+ * Alan Cox : NULL return from skb_peek_copy() understood
+ * Alan Cox : Rewrote skb_read_datagram to avoid the skb_peek_copy stuff.
+ * Alan Cox : Added support for SOCK_SEQPACKET. IPX can no longer use the SO_TYPE hack but
+ * AX.25 now works right, and SPX is feasible.
+ * Alan Cox : Fixed write poll of non IP protocol crash.
+ * Florian La Roche: Changed for my new skbuff handling.
+ * Darryl Miles : Fixed non-blocking SOCK_SEQPACKET.
+ * Linus Torvalds : BSD semantic fixes.
+ * Alan Cox : Datagram iovec handling
+ * Darryl Miles : Fixed non-blocking SOCK_STREAM.
+ * Alan Cox : POSIXisms
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/poll.h>
+
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+
+
+/*
+ * Wait for a packet..
+ *
+ * Interrupts off so that no packet arrives before we begin sleeping.
+ * Otherwise we might miss our wake up
+ */
+
+static inline void wait_for_packet(struct sock * sk)
+{
+ struct wait_queue wait = { current, NULL };
+
+ add_wait_queue(sk->sleep, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+
+ if (skb_peek(&sk->receive_queue) == NULL)
+ schedule();
+
+ current->state = TASK_RUNNING;
+ remove_wait_queue(sk->sleep, &wait);
+}
+
+/*
+ * Is a socket 'connection oriented' ?
+ */
+
+static inline int connection_based(struct sock *sk)
+{
+ return (sk->type==SOCK_SEQPACKET || sk->type==SOCK_STREAM);
+}
+
+/*
+ * Get a datagram skbuff, understands the peeking, nonblocking wakeups and possible
+ * races. This replaces identical code in packet,raw and udp, as well as the IPX
+ * AX.25 and Appletalk. It also finally fixes the long standing peek and read
+ * race for datagram sockets. If you alter this routine remember it must be
+ * re-entrant.
+ *
+ * This function will lock the socket if a skb is returned, so the caller
+ * needs to unlock the socket in that case (usually by calling skb_free_datagram)
+ *
+ * * It does not lock socket since today. This function is
+ * * free of race conditions. This measure should/can improve
+ * * significantly datagram socket latencies at high loads,
+ * * when data copying to user space takes lots of time.
+ * * (BTW I've just killed the last cli() in IP/IPv6/core/netlink/packet
+ * * 8) Great win.)
+ * * --ANK (980729)
+ *
+ * The order of the tests when we find no data waiting are specified
+ * quite explicitly by POSIX 1003.1g, don't change them without having
+ * the standard around please.
+ */
+
+struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, int *err)
+{
+ int error;
+ struct sk_buff *skb;
+
+ /* Caller is allowed not to check sk->err before skb_recv_datagram() */
+ error = sock_error(sk);
+ if (error)
+ goto no_packet;
+
+restart:
+ while(skb_queue_empty(&sk->receive_queue)) /* No data */
+ {
+ /* Socket errors? */
+ error = sock_error(sk);
+ if (error)
+ goto no_packet;
+
+ /* Socket shut down? */
+ if (sk->shutdown & RCV_SHUTDOWN)
+ goto no_packet;
+
+ /* Sequenced packets can come disconnected. If so we report the problem */
+ error = -ENOTCONN;
+ if(connection_based(sk) && sk->state!=TCP_ESTABLISHED)
+ goto no_packet;
+
+ /* handle signals */
+ error = -ERESTARTSYS;
+ if (signal_pending(current))
+ goto no_packet;
+
+ /* User doesn't want to wait */
+ error = -EAGAIN;
+ if (noblock)
+ goto no_packet;
+
+ wait_for_packet(sk);
+ }
+
+ /* Again only user level code calls this function, so nothing interrupt level
+ will suddenly eat the receive_queue */
+ if (flags & MSG_PEEK)
+ {
+ unsigned long cpu_flags;
+
+ /* It is the only POTENTIAL race condition
+ in this function. skb may be stolen by
+ another receiver after peek, but before
+ incrementing use count, provided kernel
+ is reentearble (it is not) or this function
+ is called by interrupts.
+
+ Protect it with global skb spinlock,
+ though for now even this is overkill.
+ --ANK (980728)
+ */
+ spin_lock_irqsave(&skb_queue_lock, cpu_flags);
+ skb = skb_peek(&sk->receive_queue);
+ if(skb!=NULL)
+ atomic_inc(&skb->users);
+ spin_unlock_irqrestore(&skb_queue_lock, cpu_flags);
+ } else
+ skb = skb_dequeue(&sk->receive_queue);
+
+ if (!skb) /* Avoid race if someone beats us to the data */
+ goto restart;
+ return skb;
+
+no_packet:
+ *err = error;
+ return NULL;
+}
+
+void skb_free_datagram(struct sock * sk, struct sk_buff *skb)
+{
+ kfree_skb(skb);
+}
+
+/*
+ * Copy a datagram to a linear buffer.
+ */
+
+int skb_copy_datagram(struct sk_buff *skb, int offset, char *to, int size)
+{
+ int err = -EFAULT;
+
+ if (!copy_to_user(to, skb->h.raw + offset, size))
+ err = 0;
+ return err;
+}
+
+
+/*
+ * Copy a datagram to an iovec.
+ * Note: the iovec is modified during the copy.
+ */
+
+int skb_copy_datagram_iovec(struct sk_buff *skb, int offset, struct iovec *to,
+ int size)
+{
+ return memcpy_toiovec(to, skb->h.raw + offset, size);
+}
+
+/*
+ * Datagram poll: Again totally generic. This also handles
+ * sequenced packet sockets providing the socket receive queue
+ * is only ever holding data ready to receive.
+ *
+ * Note: when you _don't_ use this routine for this protocol,
+ * and you use a different write policy from sock_writeable()
+ * then please supply your own write_space callback.
+ */
+
+unsigned int datagram_poll(struct file * file, struct socket *sock, poll_table *wait)
+{
+ struct sock *sk = sock->sk;
+ unsigned int mask;
+
+ poll_wait(file, sk->sleep, wait);
+ mask = 0;
+
+ /* exceptional events? */
+ if (sk->err || !skb_queue_empty(&sk->error_queue))
+ mask |= POLLERR;
+ if (sk->shutdown & RCV_SHUTDOWN)
+ mask |= POLLHUP;
+
+ /* readable? */
+ if (!skb_queue_empty(&sk->receive_queue))
+ mask |= POLLIN | POLLRDNORM;
+
+ /* Connection-based need to check for termination and startup */
+ if (connection_based(sk)) {
+ if (sk->state==TCP_CLOSE)
+ mask |= POLLHUP;
+ /* connection hasn't started yet? */
+ if (sk->state == TCP_SYN_SENT)
+ return mask;
+ }
+
+ /* writable? */
+ if (sock_writeable(sk))
+ mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+ else
+ sk->socket->flags |= SO_NOSPACE;
+
+ return mask;
+}
diff --git a/pfinet/linux-src/net/core/dev.c b/pfinet/linux-src/net/core/dev.c
new file mode 100644
index 00000000..b47c5027
--- /dev/null
+++ b/pfinet/linux-src/net/core/dev.c
@@ -0,0 +1,2074 @@
+/*
+ * NET3 Protocol independent device support routines.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Derived from the non IP parts of dev.c 1.0.19
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ *
+ * Additional Authors:
+ * Florian la Roche <rzsfl@rz.uni-sb.de>
+ * Alan Cox <gw4pts@gw4pts.ampr.org>
+ * David Hinds <dhinds@allegro.stanford.edu>
+ * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ * Adam Sulmicki <adam@cfar.umd.edu>
+ *
+ * Changes:
+ * Marcelo Tosatti <marcelo@conectiva.com.br> : dont accept mtu 0 or <
+ * Alan Cox : device private ioctl copies fields back.
+ * Alan Cox : Transmit queue code does relevant stunts to
+ * keep the queue safe.
+ * Alan Cox : Fixed double lock.
+ * Alan Cox : Fixed promisc NULL pointer trap
+ * ???????? : Support the full private ioctl range
+ * Alan Cox : Moved ioctl permission check into drivers
+ * Tim Kordas : SIOCADDMULTI/SIOCDELMULTI
+ * Alan Cox : 100 backlog just doesn't cut it when
+ * you start doing multicast video 8)
+ * Alan Cox : Rewrote net_bh and list manager.
+ * Alan Cox : Fix ETH_P_ALL echoback lengths.
+ * Alan Cox : Took out transmit every packet pass
+ * Saved a few bytes in the ioctl handler
+ * Alan Cox : Network driver sets packet type before calling netif_rx. Saves
+ * a function call a packet.
+ * Alan Cox : Hashed net_bh()
+ * Richard Kooijman: Timestamp fixes.
+ * Alan Cox : Wrong field in SIOCGIFDSTADDR
+ * Alan Cox : Device lock protection.
+ * Alan Cox : Fixed nasty side effect of device close changes.
+ * Rudi Cilibrasi : Pass the right thing to set_mac_address()
+ * Dave Miller : 32bit quantity for the device lock to make it work out
+ * on a Sparc.
+ * Bjorn Ekwall : Added KERNELD hack.
+ * Alan Cox : Cleaned up the backlog initialise.
+ * Craig Metz : SIOCGIFCONF fix if space for under
+ * 1 device.
+ * Thomas Bogendoerfer : Return ENODEV for dev_open, if there
+ * is no device open function.
+ * Andi Kleen : Fix error reporting for SIOCGIFCONF
+ * Michael Chastain : Fix signed/unsigned for SIOCGIFCONF
+ * Cyrus Durgin : Cleaned for KMOD
+ * Adam Sulmicki : Bug Fix : Network Device Unload
+ * A network device unload needs to purge
+ * the backlog queue.
+ * Paul Rusty Russel : SIOCSIFNAME
+ * Andrea Arcangeli : dev_clear_backlog() needs the
+ * skb_queue_lock held.
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/notifier.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <linux/rtnetlink.h>
+#include <net/slhc.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <net/br.h>
+#include <net/dst.h>
+#include <net/pkt_sched.h>
+#include <net/profile.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h>
+#endif /* CONFIG_NET_RADIO */
+#ifdef CONFIG_PLIP
+extern int plip_init(void);
+#endif
+
+NET_PROFILE_DEFINE(dev_queue_xmit)
+NET_PROFILE_DEFINE(net_bh)
+NET_PROFILE_DEFINE(net_bh_skb)
+
+
+const char *if_port_text[] = {
+ "unknown",
+ "BNC",
+ "10baseT",
+ "AUI",
+ "100baseT",
+ "100baseTX",
+ "100baseFX"
+};
+
+/*
+ * The list of packet types we will receive (as opposed to discard)
+ * and the routines to invoke.
+ *
+ * Why 16. Because with 16 the only overlap we get on a hash of the
+ * low nibble of the protocol value is RARP/SNAP/X.25.
+ *
+ * 0800 IP
+ * 0001 802.3
+ * 0002 AX.25
+ * 0004 802.2
+ * 8035 RARP
+ * 0005 SNAP
+ * 0805 X.25
+ * 0806 ARP
+ * 8137 IPX
+ * 0009 Localtalk
+ * 86DD IPv6
+ */
+
+struct packet_type *ptype_base[16]; /* 16 way hashed list */
+struct packet_type *ptype_all = NULL; /* Taps */
+
+/*
+ * Device list lock. Setting it provides that interface
+ * will not disappear unexpectedly while kernel sleeps.
+ */
+
+atomic_t dev_lockct = ATOMIC_INIT(0);
+
+/*
+ * Our notifier list
+ */
+
+#ifdef _HURD_
+struct notifier_block *netdev_chain=NULL;
+#else
+static struct notifier_block *netdev_chain=NULL;
+#endif
+
+/*
+ * Device drivers call our routines to queue packets here. We empty the
+ * queue in the bottom half handler.
+ */
+
+static struct sk_buff_head backlog;
+
+#ifdef CONFIG_NET_FASTROUTE
+int netdev_fastroute;
+int netdev_fastroute_obstacles;
+struct net_fastroute_stats dev_fastroute_stat;
+#endif
+
+static void dev_clear_backlog(struct device *dev);
+
+
+/******************************************************************************************
+
+ Protocol management and registration routines
+
+*******************************************************************************************/
+
+/*
+ * For efficiency
+ */
+
+int netdev_nit=0;
+
+/*
+ * Add a protocol ID to the list. Now that the input handler is
+ * smarter we can dispense with all the messy stuff that used to be
+ * here.
+ *
+ * BEWARE!!! Protocol handlers, mangling input packets,
+ * MUST BE last in hash buckets and checking protocol handlers
+ * MUST start from promiscuous ptype_all chain in net_bh.
+ * It is true now, do not change it.
+ * Explantion follows: if protocol handler, mangling packet, will
+ * be the first on list, it is not able to sense, that packet
+ * is cloned and should be copied-on-write, so that it will
+ * change it and subsequent readers will get broken packet.
+ * --ANK (980803)
+ */
+
+void dev_add_pack(struct packet_type *pt)
+{
+ int hash;
+#ifdef CONFIG_NET_FASTROUTE
+ /* Hack to detect packet socket */
+ if (pt->data) {
+ netdev_fastroute_obstacles++;
+ dev_clear_fastroute(pt->dev);
+ }
+#endif
+ if(pt->type==htons(ETH_P_ALL))
+ {
+ netdev_nit++;
+ pt->next=ptype_all;
+ ptype_all=pt;
+ }
+ else
+ {
+ hash=ntohs(pt->type)&15;
+ pt->next = ptype_base[hash];
+ ptype_base[hash] = pt;
+ }
+}
+
+
+/*
+ * Remove a protocol ID from the list.
+ */
+
+void dev_remove_pack(struct packet_type *pt)
+{
+ struct packet_type **pt1;
+ if(pt->type==htons(ETH_P_ALL))
+ {
+ netdev_nit--;
+ pt1=&ptype_all;
+ }
+ else
+ pt1=&ptype_base[ntohs(pt->type)&15];
+ for(; (*pt1)!=NULL; pt1=&((*pt1)->next))
+ {
+ if(pt==(*pt1))
+ {
+ *pt1=pt->next;
+ synchronize_bh();
+#ifdef CONFIG_NET_FASTROUTE
+ if (pt->data)
+ netdev_fastroute_obstacles--;
+#endif
+ return;
+ }
+ }
+ printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt);
+}
+
+/*****************************************************************************************
+
+ Device Interface Subroutines
+
+******************************************************************************************/
+
+/*
+ * Find an interface by name.
+ */
+
+struct device *dev_get(const char *name)
+{
+ struct device *dev;
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if (strcmp(dev->name, name) == 0)
+ return(dev);
+ }
+ return NULL;
+}
+
+struct device * dev_get_by_index(int ifindex)
+{
+ struct device *dev;
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if (dev->ifindex == ifindex)
+ return(dev);
+ }
+ return NULL;
+}
+
+struct device *dev_getbyhwaddr(unsigned short type, char *ha)
+{
+ struct device *dev;
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if (dev->type == type &&
+ memcmp(dev->dev_addr, ha, dev->addr_len) == 0)
+ return(dev);
+ }
+ return(NULL);
+}
+
+/*
+ * Passed a format string - eg "lt%d" it will try and find a suitable
+ * id. Not efficient for many devices, not called a lot..
+ */
+
+int dev_alloc_name(struct device *dev, const char *name)
+{
+ int i;
+ /*
+ * If you need over 100 please also fix the algorithm...
+ */
+ for(i=0;i<100;i++)
+ {
+ sprintf(dev->name,name,i);
+ if(dev_get(dev->name)==NULL)
+ return i;
+ }
+ return -ENFILE; /* Over 100 of the things .. bail out! */
+}
+
+struct device *dev_alloc(const char *name, int *err)
+{
+ struct device *dev=kmalloc(sizeof(struct device)+16, GFP_KERNEL);
+ if(dev==NULL)
+ {
+ *err=-ENOBUFS;
+ return NULL;
+ }
+ dev->name=(char *)(dev+1); /* Name string space */
+ *err=dev_alloc_name(dev,name);
+ if(*err<0)
+ {
+ kfree(dev);
+ return NULL;
+ }
+ return dev;
+}
+
+void netdev_state_change(struct device *dev)
+{
+ if (dev->flags&IFF_UP)
+ notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev);
+}
+
+
+/*
+ * Find and possibly load an interface.
+ */
+
+#ifdef CONFIG_KMOD
+
+void dev_load(const char *name)
+{
+ if(!dev_get(name) && capable(CAP_SYS_MODULE))
+ request_module(name);
+}
+
+#else
+
+extern inline void dev_load(const char *unused){;}
+
+#endif
+
+static int default_rebuild_header(struct sk_buff *skb)
+{
+ printk(KERN_DEBUG "%s: default_rebuild_header called -- BUG!\n", skb->dev ? skb->dev->name : "NULL!!!");
+ kfree_skb(skb);
+ return 1;
+}
+
+/*
+ * Prepare an interface for use.
+ */
+
+int dev_open(struct device *dev)
+{
+ int ret = 0;
+
+ /*
+ * Is it already up?
+ */
+
+ if (dev->flags&IFF_UP)
+ return 0;
+
+ /*
+ * Call device private open method
+ */
+
+ if (dev->open)
+ ret = dev->open(dev);
+
+ /*
+ * If it went open OK then:
+ */
+
+ if (ret == 0)
+ {
+ /*
+ * nil rebuild_header routine,
+ * that should be never called and used as just bug trap.
+ */
+
+ if (dev->rebuild_header == NULL)
+ dev->rebuild_header = default_rebuild_header;
+
+ /*
+ * Set the flags.
+ */
+ dev->flags |= (IFF_UP | IFF_RUNNING);
+
+ /*
+ * Initialize multicasting status
+ */
+ dev_mc_upload(dev);
+
+ /*
+ * Wakeup transmit queue engine
+ */
+ dev_activate(dev);
+
+ /*
+ * ... and announce new interface.
+ */
+ notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
+
+ }
+ return(ret);
+}
+
+#ifdef CONFIG_NET_FASTROUTE
+
+static __inline__ void dev_do_clear_fastroute(struct device *dev)
+{
+ if (dev->accept_fastpath) {
+ int i;
+
+ for (i=0; i<=NETDEV_FASTROUTE_HMASK; i++)
+ dst_release_irqwait(xchg(dev->fastpath+i, NULL));
+ }
+}
+
+void dev_clear_fastroute(struct device *dev)
+{
+ if (dev) {
+ dev_do_clear_fastroute(dev);
+ } else {
+ for (dev = dev_base; dev; dev = dev->next)
+ dev_do_clear_fastroute(dev);
+ }
+}
+#endif
+
+/*
+ * Completely shutdown an interface.
+ */
+
+int dev_close(struct device *dev)
+{
+ if (!(dev->flags&IFF_UP))
+ return 0;
+
+ dev_deactivate(dev);
+
+ dev_lock_wait();
+
+ /*
+ * Call the device specific close. This cannot fail.
+ * Only if device is UP
+ */
+
+ if (dev->stop)
+ dev->stop(dev);
+
+ if (dev->start)
+ printk("dev_close: bug %s still running\n", dev->name);
+
+ /*
+ * Device is now down.
+ */
+ dev_clear_backlog(dev);
+
+ dev->flags&=~(IFF_UP|IFF_RUNNING);
+#ifdef CONFIG_NET_FASTROUTE
+ dev_clear_fastroute(dev);
+#endif
+
+ /*
+ * Tell people we are going down
+ */
+ notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
+
+ return(0);
+}
+
+
+/*
+ * Device change register/unregister. These are not inline or static
+ * as we export them to the world.
+ */
+
+int register_netdevice_notifier(struct notifier_block *nb)
+{
+ return notifier_chain_register(&netdev_chain, nb);
+}
+
+int unregister_netdevice_notifier(struct notifier_block *nb)
+{
+ return notifier_chain_unregister(&netdev_chain,nb);
+}
+
+/*
+ * Support routine. Sends outgoing frames to any network
+ * taps currently in use.
+ */
+
+void dev_queue_xmit_nit(struct sk_buff *skb, struct device *dev)
+{
+ struct packet_type *ptype;
+ get_fast_time(&skb->stamp);
+
+ for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next)
+ {
+ /* Never send packets back to the socket
+ * they originated from - MvS (miquels@drinkel.ow.org)
+ */
+ if ((ptype->dev == dev || !ptype->dev) &&
+ ((struct sock *)ptype->data != skb->sk))
+ {
+ struct sk_buff *skb2;
+ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
+ break;
+
+ /* Code, following below is wrong.
+
+ The only reason, why it does work is that
+ ONLY packet sockets receive outgoing
+ packets. If such a packet will be (occasionally)
+ received by normal packet handler, which expects
+ that mac header is pulled...
+ */
+
+ /* More sensible variant. skb->nh should be correctly
+ set by sender, so that the second statement is
+ just protection against buggy protocols.
+ */
+ skb2->mac.raw = skb2->data;
+
+ if (skb2->nh.raw < skb2->data || skb2->nh.raw >= skb2->tail) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "protocol %04x is buggy, dev %s\n", skb2->protocol, dev->name);
+ skb2->nh.raw = skb2->data;
+ if (dev->hard_header)
+ skb2->nh.raw += dev->hard_header_len;
+ }
+
+ skb2->h.raw = skb2->nh.raw;
+ skb2->pkt_type = PACKET_OUTGOING;
+ ptype->func(skb2, skb->dev, ptype);
+ }
+ }
+}
+
+/*
+ * Fast path for loopback frames.
+ */
+
+void dev_loopback_xmit(struct sk_buff *skb)
+{
+ struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
+ if (newskb==NULL)
+ return;
+
+ newskb->mac.raw = newskb->data;
+ skb_pull(newskb, newskb->nh.raw - newskb->data);
+ newskb->pkt_type = PACKET_LOOPBACK;
+ newskb->ip_summed = CHECKSUM_UNNECESSARY;
+ if (newskb->dst==NULL)
+ printk(KERN_DEBUG "BUG: packet without dst looped back 1\n");
+ netif_rx(newskb);
+}
+
+int dev_queue_xmit(struct sk_buff *skb)
+{
+ struct device *dev = skb->dev;
+ struct Qdisc *q;
+
+#ifdef CONFIG_NET_PROFILE
+ start_bh_atomic();
+ NET_PROFILE_ENTER(dev_queue_xmit);
+#endif
+
+ start_bh_atomic();
+ q = dev->qdisc;
+ if (q->enqueue) {
+ q->enqueue(skb, q);
+ qdisc_wakeup(dev);
+ end_bh_atomic();
+
+#ifdef CONFIG_NET_PROFILE
+ NET_PROFILE_LEAVE(dev_queue_xmit);
+ end_bh_atomic();
+#endif
+
+ return 0;
+ }
+
+ /* The device has no queue. Common case for software devices:
+ loopback, all the sorts of tunnels...
+
+ Really, it is unlikely that bh protection is necessary here:
+ virtual devices do not generate EOI events.
+ However, it is possible, that they rely on bh protection
+ made by us here.
+ */
+ if (dev->flags&IFF_UP) {
+ if (netdev_nit)
+ dev_queue_xmit_nit(skb,dev);
+ if (dev->hard_start_xmit(skb, dev) == 0) {
+ end_bh_atomic();
+
+#ifdef CONFIG_NET_PROFILE
+ NET_PROFILE_LEAVE(dev_queue_xmit);
+ end_bh_atomic();
+#endif
+
+ return 0;
+ }
+ if (net_ratelimit())
+ printk(KERN_DEBUG "Virtual device %s asks to queue packet!\n", dev->name);
+ }
+ end_bh_atomic();
+
+ kfree_skb(skb);
+
+#ifdef CONFIG_NET_PROFILE
+ NET_PROFILE_LEAVE(dev_queue_xmit);
+ end_bh_atomic();
+#endif
+
+ return 0;
+}
+
+
+/*=======================================================================
+ Receiver rotutines
+ =======================================================================*/
+
+int netdev_dropping = 0;
+int netdev_max_backlog = 300;
+atomic_t netdev_rx_dropped;
+#ifdef CONFIG_CPU_IS_SLOW
+int net_cpu_congestion;
+#endif
+
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+int netdev_throttle_events;
+static unsigned long netdev_fc_mask = 1;
+unsigned long netdev_fc_xoff = 0;
+
+static struct
+{
+ void (*stimul)(struct device *);
+ struct device *dev;
+} netdev_fc_slots[32];
+
+int netdev_register_fc(struct device *dev, void (*stimul)(struct device *dev))
+{
+ int bit = 0;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (netdev_fc_mask != ~0UL) {
+ bit = ffz(netdev_fc_mask);
+ netdev_fc_slots[bit].stimul = stimul;
+ netdev_fc_slots[bit].dev = dev;
+ set_bit(bit, &netdev_fc_mask);
+ clear_bit(bit, &netdev_fc_xoff);
+ }
+ restore_flags(flags);
+ return bit;
+}
+
+void netdev_unregister_fc(int bit)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (bit > 0) {
+ netdev_fc_slots[bit].stimul = NULL;
+ netdev_fc_slots[bit].dev = NULL;
+ clear_bit(bit, &netdev_fc_mask);
+ clear_bit(bit, &netdev_fc_xoff);
+ }
+ restore_flags(flags);
+}
+
+static void netdev_wakeup(void)
+{
+ unsigned long xoff;
+
+ cli();
+ xoff = netdev_fc_xoff;
+ netdev_fc_xoff = 0;
+ netdev_dropping = 0;
+ netdev_throttle_events++;
+ while (xoff) {
+ int i = ffz(~xoff);
+ xoff &= ~(1<<i);
+ netdev_fc_slots[i].stimul(netdev_fc_slots[i].dev);
+ }
+ sti();
+}
+#endif
+
+static void dev_clear_backlog(struct device *dev)
+{
+ struct sk_buff *curr;
+ unsigned long flags;
+
+ /*
+ *
+ * Let now clear backlog queue. -AS
+ *
+ * We are competing here both with netif_rx() and net_bh().
+ * We don't want either of those to mess with skb ptrs
+ * while we work on them, thus we must grab the
+ * skb_queue_lock.
+ */
+
+ if (backlog.qlen) {
+ repeat:
+ spin_lock_irqsave(&skb_queue_lock, flags);
+ for (curr = backlog.next;
+ curr != (struct sk_buff *)(&backlog);
+ curr = curr->next)
+ if (curr->dev == dev)
+ {
+ __skb_unlink(curr, &backlog);
+ spin_unlock_irqrestore(&skb_queue_lock, flags);
+ kfree_skb(curr);
+ goto repeat;
+ }
+ spin_unlock_irqrestore(&skb_queue_lock, flags);
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+ if (netdev_dropping)
+ netdev_wakeup();
+#else
+ netdev_dropping = 0;
+#endif
+ }
+}
+
+/*
+ * Receive a packet from a device driver and queue it for the upper
+ * (protocol) levels. It always succeeds.
+ */
+
+void netif_rx(struct sk_buff *skb)
+{
+#ifndef CONFIG_CPU_IS_SLOW
+ if(skb->stamp.tv_sec==0)
+ get_fast_time(&skb->stamp);
+#else
+ skb->stamp = xtime;
+#endif
+
+ /* The code is rearranged so that the path is the most
+ short when CPU is congested, but is still operating.
+ */
+
+ if (backlog.qlen <= netdev_max_backlog) {
+ if (backlog.qlen) {
+ if (netdev_dropping == 0) {
+ skb_queue_tail(&backlog,skb);
+ mark_bh(NET_BH);
+ return;
+ }
+ atomic_inc(&netdev_rx_dropped);
+ kfree_skb(skb);
+ return;
+ }
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+ if (netdev_dropping)
+ netdev_wakeup();
+#else
+ netdev_dropping = 0;
+#endif
+ skb_queue_tail(&backlog,skb);
+ mark_bh(NET_BH);
+ return;
+ }
+ netdev_dropping = 1;
+ atomic_inc(&netdev_rx_dropped);
+ kfree_skb(skb);
+}
+
+#ifdef CONFIG_BRIDGE
+static inline void handle_bridge(struct sk_buff *skb, unsigned short type)
+{
+ /*
+ * The br_stats.flags is checked here to save the expense of a
+ * function call.
+ */
+ if ((br_stats.flags & BR_UP) && br_call_bridge(skb, type))
+ {
+ /*
+ * We pass the bridge a complete frame. This means
+ * recovering the MAC header first.
+ */
+
+ int offset;
+
+ skb=skb_clone(skb, GFP_ATOMIC);
+ if(skb==NULL)
+ return;
+
+ offset=skb->data-skb->mac.raw;
+ skb_push(skb,offset); /* Put header back on for bridge */
+
+ if(br_receive_frame(skb))
+ return;
+ kfree_skb(skb);
+ }
+ return;
+}
+#endif
+
+/*
+ * When we are called the queue is ready to grab, the interrupts are
+ * on and hardware can interrupt and queue to the receive queue as we
+ * run with no problems.
+ * This is run as a bottom half after an interrupt handler that does
+ * mark_bh(NET_BH);
+ */
+
+void net_bh(void)
+{
+ struct packet_type *ptype;
+ struct packet_type *pt_prev;
+ unsigned short type;
+#ifndef _HURD_
+ unsigned long start_time = jiffies;
+#ifdef CONFIG_CPU_IS_SLOW
+ static unsigned long start_busy = 0;
+ static unsigned long ave_busy = 0;
+
+ if (start_busy == 0)
+ start_busy = start_time;
+ net_cpu_congestion = ave_busy>>8;
+#endif
+#endif
+
+ NET_PROFILE_ENTER(net_bh);
+ /*
+ * Can we send anything now? We want to clear the
+ * decks for any more sends that get done as we
+ * process the input. This also minimises the
+ * latency on a transmit interrupt bh.
+ */
+
+ if (qdisc_head.forw != &qdisc_head)
+ qdisc_run_queues();
+
+ /*
+ * Any data left to process. This may occur because a
+ * mark_bh() is done after we empty the queue including
+ * that from the device which does a mark_bh() just after
+ */
+
+ /*
+ * While the queue is not empty..
+ *
+ * Note that the queue never shrinks due to
+ * an interrupt, so we can do this test without
+ * disabling interrupts.
+ */
+
+ while (!skb_queue_empty(&backlog))
+ {
+ struct sk_buff * skb;
+
+#ifndef _HURD_
+ /* Give chance to other bottom halves to run */
+ if (jiffies - start_time > 1)
+ goto net_bh_break;
+#endif
+
+ /*
+ * We have a packet. Therefore the queue has shrunk
+ */
+ skb = skb_dequeue(&backlog);
+
+#ifndef _HURD_
+#ifdef CONFIG_CPU_IS_SLOW
+ if (ave_busy > 128*16) {
+ kfree_skb(skb);
+ while ((skb = skb_dequeue(&backlog)) != NULL)
+ kfree_skb(skb);
+ break;
+ }
+#endif
+#endif
+
+
+#if 0
+ NET_PROFILE_SKB_PASSED(skb, net_bh_skb);
+#endif
+#ifdef CONFIG_NET_FASTROUTE
+ if (skb->pkt_type == PACKET_FASTROUTE) {
+ dev_queue_xmit(skb);
+ continue;
+ }
+#endif
+
+ /*
+ * Bump the pointer to the next structure.
+ *
+ * On entry to the protocol layer. skb->data and
+ * skb->nh.raw point to the MAC and encapsulated data
+ */
+
+ /* XXX until we figure out every place to modify.. */
+ skb->h.raw = skb->nh.raw = skb->data;
+
+ if (skb->mac.raw < skb->head || skb->mac.raw > skb->data) {
+ printk(KERN_CRIT "%s: wrong mac.raw ptr, proto=%04x\n", skb->dev->name, skb->protocol);
+ kfree_skb(skb);
+ continue;
+ }
+
+ /*
+ * Fetch the packet protocol ID.
+ */
+
+ type = skb->protocol;
+
+#ifdef CONFIG_BRIDGE
+ /*
+ * If we are bridging then pass the frame up to the
+ * bridging code (if this protocol is to be bridged).
+ * If it is bridged then move on
+ */
+ handle_bridge(skb, type);
+#endif
+
+ /*
+ * We got a packet ID. Now loop over the "known protocols"
+ * list. There are two lists. The ptype_all list of taps (normally empty)
+ * and the main protocol list which is hashed perfectly for normal protocols.
+ */
+
+ pt_prev = NULL;
+ for (ptype = ptype_all; ptype!=NULL; ptype=ptype->next)
+ {
+ if (!ptype->dev || ptype->dev == skb->dev) {
+ if(pt_prev)
+ {
+ struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC);
+ if(skb2)
+ pt_prev->func(skb2,skb->dev, pt_prev);
+ }
+ pt_prev=ptype;
+ }
+ }
+
+ for (ptype = ptype_base[ntohs(type)&15]; ptype != NULL; ptype = ptype->next)
+ {
+ if (ptype->type == type && (!ptype->dev || ptype->dev==skb->dev))
+ {
+ /*
+ * We already have a match queued. Deliver
+ * to it and then remember the new match
+ */
+ if(pt_prev)
+ {
+ struct sk_buff *skb2;
+
+ skb2=skb_clone(skb, GFP_ATOMIC);
+
+ /*
+ * Kick the protocol handler. This should be fast
+ * and efficient code.
+ */
+
+ if(skb2)
+ pt_prev->func(skb2, skb->dev, pt_prev);
+ }
+ /* Remember the current last to do */
+ pt_prev=ptype;
+ }
+ } /* End of protocol list loop */
+
+ /*
+ * Is there a last item to send to ?
+ */
+
+ if(pt_prev)
+ pt_prev->func(skb, skb->dev, pt_prev);
+ /*
+ * Has an unknown packet has been received ?
+ */
+
+ else {
+ kfree_skb(skb);
+ }
+ } /* End of queue loop */
+
+ /*
+ * We have emptied the queue
+ */
+
+ /*
+ * One last output flush.
+ */
+
+ if (qdisc_head.forw != &qdisc_head)
+ qdisc_run_queues();
+
+#ifndef _HURD_
+#ifdef CONFIG_CPU_IS_SLOW
+ if (1) {
+ unsigned long start_idle = jiffies;
+ ave_busy += ((start_idle - start_busy)<<3) - (ave_busy>>4);
+ start_busy = 0;
+ }
+#endif
+#endif
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+ if (netdev_dropping)
+ netdev_wakeup();
+#else
+ netdev_dropping = 0;
+#endif
+ NET_PROFILE_LEAVE(net_bh);
+ return;
+
+#ifndef _HURD_
+net_bh_break:
+ mark_bh(NET_BH);
+ NET_PROFILE_LEAVE(net_bh);
+ return;
+#endif
+}
+
+/* Protocol dependent address dumping routines */
+
+static gifconf_func_t * gifconf_list [NPROTO];
+
+int register_gifconf(unsigned int family, gifconf_func_t * gifconf)
+{
+ if (family>=NPROTO)
+ return -EINVAL;
+ gifconf_list[family] = gifconf;
+ return 0;
+}
+
+
+/*
+ * Map an interface index to its name (SIOCGIFNAME)
+ */
+
+/*
+ * This call is useful, but I'd remove it too.
+ *
+ * The reason is purely aestetical, it is the only call
+ * from SIOC* family using struct ifreq in reversed manner.
+ * Besides that, it is pretty silly to put "drawing" facility
+ * to kernel, it is useful only to print ifindices
+ * in readable form, is not it? --ANK
+ *
+ * We need this ioctl for efficient implementation of the
+ * if_indextoname() function required by the IPv6 API. Without
+ * it, we would have to search all the interfaces to find a
+ * match. --pb
+ */
+
+static int dev_ifname(struct ifreq *arg)
+{
+ struct device *dev;
+ struct ifreq ifr;
+ int err;
+
+ /*
+ * Fetch the caller's info block.
+ */
+
+ err = copy_from_user(&ifr, arg, sizeof(struct ifreq));
+ if (err)
+ return -EFAULT;
+
+ dev = dev_get_by_index(ifr.ifr_ifindex);
+ if (!dev)
+ return -ENODEV;
+
+ strcpy(ifr.ifr_name, dev->name);
+
+ err = copy_to_user(arg, &ifr, sizeof(struct ifreq));
+ return (err)?-EFAULT:0;
+}
+
+/*
+ * Perform a SIOCGIFCONF call. This structure will change
+ * size eventually, and there is nothing I can do about it.
+ * Thus we will need a 'compatibility mode'.
+ */
+
+#ifdef _HURD_
+int dev_ifconf(char *arg)
+#else
+static int dev_ifconf(char *arg)
+#endif
+{
+ struct ifconf ifc;
+ struct device *dev;
+ char *pos;
+ int len;
+ int total;
+ int i;
+
+ /*
+ * Fetch the caller's info block.
+ */
+
+ if (copy_from_user(&ifc, arg, sizeof(struct ifconf)))
+ return -EFAULT;
+
+ pos = ifc.ifc_buf;
+ len = ifc.ifc_len;
+
+ /*
+ * Loop over the interfaces, and write an info block for each.
+ */
+
+ total = 0;
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
+ for (i=0; i<NPROTO; i++) {
+ if (gifconf_list[i]) {
+ int done;
+ if (pos==NULL) {
+ done = gifconf_list[i](dev, NULL, 0);
+ } else {
+ done = gifconf_list[i](dev, pos+total, len-total);
+ }
+ if (done<0)
+ return -EFAULT;
+ total += done;
+ }
+ }
+ }
+
+ /*
+ * All done. Write the updated control block back to the caller.
+ */
+ ifc.ifc_len = total;
+
+ if (copy_to_user(arg, &ifc, sizeof(struct ifconf)))
+ return -EFAULT;
+
+ /*
+ * Both BSD and Solaris return 0 here, so we do too.
+ */
+ return 0;
+}
+
+/*
+ * This is invoked by the /proc filesystem handler to display a device
+ * in detail.
+ */
+
+#ifdef CONFIG_PROC_FS
+static int sprintf_stats(char *buffer, struct device *dev)
+{
+ struct net_device_stats *stats = (dev->get_stats ? dev->get_stats(dev): NULL);
+ int size;
+
+ if (stats)
+ size = sprintf(buffer, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu %8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
+ dev->name,
+ stats->rx_bytes,
+ stats->rx_packets, stats->rx_errors,
+ stats->rx_dropped + stats->rx_missed_errors,
+ stats->rx_fifo_errors,
+ stats->rx_length_errors + stats->rx_over_errors
+ + stats->rx_crc_errors + stats->rx_frame_errors,
+ stats->rx_compressed, stats->multicast,
+ stats->tx_bytes,
+ stats->tx_packets, stats->tx_errors, stats->tx_dropped,
+ stats->tx_fifo_errors, stats->collisions,
+ stats->tx_carrier_errors + stats->tx_aborted_errors
+ + stats->tx_window_errors + stats->tx_heartbeat_errors,
+ stats->tx_compressed);
+ else
+ size = sprintf(buffer, "%6s: No statistics available.\n", dev->name);
+
+ return size;
+}
+
+/*
+ * Called from the PROCfs module. This now uses the new arbitrary sized /proc/net interface
+ * to create /proc/net/dev
+ */
+
+int dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len=0;
+ off_t begin=0;
+ off_t pos=0;
+ int size;
+
+ struct device *dev;
+
+
+ size = sprintf(buffer,
+ "Inter-| Receive | Transmit\n"
+ " face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n");
+
+ pos+=size;
+ len+=size;
+
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ size = sprintf_stats(buffer+len, dev);
+ len+=size;
+ pos=begin+len;
+
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ break;
+ }
+
+ *start=buffer+(offset-begin); /* Start of wanted data */
+ len-=(offset-begin); /* Start slop */
+ if(len>length)
+ len=length; /* Ending slop */
+ return len;
+}
+
+static int dev_proc_stats(char *buffer, char **start, off_t offset,
+ int length, int *eof, void *data)
+{
+ int len;
+
+ len = sprintf(buffer, "%08x %08x %08x %08x %08x\n",
+ atomic_read(&netdev_rx_dropped),
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+ netdev_throttle_events,
+#else
+ 0,
+#endif
+#ifdef CONFIG_NET_FASTROUTE
+ dev_fastroute_stat.hits,
+ dev_fastroute_stat.succeed,
+ dev_fastroute_stat.deferred
+#else
+ 0, 0, 0
+#endif
+ );
+
+ len -= offset;
+
+ if (len > length)
+ len = length;
+ if(len < 0)
+ len = 0;
+
+ *start = buffer + offset;
+ *eof = 1;
+
+ return len;
+}
+
+#endif /* CONFIG_PROC_FS */
+
+
+#ifdef CONFIG_NET_RADIO
+#ifdef CONFIG_PROC_FS
+
+/*
+ * Print one entry of /proc/net/wireless
+ * This is a clone of /proc/net/dev (just above)
+ */
+static int sprintf_wireless_stats(char *buffer, struct device *dev)
+{
+ /* Get stats from the driver */
+ struct iw_statistics *stats = (dev->get_wireless_stats ?
+ dev->get_wireless_stats(dev) :
+ (struct iw_statistics *) NULL);
+ int size;
+
+ if(stats != (struct iw_statistics *) NULL)
+ {
+ size = sprintf(buffer,
+ "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d\n",
+ dev->name,
+ stats->status,
+ stats->qual.qual,
+ stats->qual.updated & 1 ? '.' : ' ',
+ stats->qual.level,
+ stats->qual.updated & 2 ? '.' : ' ',
+ stats->qual.noise,
+ stats->qual.updated & 4 ? '.' : ' ',
+ stats->discard.nwid,
+ stats->discard.code,
+ stats->discard.misc);
+ stats->qual.updated = 0;
+ }
+ else
+ size = 0;
+
+ return size;
+}
+
+/*
+ * Print info for /proc/net/wireless (print all entries)
+ * This is a clone of /proc/net/dev (just above)
+ */
+int dev_get_wireless_info(char * buffer, char **start, off_t offset,
+ int length, int dummy)
+{
+ int len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+ int size;
+
+ struct device * dev;
+
+ size = sprintf(buffer,
+ "Inter-| sta-| Quality | Discarded packets\n"
+ " face | tus | link level noise | nwid crypt misc\n"
+ );
+
+ pos+=size;
+ len+=size;
+
+ for(dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ size = sprintf_wireless_stats(buffer+len, dev);
+ len+=size;
+ pos=begin+len;
+
+ if(pos < offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos > offset + length)
+ break;
+ }
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin); /* Start slop */
+ if(len > length)
+ len = length; /* Ending slop */
+
+ return len;
+}
+#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_NET_RADIO */
+
+void dev_set_promiscuity(struct device *dev, int inc)
+{
+ unsigned short old_flags = dev->flags;
+
+ dev->flags |= IFF_PROMISC;
+ if ((dev->promiscuity += inc) == 0)
+ dev->flags &= ~IFF_PROMISC;
+ if (dev->flags^old_flags) {
+#ifdef CONFIG_NET_FASTROUTE
+ if (dev->flags&IFF_PROMISC) {
+ netdev_fastroute_obstacles++;
+ dev_clear_fastroute(dev);
+ } else
+ netdev_fastroute_obstacles--;
+#endif
+ dev_mc_upload(dev);
+ printk(KERN_INFO "device %s %s promiscuous mode\n",
+ dev->name, (dev->flags&IFF_PROMISC) ? "entered" : "left");
+ }
+}
+
+void dev_set_allmulti(struct device *dev, int inc)
+{
+ unsigned short old_flags = dev->flags;
+
+ dev->flags |= IFF_ALLMULTI;
+ if ((dev->allmulti += inc) == 0)
+ dev->flags &= ~IFF_ALLMULTI;
+ if (dev->flags^old_flags)
+ dev_mc_upload(dev);
+}
+
+int dev_change_flags(struct device *dev, unsigned flags)
+{
+ int ret;
+ int old_flags = dev->flags;
+
+ /*
+ * Set the flags on our device.
+ */
+
+ dev->flags = (flags & (IFF_DEBUG|IFF_NOTRAILERS|IFF_RUNNING|IFF_NOARP|
+ IFF_SLAVE|IFF_MASTER|IFF_DYNAMIC|
+ IFF_MULTICAST|IFF_PORTSEL|IFF_AUTOMEDIA)) |
+ (dev->flags & (IFF_UP|IFF_VOLATILE|IFF_PROMISC|IFF_ALLMULTI));
+
+ /*
+ * Load in the correct multicast list now the flags have changed.
+ */
+
+ dev_mc_upload(dev);
+
+ /*
+ * Have we downed the interface. We handle IFF_UP ourselves
+ * according to user attempts to set it, rather than blindly
+ * setting it.
+ */
+
+ ret = 0;
+ if ((old_flags^flags)&IFF_UP) /* Bit is different ? */
+ {
+ ret = ((old_flags & IFF_UP) ? dev_close : dev_open)(dev);
+
+ if (ret == 0)
+ dev_mc_upload(dev);
+ }
+
+ if (dev->flags&IFF_UP &&
+ ((old_flags^dev->flags)&~(IFF_UP|IFF_RUNNING|IFF_PROMISC|IFF_ALLMULTI|IFF_VOLATILE)))
+ notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev);
+
+ if ((flags^dev->gflags)&IFF_PROMISC) {
+ int inc = (flags&IFF_PROMISC) ? +1 : -1;
+ dev->gflags ^= IFF_PROMISC;
+ dev_set_promiscuity(dev, inc);
+ }
+
+ /* NOTE: order of synchronization of IFF_PROMISC and IFF_ALLMULTI
+ is important. Some (broken) drivers set IFF_PROMISC, when
+ IFF_ALLMULTI is requested not asking us and not reporting.
+ */
+ if ((flags^dev->gflags)&IFF_ALLMULTI) {
+ int inc = (flags&IFF_ALLMULTI) ? +1 : -1;
+ dev->gflags ^= IFF_ALLMULTI;
+ dev_set_allmulti(dev, inc);
+ }
+
+ if (!ret && dev->change_flags)
+ ret = dev->change_flags(dev, dev->flags);
+
+ return ret;
+}
+
+#ifdef _HURD_
+
+#define dev_ioctl 0
+
+#else
+
+/*
+ * Perform the SIOCxIFxxx calls.
+ */
+
+static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd)
+{
+ struct device *dev;
+ int err;
+
+ if ((dev = dev_get(ifr->ifr_name)) == NULL)
+ return -ENODEV;
+
+ switch(cmd)
+ {
+ case SIOCGIFFLAGS: /* Get interface flags */
+ ifr->ifr_flags = (dev->flags&~(IFF_PROMISC|IFF_ALLMULTI))
+ |(dev->gflags&(IFF_PROMISC|IFF_ALLMULTI));
+ return 0;
+
+ case SIOCSIFFLAGS: /* Set interface flags */
+ return dev_change_flags(dev, ifr->ifr_flags);
+
+ case SIOCGIFMETRIC: /* Get the metric on the interface (currently unused) */
+ ifr->ifr_metric = 0;
+ return 0;
+
+ case SIOCSIFMETRIC: /* Set the metric on the interface (currently unused) */
+ return -EOPNOTSUPP;
+
+ case SIOCGIFMTU: /* Get the MTU of a device */
+ ifr->ifr_mtu = dev->mtu;
+ return 0;
+
+ case SIOCSIFMTU: /* Set the MTU of a device */
+ if (ifr->ifr_mtu == dev->mtu)
+ return 0;
+
+ /*
+ * MTU must be positive.
+ */
+
+ if (ifr->ifr_mtu<=0)
+ return -EINVAL;
+
+ if (dev->change_mtu)
+ err = dev->change_mtu(dev, ifr->ifr_mtu);
+ else {
+ dev->mtu = ifr->ifr_mtu;
+ err = 0;
+ }
+ if (!err && dev->flags&IFF_UP)
+ notifier_call_chain(&netdev_chain, NETDEV_CHANGEMTU, dev);
+ return err;
+
+ case SIOCGIFHWADDR:
+ memcpy(ifr->ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN);
+ ifr->ifr_hwaddr.sa_family=dev->type;
+ return 0;
+
+ case SIOCSIFHWADDR:
+ if(dev->set_mac_address==NULL)
+ return -EOPNOTSUPP;
+ if(ifr->ifr_hwaddr.sa_family!=dev->type)
+ return -EINVAL;
+ err=dev->set_mac_address(dev,&ifr->ifr_hwaddr);
+ if (!err)
+ notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev);
+ return err;
+
+ case SIOCSIFHWBROADCAST:
+ if(ifr->ifr_hwaddr.sa_family!=dev->type)
+ return -EINVAL;
+ memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, MAX_ADDR_LEN);
+ notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev);
+ return 0;
+
+ case SIOCGIFMAP:
+ ifr->ifr_map.mem_start=dev->mem_start;
+ ifr->ifr_map.mem_end=dev->mem_end;
+ ifr->ifr_map.base_addr=dev->base_addr;
+ ifr->ifr_map.irq=dev->irq;
+ ifr->ifr_map.dma=dev->dma;
+ ifr->ifr_map.port=dev->if_port;
+ return 0;
+
+ case SIOCSIFMAP:
+ if (dev->set_config)
+ return dev->set_config(dev,&ifr->ifr_map);
+ return -EOPNOTSUPP;
+
+ case SIOCADDMULTI:
+ if(dev->set_multicast_list==NULL ||
+ ifr->ifr_hwaddr.sa_family!=AF_UNSPEC)
+ return -EINVAL;
+ dev_mc_add(dev,ifr->ifr_hwaddr.sa_data, dev->addr_len, 1);
+ return 0;
+
+ case SIOCDELMULTI:
+ if(dev->set_multicast_list==NULL ||
+ ifr->ifr_hwaddr.sa_family!=AF_UNSPEC)
+ return -EINVAL;
+ dev_mc_delete(dev,ifr->ifr_hwaddr.sa_data,dev->addr_len, 1);
+ return 0;
+
+ case SIOCGIFINDEX:
+ ifr->ifr_ifindex = dev->ifindex;
+ return 0;
+
+ case SIOCGIFTXQLEN:
+ ifr->ifr_qlen = dev->tx_queue_len;
+ return 0;
+
+ case SIOCSIFTXQLEN:
+ if(ifr->ifr_qlen<0)
+ return -EINVAL;
+ dev->tx_queue_len = ifr->ifr_qlen;
+ return 0;
+
+ case SIOCSIFNAME:
+ if (dev->flags&IFF_UP)
+ return -EBUSY;
+ if (dev_get(ifr->ifr_newname))
+ return -EEXIST;
+ memcpy(dev->name, ifr->ifr_newname, IFNAMSIZ);
+ dev->name[IFNAMSIZ-1] = 0;
+ notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev);
+ return 0;
+
+ /*
+ * Unknown or private ioctl
+ */
+
+ default:
+ if(cmd >= SIOCDEVPRIVATE &&
+ cmd <= SIOCDEVPRIVATE + 15) {
+ if (dev->do_ioctl)
+ return dev->do_ioctl(dev, ifr, cmd);
+ return -EOPNOTSUPP;
+ }
+
+#ifdef CONFIG_NET_RADIO
+ if(cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
+ if (dev->do_ioctl)
+ return dev->do_ioctl(dev, ifr, cmd);
+ return -EOPNOTSUPP;
+ }
+#endif /* CONFIG_NET_RADIO */
+
+ }
+ return -EINVAL;
+}
+
+
+/*
+ * This function handles all "interface"-type I/O control requests. The actual
+ * 'doing' part of this is dev_ifsioc above.
+ */
+
+int dev_ioctl(unsigned int cmd, void *arg)
+{
+ struct ifreq ifr;
+ int ret;
+ char *colon;
+
+ /* One special case: SIOCGIFCONF takes ifconf argument
+ and requires shared lock, because it sleeps writing
+ to user space.
+ */
+
+ if (cmd == SIOCGIFCONF) {
+ rtnl_shlock();
+ ret = dev_ifconf((char *) arg);
+ rtnl_shunlock();
+ return ret;
+ }
+ if (cmd == SIOCGIFNAME) {
+ return dev_ifname((struct ifreq *)arg);
+ }
+
+ if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+ return -EFAULT;
+
+ ifr.ifr_name[IFNAMSIZ-1] = 0;
+
+ colon = strchr(ifr.ifr_name, ':');
+ if (colon)
+ *colon = 0;
+
+ /*
+ * See which interface the caller is talking about.
+ */
+
+ switch(cmd)
+ {
+ /*
+ * These ioctl calls:
+ * - can be done by all.
+ * - atomic and do not require locking.
+ * - return a value
+ */
+
+ case SIOCGIFFLAGS:
+ case SIOCGIFMETRIC:
+ case SIOCGIFMTU:
+ case SIOCGIFHWADDR:
+ case SIOCGIFSLAVE:
+ case SIOCGIFMAP:
+ case SIOCGIFINDEX:
+ case SIOCGIFTXQLEN:
+ dev_load(ifr.ifr_name);
+ ret = dev_ifsioc(&ifr, cmd);
+ if (!ret) {
+ if (colon)
+ *colon = ':';
+ if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+ return -EFAULT;
+ }
+ return ret;
+
+ /*
+ * These ioctl calls:
+ * - require superuser power.
+ * - require strict serialization.
+ * - do not return a value
+ */
+
+ case SIOCSIFFLAGS:
+ case SIOCSIFMETRIC:
+ case SIOCSIFMTU:
+ case SIOCSIFMAP:
+ case SIOCSIFHWADDR:
+ case SIOCSIFSLAVE:
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ case SIOCSIFHWBROADCAST:
+ case SIOCSIFTXQLEN:
+ case SIOCSIFNAME:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ dev_load(ifr.ifr_name);
+ rtnl_lock();
+ ret = dev_ifsioc(&ifr, cmd);
+ rtnl_unlock();
+ return ret;
+
+ case SIOCGIFMEM:
+ /* Get the per device memory space. We can add this but currently
+ do not support it */
+ case SIOCSIFMEM:
+ /* Set the per device memory buffer space. Not applicable in our case */
+ case SIOCSIFLINK:
+ return -EINVAL;
+
+ /*
+ * Unknown or private ioctl.
+ */
+
+ default:
+ if (cmd >= SIOCDEVPRIVATE &&
+ cmd <= SIOCDEVPRIVATE + 15) {
+ dev_load(ifr.ifr_name);
+ rtnl_lock();
+ ret = dev_ifsioc(&ifr, cmd);
+ rtnl_unlock();
+ if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+ return -EFAULT;
+ return ret;
+ }
+#ifdef CONFIG_NET_RADIO
+ if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
+ dev_load(ifr.ifr_name);
+ if (IW_IS_SET(cmd)) {
+ if (!suser())
+ return -EPERM;
+ rtnl_lock();
+ }
+ ret = dev_ifsioc(&ifr, cmd);
+ if (IW_IS_SET(cmd))
+ rtnl_unlock();
+ if (!ret && IW_IS_GET(cmd) &&
+ copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+ return -EFAULT;
+ return ret;
+ }
+#endif /* CONFIG_NET_RADIO */
+ return -EINVAL;
+ }
+}
+
+#endif
+
+int dev_new_index(void)
+{
+ static int ifindex;
+ for (;;) {
+ if (++ifindex <= 0)
+ ifindex=1;
+ if (dev_get_by_index(ifindex) == NULL)
+ return ifindex;
+ }
+}
+
+static int dev_boot_phase = 1;
+
+
+int register_netdevice(struct device *dev)
+{
+ struct device *d, **dp;
+
+ if (dev_boot_phase) {
+ /* This is NOT bug, but I am not sure, that all the
+ devices, initialized before netdev module is started
+ are sane.
+
+ Now they are chained to device boot list
+ and probed later. If a module is initialized
+ before netdev, but assumes that dev->init
+ is really called by register_netdev(), it will fail.
+
+ So that this message should be printed for a while.
+ */
+ printk(KERN_INFO "early initialization of device %s is deferred\n", dev->name);
+
+ /* Check for existence, and append to tail of chain */
+ for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) {
+ if (d == dev || strcmp(d->name, dev->name) == 0)
+ return -EEXIST;
+ }
+ dev->next = NULL;
+ *dp = dev;
+ return 0;
+ }
+
+ dev->iflink = -1;
+
+ /* Init, if this function is available */
+ if (dev->init && dev->init(dev) != 0)
+ return -EIO;
+
+ /* Check for existence, and append to tail of chain */
+ for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) {
+ if (d == dev || strcmp(d->name, dev->name) == 0)
+ return -EEXIST;
+ }
+ dev->next = NULL;
+ dev_init_scheduler(dev);
+ dev->ifindex = dev_new_index();
+ if (dev->iflink == -1)
+ dev->iflink = dev->ifindex;
+ *dp = dev;
+
+ /* Notify protocols, that a new device appeared. */
+ notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
+
+ return 0;
+}
+
+int unregister_netdevice(struct device *dev)
+{
+ struct device *d, **dp;
+
+ if (dev_boot_phase == 0) {
+ /* If device is running, close it.
+ It is very bad idea, really we should
+ complain loudly here, but random hackery
+ in linux/drivers/net likes it.
+ */
+ if (dev->flags & IFF_UP)
+ dev_close(dev);
+
+#ifdef CONFIG_NET_FASTROUTE
+ dev_clear_fastroute(dev);
+#endif
+
+ /* Shutdown queueing discipline. */
+ dev_shutdown(dev);
+
+ /* Notify protocols, that we are about to destroy
+ this device. They should clean all the things.
+ */
+ notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev);
+
+ /*
+ * Flush the multicast chain
+ */
+ dev_mc_discard(dev);
+
+ /* To avoid pointers looking to nowhere,
+ we wait for end of critical section */
+ dev_lock_wait();
+ }
+
+ /* And unlink it from device chain. */
+ for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) {
+ if (d == dev) {
+ *dp = d->next;
+ synchronize_bh();
+ d->next = NULL;
+
+ if (dev->destructor)
+ dev->destructor(dev);
+ return 0;
+ }
+ }
+ return -ENODEV;
+}
+
+
+/*
+ * Initialize the DEV module. At boot time this walks the device list and
+ * unhooks any devices that fail to initialise (normally hardware not
+ * present) and leaves us with a valid list of present and active devices.
+ *
+ */
+extern int lance_init(void);
+extern int bpq_init(void);
+extern int scc_init(void);
+extern void sdla_setup(void);
+extern void sdla_c_setup(void);
+extern void dlci_setup(void);
+extern int dmascc_init(void);
+extern int sm_init(void);
+
+extern int baycom_ser_fdx_init(void);
+extern int baycom_ser_hdx_init(void);
+extern int baycom_par_init(void);
+
+extern int lapbeth_init(void);
+extern int comx_init(void);
+extern void arcnet_init(void);
+extern void ip_auto_config(void);
+#ifdef CONFIG_8xx
+extern int cpm_enet_init(void);
+#endif /* CONFIG_8xx */
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_dev = {
+ PROC_NET_DEV, 3, "dev",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ dev_get_info
+};
+#endif
+
+#ifdef CONFIG_NET_RADIO
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_wireless = {
+ PROC_NET_WIRELESS, 8, "wireless",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ dev_get_wireless_info
+};
+#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_NET_RADIO */
+
+__initfunc(int net_dev_init(void))
+{
+ struct device *dev, **dp;
+
+#ifdef CONFIG_NET_SCHED
+ pktsched_init();
+#endif
+
+ /*
+ * Initialise the packet receive queue.
+ */
+
+ skb_queue_head_init(&backlog);
+
+ /*
+ * The bridge has to be up before the devices
+ */
+
+#ifdef CONFIG_BRIDGE
+ br_init();
+#endif
+
+ /*
+ * This is Very Ugly(tm).
+ *
+ * Some devices want to be initialized early..
+ */
+
+#if defined(CONFIG_SCC)
+ scc_init();
+#endif
+#if defined(CONFIG_DMASCC)
+ dmascc_init();
+#endif
+#if defined(CONFIG_BPQETHER)
+ bpq_init();
+#endif
+#if defined(CONFIG_DLCI)
+ dlci_setup();
+#endif
+#if defined(CONFIG_SDLA)
+ sdla_c_setup();
+#endif
+#if defined(CONFIG_BAYCOM_PAR)
+ baycom_par_init();
+#endif
+#if defined(CONFIG_BAYCOM_SER_FDX)
+ baycom_ser_fdx_init();
+#endif
+#if defined(CONFIG_BAYCOM_SER_HDX)
+ baycom_ser_hdx_init();
+#endif
+#if defined(CONFIG_SOUNDMODEM)
+ sm_init();
+#endif
+#if defined(CONFIG_LAPBETHER)
+ lapbeth_init();
+#endif
+#if defined(CONFIG_PLIP)
+ plip_init();
+#endif
+#if defined(CONFIG_ARCNET)
+ arcnet_init();
+#endif
+#if defined(CONFIG_8xx)
+ cpm_enet_init();
+#endif
+#if defined(CONFIG_COMX)
+ comx_init();
+#endif
+ /*
+ * SLHC if present needs attaching so other people see it
+ * even if not opened.
+ */
+
+#ifdef CONFIG_INET
+#if (defined(CONFIG_SLIP) && defined(CONFIG_SLIP_COMPRESSED)) \
+ || defined(CONFIG_PPP) \
+ || (defined(CONFIG_ISDN) && defined(CONFIG_ISDN_PPP))
+ slhc_install();
+#endif
+#endif
+
+#ifdef CONFIG_NET_PROFILE
+ net_profile_init();
+ NET_PROFILE_REGISTER(dev_queue_xmit);
+ NET_PROFILE_REGISTER(net_bh);
+#if 0
+ NET_PROFILE_REGISTER(net_bh_skb);
+#endif
+#endif
+ /*
+ * Add the devices.
+ * If the call to dev->init fails, the dev is removed
+ * from the chain disconnecting the device until the
+ * next reboot.
+ */
+
+ dp = &dev_base;
+ while ((dev = *dp) != NULL)
+ {
+ dev->iflink = -1;
+ if (dev->init && dev->init(dev))
+ {
+ /*
+ * It failed to come up. Unhook it.
+ */
+ *dp = dev->next;
+ synchronize_bh();
+ }
+ else
+ {
+ dp = &dev->next;
+ dev->ifindex = dev_new_index();
+ if (dev->iflink == -1)
+ dev->iflink = dev->ifindex;
+ dev_init_scheduler(dev);
+ }
+ }
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_net_dev);
+ {
+ struct proc_dir_entry *ent = create_proc_entry("net/dev_stat", 0, 0);
+ ent->read_proc = dev_proc_stats;
+ }
+#endif
+
+#ifdef CONFIG_NET_RADIO
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_net_wireless);
+#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_NET_RADIO */
+
+ init_bh(NET_BH, net_bh);
+
+ dev_boot_phase = 0;
+
+ dev_mcast_init();
+
+#ifdef CONFIG_BRIDGE
+ /*
+ * Register any statically linked ethernet devices with the bridge
+ */
+ br_spacedevice_register();
+#endif
+
+#ifdef CONFIG_IP_PNP
+ ip_auto_config();
+#endif
+
+ return 0;
+}
diff --git a/pfinet/linux-src/net/core/dev_mcast.c b/pfinet/linux-src/net/core/dev_mcast.c
new file mode 100644
index 00000000..0a67b53d
--- /dev/null
+++ b/pfinet/linux-src/net/core/dev_mcast.c
@@ -0,0 +1,251 @@
+/*
+ * Linux NET3: Multicast List maintenance.
+ *
+ * Authors:
+ * Tim Kordas <tjk@nostromo.eeap.cwru.edu>
+ * Richard Underwood <richard@wuzz.demon.co.uk>
+ *
+ * Stir fried together from the IP multicast and CAP patches above
+ * Alan Cox <Alan.Cox@linux.org>
+ *
+ * Fixes:
+ * Alan Cox : Update the device on a real delete
+ * rather than any time but...
+ * Alan Cox : IFF_ALLMULTI support.
+ * Alan Cox : New format set_multicast_list() calls.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <net/ip.h>
+#include <net/route.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/arp.h>
+
+
+/*
+ * Device multicast list maintenance.
+ *
+ * This is used both by IP and by the user level maintenance functions.
+ * Unlike BSD we maintain a usage count on a given multicast address so
+ * that a casual user application can add/delete multicasts used by
+ * protocols without doing damage to the protocols when it deletes the
+ * entries. It also helps IP as it tracks overlapping maps.
+ *
+ * Device mc lists are changed by bh at least if IPv6 is enabled,
+ * so that it must be bh protected.
+ */
+
+/*
+ * Update the multicast list into the physical NIC controller.
+ */
+
+void dev_mc_upload(struct device *dev)
+{
+ /* Don't do anything till we up the interface
+ [dev_open will call this function so the list will
+ stay sane] */
+
+ if(!(dev->flags&IFF_UP))
+ return;
+
+ /*
+ * Devices with no set multicast don't get set
+ */
+
+ if(dev->set_multicast_list==NULL)
+ return;
+
+ start_bh_atomic();
+ dev->set_multicast_list(dev);
+ end_bh_atomic();
+}
+
+/*
+ * Delete a device level multicast
+ */
+
+int dev_mc_delete(struct device *dev, void *addr, int alen, int glbl)
+{
+ int err = 0;
+ struct dev_mc_list *dmi, **dmip;
+
+ start_bh_atomic();
+ for (dmip=&dev->mc_list; (dmi=*dmip)!=NULL; dmip=&dmi->next) {
+ /*
+ * Find the entry we want to delete. The device could
+ * have variable length entries so check these too.
+ */
+ if (memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && alen==dmi->dmi_addrlen) {
+ if (glbl) {
+ int old_glbl = dmi->dmi_gusers;
+ dmi->dmi_gusers = 0;
+ if (old_glbl == 0)
+ break;
+ }
+ if(--dmi->dmi_users)
+ goto done;
+
+ /*
+ * Last user. So delete the entry.
+ */
+ *dmip = dmi->next;
+ dev->mc_count--;
+ kfree_s(dmi,sizeof(*dmi));
+ /*
+ * We have altered the list, so the card
+ * loaded filter is now wrong. Fix it
+ */
+ end_bh_atomic();
+ dev_mc_upload(dev);
+ return 0;
+ }
+ }
+ err = -ENOENT;
+done:
+ end_bh_atomic();
+ return err;
+}
+
+/*
+ * Add a device level multicast
+ */
+
+int dev_mc_add(struct device *dev, void *addr, int alen, int glbl)
+{
+ int err = 0;
+ struct dev_mc_list *dmi, *dmi1;
+
+ dmi1 = (struct dev_mc_list *)kmalloc(sizeof(*dmi), gfp_any());
+
+ start_bh_atomic();
+ for(dmi=dev->mc_list; dmi!=NULL; dmi=dmi->next) {
+ if (memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen) {
+ if (glbl) {
+ int old_glbl = dmi->dmi_gusers;
+ dmi->dmi_gusers = 1;
+ if (old_glbl)
+ goto done;
+ }
+ dmi->dmi_users++;
+ goto done;
+ }
+ }
+
+ if ((dmi=dmi1)==NULL)
+ return -ENOMEM;
+ memcpy(dmi->dmi_addr, addr, alen);
+ dmi->dmi_addrlen=alen;
+ dmi->next=dev->mc_list;
+ dmi->dmi_users=1;
+ dmi->dmi_gusers=glbl ? 1 : 0;
+ dev->mc_list=dmi;
+ dev->mc_count++;
+ end_bh_atomic();
+ dev_mc_upload(dev);
+ return 0;
+
+done:
+ end_bh_atomic();
+ if (dmi1)
+ kfree(dmi1);
+ return err;
+}
+
+/*
+ * Discard multicast list when a device is downed
+ */
+
+void dev_mc_discard(struct device *dev)
+{
+ start_bh_atomic();
+ while (dev->mc_list!=NULL) {
+ struct dev_mc_list *tmp=dev->mc_list;
+ dev->mc_list=tmp->next;
+ if (tmp->dmi_users > tmp->dmi_gusers)
+ printk("dev_mc_discard: multicast leakage! dmi_users=%d\n", tmp->dmi_users);
+ kfree_s(tmp,sizeof(*tmp));
+ }
+ dev->mc_count=0;
+ end_bh_atomic();
+}
+
+#ifdef CONFIG_PROC_FS
+static int dev_mc_read_proc(char *buffer, char **start, off_t offset,
+ int length, int *eof, void *data)
+{
+ off_t pos=0, begin=0;
+ struct dev_mc_list *m;
+ int len=0;
+ struct device *dev;
+
+ start_bh_atomic();
+
+ for (dev = dev_base; dev; dev = dev->next) {
+ for (m = dev->mc_list; m; m = m->next) {
+ int i;
+
+ len += sprintf(buffer+len,"%-4d %-15s %-5d %-5d ", dev->ifindex, dev->name,
+ m->dmi_users, m->dmi_gusers);
+
+ for (i=0; i<m->dmi_addrlen; i++)
+ len += sprintf(buffer+len, "%02x", m->dmi_addr[i]);
+
+ len+=sprintf(buffer+len, "\n");
+
+ pos=begin+len;
+ if (pos < offset) {
+ len=0;
+ begin=pos;
+ }
+ if (pos > offset+length)
+ goto done;
+ }
+ }
+ *eof = 1;
+
+done:
+ end_bh_atomic();
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ if(len<0)
+ len=0;
+ return len;
+}
+#endif
+
+__initfunc(void dev_mcast_init(void))
+{
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *ent;
+
+ ent = create_proc_entry("net/dev_mcast", 0, 0);
+ ent->read_proc = dev_mc_read_proc;
+#endif
+}
diff --git a/pfinet/linux-src/net/core/dst.c b/pfinet/linux-src/net/core/dst.c
new file mode 100644
index 00000000..9007dde6
--- /dev/null
+++ b/pfinet/linux-src/net/core/dst.c
@@ -0,0 +1,145 @@
+/*
+ * net/dst.c Protocol independent destination cache.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+
+#include <net/dst.h>
+
+struct dst_entry * dst_garbage_list;
+atomic_t dst_total = ATOMIC_INIT(0);
+
+static unsigned long dst_gc_timer_expires;
+static unsigned long dst_gc_timer_inc = DST_GC_MAX;
+static void dst_run_gc(unsigned long);
+
+static struct timer_list dst_gc_timer =
+ { NULL, NULL, DST_GC_MIN, 0L, dst_run_gc };
+
+#if RT_CACHE_DEBUG >= 2
+atomic_t hh_count;
+#endif
+
+static void dst_run_gc(unsigned long dummy)
+{
+ int delayed = 0;
+ struct dst_entry * dst, **dstp;
+
+ del_timer(&dst_gc_timer);
+ dstp = &dst_garbage_list;
+ while ((dst = *dstp) != NULL) {
+ if (atomic_read(&dst->use)) {
+ dstp = &dst->next;
+ delayed++;
+ continue;
+ }
+ *dstp = dst->next;
+ dst_destroy(dst);
+ }
+ if (!dst_garbage_list) {
+ dst_gc_timer_inc = DST_GC_MAX;
+ return;
+ }
+ if ((dst_gc_timer_expires += dst_gc_timer_inc) > DST_GC_MAX)
+ dst_gc_timer_expires = DST_GC_MAX;
+ dst_gc_timer_inc += DST_GC_INC;
+ dst_gc_timer.expires = jiffies + dst_gc_timer_expires;
+#if RT_CACHE_DEBUG >= 2
+ printk("dst_total: %d/%d %ld\n",
+ atomic_read(&dst_total), delayed, dst_gc_timer_expires);
+#endif
+ add_timer(&dst_gc_timer);
+}
+
+static int dst_discard(struct sk_buff *skb)
+{
+ kfree_skb(skb);
+ return 0;
+}
+
+static int dst_blackhole(struct sk_buff *skb)
+{
+ kfree_skb(skb);
+ return 0;
+}
+
+void * dst_alloc(int size, struct dst_ops * ops)
+{
+ struct dst_entry * dst;
+
+ if (ops->gc && atomic_read(&ops->entries) > ops->gc_thresh) {
+ if (ops->gc())
+ return NULL;
+ }
+ dst = kmalloc(size, GFP_ATOMIC);
+ if (!dst)
+ return NULL;
+ memset(dst, 0, size);
+ dst->ops = ops;
+ atomic_set(&dst->refcnt, 0);
+ dst->lastuse = jiffies;
+ dst->input = dst_discard;
+ dst->output = dst_blackhole;
+ atomic_inc(&dst_total);
+ atomic_inc(&ops->entries);
+ return dst;
+}
+
+void __dst_free(struct dst_entry * dst)
+{
+ start_bh_atomic();
+ /* The first case (dev==NULL) is required, when
+ protocol module is unloaded.
+ */
+ if (dst->dev == NULL || !(dst->dev->flags&IFF_UP)) {
+ dst->input = dst_discard;
+ dst->output = dst_blackhole;
+ dst->dev = &loopback_dev;
+ }
+ dst->obsolete = 2;
+ dst->next = dst_garbage_list;
+ dst_garbage_list = dst;
+ if (dst_gc_timer_inc > DST_GC_INC) {
+ del_timer(&dst_gc_timer);
+ dst_gc_timer_inc = DST_GC_INC;
+ dst_gc_timer_expires = DST_GC_MIN;
+ dst_gc_timer.expires = jiffies + dst_gc_timer_expires;
+ add_timer(&dst_gc_timer);
+ }
+ end_bh_atomic();
+}
+
+void dst_destroy(struct dst_entry * dst)
+{
+ struct neighbour *neigh = dst->neighbour;
+ struct hh_cache *hh = dst->hh;
+
+ dst->hh = NULL;
+ if (hh && atomic_dec_and_test(&hh->hh_refcnt))
+ kfree(hh);
+
+ if (neigh) {
+ dst->neighbour = NULL;
+ neigh_release(neigh);
+ }
+
+ atomic_dec(&dst->ops->entries);
+
+ if (dst->ops->destroy)
+ dst->ops->destroy(dst);
+ atomic_dec(&dst_total);
+ kfree(dst);
+}
diff --git a/pfinet/linux-src/net/core/filter.c b/pfinet/linux-src/net/core/filter.c
new file mode 100644
index 00000000..8e1ffb62
--- /dev/null
+++ b/pfinet/linux-src/net/core/filter.c
@@ -0,0 +1,454 @@
+/*
+ * Linux Socket Filter - Kernel level socket filtering
+ *
+ * Author:
+ * Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
+ *
+ * Based on the design of:
+ * - The Berkeley Packet Filter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Andi Kleen - Fix a few bad bugs and races.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_FILTER)
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fcntl.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_packet.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/filter.h>
+
+/* No hurry in this branch */
+
+static u8 *load_pointer(struct sk_buff *skb, int k)
+{
+ u8 *ptr = NULL;
+
+ if (k>=SKF_NET_OFF)
+ ptr = skb->nh.raw + k - SKF_NET_OFF;
+ else if (k>=SKF_LL_OFF)
+ ptr = skb->mac.raw + k - SKF_LL_OFF;
+
+ if (ptr<skb->head && ptr < skb->tail)
+ return ptr;
+ return NULL;
+}
+
+/*
+ * Decode and apply filter instructions to the skb->data.
+ * Return length to keep, 0 for none. skb is the data we are
+ * filtering, filter is the array of filter instructions, and
+ * len is the number of filter blocks in the array.
+ */
+
+int sk_run_filter(struct sk_buff *skb, struct sock_filter *filter, int flen)
+{
+ unsigned char *data = skb->data;
+ /* len is UNSIGNED. Byte wide insns relies only on implicit
+ type casts to prevent reading arbitrary memory locations.
+ */
+ unsigned int len = skb->len;
+ struct sock_filter *fentry; /* We walk down these */
+ u32 A = 0; /* Accumulator */
+ u32 X = 0; /* Index Register */
+ u32 mem[BPF_MEMWORDS]; /* Scratch Memory Store */
+ int k;
+ int pc;
+
+ /*
+ * Process array of filter instructions.
+ */
+
+ for(pc = 0; pc < flen; pc++)
+ {
+ fentry = &filter[pc];
+
+ switch(fentry->code)
+ {
+ case BPF_ALU|BPF_ADD|BPF_X:
+ A += X;
+ continue;
+
+ case BPF_ALU|BPF_ADD|BPF_K:
+ A += fentry->k;
+ continue;
+
+ case BPF_ALU|BPF_SUB|BPF_X:
+ A -= X;
+ continue;
+
+ case BPF_ALU|BPF_SUB|BPF_K:
+ A -= fentry->k;
+ continue;
+
+ case BPF_ALU|BPF_MUL|BPF_X:
+ A *= X;
+ continue;
+
+ case BPF_ALU|BPF_MUL|BPF_K:
+ A *= fentry->k;
+ continue;
+
+ case BPF_ALU|BPF_DIV|BPF_X:
+ if(X == 0)
+ return (0);
+ A /= X;
+ continue;
+
+ case BPF_ALU|BPF_DIV|BPF_K:
+ if(fentry->k == 0)
+ return (0);
+ A /= fentry->k;
+ continue;
+
+ case BPF_ALU|BPF_AND|BPF_X:
+ A &= X;
+ continue;
+
+ case BPF_ALU|BPF_AND|BPF_K:
+ A &= fentry->k;
+ continue;
+
+ case BPF_ALU|BPF_OR|BPF_X:
+ A |= X;
+ continue;
+
+ case BPF_ALU|BPF_OR|BPF_K:
+ A |= fentry->k;
+ continue;
+
+ case BPF_ALU|BPF_LSH|BPF_X:
+ A <<= X;
+ continue;
+
+ case BPF_ALU|BPF_LSH|BPF_K:
+ A <<= fentry->k;
+ continue;
+
+ case BPF_ALU|BPF_RSH|BPF_X:
+ A >>= X;
+ continue;
+
+ case BPF_ALU|BPF_RSH|BPF_K:
+ A >>= fentry->k;
+ continue;
+
+ case BPF_ALU|BPF_NEG:
+ A = -A;
+ continue;
+
+ case BPF_JMP|BPF_JA:
+ pc += fentry->k;
+ continue;
+
+ case BPF_JMP|BPF_JGT|BPF_K:
+ pc += (A > fentry->k) ? fentry->jt : fentry->jf;
+ continue;
+
+ case BPF_JMP|BPF_JGE|BPF_K:
+ pc += (A >= fentry->k) ? fentry->jt : fentry->jf;
+ continue;
+
+ case BPF_JMP|BPF_JEQ|BPF_K:
+ pc += (A == fentry->k) ? fentry->jt : fentry->jf;
+ continue;
+
+ case BPF_JMP|BPF_JSET|BPF_K:
+ pc += (A & fentry->k) ? fentry->jt : fentry->jf;
+ continue;
+
+ case BPF_JMP|BPF_JGT|BPF_X:
+ pc += (A > X) ? fentry->jt : fentry->jf;
+ continue;
+
+ case BPF_JMP|BPF_JGE|BPF_X:
+ pc += (A >= X) ? fentry->jt : fentry->jf;
+ continue;
+
+ case BPF_JMP|BPF_JEQ|BPF_X:
+ pc += (A == X) ? fentry->jt : fentry->jf;
+ continue;
+
+ case BPF_JMP|BPF_JSET|BPF_X:
+ pc += (A & X) ? fentry->jt : fentry->jf;
+ continue;
+
+ case BPF_LD|BPF_W|BPF_ABS:
+ k = fentry->k;
+load_w:
+ if(k+sizeof(u32) <= len) {
+ A = ntohl(*(u32*)&data[k]);
+ continue;
+ }
+ if (k<0) {
+ u8 *ptr;
+
+ if (k>=SKF_AD_OFF)
+ break;
+ if ((ptr = load_pointer(skb, k)) != NULL) {
+ A = ntohl(*(u32*)ptr);
+ continue;
+ }
+ }
+ return 0;
+
+ case BPF_LD|BPF_H|BPF_ABS:
+ k = fentry->k;
+load_h:
+ if(k + sizeof(u16) <= len) {
+ A = ntohs(*(u16*)&data[k]);
+ continue;
+ }
+ if (k<0) {
+ u8 *ptr;
+
+ if (k>=SKF_AD_OFF)
+ break;
+ if ((ptr = load_pointer(skb, k)) != NULL) {
+ A = ntohs(*(u16*)ptr);
+ continue;
+ }
+ }
+ return 0;
+
+ case BPF_LD|BPF_B|BPF_ABS:
+ k = fentry->k;
+load_b:
+ if(k < len) {
+ A = data[k];
+ continue;
+ }
+ if (k<0) {
+ u8 *ptr;
+
+ if (k>=SKF_AD_OFF)
+ break;
+ if ((ptr = load_pointer(skb, k)) != NULL) {
+ A = *ptr;
+ continue;
+ }
+ }
+
+ case BPF_LD|BPF_W|BPF_LEN:
+ A = len;
+ continue;
+
+ case BPF_LDX|BPF_W|BPF_LEN:
+ X = len;
+ continue;
+
+ case BPF_LD|BPF_W|BPF_IND:
+ k = X + fentry->k;
+ goto load_w;
+
+ case BPF_LD|BPF_H|BPF_IND:
+ k = X + fentry->k;
+ goto load_h;
+
+ case BPF_LD|BPF_B|BPF_IND:
+ k = X + fentry->k;
+ goto load_b;
+
+ case BPF_LDX|BPF_B|BPF_MSH:
+ k = fentry->k;
+ if(k >= len)
+ return (0);
+ X = (data[k] & 0xf) << 2;
+ continue;
+
+ case BPF_LD|BPF_IMM:
+ A = fentry->k;
+ continue;
+
+ case BPF_LDX|BPF_IMM:
+ X = fentry->k;
+ continue;
+
+ case BPF_LD|BPF_MEM:
+ A = mem[fentry->k];
+ continue;
+
+ case BPF_LDX|BPF_MEM:
+ X = mem[fentry->k];
+ continue;
+
+ case BPF_MISC|BPF_TAX:
+ X = A;
+ continue;
+
+ case BPF_MISC|BPF_TXA:
+ A = X;
+ continue;
+
+ case BPF_RET|BPF_K:
+ return ((unsigned int)fentry->k);
+
+ case BPF_RET|BPF_A:
+ return ((unsigned int)A);
+
+ case BPF_ST:
+ mem[fentry->k] = A;
+ continue;
+
+ case BPF_STX:
+ mem[fentry->k] = X;
+ continue;
+
+ default:
+ /* Invalid instruction counts as RET */
+ return (0);
+ }
+
+ /* Handle ancillary data, which are impossible
+ (or very difficult) to get parsing packet contents.
+ */
+ switch (k-SKF_AD_OFF) {
+ case SKF_AD_PROTOCOL:
+ A = htons(skb->protocol);
+ continue;
+ case SKF_AD_PKTTYPE:
+ A = skb->pkt_type;
+ continue;
+ case SKF_AD_IFINDEX:
+ A = skb->dev->ifindex;
+ continue;
+ default:
+ return 0;
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Check the user's filter code. If we let some ugly
+ * filter code slip through kaboom!
+ */
+
+int sk_chk_filter(struct sock_filter *filter, int flen)
+{
+ struct sock_filter *ftest;
+ int pc;
+
+ /*
+ * Check the filter code now.
+ */
+ for(pc = 0; pc < flen; pc++)
+ {
+ /*
+ * All jumps are forward as they are not signed
+ */
+
+ ftest = &filter[pc];
+ if(BPF_CLASS(ftest->code) == BPF_JMP)
+ {
+ /*
+ * But they mustn't jump off the end.
+ */
+ if(BPF_OP(ftest->code) == BPF_JA)
+ {
+ /* Note, the large ftest->k might cause
+ loops. Compare this with conditional
+ jumps below, where offsets are limited. --ANK (981016)
+ */
+ if (ftest->k >= (unsigned)(flen-pc-1))
+ return (-EINVAL);
+ }
+ else
+ {
+ /*
+ * For conditionals both must be safe
+ */
+ if(pc + ftest->jt +1 >= flen || pc + ftest->jf +1 >= flen)
+ return (-EINVAL);
+ }
+ }
+
+ /*
+ * Check that memory operations use valid addresses.
+ */
+
+ if (ftest->k >= BPF_MEMWORDS)
+ {
+ /*
+ * But it might not be a memory operation...
+ */
+ switch (ftest->code) {
+ case BPF_ST:
+ case BPF_STX:
+ case BPF_LD|BPF_MEM:
+ case BPF_LDX|BPF_MEM:
+ return -EINVAL;
+ }
+ }
+ }
+
+ /*
+ * The program must end with a return. We don't care where they
+ * jumped within the script (its always forwards) but in the
+ * end they _will_ hit this.
+ */
+
+ return (BPF_CLASS(filter[flen - 1].code) == BPF_RET)?0:-EINVAL;
+}
+
+/*
+ * Attach the user's filter code. We first run some sanity checks on
+ * it to make sure it does not explode on us later.
+ */
+
+int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
+{
+ struct sk_filter *fp;
+ unsigned int fsize = sizeof(struct sock_filter) * fprog->len;
+ int err;
+
+ /* Make sure new filter is there and in the right amounts. */
+ if (fprog->filter == NULL || fprog->len > BPF_MAXINSNS)
+ return (-EINVAL);
+
+ fp = (struct sk_filter *)sock_kmalloc(sk, fsize+sizeof(*fp), GFP_KERNEL);
+ if(fp == NULL)
+ return (-ENOMEM);
+
+ if (copy_from_user(fp->insns, fprog->filter, fsize)) {
+ sock_kfree_s(sk, fp, fsize+sizeof(*fp));
+ return -EFAULT;
+ }
+
+ atomic_set(&fp->refcnt, 1);
+ fp->len = fprog->len;
+
+ if ((err = sk_chk_filter(fp->insns, fp->len))==0) {
+ struct sk_filter *old_fp = sk->filter;
+ sk->filter = fp;
+ synchronize_bh();
+ fp = old_fp;
+ }
+
+ if (fp)
+ sk_filter_release(sk, fp);
+
+ return (err);
+}
+#endif /* CONFIG_FILTER */
diff --git a/pfinet/linux-src/net/core/firewall.c b/pfinet/linux-src/net/core/firewall.c
new file mode 100644
index 00000000..fc7b1a51
--- /dev/null
+++ b/pfinet/linux-src/net/core/firewall.c
@@ -0,0 +1,160 @@
+/*
+ * Generic loadable firewalls. At the moment only IP will actually
+ * use these, but people can add the others as they are needed.
+ *
+ * Authors: Dave Bonn (for IP)
+ * much hacked by: Alan Cox
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/firewall.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <asm/semaphore.h>
+
+struct semaphore firewall_sem = MUTEX;
+static int firewall_policy[NPROTO];
+static struct firewall_ops *firewall_chain[NPROTO];
+
+/*
+ * Register a firewall
+ */
+
+int register_firewall(int pf, struct firewall_ops *fw)
+{
+ struct firewall_ops **p;
+
+ if(pf<0||pf>=NPROTO)
+ return -EINVAL;
+
+ /*
+ * Don't allow two people to adjust at once.
+ */
+
+ down(&firewall_sem);
+
+ p=&firewall_chain[pf];
+
+ while(*p)
+ {
+ if(fw->fw_priority > (*p)->fw_priority)
+ break;
+ p=&((*p)->next);
+ }
+
+ /*
+ * We need to use a memory barrier to make sure that this
+ * works correctly even in SMP with weakly ordered writes.
+ *
+ * This is atomic wrt interrupts (and generally walking the
+ * chain), but not wrt itself (so you can't call this from
+ * an interrupt. Not that you'd want to).
+ */
+
+ fw->next=*p;
+ mb();
+ *p = fw;
+
+ /*
+ * And release the sleep lock
+ */
+
+ up(&firewall_sem);
+ return 0;
+}
+
+/*
+ * Unregister a firewall
+ */
+
+int unregister_firewall(int pf, struct firewall_ops *fw)
+{
+ struct firewall_ops **nl;
+
+ if(pf<0||pf>=NPROTO)
+ return -EINVAL;
+
+ /*
+ * Don't allow two people to adjust at once.
+ */
+
+ down(&firewall_sem);
+
+ nl=&firewall_chain[pf];
+
+ while(*nl!=NULL)
+ {
+ if(*nl==fw)
+ {
+ struct firewall_ops *f=fw->next;
+ *nl = f;
+ up(&firewall_sem);
+ synchronize_bh();
+ return 0;
+ }
+ nl=&((*nl)->next);
+ }
+ up(&firewall_sem);
+ return -ENOENT;
+}
+
+int call_fw_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **skb)
+{
+ struct firewall_ops *fw=firewall_chain[pf];
+
+ while(fw!=NULL)
+ {
+ int rc=fw->fw_forward(fw,pf,dev,phdr,arg,skb);
+ if(rc!=FW_SKIP)
+ return rc;
+ fw=fw->next;
+ }
+ return firewall_policy[pf];
+}
+
+/*
+ * Actual invocation of the chains
+ */
+
+int call_in_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **skb)
+{
+ struct firewall_ops *fw=firewall_chain[pf];
+
+ while(fw!=NULL)
+ {
+ int rc=fw->fw_input(fw,pf,dev,phdr,arg,skb);
+ if(rc!=FW_SKIP)
+ return rc;
+ fw=fw->next;
+ }
+ return firewall_policy[pf];
+}
+
+int call_out_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **skb)
+{
+ struct firewall_ops *fw=firewall_chain[pf];
+
+ while(fw!=NULL)
+ {
+ int rc=fw->fw_output(fw,pf,dev,phdr,arg,skb);
+ if(rc!=FW_SKIP)
+ return rc;
+ fw=fw->next;
+ }
+ /* alan, is this right? */
+ return firewall_policy[pf];
+}
+
+EXPORT_SYMBOL(register_firewall);
+EXPORT_SYMBOL(unregister_firewall);
+EXPORT_SYMBOL(call_in_firewall);
+EXPORT_SYMBOL(call_out_firewall);
+EXPORT_SYMBOL(call_fw_firewall);
+
+__initfunc(void fwchain_init(void))
+{
+ int i;
+ for(i=0;i<NPROTO;i++)
+ firewall_policy[i]=FW_ACCEPT;
+}
diff --git a/pfinet/linux-src/net/core/iovec.c b/pfinet/linux-src/net/core/iovec.c
new file mode 100644
index 00000000..c20f8530
--- /dev/null
+++ b/pfinet/linux-src/net/core/iovec.c
@@ -0,0 +1,278 @@
+/*
+ * iovec manipulation routines.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Fixes:
+ * Andrew Lunn : Errors in iovec copying.
+ * Pedro Roque : Added memcpy_fromiovecend and
+ * csum_..._fromiovecend.
+ * Andi Kleen : fixed error handling for 2.1
+ * Alexey Kuznetsov: 2.1 optimisations
+ * Andi Kleen : Fix csum*fromiovecend for IPv6.
+ */
+
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/net.h>
+#include <linux/in6.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+#include <net/checksum.h>
+
+/*
+ * Verify iovec. The caller must ensure that the iovec is big enough
+ * to hold the message iovec.
+ *
+ * Save time not doing verify_area. copy_*_user will make this work
+ * in any case.
+ */
+
+int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode)
+{
+ int size, err, ct;
+
+ if(m->msg_namelen)
+ {
+ if(mode==VERIFY_READ)
+ {
+ err=move_addr_to_kernel(m->msg_name, m->msg_namelen, address);
+ if(err<0)
+ goto out;
+ }
+
+ m->msg_name = address;
+ } else
+ m->msg_name = NULL;
+
+ err = -EFAULT;
+ size = m->msg_iovlen * sizeof(struct iovec);
+ if (copy_from_user(iov, m->msg_iov, size))
+ goto out;
+ m->msg_iov=iov;
+
+ for (err = 0, ct = 0; ct < m->msg_iovlen; ct++) {
+ err += iov[ct].iov_len;
+ /* Goal is not to verify user data, but to prevent returning
+ negative value, which is interpreted as errno.
+ Overflow is still possible, but it is harmless.
+ */
+ if (err < 0)
+ return -EMSGSIZE;
+ }
+out:
+ return err;
+}
+
+/*
+ * Copy kernel to iovec. Returns -EFAULT on error.
+ *
+ * Note: this modifies the original iovec.
+ */
+
+int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
+{
+ int err = -EFAULT;
+
+ while(len>0)
+ {
+ if(iov->iov_len)
+ {
+ int copy = min(iov->iov_len, len);
+ if (copy_to_user(iov->iov_base, kdata, copy))
+ goto out;
+ kdata+=copy;
+ len-=copy;
+ iov->iov_len-=copy;
+ iov->iov_base+=copy;
+ }
+ iov++;
+ }
+ err = 0;
+out:
+ return err;
+}
+
+/*
+ * In kernel copy to iovec. Returns -EFAULT on error.
+ *
+ * Note: this modifies the original iovec.
+ */
+
+void memcpy_tokerneliovec(struct iovec *iov, unsigned char *kdata, int len)
+{
+ while(len>0)
+ {
+ if(iov->iov_len)
+ {
+ int copy = min(iov->iov_len, len);
+ memcpy(iov->iov_base, kdata, copy);
+ kdata+=copy;
+ len-=copy;
+ iov->iov_len-=copy;
+ iov->iov_base+=copy;
+ }
+ iov++;
+ }
+}
+
+
+/*
+ * Copy iovec to kernel. Returns -EFAULT on error.
+ *
+ * Note: this modifies the original iovec.
+ */
+
+int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
+{
+ int err = -EFAULT;
+
+ while(len>0)
+ {
+ if(iov->iov_len)
+ {
+ int copy = min(len, iov->iov_len);
+ if (copy_from_user(kdata, iov->iov_base, copy))
+ goto out;
+ len-=copy;
+ kdata+=copy;
+ iov->iov_base+=copy;
+ iov->iov_len-=copy;
+ }
+ iov++;
+ }
+ err = 0;
+out:
+ return err;
+}
+
+
+/*
+ * For use with ip_build_xmit
+ */
+
+int memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset,
+ int len)
+{
+ int err = -EFAULT;
+
+ /* Skip over the finished iovecs */
+ while(offset >= iov->iov_len)
+ {
+ offset -= iov->iov_len;
+ iov++;
+ }
+
+ while (len > 0)
+ {
+ u8 *base = iov->iov_base + offset;
+ int copy = min(len, iov->iov_len - offset);
+
+ offset = 0;
+ if (copy_from_user(kdata, base, copy))
+ goto out;
+ len -= copy;
+ kdata += copy;
+ iov++;
+ }
+ err = 0;
+out:
+ return err;
+}
+
+/*
+ * And now for the all-in-one: copy and checksum from a user iovec
+ * directly to a datagram
+ * Calls to csum_partial but the last must be in 32 bit chunks
+ *
+ * ip_build_xmit must ensure that when fragmenting only the last
+ * call to this function will be unaligned also.
+ */
+
+int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
+ int offset, unsigned int len, int *csump)
+{
+ int csum = *csump;
+ int partial_cnt = 0, err = 0;
+
+ /* Skip over the finished iovecs */
+ while (offset >= iov->iov_len)
+ {
+ offset -= iov->iov_len;
+ iov++;
+ }
+
+ while (len > 0)
+ {
+ u8 *base = iov->iov_base + offset;
+ unsigned int copy = min(len, iov->iov_len - offset);
+
+ offset = 0;
+ /* There is a remnant from previous iov. */
+ if (partial_cnt)
+ {
+ int par_len = 4 - partial_cnt;
+
+ /* iov component is too short ... */
+ if (par_len > copy) {
+ if (copy_from_user(kdata, base, copy))
+ goto out_fault;
+ kdata += copy;
+ base += copy;
+ partial_cnt += copy;
+ len -= copy;
+ iov++;
+ if (len)
+ continue;
+ *csump = csum_partial(kdata - partial_cnt,
+ partial_cnt, csum);
+ goto out;
+ }
+ if (copy_from_user(kdata, base, par_len))
+ goto out_fault;
+ csum = csum_partial(kdata - partial_cnt, 4, csum);
+ kdata += par_len;
+ base += par_len;
+ copy -= par_len;
+ len -= par_len;
+ partial_cnt = 0;
+ }
+
+ if (len > copy)
+ {
+ partial_cnt = copy % 4;
+ if (partial_cnt)
+ {
+ copy -= partial_cnt;
+ if (copy_from_user(kdata + copy, base + copy,
+ partial_cnt))
+ goto out_fault;
+ }
+ }
+
+ if (copy) {
+ csum = csum_and_copy_from_user(base, kdata, copy,
+ csum, &err);
+ if (err)
+ goto out;
+ }
+ len -= copy + partial_cnt;
+ kdata += copy + partial_cnt;
+ iov++;
+ }
+ *csump = csum;
+out:
+ return err;
+
+out_fault:
+ err = -EFAULT;
+ goto out;
+}
diff --git a/pfinet/linux-src/net/core/neighbour.c b/pfinet/linux-src/net/core/neighbour.c
new file mode 100644
index 00000000..6afbfdcc
--- /dev/null
+++ b/pfinet/linux-src/net/core/neighbour.c
@@ -0,0 +1,1394 @@
+/*
+ * Generic address resolution entity
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Fixes:
+ * Vitaly E. Lavrov releasing NULL neighbor in neigh_add.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/socket.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/sock.h>
+#include <linux/rtnetlink.h>
+
+/*
+ NOTE. The most unpleasent question is serialization of
+ accesses to resolved addresses. The problem is that addresses
+ are modified by bh, but they are referenced from normal
+ kernel thread. Before today no locking was made.
+ My reasoning was that corrupted address token will be copied
+ to packet with cosmologically small probability
+ (it is even difficult to estimate such small number)
+ and it is very silly to waste cycles in fast path to lock them.
+
+ But now I changed my mind, but not because previous statement
+ is wrong. Actually, neigh->ha MAY BE not opaque byte array,
+ but reference to some private data. In this case even neglibible
+ corruption probability becomes bug.
+
+ - hh cache is protected by rwlock. It assumes that
+ hh cache update procedure is short and fast, and that
+ read_lock is cheaper than start_bh_atomic().
+ - ha tokens, saved in neighbour entries, are protected
+ by bh_atomic().
+ - no protection is made in /proc reading. It is OK, because
+ /proc is broken by design in any case, and
+ corrupted output is normal behaviour there.
+
+ --ANK (981025)
+ */
+
+#define NEIGH_DEBUG 1
+
+#define NEIGH_PRINTK(x...) printk(x)
+#define NEIGH_NOPRINTK(x...) do { ; } while(0)
+#define NEIGH_PRINTK0 NEIGH_PRINTK
+#define NEIGH_PRINTK1 NEIGH_NOPRINTK
+#define NEIGH_PRINTK2 NEIGH_NOPRINTK
+
+#if NEIGH_DEBUG >= 1
+#undef NEIGH_PRINTK1
+#define NEIGH_PRINTK1 NEIGH_PRINTK
+#endif
+#if NEIGH_DEBUG >= 2
+#undef NEIGH_PRINTK2
+#define NEIGH_PRINTK2 NEIGH_PRINTK
+#endif
+
+static void neigh_timer_handler(unsigned long arg);
+#ifdef CONFIG_ARPD
+static void neigh_app_notify(struct neighbour *n);
+#endif
+static int pneigh_ifdown(struct neigh_table *tbl, struct device *dev);
+
+static int neigh_glbl_allocs;
+static struct neigh_table *neigh_tables;
+
+static int neigh_blackhole(struct sk_buff *skb)
+{
+ kfree_skb(skb);
+ return -ENETDOWN;
+}
+
+/*
+ * It is random distribution in the interval (1/2)*base...(3/2)*base.
+ * It corresponds to default IPv6 settings and is not overridable,
+ * because it is really reasonbale choice.
+ */
+
+unsigned long neigh_rand_reach_time(unsigned long base)
+{
+ return (net_random() % base) + (base>>1);
+}
+
+
+static int neigh_forced_gc(struct neigh_table *tbl)
+{
+ int shrunk = 0;
+ int i;
+
+ if (atomic_read(&tbl->lock))
+ return 0;
+
+ for (i=0; i<=NEIGH_HASHMASK; i++) {
+ struct neighbour *n, **np;
+
+ np = &tbl->hash_buckets[i];
+ while ((n = *np) != NULL) {
+ /* Neighbour record may be discarded if:
+ - nobody refers to it.
+ - it is not premanent
+ - (NEW and probably wrong)
+ INCOMPLETE entries are kept at least for
+ n->parms->retrans_time, otherwise we could
+ flood network with resolution requests.
+ It is not clear, what is better table overflow
+ or flooding.
+ */
+ if (atomic_read(&n->refcnt) == 0 &&
+ !(n->nud_state&NUD_PERMANENT) &&
+ (n->nud_state != NUD_INCOMPLETE ||
+ jiffies - n->used > n->parms->retrans_time)) {
+ *np = n->next;
+ n->tbl = NULL;
+ tbl->entries--;
+ shrunk = 1;
+ neigh_destroy(n);
+ continue;
+ }
+ np = &n->next;
+ }
+ }
+
+ tbl->last_flush = jiffies;
+ return shrunk;
+}
+
+int neigh_ifdown(struct neigh_table *tbl, struct device *dev)
+{
+ int i;
+
+ if (atomic_read(&tbl->lock)) {
+ NEIGH_PRINTK1("neigh_ifdown: impossible event 1763\n");
+ return -EBUSY;
+ }
+
+ start_bh_atomic();
+ for (i=0; i<=NEIGH_HASHMASK; i++) {
+ struct neighbour *n, **np;
+
+ np = &tbl->hash_buckets[i];
+ while ((n = *np) != NULL) {
+ if (dev && n->dev != dev) {
+ np = &n->next;
+ continue;
+ }
+ *np = n->next;
+ n->tbl = NULL;
+ tbl->entries--;
+ if (atomic_read(&n->refcnt)) {
+ /* The most unpleasant situation.
+ We must destroy neighbour entry,
+ but someone still uses it.
+
+ The destroy will be delayed until
+ the last user releases us, but
+ we must kill timers etc. and move
+ it to safe state.
+ */
+ if (n->nud_state & NUD_IN_TIMER)
+ del_timer(&n->timer);
+ n->parms = &tbl->parms;
+ skb_queue_purge(&n->arp_queue);
+ n->output = neigh_blackhole;
+ if (n->nud_state&NUD_VALID)
+ n->nud_state = NUD_NOARP;
+ else
+ n->nud_state = NUD_NONE;
+ NEIGH_PRINTK2("neigh %p is stray.\n", n);
+ } else
+ neigh_destroy(n);
+ }
+ }
+
+ del_timer(&tbl->proxy_timer);
+ skb_queue_purge(&tbl->proxy_queue);
+ pneigh_ifdown(tbl, dev);
+ end_bh_atomic();
+ return 0;
+}
+
+static struct neighbour *neigh_alloc(struct neigh_table *tbl, int creat)
+{
+ struct neighbour *n;
+ unsigned long now = jiffies;
+
+ if (tbl->entries > tbl->gc_thresh1) {
+ if (creat < 0)
+ return NULL;
+ if (tbl->entries > tbl->gc_thresh3 ||
+ (tbl->entries > tbl->gc_thresh2 &&
+ now - tbl->last_flush > 5*HZ)) {
+ if (neigh_forced_gc(tbl) == 0 &&
+ tbl->entries > tbl->gc_thresh3)
+ return NULL;
+ }
+ }
+
+ n = kmalloc(tbl->entry_size, GFP_ATOMIC);
+ if (n == NULL)
+ return NULL;
+
+ memset(n, 0, tbl->entry_size);
+
+ skb_queue_head_init(&n->arp_queue);
+ n->updated = n->used = now;
+ n->nud_state = NUD_NONE;
+ n->output = neigh_blackhole;
+ n->parms = &tbl->parms;
+ init_timer(&n->timer);
+ n->timer.function = neigh_timer_handler;
+ n->timer.data = (unsigned long)n;
+ tbl->stats.allocs++;
+ neigh_glbl_allocs++;
+ return n;
+}
+
+
+struct neighbour * __neigh_lookup(struct neigh_table *tbl, const void *pkey,
+ struct device *dev, int creat)
+{
+ struct neighbour *n;
+ u32 hash_val;
+ int key_len = tbl->key_len;
+
+ hash_val = *(u32*)(pkey + key_len - 4);
+ hash_val ^= (hash_val>>16);
+ hash_val ^= hash_val>>8;
+ hash_val ^= hash_val>>3;
+ hash_val = (hash_val^dev->ifindex)&NEIGH_HASHMASK;
+
+ for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
+ if (dev == n->dev &&
+ memcmp(n->primary_key, pkey, key_len) == 0) {
+ atomic_inc(&n->refcnt);
+ return n;
+ }
+ }
+ if (!creat)
+ return NULL;
+
+ n = neigh_alloc(tbl, creat);
+ if (n == NULL)
+ return NULL;
+
+ memcpy(n->primary_key, pkey, key_len);
+ n->dev = dev;
+
+ /* Protocol specific setup. */
+ if (tbl->constructor && tbl->constructor(n) < 0) {
+ neigh_destroy(n);
+ return NULL;
+ }
+
+ /* Device specific setup. */
+ if (n->parms && n->parms->neigh_setup && n->parms->neigh_setup(n) < 0) {
+ neigh_destroy(n);
+ return NULL;
+ }
+
+ n->confirmed = jiffies - (n->parms->base_reachable_time<<1);
+ atomic_set(&n->refcnt, 1);
+ tbl->entries++;
+ n->next = tbl->hash_buckets[hash_val];
+ tbl->hash_buckets[hash_val] = n;
+ n->tbl = tbl;
+ NEIGH_PRINTK2("neigh %p is created.\n", n);
+ return n;
+}
+
+struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, const void *pkey,
+ struct device *dev, int creat)
+{
+ struct pneigh_entry *n;
+ u32 hash_val;
+ int key_len = tbl->key_len;
+
+ hash_val = *(u32*)(pkey + key_len - 4);
+ hash_val ^= (hash_val>>16);
+ hash_val ^= hash_val>>8;
+ hash_val ^= hash_val>>4;
+ hash_val &= PNEIGH_HASHMASK;
+
+ for (n = tbl->phash_buckets[hash_val]; n; n = n->next) {
+ if (memcmp(n->key, pkey, key_len) == 0 &&
+ (n->dev == dev || !n->dev))
+ return n;
+ }
+ if (!creat)
+ return NULL;
+
+ n = kmalloc(sizeof(*n) + key_len, GFP_KERNEL);
+ if (n == NULL)
+ return NULL;
+
+ memcpy(n->key, pkey, key_len);
+ n->dev = dev;
+
+ if (tbl->pconstructor && tbl->pconstructor(n)) {
+ kfree(n);
+ return NULL;
+ }
+
+ n->next = tbl->phash_buckets[hash_val];
+ tbl->phash_buckets[hash_val] = n;
+ return n;
+}
+
+
+int pneigh_delete(struct neigh_table *tbl, const void *pkey, struct device *dev)
+{
+ struct pneigh_entry *n, **np;
+ u32 hash_val;
+ int key_len = tbl->key_len;
+
+ hash_val = *(u32*)(pkey + key_len - 4);
+ hash_val ^= (hash_val>>16);
+ hash_val ^= hash_val>>8;
+ hash_val ^= hash_val>>4;
+ hash_val &= PNEIGH_HASHMASK;
+
+ for (np = &tbl->phash_buckets[hash_val]; (n=*np) != NULL; np = &n->next) {
+ if (memcmp(n->key, pkey, key_len) == 0 && n->dev == dev) {
+ *np = n->next;
+ synchronize_bh();
+ if (tbl->pdestructor)
+ tbl->pdestructor(n);
+ kfree(n);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+static int pneigh_ifdown(struct neigh_table *tbl, struct device *dev)
+{
+ struct pneigh_entry *n, **np;
+ u32 h;
+
+ for (h=0; h<=PNEIGH_HASHMASK; h++) {
+ np = &tbl->phash_buckets[h];
+ while ((n=*np) != NULL) {
+ if (n->dev == dev || dev == NULL) {
+ *np = n->next;
+ synchronize_bh();
+ if (tbl->pdestructor)
+ tbl->pdestructor(n);
+ kfree(n);
+ continue;
+ }
+ np = &n->next;
+ }
+ }
+ return -ENOENT;
+}
+
+
+/*
+ * neighbour must already be out of the table;
+ *
+ */
+void neigh_destroy(struct neighbour *neigh)
+{
+ struct hh_cache *hh;
+
+ if (neigh->tbl || atomic_read(&neigh->refcnt)) {
+ NEIGH_PRINTK1("neigh_destroy: neighbour is use tbl=%p, ref=%d: "
+ "called from %p\n", neigh->tbl, atomic_read(&neigh->refcnt), __builtin_return_address(0));
+ return;
+ }
+
+ if (neigh->nud_state&NUD_IN_TIMER)
+ del_timer(&neigh->timer);
+
+ while ((hh = neigh->hh) != NULL) {
+ neigh->hh = hh->hh_next;
+ hh->hh_next = NULL;
+ hh->hh_output = neigh_blackhole;
+ if (atomic_dec_and_test(&hh->hh_refcnt))
+ kfree(hh);
+ }
+
+ if (neigh->ops && neigh->ops->destructor)
+ (neigh->ops->destructor)(neigh);
+
+ skb_queue_purge(&neigh->arp_queue);
+
+ NEIGH_PRINTK2("neigh %p is destroyed.\n", neigh);
+
+ neigh_glbl_allocs--;
+ kfree(neigh);
+}
+
+/* Neighbour state is suspicious;
+ disable fast path.
+ */
+static void neigh_suspect(struct neighbour *neigh)
+{
+ struct hh_cache *hh;
+
+ NEIGH_PRINTK2("neigh %p is suspecteded.\n", neigh);
+
+ neigh->output = neigh->ops->output;
+
+ for (hh = neigh->hh; hh; hh = hh->hh_next)
+ hh->hh_output = neigh->ops->output;
+}
+
+/* Neighbour state is OK;
+ enable fast path.
+ */
+static void neigh_connect(struct neighbour *neigh)
+{
+ struct hh_cache *hh;
+
+ NEIGH_PRINTK2("neigh %p is connected.\n", neigh);
+
+ neigh->output = neigh->ops->connected_output;
+
+ for (hh = neigh->hh; hh; hh = hh->hh_next)
+ hh->hh_output = neigh->ops->hh_output;
+}
+
+/*
+ Transitions NUD_STALE <-> NUD_REACHABLE do not occur
+ when fast path is built: we have no timers assotiated with
+ these states, we do not have time to check state when sending.
+ neigh_periodic_timer check periodically neigh->confirmed
+ time and moves NUD_REACHABLE -> NUD_STALE.
+
+ If a routine wants to know TRUE entry state, it calls
+ neigh_sync before checking state.
+ */
+
+static void neigh_sync(struct neighbour *n)
+{
+ unsigned long now = jiffies;
+ u8 state = n->nud_state;
+
+ if (state&(NUD_NOARP|NUD_PERMANENT))
+ return;
+ if (state&NUD_REACHABLE) {
+ if (now - n->confirmed > n->parms->reachable_time) {
+ n->nud_state = NUD_STALE;
+ neigh_suspect(n);
+ }
+ } else if (state&NUD_VALID) {
+ if (now - n->confirmed < n->parms->reachable_time) {
+ if (state&NUD_IN_TIMER)
+ del_timer(&n->timer);
+ n->nud_state = NUD_REACHABLE;
+ neigh_connect(n);
+ }
+ }
+}
+
+static void neigh_periodic_timer(unsigned long arg)
+{
+ struct neigh_table *tbl = (struct neigh_table*)arg;
+ unsigned long now = jiffies;
+ int i;
+
+ if (atomic_read(&tbl->lock)) {
+ tbl->gc_timer.expires = now + 1*HZ;
+ add_timer(&tbl->gc_timer);
+ return;
+ }
+
+ /*
+ * periodicly recompute ReachableTime from random function
+ */
+
+ if (now - tbl->last_rand > 300*HZ) {
+ struct neigh_parms *p;
+ tbl->last_rand = now;
+ for (p=&tbl->parms; p; p = p->next)
+ p->reachable_time = neigh_rand_reach_time(p->base_reachable_time);
+ }
+
+ for (i=0; i <= NEIGH_HASHMASK; i++) {
+ struct neighbour *n, **np;
+
+ np = &tbl->hash_buckets[i];
+ while ((n = *np) != NULL) {
+ unsigned state = n->nud_state;
+
+ if (state&(NUD_PERMANENT|NUD_IN_TIMER))
+ goto next_elt;
+
+ if ((long)(n->used - n->confirmed) < 0)
+ n->used = n->confirmed;
+
+ if (atomic_read(&n->refcnt) == 0 &&
+ (state == NUD_FAILED || now - n->used > n->parms->gc_staletime)) {
+ *np = n->next;
+ n->tbl = NULL;
+ n->next = NULL;
+ tbl->entries--;
+ neigh_destroy(n);
+ continue;
+ }
+
+ if (n->nud_state&NUD_REACHABLE &&
+ now - n->confirmed > n->parms->reachable_time) {
+ n->nud_state = NUD_STALE;
+ neigh_suspect(n);
+ }
+
+next_elt:
+ np = &n->next;
+ }
+ }
+
+ tbl->gc_timer.expires = now + tbl->gc_interval;
+ add_timer(&tbl->gc_timer);
+}
+
+static __inline__ int neigh_max_probes(struct neighbour *n)
+{
+ struct neigh_parms *p = n->parms;
+ return p->ucast_probes + p->app_probes + p->mcast_probes;
+}
+
+
+/* Called when a timer expires for a neighbour entry. */
+
+static void neigh_timer_handler(unsigned long arg)
+{
+ unsigned long now = jiffies;
+ struct neighbour *neigh = (struct neighbour*)arg;
+ unsigned state = neigh->nud_state;
+
+ if (!(state&NUD_IN_TIMER)) {
+ NEIGH_PRINTK1("neigh: timer & !nud_in_timer\n");
+ return;
+ }
+
+ if ((state&NUD_VALID) &&
+ now - neigh->confirmed < neigh->parms->reachable_time) {
+ neigh->nud_state = NUD_REACHABLE;
+ NEIGH_PRINTK2("neigh %p is still alive.\n", neigh);
+ neigh_connect(neigh);
+ return;
+ }
+ if (state == NUD_DELAY) {
+ NEIGH_PRINTK2("neigh %p is probed.\n", neigh);
+ neigh->nud_state = NUD_PROBE;
+ neigh->probes = 0;
+ }
+
+ if (neigh->probes >= neigh_max_probes(neigh)) {
+ struct sk_buff *skb;
+
+ neigh->nud_state = NUD_FAILED;
+ neigh->tbl->stats.res_failed++;
+ NEIGH_PRINTK2("neigh %p is failed.\n", neigh);
+
+ /* It is very thin place. report_unreachable is very complicated
+ routine. Particularly, it can hit the same neighbour entry!
+
+ So that, we try to be accurate and avoid dead loop. --ANK
+ */
+ while(neigh->nud_state==NUD_FAILED && (skb=__skb_dequeue(&neigh->arp_queue)) != NULL)
+ neigh->ops->error_report(neigh, skb);
+ skb_queue_purge(&neigh->arp_queue);
+ return;
+ }
+
+ neigh->timer.expires = now + neigh->parms->retrans_time;
+ add_timer(&neigh->timer);
+
+ neigh->ops->solicit(neigh, skb_peek(&neigh->arp_queue));
+ neigh->probes++;
+}
+
+int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
+{
+ start_bh_atomic();
+ if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE))) {
+ if (!(neigh->nud_state&(NUD_STALE|NUD_INCOMPLETE))) {
+ if (neigh->tbl == NULL) {
+ NEIGH_PRINTK2("neigh %p used after death.\n", neigh);
+ if (skb)
+ kfree_skb(skb);
+ end_bh_atomic();
+ return 1;
+ }
+ if (neigh->parms->mcast_probes + neigh->parms->app_probes) {
+ neigh->probes = neigh->parms->ucast_probes;
+ neigh->nud_state = NUD_INCOMPLETE;
+ neigh->timer.expires = jiffies + neigh->parms->retrans_time;
+ add_timer(&neigh->timer);
+
+ neigh->ops->solicit(neigh, skb);
+ neigh->probes++;
+ } else {
+ neigh->nud_state = NUD_FAILED;
+ if (skb)
+ kfree_skb(skb);
+ end_bh_atomic();
+ return 1;
+ }
+ }
+ if (neigh->nud_state == NUD_INCOMPLETE) {
+ if (skb) {
+ if (skb_queue_len(&neigh->arp_queue) >= neigh->parms->queue_len) {
+ struct sk_buff *buff;
+ buff = neigh->arp_queue.prev;
+ __skb_unlink(buff, &neigh->arp_queue);
+ kfree_skb(buff);
+ }
+ __skb_queue_head(&neigh->arp_queue, skb);
+ }
+ end_bh_atomic();
+ return 1;
+ }
+ if (neigh->nud_state == NUD_STALE) {
+ NEIGH_PRINTK2("neigh %p is delayed.\n", neigh);
+ neigh->nud_state = NUD_DELAY;
+ neigh->timer.expires = jiffies + neigh->parms->delay_probe_time;
+ add_timer(&neigh->timer);
+ }
+ }
+ end_bh_atomic();
+ return 0;
+}
+
+static __inline__ void neigh_update_hhs(struct neighbour *neigh)
+{
+ struct hh_cache *hh;
+ void (*update)(struct hh_cache*, struct device*, unsigned char*) =
+ neigh->dev->header_cache_update;
+
+ if (update) {
+ for (hh=neigh->hh; hh; hh=hh->hh_next) {
+ write_lock_irq(&hh->hh_lock);
+ update(hh, neigh->dev, neigh->ha);
+ write_unlock_irq(&hh->hh_lock);
+ }
+ }
+}
+
+
+
+/* Generic update routine.
+ -- lladdr is new lladdr or NULL, if it is not supplied.
+ -- new is new state.
+ -- override==1 allows to override existing lladdr, if it is different.
+ -- arp==0 means that the change is administrative.
+ */
+
+int neigh_update(struct neighbour *neigh, u8 *lladdr, u8 new, int override, int arp)
+{
+ u8 old = neigh->nud_state;
+ struct device *dev = neigh->dev;
+
+ if (arp && (old&(NUD_NOARP|NUD_PERMANENT)))
+ return -EPERM;
+
+ if (!(new&NUD_VALID)) {
+ if (old&NUD_IN_TIMER)
+ del_timer(&neigh->timer);
+ if (old&NUD_CONNECTED)
+ neigh_suspect(neigh);
+ neigh->nud_state = new;
+ return 0;
+ }
+
+ /* Compare new lladdr with cached one */
+ if (dev->addr_len == 0) {
+ /* First case: device needs no address. */
+ lladdr = neigh->ha;
+ } else if (lladdr) {
+ /* The second case: if something is already cached
+ and a new address is proposed:
+ - compare new & old
+ - if they are different, check override flag
+ */
+ if (old&NUD_VALID) {
+ if (memcmp(lladdr, neigh->ha, dev->addr_len) == 0)
+ lladdr = neigh->ha;
+ else if (!override)
+ return -EPERM;
+ }
+ } else {
+ /* No address is supplied; if we know something,
+ use it, otherwise discard the request.
+ */
+ if (!(old&NUD_VALID))
+ return -EINVAL;
+ lladdr = neigh->ha;
+ }
+
+ neigh_sync(neigh);
+ old = neigh->nud_state;
+ if (new&NUD_CONNECTED)
+ neigh->confirmed = jiffies;
+ neigh->updated = jiffies;
+
+ /* If entry was valid and address is not changed,
+ do not change entry state, if new one is STALE.
+ */
+ if (old&NUD_VALID) {
+ if (lladdr == neigh->ha)
+ if (new == old || (new == NUD_STALE && (old&NUD_CONNECTED)))
+ return 0;
+ }
+ if (old&NUD_IN_TIMER)
+ del_timer(&neigh->timer);
+ neigh->nud_state = new;
+ if (lladdr != neigh->ha) {
+ memcpy(&neigh->ha, lladdr, dev->addr_len);
+ neigh_update_hhs(neigh);
+ neigh->confirmed = jiffies - (neigh->parms->base_reachable_time<<1);
+#ifdef CONFIG_ARPD
+ if (neigh->parms->app_probes)
+ neigh_app_notify(neigh);
+#endif
+ }
+ if (new == old)
+ return 0;
+ if (new&NUD_CONNECTED)
+ neigh_connect(neigh);
+ else
+ neigh_suspect(neigh);
+ if (!(old&NUD_VALID)) {
+ struct sk_buff *skb;
+
+ /* Again: avoid dead loop if something went wrong */
+
+ while (neigh->nud_state&NUD_VALID &&
+ (skb=__skb_dequeue(&neigh->arp_queue)) != NULL) {
+ struct neighbour *n1 = neigh;
+ /* On shaper/eql skb->dst->neighbour != neigh :( */
+ if (skb->dst && skb->dst->neighbour)
+ n1 = skb->dst->neighbour;
+ n1->output(skb);
+ }
+ skb_queue_purge(&neigh->arp_queue);
+ }
+ return 0;
+}
+
+struct neighbour * neigh_event_ns(struct neigh_table *tbl,
+ u8 *lladdr, void *saddr,
+ struct device *dev)
+{
+ struct neighbour *neigh;
+
+ neigh = __neigh_lookup(tbl, saddr, dev, lladdr || !dev->addr_len);
+ if (neigh)
+ neigh_update(neigh, lladdr, NUD_STALE, 1, 1);
+ return neigh;
+}
+
+static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst, u16 protocol)
+{
+ struct hh_cache *hh = NULL;
+ struct device *dev = dst->dev;
+
+ for (hh=n->hh; hh; hh = hh->hh_next)
+ if (hh->hh_type == protocol)
+ break;
+
+ if (!hh && (hh = kmalloc(sizeof(*hh), GFP_ATOMIC)) != NULL) {
+ memset(hh, 0, sizeof(struct hh_cache));
+ hh->hh_type = protocol;
+ atomic_set(&hh->hh_refcnt, 0);
+ hh->hh_next = NULL;
+ if (dev->hard_header_cache(n, hh)) {
+ kfree(hh);
+ hh = NULL;
+ } else {
+ atomic_inc(&hh->hh_refcnt);
+ hh->hh_next = n->hh;
+ n->hh = hh;
+ if (n->nud_state&NUD_CONNECTED)
+ hh->hh_output = n->ops->hh_output;
+ else
+ hh->hh_output = n->ops->output;
+ }
+ }
+ if (hh) {
+ atomic_inc(&hh->hh_refcnt);
+ dst->hh = hh;
+ }
+}
+
+/* This function can be used in contexts, where only old dev_queue_xmit
+ worked, f.e. if you want to override normal output path (eql, shaper),
+ but resoltution is not made yet.
+ */
+
+int neigh_compat_output(struct sk_buff *skb)
+{
+ struct device *dev = skb->dev;
+
+ __skb_pull(skb, skb->nh.raw - skb->data);
+
+ if (dev->hard_header &&
+ dev->hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL, skb->len) < 0 &&
+ dev->rebuild_header(skb))
+ return 0;
+
+ return dev_queue_xmit(skb);
+}
+
+/* Slow and careful. */
+
+int neigh_resolve_output(struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb->dst;
+ struct neighbour *neigh;
+
+ if (!dst || !(neigh = dst->neighbour))
+ goto discard;
+
+ __skb_pull(skb, skb->nh.raw - skb->data);
+
+ if (neigh_event_send(neigh, skb) == 0) {
+ int err;
+ struct device *dev = neigh->dev;
+ if (dev->hard_header_cache && dst->hh == NULL) {
+ start_bh_atomic();
+ if (dst->hh == NULL)
+ neigh_hh_init(neigh, dst, dst->ops->protocol);
+ err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len);
+ end_bh_atomic();
+ } else {
+ start_bh_atomic();
+ err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len);
+ end_bh_atomic();
+ }
+ if (err >= 0)
+ return neigh->ops->queue_xmit(skb);
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+ return 0;
+
+discard:
+ NEIGH_PRINTK1("neigh_resolve_output: dst=%p neigh=%p\n", dst, dst ? dst->neighbour : NULL);
+ kfree_skb(skb);
+ return -EINVAL;
+}
+
+/* As fast as possible without hh cache */
+
+int neigh_connected_output(struct sk_buff *skb)
+{
+ int err;
+ struct dst_entry *dst = skb->dst;
+ struct neighbour *neigh = dst->neighbour;
+ struct device *dev = neigh->dev;
+
+ __skb_pull(skb, skb->nh.raw - skb->data);
+
+ start_bh_atomic();
+ err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len);
+ end_bh_atomic();
+ if (err >= 0)
+ return neigh->ops->queue_xmit(skb);
+ kfree_skb(skb);
+ return -EINVAL;
+}
+
+static void neigh_proxy_process(unsigned long arg)
+{
+ struct neigh_table *tbl = (struct neigh_table *)arg;
+ long sched_next = 0;
+ unsigned long now = jiffies;
+ struct sk_buff *skb = tbl->proxy_queue.next;
+
+ while (skb != (struct sk_buff*)&tbl->proxy_queue) {
+ struct sk_buff *back = skb;
+ long tdif = back->stamp.tv_usec - now;
+
+ skb = skb->next;
+ if (tdif <= 0) {
+ __skb_unlink(back, &tbl->proxy_queue);
+ if (tbl->proxy_redo)
+ tbl->proxy_redo(back);
+ else
+ kfree_skb(back);
+ } else if (!sched_next || tdif < sched_next)
+ sched_next = tdif;
+ }
+ del_timer(&tbl->proxy_timer);
+ if (sched_next) {
+ tbl->proxy_timer.expires = jiffies + sched_next;
+ add_timer(&tbl->proxy_timer);
+ }
+}
+
+void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
+ struct sk_buff *skb)
+{
+ unsigned long now = jiffies;
+ long sched_next = net_random()%p->proxy_delay;
+
+ if (tbl->proxy_queue.qlen > p->proxy_qlen) {
+ kfree_skb(skb);
+ return;
+ }
+ skb->stamp.tv_sec = 0;
+ skb->stamp.tv_usec = now + sched_next;
+ if (del_timer(&tbl->proxy_timer)) {
+ long tval = tbl->proxy_timer.expires - now;
+ if (tval < sched_next)
+ sched_next = tval;
+ }
+ tbl->proxy_timer.expires = now + sched_next;
+ dst_release(skb->dst);
+ skb->dst = NULL;
+ __skb_queue_tail(&tbl->proxy_queue, skb);
+ add_timer(&tbl->proxy_timer);
+}
+
+
+struct neigh_parms *neigh_parms_alloc(struct device *dev, struct neigh_table *tbl)
+{
+ struct neigh_parms *p;
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (p) {
+ memcpy(p, &tbl->parms, sizeof(*p));
+ p->tbl = tbl;
+ p->reachable_time = neigh_rand_reach_time(p->base_reachable_time);
+ if (dev && dev->neigh_setup) {
+ if (dev->neigh_setup(dev, p)) {
+ kfree(p);
+ return NULL;
+ }
+ }
+ p->next = tbl->parms.next;
+ tbl->parms.next = p;
+ }
+ return p;
+}
+
+void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)
+{
+ struct neigh_parms **p;
+
+ if (parms == NULL || parms == &tbl->parms)
+ return;
+ for (p = &tbl->parms.next; *p; p = &(*p)->next) {
+ if (*p == parms) {
+ *p = parms->next;
+ synchronize_bh();
+#ifdef CONFIG_SYSCTL
+ neigh_sysctl_unregister(parms);
+#endif
+ kfree(parms);
+ return;
+ }
+ }
+ NEIGH_PRINTK1("neigh_release_parms: not found\n");
+}
+
+
+void neigh_table_init(struct neigh_table *tbl)
+{
+ unsigned long now = jiffies;
+
+ tbl->parms.reachable_time = neigh_rand_reach_time(tbl->parms.base_reachable_time);
+
+ init_timer(&tbl->gc_timer);
+ tbl->gc_timer.data = (unsigned long)tbl;
+ tbl->gc_timer.function = neigh_periodic_timer;
+ tbl->gc_timer.expires = now + tbl->gc_interval + tbl->parms.reachable_time;
+ add_timer(&tbl->gc_timer);
+
+ init_timer(&tbl->proxy_timer);
+ tbl->proxy_timer.data = (unsigned long)tbl;
+ tbl->proxy_timer.function = neigh_proxy_process;
+ skb_queue_head_init(&tbl->proxy_queue);
+
+ tbl->last_flush = now;
+ tbl->last_rand = now + tbl->parms.reachable_time*20;
+ tbl->next = neigh_tables;
+ neigh_tables = tbl;
+}
+
+int neigh_table_clear(struct neigh_table *tbl)
+{
+ struct neigh_table **tp;
+
+ start_bh_atomic();
+ del_timer(&tbl->gc_timer);
+ del_timer(&tbl->proxy_timer);
+ skb_queue_purge(&tbl->proxy_queue);
+ neigh_ifdown(tbl, NULL);
+ end_bh_atomic();
+ if (tbl->entries)
+ printk(KERN_CRIT "neighbour leakage\n");
+ for (tp = &neigh_tables; *tp; tp = &(*tp)->next) {
+ if (*tp == tbl) {
+ *tp = tbl->next;
+ synchronize_bh();
+ break;
+ }
+ }
+#ifdef CONFIG_SYSCTL
+ neigh_sysctl_unregister(&tbl->parms);
+#endif
+ return 0;
+}
+
+#ifdef CONFIG_RTNETLINK
+
+
+int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct ndmsg *ndm = NLMSG_DATA(nlh);
+ struct rtattr **nda = arg;
+ struct neigh_table *tbl;
+ struct device *dev = NULL;
+
+ if (ndm->ndm_ifindex) {
+ if ((dev = dev_get_by_index(ndm->ndm_ifindex)) == NULL)
+ return -ENODEV;
+ }
+
+ for (tbl=neigh_tables; tbl; tbl = tbl->next) {
+ int err = 0;
+ struct neighbour *n;
+
+ if (tbl->family != ndm->ndm_family)
+ continue;
+
+ if (nda[NDA_DST-1] == NULL ||
+ nda[NDA_DST-1]->rta_len != RTA_LENGTH(tbl->key_len))
+ return -EINVAL;
+
+ if (ndm->ndm_flags&NTF_PROXY)
+ return pneigh_delete(tbl, RTA_DATA(nda[NDA_DST-1]), dev);
+
+ if (dev == NULL)
+ return -EINVAL;
+
+ start_bh_atomic();
+ n = __neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev, 0);
+ if (n) {
+ err = neigh_update(n, NULL, NUD_FAILED, 1, 0);
+ neigh_release(n);
+ }
+ end_bh_atomic();
+ return err;
+ }
+
+ return -EADDRNOTAVAIL;
+}
+
+int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct ndmsg *ndm = NLMSG_DATA(nlh);
+ struct rtattr **nda = arg;
+ struct neigh_table *tbl;
+ struct device *dev = NULL;
+
+ if (ndm->ndm_ifindex) {
+ if ((dev = dev_get_by_index(ndm->ndm_ifindex)) == NULL)
+ return -ENODEV;
+ }
+
+ for (tbl=neigh_tables; tbl; tbl = tbl->next) {
+ int err = 0;
+ struct neighbour *n;
+
+ if (tbl->family != ndm->ndm_family)
+ continue;
+ if (nda[NDA_DST-1] == NULL ||
+ nda[NDA_DST-1]->rta_len != RTA_LENGTH(tbl->key_len))
+ return -EINVAL;
+ if (ndm->ndm_flags&NTF_PROXY) {
+ if (pneigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev, 1))
+ return 0;
+ return -ENOBUFS;
+ }
+ if (dev == NULL)
+ return -EINVAL;
+ if (nda[NDA_LLADDR-1] != NULL &&
+ nda[NDA_LLADDR-1]->rta_len != RTA_LENGTH(dev->addr_len))
+ return -EINVAL;
+ start_bh_atomic();
+ n = __neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev, 0);
+ if (n) {
+ if (nlh->nlmsg_flags&NLM_F_EXCL)
+ err = -EEXIST;
+ } else if (!(nlh->nlmsg_flags&NLM_F_CREATE))
+ err = -ENOENT;
+ else {
+ n = __neigh_lookup(tbl, RTA_DATA(nda[NDA_DST-1]), dev, 1);
+ if (n == NULL)
+ err = -ENOBUFS;
+ }
+ if (err == 0) {
+ err = neigh_update(n, nda[NDA_LLADDR-1] ? RTA_DATA(nda[NDA_LLADDR-1]) : NULL,
+ ndm->ndm_state,
+ nlh->nlmsg_flags&NLM_F_REPLACE, 0);
+ }
+ if (n)
+ neigh_release(n);
+ end_bh_atomic();
+ return err;
+ }
+
+ return -EADDRNOTAVAIL;
+}
+
+
+static int neigh_fill_info(struct sk_buff *skb, struct neighbour *n,
+ u32 pid, u32 seq, int event)
+{
+ unsigned long now = jiffies;
+ struct ndmsg *ndm;
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+ struct nda_cacheinfo ci;
+
+ nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*ndm));
+ ndm = NLMSG_DATA(nlh);
+ ndm->ndm_family = n->ops->family;
+ ndm->ndm_flags = n->flags;
+ ndm->ndm_type = n->type;
+ ndm->ndm_state = n->nud_state;
+ ndm->ndm_ifindex = n->dev->ifindex;
+ RTA_PUT(skb, NDA_DST, n->tbl->key_len, n->primary_key);
+ if (n->nud_state&NUD_VALID)
+ RTA_PUT(skb, NDA_LLADDR, n->dev->addr_len, n->ha);
+ ci.ndm_used = now - n->used;
+ ci.ndm_confirmed = now - n->confirmed;
+ ci.ndm_updated = now - n->updated;
+ ci.ndm_refcnt = atomic_read(&n->refcnt);
+ RTA_PUT(skb, NDA_CACHEINFO, sizeof(ci), &ci);
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+
+static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct neighbour *n;
+ int h, s_h;
+ int idx, s_idx;
+
+ s_h = cb->args[1];
+ s_idx = idx = cb->args[2];
+ for (h=0; h <= NEIGH_HASHMASK; h++) {
+ if (h < s_h) continue;
+ if (h > s_h)
+ s_idx = 0;
+ start_bh_atomic();
+ for (n = tbl->hash_buckets[h], idx = 0; n;
+ n = n->next, idx++) {
+ if (idx < s_idx)
+ continue;
+ if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, RTM_NEWNEIGH) <= 0) {
+ end_bh_atomic();
+ cb->args[1] = h;
+ cb->args[2] = idx;
+ return -1;
+ }
+ }
+ end_bh_atomic();
+ }
+
+ cb->args[1] = h;
+ cb->args[2] = idx;
+ return skb->len;
+}
+
+int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int t;
+ int s_t;
+ struct neigh_table *tbl;
+ int family = ((struct rtgenmsg*)NLMSG_DATA(cb->nlh))->rtgen_family;
+
+ s_t = cb->args[0];
+
+ for (tbl=neigh_tables, t=0; tbl; tbl = tbl->next, t++) {
+ if (t < s_t) continue;
+ if (family && tbl->family != family)
+ continue;
+ if (t > s_t)
+ memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));
+ if (neigh_dump_table(tbl, skb, cb) < 0)
+ break;
+ }
+
+ cb->args[0] = t;
+
+ return skb->len;
+}
+
+#ifdef CONFIG_ARPD
+void neigh_app_ns(struct neighbour *n)
+{
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ int size = NLMSG_SPACE(sizeof(struct ndmsg)+256);
+
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (!skb)
+ return;
+
+ if (neigh_fill_info(skb, n, 0, 0, RTM_GETNEIGH) < 0) {
+ kfree_skb(skb);
+ return;
+ }
+ nlh = (struct nlmsghdr*)skb->data;
+ nlh->nlmsg_flags = NLM_F_REQUEST;
+ NETLINK_CB(skb).dst_groups = RTMGRP_NEIGH;
+ netlink_broadcast(rtnl, skb, 0, RTMGRP_NEIGH, GFP_ATOMIC);
+}
+
+static void neigh_app_notify(struct neighbour *n)
+{
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+ int size = NLMSG_SPACE(sizeof(struct ndmsg)+256);
+
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (!skb)
+ return;
+
+ if (neigh_fill_info(skb, n, 0, 0, RTM_NEWNEIGH) < 0) {
+ kfree_skb(skb);
+ return;
+ }
+ nlh = (struct nlmsghdr*)skb->data;
+ NETLINK_CB(skb).dst_groups = RTMGRP_NEIGH;
+ netlink_broadcast(rtnl, skb, 0, RTMGRP_NEIGH, GFP_ATOMIC);
+}
+
+
+
+#endif
+
+
+#endif
+
+#ifdef CONFIG_SYSCTL
+
+struct neigh_sysctl_table
+{
+ struct ctl_table_header *sysctl_header;
+ ctl_table neigh_vars[17];
+ ctl_table neigh_dev[2];
+ ctl_table neigh_neigh_dir[2];
+ ctl_table neigh_proto_dir[2];
+ ctl_table neigh_root_dir[2];
+} neigh_sysctl_template = {
+ NULL,
+ {{NET_NEIGH_MCAST_SOLICIT, "mcast_solicit",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_NEIGH_UCAST_SOLICIT, "ucast_solicit",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_NEIGH_APP_SOLICIT, "app_solicit",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_NEIGH_RETRANS_TIME, "retrans_time",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_NEIGH_REACHABLE_TIME, "base_reachable_time",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies},
+ {NET_NEIGH_DELAY_PROBE_TIME, "delay_first_probe_time",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies},
+ {NET_NEIGH_GC_STALE_TIME, "gc_stale_time",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies},
+ {NET_NEIGH_UNRES_QLEN, "unres_qlen",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_NEIGH_PROXY_QLEN, "proxy_qlen",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_NEIGH_ANYCAST_DELAY, "anycast_delay",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_NEIGH_PROXY_DELAY, "proxy_delay",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_NEIGH_LOCKTIME, "locktime",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_NEIGH_GC_INTERVAL, "gc_interval",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies},
+ {NET_NEIGH_GC_THRESH1, "gc_thresh1",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_NEIGH_GC_THRESH2, "gc_thresh2",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_NEIGH_GC_THRESH3, "gc_thresh3",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {0}},
+
+ {{NET_PROTO_CONF_DEFAULT, "default", NULL, 0, 0555, NULL},{0}},
+ {{0, "neigh", NULL, 0, 0555, NULL},{0}},
+ {{0, NULL, NULL, 0, 0555, NULL},{0}},
+ {{CTL_NET, "net", NULL, 0, 0555, NULL},{0}}
+};
+
+int neigh_sysctl_register(struct device *dev, struct neigh_parms *p,
+ int p_id, int pdev_id, char *p_name)
+{
+ struct neigh_sysctl_table *t;
+
+ t = kmalloc(sizeof(*t), GFP_KERNEL);
+ if (t == NULL)
+ return -ENOBUFS;
+ memcpy(t, &neigh_sysctl_template, sizeof(*t));
+ t->neigh_vars[0].data = &p->mcast_probes;
+ t->neigh_vars[1].data = &p->ucast_probes;
+ t->neigh_vars[2].data = &p->app_probes;
+ t->neigh_vars[3].data = &p->retrans_time;
+ t->neigh_vars[4].data = &p->base_reachable_time;
+ t->neigh_vars[5].data = &p->delay_probe_time;
+ t->neigh_vars[6].data = &p->gc_staletime;
+ t->neigh_vars[7].data = &p->queue_len;
+ t->neigh_vars[8].data = &p->proxy_qlen;
+ t->neigh_vars[9].data = &p->anycast_delay;
+ t->neigh_vars[10].data = &p->proxy_delay;
+ t->neigh_vars[11].data = &p->locktime;
+ if (dev) {
+ t->neigh_dev[0].procname = dev->name;
+ t->neigh_dev[0].ctl_name = dev->ifindex;
+ memset(&t->neigh_vars[12], 0, sizeof(ctl_table));
+ } else {
+ t->neigh_vars[12].data = (int*)(p+1);
+ t->neigh_vars[13].data = (int*)(p+1) + 1;
+ t->neigh_vars[14].data = (int*)(p+1) + 2;
+ t->neigh_vars[15].data = (int*)(p+1) + 3;
+ }
+ t->neigh_neigh_dir[0].ctl_name = pdev_id;
+
+ t->neigh_proto_dir[0].procname = p_name;
+ t->neigh_proto_dir[0].ctl_name = p_id;
+
+ t->neigh_dev[0].child = t->neigh_vars;
+ t->neigh_neigh_dir[0].child = t->neigh_dev;
+ t->neigh_proto_dir[0].child = t->neigh_neigh_dir;
+ t->neigh_root_dir[0].child = t->neigh_proto_dir;
+
+ t->sysctl_header = register_sysctl_table(t->neigh_root_dir, 0);
+ if (t->sysctl_header == NULL) {
+ kfree(t);
+ return -ENOBUFS;
+ }
+ p->sysctl_table = t;
+ return 0;
+}
+
+void neigh_sysctl_unregister(struct neigh_parms *p)
+{
+ if (p->sysctl_table) {
+ struct neigh_sysctl_table *t = p->sysctl_table;
+ p->sysctl_table = NULL;
+ unregister_sysctl_table(t->sysctl_header);
+ kfree(t);
+ }
+}
+
+#endif /* CONFIG_SYSCTL */
diff --git a/pfinet/linux-src/net/core/profile.c b/pfinet/linux-src/net/core/profile.c
new file mode 100644
index 00000000..fc7464b7
--- /dev/null
+++ b/pfinet/linux-src/net/core/profile.c
@@ -0,0 +1,305 @@
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/ip.h>
+#include <linux/inet.h>
+#include <net/checksum.h>
+
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <net/profile.h>
+
+#ifdef CONFIG_NET_PROFILE
+
+atomic_t net_profile_active;
+struct timeval net_profile_adjust;
+
+NET_PROFILE_DEFINE(total);
+
+struct net_profile_slot *net_profile_chain = &net_prof_total;
+
+#ifdef __alpha__
+__u32 alpha_lo;
+long alpha_hi;
+
+static void alpha_tick(unsigned long);
+
+static struct timer_list alpha_timer =
+ { NULL, NULL, 0, 0L, alpha_tick };
+
+void alpha_tick(unsigned long dummy)
+{
+ struct timeval dummy_stamp;
+ net_profile_stamp(&dummy_stamp);
+ alpha_timer.expires = jiffies + 4*HZ;
+ add_timer(&alpha_timer);
+}
+
+#endif
+
+void net_profile_irq_adjust(struct timeval *entered, struct timeval* leaved)
+{
+ struct net_profile_slot *s;
+
+ net_profile_sub(entered, leaved);
+ for (s = net_profile_chain; s; s = s->next) {
+ if (s->active)
+ net_profile_add(leaved, &s->irq);
+ }
+}
+
+
+#ifdef CONFIG_PROC_FS
+static int profile_read_proc(char *buffer, char **start, off_t offset,
+ int length, int *eof, void *data)
+{
+ off_t pos=0;
+ off_t begin=0;
+ int len=0;
+ struct net_profile_slot *s;
+
+ len+= sprintf(buffer, "Slot Hits Hi Lo OnIrqHi OnIrqLo Ufl\n");
+
+ if (offset == 0) {
+ cli();
+ net_prof_total.active = 1;
+ atomic_inc(&net_profile_active);
+ NET_PROFILE_LEAVE(total);
+ sti();
+ }
+ for (s = net_profile_chain; s; s = s->next) {
+ struct net_profile_slot tmp;
+
+ cli();
+ tmp = *s;
+
+ /* Wrong, but pretty close to truth */
+
+ s->accumulator.tv_sec = 0;
+ s->accumulator.tv_usec = 0;
+ s->irq.tv_sec = 0;
+ s->irq.tv_usec = 0;
+ s->hits = 0;
+ s->underflow = 0;
+ /* Repair active count, it is possible, only if code has a bug */
+ if (s->active) {
+ s->active = 0;
+ atomic_dec(&net_profile_active);
+ }
+ sti();
+
+ net_profile_sub(&tmp.irq, &tmp.accumulator);
+
+ len += sprintf(buffer+len,"%-15s %-10d %-10ld %-10lu %-10lu %-10lu %d/%d",
+ tmp.id,
+ tmp.hits,
+ tmp.accumulator.tv_sec,
+ tmp.accumulator.tv_usec,
+ tmp.irq.tv_sec,
+ tmp.irq.tv_usec,
+ tmp.underflow, tmp.active);
+
+ buffer[len++]='\n';
+
+ pos=begin+len;
+ if(pos<offset) {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ goto done;
+ }
+ *eof = 1;
+
+done:
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ if (len < 0) {
+ len = 0;
+ printk(KERN_CRIT "Yep, guys... our template for proc_*_read is crappy :-)\n");
+ }
+ if (offset == 0) {
+ cli();
+ net_prof_total.active = 0;
+ net_prof_total.hits = 0;
+ net_profile_stamp(&net_prof_total.entered);
+ sti();
+ }
+ return len;
+}
+#endif
+
+struct iphdr whitehole_iph;
+int whitehole_count;
+
+static int whitehole_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct net_device_stats *stats;
+ dev_kfree_skb(skb);
+ stats = (struct net_device_stats *)dev->priv;
+ stats->tx_packets++;
+ stats->tx_bytes+=skb->len;
+
+ return 0;
+}
+
+static void whitehole_inject(unsigned long);
+int whitehole_init(struct device *dev);
+
+static struct timer_list whitehole_timer =
+ { NULL, NULL, 0, 0L, whitehole_inject };
+
+static struct device whitehole_dev = {
+ "whitehole", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, whitehole_init, };
+
+static int whitehole_open(struct device *dev)
+{
+ whitehole_count = 100000;
+ whitehole_timer.expires = jiffies + 5*HZ;
+ add_timer(&whitehole_timer);
+ return 0;
+}
+
+static int whitehole_close(struct device *dev)
+{
+ del_timer(&whitehole_timer);
+ return 0;
+}
+
+static void whitehole_inject(unsigned long dummy)
+{
+ struct net_device_stats *stats = (struct net_device_stats *)whitehole_dev.priv;
+ extern int netdev_dropping;
+
+ do {
+ struct iphdr *iph;
+ struct sk_buff *skb = alloc_skb(128, GFP_ATOMIC);
+ if (!skb)
+ break;
+ skb_reserve(skb, 32);
+ iph = (struct iphdr*)skb_put(skb, sizeof(*iph));
+ skb->mac.raw = ((u8*)iph) - 14;
+ memcpy(iph, &whitehole_iph, sizeof(*iph));
+ skb->protocol = __constant_htons(ETH_P_IP);
+ skb->dev = &whitehole_dev;
+ skb->pkt_type = PACKET_HOST;
+ stats->rx_packets++;
+ stats->rx_bytes += skb->len;
+ netif_rx(skb);
+ whitehole_count--;
+ } while (netdev_dropping == 0 && whitehole_count>0);
+ if (whitehole_count > 0) {
+ whitehole_timer.expires = jiffies + 1;
+ add_timer(&whitehole_timer);
+ }
+}
+
+static struct net_device_stats *whitehole_get_stats(struct device *dev)
+{
+ struct net_device_stats *stats = (struct net_device_stats *) dev->priv;
+ return stats;
+}
+
+__initfunc(int whitehole_init(struct device *dev))
+{
+ dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOBUFS;
+ memset(dev->priv, 0, sizeof(struct net_device_stats));
+ dev->get_stats = whitehole_get_stats;
+ dev->hard_start_xmit = whitehole_xmit;
+ dev->open = whitehole_open;
+ dev->stop = whitehole_close;
+ ether_setup(dev);
+ dev->tx_queue_len = 0;
+ dev->flags |= IFF_NOARP;
+ dev->flags &= ~(IFF_BROADCAST|IFF_MULTICAST);
+ dev->iflink = 0;
+ whitehole_iph.ihl = 5;
+ whitehole_iph.version = 4;
+ whitehole_iph.ttl = 2;
+ whitehole_iph.saddr = in_aton("193.233.7.21");
+ whitehole_iph.daddr = in_aton("193.233.7.10");
+ whitehole_iph.tot_len = htons(20);
+ whitehole_iph.check = ip_compute_csum((void *)&whitehole_iph, 20);
+ return 0;
+}
+
+int net_profile_register(struct net_profile_slot *slot)
+{
+ cli();
+ slot->next = net_profile_chain;
+ net_profile_chain = slot;
+ sti();
+ return 0;
+}
+
+int net_profile_unregister(struct net_profile_slot *slot)
+{
+ struct net_profile_slot **sp, *s;
+
+ for (sp = &net_profile_chain; (s = *sp) != NULL; sp = &s->next) {
+ if (s == slot) {
+ cli();
+ *sp = s->next;
+ sti();
+ return 0;
+ }
+ }
+ return -ESRCH;
+}
+
+
+__initfunc(int net_profile_init(void))
+{
+ int i;
+
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *ent;
+
+ ent = create_proc_entry("net/profile", 0, 0);
+ ent->read_proc = profile_read_proc;
+#endif
+
+ register_netdevice(&whitehole_dev);
+
+ printk("Evaluating net profiler cost ...");
+#if CPU == 586 || CPU == 686
+ if (!(boot_cpu_data.x86_capability & X86_FEATURE_TSC)) {
+ printk(KERN_ERR "Sorry, your CPU does not support TSC. Net profiler disabled.\n");
+ return -1;
+ }
+#endif
+ start_bh_atomic();
+#ifdef __alpha__
+ alpha_tick(0);
+#endif
+ for (i=0; i<1024; i++) {
+ NET_PROFILE_ENTER(total);
+ NET_PROFILE_LEAVE(total);
+ }
+ if (net_prof_total.accumulator.tv_sec) {
+ printk(" too high!\n");
+ } else {
+ net_profile_adjust.tv_usec = net_prof_total.accumulator.tv_usec>>10;
+ printk("%ld units\n", net_profile_adjust.tv_usec);
+ }
+ net_prof_total.hits = 0;
+ net_profile_stamp(&net_prof_total.entered);
+ end_bh_atomic();
+ return 0;
+}
+
+#endif
diff --git a/pfinet/linux-src/net/core/rtnetlink.c b/pfinet/linux-src/net/core/rtnetlink.c
new file mode 100644
index 00000000..7f89e54a
--- /dev/null
+++ b/pfinet/linux-src/net/core/rtnetlink.c
@@ -0,0 +1,512 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Routing netlink socket interface: protocol independent part.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Fixes:
+ * Vitaly E. Lavrov RTA_OK arithmetics was wrong.
+ * Alexey Zhuravlev ifi_change does something useful
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/capability.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/string.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/arp.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <net/sock.h>
+#include <net/pkt_sched.h>
+
+atomic_t rtnl_rlockct;
+struct wait_queue *rtnl_wait;
+
+
+void rtnl_lock()
+{
+ rtnl_shlock();
+ rtnl_exlock();
+}
+
+void rtnl_unlock()
+{
+ rtnl_exunlock();
+ rtnl_shunlock();
+}
+
+int rtattr_parse(struct rtattr *tb[], int maxattr, struct rtattr *rta, int len)
+{
+ memset(tb, 0, sizeof(struct rtattr*)*maxattr);
+
+ while (RTA_OK(rta, len)) {
+ unsigned flavor = rta->rta_type;
+ if (flavor && flavor <= maxattr)
+ tb[flavor-1] = rta;
+ rta = RTA_NEXT(rta, len);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_RTNETLINK
+struct sock *rtnl;
+
+unsigned long rtnl_wlockct;
+
+struct rtnetlink_link * rtnetlink_links[NPROTO];
+
+#define _S 1 /* superuser privileges required */
+#define _X 2 /* exclusive access to tables required */
+#define _G 4 /* GET request */
+
+static const int rtm_min[(RTM_MAX+1-RTM_BASE)/4] =
+{
+ NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+ NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
+ NLMSG_LENGTH(sizeof(struct rtmsg)),
+ NLMSG_LENGTH(sizeof(struct ndmsg)),
+ NLMSG_LENGTH(sizeof(struct rtmsg)),
+ NLMSG_LENGTH(sizeof(struct tcmsg)),
+ NLMSG_LENGTH(sizeof(struct tcmsg)),
+ NLMSG_LENGTH(sizeof(struct tcmsg))
+};
+
+static const int rta_max[(RTM_MAX+1-RTM_BASE)/4] =
+{
+ IFLA_MAX,
+ IFA_MAX,
+ RTA_MAX,
+ NDA_MAX,
+ RTA_MAX,
+ TCA_MAX,
+ TCA_MAX,
+ TCA_MAX
+};
+
+void __rta_fill(struct sk_buff *skb, int attrtype, int attrlen, const void *data)
+{
+ struct rtattr *rta;
+ int size = RTA_LENGTH(attrlen);
+
+ rta = (struct rtattr*)skb_put(skb, RTA_ALIGN(size));
+ rta->rta_type = attrtype;
+ rta->rta_len = size;
+ memcpy(RTA_DATA(rta), data, attrlen);
+}
+
+int rtnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo)
+{
+ int err = 0;
+
+ NETLINK_CB(skb).dst_groups = group;
+ if (echo)
+ atomic_inc(&skb->users);
+ netlink_broadcast(rtnl, skb, pid, group, GFP_KERNEL);
+ if (echo)
+ err = netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT);
+ return err;
+}
+
+static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct device *dev,
+ int type, u32 pid, u32 seq, u32 change)
+{
+ struct ifinfomsg *r;
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+
+ nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*r));
+ if (pid) nlh->nlmsg_flags |= NLM_F_MULTI;
+ r = NLMSG_DATA(nlh);
+ r->ifi_family = AF_UNSPEC;
+ r->ifi_type = dev->type;
+ r->ifi_index = dev->ifindex;
+ r->ifi_flags = dev->flags;
+ r->ifi_change = change;
+
+ RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name);
+ if (dev->addr_len) {
+ RTA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr);
+ RTA_PUT(skb, IFLA_BROADCAST, dev->addr_len, dev->broadcast);
+ }
+ if (1) {
+ unsigned mtu = dev->mtu;
+ RTA_PUT(skb, IFLA_MTU, sizeof(mtu), &mtu);
+ }
+ if (dev->ifindex != dev->iflink)
+ RTA_PUT(skb, IFLA_LINK, sizeof(int), &dev->iflink);
+ if (dev->qdisc_sleeping)
+ RTA_PUT(skb, IFLA_QDISC,
+ strlen(dev->qdisc_sleeping->ops->id) + 1,
+ dev->qdisc_sleeping->ops->id);
+ if (dev->get_stats) {
+ struct net_device_stats *stats = dev->get_stats(dev);
+ if (stats)
+ RTA_PUT(skb, IFLA_STATS, sizeof(*stats), stats);
+ }
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+int rtnetlink_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int idx;
+ int s_idx = cb->args[0];
+ struct device *dev;
+
+ for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
+ if (idx < s_idx)
+ continue;
+ if (rtnetlink_fill_ifinfo(skb, dev, RTM_NEWLINK, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, 0) <= 0)
+ break;
+ }
+ cb->args[0] = idx;
+
+ return skb->len;
+}
+
+int rtnetlink_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int idx;
+ int s_idx = cb->family;
+
+ if (s_idx == 0)
+ s_idx = 1;
+ for (idx=1; idx<NPROTO; idx++) {
+ int type = cb->nlh->nlmsg_type-RTM_BASE;
+ if (idx < s_idx || idx == PF_PACKET)
+ continue;
+ if (rtnetlink_links[idx] == NULL ||
+ rtnetlink_links[idx][type].dumpit == NULL)
+ continue;
+ if (idx > s_idx)
+ memset(&cb->args[0], 0, sizeof(cb->args));
+ if (rtnetlink_links[idx][type].dumpit(skb, cb) == 0)
+ continue;
+ if (skb_tailroom(skb) < 256)
+ break;
+ }
+ cb->family = idx;
+
+ return skb->len;
+}
+
+void rtmsg_ifinfo(int type, struct device *dev)
+{
+ struct sk_buff *skb;
+ int size = NLMSG_GOODSIZE;
+
+ skb = alloc_skb(size, GFP_KERNEL);
+ if (!skb)
+ return;
+
+ if (rtnetlink_fill_ifinfo(skb, dev, type, 0, 0, ~0U) < 0) {
+ kfree_skb(skb);
+ return;
+ }
+ NETLINK_CB(skb).dst_groups = RTMGRP_LINK;
+ netlink_broadcast(rtnl, skb, 0, RTMGRP_LINK, GFP_KERNEL);
+}
+
+static int rtnetlink_done(struct netlink_callback *cb)
+{
+ if (cap_raised(NETLINK_CB(cb->skb).eff_cap, CAP_NET_ADMIN) && cb->nlh->nlmsg_flags&NLM_F_ATOMIC)
+ rtnl_shunlock();
+ return 0;
+}
+
+/* Process one rtnetlink message. */
+
+extern __inline__ int
+rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp)
+{
+ struct rtnetlink_link *link;
+ struct rtnetlink_link *link_tab;
+ struct rtattr *rta[RTATTR_MAX];
+
+ int exclusive = 0;
+ int sz_idx, kind;
+ int min_len;
+ int family;
+ int type;
+ int err;
+
+ /* Only requests are handled by kernel now */
+ if (!(nlh->nlmsg_flags&NLM_F_REQUEST))
+ return 0;
+
+ type = nlh->nlmsg_type;
+
+ /* A control message: ignore them */
+ if (type < RTM_BASE)
+ return 0;
+
+ /* Unknown message: reply with EINVAL */
+ if (type > RTM_MAX)
+ goto err_inval;
+
+ type -= RTM_BASE;
+
+ /* All the messages must have at least 1 byte length */
+ if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct rtgenmsg)))
+ return 0;
+
+ family = ((struct rtgenmsg*)NLMSG_DATA(nlh))->rtgen_family;
+ if (family > NPROTO) {
+ *errp = -EAFNOSUPPORT;
+ return -1;
+ }
+
+ link_tab = rtnetlink_links[family];
+ if (link_tab == NULL)
+ link_tab = rtnetlink_links[PF_UNSPEC];
+ link = &link_tab[type];
+
+ sz_idx = type>>2;
+ kind = type&3;
+
+ if (kind != 2 && !cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) {
+ *errp = -EPERM;
+ return -1;
+ }
+
+ if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
+ u32 rlen;
+
+ if (link->dumpit == NULL)
+ link = &(rtnetlink_links[PF_UNSPEC][type]);
+
+ if (link->dumpit == NULL)
+ goto err_inval;
+
+ /* Super-user locks all the tables to get atomic snapshot */
+ if (cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)
+ && nlh->nlmsg_flags&NLM_F_ATOMIC)
+ atomic_inc(&rtnl_rlockct);
+ if ((*errp = netlink_dump_start(rtnl, skb, nlh,
+ link->dumpit,
+ rtnetlink_done)) != 0) {
+ if (cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN) && nlh->nlmsg_flags&NLM_F_ATOMIC)
+ atomic_dec(&rtnl_rlockct);
+ return -1;
+ }
+ rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+ if (rlen > skb->len)
+ rlen = skb->len;
+ skb_pull(skb, rlen);
+ return -1;
+ }
+
+ if (kind != 2) {
+ if (rtnl_exlock_nowait()) {
+ *errp = 0;
+ return -1;
+ }
+ exclusive = 1;
+ }
+
+ memset(&rta, 0, sizeof(rta));
+
+ min_len = rtm_min[sz_idx];
+ if (nlh->nlmsg_len < min_len)
+ goto err_inval;
+
+ if (nlh->nlmsg_len > min_len) {
+ int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
+ struct rtattr *attr = (void*)nlh + NLMSG_ALIGN(min_len);
+
+ while (RTA_OK(attr, attrlen)) {
+ unsigned flavor = attr->rta_type;
+ if (flavor) {
+ if (flavor > rta_max[sz_idx])
+ goto err_inval;
+ rta[flavor-1] = attr;
+ }
+ attr = RTA_NEXT(attr, attrlen);
+ }
+ }
+
+ if (link->doit == NULL)
+ link = &(rtnetlink_links[PF_UNSPEC][type]);
+ if (link->doit == NULL)
+ goto err_inval;
+ err = link->doit(skb, nlh, (void *)&rta);
+
+ if (exclusive)
+ rtnl_exunlock();
+ *errp = err;
+ return err;
+
+err_inval:
+ if (exclusive)
+ rtnl_exunlock();
+ *errp = -EINVAL;
+ return -1;
+}
+
+/*
+ * Process one packet of messages.
+ * Malformed skbs with wrong lengths of messages are discarded silently.
+ */
+
+extern __inline__ int rtnetlink_rcv_skb(struct sk_buff *skb)
+{
+ int err;
+ struct nlmsghdr * nlh;
+
+ while (skb->len >= NLMSG_SPACE(0)) {
+ u32 rlen;
+
+ nlh = (struct nlmsghdr *)skb->data;
+ if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
+ return 0;
+ rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+ if (rlen > skb->len)
+ rlen = skb->len;
+ if (rtnetlink_rcv_msg(skb, nlh, &err)) {
+ /* Not error, but we must interrupt processing here:
+ * Note, that in this case we do not pull message
+ * from skb, it will be processed later.
+ */
+ if (err == 0)
+ return -1;
+ netlink_ack(skb, nlh, err);
+ } else if (nlh->nlmsg_flags&NLM_F_ACK)
+ netlink_ack(skb, nlh, 0);
+ skb_pull(skb, rlen);
+ }
+
+ return 0;
+}
+
+/*
+ * rtnetlink input queue processing routine:
+ * - try to acquire shared lock. If it is failed, defer processing.
+ * - feed skbs to rtnetlink_rcv_skb, until it refuse a message,
+ * that will occur, when a dump started and/or acquisition of
+ * exclusive lock failed.
+ */
+
+static void rtnetlink_rcv(struct sock *sk, int len)
+{
+ struct sk_buff *skb;
+
+ if (rtnl_shlock_nowait())
+ return;
+
+ while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
+ if (rtnetlink_rcv_skb(skb)) {
+ if (skb->len)
+ skb_queue_head(&sk->receive_queue, skb);
+ else
+ kfree_skb(skb);
+ break;
+ }
+ kfree_skb(skb);
+ }
+
+ rtnl_shunlock();
+}
+
+static struct rtnetlink_link link_rtnetlink_table[RTM_MAX-RTM_BASE+1] =
+{
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, rtnetlink_dump_ifinfo, },
+ { NULL, NULL, },
+
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, rtnetlink_dump_all, },
+ { NULL, NULL, },
+
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, rtnetlink_dump_all, },
+ { NULL, NULL, },
+
+ { neigh_add, NULL, },
+ { neigh_delete, NULL, },
+ { NULL, neigh_dump_info, },
+ { NULL, NULL, },
+
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+};
+
+
+static int rtnetlink_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ struct device *dev = ptr;
+ switch (event) {
+ case NETDEV_UNREGISTER:
+ rtmsg_ifinfo(RTM_DELLINK, dev);
+ break;
+ default:
+ rtmsg_ifinfo(RTM_NEWLINK, dev);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+struct notifier_block rtnetlink_dev_notifier = {
+ rtnetlink_event,
+ NULL,
+ 0
+};
+
+
+__initfunc(void rtnetlink_init(void))
+{
+#ifdef RTNL_DEBUG
+ printk("Initializing RT netlink socket\n");
+#endif
+ rtnl = netlink_kernel_create(NETLINK_ROUTE, rtnetlink_rcv);
+ if (rtnl == NULL)
+ panic("rtnetlink_init: cannot initialize rtnetlink\n");
+ register_netdevice_notifier(&rtnetlink_dev_notifier);
+ rtnetlink_links[PF_UNSPEC] = link_rtnetlink_table;
+ rtnetlink_links[PF_PACKET] = link_rtnetlink_table;
+}
+
+
+
+#endif
diff --git a/pfinet/linux-src/net/core/scm.c b/pfinet/linux-src/net/core/scm.c
new file mode 100644
index 00000000..cdb5f3d0
--- /dev/null
+++ b/pfinet/linux-src/net/core/scm.c
@@ -0,0 +1,280 @@
+/* scm.c - Socket level control messages processing.
+ *
+ * Author: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * Alignment and value checking mods by Craig Metz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/stat.h>
+#include <linux/socket.h>
+#include <linux/file.h>
+#include <linux/fcntl.h>
+#include <linux/net.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <linux/inet.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/rarp.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/scm.h>
+
+
+/*
+ * Only allow a user to send credentials, that they could set with
+ * setu(g)id.
+ */
+
+static __inline__ int scm_check_creds(struct ucred *creds)
+{
+ if ((creds->pid == current->pid || capable(CAP_SYS_ADMIN)) &&
+ ((creds->uid == current->uid || creds->uid == current->euid ||
+ creds->uid == current->suid) || capable(CAP_SETUID)) &&
+ ((creds->gid == current->gid || creds->gid == current->egid ||
+ creds->gid == current->sgid) || capable(CAP_SETGID))) {
+ return 0;
+ }
+ return -EPERM;
+}
+
+static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
+{
+ int *fdp = (int*)CMSG_DATA(cmsg);
+ struct scm_fp_list *fpl = *fplp;
+ struct file **fpp;
+ int i, num;
+
+ num = (cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)))/sizeof(int);
+
+ if (num <= 0)
+ return 0;
+
+ if (num > SCM_MAX_FD)
+ return -EINVAL;
+
+ if (!fpl)
+ {
+ fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL);
+ if (!fpl)
+ return -ENOMEM;
+ *fplp = fpl;
+ fpl->count = 0;
+ }
+ fpp = &fpl->fp[fpl->count];
+
+ if (fpl->count + num > SCM_MAX_FD)
+ return -EINVAL;
+
+ /*
+ * Verify the descriptors and increment the usage count.
+ */
+
+ for (i=0; i< num; i++)
+ {
+ int fd = fdp[i];
+ struct file *file;
+
+ if (fd < 0 || !(file = fget(fd)))
+ return -EBADF;
+ *fpp++ = file;
+ fpl->count++;
+ }
+ return num;
+}
+
+void __scm_destroy(struct scm_cookie *scm)
+{
+ struct scm_fp_list *fpl = scm->fp;
+ int i;
+
+ if (fpl) {
+ scm->fp = NULL;
+ for (i=fpl->count-1; i>=0; i--)
+ fput(fpl->fp[i]);
+ kfree(fpl);
+ }
+}
+
+int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
+{
+ struct cmsghdr *cmsg;
+ int err;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg))
+ {
+ err = -EINVAL;
+
+ /* Verify that cmsg_len is at least sizeof(struct cmsghdr) */
+ /* The first check was omitted in <= 2.2.5. The reasoning was
+ that parser checks cmsg_len in any case, so that
+ additional check would be work duplication.
+ But if cmsg_level is not SOL_SOCKET, we do not check
+ for too short ancillary data object at all! Oops.
+ OK, let's add it...
+ */
+ if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
+ (unsigned long)(((char*)cmsg - (char*)msg->msg_control)
+ + cmsg->cmsg_len) > msg->msg_controllen)
+ goto error;
+
+ if (cmsg->cmsg_level != SOL_SOCKET)
+ continue;
+
+ switch (cmsg->cmsg_type)
+ {
+ case SCM_RIGHTS:
+ err=scm_fp_copy(cmsg, &p->fp);
+ if (err<0)
+ goto error;
+ break;
+ case SCM_CREDENTIALS:
+ if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred)))
+ goto error;
+ memcpy(&p->creds, CMSG_DATA(cmsg), sizeof(struct ucred));
+ err = scm_check_creds(&p->creds);
+ if (err)
+ goto error;
+ break;
+ default:
+ goto error;
+ }
+ }
+
+ if (p->fp && !p->fp->count)
+ {
+ kfree(p->fp);
+ p->fp = NULL;
+ }
+
+ err = -EINVAL;
+ if (msg->msg_flags & MSG_CTLFLAGS)
+ goto error;
+
+ return 0;
+
+error:
+ scm_destroy(p);
+ return err;
+}
+
+int put_cmsg(struct msghdr * msg, int level, int type, int len, void *data)
+{
+ struct cmsghdr *cm = (struct cmsghdr*)msg->msg_control;
+ struct cmsghdr cmhdr;
+ int cmlen = CMSG_LEN(len);
+ int err;
+
+ if (cm==NULL || msg->msg_controllen < sizeof(*cm)) {
+ msg->msg_flags |= MSG_CTRUNC;
+ return 0; /* XXX: return error? check spec. */
+ }
+ if (msg->msg_controllen < cmlen) {
+ msg->msg_flags |= MSG_CTRUNC;
+ cmlen = msg->msg_controllen;
+ }
+ cmhdr.cmsg_level = level;
+ cmhdr.cmsg_type = type;
+ cmhdr.cmsg_len = cmlen;
+
+ err = -EFAULT;
+ if (copy_to_user(cm, &cmhdr, sizeof cmhdr))
+ goto out;
+ if (copy_to_user(CMSG_DATA(cm), data, cmlen - sizeof(struct cmsghdr)))
+ goto out;
+ cmlen = CMSG_SPACE(len);
+ msg->msg_control += cmlen;
+ msg->msg_controllen -= cmlen;
+ err = 0;
+out:
+ return err;
+}
+
+void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
+{
+ struct cmsghdr *cm = (struct cmsghdr*)msg->msg_control;
+
+ int fdmax = (msg->msg_controllen - sizeof(struct cmsghdr))/sizeof(int);
+ int fdnum = scm->fp->count;
+ struct file **fp = scm->fp->fp;
+ int *cmfptr;
+ int err = 0, i;
+
+ if (fdnum < fdmax)
+ fdmax = fdnum;
+
+ for (i=0, cmfptr=(int*)CMSG_DATA(cm); i<fdmax; i++, cmfptr++)
+ {
+ int new_fd;
+ err = get_unused_fd();
+ if (err < 0)
+ break;
+ new_fd = err;
+ err = put_user(new_fd, cmfptr);
+ if (err) {
+ put_unused_fd(new_fd);
+ break;
+ }
+ /* Bump the usage count and install the file. */
+ fp[i]->f_count++;
+ current->files->fd[new_fd] = fp[i];
+ }
+
+ if (i > 0)
+ {
+ int cmlen = CMSG_LEN(i*sizeof(int));
+ if (!err)
+ err = put_user(SOL_SOCKET, &cm->cmsg_level);
+ if (!err)
+ err = put_user(SCM_RIGHTS, &cm->cmsg_type);
+ if (!err)
+ err = put_user(cmlen, &cm->cmsg_len);
+ if (!err) {
+ cmlen = CMSG_SPACE(i*sizeof(int));
+ msg->msg_control += cmlen;
+ msg->msg_controllen -= cmlen;
+ }
+ }
+ if (i < fdnum)
+ msg->msg_flags |= MSG_CTRUNC;
+
+ /*
+ * All of the files that fit in the message have had their
+ * usage counts incremented, so we just free the list.
+ */
+ __scm_destroy(scm);
+}
+
+struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
+{
+ struct scm_fp_list *new_fpl;
+ int i;
+
+ if (!fpl)
+ return NULL;
+
+ new_fpl = kmalloc(sizeof(*fpl), GFP_KERNEL);
+ if (new_fpl) {
+ memcpy(new_fpl, fpl, sizeof(*fpl));
+
+ for (i=fpl->count-1; i>=0; i--)
+ fpl->fp[i]->f_count++;
+ }
+ return new_fpl;
+}
diff --git a/pfinet/linux-src/net/core/skbuff.c b/pfinet/linux-src/net/core/skbuff.c
new file mode 100644
index 00000000..b7636437
--- /dev/null
+++ b/pfinet/linux-src/net/core/skbuff.c
@@ -0,0 +1,385 @@
+/*
+ * Routines having to do with the 'struct sk_buff' memory handlers.
+ *
+ * Authors: Alan Cox <iiitac@pyr.swan.ac.uk>
+ * Florian La Roche <rzsfl@rz.uni-sb.de>
+ *
+ * Version: $Id: skbuff.c,v 1.55 1999/02/23 08:12:27 davem Exp $
+ *
+ * Fixes:
+ * Alan Cox : Fixed the worst of the load balancer bugs.
+ * Dave Platt : Interrupt stacking fix.
+ * Richard Kooijman : Timestamp fixes.
+ * Alan Cox : Changed buffer format.
+ * Alan Cox : destructor hook for AF_UNIX etc.
+ * Linus Torvalds : Better skb_clone.
+ * Alan Cox : Added skb_copy.
+ * Alan Cox : Added all the changed routines Linus
+ * only put in the headers
+ * Ray VanTassle : Fixed --skb->lock in free
+ * Alan Cox : skb_copy copy arp field
+ * Andi Kleen : slabified it.
+ *
+ * NOTE:
+ * The __skb_ routines should be called with interrupts
+ * disabled, or you better be *real* sure that the operation is atomic
+ * with respect to whatever list is being frobbed (e.g. via lock_sock()
+ * or via disabling bottom half handlers, etc).
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * The functions in this file will not compile correctly with gcc 2.4.x
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/dst.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <net/sock.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+/*
+ * Skb list spinlock
+ */
+spinlock_t skb_queue_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Resource tracking variables
+ */
+
+static atomic_t net_skbcount = ATOMIC_INIT(0);
+static atomic_t net_allocs = ATOMIC_INIT(0);
+static atomic_t net_fails = ATOMIC_INIT(0);
+
+extern atomic_t ip_frag_mem;
+
+static kmem_cache_t *skbuff_head_cache;
+
+/*
+ * Keep out-of-line to prevent kernel bloat.
+ * __builtin_return_address is not used because it is not always
+ * reliable.
+ */
+
+void skb_over_panic(struct sk_buff *skb, int sz, void *here)
+{
+ panic("skput:over: %p:%d put:%d dev:%s",
+ here, skb->len, sz, skb->dev ? skb->dev->name : "<NULL>");
+}
+
+void skb_under_panic(struct sk_buff *skb, int sz, void *here)
+{
+ panic("skput:under: %p:%d put:%d dev:%s",
+ here, skb->len, sz, skb->dev ? skb->dev->name : "<NULL>");
+}
+
+void show_net_buffers(void)
+{
+ printk("Networking buffers in use : %u\n",
+ atomic_read(&net_skbcount));
+ printk("Total network buffer allocations : %u\n",
+ atomic_read(&net_allocs));
+ printk("Total failed network buffer allocs : %u\n",
+ atomic_read(&net_fails));
+#ifdef CONFIG_INET
+ printk("IP fragment buffer size : %u\n",
+ atomic_read(&ip_frag_mem));
+#endif
+}
+
+/* Allocate a new skbuff. We do this ourselves so we can fill in a few
+ * 'private' fields and also do memory statistics to find all the
+ * [BEEP] leaks.
+ *
+ */
+
+struct sk_buff *alloc_skb(unsigned int size,int gfp_mask)
+{
+ struct sk_buff *skb;
+ u8 *data;
+
+ if (in_interrupt() && (gfp_mask & __GFP_WAIT)) {
+ static int count = 0;
+ if (++count < 5) {
+ printk(KERN_ERR "alloc_skb called nonatomically "
+ "from interrupt %p\n", __builtin_return_address(0));
+ }
+ gfp_mask &= ~__GFP_WAIT;
+ }
+
+ /* Get the HEAD */
+ skb = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
+ if (skb == NULL)
+ goto nohead;
+
+ /* Get the DATA. Size must match skb_add_mtu(). */
+ size = ((size + 15) & ~15);
+ data = kmalloc(size + sizeof(atomic_t), gfp_mask);
+ if (data == NULL)
+ goto nodata;
+
+ /* Note that this counter is useless now - you can just look in the
+ * skbuff_head entry in /proc/slabinfo. We keep it only for emergency
+ * cases.
+ */
+ atomic_inc(&net_allocs);
+
+ skb->truesize = size;
+
+ atomic_inc(&net_skbcount);
+
+ /* Load the data pointers. */
+ skb->head = data;
+ skb->data = data;
+ skb->tail = data;
+ skb->end = data + size;
+
+ /* Set up other state */
+ skb->len = 0;
+ skb->is_clone = 0;
+ skb->cloned = 0;
+
+ atomic_set(&skb->users, 1);
+ atomic_set(skb_datarefp(skb), 1);
+ return skb;
+
+nodata:
+ kmem_cache_free(skbuff_head_cache, skb);
+nohead:
+ atomic_inc(&net_fails);
+ return NULL;
+}
+
+
+/*
+ * Slab constructor for a skb head.
+ */
+static inline void skb_headerinit(void *p, kmem_cache_t *cache,
+ unsigned long flags)
+{
+ struct sk_buff *skb = p;
+
+ skb->destructor = NULL;
+ skb->pkt_type = PACKET_HOST; /* Default type */
+ skb->pkt_bridged = 0; /* Not bridged */
+ skb->prev = skb->next = NULL;
+ skb->list = NULL;
+ skb->sk = NULL;
+ skb->stamp.tv_sec=0; /* No idea about time */
+ skb->ip_summed = 0;
+ skb->security = 0; /* By default packets are insecure */
+ skb->dst = NULL;
+#ifdef CONFIG_IP_FIREWALL
+ skb->fwmark = 0;
+#endif
+ memset(skb->cb, 0, sizeof(skb->cb));
+ skb->priority = 0;
+}
+
+/*
+ * Free an skbuff by memory without cleaning the state.
+ */
+void kfree_skbmem(struct sk_buff *skb)
+{
+ if (!skb->cloned || atomic_dec_and_test(skb_datarefp(skb)))
+ kfree(skb->head);
+
+ kmem_cache_free(skbuff_head_cache, skb);
+ atomic_dec(&net_skbcount);
+}
+
+/*
+ * Free an sk_buff. Release anything attached to the buffer. Clean the state.
+ */
+
+void __kfree_skb(struct sk_buff *skb)
+{
+ if (skb->list)
+ printk(KERN_WARNING "Warning: kfree_skb passed an skb still "
+ "on a list (from %p).\n", __builtin_return_address(0));
+
+ dst_release(skb->dst);
+ if(skb->destructor)
+ skb->destructor(skb);
+ skb_headerinit(skb, NULL, 0); /* clean state */
+ kfree_skbmem(skb);
+}
+
+/*
+ * Duplicate an sk_buff. The new one is not owned by a socket.
+ */
+
+struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask)
+{
+ struct sk_buff *n;
+
+ n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
+ if (!n)
+ return NULL;
+
+ memcpy(n, skb, sizeof(*n));
+ atomic_inc(skb_datarefp(skb));
+ skb->cloned = 1;
+
+ atomic_inc(&net_allocs);
+ atomic_inc(&net_skbcount);
+ dst_clone(n->dst);
+ n->cloned = 1;
+ n->next = n->prev = NULL;
+ n->list = NULL;
+ n->sk = NULL;
+ n->is_clone = 1;
+ atomic_set(&n->users, 1);
+ n->destructor = NULL;
+ return n;
+}
+
+/*
+ * This is slower, and copies the whole data area
+ */
+
+struct sk_buff *skb_copy(struct sk_buff *skb, int gfp_mask)
+{
+ struct sk_buff *n;
+ unsigned long offset;
+
+ /*
+ * Allocate the copy buffer
+ */
+
+ n=alloc_skb(skb->end - skb->head, gfp_mask);
+ if(n==NULL)
+ return NULL;
+
+ /*
+ * Shift between the two data areas in bytes
+ */
+
+ offset=n->head-skb->head;
+
+ /* Set the data pointer */
+ skb_reserve(n,skb->data-skb->head);
+ /* Set the tail pointer and length */
+ skb_put(n,skb->len);
+ /* Copy the bytes */
+ memcpy(n->head,skb->head,skb->end-skb->head);
+ n->csum = skb->csum;
+ n->list=NULL;
+ n->sk=NULL;
+ n->dev=skb->dev;
+ n->priority=skb->priority;
+ n->protocol=skb->protocol;
+ n->dst=dst_clone(skb->dst);
+ n->h.raw=skb->h.raw+offset;
+ n->nh.raw=skb->nh.raw+offset;
+ n->mac.raw=skb->mac.raw+offset;
+ memcpy(n->cb, skb->cb, sizeof(skb->cb));
+ n->used=skb->used;
+ n->is_clone=0;
+ atomic_set(&n->users, 1);
+ n->pkt_type=skb->pkt_type;
+ n->stamp=skb->stamp;
+ n->destructor = NULL;
+ n->security=skb->security;
+#ifdef CONFIG_IP_FIREWALL
+ n->fwmark = skb->fwmark;
+#endif
+ return n;
+}
+
+struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, int newheadroom)
+{
+ struct sk_buff *n;
+ unsigned long offset;
+ int headroom = skb_headroom(skb);
+
+ /*
+ * Allocate the copy buffer
+ */
+
+ n=alloc_skb(skb->truesize+newheadroom-headroom, GFP_ATOMIC);
+ if(n==NULL)
+ return NULL;
+
+ skb_reserve(n,newheadroom);
+
+ /*
+ * Shift between the two data areas in bytes
+ */
+
+ offset=n->data-skb->data;
+
+ /* Set the tail pointer and length */
+ skb_put(n,skb->len);
+ /* Copy the bytes */
+ memcpy(n->data,skb->data,skb->len);
+ n->list=NULL;
+ n->sk=NULL;
+ n->priority=skb->priority;
+ n->protocol=skb->protocol;
+ n->dev=skb->dev;
+ n->dst=dst_clone(skb->dst);
+ n->h.raw=skb->h.raw+offset;
+ n->nh.raw=skb->nh.raw+offset;
+ n->mac.raw=skb->mac.raw+offset;
+ memcpy(n->cb, skb->cb, sizeof(skb->cb));
+ n->used=skb->used;
+ n->is_clone=0;
+ atomic_set(&n->users, 1);
+ n->pkt_type=skb->pkt_type;
+ n->stamp=skb->stamp;
+ n->destructor = NULL;
+ n->security=skb->security;
+#ifdef CONFIG_IP_FIREWALL
+ n->fwmark = skb->fwmark;
+#endif
+
+ return n;
+}
+
+#if 0
+/*
+ * Tune the memory allocator for a new MTU size.
+ */
+void skb_add_mtu(int mtu)
+{
+ /* Must match allocation in alloc_skb */
+ mtu = ((mtu + 15) & ~15) + sizeof(atomic_t);
+
+ kmem_add_cache_size(mtu);
+}
+#endif
+
+void __init skb_init(void)
+{
+ skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
+ sizeof(struct sk_buff),
+ 0,
+ SLAB_HWCACHE_ALIGN,
+ skb_headerinit, NULL);
+ if (!skbuff_head_cache)
+ panic("cannot create skbuff cache");
+}
diff --git a/pfinet/linux-src/net/core/sock.c b/pfinet/linux-src/net/core/sock.c
new file mode 100644
index 00000000..c47c935b
--- /dev/null
+++ b/pfinet/linux-src/net/core/sock.c
@@ -0,0 +1,1051 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Generic socket support routines. Memory allocators, socket lock/release
+ * handler for protocols to use and generic option handler.
+ *
+ *
+ * Version: $Id: sock.c,v 1.80 1999/05/08 03:04:34 davem Exp $
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Florian La Roche, <flla@stud.uni-sb.de>
+ * Alan Cox, <A.Cox@swansea.ac.uk>
+ *
+ * Fixes:
+ * Alan Cox : Numerous verify_area() problems
+ * Alan Cox : Connecting on a connecting socket
+ * now returns an error for tcp.
+ * Alan Cox : sock->protocol is set correctly.
+ * and is not sometimes left as 0.
+ * Alan Cox : connect handles icmp errors on a
+ * connect properly. Unfortunately there
+ * is a restart syscall nasty there. I
+ * can't match BSD without hacking the C
+ * library. Ideas urgently sought!
+ * Alan Cox : Disallow bind() to addresses that are
+ * not ours - especially broadcast ones!!
+ * Alan Cox : Socket 1024 _IS_ ok for users. (fencepost)
+ * Alan Cox : sock_wfree/sock_rfree don't destroy sockets,
+ * instead they leave that for the DESTROY timer.
+ * Alan Cox : Clean up error flag in accept
+ * Alan Cox : TCP ack handling is buggy, the DESTROY timer
+ * was buggy. Put a remove_sock() in the handler
+ * for memory when we hit 0. Also altered the timer
+ * code. The ACK stuff can wait and needs major
+ * TCP layer surgery.
+ * Alan Cox : Fixed TCP ack bug, removed remove sock
+ * and fixed timer/inet_bh race.
+ * Alan Cox : Added zapped flag for TCP
+ * Alan Cox : Move kfree_skb into skbuff.c and tidied up surplus code
+ * Alan Cox : for new sk_buff allocations wmalloc/rmalloc now call alloc_skb
+ * Alan Cox : kfree_s calls now are kfree_skbmem so we can track skb resources
+ * Alan Cox : Supports socket option broadcast now as does udp. Packet and raw need fixing.
+ * Alan Cox : Added RCVBUF,SNDBUF size setting. It suddenly occurred to me how easy it was so...
+ * Rick Sladkey : Relaxed UDP rules for matching packets.
+ * C.E.Hawkins : IFF_PROMISC/SIOCGHWADDR support
+ * Pauline Middelink : identd support
+ * Alan Cox : Fixed connect() taking signals I think.
+ * Alan Cox : SO_LINGER supported
+ * Alan Cox : Error reporting fixes
+ * Anonymous : inet_create tidied up (sk->reuse setting)
+ * Alan Cox : inet sockets don't set sk->type!
+ * Alan Cox : Split socket option code
+ * Alan Cox : Callbacks
+ * Alan Cox : Nagle flag for Charles & Johannes stuff
+ * Alex : Removed restriction on inet fioctl
+ * Alan Cox : Splitting INET from NET core
+ * Alan Cox : Fixed bogus SO_TYPE handling in getsockopt()
+ * Adam Caldwell : Missing return in SO_DONTROUTE/SO_DEBUG code
+ * Alan Cox : Split IP from generic code
+ * Alan Cox : New kfree_skbmem()
+ * Alan Cox : Make SO_DEBUG superuser only.
+ * Alan Cox : Allow anyone to clear SO_DEBUG
+ * (compatibility fix)
+ * Alan Cox : Added optimistic memory grabbing for AF_UNIX throughput.
+ * Alan Cox : Allocator for a socket is settable.
+ * Alan Cox : SO_ERROR includes soft errors.
+ * Alan Cox : Allow NULL arguments on some SO_ opts
+ * Alan Cox : Generic socket allocation to make hooks
+ * easier (suggested by Craig Metz).
+ * Michael Pall : SO_ERROR returns positive errno again
+ * Steve Whitehouse: Added default destructor to free
+ * protocol private data.
+ * Steve Whitehouse: Added various other default routines
+ * common to several socket families.
+ * Chris Evans : Call suser() check last on F_SETOWN
+ * Jay Schulist : Added SO_ATTACH_FILTER and SO_DETACH_FILTER.
+ * Andi Kleen : Add sock_kmalloc()/sock_kfree_s()
+ * Andi Kleen : Fix write_space callback
+ *
+ * To Fix:
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/arp.h>
+#include <net/rarp.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/raw.h>
+#include <net/icmp.h>
+#include <linux/ipsec.h>
+
+#ifdef CONFIG_FILTER
+#include <linux/filter.h>
+#endif
+
+#define min(a,b) ((a)<(b)?(a):(b))
+
+/* Run time adjustable parameters. */
+__u32 sysctl_wmem_max = SK_WMEM_MAX;
+__u32 sysctl_rmem_max = SK_RMEM_MAX;
+__u32 sysctl_wmem_default = SK_WMEM_MAX;
+__u32 sysctl_rmem_default = SK_RMEM_MAX;
+
+/* Maximal space eaten by iovec or ancillary data plus some space */
+int sysctl_optmem_max = sizeof(unsigned long)*(2*UIO_MAXIOV + 512);
+
+/*
+ * This is meant for all protocols to use and covers goings on
+ * at the socket level. Everything here is generic.
+ */
+
+int sock_setsockopt(struct socket *sock, int level, int optname,
+ char *optval, int optlen)
+{
+ struct sock *sk=sock->sk;
+#ifdef CONFIG_FILTER
+ struct sk_filter *filter;
+#endif
+ int val;
+ int valbool;
+ int err;
+ struct linger ling;
+ int ret = 0;
+
+ /*
+ * Options without arguments
+ */
+
+#ifdef SO_DONTLINGER /* Compatibility item... */
+ switch(optname)
+ {
+ case SO_DONTLINGER:
+ sk->linger=0;
+ return 0;
+ }
+#endif
+
+ if(optlen<sizeof(int))
+ return(-EINVAL);
+
+ err = get_user(val, (int *)optval);
+ if (err)
+ return err;
+
+ valbool = val?1:0;
+
+ switch(optname)
+ {
+ case SO_DEBUG:
+ if(val && !capable(CAP_NET_ADMIN))
+ {
+ ret = -EACCES;
+ }
+ else
+ sk->debug=valbool;
+ break;
+ case SO_REUSEADDR:
+ sk->reuse = valbool;
+ break;
+ case SO_TYPE:
+ case SO_ERROR:
+ ret = -ENOPROTOOPT;
+ break;
+ case SO_DONTROUTE:
+ sk->localroute=valbool;
+ break;
+ case SO_BROADCAST:
+ sk->broadcast=valbool;
+ break;
+ case SO_SNDBUF:
+ /* Don't error on this BSD doesn't and if you think
+ about it this is right. Otherwise apps have to
+ play 'guess the biggest size' games. RCVBUF/SNDBUF
+ are treated in BSD as hints */
+
+ if (val > sysctl_wmem_max)
+ val = sysctl_wmem_max;
+
+ sk->sndbuf = max(val*2,2048);
+
+ /*
+ * Wake up sending tasks if we
+ * upped the value.
+ */
+ sk->write_space(sk);
+ break;
+
+ case SO_RCVBUF:
+ /* Don't error on this BSD doesn't and if you think
+ about it this is right. Otherwise apps have to
+ play 'guess the biggest size' games. RCVBUF/SNDBUF
+ are treated in BSD as hints */
+
+ if (val > sysctl_rmem_max)
+ val = sysctl_rmem_max;
+
+ /* FIXME: is this lower bound the right one? */
+ sk->rcvbuf = max(val*2,256);
+ break;
+
+ case SO_KEEPALIVE:
+#ifdef CONFIG_INET
+ if (sk->protocol == IPPROTO_TCP)
+ {
+ tcp_set_keepalive(sk, valbool);
+ }
+#endif
+ sk->keepopen = valbool;
+ break;
+
+ case SO_OOBINLINE:
+ sk->urginline = valbool;
+ break;
+
+ case SO_NO_CHECK:
+ sk->no_check = valbool;
+ break;
+
+ case SO_PRIORITY:
+ if ((val >= 0 && val <= 6) || capable(CAP_NET_ADMIN))
+ sk->priority = val;
+ else
+ return(-EPERM);
+ break;
+
+ case SO_LINGER:
+ if(optlen<sizeof(ling))
+ return -EINVAL; /* 1003.1g */
+ err = copy_from_user(&ling,optval,sizeof(ling));
+ if (err)
+ {
+ ret = -EFAULT;
+ break;
+ }
+ if(ling.l_onoff==0)
+ sk->linger=0;
+ else
+ {
+ sk->lingertime=ling.l_linger;
+ sk->linger=1;
+ }
+ break;
+
+ case SO_BSDCOMPAT:
+ sk->bsdism = valbool;
+ break;
+
+ case SO_PASSCRED:
+ sock->passcred = valbool;
+ break;
+
+
+#ifdef CONFIG_NETDEVICES
+ case SO_BINDTODEVICE:
+ {
+ char devname[IFNAMSIZ];
+
+ /* Sorry... */
+ if (!capable(CAP_NET_RAW))
+ return -EPERM;
+
+ /* Bind this socket to a particular device like "eth0",
+ * as specified in the passed interface name. If the
+ * name is "" or the option length is zero the socket
+ * is not bound.
+ */
+
+ if (!valbool) {
+ sk->bound_dev_if = 0;
+ } else {
+ if (optlen > IFNAMSIZ)
+ optlen = IFNAMSIZ;
+ if (copy_from_user(devname, optval, optlen))
+ return -EFAULT;
+
+ /* Remove any cached route for this socket. */
+ lock_sock(sk);
+ dst_release(xchg(&sk->dst_cache, NULL));
+ release_sock(sk);
+
+ if (devname[0] == '\0') {
+ sk->bound_dev_if = 0;
+ } else {
+ struct device *dev = dev_get(devname);
+ if (!dev)
+ return -EINVAL;
+ sk->bound_dev_if = dev->ifindex;
+ }
+ return 0;
+ }
+ }
+#endif
+
+
+#ifdef CONFIG_FILTER
+ case SO_ATTACH_FILTER:
+ ret = -EINVAL;
+ if (optlen == sizeof(struct sock_fprog)) {
+ struct sock_fprog fprog;
+
+ ret = -EFAULT;
+ if (copy_from_user(&fprog, optval, sizeof(fprog)))
+ break;
+
+ ret = sk_attach_filter(&fprog, sk);
+ }
+ break;
+
+ case SO_DETACH_FILTER:
+ filter = sk->filter;
+ if(filter) {
+ sk->filter = NULL;
+ synchronize_bh();
+ sk_filter_release(sk, filter);
+ return 0;
+ }
+ return -ENOENT;
+#endif
+ /* We implement the SO_SNDLOWAT etc to
+ not be settable (1003.1g 5.3) */
+ default:
+ return(-ENOPROTOOPT);
+ }
+ return ret;
+}
+
+
+int sock_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen)
+{
+ struct sock *sk = sock->sk;
+
+ union
+ {
+ int val;
+ struct linger ling;
+ struct timeval tm;
+ } v;
+
+ int lv=sizeof(int),len;
+
+ if(get_user(len,optlen))
+ return -EFAULT;
+
+ switch(optname)
+ {
+ case SO_DEBUG:
+ v.val = sk->debug;
+ break;
+
+ case SO_DONTROUTE:
+ v.val = sk->localroute;
+ break;
+
+ case SO_BROADCAST:
+ v.val= sk->broadcast;
+ break;
+
+ case SO_SNDBUF:
+ v.val=sk->sndbuf;
+ break;
+
+ case SO_RCVBUF:
+ v.val =sk->rcvbuf;
+ break;
+
+ case SO_REUSEADDR:
+ v.val = sk->reuse;
+ break;
+
+ case SO_KEEPALIVE:
+ v.val = sk->keepopen;
+ break;
+
+ case SO_TYPE:
+ v.val = sk->type;
+ break;
+
+ case SO_ERROR:
+ v.val = -sock_error(sk);
+ if(v.val==0)
+ v.val=xchg(&sk->err_soft,0);
+ break;
+
+ case SO_OOBINLINE:
+ v.val = sk->urginline;
+ break;
+
+ case SO_NO_CHECK:
+ v.val = sk->no_check;
+ break;
+
+ case SO_PRIORITY:
+ v.val = sk->priority;
+ break;
+
+ case SO_LINGER:
+ lv=sizeof(v.ling);
+ v.ling.l_onoff=sk->linger;
+ v.ling.l_linger=sk->lingertime;
+ break;
+
+ case SO_BSDCOMPAT:
+ v.val = sk->bsdism;
+ break;
+
+ case SO_RCVTIMEO:
+ case SO_SNDTIMEO:
+ lv=sizeof(struct timeval);
+ v.tm.tv_sec=0;
+ v.tm.tv_usec=0;
+ break;
+
+ case SO_RCVLOWAT:
+ case SO_SNDLOWAT:
+ v.val=1;
+ break;
+
+ case SO_PASSCRED:
+ v.val = sock->passcred;
+ break;
+
+ case SO_PEERCRED:
+ lv=sizeof(sk->peercred);
+ len=min(len, lv);
+ if(copy_to_user((void*)optval, &sk->peercred, len))
+ return -EFAULT;
+ goto lenout;
+
+ default:
+ return(-ENOPROTOOPT);
+ }
+ len=min(len,lv);
+ if(copy_to_user(optval,&v,len))
+ return -EFAULT;
+lenout:
+ if(put_user(len, optlen))
+ return -EFAULT;
+ return 0;
+}
+
+static kmem_cache_t *sk_cachep;
+
+/*
+ * All socket objects are allocated here. This is for future
+ * usage.
+ */
+
+struct sock *sk_alloc(int family, int priority, int zero_it)
+{
+ struct sock *sk = kmem_cache_alloc(sk_cachep, priority);
+
+ if(sk) {
+ if (zero_it)
+ memset(sk, 0, sizeof(struct sock));
+ sk->family = family;
+ }
+
+ return sk;
+}
+
+void sk_free(struct sock *sk)
+{
+#ifdef CONFIG_FILTER
+ struct sk_filter *filter;
+#endif
+ if (sk->destruct)
+ sk->destruct(sk);
+
+#ifdef CONFIG_FILTER
+ filter = sk->filter;
+ if (filter) {
+ sk_filter_release(sk, filter);
+ sk->filter = NULL;
+ }
+#endif
+
+ if (atomic_read(&sk->omem_alloc))
+ printk(KERN_DEBUG "sk_free: optmem leakage (%d bytes) detected.\n", atomic_read(&sk->omem_alloc));
+
+ kmem_cache_free(sk_cachep, sk);
+}
+
+void __init sk_init(void)
+{
+ sk_cachep = kmem_cache_create("sock", sizeof(struct sock), 0,
+ SLAB_HWCACHE_ALIGN, 0, 0);
+
+}
+
+/*
+ * Simple resource managers for sockets.
+ */
+
+
+/*
+ * Write buffer destructor automatically called from kfree_skb.
+ */
+void sock_wfree(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+
+ /* In case it might be waiting for more memory. */
+ atomic_sub(skb->truesize, &sk->wmem_alloc);
+ sk->write_space(sk);
+}
+
+/*
+ * Read buffer destructor automatically called from kfree_skb.
+ */
+void sock_rfree(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+
+ atomic_sub(skb->truesize, &sk->rmem_alloc);
+}
+
+
+/*
+ * Allocate a skb from the socket's send buffer.
+ */
+struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force, int priority)
+{
+ if (force || atomic_read(&sk->wmem_alloc) < sk->sndbuf) {
+ struct sk_buff * skb = alloc_skb(size, priority);
+ if (skb) {
+ atomic_add(skb->truesize, &sk->wmem_alloc);
+ skb->destructor = sock_wfree;
+ skb->sk = sk;
+ return skb;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Allocate a skb from the socket's receive buffer.
+ */
+struct sk_buff *sock_rmalloc(struct sock *sk, unsigned long size, int force, int priority)
+{
+ if (force || atomic_read(&sk->rmem_alloc) < sk->rcvbuf) {
+ struct sk_buff *skb = alloc_skb(size, priority);
+ if (skb) {
+ atomic_add(skb->truesize, &sk->rmem_alloc);
+ skb->destructor = sock_rfree;
+ skb->sk = sk;
+ return skb;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Allocate a memory block from the socket's option memory buffer.
+ */
+void *sock_kmalloc(struct sock *sk, int size, int priority)
+{
+ if (atomic_read(&sk->omem_alloc)+size < sysctl_optmem_max) {
+ void *mem;
+ /* First do the add, to avoid the race if kmalloc
+ * might sleep.
+ */
+ atomic_add(size, &sk->omem_alloc);
+ mem = kmalloc(size, priority);
+ if (mem)
+ return mem;
+ atomic_sub(size, &sk->omem_alloc);
+ }
+ return NULL;
+}
+
+/*
+ * Free an option memory block.
+ */
+void sock_kfree_s(struct sock *sk, void *mem, int size)
+{
+ kfree_s(mem, size);
+ atomic_sub(size, &sk->omem_alloc);
+}
+
+/* FIXME: this is insane. We are trying suppose to be controlling how
+ * how much space we have for data bytes, not packet headers.
+ * This really points out that we need a better system for doing the
+ * receive buffer. -- erics
+ * WARNING: This is currently ONLY used in tcp. If you need it else where
+ * this will probably not be what you want. Possibly these two routines
+ * should move over to the ipv4 directory.
+ */
+unsigned long sock_rspace(struct sock *sk)
+{
+ int amt = 0;
+
+ if (sk != NULL) {
+ /* This used to have some bizarre complications that
+ * to attempt to reserve some amount of space. This doesn't
+ * make sense, since the number returned here does not
+ * actually reflect allocated space, but rather the amount
+ * of space we committed to. We gamble that we won't
+ * run out of memory, and returning a smaller number does
+ * not change the gamble. If we lose the gamble tcp still
+ * works, it may just slow down for retransmissions.
+ */
+ amt = sk->rcvbuf - atomic_read(&sk->rmem_alloc);
+ if (amt < 0)
+ amt = 0;
+ }
+ return amt;
+}
+
+
+/* It is almost wait_for_tcp_memory minus release_sock/lock_sock.
+ I think, these locks should be removed for datagram sockets.
+ */
+static void sock_wait_for_wmem(struct sock * sk)
+{
+ struct wait_queue wait = { current, NULL };
+
+ sk->socket->flags &= ~SO_NOSPACE;
+ add_wait_queue(sk->sleep, &wait);
+ for (;;) {
+ if (signal_pending(current))
+ break;
+ current->state = TASK_INTERRUPTIBLE;
+ if (atomic_read(&sk->wmem_alloc) < sk->sndbuf)
+ break;
+ if (sk->shutdown & SEND_SHUTDOWN)
+ break;
+ if (sk->err)
+ break;
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(sk->sleep, &wait);
+}
+
+
+/*
+ * Generic send/receive buffer handlers
+ */
+
+struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size,
+ unsigned long fallback, int noblock, int *errcode)
+{
+ int err;
+ struct sk_buff *skb;
+
+ while (1) {
+ unsigned long try_size = size;
+
+ err = sock_error(sk);
+ if (err != 0)
+ goto failure;
+
+ /*
+ * We should send SIGPIPE in these cases according to
+ * 1003.1g draft 6.4. If we (the user) did a shutdown()
+ * call however we should not.
+ *
+ * Note: This routine isn't just used for datagrams and
+ * anyway some datagram protocols have a notion of
+ * close down.
+ */
+
+ err = -EPIPE;
+ if (sk->shutdown&SEND_SHUTDOWN)
+ goto failure;
+
+ if (fallback) {
+ /* The buffer get won't block, or use the atomic queue.
+ * It does produce annoying no free page messages still.
+ */
+ skb = sock_wmalloc(sk, size, 0, GFP_BUFFER);
+ if (skb)
+ break;
+ try_size = fallback;
+ }
+ skb = sock_wmalloc(sk, try_size, 0, sk->allocation);
+ if (skb)
+ break;
+
+ /*
+ * This means we have too many buffers for this socket already.
+ */
+
+ sk->socket->flags |= SO_NOSPACE;
+ err = -EAGAIN;
+ if (noblock)
+ goto failure;
+ err = -ERESTARTSYS;
+ if (signal_pending(current))
+ goto failure;
+ sock_wait_for_wmem(sk);
+ }
+
+ return skb;
+
+failure:
+ *errcode = err;
+ return NULL;
+}
+
+
+void __release_sock(struct sock *sk)
+{
+#ifdef CONFIG_INET
+ if (!sk->prot || !sk->backlog_rcv)
+ return;
+
+ /* See if we have any packets built up. */
+ start_bh_atomic();
+ while (!skb_queue_empty(&sk->back_log)) {
+ struct sk_buff * skb = sk->back_log.next;
+ __skb_unlink(skb, &sk->back_log);
+ sk->backlog_rcv(sk, skb);
+ }
+ end_bh_atomic();
+#endif
+}
+
+
+/*
+ * Generic socket manager library. Most simpler socket families
+ * use this to manage their socket lists. At some point we should
+ * hash these. By making this generic we get the lot hashed for free.
+ */
+
+void sklist_remove_socket(struct sock **list, struct sock *sk)
+{
+ struct sock *s;
+
+ start_bh_atomic();
+
+ s= *list;
+ if(s==sk)
+ {
+ *list = s->next;
+ end_bh_atomic();
+ return;
+ }
+ while(s && s->next)
+ {
+ if(s->next==sk)
+ {
+ s->next=sk->next;
+ break;
+ }
+ s=s->next;
+ }
+ end_bh_atomic();
+}
+
+void sklist_insert_socket(struct sock **list, struct sock *sk)
+{
+ start_bh_atomic();
+ sk->next= *list;
+ *list=sk;
+ end_bh_atomic();
+}
+
+/*
+ * This is only called from user mode. Thus it protects itself against
+ * interrupt users but doesn't worry about being called during work.
+ * Once it is removed from the queue no interrupt or bottom half will
+ * touch it and we are (fairly 8-) ) safe.
+ */
+
+void sklist_destroy_socket(struct sock **list, struct sock *sk);
+
+/*
+ * Handler for deferred kills.
+ */
+
+static void sklist_destroy_timer(unsigned long data)
+{
+ struct sock *sk=(struct sock *)data;
+ sklist_destroy_socket(NULL,sk);
+}
+
+/*
+ * Destroy a socket. We pass NULL for a list if we know the
+ * socket is not on a list.
+ */
+
+void sklist_destroy_socket(struct sock **list,struct sock *sk)
+{
+ struct sk_buff *skb;
+ if(list)
+ sklist_remove_socket(list, sk);
+
+ while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
+ {
+ kfree_skb(skb);
+ }
+
+ if(atomic_read(&sk->wmem_alloc) == 0 &&
+ atomic_read(&sk->rmem_alloc) == 0 &&
+ sk->dead)
+ {
+ sk_free(sk);
+ }
+ else
+ {
+ /*
+ * Someone is using our buffers still.. defer
+ */
+ init_timer(&sk->timer);
+ sk->timer.expires=jiffies+SOCK_DESTROY_TIME;
+ sk->timer.function=sklist_destroy_timer;
+ sk->timer.data = (unsigned long)sk;
+ add_timer(&sk->timer);
+ }
+}
+
+/*
+ * Set of default routines for initialising struct proto_ops when
+ * the protocol does not support a particular function. In certain
+ * cases where it makes no sense for a protocol to have a "do nothing"
+ * function, some default processing is provided.
+ */
+
+int sock_no_dup(struct socket *newsock, struct socket *oldsock)
+{
+ struct sock *sk = oldsock->sk;
+
+ return net_families[sk->family]->create(newsock, sk->protocol);
+}
+
+int sock_no_release(struct socket *sock, struct socket *peersock)
+{
+ return 0;
+}
+
+int sock_no_bind(struct socket *sock, struct sockaddr *saddr, int len)
+{
+ return -EOPNOTSUPP;
+}
+
+int sock_no_connect(struct socket *sock, struct sockaddr *saddr,
+ int len, int flags)
+{
+ return -EOPNOTSUPP;
+}
+
+int sock_no_socketpair(struct socket *sock1, struct socket *sock2)
+{
+ return -EOPNOTSUPP;
+}
+
+int sock_no_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ return -EOPNOTSUPP;
+}
+
+int sock_no_getname(struct socket *sock, struct sockaddr *saddr,
+ int *len, int peer)
+{
+ return -EOPNOTSUPP;
+}
+
+unsigned int sock_no_poll(struct file * file, struct socket *sock, poll_table *pt)
+{
+ return 0;
+}
+
+int sock_no_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ return -EOPNOTSUPP;
+}
+
+int sock_no_listen(struct socket *sock, int backlog)
+{
+ return -EOPNOTSUPP;
+}
+
+int sock_no_shutdown(struct socket *sock, int how)
+{
+ return -EOPNOTSUPP;
+}
+
+int sock_no_setsockopt(struct socket *sock, int level, int optname,
+ char *optval, int optlen)
+{
+ return -EOPNOTSUPP;
+}
+
+int sock_no_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen)
+{
+ return -EOPNOTSUPP;
+}
+
+/*
+ * Note: if you add something that sleeps here then change sock_fcntl()
+ * to do proper fd locking.
+ */
+int sock_no_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk = sock->sk;
+
+ switch(cmd)
+ {
+ case F_SETOWN:
+ /*
+ * This is a little restrictive, but it's the only
+ * way to make sure that you can't send a sigurg to
+ * another process.
+ */
+ if (current->pgrp != -arg &&
+ current->pid != arg &&
+ !capable(CAP_KILL)) return(-EPERM);
+ sk->proc = arg;
+ return(0);
+ case F_GETOWN:
+ return(sk->proc);
+ default:
+ return(-EINVAL);
+ }
+}
+
+int sock_no_sendmsg(struct socket *sock, struct msghdr *m, int flags,
+ struct scm_cookie *scm)
+{
+ return -EOPNOTSUPP;
+}
+
+int sock_no_recvmsg(struct socket *sock, struct msghdr *m, int flags,
+ struct scm_cookie *scm)
+{
+ return -EOPNOTSUPP;
+}
+
+
+
+/*
+ * Default Socket Callbacks
+ */
+
+void sock_def_wakeup(struct sock *sk)
+{
+ if(!sk->dead)
+ wake_up_interruptible(sk->sleep);
+}
+
+void sock_def_error_report(struct sock *sk)
+{
+ if (!sk->dead) {
+ wake_up_interruptible(sk->sleep);
+ sock_wake_async(sk->socket,0);
+ }
+}
+
+void sock_def_readable(struct sock *sk, int len)
+{
+ if(!sk->dead) {
+ wake_up_interruptible(sk->sleep);
+ sock_wake_async(sk->socket,1);
+ }
+}
+
+void sock_def_write_space(struct sock *sk)
+{
+ /* Do not wake up a writer until he can make "significant"
+ * progress. --DaveM
+ */
+ if(!sk->dead &&
+ ((atomic_read(&sk->wmem_alloc) << 1) <= sk->sndbuf)) {
+ wake_up_interruptible(sk->sleep);
+
+ /* Should agree with poll, otherwise some programs break */
+ if (sock_writeable(sk))
+ sock_wake_async(sk->socket, 2);
+ }
+}
+
+void sock_def_destruct(struct sock *sk)
+{
+ if (sk->protinfo.destruct_hook)
+ kfree(sk->protinfo.destruct_hook);
+}
+
+void sock_init_data(struct socket *sock, struct sock *sk)
+{
+ skb_queue_head_init(&sk->receive_queue);
+ skb_queue_head_init(&sk->write_queue);
+ skb_queue_head_init(&sk->back_log);
+ skb_queue_head_init(&sk->error_queue);
+
+ init_timer(&sk->timer);
+
+ sk->allocation = GFP_KERNEL;
+ sk->rcvbuf = sysctl_rmem_default;
+ sk->sndbuf = sysctl_wmem_default;
+ sk->state = TCP_CLOSE;
+ sk->zapped = 1;
+ sk->socket = sock;
+
+ if(sock)
+ {
+ sk->type = sock->type;
+ sk->sleep = &sock->wait;
+ sock->sk = sk;
+ }
+
+ sk->state_change = sock_def_wakeup;
+ sk->data_ready = sock_def_readable;
+ sk->write_space = sock_def_write_space;
+ sk->error_report = sock_def_error_report;
+ sk->destruct = sock_def_destruct;
+
+ sk->peercred.pid = 0;
+ sk->peercred.uid = -1;
+ sk->peercred.gid = -1;
+
+}
diff --git a/pfinet/linux-src/net/core/sysctl_net_core.c b/pfinet/linux-src/net/core/sysctl_net_core.c
new file mode 100644
index 00000000..446ca145
--- /dev/null
+++ b/pfinet/linux-src/net/core/sysctl_net_core.c
@@ -0,0 +1,61 @@
+/* -*- linux-c -*-
+ * sysctl_net_core.c: sysctl interface to net core subsystem.
+ *
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net/core directory entry (empty =) ). [MS]
+ */
+
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+#include <linux/config.h>
+
+#ifdef CONFIG_SYSCTL
+
+extern int netdev_max_backlog;
+extern int netdev_fastroute;
+extern int net_msg_cost;
+extern int net_msg_burst;
+
+extern __u32 sysctl_wmem_max;
+extern __u32 sysctl_rmem_max;
+extern __u32 sysctl_wmem_default;
+extern __u32 sysctl_rmem_default;
+
+extern int sysctl_core_destroy_delay;
+extern int sysctl_optmem_max;
+
+ctl_table core_table[] = {
+#ifdef CONFIG_NET
+ {NET_CORE_WMEM_MAX, "wmem_max",
+ &sysctl_wmem_max, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_CORE_RMEM_MAX, "rmem_max",
+ &sysctl_rmem_max, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_CORE_WMEM_DEFAULT, "wmem_default",
+ &sysctl_wmem_default, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_CORE_RMEM_DEFAULT, "rmem_default",
+ &sysctl_rmem_default, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_CORE_MAX_BACKLOG, "netdev_max_backlog",
+ &netdev_max_backlog, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+#ifdef CONFIG_NET_FASTROUTE
+ {NET_CORE_FASTROUTE, "netdev_fastroute",
+ &netdev_fastroute, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+#endif
+ {NET_CORE_MSG_COST, "message_cost",
+ &net_msg_cost, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies},
+ {NET_CORE_MSG_BURST, "message_burst",
+ &net_msg_burst, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies},
+ {NET_CORE_OPTMEM_MAX, "optmem_max",
+ &sysctl_optmem_max, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+#endif /* CONFIG_NET */
+ { 0 }
+};
+#endif
diff --git a/pfinet/linux-src/net/core/utils.c b/pfinet/linux-src/net/core/utils.c
new file mode 100644
index 00000000..415926b8
--- /dev/null
+++ b/pfinet/linux-src/net/core/utils.c
@@ -0,0 +1,66 @@
+/*
+ * Generic address resultion entity
+ *
+ * Authors:
+ * net_random Alan Cox
+ * net_ratelimit Andy Kleen
+ *
+ * Created by Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+
+static unsigned long net_rand_seed = 152L;
+
+unsigned long net_random(void)
+{
+ net_rand_seed=net_rand_seed*69069L+1;
+ return net_rand_seed^jiffies;
+}
+
+void net_srandom(unsigned long entropy)
+{
+ net_rand_seed ^= entropy;
+ net_random();
+}
+
+int net_msg_cost = 5*HZ;
+int net_msg_burst = 10*5*HZ;
+
+/*
+ * This enforces a rate limit: not more than one kernel message
+ * every 5secs to make a denial-of-service attack impossible.
+ *
+ * All warning printk()s should be guarded by this function.
+ */
+int net_ratelimit(void)
+{
+ static unsigned long toks = 10*5*HZ;
+ static unsigned long last_msg;
+ static int missed;
+ unsigned long now = jiffies;
+
+ toks += now - xchg(&last_msg, now);
+ if (toks > net_msg_burst)
+ toks = net_msg_burst;
+ if (toks >= net_msg_cost) {
+ toks -= net_msg_cost;
+ if (missed)
+ printk(KERN_WARNING "NET: %d messages suppressed.\n", missed);
+ missed = 0;
+ return 1;
+ }
+ missed++;
+ return 0;
+}
diff --git a/pfinet/linux-src/net/ethernet/Makefile b/pfinet/linux-src/net/ethernet/Makefile
new file mode 100644
index 00000000..193d6af8
--- /dev/null
+++ b/pfinet/linux-src/net/ethernet/Makefile
@@ -0,0 +1,33 @@
+#
+# Makefile for the Linux Ethernet layer.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := ethernet.o
+
+OBJS := eth.o
+
+ifeq ($(CONFIG_SYSCTL),y)
+OBJS += sysctl_net_ether.o
+endif
+
+ifdef CONFIG_IPX
+OBJ2 := pe2.o
+endif
+
+ifdef CONFIG_ATALK
+OBJ2 := pe2.o
+endif
+
+ifdef CONFIG_NET
+O_OBJS := $(OBJS) $(OBJ2)
+endif
+
+include $(TOPDIR)/Rules.make
+
+tar:
+ tar -cvf /dev/f1 .
diff --git a/pfinet/linux-src/net/ethernet/eth.c b/pfinet/linux-src/net/ethernet/eth.c
new file mode 100644
index 00000000..945f4a06
--- /dev/null
+++ b/pfinet/linux-src/net/ethernet/eth.c
@@ -0,0 +1,298 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Ethernet-type device handling.
+ *
+ * Version: @(#)eth.c 1.0.7 05/25/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ * Florian La Roche, <rzsfl@rz.uni-sb.de>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ *
+ * Fixes:
+ * Mr Linux : Arp problems
+ * Alan Cox : Generic queue tidyup (very tiny here)
+ * Alan Cox : eth_header ntohs should be htons
+ * Alan Cox : eth_rebuild_header missing an htons and
+ * minor other things.
+ * Tegge : Arp bug fixes.
+ * Florian : Removed many unnecessary functions, code cleanup
+ * and changes for new arp and skbuff.
+ * Alan Cox : Redid header building to reflect new format.
+ * Alan Cox : ARP only when compiled with CONFIG_INET
+ * Greg Page : 802.2 and SNAP stuff.
+ * Alan Cox : MAC layer pointers/new format.
+ * Paul Gortmaker : eth_copy_and_sum shouldn't csum padding.
+ * Alan Cox : Protect against forwarding explosions with
+ * older network drivers and IFF_ALLMULTI.
+ * Christer Weinigel : Better rebuild header message.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/ip.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <net/dst.h>
+#include <net/arp.h>
+#include <net/sock.h>
+#include <net/ipv6.h>
+#include <net/ip.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/checksum.h>
+
+
+__initfunc(void eth_setup(char *str, int *ints))
+{
+ struct device *d = dev_base;
+
+ if (!str || !*str)
+ return;
+ while (d)
+ {
+ if (!strcmp(str,d->name))
+ {
+ if (ints[0] > 0)
+ d->irq=ints[1];
+ if (ints[0] > 1)
+ d->base_addr=ints[2];
+ if (ints[0] > 2)
+ d->mem_start=ints[3];
+ if (ints[0] > 3)
+ d->mem_end=ints[4];
+ break;
+ }
+ d=d->next;
+ }
+}
+
+
+/*
+ * Create the Ethernet MAC header for an arbitrary protocol layer
+ *
+ * saddr=NULL means use device source address
+ * daddr=NULL means leave destination address (eg unresolved arp)
+ */
+
+int eth_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+ void *daddr, void *saddr, unsigned len)
+{
+ struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
+
+ /*
+ * Set the protocol type. For a packet of type ETH_P_802_3 we put the length
+ * in here instead. It is up to the 802.2 layer to carry protocol information.
+ */
+
+ if(type!=ETH_P_802_3)
+ eth->h_proto = htons(type);
+ else
+ eth->h_proto = htons(len);
+
+ /*
+ * Set the source hardware address.
+ */
+
+ if(saddr)
+ memcpy(eth->h_source,saddr,dev->addr_len);
+ else
+ memcpy(eth->h_source,dev->dev_addr,dev->addr_len);
+
+ /*
+ * Anyway, the loopback-device should never use this function...
+ */
+
+ if (dev->flags & (IFF_LOOPBACK|IFF_NOARP))
+ {
+ memset(eth->h_dest, 0, dev->addr_len);
+ return(dev->hard_header_len);
+ }
+
+ if(daddr)
+ {
+ memcpy(eth->h_dest,daddr,dev->addr_len);
+ return dev->hard_header_len;
+ }
+
+ return -dev->hard_header_len;
+}
+
+
+/*
+ * Rebuild the Ethernet MAC header. This is called after an ARP
+ * (or in future other address resolution) has completed on this
+ * sk_buff. We now let ARP fill in the other fields.
+ *
+ * This routine CANNOT use cached dst->neigh!
+ * Really, it is used only when dst->neigh is wrong.
+ */
+
+int eth_rebuild_header(struct sk_buff *skb)
+{
+ struct ethhdr *eth = (struct ethhdr *)skb->data;
+ struct device *dev = skb->dev;
+
+ switch (eth->h_proto)
+ {
+#ifdef CONFIG_INET
+ case __constant_htons(ETH_P_IP):
+ return arp_find(eth->h_dest, skb);
+#endif
+ default:
+ printk(KERN_DEBUG
+ "%s: unable to resolve type %X addresses.\n",
+ dev->name, (int)eth->h_proto);
+
+ memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Determine the packet's protocol ID. The rule here is that we
+ * assume 802.3 if the type field is short enough to be a length.
+ * This is normal practice and works for any 'now in use' protocol.
+ */
+
+unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev)
+{
+ struct ethhdr *eth;
+ unsigned char *rawp;
+
+ skb->mac.raw=skb->data;
+ skb_pull(skb,dev->hard_header_len);
+ eth= skb->mac.ethernet;
+
+ if(*eth->h_dest&1)
+ {
+ if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+ skb->pkt_type=PACKET_BROADCAST;
+ else
+ skb->pkt_type=PACKET_MULTICAST;
+ }
+
+ /*
+ * This ALLMULTI check should be redundant by 1.4
+ * so don't forget to remove it.
+ *
+ * Seems, you forgot to remove it. All silly devices
+ * seems to set IFF_PROMISC.
+ */
+
+ else if(1 /*dev->flags&IFF_PROMISC*/)
+ {
+ if(memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
+ skb->pkt_type=PACKET_OTHERHOST;
+ }
+
+ if (ntohs(eth->h_proto) >= 1536)
+ return eth->h_proto;
+
+ rawp = skb->data;
+
+ /*
+ * This is a magic hack to spot IPX packets. Older Novell breaks
+ * the protocol design and runs IPX over 802.3 without an 802.2 LLC
+ * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+ * won't work for fault tolerant netware but does for the rest.
+ */
+ if (*(unsigned short *)rawp == 0xFFFF)
+ return htons(ETH_P_802_3);
+
+ /*
+ * Real 802.2 LLC
+ */
+ return htons(ETH_P_802_2);
+}
+
+int eth_header_parse(struct sk_buff *skb, unsigned char *haddr)
+{
+ struct ethhdr *eth = skb->mac.ethernet;
+ memcpy(haddr, eth->h_source, ETH_ALEN);
+ return ETH_ALEN;
+}
+
+int eth_header_cache(struct neighbour *neigh, struct hh_cache *hh)
+{
+ unsigned short type = hh->hh_type;
+ struct ethhdr *eth = (struct ethhdr*)(((u8*)hh->hh_data) + 2);
+ struct device *dev = neigh->dev;
+
+ if (type == __constant_htons(ETH_P_802_3))
+ return -1;
+
+ eth->h_proto = type;
+ memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+ memcpy(eth->h_dest, neigh->ha, dev->addr_len);
+ return 0;
+}
+
+/*
+ * Called by Address Resolution module to notify changes in address.
+ */
+
+void eth_header_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr)
+{
+ memcpy(((u8*)hh->hh_data) + 2, haddr, dev->addr_len);
+}
+
+#ifndef CONFIG_IP_ROUTER
+
+/*
+ * Copy from an ethernet device memory space to an sk_buff while checksumming if IP
+ */
+
+void eth_copy_and_sum(struct sk_buff *dest, unsigned char *src, int length, int base)
+{
+ struct ethhdr *eth;
+ struct iphdr *iph;
+ int ip_length;
+
+ eth=(struct ethhdr *)src;
+ if(eth->h_proto!=htons(ETH_P_IP))
+ {
+ memcpy(dest->data,src,length);
+ return;
+ }
+ /*
+ * We have to watch for padded packets. The csum doesn't include the
+ * padding, and there is no point in copying the padding anyway.
+ * We have to use the smaller of length and ip_length because it
+ * can happen that ip_length > length.
+ */
+ memcpy(dest->data,src,sizeof(struct iphdr)+ETH_HLEN); /* ethernet is always >= 34 */
+ length -= sizeof(struct iphdr) + ETH_HLEN;
+ iph=(struct iphdr*)(src+ETH_HLEN);
+ ip_length = ntohs(iph->tot_len) - sizeof(struct iphdr);
+
+ /* Also watch out for bogons - min IP size is 8 (rfc-1042) */
+ if ((ip_length <= length) && (ip_length > 7))
+ length=ip_length;
+
+ dest->csum=csum_partial_copy(src+sizeof(struct iphdr)+ETH_HLEN,dest->data+sizeof(struct iphdr)+ETH_HLEN,length,base);
+ dest->ip_summed=1;
+}
+
+#endif /* !(CONFIG_IP_ROUTER) */
diff --git a/pfinet/linux-src/net/ethernet/pe2.c b/pfinet/linux-src/net/ethernet/pe2.c
new file mode 100644
index 00000000..4915f070
--- /dev/null
+++ b/pfinet/linux-src/net/ethernet/pe2.c
@@ -0,0 +1,38 @@
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/datalink.h>
+#include <linux/mm.h>
+#include <linux/in.h>
+
+static void
+pEII_datalink_header(struct datalink_proto *dl,
+ struct sk_buff *skb, unsigned char *dest_node)
+{
+ struct device *dev = skb->dev;
+
+ skb->protocol = htons (ETH_P_IPX);
+ if(dev->hard_header)
+ dev->hard_header(skb, dev, ETH_P_IPX, dest_node, NULL, skb->len);
+}
+
+struct datalink_proto *
+make_EII_client(void)
+{
+ struct datalink_proto *proto;
+
+ proto = (struct datalink_proto *) kmalloc(sizeof(*proto), GFP_ATOMIC);
+ if (proto != NULL) {
+ proto->type_len = 0;
+ proto->header_length = 0;
+ proto->datalink_header = pEII_datalink_header;
+ proto->string_name = "EtherII";
+ }
+
+ return proto;
+}
+
+void destroy_EII_client(struct datalink_proto *dl)
+{
+ if (dl)
+ kfree_s(dl, sizeof(struct datalink_proto));
+}
diff --git a/pfinet/linux-src/net/ethernet/sysctl_net_ether.c b/pfinet/linux-src/net/ethernet/sysctl_net_ether.c
new file mode 100644
index 00000000..b81a6d53
--- /dev/null
+++ b/pfinet/linux-src/net/ethernet/sysctl_net_ether.c
@@ -0,0 +1,13 @@
+/* -*- linux-c -*-
+ * sysctl_net_ether.c: sysctl interface to net Ethernet subsystem.
+ *
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net/ether directory entry (empty =) ). [MS]
+ */
+
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+
+ctl_table ether_table[] = {
+ {0}
+};
diff --git a/pfinet/linux-src/net/ipv4/Config.in b/pfinet/linux-src/net/ipv4/Config.in
new file mode 100644
index 00000000..d1c7a5f5
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/Config.in
@@ -0,0 +1,84 @@
+#
+# IP configuration
+#
+bool 'IP: multicasting' CONFIG_IP_MULTICAST
+bool 'IP: advanced router' CONFIG_IP_ADVANCED_ROUTER
+if [ "$CONFIG_IP_ADVANCED_ROUTER" = "y" ]; then
+ define_bool CONFIG_RTNETLINK y
+ define_bool CONFIG_NETLINK y
+ bool 'IP: policy routing' CONFIG_IP_MULTIPLE_TABLES
+ bool 'IP: equal cost multipath' CONFIG_IP_ROUTE_MULTIPATH
+ bool 'IP: use TOS value as routing key' CONFIG_IP_ROUTE_TOS
+ bool 'IP: verbose route monitoring' CONFIG_IP_ROUTE_VERBOSE
+ bool 'IP: large routing tables' CONFIG_IP_ROUTE_LARGE_TABLES
+ if [ "$CONFIG_IP_MULTIPLE_TABLES" = "y" ]; then
+ bool 'IP: fast network address translation' CONFIG_IP_ROUTE_NAT
+ fi
+fi
+bool 'IP: kernel level autoconfiguration' CONFIG_IP_PNP
+if [ "$CONFIG_IP_PNP" = "y" ]; then
+ bool ' BOOTP support' CONFIG_IP_PNP_BOOTP
+ bool ' RARP support' CONFIG_IP_PNP_RARP
+# not yet ready..
+# bool ' ARP support' CONFIG_IP_PNP_ARP
+fi
+if [ "$CONFIG_FIREWALL" = "y" ]; then
+ bool 'IP: firewalling' CONFIG_IP_FIREWALL
+ if [ "$CONFIG_IP_FIREWALL" = "y" ]; then
+ if [ "$CONFIG_NETLINK" = "y" ]; then
+ bool 'IP: firewall packet netlink device' CONFIG_IP_FIREWALL_NETLINK
+ if [ "$CONFIG_IP_FIREWALL_NETLINK" = "y" ]; then
+ define_bool CONFIG_NETLINK_DEV y
+ fi
+ fi
+ if [ "$CONFIG_IP_MULTIPLE_TABLES" = "y" ]; then
+ bool 'IP: use FWMARK value as routing key' CONFIG_IP_ROUTE_FWMARK
+ fi
+ fi
+fi
+if [ "$CONFIG_IP_FIREWALL" = "y" ]; then
+ bool 'IP: transparent proxy support' CONFIG_IP_TRANSPARENT_PROXY
+ bool 'IP: masquerading' CONFIG_IP_MASQUERADE
+ if [ "$CONFIG_IP_MASQUERADE" != "n" ]; then
+ comment 'Protocol-specific masquerading support will be built as modules.'
+ bool 'IP: ICMP masquerading' CONFIG_IP_MASQUERADE_ICMP
+ comment 'Protocol-specific masquerading support will be built as modules.'
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool 'IP: masquerading special modules support' CONFIG_IP_MASQUERADE_MOD
+ if [ "$CONFIG_IP_MASQUERADE_MOD" = "y" ]; then
+ tristate 'IP: ipautofw masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPAUTOFW
+ tristate 'IP: ipportfw masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPPORTFW
+ tristate 'IP: ip fwmark masq-forwarding support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_MFW
+ fi
+ fi
+ fi
+fi
+bool 'IP: optimize as router not host' CONFIG_IP_ROUTER
+tristate 'IP: tunneling' CONFIG_NET_IPIP
+tristate 'IP: GRE tunnels over IP' CONFIG_NET_IPGRE
+if [ "$CONFIG_IP_MULTICAST" = "y" ]; then
+ if [ "$CONFIG_NET_IPGRE" != "n" ]; then
+ bool 'IP: broadcast GRE over IP' CONFIG_NET_IPGRE_BROADCAST
+ fi
+ bool 'IP: multicast routing' CONFIG_IP_MROUTE
+ if [ "$CONFIG_IP_MROUTE" = "y" ]; then
+ bool 'IP: PIM-SM version 1 support' CONFIG_IP_PIMSM_V1
+ bool 'IP: PIM-SM version 2 support' CONFIG_IP_PIMSM_V2
+ fi
+fi
+bool 'IP: aliasing support' CONFIG_IP_ALIAS
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ if [ "$CONFIG_RTNETLINK" = "y" ]; then
+ bool 'IP: ARP daemon support (EXPERIMENTAL)' CONFIG_ARPD
+ fi
+fi
+bool 'IP: TCP syncookie support (not enabled per default)' CONFIG_SYN_COOKIES
+comment '(it is safe to leave these untouched)'
+#bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP
+tristate 'IP: Reverse ARP' CONFIG_INET_RARP
+#bool 'IP: Path MTU Discovery (normally enabled)' CONFIG_PATH_MTU_DISCOVERY
+#bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF
+bool 'IP: Allow large windows (not recommended if <16Mb of memory)' CONFIG_SKB_LARGE
+#if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+#bool 'IP: support experimental checksum copy to user for UDP' CONFIG_UDP_DELAY_CSUM
+#fi
diff --git a/pfinet/linux-src/net/ipv4/Makefile b/pfinet/linux-src/net/ipv4/Makefile
new file mode 100644
index 00000000..8ab280de
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/Makefile
@@ -0,0 +1,116 @@
+#
+# Makefile for the Linux TCP/IP (INET) layer.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := ipv4.o
+IPV4_OBJS := utils.o route.o proc.o timer.o protocol.o \
+ ip_input.o ip_fragment.o ip_forward.o ip_options.o \
+ ip_output.o ip_sockglue.o \
+ tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o\
+ raw.o udp.o arp.o icmp.o devinet.o af_inet.o igmp.o \
+ sysctl_net_ipv4.o fib_frontend.o fib_semantics.o fib_hash.o
+IPV4X_OBJS :=
+
+MOD_LIST_NAME := IPV4_MODULES
+M_OBJS :=
+
+ifeq ($(CONFIG_IP_FIREWALL),y)
+IPV4_OBJS += ip_fw.o
+endif
+
+ifeq ($(CONFIG_IP_MULTIPLE_TABLES),y)
+IPV4_OBJS += fib_rules.o
+endif
+
+ifeq ($(CONFIG_IP_ROUTE_NAT),y)
+IPV4_OBJS += ip_nat_dumb.o
+endif
+
+ifeq ($(CONFIG_IP_MROUTE),y)
+IPV4_OBJS += ipmr.o
+endif
+
+ifeq ($(CONFIG_INET_RARP),y)
+IPV4_OBJS += rarp.o
+else
+ ifeq ($(CONFIG_INET_RARP),m)
+ M_OBJS += rarp.o
+ endif
+endif
+
+ifeq ($(CONFIG_NET_IPIP),y)
+IPV4X_OBJS += ipip.o
+else
+ ifeq ($(CONFIG_NET_IPIP),m)
+ MX_OBJS += ipip.o
+ endif
+endif
+
+ifeq ($(CONFIG_NET_IPGRE),y)
+IPV4X_OBJS += ip_gre.o
+else
+ ifeq ($(CONFIG_NET_IPGRE),m)
+ MX_OBJS += ip_gre.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_MASQUERADE),y)
+IPV4X_OBJS += ip_masq.o ip_masq_app.o
+
+ifeq ($(CONFIG_IP_MASQUERADE_MOD),y)
+ IPV4X_OBJS += ip_masq_mod.o
+
+ ifeq ($(CONFIG_IP_MASQUERADE_IPAUTOFW),y)
+ IPV4_OBJS += ip_masq_autofw.o
+ else
+ ifeq ($(CONFIG_IP_MASQUERADE_IPAUTOFW),m)
+ M_OBJS += ip_masq_autofw.o
+ endif
+ endif
+
+ ifeq ($(CONFIG_IP_MASQUERADE_IPPORTFW),y)
+ IPV4_OBJS += ip_masq_portfw.o
+ else
+ ifeq ($(CONFIG_IP_MASQUERADE_IPPORTFW),m)
+ M_OBJS += ip_masq_portfw.o
+ endif
+ endif
+
+ ifeq ($(CONFIG_IP_MASQUERADE_MFW),y)
+ IPV4_OBJS += ip_masq_mfw.o
+ else
+ ifeq ($(CONFIG_IP_MASQUERADE_MFW),m)
+ M_OBJS += ip_masq_mfw.o
+ endif
+ endif
+
+endif
+
+M_OBJS += ip_masq_user.o
+M_OBJS += ip_masq_ftp.o ip_masq_irc.o ip_masq_raudio.o ip_masq_quake.o
+M_OBJS += ip_masq_vdolive.o ip_masq_cuseeme.o
+endif
+
+ifeq ($(CONFIG_SYN_COOKIES),y)
+IPV4_OBJS += syncookies.o
+# module not supported, because it would be too messy.
+endif
+
+ifeq ($(CONFIG_IP_PNP),y)
+IPV4_OBJS += ipconfig.o
+endif
+
+ifdef CONFIG_INET
+O_OBJS := $(IPV4_OBJS)
+OX_OBJS := $(IPV4X_OBJS)
+endif
+
+include $(TOPDIR)/Rules.make
+
+tar:
+ tar -cvf /dev/f1 .
diff --git a/pfinet/linux-src/net/ipv4/af_inet.c b/pfinet/linux-src/net/ipv4/af_inet.c
new file mode 100644
index 00000000..04e05107
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/af_inet.c
@@ -0,0 +1,1167 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * PF_INET protocol family socket handler.
+ *
+ * Version: $Id: af_inet.c,v 1.87.2.5 1999/08/08 08:43:10 davem Exp $
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Florian La Roche, <flla@stud.uni-sb.de>
+ * Alan Cox, <A.Cox@swansea.ac.uk>
+ *
+ * Changes (see also sock.c)
+ *
+ * A.N.Kuznetsov : Socket death error in accept().
+ * John Richardson : Fix non blocking error in connect()
+ * so sockets that fail to connect
+ * don't return -EINPROGRESS.
+ * Alan Cox : Asynchronous I/O support
+ * Alan Cox : Keep correct socket pointer on sock structures
+ * when accept() ed
+ * Alan Cox : Semantics of SO_LINGER aren't state moved
+ * to close when you look carefully. With
+ * this fixed and the accept bug fixed
+ * some RPC stuff seems happier.
+ * Niibe Yutaka : 4.4BSD style write async I/O
+ * Alan Cox,
+ * Tony Gale : Fixed reuse semantics.
+ * Alan Cox : bind() shouldn't abort existing but dead
+ * sockets. Stops FTP netin:.. I hope.
+ * Alan Cox : bind() works correctly for RAW sockets. Note
+ * that FreeBSD at least was broken in this respect
+ * so be careful with compatibility tests...
+ * Alan Cox : routing cache support
+ * Alan Cox : memzero the socket structure for compactness.
+ * Matt Day : nonblock connect error handler
+ * Alan Cox : Allow large numbers of pending sockets
+ * (eg for big web sites), but only if
+ * specifically application requested.
+ * Alan Cox : New buffering throughout IP. Used dumbly.
+ * Alan Cox : New buffering now used smartly.
+ * Alan Cox : BSD rather than common sense interpretation of
+ * listen.
+ * Germano Caronni : Assorted small races.
+ * Alan Cox : sendmsg/recvmsg basic support.
+ * Alan Cox : Only sendmsg/recvmsg now supported.
+ * Alan Cox : Locked down bind (see security list).
+ * Alan Cox : Loosened bind a little.
+ * Mike McLagan : ADD/DEL DLCI Ioctls
+ * Willy Konynenberg : Transparent proxying support.
+ * David S. Miller : New socket lookup architecture.
+ * Some other random speedups.
+ * Cyrus Durgin : Cleaned up file for kmod hacks.
+ * Andi Kleen : Fix inet_stream_connect TCP race.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/arp.h>
+#include <net/rarp.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/raw.h>
+#include <net/icmp.h>
+#include <net/ipip.h>
+#include <net/inet_common.h>
+#include <linux/ip_fw.h>
+#ifdef CONFIG_IP_MROUTE
+#include <linux/mroute.h>
+#endif
+#ifdef CONFIG_IP_MASQUERADE
+#include <net/ip_masq.h>
+#endif
+#ifdef CONFIG_BRIDGE
+#include <net/br.h>
+#endif
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h>
+#endif /* CONFIG_NET_RADIO */
+
+#define min(a,b) ((a)<(b)?(a):(b))
+
+struct linux_mib net_statistics;
+
+extern int raw_get_info(char *, char **, off_t, int, int);
+extern int snmp_get_info(char *, char **, off_t, int, int);
+extern int netstat_get_info(char *, char **, off_t, int, int);
+extern int afinet_get_info(char *, char **, off_t, int, int);
+extern int tcp_get_info(char *, char **, off_t, int, int);
+extern int udp_get_info(char *, char **, off_t, int, int);
+extern void ip_mc_drop_socket(struct sock *sk);
+
+#ifdef CONFIG_DLCI
+extern int dlci_ioctl(unsigned int, void*);
+#endif
+
+#ifdef CONFIG_DLCI_MODULE
+int (*dlci_ioctl_hook)(unsigned int, void *) = NULL;
+#endif
+
+int (*rarp_ioctl_hook)(unsigned int,void*) = NULL;
+
+/*
+ * Destroy an AF_INET socket
+ */
+
+static __inline__ void kill_sk_queues(struct sock *sk)
+{
+ struct sk_buff *skb;
+
+ /* First the read buffer. */
+ while((skb = skb_dequeue(&sk->receive_queue)) != NULL)
+ kfree_skb(skb);
+
+ /* Next, the error queue. */
+ while((skb = skb_dequeue(&sk->error_queue)) != NULL)
+ kfree_skb(skb);
+
+ /* Now the backlog. */
+ while((skb=skb_dequeue(&sk->back_log)) != NULL)
+ kfree_skb(skb);
+}
+
+static __inline__ void kill_sk_now(struct sock *sk)
+{
+ /* No longer exists. */
+ del_from_prot_sklist(sk);
+
+ /* Remove from protocol hash chains. */
+ sk->prot->unhash(sk);
+
+ if(sk->opt)
+ kfree(sk->opt);
+ dst_release(sk->dst_cache);
+ sk_free(sk);
+}
+
+static __inline__ void kill_sk_later(struct sock *sk)
+{
+ /* this should never happen. */
+ /* actually it can if an ack has just been sent. */
+ /*
+ * It's more normal than that...
+ * It can happen because a skb is still in the device queues
+ * [PR]
+ */
+
+ NETDEBUG(printk(KERN_DEBUG "Socket destroy delayed (r=%d w=%d)\n",
+ atomic_read(&sk->rmem_alloc),
+ atomic_read(&sk->wmem_alloc)));
+
+ sk->ack_backlog = 0;
+ release_sock(sk);
+ net_reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
+}
+
+void destroy_sock(struct sock *sk)
+{
+ lock_sock(sk); /* just to be safe. */
+
+ /* Now we can no longer get new packets or once the
+ * timers are killed, send them.
+ */
+ net_delete_timer(sk);
+
+ if (sk->prot->destroy && !sk->destroy)
+ sk->prot->destroy(sk);
+
+ sk->destroy = 1;
+
+ kill_sk_queues(sk);
+
+ /* Now if everything is gone we can free the socket
+ * structure, otherwise we need to keep it around until
+ * everything is gone.
+ */
+ if (atomic_read(&sk->rmem_alloc) == 0 && atomic_read(&sk->wmem_alloc) == 0)
+ kill_sk_now(sk);
+ else
+ kill_sk_later(sk);
+}
+
+/*
+ * The routines beyond this point handle the behaviour of an AF_INET
+ * socket object. Mostly it punts to the subprotocols of IP to do
+ * the work.
+ */
+
+
+/*
+ * Set socket options on an inet socket.
+ */
+
+int inet_setsockopt(struct socket *sock, int level, int optname,
+ char *optval, int optlen)
+{
+ struct sock *sk=sock->sk;
+ if (sk->prot->setsockopt==NULL)
+ return(-EOPNOTSUPP);
+ return sk->prot->setsockopt(sk,level,optname,optval,optlen);
+}
+
+/*
+ * Get a socket option on an AF_INET socket.
+ *
+ * FIX: POSIX 1003.1g is very ambiguous here. It states that
+ * asynchronous errors should be reported by getsockopt. We assume
+ * this means if you specify SO_ERROR (otherwise whats the point of it).
+ */
+
+int inet_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen)
+{
+ struct sock *sk=sock->sk;
+ if (sk->prot->getsockopt==NULL)
+ return(-EOPNOTSUPP);
+ return sk->prot->getsockopt(sk,level,optname,optval,optlen);
+}
+
+/*
+ * Automatically bind an unbound socket.
+ */
+
+static int inet_autobind(struct sock *sk)
+{
+ /* We may need to bind the socket. */
+ if (sk->num == 0) {
+ if (sk->prot->get_port(sk, 0) != 0)
+ return(-EAGAIN);
+ sk->sport = htons(sk->num);
+ sk->prot->hash(sk);
+ add_to_prot_sklist(sk);
+ }
+ return 0;
+}
+
+/*
+ * Move a socket into listening state.
+ */
+
+int inet_listen(struct socket *sock, int backlog)
+{
+ struct sock *sk = sock->sk;
+ unsigned char old_state;
+
+ if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM)
+ return(-EINVAL);
+
+ if ((unsigned) backlog == 0) /* BSDism */
+ backlog = 1;
+ if ((unsigned) backlog > SOMAXCONN)
+ backlog = SOMAXCONN;
+ sk->max_ack_backlog = backlog;
+
+ /* Really, if the socket is already in listen state
+ * we can only allow the backlog to be adjusted.
+ */
+ old_state = sk->state;
+ if (old_state != TCP_LISTEN) {
+ sk->state = TCP_LISTEN;
+ sk->ack_backlog = 0;
+ if (sk->num == 0) {
+ if (sk->prot->get_port(sk, 0) != 0) {
+ sk->state = old_state;
+ return -EAGAIN;
+ }
+ sk->sport = htons(sk->num);
+ add_to_prot_sklist(sk);
+ } else {
+ if (sk->prev)
+ ((struct tcp_bind_bucket*)sk->prev)->fastreuse = 0;
+ }
+
+ dst_release(xchg(&sk->dst_cache, NULL));
+ sk->prot->hash(sk);
+ sk->socket->flags |= SO_ACCEPTCON;
+ }
+ return 0;
+}
+
+/*
+ * Create an inet socket.
+ *
+ * FIXME: Gcc would generate much better code if we set the parameters
+ * up in in-memory structure order. Gcc68K even more so
+ */
+
+static int inet_create(struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct proto *prot;
+
+ /* Compatibility */
+ if (sock->type == SOCK_PACKET) {
+ static int warned;
+ if (net_families[PF_PACKET]==NULL)
+ {
+#if defined(CONFIG_KMOD) && defined(CONFIG_PACKET_MODULE)
+ char module_name[30];
+ sprintf(module_name,"net-pf-%d", PF_PACKET);
+ request_module(module_name);
+ if (net_families[PF_PACKET] == NULL)
+#endif
+ return -ESOCKTNOSUPPORT;
+ }
+ if (!warned++)
+ printk(KERN_INFO "%s uses obsolete (PF_INET,SOCK_PACKET)\n", current->comm);
+ return net_families[PF_PACKET]->create(sock, protocol);
+ }
+
+ sock->state = SS_UNCONNECTED;
+ sk = sk_alloc(PF_INET, GFP_KERNEL, 1);
+ if (sk == NULL)
+ goto do_oom;
+
+ switch (sock->type) {
+ case SOCK_STREAM:
+ if (protocol && protocol != IPPROTO_TCP)
+ goto free_and_noproto;
+ protocol = IPPROTO_TCP;
+ if (ipv4_config.no_pmtu_disc)
+ sk->ip_pmtudisc = IP_PMTUDISC_DONT;
+ else
+ sk->ip_pmtudisc = IP_PMTUDISC_WANT;
+ prot = &tcp_prot;
+ sock->ops = &inet_stream_ops;
+ break;
+ case SOCK_SEQPACKET:
+ goto free_and_badtype;
+ case SOCK_DGRAM:
+ if (protocol && protocol != IPPROTO_UDP)
+ goto free_and_noproto;
+ protocol = IPPROTO_UDP;
+ sk->no_check = UDP_NO_CHECK;
+ sk->ip_pmtudisc = IP_PMTUDISC_DONT;
+ prot=&udp_prot;
+ sock->ops = &inet_dgram_ops;
+ break;
+ case SOCK_RAW:
+ if (!capable(CAP_NET_RAW))
+ goto free_and_badperm;
+ if (!protocol)
+ goto free_and_noproto;
+ prot = &raw_prot;
+ sk->reuse = 1;
+ sk->ip_pmtudisc = IP_PMTUDISC_DONT;
+ sk->num = protocol;
+ sock->ops = &inet_dgram_ops;
+ if (protocol == IPPROTO_RAW)
+ sk->ip_hdrincl = 1;
+ break;
+ default:
+ goto free_and_badtype;
+ }
+
+ sock_init_data(sock,sk);
+
+ sk->destruct = NULL;
+
+ sk->zapped=0;
+#ifdef CONFIG_TCP_NAGLE_OFF
+ sk->nonagle = 1;
+#endif
+ sk->family = PF_INET;
+ sk->protocol = protocol;
+
+ sk->prot = prot;
+ sk->backlog_rcv = prot->backlog_rcv;
+
+ sk->timer.data = (unsigned long)sk;
+ sk->timer.function = &net_timer;
+
+ sk->ip_ttl=ip_statistics.IpDefaultTTL;
+
+ sk->ip_mc_loop=1;
+ sk->ip_mc_ttl=1;
+ sk->ip_mc_index=0;
+ sk->ip_mc_list=NULL;
+
+ if (sk->num) {
+ /* It assumes that any protocol which allows
+ * the user to assign a number at socket
+ * creation time automatically
+ * shares.
+ */
+ sk->sport = htons(sk->num);
+
+ /* Add to protocol hash chains. */
+ sk->prot->hash(sk);
+ add_to_prot_sklist(sk);
+ }
+
+ if (sk->prot->init) {
+ int err = sk->prot->init(sk);
+ if (err != 0) {
+ destroy_sock(sk);
+ return(err);
+ }
+ }
+ return(0);
+
+free_and_badtype:
+ sk_free(sk);
+ return -ESOCKTNOSUPPORT;
+
+free_and_badperm:
+ sk_free(sk);
+ return -EPERM;
+
+free_and_noproto:
+ sk_free(sk);
+ return -EPROTONOSUPPORT;
+
+do_oom:
+ return -ENOBUFS;
+}
+
+
+/*
+ * The peer socket should always be NULL (or else). When we call this
+ * function we are destroying the object and from then on nobody
+ * should refer to it.
+ */
+
+int inet_release(struct socket *sock, struct socket *peersock)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk) {
+ long timeout;
+
+ /* Begin closedown and wake up sleepers. */
+ if (sock->state != SS_UNCONNECTED)
+ sock->state = SS_DISCONNECTING;
+ sk->state_change(sk);
+
+ /* Applications forget to leave groups before exiting */
+ ip_mc_drop_socket(sk);
+
+ /* If linger is set, we don't return until the close
+ * is complete. Otherwise we return immediately. The
+ * actually closing is done the same either way.
+ *
+ * If the close is due to the process exiting, we never
+ * linger..
+ */
+ timeout = 0;
+ if (sk->linger && !(current->flags & PF_EXITING)) {
+ timeout = HZ * sk->lingertime;
+ if (!timeout)
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ }
+ sock->sk = NULL;
+ sk->socket = NULL;
+ sk->prot->close(sk, timeout);
+ }
+ return(0);
+}
+
+static int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ struct sockaddr_in *addr=(struct sockaddr_in *)uaddr;
+ struct sock *sk=sock->sk;
+ unsigned short snum;
+ int chk_addr_ret;
+
+ /* If the socket has its own bind function then use it. (RAW) */
+ if(sk->prot->bind)
+ return sk->prot->bind(sk, uaddr, addr_len);
+
+ /* Check these errors (active socket, bad address length, double bind). */
+ if ((sk->state != TCP_CLOSE) ||
+ (addr_len < sizeof(struct sockaddr_in)) ||
+ (sk->num != 0))
+ return -EINVAL;
+
+ chk_addr_ret = inet_addr_type(addr->sin_addr.s_addr);
+ if (addr->sin_addr.s_addr != 0 && chk_addr_ret != RTN_LOCAL &&
+ chk_addr_ret != RTN_MULTICAST && chk_addr_ret != RTN_BROADCAST) {
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ /* Superuser may bind to any address to allow transparent proxying. */
+ if(chk_addr_ret != RTN_UNICAST || !capable(CAP_NET_ADMIN))
+#endif
+ return -EADDRNOTAVAIL; /* Source address MUST be ours! */
+ }
+
+ /* We keep a pair of addresses. rcv_saddr is the one
+ * used by hash lookups, and saddr is used for transmit.
+ *
+ * In the BSD API these are the same except where it
+ * would be illegal to use them (multicast/broadcast) in
+ * which case the sending device address is used.
+ */
+ sk->rcv_saddr = sk->saddr = addr->sin_addr.s_addr;
+ if(chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
+ sk->saddr = 0; /* Use device */
+
+ snum = ntohs(addr->sin_port);
+#ifdef CONFIG_IP_MASQUERADE
+ /* The kernel masquerader needs some ports. */
+ if((snum >= PORT_MASQ_BEGIN) && (snum <= PORT_MASQ_END))
+ return -EADDRINUSE;
+#endif
+ if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
+ return(-EACCES);
+
+ /* Make sure we are allowed to bind here. */
+ if (sk->prot->get_port(sk, snum) != 0)
+ return -EADDRINUSE;
+
+ sk->sport = htons(sk->num);
+ sk->daddr = 0;
+ sk->dport = 0;
+ sk->prot->hash(sk);
+ add_to_prot_sklist(sk);
+ dst_release(sk->dst_cache);
+ sk->dst_cache=NULL;
+ return(0);
+}
+
+int inet_dgram_connect(struct socket *sock, struct sockaddr * uaddr,
+ int addr_len, int flags)
+{
+ struct sock *sk=sock->sk;
+ int err;
+
+ if (inet_autobind(sk) != 0)
+ return(-EAGAIN);
+ if (sk->prot->connect == NULL)
+ return(-EOPNOTSUPP);
+ err = sk->prot->connect(sk, (struct sockaddr *)uaddr, addr_len);
+ if (err < 0)
+ return(err);
+ return(0);
+}
+
+static void inet_wait_for_connect(struct sock *sk)
+{
+ struct wait_queue wait = { current, NULL };
+
+ add_wait_queue(sk->sleep, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+ while (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) {
+ if (signal_pending(current))
+ break;
+ if (sk->err)
+ break;
+ schedule();
+ current->state = TASK_INTERRUPTIBLE;
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(sk->sleep, &wait);
+}
+
+/*
+ * Connect to a remote host. There is regrettably still a little
+ * TCP 'magic' in here.
+ */
+
+int inet_stream_connect(struct socket *sock, struct sockaddr * uaddr,
+ int addr_len, int flags)
+{
+ struct sock *sk=sock->sk;
+ int err;
+
+ if(sock->state != SS_UNCONNECTED && sock->state != SS_CONNECTING) {
+ if(sock->state == SS_CONNECTED)
+ return -EISCONN;
+ return -EINVAL;
+ }
+
+ if(sock->state == SS_CONNECTING) {
+ /* Note: tcp_connected contains SYN_RECV, which may cause
+ bogus results here. -AK */
+ if(tcp_connected(sk->state)) {
+ sock->state = SS_CONNECTED;
+ return 0;
+ }
+ if (sk->zapped || sk->err)
+ goto sock_error;
+ if (flags & O_NONBLOCK)
+ return -EALREADY;
+ } else {
+ if (sk->prot->connect == NULL)
+ return(-EOPNOTSUPP);
+
+ /* We may need to bind the socket. */
+ if (inet_autobind(sk) != 0)
+ return(-EAGAIN);
+
+ err = sk->prot->connect(sk, uaddr, addr_len);
+ /* Note: there is a theoretical race here when an wake up
+ occurred before inet_wait_for_connect is entered. In 2.3
+ the wait queue setup should be moved before the low level
+ connect call. -AK*/
+ if (err < 0)
+ return(err);
+ sock->state = SS_CONNECTING;
+ }
+
+ if (sk->state > TCP_FIN_WAIT2 && sock->state == SS_CONNECTING)
+ goto sock_error;
+
+ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
+ return (-EINPROGRESS);
+
+ if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) {
+ inet_wait_for_connect(sk);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ }
+
+ sock->state = SS_CONNECTED;
+ if ((sk->state != TCP_ESTABLISHED) && sk->err)
+ goto sock_error;
+ return(0);
+
+sock_error:
+ /* This is ugly but needed to fix a race in the ICMP error handler */
+ if (sk->zapped && sk->state != TCP_CLOSE) {
+ lock_sock(sk);
+ tcp_set_state(sk, TCP_CLOSE);
+ release_sock(sk);
+ sk->zapped = 0;
+ }
+ sock->state = SS_UNCONNECTED;
+ return sock_error(sk);
+}
+
+/*
+ * Accept a pending connection. The TCP layer now gives BSD semantics.
+ */
+
+int inet_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ struct sock *sk1 = sock->sk, *sk2;
+ struct sock *newsk = newsock->sk;
+ int err = -EINVAL;
+
+ if (sock->state != SS_UNCONNECTED || !(sock->flags & SO_ACCEPTCON))
+ goto do_err;
+
+ err = -EOPNOTSUPP;
+ if (sk1->prot->accept == NULL)
+ goto do_err;
+
+ if((sk2 = sk1->prot->accept(sk1,flags)) == NULL)
+ goto do_sk1_err;
+
+ /*
+ * We've been passed an extra socket.
+ * We need to free it up because the tcp module creates
+ * its own when it accepts one.
+ */
+ sk2->sleep = newsk->sleep;
+
+ newsock->sk = sk2;
+ sk2->socket = newsock;
+ newsk->socket = NULL;
+
+ if (flags & O_NONBLOCK)
+ goto do_half_success;
+
+ if(sk2->state == TCP_ESTABLISHED)
+ goto do_full_success;
+ if(sk2->err > 0)
+ goto do_connect_err;
+ err = -ECONNABORTED;
+ if (sk2->state == TCP_CLOSE)
+ goto do_bad_connection;
+do_full_success:
+ destroy_sock(newsk);
+ newsock->state = SS_CONNECTED;
+ return 0;
+
+do_half_success:
+ destroy_sock(newsk);
+ return(0);
+
+do_connect_err:
+ err = sock_error(sk2);
+do_bad_connection:
+ sk2->sleep = NULL;
+ sk2->socket = NULL;
+ destroy_sock(sk2);
+ newsock->sk = newsk;
+ newsk->socket = newsock;
+ return err;
+
+do_sk1_err:
+ err = sock_error(sk1);
+do_err:
+ return err;
+}
+
+
+/*
+ * This does both peername and sockname.
+ */
+
+static int inet_getname(struct socket *sock, struct sockaddr *uaddr,
+ int *uaddr_len, int peer)
+{
+ struct sock *sk = sock->sk;
+ struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
+
+ sin->sin_family = AF_INET;
+ if (peer) {
+ if (!tcp_connected(sk->state))
+ return(-ENOTCONN);
+ sin->sin_port = sk->dport;
+ sin->sin_addr.s_addr = sk->daddr;
+ } else {
+ __u32 addr = sk->rcv_saddr;
+ if (!addr)
+ addr = sk->saddr;
+ sin->sin_port = sk->sport;
+ sin->sin_addr.s_addr = addr;
+ }
+ *uaddr_len = sizeof(*sin);
+ return(0);
+}
+
+
+
+int inet_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+ int flags, struct scm_cookie *scm)
+{
+ struct sock *sk = sock->sk;
+ int addr_len = 0;
+ int err;
+
+ if (sock->flags & SO_ACCEPTCON)
+ return(-EINVAL);
+ if (sk->prot->recvmsg == NULL)
+ return(-EOPNOTSUPP);
+ /* We may need to bind the socket. */
+ if (inet_autobind(sk) != 0)
+ return(-EAGAIN);
+ err = sk->prot->recvmsg(sk, msg, size, flags&MSG_DONTWAIT,
+ flags&~MSG_DONTWAIT, &addr_len);
+ if (err >= 0)
+ msg->msg_namelen = addr_len;
+ return err;
+}
+
+
+int inet_sendmsg(struct socket *sock, struct msghdr *msg, int size,
+ struct scm_cookie *scm)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk->shutdown & SEND_SHUTDOWN) {
+ if (!(msg->msg_flags&MSG_NOSIGNAL))
+ send_sig(SIGPIPE, current, 1);
+ return(-EPIPE);
+ }
+ if (sk->prot->sendmsg == NULL)
+ return(-EOPNOTSUPP);
+ if(sk->err)
+ return sock_error(sk);
+
+ /* We may need to bind the socket. */
+ if (inet_autobind(sk) != 0)
+ return -EAGAIN;
+
+ return sk->prot->sendmsg(sk, msg, size);
+}
+
+
+int inet_shutdown(struct socket *sock, int how)
+{
+ struct sock *sk = sock->sk;
+
+ /* This should really check to make sure
+ * the socket is a TCP socket. (WHY AC...)
+ */
+ how++; /* maps 0->1 has the advantage of making bit 1 rcvs and
+ 1->2 bit 2 snds.
+ 2->3 */
+ if ((how & ~SHUTDOWN_MASK) || how==0) /* MAXINT->0 */
+ return(-EINVAL);
+ if (!sk)
+ return(-ENOTCONN);
+ if (sock->state == SS_CONNECTING && sk->state == TCP_ESTABLISHED)
+ sock->state = SS_CONNECTED;
+ if (!tcp_connected(sk->state))
+ return(-ENOTCONN);
+ sk->shutdown |= how;
+ if (sk->prot->shutdown)
+ sk->prot->shutdown(sk, how);
+ /* Wake up anyone sleeping in poll. */
+ sk->state_change(sk);
+ return(0);
+}
+
+
+unsigned int inet_poll(struct file * file, struct socket *sock, poll_table *wait)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk->prot->poll == NULL)
+ return(0);
+ return sk->prot->poll(file, sock, wait);
+}
+
+#ifdef _HURD_
+#define inet_ioctl 0
+#else
+
+/*
+ * ioctl() calls you can issue on an INET socket. Most of these are
+ * device configuration and stuff and very rarely used. Some ioctls
+ * pass on to the socket itself.
+ *
+ * NOTE: I like the idea of a module for the config stuff. ie ifconfig
+ * loads the devconfigure module does its configuring and unloads it.
+ * There's a good 20K of config code hanging around the kernel.
+ */
+
+static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk = sock->sk;
+ int err;
+ int pid;
+
+ switch(cmd)
+ {
+ case FIOSETOWN:
+ case SIOCSPGRP:
+ err = get_user(pid, (int *) arg);
+ if (err)
+ return err;
+ if (current->pid != pid && current->pgrp != -pid &&
+ !capable(CAP_NET_ADMIN))
+ return -EPERM;
+ sk->proc = pid;
+ return(0);
+ case FIOGETOWN:
+ case SIOCGPGRP:
+ return put_user(sk->proc, (int *)arg);
+ case SIOCGSTAMP:
+ if(sk->stamp.tv_sec==0)
+ return -ENOENT;
+ err = copy_to_user((void *)arg,&sk->stamp,sizeof(struct timeval));
+ if (err)
+ err = -EFAULT;
+ return err;
+ case SIOCADDRT:
+ case SIOCDELRT:
+ case SIOCRTMSG:
+ return(ip_rt_ioctl(cmd,(void *) arg));
+ case SIOCDARP:
+ case SIOCGARP:
+ case SIOCSARP:
+ return(arp_ioctl(cmd,(void *) arg));
+ case SIOCDRARP:
+ case SIOCGRARP:
+ case SIOCSRARP:
+#ifdef CONFIG_KMOD
+ if (rarp_ioctl_hook == NULL)
+ request_module("rarp");
+#endif
+ if (rarp_ioctl_hook != NULL)
+ return(rarp_ioctl_hook(cmd,(void *) arg));
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ case SIOCGIFBRDADDR:
+ case SIOCSIFBRDADDR:
+ case SIOCGIFNETMASK:
+ case SIOCSIFNETMASK:
+ case SIOCGIFDSTADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCSIFPFLAGS:
+ case SIOCGIFPFLAGS:
+ case SIOCSIFFLAGS:
+ return(devinet_ioctl(cmd,(void *) arg));
+ case SIOCGIFBR:
+ case SIOCSIFBR:
+#ifdef CONFIG_BRIDGE
+ return(br_ioctl(cmd,(void *) arg));
+#else
+ return -ENOPKG;
+#endif
+
+ case SIOCADDDLCI:
+ case SIOCDELDLCI:
+#ifdef CONFIG_DLCI
+ return(dlci_ioctl(cmd, (void *) arg));
+#endif
+
+#ifdef CONFIG_DLCI_MODULE
+
+#ifdef CONFIG_KMOD
+ if (dlci_ioctl_hook == NULL)
+ request_module("dlci");
+#endif
+
+ if (dlci_ioctl_hook)
+ return((*dlci_ioctl_hook)(cmd, (void *) arg));
+#endif
+ return -ENOPKG;
+
+ default:
+ if ((cmd >= SIOCDEVPRIVATE) &&
+ (cmd <= (SIOCDEVPRIVATE + 15)))
+ return(dev_ioctl(cmd,(void *) arg));
+
+#ifdef CONFIG_NET_RADIO
+ if((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST))
+ return(dev_ioctl(cmd,(void *) arg));
+#endif
+
+ if (sk->prot->ioctl==NULL || (err=sk->prot->ioctl(sk, cmd, arg))==-ENOIOCTLCMD)
+ return(dev_ioctl(cmd,(void *) arg));
+ return err;
+ }
+ /*NOTREACHED*/
+ return(0);
+}
+
+#endif
+
+struct proto_ops inet_stream_ops = {
+ PF_INET,
+
+ sock_no_dup,
+ inet_release,
+ inet_bind,
+ inet_stream_connect,
+ sock_no_socketpair,
+ inet_accept,
+ inet_getname,
+ inet_poll,
+ inet_ioctl,
+ inet_listen,
+ inet_shutdown,
+ inet_setsockopt,
+ inet_getsockopt,
+ sock_no_fcntl,
+ inet_sendmsg,
+ inet_recvmsg
+};
+
+struct proto_ops inet_dgram_ops = {
+ PF_INET,
+
+ sock_no_dup,
+ inet_release,
+ inet_bind,
+ inet_dgram_connect,
+ sock_no_socketpair,
+ sock_no_accept,
+ inet_getname,
+ datagram_poll,
+ inet_ioctl,
+ sock_no_listen,
+ inet_shutdown,
+ inet_setsockopt,
+ inet_getsockopt,
+ sock_no_fcntl,
+ inet_sendmsg,
+ inet_recvmsg
+};
+
+struct net_proto_family inet_family_ops = {
+ PF_INET,
+ inet_create
+};
+
+
+#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_INET_RARP
+static struct proc_dir_entry proc_net_rarp = {
+ PROC_NET_RARP, 4, "rarp",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rarp_get_info
+};
+#endif /* RARP */
+static struct proc_dir_entry proc_net_raw = {
+ PROC_NET_RAW, 3, "raw",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ raw_get_info
+};
+static struct proc_dir_entry proc_net_netstat = {
+ PROC_NET_NETSTAT, 7, "netstat",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ netstat_get_info
+};
+static struct proc_dir_entry proc_net_snmp = {
+ PROC_NET_SNMP, 4, "snmp",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ snmp_get_info
+};
+static struct proc_dir_entry proc_net_sockstat = {
+ PROC_NET_SOCKSTAT, 8, "sockstat",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ afinet_get_info
+};
+static struct proc_dir_entry proc_net_tcp = {
+ PROC_NET_TCP, 3, "tcp",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ tcp_get_info
+};
+static struct proc_dir_entry proc_net_udp = {
+ PROC_NET_UDP, 3, "udp",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ udp_get_info
+};
+#endif /* CONFIG_PROC_FS */
+
+extern void tcp_init(void);
+extern void tcp_v4_init(struct net_proto_family *);
+
+
+/*
+ * Called by socket.c on kernel startup.
+ */
+
+__initfunc(void inet_proto_init(struct net_proto *pro))
+{
+ struct sk_buff *dummy_skb;
+ struct inet_protocol *p;
+
+ printk(KERN_INFO "NET4: Linux TCP/IP 1.0 for NET4.0\n");
+
+ if (sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb))
+ {
+ printk(KERN_CRIT "inet_proto_init: panic\n");
+ return;
+ }
+
+ /*
+ * Tell SOCKET that we are alive...
+ */
+
+ (void) sock_register(&inet_family_ops);
+
+ /*
+ * Add all the protocols.
+ */
+
+ printk(KERN_INFO "IP Protocols: ");
+ for(p = inet_protocol_base; p != NULL;)
+ {
+ struct inet_protocol *tmp = (struct inet_protocol *) p->next;
+ inet_add_protocol(p);
+ printk("%s%s",p->name,tmp?", ":"\n");
+ p = tmp;
+ }
+
+ /*
+ * Set the ARP module up
+ */
+
+ arp_init();
+
+ /*
+ * Set the IP module up
+ */
+
+ ip_init();
+
+ tcp_v4_init(&inet_family_ops);
+
+ /* Setup TCP slab cache for open requests. */
+ tcp_init();
+
+
+ /*
+ * Set the ICMP layer up
+ */
+
+ icmp_init(&inet_family_ops);
+
+ /* I wish inet_add_protocol had no constructor hook...
+ I had to move IPIP from net/ipv4/protocol.c :-( --ANK
+ */
+#ifdef CONFIG_NET_IPIP
+ ipip_init();
+#endif
+#ifdef CONFIG_NET_IPGRE
+ ipgre_init();
+#endif
+
+ /*
+ * Set the firewalling up
+ */
+#if defined(CONFIG_IP_FIREWALL)
+ ip_fw_init();
+#endif
+
+#ifdef CONFIG_IP_MASQUERADE
+ ip_masq_init();
+#endif
+
+ /*
+ * Initialise the multicast router
+ */
+#if defined(CONFIG_IP_MROUTE)
+ ip_mr_init();
+#endif
+
+#ifdef CONFIG_INET_RARP
+ rarp_ioctl_hook = rarp_ioctl;
+#endif
+ /*
+ * Create all the /proc entries.
+ */
+
+#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_INET_RARP
+ proc_net_register(&proc_net_rarp);
+#endif /* RARP */
+ proc_net_register(&proc_net_raw);
+ proc_net_register(&proc_net_snmp);
+ proc_net_register(&proc_net_netstat);
+ proc_net_register(&proc_net_sockstat);
+ proc_net_register(&proc_net_tcp);
+ proc_net_register(&proc_net_udp);
+#endif /* CONFIG_PROC_FS */
+}
diff --git a/pfinet/linux-src/net/ipv4/arp.c b/pfinet/linux-src/net/ipv4/arp.c
new file mode 100644
index 00000000..53d155a9
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/arp.c
@@ -0,0 +1,1191 @@
+/* linux/net/inet/arp.c
+ *
+ * Version: $Id: arp.c,v 1.77.2.4 1999/09/23 19:03:36 davem Exp $
+ *
+ * Copyright (C) 1994 by Florian La Roche
+ *
+ * This module implements the Address Resolution Protocol ARP (RFC 826),
+ * which is used to convert IP addresses (or in the future maybe other
+ * high-level addresses) into a low-level hardware address (like an Ethernet
+ * address).
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Fixes:
+ * Alan Cox : Removed the Ethernet assumptions in
+ * Florian's code
+ * Alan Cox : Fixed some small errors in the ARP
+ * logic
+ * Alan Cox : Allow >4K in /proc
+ * Alan Cox : Make ARP add its own protocol entry
+ * Ross Martin : Rewrote arp_rcv() and arp_get_info()
+ * Stephen Henson : Add AX25 support to arp_get_info()
+ * Alan Cox : Drop data when a device is downed.
+ * Alan Cox : Use init_timer().
+ * Alan Cox : Double lock fixes.
+ * Martin Seine : Move the arphdr structure
+ * to if_arp.h for compatibility.
+ * with BSD based programs.
+ * Andrew Tridgell : Added ARP netmask code and
+ * re-arranged proxy handling.
+ * Alan Cox : Changed to use notifiers.
+ * Niibe Yutaka : Reply for this device or proxies only.
+ * Alan Cox : Don't proxy across hardware types!
+ * Jonathan Naylor : Added support for NET/ROM.
+ * Mike Shaver : RFC1122 checks.
+ * Jonathan Naylor : Only lookup the hardware address for
+ * the correct hardware type.
+ * Germano Caronni : Assorted subtle races.
+ * Craig Schlenter : Don't modify permanent entry
+ * during arp_rcv.
+ * Russ Nelson : Tidied up a few bits.
+ * Alexey Kuznetsov: Major changes to caching and behaviour,
+ * eg intelligent arp probing and
+ * generation
+ * of host down events.
+ * Alan Cox : Missing unlock in device events.
+ * Eckes : ARP ioctl control errors.
+ * Alexey Kuznetsov: Arp free fix.
+ * Manuel Rodriguez: Gratuitous ARP.
+ * Jonathan Layes : Added arpd support through kerneld
+ * message queue (960314)
+ * Mike Shaver : /proc/sys/net/ipv4/arp_* support
+ * Mike McLagan : Routing by source
+ * Stuart Cheshire : Metricom and grat arp fixes
+ * *** FOR 2.1 clean this up ***
+ * Lawrence V. Stefani: (08/12/96) Added FDDI support.
+ * Alan Cox : Took the AP1000 nasty FDDI hack and
+ * folded into the mainstream FDDI code.
+ * Ack spit, Linus how did you allow that
+ * one in...
+ * Jes Sorensen : Make FDDI work again in 2.1.x and
+ * clean up the APFDDI & gen. FDDI bits.
+ * Alexey Kuznetsov: new arp state machine;
+ * now it is in net/core/neighbour.c.
+ * Julian Anastasov: "hidden" flag: hide the
+ * interface and don't reply for it
+ */
+
+/* RFC1122 Status:
+ 2.3.2.1 (ARP Cache Validation):
+ MUST provide mechanism to flush stale cache entries (OK)
+ SHOULD be able to configure cache timeout (OK)
+ MUST throttle ARP retransmits (OK)
+ 2.3.2.2 (ARP Packet Queue):
+ SHOULD save at least one packet from each "conversation" with an
+ unresolved IP address. (OK)
+ 950727 -- MS
+*/
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/config.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/mm.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/fddidevice.h>
+#include <linux/if_arp.h>
+#include <linux/trdevice.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
+
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <net/route.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+#include <net/sock.h>
+#include <net/arp.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <net/ax25.h>
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+#include <net/netrom.h>
+#endif
+#endif
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+/*
+ * Interface to generic neighbour cache.
+ */
+static int arp_constructor(struct neighbour *neigh);
+static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb);
+static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb);
+static void parp_redo(struct sk_buff *skb);
+
+static struct neigh_ops arp_generic_ops =
+{
+ AF_INET,
+ NULL,
+ arp_solicit,
+ arp_error_report,
+ neigh_resolve_output,
+ neigh_connected_output,
+ dev_queue_xmit,
+ dev_queue_xmit
+};
+
+static struct neigh_ops arp_hh_ops =
+{
+ AF_INET,
+ NULL,
+ arp_solicit,
+ arp_error_report,
+ neigh_resolve_output,
+ neigh_resolve_output,
+ dev_queue_xmit,
+ dev_queue_xmit
+};
+
+static struct neigh_ops arp_direct_ops =
+{
+ AF_INET,
+ NULL,
+ NULL,
+ NULL,
+ dev_queue_xmit,
+ dev_queue_xmit,
+ dev_queue_xmit,
+ dev_queue_xmit
+};
+
+struct neigh_ops arp_broken_ops =
+{
+ AF_INET,
+ NULL,
+ arp_solicit,
+ arp_error_report,
+ neigh_compat_output,
+ neigh_compat_output,
+ dev_queue_xmit,
+ dev_queue_xmit,
+};
+
+struct neigh_table arp_tbl =
+{
+ NULL,
+ AF_INET,
+ sizeof(struct neighbour) + 4,
+ 4,
+ arp_constructor,
+ NULL,
+ NULL,
+ parp_redo,
+ { NULL, NULL, &arp_tbl, 0, NULL, NULL,
+ 30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 64, 1*HZ },
+ 30*HZ, 128, 512, 1024,
+};
+
+int arp_mc_map(u32 addr, u8 *haddr, struct device *dev, int dir)
+{
+ switch (dev->type) {
+ case ARPHRD_ETHER:
+ case ARPHRD_FDDI:
+ ip_eth_mc_map(addr, haddr) ;
+ return 0 ;
+ case ARPHRD_IEEE802:
+ if ( (dev->name[0] == 't') && (dev->name[1] == 'r')) /* Token Ring */
+ ip_tr_mc_map(addr,haddr) ;
+ else
+ ip_eth_mc_map(addr, haddr);
+ return 0;
+ default:
+ if (dir) {
+ memcpy(haddr, dev->broadcast, dev->addr_len);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+
+
+static int arp_constructor(struct neighbour *neigh)
+{
+ u32 addr = *(u32*)neigh->primary_key;
+ struct device *dev = neigh->dev;
+ struct in_device *in_dev = dev->ip_ptr;
+
+ if (in_dev == NULL)
+ return -EINVAL;
+
+ neigh->type = inet_addr_type(addr);
+ if (in_dev->arp_parms)
+ neigh->parms = in_dev->arp_parms;
+
+ if (dev->hard_header == NULL) {
+ neigh->nud_state = NUD_NOARP;
+ neigh->ops = &arp_direct_ops;
+ neigh->output = neigh->ops->queue_xmit;
+ } else {
+ /* Good devices (checked by reading texts, but only Ethernet is
+ tested)
+
+ ARPHRD_ETHER: (ethernet, apfddi)
+ ARPHRD_FDDI: (fddi)
+ ARPHRD_IEEE802: (tr)
+ ARPHRD_METRICOM: (strip)
+ ARPHRD_ARCNET:
+ etc. etc. etc.
+
+ ARPHRD_IPDDP will also work, if author repairs it.
+ I did not it, because this driver does not work even
+ in old paradigm.
+ */
+
+#if 1
+ /* So... these "amateur" devices are hopeless.
+ The only thing, that I can say now:
+ It is very sad that we need to keep ugly obsolete
+ code to make them happy.
+
+ They should be moved to more reasonable state, now
+ they use rebuild_header INSTEAD OF hard_start_xmit!!!
+ Besides that, they are sort of out of date
+ (a lot of redundant clones/copies, useless in 2.1),
+ I wonder why people believe that they work.
+ */
+ switch (dev->type) {
+ default:
+ break;
+ case ARPHRD_ROSE:
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ case ARPHRD_AX25:
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+ case ARPHRD_NETROM:
+#endif
+ neigh->ops = &arp_broken_ops;
+ neigh->output = neigh->ops->output;
+ return 0;
+#endif
+ break;
+ }
+#endif
+ if (neigh->type == RTN_MULTICAST) {
+ neigh->nud_state = NUD_NOARP;
+ arp_mc_map(addr, neigh->ha, dev, 1);
+ } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
+ neigh->nud_state = NUD_NOARP;
+ memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
+ } else if (neigh->type == RTN_BROADCAST || dev->flags&IFF_POINTOPOINT) {
+ neigh->nud_state = NUD_NOARP;
+ memcpy(neigh->ha, dev->broadcast, dev->addr_len);
+ }
+ if (dev->hard_header_cache)
+ neigh->ops = &arp_hh_ops;
+ else
+ neigh->ops = &arp_generic_ops;
+ if (neigh->nud_state&NUD_VALID)
+ neigh->output = neigh->ops->connected_output;
+ else
+ neigh->output = neigh->ops->output;
+ }
+
+ return 0;
+}
+
+static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb)
+{
+ dst_link_failure(skb);
+ kfree_skb(skb);
+}
+
+static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
+{
+ u32 saddr;
+ u8 *dst_ha = NULL;
+ struct device *dev = neigh->dev;
+ struct device *dev2;
+ struct in_device *in_dev2;
+ u32 target = *(u32*)neigh->primary_key;
+ int probes = neigh->probes;
+
+ if (skb &&
+ (dev2 = ip_dev_find(skb->nh.iph->saddr)) != NULL &&
+ (in_dev2 = dev2->ip_ptr) != NULL &&
+ !IN_DEV_HIDDEN(in_dev2))
+ saddr = skb->nh.iph->saddr;
+ else
+ saddr = inet_select_addr(dev, target, RT_SCOPE_LINK);
+
+ if ((probes -= neigh->parms->ucast_probes) < 0) {
+ if (!(neigh->nud_state&NUD_VALID))
+ printk(KERN_DEBUG "trying to ucast probe in NUD_INVALID\n");
+ dst_ha = neigh->ha;
+ } else if ((probes -= neigh->parms->app_probes) < 0) {
+#ifdef CONFIG_ARPD
+ neigh_app_ns(neigh);
+#endif
+ return;
+ }
+
+ arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr,
+ dst_ha, dev->dev_addr, NULL);
+}
+
+/* OBSOLETE FUNCTIONS */
+
+/*
+ * Find an arp mapping in the cache. If not found, post a request.
+ *
+ * It is very UGLY routine: it DOES NOT use skb->dst->neighbour,
+ * even if it exists. It is supposed that skb->dev was mangled
+ * by a virtual device (eql, shaper). Nobody but broken devices
+ * is allowed to use this function, it is scheduled to be removed. --ANK
+ */
+
+static int arp_set_predefined(int addr_hint, unsigned char * haddr, u32 paddr, struct device * dev)
+{
+ switch (addr_hint) {
+ case RTN_LOCAL:
+ printk(KERN_DEBUG "ARP: arp called for own IP address\n");
+ memcpy(haddr, dev->dev_addr, dev->addr_len);
+ return 1;
+ case RTN_MULTICAST:
+ arp_mc_map(paddr, haddr, dev, 1);
+ return 1;
+ case RTN_BROADCAST:
+ memcpy(haddr, dev->broadcast, dev->addr_len);
+ return 1;
+ }
+ return 0;
+}
+
+
+int arp_find(unsigned char *haddr, struct sk_buff *skb)
+{
+ struct device *dev = skb->dev;
+ u32 paddr;
+ struct neighbour *n;
+
+ if (!skb->dst) {
+ printk(KERN_DEBUG "arp_find is called with dst==NULL\n");
+ kfree_skb(skb);
+ return 1;
+ }
+
+ paddr = ((struct rtable*)skb->dst)->rt_gateway;
+
+ if (arp_set_predefined(inet_addr_type(paddr), haddr, paddr, dev))
+ return 0;
+
+ start_bh_atomic();
+ n = __neigh_lookup(&arp_tbl, &paddr, dev, 1);
+
+ if (n) {
+ n->used = jiffies;
+ if (n->nud_state&NUD_VALID || neigh_event_send(n, skb) == 0) {
+ memcpy(haddr, n->ha, dev->addr_len);
+ neigh_release(n);
+ end_bh_atomic();
+ return 0;
+ }
+ neigh_release(n);
+ } else
+ kfree_skb(skb);
+ end_bh_atomic();
+ return 1;
+}
+
+/* END OF OBSOLETE FUNCTIONS */
+
+/*
+ * Note: requires bh_atomic locking.
+ */
+int arp_bind_neighbour(struct dst_entry *dst)
+{
+ struct device *dev = dst->dev;
+
+ if (dev == NULL)
+ return 0;
+ if (dst->neighbour == NULL) {
+ u32 nexthop = ((struct rtable*)dst)->rt_gateway;
+ if (dev->flags&(IFF_LOOPBACK|IFF_POINTOPOINT))
+ nexthop = 0;
+ dst->neighbour = __neigh_lookup(&arp_tbl, &nexthop, dev, 1);
+ }
+ return (dst->neighbour != NULL);
+}
+
+/*
+ * Interface to link layer: send routine and receive handler.
+ */
+
+/*
+ * Create and send an arp packet. If (dest_hw == NULL), we create a broadcast
+ * message.
+ */
+
+void arp_send(int type, int ptype, u32 dest_ip,
+ struct device *dev, u32 src_ip,
+ unsigned char *dest_hw, unsigned char *src_hw,
+ unsigned char *target_hw)
+{
+ struct sk_buff *skb;
+ struct arphdr *arp;
+ unsigned char *arp_ptr;
+
+ /*
+ * No arp on this interface.
+ */
+
+ if (dev->flags&IFF_NOARP)
+ return;
+
+ /*
+ * Allocate a buffer
+ */
+
+ skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4)
+ + dev->hard_header_len + 15, GFP_ATOMIC);
+ if (skb == NULL)
+ return;
+
+ skb_reserve(skb, (dev->hard_header_len+15)&~15);
+ skb->nh.raw = skb->data;
+ arp = (struct arphdr *) skb_put(skb,sizeof(struct arphdr) + 2*(dev->addr_len+4));
+ skb->dev = dev;
+ skb->protocol = __constant_htons (ETH_P_ARP);
+ if (src_hw == NULL)
+ src_hw = dev->dev_addr;
+ if (dest_hw == NULL)
+ dest_hw = dev->broadcast;
+
+ /*
+ * Fill the device header for the ARP frame
+ */
+ dev->hard_header(skb,dev,ptype,dest_hw,src_hw,skb->len);
+
+ /*
+ * Fill out the arp protocol part.
+ *
+ * The arp hardware type should match the device type, except for FDDI,
+ * which (according to RFC 1390) should always equal 1 (Ethernet).
+ */
+ /*
+ * Exceptions everywhere. AX.25 uses the AX.25 PID value not the
+ * DIX code for the protocol. Make these device structure fields.
+ */
+ switch (dev->type) {
+ default:
+ arp->ar_hrd = htons(dev->type);
+ arp->ar_pro = __constant_htons(ETH_P_IP);
+ break;
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ case ARPHRD_AX25:
+ arp->ar_hrd = __constant_htons(ARPHRD_AX25);
+ arp->ar_pro = __constant_htons(AX25_P_IP);
+ break;
+
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+ case ARPHRD_NETROM:
+ arp->ar_hrd = __constant_htons(ARPHRD_NETROM);
+ arp->ar_pro = __constant_htons(AX25_P_IP);
+ break;
+#endif
+#endif
+
+#ifdef CONFIG_FDDI
+ case ARPHRD_FDDI:
+ arp->ar_hrd = __constant_htons(ARPHRD_ETHER);
+ arp->ar_pro = __constant_htons(ETH_P_IP);
+ break;
+#endif
+ }
+
+ arp->ar_hln = dev->addr_len;
+ arp->ar_pln = 4;
+ arp->ar_op = htons(type);
+
+ arp_ptr=(unsigned char *)(arp+1);
+
+ memcpy(arp_ptr, src_hw, dev->addr_len);
+ arp_ptr+=dev->addr_len;
+ memcpy(arp_ptr, &src_ip,4);
+ arp_ptr+=4;
+ if (target_hw != NULL)
+ memcpy(arp_ptr, target_hw, dev->addr_len);
+ else
+ memset(arp_ptr, 0, dev->addr_len);
+ arp_ptr+=dev->addr_len;
+ memcpy(arp_ptr, &dest_ip, 4);
+ skb->dev = dev;
+
+ dev_queue_xmit(skb);
+}
+
+static void parp_redo(struct sk_buff *skb)
+{
+ arp_rcv(skb, skb->dev, NULL);
+}
+
+/*
+ * Receive an arp request by the device layer.
+ */
+
+int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct arphdr *arp = skb->nh.arph;
+ unsigned char *arp_ptr= (unsigned char *)(arp+1);
+ struct rtable *rt;
+ unsigned char *sha, *tha;
+ u32 sip, tip;
+ u16 dev_type = dev->type;
+ int addr_type;
+ struct in_device *in_dev = dev->ip_ptr;
+ struct neighbour *n;
+
+/*
+ * The hardware length of the packet should match the hardware length
+ * of the device. Similarly, the hardware types should match. The
+ * device should be ARP-able. Also, if pln is not 4, then the lookup
+ * is not from an IP number. We can't currently handle this, so toss
+ * it.
+ */
+ if (in_dev == NULL ||
+ arp->ar_hln != dev->addr_len ||
+ dev->flags & IFF_NOARP ||
+ skb->pkt_type == PACKET_OTHERHOST ||
+ skb->pkt_type == PACKET_LOOPBACK ||
+ arp->ar_pln != 4)
+ goto out;
+
+ switch (dev_type) {
+ default:
+ if (arp->ar_pro != __constant_htons(ETH_P_IP))
+ goto out;
+ if (htons(dev_type) != arp->ar_hrd)
+ goto out;
+ break;
+#ifdef CONFIG_NET_ETHERNET
+ case ARPHRD_ETHER:
+ /*
+ * ETHERNET devices will accept ARP hardware types of either
+ * 1 (Ethernet) or 6 (IEEE 802.2).
+ */
+ if (arp->ar_hrd != __constant_htons(ARPHRD_ETHER) &&
+ arp->ar_hrd != __constant_htons(ARPHRD_IEEE802))
+ goto out;
+ if (arp->ar_pro != __constant_htons(ETH_P_IP))
+ goto out;
+ break;
+#endif
+#ifdef CONFIG_FDDI
+ case ARPHRD_FDDI:
+ /*
+ * According to RFC 1390, FDDI devices should accept ARP hardware types
+ * of 1 (Ethernet). However, to be more robust, we'll accept hardware
+ * types of either 1 (Ethernet) or 6 (IEEE 802.2).
+ */
+ if (arp->ar_hrd != __constant_htons(ARPHRD_ETHER) &&
+ arp->ar_hrd != __constant_htons(ARPHRD_IEEE802))
+ goto out;
+ if (arp->ar_pro != __constant_htons(ETH_P_IP))
+ goto out;
+ break;
+#endif
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ case ARPHRD_AX25:
+ if (arp->ar_pro != __constant_htons(AX25_P_IP))
+ goto out;
+ if (arp->ar_hrd != __constant_htons(ARPHRD_AX25))
+ goto out;
+ break;
+#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+ case ARPHRD_NETROM:
+ if (arp->ar_pro != __constant_htons(AX25_P_IP))
+ goto out;
+ if (arp->ar_hrd != __constant_htons(ARPHRD_NETROM))
+ goto out;
+ break;
+#endif
+#endif
+ }
+
+ /* Undertsand only these message types */
+
+ if (arp->ar_op != __constant_htons(ARPOP_REPLY) &&
+ arp->ar_op != __constant_htons(ARPOP_REQUEST))
+ goto out;
+
+/*
+ * Extract fields
+ */
+ sha=arp_ptr;
+ arp_ptr += dev->addr_len;
+ memcpy(&sip, arp_ptr, 4);
+ arp_ptr += 4;
+ tha=arp_ptr;
+ arp_ptr += dev->addr_len;
+ memcpy(&tip, arp_ptr, 4);
+/*
+ * Check for bad requests for 127.x.x.x and requests for multicast
+ * addresses. If this is one such, delete it.
+ */
+ if (LOOPBACK(tip) || MULTICAST(tip))
+ goto out;
+
+/*
+ * Process entry. The idea here is we want to send a reply if it is a
+ * request for us or if it is a request for someone else that we hold
+ * a proxy for. We want to add an entry to our cache if it is a reply
+ * to us or if it is a request for our address.
+ * (The assumption for this last is that if someone is requesting our
+ * address, they are probably intending to talk to us, so it saves time
+ * if we cache their address. Their address is also probably not in
+ * our cache, since ours is not in their cache.)
+ *
+ * Putting this another way, we only care about replies if they are to
+ * us, in which case we add them to the cache. For requests, we care
+ * about those for us and those for our proxies. We reply to both,
+ * and in the case of requests for us we add the requester to the arp
+ * cache.
+ */
+
+ /* Special case: IPv4 duplicate address detection packet (RFC2131) */
+ if (sip == 0) {
+ struct device *dev2;
+ struct in_device *in_dev2;
+
+ if (arp->ar_op == __constant_htons(ARPOP_REQUEST) &&
+ (dev2 = ip_dev_find(tip)) != NULL &&
+ (dev2 == dev ||
+ ((in_dev2 = dev2->ip_ptr) != NULL &&
+ !IN_DEV_HIDDEN(in_dev2))))
+ arp_send(ARPOP_REPLY,ETH_P_ARP,tip,dev,tip,sha,dev->dev_addr,dev->dev_addr);
+ goto out;
+ }
+
+ if (arp->ar_op == __constant_htons(ARPOP_REQUEST) &&
+ ip_route_input(skb, tip, sip, 0, dev) == 0) {
+
+ rt = (struct rtable*)skb->dst;
+ addr_type = rt->rt_type;
+
+ if (addr_type == RTN_LOCAL) {
+ n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
+ if (n) {
+ if (ipv4_devconf.hidden &&
+ skb->pkt_type != PACKET_HOST) {
+ struct device *dev2;
+ struct in_device *in_dev2;
+
+ if ((dev2 = ip_dev_find(tip)) != NULL &&
+ dev2 != dev &&
+ (in_dev2 = dev2->ip_ptr) != NULL &&
+ IN_DEV_HIDDEN(in_dev2)) {
+ neigh_release(n);
+ goto out;
+ }
+ }
+
+ arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
+ neigh_release(n);
+ }
+ goto out;
+ } else if (IN_DEV_FORWARD(in_dev)) {
+ if ((rt->rt_flags&RTCF_DNAT) ||
+ (addr_type == RTN_UNICAST && rt->u.dst.dev != dev &&
+ (IN_DEV_PROXY_ARP(in_dev) || pneigh_lookup(&arp_tbl, &tip, dev, 0)))) {
+ n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
+ neigh_release(n);
+
+ if (skb->stamp.tv_sec == 0 ||
+ skb->pkt_type == PACKET_HOST ||
+ in_dev->arp_parms->proxy_delay == 0) {
+ arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
+ } else {
+ pneigh_enqueue(&arp_tbl, in_dev->arp_parms, skb);
+ return 0;
+ }
+ goto out;
+ }
+ }
+ }
+
+ /* Update our ARP tables */
+
+ n = __neigh_lookup(&arp_tbl, &sip, dev, 0);
+
+#ifdef CONFIG_IP_ACCEPT_UNSOLICITED_ARP
+ /* Unsolicited ARP is not accepted by default.
+ It is possible, that this option should be enabled for some
+ devices (strip is candidate)
+ */
+ if (n == NULL &&
+ arp->ar_op == __constant_htons(ARPOP_REPLY) &&
+ inet_addr_type(sip) == RTN_UNICAST)
+ n = __neigh_lookup(&arp_tbl, &sip, dev, -1);
+#endif
+
+ if (n) {
+ int state = NUD_REACHABLE;
+ int override = 0;
+
+ /* If several different ARP replies follows back-to-back,
+ use the FIRST one. It is possible, if several proxy
+ agents are active. Taking the first reply prevents
+ arp trashing and chooses the fastest router.
+ */
+ if (jiffies - n->updated >= n->parms->locktime)
+ override = 1;
+
+ /* Broadcast replies and request packets
+ do not assert neighbour reachability.
+ */
+ if (arp->ar_op != __constant_htons(ARPOP_REPLY) ||
+ skb->pkt_type != PACKET_HOST)
+ state = NUD_STALE;
+ neigh_update(n, sha, state, override, 1);
+ neigh_release(n);
+ }
+
+out:
+ kfree_skb(skb);
+ return 0;
+}
+
+
+
+/*
+ * User level interface (ioctl, /proc)
+ */
+
+/*
+ * Set (create) an ARP cache entry.
+ */
+
+int arp_req_set(struct arpreq *r, struct device * dev)
+{
+ u32 ip = ((struct sockaddr_in *) &r->arp_pa)->sin_addr.s_addr;
+ struct neighbour *neigh;
+ int err;
+
+ if (r->arp_flags&ATF_PUBL) {
+ u32 mask = ((struct sockaddr_in *) &r->arp_netmask)->sin_addr.s_addr;
+ if (mask && mask != 0xFFFFFFFF)
+ return -EINVAL;
+ if (!dev && (r->arp_flags & ATF_COM)) {
+ dev = dev_getbyhwaddr(r->arp_ha.sa_family, r->arp_ha.sa_data);
+ if (!dev)
+ return -ENODEV;
+ }
+ if (mask) {
+ if (pneigh_lookup(&arp_tbl, &ip, dev, 1) == NULL)
+ return -ENOBUFS;
+ return 0;
+ }
+ if (dev == NULL) {
+ ipv4_devconf.proxy_arp = 1;
+ return 0;
+ }
+ if (dev->ip_ptr) {
+ ((struct in_device*)dev->ip_ptr)->cnf.proxy_arp = 1;
+ return 0;
+ }
+ return -ENXIO;
+ }
+
+ if (r->arp_flags & ATF_PERM)
+ r->arp_flags |= ATF_COM;
+ if (dev == NULL) {
+ struct rtable * rt;
+ if ((err = ip_route_output(&rt, ip, 0, RTO_ONLINK, 0)) != 0)
+ return err;
+ dev = rt->u.dst.dev;
+ ip_rt_put(rt);
+ if (!dev)
+ return -EINVAL;
+ }
+ if (r->arp_ha.sa_family != dev->type)
+ return -EINVAL;
+
+ err = -ENOBUFS;
+ start_bh_atomic();
+ neigh = __neigh_lookup(&arp_tbl, &ip, dev, 1);
+ if (neigh) {
+ unsigned state = NUD_STALE;
+ if (r->arp_flags & ATF_PERM)
+ state = NUD_PERMANENT;
+ err = neigh_update(neigh, (r->arp_flags&ATF_COM) ?
+ r->arp_ha.sa_data : NULL, state, 1, 0);
+ neigh_release(neigh);
+ }
+ end_bh_atomic();
+ return err;
+}
+
+static unsigned arp_state_to_flags(struct neighbour *neigh)
+{
+ unsigned flags = 0;
+ if (neigh->nud_state&NUD_PERMANENT)
+ flags = ATF_PERM|ATF_COM;
+ else if (neigh->nud_state&NUD_VALID)
+ flags = ATF_COM;
+ return flags;
+}
+
+/*
+ * Get an ARP cache entry.
+ */
+
+static int arp_req_get(struct arpreq *r, struct device *dev)
+{
+ u32 ip = ((struct sockaddr_in *) &r->arp_pa)->sin_addr.s_addr;
+ struct neighbour *neigh;
+ int err = -ENXIO;
+
+ start_bh_atomic();
+ neigh = __neigh_lookup(&arp_tbl, &ip, dev, 0);
+ if (neigh) {
+ memcpy(r->arp_ha.sa_data, neigh->ha, dev->addr_len);
+ r->arp_ha.sa_family = dev->type;
+ strncpy(r->arp_dev, dev->name, sizeof(r->arp_dev));
+ r->arp_flags = arp_state_to_flags(neigh);
+ neigh_release(neigh);
+ err = 0;
+ }
+ end_bh_atomic();
+ return err;
+}
+
+int arp_req_delete(struct arpreq *r, struct device * dev)
+{
+ int err;
+ u32 ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;
+ struct neighbour *neigh;
+
+ if (r->arp_flags & ATF_PUBL) {
+ u32 mask = ((struct sockaddr_in *) &r->arp_netmask)->sin_addr.s_addr;
+ if (mask == 0xFFFFFFFF)
+ return pneigh_delete(&arp_tbl, &ip, dev);
+ if (mask == 0) {
+ if (dev == NULL) {
+ ipv4_devconf.proxy_arp = 0;
+ return 0;
+ }
+ if (dev->ip_ptr) {
+ ((struct in_device*)dev->ip_ptr)->cnf.proxy_arp = 0;
+ return 0;
+ }
+ return -ENXIO;
+ }
+ return -EINVAL;
+ }
+
+ if (dev == NULL) {
+ struct rtable * rt;
+ if ((err = ip_route_output(&rt, ip, 0, RTO_ONLINK, 0)) != 0)
+ return err;
+ dev = rt->u.dst.dev;
+ ip_rt_put(rt);
+ if (!dev)
+ return -EINVAL;
+ }
+ err = -ENXIO;
+ start_bh_atomic();
+ neigh = __neigh_lookup(&arp_tbl, &ip, dev, 0);
+ if (neigh) {
+ if (neigh->nud_state&~NUD_NOARP)
+ err = neigh_update(neigh, NULL, NUD_FAILED, 1, 0);
+ neigh_release(neigh);
+ }
+ end_bh_atomic();
+ return err;
+}
+
+#ifdef _HURD_
+#define arp_ioctl 0
+#else
+/*
+ * Handle an ARP layer I/O control request.
+ */
+
+int arp_ioctl(unsigned int cmd, void *arg)
+{
+ int err;
+ struct arpreq r;
+ struct device * dev = NULL;
+
+ switch(cmd) {
+ case SIOCDARP:
+ case SIOCSARP:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ case SIOCGARP:
+ err = copy_from_user(&r, arg, sizeof(struct arpreq));
+ if (err)
+ return -EFAULT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (r.arp_pa.sa_family != AF_INET)
+ return -EPFNOSUPPORT;
+
+ if (!(r.arp_flags & ATF_PUBL) &&
+ (r.arp_flags & (ATF_NETMASK|ATF_DONTPUB)))
+ return -EINVAL;
+ if (!(r.arp_flags & ATF_NETMASK))
+ ((struct sockaddr_in *)&r.arp_netmask)->sin_addr.s_addr=__constant_htonl(0xFFFFFFFFUL);
+
+ rtnl_lock();
+ if (r.arp_dev[0]) {
+ err = -ENODEV;
+ if ((dev = dev_get(r.arp_dev)) == NULL)
+ goto out;
+
+ /* Mmmm... It is wrong... ARPHRD_NETROM==0 */
+ if (!r.arp_ha.sa_family)
+ r.arp_ha.sa_family = dev->type;
+ err = -EINVAL;
+ if ((r.arp_flags & ATF_COM) && r.arp_ha.sa_family != dev->type)
+ goto out;
+ } else if (cmd == SIOCGARP) {
+ err = -ENODEV;
+ goto out;
+ }
+
+ switch(cmd) {
+ case SIOCDARP:
+ err = arp_req_delete(&r, dev);
+ break;
+ case SIOCSARP:
+ err = arp_req_set(&r, dev);
+ break;
+ case SIOCGARP:
+ err = arp_req_get(&r, dev);
+ if (!err && copy_to_user(arg, &r, sizeof(r)))
+ err = -EFAULT;
+ break;
+ }
+out:
+ rtnl_unlock();
+ return err;
+}
+#endif
+
+/*
+ * Write the contents of the ARP cache to a PROCfs file.
+ */
+#ifdef CONFIG_PROC_FS
+
+#define HBUFFERLEN 30
+
+int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len=0;
+ off_t pos=0;
+ int size;
+ char hbuffer[HBUFFERLEN];
+ int i,j,k;
+ const char hexbuf[] = "0123456789ABCDEF";
+
+ size = sprintf(buffer,"IP address HW type Flags HW address Mask Device\n");
+
+ pos+=size;
+ len+=size;
+
+ neigh_table_lock(&arp_tbl);
+
+ for(i=0; i<=NEIGH_HASHMASK; i++) {
+ struct neighbour *n;
+ for (n=arp_tbl.hash_buckets[i]; n; n=n->next) {
+ struct device *dev = n->dev;
+ int hatype = dev->type;
+
+ /* Do not confuse users "arp -a" with magic entries */
+ if (!(n->nud_state&~NUD_NOARP))
+ continue;
+
+ /* I'd get great pleasure deleting
+ this ugly code. Let's output it in hexadecimal format.
+ "arp" utility will eventually repaired --ANK
+ */
+#if 1 /* UGLY CODE */
+/*
+ * Convert hardware address to XX:XX:XX:XX ... form.
+ */
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ if (hatype == ARPHRD_AX25 || hatype == ARPHRD_NETROM)
+ strcpy(hbuffer,ax2asc((ax25_address *)n->ha));
+ else {
+#endif
+ for (k=0,j=0;k<HBUFFERLEN-3 && j<dev->addr_len;j++) {
+ hbuffer[k++]=hexbuf[(n->ha[j]>>4)&15 ];
+ hbuffer[k++]=hexbuf[n->ha[j]&15 ];
+ hbuffer[k++]=':';
+ }
+ hbuffer[--k]=0;
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ }
+#endif
+#else
+ if ((neigh->nud_state&NUD_VALID) && dev->addr_len) {
+ int j;
+ for (j=0; j < dev->addr_len; j++)
+ sprintf(hbuffer+2*j, "%02x", neigh->ha[j]);
+ } else
+ sprintf(hbuffer, "0");
+#endif
+
+ size = sprintf(buffer+len,
+ "%-17s0x%-10x0x%-10x%s",
+ in_ntoa(*(u32*)n->primary_key),
+ hatype,
+ arp_state_to_flags(n),
+ hbuffer);
+ size += sprintf(buffer+len+size,
+ " %-17s %s\n",
+ "*", dev->name);
+
+ len += size;
+ pos += size;
+
+ if (pos <= offset)
+ len=0;
+ if (pos >= offset+length)
+ goto done;
+ }
+ }
+
+ for (i=0; i<=PNEIGH_HASHMASK; i++) {
+ struct pneigh_entry *n;
+ for (n=arp_tbl.phash_buckets[i]; n; n=n->next) {
+ struct device *dev = n->dev;
+ int hatype = dev ? dev->type : 0;
+
+ size = sprintf(buffer+len,
+ "%-17s0x%-10x0x%-10x%s",
+ in_ntoa(*(u32*)n->key),
+ hatype,
+ ATF_PUBL|ATF_PERM,
+ "00:00:00:00:00:00");
+ size += sprintf(buffer+len+size,
+ " %-17s %s\n",
+ "*", dev ? dev->name : "*");
+
+ len += size;
+ pos += size;
+
+ if (pos <= offset)
+ len=0;
+ if (pos >= offset+length)
+ goto done;
+ }
+ }
+
+done:
+ neigh_table_unlock(&arp_tbl);
+
+ *start = buffer+len-(pos-offset); /* Start of wanted data */
+ len = pos-offset; /* Start slop */
+ if (len>length)
+ len = length; /* Ending slop */
+ if (len<0)
+ len = 0;
+ return len;
+}
+#endif
+
+/* Note, that it is not on notifier chain.
+ It is necessary, that this routine was called after route cache will be
+ flushed.
+ */
+void arp_ifdown(struct device *dev)
+{
+ neigh_ifdown(&arp_tbl, dev);
+}
+
+
+/*
+ * Called once on startup.
+ */
+
+static struct packet_type arp_packet_type =
+{
+ __constant_htons(ETH_P_ARP),
+ NULL, /* All devices */
+ arp_rcv,
+ NULL,
+ NULL
+};
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_arp = {
+ PROC_NET_ARP, 3, "arp",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ arp_get_info
+};
+#endif
+
+__initfunc(void arp_init (void))
+{
+ neigh_table_init(&arp_tbl);
+
+ dev_add_pack(&arp_packet_type);
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_net_arp);
+#endif
+#ifdef CONFIG_SYSCTL
+ neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4");
+#endif
+}
+
+
+#ifdef CONFIG_AX25_MODULE
+
+/*
+ * ax25 -> ASCII conversion
+ */
+char *ax2asc(ax25_address *a)
+{
+ static char buf[11];
+ char c, *s;
+ int n;
+
+ for (n = 0, s = buf; n < 6; n++) {
+ c = (a->ax25_call[n] >> 1) & 0x7F;
+
+ if (c != ' ') *s++ = c;
+ }
+
+ *s++ = '-';
+
+ if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) {
+ *s++ = '1';
+ n -= 10;
+ }
+
+ *s++ = n + '0';
+ *s++ = '\0';
+
+ if (*buf == '\0' || *buf == '-')
+ return "*";
+
+ return buf;
+
+}
+
+#endif
diff --git a/pfinet/linux-src/net/ipv4/devinet.c b/pfinet/linux-src/net/ipv4/devinet.c
new file mode 100644
index 00000000..0416ee82
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/devinet.c
@@ -0,0 +1,1121 @@
+/*
+ * NET3 IP device support routines.
+ *
+ * Version: $Id: devinet.c,v 1.28.2.2 1999/08/07 10:56:18 davem Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Derived from the IP parts of dev.c 1.0.19
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ *
+ * Additional Authors:
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Changes:
+ * Alexey Kuznetsov: pa_* fields are replaced with ifaddr lists.
+ * Cyrus Durgin: updated for kmod
+ */
+
+#include <linux/config.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/init.h>
+#include <linux/notifier.h>
+#include <linux/inetdevice.h>
+#include <linux/igmp.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#include <net/ip.h>
+#include <net/route.h>
+#include <net/ip_fib.h>
+
+struct ipv4_devconf ipv4_devconf = { 1, 1, 1, 1, 0, };
+static struct ipv4_devconf ipv4_devconf_dflt = { 1, 1, 1, 1, 1, };
+
+#ifdef CONFIG_RTNETLINK
+static void rtmsg_ifa(int event, struct in_ifaddr *);
+#else
+#define rtmsg_ifa(a,b) do { } while(0)
+#endif
+
+static struct notifier_block *inetaddr_chain;
+static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, int destroy);
+#ifdef CONFIG_SYSCTL
+static void devinet_sysctl_register(struct in_device *in_dev, struct ipv4_devconf *p);
+static void devinet_sysctl_unregister(struct ipv4_devconf *p);
+#endif
+
+int inet_ifa_count;
+int inet_dev_count;
+
+static struct in_ifaddr * inet_alloc_ifa(void)
+{
+ struct in_ifaddr *ifa;
+
+ ifa = kmalloc(sizeof(*ifa), GFP_KERNEL);
+ if (ifa) {
+ memset(ifa, 0, sizeof(*ifa));
+ inet_ifa_count++;
+ }
+
+ return ifa;
+}
+
+static __inline__ void inet_free_ifa(struct in_ifaddr *ifa)
+{
+ kfree_s(ifa, sizeof(*ifa));
+ inet_ifa_count--;
+}
+
+struct in_device *inetdev_init(struct device *dev)
+{
+ struct in_device *in_dev;
+
+ if (dev->mtu < 68)
+ return NULL;
+
+ in_dev = kmalloc(sizeof(*in_dev), GFP_KERNEL);
+ if (!in_dev)
+ return NULL;
+ inet_dev_count++;
+ memset(in_dev, 0, sizeof(*in_dev));
+ memcpy(&in_dev->cnf, &ipv4_devconf_dflt, sizeof(in_dev->cnf));
+ in_dev->cnf.sysctl = NULL;
+ in_dev->dev = dev;
+ if ((in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl)) == NULL) {
+ kfree(in_dev);
+ return NULL;
+ }
+#ifdef CONFIG_SYSCTL
+ neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4");
+#endif
+ dev->ip_ptr = in_dev;
+#ifdef CONFIG_SYSCTL
+ devinet_sysctl_register(in_dev, &in_dev->cnf);
+#endif
+ if (dev->flags&IFF_UP)
+ ip_mc_up(in_dev);
+ return in_dev;
+}
+
+static void inetdev_destroy(struct in_device *in_dev)
+{
+ struct in_ifaddr *ifa;
+
+ ip_mc_destroy_dev(in_dev);
+
+ while ((ifa = in_dev->ifa_list) != NULL) {
+ inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
+ inet_free_ifa(ifa);
+ }
+
+#ifdef CONFIG_SYSCTL
+ devinet_sysctl_unregister(&in_dev->cnf);
+#endif
+ in_dev->dev->ip_ptr = NULL;
+ synchronize_bh();
+ neigh_parms_release(&arp_tbl, in_dev->arp_parms);
+ kfree(in_dev);
+}
+
+struct in_ifaddr * inet_addr_onlink(struct in_device *in_dev, u32 a, u32 b)
+{
+ for_primary_ifa(in_dev) {
+ if (inet_ifa_match(a, ifa)) {
+ if (!b || inet_ifa_match(b, ifa))
+ return ifa;
+ }
+ } endfor_ifa(in_dev);
+ return NULL;
+}
+
+static void
+inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, int destroy)
+{
+ struct in_ifaddr *ifa1 = *ifap;
+
+ /* 1. Deleting primary ifaddr forces deletion all secondaries */
+
+ if (!(ifa1->ifa_flags&IFA_F_SECONDARY)) {
+ struct in_ifaddr *ifa;
+ struct in_ifaddr **ifap1 = &ifa1->ifa_next;
+
+ while ((ifa=*ifap1) != NULL) {
+ if (!(ifa->ifa_flags&IFA_F_SECONDARY) ||
+ ifa1->ifa_mask != ifa->ifa_mask ||
+ !inet_ifa_match(ifa1->ifa_address, ifa)) {
+ ifap1 = &ifa->ifa_next;
+ continue;
+ }
+ *ifap1 = ifa->ifa_next;
+ synchronize_bh();
+
+ rtmsg_ifa(RTM_DELADDR, ifa);
+ notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa);
+ inet_free_ifa(ifa);
+ }
+ }
+
+ /* 2. Unlink it */
+
+ *ifap = ifa1->ifa_next;
+ synchronize_bh();
+
+ /* 3. Announce address deletion */
+
+ /* Send message first, then call notifier.
+ At first sight, FIB update triggered by notifier
+ will refer to already deleted ifaddr, that could confuse
+ netlink listeners. It is not true: look, gated sees
+ that route deleted and if it still thinks that ifaddr
+ is valid, it will try to restore deleted routes... Grr.
+ So that, this order is correct.
+ */
+ rtmsg_ifa(RTM_DELADDR, ifa1);
+ notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
+ if (destroy) {
+ inet_free_ifa(ifa1);
+ if (in_dev->ifa_list == NULL)
+ inetdev_destroy(in_dev);
+ }
+}
+
+static int
+inet_insert_ifa(struct in_device *in_dev, struct in_ifaddr *ifa)
+{
+ struct in_ifaddr *ifa1, **ifap, **last_primary;
+
+ if (ifa->ifa_local == 0) {
+ inet_free_ifa(ifa);
+ return 0;
+ }
+
+ ifa->ifa_flags &= ~IFA_F_SECONDARY;
+ last_primary = &in_dev->ifa_list;
+
+ for (ifap=&in_dev->ifa_list; (ifa1=*ifap)!=NULL; ifap=&ifa1->ifa_next) {
+ if (!(ifa1->ifa_flags&IFA_F_SECONDARY) && ifa->ifa_scope <= ifa1->ifa_scope)
+ last_primary = &ifa1->ifa_next;
+ if (ifa1->ifa_mask == ifa->ifa_mask && inet_ifa_match(ifa1->ifa_address, ifa)) {
+ if (ifa1->ifa_local == ifa->ifa_local) {
+ inet_free_ifa(ifa);
+ return -EEXIST;
+ }
+ if (ifa1->ifa_scope != ifa->ifa_scope) {
+ inet_free_ifa(ifa);
+ return -EINVAL;
+ }
+ ifa->ifa_flags |= IFA_F_SECONDARY;
+ }
+ }
+
+ if (!(ifa->ifa_flags&IFA_F_SECONDARY)) {
+ net_srandom(ifa->ifa_local);
+ ifap = last_primary;
+ }
+
+ ifa->ifa_next = *ifap;
+ wmb();
+ *ifap = ifa;
+
+ /* Send message first, then call notifier.
+ Notifier will trigger FIB update, so that
+ listeners of netlink will know about new ifaddr */
+ rtmsg_ifa(RTM_NEWADDR, ifa);
+ notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
+
+ return 0;
+}
+
+static int
+inet_set_ifa(struct device *dev, struct in_ifaddr *ifa)
+{
+ struct in_device *in_dev = dev->ip_ptr;
+
+ if (in_dev == NULL) {
+ in_dev = inetdev_init(dev);
+ if (in_dev == NULL) {
+ inet_free_ifa(ifa);
+ return -ENOBUFS;
+ }
+ }
+ ifa->ifa_dev = in_dev;
+ if (LOOPBACK(ifa->ifa_local))
+ ifa->ifa_scope = RT_SCOPE_HOST;
+ return inet_insert_ifa(in_dev, ifa);
+}
+
+struct in_device *inetdev_by_index(int ifindex)
+{
+ struct device *dev;
+ dev = dev_get_by_index(ifindex);
+ if (dev)
+ return dev->ip_ptr;
+ return NULL;
+}
+
+struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, u32 prefix, u32 mask)
+{
+ for_primary_ifa(in_dev) {
+ if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
+ return ifa;
+ } endfor_ifa(in_dev);
+ return NULL;
+}
+
+#ifdef CONFIG_RTNETLINK
+
+/* rtm_{add|del} functions are not reenterable, so that
+ this structure can be made static
+ */
+
+int
+inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct rtattr **rta = arg;
+ struct in_device *in_dev;
+ struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
+ struct in_ifaddr *ifa, **ifap;
+
+ if ((in_dev = inetdev_by_index(ifm->ifa_index)) == NULL)
+ return -EADDRNOTAVAIL;
+
+ for (ifap=&in_dev->ifa_list; (ifa=*ifap)!=NULL; ifap=&ifa->ifa_next) {
+ if ((rta[IFA_LOCAL-1] && memcmp(RTA_DATA(rta[IFA_LOCAL-1]), &ifa->ifa_local, 4)) ||
+ (rta[IFA_LABEL-1] && strcmp(RTA_DATA(rta[IFA_LABEL-1]), ifa->ifa_label)) ||
+ (rta[IFA_ADDRESS-1] &&
+ (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
+ !inet_ifa_match(*(u32*)RTA_DATA(rta[IFA_ADDRESS-1]), ifa))))
+ continue;
+ inet_del_ifa(in_dev, ifap, 1);
+ return 0;
+ }
+
+ return -EADDRNOTAVAIL;
+}
+
+int
+inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct rtattr **rta = arg;
+ struct device *dev;
+ struct in_device *in_dev;
+ struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
+ struct in_ifaddr *ifa;
+
+ if (ifm->ifa_prefixlen > 32 || rta[IFA_LOCAL-1] == NULL)
+ return -EINVAL;
+
+ if ((dev = dev_get_by_index(ifm->ifa_index)) == NULL)
+ return -ENODEV;
+
+ if ((in_dev = dev->ip_ptr) == NULL) {
+ in_dev = inetdev_init(dev);
+ if (!in_dev)
+ return -ENOBUFS;
+ }
+
+ if ((ifa = inet_alloc_ifa()) == NULL)
+ return -ENOBUFS;
+
+ if (rta[IFA_ADDRESS-1] == NULL)
+ rta[IFA_ADDRESS-1] = rta[IFA_LOCAL-1];
+ memcpy(&ifa->ifa_local, RTA_DATA(rta[IFA_LOCAL-1]), 4);
+ memcpy(&ifa->ifa_address, RTA_DATA(rta[IFA_ADDRESS-1]), 4);
+ ifa->ifa_prefixlen = ifm->ifa_prefixlen;
+ ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
+ if (rta[IFA_BROADCAST-1])
+ memcpy(&ifa->ifa_broadcast, RTA_DATA(rta[IFA_BROADCAST-1]), 4);
+ if (rta[IFA_ANYCAST-1])
+ memcpy(&ifa->ifa_anycast, RTA_DATA(rta[IFA_ANYCAST-1]), 4);
+ ifa->ifa_flags = ifm->ifa_flags;
+ ifa->ifa_scope = ifm->ifa_scope;
+ ifa->ifa_dev = in_dev;
+ if (rta[IFA_LABEL-1])
+ memcpy(ifa->ifa_label, RTA_DATA(rta[IFA_LABEL-1]), IFNAMSIZ);
+ else
+ memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
+
+ return inet_insert_ifa(in_dev, ifa);
+}
+
+#endif
+
+/*
+ * Determine a default network mask, based on the IP address.
+ */
+
+static __inline__ int inet_abc_len(u32 addr)
+{
+ if (ZERONET(addr))
+ return 0;
+
+ addr = ntohl(addr);
+ if (IN_CLASSA(addr))
+ return 8;
+ if (IN_CLASSB(addr))
+ return 16;
+ if (IN_CLASSC(addr))
+ return 24;
+
+ /*
+ * Something else, probably a multicast.
+ */
+
+ return -1;
+}
+
+
+#ifdef _HURD_
+
+#define devinet_ioctl 0
+
+error_t
+configure_device (struct device *dev,
+ uint32_t addr, uint32_t netmask, uint32_t peer,
+ uint32_t broadcast)
+{
+ struct in_device *in_dev = dev->ip_ptr;
+ struct in_ifaddr *ifa = in_dev ? in_dev->ifa_list : 0;
+
+ if (ifa)
+ {
+ inet_del_ifa (in_dev, &in_dev->ifa_list, 0);
+ ifa->ifa_broadcast = 0;
+ ifa->ifa_anycast = 0;
+ }
+ else
+ {
+ ifa = inet_alloc_ifa ();
+ if (!ifa)
+ return ENOBUFS;
+ memcpy (ifa->ifa_label, dev->name, IFNAMSIZ);
+
+ ifa->ifa_address = INADDR_NONE;
+ ifa->ifa_mask = INADDR_NONE;
+ ifa->ifa_broadcast = INADDR_NONE;
+ ifa->ifa_local = INADDR_NONE;
+ }
+
+ if (addr != INADDR_NONE)
+ ifa->ifa_address = ifa->ifa_local = addr;
+ if (netmask != INADDR_NONE && !(dev->flags & IFF_POINTOPOINT))
+ {
+ ifa->ifa_mask = netmask;
+ ifa->ifa_prefixlen = inet_mask_len (ifa->ifa_mask);
+ if ((dev->flags&IFF_BROADCAST) && ifa->ifa_prefixlen < 31)
+ ifa->ifa_broadcast = ifa->ifa_address|~ifa->ifa_mask;
+ else
+ ifa->ifa_broadcast = 0;
+ }
+ if (peer != INADDR_NONE && (dev->flags & IFF_POINTOPOINT))
+ {
+ ifa->ifa_prefixlen = 32;
+ ifa->ifa_mask = inet_make_mask(32);
+ ifa->ifa_address = peer;
+ }
+
+ if (broadcast != INADDR_NONE)
+ ifa->ifa_broadcast = broadcast;
+
+ return - (inet_set_ifa (dev, ifa)
+ ?: dev_change_flags (dev, dev->flags | IFF_UP));
+}
+
+void
+inquire_device (struct device *dev,
+ uint32_t *addr, uint32_t *netmask, uint32_t *peer,
+ uint32_t *broadcast)
+{
+ struct in_device *in_dev = dev->ip_ptr;
+ struct in_ifaddr *ifa = in_dev ? in_dev->ifa_list : 0;
+
+ if (ifa)
+ {
+ *addr = ifa->ifa_local;
+ *netmask = ifa->ifa_mask;
+ *peer = ifa->ifa_address;
+ *broadcast = ifa->ifa_broadcast;
+ }
+ else
+ *addr = *netmask = *peer = *broadcast = INADDR_NONE;
+}
+
+#else
+
+int devinet_ioctl(unsigned int cmd, void *arg)
+{
+ struct ifreq ifr;
+ struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
+ struct in_device *in_dev;
+ struct in_ifaddr **ifap = NULL;
+ struct in_ifaddr *ifa = NULL;
+ struct device *dev;
+#ifdef CONFIG_IP_ALIAS
+ char *colon;
+#endif
+ int exclusive = 0;
+ int ret = 0;
+
+ /*
+ * Fetch the caller's info block into kernel space
+ */
+
+ if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+ return -EFAULT;
+ ifr.ifr_name[IFNAMSIZ-1] = 0;
+
+#ifdef CONFIG_IP_ALIAS
+ colon = strchr(ifr.ifr_name, ':');
+ if (colon)
+ *colon = 0;
+#endif
+
+#ifdef CONFIG_KMOD
+ dev_load(ifr.ifr_name);
+#endif
+
+ switch(cmd) {
+ case SIOCGIFADDR: /* Get interface address */
+ case SIOCGIFBRDADDR: /* Get the broadcast address */
+ case SIOCGIFDSTADDR: /* Get the destination address */
+ case SIOCGIFNETMASK: /* Get the netmask for the interface */
+ /* Note that this ioctls will not sleep,
+ so that we do not impose a lock.
+ One day we will be forced to put shlock here (I mean SMP)
+ */
+ memset(sin, 0, sizeof(*sin));
+ sin->sin_family = AF_INET;
+ break;
+
+ case SIOCSIFFLAGS:
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+ rtnl_lock();
+ exclusive = 1;
+ break;
+ case SIOCSIFADDR: /* Set interface address (and family) */
+ case SIOCSIFBRDADDR: /* Set the broadcast address */
+ case SIOCSIFDSTADDR: /* Set the destination address */
+ case SIOCSIFNETMASK: /* Set the netmask for the interface */
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+ if (sin->sin_family != AF_INET)
+ return -EINVAL;
+ rtnl_lock();
+ exclusive = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ if ((dev = dev_get(ifr.ifr_name)) == NULL) {
+ ret = -ENODEV;
+ goto done;
+ }
+
+#ifdef CONFIG_IP_ALIAS
+ if (colon)
+ *colon = ':';
+#endif
+
+ if ((in_dev=dev->ip_ptr) != NULL) {
+ for (ifap=&in_dev->ifa_list; (ifa=*ifap) != NULL; ifap=&ifa->ifa_next)
+ if (strcmp(ifr.ifr_name, ifa->ifa_label) == 0)
+ break;
+ }
+
+ if (ifa == NULL && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) {
+ ret = -EADDRNOTAVAIL;
+ goto done;
+ }
+
+ switch(cmd) {
+ case SIOCGIFADDR: /* Get interface address */
+ sin->sin_addr.s_addr = ifa->ifa_local;
+ goto rarok;
+
+ case SIOCGIFBRDADDR: /* Get the broadcast address */
+ sin->sin_addr.s_addr = ifa->ifa_broadcast;
+ goto rarok;
+
+ case SIOCGIFDSTADDR: /* Get the destination address */
+ sin->sin_addr.s_addr = ifa->ifa_address;
+ goto rarok;
+
+ case SIOCGIFNETMASK: /* Get the netmask for the interface */
+ sin->sin_addr.s_addr = ifa->ifa_mask;
+ goto rarok;
+
+ case SIOCSIFFLAGS:
+#ifdef CONFIG_IP_ALIAS
+ if (colon) {
+ if (ifa == NULL) {
+ ret = -EADDRNOTAVAIL;
+ break;
+ }
+ if (!(ifr.ifr_flags&IFF_UP))
+ inet_del_ifa(in_dev, ifap, 1);
+ break;
+ }
+#endif
+ ret = dev_change_flags(dev, ifr.ifr_flags);
+ break;
+
+ case SIOCSIFADDR: /* Set interface address (and family) */
+ if (inet_abc_len(sin->sin_addr.s_addr) < 0) {
+ ret = -EINVAL;
+ break;
+ }
+
+ if (!ifa) {
+ if ((ifa = inet_alloc_ifa()) == NULL) {
+ ret = -ENOBUFS;
+ break;
+ }
+#ifdef CONFIG_IP_ALIAS
+ if (colon)
+ memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
+ else
+#endif
+ memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
+ } else {
+ ret = 0;
+ if (ifa->ifa_local == sin->sin_addr.s_addr)
+ break;
+ inet_del_ifa(in_dev, ifap, 0);
+ ifa->ifa_broadcast = 0;
+ ifa->ifa_anycast = 0;
+ }
+
+ ifa->ifa_address =
+ ifa->ifa_local = sin->sin_addr.s_addr;
+
+ if (!(dev->flags&IFF_POINTOPOINT)) {
+ ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
+ ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
+ if ((dev->flags&IFF_BROADCAST) && ifa->ifa_prefixlen < 31)
+ ifa->ifa_broadcast = ifa->ifa_address|~ifa->ifa_mask;
+ } else {
+ ifa->ifa_prefixlen = 32;
+ ifa->ifa_mask = inet_make_mask(32);
+ }
+ ret = inet_set_ifa(dev, ifa);
+ break;
+
+ case SIOCSIFBRDADDR: /* Set the broadcast address */
+ if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
+ inet_del_ifa(in_dev, ifap, 0);
+ ifa->ifa_broadcast = sin->sin_addr.s_addr;
+ inet_insert_ifa(in_dev, ifa);
+ }
+ break;
+
+ case SIOCSIFDSTADDR: /* Set the destination address */
+ if (ifa->ifa_address != sin->sin_addr.s_addr) {
+ if (inet_abc_len(sin->sin_addr.s_addr) < 0) {
+ ret = -EINVAL;
+ break;
+ }
+ inet_del_ifa(in_dev, ifap, 0);
+ ifa->ifa_address = sin->sin_addr.s_addr;
+ inet_insert_ifa(in_dev, ifa);
+ }
+ break;
+
+ case SIOCSIFNETMASK: /* Set the netmask for the interface */
+
+ /*
+ * The mask we set must be legal.
+ */
+ if (bad_mask(sin->sin_addr.s_addr, 0)) {
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ifa->ifa_mask != sin->sin_addr.s_addr) {
+ inet_del_ifa(in_dev, ifap, 0);
+ ifa->ifa_mask = sin->sin_addr.s_addr;
+ ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
+ inet_set_ifa(dev, ifa);
+ }
+ break;
+ }
+done:
+ if (exclusive)
+ rtnl_unlock();
+ return ret;
+
+rarok:
+ if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+ return -EFAULT;
+ return 0;
+}
+
+#endif
+
+static int
+inet_gifconf(struct device *dev, char *buf, int len)
+{
+ struct in_device *in_dev = dev->ip_ptr;
+ struct in_ifaddr *ifa;
+ struct ifreq ifr;
+ int done=0;
+
+ if (in_dev==NULL || (ifa=in_dev->ifa_list)==NULL)
+ return 0;
+
+ for ( ; ifa; ifa = ifa->ifa_next) {
+ if (!buf) {
+ done += sizeof(ifr);
+ continue;
+ }
+ if (len < (int) sizeof(ifr))
+ return done;
+ memset(&ifr, 0, sizeof(struct ifreq));
+ if (ifa->ifa_label)
+ strcpy(ifr.ifr_name, ifa->ifa_label);
+ else
+ strcpy(ifr.ifr_name, dev->name);
+
+#ifdef _HURD_
+ (*(struct sockaddr_in *) &ifr.ifr_addr).sin_len = sizeof (struct sockaddr_in);
+#endif
+ (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = AF_INET;
+ (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = ifa->ifa_local;
+
+ if (copy_to_user(buf, &ifr, sizeof(struct ifreq)))
+ return -EFAULT;
+ buf += sizeof(struct ifreq);
+ len -= sizeof(struct ifreq);
+ done += sizeof(struct ifreq);
+ }
+ return done;
+}
+
+u32 inet_select_addr(struct device *dev, u32 dst, int scope)
+{
+ u32 addr = 0;
+ struct in_device *in_dev = dev->ip_ptr;
+
+ if (in_dev == NULL)
+ return 0;
+
+ for_primary_ifa(in_dev) {
+ if (ifa->ifa_scope > scope)
+ continue;
+ if (!dst || inet_ifa_match(dst, ifa))
+ return ifa->ifa_local;
+ if (!addr)
+ addr = ifa->ifa_local;
+ } endfor_ifa(in_dev);
+
+ if (addr)
+ return addr;
+
+ /* Not loopback addresses on loopback should be preferred
+ in this case. It is importnat that lo is the first interface
+ in dev_base list.
+ */
+ for (dev=dev_base; dev; dev=dev->next) {
+ if ((in_dev=dev->ip_ptr) == NULL)
+ continue;
+
+ for_primary_ifa(in_dev) {
+ if (!IN_DEV_HIDDEN(in_dev) &&
+ ifa->ifa_scope <= scope &&
+ ifa->ifa_scope != RT_SCOPE_LINK)
+ return ifa->ifa_local;
+ } endfor_ifa(in_dev);
+ }
+
+ return 0;
+}
+
+/*
+ * Device notifier
+ */
+
+int register_inetaddr_notifier(struct notifier_block *nb)
+{
+ return notifier_chain_register(&inetaddr_chain, nb);
+}
+
+int unregister_inetaddr_notifier(struct notifier_block *nb)
+{
+ return notifier_chain_unregister(&inetaddr_chain,nb);
+}
+
+static int inetdev_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ struct device *dev = ptr;
+ struct in_device *in_dev = dev->ip_ptr;
+
+ if (in_dev == NULL)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_REGISTER:
+ if (in_dev)
+ printk(KERN_DEBUG "inetdev_event: bug\n");
+ dev->ip_ptr = NULL;
+ break;
+ case NETDEV_UP:
+ if (dev == &loopback_dev) {
+ struct in_ifaddr *ifa;
+ if ((ifa = inet_alloc_ifa()) != NULL) {
+ ifa->ifa_local =
+ ifa->ifa_address = htonl(INADDR_LOOPBACK);
+ ifa->ifa_prefixlen = 8;
+ ifa->ifa_mask = inet_make_mask(8);
+ ifa->ifa_dev = in_dev;
+ ifa->ifa_scope = RT_SCOPE_HOST;
+ memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
+ inet_insert_ifa(in_dev, ifa);
+ }
+ }
+ ip_mc_up(in_dev);
+ break;
+ case NETDEV_DOWN:
+ ip_mc_down(in_dev);
+ break;
+ case NETDEV_CHANGEMTU:
+ if (dev->mtu >= 68)
+ break;
+ /* MTU falled under minimal IP mtu. Disable IP. */
+ case NETDEV_UNREGISTER:
+ inetdev_destroy(in_dev);
+ break;
+ case NETDEV_CHANGENAME:
+ if (in_dev->ifa_list) {
+ struct in_ifaddr *ifa;
+ for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next)
+ memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
+ /* Do not notify about label change, this event is
+ not interesting to applications using netlink.
+ */
+ }
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+struct notifier_block ip_netdev_notifier={
+ inetdev_event,
+ NULL,
+ 0
+};
+
+#ifdef CONFIG_RTNETLINK
+
+static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
+ u32 pid, u32 seq, int event)
+{
+ struct ifaddrmsg *ifm;
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+
+ nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*ifm));
+ ifm = NLMSG_DATA(nlh);
+ ifm->ifa_family = AF_INET;
+ ifm->ifa_prefixlen = ifa->ifa_prefixlen;
+ ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT;
+ ifm->ifa_scope = ifa->ifa_scope;
+ ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
+ if (ifa->ifa_address)
+ RTA_PUT(skb, IFA_ADDRESS, 4, &ifa->ifa_address);
+ if (ifa->ifa_local)
+ RTA_PUT(skb, IFA_LOCAL, 4, &ifa->ifa_local);
+ if (ifa->ifa_broadcast)
+ RTA_PUT(skb, IFA_BROADCAST, 4, &ifa->ifa_broadcast);
+ if (ifa->ifa_anycast)
+ RTA_PUT(skb, IFA_ANYCAST, 4, &ifa->ifa_anycast);
+ if (ifa->ifa_label[0])
+ RTA_PUT(skb, IFA_LABEL, IFNAMSIZ, &ifa->ifa_label);
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int idx, ip_idx;
+ int s_idx, s_ip_idx;
+ struct device *dev;
+ struct in_device *in_dev;
+ struct in_ifaddr *ifa;
+
+ s_idx = cb->args[0];
+ s_ip_idx = ip_idx = cb->args[1];
+ for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
+ if (idx < s_idx)
+ continue;
+ if (idx > s_idx)
+ s_ip_idx = 0;
+ if ((in_dev = dev->ip_ptr) == NULL)
+ continue;
+ for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
+ ifa = ifa->ifa_next, ip_idx++) {
+ if (ip_idx < s_ip_idx)
+ continue;
+ if (inet_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, RTM_NEWADDR) <= 0)
+ goto done;
+ }
+ }
+done:
+ cb->args[0] = idx;
+ cb->args[1] = ip_idx;
+
+ return skb->len;
+}
+
+static void rtmsg_ifa(int event, struct in_ifaddr * ifa)
+{
+ struct sk_buff *skb;
+ int size = NLMSG_SPACE(sizeof(struct ifaddrmsg)+128);
+
+ skb = alloc_skb(size, GFP_KERNEL);
+ if (!skb) {
+ netlink_set_err(rtnl, 0, RTMGRP_IPV4_IFADDR, ENOBUFS);
+ return;
+ }
+ if (inet_fill_ifaddr(skb, ifa, 0, 0, event) < 0) {
+ kfree_skb(skb);
+ netlink_set_err(rtnl, 0, RTMGRP_IPV4_IFADDR, EINVAL);
+ return;
+ }
+ NETLINK_CB(skb).dst_groups = RTMGRP_IPV4_IFADDR;
+ netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV4_IFADDR, GFP_KERNEL);
+}
+
+
+static struct rtnetlink_link inet_rtnetlink_table[RTM_MAX-RTM_BASE+1] =
+{
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+
+ { inet_rtm_newaddr, NULL, },
+ { inet_rtm_deladdr, NULL, },
+ { NULL, inet_dump_ifaddr, },
+ { NULL, NULL, },
+
+ { inet_rtm_newroute, NULL, },
+ { inet_rtm_delroute, NULL, },
+ { inet_rtm_getroute, inet_dump_fib, },
+ { NULL, NULL, },
+
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+ { inet_rtm_newrule, NULL, },
+ { inet_rtm_delrule, NULL, },
+ { NULL, inet_dump_rules, },
+ { NULL, NULL, },
+#else
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+#endif
+};
+
+#endif /* CONFIG_RTNETLINK */
+
+
+#ifdef CONFIG_SYSCTL
+
+void inet_forward_change()
+{
+ struct device *dev;
+ int on = ipv4_devconf.forwarding;
+
+ ipv4_devconf.accept_redirects = !on;
+ ipv4_devconf_dflt.forwarding = on;
+
+ for (dev = dev_base; dev; dev = dev->next) {
+ struct in_device *in_dev = dev->ip_ptr;
+ if (in_dev)
+ in_dev->cnf.forwarding = on;
+ }
+
+ rt_cache_flush(0);
+
+ ip_statistics.IpForwarding = on ? 1 : 2;
+}
+
+static
+int devinet_sysctl_forward(ctl_table *ctl, int write, struct file * filp,
+ void *buffer, size_t *lenp)
+{
+ int *valp = ctl->data;
+ int val = *valp;
+ int ret;
+
+ ret = proc_dointvec(ctl, write, filp, buffer, lenp);
+
+ if (write && *valp != val) {
+ if (valp == &ipv4_devconf.forwarding)
+ inet_forward_change();
+ else if (valp != &ipv4_devconf_dflt.forwarding)
+ rt_cache_flush(0);
+ }
+
+ return ret;
+}
+
+static struct devinet_sysctl_table
+{
+ struct ctl_table_header *sysctl_header;
+ ctl_table devinet_vars[13];
+ ctl_table devinet_dev[2];
+ ctl_table devinet_conf_dir[2];
+ ctl_table devinet_proto_dir[2];
+ ctl_table devinet_root_dir[2];
+} devinet_sysctl = {
+ NULL,
+ {{NET_IPV4_CONF_FORWARDING, "forwarding",
+ &ipv4_devconf.forwarding, sizeof(int), 0644, NULL,
+ &devinet_sysctl_forward},
+ {NET_IPV4_CONF_MC_FORWARDING, "mc_forwarding",
+ &ipv4_devconf.mc_forwarding, sizeof(int), 0444, NULL,
+ &proc_dointvec},
+ {NET_IPV4_CONF_ACCEPT_REDIRECTS, "accept_redirects",
+ &ipv4_devconf.accept_redirects, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_CONF_SECURE_REDIRECTS, "secure_redirects",
+ &ipv4_devconf.secure_redirects, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_CONF_SHARED_MEDIA, "shared_media",
+ &ipv4_devconf.shared_media, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_CONF_RP_FILTER, "rp_filter",
+ &ipv4_devconf.rp_filter, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_CONF_SEND_REDIRECTS, "send_redirects",
+ &ipv4_devconf.send_redirects, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_CONF_ACCEPT_SOURCE_ROUTE, "accept_source_route",
+ &ipv4_devconf.accept_source_route, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_CONF_PROXY_ARP, "proxy_arp",
+ &ipv4_devconf.proxy_arp, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_CONF_BOOTP_RELAY, "bootp_relay",
+ &ipv4_devconf.bootp_relay, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_CONF_LOG_MARTIANS, "log_martians",
+ &ipv4_devconf.log_martians, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_CONF_HIDDEN, "hidden",
+ &ipv4_devconf.hidden, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {0}},
+
+ {{NET_PROTO_CONF_ALL, "all", NULL, 0, 0555, devinet_sysctl.devinet_vars},{0}},
+ {{NET_IPV4_CONF, "conf", NULL, 0, 0555, devinet_sysctl.devinet_dev},{0}},
+ {{NET_IPV4, "ipv4", NULL, 0, 0555, devinet_sysctl.devinet_conf_dir},{0}},
+ {{CTL_NET, "net", NULL, 0, 0555, devinet_sysctl.devinet_proto_dir},{0}}
+};
+
+static void devinet_sysctl_register(struct in_device *in_dev, struct ipv4_devconf *p)
+{
+ int i;
+ struct device *dev = in_dev ? in_dev->dev : NULL;
+ struct devinet_sysctl_table *t;
+
+ t = kmalloc(sizeof(*t), GFP_KERNEL);
+ if (t == NULL)
+ return;
+ memcpy(t, &devinet_sysctl, sizeof(*t));
+ for (i=0; i<sizeof(t->devinet_vars)/sizeof(t->devinet_vars[0])-1; i++) {
+ t->devinet_vars[i].data += (char*)p - (char*)&ipv4_devconf;
+ t->devinet_vars[i].de = NULL;
+ }
+ if (dev) {
+ t->devinet_dev[0].procname = dev->name;
+ t->devinet_dev[0].ctl_name = dev->ifindex;
+ } else {
+ t->devinet_dev[0].procname = "default";
+ t->devinet_dev[0].ctl_name = NET_PROTO_CONF_DEFAULT;
+ }
+ t->devinet_dev[0].child = t->devinet_vars;
+ t->devinet_dev[0].de = NULL;
+ t->devinet_conf_dir[0].child = t->devinet_dev;
+ t->devinet_conf_dir[0].de = NULL;
+ t->devinet_proto_dir[0].child = t->devinet_conf_dir;
+ t->devinet_proto_dir[0].de = NULL;
+ t->devinet_root_dir[0].child = t->devinet_proto_dir;
+ t->devinet_root_dir[0].de = NULL;
+
+ t->sysctl_header = register_sysctl_table(t->devinet_root_dir, 0);
+ if (t->sysctl_header == NULL)
+ kfree(t);
+ else
+ p->sysctl = t;
+}
+
+static void devinet_sysctl_unregister(struct ipv4_devconf *p)
+{
+ if (p->sysctl) {
+ struct devinet_sysctl_table *t = p->sysctl;
+ p->sysctl = NULL;
+ unregister_sysctl_table(t->sysctl_header);
+ kfree(t);
+ }
+}
+#endif
+
+__initfunc(void devinet_init(void))
+{
+ register_gifconf(PF_INET, inet_gifconf);
+ register_netdevice_notifier(&ip_netdev_notifier);
+#ifdef CONFIG_RTNETLINK
+ rtnetlink_links[PF_INET] = inet_rtnetlink_table;
+#endif
+#ifdef CONFIG_SYSCTL
+ devinet_sysctl.sysctl_header =
+ register_sysctl_table(devinet_sysctl.devinet_root_dir, 0);
+ devinet_sysctl_register(NULL, &ipv4_devconf_dflt);
+#endif
+}
diff --git a/pfinet/linux-src/net/ipv4/fib_frontend.c b/pfinet/linux-src/net/ipv4/fib_frontend.c
new file mode 100644
index 00000000..ec139eb1
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/fib_frontend.c
@@ -0,0 +1,627 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * IPv4 Forwarding Information Base: FIB frontend.
+ *
+ * Version: $Id: fib_frontend.c,v 1.15 1999/03/21 05:22:31 davem Exp $
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/init.h>
+
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/sock.h>
+#include <net/icmp.h>
+#include <net/arp.h>
+#include <net/ip_fib.h>
+
+#define FFprint(a...) printk(KERN_DEBUG a)
+
+#ifndef CONFIG_IP_MULTIPLE_TABLES
+
+#define RT_TABLE_MIN RT_TABLE_MAIN
+
+struct fib_table *local_table;
+struct fib_table *main_table;
+
+#else
+
+#define RT_TABLE_MIN 1
+
+struct fib_table *fib_tables[RT_TABLE_MAX+1];
+
+struct fib_table *__fib_new_table(int id)
+{
+ struct fib_table *tb;
+
+ tb = fib_hash_init(id);
+ if (!tb)
+ return NULL;
+ fib_tables[id] = tb;
+ return tb;
+}
+
+
+#endif /* CONFIG_IP_MULTIPLE_TABLES */
+
+
+void fib_flush(void)
+{
+ int flushed = 0;
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+ struct fib_table *tb;
+ int id;
+
+ for (id = RT_TABLE_MAX; id>0; id--) {
+ if ((tb = fib_get_table(id))==NULL)
+ continue;
+ flushed += tb->tb_flush(tb);
+ }
+#else /* CONFIG_IP_MULTIPLE_TABLES */
+ flushed += main_table->tb_flush(main_table);
+ flushed += local_table->tb_flush(local_table);
+#endif /* CONFIG_IP_MULTIPLE_TABLES */
+
+ if (flushed)
+ rt_cache_flush(-1);
+}
+
+
+#ifdef CONFIG_PROC_FS
+
+/*
+ * Called from the PROCfs module. This outputs /proc/net/route.
+ *
+ * It always works in backward compatibility mode.
+ * The format of the file is not supposed to be changed.
+ */
+
+static int
+fib_get_procinfo(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int first = offset/128;
+ char *ptr = buffer;
+ int count = (length+127)/128;
+ int len;
+
+ *start = buffer + offset%128;
+
+ if (--first < 0) {
+ sprintf(buffer, "%-127s\n", "Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT");
+ --count;
+ ptr += 128;
+ first = 0;
+ }
+
+ /* rtnl_shlock(); -- it is pointless at the moment --ANK */
+ if (main_table && count > 0) {
+ int n = main_table->tb_get_info(main_table, ptr, first, count);
+ count -= n;
+ ptr += n*128;
+ }
+ /* rtnl_shunlock(); */
+ len = ptr - *start;
+ if (len >= length)
+ return length;
+ if (len >= 0)
+ return len;
+ return 0;
+}
+
+#endif /* CONFIG_PROC_FS */
+
+/*
+ * Find the first device with a given source address.
+ */
+
+struct device * ip_dev_find(u32 addr)
+{
+ struct rt_key key;
+ struct fib_result res;
+
+ memset(&key, 0, sizeof(key));
+ key.dst = addr;
+
+ if (!local_table || local_table->tb_lookup(local_table, &key, &res)
+ || res.type != RTN_LOCAL)
+ return NULL;
+
+ return FIB_RES_DEV(res);
+}
+
+unsigned inet_addr_type(u32 addr)
+{
+ struct rt_key key;
+ struct fib_result res;
+
+ if (ZERONET(addr) || BADCLASS(addr))
+ return RTN_BROADCAST;
+ if (MULTICAST(addr))
+ return RTN_MULTICAST;
+
+ memset(&key, 0, sizeof(key));
+ key.dst = addr;
+
+ if (local_table) {
+ if (local_table->tb_lookup(local_table, &key, &res) == 0)
+ return res.type;
+ return RTN_UNICAST;
+ }
+ return RTN_BROADCAST;
+}
+
+/* Given (packet source, input interface) and optional (dst, oif, tos):
+ - (main) check, that source is valid i.e. not broadcast or our local
+ address.
+ - figure out what "logical" interface this packet arrived
+ and calculate "specific destination" address.
+ - check, that packet arrived from expected physical interface.
+ */
+
+int fib_validate_source(u32 src, u32 dst, u8 tos, int oif,
+ struct device *dev, u32 *spec_dst, u32 *itag)
+{
+ struct in_device *in_dev = dev->ip_ptr;
+ struct rt_key key;
+ struct fib_result res;
+
+ key.dst = src;
+ key.src = dst;
+ key.tos = tos;
+ key.oif = 0;
+ key.iif = oif;
+ key.scope = RT_SCOPE_UNIVERSE;
+
+ if (in_dev == NULL)
+ return -EINVAL;
+ if (fib_lookup(&key, &res))
+ goto last_resort;
+ if (res.type != RTN_UNICAST)
+ return -EINVAL;
+ *spec_dst = FIB_RES_PREFSRC(res);
+ if (itag)
+ fib_combine_itag(itag, &res);
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ if (FIB_RES_DEV(res) == dev || res.fi->fib_nhs > 1)
+#else
+ if (FIB_RES_DEV(res) == dev)
+#endif
+ return FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
+
+ if (in_dev->ifa_list == NULL)
+ goto last_resort;
+ if (IN_DEV_RPFILTER(in_dev))
+ return -EINVAL;
+ key.oif = dev->ifindex;
+ if (fib_lookup(&key, &res) == 0 && res.type == RTN_UNICAST) {
+ *spec_dst = FIB_RES_PREFSRC(res);
+ return FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
+ }
+ return 0;
+
+last_resort:
+ if (IN_DEV_RPFILTER(in_dev))
+ return -EINVAL;
+ *spec_dst = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE);
+ *itag = 0;
+ return 0;
+}
+
+#ifndef CONFIG_IP_NOSIOCRT
+
+/*
+ * Handle IP routing ioctl calls. These are used to manipulate the routing tables
+ */
+
+int ip_rt_ioctl(unsigned int cmd, void *arg)
+{
+ int err;
+ struct kern_rta rta;
+ struct rtentry r;
+ struct {
+ struct nlmsghdr nlh;
+ struct rtmsg rtm;
+ } req;
+
+ switch (cmd) {
+ case SIOCADDRT: /* Add a route */
+ case SIOCDELRT: /* Delete a route */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ if (copy_from_user(&r, arg, sizeof(struct rtentry)))
+ return -EFAULT;
+ rtnl_lock();
+ err = fib_convert_rtentry(cmd, &req.nlh, &req.rtm, &rta, &r);
+ if (err == 0) {
+ if (cmd == SIOCDELRT) {
+ struct fib_table *tb = fib_get_table(req.rtm.rtm_table);
+ err = -ESRCH;
+ if (tb)
+ err = tb->tb_delete(tb, &req.rtm, &rta, &req.nlh, NULL);
+ } else {
+ struct fib_table *tb = fib_new_table(req.rtm.rtm_table);
+ err = -ENOBUFS;
+ if (tb)
+ err = tb->tb_insert(tb, &req.rtm, &rta, &req.nlh, NULL);
+ }
+ if (rta.rta_mx)
+ kfree(rta.rta_mx);
+ }
+ rtnl_unlock();
+ return err;
+ }
+ return -EINVAL;
+}
+
+#else
+
+int ip_rt_ioctl(unsigned int cmd, void *arg)
+{
+ return -EINVAL;
+}
+
+#endif
+
+#ifdef CONFIG_RTNETLINK
+
+static int inet_check_attr(struct rtmsg *r, struct rtattr **rta)
+{
+ int i;
+
+ for (i=1; i<=RTA_MAX; i++) {
+ struct rtattr *attr = rta[i-1];
+ if (attr) {
+ if (RTA_PAYLOAD(attr) < 4)
+ return -EINVAL;
+ if (i != RTA_MULTIPATH && i != RTA_METRICS)
+ rta[i-1] = (struct rtattr*)RTA_DATA(attr);
+ }
+ }
+ return 0;
+}
+
+int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+{
+ struct fib_table * tb;
+ struct rtattr **rta = arg;
+ struct rtmsg *r = NLMSG_DATA(nlh);
+
+ if (inet_check_attr(r, rta))
+ return -EINVAL;
+
+ tb = fib_get_table(r->rtm_table);
+ if (tb)
+ return tb->tb_delete(tb, r, (struct kern_rta*)rta, nlh, &NETLINK_CB(skb));
+ return -ESRCH;
+}
+
+int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+{
+ struct fib_table * tb;
+ struct rtattr **rta = arg;
+ struct rtmsg *r = NLMSG_DATA(nlh);
+
+ if (inet_check_attr(r, rta))
+ return -EINVAL;
+
+ tb = fib_new_table(r->rtm_table);
+ if (tb)
+ return tb->tb_insert(tb, r, (struct kern_rta*)rta, nlh, &NETLINK_CB(skb));
+ return -ENOBUFS;
+}
+
+int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int t;
+ int s_t;
+ struct fib_table *tb;
+
+ if (NLMSG_PAYLOAD(cb->nlh, 0) >= sizeof(struct rtmsg) &&
+ ((struct rtmsg*)NLMSG_DATA(cb->nlh))->rtm_flags&RTM_F_CLONED)
+ return ip_rt_dump(skb, cb);
+
+ s_t = cb->args[0];
+ if (s_t == 0)
+ s_t = cb->args[0] = RT_TABLE_MIN;
+
+ for (t=s_t; t<=RT_TABLE_MAX; t++) {
+ if (t < s_t) continue;
+ if (t > s_t)
+ memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));
+ if ((tb = fib_get_table(t))==NULL)
+ continue;
+ if (tb->tb_dump(tb, skb, cb) < 0)
+ break;
+ }
+
+ cb->args[0] = t;
+
+ return skb->len;
+}
+
+#endif
+
+/* Prepare and feed intra-kernel routing request.
+ Really, it should be netlink message, but :-( netlink
+ can be not configured, so that we feed it directly
+ to fib engine. It is legal, because all events occur
+ only when netlink is already locked.
+ */
+
+static void fib_magic(int cmd, int type, u32 dst, int dst_len, struct in_ifaddr *ifa)
+{
+ struct fib_table * tb;
+ struct {
+ struct nlmsghdr nlh;
+ struct rtmsg rtm;
+ } req;
+ struct kern_rta rta;
+
+ memset(&req.rtm, 0, sizeof(req.rtm));
+ memset(&rta, 0, sizeof(rta));
+
+ if (type == RTN_UNICAST)
+ tb = fib_new_table(RT_TABLE_MAIN);
+ else
+ tb = fib_new_table(RT_TABLE_LOCAL);
+
+ if (tb == NULL)
+ return;
+
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = cmd;
+ req.nlh.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_APPEND;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = 0;
+
+ req.rtm.rtm_dst_len = dst_len;
+ req.rtm.rtm_table = tb->tb_id;
+ req.rtm.rtm_protocol = RTPROT_KERNEL;
+ req.rtm.rtm_scope = (type != RTN_LOCAL ? RT_SCOPE_LINK : RT_SCOPE_HOST);
+ req.rtm.rtm_type = type;
+
+ rta.rta_dst = &dst;
+ rta.rta_prefsrc = &ifa->ifa_local;
+ rta.rta_oif = &ifa->ifa_dev->dev->ifindex;
+
+ if (cmd == RTM_NEWROUTE)
+ tb->tb_insert(tb, &req.rtm, &rta, &req.nlh, NULL);
+ else
+ tb->tb_delete(tb, &req.rtm, &rta, &req.nlh, NULL);
+}
+
+static void fib_add_ifaddr(struct in_ifaddr *ifa)
+{
+ struct in_device *in_dev = ifa->ifa_dev;
+ struct device *dev = in_dev->dev;
+ struct in_ifaddr *prim = ifa;
+ u32 mask = ifa->ifa_mask;
+ u32 addr = ifa->ifa_local;
+ u32 prefix = ifa->ifa_address&mask;
+
+ if (ifa->ifa_flags&IFA_F_SECONDARY) {
+ prim = inet_ifa_byprefix(in_dev, prefix, mask);
+ if (prim == NULL) {
+ printk(KERN_DEBUG "fib_add_ifaddr: bug: prim == NULL\n");
+ return;
+ }
+ }
+
+ fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim);
+
+ if (!(dev->flags&IFF_UP))
+ return;
+
+ /* Add broadcast address, if it is explicitly assigned. */
+ if (ifa->ifa_broadcast && ifa->ifa_broadcast != 0xFFFFFFFF)
+ fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
+
+ if (!ZERONET(prefix) && !(ifa->ifa_flags&IFA_F_SECONDARY) &&
+ (prefix != addr || ifa->ifa_prefixlen < 32)) {
+ fib_magic(RTM_NEWROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL :
+ RTN_UNICAST, prefix, ifa->ifa_prefixlen, prim);
+
+ /* Add network specific broadcasts, when it takes a sense */
+ if (ifa->ifa_prefixlen < 31) {
+ fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix, 32, prim);
+ fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix|~mask, 32, prim);
+ }
+ }
+}
+
+static void fib_del_ifaddr(struct in_ifaddr *ifa)
+{
+ struct in_device *in_dev = ifa->ifa_dev;
+ struct device *dev = in_dev->dev;
+ struct in_ifaddr *ifa1;
+ struct in_ifaddr *prim = ifa;
+ u32 brd = ifa->ifa_address|~ifa->ifa_mask;
+ u32 any = ifa->ifa_address&ifa->ifa_mask;
+#define LOCAL_OK 1
+#define BRD_OK 2
+#define BRD0_OK 4
+#define BRD1_OK 8
+ unsigned ok = 0;
+
+ if (!(ifa->ifa_flags&IFA_F_SECONDARY))
+ fib_magic(RTM_DELROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL :
+ RTN_UNICAST, any, ifa->ifa_prefixlen, prim);
+ else {
+ prim = inet_ifa_byprefix(in_dev, any, ifa->ifa_mask);
+ if (prim == NULL) {
+ printk(KERN_DEBUG "fib_del_ifaddr: bug: prim == NULL\n");
+ return;
+ }
+ }
+
+ /* Deletion is more complicated than add.
+ We should take care of not to delete too much :-)
+
+ Scan address list to be sure that addresses are really gone.
+ */
+
+ for (ifa1 = in_dev->ifa_list; ifa1; ifa1 = ifa1->ifa_next) {
+ if (ifa->ifa_local == ifa1->ifa_local)
+ ok |= LOCAL_OK;
+ if (ifa->ifa_broadcast == ifa1->ifa_broadcast)
+ ok |= BRD_OK;
+ if (brd == ifa1->ifa_broadcast)
+ ok |= BRD1_OK;
+ if (any == ifa1->ifa_broadcast)
+ ok |= BRD0_OK;
+ }
+
+ if (!(ok&BRD_OK))
+ fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
+ if (!(ok&BRD1_OK))
+ fib_magic(RTM_DELROUTE, RTN_BROADCAST, brd, 32, prim);
+ if (!(ok&BRD0_OK))
+ fib_magic(RTM_DELROUTE, RTN_BROADCAST, any, 32, prim);
+ if (!(ok&LOCAL_OK)) {
+ fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 32, prim);
+
+ /* Check, that this local address finally disappeared. */
+ if (inet_addr_type(ifa->ifa_local) != RTN_LOCAL) {
+ /* And the last, but not the least thing.
+ We must flush stray FIB entries.
+
+ First of all, we scan fib_info list searching
+ for stray nexthop entries, then ignite fib_flush.
+ */
+ if (fib_sync_down(ifa->ifa_local, NULL, 0))
+ fib_flush();
+ }
+ }
+#undef LOCAL_OK
+#undef BRD_OK
+#undef BRD0_OK
+#undef BRD1_OK
+}
+
+static void fib_disable_ip(struct device *dev, int force)
+{
+ if (fib_sync_down(0, dev, force))
+ fib_flush();
+ rt_cache_flush(0);
+ arp_ifdown(dev);
+}
+
+static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ struct in_ifaddr *ifa = (struct in_ifaddr*)ptr;
+
+ switch (event) {
+ case NETDEV_UP:
+ fib_add_ifaddr(ifa);
+ rt_cache_flush(-1);
+ break;
+ case NETDEV_DOWN:
+ if (ifa->ifa_dev && ifa->ifa_dev->ifa_list == NULL) {
+ /* Last address was deleted from this interface.
+ Disable IP.
+ */
+ fib_disable_ip(ifa->ifa_dev->dev, 1);
+ } else {
+ fib_del_ifaddr(ifa);
+ rt_cache_flush(-1);
+ }
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static int fib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ struct device *dev = ptr;
+ struct in_device *in_dev = dev->ip_ptr;
+
+ if (!in_dev)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_UP:
+ for_ifa(in_dev) {
+ fib_add_ifaddr(ifa);
+ } endfor_ifa(in_dev);
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ fib_sync_up(dev);
+#endif
+ rt_cache_flush(-1);
+ break;
+ case NETDEV_DOWN:
+ fib_disable_ip(dev, 0);
+ break;
+ case NETDEV_UNREGISTER:
+ fib_disable_ip(dev, 1);
+ break;
+ case NETDEV_CHANGEMTU:
+ case NETDEV_CHANGE:
+ rt_cache_flush(0);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+struct notifier_block fib_inetaddr_notifier = {
+ fib_inetaddr_event,
+ NULL,
+ 0
+};
+
+struct notifier_block fib_netdev_notifier = {
+ fib_netdev_event,
+ NULL,
+ 0
+};
+
+__initfunc(void ip_fib_init(void))
+{
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&(struct proc_dir_entry) {
+ PROC_NET_ROUTE, 5, "route",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ fib_get_procinfo
+ });
+#endif /* CONFIG_PROC_FS */
+
+#ifndef CONFIG_IP_MULTIPLE_TABLES
+ local_table = fib_hash_init(RT_TABLE_LOCAL);
+ main_table = fib_hash_init(RT_TABLE_MAIN);
+#else
+ fib_rules_init();
+#endif
+
+ register_netdevice_notifier(&fib_netdev_notifier);
+ register_inetaddr_notifier(&fib_inetaddr_notifier);
+}
diff --git a/pfinet/linux-src/net/ipv4/fib_hash.c b/pfinet/linux-src/net/ipv4/fib_hash.c
new file mode 100644
index 00000000..e3987eac
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/fib_hash.c
@@ -0,0 +1,885 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * IPv4 FIB: lookup engine and maintenance routines.
+ *
+ * Version: $Id: fib_hash.c,v 1.8 1999/03/25 10:04:17 davem Exp $
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/init.h>
+
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/sock.h>
+#include <net/ip_fib.h>
+
+#define FTprint(a...)
+/*
+ printk(KERN_DEBUG a)
+ */
+
+/*
+ These bizarre types are just to force strict type checking.
+ When I reversed order of bytes and changed to natural mask lengths,
+ I forgot to make fixes in several places. Now I am lazy to return
+ it back.
+ */
+
+typedef struct {
+ u32 datum;
+} fn_key_t;
+
+typedef struct {
+ u32 datum;
+} fn_hash_idx_t;
+
+struct fib_node
+{
+ struct fib_node *fn_next;
+ struct fib_info *fn_info;
+#define FIB_INFO(f) ((f)->fn_info)
+ fn_key_t fn_key;
+ u8 fn_tos;
+ u8 fn_type;
+ u8 fn_scope;
+ u8 fn_state;
+};
+
+#define FN_S_ZOMBIE 1
+#define FN_S_ACCESSED 2
+
+static int fib_hash_zombies;
+
+struct fn_zone
+{
+ struct fn_zone *fz_next; /* Next not empty zone */
+ struct fib_node **fz_hash; /* Hash table pointer */
+ int fz_nent; /* Number of entries */
+
+ int fz_divisor; /* Hash divisor */
+ u32 fz_hashmask; /* (1<<fz_divisor) - 1 */
+#define FZ_HASHMASK(fz) ((fz)->fz_hashmask)
+
+ int fz_order; /* Zone order */
+ u32 fz_mask;
+#define FZ_MASK(fz) ((fz)->fz_mask)
+};
+
+/* NOTE. On fast computers evaluation of fz_hashmask and fz_mask
+ can be cheaper than memory lookup, so that FZ_* macros are used.
+ */
+
+struct fn_hash
+{
+ struct fn_zone *fn_zones[33];
+ struct fn_zone *fn_zone_list;
+};
+
+static __inline__ fn_hash_idx_t fn_hash(fn_key_t key, struct fn_zone *fz)
+{
+ u32 h = ntohl(key.datum)>>(32 - fz->fz_order);
+ h ^= (h>>20);
+ h ^= (h>>10);
+ h ^= (h>>5);
+ h &= FZ_HASHMASK(fz);
+ return *(fn_hash_idx_t*)&h;
+}
+
+#define fz_key_0(key) ((key).datum = 0)
+#define fz_prefix(key,fz) ((key).datum)
+
+static __inline__ fn_key_t fz_key(u32 dst, struct fn_zone *fz)
+{
+ fn_key_t k;
+ k.datum = dst & FZ_MASK(fz);
+ return k;
+}
+
+static __inline__ struct fib_node ** fz_chain_p(fn_key_t key, struct fn_zone *fz)
+{
+ return &fz->fz_hash[fn_hash(key, fz).datum];
+}
+
+static __inline__ struct fib_node * fz_chain(fn_key_t key, struct fn_zone *fz)
+{
+ return fz->fz_hash[fn_hash(key, fz).datum];
+}
+
+extern __inline__ int fn_key_eq(fn_key_t a, fn_key_t b)
+{
+ return a.datum == b.datum;
+}
+
+extern __inline__ int fn_key_leq(fn_key_t a, fn_key_t b)
+{
+ return a.datum <= b.datum;
+}
+
+#define FZ_MAX_DIVISOR 1024
+
+#ifdef CONFIG_IP_ROUTE_LARGE_TABLES
+
+static __inline__ void fn_rebuild_zone(struct fn_zone *fz,
+ struct fib_node **old_ht,
+ int old_divisor)
+{
+ int i;
+ struct fib_node *f, **fp, *next;
+
+ for (i=0; i<old_divisor; i++) {
+ for (f=old_ht[i]; f; f=next) {
+ next = f->fn_next;
+ for (fp = fz_chain_p(f->fn_key, fz);
+ *fp && fn_key_leq((*fp)->fn_key, f->fn_key);
+ fp = &(*fp)->fn_next)
+ /* NONE */;
+ f->fn_next = *fp;
+ *fp = f;
+ }
+ }
+}
+
+static void fn_rehash_zone(struct fn_zone *fz)
+{
+ struct fib_node **ht, **old_ht;
+ int old_divisor, new_divisor;
+ u32 new_hashmask;
+
+ old_divisor = fz->fz_divisor;
+
+ switch (old_divisor) {
+ case 16:
+ new_divisor = 256;
+ new_hashmask = 0xFF;
+ break;
+ case 256:
+ new_divisor = 1024;
+ new_hashmask = 0x3FF;
+ break;
+ default:
+ printk(KERN_CRIT "route.c: bad divisor %d!\n", old_divisor);
+ return;
+ }
+#if RT_CACHE_DEBUG >= 2
+ printk("fn_rehash_zone: hash for zone %d grows from %d\n", fz->fz_order, old_divisor);
+#endif
+
+ ht = kmalloc(new_divisor*sizeof(struct fib_node*), GFP_KERNEL);
+
+ if (ht) {
+ memset(ht, 0, new_divisor*sizeof(struct fib_node*));
+ start_bh_atomic();
+ old_ht = fz->fz_hash;
+ fz->fz_hash = ht;
+ fz->fz_hashmask = new_hashmask;
+ fz->fz_divisor = new_divisor;
+ fn_rebuild_zone(fz, old_ht, old_divisor);
+ end_bh_atomic();
+ kfree(old_ht);
+ }
+}
+#endif /* CONFIG_IP_ROUTE_LARGE_TABLES */
+
+static void fn_free_node(struct fib_node * f)
+{
+ fib_release_info(FIB_INFO(f));
+ kfree_s(f, sizeof(struct fib_node));
+}
+
+
+static struct fn_zone *
+fn_new_zone(struct fn_hash *table, int z)
+{
+ int i;
+ struct fn_zone *fz = kmalloc(sizeof(struct fn_zone), GFP_KERNEL);
+ if (!fz)
+ return NULL;
+
+ memset(fz, 0, sizeof(struct fn_zone));
+ if (z) {
+ fz->fz_divisor = 16;
+ fz->fz_hashmask = 0xF;
+ } else {
+ fz->fz_divisor = 1;
+ fz->fz_hashmask = 0;
+ }
+ fz->fz_hash = kmalloc(fz->fz_divisor*sizeof(struct fib_node*), GFP_KERNEL);
+ if (!fz->fz_hash) {
+ kfree(fz);
+ return NULL;
+ }
+ memset(fz->fz_hash, 0, fz->fz_divisor*sizeof(struct fib_node*));
+ fz->fz_order = z;
+ fz->fz_mask = inet_make_mask(z);
+
+ /* Find the first not empty zone with more specific mask */
+ for (i=z+1; i<=32; i++)
+ if (table->fn_zones[i])
+ break;
+ if (i>32) {
+ /* No more specific masks, we are the first. */
+ fz->fz_next = table->fn_zone_list;
+ table->fn_zone_list = fz;
+ } else {
+ fz->fz_next = table->fn_zones[i]->fz_next;
+ table->fn_zones[i]->fz_next = fz;
+ }
+ table->fn_zones[z] = fz;
+ return fz;
+}
+
+static int
+fn_hash_lookup(struct fib_table *tb, const struct rt_key *key, struct fib_result *res)
+{
+ int err;
+ struct fn_zone *fz;
+ struct fn_hash *t = (struct fn_hash*)tb->tb_data;
+
+ for (fz = t->fn_zone_list; fz; fz = fz->fz_next) {
+ struct fib_node *f;
+ fn_key_t k = fz_key(key->dst, fz);
+
+ for (f = fz_chain(k, fz); f; f = f->fn_next) {
+ if (!fn_key_eq(k, f->fn_key)) {
+ if (fn_key_leq(k, f->fn_key))
+ break;
+ else
+ continue;
+ }
+#ifdef CONFIG_IP_ROUTE_TOS
+ if (f->fn_tos && f->fn_tos != key->tos)
+ continue;
+#endif
+ f->fn_state |= FN_S_ACCESSED;
+
+ if (f->fn_state&FN_S_ZOMBIE)
+ continue;
+ if (f->fn_scope < key->scope)
+ continue;
+
+ err = fib_semantic_match(f->fn_type, FIB_INFO(f), key, res);
+ if (err == 0) {
+ res->type = f->fn_type;
+ res->scope = f->fn_scope;
+ res->prefixlen = fz->fz_order;
+ res->prefix = &fz_prefix(f->fn_key, fz);
+ return 0;
+ }
+ if (err < 0)
+ return err;
+ }
+ }
+ return 1;
+}
+
+static int fn_hash_last_dflt=-1;
+
+static int fib_detect_death(struct fib_info *fi, int order,
+ struct fib_info **last_resort, int *last_idx)
+{
+ struct neighbour *n;
+ int state = NUD_NONE;
+
+ n = neigh_lookup(&arp_tbl, &fi->fib_nh[0].nh_gw, fi->fib_dev);
+ if (n) {
+ state = n->nud_state;
+ neigh_release(n);
+ }
+ if (state==NUD_REACHABLE)
+ return 0;
+ if ((state&NUD_VALID) && order != fn_hash_last_dflt)
+ return 0;
+ if ((state&NUD_VALID) ||
+ (*last_idx<0 && order > fn_hash_last_dflt)) {
+ *last_resort = fi;
+ *last_idx = order;
+ }
+ return 1;
+}
+
+static void
+fn_hash_select_default(struct fib_table *tb, const struct rt_key *key, struct fib_result *res)
+{
+ int order, last_idx;
+ struct fib_node *f;
+ struct fib_info *fi = NULL;
+ struct fib_info *last_resort;
+ struct fn_hash *t = (struct fn_hash*)tb->tb_data;
+ struct fn_zone *fz = t->fn_zones[0];
+
+ if (fz == NULL)
+ return;
+
+ last_idx = -1;
+ last_resort = NULL;
+ order = -1;
+
+ for (f = fz->fz_hash[0]; f; f = f->fn_next) {
+ struct fib_info *next_fi = FIB_INFO(f);
+
+ if ((f->fn_state&FN_S_ZOMBIE) ||
+ f->fn_scope != res->scope ||
+ f->fn_type != RTN_UNICAST)
+ continue;
+
+ if (next_fi->fib_priority > res->fi->fib_priority)
+ break;
+ if (!next_fi->fib_nh[0].nh_gw || next_fi->fib_nh[0].nh_scope != RT_SCOPE_LINK)
+ continue;
+ f->fn_state |= FN_S_ACCESSED;
+
+ if (fi == NULL) {
+ if (next_fi != res->fi)
+ break;
+ } else if (!fib_detect_death(fi, order, &last_resort, &last_idx)) {
+ res->fi = fi;
+ fn_hash_last_dflt = order;
+ return;
+ }
+ fi = next_fi;
+ order++;
+ }
+
+ if (order<=0 || fi==NULL) {
+ fn_hash_last_dflt = -1;
+ return;
+ }
+
+ if (!fib_detect_death(fi, order, &last_resort, &last_idx)) {
+ res->fi = fi;
+ fn_hash_last_dflt = order;
+ return;
+ }
+
+ if (last_idx >= 0)
+ res->fi = last_resort;
+ fn_hash_last_dflt = last_idx;
+}
+
+#define FIB_SCAN(f, fp) \
+for ( ; ((f) = *(fp)) != NULL; (fp) = &(f)->fn_next)
+
+#define FIB_SCAN_KEY(f, fp, key) \
+for ( ; ((f) = *(fp)) != NULL && fn_key_eq((f)->fn_key, (key)); (fp) = &(f)->fn_next)
+
+#ifndef CONFIG_IP_ROUTE_TOS
+#define FIB_SCAN_TOS(f, fp, key, tos) FIB_SCAN_KEY(f, fp, key)
+#else
+#define FIB_SCAN_TOS(f, fp, key, tos) \
+for ( ; ((f) = *(fp)) != NULL && fn_key_eq((f)->fn_key, (key)) && \
+ (f)->fn_tos == (tos) ; (fp) = &(f)->fn_next)
+#endif
+
+
+#ifdef CONFIG_RTNETLINK
+static void rtmsg_fib(int, struct fib_node*, int, int,
+ struct nlmsghdr *n,
+ struct netlink_skb_parms *);
+#else
+#define rtmsg_fib(a, b, c, d, e, f) (void) 0
+#endif
+
+
+static int
+fn_hash_insert(struct fib_table *tb, struct rtmsg *r, struct kern_rta *rta,
+ struct nlmsghdr *n, struct netlink_skb_parms *req)
+{
+ struct fn_hash *table = (struct fn_hash*)tb->tb_data;
+ struct fib_node *new_f, *f, **fp, **del_fp;
+ struct fn_zone *fz;
+ struct fib_info *fi;
+
+ int z = r->rtm_dst_len;
+ int type = r->rtm_type;
+#ifdef CONFIG_IP_ROUTE_TOS
+ u8 tos = r->rtm_tos;
+#endif
+ fn_key_t key;
+ int err;
+
+FTprint("tb(%d)_insert: %d %08x/%d %d %08x\n", tb->tb_id, r->rtm_type, rta->rta_dst ?
+*(u32*)rta->rta_dst : 0, z, rta->rta_oif ? *rta->rta_oif : -1,
+rta->rta_prefsrc ? *(u32*)rta->rta_prefsrc : 0);
+ if (z > 32)
+ return -EINVAL;
+ fz = table->fn_zones[z];
+ if (!fz && !(fz = fn_new_zone(table, z)))
+ return -ENOBUFS;
+
+ fz_key_0(key);
+ if (rta->rta_dst) {
+ u32 dst;
+ memcpy(&dst, rta->rta_dst, 4);
+ if (dst & ~FZ_MASK(fz))
+ return -EINVAL;
+ key = fz_key(dst, fz);
+ }
+
+ if ((fi = fib_create_info(r, rta, n, &err)) == NULL)
+ return err;
+
+#ifdef CONFIG_IP_ROUTE_LARGE_TABLES
+ if (fz->fz_nent > (fz->fz_divisor<<2) &&
+ fz->fz_divisor < FZ_MAX_DIVISOR &&
+ (z==32 || (1<<z) > fz->fz_divisor))
+ fn_rehash_zone(fz);
+#endif
+
+ fp = fz_chain_p(key, fz);
+
+ /*
+ * Scan list to find the first route with the same destination
+ */
+ FIB_SCAN(f, fp) {
+ if (fn_key_leq(key,f->fn_key))
+ break;
+ }
+
+#ifdef CONFIG_IP_ROUTE_TOS
+ /*
+ * Find route with the same destination and tos.
+ */
+ FIB_SCAN_KEY(f, fp, key) {
+ if (f->fn_tos <= tos)
+ break;
+ }
+#endif
+
+ del_fp = NULL;
+
+ if (f && (f->fn_state&FN_S_ZOMBIE) &&
+#ifdef CONFIG_IP_ROUTE_TOS
+ f->fn_tos == tos &&
+#endif
+ fn_key_eq(f->fn_key, key)) {
+ del_fp = fp;
+ fp = &f->fn_next;
+ f = *fp;
+ goto create;
+ }
+
+ FIB_SCAN_TOS(f, fp, key, tos) {
+ if (fi->fib_priority <= FIB_INFO(f)->fib_priority)
+ break;
+ }
+
+ /* Now f==*fp points to the first node with the same
+ keys [prefix,tos,priority], if such key already
+ exists or to the node, before which we will insert new one.
+ */
+
+ if (f &&
+#ifdef CONFIG_IP_ROUTE_TOS
+ f->fn_tos == tos &&
+#endif
+ fn_key_eq(f->fn_key, key) &&
+ fi->fib_priority == FIB_INFO(f)->fib_priority) {
+ struct fib_node **ins_fp;
+
+ err = -EEXIST;
+ if (n->nlmsg_flags&NLM_F_EXCL)
+ goto out;
+
+ if (n->nlmsg_flags&NLM_F_REPLACE) {
+ del_fp = fp;
+ fp = &f->fn_next;
+ f = *fp;
+ goto replace;
+ }
+
+ ins_fp = fp;
+ err = -EEXIST;
+
+ FIB_SCAN_TOS(f, fp, key, tos) {
+ if (fi->fib_priority != FIB_INFO(f)->fib_priority)
+ break;
+ if (f->fn_type == type && f->fn_scope == r->rtm_scope
+ && FIB_INFO(f) == fi)
+ goto out;
+ }
+
+ if (!(n->nlmsg_flags&NLM_F_APPEND)) {
+ fp = ins_fp;
+ f = *fp;
+ }
+ }
+
+create:
+ err = -ENOENT;
+ if (!(n->nlmsg_flags&NLM_F_CREATE))
+ goto out;
+
+replace:
+ err = -ENOBUFS;
+ new_f = (struct fib_node *) kmalloc(sizeof(struct fib_node), GFP_KERNEL);
+ if (new_f == NULL)
+ goto out;
+
+ memset(new_f, 0, sizeof(struct fib_node));
+
+ new_f->fn_key = key;
+#ifdef CONFIG_IP_ROUTE_TOS
+ new_f->fn_tos = tos;
+#endif
+ new_f->fn_type = type;
+ new_f->fn_scope = r->rtm_scope;
+ FIB_INFO(new_f) = fi;
+
+ /*
+ * Insert new entry to the list.
+ */
+
+ new_f->fn_next = f;
+ *fp = new_f;
+ fz->fz_nent++;
+
+ if (del_fp) {
+ f = *del_fp;
+ /* Unlink replaced node */
+ *del_fp = f->fn_next;
+ synchronize_bh();
+
+ if (!(f->fn_state&FN_S_ZOMBIE))
+ rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req);
+ if (f->fn_state&FN_S_ACCESSED)
+ rt_cache_flush(-1);
+ fn_free_node(f);
+ fz->fz_nent--;
+ } else {
+ rt_cache_flush(-1);
+ }
+ rtmsg_fib(RTM_NEWROUTE, new_f, z, tb->tb_id, n, req);
+ return 0;
+
+out:
+ fib_release_info(fi);
+ return err;
+}
+
+
+static int
+fn_hash_delete(struct fib_table *tb, struct rtmsg *r, struct kern_rta *rta,
+ struct nlmsghdr *n, struct netlink_skb_parms *req)
+{
+ struct fn_hash *table = (struct fn_hash*)tb->tb_data;
+ struct fib_node **fp, **del_fp, *f;
+ int z = r->rtm_dst_len;
+ struct fn_zone *fz;
+ fn_key_t key;
+ int matched;
+#ifdef CONFIG_IP_ROUTE_TOS
+ u8 tos = r->rtm_tos;
+#endif
+
+FTprint("tb(%d)_delete: %d %08x/%d %d\n", tb->tb_id, r->rtm_type, rta->rta_dst ?
+ *(u32*)rta->rta_dst : 0, z, rta->rta_oif ? *rta->rta_oif : -1);
+ if (z > 32)
+ return -EINVAL;
+ if ((fz = table->fn_zones[z]) == NULL)
+ return -ESRCH;
+
+ fz_key_0(key);
+ if (rta->rta_dst) {
+ u32 dst;
+ memcpy(&dst, rta->rta_dst, 4);
+ if (dst & ~FZ_MASK(fz))
+ return -EINVAL;
+ key = fz_key(dst, fz);
+ }
+
+ fp = fz_chain_p(key, fz);
+
+ FIB_SCAN(f, fp) {
+ if (fn_key_eq(f->fn_key, key))
+ break;
+ if (fn_key_leq(key, f->fn_key))
+ return -ESRCH;
+ }
+#ifdef CONFIG_IP_ROUTE_TOS
+ FIB_SCAN_KEY(f, fp, key) {
+ if (f->fn_tos == tos)
+ break;
+ }
+#endif
+
+ matched = 0;
+ del_fp = NULL;
+ FIB_SCAN_TOS(f, fp, key, tos) {
+ struct fib_info * fi = FIB_INFO(f);
+
+ if (f->fn_state&FN_S_ZOMBIE)
+ return -ESRCH;
+
+ matched++;
+
+ if (del_fp == NULL &&
+ (!r->rtm_type || f->fn_type == r->rtm_type) &&
+ (r->rtm_scope == RT_SCOPE_NOWHERE || f->fn_scope == r->rtm_scope) &&
+ (!r->rtm_protocol || fi->fib_protocol == r->rtm_protocol) &&
+ fib_nh_match(r, n, rta, fi) == 0)
+ del_fp = fp;
+ }
+
+ if (del_fp) {
+ f = *del_fp;
+ rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req);
+
+ if (matched != 1) {
+ *del_fp = f->fn_next;
+ synchronize_bh();
+
+ if (f->fn_state&FN_S_ACCESSED)
+ rt_cache_flush(-1);
+ fn_free_node(f);
+ fz->fz_nent--;
+ } else {
+ f->fn_state |= FN_S_ZOMBIE;
+ if (f->fn_state&FN_S_ACCESSED) {
+ f->fn_state &= ~FN_S_ACCESSED;
+ rt_cache_flush(-1);
+ }
+ if (++fib_hash_zombies > 128)
+ fib_flush();
+ }
+
+ return 0;
+ }
+ return -ESRCH;
+}
+
+extern __inline__ int
+fn_flush_list(struct fib_node ** fp, int z, struct fn_hash *table)
+{
+ int found = 0;
+ struct fib_node *f;
+
+ while ((f = *fp) != NULL) {
+ struct fib_info *fi = FIB_INFO(f);
+
+ if (fi && ((f->fn_state&FN_S_ZOMBIE) || (fi->fib_flags&RTNH_F_DEAD))) {
+ *fp = f->fn_next;
+ synchronize_bh();
+
+ fn_free_node(f);
+ found++;
+ continue;
+ }
+ fp = &f->fn_next;
+ }
+ return found;
+}
+
+static int fn_hash_flush(struct fib_table *tb)
+{
+ struct fn_hash *table = (struct fn_hash*)tb->tb_data;
+ struct fn_zone *fz;
+ int found = 0;
+
+ fib_hash_zombies = 0;
+ for (fz = table->fn_zone_list; fz; fz = fz->fz_next) {
+ int i;
+ int tmp = 0;
+ for (i=fz->fz_divisor-1; i>=0; i--)
+ tmp += fn_flush_list(&fz->fz_hash[i], fz->fz_order, table);
+ fz->fz_nent -= tmp;
+ found += tmp;
+ }
+ return found;
+}
+
+
+#ifdef CONFIG_PROC_FS
+
+static int fn_hash_get_info(struct fib_table *tb, char *buffer, int first, int count)
+{
+ struct fn_hash *table = (struct fn_hash*)tb->tb_data;
+ struct fn_zone *fz;
+ int pos = 0;
+ int n = 0;
+
+ for (fz=table->fn_zone_list; fz; fz = fz->fz_next) {
+ int i;
+ struct fib_node *f;
+ int maxslot = fz->fz_divisor;
+ struct fib_node **fp = fz->fz_hash;
+
+ if (fz->fz_nent == 0)
+ continue;
+
+ if (pos + fz->fz_nent <= first) {
+ pos += fz->fz_nent;
+ continue;
+ }
+
+ for (i=0; i < maxslot; i++, fp++) {
+ for (f = *fp; f; f = f->fn_next) {
+ if (++pos <= first)
+ continue;
+ fib_node_get_info(f->fn_type,
+ f->fn_state&FN_S_ZOMBIE,
+ FIB_INFO(f),
+ fz_prefix(f->fn_key, fz),
+ FZ_MASK(fz), buffer);
+ buffer += 128;
+ if (++n >= count)
+ return n;
+ }
+ }
+ }
+ return n;
+}
+#endif
+
+
+#ifdef CONFIG_RTNETLINK
+
+extern __inline__ int
+fn_hash_dump_bucket(struct sk_buff *skb, struct netlink_callback *cb,
+ struct fib_table *tb,
+ struct fn_zone *fz,
+ struct fib_node *f)
+{
+ int i, s_i;
+
+ s_i = cb->args[3];
+ for (i=0; f; i++, f=f->fn_next) {
+ if (i < s_i) continue;
+ if (f->fn_state&FN_S_ZOMBIE) continue;
+ if (fib_dump_info(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+ RTM_NEWROUTE,
+ tb->tb_id, (f->fn_state&FN_S_ZOMBIE) ? 0 : f->fn_type, f->fn_scope,
+ &f->fn_key, fz->fz_order, f->fn_tos,
+ f->fn_info) < 0) {
+ cb->args[3] = i;
+ return -1;
+ }
+ }
+ cb->args[3] = i;
+ return skb->len;
+}
+
+extern __inline__ int
+fn_hash_dump_zone(struct sk_buff *skb, struct netlink_callback *cb,
+ struct fib_table *tb,
+ struct fn_zone *fz)
+{
+ int h, s_h;
+
+ s_h = cb->args[2];
+ for (h=0; h < fz->fz_divisor; h++) {
+ if (h < s_h) continue;
+ if (h > s_h)
+ memset(&cb->args[3], 0, sizeof(cb->args) - 3*sizeof(cb->args[0]));
+ if (fz->fz_hash == NULL || fz->fz_hash[h] == NULL)
+ continue;
+ if (fn_hash_dump_bucket(skb, cb, tb, fz, fz->fz_hash[h]) < 0) {
+ cb->args[2] = h;
+ return -1;
+ }
+ }
+ cb->args[2] = h;
+ return skb->len;
+}
+
+static int fn_hash_dump(struct fib_table *tb, struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int m, s_m;
+ struct fn_zone *fz;
+ struct fn_hash *table = (struct fn_hash*)tb->tb_data;
+
+ s_m = cb->args[1];
+ for (fz = table->fn_zone_list, m=0; fz; fz = fz->fz_next, m++) {
+ if (m < s_m) continue;
+ if (m > s_m)
+ memset(&cb->args[2], 0, sizeof(cb->args) - 2*sizeof(cb->args[0]));
+ if (fn_hash_dump_zone(skb, cb, tb, fz) < 0) {
+ cb->args[1] = m;
+ return -1;
+ }
+ }
+ cb->args[1] = m;
+ return skb->len;
+}
+
+static void rtmsg_fib(int event, struct fib_node* f, int z, int tb_id,
+ struct nlmsghdr *n, struct netlink_skb_parms *req)
+{
+ struct sk_buff *skb;
+ u32 pid = req ? req->pid : 0;
+ int size = NLMSG_SPACE(sizeof(struct rtmsg)+256);
+
+ skb = alloc_skb(size, GFP_KERNEL);
+ if (!skb)
+ return;
+
+ if (fib_dump_info(skb, pid, n->nlmsg_seq, event, tb_id,
+ f->fn_type, f->fn_scope, &f->fn_key, z, f->fn_tos,
+ FIB_INFO(f)) < 0) {
+ kfree_skb(skb);
+ return;
+ }
+ NETLINK_CB(skb).dst_groups = RTMGRP_IPV4_ROUTE;
+ if (n->nlmsg_flags&NLM_F_ECHO)
+ atomic_inc(&skb->users);
+ netlink_broadcast(rtnl, skb, pid, RTMGRP_IPV4_ROUTE, GFP_KERNEL);
+ if (n->nlmsg_flags&NLM_F_ECHO)
+ netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT);
+}
+
+#endif /* CONFIG_RTNETLINK */
+
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+struct fib_table * fib_hash_init(int id)
+#else
+__initfunc(struct fib_table * fib_hash_init(int id))
+#endif
+{
+ struct fib_table *tb;
+ tb = kmalloc(sizeof(struct fib_table) + sizeof(struct fn_hash), GFP_KERNEL);
+ if (tb == NULL)
+ return NULL;
+ tb->tb_id = id;
+ tb->tb_lookup = fn_hash_lookup;
+ tb->tb_insert = fn_hash_insert;
+ tb->tb_delete = fn_hash_delete;
+ tb->tb_flush = fn_hash_flush;
+ tb->tb_select_default = fn_hash_select_default;
+#ifdef CONFIG_RTNETLINK
+ tb->tb_dump = fn_hash_dump;
+#endif
+#ifdef CONFIG_PROC_FS
+ tb->tb_get_info = fn_hash_get_info;
+#endif
+ memset(tb->tb_data, 0, sizeof(struct fn_hash));
+ return tb;
+}
diff --git a/pfinet/linux-src/net/ipv4/fib_rules.c b/pfinet/linux-src/net/ipv4/fib_rules.c
new file mode 100644
index 00000000..868c44c3
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/fib_rules.c
@@ -0,0 +1,419 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * IPv4 Forwarding Information Base: policy rules.
+ *
+ * Version: $Id: fib_rules.c,v 1.9 1999/03/25 10:04:23 davem Exp $
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Fixes:
+ * Rani Assaf : local_rule cannot be deleted
+ * Marc Boucher : routing by fwmark
+ */
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/init.h>
+
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/sock.h>
+#include <net/ip_fib.h>
+
+#define FRprintk(a...)
+
+struct fib_rule
+{
+ struct fib_rule *r_next;
+ u32 r_preference;
+ unsigned char r_table;
+ unsigned char r_action;
+ unsigned char r_dst_len;
+ unsigned char r_src_len;
+ u32 r_src;
+ u32 r_srcmask;
+ u32 r_dst;
+ u32 r_dstmask;
+ u32 r_srcmap;
+ u8 r_flags;
+ u8 r_tos;
+#ifdef CONFIG_IP_ROUTE_FWMARK
+ u32 r_fwmark;
+#endif
+ int r_ifindex;
+#ifdef CONFIG_NET_CLS_ROUTE
+ __u32 r_tclassid;
+#endif
+ char r_ifname[IFNAMSIZ];
+};
+
+static struct fib_rule default_rule = { NULL, 0x7FFF, RT_TABLE_DEFAULT, RTN_UNICAST, };
+static struct fib_rule main_rule = { &default_rule, 0x7FFE, RT_TABLE_MAIN, RTN_UNICAST, };
+static struct fib_rule local_rule = { &main_rule, 0, RT_TABLE_LOCAL, RTN_UNICAST, };
+
+static struct fib_rule *fib_rules = &local_rule;
+
+int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+{
+ struct rtattr **rta = arg;
+ struct rtmsg *rtm = NLMSG_DATA(nlh);
+ struct fib_rule *r, **rp;
+
+ for (rp=&fib_rules; (r=*rp) != NULL; rp=&r->r_next) {
+ if ((!rta[RTA_SRC-1] || memcmp(RTA_DATA(rta[RTA_SRC-1]), &r->r_src, 4) == 0) &&
+ rtm->rtm_src_len == r->r_src_len &&
+ rtm->rtm_dst_len == r->r_dst_len &&
+ (!rta[RTA_DST-1] || memcmp(RTA_DATA(rta[RTA_DST-1]), &r->r_dst, 4) == 0) &&
+ rtm->rtm_tos == r->r_tos &&
+#ifdef CONFIG_IP_ROUTE_FWMARK
+ (!rta[RTA_PROTOINFO-1] || memcmp(RTA_DATA(rta[RTA_PROTOINFO-1]), &r->r_fwmark, 4) == 0) &&
+#endif
+ (!rtm->rtm_type || rtm->rtm_type == r->r_action) &&
+ (!rta[RTA_PRIORITY-1] || memcmp(RTA_DATA(rta[RTA_PRIORITY-1]), &r->r_preference, 4) == 0) &&
+ (!rta[RTA_IIF-1] || strcmp(RTA_DATA(rta[RTA_IIF-1]), r->r_ifname) == 0) &&
+ (!rtm->rtm_table || (r && rtm->rtm_table == r->r_table))) {
+ if (r == &local_rule)
+ return -EPERM;
+
+ *rp = r->r_next;
+ synchronize_bh();
+
+ if (r != &default_rule && r != &main_rule)
+ kfree(r);
+ return 0;
+ }
+ }
+ return -ESRCH;
+}
+
+/* Allocate new unique table id */
+
+static struct fib_table *fib_empty_table(void)
+{
+ int id;
+
+ for (id = 1; id <= RT_TABLE_MAX; id++)
+ if (fib_tables[id] == NULL)
+ return __fib_new_table(id);
+ return NULL;
+}
+
+
+int inet_rtm_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+{
+ struct rtattr **rta = arg;
+ struct rtmsg *rtm = NLMSG_DATA(nlh);
+ struct fib_rule *r, *new_r, **rp;
+ unsigned char table_id;
+
+ if (rtm->rtm_src_len > 32 || rtm->rtm_dst_len > 32 ||
+ (rtm->rtm_tos & ~IPTOS_TOS_MASK))
+ return -EINVAL;
+
+ if (rta[RTA_IIF-1] && RTA_PAYLOAD(rta[RTA_IIF-1]) > IFNAMSIZ)
+ return -EINVAL;
+
+ table_id = rtm->rtm_table;
+ if (table_id == RT_TABLE_UNSPEC) {
+ struct fib_table *table;
+ if (rtm->rtm_type == RTN_UNICAST || rtm->rtm_type == RTN_NAT) {
+ if ((table = fib_empty_table()) == NULL)
+ return -ENOBUFS;
+ table_id = table->tb_id;
+ }
+ }
+
+ new_r = kmalloc(sizeof(*new_r), GFP_KERNEL);
+ if (!new_r)
+ return -ENOMEM;
+ memset(new_r, 0, sizeof(*new_r));
+ if (rta[RTA_SRC-1])
+ memcpy(&new_r->r_src, RTA_DATA(rta[RTA_SRC-1]), 4);
+ if (rta[RTA_DST-1])
+ memcpy(&new_r->r_dst, RTA_DATA(rta[RTA_DST-1]), 4);
+ if (rta[RTA_GATEWAY-1])
+ memcpy(&new_r->r_srcmap, RTA_DATA(rta[RTA_GATEWAY-1]), 4);
+ new_r->r_src_len = rtm->rtm_src_len;
+ new_r->r_dst_len = rtm->rtm_dst_len;
+ new_r->r_srcmask = inet_make_mask(rtm->rtm_src_len);
+ new_r->r_dstmask = inet_make_mask(rtm->rtm_dst_len);
+ new_r->r_tos = rtm->rtm_tos;
+#ifdef CONFIG_IP_ROUTE_FWMARK
+ if (rta[RTA_PROTOINFO-1])
+ memcpy(&new_r->r_fwmark, RTA_DATA(rta[RTA_PROTOINFO-1]), 4);
+#endif
+ new_r->r_action = rtm->rtm_type;
+ new_r->r_flags = rtm->rtm_flags;
+ if (rta[RTA_PRIORITY-1])
+ memcpy(&new_r->r_preference, RTA_DATA(rta[RTA_PRIORITY-1]), 4);
+ new_r->r_table = table_id;
+ if (rta[RTA_IIF-1]) {
+ struct device *dev;
+ memcpy(new_r->r_ifname, RTA_DATA(rta[RTA_IIF-1]), IFNAMSIZ);
+ new_r->r_ifname[IFNAMSIZ-1] = 0;
+ new_r->r_ifindex = -1;
+ dev = dev_get(new_r->r_ifname);
+ if (dev)
+ new_r->r_ifindex = dev->ifindex;
+ }
+#ifdef CONFIG_NET_CLS_ROUTE
+ if (rta[RTA_FLOW-1])
+ memcpy(&new_r->r_tclassid, RTA_DATA(rta[RTA_FLOW-1]), 4);
+#endif
+
+ rp = &fib_rules;
+ if (!new_r->r_preference) {
+ r = fib_rules;
+ if (r && (r = r->r_next) != NULL) {
+ rp = &fib_rules->r_next;
+ if (r->r_preference)
+ new_r->r_preference = r->r_preference - 1;
+ }
+ }
+
+ while ( (r = *rp) != NULL ) {
+ if (r->r_preference > new_r->r_preference)
+ break;
+ rp = &r->r_next;
+ }
+
+ new_r->r_next = r;
+ *rp = new_r;
+ return 0;
+}
+
+u32 fib_rules_map_destination(u32 daddr, struct fib_result *res)
+{
+ u32 mask = inet_make_mask(res->prefixlen);
+ return (daddr&~mask)|res->fi->fib_nh->nh_gw;
+}
+
+u32 fib_rules_policy(u32 saddr, struct fib_result *res, unsigned *flags)
+{
+ struct fib_rule *r = res->r;
+
+ if (r->r_action == RTN_NAT) {
+ int addrtype = inet_addr_type(r->r_srcmap);
+
+ if (addrtype == RTN_NAT) {
+ /* Packet is from translated source; remember it */
+ saddr = (saddr&~r->r_srcmask)|r->r_srcmap;
+ *flags |= RTCF_SNAT;
+ } else if (addrtype == RTN_LOCAL || r->r_srcmap == 0) {
+ /* Packet is from masqueraded source; remember it */
+ saddr = r->r_srcmap;
+ *flags |= RTCF_MASQ;
+ }
+ }
+ return saddr;
+}
+
+#ifdef CONFIG_NET_CLS_ROUTE
+u32 fib_rules_tclass(struct fib_result *res)
+{
+ if (res->r)
+ return res->r->r_tclassid;
+ return 0;
+}
+#endif
+
+
+static void fib_rules_detach(struct device *dev)
+{
+ struct fib_rule *r;
+
+ for (r=fib_rules; r; r=r->r_next) {
+ if (r->r_ifindex == dev->ifindex)
+ r->r_ifindex = -1;
+ }
+}
+
+static void fib_rules_attach(struct device *dev)
+{
+ struct fib_rule *r;
+
+ for (r=fib_rules; r; r=r->r_next) {
+ if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0)
+ r->r_ifindex = dev->ifindex;
+ }
+}
+
+int fib_lookup(const struct rt_key *key, struct fib_result *res)
+{
+ int err;
+ struct fib_rule *r, *policy;
+ struct fib_table *tb;
+
+ u32 daddr = key->dst;
+ u32 saddr = key->src;
+
+FRprintk("Lookup: %08x <- %08x ", key->dst, key->src);
+ for (r = fib_rules; r; r=r->r_next) {
+ if (((saddr^r->r_src) & r->r_srcmask) ||
+ ((daddr^r->r_dst) & r->r_dstmask) ||
+#ifdef CONFIG_IP_ROUTE_TOS
+ (r->r_tos && r->r_tos != key->tos) ||
+#endif
+#ifdef CONFIG_IP_ROUTE_FWMARK
+ (r->r_fwmark && r->r_fwmark != key->fwmark) ||
+#endif
+ (r->r_ifindex && r->r_ifindex != key->iif))
+ continue;
+
+FRprintk("tb %d r %d ", r->r_table, r->r_action);
+ switch (r->r_action) {
+ case RTN_UNICAST:
+ case RTN_NAT:
+ policy = r;
+ break;
+ case RTN_UNREACHABLE:
+ return -ENETUNREACH;
+ default:
+ case RTN_BLACKHOLE:
+ return -EINVAL;
+ case RTN_PROHIBIT:
+ return -EACCES;
+ }
+
+ if ((tb = fib_get_table(r->r_table)) == NULL)
+ continue;
+ err = tb->tb_lookup(tb, key, res);
+ if (err == 0) {
+FRprintk("ok\n");
+ res->r = policy;
+ return 0;
+ }
+ if (err < 0 && err != -EAGAIN)
+ return err;
+ }
+FRprintk("FAILURE\n");
+ return -ENETUNREACH;
+}
+
+void fib_select_default(const struct rt_key *key, struct fib_result *res)
+{
+ if (res->r && res->r->r_action == RTN_UNICAST &&
+ FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) {
+ struct fib_table *tb;
+ if ((tb = fib_get_table(res->r->r_table)) != NULL)
+ tb->tb_select_default(tb, key, res);
+ }
+}
+
+static int fib_rules_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ struct device *dev = ptr;
+
+ if (event == NETDEV_UNREGISTER)
+ fib_rules_detach(dev);
+ else if (event == NETDEV_REGISTER)
+ fib_rules_attach(dev);
+ return NOTIFY_DONE;
+}
+
+
+struct notifier_block fib_rules_notifier = {
+ fib_rules_event,
+ NULL,
+ 0
+};
+
+#ifdef CONFIG_RTNETLINK
+
+extern __inline__ int inet_fill_rule(struct sk_buff *skb,
+ struct fib_rule *r,
+ struct netlink_callback *cb)
+{
+ struct rtmsg *rtm;
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+
+ nlh = NLMSG_PUT(skb, NETLINK_CREDS(cb->skb)->pid, cb->nlh->nlmsg_seq, RTM_NEWRULE, sizeof(*rtm));
+ rtm = NLMSG_DATA(nlh);
+ rtm->rtm_family = AF_INET;
+ rtm->rtm_dst_len = r->r_dst_len;
+ rtm->rtm_src_len = r->r_src_len;
+ rtm->rtm_tos = r->r_tos;
+#ifdef CONFIG_IP_ROUTE_FWMARK
+ if (r->r_fwmark)
+ RTA_PUT(skb, RTA_PROTOINFO, 4, &r->r_fwmark);
+#endif
+ rtm->rtm_table = r->r_table;
+ rtm->rtm_protocol = 0;
+ rtm->rtm_scope = 0;
+ rtm->rtm_type = r->r_action;
+ rtm->rtm_flags = r->r_flags;
+
+ if (r->r_dst_len)
+ RTA_PUT(skb, RTA_DST, 4, &r->r_dst);
+ if (r->r_src_len)
+ RTA_PUT(skb, RTA_SRC, 4, &r->r_src);
+ if (r->r_ifname[0])
+ RTA_PUT(skb, RTA_IIF, IFNAMSIZ, &r->r_ifname);
+ if (r->r_preference)
+ RTA_PUT(skb, RTA_PRIORITY, 4, &r->r_preference);
+ if (r->r_srcmap)
+ RTA_PUT(skb, RTA_GATEWAY, 4, &r->r_srcmap);
+#ifdef CONFIG_NET_CLS_ROUTE
+ if (r->r_tclassid)
+ RTA_PUT(skb, RTA_FLOW, 4, &r->r_tclassid);
+#endif
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+ skb_put(skb, b - skb->tail);
+ return -1;
+}
+
+int inet_dump_rules(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int idx;
+ int s_idx = cb->args[0];
+ struct fib_rule *r;
+
+ for (r=fib_rules, idx=0; r; r = r->r_next, idx++) {
+ if (idx < s_idx)
+ continue;
+ if (inet_fill_rule(skb, r, cb) < 0)
+ break;
+ }
+ cb->args[0] = idx;
+
+ return skb->len;
+}
+
+#endif /* CONFIG_RTNETLINK */
+
+__initfunc(void fib_rules_init(void))
+{
+ register_netdevice_notifier(&fib_rules_notifier);
+}
diff --git a/pfinet/linux-src/net/ipv4/fib_semantics.c b/pfinet/linux-src/net/ipv4/fib_semantics.c
new file mode 100644
index 00000000..b7edb29b
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/fib_semantics.c
@@ -0,0 +1,991 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * IPv4 Forwarding Information Base: semantics.
+ *
+ * Version: $Id: fib_semantics.c,v 1.13 1999/03/21 05:22:34 davem Exp $
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/init.h>
+
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/sock.h>
+#include <net/ip_fib.h>
+
+#define FSprintk(a...)
+
+static struct fib_info *fib_info_list;
+
+#define for_fib_info() { struct fib_info *fi; \
+ for (fi = fib_info_list; fi; fi = fi->fib_next)
+
+#define endfor_fib_info() }
+
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+
+#define for_nexthops(fi) { int nhsel; const struct fib_nh * nh; \
+for (nhsel=0, nh = (fi)->fib_nh; nhsel < (fi)->fib_nhs; nh++, nhsel++)
+
+#define change_nexthops(fi) { int nhsel; struct fib_nh * nh; \
+for (nhsel=0, nh = (struct fib_nh*)((fi)->fib_nh); nhsel < (fi)->fib_nhs; nh++, nhsel++)
+
+#else /* CONFIG_IP_ROUTE_MULTIPATH */
+
+/* Hope, that gcc will optimize it to get rid of dummy loop */
+
+#define for_nexthops(fi) { int nhsel=0; const struct fib_nh * nh = (fi)->fib_nh; \
+for (nhsel=0; nhsel < 1; nhsel++)
+
+#define change_nexthops(fi) { int nhsel=0; struct fib_nh * nh = (struct fib_nh*)((fi)->fib_nh); \
+for (nhsel=0; nhsel < 1; nhsel++)
+
+#endif /* CONFIG_IP_ROUTE_MULTIPATH */
+
+#define endfor_nexthops(fi) }
+
+
+static struct
+{
+ int error;
+ u8 scope;
+} fib_props[RTA_MAX+1] = {
+ { 0, RT_SCOPE_NOWHERE}, /* RTN_UNSPEC */
+ { 0, RT_SCOPE_UNIVERSE}, /* RTN_UNICAST */
+ { 0, RT_SCOPE_HOST}, /* RTN_LOCAL */
+ { 0, RT_SCOPE_LINK}, /* RTN_BROADCAST */
+ { 0, RT_SCOPE_LINK}, /* RTN_ANYCAST */
+ { 0, RT_SCOPE_UNIVERSE}, /* RTN_MULTICAST */
+ { -EINVAL, RT_SCOPE_UNIVERSE}, /* RTN_BLACKHOLE */
+ { -EHOSTUNREACH, RT_SCOPE_UNIVERSE},/* RTN_UNREACHABLE */
+ { -EACCES, RT_SCOPE_UNIVERSE}, /* RTN_PROHIBIT */
+ { -EAGAIN, RT_SCOPE_UNIVERSE}, /* RTN_THROW */
+#ifdef CONFIG_IP_ROUTE_NAT
+ { 0, RT_SCOPE_HOST}, /* RTN_NAT */
+#else
+ { -EINVAL, RT_SCOPE_NOWHERE}, /* RTN_NAT */
+#endif
+ { -EINVAL, RT_SCOPE_NOWHERE} /* RTN_XRESOLVE */
+};
+
+/* Release a nexthop info record */
+
+void fib_release_info(struct fib_info *fi)
+{
+ if (fi && !--fi->fib_refcnt) {
+ if (fi->fib_next)
+ fi->fib_next->fib_prev = fi->fib_prev;
+ if (fi->fib_prev)
+ fi->fib_prev->fib_next = fi->fib_next;
+ if (fi == fib_info_list)
+ fib_info_list = fi->fib_next;
+ kfree(fi);
+ }
+}
+
+extern __inline__ int nh_comp(const struct fib_info *fi, const struct fib_info *ofi)
+{
+ const struct fib_nh *onh = ofi->fib_nh;
+
+ for_nexthops(fi) {
+ if (nh->nh_oif != onh->nh_oif ||
+ nh->nh_gw != onh->nh_gw ||
+ nh->nh_scope != onh->nh_scope ||
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ nh->nh_weight != onh->nh_weight ||
+#endif
+#ifdef CONFIG_NET_CLS_ROUTE
+ nh->nh_tclassid != onh->nh_tclassid ||
+#endif
+ ((nh->nh_flags^onh->nh_flags)&~RTNH_F_DEAD))
+ return -1;
+ onh++;
+ } endfor_nexthops(fi);
+ return 0;
+}
+
+extern __inline__ struct fib_info * fib_find_info(const struct fib_info *nfi)
+{
+ for_fib_info() {
+ if (fi->fib_nhs != nfi->fib_nhs)
+ continue;
+ if (nfi->fib_protocol == fi->fib_protocol &&
+ nfi->fib_prefsrc == fi->fib_prefsrc &&
+ nfi->fib_priority == fi->fib_priority &&
+ nfi->fib_mtu == fi->fib_mtu &&
+ nfi->fib_rtt == fi->fib_rtt &&
+ nfi->fib_window == fi->fib_window &&
+ ((nfi->fib_flags^fi->fib_flags)&~RTNH_F_DEAD) == 0 &&
+ (nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0))
+ return fi;
+ } endfor_fib_info();
+ return NULL;
+}
+
+/* Check, that the gateway is already configured.
+ Used only by redirect accept routine.
+ */
+
+int ip_fib_check_default(u32 gw, struct device *dev)
+{
+ for_fib_info() {
+ if (fi->fib_flags & RTNH_F_DEAD)
+ continue;
+ for_nexthops(fi) {
+ if (nh->nh_dev == dev && nh->nh_gw == gw &&
+ !(nh->nh_flags&RTNH_F_DEAD))
+ return 0;
+ } endfor_nexthops(fi);
+ } endfor_fib_info();
+ return -1;
+}
+
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+
+static u32 fib_get_attr32(struct rtattr *attr, int attrlen, int type)
+{
+ while (RTA_OK(attr,attrlen)) {
+ if (attr->rta_type == type)
+ return *(u32*)RTA_DATA(attr);
+ attr = RTA_NEXT(attr, attrlen);
+ }
+ return 0;
+}
+
+static int
+fib_count_nexthops(struct rtattr *rta)
+{
+ int nhs = 0;
+ struct rtnexthop *nhp = RTA_DATA(rta);
+ int nhlen = RTA_PAYLOAD(rta);
+
+ while (nhlen >= (int)sizeof(struct rtnexthop)) {
+ if ((nhlen -= nhp->rtnh_len) < 0)
+ return 0;
+ nhs++;
+ nhp = RTNH_NEXT(nhp);
+ };
+ return nhs;
+}
+
+static int
+fib_get_nhs(struct fib_info *fi, const struct rtattr *rta, const struct rtmsg *r)
+{
+ struct rtnexthop *nhp = RTA_DATA(rta);
+ int nhlen = RTA_PAYLOAD(rta);
+
+ change_nexthops(fi) {
+ int attrlen = nhlen - sizeof(struct rtnexthop);
+ if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0)
+ return -EINVAL;
+ nh->nh_flags = (r->rtm_flags&~0xFF) | nhp->rtnh_flags;
+ nh->nh_oif = nhp->rtnh_ifindex;
+ nh->nh_weight = nhp->rtnh_hops + 1;
+ if (attrlen) {
+ nh->nh_gw = fib_get_attr32(RTNH_DATA(nhp), attrlen, RTA_GATEWAY);
+#ifdef CONFIG_NET_CLS_ROUTE
+ nh->nh_tclassid = fib_get_attr32(RTNH_DATA(nhp), attrlen, RTA_FLOW);
+#endif
+ }
+ nhp = RTNH_NEXT(nhp);
+ } endfor_nexthops(fi);
+ return 0;
+}
+
+#endif
+
+int fib_nh_match(struct rtmsg *r, struct nlmsghdr *nlh, struct kern_rta *rta,
+ struct fib_info *fi)
+{
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ struct rtnexthop *nhp;
+ int nhlen;
+#endif
+
+ if (rta->rta_priority &&
+ *rta->rta_priority != fi->fib_priority)
+ return 1;
+
+ if (rta->rta_oif || rta->rta_gw) {
+ if ((!rta->rta_oif || *rta->rta_oif == fi->fib_nh->nh_oif) &&
+ (!rta->rta_gw || memcmp(rta->rta_gw, &fi->fib_nh->nh_gw, 4) == 0))
+ return 0;
+ return 1;
+ }
+
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ if (rta->rta_mp == NULL)
+ return 0;
+ nhp = RTA_DATA(rta->rta_mp);
+ nhlen = RTA_PAYLOAD(rta->rta_mp);
+
+ for_nexthops(fi) {
+ int attrlen = nhlen - sizeof(struct rtnexthop);
+ u32 gw;
+
+ if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0)
+ return -EINVAL;
+ if (nhp->rtnh_ifindex && nhp->rtnh_ifindex != nh->nh_oif)
+ return 1;
+ if (attrlen) {
+ gw = fib_get_attr32(RTNH_DATA(nhp), attrlen, RTA_GATEWAY);
+ if (gw && gw != nh->nh_gw)
+ return 1;
+#ifdef CONFIG_NET_CLS_ROUTE
+ gw = fib_get_attr32(RTNH_DATA(nhp), attrlen, RTA_FLOW);
+ if (gw && gw != nh->nh_tclassid)
+ return 1;
+#endif
+ }
+ nhp = RTNH_NEXT(nhp);
+ } endfor_nexthops(fi);
+#endif
+ return 0;
+}
+
+
+/*
+ Picture
+ -------
+
+ Semantics of nexthop is very messy by historical reasons.
+ We have to take into account, that:
+ a) gateway can be actually local interface address,
+ so that gatewayed route is direct.
+ b) gateway must be on-link address, possibly
+ described not by an ifaddr, but also by a direct route.
+ c) If both gateway and interface are specified, they should not
+ contradict.
+ d) If we use tunnel routes, gateway could be not on-link.
+
+ Attempt to reconcile all of these (alas, self-contradictory) conditions
+ results in pretty ugly and hairy code with obscure logic.
+
+ I chose to generalize it instead, so that the size
+ of code does not increase practically, but it becomes
+ much more general.
+ Every prefix is assigned a "scope" value: "host" is local address,
+ "link" is direct route,
+ [ ... "site" ... "interior" ... ]
+ and "universe" is true gateway route with global meaning.
+
+ Every prefix refers to a set of "nexthop"s (gw, oif),
+ where gw must have narrower scope. This recursion stops
+ when gw has LOCAL scope or if "nexthop" is declared ONLINK,
+ which means that gw is forced to be on link.
+
+ Code is still hairy, but now it is apparently logically
+ consistent and very flexible. F.e. as by-product it allows
+ to co-exists in peace independent exterior and interior
+ routing processes.
+
+ Normally it looks as following.
+
+ {universe prefix} -> (gw, oif) [scope link]
+ |
+ |-> {link prefix} -> (gw, oif) [scope local]
+ |
+ |-> {local prefix} (terminal node)
+ */
+
+static int fib_check_nh(const struct rtmsg *r, struct fib_info *fi, struct fib_nh *nh)
+{
+ int err;
+
+ if (nh->nh_gw) {
+ struct rt_key key;
+ struct fib_result res;
+
+#ifdef CONFIG_IP_ROUTE_PERVASIVE
+ if (nh->nh_flags&RTNH_F_PERVASIVE)
+ return 0;
+#endif
+ if (nh->nh_flags&RTNH_F_ONLINK) {
+ struct device *dev;
+
+ if (r->rtm_scope >= RT_SCOPE_LINK)
+ return -EINVAL;
+ if (inet_addr_type(nh->nh_gw) != RTN_UNICAST)
+ return -EINVAL;
+ if ((dev = dev_get_by_index(nh->nh_oif)) == NULL)
+ return -ENODEV;
+ if (!(dev->flags&IFF_UP))
+ return -ENETDOWN;
+ nh->nh_dev = dev;
+ nh->nh_scope = RT_SCOPE_LINK;
+ return 0;
+ }
+ memset(&key, 0, sizeof(key));
+ key.dst = nh->nh_gw;
+ key.oif = nh->nh_oif;
+ key.scope = r->rtm_scope + 1;
+
+ /* It is not necessary, but requires a bit of thinking */
+ if (key.scope < RT_SCOPE_LINK)
+ key.scope = RT_SCOPE_LINK;
+
+ if ((err = fib_lookup(&key, &res)) != 0)
+ return err;
+ nh->nh_scope = res.scope;
+ nh->nh_oif = FIB_RES_OIF(res);
+ nh->nh_dev = FIB_RES_DEV(res);
+ } else {
+ struct in_device *in_dev;
+
+ if (nh->nh_flags&(RTNH_F_PERVASIVE|RTNH_F_ONLINK))
+ return -EINVAL;
+
+ in_dev = inetdev_by_index(nh->nh_oif);
+ if (in_dev == NULL)
+ return -ENODEV;
+ if (!(in_dev->dev->flags&IFF_UP))
+ return -ENETDOWN;
+ nh->nh_dev = in_dev->dev;
+ nh->nh_scope = RT_SCOPE_HOST;
+ }
+ return 0;
+}
+
+struct fib_info *
+fib_create_info(const struct rtmsg *r, struct kern_rta *rta,
+ const struct nlmsghdr *nlh, int *errp)
+{
+ int err;
+ struct fib_info *fi = NULL;
+ struct fib_info *ofi;
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ int nhs = 1;
+#else
+ const int nhs = 1;
+#endif
+
+ /* Fast check to catch the most weird cases */
+ if (fib_props[r->rtm_type].scope > r->rtm_scope)
+ goto err_inval;
+
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ if (rta->rta_mp) {
+ nhs = fib_count_nexthops(rta->rta_mp);
+ if (nhs == 0)
+ goto err_inval;
+ }
+#endif
+
+ fi = kmalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
+ err = -ENOBUFS;
+ if (fi == NULL)
+ goto failure;
+ memset(fi, 0, sizeof(*fi)+nhs*sizeof(struct fib_nh));
+
+ fi->fib_protocol = r->rtm_protocol;
+ fi->fib_nhs = nhs;
+ fi->fib_flags = r->rtm_flags;
+ if (rta->rta_priority)
+ fi->fib_priority = *rta->rta_priority;
+ if (rta->rta_mx) {
+ int attrlen = RTA_PAYLOAD(rta->rta_mx);
+ struct rtattr *attr = RTA_DATA(rta->rta_mx);
+
+ while (RTA_OK(attr, attrlen)) {
+ unsigned flavor = attr->rta_type;
+ if (flavor) {
+ if (flavor > FIB_MAX_METRICS)
+ goto err_inval;
+ fi->fib_metrics[flavor-1] = *(unsigned*)RTA_DATA(attr);
+ }
+ attr = RTA_NEXT(attr, attrlen);
+ }
+ }
+ if (rta->rta_prefsrc)
+ memcpy(&fi->fib_prefsrc, rta->rta_prefsrc, 4);
+
+ if (rta->rta_mp) {
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ if ((err = fib_get_nhs(fi, rta->rta_mp, r)) != 0)
+ goto failure;
+ if (rta->rta_oif && fi->fib_nh->nh_oif != *rta->rta_oif)
+ goto err_inval;
+ if (rta->rta_gw && memcmp(&fi->fib_nh->nh_gw, rta->rta_gw, 4))
+ goto err_inval;
+#ifdef CONFIG_NET_CLS_ROUTE
+ if (rta->rta_flow && memcmp(&fi->fib_nh->nh_tclassid, rta->rta_flow, 4))
+ goto err_inval;
+#endif
+#else
+ goto err_inval;
+#endif
+ } else {
+ struct fib_nh *nh = fi->fib_nh;
+ if (rta->rta_oif)
+ nh->nh_oif = *rta->rta_oif;
+ if (rta->rta_gw)
+ memcpy(&nh->nh_gw, rta->rta_gw, 4);
+#ifdef CONFIG_NET_CLS_ROUTE
+ if (rta->rta_flow)
+ memcpy(&nh->nh_tclassid, rta->rta_flow, 4);
+#endif
+ nh->nh_flags = r->rtm_flags;
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ nh->nh_weight = 1;
+#endif
+ }
+
+#ifdef CONFIG_IP_ROUTE_NAT
+ if (r->rtm_type == RTN_NAT) {
+ if (rta->rta_gw == NULL || nhs != 1 || rta->rta_oif)
+ goto err_inval;
+ memcpy(&fi->fib_nh->nh_gw, rta->rta_gw, 4);
+ goto link_it;
+ }
+#endif
+
+ if (fib_props[r->rtm_type].error) {
+ if (rta->rta_gw || rta->rta_oif || rta->rta_mp)
+ goto err_inval;
+ goto link_it;
+ }
+
+ if (r->rtm_scope > RT_SCOPE_HOST)
+ goto err_inval;
+
+ if (r->rtm_scope == RT_SCOPE_HOST) {
+ struct fib_nh *nh = fi->fib_nh;
+
+ /* Local address is added. */
+ if (nhs != 1 || nh->nh_gw)
+ goto err_inval;
+ nh->nh_scope = RT_SCOPE_NOWHERE;
+ nh->nh_dev = dev_get_by_index(fi->fib_nh->nh_oif);
+ err = -ENODEV;
+ if (nh->nh_dev == NULL)
+ goto failure;
+ } else {
+ change_nexthops(fi) {
+ if ((err = fib_check_nh(r, fi, nh)) != 0)
+ goto failure;
+ } endfor_nexthops(fi)
+ }
+
+ if (fi->fib_prefsrc) {
+ if (r->rtm_type != RTN_LOCAL || rta->rta_dst == NULL ||
+ memcmp(&fi->fib_prefsrc, rta->rta_dst, 4))
+ if (inet_addr_type(fi->fib_prefsrc) != RTN_LOCAL)
+ goto err_inval;
+ }
+
+link_it:
+ if ((ofi = fib_find_info(fi)) != NULL) {
+ kfree(fi);
+ ofi->fib_refcnt++;
+ return ofi;
+ }
+
+ fi->fib_refcnt++;
+ fi->fib_next = fib_info_list;
+ fi->fib_prev = NULL;
+ if (fib_info_list)
+ fib_info_list->fib_prev = fi;
+ fib_info_list = fi;
+ return fi;
+
+err_inval:
+ err = -EINVAL;
+
+failure:
+ *errp = err;
+ if (fi)
+ kfree(fi);
+ return NULL;
+}
+
+int
+fib_semantic_match(int type, struct fib_info *fi, const struct rt_key *key, struct fib_result *res)
+{
+ int err = fib_props[type].error;
+
+ if (err == 0) {
+ if (fi->fib_flags&RTNH_F_DEAD)
+ return 1;
+
+ res->fi = fi;
+
+ switch (type) {
+#ifdef CONFIG_IP_ROUTE_NAT
+ case RTN_NAT:
+ FIB_RES_RESET(*res);
+ return 0;
+#endif
+ case RTN_UNICAST:
+ case RTN_LOCAL:
+ case RTN_BROADCAST:
+ case RTN_ANYCAST:
+ case RTN_MULTICAST:
+ for_nexthops(fi) {
+ if (nh->nh_flags&RTNH_F_DEAD)
+ continue;
+ if (!key->oif || key->oif == nh->nh_oif)
+ break;
+ }
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ if (nhsel < fi->fib_nhs) {
+ res->nh_sel = nhsel;
+ return 0;
+ }
+#else
+ if (nhsel < 1)
+ return 0;
+#endif
+ endfor_nexthops(fi);
+ return 1;
+ default:
+ printk(KERN_DEBUG "impossible 102\n");
+ return -EINVAL;
+ }
+ }
+ return err;
+}
+
+/* Find appropriate source address to this destination */
+
+u32 __fib_res_prefsrc(struct fib_result *res)
+{
+ return inet_select_addr(FIB_RES_DEV(*res), FIB_RES_GW(*res), res->scope);
+}
+
+#ifdef CONFIG_RTNETLINK
+
+int
+fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
+ u8 tb_id, u8 type, u8 scope, void *dst, int dst_len, u8 tos,
+ struct fib_info *fi)
+{
+ struct rtmsg *rtm;
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+
+ nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*rtm));
+ rtm = NLMSG_DATA(nlh);
+ rtm->rtm_family = AF_INET;
+ rtm->rtm_dst_len = dst_len;
+ rtm->rtm_src_len = 0;
+ rtm->rtm_tos = tos;
+ rtm->rtm_table = tb_id;
+ rtm->rtm_type = type;
+ rtm->rtm_flags = fi->fib_flags;
+ rtm->rtm_scope = scope;
+ if (rtm->rtm_dst_len)
+ RTA_PUT(skb, RTA_DST, 4, dst);
+ rtm->rtm_protocol = fi->fib_protocol;
+ if (fi->fib_priority)
+ RTA_PUT(skb, RTA_PRIORITY, 4, &fi->fib_priority);
+#ifdef CONFIG_NET_CLS_ROUTE
+ if (fi->fib_nh[0].nh_tclassid)
+ RTA_PUT(skb, RTA_FLOW, 4, &fi->fib_nh[0].nh_tclassid);
+#endif
+ if (fi->fib_mtu || fi->fib_window || fi->fib_rtt) {
+ int i;
+ struct rtattr *mx = (struct rtattr *)skb->tail;
+ RTA_PUT(skb, RTA_METRICS, 0, NULL);
+ for (i=0; i<FIB_MAX_METRICS; i++) {
+ if (fi->fib_metrics[i])
+ RTA_PUT(skb, i+1, sizeof(unsigned), fi->fib_metrics + i);
+ }
+ mx->rta_len = skb->tail - (u8*)mx;
+ }
+ if (fi->fib_prefsrc)
+ RTA_PUT(skb, RTA_PREFSRC, 4, &fi->fib_prefsrc);
+ if (fi->fib_nhs == 1) {
+ if (fi->fib_nh->nh_gw)
+ RTA_PUT(skb, RTA_GATEWAY, 4, &fi->fib_nh->nh_gw);
+ if (fi->fib_nh->nh_oif)
+ RTA_PUT(skb, RTA_OIF, sizeof(int), &fi->fib_nh->nh_oif);
+ }
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ if (fi->fib_nhs > 1) {
+ struct rtnexthop *nhp;
+ struct rtattr *mp_head;
+ if (skb_tailroom(skb) <= RTA_SPACE(0))
+ goto rtattr_failure;
+ mp_head = (struct rtattr*)skb_put(skb, RTA_SPACE(0));
+
+ for_nexthops(fi) {
+ if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
+ goto rtattr_failure;
+ nhp = (struct rtnexthop*)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
+ nhp->rtnh_flags = nh->nh_flags & 0xFF;
+ nhp->rtnh_hops = nh->nh_weight-1;
+ nhp->rtnh_ifindex = nh->nh_oif;
+ if (nh->nh_gw)
+ RTA_PUT(skb, RTA_GATEWAY, 4, &nh->nh_gw);
+ nhp->rtnh_len = skb->tail - (unsigned char*)nhp;
+ } endfor_nexthops(fi);
+ mp_head->rta_type = RTA_MULTIPATH;
+ mp_head->rta_len = skb->tail - (u8*)mp_head;
+ }
+#endif
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+#endif /* CONFIG_RTNETLINK */
+
+#ifndef CONFIG_IP_NOSIOCRT
+
+int
+fib_convert_rtentry(int cmd, struct nlmsghdr *nl, struct rtmsg *rtm,
+ struct kern_rta *rta, struct rtentry *r)
+{
+ int plen;
+ u32 *ptr;
+
+ memset(rtm, 0, sizeof(*rtm));
+ memset(rta, 0, sizeof(*rta));
+
+ if (r->rt_dst.sa_family != AF_INET)
+ return -EAFNOSUPPORT;
+
+ /* Check mask for validity:
+ a) it must be contiguous.
+ b) destination must have all host bits clear.
+ c) if application forgot to set correct family (AF_INET),
+ reject request unless it is absolutely clear i.e.
+ both family and mask are zero.
+ */
+ plen = 32;
+ ptr = &((struct sockaddr_in*)&r->rt_dst)->sin_addr.s_addr;
+ if (!(r->rt_flags&RTF_HOST)) {
+ u32 mask = ((struct sockaddr_in*)&r->rt_genmask)->sin_addr.s_addr;
+ if (r->rt_genmask.sa_family != AF_INET) {
+ if (mask || r->rt_genmask.sa_family)
+ return -EAFNOSUPPORT;
+ }
+ if (bad_mask(mask, *ptr))
+ return -EINVAL;
+ plen = inet_mask_len(mask);
+ }
+
+ nl->nlmsg_flags = NLM_F_REQUEST;
+ nl->nlmsg_pid = 0;
+ nl->nlmsg_seq = 0;
+ nl->nlmsg_len = NLMSG_LENGTH(sizeof(*rtm));
+ if (cmd == SIOCDELRT) {
+ nl->nlmsg_type = RTM_DELROUTE;
+ nl->nlmsg_flags = 0;
+ } else {
+ nl->nlmsg_type = RTM_NEWROUTE;
+ nl->nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE;
+ rtm->rtm_protocol = RTPROT_BOOT;
+ }
+
+ rtm->rtm_dst_len = plen;
+ rta->rta_dst = ptr;
+
+ if (r->rt_metric) {
+ *(u32*)&r->rt_pad3 = r->rt_metric - 1;
+ rta->rta_priority = (u32*)&r->rt_pad3;
+ }
+ if (r->rt_flags&RTF_REJECT) {
+ rtm->rtm_scope = RT_SCOPE_HOST;
+ rtm->rtm_type = RTN_UNREACHABLE;
+ return 0;
+ }
+ rtm->rtm_scope = RT_SCOPE_NOWHERE;
+ rtm->rtm_type = RTN_UNICAST;
+
+ if (r->rt_dev) {
+#ifdef CONFIG_IP_ALIAS
+ char *colon;
+#endif
+ struct device *dev;
+ char devname[IFNAMSIZ];
+
+ if (copy_from_user(devname, r->rt_dev, IFNAMSIZ-1))
+ return -EFAULT;
+ devname[IFNAMSIZ-1] = 0;
+#ifdef CONFIG_IP_ALIAS
+ colon = strchr(devname, ':');
+ if (colon)
+ *colon = 0;
+#endif
+ dev = dev_get(devname);
+ if (!dev)
+ return -ENODEV;
+ rta->rta_oif = &dev->ifindex;
+#ifdef CONFIG_IP_ALIAS
+ if (colon) {
+ struct in_ifaddr *ifa;
+ struct in_device *in_dev = dev->ip_ptr;
+ if (!in_dev)
+ return -ENODEV;
+ *colon = ':';
+ for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next)
+ if (strcmp(ifa->ifa_label, devname) == 0)
+ break;
+ if (ifa == NULL)
+ return -ENODEV;
+ rta->rta_prefsrc = &ifa->ifa_local;
+ }
+#endif
+ }
+
+ ptr = &((struct sockaddr_in*)&r->rt_gateway)->sin_addr.s_addr;
+ if (r->rt_gateway.sa_family == AF_INET && *ptr) {
+ rta->rta_gw = ptr;
+ if (r->rt_flags&RTF_GATEWAY && inet_addr_type(*ptr) == RTN_UNICAST)
+ rtm->rtm_scope = RT_SCOPE_UNIVERSE;
+ }
+
+ if (cmd == SIOCDELRT)
+ return 0;
+
+ if (r->rt_flags&RTF_GATEWAY && rta->rta_gw == NULL)
+ return -EINVAL;
+
+ if (rtm->rtm_scope == RT_SCOPE_NOWHERE)
+ rtm->rtm_scope = RT_SCOPE_LINK;
+
+ if (r->rt_flags&(RTF_MTU|RTF_WINDOW|RTF_IRTT)) {
+ struct rtattr *rec;
+ struct rtattr *mx = kmalloc(RTA_LENGTH(3*RTA_LENGTH(4)), GFP_KERNEL);
+ if (mx == NULL)
+ return -ENOMEM;
+ rta->rta_mx = mx;
+ mx->rta_type = RTA_METRICS;
+ mx->rta_len = RTA_LENGTH(0);
+ if (r->rt_flags&RTF_MTU) {
+ rec = (void*)((char*)mx + RTA_ALIGN(mx->rta_len));
+ rec->rta_type = RTAX_MTU;
+ rec->rta_len = RTA_LENGTH(4);
+ mx->rta_len += RTA_LENGTH(4);
+ *(u32*)RTA_DATA(rec) = r->rt_mtu;
+ }
+ if (r->rt_flags&RTF_WINDOW) {
+ rec = (void*)((char*)mx + RTA_ALIGN(mx->rta_len));
+ rec->rta_type = RTAX_WINDOW;
+ rec->rta_len = RTA_LENGTH(4);
+ mx->rta_len += RTA_LENGTH(4);
+ *(u32*)RTA_DATA(rec) = r->rt_window;
+ }
+ if (r->rt_flags&RTF_IRTT) {
+ rec = (void*)((char*)mx + RTA_ALIGN(mx->rta_len));
+ rec->rta_type = RTAX_RTT;
+ rec->rta_len = RTA_LENGTH(4);
+ mx->rta_len += RTA_LENGTH(4);
+ *(u32*)RTA_DATA(rec) = r->rt_irtt;
+ }
+ }
+ return 0;
+}
+
+#endif
+
+/*
+ Update FIB if:
+ - local address disappeared -> we must delete all the entries
+ referring to it.
+ - device went down -> we must shutdown all nexthops going via it.
+ */
+
+int fib_sync_down(u32 local, struct device *dev, int force)
+{
+ int ret = 0;
+ int scope = RT_SCOPE_NOWHERE;
+
+ if (force)
+ scope = -1;
+
+ for_fib_info() {
+ if (local && fi->fib_prefsrc == local) {
+ fi->fib_flags |= RTNH_F_DEAD;
+ ret++;
+ } else if (dev && fi->fib_nhs) {
+ int dead = 0;
+
+ change_nexthops(fi) {
+ if (nh->nh_flags&RTNH_F_DEAD)
+ dead++;
+ else if (nh->nh_dev == dev &&
+ nh->nh_scope != scope) {
+ nh->nh_flags |= RTNH_F_DEAD;
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ fi->fib_power -= nh->nh_power;
+ nh->nh_power = 0;
+#endif
+ dead++;
+ }
+ } endfor_nexthops(fi)
+ if (dead == fi->fib_nhs) {
+ fi->fib_flags |= RTNH_F_DEAD;
+ ret++;
+ }
+ }
+ } endfor_fib_info();
+ return ret;
+}
+
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+
+/*
+ Dead device goes up. We wake up dead nexthops.
+ It takes sense only on multipath routes.
+ */
+
+int fib_sync_up(struct device *dev)
+{
+ int ret = 0;
+
+ if (!(dev->flags&IFF_UP))
+ return 0;
+
+ for_fib_info() {
+ int alive = 0;
+
+ change_nexthops(fi) {
+ if (!(nh->nh_flags&RTNH_F_DEAD)) {
+ alive++;
+ continue;
+ }
+ if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP))
+ continue;
+ if (nh->nh_dev != dev || dev->ip_ptr == NULL)
+ continue;
+ alive++;
+ nh->nh_power = 0;
+ nh->nh_flags &= ~RTNH_F_DEAD;
+ } endfor_nexthops(fi)
+
+ if (alive == fi->fib_nhs) {
+ fi->fib_flags &= ~RTNH_F_DEAD;
+ ret++;
+ }
+ } endfor_fib_info();
+ return ret;
+}
+
+/*
+ The algorithm is suboptimal, but it provides really
+ fair weighted route distribution.
+ */
+
+void fib_select_multipath(const struct rt_key *key, struct fib_result *res)
+{
+ struct fib_info *fi = res->fi;
+ int w;
+
+ if (fi->fib_power <= 0) {
+ int power = 0;
+ change_nexthops(fi) {
+ if (!(nh->nh_flags&RTNH_F_DEAD)) {
+ power += nh->nh_weight;
+ nh->nh_power = nh->nh_weight;
+ }
+ } endfor_nexthops(fi);
+ fi->fib_power = power;
+#if 1
+ if (power <= 0) {
+ printk(KERN_CRIT "impossible 777\n");
+ return;
+ }
+#endif
+ }
+
+
+ /* w should be random number [0..fi->fib_power-1],
+ it is pretty bad approximation.
+ */
+
+ w = jiffies % fi->fib_power;
+
+ change_nexthops(fi) {
+ if (!(nh->nh_flags&RTNH_F_DEAD) && nh->nh_power) {
+ if ((w -= nh->nh_power) <= 0) {
+ nh->nh_power--;
+ fi->fib_power--;
+ res->nh_sel = nhsel;
+ return;
+ }
+ }
+ } endfor_nexthops(fi);
+
+#if 1
+ printk(KERN_CRIT "impossible 888\n");
+#endif
+ return;
+}
+#endif
+
+
+#ifdef CONFIG_PROC_FS
+
+static unsigned fib_flag_trans(int type, int dead, u32 mask, struct fib_info *fi)
+{
+ static unsigned type2flags[RTN_MAX+1] = {
+ 0, 0, 0, 0, 0, 0, 0, RTF_REJECT, RTF_REJECT, 0, 0, 0
+ };
+ unsigned flags = type2flags[type];
+
+ if (fi && fi->fib_nh->nh_gw)
+ flags |= RTF_GATEWAY;
+ if (mask == 0xFFFFFFFF)
+ flags |= RTF_HOST;
+ if (!dead)
+ flags |= RTF_UP;
+ return flags;
+}
+
+void fib_node_get_info(int type, int dead, struct fib_info *fi, u32 prefix, u32 mask, char *buffer)
+{
+ int len;
+ unsigned flags = fib_flag_trans(type, dead, mask, fi);
+
+ if (fi) {
+ len = sprintf(buffer, "%s\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u",
+ fi->fib_dev ? fi->fib_dev->name : "*", prefix,
+ fi->fib_nh->nh_gw, flags, 0, 0, fi->fib_priority,
+ mask, fi->fib_mtu, fi->fib_window, fi->fib_rtt);
+ } else {
+ len = sprintf(buffer, "*\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u",
+ prefix, 0,
+ flags, 0, 0, 0,
+ mask, 0, 0, 0);
+ }
+ memset(buffer+len, ' ', 127-len);
+ buffer[127] = '\n';
+}
+
+#endif
diff --git a/pfinet/linux-src/net/ipv4/icmp.c b/pfinet/linux-src/net/ipv4/icmp.c
new file mode 100644
index 00000000..7fad4783
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/icmp.c
@@ -0,0 +1,1156 @@
+/*
+ * NET3: Implementation of the ICMP protocol layer.
+ *
+ * Alan Cox, <alan@redhat.com>
+ *
+ * Version: $Id: icmp.c,v 1.52.2.4 1999/11/16 02:28:40 davem Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Some of the function names and the icmp unreach table for this
+ * module were derived from [icmp.c 1.0.11 06/02/93] by
+ * Ross Biro, Fred N. van Kempen, Mark Evans, Alan Cox, Gerhard Koerting.
+ * Other than that this module is a complete rewrite.
+ *
+ * Fixes:
+ * Mike Shaver : RFC1122 checks.
+ * Alan Cox : Multicast ping reply as self.
+ * Alan Cox : Fix atomicity lockup in ip_build_xmit
+ * call.
+ * Alan Cox : Added 216,128 byte paths to the MTU
+ * code.
+ * Martin Mares : RFC1812 checks.
+ * Martin Mares : Can be configured to follow redirects
+ * if acting as a router _without_ a
+ * routing protocol (RFC 1812).
+ * Martin Mares : Echo requests may be configured to
+ * be ignored (RFC 1812).
+ * Martin Mares : Limitation of ICMP error message
+ * transmit rate (RFC 1812).
+ * Martin Mares : TOS and Precedence set correctly
+ * (RFC 1812).
+ * Martin Mares : Now copying as much data from the
+ * original packet as we can without
+ * exceeding 576 bytes (RFC 1812).
+ * Willy Konynenberg : Transparent proxying support.
+ * Keith Owens : RFC1191 correction for 4.2BSD based
+ * path MTU bug.
+ * Thomas Quinot : ICMP Dest Unreach codes up to 15 are
+ * valid (RFC 1812).
+ * Andi Kleen : Check all packet lengths properly
+ * and moved all kfree_skb() up to
+ * icmp_rcv.
+ * Andi Kleen : Move the rate limit bookkeeping
+ * into the dest entry and use a token
+ * bucket filter (thanks to ANK). Make
+ * the rates sysctl configurable.
+ * Yu Tianli : Fixed two ugly bugs in icmp_send
+ * - IP option length was accounted wrongly
+ * - ICMP header length was not accounted at all.
+ * Tristan Greaves : Added sysctl option to ignore bogus broadcast
+ * responses from broken routers.
+ *
+ * To Fix:
+ *
+ * - Should use skb_pull() instead of all the manual checking.
+ * This would also greatly simply some upper layer error handlers. --AK
+ *
+ * RFC1122 (Host Requirements -- Comm. Layer) Status:
+ * (boy, are there a lot of rules for ICMP)
+ * 3.2.2 (Generic ICMP stuff)
+ * MUST discard messages of unknown type. (OK)
+ * MUST copy at least the first 8 bytes from the offending packet
+ * when sending ICMP errors. (OBSOLETE -- see RFC1812)
+ * MUST pass received ICMP errors up to protocol level. (OK)
+ * SHOULD send ICMP errors with TOS == 0. (OBSOLETE -- see RFC1812)
+ * MUST NOT send ICMP errors in reply to:
+ * ICMP errors (OK)
+ * Broadcast/multicast datagrams (OK)
+ * MAC broadcasts (OK)
+ * Non-initial fragments (OK)
+ * Datagram with a source address that isn't a single host. (OK)
+ * 3.2.2.1 (Destination Unreachable)
+ * All the rules govern the IP layer, and are dealt with in ip.c, not here.
+ * 3.2.2.2 (Redirect)
+ * Host SHOULD NOT send ICMP_REDIRECTs. (OK)
+ * MUST update routing table in response to host or network redirects.
+ * (host OK, network OBSOLETE)
+ * SHOULD drop redirects if they're not from directly connected gateway
+ * (OK -- we drop it if it's not from our old gateway, which is close
+ * enough)
+ * 3.2.2.3 (Source Quench)
+ * MUST pass incoming SOURCE_QUENCHs to transport layer (OK)
+ * Other requirements are dealt with at the transport layer.
+ * 3.2.2.4 (Time Exceeded)
+ * MUST pass TIME_EXCEEDED to transport layer (OK)
+ * Other requirements dealt with at IP (generating TIME_EXCEEDED).
+ * 3.2.2.5 (Parameter Problem)
+ * SHOULD generate these (OK)
+ * MUST pass received PARAMPROBLEM to transport layer (NOT YET)
+ * [Solaris 2.X seems to assert EPROTO when this occurs] -- AC
+ * 3.2.2.6 (Echo Request/Reply)
+ * MUST reply to ECHO_REQUEST, and give app to do ECHO stuff (OK, OK)
+ * MAY discard broadcast ECHO_REQUESTs. (Configurable with a sysctl.)
+ * MUST reply using same source address as the request was sent to.
+ * We're OK for unicast ECHOs, and it doesn't say anything about
+ * how to handle broadcast ones, since it's optional.
+ * MUST copy data from REQUEST to REPLY (OK)
+ * unless it would require illegal fragmentation (OK)
+ * MUST pass REPLYs to transport/user layer (OK)
+ * MUST use any provided source route (reversed) for REPLY. (NOT YET)
+ * 3.2.2.7 (Information Request/Reply)
+ * MUST NOT implement this. (I guess that means silently discard...?) (OK)
+ * 3.2.2.8 (Timestamp Request/Reply)
+ * MAY implement (OK)
+ * SHOULD be in-kernel for "minimum variability" (OK)
+ * MAY discard broadcast REQUESTs. (OK, but see source for inconsistency)
+ * MUST reply using same source address as the request was sent to. (OK)
+ * MUST reverse source route, as per ECHO (NOT YET)
+ * MUST pass REPLYs to transport/user layer (requires RAW, just like
+ * ECHO) (OK)
+ * MUST update clock for timestamp at least 15 times/sec (OK)
+ * MUST be "correct within a few minutes" (OK)
+ * 3.2.2.9 (Address Mask Request/Reply)
+ * MAY implement (OK)
+ * MUST send a broadcast REQUEST if using this system to set netmask
+ * (OK... we don't use it)
+ * MUST discard received REPLYs if not using this system (OK)
+ * MUST NOT send replies unless specifically made agent for this sort
+ * of thing. (OK)
+ *
+ *
+ * RFC 1812 (IPv4 Router Requirements) Status (even longer):
+ * 4.3.2.1 (Unknown Message Types)
+ * MUST pass messages of unknown type to ICMP user iface or silently discard
+ * them (OK)
+ * 4.3.2.2 (ICMP Message TTL)
+ * MUST initialize TTL when originating an ICMP message (OK)
+ * 4.3.2.3 (Original Message Header)
+ * SHOULD copy as much data from the offending packet as possible without
+ * the length of the ICMP datagram exceeding 576 bytes (OK)
+ * MUST leave original IP header of the offending packet, but we're not
+ * required to undo modifications made (OK)
+ * 4.3.2.4 (Original Message Source Address)
+ * MUST use one of addresses for the interface the orig. packet arrived as
+ * source address (OK)
+ * 4.3.2.5 (TOS and Precedence)
+ * SHOULD leave TOS set to the same value unless the packet would be
+ * discarded for that reason (OK)
+ * MUST use TOS=0 if not possible to leave original value (OK)
+ * MUST leave IP Precedence for Source Quench messages (OK -- not sent
+ * at all)
+ * SHOULD use IP Precedence = 6 (Internetwork Control) or 7 (Network Control)
+ * for all other error messages (OK, we use 6)
+ * MAY allow configuration of IP Precedence (OK -- not done)
+ * MUST leave IP Precedence and TOS for reply messages (OK)
+ * 4.3.2.6 (Source Route)
+ * SHOULD use reverse source route UNLESS sending Parameter Problem on source
+ * routing and UNLESS the packet would be immediately discarded (NOT YET)
+ * 4.3.2.7 (When Not to Send ICMP Errors)
+ * MUST NOT send ICMP errors in reply to:
+ * ICMP errors (OK)
+ * Packets failing IP header validation tests unless otherwise noted (OK)
+ * Broadcast/multicast datagrams (OK)
+ * MAC broadcasts (OK)
+ * Non-initial fragments (OK)
+ * Datagram with a source address that isn't a single host. (OK)
+ * 4.3.2.8 (Rate Limiting)
+ * SHOULD be able to limit error message rate (OK)
+ * SHOULD allow setting of rate limits (OK, in the source)
+ * 4.3.3.1 (Destination Unreachable)
+ * All the rules govern the IP layer, and are dealt with in ip.c, not here.
+ * 4.3.3.2 (Redirect)
+ * MAY ignore ICMP Redirects if running a routing protocol or if forwarding
+ * is enabled on the interface (OK -- ignores)
+ * 4.3.3.3 (Source Quench)
+ * SHOULD NOT originate SQ messages (OK)
+ * MUST be able to limit SQ rate if originates them (OK as we don't
+ * send them)
+ * MAY ignore SQ messages it receives (OK -- we don't)
+ * 4.3.3.4 (Time Exceeded)
+ * Requirements dealt with at IP (generating TIME_EXCEEDED).
+ * 4.3.3.5 (Parameter Problem)
+ * MUST generate these for all errors not covered by other messages (OK)
+ * MUST include original value of the value pointed by (OK)
+ * 4.3.3.6 (Echo Request)
+ * MUST implement echo server function (OK)
+ * MUST process at ER of at least max(576, MTU) (OK)
+ * MAY reject broadcast/multicast ER's (We don't, but that's OK)
+ * SHOULD have a config option for silently ignoring ER's (OK)
+ * MUST have a default value for the above switch = NO (OK)
+ * MUST have application layer interface for Echo Request/Reply (OK)
+ * MUST reply using same source address as the request was sent to.
+ * We're OK for unicast ECHOs, and it doesn't say anything about
+ * how to handle broadcast ones, since it's optional.
+ * MUST copy data from Request to Reply (OK)
+ * SHOULD update Record Route / Timestamp options (??)
+ * MUST use reversed Source Route for Reply if possible (NOT YET)
+ * 4.3.3.7 (Information Request/Reply)
+ * SHOULD NOT originate or respond to these (OK)
+ * 4.3.3.8 (Timestamp / Timestamp Reply)
+ * MAY implement (OK)
+ * MUST reply to every Timestamp message received (OK)
+ * MAY discard broadcast REQUESTs. (OK, but see source for inconsistency)
+ * MUST reply using same source address as the request was sent to. (OK)
+ * MUST use reversed Source Route if possible (NOT YET)
+ * SHOULD update Record Route / Timestamp options (??)
+ * MUST pass REPLYs to transport/user layer (requires RAW, just like
+ * ECHO) (OK)
+ * MUST update clock for timestamp at least 16 times/sec (OK)
+ * MUST be "correct within a few minutes" (OK)
+ * 4.3.3.9 (Address Mask Request/Reply)
+ * MUST have support for receiving AMRq and responding with AMRe (OK,
+ * but only as a compile-time option)
+ * SHOULD have option for each interface for AMRe's, MUST default to
+ * NO (NOT YET)
+ * MUST NOT reply to AMRq before knows the correct AM (OK)
+ * MUST NOT respond to AMRq with source address 0.0.0.0 on physical
+ * interfaces having multiple logical i-faces with different masks
+ * (NOT YET)
+ * SHOULD examine all AMRe's it receives and check them (NOT YET)
+ * SHOULD log invalid AMRe's (AM+sender) (NOT YET)
+ * MUST NOT use contents of AMRe to determine correct AM (OK)
+ * MAY broadcast AMRe's after having configured address masks (OK -- doesn't)
+ * MUST NOT do broadcast AMRe's if not set by extra option (OK, no option)
+ * MUST use the { <NetPrefix>, -1 } form of broadcast addresses (OK)
+ * 4.3.3.10 (Router Advertisement and Solicitations)
+ * MUST support router part of Router Discovery Protocol on all networks we
+ * support broadcast or multicast addressing. (OK -- done by gated)
+ * MUST have all config parameters with the respective defaults (OK)
+ * 5.2.7.1 (Destination Unreachable)
+ * MUST generate DU's (OK)
+ * SHOULD choose a best-match response code (OK)
+ * SHOULD NOT generate Host Isolated codes (OK)
+ * SHOULD use Communication Administratively Prohibited when administratively
+ * filtering packets (NOT YET -- bug-to-bug compatibility)
+ * MAY include config option for not generating the above and silently
+ * discard the packets instead (OK)
+ * MAY include config option for not generating Precedence Violation and
+ * Precedence Cutoff messages (OK as we don't generate them at all)
+ * MUST use Host Unreachable or Dest. Host Unknown codes whenever other hosts
+ * on the same network might be reachable (OK -- no net unreach's at all)
+ * MUST use new form of Fragmentation Needed and DF Set messages (OK)
+ * 5.2.7.2 (Redirect)
+ * MUST NOT generate network redirects (OK)
+ * MUST be able to generate host redirects (OK)
+ * SHOULD be able to generate Host+TOS redirects (NO as we don't use TOS)
+ * MUST have an option to use Host redirects instead of Host+TOS ones (OK as
+ * no Host+TOS Redirects are used)
+ * MUST NOT generate redirects unless forwarding to the same i-face and the
+ * dest. address is on the same subnet as the src. address and no source
+ * routing is in use. (OK)
+ * MUST NOT follow redirects when using a routing protocol (OK)
+ * MAY use redirects if not using a routing protocol (OK, compile-time option)
+ * MUST comply to Host Requirements when not acting as a router (OK)
+ * 5.2.7.3 (Time Exceeded)
+ * MUST generate Time Exceeded Code 0 when discarding packet due to TTL=0 (OK)
+ * MAY have a per-interface option to disable origination of TE messages, but
+ * it MUST default to "originate" (OK -- we don't support it)
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/fcntl.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/string.h>
+#include <net/snmp.h>
+#include <net/ip.h>
+#include <net/route.h>
+#include <net/protocol.h>
+#include <net/icmp.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <net/raw.h>
+#include <net/snmp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <net/checksum.h>
+
+#ifdef CONFIG_IP_MASQUERADE
+#include <net/ip_masq.h>
+#endif
+
+#define min(a,b) ((a)<(b)?(a):(b))
+
+/*
+ * Statistics
+ */
+
+struct icmp_mib icmp_statistics;
+
+/* An array of errno for error messages from dest unreach. */
+/* RFC 1122: 3.2.2.1 States that NET_UNREACH, HOS_UNREACH and SR_FAIELD MUST be considered 'transient errs'. */
+
+struct icmp_err icmp_err_convert[] = {
+ { ENETUNREACH, 0 }, /* ICMP_NET_UNREACH */
+ { EHOSTUNREACH, 0 }, /* ICMP_HOST_UNREACH */
+ { ENOPROTOOPT, 1 }, /* ICMP_PROT_UNREACH */
+ { ECONNREFUSED, 1 }, /* ICMP_PORT_UNREACH */
+ { EMSGSIZE, 0 }, /* ICMP_FRAG_NEEDED */
+ { EOPNOTSUPP, 0 }, /* ICMP_SR_FAILED */
+ { ENETUNREACH, 1 }, /* ICMP_NET_UNKNOWN */
+ { EHOSTDOWN, 1 }, /* ICMP_HOST_UNKNOWN */
+ { ENONET, 1 }, /* ICMP_HOST_ISOLATED */
+ { ENETUNREACH, 1 }, /* ICMP_NET_ANO */
+ { EHOSTUNREACH, 1 }, /* ICMP_HOST_ANO */
+ { ENETUNREACH, 0 }, /* ICMP_NET_UNR_TOS */
+ { EHOSTUNREACH, 0 }, /* ICMP_HOST_UNR_TOS */
+ { EHOSTUNREACH, 1 }, /* ICMP_PKT_FILTERED */
+ { EHOSTUNREACH, 1 }, /* ICMP_PREC_VIOLATION */
+ { EHOSTUNREACH, 1 } /* ICMP_PREC_CUTOFF */
+};
+
+/* Control parameters for ECHO relies. */
+int sysctl_icmp_echo_ignore_all = 0;
+int sysctl_icmp_echo_ignore_broadcasts = 0;
+
+/* Control parameter - ignore bogus broadcast responses? */
+int sysctl_icmp_ignore_bogus_error_responses =0;
+
+extern int sysctl_ip_always_defrag;
+
+/*
+ * ICMP control array. This specifies what to do with each ICMP.
+ */
+
+struct icmp_control
+{
+ unsigned long *output; /* Address to increment on output */
+ unsigned long *input; /* Address to increment on input */
+ void (*handler)(struct icmphdr *icmph, struct sk_buff *skb, int len);
+ short error; /* This ICMP is classed as an error message */
+ int *timeout; /* Rate limit */
+};
+
+static struct icmp_control icmp_pointers[NR_ICMP_TYPES+1];
+
+/*
+ * Build xmit assembly blocks
+ */
+
+struct icmp_bxm
+{
+ void *data_ptr;
+ int data_len;
+ struct icmphdr icmph;
+ unsigned long csum;
+ struct ip_options replyopts;
+ unsigned char optbuf[40];
+};
+
+/*
+ * The ICMP socket. This is the most convenient way to flow control
+ * our ICMP output as well as maintain a clean interface throughout
+ * all layers. All Socketless IP sends will soon be gone.
+ */
+
+struct inode icmp_inode;
+struct socket *icmp_socket=&icmp_inode.u.socket_i;
+
+/*
+ * Send an ICMP frame.
+ */
+
+/*
+ * Check transmit rate limitation for given message.
+ * The rate information is held in the destination cache now.
+ * This function is generic and could be used for other purposes
+ * too. It uses a Token bucket filter as suggested by Alexey Kuznetsov.
+ *
+ * Note that the same dst_entry fields are modified by functions in
+ * route.c too, but these work for packet destinations while xrlim_allow
+ * works for icmp destinations. This means the rate limiting information
+ * for one "ip object" is shared.
+ *
+ * Note that the same dst_entry fields are modified by functions in
+ * route.c too, but these work for packet destinations while xrlim_allow
+ * works for icmp destinations. This means the rate limiting information
+ * for one "ip object" is shared - and these ICMPs are twice limited:
+ * by source and by destination.
+ *
+ * RFC 1812: 4.3.2.8 SHOULD be able to limit error message rate
+ * SHOULD allow setting of rate limits
+ *
+ * Shared between ICMPv4 and ICMPv6.
+ */
+#define XRLIM_BURST_FACTOR 6
+int xrlim_allow(struct dst_entry *dst, int timeout)
+{
+ unsigned long now;
+
+ now = jiffies;
+ dst->rate_tokens += now - dst->rate_last;
+ dst->rate_last = now;
+ if (dst->rate_tokens > XRLIM_BURST_FACTOR*timeout)
+ dst->rate_tokens = XRLIM_BURST_FACTOR*timeout;
+ if (dst->rate_tokens >= timeout) {
+ dst->rate_tokens -= timeout;
+ return 1;
+ }
+ return 0;
+}
+
+static inline int icmpv4_xrlim_allow(struct rtable *rt, int type, int code)
+{
+ struct dst_entry *dst = &rt->u.dst;
+
+ if (type > NR_ICMP_TYPES || !icmp_pointers[type].timeout)
+ return 1;
+
+ /* Don't limit PMTU discovery. */
+ if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
+ return 1;
+
+ /* Redirect has its own rate limit mechanism */
+ if (type == ICMP_REDIRECT)
+ return 1;
+
+ /* No rate limit on loopback */
+ if (dst->dev && (dst->dev->flags&IFF_LOOPBACK))
+ return 1;
+
+ return xrlim_allow(dst, *(icmp_pointers[type].timeout));
+}
+
+/*
+ * Maintain the counters used in the SNMP statistics for outgoing ICMP
+ */
+
+static void icmp_out_count(int type)
+{
+ if (type>NR_ICMP_TYPES)
+ return;
+ (*icmp_pointers[type].output)++;
+ icmp_statistics.IcmpOutMsgs++;
+}
+
+/*
+ * Checksum each fragment, and on the first include the headers and final checksum.
+ */
+
+static int icmp_glue_bits(const void *p, char *to, unsigned int offset, unsigned int fraglen)
+{
+ struct icmp_bxm *icmp_param = (struct icmp_bxm *)p;
+ struct icmphdr *icmph;
+ unsigned long csum;
+
+ if (offset) {
+ icmp_param->csum=csum_partial_copy(icmp_param->data_ptr+offset-sizeof(struct icmphdr),
+ to, fraglen,icmp_param->csum);
+ return 0;
+ }
+
+ /*
+ * First fragment includes header. Note that we've done
+ * the other fragments first, so that we get the checksum
+ * for the whole packet here.
+ */
+ csum = csum_partial_copy((void *)&icmp_param->icmph,
+ to, sizeof(struct icmphdr),
+ icmp_param->csum);
+ csum = csum_partial_copy(icmp_param->data_ptr,
+ to+sizeof(struct icmphdr),
+ fraglen-sizeof(struct icmphdr), csum);
+ icmph=(struct icmphdr *)to;
+ icmph->checksum = csum_fold(csum);
+ return 0;
+}
+
+/*
+ * Driving logic for building and sending ICMP messages.
+ */
+
+static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
+{
+ struct sock *sk=icmp_socket->sk;
+ struct ipcm_cookie ipc;
+ struct rtable *rt = (struct rtable*)skb->dst;
+ u32 daddr;
+
+ if (ip_options_echo(&icmp_param->replyopts, skb))
+ return;
+
+ icmp_param->icmph.checksum=0;
+ icmp_param->csum=0;
+ icmp_out_count(icmp_param->icmph.type);
+
+ sk->ip_tos = skb->nh.iph->tos;
+ daddr = ipc.addr = rt->rt_src;
+ ipc.opt = &icmp_param->replyopts;
+ if (ipc.opt->srr)
+ daddr = icmp_param->replyopts.faddr;
+ if (ip_route_output(&rt, daddr, rt->rt_spec_dst, RT_TOS(skb->nh.iph->tos), 0))
+ return;
+ ip_build_xmit(sk, icmp_glue_bits, icmp_param,
+ icmp_param->data_len+sizeof(struct icmphdr),
+ &ipc, rt, MSG_DONTWAIT);
+ ip_rt_put(rt);
+}
+
+
+/*
+ * Send an ICMP message in response to a situation
+ *
+ * RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes of header. MAY send more (we do).
+ * MUST NOT change this header information.
+ * MUST NOT reply to a multicast/broadcast IP address.
+ * MUST NOT reply to a multicast/broadcast MAC address.
+ * MUST reply to only the first fragment.
+ */
+
+void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info)
+{
+ struct iphdr *iph;
+ struct icmphdr *icmph;
+ int room;
+ struct icmp_bxm icmp_param;
+ struct rtable *rt = (struct rtable*)skb_in->dst;
+ struct ipcm_cookie ipc;
+ u32 saddr;
+ u8 tos;
+
+ /*
+ * Find the original header
+ */
+
+ iph = skb_in->nh.iph;
+
+ /*
+ * No replies to physical multicast/broadcast
+ */
+
+ if (skb_in->pkt_type!=PACKET_HOST)
+ return;
+
+ /*
+ * Now check at the protocol level
+ */
+ if (!rt) {
+ if (sysctl_ip_always_defrag == 0 &&
+ net_ratelimit())
+ printk(KERN_DEBUG "icmp_send: destinationless packet\n");
+ return;
+ }
+ if (rt->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST))
+ return;
+
+
+ /*
+ * Only reply to fragment 0. We byte re-order the constant
+ * mask for efficiency.
+ */
+
+ if (iph->frag_off&htons(IP_OFFSET))
+ return;
+
+ /*
+ * If we send an ICMP error to an ICMP error a mess would result..
+ */
+
+ if (icmp_pointers[type].error) {
+ /*
+ * We are an error, check if we are replying to an ICMP error
+ */
+
+ if (iph->protocol==IPPROTO_ICMP) {
+ icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
+ /*
+ * Assume any unknown ICMP type is an error. This isn't
+ * specified by the RFC, but think about it..
+ */
+ if (icmph->type>NR_ICMP_TYPES || icmp_pointers[icmph->type].error)
+ return;
+ }
+ }
+
+
+ /*
+ * Construct source address and options.
+ */
+
+#ifdef CONFIG_IP_ROUTE_NAT
+ /*
+ * Restore original addresses if packet has been translated.
+ */
+ if (rt->rt_flags&RTCF_NAT && IPCB(skb_in)->flags&IPSKB_TRANSLATED) {
+ iph->daddr = rt->key.dst;
+ iph->saddr = rt->key.src;
+ }
+#endif
+#ifdef CONFIG_IP_MASQUERADE
+ if (type==ICMP_DEST_UNREACH && IPCB(skb_in)->flags&IPSKB_MASQUERADED) {
+ ip_fw_unmasq_icmp(skb_in);
+ }
+#endif
+
+ saddr = iph->daddr;
+ if (!(rt->rt_flags & RTCF_LOCAL))
+ saddr = 0;
+
+ tos = icmp_pointers[type].error ?
+ ((iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL) :
+ iph->tos;
+
+ /* XXX: use a more aggressive expire for routes created by
+ * this call (not longer than the rate limit timeout).
+ * It could be also worthwhile to not put them into ipv4
+ * fast routing cache at first. Otherwise an attacker can
+ * grow the routing table.
+ */
+ if (ip_route_output(&rt, iph->saddr, saddr, RT_TOS(tos), 0))
+ return;
+
+ if (ip_options_echo(&icmp_param.replyopts, skb_in))
+ goto ende;
+
+
+ /*
+ * Prepare data for ICMP header.
+ */
+
+ icmp_param.icmph.type=type;
+ icmp_param.icmph.code=code;
+ icmp_param.icmph.un.gateway = info;
+ icmp_param.icmph.checksum=0;
+ icmp_param.csum=0;
+ icmp_param.data_ptr=iph;
+ icmp_out_count(icmp_param.icmph.type);
+ icmp_socket->sk->ip_tos = tos;
+ ipc.addr = iph->saddr;
+ ipc.opt = &icmp_param.replyopts;
+ if (icmp_param.replyopts.srr) {
+ ip_rt_put(rt);
+ if (ip_route_output(&rt, icmp_param.replyopts.faddr, saddr, RT_TOS(tos), 0))
+ return;
+ }
+
+ if (!icmpv4_xrlim_allow(rt, type, code))
+ goto ende;
+
+ /* RFC says return as much as we can without exceeding 576 bytes. */
+
+ room = rt->u.dst.pmtu;
+ if (room > 576)
+ room = 576;
+ room -= sizeof(struct iphdr) + icmp_param.replyopts.optlen;
+ room -= sizeof(struct icmphdr);
+
+ icmp_param.data_len=(iph->ihl<<2)+skb_in->len;
+ if (icmp_param.data_len > room)
+ icmp_param.data_len = room;
+
+ ip_build_xmit(icmp_socket->sk, icmp_glue_bits, &icmp_param,
+ icmp_param.data_len+sizeof(struct icmphdr),
+ &ipc, rt, MSG_DONTWAIT);
+
+ende:
+ ip_rt_put(rt);
+}
+
+
+/*
+ * Handle ICMP_DEST_UNREACH, ICMP_TIME_EXCEED, and ICMP_QUENCH.
+ */
+
+static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, int len)
+{
+ struct iphdr *iph;
+ int hash;
+ struct inet_protocol *ipprot;
+ unsigned char *dp;
+ struct sock *raw_sk;
+
+ /*
+ * Incomplete header ?
+ * Only checks for the IP header, there should be an
+ * additional check for longer headers in upper levels.
+ */
+
+ if(len<sizeof(struct iphdr)) {
+ icmp_statistics.IcmpInErrors++;
+ return;
+ }
+
+ iph = (struct iphdr *) (icmph + 1);
+ dp = (unsigned char*)iph;
+
+ if(icmph->type==ICMP_DEST_UNREACH) {
+ switch(icmph->code & 15) {
+ case ICMP_NET_UNREACH:
+ break;
+ case ICMP_HOST_UNREACH:
+ break;
+ case ICMP_PROT_UNREACH:
+ break;
+ case ICMP_PORT_UNREACH:
+ break;
+ case ICMP_FRAG_NEEDED:
+ if (ipv4_config.no_pmtu_disc) {
+ if (sysctl_ip_always_defrag == 0 && net_ratelimit())
+ printk(KERN_INFO "ICMP: %d.%d.%d.%d: fragmentation needed and DF set.\n",
+ NIPQUAD(iph->daddr));
+ } else {
+ unsigned short new_mtu;
+ new_mtu = ip_rt_frag_needed(iph, ntohs(icmph->un.frag.mtu));
+ if (!new_mtu)
+ return;
+ icmph->un.frag.mtu = htons(new_mtu);
+ }
+ break;
+ case ICMP_SR_FAILED:
+ if (sysctl_ip_always_defrag == 0 && net_ratelimit())
+ printk(KERN_INFO "ICMP: %d.%d.%d.%d: Source Route Failed.\n", NIPQUAD(iph->daddr));
+ break;
+ default:
+ break;
+ }
+ if (icmph->code>NR_ICMP_UNREACH)
+ return;
+ }
+
+ /*
+ * Throw it at our lower layers
+ *
+ * RFC 1122: 3.2.2 MUST extract the protocol ID from the passed header.
+ * RFC 1122: 3.2.2.1 MUST pass ICMP unreach messages to the transport layer.
+ * RFC 1122: 3.2.2.2 MUST pass ICMP time expired messages to transport layer.
+ */
+
+ /*
+ * Check the other end isn't violating RFC 1122. Some routers send
+ * bogus responses to broadcast frames. If you see this message
+ * first check your netmask matches at both ends, if it does then
+ * get the other vendor to fix their kit.
+ */
+
+ if (!sysctl_icmp_ignore_bogus_error_responses)
+ {
+
+ if (inet_addr_type(iph->daddr) == RTN_BROADCAST)
+ {
+ if (net_ratelimit())
+ printk(KERN_WARNING "%d.%d.%d.%d sent an invalid ICMP error to a broadcast.\n",
+ NIPQUAD(skb->nh.iph->saddr));
+ return;
+ }
+ }
+
+ /*
+ * Deliver ICMP message to raw sockets. Pretty useless feature?
+ */
+
+ /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
+ hash = iph->protocol & (MAX_INET_PROTOS - 1);
+ if ((raw_sk = raw_v4_htable[hash]) != NULL)
+ {
+ while ((raw_sk = raw_v4_lookup(raw_sk, iph->protocol, iph->saddr,
+ iph->daddr, skb->dev->ifindex)) != NULL) {
+ raw_err(raw_sk, skb);
+ raw_sk = raw_sk->next;
+ }
+ }
+
+ /*
+ * This can't change while we are doing it.
+ */
+
+ ipprot = (struct inet_protocol *) inet_protos[hash];
+ while(ipprot != NULL) {
+ struct inet_protocol *nextip;
+
+ nextip = (struct inet_protocol *) ipprot->next;
+
+ /*
+ * Pass it off to everyone who wants it.
+ */
+
+ /* RFC1122: OK. Passes appropriate ICMP errors to the */
+ /* appropriate protocol layer (MUST), as per 3.2.2. */
+
+ if (iph->protocol == ipprot->protocol && ipprot->err_handler)
+ ipprot->err_handler(skb, dp, len);
+
+ ipprot = nextip;
+ }
+}
+
+
+/*
+ * Handle ICMP_REDIRECT.
+ */
+
+static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, int len)
+{
+ struct iphdr *iph;
+ unsigned long ip;
+
+ if (len < sizeof(struct iphdr)) {
+ icmp_statistics.IcmpInErrors++;
+ return;
+ }
+
+ /*
+ * Get the copied header of the packet that caused the redirect
+ */
+
+ iph = (struct iphdr *) (icmph + 1);
+ ip = iph->daddr;
+
+ switch(icmph->code & 7) {
+ case ICMP_REDIR_NET:
+ case ICMP_REDIR_NETTOS:
+ /*
+ * As per RFC recommendations now handle it as
+ * a host redirect.
+ */
+
+ case ICMP_REDIR_HOST:
+ case ICMP_REDIR_HOSTTOS:
+ ip_rt_redirect(skb->nh.iph->saddr, ip, icmph->un.gateway, iph->saddr, iph->tos, skb->dev);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Handle ICMP_ECHO ("ping") requests.
+ *
+ * RFC 1122: 3.2.2.6 MUST have an echo server that answers ICMP echo requests.
+ * RFC 1122: 3.2.2.6 Data received in the ICMP_ECHO request MUST be included in the reply.
+ * RFC 1812: 4.3.3.6 SHOULD have a config option for silently ignoring echo requests, MUST have default=NOT.
+ * See also WRT handling of options once they are done and working.
+ */
+
+static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, int len)
+{
+ if (!sysctl_icmp_echo_ignore_all) {
+ struct icmp_bxm icmp_param;
+
+ icmp_param.icmph=*icmph;
+ icmp_param.icmph.type=ICMP_ECHOREPLY;
+ icmp_param.data_ptr=(icmph+1);
+ icmp_param.data_len=len;
+ icmp_reply(&icmp_param, skb);
+ }
+}
+
+/*
+ * Handle ICMP Timestamp requests.
+ * RFC 1122: 3.2.2.8 MAY implement ICMP timestamp requests.
+ * SHOULD be in the kernel for minimum random latency.
+ * MUST be accurate to a few minutes.
+ * MUST be updated at least at 15Hz.
+ */
+
+static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, int len)
+{
+ struct timeval tv;
+ __u32 times[3]; /* So the new timestamp works on ALPHA's.. */
+ struct icmp_bxm icmp_param;
+
+ /*
+ * Too short.
+ */
+
+ if(len<12) {
+ icmp_statistics.IcmpInErrors++;
+ return;
+ }
+
+ /*
+ * Fill in the current time as ms since midnight UT:
+ */
+
+ do_gettimeofday(&tv);
+ times[1] = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
+ times[2] = times[1];
+ memcpy((void *)&times[0], icmph+1, 4); /* Incoming stamp */
+ icmp_param.icmph=*icmph;
+ icmp_param.icmph.type=ICMP_TIMESTAMPREPLY;
+ icmp_param.icmph.code=0;
+ icmp_param.data_ptr=&times;
+ icmp_param.data_len=12;
+ icmp_reply(&icmp_param, skb);
+}
+
+
+/*
+ * Handle ICMP_ADDRESS_MASK requests. (RFC950)
+ *
+ * RFC1122 (3.2.2.9). A host MUST only send replies to
+ * ADDRESS_MASK requests if it's been configured as an address mask
+ * agent. Receiving a request doesn't constitute implicit permission to
+ * act as one. Of course, implementing this correctly requires (SHOULD)
+ * a way to turn the functionality on and off. Another one for sysctl(),
+ * I guess. -- MS
+ *
+ * RFC1812 (4.3.3.9). A router MUST implement it.
+ * A router SHOULD have switch turning it on/off.
+ * This switch MUST be ON by default.
+ *
+ * Gratuitous replies, zero-source replies are not implemented,
+ * that complies with RFC. DO NOT implement them!!! All the idea
+ * of broadcast addrmask replies as specified in RFC950 is broken.
+ * The problem is that it is not uncommon to have several prefixes
+ * on one physical interface. Moreover, addrmask agent can even be
+ * not aware of existing another prefixes.
+ * If source is zero, addrmask agent cannot choose correct prefix.
+ * Gratuitous mask announcements suffer from the same problem.
+ * RFC1812 explains it, but still allows to use ADDRMASK,
+ * that is pretty silly. --ANK
+ *
+ * All these rules are so bizarre, that I removed kernel addrmask
+ * support at all. It is wrong, it is obsolete, nobody uses it in
+ * any case. --ANK
+ *
+ * Furthermore you can do it with a usermode address agent program
+ * anyway...
+ */
+
+static void icmp_address(struct icmphdr *icmph, struct sk_buff *skb, int len)
+{
+#if 0
+ if (sysctl_ip_always_defrag == 0 && net_ratelimit())
+ printk(KERN_DEBUG "a guy asks for address mask. Who is it?\n");
+#endif
+}
+
+/*
+ * RFC1812 (4.3.3.9). A router SHOULD listen all replies, and complain
+ * loudly if an inconsistency is found.
+ */
+
+static void icmp_address_reply(struct icmphdr *icmph, struct sk_buff *skb, int len)
+{
+ struct rtable *rt = (struct rtable*)skb->dst;
+ struct device *dev = skb->dev;
+ struct in_device *in_dev = dev->ip_ptr;
+ struct in_ifaddr *ifa;
+ u32 mask;
+
+ if (!in_dev || !in_dev->ifa_list ||
+ !IN_DEV_LOG_MARTIANS(in_dev) ||
+ !IN_DEV_FORWARD(in_dev) ||
+ len < 4 ||
+ !(rt->rt_flags&RTCF_DIRECTSRC))
+ return;
+
+ mask = *(u32*)&icmph[1];
+ for (ifa=in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
+ if (mask == ifa->ifa_mask && inet_ifa_match(rt->rt_src, ifa))
+ return;
+ }
+ if (sysctl_ip_always_defrag == 0 && net_ratelimit())
+ printk(KERN_INFO "Wrong address mask %08X from %08X/%s\n",
+ ntohl(mask), ntohl(rt->rt_src), dev->name);
+}
+
+static void icmp_discard(struct icmphdr *icmph, struct sk_buff *skb, int len)
+{
+}
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+/*
+ * Check incoming icmp packets not addressed locally, to check whether
+ * they relate to a (proxying) socket on our system.
+ * Needed for transparent proxying.
+ *
+ * This code is presently ugly and needs cleanup.
+ * Probably should add a chkaddr entry to ipprot to call a chk routine
+ * in udp.c or tcp.c...
+ */
+
+/* This should work with the new hashes now. -DaveM */
+extern struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif);
+extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif);
+
+int icmp_chkaddr(struct sk_buff *skb)
+{
+ struct icmphdr *icmph=(struct icmphdr *)(skb->nh.raw + skb->nh.iph->ihl*4);
+ struct iphdr *iph = (struct iphdr *) (icmph + 1);
+ void (*handler)(struct icmphdr *icmph, struct sk_buff *skb, int len) = icmp_pointers[icmph->type].handler;
+
+ if (handler == icmp_unreach || handler == icmp_redirect) {
+ struct sock *sk;
+
+ switch (iph->protocol) {
+ case IPPROTO_TCP:
+ {
+ struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
+
+ sk = tcp_v4_lookup(iph->daddr, th->dest, iph->saddr, th->source, skb->dev->ifindex);
+ if (!sk || (sk->state == TCP_LISTEN))
+ return 0;
+ /*
+ * This packet came from us.
+ */
+ return 1;
+ }
+ case IPPROTO_UDP:
+ {
+ struct udphdr *uh = (struct udphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
+
+ sk = udp_v4_lookup(iph->daddr, uh->dest, iph->saddr, uh->source, skb->dev->ifindex);
+ if (!sk) return 0;
+ if (sk->saddr != iph->saddr && inet_addr_type(iph->saddr) != RTN_LOCAL)
+ return 0;
+ /*
+ * This packet may have come from us.
+ * Assume it did.
+ */
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+#endif
+
+/*
+ * Deal with incoming ICMP packets.
+ */
+
+int icmp_rcv(struct sk_buff *skb, unsigned short len)
+{
+ struct icmphdr *icmph = skb->h.icmph;
+ struct rtable *rt = (struct rtable*)skb->dst;
+
+ icmp_statistics.IcmpInMsgs++;
+
+ /*
+ * 18 is the highest 'known' ICMP type. Anything else is a mystery
+ *
+ * RFC 1122: 3.2.2 Unknown ICMP messages types MUST be silently discarded.
+ */
+ if(len < sizeof(struct icmphdr) ||
+ ip_compute_csum((unsigned char *) icmph, len) ||
+ icmph->type > NR_ICMP_TYPES)
+ goto error;
+
+ /*
+ * Parse the ICMP message
+ */
+
+ if (rt->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST)) {
+ /*
+ * RFC 1122: 3.2.2.6 An ICMP_ECHO to broadcast MAY be
+ * silently ignored (we let user decide with a sysctl).
+ * RFC 1122: 3.2.2.8 An ICMP_TIMESTAMP MAY be silently
+ * discarded if to broadcast/multicast.
+ */
+ if (icmph->type == ICMP_ECHO &&
+ sysctl_icmp_echo_ignore_broadcasts) {
+ goto error;
+ }
+ if (icmph->type != ICMP_ECHO &&
+ icmph->type != ICMP_TIMESTAMP &&
+ icmph->type != ICMP_ADDRESS &&
+ icmph->type != ICMP_ADDRESSREPLY) {
+ goto error;
+ }
+ }
+
+ len -= sizeof(struct icmphdr);
+ (*icmp_pointers[icmph->type].input)++;
+ (icmp_pointers[icmph->type].handler)(icmph, skb, len);
+
+drop:
+ kfree_skb(skb);
+ return 0;
+error:
+ icmp_statistics.IcmpInErrors++;
+ goto drop;
+}
+
+/*
+ * A spare long used to speed up statistics updating
+ */
+
+static unsigned long dummy;
+
+/*
+ * Configurable rate limits.
+ * Someone should check if these default values are correct.
+ * Note that these values interact with the routing cache GC timeout.
+ * If you chose them too high they won't take effect, because the
+ * dst_entry gets expired too early. The same should happen when
+ * the cache grows too big.
+ */
+int sysctl_icmp_destunreach_time = 1*HZ;
+int sysctl_icmp_timeexceed_time = 1*HZ;
+int sysctl_icmp_paramprob_time = 1*HZ;
+int sysctl_icmp_echoreply_time = 0; /* don't limit it per default. */
+
+/*
+ * This table is the definition of how we handle ICMP.
+ */
+
+static struct icmp_control icmp_pointers[NR_ICMP_TYPES+1] = {
+/* ECHO REPLY (0) */
+ { &icmp_statistics.IcmpOutEchoReps, &icmp_statistics.IcmpInEchoReps, icmp_discard, 0, &sysctl_icmp_echoreply_time},
+ { &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, },
+ { &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, },
+/* DEST UNREACH (3) */
+ { &icmp_statistics.IcmpOutDestUnreachs, &icmp_statistics.IcmpInDestUnreachs, icmp_unreach, 1, &sysctl_icmp_destunreach_time },
+/* SOURCE QUENCH (4) */
+ { &icmp_statistics.IcmpOutSrcQuenchs, &icmp_statistics.IcmpInSrcQuenchs, icmp_unreach, 1, },
+/* REDIRECT (5) */
+ { &icmp_statistics.IcmpOutRedirects, &icmp_statistics.IcmpInRedirects, icmp_redirect, 1, },
+ { &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, },
+ { &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, },
+/* ECHO (8) */
+ { &icmp_statistics.IcmpOutEchos, &icmp_statistics.IcmpInEchos, icmp_echo, 0, },
+ { &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, },
+ { &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, },
+/* TIME EXCEEDED (11) */
+ { &icmp_statistics.IcmpOutTimeExcds, &icmp_statistics.IcmpInTimeExcds, icmp_unreach, 1, &sysctl_icmp_timeexceed_time },
+/* PARAMETER PROBLEM (12) */
+ { &icmp_statistics.IcmpOutParmProbs, &icmp_statistics.IcmpInParmProbs, icmp_unreach, 1, &sysctl_icmp_paramprob_time },
+/* TIMESTAMP (13) */
+ { &icmp_statistics.IcmpOutTimestamps, &icmp_statistics.IcmpInTimestamps, icmp_timestamp, 0, },
+/* TIMESTAMP REPLY (14) */
+ { &icmp_statistics.IcmpOutTimestampReps, &icmp_statistics.IcmpInTimestampReps, icmp_discard, 0, },
+/* INFO (15) */
+ { &dummy, &dummy, icmp_discard, 0, },
+/* INFO REPLY (16) */
+ { &dummy, &dummy, icmp_discard, 0, },
+/* ADDR MASK (17) */
+ { &icmp_statistics.IcmpOutAddrMasks, &icmp_statistics.IcmpInAddrMasks, icmp_address, 0, },
+/* ADDR MASK REPLY (18) */
+ { &icmp_statistics.IcmpOutAddrMaskReps, &icmp_statistics.IcmpInAddrMaskReps, icmp_address_reply, 0, }
+};
+
+__initfunc(void icmp_init(struct net_proto_family *ops))
+{
+ int err;
+
+ icmp_inode.i_mode = S_IFSOCK;
+ icmp_inode.i_sock = 1;
+ icmp_inode.i_uid = 0;
+ icmp_inode.i_gid = 0;
+
+ icmp_socket->inode = &icmp_inode;
+ icmp_socket->state = SS_UNCONNECTED;
+ icmp_socket->type=SOCK_RAW;
+
+ if ((err=ops->create(icmp_socket, IPPROTO_ICMP))<0)
+ panic("Failed to create the ICMP control socket.\n");
+ icmp_socket->sk->allocation=GFP_ATOMIC;
+ icmp_socket->sk->num = 256; /* Don't receive any data */
+ icmp_socket->sk->ip_ttl = MAXTTL;
+}
diff --git a/pfinet/linux-src/net/ipv4/igmp.c b/pfinet/linux-src/net/ipv4/igmp.c
new file mode 100644
index 00000000..46269106
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/igmp.c
@@ -0,0 +1,697 @@
+/*
+ * Linux NET3: Internet Group Management Protocol [IGMP]
+ *
+ * This code implements the IGMP protocol as defined in RFC1112. There has
+ * been a further revision of this protocol since which is now supported.
+ *
+ * If you have trouble with this module be careful what gcc you have used,
+ * the older version didn't come out right using gcc 2.5.8, the newer one
+ * seems to fall out with gcc 2.6.2.
+ *
+ * Version: $Id: igmp.c,v 1.30.2.1 1999/07/23 15:29:22 davem Exp $
+ *
+ * Authors:
+ * Alan Cox <Alan.Cox@linux.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Fixes:
+ *
+ * Alan Cox : Added lots of __inline__ to optimise
+ * the memory usage of all the tiny little
+ * functions.
+ * Alan Cox : Dumped the header building experiment.
+ * Alan Cox : Minor tweaks ready for multicast routing
+ * and extended IGMP protocol.
+ * Alan Cox : Removed a load of inline directives. Gcc 2.5.8
+ * writes utterly bogus code otherwise (sigh)
+ * fixed IGMP loopback to behave in the manner
+ * desired by mrouted, fixed the fact it has been
+ * broken since 1.3.6 and cleaned up a few minor
+ * points.
+ *
+ * Chih-Jen Chang : Tried to revise IGMP to Version 2
+ * Tsu-Sheng Tsao E-mail: chihjenc@scf.usc.edu and tsusheng@scf.usc.edu
+ * The enhancements are mainly based on Steve Deering's
+ * ipmulti-3.5 source code.
+ * Chih-Jen Chang : Added the igmp_get_mrouter_info and
+ * Tsu-Sheng Tsao igmp_set_mrouter_info to keep track of
+ * the mrouted version on that device.
+ * Chih-Jen Chang : Added the max_resp_time parameter to
+ * Tsu-Sheng Tsao igmp_heard_query(). Using this parameter
+ * to identify the multicast router version
+ * and do what the IGMP version 2 specified.
+ * Chih-Jen Chang : Added a timer to revert to IGMP V2 router
+ * Tsu-Sheng Tsao if the specified time expired.
+ * Alan Cox : Stop IGMP from 0.0.0.0 being accepted.
+ * Alan Cox : Use GFP_ATOMIC in the right places.
+ * Christian Daudt : igmp timer wasn't set for local group
+ * memberships but was being deleted,
+ * which caused a "del_timer() called
+ * from %p with timer not initialized\n"
+ * message (960131).
+ * Christian Daudt : removed del_timer from
+ * igmp_timer_expire function (960205).
+ * Christian Daudt : igmp_heard_report now only calls
+ * igmp_timer_expire if tm->running is
+ * true (960216).
+ * Malcolm Beattie : ttl comparison wrong in igmp_rcv made
+ * igmp_heard_query never trigger. Expiry
+ * miscalculation fixed in igmp_heard_query
+ * and random() made to return unsigned to
+ * prevent negative expiry times.
+ * Alexey Kuznetsov: Wrong group leaving behaviour, backport
+ * fix from pending 2.1.x patches.
+ * Alan Cox: Forget to enable FDDI support earlier.
+ * Alexey Kuznetsov: Fixed leaving groups on device down.
+ * Alexey Kuznetsov: Accordance to igmp-v2-06 draft.
+ */
+
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/inetdevice.h>
+#include <linux/igmp.h>
+#include <linux/if_arp.h>
+#include <linux/rtnetlink.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/sock.h>
+#include <net/checksum.h>
+#ifdef CONFIG_IP_MROUTE
+#include <linux/mroute.h>
+#endif
+
+#define IP_MAX_MEMBERSHIPS 20
+
+#ifdef CONFIG_IP_MULTICAST
+
+/* Parameter names and values are taken from igmp-v2-06 draft */
+
+#define IGMP_V1_Router_Present_Timeout (400*HZ)
+#define IGMP_Unsolicited_Report_Interval (10*HZ)
+#define IGMP_Query_Response_Interval (10*HZ)
+#define IGMP_Unsolicited_Report_Count 2
+
+
+#define IGMP_Initial_Report_Delay (1*HZ)
+
+/* IGMP_Initial_Report_Delay is not from IGMP specs!
+ * IGMP specs require to report membership immediately after
+ * joining a group, but we delay the first report by a
+ * small interval. It seems more natural and still does not
+ * contradict to specs provided this delay is small enough.
+ */
+
+#define IGMP_V1_SEEN(in_dev) ((in_dev)->mr_v1_seen && (long)(jiffies - (in_dev)->mr_v1_seen) < 0)
+
+/*
+ * Timer management
+ */
+
+static __inline__ void igmp_stop_timer(struct ip_mc_list *im)
+{
+ if (im->tm_running) {
+ del_timer(&im->timer);
+ im->tm_running=0;
+ }
+}
+
+static __inline__ void igmp_start_timer(struct ip_mc_list *im, int max_delay)
+{
+ int tv;
+ if (im->tm_running)
+ return;
+ tv=net_random() % max_delay;
+ im->timer.expires=jiffies+tv+2;
+ im->tm_running=1;
+ add_timer(&im->timer);
+}
+
+/*
+ * Send an IGMP report.
+ */
+
+#define IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+4)
+
+static int igmp_send_report(struct device *dev, u32 group, int type)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ struct igmphdr *ih;
+ struct rtable *rt;
+ u32 dst;
+
+ /* According to IGMPv2 specs, LEAVE messages are
+ * sent to all-routers group.
+ */
+ dst = group;
+ if (type == IGMP_HOST_LEAVE_MESSAGE)
+ dst = IGMP_ALL_ROUTER;
+
+ if (ip_route_output(&rt, dst, 0, 0, dev->ifindex))
+ return -1;
+ if (rt->rt_src == 0) {
+ ip_rt_put(rt);
+ return -1;
+ }
+
+ skb=alloc_skb(IGMP_SIZE+dev->hard_header_len+15, GFP_ATOMIC);
+ if (skb == NULL) {
+ ip_rt_put(rt);
+ return -1;
+ }
+
+ skb->dst = &rt->u.dst;
+
+ skb_reserve(skb, (dev->hard_header_len+15)&~15);
+
+ skb->nh.iph = iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr)+4);
+
+ iph->version = 4;
+ iph->ihl = (sizeof(struct iphdr)+4)>>2;
+ iph->tos = 0;
+ iph->frag_off = 0;
+ iph->ttl = 1;
+ iph->daddr = dst;
+ iph->saddr = rt->rt_src;
+ iph->protocol = IPPROTO_IGMP;
+ iph->tot_len = htons(IGMP_SIZE);
+ iph->id = htons(ip_id_count++);
+ ((u8*)&iph[1])[0] = IPOPT_RA;
+ ((u8*)&iph[1])[1] = 4;
+ ((u8*)&iph[1])[2] = 0;
+ ((u8*)&iph[1])[3] = 0;
+ ip_send_check(iph);
+
+ ih = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
+ ih->type=type;
+ ih->code=0;
+ ih->csum=0;
+ ih->group=group;
+ ih->csum=ip_compute_csum((void *)ih, sizeof(struct igmphdr));
+
+ return skb->dst->output(skb);
+}
+
+
+static void igmp_timer_expire(unsigned long data)
+{
+ struct ip_mc_list *im=(struct ip_mc_list *)data;
+ struct in_device *in_dev = im->interface;
+ int err;
+
+ im->tm_running=0;
+
+ if (IGMP_V1_SEEN(in_dev))
+ err = igmp_send_report(in_dev->dev, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
+ else
+ err = igmp_send_report(in_dev->dev, im->multiaddr, IGMP_HOST_NEW_MEMBERSHIP_REPORT);
+
+ /* Failed. Retry later. */
+ if (err) {
+ igmp_start_timer(im, IGMP_Unsolicited_Report_Interval);
+ return;
+ }
+
+ if (im->unsolicit_count) {
+ im->unsolicit_count--;
+ igmp_start_timer(im, IGMP_Unsolicited_Report_Interval);
+ }
+ im->reporter = 1;
+}
+
+static void igmp_heard_report(struct in_device *in_dev, u32 group)
+{
+ struct ip_mc_list *im;
+
+ /* Timers are only set for non-local groups */
+
+ if (group == IGMP_ALL_HOSTS)
+ return;
+
+ for (im=in_dev->mc_list; im!=NULL; im=im->next) {
+ if (im->multiaddr == group) {
+ igmp_stop_timer(im);
+ im->reporter = 0;
+ im->unsolicit_count = 0;
+ return;
+ }
+ }
+}
+
+static void igmp_heard_query(struct in_device *in_dev, unsigned char max_resp_time,
+ u32 group)
+{
+ struct ip_mc_list *im;
+ int max_delay;
+
+ max_delay = max_resp_time*(HZ/IGMP_TIMER_SCALE);
+
+ if (max_resp_time == 0) {
+ /* Alas, old v1 router presents here. */
+
+ max_delay = IGMP_Query_Response_Interval;
+ in_dev->mr_v1_seen = jiffies + IGMP_V1_Router_Present_Timeout;
+ group = 0;
+ }
+
+ /*
+ * - Start the timers in all of our membership records
+ * that the query applies to for the interface on
+ * which the query arrived excl. those that belong
+ * to a "local" group (224.0.0.X)
+ * - For timers already running check if they need to
+ * be reset.
+ * - Use the igmp->igmp_code field as the maximum
+ * delay possible
+ */
+ for (im=in_dev->mc_list; im!=NULL; im=im->next) {
+ if (group && group != im->multiaddr)
+ continue;
+ if (im->multiaddr == IGMP_ALL_HOSTS)
+ continue;
+ im->unsolicit_count = 0;
+ if (im->tm_running && (long)(im->timer.expires-jiffies) > max_delay)
+ igmp_stop_timer(im);
+ igmp_start_timer(im, max_delay);
+ }
+}
+
+int igmp_rcv(struct sk_buff *skb, unsigned short len)
+{
+ /* This basically follows the spec line by line -- see RFC1112 */
+ struct igmphdr *ih = skb->h.igmph;
+ struct in_device *in_dev = skb->dev->ip_ptr;
+
+ if (len < sizeof(struct igmphdr) || ip_compute_csum((void *)ih, len)
+ || in_dev==NULL) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ switch (ih->type) {
+ case IGMP_HOST_MEMBERSHIP_QUERY:
+ igmp_heard_query(in_dev, ih->code, ih->group);
+ break;
+ case IGMP_HOST_MEMBERSHIP_REPORT:
+ case IGMP_HOST_NEW_MEMBERSHIP_REPORT:
+ /* Is it our report looped back? */
+ if (((struct rtable*)skb->dst)->key.iif == 0)
+ break;
+ igmp_heard_report(in_dev, ih->group);
+ break;
+ case IGMP_PIM:
+#ifdef CONFIG_IP_PIMSM_V1
+ return pim_rcv_v1(skb, len);
+#endif
+ case IGMP_DVMRP:
+ case IGMP_TRACE:
+ case IGMP_HOST_LEAVE_MESSAGE:
+ case IGMP_MTRACE:
+ case IGMP_MTRACE_RESP:
+ break;
+ default:
+ NETDEBUG(printk(KERN_DEBUG "New IGMP type=%d, why we do not know about it?\n", ih->type));
+ }
+ kfree_skb(skb);
+ return 0;
+}
+
+#endif
+
+
+/*
+ * Add a filter to a device
+ */
+
+static void ip_mc_filter_add(struct in_device *in_dev, u32 addr)
+{
+ char buf[MAX_ADDR_LEN];
+ struct device *dev = in_dev->dev;
+
+ /* Checking for IFF_MULTICAST here is WRONG-WRONG-WRONG.
+ We will get multicast token leakage, when IFF_MULTICAST
+ is changed. This check should be done in dev->set_multicast_list
+ routine. Something sort of:
+ if (dev->mc_list && dev->flags&IFF_MULTICAST) { do it; }
+ --ANK
+ */
+ if (arp_mc_map(addr, buf, dev, 0) == 0)
+ dev_mc_add(dev,buf,dev->addr_len,0);
+}
+
+/*
+ * Remove a filter from a device
+ */
+
+static void ip_mc_filter_del(struct in_device *in_dev, u32 addr)
+{
+ char buf[MAX_ADDR_LEN];
+ struct device *dev = in_dev->dev;
+
+ if (arp_mc_map(addr, buf, dev, 0) == 0)
+ dev_mc_delete(dev,buf,dev->addr_len,0);
+}
+
+static void igmp_group_dropped(struct ip_mc_list *im)
+{
+ if (im->loaded) {
+ im->loaded = 0;
+ ip_mc_filter_del(im->interface, im->multiaddr);
+ }
+
+#ifdef CONFIG_IP_MULTICAST
+ if (im->multiaddr == IGMP_ALL_HOSTS)
+ return;
+
+ start_bh_atomic();
+ igmp_stop_timer(im);
+ end_bh_atomic();
+
+ if (im->reporter && !IGMP_V1_SEEN(im->interface))
+ igmp_send_report(im->interface->dev, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
+#endif
+}
+
+static void igmp_group_added(struct ip_mc_list *im)
+{
+ if (im->loaded == 0) {
+ im->loaded = 1;
+ ip_mc_filter_add(im->interface, im->multiaddr);
+ }
+
+#ifdef CONFIG_IP_MULTICAST
+ if (im->multiaddr == IGMP_ALL_HOSTS)
+ return;
+
+ start_bh_atomic();
+ igmp_start_timer(im, IGMP_Initial_Report_Delay);
+ end_bh_atomic();
+#endif
+}
+
+
+/*
+ * Multicast list managers
+ */
+
+
+/*
+ * A socket has joined a multicast group on device dev.
+ */
+
+void ip_mc_inc_group(struct in_device *in_dev, u32 addr)
+{
+ struct ip_mc_list *i, *im;
+
+ im = (struct ip_mc_list *)kmalloc(sizeof(*im), GFP_KERNEL);
+
+ for (i=in_dev->mc_list; i; i=i->next) {
+ if (i->multiaddr == addr) {
+ i->users++;
+ if (im)
+ kfree(im);
+ return;
+ }
+ }
+ if (!im)
+ return;
+ im->users=1;
+ im->interface=in_dev;
+ im->multiaddr=addr;
+#ifdef CONFIG_IP_MULTICAST
+ im->tm_running=0;
+ init_timer(&im->timer);
+ im->timer.data=(unsigned long)im;
+ im->timer.function=&igmp_timer_expire;
+ im->unsolicit_count = IGMP_Unsolicited_Report_Count;
+ im->reporter = 0;
+ im->loaded = 0;
+#endif
+ im->next=in_dev->mc_list;
+ in_dev->mc_list=im;
+ igmp_group_added(im);
+ if (in_dev->dev->flags & IFF_UP)
+ ip_rt_multicast_event(in_dev);
+ return;
+}
+
+/*
+ * A socket has left a multicast group on device dev
+ */
+
+int ip_mc_dec_group(struct in_device *in_dev, u32 addr)
+{
+ struct ip_mc_list *i, **ip;
+
+ for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) {
+ if (i->multiaddr==addr) {
+ if (--i->users == 0) {
+ *ip = i->next;
+ synchronize_bh();
+
+ igmp_group_dropped(i);
+ if (in_dev->dev->flags & IFF_UP)
+ ip_rt_multicast_event(in_dev);
+ kfree_s(i, sizeof(*i));
+ }
+ return 0;
+ }
+ }
+ return -ESRCH;
+}
+
+/* Device going down */
+
+void ip_mc_down(struct in_device *in_dev)
+{
+ struct ip_mc_list *i;
+
+ for (i=in_dev->mc_list; i; i=i->next)
+ igmp_group_dropped(i);
+
+ ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS);
+}
+
+/* Device going up */
+
+void ip_mc_up(struct in_device *in_dev)
+{
+ struct ip_mc_list *i;
+
+ ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS);
+
+ for (i=in_dev->mc_list; i; i=i->next)
+ igmp_group_added(i);
+}
+
+/*
+ * Device is about to be destroyed: clean up.
+ */
+
+void ip_mc_destroy_dev(struct in_device *in_dev)
+{
+ struct ip_mc_list *i;
+
+ while ((i = in_dev->mc_list) != NULL) {
+ in_dev->mc_list = i->next;
+ igmp_group_dropped(i);
+ kfree_s(i, sizeof(*i));
+ }
+}
+
+static struct in_device * ip_mc_find_dev(struct ip_mreqn *imr)
+{
+ struct rtable *rt;
+ struct device *dev = NULL;
+
+ if (imr->imr_address.s_addr) {
+ dev = ip_dev_find(imr->imr_address.s_addr);
+ if (!dev)
+ return NULL;
+ }
+
+ if (!dev && !ip_route_output(&rt, imr->imr_multiaddr.s_addr, 0, 0, 0)) {
+ dev = rt->u.dst.dev;
+ ip_rt_put(rt);
+ }
+ if (dev) {
+ imr->imr_ifindex = dev->ifindex;
+ return dev->ip_ptr;
+ }
+ return NULL;
+}
+
+/*
+ * Join a socket to a group
+ */
+int sysctl_igmp_max_memberships = IP_MAX_MEMBERSHIPS;
+
+int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
+{
+ int err;
+ u32 addr = imr->imr_multiaddr.s_addr;
+ struct ip_mc_socklist *iml, *i;
+ struct in_device *in_dev;
+ int count = 0;
+
+ if (!MULTICAST(addr))
+ return -EINVAL;
+
+ rtnl_shlock();
+
+ if (!imr->imr_ifindex)
+ in_dev = ip_mc_find_dev(imr);
+ else
+ in_dev = inetdev_by_index(imr->imr_ifindex);
+
+ if (!in_dev) {
+ iml = NULL;
+ err = -ENODEV;
+ goto done;
+ }
+
+ iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL);
+
+ err = -EADDRINUSE;
+ for (i=sk->ip_mc_list; i; i=i->next) {
+ if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) {
+ /* New style additions are reference counted */
+ if (imr->imr_address.s_addr == 0) {
+ i->count++;
+ err = 0;
+ }
+ goto done;
+ }
+ count++;
+ }
+ err = -ENOBUFS;
+ if (iml == NULL || count >= sysctl_igmp_max_memberships)
+ goto done;
+ memcpy(&iml->multi, imr, sizeof(*imr));
+ iml->next = sk->ip_mc_list;
+ iml->count = 1;
+ sk->ip_mc_list = iml;
+ ip_mc_inc_group(in_dev, addr);
+ iml = NULL;
+ err = 0;
+done:
+ rtnl_shunlock();
+ if (iml)
+ sock_kfree_s(sk, iml, sizeof(*iml));
+ return err;
+}
+
+/*
+ * Ask a socket to leave a group.
+ */
+
+int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
+{
+ struct ip_mc_socklist *iml, **imlp;
+
+ for (imlp=&sk->ip_mc_list; (iml=*imlp)!=NULL; imlp=&iml->next) {
+ if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr &&
+ iml->multi.imr_address.s_addr==imr->imr_address.s_addr &&
+ (!imr->imr_ifindex || iml->multi.imr_ifindex==imr->imr_ifindex)) {
+ struct in_device *in_dev;
+ if (--iml->count)
+ return 0;
+
+ *imlp = iml->next;
+ synchronize_bh();
+
+ in_dev = inetdev_by_index(iml->multi.imr_ifindex);
+ if (in_dev)
+ ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr);
+ sock_kfree_s(sk, iml, sizeof(*iml));
+ return 0;
+ }
+ }
+ return -EADDRNOTAVAIL;
+}
+
+/*
+ * A socket is closing.
+ */
+
+void ip_mc_drop_socket(struct sock *sk)
+{
+ struct ip_mc_socklist *iml;
+
+ while ((iml=sk->ip_mc_list) != NULL) {
+ struct in_device *in_dev;
+ sk->ip_mc_list = iml->next;
+ if ((in_dev = inetdev_by_index(iml->multi.imr_ifindex)) != NULL)
+ ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
+ sock_kfree_s(sk, iml, sizeof(*iml));
+ }
+}
+
+
+#ifdef CONFIG_IP_MULTICAST
+
+int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ off_t pos=0, begin=0;
+ struct ip_mc_list *im;
+ int len=0;
+ struct device *dev;
+
+ len=sprintf(buffer,"Idx\tDevice : Count Querier\tGroup Users Timer\tReporter\n");
+
+ for(dev = dev_base; dev; dev = dev->next)
+ {
+ struct in_device *in_dev = dev->ip_ptr;
+ char *querier = "NONE";
+
+ if (in_dev == NULL)
+ continue;
+
+ querier = IGMP_V1_SEEN(in_dev) ? "V1" : "V2";
+
+ len+=sprintf(buffer+len,"%d\t%-10s: %5d %7s\n",
+ dev->ifindex, dev->name, dev->mc_count, querier);
+
+ for (im = in_dev->mc_list; im; im = im->next) {
+ len+=sprintf(buffer+len,
+ "\t\t\t\t%08lX %5d %d:%08lX\t\t%d\n",
+ im->multiaddr, im->users,
+ im->tm_running, im->timer.expires-jiffies, im->reporter);
+
+ pos=begin+len;
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ goto done;
+ }
+ }
+done:
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ if(len<0)
+ len=0;
+ return len;
+}
+#endif
diff --git a/pfinet/linux-src/net/ipv4/ip_forward.c b/pfinet/linux-src/net/ipv4/ip_forward.c
new file mode 100644
index 00000000..fd8c0435
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_forward.c
@@ -0,0 +1,292 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * The IP forwarding functionality.
+ *
+ * Version: $Id: ip_forward.c,v 1.43.2.1 1999/11/16 06:33:43 davem Exp $
+ *
+ * Authors: see ip.c
+ *
+ * Fixes:
+ * Joseph Gooch : Removed maddr selection for ip_masq, now done in ip_masq.c
+ * Many : Split from ip.c , see ip_input.c for
+ * history.
+ * Dave Gregorich : NULL ip_rt_put fix for multicast
+ * routing.
+ * Jos Vos : Add call_out_firewall before sending,
+ * use output device for accounting.
+ * Jos Vos : Call forward firewall after routing
+ * (always use output device).
+ * Mike McLagan : Routing by source
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
+#include <linux/netdevice.h>
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <net/icmp.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/firewall.h>
+#include <linux/ip_fw.h>
+#ifdef CONFIG_IP_MASQUERADE
+#include <net/ip_masq.h>
+#endif
+#include <net/checksum.h>
+#include <linux/route.h>
+#include <net/route.h>
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+/*
+ * Check the packet against our socket administration to see
+ * if it is related to a connection on our system.
+ * Needed for transparent proxying.
+ */
+
+int ip_chksock(struct sk_buff *skb)
+{
+ switch (skb->nh.iph->protocol) {
+ case IPPROTO_ICMP:
+ return icmp_chkaddr(skb);
+ case IPPROTO_TCP:
+ return tcp_chkaddr(skb);
+ case IPPROTO_UDP:
+ return udp_chkaddr(skb);
+ default:
+ return 0;
+ }
+}
+#endif
+
+
+int ip_forward(struct sk_buff *skb)
+{
+ struct device *dev2; /* Output device */
+ struct iphdr *iph; /* Our header */
+ struct rtable *rt; /* Route we use */
+ struct ip_options * opt = &(IPCB(skb)->opt);
+ unsigned short mtu;
+#if defined(CONFIG_FIREWALL) || defined(CONFIG_IP_MASQUERADE)
+ int fw_res = 0;
+#endif
+
+ if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
+ return 0;
+
+ if (skb->pkt_type != PACKET_HOST)
+ goto drop;
+
+ /*
+ * According to the RFC, we must first decrease the TTL field. If
+ * that reaches zero, we must reply an ICMP control message telling
+ * that the packet's lifetime expired.
+ */
+
+ iph = skb->nh.iph;
+ rt = (struct rtable*)skb->dst;
+
+#ifdef CONFIG_CPU_IS_SLOW
+ if (net_cpu_congestion > 1 && !(iph->tos&IPTOS_RELIABILITY) &&
+ IPTOS_PREC(iph->tos) < IPTOS_PREC_INTERNETCONTROL) {
+ if (((xtime.tv_usec&0xF)<<net_cpu_congestion) > 0x1C)
+ goto drop;
+ }
+#endif
+
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ if (ip_chksock(skb))
+ goto local_pkt;
+#endif
+
+ if (iph->ttl <= 1)
+ goto too_many_hops;
+
+ if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
+ goto sr_failed;
+
+ /*
+ * Having picked a route we can now send the frame out
+ * after asking the firewall permission to do so.
+ */
+
+ skb->priority = rt_tos2priority(iph->tos);
+ dev2 = rt->u.dst.dev;
+ mtu = rt->u.dst.pmtu;
+
+#ifdef CONFIG_NET_SECURITY
+ call_fw_firewall(PF_SECURITY, dev2, NULL, &mtu, NULL);
+#endif
+
+ /*
+ * We now generate an ICMP HOST REDIRECT giving the route
+ * we calculated.
+ */
+ if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr)
+ ip_rt_send_redirect(skb);
+
+ /* We are about to mangle packet. Copy it! */
+ if ((skb = skb_cow(skb, dev2->hard_header_len)) == NULL)
+ return -1;
+ iph = skb->nh.iph;
+ opt = &(IPCB(skb)->opt);
+
+ /* Decrease ttl after skb cow done */
+ ip_decrease_ttl(iph);
+
+ /*
+ * We now may allocate a new buffer, and copy the datagram into it.
+ * If the indicated interface is up and running, kick it.
+ */
+
+ if (skb->len > mtu && (ntohs(iph->frag_off) & IP_DF))
+ goto frag_needed;
+
+#ifdef CONFIG_IP_ROUTE_NAT
+ if (rt->rt_flags & RTCF_NAT) {
+ if (ip_do_nat(skb)) {
+ kfree_skb(skb);
+ return -1;
+ }
+ }
+#endif
+
+#ifdef CONFIG_IP_MASQUERADE
+ if(!(IPCB(skb)->flags&IPSKB_MASQUERADED)) {
+ /*
+ * Check that any ICMP packets are not for a
+ * masqueraded connection. If so rewrite them
+ * and skip the firewall checks
+ */
+ if (iph->protocol == IPPROTO_ICMP) {
+#ifdef CONFIG_IP_MASQUERADE_ICMP
+ struct icmphdr *icmph = (struct icmphdr *)((char*)iph + (iph->ihl << 2));
+ if ((icmph->type==ICMP_DEST_UNREACH)||
+ (icmph->type==ICMP_SOURCE_QUENCH)||
+ (icmph->type==ICMP_TIME_EXCEEDED))
+ {
+#endif
+ fw_res = ip_fw_masquerade(&skb, 0);
+ if (fw_res < 0) {
+ kfree_skb(skb);
+ return -1;
+ }
+
+ if (fw_res)
+ /* ICMP matched - skip firewall */
+ goto skip_call_fw_firewall;
+#ifdef CONFIG_IP_MASQUERADE_ICMP
+ }
+#endif
+ }
+ if (rt->rt_flags&RTCF_MASQ)
+ goto skip_call_fw_firewall;
+#endif /* CONFIG_IP_MASQUERADE */
+
+#ifdef CONFIG_FIREWALL
+ fw_res=call_fw_firewall(PF_INET, dev2, iph, NULL, &skb);
+ switch (fw_res) {
+ case FW_ACCEPT:
+ case FW_MASQUERADE:
+ break;
+ case FW_REJECT:
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+ /* fall thru */
+ default:
+ kfree_skb(skb);
+ return -1;
+ }
+#endif
+
+#ifdef CONFIG_IP_MASQUERADE
+ }
+
+skip_call_fw_firewall:
+ /*
+ * If this fragment needs masquerading, make it so...
+ * (Don't masquerade de-masqueraded fragments)
+ */
+ if (!(IPCB(skb)->flags&IPSKB_MASQUERADED) &&
+ (fw_res==FW_MASQUERADE || rt->rt_flags&RTCF_MASQ)) {
+ u32 maddr = 0;
+
+#ifdef CONFIG_IP_ROUTE_NAT
+ maddr = (rt->rt_flags&RTCF_MASQ) ? rt->rt_src_map : 0;
+#endif
+ if (ip_fw_masquerade(&skb, maddr) < 0) {
+ kfree_skb(skb);
+ return -1;
+ } else {
+ /*
+ * Masquerader may have changed skb
+ */
+ iph = skb->nh.iph;
+ opt = &(IPCB(skb)->opt);
+ }
+ }
+#endif
+
+
+#ifdef CONFIG_FIREWALL
+ if ((fw_res = call_out_firewall(PF_INET, dev2, iph, NULL,&skb)) < FW_ACCEPT) {
+ /* FW_ACCEPT and FW_MASQUERADE are treated equal:
+ masquerading is only supported via forward rules */
+ if (fw_res == FW_REJECT)
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+ kfree_skb(skb);
+ return -1;
+ }
+#endif
+
+ ip_statistics.IpForwDatagrams++;
+
+ if (opt->optlen == 0) {
+#ifdef CONFIG_NET_FASTROUTE
+ if (rt->rt_flags&RTCF_FAST && !netdev_fastroute_obstacles) {
+ unsigned h = ((*(u8*)&rt->key.dst)^(*(u8*)&rt->key.src))&NETDEV_FASTROUTE_HMASK;
+ /* Time to switch to functional programming :-) */
+ dst_release_irqwait(xchg(&skb->dev->fastpath[h], dst_clone(&rt->u.dst)));
+ }
+#endif
+ ip_send(skb);
+ return 0;
+ }
+
+ ip_forward_options(skb);
+ ip_send(skb);
+ return 0;
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+local_pkt:
+ return ip_local_deliver(skb);
+#endif
+
+frag_needed:
+ ip_statistics.IpFragFails++;
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
+ goto drop;
+
+sr_failed:
+ /*
+ * Strict routing permits no gatewaying
+ */
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);
+ goto drop;
+
+too_many_hops:
+ /* Tell the sender its packet died... */
+ icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
+drop:
+ kfree_skb(skb);
+ return -1;
+}
diff --git a/pfinet/linux-src/net/ipv4/ip_fragment.c b/pfinet/linux-src/net/ipv4/ip_fragment.c
new file mode 100644
index 00000000..f066e607
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_fragment.c
@@ -0,0 +1,593 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * The IP fragmentation functionality.
+ *
+ * Version: $Id: ip_fragment.c,v 1.40 1999/03/20 23:58:34 davem Exp $
+ *
+ * Authors: Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
+ * Alan Cox <Alan.Cox@linux.org>
+ *
+ * Fixes:
+ * Alan Cox : Split from ip.c , see ip_input.c for history.
+ * David S. Miller : Begin massive cleanup...
+ * Andi Kleen : Add sysctls.
+ * xxxx : Overlapfrag bug.
+ * Ultima : ip_expire() kernel panic.
+ * Bill Hawes : Frag accounting and evictor fixes.
+ * John McDonald : 0 length frag bug.
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
+#include <linux/netdevice.h>
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/inet.h>
+#include <linux/firewall.h>
+#include <linux/ip_fw.h>
+
+/* Fragment cache limits. We will commit 256K at one time. Should we
+ * cross that limit we will prune down to 192K. This should cope with
+ * even the most extreme cases without allowing an attacker to measurably
+ * harm machine performance.
+ */
+int sysctl_ipfrag_high_thresh = 256*1024;
+int sysctl_ipfrag_low_thresh = 192*1024;
+
+int sysctl_ipfrag_time = IP_FRAG_TIME;
+
+/* Describe an IP fragment. */
+struct ipfrag {
+ int offset; /* offset of fragment in IP datagram */
+ int end; /* last byte of data in datagram */
+ int len; /* length of this fragment */
+ struct sk_buff *skb; /* complete received fragment */
+ unsigned char *ptr; /* pointer into real fragment data */
+ struct ipfrag *next; /* linked list pointers */
+ struct ipfrag *prev;
+};
+
+/* Describe an entry in the "incomplete datagrams" queue. */
+struct ipq {
+ struct iphdr *iph; /* pointer to IP header */
+ struct ipq *next; /* linked list pointers */
+ struct ipfrag *fragments; /* linked list of received fragments */
+ int len; /* total length of original datagram */
+ short ihlen; /* length of the IP header */
+ struct timer_list timer; /* when will this queue expire? */
+ struct ipq **pprev;
+ struct device *dev; /* Device - for icmp replies */
+};
+
+#define IPQ_HASHSZ 64
+
+struct ipq *ipq_hash[IPQ_HASHSZ];
+
+#define ipqhashfn(id, saddr, daddr, prot) \
+ ((((id) >> 1) ^ (saddr) ^ (daddr) ^ (prot)) & (IPQ_HASHSZ - 1))
+
+atomic_t ip_frag_mem = ATOMIC_INIT(0); /* Memory used for fragments */
+
+/* Memory Tracking Functions. */
+extern __inline__ void frag_kfree_skb(struct sk_buff *skb)
+{
+ atomic_sub(skb->truesize, &ip_frag_mem);
+ kfree_skb(skb);
+}
+
+extern __inline__ void frag_kfree_s(void *ptr, int len)
+{
+ atomic_sub(len, &ip_frag_mem);
+ kfree(ptr);
+}
+
+extern __inline__ void *frag_kmalloc(int size, int pri)
+{
+ void *vp = kmalloc(size, pri);
+
+ if(!vp)
+ return NULL;
+ atomic_add(size, &ip_frag_mem);
+ return vp;
+}
+
+/* Create a new fragment entry. */
+static struct ipfrag *ip_frag_create(int offset, int end,
+ struct sk_buff *skb, unsigned char *ptr)
+{
+ struct ipfrag *fp;
+
+ fp = (struct ipfrag *) frag_kmalloc(sizeof(struct ipfrag), GFP_ATOMIC);
+ if (fp == NULL)
+ goto out_nomem;
+
+ /* Fill in the structure. */
+ fp->offset = offset;
+ fp->end = end;
+ fp->len = end - offset;
+ fp->skb = skb;
+ fp->ptr = ptr;
+ fp->next = fp->prev = NULL;
+
+ /* Charge for the SKB as well. */
+ atomic_add(skb->truesize, &ip_frag_mem);
+
+ return(fp);
+
+out_nomem:
+ NETDEBUG(printk(KERN_ERR "IP: frag_create: no memory left !\n"));
+ return(NULL);
+}
+
+/* Find the correct entry in the "incomplete datagrams" queue for
+ * this IP datagram, and return the queue entry address if found.
+ */
+static inline struct ipq *ip_find(struct iphdr *iph, struct dst_entry *dst)
+{
+ __u16 id = iph->id;
+ __u32 saddr = iph->saddr;
+ __u32 daddr = iph->daddr;
+ __u8 protocol = iph->protocol;
+ unsigned int hash = ipqhashfn(id, saddr, daddr, protocol);
+ struct ipq *qp;
+
+ /* Always, we are in a BH context, so no locking. -DaveM */
+ for(qp = ipq_hash[hash]; qp; qp = qp->next) {
+ if(qp->iph->id == id &&
+ qp->iph->saddr == saddr &&
+ qp->iph->daddr == daddr &&
+ qp->iph->protocol == protocol) {
+ del_timer(&qp->timer);
+ break;
+ }
+ }
+ return qp;
+}
+
+/* Remove an entry from the "incomplete datagrams" queue, either
+ * because we completed, reassembled and processed it, or because
+ * it timed out.
+ *
+ * This is called _only_ from BH contexts, on packet reception
+ * processing and from frag queue expiration timers. -DaveM
+ */
+static void ip_free(struct ipq *qp)
+{
+ struct ipfrag *fp;
+
+ /* Stop the timer for this entry. */
+ del_timer(&qp->timer);
+
+ /* Remove this entry from the "incomplete datagrams" queue. */
+ if(qp->next)
+ qp->next->pprev = qp->pprev;
+ *qp->pprev = qp->next;
+
+ /* Release all fragment data. */
+ fp = qp->fragments;
+ while (fp) {
+ struct ipfrag *xp = fp->next;
+
+ frag_kfree_skb(fp->skb);
+ frag_kfree_s(fp, sizeof(struct ipfrag));
+ fp = xp;
+ }
+
+ /* Release the IP header. */
+ frag_kfree_s(qp->iph, 64 + 8);
+
+ /* Finally, release the queue descriptor itself. */
+ frag_kfree_s(qp, sizeof(struct ipq));
+}
+
+/*
+ * Oops, a fragment queue timed out. Kill it and send an ICMP reply.
+ */
+static void ip_expire(unsigned long arg)
+{
+ struct ipq *qp = (struct ipq *) arg;
+
+ if(!qp->fragments)
+ {
+#ifdef IP_EXPIRE_DEBUG
+ printk("warning: possible ip-expire attack\n");
+#endif
+ goto out;
+ }
+
+ /* Send an ICMP "Fragment Reassembly Timeout" message. */
+ ip_statistics.IpReasmTimeout++;
+ ip_statistics.IpReasmFails++;
+ icmp_send(qp->fragments->skb, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0);
+
+out:
+ /* Nuke the fragment queue. */
+ ip_free(qp);
+}
+
+/* Memory limiting on fragments. Evictor trashes the oldest
+ * fragment queue until we are back under the low threshold.
+ */
+static void ip_evictor(void)
+{
+ int i, progress;
+
+restart:
+ progress = 0;
+ /* FIXME: Make LRU queue of frag heads. -DaveM */
+ for (i = 0; i < IPQ_HASHSZ; i++) {
+ struct ipq *qp;
+ if (atomic_read(&ip_frag_mem) <= sysctl_ipfrag_low_thresh)
+ return;
+ /* We are in a BH context, so these queue
+ * accesses are safe. -DaveM
+ */
+ qp = ipq_hash[i];
+ if (qp) {
+ /* find the oldest queue for this hash bucket */
+ while (qp->next)
+ qp = qp->next;
+ ip_free(qp);
+ progress = 1;
+ }
+ }
+ if (progress)
+ goto restart;
+ panic("ip_evictor: memcount");
+}
+
+/* Add an entry to the 'ipq' queue for a newly received IP datagram.
+ * We will (hopefully :-) receive all other fragments of this datagram
+ * in time, so we just create a queue for this datagram, in which we
+ * will insert the received fragments at their respective positions.
+ */
+static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph)
+{
+ struct ipq *qp;
+ unsigned int hash;
+ int ihlen;
+
+ qp = (struct ipq *) frag_kmalloc(sizeof(struct ipq), GFP_ATOMIC);
+ if (qp == NULL)
+ goto out_nomem;
+
+ /* Allocate memory for the IP header (plus 8 octets for ICMP). */
+ ihlen = iph->ihl * 4;
+
+ qp->iph = (struct iphdr *) frag_kmalloc(64 + 8, GFP_ATOMIC);
+ if (qp->iph == NULL)
+ goto out_free;
+
+ memcpy(qp->iph, iph, ihlen + 8);
+ qp->len = 0;
+ qp->ihlen = ihlen;
+ qp->fragments = NULL;
+ qp->dev = skb->dev;
+
+ /* Initialize a timer for this entry. */
+ init_timer(&qp->timer);
+ qp->timer.expires = 0; /* (to be set later) */
+ qp->timer.data = (unsigned long) qp; /* pointer to queue */
+ qp->timer.function = ip_expire; /* expire function */
+
+ /* Add this entry to the queue. */
+ hash = ipqhashfn(iph->id, iph->saddr, iph->daddr, iph->protocol);
+
+ /* We are in a BH context, no locking necessary. -DaveM */
+ if((qp->next = ipq_hash[hash]) != NULL)
+ qp->next->pprev = &qp->next;
+ ipq_hash[hash] = qp;
+ qp->pprev = &ipq_hash[hash];
+
+ return qp;
+
+out_free:
+ frag_kfree_s(qp, sizeof(struct ipq));
+out_nomem:
+ NETDEBUG(printk(KERN_ERR "IP: create: no memory left !\n"));
+ return(NULL);
+}
+
+/* See if a fragment queue is complete. */
+static int ip_done(struct ipq *qp)
+{
+ struct ipfrag *fp;
+ int offset;
+
+ /* Only possible if we received the final fragment. */
+ if (qp->len == 0)
+ return 0;
+
+ /* Check all fragment offsets to see if they connect. */
+ fp = qp->fragments;
+ offset = 0;
+ while (fp) {
+ if (fp->offset > offset)
+ return(0); /* fragment(s) missing */
+ offset = fp->end;
+ fp = fp->next;
+ }
+
+ /* All fragments are present. */
+ return 1;
+}
+
+/* Build a new IP datagram from all its fragments.
+ *
+ * FIXME: We copy here because we lack an effective way of handling lists
+ * of bits on input. Until the new skb data handling is in I'm not going
+ * to touch this with a bargepole.
+ */
+static struct sk_buff *ip_glue(struct ipq *qp)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ struct ipfrag *fp;
+ unsigned char *ptr;
+ int count, len;
+
+ /* Allocate a new buffer for the datagram. */
+ len = qp->ihlen + qp->len;
+
+ if(len > 65535)
+ goto out_oversize;
+
+ skb = dev_alloc_skb(len);
+ if (!skb)
+ goto out_nomem;
+
+ /* Fill in the basic details. */
+ skb->mac.raw = ptr = skb->data;
+ skb->nh.iph = iph = (struct iphdr *) skb_put(skb, len);
+
+ /* Copy the original IP headers into the new buffer. */
+ memcpy(ptr, qp->iph, qp->ihlen);
+ ptr += qp->ihlen;
+
+ /* Copy the data portions of all fragments into the new buffer. */
+ fp = qp->fragments;
+ count = qp->ihlen;
+ while(fp) {
+ if ((fp->len <= 0) || ((count + fp->len) > skb->len))
+ goto out_invalid;
+ memcpy((ptr + fp->offset), fp->ptr, fp->len);
+ if (count == qp->ihlen) {
+ skb->dst = dst_clone(fp->skb->dst);
+ skb->dev = fp->skb->dev;
+ }
+ count += fp->len;
+ fp = fp->next;
+ }
+
+ skb->pkt_type = qp->fragments->skb->pkt_type;
+ skb->protocol = qp->fragments->skb->protocol;
+ /*
+ * Clearly bogus, because security markings of the individual
+ * fragments should have been checked for consistency before
+ * gluing, and intermediate coalescing of fragments may have
+ * taken place in ip_defrag() before ip_glue() ever got called.
+ * If we're not going to do the consistency checking, we might
+ * as well take the value associated with the first fragment.
+ * --rct
+ */
+ skb->security = qp->fragments->skb->security;
+
+ /* Done with all fragments. Fixup the new IP header. */
+ iph = skb->nh.iph;
+ iph->frag_off = 0;
+ iph->tot_len = htons(count);
+ ip_statistics.IpReasmOKs++;
+ return skb;
+
+out_invalid:
+ NETDEBUG(printk(KERN_ERR
+ "Invalid fragment list: Fragment over size.\n"));
+ kfree_skb(skb);
+ goto out_fail;
+out_nomem:
+ NETDEBUG(printk(KERN_ERR
+ "IP: queue_glue: no memory for gluing queue %p\n",
+ qp));
+ goto out_fail;
+out_oversize:
+ if (net_ratelimit())
+ printk(KERN_INFO
+ "Oversized IP packet from %d.%d.%d.%d.\n",
+ NIPQUAD(qp->iph->saddr));
+out_fail:
+ ip_statistics.IpReasmFails++;
+ return NULL;
+}
+
+/* Process an incoming IP datagram fragment. */
+struct sk_buff *ip_defrag(struct sk_buff *skb)
+{
+ struct iphdr *iph = skb->nh.iph;
+ struct ipfrag *prev, *next, *tmp, *tfp;
+ struct ipq *qp;
+ unsigned char *ptr;
+ int flags, offset;
+ int i, ihl, end;
+
+ ip_statistics.IpReasmReqds++;
+
+ /* Start by cleaning up the memory. */
+ if (atomic_read(&ip_frag_mem) > sysctl_ipfrag_high_thresh)
+ ip_evictor();
+
+ /*
+ * Look for the entry for this IP datagram in the
+ * "incomplete datagrams" queue. If found, the
+ * timer is removed.
+ */
+ qp = ip_find(iph, skb->dst);
+
+ /* Is this a non-fragmented datagram? */
+ offset = ntohs(iph->frag_off);
+ flags = offset & ~IP_OFFSET;
+ offset &= IP_OFFSET;
+
+ offset <<= 3; /* offset is in 8-byte chunks */
+ ihl = iph->ihl * 4;
+
+ /*
+ * Check whether to create a fresh queue entry. If the
+ * queue already exists, its timer will be restarted as
+ * long as we continue to receive fragments.
+ */
+ if (qp) {
+ /* ANK. If the first fragment is received,
+ * we should remember the correct IP header (with options)
+ */
+ if (offset == 0) {
+ /* Fragmented frame replaced by unfragmented copy? */
+ if ((flags & IP_MF) == 0)
+ goto out_freequeue;
+ qp->ihlen = ihl;
+ memcpy(qp->iph, iph, (ihl + 8));
+ }
+ } else {
+ /* Fragmented frame replaced by unfragmented copy? */
+ if ((offset == 0) && ((flags & IP_MF) == 0))
+ goto out_skb;
+
+ /* If we failed to create it, then discard the frame. */
+ qp = ip_create(skb, iph);
+ if (!qp)
+ goto out_freeskb;
+ }
+
+ /* Attempt to construct an oversize packet. */
+ if((ntohs(iph->tot_len) + ((int) offset)) > 65535)
+ goto out_oversize;
+
+ /* Determine the position of this fragment. */
+ end = offset + ntohs(iph->tot_len) - ihl;
+
+ /* Is this the final fragment? */
+ if ((flags & IP_MF) == 0)
+ qp->len = end;
+
+ /* Find out which fragments are in front and at the back of us
+ * in the chain of fragments so far. We must know where to put
+ * this fragment, right?
+ */
+ prev = NULL;
+ for(next = qp->fragments; next != NULL; next = next->next) {
+ if (next->offset >= offset)
+ break; /* bingo! */
+ prev = next;
+ }
+
+ /* Point into the IP datagram 'data' part. */
+ ptr = skb->data + ihl;
+
+ /* We found where to put this one. Check for overlap with
+ * preceding fragment, and, if needed, align things so that
+ * any overlaps are eliminated.
+ */
+ if ((prev != NULL) && (offset < prev->end)) {
+ i = prev->end - offset;
+ offset += i; /* ptr into datagram */
+ ptr += i; /* ptr into fragment data */
+ }
+
+ /* Look for overlap with succeeding segments.
+ * If we can merge fragments, do it.
+ */
+ for (tmp = next; tmp != NULL; tmp = tfp) {
+ tfp = tmp->next;
+ if (tmp->offset >= end)
+ break; /* no overlaps at all */
+
+ i = end - next->offset; /* overlap is 'i' bytes */
+ tmp->len -= i; /* so reduce size of */
+ tmp->offset += i; /* next fragment */
+ tmp->ptr += i;
+
+ /* If we get a frag size of <= 0, remove it and the packet
+ * that it goes with.
+ */
+ if (tmp->len <= 0) {
+ if (tmp->prev != NULL)
+ tmp->prev->next = tmp->next;
+ else
+ qp->fragments = tmp->next;
+
+ if (tmp->next != NULL)
+ tmp->next->prev = tmp->prev;
+
+ /* We have killed the original next frame. */
+ next = tfp;
+
+ frag_kfree_skb(tmp->skb);
+ frag_kfree_s(tmp, sizeof(struct ipfrag));
+ }
+ }
+
+ /*
+ * Create a fragment to hold this skb.
+ * No memory to save the fragment? throw the lot ...
+ */
+ tfp = ip_frag_create(offset, end, skb, ptr);
+ if (!tfp)
+ goto out_freeskb;
+
+ /* Insert this fragment in the chain of fragments. */
+ tfp->prev = prev;
+ tfp->next = next;
+ if (prev != NULL)
+ prev->next = tfp;
+ else
+ qp->fragments = tfp;
+
+ if (next != NULL)
+ next->prev = tfp;
+
+ /* OK, so we inserted this new fragment into the chain.
+ * Check if we now have a full IP datagram which we can
+ * bump up to the IP layer...
+ */
+ if (ip_done(qp)) {
+ /* Glue together the fragments. */
+ skb = ip_glue(qp);
+ /* Free the queue entry. */
+out_freequeue:
+ ip_free(qp);
+out_skb:
+ return skb;
+ }
+
+ /*
+ * The queue is still active ... reset its timer.
+ */
+out_timer:
+ mod_timer(&qp->timer, jiffies + sysctl_ipfrag_time); /* ~ 30 seconds */
+out:
+ return NULL;
+
+ /*
+ * Error exits ... we need to reset the timer if there's a queue.
+ */
+out_oversize:
+ if (net_ratelimit())
+ printk(KERN_INFO "Oversized packet received from %d.%d.%d.%d\n",
+ NIPQUAD(iph->saddr));
+ /* the skb isn't in a fragment, so fall through to free it */
+out_freeskb:
+ kfree_skb(skb);
+ ip_statistics.IpReasmFails++;
+ if (qp)
+ goto out_timer;
+ goto out;
+}
diff --git a/pfinet/linux-src/net/ipv4/ip_fw.c b/pfinet/linux-src/net/ipv4/ip_fw.c
new file mode 100644
index 00000000..73af70ae
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_fw.c
@@ -0,0 +1,1773 @@
+/*
+ * This code is heavily based on the code on the old ip_fw.c code; see below for
+ * copyrights and attributions of the old code. This code is basically GPL.
+ *
+ * 15-Aug-1997: Major changes to allow graphs for firewall rules.
+ * Paul Russell <Paul.Russell@rustcorp.com.au> and
+ * Michael Neuling <Michael.Neuling@rustcorp.com.au>
+ * 24-Aug-1997: Generalised protocol handling (not just TCP/UDP/ICMP).
+ * Added explicit RETURN from chains.
+ * Removed TOS mangling (done in ipchains 1.0.1).
+ * Fixed read & reset bug by reworking proc handling.
+ * Paul Russell <Paul.Russell@rustcorp.com.au>
+ * 28-Sep-1997: Added packet marking for net sched code.
+ * Removed fw_via comparisons: all done on device name now,
+ * similar to changes in ip_fw.c in DaveM's CVS970924 tree.
+ * Paul Russell <Paul.Russell@rustcorp.com.au>
+ * 2-Nov-1997: Moved types across to __u16, etc.
+ * Added inverse flags.
+ * Fixed fragment bug (in args to port_match).
+ * Changed mark to only one flag (MARKABS).
+ * 21-Nov-1997: Added ability to test ICMP code.
+ * 19-Jan-1998: Added wildcard interfaces.
+ * 6-Feb-1998: Merged 2.0 and 2.1 versions.
+ * Initialised ip_masq for 2.0.x version.
+ * Added explicit NETLINK option for 2.1.x version.
+ * Added packet and byte counters for policy matches.
+ * 26-Feb-1998: Fixed race conditions, added SMP support.
+ * 18-Mar-1998: Fix SMP, fix race condition fix.
+ * 1-May-1998: Remove caching of device pointer.
+ * 12-May-1998: Allow tiny fragment case for TCP/UDP.
+ * 15-May-1998: Treat short packets as fragments, don't just block.
+ * 3-Jan-1999: Fixed serious procfs security hole -- users should never
+ * be allowed to view the chains!
+ * Marc Santoro <ultima@snicker.emoti.com>
+ * 29-Jan-1999: Locally generated bogus IPs dealt with, rather than crash
+ * during dump_packet. --RR.
+ * 19-May-1999: Star Wars: The Phantom Menace opened. Rule num
+ * printed in log (modified from Michael Hasenstein's patch).
+ * Added SYN in log message. --RR
+ * 23-Jul-1999: Fixed small fragment security exposure opened on 15-May-1998.
+ * John McDonald <jm@dataprotect.com>
+ * Thomas Lopatic <tl@dataprotect.com>
+ * 21-Oct-1999: Applied count fix by Emanuele Caratti <wiz@iol.it> --RR
+ */
+
+/*
+ *
+ * The original Linux port was done Alan Cox, with changes/fixes from
+ * Pauline Middlelink, Jos Vos, Thomas Quinot, Wouter Gadeyne, Juan
+ * Jose Ciarlante, Bernd Eckenfels, Keith Owens and others.
+ *
+ * Copyright from the original FreeBSD version follows:
+ *
+ * Copyright (c) 1993 Daniel Boulet
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind. */
+
+
+#include <linux/config.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/icmp.h>
+#include <linux/udp.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <net/sock.h>
+#include <net/icmp.h>
+#include <net/ip_masq.h>
+#include <linux/netlink.h>
+#include <linux/init.h>
+#include <linux/firewall.h>
+#include <linux/ip_fw.h>
+
+#ifdef CONFIG_IP_MASQUERADE
+#include <net/ip_masq.h>
+#endif
+
+#include <net/checksum.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+
+/* Understanding locking in this code: (thanks to Alan Cox for using
+ * little words to explain this to me). -- PR
+ *
+ * In UP, there can be two packets traversing the chains:
+ * 1) A packet from the current userspace context
+ * 2) A packet off the bh handlers (timer or net).
+ *
+ * For SMP (kernel v2.1+), multiply this by # CPUs.
+ *
+ * [Note that this in not correct for 2.2 - because the socket code always
+ * uses lock_kernel() to serialize, and bottom halves (timers and net_bhs)
+ * only run on one CPU at a time. This will probably change for 2.3.
+ * It is still good to use spinlocks because that avoids the global cli()
+ * for updating the tables, which is rather costly in SMP kernels -AK]
+ *
+ * This means counters and backchains can get corrupted if no precautions
+ * are taken.
+ *
+ * To actually alter a chain on UP, we need only do a cli(), as this will
+ * stop a bh handler firing, as we are in the current userspace context
+ * (coming from a setsockopt()).
+ *
+ * On SMP, we need a write_lock_irqsave(), which is a simple cli() in
+ * UP.
+ *
+ * For backchains and counters, we use an array, indexed by
+ * [cpu_number_map[smp_processor_id()]*2 + !in_interrupt()]; the array is of
+ * size [smp_num_cpus*2]. For v2.0, smp_num_cpus is effectively 1. So,
+ * confident of uniqueness, we modify counters even though we only
+ * have a read lock (to read the counters, you need a write lock,
+ * though). */
+
+/* Why I didn't use straight locking... -- PR
+ *
+ * The backchains can be separated out of the ip_chains structure, and
+ * allocated as needed inside ip_fw_check().
+ *
+ * The counters, however, can't. Trying to lock these means blocking
+ * interrupts every time we want to access them. This would suck HARD
+ * performance-wise. Not locking them leads to possible corruption,
+ * made worse on 32-bit machines (counters are 64-bit). */
+
+/*#define DEBUG_IP_FIREWALL*/
+/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
+/*#define DEBUG_IP_FIREWALL_USER*/
+/*#define DEBUG_IP_FIREWALL_LOCKING*/
+
+#ifdef CONFIG_IP_FIREWALL_NETLINK
+static struct sock *ipfwsk;
+#endif
+
+#ifdef __SMP__
+#define SLOT_NUMBER() (cpu_number_map[smp_processor_id()]*2 + !in_interrupt())
+#else
+#define SLOT_NUMBER() (!in_interrupt())
+#endif
+#define NUM_SLOTS (smp_num_cpus*2)
+
+#define SIZEOF_STRUCT_IP_CHAIN (sizeof(struct ip_chain) \
+ + NUM_SLOTS*sizeof(struct ip_reent))
+#define SIZEOF_STRUCT_IP_FW_KERNEL (sizeof(struct ip_fwkernel) \
+ + NUM_SLOTS*sizeof(struct ip_counters))
+
+#ifdef DEBUG_IP_FIREWALL_LOCKING
+static unsigned int fwc_rlocks, fwc_wlocks;
+#define FWC_DEBUG_LOCK(d) \
+do { \
+ FWC_DONT_HAVE_LOCK(d); \
+ d |= (1 << SLOT_NUMBER()); \
+} while (0)
+
+#define FWC_DEBUG_UNLOCK(d) \
+do { \
+ FWC_HAVE_LOCK(d); \
+ d &= ~(1 << SLOT_NUMBER()); \
+} while (0)
+
+#define FWC_DONT_HAVE_LOCK(d) \
+do { \
+ if ((d) & (1 << SLOT_NUMBER())) \
+ printk("%s:%i: Got lock on %i already!\n", \
+ __FILE__, __LINE__, SLOT_NUMBER()); \
+} while(0)
+
+#define FWC_HAVE_LOCK(d) \
+do { \
+ if (!((d) & (1 << SLOT_NUMBER()))) \
+ printk("%s:%i:No lock on %i!\n", \
+ __FILE__, __LINE__, SLOT_NUMBER()); \
+} while (0)
+
+#else
+#define FWC_DEBUG_LOCK(d) do { } while(0)
+#define FWC_DEBUG_UNLOCK(d) do { } while(0)
+#define FWC_DONT_HAVE_LOCK(d) do { } while(0)
+#define FWC_HAVE_LOCK(d) do { } while(0)
+#endif /*DEBUG_IP_FIRWALL_LOCKING*/
+
+#define FWC_READ_LOCK(l) do { FWC_DEBUG_LOCK(fwc_rlocks); read_lock(l); } while (0)
+#define FWC_WRITE_LOCK(l) do { FWC_DEBUG_LOCK(fwc_wlocks); write_lock(l); } while (0)
+#define FWC_READ_LOCK_IRQ(l,f) do { FWC_DEBUG_LOCK(fwc_rlocks); read_lock_irqsave(l,f); } while (0)
+#define FWC_WRITE_LOCK_IRQ(l,f) do { FWC_DEBUG_LOCK(fwc_wlocks); write_lock_irqsave(l,f); } while (0)
+#define FWC_READ_UNLOCK(l) do { FWC_DEBUG_UNLOCK(fwc_rlocks); read_unlock(l); } while (0)
+#define FWC_WRITE_UNLOCK(l) do { FWC_DEBUG_UNLOCK(fwc_wlocks); write_unlock(l); } while (0)
+#define FWC_READ_UNLOCK_IRQ(l,f) do { FWC_DEBUG_UNLOCK(fwc_rlocks); read_unlock_irqrestore(l,f); } while (0)
+#define FWC_WRITE_UNLOCK_IRQ(l,f) do { FWC_DEBUG_UNLOCK(fwc_wlocks); write_unlock_irqrestore(l,f); } while (0)
+
+struct ip_chain;
+
+struct ip_counters
+{
+ __u64 pcnt, bcnt; /* Packet and byte counters */
+};
+
+struct ip_fwkernel
+{
+ struct ip_fw ipfw;
+ struct ip_fwkernel *next; /* where to go next if current
+ * rule doesn't match */
+ struct ip_chain *branch; /* which branch to jump to if
+ * current rule matches */
+ int simplebranch; /* Use this if branch == NULL */
+ struct ip_counters counters[0]; /* Actually several of these */
+};
+
+struct ip_reent
+{
+ struct ip_chain *prevchain; /* Pointer to referencing chain */
+ struct ip_fwkernel *prevrule; /* Pointer to referencing rule */
+ unsigned int count;
+ struct ip_counters counters;
+};
+
+struct ip_chain
+{
+ ip_chainlabel label; /* Defines the label for each block */
+ struct ip_chain *next; /* Pointer to next block */
+ struct ip_fwkernel *chain; /* Pointer to first rule in block */
+ __u32 refcount; /* Number of references to block */
+ int policy; /* Default rule for chain. Only *
+ * used in built in chains */
+ struct ip_reent reent[0]; /* Actually several of these */
+};
+
+/*
+ * Implement IP packet firewall
+ */
+
+#ifdef DEBUG_IP_FIREWALL
+#define dprintf(format, args...) printk(format , ## args)
+#else
+#define dprintf(format, args...)
+#endif
+
+#ifdef DEBUG_IP_FIREWALL_USER
+#define duprintf(format, args...) printk(format , ## args)
+#else
+#define duprintf(format, args...)
+#endif
+
+/* Lock around ip_fw_chains linked list structure */
+rwlock_t ip_fw_lock = RW_LOCK_UNLOCKED;
+
+/* Head of linked list of fw rules */
+static struct ip_chain *ip_fw_chains;
+
+#define IP_FW_INPUT_CHAIN ip_fw_chains
+#define IP_FW_FORWARD_CHAIN (ip_fw_chains->next)
+#define IP_FW_OUTPUT_CHAIN (ip_fw_chains->next->next)
+
+/* Returns 1 if the port is matched by the range, 0 otherwise */
+extern inline int port_match(__u16 min, __u16 max, __u16 port,
+ int frag, int invert)
+{
+ if (frag) /* Fragments fail ANY port test. */
+ return (min == 0 && max == 0xFFFF);
+ else return (port >= min && port <= max) ^ invert;
+}
+
+/* Returns whether matches rule or not. */
+static int ip_rule_match(struct ip_fwkernel *f,
+ const char *ifname,
+ struct iphdr *ip,
+ char tcpsyn,
+ __u16 src_port, __u16 dst_port,
+ char isfrag)
+{
+#define FWINV(bool,invflg) ((bool) ^ !!(f->ipfw.fw_invflg & invflg))
+ /*
+ * This is a bit simpler as we don't have to walk
+ * an interface chain as you do in BSD - same logic
+ * however.
+ */
+
+ if (FWINV((ip->saddr&f->ipfw.fw_smsk.s_addr) != f->ipfw.fw_src.s_addr,
+ IP_FW_INV_SRCIP)
+ || FWINV((ip->daddr&f->ipfw.fw_dmsk.s_addr)!=f->ipfw.fw_dst.s_addr,
+ IP_FW_INV_DSTIP)) {
+ dprintf("Source or dest mismatch.\n");
+
+ dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
+ f->ipfw.fw_smsk.s_addr, f->ipfw.fw_src.s_addr,
+ f->ipfw.fw_invflg & IP_FW_INV_SRCIP ? " (INV)" : "");
+ dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
+ f->ipfw.fw_dmsk.s_addr, f->ipfw.fw_dst.s_addr,
+ f->ipfw.fw_invflg & IP_FW_INV_DSTIP ? " (INV)" : "");
+ return 0;
+ }
+
+ /*
+ * Look for a VIA device match
+ */
+ if (f->ipfw.fw_flg & IP_FW_F_WILDIF) {
+ if (FWINV(strncmp(ifname, f->ipfw.fw_vianame,
+ strlen(f->ipfw.fw_vianame)) != 0,
+ IP_FW_INV_VIA)) {
+ dprintf("Wildcard interface mismatch.%s\n",
+ f->ipfw.fw_invflg & IP_FW_INV_VIA ? " (INV)" : "");
+ return 0; /* Mismatch */
+ }
+ }
+ else if (FWINV(strcmp(ifname, f->ipfw.fw_vianame) != 0,
+ IP_FW_INV_VIA)) {
+ dprintf("Interface name does not match.%s\n",
+ f->ipfw.fw_invflg & IP_FW_INV_VIA
+ ? " (INV)" : "");
+ return 0; /* Mismatch */
+ }
+
+ /*
+ * Ok the chain addresses match.
+ */
+
+ /* If we have a fragment rule but the packet is not a fragment
+ * the we return zero */
+ if (FWINV((f->ipfw.fw_flg&IP_FW_F_FRAG) && !isfrag, IP_FW_INV_FRAG)) {
+ dprintf("Fragment rule but not fragment.%s\n",
+ f->ipfw.fw_invflg & IP_FW_INV_FRAG ? " (INV)" : "");
+ return 0;
+ }
+
+ /* Fragment NEVER passes a SYN test, even an inverted one. */
+ if (FWINV((f->ipfw.fw_flg&IP_FW_F_TCPSYN) && !tcpsyn, IP_FW_INV_SYN)
+ || (isfrag && (f->ipfw.fw_flg&IP_FW_F_TCPSYN))) {
+ dprintf("Rule requires SYN and packet has no SYN.%s\n",
+ f->ipfw.fw_invflg & IP_FW_INV_SYN ? " (INV)" : "");
+ return 0;
+ }
+
+ if (f->ipfw.fw_proto) {
+ /*
+ * Specific firewall - packet's protocol
+ * must match firewall's.
+ */
+
+ if (FWINV(ip->protocol!=f->ipfw.fw_proto, IP_FW_INV_PROTO)) {
+ dprintf("Packet protocol %hi does not match %hi.%s\n",
+ ip->protocol, f->ipfw.fw_proto,
+ f->ipfw.fw_invflg&IP_FW_INV_PROTO ? " (INV)":"");
+ return 0;
+ }
+
+ /* For non TCP/UDP/ICMP, port range is max anyway. */
+ if (!port_match(f->ipfw.fw_spts[0],
+ f->ipfw.fw_spts[1],
+ src_port, isfrag,
+ !!(f->ipfw.fw_invflg&IP_FW_INV_SRCPT))
+ || !port_match(f->ipfw.fw_dpts[0],
+ f->ipfw.fw_dpts[1],
+ dst_port, isfrag,
+ !!(f->ipfw.fw_invflg
+ &IP_FW_INV_DSTPT))) {
+ dprintf("Port match failed.\n");
+ return 0;
+ }
+ }
+
+ dprintf("Match succeeded.\n");
+ return 1;
+}
+
+static const char *branchname(struct ip_chain *branch,int simplebranch)
+{
+ if (branch)
+ return branch->label;
+ switch (simplebranch)
+ {
+ case FW_BLOCK: return IP_FW_LABEL_BLOCK;
+ case FW_ACCEPT: return IP_FW_LABEL_ACCEPT;
+ case FW_REJECT: return IP_FW_LABEL_REJECT;
+ case FW_REDIRECT: return IP_FW_LABEL_REDIRECT;
+ case FW_MASQUERADE: return IP_FW_LABEL_MASQUERADE;
+ case FW_SKIP: return "-";
+ case FW_SKIP+1: return IP_FW_LABEL_RETURN;
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/*
+ * VERY ugly piece of code which actually
+ * makes kernel printf for matching packets...
+ */
+static void dump_packet(const struct iphdr *ip,
+ const char *ifname,
+ struct ip_fwkernel *f,
+ const ip_chainlabel chainlabel,
+ __u16 src_port,
+ __u16 dst_port,
+ unsigned int count,
+ int syn)
+{
+ __u32 *opt = (__u32 *) (ip + 1);
+ int opti;
+
+ if (f)
+ {
+ printk(KERN_INFO "Packet log: %s ",chainlabel);
+
+ printk("%s ",branchname(f->branch,f->simplebranch));
+ if (f->simplebranch==FW_REDIRECT)
+ printk("%d ",f->ipfw.fw_redirpt);
+ }
+
+ printk("%s PROTO=%d %d.%d.%d.%d:%hu %d.%d.%d.%d:%hu"
+ " L=%hu S=0x%2.2hX I=%hu F=0x%4.4hX T=%hu",
+ ifname, ip->protocol,
+ (ntohl(ip->saddr)>>24)&0xFF,
+ (ntohl(ip->saddr)>>16)&0xFF,
+ (ntohl(ip->saddr)>>8)&0xFF,
+ (ntohl(ip->saddr))&0xFF,
+ src_port,
+ (ntohl(ip->daddr)>>24)&0xFF,
+ (ntohl(ip->daddr)>>16)&0xFF,
+ (ntohl(ip->daddr)>>8)&0xFF,
+ (ntohl(ip->daddr))&0xFF,
+ dst_port,
+ ntohs(ip->tot_len), ip->tos, ntohs(ip->id),
+ ntohs(ip->frag_off), ip->ttl);
+
+ for (opti = 0; opti < (ip->ihl - sizeof(struct iphdr) / 4); opti++)
+ printk(" O=0x%8.8X", *opt++);
+ printk(" %s(#%d)\n", syn ? "SYN " : /* "PENANCE" */ "", count);
+}
+
+/* function for checking chain labels for user space. */
+static int check_label(ip_chainlabel label)
+{
+ unsigned int i;
+ /* strlen must be < IP_FW_MAX_LABEL_LENGTH. */
+ for (i = 0; i < IP_FW_MAX_LABEL_LENGTH + 1; i++)
+ if (label[i] == '\0') return 1;
+
+ return 0;
+}
+
+/* This function returns a pointer to the first chain with a label
+ * that matches the one given. */
+static struct ip_chain *find_label(ip_chainlabel label)
+{
+ struct ip_chain *tmp;
+ FWC_HAVE_LOCK(fwc_rlocks | fwc_wlocks);
+ for (tmp = ip_fw_chains; tmp; tmp = tmp->next)
+ if (strcmp(tmp->label,label) == 0)
+ break;
+ return tmp;
+}
+
+/* This function returns a boolean which when true sets answer to one
+ of the FW_*. */
+static int find_special(ip_chainlabel label, int *answer)
+{
+ if (label[0] == '\0') {
+ *answer = FW_SKIP; /* => pass-through rule */
+ return 1;
+ } else if (strcmp(label,IP_FW_LABEL_ACCEPT) == 0) {
+ *answer = FW_ACCEPT;
+ return 1;
+ } else if (strcmp(label,IP_FW_LABEL_BLOCK) == 0) {
+ *answer = FW_BLOCK;
+ return 1;
+ } else if (strcmp(label,IP_FW_LABEL_REJECT) == 0) {
+ *answer = FW_REJECT;
+ return 1;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ } else if (strcmp(label,IP_FW_LABEL_REDIRECT) == 0) {
+ extern int sysctl_ip_always_defrag;
+ static int enabled = 0;
+
+ if(!enabled)
+ {
+ enabled=1;
+ sysctl_ip_always_defrag++;
+ }
+ *answer = FW_REDIRECT;
+ return 1;
+#endif
+#ifdef CONFIG_IP_MASQUERADE
+ } else if (strcmp(label,IP_FW_LABEL_MASQUERADE) == 0) {
+ *answer = FW_MASQUERADE;
+ return 1;
+#endif
+ } else if (strcmp(label, IP_FW_LABEL_RETURN) == 0) {
+ *answer = FW_SKIP+1;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* This function cleans up the prevchain and prevrule. If the verbose
+ * flag is set then he names of the chains will be printed as it
+ * cleans up. */
+static void cleanup(struct ip_chain *chain,
+ const int verbose,
+ unsigned int slot)
+{
+ struct ip_chain *tmpchain = chain->reent[slot].prevchain;
+ if (verbose)
+ printk(KERN_ERR "Chain backtrace: ");
+ while (tmpchain) {
+ if (verbose)
+ printk("%s<-",chain->label);
+ chain->reent[slot].prevchain = NULL;
+ chain = tmpchain;
+ tmpchain = chain->reent[slot].prevchain;
+ }
+ if (verbose)
+ printk("%s\n",chain->label);
+}
+
+static inline int
+ip_fw_domatch(struct ip_fwkernel *f,
+ struct iphdr *ip,
+ const char *rif,
+ const ip_chainlabel label,
+ struct sk_buff *skb,
+ unsigned int slot,
+ __u16 src_port, __u16 dst_port,
+ unsigned int count,
+ int tcpsyn)
+{
+ f->counters[slot].bcnt+=ntohs(ip->tot_len);
+ f->counters[slot].pcnt++;
+ if (f->ipfw.fw_flg & IP_FW_F_PRN) {
+ dump_packet(ip,rif,f,label,src_port,dst_port,count,tcpsyn);
+ }
+ ip->tos = (ip->tos & f->ipfw.fw_tosand) ^ f->ipfw.fw_tosxor;
+
+/* This functionality is useless in stock 2.0.x series, but we don't
+ * discard the mark thing altogether, to avoid breaking ipchains (and,
+ * more importantly, the ipfwadm wrapper) --PR */
+ if (f->ipfw.fw_flg & IP_FW_F_MARKABS)
+ skb->fwmark = f->ipfw.fw_mark;
+ else
+ skb->fwmark+=f->ipfw.fw_mark;
+#ifdef CONFIG_IP_FIREWALL_NETLINK
+ if (f->ipfw.fw_flg & IP_FW_F_NETLINK) {
+ size_t len = min(f->ipfw.fw_outputsize, ntohs(ip->tot_len))
+ + sizeof(__u32) + sizeof(skb->fwmark) + IFNAMSIZ;
+ struct sk_buff *outskb=alloc_skb(len, GFP_ATOMIC);
+
+ duprintf("Sending packet out NETLINK (length = %u).\n",
+ (unsigned int)len);
+ if (outskb) {
+ /* Prepend length, mark & interface */
+ skb_put(outskb, len);
+ *((__u32 *)outskb->data) = (__u32)len;
+ *((__u32 *)(outskb->data+sizeof(__u32))) = skb->fwmark;
+ strcpy(outskb->data+sizeof(__u32)*2, rif);
+ memcpy(outskb->data+sizeof(__u32)*2+IFNAMSIZ, ip,
+ len-(sizeof(__u32)*2+IFNAMSIZ));
+ netlink_broadcast(ipfwsk, outskb, 0, ~0, GFP_KERNEL);
+ }
+ else {
+ if (net_ratelimit())
+ printk(KERN_WARNING "ip_fw: packet drop due to "
+ "netlink failure\n");
+ return 0;
+ }
+ }
+#endif
+ return 1;
+}
+
+/*
+ * Returns one of the generic firewall policies, like FW_ACCEPT.
+ *
+ * The testing is either false for normal firewall mode or true for
+ * user checking mode (counters are not updated, TOS & mark not done).
+ */
+static int
+ip_fw_check(struct iphdr *ip,
+ const char *rif,
+ __u16 *redirport,
+ struct ip_chain *chain,
+ struct sk_buff *skb,
+ unsigned int slot,
+ int testing)
+{
+ struct tcphdr *tcp=(struct tcphdr *)((__u32 *)ip+ip->ihl);
+ struct udphdr *udp=(struct udphdr *)((__u32 *)ip+ip->ihl);
+ struct icmphdr *icmp=(struct icmphdr *)((__u32 *)ip+ip->ihl);
+ __u32 src, dst;
+ __u16 src_port = 0xFFFF, dst_port = 0xFFFF;
+ char tcpsyn=0;
+ __u16 offset;
+ unsigned char oldtos;
+ struct ip_fwkernel *f;
+ int ret = FW_SKIP+2;
+ unsigned int count;
+
+ /* We handle fragments by dealing with the first fragment as
+ * if it was a normal packet. All other fragments are treated
+ * normally, except that they will NEVER match rules that ask
+ * things we don't know, ie. tcp syn flag or ports). If the
+ * rule is also a fragment-specific rule, non-fragments won't
+ * match it. */
+
+ offset = ntohs(ip->frag_off) & IP_OFFSET;
+
+ /*
+ * Don't allow a fragment of TCP 8 bytes in. Nobody
+ * normal causes this. Its a cracker trying to break
+ * in by doing a flag overwrite to pass the direction
+ * checks.
+ */
+
+ if (offset == 1 && ip->protocol == IPPROTO_TCP) {
+ if (!testing && net_ratelimit()) {
+ printk("Suspect TCP fragment.\n");
+ dump_packet(ip,rif,NULL,NULL,0,0,0,0);
+ }
+ return FW_BLOCK;
+ }
+
+ /* If we can't investigate ports, treat as fragment. It's
+ * either a trucated whole packet, or a truncated first
+ * fragment, or a TCP first fragment of length 8-15, in which
+ * case the above rule stops reassembly.
+ */
+ if (offset == 0) {
+ unsigned int size_req;
+ switch (ip->protocol) {
+ case IPPROTO_TCP:
+ /* Don't care about things past flags word */
+ size_req = 16;
+ break;
+
+ case IPPROTO_UDP:
+ case IPPROTO_ICMP:
+ size_req = 8;
+ break;
+
+ default:
+ size_req = 0;
+ }
+ offset = (ntohs(ip->tot_len) < (ip->ihl<<2)+size_req);
+
+ /* If it is a truncated first fragment then it can be
+ * used to rewrite port information, and thus should
+ * be blocked.
+ */
+ if (offset && (ntohs(ip->frag_off) & IP_MF)) {
+ if (!testing && net_ratelimit()) {
+ printk("Suspect short first fragment.\n");
+ dump_packet(ip,rif,NULL,NULL,0,0,0,0);
+ }
+ return FW_BLOCK;
+ }
+ }
+
+ src = ip->saddr;
+ dst = ip->daddr;
+ oldtos = ip->tos;
+
+ /*
+ * If we got interface from which packet came
+ * we can use the address directly. Linux 2.1 now uses address
+ * chains per device too, but unlike BSD we first check if the
+ * incoming packet matches a device address and the routing
+ * table before calling the firewall.
+ */
+
+ dprintf("Packet ");
+ switch(ip->protocol)
+ {
+ case IPPROTO_TCP:
+ dprintf("TCP ");
+ if (!offset) {
+ src_port=ntohs(tcp->source);
+ dst_port=ntohs(tcp->dest);
+
+ /* Connection initilisation can only
+ * be made when the syn bit is set and
+ * neither of the ack or reset is
+ * set. */
+ if(tcp->syn && !(tcp->ack || tcp->rst))
+ tcpsyn=1;
+ }
+ break;
+ case IPPROTO_UDP:
+ dprintf("UDP ");
+ if (!offset) {
+ src_port=ntohs(udp->source);
+ dst_port=ntohs(udp->dest);
+ }
+ break;
+ case IPPROTO_ICMP:
+ if (!offset) {
+ src_port=(__u16)icmp->type;
+ dst_port=(__u16)icmp->code;
+ }
+ dprintf("ICMP ");
+ break;
+ default:
+ dprintf("p=%d ",ip->protocol);
+ break;
+ }
+#ifdef DEBUG_IP_FIREWALL
+ print_ip(ip->saddr);
+
+ if (offset)
+ dprintf(":fragment (%i) ", ((int)offset)<<2);
+ else if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP
+ || ip->protocol==IPPROTO_ICMP)
+ dprintf(":%hu:%hu", src_port, dst_port);
+ dprintf("\n");
+#endif
+
+ if (!testing) FWC_READ_LOCK(&ip_fw_lock);
+ else FWC_HAVE_LOCK(fwc_rlocks);
+
+ f = chain->chain;
+ count = 0;
+ do {
+ for (; f; f = f->next) {
+ count++;
+ if (ip_rule_match(f,rif,ip,
+ tcpsyn,src_port,dst_port,offset)) {
+ if (!testing
+ && !ip_fw_domatch(f, ip, rif, chain->label,
+ skb, slot,
+ src_port, dst_port,
+ count, tcpsyn)) {
+ ret = FW_BLOCK;
+ goto out;
+ }
+ break;
+ }
+ }
+ if (f) {
+ if (f->branch) {
+ /* Do sanity check to see if we have
+ * already set prevchain and if so we
+ * must be in a loop */
+ if (f->branch->reent[slot].prevchain) {
+ if (!testing) {
+ printk(KERN_ERR
+ "IP firewall: "
+ "Loop detected "
+ "at `%s'.\n",
+ f->branch->label);
+ cleanup(chain, 1, slot);
+ ret = FW_BLOCK;
+ } else {
+ cleanup(chain, 0, slot);
+ ret = FW_SKIP+1;
+ }
+ }
+ else {
+ f->branch->reent[slot].prevchain
+ = chain;
+ f->branch->reent[slot].count = count;
+ f->branch->reent[slot].prevrule
+ = f->next;
+ chain = f->branch;
+ f = chain->chain;
+ count = 0;
+ }
+ }
+ else if (f->simplebranch == FW_SKIP)
+ f = f->next;
+ else if (f->simplebranch == FW_SKIP+1) {
+ /* Just like falling off the chain */
+ goto fall_off_chain;
+ }
+ else {
+ cleanup(chain, 0, slot);
+ ret = f->simplebranch;
+ }
+ } /* f == NULL */
+ else {
+ fall_off_chain:
+ if (chain->reent[slot].prevchain) {
+ struct ip_chain *tmp = chain;
+ f = chain->reent[slot].prevrule;
+ count = chain->reent[slot].count;
+ chain = chain->reent[slot].prevchain;
+ tmp->reent[slot].prevchain = NULL;
+ }
+ else {
+ ret = chain->policy;
+ if (!testing) {
+ chain->reent[slot].counters.pcnt++;
+ chain->reent[slot].counters.bcnt
+ += ntohs(ip->tot_len);
+ }
+ }
+ }
+ } while (ret == FW_SKIP+2);
+
+ out:
+ if (!testing) FWC_READ_UNLOCK(&ip_fw_lock);
+
+ /* Recalculate checksum if not going to reject, and TOS changed. */
+ if (ip->tos != oldtos
+ && ret != FW_REJECT && ret != FW_BLOCK
+ && !testing)
+ ip_send_check(ip);
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ if (ret == FW_REDIRECT && redirport) {
+ if ((*redirport = htons(f->ipfw.fw_redirpt)) == 0) {
+ /* Wildcard redirection.
+ * Note that redirport will become
+ * 0xFFFF for non-TCP/UDP packets.
+ */
+ *redirport = htons(dst_port);
+ }
+ }
+#endif
+
+#ifdef DEBUG_ALLOW_ALL
+ return (testing ? ret : FW_ACCEPT);
+#else
+ return ret;
+#endif
+}
+
+/* Must have write lock & interrupts off for any of these */
+
+/* This function sets all the byte counters in a chain to zero. The
+ * input is a pointer to the chain required for zeroing */
+static int zero_fw_chain(struct ip_chain *chainptr)
+{
+ struct ip_fwkernel *i;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+ for (i = chainptr->chain; i; i = i->next)
+ memset(i->counters, 0, sizeof(struct ip_counters)*NUM_SLOTS);
+ return 0;
+}
+
+static int clear_fw_chain(struct ip_chain *chainptr)
+{
+ struct ip_fwkernel *i= chainptr->chain;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+ chainptr->chain=NULL;
+
+ while (i) {
+ struct ip_fwkernel *tmp = i->next;
+ if (i->branch)
+ i->branch->refcount--;
+ kfree(i);
+ i = tmp;
+ }
+ return 0;
+}
+
+static int replace_in_chain(struct ip_chain *chainptr,
+ struct ip_fwkernel *frwl,
+ __u32 position)
+{
+ struct ip_fwkernel *f = chainptr->chain;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+
+ while (--position && f != NULL) f = f->next;
+ if (f == NULL)
+ return EINVAL;
+
+ if (f->branch) f->branch->refcount--;
+ if (frwl->branch) frwl->branch->refcount++;
+
+ frwl->next = f->next;
+ memcpy(f,frwl,sizeof(struct ip_fwkernel));
+ kfree(frwl);
+ return 0;
+}
+
+static int append_to_chain(struct ip_chain *chainptr, struct ip_fwkernel *rule)
+{
+ struct ip_fwkernel *i;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+ /* Special case if no rules already present */
+ if (chainptr->chain == NULL) {
+
+ /* If pointer writes are atomic then turning off
+ * interupts is not necessary. */
+ chainptr->chain = rule;
+ if (rule->branch) rule->branch->refcount++;
+ return 0;
+ }
+
+ /* Find the rule before the end of the chain */
+ for (i = chainptr->chain; i->next; i = i->next);
+ i->next = rule;
+ if (rule->branch) rule->branch->refcount++;
+ return 0;
+}
+
+/* This function inserts a rule at the position of position in the
+ * chain refenced by chainptr. If position is 1 then this rule will
+ * become the new rule one. */
+static int insert_in_chain(struct ip_chain *chainptr,
+ struct ip_fwkernel *frwl,
+ __u32 position)
+{
+ struct ip_fwkernel *f = chainptr->chain;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+ /* special case if the position is number 1 */
+ if (position == 1) {
+ frwl->next = chainptr->chain;
+ if (frwl->branch) frwl->branch->refcount++;
+ chainptr->chain = frwl;
+ return 0;
+ }
+ position--;
+ while (--position && f != NULL) f = f->next;
+ if (f == NULL)
+ return EINVAL;
+ if (frwl->branch) frwl->branch->refcount++;
+ frwl->next = f->next;
+
+ f->next = frwl;
+ return 0;
+}
+
+/* This function deletes the a rule from a given rulenum and chain.
+ * With rulenum = 1 is the first rule is deleted. */
+
+static int del_num_from_chain(struct ip_chain *chainptr, __u32 rulenum)
+{
+ struct ip_fwkernel *i=chainptr->chain,*tmp;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+
+ if (!chainptr->chain)
+ return ENOENT;
+
+ /* Need a special case for the first rule */
+ if (rulenum == 1) {
+ /* store temp to allow for freeing up of memory */
+ tmp = chainptr->chain;
+ if (chainptr->chain->branch) chainptr->chain->branch->refcount--;
+ chainptr->chain = chainptr->chain->next;
+ kfree(tmp); /* free memory that is now unused */
+ } else {
+ rulenum--;
+ while (--rulenum && i->next ) i = i->next;
+ if (!i->next)
+ return ENOENT;
+ tmp = i->next;
+ if (i->next->branch)
+ i->next->branch->refcount--;
+ i->next = i->next->next;
+ kfree(tmp);
+ }
+ return 0;
+}
+
+
+/* This function deletes the a rule from a given rule and chain.
+ * The rule that is deleted is the first occursance of that rule. */
+static int del_rule_from_chain(struct ip_chain *chainptr,
+ struct ip_fwkernel *frwl)
+{
+ struct ip_fwkernel *ltmp,*ftmp = chainptr->chain ;
+ int was_found;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+
+ /* Sure, we should compare marks, but since the `ipfwadm'
+ * script uses it for an unholy hack... well, life is easier
+ * this way. We also mask it out of the flags word. --PR */
+ for (ltmp=NULL, was_found=0;
+ !was_found && ftmp != NULL;
+ ltmp = ftmp,ftmp = ftmp->next) {
+ if (ftmp->ipfw.fw_src.s_addr!=frwl->ipfw.fw_src.s_addr
+ || ftmp->ipfw.fw_dst.s_addr!=frwl->ipfw.fw_dst.s_addr
+ || ftmp->ipfw.fw_smsk.s_addr!=frwl->ipfw.fw_smsk.s_addr
+ || ftmp->ipfw.fw_dmsk.s_addr!=frwl->ipfw.fw_dmsk.s_addr
+#if 0
+ || ftmp->ipfw.fw_flg!=frwl->ipfw.fw_flg
+#else
+ || ((ftmp->ipfw.fw_flg & ~IP_FW_F_MARKABS)
+ != (frwl->ipfw.fw_flg & ~IP_FW_F_MARKABS))
+#endif
+ || ftmp->ipfw.fw_invflg!=frwl->ipfw.fw_invflg
+ || ftmp->ipfw.fw_proto!=frwl->ipfw.fw_proto
+#if 0
+ || ftmp->ipfw.fw_mark!=frwl->ipfw.fw_mark
+#endif
+ || ftmp->ipfw.fw_redirpt!=frwl->ipfw.fw_redirpt
+ || ftmp->ipfw.fw_spts[0]!=frwl->ipfw.fw_spts[0]
+ || ftmp->ipfw.fw_spts[1]!=frwl->ipfw.fw_spts[1]
+ || ftmp->ipfw.fw_dpts[0]!=frwl->ipfw.fw_dpts[0]
+ || ftmp->ipfw.fw_dpts[1]!=frwl->ipfw.fw_dpts[1]
+ || ftmp->ipfw.fw_outputsize!=frwl->ipfw.fw_outputsize) {
+ duprintf("del_rule_from_chain: mismatch:"
+ "src:%u/%u dst:%u/%u smsk:%u/%u dmsk:%u/%u "
+ "flg:%hX/%hX invflg:%hX/%hX proto:%u/%u "
+ "mark:%u/%u "
+ "ports:%hu-%hu/%hu-%hu %hu-%hu/%hu-%hu "
+ "outputsize:%hu-%hu\n",
+ ftmp->ipfw.fw_src.s_addr,
+ frwl->ipfw.fw_src.s_addr,
+ ftmp->ipfw.fw_dst.s_addr,
+ frwl->ipfw.fw_dst.s_addr,
+ ftmp->ipfw.fw_smsk.s_addr,
+ frwl->ipfw.fw_smsk.s_addr,
+ ftmp->ipfw.fw_dmsk.s_addr,
+ frwl->ipfw.fw_dmsk.s_addr,
+ ftmp->ipfw.fw_flg,
+ frwl->ipfw.fw_flg,
+ ftmp->ipfw.fw_invflg,
+ frwl->ipfw.fw_invflg,
+ ftmp->ipfw.fw_proto,
+ frwl->ipfw.fw_proto,
+ ftmp->ipfw.fw_mark,
+ frwl->ipfw.fw_mark,
+ ftmp->ipfw.fw_spts[0],
+ frwl->ipfw.fw_spts[0],
+ ftmp->ipfw.fw_spts[1],
+ frwl->ipfw.fw_spts[1],
+ ftmp->ipfw.fw_dpts[0],
+ frwl->ipfw.fw_dpts[0],
+ ftmp->ipfw.fw_dpts[1],
+ frwl->ipfw.fw_dpts[1],
+ ftmp->ipfw.fw_outputsize,
+ frwl->ipfw.fw_outputsize);
+ continue;
+ }
+
+ if (strncmp(ftmp->ipfw.fw_vianame,
+ frwl->ipfw.fw_vianame,
+ IFNAMSIZ)) {
+ duprintf("del_rule_from_chain: if mismatch: %s/%s\n",
+ ftmp->ipfw.fw_vianame,
+ frwl->ipfw.fw_vianame);
+ continue;
+ }
+ if (ftmp->branch != frwl->branch) {
+ duprintf("del_rule_from_chain: branch mismatch: "
+ "%s/%s\n",
+ ftmp->branch?ftmp->branch->label:"(null)",
+ frwl->branch?frwl->branch->label:"(null)");
+ continue;
+ }
+ if (ftmp->branch == NULL
+ && ftmp->simplebranch != frwl->simplebranch) {
+ duprintf("del_rule_from_chain: simplebranch mismatch: "
+ "%i/%i\n",
+ ftmp->simplebranch, frwl->simplebranch);
+ continue;
+ }
+ was_found = 1;
+ if (ftmp->branch)
+ ftmp->branch->refcount--;
+ if (ltmp)
+ ltmp->next = ftmp->next;
+ else
+ chainptr->chain = ftmp->next;
+ kfree(ftmp);
+ break;
+ }
+
+ if (was_found)
+ return 0;
+ else {
+ duprintf("del_rule_from_chain: no matching rule found\n");
+ return EINVAL;
+ }
+}
+
+/* This function takes the label of a chain and deletes the first
+ * chain with that name. No special cases required for the built in
+ * chains as they have their refcount initilised to 1 so that they are
+ * never deleted. */
+static int del_chain(ip_chainlabel label)
+{
+ struct ip_chain *tmp,*tmp2;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+ /* Corner case: return EBUSY not ENOENT for first elem ("input") */
+ if (strcmp(label, ip_fw_chains->label) == 0)
+ return EBUSY;
+
+ for (tmp = ip_fw_chains; tmp->next; tmp = tmp->next)
+ if(strcmp(tmp->next->label,label) == 0)
+ break;
+
+ tmp2 = tmp->next;
+ if (!tmp2)
+ return ENOENT;
+
+ if (tmp2->refcount)
+ return EBUSY;
+
+ if (tmp2->chain)
+ return ENOTEMPTY;
+
+ tmp->next = tmp2->next;
+ kfree(tmp2);
+ return 0;
+}
+
+/* This is a function to initilise a chain. Built in rules start with
+ * refcount = 1 so that they cannot be deleted. User defined rules
+ * start with refcount = 0 so they can be deleted. */
+static struct ip_chain *ip_init_chain(ip_chainlabel name,
+ __u32 ref,
+ int policy)
+{
+ unsigned int i;
+ struct ip_chain *label
+ = kmalloc(SIZEOF_STRUCT_IP_CHAIN, GFP_KERNEL);
+ if (label == NULL)
+ panic("Can't kmalloc for firewall chains.\n");
+ strcpy(label->label,name);
+ label->next = NULL;
+ label->chain = NULL;
+ label->refcount = ref;
+ label->policy = policy;
+ for (i = 0; i < smp_num_cpus*2; i++) {
+ label->reent[i].counters.pcnt = label->reent[i].counters.bcnt
+ = 0;
+ label->reent[i].prevchain = NULL;
+ label->reent[i].prevrule = NULL;
+ }
+
+ return label;
+}
+
+/* This is a function for reating a new chain. The chains is not
+ * created if a chain of the same name already exists */
+static int create_chain(ip_chainlabel label)
+{
+ struct ip_chain *tmp;
+
+ if (!check_label(label))
+ return EINVAL;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+ for (tmp = ip_fw_chains; tmp->next; tmp = tmp->next)
+ if (strcmp(tmp->label,label) == 0)
+ return EEXIST;
+
+ if (strcmp(tmp->label,label) == 0)
+ return EEXIST;
+
+ tmp->next = ip_init_chain(label, 0, FW_SKIP); /* refcount is
+ * zero since this is a
+ * user defined chain *
+ * and therefore can be
+ * deleted */
+ return 0;
+}
+
+/* This function simply changes the policy on one of the built in
+ * chains. checking must be done before this is call to ensure that
+ * chainptr is pointing to one of the three possible chains */
+static int change_policy(struct ip_chain *chainptr, int policy)
+{
+ FWC_HAVE_LOCK(fwc_wlocks);
+ chainptr->policy = policy;
+ return 0;
+}
+
+/* This function takes an ip_fwuser and converts it to a ip_fwkernel. It also
+ * performs some checks in the structure. */
+static struct ip_fwkernel *convert_ipfw(struct ip_fwuser *fwuser, int *errno)
+{
+ struct ip_fwkernel *fwkern;
+
+ if ( (fwuser->ipfw.fw_flg & ~IP_FW_F_MASK) != 0 ) {
+ duprintf("convert_ipfw: undefined flag bits set (flags=%x)\n",
+ fwuser->ipfw.fw_flg);
+ *errno = EINVAL;
+ return NULL;
+ }
+
+#ifdef DEBUG_IP_FIREWALL_USER
+ /* These are sanity checks that don't really matter.
+ * We can get rid of these once testing is complete.
+ */
+ if ((fwuser->ipfw.fw_flg & IP_FW_F_TCPSYN)
+ && ((fwuser->ipfw.fw_invflg & IP_FW_INV_PROTO)
+ || fwuser->ipfw.fw_proto != IPPROTO_TCP)) {
+ duprintf("convert_ipfw: TCP SYN flag set but proto != TCP!\n");
+ *errno = EINVAL;
+ return NULL;
+ }
+
+ if (strcmp(fwuser->label, IP_FW_LABEL_REDIRECT) != 0
+ && fwuser->ipfw.fw_redirpt != 0) {
+ duprintf("convert_ipfw: Target not REDIR but redirpt != 0!\n");
+ *errno = EINVAL;
+ return NULL;
+ }
+
+ if ((!(fwuser->ipfw.fw_flg & IP_FW_F_FRAG)
+ && (fwuser->ipfw.fw_invflg & IP_FW_INV_FRAG))
+ || (!(fwuser->ipfw.fw_flg & IP_FW_F_TCPSYN)
+ && (fwuser->ipfw.fw_invflg & IP_FW_INV_SYN))) {
+ duprintf("convert_ipfw: Can't have INV flag if flag unset!\n");
+ *errno = EINVAL;
+ return NULL;
+ }
+
+ if (((fwuser->ipfw.fw_invflg & IP_FW_INV_SRCPT)
+ && fwuser->ipfw.fw_spts[0] == 0
+ && fwuser->ipfw.fw_spts[1] == 0xFFFF)
+ || ((fwuser->ipfw.fw_invflg & IP_FW_INV_DSTPT)
+ && fwuser->ipfw.fw_dpts[0] == 0
+ && fwuser->ipfw.fw_dpts[1] == 0xFFFF)
+ || ((fwuser->ipfw.fw_invflg & IP_FW_INV_VIA)
+ && (fwuser->ipfw.fw_vianame)[0] == '\0')
+ || ((fwuser->ipfw.fw_invflg & IP_FW_INV_SRCIP)
+ && fwuser->ipfw.fw_smsk.s_addr == 0)
+ || ((fwuser->ipfw.fw_invflg & IP_FW_INV_DSTIP)
+ && fwuser->ipfw.fw_dmsk.s_addr == 0)) {
+ duprintf("convert_ipfw: INV flag makes rule unmatchable!\n");
+ *errno = EINVAL;
+ return NULL;
+ }
+
+ if ((fwuser->ipfw.fw_flg & IP_FW_F_FRAG)
+ && !(fwuser->ipfw.fw_invflg & IP_FW_INV_FRAG)
+ && (fwuser->ipfw.fw_spts[0] != 0
+ || fwuser->ipfw.fw_spts[1] != 0xFFFF
+ || fwuser->ipfw.fw_dpts[0] != 0
+ || fwuser->ipfw.fw_dpts[1] != 0xFFFF
+ || (fwuser->ipfw.fw_flg & IP_FW_F_TCPSYN))) {
+ duprintf("convert_ipfw: Can't test ports or SYN with frag!\n");
+ *errno = EINVAL;
+ return NULL;
+ }
+#endif
+
+ if ((fwuser->ipfw.fw_spts[0] != 0
+ || fwuser->ipfw.fw_spts[1] != 0xFFFF
+ || fwuser->ipfw.fw_dpts[0] != 0
+ || fwuser->ipfw.fw_dpts[1] != 0xFFFF)
+ && ((fwuser->ipfw.fw_invflg & IP_FW_INV_PROTO)
+ || (fwuser->ipfw.fw_proto != IPPROTO_TCP
+ && fwuser->ipfw.fw_proto != IPPROTO_UDP
+ && fwuser->ipfw.fw_proto != IPPROTO_ICMP))) {
+ duprintf("convert_ipfw: Can only test ports for TCP/UDP/ICMP!\n");
+ *errno = EINVAL;
+ return NULL;
+ }
+
+ fwkern = kmalloc(SIZEOF_STRUCT_IP_FW_KERNEL, GFP_KERNEL);
+ if (!fwkern) {
+ duprintf("convert_ipfw: kmalloc failed!\n");
+ *errno = ENOMEM;
+ return NULL;
+ }
+ memcpy(&fwkern->ipfw,&fwuser->ipfw,sizeof(struct ip_fw));
+
+ if (!find_special(fwuser->label, &fwkern->simplebranch)) {
+ fwkern->branch = find_label(fwuser->label);
+ if (!fwkern->branch) {
+ duprintf("convert_ipfw: chain doesn't exist `%s'.\n",
+ fwuser->label);
+ kfree(fwkern);
+ *errno = ENOENT;
+ return NULL;
+ } else if (fwkern->branch == IP_FW_INPUT_CHAIN
+ || fwkern->branch == IP_FW_FORWARD_CHAIN
+ || fwkern->branch == IP_FW_OUTPUT_CHAIN) {
+ duprintf("convert_ipfw: Can't branch to builtin chain `%s'.\n",
+ fwuser->label);
+ kfree(fwkern);
+ *errno = ENOENT;
+ return NULL;
+ }
+ } else
+ fwkern->branch = NULL;
+ memset(fwkern->counters, 0, sizeof(struct ip_counters)*NUM_SLOTS);
+
+ /* Handle empty vianame by making it a wildcard */
+ if ((fwkern->ipfw.fw_vianame)[0] == '\0')
+ fwkern->ipfw.fw_flg |= IP_FW_F_WILDIF;
+
+ fwkern->next = NULL;
+ return fwkern;
+}
+
+int ip_fw_ctl(int cmd, void *m, int len)
+{
+ int ret;
+ struct ip_chain *chain;
+ unsigned long flags;
+
+ FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
+
+ switch (cmd) {
+ case IP_FW_FLUSH:
+ if (len != sizeof(ip_chainlabel) || !check_label(m))
+ ret = EINVAL;
+ else if ((chain = find_label(m)) == NULL)
+ ret = ENOENT;
+ else ret = clear_fw_chain(chain);
+ break;
+
+ case IP_FW_ZERO:
+ if (len != sizeof(ip_chainlabel) || !check_label(m))
+ ret = EINVAL;
+ else if ((chain = find_label(m)) == NULL)
+ ret = ENOENT;
+ else ret = zero_fw_chain(chain);
+ break;
+
+ case IP_FW_CHECK: {
+ struct ip_fwtest *new = m;
+ struct iphdr *ip;
+
+ /* Don't need write lock. */
+ FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+
+ if (len != sizeof(struct ip_fwtest) || !check_label(m))
+ return EINVAL;
+
+ /* Need readlock to do find_label */
+ FWC_READ_LOCK(&ip_fw_lock);
+
+ if ((chain = find_label(new->fwt_label)) == NULL)
+ ret = ENOENT;
+ else {
+ ip = &(new->fwt_packet.fwp_iph);
+
+ if (ip->ihl != sizeof(struct iphdr) / sizeof(int)) {
+ duprintf("ip_fw_ctl: ip->ihl=%d, want %d\n",
+ ip->ihl,
+ sizeof(struct iphdr) / sizeof(int));
+ ret = EINVAL;
+ }
+ else {
+ ret = ip_fw_check(ip, new->fwt_packet.fwp_vianame,
+ NULL, chain,
+ NULL, SLOT_NUMBER(), 1);
+ switch (ret) {
+ case FW_ACCEPT:
+ ret = 0; break;
+ case FW_REDIRECT:
+ ret = ECONNABORTED; break;
+ case FW_MASQUERADE:
+ ret = ECONNRESET; break;
+ case FW_REJECT:
+ ret = ECONNREFUSED; break;
+ /* Hack to help diag; these only get
+ returned when testing. */
+ case FW_SKIP+1:
+ ret = ELOOP; break;
+ case FW_SKIP:
+ ret = ENFILE; break;
+ default: /* FW_BLOCK */
+ ret = ETIMEDOUT; break;
+ }
+ }
+ }
+ FWC_READ_UNLOCK(&ip_fw_lock);
+ return ret;
+ }
+
+ case IP_FW_MASQ_TIMEOUTS: {
+#ifdef CONFIG_IP_MASQUERADE
+ ret = ip_fw_masq_timeouts(m, len);
+#else
+ ret = EINVAL;
+#endif
+ }
+ break;
+
+ case IP_FW_REPLACE: {
+ struct ip_fwkernel *ip_fwkern;
+ struct ip_fwnew *new = m;
+
+ if (len != sizeof(struct ip_fwnew)
+ || !check_label(new->fwn_label))
+ ret = EINVAL;
+ else if ((chain = find_label(new->fwn_label)) == NULL)
+ ret = ENOENT;
+ else if ((ip_fwkern = convert_ipfw(&new->fwn_rule, &ret))
+ != NULL)
+ ret = replace_in_chain(chain, ip_fwkern,
+ new->fwn_rulenum);
+ }
+ break;
+
+ case IP_FW_APPEND: {
+ struct ip_fwchange *new = m;
+ struct ip_fwkernel *ip_fwkern;
+
+ if (len != sizeof(struct ip_fwchange)
+ || !check_label(new->fwc_label))
+ ret = EINVAL;
+ else if ((chain = find_label(new->fwc_label)) == NULL)
+ ret = ENOENT;
+ else if ((ip_fwkern = convert_ipfw(&new->fwc_rule, &ret))
+ != NULL)
+ ret = append_to_chain(chain, ip_fwkern);
+ }
+ break;
+
+ case IP_FW_INSERT: {
+ struct ip_fwkernel *ip_fwkern;
+ struct ip_fwnew *new = m;
+
+ if (len != sizeof(struct ip_fwnew)
+ || !check_label(new->fwn_label))
+ ret = EINVAL;
+ else if ((chain = find_label(new->fwn_label)) == NULL)
+ ret = ENOENT;
+ else if ((ip_fwkern = convert_ipfw(&new->fwn_rule, &ret))
+ != NULL)
+ ret = insert_in_chain(chain, ip_fwkern,
+ new->fwn_rulenum);
+ }
+ break;
+
+ case IP_FW_DELETE: {
+ struct ip_fwchange *new = m;
+ struct ip_fwkernel *ip_fwkern;
+
+ if (len != sizeof(struct ip_fwchange)
+ || !check_label(new->fwc_label))
+ ret = EINVAL;
+ else if ((chain = find_label(new->fwc_label)) == NULL)
+ ret = ENOENT;
+ else if ((ip_fwkern = convert_ipfw(&new->fwc_rule, &ret))
+ != NULL) {
+ ret = del_rule_from_chain(chain, ip_fwkern);
+ kfree(ip_fwkern);
+ }
+ }
+ break;
+
+ case IP_FW_DELETE_NUM: {
+ struct ip_fwdelnum *new = m;
+
+ if (len != sizeof(struct ip_fwdelnum)
+ || !check_label(new->fwd_label))
+ ret = EINVAL;
+ else if ((chain = find_label(new->fwd_label)) == NULL)
+ ret = ENOENT;
+ else ret = del_num_from_chain(chain, new->fwd_rulenum);
+ }
+ break;
+
+ case IP_FW_CREATECHAIN: {
+ if (len != sizeof(ip_chainlabel)) {
+ duprintf("create_chain: bad size %i\n", len);
+ ret = EINVAL;
+ }
+ else ret = create_chain(m);
+ }
+ break;
+
+ case IP_FW_DELETECHAIN: {
+ if (len != sizeof(ip_chainlabel)) {
+ duprintf("delete_chain: bad size %i\n", len);
+ ret = EINVAL;
+ }
+ else ret = del_chain(m);
+ }
+ break;
+
+ case IP_FW_POLICY: {
+ struct ip_fwpolicy *new = m;
+
+ if (len != sizeof(struct ip_fwpolicy)
+ || !check_label(new->fwp_label))
+ ret = EINVAL;
+ else if ((chain = find_label(new->fwp_label)) == NULL)
+ ret = ENOENT;
+ else if (chain != IP_FW_INPUT_CHAIN
+ && chain != IP_FW_FORWARD_CHAIN
+ && chain != IP_FW_OUTPUT_CHAIN) {
+ duprintf("change_policy: can't change policy on user"
+ " defined chain.\n");
+ ret = EINVAL;
+ }
+ else {
+ int pol = FW_SKIP;
+ find_special(new->fwp_policy, &pol);
+
+ switch(pol) {
+ case FW_MASQUERADE:
+ if (chain != IP_FW_FORWARD_CHAIN) {
+ ret = EINVAL;
+ break;
+ }
+ /* Fall thru... */
+ case FW_BLOCK:
+ case FW_ACCEPT:
+ case FW_REJECT:
+ ret = change_policy(chain, pol);
+ break;
+ default:
+ duprintf("change_policy: bad policy `%s'\n",
+ new->fwp_policy);
+ ret = EINVAL;
+ }
+ }
+ break;
+
+ }
+ default:
+ duprintf("ip_fw_ctl: unknown request %d\n",cmd);
+ ret = EINVAL;
+ }
+
+ FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+ return ret;
+}
+
+/* Returns bytes used - doesn't NUL terminate */
+static int dump_rule(char *buffer,
+ const char *chainlabel,
+ const struct ip_fwkernel *rule)
+{
+ int len;
+ unsigned int i;
+ __u64 packets = 0, bytes = 0;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+ for (i = 0; i < NUM_SLOTS; i++) {
+ packets += rule->counters[i].pcnt;
+ bytes += rule->counters[i].bcnt;
+ }
+
+ len=sprintf(buffer,
+ "%9s " /* Chain name */
+ "%08X/%08X->%08X/%08X " /* Source & Destination IPs */
+ "%.16s " /* Interface */
+ "%X %X " /* fw_flg and fw_invflg fields */
+ "%u " /* Protocol */
+ "%-9u %-9u %-9u %-9u " /* Packet & byte counters */
+ "%u-%u %u-%u " /* Source & Dest port ranges */
+ "A%02X X%02X " /* TOS and and xor masks */
+ "%08X " /* Redirection port */
+ "%u " /* fw_mark field */
+ "%u " /* output size */
+ "%9s\n", /* Target */
+ chainlabel,
+ ntohl(rule->ipfw.fw_src.s_addr),
+ ntohl(rule->ipfw.fw_smsk.s_addr),
+ ntohl(rule->ipfw.fw_dst.s_addr),
+ ntohl(rule->ipfw.fw_dmsk.s_addr),
+ (rule->ipfw.fw_vianame)[0] ? rule->ipfw.fw_vianame : "-",
+ rule->ipfw.fw_flg,
+ rule->ipfw.fw_invflg,
+ rule->ipfw.fw_proto,
+ (__u32)(packets >> 32), (__u32)packets,
+ (__u32)(bytes >> 32), (__u32)bytes,
+ rule->ipfw.fw_spts[0], rule->ipfw.fw_spts[1],
+ rule->ipfw.fw_dpts[0], rule->ipfw.fw_dpts[1],
+ rule->ipfw.fw_tosand, rule->ipfw.fw_tosxor,
+ rule->ipfw.fw_redirpt,
+ rule->ipfw.fw_mark,
+ rule->ipfw.fw_outputsize,
+ branchname(rule->branch,rule->simplebranch));
+
+ duprintf("dump_rule: %i bytes done.\n", len);
+ return len;
+}
+
+/* File offset is actually in records, not bytes. */
+static int ip_chain_procinfo(char *buffer, char **start,
+ off_t offset, int length, int reset)
+{
+ struct ip_chain *i;
+ struct ip_fwkernel *j = ip_fw_chains->chain;
+ unsigned long flags;
+ int len = 0;
+ int last_len = 0;
+ off_t upto = 0;
+
+ duprintf("Offset starts at %lu\n", offset);
+ duprintf("ip_fw_chains is 0x%0lX\n", (unsigned long int)ip_fw_chains);
+
+ /* Need a write lock to lock out ``readers'' which update counters. */
+ FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
+
+ for (i = ip_fw_chains; i; i = i->next) {
+ for (j = i->chain; j; j = j->next) {
+ if (upto == offset) break;
+ duprintf("Skipping rule in chain `%s'\n",
+ i->label);
+ upto++;
+ }
+ if (upto == offset) break;
+ }
+
+ /* Don't init j first time, or once i = NULL */
+ for (; i; (void)((i = i->next) && (j = i->chain))) {
+ duprintf("Dumping chain `%s'\n", i->label);
+ for (; j; j = j->next, upto++, last_len = len)
+ {
+ len += dump_rule(buffer+len, i->label, j);
+ if (len > length) {
+ duprintf("Dumped to %i (past %i). "
+ "Moving back to %i.\n",
+ len, length, last_len);
+ len = last_len;
+ goto outside;
+ }
+ else if (reset)
+ memset(j->counters, 0,
+ sizeof(struct ip_counters)*NUM_SLOTS);
+ }
+ }
+outside:
+ FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+ buffer[len] = '\0';
+
+ duprintf("ip_chain_procinfo: Length = %i (of %i). Offset = %li.\n",
+ len, length, upto);
+ /* `start' hack - see fs/proc/generic.c line ~165 */
+ *start=(char *)((unsigned int)upto-offset);
+ return len;
+}
+
+static int ip_chain_name_procinfo(char *buffer, char **start,
+ off_t offset, int length, int reset)
+{
+ struct ip_chain *i;
+ int len = 0,last_len = 0;
+ off_t pos = 0,begin = 0;
+ unsigned long flags;
+
+ /* Need a write lock to lock out ``readers'' which update counters. */
+ FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
+
+ for (i = ip_fw_chains; i; i = i->next)
+ {
+ unsigned int j;
+ __u32 packetsHi = 0, packetsLo = 0, bytesHi = 0, bytesLo = 0;
+
+ for (j = 0; j < NUM_SLOTS; j++) {
+ packetsLo += i->reent[j].counters.pcnt & 0xFFFFFFFF;
+ packetsHi += ((i->reent[j].counters.pcnt >> 32)
+ & 0xFFFFFFFF);
+ bytesLo += i->reent[j].counters.bcnt & 0xFFFFFFFF;
+ bytesHi += ((i->reent[j].counters.bcnt >> 32)
+ & 0xFFFFFFFF);
+ }
+
+ /* print the label and the policy */
+ len+=sprintf(buffer+len,"%s %s %i %u %u %u %u\n",
+ i->label,branchname(NULL, i->policy),i->refcount,
+ packetsHi, packetsLo, bytesHi, bytesLo);
+ pos=begin+len;
+ if(pos<offset) {
+ len=0;
+ begin=pos;
+ }
+ else if(pos>offset+length) {
+ len = last_len;
+ break;
+ }
+
+ last_len = len;
+ }
+ FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+
+ *start = buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ return len;
+}
+
+/*
+ * Interface to the generic firewall chains.
+ */
+int ipfw_input_check(struct firewall_ops *this, int pf, struct device *dev,
+ void *phdr, void *arg, struct sk_buff **pskb)
+{
+ return ip_fw_check(phdr, dev->name,
+ arg, IP_FW_INPUT_CHAIN, *pskb, SLOT_NUMBER(), 0);
+}
+
+int ipfw_output_check(struct firewall_ops *this, int pf, struct device *dev,
+ void *phdr, void *arg, struct sk_buff **pskb)
+{
+ /* Locally generated bogus packets by root. <SIGH>. */
+ if (((struct iphdr *)phdr)->ihl * 4 < sizeof(struct iphdr)
+ || (*pskb)->len < sizeof(struct iphdr))
+ return FW_ACCEPT;
+ return ip_fw_check(phdr, dev->name,
+ arg, IP_FW_OUTPUT_CHAIN, *pskb, SLOT_NUMBER(), 0);
+}
+
+int ipfw_forward_check(struct firewall_ops *this, int pf, struct device *dev,
+ void *phdr, void *arg, struct sk_buff **pskb)
+{
+ return ip_fw_check(phdr, dev->name,
+ arg, IP_FW_FORWARD_CHAIN, *pskb, SLOT_NUMBER(), 0);
+}
+
+struct firewall_ops ipfw_ops=
+{
+ NULL,
+ ipfw_forward_check,
+ ipfw_input_check,
+ ipfw_output_check,
+ PF_INET,
+ 0 /* We don't even allow a fall through so we are last */
+};
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_ipfwchains_chain = {
+ PROC_NET_IPFW_CHAINS, sizeof(IP_FW_PROC_CHAINS)-1,
+ IP_FW_PROC_CHAINS, S_IFREG | S_IRUSR | S_IWUSR, 1, 0, 0,
+ 0, &proc_net_inode_operations, ip_chain_procinfo
+};
+
+static struct proc_dir_entry proc_net_ipfwchains_chainnames = {
+ PROC_NET_IPFW_CHAIN_NAMES, sizeof(IP_FW_PROC_CHAIN_NAMES)-1,
+ IP_FW_PROC_CHAIN_NAMES, S_IFREG | S_IRUSR | S_IWUSR, 1, 0, 0,
+ 0, &proc_net_inode_operations, ip_chain_name_procinfo
+};
+
+#endif
+
+__initfunc(void ip_fw_init(void))
+{
+#ifdef DEBUG_IP_FIRWALL_LOCKING
+ fwc_wlocks = fwc_rlocks = 0;
+#endif
+
+ IP_FW_INPUT_CHAIN = ip_init_chain(IP_FW_LABEL_INPUT, 1, FW_ACCEPT);
+ IP_FW_FORWARD_CHAIN = ip_init_chain(IP_FW_LABEL_FORWARD, 1, FW_ACCEPT);
+ IP_FW_OUTPUT_CHAIN = ip_init_chain(IP_FW_LABEL_OUTPUT, 1, FW_ACCEPT);
+
+ if(register_firewall(PF_INET,&ipfw_ops)<0)
+ panic("Unable to register IP firewall.\n");
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_net_ipfwchains_chain);
+ proc_net_register(&proc_net_ipfwchains_chainnames);
+#endif
+
+#ifdef CONFIG_IP_FIREWALL_NETLINK
+ ipfwsk = netlink_kernel_create(NETLINK_FIREWALL, NULL);
+ if (ipfwsk == NULL)
+ panic("ip_fw_init: cannot initialize netlink\n");
+#endif
+#if defined(DEBUG_IP_FIREWALL) || defined(DEBUG_IP_FIREWALL_USER)
+ printk("Firewall graphs enabled! Untested kernel coming thru. \n");
+#endif
+}
diff --git a/pfinet/linux-src/net/ipv4/ip_gre.c b/pfinet/linux-src/net/ipv4/ip_gre.c
new file mode 100644
index 00000000..4b03c226
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_gre.c
@@ -0,0 +1,1223 @@
+/*
+ * Linux NET3: GRE over IP protocol decoder.
+ *
+ * Authors: Alexey Kuznetsov (kuznet@ms2.inr.ac.ru)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/uaccess.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/in.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/if_arp.h>
+#include <linux/mroute.h>
+#include <linux/init.h>
+#include <linux/in6.h>
+#include <linux/inetdevice.h>
+#include <linux/igmp.h>
+
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <net/protocol.h>
+#include <net/ipip.h>
+#include <net/arp.h>
+#include <net/checksum.h>
+
+#ifdef CONFIG_IPV6
+#include <net/ipv6.h>
+#include <net/ip6_fib.h>
+#include <net/ip6_route.h>
+#endif
+
+/*
+ Problems & solutions
+ --------------------
+
+ 1. The most important issue is detecting local dead loops.
+ They would cause complete host lockup in transmit, which
+ would be "resolved" by stack overflow or, if queueing is enabled,
+ with infinite looping in net_bh.
+
+ We cannot track such dead loops during route installation,
+ it is infeasible task. The most general solutions would be
+ to keep skb->encapsulation counter (sort of local ttl),
+ and silently drop packet when it expires. It is the best
+ solution, but it supposes maintaing new variable in ALL
+ skb, even if no tunneling is used.
+
+ Current solution: t->recursion lock breaks dead loops. It looks
+ like dev->tbusy flag, but I preferred new variable, because
+ the semantics is different. One day, when hard_start_xmit
+ will be multithreaded we will have to use skb->encapsulation.
+
+
+
+ 2. Networking dead loops would not kill routers, but would really
+ kill network. IP hop limit plays role of "t->recursion" in this case,
+ if we copy it from packet being encapsulated to upper header.
+ It is very good solution, but it introduces two problems:
+
+ - Routing protocols, using packets with ttl=1 (OSPF, RIP2),
+ do not work over tunnels.
+ - traceroute does not work. I planned to relay ICMP from tunnel,
+ so that this problem would be solved and traceroute output
+ would even more informative. This idea appeared to be wrong:
+ only Linux complies to rfc1812 now (yes, guys, Linux is the only
+ true router now :-)), all routers (at least, in neighbourhood of mine)
+ return only 8 bytes of payload. It is the end.
+
+ Hence, if we want that OSPF worked or traceroute said something reasonable,
+ we should search for another solution.
+
+ One of them is to parse packet trying to detect inner encapsulation
+ made by our node. It is difficult or even impossible, especially,
+ taking into account fragmentation. TO be short, tt is not solution at all.
+
+ Current solution: The solution was UNEXPECTEDLY SIMPLE.
+ We force DF flag on tunnels with preconfigured hop limit,
+ that is ALL. :-) Well, it does not remove the problem completely,
+ but exponential growth of network traffic is changed to linear
+ (branches, that exceed pmtu are pruned) and tunnel mtu
+ fastly degrades to value <68, where looping stops.
+ Yes, it is not good if there exists a router in the loop,
+ which does not force DF, even when encapsulating packets have DF set.
+ But it is not our problem! Nobody could accuse us, we made
+ all that we could make. Even if it is your gated who injected
+ fatal route to network, even if it were you who configured
+ fatal static route: you are innocent. :-)
+
+
+
+ 3. Really, ipv4/ipip.c, ipv4/ip_gre.c and ipv6/sit.c contain
+ practically identical code. It would be good to glue them
+ together, but it is not very evident, how to make them modular.
+ sit is integral part of IPv6, ipip and gre are naturally modular.
+ We could extract common parts (hash table, ioctl etc)
+ to a separate module (ip_tunnel.c).
+
+ Alexey Kuznetsov.
+ */
+
+static int ipgre_tunnel_init(struct device *dev);
+
+/* Fallback tunnel: no source, no destination, no key, no options */
+
+static int ipgre_fb_tunnel_init(struct device *dev);
+
+static struct device ipgre_fb_tunnel_dev = {
+ NULL, 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, ipgre_fb_tunnel_init,
+};
+
+static struct ip_tunnel ipgre_fb_tunnel = {
+ NULL, &ipgre_fb_tunnel_dev, {0, }, 0, 0, 0, 0, 0, 0, 0, {"gre0", }
+};
+
+/* Tunnel hash table */
+
+/*
+ 4 hash tables:
+
+ 3: (remote,local)
+ 2: (remote,*)
+ 1: (*,local)
+ 0: (*,*)
+
+ We require exact key match i.e. if a key is present in packet
+ it will match only tunnel with the same key; if it is not present,
+ it will match only keyless tunnel.
+
+ All keysless packets, if not matched configured keyless tunnels
+ will match fallback tunnel.
+ */
+
+#define HASH_SIZE 16
+#define HASH(addr) ((addr^(addr>>4))&0xF)
+
+static struct ip_tunnel *tunnels[4][HASH_SIZE];
+
+#define tunnels_r_l (tunnels[3])
+#define tunnels_r (tunnels[2])
+#define tunnels_l (tunnels[1])
+#define tunnels_wc (tunnels[0])
+
+/* Given src, dst and key, find approriate for input tunnel. */
+
+static struct ip_tunnel * ipgre_tunnel_lookup(u32 remote, u32 local, u32 key)
+{
+ unsigned h0 = HASH(remote);
+ unsigned h1 = HASH(key);
+ struct ip_tunnel *t;
+
+ for (t = tunnels_r_l[h0^h1]; t; t = t->next) {
+ if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) {
+ if (t->parms.i_key == key && (t->dev->flags&IFF_UP))
+ return t;
+ }
+ }
+ for (t = tunnels_r[h0^h1]; t; t = t->next) {
+ if (remote == t->parms.iph.daddr) {
+ if (t->parms.i_key == key && (t->dev->flags&IFF_UP))
+ return t;
+ }
+ }
+ for (t = tunnels_l[h1]; t; t = t->next) {
+ if (local == t->parms.iph.saddr ||
+ (local == t->parms.iph.daddr && MULTICAST(local))) {
+ if (t->parms.i_key == key && (t->dev->flags&IFF_UP))
+ return t;
+ }
+ }
+ for (t = tunnels_wc[h1]; t; t = t->next) {
+ if (t->parms.i_key == key && (t->dev->flags&IFF_UP))
+ return t;
+ }
+ if (ipgre_fb_tunnel_dev.flags&IFF_UP)
+ return &ipgre_fb_tunnel;
+ return NULL;
+}
+
+static struct ip_tunnel **ipgre_bucket(struct ip_tunnel *t)
+{
+ u32 remote = t->parms.iph.daddr;
+ u32 local = t->parms.iph.saddr;
+ u32 key = t->parms.i_key;
+ unsigned h = HASH(key);
+ int prio = 0;
+
+ if (local)
+ prio |= 1;
+ if (remote && !MULTICAST(remote)) {
+ prio |= 2;
+ h ^= HASH(remote);
+ }
+
+ return &tunnels[prio][h];
+}
+
+static void ipgre_tunnel_link(struct ip_tunnel *t)
+{
+ struct ip_tunnel **tp = ipgre_bucket(t);
+
+ t->next = *tp;
+ wmb();
+ *tp = t;
+}
+
+static void ipgre_tunnel_unlink(struct ip_tunnel *t)
+{
+ struct ip_tunnel **tp;
+
+ for (tp = ipgre_bucket(t); *tp; tp = &(*tp)->next) {
+ if (t == *tp) {
+ *tp = t->next;
+ synchronize_bh();
+ break;
+ }
+ }
+}
+
+static struct ip_tunnel * ipgre_tunnel_locate(struct ip_tunnel_parm *parms, int create)
+{
+ u32 remote = parms->iph.daddr;
+ u32 local = parms->iph.saddr;
+ u32 key = parms->i_key;
+ struct ip_tunnel *t, **tp, *nt;
+ struct device *dev;
+ unsigned h = HASH(key);
+ int prio = 0;
+
+ if (local)
+ prio |= 1;
+ if (remote && !MULTICAST(remote)) {
+ prio |= 2;
+ h ^= HASH(remote);
+ }
+ for (tp = &tunnels[prio][h]; (t = *tp) != NULL; tp = &t->next) {
+ if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) {
+ if (key == t->parms.i_key)
+ return t;
+ }
+ }
+ if (!create)
+ return NULL;
+
+ MOD_INC_USE_COUNT;
+ dev = kmalloc(sizeof(*dev) + sizeof(*t), GFP_KERNEL);
+ if (dev == NULL) {
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+ memset(dev, 0, sizeof(*dev) + sizeof(*t));
+ dev->priv = (void*)(dev+1);
+ nt = (struct ip_tunnel*)dev->priv;
+ nt->dev = dev;
+ dev->name = nt->parms.name;
+ dev->init = ipgre_tunnel_init;
+ memcpy(&nt->parms, parms, sizeof(*parms));
+ if (dev->name[0] == 0) {
+ int i;
+ for (i=1; i<100; i++) {
+ sprintf(dev->name, "gre%d", i);
+ if (dev_get(dev->name) == NULL)
+ break;
+ }
+ if (i==100)
+ goto failed;
+ memcpy(parms->name, dev->name, IFNAMSIZ);
+ }
+ if (register_netdevice(dev) < 0)
+ goto failed;
+
+ ipgre_tunnel_link(nt);
+ /* Do not decrement MOD_USE_COUNT here. */
+ return nt;
+
+failed:
+ kfree(dev);
+ MOD_DEC_USE_COUNT;
+ return NULL;
+}
+
+static void ipgre_tunnel_destroy(struct device *dev)
+{
+ ipgre_tunnel_unlink((struct ip_tunnel*)dev->priv);
+
+ if (dev != &ipgre_fb_tunnel_dev) {
+ kfree(dev);
+ MOD_DEC_USE_COUNT;
+ }
+}
+
+
+void ipgre_err(struct sk_buff *skb, unsigned char *dp, int len)
+{
+#ifndef I_WISH_WORLD_WERE_PERFECT
+
+/* It is not :-( All the routers (except for Linux) return only
+ 8 bytes of packet payload. It means, that precise relaying of
+ ICMP in the real Internet is absolutely infeasible.
+
+ Moreover, Cisco "wise men" put GRE key to the third word
+ in GRE header. It makes impossible maintaining even soft state for keyed
+ GRE tunnels with enabled checksum. Tell them "thank you".
+
+ Well, I wonder, rfc1812 was written by Cisco employee,
+ what the hell these idiots break standrads established
+ by themself???
+ */
+
+ struct iphdr *iph = (struct iphdr*)dp;
+ u16 *p = (u16*)(dp+(iph->ihl<<2));
+ int grehlen = (iph->ihl<<2) + 4;
+ int type = skb->h.icmph->type;
+ int code = skb->h.icmph->code;
+ struct ip_tunnel *t;
+ u16 flags;
+
+ flags = p[0];
+ if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
+ if (flags&(GRE_VERSION|GRE_ROUTING))
+ return;
+ if (flags&GRE_KEY) {
+ grehlen += 4;
+ if (flags&GRE_CSUM)
+ grehlen += 4;
+ }
+ }
+
+ /* If only 8 bytes returned, keyed message will be dropped here */
+ if (len < grehlen)
+ return;
+
+ switch (type) {
+ default:
+ case ICMP_PARAMETERPROB:
+ return;
+
+ case ICMP_DEST_UNREACH:
+ switch (code) {
+ case ICMP_SR_FAILED:
+ case ICMP_PORT_UNREACH:
+ /* Impossible event. */
+ return;
+ case ICMP_FRAG_NEEDED:
+ /* Soft state for pmtu is maintained by IP core. */
+ return;
+ default:
+ /* All others are translated to HOST_UNREACH.
+ rfc2003 contains "deep thoughts" about NET_UNREACH,
+ I believe they are just ether pollution. --ANK
+ */
+ break;
+ }
+ break;
+ case ICMP_TIME_EXCEEDED:
+ if (code != ICMP_EXC_TTL)
+ return;
+ break;
+ }
+
+ t = ipgre_tunnel_lookup(iph->daddr, iph->saddr, (flags&GRE_KEY) ? *(((u32*)p) + (grehlen>>2) - 1) : 0);
+ if (t == NULL || t->parms.iph.daddr == 0 || MULTICAST(t->parms.iph.daddr))
+ return;
+
+ if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
+ return;
+
+ if (jiffies - t->err_time < IPTUNNEL_ERR_TIMEO)
+ t->err_count++;
+ else
+ t->err_count = 1;
+ t->err_time = jiffies;
+ return;
+#else
+ struct iphdr *iph = (struct iphdr*)dp;
+ struct iphdr *eiph;
+ u16 *p = (u16*)(dp+(iph->ihl<<2));
+ int type = skb->h.icmph->type;
+ int code = skb->h.icmph->code;
+ int rel_type = 0;
+ int rel_code = 0;
+ int rel_info = 0;
+ u16 flags;
+ int grehlen = (iph->ihl<<2) + 4;
+ struct sk_buff *skb2;
+ struct rtable *rt;
+
+ if (p[1] != __constant_htons(ETH_P_IP))
+ return;
+
+ flags = p[0];
+ if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
+ if (flags&(GRE_VERSION|GRE_ROUTING))
+ return;
+ if (flags&GRE_CSUM)
+ grehlen += 4;
+ if (flags&GRE_KEY)
+ grehlen += 4;
+ if (flags&GRE_SEQ)
+ grehlen += 4;
+ }
+ if (len < grehlen + sizeof(struct iphdr))
+ return;
+ eiph = (struct iphdr*)(dp + grehlen);
+
+ switch (type) {
+ default:
+ return;
+ case ICMP_PARAMETERPROB:
+ if (skb->h.icmph->un.gateway < (iph->ihl<<2))
+ return;
+
+ /* So... This guy found something strange INSIDE encapsulated
+ packet. Well, he is fool, but what can we do ?
+ */
+ rel_type = ICMP_PARAMETERPROB;
+ rel_info = skb->h.icmph->un.gateway - grehlen;
+ break;
+
+ case ICMP_DEST_UNREACH:
+ switch (code) {
+ case ICMP_SR_FAILED:
+ case ICMP_PORT_UNREACH:
+ /* Impossible event. */
+ return;
+ case ICMP_FRAG_NEEDED:
+ /* And it is the only really necessary thing :-) */
+ rel_info = ntohs(skb->h.icmph->un.frag.mtu);
+ if (rel_info < grehlen+68)
+ return;
+ rel_info -= grehlen;
+ /* BSD 4.2 MORE DOES NOT EXIST IN NATURE. */
+ if (rel_info > ntohs(eiph->tot_len))
+ return;
+ break;
+ default:
+ /* All others are translated to HOST_UNREACH.
+ rfc2003 contains "deep thoughts" about NET_UNREACH,
+ I believe, it is just ether pollution. --ANK
+ */
+ rel_type = ICMP_DEST_UNREACH;
+ rel_code = ICMP_HOST_UNREACH;
+ break;
+ }
+ break;
+ case ICMP_TIME_EXCEEDED:
+ if (code != ICMP_EXC_TTL)
+ return;
+ break;
+ }
+
+ /* Prepare fake skb to feed it to icmp_send */
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2 == NULL)
+ return;
+ dst_release(skb2->dst);
+ skb2->dst = NULL;
+ skb_pull(skb2, skb->data - (u8*)eiph);
+ skb2->nh.raw = skb2->data;
+
+ /* Try to guess incoming interface */
+ if (ip_route_output(&rt, eiph->saddr, 0, RT_TOS(eiph->tos), 0)) {
+ kfree_skb(skb2);
+ return;
+ }
+ skb2->dev = rt->u.dst.dev;
+
+ /* route "incoming" packet */
+ if (rt->rt_flags&RTCF_LOCAL) {
+ ip_rt_put(rt);
+ rt = NULL;
+ if (ip_route_output(&rt, eiph->daddr, eiph->saddr, eiph->tos, 0) ||
+ rt->u.dst.dev->type != ARPHRD_IPGRE) {
+ ip_rt_put(rt);
+ kfree_skb(skb2);
+ return;
+ }
+ } else {
+ ip_rt_put(rt);
+ if (ip_route_input(skb2, eiph->daddr, eiph->saddr, eiph->tos, skb2->dev) ||
+ skb2->dst->dev->type != ARPHRD_IPGRE) {
+ kfree_skb(skb2);
+ return;
+ }
+ }
+
+ /* change mtu on this route */
+ if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
+ if (rel_info > skb2->dst->pmtu) {
+ kfree_skb(skb2);
+ return;
+ }
+ skb2->dst->pmtu = rel_info;
+ rel_info = htonl(rel_info);
+ } else if (type == ICMP_TIME_EXCEEDED) {
+ struct ip_tunnel *t = (struct ip_tunnel*)skb2->dev->priv;
+ if (t->parms.iph.ttl) {
+ rel_type = ICMP_DEST_UNREACH;
+ rel_code = ICMP_HOST_UNREACH;
+ }
+ }
+
+ icmp_send(skb2, rel_type, rel_code, rel_info);
+ kfree_skb(skb2);
+#endif
+}
+
+int ipgre_rcv(struct sk_buff *skb, unsigned short len)
+{
+ struct iphdr *iph = skb->nh.iph;
+ u8 *h = skb->h.raw;
+ u16 flags = *(u16*)h;
+ u16 csum = 0;
+ u32 key = 0;
+ u32 seqno = 0;
+ struct ip_tunnel *tunnel;
+ int offset = 4;
+
+ if (flags&(GRE_CSUM|GRE_KEY|GRE_ROUTING|GRE_SEQ|GRE_VERSION)) {
+ /* - Version must be 0.
+ - We do not support routing headers.
+ */
+ if (flags&(GRE_VERSION|GRE_ROUTING))
+ goto drop;
+
+ if (flags&GRE_CSUM) {
+ csum = ip_compute_csum(h, len);
+ offset += 4;
+ }
+ if (flags&GRE_KEY) {
+ key = *(u32*)(h + offset);
+ offset += 4;
+ }
+ if (flags&GRE_SEQ) {
+ seqno = ntohl(*(u32*)(h + offset));
+ offset += 4;
+ }
+ }
+
+ if ((tunnel = ipgre_tunnel_lookup(iph->saddr, iph->daddr, key)) != NULL) {
+ skb->mac.raw = skb->nh.raw;
+ skb->nh.raw = skb_pull(skb, h + offset - skb->data);
+ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
+ skb->ip_summed = 0;
+ skb->protocol = *(u16*)(h + 2);
+ skb->pkt_type = PACKET_HOST;
+#ifdef CONFIG_NET_IPGRE_BROADCAST
+ if (MULTICAST(iph->daddr)) {
+ /* Looped back packet, drop it! */
+ if (((struct rtable*)skb->dst)->key.iif == 0)
+ goto drop;
+ tunnel->stat.multicast++;
+ skb->pkt_type = PACKET_BROADCAST;
+ }
+#endif
+
+ if (((flags&GRE_CSUM) && csum) ||
+ (!(flags&GRE_CSUM) && tunnel->parms.i_flags&GRE_CSUM)) {
+ tunnel->stat.rx_crc_errors++;
+ tunnel->stat.rx_errors++;
+ goto drop;
+ }
+ if (tunnel->parms.i_flags&GRE_SEQ) {
+ if (!(flags&GRE_SEQ) ||
+ (tunnel->i_seqno && (s32)(seqno - tunnel->i_seqno) < 0)) {
+ tunnel->stat.rx_fifo_errors++;
+ tunnel->stat.rx_errors++;
+ goto drop;
+ }
+ tunnel->i_seqno = seqno + 1;
+ }
+ tunnel->stat.rx_packets++;
+ tunnel->stat.rx_bytes += skb->len;
+ skb->dev = tunnel->dev;
+ dst_release(skb->dst);
+ skb->dst = NULL;
+ netif_rx(skb);
+ return(0);
+ }
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
+
+drop:
+ kfree_skb(skb);
+ return(0);
+}
+
+static int ipgre_tunnel_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct ip_tunnel *tunnel = (struct ip_tunnel*)dev->priv;
+ struct net_device_stats *stats = &tunnel->stat;
+ struct iphdr *old_iph = skb->nh.iph;
+ struct iphdr *tiph;
+ u8 tos;
+ u16 df;
+ struct rtable *rt; /* Route to the other host */
+ struct device *tdev; /* Device to other host */
+ struct iphdr *iph; /* Our new IP header */
+ int max_headroom; /* The extra header space needed */
+ int gre_hlen;
+ u32 dst;
+ int mtu;
+
+ if (tunnel->recursion++) {
+ tunnel->stat.collisions++;
+ goto tx_error;
+ }
+
+ if (dev->hard_header) {
+ gre_hlen = 0;
+ tiph = (struct iphdr*)skb->data;
+ } else {
+ gre_hlen = tunnel->hlen;
+ tiph = &tunnel->parms.iph;
+ }
+
+ if ((dst = tiph->daddr) == 0) {
+ /* NBMA tunnel */
+
+ if (skb->dst == NULL) {
+ tunnel->stat.tx_fifo_errors++;
+ goto tx_error;
+ }
+
+ if (skb->protocol == __constant_htons(ETH_P_IP)) {
+ rt = (struct rtable*)skb->dst;
+ if ((dst = rt->rt_gateway) == 0)
+ goto tx_error_icmp;
+ }
+#ifdef CONFIG_IPV6
+ else if (skb->protocol == __constant_htons(ETH_P_IPV6)) {
+ struct in6_addr *addr6;
+ int addr_type;
+ struct neighbour *neigh = skb->dst->neighbour;
+
+ if (neigh == NULL)
+ goto tx_error;
+
+ addr6 = (struct in6_addr*)&neigh->primary_key;
+ addr_type = ipv6_addr_type(addr6);
+
+ if (addr_type == IPV6_ADDR_ANY) {
+ addr6 = &skb->nh.ipv6h->daddr;
+ addr_type = ipv6_addr_type(addr6);
+ }
+
+ if ((addr_type & IPV6_ADDR_COMPATv4) == 0)
+ goto tx_error_icmp;
+
+ dst = addr6->s6_addr32[3];
+ }
+#endif
+ else
+ goto tx_error;
+ }
+
+ tos = tiph->tos;
+ if (tos&1) {
+ if (skb->protocol == __constant_htons(ETH_P_IP))
+ tos = old_iph->tos;
+ tos &= ~1;
+ }
+
+ if (ip_route_output(&rt, dst, tiph->saddr, RT_TOS(tos), tunnel->parms.link)) {
+ tunnel->stat.tx_carrier_errors++;
+ goto tx_error;
+ }
+ tdev = rt->u.dst.dev;
+
+ if (tdev == dev) {
+ ip_rt_put(rt);
+ tunnel->stat.collisions++;
+ goto tx_error;
+ }
+
+ df = tiph->frag_off;
+ mtu = rt->u.dst.pmtu - tunnel->hlen;
+
+ if (skb->protocol == __constant_htons(ETH_P_IP)) {
+ if (skb->dst && mtu < skb->dst->pmtu && mtu >= 68)
+ skb->dst->pmtu = mtu;
+
+ df |= (old_iph->frag_off&__constant_htons(IP_DF));
+
+ if ((old_iph->frag_off&__constant_htons(IP_DF)) &&
+ mtu < ntohs(old_iph->tot_len)) {
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
+ ip_rt_put(rt);
+ goto tx_error;
+ }
+ }
+#ifdef CONFIG_IPV6
+ else if (skb->protocol == __constant_htons(ETH_P_IPV6)) {
+ struct rt6_info *rt6 = (struct rt6_info*)skb->dst;
+
+ if (rt6 && mtu < rt6->u.dst.pmtu && mtu >= IPV6_MIN_MTU) {
+ if ((tunnel->parms.iph.daddr && !MULTICAST(tunnel->parms.iph.daddr)) ||
+ rt6->rt6i_dst.plen == 128) {
+ rt6->rt6i_flags |= RTF_MODIFIED;
+ skb->dst->pmtu = mtu;
+ }
+ }
+
+ if (mtu >= IPV6_MIN_MTU && mtu < skb->len - tunnel->hlen + gre_hlen) {
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
+ ip_rt_put(rt);
+ goto tx_error;
+ }
+ }
+#endif
+
+ if (tunnel->err_count > 0) {
+ if (jiffies - tunnel->err_time < IPTUNNEL_ERR_TIMEO) {
+ tunnel->err_count--;
+
+ dst_link_failure(skb);
+ } else
+ tunnel->err_count = 0;
+ }
+
+ skb->h.raw = skb->nh.raw;
+
+ max_headroom = ((tdev->hard_header_len+15)&~15)+ gre_hlen;
+
+ if (skb_headroom(skb) < max_headroom || skb_cloned(skb) || skb_shared(skb)) {
+ struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
+ if (!new_skb) {
+ ip_rt_put(rt);
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ tunnel->recursion--;
+ return 0;
+ }
+ if (skb->sk)
+ skb_set_owner_w(new_skb, skb->sk);
+ dev_kfree_skb(skb);
+ skb = new_skb;
+ }
+
+ skb->nh.raw = skb_push(skb, gre_hlen);
+ memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
+ dst_release(skb->dst);
+ skb->dst = &rt->u.dst;
+
+ /*
+ * Push down and install the IPIP header.
+ */
+
+ iph = skb->nh.iph;
+ iph->version = 4;
+ iph->ihl = sizeof(struct iphdr) >> 2;
+ iph->frag_off = df;
+ iph->protocol = IPPROTO_GRE;
+ iph->tos = tos;
+ iph->daddr = rt->rt_dst;
+ iph->saddr = rt->rt_src;
+
+ if ((iph->ttl = tiph->ttl) == 0) {
+ if (skb->protocol == __constant_htons(ETH_P_IP))
+ iph->ttl = old_iph->ttl;
+#ifdef CONFIG_IPV6
+ else if (skb->protocol == __constant_htons(ETH_P_IPV6))
+ iph->ttl = ((struct ipv6hdr*)old_iph)->hop_limit;
+#endif
+ else
+ iph->ttl = ip_statistics.IpDefaultTTL;
+ }
+
+ ((u16*)(iph+1))[0] = tunnel->parms.o_flags;
+ ((u16*)(iph+1))[1] = skb->protocol;
+
+ if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) {
+ u32 *ptr = (u32*)(((u8*)iph) + tunnel->hlen - 4);
+
+ if (tunnel->parms.o_flags&GRE_SEQ) {
+ ++tunnel->o_seqno;
+ *ptr = htonl(tunnel->o_seqno);
+ ptr--;
+ }
+ if (tunnel->parms.o_flags&GRE_KEY) {
+ *ptr = tunnel->parms.o_key;
+ ptr--;
+ }
+ if (tunnel->parms.o_flags&GRE_CSUM) {
+ *ptr = 0;
+ *(__u16*)ptr = ip_compute_csum((void*)(iph+1), skb->len - sizeof(struct iphdr));
+ }
+ }
+
+ iph->tot_len = htons(skb->len);
+ iph->id = htons(ip_id_count++);
+ ip_send_check(iph);
+
+ stats->tx_bytes += skb->len;
+ stats->tx_packets++;
+ ip_send(skb);
+ tunnel->recursion--;
+ return 0;
+
+tx_error_icmp:
+ dst_link_failure(skb);
+
+tx_error:
+ stats->tx_errors++;
+ dev_kfree_skb(skb);
+ tunnel->recursion--;
+ return 0;
+}
+
+static int
+ipgre_tunnel_ioctl (struct device *dev, struct ifreq *ifr, int cmd)
+{
+ int err = 0;
+ struct ip_tunnel_parm p;
+ struct ip_tunnel *t;
+
+ MOD_INC_USE_COUNT;
+
+ switch (cmd) {
+ case SIOCGETTUNNEL:
+ t = NULL;
+ if (dev == &ipgre_fb_tunnel_dev) {
+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
+ err = -EFAULT;
+ break;
+ }
+ t = ipgre_tunnel_locate(&p, 0);
+ }
+ if (t == NULL)
+ t = (struct ip_tunnel*)dev->priv;
+ memcpy(&p, &t->parms, sizeof(p));
+ if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
+ err = -EFAULT;
+ break;
+
+ case SIOCADDTUNNEL:
+ case SIOCCHGTUNNEL:
+ err = -EPERM;
+ if (!capable(CAP_NET_ADMIN))
+ goto done;
+
+ err = -EFAULT;
+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
+ goto done;
+
+ err = -EINVAL;
+ if (p.iph.version != 4 || p.iph.protocol != IPPROTO_GRE ||
+ p.iph.ihl != 5 || (p.iph.frag_off&__constant_htons(~IP_DF)) ||
+ ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING)))
+ goto done;
+ if (p.iph.ttl)
+ p.iph.frag_off |= __constant_htons(IP_DF);
+
+ if (!(p.i_flags&GRE_KEY))
+ p.i_key = 0;
+ if (!(p.o_flags&GRE_KEY))
+ p.o_key = 0;
+
+ t = ipgre_tunnel_locate(&p, cmd == SIOCADDTUNNEL);
+
+ if (dev != &ipgre_fb_tunnel_dev && cmd == SIOCCHGTUNNEL &&
+ t != &ipgre_fb_tunnel) {
+ if (t != NULL) {
+ if (t->dev != dev) {
+ err = -EEXIST;
+ break;
+ }
+ } else {
+ unsigned nflags=0;
+
+ t = (struct ip_tunnel*)dev->priv;
+
+ if (MULTICAST(p.iph.daddr))
+ nflags = IFF_BROADCAST;
+ else if (p.iph.daddr)
+ nflags = IFF_POINTOPOINT;
+
+ if ((dev->flags^nflags)&(IFF_POINTOPOINT|IFF_BROADCAST)) {
+ err = -EINVAL;
+ break;
+ }
+ start_bh_atomic();
+ ipgre_tunnel_unlink(t);
+ t->parms.iph.saddr = p.iph.saddr;
+ t->parms.iph.daddr = p.iph.daddr;
+ t->parms.i_key = p.i_key;
+ t->parms.o_key = p.o_key;
+ memcpy(dev->dev_addr, &p.iph.saddr, 4);
+ memcpy(dev->broadcast, &p.iph.daddr, 4);
+ ipgre_tunnel_link(t);
+ end_bh_atomic();
+ netdev_state_change(dev);
+ }
+ }
+
+ if (t) {
+ err = 0;
+ if (cmd == SIOCCHGTUNNEL) {
+ t->parms.iph.ttl = p.iph.ttl;
+ t->parms.iph.tos = p.iph.tos;
+ t->parms.iph.frag_off = p.iph.frag_off;
+ }
+ if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p)))
+ err = -EFAULT;
+ } else
+ err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
+ break;
+
+ case SIOCDELTUNNEL:
+ err = -EPERM;
+ if (!capable(CAP_NET_ADMIN))
+ goto done;
+
+ if (dev == &ipgre_fb_tunnel_dev) {
+ err = -EFAULT;
+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
+ goto done;
+ err = -ENOENT;
+ if ((t = ipgre_tunnel_locate(&p, 0)) == NULL)
+ goto done;
+ err = -EPERM;
+ if (t == &ipgre_fb_tunnel)
+ goto done;
+ }
+ err = unregister_netdevice(dev);
+ break;
+
+ default:
+ err = -EINVAL;
+ }
+
+done:
+ MOD_DEC_USE_COUNT;
+ return err;
+}
+
+static struct net_device_stats *ipgre_tunnel_get_stats(struct device *dev)
+{
+ return &(((struct ip_tunnel*)dev->priv)->stat);
+}
+
+static int ipgre_tunnel_change_mtu(struct device *dev, int new_mtu)
+{
+ struct ip_tunnel *tunnel = (struct ip_tunnel*)dev->priv;
+ if (new_mtu < 68 || new_mtu > 0xFFF8 - tunnel->hlen)
+ return -EINVAL;
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+#ifdef CONFIG_NET_IPGRE_BROADCAST
+/* Nice toy. Unfortunately, useless in real life :-)
+ It allows to construct virtual multiprotocol broadcast "LAN"
+ over the Internet, provided multicast routing is tuned.
+
+
+ I have no idea was this bicycle invented before me,
+ so that I had to set ARPHRD_IPGRE to a random value.
+ I have an impression, that Cisco could make something similar,
+ but this feature is apparently missing in IOS<=11.2(8).
+
+ I set up 10.66.66/24 and fec0:6666:6666::0/96 as virtual networks
+ with broadcast 224.66.66.66. If you have access to mbone, play with me :-)
+
+ ping -t 255 224.66.66.66
+
+ If nobody answers, mbone does not work.
+
+ ip tunnel add Universe mode gre remote 224.66.66.66 local <Your_real_addr> ttl 255
+ ip addr add 10.66.66.<somewhat>/24 dev Universe
+ ifconfig Universe up
+ ifconfig Universe add fe80::<Your_real_addr>/10
+ ifconfig Universe add fec0:6666:6666::<Your_real_addr>/96
+ ftp 10.66.66.66
+ ...
+ ftp fec0:6666:6666::193.233.7.65
+ ...
+
+ */
+
+static int ipgre_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+ void *daddr, void *saddr, unsigned len)
+{
+ struct ip_tunnel *t = (struct ip_tunnel*)dev->priv;
+ struct iphdr *iph = (struct iphdr *)skb_push(skb, t->hlen);
+ u16 *p = (u16*)(iph+1);
+
+ memcpy(iph, &t->parms.iph, sizeof(struct iphdr));
+ p[0] = t->parms.o_flags;
+ p[1] = htons(type);
+
+ /*
+ * Set the source hardware address.
+ */
+
+ if (saddr)
+ memcpy(&iph->saddr, saddr, 4);
+
+ if (daddr) {
+ memcpy(&iph->daddr, daddr, 4);
+ return t->hlen;
+ }
+ if (iph->daddr && !MULTICAST(iph->daddr))
+ return t->hlen;
+
+ return -t->hlen;
+}
+
+static int ipgre_open(struct device *dev)
+{
+ struct ip_tunnel *t = (struct ip_tunnel*)dev->priv;
+
+ MOD_INC_USE_COUNT;
+ if (MULTICAST(t->parms.iph.daddr)) {
+ struct rtable *rt;
+ if (ip_route_output(&rt, t->parms.iph.daddr,
+ t->parms.iph.saddr, RT_TOS(t->parms.iph.tos),
+ t->parms.link)) {
+ MOD_DEC_USE_COUNT;
+ return -EADDRNOTAVAIL;
+ }
+ dev = rt->u.dst.dev;
+ ip_rt_put(rt);
+ if (dev->ip_ptr == NULL) {
+ MOD_DEC_USE_COUNT;
+ return -EADDRNOTAVAIL;
+ }
+ t->mlink = dev->ifindex;
+ ip_mc_inc_group(dev->ip_ptr, t->parms.iph.daddr);
+ }
+ return 0;
+}
+
+static int ipgre_close(struct device *dev)
+{
+ struct ip_tunnel *t = (struct ip_tunnel*)dev->priv;
+ if (MULTICAST(t->parms.iph.daddr) && t->mlink) {
+ dev = dev_get_by_index(t->mlink);
+ if (dev && dev->ip_ptr)
+ ip_mc_dec_group(dev->ip_ptr, t->parms.iph.daddr);
+ }
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#endif
+
+static void ipgre_tunnel_init_gen(struct device *dev)
+{
+ struct ip_tunnel *t = (struct ip_tunnel*)dev->priv;
+
+ dev->destructor = ipgre_tunnel_destroy;
+ dev->hard_start_xmit = ipgre_tunnel_xmit;
+ dev->get_stats = ipgre_tunnel_get_stats;
+ dev->do_ioctl = ipgre_tunnel_ioctl;
+ dev->change_mtu = ipgre_tunnel_change_mtu;
+
+ dev_init_buffers(dev);
+
+ dev->type = ARPHRD_IPGRE;
+ dev->hard_header_len = LL_MAX_HEADER + sizeof(struct iphdr) + 4;
+ dev->mtu = 1500 - sizeof(struct iphdr) - 4;
+ dev->flags = IFF_NOARP;
+ dev->iflink = 0;
+ dev->addr_len = 4;
+ memcpy(dev->dev_addr, &t->parms.iph.saddr, 4);
+ memcpy(dev->broadcast, &t->parms.iph.daddr, 4);
+}
+
+static int ipgre_tunnel_init(struct device *dev)
+{
+ struct device *tdev = NULL;
+ struct ip_tunnel *tunnel;
+ struct iphdr *iph;
+ int hlen = LL_MAX_HEADER;
+ int mtu = 1500;
+ int addend = sizeof(struct iphdr) + 4;
+
+ tunnel = (struct ip_tunnel*)dev->priv;
+ iph = &tunnel->parms.iph;
+
+ ipgre_tunnel_init_gen(dev);
+
+ /* Guess output device to choose reasonable mtu and hard_header_len */
+
+ if (iph->daddr) {
+ struct rtable *rt;
+ if (!ip_route_output(&rt, iph->daddr, iph->saddr, RT_TOS(iph->tos), tunnel->parms.link)) {
+ tdev = rt->u.dst.dev;
+ ip_rt_put(rt);
+ }
+
+ dev->flags |= IFF_POINTOPOINT;
+
+#ifdef CONFIG_NET_IPGRE_BROADCAST
+ if (MULTICAST(iph->daddr)) {
+ if (!iph->saddr)
+ return -EINVAL;
+ dev->flags = IFF_BROADCAST;
+ dev->hard_header = ipgre_header;
+ dev->open = ipgre_open;
+ dev->stop = ipgre_close;
+ }
+#endif
+ }
+
+ if (!tdev && tunnel->parms.link)
+ tdev = dev_get_by_index(tunnel->parms.link);
+
+ if (tdev) {
+ hlen = tdev->hard_header_len;
+ mtu = tdev->mtu;
+ }
+ dev->iflink = tunnel->parms.link;
+
+ /* Precalculate GRE options length */
+ if (tunnel->parms.o_flags&(GRE_CSUM|GRE_KEY|GRE_SEQ)) {
+ if (tunnel->parms.o_flags&GRE_CSUM)
+ addend += 4;
+ if (tunnel->parms.o_flags&GRE_KEY)
+ addend += 4;
+ if (tunnel->parms.o_flags&GRE_SEQ)
+ addend += 4;
+ }
+ dev->hard_header_len = hlen + addend;
+ dev->mtu = mtu - addend;
+ tunnel->hlen = addend;
+ return 0;
+}
+
+#ifdef MODULE
+static int ipgre_fb_tunnel_open(struct device *dev)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int ipgre_fb_tunnel_close(struct device *dev)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+#endif
+
+__initfunc(int ipgre_fb_tunnel_init(struct device *dev))
+{
+ struct ip_tunnel *tunnel = (struct ip_tunnel*)dev->priv;
+ struct iphdr *iph;
+
+ ipgre_tunnel_init_gen(dev);
+#ifdef MODULE
+ dev->open = ipgre_fb_tunnel_open;
+ dev->stop = ipgre_fb_tunnel_close;
+#endif
+
+ iph = &ipgre_fb_tunnel.parms.iph;
+ iph->version = 4;
+ iph->protocol = IPPROTO_GRE;
+ iph->ihl = 5;
+ tunnel->hlen = sizeof(struct iphdr) + 4;
+
+ tunnels_wc[0] = &ipgre_fb_tunnel;
+ return 0;
+}
+
+
+static struct inet_protocol ipgre_protocol = {
+ ipgre_rcv, /* GRE handler */
+ ipgre_err, /* TUNNEL error control */
+ 0, /* next */
+ IPPROTO_GRE, /* protocol ID */
+ 0, /* copy */
+ NULL, /* data */
+ "GRE" /* name */
+};
+
+
+/*
+ * And now the modules code and kernel interface.
+ */
+
+#ifdef MODULE
+int init_module(void)
+#else
+__initfunc(int ipgre_init(void))
+#endif
+{
+ printk(KERN_INFO "GRE over IPv4 tunneling driver\n");
+
+ ipgre_fb_tunnel_dev.priv = (void*)&ipgre_fb_tunnel;
+ ipgre_fb_tunnel_dev.name = ipgre_fb_tunnel.parms.name;
+#ifdef MODULE
+ register_netdev(&ipgre_fb_tunnel_dev);
+#else
+ register_netdevice(&ipgre_fb_tunnel_dev);
+#endif
+
+ inet_add_protocol(&ipgre_protocol);
+ return 0;
+}
+
+#ifdef MODULE
+
+void cleanup_module(void)
+{
+ if ( inet_del_protocol(&ipgre_protocol) < 0 )
+ printk(KERN_INFO "ipgre close: can't remove protocol\n");
+
+ unregister_netdev(&ipgre_fb_tunnel_dev);
+}
+
+#endif
diff --git a/pfinet/linux-src/net/ipv4/ip_input.c b/pfinet/linux-src/net/ipv4/ip_input.c
new file mode 100644
index 00000000..a6ff57c7
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_input.c
@@ -0,0 +1,550 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * The Internet Protocol (IP) module.
+ *
+ * Version: $Id: ip_input.c,v 1.37 1999/04/22 10:38:36 davem Exp $
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Donald Becker, <becker@super.org>
+ * Alan Cox, <Alan.Cox@linux.org>
+ * Richard Underwood
+ * Stefan Becker, <stefanb@yello.ping.de>
+ * Jorge Cwik, <jorge@laser.satlink.net>
+ * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ *
+ *
+ * Fixes:
+ * Alan Cox : Commented a couple of minor bits of surplus code
+ * Alan Cox : Undefining IP_FORWARD doesn't include the code
+ * (just stops a compiler warning).
+ * Alan Cox : Frames with >=MAX_ROUTE record routes, strict routes or loose routes
+ * are junked rather than corrupting things.
+ * Alan Cox : Frames to bad broadcast subnets are dumped
+ * We used to process them non broadcast and
+ * boy could that cause havoc.
+ * Alan Cox : ip_forward sets the free flag on the
+ * new frame it queues. Still crap because
+ * it copies the frame but at least it
+ * doesn't eat memory too.
+ * Alan Cox : Generic queue code and memory fixes.
+ * Fred Van Kempen : IP fragment support (borrowed from NET2E)
+ * Gerhard Koerting: Forward fragmented frames correctly.
+ * Gerhard Koerting: Fixes to my fix of the above 8-).
+ * Gerhard Koerting: IP interface addressing fix.
+ * Linus Torvalds : More robustness checks
+ * Alan Cox : Even more checks: Still not as robust as it ought to be
+ * Alan Cox : Save IP header pointer for later
+ * Alan Cox : ip option setting
+ * Alan Cox : Use ip_tos/ip_ttl settings
+ * Alan Cox : Fragmentation bogosity removed
+ * (Thanks to Mark.Bush@prg.ox.ac.uk)
+ * Dmitry Gorodchanin : Send of a raw packet crash fix.
+ * Alan Cox : Silly ip bug when an overlength
+ * fragment turns up. Now frees the
+ * queue.
+ * Linus Torvalds/ : Memory leakage on fragmentation
+ * Alan Cox : handling.
+ * Gerhard Koerting: Forwarding uses IP priority hints
+ * Teemu Rantanen : Fragment problems.
+ * Alan Cox : General cleanup, comments and reformat
+ * Alan Cox : SNMP statistics
+ * Alan Cox : BSD address rule semantics. Also see
+ * UDP as there is a nasty checksum issue
+ * if you do things the wrong way.
+ * Alan Cox : Always defrag, moved IP_FORWARD to the config.in file
+ * Alan Cox : IP options adjust sk->priority.
+ * Pedro Roque : Fix mtu/length error in ip_forward.
+ * Alan Cox : Avoid ip_chk_addr when possible.
+ * Richard Underwood : IP multicasting.
+ * Alan Cox : Cleaned up multicast handlers.
+ * Alan Cox : RAW sockets demultiplex in the BSD style.
+ * Gunther Mayer : Fix the SNMP reporting typo
+ * Alan Cox : Always in group 224.0.0.1
+ * Pauline Middelink : Fast ip_checksum update when forwarding
+ * Masquerading support.
+ * Alan Cox : Multicast loopback error for 224.0.0.1
+ * Alan Cox : IP_MULTICAST_LOOP option.
+ * Alan Cox : Use notifiers.
+ * Bjorn Ekwall : Removed ip_csum (from slhc.c too)
+ * Bjorn Ekwall : Moved ip_fast_csum to ip.h (inline!)
+ * Stefan Becker : Send out ICMP HOST REDIRECT
+ * Arnt Gulbrandsen : ip_build_xmit
+ * Alan Cox : Per socket routing cache
+ * Alan Cox : Fixed routing cache, added header cache.
+ * Alan Cox : Loopback didn't work right in original ip_build_xmit - fixed it.
+ * Alan Cox : Only send ICMP_REDIRECT if src/dest are the same net.
+ * Alan Cox : Incoming IP option handling.
+ * Alan Cox : Set saddr on raw output frames as per BSD.
+ * Alan Cox : Stopped broadcast source route explosions.
+ * Alan Cox : Can disable source routing
+ * Takeshi Sone : Masquerading didn't work.
+ * Dave Bonn,Alan Cox : Faster IP forwarding whenever possible.
+ * Alan Cox : Memory leaks, tramples, misc debugging.
+ * Alan Cox : Fixed multicast (by popular demand 8))
+ * Alan Cox : Fixed forwarding (by even more popular demand 8))
+ * Alan Cox : Fixed SNMP statistics [I think]
+ * Gerhard Koerting : IP fragmentation forwarding fix
+ * Alan Cox : Device lock against page fault.
+ * Alan Cox : IP_HDRINCL facility.
+ * Werner Almesberger : Zero fragment bug
+ * Alan Cox : RAW IP frame length bug
+ * Alan Cox : Outgoing firewall on build_xmit
+ * A.N.Kuznetsov : IP_OPTIONS support throughout the kernel
+ * Alan Cox : Multicast routing hooks
+ * Jos Vos : Do accounting *before* call_in_firewall
+ * Willy Konynenberg : Transparent proxying support
+ * Stephan Uphoff : Check IP header length field
+ *
+ *
+ *
+ * To Fix:
+ * IP fragmentation wants rewriting cleanly. The RFC815 algorithm is much more efficient
+ * and could be made very efficient with the addition of some virtual memory hacks to permit
+ * the allocation of a buffer that can then be 'grown' by twiddling page tables.
+ * Output fragmentation wants updating along with the buffer management to use a single
+ * interleaved copy algorithm so that fragmenting has a one copy overhead. Actual packet
+ * output should probably do its own fragmentation at the UDP/RAW layer. TCP shouldn't cause
+ * fragmentation anyway.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/config.h>
+
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <net/snmp.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/arp.h>
+#include <net/icmp.h>
+#include <net/raw.h>
+#include <net/checksum.h>
+#include <linux/ip_fw.h>
+#ifdef CONFIG_IP_MASQUERADE
+#include <net/ip_masq.h>
+#endif
+#include <linux/firewall.h>
+#include <linux/mroute.h>
+#include <linux/netlink.h>
+
+/*
+ * SNMP management statistics
+ */
+
+struct ip_mib ip_statistics={2,IPDEFTTL,}; /* Forwarding=No, Default TTL=64 */
+
+int sysctl_ip_always_defrag = 0;
+
+/*
+ * Handle the issuing of an ioctl() request
+ * for the ip device. This is scheduled to
+ * disappear
+ */
+
+int ip_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ switch(cmd)
+ {
+ default:
+ return(-EINVAL);
+ }
+}
+
+/*
+ * 0 - deliver
+ * 1 - block
+ */
+static __inline__ int icmp_filter(struct sock *sk, struct sk_buff *skb)
+{
+ int type;
+
+ type = skb->h.icmph->type;
+ if (type < 32)
+ return test_bit(type, &sk->tp_pinfo.tp_raw4.filter);
+
+ /* Do not block unknown ICMP types */
+ return 0;
+}
+
+/*
+ * Process Router Attention IP option
+ */
+int ip_call_ra_chain(struct sk_buff *skb)
+{
+ struct ip_ra_chain *ra;
+ u8 protocol = skb->nh.iph->protocol;
+ struct sock *last = NULL;
+
+ for (ra = ip_ra_chain; ra; ra = ra->next) {
+ struct sock *sk = ra->sk;
+ if (sk && sk->num == protocol) {
+ if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
+ skb = ip_defrag(skb);
+ if (skb == NULL)
+ return 1;
+ }
+ if (last) {
+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2)
+ raw_rcv(last, skb2);
+ }
+ last = sk;
+ }
+ }
+
+ if (last) {
+ raw_rcv(last, skb);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Deliver IP Packets to the higher protocol layers.
+ */
+int ip_local_deliver(struct sk_buff *skb)
+{
+ struct iphdr *iph = skb->nh.iph;
+ struct inet_protocol *ipprot;
+ struct sock *raw_sk=NULL;
+ unsigned char hash;
+ int flag = 0;
+
+ /*
+ * Reassemble IP fragments.
+ */
+
+ if (sysctl_ip_always_defrag == 0 &&
+ (iph->frag_off & htons(IP_MF|IP_OFFSET))) {
+ skb = ip_defrag(skb);
+ if (!skb)
+ return 0;
+ iph = skb->nh.iph;
+ }
+
+#ifdef CONFIG_IP_MASQUERADE
+ /*
+ * Do we need to de-masquerade this packet?
+ */
+ {
+ int ret;
+ /*
+ * Some masq modules can re-inject packets if
+ * bad configured.
+ */
+
+ if((IPCB(skb)->flags&IPSKB_MASQUERADED)) {
+ printk(KERN_DEBUG "ip_input(): demasq recursion detected. Check masq modules configuration\n");
+ kfree_skb(skb);
+ return 0;
+ }
+
+ ret = ip_fw_demasquerade(&skb);
+ if (ret < 0) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (ret) {
+ iph=skb->nh.iph;
+ IPCB(skb)->flags |= IPSKB_MASQUERADED;
+ dst_release(skb->dst);
+ skb->dst = NULL;
+ if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev)) {
+ kfree_skb(skb);
+ return 0;
+ }
+ return skb->dst->input(skb);
+ }
+ }
+#endif
+
+ /*
+ * Point into the IP datagram, just past the header.
+ */
+
+ skb->h.raw = skb->nh.raw + iph->ihl*4;
+
+ /*
+ * Deliver to raw sockets. This is fun as to avoid copies we want to make no
+ * surplus copies.
+ *
+ * RFC 1122: SHOULD pass TOS value up to the transport layer.
+ * -> It does. And not only TOS, but all IP header.
+ */
+
+ /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
+ hash = iph->protocol & (MAX_INET_PROTOS - 1);
+
+ /*
+ * If there maybe a raw socket we must check - if not we don't care less
+ */
+
+ if((raw_sk = raw_v4_htable[hash]) != NULL) {
+ struct sock *sknext = NULL;
+ struct sk_buff *skb1;
+ raw_sk = raw_v4_lookup(raw_sk, iph->protocol, iph->saddr, iph->daddr, skb->dev->ifindex);
+ if(raw_sk) { /* Any raw sockets */
+ do {
+ /* Find the next */
+ sknext = raw_v4_lookup(raw_sk->next, iph->protocol,
+ iph->saddr, iph->daddr, skb->dev->ifindex);
+ if (iph->protocol != IPPROTO_ICMP || !icmp_filter(raw_sk, skb)) {
+ if (sknext == NULL)
+ break;
+ skb1 = skb_clone(skb, GFP_ATOMIC);
+ if(skb1)
+ {
+ raw_rcv(raw_sk, skb1);
+ }
+ }
+ raw_sk = sknext;
+ } while(raw_sk!=NULL);
+
+ /* Here either raw_sk is the last raw socket, or NULL if
+ * none. We deliver to the last raw socket AFTER the
+ * protocol checks as it avoids a surplus copy.
+ */
+ }
+ }
+
+ /*
+ * skb->h.raw now points at the protocol beyond the IP header.
+ */
+
+ for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
+ {
+ struct sk_buff *skb2;
+
+ if (ipprot->protocol != iph->protocol)
+ continue;
+ /*
+ * See if we need to make a copy of it. This will
+ * only be set if more than one protocol wants it.
+ * and then not for the last one. If there is a pending
+ * raw delivery wait for that
+ */
+
+ if (ipprot->copy || raw_sk)
+ {
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if(skb2==NULL)
+ continue;
+ }
+ else
+ {
+ skb2 = skb;
+ }
+ flag = 1;
+
+ /*
+ * Pass on the datagram to each protocol that wants it,
+ * based on the datagram protocol. We should really
+ * check the protocol handler's return values here...
+ */
+
+ ipprot->handler(skb2, ntohs(iph->tot_len) - (iph->ihl * 4));
+ }
+
+ /*
+ * All protocols checked.
+ * If this packet was a broadcast, we may *not* reply to it, since that
+ * causes (proven, grin) ARP storms and a leakage of memory (i.e. all
+ * ICMP reply messages get queued up for transmission...)
+ */
+
+ if(raw_sk!=NULL) /* Shift to last raw user */
+ {
+ raw_rcv(raw_sk, skb);
+
+ }
+ else if (!flag) /* Free and report errors */
+ {
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
+ kfree_skb(skb);
+ }
+
+ return(0);
+}
+
+/*
+ * Main IP Receive routine.
+ */
+int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct iphdr *iph = skb->nh.iph;
+#ifdef CONFIG_FIREWALL
+ int fwres;
+ u16 rport;
+#endif /* CONFIG_FIREWALL */
+
+ /*
+ * When the interface is in promisc. mode, drop all the crap
+ * that it receives, do not try to analyse it.
+ */
+ if (skb->pkt_type == PACKET_OTHERHOST)
+ goto drop;
+
+ ip_statistics.IpInReceives++;
+
+ /*
+ * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum.
+ *
+ * Is the datagram acceptable?
+ *
+ * 1. Length at least the size of an ip header
+ * 2. Version of 4
+ * 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums]
+ * 4. Doesn't have a bogus length
+ */
+
+ if (skb->len < sizeof(struct iphdr))
+ goto inhdr_error;
+
+ if (skb->len < (iph->ihl << 2))
+ goto inhdr_error;
+
+ if (iph->ihl < 5 || iph->version != 4 || ip_fast_csum((u8 *)iph, iph->ihl) != 0)
+ goto inhdr_error;
+
+ {
+ __u32 len = ntohs(iph->tot_len);
+ if (skb->len < len)
+ goto inhdr_error;
+
+ if (len < (iph->ihl << 2))
+ goto inhdr_error;
+
+ /*
+ * Our transport medium may have padded the buffer out. Now we know it
+ * is IP we can trim to the true length of the frame.
+ * Note this now means skb->len holds ntohs(iph->tot_len).
+ */
+
+ __skb_trim(skb, len);
+ }
+
+ /* Won't send ICMP reply, since skb->dst == NULL. --RR */
+ if (sysctl_ip_always_defrag != 0 &&
+ iph->frag_off & htons(IP_MF|IP_OFFSET)) {
+ skb = ip_defrag(skb);
+ if (!skb)
+ return 0;
+ iph = skb->nh.iph;
+ ip_send_check(iph);
+ }
+
+#ifdef CONFIG_FIREWALL
+ /*
+ * See if the firewall wants to dispose of the packet.
+ *
+ * We can't do ICMP reply or local delivery before routing,
+ * so we delay those decisions until after route. --RR
+ */
+ fwres = call_in_firewall(PF_INET, dev, iph, &rport, &skb);
+ if (fwres < FW_ACCEPT && fwres != FW_REJECT)
+ goto drop;
+ iph = skb->nh.iph;
+#endif /* CONFIG_FIREWALL */
+
+ /*
+ * Initialise the virtual path cache for the packet. It describes
+ * how the packet travels inside Linux networking.
+ */
+ if (skb->dst == NULL) {
+ if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))
+ goto drop;
+#ifdef CONFIG_CPU_IS_SLOW
+ if (net_cpu_congestion > 10 && !(iph->tos&IPTOS_RELIABILITY) &&
+ IPTOS_PREC(iph->tos) < IPTOS_PREC_INTERNETCONTROL) {
+ goto drop;
+ }
+#endif
+ }
+
+#ifdef CONFIG_NET_CLS_ROUTE
+ if (skb->dst->tclassid) {
+ u32 idx = skb->dst->tclassid;
+ ip_rt_acct[idx&0xFF].o_packets++;
+ ip_rt_acct[idx&0xFF].o_bytes+=skb->len;
+ ip_rt_acct[(idx>>16)&0xFF].i_packets++;
+ ip_rt_acct[(idx>>16)&0xFF].i_bytes+=skb->len;
+ }
+#endif
+
+ if (iph->ihl > 5) {
+ struct ip_options *opt;
+
+ /* It looks as overkill, because not all
+ IP options require packet mangling.
+ But it is the easiest for now, especially taking
+ into account that combination of IP options
+ and running sniffer is extremely rare condition.
+ --ANK (980813)
+ */
+
+ skb = skb_cow(skb, skb_headroom(skb));
+ if (skb == NULL)
+ return 0;
+ iph = skb->nh.iph;
+
+ skb->ip_summed = 0;
+ if (ip_options_compile(NULL, skb))
+ goto inhdr_error;
+
+ opt = &(IPCB(skb)->opt);
+ if (opt->srr) {
+ struct in_device *in_dev = dev->ip_ptr;
+ if (in_dev && !IN_DEV_SOURCE_ROUTE(in_dev)) {
+ if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())
+ printk(KERN_INFO "source route option %d.%d.%d.%d -> %d.%d.%d.%d\n",
+ NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+ goto drop;
+ }
+ if (ip_options_rcv_srr(skb))
+ goto drop;
+ }
+ }
+
+#ifdef CONFIG_FIREWALL
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ if (fwres == FW_REDIRECT && (IPCB(skb)->redirport = rport) != 0)
+ return ip_local_deliver(skb);
+#endif /* CONFIG_IP_TRANSPARENT_PROXY */
+
+ if (fwres == FW_REJECT) {
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+ goto drop;
+ }
+#endif /* CONFIG_FIREWALL */
+
+ return skb->dst->input(skb);
+
+inhdr_error:
+ ip_statistics.IpInHdrErrors++;
+drop:
+ kfree_skb(skb);
+ return(0);
+}
diff --git a/pfinet/linux-src/net/ipv4/ip_masq.c b/pfinet/linux-src/net/ipv4/ip_masq.c
new file mode 100644
index 00000000..8f00409f
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_masq.c
@@ -0,0 +1,2570 @@
+/*
+ *
+ * Masquerading functionality
+ *
+ * Copyright (c) 1994 Pauline Middelink
+ *
+ * $Id: ip_masq.c,v 1.34.2.2 1999/08/07 10:56:28 davem Exp $
+ *
+ *
+ * See ip_fw.c for original log
+ *
+ * Fixes:
+ * Joseph Gooch : Modified ip_fw_masquerade() to do a ip_route_output()
+ * (help by Dan Drown) : to choose the proper local address.
+ * (and Alexey) :
+ * Juan Jose Ciarlante : Modularized application masquerading (see ip_masq_app.c)
+ * Juan Jose Ciarlante : New struct ip_masq_seq that holds output/input delta seq.
+ * Juan Jose Ciarlante : Added hashed lookup by proto,maddr,mport and proto,saddr,sport
+ * Juan Jose Ciarlante : Fixed deadlock if free ports get exhausted
+ * Juan Jose Ciarlante : Added NO_ADDR status flag.
+ * Richard Lynch : Added IP Autoforward
+ * Nigel Metheringham : Added ICMP handling for demasquerade
+ * Nigel Metheringham : Checksum checking of masqueraded data
+ * Nigel Metheringham : Better handling of timeouts of TCP conns
+ * Delian Delchev : Added support for ICMP requests and replys
+ * Nigel Metheringham : ICMP in ICMP handling, tidy ups, bug fixes, made ICMP optional
+ * Juan Jose Ciarlante : re-assign maddr if no packet received from outside
+ * Juan Jose Ciarlante : ported to 2.1 tree
+ * Juan Jose Ciarlante : reworked control connections
+ * Steven Clarke : Added Port Forwarding
+ * Juan Jose Ciarlante : Just ONE ip_masq_new (!)
+ * Juan Jose Ciarlante : IP masq modules support
+ * Juan Jose Ciarlante : don't go into search loop if mport specified
+ * Juan Jose Ciarlante : locking
+ * Steven Clarke : IP_MASQ_S_xx state design
+ * Juan Jose Ciarlante : IP_MASQ_S state implementation
+ * Juan Jose Ciarlante : xx_get() clears timer, _put() inserts it
+ * Juan Jose Ciarlante : create /proc/net/ip_masq/
+ * Juan Jose Ciarlante : reworked checksums (save payload csum if possible)
+ * Juan Jose Ciarlante : added missing ip_fw_masquerade checksum
+ * Juan Jose Ciarlante : csum savings
+ * Juan Jose Ciarlante : added user-space tunnel creation/del, etc
+ * Juan Jose Ciarlante : (last) moved to ip_masq_user runtime module
+ * Juan Jose Ciarlante : user timeout handling again
+ * Juan Jose Ciarlante : make new modules support optional
+ * Juan Jose Ciarlante : u-space context => locks reworked
+ * Juan Jose Ciarlante : fixed stupid SMP locking bug
+ * Juan Jose Ciarlante : fixed "tap"ing in demasq path by copy-on-w
+ * Juan Jose Ciarlante : make masq_proto_doff() robust against fake sized/corrupted packets
+ * Kai Bankett : do not toss other IP protos in proto_doff()
+ * Dan Kegel : pointed correct NAT behavior for UDP streams
+ * Julian Anastasov : use daddr and dport as hash keys
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <asm/system.h>
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/inet.h>
+#include <linux/init.h>
+#include <net/protocol.h>
+#include <net/icmp.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <net/checksum.h>
+#include <net/ip_masq.h>
+
+#ifdef CONFIG_IP_MASQUERADE_MOD
+#include <net/ip_masq_mod.h>
+#endif
+
+#include <linux/sysctl.h>
+#include <linux/ip_fw.h>
+#include <linux/ip_masq.h>
+
+int sysctl_ip_masq_debug = 0;
+
+/*
+ * Exported wrapper
+ */
+int ip_masq_get_debug_level(void)
+{
+ return sysctl_ip_masq_debug;
+}
+
+struct ip_masq_hook *ip_masq_user_hook = NULL;
+
+/*
+ * Timeout table[state]
+ */
+/* static int masq_timeout_table[IP_MASQ_S_LAST+1] = { */
+static struct ip_masq_timeout_table masq_timeout_table = {
+ ATOMIC_INIT(0), /* refcnt */
+ 0, /* scale */
+ {
+ 30*60*HZ, /* IP_MASQ_S_NONE, */
+ 15*60*HZ, /* IP_MASQ_S_ESTABLISHED, */
+ 2*60*HZ, /* IP_MASQ_S_SYN_SENT, */
+ 1*60*HZ, /* IP_MASQ_S_SYN_RECV, */
+ 2*60*HZ, /* IP_MASQ_S_FIN_WAIT, */
+ 2*60*HZ, /* IP_MASQ_S_TIME_WAIT, */
+ 10*HZ, /* IP_MASQ_S_CLOSE, */
+ 60*HZ, /* IP_MASQ_S_CLOSE_WAIT, */
+ 30*HZ, /* IP_MASQ_S_LAST_ACK, */
+ 2*60*HZ, /* IP_MASQ_S_LISTEN, */
+ 5*60*HZ, /* IP_MASQ_S_UDP, */
+ 1*60*HZ, /* IP_MASQ_S_ICMP, */
+ 2*HZ,/* IP_MASQ_S_LAST */
+ }, /* timeout */
+};
+
+#define MASQUERADE_EXPIRE_RETRY masq_timeout_table.timeout[IP_MASQ_S_TIME_WAIT]
+
+static const char * state_name_table[IP_MASQ_S_LAST+1] = {
+ "NONE", /* IP_MASQ_S_NONE, */
+ "ESTABLISHED", /* IP_MASQ_S_ESTABLISHED, */
+ "SYN_SENT", /* IP_MASQ_S_SYN_SENT, */
+ "SYN_RECV", /* IP_MASQ_S_SYN_RECV, */
+ "FIN_WAIT", /* IP_MASQ_S_FIN_WAIT, */
+ "TIME_WAIT", /* IP_MASQ_S_TIME_WAIT, */
+ "CLOSE", /* IP_MASQ_S_CLOSE, */
+ "CLOSE_WAIT", /* IP_MASQ_S_CLOSE_WAIT, */
+ "LAST_ACK", /* IP_MASQ_S_LAST_ACK, */
+ "LISTEN", /* IP_MASQ_S_LISTEN, */
+ "UDP", /* IP_MASQ_S_UDP, */
+ "ICMP", /* IP_MASQ_S_ICMP, */
+ "BUG!", /* IP_MASQ_S_LAST */
+};
+
+#define mNO IP_MASQ_S_NONE
+#define mES IP_MASQ_S_ESTABLISHED
+#define mSS IP_MASQ_S_SYN_SENT
+#define mSR IP_MASQ_S_SYN_RECV
+#define mFW IP_MASQ_S_FIN_WAIT
+#define mTW IP_MASQ_S_TIME_WAIT
+#define mCL IP_MASQ_S_CLOSE
+#define mCW IP_MASQ_S_CLOSE_WAIT
+#define mLA IP_MASQ_S_LAST_ACK
+#define mLI IP_MASQ_S_LISTEN
+
+struct masq_tcp_states_t {
+ int next_state[IP_MASQ_S_LAST]; /* should be _LAST_TCP */
+};
+
+const char * ip_masq_state_name(int state)
+{
+ if (state >= IP_MASQ_S_LAST)
+ return "ERR!";
+ return state_name_table[state];
+}
+
+struct masq_tcp_states_t masq_tcp_states [] = {
+/* INPUT */
+/* mNO, mES, mSS, mSR, mFW, mTW, mCL, mCW, mLA, mLI */
+/*syn*/ {{mSR, mES, mES, mSR, mSR, mSR, mSR, mSR, mSR, mSR }},
+/*fin*/ {{mCL, mCW, mSS, mTW, mTW, mTW, mCL, mCW, mLA, mLI }},
+/*ack*/ {{mCL, mES, mSS, mSR, mFW, mTW, mCL, mCW, mCL, mLI }},
+/*rst*/ {{mCL, mCL, mCL, mSR, mCL, mCL, mCL, mCL, mLA, mLI }},
+
+/* OUTPUT */
+/* mNO, mES, mSS, mSR, mFW, mTW, mCL, mCW, mLA, mLI */
+/*syn*/ {{mSS, mES, mSS, mES, mSS, mSS, mSS, mSS, mSS, mLI }},
+/*fin*/ {{mTW, mFW, mSS, mTW, mFW, mTW, mCL, mTW, mLA, mLI }},
+/*ack*/ {{mES, mES, mSS, mSR, mFW, mTW, mCL, mCW, mLA, mES }},
+/*rst*/ {{mCL, mCL, mSS, mCL, mCL, mTW, mCL, mCL, mCL, mCL }},
+};
+
+static __inline__ int masq_tcp_state_idx(struct tcphdr *th, int output)
+{
+ /*
+ * [0-3]: input states, [4-7]: output.
+ */
+ if (output)
+ output=4;
+
+ if (th->rst)
+ return output+3;
+ if (th->syn)
+ return output+0;
+ if (th->fin)
+ return output+1;
+ if (th->ack)
+ return output+2;
+ return -1;
+}
+
+
+
+static int masq_set_state_timeout(struct ip_masq *ms, int state)
+{
+ struct ip_masq_timeout_table *mstim = ms->timeout_table;
+ int scale;
+
+ /*
+ * Use default timeout table if no specific for this entry
+ */
+ if (!mstim)
+ mstim = &masq_timeout_table;
+
+ ms->timeout = mstim->timeout[ms->state=state];
+ scale = mstim->scale;
+
+ if (scale<0)
+ ms->timeout >>= -scale;
+ else if (scale > 0)
+ ms->timeout <<= scale;
+
+ return state;
+}
+
+static int masq_tcp_state(struct ip_masq *ms, int output, struct tcphdr *th)
+{
+ int state_idx;
+ int new_state = IP_MASQ_S_CLOSE;
+
+ if ((state_idx = masq_tcp_state_idx(th, output)) < 0) {
+ IP_MASQ_DEBUG(1, "masq_state_idx(%d)=%d!!!\n",
+ output, state_idx);
+ goto tcp_state_out;
+ }
+
+ new_state = masq_tcp_states[state_idx].next_state[ms->state];
+
+tcp_state_out:
+ if (new_state!=ms->state)
+ IP_MASQ_DEBUG(1, "%s %s [%c%c%c%c] %08lX:%04X-%08lX:%04X state: %s->%s\n",
+ masq_proto_name(ms->protocol),
+ output? "output" : "input ",
+ th->syn? 'S' : '.',
+ th->fin? 'F' : '.',
+ th->ack? 'A' : '.',
+ th->rst? 'R' : '.',
+ ntohl(ms->saddr), ntohs(ms->sport),
+ ntohl(ms->daddr), ntohs(ms->dport),
+ ip_masq_state_name(ms->state),
+ ip_masq_state_name(new_state));
+ return masq_set_state_timeout(ms, new_state);
+}
+
+
+/*
+ * Handle state transitions
+ */
+static int masq_set_state(struct ip_masq *ms, int output, struct iphdr *iph, void *tp)
+{
+ switch (iph->protocol) {
+ case IPPROTO_ICMP:
+ return masq_set_state_timeout(ms, IP_MASQ_S_ICMP);
+ case IPPROTO_UDP:
+ return masq_set_state_timeout(ms, IP_MASQ_S_UDP);
+ case IPPROTO_TCP:
+ return masq_tcp_state(ms, output, tp);
+ }
+ return -1;
+}
+
+/*
+ * Set LISTEN timeout. (ip_masq_put will setup timer)
+ */
+int ip_masq_listen(struct ip_masq *ms)
+{
+ masq_set_state_timeout(ms, IP_MASQ_S_LISTEN);
+ return ms->timeout;
+}
+
+/*
+ * Dynamic address rewriting
+ */
+extern int sysctl_ip_dynaddr;
+
+/*
+ * Lookup lock
+ */
+rwlock_t __ip_masq_lock = RW_LOCK_UNLOCKED;
+
+/*
+ * Implement IP packet masquerading
+ */
+
+/*
+ * Converts an ICMP reply code into the equivalent request code
+ */
+static __inline__ const __u8 icmp_type_request(__u8 type)
+{
+ switch (type)
+ {
+ case ICMP_ECHOREPLY: return ICMP_ECHO; break;
+ case ICMP_TIMESTAMPREPLY: return ICMP_TIMESTAMP; break;
+ case ICMP_INFO_REPLY: return ICMP_INFO_REQUEST; break;
+ case ICMP_ADDRESSREPLY: return ICMP_ADDRESS; break;
+ default: return (255); break;
+ }
+}
+
+/*
+ * Helper macros - attempt to make code clearer!
+ */
+
+/* ID used in ICMP lookups */
+#define icmp_id(icmph) ((icmph->un).echo.id)
+/* (port) hash value using in ICMP lookups for requests */
+#define icmp_hv_req(icmph) ((__u16)(icmph->code+(__u16)(icmph->type<<8)))
+/* (port) hash value using in ICMP lookups for replies */
+#define icmp_hv_rep(icmph) ((__u16)(icmph->code+(__u16)(icmp_type_request(icmph->type)<<8)))
+
+/*
+ * Last masq_port number in use.
+ * Will cycle in MASQ_PORT boundaries.
+ */
+static __u16 masq_port = PORT_MASQ_BEGIN;
+static spinlock_t masq_port_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * free ports counters (UDP & TCP)
+ *
+ * Their value is _less_ or _equal_ to actual free ports:
+ * same masq port, diff masq addr (firewall iface address) allocated
+ * entries are accounted but their actually don't eat a more than 1 port.
+ *
+ * Greater values could lower MASQ_EXPIRATION setting as a way to
+ * manage 'masq_entries resource'.
+ *
+ * By default we will reuse masq.port iff (output) connection
+ * (5-upla) if not duplicated.
+ * This may break midentd and others ...
+ */
+
+#ifdef CONFIG_IP_MASQ_NREUSE
+#define PORT_MASQ_MUL 1
+#else
+#define PORT_MASQ_MUL 10
+#endif
+
+/*
+ * At the moment, hardcore in sync with masq_proto_num
+ */
+atomic_t ip_masq_free_ports[3] = {
+ ATOMIC_INIT((PORT_MASQ_END-PORT_MASQ_BEGIN) * PORT_MASQ_MUL),/* UDP */
+ ATOMIC_INIT((PORT_MASQ_END-PORT_MASQ_BEGIN) * PORT_MASQ_MUL),/* TCP */
+ ATOMIC_INIT((PORT_MASQ_END-PORT_MASQ_BEGIN) * PORT_MASQ_MUL),/* ICMP */
+};
+
+/*
+ * Counts entries that have been requested with specific mport.
+ * Used for incoming packets to "relax" input rule (port in MASQ range).
+ */
+atomic_t mport_count = ATOMIC_INIT(0);
+
+EXPORT_SYMBOL(ip_masq_get_debug_level);
+EXPORT_SYMBOL(ip_masq_new);
+EXPORT_SYMBOL(ip_masq_listen);
+EXPORT_SYMBOL(ip_masq_free_ports);
+EXPORT_SYMBOL(ip_masq_out_get);
+EXPORT_SYMBOL(ip_masq_in_get);
+EXPORT_SYMBOL(ip_masq_put);
+EXPORT_SYMBOL(ip_masq_control_add);
+EXPORT_SYMBOL(ip_masq_control_del);
+EXPORT_SYMBOL(ip_masq_control_get);
+EXPORT_SYMBOL(ip_masq_user_hook);
+EXPORT_SYMBOL(ip_masq_state_name);
+EXPORT_SYMBOL(ip_masq_select_addr);
+EXPORT_SYMBOL(__ip_masq_lock);
+EXPORT_SYMBOL(ip_masq_m_table);
+EXPORT_SYMBOL(ip_masq_s_table);
+EXPORT_SYMBOL(ip_masq_d_table);
+
+/*
+ * 3 ip_masq hash double linked tables:
+ * 2 for input m{addr,port} and output s{addr,port} pkts lookups.
+ * 1 for extra modules support (daddr)
+ */
+
+#define IP_MASQ_NTABLES 3
+
+struct list_head ip_masq_m_table[IP_MASQ_TAB_SIZE];
+struct list_head ip_masq_s_table[IP_MASQ_TAB_SIZE];
+struct list_head ip_masq_d_table[IP_MASQ_TAB_SIZE];
+
+/*
+ * timeouts
+ */
+
+#if 000 /* FIXED timeout handling */
+static struct ip_fw_masq ip_masq_dummy = {
+ MASQUERADE_EXPIRE_TCP,
+ MASQUERADE_EXPIRE_TCP_FIN,
+ MASQUERADE_EXPIRE_UDP
+};
+
+EXPORT_SYMBOL(ip_masq_expire);
+struct ip_fw_masq *ip_masq_expire = &ip_masq_dummy;
+#endif
+
+/*
+ * These flags enable non-strict d{addr,port} checks
+ * Given that both (in/out) lookup tables are hashed
+ * by m{addr,port} and s{addr,port} this is quite easy
+ */
+
+#define MASQ_DADDR_PASS (IP_MASQ_F_NO_DADDR|IP_MASQ_F_DLOOSE)
+#define MASQ_DPORT_PASS (IP_MASQ_F_NO_DPORT|IP_MASQ_F_DLOOSE)
+
+/*
+ * By default enable dest loose semantics
+ */
+#define CONFIG_IP_MASQ_LOOSE_DEFAULT 1
+
+
+/*
+ * Set masq expiration (deletion) and adds timer,
+ * if timeout==0 cancel expiration.
+ * Warning: it does not check/delete previous timer!
+ */
+
+static void __ip_masq_set_expire(struct ip_masq *ms, unsigned long tout)
+{
+ if (tout) {
+ ms->timer.expires = jiffies+tout;
+ add_timer(&ms->timer);
+ } else {
+ del_timer(&ms->timer);
+ }
+}
+
+
+/*
+ * Returns hash value
+ */
+
+static __inline__ unsigned
+ip_masq_hash_key(unsigned proto, __u32 addr, __u16 port)
+{
+ return (proto^ntohl(addr)^ntohs(port)) & (IP_MASQ_TAB_SIZE-1);
+}
+
+/*
+ * Hashes ip_masq by its proto,addrs,ports.
+ * should be called with locked tables.
+ * returns bool success.
+ */
+
+static int ip_masq_hash(struct ip_masq *ms)
+{
+ unsigned hash;
+
+ if (ms->flags & IP_MASQ_F_HASHED) {
+ IP_MASQ_ERR( "ip_masq_hash(): request for already hashed, called from %p\n",
+ __builtin_return_address(0));
+ return 0;
+ }
+ atomic_add(IP_MASQ_NTABLES, &ms->refcnt);
+
+ if ((ms->flags & (MASQ_DADDR_PASS | MASQ_DPORT_PASS |
+ IP_MASQ_F_SIMPLE_HASH)) == 0)
+ /*
+ * Hash by proto,m{addr,port},d{addr,port}
+ */
+ hash = ip_masq_hash_key(ms->protocol,
+ ms->maddr^ms->daddr, ms->mport^ms->dport);
+ else
+ /*
+ * Hash by proto,m{addr,port}
+ */
+ hash = ip_masq_hash_key(ms->protocol, ms->maddr, ms->mport);
+
+ list_add(&ms->m_list, &ip_masq_m_table[hash]);
+
+ if ((ms->flags & (MASQ_DADDR_PASS | MASQ_DPORT_PASS |
+ IP_MASQ_F_NO_SADDR | IP_MASQ_F_NO_SPORT |
+ IP_MASQ_F_SIMPLE_HASH)) == 0)
+ /*
+ * Hash by proto,s{addr,port},d{addr,port}
+ */
+ hash = ip_masq_hash_key(ms->protocol,
+ ms->saddr^ms->daddr, ms->sport^ms->dport);
+ else
+ /*
+ * Hash by proto,s{addr,port}
+ */
+ hash = ip_masq_hash_key(ms->protocol, ms->saddr, ms->sport);
+
+ list_add(&ms->s_list, &ip_masq_s_table[hash]);
+
+ /*
+ * Hash by proto,d{addr,port}
+ */
+ hash = ip_masq_hash_key(ms->protocol, ms->daddr, ms->dport);
+ list_add(&ms->d_list, &ip_masq_d_table[hash]);
+
+
+ ms->flags |= IP_MASQ_F_HASHED;
+ return 1;
+}
+
+/*
+ * UNhashes ip_masq from ip_masq_[ms]_tables.
+ * should be called with locked tables.
+ * returns bool success.
+ */
+
+static int ip_masq_unhash(struct ip_masq *ms)
+{
+ if (!(ms->flags & IP_MASQ_F_HASHED)) {
+ IP_MASQ_ERR( "ip_masq_unhash(): request for unhash flagged, called from %p\n",
+ __builtin_return_address(0));
+ return 0;
+ }
+ list_del(&ms->m_list);
+ list_del(&ms->s_list);
+ list_del(&ms->d_list);
+
+ atomic_sub(IP_MASQ_NTABLES, &ms->refcnt);
+
+ ms->flags &= ~IP_MASQ_F_HASHED;
+ return 1;
+}
+
+/*
+ * Returns ip_masq associated with supplied parameters, either
+ * broken out of the ip/tcp headers or directly supplied for those
+ * pathological protocols with address/port in the data stream
+ * (ftp, irc). addresses and ports are in network order.
+ * called for pkts coming from OUTside-to-INside the firewall.
+ *
+ * s_addr, s_port: pkt source address (foreign host)
+ * d_addr, d_port: pkt dest address (firewall)
+ *
+ * NB. Cannot check destination address, just for the incoming port.
+ * reason: archie.doc.ac.uk has 6 interfaces, you send to
+ * phoenix and get a reply from any other interface(==dst)!
+ *
+ * [Only for UDP] - AC
+ *
+ * Caller must lock tables
+ */
+
+static struct ip_masq * __ip_masq_in_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port)
+{
+ unsigned hash;
+ struct ip_masq *ms = NULL;
+ struct list_head *l,*e;
+
+ hash = ip_masq_hash_key(protocol, d_addr^s_addr, d_port^s_port);
+
+ l = &ip_masq_m_table[hash];
+ for (e=l->next; e!=l; e=e->next) {
+ ms = list_entry(e, struct ip_masq, m_list);
+ if (s_port==ms->dport && s_addr==ms->daddr &&
+ d_port==ms->mport && protocol==ms->protocol &&
+ d_addr==ms->maddr &&
+ ((ms->flags & (MASQ_DADDR_PASS | MASQ_DPORT_PASS)) == 0)
+ ) {
+ IP_MASQ_DEBUG(2, "look/in %d %08X:%04hX->%08X:%04hX OK\n",
+ protocol,
+ s_addr,
+ s_port,
+ d_addr,
+ d_port);
+ atomic_inc(&ms->refcnt);
+ goto out;
+ }
+ }
+
+ hash = ip_masq_hash_key(protocol, d_addr, d_port);
+
+ l = &ip_masq_m_table[hash];
+ for (e=l->next; e!=l; e=e->next) {
+ ms = list_entry(e, struct ip_masq, m_list);
+ if (protocol==ms->protocol &&
+ (d_addr==ms->maddr && d_port==ms->mport) &&
+ (s_addr==ms->daddr || ms->flags & MASQ_DADDR_PASS) &&
+ (s_port==ms->dport || ms->flags & MASQ_DPORT_PASS)
+ ) {
+ IP_MASQ_DEBUG(2, "look/in %d %08X:%04hX->%08X:%04hX OK\n",
+ protocol,
+ s_addr,
+ s_port,
+ d_addr,
+ d_port);
+ atomic_inc(&ms->refcnt);
+ goto out;
+ }
+ }
+ IP_MASQ_DEBUG(2, "look/in %d %08X:%04hX->%08X:%04hX fail\n",
+ protocol,
+ s_addr,
+ s_port,
+ d_addr,
+ d_port);
+
+ ms = NULL;
+out:
+ return ms;
+}
+
+/*
+ * Returns ip_masq associated with supplied parameters, either
+ * broken out of the ip/tcp headers or directly supplied for those
+ * pathological protocols with address/port in the data stream
+ * (ftp, irc). addresses and ports are in network order.
+ * called for pkts coming from inside-to-OUTside the firewall.
+ *
+ * Normally we know the source address and port but for some protocols
+ * (e.g. ftp PASV) we do not know the source port initially. Alas the
+ * hash is keyed on source port so if the first lookup fails then try again
+ * with a zero port, this time only looking at entries marked "no source
+ * port".
+ *
+ * Caller must lock tables
+ */
+
+static struct ip_masq * __ip_masq_out_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port)
+{
+ unsigned hash;
+ struct ip_masq *ms = NULL;
+ struct list_head *l,*e;
+
+ /*
+ * Check for "full" addressed entries
+ */
+ hash = ip_masq_hash_key(protocol, s_addr^d_addr, s_port^d_port);
+
+ l = &ip_masq_s_table[hash];
+ for (e=l->next; e!=l; e=e->next) {
+ ms = list_entry(e, struct ip_masq, s_list);
+ if (d_addr==ms->daddr && d_port==ms->dport &&
+ s_addr==ms->saddr && s_port==ms->sport &&
+ protocol==ms->protocol &&
+ ((ms->flags & (MASQ_DADDR_PASS | MASQ_DPORT_PASS |
+ IP_MASQ_F_NO_SADDR | IP_MASQ_F_NO_SPORT)) == 0)
+ ) {
+ IP_MASQ_DEBUG(2, "lk/out0 %d %08X:%04hX->%08X:%04hX OK\n",
+ protocol,
+ s_addr,
+ s_port,
+ d_addr,
+ d_port);
+
+ atomic_inc(&ms->refcnt);
+ goto out;
+ }
+
+ }
+
+ hash = ip_masq_hash_key(protocol, s_addr, s_port);
+
+ l = &ip_masq_s_table[hash];
+ for (e=l->next; e!=l; e=e->next) {
+ ms = list_entry(e, struct ip_masq, s_list);
+ if (protocol == ms->protocol &&
+ s_addr == ms->saddr && s_port == ms->sport &&
+ (d_addr==ms->daddr || ms->flags & MASQ_DADDR_PASS) &&
+ (d_port==ms->dport || ms->flags & MASQ_DPORT_PASS)
+ ) {
+ IP_MASQ_DEBUG(2, "lk/out1 %d %08X:%04hX->%08X:%04hX OK\n",
+ protocol,
+ s_addr,
+ s_port,
+ d_addr,
+ d_port);
+
+ atomic_inc(&ms->refcnt);
+ goto out;
+ }
+
+ }
+
+ /*
+ * Check for NO_SPORT entries
+ */
+ hash = ip_masq_hash_key(protocol, s_addr, 0);
+ l = &ip_masq_s_table[hash];
+ for (e=l->next; e!=l; e=e->next) {
+ ms = list_entry(e, struct ip_masq, s_list);
+ if (ms->flags & IP_MASQ_F_NO_SPORT &&
+ protocol == ms->protocol &&
+ s_addr == ms->saddr &&
+ (d_addr==ms->daddr || ms->flags & MASQ_DADDR_PASS) &&
+ (d_port==ms->dport || ms->flags & MASQ_DPORT_PASS)
+ ) {
+ IP_MASQ_DEBUG(2, "lk/out2 %d %08X:%04hX->%08X:%04hX OK\n",
+ protocol,
+ s_addr,
+ s_port,
+ d_addr,
+ d_port);
+
+ atomic_inc(&ms->refcnt);
+ goto out;
+ }
+ }
+ IP_MASQ_DEBUG(2, "lk/out1 %d %08X:%04hX->%08X:%04hX fail\n",
+ protocol,
+ s_addr,
+ s_port,
+ d_addr,
+ d_port);
+
+ ms = NULL;
+out:
+ return ms;
+}
+
+#ifdef CONFIG_IP_MASQ_NREUSE
+/*
+ * Returns ip_masq for given proto,m_addr,m_port.
+ * called by allocation routine to find an unused m_port.
+ *
+ * Caller must lock tables
+ */
+
+static struct ip_masq * __ip_masq_getbym(int protocol, __u32 m_addr, __u16 m_port)
+{
+ unsigned hash;
+ struct ip_masq *ms = NULL;
+
+ hash = ip_masq_hash_key(protocol, m_addr, m_port);
+
+ for(ms = ip_masq_m_tab[hash]; ms ; ms = ms->m_link) {
+ if ( protocol==ms->protocol &&
+ (m_addr==ms->maddr && m_port==ms->mport)) {
+ atomic_inc(&ms->refcnt);
+ goto out;
+ }
+ }
+
+out:
+ return ms;
+}
+#endif
+
+struct ip_masq * ip_masq_out_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port)
+{
+ struct ip_masq *ms;
+
+ read_lock(&__ip_masq_lock);
+ ms = __ip_masq_out_get(protocol, s_addr, s_port, d_addr, d_port);
+ read_unlock(&__ip_masq_lock);
+
+ if (ms)
+ __ip_masq_set_expire(ms, 0);
+ return ms;
+}
+
+struct ip_masq * ip_masq_in_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port)
+{
+ struct ip_masq *ms;
+
+ read_lock(&__ip_masq_lock);
+ ms = __ip_masq_in_get(protocol, s_addr, s_port, d_addr, d_port);
+ read_unlock(&__ip_masq_lock);
+
+ if (ms)
+ __ip_masq_set_expire(ms, 0);
+ return ms;
+}
+
+static __inline__ void __ip_masq_put(struct ip_masq *ms)
+{
+ atomic_dec(&ms->refcnt);
+}
+
+void ip_masq_put(struct ip_masq *ms)
+{
+ /*
+ * Decrement refcnt
+ */
+ __ip_masq_put(ms);
+
+ /*
+ * if refcnt==IP_MASQ_NTABLES
+ */
+ if (atomic_read(&ms->refcnt)==IP_MASQ_NTABLES) {
+ __ip_masq_set_expire(ms, ms->timeout);
+ } else {
+ IP_MASQ_DEBUG(0, "did not set timer with refcnt=%d, called from %p\n",
+ atomic_read(&ms->refcnt),
+ __builtin_return_address(0));
+ }
+}
+
+extern int sysctl_ip_always_defrag;
+
+static void masq_expire(unsigned long data)
+{
+ struct ip_masq *ms = (struct ip_masq *)data;
+ ms->timeout = MASQUERADE_EXPIRE_RETRY;
+
+ /*
+ * hey, I'm using it
+ */
+ atomic_inc(&ms->refcnt);
+
+ IP_MASQ_DEBUG(1, "Masqueraded %s %08lX:%04X expired\n",
+ masq_proto_name(ms->protocol),
+ ntohl(ms->saddr),ntohs(ms->sport));
+
+ write_lock(&__ip_masq_lock);
+
+#if 0000
+ /*
+ * Already locked, do bounce ...
+ */
+ if (ip_masq_nlocks(&__ip_masq_lock) != 1) {
+ goto masq_expire_later;
+ }
+
+#endif
+ /*
+ * do I control anybody?
+ */
+ if (atomic_read(&ms->n_control))
+ goto masq_expire_later;
+
+ /*
+ * does anybody controls me?
+ */
+
+ if (ms->control)
+ ip_masq_control_del(ms);
+
+ if (ip_masq_unhash(ms)) {
+ if (ms->flags&IP_MASQ_F_MPORT) {
+ atomic_dec(&mport_count);
+ } else {
+ atomic_inc(ip_masq_free_ports + masq_proto_num(ms->protocol));
+ }
+ ip_masq_unbind_app(ms);
+ }
+
+ /*
+ * refcnt==1 implies I'm the only one referrer
+ */
+ if (atomic_read(&ms->refcnt) == 1) {
+ kfree_s(ms,sizeof(*ms));
+ sysctl_ip_always_defrag--;
+ MOD_DEC_USE_COUNT;
+ goto masq_expire_out;
+ }
+
+masq_expire_later:
+ IP_MASQ_DEBUG(0, "masq_expire delayed: %s %08lX:%04X->%08lX:%04X masq.refcnt-1=%d masq.n_control=%d\n",
+ masq_proto_name(ms->protocol),
+ ntohl(ms->saddr), ntohs(ms->sport),
+ ntohl(ms->daddr), ntohs(ms->dport),
+ atomic_read(&ms->refcnt)-1,
+ atomic_read(&ms->n_control));
+
+ ip_masq_put(ms);
+
+masq_expire_out:
+ write_unlock(&__ip_masq_lock);
+}
+
+static __u16 get_next_mport(void)
+{
+ __u16 mport;
+
+ spin_lock_irq(&masq_port_lock);
+ /*
+ * Try the next available port number
+ */
+ mport = htons(masq_port++);
+ if (masq_port==PORT_MASQ_END) masq_port = PORT_MASQ_BEGIN;
+
+ spin_unlock_irq(&masq_port_lock);
+ return mport;
+}
+
+/*
+ * Create a new masquerade list entry, also allocate an
+ * unused mport, keeping the portnumber between the
+ * given boundaries MASQ_BEGIN and MASQ_END.
+ *
+ * Be careful, it can be called from u-space
+ */
+
+struct ip_masq * ip_masq_new(int proto, __u32 maddr, __u16 mport, __u32 saddr, __u16 sport, __u32 daddr, __u16 dport, unsigned mflags)
+{
+ struct ip_masq *ms, *mst;
+ int ports_tried;
+ atomic_t *free_ports_p = NULL;
+ static int n_fails = 0;
+ int prio;
+
+
+ if (masq_proto_num(proto)!=-1 && mport == 0) {
+ free_ports_p = ip_masq_free_ports + masq_proto_num(proto);
+
+ if (atomic_read(free_ports_p) == 0) {
+ if (++n_fails < 5)
+ IP_MASQ_ERR( "ip_masq_new(proto=%s): no free ports.\n",
+ masq_proto_name(proto));
+ return NULL;
+ }
+ }
+
+ prio = (mflags&IP_MASQ_F_USER) ? GFP_KERNEL : GFP_ATOMIC;
+
+ ms = (struct ip_masq *) kmalloc(sizeof(struct ip_masq), prio);
+ if (ms == NULL) {
+ if (++n_fails < 5)
+ IP_MASQ_ERR("ip_masq_new(proto=%s): no memory available.\n",
+ masq_proto_name(proto));
+ return NULL;
+ }
+ MOD_INC_USE_COUNT;
+ sysctl_ip_always_defrag++;
+ memset(ms, 0, sizeof(*ms));
+ INIT_LIST_HEAD(&ms->s_list);
+ INIT_LIST_HEAD(&ms->m_list);
+ INIT_LIST_HEAD(&ms->d_list);
+ init_timer(&ms->timer);
+ ms->timer.data = (unsigned long)ms;
+ ms->timer.function = masq_expire;
+ ms->protocol = proto;
+ ms->saddr = saddr;
+ ms->sport = sport;
+ ms->daddr = daddr;
+ ms->dport = dport;
+ ms->flags = mflags;
+ ms->app_data = NULL;
+ ms->control = NULL;
+
+ atomic_set(&ms->n_control,0);
+ atomic_set(&ms->refcnt,0);
+
+ if (proto == IPPROTO_UDP && !mport)
+#ifdef CONFIG_IP_MASQ_LOOSE_DEFAULT
+ /*
+ * Flag this tunnel as "dest loose"
+ *
+ */
+ ms->flags |= IP_MASQ_F_DLOOSE;
+#else
+ ms->flags |= IP_MASQ_F_NO_DADDR;
+#endif
+
+
+ /* get masq address from rif */
+ ms->maddr = maddr;
+
+ /*
+ * This flag will allow masq. addr (ms->maddr)
+ * to follow forwarding interface address.
+ */
+ ms->flags |= IP_MASQ_F_NO_REPLY;
+
+ /*
+ * We want a specific mport. Be careful.
+ */
+ if (masq_proto_num(proto) == -1 || mport) {
+ ms->mport = mport;
+
+ /*
+ * Check 5-upla uniqueness
+ */
+ if (mflags & IP_MASQ_F_USER)
+ write_lock_bh(&__ip_masq_lock);
+ else
+ write_lock(&__ip_masq_lock);
+
+ mst = __ip_masq_in_get(proto, daddr, dport, maddr, mport);
+ if (mst==NULL) {
+ ms->flags |= IP_MASQ_F_MPORT;
+
+ atomic_inc(&mport_count);
+ ip_masq_hash(ms);
+
+ if (mflags & IP_MASQ_F_USER)
+ write_unlock_bh(&__ip_masq_lock);
+ else
+ write_unlock(&__ip_masq_lock);
+
+ ip_masq_bind_app(ms);
+ atomic_inc(&ms->refcnt);
+ masq_set_state_timeout(ms, IP_MASQ_S_NONE);
+ return ms;
+ }
+ if (mflags & IP_MASQ_F_USER)
+ write_unlock_bh(&__ip_masq_lock);
+ else
+ write_unlock(&__ip_masq_lock);
+
+ __ip_masq_put(mst);
+
+ IP_MASQ_ERR( "Already used connection: %s, %d.%d.%d.%d:%d => %d.%d.%d.%d:%d, called from %p\n",
+ masq_proto_name(proto),
+ NIPQUAD(maddr), ntohs(mport),
+ NIPQUAD(daddr), ntohs(dport),
+ __builtin_return_address(0));
+
+
+ goto mport_nono;
+ }
+
+
+ for (ports_tried = 0;
+ (atomic_read(free_ports_p) && (ports_tried <= (PORT_MASQ_END - PORT_MASQ_BEGIN)));
+ ports_tried++){
+
+ mport = ms->mport = get_next_mport();
+ /*
+ * lookup to find out if this connection is used.
+ */
+
+ if (mflags & IP_MASQ_F_USER)
+ write_lock_bh(&__ip_masq_lock);
+ else
+ write_lock(&__ip_masq_lock);
+
+#ifdef CONFIG_IP_MASQ_NREUSE
+ mst = __ip_masq_getbym(proto, maddr, mport);
+#else
+ mst = __ip_masq_in_get(proto, daddr, dport, maddr, mport);
+#endif
+ if (mst == NULL) {
+
+ if (atomic_read(free_ports_p) == 0) {
+ if (mflags & IP_MASQ_F_USER)
+ write_unlock_bh(&__ip_masq_lock);
+ else
+ write_unlock(&__ip_masq_lock);
+
+ break;
+ }
+ atomic_dec(free_ports_p);
+ ip_masq_hash(ms);
+
+ if (mflags & IP_MASQ_F_USER)
+ write_unlock_bh(&__ip_masq_lock);
+ else
+ write_unlock(&__ip_masq_lock);
+
+ ip_masq_bind_app(ms);
+ n_fails = 0;
+ atomic_inc(&ms->refcnt);
+ masq_set_state_timeout(ms, IP_MASQ_S_NONE);
+ return ms;
+ }
+ if (mflags & IP_MASQ_F_USER)
+ write_unlock_bh(&__ip_masq_lock);
+ else
+ write_unlock(&__ip_masq_lock);
+
+ __ip_masq_put(mst);
+ }
+
+ if (++n_fails < 5)
+ IP_MASQ_ERR( "ip_masq_new(proto=%s): could not get free masq entry (free=%d).\n",
+ masq_proto_name(ms->protocol),
+ atomic_read(free_ports_p));
+mport_nono:
+ kfree_s(ms, sizeof(*ms));
+
+ sysctl_ip_always_defrag--;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+}
+
+/*
+ * Get transport protocol data offset, check against size
+ * return:
+ * 0 if other IP proto
+ * -1 if error
+ */
+static __inline__ int proto_doff(unsigned proto, char *th, unsigned size)
+{
+ int ret = -1;
+ switch (proto) {
+ case IPPROTO_ICMP:
+ if (size >= sizeof(struct icmphdr))
+ ret = sizeof(struct icmphdr);
+ break;
+ case IPPROTO_UDP:
+ if (size >= sizeof(struct udphdr))
+ ret = sizeof(struct udphdr);
+ break;
+ case IPPROTO_TCP:
+ /*
+ * Is this case, this check _also_ avoids
+ * touching an invalid pointer if
+ * size is invalid
+ */
+ if (size >= sizeof(struct tcphdr)) {
+ ret = ((struct tcphdr*)th)->doff << 2;
+ if (ret > size) {
+ ret = -1 ;
+ }
+ }
+
+ break;
+ default:
+ /* Other proto: nothing to say, by now :) */
+ ret = 0;
+ }
+ if (ret < 0)
+ IP_MASQ_DEBUG(0, "mess proto_doff for proto=%d, size =%d\n",
+ proto, size);
+ return ret;
+}
+
+int ip_fw_masquerade(struct sk_buff **skb_p, __u32 maddr)
+{
+ struct sk_buff *skb = *skb_p;
+ struct iphdr *iph = skb->nh.iph;
+ union ip_masq_tphdr h;
+ struct ip_masq *ms;
+ int size;
+
+ /*
+ * doff holds transport protocol data offset
+ * csum holds its checksum
+ * csum_ok says if csum is valid
+ */
+ int doff = 0;
+ int csum = 0;
+ int csum_ok = 0;
+
+ /*
+ * We can only masquerade protocols with ports... and hack some ICMPs
+ */
+
+ h.raw = (char*) iph + iph->ihl * 4;
+ size = ntohs(iph->tot_len) - (iph->ihl * 4);
+
+
+ doff = proto_doff(iph->protocol, h.raw, size);
+ if (doff <= 0) {
+ /*
+ * Output path: do not pass other IP protos nor
+ * invalid packets.
+ */
+ return -1;
+ }
+
+ /* Lets determine our maddr now, shall we? */
+ if (maddr == 0) {
+ struct rtable *rt;
+ struct rtable *skb_rt = (struct rtable*)skb->dst;
+ struct device *skb_dev = skb_rt->u.dst.dev;
+
+ if (ip_route_output(&rt, iph->daddr, 0, RT_TOS(iph->tos)|RTO_CONN, skb_dev?skb_dev->ifindex:0)) {
+ /* Fallback on old method */
+ /* This really shouldn't happen... */
+ maddr = inet_select_addr(skb_dev, skb_rt->rt_gateway, RT_SCOPE_UNIVERSE);
+ } else {
+ /* Route lookup succeeded */
+ maddr = rt->rt_src;
+ ip_rt_put(rt);
+ }
+ }
+
+ switch (iph->protocol) {
+ case IPPROTO_ICMP:
+ return(ip_fw_masq_icmp(skb_p, maddr));
+ case IPPROTO_UDP:
+ if (h.uh->check == 0)
+ /* No UDP checksum */
+ break;
+ case IPPROTO_TCP:
+ /* Make sure packet is in the masq range */
+ IP_MASQ_DEBUG(3, "O-pkt: %s size=%d\n",
+ masq_proto_name(iph->protocol),
+ size);
+
+#ifdef CONFIG_IP_MASQ_DEBUG
+ if (ip_masq_get_debug_level() > 3) {
+ skb->ip_summed = CHECKSUM_NONE;
+ }
+#endif
+ /* Check that the checksum is OK */
+ switch (skb->ip_summed)
+ {
+ case CHECKSUM_NONE:
+ {
+ csum = csum_partial(h.raw + doff, size - doff, 0);
+ IP_MASQ_DEBUG(3, "O-pkt: %s I-datacsum=%d\n",
+ masq_proto_name(iph->protocol),
+ csum);
+
+ skb->csum = csum_partial(h.raw , doff, csum);
+ }
+ case CHECKSUM_HW:
+ if (csum_tcpudp_magic(iph->saddr, iph->daddr,
+ size, iph->protocol, skb->csum))
+ {
+ IP_MASQ_DEBUG(0, "Outgoing failed %s checksum from %d.%d.%d.%d (size=%d)!\n",
+ masq_proto_name(iph->protocol),
+ NIPQUAD(iph->saddr),
+ size);
+ return -1;
+ }
+ default:
+ /* CHECKSUM_UNNECESSARY */
+ }
+ break;
+ default:
+ return -1;
+ }
+ /*
+ * Now hunt the list to see if we have an old entry
+ */
+
+ /* h.raw = (char*) iph + iph->ihl * 4; */
+
+ IP_MASQ_DEBUG(2, "Outgoing %s %08lX:%04X -> %08lX:%04X\n",
+ masq_proto_name(iph->protocol),
+ ntohl(iph->saddr), ntohs(h.portp[0]),
+ ntohl(iph->daddr), ntohs(h.portp[1]));
+
+ ms = ip_masq_out_get_iph(iph);
+ if (ms!=NULL) {
+
+ /*
+ * If sysctl !=0 and no pkt has been received yet
+ * in this tunnel and routing iface address has changed...
+ * "You are welcome, diald".
+ */
+ if ( sysctl_ip_dynaddr && ms->flags & IP_MASQ_F_NO_REPLY && maddr != ms->maddr) {
+
+ if (sysctl_ip_dynaddr > 1) {
+ IP_MASQ_INFO( "ip_fw_masquerade(): change masq.addr from %d.%d.%d.%d to %d.%d.%d.%d\n",
+ NIPQUAD(ms->maddr),NIPQUAD(maddr));
+ }
+
+ write_lock(&__ip_masq_lock);
+
+ ip_masq_unhash(ms);
+ ms->maddr = maddr;
+ ip_masq_hash(ms);
+
+ write_unlock(&__ip_masq_lock);
+ }
+
+ /*
+ * Set sport if not defined yet (e.g. ftp PASV). Because
+ * masq entries are hashed on sport, unhash with old value
+ * and hash with new.
+ */
+
+ if ( ms->flags & IP_MASQ_F_NO_SPORT && ms->protocol == IPPROTO_TCP ) {
+
+ write_lock(&__ip_masq_lock);
+
+ ip_masq_unhash(ms);
+ ms->flags &= ~IP_MASQ_F_NO_SPORT;
+ ms->sport = h.portp[0];
+ ip_masq_hash(ms); /* hash on new sport */
+
+ write_unlock(&__ip_masq_lock);
+
+ IP_MASQ_DEBUG(1, "ip_fw_masquerade(): filled sport=%d\n",
+ ntohs(ms->sport));
+ }
+ if (ms->flags & IP_MASQ_F_DLOOSE) {
+ /*
+ * update dest loose values
+ */
+ ms->dport = h.portp[1];
+ ms->daddr = iph->daddr;
+ }
+ } else {
+ /*
+ * Nope, not found, create a new entry for it
+ */
+
+#ifdef CONFIG_IP_MASQUERADE_MOD
+ if (!(ms = ip_masq_mod_out_create(skb, iph, maddr)))
+#endif
+ ms = ip_masq_new(iph->protocol,
+ maddr, 0,
+ iph->saddr, h.portp[0],
+ iph->daddr, h.portp[1],
+ 0);
+ if (ms == NULL)
+ return -1;
+ }
+
+ /*
+ * Call module's output update hook
+ */
+
+#ifdef CONFIG_IP_MASQUERADE_MOD
+ ip_masq_mod_out_update(skb, iph, ms);
+#endif
+
+ /*
+ * Change the fragments origin
+ */
+
+ size = skb->len - (h.raw - skb->nh.raw);
+
+ /*
+ * Set iph addr and port from ip_masq obj.
+ */
+ iph->saddr = ms->maddr;
+ h.portp[0] = ms->mport;
+
+ /*
+ * Invalidate csum saving if tunnel has masq helper
+ */
+
+ if (ms->app)
+ csum_ok = 0;
+
+ /*
+ * Attempt ip_masq_app call.
+ * will fix ip_masq and iph seq stuff
+ */
+ if (ip_masq_app_pkt_out(ms, skb_p, maddr) != 0)
+ {
+ /*
+ * skb has possibly changed, update pointers.
+ */
+ skb = *skb_p;
+ iph = skb->nh.iph;
+ h.raw = (char*) iph + iph->ihl *4;
+ size = skb->len - (h.raw - skb->nh.raw);
+ /* doff should have not changed */
+ }
+
+ /*
+ * Adjust packet accordingly to protocol
+ */
+
+ /*
+ * Transport's payload partial csum
+ */
+
+ if (!csum_ok) {
+ csum = csum_partial(h.raw + doff, size - doff, 0);
+ }
+ skb->csum = csum;
+
+ IP_MASQ_DEBUG(3, "O-pkt: %s size=%d O-datacsum=%d\n",
+ masq_proto_name(iph->protocol),
+ size,
+ csum);
+
+ /*
+ * Protocol csum
+ */
+ switch (iph->protocol) {
+ case IPPROTO_TCP:
+ h.th->check = 0;
+ h.th->check=csum_tcpudp_magic(iph->saddr, iph->daddr,
+ size, iph->protocol,
+ csum_partial(h.raw , doff, csum));
+ IP_MASQ_DEBUG(3, "O-pkt: %s O-csum=%d (+%d)\n",
+ masq_proto_name(iph->protocol),
+ h.th->check,
+ (char*) & (h.th->check) - (char*) h.raw);
+
+ break;
+ case IPPROTO_UDP:
+ h.uh->check = 0;
+ h.uh->check=csum_tcpudp_magic(iph->saddr, iph->daddr,
+ size, iph->protocol,
+ csum_partial(h.raw , doff, csum));
+ if (h.uh->check == 0)
+ h.uh->check = 0xFFFF;
+ IP_MASQ_DEBUG(3, "O-pkt: %s O-csum=%d (+%d)\n",
+ masq_proto_name(iph->protocol),
+ h.uh->check,
+ (char*) &(h.uh->check)- (char*) h.raw);
+ break;
+ }
+ ip_send_check(iph);
+
+ IP_MASQ_DEBUG(2, "O-routed from %08lX:%04X with masq.addr %08lX\n",
+ ntohl(ms->maddr),ntohs(ms->mport),ntohl(maddr));
+
+ masq_set_state(ms, 1, iph, h.portp);
+ ip_masq_put(ms);
+
+ return 0;
+ }
+
+/*
+ * Restore original addresses and ports in the original IP
+ * datagram if the failing packet has been [de]masqueraded.
+ * This is ugly in the extreme. We no longer have the original
+ * packet so we have to reconstruct it from the failing packet
+ * plus data in the masq tables. The resulting "original data"
+ * should be good enough to tell the sender which session to
+ * throttle. Relies on far too much knowledge of masq internals,
+ * there ought to be a better way - KAO 990303.
+ *
+ * Moved here from icmp.c - JJC.
+ * Already known: type == ICMP_DEST_UNREACH, IPSKB_MASQUERADED
+ * skb->nh.iph points to original header.
+ *
+ * Must try both OUT and IN tables; we could add a flag
+ * ala IPSKB_MASQUERADED to avoid 2nd tables lookup, but this is VERY
+ * unlike because routing makes mtu decision before reaching
+ * ip_fw_masquerade().
+ *
+ */
+int ip_fw_unmasq_icmp(struct sk_buff *skb) {
+ struct ip_masq *ms;
+ struct iphdr *iph = skb->nh.iph;
+ __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
+
+ /*
+ * Always called from _bh context: use read_[un]lock()
+ */
+
+ /*
+ * Peek "out" table, this packet has bounced:
+ * out->in(frag_needed!)->OUT[icmp]
+ *
+ * iph->daddr is IN host
+ * iph->saddr is OUT host
+ */
+ read_lock(&__ip_masq_lock);
+ ms = __ip_masq_out_get(iph->protocol,
+ iph->daddr, portp[1],
+ iph->saddr, portp[0]);
+ read_unlock(&__ip_masq_lock);
+ if (ms) {
+ IP_MASQ_DEBUG(1, "Incoming frag_need rewrited from %d.%d.%d.%d to %d.%d.%d.%d\n",
+ NIPQUAD(iph->daddr), NIPQUAD(ms->maddr));
+ iph->daddr = ms->maddr;
+ portp[1] = ms->mport;
+ __ip_masq_put(ms);
+ return 1;
+ }
+ /*
+ * Peek "in" table
+ * in->out(frag_needed!)->IN[icmp]
+ *
+ * iph->daddr is OUT host
+ * iph->saddr is MASQ host
+ *
+ */
+ read_lock(&__ip_masq_lock);
+ ms = __ip_masq_in_get(iph->protocol,
+ iph->daddr, portp[1],
+ iph->saddr, portp[0]);
+ read_unlock(&__ip_masq_lock);
+ if (ms) {
+ IP_MASQ_DEBUG(1, "Outgoing frag_need rewrited from %d.%d.%d.%d to %d.%d.%d.%d\n",
+ NIPQUAD(iph->saddr), NIPQUAD(ms->saddr));
+ iph->saddr = ms->saddr;
+ portp[0] = ms->sport;
+ __ip_masq_put(ms);
+ return 1;
+ }
+ return 0;
+
+}
+/*
+ * Handle ICMP messages in forward direction.
+ * Find any that might be relevant, check against existing connections,
+ * forward to masqueraded host if relevant.
+ * Currently handles error types - unreachable, quench, ttl exceeded
+ */
+
+int ip_fw_masq_icmp(struct sk_buff **skb_p, __u32 maddr)
+{
+ struct sk_buff *skb = *skb_p;
+ struct iphdr *iph = skb->nh.iph;
+ struct icmphdr *icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
+ struct iphdr *ciph; /* The ip header contained within the ICMP */
+ __u16 *pptr; /* port numbers from TCP/UDP contained header */
+ struct ip_masq *ms;
+ unsigned short len = ntohs(iph->tot_len) - (iph->ihl * 4);
+
+ IP_MASQ_DEBUG(2, "Incoming forward ICMP (%d,%d) %lX -> %lX\n",
+ icmph->type, ntohs(icmp_id(icmph)),
+ ntohl(iph->saddr), ntohl(iph->daddr));
+
+#ifdef CONFIG_IP_MASQUERADE_ICMP
+ if ((icmph->type == ICMP_ECHO ) ||
+ (icmph->type == ICMP_TIMESTAMP ) ||
+ (icmph->type == ICMP_INFO_REQUEST ) ||
+ (icmph->type == ICMP_ADDRESS )) {
+
+ IP_MASQ_DEBUG(2, "icmp request rcv %lX->%lX id %d type %d\n",
+ ntohl(iph->saddr),
+ ntohl(iph->daddr),
+ ntohs(icmp_id(icmph)),
+ icmph->type);
+
+ ms = ip_masq_out_get(iph->protocol,
+ iph->saddr,
+ icmp_id(icmph),
+ iph->daddr,
+ icmp_hv_req(icmph));
+ if (ms == NULL) {
+ ms = ip_masq_new(iph->protocol,
+ maddr, 0,
+ iph->saddr, icmp_id(icmph),
+ iph->daddr, icmp_hv_req(icmph),
+ 0);
+ if (ms == NULL)
+ return (-1);
+ IP_MASQ_DEBUG(1, "Created new icmp entry\n");
+ }
+ /* Rewrite source address */
+
+ /*
+ * If sysctl !=0 and no pkt has been received yet
+ * in this tunnel and routing iface address has changed...
+ * "You are welcome, diald".
+ */
+ if ( sysctl_ip_dynaddr && ms->flags & IP_MASQ_F_NO_REPLY && maddr != ms->maddr) {
+
+ if (sysctl_ip_dynaddr > 1) {
+ IP_MASQ_INFO( "ip_fw_masq_icmp(): change masq.addr %d.%d.%d.%d to %d.%d.%d.%d",
+ NIPQUAD(ms->maddr), NIPQUAD(maddr));
+ }
+
+ write_lock(&__ip_masq_lock);
+
+ ip_masq_unhash(ms);
+ ms->maddr = maddr;
+ ip_masq_hash(ms);
+
+ write_unlock(&__ip_masq_lock);
+ }
+
+ iph->saddr = ms->maddr;
+ ip_send_check(iph);
+ /* Rewrite port (id) */
+ (icmph->un).echo.id = ms->mport;
+ icmph->checksum = 0;
+ icmph->checksum = ip_compute_csum((unsigned char *)icmph, len);
+
+ IP_MASQ_DEBUG(2, "icmp request rwt %lX->%lX id %d type %d\n",
+ ntohl(iph->saddr),
+ ntohl(iph->daddr),
+ ntohs(icmp_id(icmph)),
+ icmph->type);
+
+ masq_set_state(ms, 1, iph, icmph);
+ ip_masq_put(ms);
+
+ return 1;
+ }
+#endif
+
+ /*
+ * Work through seeing if this is for us.
+ * These checks are supposed to be in an order that
+ * means easy things are checked first to speed up
+ * processing.... however this means that some
+ * packets will manage to get a long way down this
+ * stack and then be rejected, but thats life
+ */
+ if ((icmph->type != ICMP_DEST_UNREACH) &&
+ (icmph->type != ICMP_SOURCE_QUENCH) &&
+ (icmph->type != ICMP_TIME_EXCEEDED))
+ return 0;
+
+ /* Now find the contained IP header */
+ ciph = (struct iphdr *) (icmph + 1);
+
+#ifdef CONFIG_IP_MASQUERADE_ICMP
+ if (ciph->protocol == IPPROTO_ICMP) {
+ /*
+ * This section handles ICMP errors for ICMP packets
+ */
+ struct icmphdr *cicmph = (struct icmphdr *)((char *)ciph +
+ (ciph->ihl<<2));
+
+
+ IP_MASQ_DEBUG(2, "fw icmp/icmp rcv %lX->%lX id %d type %d\n",
+ ntohl(ciph->saddr),
+ ntohl(ciph->daddr),
+ ntohs(icmp_id(cicmph)),
+ cicmph->type);
+
+ read_lock(&__ip_masq_lock);
+ ms = __ip_masq_out_get(ciph->protocol,
+ ciph->daddr,
+ icmp_id(cicmph),
+ ciph->saddr,
+ icmp_hv_rep(cicmph));
+ read_unlock(&__ip_masq_lock);
+
+ if (ms == NULL)
+ return 0;
+
+ /* Now we do real damage to this packet...! */
+ /* First change the source IP address, and recalc checksum */
+ iph->saddr = ms->maddr;
+ ip_send_check(iph);
+
+ /* Now change the *dest* address in the contained IP */
+ ciph->daddr = ms->maddr;
+ __ip_masq_put(ms);
+
+ ip_send_check(ciph);
+
+ /* Change the ID to the masqed one! */
+ (cicmph->un).echo.id = ms->mport;
+
+ /* And finally the ICMP checksum */
+ icmph->checksum = 0;
+ icmph->checksum = ip_compute_csum((unsigned char *) icmph, len);
+
+
+ IP_MASQ_DEBUG(2, "fw icmp/icmp rwt %lX->%lX id %d type %d\n",
+ ntohl(ciph->saddr),
+ ntohl(ciph->daddr),
+ ntohs(icmp_id(cicmph)),
+ cicmph->type);
+
+ return 1;
+ }
+#endif /* CONFIG_IP_MASQUERADE_ICMP */
+
+ /* We are only interested ICMPs generated from TCP or UDP packets */
+ if ((ciph->protocol != IPPROTO_UDP) && (ciph->protocol != IPPROTO_TCP))
+ return 0;
+
+ /*
+ * Find the ports involved - this packet was
+ * incoming so the ports are right way round
+ * (but reversed relative to outer IP header!)
+ */
+ pptr = (__u16 *)&(((char *)ciph)[ciph->ihl*4]);
+#if 0
+ if (ntohs(pptr[1]) < PORT_MASQ_BEGIN ||
+ ntohs(pptr[1]) > PORT_MASQ_END)
+ return 0;
+#endif
+
+ /* Ensure the checksum is correct */
+ if (ip_compute_csum((unsigned char *) icmph, len))
+ {
+ /* Failed checksum! */
+ IP_MASQ_DEBUG(0, "forward ICMP: failed checksum from %d.%d.%d.%d!\n",
+ NIPQUAD(iph->saddr));
+ return(-1);
+ }
+
+
+ IP_MASQ_DEBUG(2, "Handling forward ICMP for %08lX:%04X -> %08lX:%04X\n",
+ ntohl(ciph->saddr), ntohs(pptr[0]),
+ ntohl(ciph->daddr), ntohs(pptr[1]));
+
+
+#if 0
+ /* This is pretty much what __ip_masq_in_get_iph() does */
+ ms = __ip_masq_in_get(ciph->protocol, ciph->saddr, pptr[0], ciph->daddr, pptr[1]);
+#endif
+ read_lock(&__ip_masq_lock);
+ ms = __ip_masq_out_get(ciph->protocol,
+ ciph->daddr,
+ pptr[1],
+ ciph->saddr,
+ pptr[0]);
+ read_unlock(&__ip_masq_lock);
+
+ if (ms == NULL)
+ return 0;
+
+ /* Now we do real damage to this packet...! */
+ /* First change the source IP address, and recalc checksum */
+ iph->saddr = ms->maddr;
+ ip_send_check(iph);
+
+ /* Now change the *dest* address in the contained IP */
+ ciph->daddr = ms->maddr;
+ ip_send_check(ciph);
+
+ /* the TCP/UDP dest port - cannot redo check */
+ pptr[1] = ms->mport;
+ __ip_masq_put(ms);
+
+ /* And finally the ICMP checksum */
+ icmph->checksum = 0;
+ icmph->checksum = ip_compute_csum((unsigned char *) icmph, len);
+
+
+ IP_MASQ_DEBUG(2, "Rewrote forward ICMP to %08lX:%04X -> %08lX:%04X\n",
+ ntohl(ciph->saddr), ntohs(pptr[0]),
+ ntohl(ciph->daddr), ntohs(pptr[1]));
+
+
+ return 1;
+}
+
+
+/*
+ * Own skb_cow() beast, tweaked for rewriting commonly
+ * used pointers in masq code
+ */
+static struct sk_buff * masq_skb_cow(struct sk_buff **skb_p,
+ struct iphdr **iph_p, unsigned char **t_p) {
+ struct sk_buff *skb=(*skb_p);
+ if (skb_cloned(skb)) {
+ skb = skb_copy(skb, GFP_ATOMIC);
+ if (skb) {
+ /*
+ * skb changed, update other pointers
+ */
+ struct iphdr *iph = skb->nh.iph;
+ kfree_skb(*skb_p);
+ *skb_p = skb;
+ *iph_p = iph;
+ *t_p = (char*) iph + iph->ihl * 4;
+ }
+ }
+ return skb;
+}
+
+/*
+ * Handle ICMP messages in reverse (demasquerade) direction.
+ * Find any that might be relevant, check against existing connections,
+ * forward to masqueraded host if relevant.
+ * Currently handles error types - unreachable, quench, ttl exceeded
+ */
+
+int ip_fw_demasq_icmp(struct sk_buff **skb_p)
+{
+ struct sk_buff *skb = *skb_p;
+ struct iphdr *iph = skb->nh.iph;
+ struct icmphdr *icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
+ struct iphdr *ciph; /* The ip header contained within the ICMP */
+ __u16 *pptr; /* port numbers from TCP/UDP contained header */
+ struct ip_masq *ms;
+ unsigned short len = ntohs(iph->tot_len) - (iph->ihl * 4);
+
+
+ IP_MASQ_DEBUG(2, "icmp in/rev (%d,%d) %lX -> %lX\n",
+ icmph->type, ntohs(icmp_id(icmph)),
+ ntohl(iph->saddr), ntohl(iph->daddr));
+
+
+#ifdef CONFIG_IP_MASQUERADE_ICMP
+ if ((icmph->type == ICMP_ECHOREPLY) ||
+ (icmph->type == ICMP_TIMESTAMPREPLY) ||
+ (icmph->type == ICMP_INFO_REPLY) ||
+ (icmph->type == ICMP_ADDRESSREPLY)) {
+
+ IP_MASQ_DEBUG(2, "icmp reply rcv %lX->%lX id %d type %d, req %d\n",
+ ntohl(iph->saddr),
+ ntohl(iph->daddr),
+ ntohs(icmp_id(icmph)),
+ icmph->type,
+ icmp_type_request(icmph->type));
+
+ ms = ip_masq_in_get(iph->protocol,
+ iph->saddr,
+ icmp_hv_rep(icmph),
+ iph->daddr,
+ icmp_id(icmph));
+ if (ms == NULL)
+ return 0;
+
+ /*
+ * got reply, so clear flag
+ */
+ ms->flags &= ~IP_MASQ_F_NO_REPLY;
+
+ if ((skb=masq_skb_cow(skb_p, &iph, (unsigned char**)&icmph)) == NULL) {
+ ip_masq_put(ms);
+ return -1;
+ }
+
+ /* Reset source address */
+ iph->daddr = ms->saddr;
+ /* Redo IP header checksum */
+ ip_send_check(iph);
+ /* Set ID to fake port number */
+ (icmph->un).echo.id = ms->sport;
+ /* Reset ICMP checksum and set expiry */
+ icmph->checksum=0;
+ icmph->checksum=ip_compute_csum((unsigned char *)icmph,len);
+
+
+
+ IP_MASQ_DEBUG(2, "icmp reply rwt %lX->%lX id %d type %d\n",
+ ntohl(iph->saddr),
+ ntohl(iph->daddr),
+ ntohs(icmp_id(icmph)),
+ icmph->type);
+
+ masq_set_state(ms, 0, iph, icmph);
+ ip_masq_put(ms);
+
+ return 1;
+ } else {
+#endif
+ if ((icmph->type != ICMP_DEST_UNREACH) &&
+ (icmph->type != ICMP_SOURCE_QUENCH) &&
+ (icmph->type != ICMP_TIME_EXCEEDED))
+ return 0;
+#ifdef CONFIG_IP_MASQUERADE_ICMP
+ }
+#endif
+ /*
+ * If we get here we have an ICMP error of one of the above 3 types
+ * Now find the contained IP header
+ */
+
+ ciph = (struct iphdr *) (icmph + 1);
+
+#ifdef CONFIG_IP_MASQUERADE_ICMP
+ if (ciph->protocol == IPPROTO_ICMP) {
+ /*
+ * This section handles ICMP errors for ICMP packets
+ *
+ * First get a new ICMP header structure out of the IP packet
+ */
+ struct icmphdr *cicmph = (struct icmphdr *)((char *)ciph +
+ (ciph->ihl<<2));
+
+
+ IP_MASQ_DEBUG(2, "rv icmp/icmp rcv %lX->%lX id %d type %d\n",
+ ntohl(ciph->saddr),
+ ntohl(ciph->daddr),
+ ntohs(icmp_id(cicmph)),
+ cicmph->type);
+
+ read_lock(&__ip_masq_lock);
+ ms = __ip_masq_in_get(ciph->protocol,
+ ciph->daddr,
+ icmp_hv_req(cicmph),
+ ciph->saddr,
+ icmp_id(cicmph));
+ read_unlock(&__ip_masq_lock);
+
+ if (ms == NULL)
+ return 0;
+
+ if ((skb=masq_skb_cow(skb_p, &iph, (unsigned char**)&icmph)) == NULL) {
+ __ip_masq_put(ms);
+ return -1;
+ }
+ ciph = (struct iphdr *) (icmph + 1);
+ cicmph = (struct icmphdr *)((char *)ciph +
+ (ciph->ihl<<2));
+ /* Now we do real damage to this packet...! */
+ /* First change the dest IP address, and recalc checksum */
+ iph->daddr = ms->saddr;
+ ip_send_check(iph);
+
+ /* Now change the *source* address in the contained IP */
+ ciph->saddr = ms->saddr;
+ ip_send_check(ciph);
+
+ /* Change the ID to the original one! */
+ (cicmph->un).echo.id = ms->sport;
+ __ip_masq_put(ms);
+
+ /* And finally the ICMP checksum */
+ icmph->checksum = 0;
+ icmph->checksum = ip_compute_csum((unsigned char *) icmph, len);
+
+
+ IP_MASQ_DEBUG(2, "rv icmp/icmp rwt %lX->%lX id %d type %d\n",
+ ntohl(ciph->saddr),
+ ntohl(ciph->daddr),
+ ntohs(icmp_id(cicmph)),
+ cicmph->type);
+
+ return 1;
+ }
+#endif /* CONFIG_IP_MASQUERADE_ICMP */
+
+ /* We are only interested ICMPs generated from TCP or UDP packets */
+ if ((ciph->protocol != IPPROTO_UDP) &&
+ (ciph->protocol != IPPROTO_TCP))
+ return 0;
+
+ /*
+ * Find the ports involved - remember this packet was
+ * *outgoing* so the ports are reversed (and addresses)
+ */
+ pptr = (__u16 *)&(((char *)ciph)[ciph->ihl*4]);
+ if (ntohs(pptr[0]) < PORT_MASQ_BEGIN ||
+ ntohs(pptr[0]) > PORT_MASQ_END)
+ return 0;
+
+ /* Ensure the checksum is correct */
+ if (ip_compute_csum((unsigned char *) icmph, len))
+ {
+ /* Failed checksum! */
+ IP_MASQ_ERR( "reverse ICMP: failed checksum from %d.%d.%d.%d!\n",
+ NIPQUAD(iph->saddr));
+ return(-1);
+ }
+
+
+ IP_MASQ_DEBUG(2, "Handling reverse ICMP for %08lX:%04X -> %08lX:%04X\n",
+ ntohl(ciph->saddr), ntohs(pptr[0]),
+ ntohl(ciph->daddr), ntohs(pptr[1]));
+
+
+ /* This is pretty much what __ip_masq_in_get_iph() does, except params are wrong way round */
+ read_lock(&__ip_masq_lock);
+ ms = __ip_masq_in_get(ciph->protocol,
+ ciph->daddr,
+ pptr[1],
+ ciph->saddr,
+ pptr[0]);
+ read_unlock(&__ip_masq_lock);
+
+ if (ms == NULL)
+ return 0;
+
+ if ((skb=masq_skb_cow(skb_p, &iph, (unsigned char**)&icmph)) == NULL) {
+ __ip_masq_put(ms);
+ return -1;
+ }
+ ciph = (struct iphdr *) (icmph + 1);
+ pptr = (__u16 *)&(((char *)ciph)[ciph->ihl*4]);
+
+ /* Now we do real damage to this packet...! */
+ /* First change the dest IP address, and recalc checksum */
+ iph->daddr = ms->saddr;
+ ip_send_check(iph);
+
+ /* Now change the *source* address in the contained IP */
+ ciph->saddr = ms->saddr;
+ ip_send_check(ciph);
+
+ /* the TCP/UDP source port - cannot redo check */
+ pptr[0] = ms->sport;
+ __ip_masq_put(ms);
+
+ /* And finally the ICMP checksum */
+ icmph->checksum = 0;
+ icmph->checksum = ip_compute_csum((unsigned char *) icmph, len);
+
+
+ IP_MASQ_DEBUG(2, "Rewrote reverse ICMP to %08lX:%04X -> %08lX:%04X\n",
+ ntohl(ciph->saddr), ntohs(pptr[0]),
+ ntohl(ciph->daddr), ntohs(pptr[1]));
+
+
+ return 1;
+}
+
+ /*
+ * Check if it's an masqueraded port, look it up,
+ * and send it on its way...
+ *
+ * Better not have many hosts using the designated portrange
+ * as 'normal' ports, or you'll be spending many time in
+ * this function.
+ */
+
+int ip_fw_demasquerade(struct sk_buff **skb_p)
+{
+ struct sk_buff *skb = *skb_p;
+ struct iphdr *iph = skb->nh.iph;
+ union ip_masq_tphdr h;
+ struct ip_masq *ms;
+ unsigned short size;
+ int doff = 0;
+ int csum = 0;
+ int csum_ok = 0;
+ __u32 maddr;
+
+ /*
+ * Big tappo: only PACKET_HOST (nor loopback neither mcasts)
+ * ... don't know why 1st test DOES NOT include 2nd (?)
+ */
+
+ if (skb->pkt_type != PACKET_HOST || skb->dev == &loopback_dev) {
+ IP_MASQ_DEBUG(2, "ip_fw_demasquerade(): packet type=%d proto=%d daddr=%d.%d.%d.%d ignored\n",
+ skb->pkt_type,
+ iph->protocol,
+ NIPQUAD(iph->daddr));
+ return 0;
+ }
+
+ h.raw = (char*) iph + iph->ihl * 4;
+
+ /*
+ * IP payload size
+ */
+ size = ntohs(iph->tot_len) - (iph->ihl * 4);
+
+ doff = proto_doff(iph->protocol, h.raw, size);
+
+ switch (doff) {
+ case 0:
+ /*
+ * Input path: other IP protos Ok, will
+ * reach local sockets path.
+ */
+ return 0;
+ case -1:
+ IP_MASQ_DEBUG(0, "I-pkt invalid packet data size\n");
+ return -1;
+ }
+
+ maddr = iph->daddr;
+ switch (iph->protocol) {
+ case IPPROTO_ICMP:
+ return(ip_fw_demasq_icmp(skb_p));
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ /*
+ * Make sure packet is in the masq range
+ * ... or some mod-ule relaxes input range
+ * ... or there is still some `special' mport opened
+ */
+ if ((ntohs(h.portp[1]) < PORT_MASQ_BEGIN
+ || ntohs(h.portp[1]) > PORT_MASQ_END)
+#ifdef CONFIG_IP_MASQUERADE_MOD
+ && (ip_masq_mod_in_rule(skb, iph) != 1)
+#endif
+ && atomic_read(&mport_count) == 0 )
+ return 0;
+
+ /* Check that the checksum is OK */
+ if ((iph->protocol == IPPROTO_UDP) && (h.uh->check == 0))
+ /* No UDP checksum */
+ break;
+#ifdef CONFIG_IP_MASQ_DEBUG
+ if (ip_masq_get_debug_level() > 3) {
+ skb->ip_summed = CHECKSUM_NONE;
+ }
+#endif
+
+ switch (skb->ip_summed)
+ {
+ case CHECKSUM_NONE:
+ csum = csum_partial(h.raw + doff, size - doff, 0);
+ csum_ok++;
+ skb->csum = csum_partial(h.raw , doff, csum);
+
+ case CHECKSUM_HW:
+ if (csum_tcpudp_magic(iph->saddr, iph->daddr,
+ size, iph->protocol, skb->csum))
+ {
+ IP_MASQ_DEBUG(0, "Incoming failed %s checksum from %d.%d.%d.%d (size=%d)!\n",
+ masq_proto_name(iph->protocol),
+ NIPQUAD(iph->saddr),
+ size);
+ return -1;
+ }
+ default:
+ /* CHECKSUM_UNNECESSARY */
+ }
+ break;
+ default:
+ return 0;
+ }
+
+
+
+ IP_MASQ_DEBUG(2, "Incoming %s %08lX:%04X -> %08lX:%04X\n",
+ masq_proto_name(iph->protocol),
+ ntohl(iph->saddr), ntohs(h.portp[0]),
+ ntohl(iph->daddr), ntohs(h.portp[1]));
+
+ /*
+ * reroute to original host:port if found...
+ */
+
+ ms = ip_masq_in_get_iph(iph);
+
+ /*
+ * Give additional modules a chance to create an entry
+ */
+#ifdef CONFIG_IP_MASQUERADE_MOD
+ if (!ms)
+ ms = ip_masq_mod_in_create(skb, iph, maddr);
+
+ /*
+ * Call module's input update hook
+ */
+ ip_masq_mod_in_update(skb, iph, ms);
+#endif
+
+
+ if (ms != NULL)
+ {
+
+ /*
+ * got reply, so clear flag
+ */
+ ms->flags &= ~IP_MASQ_F_NO_REPLY;
+
+ /*
+ * Set daddr,dport if not defined yet
+ * and tunnel is not setup as "dest loose"
+ */
+
+ if (ms->flags & IP_MASQ_F_DLOOSE) {
+ /*
+ * update dest loose values
+ */
+ ms->dport = h.portp[0];
+ ms->daddr = iph->saddr;
+ } else {
+ if ( ms->flags & IP_MASQ_F_NO_DPORT ) { /* && ms->protocol == IPPROTO_TCP ) { */
+
+ write_lock(&__ip_masq_lock);
+
+ ip_masq_unhash(ms);
+ ms->flags &= ~IP_MASQ_F_NO_DPORT;
+ ms->dport = h.portp[0];
+ ip_masq_hash(ms); /* hash on new dport */
+
+ write_unlock(&__ip_masq_lock);
+
+ IP_MASQ_DEBUG(1, "ip_fw_demasquerade(): filled dport=%d\n",
+ ntohs(ms->dport));
+
+ }
+ if (ms->flags & IP_MASQ_F_NO_DADDR ) { /* && ms->protocol == IPPROTO_TCP) { */
+
+ write_lock(&__ip_masq_lock);
+
+ ip_masq_unhash(ms);
+ ms->flags &= ~IP_MASQ_F_NO_DADDR;
+ ms->daddr = iph->saddr;
+ ip_masq_hash(ms); /* hash on new daddr */
+
+ write_unlock(&__ip_masq_lock);
+
+ IP_MASQ_DEBUG(1, "ip_fw_demasquerade(): filled daddr=%lX\n",
+ ntohl(ms->daddr));
+
+ }
+ }
+ if ((skb=masq_skb_cow(skb_p, &iph, &h.raw)) == NULL) {
+ ip_masq_put(ms);
+ return -1;
+ }
+ iph->daddr = ms->saddr;
+ h.portp[1] = ms->sport;
+
+ /*
+ * Invalidate csum saving if tunnel has masq helper
+ */
+
+ if (ms->app)
+ csum_ok = 0;
+
+ /*
+ * Attempt ip_masq_app call.
+ * will fix ip_masq and iph ack_seq stuff
+ */
+
+ if (ip_masq_app_pkt_in(ms, skb_p, maddr) != 0)
+ {
+ /*
+ * skb has changed, update pointers.
+ */
+
+ skb = *skb_p;
+ iph = skb->nh.iph;
+ h.raw = (char*) iph + iph->ihl*4;
+ size = ntohs(iph->tot_len) - (iph->ihl * 4);
+ }
+
+ /*
+ * Yug! adjust UDP/TCP checksums
+ */
+
+ /*
+ * Transport's payload partial csum
+ */
+
+ if (!csum_ok) {
+ csum = csum_partial(h.raw + doff, size - doff, 0);
+ }
+ skb->csum = csum;
+
+ /*
+ * Protocol csum
+ */
+ switch (iph->protocol) {
+ case IPPROTO_TCP:
+ h.th->check = 0;
+ h.th->check=csum_tcpudp_magic(iph->saddr, iph->daddr,
+ size, iph->protocol,
+ csum_partial(h.raw , doff, csum));
+ break;
+ case IPPROTO_UDP:
+ h.uh->check = 0;
+ h.uh->check=csum_tcpudp_magic(iph->saddr, iph->daddr,
+ size, iph->protocol,
+ csum_partial(h.raw , doff, csum));
+ if (h.uh->check == 0)
+ h.uh->check = 0xFFFF;
+ break;
+ }
+ ip_send_check(iph);
+
+ IP_MASQ_DEBUG(2, "I-routed to %08lX:%04X\n",ntohl(iph->daddr),ntohs(h.portp[1]));
+
+ masq_set_state (ms, 0, iph, h.portp);
+ ip_masq_put(ms);
+
+ return 1;
+ }
+
+ /* sorry, all this trouble for a no-hit :) */
+ return 0;
+}
+
+
+void ip_masq_control_add(struct ip_masq *ms, struct ip_masq* ctl_ms)
+{
+ if (ms->control) {
+ IP_MASQ_ERR( "request control ADD for already controlled: %d.%d.%d.%d:%d to %d.%d.%d.%d:%d\n",
+ NIPQUAD(ms->saddr),ntohs(ms->sport),
+ NIPQUAD(ms->daddr),ntohs(ms->dport));
+ ip_masq_control_del(ms);
+ }
+ IP_MASQ_DEBUG(1, "ADDing control for: ms.dst=%d.%d.%d.%d:%d ctl_ms.dst=%d.%d.%d.%d:%d\n",
+ NIPQUAD(ms->daddr),ntohs(ms->dport),
+ NIPQUAD(ctl_ms->daddr),ntohs(ctl_ms->dport));
+ ms->control = ctl_ms;
+ atomic_inc(&ctl_ms->n_control);
+}
+
+void ip_masq_control_del(struct ip_masq *ms)
+{
+ struct ip_masq *ctl_ms = ms->control;
+ if (!ctl_ms) {
+ IP_MASQ_ERR( "request control DEL for uncontrolled: %d.%d.%d.%d:%d to %d.%d.%d.%d:%d\n",
+ NIPQUAD(ms->saddr),ntohs(ms->sport),
+ NIPQUAD(ms->daddr),ntohs(ms->dport));
+ return;
+ }
+ IP_MASQ_DEBUG(1, "DELeting control for: ms.dst=%d.%d.%d.%d:%d ctl_ms.dst=%d.%d.%d.%d:%d\n",
+ NIPQUAD(ms->daddr),ntohs(ms->dport),
+ NIPQUAD(ctl_ms->daddr),ntohs(ctl_ms->dport));
+ ms->control = NULL;
+ if (atomic_read(&ctl_ms->n_control) == 0) {
+ IP_MASQ_ERR( "BUG control DEL with n=0 : %d.%d.%d.%d:%d to %d.%d.%d.%d:%d\n",
+ NIPQUAD(ms->saddr),ntohs(ms->sport),
+ NIPQUAD(ms->daddr),ntohs(ms->dport));
+ return;
+
+ }
+ atomic_dec(&ctl_ms->n_control);
+}
+
+struct ip_masq * ip_masq_control_get(struct ip_masq *ms)
+{
+ return ms->control;
+}
+
+
+#ifdef CONFIG_PROC_FS
+/*
+ * /proc/net entries
+ * From userspace
+ */
+static int ip_msqhst_procinfo(char *buffer, char **start, off_t offset,
+ int length, int unused)
+{
+ off_t pos=0, begin;
+ struct ip_masq *ms;
+ char temp[129];
+ int idx = 0;
+ int len=0;
+ struct list_head *l,*e;
+
+ if (offset < 128)
+ {
+ sprintf(temp,
+ "Prc FromIP FPrt ToIP TPrt Masq Init-seq Delta PDelta Expires (free=%d,%d,%d)",
+ atomic_read(ip_masq_free_ports),
+ atomic_read(ip_masq_free_ports+1),
+ atomic_read(ip_masq_free_ports+2));
+ len = sprintf(buffer, "%-127s\n", temp);
+ }
+ pos = 128;
+
+ for(idx = 0; idx < IP_MASQ_TAB_SIZE; idx++)
+ {
+ /*
+ * Lock is actually only need in next loop
+ * we are called from uspace: must stop bh.
+ */
+ read_lock_bh(&__ip_masq_lock);
+
+ l = &ip_masq_m_table[idx];
+ for (e=l->next; e!=l; e=e->next) {
+ ms = list_entry(e, struct ip_masq, m_list);
+ pos += 128;
+ if (pos <= offset) {
+ len = 0;
+ continue;
+ }
+
+ /*
+ * We have locked the tables, no need to del/add timers
+ * nor cli() 8)
+ */
+
+ sprintf(temp,"%s %08X:%04X %08X:%04X %04X %08X %6d %6d %7lu",
+ masq_proto_name(ms->protocol),
+ ntohl(ms->saddr), ntohs(ms->sport),
+ ntohl(ms->daddr), ntohs(ms->dport),
+ ntohs(ms->mport),
+ ms->out_seq.init_seq,
+ ms->out_seq.delta,
+ ms->out_seq.previous_delta,
+ ms->timer.expires-jiffies);
+ len += sprintf(buffer+len, "%-127s\n", temp);
+
+ if(len >= length) {
+
+ read_unlock_bh(&__ip_masq_lock);
+ goto done;
+ }
+ }
+ read_unlock_bh(&__ip_masq_lock);
+
+ }
+done:
+
+
+ begin = len - (pos - offset);
+ *start = buffer + begin;
+ len -= begin;
+ if(len>length)
+ len = length;
+ return len;
+}
+
+#endif
+
+/*
+ * Timeouts handling by ipfwadm/ipchains
+ * From ip_fw.c
+ */
+
+int ip_fw_masq_timeouts(void *m, int len)
+{
+ struct ip_fw_masq *masq;
+ int ret = EINVAL;
+
+ if (len != sizeof(struct ip_fw_masq)) {
+ IP_MASQ_DEBUG(1, "ip_fw_masq_timeouts: length %d, expected %d\n",
+ len, sizeof(struct ip_fw_masq));
+ } else {
+ masq = (struct ip_fw_masq *)m;
+ if (masq->tcp_timeout)
+ masq_timeout_table.timeout[IP_MASQ_S_ESTABLISHED]
+ = masq->tcp_timeout;
+
+ if (masq->tcp_fin_timeout)
+ masq_timeout_table.timeout[IP_MASQ_S_FIN_WAIT]
+ = masq->tcp_fin_timeout;
+
+ if (masq->udp_timeout)
+ masq_timeout_table.timeout[IP_MASQ_S_UDP]
+ = masq->udp_timeout;
+ ret = 0;
+ }
+ return ret;
+}
+/*
+ * Module autoloading stuff
+ */
+
+static int ip_masq_user_check_hook(void) {
+#ifdef CONFIG_KMOD
+ if (ip_masq_user_hook == NULL) {
+ IP_MASQ_DEBUG(1, "About to request \"ip_masq_user\" module\n");
+ request_module("ip_masq_user");
+ }
+#endif /* CONFIG_KMOD */
+ return (ip_masq_user_hook != NULL);
+}
+
+/*
+ * user module hook- info
+ */
+static int ip_masq_user_info(char *buffer, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ int ret = -ENOPKG;
+ if (ip_masq_user_check_hook()) {
+ ret = ip_masq_user_hook->info(buffer, start, offset, len, (int) data);
+ }
+ return ret;
+}
+
+/*
+ * user module hook- entry mgmt
+ */
+static int ip_masq_user_ctl(int optname, void *arg, int arglen)
+{
+ int ret = -ENOPKG;
+ if (ip_masq_user_check_hook()) {
+ ret = ip_masq_user_hook->ctl(optname, arg, arglen);
+ }
+ return ret;
+}
+
+/*
+ * Control from ip_sockglue
+ * MAIN ENTRY point from userspace (apart from /proc *info entries)
+ * Returns errno
+ */
+int ip_masq_uctl(int optname, char * optval , int optlen)
+{
+ struct ip_masq_ctl masq_ctl;
+ int ret = -EINVAL;
+
+ if(optlen>sizeof(masq_ctl))
+ return -EINVAL;
+
+ if(copy_from_user(&masq_ctl,optval,optlen))
+ return -EFAULT;
+
+ IP_MASQ_DEBUG(1,"ip_masq_ctl(optname=%d, optlen=%d, target=%d, cmd=%d)\n",
+ optname, optlen, masq_ctl.m_target, masq_ctl.m_cmd);
+
+ switch (masq_ctl.m_target) {
+ case IP_MASQ_TARGET_USER:
+ ret = ip_masq_user_ctl(optname, &masq_ctl, optlen);
+ break;
+#ifdef CONFIG_IP_MASQUERADE_MOD
+ case IP_MASQ_TARGET_MOD:
+ ret = ip_masq_mod_ctl(optname, &masq_ctl, optlen);
+ break;
+#endif
+ }
+
+ /*
+ * If ret>0, copy to user space
+ */
+
+ if (ret > 0 && ret <= sizeof (masq_ctl)) {
+ if (copy_to_user(optval, &masq_ctl, ret) )
+ return -EFAULT;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *proc_net_ip_masq = NULL;
+
+#ifdef MODULE
+static void ip_masq_proc_count(struct inode *inode, int fill)
+{
+ if (fill)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+}
+#endif
+
+int ip_masq_proc_register(struct proc_dir_entry *ent)
+{
+ if (!proc_net_ip_masq) return -1;
+ IP_MASQ_DEBUG(1, "registering \"/proc/net/ip_masq/%s\" entry\n",
+ ent->name);
+ return proc_register(proc_net_ip_masq, ent);
+}
+void ip_masq_proc_unregister(struct proc_dir_entry *ent)
+{
+ if (!proc_net_ip_masq) return;
+ IP_MASQ_DEBUG(1, "unregistering \"/proc/net/ip_masq/%s\" entry\n",
+ ent->name);
+ proc_unregister(proc_net_ip_masq, ent->low_ino);
+}
+
+
+__initfunc(static void masq_proc_init(void))
+{
+ IP_MASQ_DEBUG(1,"registering /proc/net/ip_masq\n");
+ if (!proc_net_ip_masq) {
+ struct proc_dir_entry *ent;
+ ent = create_proc_entry("net/ip_masq", S_IFDIR, 0);
+ if (ent) {
+#ifdef MODULE
+ ent->fill_inode = ip_masq_proc_count;
+#endif
+ proc_net_ip_masq = ent;
+ } else {
+ IP_MASQ_ERR("Could not create \"/proc/net/ip_masq\" entry\n");
+ }
+ }
+}
+#endif /* CONFIG_PROC_FS */
+/*
+ * Wrapper over inet_select_addr()
+ */
+u32 ip_masq_select_addr(struct device *dev, u32 dst, int scope)
+{
+ return inet_select_addr(dev, dst, scope);
+}
+
+/*
+ * Initialize ip masquerading
+ */
+__initfunc(int ip_masq_init(void))
+{
+ int idx;
+ for(idx = 0; idx < IP_MASQ_TAB_SIZE; idx++) {
+ INIT_LIST_HEAD(&ip_masq_s_table[idx]);
+ INIT_LIST_HEAD(&ip_masq_m_table[idx]);
+ INIT_LIST_HEAD(&ip_masq_d_table[idx]);
+ }
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&(struct proc_dir_entry) {
+ PROC_NET_IPMSQHST, 13, "ip_masquerade",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ ip_msqhst_procinfo
+ });
+ masq_proc_init();
+
+ ip_masq_proc_register(&(struct proc_dir_entry) {
+ 0, 3, "tcp",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ NULL, /* get_info */
+ NULL, /* fill_inode */
+ NULL, NULL, NULL,
+ (char *) IPPROTO_TCP,
+ ip_masq_user_info
+ });
+ ip_masq_proc_register(&(struct proc_dir_entry) {
+ 0, 3, "udp",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ NULL, /* get_info */
+ NULL, /* fill_inode */
+ NULL, NULL, NULL,
+ (char *) IPPROTO_UDP,
+ ip_masq_user_info
+ });
+ ip_masq_proc_register(&(struct proc_dir_entry) {
+ 0, 4, "icmp",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ NULL, /* get_info */
+ NULL, /* fill_inode */
+ NULL, NULL, NULL,
+ (char *) IPPROTO_ICMP,
+ ip_masq_user_info
+ });
+#endif
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+ ip_autofw_init();
+#endif
+#ifdef CONFIG_IP_MASQUERADE_IPPORTFW
+ ip_portfw_init();
+#endif
+#ifdef CONFIG_IP_MASQUERADE_MFW
+ ip_mfw_init();
+#endif
+ ip_masq_app_init();
+
+ return 0;
+}
diff --git a/pfinet/linux-src/net/ipv4/ip_masq_app.c b/pfinet/linux-src/net/ipv4/ip_masq_app.c
new file mode 100644
index 00000000..84e059fa
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_masq_app.c
@@ -0,0 +1,603 @@
+/*
+ * IP_MASQ_APP application masquerading module
+ *
+ *
+ * $Id: ip_masq_app.c,v 1.16 1998/08/29 23:51:14 davem Exp $
+ *
+ * Author: Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Fixes:
+ * JJC : Implemented also input pkt hook
+ * Miquel van Smoorenburg : Copy more stuff when resizing skb
+ *
+ *
+ * FIXME:
+ * - ip_masq_skb_replace(): use same skb if space available.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/init.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <asm/system.h>
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#include <net/ip_masq.h>
+
+#define IP_MASQ_APP_TAB_SIZE 16 /* must be power of 2 */
+
+#define IP_MASQ_APP_HASH(proto, port) ((port^proto) & (IP_MASQ_APP_TAB_SIZE-1))
+#define IP_MASQ_APP_TYPE(proto, port) ( proto<<16 | port )
+#define IP_MASQ_APP_PORT(type) ( type & 0xffff )
+#define IP_MASQ_APP_PROTO(type) ( (type>>16) & 0x00ff )
+
+
+EXPORT_SYMBOL(register_ip_masq_app);
+EXPORT_SYMBOL(unregister_ip_masq_app);
+EXPORT_SYMBOL(ip_masq_skb_replace);
+
+/*
+ * will hold masq app. hashed list heads
+ */
+
+struct ip_masq_app *ip_masq_app_base[IP_MASQ_APP_TAB_SIZE];
+
+/*
+ * ip_masq_app registration routine
+ * port: host byte order.
+ */
+
+int register_ip_masq_app(struct ip_masq_app *mapp, unsigned short proto, __u16 port)
+{
+ unsigned long flags;
+ unsigned hash;
+ if (!mapp) {
+ IP_MASQ_ERR("register_ip_masq_app(): NULL arg\n");
+ return -EINVAL;
+ }
+ mapp->type = IP_MASQ_APP_TYPE(proto, port);
+ mapp->n_attach = 0;
+ hash = IP_MASQ_APP_HASH(proto, port);
+
+ save_flags(flags);
+ cli();
+ mapp->next = ip_masq_app_base[hash];
+ ip_masq_app_base[hash] = mapp;
+ restore_flags(flags);
+
+ return 0;
+}
+
+/*
+ * ip_masq_app unreg. routine.
+ */
+
+int unregister_ip_masq_app(struct ip_masq_app *mapp)
+{
+ struct ip_masq_app **mapp_p;
+ unsigned hash;
+ unsigned long flags;
+ if (!mapp) {
+ IP_MASQ_ERR("unregister_ip_masq_app(): NULL arg\n");
+ return -EINVAL;
+ }
+ /*
+ * only allow unregistration if it has no attachments
+ */
+ if (mapp->n_attach) {
+ IP_MASQ_ERR("unregister_ip_masq_app(): has %d attachments. failed\n",
+ mapp->n_attach);
+ return -EINVAL;
+ }
+ hash = IP_MASQ_APP_HASH(IP_MASQ_APP_PROTO(mapp->type), IP_MASQ_APP_PORT(mapp->type));
+
+ save_flags(flags);
+ cli();
+ for (mapp_p = &ip_masq_app_base[hash]; *mapp_p ; mapp_p = &(*mapp_p)->next)
+ if (mapp == (*mapp_p)) {
+ *mapp_p = mapp->next;
+ restore_flags(flags);
+ return 0;
+ }
+
+ restore_flags(flags);
+ IP_MASQ_ERR("unregister_ip_masq_app(proto=%s,port=%u): not hashed!\n",
+ masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)), IP_MASQ_APP_PORT(mapp->type));
+ return -EINVAL;
+}
+
+/*
+ * get ip_masq_app object by its proto and port (net byte order).
+ */
+
+struct ip_masq_app * ip_masq_app_get(unsigned short proto, __u16 port)
+{
+ struct ip_masq_app *mapp;
+ unsigned hash;
+ unsigned type;
+
+ port = ntohs(port);
+ type = IP_MASQ_APP_TYPE(proto,port);
+ hash = IP_MASQ_APP_HASH(proto,port);
+ for(mapp = ip_masq_app_base[hash]; mapp ; mapp = mapp->next) {
+ if (type == mapp->type) return mapp;
+ }
+ return NULL;
+}
+
+/*
+ * ip_masq_app object binding related funcs.
+ */
+
+/*
+ * change ip_masq_app object's number of bindings
+ */
+
+static __inline__ int ip_masq_app_bind_chg(struct ip_masq_app *mapp, int delta)
+{
+ unsigned long flags;
+ int n_at;
+ if (!mapp) return -1;
+ save_flags(flags);
+ cli();
+ n_at = mapp->n_attach + delta;
+ if (n_at < 0) {
+ restore_flags(flags);
+ IP_MASQ_ERR("ip_masq_app: tried to set n_attach < 0 for (proto=%s,port==%d) ip_masq_app object.\n",
+ masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)),
+ IP_MASQ_APP_PORT(mapp->type));
+ return -1;
+ }
+ mapp->n_attach = n_at;
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * Bind ip_masq to its ip_masq_app based on proto and dport ALREADY
+ * set in ip_masq struct. Also calls constructor.
+ */
+
+struct ip_masq_app * ip_masq_bind_app(struct ip_masq *ms)
+{
+ struct ip_masq_app * mapp;
+
+ if (ms->protocol != IPPROTO_TCP && ms->protocol != IPPROTO_UDP)
+ return NULL;
+
+ mapp = ip_masq_app_get(ms->protocol, ms->dport);
+
+#if 0000
+/* #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW */
+ if (mapp == NULL)
+ mapp = ip_masq_app_get(ms->protocol, ms->sport);
+/* #endif */
+#endif
+
+ if (mapp != NULL) {
+ /*
+ * don't allow binding if already bound
+ */
+
+ if (ms->app != NULL) {
+ IP_MASQ_ERR("ip_masq_bind_app() called for already bound object.\n");
+ return ms->app;
+ }
+
+ ms->app = mapp;
+ if (mapp->masq_init_1) mapp->masq_init_1(mapp, ms);
+ ip_masq_app_bind_chg(mapp, +1);
+ }
+ return mapp;
+}
+
+/*
+ * Unbind ms from type object and call ms destructor (does not kfree()).
+ */
+
+int ip_masq_unbind_app(struct ip_masq *ms)
+{
+ struct ip_masq_app * mapp;
+ mapp = ms->app;
+
+ if (ms->protocol != IPPROTO_TCP && ms->protocol != IPPROTO_UDP)
+ return 0;
+
+ if (mapp != NULL) {
+ if (mapp->masq_done_1) mapp->masq_done_1(mapp, ms);
+ ms->app = NULL;
+ ip_masq_app_bind_chg(mapp, -1);
+ }
+ return (mapp != NULL);
+}
+
+/*
+ * Fixes th->seq based on ip_masq_seq info.
+ */
+
+static __inline__ void masq_fix_seq(const struct ip_masq_seq *ms_seq, struct tcphdr *th)
+{
+ __u32 seq;
+
+ seq = ntohl(th->seq);
+
+ /*
+ * Adjust seq with delta-offset for all packets after
+ * the most recent resized pkt seq and with previous_delta offset
+ * for all packets before most recent resized pkt seq.
+ */
+
+ if (ms_seq->delta || ms_seq->previous_delta) {
+ if(after(seq,ms_seq->init_seq) ) {
+ th->seq = htonl(seq + ms_seq->delta);
+ IP_MASQ_DEBUG(1, "masq_fix_seq() : added delta (%d) to seq\n",ms_seq->delta);
+ } else {
+ th->seq = htonl(seq + ms_seq->previous_delta);
+ IP_MASQ_DEBUG(1, "masq_fix_seq() : added previous_delta (%d) to seq\n",ms_seq->previous_delta);
+ }
+ }
+
+
+}
+
+/*
+ * Fixes th->ack_seq based on ip_masq_seq info.
+ */
+
+static __inline__ void masq_fix_ack_seq(const struct ip_masq_seq *ms_seq, struct tcphdr *th)
+{
+ __u32 ack_seq;
+
+ ack_seq=ntohl(th->ack_seq);
+
+ /*
+ * Adjust ack_seq with delta-offset for
+ * the packets AFTER most recent resized pkt has caused a shift
+ * for packets before most recent resized pkt, use previous_delta
+ */
+
+ if (ms_seq->delta || ms_seq->previous_delta) {
+ if(after(ack_seq,ms_seq->init_seq)) {
+ th->ack_seq = htonl(ack_seq-ms_seq->delta);
+ IP_MASQ_DEBUG(1, "masq_fix_ack_seq() : subtracted delta (%d) from ack_seq\n",ms_seq->delta);
+
+ } else {
+ th->ack_seq = htonl(ack_seq-ms_seq->previous_delta);
+ IP_MASQ_DEBUG(1, "masq_fix_ack_seq() : subtracted previous_delta (%d) from ack_seq\n",ms_seq->previous_delta);
+ }
+ }
+
+}
+
+/*
+ * Updates ip_masq_seq if pkt has been resized
+ * Assumes already checked proto==IPPROTO_TCP and diff!=0.
+ */
+
+static __inline__ void masq_seq_update(struct ip_masq *ms, struct ip_masq_seq *ms_seq, unsigned mflag, __u32 seq, int diff)
+{
+ /* if (diff == 0) return; */
+
+ if ( !(ms->flags & mflag) || after(seq, ms_seq->init_seq))
+ {
+ ms_seq->previous_delta=ms_seq->delta;
+ ms_seq->delta+=diff;
+ ms_seq->init_seq=seq;
+ ms->flags |= mflag;
+ }
+}
+
+/*
+ * Output pkt hook. Will call bound ip_masq_app specific function
+ * called by ip_fw_masquerade(), assumes previously checked ms!=NULL
+ * returns (new - old) skb->len diff.
+ */
+
+int ip_masq_app_pkt_out(struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+ struct ip_masq_app * mapp;
+ struct iphdr *iph;
+ struct tcphdr *th;
+ int diff;
+ __u32 seq;
+
+ /*
+ * check if application masquerading is bound to
+ * this ip_masq.
+ * assumes that once an ip_masq is bound,
+ * it will not be unbound during its life.
+ */
+
+ if ( (mapp = ms->app) == NULL)
+ return 0;
+
+ iph = (*skb_p)->nh.iph;
+ th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
+
+ /*
+ * Remember seq number in case this pkt gets resized
+ */
+
+ seq = ntohl(th->seq);
+
+ /*
+ * Fix seq stuff if flagged as so.
+ */
+
+ if (ms->protocol == IPPROTO_TCP) {
+ if (ms->flags & IP_MASQ_F_OUT_SEQ)
+ masq_fix_seq(&ms->out_seq, th);
+ if (ms->flags & IP_MASQ_F_IN_SEQ)
+ masq_fix_ack_seq(&ms->in_seq, th);
+ }
+
+ /*
+ * Call private output hook function
+ */
+
+ if ( mapp->pkt_out == NULL )
+ return 0;
+
+ diff = mapp->pkt_out(mapp, ms, skb_p, maddr);
+
+ /*
+ * Update ip_masq seq stuff if len has changed.
+ */
+
+ if (diff != 0 && ms->protocol == IPPROTO_TCP)
+ masq_seq_update(ms, &ms->out_seq, IP_MASQ_F_OUT_SEQ, seq, diff);
+
+ return diff;
+}
+
+/*
+ * Input pkt hook. Will call bound ip_masq_app specific function
+ * called by ip_fw_demasquerade(), assumes previously checked ms!=NULL.
+ * returns (new - old) skb->len diff.
+ */
+
+int ip_masq_app_pkt_in(struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+ struct ip_masq_app * mapp;
+ struct iphdr *iph;
+ struct tcphdr *th;
+ int diff;
+ __u32 seq;
+
+ /*
+ * check if application masquerading is bound to
+ * this ip_masq.
+ * assumes that once an ip_masq is bound,
+ * it will not be unbound during its life.
+ */
+
+ if ( (mapp = ms->app) == NULL)
+ return 0;
+
+ iph = (*skb_p)->nh.iph;
+ th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
+
+ /*
+ * Remember seq number in case this pkt gets resized
+ */
+
+ seq = ntohl(th->seq);
+
+ /*
+ * Fix seq stuff if flagged as so.
+ */
+
+ if (ms->protocol == IPPROTO_TCP) {
+ if (ms->flags & IP_MASQ_F_IN_SEQ)
+ masq_fix_seq(&ms->in_seq, th);
+ if (ms->flags & IP_MASQ_F_OUT_SEQ)
+ masq_fix_ack_seq(&ms->out_seq, th);
+ }
+
+ /*
+ * Call private input hook function
+ */
+
+ if ( mapp->pkt_in == NULL )
+ return 0;
+
+ diff = mapp->pkt_in(mapp, ms, skb_p, maddr);
+
+ /*
+ * Update ip_masq seq stuff if len has changed.
+ */
+
+ if (diff != 0 && ms->protocol == IPPROTO_TCP)
+ masq_seq_update(ms, &ms->in_seq, IP_MASQ_F_IN_SEQ, seq, diff);
+
+ return diff;
+}
+
+/*
+ * /proc/ip_masq_app entry function
+ */
+
+int ip_masq_app_getinfo(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ off_t pos=0, begin=0;
+ int len=0;
+ struct ip_masq_app * mapp;
+ unsigned idx;
+
+ if (offset < 40)
+ len=sprintf(buffer,"%-39s\n", "prot port n_attach name");
+ pos = 40;
+
+ for (idx=0 ; idx < IP_MASQ_APP_TAB_SIZE; idx++)
+ for (mapp = ip_masq_app_base[idx]; mapp ; mapp = mapp->next) {
+ /*
+ * If you change the length of this sprintf, then all
+ * the length calculations need fixing too!
+ * Line length = 40 (3 + 2 + 7 + 1 + 7 + 1 + 2 + 17)
+ */
+ pos += 40;
+ if (pos < offset)
+ continue;
+
+ len += sprintf(buffer+len, "%-3s %-7u %-7d %-17s\n",
+ masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)),
+ IP_MASQ_APP_PORT(mapp->type), mapp->n_attach,
+ mapp->name);
+
+ if(len >= length)
+ goto done;
+ }
+done:
+ begin = len - (pos - offset);
+ *start = buffer + begin;
+ len -= begin;
+ if (len > length)
+ len = length;
+ return len;
+}
+
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_ip_masq_app = {
+ PROC_NET_IP_MASQ_APP, 3, "app",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ ip_masq_app_getinfo
+};
+#endif
+
+/*
+ * Initialization routine
+ */
+
+__initfunc(int ip_masq_app_init(void))
+{
+#ifdef CONFIG_PROC_FS
+ ip_masq_proc_register(&proc_net_ip_masq_app);
+#endif
+ return 0;
+}
+
+/*
+ * Replace a segment (of skb->data) with a new one.
+ * FIXME: Should re-use same skb if space available, this could
+ * be done if n_len < o_len, unless some extra space
+ * were already allocated at driver level :P .
+ */
+
+static struct sk_buff * skb_replace(struct sk_buff *skb, int pri, char *o_buf, int o_len, char *n_buf, int n_len)
+{
+ int maxsize, diff, o_offset;
+ struct sk_buff *n_skb;
+ int offset;
+
+ maxsize = skb->truesize;
+
+ diff = n_len - o_len;
+ o_offset = o_buf - (char*) skb->data;
+
+ if (maxsize <= n_len) {
+ if (diff != 0) {
+ memcpy(skb->data + o_offset + n_len,o_buf + o_len,
+ skb->len - (o_offset + o_len));
+ }
+
+ memcpy(skb->data + o_offset, n_buf, n_len);
+
+ n_skb = skb;
+ skb->len = n_len;
+ skb->end = skb->head+n_len;
+ } else {
+ /*
+ * Sizes differ, make a copy.
+ *
+ * FIXME: move this to core/sbuff.c:skb_grow()
+ */
+
+ n_skb = alloc_skb(MAX_HEADER + skb->len + diff, pri);
+ if (n_skb == NULL) {
+ IP_MASQ_ERR("skb_replace(): no room left (from %p)\n",
+ __builtin_return_address(0));
+ return skb;
+
+ }
+ skb_reserve(n_skb, MAX_HEADER);
+ skb_put(n_skb, skb->len + diff);
+
+ /*
+ * Copy as much data from the old skb as possible. Even
+ * though we're only forwarding packets, we need stuff
+ * like skb->protocol (PPP driver wants it).
+ */
+ offset = n_skb->data - skb->data;
+ n_skb->nh.raw = skb->nh.raw + offset;
+ n_skb->h.raw = skb->h.raw + offset;
+ n_skb->dev = skb->dev;
+ n_skb->mac.raw = skb->mac.raw + offset;
+ n_skb->pkt_type = skb->pkt_type;
+ n_skb->protocol = skb->protocol;
+ n_skb->ip_summed = skb->ip_summed;
+ n_skb->dst = dst_clone(skb->dst);
+
+ /*
+ * Copy pkt in new buffer
+ */
+
+ memcpy(n_skb->data, skb->data, o_offset);
+ memcpy(n_skb->data + o_offset, n_buf, n_len);
+ memcpy(n_skb->data + o_offset + n_len, o_buf + o_len,
+ skb->len - (o_offset + o_len) );
+
+ /*
+ * Problem, how to replace the new skb with old one,
+ * preferably inplace
+ */
+
+ kfree_skb(skb);
+ }
+ return n_skb;
+}
+
+/*
+ * calls skb_replace() and update ip header if new skb was allocated
+ */
+
+struct sk_buff * ip_masq_skb_replace(struct sk_buff *skb, int pri, char *o_buf, int o_len, char *n_buf, int n_len)
+{
+ int diff;
+ struct sk_buff *n_skb;
+ unsigned skb_len;
+
+ diff = n_len - o_len;
+ n_skb = skb_replace(skb, pri, o_buf, o_len, n_buf, n_len);
+ skb_len = skb->len;
+
+ if (diff)
+ {
+ struct iphdr *iph;
+ IP_MASQ_DEBUG(1, "masq_skb_replace(): pkt resized for %d bytes (len=%d)\n", diff, skb->len);
+ /*
+ * update ip header
+ */
+ iph = n_skb->nh.iph;
+ iph->check = 0;
+ iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+ iph->tot_len = htons(skb_len + diff);
+ }
+ return n_skb;
+}
diff --git a/pfinet/linux-src/net/ipv4/ip_masq_autofw.c b/pfinet/linux-src/net/ipv4/ip_masq_autofw.c
new file mode 100644
index 00000000..30301441
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_masq_autofw.c
@@ -0,0 +1,448 @@
+/*
+ * IP_MASQ_AUTOFW auto forwarding module
+ *
+ *
+ * $Id: ip_masq_autofw.c,v 1.3.2.1 1999/08/13 18:26:20 davem Exp $
+ *
+ * Author: Richard Lynch
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ *
+ * Fixes:
+ * Juan Jose Ciarlante : created this new file from ip_masq.c and ip_fw.c
+ * Juan Jose Ciarlante : modularized
+ * Juan Jose Ciarlante : use GFP_KERNEL when creating entries
+ * Juan Jose Ciarlante : call del_timer() when freeing entries (!)
+ * FIXME:
+ * - implement refcnt
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <asm/system.h>
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#include <linux/if.h>
+#include <linux/init.h>
+#include <linux/ip_fw.h>
+#include <net/ip_masq.h>
+#include <net/ip_masq_mod.h>
+#include <linux/ip_masq.h>
+
+#define IP_AUTOFW_EXPIRE 15*HZ
+
+/* WARNING: bitwise equal to ip_autofw_user in linux/ip_masq.h */
+struct ip_autofw {
+ struct ip_autofw * next;
+ __u16 type;
+ __u16 low;
+ __u16 hidden;
+ __u16 high;
+ __u16 visible;
+ __u16 protocol;
+ __u32 lastcontact;
+ __u32 where;
+ __u16 ctlproto;
+ __u16 ctlport;
+ __u16 flags;
+ struct timer_list timer;
+};
+
+/*
+ * Debug level
+ */
+#ifdef CONFIG_IP_MASQ_DEBUG
+static int debug=0;
+MODULE_PARM(debug, "i");
+#endif
+
+/*
+ * Auto-forwarding table
+ */
+
+static struct ip_autofw * ip_autofw_hosts = NULL;
+static struct ip_masq_mod * mmod_self = NULL;
+
+/*
+ * Check if a masq entry should be created for a packet
+ */
+
+static __inline__ struct ip_autofw * ip_autofw_check_range (__u32 where, __u16 port, __u16 protocol, int reqact)
+{
+ struct ip_autofw *af;
+ af=ip_autofw_hosts;
+ port=ntohs(port);
+ while (af) {
+ if (af->type==IP_FWD_RANGE &&
+ port>=af->low &&
+ port<=af->high &&
+ protocol==af->protocol &&
+
+ /*
+ * It's ok to create masq entries after
+ * the timeout if we're in insecure mode
+ */
+ (af->flags & IP_AUTOFW_ACTIVE || !reqact || !(af->flags & IP_AUTOFW_SECURE)) &&
+ (!(af->flags & IP_AUTOFW_SECURE) || af->lastcontact==where || !reqact))
+ return(af);
+ af=af->next;
+ }
+ return(NULL);
+}
+
+static __inline__ struct ip_autofw * ip_autofw_check_port (__u16 port, __u16 protocol)
+{
+ struct ip_autofw *af;
+ af=ip_autofw_hosts;
+ port=ntohs(port);
+ while (af)
+ {
+ if (af->type==IP_FWD_PORT && port==af->visible && protocol==af->protocol)
+ return(af);
+ af=af->next;
+ }
+ return(NULL);
+}
+
+static __inline__ struct ip_autofw * ip_autofw_check_direct (__u16 port, __u16 protocol)
+{
+ struct ip_autofw *af;
+ af=ip_autofw_hosts;
+ port=ntohs(port);
+ while (af)
+ {
+ if (af->type==IP_FWD_DIRECT && af->low<=port && af->high>=port)
+ return(af);
+ af=af->next;
+ }
+ return(NULL);
+}
+
+static __inline__ void ip_autofw_update_out (__u32 who, __u32 where, __u16 port, __u16 protocol)
+{
+ struct ip_autofw *af;
+ af=ip_autofw_hosts;
+ port=ntohs(port);
+ while (af)
+ {
+ if (af->type==IP_FWD_RANGE && af->ctlport==port && af->ctlproto==protocol)
+ {
+ if (af->flags & IP_AUTOFW_USETIME)
+ {
+ mod_timer(&af->timer,
+ jiffies+IP_AUTOFW_EXPIRE);
+ }
+ af->flags|=IP_AUTOFW_ACTIVE;
+ af->lastcontact=where;
+ af->where=who;
+ }
+ af=af->next;
+ }
+}
+
+#if 0
+static __inline__ void ip_autofw_update_in (__u32 where, __u16 port, __u16 protocol)
+{
+ struct ip_autofw *af;
+ af=ip_autofw_check_range(where, port,protocol);
+ if (af)
+ {
+ mod_timer(&af->timer, jiffies+IP_AUTOFW_EXPIRE);
+ }
+}
+#endif
+
+
+static __inline__ void ip_autofw_expire(unsigned long data)
+{
+ struct ip_autofw * af;
+ af=(struct ip_autofw *) data;
+ af->flags &= ~IP_AUTOFW_ACTIVE;
+ af->timer.expires=0;
+ af->lastcontact=0;
+ if (af->flags & IP_AUTOFW_SECURE)
+ af->where=0;
+}
+
+
+
+static __inline__ int ip_autofw_add(struct ip_autofw_user * af)
+{
+ struct ip_autofw * newaf;
+ newaf = kmalloc( sizeof(struct ip_autofw), GFP_KERNEL );
+ if ( newaf == NULL )
+ {
+ printk("ip_autofw_add: malloc said no\n");
+ return( ENOMEM );
+ }
+
+ init_timer(&newaf->timer);
+ MOD_INC_USE_COUNT;
+
+ memcpy(newaf, af, sizeof(struct ip_autofw_user));
+ newaf->timer.data = (unsigned long) newaf;
+ newaf->timer.function = ip_autofw_expire;
+ newaf->timer.expires = 0;
+ newaf->lastcontact=0;
+ newaf->next=ip_autofw_hosts;
+ ip_autofw_hosts=newaf;
+ ip_masq_mod_inc_nent(mmod_self);
+ return(0);
+}
+
+static __inline__ int ip_autofw_del(struct ip_autofw_user * af)
+{
+ struct ip_autofw ** af_p, *curr;
+
+ for (af_p=&ip_autofw_hosts, curr=*af_p; (curr=*af_p); af_p = &(*af_p)->next) {
+ if (af->type == curr->type &&
+ af->low == curr->low &&
+ af->high == curr->high &&
+ af->hidden == curr->hidden &&
+ af->visible == curr->visible &&
+ af->protocol == curr->protocol &&
+ af->where == curr->where &&
+ af->ctlproto == curr->ctlproto &&
+ af->ctlport == curr->ctlport)
+ {
+ ip_masq_mod_dec_nent(mmod_self);
+ *af_p = curr->next;
+ if (af->flags&IP_AUTOFW_ACTIVE)
+ del_timer(&curr->timer);
+ kfree_s(curr,sizeof(struct ip_autofw));
+ MOD_DEC_USE_COUNT;
+ return 0;
+ }
+ curr=curr->next;
+ }
+ return EINVAL;
+}
+
+static __inline__ int ip_autofw_flush(void)
+{
+ struct ip_autofw * af;
+
+ while (ip_autofw_hosts)
+ {
+ af=ip_autofw_hosts;
+ ip_masq_mod_dec_nent(mmod_self);
+ ip_autofw_hosts=ip_autofw_hosts->next;
+ if (af->flags&IP_AUTOFW_ACTIVE)
+ del_timer(&af->timer);
+ kfree_s(af,sizeof(struct ip_autofw));
+ MOD_DEC_USE_COUNT;
+ }
+ return(0);
+}
+
+/*
+ * Methods for registered object
+ */
+
+static int autofw_ctl(int optname, struct ip_masq_ctl *mctl, int optlen)
+{
+ struct ip_autofw_user *af = &mctl->u.autofw_user;
+
+ switch (mctl->m_cmd) {
+ case IP_MASQ_CMD_ADD:
+ case IP_MASQ_CMD_INSERT:
+ if (optlen<sizeof(*af))
+ return EINVAL;
+ return ip_autofw_add(af);
+ case IP_MASQ_CMD_DEL:
+ if (optlen<sizeof(*af))
+ return EINVAL;
+ return ip_autofw_del(af);
+ case IP_MASQ_CMD_FLUSH:
+ return ip_autofw_flush();
+
+ }
+ return EINVAL;
+}
+
+
+static int autofw_out_update(const struct sk_buff *skb, const struct iphdr *iph, struct ip_masq *ms)
+{
+ const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
+ /*
+ * Update any ipautofw entries ...
+ */
+
+ ip_autofw_update_out(iph->saddr, iph->daddr, portp[1], iph->protocol);
+ return IP_MASQ_MOD_NOP;
+}
+
+static struct ip_masq * autofw_out_create(const struct sk_buff *skb, const struct iphdr *iph, __u32 maddr)
+{
+ const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
+ /*
+ * If the source port is supposed to match the masq port, then
+ * make it so
+ */
+
+ if (ip_autofw_check_direct(portp[1],iph->protocol)) {
+ return ip_masq_new(iph->protocol,
+ maddr, portp[0],
+ iph->saddr, portp[0],
+ iph->daddr, portp[1],
+ 0);
+ }
+ return NULL;
+}
+
+#if 0
+static int autofw_in_update(const struct sk_buff *skb, const struct iphdr *iph, __u16 *portp, struct ip_masq *ms)
+{
+ const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
+ ip_autofw_update_in(iph->saddr, portp[1], iph->protocol);
+ return IP_MASQ_MOD_NOP;
+}
+#endif
+
+static int autofw_in_rule(const struct sk_buff *skb, const struct iphdr *iph)
+{
+ const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
+ return (ip_autofw_check_range(iph->saddr, portp[1], iph->protocol, 0)
+ || ip_autofw_check_direct(portp[1], iph->protocol)
+ || ip_autofw_check_port(portp[1], iph->protocol));
+}
+
+static struct ip_masq * autofw_in_create(const struct sk_buff *skb, const struct iphdr *iph, __u32 maddr)
+{
+ const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
+ struct ip_autofw *af;
+
+ if ((af=ip_autofw_check_range(iph->saddr, portp[1], iph->protocol, 0))) {
+ IP_MASQ_DEBUG(1-debug, "autofw_check_range HIT\n");
+ return ip_masq_new(iph->protocol,
+ maddr, portp[1],
+ af->where, portp[1],
+ iph->saddr, portp[0],
+ 0);
+ }
+ if ((af=ip_autofw_check_port(portp[1], iph->protocol)) ) {
+ IP_MASQ_DEBUG(1-debug, "autofw_check_port HIT\n");
+ return ip_masq_new(iph->protocol,
+ maddr, htons(af->visible),
+ af->where, htons(af->hidden),
+ iph->saddr, portp[0],
+ 0);
+ }
+ return NULL;
+}
+
+#ifdef CONFIG_PROC_FS
+static int autofw_procinfo(char *buffer, char **start, off_t offset,
+ int length, int unused)
+{
+ off_t pos=0, begin=0;
+ struct ip_autofw * af;
+ int len=0;
+
+ len=sprintf(buffer,"Type Prot Low High Vis Hid Where Last CPto CPrt Timer Flags\n");
+
+ for(af = ip_autofw_hosts; af ; af = af->next)
+ {
+ len+=sprintf(buffer+len,"%4X %4X %04X-%04X/%04X %04X %08lX %08lX %04X %04X %6lu %4X\n",
+ af->type,
+ af->protocol,
+ af->low,
+ af->high,
+ af->visible,
+ af->hidden,
+ ntohl(af->where),
+ ntohl(af->lastcontact),
+ af->ctlproto,
+ af->ctlport,
+ (af->timer.expires<jiffies ? 0 : af->timer.expires-jiffies),
+ af->flags);
+
+ pos=begin+len;
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ break;
+ }
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ return len;
+}
+
+static struct proc_dir_entry autofw_proc_entry = {
+ 0, 0, NULL,
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ autofw_procinfo
+};
+
+#define proc_ent &autofw_proc_entry
+#else /* !CONFIG_PROC_FS */
+
+#define proc_ent NULL
+#endif
+
+
+#define autofw_in_update NULL
+#define autofw_out_rule NULL
+#define autofw_mod_init NULL
+#define autofw_mod_done NULL
+
+static struct ip_masq_mod autofw_mod = {
+ NULL, /* next */
+ NULL, /* next_reg */
+ "autofw", /* name */
+ ATOMIC_INIT(0), /* nent */
+ ATOMIC_INIT(0), /* refcnt */
+ proc_ent,
+ autofw_ctl,
+ autofw_mod_init,
+ autofw_mod_done,
+ autofw_in_rule,
+ autofw_in_update,
+ autofw_in_create,
+ autofw_out_rule,
+ autofw_out_update,
+ autofw_out_create,
+};
+
+__initfunc(int ip_autofw_init(void))
+{
+ return register_ip_masq_mod ((mmod_self=&autofw_mod));
+}
+
+int ip_autofw_done(void)
+{
+ return unregister_ip_masq_mod(&autofw_mod);
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+ if (ip_autofw_init() != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if (ip_autofw_done() != 0)
+ printk(KERN_INFO "ip_autofw_done(): can't remove module");
+}
+
+#endif /* MODULE */
diff --git a/pfinet/linux-src/net/ipv4/ip_masq_cuseeme.c b/pfinet/linux-src/net/ipv4/ip_masq_cuseeme.c
new file mode 100644
index 00000000..9b412baf
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_masq_cuseeme.c
@@ -0,0 +1,264 @@
+/*
+ * IP_MASQ_FTP CUSeeMe masquerading module
+ *
+ *
+ * Version: @(#)$Id: ip_masq_cuseeme.c,v 1.4 1998/10/06 04:48:57 davem Exp $
+ *
+ * Author: Richard Lynch
+ *
+ *
+ * Fixes:
+ * Richard Lynch : Updated patch to conform to new module
+ * specifications
+ * Nigel Metheringham : Multiple port support
+ * Michael Owings : Fixed broken init code
+ * Added code to update inbound
+ * packets with correct local addresses.
+ * Fixes audio and "chat" problems
+ * Thanx to the CU-SeeMe Consortium for
+ * technical docs
+ * Steven Clarke : Small changes for 2.1
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Multiple Port Support
+ * The helper can be made to handle up to MAX_MASQ_APP_PORTS (normally 12)
+ * with the port numbers being defined at module load time. The module
+ * uses the symbol "ports" to define a list of monitored ports, which can
+ * be specified on the insmod command line as
+ * ports=x1,x2,x3...
+ * where x[n] are integer port numbers. This option can be put into
+ * /etc/conf.modules (or /etc/modules.conf depending on your config)
+ * where modload will pick it up should you use modload to load your
+ * modules.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/init.h>
+#include <net/protocol.h>
+#include <net/udp.h>
+
+/* #define IP_MASQ_NDEBUG */
+#include <net/ip_masq.h>
+
+#pragma pack(1)
+/* CU-SeeMe Data Header */
+typedef struct {
+ u_short dest_family;
+ u_short dest_port;
+ u_long dest_addr;
+ short family;
+ u_short port;
+ u_long addr;
+ u_long seq;
+ u_short msg;
+ u_short data_type;
+ u_short packet_len;
+} cu_header;
+
+/* Open Continue Header */
+typedef struct {
+ cu_header cu_head;
+ u_short client_count; /* Number of client info structs */
+ u_long seq_no;
+ char user_name[20];
+ char stuff[4]; /* flags, version stuff, etc */
+}oc_header;
+
+/* client info structures */
+typedef struct {
+ u_long address; /* Client address */
+ char stuff[8]; /* Flags, pruning bitfield, packet counts etc */
+} client_info;
+#pragma pack()
+
+/*
+ * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper
+ * First port is set to the default port.
+ */
+static int ports[MAX_MASQ_APP_PORTS] = {7648}; /* I rely on the trailing items being set to zero */
+struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS];
+
+/*
+ * Debug level
+ */
+#ifdef CONFIG_IP_MASQ_DEBUG
+static int debug=0;
+MODULE_PARM(debug, "i");
+#endif
+
+MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i");
+
+static int
+masq_cuseeme_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+masq_cuseeme_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+int
+masq_cuseeme_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+ struct sk_buff *skb = *skb_p;
+ struct iphdr *iph = skb->nh.iph;
+ struct udphdr *uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]);
+ cu_header *cu_head;
+ char *data=(char *)&uh[1];
+
+ if (skb->len - ((unsigned char *) data - skb->h.raw) >= sizeof(cu_header))
+ {
+ cu_head = (cu_header *) data;
+ /* cu_head->port = ms->mport; */
+ if( cu_head->addr )
+ cu_head->addr = (u_long) maddr;
+ if(ntohs(cu_head->data_type) == 257)
+ IP_MASQ_DEBUG(1-debug, "Sending talk packet!\n");
+ }
+ return 0;
+}
+
+int
+masq_cuseeme_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+ struct sk_buff *skb = *skb_p;
+ struct iphdr *iph = skb->nh.iph;
+ struct udphdr *uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]);
+ cu_header *cu_head;
+ oc_header *oc;
+ client_info *ci;
+ char *data=(char *)&uh[1];
+ u_short len = skb->len - ((unsigned char *) data - skb->h.raw);
+ int i, off;
+
+ if (len >= sizeof(cu_header))
+ {
+ cu_head = (cu_header *) data;
+ if(cu_head->dest_addr) /* Correct destination address */
+ cu_head->dest_addr = (u_long) ms->saddr;
+ if(ntohs(cu_head->data_type)==101 && len > sizeof(oc_header))
+ {
+ oc = (oc_header * ) data;
+ /* Spin (grovel) thru client_info structs till we find our own */
+ off=sizeof(oc_header);
+ for(i=0;
+ (i < oc->client_count && off+sizeof(client_info) <= len);
+ i++)
+ {
+ ci=(client_info *)(data+off);
+ if(ci->address==(u_long) maddr)
+ {
+ /* Update w/ our real ip address and exit */
+ ci->address = (u_long) ms->saddr;
+ break;
+ }
+ else
+ off+=sizeof(client_info);
+ }
+ }
+ }
+ return 0;
+}
+
+struct ip_masq_app ip_masq_cuseeme = {
+ NULL, /* next */
+ "cuseeme",
+ 0, /* type */
+ 0, /* n_attach */
+ masq_cuseeme_init_1, /* ip_masq_init_1 */
+ masq_cuseeme_done_1, /* ip_masq_done_1 */
+ masq_cuseeme_out, /* pkt_out */
+ masq_cuseeme_in /* pkt_in */
+};
+
+
+/*
+ * ip_masq_cuseeme initialization
+ */
+
+__initfunc(int ip_masq_cuseeme_init(void))
+{
+ int i, j;
+
+ for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+ if (ports[i]) {
+ if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app),
+ GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ memcpy(masq_incarnations[i], &ip_masq_cuseeme, sizeof(struct ip_masq_app));
+ if ((j = register_ip_masq_app(masq_incarnations[i],
+ IPPROTO_UDP,
+ ports[i]))) {
+ return j;
+ }
+#if DEBUG_CONFIG_IP_MASQ_CUSEEME
+ IP_MASQ_DEBUG(1-debug, "CuSeeMe: loaded support on port[%d] = %d\n",
+ i, ports[i]);
+#endif
+ } else {
+ /* To be safe, force the incarnation table entry to NULL */
+ masq_incarnations[i] = NULL;
+ }
+ }
+ return 0;
+}
+
+/*
+ * ip_masq_cuseeme fin.
+ */
+
+int ip_masq_cuseeme_done(void)
+{
+ int i, j, k;
+
+ k=0;
+ for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+ if (masq_incarnations[i]) {
+ if ((j = unregister_ip_masq_app(masq_incarnations[i]))) {
+ k = j;
+ } else {
+ kfree(masq_incarnations[i]);
+ masq_incarnations[i] = NULL;
+ IP_MASQ_DEBUG(1-debug, "CuSeeMe: unloaded support on port[%d] = %d\n", i, ports[i]);
+ }
+ }
+ }
+ return k;
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+ if (ip_masq_cuseeme_init() != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if (ip_masq_cuseeme_done() != 0)
+ IP_MASQ_DEBUG(1-debug, "ip_masq_cuseeme: can't remove module");
+}
+
+#endif /* MODULE */
diff --git a/pfinet/linux-src/net/ipv4/ip_masq_ftp.c b/pfinet/linux-src/net/ipv4/ip_masq_ftp.c
new file mode 100644
index 00000000..35d1f544
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_masq_ftp.c
@@ -0,0 +1,393 @@
+/*
+ * IP_MASQ_FTP ftp masquerading module
+ *
+ *
+ * Version: @(#)ip_masq_ftp.c 0.04 02/05/96
+ *
+ * Author: Wouter Gadeyne
+ *
+ *
+ * Fixes:
+ * Wouter Gadeyne : Fixed masquerading support of ftp PORT commands
+ * Juan Jose Ciarlante : Code moved and adapted from ip_fw.c
+ * Keith Owens : Add keep alive for ftp control channel
+ * Nigel Metheringham : Added multiple port support
+ * Juan Jose Ciarlante : Use control_add() for ftp control chan
+ * Juan Jose Ciarlante : Litl bits for 2.1
+ * Juan Jose Ciarlante : use ip_masq_listen()
+ * Juan Jose Ciarlante : use private app_data for own flag(s)
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Multiple Port Support
+ * The helper can be made to handle up to MAX_MASQ_APP_PORTS (normally 12)
+ * with the port numbers being defined at module load time. The module
+ * uses the symbol "ports" to define a list of monitored ports, which can
+ * be specified on the insmod command line as
+ * ports=x1,x2,x3...
+ * where x[n] are integer port numbers. This option can be put into
+ * /etc/conf.modules (or /etc/modules.conf depending on your config)
+ * where modload will pick it up should you use modload to load your
+ * modules.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/init.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+
+/* #define IP_MASQ_NDEBUG */
+#include <net/ip_masq.h>
+
+
+/*
+ * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper
+ * First port is set to the default port.
+ */
+static int ports[MAX_MASQ_APP_PORTS] = {21}; /* I rely on the trailing items being set to zero */
+struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS];
+
+/*
+ * Debug level
+ */
+#ifdef CONFIG_IP_MASQ_DEBUG
+static int debug=0;
+MODULE_PARM(debug, "i");
+#endif
+
+MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i");
+
+/* Dummy variable */
+static int masq_ftp_pasv;
+
+static int
+masq_ftp_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+masq_ftp_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+int
+masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ struct tcphdr *th;
+ char *p, *data, *data_limit;
+ unsigned char p1,p2,p3,p4,p5,p6;
+ __u32 from;
+ __u16 port;
+ struct ip_masq *n_ms;
+ char buf[24]; /* xxx.xxx.xxx.xxx,ppp,ppp\000 */
+ unsigned buf_len;
+ int diff;
+
+ skb = *skb_p;
+ iph = skb->nh.iph;
+ th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
+ data = (char *)&th[1];
+
+ data_limit = skb->h.raw + skb->len - 18;
+ if (skb->len >= 6 && (memcmp(data, "PASV\r\n", 6) == 0 || memcmp(data, "pasv\r\n", 6) == 0))
+ ms->app_data = &masq_ftp_pasv;
+
+ while (data < data_limit)
+ {
+ if (memcmp(data,"PORT ",5) && memcmp(data,"port ",5))
+ {
+ data ++;
+ continue;
+ }
+ p = data+5;
+ p1 = simple_strtoul(data+5,&data,10);
+ if (*data!=',')
+ continue;
+ p2 = simple_strtoul(data+1,&data,10);
+ if (*data!=',')
+ continue;
+ p3 = simple_strtoul(data+1,&data,10);
+ if (*data!=',')
+ continue;
+ p4 = simple_strtoul(data+1,&data,10);
+ if (*data!=',')
+ continue;
+ p5 = simple_strtoul(data+1,&data,10);
+ if (*data!=',')
+ continue;
+ p6 = simple_strtoul(data+1,&data,10);
+ if (*data!='\r' && *data!='\n')
+ continue;
+
+ from = (p1<<24) | (p2<<16) | (p3<<8) | p4;
+ port = (p5<<8) | p6;
+
+ IP_MASQ_DEBUG(1-debug, "PORT %X:%X detected\n",from,port);
+
+ /*
+ * Now update or create an masquerade entry for it
+ */
+
+ IP_MASQ_DEBUG(1-debug, "protocol %d %lX:%X %X:%X\n", iph->protocol, htonl(from), htons(port), iph->daddr, 0);
+
+ n_ms = ip_masq_out_get(iph->protocol,
+ htonl(from), htons(port),
+ iph->daddr, 0);
+ if (!n_ms) {
+ n_ms = ip_masq_new(IPPROTO_TCP,
+ maddr, 0,
+ htonl(from), htons(port),
+ iph->daddr, 0,
+ IP_MASQ_F_NO_DPORT);
+
+ if (n_ms==NULL)
+ return 0;
+ ip_masq_control_add(n_ms, ms);
+ }
+
+ /*
+ * Replace the old PORT with the new one
+ */
+ from = ntohl(n_ms->maddr);
+ port = ntohs(n_ms->mport);
+ sprintf(buf,"%d,%d,%d,%d,%d,%d",
+ from>>24&255,from>>16&255,from>>8&255,from&255,
+ port>>8&255,port&255);
+ buf_len = strlen(buf);
+
+ IP_MASQ_DEBUG(1-debug, "new PORT %X:%X\n",from,port);
+
+ /*
+ * Calculate required delta-offset to keep TCP happy
+ */
+
+ diff = buf_len - (data-p);
+
+ /*
+ * No shift.
+ */
+
+ if (diff==0) {
+ /*
+ * simple case, just replace the old PORT cmd
+ */
+ memcpy(p,buf,buf_len);
+ } else {
+
+ *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, p, data-p, buf, buf_len);
+ }
+ /*
+ * Move tunnel to listen state
+ */
+ ip_masq_listen(n_ms);
+ ip_masq_put(n_ms);
+
+ return diff;
+
+ }
+ return 0;
+
+}
+
+/*
+ * Look at incoming ftp packets to catch the response to a PASV command. When
+ * we see one we build a masquerading entry for the client address, client port
+ * 0 (unknown at the moment), the server address and the server port. Mark the
+ * current masquerade entry as a control channel and point the new entry at the
+ * control entry. All this work just for ftp keepalive across masquerading.
+ *
+ * The incoming packet should be something like
+ * "227 Entering Passive Mode (xxx,xxx,xxx,xxx,ppp,ppp)".
+ * xxx,xxx,xxx,xxx is the server address, ppp,ppp is the server port number.
+ * ncftp 2.3.0 cheats by skipping the leading number then going 22 bytes into
+ * the data so we do the same. If it's good enough for ncftp then it's good
+ * enough for me.
+ *
+ * In this case, the client is the source machine being masqueraded, the server
+ * is the destination for ftp requests. It all depends on your point of view ...
+ */
+
+int
+masq_ftp_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ struct tcphdr *th;
+ char *data, *data_limit;
+ unsigned char p1,p2,p3,p4,p5,p6;
+ __u32 to;
+ __u16 port;
+ struct ip_masq *n_ms;
+
+ if (ms->app_data != &masq_ftp_pasv)
+ return 0; /* quick exit if no outstanding PASV */
+
+ skb = *skb_p;
+ iph = skb->nh.iph;
+ th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
+ data = (char *)&th[1];
+ data_limit = skb->h.raw + skb->len;
+
+ while (data < data_limit && *data != ' ')
+ ++data;
+ while (data < data_limit && *data == ' ')
+ ++data;
+ data += 22;
+ if (data >= data_limit || *data != '(')
+ return 0;
+ p1 = simple_strtoul(data+1, &data, 10);
+ if (data >= data_limit || *data != ',')
+ return 0;
+ p2 = simple_strtoul(data+1, &data, 10);
+ if (data >= data_limit || *data != ',')
+ return 0;
+ p3 = simple_strtoul(data+1, &data, 10);
+ if (data >= data_limit || *data != ',')
+ return 0;
+ p4 = simple_strtoul(data+1, &data, 10);
+ if (data >= data_limit || *data != ',')
+ return 0;
+ p5 = simple_strtoul(data+1, &data, 10);
+ if (data >= data_limit || *data != ',')
+ return 0;
+ p6 = simple_strtoul(data+1, &data, 10);
+ if (data >= data_limit || *data != ')')
+ return 0;
+
+ to = (p1<<24) | (p2<<16) | (p3<<8) | p4;
+ port = (p5<<8) | p6;
+
+ /*
+ * Now update or create an masquerade entry for it
+ */
+ IP_MASQ_DEBUG(1-debug, "PASV response %lX:%X %X:%X detected\n", ntohl(ms->saddr), 0, to, port);
+
+ n_ms = ip_masq_out_get(iph->protocol,
+ ms->saddr, 0,
+ htonl(to), htons(port));
+ if (!n_ms) {
+ n_ms = ip_masq_new(IPPROTO_TCP,
+ maddr, 0,
+ ms->saddr, 0,
+ htonl(to), htons(port),
+ IP_MASQ_F_NO_SPORT);
+
+ if (n_ms==NULL)
+ return 0;
+ ip_masq_control_add(n_ms, ms);
+ }
+
+#if 0 /* v0.12 state processing */
+
+ /*
+ * keep for a bit longer than tcp_fin, client may not issue open
+ * to server port before tcp_fin_timeout.
+ */
+ n_ms->timeout = ip_masq_expire->tcp_fin_timeout*3;
+#endif
+ ms->app_data = NULL;
+ ip_masq_put(n_ms);
+
+ return 0; /* no diff required for incoming packets, thank goodness */
+}
+
+struct ip_masq_app ip_masq_ftp = {
+ NULL, /* next */
+ "ftp", /* name */
+ 0, /* type */
+ 0, /* n_attach */
+ masq_ftp_init_1, /* ip_masq_init_1 */
+ masq_ftp_done_1, /* ip_masq_done_1 */
+ masq_ftp_out, /* pkt_out */
+ masq_ftp_in, /* pkt_in */
+};
+
+/*
+ * ip_masq_ftp initialization
+ */
+
+__initfunc(int ip_masq_ftp_init(void))
+{
+ int i, j;
+
+ for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+ if (ports[i]) {
+ if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app),
+ GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ memcpy(masq_incarnations[i], &ip_masq_ftp, sizeof(struct ip_masq_app));
+ if ((j = register_ip_masq_app(masq_incarnations[i],
+ IPPROTO_TCP,
+ ports[i]))) {
+ return j;
+ }
+ IP_MASQ_DEBUG(1-debug, "Ftp: loaded support on port[%d] = %d\n",
+ i, ports[i]);
+ } else {
+ /* To be safe, force the incarnation table entry to NULL */
+ masq_incarnations[i] = NULL;
+ }
+ }
+ return 0;
+}
+
+/*
+ * ip_masq_ftp fin.
+ */
+
+int ip_masq_ftp_done(void)
+{
+ int i, j, k;
+
+ k=0;
+ for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+ if (masq_incarnations[i]) {
+ if ((j = unregister_ip_masq_app(masq_incarnations[i]))) {
+ k = j;
+ } else {
+ kfree(masq_incarnations[i]);
+ masq_incarnations[i] = NULL;
+ IP_MASQ_DEBUG(1-debug, "Ftp: unloaded support on port[%d] = %d\n",
+ i, ports[i]);
+ }
+ }
+ }
+ return k;
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+ if (ip_masq_ftp_init() != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if (ip_masq_ftp_done() != 0)
+ printk(KERN_INFO "ip_masq_ftp: can't remove module");
+}
+
+#endif /* MODULE */
diff --git a/pfinet/linux-src/net/ipv4/ip_masq_irc.c b/pfinet/linux-src/net/ipv4/ip_masq_irc.c
new file mode 100644
index 00000000..e52a5720
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_masq_irc.c
@@ -0,0 +1,345 @@
+/*
+ * IP_MASQ_IRC irc masquerading module
+ *
+ *
+ * Version: @(#)ip_masq_irc.c 0.04 99/06/19
+ *
+ * Author: Juan Jose Ciarlante
+ *
+ * Additions:
+ * - recognize a few non-irc-II DCC requests (Oliver Wagner)
+ * DCC MOVE (AmIRC/DCC.MOVE; SEND with resuming)
+ * DCC SCHAT (AmIRC IDEA encrypted CHAT)
+ * DCC TSEND (AmIRC/PIRCH SEND without ACKs)
+ * Fixes:
+ * Juan Jose Ciarlante : set NO_DADDR flag in ip_masq_new()
+ * Nigel Metheringham : Added multiple port support
+ * Juan Jose Ciarlante : litl bits for 2.1
+ * Oliver Wagner : more IRC cmds processing
+ * <winmute@lucifer.gv.kotnet.org>
+ * Juan Jose Ciarlante : put new ms entry to listen()
+ * Scottie Shore : added support for clients that add extra args
+ * <sshore@escape.ca>
+ *
+ * FIXME:
+ * - detect also previous "PRIVMSG" string ?.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Multiple Port Support
+ * The helper can be made to handle up to MAX_MASQ_APP_PORTS (normally 12)
+ * with the port numbers being defined at module load time. The module
+ * uses the symbol "ports" to define a list of monitored ports, which can
+ * be specified on the insmod command line as
+ * ports=x1,x2,x3...
+ * where x[n] are integer port numbers. This option can be put into
+ * /etc/conf.modules (or /etc/modules.conf depending on your config)
+ * where modload will pick it up should you use modload to load your
+ * modules.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/system.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/init.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+#include <net/ip_masq.h>
+
+
+/*
+ * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper
+ * First port is set to the default port.
+ */
+int ports[MAX_MASQ_APP_PORTS] = {6667}; /* I rely on the trailing items being set to zero */
+struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS];
+/*
+ * Debug level
+ */
+#ifdef CONFIG_IP_MASQ_DEBUG
+static int debug=0;
+MODULE_PARM(debug, "i");
+#endif
+
+MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i");
+
+
+/*
+ * List of supported DCC protocols
+ */
+
+#define NUM_DCCPROTO 5
+
+struct dccproto
+{
+ char *match;
+ int matchlen;
+};
+
+struct dccproto dccprotos[NUM_DCCPROTO] = {
+ { "SEND ", 5 },
+ { "CHAT ", 5 },
+ { "MOVE ", 5 },
+ { "TSEND ", 6 },
+ { "SCHAT ", 6 }
+};
+#define MAXMATCHLEN 6
+
+static int
+masq_irc_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+masq_irc_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+int
+masq_irc_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ struct tcphdr *th;
+ char *data, *data_limit;
+ __u32 s_addr;
+ __u16 s_port;
+ struct ip_masq *n_ms;
+ char buf[20]; /* "m_addr m_port" (dec base)*/
+ unsigned buf_len;
+ int diff;
+ char *dcc_p, *addr_beg_p, *addr_end_p;
+
+ skb = *skb_p;
+ iph = skb->nh.iph;
+ th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
+ data = (char *)&th[1];
+
+ /*
+ * Hunt irc DCC string, the _shortest_:
+ *
+ * strlen("\1DCC CHAT chat AAAAAAAA P\1\n")=27
+ * strlen("\1DCC SCHAT chat AAAAAAAA P\1\n")=28
+ * strlen("\1DCC SEND F AAAAAAAA P S\1\n")=26
+ * strlen("\1DCC MOVE F AAAAAAAA P S\1\n")=26
+ * strlen("\1DCC TSEND F AAAAAAAA P S\1\n")=27
+ * AAAAAAAAA: bound addr (1.0.0.0==16777216, min 8 digits)
+ * P: bound port (min 1 d )
+ * F: filename (min 1 d )
+ * S: size (min 1 d )
+ * 0x01, \n: terminators
+ */
+
+ data_limit = skb->h.raw + skb->len;
+
+ while (data < (data_limit - ( 22 + MAXMATCHLEN ) ) )
+ {
+ int i;
+ if (memcmp(data,"\1DCC ",5)) {
+ data ++;
+ continue;
+ }
+
+ dcc_p = data;
+ data += 5; /* point to DCC cmd */
+
+ for(i=0; i<NUM_DCCPROTO; i++)
+ {
+ /*
+ * go through the table and hunt a match string
+ */
+
+ if( memcmp(data, dccprotos[i].match, dccprotos[i].matchlen ) == 0 )
+ {
+ data += dccprotos[i].matchlen;
+
+ /*
+ * skip next string.
+ */
+
+ while( *data++ != ' ')
+
+ /*
+ * must still parse, at least, "AAAAAAAA P\1\n",
+ * 12 bytes left.
+ */
+ if (data > (data_limit-12)) return 0;
+
+
+ addr_beg_p = data;
+
+ /*
+ * client bound address in dec base
+ */
+
+ s_addr = simple_strtoul(data,&data,10);
+ if (*data++ !=' ')
+ continue;
+
+ /*
+ * client bound port in dec base
+ */
+
+ s_port = simple_strtoul(data,&data,10);
+ addr_end_p = data;
+
+ /*
+ * Now create an masquerade entry for it
+ * must set NO_DPORT and NO_DADDR because
+ * connection is requested by another client.
+ */
+
+ n_ms = ip_masq_new(IPPROTO_TCP,
+ maddr, 0,
+ htonl(s_addr),htons(s_port),
+ 0, 0,
+ IP_MASQ_F_NO_DPORT|IP_MASQ_F_NO_DADDR);
+ if (n_ms==NULL)
+ return 0;
+
+ /*
+ * Replace the old "address port" with the new one
+ */
+
+ buf_len = sprintf(buf,"%lu %u",
+ ntohl(n_ms->maddr),ntohs(n_ms->mport));
+
+ /*
+ * Calculate required delta-offset to keep TCP happy
+ */
+
+ diff = buf_len - (addr_end_p-addr_beg_p);
+
+ *addr_beg_p = '\0';
+ IP_MASQ_DEBUG(1-debug, "masq_irc_out(): '%s' %X:%X detected (diff=%d)\n", dcc_p, s_addr,s_port, diff);
+
+ /*
+ * No shift.
+ */
+
+ if (diff==0) {
+ /*
+ * simple case, just copy.
+ */
+ memcpy(addr_beg_p,buf,buf_len);
+ } else {
+
+ *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC,
+ addr_beg_p, addr_end_p-addr_beg_p,
+ buf, buf_len);
+ }
+ ip_masq_listen(n_ms);
+ ip_masq_put(n_ms);
+ return diff;
+ }
+ }
+ }
+ return 0;
+
+}
+
+/*
+ * Main irc object
+ * You need 1 object per port in case you need
+ * to offer also other used irc ports (6665,6666,etc),
+ * they will share methods but they need own space for
+ * data.
+ */
+
+struct ip_masq_app ip_masq_irc = {
+ NULL, /* next */
+ "irc", /* name */
+ 0, /* type */
+ 0, /* n_attach */
+ masq_irc_init_1, /* init_1 */
+ masq_irc_done_1, /* done_1 */
+ masq_irc_out, /* pkt_out */
+ NULL /* pkt_in */
+};
+
+/*
+ * ip_masq_irc initialization
+ */
+
+__initfunc(int ip_masq_irc_init(void))
+{
+ int i, j;
+
+ for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+ if (ports[i]) {
+ if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app),
+ GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ memcpy(masq_incarnations[i], &ip_masq_irc, sizeof(struct ip_masq_app));
+ if ((j = register_ip_masq_app(masq_incarnations[i],
+ IPPROTO_TCP,
+ ports[i]))) {
+ return j;
+ }
+ IP_MASQ_DEBUG(1-debug,
+ "Irc: loaded support on port[%d] = %d\n",
+ i, ports[i]);
+ } else {
+ /* To be safe, force the incarnation table entry to NULL */
+ masq_incarnations[i] = NULL;
+ }
+ }
+ return 0;
+}
+
+/*
+ * ip_masq_irc fin.
+ */
+
+int ip_masq_irc_done(void)
+{
+ int i, j, k;
+
+ k=0;
+ for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+ if (masq_incarnations[i]) {
+ if ((j = unregister_ip_masq_app(masq_incarnations[i]))) {
+ k = j;
+ } else {
+ kfree(masq_incarnations[i]);
+ masq_incarnations[i] = NULL;
+ IP_MASQ_DEBUG(1-debug, "Irc: unloaded support on port[%d] = %d\n",
+ i, ports[i]);
+ }
+ }
+ }
+ return k;
+}
+
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+ if (ip_masq_irc_init() != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if (ip_masq_irc_done() != 0)
+ printk(KERN_INFO "ip_masq_irc: can't remove module");
+}
+
+#endif /* MODULE */
diff --git a/pfinet/linux-src/net/ipv4/ip_masq_mfw.c b/pfinet/linux-src/net/ipv4/ip_masq_mfw.c
new file mode 100644
index 00000000..d28f610a
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_masq_mfw.c
@@ -0,0 +1,770 @@
+/*
+ * IP_MASQ_MARKFW masquerading module
+ *
+ * Does (reverse-masq) forwarding based on skb->fwmark value
+ *
+ * $Id: ip_masq_mfw.c,v 1.3.2.3 1999/09/22 16:33:26 davem Exp $
+ *
+ * Author: Juan Jose Ciarlante <jjciarla@raiz.uncu.edu.ar>
+ * based on Steven Clarke's portfw
+ *
+ * Fixes:
+ * JuanJo Ciarlante: added u-space sched support
+ * JuanJo Ciarlante: if rport==0, use packet dest port *grin*
+ * JuanJo Ciarlante: fixed tcp syn&&!ack creation
+ *
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <net/ip.h>
+#include <linux/ip_fw.h>
+#include <linux/ip_masq.h>
+#include <net/ip_masq.h>
+#include <net/ip_masq_mod.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <asm/softirq.h>
+#include <asm/spinlock.h>
+#include <asm/atomic.h>
+
+static struct ip_masq_mod *mmod_self = NULL;
+#ifdef CONFIG_IP_MASQ_DEBUG
+static int debug=0;
+MODULE_PARM(debug, "i");
+#endif
+
+/*
+ * Lists structure:
+ * There is a "main" linked list with entries hashed
+ * by fwmark value (struct ip_masq_mfw, the "m-entries").
+ *
+ * Each of this m-entry holds a double linked list
+ * of "forward-to" hosts (struct ip_masq_mfw_host, the "m.host"),
+ * the round-robin scheduling takes place by rotating m.host entries
+ * "inside" its m-entry.
+ */
+
+/*
+ * Each forwarded host (addr:port) is stored here
+ */
+struct ip_masq_mfw_host {
+ struct list_head list;
+ __u32 addr;
+ __u16 port;
+ __u16 pad0;
+ __u32 fwmark;
+ int pref;
+ atomic_t pref_cnt;
+};
+
+#define IP_MASQ_MFW_HSIZE 16
+/*
+ * This entries are indexed by fwmark,
+ * they hold a list of forwarded addr:port
+ */
+
+struct ip_masq_mfw {
+ struct ip_masq_mfw *next; /* linked list */
+ __u32 fwmark; /* key: firewall mark */
+ struct list_head hosts; /* list of forward-to hosts */
+ atomic_t nhosts; /* number of "" */
+ rwlock_t lock;
+};
+
+
+static struct semaphore mfw_sema = MUTEX;
+static rwlock_t mfw_lock = RW_LOCK_UNLOCKED;
+
+static struct ip_masq_mfw *ip_masq_mfw_table[IP_MASQ_MFW_HSIZE];
+
+static __inline__ int mfw_hash_val(int fwmark)
+{
+ return fwmark & 0x0f;
+}
+
+/*
+ * Get m-entry by "fwmark"
+ * Caller must lock tables.
+ */
+
+static struct ip_masq_mfw *__mfw_get(int fwmark)
+{
+ struct ip_masq_mfw* mfw;
+ int hash = mfw_hash_val(fwmark);
+
+ for (mfw=ip_masq_mfw_table[hash];mfw;mfw=mfw->next) {
+ if (mfw->fwmark==fwmark) {
+ goto out;
+ }
+ }
+out:
+ return mfw;
+}
+
+/*
+ * Links m-entry.
+ * Caller should have checked if already present for same fwmark
+ *
+ * Caller must lock tables.
+ */
+static int __mfw_add(struct ip_masq_mfw *mfw)
+{
+ int fwmark = mfw->fwmark;
+ int hash = mfw_hash_val(fwmark);
+
+ mfw->next = ip_masq_mfw_table[hash];
+ ip_masq_mfw_table[hash] = mfw;
+ ip_masq_mod_inc_nent(mmod_self);
+
+ return 0;
+}
+
+/*
+ * Creates a m-entry (doesn't link it)
+ */
+
+static struct ip_masq_mfw * mfw_new(int fwmark)
+{
+ struct ip_masq_mfw *mfw;
+
+ mfw = kmalloc(sizeof(*mfw), GFP_KERNEL);
+ if (mfw == NULL)
+ goto out;
+
+ MOD_INC_USE_COUNT;
+ memset(mfw, 0, sizeof(*mfw));
+ mfw->fwmark = fwmark;
+ mfw->lock = RW_LOCK_UNLOCKED;
+
+ INIT_LIST_HEAD(&mfw->hosts);
+out:
+ return mfw;
+}
+
+static void mfw_host_to_user(struct ip_masq_mfw_host *h, struct ip_mfw_user *mu)
+{
+ mu->raddr = h->addr;
+ mu->rport = h->port;
+ mu->fwmark = h->fwmark;
+ mu->pref = h->pref;
+}
+
+/*
+ * Creates a m.host (doesn't link it in a m-entry)
+ */
+static struct ip_masq_mfw_host * mfw_host_new(struct ip_mfw_user *mu)
+{
+ struct ip_masq_mfw_host * mfw_host;
+ mfw_host = kmalloc(sizeof (*mfw_host), GFP_KERNEL);
+ if (!mfw_host)
+ return NULL;
+
+ MOD_INC_USE_COUNT;
+ memset(mfw_host, 0, sizeof(*mfw_host));
+ mfw_host->addr = mu->raddr;
+ mfw_host->port = mu->rport;
+ mfw_host->fwmark = mu->fwmark;
+ mfw_host->pref = mu->pref;
+ atomic_set(&mfw_host->pref_cnt, mu->pref);
+
+ return mfw_host;
+}
+
+/*
+ * Create AND link m.host to m-entry.
+ * It locks m.lock.
+ */
+static int mfw_addhost(struct ip_masq_mfw *mfw, struct ip_mfw_user *mu, int attail)
+{
+ struct ip_masq_mfw_host *mfw_host;
+
+ mfw_host = mfw_host_new(mu);
+ if (!mfw_host)
+ return -ENOMEM;
+
+ write_lock_bh(&mfw->lock);
+ list_add(&mfw_host->list, attail? mfw->hosts.prev : &mfw->hosts);
+ atomic_inc(&mfw->nhosts);
+ write_unlock_bh(&mfw->lock);
+
+ return 0;
+}
+
+/*
+ * Unlink AND destroy m.host(s) from m-entry.
+ * Wildcard (nul host or addr) ok.
+ * It uses m.lock.
+ */
+static int mfw_delhost(struct ip_masq_mfw *mfw, struct ip_mfw_user *mu)
+{
+
+ struct list_head *l,*e;
+ struct ip_masq_mfw_host *h;
+ int n_del = 0;
+ l = &mfw->hosts;
+
+ write_lock_bh(&mfw->lock);
+ for (e=l->next; e!=l; e=e->next)
+ {
+ h = list_entry(e, struct ip_masq_mfw_host, list);
+ if ((!mu->raddr || h->addr == mu->raddr) &&
+ (!mu->rport || h->port == mu->rport)) {
+ /* HIT */
+ atomic_dec(&mfw->nhosts);
+ e = h->list.prev;
+ list_del(&h->list);
+ kfree_s(h, sizeof(*h));
+ MOD_DEC_USE_COUNT;
+ n_del++;
+ }
+
+ }
+ write_unlock_bh(&mfw->lock);
+ return n_del? 0 : -ESRCH;
+}
+
+/*
+ * Changes m.host parameters
+ * Wildcards ok
+ *
+ * Caller must lock tables.
+ */
+static int __mfw_edithost(struct ip_masq_mfw *mfw, struct ip_mfw_user *mu)
+{
+
+ struct list_head *l,*e;
+ struct ip_masq_mfw_host *h;
+ int n_edit = 0;
+ l = &mfw->hosts;
+
+ for (e=l->next; e!=l; e=e->next)
+ {
+ h = list_entry(e, struct ip_masq_mfw_host, list);
+ if ((!mu->raddr || h->addr == mu->raddr) &&
+ (!mu->rport || h->port == mu->rport)) {
+ /* HIT */
+ h->pref = mu->pref;
+ atomic_set(&h->pref_cnt, mu->pref);
+ n_edit++;
+ }
+
+ }
+ return n_edit? 0 : -ESRCH;
+}
+
+/*
+ * Destroys m-entry.
+ * Caller must have checked that it doesn't hold any m.host(s)
+ */
+static void mfw_destroy(struct ip_masq_mfw *mfw)
+{
+ kfree_s(mfw, sizeof(*mfw));
+ MOD_DEC_USE_COUNT;
+}
+
+/*
+ * Unlink m-entry.
+ *
+ * Caller must lock tables.
+ */
+static int __mfw_del(struct ip_masq_mfw *mfw)
+{
+ struct ip_masq_mfw **mfw_p;
+ int ret = -EINVAL;
+
+
+ for(mfw_p=&ip_masq_mfw_table[mfw_hash_val(mfw->fwmark)];
+ *mfw_p;
+ mfw_p = &((*mfw_p)->next))
+ {
+ if (mfw==(*mfw_p)) {
+ *mfw_p = mfw->next;
+ ip_masq_mod_dec_nent(mmod_self);
+ ret = 0;
+ goto out;
+ }
+ }
+out:
+ return ret;
+}
+
+/*
+ * Crude m.host scheduler
+ * This interface could be exported to allow playing with
+ * other sched policies.
+ *
+ * Caller must lock m-entry.
+ */
+static struct ip_masq_mfw_host * __mfw_sched(struct ip_masq_mfw *mfw, int force)
+{
+ struct ip_masq_mfw_host *h = NULL;
+
+ if (atomic_read(&mfw->nhosts) == 0)
+ goto out;
+
+ /*
+ * Here resides actual sched policy:
+ * When pref_cnt touches 0, entry gets shifted to tail and
+ * its pref_cnt reloaded from h->pref (actual value
+ * passed from u-space).
+ *
+ * Exception is pref==0: avoid scheduling.
+ */
+
+ h = list_entry(mfw->hosts.next, struct ip_masq_mfw_host, list);
+
+ if (atomic_read(&mfw->nhosts) <= 1)
+ goto out;
+
+ if ((h->pref && atomic_dec_and_test(&h->pref_cnt)) || force) {
+ atomic_set(&h->pref_cnt, h->pref);
+ list_del(&h->list);
+ list_add(&h->list, mfw->hosts.prev);
+ }
+out:
+ return h;
+}
+
+/*
+ * Main lookup routine.
+ * HITs fwmark and schedules m.host entries if required
+ */
+static struct ip_masq_mfw_host * mfw_lookup(int fwmark)
+{
+ struct ip_masq_mfw *mfw;
+ struct ip_masq_mfw_host *h = NULL;
+
+ read_lock(&mfw_lock);
+ mfw = __mfw_get(fwmark);
+
+ if (mfw) {
+ write_lock(&mfw->lock);
+ h = __mfw_sched(mfw, 0);
+ write_unlock(&mfw->lock);
+ }
+
+ read_unlock(&mfw_lock);
+ return h;
+}
+
+#ifdef CONFIG_PROC_FS
+static int mfw_procinfo(char *buffer, char **start, off_t offset,
+ int length, int dummy)
+{
+ struct ip_masq_mfw *mfw;
+ struct ip_masq_mfw_host *h;
+ struct list_head *l,*e;
+ off_t pos=0, begin;
+ char temp[129];
+ int idx = 0;
+ int len=0;
+
+ MOD_INC_USE_COUNT;
+
+ IP_MASQ_DEBUG(1-debug, "Entered mfw_info\n");
+
+ if (offset < 64)
+ {
+ sprintf(temp, "FwMark > RAddr RPort PrCnt Pref");
+ len = sprintf(buffer, "%-63s\n", temp);
+ }
+ pos = 64;
+
+ for(idx = 0; idx < IP_MASQ_MFW_HSIZE; idx++)
+ {
+ read_lock(&mfw_lock);
+ for(mfw = ip_masq_mfw_table[idx]; mfw ; mfw = mfw->next)
+ {
+ read_lock_bh(&mfw->lock);
+ l=&mfw->hosts;
+
+ for(e=l->next;l!=e;e=e->next) {
+ h = list_entry(e, struct ip_masq_mfw_host, list);
+ pos += 64;
+ if (pos <= offset) {
+ len = 0;
+ continue;
+ }
+
+ sprintf(temp,"0x%x > %08lX %5u %5d %5d",
+ h->fwmark,
+ ntohl(h->addr), ntohs(h->port),
+ atomic_read(&h->pref_cnt), h->pref);
+ len += sprintf(buffer+len, "%-63s\n", temp);
+
+ if(len >= length) {
+ read_unlock_bh(&mfw->lock);
+ read_unlock(&mfw_lock);
+ goto done;
+ }
+ }
+ read_unlock_bh(&mfw->lock);
+ }
+ read_unlock(&mfw_lock);
+ }
+
+done:
+
+ if (len) {
+ begin = len - (pos - offset);
+ *start = buffer + begin;
+ len -= begin;
+ }
+ if(len>length)
+ len = length;
+ MOD_DEC_USE_COUNT;
+ return len;
+}
+static struct proc_dir_entry mfw_proc_entry = {
+/* 0, 0, NULL", */
+ 0, 3, "mfw",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ mfw_procinfo
+};
+
+#define proc_ent &mfw_proc_entry
+#else /* !CONFIG_PROC_FS */
+
+#define proc_ent NULL
+#endif
+
+
+static void mfw_flush(void)
+{
+ struct ip_masq_mfw *mfw, *local_table[IP_MASQ_MFW_HSIZE];
+ struct ip_masq_mfw_host *h;
+ struct ip_masq_mfw *mfw_next;
+ int idx;
+ struct list_head *l,*e;
+
+ write_lock_bh(&mfw_lock);
+ memcpy(local_table, ip_masq_mfw_table, sizeof ip_masq_mfw_table);
+ memset(ip_masq_mfw_table, 0, sizeof ip_masq_mfw_table);
+ write_unlock_bh(&mfw_lock);
+
+ /*
+ * For every hash table row ...
+ */
+ for(idx=0;idx<IP_MASQ_MFW_HSIZE;idx++) {
+
+ /*
+ * For every m-entry in row ...
+ */
+ for(mfw=local_table[idx];mfw;mfw=mfw_next) {
+ /*
+ * For every m.host in m-entry ...
+ */
+ l=&mfw->hosts;
+ while((e=l->next) != l) {
+ h = list_entry(e, struct ip_masq_mfw_host, list);
+ atomic_dec(&mfw->nhosts);
+ list_del(&h->list);
+ kfree_s(h, sizeof(*h));
+ MOD_DEC_USE_COUNT;
+ }
+
+ if (atomic_read(&mfw->nhosts)) {
+ IP_MASQ_ERR("mfw_flush(): after flushing row nhosts=%d\n",
+ atomic_read(&mfw->nhosts));
+ }
+ mfw_next = mfw->next;
+ kfree_s(mfw, sizeof(*mfw));
+ MOD_DEC_USE_COUNT;
+ ip_masq_mod_dec_nent(mmod_self);
+ }
+ }
+}
+
+/*
+ * User space control entry point
+ */
+static int mfw_ctl(int optname, struct ip_masq_ctl *mctl, int optlen)
+{
+ struct ip_mfw_user *mu = &mctl->u.mfw_user;
+ struct ip_masq_mfw *mfw;
+ int ret = EINVAL;
+ int arglen = optlen - IP_MASQ_CTL_BSIZE;
+ int cmd;
+
+
+ IP_MASQ_DEBUG(1-debug, "ip_masq_user_ctl(len=%d/%d|%d/%d)\n",
+ arglen,
+ sizeof (*mu),
+ optlen,
+ sizeof (*mctl));
+
+ /*
+ * checks ...
+ */
+ if (arglen != sizeof(*mu) && optlen != sizeof(*mctl))
+ return -EINVAL;
+
+ /*
+ * Don't trust the lusers - plenty of error checking!
+ */
+ cmd = mctl->m_cmd;
+ IP_MASQ_DEBUG(1-debug, "ip_masq_mfw_ctl(cmd=%d, fwmark=%d)\n",
+ cmd, mu->fwmark);
+
+
+ switch(cmd) {
+ case IP_MASQ_CMD_NONE:
+ return 0;
+ case IP_MASQ_CMD_FLUSH:
+ break;
+ case IP_MASQ_CMD_ADD:
+ case IP_MASQ_CMD_INSERT:
+ case IP_MASQ_CMD_SET:
+ if (mu->fwmark == 0) {
+ IP_MASQ_DEBUG(1-debug, "invalid fwmark==0\n");
+ return -EINVAL;
+ }
+ if (mu->pref < 0) {
+ IP_MASQ_DEBUG(1-debug, "invalid pref==%d\n",
+ mu->pref);
+ return -EINVAL;
+ }
+ break;
+ }
+
+
+ ret = -EINVAL;
+
+ switch(cmd) {
+ case IP_MASQ_CMD_ADD:
+ case IP_MASQ_CMD_INSERT:
+ if (!mu->raddr) {
+ IP_MASQ_DEBUG(0-debug, "ip_masq_mfw_ctl(ADD): invalid redirect 0x%x:%d\n",
+ mu->raddr, mu->rport);
+ goto out;
+ }
+
+ /*
+ * Cannot just use mfw_lock because below
+ * are allocations that can sleep; so
+ * to assure "new entry" atomic creation
+ * I use a semaphore.
+ *
+ */
+ down(&mfw_sema);
+
+ read_lock(&mfw_lock);
+ mfw = __mfw_get(mu->fwmark);
+ read_unlock(&mfw_lock);
+
+ /*
+ * If first host, create m-entry
+ */
+ if (mfw == NULL) {
+ mfw = mfw_new(mu->fwmark);
+ if (mfw == NULL)
+ ret = -ENOMEM;
+ }
+
+ if (mfw) {
+ /*
+ * Put m.host in m-entry.
+ */
+ ret = mfw_addhost(mfw, mu, cmd == IP_MASQ_CMD_ADD);
+
+ /*
+ * If first host, link m-entry to hash table.
+ * Already protected by global lock.
+ */
+ if (ret == 0 && atomic_read(&mfw->nhosts) == 1) {
+ write_lock_bh(&mfw_lock);
+ __mfw_add(mfw);
+ write_unlock_bh(&mfw_lock);
+ }
+ if (atomic_read(&mfw->nhosts) == 0) {
+ mfw_destroy(mfw);
+ }
+ }
+
+ up(&mfw_sema);
+
+ break;
+
+ case IP_MASQ_CMD_DEL:
+ down(&mfw_sema);
+
+ read_lock(&mfw_lock);
+ mfw = __mfw_get(mu->fwmark);
+ read_unlock(&mfw_lock);
+
+ if (mfw) {
+ ret = mfw_delhost(mfw, mu);
+
+ /*
+ * Last lease will free
+ * XXX check logic XXX
+ */
+ if (atomic_read(&mfw->nhosts) == 0) {
+ write_lock_bh(&mfw_lock);
+ __mfw_del(mfw);
+ write_unlock_bh(&mfw_lock);
+ mfw_destroy(mfw);
+ }
+ } else
+ ret = -ESRCH;
+
+ up(&mfw_sema);
+ break;
+ case IP_MASQ_CMD_FLUSH:
+
+ down(&mfw_sema);
+ mfw_flush();
+ up(&mfw_sema);
+ ret = 0;
+ break;
+ case IP_MASQ_CMD_SET:
+ /*
+ * No need to semaphorize here, main list is not
+ * modified.
+ */
+ read_lock(&mfw_lock);
+
+ mfw = __mfw_get(mu->fwmark);
+ if (mfw) {
+ write_lock_bh(&mfw->lock);
+
+ if (mu->flags & IP_MASQ_MFW_SCHED) {
+ struct ip_masq_mfw_host *h;
+ if ((h=__mfw_sched(mfw, 1))) {
+ mfw_host_to_user(h, mu);
+ ret = 0;
+ }
+ } else {
+ ret = __mfw_edithost(mfw, mu);
+ }
+
+ write_unlock_bh(&mfw->lock);
+ }
+
+ read_unlock(&mfw_lock);
+ break;
+ }
+out:
+
+ return ret;
+}
+
+/*
+ * Module stubs called from ip_masq core module
+ */
+
+/*
+ * Input rule stub, called very early for each incoming packet,
+ * to see if this module has "interest" in packet.
+ */
+static int mfw_in_rule(const struct sk_buff *skb, const struct iphdr *iph)
+{
+ int val;
+ read_lock(&mfw_lock);
+ val = ( __mfw_get(skb->fwmark) != 0);
+ read_unlock(&mfw_lock);
+ return val;
+}
+
+/*
+ * Input-create stub, called to allow "custom" masq creation
+ */
+static struct ip_masq * mfw_in_create(const struct sk_buff *skb, const struct iphdr *iph, __u32 maddr)
+{
+ union ip_masq_tphdr tph;
+ struct ip_masq *ms = NULL;
+ struct ip_masq_mfw_host *h = NULL;
+
+ tph.raw = (char*) iph + iph->ihl * 4;
+
+ switch (iph->protocol) {
+ case IPPROTO_TCP:
+ /*
+ * Only open TCP tunnel if SYN+!ACK packet
+ */
+ if (!tph.th->syn || tph.th->ack)
+ return NULL;
+ case IPPROTO_UDP:
+ break;
+ default:
+ return NULL;
+ }
+
+ /*
+ * If no entry exists in the masquerading table
+ * and the port is involved
+ * in port forwarding, create a new masq entry
+ */
+
+ if ((h=mfw_lookup(skb->fwmark))) {
+ ms = ip_masq_new(iph->protocol,
+ iph->daddr, tph.portp[1],
+ /* if no redir-port, use packet dest port */
+ h->addr, h->port? h->port : tph.portp[1],
+ iph->saddr, tph.portp[0],
+ 0);
+
+ if (ms != NULL)
+ ip_masq_listen(ms);
+ }
+ return ms;
+}
+
+
+#define mfw_in_update NULL
+#define mfw_out_rule NULL
+#define mfw_out_create NULL
+#define mfw_out_update NULL
+
+static struct ip_masq_mod mfw_mod = {
+ NULL, /* next */
+ NULL, /* next_reg */
+ "mfw", /* name */
+ ATOMIC_INIT(0), /* nent */
+ ATOMIC_INIT(0), /* refcnt */
+ proc_ent,
+ mfw_ctl,
+ NULL, /* masq_mod_init */
+ NULL, /* masq_mod_done */
+ mfw_in_rule,
+ mfw_in_update,
+ mfw_in_create,
+ mfw_out_rule,
+ mfw_out_update,
+ mfw_out_create,
+};
+
+
+__initfunc(int ip_mfw_init(void))
+{
+ return register_ip_masq_mod ((mmod_self=&mfw_mod));
+}
+
+int ip_mfw_done(void)
+{
+ return unregister_ip_masq_mod(&mfw_mod);
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+ if (ip_mfw_init() != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if (ip_mfw_done() != 0)
+ printk(KERN_INFO "can't remove module");
+}
+
+#endif /* MODULE */
diff --git a/pfinet/linux-src/net/ipv4/ip_masq_mod.c b/pfinet/linux-src/net/ipv4/ip_masq_mod.c
new file mode 100644
index 00000000..b99502f3
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_masq_mod.c
@@ -0,0 +1,322 @@
+/*
+ * IP_MASQ_MOD masq modules support
+ *
+ *
+ * Author: Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
+ *
+ * $Id: ip_masq_mod.c,v 1.5.2.1 1999/07/02 10:10:03 davem Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Changes:
+ * Cyrus Durgin: fixed kerneld stuff for kmod.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <net/ip_masq.h>
+#include <net/ip_masq_mod.h>
+
+#include <linux/ip_masq.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+EXPORT_SYMBOL(register_ip_masq_mod);
+EXPORT_SYMBOL(unregister_ip_masq_mod);
+EXPORT_SYMBOL(ip_masq_mod_lkp_link);
+EXPORT_SYMBOL(ip_masq_mod_lkp_unlink);
+
+static spinlock_t masq_mod_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Base pointer for registered modules
+ */
+struct ip_masq_mod * ip_masq_mod_reg_base = NULL;
+
+/*
+ * Base pointer for lookup (subset of above, a module could be
+ * registered, but it could have no active rule); will avoid
+ * unnecessary lookups.
+ */
+struct ip_masq_mod * ip_masq_mod_lkp_base = NULL;
+
+int ip_masq_mod_register_proc(struct ip_masq_mod *mmod)
+{
+#ifdef CONFIG_PROC_FS
+ int ret;
+
+ struct proc_dir_entry *ent = mmod->mmod_proc_ent;
+
+ if (!ent)
+ return 0;
+ if (!ent->name) {
+ ent->name = mmod->mmod_name;
+ ent->namelen = strlen (mmod->mmod_name);
+ }
+ ret = ip_masq_proc_register(ent);
+ if (ret) mmod->mmod_proc_ent = NULL;
+
+ return ret;
+#else
+ return 0;
+#endif
+}
+
+void ip_masq_mod_unregister_proc(struct ip_masq_mod *mmod)
+{
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *ent = mmod->mmod_proc_ent;
+ if (!ent)
+ return;
+ ip_masq_proc_unregister(ent);
+#endif
+}
+
+/*
+ * Link/unlink object for lookups
+ */
+
+int ip_masq_mod_lkp_unlink(struct ip_masq_mod *mmod)
+{
+ struct ip_masq_mod **mmod_p;
+
+ write_lock_bh(&masq_mod_lock);
+
+ for (mmod_p = &ip_masq_mod_lkp_base; *mmod_p ; mmod_p = &(*mmod_p)->next)
+ if (mmod == (*mmod_p)) {
+ *mmod_p = mmod->next;
+ mmod->next = NULL;
+ write_unlock_bh(&masq_mod_lock);
+ return 0;
+ }
+
+ write_unlock_bh(&masq_mod_lock);
+ return -EINVAL;
+}
+
+int ip_masq_mod_lkp_link(struct ip_masq_mod *mmod)
+{
+ write_lock_bh(&masq_mod_lock);
+
+ mmod->next = ip_masq_mod_lkp_base;
+ ip_masq_mod_lkp_base=mmod;
+
+ write_unlock_bh(&masq_mod_lock);
+ return 0;
+}
+
+int register_ip_masq_mod(struct ip_masq_mod *mmod)
+{
+ if (!mmod) {
+ IP_MASQ_ERR("register_ip_masq_mod(): NULL arg\n");
+ return -EINVAL;
+ }
+ if (!mmod->mmod_name) {
+ IP_MASQ_ERR("register_ip_masq_mod(): NULL mmod_name\n");
+ return -EINVAL;
+ }
+ ip_masq_mod_register_proc(mmod);
+
+ mmod->next_reg = ip_masq_mod_reg_base;
+ ip_masq_mod_reg_base=mmod;
+
+ return 0;
+}
+
+int unregister_ip_masq_mod(struct ip_masq_mod *mmod)
+{
+ struct ip_masq_mod **mmod_p;
+
+ if (!mmod) {
+ IP_MASQ_ERR( "unregister_ip_masq_mod(): NULL arg\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Only allow unregistration if it is not referenced
+ */
+ if (atomic_read(&mmod->refcnt)) {
+ IP_MASQ_ERR( "unregister_ip_masq_mod(): is in use by %d guys. failed\n",
+ atomic_read(&mmod->refcnt));
+ return -EINVAL;
+ }
+
+ /*
+ * Must be already unlinked from lookup list
+ */
+ if (mmod->next) {
+ IP_MASQ_WARNING("MASQ: unregistering \"%s\" while in lookup list.fixed.",
+ mmod->mmod_name);
+ ip_masq_mod_lkp_unlink(mmod);
+ }
+
+ for (mmod_p = &ip_masq_mod_reg_base; *mmod_p ; mmod_p = &(*mmod_p)->next_reg)
+ if (mmod == (*mmod_p)) {
+ ip_masq_mod_unregister_proc(mmod);
+ *mmod_p = mmod->next_reg;
+ return 0;
+ }
+
+ IP_MASQ_ERR("unregister_ip_masq_mod(%s): not linked \n", mmod->mmod_name);
+ return -EINVAL;
+}
+
+int ip_masq_mod_in_rule(const struct sk_buff *skb, const struct iphdr *iph)
+{
+ struct ip_masq_mod *mmod;
+ int ret = IP_MASQ_MOD_NOP;
+
+ for (mmod=ip_masq_mod_lkp_base;mmod;mmod=mmod->next) {
+ if (!mmod->mmod_in_rule) continue;
+ switch (ret=mmod->mmod_in_rule(skb, iph)) {
+ case IP_MASQ_MOD_NOP:
+ continue;
+ case IP_MASQ_MOD_ACCEPT:
+ case IP_MASQ_MOD_REJECT:
+ goto out;
+ }
+ }
+out:
+ return ret;
+}
+
+int ip_masq_mod_out_rule(const struct sk_buff *skb, const struct iphdr *iph)
+{
+ struct ip_masq_mod *mmod;
+ int ret = IP_MASQ_MOD_NOP;
+
+ for (mmod=ip_masq_mod_lkp_base;mmod;mmod=mmod->next) {
+ if (!mmod->mmod_out_rule) continue;
+ switch (ret=mmod->mmod_out_rule(skb, iph)) {
+ case IP_MASQ_MOD_NOP:
+ continue;
+ case IP_MASQ_MOD_ACCEPT:
+ case IP_MASQ_MOD_REJECT:
+ goto out;
+ }
+ }
+out:
+ return ret;
+}
+
+struct ip_masq * ip_masq_mod_in_create(const struct sk_buff *skb, const struct iphdr *iph, __u32 maddr)
+{
+ struct ip_masq_mod *mmod;
+ struct ip_masq *ms = NULL;
+
+ for (mmod=ip_masq_mod_lkp_base;mmod;mmod=mmod->next) {
+ if (!mmod->mmod_in_create) continue;
+ if ((ms=mmod->mmod_in_create(skb, iph, maddr))) {
+ goto out;
+ }
+ }
+out:
+ return ms;
+}
+
+struct ip_masq * ip_masq_mod_out_create(const struct sk_buff *skb, const struct iphdr *iph, __u32 maddr)
+{
+ struct ip_masq_mod *mmod;
+ struct ip_masq *ms = NULL;
+
+ for (mmod=ip_masq_mod_lkp_base;mmod;mmod=mmod->next) {
+ if (!mmod->mmod_out_create) continue;
+ if ((ms=mmod->mmod_out_create(skb, iph, maddr))) {
+ goto out;
+ }
+ }
+out:
+ return ms;
+}
+
+int ip_masq_mod_in_update(const struct sk_buff *skb, const struct iphdr *iph, struct ip_masq *ms)
+{
+ struct ip_masq_mod *mmod;
+ int ret = IP_MASQ_MOD_NOP;
+
+ for (mmod=ip_masq_mod_lkp_base;mmod;mmod=mmod->next) {
+ if (!mmod->mmod_in_update) continue;
+ switch (ret=mmod->mmod_in_update(skb, iph, ms)) {
+ case IP_MASQ_MOD_NOP:
+ continue;
+ case IP_MASQ_MOD_ACCEPT:
+ case IP_MASQ_MOD_REJECT:
+ goto out;
+ }
+ }
+out:
+ return ret;
+}
+
+int ip_masq_mod_out_update(const struct sk_buff *skb, const struct iphdr *iph, struct ip_masq *ms)
+{
+ struct ip_masq_mod *mmod;
+ int ret = IP_MASQ_MOD_NOP;
+
+ for (mmod=ip_masq_mod_lkp_base;mmod;mmod=mmod->next) {
+ if (!mmod->mmod_out_update) continue;
+ switch (ret=mmod->mmod_out_update(skb, iph, ms)) {
+ case IP_MASQ_MOD_NOP:
+ continue;
+ case IP_MASQ_MOD_ACCEPT:
+ case IP_MASQ_MOD_REJECT:
+ goto out;
+ }
+ }
+out:
+ return ret;
+}
+
+struct ip_masq_mod * ip_masq_mod_getbyname(const char *mmod_name)
+{
+ struct ip_masq_mod * mmod;
+
+ IP_MASQ_DEBUG(1, "searching mmod_name \"%s\"\n", mmod_name);
+
+ for (mmod=ip_masq_mod_reg_base; mmod ; mmod=mmod->next_reg) {
+ if (mmod->mmod_ctl && *(mmod_name)
+ && (strcmp(mmod_name, mmod->mmod_name)==0)) {
+ /* HIT */
+ return mmod;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Module control entry
+ */
+int ip_masq_mod_ctl(int optname, struct ip_masq_ctl *mctl, int optlen)
+{
+ struct ip_masq_mod * mmod;
+#ifdef CONFIG_KMOD
+ char kmod_name[IP_MASQ_TNAME_MAX+8];
+#endif
+ /* tappo */
+ mctl->m_tname[IP_MASQ_TNAME_MAX-1] = 0;
+
+ mmod = ip_masq_mod_getbyname(mctl->m_tname);
+ if (mmod)
+ return mmod->mmod_ctl(optname, mctl, optlen);
+#ifdef CONFIG_KMOD
+ sprintf(kmod_name,"ip_masq_%s", mctl->m_tname);
+
+ IP_MASQ_DEBUG(1, "About to request \"%s\" module\n", kmod_name);
+
+ /*
+ * Let sleep for a while ...
+ */
+ request_module(kmod_name);
+ mmod = ip_masq_mod_getbyname(mctl->m_tname);
+ if (mmod)
+ return mmod->mmod_ctl(optname, mctl, optlen);
+#endif
+ return ESRCH;
+}
diff --git a/pfinet/linux-src/net/ipv4/ip_masq_portfw.c b/pfinet/linux-src/net/ipv4/ip_masq_portfw.c
new file mode 100644
index 00000000..c4b1ef4c
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_masq_portfw.c
@@ -0,0 +1,509 @@
+/*
+ * IP_MASQ_PORTFW masquerading module
+ *
+ *
+ * $Id: ip_masq_portfw.c,v 1.3.2.2 1999/08/13 18:26:29 davem Exp $
+ *
+ * Author: Steven Clarke <steven.clarke@monmouth.demon.co.uk>
+ *
+ * Fixes:
+ * Juan Jose Ciarlante : created this new file from ip_masq.c and ip_fw.c
+ * Juan Jose Ciarlante : modularized
+ * Juan Jose Ciarlante : use GFP_KERNEL
+ * Juan Jose Ciarlante : locking
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <net/ip.h>
+#include <linux/ip_fw.h>
+#include <linux/ip_masq.h>
+#include <net/ip_masq.h>
+#include <net/ip_masq_mod.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+
+#define IP_PORTFW_PORT_MIN 1
+#define IP_PORTFW_PORT_MAX 60999
+
+struct ip_portfw {
+ struct list_head list;
+ __u32 laddr, raddr;
+ __u16 lport, rport;
+ atomic_t pref_cnt; /* pref "counter" down to 0 */
+ int pref; /* user set pref */
+};
+
+static struct ip_masq_mod *mmod_self = NULL;
+/*
+ * Debug level
+ */
+#ifdef CONFIG_IP_MASQ_DEBUG
+static int debug=0;
+MODULE_PARM(debug, "i");
+#endif
+
+/*
+ * Lock
+ */
+static rwlock_t portfw_lock = RW_LOCK_UNLOCKED;
+
+static struct list_head portfw_list[2];
+static __inline__ int portfw_idx(int protocol)
+{
+ return (protocol==IPPROTO_TCP);
+}
+
+/*
+ *
+ * Delete forwarding entry(s):
+ * called from _DEL, u-space.
+ * . "relaxed" match, except for lport
+ *
+ */
+
+static __inline__ int ip_portfw_del(__u16 protocol, __u16 lport, __u32 laddr, __u16 rport, __u32 raddr)
+{
+ int prot = portfw_idx(protocol);
+ struct ip_portfw *n;
+ struct list_head *entry;
+ struct list_head *list = &portfw_list[prot];
+ int nent;
+
+ nent = atomic_read(&mmod_self->mmod_nent);
+
+ write_lock_bh(&portfw_lock);
+
+ for (entry=list->next;entry != list;entry = entry->next) {
+ n = list_entry(entry, struct ip_portfw, list);
+ if (n->lport == lport &&
+ (!laddr || n->laddr == laddr) &&
+ (!raddr || n->raddr == raddr) &&
+ (!rport || n->rport == rport)) {
+ entry = n->list.prev;
+ list_del(&n->list);
+ ip_masq_mod_dec_nent(mmod_self);
+ kfree_s(n, sizeof(struct ip_portfw));
+ MOD_DEC_USE_COUNT;
+ }
+ }
+ write_unlock_bh(&portfw_lock);
+
+ return nent==atomic_read(&mmod_self->mmod_nent)? ESRCH : 0;
+}
+
+/*
+ * Flush tables
+ * called from _FLUSH, u-space.
+ */
+static __inline__ void ip_portfw_flush(void)
+{
+ int prot;
+ struct list_head *l;
+ struct list_head *e;
+ struct ip_portfw *n;
+
+ write_lock_bh(&portfw_lock);
+
+ for (prot = 0; prot < 2;prot++) {
+ l = &portfw_list[prot];
+ while((e=l->next) != l) {
+ ip_masq_mod_dec_nent(mmod_self);
+ n = list_entry (e, struct ip_portfw, list);
+ list_del(e);
+ kfree_s(n, sizeof (*n));
+ MOD_DEC_USE_COUNT;
+ }
+ }
+
+ write_unlock_bh(&portfw_lock);
+}
+
+/*
+ * Lookup routine for lport,laddr match
+ * must be called with locked tables
+ */
+static __inline__ struct ip_portfw *ip_portfw_lookup(__u16 protocol, __u16 lport, __u32 laddr, __u32 *daddr_p, __u16 *dport_p)
+{
+ int prot = portfw_idx(protocol);
+
+ struct ip_portfw *n = NULL;
+ struct list_head *l, *e;
+
+ l = &portfw_list[prot];
+
+ for (e=l->next;e!=l;e=e->next) {
+ n = list_entry(e, struct ip_portfw, list);
+ if (lport == n->lport && laddr == n->laddr) {
+ /* Please be nice, don't pass only a NULL dport */
+ if (daddr_p) {
+ *daddr_p = n->raddr;
+ *dport_p = n->rport;
+ }
+
+ goto out;
+ }
+ }
+ n = NULL;
+out:
+ return n;
+}
+
+/*
+ * Edit routine for lport,[laddr], [raddr], [rport] match
+ * By now, only called from u-space
+ */
+static __inline__ int ip_portfw_edit(__u16 protocol, __u16 lport, __u32 laddr, __u16 rport, __u32 raddr, int pref)
+{
+ int prot = portfw_idx(protocol);
+
+ struct ip_portfw *n = NULL;
+ struct list_head *l, *e;
+ int count = 0;
+
+
+ read_lock_bh(&portfw_lock);
+
+ l = &portfw_list[prot];
+
+ for (e=l->next;e!=l;e=e->next) {
+ n = list_entry(e, struct ip_portfw, list);
+ if (lport == n->lport &&
+ (!laddr || laddr == n->laddr) &&
+ (!rport || rport == n->rport) &&
+ (!raddr || raddr == n->raddr)) {
+ n->pref = pref;
+ atomic_set(&n->pref_cnt, pref);
+ count++;
+ }
+ }
+
+ read_unlock_bh(&portfw_lock);
+
+ return count;
+}
+
+/*
+ * Add/edit en entry
+ * called from _ADD, u-space.
+ * must return 0 or +errno
+ */
+static __inline__ int ip_portfw_add(__u16 protocol, __u16 lport, __u32 laddr, __u16 rport, __u32 raddr, int pref)
+{
+ struct ip_portfw *npf;
+ int prot = portfw_idx(protocol);
+
+ if (pref <= 0)
+ return EINVAL;
+
+ if (ip_portfw_edit(protocol, lport, laddr, rport, raddr, pref)) {
+ /*
+ * Edit ok ...
+ */
+ return 0;
+ }
+
+ /* may block ... */
+ npf = (struct ip_portfw*) kmalloc(sizeof(struct ip_portfw), GFP_KERNEL);
+
+ if (!npf)
+ return ENOMEM;
+
+ MOD_INC_USE_COUNT;
+ memset(npf, 0, sizeof(*npf));
+
+ npf->laddr = laddr;
+ npf->lport = lport;
+ npf->rport = rport;
+ npf->raddr = raddr;
+ npf->pref = pref;
+
+ atomic_set(&npf->pref_cnt, npf->pref);
+ INIT_LIST_HEAD(&npf->list);
+
+ write_lock_bh(&portfw_lock);
+
+ /*
+ * Add at head
+ */
+ list_add(&npf->list, &portfw_list[prot]);
+
+ write_unlock_bh(&portfw_lock);
+
+ ip_masq_mod_inc_nent(mmod_self);
+ return 0;
+}
+
+
+
+static __inline__ int portfw_ctl(int optname, struct ip_masq_ctl *mctl, int optlen)
+{
+ struct ip_portfw_user *mm = &mctl->u.portfw_user;
+ int ret = EINVAL;
+ int arglen = optlen - IP_MASQ_CTL_BSIZE;
+ int cmd;
+
+
+ IP_MASQ_DEBUG(1-debug, "ip_masq_user_ctl(len=%d/%d|%d/%d)\n",
+ arglen,
+ sizeof (*mm),
+ optlen,
+ sizeof (*mctl));
+
+ /*
+ * Yes, I'm a bad guy ...
+ */
+ if (arglen != sizeof(*mm) && optlen != sizeof(*mctl))
+ return EINVAL;
+
+ /*
+ * Don't trust the lusers - plenty of error checking!
+ */
+ cmd = mctl->m_cmd;
+ IP_MASQ_DEBUG(1-debug, "ip_masq_portfw_ctl(cmd=%d)\n", cmd);
+
+
+ switch (cmd) {
+ case IP_MASQ_CMD_NONE:
+ return 0;
+ case IP_MASQ_CMD_FLUSH:
+ break;
+ default:
+ if (htons(mm->lport) < IP_PORTFW_PORT_MIN || htons(mm->lport) > IP_PORTFW_PORT_MAX)
+ return EINVAL;
+
+ if (mm->protocol!=IPPROTO_TCP && mm->protocol!=IPPROTO_UDP)
+ return EINVAL;
+ }
+
+ switch(cmd) {
+ case IP_MASQ_CMD_ADD:
+ ret = ip_portfw_add(mm->protocol,
+ mm->lport, mm->laddr,
+ mm->rport, mm->raddr,
+ mm->pref);
+ break;
+
+ case IP_MASQ_CMD_DEL:
+ ret = ip_portfw_del(mm->protocol,
+ mm->lport, mm->laddr,
+ mm->rport, mm->raddr);
+ break;
+ case IP_MASQ_CMD_FLUSH:
+ ip_portfw_flush();
+ ret = 0;
+ break;
+ }
+
+
+ return ret;
+}
+
+
+
+
+#ifdef CONFIG_PROC_FS
+
+static int portfw_procinfo(char *buffer, char **start, off_t offset,
+ int length, int unused)
+{
+ off_t pos=0, begin;
+ struct ip_portfw *pf;
+ struct list_head *l, *e;
+ char temp[65];
+ int ind;
+ int len=0;
+
+
+ if (offset < 64)
+ {
+ sprintf(temp, "Prot LAddr LPort > RAddr RPort PrCnt Pref");
+ len = sprintf(buffer, "%-63s\n", temp);
+ }
+ pos = 64;
+
+ read_lock_bh(&portfw_lock);
+
+ for(ind = 0; ind < 2; ind++)
+ {
+ l = &portfw_list[ind];
+ for (e=l->next; e!=l; e=e->next)
+ {
+ pf = list_entry(e, struct ip_portfw, list);
+ pos += 64;
+ if (pos <= offset) {
+ len = 0;
+ continue;
+ }
+
+ sprintf(temp,"%s %08lX %5u > %08lX %5u %5d %5d",
+ ind ? "TCP" : "UDP",
+ ntohl(pf->laddr), ntohs(pf->lport),
+ ntohl(pf->raddr), ntohs(pf->rport),
+ atomic_read(&pf->pref_cnt), pf->pref);
+ len += sprintf(buffer+len, "%-63s\n", temp);
+
+ if (len >= length)
+ goto done;
+ }
+ }
+done:
+ read_unlock_bh(&portfw_lock);
+
+ begin = len - (pos - offset);
+ *start = buffer + begin;
+ len -= begin;
+ if(len>length)
+ len = length;
+ return len;
+}
+
+static struct proc_dir_entry portfw_proc_entry = {
+/* 0, 0, NULL", */
+ 0, 6, "portfw", /* Just for compatibility, for now ... */
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ portfw_procinfo
+};
+
+#define proc_ent &portfw_proc_entry
+#else /* !CONFIG_PROC_FS */
+
+#define proc_ent NULL
+#endif
+
+static int portfw_in_rule(const struct sk_buff *skb, const struct iphdr *iph)
+{
+ const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
+#ifdef CONFIG_IP_MASQ_DEBUG
+ struct rtable *rt = (struct rtable *)skb->dst;
+#endif
+ struct ip_portfw *pfw;
+
+ IP_MASQ_DEBUG(2, "portfw_in_rule(): skb:= dev=%s (index=%d), rt_iif=%d, rt_flags=0x%x rt_dev___=%s daddr=%d.%d.%d.%d dport=%d\n",
+ skb->dev->name, skb->dev->ifindex, rt->rt_iif, rt->rt_flags,
+ rt->u.dst.dev->name,
+ NIPQUAD(iph->daddr), ntohs(portp[1]));
+
+ read_lock(&portfw_lock);
+ pfw = ip_portfw_lookup(iph->protocol, portp[1], iph->daddr, NULL, NULL);
+ read_unlock(&portfw_lock);
+ return (pfw!=0);
+}
+
+static struct ip_masq * portfw_in_create(const struct sk_buff *skb, const struct iphdr *iph, __u32 maddr)
+{
+ /*
+ * If no entry exists in the masquerading table
+ * and the port is involved
+ * in port forwarding, create a new masq entry
+ */
+
+ __u32 raddr;
+ __u16 rport;
+ const __u16 *portp = (__u16 *)&(((char *)iph)[iph->ihl*4]);
+ struct ip_masq *ms = NULL;
+ struct ip_portfw *pf;
+
+ /*
+ * Lock for writing.
+ */
+ write_lock(&portfw_lock);
+
+ if ((pf=ip_portfw_lookup(iph->protocol,
+ portp[1], iph->daddr,
+ &raddr, &rport))) {
+ ms = ip_masq_new(iph->protocol,
+ iph->daddr, portp[1],
+ raddr, rport,
+ iph->saddr, portp[0],
+ 0);
+ if (!ms || atomic_read(&mmod_self->mmod_nent) <= 1
+ /* || ip_masq_nlocks(&portfw_lock) != 1 */ )
+ /*
+ * Maybe later...
+ */
+ goto out;
+
+ ip_masq_listen(ms);
+
+ /*
+ * Entry created, lock==1.
+ * if pref_cnt == 0, move
+ * entry at _tail_.
+ * This is a simple load balance scheduling
+ */
+
+ if (atomic_dec_and_test(&pf->pref_cnt)) {
+
+ atomic_set(&pf->pref_cnt, pf->pref);
+ list_del(&pf->list);
+ list_add(&pf->list,
+ portfw_list[portfw_idx(iph->protocol)].prev);
+
+ }
+ }
+out:
+ write_unlock(&portfw_lock);
+ return ms;
+}
+
+#define portfw_in_update NULL
+#define portfw_out_rule NULL
+#define portfw_out_create NULL
+#define portfw_out_update NULL
+
+static struct ip_masq_mod portfw_mod = {
+ NULL, /* next */
+ NULL, /* next_reg */
+ "portfw", /* name */
+ ATOMIC_INIT(0), /* nent */
+ ATOMIC_INIT(0), /* refcnt */
+ proc_ent,
+ portfw_ctl,
+ NULL, /* masq_mod_init */
+ NULL, /* masq_mod_done */
+ portfw_in_rule,
+ portfw_in_update,
+ portfw_in_create,
+ portfw_out_rule,
+ portfw_out_update,
+ portfw_out_create,
+};
+
+
+
+__initfunc(int ip_portfw_init(void))
+{
+ INIT_LIST_HEAD(&portfw_list[0]);
+ INIT_LIST_HEAD(&portfw_list[1]);
+ return register_ip_masq_mod ((mmod_self=&portfw_mod));
+}
+
+int ip_portfw_done(void)
+{
+ return unregister_ip_masq_mod(&portfw_mod);
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+ if (ip_portfw_init() != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if (ip_portfw_done() != 0)
+ printk(KERN_INFO "ip_portfw_done(): can't remove module");
+}
+
+#endif /* MODULE */
diff --git a/pfinet/linux-src/net/ipv4/ip_masq_quake.c b/pfinet/linux-src/net/ipv4/ip_masq_quake.c
new file mode 100644
index 00000000..b9819645
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_masq_quake.c
@@ -0,0 +1,320 @@
+/*
+ * IP_MASQ_QUAKE quake masquerading module
+ *
+ *
+ * Version: @(#)ip_masq_quake.c 0.02 22/02/97
+ *
+ * Author: Harald Hoyer mailto:HarryH@Royal.Net
+ *
+ *
+ * Fixes:
+ * Harald Hoyer : Unofficial Quake Specs found at
+ * http://www.gamers.org/dEngine/quake/spec/
+ * Harald Hoyer : Check for QUAKE-STRING
+ * Juan Jose Ciarlante : litl bits for 2.1
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/init.h>
+#include <net/protocol.h>
+#include <net/udp.h>
+#include <net/ip_masq.h>
+
+#define DEBUG_CONFIG_IP_MASQ_QUAKE 0
+
+typedef struct
+{
+ __u16 type; // (Little Endian) Type of message.
+ __u16 length; // (Little Endian) Length of message, header included.
+ char message[0]; // The contents of the message.
+} QUAKEHEADER;
+
+struct quake_priv_data {
+ /* Have we seen a client connect message */
+ signed char cl_connect;
+};
+
+static int
+masq_quake_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+ MOD_INC_USE_COUNT;
+ if ((ms->app_data = kmalloc(sizeof(struct quake_priv_data),
+ GFP_ATOMIC)) == NULL)
+ printk(KERN_INFO "Quake: No memory for application data\n");
+ else
+ {
+ struct quake_priv_data *priv =
+ (struct quake_priv_data *)ms->app_data;
+ priv->cl_connect = 0;
+ }
+ return 0;
+}
+
+static int
+masq_quake_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+ MOD_DEC_USE_COUNT;
+ if (ms->app_data)
+ kfree_s(ms->app_data, sizeof(struct quake_priv_data));
+ return 0;
+}
+
+int
+masq_quake_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ struct udphdr *uh;
+ QUAKEHEADER *qh;
+ __u16 udp_port;
+ char *data;
+ unsigned char code;
+ struct quake_priv_data *priv = (struct quake_priv_data *)ms->app_data;
+
+ if(priv->cl_connect == -1)
+ return 0;
+
+ skb = *skb_p;
+
+ iph = skb->nh.iph;
+ uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]);
+
+ /* Check for length */
+ if(ntohs(uh->len) < 5)
+ return 0;
+
+ qh = (QUAKEHEADER *)&uh[1];
+
+ if(qh->type != 0x0080)
+ return 0;
+
+
+ code = qh->message[0];
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_in: code = %d \n", (int)code);
+#endif
+
+ switch(code) {
+ case 0x01:
+ /* Connection Request */
+
+ if(ntohs(qh->length) < 0x0c) {
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_in: length < 0xc \n");
+#endif
+ return 0;
+ }
+
+ data = &qh->message[1];
+
+ /* Check for stomping string */
+ if(memcmp(data,"QUAKE\0\3",7)) {
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_out: memcmp failed \n");
+#endif
+ return 0;
+ }
+ else {
+ priv->cl_connect = 1;
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_out: memcmp ok \n");
+#endif
+ }
+ break;
+
+ case 0x81:
+ /* Accept Connection */
+ if((ntohs(qh->length) < 0x09) || (priv->cl_connect == 0))
+ return 0;
+ data = &qh->message[1];
+
+ memcpy(&udp_port, data, 2);
+
+ ms->dport = htons(udp_port);
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_in: in_rewrote UDP port %d \n", udp_port);
+#endif
+ priv->cl_connect = -1;
+
+ break;
+ }
+
+ return 0;
+}
+
+int
+masq_quake_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ struct udphdr *uh;
+ QUAKEHEADER *qh;
+ __u16 udp_port;
+ char *data;
+ unsigned char code;
+ struct ip_masq *n_ms;
+ struct quake_priv_data *priv = (struct quake_priv_data *)ms->app_data;
+
+ if(priv->cl_connect == -1)
+ return 0;
+
+ skb = *skb_p;
+
+ iph = skb->nh.iph;
+ uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]);
+
+ /* Check for length */
+ if(ntohs(uh->len) < 5)
+ return 0;
+
+ qh = (QUAKEHEADER *)&uh[1];
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_out: qh->type = %d \n", (int)qh->type);
+#endif
+
+ if(qh->type != 0x0080)
+ return 0;
+
+ code = qh->message[0];
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_out: code = %d \n", (int)code);
+#endif
+
+ switch(code) {
+ case 0x01:
+ /* Connection Request */
+
+ if(ntohs(qh->length) < 0x0c) {
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_out: length < 0xc \n");
+#endif
+ return 0;
+ }
+
+ data = &qh->message[1];
+
+ /* Check for stomping string */
+ if(memcmp(data,"QUAKE\0\3",7)) {
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_out: memcmp failed \n");
+#endif
+ return 0;
+ }
+ else {
+ priv->cl_connect = 1;
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_out: memcmp ok \n");
+#endif
+ }
+ break;
+
+ case 0x81:
+ /* Accept Connection */
+ if((ntohs(qh->length) < 0x09) || (priv->cl_connect == 0))
+ return 0;
+
+ data = &qh->message[1];
+
+ memcpy(&udp_port, data, 2);
+
+ n_ms = ip_masq_new(IPPROTO_UDP,
+ maddr, 0,
+ ms->saddr, htons(udp_port),
+ ms->daddr, ms->dport,
+ 0);
+
+ if (n_ms==NULL)
+ return 0;
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_out: out_rewrote UDP port %d -> %d\n",
+ udp_port, ntohs(n_ms->mport));
+#endif
+ udp_port = ntohs(n_ms->mport);
+ memcpy(data, &udp_port, 2);
+
+ ip_masq_listen(n_ms);
+ ip_masq_control_add(n_ms, ms);
+ ip_masq_put(n_ms);
+
+ break;
+ }
+
+ return 0;
+}
+
+struct ip_masq_app ip_masq_quake = {
+ NULL, /* next */
+ "Quake_26", /* name */
+ 0, /* type */
+ 0, /* n_attach */
+ masq_quake_init_1, /* ip_masq_init_1 */
+ masq_quake_done_1, /* ip_masq_done_1 */
+ masq_quake_out, /* pkt_out */
+ masq_quake_in /* pkt_in */
+};
+struct ip_masq_app ip_masq_quakenew = {
+ NULL, /* next */
+ "Quake_27", /* name */
+ 0, /* type */
+ 0, /* n_attach */
+ masq_quake_init_1, /* ip_masq_init_1 */
+ masq_quake_done_1, /* ip_masq_done_1 */
+ masq_quake_out, /* pkt_out */
+ masq_quake_in /* pkt_in */
+};
+
+/*
+ * ip_masq_quake initialization
+ */
+
+__initfunc(int ip_masq_quake_init(void))
+{
+ return (register_ip_masq_app(&ip_masq_quake, IPPROTO_UDP, 26000) +
+ register_ip_masq_app(&ip_masq_quakenew, IPPROTO_UDP, 27000));
+}
+
+/*
+ * ip_masq_quake fin.
+ */
+
+int ip_masq_quake_done(void)
+{
+ return (unregister_ip_masq_app(&ip_masq_quake) +
+ unregister_ip_masq_app(&ip_masq_quakenew));
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+ if (ip_masq_quake_init() != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if (ip_masq_quake_done() != 0)
+ printk("ip_masq_quake: can't remove module");
+}
+
+#endif /* MODULE */
diff --git a/pfinet/linux-src/net/ipv4/ip_masq_raudio.c b/pfinet/linux-src/net/ipv4/ip_masq_raudio.c
new file mode 100644
index 00000000..ee3e276b
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_masq_raudio.c
@@ -0,0 +1,578 @@
+/*
+ * IP_MASQ_RAUDIO - Real Audio masquerading module
+ *
+ *
+ * Version: @(#)$Id: ip_masq_raudio.c,v 1.11 1998/10/06 04:49:04 davem Exp $
+ *
+ * Author: Nigel Metheringham
+ * Real Time Streaming code by Progressive Networks
+ * [strongly based on ftp module by Juan Jose Ciarlante & Wouter Gadeyne]
+ * [Real Audio information taken from Progressive Networks firewall docs]
+ * [Kudos to Progressive Networks for making the protocol specs available]
+ *
+ *
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ *
+ * Limitations
+ * The IP Masquerading proxies at present do not have access to a processed
+ * data stream. Hence for a protocol like the Real Audio control protocol,
+ * which depends on knowing where you are in the data stream, you either
+ * to keep a *lot* of state in your proxy, or you cheat and simplify the
+ * problem [needless to say I did the latter].
+ *
+ * This proxy only handles data in the first packet. Everything else is
+ * passed transparently. This means it should work under all normal
+ * circumstances, but it could be fooled by new data formats or a
+ * malicious application!
+ *
+ * At present the "first packet" is defined as a packet starting with
+ * the protocol ID string - "PNA".
+ * When the link is up there appears to be enough control data
+ * crossing the control link to keep it open even if a long audio
+ * piece is playing.
+ *
+ * The Robust UDP support added in RealAudio 3.0 is supported, but due
+ * to servers/clients not making great use of this has not been greatly
+ * tested. RealVideo (as used in the Real client version 4.0beta1) is
+ * supported but again is not greatly tested (bandwidth requirements
+ * appear to exceed that available at the sites supporting the protocol).
+ *
+ * Multiple Port Support
+ * The helper can be made to handle up to MAX_MASQ_APP_PORTS (normally 12)
+ * with the port numbers being defined at module load time. The module
+ * uses the symbol "ports" to define a list of monitored ports, which can
+ * be specified on the insmod command line as
+ * ports=x1,x2,x3...
+ * where x[n] are integer port numbers. This option can be put into
+ * /etc/conf.modules (or /etc/modules.conf depending on your config)
+ * where modload will pick it up should you use modload to load your
+ * modules.
+ *
+ * Fixes:
+ * Juan Jose Ciarlante : Use control_add() for control chan
+ * 10/15/97 - Modifications to allow masquerading of RTSP connections as
+ * well as PNA, which can potentially exist on the same port.
+ * Joe Rumsey <ogre@real.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/init.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+#include <net/ip_masq.h>
+
+/*
+#ifndef DEBUG_CONFIG_IP_MASQ_RAUDIO
+#define DEBUG_CONFIG_IP_MASQ_RAUDIO 0
+#endif
+*/
+
+#define TOLOWER(c) (((c) >= 'A' && (c) <= 'Z') ? ((c) - 'A' + 'a') : (c))
+#define ISDIGIT(c) (((c) >= '0') && ((c) <= '9'))
+
+struct raudio_priv_data {
+ /* Associated data connection - setup but not used at present */
+ struct ip_masq *data_conn;
+ /* UDP Error correction connection - setup but not used at present */
+ struct ip_masq *error_conn;
+ /* Have we seen and performed setup */
+ short seen_start;
+ short is_rtsp;
+};
+
+int
+masq_rtsp_out (struct ip_masq_app *mapp,
+ struct ip_masq *ms,
+ struct sk_buff **skb_p,
+ __u32 maddr);
+
+/*
+ * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper
+ * First port is set to the default port.
+ */
+int ports[MAX_MASQ_APP_PORTS] = {554, 7070, 0}; /* I rely on the trailing items being set to zero */
+struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS];
+
+/*
+ * Debug level
+ */
+#ifdef CONFIG_IP_MASQ_DEBUG
+static int debug=0;
+MODULE_PARM(debug, "i");
+#endif
+
+MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i");
+
+
+static int
+masq_raudio_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+ MOD_INC_USE_COUNT;
+ if ((ms->app_data = kmalloc(sizeof(struct raudio_priv_data),
+ GFP_ATOMIC)) == NULL)
+ printk(KERN_INFO "RealAudio: No memory for application data\n");
+ else
+ {
+ struct raudio_priv_data *priv =
+ (struct raudio_priv_data *)ms->app_data;
+ priv->seen_start = 0;
+ priv->data_conn = NULL;
+ priv->error_conn = NULL;
+ priv->is_rtsp = 0;
+ }
+ return 0;
+}
+
+static int
+masq_raudio_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+ MOD_DEC_USE_COUNT;
+ if (ms->app_data)
+ kfree_s(ms->app_data, sizeof(struct raudio_priv_data));
+ return 0;
+}
+
+int
+masq_raudio_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ struct tcphdr *th;
+ char *p, *data, *data_limit;
+ struct ip_masq *n_ms;
+ unsigned short version, msg_id, msg_len, udp_port;
+ struct raudio_priv_data *priv =
+ (struct raudio_priv_data *)ms->app_data;
+
+ /* Everything running correctly already */
+ if (priv && priv->seen_start)
+ return 0;
+
+ if(priv && priv->is_rtsp)
+ return masq_rtsp_out(mapp, ms, skb_p, maddr);
+
+ skb = *skb_p;
+ iph = skb->nh.iph;
+ th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
+ data = (char *)&th[1];
+
+ data_limit = skb->h.raw + skb->len;
+
+ if(memcmp(data, "OPTIONS", 7) == 0 ||
+ memcmp(data, "DESCRIBE", 8) == 0)
+ {
+ IP_MASQ_DEBUG(1-debug, "RealAudio: Detected RTSP connection\n");
+ /* This is an RTSP client */
+ if(priv)
+ priv->is_rtsp = 1;
+ return masq_rtsp_out(mapp, ms, skb_p, maddr);
+ }
+
+ /* Check to see if this is the first packet with protocol ID */
+ if (memcmp(data, "PNA", 3)) {
+ IP_MASQ_DEBUG(1-debug, "RealAudio: not initial protocol packet - ignored\n");
+ return(0);
+ }
+ data += 3;
+ memcpy(&version, data, 2);
+
+ IP_MASQ_DEBUG(1-debug, "RealAudio: initial seen - protocol version %d\n",
+ ntohs(version));
+ if (priv)
+ priv->seen_start = 1;
+
+ if (ntohs(version) >= 256)
+ {
+ printk(KERN_INFO "RealAudio: version (%d) not supported\n",
+ ntohs(version));
+ return 0;
+ }
+
+ data += 2;
+ while (data+4 < data_limit) {
+ memcpy(&msg_id, data, 2);
+ data += 2;
+ memcpy(&msg_len, data, 2);
+ data += 2;
+ if (ntohs(msg_id) == 0) {
+ /* The zero tag indicates the end of options */
+ IP_MASQ_DEBUG(1-debug, "RealAudio: packet end tag seen\n");
+ return 0;
+ }
+ IP_MASQ_DEBUG(1-debug, "RealAudio: msg %d - %d byte\n",
+ ntohs(msg_id), ntohs(msg_len));
+ if (ntohs(msg_id) == 0) {
+ /* The zero tag indicates the end of options */
+ return 0;
+ }
+ p = data;
+ data += ntohs(msg_len);
+ if (data > data_limit)
+ {
+ printk(KERN_INFO "RealAudio: Packet too short for data\n");
+ return 0;
+ }
+ if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
+ /*
+ * MsgId == 1
+ * Audio UDP data port on client
+ *
+ * MsgId == 7
+ * Robust UDP error correction port number on client
+ *
+ * Since these messages are treated just the same, they
+ * are bundled together here....
+ */
+ memcpy(&udp_port, p, 2);
+
+ /*
+ * Sometimes a server sends a message 7 with a zero UDP port
+ * Rather than do anything with this, just ignore it!
+ */
+ if (udp_port == 0)
+ continue;
+
+
+ n_ms = ip_masq_new(IPPROTO_UDP,
+ maddr, 0,
+ ms->saddr, udp_port,
+ ms->daddr, 0,
+ IP_MASQ_F_NO_DPORT);
+
+ if (n_ms==NULL)
+ return 0;
+
+ ip_masq_listen(n_ms);
+ ip_masq_control_add(n_ms, ms);
+
+ memcpy(p, &(n_ms->mport), 2);
+ IP_MASQ_DEBUG(1-debug, "RealAudio: rewrote UDP port %d -> %d in msg %d\n",
+ ntohs(udp_port), ntohs(n_ms->mport), ntohs(msg_id));
+
+ /* Make ref in application data to data connection */
+ if (priv) {
+ if (ntohs(msg_id) == 1)
+ priv->data_conn = n_ms;
+ else
+ priv->error_conn = n_ms;
+ }
+
+ ip_masq_put(n_ms);
+ }
+ }
+ return 0;
+}
+
+/*
+ * masq_rtsp_out
+ *
+ *
+ */
+int
+masq_rtsp_out (struct ip_masq_app *mapp,
+ struct ip_masq *ms,
+ struct sk_buff **skb_p,
+ __u32 maddr)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ struct tcphdr *th;
+ char *data, *data_limit;
+ struct ip_masq *n_ms, *n_ms2;
+ unsigned short udp_port;
+ struct raudio_priv_data *priv =
+ (struct raudio_priv_data *)ms->app_data;
+ const char* srch = "transport:";
+ const char* srchpos = srch;
+ const char* srchend = srch + strlen(srch);
+ int state = 0;
+ char firstport[6];
+ int firstportpos = 0;
+ char secondport[6];
+ int secondportpos = 0;
+ char *portstart = NULL, *portend = NULL;
+ int diff;
+
+ /* Everything running correctly already */
+ if (priv && priv->seen_start)
+ return 0;
+
+ skb = *skb_p;
+ iph = skb->nh.iph;
+ th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
+ data = (char *)&th[1];
+
+ data_limit = skb->h.raw + skb->len;
+
+ firstport[0] = 0;
+ secondport[0] = 0;
+
+ while(data < data_limit && state >= 0)
+ {
+ switch(state)
+ {
+ case 0:
+ case 1:
+ if(TOLOWER(*data) == *srchpos)
+ {
+ srchpos++;
+ if(srchpos == srchend)
+ {
+ IP_MASQ_DEBUG(1-debug, "Found string %s in message\n",
+ srch);
+ state++;
+ if(state == 1)
+ {
+ srch = "client_port";
+ srchpos = srch;
+ srchend = srch + strlen(srch);
+ }
+ }
+ }
+ else
+ {
+ srchpos = srch;
+ }
+ break;
+ case 2:
+ if(*data == '=')
+ state = 3;
+ break;
+ case 3:
+ if(ISDIGIT(*data))
+ {
+ portstart = data;
+ firstportpos = 0;
+ firstport[firstportpos++] = *data;
+ state = 4;
+ }
+ break;
+ case 4:
+ if(*data == '-')
+ {
+ state = 5;
+ }
+ else if(*data == ';')
+ {
+ portend = data - 1;
+ firstport[firstportpos] = 0;
+ state = -1;
+ }
+ else if(ISDIGIT(*data))
+ {
+ firstport[firstportpos++] = *data;
+ }
+ else if(*data != ' ' && *data != '\t')
+ {
+ /* This is a badly formed RTSP message, let's bail out */
+ IP_MASQ_DEBUG(1-debug, "Badly formed RTSP Message\n");
+ return 0;
+ }
+ break;
+ case 5:
+ if(ISDIGIT(*data))
+ {
+ secondportpos = 0;
+ secondport[secondportpos++] = *data;
+ state = 6;
+ }
+ else if(*data == ';')
+ {
+ portend = data - 1;
+ secondport[secondportpos] = 0;
+ state = -1;
+ }
+ break;
+ case 6:
+ if(*data == ';')
+ {
+ portend = data - 1;
+ secondport[secondportpos] = 0;
+ state = -1;
+ }
+ else if(ISDIGIT(*data))
+ {
+ secondport[secondportpos++] = *data;
+ }
+ else if(*data != ' ' && *data != '\t')
+ {
+ /* This is a badly formed RTSP message, let's bail out */
+ IP_MASQ_DEBUG(1-debug, "Badly formed RTSP Message\n");
+ return 0;
+ }
+ break;
+ }
+ data++;
+ }
+
+ if(state >= 0)
+ return 0;
+
+ if(firstportpos > 0)
+ {
+ char newbuf[12]; /* xxxxx-xxxxx\0 */
+ char* tmpptr;
+
+ udp_port = htons(simple_strtoul(firstport, &tmpptr, 10));
+ n_ms = ip_masq_new(IPPROTO_UDP,
+ maddr, 0,
+ ms->saddr, udp_port,
+ ms->daddr, 0,
+ IP_MASQ_F_NO_DPORT);
+ if (n_ms==NULL)
+ return 0;
+
+ ip_masq_listen(n_ms);
+ ip_masq_control_add(n_ms, ms);
+
+ if(secondportpos > 0)
+ {
+ udp_port = htons(simple_strtoul(secondport, &tmpptr, 10));
+ n_ms2 = ip_masq_new(IPPROTO_UDP,
+ maddr, 0,
+ ms->saddr, udp_port,
+ ms->daddr, 0,
+ IP_MASQ_F_NO_DPORT);
+ if (n_ms2==NULL) {
+ ip_masq_put(n_ms);
+ return 0;
+ }
+
+ ip_masq_listen(n_ms2);
+ ip_masq_control_add(n_ms2, ms);
+ sprintf(newbuf, "%d-%d", ntohs(n_ms->mport),
+ ntohs(n_ms2->mport));
+ }
+ else
+ {
+ sprintf(newbuf, "%d", ntohs(n_ms->mport));
+ n_ms2 = NULL;
+ }
+ *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC,
+ portstart, portend - portstart + 1,
+ newbuf, strlen(newbuf));
+ IP_MASQ_DEBUG(1-debug, "RTSP: rewrote client_port to %s\n", newbuf);
+ diff = strlen(newbuf) - (portend - portstart);
+ }
+ else
+ {
+ return 0;
+ }
+
+ if(priv)
+ {
+ priv->seen_start = 1;
+ if(n_ms)
+ priv->data_conn = n_ms;
+ if(n_ms2)
+ priv->error_conn = n_ms2;
+ }
+ /*
+ * Release tunnels
+ */
+
+ if (n_ms)
+ ip_masq_put(n_ms);
+
+ if (n_ms2)
+ ip_masq_put(n_ms2);
+
+ return diff;
+}
+
+struct ip_masq_app ip_masq_raudio = {
+ NULL, /* next */
+ "RealAudio", /* name */
+ 0, /* type */
+ 0, /* n_attach */
+ masq_raudio_init_1, /* ip_masq_init_1 */
+ masq_raudio_done_1, /* ip_masq_done_1 */
+ masq_raudio_out, /* pkt_out */
+ NULL /* pkt_in */
+};
+
+/*
+ * ip_masq_raudio initialization
+ */
+
+__initfunc(int ip_masq_raudio_init(void))
+{
+ int i, j;
+
+ for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+ if (ports[i]) {
+ if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app),
+ GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ memcpy(masq_incarnations[i], &ip_masq_raudio, sizeof(struct ip_masq_app));
+ if ((j = register_ip_masq_app(masq_incarnations[i],
+ IPPROTO_TCP,
+ ports[i]))) {
+ return j;
+ }
+ IP_MASQ_DEBUG(1-debug, "RealAudio: loaded support on port[%d] = %d\n",
+ i, ports[i]);
+ } else {
+ /* To be safe, force the incarnation table entry to NULL */
+ masq_incarnations[i] = NULL;
+ }
+ }
+ return 0;
+}
+
+/*
+ * ip_masq_raudio fin.
+ */
+
+int ip_masq_raudio_done(void)
+{
+ int i, j, k;
+
+ k=0;
+ for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+ if (masq_incarnations[i]) {
+ if ((j = unregister_ip_masq_app(masq_incarnations[i]))) {
+ k = j;
+ } else {
+ kfree(masq_incarnations[i]);
+ masq_incarnations[i] = NULL;
+ IP_MASQ_DEBUG(1-debug, "RealAudio: unloaded support on port[%d] = %d\n",
+ i, ports[i]);
+ }
+ }
+ }
+ return k;
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+ if (ip_masq_raudio_init() != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if (ip_masq_raudio_done() != 0)
+ printk(KERN_INFO "ip_masq_raudio: can't remove module");
+}
+
+#endif /* MODULE */
diff --git a/pfinet/linux-src/net/ipv4/ip_masq_user.c b/pfinet/linux-src/net/ipv4/ip_masq_user.c
new file mode 100644
index 00000000..848cbfe8
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_masq_user.c
@@ -0,0 +1,473 @@
+/*
+ * IP_MASQ_USER user space control module
+ *
+ *
+ * $Id: ip_masq_user.c,v 1.1.2.3 1999/11/16 06:33:51 davem Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <asm/system.h>
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/inet.h>
+#include <linux/init.h>
+#include <net/protocol.h>
+#include <net/icmp.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <net/checksum.h>
+#include <net/ip_masq.h>
+#include <net/ip_masq_mod.h>
+#include <linux/sysctl.h>
+#include <linux/ip_fw.h>
+
+#include <linux/ip_masq.h>
+
+/*
+ * Debug level
+ */
+static int debug=0;
+
+MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i");
+MODULE_PARM(debug, "i");
+
+/*
+static int check_5uple (struct ip_masq_user *ums) {
+ return 0;
+}
+*/
+static void masq_user_k2u(const struct ip_masq *ms, struct ip_masq_user *ums)
+{
+ ums->protocol = ms->protocol;
+ ums->daddr = ms->daddr;
+ ums->dport = ms->dport;
+ ums->maddr = ms->maddr;
+ ums->mport = ms->mport;
+ ums->saddr = ms->saddr;
+ ums->sport = ms->sport;
+ ums->timeout = ms->timeout;
+}
+
+
+static int ip_masq_user_maddr(struct ip_masq_user *ums)
+{
+ struct device *dev;
+ struct rtable *rt;
+ int ret = -EINVAL;
+ u32 rt_daddr, rt_saddr;
+ u32 tos;
+
+ /*
+ * Did specify masq address.
+ */
+ if (ums->maddr)
+ return 0;
+
+ /*
+ * Select address to use for routing query
+ */
+
+ rt_daddr = ums->rt_daddr? ums->rt_daddr : ums->daddr;
+ rt_saddr = ums->rt_saddr? ums->rt_saddr : ums->saddr;
+
+
+ /*
+ * No address for routing, cannot continue
+ */
+ if (rt_daddr == 0) {
+ IP_MASQ_DEBUG(1-debug, "cannot setup maddr with daddr=%lX, rt_addr=%lX\n",
+ ntohl(ums->daddr), ntohl(ums->rt_daddr));
+ return -EINVAL;
+ }
+
+ /*
+ * Find out rt device
+ */
+
+ rt_saddr = 0;
+ tos = RT_TOS(ums->ip_tos) | RTO_CONN;
+
+ if ((ret=ip_route_output(&rt, rt_daddr, rt_saddr, tos, 0 /* dev */))) {
+ IP_MASQ_DEBUG(0-debug, "could not setup maddr for routing daddr=%lX, saddr=%lX\n",
+ ntohl(rt_daddr), ntohl(rt_saddr));
+ return ret;
+ }
+ dev = rt->u.dst.dev;
+ ums->maddr = rt->rt_src; /* Per Alexey */
+
+ IP_MASQ_DEBUG(1-debug, "did setup maddr=%lX\n", ntohl(ums->maddr));
+ ip_rt_put(rt);
+ return 0;
+}
+
+/*
+ * Create new entry (from uspace)
+ */
+static int ip_masq_user_new(struct ip_masq_user *ums)
+{
+ struct ip_masq *ms = NULL;
+ unsigned mflags = 0;
+ int ret;
+
+ if (masq_proto_num (ums->protocol) == -1) {
+ return EPROTONOSUPPORT;
+ }
+
+ if (ums->dport == 0) {
+ ums->flags |= IP_MASQ_USER_F_LISTEN;
+ }
+
+ if (ums->flags | IP_MASQ_USER_F_LISTEN) {
+ if ((ums->saddr == 0) || (ums->sport == 0)) {
+ return EINVAL;
+ }
+ mflags |= (IP_MASQ_F_NO_DPORT|IP_MASQ_F_NO_DADDR);
+
+ }
+
+ if ((ret = ip_masq_user_maddr(ums)) < 0) {
+ return -ret;
+ }
+
+ mflags |= IP_MASQ_F_USER;
+ ms = ip_masq_new(ums->protocol,
+ ums->maddr, ums->mport,
+ ums->saddr, ums->sport,
+ ums->daddr, ums->dport,
+ mflags);
+
+ if (ms == NULL) {
+ /*
+ * FIXME: ip_masq_new() should return errno
+ */
+ return EBUSY;
+ }
+
+ /*
+ * Setup timeouts for this new entry
+ */
+
+ if (ums->timeout) {
+ ms->timeout = ums->timeout;
+ } else if (ums->flags | IP_MASQ_USER_F_LISTEN) {
+ ip_masq_listen(ms);
+ }
+
+ masq_user_k2u(ms, ums);
+ ip_masq_put(ms);
+ return 0;
+}
+
+/*
+ * Delete existing entry
+ */
+static int ip_masq_user_del(struct ip_masq_user *ums)
+{
+ struct ip_masq *ms=NULL;
+
+ if (masq_proto_num (ums->protocol) == -1) {
+ return EPROTONOSUPPORT;
+ }
+ start_bh_atomic();
+ if (ums->mport && ums->maddr) {
+ ms = ip_masq_in_get(ums->protocol,
+ ums->daddr, ums->dport,
+ ums->maddr, ums->mport);
+ end_bh_atomic();
+ } else if (ums->sport && ums->saddr) {
+ ms = ip_masq_out_get(ums->protocol,
+ ums->saddr, ums->sport,
+ ums->daddr, ums->dport);
+ end_bh_atomic();
+ } else
+ return EINVAL;
+
+ if (ms == NULL) {
+ return ESRCH;
+ }
+
+ /*
+ * got (locked) entry, setup almost tiny timeout :) and
+ * give away
+ *
+ * FIXME: should use something better than S_CLOSE
+ */
+ ms->timeout = IP_MASQ_S_CLOSE;
+
+ masq_user_k2u(ms, ums);
+ ip_masq_put(ms);
+ return 0;
+}
+
+static struct ip_masq * ip_masq_user_locked_get (struct ip_masq_user *ums, int *err)
+{
+ struct ip_masq *ms=NULL;
+ if (masq_proto_num (ums->protocol) == -1) {
+ *err = EPROTONOSUPPORT;
+ }
+
+ start_bh_atomic();
+ if (ums->mport && ums->maddr) {
+ ms = ip_masq_in_get(ums->protocol,
+ ums->daddr, ums->dport,
+ ums->maddr, ums->mport);
+ end_bh_atomic();
+ } else if (ums->sport && ums->saddr) {
+ ms = ip_masq_out_get(ums->protocol,
+ ums->saddr, ums->sport,
+ ums->daddr, ums->dport);
+ end_bh_atomic();
+ } else
+ *err = EINVAL;
+
+ if (ms == NULL) *err = ESRCH;
+ return ms;
+}
+
+/*
+ * Get existing entry (complete full tunnel info)
+ */
+static int ip_masq_user_get(struct ip_masq_user *ums)
+{
+ struct ip_masq *ms=NULL;
+ int err;
+
+ ms = ip_masq_user_locked_get(ums, &err);
+ if (ms == NULL)
+ return err;
+
+ masq_user_k2u(ms, ums);
+
+ ip_masq_put(ms);
+ return 0;
+}
+
+/*
+ * Set (some, valid) entry parameters
+ */
+static int ip_masq_user_set(struct ip_masq_user *ums)
+{
+ struct ip_masq *ms = NULL;
+ int err;
+
+ ms = ip_masq_user_locked_get(ums, &err);
+ if (ms == NULL)
+ return err;
+
+ /*
+ * FIXME: must allow selecting what you want to set
+ */
+ ms->timeout = ums->timeout;
+
+ masq_user_k2u(ms, ums);
+
+ ip_masq_put(ms);
+ return 0;
+}
+
+
+/*
+ * Entry point
+ * ret value:
+ * <0 err
+ * ==0 ok
+ * >0 ok, copy to user
+ */
+static int ip_masq_user_ctl(int optname, struct ip_masq_ctl *mctl, int optlen)
+{
+ struct ip_masq_user *ums = &mctl->u.user;
+ int ret = EINVAL;
+ int arglen = optlen - IP_MASQ_CTL_BSIZE;
+ int cmd;
+
+ IP_MASQ_DEBUG(1-debug, "ip_masq_user_ctl(len=%d/%d|%d/%d)\n",
+ arglen,
+ sizeof (*ums),
+ optlen,
+ sizeof (*mctl));
+
+ /*
+ * Yes, I'm a bad guy ...
+ */
+ if (arglen != sizeof(*ums) && optlen != sizeof(*mctl))
+ return EINVAL;
+
+ MOD_INC_USE_COUNT;
+
+ /*
+ * Don't trust the lusers - plenty of error checking!
+ */
+ cmd = mctl->m_cmd;
+ IP_MASQ_DEBUG(1-debug, "ip_masq_user_ctl(cmd=%d)\n", cmd);
+
+ switch (mctl->m_cmd) {
+ case IP_MASQ_CMD_ADD:
+ case IP_MASQ_CMD_INSERT:
+ ret = ip_masq_user_new(ums);
+ break;
+ case IP_MASQ_CMD_DEL:
+ ret = ip_masq_user_del(ums);
+ break;
+ case IP_MASQ_CMD_SET:
+ ret = ip_masq_user_set(ums);
+ break;
+ case IP_MASQ_CMD_GET:
+ ret = ip_masq_user_get(ums);
+ break;
+ }
+
+ /*
+ * For all of the above, return masq tunnel info
+ */
+
+ ret = -ret;
+
+ if (ret == 0) {
+ ret = sizeof (*ums) + IP_MASQ_CTL_BSIZE;
+ IP_MASQ_DEBUG(1-debug, "will return %d bytes to user\n", ret);
+ }
+
+ MOD_DEC_USE_COUNT;
+ return ret;
+}
+
+
+#ifdef CONFIG_PROC_FS
+static int ip_masq_user_info(char *buffer, char **start, off_t offset,
+ int length, int proto)
+{
+ off_t pos=0, begin;
+ struct ip_masq *ms;
+ char temp[129];
+ int idx = 0;
+ int col;
+ int len=0;
+ int magic_control;
+ struct list_head *l,*e;
+
+ MOD_INC_USE_COUNT;
+
+ IP_MASQ_DEBUG(1-debug, "Entered user_info with proto=%d\n", proto);
+
+ if (offset < 128)
+ {
+ sprintf(temp,
+ "Prot SrcIP SPrt DstIP DPrt MAddr MPrt State Flgs Ref Ctl Expires HRow HCol (free=%d,%d,%d)",
+ atomic_read(ip_masq_free_ports),
+ atomic_read(ip_masq_free_ports+1),
+ atomic_read(ip_masq_free_ports+2));
+ len = sprintf(buffer, "%-127s\n", temp);
+ }
+ pos = 128;
+
+ for(idx = 0; idx < IP_MASQ_TAB_SIZE; idx++)
+ {
+ /*
+ * Lock is actually only need in next loop
+ * we are called from uspace: must stop bh.
+ */
+ col=0;
+ read_lock_bh(&__ip_masq_lock);
+ l = &ip_masq_m_table[idx];
+ for (e=l->next; e!=l; e=e->next) {
+ col++;
+ ms = list_entry(e, struct ip_masq, m_list);
+ if (ms->protocol != proto) {
+ continue;
+ }
+
+ pos += 128;
+ if (pos <= offset) {
+ len = 0;
+ continue;
+ }
+
+ /*
+ * We have locked the tables, no need to del/add timers
+ * nor cli() 8)
+ */
+
+
+ magic_control = atomic_read(&ms->n_control);
+ if (!magic_control && ms->control) magic_control = -1;
+ sprintf(temp,"%-4s %08lX:%04X %08lX:%04X %08lX:%04X %-12s %3X %4d %3d %7lu %4d %4d",
+ masq_proto_name(ms->protocol),
+ ntohl(ms->saddr), ntohs(ms->sport),
+ ntohl(ms->daddr), ntohs(ms->dport),
+ ntohl(ms->maddr), ntohs(ms->mport),
+ ip_masq_state_name(ms->state),
+ ms->flags,
+ atomic_read(&ms->refcnt),
+ magic_control,
+ (ms->timer.expires-jiffies)/HZ,
+ idx, col);
+ len += sprintf(buffer+len, "%-127s\n", temp);
+
+ if(len >= length) {
+ read_unlock_bh(&__ip_masq_lock);
+ goto done;
+ }
+ }
+ read_unlock_bh(&__ip_masq_lock);
+ }
+
+done:
+
+ if (len) {
+ begin = len - (pos - offset);
+ *start = buffer + begin;
+ len -= begin;
+ }
+ if(len>length)
+ len = length;
+ MOD_DEC_USE_COUNT;
+ return len;
+}
+#else
+#define ip_masq_user_info NULL
+#endif
+
+static struct ip_masq_hook ip_masq_user = {
+ ip_masq_user_ctl,
+ ip_masq_user_info
+};
+
+int ip_masq_user_init(void)
+{
+ if (ip_masq_user_hook != NULL)
+ return -EEXIST;
+ ip_masq_user_hook = &ip_masq_user;
+ return 0;
+}
+
+int ip_masq_user_done(void)
+{
+ if (ip_masq_user_hook == NULL)
+ return ENOENT;
+ ip_masq_user_hook = NULL;
+ return 0;
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+int init_module(void)
+{
+ if (ip_masq_user_init() != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if (ip_masq_user_done() != 0)
+ printk(KERN_INFO "ip_masq_user_done(): can't remove module");
+}
+
+#endif /* MODULE */
diff --git a/pfinet/linux-src/net/ipv4/ip_masq_vdolive.c b/pfinet/linux-src/net/ipv4/ip_masq_vdolive.c
new file mode 100644
index 00000000..4724e3b9
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_masq_vdolive.c
@@ -0,0 +1,294 @@
+/*
+ * IP_MASQ_VDOLIVE - VDO Live masquerading module
+ *
+ *
+ * Version: @(#)$Id: ip_masq_vdolive.c,v 1.4 1998/10/06 04:49:07 davem Exp $
+ *
+ * Author: Nigel Metheringham <Nigel.Metheringham@ThePLAnet.net>
+ * PLAnet Online Ltd
+ *
+ * Fixes: Minor changes for 2.1 by
+ * Steven Clarke <Steven.Clarke@ThePlanet.Net>, Planet Online Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Thanks:
+ * Thank you to VDOnet Corporation for allowing me access to
+ * a protocol description without an NDA. This means that
+ * this module can be distributed as source - a great help!
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/system.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/init.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+#include <net/ip_masq.h>
+
+struct vdolive_priv_data {
+ /* Ports used */
+ unsigned short origport;
+ unsigned short masqport;
+ /* State of decode */
+ unsigned short state;
+};
+
+/*
+ * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper
+ * First port is set to the default port.
+ */
+static int ports[MAX_MASQ_APP_PORTS] = {7000}; /* I rely on the trailing items being set to zero */
+struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS];
+
+/*
+ * Debug level
+ */
+#ifdef CONFIG_IP_MASQ_DEBUG
+static int debug=0;
+MODULE_PARM(debug, "i");
+#endif
+
+MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i");
+
+static int
+masq_vdolive_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+ MOD_INC_USE_COUNT;
+ if ((ms->app_data = kmalloc(sizeof(struct vdolive_priv_data),
+ GFP_ATOMIC)) == NULL)
+ IP_MASQ_DEBUG(1-debug, "VDOlive: No memory for application data\n");
+ else
+ {
+ struct vdolive_priv_data *priv =
+ (struct vdolive_priv_data *)ms->app_data;
+ priv->origport = 0;
+ priv->masqport = 0;
+ priv->state = 0;
+ }
+ return 0;
+}
+
+static int
+masq_vdolive_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+ MOD_DEC_USE_COUNT;
+ if (ms->app_data)
+ kfree_s(ms->app_data, sizeof(struct vdolive_priv_data));
+ return 0;
+}
+
+int
+masq_vdolive_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ struct tcphdr *th;
+ char *data, *data_limit;
+ unsigned int tagval; /* This should be a 32 bit quantity */
+ struct ip_masq *n_ms;
+ struct vdolive_priv_data *priv =
+ (struct vdolive_priv_data *)ms->app_data;
+
+ /* This doesn't work at all if no priv data was allocated on startup */
+ if (!priv)
+ return 0;
+
+ /* Everything running correctly already */
+ if (priv->state == 3)
+ return 0;
+
+ skb = *skb_p;
+ iph = skb->nh.iph;
+ th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
+ data = (char *)&th[1];
+
+ data_limit = skb->h.raw + skb->len;
+
+ if (data+8 > data_limit) {
+ IP_MASQ_DEBUG(1-debug, "VDOlive: packet too short for ID %p %p\n", data, data_limit);
+ return 0;
+ }
+ memcpy(&tagval, data+4, 4);
+ IP_MASQ_DEBUG(1-debug, "VDOlive: packet seen, tag %ld, in initial state %d\n", ntohl(tagval), priv->state);
+
+ /* Check for leading packet ID */
+ if ((ntohl(tagval) != 6) && (ntohl(tagval) != 1)) {
+ IP_MASQ_DEBUG(1-debug, "VDOlive: unrecognised tag %ld, in initial state %d\n", ntohl(tagval), priv->state);
+ return 0;
+ }
+
+
+ /* Check packet is long enough for data - ignore if not */
+ if ((ntohl(tagval) == 6) && (data+36 > data_limit)) {
+ IP_MASQ_DEBUG(1-debug, "VDOlive: initial packet too short %p %p\n", data, data_limit);
+ return 0;
+ } else if ((ntohl(tagval) == 1) && (data+20 > data_limit)) {
+ IP_MASQ_DEBUG(1-debug,"VDOlive: secondary packet too short %p %p\n", data, data_limit);
+ return 0;
+ }
+
+ /* Adjust data pointers */
+ /*
+ * I could check the complete protocol version tag
+ * in here however I am just going to look for the
+ * "VDO Live" tag in the hope that this part will
+ * remain constant even if the version changes
+ */
+ if (ntohl(tagval) == 6) {
+ data += 24;
+ IP_MASQ_DEBUG(1-debug, "VDOlive: initial packet found\n");
+ } else {
+ data += 8;
+ IP_MASQ_DEBUG(1-debug, "VDOlive: secondary packet found\n");
+ }
+
+ if (memcmp(data, "VDO Live", 8) != 0) {
+ IP_MASQ_DEBUG(1-debug,"VDOlive: did not find tag\n");
+ return 0;
+ }
+ /*
+ * The port number is the next word after the tag.
+ * VDOlive encodes all of these values
+ * in 32 bit words, so in this case I am
+ * skipping the first 2 bytes of the next
+ * word to get to the relevant 16 bits
+ */
+ data += 10;
+
+ /*
+ * If we have not seen the port already,
+ * set the masquerading tunnel up
+ */
+ if (!priv->origport) {
+ memcpy(&priv->origport, data, 2);
+ IP_MASQ_DEBUG(1-debug, "VDOlive: found port %d\n", ntohs(priv->origport));
+
+ /* Open up a tunnel */
+ n_ms = ip_masq_new(IPPROTO_UDP,
+ maddr, 0,
+ ms->saddr, priv->origport,
+ ms->daddr, 0,
+ IP_MASQ_F_NO_DPORT);
+
+ if (n_ms==NULL) {
+ ip_masq_put(n_ms);
+ IP_MASQ_DEBUG(1-debug, "VDOlive: unable to build UDP tunnel for %x:%x\n", ms->saddr, priv->origport);
+ /* Leave state as unset */
+ priv->origport = 0;
+ return 0;
+ }
+ ip_masq_listen(n_ms);
+
+ ip_masq_put(ms);
+ priv->masqport = n_ms->mport;
+ } else if (memcmp(data, &(priv->origport), 2)) {
+ IP_MASQ_DEBUG(1-debug, "VDOlive: ports do not match\n");
+ /* Write the port in anyhow!!! */
+ }
+
+ /*
+ * Write masq port into packet
+ */
+ memcpy(data, &(priv->masqport), 2);
+ IP_MASQ_DEBUG(1-debug, "VDOlive: rewrote port %d to %d, server %08X\n", ntohs(priv->origport), ntohs(priv->masqport), ms->saddr);
+
+ /*
+ * Set state bit to make which bit has been done
+ */
+
+ priv->state |= (ntohl(tagval) == 6) ? 1 : 2;
+
+ return 0;
+}
+
+
+struct ip_masq_app ip_masq_vdolive = {
+ NULL, /* next */
+ "VDOlive", /* name */
+ 0, /* type */
+ 0, /* n_attach */
+ masq_vdolive_init_1, /* ip_masq_init_1 */
+ masq_vdolive_done_1, /* ip_masq_done_1 */
+ masq_vdolive_out, /* pkt_out */
+ NULL /* pkt_in */
+};
+
+/*
+ * ip_masq_vdolive initialization
+ */
+
+__initfunc(int ip_masq_vdolive_init(void))
+{
+ int i, j;
+
+ for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+ if (ports[i]) {
+ if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app),
+ GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ memcpy(masq_incarnations[i], &ip_masq_vdolive, sizeof(struct ip_masq_app));
+ if ((j = register_ip_masq_app(masq_incarnations[i],
+ IPPROTO_TCP,
+ ports[i]))) {
+ return j;
+ }
+ IP_MASQ_DEBUG(1-debug, "RealAudio: loaded support on port[%d] = %d\n", i, ports[i]);
+ } else {
+ /* To be safe, force the incarnation table entry to NULL */
+ masq_incarnations[i] = NULL;
+ }
+ }
+ return 0;
+}
+
+/*
+ * ip_masq_vdolive fin.
+ */
+
+int ip_masq_vdolive_done(void)
+{
+ int i, j, k;
+
+ k=0;
+ for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+ if (masq_incarnations[i]) {
+ if ((j = unregister_ip_masq_app(masq_incarnations[i]))) {
+ k = j;
+ } else {
+ kfree(masq_incarnations[i]);
+ masq_incarnations[i] = NULL;
+ IP_MASQ_DEBUG(1-debug,"VDOlive: unloaded support on port[%d] = %d\n", i, ports[i]);
+ }
+ }
+ }
+ return k;
+}
+
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+ if (ip_masq_vdolive_init() != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if (ip_masq_vdolive_done() != 0)
+ IP_MASQ_DEBUG(1-debug, "ip_masq_vdolive: can't remove module");
+}
+
+#endif /* MODULE */
diff --git a/pfinet/linux-src/net/ipv4/ip_nat_dumb.c b/pfinet/linux-src/net/ipv4/ip_nat_dumb.c
new file mode 100644
index 00000000..5a1c6d75
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_nat_dumb.c
@@ -0,0 +1,158 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Dumb Network Address Translation.
+ *
+ * Version: $Id: ip_nat_dumb.c,v 1.8 1999/03/21 05:22:40 davem Exp $
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Fixes:
+ * Rani Assaf : A zero checksum is a special case
+ * only in UDP
+ * Rani Assaf : Added ICMP messages rewriting
+ * Rani Assaf : Repaired wrong changes, made by ANK.
+ *
+ *
+ * NOTE: It is just working model of real NAT.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
+#include <linux/netdevice.h>
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/firewall.h>
+#include <linux/ip_fw.h>
+#include <net/checksum.h>
+#include <linux/route.h>
+#include <net/route.h>
+#include <net/ip_fib.h>
+
+
+int
+ip_do_nat(struct sk_buff *skb)
+{
+ struct rtable *rt = (struct rtable*)skb->dst;
+ struct iphdr *iph = skb->nh.iph;
+ u32 odaddr = iph->daddr;
+ u32 osaddr = iph->saddr;
+ u16 check;
+
+ IPCB(skb)->flags |= IPSKB_TRANSLATED;
+
+ /* Rewrite IP header */
+ iph->daddr = rt->rt_dst_map;
+ iph->saddr = rt->rt_src_map;
+ iph->check = 0;
+ iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+
+ /* If it is the first fragment, rewrite protocol headers */
+
+ if (!(iph->frag_off & htons(IP_OFFSET))) {
+ u16 *cksum;
+
+ switch(iph->protocol) {
+ case IPPROTO_TCP:
+ cksum = (u16*)&((struct tcphdr*)(((char*)iph) + (iph->ihl<<2)))->check;
+ if ((u8*)(cksum+1) > skb->tail)
+ goto truncated;
+ check = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, ~(*cksum));
+ *cksum = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check);
+ break;
+ case IPPROTO_UDP:
+ cksum = (u16*)&((struct udphdr*)(((char*)iph) + (iph->ihl<<2)))->check;
+ if ((u8*)(cksum+1) > skb->tail)
+ goto truncated;
+ if ((check = *cksum) != 0) {
+ check = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, ~check);
+ check = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check);
+ *cksum = check ? : 0xFFFF;
+ }
+ break;
+ case IPPROTO_ICMP:
+ {
+ struct icmphdr *icmph = (struct icmphdr*)((char*)iph + (iph->ihl<<2));
+ struct iphdr *ciph;
+ u32 idaddr, isaddr;
+ int updated;
+
+ if ((icmph->type != ICMP_DEST_UNREACH) &&
+ (icmph->type != ICMP_TIME_EXCEEDED) &&
+ (icmph->type != ICMP_PARAMETERPROB))
+ break;
+
+ ciph = (struct iphdr *) (icmph + 1);
+
+ if ((u8*)(ciph+1) > skb->tail)
+ goto truncated;
+
+ isaddr = ciph->saddr;
+ idaddr = ciph->daddr;
+ updated = 0;
+
+ if (rt->rt_flags&RTCF_DNAT && ciph->saddr == odaddr) {
+ ciph->saddr = iph->daddr;
+ updated = 1;
+ }
+ if (rt->rt_flags&RTCF_SNAT) {
+ if (ciph->daddr != osaddr) {
+ struct fib_result res;
+ struct rt_key key;
+ unsigned flags = 0;
+
+ key.src = ciph->daddr;
+ key.dst = ciph->saddr;
+ key.iif = skb->dev->ifindex;
+ key.oif = 0;
+#ifdef CONFIG_IP_ROUTE_TOS
+ key.tos = RT_TOS(ciph->tos);
+#endif
+#ifdef CONFIG_IP_ROUTE_FWMARK
+ key.fwmark = 0;
+#endif
+ /* Use fib_lookup() until we get our own
+ * hash table of NATed hosts -- Rani
+ */
+ if (fib_lookup(&key, &res) == 0 && res.r) {
+ ciph->daddr = fib_rules_policy(ciph->daddr, &res, &flags);
+ if (ciph->daddr != idaddr)
+ updated = 1;
+ }
+ } else {
+ ciph->daddr = iph->saddr;
+ updated = 1;
+ }
+ }
+ if (updated) {
+ cksum = &icmph->checksum;
+ /* Using tcpudp primitive. Why not? */
+ check = csum_tcpudp_magic(ciph->saddr, ciph->daddr, 0, 0, ~(*cksum));
+ *cksum = csum_tcpudp_magic(~isaddr, ~idaddr, 0, 0, ~check);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return 0;
+
+truncated:
+ return -EINVAL;
+}
diff --git a/pfinet/linux-src/net/ipv4/ip_options.c b/pfinet/linux-src/net/ipv4/ip_options.c
new file mode 100644
index 00000000..ec21054d
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_options.c
@@ -0,0 +1,620 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * The options processing module for ip.c
+ *
+ * Version: $Id: ip_options.c,v 1.16.2.1 1999/06/02 04:06:19 davem Exp $
+ *
+ * Authors: A.N.Kuznetsov
+ *
+ */
+
+#include <linux/types.h>
+#include <asm/uaccess.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+
+/*
+ * Write options to IP header, record destination address to
+ * source route option, address of outgoing interface
+ * (we should already know it, so that this function is allowed be
+ * called only after routing decision) and timestamp,
+ * if we originate this datagram.
+ *
+ * daddr is real destination address, next hop is recorded in IP header.
+ * saddr is address of outgoing interface.
+ */
+
+void ip_options_build(struct sk_buff * skb, struct ip_options * opt,
+ u32 daddr, struct rtable *rt, int is_frag)
+{
+ unsigned char * iph = skb->nh.raw;
+
+ memcpy(&(IPCB(skb)->opt), opt, sizeof(struct ip_options));
+ memcpy(iph+sizeof(struct iphdr), opt->__data, opt->optlen);
+ opt = &(IPCB(skb)->opt);
+ opt->is_data = 0;
+
+ if (opt->srr)
+ memcpy(iph+opt->srr+iph[opt->srr+1]-4, &daddr, 4);
+
+ if (!is_frag) {
+ if (opt->rr_needaddr)
+ ip_rt_get_source(iph+opt->rr+iph[opt->rr+2]-5, rt);
+ if (opt->ts_needaddr)
+ ip_rt_get_source(iph+opt->ts+iph[opt->ts+2]-9, rt);
+ if (opt->ts_needtime) {
+ struct timeval tv;
+ __u32 midtime;
+ do_gettimeofday(&tv);
+ midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
+ memcpy(iph+opt->ts+iph[opt->ts+2]-5, &midtime, 4);
+ }
+ return;
+ }
+ if (opt->rr) {
+ memset(iph+opt->rr, IPOPT_NOP, iph[opt->rr+1]);
+ opt->rr = 0;
+ opt->rr_needaddr = 0;
+ }
+ if (opt->ts) {
+ memset(iph+opt->ts, IPOPT_NOP, iph[opt->ts+1]);
+ opt->ts = 0;
+ opt->ts_needaddr = opt->ts_needtime = 0;
+ }
+}
+
+/*
+ * Provided (sopt, skb) points to received options,
+ * build in dopt compiled option set appropriate for answering.
+ * i.e. invert SRR option, copy anothers,
+ * and grab room in RR/TS options.
+ *
+ * NOTE: dopt cannot point to skb.
+ */
+
+int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb)
+{
+ struct ip_options *sopt;
+ unsigned char *sptr, *dptr;
+ int soffset, doffset;
+ int optlen;
+ u32 daddr;
+
+ memset(dopt, 0, sizeof(struct ip_options));
+
+ dopt->is_data = 1;
+
+ sopt = &(IPCB(skb)->opt);
+
+ if (sopt->optlen == 0) {
+ dopt->optlen = 0;
+ return 0;
+ }
+
+ sptr = skb->nh.raw;
+ dptr = dopt->__data;
+
+ if (skb->dst)
+ daddr = ((struct rtable*)skb->dst)->rt_spec_dst;
+ else
+ daddr = skb->nh.iph->daddr;
+
+ if (sopt->rr) {
+ optlen = sptr[sopt->rr+1];
+ soffset = sptr[sopt->rr+2];
+ dopt->rr = dopt->optlen + sizeof(struct iphdr);
+ memcpy(dptr, sptr+sopt->rr, optlen);
+ if (sopt->rr_needaddr && soffset <= optlen) {
+ if (soffset + 3 > optlen)
+ return -EINVAL;
+ dptr[2] = soffset + 4;
+ dopt->rr_needaddr = 1;
+ }
+ dptr += optlen;
+ dopt->optlen += optlen;
+ }
+ if (sopt->ts) {
+ optlen = sptr[sopt->ts+1];
+ soffset = sptr[sopt->ts+2];
+ dopt->ts = dopt->optlen + sizeof(struct iphdr);
+ memcpy(dptr, sptr+sopt->ts, optlen);
+ if (soffset <= optlen) {
+ if (sopt->ts_needaddr) {
+ if (soffset + 3 > optlen)
+ return -EINVAL;
+ dopt->ts_needaddr = 1;
+ soffset += 4;
+ }
+ if (sopt->ts_needtime) {
+ if (soffset + 3 > optlen)
+ return -EINVAL;
+ if ((dptr[3]&0xF) != IPOPT_TS_PRESPEC) {
+ dopt->ts_needtime = 1;
+ soffset += 4;
+ } else {
+ dopt->ts_needtime = 0;
+
+ if (soffset + 8 <= optlen) {
+ __u32 addr;
+
+ memcpy(&addr, sptr+soffset-1, 4);
+ if (inet_addr_type(addr) != RTN_LOCAL) {
+ dopt->ts_needtime = 1;
+ soffset += 8;
+ }
+ }
+ }
+ }
+ dptr[2] = soffset;
+ }
+ dptr += optlen;
+ dopt->optlen += optlen;
+ }
+ if (sopt->srr) {
+ unsigned char * start = sptr+sopt->srr;
+ u32 faddr;
+
+ optlen = start[1];
+ soffset = start[2];
+ doffset = 0;
+ if (soffset > optlen)
+ soffset = optlen + 1;
+ soffset -= 4;
+ if (soffset > 3) {
+ memcpy(&faddr, &start[soffset-1], 4);
+ for (soffset-=4, doffset=4; soffset > 3; soffset-=4, doffset+=4)
+ memcpy(&dptr[doffset-1], &start[soffset-1], 4);
+ /*
+ * RFC1812 requires to fix illegal source routes.
+ */
+ if (memcmp(&skb->nh.iph->saddr, &start[soffset+3], 4) == 0)
+ doffset -= 4;
+ }
+ if (doffset > 3) {
+ memcpy(&start[doffset-1], &daddr, 4);
+ dopt->faddr = faddr;
+ dptr[0] = start[0];
+ dptr[1] = doffset+3;
+ dptr[2] = 4;
+ dptr += doffset+3;
+ dopt->srr = dopt->optlen + sizeof(struct iphdr);
+ dopt->optlen += doffset+3;
+ dopt->is_strictroute = sopt->is_strictroute;
+ }
+ }
+ while (dopt->optlen & 3) {
+ *dptr++ = IPOPT_END;
+ dopt->optlen++;
+ }
+ return 0;
+}
+
+/*
+ * Options "fragmenting", just fill options not
+ * allowed in fragments with NOOPs.
+ * Simple and stupid 8), but the most efficient way.
+ */
+
+void ip_options_fragment(struct sk_buff * skb)
+{
+ unsigned char * optptr = skb->nh.raw;
+ struct ip_options * opt = &(IPCB(skb)->opt);
+ int l = opt->optlen;
+ int optlen;
+
+ while (l > 0) {
+ switch (*optptr) {
+ case IPOPT_END:
+ return;
+ case IPOPT_NOOP:
+ l--;
+ optptr++;
+ continue;
+ }
+ optlen = optptr[1];
+ if (optlen<2 || optlen>l)
+ return;
+ if (!IPOPT_COPIED(*optptr))
+ memset(optptr, IPOPT_NOOP, optlen);
+ l -= optlen;
+ optptr += optlen;
+ }
+ opt->ts = 0;
+ opt->rr = 0;
+ opt->rr_needaddr = 0;
+ opt->ts_needaddr = 0;
+ opt->ts_needtime = 0;
+ return;
+}
+
+/*
+ * Verify options and fill pointers in struct options.
+ * Caller should clear *opt, and set opt->data.
+ * If opt == NULL, then skb->data should point to IP header.
+ */
+
+int ip_options_compile(struct ip_options * opt, struct sk_buff * skb)
+{
+ int l;
+ unsigned char * iph;
+ unsigned char * optptr;
+ int optlen;
+ unsigned char * pp_ptr = NULL;
+ struct rtable *rt = skb ? (struct rtable*)skb->dst : NULL;
+
+ if (!opt) {
+ opt = &(IPCB(skb)->opt);
+ memset(opt, 0, sizeof(struct ip_options));
+ iph = skb->nh.raw;
+ opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr);
+ optptr = iph + sizeof(struct iphdr);
+ opt->is_data = 0;
+ } else {
+ optptr = opt->is_data ? opt->__data : (unsigned char*)&(skb->nh.iph[1]);
+ iph = optptr - sizeof(struct iphdr);
+ }
+
+ for (l = opt->optlen; l > 0; ) {
+ switch (*optptr) {
+ case IPOPT_END:
+ for (optptr++, l--; l>0; l--) {
+ if (*optptr != IPOPT_END) {
+ *optptr = IPOPT_END;
+ opt->is_changed = 1;
+ }
+ }
+ goto eol;
+ case IPOPT_NOOP:
+ l--;
+ optptr++;
+ continue;
+ }
+ optlen = optptr[1];
+ if (optlen<2 || optlen>l) {
+ pp_ptr = optptr;
+ goto error;
+ }
+ switch (*optptr) {
+ case IPOPT_SSRR:
+ case IPOPT_LSRR:
+ if (optlen < 3) {
+ pp_ptr = optptr + 1;
+ goto error;
+ }
+ if (optptr[2] < 4) {
+ pp_ptr = optptr + 2;
+ goto error;
+ }
+ /* NB: cf RFC-1812 5.2.4.1 */
+ if (opt->srr) {
+ pp_ptr = optptr;
+ goto error;
+ }
+ if (!skb) {
+ if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) {
+ pp_ptr = optptr + 1;
+ goto error;
+ }
+ memcpy(&opt->faddr, &optptr[3], 4);
+ if (optlen > 7)
+ memmove(&optptr[3], &optptr[7], optlen-7);
+ }
+ opt->is_strictroute = (optptr[0] == IPOPT_SSRR);
+ opt->srr = optptr - iph;
+ break;
+ case IPOPT_RR:
+ if (opt->rr) {
+ pp_ptr = optptr;
+ goto error;
+ }
+ if (optlen < 3) {
+ pp_ptr = optptr + 1;
+ goto error;
+ }
+ if (optptr[2] < 4) {
+ pp_ptr = optptr + 2;
+ goto error;
+ }
+ if (optptr[2] <= optlen) {
+ if (optptr[2]+3 > optlen) {
+ pp_ptr = optptr + 2;
+ goto error;
+ }
+ if (skb) {
+ memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4);
+ opt->is_changed = 1;
+ }
+ optptr[2] += 4;
+ opt->rr_needaddr = 1;
+ }
+ opt->rr = optptr - iph;
+ break;
+ case IPOPT_TIMESTAMP:
+ if (opt->ts) {
+ pp_ptr = optptr;
+ goto error;
+ }
+ if (optlen < 4) {
+ pp_ptr = optptr + 1;
+ goto error;
+ }
+ if (optptr[2] < 5) {
+ pp_ptr = optptr + 2;
+ goto error;
+ }
+ if (optptr[2] <= optlen) {
+ __u32 * timeptr = NULL;
+ if (optptr[2]+3 > optptr[1]) {
+ pp_ptr = optptr + 2;
+ goto error;
+ }
+ switch (optptr[3]&0xF) {
+ case IPOPT_TS_TSONLY:
+ opt->ts = optptr - iph;
+ if (skb)
+ timeptr = (__u32*)&optptr[optptr[2]-1];
+ opt->ts_needtime = 1;
+ optptr[2] += 4;
+ break;
+ case IPOPT_TS_TSANDADDR:
+ if (optptr[2]+7 > optptr[1]) {
+ pp_ptr = optptr + 2;
+ goto error;
+ }
+ opt->ts = optptr - iph;
+ if (skb) {
+ memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4);
+ timeptr = (__u32*)&optptr[optptr[2]+3];
+ }
+ opt->ts_needaddr = 1;
+ opt->ts_needtime = 1;
+ optptr[2] += 8;
+ break;
+ case IPOPT_TS_PRESPEC:
+ if (optptr[2]+7 > optptr[1]) {
+ pp_ptr = optptr + 2;
+ goto error;
+ }
+ opt->ts = optptr - iph;
+ {
+ u32 addr;
+ memcpy(&addr, &optptr[optptr[2]-1], 4);
+ if (inet_addr_type(addr) == RTN_UNICAST)
+ break;
+ if (skb)
+ timeptr = (__u32*)&optptr[optptr[2]+3];
+ }
+ opt->ts_needtime = 1;
+ optptr[2] += 8;
+ break;
+ default:
+ if (!skb && !capable(CAP_NET_RAW)) {
+ pp_ptr = optptr + 3;
+ goto error;
+ }
+ break;
+ }
+ if (timeptr) {
+ struct timeval tv;
+ __u32 midtime;
+ do_gettimeofday(&tv);
+ midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
+ memcpy(timeptr, &midtime, sizeof(__u32));
+ opt->is_changed = 1;
+ }
+ } else {
+ unsigned overflow = optptr[3]>>4;
+ if (overflow == 15) {
+ pp_ptr = optptr + 3;
+ goto error;
+ }
+ opt->ts = optptr - iph;
+ if (skb) {
+ optptr[3] = (optptr[3]&0xF)|((overflow+1)<<4);
+ opt->is_changed = 1;
+ }
+ }
+ break;
+ case IPOPT_RA:
+ if (optlen < 4) {
+ pp_ptr = optptr + 1;
+ goto error;
+ }
+ if (optptr[2] == 0 && optptr[3] == 0)
+ opt->router_alert = optptr - iph;
+ break;
+ case IPOPT_SEC:
+ case IPOPT_SID:
+ default:
+ if (!skb && !capable(CAP_NET_RAW)) {
+ pp_ptr = optptr;
+ goto error;
+ }
+ break;
+ }
+ l -= optlen;
+ optptr += optlen;
+ }
+
+eol:
+ if (!pp_ptr)
+ return 0;
+
+error:
+ if (skb) {
+ icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((pp_ptr-iph)<<24));
+ }
+ return -EINVAL;
+}
+
+
+/*
+ * Undo all the changes done by ip_options_compile().
+ */
+
+void ip_options_undo(struct ip_options * opt)
+{
+ if (opt->srr) {
+ unsigned char * optptr = opt->__data+opt->srr-sizeof(struct iphdr);
+ memmove(optptr+7, optptr+3, optptr[1]-7);
+ memcpy(optptr+3, &opt->faddr, 4);
+ }
+ if (opt->rr_needaddr) {
+ unsigned char * optptr = opt->__data+opt->rr-sizeof(struct iphdr);
+ optptr[2] -= 4;
+ memset(&optptr[optptr[2]-1], 0, 4);
+ }
+ if (opt->ts) {
+ unsigned char * optptr = opt->__data+opt->ts-sizeof(struct iphdr);
+ if (opt->ts_needtime) {
+ optptr[2] -= 4;
+ memset(&optptr[optptr[2]-1], 0, 4);
+ if ((optptr[3]&0xF) == IPOPT_TS_PRESPEC)
+ optptr[2] -= 4;
+ }
+ if (opt->ts_needaddr) {
+ optptr[2] -= 4;
+ memset(&optptr[optptr[2]-1], 0, 4);
+ }
+ }
+}
+
+int ip_options_get(struct ip_options **optp, unsigned char *data, int optlen, int user)
+{
+ struct ip_options *opt;
+ size_t opt_size = sizeof(struct ip_options)+((optlen+3)&~3);
+
+ opt = kmalloc(opt_size, GFP_KERNEL);
+ if (!opt)
+ return -ENOMEM;
+ memset(opt, 0, sizeof(struct ip_options));
+ if (optlen) {
+ if (user) {
+ if (copy_from_user(opt->__data, data, optlen)) {
+ kfree_s(opt, opt_size);
+ return -EFAULT;
+ }
+ } else
+ memcpy(opt->__data, data, optlen);
+ }
+ while (optlen & 3)
+ opt->__data[optlen++] = IPOPT_END;
+ opt->optlen = optlen;
+ opt->is_data = 1;
+ opt->is_setbyuser = 1;
+ if (optlen && ip_options_compile(opt, NULL)) {
+ kfree_s(opt, opt_size);
+ return -EINVAL;
+ }
+ *optp = opt;
+ return 0;
+}
+
+void ip_forward_options(struct sk_buff *skb)
+{
+ struct ip_options * opt = &(IPCB(skb)->opt);
+ unsigned char * optptr;
+ struct rtable *rt = (struct rtable*)skb->dst;
+ unsigned char *raw = skb->nh.raw;
+
+ if (opt->rr_needaddr) {
+ optptr = (unsigned char *)raw + opt->rr;
+ ip_rt_get_source(&optptr[optptr[2]-5], rt);
+ opt->is_changed = 1;
+ }
+ if (opt->srr_is_hit) {
+ int srrptr, srrspace;
+
+ optptr = raw + opt->srr;
+
+ for ( srrptr=optptr[2], srrspace = optptr[1];
+ srrptr <= srrspace;
+ srrptr += 4
+ ) {
+ if (srrptr + 3 > srrspace)
+ break;
+ if (memcmp(&rt->rt_dst, &optptr[srrptr-1], 4) == 0)
+ break;
+ }
+ if (srrptr + 3 <= srrspace) {
+ opt->is_changed = 1;
+ ip_rt_get_source(&optptr[srrptr-1], rt);
+ skb->nh.iph->daddr = rt->rt_dst;
+ optptr[2] = srrptr+4;
+ } else
+ printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n");
+ if (opt->ts_needaddr) {
+ optptr = raw + opt->ts;
+ ip_rt_get_source(&optptr[optptr[2]-9], rt);
+ opt->is_changed = 1;
+ }
+ }
+ if (opt->is_changed) {
+ opt->is_changed = 0;
+ ip_send_check(skb->nh.iph);
+ }
+}
+
+int ip_options_rcv_srr(struct sk_buff *skb)
+{
+ struct ip_options *opt = &(IPCB(skb)->opt);
+ int srrspace, srrptr;
+ u32 nexthop;
+ struct iphdr *iph = skb->nh.iph;
+ unsigned char * optptr = skb->nh.raw + opt->srr;
+ struct rtable *rt = (struct rtable*)skb->dst;
+ struct rtable *rt2;
+ int err;
+
+ if (!opt->srr)
+ return 0;
+
+ if (skb->pkt_type != PACKET_HOST)
+ return -EINVAL;
+ if (rt->rt_type == RTN_UNICAST) {
+ if (!opt->is_strictroute)
+ return 0;
+ icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl(16<<24));
+ return -EINVAL;
+ }
+ if (rt->rt_type != RTN_LOCAL)
+ return -EINVAL;
+
+ for (srrptr=optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4) {
+ if (srrptr + 3 > srrspace) {
+ icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((opt->srr+2)<<24));
+ return -EINVAL;
+ }
+ memcpy(&nexthop, &optptr[srrptr-1], 4);
+
+ rt = (struct rtable*)skb->dst;
+ skb->dst = NULL;
+ err = ip_route_input(skb, nexthop, iph->saddr, iph->tos, skb->dev);
+ rt2 = (struct rtable*)skb->dst;
+ if (err || (rt2->rt_type != RTN_UNICAST && rt2->rt_type != RTN_LOCAL)) {
+ ip_rt_put(rt2);
+ skb->dst = &rt->u.dst;
+ return -EINVAL;
+ }
+ ip_rt_put(rt);
+ if (rt2->rt_type != RTN_LOCAL)
+ break;
+ /* Superfast 8) loopback forward */
+ memcpy(&iph->daddr, &optptr[srrptr-1], 4);
+ opt->is_changed = 1;
+ }
+ if (srrptr <= srrspace) {
+ opt->srr_is_hit = 1;
+ opt->is_changed = 1;
+ }
+ return 0;
+}
diff --git a/pfinet/linux-src/net/ipv4/ip_output.c b/pfinet/linux-src/net/ipv4/ip_output.c
new file mode 100644
index 00000000..f43f4ffd
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_output.c
@@ -0,0 +1,991 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * The Internet Protocol (IP) output module.
+ *
+ * Version: $Id: ip_output.c,v 1.67.2.1 1999/09/07 02:25:23 davem Exp $
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Donald Becker, <becker@super.org>
+ * Alan Cox, <Alan.Cox@linux.org>
+ * Richard Underwood
+ * Stefan Becker, <stefanb@yello.ping.de>
+ * Jorge Cwik, <jorge@laser.satlink.net>
+ * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ *
+ * See ip_input.c for original log
+ *
+ * Fixes:
+ * Alan Cox : Missing nonblock feature in ip_build_xmit.
+ * Mike Kilburn : htons() missing in ip_build_xmit.
+ * Bradford Johnson: Fix faulty handling of some frames when
+ * no route is found.
+ * Alexander Demenshin: Missing sk/skb free in ip_queue_xmit
+ * (in case if packet not accepted by
+ * output firewall rules)
+ * Mike McLagan : Routing by source
+ * Alexey Kuznetsov: use new route cache
+ * Andi Kleen: Fix broken PMTU recovery and remove
+ * some redundant tests.
+ * Vitaly E. Lavrov : Transparent proxy revived after year coma.
+ * Andi Kleen : Replace ip_reply with ip_send_reply.
+ * Andi Kleen : Split fast and slow ip_build_xmit path
+ * for decreased register pressure on x86
+ * and more readibility.
+ * Marc Boucher : When call_out_firewall returns FW_QUEUE,
+ * silently drop skb instead of failing with -EPERM.
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/config.h>
+
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+
+#include <net/snmp.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/arp.h>
+#include <net/icmp.h>
+#include <net/raw.h>
+#include <net/checksum.h>
+#include <linux/igmp.h>
+#include <linux/ip_fw.h>
+#include <linux/firewall.h>
+#include <linux/mroute.h>
+#include <linux/netlink.h>
+
+/*
+ * Shall we try to damage output packets if routing dev changes?
+ */
+
+int sysctl_ip_dynaddr = 0;
+
+
+int ip_id_count = 0;
+
+/* Generate a checksum for an outgoing IP datagram. */
+__inline__ void ip_send_check(struct iphdr *iph)
+{
+ iph->check = 0;
+ iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+}
+
+/*
+ * Add an ip header to a skbuff and send it out.
+ */
+void ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
+ u32 saddr, u32 daddr, struct ip_options *opt)
+{
+ struct rtable *rt = (struct rtable *)skb->dst;
+ struct iphdr *iph;
+ struct device *dev;
+
+ /* Build the IP header. */
+ if (opt)
+ iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr) + opt->optlen);
+ else
+ iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr));
+
+ iph->version = 4;
+ iph->ihl = 5;
+ iph->tos = sk->ip_tos;
+ iph->frag_off = 0;
+ if (ip_dont_fragment(sk, &rt->u.dst))
+ iph->frag_off |= htons(IP_DF);
+ iph->ttl = sk->ip_ttl;
+ iph->daddr = rt->rt_dst;
+ iph->saddr = rt->rt_src;
+ iph->protocol = sk->protocol;
+ iph->tot_len = htons(skb->len);
+ iph->id = htons(ip_id_count++);
+ skb->nh.iph = iph;
+
+ if (opt && opt->optlen) {
+ iph->ihl += opt->optlen>>2;
+ ip_options_build(skb, opt, daddr, rt, 0);
+ }
+
+ dev = rt->u.dst.dev;
+
+#ifdef CONFIG_FIREWALL
+ /* Now we have no better mechanism to notify about error. */
+ switch (call_out_firewall(PF_INET, dev, iph, NULL, &skb)) {
+ case FW_REJECT:
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+ /* Fall thru... */
+ case FW_BLOCK:
+ case FW_QUEUE:
+ kfree_skb(skb);
+ return;
+ }
+#endif
+
+ ip_send_check(iph);
+
+ /* Send it out. */
+ skb->dst->output(skb);
+ return;
+}
+
+int __ip_finish_output(struct sk_buff *skb)
+{
+ return ip_finish_output(skb);
+}
+
+int ip_mc_output(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+ struct rtable *rt = (struct rtable*)skb->dst;
+ struct device *dev = rt->u.dst.dev;
+
+ /*
+ * If the indicated interface is up and running, send the packet.
+ */
+
+ ip_statistics.IpOutRequests++;
+#ifdef CONFIG_IP_ROUTE_NAT
+ if (rt->rt_flags & RTCF_NAT)
+ ip_do_nat(skb);
+#endif
+
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_IP);
+
+ /*
+ * Multicasts are looped back for other local users
+ */
+
+ if (rt->rt_flags&RTCF_MULTICAST && (!sk || sk->ip_mc_loop)) {
+#ifdef CONFIG_IP_MROUTE
+ /* Small optimization: do not loopback not local frames,
+ which returned after forwarding; they will be dropped
+ by ip_mr_input in any case.
+ Note, that local frames are looped back to be delivered
+ to local recipients.
+
+ This check is duplicated in ip_mr_input at the moment.
+ */
+ if ((rt->rt_flags&RTCF_LOCAL) || !(IPCB(skb)->flags&IPSKB_FORWARDED))
+#endif
+ dev_loopback_xmit(skb);
+
+ /* Multicasts with ttl 0 must not go beyond the host */
+
+ if (skb->nh.iph->ttl == 0) {
+ kfree_skb(skb);
+ return 0;
+ }
+ }
+
+ if (rt->rt_flags&RTCF_BROADCAST)
+ dev_loopback_xmit(skb);
+
+ return ip_finish_output(skb);
+}
+
+int ip_output(struct sk_buff *skb)
+{
+#ifdef CONFIG_IP_ROUTE_NAT
+ struct rtable *rt = (struct rtable*)skb->dst;
+#endif
+
+ ip_statistics.IpOutRequests++;
+
+#ifdef CONFIG_IP_ROUTE_NAT
+ if (rt->rt_flags&RTCF_NAT)
+ ip_do_nat(skb);
+#endif
+
+ return ip_finish_output(skb);
+}
+
+/* Queues a packet to be sent, and starts the transmitter if necessary.
+ * This routine also needs to put in the total length and compute the
+ * checksum. We use to do this in two stages, ip_build_header() then
+ * this, but that scheme created a mess when routes disappeared etc.
+ * So we do it all here, and the TCP send engine has been changed to
+ * match. (No more unroutable FIN disasters, etc. wheee...) This will
+ * most likely make other reliable transport layers above IP easier
+ * to implement under Linux.
+ */
+void ip_queue_xmit(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+ struct ip_options *opt = sk->opt;
+ struct rtable *rt;
+ struct device *dev;
+ struct iphdr *iph;
+ unsigned int tot_len;
+
+ /* Make sure we can route this packet. */
+ rt = (struct rtable *) sk->dst_cache;
+ if(rt == NULL || rt->u.dst.obsolete) {
+ u32 daddr;
+
+ sk->dst_cache = NULL;
+ ip_rt_put(rt);
+
+ /* Use correct destination address if we have options. */
+ daddr = sk->daddr;
+ if(opt && opt->srr)
+ daddr = opt->faddr;
+
+ /* If this fails, retransmit mechanism of transport layer will
+ * keep trying until route appears or the connection times itself
+ * out.
+ */
+ if(ip_route_output(&rt, daddr, sk->saddr,
+ RT_TOS(sk->ip_tos) | RTO_CONN | sk->localroute,
+ sk->bound_dev_if))
+ goto drop;
+ sk->dst_cache = &rt->u.dst;
+ }
+ if(opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
+ goto no_route;
+
+ /* We have a route, so grab a reference. */
+ skb->dst = dst_clone(sk->dst_cache);
+
+ /* OK, we know where to send it, allocate and build IP header. */
+ iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));
+ iph->version = 4;
+ iph->ihl = 5;
+ iph->tos = sk->ip_tos;
+ iph->frag_off = 0;
+ iph->ttl = sk->ip_ttl;
+ iph->daddr = rt->rt_dst;
+ iph->saddr = rt->rt_src;
+ iph->protocol = sk->protocol;
+ skb->nh.iph = iph;
+ /* Transport layer set skb->h.foo itself. */
+
+ if(opt && opt->optlen) {
+ iph->ihl += opt->optlen >> 2;
+ ip_options_build(skb, opt, sk->daddr, rt, 0);
+ }
+
+ tot_len = skb->len;
+ iph->tot_len = htons(tot_len);
+ iph->id = htons(ip_id_count++);
+
+ dev = rt->u.dst.dev;
+
+#ifdef CONFIG_FIREWALL
+ /* Now we have no better mechanism to notify about error. */
+ switch (call_out_firewall(PF_INET, dev, iph, NULL, &skb)) {
+ case FW_REJECT:
+ start_bh_atomic();
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+ end_bh_atomic();
+ /* Fall thru... */
+ case FW_BLOCK:
+ case FW_QUEUE:
+ goto drop;
+ }
+#endif
+
+ /* This can happen when the transport layer has segments queued
+ * with a cached route, and by the time we get here things are
+ * re-routed to a device with a different MTU than the original
+ * device. Sick, but we must cover it.
+ */
+ if (skb_headroom(skb) < dev->hard_header_len && dev->hard_header) {
+ struct sk_buff *skb2;
+
+ skb2 = skb_realloc_headroom(skb, (dev->hard_header_len + 15) & ~15);
+ kfree_skb(skb);
+ if (skb2 == NULL)
+ return;
+ if (sk)
+ skb_set_owner_w(skb2, sk);
+ skb = skb2;
+ iph = skb->nh.iph;
+ }
+
+ /* Do we need to fragment. Again this is inefficient. We
+ * need to somehow lock the original buffer and use bits of it.
+ */
+ if (tot_len > rt->u.dst.pmtu)
+ goto fragment;
+
+ if (ip_dont_fragment(sk, &rt->u.dst))
+ iph->frag_off |= __constant_htons(IP_DF);
+
+ /* Add an IP checksum. */
+ ip_send_check(iph);
+
+ skb->priority = sk->priority;
+ skb->dst->output(skb);
+ return;
+
+fragment:
+ if (ip_dont_fragment(sk, &rt->u.dst) &&
+ tot_len > (iph->ihl<<2) + sizeof(struct tcphdr)+16) {
+ /* Reject packet ONLY if TCP might fragment
+ it itself, if were careful enough.
+ Test is not precise (f.e. it does not take sacks
+ into account). Actually, tcp should make it. --ANK (980801)
+ */
+ iph->frag_off |= __constant_htons(IP_DF);
+ NETDEBUG(printk(KERN_DEBUG "sending pkt_too_big to self\n"));
+
+ /* icmp_send is not reenterable, so that bh_atomic... --ANK */
+ start_bh_atomic();
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
+ htonl(rt->u.dst.pmtu));
+ end_bh_atomic();
+ goto drop;
+ }
+ ip_fragment(skb, skb->dst->output);
+ return;
+
+no_route:
+ sk->dst_cache = NULL;
+ ip_rt_put(rt);
+ ip_statistics.IpOutNoRoutes++;
+ /* Fall through... */
+drop:
+ kfree_skb(skb);
+}
+
+/*
+ * Build and send a packet, with as little as one copy
+ *
+ * Doesn't care much about ip options... option length can be
+ * different for fragment at 0 and other fragments.
+ *
+ * Note that the fragment at the highest offset is sent first,
+ * so the getfrag routine can fill in the TCP/UDP checksum header
+ * field in the last fragment it sends... actually it also helps
+ * the reassemblers, they can put most packets in at the head of
+ * the fragment queue, and they know the total size in advance. This
+ * last feature will measurably improve the Linux fragment handler one
+ * day.
+ *
+ * The callback has five args, an arbitrary pointer (copy of frag),
+ * the source IP address (may depend on the routing table), the
+ * destination address (char *), the offset to copy from, and the
+ * length to be copied.
+ */
+
+int ip_build_xmit_slow(struct sock *sk,
+ int getfrag (const void *,
+ char *,
+ unsigned int,
+ unsigned int),
+ const void *frag,
+ unsigned length,
+ struct ipcm_cookie *ipc,
+ struct rtable *rt,
+ int flags)
+{
+ unsigned int fraglen, maxfraglen, fragheaderlen;
+ int err;
+ int offset, mf;
+ int mtu;
+ unsigned short id;
+
+ int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
+ int nfrags=0;
+ struct ip_options *opt = ipc->opt;
+ int df = 0;
+
+ mtu = rt->u.dst.pmtu;
+ if (ip_dont_fragment(sk, &rt->u.dst))
+ df = htons(IP_DF);
+
+ length -= sizeof(struct iphdr);
+
+ if (opt) {
+ fragheaderlen = sizeof(struct iphdr) + opt->optlen;
+ maxfraglen = ((mtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;
+ } else {
+ fragheaderlen = sizeof(struct iphdr);
+
+ /*
+ * Fragheaderlen is the size of 'overhead' on each buffer. Now work
+ * out the size of the frames to send.
+ */
+
+ maxfraglen = ((mtu-sizeof(struct iphdr)) & ~7) + fragheaderlen;
+ }
+
+ if (length + fragheaderlen > 0xFFFF) {
+ ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, mtu);
+ return -EMSGSIZE;
+ }
+
+ /*
+ * Start at the end of the frame by handling the remainder.
+ */
+
+ offset = length - (length % (maxfraglen - fragheaderlen));
+
+ /*
+ * Amount of memory to allocate for final fragment.
+ */
+
+ fraglen = length - offset + fragheaderlen;
+
+ if (length-offset==0) {
+ fraglen = maxfraglen;
+ offset -= maxfraglen-fragheaderlen;
+ }
+
+
+ /*
+ * The last fragment will not have MF (more fragments) set.
+ */
+
+ mf = 0;
+
+ /*
+ * Don't fragment packets for path mtu discovery.
+ */
+
+ if (offset > 0 && df) {
+ ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, mtu);
+ return(-EMSGSIZE);
+ }
+
+ /*
+ * Lock the device lists.
+ */
+
+ dev_lock_list();
+
+ /*
+ * Get an identifier
+ */
+
+ id = htons(ip_id_count++);
+
+ /*
+ * Begin outputting the bytes.
+ */
+
+ do {
+ char *data;
+ struct sk_buff * skb;
+
+ /*
+ * Get the memory we require with some space left for alignment.
+ */
+
+ skb = sock_alloc_send_skb(sk, fraglen+hh_len+15, 0, flags&MSG_DONTWAIT, &err);
+ if (skb == NULL)
+ goto error;
+
+ /*
+ * Fill in the control structures
+ */
+
+ skb->priority = sk->priority;
+ skb->dst = dst_clone(&rt->u.dst);
+ skb_reserve(skb, hh_len);
+
+ /*
+ * Find where to start putting bytes.
+ */
+
+ data = skb_put(skb, fraglen);
+ skb->nh.iph = (struct iphdr *)data;
+
+ /*
+ * Only write IP header onto non-raw packets
+ */
+
+ {
+ struct iphdr *iph = (struct iphdr *)data;
+
+ iph->version = 4;
+ iph->ihl = 5;
+ if (opt) {
+ iph->ihl += opt->optlen>>2;
+ ip_options_build(skb, opt,
+ ipc->addr, rt, offset);
+ }
+ iph->tos = sk->ip_tos;
+ iph->tot_len = htons(fraglen - fragheaderlen + iph->ihl*4);
+ iph->id = id;
+ iph->frag_off = htons(offset>>3);
+ iph->frag_off |= mf|df;
+ if (rt->rt_type == RTN_MULTICAST)
+ iph->ttl = sk->ip_mc_ttl;
+ else
+ iph->ttl = sk->ip_ttl;
+ iph->protocol = sk->protocol;
+ iph->check = 0;
+ iph->saddr = rt->rt_src;
+ iph->daddr = rt->rt_dst;
+ iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+ data += iph->ihl*4;
+
+ /*
+ * Any further fragments will have MF set.
+ */
+
+ mf = htons(IP_MF);
+ }
+
+ /*
+ * User data callback
+ */
+
+ if (getfrag(frag, data, offset, fraglen-fragheaderlen)) {
+ err = -EFAULT;
+ kfree_skb(skb);
+ goto error;
+ }
+
+ offset -= (maxfraglen-fragheaderlen);
+ fraglen = maxfraglen;
+
+ nfrags++;
+
+#ifdef CONFIG_FIREWALL
+ switch (call_out_firewall(PF_INET, rt->u.dst.dev, skb->nh.iph, NULL, &skb)) {
+ case FW_QUEUE:
+ kfree_skb(skb);
+ continue;
+ case FW_BLOCK:
+ case FW_REJECT:
+ kfree_skb(skb);
+ err = -EPERM;
+ goto error;
+ }
+#endif
+
+ err = -ENETDOWN;
+ if (rt->u.dst.output(skb))
+ goto error;
+ } while (offset >= 0);
+
+ if (nfrags>1)
+ ip_statistics.IpFragCreates += nfrags;
+ dev_unlock_list();
+ return 0;
+
+error:
+ ip_statistics.IpOutDiscards++;
+ if (nfrags>1)
+ ip_statistics.IpFragCreates += nfrags;
+ dev_unlock_list();
+ return err;
+}
+
+
+/*
+ * Fast path for unfragmented packets.
+ */
+int ip_build_xmit(struct sock *sk,
+ int getfrag (const void *,
+ char *,
+ unsigned int,
+ unsigned int),
+ const void *frag,
+ unsigned length,
+ struct ipcm_cookie *ipc,
+ struct rtable *rt,
+ int flags)
+{
+ int err;
+ struct sk_buff *skb;
+ int df;
+ struct iphdr *iph;
+
+ /*
+ * Try the simple case first. This leaves fragmented frames, and by
+ * choice RAW frames within 20 bytes of maximum size(rare) to the long path
+ */
+
+ if (!sk->ip_hdrincl) {
+ length += sizeof(struct iphdr);
+
+ /*
+ * Check for slow path.
+ */
+ if (length > rt->u.dst.pmtu || ipc->opt != NULL)
+ return ip_build_xmit_slow(sk,getfrag,frag,length,ipc,rt,flags);
+ } else {
+ if (length > rt->u.dst.dev->mtu) {
+ ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, rt->u.dst.dev->mtu);
+ return -EMSGSIZE;
+ }
+ }
+
+ /*
+ * Do path mtu discovery if needed.
+ */
+ df = 0;
+ if (ip_dont_fragment(sk, &rt->u.dst))
+ df = htons(IP_DF);
+
+ /*
+ * Fast path for unfragmented frames without options.
+ */
+ {
+ int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
+
+ skb = sock_alloc_send_skb(sk, length+hh_len+15,
+ 0, flags&MSG_DONTWAIT, &err);
+ if(skb==NULL)
+ goto error;
+ skb_reserve(skb, hh_len);
+ }
+
+ skb->priority = sk->priority;
+ skb->dst = dst_clone(&rt->u.dst);
+
+ skb->nh.iph = iph = (struct iphdr *)skb_put(skb, length);
+
+ dev_lock_list();
+
+ if(!sk->ip_hdrincl) {
+ iph->version=4;
+ iph->ihl=5;
+ iph->tos=sk->ip_tos;
+ iph->tot_len = htons(length);
+ iph->id=htons(ip_id_count++);
+ iph->frag_off = df;
+ iph->ttl=sk->ip_mc_ttl;
+ if (rt->rt_type != RTN_MULTICAST)
+ iph->ttl=sk->ip_ttl;
+ iph->protocol=sk->protocol;
+ iph->saddr=rt->rt_src;
+ iph->daddr=rt->rt_dst;
+ iph->check=0;
+ iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+ err = getfrag(frag, ((char *)iph)+iph->ihl*4,0, length-iph->ihl*4);
+ }
+ else
+ err = getfrag(frag, (void *)iph, 0, length);
+
+ dev_unlock_list();
+
+ if (err)
+ goto error_fault;
+
+#ifdef CONFIG_FIREWALL
+ switch (call_out_firewall(PF_INET, rt->u.dst.dev, iph, NULL, &skb)) {
+ case FW_QUEUE:
+ kfree_skb(skb);
+ return 0;
+ case FW_BLOCK:
+ case FW_REJECT:
+ kfree_skb(skb);
+ err = -EPERM;
+ goto error;
+ }
+#endif
+
+ return rt->u.dst.output(skb);
+
+error_fault:
+ err = -EFAULT;
+ kfree_skb(skb);
+error:
+ ip_statistics.IpOutDiscards++;
+ return err;
+}
+
+
+
+/*
+ * This IP datagram is too large to be sent in one piece. Break it up into
+ * smaller pieces (each of size equal to IP header plus
+ * a block of the data of the original IP data part) that will yet fit in a
+ * single device frame, and queue such a frame for sending.
+ *
+ * Yes this is inefficient, feel free to submit a quicker one.
+ */
+
+void ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
+{
+ struct iphdr *iph;
+ unsigned char *raw;
+ unsigned char *ptr;
+ struct device *dev;
+ struct sk_buff *skb2;
+ unsigned int mtu, hlen, left, len;
+ int offset;
+ int not_last_frag;
+ struct rtable *rt = (struct rtable*)skb->dst;
+
+ dev = rt->u.dst.dev;
+
+ /*
+ * Point into the IP datagram header.
+ */
+
+ raw = skb->nh.raw;
+ iph = (struct iphdr*)raw;
+
+ /*
+ * Setup starting values.
+ */
+
+ hlen = iph->ihl * 4;
+ left = ntohs(iph->tot_len) - hlen; /* Space per frame */
+ mtu = rt->u.dst.pmtu - hlen; /* Size of data space */
+ ptr = raw + hlen; /* Where to start from */
+
+ /*
+ * The protocol doesn't seem to say what to do in the case that the
+ * frame + options doesn't fit the mtu. As it used to fall down dead
+ * in this case we were fortunate it didn't happen
+ *
+ * It is impossible, because mtu>=68. --ANK (980801)
+ */
+
+#ifdef CONFIG_NET_PARANOIA
+ if (mtu<8)
+ goto fail;
+#endif
+
+ /*
+ * Fragment the datagram.
+ */
+
+ offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
+ not_last_frag = iph->frag_off & htons(IP_MF);
+
+ /*
+ * Keep copying data until we run out.
+ */
+
+ while(left > 0) {
+ len = left;
+ /* IF: it doesn't fit, use 'mtu' - the data space left */
+ if (len > mtu)
+ len = mtu;
+ /* IF: we are not sending up to and including the packet end
+ then align the next start on an eight byte boundary */
+ if (len < left) {
+ len &= ~7;
+ }
+ /*
+ * Allocate buffer.
+ */
+
+ if ((skb2 = alloc_skb(len+hlen+dev->hard_header_len+15,GFP_ATOMIC)) == NULL) {
+ NETDEBUG(printk(KERN_INFO "IP: frag: no memory for new fragment!\n"));
+ goto fail;
+ }
+
+ /*
+ * Set up data on packet
+ */
+
+ skb2->pkt_type = skb->pkt_type;
+ skb2->priority = skb->priority;
+ skb_reserve(skb2, (dev->hard_header_len+15)&~15);
+ skb_put(skb2, len + hlen);
+ skb2->nh.raw = skb2->data;
+ skb2->h.raw = skb2->data + hlen;
+
+ /*
+ * Charge the memory for the fragment to any owner
+ * it might possess
+ */
+
+ if (skb->sk)
+ skb_set_owner_w(skb2, skb->sk);
+ skb2->dst = dst_clone(skb->dst);
+
+ /*
+ * Copy the packet header into the new buffer.
+ */
+
+ memcpy(skb2->nh.raw, raw, hlen);
+
+ /*
+ * Copy a block of the IP datagram.
+ */
+ memcpy(skb2->h.raw, ptr, len);
+ left -= len;
+
+ /*
+ * Fill in the new header fields.
+ */
+ iph = skb2->nh.iph;
+ iph->frag_off = htons((offset >> 3));
+
+ /* ANK: dirty, but effective trick. Upgrade options only if
+ * the segment to be fragmented was THE FIRST (otherwise,
+ * options are already fixed) and make it ONCE
+ * on the initial skb, so that all the following fragments
+ * will inherit fixed options.
+ */
+ if (offset == 0)
+ ip_options_fragment(skb);
+
+ /*
+ * Added AC : If we are fragmenting a fragment that's not the
+ * last fragment then keep MF on each bit
+ */
+ if (left > 0 || not_last_frag)
+ iph->frag_off |= htons(IP_MF);
+ ptr += len;
+ offset += len;
+
+ /*
+ * Put this fragment into the sending queue.
+ */
+
+ ip_statistics.IpFragCreates++;
+
+ iph->tot_len = htons(len + hlen);
+
+ ip_send_check(iph);
+
+ output(skb2);
+ }
+ kfree_skb(skb);
+ ip_statistics.IpFragOKs++;
+ return;
+
+fail:
+ kfree_skb(skb);
+ ip_statistics.IpFragFails++;
+}
+
+/*
+ * Fetch data from kernel space and fill in checksum if needed.
+ */
+static int ip_reply_glue_bits(const void *dptr, char *to, unsigned int offset,
+ unsigned int fraglen)
+{
+ struct ip_reply_arg *dp = (struct ip_reply_arg*)dptr;
+ u16 *pktp = (u16 *)to;
+ struct iovec *iov;
+ int len;
+ int hdrflag = 1;
+
+ iov = &dp->iov[0];
+ if (offset >= iov->iov_len) {
+ offset -= iov->iov_len;
+ iov++;
+ hdrflag = 0;
+ }
+ len = iov->iov_len - offset;
+ if (fraglen > len) { /* overlapping. */
+ dp->csum = csum_partial_copy_nocheck(iov->iov_base+offset, to, len,
+ dp->csum);
+ offset = 0;
+ fraglen -= len;
+ to += len;
+ iov++;
+ }
+
+ dp->csum = csum_partial_copy_nocheck(iov->iov_base+offset, to, fraglen,
+ dp->csum);
+
+ if (hdrflag && dp->csumoffset)
+ *(pktp + dp->csumoffset) = csum_fold(dp->csum); /* fill in checksum */
+ return 0;
+}
+
+/*
+ * Generic function to send a packet as reply to another packet.
+ * Used to send TCP resets so far. ICMP should use this function too.
+ *
+ * Should run single threaded per socket because it uses the sock
+ * structure to pass arguments.
+ */
+void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *arg,
+ unsigned int len)
+{
+ struct {
+ struct ip_options opt;
+ char data[40];
+ } replyopts;
+ struct ipcm_cookie ipc;
+ u32 daddr;
+ struct rtable *rt = (struct rtable*)skb->dst;
+
+ if (ip_options_echo(&replyopts.opt, skb))
+ return;
+
+ sk->ip_tos = skb->nh.iph->tos;
+ sk->priority = skb->priority;
+ sk->protocol = skb->nh.iph->protocol;
+
+ daddr = ipc.addr = rt->rt_src;
+ ipc.opt = &replyopts.opt;
+
+ if (ipc.opt->srr)
+ daddr = replyopts.opt.faddr;
+ if (ip_route_output(&rt, daddr, rt->rt_spec_dst, RT_TOS(skb->nh.iph->tos), 0))
+ return;
+
+ /* And let IP do all the hard work. */
+ ip_build_xmit(sk, ip_reply_glue_bits, arg, len, &ipc, rt, MSG_DONTWAIT);
+ ip_rt_put(rt);
+}
+
+/*
+ * IP protocol layer initialiser
+ */
+
+static struct packet_type ip_packet_type =
+{
+ __constant_htons(ETH_P_IP),
+ NULL, /* All devices */
+ ip_rcv,
+ NULL,
+ NULL,
+};
+
+
+
+#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_IP_MULTICAST
+static struct proc_dir_entry proc_net_igmp = {
+ PROC_NET_IGMP, 4, "igmp",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ ip_mc_procinfo
+};
+#endif
+#endif
+
+/*
+ * IP registers the packet type and then calls the subprotocol initialisers
+ */
+
+__initfunc(void ip_init(void))
+{
+ dev_add_pack(&ip_packet_type);
+
+ ip_rt_init();
+
+#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_IP_MULTICAST
+ proc_net_register(&proc_net_igmp);
+#endif
+#endif
+}
diff --git a/pfinet/linux-src/net/ipv4/ip_sockglue.c b/pfinet/linux-src/net/ipv4/ip_sockglue.c
new file mode 100644
index 00000000..369a6770
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ip_sockglue.c
@@ -0,0 +1,739 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * The IP to API glue.
+ *
+ * Version: $Id: ip_sockglue.c,v 1.42 1999/04/22 10:07:34 davem Exp $
+ *
+ * Authors: see ip.c
+ *
+ * Fixes:
+ * Many : Split from ip.c , see ip.c for history.
+ * Martin Mares : TOS setting fixed.
+ * Alan Cox : Fixed a couple of oopses in Martin's
+ * TOS tweaks.
+ * Mike McLagan : Routing by source
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
+#include <linux/netdevice.h>
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <net/tcp.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/igmp.h>
+#include <linux/firewall.h>
+#include <linux/ip_fw.h>
+#include <linux/route.h>
+#include <linux/mroute.h>
+#include <net/route.h>
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#include <net/transp_v6.h>
+#endif
+
+#ifdef CONFIG_IP_MASQUERADE
+#include <linux/ip_masq.h>
+#endif
+
+#include <linux/errqueue.h>
+#include <asm/uaccess.h>
+
+#define MAX(a,b) ((a)>(b)?(a):(b))
+
+#define IP_CMSG_PKTINFO 1
+#define IP_CMSG_TTL 2
+#define IP_CMSG_TOS 4
+#define IP_CMSG_RECVOPTS 8
+#define IP_CMSG_RETOPTS 16
+
+/*
+ * SOL_IP control messages.
+ */
+
+static void ip_cmsg_recv_pktinfo(struct msghdr *msg, struct sk_buff *skb)
+{
+ struct in_pktinfo info;
+ struct rtable *rt = (struct rtable *)skb->dst;
+
+ info.ipi_addr.s_addr = skb->nh.iph->daddr;
+ if (rt) {
+ info.ipi_ifindex = rt->rt_iif;
+ info.ipi_spec_dst.s_addr = rt->rt_spec_dst;
+ } else {
+ info.ipi_ifindex = 0;
+ info.ipi_spec_dst.s_addr = 0;
+ }
+
+ put_cmsg(msg, SOL_IP, IP_PKTINFO, sizeof(info), &info);
+}
+
+static void ip_cmsg_recv_ttl(struct msghdr *msg, struct sk_buff *skb)
+{
+ int ttl = skb->nh.iph->ttl;
+ put_cmsg(msg, SOL_IP, IP_TTL, sizeof(int), &ttl);
+}
+
+static void ip_cmsg_recv_tos(struct msghdr *msg, struct sk_buff *skb)
+{
+ put_cmsg(msg, SOL_IP, IP_TOS, 1, &skb->nh.iph->tos);
+}
+
+static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb)
+{
+ if (IPCB(skb)->opt.optlen == 0)
+ return;
+
+ put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen, skb->nh.iph+1);
+}
+
+
+void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
+{
+ unsigned char optbuf[sizeof(struct ip_options) + 40];
+ struct ip_options * opt = (struct ip_options*)optbuf;
+
+ if (IPCB(skb)->opt.optlen == 0)
+ return;
+
+ if (ip_options_echo(opt, skb)) {
+ msg->msg_flags |= MSG_CTRUNC;
+ return;
+ }
+ ip_options_undo(opt);
+
+ put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data);
+}
+
+
+void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
+{
+ unsigned flags = skb->sk->ip_cmsg_flags;
+
+ /* Ordered by supposed usage frequency */
+ if (flags & 1)
+ ip_cmsg_recv_pktinfo(msg, skb);
+ if ((flags>>=1) == 0)
+ return;
+
+ if (flags & 1)
+ ip_cmsg_recv_ttl(msg, skb);
+ if ((flags>>=1) == 0)
+ return;
+
+ if (flags & 1)
+ ip_cmsg_recv_tos(msg, skb);
+ if ((flags>>=1) == 0)
+ return;
+
+ if (flags & 1)
+ ip_cmsg_recv_opts(msg, skb);
+ if ((flags>>=1) == 0)
+ return;
+
+ if (flags & 1)
+ ip_cmsg_recv_retopts(msg, skb);
+}
+
+int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc)
+{
+ int err;
+ struct cmsghdr *cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
+ (unsigned long)(((char*)cmsg - (char*)msg->msg_control)
+ + cmsg->cmsg_len) > msg->msg_controllen) {
+ return -EINVAL;
+ }
+ if (cmsg->cmsg_level != SOL_IP)
+ continue;
+ switch (cmsg->cmsg_type) {
+ case IP_RETOPTS:
+ err = cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr));
+ err = ip_options_get(&ipc->opt, CMSG_DATA(cmsg), err < 40 ? err : 40, 0);
+ if (err)
+ return err;
+ break;
+ case IP_PKTINFO:
+ {
+ struct in_pktinfo *info;
+ if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct in_pktinfo)))
+ return -EINVAL;
+ info = (struct in_pktinfo *)CMSG_DATA(cmsg);
+ ipc->oif = info->ipi_ifindex;
+ ipc->addr = info->ipi_spec_dst.s_addr;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+
+/* Special input handler for packets catched by router alert option.
+ They are selected only by protocol field, and then processed likely
+ local ones; but only if someone wants them! Otherwise, router
+ not running rsvpd will kill RSVP.
+
+ It is user level problem, what it will make with them.
+ I have no idea, how it will masquearde or NAT them (it is joke, joke :-)),
+ but receiver should be enough clever f.e. to forward mtrace requests,
+ sent to multicast group to reach destination designated router.
+ */
+struct ip_ra_chain *ip_ra_chain;
+
+int ip_ra_control(struct sock *sk, unsigned char on, void (*destructor)(struct sock *))
+{
+ struct ip_ra_chain *ra, *new_ra, **rap;
+
+ if (sk->type != SOCK_RAW || sk->num == IPPROTO_RAW)
+ return -EINVAL;
+
+ new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
+
+ for (rap = &ip_ra_chain; (ra=*rap) != NULL; rap = &ra->next) {
+ if (ra->sk == sk) {
+ if (on) {
+ if (new_ra)
+ kfree(new_ra);
+ return -EADDRINUSE;
+ }
+ *rap = ra->next;
+ synchronize_bh();
+
+ if (ra->destructor)
+ ra->destructor(sk);
+ kfree(ra);
+ return 0;
+ }
+ }
+ if (new_ra == NULL)
+ return -ENOBUFS;
+ new_ra->sk = sk;
+ new_ra->destructor = destructor;
+
+ new_ra->next = ra;
+ wmb();
+ *rap = new_ra;
+
+ return 0;
+}
+
+void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
+ u16 port, u32 info, u8 *payload)
+{
+ struct sock_exterr_skb *serr;
+
+ if (!sk->ip_recverr)
+ return;
+
+ skb = skb_clone(skb, GFP_ATOMIC);
+ if (!skb)
+ return;
+
+ serr = SKB_EXT_ERR(skb);
+ serr->ee.ee_errno = err;
+ serr->ee.ee_origin = SO_EE_ORIGIN_ICMP;
+ serr->ee.ee_type = skb->h.icmph->type;
+ serr->ee.ee_code = skb->h.icmph->code;
+ serr->ee.ee_pad = 0;
+ serr->ee.ee_info = info;
+ serr->ee.ee_data = 0;
+ serr->addr_offset = (u8*)&(((struct iphdr*)(skb->h.icmph+1))->daddr) - skb->nh.raw;
+ serr->port = port;
+
+ skb->h.raw = payload;
+ skb_pull(skb, payload - skb->data);
+
+ if (sock_queue_err_skb(sk, skb))
+ kfree_skb(skb);
+}
+
+void ip_local_error(struct sock *sk, int err, u32 daddr, u16 port, u32 info)
+{
+ struct sock_exterr_skb *serr;
+ struct iphdr *iph;
+ struct sk_buff *skb;
+
+ if (!sk->ip_recverr)
+ return;
+
+ skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC);
+ if (!skb)
+ return;
+
+ iph = (struct iphdr*)skb_put(skb, sizeof(struct iphdr));
+ skb->nh.iph = iph;
+ iph->daddr = daddr;
+
+ serr = SKB_EXT_ERR(skb);
+ serr->ee.ee_errno = err;
+ serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL;
+ serr->ee.ee_type = 0;
+ serr->ee.ee_code = 0;
+ serr->ee.ee_pad = 0;
+ serr->ee.ee_info = info;
+ serr->ee.ee_data = 0;
+ serr->addr_offset = (u8*)&iph->daddr - skb->nh.raw;
+ serr->port = port;
+
+ skb->h.raw = skb->tail;
+ skb_pull(skb, skb->tail - skb->data);
+
+ if (sock_queue_err_skb(sk, skb))
+ kfree_skb(skb);
+}
+
+/*
+ * Handle MSG_ERRQUEUE
+ */
+int ip_recv_error(struct sock *sk, struct msghdr *msg, int len)
+{
+ struct sock_exterr_skb *serr;
+ struct sk_buff *skb, *skb2;
+ struct sockaddr_in *sin;
+ struct {
+ struct sock_extended_err ee;
+ struct sockaddr_in offender;
+ } errhdr;
+ int err;
+ int copied;
+
+ err = -EAGAIN;
+ skb = skb_dequeue(&sk->error_queue);
+ if (skb == NULL)
+ goto out;
+
+ copied = skb->len;
+ if (copied > len) {
+ msg->msg_flags |= MSG_TRUNC;
+ copied = len;
+ }
+ err = memcpy_toiovec(msg->msg_iov, skb->data, copied);
+ if (err)
+ goto out_free_skb;
+
+ serr = SKB_EXT_ERR(skb);
+
+ sin = (struct sockaddr_in *)msg->msg_name;
+ if (sin) {
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = *(u32*)(skb->nh.raw + serr->addr_offset);
+ sin->sin_port = serr->port;
+ }
+
+ memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
+ sin = &errhdr.offender;
+ sin->sin_family = AF_UNSPEC;
+ if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP) {
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = skb->nh.iph->saddr;
+ if (sk->ip_cmsg_flags)
+ ip_cmsg_recv(msg, skb);
+ }
+
+ put_cmsg(msg, SOL_IP, IP_RECVERR, sizeof(errhdr), &errhdr);
+
+ /* Now we could try to dump offended packet options */
+
+ msg->msg_flags |= MSG_ERRQUEUE;
+ err = copied;
+
+ /* Reset and regenerate socket error */
+ sk->err = 0;
+ if ((skb2 = skb_peek(&sk->error_queue)) != NULL) {
+ sk->err = SKB_EXT_ERR(skb2)->ee.ee_errno;
+ sk->error_report(sk);
+ }
+
+out_free_skb:
+ kfree_skb(skb);
+out:
+ return err;
+}
+
+
+/*
+ * Socket option code for IP. This is the end of the line after any TCP,UDP etc options on
+ * an IP socket.
+ *
+ * We implement IP_TOS (type of service), IP_TTL (time to live).
+ */
+
+int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
+{
+ int val=0,err;
+#if defined(CONFIG_IP_FIREWALL)
+ char tmp_fw[MAX(sizeof(struct ip_fwtest),sizeof(struct ip_fwnew))];
+#endif
+ if(optlen>=sizeof(int)) {
+ if(get_user(val, (int *) optval))
+ return -EFAULT;
+ } else if(optlen>=sizeof(char)) {
+ unsigned char ucval;
+ if(get_user(ucval, (unsigned char *) optval))
+ return -EFAULT;
+ val = (int)ucval;
+ }
+ /* If optlen==0, it is equivalent to val == 0 */
+
+ if(level!=SOL_IP)
+ return -ENOPROTOOPT;
+#ifdef CONFIG_IP_MROUTE
+ if(optname>=MRT_BASE && optname <=MRT_BASE+10)
+ {
+ return ip_mroute_setsockopt(sk,optname,optval,optlen);
+ }
+#endif
+
+ switch(optname)
+ {
+ case IP_OPTIONS:
+ {
+ struct ip_options * opt = NULL;
+ if (optlen > 40 || optlen < 0)
+ return -EINVAL;
+ err = ip_options_get(&opt, optval, optlen, 1);
+ if (err)
+ return err;
+ lock_sock(sk);
+ if (sk->type == SOCK_STREAM) {
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ if (sk->family == PF_INET ||
+ ((tcp_connected(sk->state) || sk->state == TCP_SYN_SENT)
+ && sk->daddr != LOOPBACK4_IPV6)) {
+#endif
+ if (opt)
+ tp->ext_header_len = opt->optlen;
+ tcp_sync_mss(sk, tp->pmtu_cookie);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ }
+#endif
+ }
+ opt = xchg(&sk->opt, opt);
+ release_sock(sk);
+ if (opt)
+ kfree_s(opt, sizeof(struct ip_options) + opt->optlen);
+ return 0;
+ }
+ case IP_PKTINFO:
+ if (val)
+ sk->ip_cmsg_flags |= IP_CMSG_PKTINFO;
+ else
+ sk->ip_cmsg_flags &= ~IP_CMSG_PKTINFO;
+ return 0;
+ case IP_RECVTTL:
+ if (val)
+ sk->ip_cmsg_flags |= IP_CMSG_TTL;
+ else
+ sk->ip_cmsg_flags &= ~IP_CMSG_TTL;
+ return 0;
+ case IP_RECVTOS:
+ if (val)
+ sk->ip_cmsg_flags |= IP_CMSG_TOS;
+ else
+ sk->ip_cmsg_flags &= ~IP_CMSG_TOS;
+ return 0;
+ case IP_RECVOPTS:
+ if (val)
+ sk->ip_cmsg_flags |= IP_CMSG_RECVOPTS;
+ else
+ sk->ip_cmsg_flags &= ~IP_CMSG_RECVOPTS;
+ return 0;
+ case IP_RETOPTS:
+ if (val)
+ sk->ip_cmsg_flags |= IP_CMSG_RETOPTS;
+ else
+ sk->ip_cmsg_flags &= ~IP_CMSG_RETOPTS;
+ return 0;
+ case IP_TOS: /* This sets both TOS and Precedence */
+ /* Reject setting of unused bits */
+ if (val & ~(IPTOS_TOS_MASK|IPTOS_PREC_MASK))
+ return -EINVAL;
+ if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP &&
+ !capable(CAP_NET_ADMIN))
+ return -EPERM;
+ if (sk->ip_tos != val) {
+ lock_sock(sk);
+ sk->ip_tos=val;
+ sk->priority = rt_tos2priority(val);
+ dst_release(xchg(&sk->dst_cache, NULL));
+ release_sock(sk);
+ }
+ return 0;
+ case IP_TTL:
+ if (optlen<1)
+ return -EINVAL;
+ if(val==-1)
+ val = ip_statistics.IpDefaultTTL;
+ if(val<1||val>255)
+ return -EINVAL;
+ sk->ip_ttl=val;
+ return 0;
+ case IP_HDRINCL:
+ if(sk->type!=SOCK_RAW)
+ return -ENOPROTOOPT;
+ sk->ip_hdrincl=val?1:0;
+ return 0;
+ case IP_MTU_DISCOVER:
+ if (val<0 || val>2)
+ return -EINVAL;
+ sk->ip_pmtudisc = val;
+ return 0;
+ case IP_RECVERR:
+ sk->ip_recverr = !!val;
+ if (!val)
+ skb_queue_purge(&sk->error_queue);
+ return 0;
+ case IP_MULTICAST_TTL:
+ if (optlen<1)
+ return -EINVAL;
+ if (val==-1)
+ val = 1;
+ if (val < 0 || val > 255)
+ return -EINVAL;
+ sk->ip_mc_ttl=val;
+ return 0;
+ case IP_MULTICAST_LOOP:
+ if (optlen<1)
+ return -EINVAL;
+ sk->ip_mc_loop = val ? 1 : 0;
+ return 0;
+ case IP_MULTICAST_IF:
+ {
+ struct ip_mreqn mreq;
+ struct device *dev = NULL;
+
+ /*
+ * Check the arguments are allowable
+ */
+
+ if (optlen >= sizeof(struct ip_mreqn)) {
+ if (copy_from_user(&mreq,optval,sizeof(mreq)))
+ return -EFAULT;
+ } else {
+ memset(&mreq, 0, sizeof(mreq));
+ if (optlen >= sizeof(struct in_addr) &&
+ copy_from_user(&mreq.imr_address,optval,sizeof(struct in_addr)))
+ return -EFAULT;
+ }
+
+ if (!mreq.imr_ifindex) {
+ if (mreq.imr_address.s_addr == INADDR_ANY) {
+ sk->ip_mc_index = 0;
+ sk->ip_mc_addr = 0;
+ return 0;
+ }
+ dev = ip_dev_find(mreq.imr_address.s_addr);
+ } else
+ dev = dev_get_by_index(mreq.imr_ifindex);
+
+ if (!dev)
+ return -EADDRNOTAVAIL;
+
+ if (sk->bound_dev_if && dev->ifindex != sk->bound_dev_if)
+ return -EINVAL;
+
+ sk->ip_mc_index = mreq.imr_ifindex;
+ sk->ip_mc_addr = mreq.imr_address.s_addr;
+ return 0;
+ }
+
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ {
+ struct ip_mreqn mreq;
+
+ if (optlen < sizeof(struct ip_mreq))
+ return -EINVAL;
+ if (optlen >= sizeof(struct ip_mreqn)) {
+ if(copy_from_user(&mreq,optval,sizeof(mreq)))
+ return -EFAULT;
+ } else {
+ memset(&mreq, 0, sizeof(mreq));
+ if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq)))
+ return -EFAULT;
+ }
+
+ if (optname == IP_ADD_MEMBERSHIP)
+ return ip_mc_join_group(sk,&mreq);
+ else
+ return ip_mc_leave_group(sk,&mreq);
+ }
+ case IP_ROUTER_ALERT:
+ return ip_ra_control(sk, val ? 1 : 0, NULL);
+
+#ifdef CONFIG_IP_FIREWALL
+ case IP_FW_MASQ_TIMEOUTS:
+ case IP_FW_APPEND:
+ case IP_FW_REPLACE:
+ case IP_FW_DELETE:
+ case IP_FW_DELETE_NUM:
+ case IP_FW_INSERT:
+ case IP_FW_FLUSH:
+ case IP_FW_ZERO:
+ case IP_FW_CHECK:
+ case IP_FW_CREATECHAIN:
+ case IP_FW_DELETECHAIN:
+ case IP_FW_POLICY:
+ if(!capable(CAP_NET_ADMIN))
+ return -EACCES;
+ if(optlen>sizeof(tmp_fw) || optlen<1)
+ return -EINVAL;
+ if(copy_from_user(&tmp_fw,optval,optlen))
+ return -EFAULT;
+ err=ip_fw_ctl(optname, &tmp_fw,optlen);
+ return -err; /* -0 is 0 after all */
+#endif /* CONFIG_IP_FIREWALL */
+#ifdef CONFIG_IP_MASQUERADE
+ case IP_FW_MASQ_CTL:
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ if(optlen<1)
+ return -EINVAL;
+ err=ip_masq_uctl(optname, optval ,optlen);
+ return err;
+
+#endif
+ default:
+ return(-ENOPROTOOPT);
+ }
+}
+
+/*
+ * Get the options. Note for future reference. The GET of IP options gets the
+ * _received_ ones. The set sets the _sent_ ones.
+ */
+
+int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen)
+{
+ int val;
+ int len;
+
+ if(level!=SOL_IP)
+ return -EOPNOTSUPP;
+
+#ifdef CONFIG_IP_MROUTE
+ if(optname>=MRT_BASE && optname <=MRT_BASE+10)
+ {
+ return ip_mroute_getsockopt(sk,optname,optval,optlen);
+ }
+#endif
+
+ if(get_user(len,optlen))
+ return -EFAULT;
+
+ switch(optname)
+ {
+ case IP_OPTIONS:
+ {
+ unsigned char optbuf[sizeof(struct ip_options)+40];
+ struct ip_options * opt = (struct ip_options*)optbuf;
+ lock_sock(sk);
+ opt->optlen = 0;
+ if (sk->opt)
+ memcpy(optbuf, sk->opt, sizeof(struct ip_options)+sk->opt->optlen);
+ release_sock(sk);
+ if (opt->optlen == 0)
+ return put_user(0, optlen);
+
+ ip_options_undo(opt);
+
+ len=min(len, opt->optlen);
+ if(put_user(len, optlen))
+ return -EFAULT;
+ if(copy_to_user(optval, opt->__data, len))
+ return -EFAULT;
+ return 0;
+ }
+ case IP_PKTINFO:
+ val = (sk->ip_cmsg_flags & IP_CMSG_PKTINFO) != 0;
+ break;
+ case IP_RECVTTL:
+ val = (sk->ip_cmsg_flags & IP_CMSG_TTL) != 0;
+ break;
+ case IP_RECVTOS:
+ val = (sk->ip_cmsg_flags & IP_CMSG_TOS) != 0;
+ break;
+ case IP_RECVOPTS:
+ val = (sk->ip_cmsg_flags & IP_CMSG_RECVOPTS) != 0;
+ break;
+ case IP_RETOPTS:
+ val = (sk->ip_cmsg_flags & IP_CMSG_RETOPTS) != 0;
+ break;
+ case IP_TOS:
+ val=sk->ip_tos;
+ break;
+ case IP_TTL:
+ val=sk->ip_ttl;
+ break;
+ case IP_HDRINCL:
+ val=sk->ip_hdrincl;
+ break;
+ case IP_MTU_DISCOVER:
+ val=sk->ip_pmtudisc;
+ break;
+ case IP_MTU:
+ val = 0;
+ lock_sock(sk);
+ if (sk->dst_cache)
+ val = sk->dst_cache->pmtu;
+ release_sock(sk);
+ if (!val)
+ return -ENOTCONN;
+ break;
+ case IP_RECVERR:
+ val=sk->ip_recverr;
+ break;
+ case IP_MULTICAST_TTL:
+ val=sk->ip_mc_ttl;
+ break;
+ case IP_MULTICAST_LOOP:
+ val=sk->ip_mc_loop;
+ break;
+ case IP_MULTICAST_IF:
+ {
+ struct ip_mreqn mreq;
+ len = min(len,sizeof(struct ip_mreqn));
+ if(put_user(len, optlen))
+ return -EFAULT;
+ mreq.imr_ifindex = sk->ip_mc_index;
+ mreq.imr_address.s_addr = sk->ip_mc_addr;
+ mreq.imr_multiaddr.s_addr = 0;
+ if(copy_to_user((void *)optval, &mreq, len))
+ return -EFAULT;
+ return 0;
+ }
+ default:
+ return(-ENOPROTOOPT);
+ }
+
+ if (len < sizeof(int) && len > 0 && val>=0 && val<255) {
+ unsigned char ucval = (unsigned char)val;
+ len = 1;
+ if(put_user(len, optlen))
+ return -EFAULT;
+ if(copy_to_user(optval,&ucval,1))
+ return -EFAULT;
+ } else {
+ len=min(sizeof(int),len);
+ if(put_user(len, optlen))
+ return -EFAULT;
+ if(copy_to_user(optval,&val,len))
+ return -EFAULT;
+ }
+ return 0;
+}
diff --git a/pfinet/linux-src/net/ipv4/ipconfig.c b/pfinet/linux-src/net/ipv4/ipconfig.c
new file mode 100644
index 00000000..bb95824c
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ipconfig.c
@@ -0,0 +1,970 @@
+/*
+ * $Id: ipconfig.c,v 1.20.2.1 1999/06/28 11:33:27 davem Exp $
+ *
+ * Automatic Configuration of IP -- use BOOTP or RARP or user-supplied
+ * information to configure own IP address and routes.
+ *
+ * Copyright (C) 1996--1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ *
+ * Derived from network configuration code in fs/nfs/nfsroot.c,
+ * originally Copyright (C) 1995, 1996 Gero Kuhlmann and me.
+ *
+ * BOOTP rewritten to construct and analyse packets itself instead
+ * of misusing the IP layer. num_bugs_causing_wrong_arp_replies--;
+ * -- MJ, December 1998
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/random.h>
+#include <linux/init.h>
+#include <linux/utsname.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/socket.h>
+#include <linux/route.h>
+#include <linux/udp.h>
+#include <net/arp.h>
+#include <net/ip.h>
+#include <net/ipconfig.h>
+
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+
+/* Define this to allow debugging output */
+#undef IPCONFIG_DEBUG
+
+#ifdef IPCONFIG_DEBUG
+#define DBG(x) printk x
+#else
+#define DBG(x) do { } while(0)
+#endif
+
+/* Define the timeout for waiting for a RARP/BOOTP reply */
+#define CONF_BASE_TIMEOUT (HZ*5) /* Initial timeout: 5 seconds */
+#define CONF_RETRIES 10 /* 10 retries */
+#define CONF_TIMEOUT_RANDOM (HZ) /* Maximum amount of randomization */
+#define CONF_TIMEOUT_MULT *5/4 /* Rate of timeout growth */
+#define CONF_TIMEOUT_MAX (HZ*30) /* Maximum allowed timeout */
+
+/* IP configuration */
+static char user_dev_name[IFNAMSIZ] __initdata = { 0, };/* Name of user-selected boot device */
+u32 ic_myaddr __initdata = INADDR_NONE; /* My IP address */
+u32 ic_servaddr __initdata = INADDR_NONE; /* Server IP address */
+u32 ic_gateway __initdata = INADDR_NONE; /* Gateway IP address */
+u32 ic_netmask __initdata = INADDR_NONE; /* Netmask for local subnet */
+int ic_enable __initdata = 1; /* Automatic IP configuration enabled */
+int ic_host_name_set __initdata = 0; /* Host name configured manually */
+int ic_set_manually __initdata = 0; /* IPconfig parameters set manually */
+
+u32 root_server_addr __initdata = INADDR_NONE; /* Address of boot server */
+u8 root_server_path[256] __initdata = { 0, }; /* Path to mount as root */
+
+#if defined(CONFIG_IP_PNP_BOOTP) || defined(CONFIG_IP_PNP_RARP)
+
+#define CONFIG_IP_PNP_DYNAMIC
+
+static int ic_proto_enabled __initdata = 0 /* Protocols enabled */
+#ifdef CONFIG_IP_PNP_BOOTP
+ | IC_BOOTP
+#endif
+#ifdef CONFIG_IP_PNP_RARP
+ | IC_RARP
+#endif
+ ;
+static int ic_got_reply __initdata = 0; /* Protocol(s) we got reply from */
+
+#else
+
+static int ic_proto_enabled __initdata = 0;
+
+#endif
+
+static int ic_proto_have_if __initdata = 0;
+
+/*
+ * Network devices
+ */
+
+struct ic_device {
+ struct ic_device *next;
+ struct device *dev;
+ unsigned short flags;
+ int able;
+};
+
+static struct ic_device *ic_first_dev __initdata = NULL;/* List of open device */
+static struct device *ic_dev __initdata = NULL; /* Selected device */
+
+static int __init ic_open_devs(void)
+{
+ struct ic_device *d, **last;
+ struct device *dev;
+ unsigned short oflags;
+
+ last = &ic_first_dev;
+ for (dev = dev_base; dev; dev = dev->next)
+ if (user_dev_name[0] ? !strcmp(dev->name, user_dev_name) :
+ (!(dev->flags & IFF_LOOPBACK) &&
+ (dev->flags & (IFF_POINTOPOINT|IFF_BROADCAST)) &&
+ strncmp(dev->name, "dummy", 5))) {
+ int able = 0;
+ if (dev->mtu >= 364)
+ able |= IC_BOOTP;
+ else
+ printk(KERN_WARNING "BOOTP: Ignoring device %s, MTU %d too small", dev->name, dev->mtu);
+ if (!(dev->flags & IFF_NOARP))
+ able |= IC_RARP;
+ able &= ic_proto_enabled;
+ if (ic_proto_enabled && !able)
+ continue;
+ oflags = dev->flags;
+ if (dev_change_flags(dev, oflags | IFF_UP) < 0) {
+ printk(KERN_ERR "IP-Config: Failed to open %s\n", dev->name);
+ continue;
+ }
+ if (!(d = kmalloc(sizeof(struct ic_device), GFP_KERNEL)))
+ return -1;
+ d->dev = dev;
+ *last = d;
+ last = &d->next;
+ d->flags = oflags;
+ d->able = able;
+ ic_proto_have_if |= able;
+ DBG(("IP-Config: Opened %s (able=%d)\n", dev->name, able));
+ }
+ *last = NULL;
+
+ if (!ic_first_dev) {
+ if (user_dev_name[0])
+ printk(KERN_ERR "IP-Config: Device `%s' not found.\n", user_dev_name);
+ else
+ printk(KERN_ERR "IP-Config: No network devices available.\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void __init ic_close_devs(void)
+{
+ struct ic_device *d, *next;
+ struct device *dev;
+
+ next = ic_first_dev;
+ while ((d = next)) {
+ next = d->next;
+ dev = d->dev;
+ if (dev != ic_dev) {
+ DBG(("IP-Config: Downing %s\n", dev->name));
+ dev_change_flags(dev, d->flags);
+ }
+ kfree_s(d, sizeof(struct ic_device));
+ }
+}
+
+/*
+ * Interface to various network functions.
+ */
+
+static inline void
+set_sockaddr(struct sockaddr_in *sin, u32 addr, u16 port)
+{
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = addr;
+ sin->sin_port = port;
+}
+
+static int __init ic_dev_ioctl(unsigned int cmd, struct ifreq *arg)
+{
+ int res;
+
+ mm_segment_t oldfs = get_fs();
+ set_fs(get_ds());
+ res = devinet_ioctl(cmd, arg);
+ set_fs(oldfs);
+ return res;
+}
+
+static int __init ic_route_ioctl(unsigned int cmd, struct rtentry *arg)
+{
+ int res;
+
+ mm_segment_t oldfs = get_fs();
+ set_fs(get_ds());
+ res = ip_rt_ioctl(cmd, arg);
+ set_fs(oldfs);
+ return res;
+}
+
+/*
+ * Set up interface addresses and routes.
+ */
+
+static int __init ic_setup_if(void)
+{
+ struct ifreq ir;
+ struct sockaddr_in *sin = (void *) &ir.ifr_ifru.ifru_addr;
+ int err;
+
+ memset(&ir, 0, sizeof(ir));
+ strcpy(ir.ifr_ifrn.ifrn_name, ic_dev->name);
+ set_sockaddr(sin, ic_myaddr, 0);
+ if ((err = ic_dev_ioctl(SIOCSIFADDR, &ir)) < 0) {
+ printk(KERN_ERR "IP-Config: Unable to set interface address (%d).\n", err);
+ return -1;
+ }
+ set_sockaddr(sin, ic_netmask, 0);
+ if ((err = ic_dev_ioctl(SIOCSIFNETMASK, &ir)) < 0) {
+ printk(KERN_ERR "IP-Config: Unable to set interface netmask (%d).\n", err);
+ return -1;
+ }
+ set_sockaddr(sin, ic_myaddr | ~ic_netmask, 0);
+ if ((err = ic_dev_ioctl(SIOCSIFBRDADDR, &ir)) < 0) {
+ printk(KERN_ERR "IP-Config: Unable to set interface broadcast address (%d).\n", err);
+ return -1;
+ }
+ return 0;
+}
+
+static int __init ic_setup_routes(void)
+{
+ /* No need to setup device routes, only the default route... */
+
+ if (ic_gateway != INADDR_NONE) {
+ struct rtentry rm;
+ int err;
+
+ memset(&rm, 0, sizeof(rm));
+ if ((ic_gateway ^ ic_myaddr) & ic_netmask) {
+ printk(KERN_ERR "IP-Config: Gateway not on directly connected network.\n");
+ return -1;
+ }
+ set_sockaddr((struct sockaddr_in *) &rm.rt_dst, 0, 0);
+ set_sockaddr((struct sockaddr_in *) &rm.rt_genmask, 0, 0);
+ set_sockaddr((struct sockaddr_in *) &rm.rt_gateway, ic_gateway, 0);
+ rm.rt_flags = RTF_UP | RTF_GATEWAY;
+ if ((err = ic_route_ioctl(SIOCADDRT, &rm)) < 0) {
+ printk(KERN_ERR "IP-Config: Cannot add default route (%d).\n", err);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Fill in default values for all missing parameters.
+ */
+
+static int __init ic_defaults(void)
+{
+ /*
+ * At this point we have no userspace running so need not
+ * claim locks on system_utsname
+ */
+
+ if (!ic_host_name_set)
+ strcpy(system_utsname.nodename, in_ntoa(ic_myaddr));
+
+ if (root_server_addr == INADDR_NONE)
+ root_server_addr = ic_servaddr;
+
+ if (ic_netmask == INADDR_NONE) {
+ if (IN_CLASSA(ntohl(ic_myaddr)))
+ ic_netmask = htonl(IN_CLASSA_NET);
+ else if (IN_CLASSB(ntohl(ic_myaddr)))
+ ic_netmask = htonl(IN_CLASSB_NET);
+ else if (IN_CLASSC(ntohl(ic_myaddr)))
+ ic_netmask = htonl(IN_CLASSC_NET);
+ else {
+ printk(KERN_ERR "IP-Config: Unable to guess netmask for address %08x\n", ic_myaddr);
+ return -1;
+ }
+ printk("IP-Config: Guessing netmask %s\n", in_ntoa(ic_netmask));
+ }
+
+ return 0;
+}
+
+/*
+ * RARP support.
+ */
+
+#ifdef CONFIG_IP_PNP_RARP
+
+static int ic_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt);
+
+static struct packet_type rarp_packet_type __initdata = {
+ __constant_htons(ETH_P_RARP),
+ NULL, /* Listen to all devices */
+ ic_rarp_recv,
+ NULL,
+ NULL
+};
+
+static inline void ic_rarp_init(void)
+{
+ dev_add_pack(&rarp_packet_type);
+}
+
+static inline void ic_rarp_cleanup(void)
+{
+ dev_remove_pack(&rarp_packet_type);
+}
+
+/*
+ * Process received RARP packet.
+ */
+static int __init
+ic_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct arphdr *rarp = (struct arphdr *)skb->h.raw;
+ unsigned char *rarp_ptr = (unsigned char *) (rarp + 1);
+ unsigned long sip, tip;
+ unsigned char *sha, *tha; /* s for "source", t for "target" */
+
+ /* If we already have a reply, just drop the packet */
+ if (ic_got_reply)
+ goto drop;
+
+ /* If this test doesn't pass, it's not IP, or we should ignore it anyway */
+ if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd))
+ goto drop;
+
+ /* If it's not a RARP reply, delete it. */
+ if (rarp->ar_op != htons(ARPOP_RREPLY))
+ goto drop;
+
+ /* If it's not Ethernet, delete it. */
+ if (rarp->ar_pro != htons(ETH_P_IP))
+ goto drop;
+
+ /* Extract variable-width fields */
+ sha = rarp_ptr;
+ rarp_ptr += dev->addr_len;
+ memcpy(&sip, rarp_ptr, 4);
+ rarp_ptr += 4;
+ tha = rarp_ptr;
+ rarp_ptr += dev->addr_len;
+ memcpy(&tip, rarp_ptr, 4);
+
+ /* Discard packets which are not meant for us. */
+ if (memcmp(tha, dev->dev_addr, dev->addr_len))
+ goto drop;
+
+ /* Discard packets which are not from specified server. */
+ if (ic_servaddr != INADDR_NONE && ic_servaddr != sip)
+ goto drop;
+
+ /* Victory! The packet is what we were looking for! */
+ if (!ic_got_reply) {
+ ic_got_reply = IC_RARP;
+ ic_dev = dev;
+ if (ic_myaddr == INADDR_NONE)
+ ic_myaddr = tip;
+ ic_servaddr = sip;
+ }
+
+ /* And throw the packet out... */
+drop:
+ kfree_skb(skb);
+ return 0;
+}
+
+
+/*
+ * Send RARP request packet over all devices which allow RARP.
+ */
+static void __init ic_rarp_send(void)
+{
+ struct ic_device *d;
+
+ for (d=ic_first_dev; d; d=d->next)
+ if (d->able & IC_RARP) {
+ struct device *dev = d->dev;
+ arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL,
+ dev->dev_addr, dev->dev_addr);
+ }
+}
+
+#endif
+
+/*
+ * BOOTP support.
+ */
+
+#ifdef CONFIG_IP_PNP_BOOTP
+
+struct bootp_pkt { /* BOOTP packet format */
+ struct iphdr iph; /* IP header */
+ struct udphdr udph; /* UDP header */
+ u8 op; /* 1=request, 2=reply */
+ u8 htype; /* HW address type */
+ u8 hlen; /* HW address length */
+ u8 hops; /* Used only by gateways */
+ u32 xid; /* Transaction ID */
+ u16 secs; /* Seconds since we started */
+ u16 flags; /* Just what it says */
+ u32 client_ip; /* Client's IP address if known */
+ u32 your_ip; /* Assigned IP address */
+ u32 server_ip; /* Server's IP address */
+ u32 relay_ip; /* IP address of BOOTP relay */
+ u8 hw_addr[16]; /* Client's HW address */
+ u8 serv_name[64]; /* Server host name */
+ u8 boot_file[128]; /* Name of boot file */
+ u8 vendor_area[128]; /* Area for extensions */
+};
+
+#define BOOTP_REQUEST 1
+#define BOOTP_REPLY 2
+
+static u32 ic_bootp_xid;
+
+static int ic_bootp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt);
+
+static struct packet_type bootp_packet_type __initdata = {
+ __constant_htons(ETH_P_IP),
+ NULL, /* Listen to all devices */
+ ic_bootp_recv,
+ NULL,
+ NULL
+};
+
+
+/*
+ * Initialize BOOTP extension fields in the request.
+ */
+static void __init ic_bootp_init_ext(u8 *e)
+{
+ *e++ = 99; /* RFC1048 Magic Cookie */
+ *e++ = 130;
+ *e++ = 83;
+ *e++ = 99;
+ *e++ = 1; /* Subnet mask request */
+ *e++ = 4;
+ e += 4;
+ *e++ = 3; /* Default gateway request */
+ *e++ = 4;
+ e += 4;
+ *e++ = 12; /* Host name request */
+ *e++ = 32;
+ e += 32;
+ *e++ = 40; /* NIS Domain name request */
+ *e++ = 32;
+ e += 32;
+ *e++ = 17; /* Boot path */
+ *e++ = 32;
+ e += 32;
+ *e = 255; /* End of the list */
+}
+
+
+/*
+ * Initialize the BOOTP mechanism.
+ */
+static inline void ic_bootp_init(void)
+{
+ get_random_bytes(&ic_bootp_xid, sizeof(u32));
+ DBG(("BOOTP: XID=%08x\n", ic_bootp_xid));
+ dev_add_pack(&bootp_packet_type);
+}
+
+
+/*
+ * BOOTP cleanup.
+ */
+static inline void ic_bootp_cleanup(void)
+{
+ dev_remove_pack(&bootp_packet_type);
+}
+
+
+/*
+ * Send BOOTP request to single interface.
+ */
+static void __init ic_bootp_send_if(struct ic_device *d, u32 jiffies)
+{
+ struct device *dev = d->dev;
+ struct sk_buff *skb;
+ struct bootp_pkt *b;
+ int hh_len = (dev->hard_header_len + 15) & ~15;
+ struct iphdr *h;
+
+ /* Allocate packet */
+ skb = alloc_skb(sizeof(struct bootp_pkt) + hh_len + 15, GFP_KERNEL);
+ if (!skb)
+ return;
+ skb_reserve(skb, hh_len);
+ b = (struct bootp_pkt *) skb_put(skb, sizeof(struct bootp_pkt));
+ memset(b, 0, sizeof(struct bootp_pkt));
+
+ /* Construct IP header */
+ skb->nh.iph = h = &b->iph;
+ h->version = 4;
+ h->ihl = 5;
+ h->tot_len = htons(sizeof(struct bootp_pkt));
+ h->frag_off = htons(IP_DF);
+ h->ttl = 64;
+ h->protocol = IPPROTO_UDP;
+ h->daddr = INADDR_BROADCAST;
+ h->check = ip_fast_csum((unsigned char *) h, h->ihl);
+
+ /* Construct UDP header */
+ b->udph.source = htons(68);
+ b->udph.dest = htons(67);
+ b->udph.len = htons(sizeof(struct bootp_pkt) - sizeof(struct iphdr));
+ /* UDP checksum not calculated -- explicitly allowed in BOOTP RFC */
+
+ /* Construct BOOTP header */
+ b->op = BOOTP_REQUEST;
+ b->htype = dev->type;
+ b->hlen = dev->addr_len;
+ memcpy(b->hw_addr, dev->dev_addr, dev->addr_len);
+ b->secs = htons(jiffies / HZ);
+ b->xid = ic_bootp_xid;
+ ic_bootp_init_ext(b->vendor_area);
+
+ /* Chain packet down the line... */
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_IP);
+ if ((dev->hard_header &&
+ dev->hard_header(skb, dev, ntohs(skb->protocol), dev->broadcast, dev->dev_addr, skb->len) < 0) ||
+ dev_queue_xmit(skb) < 0)
+ printk("E");
+}
+
+
+/*
+ * Send BOOTP requests to all interfaces.
+ */
+static void __init ic_bootp_send(u32 jiffies)
+{
+ struct ic_device *d;
+
+ for(d=ic_first_dev; d; d=d->next)
+ if (d->able & IC_BOOTP)
+ ic_bootp_send_if(d, jiffies);
+}
+
+
+/*
+ * Copy BOOTP-supplied string if not already set.
+ */
+static int __init ic_bootp_string(char *dest, char *src, int len, int max)
+{
+ if (!len)
+ return 0;
+ if (len > max-1)
+ len = max-1;
+ strncpy(dest, src, len);
+ dest[len] = '\0';
+ return 1;
+}
+
+
+/*
+ * Process BOOTP extension.
+ */
+static void __init ic_do_bootp_ext(u8 *ext)
+{
+#ifdef IPCONFIG_DEBUG
+ u8 *c;
+
+ printk("BOOTP: Got extension %02x",*ext);
+ for(c=ext+2; c<ext+2+ext[1]; c++)
+ printk(" %02x", *c);
+ printk("\n");
+#endif
+
+ switch (*ext++) {
+ case 1: /* Subnet mask */
+ if (ic_netmask == INADDR_NONE)
+ memcpy(&ic_netmask, ext+1, 4);
+ break;
+ case 3: /* Default gateway */
+ if (ic_gateway == INADDR_NONE)
+ memcpy(&ic_gateway, ext+1, 4);
+ break;
+ case 12: /* Host name */
+ ic_bootp_string(system_utsname.nodename, ext+1, *ext, __NEW_UTS_LEN);
+ ic_host_name_set = 1;
+ break;
+ case 40: /* NIS Domain name */
+ ic_bootp_string(system_utsname.domainname, ext+1, *ext, __NEW_UTS_LEN);
+ break;
+ case 17: /* Root path */
+ if (!root_server_path[0])
+ ic_bootp_string(root_server_path, ext+1, *ext, sizeof(root_server_path));
+ break;
+ }
+}
+
+
+/*
+ * Receive BOOTP reply.
+ */
+static int __init ic_bootp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct bootp_pkt *b = (struct bootp_pkt *) skb->nh.iph;
+ struct iphdr *h = &b->iph;
+ int len;
+
+ /* If we already have a reply, just drop the packet */
+ if (ic_got_reply)
+ goto drop;
+
+ /* Check whether it's a BOOTP packet */
+ if (skb->pkt_type == PACKET_OTHERHOST ||
+ skb->len < sizeof(struct udphdr) + sizeof(struct iphdr) ||
+ h->ihl != 5 ||
+ h->version != 4 ||
+ ip_fast_csum((char *) h, h->ihl) != 0 ||
+ skb->len < ntohs(h->tot_len) ||
+ h->protocol != IPPROTO_UDP ||
+ b->udph.source != htons(67) ||
+ b->udph.dest != htons(68) ||
+ ntohs(h->tot_len) < ntohs(b->udph.len) + sizeof(struct iphdr))
+ goto drop;
+
+ /* Fragments are not supported */
+ if (h->frag_off & htons(IP_OFFSET|IP_MF)) {
+ printk(KERN_ERR "BOOTP: Ignoring fragmented reply.\n");
+ goto drop;
+ }
+
+ /* Is it a reply to our BOOTP request? */
+ len = ntohs(b->udph.len) - sizeof(struct udphdr);
+ if (len < 300 || /* See RFC 951:2.1 */
+ b->op != BOOTP_REPLY ||
+ b->xid != ic_bootp_xid) {
+ printk("?");
+ goto drop;
+ }
+
+ /* Extract basic fields */
+ ic_myaddr = b->your_ip;
+ ic_servaddr = b->server_ip;
+ ic_got_reply = IC_BOOTP;
+ ic_dev = dev;
+
+ /* Parse extensions */
+ if (b->vendor_area[0] == 99 && /* Check magic cookie */
+ b->vendor_area[1] == 130 &&
+ b->vendor_area[2] == 83 &&
+ b->vendor_area[3] == 99) {
+ u8 *ext = &b->vendor_area[4];
+ u8 *end = (u8 *) b + ntohs(b->iph.tot_len);
+ while (ext < end && *ext != 0xff) {
+ if (*ext == 0) /* Padding */
+ ext++;
+ else {
+ u8 *opt = ext;
+ ext += ext[1] + 2;
+ if (ext <= end)
+ ic_do_bootp_ext(opt);
+ }
+ }
+ }
+
+ if (ic_gateway == INADDR_NONE && b->relay_ip)
+ ic_gateway = b->relay_ip;
+
+drop:
+ kfree_skb(skb);
+ return 0;
+}
+
+
+#endif
+
+
+/*
+ * Dynamic IP configuration -- BOOTP and RARP.
+ */
+
+#ifdef CONFIG_IP_PNP_DYNAMIC
+
+static int __init ic_dynamic(void)
+{
+ int retries;
+ unsigned long timeout, jiff;
+ unsigned long start_jiffies;
+ int do_rarp = ic_proto_have_if & IC_RARP;
+ int do_bootp = ic_proto_have_if & IC_BOOTP;
+
+ /*
+ * If neither BOOTP nor RARP was selected, return with an error. This
+ * routine gets only called when some pieces of information are mis-
+ * sing, and without BOOTP and RARP we are not able to get that in-
+ * formation.
+ */
+ if (!ic_proto_enabled) {
+ printk(KERN_ERR "IP-Config: Incomplete network configuration information.\n");
+ return -1;
+ }
+
+#ifdef CONFIG_IP_PNP_BOOTP
+ if ((ic_proto_enabled ^ ic_proto_have_if) & IC_BOOTP)
+ printk(KERN_ERR "BOOTP: No suitable device found.\n");
+#endif
+
+#ifdef CONFIG_IP_PNP_RARP
+ if ((ic_proto_enabled ^ ic_proto_have_if) & IC_RARP)
+ printk(KERN_ERR "RARP: No suitable device found.\n");
+#endif
+
+ if (!ic_proto_have_if)
+ /* Error message already printed */
+ return -1;
+
+ /*
+ * Setup RARP and BOOTP protocols
+ */
+#ifdef CONFIG_IP_PNP_RARP
+ if (do_rarp)
+ ic_rarp_init();
+#endif
+#ifdef CONFIG_IP_PNP_BOOTP
+ if (do_bootp)
+ ic_bootp_init();
+#endif
+
+ /*
+ * Send requests and wait, until we get an answer. This loop
+ * seems to be a terrible waste of CPU time, but actually there is
+ * only one process running at all, so we don't need to use any
+ * scheduler functions.
+ * [Actually we could now, but the nothing else running note still
+ * applies.. - AC]
+ */
+ printk(KERN_NOTICE "Sending %s%s%s requests...",
+ do_bootp ? "BOOTP" : "",
+ do_bootp && do_rarp ? " and " : "",
+ do_rarp ? "RARP" : "");
+ start_jiffies = jiffies;
+ retries = CONF_RETRIES;
+ get_random_bytes(&timeout, sizeof(timeout));
+ timeout = CONF_BASE_TIMEOUT + (timeout % (unsigned) CONF_TIMEOUT_RANDOM);
+ for(;;) {
+#ifdef CONFIG_IP_PNP_BOOTP
+ if (do_bootp)
+ ic_bootp_send(jiffies - start_jiffies);
+#endif
+#ifdef CONFIG_IP_PNP_RARP
+ if (do_rarp)
+ ic_rarp_send();
+#endif
+ printk(".");
+ jiff = jiffies + timeout;
+ while (jiffies < jiff && !ic_got_reply)
+ ;
+ if (ic_got_reply) {
+ printk(" OK\n");
+ break;
+ }
+ if (! --retries) {
+ printk(" timed out!\n");
+ break;
+ }
+ timeout = timeout CONF_TIMEOUT_MULT;
+ if (timeout > CONF_TIMEOUT_MAX)
+ timeout = CONF_TIMEOUT_MAX;
+ }
+
+#ifdef CONFIG_IP_PNP_RARP
+ if (do_rarp)
+ ic_rarp_cleanup();
+#endif
+#ifdef CONFIG_IP_PNP_BOOTP
+ if (do_bootp)
+ ic_bootp_cleanup();
+#endif
+
+ if (!ic_got_reply)
+ return -1;
+
+ printk("IP-Config: Got %s answer from %s, ",
+ (ic_got_reply & IC_BOOTP) ? "BOOTP" : "RARP",
+ in_ntoa(ic_servaddr));
+ printk("my address is %s\n", in_ntoa(ic_myaddr));
+
+ return 0;
+}
+
+#endif
+
+/*
+ * IP Autoconfig dispatcher.
+ */
+
+int __init ip_auto_config(void)
+{
+ if (!ic_enable)
+ return 0;
+
+ DBG(("IP-Config: Entered.\n"));
+
+ /* Setup all network devices */
+ if (ic_open_devs() < 0)
+ return -1;
+
+ /*
+ * If the config information is insufficient (e.g., our IP address or
+ * IP address of the boot server is missing or we have multiple network
+ * interfaces and no default was set), use BOOTP or RARP to get the
+ * missing values.
+ */
+ if (ic_myaddr == INADDR_NONE ||
+#ifdef CONFIG_ROOT_NFS
+ (root_server_addr == INADDR_NONE && ic_servaddr == INADDR_NONE) ||
+#endif
+ ic_first_dev->next) {
+#ifdef CONFIG_IP_PNP_DYNAMIC
+ if (ic_dynamic() < 0) {
+ printk(KERN_ERR "IP-Config: Auto-configuration of network failed.\n");
+ ic_close_devs();
+ return -1;
+ }
+#else
+ printk(KERN_ERR "IP-Config: Incomplete network configuration information.\n");
+ ic_close_devs();
+ return -1;
+#endif
+ } else {
+ ic_dev = ic_first_dev->dev; /* Device selected manually or only one device -> use it */
+ }
+
+ /*
+ * Use defaults wherever applicable.
+ */
+ if (ic_defaults() < 0)
+ return -1;
+
+ /*
+ * Close all network devices except the device we've
+ * autoconfigured and set up routes.
+ */
+ ic_close_devs();
+ if (ic_setup_if() < 0 || ic_setup_routes() < 0)
+ return -1;
+
+ DBG(("IP-Config: device=%s, local=%08x, server=%08x, boot=%08x, gw=%08x, mask=%08x\n",
+ ic_dev->name, ic_myaddr, ic_servaddr, root_server_addr, ic_gateway, ic_netmask));
+ DBG(("IP-Config: host=%s, domain=%s, path=`%s'\n", system_utsname.nodename,
+ system_utsname.domainname, root_server_path));
+ return 0;
+}
+
+/*
+ * Decode any IP configuration options in the "ip=" or "nfsaddrs=" kernel
+ * command line parameter. It consists of option fields separated by colons in
+ * the following order:
+ *
+ * <client-ip>:<server-ip>:<gw-ip>:<netmask>:<host name>:<device>:<bootp|rarp>
+ *
+ * Any of the fields can be empty which means to use a default value:
+ * <client-ip> - address given by BOOTP or RARP
+ * <server-ip> - address of host returning BOOTP or RARP packet
+ * <gw-ip> - none, or the address returned by BOOTP
+ * <netmask> - automatically determined from <client-ip>, or the
+ * one returned by BOOTP
+ * <host name> - <client-ip> in ASCII notation, or the name returned
+ * by BOOTP
+ * <device> - use all available devices
+ * <bootp|rarp|both|off> - use both protocols to determine my own address
+ */
+static int __init ic_proto_name(char *name)
+{
+ if (!strcmp(name, "off")) {
+ ic_proto_enabled = 0;
+ return 1;
+ }
+#ifdef CONFIG_IP_PNP_BOOTP
+ else if (!strcmp(name, "bootp")) {
+ ic_proto_enabled &= ~IC_RARP;
+ return 1;
+ }
+#endif
+#ifdef CONFIG_IP_PNP_RARP
+ else if (!strcmp(name, "rarp")) {
+ ic_proto_enabled &= ~IC_BOOTP;
+ return 1;
+ }
+#endif
+#ifdef CONFIG_IP_PNP_DYNAMIC
+ else if (!strcmp(name, "both")) {
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+void __init ip_auto_config_setup(char *addrs, int *ints)
+{
+ char *cp, *ip, *dp;
+ int num = 0;
+
+ ic_set_manually = 1;
+ if (!strcmp(addrs, "off")) {
+ ic_enable = 0;
+ return;
+ }
+ if (ic_proto_name(addrs))
+ return;
+
+ /* Parse the whole string */
+ ip = addrs;
+ while (ip && *ip) {
+ if ((cp = strchr(ip, ':')))
+ *cp++ = '\0';
+ if (strlen(ip) > 0) {
+ DBG(("IP-Config: Parameter #%d: `%s'\n", num, ip));
+ switch (num) {
+ case 0:
+ if ((ic_myaddr = in_aton(ip)) == INADDR_ANY)
+ ic_myaddr = INADDR_NONE;
+ break;
+ case 1:
+ if ((ic_servaddr = in_aton(ip)) == INADDR_ANY)
+ ic_servaddr = INADDR_NONE;
+ break;
+ case 2:
+ if ((ic_gateway = in_aton(ip)) == INADDR_ANY)
+ ic_gateway = INADDR_NONE;
+ break;
+ case 3:
+ if ((ic_netmask = in_aton(ip)) == INADDR_ANY)
+ ic_netmask = INADDR_NONE;
+ break;
+ case 4:
+ if ((dp = strchr(ip, '.'))) {
+ *dp++ = '\0';
+ strncpy(system_utsname.domainname, dp, __NEW_UTS_LEN);
+ system_utsname.domainname[__NEW_UTS_LEN] = '\0';
+ }
+ strncpy(system_utsname.nodename, ip, __NEW_UTS_LEN);
+ system_utsname.nodename[__NEW_UTS_LEN] = '\0';
+ ic_host_name_set = 1;
+ break;
+ case 5:
+ strncpy(user_dev_name, ip, IFNAMSIZ);
+ user_dev_name[IFNAMSIZ-1] = '\0';
+ break;
+ case 6:
+ ic_proto_name(ip);
+ break;
+ }
+ }
+ ip = cp;
+ num++;
+ }
+}
diff --git a/pfinet/linux-src/net/ipv4/ipip.c b/pfinet/linux-src/net/ipv4/ipip.c
new file mode 100644
index 00000000..119d7567
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ipip.c
@@ -0,0 +1,870 @@
+/*
+ * Linux NET3: IP/IP protocol decoder.
+ *
+ * Version: $Id: ipip.c,v 1.26 1999/03/25 10:04:32 davem Exp $
+ *
+ * Authors:
+ * Sam Lantinga (slouken@cs.ucdavis.edu) 02/01/95
+ *
+ * Fixes:
+ * Alan Cox : Merged and made usable non modular (its so tiny its silly as
+ * a module taking up 2 pages).
+ * Alan Cox : Fixed bug with 1.3.18 and IPIP not working (now needs to set skb->h.iph)
+ * to keep ip_forward happy.
+ * Alan Cox : More fixes for 1.3.21, and firewall fix. Maybe this will work soon 8).
+ * Kai Schulte : Fixed #defines for IP_FIREWALL->FIREWALL
+ * David Woodhouse : Perform some basic ICMP handling.
+ * IPIP Routing without decapsulation.
+ * Carlos Picoto : GRE over IP support
+ * Alexey Kuznetsov: Reworked. Really, now it is truncated version of ipv4/ip_gre.c.
+ * I do not want to merge them together.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+/* tunnel.c: an IP tunnel driver
+
+ The purpose of this driver is to provide an IP tunnel through
+ which you can tunnel network traffic transparently across subnets.
+
+ This was written by looking at Nick Holloway's dummy driver
+ Thanks for the great code!
+
+ -Sam Lantinga (slouken@cs.ucdavis.edu) 02/01/95
+
+ Minor tweaks:
+ Cleaned up the code a little and added some pre-1.3.0 tweaks.
+ dev->hard_header/hard_header_len changed to use no headers.
+ Comments/bracketing tweaked.
+ Made the tunnels use dev->name not tunnel: when error reporting.
+ Added tx_dropped stat
+
+ -Alan Cox (Alan.Cox@linux.org) 21 March 95
+
+ Reworked:
+ Changed to tunnel to destination gateway in addition to the
+ tunnel's pointopoint address
+ Almost completely rewritten
+ Note: There is currently no firewall or ICMP handling done.
+
+ -Sam Lantinga (slouken@cs.ucdavis.edu) 02/13/96
+
+*/
+
+/* Things I wish I had known when writing the tunnel driver:
+
+ When the tunnel_xmit() function is called, the skb contains the
+ packet to be sent (plus a great deal of extra info), and dev
+ contains the tunnel device that _we_ are.
+
+ When we are passed a packet, we are expected to fill in the
+ source address with our source IP address.
+
+ What is the proper way to allocate, copy and free a buffer?
+ After you allocate it, it is a "0 length" chunk of memory
+ starting at zero. If you want to add headers to the buffer
+ later, you'll have to call "skb_reserve(skb, amount)" with
+ the amount of memory you want reserved. Then, you call
+ "skb_put(skb, amount)" with the amount of space you want in
+ the buffer. skb_put() returns a pointer to the top (#0) of
+ that buffer. skb->len is set to the amount of space you have
+ "allocated" with skb_put(). You can then write up to skb->len
+ bytes to that buffer. If you need more, you can call skb_put()
+ again with the additional amount of space you need. You can
+ find out how much more space you can allocate by calling
+ "skb_tailroom(skb)".
+ Now, to add header space, call "skb_push(skb, header_len)".
+ This creates space at the beginning of the buffer and returns
+ a pointer to this new space. If later you need to strip a
+ header from a buffer, call "skb_pull(skb, header_len)".
+ skb_headroom() will return how much space is left at the top
+ of the buffer (before the main data). Remember, this headroom
+ space must be reserved before the skb_put() function is called.
+ */
+
+/*
+ This version of net/ipv4/ipip.c is cloned of net/ipv4/ip_gre.c
+
+ For comments look at net/ipv4/ip_gre.c --ANK
+ */
+
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/uaccess.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/in.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/if_arp.h>
+#include <linux/mroute.h>
+#include <linux/init.h>
+
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <net/protocol.h>
+#include <net/ipip.h>
+
+#define HASH_SIZE 16
+#define HASH(addr) ((addr^(addr>>4))&0xF)
+
+static int ipip_fb_tunnel_init(struct device *dev);
+static int ipip_tunnel_init(struct device *dev);
+
+static struct device ipip_fb_tunnel_dev = {
+ NULL, 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, ipip_fb_tunnel_init,
+};
+
+static struct ip_tunnel ipip_fb_tunnel = {
+ NULL, &ipip_fb_tunnel_dev, {0, }, 0, 0, 0, 0, 0, 0, 0, {"tunl0", }
+};
+
+static struct ip_tunnel *tunnels_r_l[HASH_SIZE];
+static struct ip_tunnel *tunnels_r[HASH_SIZE];
+static struct ip_tunnel *tunnels_l[HASH_SIZE];
+static struct ip_tunnel *tunnels_wc[1];
+static struct ip_tunnel **tunnels[4] = { tunnels_wc, tunnels_l, tunnels_r, tunnels_r_l };
+
+static struct ip_tunnel * ipip_tunnel_lookup(u32 remote, u32 local)
+{
+ unsigned h0 = HASH(remote);
+ unsigned h1 = HASH(local);
+ struct ip_tunnel *t;
+
+ for (t = tunnels_r_l[h0^h1]; t; t = t->next) {
+ if (local == t->parms.iph.saddr &&
+ remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP))
+ return t;
+ }
+ for (t = tunnels_r[h0]; t; t = t->next) {
+ if (remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP))
+ return t;
+ }
+ for (t = tunnels_l[h1]; t; t = t->next) {
+ if (local == t->parms.iph.saddr && (t->dev->flags&IFF_UP))
+ return t;
+ }
+ if ((t = tunnels_wc[0]) != NULL && (t->dev->flags&IFF_UP))
+ return t;
+ return NULL;
+}
+
+static struct ip_tunnel **ipip_bucket(struct ip_tunnel *t)
+{
+ u32 remote = t->parms.iph.daddr;
+ u32 local = t->parms.iph.saddr;
+ unsigned h = 0;
+ int prio = 0;
+
+ if (remote) {
+ prio |= 2;
+ h ^= HASH(remote);
+ }
+ if (local) {
+ prio |= 1;
+ h ^= HASH(local);
+ }
+ return &tunnels[prio][h];
+}
+
+
+static void ipip_tunnel_unlink(struct ip_tunnel *t)
+{
+ struct ip_tunnel **tp;
+
+ for (tp = ipip_bucket(t); *tp; tp = &(*tp)->next) {
+ if (t == *tp) {
+ *tp = t->next;
+ synchronize_bh();
+ break;
+ }
+ }
+}
+
+static void ipip_tunnel_link(struct ip_tunnel *t)
+{
+ struct ip_tunnel **tp = ipip_bucket(t);
+
+ t->next = *tp;
+ wmb();
+ *tp = t;
+}
+
+struct ip_tunnel * ipip_tunnel_locate(struct ip_tunnel_parm *parms, int create)
+{
+ u32 remote = parms->iph.daddr;
+ u32 local = parms->iph.saddr;
+ struct ip_tunnel *t, **tp, *nt;
+ struct device *dev;
+ unsigned h = 0;
+ int prio = 0;
+
+ if (remote) {
+ prio |= 2;
+ h ^= HASH(remote);
+ }
+ if (local) {
+ prio |= 1;
+ h ^= HASH(local);
+ }
+ for (tp = &tunnels[prio][h]; (t = *tp) != NULL; tp = &t->next) {
+ if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr)
+ return t;
+ }
+ if (!create)
+ return NULL;
+
+ MOD_INC_USE_COUNT;
+ dev = kmalloc(sizeof(*dev) + sizeof(*t), GFP_KERNEL);
+ if (dev == NULL) {
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+ memset(dev, 0, sizeof(*dev) + sizeof(*t));
+ dev->priv = (void*)(dev+1);
+ nt = (struct ip_tunnel*)dev->priv;
+ nt->dev = dev;
+ dev->name = nt->parms.name;
+ dev->init = ipip_tunnel_init;
+ memcpy(&nt->parms, parms, sizeof(*parms));
+ if (dev->name[0] == 0) {
+ int i;
+ for (i=1; i<100; i++) {
+ sprintf(dev->name, "tunl%d", i);
+ if (dev_get(dev->name) == NULL)
+ break;
+ }
+ if (i==100)
+ goto failed;
+ memcpy(parms->name, dev->name, IFNAMSIZ);
+ }
+ if (register_netdevice(dev) < 0)
+ goto failed;
+
+ ipip_tunnel_link(nt);
+ /* Do not decrement MOD_USE_COUNT here. */
+ return nt;
+
+failed:
+ kfree(dev);
+ MOD_DEC_USE_COUNT;
+ return NULL;
+}
+
+
+static void ipip_tunnel_destroy(struct device *dev)
+{
+ if (dev == &ipip_fb_tunnel_dev) {
+ tunnels_wc[0] = NULL;
+ synchronize_bh();
+ } else {
+ ipip_tunnel_unlink((struct ip_tunnel*)dev->priv);
+ kfree(dev);
+ MOD_DEC_USE_COUNT;
+ }
+}
+
+void ipip_err(struct sk_buff *skb, unsigned char *dp, int len)
+{
+#ifndef I_WISH_WORLD_WERE_PERFECT
+
+/* It is not :-( All the routers (except for Linux) return only
+ 8 bytes of packet payload. It means, that precise relaying of
+ ICMP in the real Internet is absolutely infeasible.
+ */
+ struct iphdr *iph = (struct iphdr*)dp;
+ int type = skb->h.icmph->type;
+ int code = skb->h.icmph->code;
+ struct ip_tunnel *t;
+
+ if (len < sizeof(struct iphdr))
+ return;
+
+ switch (type) {
+ default:
+ case ICMP_PARAMETERPROB:
+ return;
+
+ case ICMP_DEST_UNREACH:
+ switch (code) {
+ case ICMP_SR_FAILED:
+ case ICMP_PORT_UNREACH:
+ /* Impossible event. */
+ return;
+ case ICMP_FRAG_NEEDED:
+ /* Soft state for pmtu is maintained by IP core. */
+ return;
+ default:
+ /* All others are translated to HOST_UNREACH.
+ rfc2003 contains "deep thoughts" about NET_UNREACH,
+ I believe they are just ether pollution. --ANK
+ */
+ break;
+ }
+ break;
+ case ICMP_TIME_EXCEEDED:
+ if (code != ICMP_EXC_TTL)
+ return;
+ break;
+ }
+
+ t = ipip_tunnel_lookup(iph->daddr, iph->saddr);
+ if (t == NULL || t->parms.iph.daddr == 0)
+ return;
+ if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
+ return;
+
+ if (jiffies - t->err_time < IPTUNNEL_ERR_TIMEO)
+ t->err_count++;
+ else
+ t->err_count = 1;
+ t->err_time = jiffies;
+ return;
+#else
+ struct iphdr *iph = (struct iphdr*)dp;
+ int hlen = iph->ihl<<2;
+ struct iphdr *eiph;
+ int type = skb->h.icmph->type;
+ int code = skb->h.icmph->code;
+ int rel_type = 0;
+ int rel_code = 0;
+ int rel_info = 0;
+ struct sk_buff *skb2;
+ struct rtable *rt;
+
+ if (len < hlen + sizeof(struct iphdr))
+ return;
+ eiph = (struct iphdr*)(dp + hlen);
+
+ switch (type) {
+ default:
+ return;
+ case ICMP_PARAMETERPROB:
+ if (skb->h.icmph->un.gateway < hlen)
+ return;
+
+ /* So... This guy found something strange INSIDE encapsulated
+ packet. Well, he is fool, but what can we do ?
+ */
+ rel_type = ICMP_PARAMETERPROB;
+ rel_info = skb->h.icmph->un.gateway - hlen;
+ break;
+
+ case ICMP_DEST_UNREACH:
+ switch (code) {
+ case ICMP_SR_FAILED:
+ case ICMP_PORT_UNREACH:
+ /* Impossible event. */
+ return;
+ case ICMP_FRAG_NEEDED:
+ /* And it is the only really necessary thing :-) */
+ rel_info = ntohs(skb->h.icmph->un.frag.mtu);
+ if (rel_info < hlen+68)
+ return;
+ rel_info -= hlen;
+ /* BSD 4.2 MORE DOES NOT EXIST IN NATURE. */
+ if (rel_info > ntohs(eiph->tot_len))
+ return;
+ break;
+ default:
+ /* All others are translated to HOST_UNREACH.
+ rfc2003 contains "deep thoughts" about NET_UNREACH,
+ I believe, it is just ether pollution. --ANK
+ */
+ rel_type = ICMP_DEST_UNREACH;
+ rel_code = ICMP_HOST_UNREACH;
+ break;
+ }
+ break;
+ case ICMP_TIME_EXCEEDED:
+ if (code != ICMP_EXC_TTL)
+ return;
+ break;
+ }
+
+ /* Prepare fake skb to feed it to icmp_send */
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2 == NULL)
+ return;
+ dst_release(skb2->dst);
+ skb2->dst = NULL;
+ skb_pull(skb2, skb->data - (u8*)eiph);
+ skb2->nh.raw = skb2->data;
+
+ /* Try to guess incoming interface */
+ if (ip_route_output(&rt, eiph->saddr, 0, RT_TOS(eiph->tos), 0)) {
+ kfree_skb(skb2);
+ return;
+ }
+ skb2->dev = rt->u.dst.dev;
+
+ /* route "incoming" packet */
+ if (rt->rt_flags&RTCF_LOCAL) {
+ ip_rt_put(rt);
+ rt = NULL;
+ if (ip_route_output(&rt, eiph->daddr, eiph->saddr, eiph->tos, 0) ||
+ rt->u.dst.dev->type != ARPHRD_IPGRE) {
+ ip_rt_put(rt);
+ kfree_skb(skb2);
+ return;
+ }
+ } else {
+ ip_rt_put(rt);
+ if (ip_route_input(skb2, eiph->daddr, eiph->saddr, eiph->tos, skb2->dev) ||
+ skb2->dst->dev->type != ARPHRD_IPGRE) {
+ kfree_skb(skb2);
+ return;
+ }
+ }
+
+ /* change mtu on this route */
+ if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
+ if (rel_info > skb2->dst->pmtu) {
+ kfree_skb(skb2);
+ return;
+ }
+ skb2->dst->pmtu = rel_info;
+ rel_info = htonl(rel_info);
+ } else if (type == ICMP_TIME_EXCEEDED) {
+ struct ip_tunnel *t = (struct ip_tunnel*)skb2->dev->priv;
+ if (t->parms.iph.ttl) {
+ rel_type = ICMP_DEST_UNREACH;
+ rel_code = ICMP_HOST_UNREACH;
+ }
+ }
+
+ icmp_send(skb2, rel_type, rel_code, rel_info);
+ kfree_skb(skb2);
+ return;
+#endif
+}
+
+int ipip_rcv(struct sk_buff *skb, unsigned short len)
+{
+ struct iphdr *iph;
+ struct ip_tunnel *tunnel;
+
+ iph = skb->nh.iph;
+ skb->mac.raw = skb->nh.raw;
+ skb->nh.raw = skb_pull(skb, skb->h.raw - skb->data);
+ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
+ skb->protocol = __constant_htons(ETH_P_IP);
+ skb->ip_summed = 0;
+ skb->pkt_type = PACKET_HOST;
+
+ if ((tunnel = ipip_tunnel_lookup(iph->saddr, iph->daddr)) != NULL) {
+ tunnel->stat.rx_packets++;
+ tunnel->stat.rx_bytes += skb->len;
+ skb->dev = tunnel->dev;
+ dst_release(skb->dst);
+ skb->dst = NULL;
+ netif_rx(skb);
+ return 0;
+ }
+
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
+ kfree_skb(skb);
+ return 0;
+}
+
+/*
+ * This function assumes it is being called from dev_queue_xmit()
+ * and that skb is filled properly by that function.
+ */
+
+static int ipip_tunnel_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct ip_tunnel *tunnel = (struct ip_tunnel*)dev->priv;
+ struct net_device_stats *stats = &tunnel->stat;
+ struct iphdr *tiph = &tunnel->parms.iph;
+ u8 tos = tunnel->parms.iph.tos;
+ u16 df = tiph->frag_off;
+ struct rtable *rt; /* Route to the other host */
+ struct device *tdev; /* Device to other host */
+ struct iphdr *old_iph = skb->nh.iph;
+ struct iphdr *iph; /* Our new IP header */
+ int max_headroom; /* The extra header space needed */
+ u32 dst = tiph->daddr;
+ int mtu;
+
+ if (tunnel->recursion++) {
+ tunnel->stat.collisions++;
+ goto tx_error;
+ }
+
+ if (skb->protocol != __constant_htons(ETH_P_IP))
+ goto tx_error;
+
+ if (tos&1)
+ tos = old_iph->tos;
+
+ if (!dst) {
+ /* NBMA tunnel */
+ if ((rt = (struct rtable*)skb->dst) == NULL) {
+ tunnel->stat.tx_fifo_errors++;
+ goto tx_error;
+ }
+ if ((dst = rt->rt_gateway) == 0)
+ goto tx_error_icmp;
+ }
+
+ if (ip_route_output(&rt, dst, tiph->saddr, RT_TOS(tos), tunnel->parms.link)) {
+ tunnel->stat.tx_carrier_errors++;
+ goto tx_error_icmp;
+ }
+ tdev = rt->u.dst.dev;
+
+ if (tdev == dev) {
+ ip_rt_put(rt);
+ tunnel->stat.collisions++;
+ goto tx_error;
+ }
+
+ mtu = rt->u.dst.pmtu - sizeof(struct iphdr);
+ if (mtu < 68) {
+ tunnel->stat.collisions++;
+ ip_rt_put(rt);
+ goto tx_error;
+ }
+ if (skb->dst && mtu < skb->dst->pmtu)
+ skb->dst->pmtu = mtu;
+
+ df |= (old_iph->frag_off&__constant_htons(IP_DF));
+
+ if ((old_iph->frag_off&__constant_htons(IP_DF)) && mtu < ntohs(old_iph->tot_len)) {
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
+ ip_rt_put(rt);
+ goto tx_error;
+ }
+
+ if (tunnel->err_count > 0) {
+ if (jiffies - tunnel->err_time < IPTUNNEL_ERR_TIMEO) {
+ tunnel->err_count--;
+ dst_link_failure(skb);
+ } else
+ tunnel->err_count = 0;
+ }
+
+ skb->h.raw = skb->nh.raw;
+
+ /*
+ * Okay, now see if we can stuff it in the buffer as-is.
+ */
+ max_headroom = (((tdev->hard_header_len+15)&~15)+sizeof(struct iphdr));
+
+ if (skb_headroom(skb) < max_headroom || skb_cloned(skb) || skb_shared(skb)) {
+ struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
+ if (!new_skb) {
+ ip_rt_put(rt);
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ tunnel->recursion--;
+ return 0;
+ }
+ if (skb->sk)
+ skb_set_owner_w(new_skb, skb->sk);
+ dev_kfree_skb(skb);
+ skb = new_skb;
+ }
+
+ skb->nh.raw = skb_push(skb, sizeof(struct iphdr));
+ memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
+ dst_release(skb->dst);
+ skb->dst = &rt->u.dst;
+
+ /*
+ * Push down and install the IPIP header.
+ */
+
+ iph = skb->nh.iph;
+ iph->version = 4;
+ iph->ihl = sizeof(struct iphdr)>>2;
+ iph->frag_off = df;
+ iph->protocol = IPPROTO_IPIP;
+ iph->tos = tos;
+ iph->daddr = rt->rt_dst;
+ iph->saddr = rt->rt_src;
+
+ if ((iph->ttl = tiph->ttl) == 0)
+ iph->ttl = old_iph->ttl;
+
+ iph->tot_len = htons(skb->len);
+ iph->id = htons(ip_id_count++);
+ ip_send_check(iph);
+
+ stats->tx_bytes += skb->len;
+ stats->tx_packets++;
+ ip_send(skb);
+ tunnel->recursion--;
+ return 0;
+
+tx_error_icmp:
+ dst_link_failure(skb);
+tx_error:
+ stats->tx_errors++;
+ dev_kfree_skb(skb);
+ tunnel->recursion--;
+ return 0;
+}
+
+static int
+ipip_tunnel_ioctl (struct device *dev, struct ifreq *ifr, int cmd)
+{
+ int err = 0;
+ struct ip_tunnel_parm p;
+ struct ip_tunnel *t;
+
+ MOD_INC_USE_COUNT;
+
+ switch (cmd) {
+ case SIOCGETTUNNEL:
+ t = NULL;
+ if (dev == &ipip_fb_tunnel_dev) {
+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
+ err = -EFAULT;
+ break;
+ }
+ t = ipip_tunnel_locate(&p, 0);
+ }
+ if (t == NULL)
+ t = (struct ip_tunnel*)dev->priv;
+ memcpy(&p, &t->parms, sizeof(p));
+ if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
+ err = -EFAULT;
+ break;
+
+ case SIOCADDTUNNEL:
+ case SIOCCHGTUNNEL:
+ err = -EPERM;
+ if (!capable(CAP_NET_ADMIN))
+ goto done;
+
+ err = -EFAULT;
+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
+ goto done;
+
+ err = -EINVAL;
+ if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPIP ||
+ p.iph.ihl != 5 || (p.iph.frag_off&__constant_htons(~IP_DF)))
+ goto done;
+ if (p.iph.ttl)
+ p.iph.frag_off |= __constant_htons(IP_DF);
+
+ t = ipip_tunnel_locate(&p, cmd == SIOCADDTUNNEL);
+
+ if (dev != &ipip_fb_tunnel_dev && cmd == SIOCCHGTUNNEL &&
+ t != &ipip_fb_tunnel) {
+ if (t != NULL) {
+ if (t->dev != dev) {
+ err = -EEXIST;
+ break;
+ }
+ } else {
+ if (((dev->flags&IFF_POINTOPOINT) && !p.iph.daddr) ||
+ (!(dev->flags&IFF_POINTOPOINT) && p.iph.daddr)) {
+ err = -EINVAL;
+ break;
+ }
+ t = (struct ip_tunnel*)dev->priv;
+ start_bh_atomic();
+ ipip_tunnel_unlink(t);
+ t->parms.iph.saddr = p.iph.saddr;
+ t->parms.iph.daddr = p.iph.daddr;
+ memcpy(dev->dev_addr, &p.iph.saddr, 4);
+ memcpy(dev->broadcast, &p.iph.daddr, 4);
+ ipip_tunnel_link(t);
+ end_bh_atomic();
+ netdev_state_change(dev);
+ }
+ }
+
+ if (t) {
+ err = 0;
+ if (cmd == SIOCCHGTUNNEL) {
+ t->parms.iph.ttl = p.iph.ttl;
+ t->parms.iph.tos = p.iph.tos;
+ t->parms.iph.frag_off = p.iph.frag_off;
+ }
+ if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p)))
+ err = -EFAULT;
+ } else
+ err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
+ break;
+
+ case SIOCDELTUNNEL:
+ err = -EPERM;
+ if (!capable(CAP_NET_ADMIN))
+ goto done;
+
+ if (dev == &ipip_fb_tunnel_dev) {
+ err = -EFAULT;
+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
+ goto done;
+ err = -ENOENT;
+ if ((t = ipip_tunnel_locate(&p, 0)) == NULL)
+ goto done;
+ err = -EPERM;
+ if (t == &ipip_fb_tunnel)
+ goto done;
+ }
+ err = unregister_netdevice(dev);
+ break;
+
+ default:
+ err = -EINVAL;
+ }
+
+done:
+ MOD_DEC_USE_COUNT;
+ return err;
+}
+
+static struct net_device_stats *ipip_tunnel_get_stats(struct device *dev)
+{
+ return &(((struct ip_tunnel*)dev->priv)->stat);
+}
+
+static int ipip_tunnel_change_mtu(struct device *dev, int new_mtu)
+{
+ if (new_mtu < 68 || new_mtu > 0xFFF8 - sizeof(struct iphdr))
+ return -EINVAL;
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+static void ipip_tunnel_init_gen(struct device *dev)
+{
+ struct ip_tunnel *t = (struct ip_tunnel*)dev->priv;
+
+ dev->destructor = ipip_tunnel_destroy;
+ dev->hard_start_xmit = ipip_tunnel_xmit;
+ dev->get_stats = ipip_tunnel_get_stats;
+ dev->do_ioctl = ipip_tunnel_ioctl;
+ dev->change_mtu = ipip_tunnel_change_mtu;
+
+ dev_init_buffers(dev);
+
+ dev->type = ARPHRD_TUNNEL;
+ dev->hard_header_len = LL_MAX_HEADER + sizeof(struct iphdr);
+ dev->mtu = 1500 - sizeof(struct iphdr);
+ dev->flags = IFF_NOARP;
+ dev->iflink = 0;
+ dev->addr_len = 4;
+ memcpy(dev->dev_addr, &t->parms.iph.saddr, 4);
+ memcpy(dev->broadcast, &t->parms.iph.daddr, 4);
+}
+
+static int ipip_tunnel_init(struct device *dev)
+{
+ struct device *tdev = NULL;
+ struct ip_tunnel *tunnel;
+ struct iphdr *iph;
+
+ tunnel = (struct ip_tunnel*)dev->priv;
+ iph = &tunnel->parms.iph;
+
+ ipip_tunnel_init_gen(dev);
+
+ if (iph->daddr) {
+ struct rtable *rt;
+ if (!ip_route_output(&rt, iph->daddr, iph->saddr, RT_TOS(iph->tos), tunnel->parms.link)) {
+ tdev = rt->u.dst.dev;
+ ip_rt_put(rt);
+ }
+ dev->flags |= IFF_POINTOPOINT;
+ }
+
+ if (!tdev && tunnel->parms.link)
+ tdev = dev_get_by_index(tunnel->parms.link);
+
+ if (tdev) {
+ dev->hard_header_len = tdev->hard_header_len + sizeof(struct iphdr);
+ dev->mtu = tdev->mtu - sizeof(struct iphdr);
+ }
+ dev->iflink = tunnel->parms.link;
+
+ return 0;
+}
+
+#ifdef MODULE
+static int ipip_fb_tunnel_open(struct device *dev)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int ipip_fb_tunnel_close(struct device *dev)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+#endif
+
+__initfunc(int ipip_fb_tunnel_init(struct device *dev))
+{
+ struct iphdr *iph;
+
+ ipip_tunnel_init_gen(dev);
+#ifdef MODULE
+ dev->open = ipip_fb_tunnel_open;
+ dev->stop = ipip_fb_tunnel_close;
+#endif
+
+ iph = &ipip_fb_tunnel.parms.iph;
+ iph->version = 4;
+ iph->protocol = IPPROTO_IPIP;
+ iph->ihl = 5;
+
+ tunnels_wc[0] = &ipip_fb_tunnel;
+ return 0;
+}
+
+static struct inet_protocol ipip_protocol = {
+ ipip_rcv, /* IPIP handler */
+ ipip_err, /* TUNNEL error control */
+ 0, /* next */
+ IPPROTO_IPIP, /* protocol ID */
+ 0, /* copy */
+ NULL, /* data */
+ "IPIP" /* name */
+};
+
+#ifdef MODULE
+int init_module(void)
+#else
+__initfunc(int ipip_init(void))
+#endif
+{
+ printk(KERN_INFO "IPv4 over IPv4 tunneling driver\n");
+
+ ipip_fb_tunnel_dev.priv = (void*)&ipip_fb_tunnel;
+ ipip_fb_tunnel_dev.name = ipip_fb_tunnel.parms.name;
+#ifdef MODULE
+ register_netdev(&ipip_fb_tunnel_dev);
+#else
+ register_netdevice(&ipip_fb_tunnel_dev);
+#endif
+
+ inet_add_protocol(&ipip_protocol);
+ return 0;
+}
+
+#ifdef MODULE
+
+void cleanup_module(void)
+{
+ if ( inet_del_protocol(&ipip_protocol) < 0 )
+ printk(KERN_INFO "ipip close: can't remove protocol\n");
+
+ unregister_netdevice(&ipip_fb_tunnel_dev);
+}
+
+#endif
diff --git a/pfinet/linux-src/net/ipv4/ipmr.c b/pfinet/linux-src/net/ipv4/ipmr.c
new file mode 100644
index 00000000..08d1a364
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/ipmr.c
@@ -0,0 +1,1609 @@
+/*
+ * IP multicast routing support for mrouted 3.6/3.8
+ *
+ * (c) 1995 Alan Cox, <alan@redhat.com>
+ * Linux Consultancy and Custom Driver Development
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Version: $Id: ipmr.c,v 1.40.2.2 1999/06/20 21:27:44 davem Exp $
+ *
+ * Fixes:
+ * Michael Chastain : Incorrect size of copying.
+ * Alan Cox : Added the cache manager code
+ * Alan Cox : Fixed the clone/copy bug and device race.
+ * Mike McLagan : Routing by source
+ * Malcolm Beattie : Buffer handling fixes.
+ * Alexey Kuznetsov : Double buffer free and other fixes.
+ * SVR Anand : Fixed several multicast bugs and problems.
+ * Alexey Kuznetsov : Status, optimisations and more.
+ * Brad Parker : Better behaviour on mrouted upcall
+ * overflow.
+ * Carlos Picoto : PIMv1 Support
+ * Pavlin Ivanov Radoslavov: PIMv2 Registers must checksum only PIM header
+ * Relax this requrement to work with older peers.
+ *
+ */
+
+#include <linux/config.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/igmp.h>
+#include <linux/proc_fs.h>
+#include <linux/mroute.h>
+#include <linux/init.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/icmp.h>
+#include <net/udp.h>
+#include <net/raw.h>
+#include <linux/notifier.h>
+#include <linux/if_arp.h>
+#include <linux/ip_fw.h>
+#include <linux/firewall.h>
+#include <net/ipip.h>
+#include <net/checksum.h>
+
+#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
+#define CONFIG_IP_PIMSM 1
+#endif
+
+/*
+ * Multicast router control variables
+ */
+
+static struct vif_device vif_table[MAXVIFS]; /* Devices */
+static unsigned long vifc_map; /* Active device map */
+static int maxvif;
+int mroute_do_assert = 0; /* Set in PIM assert */
+int mroute_do_pim = 0;
+static struct mfc_cache *mfc_cache_array[MFC_LINES]; /* Forwarding cache */
+int cache_resolve_queue_len = 0; /* Size of unresolved */
+
+static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local);
+static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert);
+static int ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm);
+
+extern struct inet_protocol pim_protocol;
+
+static
+struct device *ipmr_new_tunnel(struct vifctl *v)
+{
+ struct device *dev = NULL;
+
+ rtnl_lock();
+ dev = dev_get("tunl0");
+
+ if (dev) {
+ int err;
+ struct ifreq ifr;
+ mm_segment_t oldfs;
+ struct ip_tunnel_parm p;
+ struct in_device *in_dev;
+
+ memset(&p, 0, sizeof(p));
+ p.iph.daddr = v->vifc_rmt_addr.s_addr;
+ p.iph.saddr = v->vifc_lcl_addr.s_addr;
+ p.iph.version = 4;
+ p.iph.ihl = 5;
+ p.iph.protocol = IPPROTO_IPIP;
+ sprintf(p.name, "dvmrp%d", v->vifc_vifi);
+ ifr.ifr_ifru.ifru_data = (void*)&p;
+
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ err = dev->do_ioctl(dev, &ifr, SIOCADDTUNNEL);
+ set_fs(oldfs);
+
+ if (err == 0 && (dev = dev_get(p.name)) != NULL) {
+ dev->flags |= IFF_MULTICAST;
+
+ in_dev = dev->ip_ptr;
+ if (in_dev == NULL && (in_dev = inetdev_init(dev)) == NULL)
+ goto failure;
+ in_dev->cnf.rp_filter = 0;
+
+ if (dev_open(dev))
+ goto failure;
+ }
+ }
+ rtnl_unlock();
+ return dev;
+
+failure:
+ unregister_netdevice(dev);
+ rtnl_unlock();
+ return NULL;
+}
+
+#ifdef CONFIG_IP_PIMSM
+
+static int reg_vif_num = -1;
+static struct device * reg_dev;
+
+static int reg_vif_xmit(struct sk_buff *skb, struct device *dev)
+{
+ ((struct net_device_stats*)dev->priv)->tx_bytes += skb->len;
+ ((struct net_device_stats*)dev->priv)->tx_packets++;
+ ipmr_cache_report(skb, reg_vif_num, IGMPMSG_WHOLEPKT);
+ kfree_skb(skb);
+ return 0;
+}
+
+static struct net_device_stats *reg_vif_get_stats(struct device *dev)
+{
+ return (struct net_device_stats*)dev->priv;
+}
+
+static
+struct device *ipmr_reg_vif(struct vifctl *v)
+{
+ struct device *dev;
+ struct in_device *in_dev;
+ int size;
+
+ size = sizeof(*dev) + IFNAMSIZ + sizeof(struct net_device_stats);
+ dev = kmalloc(size, GFP_KERNEL);
+ if (!dev)
+ return NULL;
+
+ memset(dev, 0, size);
+
+ dev->priv = dev + 1;
+ dev->name = dev->priv + sizeof(struct net_device_stats);
+
+ strcpy(dev->name, "pimreg");
+
+ dev->type = ARPHRD_PIMREG;
+ dev->mtu = 1500 - sizeof(struct iphdr) - 8;
+ dev->flags = IFF_NOARP;
+ dev->hard_start_xmit = reg_vif_xmit;
+ dev->get_stats = reg_vif_get_stats;
+
+ rtnl_lock();
+
+ if (register_netdevice(dev)) {
+ rtnl_unlock();
+ kfree(dev);
+ return NULL;
+ }
+ dev->iflink = 0;
+
+ if ((in_dev = inetdev_init(dev)) == NULL)
+ goto failure;
+
+ in_dev->cnf.rp_filter = 0;
+
+ if (dev_open(dev))
+ goto failure;
+
+ rtnl_unlock();
+ reg_dev = dev;
+ return dev;
+
+failure:
+ unregister_netdevice(dev);
+ rtnl_unlock();
+ kfree(dev);
+ return NULL;
+}
+#endif
+
+/*
+ * Delete a VIF entry
+ */
+
+static int vif_delete(int vifi)
+{
+ struct vif_device *v;
+ struct device *dev;
+ struct in_device *in_dev;
+
+ if (vifi < 0 || vifi >= maxvif || !(vifc_map&(1<<vifi)))
+ return -EADDRNOTAVAIL;
+
+ v = &vif_table[vifi];
+
+ dev = v->dev;
+ v->dev = NULL;
+ vifc_map &= ~(1<<vifi);
+
+ if ((in_dev = dev->ip_ptr) != NULL)
+ in_dev->cnf.mc_forwarding = 0;
+
+ dev_set_allmulti(dev, -1);
+ ip_rt_multicast_event(in_dev);
+
+ if (v->flags&(VIFF_TUNNEL|VIFF_REGISTER)) {
+#ifdef CONFIG_IP_PIMSM
+ if (vifi == reg_vif_num) {
+ reg_vif_num = -1;
+ reg_dev = NULL;
+ }
+#endif
+ unregister_netdevice(dev);
+ if (v->flags&VIFF_REGISTER)
+ kfree(dev);
+ }
+
+ if (vifi+1 == maxvif) {
+ int tmp;
+ for (tmp=vifi-1; tmp>=0; tmp--) {
+ if (vifc_map&(1<<tmp))
+ break;
+ }
+ maxvif = tmp+1;
+ }
+ return 0;
+}
+
+static void ipmr_update_threshoulds(struct mfc_cache *cache, unsigned char *ttls)
+{
+ int vifi;
+
+ start_bh_atomic();
+
+ cache->mfc_minvif = MAXVIFS;
+ cache->mfc_maxvif = 0;
+ memset(cache->mfc_ttls, 255, MAXVIFS);
+
+ for (vifi=0; vifi<maxvif; vifi++) {
+ if (vifc_map&(1<<vifi) && ttls[vifi] && ttls[vifi] < 255) {
+ cache->mfc_ttls[vifi] = ttls[vifi];
+ if (cache->mfc_minvif > vifi)
+ cache->mfc_minvif = vifi;
+ if (cache->mfc_maxvif <= vifi)
+ cache->mfc_maxvif = vifi + 1;
+ }
+ }
+ end_bh_atomic();
+}
+
+/*
+ * Delete a multicast route cache entry
+ */
+
+static void ipmr_cache_delete(struct mfc_cache *cache)
+{
+ struct sk_buff *skb;
+ int line;
+ struct mfc_cache **cp;
+
+ /*
+ * Find the right cache line
+ */
+
+ line=MFC_HASH(cache->mfc_mcastgrp,cache->mfc_origin);
+ cp=&(mfc_cache_array[line]);
+
+ if(cache->mfc_flags&MFC_QUEUED)
+ del_timer(&cache->mfc_timer);
+
+ /*
+ * Unlink the buffer
+ */
+
+ while(*cp!=NULL)
+ {
+ if(*cp==cache)
+ {
+ *cp=cache->next;
+ break;
+ }
+ cp=&((*cp)->next);
+ }
+
+ /*
+ * Free the buffer. If it is a pending resolution
+ * clean up the other resources.
+ */
+
+ if(cache->mfc_flags&MFC_QUEUED)
+ {
+ cache_resolve_queue_len--;
+ while((skb=skb_dequeue(&cache->mfc_unresolved))) {
+#ifdef CONFIG_RTNETLINK
+ if (skb->nh.iph->version == 0) {
+ struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
+ nlh->nlmsg_type = NLMSG_ERROR;
+ nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
+ skb_trim(skb, nlh->nlmsg_len);
+ ((struct nlmsgerr*)NLMSG_DATA(nlh))->error = -ETIMEDOUT;
+ netlink_unicast(rtnl, skb, NETLINK_CB(skb).dst_pid, MSG_DONTWAIT);
+ } else
+#endif
+ kfree_skb(skb);
+ }
+ }
+ kfree_s(cache,sizeof(*cache));
+}
+
+/*
+ * Cache expiry timer
+ */
+
+static void ipmr_cache_timer(unsigned long data)
+{
+ struct mfc_cache *cache=(struct mfc_cache *)data;
+ ipmr_cache_delete(cache);
+}
+
+/*
+ * Insert a multicast cache entry
+ */
+
+static void ipmr_cache_insert(struct mfc_cache *c)
+{
+ int line=MFC_HASH(c->mfc_mcastgrp,c->mfc_origin);
+ c->next=mfc_cache_array[line];
+ mfc_cache_array[line]=c;
+}
+
+/*
+ * Find a multicast cache entry
+ */
+
+struct mfc_cache *ipmr_cache_find(__u32 origin, __u32 mcastgrp)
+{
+ int line=MFC_HASH(mcastgrp,origin);
+ struct mfc_cache *cache;
+
+ cache=mfc_cache_array[line];
+ while(cache!=NULL)
+ {
+ if(cache->mfc_origin==origin && cache->mfc_mcastgrp==mcastgrp)
+ return cache;
+ cache=cache->next;
+ }
+ return NULL;
+}
+
+/*
+ * Allocate a multicast cache entry
+ */
+
+static struct mfc_cache *ipmr_cache_alloc(int priority)
+{
+ struct mfc_cache *c=(struct mfc_cache *)kmalloc(sizeof(struct mfc_cache), priority);
+ if(c==NULL)
+ return NULL;
+ memset(c, 0, sizeof(*c));
+ skb_queue_head_init(&c->mfc_unresolved);
+ init_timer(&c->mfc_timer);
+ c->mfc_timer.data=(long)c;
+ c->mfc_timer.function=ipmr_cache_timer;
+ c->mfc_minvif = MAXVIFS;
+ return c;
+}
+
+/*
+ * A cache entry has gone into a resolved state from queued
+ */
+
+static void ipmr_cache_resolve(struct mfc_cache *cache)
+{
+ struct sk_buff *skb;
+
+ start_bh_atomic();
+
+ /*
+ * Kill the queue entry timer.
+ */
+
+ del_timer(&cache->mfc_timer);
+
+ if (cache->mfc_flags&MFC_QUEUED) {
+ cache->mfc_flags&=~MFC_QUEUED;
+ cache_resolve_queue_len--;
+ }
+
+ end_bh_atomic();
+
+ /*
+ * Play the pending entries through our router
+ */
+ while((skb=skb_dequeue(&cache->mfc_unresolved))) {
+#ifdef CONFIG_RTNETLINK
+ if (skb->nh.iph->version == 0) {
+ int err;
+ struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
+
+ if (ipmr_fill_mroute(skb, cache, NLMSG_DATA(nlh)) > 0) {
+ nlh->nlmsg_len = skb->tail - (u8*)nlh;
+ } else {
+ nlh->nlmsg_type = NLMSG_ERROR;
+ nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
+ skb_trim(skb, nlh->nlmsg_len);
+ ((struct nlmsgerr*)NLMSG_DATA(nlh))->error = -EMSGSIZE;
+ }
+ err = netlink_unicast(rtnl, skb, NETLINK_CB(skb).dst_pid, MSG_DONTWAIT);
+ } else
+#endif
+ ip_mr_forward(skb, cache, 0);
+ }
+}
+
+/*
+ * Bounce a cache query up to mrouted. We could use netlink for this but mrouted
+ * expects the following bizarre scheme..
+ */
+
+static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert)
+{
+ struct sk_buff *skb;
+ int ihl = pkt->nh.iph->ihl<<2;
+ struct igmphdr *igmp;
+ struct igmpmsg *msg;
+ int ret;
+
+ if (mroute_socket==NULL)
+ return -EINVAL;
+
+#ifdef CONFIG_IP_PIMSM
+ if (assert == IGMPMSG_WHOLEPKT)
+ skb = skb_realloc_headroom(pkt, sizeof(struct iphdr));
+ else
+#endif
+ skb = alloc_skb(128, GFP_ATOMIC);
+
+ if(!skb)
+ return -ENOBUFS;
+
+#ifdef CONFIG_IP_PIMSM
+ if (assert == IGMPMSG_WHOLEPKT) {
+ /* Ugly, but we have no choice with this interface.
+ Duplicate old header, fix ihl, length etc.
+ And all this only to mangle msg->im_msgtype and
+ to set msg->im_mbz to "mbz" :-)
+ */
+ msg = (struct igmpmsg*)skb_push(skb, sizeof(struct iphdr));
+ skb->nh.raw = skb->h.raw = (u8*)msg;
+ memcpy(msg, pkt->nh.raw, sizeof(struct iphdr));
+ msg->im_msgtype = IGMPMSG_WHOLEPKT;
+ msg->im_mbz = 0;
+ msg->im_vif = reg_vif_num;
+ skb->nh.iph->ihl = sizeof(struct iphdr) >> 2;
+ skb->nh.iph->tot_len = htons(ntohs(pkt->nh.iph->tot_len) + sizeof(struct iphdr));
+ } else
+#endif
+ {
+
+ /*
+ * Copy the IP header
+ */
+
+ skb->nh.iph = (struct iphdr *)skb_put(skb, ihl);
+ memcpy(skb->data,pkt->data,ihl);
+ skb->nh.iph->protocol = 0; /* Flag to the kernel this is a route add */
+ msg = (struct igmpmsg*)skb->nh.iph;
+ msg->im_vif = vifi;
+ skb->dst = dst_clone(pkt->dst);
+
+ /*
+ * Add our header
+ */
+
+ igmp=(struct igmphdr *)skb_put(skb,sizeof(struct igmphdr));
+ igmp->type =
+ msg->im_msgtype = assert;
+ igmp->code = 0;
+ skb->nh.iph->tot_len=htons(skb->len); /* Fix the length */
+ skb->h.raw = skb->nh.raw;
+ }
+
+ /*
+ * Deliver to mrouted
+ */
+ if ((ret=sock_queue_rcv_skb(mroute_socket,skb))<0) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "mroute: pending queue full, dropping entries.\n");
+ kfree_skb(skb);
+ }
+
+ return ret;
+}
+
+/*
+ * Queue a packet for resolution
+ */
+
+static int ipmr_cache_unresolved(struct mfc_cache *cache, vifi_t vifi, struct sk_buff *skb)
+{
+ if(cache==NULL)
+ {
+ /*
+ * Create a new entry if allowable
+ */
+ if(cache_resolve_queue_len>=10 || (cache=ipmr_cache_alloc(GFP_ATOMIC))==NULL)
+ {
+ kfree_skb(skb);
+ return -ENOBUFS;
+ }
+ /*
+ * Fill in the new cache entry
+ */
+ cache->mfc_parent=ALL_VIFS;
+ cache->mfc_origin=skb->nh.iph->saddr;
+ cache->mfc_mcastgrp=skb->nh.iph->daddr;
+ cache->mfc_flags=MFC_QUEUED;
+ /*
+ * Link to the unresolved list
+ */
+ ipmr_cache_insert(cache);
+ cache_resolve_queue_len++;
+ /*
+ * Fire off the expiry timer
+ */
+ cache->mfc_timer.expires=jiffies+10*HZ;
+ add_timer(&cache->mfc_timer);
+ /*
+ * Reflect first query at mrouted.
+ */
+ if(mroute_socket)
+ {
+ /* If the report failed throw the cache entry
+ out - Brad Parker
+
+ OK, OK, Brad. Only do not forget to free skb
+ and return :-) --ANK
+ */
+ if (ipmr_cache_report(skb, vifi, IGMPMSG_NOCACHE)<0) {
+ ipmr_cache_delete(cache);
+ kfree_skb(skb);
+ return -ENOBUFS;
+ }
+ }
+ }
+ /*
+ * See if we can append the packet
+ */
+ if(cache->mfc_queuelen>3)
+ {
+ kfree_skb(skb);
+ return -ENOBUFS;
+ }
+ cache->mfc_queuelen++;
+ skb_queue_tail(&cache->mfc_unresolved,skb);
+ return 0;
+}
+
+/*
+ * MFC cache manipulation by user space mroute daemon
+ */
+
+int ipmr_mfc_modify(int action, struct mfcctl *mfc)
+{
+ struct mfc_cache *cache;
+
+ if(!MULTICAST(mfc->mfcc_mcastgrp.s_addr))
+ return -EINVAL;
+ /*
+ * Find the cache line
+ */
+
+ start_bh_atomic();
+
+ cache=ipmr_cache_find(mfc->mfcc_origin.s_addr,mfc->mfcc_mcastgrp.s_addr);
+
+ /*
+ * Delete an entry
+ */
+ if(action==MRT_DEL_MFC)
+ {
+ if(cache)
+ {
+ ipmr_cache_delete(cache);
+ end_bh_atomic();
+ return 0;
+ }
+ end_bh_atomic();
+ return -ENOENT;
+ }
+ if(cache)
+ {
+
+ /*
+ * Update the cache, see if it frees a pending queue
+ */
+
+ cache->mfc_flags|=MFC_RESOLVED;
+ cache->mfc_parent=mfc->mfcc_parent;
+ ipmr_update_threshoulds(cache, mfc->mfcc_ttls);
+
+ /*
+ * Check to see if we resolved a queued list. If so we
+ * need to send on the frames and tidy up.
+ */
+
+ if(cache->mfc_flags&MFC_QUEUED)
+ ipmr_cache_resolve(cache); /* Unhook & send the frames */
+ end_bh_atomic();
+ return 0;
+ }
+
+ /*
+ * Unsolicited update - that's ok, add anyway.
+ */
+
+
+ cache=ipmr_cache_alloc(GFP_ATOMIC);
+ if(cache==NULL)
+ {
+ end_bh_atomic();
+ return -ENOMEM;
+ }
+ cache->mfc_flags=MFC_RESOLVED;
+ cache->mfc_origin=mfc->mfcc_origin.s_addr;
+ cache->mfc_mcastgrp=mfc->mfcc_mcastgrp.s_addr;
+ cache->mfc_parent=mfc->mfcc_parent;
+ ipmr_update_threshoulds(cache, mfc->mfcc_ttls);
+ ipmr_cache_insert(cache);
+ end_bh_atomic();
+ return 0;
+}
+
+static void mrtsock_destruct(struct sock *sk)
+{
+ if (sk == mroute_socket) {
+ ipv4_devconf.mc_forwarding = 0;
+
+ mroute_socket=NULL;
+ synchronize_bh();
+
+ mroute_close(sk);
+ }
+}
+
+/*
+ * Socket options and virtual interface manipulation. The whole
+ * virtual interface system is a complete heap, but unfortunately
+ * that's how BSD mrouted happens to think. Maybe one day with a proper
+ * MOSPF/PIM router set up we can clean this up.
+ */
+
+int ip_mroute_setsockopt(struct sock *sk,int optname,char *optval,int optlen)
+{
+ struct vifctl vif;
+ struct mfcctl mfc;
+
+ if(optname!=MRT_INIT)
+ {
+ if(sk!=mroute_socket)
+ return -EACCES;
+ }
+
+ switch(optname)
+ {
+ case MRT_INIT:
+ if(sk->type!=SOCK_RAW || sk->num!=IPPROTO_IGMP)
+ return -EOPNOTSUPP;
+ if(optlen!=sizeof(int))
+ return -ENOPROTOOPT;
+ {
+ int opt;
+ if (get_user(opt,(int *)optval))
+ return -EFAULT;
+ if (opt != 1)
+ return -ENOPROTOOPT;
+ }
+ if(mroute_socket)
+ return -EADDRINUSE;
+ mroute_socket=sk;
+ ipv4_devconf.mc_forwarding = 1;
+ if (ip_ra_control(sk, 1, mrtsock_destruct) == 0)
+ return 0;
+ mrtsock_destruct(sk);
+ return -EADDRINUSE;
+ case MRT_DONE:
+ return ip_ra_control(sk, 0, NULL);
+ case MRT_ADD_VIF:
+ case MRT_DEL_VIF:
+ if(optlen!=sizeof(vif))
+ return -EINVAL;
+ if (copy_from_user(&vif,optval,sizeof(vif)))
+ return -EFAULT;
+ if(vif.vifc_vifi >= MAXVIFS)
+ return -ENFILE;
+ if(optname==MRT_ADD_VIF)
+ {
+ struct vif_device *v=&vif_table[vif.vifc_vifi];
+ struct device *dev;
+ struct in_device *in_dev;
+
+ /* Is vif busy ? */
+ if (vifc_map&(1<<vif.vifc_vifi))
+ return -EADDRINUSE;
+
+ switch (vif.vifc_flags) {
+#ifdef CONFIG_IP_PIMSM
+ case VIFF_REGISTER:
+
+ /*
+ * Special Purpose VIF in PIM
+ * All the packets will be sent to the daemon
+ */
+ if (reg_vif_num >= 0)
+ return -EADDRINUSE;
+ reg_vif_num = vif.vifc_vifi;
+ dev = ipmr_reg_vif(&vif);
+ if (!dev) {
+ reg_vif_num = -1;
+ return -ENOBUFS;
+ }
+ break;
+#endif
+ case VIFF_TUNNEL:
+ dev = ipmr_new_tunnel(&vif);
+ if (!dev)
+ return -ENOBUFS;
+ break;
+ case 0:
+ dev=ip_dev_find(vif.vifc_lcl_addr.s_addr);
+ if (!dev)
+ return -EADDRNOTAVAIL;
+ break;
+ default:
+#if 0
+ printk(KERN_DEBUG "ipmr_add_vif: flags %02x\n", vif.vifc_flags);
+#endif
+ return -EINVAL;
+ }
+
+ if ((in_dev = dev->ip_ptr) == NULL)
+ return -EADDRNOTAVAIL;
+ if (in_dev->cnf.mc_forwarding)
+ return -EADDRINUSE;
+ in_dev->cnf.mc_forwarding = 1;
+ dev_set_allmulti(dev, +1);
+ ip_rt_multicast_event(in_dev);
+
+ /*
+ * Fill in the VIF structures
+ */
+ start_bh_atomic();
+ v->rate_limit=vif.vifc_rate_limit;
+ v->local=vif.vifc_lcl_addr.s_addr;
+ v->remote=vif.vifc_rmt_addr.s_addr;
+ v->flags=vif.vifc_flags;
+ v->threshold=vif.vifc_threshold;
+ v->dev=dev;
+ v->bytes_in = 0;
+ v->bytes_out = 0;
+ v->pkt_in = 0;
+ v->pkt_out = 0;
+ v->link = dev->ifindex;
+ if (vif.vifc_flags&(VIFF_TUNNEL|VIFF_REGISTER))
+ v->link = dev->iflink;
+ vifc_map|=(1<<vif.vifc_vifi);
+ if (vif.vifc_vifi+1 > maxvif)
+ maxvif = vif.vifc_vifi+1;
+ end_bh_atomic();
+ return 0;
+ } else {
+ int ret;
+ rtnl_lock();
+ ret = vif_delete(vif.vifc_vifi);
+ rtnl_unlock();
+ return ret;
+ }
+
+ /*
+ * Manipulate the forwarding caches. These live
+ * in a sort of kernel/user symbiosis.
+ */
+ case MRT_ADD_MFC:
+ case MRT_DEL_MFC:
+ if(optlen!=sizeof(mfc))
+ return -EINVAL;
+ if (copy_from_user(&mfc,optval, sizeof(mfc)))
+ return -EFAULT;
+ return ipmr_mfc_modify(optname, &mfc);
+ /*
+ * Control PIM assert.
+ */
+ case MRT_ASSERT:
+ {
+ int v;
+ if(get_user(v,(int *)optval))
+ return -EFAULT;
+ mroute_do_assert=(v)?1:0;
+ return 0;
+ }
+#ifdef CONFIG_IP_PIMSM
+ case MRT_PIM:
+ {
+ int v;
+ if(get_user(v,(int *)optval))
+ return -EFAULT;
+ v = (v)?1:0;
+ if (v != mroute_do_pim) {
+ mroute_do_pim = v;
+ mroute_do_assert = v;
+#ifdef CONFIG_IP_PIMSM_V2
+ if (mroute_do_pim)
+ inet_add_protocol(&pim_protocol);
+ else
+ inet_del_protocol(&pim_protocol);
+#endif
+ }
+ return 0;
+ }
+#endif
+ /*
+ * Spurious command, or MRT_VERSION which you cannot
+ * set.
+ */
+ default:
+ return -ENOPROTOOPT;
+ }
+}
+
+/*
+ * Getsock opt support for the multicast routing system.
+ */
+
+int ip_mroute_getsockopt(struct sock *sk,int optname,char *optval,int *optlen)
+{
+ int olr;
+ int val;
+
+ if(sk!=mroute_socket)
+ return -EACCES;
+ if(optname!=MRT_VERSION &&
+#ifdef CONFIG_IP_PIMSM
+ optname!=MRT_PIM &&
+#endif
+ optname!=MRT_ASSERT)
+ return -ENOPROTOOPT;
+
+ if(get_user(olr, optlen))
+ return -EFAULT;
+
+ olr=min(olr,sizeof(int));
+ if(put_user(olr,optlen))
+ return -EFAULT;
+ if(optname==MRT_VERSION)
+ val=0x0305;
+#ifdef CONFIG_IP_PIMSM
+ else if(optname==MRT_PIM)
+ val=mroute_do_pim;
+#endif
+ else
+ val=mroute_do_assert;
+ if(copy_to_user(optval,&val,olr))
+ return -EFAULT;
+ return 0;
+}
+
+/*
+ * The IP multicast ioctl support routines.
+ */
+
+int ipmr_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ struct sioc_sg_req sr;
+ struct sioc_vif_req vr;
+ struct vif_device *vif;
+ struct mfc_cache *c;
+
+ switch(cmd)
+ {
+ case SIOCGETVIFCNT:
+ if (copy_from_user(&vr,(void *)arg,sizeof(vr)))
+ return -EFAULT;
+ if(vr.vifi>=maxvif)
+ return -EINVAL;
+ vif=&vif_table[vr.vifi];
+ if(vifc_map&(1<<vr.vifi))
+ {
+ vr.icount=vif->pkt_in;
+ vr.ocount=vif->pkt_out;
+ vr.ibytes=vif->bytes_in;
+ vr.obytes=vif->bytes_out;
+ if (copy_to_user((void *)arg,&vr,sizeof(vr)))
+ return -EFAULT;
+ return 0;
+ }
+ return -EADDRNOTAVAIL;
+ case SIOCGETSGCNT:
+ if (copy_from_user(&sr,(void *)arg,sizeof(sr)))
+ return -EFAULT;
+ for (c = mfc_cache_array[MFC_HASH(sr.grp.s_addr, sr.src.s_addr)];
+ c; c = c->next) {
+ if (sr.grp.s_addr == c->mfc_mcastgrp &&
+ sr.src.s_addr == c->mfc_origin) {
+ sr.pktcnt = c->mfc_pkt;
+ sr.bytecnt = c->mfc_bytes;
+ sr.wrong_if = c->mfc_wrong_if;
+ if (copy_to_user((void *)arg,&sr,sizeof(sr)))
+ return -EFAULT;
+ return 0;
+ }
+ }
+ return -EADDRNOTAVAIL;
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+/*
+ * Close the multicast socket, and clear the vif tables etc
+ */
+
+void mroute_close(struct sock *sk)
+{
+ int i;
+
+ /*
+ * Shut down all active vif entries
+ */
+ rtnl_lock();
+ for(i=0; i<maxvif; i++)
+ vif_delete(i);
+ rtnl_unlock();
+
+ /*
+ * Wipe the cache
+ */
+ for(i=0;i<MFC_LINES;i++)
+ {
+ start_bh_atomic();
+ while(mfc_cache_array[i]!=NULL)
+ ipmr_cache_delete(mfc_cache_array[i]);
+ end_bh_atomic();
+ }
+}
+
+static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ struct vif_device *v;
+ int ct;
+ if (event != NETDEV_UNREGISTER)
+ return NOTIFY_DONE;
+ v=&vif_table[0];
+ for(ct=0;ct<maxvif;ct++) {
+ if (vifc_map&(1<<ct) && v->dev==ptr)
+ vif_delete(ct);
+ v++;
+ }
+ return NOTIFY_DONE;
+}
+
+
+static struct notifier_block ip_mr_notifier={
+ ipmr_device_event,
+ NULL,
+ 0
+};
+
+/*
+ * Encapsulate a packet by attaching a valid IPIP header to it.
+ * This avoids tunnel drivers and other mess and gives us the speed so
+ * important for multicast video.
+ */
+
+static void ip_encap(struct sk_buff *skb, u32 saddr, u32 daddr)
+{
+ struct iphdr *iph = (struct iphdr *)skb_push(skb,sizeof(struct iphdr));
+
+ iph->version = 4;
+ iph->tos = skb->nh.iph->tos;
+ iph->ttl = skb->nh.iph->ttl;
+ iph->frag_off = 0;
+ iph->daddr = daddr;
+ iph->saddr = saddr;
+ iph->protocol = IPPROTO_IPIP;
+ iph->ihl = 5;
+ iph->tot_len = htons(skb->len);
+ iph->id = htons(ip_id_count++);
+ ip_send_check(iph);
+
+ skb->h.ipiph = skb->nh.iph;
+ skb->nh.iph = iph;
+}
+
+/*
+ * Processing handlers for ipmr_forward
+ */
+
+static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c,
+ int vifi, int last)
+{
+ struct iphdr *iph = skb->nh.iph;
+ struct vif_device *vif = &vif_table[vifi];
+ struct device *dev;
+ struct rtable *rt;
+ int encap = 0;
+ struct sk_buff *skb2;
+
+#ifdef CONFIG_IP_PIMSM
+ if (vif->flags & VIFF_REGISTER) {
+ vif->pkt_out++;
+ vif->bytes_out+=skb->len;
+ ((struct net_device_stats*)vif->dev->priv)->tx_bytes += skb->len;
+ ((struct net_device_stats*)vif->dev->priv)->tx_packets++;
+ ipmr_cache_report(skb, vifi, IGMPMSG_WHOLEPKT);
+ return;
+ }
+#endif
+
+ if (vif->flags&VIFF_TUNNEL) {
+ if (ip_route_output(&rt, vif->remote, vif->local, RT_TOS(iph->tos), vif->link))
+ return;
+ encap = sizeof(struct iphdr);
+ } else {
+ if (ip_route_output(&rt, iph->daddr, 0, RT_TOS(iph->tos), vif->link))
+ return;
+ }
+
+ dev = rt->u.dst.dev;
+
+ if (skb->len+encap > rt->u.dst.pmtu && (ntohs(iph->frag_off) & IP_DF)) {
+ /* Do not fragment multicasts. Alas, IPv4 does not
+ allow to send ICMP, so that packets will disappear
+ to blackhole.
+ */
+
+ ip_statistics.IpFragFails++;
+ ip_rt_put(rt);
+ return;
+ }
+
+ encap += dev->hard_header_len;
+
+ if (skb_headroom(skb) < encap || skb_cloned(skb) || !last)
+ skb2 = skb_realloc_headroom(skb, (encap + 15)&~15);
+ else if (atomic_read(&skb->users) != 1)
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ else {
+ atomic_inc(&skb->users);
+ skb2 = skb;
+ }
+
+ if (skb2 == NULL) {
+ ip_rt_put(rt);
+ return;
+ }
+
+ vif->pkt_out++;
+ vif->bytes_out+=skb->len;
+
+ dst_release(skb2->dst);
+ skb2->dst = &rt->u.dst;
+ iph = skb2->nh.iph;
+ ip_decrease_ttl(iph);
+
+#ifdef CONFIG_FIREWALL
+ if (call_fw_firewall(PF_INET, vif->dev, skb2->nh.iph, NULL, &skb2) < FW_ACCEPT) {
+ kfree_skb(skb2);
+ return;
+ }
+ if (call_out_firewall(PF_INET, vif->dev, skb2->nh.iph, NULL, &skb2) < FW_ACCEPT) {
+ kfree_skb(skb2);
+ return;
+ }
+#endif
+ if (vif->flags & VIFF_TUNNEL) {
+ ip_encap(skb2, vif->local, vif->remote);
+#ifdef CONFIG_FIREWALL
+ /* Double output firewalling on tunnels: one is on tunnel
+ another one is on real device.
+ */
+ if (call_out_firewall(PF_INET, dev, skb2->nh.iph, NULL, &skb2) < FW_ACCEPT) {
+ kfree_skb(skb2);
+ return;
+ }
+#endif
+ ((struct ip_tunnel *)vif->dev->priv)->stat.tx_packets++;
+ ((struct ip_tunnel *)vif->dev->priv)->stat.tx_bytes+=skb2->len;
+ }
+
+ IPCB(skb2)->flags |= IPSKB_FORWARDED;
+
+
+ /*
+ * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
+ * not only before forwarding, but after forwarding on all output
+ * interfaces. It is clear, if mrouter runs a multicasting
+ * program, it should receive packets not depending to what interface
+ * program is joined.
+ * If we will not make it, the program will have to join on all
+ * interfaces. On the other hand, multihoming host (or router, but
+ * not mrouter) cannot join to more than one interface - it will
+ * result in receiving multiple packets.
+ */
+ if (skb2->len <= rt->u.dst.pmtu)
+ skb2->dst->output(skb2);
+ else
+ ip_fragment(skb2, skb2->dst->output);
+}
+
+int ipmr_find_vif(struct device *dev)
+{
+ int ct;
+ for (ct=0; ct<maxvif; ct++) {
+ if (vifc_map&(1<<ct) && vif_table[ct].dev == dev)
+ return ct;
+ }
+ return ALL_VIFS;
+}
+
+/* "local" means that we should preserve one skb (for local delivery) */
+
+int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local)
+{
+ int psend = -1;
+ int vif, ct;
+
+ vif = cache->mfc_parent;
+ cache->mfc_pkt++;
+ cache->mfc_bytes += skb->len;
+
+ /*
+ * Wrong interface: drop packet and (maybe) send PIM assert.
+ */
+ if (vif_table[vif].dev != skb->dev) {
+ int true_vifi;
+
+ if (((struct rtable*)skb->dst)->key.iif == 0) {
+ /* It is our own packet, looped back.
+ Very complicated situation...
+
+ The best workaround until routing daemons will be
+ fixed is not to redistribute packet, if it was
+ send through wrong interface. It means, that
+ multicast applications WILL NOT work for
+ (S,G), which have default multicast route pointing
+ to wrong oif. In any case, it is not a good
+ idea to use multicasting applications on router.
+ */
+ goto dont_forward;
+ }
+
+ cache->mfc_wrong_if++;
+ true_vifi = ipmr_find_vif(skb->dev);
+
+ if (true_vifi < MAXVIFS && mroute_do_assert &&
+ /* pimsm uses asserts, when switching from RPT to SPT,
+ so that we cannot check that packet arrived on an oif.
+ It is bad, but otherwise we would need to move pretty
+ large chunk of pimd to kernel. Ough... --ANK
+ */
+ (mroute_do_pim || cache->mfc_ttls[true_vifi] < 255) &&
+ jiffies - cache->mfc_last_assert > MFC_ASSERT_THRESH) {
+ cache->mfc_last_assert = jiffies;
+ ipmr_cache_report(skb, true_vifi, IGMPMSG_WRONGVIF);
+ }
+ goto dont_forward;
+ }
+
+ vif_table[vif].pkt_in++;
+ vif_table[vif].bytes_in+=skb->len;
+
+ /*
+ * Forward the frame
+ */
+ for (ct = cache->mfc_maxvif-1; ct >= cache->mfc_minvif; ct--) {
+ if (skb->nh.iph->ttl > cache->mfc_ttls[ct]) {
+ if (psend != -1)
+ ipmr_queue_xmit(skb, cache, psend, 0);
+ psend=ct;
+ }
+ }
+ if (psend != -1)
+ ipmr_queue_xmit(skb, cache, psend, !local);
+
+dont_forward:
+ if (!local)
+ kfree_skb(skb);
+ return 0;
+}
+
+
+/*
+ * Multicast packets for forwarding arrive here
+ */
+
+int ip_mr_input(struct sk_buff *skb)
+{
+ struct mfc_cache *cache;
+ int local = ((struct rtable*)skb->dst)->rt_flags&RTCF_LOCAL;
+
+ /* Packet is looped back after forward, it should not be
+ forwarded second time, but still can be delivered locally.
+ */
+ if (IPCB(skb)->flags&IPSKB_FORWARDED)
+ goto dont_forward;
+
+ if (!local) {
+ if (IPCB(skb)->opt.router_alert) {
+ if (ip_call_ra_chain(skb))
+ return 0;
+ } else if (skb->nh.iph->protocol == IPPROTO_IGMP && mroute_socket) {
+ /* IGMPv1 (and broken IGMPv2 implementations sort of
+ Cisco IOS <= 11.2(8)) do not put router alert
+ option to IGMP packets destined to routable
+ groups. It is very bad, because it means
+ that we can forward NO IGMP messages.
+ */
+ raw_rcv(mroute_socket, skb);
+ return 0;
+ }
+ }
+
+ cache = ipmr_cache_find(skb->nh.iph->saddr, skb->nh.iph->daddr);
+
+ /*
+ * No usable cache entry
+ */
+
+ if (cache==NULL || (cache->mfc_flags&MFC_QUEUED)) {
+ int vif;
+
+ if (local) {
+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+ ip_local_deliver(skb);
+ if (skb2 == NULL)
+ return -ENOBUFS;
+ skb = skb2;
+ }
+
+ vif = ipmr_find_vif(skb->dev);
+ if (vif != ALL_VIFS) {
+ ipmr_cache_unresolved(cache, vif, skb);
+ return -EAGAIN;
+ }
+ kfree_skb(skb);
+ return 0;
+ }
+
+ ip_mr_forward(skb, cache, local);
+
+ if (local)
+ return ip_local_deliver(skb);
+ return 0;
+
+dont_forward:
+ if (local)
+ return ip_local_deliver(skb);
+ kfree_skb(skb);
+ return 0;
+}
+
+#ifdef CONFIG_IP_PIMSM_V1
+/*
+ * Handle IGMP messages of PIMv1
+ */
+
+int pim_rcv_v1(struct sk_buff * skb, unsigned short len)
+{
+ struct igmphdr *pim = (struct igmphdr*)skb->h.raw;
+ struct iphdr *encap;
+
+ if (!mroute_do_pim ||
+ len < sizeof(*pim) + sizeof(*encap) ||
+ pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER ||
+ reg_dev == NULL) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ encap = (struct iphdr*)(skb->h.raw + sizeof(struct igmphdr));
+ /*
+ Check that:
+ a. packet is really destinted to a multicast group
+ b. packet is not a NULL-REGISTER
+ c. packet is not truncated
+ */
+ if (!MULTICAST(encap->daddr) ||
+ ntohs(encap->tot_len) == 0 ||
+ ntohs(encap->tot_len) + sizeof(*pim) > len) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+ skb->mac.raw = skb->nh.raw;
+ skb_pull(skb, (u8*)encap - skb->data);
+ skb->nh.iph = (struct iphdr *)skb->data;
+ skb->dev = reg_dev;
+ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
+ skb->protocol = __constant_htons(ETH_P_IP);
+ skb->ip_summed = 0;
+ skb->pkt_type = PACKET_HOST;
+ dst_release(skb->dst);
+ skb->dst = NULL;
+ ((struct net_device_stats*)reg_dev->priv)->rx_bytes += skb->len;
+ ((struct net_device_stats*)reg_dev->priv)->rx_packets++;
+ netif_rx(skb);
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_IP_PIMSM_V2
+int pim_rcv(struct sk_buff * skb, unsigned short len)
+{
+ struct pimreghdr *pim = (struct pimreghdr*)skb->h.raw;
+ struct iphdr *encap;
+
+ if (len < sizeof(*pim) + sizeof(*encap) ||
+ pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) ||
+ (pim->flags&PIM_NULL_REGISTER) ||
+ reg_dev == NULL ||
+ (ip_compute_csum((void *)pim, sizeof(*pim)) &&
+ ip_compute_csum((void *)pim, len))) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ /* check if the inner packet is destined to mcast group */
+ encap = (struct iphdr*)(skb->h.raw + sizeof(struct pimreghdr));
+ if (!MULTICAST(encap->daddr) ||
+ ntohs(encap->tot_len) == 0 ||
+ ntohs(encap->tot_len) + sizeof(*pim) > len) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+ skb->mac.raw = skb->nh.raw;
+ skb_pull(skb, (u8*)encap - skb->data);
+ skb->nh.iph = (struct iphdr *)skb->data;
+ skb->dev = reg_dev;
+ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
+ skb->protocol = __constant_htons(ETH_P_IP);
+ skb->ip_summed = 0;
+ skb->pkt_type = PACKET_HOST;
+ dst_release(skb->dst);
+ ((struct net_device_stats*)reg_dev->priv)->rx_bytes += skb->len;
+ ((struct net_device_stats*)reg_dev->priv)->rx_packets++;
+ skb->dst = NULL;
+ netif_rx(skb);
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_RTNETLINK
+
+static int
+ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm)
+{
+ int ct;
+ struct rtnexthop *nhp;
+ struct device *dev = vif_table[c->mfc_parent].dev;
+ u8 *b = skb->tail;
+ struct rtattr *mp_head;
+
+ if (dev)
+ RTA_PUT(skb, RTA_IIF, 4, &dev->ifindex);
+
+ mp_head = (struct rtattr*)skb_put(skb, RTA_LENGTH(0));
+
+ for (ct = c->mfc_minvif; ct < c->mfc_maxvif; ct++) {
+ if (c->mfc_ttls[ct] < 255) {
+ if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
+ goto rtattr_failure;
+ nhp = (struct rtnexthop*)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
+ nhp->rtnh_flags = 0;
+ nhp->rtnh_hops = c->mfc_ttls[ct];
+ nhp->rtnh_ifindex = vif_table[ct].dev->ifindex;
+ nhp->rtnh_len = sizeof(*nhp);
+ }
+ }
+ mp_head->rta_type = RTA_MULTIPATH;
+ mp_head->rta_len = skb->tail - (u8*)mp_head;
+ rtm->rtm_type = RTN_MULTICAST;
+ return 1;
+
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -EMSGSIZE;
+}
+
+int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait)
+{
+ struct mfc_cache *cache;
+ struct rtable *rt = (struct rtable*)skb->dst;
+
+ start_bh_atomic();
+ cache = ipmr_cache_find(rt->rt_src, rt->rt_dst);
+ if (cache==NULL || (cache->mfc_flags&MFC_QUEUED)) {
+ struct device *dev;
+ int vif;
+ int err;
+
+ if (nowait) {
+ end_bh_atomic();
+ return -EAGAIN;
+ }
+
+ dev = skb->dev;
+ if (dev == NULL || (vif = ipmr_find_vif(dev)) == ALL_VIFS) {
+ end_bh_atomic();
+ return -ENODEV;
+ }
+ skb->nh.raw = skb_push(skb, sizeof(struct iphdr));
+ skb->nh.iph->ihl = sizeof(struct iphdr)>>2;
+ skb->nh.iph->saddr = rt->rt_src;
+ skb->nh.iph->daddr = rt->rt_dst;
+ skb->nh.iph->version = 0;
+ err = ipmr_cache_unresolved(cache, vif, skb);
+ end_bh_atomic();
+ return err;
+ }
+ /* Resolved cache entry is not changed by net bh,
+ so that we are allowed to enable it.
+ */
+ end_bh_atomic();
+
+ if (!nowait && (rtm->rtm_flags&RTM_F_NOTIFY))
+ cache->mfc_flags |= MFC_NOTIFY;
+ return ipmr_fill_mroute(skb, cache, rtm);
+}
+#endif
+
+/*
+ * The /proc interfaces to multicast routing /proc/ip_mr_cache /proc/ip_mr_vif
+ */
+
+int ipmr_vif_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ struct vif_device *vif;
+ int len=0;
+ off_t pos=0;
+ off_t begin=0;
+ int size;
+ int ct;
+
+ len += sprintf(buffer,
+ "Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote\n");
+ pos=len;
+
+ for (ct=0;ct<maxvif;ct++)
+ {
+ char *name = "none";
+ vif=&vif_table[ct];
+ if(!(vifc_map&(1<<ct)))
+ continue;
+ if (vif->dev)
+ name = vif->dev->name;
+ size = sprintf(buffer+len, "%2d %-10s %8ld %7ld %8ld %7ld %05X %08X %08X\n",
+ ct, name, vif->bytes_in, vif->pkt_in, vif->bytes_out, vif->pkt_out,
+ vif->flags, vif->local, vif->remote);
+ len+=size;
+ pos+=size;
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ break;
+ }
+
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ return len;
+}
+
+int ipmr_mfc_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ struct mfc_cache *mfc;
+ int len=0;
+ off_t pos=0;
+ off_t begin=0;
+ int size;
+ int ct;
+
+ len += sprintf(buffer,
+ "Group Origin Iif Pkts Bytes Wrong Oifs\n");
+ pos=len;
+
+ for (ct=0;ct<MFC_LINES;ct++)
+ {
+ start_bh_atomic();
+ mfc=mfc_cache_array[ct];
+ while(mfc!=NULL)
+ {
+ int n;
+
+ /*
+ * Interface forwarding map
+ */
+ size = sprintf(buffer+len, "%08lX %08lX %-3d %8ld %8ld %8ld",
+ (unsigned long)mfc->mfc_mcastgrp,
+ (unsigned long)mfc->mfc_origin,
+ mfc->mfc_parent == ALL_VIFS ? -1 : mfc->mfc_parent,
+ (mfc->mfc_flags & MFC_QUEUED) ? mfc->mfc_unresolved.qlen : mfc->mfc_pkt,
+ mfc->mfc_bytes,
+ mfc->mfc_wrong_if);
+ for(n=mfc->mfc_minvif;n<mfc->mfc_maxvif;n++)
+ {
+ if(vifc_map&(1<<n) && mfc->mfc_ttls[n] < 255)
+ size += sprintf(buffer+len+size, " %2d:%-3d", n, mfc->mfc_ttls[n]);
+ }
+ size += sprintf(buffer+len+size, "\n");
+ len+=size;
+ pos+=size;
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ {
+ end_bh_atomic();
+ goto done;
+ }
+ mfc=mfc->next;
+ }
+ end_bh_atomic();
+ }
+done:
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ if (len < 0) {
+ len = 0;
+ }
+ return len;
+}
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_ipmr_vif = {
+ PROC_NET_IPMR_VIF, 9 ,"ip_mr_vif",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ ipmr_vif_info
+};
+static struct proc_dir_entry proc_net_ipmr_mfc = {
+ PROC_NET_IPMR_MFC, 11 ,"ip_mr_cache",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ ipmr_mfc_info
+};
+#endif
+
+#ifdef CONFIG_IP_PIMSM_V2
+struct inet_protocol pim_protocol =
+{
+ pim_rcv, /* PIM handler */
+ NULL, /* PIM error control */
+ NULL, /* next */
+ IPPROTO_PIM, /* protocol ID */
+ 0, /* copy */
+ NULL, /* data */
+ "PIM" /* name */
+};
+#endif
+
+
+/*
+ * Setup for IP multicast routing
+ */
+
+__initfunc(void ip_mr_init(void))
+{
+ printk(KERN_INFO "Linux IP multicast router 0.06 plus PIM-SM\n");
+ register_netdevice_notifier(&ip_mr_notifier);
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_net_ipmr_vif);
+ proc_net_register(&proc_net_ipmr_mfc);
+#endif
+}
diff --git a/pfinet/linux-src/net/ipv4/proc.c b/pfinet/linux-src/net/ipv4/proc.c
new file mode 100644
index 00000000..1640a056
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/proc.c
@@ -0,0 +1,387 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * This file implements the various access functions for the
+ * PROC file system. It is mainly used for debugging and
+ * statistics.
+ *
+ * Version: $Id: proc.c,v 1.34 1999/02/08 11:20:34 davem Exp $
+ *
+ * Authors: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de>
+ * Fred Baumgarten, <dc6iq@insu1.etec.uni-karlsruhe.de>
+ * Erik Schoenfelder, <schoenfr@ibr.cs.tu-bs.de>
+ *
+ * Fixes:
+ * Alan Cox : UDP sockets show the rxqueue/txqueue
+ * using hint flag for the netinfo.
+ * Pauline Middelink : identd support
+ * Alan Cox : Make /proc safer.
+ * Erik Schoenfelder : /proc/net/snmp
+ * Alan Cox : Handle dead sockets properly.
+ * Gerhard Koerting : Show both timers
+ * Alan Cox : Allow inode to be NULL (kernel socket)
+ * Andi Kleen : Add support for open_requests and
+ * split functions for more readibility.
+ * Andi Kleen : Add support for /proc/net/netstat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <asm/system.h>
+#include <linux/sched.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/un.h>
+#include <linux/in.h>
+#include <linux/param.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/raw.h>
+
+/* Format a single open_request into tmpbuf. */
+static inline void get__openreq(struct sock *sk, struct open_request *req,
+ char *tmpbuf,
+ int i)
+{
+ sprintf(tmpbuf, "%4d: %08lX:%04X %08lX:%04X"
+ " %02X %08X:%08X %02X:%08lX %08X %5d %8d %u",
+ i,
+ (long unsigned int)req->af.v4_req.loc_addr,
+ ntohs(sk->sport),
+ (long unsigned int)req->af.v4_req.rmt_addr,
+ ntohs(req->rmt_port),
+ TCP_SYN_RECV,
+ 0,0, /* could print option size, but that is af dependent. */
+ 1, /* timers active (only the expire timer) */
+ (unsigned long)(req->expires - jiffies),
+ req->retrans,
+ sk->socket ? sk->socket->inode->i_uid : 0,
+ 0, /* non standard timer */
+ 0 /* open_requests have no inode */
+ );
+}
+
+/* Format a single socket into tmpbuf. */
+static inline void get__sock(struct sock *sp, char *tmpbuf, int i, int format)
+{
+ unsigned long dest, src;
+ unsigned short destp, srcp;
+ int timer_active, timer_active1, timer_active2;
+ int tw_bucket = 0;
+ unsigned long timer_expires;
+ struct tcp_opt *tp = &sp->tp_pinfo.af_tcp;
+
+ dest = sp->daddr;
+ src = sp->rcv_saddr;
+ destp = sp->dport;
+ srcp = sp->sport;
+
+ /* FIXME: The fact that retransmit_timer occurs as a field
+ * in two different parts of the socket structure is,
+ * to say the least, confusing. This code now uses the
+ * right retransmit_timer variable, but I'm not sure
+ * the rest of the timer stuff is still correct.
+ * In particular I'm not sure what the timeout value
+ * is suppose to reflect (as opposed to tm->when). -- erics
+ */
+
+ destp = ntohs(destp);
+ srcp = ntohs(srcp);
+ if((format == 0) && (sp->state == TCP_TIME_WAIT)) {
+ extern int tcp_tw_death_row_slot;
+ struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sp;
+ int slot_dist;
+
+ tw_bucket = 1;
+ timer_active1 = timer_active2 = 0;
+ timer_active = 3;
+ slot_dist = tw->death_slot;
+ if(slot_dist > tcp_tw_death_row_slot)
+ slot_dist = (TCP_TWKILL_SLOTS - slot_dist) + tcp_tw_death_row_slot;
+ else
+ slot_dist = tcp_tw_death_row_slot - slot_dist;
+ timer_expires = jiffies + (slot_dist * TCP_TWKILL_PERIOD);
+ } else {
+ timer_active1 = del_timer(&tp->retransmit_timer);
+ timer_active2 = del_timer(&sp->timer);
+ if (!timer_active1) tp->retransmit_timer.expires=0;
+ if (!timer_active2) sp->timer.expires=0;
+ timer_active = 0;
+ timer_expires = (unsigned) -1;
+ }
+ if (timer_active1 && tp->retransmit_timer.expires < timer_expires) {
+ timer_active = 1;
+ timer_expires = tp->retransmit_timer.expires;
+ }
+ if (timer_active2 && sp->timer.expires < timer_expires) {
+ timer_active = 2;
+ timer_expires = sp->timer.expires;
+ }
+ if(timer_active == 0)
+ timer_expires = jiffies;
+ sprintf(tmpbuf, "%4d: %08lX:%04X %08lX:%04X"
+ " %02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
+ i, src, srcp, dest, destp, sp->state,
+ (tw_bucket ?
+ 0 :
+ (format == 0) ?
+ tp->write_seq-tp->snd_una : atomic_read(&sp->wmem_alloc)),
+ (tw_bucket ?
+ 0 :
+ (format == 0) ?
+ tp->rcv_nxt-tp->copied_seq: atomic_read(&sp->rmem_alloc)),
+ timer_active, timer_expires-jiffies,
+ (tw_bucket ? 0 : tp->retransmits),
+ (!tw_bucket && sp->socket) ? sp->socket->inode->i_uid : 0,
+ (!tw_bucket && timer_active) ? sp->timeout : 0,
+ (!tw_bucket && sp->socket) ? sp->socket->inode->i_ino : 0);
+
+ if (timer_active1) add_timer(&tp->retransmit_timer);
+ if (timer_active2) add_timer(&sp->timer);
+}
+
+/*
+ * Get__netinfo returns the length of that string.
+ *
+ * KNOWN BUGS
+ * As in get_unix_netinfo, the buffer might be too small. If this
+ * happens, get__netinfo returns only part of the available infos.
+ *
+ * Assumes that buffer length is a multiply of 128 - if not it will
+ * write past the end.
+ */
+static int
+get__netinfo(struct proto *pro, char *buffer, int format, char **start, off_t offset, int length)
+{
+ struct sock *sp, *next;
+ int len=0, i = 0;
+ off_t pos=0;
+ off_t begin;
+ char tmpbuf[129];
+
+ if (offset < 128)
+ len += sprintf(buffer, "%-127s\n",
+ " sl local_address rem_address st tx_queue "
+ "rx_queue tr tm->when retrnsmt uid timeout inode");
+ pos = 128;
+ SOCKHASH_LOCK();
+ sp = pro->sklist_next;
+ while(sp != (struct sock *)pro) {
+ if (format == 0 && sp->state == TCP_LISTEN) {
+ struct open_request *req;
+
+ for (req = sp->tp_pinfo.af_tcp.syn_wait_queue; req;
+ i++, req = req->dl_next) {
+ if (req->sk)
+ continue;
+ pos += 128;
+ if (pos < offset)
+ continue;
+ get__openreq(sp, req, tmpbuf, i);
+ len += sprintf(buffer+len, "%-127s\n", tmpbuf);
+ if(len >= length)
+ goto out;
+ }
+ }
+
+ pos += 128;
+ if (pos < offset)
+ goto next;
+
+ get__sock(sp, tmpbuf, i, format);
+
+ len += sprintf(buffer+len, "%-127s\n", tmpbuf);
+ if(len >= length)
+ break;
+ next:
+ next = sp->sklist_next;
+ sp = next;
+ i++;
+ }
+out:
+ SOCKHASH_UNLOCK();
+
+ begin = len - (pos - offset);
+ *start = buffer + begin;
+ len -= begin;
+ if(len>length)
+ len = length;
+ if (len<0)
+ len = 0;
+ return len;
+}
+
+int tcp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ return get__netinfo(&tcp_prot, buffer,0, start, offset, length);
+}
+
+int udp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ return get__netinfo(&udp_prot, buffer,1, start, offset, length);
+}
+
+int raw_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ return get__netinfo(&raw_prot, buffer,1, start, offset, length);
+}
+
+/*
+ * Report socket allocation statistics [mea@utu.fi]
+ */
+int afinet_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ /* From net/socket.c */
+ extern int socket_get_info(char *, char **, off_t, int);
+
+ int len = socket_get_info(buffer,start,offset,length);
+
+ len += sprintf(buffer+len,"TCP: inuse %d highest %d\n",
+ tcp_prot.inuse, tcp_prot.highestinuse);
+ len += sprintf(buffer+len,"UDP: inuse %d highest %d\n",
+ udp_prot.inuse, udp_prot.highestinuse);
+ len += sprintf(buffer+len,"RAW: inuse %d highest %d\n",
+ raw_prot.inuse, raw_prot.highestinuse);
+ if (offset >= len)
+ {
+ *start = buffer;
+ return 0;
+ }
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ if (len < 0)
+ len = 0;
+ return len;
+}
+
+
+/*
+ * Called from the PROCfs module. This outputs /proc/net/snmp.
+ */
+
+int snmp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ extern struct tcp_mib tcp_statistics;
+ extern struct udp_mib udp_statistics;
+ int len;
+/*
+ extern unsigned long tcp_rx_miss, tcp_rx_hit1,tcp_rx_hit2;
+*/
+
+ len = sprintf (buffer,
+ "Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates\n"
+ "Ip: %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
+ ip_statistics.IpForwarding, ip_statistics.IpDefaultTTL,
+ ip_statistics.IpInReceives, ip_statistics.IpInHdrErrors,
+ ip_statistics.IpInAddrErrors, ip_statistics.IpForwDatagrams,
+ ip_statistics.IpInUnknownProtos, ip_statistics.IpInDiscards,
+ ip_statistics.IpInDelivers, ip_statistics.IpOutRequests,
+ ip_statistics.IpOutDiscards, ip_statistics.IpOutNoRoutes,
+ ip_statistics.IpReasmTimeout, ip_statistics.IpReasmReqds,
+ ip_statistics.IpReasmOKs, ip_statistics.IpReasmFails,
+ ip_statistics.IpFragOKs, ip_statistics.IpFragFails,
+ ip_statistics.IpFragCreates);
+
+ len += sprintf (buffer + len,
+ "Icmp: InMsgs InErrors InDestUnreachs InTimeExcds InParmProbs InSrcQuenchs InRedirects InEchos InEchoReps InTimestamps InTimestampReps InAddrMasks InAddrMaskReps OutMsgs OutErrors OutDestUnreachs OutTimeExcds OutParmProbs OutSrcQuenchs OutRedirects OutEchos OutEchoReps OutTimestamps OutTimestampReps OutAddrMasks OutAddrMaskReps\n"
+ "Icmp: %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
+ icmp_statistics.IcmpInMsgs, icmp_statistics.IcmpInErrors,
+ icmp_statistics.IcmpInDestUnreachs, icmp_statistics.IcmpInTimeExcds,
+ icmp_statistics.IcmpInParmProbs, icmp_statistics.IcmpInSrcQuenchs,
+ icmp_statistics.IcmpInRedirects, icmp_statistics.IcmpInEchos,
+ icmp_statistics.IcmpInEchoReps, icmp_statistics.IcmpInTimestamps,
+ icmp_statistics.IcmpInTimestampReps, icmp_statistics.IcmpInAddrMasks,
+ icmp_statistics.IcmpInAddrMaskReps, icmp_statistics.IcmpOutMsgs,
+ icmp_statistics.IcmpOutErrors, icmp_statistics.IcmpOutDestUnreachs,
+ icmp_statistics.IcmpOutTimeExcds, icmp_statistics.IcmpOutParmProbs,
+ icmp_statistics.IcmpOutSrcQuenchs, icmp_statistics.IcmpOutRedirects,
+ icmp_statistics.IcmpOutEchos, icmp_statistics.IcmpOutEchoReps,
+ icmp_statistics.IcmpOutTimestamps, icmp_statistics.IcmpOutTimestampReps,
+ icmp_statistics.IcmpOutAddrMasks, icmp_statistics.IcmpOutAddrMaskReps);
+
+ len += sprintf (buffer + len,
+ "Tcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResets CurrEstab InSegs OutSegs RetransSegs InErrs OutRsts\n"
+ "Tcp: %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
+ tcp_statistics.TcpRtoAlgorithm, tcp_statistics.TcpRtoMin,
+ tcp_statistics.TcpRtoMax, tcp_statistics.TcpMaxConn,
+ tcp_statistics.TcpActiveOpens, tcp_statistics.TcpPassiveOpens,
+ tcp_statistics.TcpAttemptFails, tcp_statistics.TcpEstabResets,
+ tcp_statistics.TcpCurrEstab, tcp_statistics.TcpInSegs,
+ tcp_statistics.TcpOutSegs, tcp_statistics.TcpRetransSegs,
+ tcp_statistics.TcpInErrs, tcp_statistics.TcpOutRsts);
+
+ len += sprintf (buffer + len,
+ "Udp: InDatagrams NoPorts InErrors OutDatagrams\nUdp: %lu %lu %lu %lu\n",
+ udp_statistics.UdpInDatagrams, udp_statistics.UdpNoPorts,
+ udp_statistics.UdpInErrors, udp_statistics.UdpOutDatagrams);
+/*
+ len += sprintf( buffer + len,
+ "TCP fast path RX: H2: %ul H1: %ul L: %ul\n",
+ tcp_rx_hit2,tcp_rx_hit1,tcp_rx_miss);
+*/
+
+ if (offset >= len)
+ {
+ *start = buffer;
+ return 0;
+ }
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ if (len < 0)
+ len = 0;
+ return len;
+}
+
+/*
+ * Output /proc/net/netstat
+ */
+
+int netstat_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ extern struct linux_mib net_statistics;
+ int len;
+
+ len = sprintf(buffer,
+ "TcpExt: SyncookiesSent SyncookiesRecv SyncookiesFailed"
+ " EmbryonicRsts PruneCalled RcvPruned OfoPruned"
+ " OutOfWindowIcmps LockDroppedIcmps\n"
+ "TcpExt: %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
+ net_statistics.SyncookiesSent,
+ net_statistics.SyncookiesRecv,
+ net_statistics.SyncookiesFailed,
+ net_statistics.EmbryonicRsts,
+ net_statistics.PruneCalled,
+ net_statistics.RcvPruned,
+ net_statistics.OfoPruned,
+ net_statistics.OutOfWindowIcmps,
+ net_statistics.LockDroppedIcmps);
+
+ if (offset >= len)
+ {
+ *start = buffer;
+ return 0;
+ }
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ if (len < 0)
+ len = 0;
+ return len;
+}
diff --git a/pfinet/linux-src/net/ipv4/protocol.c b/pfinet/linux-src/net/ipv4/protocol.c
new file mode 100644
index 00000000..b47480be
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/protocol.c
@@ -0,0 +1,211 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * INET protocol dispatch tables.
+ *
+ * Version: $Id: protocol.c,v 1.9 1997/10/29 20:27:34 kuznet Exp $
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * Fixes:
+ * Alan Cox : Ahah! udp icmp errors don't work because
+ * udp_err is never called!
+ * Alan Cox : Added new fields for init and ready for
+ * proper fragmentation (_NO_ 4K limits!)
+ * Richard Colella : Hang on hash collision
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/timer.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/icmp.h>
+#include <net/udp.h>
+#include <net/ipip.h>
+#include <linux/igmp.h>
+
+#define IPPROTO_PREVIOUS NULL
+
+#ifdef CONFIG_IP_MULTICAST
+
+static struct inet_protocol igmp_protocol =
+{
+ igmp_rcv, /* IGMP handler */
+ NULL, /* IGMP error control */
+ IPPROTO_PREVIOUS, /* next */
+ IPPROTO_IGMP, /* protocol ID */
+ 0, /* copy */
+ NULL, /* data */
+ "IGMP" /* name */
+};
+
+#undef IPPROTO_PREVIOUS
+#define IPPROTO_PREVIOUS &igmp_protocol
+
+#endif
+
+static struct inet_protocol tcp_protocol =
+{
+ tcp_v4_rcv, /* TCP handler */
+ tcp_v4_err, /* TCP error control */
+ IPPROTO_PREVIOUS,
+ IPPROTO_TCP, /* protocol ID */
+ 0, /* copy */
+ NULL, /* data */
+ "TCP" /* name */
+};
+
+#undef IPPROTO_PREVIOUS
+#define IPPROTO_PREVIOUS &tcp_protocol
+
+static struct inet_protocol udp_protocol =
+{
+ udp_rcv, /* UDP handler */
+ udp_err, /* UDP error control */
+ IPPROTO_PREVIOUS, /* next */
+ IPPROTO_UDP, /* protocol ID */
+ 0, /* copy */
+ NULL, /* data */
+ "UDP" /* name */
+};
+
+#undef IPPROTO_PREVIOUS
+#define IPPROTO_PREVIOUS &udp_protocol
+
+
+static struct inet_protocol icmp_protocol =
+{
+ icmp_rcv, /* ICMP handler */
+ NULL, /* ICMP error control */
+ IPPROTO_PREVIOUS, /* next */
+ IPPROTO_ICMP, /* protocol ID */
+ 0, /* copy */
+ NULL, /* data */
+ "ICMP" /* name */
+};
+
+#undef IPPROTO_PREVIOUS
+#define IPPROTO_PREVIOUS &icmp_protocol
+
+
+struct inet_protocol *inet_protocol_base = IPPROTO_PREVIOUS;
+
+struct inet_protocol *inet_protos[MAX_INET_PROTOS] =
+{
+ NULL
+};
+
+
+/*
+ * Find a protocol in the protocol tables given its
+ * IP type.
+ */
+
+struct inet_protocol *inet_get_protocol(unsigned char prot)
+{
+ unsigned char hash;
+ struct inet_protocol *p;
+
+ hash = prot & (MAX_INET_PROTOS - 1);
+ for (p = inet_protos[hash] ; p != NULL; p=p->next)
+ {
+ if (p->protocol == prot)
+ return((struct inet_protocol *) p);
+ }
+ return(NULL);
+}
+
+/*
+ * Add a protocol handler to the hash tables
+ */
+
+void inet_add_protocol(struct inet_protocol *prot)
+{
+ unsigned char hash;
+ struct inet_protocol *p2;
+
+ hash = prot->protocol & (MAX_INET_PROTOS - 1);
+ prot ->next = inet_protos[hash];
+ inet_protos[hash] = prot;
+ prot->copy = 0;
+
+ /*
+ * Set the copy bit if we need to.
+ */
+
+ p2 = (struct inet_protocol *) prot->next;
+ while(p2 != NULL)
+ {
+ if (p2->protocol == prot->protocol)
+ {
+ prot->copy = 1;
+ break;
+ }
+ p2 = (struct inet_protocol *) p2->next;
+ }
+}
+
+/*
+ * Remove a protocol from the hash tables.
+ */
+
+int inet_del_protocol(struct inet_protocol *prot)
+{
+ struct inet_protocol *p;
+ struct inet_protocol *lp = NULL;
+ unsigned char hash;
+
+ hash = prot->protocol & (MAX_INET_PROTOS - 1);
+ if (prot == inet_protos[hash])
+ {
+ inet_protos[hash] = (struct inet_protocol *) inet_protos[hash]->next;
+ return(0);
+ }
+
+ p = (struct inet_protocol *) inet_protos[hash];
+ while(p != NULL)
+ {
+ /*
+ * We have to worry if the protocol being deleted is
+ * the last one on the list, then we may need to reset
+ * someone's copied bit.
+ */
+ if (p->next != NULL && p->next == prot)
+ {
+ /*
+ * if we are the last one with this protocol and
+ * there is a previous one, reset its copy bit.
+ */
+ if (p->copy == 0 && lp != NULL)
+ lp->copy = 0;
+ p->next = prot->next;
+ return(0);
+ }
+ if (p->next != NULL && p->next->protocol == prot->protocol)
+ lp = p;
+
+ p = (struct inet_protocol *) p->next;
+ }
+ return(-1);
+}
diff --git a/pfinet/linux-src/net/ipv4/rarp.c b/pfinet/linux-src/net/ipv4/rarp.c
new file mode 100644
index 00000000..7f7c7e3f
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/rarp.c
@@ -0,0 +1,606 @@
+/* linux/net/inet/rarp.c
+ *
+ * Copyright (C) 1994 by Ross Martin
+ * Based on linux/net/inet/arp.c, Copyright (C) 1994 by Florian La Roche
+ *
+ * $Id: rarp.c,v 1.25 1998/06/19 13:22:34 davem Exp $
+ *
+ * This module implements the Reverse Address Resolution Protocol
+ * (RARP, RFC 903), which is used to convert low level addresses such
+ * as Ethernet addresses into high level addresses such as IP addresses.
+ * The most common use of RARP is as a means for a diskless workstation
+ * to discover its IP address during a network boot.
+ *
+ **
+ *** WARNING:::::::::::::::::::::::::::::::::WARNING
+ ****
+ ***** SUN machines seem determined to boot solely from the person who
+ **** answered their RARP query. NEVER add a SUN to your RARP table
+ *** unless you have all the rest to boot the box from it.
+ **
+ *
+ * Currently, only Ethernet address -> IP address is likely to work.
+ * (Is RARP ever used for anything else?)
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Fixes
+ * Alan Cox : Rarp delete on device down needed as
+ * reported by Walter Wolfgang.
+ * Mike McLagan : Routing by source
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/in.h>
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <stdarg.h>
+#include <linux/inet.h>
+#include <linux/etherdevice.h>
+#include <net/ip.h>
+#include <net/route.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/arp.h>
+#include <net/rarp.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <net/ax25.h>
+#endif
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+
+extern int (*rarp_ioctl_hook)(unsigned int,void*);
+
+/*
+ * This structure defines the RARP mapping cache. As long as we make
+ * changes in this structure, we keep interrupts off.
+ */
+
+struct rarp_table
+{
+ struct rarp_table *next; /* Linked entry list */
+ unsigned long ip; /* ip address of entry */
+ unsigned char ha[MAX_ADDR_LEN]; /* Hardware address */
+ unsigned char hlen; /* Length of hardware address */
+ unsigned char htype; /* Type of hardware in use */
+ struct device *dev; /* Device the entry is tied to */
+};
+
+struct rarp_table *rarp_tables = NULL;
+
+static int rarp_rcv(struct sk_buff *, struct device *, struct packet_type *);
+
+static struct packet_type rarp_packet_type =
+{
+ 0, /* Should be: __constant_htons(ETH_P_RARP) - but this _doesn't_ come out constant! */
+ 0, /* copy */
+ rarp_rcv,
+ NULL,
+ NULL
+};
+
+static int initflag = 1;
+
+
+/*
+ * Release the memory for this entry.
+ */
+
+static inline void rarp_release_entry(struct rarp_table *entry)
+{
+ kfree_s(entry, sizeof(struct rarp_table));
+ MOD_DEC_USE_COUNT;
+ return;
+}
+
+/*
+ * Delete a RARP mapping entry in the cache.
+ */
+
+static void rarp_destroy(unsigned long ip_addr)
+{
+ struct rarp_table *entry;
+ struct rarp_table **pentry;
+
+ start_bh_atomic();
+ pentry = &rarp_tables;
+ while ((entry = *pentry) != NULL)
+ {
+ if (entry->ip == ip_addr)
+ {
+ *pentry = entry->next;
+ end_bh_atomic();
+ rarp_release_entry(entry);
+ return;
+ }
+ pentry = &entry->next;
+ }
+ end_bh_atomic();
+}
+
+/*
+ * Flush a device.
+ */
+
+static void rarp_destroy_dev(struct device *dev)
+{
+ struct rarp_table *entry;
+ struct rarp_table **pentry;
+
+ start_bh_atomic();
+ pentry = &rarp_tables;
+ while ((entry = *pentry) != NULL)
+ {
+ if (entry->dev == dev)
+ {
+ *pentry = entry->next;
+ rarp_release_entry(entry);
+ }
+ else
+ pentry = &entry->next;
+ }
+ end_bh_atomic();
+}
+
+static int rarp_device_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ if(event!=NETDEV_DOWN)
+ return NOTIFY_DONE;
+ rarp_destroy_dev((struct device *)ptr);
+ return NOTIFY_DONE;
+}
+
+/*
+ * Called once when data first added to rarp cache with ioctl.
+ */
+
+static struct notifier_block rarp_dev_notifier={
+ rarp_device_event,
+ NULL,
+ 0
+};
+
+static int rarp_pkt_inited=0;
+
+static void rarp_init_pkt (void)
+{
+ /* Register the packet type */
+ rarp_packet_type.type=htons(ETH_P_RARP);
+ dev_add_pack(&rarp_packet_type);
+ register_netdevice_notifier(&rarp_dev_notifier);
+ rarp_pkt_inited=1;
+}
+
+#ifdef MODULE
+
+static void rarp_end_pkt(void)
+{
+ if(!rarp_pkt_inited)
+ return;
+ dev_remove_pack(&rarp_packet_type);
+ unregister_netdevice_notifier(&rarp_dev_notifier);
+ rarp_pkt_inited=0;
+}
+
+#endif
+
+/*
+ * Receive an arp request by the device layer. Maybe it should be
+ * rewritten to use the incoming packet for the reply. The current
+ * "overhead" time isn't that high...
+ */
+
+static int rarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+/*
+ * We shouldn't use this type conversion. Check later.
+ */
+ struct arphdr *rarp = (struct arphdr *) skb->data;
+ unsigned char *rarp_ptr = skb_pull(skb,sizeof(struct arphdr));
+ struct rarp_table *entry;
+ struct in_device *in_dev = dev->ip_ptr;
+ long sip,tip;
+ unsigned char *sha,*tha; /* s for "source", t for "target" */
+
+/*
+ * If this test doesn't pass, it's not IP, or we should ignore it anyway
+ */
+
+ if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)
+ || dev->flags&IFF_NOARP || !in_dev || !in_dev->ifa_list)
+ {
+ kfree_skb(skb);
+ return 0;
+ }
+
+/*
+ * If it's not a RARP request, delete it.
+ */
+ if (rarp->ar_op != htons(ARPOP_RREQUEST))
+ {
+ kfree_skb(skb);
+ return 0;
+ }
+
+/*
+ * For now we will only deal with IP addresses.
+ */
+
+ if (
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
+#endif
+ (rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25)
+ || rarp->ar_pln != 4)
+ {
+ /*
+ * This packet is not for us. Remove it.
+ */
+ kfree_skb(skb);
+ return 0;
+ }
+
+/*
+ * Extract variable width fields
+ */
+
+ sha=rarp_ptr;
+ rarp_ptr+=dev->addr_len;
+ memcpy(&sip,rarp_ptr,4);
+ rarp_ptr+=4;
+ tha=rarp_ptr;
+ rarp_ptr+=dev->addr_len;
+ memcpy(&tip,rarp_ptr,4);
+
+/*
+ * Process entry. Use tha for table lookup according to RFC903.
+ */
+
+ for (entry = rarp_tables; entry != NULL; entry = entry->next)
+ if (!memcmp(entry->ha, tha, rarp->ar_hln))
+ break;
+
+ if (entry != NULL)
+ {
+ sip=entry->ip;
+
+ arp_send(ARPOP_RREPLY, ETH_P_RARP, sip, dev, in_dev->ifa_list->ifa_address, sha,
+ dev->dev_addr, sha);
+ }
+
+ kfree_skb(skb);
+ return 0;
+}
+
+
+/*
+ * Set (create) a RARP cache entry.
+ */
+
+static int rarp_req_set(struct arpreq *req)
+{
+ struct arpreq r;
+ struct rarp_table *entry;
+ struct sockaddr_in *si;
+ int htype, hlen;
+ unsigned long ip;
+ struct rtable *rt;
+ struct device * dev;
+ int err;
+
+ err = copy_from_user(&r, req, sizeof(r));
+ if (err)
+ return -EFAULT;
+
+ /*
+ * We only understand about IP addresses...
+ */
+
+ if (r.arp_pa.sa_family != AF_INET)
+ return -EPFNOSUPPORT;
+
+ switch (r.arp_ha.sa_family)
+ {
+ case ARPHRD_ETHER:
+ htype = ARPHRD_ETHER;
+ hlen = ETH_ALEN;
+ break;
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ case ARPHRD_AX25:
+ htype = ARPHRD_AX25;
+ hlen = 7;
+ break;
+#endif
+ default:
+ return -EPFNOSUPPORT;
+ }
+
+ si = (struct sockaddr_in *) &r.arp_pa;
+ ip = si->sin_addr.s_addr;
+ if (ip == 0)
+ {
+ printk(KERN_DEBUG "RARP: SETRARP: requested PA is 0.0.0.0 !\n");
+ return -EINVAL;
+ }
+
+/*
+ * Is it reachable directly ?
+ */
+
+ err = ip_route_output(&rt, ip, 0, 1, 0);
+ if (err)
+ return err;
+ if (rt->rt_flags&(RTCF_LOCAL|RTCF_BROADCAST|RTCF_MULTICAST|RTCF_DNAT)) {
+ ip_rt_put(rt);
+ return -EINVAL;
+ }
+ dev = rt->u.dst.dev;
+
+/*
+ * Is there an existing entry for this address? Find out...
+ */
+
+ for (entry = rarp_tables; entry != NULL; entry = entry->next)
+ if (entry->ip == ip)
+ break;
+
+/*
+ * If no entry was found, create a new one.
+ */
+
+ if (entry == NULL)
+ {
+ entry = (struct rarp_table *) kmalloc(sizeof(struct rarp_table),
+ GFP_ATOMIC);
+ if (entry == NULL)
+ {
+ return -ENOMEM;
+ }
+ if (initflag)
+ {
+ rarp_init_pkt();
+ initflag=0;
+ }
+
+ /* Block interrupts until table modification is finished */
+
+ cli();
+ entry->next = rarp_tables;
+ rarp_tables = entry;
+ }
+ cli();
+ entry->ip = ip;
+ entry->hlen = hlen;
+ entry->htype = htype;
+ memcpy(&entry->ha, &r.arp_ha.sa_data, hlen);
+ entry->dev = dev;
+ sti();
+
+ /* Don't unlink if we have entries to serve. */
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+
+/*
+ * Get a RARP cache entry.
+ */
+
+static int rarp_req_get(struct arpreq *req)
+{
+ struct arpreq r;
+ struct rarp_table *entry;
+ struct sockaddr_in *si;
+ unsigned long ip;
+ int err;
+
+/*
+ * We only understand about IP addresses...
+ */
+
+ err = copy_from_user(&r, req, sizeof(r));
+ if (err)
+ return -EFAULT;
+
+ if (r.arp_pa.sa_family != AF_INET)
+ return -EPFNOSUPPORT;
+
+/*
+ * Is there an existing entry for this address?
+ */
+
+ si = (struct sockaddr_in *) &r.arp_pa;
+ ip = si->sin_addr.s_addr;
+
+ for (entry = rarp_tables; entry != NULL; entry = entry->next)
+ if (entry->ip == ip)
+ break;
+
+ if (entry == NULL)
+ {
+ return -ENXIO;
+ }
+
+/*
+ * We found it; copy into structure.
+ */
+
+ memcpy(r.arp_ha.sa_data, &entry->ha, entry->hlen);
+ r.arp_ha.sa_family = entry->htype;
+
+/*
+ * Copy the information back
+ */
+
+ return copy_to_user(req, &r, sizeof(r)) ? -EFAULT : 0;
+}
+
+
+/*
+ * Handle a RARP layer I/O control request.
+ */
+
+int rarp_ioctl(unsigned int cmd, void *arg)
+{
+ struct arpreq r;
+ struct sockaddr_in *si;
+ int err;
+
+ switch(cmd)
+ {
+ case SIOCDRARP:
+ if (!suser())
+ return -EPERM;
+ err = copy_from_user(&r, arg, sizeof(r));
+ if (err)
+ return -EFAULT;
+ if (r.arp_pa.sa_family != AF_INET)
+ return -EPFNOSUPPORT;
+ si = (struct sockaddr_in *) &r.arp_pa;
+ rarp_destroy(si->sin_addr.s_addr);
+ return 0;
+
+ case SIOCGRARP:
+
+ return rarp_req_get((struct arpreq *)arg);
+ case SIOCSRARP:
+ if (!suser())
+ return -EPERM;
+ return rarp_req_set((struct arpreq *)arg);
+ default:
+ return -EINVAL;
+ }
+
+ /*NOTREACHED*/
+ return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+int rarp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len=0;
+ off_t begin=0;
+ off_t pos=0;
+ int size;
+ struct rarp_table *entry;
+ char ipbuffer[20];
+ unsigned long netip;
+ if (initflag)
+ {
+ size = sprintf(buffer,"RARP disabled until entries added to cache.\n");
+ pos+=size;
+ len+=size;
+ }
+ else
+ {
+ size = sprintf(buffer,
+ "IP address HW type HW address\n");
+ pos+=size;
+ len+=size;
+
+ for(entry=rarp_tables; entry!=NULL; entry=entry->next)
+ {
+ netip=htonl(entry->ip); /* switch to network order */
+ sprintf(ipbuffer,"%d.%d.%d.%d",
+ (unsigned int)(netip>>24)&255,
+ (unsigned int)(netip>>16)&255,
+ (unsigned int)(netip>>8)&255,
+ (unsigned int)(netip)&255);
+
+ size = sprintf(buffer+len,
+ "%-17s%-20s%02x:%02x:%02x:%02x:%02x:%02x\n",
+ ipbuffer,
+ "10Mbps Ethernet",
+ (unsigned int)entry->ha[0],
+ (unsigned int)entry->ha[1],
+ (unsigned int)entry->ha[2],
+ (unsigned int)entry->ha[3],
+ (unsigned int)entry->ha[4],
+ (unsigned int)entry->ha[5]);
+
+ len+=size;
+ pos=begin+len;
+
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ break;
+ }
+ }
+
+ *start = buffer+(offset-begin); /* Start of wanted data */
+ len -= (offset-begin); /* Start slop */
+ if (len>length)
+ len = length; /* Ending slop */
+ return len;
+}
+
+struct proc_dir_entry proc_net_rarp = {
+ PROC_NET_RARP, 4, "rarp",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rarp_get_info
+};
+#endif
+
+__initfunc(void
+rarp_init(void))
+{
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_net_rarp);
+#endif
+ rarp_ioctl_hook = rarp_ioctl;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+ rarp_init();
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ struct rarp_table *rt, *rt_next;
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(PROC_NET_RARP);
+#endif
+ rarp_ioctl_hook = NULL;
+ cli();
+ /* Destroy the RARP-table */
+ rt = rarp_tables;
+ rarp_tables = NULL;
+ sti();
+ /* ... and free it. */
+ for ( ; rt != NULL; rt = rt_next) {
+ rt_next = rt->next;
+ rarp_release_entry(rt);
+ }
+ rarp_end_pkt();
+}
+#endif
diff --git a/pfinet/linux-src/net/ipv4/raw.c b/pfinet/linux-src/net/ipv4/raw.c
new file mode 100644
index 00000000..a0aaa82e
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/raw.c
@@ -0,0 +1,573 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * RAW - implementation of IP "raw" sockets.
+ *
+ * Version: $Id: raw.c,v 1.39.2.1 1999/06/20 20:14:50 davem Exp $
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * Fixes:
+ * Alan Cox : verify_area() fixed up
+ * Alan Cox : ICMP error handling
+ * Alan Cox : EMSGSIZE if you send too big a packet
+ * Alan Cox : Now uses generic datagrams and shared skbuff
+ * library. No more peek crashes, no more backlogs
+ * Alan Cox : Checks sk->broadcast.
+ * Alan Cox : Uses skb_free_datagram/skb_copy_datagram
+ * Alan Cox : Raw passes ip options too
+ * Alan Cox : Setsocketopt added
+ * Alan Cox : Fixed error return for broadcasts
+ * Alan Cox : Removed wake_up calls
+ * Alan Cox : Use ttl/tos
+ * Alan Cox : Cleaned up old debugging
+ * Alan Cox : Use new kernel side addresses
+ * Arnt Gulbrandsen : Fixed MSG_DONTROUTE in raw sockets.
+ * Alan Cox : BSD style RAW socket demultiplexing.
+ * Alan Cox : Beginnings of mrouted support.
+ * Alan Cox : Added IP_HDRINCL option.
+ * Alan Cox : Skip broadcast check if BSDism set.
+ * David S. Miller : New socket lookup architecture.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/fcntl.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/mroute.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/icmp.h>
+#include <net/udp.h>
+#include <net/raw.h>
+#include <net/checksum.h>
+
+#ifdef CONFIG_IP_MROUTE
+struct sock *mroute_socket=NULL;
+#endif
+
+struct sock *raw_v4_htable[RAWV4_HTABLE_SIZE];
+
+static void raw_v4_hash(struct sock *sk)
+{
+ struct sock **skp = &raw_v4_htable[sk->num & (RAWV4_HTABLE_SIZE - 1)];
+
+ SOCKHASH_LOCK();
+ if ((sk->next = *skp) != NULL)
+ (*skp)->pprev = &sk->next;
+ *skp = sk;
+ sk->pprev = skp;
+ SOCKHASH_UNLOCK();
+}
+
+static void raw_v4_unhash(struct sock *sk)
+{
+ SOCKHASH_LOCK();
+ if (sk->pprev) {
+ if (sk->next)
+ sk->next->pprev = sk->pprev;
+ *sk->pprev = sk->next;
+ sk->pprev = NULL;
+ }
+ SOCKHASH_UNLOCK();
+}
+
+/* Grumble... icmp and ip_input want to get at this... */
+struct sock *raw_v4_lookup(struct sock *sk, unsigned short num,
+ unsigned long raddr, unsigned long laddr, int dif)
+{
+ struct sock *s = sk;
+
+ SOCKHASH_LOCK();
+ for(s = sk; s; s = s->next) {
+ if((s->num == num) &&
+ !(s->dead && (s->state == TCP_CLOSE)) &&
+ !(s->daddr && s->daddr != raddr) &&
+ !(s->rcv_saddr && s->rcv_saddr != laddr) &&
+ !(s->bound_dev_if && s->bound_dev_if != dif))
+ break; /* gotcha */
+ }
+ SOCKHASH_UNLOCK();
+ return s;
+}
+
+void raw_err (struct sock *sk, struct sk_buff *skb)
+{
+ int type = skb->h.icmph->type;
+ int code = skb->h.icmph->code;
+ u32 info = 0;
+ int err = 0;
+ int harderr = 0;
+
+ /* Report error on raw socket, if:
+ 1. User requested ip_recverr.
+ 2. Socket is connected (otherwise the error indication
+ is useless without ip_recverr and error is hard.
+ */
+ if (!sk->ip_recverr && sk->state != TCP_ESTABLISHED)
+ return;
+
+ switch (type) {
+ default:
+ case ICMP_TIME_EXCEEDED:
+ err = EHOSTUNREACH;
+ break;
+ case ICMP_SOURCE_QUENCH:
+ return;
+ case ICMP_PARAMETERPROB:
+ err = EPROTO;
+ info = ntohl(skb->h.icmph->un.gateway)>>24;
+ harderr = 1;
+ break;
+ case ICMP_DEST_UNREACH:
+ err = EHOSTUNREACH;
+ if (code > NR_ICMP_UNREACH)
+ break;
+ err = icmp_err_convert[code].errno;
+ harderr = icmp_err_convert[code].fatal;
+ if (code == ICMP_FRAG_NEEDED) {
+ harderr = (sk->ip_pmtudisc != IP_PMTUDISC_DONT);
+ err = EMSGSIZE;
+ info = ntohs(skb->h.icmph->un.frag.mtu);
+ }
+ }
+
+ if (sk->ip_recverr)
+ ip_icmp_error(sk, skb, err, 0, info, (u8 *)(skb->h.icmph + 1));
+
+ if (sk->ip_recverr || harderr) {
+ sk->err = err;
+ sk->error_report(sk);
+ }
+}
+
+static int raw_rcv_skb(struct sock * sk, struct sk_buff * skb)
+{
+ /* Charge it to the socket. */
+
+ if (sock_queue_rcv_skb(sk,skb)<0)
+ {
+ ip_statistics.IpInDiscards++;
+ kfree_skb(skb);
+ return -1;
+ }
+
+ ip_statistics.IpInDelivers++;
+ return 0;
+}
+
+/*
+ * This should be the easiest of all, all we do is
+ * copy it into a buffer. All demultiplexing is done
+ * in ip.c
+ */
+
+int raw_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ /* Now we need to copy this into memory. */
+ skb_trim(skb, ntohs(skb->nh.iph->tot_len));
+
+ skb->h.raw = skb->nh.raw;
+
+ raw_rcv_skb(sk, skb);
+ return 0;
+}
+
+struct rawfakehdr
+{
+ struct iovec *iov;
+ u32 saddr;
+};
+
+/*
+ * Send a RAW IP packet.
+ */
+
+/*
+ * Callback support is trivial for SOCK_RAW
+ */
+
+static int raw_getfrag(const void *p, char *to, unsigned int offset, unsigned int fraglen)
+{
+ struct rawfakehdr *rfh = (struct rawfakehdr *) p;
+ return memcpy_fromiovecend(to, rfh->iov, offset, fraglen);
+}
+
+/*
+ * IPPROTO_RAW needs extra work.
+ */
+
+static int raw_getrawfrag(const void *p, char *to, unsigned int offset, unsigned int fraglen)
+{
+ struct rawfakehdr *rfh = (struct rawfakehdr *) p;
+
+ if (memcpy_fromiovecend(to, rfh->iov, offset, fraglen))
+ return -EFAULT;
+
+ if (offset==0) {
+ struct iphdr *iph = (struct iphdr *)to;
+ if (!iph->saddr)
+ iph->saddr = rfh->saddr;
+ iph->check=0;
+ iph->tot_len=htons(fraglen); /* This is right as you can't frag
+ RAW packets */
+ /*
+ * Deliberate breach of modularity to keep
+ * ip_build_xmit clean (well less messy).
+ */
+ if (!iph->id)
+ iph->id = htons(ip_id_count++);
+ iph->check=ip_fast_csum((unsigned char *)iph, iph->ihl);
+ }
+ return 0;
+}
+
+static int raw_sendmsg(struct sock *sk, struct msghdr *msg, int len)
+{
+ struct ipcm_cookie ipc;
+ struct rawfakehdr rfh;
+ struct rtable *rt = NULL;
+ int free = 0;
+ u32 daddr;
+ u8 tos;
+ int err;
+
+ /* This check is ONLY to check for arithmetic overflow
+ on integer(!) len. Not more! Real check will be made
+ in ip_build_xmit --ANK
+
+ BTW socket.c -> af_*.c -> ... make multiple
+ invalid conversions size_t -> int. We MUST repair it f.e.
+ by replacing all of them with size_t and revise all
+ the places sort of len += sizeof(struct iphdr)
+ If len was ULONG_MAX-10 it would be cathastrophe --ANK
+ */
+
+ if (len < 0 || len > 0xFFFF)
+ return -EMSGSIZE;
+
+ /*
+ * Check the flags.
+ */
+
+ if (msg->msg_flags & MSG_OOB) /* Mirror BSD error message compatibility */
+ return -EOPNOTSUPP;
+
+ if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
+ return(-EINVAL);
+
+ /*
+ * Get and verify the address.
+ */
+
+ if (msg->msg_namelen) {
+ struct sockaddr_in *usin = (struct sockaddr_in*)msg->msg_name;
+ if (msg->msg_namelen < sizeof(*usin))
+ return(-EINVAL);
+ if (usin->sin_family != AF_INET) {
+ static int complained;
+ if (!complained++)
+ printk(KERN_INFO "%s forgot to set AF_INET in raw sendmsg. Fix it!\n", current->comm);
+ if (usin->sin_family)
+ return -EINVAL;
+ }
+ daddr = usin->sin_addr.s_addr;
+ /* ANK: I did not forget to get protocol from port field.
+ * I just do not know, who uses this weirdness.
+ * IP_HDRINCL is much more convenient.
+ */
+ } else {
+ if (sk->state != TCP_ESTABLISHED)
+ return(-EINVAL);
+ daddr = sk->daddr;
+ }
+
+ ipc.addr = sk->saddr;
+ ipc.opt = NULL;
+ ipc.oif = sk->bound_dev_if;
+
+ if (msg->msg_controllen) {
+ int tmp = ip_cmsg_send(msg, &ipc);
+ if (tmp)
+ return tmp;
+ if (ipc.opt)
+ free=1;
+ }
+
+ rfh.saddr = ipc.addr;
+ ipc.addr = daddr;
+
+ if (!ipc.opt)
+ ipc.opt = sk->opt;
+
+ if (ipc.opt) {
+ err = -EINVAL;
+ /* Linux does not mangle headers on raw sockets,
+ * so that IP options + IP_HDRINCL is non-sense.
+ */
+ if (sk->ip_hdrincl)
+ goto done;
+ if (ipc.opt->srr) {
+ if (!daddr)
+ goto done;
+ daddr = ipc.opt->faddr;
+ }
+ }
+ tos = RT_TOS(sk->ip_tos) | sk->localroute;
+ if (msg->msg_flags&MSG_DONTROUTE)
+ tos |= RTO_ONLINK;
+
+ if (MULTICAST(daddr)) {
+ if (!ipc.oif)
+ ipc.oif = sk->ip_mc_index;
+ if (!rfh.saddr)
+ rfh.saddr = sk->ip_mc_addr;
+ }
+
+ err = ip_route_output(&rt, daddr, rfh.saddr, tos, ipc.oif);
+
+ if (err)
+ goto done;
+
+ err = -EACCES;
+ if (rt->rt_flags&RTCF_BROADCAST && !sk->broadcast)
+ goto done;
+
+ rfh.iov = msg->msg_iov;
+ rfh.saddr = rt->rt_src;
+ if (!ipc.addr)
+ ipc.addr = rt->rt_dst;
+ err=ip_build_xmit(sk, sk->ip_hdrincl ? raw_getrawfrag : raw_getfrag,
+ &rfh, len, &ipc, rt, msg->msg_flags);
+
+done:
+ if (free)
+ kfree(ipc.opt);
+ ip_rt_put(rt);
+
+ return err<0 ? err : len;
+}
+
+static void raw_close(struct sock *sk, long timeout)
+{
+ /* Observation: when raw_close is called, processes have
+ no access to socket anymore. But net still has.
+ Step one, detach it from networking:
+
+ A. Remove from hash tables.
+ */
+ sk->state = TCP_CLOSE;
+ raw_v4_unhash(sk);
+ /*
+ B. Raw sockets may have direct kernel references. Kill them.
+ */
+ ip_ra_control(sk, 0, NULL);
+
+ /* In this point socket cannot receive new packets anymore */
+
+
+ /* But we still have packets pending on receive
+ queue and probably, our own packets waiting in device queues.
+ sock_destroy will drain receive queue, but transmitted
+ packets will delay socket destruction.
+ Set sk->dead=1 in order to prevent wakeups, when these
+ packet will be freed.
+ */
+ sk->dead=1;
+ destroy_sock(sk);
+
+ /* That's all. No races here. */
+}
+
+/* This gets rid of all the nasties in af_inet. -DaveM */
+static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+{
+ struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
+ int chk_addr_ret;
+
+ if((sk->state != TCP_CLOSE) || (addr_len < sizeof(struct sockaddr_in)))
+ return -EINVAL;
+ chk_addr_ret = inet_addr_type(addr->sin_addr.s_addr);
+ if(addr->sin_addr.s_addr != 0 && chk_addr_ret != RTN_LOCAL &&
+ chk_addr_ret != RTN_MULTICAST && chk_addr_ret != RTN_BROADCAST) {
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ /* Superuser may bind to any address to allow transparent proxying. */
+ if(chk_addr_ret != RTN_UNICAST || !capable(CAP_NET_ADMIN))
+#endif
+ return -EADDRNOTAVAIL;
+ }
+ sk->rcv_saddr = sk->saddr = addr->sin_addr.s_addr;
+ if(chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
+ sk->saddr = 0; /* Use device */
+ dst_release(xchg(&sk->dst_cache, NULL));
+ return 0;
+}
+
+/*
+ * This should be easy, if there is something there
+ * we return it, otherwise we block.
+ */
+
+int raw_recvmsg(struct sock *sk, struct msghdr *msg, int len,
+ int noblock, int flags,int *addr_len)
+{
+ int copied=0;
+ struct sk_buff *skb;
+ int err;
+ struct sockaddr_in *sin=(struct sockaddr_in *)msg->msg_name;
+
+ if (flags & MSG_OOB)
+ return -EOPNOTSUPP;
+
+ if (addr_len)
+ *addr_len=sizeof(*sin);
+
+ if (flags & MSG_ERRQUEUE)
+ return ip_recv_error(sk, msg, len);
+
+ skb=skb_recv_datagram(sk,flags,noblock,&err);
+ if(skb==NULL)
+ return err;
+
+ copied = skb->len;
+ if (len < copied)
+ {
+ msg->msg_flags |= MSG_TRUNC;
+ copied = len;
+ }
+
+ err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ if (err)
+ goto done;
+
+ sk->stamp=skb->stamp;
+
+ /* Copy the address. */
+ if (sin) {
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = skb->nh.iph->saddr;
+ }
+ if (sk->ip_cmsg_flags)
+ ip_cmsg_recv(msg, skb);
+done:
+ skb_free_datagram(sk, skb);
+ return (err ? : copied);
+}
+
+static int raw_init(struct sock *sk)
+{
+ struct raw_opt *tp = &(sk->tp_pinfo.tp_raw4);
+ if (sk->num == IPPROTO_ICMP)
+ memset(&tp->filter, 0, sizeof(tp->filter));
+ return 0;
+}
+
+static int raw_seticmpfilter(struct sock *sk, char *optval, int optlen)
+{
+ if (optlen > sizeof(struct icmp_filter))
+ optlen = sizeof(struct icmp_filter);
+ if (copy_from_user(&sk->tp_pinfo.tp_raw4.filter, optval, optlen))
+ return -EFAULT;
+ return 0;
+}
+
+static int raw_geticmpfilter(struct sock *sk, char *optval, int *optlen)
+{
+ int len;
+
+ if (get_user(len,optlen))
+ return -EFAULT;
+ if (len > sizeof(struct icmp_filter))
+ len = sizeof(struct icmp_filter);
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &sk->tp_pinfo.tp_raw4.filter, len))
+ return -EFAULT;
+ return 0;
+}
+
+static int raw_setsockopt(struct sock *sk, int level, int optname,
+ char *optval, int optlen)
+{
+ if (level != SOL_RAW)
+ return ip_setsockopt(sk, level, optname, optval, optlen);
+
+ switch (optname) {
+ case ICMP_FILTER:
+ if (sk->num != IPPROTO_ICMP)
+ return -EOPNOTSUPP;
+ return raw_seticmpfilter(sk, optval, optlen);
+ };
+
+ return -ENOPROTOOPT;
+}
+
+static int raw_getsockopt(struct sock *sk, int level, int optname,
+ char *optval, int *optlen)
+{
+ if (level != SOL_RAW)
+ return ip_getsockopt(sk, level, optname, optval, optlen);
+
+ switch (optname) {
+ case ICMP_FILTER:
+ if (sk->num != IPPROTO_ICMP)
+ return -EOPNOTSUPP;
+ return raw_geticmpfilter(sk, optval, optlen);
+ };
+
+ return -ENOPROTOOPT;
+}
+
+struct proto raw_prot = {
+ (struct sock *)&raw_prot, /* sklist_next */
+ (struct sock *)&raw_prot, /* sklist_prev */
+ raw_close, /* close */
+ udp_connect, /* connect */
+ NULL, /* accept */
+ NULL, /* retransmit */
+ NULL, /* write_wakeup */
+ NULL, /* read_wakeup */
+ datagram_poll, /* poll */
+#ifdef CONFIG_IP_MROUTE
+ ipmr_ioctl, /* ioctl */
+#else
+ NULL, /* ioctl */
+#endif
+ raw_init, /* init */
+ NULL, /* destroy */
+ NULL, /* shutdown */
+ raw_setsockopt, /* setsockopt */
+ raw_getsockopt, /* getsockopt */
+ raw_sendmsg, /* sendmsg */
+ raw_recvmsg, /* recvmsg */
+ raw_bind, /* bind */
+ raw_rcv_skb, /* backlog_rcv */
+ raw_v4_hash, /* hash */
+ raw_v4_unhash, /* unhash */
+ NULL, /* get_port */
+ 128, /* max_header */
+ 0, /* retransmits */
+ "RAW", /* name */
+ 0, /* inuse */
+ 0 /* highestinuse */
+};
diff --git a/pfinet/linux-src/net/ipv4/route.c b/pfinet/linux-src/net/ipv4/route.c
new file mode 100644
index 00000000..f4e61b95
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/route.c
@@ -0,0 +1,2050 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * ROUTE - implementation of the IP router.
+ *
+ * Version: $Id: route.c,v 1.67.2.4 1999/11/16 02:28:43 davem Exp $
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ * Linus Torvalds, <Linus.Torvalds@helsinki.fi>
+ * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Fixes:
+ * Alan Cox : Verify area fixes.
+ * Alan Cox : cli() protects routing changes
+ * Rui Oliveira : ICMP routing table updates
+ * (rco@di.uminho.pt) Routing table insertion and update
+ * Linus Torvalds : Rewrote bits to be sensible
+ * Alan Cox : Added BSD route gw semantics
+ * Alan Cox : Super /proc >4K
+ * Alan Cox : MTU in route table
+ * Alan Cox : MSS actually. Also added the window
+ * clamper.
+ * Sam Lantinga : Fixed route matching in rt_del()
+ * Alan Cox : Routing cache support.
+ * Alan Cox : Removed compatibility cruft.
+ * Alan Cox : RTF_REJECT support.
+ * Alan Cox : TCP irtt support.
+ * Jonathan Naylor : Added Metric support.
+ * Miquel van Smoorenburg : BSD API fixes.
+ * Miquel van Smoorenburg : Metrics.
+ * Alan Cox : Use __u32 properly
+ * Alan Cox : Aligned routing errors more closely with BSD
+ * our system is still very different.
+ * Alan Cox : Faster /proc handling
+ * Alexey Kuznetsov : Massive rework to support tree based routing,
+ * routing caches and better behaviour.
+ *
+ * Olaf Erb : irtt wasn't being copied right.
+ * Bjorn Ekwall : Kerneld route support.
+ * Alan Cox : Multicast fixed (I hope)
+ * Pavel Krauz : Limited broadcast fixed
+ * Mike McLagan : Routing by source
+ * Alexey Kuznetsov : End of old history. Splitted to fib.c and
+ * route.c and rewritten from scratch.
+ * Andi Kleen : Load-limit warning messages.
+ * Vitaly E. Lavrov : Transparent proxy revived after year coma.
+ * Vitaly E. Lavrov : Race condition in ip_route_input_slow.
+ * Tobias Ringstrom : Uninitialized res.type in ip_route_output_slow.
+ * Vladimir V. Ivanov : IP rule info (flowid) is really useful.
+ * Marc Boucher : routing by fwmark
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/inetdevice.h>
+#include <linux/igmp.h>
+#include <linux/pkt_sched.h>
+#include <linux/mroute.h>
+#include <net/protocol.h>
+#include <net/ip.h>
+#include <net/route.h>
+#include <net/sock.h>
+#include <net/ip_fib.h>
+#include <net/arp.h>
+#include <net/tcp.h>
+#include <net/icmp.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
+
+#define IP_MAX_MTU 0xFFF0
+
+#define RT_GC_TIMEOUT (300*HZ)
+
+int ip_rt_min_delay = 2*HZ;
+int ip_rt_max_delay = 10*HZ;
+int ip_rt_gc_thresh = RT_HASH_DIVISOR;
+int ip_rt_max_size = RT_HASH_DIVISOR*16;
+int ip_rt_gc_timeout = RT_GC_TIMEOUT;
+int ip_rt_gc_interval = 60*HZ;
+int ip_rt_gc_min_interval = 5*HZ;
+int ip_rt_redirect_number = 9;
+int ip_rt_redirect_load = HZ/50;
+int ip_rt_redirect_silence = ((HZ/50) << (9+1));
+int ip_rt_error_cost = HZ;
+int ip_rt_error_burst = 5*HZ;
+int ip_rt_gc_elasticity = 8;
+int ip_rt_mtu_expires = 10*60*HZ;
+
+static unsigned long rt_deadline = 0;
+
+#define RTprint(a...) printk(KERN_DEBUG a)
+
+static void rt_run_flush(unsigned long dummy);
+
+static struct timer_list rt_flush_timer =
+ { NULL, NULL, 0, 0L, rt_run_flush };
+static struct timer_list rt_periodic_timer =
+ { NULL, NULL, 0, 0L, NULL };
+
+/*
+ * Interface to generic destination cache.
+ */
+
+static struct dst_entry * ipv4_dst_check(struct dst_entry * dst, u32);
+static struct dst_entry * ipv4_dst_reroute(struct dst_entry * dst,
+ struct sk_buff *);
+static struct dst_entry * ipv4_negative_advice(struct dst_entry *);
+static void ipv4_link_failure(struct sk_buff *skb);
+static int rt_garbage_collect(void);
+
+
+struct dst_ops ipv4_dst_ops =
+{
+ AF_INET,
+ __constant_htons(ETH_P_IP),
+ RT_HASH_DIVISOR,
+
+ rt_garbage_collect,
+ ipv4_dst_check,
+ ipv4_dst_reroute,
+ NULL,
+ ipv4_negative_advice,
+ ipv4_link_failure,
+};
+
+__u8 ip_tos2prio[16] = {
+ TC_PRIO_BESTEFFORT,
+ TC_PRIO_FILLER,
+ TC_PRIO_BESTEFFORT,
+ TC_PRIO_FILLER,
+ TC_PRIO_BULK,
+ TC_PRIO_FILLER,
+ TC_PRIO_BULK,
+ TC_PRIO_FILLER,
+ TC_PRIO_INTERACTIVE,
+ TC_PRIO_FILLER,
+ TC_PRIO_INTERACTIVE,
+ TC_PRIO_FILLER,
+ TC_PRIO_INTERACTIVE_BULK,
+ TC_PRIO_FILLER,
+ TC_PRIO_INTERACTIVE_BULK,
+ TC_PRIO_FILLER
+};
+
+
+/*
+ * Route cache.
+ */
+
+struct rtable *rt_hash_table[RT_HASH_DIVISOR];
+
+static int rt_intern_hash(unsigned hash, struct rtable * rth, struct rtable ** res);
+
+static __inline__ unsigned rt_hash_code(u32 daddr, u32 saddr, u8 tos)
+{
+ unsigned hash = ((daddr&0xF0F0F0F0)>>4)|((daddr&0x0F0F0F0F)<<4);
+ hash = hash^saddr^tos;
+ hash = hash^(hash>>16);
+ return (hash^(hash>>8)) & 0xFF;
+}
+
+#ifdef CONFIG_PROC_FS
+
+static int rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len=0;
+ off_t pos=0;
+ char temp[129];
+ struct rtable *r;
+ int i;
+
+ pos = 128;
+
+ if (offset<128) {
+ sprintf(buffer,"%-127s\n", "Iface\tDestination\tGateway \tFlags\t\tRefCnt\tUse\tMetric\tSource\t\tMTU\tWindow\tIRTT\tTOS\tHHRef\tHHUptod\tSpecDst");
+ len = 128;
+ }
+
+
+ start_bh_atomic();
+
+ for (i = 0; i<RT_HASH_DIVISOR; i++) {
+ for (r = rt_hash_table[i]; r; r = r->u.rt_next) {
+ /*
+ * Spin through entries until we are ready
+ */
+ pos += 128;
+
+ if (pos <= offset) {
+ len = 0;
+ continue;
+ }
+ sprintf(temp, "%s\t%08lX\t%08lX\t%8X\t%d\t%u\t%d\t%08lX\t%d\t%u\t%u\t%02X\t%d\t%1d\t%08X",
+ r->u.dst.dev ? r->u.dst.dev->name : "*",
+ (unsigned long)r->rt_dst,
+ (unsigned long)r->rt_gateway,
+ r->rt_flags,
+ atomic_read(&r->u.dst.use),
+ atomic_read(&r->u.dst.refcnt),
+ 0,
+ (unsigned long)r->rt_src, (int)r->u.dst.pmtu,
+ r->u.dst.window,
+ (int)r->u.dst.rtt, r->key.tos,
+ r->u.dst.hh ? atomic_read(&r->u.dst.hh->hh_refcnt) : -1,
+ r->u.dst.hh ? (r->u.dst.hh->hh_output == dev_queue_xmit) : 0,
+ r->rt_spec_dst);
+ sprintf(buffer+len,"%-127s\n",temp);
+ len += 128;
+ if (pos >= offset+length)
+ goto done;
+ }
+ }
+
+done:
+ end_bh_atomic();
+
+ *start = buffer+len-(pos-offset);
+ len = pos-offset;
+ if (len>length)
+ len = length;
+ return len;
+}
+#endif
+
+static __inline__ void rt_free(struct rtable *rt)
+{
+ dst_free(&rt->u.dst);
+}
+
+static __inline__ void rt_drop(struct rtable *rt)
+{
+ ip_rt_put(rt);
+ dst_free(&rt->u.dst);
+}
+
+static __inline__ int rt_fast_clean(struct rtable *rth)
+{
+ /* Kill broadcast/multicast entries very aggresively, if they
+ collide in hash table with more useful entries */
+ return ((rth->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST))
+ && rth->key.iif && rth->u.rt_next);
+}
+
+static __inline__ int rt_valuable(struct rtable *rth)
+{
+ return ((rth->rt_flags&(RTCF_REDIRECTED|RTCF_NOTIFY))
+ || rth->u.dst.expires);
+}
+
+static __inline__ int rt_may_expire(struct rtable *rth, int tmo1, int tmo2)
+{
+ int age;
+
+ if (atomic_read(&rth->u.dst.use))
+ return 0;
+
+ if (rth->u.dst.expires && (long)(rth->u.dst.expires - jiffies) <= 0)
+ return 1;
+
+ age = jiffies - rth->u.dst.lastuse;
+ if (age <= tmo1 && !rt_fast_clean(rth))
+ return 0;
+ if (age <= tmo2 && rt_valuable(rth))
+ return 0;
+ return 1;
+}
+
+static void rt_check_expire(unsigned long dummy)
+{
+ int i;
+ static int rover;
+ struct rtable *rth, **rthp;
+ unsigned long now = jiffies;
+
+ for (i=0; i<RT_HASH_DIVISOR/5; i++) {
+ unsigned tmo = ip_rt_gc_timeout;
+
+ rover = (rover + 1) & (RT_HASH_DIVISOR-1);
+ rthp = &rt_hash_table[rover];
+
+ while ((rth = *rthp) != NULL) {
+ if (rth->u.dst.expires) {
+ /* Entrie is expired even if it is in use */
+ if ((long)(now - rth->u.dst.expires) <= 0) {
+ tmo >>= 1;
+ rthp = &rth->u.rt_next;
+ continue;
+ }
+ } else if (!rt_may_expire(rth, tmo, ip_rt_gc_timeout)) {
+ tmo >>= 1;
+ rthp = &rth->u.rt_next;
+ continue;
+ }
+
+ /*
+ * Cleanup aged off entries.
+ */
+ *rthp = rth->u.rt_next;
+ rt_free(rth);
+ }
+
+ /* Fallback loop breaker. */
+ if ((jiffies - now) > 0)
+ break;
+ }
+ rt_periodic_timer.expires = now + ip_rt_gc_interval;
+ add_timer(&rt_periodic_timer);
+}
+
+static void rt_run_flush(unsigned long dummy)
+{
+ int i;
+ struct rtable * rth, * next;
+
+ rt_deadline = 0;
+
+ start_bh_atomic();
+ for (i=0; i<RT_HASH_DIVISOR; i++) {
+ if ((rth = xchg(&rt_hash_table[i], NULL)) == NULL)
+ continue;
+ end_bh_atomic();
+
+ for (; rth; rth=next) {
+ next = rth->u.rt_next;
+ rth->u.rt_next = NULL;
+ rt_free(rth);
+ }
+
+ start_bh_atomic();
+ }
+ end_bh_atomic();
+}
+
+void rt_cache_flush(int delay)
+{
+ unsigned long now = jiffies;
+ int user_mode = !in_interrupt();
+
+ if (delay < 0)
+ delay = ip_rt_min_delay;
+
+ start_bh_atomic();
+
+ if (del_timer(&rt_flush_timer) && delay > 0 && rt_deadline) {
+ long tmo = (long)(rt_deadline - now);
+
+ /* If flush timer is already running
+ and flush request is not immediate (delay > 0):
+
+ if deadline is not achieved, prolongate timer to "delay",
+ otherwise fire it at deadline time.
+ */
+
+ if (user_mode && tmo < ip_rt_max_delay-ip_rt_min_delay)
+ tmo = 0;
+
+ if (delay > tmo)
+ delay = tmo;
+ }
+
+ if (delay <= 0) {
+ end_bh_atomic();
+ rt_run_flush(0);
+ return;
+ }
+
+ if (rt_deadline == 0)
+ rt_deadline = now + ip_rt_max_delay;
+
+ rt_flush_timer.expires = now + delay;
+ add_timer(&rt_flush_timer);
+ end_bh_atomic();
+}
+
+/*
+ Short description of GC goals.
+
+ We want to build algorithm, which will keep routing cache
+ at some equilibrium point, when number of aged off entries
+ is kept approximately equal to newly generated ones.
+
+ Current expiration strength is variable "expire".
+ We try to adjust it dynamically, so that if networking
+ is idle expires is large enough to keep enough of warm entries,
+ and when load increases it reduces to limit cache size.
+ */
+
+static int rt_garbage_collect(void)
+{
+ static unsigned expire = RT_GC_TIMEOUT;
+ static unsigned long last_gc;
+ static int rover;
+ static int equilibrium;
+ struct rtable *rth, **rthp;
+ unsigned long now = jiffies;
+ int goal;
+
+ /*
+ * Garbage collection is pretty expensive,
+ * do not make it too frequently.
+ */
+ if (now - last_gc < ip_rt_gc_min_interval &&
+ atomic_read(&ipv4_dst_ops.entries) < ip_rt_max_size)
+ return 0;
+
+ /* Calculate number of entries, which we want to expire now. */
+ goal = atomic_read(&ipv4_dst_ops.entries) - RT_HASH_DIVISOR*ip_rt_gc_elasticity;
+ if (goal <= 0) {
+ if (equilibrium < ipv4_dst_ops.gc_thresh)
+ equilibrium = ipv4_dst_ops.gc_thresh;
+ goal = atomic_read(&ipv4_dst_ops.entries) - equilibrium;
+ if (goal > 0) {
+ equilibrium += min(goal/2, RT_HASH_DIVISOR);
+ goal = atomic_read(&ipv4_dst_ops.entries) - equilibrium;
+ }
+ } else {
+ /* We are in dangerous area. Try to reduce cache really
+ * aggressively.
+ */
+ goal = max(goal/2, RT_HASH_DIVISOR);
+ equilibrium = atomic_read(&ipv4_dst_ops.entries) - goal;
+ }
+
+ if (now - last_gc >= ip_rt_gc_min_interval)
+ last_gc = now;
+
+ if (goal <= 0) {
+ equilibrium += goal;
+ goto work_done;
+ }
+
+ do {
+ int i, k;
+
+ start_bh_atomic();
+ for (i=0, k=rover; i<RT_HASH_DIVISOR; i++) {
+ unsigned tmo = expire;
+
+ k = (k + 1) & (RT_HASH_DIVISOR-1);
+ rthp = &rt_hash_table[k];
+ while ((rth = *rthp) != NULL) {
+ if (!rt_may_expire(rth, tmo, expire)) {
+ tmo >>= 1;
+ rthp = &rth->u.rt_next;
+ continue;
+ }
+ *rthp = rth->u.rt_next;
+ rth->u.rt_next = NULL;
+ rt_free(rth);
+ goal--;
+ }
+ if (goal <= 0)
+ break;
+ }
+ rover = k;
+ end_bh_atomic();
+
+ if (goal <= 0)
+ goto work_done;
+
+ /* Goal is not achieved. We stop process if:
+
+ - if expire reduced to zero. Otherwise, expire is halfed.
+ - if table is not full.
+ - if we are called from interrupt.
+ - jiffies check is just fallback/debug loop breaker.
+ We will not spin here for long time in any case.
+ */
+
+ if (expire == 0)
+ break;
+
+ expire >>= 1;
+#if RT_CACHE_DEBUG >= 2
+ printk(KERN_DEBUG "expire>> %u %d %d %d\n", expire, atomic_read(&ipv4_dst_ops.entries), goal, i);
+#endif
+
+ if (atomic_read(&ipv4_dst_ops.entries) < ip_rt_max_size)
+ return 0;
+ } while (!in_interrupt() && jiffies - now < 1);
+
+ if (atomic_read(&ipv4_dst_ops.entries) < ip_rt_max_size)
+ return 0;
+ if (net_ratelimit())
+ printk("dst cache overflow\n");
+ return 1;
+
+work_done:
+ expire += ip_rt_gc_min_interval;
+ if (expire > ip_rt_gc_timeout ||
+ atomic_read(&ipv4_dst_ops.entries) < ipv4_dst_ops.gc_thresh)
+ expire = ip_rt_gc_timeout;
+#if RT_CACHE_DEBUG >= 2
+ printk(KERN_DEBUG "expire++ %u %d %d %d\n", expire, atomic_read(&ipv4_dst_ops.entries), goal, rover);
+#endif
+ return 0;
+}
+
+static int rt_intern_hash(unsigned hash, struct rtable * rt, struct rtable ** rp)
+{
+ struct rtable *rth, **rthp;
+ unsigned long now = jiffies;
+ int attempts = !in_interrupt();
+
+restart:
+ start_bh_atomic();
+
+ rthp = &rt_hash_table[hash];
+
+ while ((rth = *rthp) != NULL) {
+ if (memcmp(&rth->key, &rt->key, sizeof(rt->key)) == 0) {
+ /* Put it first */
+ *rthp = rth->u.rt_next;
+ rth->u.rt_next = rt_hash_table[hash];
+ rt_hash_table[hash] = rth;
+
+ atomic_inc(&rth->u.dst.refcnt);
+ atomic_inc(&rth->u.dst.use);
+ rth->u.dst.lastuse = now;
+ end_bh_atomic();
+
+ rt_drop(rt);
+ *rp = rth;
+ return 0;
+ }
+
+ rthp = &rth->u.rt_next;
+ }
+
+ /* Try to bind route to arp only if it is output
+ route or unicast forwarding path.
+ */
+ if (rt->rt_type == RTN_UNICAST || rt->key.iif == 0) {
+ if (!arp_bind_neighbour(&rt->u.dst)) {
+ end_bh_atomic();
+
+ /* Neighbour tables are full and nothing
+ can be released. Try to shrink route cache,
+ it is most likely it holds some neighbour records.
+ */
+ if (attempts-- > 0) {
+ int saved_elasticity = ip_rt_gc_elasticity;
+ int saved_int = ip_rt_gc_min_interval;
+ ip_rt_gc_elasticity = 1;
+ ip_rt_gc_min_interval = 0;
+ rt_garbage_collect();
+ ip_rt_gc_min_interval = saved_int;
+ ip_rt_gc_elasticity = saved_elasticity;
+ goto restart;
+ }
+
+ rt_drop(rt);
+ if (net_ratelimit())
+ printk("neighbour table overflow\n");
+ return -ENOBUFS;
+ }
+ }
+
+ rt->u.rt_next = rt_hash_table[hash];
+#if RT_CACHE_DEBUG >= 2
+ if (rt->u.rt_next) {
+ struct rtable * trt;
+ printk("rt_cache @%02x: %08x", hash, rt->rt_dst);
+ for (trt=rt->u.rt_next; trt; trt=trt->u.rt_next)
+ printk(" . %08x", trt->rt_dst);
+ printk("\n");
+ }
+#endif
+ rt_hash_table[hash] = rt;
+ end_bh_atomic();
+ *rp = rt;
+ return 0;
+}
+
+static void rt_del(unsigned hash, struct rtable *rt)
+{
+ struct rtable **rthp;
+
+ start_bh_atomic();
+ ip_rt_put(rt);
+ for (rthp = &rt_hash_table[hash]; *rthp; rthp = &(*rthp)->u.rt_next) {
+ if (*rthp == rt) {
+ *rthp = rt->u.rt_next;
+ rt_free(rt);
+ break;
+ }
+ }
+ end_bh_atomic();
+}
+
+void ip_rt_redirect(u32 old_gw, u32 daddr, u32 new_gw,
+ u32 saddr, u8 tos, struct device *dev)
+{
+ int i, k;
+ struct in_device *in_dev = dev->ip_ptr;
+ struct rtable *rth, **rthp;
+ u32 skeys[2] = { saddr, 0 };
+ int ikeys[2] = { dev->ifindex, 0 };
+
+ tos &= IPTOS_TOS_MASK;
+
+ if (!in_dev)
+ return;
+
+ if (new_gw == old_gw || !IN_DEV_RX_REDIRECTS(in_dev)
+ || MULTICAST(new_gw) || BADCLASS(new_gw) || ZERONET(new_gw))
+ goto reject_redirect;
+
+ if (!IN_DEV_SHARED_MEDIA(in_dev)) {
+ if (!inet_addr_onlink(in_dev, new_gw, old_gw))
+ goto reject_redirect;
+ if (IN_DEV_SEC_REDIRECTS(in_dev) && ip_fib_check_default(new_gw, dev))
+ goto reject_redirect;
+ } else {
+ if (inet_addr_type(new_gw) != RTN_UNICAST)
+ goto reject_redirect;
+ }
+
+ for (i=0; i<2; i++) {
+ for (k=0; k<2; k++) {
+ unsigned hash = rt_hash_code(daddr, skeys[i]^(ikeys[k]<<5), tos);
+
+ rthp=&rt_hash_table[hash];
+
+ while ( (rth = *rthp) != NULL) {
+ struct rtable *rt;
+
+ if (rth->key.dst != daddr ||
+ rth->key.src != skeys[i] ||
+ rth->key.tos != tos ||
+ rth->key.oif != ikeys[k] ||
+ rth->key.iif != 0) {
+ rthp = &rth->u.rt_next;
+ continue;
+ }
+
+ if (rth->rt_dst != daddr ||
+ rth->rt_src != saddr ||
+ rth->u.dst.error ||
+ rth->rt_gateway != old_gw ||
+ rth->u.dst.dev != dev)
+ break;
+
+ dst_clone(&rth->u.dst);
+
+ rt = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops);
+ if (rt == NULL) {
+ ip_rt_put(rth);
+ return;
+ }
+
+ /*
+ * Copy all the information.
+ */
+ *rt = *rth;
+ atomic_set(&rt->u.dst.refcnt, 1);
+ atomic_set(&rt->u.dst.use, 1);
+ rt->u.dst.lastuse = jiffies;
+ rt->u.dst.neighbour = NULL;
+ rt->u.dst.hh = NULL;
+ rt->u.dst.obsolete = 0;
+
+ rt->rt_flags |= RTCF_REDIRECTED;
+
+ /* Gateway is different ... */
+ rt->rt_gateway = new_gw;
+
+ /* Redirect received -> path was valid */
+ dst_confirm(&rth->u.dst);
+
+ if (!arp_bind_neighbour(&rt->u.dst) ||
+ !(rt->u.dst.neighbour->nud_state&NUD_VALID)) {
+ if (rt->u.dst.neighbour)
+ neigh_event_send(rt->u.dst.neighbour, NULL);
+ ip_rt_put(rth);
+ rt_drop(rt);
+ break;
+ }
+
+ rt_del(hash, rth);
+
+ if (!rt_intern_hash(hash, rt, &rt))
+ ip_rt_put(rt);
+ break;
+ }
+ }
+ }
+ return;
+
+reject_redirect:
+#ifdef CONFIG_IP_ROUTE_VERBOSE
+ if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())
+ printk(KERN_INFO "Redirect from %X/%s to %X ignored."
+ "Path = %X -> %X, tos %02x\n",
+ ntohl(old_gw), dev->name, ntohl(new_gw),
+ ntohl(saddr), ntohl(daddr), tos);
+#else
+ ; /* Do nothing. */
+#endif
+}
+
+static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst)
+{
+ struct rtable *rt = (struct rtable*)dst;
+
+ if (rt != NULL) {
+ if (dst->obsolete) {
+ ip_rt_put(rt);
+ return NULL;
+ }
+ if ((rt->rt_flags&RTCF_REDIRECTED) || rt->u.dst.expires) {
+ unsigned hash = rt_hash_code(rt->key.dst, rt->key.src^(rt->key.oif<<5), rt->key.tos);
+#if RT_CACHE_DEBUG >= 1
+ printk(KERN_DEBUG "ip_rt_advice: redirect to %d.%d.%d.%d/%02x dropped\n", NIPQUAD(rt->rt_dst), rt->key.tos);
+#endif
+ rt_del(hash, rt);
+ return NULL;
+ }
+ }
+ return dst;
+}
+
+/*
+ * Algorithm:
+ * 1. The first ip_rt_redirect_number redirects are sent
+ * with exponential backoff, then we stop sending them at all,
+ * assuming that the host ignores our redirects.
+ * 2. If we did not see packets requiring redirects
+ * during ip_rt_redirect_silence, we assume that the host
+ * forgot redirected route and start to send redirects again.
+ *
+ * This algorithm is much cheaper and more intelligent than dumb load limiting
+ * in icmp.c.
+ *
+ * NOTE. Do not forget to inhibit load limiting for redirects (redundant)
+ * and "frag. need" (breaks PMTU discovery) in icmp.c.
+ */
+
+void ip_rt_send_redirect(struct sk_buff *skb)
+{
+ struct rtable *rt = (struct rtable*)skb->dst;
+ struct in_device *in_dev = (struct in_device*)rt->u.dst.dev->ip_ptr;
+
+ if (!in_dev || !IN_DEV_TX_REDIRECTS(in_dev))
+ return;
+
+ /* No redirected packets during ip_rt_redirect_silence;
+ * reset the algorithm.
+ */
+ if (jiffies - rt->u.dst.rate_last > ip_rt_redirect_silence)
+ rt->u.dst.rate_tokens = 0;
+
+ /* Too many ignored redirects; do not send anything
+ * set u.dst.rate_last to the last seen redirected packet.
+ */
+ if (rt->u.dst.rate_tokens >= ip_rt_redirect_number) {
+ rt->u.dst.rate_last = jiffies;
+ return;
+ }
+
+ /* Check for load limit; set rate_last to the latest sent
+ * redirect.
+ */
+ if (jiffies - rt->u.dst.rate_last > (ip_rt_redirect_load<<rt->u.dst.rate_tokens)) {
+ icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, rt->rt_gateway);
+ rt->u.dst.rate_last = jiffies;
+ ++rt->u.dst.rate_tokens;
+#ifdef CONFIG_IP_ROUTE_VERBOSE
+ if (IN_DEV_LOG_MARTIANS(in_dev) &&
+ rt->u.dst.rate_tokens == ip_rt_redirect_number && net_ratelimit())
+ printk(KERN_WARNING "host %08x/if%d ignores redirects for %08x to %08x.\n",
+ rt->rt_src, rt->rt_iif, rt->rt_dst, rt->rt_gateway);
+#endif
+ }
+}
+
+static int ip_error(struct sk_buff *skb)
+{
+ struct rtable *rt = (struct rtable*)skb->dst;
+ unsigned long now;
+ int code;
+
+ switch (rt->u.dst.error) {
+ case EINVAL:
+ default:
+ kfree_skb(skb);
+ return 0;
+ case EHOSTUNREACH:
+ code = ICMP_HOST_UNREACH;
+ break;
+ case ENETUNREACH:
+ code = ICMP_NET_UNREACH;
+ break;
+ case EACCES:
+ code = ICMP_PKT_FILTERED;
+ break;
+ }
+
+ now = jiffies;
+ if ((rt->u.dst.rate_tokens += (now - rt->u.dst.rate_last)) > ip_rt_error_burst)
+ rt->u.dst.rate_tokens = ip_rt_error_burst;
+ rt->u.dst.rate_last = now;
+ if (rt->u.dst.rate_tokens >= ip_rt_error_cost) {
+ rt->u.dst.rate_tokens -= ip_rt_error_cost;
+ icmp_send(skb, ICMP_DEST_UNREACH, code, 0);
+ }
+
+ kfree_skb(skb);
+ return 0;
+}
+
+/*
+ * The last two values are not from the RFC but
+ * are needed for AMPRnet AX.25 paths.
+ */
+
+static unsigned short mtu_plateau[] =
+{32000, 17914, 8166, 4352, 2002, 1492, 576, 296, 216, 128 };
+
+static __inline__ unsigned short guess_mtu(unsigned short old_mtu)
+{
+ int i;
+
+ for (i = 0; i < sizeof(mtu_plateau)/sizeof(mtu_plateau[0]); i++)
+ if (old_mtu > mtu_plateau[i])
+ return mtu_plateau[i];
+ return 68;
+}
+
+unsigned short ip_rt_frag_needed(struct iphdr *iph, unsigned short new_mtu)
+{
+ int i;
+ unsigned short old_mtu = ntohs(iph->tot_len);
+ struct rtable *rth;
+ u32 skeys[2] = { iph->saddr, 0, };
+ u32 daddr = iph->daddr;
+ u8 tos = iph->tos & IPTOS_TOS_MASK;
+ unsigned short est_mtu = 0;
+
+ if (ipv4_config.no_pmtu_disc)
+ return 0;
+
+ for (i=0; i<2; i++) {
+ unsigned hash = rt_hash_code(daddr, skeys[i], tos);
+
+ for (rth = rt_hash_table[hash]; rth; rth = rth->u.rt_next) {
+ if (rth->key.dst == daddr &&
+ rth->key.src == skeys[i] &&
+ rth->rt_dst == daddr &&
+ rth->rt_src == iph->saddr &&
+ rth->key.tos == tos &&
+ rth->key.iif == 0 &&
+ !(rth->u.dst.mxlock&(1<<RTAX_MTU))) {
+ unsigned short mtu = new_mtu;
+
+ if (new_mtu < 68 || new_mtu >= old_mtu) {
+
+ /* BSD 4.2 compatibility hack :-( */
+ if (mtu == 0 && old_mtu >= rth->u.dst.pmtu &&
+ old_mtu >= 68 + (iph->ihl<<2))
+ old_mtu -= iph->ihl<<2;
+
+ mtu = guess_mtu(old_mtu);
+ }
+ if (mtu <= rth->u.dst.pmtu) {
+ if (mtu < rth->u.dst.pmtu) {
+ dst_confirm(&rth->u.dst);
+ rth->u.dst.pmtu = mtu;
+ dst_set_expires(&rth->u.dst, ip_rt_mtu_expires);
+ }
+ est_mtu = mtu;
+ }
+ }
+ }
+ }
+ return est_mtu ? : new_mtu;
+}
+
+void ip_rt_update_pmtu(struct dst_entry *dst, unsigned mtu)
+{
+ if (dst->pmtu > mtu && mtu >= 68 &&
+ !(dst->mxlock&(1<<RTAX_MTU))) {
+ dst->pmtu = mtu;
+ dst_set_expires(dst, ip_rt_mtu_expires);
+ }
+}
+
+static struct dst_entry * ipv4_dst_check(struct dst_entry * dst, u32 cookie)
+{
+ dst_release(dst);
+ return NULL;
+}
+
+static struct dst_entry * ipv4_dst_reroute(struct dst_entry * dst,
+ struct sk_buff *skb)
+{
+ return NULL;
+}
+
+static void ipv4_link_failure(struct sk_buff *skb)
+{
+ struct rtable *rt;
+
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+
+ rt = (struct rtable *) skb->dst;
+ if (rt)
+ dst_set_expires(&rt->u.dst, 0);
+}
+
+static int ip_rt_bug(struct sk_buff *skb)
+{
+ printk(KERN_DEBUG "ip_rt_bug: %08x -> %08x, %s\n", skb->nh.iph->saddr,
+ skb->nh.iph->daddr, skb->dev ? skb->dev->name : "?");
+ kfree_skb(skb);
+ return 0;
+}
+
+/*
+ We do not cache source address of outgoing interface,
+ because it is used only by IP RR, TS and SRR options,
+ so that it out of fast path.
+
+ BTW remember: "addr" is allowed to be not aligned
+ in IP options!
+ */
+
+void ip_rt_get_source(u8 *addr, struct rtable *rt)
+{
+ u32 src;
+ struct fib_result res;
+
+ if (rt->key.iif == 0)
+ src = rt->rt_src;
+ else if (fib_lookup(&rt->key, &res) == 0 && res.type != RTN_NAT)
+ src = FIB_RES_PREFSRC(res);
+ else
+ src = inet_select_addr(rt->u.dst.dev, rt->rt_gateway, RT_SCOPE_UNIVERSE);
+ memcpy(addr, &src, 4);
+}
+
+#ifdef CONFIG_NET_CLS_ROUTE
+static void set_class_tag(struct rtable *rt, u32 tag)
+{
+ if (!(rt->u.dst.tclassid&0xFFFF))
+ rt->u.dst.tclassid |= tag&0xFFFF;
+ if (!(rt->u.dst.tclassid&0xFFFF0000))
+ rt->u.dst.tclassid |= tag&0xFFFF0000;
+}
+#endif
+
+static void rt_set_nexthop(struct rtable *rt, struct fib_result *res, u32 itag)
+{
+ struct fib_info *fi = res->fi;
+
+ if (fi) {
+ if (FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK)
+ rt->rt_gateway = FIB_RES_GW(*res);
+ rt->u.dst.mxlock = fi->fib_metrics[RTAX_LOCK-1];
+ rt->u.dst.pmtu = fi->fib_mtu;
+ if (fi->fib_mtu == 0) {
+ rt->u.dst.pmtu = rt->u.dst.dev->mtu;
+ if (rt->u.dst.pmtu > IP_MAX_MTU)
+ rt->u.dst.pmtu = IP_MAX_MTU;
+ if (rt->u.dst.pmtu < 68)
+ rt->u.dst.pmtu = 68;
+ if (rt->u.dst.mxlock&(1<<RTAX_MTU) &&
+ rt->rt_gateway != rt->rt_dst &&
+ rt->u.dst.pmtu > 576)
+ rt->u.dst.pmtu = 576;
+ }
+ rt->u.dst.window= fi->fib_window ? : 0;
+ rt->u.dst.rtt = fi->fib_rtt ? : TCP_TIMEOUT_INIT;
+#ifdef CONFIG_NET_CLS_ROUTE
+ rt->u.dst.tclassid = FIB_RES_NH(*res).nh_tclassid;
+#endif
+ } else {
+ rt->u.dst.pmtu = rt->u.dst.dev->mtu;
+ if (rt->u.dst.pmtu > IP_MAX_MTU)
+ rt->u.dst.pmtu = IP_MAX_MTU;
+ if (rt->u.dst.pmtu < 68)
+ rt->u.dst.pmtu = 68;
+ rt->u.dst.window= 0;
+ rt->u.dst.rtt = TCP_TIMEOUT_INIT;
+ }
+#ifdef CONFIG_NET_CLS_ROUTE
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+ set_class_tag(rt, fib_rules_tclass(res));
+#endif
+ set_class_tag(rt, itag);
+#endif
+ rt->rt_type = res->type;
+}
+
+static int
+ip_route_input_mc(struct sk_buff *skb, u32 daddr, u32 saddr,
+ u8 tos, struct device *dev, int our)
+{
+ unsigned hash;
+ struct rtable *rth;
+ u32 spec_dst;
+ struct in_device *in_dev = dev->ip_ptr;
+ u32 itag = 0;
+
+ /* Primary sanity checks. */
+
+ if (MULTICAST(saddr) || BADCLASS(saddr) || LOOPBACK(saddr) ||
+ in_dev == NULL || skb->protocol != __constant_htons(ETH_P_IP))
+ return -EINVAL;
+
+ if (ZERONET(saddr)) {
+ if (!LOCAL_MCAST(daddr))
+ return -EINVAL;
+ spec_dst = inet_select_addr(dev, 0, RT_SCOPE_LINK);
+ } else if (fib_validate_source(saddr, 0, tos, 0, dev, &spec_dst, &itag) < 0)
+ return -EINVAL;
+
+ rth = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops);
+ if (!rth)
+ return -ENOBUFS;
+
+ rth->u.dst.output= ip_rt_bug;
+
+ atomic_set(&rth->u.dst.use, 1);
+ rth->key.dst = daddr;
+ rth->rt_dst = daddr;
+ rth->key.tos = tos;
+#ifdef CONFIG_IP_ROUTE_FWMARK
+ rth->key.fwmark = skb->fwmark;
+#endif
+ rth->key.src = saddr;
+ rth->rt_src = saddr;
+#ifdef CONFIG_IP_ROUTE_NAT
+ rth->rt_dst_map = daddr;
+ rth->rt_src_map = saddr;
+#endif
+#ifdef CONFIG_NET_CLS_ROUTE
+ rth->u.dst.tclassid = itag;
+#endif
+ rth->rt_iif =
+ rth->key.iif = dev->ifindex;
+ rth->u.dst.dev = &loopback_dev;
+ rth->key.oif = 0;
+ rth->rt_gateway = daddr;
+ rth->rt_spec_dst= spec_dst;
+ rth->rt_type = RTN_MULTICAST;
+ rth->rt_flags = RTCF_MULTICAST;
+ if (our) {
+ rth->u.dst.input= ip_local_deliver;
+ rth->rt_flags |= RTCF_LOCAL;
+ }
+
+#ifdef CONFIG_IP_MROUTE
+ if (!LOCAL_MCAST(daddr) && IN_DEV_MFORWARD(in_dev))
+ rth->u.dst.input = ip_mr_input;
+#endif
+
+ hash = rt_hash_code(daddr, saddr^(dev->ifindex<<5), tos);
+ return rt_intern_hash(hash, rth, (struct rtable**)&skb->dst);
+}
+
+/*
+ * NOTE. We drop all the packets that has local source
+ * addresses, because every properly looped back packet
+ * must have correct destination already attached by output routine.
+ *
+ * Such approach solves two big problems:
+ * 1. Not simplex devices are handled properly.
+ * 2. IP spoofing attempts are filtered with 100% of guarantee.
+ */
+
+int ip_route_input_slow(struct sk_buff *skb, u32 daddr, u32 saddr,
+ u8 tos, struct device *dev)
+{
+ struct rt_key key;
+ struct fib_result res;
+ struct in_device *in_dev = dev->ip_ptr;
+ struct in_device *out_dev;
+ unsigned flags = 0;
+ u32 itag = 0;
+ struct rtable * rth;
+ unsigned hash;
+ u32 spec_dst;
+ int err = -EINVAL;
+
+ /*
+ * IP on this device is disabled.
+ */
+
+ if (!in_dev)
+ return -EINVAL;
+
+ key.dst = daddr;
+ key.src = saddr;
+ key.tos = tos;
+#ifdef CONFIG_IP_ROUTE_FWMARK
+ key.fwmark = skb->fwmark;
+#endif
+ key.iif = dev->ifindex;
+ key.oif = 0;
+ key.scope = RT_SCOPE_UNIVERSE;
+
+ hash = rt_hash_code(daddr, saddr^(key.iif<<5), tos);
+
+ /* Check for the most weird martians, which can be not detected
+ by fib_lookup.
+ */
+
+ if (MULTICAST(saddr) || BADCLASS(saddr) || LOOPBACK(saddr))
+ goto martian_source;
+
+ if (daddr == 0xFFFFFFFF || (saddr == 0 && daddr == 0))
+ goto brd_input;
+
+ /* Accept zero addresses only to limited broadcast;
+ * I even do not know to fix it or not. Waiting for complains :-)
+ */
+ if (ZERONET(saddr))
+ goto martian_source;
+
+ if (BADCLASS(daddr) || ZERONET(daddr) || LOOPBACK(daddr))
+ goto martian_destination;
+
+ /*
+ * Now we are ready to route packet.
+ */
+ if ((err = fib_lookup(&key, &res))) {
+ if (!IN_DEV_FORWARD(in_dev))
+ return -EINVAL;
+ goto no_route;
+ }
+
+#ifdef CONFIG_IP_ROUTE_NAT
+ /* Policy is applied before mapping destination,
+ but rerouting after map should be made with old source.
+ */
+
+ if (1) {
+ u32 src_map = saddr;
+ if (res.r)
+ src_map = fib_rules_policy(saddr, &res, &flags);
+
+ if (res.type == RTN_NAT) {
+ key.dst = fib_rules_map_destination(daddr, &res);
+ if (fib_lookup(&key, &res) || res.type != RTN_UNICAST)
+ return -EINVAL;
+ flags |= RTCF_DNAT;
+ }
+ key.src = src_map;
+ }
+#endif
+
+ if (res.type == RTN_BROADCAST)
+ goto brd_input;
+
+ if (res.type == RTN_LOCAL) {
+ int result;
+ result = fib_validate_source(saddr, daddr, tos, loopback_dev.ifindex,
+ dev, &spec_dst, &itag);
+ if (result < 0)
+ goto martian_source;
+ if (result)
+ flags |= RTCF_DIRECTSRC;
+ spec_dst = daddr;
+ goto local_input;
+ }
+
+ if (!IN_DEV_FORWARD(in_dev))
+ return -EINVAL;
+ if (res.type != RTN_UNICAST)
+ goto martian_destination;
+
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ if (res.fi->fib_nhs > 1 && key.oif == 0)
+ fib_select_multipath(&key, &res);
+#endif
+ out_dev = FIB_RES_DEV(res)->ip_ptr;
+ if (out_dev == NULL) {
+ if (net_ratelimit())
+ printk(KERN_CRIT "Bug in ip_route_input_slow(). Please, report\n");
+ return -EINVAL;
+ }
+
+ err = fib_validate_source(saddr, daddr, tos, FIB_RES_OIF(res), dev, &spec_dst, &itag);
+ if (err < 0)
+ goto martian_source;
+
+ if (err)
+ flags |= RTCF_DIRECTSRC;
+
+ if (out_dev == in_dev && err && !(flags&(RTCF_NAT|RTCF_MASQ)) &&
+ (IN_DEV_SHARED_MEDIA(out_dev)
+ || inet_addr_onlink(out_dev, saddr, FIB_RES_GW(res))))
+ flags |= RTCF_DOREDIRECT;
+
+ if (skb->protocol != __constant_htons(ETH_P_IP)) {
+ /* Not IP (i.e. ARP). Do not create route, if it is
+ * invalid for proxy arp. DNAT routes are always valid.
+ */
+ if (out_dev == in_dev && !(flags&RTCF_DNAT))
+ return -EINVAL;
+ }
+
+ rth = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops);
+ if (!rth)
+ return -ENOBUFS;
+
+ atomic_set(&rth->u.dst.use, 1);
+ rth->key.dst = daddr;
+ rth->rt_dst = daddr;
+ rth->key.tos = tos;
+#ifdef CONFIG_IP_ROUTE_FWMARK
+ rth->key.fwmark = skb->fwmark;
+#endif
+ rth->key.src = saddr;
+ rth->rt_src = saddr;
+ rth->rt_gateway = daddr;
+#ifdef CONFIG_IP_ROUTE_NAT
+ rth->rt_src_map = key.src;
+ rth->rt_dst_map = key.dst;
+ if (flags&RTCF_DNAT)
+ rth->rt_gateway = key.dst;
+#endif
+ rth->rt_iif =
+ rth->key.iif = dev->ifindex;
+ rth->u.dst.dev = out_dev->dev;
+ rth->key.oif = 0;
+ rth->rt_spec_dst= spec_dst;
+
+ rth->u.dst.input = ip_forward;
+ rth->u.dst.output = ip_output;
+
+ rt_set_nexthop(rth, &res, itag);
+
+ rth->rt_flags = flags;
+
+#ifdef CONFIG_NET_FASTROUTE
+ if (netdev_fastroute && !(flags&(RTCF_NAT|RTCF_MASQ|RTCF_DOREDIRECT))) {
+ struct device *odev = rth->u.dst.dev;
+ if (odev != dev &&
+ dev->accept_fastpath &&
+ odev->mtu >= dev->mtu &&
+ dev->accept_fastpath(dev, &rth->u.dst) == 0)
+ rth->rt_flags |= RTCF_FAST;
+ }
+#endif
+
+ return rt_intern_hash(hash, rth, (struct rtable**)&skb->dst);
+
+brd_input:
+ if (skb->protocol != __constant_htons(ETH_P_IP))
+ return -EINVAL;
+
+ if (ZERONET(saddr)) {
+ spec_dst = inet_select_addr(dev, 0, RT_SCOPE_LINK);
+ } else {
+ err = fib_validate_source(saddr, 0, tos, 0, dev, &spec_dst, &itag);
+ if (err < 0)
+ goto martian_source;
+ if (err)
+ flags |= RTCF_DIRECTSRC;
+ }
+ flags |= RTCF_BROADCAST;
+ res.type = RTN_BROADCAST;
+
+local_input:
+ rth = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops);
+ if (!rth)
+ return -ENOBUFS;
+
+ rth->u.dst.output= ip_rt_bug;
+
+ atomic_set(&rth->u.dst.use, 1);
+ rth->key.dst = daddr;
+ rth->rt_dst = daddr;
+ rth->key.tos = tos;
+#ifdef CONFIG_IP_ROUTE_FWMARK
+ rth->key.fwmark = skb->fwmark;
+#endif
+ rth->key.src = saddr;
+ rth->rt_src = saddr;
+#ifdef CONFIG_IP_ROUTE_NAT
+ rth->rt_dst_map = key.dst;
+ rth->rt_src_map = key.src;
+#endif
+#ifdef CONFIG_NET_CLS_ROUTE
+ rth->u.dst.tclassid = itag;
+#endif
+ rth->rt_iif =
+ rth->key.iif = dev->ifindex;
+ rth->u.dst.dev = &loopback_dev;
+ rth->key.oif = 0;
+ rth->rt_gateway = daddr;
+ rth->rt_spec_dst= spec_dst;
+ rth->u.dst.input= ip_local_deliver;
+ rth->rt_flags = flags|RTCF_LOCAL;
+ if (res.type == RTN_UNREACHABLE) {
+ rth->u.dst.input= ip_error;
+ rth->u.dst.error= -err;
+ rth->rt_flags &= ~RTCF_LOCAL;
+ }
+ rth->rt_type = res.type;
+ return rt_intern_hash(hash, rth, (struct rtable**)&skb->dst);
+
+no_route:
+ spec_dst = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE);
+ res.type = RTN_UNREACHABLE;
+ goto local_input;
+
+ /*
+ * Do not cache martian addresses: they should be logged (RFC1812)
+ */
+martian_destination:
+#ifdef CONFIG_IP_ROUTE_VERBOSE
+ if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())
+ printk(KERN_WARNING "martian destination %08x from %08x, dev %s\n", daddr, saddr, dev->name);
+#endif
+ return -EINVAL;
+
+martian_source:
+#ifdef CONFIG_IP_ROUTE_VERBOSE
+ if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit()) {
+ /*
+ * RFC1812 recommenadtion, if source is martian,
+ * the only hint is MAC header.
+ */
+ printk(KERN_WARNING "martian source %08x for %08x, dev %s\n", saddr, daddr, dev->name);
+ if (dev->hard_header_len) {
+ int i;
+ unsigned char *p = skb->mac.raw;
+ printk(KERN_WARNING "ll header:");
+ for (i=0; i<dev->hard_header_len; i++, p++)
+ printk(" %02x", *p);
+ printk("\n");
+ }
+ }
+#endif
+ return -EINVAL;
+}
+
+int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr,
+ u8 tos, struct device *dev)
+{
+ struct rtable * rth;
+ unsigned hash;
+ int iif = dev->ifindex;
+
+ tos &= IPTOS_TOS_MASK;
+ hash = rt_hash_code(daddr, saddr^(iif<<5), tos);
+
+ for (rth=rt_hash_table[hash]; rth; rth=rth->u.rt_next) {
+ if (rth->key.dst == daddr &&
+ rth->key.src == saddr &&
+ rth->key.iif == iif &&
+ rth->key.oif == 0 &&
+#ifdef CONFIG_IP_ROUTE_FWMARK
+ rth->key.fwmark == skb->fwmark &&
+#endif
+ rth->key.tos == tos) {
+ rth->u.dst.lastuse = jiffies;
+ atomic_inc(&rth->u.dst.use);
+ atomic_inc(&rth->u.dst.refcnt);
+ skb->dst = (struct dst_entry*)rth;
+ return 0;
+ }
+ }
+
+ /* Multicast recognition logic is moved from route cache to here.
+ The problem was that too many Ethernet cards have broken/missing
+ hardware multicast filters :-( As result the host on multicasting
+ network acquires a lot of useless route cache entries, sort of
+ SDR messages from all the world. Now we try to get rid of them.
+ Really, provided software IP multicast filter is organized
+ reasonably (at least, hashed), it does not result in a slowdown
+ comparing with route cache reject entries.
+ Note, that multicast routers are not affected, because
+ route cache entry is created eventually.
+ */
+ if (MULTICAST(daddr)) {
+ int our = ip_check_mc(dev, daddr);
+ if (!our
+#ifdef CONFIG_IP_MROUTE
+ && (LOCAL_MCAST(daddr) || !dev->ip_ptr ||
+ !IN_DEV_MFORWARD((struct in_device*)dev->ip_ptr))
+#endif
+ ) return -EINVAL;
+ return ip_route_input_mc(skb, daddr, saddr, tos, dev, our);
+ }
+ return ip_route_input_slow(skb, daddr, saddr, tos, dev);
+}
+
+/*
+ * Major route resolver routine.
+ */
+
+int ip_route_output_slow(struct rtable **rp, u32 daddr, u32 saddr, u32 tos, int oif)
+{
+ struct rt_key key;
+ struct fib_result res;
+ unsigned flags = 0;
+ struct rtable *rth;
+ struct device *dev_out = NULL;
+ unsigned hash;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ u32 nochecksrc = (tos & RTO_TPROXY);
+#endif
+
+ tos &= IPTOS_TOS_MASK|RTO_ONLINK;
+ key.dst = daddr;
+ key.src = saddr;
+ key.tos = tos&IPTOS_TOS_MASK;
+ key.iif = loopback_dev.ifindex;
+ key.oif = oif;
+ key.scope = (tos&RTO_ONLINK) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
+ res.fi = NULL;
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+ res.r = NULL;
+#endif
+
+ if (saddr) {
+ if (MULTICAST(saddr) || BADCLASS(saddr) || ZERONET(saddr))
+ return -EINVAL;
+
+ /* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */
+ dev_out = ip_dev_find(saddr);
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ /* If address is not local, test for transparent proxy flag;
+ if address is local --- clear the flag.
+ */
+ if (dev_out == NULL) {
+ if (nochecksrc == 0 || inet_addr_type(saddr) != RTN_UNICAST)
+ return -EINVAL;
+ flags |= RTCF_TPROXY;
+ }
+#else
+ if (dev_out == NULL)
+ return -EINVAL;
+#endif
+
+ /* I removed check for oif == dev_out->oif here.
+ It was wrong by three reasons:
+ 1. ip_dev_find(saddr) can return wrong iface, if saddr is
+ assigned to multiple interfaces.
+ 2. Moreover, we are allowed to send packets with saddr
+ of another iface. --ANK
+ */
+
+ if (oif == 0 &&
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ dev_out &&
+#endif
+ (MULTICAST(daddr) || daddr == 0xFFFFFFFF)) {
+ /* Special hack: user can direct multicasts
+ and limited broadcast via necessary interface
+ without fiddling with IP_MULTICAST_IF or IP_PKTINFO.
+ This hack is not just for fun, it allows
+ vic,vat and friends to work.
+ They bind socket to loopback, set ttl to zero
+ and expect that it will work.
+ From the viewpoint of routing cache they are broken,
+ because we are not allowed to build multicast path
+ with loopback source addr (look, routing cache
+ cannot know, that ttl is zero, so that packet
+ will not leave this host and route is valid).
+ Luckily, this hack is good workaround.
+ */
+
+ key.oif = dev_out->ifindex;
+ goto make_route;
+ }
+ dev_out = NULL;
+ }
+ if (oif) {
+ dev_out = dev_get_by_index(oif);
+ if (dev_out == NULL)
+ return -ENODEV;
+ if (dev_out->ip_ptr == NULL)
+ return -ENODEV; /* Wrong error code */
+
+ if (LOCAL_MCAST(daddr) || daddr == 0xFFFFFFFF) {
+ if (!key.src)
+ key.src = inet_select_addr(dev_out, 0, RT_SCOPE_LINK);
+ goto make_route;
+ }
+ if (!key.src) {
+ if (MULTICAST(daddr))
+ key.src = inet_select_addr(dev_out, 0, key.scope);
+ else if (!daddr)
+ key.src = inet_select_addr(dev_out, 0, RT_SCOPE_HOST);
+ }
+ }
+
+ if (!key.dst) {
+ key.dst = key.src;
+ if (!key.dst)
+ key.dst = key.src = htonl(INADDR_LOOPBACK);
+ dev_out = &loopback_dev;
+ key.oif = loopback_dev.ifindex;
+ res.type = RTN_LOCAL;
+ flags |= RTCF_LOCAL;
+ goto make_route;
+ }
+
+ if (fib_lookup(&key, &res)) {
+ res.fi = NULL;
+ if (oif) {
+ /* Apparently, routing tables are wrong. Assume,
+ that the destination is on link.
+
+ WHY? DW.
+ Because we are allowed to send to iface
+ even if it has NO routes and NO assigned
+ addresses. When oif is specified, routing
+ tables are looked up with only one purpose:
+ to catch if destination is gatewayed, rather than
+ direct. Moreover, if MSG_DONTROUTE is set,
+ we send packet, ignoring both routing tables
+ and ifaddr state. --ANK
+
+
+ We could make it even if oif is unknown,
+ likely IPv6, but we do not.
+ */
+
+ if (key.src == 0)
+ key.src = inet_select_addr(dev_out, 0, RT_SCOPE_LINK);
+ res.type = RTN_UNICAST;
+ goto make_route;
+ }
+ return -ENETUNREACH;
+ }
+
+ if (res.type == RTN_NAT)
+ return -EINVAL;
+
+ if (res.type == RTN_LOCAL) {
+ if (!key.src)
+ key.src = key.dst;
+ dev_out = &loopback_dev;
+ key.oif = dev_out->ifindex;
+ res.fi = NULL;
+ flags |= RTCF_LOCAL;
+ goto make_route;
+ }
+
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ if (res.fi->fib_nhs > 1 && key.oif == 0)
+ fib_select_multipath(&key, &res);
+ else
+#endif
+ if (res.prefixlen==0 && res.type == RTN_UNICAST && key.oif == 0)
+ fib_select_default(&key, &res);
+
+ if (!key.src)
+ key.src = FIB_RES_PREFSRC(res);
+
+ dev_out = FIB_RES_DEV(res);
+ key.oif = dev_out->ifindex;
+
+make_route:
+ if (LOOPBACK(key.src) && !(dev_out->flags&IFF_LOOPBACK))
+ return -EINVAL;
+
+ if (key.dst == 0xFFFFFFFF)
+ res.type = RTN_BROADCAST;
+ else if (MULTICAST(key.dst))
+ res.type = RTN_MULTICAST;
+ else if (BADCLASS(key.dst) || ZERONET(key.dst))
+ return -EINVAL;
+
+ if (dev_out->flags&IFF_LOOPBACK)
+ flags |= RTCF_LOCAL;
+
+ if (res.type == RTN_BROADCAST) {
+ flags |= RTCF_BROADCAST|RTCF_LOCAL;
+ res.fi = NULL;
+ } else if (res.type == RTN_MULTICAST) {
+ flags |= RTCF_MULTICAST|RTCF_LOCAL;
+ if (!ip_check_mc(dev_out, daddr))
+ flags &= ~RTCF_LOCAL;
+ /* If multicast route do not exist use
+ default one, but do not gateway in this case.
+ Yes, it is hack.
+ */
+ if (res.fi && res.prefixlen < 4)
+ res.fi = NULL;
+ }
+
+ rth = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops);
+ if (!rth)
+ return -ENOBUFS;
+
+ atomic_set(&rth->u.dst.use, 1);
+ rth->key.dst = daddr;
+ rth->key.tos = tos;
+ rth->key.src = saddr;
+ rth->key.iif = 0;
+ rth->key.oif = oif;
+ rth->rt_dst = key.dst;
+ rth->rt_src = key.src;
+#ifdef CONFIG_IP_ROUTE_NAT
+ rth->rt_dst_map = key.dst;
+ rth->rt_src_map = key.src;
+#endif
+ rth->rt_iif = oif ? : dev_out->ifindex;
+ rth->u.dst.dev = dev_out;
+ rth->rt_gateway = key.dst;
+ rth->rt_spec_dst= key.src;
+
+ rth->u.dst.output=ip_output;
+
+ if (flags&RTCF_LOCAL) {
+ rth->u.dst.input = ip_local_deliver;
+ rth->rt_spec_dst = key.dst;
+ }
+ if (flags&(RTCF_BROADCAST|RTCF_MULTICAST)) {
+ rth->rt_spec_dst = key.src;
+ if (flags&RTCF_LOCAL && !(dev_out->flags&IFF_LOOPBACK))
+ rth->u.dst.output = ip_mc_output;
+#ifdef CONFIG_IP_MROUTE
+ if (res.type == RTN_MULTICAST && dev_out->ip_ptr) {
+ struct in_device *in_dev = dev_out->ip_ptr;
+ if (IN_DEV_MFORWARD(in_dev) && !LOCAL_MCAST(daddr)) {
+ rth->u.dst.input = ip_mr_input;
+ rth->u.dst.output = ip_mc_output;
+ }
+ }
+#endif
+ }
+
+ rt_set_nexthop(rth, &res, 0);
+
+ rth->rt_flags = flags;
+
+ hash = rt_hash_code(daddr, saddr^(oif<<5), tos);
+ return rt_intern_hash(hash, rth, rp);
+}
+
+int ip_route_output(struct rtable **rp, u32 daddr, u32 saddr, u32 tos, int oif)
+{
+ unsigned hash;
+ struct rtable *rth;
+
+ hash = rt_hash_code(daddr, saddr^(oif<<5), tos);
+
+ start_bh_atomic();
+ for (rth=rt_hash_table[hash]; rth; rth=rth->u.rt_next) {
+ if (rth->key.dst == daddr &&
+ rth->key.src == saddr &&
+ rth->key.iif == 0 &&
+ rth->key.oif == oif &&
+#ifndef CONFIG_IP_TRANSPARENT_PROXY
+ rth->key.tos == tos
+#else
+ !((rth->key.tos^tos)&(IPTOS_TOS_MASK|RTO_ONLINK)) &&
+ ((tos&RTO_TPROXY) || !(rth->rt_flags&RTCF_TPROXY))
+#endif
+ ) {
+ rth->u.dst.lastuse = jiffies;
+ atomic_inc(&rth->u.dst.use);
+ atomic_inc(&rth->u.dst.refcnt);
+ end_bh_atomic();
+ *rp = rth;
+ return 0;
+ }
+ }
+ end_bh_atomic();
+
+ return ip_route_output_slow(rp, daddr, saddr, tos, oif);
+}
+
+#ifdef CONFIG_RTNETLINK
+
+static int rt_fill_info(struct sk_buff *skb, u32 pid, u32 seq, int event, int nowait)
+{
+ struct rtable *rt = (struct rtable*)skb->dst;
+ struct rtmsg *r;
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+ struct rta_cacheinfo ci;
+#ifdef CONFIG_IP_MROUTE
+ struct rtattr *eptr;
+#endif
+ struct rtattr *mx;
+
+ nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*r));
+ r = NLMSG_DATA(nlh);
+ nlh->nlmsg_flags = (nowait && pid) ? NLM_F_MULTI : 0;
+ r->rtm_family = AF_INET;
+ r->rtm_dst_len = 32;
+ r->rtm_src_len = 0;
+ r->rtm_tos = rt->key.tos;
+ r->rtm_table = RT_TABLE_MAIN;
+ r->rtm_type = rt->rt_type;
+ r->rtm_scope = RT_SCOPE_UNIVERSE;
+ r->rtm_protocol = RTPROT_UNSPEC;
+ r->rtm_flags = (rt->rt_flags&~0xFFFF) | RTM_F_CLONED;
+ if (rt->rt_flags & RTCF_NOTIFY)
+ r->rtm_flags |= RTM_F_NOTIFY;
+ RTA_PUT(skb, RTA_DST, 4, &rt->rt_dst);
+ if (rt->key.src) {
+ r->rtm_src_len = 32;
+ RTA_PUT(skb, RTA_SRC, 4, &rt->key.src);
+ }
+ if (rt->u.dst.dev)
+ RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->u.dst.dev->ifindex);
+#ifdef CONFIG_NET_CLS_ROUTE
+ if (rt->u.dst.tclassid)
+ RTA_PUT(skb, RTA_FLOW, 4, &rt->u.dst.tclassid);
+#endif
+ if (rt->key.iif)
+ RTA_PUT(skb, RTA_PREFSRC, 4, &rt->rt_spec_dst);
+ else if (rt->rt_src != rt->key.src)
+ RTA_PUT(skb, RTA_PREFSRC, 4, &rt->rt_src);
+ if (rt->rt_dst != rt->rt_gateway)
+ RTA_PUT(skb, RTA_GATEWAY, 4, &rt->rt_gateway);
+ mx = (struct rtattr*)skb->tail;
+ RTA_PUT(skb, RTA_METRICS, 0, NULL);
+ if (rt->u.dst.mxlock)
+ RTA_PUT(skb, RTAX_LOCK, sizeof(unsigned), &rt->u.dst.mxlock);
+ if (rt->u.dst.pmtu)
+ RTA_PUT(skb, RTAX_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
+ if (rt->u.dst.window)
+ RTA_PUT(skb, RTAX_WINDOW, sizeof(unsigned), &rt->u.dst.window);
+ if (rt->u.dst.rtt)
+ RTA_PUT(skb, RTAX_RTT, sizeof(unsigned), &rt->u.dst.rtt);
+ mx->rta_len = skb->tail - (u8*)mx;
+ if (mx->rta_len == RTA_LENGTH(0))
+ skb_trim(skb, (u8*)mx - skb->data);
+ ci.rta_lastuse = jiffies - rt->u.dst.lastuse;
+ ci.rta_used = atomic_read(&rt->u.dst.refcnt);
+ ci.rta_clntref = atomic_read(&rt->u.dst.use);
+ if (rt->u.dst.expires)
+ ci.rta_expires = rt->u.dst.expires - jiffies;
+ else
+ ci.rta_expires = 0;
+ ci.rta_error = rt->u.dst.error;
+#ifdef CONFIG_IP_MROUTE
+ eptr = (struct rtattr*)skb->tail;
+#endif
+ RTA_PUT(skb, RTA_CACHEINFO, sizeof(ci), &ci);
+ if (rt->key.iif) {
+#ifdef CONFIG_IP_MROUTE
+ u32 dst = rt->rt_dst;
+
+ if (MULTICAST(dst) && !LOCAL_MCAST(dst) && ipv4_devconf.mc_forwarding) {
+ int err = ipmr_get_route(skb, r, nowait);
+ if (err <= 0) {
+ if (!nowait) {
+ if (err == 0)
+ return 0;
+ goto nlmsg_failure;
+ } else {
+ if (err == -EMSGSIZE)
+ goto nlmsg_failure;
+ ((struct rta_cacheinfo*)RTA_DATA(eptr))->rta_error = err;
+ }
+ }
+ } else
+#endif
+ {
+ RTA_PUT(skb, RTA_IIF, sizeof(int), &rt->key.iif);
+ }
+ }
+
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
+{
+ struct rtattr **rta = arg;
+ struct rtmsg *rtm = NLMSG_DATA(nlh);
+ struct rtable *rt = NULL;
+ u32 dst = 0;
+ u32 src = 0;
+ int iif = 0;
+ int err;
+ struct sk_buff *skb;
+
+ skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (skb == NULL)
+ return -ENOBUFS;
+
+ /* Reserve room for dummy headers, this skb can pass
+ through good chunk of routing engine.
+ */
+ skb->mac.raw = skb->data;
+ skb_reserve(skb, MAX_HEADER + sizeof(struct iphdr));
+
+ if (rta[RTA_SRC-1])
+ memcpy(&src, RTA_DATA(rta[RTA_SRC-1]), 4);
+ if (rta[RTA_DST-1])
+ memcpy(&dst, RTA_DATA(rta[RTA_DST-1]), 4);
+ if (rta[RTA_IIF-1])
+ memcpy(&iif, RTA_DATA(rta[RTA_IIF-1]), sizeof(int));
+
+ if (iif) {
+ struct device *dev;
+ dev = dev_get_by_index(iif);
+ if (!dev)
+ return -ENODEV;
+ skb->protocol = __constant_htons(ETH_P_IP);
+ skb->dev = dev;
+ start_bh_atomic();
+ err = ip_route_input(skb, dst, src, rtm->rtm_tos, dev);
+ end_bh_atomic();
+ rt = (struct rtable*)skb->dst;
+ if (!err && rt->u.dst.error)
+ err = -rt->u.dst.error;
+ } else {
+ int oif = 0;
+ if (rta[RTA_OIF-1])
+ memcpy(&oif, RTA_DATA(rta[RTA_OIF-1]), sizeof(int));
+ err = ip_route_output(&rt, dst, src, rtm->rtm_tos, oif);
+ }
+ if (err) {
+ kfree_skb(skb);
+ return err;
+ }
+
+ skb->dst = &rt->u.dst;
+ if (rtm->rtm_flags & RTM_F_NOTIFY)
+ rt->rt_flags |= RTCF_NOTIFY;
+
+ NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid;
+
+ err = rt_fill_info(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, RTM_NEWROUTE, 0);
+ if (err == 0)
+ return 0;
+ if (err < 0)
+ return -EMSGSIZE;
+
+ err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+
+int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct rtable *rt;
+ int h, s_h;
+ int idx, s_idx;
+
+ s_h = cb->args[0];
+ s_idx = idx = cb->args[1];
+ for (h=0; h < RT_HASH_DIVISOR; h++) {
+ if (h < s_h) continue;
+ if (h > s_h)
+ s_idx = 0;
+ start_bh_atomic();
+ for (rt = rt_hash_table[h], idx = 0; rt; rt = rt->u.rt_next, idx++) {
+ if (idx < s_idx)
+ continue;
+ skb->dst = dst_clone(&rt->u.dst);
+ if (rt_fill_info(skb, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, RTM_NEWROUTE, 1) <= 0) {
+ dst_release(xchg(&skb->dst, NULL));
+ end_bh_atomic();
+ goto done;
+ }
+ dst_release(xchg(&skb->dst, NULL));
+ }
+ end_bh_atomic();
+ }
+
+done:
+ cb->args[0] = h;
+ cb->args[1] = idx;
+ return skb->len;
+}
+
+#endif /* CONFIG_RTNETLINK */
+
+void ip_rt_multicast_event(struct in_device *in_dev)
+{
+ rt_cache_flush(0);
+}
+
+
+
+#ifdef CONFIG_SYSCTL
+
+static int flush_delay;
+
+static
+int ipv4_sysctl_rtcache_flush(ctl_table *ctl, int write, struct file * filp,
+ void *buffer, size_t *lenp)
+{
+ if (write) {
+ proc_dointvec(ctl, write, filp, buffer, lenp);
+ rt_cache_flush(flush_delay);
+ return 0;
+ } else
+ return -EINVAL;
+}
+
+static int ipv4_sysctl_rtcache_flush_strategy(ctl_table *table, int *name, int nlen,
+ void *oldval, size_t *oldlenp,
+ void *newval, size_t newlen,
+ void **context)
+{
+ int delay;
+ if (newlen != sizeof(int))
+ return -EINVAL;
+ if (get_user(delay,(int *)newval))
+ return -EFAULT;
+ rt_cache_flush(delay);
+ return 0;
+}
+
+ctl_table ipv4_route_table[] = {
+ {NET_IPV4_ROUTE_FLUSH, "flush",
+ &flush_delay, sizeof(int), 0644, NULL,
+ &ipv4_sysctl_rtcache_flush, &ipv4_sysctl_rtcache_flush_strategy },
+ {NET_IPV4_ROUTE_MIN_DELAY, "min_delay",
+ &ip_rt_min_delay, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies, &sysctl_jiffies},
+ {NET_IPV4_ROUTE_MAX_DELAY, "max_delay",
+ &ip_rt_max_delay, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies, &sysctl_jiffies},
+ {NET_IPV4_ROUTE_GC_THRESH, "gc_thresh",
+ &ipv4_dst_ops.gc_thresh, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_ROUTE_MAX_SIZE, "max_size",
+ &ip_rt_max_size, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_ROUTE_GC_MIN_INTERVAL, "gc_min_interval",
+ &ip_rt_gc_min_interval, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies, &sysctl_jiffies},
+ {NET_IPV4_ROUTE_GC_TIMEOUT, "gc_timeout",
+ &ip_rt_gc_timeout, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies, &sysctl_jiffies},
+ {NET_IPV4_ROUTE_GC_INTERVAL, "gc_interval",
+ &ip_rt_gc_interval, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies, &sysctl_jiffies},
+ {NET_IPV4_ROUTE_REDIRECT_LOAD, "redirect_load",
+ &ip_rt_redirect_load, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_ROUTE_REDIRECT_NUMBER, "redirect_number",
+ &ip_rt_redirect_number, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_ROUTE_REDIRECT_SILENCE, "redirect_silence",
+ &ip_rt_redirect_silence, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_ROUTE_ERROR_COST, "error_cost",
+ &ip_rt_error_cost, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_ROUTE_ERROR_BURST, "error_burst",
+ &ip_rt_error_burst, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_ROUTE_GC_ELASTICITY, "gc_elasticity",
+ &ip_rt_gc_elasticity, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_ROUTE_MTU_EXPIRES, "mtu_expires",
+ &ip_rt_mtu_expires, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies, &sysctl_jiffies},
+ {0}
+};
+#endif
+
+#ifdef CONFIG_NET_CLS_ROUTE
+struct ip_rt_acct ip_rt_acct[256];
+
+#ifdef CONFIG_PROC_FS
+static int ip_rt_acct_read(char *buffer, char **start, off_t offset,
+ int length, int *eof, void *data)
+{
+ *start=buffer;
+
+ if (offset + length > sizeof(ip_rt_acct)) {
+ length = sizeof(ip_rt_acct) - offset;
+ *eof = 1;
+ }
+ if (length > 0) {
+ start_bh_atomic();
+ memcpy(buffer, ((u8*)&ip_rt_acct)+offset, length);
+ end_bh_atomic();
+ return length;
+ }
+ return 0;
+}
+#endif
+#endif
+
+
+__initfunc(void ip_rt_init(void))
+{
+#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_NET_CLS_ROUTE
+ struct proc_dir_entry *ent;
+#endif
+#endif
+ devinet_init();
+ ip_fib_init();
+ rt_periodic_timer.function = rt_check_expire;
+ /* All the timers, started at system startup tend
+ to synchronize. Perturb it a bit.
+ */
+ rt_periodic_timer.expires = jiffies + net_random()%ip_rt_gc_interval
+ + ip_rt_gc_interval;
+ add_timer(&rt_periodic_timer);
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&(struct proc_dir_entry) {
+ PROC_NET_RTCACHE, 8, "rt_cache",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rt_cache_get_info
+ });
+#ifdef CONFIG_NET_CLS_ROUTE
+ ent = create_proc_entry("net/rt_acct", 0, 0);
+ ent->read_proc = ip_rt_acct_read;
+#endif
+#endif
+}
diff --git a/pfinet/linux-src/net/ipv4/syncookies.c b/pfinet/linux-src/net/ipv4/syncookies.c
new file mode 100644
index 00000000..62884aae
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/syncookies.c
@@ -0,0 +1,203 @@
+/*
+ * Syncookies implementation for the Linux kernel
+ *
+ * Copyright (C) 1997 Andi Kleen
+ * Based on ideas by D.J.Bernstein and Eric Schenk.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * $Id: syncookies.c,v 1.7.2.3 1999/12/07 03:11:07 davem Exp $
+ *
+ * Missing: IPv6 support.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_SYN_COOKIES)
+#include <linux/tcp.h>
+#include <linux/malloc.h>
+#include <linux/random.h>
+#include <net/tcp.h>
+
+extern int sysctl_tcp_syncookies;
+
+static unsigned long tcp_lastsynq_overflow;
+
+/*
+ * This table has to be sorted and terminated with (__u16)-1.
+ * XXX generate a better table.
+ * Unresolved Issues: HIPPI with a 64k MSS is not well supported.
+ */
+static __u16 const msstab[] = {
+ 64-1,
+ 256-1,
+ 512-1,
+ 536-1,
+ 1024-1,
+ 1440-1,
+ 1460-1,
+ 4312-1,
+ (__u16)-1
+};
+/* The number doesn't include the -1 terminator */
+#define NUM_MSS (sizeof(msstab)/sizeof(msstab[0]) - 1)
+
+/*
+ * Generate a syncookie. mssp points to the mss, which is returned
+ * rounded down to the value encoded in the cookie.
+ */
+__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb,
+ __u16 *mssp)
+{
+ int mssind;
+ const __u16 mss = *mssp;
+
+ tcp_lastsynq_overflow = jiffies;
+ /* XXX sort msstab[] by probability? Binary search? */
+ for (mssind = 0; mss > msstab[mssind+1]; mssind++)
+ ;
+ *mssp = msstab[mssind]+1;
+
+ net_statistics.SyncookiesSent++;
+
+ return secure_tcp_syn_cookie(skb->nh.iph->saddr, skb->nh.iph->daddr,
+ skb->h.th->source, skb->h.th->dest,
+ ntohl(skb->h.th->seq),
+ jiffies / (HZ*60), mssind);
+}
+
+/*
+ * This (misnamed) value is the age of syncookie which is permitted.
+ * Its ideal value should be dependent on TCP_TIMEOUT_INIT and
+ * sysctl_tcp_retries1. It's a rather complicated formula (exponential
+ * backoff) to compute at runtime so it's currently hardcoded here.
+ */
+#define COUNTER_TRIES 4
+/*
+ * Check if a ack sequence number is a valid syncookie.
+ * Return the decoded mss if it is, or 0 if not.
+ */
+static inline int cookie_check(struct sk_buff *skb, __u32 cookie)
+{
+ __u32 seq;
+ __u32 mssind;
+
+ if ((jiffies - tcp_lastsynq_overflow) > TCP_TIMEOUT_INIT)
+ return 0;
+
+ seq = ntohl(skb->h.th->seq)-1;
+ mssind = check_tcp_syn_cookie(cookie,
+ skb->nh.iph->saddr, skb->nh.iph->daddr,
+ skb->h.th->source, skb->h.th->dest,
+ seq, jiffies/(HZ*60), COUNTER_TRIES);
+
+ return mssind < NUM_MSS ? msstab[mssind]+1 : 0;
+}
+
+extern struct or_calltable or_ipv4;
+
+static inline struct sock *
+get_cookie_sock(struct sock *sk, struct sk_buff *skb, struct open_request *req,
+ struct dst_entry *dst)
+{
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+
+ sk = tp->af_specific->syn_recv_sock(sk, skb, req, dst);
+ req->sk = sk;
+
+ /* Queue up for accept() */
+ tcp_synq_queue(tp, req);
+
+ return sk;
+}
+
+struct sock *
+cookie_v4_check(struct sock *sk, struct sk_buff *skb, struct ip_options *opt)
+{
+ __u32 cookie = ntohl(skb->h.th->ack_seq)-1;
+ struct open_request *req;
+ int mss;
+ struct rtable *rt;
+ __u8 rcv_wscale;
+
+ if (!sysctl_tcp_syncookies)
+ return sk;
+ if (!skb->h.th->ack)
+ return sk;
+
+ mss = cookie_check(skb, cookie);
+ if (mss == 0) {
+ net_statistics.SyncookiesFailed++;
+ return sk;
+ }
+
+ net_statistics.SyncookiesRecv++;
+
+ req = tcp_openreq_alloc();
+ if (req == NULL)
+ return NULL;
+
+ req->rcv_isn = htonl(skb->h.th->seq)-1;
+ req->snt_isn = cookie;
+ req->mss = mss;
+ req->rmt_port = skb->h.th->source;
+ req->af.v4_req.loc_addr = skb->nh.iph->daddr;
+ req->af.v4_req.rmt_addr = skb->nh.iph->saddr;
+ req->class = &or_ipv4; /* for safety */
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ req->lcl_port = skb->h.th->dest;
+#endif
+
+ req->af.v4_req.opt = NULL;
+
+ /* We throwed the options of the initial SYN away, so we hope
+ * the ACK carries the same options again (see RFC1122 4.2.3.8)
+ */
+ if (opt && opt->optlen) {
+ int opt_size = sizeof(struct ip_options) + opt->optlen;
+
+ req->af.v4_req.opt = kmalloc(opt_size, GFP_ATOMIC);
+ if (req->af.v4_req.opt) {
+ if (ip_options_echo(req->af.v4_req.opt, skb)) {
+ kfree_s(req->af.v4_req.opt, opt_size);
+ req->af.v4_req.opt = NULL;
+ }
+ }
+ }
+
+ req->snd_wscale = req->rcv_wscale = req->tstamp_ok = 0;
+ req->wscale_ok = 0;
+ req->expires = 0UL;
+ req->retrans = 0;
+
+ /*
+ * We need to lookup the route here to get at the correct
+ * window size. We should better make sure that the window size
+ * hasn't changed since we received the original syn, but I see
+ * no easy way to do this.
+ */
+ if (ip_route_output(&rt,
+ opt &&
+ opt->srr ? opt->faddr : req->af.v4_req.rmt_addr,
+ req->af.v4_req.loc_addr,
+ sk->ip_tos | RTO_CONN,
+ 0)) {
+ if (req->af.v4_req.opt)
+ kfree(req->af.v4_req.opt);
+ tcp_openreq_free(req);
+ return NULL;
+ }
+
+ /* Try to redo what tcp_v4_send_synack did. */
+ req->window_clamp = rt->u.dst.window;
+ tcp_select_initial_window(sock_rspace(sk)/2,req->mss,
+ &req->rcv_wnd, &req->window_clamp,
+ 0, &rcv_wscale);
+ req->rcv_wscale = rcv_wscale;
+
+ return get_cookie_sock(sk, skb, req, &rt->u.dst);
+}
+
+#endif
diff --git a/pfinet/linux-src/net/ipv4/sysctl_net_ipv4.c b/pfinet/linux-src/net/ipv4/sysctl_net_ipv4.c
new file mode 100644
index 00000000..235b36d1
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/sysctl_net_ipv4.c
@@ -0,0 +1,210 @@
+/*
+ * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem.
+ *
+ * $Id: sysctl_net_ipv4.c,v 1.38.2.2 1999/09/22 16:33:30 davem Exp $
+ *
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS]
+ */
+
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+#include <linux/config.h>
+#include <net/snmp.h>
+#include <net/ip.h>
+#include <net/route.h>
+#include <net/tcp.h>
+
+/*
+ * TCP configuration parameters
+ */
+
+#define TCP_PMTU_DISC 0x00000001 /* perform PMTU discovery */
+#define TCP_CONG_AVOID 0x00000002 /* congestion avoidance algorithm */
+#define TCP_DELAY_ACKS 0x00000003 /* delayed ack stategy */
+
+#if 0
+static int boolean_min = 0;
+static int boolean_max = 1;
+#endif
+
+/* From icmp.c */
+extern int sysctl_icmp_echo_ignore_all;
+extern int sysctl_icmp_echo_ignore_broadcasts;
+extern int sysctl_icmp_ignore_bogus_error_responses;
+
+/* From ip_fragment.c */
+extern int sysctl_ipfrag_low_thresh;
+extern int sysctl_ipfrag_high_thresh;
+extern int sysctl_ipfrag_time;
+
+/* From ip_output.c */
+extern int sysctl_ip_dynaddr;
+
+/* From ip_input.c */
+extern int sysctl_ip_always_defrag;
+
+/* From ip_masq.c */
+extern int sysctl_ip_masq_debug;
+
+extern int sysctl_tcp_timestamps;
+extern int sysctl_tcp_window_scaling;
+extern int sysctl_tcp_sack;
+extern int sysctl_tcp_retrans_collapse;
+extern int sysctl_tcp_keepalive_time;
+extern int sysctl_tcp_keepalive_probes;
+extern int sysctl_tcp_max_ka_probes;
+extern int sysctl_tcp_retries1;
+extern int sysctl_tcp_retries2;
+extern int sysctl_tcp_fin_timeout;
+extern int sysctl_tcp_syncookies;
+extern int sysctl_tcp_syn_retries;
+extern int sysctl_tcp_stdurg;
+extern int sysctl_tcp_rfc1337;
+extern int sysctl_tcp_syn_taildrop;
+extern int sysctl_max_syn_backlog;
+
+/* From icmp.c */
+extern int sysctl_icmp_destunreach_time;
+extern int sysctl_icmp_timeexceed_time;
+extern int sysctl_icmp_paramprob_time;
+extern int sysctl_icmp_echoreply_time;
+
+/* From igmp.c */
+extern int sysctl_igmp_max_memberships;
+
+int tcp_retr1_max = 255;
+
+struct ipv4_config ipv4_config;
+
+extern ctl_table ipv4_route_table[];
+
+#ifdef CONFIG_SYSCTL
+
+static
+int ipv4_sysctl_forward(ctl_table *ctl, int write, struct file * filp,
+ void *buffer, size_t *lenp)
+{
+ int val = ipv4_devconf.forwarding;
+ int ret;
+
+ ret = proc_dointvec(ctl, write, filp, buffer, lenp);
+
+ if (write && ipv4_devconf.forwarding != val)
+ inet_forward_change();
+
+ return ret;
+}
+
+static int ipv4_sysctl_forward_strategy(ctl_table *table, int *name, int nlen,
+ void *oldval, size_t *oldlenp,
+ void *newval, size_t newlen,
+ void **context)
+{
+ int new;
+ if (newlen != sizeof(int))
+ return -EINVAL;
+ if (get_user(new,(int *)newval))
+ return -EFAULT;
+ if (new != ipv4_devconf.forwarding)
+ inet_forward_change();
+ return 0; /* caller does change again and handles handles oldval */
+}
+
+ctl_table ipv4_table[] = {
+ {NET_IPV4_TCP_TIMESTAMPS, "tcp_timestamps",
+ &sysctl_tcp_timestamps, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_TCP_WINDOW_SCALING, "tcp_window_scaling",
+ &sysctl_tcp_window_scaling, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_TCP_SACK, "tcp_sack",
+ &sysctl_tcp_sack, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_TCP_RETRANS_COLLAPSE, "tcp_retrans_collapse",
+ &sysctl_tcp_retrans_collapse, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_FORWARD, "ip_forward",
+ &ipv4_devconf.forwarding, sizeof(int), 0644, NULL,
+ &ipv4_sysctl_forward,&ipv4_sysctl_forward_strategy},
+ {NET_IPV4_DEFAULT_TTL, "ip_default_ttl",
+ &ip_statistics.IpDefaultTTL, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_AUTOCONFIG, "ip_autoconfig",
+ &ipv4_config.autoconfig, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_NO_PMTU_DISC, "ip_no_pmtu_disc",
+ &ipv4_config.no_pmtu_disc, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_TCP_SYN_RETRIES, "tcp_syn_retries",
+ &sysctl_tcp_syn_retries, sizeof(int), 0644, NULL, &proc_dointvec},
+ {NET_IPV4_IPFRAG_HIGH_THRESH, "ipfrag_high_thresh",
+ &sysctl_ipfrag_high_thresh, sizeof(int), 0644, NULL, &proc_dointvec},
+ {NET_IPV4_IPFRAG_LOW_THRESH, "ipfrag_low_thresh",
+ &sysctl_ipfrag_low_thresh, sizeof(int), 0644, NULL, &proc_dointvec},
+ {NET_IPV4_DYNADDR, "ip_dynaddr",
+ &sysctl_ip_dynaddr, sizeof(int), 0644, NULL, &proc_dointvec},
+ {NET_IPV4_ALWAYS_DEFRAG, "ip_always_defrag",
+ &sysctl_ip_always_defrag, sizeof(int), 0644, NULL, &proc_dointvec},
+#ifdef CONFIG_IP_MASQUERADE
+ {NET_IPV4_IP_MASQ_DEBUG, "ip_masq_debug",
+ &sysctl_ip_masq_debug, sizeof(int), 0644, NULL, &proc_dointvec},
+#endif
+ {NET_IPV4_IPFRAG_TIME, "ipfrag_time",
+ &sysctl_ipfrag_time, sizeof(int), 0644, NULL, &proc_dointvec_jiffies,
+ &sysctl_jiffies},
+ {NET_IPV4_TCP_MAX_KA_PROBES, "tcp_max_ka_probes",
+ &sysctl_tcp_max_ka_probes, sizeof(int), 0644, NULL, &proc_dointvec},
+ {NET_IPV4_TCP_KEEPALIVE_TIME, "tcp_keepalive_time",
+ &sysctl_tcp_keepalive_time, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies, &sysctl_jiffies},
+ {NET_IPV4_TCP_KEEPALIVE_PROBES, "tcp_keepalive_probes",
+ &sysctl_tcp_keepalive_probes, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_TCP_RETRIES1, "tcp_retries1",
+ &sysctl_tcp_retries1, sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL, NULL, &tcp_retr1_max},
+ {NET_IPV4_TCP_RETRIES2, "tcp_retries2",
+ &sysctl_tcp_retries2, sizeof(int), 0644, NULL, &proc_dointvec},
+ {NET_IPV4_TCP_FIN_TIMEOUT, "tcp_fin_timeout",
+ &sysctl_tcp_fin_timeout, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies, &sysctl_jiffies},
+#ifdef CONFIG_SYN_COOKIES
+ {NET_TCP_SYNCOOKIES, "tcp_syncookies",
+ &sysctl_tcp_syncookies, sizeof(int), 0644, NULL, &proc_dointvec},
+#endif
+ {NET_TCP_STDURG, "tcp_stdurg", &sysctl_tcp_stdurg,
+ sizeof(int), 0644, NULL, &proc_dointvec},
+ {NET_TCP_RFC1337, "tcp_rfc1337", &sysctl_tcp_rfc1337,
+ sizeof(int), 0644, NULL, &proc_dointvec},
+ {NET_TCP_MAX_SYN_BACKLOG, "tcp_max_syn_backlog", &sysctl_max_syn_backlog,
+ sizeof(int), 0644, NULL, &proc_dointvec},
+ {NET_IPV4_LOCAL_PORT_RANGE, "ip_local_port_range",
+ &sysctl_local_port_range, sizeof(sysctl_local_port_range), 0644,
+ NULL, &proc_dointvec},
+ {NET_IPV4_ICMP_ECHO_IGNORE_ALL, "icmp_echo_ignore_all",
+ &sysctl_icmp_echo_ignore_all, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_ICMP_ECHO_IGNORE_BROADCASTS, "icmp_echo_ignore_broadcasts",
+ &sysctl_icmp_echo_ignore_broadcasts, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_ICMP_IGNORE_BOGUS_ERROR_RESPONSES, "icmp_ignore_bogus_error_responses",
+ &sysctl_icmp_ignore_bogus_error_responses, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_ICMP_DESTUNREACH_RATE, "icmp_destunreach_rate",
+ &sysctl_icmp_destunreach_time, sizeof(int), 0644, NULL, &proc_dointvec},
+ {NET_IPV4_ICMP_TIMEEXCEED_RATE, "icmp_timeexceed_rate",
+ &sysctl_icmp_timeexceed_time, sizeof(int), 0644, NULL, &proc_dointvec},
+ {NET_IPV4_ICMP_PARAMPROB_RATE, "icmp_paramprob_rate",
+ &sysctl_icmp_paramprob_time, sizeof(int), 0644, NULL, &proc_dointvec},
+ {NET_IPV4_ICMP_ECHOREPLY_RATE, "icmp_echoreply_rate",
+ &sysctl_icmp_echoreply_time, sizeof(int), 0644, NULL, &proc_dointvec},
+ {NET_IPV4_ROUTE, "route", NULL, 0, 0555, ipv4_route_table},
+#ifdef CONFIG_IP_MULTICAST
+ {NET_IPV4_IGMP_MAX_MEMBERSHIPS, "igmp_max_memberships",
+ &sysctl_igmp_max_memberships, sizeof(int), 0644, NULL, &proc_dointvec},
+#endif
+ {0}
+};
+
+#endif /* CONFIG_SYSCTL */
diff --git a/pfinet/linux-src/net/ipv4/tcp.c b/pfinet/linux-src/net/ipv4/tcp.c
new file mode 100644
index 00000000..8cde3854
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/tcp.c
@@ -0,0 +1,1886 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Implementation of the Transmission Control Protocol(TCP).
+ *
+ * Version: $Id: tcp.c,v 1.140.2.5 1999/09/23 19:21:16 davem Exp $
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ * Corey Minyard <wf-rch!minyard@relay.EU.net>
+ * Florian La Roche, <flla@stud.uni-sb.de>
+ * Charles Hedrick, <hedrick@klinzhai.rutgers.edu>
+ * Linus Torvalds, <torvalds@cs.helsinki.fi>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ * Matthew Dillon, <dillon@apollo.west.oic.com>
+ * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ * Jorge Cwik, <jorge@laser.satlink.net>
+ *
+ * Fixes:
+ * Alan Cox : Numerous verify_area() calls
+ * Alan Cox : Set the ACK bit on a reset
+ * Alan Cox : Stopped it crashing if it closed while
+ * sk->inuse=1 and was trying to connect
+ * (tcp_err()).
+ * Alan Cox : All icmp error handling was broken
+ * pointers passed where wrong and the
+ * socket was looked up backwards. Nobody
+ * tested any icmp error code obviously.
+ * Alan Cox : tcp_err() now handled properly. It
+ * wakes people on errors. poll
+ * behaves and the icmp error race
+ * has gone by moving it into sock.c
+ * Alan Cox : tcp_send_reset() fixed to work for
+ * everything not just packets for
+ * unknown sockets.
+ * Alan Cox : tcp option processing.
+ * Alan Cox : Reset tweaked (still not 100%) [Had
+ * syn rule wrong]
+ * Herp Rosmanith : More reset fixes
+ * Alan Cox : No longer acks invalid rst frames.
+ * Acking any kind of RST is right out.
+ * Alan Cox : Sets an ignore me flag on an rst
+ * receive otherwise odd bits of prattle
+ * escape still
+ * Alan Cox : Fixed another acking RST frame bug.
+ * Should stop LAN workplace lockups.
+ * Alan Cox : Some tidyups using the new skb list
+ * facilities
+ * Alan Cox : sk->keepopen now seems to work
+ * Alan Cox : Pulls options out correctly on accepts
+ * Alan Cox : Fixed assorted sk->rqueue->next errors
+ * Alan Cox : PSH doesn't end a TCP read. Switched a
+ * bit to skb ops.
+ * Alan Cox : Tidied tcp_data to avoid a potential
+ * nasty.
+ * Alan Cox : Added some better commenting, as the
+ * tcp is hard to follow
+ * Alan Cox : Removed incorrect check for 20 * psh
+ * Michael O'Reilly : ack < copied bug fix.
+ * Johannes Stille : Misc tcp fixes (not all in yet).
+ * Alan Cox : FIN with no memory -> CRASH
+ * Alan Cox : Added socket option proto entries.
+ * Also added awareness of them to accept.
+ * Alan Cox : Added TCP options (SOL_TCP)
+ * Alan Cox : Switched wakeup calls to callbacks,
+ * so the kernel can layer network
+ * sockets.
+ * Alan Cox : Use ip_tos/ip_ttl settings.
+ * Alan Cox : Handle FIN (more) properly (we hope).
+ * Alan Cox : RST frames sent on unsynchronised
+ * state ack error.
+ * Alan Cox : Put in missing check for SYN bit.
+ * Alan Cox : Added tcp_select_window() aka NET2E
+ * window non shrink trick.
+ * Alan Cox : Added a couple of small NET2E timer
+ * fixes
+ * Charles Hedrick : TCP fixes
+ * Toomas Tamm : TCP window fixes
+ * Alan Cox : Small URG fix to rlogin ^C ack fight
+ * Charles Hedrick : Rewrote most of it to actually work
+ * Linus : Rewrote tcp_read() and URG handling
+ * completely
+ * Gerhard Koerting: Fixed some missing timer handling
+ * Matthew Dillon : Reworked TCP machine states as per RFC
+ * Gerhard Koerting: PC/TCP workarounds
+ * Adam Caldwell : Assorted timer/timing errors
+ * Matthew Dillon : Fixed another RST bug
+ * Alan Cox : Move to kernel side addressing changes.
+ * Alan Cox : Beginning work on TCP fastpathing
+ * (not yet usable)
+ * Arnt Gulbrandsen: Turbocharged tcp_check() routine.
+ * Alan Cox : TCP fast path debugging
+ * Alan Cox : Window clamping
+ * Michael Riepe : Bug in tcp_check()
+ * Matt Dillon : More TCP improvements and RST bug fixes
+ * Matt Dillon : Yet more small nasties remove from the
+ * TCP code (Be very nice to this man if
+ * tcp finally works 100%) 8)
+ * Alan Cox : BSD accept semantics.
+ * Alan Cox : Reset on closedown bug.
+ * Peter De Schrijver : ENOTCONN check missing in tcp_sendto().
+ * Michael Pall : Handle poll() after URG properly in
+ * all cases.
+ * Michael Pall : Undo the last fix in tcp_read_urg()
+ * (multi URG PUSH broke rlogin).
+ * Michael Pall : Fix the multi URG PUSH problem in
+ * tcp_readable(), poll() after URG
+ * works now.
+ * Michael Pall : recv(...,MSG_OOB) never blocks in the
+ * BSD api.
+ * Alan Cox : Changed the semantics of sk->socket to
+ * fix a race and a signal problem with
+ * accept() and async I/O.
+ * Alan Cox : Relaxed the rules on tcp_sendto().
+ * Yury Shevchuk : Really fixed accept() blocking problem.
+ * Craig I. Hagan : Allow for BSD compatible TIME_WAIT for
+ * clients/servers which listen in on
+ * fixed ports.
+ * Alan Cox : Cleaned the above up and shrank it to
+ * a sensible code size.
+ * Alan Cox : Self connect lockup fix.
+ * Alan Cox : No connect to multicast.
+ * Ross Biro : Close unaccepted children on master
+ * socket close.
+ * Alan Cox : Reset tracing code.
+ * Alan Cox : Spurious resets on shutdown.
+ * Alan Cox : Giant 15 minute/60 second timer error
+ * Alan Cox : Small whoops in polling before an
+ * accept.
+ * Alan Cox : Kept the state trace facility since
+ * it's handy for debugging.
+ * Alan Cox : More reset handler fixes.
+ * Alan Cox : Started rewriting the code based on
+ * the RFC's for other useful protocol
+ * references see: Comer, KA9Q NOS, and
+ * for a reference on the difference
+ * between specifications and how BSD
+ * works see the 4.4lite source.
+ * A.N.Kuznetsov : Don't time wait on completion of tidy
+ * close.
+ * Linus Torvalds : Fin/Shutdown & copied_seq changes.
+ * Linus Torvalds : Fixed BSD port reuse to work first syn
+ * Alan Cox : Reimplemented timers as per the RFC
+ * and using multiple timers for sanity.
+ * Alan Cox : Small bug fixes, and a lot of new
+ * comments.
+ * Alan Cox : Fixed dual reader crash by locking
+ * the buffers (much like datagram.c)
+ * Alan Cox : Fixed stuck sockets in probe. A probe
+ * now gets fed up of retrying without
+ * (even a no space) answer.
+ * Alan Cox : Extracted closing code better
+ * Alan Cox : Fixed the closing state machine to
+ * resemble the RFC.
+ * Alan Cox : More 'per spec' fixes.
+ * Jorge Cwik : Even faster checksumming.
+ * Alan Cox : tcp_data() doesn't ack illegal PSH
+ * only frames. At least one pc tcp stack
+ * generates them.
+ * Alan Cox : Cache last socket.
+ * Alan Cox : Per route irtt.
+ * Matt Day : poll()->select() match BSD precisely on error
+ * Alan Cox : New buffers
+ * Marc Tamsky : Various sk->prot->retransmits and
+ * sk->retransmits misupdating fixed.
+ * Fixed tcp_write_timeout: stuck close,
+ * and TCP syn retries gets used now.
+ * Mark Yarvis : In tcp_read_wakeup(), don't send an
+ * ack if state is TCP_CLOSED.
+ * Alan Cox : Look up device on a retransmit - routes may
+ * change. Doesn't yet cope with MSS shrink right
+ * but its a start!
+ * Marc Tamsky : Closing in closing fixes.
+ * Mike Shaver : RFC1122 verifications.
+ * Alan Cox : rcv_saddr errors.
+ * Alan Cox : Block double connect().
+ * Alan Cox : Small hooks for enSKIP.
+ * Alexey Kuznetsov: Path MTU discovery.
+ * Alan Cox : Support soft errors.
+ * Alan Cox : Fix MTU discovery pathological case
+ * when the remote claims no mtu!
+ * Marc Tamsky : TCP_CLOSE fix.
+ * Colin (G3TNE) : Send a reset on syn ack replies in
+ * window but wrong (fixes NT lpd problems)
+ * Pedro Roque : Better TCP window handling, delayed ack.
+ * Joerg Reuter : No modification of locked buffers in
+ * tcp_do_retransmit()
+ * Eric Schenk : Changed receiver side silly window
+ * avoidance algorithm to BSD style
+ * algorithm. This doubles throughput
+ * against machines running Solaris,
+ * and seems to result in general
+ * improvement.
+ * Stefan Magdalinski : adjusted tcp_readable() to fix FIONREAD
+ * Willy Konynenberg : Transparent proxying support.
+ * Mike McLagan : Routing by source
+ * Keith Owens : Do proper merging with partial SKB's in
+ * tcp_do_sendmsg to avoid burstiness.
+ * Eric Schenk : Fix fast close down bug with
+ * shutdown() followed by close().
+ * Andi Kleen : Make poll agree with SIGIO
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or(at your option) any later version.
+ *
+ * Description of States:
+ *
+ * TCP_SYN_SENT sent a connection request, waiting for ack
+ *
+ * TCP_SYN_RECV received a connection request, sent ack,
+ * waiting for final ack in three-way handshake.
+ *
+ * TCP_ESTABLISHED connection established
+ *
+ * TCP_FIN_WAIT1 our side has shutdown, waiting to complete
+ * transmission of remaining buffered data
+ *
+ * TCP_FIN_WAIT2 all buffered data sent, waiting for remote
+ * to shutdown
+ *
+ * TCP_CLOSING both sides have shutdown but we still have
+ * data we have to finish sending
+ *
+ * TCP_TIME_WAIT timeout to catch resent junk before entering
+ * closed, can only be entered from FIN_WAIT2
+ * or CLOSING. Required because the other end
+ * may not have gotten our last ACK causing it
+ * to retransmit the data packet (which we ignore)
+ *
+ * TCP_CLOSE_WAIT remote side has shutdown and is waiting for
+ * us to finish writing our data and to shutdown
+ * (we have to close() to move on to LAST_ACK)
+ *
+ * TCP_LAST_ACK out side has shutdown after remote has
+ * shutdown. There may still be data in our
+ * buffer that we have to finish sending
+ *
+ * TCP_CLOSE socket is finished
+ */
+
+/*
+ * RFC1122 status:
+ * NOTE: I'm not going to be doing comments in the code for this one except
+ * for violations and the like. tcp.c is just too big... If I say something
+ * "does?" or "doesn't?", it means I'm not sure, and will have to hash it out
+ * with Alan. -- MS 950903
+ * [Note: Most of the TCP code has been rewriten/redesigned since this
+ * RFC1122 check. It is probably not correct anymore. It should be redone
+ * before 2.2. -AK]
+ *
+ * Use of PSH (4.2.2.2)
+ * MAY aggregate data sent without the PSH flag. (does)
+ * MAY queue data received without the PSH flag. (does)
+ * SHOULD collapse successive PSH flags when it packetizes data. (doesn't)
+ * MAY implement PSH on send calls. (doesn't, thus:)
+ * MUST NOT buffer data indefinitely (doesn't [1 second])
+ * MUST set PSH on last segment (does)
+ * MAY pass received PSH to application layer (doesn't)
+ * SHOULD send maximum-sized segment whenever possible. (almost always does)
+ *
+ * Window Size (4.2.2.3, 4.2.2.16)
+ * MUST treat window size as an unsigned number (does)
+ * SHOULD treat window size as a 32-bit number (does not)
+ * MUST NOT shrink window once it is offered (does not normally)
+ *
+ * Urgent Pointer (4.2.2.4)
+ * **MUST point urgent pointer to last byte of urgent data (not right
+ * after). (doesn't, to be like BSD. That's configurable, but defaults
+ * to off)
+ * MUST inform application layer asynchronously of incoming urgent
+ * data. (does)
+ * MUST provide application with means of determining the amount of
+ * urgent data pending. (does)
+ * **MUST support urgent data sequence of arbitrary length. (doesn't, but
+ * it's sort of tricky to fix, as urg_ptr is a 16-bit quantity)
+ * [Follows BSD 1 byte of urgent data]
+ *
+ * TCP Options (4.2.2.5)
+ * MUST be able to receive TCP options in any segment. (does)
+ * MUST ignore unsupported options (does)
+ *
+ * Maximum Segment Size Option (4.2.2.6)
+ * MUST implement both sending and receiving MSS. (does, but currently
+ * only uses the smaller of both of them)
+ * SHOULD send an MSS with every SYN where receive MSS != 536 (MAY send
+ * it always). (does, even when MSS == 536, which is legal)
+ * MUST assume MSS == 536 if no MSS received at connection setup (does)
+ * MUST calculate "effective send MSS" correctly:
+ * min(physical_MTU, remote_MSS+20) - sizeof(tcphdr) - sizeof(ipopts)
+ * (does - but allows operator override)
+ *
+ * TCP Checksum (4.2.2.7)
+ * MUST generate and check TCP checksum. (does)
+ *
+ * Initial Sequence Number Selection (4.2.2.8)
+ * MUST use the RFC 793 clock selection mechanism. (doesn't, but it's
+ * OK: RFC 793 specifies a 250KHz clock, while we use 1MHz, which is
+ * necessary for 10Mbps networks - and harder than BSD to spoof!
+ * With syncookies we don't)
+ *
+ * Simultaneous Open Attempts (4.2.2.10)
+ * MUST support simultaneous open attempts (does)
+ *
+ * Recovery from Old Duplicate SYN (4.2.2.11)
+ * MUST keep track of active vs. passive open (does)
+ *
+ * RST segment (4.2.2.12)
+ * SHOULD allow an RST segment to contain data (does, but doesn't do
+ * anything with it, which is standard)
+ *
+ * Closing a Connection (4.2.2.13)
+ * MUST inform application of whether connection was closed by RST or
+ * normal close. (does)
+ * MAY allow "half-duplex" close (treat connection as closed for the
+ * local app, even before handshake is done). (does)
+ * MUST linger in TIME_WAIT for 2 * MSL (does)
+ *
+ * Retransmission Timeout (4.2.2.15)
+ * MUST implement Jacobson's slow start and congestion avoidance
+ * stuff. (does)
+ *
+ * Probing Zero Windows (4.2.2.17)
+ * MUST support probing of zero windows. (does)
+ * MAY keep offered window closed indefinitely. (does)
+ * MUST allow remote window to stay closed indefinitely. (does)
+ *
+ * Passive Open Calls (4.2.2.18)
+ * MUST NOT let new passive open affect other connections. (doesn't)
+ * MUST support passive opens (LISTENs) concurrently. (does)
+ *
+ * Time to Live (4.2.2.19)
+ * MUST make TCP TTL configurable. (does - IP_TTL option)
+ *
+ * Event Processing (4.2.2.20)
+ * SHOULD queue out-of-order segments. (does)
+ * MUST aggregate ACK segments whenever possible. (does but badly)
+ *
+ * Retransmission Timeout Calculation (4.2.3.1)
+ * MUST implement Karn's algorithm and Jacobson's algorithm for RTO
+ * calculation. (does, or at least explains them in the comments 8*b)
+ * SHOULD initialize RTO to 0 and RTT to 3. (does)
+ *
+ * When to Send an ACK Segment (4.2.3.2)
+ * SHOULD implement delayed ACK. (does)
+ * MUST keep ACK delay < 0.5 sec. (does)
+ *
+ * When to Send a Window Update (4.2.3.3)
+ * MUST implement receiver-side SWS. (does)
+ *
+ * When to Send Data (4.2.3.4)
+ * MUST implement sender-side SWS. (does)
+ * SHOULD implement Nagle algorithm. (does)
+ *
+ * TCP Connection Failures (4.2.3.5)
+ * MUST handle excessive retransmissions "properly" (see the RFC). (does)
+ * SHOULD inform application layer of soft errors. (does)
+ *
+ * TCP Keep-Alives (4.2.3.6)
+ * MAY provide keep-alives. (does)
+ * MUST make keep-alives configurable on a per-connection basis. (does)
+ * MUST default to no keep-alives. (does)
+ * MUST make keep-alive interval configurable. (does)
+ * MUST make default keep-alive interval > 2 hours. (does)
+ * MUST NOT interpret failure to ACK keep-alive packet as dead
+ * connection. (doesn't)
+ * SHOULD send keep-alive with no data. (does)
+ *
+ * TCP Multihoming (4.2.3.7)
+ * MUST get source address from IP layer before sending first
+ * SYN. (does)
+ * MUST use same local address for all segments of a connection. (does)
+ *
+ * IP Options (4.2.3.8)
+ * MUST ignore unsupported IP options. (does)
+ * MAY support Time Stamp and Record Route. (does)
+ * MUST allow application to specify a source route. (does)
+ * MUST allow received Source Route option to set route for all future
+ * segments on this connection. (does not (security issues))
+ *
+ * ICMP messages (4.2.3.9)
+ * MUST act on ICMP errors. (does)
+ * MUST slow transmission upon receipt of a Source Quench. (doesn't anymore
+ * because that is deprecated now by the IETF, can be turned on)
+ * MUST NOT abort connection upon receipt of soft Destination
+ * Unreachables (0, 1, 5), Time Exceededs and Parameter
+ * Problems. (doesn't)
+ * SHOULD report soft Destination Unreachables etc. to the
+ * application. (does, except during SYN_RECV and may drop messages
+ * in some rare cases before accept() - ICMP is unreliable)
+ * SHOULD abort connection upon receipt of hard Destination Unreachable
+ * messages (2, 3, 4). (does, but see above)
+ *
+ * Remote Address Validation (4.2.3.10)
+ * MUST reject as an error OPEN for invalid remote IP address. (does)
+ * MUST ignore SYN with invalid source address. (does)
+ * MUST silently discard incoming SYN for broadcast/multicast
+ * address. (does)
+ *
+ * Asynchronous Reports (4.2.4.1)
+ * MUST provide mechanism for reporting soft errors to application
+ * layer. (does)
+ *
+ * Type of Service (4.2.4.2)
+ * MUST allow application layer to set Type of Service. (does IP_TOS)
+ *
+ * (Whew. -- MS 950903)
+ * (Updated by AK, but not complete yet.)
+ **/
+
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+
+#include <net/icmp.h>
+#include <net/tcp.h>
+
+#include <asm/uaccess.h>
+
+int sysctl_tcp_fin_timeout = TCP_FIN_TIMEOUT;
+
+struct tcp_mib tcp_statistics;
+
+kmem_cache_t *tcp_openreq_cachep;
+kmem_cache_t *tcp_bucket_cachep;
+kmem_cache_t *tcp_timewait_cachep;
+
+/*
+ * Find someone to 'accept'. Must be called with
+ * the socket locked or with interrupts disabled
+ */
+
+static struct open_request *tcp_find_established(struct tcp_opt *tp,
+ struct open_request **prevp)
+{
+ struct open_request *req = tp->syn_wait_queue;
+ struct open_request *prev = (struct open_request *)&tp->syn_wait_queue;
+ while(req) {
+ if (req->sk &&
+ ((1 << req->sk->state) &
+ ~(TCPF_SYN_SENT|TCPF_SYN_RECV)))
+ break;
+ prev = req;
+ req = req->dl_next;
+ }
+ *prevp = prev;
+ return req;
+}
+
+/*
+ * Walk down the receive queue counting readable data.
+ *
+ * Must be called with the socket lock held.
+ */
+
+static int tcp_readable(struct sock *sk)
+{
+ unsigned long counted;
+ unsigned long amount;
+ struct sk_buff *skb;
+ int sum;
+
+ SOCK_DEBUG(sk, "tcp_readable: %p - ",sk);
+
+ skb = skb_peek(&sk->receive_queue);
+ if (skb == NULL) {
+ SOCK_DEBUG(sk, "empty\n");
+ return(0);
+ }
+
+ counted = sk->tp_pinfo.af_tcp.copied_seq; /* Where we are at the moment */
+ amount = 0;
+
+ /* Do until a push or until we are out of data. */
+ do {
+ /* Found a hole so stops here. */
+ if (before(counted, TCP_SKB_CB(skb)->seq)) /* should not happen */
+ break;
+
+ /* Length - header but start from where we are up to
+ * avoid overlaps.
+ */
+ sum = skb->len - (counted - TCP_SKB_CB(skb)->seq);
+ if (sum >= 0) {
+ /* Add it up, move on. */
+ amount += sum;
+ counted += sum;
+ if (skb->h.th->syn)
+ counted++;
+ }
+
+ /* Don't count urg data ... but do it in the right place!
+ * Consider: "old_data (ptr is here) URG PUSH data"
+ * The old code would stop at the first push because
+ * it counted the urg (amount==1) and then does amount--
+ * *after* the loop. This means tcp_readable() always
+ * returned zero if any URG PUSH was in the queue, even
+ * though there was normal data available. If we subtract
+ * the urg data right here, we even get it to work for more
+ * than one URG PUSH skb without normal data.
+ * This means that poll() finally works now with urg data
+ * in the queue. Note that rlogin was never affected
+ * because it doesn't use poll(); it uses two processes
+ * and a blocking read(). And the queue scan in tcp_read()
+ * was correct. Mike <pall@rz.uni-karlsruhe.de>
+ */
+
+ /* Don't count urg data. */
+ if (skb->h.th->urg)
+ amount--;
+#if 0
+ if (amount && skb->h.th->psh) break;
+#endif
+ skb = skb->next;
+ } while(skb != (struct sk_buff *)&sk->receive_queue);
+
+ SOCK_DEBUG(sk, "got %lu bytes.\n",amount);
+ return(amount);
+}
+
+/*
+ * LISTEN is a special case for poll..
+ */
+static unsigned int tcp_listen_poll(struct sock *sk, poll_table *wait)
+{
+ struct open_request *req, *dummy;
+
+ lock_sock(sk);
+ req = tcp_find_established(&sk->tp_pinfo.af_tcp, &dummy);
+ release_sock(sk);
+ if (req)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+/*
+ * Compute minimal free write space needed to queue new packets.
+ */
+#define tcp_min_write_space(__sk) \
+ (atomic_read(&(__sk)->wmem_alloc) / 2)
+
+/*
+ * Wait for a TCP event.
+ *
+ * Note that we don't need to lock the socket, as the upper poll layers
+ * take care of normal races (between the test and the event) and we don't
+ * go look at any of the socket buffers directly.
+ */
+unsigned int tcp_poll(struct file * file, struct socket *sock, poll_table *wait)
+{
+ unsigned int mask;
+ struct sock *sk = sock->sk;
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ poll_wait(file, sk->sleep, wait);
+ if (sk->state == TCP_LISTEN)
+ return tcp_listen_poll(sk, wait);
+
+ mask = 0;
+ if (sk->err)
+ mask = POLLERR;
+
+ /*
+ * POLLHUP is certainly not done right. But poll() doesn't
+ * have a notion of HUP in just one direction, and for a
+ * socket the read side is more interesting.
+ *
+ * Some poll() documentation says that POLLHUP is incompatible
+ * with the POLLOUT/POLLWR flags, so somebody should check this
+ * all. But careful, it tends to be safer to return too many
+ * bits than too few, and you can easily break real applications
+ * if you don't tell them that something has hung up!
+ *
+ * Check-me.
+ */
+ if (sk->shutdown & RCV_SHUTDOWN)
+ mask |= POLLHUP;
+
+ /* Connected? */
+ if ((1 << sk->state) & ~(TCPF_SYN_SENT|TCPF_SYN_RECV)) {
+ if ((tp->rcv_nxt != tp->copied_seq) &&
+ (tp->urg_seq != tp->copied_seq ||
+ tp->rcv_nxt != tp->copied_seq+1 ||
+ sk->urginline || !tp->urg_data))
+ mask |= POLLIN | POLLRDNORM;
+
+ if (!(sk->shutdown & SEND_SHUTDOWN)) {
+ if (sock_wspace(sk) >= tcp_min_write_space(sk)) {
+ mask |= POLLOUT | POLLWRNORM;
+ } else { /* send SIGIO later */
+ sk->socket->flags |= SO_NOSPACE;
+ }
+ }
+
+ if (tp->urg_data & URG_VALID)
+ mask |= POLLPRI;
+ }
+ return mask;
+}
+
+/*
+ * Socket write_space callback.
+ * This (or rather the sock_wake_async) should agree with poll.
+ */
+void tcp_write_space(struct sock *sk)
+{
+ if (sk->dead)
+ return;
+
+ wake_up_interruptible(sk->sleep);
+ if (sock_wspace(sk) >=
+ tcp_min_write_space(sk))
+ sock_wake_async(sk->socket, 2);
+}
+
+
+#ifdef _HURD_
+
+#define tcp_ioctl 0
+
+error_t
+tcp_tiocinq(struct sock *sk, mach_msg_type_number_t *amount)
+{
+ if (sk->state == TCP_LISTEN)
+ return EINVAL;
+ lock_sock(sk);
+ *amount = tcp_readable(sk);
+ release_sock(sk);
+ return 0;
+}
+
+#else
+
+int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ int answ;
+
+ switch(cmd) {
+ case TIOCINQ:
+#ifdef FIXME /* FIXME: */
+ case FIONREAD:
+#endif
+ if (sk->state == TCP_LISTEN)
+ return(-EINVAL);
+ lock_sock(sk);
+ answ = tcp_readable(sk);
+ release_sock(sk);
+ break;
+ case SIOCATMARK:
+ {
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ answ = tp->urg_data && tp->urg_seq == tp->copied_seq;
+ break;
+ }
+ case TIOCOUTQ:
+ if (sk->state == TCP_LISTEN)
+ return(-EINVAL);
+ answ = sock_wspace(sk);
+ break;
+ default:
+ return(-ENOIOCTLCMD);
+ };
+
+ return put_user(answ, (int *)arg);
+}
+
+#endif
+
+/*
+ * Wait for a socket to get into the connected state
+ *
+ * Note: must be called with the socket locked.
+ */
+static int wait_for_tcp_connect(struct sock * sk, int flags)
+{
+ struct task_struct *tsk = current;
+ struct wait_queue wait = { tsk, NULL };
+
+ while((1 << sk->state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) {
+ if(sk->err)
+ return sock_error(sk);
+ if((1 << sk->state) &
+ ~(TCPF_SYN_SENT | TCPF_SYN_RECV)) {
+ if(sk->keepopen && !(flags&MSG_NOSIGNAL))
+ send_sig(SIGPIPE, tsk, 0);
+ return -EPIPE;
+ }
+ if(flags & MSG_DONTWAIT)
+ return -EAGAIN;
+ if(signal_pending(tsk))
+ return -ERESTARTSYS;
+
+ tsk->state = TASK_INTERRUPTIBLE;
+ add_wait_queue(sk->sleep, &wait);
+ release_sock(sk);
+
+ if (((1 << sk->state) & ~(TCPF_ESTABLISHED|TCPF_CLOSE_WAIT)) &&
+ sk->err == 0)
+ schedule();
+
+ tsk->state = TASK_RUNNING;
+ remove_wait_queue(sk->sleep, &wait);
+ lock_sock(sk);
+ }
+ return 0;
+}
+
+static inline int tcp_memory_free(struct sock *sk)
+{
+ return atomic_read(&sk->wmem_alloc) < sk->sndbuf;
+}
+
+/*
+ * Wait for more memory for a socket
+ */
+static void wait_for_tcp_memory(struct sock * sk)
+{
+ release_sock(sk);
+ if (!tcp_memory_free(sk)) {
+ struct wait_queue wait = { current, NULL };
+
+ sk->socket->flags &= ~SO_NOSPACE;
+ add_wait_queue(sk->sleep, &wait);
+ for (;;) {
+ if (signal_pending(current))
+ break;
+ current->state = TASK_INTERRUPTIBLE;
+ if (tcp_memory_free(sk))
+ break;
+ if (sk->shutdown & SEND_SHUTDOWN)
+ break;
+ if (sk->err)
+ break;
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(sk->sleep, &wait);
+ }
+ lock_sock(sk);
+}
+
+/*
+ * Wait for a buffer.
+ */
+static int wait_for_buffer(struct sock *sk)
+{
+ struct wait_queue wait = { current, NULL };
+
+ release_sock(sk);
+ add_wait_queue(sk->sleep, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ current->state = TASK_RUNNING;
+ remove_wait_queue(sk->sleep, &wait);
+ lock_sock(sk);
+ return 0;
+}
+
+/* When all user supplied data has been queued set the PSH bit */
+#define PSH_NEEDED (seglen == 0 && iovlen == 0)
+
+/*
+ * This routine copies from a user buffer into a socket,
+ * and starts the transmit system.
+ *
+ * Note: must be called with the socket locked.
+ */
+
+int tcp_do_sendmsg(struct sock *sk, struct msghdr *msg)
+{
+ struct iovec *iov;
+ struct tcp_opt *tp;
+ struct sk_buff *skb;
+ int iovlen, flags;
+ int mss_now;
+ int err, copied;
+
+ lock_sock(sk);
+
+ err = 0;
+ tp = &(sk->tp_pinfo.af_tcp);
+
+ /* Wait for a connection to finish. */
+ flags = msg->msg_flags;
+ if ((1 << sk->state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))
+ if((err = wait_for_tcp_connect(sk, flags)) != 0)
+ goto out;
+
+ /* This should be in poll */
+ sk->socket->flags &= ~SO_NOSPACE; /* clear SIGIO XXX */
+
+ mss_now = tcp_current_mss(sk);
+
+ /* Ok commence sending. */
+ iovlen = msg->msg_iovlen;
+ iov = msg->msg_iov;
+ copied = 0;
+
+ while(--iovlen >= 0) {
+ int seglen=iov->iov_len;
+ unsigned char * from=iov->iov_base;
+
+ iov++;
+
+ while(seglen > 0) {
+ int copy, tmp, queue_it, psh;
+
+ if (err)
+ goto do_fault2;
+
+ /* Stop on errors. */
+ if (sk->err)
+ goto do_sock_err;
+
+ /* Make sure that we are established. */
+ if (sk->shutdown & SEND_SHUTDOWN)
+ goto do_shutdown;
+
+ /* Now we need to check if we have a half
+ * built packet we can tack some data onto.
+ */
+ if (tp->send_head && !(flags & MSG_OOB)) {
+ skb = sk->write_queue.prev;
+ copy = skb->len;
+ /* If the remote does SWS avoidance we should
+ * queue the best we can if not we should in
+ * fact send multiple packets...
+ * A method for detecting this would be most
+ * welcome.
+ */
+ if (skb_tailroom(skb) > 0 &&
+ (mss_now - copy) > 0 &&
+ tp->snd_nxt < TCP_SKB_CB(skb)->end_seq) {
+ int last_byte_was_odd = (copy % 4);
+
+ /*
+ * Check for parallel writers sleeping in user access.
+ */
+ if (tp->partial_writers++ > 0) {
+ wait_for_buffer(sk);
+ tp->partial_writers--;
+ continue;
+ }
+
+ copy = mss_now - copy;
+ if(copy > skb_tailroom(skb))
+ copy = skb_tailroom(skb);
+ if(copy > seglen)
+ copy = seglen;
+
+ if(last_byte_was_odd) {
+ if(copy_from_user(skb_put(skb, copy),
+ from, copy))
+ err = -EFAULT;
+ skb->csum = csum_partial(skb->data,
+ skb->len, 0);
+ } else {
+ skb->csum =
+ csum_and_copy_from_user(
+ from, skb_put(skb, copy),
+ copy, skb->csum, &err);
+ }
+
+ /*
+ * FIXME: the *_user functions should
+ * return how much data was
+ * copied before the fault
+ * occurred and then a partial
+ * packet with this data should
+ * be sent. Unfortunately
+ * csum_and_copy_from_user doesn't
+ * return this information.
+ * ATM it might send partly zeroed
+ * data in this case.
+ */
+ tp->write_seq += copy;
+ TCP_SKB_CB(skb)->end_seq += copy;
+ from += copy;
+ copied += copy;
+ seglen -= copy;
+ if (PSH_NEEDED)
+ TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_PSH;
+
+ if (--tp->partial_writers > 0)
+ wake_up_interruptible(sk->sleep);
+
+ continue;
+ }
+ }
+
+ /* We also need to worry about the window. If
+ * window < 1/2 the maximum window we've seen
+ * from this host, don't use it. This is
+ * sender side silly window prevention, as
+ * specified in RFC1122. (Note that this is
+ * different than earlier versions of SWS
+ * prevention, e.g. RFC813.). What we
+ * actually do is use the whole MSS. Since
+ * the results in the right edge of the packet
+ * being outside the window, it will be queued
+ * for later rather than sent.
+ */
+ psh = 0;
+ copy = tp->snd_wnd - (tp->snd_nxt - tp->snd_una);
+ if(copy > (tp->max_window >> 1)) {
+ copy = min(copy, mss_now);
+ psh = 1;
+ } else {
+ copy = mss_now;
+ }
+ if(copy > seglen)
+ copy = seglen;
+
+ /* Determine how large of a buffer to allocate. */
+ tmp = MAX_HEADER + sk->prot->max_header;
+ if (copy < min(mss_now, tp->max_window >> 1) &&
+ !(flags & MSG_OOB)) {
+ tmp += min(mss_now, tp->max_window);
+
+ /* What is happening here is that we want to
+ * tack on later members of the users iovec
+ * if possible into a single frame. When we
+ * leave this loop our caller checks to see if
+ * we can send queued frames onto the wire.
+ * See tcp_v[46]_sendmsg() for this.
+ */
+ queue_it = 1;
+ } else {
+ tmp += copy;
+ queue_it = 0;
+ }
+ skb = sock_wmalloc(sk, tmp, 0, GFP_KERNEL);
+
+ /* If we didn't get any memory, we need to sleep. */
+ if (skb == NULL) {
+ sk->socket->flags |= SO_NOSPACE;
+ if (flags&MSG_DONTWAIT) {
+ err = -EAGAIN;
+ goto do_interrupted;
+ }
+ if (signal_pending(current)) {
+ err = -ERESTARTSYS;
+ goto do_interrupted;
+ }
+ tcp_push_pending_frames(sk, tp);
+ wait_for_tcp_memory(sk);
+
+ /* If SACK's were formed or PMTU events happened,
+ * we must find out about it.
+ */
+ mss_now = tcp_current_mss(sk);
+ continue;
+ }
+
+ seglen -= copy;
+
+ /* Prepare control bits for TCP header creation engine. */
+ TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK |
+ ((PSH_NEEDED || psh) ?
+ TCPCB_FLAG_PSH : 0));
+ TCP_SKB_CB(skb)->sacked = 0;
+ if (flags & MSG_OOB) {
+ TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_URG;
+ TCP_SKB_CB(skb)->urg_ptr = copy;
+ } else
+ TCP_SKB_CB(skb)->urg_ptr = 0;
+
+ /* TCP data bytes are SKB_PUT() on top, later
+ * TCP+IP+DEV headers are SKB_PUSH()'d beneath.
+ * Reserve header space and checksum the data.
+ */
+ skb_reserve(skb, MAX_HEADER + sk->prot->max_header);
+ skb->csum = csum_and_copy_from_user(from,
+ skb_put(skb, copy), copy, 0, &err);
+
+ if (err)
+ goto do_fault;
+
+ from += copy;
+ copied += copy;
+
+ TCP_SKB_CB(skb)->seq = tp->write_seq;
+ TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + copy;
+
+ /* This advances tp->write_seq for us. */
+ tcp_send_skb(sk, skb, queue_it);
+ }
+ }
+ sk->err = 0;
+ err = copied;
+ goto out;
+
+do_sock_err:
+ if(copied)
+ err = copied;
+ else
+ err = sock_error(sk);
+ goto out;
+do_shutdown:
+ if(copied)
+ err = copied;
+ else {
+ if (!(flags&MSG_NOSIGNAL))
+ send_sig(SIGPIPE, current, 0);
+ err = -EPIPE;
+ }
+ goto out;
+do_interrupted:
+ if(copied)
+ err = copied;
+ goto out;
+do_fault:
+ kfree_skb(skb);
+do_fault2:
+ err = -EFAULT;
+out:
+ tcp_push_pending_frames(sk, tp);
+ release_sock(sk);
+ return err;
+}
+
+#undef PSH_NEEDED
+
+/*
+ * Send an ack if one is backlogged at this point. Ought to merge
+ * this with tcp_send_ack().
+ * This is called for delayed acks also.
+ */
+
+void tcp_read_wakeup(struct sock *sk)
+{
+ /* If we're closed, don't send an ack, or we'll get a RST
+ * from the closed destination.
+ */
+ if (sk->state != TCP_CLOSE)
+ tcp_send_ack(sk);
+}
+
+/*
+ * Handle reading urgent data. BSD has very simple semantics for
+ * this, no blocking and very strange errors 8)
+ */
+
+static int tcp_recv_urg(struct sock * sk, int nonblock,
+ struct msghdr *msg, int len, int flags,
+ int *addr_len)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ /* No URG data to read. */
+ if (sk->urginline || !tp->urg_data || tp->urg_data == URG_READ)
+ return -EINVAL; /* Yes this is right ! */
+
+ if (sk->err)
+ return sock_error(sk);
+
+ if (sk->done)
+ return -ENOTCONN;
+
+ if (sk->state == TCP_CLOSE || (sk->shutdown & RCV_SHUTDOWN)) {
+ sk->done = 1;
+ return 0;
+ }
+
+ lock_sock(sk);
+ if (tp->urg_data & URG_VALID) {
+ int err = 0;
+ char c = tp->urg_data;
+
+ if (!(flags & MSG_PEEK))
+ tp->urg_data = URG_READ;
+
+ if(msg->msg_name)
+ tp->af_specific->addr2sockaddr(sk, (struct sockaddr *)
+ msg->msg_name);
+
+ if(addr_len)
+ *addr_len = tp->af_specific->sockaddr_len;
+
+ /* Read urgent data. */
+ msg->msg_flags|=MSG_OOB;
+ release_sock(sk);
+
+ if(len>0)
+ {
+ err = memcpy_toiovec(msg->msg_iov, &c, 1);
+ /* N.B. already set above ... */
+ msg->msg_flags|=MSG_OOB;
+ }
+ else
+ msg->msg_flags|=MSG_TRUNC;
+
+ /* N.B. Is this right?? If len == 0 we didn't read any data */
+ return err ? -EFAULT : 1;
+ }
+ release_sock(sk);
+
+ /* Fixed the recv(..., MSG_OOB) behaviour. BSD docs and
+ * the available implementations agree in this case:
+ * this call should never block, independent of the
+ * blocking state of the socket.
+ * Mike <pall@rz.uni-karlsruhe.de>
+ */
+ return -EAGAIN;
+}
+
+/*
+ * Release a skb if it is no longer needed. This routine
+ * must be called with interrupts disabled or with the
+ * socket locked so that the sk_buff queue operation is ok.
+ */
+
+static inline void tcp_eat_skb(struct sock *sk, struct sk_buff * skb)
+{
+ __skb_unlink(skb, &sk->receive_queue);
+ kfree_skb(skb);
+}
+
+/* Clean up the receive buffer for full frames taken by the user,
+ * then send an ACK if necessary. COPIED is the number of bytes
+ * tcp_recvmsg has given to the user so far, it speeds up the
+ * calculation of whether or not we must ACK for the sake of
+ * a window update.
+ */
+static void cleanup_rbuf(struct sock *sk, int copied)
+{
+ struct sk_buff *skb;
+
+ /* NOTE! The socket must be locked, so that we don't get
+ * a messed-up receive queue.
+ */
+ while ((skb=skb_peek(&sk->receive_queue)) != NULL) {
+ if (!skb->used || atomic_read(&skb->users) > 1)
+ break;
+ tcp_eat_skb(sk, skb);
+ }
+
+ /* We send an ACK if we can now advertise a non-zero window
+ * which has been raised "significantly".
+ */
+ if(copied > 0) {
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ __u32 rcv_window_now = tcp_receive_window(tp);
+ __u32 new_window = __tcp_select_window(sk);
+
+ /* We won't be raising the window any further than
+ * the window-clamp allows. Our window selection
+ * also keeps things a nice multiple of MSS. These
+ * checks are necessary to prevent spurious ACKs
+ * which don't advertize a larger window.
+ */
+ if((new_window && (new_window >= rcv_window_now * 2)) &&
+ ((rcv_window_now + tp->mss_cache) <= tp->window_clamp))
+ tcp_read_wakeup(sk);
+ }
+}
+
+
+/*
+ * This routine copies from a sock struct into the user buffer.
+ */
+
+int tcp_recvmsg(struct sock *sk, struct msghdr *msg,
+ int len, int nonblock, int flags, int *addr_len)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct wait_queue wait = { current, NULL };
+ int copied = 0;
+ u32 peek_seq;
+ volatile u32 *seq; /* So gcc doesn't overoptimise */
+ unsigned long used;
+ int err = 0;
+ int target = 1; /* Read at least this many bytes */
+
+ if (sk->err)
+ return sock_error(sk);
+
+ if (sk->state == TCP_LISTEN)
+ return -ENOTCONN;
+
+ /* Urgent data needs to be handled specially. */
+ if (flags & MSG_OOB)
+ return tcp_recv_urg(sk, nonblock, msg, len, flags, addr_len);
+
+ /* Copying sequence to update. This is volatile to handle
+ * the multi-reader case neatly (memcpy_to/fromfs might be
+ * inline and thus not flush cached variables otherwise).
+ */
+ peek_seq = tp->copied_seq;
+ seq = &tp->copied_seq;
+ if (flags & MSG_PEEK)
+ seq = &peek_seq;
+
+ /* Handle the POSIX bogosity MSG_WAITALL. */
+ if (flags & MSG_WAITALL)
+ target=len;
+
+ add_wait_queue(sk->sleep, &wait);
+ lock_sock(sk);
+
+ /*
+ * BUG BUG BUG
+ * This violates 1003.1g compliance. We must wait for
+ * data to exist even if we read none!
+ */
+
+ while (len > 0) {
+ struct sk_buff * skb;
+ u32 offset;
+
+ /* Are we at urgent data? Stop if we have read anything. */
+ if (copied && tp->urg_data && tp->urg_seq == *seq)
+ break;
+
+ /* We need to check signals first, to get correct SIGURG
+ * handling. FIXME: Need to check this doesn't impact 1003.1g
+ * and move it down to the bottom of the loop
+ */
+ if (signal_pending(current)) {
+ if (copied)
+ break;
+ copied = -ERESTARTSYS;
+ if (nonblock)
+ copied = -EAGAIN;
+ break;
+ }
+
+ /* Next get a buffer. */
+ current->state = TASK_INTERRUPTIBLE;
+
+ skb = skb_peek(&sk->receive_queue);
+ do {
+ if (!skb)
+ break;
+
+ /* Now that we have two receive queues this
+ * shouldn't happen.
+ */
+ if (before(*seq, TCP_SKB_CB(skb)->seq)) {
+ printk(KERN_INFO "recvmsg bug: copied %X seq %X\n",
+ *seq, TCP_SKB_CB(skb)->seq);
+ break;
+ }
+ offset = *seq - TCP_SKB_CB(skb)->seq;
+ if (skb->h.th->syn)
+ offset--;
+ if (offset < skb->len)
+ goto found_ok_skb;
+ if (skb->h.th->fin)
+ goto found_fin_ok;
+ if (!(flags & MSG_PEEK))
+ skb->used = 1;
+ skb = skb->next;
+ } while (skb != (struct sk_buff *)&sk->receive_queue);
+
+ if (copied >= target)
+ break;
+
+ /*
+ These three lines and clause if (sk->state == TCP_CLOSE)
+ are unlikely to be correct, if target > 1.
+ I DO NOT FIX IT, because I have no idea, what
+ POSIX prescribes to make here. Probably, it really
+ wants to lose data 8), if not all target is received.
+ --ANK
+ */
+ if (sk->err && !(flags&MSG_PEEK)) {
+ copied = sock_error(sk);
+ break;
+ }
+
+ if (sk->shutdown & RCV_SHUTDOWN) {
+ sk->done = 1;
+ break;
+ }
+
+ if (sk->state == TCP_CLOSE) {
+ if (!sk->done) {
+ sk->done = 1;
+ break;
+ }
+ copied = -ENOTCONN;
+ break;
+ }
+
+ if (nonblock) {
+ copied = -EAGAIN;
+ break;
+ }
+
+ cleanup_rbuf(sk, copied);
+ release_sock(sk);
+ sk->socket->flags |= SO_WAITDATA;
+ schedule();
+ sk->socket->flags &= ~SO_WAITDATA;
+ lock_sock(sk);
+ continue;
+
+ found_ok_skb:
+ /* Lock the buffer. We can be fairly relaxed as
+ * an interrupt will never steal a buffer we are
+ * using unless I've missed something serious in
+ * tcp_data.
+ */
+ atomic_inc(&skb->users);
+
+ /* Ok so how much can we use? */
+ used = skb->len - offset;
+ if (len < used)
+ used = len;
+
+ /* Do we have urgent data here? */
+ if (tp->urg_data) {
+ u32 urg_offset = tp->urg_seq - *seq;
+ if (urg_offset < used) {
+ if (!urg_offset) {
+ if (!sk->urginline) {
+ ++*seq;
+ offset++;
+ used--;
+ }
+ } else
+ used = urg_offset;
+ }
+ }
+
+ /* Copy it - We _MUST_ update *seq first so that we
+ * don't ever double read when we have dual readers
+ */
+ *seq += used;
+
+ /* This memcpy_toiovec can sleep. If it sleeps and we
+ * do a second read it relies on the skb->users to avoid
+ * a crash when cleanup_rbuf() gets called.
+ */
+ err = memcpy_toiovec(msg->msg_iov, ((unsigned char *)skb->h.th) + skb->h.th->doff*4 + offset, used);
+ if (err) {
+ /* Exception. Bailout! */
+ atomic_dec(&skb->users);
+ copied = -EFAULT;
+ break;
+ }
+
+ copied += used;
+ len -= used;
+
+ /* We now will not sleep again until we are finished
+ * with skb. Sorry if you are doing the SMP port
+ * but you'll just have to fix it neatly ;)
+ */
+ atomic_dec(&skb->users);
+
+ if (after(tp->copied_seq,tp->urg_seq))
+ tp->urg_data = 0;
+ if (used + offset < skb->len)
+ continue;
+
+ /* Process the FIN. We may also need to handle PSH
+ * here and make it break out of MSG_WAITALL.
+ */
+ if (skb->h.th->fin)
+ goto found_fin_ok;
+ if (flags & MSG_PEEK)
+ continue;
+ skb->used = 1;
+ if (atomic_read(&skb->users) == 1)
+ tcp_eat_skb(sk, skb);
+ continue;
+
+ found_fin_ok:
+ ++*seq;
+ if (flags & MSG_PEEK)
+ break;
+
+ /* All is done. */
+ skb->used = 1;
+ sk->shutdown |= RCV_SHUTDOWN;
+ break;
+ }
+
+ if(copied >= 0 && msg->msg_name) {
+ tp->af_specific->addr2sockaddr(sk, (struct sockaddr *)
+ msg->msg_name);
+ if(addr_len)
+ *addr_len = tp->af_specific->sockaddr_len;
+ }
+
+ remove_wait_queue(sk->sleep, &wait);
+ current->state = TASK_RUNNING;
+
+ /* Clean up data we have read: This will do ACK frames. */
+ cleanup_rbuf(sk, copied);
+ release_sock(sk);
+ return copied;
+}
+
+/*
+ * Check whether to renew the timer.
+ */
+static inline void tcp_check_fin_timer(struct sock *sk)
+{
+ if (sk->state == TCP_FIN_WAIT2 && !sk->timer.prev)
+ tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout);
+}
+
+/*
+ * State processing on a close. This implements the state shift for
+ * sending our FIN frame. Note that we only send a FIN for some
+ * states. A shutdown() may have already sent the FIN, or we may be
+ * closed.
+ */
+
+static unsigned char new_state[16] = {
+ /* current state: new state: action: */
+ /* (Invalid) */ TCP_CLOSE,
+ /* TCP_ESTABLISHED */ TCP_FIN_WAIT1 | TCP_ACTION_FIN,
+ /* TCP_SYN_SENT */ TCP_CLOSE,
+ /* TCP_SYN_RECV */ TCP_FIN_WAIT1 | TCP_ACTION_FIN,
+ /* TCP_FIN_WAIT1 */ TCP_FIN_WAIT1,
+ /* TCP_FIN_WAIT2 */ TCP_FIN_WAIT2,
+ /* TCP_TIME_WAIT */ TCP_CLOSE,
+ /* TCP_CLOSE */ TCP_CLOSE,
+ /* TCP_CLOSE_WAIT */ TCP_LAST_ACK | TCP_ACTION_FIN,
+ /* TCP_LAST_ACK */ TCP_LAST_ACK,
+ /* TCP_LISTEN */ TCP_CLOSE,
+ /* TCP_CLOSING */ TCP_CLOSING,
+};
+
+static int tcp_close_state(struct sock *sk, int dead)
+{
+ int next = (int) new_state[sk->state];
+ int ns = (next & TCP_STATE_MASK);
+
+ tcp_set_state(sk, ns);
+
+ /* This is a (useful) BSD violating of the RFC. There is a
+ * problem with TCP as specified in that the other end could
+ * keep a socket open forever with no application left this end.
+ * We use a 3 minute timeout (about the same as BSD) then kill
+ * our end. If they send after that then tough - BUT: long enough
+ * that we won't make the old 4*rto = almost no time - whoops
+ * reset mistake.
+ */
+ if (dead)
+ tcp_check_fin_timer(sk);
+
+ return (next & TCP_ACTION_FIN);
+}
+
+/*
+ * Shutdown the sending side of a connection. Much like close except
+ * that we don't receive shut down or set sk->dead.
+ */
+
+void tcp_shutdown(struct sock *sk, int how)
+{
+ /* We need to grab some memory, and put together a FIN,
+ * and then put it into the queue to be sent.
+ * Tim MacKenzie(tym@dibbler.cs.monash.edu.au) 4 Dec '92.
+ */
+ if (!(how & SEND_SHUTDOWN))
+ return;
+
+ /* If we've already sent a FIN, or it's a closed state, skip this. */
+ if ((1 << sk->state) &
+ (TCPF_ESTABLISHED|TCPF_SYN_SENT|TCPF_SYN_RECV|TCPF_CLOSE_WAIT)) {
+ lock_sock(sk);
+
+ /* Clear out any half completed packets. FIN if needed. */
+ if (tcp_close_state(sk,0))
+ tcp_send_fin(sk);
+
+ release_sock(sk);
+ }
+}
+
+
+/*
+ * Return 1 if we still have things to send in our buffers.
+ */
+
+static inline int closing(struct sock * sk)
+{
+ return ((1 << sk->state) & (TCPF_FIN_WAIT1|TCPF_CLOSING|TCPF_LAST_ACK));
+}
+
+/*
+ * This routine closes sockets which have been at least partially
+ * opened, but not yet accepted. Currently it is only called by
+ * tcp_close, and timeout mirrors the value there.
+ */
+
+static void tcp_close_pending (struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct open_request *req = tp->syn_wait_queue;
+
+ while(req) {
+ struct open_request *iter;
+
+ if (req->sk)
+ tcp_close(req->sk, 0);
+
+ iter = req;
+ req = req->dl_next;
+
+ (*iter->class->destructor)(iter);
+ tcp_dec_slow_timer(TCP_SLT_SYNACK);
+ sk->ack_backlog--;
+ tcp_openreq_free(iter);
+ }
+
+ tcp_synq_init(tp);
+}
+
+void tcp_close(struct sock *sk, long timeout)
+{
+ struct sk_buff *skb;
+ int data_was_unread = 0;
+
+ /*
+ * Check whether the socket is locked ... supposedly
+ * it's impossible to tcp_close() a locked socket.
+ */
+ if (atomic_read(&sk->sock_readers))
+ printk("tcp_close: socket already locked!\n");
+
+ /* We need to grab some memory, and put together a FIN,
+ * and then put it into the queue to be sent.
+ */
+ lock_sock(sk);
+ if(sk->state == TCP_LISTEN) {
+ /* Special case. */
+ tcp_set_state(sk, TCP_CLOSE);
+ tcp_close_pending(sk);
+ release_sock(sk);
+ sk->dead = 1;
+ return;
+ }
+
+ /* It is questionable, what the role of this is now.
+ * In any event either it should be removed, or
+ * increment of SLT_KEEPALIVE be done, this is causing
+ * big problems. For now I comment it out. -DaveM
+ */
+ /* sk->keepopen = 1; */
+ sk->shutdown = SHUTDOWN_MASK;
+
+ if (!sk->dead)
+ sk->state_change(sk);
+
+ /* We need to flush the recv. buffs. We do this only on the
+ * descriptor close, not protocol-sourced closes, because the
+ * reader process may not have drained the data yet!
+ */
+ while((skb=__skb_dequeue(&sk->receive_queue))!=NULL) {
+ u32 len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq - skb->h.th->fin;
+ data_was_unread += len;
+ kfree_skb(skb);
+ }
+
+ /* As outlined in draft-ietf-tcpimpl-prob-03.txt, section
+ * 3.10, we send a RST here because data was lost. To
+ * witness the awful effects of the old behavior of always
+ * doing a FIN, run an older 2.1.x kernel or 2.0.x, start
+ * a bulk GET in an FTP client, suspend the process, wait
+ * for the client to advertise a zero window, then kill -9
+ * the FTP client, wheee... Note: timeout is always zero
+ * in such a case.
+ */
+ if(data_was_unread != 0) {
+ /* Unread data was tossed, zap the connection. */
+ tcp_set_state(sk, TCP_CLOSE);
+ tcp_send_active_reset(sk);
+ } else if (tcp_close_state(sk,1)) {
+ /* We FIN if the application ate all the data before
+ * zapping the connection.
+ */
+ tcp_send_fin(sk);
+ }
+
+ if (timeout) {
+ struct task_struct *tsk = current;
+ struct wait_queue wait = { tsk, NULL };
+
+ add_wait_queue(sk->sleep, &wait);
+ release_sock(sk);
+
+ while (1) {
+ tsk->state = TASK_INTERRUPTIBLE;
+ if (!closing(sk))
+ break;
+ timeout = schedule_timeout(timeout);
+ if (signal_pending(tsk) || !timeout)
+ break;
+ }
+
+ tsk->state = TASK_RUNNING;
+ remove_wait_queue(sk->sleep, &wait);
+
+ lock_sock(sk);
+ }
+
+ /* Now that the socket is dead, if we are in the FIN_WAIT2 state
+ * we may need to set up a timer.
+ */
+ tcp_check_fin_timer(sk);
+
+ release_sock(sk);
+ sk->dead = 1;
+}
+
+/*
+ * Wait for an incoming connection, avoid race
+ * conditions. This must be called with the socket locked.
+ */
+static struct open_request * wait_for_connect(struct sock * sk,
+ struct open_request **pprev)
+{
+ struct wait_queue wait = { current, NULL };
+ struct open_request *req;
+
+ add_wait_queue(sk->sleep, &wait);
+ for (;;) {
+ current->state = TASK_INTERRUPTIBLE;
+ release_sock(sk);
+ schedule();
+ lock_sock(sk);
+ req = tcp_find_established(&(sk->tp_pinfo.af_tcp), pprev);
+ if (req)
+ break;
+ if (signal_pending(current))
+ break;
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(sk->sleep, &wait);
+ return req;
+}
+
+/*
+ * This will accept the next outstanding connection.
+ *
+ * Be careful about race conditions here - this is subtle.
+ */
+
+struct sock *tcp_accept(struct sock *sk, int flags)
+{
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+ struct open_request *req, *prev;
+ struct sock *newsk = NULL;
+ int error;
+
+ lock_sock(sk);
+
+ /* We need to make sure that this socket is listening,
+ * and that it has something pending.
+ */
+ error = EINVAL;
+ if (sk->state != TCP_LISTEN)
+ goto out;
+
+ /* Find already established connection */
+ req = tcp_find_established(tp, &prev);
+ if (!req) {
+ /* If this is a non blocking socket don't sleep */
+ error = EAGAIN;
+ if (flags & O_NONBLOCK)
+ goto out;
+
+ error = ERESTARTSYS;
+ req = wait_for_connect(sk, &prev);
+ if (!req)
+ goto out;
+ }
+
+ tcp_synq_unlink(tp, req, prev);
+ newsk = req->sk;
+ req->class->destructor(req);
+ tcp_openreq_free(req);
+ sk->ack_backlog--;
+ if(sk->keepopen)
+ tcp_inc_slow_timer(TCP_SLT_KEEPALIVE);
+
+ release_sock(sk);
+ return newsk;
+
+out:
+ /* sk should be in LISTEN state, thus accept can use sk->err for
+ * internal purposes without stomping one anyone's feed.
+ */
+ sk->err = error;
+ release_sock(sk);
+ return newsk;
+}
+
+/*
+ * Socket option code for TCP.
+ */
+
+int tcp_setsockopt(struct sock *sk, int level, int optname, char *optval,
+ int optlen)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ int val;
+
+ if (level != SOL_TCP)
+ return tp->af_specific->setsockopt(sk, level, optname,
+ optval, optlen);
+
+ if(optlen<sizeof(int))
+ return -EINVAL;
+
+ if (get_user(val, (int *)optval))
+ return -EFAULT;
+
+ switch(optname) {
+ case TCP_MAXSEG:
+ /* values greater than interface MTU won't take effect. however at
+ * the point when this call is done we typically don't yet know
+ * which interface is going to be used
+ */
+ if(val < 1 || val > MAX_WINDOW)
+ return -EINVAL;
+ tp->user_mss = val;
+ return 0;
+
+ case TCP_NODELAY:
+ /* You cannot try to use this and TCP_CORK in
+ * tandem, so let the user know.
+ */
+ if (sk->nonagle == 2)
+ return -EINVAL;
+ sk->nonagle = (val == 0) ? 0 : 1;
+ return 0;
+
+ case TCP_CORK:
+ /* When set indicates to always queue non-full frames.
+ * Later the user clears this option and we transmit
+ * any pending partial frames in the queue. This is
+ * meant to be used alongside sendfile() to get properly
+ * filled frames when the user (for example) must write
+ * out headers with a write() call first and then use
+ * sendfile to send out the data parts.
+ *
+ * You cannot try to use TCP_NODELAY and this mechanism
+ * at the same time, so let the user know.
+ */
+ if (sk->nonagle == 1)
+ return -EINVAL;
+ if (val != 0) {
+ sk->nonagle = 2;
+ } else {
+ sk->nonagle = 0;
+
+ lock_sock(sk);
+ tcp_push_pending_frames(sk, tp);
+ release_sock(sk);
+ }
+ return 0;
+
+ default:
+ return -ENOPROTOOPT;
+ };
+}
+
+int tcp_getsockopt(struct sock *sk, int level, int optname, char *optval,
+ int *optlen)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ int val, len;
+
+ if(level != SOL_TCP)
+ return tp->af_specific->getsockopt(sk, level, optname,
+ optval, optlen);
+
+ if(get_user(len,optlen))
+ return -EFAULT;
+
+ len = min(len, sizeof(int));
+
+ switch(optname) {
+ case TCP_MAXSEG:
+ val = tp->user_mss;
+ break;
+ case TCP_NODELAY:
+ val = (sk->nonagle == 1);
+ break;
+ case TCP_CORK:
+ val = (sk->nonagle == 2);
+ break;
+ default:
+ return -ENOPROTOOPT;
+ };
+
+ if(put_user(len, optlen))
+ return -EFAULT;
+ if(copy_to_user(optval, &val,len))
+ return -EFAULT;
+ return 0;
+}
+
+void tcp_set_keepalive(struct sock *sk, int val)
+{
+ if (!sk->keepopen && val)
+ tcp_inc_slow_timer(TCP_SLT_KEEPALIVE);
+ else if (sk->keepopen && !val)
+ tcp_dec_slow_timer(TCP_SLT_KEEPALIVE);
+}
+
+extern void __skb_cb_too_small_for_tcp(int, int);
+
+void __init tcp_init(void)
+{
+ struct sk_buff *skb = NULL;
+ unsigned long goal;
+ int order;
+
+ if(sizeof(struct tcp_skb_cb) > sizeof(skb->cb))
+ __skb_cb_too_small_for_tcp(sizeof(struct tcp_skb_cb),
+ sizeof(skb->cb));
+
+ tcp_openreq_cachep = kmem_cache_create("tcp_open_request",
+ sizeof(struct open_request),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if(!tcp_openreq_cachep)
+ panic("tcp_init: Cannot alloc open_request cache.");
+
+ tcp_bucket_cachep = kmem_cache_create("tcp_bind_bucket",
+ sizeof(struct tcp_bind_bucket),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if(!tcp_bucket_cachep)
+ panic("tcp_init: Cannot alloc tcp_bind_bucket cache.");
+
+ tcp_timewait_cachep = kmem_cache_create("tcp_tw_bucket",
+ sizeof(struct tcp_tw_bucket),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if(!tcp_timewait_cachep)
+ panic("tcp_init: Cannot alloc tcp_tw_bucket cache.");
+
+ /* Size and allocate TCP hash tables. */
+ goal = num_physpages >> (20 - PAGE_SHIFT);
+ for (order = 0; (1UL << order) < goal; order++)
+ ;
+ do {
+ tcp_ehash_size = (1UL << order) * PAGE_SIZE /
+ sizeof(struct sock *);
+ tcp_ehash = (struct sock **)
+ __get_free_pages(GFP_ATOMIC, order);
+ } while (tcp_ehash == NULL && --order >= 0);
+
+ if (!tcp_ehash)
+ panic("Failed to allocate TCP established hash table\n");
+ memset(tcp_ehash, 0, tcp_ehash_size * sizeof(struct sock *));
+
+ goal = (((1UL << order) * PAGE_SIZE) / sizeof(struct tcp_bind_bucket *));
+ if (goal > (64 * 1024)) {
+ /* Don't size the bind-hash larger than the port
+ * space, that is just silly.
+ */
+ goal = (((64 * 1024) * sizeof(struct tcp_bind_bucket *)) / PAGE_SIZE);
+ for (order = 0; (1UL << order) < goal; order++)
+ ;
+ }
+
+ do {
+ tcp_bhash_size = (1UL << order) * PAGE_SIZE /
+ sizeof(struct tcp_bind_bucket *);
+ tcp_bhash = (struct tcp_bind_bucket **)
+ __get_free_pages(GFP_ATOMIC, order);
+ } while (tcp_bhash == NULL && --order >= 0);
+
+ if (!tcp_bhash)
+ panic("Failed to allocate TCP bind hash table\n");
+ memset(tcp_bhash, 0, tcp_bhash_size * sizeof(struct tcp_bind_bucket *));
+
+ printk("TCP: Hash tables configured (ehash %d bhash %d)\n",
+ tcp_ehash_size, tcp_bhash_size);
+}
diff --git a/pfinet/linux-src/net/ipv4/tcp_input.c b/pfinet/linux-src/net/ipv4/tcp_input.c
new file mode 100644
index 00000000..e84eaf43
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/tcp_input.c
@@ -0,0 +1,2432 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Implementation of the Transmission Control Protocol(TCP).
+ *
+ * Version: $Id: tcp_input.c,v 1.164.2.8 1999/09/23 19:21:23 davem Exp $
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ * Corey Minyard <wf-rch!minyard@relay.EU.net>
+ * Florian La Roche, <flla@stud.uni-sb.de>
+ * Charles Hedrick, <hedrick@klinzhai.rutgers.edu>
+ * Linus Torvalds, <torvalds@cs.helsinki.fi>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ * Matthew Dillon, <dillon@apollo.west.oic.com>
+ * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ * Jorge Cwik, <jorge@laser.satlink.net>
+ */
+
+/*
+ * Changes:
+ * Pedro Roque : Fast Retransmit/Recovery.
+ * Two receive queues.
+ * Retransmit queue handled by TCP.
+ * Better retransmit timer handling.
+ * New congestion avoidance.
+ * Header prediction.
+ * Variable renaming.
+ *
+ * Eric : Fast Retransmit.
+ * Randy Scott : MSS option defines.
+ * Eric Schenk : Fixes to slow start algorithm.
+ * Eric Schenk : Yet another double ACK bug.
+ * Eric Schenk : Delayed ACK bug fixes.
+ * Eric Schenk : Floyd style fast retrans war avoidance.
+ * David S. Miller : Don't allow zero congestion window.
+ * Eric Schenk : Fix retransmitter so that it sends
+ * next packet on ack of previous packet.
+ * Andi Kleen : Moved open_request checking here
+ * and process RSTs for open_requests.
+ * Andi Kleen : Better prune_queue, and other fixes.
+ * Andrey Savochkin: Fix RTT measurements in the presnce of
+ * timestamps.
+ * Andrey Savochkin: Check sequence numbers correctly when
+ * removing SACKs due to in sequence incoming
+ * data segments.
+ * Andi Kleen: Make sure we never ack data there is not
+ * enough room for. Also make this condition
+ * a fatal error if it might still happen.
+ * Andi Kleen: Add tcp_measure_rcv_mss to make
+ * connections with MSS<min(MTU,ann. MSS)
+ * work without delayed acks.
+ * Andi Kleen: Process packets with PSH set in the
+ * fast path.
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+#include <net/tcp.h>
+#include <linux/ipsec.h>
+
+#ifdef CONFIG_SYSCTL
+#define SYNC_INIT 0 /* let the user enable it */
+#else
+#define SYNC_INIT 1
+#endif
+
+extern int sysctl_tcp_fin_timeout;
+
+/* These are on by default so the code paths get tested.
+ * For the final 2.2 this may be undone at our discretion. -DaveM
+ */
+int sysctl_tcp_timestamps = 1;
+int sysctl_tcp_window_scaling = 1;
+int sysctl_tcp_sack = 1;
+
+int sysctl_tcp_syncookies = SYNC_INIT;
+int sysctl_tcp_stdurg;
+int sysctl_tcp_rfc1337;
+
+static int prune_queue(struct sock *sk);
+
+/* There is something which you must keep in mind when you analyze the
+ * behavior of the tp->ato delayed ack timeout interval. When a
+ * connection starts up, we want to ack as quickly as possible. The
+ * problem is that "good" TCP's do slow start at the beginning of data
+ * transmission. The means that until we send the first few ACK's the
+ * sender will sit on his end and only queue most of his data, because
+ * he can only send snd_cwnd unacked packets at any given time. For
+ * each ACK we send, he increments snd_cwnd and transmits more of his
+ * queue. -DaveM
+ */
+static void tcp_delack_estimator(struct tcp_opt *tp)
+{
+ if(tp->ato == 0) {
+ tp->lrcvtime = tcp_time_stamp;
+
+ /* Help sender leave slow start quickly,
+ * and also makes sure we do not take this
+ * branch ever again for this connection.
+ */
+ tp->ato = 1;
+ tcp_enter_quickack_mode(tp);
+ } else {
+ int m = tcp_time_stamp - tp->lrcvtime;
+
+ tp->lrcvtime = tcp_time_stamp;
+ if(m <= 0)
+ m = 1;
+ if(m > tp->rto)
+ tp->ato = tp->rto;
+ else {
+ /* This funny shift makes sure we
+ * clear the "quick ack mode" bit.
+ */
+ tp->ato = ((tp->ato << 1) >> 2) + m;
+ }
+ }
+}
+
+/*
+ * Remember to send an ACK later.
+ */
+static __inline__ void tcp_remember_ack(struct tcp_opt *tp, struct tcphdr *th,
+ struct sk_buff *skb)
+{
+ tp->delayed_acks++;
+
+ /* Tiny-grams with PSH set artificially deflate our
+ * ato measurement, but with a lower bound.
+ */
+ if(th->psh && (skb->len < (tp->mss_cache >> 1))) {
+ /* Preserve the quickack state. */
+ if((tp->ato & 0x7fffffff) > HZ/50)
+ tp->ato = ((tp->ato & 0x80000000) |
+ (HZ/50));
+ }
+}
+
+/* Called to compute a smoothed rtt estimate. The data fed to this
+ * routine either comes from timestamps, or from segments that were
+ * known _not_ to have been retransmitted [see Karn/Partridge
+ * Proceedings SIGCOMM 87]. The algorithm is from the SIGCOMM 88
+ * piece by Van Jacobson.
+ * NOTE: the next three routines used to be one big routine.
+ * To save cycles in the RFC 1323 implementation it was better to break
+ * it up into three procedures. -- erics
+ */
+
+static __inline__ void tcp_rtt_estimator(struct tcp_opt *tp, __u32 mrtt)
+{
+ long m = mrtt; /* RTT */
+
+ /* The following amusing code comes from Jacobson's
+ * article in SIGCOMM '88. Note that rtt and mdev
+ * are scaled versions of rtt and mean deviation.
+ * This is designed to be as fast as possible
+ * m stands for "measurement".
+ *
+ * On a 1990 paper the rto value is changed to:
+ * RTO = rtt + 4 * mdev
+ */
+ if(m == 0)
+ m = 1;
+ if (tp->srtt != 0) {
+ m -= (tp->srtt >> 3); /* m is now error in rtt est */
+ tp->srtt += m; /* rtt = 7/8 rtt + 1/8 new */
+ if (m < 0)
+ m = -m; /* m is now abs(error) */
+ m -= (tp->mdev >> 2); /* similar update on mdev */
+ tp->mdev += m; /* mdev = 3/4 mdev + 1/4 new */
+ } else {
+ /* no previous measure. */
+ tp->srtt = m<<3; /* take the measured time to be rtt */
+ tp->mdev = m<<2; /* make sure rto = 3*rtt */
+ }
+}
+
+/* Calculate rto without backoff. This is the second half of Van Jacobson's
+ * routine referred to above.
+ */
+
+static __inline__ void tcp_set_rto(struct tcp_opt *tp)
+{
+ tp->rto = (tp->srtt >> 3) + tp->mdev;
+ tp->rto += (tp->rto >> 2) + (tp->rto >> (tp->snd_cwnd-1));
+}
+
+
+/* Keep the rto between HZ/5 and 120*HZ. 120*HZ is the upper bound
+ * on packet lifetime in the internet. We need the HZ/5 lower
+ * bound to behave correctly against BSD stacks with a fixed
+ * delayed ack.
+ * FIXME: It's not entirely clear this lower bound is the best
+ * way to avoid the problem. Is it possible to drop the lower
+ * bound and still avoid trouble with BSD stacks? Perhaps
+ * some modification to the RTO calculation that takes delayed
+ * ack bias into account? This needs serious thought. -- erics
+ */
+static __inline__ void tcp_bound_rto(struct tcp_opt *tp)
+{
+ if (tp->rto > 120*HZ)
+ tp->rto = 120*HZ;
+ if (tp->rto < HZ/5)
+ tp->rto = HZ/5;
+}
+
+/* WARNING: this must not be called if tp->saw_timestamp was false. */
+extern __inline__ void tcp_replace_ts_recent(struct sock *sk, struct tcp_opt *tp,
+ __u32 start_seq, __u32 end_seq)
+{
+ /* It is start_seq <= last_ack_seq combined
+ with in window check. If start_seq<=last_ack_seq<=rcv_nxt,
+ then segment is in window if end_seq>=rcv_nxt.
+ */
+ if (!after(start_seq, tp->last_ack_sent) &&
+ !before(end_seq, tp->rcv_nxt)) {
+ /* PAWS bug workaround wrt. ACK frames, the PAWS discard
+ * extra check below makes sure this can only happen
+ * for pure ACK frames. -DaveM
+ *
+ * Plus: expired timestamps.
+ *
+ * Plus: resets failing PAWS.
+ */
+ if((s32)(tp->rcv_tsval - tp->ts_recent) >= 0) {
+ tp->ts_recent = tp->rcv_tsval;
+ tp->ts_recent_stamp = tcp_time_stamp;
+ }
+ }
+}
+
+#define PAWS_24DAYS (HZ * 60 * 60 * 24 * 24)
+
+extern __inline__ int tcp_paws_discard(struct tcp_opt *tp, struct tcphdr *th, unsigned len)
+{
+ return ((s32)(tp->rcv_tsval - tp->ts_recent) < 0 &&
+ (s32)(tcp_time_stamp - tp->ts_recent_stamp) < PAWS_24DAYS &&
+ /* Sorry, PAWS as specified is broken wrt. pure-ACKs -DaveM */
+ len != (th->doff * 4));
+}
+
+
+static int __tcp_sequence(struct tcp_opt *tp, u32 seq, u32 end_seq)
+{
+ u32 end_window = tp->rcv_wup + tp->rcv_wnd;
+
+ if (tp->rcv_wnd &&
+ after(end_seq, tp->rcv_nxt) &&
+ before(seq, end_window))
+ return 1;
+ if (seq != end_window)
+ return 0;
+ return (seq == end_seq);
+}
+
+/* This functions checks to see if the tcp header is actually acceptable. */
+extern __inline__ int tcp_sequence(struct tcp_opt *tp, u32 seq, u32 end_seq)
+{
+ if (seq == tp->rcv_nxt)
+ return (tp->rcv_wnd || (end_seq == seq));
+
+ return __tcp_sequence(tp, seq, end_seq);
+}
+
+/* When we get a reset we do this. */
+static void tcp_reset(struct sock *sk)
+{
+ sk->zapped = 1;
+
+ /* We want the right error as BSD sees it (and indeed as we do). */
+ switch (sk->state) {
+ case TCP_SYN_SENT:
+ sk->err = ECONNREFUSED;
+ break;
+ case TCP_CLOSE_WAIT:
+ sk->err = EPIPE;
+ break;
+ default:
+ sk->err = ECONNRESET;
+ };
+ tcp_set_state(sk, TCP_CLOSE);
+ sk->shutdown = SHUTDOWN_MASK;
+ if (!sk->dead)
+ sk->state_change(sk);
+}
+
+/* This tags the retransmission queue when SACKs arrive. */
+static void tcp_sacktag_write_queue(struct sock *sk, struct tcp_sack_block *sp, int nsacks)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ int i = nsacks;
+
+ while(i--) {
+ struct sk_buff *skb = skb_peek(&sk->write_queue);
+ __u32 start_seq = ntohl(sp->start_seq);
+ __u32 end_seq = ntohl(sp->end_seq);
+ int fack_count = 0;
+
+ while((skb != NULL) &&
+ (skb != tp->send_head) &&
+ (skb != (struct sk_buff *)&sk->write_queue)) {
+ /* The retransmission queue is always in order, so
+ * we can short-circuit the walk early.
+ */
+ if(after(TCP_SKB_CB(skb)->seq, end_seq))
+ break;
+
+ /* We play conservative, we don't allow SACKS to partially
+ * tag a sequence space.
+ */
+ fack_count++;
+ if(!after(start_seq, TCP_SKB_CB(skb)->seq) &&
+ !before(end_seq, TCP_SKB_CB(skb)->end_seq)) {
+ /* If this was a retransmitted frame, account for it. */
+ if((TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) &&
+ tp->retrans_out)
+ tp->retrans_out--;
+ TCP_SKB_CB(skb)->sacked |= TCPCB_SACKED_ACKED;
+
+ /* RULE: All new SACKs will either decrease retrans_out
+ * or advance fackets_out.
+ */
+ if(fack_count > tp->fackets_out)
+ tp->fackets_out = fack_count;
+ }
+ skb = skb->next;
+ }
+ sp++; /* Move on to the next SACK block. */
+ }
+}
+
+/* Look for tcp options. Normally only called on SYN and SYNACK packets.
+ * But, this can also be called on packets in the established flow when
+ * the fast version below fails.
+ */
+void tcp_parse_options(struct sock *sk, struct tcphdr *th, struct tcp_opt *tp, int no_fancy)
+{
+ unsigned char *ptr;
+ int length=(th->doff*4)-sizeof(struct tcphdr);
+ int saw_mss = 0;
+
+ ptr = (unsigned char *)(th + 1);
+ tp->saw_tstamp = 0;
+
+ while(length>0) {
+ int opcode=*ptr++;
+ int opsize;
+
+ switch (opcode) {
+ case TCPOPT_EOL:
+ return;
+ case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */
+ length--;
+ continue;
+ default:
+ opsize=*ptr++;
+ if (opsize < 2) /* "silly options" */
+ return;
+ if (opsize > length)
+ break; /* don't parse partial options */
+ switch(opcode) {
+ case TCPOPT_MSS:
+ if(opsize==TCPOLEN_MSS && th->syn) {
+ u16 in_mss = ntohs(*(__u16 *)ptr);
+ if (in_mss == 0)
+ in_mss = 536;
+ if (tp->mss_clamp > in_mss)
+ tp->mss_clamp = in_mss;
+ saw_mss = 1;
+ }
+ break;
+ case TCPOPT_WINDOW:
+ if(opsize==TCPOLEN_WINDOW && th->syn)
+ if (!no_fancy && sysctl_tcp_window_scaling) {
+ tp->wscale_ok = 1;
+ tp->snd_wscale = *(__u8 *)ptr;
+ if(tp->snd_wscale > 14) {
+ if(net_ratelimit())
+ printk("tcp_parse_options: Illegal window "
+ "scaling value %d >14 received.",
+ tp->snd_wscale);
+ tp->snd_wscale = 14;
+ }
+ }
+ break;
+ case TCPOPT_TIMESTAMP:
+ if(opsize==TCPOLEN_TIMESTAMP) {
+ if (sysctl_tcp_timestamps && !no_fancy) {
+ tp->tstamp_ok = 1;
+ tp->saw_tstamp = 1;
+ tp->rcv_tsval = ntohl(*(__u32 *)ptr);
+ tp->rcv_tsecr = ntohl(*(__u32 *)(ptr+4));
+ }
+ }
+ break;
+ case TCPOPT_SACK_PERM:
+ if(opsize==TCPOLEN_SACK_PERM && th->syn) {
+ if (sysctl_tcp_sack && !no_fancy) {
+ tp->sack_ok = 1;
+ tp->num_sacks = 0;
+ }
+ }
+ break;
+
+ case TCPOPT_SACK:
+ if((opsize >= (TCPOLEN_SACK_BASE + TCPOLEN_SACK_PERBLOCK)) &&
+ sysctl_tcp_sack && (sk != NULL) && !th->syn) {
+ int sack_bytes = opsize - TCPOLEN_SACK_BASE;
+
+ if(!(sack_bytes % TCPOLEN_SACK_PERBLOCK)) {
+ int num_sacks = sack_bytes >> 3;
+ struct tcp_sack_block *sackp;
+
+ sackp = (struct tcp_sack_block *)ptr;
+ tcp_sacktag_write_queue(sk, sackp, num_sacks);
+ }
+ }
+ };
+ ptr+=opsize-2;
+ length-=opsize;
+ };
+ }
+ if(th->syn && saw_mss == 0)
+ tp->mss_clamp = 536;
+}
+
+/* Fast parse options. This hopes to only see timestamps.
+ * If it is wrong it falls back on tcp_parse_options().
+ */
+static __inline__ int tcp_fast_parse_options(struct sock *sk, struct tcphdr *th, struct tcp_opt *tp)
+{
+ /* If we didn't send out any options ignore them all. */
+ if (tp->tcp_header_len == sizeof(struct tcphdr))
+ return 0;
+ if (th->doff == sizeof(struct tcphdr)>>2) {
+ tp->saw_tstamp = 0;
+ return 0;
+ } else if (th->doff == (sizeof(struct tcphdr)>>2)+(TCPOLEN_TSTAMP_ALIGNED>>2)) {
+ __u32 *ptr = (__u32 *)(th + 1);
+ if (*ptr == __constant_ntohl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16)
+ | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP)) {
+ tp->saw_tstamp = 1;
+ tp->rcv_tsval = ntohl(*++ptr);
+ tp->rcv_tsecr = ntohl(*++ptr);
+ return 1;
+ }
+ }
+ tcp_parse_options(sk, th, tp, 0);
+ return 1;
+}
+
+#define FLAG_DATA 0x01 /* Incoming frame contained data. */
+#define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */
+#define FLAG_DATA_ACKED 0x04 /* This ACK acknowledged new data. */
+#define FLAG_RETRANS_DATA_ACKED 0x08 /* "" "" some of which was retransmitted. */
+
+static __inline__ void clear_fast_retransmit(struct tcp_opt *tp)
+{
+ if (tp->dup_acks > 3)
+ tp->snd_cwnd = (tp->snd_ssthresh);
+
+ tp->dup_acks = 0;
+}
+
+/* NOTE: This code assumes that tp->dup_acks gets cleared when a
+ * retransmit timer fires.
+ */
+static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ /* Note: If not_dup is set this implies we got a
+ * data carrying packet or a window update.
+ * This carries no new information about possible
+ * lost packets, so we have to ignore it for the purposes
+ * of counting duplicate acks. Ideally this does not imply we
+ * should stop our fast retransmit phase, more acks may come
+ * later without data to help us. Unfortunately this would make
+ * the code below much more complex. For now if I see such
+ * a packet I clear the fast retransmit phase.
+ */
+ if (ack == tp->snd_una && tp->packets_out && (not_dup == 0)) {
+ /* This is the standard reno style fast retransmit branch. */
+
+ /* 1. When the third duplicate ack is received, set ssthresh
+ * to one half the current congestion window, but no less
+ * than two segments. Retransmit the missing segment.
+ */
+ if (tp->high_seq == 0 || after(ack, tp->high_seq)) {
+ tp->dup_acks++;
+ if ((tp->fackets_out > 3) || (tp->dup_acks == 3)) {
+ tp->snd_ssthresh = tcp_recalc_ssthresh(tp);
+ tp->snd_cwnd = (tp->snd_ssthresh + 3);
+ tp->high_seq = tp->snd_nxt;
+ if(!tp->fackets_out)
+ tcp_retransmit_skb(sk,
+ skb_peek(&sk->write_queue));
+ else
+ tcp_fack_retransmit(sk);
+ tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+ }
+ } else if (++tp->dup_acks > 3) {
+ /* 2. Each time another duplicate ACK arrives, increment
+ * cwnd by the segment size. [...] Transmit a packet...
+ *
+ * Packet transmission will be done on normal flow processing
+ * since we're not in "retransmit mode". We do not use
+ * duplicate ACKs to artificially inflate the congestion
+ * window when doing FACK.
+ */
+ if(!tp->fackets_out) {
+ tp->snd_cwnd++;
+ } else {
+ /* Fill any further holes which may have
+ * appeared.
+ *
+ * We may want to change this to run every
+ * further multiple-of-3 dup ack increments,
+ * to be more robust against out-of-order
+ * packet delivery. -DaveM
+ */
+ tcp_fack_retransmit(sk);
+ }
+ }
+ } else if (tp->high_seq != 0) {
+ /* In this branch we deal with clearing the Floyd style
+ * block on duplicate fast retransmits, and if requested
+ * we do Hoe style secondary fast retransmits.
+ */
+ if (!before(ack, tp->high_seq) || (not_dup & FLAG_DATA) != 0) {
+ /* Once we have acked all the packets up to high_seq
+ * we are done this fast retransmit phase.
+ * Alternatively data arrived. In this case we
+ * Have to abort the fast retransmit attempt.
+ * Note that we do want to accept a window
+ * update since this is expected with Hoe's algorithm.
+ */
+ clear_fast_retransmit(tp);
+
+ /* After we have cleared up to high_seq we can
+ * clear the Floyd style block.
+ */
+ if (!before(ack, tp->high_seq)) {
+ tp->high_seq = 0;
+ tp->fackets_out = 0;
+ }
+ } else if (tp->dup_acks >= 3) {
+ if (!tp->fackets_out) {
+ /* Hoe Style. We didn't ack the whole
+ * window. Take this as a cue that
+ * another packet was lost and retransmit it.
+ * Don't muck with the congestion window here.
+ * Note that we have to be careful not to
+ * act if this was a window update and it
+ * didn't ack new data, since this does
+ * not indicate a packet left the system.
+ * We can test this by just checking
+ * if ack changed from snd_una, since
+ * the only way to get here without advancing
+ * from snd_una is if this was a window update.
+ */
+ if (ack != tp->snd_una && before(ack, tp->high_seq)) {
+ tcp_retransmit_skb(sk,
+ skb_peek(&sk->write_queue));
+ tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+ }
+ } else {
+ /* FACK style, fill any remaining holes in
+ * receiver's queue.
+ */
+ tcp_fack_retransmit(sk);
+ }
+ }
+ }
+}
+
+/* This is Jacobson's slow start and congestion avoidance.
+ * SIGCOMM '88, p. 328.
+ */
+static __inline__ void tcp_cong_avoid(struct tcp_opt *tp)
+{
+ if (tp->snd_cwnd <= tp->snd_ssthresh) {
+ /* In "safe" area, increase. */
+ tp->snd_cwnd++;
+ } else {
+ /* In dangerous area, increase slowly.
+ * In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd
+ */
+ if (tp->snd_cwnd_cnt >= tp->snd_cwnd) {
+ tp->snd_cwnd++;
+ tp->snd_cwnd_cnt=0;
+ } else
+ tp->snd_cwnd_cnt++;
+ }
+}
+
+/* Remove acknowledged frames from the retransmission queue. */
+static int tcp_clean_rtx_queue(struct sock *sk, __u32 ack,
+ __u32 *seq, __u32 *seq_rtt)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct sk_buff *skb;
+ __u32 now = tcp_time_stamp;
+ int acked = 0;
+
+ /* If we are retransmitting, and this ACK clears up to
+ * the retransmit head, or further, then clear our state.
+ */
+ if (tp->retrans_head != NULL &&
+ !before(ack, TCP_SKB_CB(tp->retrans_head)->end_seq))
+ tp->retrans_head = NULL;
+
+ while((skb=skb_peek(&sk->write_queue)) && (skb != tp->send_head)) {
+ struct tcp_skb_cb *scb = TCP_SKB_CB(skb);
+ __u8 sacked = scb->sacked;
+
+ /* If our packet is before the ack sequence we can
+ * discard it as it's confirmed to have arrived at
+ * the other end.
+ */
+ if (after(scb->end_seq, ack))
+ break;
+
+ /* Initial outgoing SYN's get put onto the write_queue
+ * just like anything else we transmit. It is not
+ * true data, and if we misinform our callers that
+ * this ACK acks real data, we will erroneously exit
+ * connection startup slow start one packet too
+ * quickly. This is severely frowned upon behavior.
+ */
+ if((sacked & TCPCB_SACKED_RETRANS) && tp->retrans_out)
+ tp->retrans_out--;
+ if(!(scb->flags & TCPCB_FLAG_SYN)) {
+ acked |= FLAG_DATA_ACKED;
+ if(sacked & TCPCB_SACKED_RETRANS)
+ acked |= FLAG_RETRANS_DATA_ACKED;
+ if(tp->fackets_out)
+ tp->fackets_out--;
+ } else {
+ /* This is pure paranoia. */
+ tp->retrans_head = NULL;
+ }
+ tp->packets_out--;
+ *seq = scb->seq;
+ *seq_rtt = now - scb->when;
+ __skb_unlink(skb, skb->list);
+ kfree_skb(skb);
+ }
+ return acked;
+}
+
+static void tcp_ack_probe(struct sock *sk, __u32 ack)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ /* Our probe was answered. */
+ tp->probes_out = 0;
+
+ /* Was it a usable window open? */
+
+ /* should always be non-null */
+ if (tp->send_head != NULL &&
+ !before (ack + tp->snd_wnd, TCP_SKB_CB(tp->send_head)->end_seq)) {
+ tp->backoff = 0;
+ tp->pending = 0;
+ tcp_clear_xmit_timer(sk, TIME_PROBE0);
+ } else {
+ tcp_reset_xmit_timer(sk, TIME_PROBE0,
+ min(tp->rto << tp->backoff, 120*HZ));
+ }
+}
+
+/* Should we open up the congestion window? */
+static __inline__ int should_advance_cwnd(struct tcp_opt *tp, int flag)
+{
+ /* Data must have been acked. */
+ if ((flag & FLAG_DATA_ACKED) == 0)
+ return 0;
+
+ /* Some of the data acked was retransmitted somehow? */
+ if ((flag & FLAG_RETRANS_DATA_ACKED) != 0) {
+ /* We advance in all cases except during
+ * non-FACK fast retransmit/recovery.
+ */
+ if (tp->fackets_out != 0 ||
+ tp->retransmits != 0)
+ return 1;
+
+ /* Non-FACK fast retransmit does it's own
+ * congestion window management, don't get
+ * in the way.
+ */
+ return 0;
+ }
+
+ /* New non-retransmitted data acked, always advance. */
+ return 1;
+}
+
+/* Read draft-ietf-tcplw-high-performance before mucking
+ * with this code. (Superceeds RFC1323)
+ */
+static void tcp_ack_saw_tstamp(struct sock *sk, struct tcp_opt *tp,
+ u32 seq, u32 ack, int flag)
+{
+ __u32 seq_rtt;
+
+ /* RTTM Rule: A TSecr value received in a segment is used to
+ * update the averaged RTT measurement only if the segment
+ * acknowledges some new data, i.e., only if it advances the
+ * left edge of the send window.
+ *
+ * See draft-ietf-tcplw-high-performance-00, section 3.3.
+ * 1998/04/10 Andrey V. Savochkin <saw@msu.ru>
+ */
+ if (!(flag & FLAG_DATA_ACKED))
+ return;
+
+ seq_rtt = tcp_time_stamp - tp->rcv_tsecr;
+ tcp_rtt_estimator(tp, seq_rtt);
+ if (tp->retransmits) {
+ if (tp->packets_out == 0) {
+ tp->retransmits = 0;
+ tp->fackets_out = 0;
+ tp->retrans_out = 0;
+ tp->backoff = 0;
+ tcp_set_rto(tp);
+ } else {
+ /* Still retransmitting, use backoff */
+ tcp_set_rto(tp);
+ tp->rto = tp->rto << tp->backoff;
+ }
+ } else {
+ tcp_set_rto(tp);
+ }
+
+ tcp_bound_rto(tp);
+}
+
+static __inline__ void tcp_ack_packets_out(struct sock *sk, struct tcp_opt *tp)
+{
+ struct sk_buff *skb = skb_peek(&sk->write_queue);
+
+ /* Some data was ACK'd, if still retransmitting (due to a
+ * timeout), resend more of the retransmit queue. The
+ * congestion window is handled properly by that code.
+ */
+ if (tp->retransmits) {
+ tcp_xmit_retransmit_queue(sk);
+ tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+ } else {
+ __u32 when = tp->rto - (tcp_time_stamp - TCP_SKB_CB(skb)->when);
+ if ((__s32)when < 0)
+ when = 1;
+ tcp_reset_xmit_timer(sk, TIME_RETRANS, when);
+ }
+}
+
+/* This routine deals with incoming acks, but not outgoing ones. */
+static int tcp_ack(struct sock *sk, struct tcphdr *th,
+ u32 ack_seq, u32 ack, int len)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ int flag = 0;
+ u32 seq = 0;
+ u32 seq_rtt = 0;
+
+ if(sk->zapped)
+ return(1); /* Dead, can't ack any more so why bother */
+
+ if (tp->pending == TIME_KEEPOPEN)
+ tp->probes_out = 0;
+
+ tp->rcv_tstamp = tcp_time_stamp;
+
+ /* If the ack is newer than sent or older than previous acks
+ * then we can probably ignore it.
+ */
+ if (after(ack, tp->snd_nxt) || before(ack, tp->snd_una))
+ goto uninteresting_ack;
+
+ /* If there is data set flag 1 */
+ if (len != th->doff*4) {
+ flag |= FLAG_DATA;
+ tcp_delack_estimator(tp);
+ }
+
+ /* Update our send window. */
+
+ /* This is the window update code as per RFC 793
+ * snd_wl{1,2} are used to prevent unordered
+ * segments from shrinking the window
+ */
+ if (before(tp->snd_wl1, ack_seq) ||
+ (tp->snd_wl1 == ack_seq && !after(tp->snd_wl2, ack))) {
+ u32 nwin = ntohs(th->window) << tp->snd_wscale;
+
+ if ((tp->snd_wl2 != ack) || (nwin > tp->snd_wnd)) {
+ flag |= FLAG_WIN_UPDATE;
+ tp->snd_wnd = nwin;
+
+ tp->snd_wl1 = ack_seq;
+ tp->snd_wl2 = ack;
+
+ if (nwin > tp->max_window)
+ tp->max_window = nwin;
+ }
+ }
+
+ /* We passed data and got it acked, remove any soft error
+ * log. Something worked...
+ */
+ sk->err_soft = 0;
+
+ /* If this ack opens up a zero window, clear backoff. It was
+ * being used to time the probes, and is probably far higher than
+ * it needs to be for normal retransmission.
+ */
+ if (tp->pending == TIME_PROBE0)
+ tcp_ack_probe(sk, ack);
+
+ /* See if we can take anything off of the retransmit queue. */
+ flag |= tcp_clean_rtx_queue(sk, ack, &seq, &seq_rtt);
+
+ /* We must do this here, before code below clears out important
+ * state contained in tp->fackets_out and tp->retransmits. -DaveM
+ */
+ if (should_advance_cwnd(tp, flag))
+ tcp_cong_avoid(tp);
+
+ /* If we have a timestamp, we always do rtt estimates. */
+ if (tp->saw_tstamp) {
+ tcp_ack_saw_tstamp(sk, tp, seq, ack, flag);
+ } else {
+ /* If we were retransmiting don't count rtt estimate. */
+ if (tp->retransmits) {
+ if (tp->packets_out == 0) {
+ tp->retransmits = 0;
+ tp->fackets_out = 0;
+ tp->retrans_out = 0;
+ }
+ } else {
+ /* We don't have a timestamp. Can only use
+ * packets that are not retransmitted to determine
+ * rtt estimates. Also, we must not reset the
+ * backoff for rto until we get a non-retransmitted
+ * packet. This allows us to deal with a situation
+ * where the network delay has increased suddenly.
+ * I.e. Karn's algorithm. (SIGCOMM '87, p5.)
+ */
+ if (flag & FLAG_DATA_ACKED) {
+ if(!(flag & FLAG_RETRANS_DATA_ACKED)) {
+ tp->backoff = 0;
+ tcp_rtt_estimator(tp, seq_rtt);
+ tcp_set_rto(tp);
+ tcp_bound_rto(tp);
+ }
+ }
+ }
+ }
+
+ if (tp->packets_out) {
+ if (flag & FLAG_DATA_ACKED)
+ tcp_ack_packets_out(sk, tp);
+ } else {
+ tcp_clear_xmit_timer(sk, TIME_RETRANS);
+ }
+
+ flag &= (FLAG_DATA | FLAG_WIN_UPDATE);
+ if ((ack == tp->snd_una && tp->packets_out && flag == 0) ||
+ (tp->high_seq != 0)) {
+ tcp_fast_retrans(sk, ack, flag);
+ } else {
+ /* Clear any aborted fast retransmit starts. */
+ tp->dup_acks = 0;
+ }
+ /* It is not a brain fart, I thought a bit now. 8)
+ *
+ * Forward progress is indicated, if:
+ * 1. the ack acknowledges new data.
+ * 2. or the ack is duplicate, but it is caused by new segment
+ * arrival. This case is filtered by:
+ * - it contains no data, syn or fin.
+ * - it does not update window.
+ * 3. or new SACK. It is difficult to check, so that we ignore it.
+ *
+ * Forward progress is also indicated by arrival new data,
+ * which was caused by window open from our side. This case is more
+ * difficult and it is made (alas, incorrectly) in tcp_data_queue().
+ * --ANK (990513)
+ */
+ if (ack != tp->snd_una || (flag == 0 && !th->fin))
+ dst_confirm(sk->dst_cache);
+
+ /* Remember the highest ack received. */
+ tp->snd_una = ack;
+ return 1;
+
+uninteresting_ack:
+ SOCK_DEBUG(sk, "Ack ignored %u %u\n", ack, tp->snd_nxt);
+ return 0;
+}
+
+/* New-style handling of TIME_WAIT sockets. */
+extern void tcp_tw_schedule(struct tcp_tw_bucket *tw);
+extern void tcp_tw_reschedule(struct tcp_tw_bucket *tw);
+extern void tcp_tw_deschedule(struct tcp_tw_bucket *tw);
+
+void tcp_timewait_kill(struct tcp_tw_bucket *tw)
+{
+ struct tcp_bind_bucket *tb = tw->tb;
+
+ /* Disassociate with bind bucket. */
+ if(tw->bind_next)
+ tw->bind_next->bind_pprev = tw->bind_pprev;
+ *(tw->bind_pprev) = tw->bind_next;
+ if (tb->owners == NULL) {
+ if (tb->next)
+ tb->next->pprev = tb->pprev;
+ *(tb->pprev) = tb->next;
+ kmem_cache_free(tcp_bucket_cachep, tb);
+ }
+
+ /* Unlink from established hashes. */
+ if(tw->next)
+ tw->next->pprev = tw->pprev;
+ *tw->pprev = tw->next;
+
+ /* We decremented the prot->inuse count when we entered TIME_WAIT
+ * and the sock from which this came was destroyed.
+ */
+ tw->sklist_next->sklist_prev = tw->sklist_prev;
+ tw->sklist_prev->sklist_next = tw->sklist_next;
+
+ /* Ok, now free it up. */
+ kmem_cache_free(tcp_timewait_cachep, tw);
+}
+
+/* We come here as a special case from the AF specific TCP input processing,
+ * and the SKB has no owner. Essentially handling this is very simple,
+ * we just keep silently eating rx'd packets, acking them if necessary,
+ * until none show up for the entire timeout period.
+ *
+ * Return 0, TCP_TW_ACK, TCP_TW_RST
+ */
+enum tcp_tw_status
+tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb,
+ struct tcphdr *th, unsigned len)
+{
+ /* RFC 1122:
+ * "When a connection is [...] on TIME-WAIT state [...]
+ * [a TCP] MAY accept a new SYN from the remote TCP to
+ * reopen the connection directly, if it:
+ *
+ * (1) assigns its initial sequence number for the new
+ * connection to be larger than the largest sequence
+ * number it used on the previous connection incarnation,
+ * and
+ *
+ * (2) returns to TIME-WAIT state if the SYN turns out
+ * to be an old duplicate".
+ */
+ if(th->syn && !th->rst && after(TCP_SKB_CB(skb)->seq, tw->rcv_nxt)) {
+ struct sock *sk;
+ struct tcp_func *af_specific = tw->af_specific;
+ __u32 isn;
+
+ isn = tw->snd_nxt + 128000;
+ if(isn == 0)
+ isn++;
+ tcp_tw_deschedule(tw);
+ tcp_timewait_kill(tw);
+ sk = af_specific->get_sock(skb, th);
+ if(sk == NULL ||
+ !ipsec_sk_policy(sk,skb) ||
+ atomic_read(&sk->sock_readers) != 0)
+ return 0;
+ skb_set_owner_r(skb, sk);
+ af_specific = sk->tp_pinfo.af_tcp.af_specific;
+ if(af_specific->conn_request(sk, skb, isn) < 0)
+ return TCP_TW_RST; /* Toss a reset back. */
+ return 0; /* Discard the frame. */
+ }
+
+ /* Check RST or SYN */
+ if(th->rst || th->syn) {
+ /* This is TIME_WAIT assassination, in two flavors.
+ * Oh well... nobody has a sufficient solution to this
+ * protocol bug yet.
+ */
+ if(sysctl_tcp_rfc1337 == 0) {
+ tcp_tw_deschedule(tw);
+ tcp_timewait_kill(tw);
+ }
+ if(!th->rst)
+ return TCP_TW_RST; /* toss a reset back */
+ return 0;
+ } else {
+ /* In this case we must reset the TIMEWAIT timer. */
+ if(th->ack)
+ tcp_tw_reschedule(tw);
+ }
+ /* Ack old packets if necessary */
+ if (!after(TCP_SKB_CB(skb)->end_seq, tw->rcv_nxt) &&
+ (th->doff * 4) > len)
+ return TCP_TW_ACK;
+ return 0;
+}
+
+/* Enter the time wait state. This is always called from BH
+ * context. Essentially we whip up a timewait bucket, copy the
+ * relevant info into it from the SK, and mess with hash chains
+ * and list linkage.
+ */
+static __inline__ void tcp_tw_hashdance(struct sock *sk, struct tcp_tw_bucket *tw)
+{
+ struct sock **head, *sktw;
+
+ /* Step 1: Remove SK from established hash. */
+ if(sk->next)
+ sk->next->pprev = sk->pprev;
+ *sk->pprev = sk->next;
+ sk->pprev = NULL;
+ tcp_reg_zap(sk);
+
+ /* Step 2: Put TW into bind hash where SK was. */
+ tw->tb = (struct tcp_bind_bucket *)sk->prev;
+ if((tw->bind_next = sk->bind_next) != NULL)
+ sk->bind_next->bind_pprev = &tw->bind_next;
+ tw->bind_pprev = sk->bind_pprev;
+ *sk->bind_pprev = (struct sock *)tw;
+ sk->prev = NULL;
+
+ /* Step 3: Same for the protocol sklist. */
+ (tw->sklist_next = sk->sklist_next)->sklist_prev = (struct sock *)tw;
+ (tw->sklist_prev = sk->sklist_prev)->sklist_next = (struct sock *)tw;
+ sk->sklist_next = NULL;
+ sk->prot->inuse--;
+
+ /* Step 4: Hash TW into TIMEWAIT half of established hash table. */
+ head = &tcp_ehash[sk->hashent + (tcp_ehash_size/2)];
+ sktw = (struct sock *)tw;
+ if((sktw->next = *head) != NULL)
+ (*head)->pprev = &sktw->next;
+ *head = sktw;
+ sktw->pprev = head;
+}
+
+void tcp_time_wait(struct sock *sk)
+{
+ struct tcp_tw_bucket *tw;
+
+ tw = kmem_cache_alloc(tcp_timewait_cachep, SLAB_ATOMIC);
+ if(tw != NULL) {
+ /* Give us an identity. */
+ tw->daddr = sk->daddr;
+ tw->rcv_saddr = sk->rcv_saddr;
+ tw->bound_dev_if= sk->bound_dev_if;
+ tw->num = sk->num;
+ tw->state = TCP_TIME_WAIT;
+ tw->sport = sk->sport;
+ tw->dport = sk->dport;
+ tw->family = sk->family;
+ tw->reuse = sk->reuse;
+ tw->rcv_nxt = sk->tp_pinfo.af_tcp.rcv_nxt;
+ tw->snd_nxt = sk->tp_pinfo.af_tcp.snd_nxt;
+ tw->window = tcp_select_window(sk);
+ tw->af_specific = sk->tp_pinfo.af_tcp.af_specific;
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ if(tw->family == PF_INET6) {
+ memcpy(&tw->v6_daddr,
+ &sk->net_pinfo.af_inet6.daddr,
+ sizeof(struct in6_addr));
+ memcpy(&tw->v6_rcv_saddr,
+ &sk->net_pinfo.af_inet6.rcv_saddr,
+ sizeof(struct in6_addr));
+ }
+#endif
+ /* Linkage updates. */
+ tcp_tw_hashdance(sk, tw);
+
+ /* Get the TIME_WAIT timeout firing. */
+ tcp_tw_schedule(tw);
+
+ /* CLOSE the SK. */
+ if(sk->state == TCP_ESTABLISHED)
+ tcp_statistics.TcpCurrEstab--;
+ sk->state = TCP_CLOSE;
+ net_reset_timer(sk, TIME_DONE,
+ min(sk->tp_pinfo.af_tcp.srtt * 2, TCP_DONE_TIME));
+ } else {
+ /* Sorry, we're out of memory, just CLOSE this
+ * socket up. We've got bigger problems than
+ * non-graceful socket closings.
+ */
+ tcp_set_state(sk, TCP_CLOSE);
+ }
+
+ /* Prevent rcvmsg/sndmsg calls, and wake people up. */
+ sk->shutdown = SHUTDOWN_MASK;
+ if(!sk->dead)
+ sk->state_change(sk);
+}
+
+/*
+ * Process the FIN bit. This now behaves as it is supposed to work
+ * and the FIN takes effect when it is validly part of sequence
+ * space. Not before when we get holes.
+ *
+ * If we are ESTABLISHED, a received fin moves us to CLOSE-WAIT
+ * (and thence onto LAST-ACK and finally, CLOSE, we never enter
+ * TIME-WAIT)
+ *
+ * If we are in FINWAIT-1, a received FIN indicates simultaneous
+ * close and we go into CLOSING (and later onto TIME-WAIT)
+ *
+ * If we are in FINWAIT-2, a received FIN moves us to TIME-WAIT.
+ */
+
+static void tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th)
+{
+ sk->tp_pinfo.af_tcp.fin_seq = TCP_SKB_CB(skb)->end_seq;
+
+ tcp_send_ack(sk);
+
+ if (!sk->dead) {
+ sk->state_change(sk);
+ sock_wake_async(sk->socket, 1);
+ }
+
+ switch(sk->state) {
+ case TCP_SYN_RECV:
+ case TCP_ESTABLISHED:
+ /* Move to CLOSE_WAIT */
+ tcp_set_state(sk, TCP_CLOSE_WAIT);
+ if (th->rst)
+ sk->shutdown = SHUTDOWN_MASK;
+ break;
+
+ case TCP_CLOSE_WAIT:
+ case TCP_CLOSING:
+ /* Received a retransmission of the FIN, do
+ * nothing.
+ */
+ break;
+ case TCP_LAST_ACK:
+ /* RFC793: Remain in the LAST-ACK state. */
+ break;
+
+ case TCP_FIN_WAIT1:
+ /* This case occurs when a simultaneous close
+ * happens, we must ack the received FIN and
+ * enter the CLOSING state.
+ *
+ * This causes a WRITE timeout, which will either
+ * move on to TIME_WAIT when we timeout, or resend
+ * the FIN properly (maybe we get rid of that annoying
+ * FIN lost hang). The TIME_WRITE code is already
+ * correct for handling this timeout.
+ */
+ tcp_set_state(sk, TCP_CLOSING);
+ break;
+ case TCP_FIN_WAIT2:
+ /* Received a FIN -- send ACK and enter TIME_WAIT. */
+ tcp_time_wait(sk);
+ break;
+ default:
+ /* Only TCP_LISTEN and TCP_CLOSE are left, in these
+ * cases we should never reach this piece of code.
+ */
+ printk("tcp_fin: Impossible, sk->state=%d\n", sk->state);
+ break;
+ };
+}
+
+/* These routines update the SACK block as out-of-order packets arrive or
+ * in-order packets close up the sequence space.
+ */
+static void tcp_sack_maybe_coalesce(struct tcp_opt *tp, struct tcp_sack_block *sp)
+{
+ int this_sack, num_sacks = tp->num_sacks;
+ struct tcp_sack_block *swalk = &tp->selective_acks[0];
+
+ /* If more than one SACK block, see if the recent change to SP eats into
+ * or hits the sequence space of other SACK blocks, if so coalesce.
+ */
+ if(num_sacks != 1) {
+ for(this_sack = 0; this_sack < num_sacks; this_sack++, swalk++) {
+ if(swalk == sp)
+ continue;
+
+ /* First case, bottom of SP moves into top of the
+ * sequence space of SWALK.
+ */
+ if(between(sp->start_seq, swalk->start_seq, swalk->end_seq)) {
+ sp->start_seq = swalk->start_seq;
+ goto coalesce;
+ }
+ /* Second case, top of SP moves into bottom of the
+ * sequence space of SWALK.
+ */
+ if(between(sp->end_seq, swalk->start_seq, swalk->end_seq)) {
+ sp->end_seq = swalk->end_seq;
+ goto coalesce;
+ }
+ }
+ }
+ /* SP is the only SACK, or no coalescing cases found. */
+ return;
+
+coalesce:
+ /* Zap SWALK, by moving every further SACK up by one slot.
+ * Decrease num_sacks.
+ */
+ for(; this_sack < num_sacks-1; this_sack++, swalk++) {
+ struct tcp_sack_block *next = (swalk + 1);
+ swalk->start_seq = next->start_seq;
+ swalk->end_seq = next->end_seq;
+ }
+ tp->num_sacks--;
+}
+
+static __inline__ void tcp_sack_swap(struct tcp_sack_block *sack1, struct tcp_sack_block *sack2)
+{
+ __u32 tmp;
+
+ tmp = sack1->start_seq;
+ sack1->start_seq = sack2->start_seq;
+ sack2->start_seq = tmp;
+
+ tmp = sack1->end_seq;
+ sack1->end_seq = sack2->end_seq;
+ sack2->end_seq = tmp;
+}
+
+static void tcp_sack_new_ofo_skb(struct sock *sk, struct sk_buff *skb)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct tcp_sack_block *sp = &tp->selective_acks[0];
+ int cur_sacks = tp->num_sacks;
+
+ if (!cur_sacks)
+ goto new_sack;
+
+ /* Optimize for the common case, new ofo frames arrive
+ * "in order". ;-) This also satisfies the requirements
+ * of RFC2018 about ordering of SACKs.
+ */
+ if(sp->end_seq == TCP_SKB_CB(skb)->seq) {
+ sp->end_seq = TCP_SKB_CB(skb)->end_seq;
+ tcp_sack_maybe_coalesce(tp, sp);
+ } else if(sp->start_seq == TCP_SKB_CB(skb)->end_seq) {
+ /* Re-ordered arrival, in this case, can be optimized
+ * as well.
+ */
+ sp->start_seq = TCP_SKB_CB(skb)->seq;
+ tcp_sack_maybe_coalesce(tp, sp);
+ } else {
+ struct tcp_sack_block *swap = sp + 1;
+ int this_sack, max_sacks = (tp->tstamp_ok ? 3 : 4);
+
+ /* Oh well, we have to move things around.
+ * Try to find a SACK we can tack this onto.
+ */
+
+ for(this_sack = 1; this_sack < cur_sacks; this_sack++, swap++) {
+ if((swap->end_seq == TCP_SKB_CB(skb)->seq) ||
+ (swap->start_seq == TCP_SKB_CB(skb)->end_seq)) {
+ if(swap->end_seq == TCP_SKB_CB(skb)->seq)
+ swap->end_seq = TCP_SKB_CB(skb)->end_seq;
+ else
+ swap->start_seq = TCP_SKB_CB(skb)->seq;
+ tcp_sack_swap(sp, swap);
+ tcp_sack_maybe_coalesce(tp, sp);
+ return;
+ }
+ }
+
+ /* Could not find an adjacent existing SACK, build a new one,
+ * put it at the front, and shift everyone else down. We
+ * always know there is at least one SACK present already here.
+ *
+ * If the sack array is full, forget about the last one.
+ */
+ if (cur_sacks >= max_sacks) {
+ cur_sacks--;
+ tp->num_sacks--;
+ }
+ while(cur_sacks >= 1) {
+ struct tcp_sack_block *this = &tp->selective_acks[cur_sacks];
+ struct tcp_sack_block *prev = (this - 1);
+ this->start_seq = prev->start_seq;
+ this->end_seq = prev->end_seq;
+ cur_sacks--;
+ }
+
+ new_sack:
+ /* Build the new head SACK, and we're done. */
+ sp->start_seq = TCP_SKB_CB(skb)->seq;
+ sp->end_seq = TCP_SKB_CB(skb)->end_seq;
+ tp->num_sacks++;
+ }
+}
+
+static void tcp_sack_remove_skb(struct tcp_opt *tp, struct sk_buff *skb)
+{
+ struct tcp_sack_block *sp = &tp->selective_acks[0];
+ int num_sacks = tp->num_sacks;
+ int this_sack;
+
+ /* This is an in order data segment _or_ an out-of-order SKB being
+ * moved to the receive queue, so we know this removed SKB will eat
+ * from the front of a SACK.
+ */
+ for(this_sack = 0; this_sack < num_sacks; this_sack++, sp++) {
+ /* Check if the start of the sack is covered by skb. */
+ if(!before(sp->start_seq, TCP_SKB_CB(skb)->seq) &&
+ before(sp->start_seq, TCP_SKB_CB(skb)->end_seq))
+ break;
+ }
+
+ /* This should only happen if so many SACKs get built that some get
+ * pushed out before we get here, or we eat some in sequence packets
+ * which are before the first SACK block.
+ */
+ if(this_sack >= num_sacks)
+ return;
+
+ sp->start_seq = TCP_SKB_CB(skb)->end_seq;
+ if(!before(sp->start_seq, sp->end_seq)) {
+ /* Zap this SACK, by moving forward any other SACKS. */
+ for(this_sack += 1; this_sack < num_sacks; this_sack++, sp++) {
+ struct tcp_sack_block *next = (sp + 1);
+ sp->start_seq = next->start_seq;
+ sp->end_seq = next->end_seq;
+ }
+ tp->num_sacks--;
+ }
+}
+
+static void tcp_sack_extend(struct tcp_opt *tp, struct sk_buff *old_skb, struct sk_buff *new_skb)
+{
+ struct tcp_sack_block *sp = &tp->selective_acks[0];
+ int num_sacks = tp->num_sacks;
+ int this_sack;
+
+ for(this_sack = 0; this_sack < num_sacks; this_sack++, sp++) {
+ if(sp->end_seq == TCP_SKB_CB(old_skb)->end_seq)
+ break;
+ }
+ if(this_sack >= num_sacks)
+ return;
+ sp->end_seq = TCP_SKB_CB(new_skb)->end_seq;
+}
+
+/* This one checks to see if we can put data from the
+ * out_of_order queue into the receive_queue.
+ */
+static void tcp_ofo_queue(struct sock *sk)
+{
+ struct sk_buff *skb;
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ while ((skb = skb_peek(&tp->out_of_order_queue))) {
+ if (after(TCP_SKB_CB(skb)->seq, tp->rcv_nxt))
+ break;
+
+ if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) {
+ SOCK_DEBUG(sk, "ofo packet was already received \n");
+ __skb_unlink(skb, skb->list);
+ kfree_skb(skb);
+ continue;
+ }
+ SOCK_DEBUG(sk, "ofo requeuing : rcv_next %X seq %X - %X\n",
+ tp->rcv_nxt, TCP_SKB_CB(skb)->seq,
+ TCP_SKB_CB(skb)->end_seq);
+
+ if(tp->sack_ok)
+ tcp_sack_remove_skb(tp, skb);
+ __skb_unlink(skb, skb->list);
+ __skb_queue_tail(&sk->receive_queue, skb);
+ tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
+ if(skb->h.th->fin)
+ tcp_fin(skb, sk, skb->h.th);
+ }
+}
+
+static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
+{
+ struct sk_buff *skb1;
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ /* Queue data for delivery to the user.
+ * Packets in sequence go to the receive queue.
+ * Out of sequence packets to the out_of_order_queue.
+ */
+ if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt) {
+ /* Ok. In sequence. */
+ queue_and_out:
+ dst_confirm(sk->dst_cache);
+ __skb_queue_tail(&sk->receive_queue, skb);
+ tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
+ if(skb->h.th->fin) {
+ tcp_fin(skb, sk, skb->h.th);
+ } else {
+ tcp_remember_ack(tp, skb->h.th, skb);
+ }
+ /* This may have eaten into a SACK block. */
+ if(tp->sack_ok && tp->num_sacks)
+ tcp_sack_remove_skb(tp, skb);
+ tcp_ofo_queue(sk);
+
+ /* Turn on fast path. */
+ if (skb_queue_len(&tp->out_of_order_queue) == 0)
+ tp->pred_flags = htonl(((tp->tcp_header_len >> 2) << 28) |
+ (0x10 << 16) |
+ tp->snd_wnd);
+ return;
+ }
+
+ /* An old packet, either a retransmit or some packet got lost. */
+ if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) {
+ /* A retransmit, 2nd most common case. Force an imediate ack. */
+ SOCK_DEBUG(sk, "retransmit received: seq %X\n", TCP_SKB_CB(skb)->seq);
+ tcp_enter_quickack_mode(tp);
+ kfree_skb(skb);
+ return;
+ }
+
+ if (before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
+ /* Partial packet, seq < rcv_next < end_seq */
+ SOCK_DEBUG(sk, "partial packet: rcv_next %X seq %X - %X\n",
+ tp->rcv_nxt, TCP_SKB_CB(skb)->seq,
+ TCP_SKB_CB(skb)->end_seq);
+
+ goto queue_and_out;
+ }
+
+ /* Ok. This is an out_of_order segment, force an ack. */
+ tp->delayed_acks++;
+ tcp_enter_quickack_mode(tp);
+
+ /* Disable header prediction. */
+ tp->pred_flags = 0;
+
+ SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X\n",
+ tp->rcv_nxt, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);
+
+ if (skb_peek(&tp->out_of_order_queue) == NULL) {
+ /* Initial out of order segment, build 1 SACK. */
+ if(tp->sack_ok) {
+ tp->num_sacks = 1;
+ tp->selective_acks[0].start_seq = TCP_SKB_CB(skb)->seq;
+ tp->selective_acks[0].end_seq = TCP_SKB_CB(skb)->end_seq;
+ }
+ __skb_queue_head(&tp->out_of_order_queue,skb);
+ } else {
+ for(skb1=tp->out_of_order_queue.prev; ; skb1 = skb1->prev) {
+ /* Already there. */
+ if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb1)->seq) {
+ if (skb->len >= skb1->len) {
+ if(tp->sack_ok)
+ tcp_sack_extend(tp, skb1, skb);
+ __skb_append(skb1, skb);
+ __skb_unlink(skb1, skb1->list);
+ kfree_skb(skb1);
+ } else {
+ /* A duplicate, smaller than what is in the
+ * out-of-order queue right now, toss it.
+ */
+ kfree_skb(skb);
+ }
+ break;
+ }
+
+ if (after(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb1)->seq)) {
+ __skb_append(skb1, skb);
+ if(tp->sack_ok)
+ tcp_sack_new_ofo_skb(sk, skb);
+ break;
+ }
+
+ /* See if we've hit the start. If so insert. */
+ if (skb1 == skb_peek(&tp->out_of_order_queue)) {
+ __skb_queue_head(&tp->out_of_order_queue,skb);
+ if(tp->sack_ok)
+ tcp_sack_new_ofo_skb(sk, skb);
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * This routine handles the data. If there is room in the buffer,
+ * it will be have already been moved into it. If there is no
+ * room, then we will just have to discard the packet.
+ */
+
+static int tcp_data(struct sk_buff *skb, struct sock *sk, unsigned int len)
+{
+ struct tcphdr *th;
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ th = skb->h.th;
+ skb_pull(skb, th->doff*4);
+ skb_trim(skb, len - (th->doff*4));
+
+ if (skb->len == 0 && !th->fin)
+ return(0);
+
+ /*
+ * If our receive queue has grown past its limits shrink it.
+ * Make sure to do this before moving snd_nxt, otherwise
+ * data might be acked for that we don't have enough room.
+ */
+ if (atomic_read(&sk->rmem_alloc) > sk->rcvbuf) {
+ if (prune_queue(sk) < 0) {
+ /* Still not enough room. That can happen when
+ * skb->true_size differs significantly from skb->len.
+ */
+ return 0;
+ }
+ }
+
+ tcp_data_queue(sk, skb);
+
+ if (before(tp->rcv_nxt, tp->copied_seq)) {
+ printk(KERN_DEBUG "*** tcp.c:tcp_data bug acked < copied\n");
+ tp->rcv_nxt = tp->copied_seq;
+ }
+
+ /* Above, tcp_data_queue() increments delayed_acks appropriately.
+ * Now tell the user we may have some data.
+ */
+ if (!sk->dead) {
+ sk->data_ready(sk,0);
+ }
+ return(1);
+}
+
+static void __tcp_data_snd_check(struct sock *sk, struct sk_buff *skb)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ if (!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una + tp->snd_wnd) &&
+ tcp_packets_in_flight(tp) < tp->snd_cwnd) {
+ /* Put more data onto the wire. */
+ tcp_write_xmit(sk);
+ } else if (tp->packets_out == 0 && !tp->pending) {
+ /* Start probing the receivers window. */
+ tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto);
+ }
+}
+
+static __inline__ void tcp_data_snd_check(struct sock *sk)
+{
+ struct sk_buff *skb = sk->tp_pinfo.af_tcp.send_head;
+
+ if (skb != NULL)
+ __tcp_data_snd_check(sk, skb);
+}
+
+/*
+ * Adapt the MSS value used to make delayed ack decision to the
+ * real world.
+ */
+static __inline__ void tcp_measure_rcv_mss(struct sock *sk, struct sk_buff *skb)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ unsigned int len = skb->len, lss;
+
+ if (len > tp->rcv_mss)
+ tp->rcv_mss = len;
+ lss = tp->last_seg_size;
+ tp->last_seg_size = 0;
+ if (len >= 536) {
+ if (len == lss)
+ tp->rcv_mss = len;
+ tp->last_seg_size = len;
+ }
+}
+
+/*
+ * Check if sending an ack is needed.
+ */
+static __inline__ void __tcp_ack_snd_check(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ /* This also takes care of updating the window.
+ * This if statement needs to be simplified.
+ *
+ * Rules for delaying an ack:
+ * - delay time <= 0.5 HZ
+ * - we don't have a window update to send
+ * - must send at least every 2 full sized packets
+ * - must send an ACK if we have any out of order data
+ *
+ * With an extra heuristic to handle loss of packet
+ * situations and also helping the sender leave slow
+ * start in an expediant manner.
+ */
+
+ /* Two full frames received or... */
+ if (((tp->rcv_nxt - tp->rcv_wup) >= tp->rcv_mss * MAX_DELAY_ACK) ||
+ /* We will update the window "significantly" or... */
+ tcp_raise_window(sk) ||
+ /* We entered "quick ACK" mode or... */
+ tcp_in_quickack_mode(tp) ||
+ /* We have out of order data */
+ (skb_peek(&tp->out_of_order_queue) != NULL)) {
+ /* Then ack it now */
+ tcp_send_ack(sk);
+ } else {
+ /* Else, send delayed ack. */
+ tcp_send_delayed_ack(tp, HZ/2);
+ }
+}
+
+static __inline__ void tcp_ack_snd_check(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ if (tp->delayed_acks == 0) {
+ /* We sent a data segment already. */
+ return;
+ }
+ __tcp_ack_snd_check(sk);
+}
+
+
+/*
+ * This routine is only called when we have urgent data
+ * signalled. Its the 'slow' part of tcp_urg. It could be
+ * moved inline now as tcp_urg is only called from one
+ * place. We handle URGent data wrong. We have to - as
+ * BSD still doesn't use the correction from RFC961.
+ * For 1003.1g we should support a new option TCP_STDURG to permit
+ * either form (or just set the sysctl tcp_stdurg).
+ */
+
+static void tcp_check_urg(struct sock * sk, struct tcphdr * th)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ u32 ptr = ntohs(th->urg_ptr);
+
+ if (ptr && !sysctl_tcp_stdurg)
+ ptr--;
+ ptr += ntohl(th->seq);
+
+ /* Ignore urgent data that we've already seen and read. */
+ if (after(tp->copied_seq, ptr))
+ return;
+
+ /* Do we already have a newer (or duplicate) urgent pointer? */
+ if (tp->urg_data && !after(ptr, tp->urg_seq))
+ return;
+
+ /* Tell the world about our new urgent pointer. */
+ if (sk->proc != 0) {
+ if (sk->proc > 0)
+ kill_proc(sk->proc, SIGURG, 1);
+ else
+ kill_pg(-sk->proc, SIGURG, 1);
+ }
+
+ /* We may be adding urgent data when the last byte read was
+ * urgent. To do this requires some care. We cannot just ignore
+ * tp->copied_seq since we would read the last urgent byte again
+ * as data, nor can we alter copied_seq until this data arrives
+ * or we break the sematics of SIOCATMARK (and thus sockatmark())
+ */
+ if (tp->urg_seq == tp->copied_seq)
+ tp->copied_seq++; /* Move the copied sequence on correctly */
+ tp->urg_data = URG_NOTYET;
+ tp->urg_seq = ptr;
+
+ /* Disable header prediction. */
+ tp->pred_flags = 0;
+}
+
+/* This is the 'fast' part of urgent handling. */
+static inline void tcp_urg(struct sock *sk, struct tcphdr *th, unsigned long len)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ /* Check if we get a new urgent pointer - normally not. */
+ if (th->urg)
+ tcp_check_urg(sk,th);
+
+ /* Do we wait for any urgent data? - normally not... */
+ if (tp->urg_data == URG_NOTYET) {
+ u32 ptr = tp->urg_seq - ntohl(th->seq) + (th->doff*4);
+
+ /* Is the urgent pointer pointing into this packet? */
+ if (ptr < len) {
+ tp->urg_data = URG_VALID | *(ptr + (unsigned char *) th);
+ if (!sk->dead)
+ sk->data_ready(sk,0);
+ }
+ }
+}
+
+/* Clean the out_of_order queue if we can, trying to get
+ * the socket within its memory limits again.
+ *
+ * Return less than zero if we should start dropping frames
+ * until the socket owning process reads some of the data
+ * to stabilize the situation.
+ */
+static int prune_queue(struct sock *sk)
+{
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+ struct sk_buff * skb;
+
+ SOCK_DEBUG(sk, "prune_queue: c=%x\n", tp->copied_seq);
+
+ net_statistics.PruneCalled++;
+
+ /* First, purge the out_of_order queue. */
+ skb = __skb_dequeue_tail(&tp->out_of_order_queue);
+ if(skb != NULL) {
+ /* Free it all. */
+ do { net_statistics.OfoPruned += skb->len;
+ kfree_skb(skb);
+ skb = __skb_dequeue_tail(&tp->out_of_order_queue);
+ } while(skb != NULL);
+
+ /* Reset SACK state. A conforming SACK implementation will
+ * do the same at a timeout based retransmit. When a connection
+ * is in a sad state like this, we care only about integrity
+ * of the connection not performance.
+ */
+ if(tp->sack_ok)
+ tp->num_sacks = 0;
+ }
+
+ /* If we are really being abused, tell the caller to silently
+ * drop receive data on the floor. It will get retransmitted
+ * and hopefully then we'll have sufficient space.
+ *
+ * We used to try to purge the in-order packets too, but that
+ * turns out to be deadly and fraught with races. Consider:
+ *
+ * 1) If we acked the data, we absolutely cannot drop the
+ * packet. This data would then never be retransmitted.
+ * 2) It is possible, with a proper sequence of events involving
+ * delayed acks and backlog queue handling, to have the user
+ * read the data before it gets acked. The previous code
+ * here got this wrong, and it lead to data corruption.
+ * 3) Too much state changes happen when the FIN arrives, so once
+ * we've seen that we can't remove any in-order data safely.
+ *
+ * The net result is that removing in-order receive data is too
+ * complex for anyones sanity. So we don't do it anymore. But
+ * if we are really having our buffer space abused we stop accepting
+ * new receive data.
+ */
+ if(atomic_read(&sk->rmem_alloc) < (sk->rcvbuf << 1))
+ return 0;
+
+ /* Massive buffer overcommit. */
+ return -1;
+}
+
+/*
+ * TCP receive function for the ESTABLISHED state.
+ *
+ * It is split into a fast path and a slow path. The fast path is
+ * disabled when:
+ * - A zero window was announced from us - zero window probing
+ * is only handled properly in the slow path.
+ * - Out of order segments arrived.
+ * - Urgent data is expected.
+ * - There is no buffer space left
+ * - Unexpected TCP flags/window values/header lengths are received
+ * (detected by checking the TCP header against pred_flags)
+ * - Data is sent in both directions. Fast path only supports pure senders
+ * or pure receivers (this means either the sequence number or the ack
+ * value must stay constant)
+ *
+ * When these conditions are not satisfied it drops into a standard
+ * receive procedure patterned after RFC793 to handle all cases.
+ * The first three cases are guaranteed by proper pred_flags setting,
+ * the rest is checked inline. Fast processing is turned on in
+ * tcp_data_queue when everything is OK.
+ */
+int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
+ struct tcphdr *th, unsigned len)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ int queued;
+ u32 flg;
+
+ /*
+ * Header prediction.
+ * The code follows the one in the famous
+ * "30 instruction TCP receive" Van Jacobson mail.
+ *
+ * Van's trick is to deposit buffers into socket queue
+ * on a device interrupt, to call tcp_recv function
+ * on the receive process context and checksum and copy
+ * the buffer to user space. smart...
+ *
+ * Our current scheme is not silly either but we take the
+ * extra cost of the net_bh soft interrupt processing...
+ * We do checksum and copy also but from device to kernel.
+ */
+
+ /*
+ * RFC1323: H1. Apply PAWS check first.
+ */
+ if (tcp_fast_parse_options(sk, th, tp)) {
+ if (tp->saw_tstamp) {
+ if (tcp_paws_discard(tp, th, len)) {
+ tcp_statistics.TcpInErrs++;
+ if (!th->rst) {
+ tcp_send_ack(sk);
+ goto discard;
+ }
+ }
+ tcp_replace_ts_recent(sk, tp,
+ TCP_SKB_CB(skb)->seq,
+ TCP_SKB_CB(skb)->end_seq);
+ }
+ }
+
+ flg = *(((u32 *)th) + 3) & ~htonl(0xFC8 << 16);
+
+ /* pred_flags is 0xS?10 << 16 + snd_wnd
+ * if header_predition is to be made
+ * 'S' will always be tp->tcp_header_len >> 2
+ * '?' will be 0 else it will be !0
+ * (when there are holes in the receive
+ * space for instance)
+ * PSH flag is ignored.
+ */
+
+ if (flg == tp->pred_flags && TCP_SKB_CB(skb)->seq == tp->rcv_nxt) {
+ if (len <= th->doff*4) {
+ /* Bulk data transfer: sender */
+ if (len == th->doff*4) {
+ tcp_ack(sk, th, TCP_SKB_CB(skb)->seq,
+ TCP_SKB_CB(skb)->ack_seq, len);
+ kfree_skb(skb);
+ tcp_data_snd_check(sk);
+ return 0;
+ } else { /* Header too small */
+ tcp_statistics.TcpInErrs++;
+ goto discard;
+ }
+ } else if (TCP_SKB_CB(skb)->ack_seq == tp->snd_una &&
+ atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) {
+ /* Bulk data transfer: receiver */
+ __skb_pull(skb,th->doff*4);
+
+ tcp_measure_rcv_mss(sk, skb);
+
+ /* DO NOT notify forward progress here.
+ * It saves dozen of CPU instructions in fast path. --ANK
+ */
+ __skb_queue_tail(&sk->receive_queue, skb);
+ tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
+
+ /* FIN bit check is not done since if FIN is set in
+ * this frame, the pred_flags won't match up. -DaveM
+ */
+ sk->data_ready(sk, 0);
+ tcp_delack_estimator(tp);
+
+ tcp_remember_ack(tp, th, skb);
+
+ __tcp_ack_snd_check(sk);
+ return 0;
+ }
+ }
+
+ /*
+ * Standard slow path.
+ */
+
+ if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {
+ /* RFC793, page 37: "In all states except SYN-SENT, all reset
+ * (RST) segments are validated by checking their SEQ-fields."
+ * And page 69: "If an incoming segment is not acceptable,
+ * an acknowledgment should be sent in reply (unless the RST bit
+ * is set, if so drop the segment and return)".
+ */
+ if (th->rst)
+ goto discard;
+ if (after(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
+ SOCK_DEBUG(sk, "seq:%d end:%d wup:%d wnd:%d\n",
+ TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq,
+ tp->rcv_wup, tp->rcv_wnd);
+ }
+ tcp_send_ack(sk);
+ goto discard;
+ }
+
+ if(th->syn && TCP_SKB_CB(skb)->seq != tp->syn_seq) {
+ SOCK_DEBUG(sk, "syn in established state\n");
+ tcp_statistics.TcpInErrs++;
+ tcp_reset(sk);
+ return 1;
+ }
+
+ if(th->rst) {
+ tcp_reset(sk);
+ goto discard;
+ }
+
+ if(th->ack)
+ tcp_ack(sk, th, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->ack_seq, len);
+
+ /* Process urgent data. */
+ tcp_urg(sk, th, len);
+
+ /* step 7: process the segment text */
+ queued = tcp_data(skb, sk, len);
+
+ /* This must be after tcp_data() does the skb_pull() to
+ * remove the header size from skb->len.
+ *
+ * Dave!!! Phrase above (and all about rcv_mss) has
+ * nothing to do with reality. rcv_mss must measure TOTAL
+ * size, including sacks, IP options etc. Hence, measure_rcv_mss
+ * must occur before pulling etc, otherwise it will flap
+ * like hell. Even putting it before tcp_data is wrong,
+ * it should use skb->tail - skb->nh.raw instead.
+ * --ANK (980805)
+ *
+ * BTW I broke it. Now all TCP options are handled equally
+ * in mss_clamp calculations (i.e. ignored, rfc1122),
+ * and mss_cache does include all of them (i.e. tstamps)
+ * except for sacks, to calculate effective mss faster.
+ * --ANK (980805)
+ */
+ tcp_measure_rcv_mss(sk, skb);
+
+ /* Be careful, tcp_data() may have put this into TIME_WAIT. */
+ if(sk->state != TCP_CLOSE) {
+ tcp_data_snd_check(sk);
+ tcp_ack_snd_check(sk);
+ }
+
+ if (!queued) {
+ discard:
+ kfree_skb(skb);
+ }
+
+ return 0;
+}
+
+/*
+ * Process an incoming SYN or SYN-ACK for SYN_RECV sockets represented
+ * as an open_request.
+ */
+
+struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
+ struct open_request *req)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ u32 flg;
+
+ /* assumption: the socket is not in use.
+ * as we checked the user count on tcp_rcv and we're
+ * running from a soft interrupt.
+ */
+
+ /* Check for syn retransmission */
+ flg = *(((u32 *)skb->h.th) + 3);
+
+ flg &= __constant_htonl(0x00170000);
+ /* Only SYN set? */
+ if (flg == __constant_htonl(0x00020000)) {
+ if (TCP_SKB_CB(skb)->seq == req->rcv_isn) {
+ /* retransmited syn.
+ */
+ req->class->rtx_syn_ack(sk, req);
+ return NULL;
+ } else {
+ return sk; /* Pass new SYN to the listen socket. */
+ }
+ }
+
+ /* We know it's an ACK here */
+ if (req->sk) {
+ /* socket already created but not
+ * yet accepted()...
+ */
+ sk = req->sk;
+ } else {
+ /* In theory the packet could be for a cookie, but
+ * TIME_WAIT should guard us against this.
+ * XXX: Nevertheless check for cookies?
+ * This sequence number check is done again later,
+ * but we do it here to prevent syn flood attackers
+ * from creating big SYN_RECV sockets.
+ */
+ if (!between(TCP_SKB_CB(skb)->ack_seq, req->snt_isn, req->snt_isn+1) ||
+ !between(TCP_SKB_CB(skb)->seq, req->rcv_isn,
+ req->rcv_isn+1+req->rcv_wnd)) {
+ req->class->send_reset(skb);
+ return NULL;
+ }
+
+ sk = tp->af_specific->syn_recv_sock(sk, skb, req, NULL);
+ tcp_dec_slow_timer(TCP_SLT_SYNACK);
+ if (sk == NULL)
+ return NULL;
+
+ req->expires = 0UL;
+ req->sk = sk;
+ }
+ skb_orphan(skb);
+ skb_set_owner_r(skb, sk);
+ return sk;
+}
+
+/*
+ * This function implements the receiving procedure of RFC 793 for
+ * all states except ESTABLISHED and TIME_WAIT.
+ * It's called from both tcp_v4_rcv and tcp_v6_rcv and should be
+ * address independent.
+ */
+
+int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
+ struct tcphdr *th, unsigned len)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ int queued = 0;
+
+ switch (sk->state) {
+ case TCP_CLOSE:
+ /* When state == CLOSED, hash lookup always fails.
+ *
+ * But, there is a back door, the backlog queue.
+ * If we have a sequence of packets in the backlog
+ * during __release_sock() which have a sequence such
+ * that:
+ * packet X causes entry to TCP_CLOSE state
+ * ...
+ * packet X + N has FIN bit set
+ *
+ * We report a (luckily) harmless error in this case.
+ * The issue is that backlog queue processing bypasses
+ * any hash lookups (we know which socket packets are for).
+ * The correct behavior here is what 2.0.x did, since
+ * a TCP_CLOSE socket does not exist. Drop the frame
+ * and send a RST back to the other end.
+ */
+ return 1;
+
+ case TCP_LISTEN:
+ /* These use the socket TOS..
+ * might want to be the received TOS
+ */
+ if(th->ack)
+ return 1;
+
+ if(th->syn) {
+ if(tp->af_specific->conn_request(sk, skb, 0) < 0)
+ return 1;
+
+ /* Now we have several options: In theory there is
+ * nothing else in the frame. KA9Q has an option to
+ * send data with the syn, BSD accepts data with the
+ * syn up to the [to be] advertised window and
+ * Solaris 2.1 gives you a protocol error. For now
+ * we just ignore it, that fits the spec precisely
+ * and avoids incompatibilities. It would be nice in
+ * future to drop through and process the data.
+ *
+ * Now that TTCP is starting to be used we ought to
+ * queue this data.
+ * But, this leaves one open to an easy denial of
+ * service attack, and SYN cookies can't defend
+ * against this problem. So, we drop the data
+ * in the interest of security over speed.
+ */
+ goto discard;
+ }
+
+ goto discard;
+ break;
+
+ case TCP_SYN_SENT:
+ /* SYN sent means we have to look for a suitable ack and
+ * either reset for bad matches or go to connected.
+ * The SYN_SENT case is unusual and should
+ * not be in line code. [AC]
+ */
+ if(th->ack) {
+ /* rfc793:
+ * "If the state is SYN-SENT then
+ * first check the ACK bit
+ * If the ACK bit is set
+ * If SEG.ACK =< ISS, or SEG.ACK > SND.NXT, send
+ * a reset (unless the RST bit is set, if so drop
+ * the segment and return)"
+ *
+ * I cite this place to emphasize one essential
+ * detail, this check is different of one
+ * in established state: SND.UNA <= SEG.ACK <= SND.NXT.
+ * SEG_ACK == SND.UNA == ISS is invalid in SYN-SENT,
+ * because we have no previous data sent before SYN.
+ * --ANK(990513)
+ *
+ * We do not send data with SYN, so that RFC-correct
+ * test reduces to:
+ */
+ if (sk->zapped ||
+ TCP_SKB_CB(skb)->ack_seq != tp->snd_nxt)
+ return 1;
+
+ /* Now ACK is acceptable.
+ *
+ * "If the RST bit is set
+ * If the ACK was acceptable then signal the user "error:
+ * connection reset", drop the segment, enter CLOSED state,
+ * delete TCB, and return."
+ */
+
+ if (th->rst) {
+ tcp_reset(sk);
+ goto discard;
+ }
+
+ /* rfc793:
+ * "fifth, if neither of the SYN or RST bits is set then
+ * drop the segment and return."
+ *
+ * See note below!
+ * --ANK(990513)
+ */
+
+ if (!th->syn)
+ goto discard;
+
+ /* rfc793:
+ * "If the SYN bit is on ...
+ * are acceptable then ...
+ * (our SYN has been ACKed), change the connection
+ * state to ESTABLISHED..."
+ *
+ * Do you see? SYN-less ACKs in SYN-SENT state are
+ * completely ignored.
+ *
+ * The bug causing stalled SYN-SENT sockets
+ * was here: tcp_ack advanced snd_una and canceled
+ * retransmit timer, so that bare ACK received
+ * in SYN-SENT state (even with invalid ack==ISS,
+ * because tcp_ack check is too weak for SYN-SENT)
+ * causes moving socket to invalid semi-SYN-SENT,
+ * semi-ESTABLISHED state and connection hangs.
+ *
+ * There exist buggy stacks, which really send
+ * such ACKs: f.e. 202.226.91.94 (okigate.oki.co.jp)
+ * Actually, if this host did not try to get something
+ * from ftp.inr.ac.ru I'd never find this bug 8)
+ *
+ * --ANK (990514)
+ */
+
+ tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
+ tcp_ack(sk,th, TCP_SKB_CB(skb)->seq,
+ TCP_SKB_CB(skb)->ack_seq, len);
+
+ /* Ok.. it's good. Set up sequence numbers and
+ * move to established.
+ */
+ tp->rcv_nxt = TCP_SKB_CB(skb)->seq+1;
+ tp->rcv_wup = TCP_SKB_CB(skb)->seq+1;
+
+ /* RFC1323: The window in SYN & SYN/ACK segments is
+ * never scaled.
+ */
+ tp->snd_wnd = htons(th->window);
+ tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
+ tp->snd_wl2 = TCP_SKB_CB(skb)->ack_seq;
+ tp->fin_seq = TCP_SKB_CB(skb)->seq;
+
+ tcp_set_state(sk, TCP_ESTABLISHED);
+ tcp_parse_options(sk, th, tp, 0);
+
+ if (tp->wscale_ok == 0) {
+ tp->snd_wscale = tp->rcv_wscale = 0;
+ tp->window_clamp = min(tp->window_clamp,65535);
+ }
+
+ if (tp->tstamp_ok) {
+ tp->tcp_header_len =
+ sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;
+ } else
+ tp->tcp_header_len = sizeof(struct tcphdr);
+ if (tp->saw_tstamp) {
+ tp->ts_recent = tp->rcv_tsval;
+ tp->ts_recent_stamp = tcp_time_stamp;
+ }
+
+ /* Can't be earlier, doff would be wrong. */
+ tcp_send_ack(sk);
+
+ sk->dport = th->source;
+ tp->copied_seq = tp->rcv_nxt;
+
+ if(!sk->dead) {
+ sk->state_change(sk);
+ sock_wake_async(sk->socket, 0);
+ }
+ } else {
+ if(th->syn && !th->rst) {
+ /* The previous version of the code
+ * checked for "connecting to self"
+ * here. that check is done now in
+ * tcp_connect.
+ */
+ tcp_set_state(sk, TCP_SYN_RECV);
+ tcp_parse_options(sk, th, tp, 0);
+ if (tp->saw_tstamp) {
+ tp->ts_recent = tp->rcv_tsval;
+ tp->ts_recent_stamp = tcp_time_stamp;
+ }
+
+ tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
+ tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1;
+
+ /* RFC1323: The window in SYN & SYN/ACK segments is
+ * never scaled.
+ */
+ tp->snd_wnd = htons(th->window);
+ tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
+
+ tcp_send_synack(sk);
+ } else
+ break;
+ }
+
+ /* tp->tcp_header_len and tp->mss_clamp
+ probably changed, synchronize mss.
+ */
+ tcp_sync_mss(sk, tp->pmtu_cookie);
+ tp->rcv_mss = tp->mss_cache;
+
+ if (sk->state == TCP_SYN_RECV)
+ goto discard;
+
+ goto step6;
+ }
+
+ /* Parse the tcp_options present on this header.
+ * By this point we really only expect timestamps.
+ * Note that this really has to be here and not later for PAWS
+ * (RFC1323) to work.
+ */
+ if (tcp_fast_parse_options(sk, th, tp)) {
+ /* NOTE: assumes saw_tstamp is never set if we didn't
+ * negotiate the option. tcp_fast_parse_options() must
+ * guarantee this.
+ */
+ if (tp->saw_tstamp) {
+ if (tcp_paws_discard(tp, th, len)) {
+ tcp_statistics.TcpInErrs++;
+ if (!th->rst) {
+ tcp_send_ack(sk);
+ goto discard;
+ }
+ }
+ tcp_replace_ts_recent(sk, tp,
+ TCP_SKB_CB(skb)->seq,
+ TCP_SKB_CB(skb)->end_seq);
+ }
+ }
+
+ /* The silly FIN test here is necessary to see an advancing ACK in
+ * retransmitted FIN frames properly. Consider the following sequence:
+ *
+ * host1 --> host2 FIN XSEQ:XSEQ(0) ack YSEQ
+ * host2 --> host1 FIN YSEQ:YSEQ(0) ack XSEQ
+ * host1 --> host2 XSEQ:XSEQ(0) ack YSEQ+1
+ * host2 --> host1 FIN YSEQ:YSEQ(0) ack XSEQ+1 (fails tcp_sequence test)
+ *
+ * At this point the connection will deadlock with host1 believing
+ * that his FIN is never ACK'd, and thus it will retransmit it's FIN
+ * forever. The following fix is from Taral (taral@taral.net).
+ */
+
+ /* step 1: check sequence number */
+ if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq) &&
+ !(th->fin && TCP_SKB_CB(skb)->end_seq == tp->rcv_nxt)) {
+ if (!th->rst) {
+ tcp_send_ack(sk);
+ }
+ goto discard;
+ }
+
+ /* step 2: check RST bit */
+ if(th->rst) {
+ tcp_reset(sk);
+ goto discard;
+ }
+
+ /* step 3: check security and precedence [ignored] */
+
+ /* step 4:
+ *
+ * Check for a SYN, and ensure it matches the SYN we were
+ * first sent. We have to handle the rather unusual (but valid)
+ * sequence that KA9Q derived products may generate of
+ *
+ * SYN
+ * SYN|ACK Data
+ * ACK (lost)
+ * SYN|ACK Data + More Data
+ * .. we must ACK not RST...
+ *
+ * We keep syn_seq as the sequence space occupied by the
+ * original syn.
+ */
+
+ if (th->syn && TCP_SKB_CB(skb)->seq != tp->syn_seq) {
+ tcp_reset(sk);
+ return 1;
+ }
+
+ /* step 5: check the ACK field */
+ if (th->ack) {
+ int acceptable = tcp_ack(sk, th, TCP_SKB_CB(skb)->seq,
+ TCP_SKB_CB(skb)->ack_seq, len);
+
+ switch(sk->state) {
+ case TCP_SYN_RECV:
+ if (acceptable) {
+ tcp_set_state(sk, TCP_ESTABLISHED);
+ sk->dport = th->source;
+ tp->copied_seq = tp->rcv_nxt;
+
+ if(!sk->dead)
+ sk->state_change(sk);
+
+ tp->snd_una = TCP_SKB_CB(skb)->ack_seq;
+ tp->snd_wnd = htons(th->window) << tp->snd_wscale;
+ tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
+ tp->snd_wl2 = TCP_SKB_CB(skb)->ack_seq;
+
+ } else {
+ SOCK_DEBUG(sk, "bad ack\n");
+ return 1;
+ }
+ break;
+
+ case TCP_FIN_WAIT1:
+ if (tp->snd_una == tp->write_seq) {
+ sk->shutdown |= SEND_SHUTDOWN;
+ tcp_set_state(sk, TCP_FIN_WAIT2);
+ if (!sk->dead)
+ sk->state_change(sk);
+ else
+ tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout);
+ }
+ break;
+
+ case TCP_CLOSING:
+ if (tp->snd_una == tp->write_seq) {
+ tcp_time_wait(sk);
+ goto discard;
+ }
+ break;
+
+ case TCP_LAST_ACK:
+ if (tp->snd_una == tp->write_seq) {
+ sk->shutdown = SHUTDOWN_MASK;
+ tcp_set_state(sk,TCP_CLOSE);
+ if (!sk->dead)
+ sk->state_change(sk);
+ goto discard;
+ }
+ break;
+ }
+ } else
+ goto discard;
+
+step6:
+ /* step 6: check the URG bit */
+ tcp_urg(sk, th, len);
+
+ /* step 7: process the segment text */
+ switch (sk->state) {
+ case TCP_CLOSE_WAIT:
+ case TCP_CLOSING:
+ if (!before(TCP_SKB_CB(skb)->seq, tp->fin_seq))
+ break;
+
+ case TCP_FIN_WAIT1:
+ case TCP_FIN_WAIT2:
+ /* RFC 793 says to queue data in these states,
+ * RFC 1122 says we MUST send a reset.
+ * BSD 4.4 also does reset.
+ */
+ if ((sk->shutdown & RCV_SHUTDOWN) && sk->dead) {
+ if (after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) {
+ tcp_reset(sk);
+ return 1;
+ }
+ }
+
+ case TCP_ESTABLISHED:
+ queued = tcp_data(skb, sk, len);
+
+ /* This must be after tcp_data() does the skb_pull() to
+ * remove the header size from skb->len.
+ */
+ tcp_measure_rcv_mss(sk, skb);
+ break;
+ }
+
+ tcp_data_snd_check(sk);
+ tcp_ack_snd_check(sk);
+
+ if (!queued) {
+discard:
+ kfree_skb(skb);
+ }
+ return 0;
+}
diff --git a/pfinet/linux-src/net/ipv4/tcp_ipv4.c b/pfinet/linux-src/net/ipv4/tcp_ipv4.c
new file mode 100644
index 00000000..df2c8b7c
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/tcp_ipv4.c
@@ -0,0 +1,2062 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Implementation of the Transmission Control Protocol(TCP).
+ *
+ * Version: $Id: tcp_ipv4.c,v 1.175.2.13 1999/11/16 06:33:53 davem Exp $
+ *
+ * IPv4 specific functions
+ *
+ *
+ * code split from:
+ * linux/ipv4/tcp.c
+ * linux/ipv4/tcp_input.c
+ * linux/ipv4/tcp_output.c
+ *
+ * See tcp.c for author information
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * Changes:
+ * David S. Miller : New socket lookup architecture.
+ * This code is dedicated to John Dyson.
+ * David S. Miller : Change semantics of established hash,
+ * half is devoted to TIME_WAIT sockets
+ * and the rest go in the other half.
+ * Andi Kleen : Add support for syncookies and fixed
+ * some bugs: ip options weren't passed to
+ * the TCP layer, missed a check for an ACK bit.
+ * Andi Kleen : Implemented fast path mtu discovery.
+ * Fixed many serious bugs in the
+ * open_request handling and moved
+ * most of it into the af independent code.
+ * Added tail drop and some other bugfixes.
+ * Added new listen sematics.
+ * Mike McLagan : Routing by source
+ * Juan Jose Ciarlante: ip_dynaddr bits
+ * Andi Kleen: various fixes.
+ * Vitaly E. Lavrov : Transparent proxy revived after year coma.
+ * Andi Kleen : Fix new listen.
+ * Andi Kleen : Fix accept error reporting.
+ * YOSHIFUJI Hideaki @USAGI and: Support IPV6_V6ONLY socket option, which
+ * Alexey Kuznetsov allow both IPv4 and IPv6 sockets to bind
+ * a single port at the same time.
+ */
+
+#include <linux/config.h>
+
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/random.h>
+#include <linux/init.h>
+#include <linux/ipsec.h>
+
+#include <net/icmp.h>
+#include <net/tcp.h>
+#include <net/ipv6.h>
+
+#include <asm/segment.h>
+
+#include <linux/inet.h>
+#include <linux/ipv6.h>
+#include <linux/stddef.h>
+
+extern int sysctl_tcp_timestamps;
+extern int sysctl_tcp_window_scaling;
+extern int sysctl_tcp_sack;
+extern int sysctl_tcp_syncookies;
+extern int sysctl_ip_dynaddr;
+extern __u32 sysctl_wmem_max;
+extern __u32 sysctl_rmem_max;
+
+/* Check TCP sequence numbers in ICMP packets. */
+#define ICMP_MIN_LENGTH 8
+
+/* Socket used for sending RSTs */
+struct inode tcp_inode;
+struct socket *tcp_socket=&tcp_inode.u.socket_i;
+
+static void tcp_v4_send_reset(struct sk_buff *skb);
+
+void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len,
+ struct sk_buff *skb);
+
+/* This is for sockets with full identity only. Sockets here will always
+ * be without wildcards and will have the following invariant:
+ * TCP_ESTABLISHED <= sk->state < TCP_CLOSE
+ *
+ * First half of the table is for sockets not in TIME_WAIT, second half
+ * is for TIME_WAIT sockets only.
+ */
+unsigned int tcp_ehash_size;
+struct sock **tcp_ehash;
+
+/* Ok, let's try this, I give up, we do need a local binding
+ * TCP hash as well as the others for fast bind/connect.
+ */
+unsigned int tcp_bhash_size;
+struct tcp_bind_bucket **tcp_bhash;
+
+/* All sockets in TCP_LISTEN state will be in here. This is the only table
+ * where wildcard'd TCP sockets can exist. Hash function here is just local
+ * port number.
+ */
+struct sock *tcp_listening_hash[TCP_LHTABLE_SIZE];
+
+/* Register cache. */
+struct sock *tcp_regs[TCP_NUM_REGS];
+
+/*
+ * This array holds the first and last local port number.
+ * For high-usage systems, use sysctl to change this to
+ * 32768-61000
+ */
+#if 0
+int sysctl_local_port_range[2] = { 1024, 4999 };
+#else
+int sysctl_local_port_range[2] = { 32768, 61000 };
+#endif
+int tcp_port_rover = (1024 - 1);
+
+static __inline__ int tcp_hashfn(__u32 laddr, __u16 lport,
+ __u32 faddr, __u16 fport)
+{
+ return ((laddr ^ lport) ^ (faddr ^ fport)) & ((tcp_ehash_size/2) - 1);
+}
+
+static __inline__ int tcp_sk_hashfn(struct sock *sk)
+{
+ __u32 laddr = sk->rcv_saddr;
+ __u16 lport = sk->num;
+ __u32 faddr = sk->daddr;
+ __u16 fport = sk->dport;
+
+ return tcp_hashfn(laddr, lport, faddr, fport);
+}
+
+/* Allocate and initialize a new TCP local port bind bucket.
+ * Always runs inside the socket hashing lock.
+ */
+struct tcp_bind_bucket *tcp_bucket_create(unsigned short snum)
+{
+ struct tcp_bind_bucket *tb;
+
+ tb = kmem_cache_alloc(tcp_bucket_cachep, SLAB_ATOMIC);
+ if(tb != NULL) {
+ struct tcp_bind_bucket **head =
+ &tcp_bhash[tcp_bhashfn(snum)];
+ tb->port = snum;
+ tb->fastreuse = 0;
+ tb->owners = NULL;
+ if((tb->next = *head) != NULL)
+ tb->next->pprev = &tb->next;
+ *head = tb;
+ tb->pprev = head;
+ }
+ return tb;
+}
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+/* Ensure that the bound bucket for the port exists.
+ * Return 0 and bump bucket reference count on success.
+ *
+ * Must run in a BH atomic section.
+ */
+static __inline__ int __tcp_bucket_check(unsigned short snum)
+{
+ struct tcp_bind_bucket *tb;
+
+ tb = tcp_bhash[tcp_bhashfn(snum)];
+ for( ; (tb && (tb->port != snum)); tb = tb->next)
+ ;
+ if (tb == NULL) {
+ if ((tb = tcp_bucket_create(snum)) == NULL)
+ return 1;
+ }
+
+ return 0;
+}
+#endif
+
+static __inline__ void __tcp_inherit_port(struct sock *sk, struct sock *child)
+{
+ struct tcp_bind_bucket *tb = (struct tcp_bind_bucket *)sk->prev;
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ if (child->num != sk->num) {
+ unsigned short snum = child->num;
+ for(tb = tcp_bhash[tcp_bhashfn(snum)];
+ tb && tb->port != snum;
+ tb = tb->next)
+ ;
+ if (tb == NULL)
+ tb = (struct tcp_bind_bucket *)sk->prev;
+ }
+#endif
+ if ((child->bind_next = tb->owners) != NULL)
+ tb->owners->bind_pprev = &child->bind_next;
+ tb->owners = child;
+ child->bind_pprev = &tb->owners;
+ child->prev = (struct sock *) tb;
+}
+
+__inline__ void tcp_inherit_port(struct sock *sk, struct sock *child)
+{
+ SOCKHASH_LOCK();
+ __tcp_inherit_port(sk, child);
+ SOCKHASH_UNLOCK();
+}
+
+/* Obtain a reference to a local port for the given sock,
+ * if snum is zero it means select any available local port.
+ */
+static int tcp_v4_get_port(struct sock *sk, unsigned short snum)
+{
+ struct tcp_bind_bucket *tb;
+
+ SOCKHASH_LOCK();
+ if (snum == 0) {
+ int rover = tcp_port_rover;
+ int low = sysctl_local_port_range[0];
+ int high = sysctl_local_port_range[1];
+ int remaining = (high - low) + 1;
+
+ do { rover++;
+ if ((rover < low) || (rover > high))
+ rover = low;
+ tb = tcp_bhash[tcp_bhashfn(rover)];
+ for ( ; tb; tb = tb->next)
+ if (tb->port == rover)
+ goto next;
+ break;
+ next:
+ ; /* Do nothing. */
+ } while (--remaining > 0);
+ tcp_port_rover = rover;
+
+ /* Exhausted local port range during search? */
+ if (remaining <= 0)
+ goto fail;
+
+ /* OK, here is the one we will use. */
+ snum = rover;
+ tb = NULL;
+ } else {
+ for (tb = tcp_bhash[tcp_bhashfn(snum)];
+ tb != NULL;
+ tb = tb->next)
+ if (tb->port == snum)
+ break;
+ }
+ if (tb != NULL && tb->owners != NULL) {
+ if (tb->fastreuse != 0 && sk->reuse != 0) {
+ goto success;
+ } else {
+ struct sock *sk2 = tb->owners;
+ int sk_reuse = sk->reuse;
+
+ for( ; sk2 != NULL; sk2 = sk2->bind_next) {
+ if (!ipv6_only_sock(sk2) &&
+ sk->bound_dev_if == sk2->bound_dev_if) {
+ if (!sk_reuse ||
+ !sk2->reuse ||
+ sk2->state == TCP_LISTEN) {
+ if (!sk2->rcv_saddr ||
+ !sk->rcv_saddr ||
+ (sk2->rcv_saddr == sk->rcv_saddr))
+ break;
+ }
+ }
+ }
+ /* If we found a conflict, fail. */
+ if (sk2 != NULL)
+ goto fail;
+ }
+ }
+ if (tb == NULL &&
+ (tb = tcp_bucket_create(snum)) == NULL)
+ goto fail;
+ if (tb->owners == NULL) {
+ if (sk->reuse && sk->state != TCP_LISTEN)
+ tb->fastreuse = 1;
+ else
+ tb->fastreuse = 0;
+ } else if (tb->fastreuse &&
+ ((sk->reuse == 0) || (sk->state == TCP_LISTEN)))
+ tb->fastreuse = 0;
+success:
+ sk->num = snum;
+ if ((sk->bind_next = tb->owners) != NULL)
+ tb->owners->bind_pprev = &sk->bind_next;
+ tb->owners = sk;
+ sk->bind_pprev = &tb->owners;
+ sk->prev = (struct sock *) tb;
+
+ SOCKHASH_UNLOCK();
+ return 0;
+
+fail:
+ SOCKHASH_UNLOCK();
+ return 1;
+}
+
+/* Get rid of any references to a local port held by the
+ * given sock.
+ */
+__inline__ void __tcp_put_port(struct sock *sk)
+{
+ struct tcp_bind_bucket *tb;
+
+ tb = (struct tcp_bind_bucket *) sk->prev;
+ if (sk->bind_next)
+ sk->bind_next->bind_pprev = sk->bind_pprev;
+ *(sk->bind_pprev) = sk->bind_next;
+ sk->prev = NULL;
+ if (tb->owners == NULL) {
+ if (tb->next)
+ tb->next->pprev = tb->pprev;
+ *(tb->pprev) = tb->next;
+ kmem_cache_free(tcp_bucket_cachep, tb);
+ }
+}
+
+void tcp_put_port(struct sock *sk)
+{
+ SOCKHASH_LOCK();
+ __tcp_put_port(sk);
+ SOCKHASH_UNLOCK();
+}
+
+static __inline__ void __tcp_v4_hash(struct sock *sk)
+{
+ struct sock **skp;
+
+ if(sk->state == TCP_LISTEN)
+ skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
+ else
+ skp = &tcp_ehash[(sk->hashent = tcp_sk_hashfn(sk))];
+
+ if((sk->next = *skp) != NULL)
+ (*skp)->pprev = &sk->next;
+ *skp = sk;
+ sk->pprev = skp;
+}
+
+static void tcp_v4_hash(struct sock *sk)
+{
+ if (sk->state != TCP_CLOSE) {
+ SOCKHASH_LOCK();
+ __tcp_v4_hash(sk);
+ SOCKHASH_UNLOCK();
+ }
+}
+
+static void tcp_v4_unhash(struct sock *sk)
+{
+ SOCKHASH_LOCK();
+ if(sk->pprev) {
+ if(sk->next)
+ sk->next->pprev = sk->pprev;
+ *sk->pprev = sk->next;
+ sk->pprev = NULL;
+ tcp_reg_zap(sk);
+ __tcp_put_port(sk);
+ }
+ SOCKHASH_UNLOCK();
+}
+
+/* Don't inline this cruft. Here are some nice properties to
+ * exploit here. The BSD API does not allow a listening TCP
+ * to specify the remote port nor the remote address for the
+ * connection. So always assume those are both wildcarded
+ * during the search since they can never be otherwise.
+ */
+static struct sock *tcp_v4_lookup_listener(u32 daddr, unsigned short hnum, int dif)
+{
+ struct sock *sk;
+ struct sock *result = NULL;
+ int score, hiscore;
+
+ hiscore=-1;
+ for(sk = tcp_listening_hash[tcp_lhashfn(hnum)]; sk; sk = sk->next) {
+ if(sk->num == hnum && !ipv6_only_sock(sk)) {
+ __u32 rcv_saddr = sk->rcv_saddr;
+
+ score = (sk->family == PF_INET ? 1 : 0);
+ if(rcv_saddr) {
+ if (rcv_saddr != daddr)
+ continue;
+ score+=2;
+ }
+ if (sk->bound_dev_if) {
+ if (sk->bound_dev_if != dif)
+ continue;
+ score+=2;
+ }
+ if (score == 5)
+ return sk;
+ if (score > hiscore) {
+ hiscore = score;
+ result = sk;
+ }
+ }
+ }
+ return result;
+}
+
+/* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
+ * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
+ * It is assumed that this code only gets called from within NET_BH.
+ */
+static inline struct sock *__tcp_v4_lookup(struct tcphdr *th,
+ u32 saddr, u16 sport,
+ u32 daddr, u16 dport, int dif)
+{
+ TCP_V4_ADDR_COOKIE(acookie, saddr, daddr)
+ __u16 hnum = ntohs(dport);
+ __u32 ports = TCP_COMBINED_PORTS(sport, hnum);
+ struct sock *sk;
+ int hash;
+
+ /* Check TCP register quick cache first. */
+ sk = TCP_RHASH(sport);
+ if(sk && TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif))
+ goto hit;
+
+ /* Optimize here for direct hit, only listening connections can
+ * have wildcards anyways.
+ */
+ hash = tcp_hashfn(daddr, hnum, saddr, sport);
+ for(sk = tcp_ehash[hash]; sk; sk = sk->next) {
+ if(TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif)) {
+ if (sk->state == TCP_ESTABLISHED)
+ TCP_RHASH(sport) = sk;
+ goto hit; /* You sunk my battleship! */
+ }
+ }
+ /* Must check for a TIME_WAIT'er before going to listener hash. */
+ for(sk = tcp_ehash[hash+(tcp_ehash_size/2)]; sk; sk = sk->next)
+ if(TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif))
+ goto hit;
+ sk = tcp_v4_lookup_listener(daddr, hnum, dif);
+hit:
+ return sk;
+}
+
+__inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif)
+{
+ return __tcp_v4_lookup(0, saddr, sport, daddr, dport, dif);
+}
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+/* Cleaned up a little and adapted to new bind bucket scheme.
+ * Oddly, this should increase performance here for
+ * transparent proxy, as tests within the inner loop have
+ * been eliminated. -DaveM
+ */
+static struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
+ unsigned short rnum, unsigned long laddr,
+ struct device *dev, unsigned short pnum,
+ int dif)
+{
+ struct sock *s, *result = NULL;
+ int badness = -1;
+ u32 paddr = 0;
+ unsigned short hnum = ntohs(num);
+ unsigned short hpnum = ntohs(pnum);
+ int firstpass = 1;
+
+ if(dev && dev->ip_ptr) {
+ struct in_device *idev = dev->ip_ptr;
+
+ if(idev->ifa_list)
+ paddr = idev->ifa_list->ifa_local;
+ }
+
+ /* This code must run only from NET_BH. */
+ {
+ struct tcp_bind_bucket *tb = tcp_bhash[tcp_bhashfn(hnum)];
+ for( ; (tb && tb->port != hnum); tb = tb->next)
+ ;
+ if(tb == NULL)
+ goto next;
+ s = tb->owners;
+ }
+pass2:
+ for(; s; s = s->bind_next) {
+ int score = 0;
+ if(s->rcv_saddr) {
+ if((s->num != hpnum || s->rcv_saddr != paddr) &&
+ (s->num != hnum || s->rcv_saddr != laddr))
+ continue;
+ score++;
+ }
+ if(s->daddr) {
+ if(s->daddr != raddr)
+ continue;
+ score++;
+ }
+ if(s->dport) {
+ if(s->dport != rnum)
+ continue;
+ score++;
+ }
+ if(s->bound_dev_if) {
+ if(s->bound_dev_if != dif)
+ continue;
+ score++;
+ }
+ if(score == 4 && s->num == hnum) {
+ result = s;
+ goto gotit;
+ } else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
+ result = s;
+ badness = score;
+ }
+ }
+next:
+ if(firstpass--) {
+ struct tcp_bind_bucket *tb = tcp_bhash[tcp_bhashfn(hpnum)];
+ for( ; (tb && tb->port != hpnum); tb = tb->next)
+ ;
+ if(tb) {
+ s = tb->owners;
+ goto pass2;
+ }
+ }
+gotit:
+ return result;
+}
+#endif /* CONFIG_IP_TRANSPARENT_PROXY */
+
+static inline __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb)
+{
+ return secure_tcp_sequence_number(skb->nh.iph->daddr,
+ skb->nh.iph->saddr,
+ skb->h.th->dest,
+ skb->h.th->source);
+}
+
+/* Check that a TCP address is unique, don't allow multiple
+ * connects to/from the same address. Actually we can optimize
+ * quite a bit, since the socket about to connect is still
+ * in TCP_CLOSE, a tcp_bind_bucket for the local port he will
+ * use will exist, with a NULL owners list. So check for that.
+ * The good_socknum and verify_bind scheme we use makes this
+ * work.
+ */
+static int tcp_v4_unique_address(struct sock *sk)
+{
+ struct tcp_bind_bucket *tb;
+ unsigned short snum = sk->num;
+ int retval = 1;
+
+ /* Freeze the hash while we snoop around. */
+ SOCKHASH_LOCK();
+ tb = tcp_bhash[tcp_bhashfn(snum)];
+ for(; tb; tb = tb->next) {
+ if(tb->port == snum && tb->owners != NULL) {
+ /* Almost certainly the re-use port case, search the real hashes
+ * so it actually scales.
+ */
+ sk = __tcp_v4_lookup(NULL, sk->daddr, sk->dport,
+ sk->rcv_saddr, htons(snum),
+ sk->bound_dev_if);
+ if((sk != NULL) && (sk->state != TCP_LISTEN))
+ retval = 0;
+ break;
+ }
+ }
+ SOCKHASH_UNLOCK();
+ return retval;
+}
+
+/* This will initiate an outgoing connection. */
+int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct sockaddr_in *usin = (struct sockaddr_in *) uaddr;
+ struct sk_buff *buff;
+ struct rtable *rt;
+ u32 daddr, nexthop;
+ int tmp;
+
+ if (sk->state != TCP_CLOSE)
+ return(-EISCONN);
+
+ /* Don't allow a double connect. */
+ if (sk->daddr)
+ return -EINVAL;
+
+ if (addr_len < sizeof(struct sockaddr_in))
+ return(-EINVAL);
+
+ if (usin->sin_family != AF_INET) {
+ static int complained;
+ if (usin->sin_family)
+ return(-EAFNOSUPPORT);
+ if (!complained++)
+ printk(KERN_DEBUG "%s forgot to set AF_INET in %s\n", current->comm, __FUNCTION__);
+ }
+
+ nexthop = daddr = usin->sin_addr.s_addr;
+ if (sk->opt && sk->opt->srr) {
+ if (daddr == 0)
+ return -EINVAL;
+ nexthop = sk->opt->faddr;
+ }
+
+ tmp = ip_route_connect(&rt, nexthop, sk->saddr,
+ RT_TOS(sk->ip_tos)|RTO_CONN|sk->localroute, sk->bound_dev_if);
+ if (tmp < 0)
+ return tmp;
+
+ if (rt->rt_flags&(RTCF_MULTICAST|RTCF_BROADCAST)) {
+ ip_rt_put(rt);
+ return -ENETUNREACH;
+ }
+
+ dst_release(xchg(&sk->dst_cache, &rt->u.dst));
+
+ buff = sock_wmalloc(sk, (MAX_HEADER + sk->prot->max_header),
+ 0, GFP_KERNEL);
+
+ if (buff == NULL)
+ return -ENOBUFS;
+
+ /* Socket has no identity, so lock_sock() is useless. Also
+ * since state==TCP_CLOSE (checked above) the socket cannot
+ * possibly be in the hashes. TCP hash locking is only
+ * needed while checking quickly for a unique address.
+ * However, the socket does need to be (and is) locked
+ * in tcp_connect().
+ * Perhaps this addresses all of ANK's concerns. 8-) -DaveM
+ */
+ sk->dport = usin->sin_port;
+ sk->daddr = rt->rt_dst;
+ if (sk->opt && sk->opt->srr)
+ sk->daddr = daddr;
+ if (!sk->saddr)
+ sk->saddr = rt->rt_src;
+ sk->rcv_saddr = sk->saddr;
+
+ if (!tcp_v4_unique_address(sk)) {
+ kfree_skb(buff);
+ sk->daddr = 0;
+ return -EADDRNOTAVAIL;
+ }
+
+ tp->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr,
+ sk->sport, usin->sin_port);
+
+ tp->ext_header_len = 0;
+ if (sk->opt)
+ tp->ext_header_len = sk->opt->optlen;
+
+ /* Reset mss clamp */
+ tp->mss_clamp = ~0;
+
+ if (!ip_dont_fragment(sk, &rt->u.dst) &&
+ rt->u.dst.pmtu > 576 && rt->rt_dst != rt->rt_gateway) {
+ /* Clamp mss at maximum of 536 and user_mss.
+ Probably, user ordered to override tiny segment size
+ in gatewayed case.
+ */
+ tp->mss_clamp = max(tp->user_mss, 536);
+ }
+
+ tcp_connect(sk, buff, rt->u.dst.pmtu);
+ return 0;
+}
+
+static int tcp_v4_sendmsg(struct sock *sk, struct msghdr *msg, int len)
+{
+ int retval = -EINVAL;
+
+ /* Do sanity checking for sendmsg/sendto/send. */
+ if (msg->msg_flags & ~(MSG_OOB|MSG_DONTROUTE|MSG_DONTWAIT|MSG_NOSIGNAL))
+ goto out;
+ if (msg->msg_name) {
+ struct sockaddr_in *addr=(struct sockaddr_in *)msg->msg_name;
+
+ if (msg->msg_namelen < sizeof(*addr))
+ goto out;
+ if (addr->sin_family && addr->sin_family != AF_INET)
+ goto out;
+ retval = -ENOTCONN;
+ if(sk->state == TCP_CLOSE)
+ goto out;
+ retval = -EISCONN;
+ if (addr->sin_port != sk->dport)
+ goto out;
+ if (addr->sin_addr.s_addr != sk->daddr)
+ goto out;
+ }
+ retval = tcp_do_sendmsg(sk, msg);
+
+out:
+ return retval;
+}
+
+
+/*
+ * Do a linear search in the socket open_request list.
+ * This should be replaced with a global hash table.
+ */
+static struct open_request *tcp_v4_search_req(struct tcp_opt *tp,
+ struct iphdr *iph,
+ struct tcphdr *th,
+ struct open_request **prevp)
+{
+ struct open_request *req, *prev;
+ __u16 rport = th->source;
+
+ /* assumption: the socket is not in use.
+ * as we checked the user count on tcp_rcv and we're
+ * running from a soft interrupt.
+ */
+ prev = (struct open_request *) (&tp->syn_wait_queue);
+ for (req = prev->dl_next; req; req = req->dl_next) {
+ if (req->af.v4_req.rmt_addr == iph->saddr &&
+ req->af.v4_req.loc_addr == iph->daddr &&
+ req->rmt_port == rport
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ && req->lcl_port == th->dest
+#endif
+ ) {
+ *prevp = prev;
+ return req;
+ }
+ prev = req;
+ }
+ return NULL;
+}
+
+
+/*
+ * This routine does path mtu discovery as defined in RFC1191.
+ */
+static inline void do_pmtu_discovery(struct sock *sk, struct iphdr *ip, unsigned mtu)
+{
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+
+ if (atomic_read(&sk->sock_readers))
+ return;
+
+ /* Don't interested in TCP_LISTEN and open_requests (SYN-ACKs
+ * send out by Linux are always <576bytes so they should go through
+ * unfragmented).
+ */
+ if (sk->state == TCP_LISTEN)
+ return;
+
+ /* We don't check in the destentry if pmtu discovery is forbidden
+ * on this route. We just assume that no packet_to_big packets
+ * are send back when pmtu discovery is not active.
+ * There is a small race when the user changes this flag in the
+ * route, but I think that's acceptable.
+ */
+ if (sk->dst_cache == NULL)
+ return;
+ ip_rt_update_pmtu(sk->dst_cache, mtu);
+ if (sk->ip_pmtudisc != IP_PMTUDISC_DONT &&
+ tp->pmtu_cookie > sk->dst_cache->pmtu) {
+ tcp_sync_mss(sk, sk->dst_cache->pmtu);
+
+ /* Resend the TCP packet because it's
+ * clear that the old packet has been
+ * dropped. This is the new "fast" path mtu
+ * discovery.
+ */
+ tcp_simple_retransmit(sk);
+ } /* else let the usual retransmit timer handle it */
+}
+
+/*
+ * This routine is called by the ICMP module when it gets some
+ * sort of error condition. If err < 0 then the socket should
+ * be closed and the error returned to the user. If err > 0
+ * it's just the icmp type << 8 | icmp code. After adjustment
+ * header points to the first 8 bytes of the tcp header. We need
+ * to find the appropriate port.
+ *
+ * The locking strategy used here is very "optimistic". When
+ * someone else accesses the socket the ICMP is just dropped
+ * and for some paths there is no check at all.
+ * A more general error queue to queue errors for later handling
+ * is probably better.
+ *
+ * sk->err and sk->err_soft should be atomic_t.
+ */
+
+void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len)
+{
+ struct iphdr *iph = (struct iphdr*)dp;
+ struct tcphdr *th;
+ struct tcp_opt *tp;
+ int type = skb->h.icmph->type;
+ int code = skb->h.icmph->code;
+#if ICMP_MIN_LENGTH < 14
+ int no_flags = 0;
+#else
+#define no_flags 0
+#endif
+ struct sock *sk;
+ __u32 seq;
+ int err;
+
+ if (len < (iph->ihl << 2) + ICMP_MIN_LENGTH) {
+ icmp_statistics.IcmpInErrors++;
+ return;
+ }
+#if ICMP_MIN_LENGTH < 14
+ if (len < (iph->ihl << 2) + 14)
+ no_flags = 1;
+#endif
+
+ th = (struct tcphdr*)(dp+(iph->ihl<<2));
+
+ sk = tcp_v4_lookup(iph->daddr, th->dest, iph->saddr, th->source, skb->dev->ifindex);
+ if (sk == NULL || sk->state == TCP_TIME_WAIT) {
+ icmp_statistics.IcmpInErrors++;
+ return;
+ }
+
+ tp = &sk->tp_pinfo.af_tcp;
+ seq = ntohl(th->seq);
+ if (sk->state != TCP_LISTEN && !between(seq, tp->snd_una, tp->snd_nxt)) {
+ net_statistics.OutOfWindowIcmps++;
+ return;
+ }
+
+ switch (type) {
+ case ICMP_SOURCE_QUENCH:
+#ifndef OLD_SOURCE_QUENCH /* This is deprecated */
+ tp->snd_ssthresh = tcp_recalc_ssthresh(tp);
+ tp->snd_cwnd = tp->snd_ssthresh;
+ tp->snd_cwnd_cnt = 0;
+ tp->high_seq = tp->snd_nxt;
+#endif
+ return;
+ case ICMP_PARAMETERPROB:
+ err = EPROTO;
+ break;
+ case ICMP_DEST_UNREACH:
+ if (code > NR_ICMP_UNREACH)
+ return;
+
+ if (code == ICMP_FRAG_NEEDED) { /* PMTU discovery (RFC1191) */
+ do_pmtu_discovery(sk, iph, ntohs(skb->h.icmph->un.frag.mtu));
+ return;
+ }
+
+ err = icmp_err_convert[code].errno;
+ break;
+ case ICMP_TIME_EXCEEDED:
+ err = EHOSTUNREACH;
+ break;
+ default:
+ return;
+ }
+
+ switch (sk->state) {
+ struct open_request *req, *prev;
+ case TCP_LISTEN:
+ /* Prevent race conditions with accept() -
+ * ICMP is unreliable.
+ */
+ if (atomic_read(&sk->sock_readers)) {
+ net_statistics.LockDroppedIcmps++;
+ /* If too many ICMPs get dropped on busy
+ * servers this needs to be solved differently.
+ */
+ return;
+ }
+
+ /* The final ACK of the handshake should be already
+ * handled in the new socket context, not here.
+ * Strictly speaking - an ICMP error for the final
+ * ACK should set the opening flag, but that is too
+ * complicated right now.
+ */
+ if (!no_flags && !th->syn && !th->ack)
+ return;
+
+ req = tcp_v4_search_req(tp, iph, th, &prev);
+ if (!req)
+ return;
+ if (seq != req->snt_isn) {
+ net_statistics.OutOfWindowIcmps++;
+ return;
+ }
+ if (req->sk) {
+ /*
+ * Already in ESTABLISHED and a big socket is created,
+ * set error code there.
+ * The error will _not_ be reported in the accept(),
+ * but only with the next operation on the socket after
+ * accept.
+ */
+ sk = req->sk;
+ } else {
+ /*
+ * Still in SYN_RECV, just remove it silently.
+ * There is no good way to pass the error to the newly
+ * created socket, and POSIX does not want network
+ * errors returned from accept().
+ */
+ tp->syn_backlog--;
+ tcp_synq_unlink(tp, req, prev);
+ req->class->destructor(req);
+ tcp_openreq_free(req);
+ return;
+ }
+ break;
+ case TCP_SYN_SENT:
+ case TCP_SYN_RECV: /* Cannot happen */
+ if (!no_flags && !th->syn)
+ return;
+ tcp_statistics.TcpAttemptFails++;
+ sk->err = err;
+ sk->zapped = 1;
+ mb();
+ sk->error_report(sk);
+ return;
+ }
+
+ /* If we've already connected we will keep trying
+ * until we time out, or the user gives up.
+ *
+ * rfc1122 4.2.3.9 allows to consider as hard errors
+ * only PROTO_UNREACH and PORT_UNREACH (well, FRAG_FAILED too,
+ * but it is obsoleted by pmtu discovery).
+ *
+ * Note, that in modern internet, where routing is unreliable
+ * and in each dark corner broken firewalls sit, sending random
+ * errors ordered by their masters even this two messages finally lose
+ * their original sense (even Linux sends invalid PORT_UNREACHs)
+ *
+ * Now we are in compliance with RFCs.
+ * --ANK (980905)
+ */
+
+ if (sk->ip_recverr) {
+ /* This code isn't serialized with the socket code */
+ /* ANK (980927) ... which is harmless now,
+ sk->err's may be safely lost.
+ */
+ sk->err = err;
+ mb();
+ sk->error_report(sk); /* Wake people up to see the error (see connect in sock.c) */
+ } else { /* Only an error on timeout */
+ sk->err_soft = err;
+ mb();
+ }
+}
+
+/* This routine computes an IPv4 TCP checksum. */
+void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len,
+ struct sk_buff *skb)
+{
+ th->check = 0;
+ th->check = tcp_v4_check(th, len, sk->saddr, sk->daddr,
+ csum_partial((char *)th, th->doff<<2, skb->csum));
+}
+
+/*
+ * This routine will send an RST to the other tcp.
+ *
+ * Someone asks: why I NEVER use socket parameters (TOS, TTL etc.)
+ * for reset.
+ * Answer: if a packet caused RST, it is not for a socket
+ * existing in our system, if it is matched to a socket,
+ * it is just duplicate segment or bug in other side's TCP.
+ * So that we build reply only basing on parameters
+ * arrived with segment.
+ * Exception: precedence violation. We do not implement it in any case.
+ */
+
+static void tcp_v4_send_reset(struct sk_buff *skb)
+{
+ struct tcphdr *th = skb->h.th;
+ struct tcphdr rth;
+ struct ip_reply_arg arg;
+
+ /* Never send a reset in response to a reset. */
+ if (th->rst)
+ return;
+
+ if (((struct rtable*)skb->dst)->rt_type != RTN_LOCAL) {
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ if (((struct rtable*)skb->dst)->rt_type == RTN_UNICAST)
+ icmp_send(skb, ICMP_DEST_UNREACH,
+ ICMP_PORT_UNREACH, 0);
+#endif
+ return;
+ }
+
+ /* Swap the send and the receive. */
+ memset(&rth, 0, sizeof(struct tcphdr));
+ rth.dest = th->source;
+ rth.source = th->dest;
+ rth.doff = sizeof(struct tcphdr)/4;
+ rth.rst = 1;
+
+ if (th->ack) {
+ rth.seq = th->ack_seq;
+ } else {
+ rth.ack = 1;
+ rth.ack_seq = th->syn ? htonl(ntohl(th->seq)+1) : th->seq;
+ }
+
+ memset(&arg, 0, sizeof arg);
+ arg.iov[0].iov_base = (unsigned char *)&rth;
+ arg.iov[0].iov_len = sizeof rth;
+ arg.csum = csum_tcpudp_nofold(skb->nh.iph->daddr,
+ skb->nh.iph->saddr, /*XXX*/
+ sizeof(struct tcphdr),
+ IPPROTO_TCP,
+ 0);
+ arg.n_iov = 1;
+ arg.csumoffset = offsetof(struct tcphdr, check) / 2;
+
+ ip_send_reply(tcp_socket->sk, skb, &arg, sizeof rth);
+
+ tcp_statistics.TcpOutSegs++;
+ tcp_statistics.TcpOutRsts++;
+}
+
+/*
+ * Send an ACK for a socket less packet (needed for time wait)
+ *
+ * FIXME: Does not echo timestamps yet.
+ *
+ * Assumes that the caller did basic address and flag checks.
+ */
+static void tcp_v4_send_ack(struct sk_buff *skb, __u32 seq, __u32 ack, __u16 window)
+{
+ struct tcphdr *th = skb->h.th;
+ struct tcphdr rth;
+ struct ip_reply_arg arg;
+
+ /* Swap the send and the receive. */
+ memset(&rth, 0, sizeof(struct tcphdr));
+ rth.dest = th->source;
+ rth.source = th->dest;
+ rth.doff = sizeof(struct tcphdr)/4;
+
+ rth.seq = seq;
+ rth.ack_seq = ack;
+ rth.ack = 1;
+
+ rth.window = htons(window);
+
+ memset(&arg, 0, sizeof arg);
+ arg.iov[0].iov_base = (unsigned char *)&rth;
+ arg.iov[0].iov_len = sizeof rth;
+ arg.csum = csum_tcpudp_nofold(skb->nh.iph->daddr,
+ skb->nh.iph->saddr, /*XXX*/
+ sizeof(struct tcphdr),
+ IPPROTO_TCP,
+ 0);
+ arg.n_iov = 1;
+ arg.csumoffset = offsetof(struct tcphdr, check) / 2;
+
+ ip_send_reply(tcp_socket->sk, skb, &arg, sizeof rth);
+
+ tcp_statistics.TcpOutSegs++;
+}
+
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+
+/*
+ Seems, I never wrote nothing more stupid.
+ I hope Gods will forgive me, but I cannot forgive myself 8)
+ --ANK (981001)
+ */
+
+static struct sock *tcp_v4_search_proxy_openreq(struct sk_buff *skb)
+{
+ struct iphdr *iph = skb->nh.iph;
+ struct tcphdr *th = (struct tcphdr *)(skb->nh.raw + iph->ihl*4);
+ struct sock *sk;
+ int i;
+
+ for (i=0; i<TCP_LHTABLE_SIZE; i++) {
+ for(sk = tcp_listening_hash[i]; sk; sk = sk->next) {
+ struct open_request *dummy;
+ if (tcp_v4_search_req(&sk->tp_pinfo.af_tcp, iph,
+ th, &dummy) &&
+ (!sk->bound_dev_if ||
+ sk->bound_dev_if == skb->dev->ifindex))
+ return sk;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Check whether a received TCP packet might be for one of our
+ * connections.
+ */
+
+int tcp_chkaddr(struct sk_buff *skb)
+{
+ struct iphdr *iph = skb->nh.iph;
+ struct tcphdr *th = (struct tcphdr *)(skb->nh.raw + iph->ihl*4);
+ struct sock *sk;
+
+ sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr,
+ th->dest, skb->dev->ifindex);
+
+ if (!sk)
+ return tcp_v4_search_proxy_openreq(skb) != NULL;
+
+ if (sk->state == TCP_LISTEN) {
+ struct open_request *dummy;
+ if (tcp_v4_search_req(&sk->tp_pinfo.af_tcp, skb->nh.iph,
+ th, &dummy) &&
+ (!sk->bound_dev_if ||
+ sk->bound_dev_if == skb->dev->ifindex))
+ return 1;
+ }
+
+ /* 0 means accept all LOCAL addresses here, not all the world... */
+
+ if (sk->rcv_saddr == 0)
+ return 0;
+
+ return 1;
+}
+#endif
+
+/*
+ * Send a SYN-ACK after having received an ACK.
+ * This still operates on a open_request only, not on a big
+ * socket.
+ */
+static void tcp_v4_send_synack(struct sock *sk, struct open_request *req)
+{
+ struct rtable *rt;
+ struct ip_options *opt;
+ struct sk_buff * skb;
+ int mss;
+
+ /* First, grab a route. */
+ opt = req->af.v4_req.opt;
+ if(ip_route_output(&rt, ((opt && opt->srr) ?
+ opt->faddr :
+ req->af.v4_req.rmt_addr),
+ req->af.v4_req.loc_addr,
+ RT_TOS(sk->ip_tos) | RTO_CONN | sk->localroute,
+ sk->bound_dev_if)) {
+ ip_statistics.IpOutNoRoutes++;
+ return;
+ }
+ if(opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) {
+ ip_rt_put(rt);
+ ip_statistics.IpOutNoRoutes++;
+ return;
+ }
+
+ mss = rt->u.dst.pmtu - sizeof(struct iphdr) - sizeof(struct tcphdr);
+
+ skb = tcp_make_synack(sk, &rt->u.dst, req, mss);
+ if (skb) {
+ struct tcphdr *th = skb->h.th;
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ th->source = req->lcl_port; /* LVE */
+#endif
+
+ th->check = tcp_v4_check(th, skb->len,
+ req->af.v4_req.loc_addr, req->af.v4_req.rmt_addr,
+ csum_partial((char *)th, skb->len, skb->csum));
+
+ ip_build_and_send_pkt(skb, sk, req->af.v4_req.loc_addr,
+ req->af.v4_req.rmt_addr, req->af.v4_req.opt);
+ }
+ ip_rt_put(rt);
+}
+
+/*
+ * IPv4 open_request destructor.
+ */
+static void tcp_v4_or_free(struct open_request *req)
+{
+ if(!req->sk && req->af.v4_req.opt)
+ kfree_s(req->af.v4_req.opt, optlength(req->af.v4_req.opt));
+}
+
+static inline void syn_flood_warning(struct sk_buff *skb)
+{
+ static unsigned long warntime;
+
+ if (jiffies - warntime > HZ*60) {
+ warntime = jiffies;
+ printk(KERN_INFO
+ "possible SYN flooding on port %d. Sending cookies.\n",
+ ntohs(skb->h.th->dest));
+ }
+}
+
+/*
+ * Save and compile IPv4 options into the open_request if needed.
+ */
+static inline struct ip_options *
+tcp_v4_save_options(struct sock *sk, struct sk_buff *skb)
+{
+ struct ip_options *opt = &(IPCB(skb)->opt);
+ struct ip_options *dopt = NULL;
+
+ if (opt && opt->optlen) {
+ int opt_size = optlength(opt);
+ dopt = kmalloc(opt_size, GFP_ATOMIC);
+ if (dopt) {
+ if (ip_options_echo(dopt, skb)) {
+ kfree_s(dopt, opt_size);
+ dopt = NULL;
+ }
+ }
+ }
+ return dopt;
+}
+
+/*
+ * Maximum number of SYN_RECV sockets in queue per LISTEN socket.
+ * One SYN_RECV socket costs about 80bytes on a 32bit machine.
+ * It would be better to replace it with a global counter for all sockets
+ * but then some measure against one socket starving all other sockets
+ * would be needed.
+ */
+int sysctl_max_syn_backlog = 128;
+
+struct or_calltable or_ipv4 = {
+ tcp_v4_send_synack,
+ tcp_v4_or_free,
+ tcp_v4_send_reset
+};
+
+#define BACKLOG(sk) ((sk)->tp_pinfo.af_tcp.syn_backlog) /* lvalue! */
+#define BACKLOGMAX(sk) sysctl_max_syn_backlog
+
+int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, __u32 isn)
+{
+ struct tcp_opt tp;
+ struct open_request *req;
+ struct tcphdr *th = skb->h.th;
+ __u32 saddr = skb->nh.iph->saddr;
+ __u32 daddr = skb->nh.iph->daddr;
+#ifdef CONFIG_SYN_COOKIES
+ int want_cookie = 0;
+#else
+#define want_cookie 0 /* Argh, why doesn't gcc optimize this :( */
+#endif
+
+ /* If the socket is dead, don't accept the connection. */
+ if (sk->dead)
+ goto dead;
+
+ /* Never answer to SYNs send to broadcast or multicast */
+ if (((struct rtable *)skb->dst)->rt_flags &
+ (RTCF_BROADCAST|RTCF_MULTICAST))
+ goto drop;
+
+ /* XXX: Check against a global syn pool counter. */
+ if (BACKLOG(sk) > BACKLOGMAX(sk)) {
+#ifdef CONFIG_SYN_COOKIES
+ if (sysctl_tcp_syncookies) {
+ syn_flood_warning(skb);
+ want_cookie = 1;
+ } else
+#endif
+ goto drop;
+ } else {
+ if (isn == 0)
+ isn = tcp_v4_init_sequence(sk, skb);
+ BACKLOG(sk)++;
+ }
+
+ req = tcp_openreq_alloc();
+ if (req == NULL) {
+ goto dropbacklog;
+ }
+
+ req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */
+
+ req->rcv_isn = TCP_SKB_CB(skb)->seq;
+ tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0;
+
+ tp.mss_clamp = 65535;
+ tcp_parse_options(NULL, th, &tp, want_cookie);
+ if (tp.mss_clamp == 65535)
+ tp.mss_clamp = 576 - sizeof(struct iphdr) - sizeof(struct iphdr);
+
+ if (sk->tp_pinfo.af_tcp.user_mss && sk->tp_pinfo.af_tcp.user_mss < tp.mss_clamp)
+ tp.mss_clamp = sk->tp_pinfo.af_tcp.user_mss;
+ req->mss = tp.mss_clamp;
+
+ if (tp.saw_tstamp)
+ req->ts_recent = tp.rcv_tsval;
+ req->tstamp_ok = tp.tstamp_ok;
+ req->sack_ok = tp.sack_ok;
+ req->snd_wscale = tp.snd_wscale;
+ req->wscale_ok = tp.wscale_ok;
+ req->rmt_port = th->source;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ req->lcl_port = th->dest ; /* LVE */
+#endif
+ req->af.v4_req.loc_addr = daddr;
+ req->af.v4_req.rmt_addr = saddr;
+
+ /* Note that we ignore the isn passed from the TIME_WAIT
+ * state here. That's the price we pay for cookies.
+ */
+ if (want_cookie)
+ isn = cookie_v4_init_sequence(sk, skb, &req->mss);
+
+ req->snt_isn = isn;
+
+ req->af.v4_req.opt = tcp_v4_save_options(sk, skb);
+
+ req->class = &or_ipv4;
+ req->retrans = 0;
+ req->sk = NULL;
+
+ tcp_v4_send_synack(sk, req);
+
+ if (want_cookie) {
+ if (req->af.v4_req.opt)
+ kfree(req->af.v4_req.opt);
+ tcp_v4_or_free(req);
+ tcp_openreq_free(req);
+ } else {
+ req->expires = jiffies + TCP_TIMEOUT_INIT;
+ tcp_inc_slow_timer(TCP_SLT_SYNACK);
+ tcp_synq_queue(&sk->tp_pinfo.af_tcp, req);
+ }
+
+ return 0;
+
+dead:
+ SOCK_DEBUG(sk, "Reset on %p: Connect on dead socket.\n",sk);
+ tcp_statistics.TcpAttemptFails++;
+ return -ENOTCONN; /* send reset */
+
+dropbacklog:
+ if (!want_cookie)
+ BACKLOG(sk)--;
+drop:
+ tcp_statistics.TcpAttemptFails++;
+ return 0;
+}
+
+/* This is not only more efficient than what we used to do, it eliminates
+ * a lot of code duplication between IPv4/IPv6 SYN recv processing. -DaveM
+ *
+ * This function wants to be moved to a common for IPv[46] file. --ANK
+ */
+struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, struct sk_buff *skb)
+{
+ struct sock *newsk = sk_alloc(PF_INET, GFP_ATOMIC, 0);
+
+ if(newsk != NULL) {
+ struct tcp_opt *newtp;
+#ifdef CONFIG_FILTER
+ struct sk_filter *filter;
+#endif
+
+ memcpy(newsk, sk, sizeof(*newsk));
+ newsk->sklist_next = NULL;
+ newsk->state = TCP_SYN_RECV;
+
+ /* Clone the TCP header template */
+ newsk->dport = req->rmt_port;
+
+ atomic_set(&newsk->sock_readers, 0);
+ atomic_set(&newsk->rmem_alloc, 0);
+ skb_queue_head_init(&newsk->receive_queue);
+ atomic_set(&newsk->wmem_alloc, 0);
+ skb_queue_head_init(&newsk->write_queue);
+ atomic_set(&newsk->omem_alloc, 0);
+
+ newsk->done = 0;
+ newsk->proc = 0;
+ skb_queue_head_init(&newsk->back_log);
+ skb_queue_head_init(&newsk->error_queue);
+#ifdef CONFIG_FILTER
+ if ((filter = newsk->filter) != NULL)
+ sk_filter_charge(newsk, filter);
+#endif
+
+ /* Now setup tcp_opt */
+ newtp = &(newsk->tp_pinfo.af_tcp);
+ newtp->pred_flags = 0;
+ newtp->rcv_nxt = req->rcv_isn + 1;
+ newtp->snd_nxt = req->snt_isn + 1;
+ newtp->snd_una = req->snt_isn + 1;
+ newtp->srtt = 0;
+ newtp->ato = 0;
+ newtp->snd_wl1 = req->rcv_isn;
+ newtp->snd_wl2 = req->snt_isn;
+
+ /* RFC1323: The window in SYN & SYN/ACK segments
+ * is never scaled.
+ */
+ newtp->snd_wnd = ntohs(skb->h.th->window);
+
+ newtp->max_window = newtp->snd_wnd;
+ newtp->pending = 0;
+ newtp->retransmits = 0;
+ newtp->last_ack_sent = req->rcv_isn + 1;
+ newtp->backoff = 0;
+ newtp->mdev = TCP_TIMEOUT_INIT;
+
+ /* So many TCP implementations out there (incorrectly) count the
+ * initial SYN frame in their delayed-ACK and congestion control
+ * algorithms that we must have the following bandaid to talk
+ * efficiently to them. -DaveM
+ */
+ newtp->snd_cwnd = 2;
+
+ newtp->rto = TCP_TIMEOUT_INIT;
+ newtp->packets_out = 0;
+ newtp->fackets_out = 0;
+ newtp->retrans_out = 0;
+ newtp->high_seq = 0;
+ newtp->snd_ssthresh = 0x7fffffff;
+ newtp->snd_cwnd_cnt = 0;
+ newtp->dup_acks = 0;
+ newtp->delayed_acks = 0;
+ init_timer(&newtp->retransmit_timer);
+ newtp->retransmit_timer.function = &tcp_retransmit_timer;
+ newtp->retransmit_timer.data = (unsigned long) newsk;
+ init_timer(&newtp->delack_timer);
+ newtp->delack_timer.function = &tcp_delack_timer;
+ newtp->delack_timer.data = (unsigned long) newsk;
+ skb_queue_head_init(&newtp->out_of_order_queue);
+ newtp->send_head = newtp->retrans_head = NULL;
+ newtp->rcv_wup = req->rcv_isn + 1;
+ newtp->write_seq = req->snt_isn + 1;
+ newtp->copied_seq = req->rcv_isn + 1;
+
+ newtp->saw_tstamp = 0;
+ newtp->mss_clamp = req->mss;
+
+ init_timer(&newtp->probe_timer);
+ newtp->probe_timer.function = &tcp_probe_timer;
+ newtp->probe_timer.data = (unsigned long) newsk;
+ newtp->probes_out = 0;
+ newtp->syn_seq = req->rcv_isn;
+ newtp->fin_seq = req->rcv_isn;
+ newtp->urg_data = 0;
+ tcp_synq_init(newtp);
+ newtp->syn_backlog = 0;
+ if (skb->len >= 536)
+ newtp->last_seg_size = skb->len;
+
+ /* Back to base struct sock members. */
+ newsk->err = 0;
+ newsk->ack_backlog = 0;
+ newsk->max_ack_backlog = SOMAXCONN;
+ newsk->priority = 0;
+
+ /* IP layer stuff */
+ newsk->timeout = 0;
+ init_timer(&newsk->timer);
+ newsk->timer.function = &net_timer;
+ newsk->timer.data = (unsigned long) newsk;
+ newsk->socket = NULL;
+
+ newtp->tstamp_ok = req->tstamp_ok;
+ if((newtp->sack_ok = req->sack_ok) != 0)
+ newtp->num_sacks = 0;
+ newtp->window_clamp = req->window_clamp;
+ newtp->rcv_wnd = req->rcv_wnd;
+ newtp->wscale_ok = req->wscale_ok;
+ if (newtp->wscale_ok) {
+ newtp->snd_wscale = req->snd_wscale;
+ newtp->rcv_wscale = req->rcv_wscale;
+ } else {
+ newtp->snd_wscale = newtp->rcv_wscale = 0;
+ newtp->window_clamp = min(newtp->window_clamp,65535);
+ }
+ if (newtp->tstamp_ok) {
+ newtp->ts_recent = req->ts_recent;
+ newtp->ts_recent_stamp = tcp_time_stamp;
+ newtp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;
+ } else {
+ newtp->tcp_header_len = sizeof(struct tcphdr);
+ }
+ }
+ return newsk;
+}
+
+/*
+ * The three way handshake has completed - we got a valid synack -
+ * now create the new socket.
+ */
+struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
+ struct open_request *req,
+ struct dst_entry *dst)
+{
+ struct ip_options *opt = req->af.v4_req.opt;
+ struct tcp_opt *newtp;
+ struct sock *newsk;
+
+ if (sk->ack_backlog > sk->max_ack_backlog)
+ goto exit; /* head drop */
+ if (dst == NULL) {
+ struct rtable *rt;
+
+ if (ip_route_output(&rt,
+ opt && opt->srr ? opt->faddr : req->af.v4_req.rmt_addr,
+ req->af.v4_req.loc_addr, sk->ip_tos|RTO_CONN, 0))
+ return NULL;
+ dst = &rt->u.dst;
+ }
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ /* The new socket created for transparent proxy may fall
+ * into a non-existed bind bucket because sk->num != newsk->num.
+ * Ensure existence of the bucket now. The placement of the check
+ * later will require to destroy just created newsk in the case of fail.
+ * 1998/04/22 Andrey V. Savochkin <saw@msu.ru>
+ */
+ if (__tcp_bucket_check(ntohs(skb->h.th->dest)))
+ goto exit;
+#endif
+
+ newsk = tcp_create_openreq_child(sk, req, skb);
+ if (!newsk)
+ goto exit;
+
+ sk->tp_pinfo.af_tcp.syn_backlog--;
+ sk->ack_backlog++;
+
+ newsk->dst_cache = dst;
+
+ newtp = &(newsk->tp_pinfo.af_tcp);
+ newsk->daddr = req->af.v4_req.rmt_addr;
+ newsk->saddr = req->af.v4_req.loc_addr;
+ newsk->rcv_saddr = req->af.v4_req.loc_addr;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ newsk->num = ntohs(skb->h.th->dest);
+ newsk->sport = req->lcl_port;
+#endif
+ newsk->opt = req->af.v4_req.opt;
+ newtp->ext_header_len = 0;
+ if (newsk->opt)
+ newtp->ext_header_len = newsk->opt->optlen;
+
+ tcp_sync_mss(newsk, dst->pmtu);
+ newtp->rcv_mss = newtp->mss_clamp;
+
+ /* It would be better to use newtp->mss_clamp here */
+ if (newsk->rcvbuf < (3 * newtp->pmtu_cookie))
+ newsk->rcvbuf = min ((3 * newtp->pmtu_cookie), sysctl_rmem_max);
+ if (newsk->sndbuf < (3 * newtp->pmtu_cookie))
+ newsk->sndbuf = min ((3 * newtp->pmtu_cookie), sysctl_wmem_max);
+
+ /* We run in BH processing itself or within a BH atomic
+ * sequence (backlog) so no locking is needed.
+ */
+ __tcp_v4_hash(newsk);
+ __tcp_inherit_port(sk, newsk);
+ __add_to_prot_sklist(newsk);
+
+ sk->data_ready(sk, 0); /* Deliver SIGIO */
+
+ return newsk;
+
+exit:
+ dst_release(dst);
+ return NULL;
+}
+
+static void tcp_v4_rst_req(struct sock *sk, struct sk_buff *skb)
+{
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+ struct open_request *req, *prev;
+
+ req = tcp_v4_search_req(tp,skb->nh.iph, skb->h.th, &prev);
+ if (!req)
+ return;
+ /* Sequence number check required by RFC793 */
+ if (before(TCP_SKB_CB(skb)->seq, req->rcv_isn) ||
+ after(TCP_SKB_CB(skb)->seq, req->rcv_isn+1))
+ return;
+ tcp_synq_unlink(tp, req, prev);
+ if (req->sk)
+ sk->ack_backlog--;
+ else
+ tp->syn_backlog--;
+ req->class->destructor(req);
+ tcp_openreq_free(req);
+
+ net_statistics.EmbryonicRsts++;
+}
+
+/* Check for embryonic sockets (open_requests) We check packets with
+ * only the SYN bit set against the open_request queue too: This
+ * increases connection latency a bit, but is required to detect
+ * retransmitted SYNs.
+ */
+static inline struct sock *tcp_v4_hnd_req(struct sock *sk,struct sk_buff *skb)
+{
+ struct tcphdr *th = skb->h.th;
+ u32 flg = ((u32 *)th)[3];
+
+ /* Check for RST */
+ if (flg & __constant_htonl(0x00040000)) {
+ tcp_v4_rst_req(sk, skb);
+ return NULL;
+ }
+
+ /* Check for SYN|ACK */
+ if (flg & __constant_htonl(0x00120000)) {
+ struct open_request *req, *dummy;
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ /* Find possible connection requests. */
+ req = tcp_v4_search_req(tp, skb->nh.iph, th, &dummy);
+ if (req) {
+ sk = tcp_check_req(sk, skb, req);
+ }
+#ifdef CONFIG_SYN_COOKIES
+ else if ((flg & __constant_htonl(0x00120000))==__constant_htonl(0x00100000))
+ {
+ sk = cookie_v4_check(sk, skb, &(IPCB(skb)->opt));
+ }
+#endif
+ }
+ return sk;
+}
+
+int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
+{
+#ifdef CONFIG_FILTER
+ struct sk_filter *filter = sk->filter;
+ if (filter && sk_filter(skb, filter))
+ goto discard;
+#endif /* CONFIG_FILTER */
+
+ /*
+ * This doesn't check if the socket has enough room for the packet.
+ * Either process the packet _without_ queueing it and then free it,
+ * or do the check later.
+ */
+ skb_set_owner_r(skb, sk);
+
+ if (sk->state == TCP_ESTABLISHED) { /* Fast path */
+ if (tcp_rcv_established(sk, skb, skb->h.th, skb->len))
+ goto reset;
+ return 0;
+ }
+
+
+ if (sk->state == TCP_LISTEN) {
+ struct sock *nsk;
+
+ nsk = tcp_v4_hnd_req(sk, skb);
+ if (!nsk)
+ goto discard;
+
+ /*
+ * Queue it on the new socket if the new socket is active,
+ * otherwise we just shortcircuit this and continue with
+ * the new socket..
+ */
+ if (atomic_read(&nsk->sock_readers)) {
+ skb_orphan(skb);
+ __skb_queue_tail(&nsk->back_log, skb);
+ return 0;
+ }
+ sk = nsk;
+ }
+
+ if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len))
+ goto reset;
+ return 0;
+
+reset:
+ tcp_v4_send_reset(skb);
+discard:
+ kfree_skb(skb);
+ /* Be careful here. If this function gets more complicated and
+ * gcc suffers from register pressure on the x86, sk (in %ebx)
+ * might be destroyed here. This current version compiles correctly,
+ * but you have been warned.
+ */
+ return 0;
+}
+
+/*
+ * From tcp_input.c
+ */
+
+int tcp_v4_rcv(struct sk_buff *skb, unsigned short len)
+{
+ struct tcphdr *th;
+ struct sock *sk;
+
+ if (skb->pkt_type!=PACKET_HOST)
+ goto discard_it;
+
+ th = skb->h.th;
+
+ /* Pull up the IP header. */
+ __skb_pull(skb, skb->h.raw - skb->data);
+
+ /* Count it even if it's bad */
+ tcp_statistics.TcpInSegs++;
+
+ len = skb->len;
+ if (len < sizeof(struct tcphdr))
+ goto bad_packet;
+
+ /* Try to use the device checksum if provided. */
+ switch (skb->ip_summed) {
+ case CHECKSUM_NONE:
+ skb->csum = csum_partial((char *)th, len, 0);
+ case CHECKSUM_HW:
+ if (tcp_v4_check(th,len,skb->nh.iph->saddr,skb->nh.iph->daddr,skb->csum)) {
+ NETDEBUG(printk(KERN_DEBUG "TCPv4 bad checksum "
+ "from %d.%d.%d.%d:%04x to %d.%d.%d.%d:%04x, "
+ "len=%d/%d/%d\n",
+ NIPQUAD(skb->nh.iph->saddr),
+ ntohs(th->source),
+ NIPQUAD(skb->nh.iph->daddr),
+ ntohs(th->dest),
+ len, skb->len,
+ ntohs(skb->nh.iph->tot_len)));
+ bad_packet:
+ tcp_statistics.TcpInErrs++;
+ goto discard_it;
+ }
+ default:
+ ; /* CHECKSUM_UNNECESSARY */
+ }
+
+ if((th->doff * 4) < sizeof(struct tcphdr) ||
+ len < (th->doff * 4))
+ goto bad_packet;
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ if (IPCB(skb)->redirport)
+ sk = tcp_v4_proxy_lookup(th->dest, skb->nh.iph->saddr, th->source,
+ skb->nh.iph->daddr, skb->dev,
+ IPCB(skb)->redirport, skb->dev->ifindex);
+ else {
+#endif
+ sk = __tcp_v4_lookup(th, skb->nh.iph->saddr, th->source,
+ skb->nh.iph->daddr, th->dest, skb->dev->ifindex);
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ if (!sk)
+ sk = tcp_v4_search_proxy_openreq(skb);
+ }
+#endif
+ if (!sk)
+ goto no_tcp_socket;
+ if(!ipsec_sk_policy(sk,skb))
+ goto discard_it;
+
+ TCP_SKB_CB(skb)->seq = ntohl(th->seq);
+ TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
+ len - th->doff*4);
+ TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
+
+ skb->used = 0;
+
+ if (sk->state == TCP_TIME_WAIT)
+ goto do_time_wait;
+ if (!atomic_read(&sk->sock_readers))
+ return tcp_v4_do_rcv(sk, skb);
+
+ __skb_queue_tail(&sk->back_log, skb);
+ return 0;
+
+no_tcp_socket:
+ tcp_v4_send_reset(skb);
+
+discard_it:
+ /* Discard frame. */
+ kfree_skb(skb);
+ return 0;
+
+do_time_wait:
+ /* Sorry for the ugly switch. 2.3 will have a better solution. */
+ switch (tcp_timewait_state_process((struct tcp_tw_bucket *)sk,
+ skb, th, skb->len)) {
+ case TCP_TW_ACK:
+ tcp_v4_send_ack(skb,
+ ((struct tcp_tw_bucket *)sk)->snd_nxt,
+ ((struct tcp_tw_bucket *)sk)->rcv_nxt,
+ ((struct tcp_tw_bucket *)sk)->window);
+ goto discard_it;
+ case TCP_TW_RST:
+ goto no_tcp_socket;
+ default:
+ goto discard_it;
+ }
+}
+
+static void __tcp_v4_rehash(struct sock *sk)
+{
+ struct sock **skp = &tcp_ehash[(sk->hashent = tcp_sk_hashfn(sk))];
+
+ SOCKHASH_LOCK();
+ if(sk->pprev) {
+ if(sk->next)
+ sk->next->pprev = sk->pprev;
+ *sk->pprev = sk->next;
+ sk->pprev = NULL;
+ tcp_reg_zap(sk);
+ }
+ if((sk->next = *skp) != NULL)
+ (*skp)->pprev = &sk->next;
+ *skp = sk;
+ sk->pprev = skp;
+ SOCKHASH_UNLOCK();
+}
+
+int tcp_v4_rebuild_header(struct sock *sk)
+{
+ struct rtable *rt = (struct rtable *)sk->dst_cache;
+ __u32 new_saddr;
+ int want_rewrite = sysctl_ip_dynaddr && sk->state == TCP_SYN_SENT;
+
+ if(rt == NULL)
+ return 0;
+
+ /* Force route checking if want_rewrite.
+ * The idea is good, the implementation is disguisting.
+ * Well, if I made bind on this socket, you cannot randomly ovewrite
+ * its source address. --ANK
+ */
+ if (want_rewrite) {
+ int tmp;
+ struct rtable *new_rt;
+ __u32 old_saddr = rt->rt_src;
+
+ /* Query new route using another rt buffer */
+ tmp = ip_route_connect(&new_rt, rt->rt_dst, 0,
+ RT_TOS(sk->ip_tos)|sk->localroute,
+ sk->bound_dev_if);
+
+ /* Only useful if different source addrs */
+ if (tmp == 0) {
+ /*
+ * Only useful if different source addrs
+ */
+ if (new_rt->rt_src != old_saddr ) {
+ dst_release(sk->dst_cache);
+ sk->dst_cache = &new_rt->u.dst;
+ rt = new_rt;
+ goto do_rewrite;
+ }
+ dst_release(&new_rt->u.dst);
+ }
+ }
+ if (rt->u.dst.obsolete) {
+ int err;
+ err = ip_route_output(&rt, rt->rt_dst, rt->rt_src, rt->key.tos|RTO_CONN, rt->key.oif);
+ if (err) {
+ sk->err_soft=-err;
+ sk->error_report(sk);
+ return -1;
+ }
+ dst_release(xchg(&sk->dst_cache, &rt->u.dst));
+ }
+
+ return 0;
+
+do_rewrite:
+ new_saddr = rt->rt_src;
+
+ /* Ouch!, this should not happen. */
+ if (!sk->saddr || !sk->rcv_saddr) {
+ printk(KERN_WARNING "tcp_v4_rebuild_header(): not valid sock addrs: "
+ "saddr=%08X rcv_saddr=%08X\n",
+ ntohl(sk->saddr),
+ ntohl(sk->rcv_saddr));
+ return 0;
+ }
+
+ if (new_saddr != sk->saddr) {
+ if (sysctl_ip_dynaddr > 1) {
+ printk(KERN_INFO "tcp_v4_rebuild_header(): shifting sk->saddr "
+ "from %d.%d.%d.%d to %d.%d.%d.%d\n",
+ NIPQUAD(sk->saddr),
+ NIPQUAD(new_saddr));
+ }
+
+ sk->saddr = new_saddr;
+ sk->rcv_saddr = new_saddr;
+
+ /* XXX The only one ugly spot where we need to
+ * XXX really change the sockets identity after
+ * XXX it has entered the hashes. -DaveM
+ */
+ __tcp_v4_rehash(sk);
+ }
+
+ return 0;
+}
+
+static struct sock * tcp_v4_get_sock(struct sk_buff *skb, struct tcphdr *th)
+{
+ return tcp_v4_lookup(skb->nh.iph->saddr, th->source,
+ skb->nh.iph->daddr, th->dest, skb->dev->ifindex);
+}
+
+static void v4_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *) uaddr;
+
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = sk->daddr;
+ sin->sin_port = sk->dport;
+}
+
+struct tcp_func ipv4_specific = {
+ ip_queue_xmit,
+ tcp_v4_send_check,
+ tcp_v4_rebuild_header,
+ tcp_v4_conn_request,
+ tcp_v4_syn_recv_sock,
+ tcp_v4_get_sock,
+ sizeof(struct iphdr),
+
+ ip_setsockopt,
+ ip_getsockopt,
+ v4_addr2sockaddr,
+ sizeof(struct sockaddr_in)
+};
+
+/* NOTE: A lot of things set to zero explicitly by call to
+ * sk_alloc() so need not be done here.
+ */
+static int tcp_v4_init_sock(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ skb_queue_head_init(&tp->out_of_order_queue);
+ tcp_init_xmit_timers(sk);
+
+ tp->rto = TCP_TIMEOUT_INIT; /*TCP_WRITE_TIME*/
+ tp->mdev = TCP_TIMEOUT_INIT;
+ tp->mss_clamp = ~0;
+
+ /* So many TCP implementations out there (incorrectly) count the
+ * initial SYN frame in their delayed-ACK and congestion control
+ * algorithms that we must have the following bandaid to talk
+ * efficiently to them. -DaveM
+ */
+ tp->snd_cwnd = 2;
+
+ /* See draft-stevens-tcpca-spec-01 for discussion of the
+ * initialization of these values.
+ */
+ tp->snd_cwnd_cnt = 0;
+ tp->snd_ssthresh = 0x7fffffff; /* Infinity */
+
+ sk->state = TCP_CLOSE;
+ sk->max_ack_backlog = SOMAXCONN;
+ tp->rcv_mss = 536;
+
+ sk->write_space = tcp_write_space;
+
+ /* Init SYN queue. */
+ tcp_synq_init(tp);
+
+ sk->tp_pinfo.af_tcp.af_specific = &ipv4_specific;
+
+ return 0;
+}
+
+static int tcp_v4_destroy_sock(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct sk_buff *skb;
+
+ tcp_clear_xmit_timers(sk);
+
+ if (sk->keepopen)
+ tcp_dec_slow_timer(TCP_SLT_KEEPALIVE);
+
+ /* Cleanup up the write buffer. */
+ while((skb = __skb_dequeue(&sk->write_queue)) != NULL)
+ kfree_skb(skb);
+
+ /* Cleans up our, hopefuly empty, out_of_order_queue. */
+ while((skb = __skb_dequeue(&tp->out_of_order_queue)) != NULL)
+ kfree_skb(skb);
+
+ /* Clean up a referenced TCP bind bucket, this only happens if a
+ * port is allocated for a socket, but it never fully connects.
+ */
+ if(sk->prev != NULL)
+ tcp_put_port(sk);
+
+ return 0;
+}
+
+struct proto tcp_prot = {
+ (struct sock *)&tcp_prot, /* sklist_next */
+ (struct sock *)&tcp_prot, /* sklist_prev */
+ tcp_close, /* close */
+ tcp_v4_connect, /* connect */
+ tcp_accept, /* accept */
+ NULL, /* retransmit */
+ tcp_write_wakeup, /* write_wakeup */
+ tcp_read_wakeup, /* read_wakeup */
+ tcp_poll, /* poll */
+ tcp_ioctl, /* ioctl */
+ tcp_v4_init_sock, /* init */
+ tcp_v4_destroy_sock, /* destroy */
+ tcp_shutdown, /* shutdown */
+ tcp_setsockopt, /* setsockopt */
+ tcp_getsockopt, /* getsockopt */
+ tcp_v4_sendmsg, /* sendmsg */
+ tcp_recvmsg, /* recvmsg */
+ NULL, /* bind */
+ tcp_v4_do_rcv, /* backlog_rcv */
+ tcp_v4_hash, /* hash */
+ tcp_v4_unhash, /* unhash */
+ tcp_v4_get_port, /* get_port */
+ 128, /* max_header */
+ 0, /* retransmits */
+ "TCP", /* name */
+ 0, /* inuse */
+ 0 /* highestinuse */
+};
+
+
+
+__initfunc(void tcp_v4_init(struct net_proto_family *ops))
+{
+ int err;
+
+ tcp_inode.i_mode = S_IFSOCK;
+ tcp_inode.i_sock = 1;
+ tcp_inode.i_uid = 0;
+ tcp_inode.i_gid = 0;
+
+ tcp_socket->inode = &tcp_inode;
+ tcp_socket->state = SS_UNCONNECTED;
+ tcp_socket->type=SOCK_RAW;
+
+ if ((err=ops->create(tcp_socket, IPPROTO_TCP))<0)
+ panic("Failed to create the TCP control socket.\n");
+ tcp_socket->sk->allocation=GFP_ATOMIC;
+ tcp_socket->sk->num = 256; /* Don't receive any data */
+ tcp_socket->sk->ip_ttl = MAXTTL;
+}
diff --git a/pfinet/linux-src/net/ipv4/tcp_output.c b/pfinet/linux-src/net/ipv4/tcp_output.c
new file mode 100644
index 00000000..9ea4b7ad
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/tcp_output.c
@@ -0,0 +1,1143 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Implementation of the Transmission Control Protocol(TCP).
+ *
+ * Version: $Id: tcp_output.c,v 1.108.2.1 1999/05/14 23:07:36 davem Exp $
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ * Corey Minyard <wf-rch!minyard@relay.EU.net>
+ * Florian La Roche, <flla@stud.uni-sb.de>
+ * Charles Hedrick, <hedrick@klinzhai.rutgers.edu>
+ * Linus Torvalds, <torvalds@cs.helsinki.fi>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ * Matthew Dillon, <dillon@apollo.west.oic.com>
+ * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ * Jorge Cwik, <jorge@laser.satlink.net>
+ */
+
+/*
+ * Changes: Pedro Roque : Retransmit queue handled by TCP.
+ * : Fragmentation on mtu decrease
+ * : Segment collapse on retransmit
+ * : AF independence
+ *
+ * Linus Torvalds : send_delayed_ack
+ * David S. Miller : Charge memory using the right skb
+ * during syn/ack processing.
+ * David S. Miller : Output engine completely rewritten.
+ * Andrea Arcangeli: SYNACK carry ts_recent in tsecr.
+ *
+ */
+
+#include <net/tcp.h>
+
+extern int sysctl_tcp_timestamps;
+extern int sysctl_tcp_window_scaling;
+extern int sysctl_tcp_sack;
+
+/* People can turn this off for buggy TCP's found in printers etc. */
+int sysctl_tcp_retrans_collapse = 1;
+
+/* Get rid of any delayed acks, we sent one already.. */
+static __inline__ void clear_delayed_acks(struct sock * sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ tp->delayed_acks = 0;
+ if(tcp_in_quickack_mode(tp))
+ tcp_exit_quickack_mode(tp);
+ tcp_clear_xmit_timer(sk, TIME_DACK);
+}
+
+static __inline__ void update_send_head(struct sock *sk)
+{
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+
+ tp->send_head = tp->send_head->next;
+ if (tp->send_head == (struct sk_buff *) &sk->write_queue)
+ tp->send_head = NULL;
+}
+
+/* This routine actually transmits TCP packets queued in by
+ * tcp_do_sendmsg(). This is used by both the initial
+ * transmission and possible later retransmissions.
+ * All SKB's seen here are completely headerless. It is our
+ * job to build the TCP header, and pass the packet down to
+ * IP so it can do the same plus pass the packet off to the
+ * device.
+ *
+ * We are working here with either a clone of the original
+ * SKB, or a fresh unique copy made by the retransmit engine.
+ */
+void tcp_transmit_skb(struct sock *sk, struct sk_buff *skb)
+{
+ if(skb != NULL) {
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
+ int tcp_header_size = tp->tcp_header_len;
+ struct tcphdr *th;
+ int sysctl_flags;
+
+#define SYSCTL_FLAG_TSTAMPS 0x1
+#define SYSCTL_FLAG_WSCALE 0x2
+#define SYSCTL_FLAG_SACK 0x4
+
+ sysctl_flags = 0;
+ if(tcb->flags & TCPCB_FLAG_SYN) {
+ tcp_header_size = sizeof(struct tcphdr) + TCPOLEN_MSS;
+ if(sysctl_tcp_timestamps) {
+ tcp_header_size += TCPOLEN_TSTAMP_ALIGNED;
+ sysctl_flags |= SYSCTL_FLAG_TSTAMPS;
+ }
+ if(sysctl_tcp_window_scaling) {
+ tcp_header_size += TCPOLEN_WSCALE_ALIGNED;
+ sysctl_flags |= SYSCTL_FLAG_WSCALE;
+ }
+ if(sysctl_tcp_sack) {
+ sysctl_flags |= SYSCTL_FLAG_SACK;
+ if(!(sysctl_flags & SYSCTL_FLAG_TSTAMPS))
+ tcp_header_size += TCPOLEN_SACKPERM_ALIGNED;
+ }
+ } else if(tp->sack_ok && tp->num_sacks) {
+ /* A SACK is 2 pad bytes, a 2 byte header, plus
+ * 2 32-bit sequence numbers for each SACK block.
+ */
+ tcp_header_size += (TCPOLEN_SACK_BASE_ALIGNED +
+ (tp->num_sacks * TCPOLEN_SACK_PERBLOCK));
+ }
+ th = (struct tcphdr *) skb_push(skb, tcp_header_size);
+ skb->h.th = th;
+ skb_set_owner_w(skb, sk);
+
+ /* Build TCP header and checksum it. */
+ th->source = sk->sport;
+ th->dest = sk->dport;
+ th->seq = htonl(TCP_SKB_CB(skb)->seq);
+ th->ack_seq = htonl(tp->rcv_nxt);
+ th->doff = (tcp_header_size >> 2);
+ th->res1 = 0;
+ *(((__u8 *)th) + 13) = tcb->flags;
+ if(!(tcb->flags & TCPCB_FLAG_SYN))
+ th->window = htons(tcp_select_window(sk));
+ th->check = 0;
+ th->urg_ptr = ntohs(tcb->urg_ptr);
+ if(tcb->flags & TCPCB_FLAG_SYN) {
+ /* RFC1323: The window in SYN & SYN/ACK segments
+ * is never scaled.
+ */
+ th->window = htons(tp->rcv_wnd);
+ tcp_syn_build_options((__u32 *)(th + 1), tp->mss_clamp,
+ (sysctl_flags & SYSCTL_FLAG_TSTAMPS),
+ (sysctl_flags & SYSCTL_FLAG_SACK),
+ (sysctl_flags & SYSCTL_FLAG_WSCALE),
+ tp->rcv_wscale,
+ TCP_SKB_CB(skb)->when,
+ tp->ts_recent);
+ } else {
+ tcp_build_and_update_options((__u32 *)(th + 1),
+ tp, TCP_SKB_CB(skb)->when);
+ }
+ tp->af_specific->send_check(sk, th, skb->len, skb);
+
+ clear_delayed_acks(sk);
+ tp->last_ack_sent = tp->rcv_nxt;
+ tcp_statistics.TcpOutSegs++;
+ tp->af_specific->queue_xmit(skb);
+ }
+#undef SYSCTL_FLAG_TSTAMPS
+#undef SYSCTL_FLAG_WSCALE
+#undef SYSCTL_FLAG_SACK
+}
+
+/* This is the main buffer sending routine. We queue the buffer
+ * and decide whether to queue or transmit now.
+ */
+void tcp_send_skb(struct sock *sk, struct sk_buff *skb, int force_queue)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ /* Advance write_seq and place onto the write_queue. */
+ tp->write_seq += (TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq);
+ __skb_queue_tail(&sk->write_queue, skb);
+
+ if (!force_queue && tp->send_head == NULL && tcp_snd_test(sk, skb)) {
+ /* Send it out now. */
+ TCP_SKB_CB(skb)->when = tcp_time_stamp;
+ tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
+ tp->packets_out++;
+ tcp_transmit_skb(sk, skb_clone(skb, GFP_KERNEL));
+ if(!tcp_timer_is_set(sk, TIME_RETRANS))
+ tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+ } else {
+ /* Queue it, remembering where we must start sending. */
+ if (tp->send_head == NULL)
+ tp->send_head = skb;
+ if (!force_queue && tp->packets_out == 0 && !tp->pending) {
+ tp->pending = TIME_PROBE0;
+ tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto);
+ }
+ }
+}
+
+/* Function to create two new TCP segments. Shrinks the given segment
+ * to the specified size and appends a new segment with the rest of the
+ * packet to the list. This won't be called frequently, I hope.
+ * Remember, these are still headerless SKBs at this point.
+ */
+static int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len)
+{
+ struct sk_buff *buff;
+ int nsize = skb->len - len;
+ u16 flags;
+
+ /* Get a new skb... force flag on. */
+ buff = sock_wmalloc(sk,
+ (nsize + MAX_HEADER + sk->prot->max_header),
+ 1, GFP_ATOMIC);
+ if (buff == NULL)
+ return -1; /* We'll just try again later. */
+
+ /* Reserve space for headers. */
+ skb_reserve(buff, MAX_HEADER + sk->prot->max_header);
+
+ /* Correct the sequence numbers. */
+ TCP_SKB_CB(buff)->seq = TCP_SKB_CB(skb)->seq + len;
+ TCP_SKB_CB(buff)->end_seq = TCP_SKB_CB(skb)->end_seq;
+
+ /* PSH and FIN should only be set in the second packet. */
+ flags = TCP_SKB_CB(skb)->flags;
+ TCP_SKB_CB(skb)->flags = flags & ~(TCPCB_FLAG_FIN | TCPCB_FLAG_PSH);
+ if(flags & TCPCB_FLAG_URG) {
+ u16 old_urg_ptr = TCP_SKB_CB(skb)->urg_ptr;
+
+ /* Urgent data is always a pain in the ass. */
+ if(old_urg_ptr > len) {
+ TCP_SKB_CB(skb)->flags &= ~(TCPCB_FLAG_URG);
+ TCP_SKB_CB(skb)->urg_ptr = 0;
+ TCP_SKB_CB(buff)->urg_ptr = old_urg_ptr - len;
+ } else {
+ flags &= ~(TCPCB_FLAG_URG);
+ }
+ }
+ if(!(flags & TCPCB_FLAG_URG))
+ TCP_SKB_CB(buff)->urg_ptr = 0;
+ TCP_SKB_CB(buff)->flags = flags;
+ TCP_SKB_CB(buff)->sacked = 0;
+
+ /* Copy and checksum data tail into the new buffer. */
+ buff->csum = csum_partial_copy(skb->data + len, skb_put(buff, nsize),
+ nsize, 0);
+
+ /* This takes care of the FIN sequence number too. */
+ TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(buff)->seq;
+ skb_trim(skb, len);
+
+ /* Rechecksum original buffer. */
+ skb->csum = csum_partial(skb->data, skb->len, 0);
+
+ /* Looks stupid, but our code really uses when of
+ * skbs, which it never sent before. --ANK
+ */
+ TCP_SKB_CB(buff)->when = TCP_SKB_CB(skb)->when;
+
+ /* Link BUFF into the send queue. */
+ __skb_append(skb, buff);
+
+ return 0;
+}
+
+/* This function synchronize snd mss to current pmtu/exthdr set.
+
+ tp->user_mss is mss set by user by TCP_MAXSEG. It does NOT counts
+ for TCP options, but includes only bare TCP header.
+
+ tp->mss_clamp is mss negotiated at connection setup.
+ It is minimum of user_mss and mss received with SYN.
+ It also does not include TCP options.
+
+ tp->pmtu_cookie is last pmtu, seen by this function.
+
+ tp->mss_cache is current effective sending mss, including
+ all tcp options except for SACKs. It is evaluated,
+ taking into account current pmtu, but never exceeds
+ tp->mss_clamp.
+
+ NOTE1. rfc1122 clearly states that advertised MSS
+ DOES NOT include either tcp or ip options.
+
+ NOTE2. tp->pmtu_cookie and tp->mss_cache are READ ONLY outside
+ this function. --ANK (980731)
+ */
+
+int tcp_sync_mss(struct sock *sk, u32 pmtu)
+{
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+ int mss_now;
+
+ /* Calculate base mss without TCP options:
+ It is MMS_S - sizeof(tcphdr) of rfc1122
+ */
+ mss_now = pmtu - tp->af_specific->net_header_len - sizeof(struct tcphdr);
+
+ /* Clamp it (mss_clamp does not include tcp options) */
+ if (mss_now > tp->mss_clamp)
+ mss_now = tp->mss_clamp;
+
+ /* Now subtract TCP options size, not including SACKs */
+ mss_now -= tp->tcp_header_len - sizeof(struct tcphdr);
+
+ /* Now subtract optional transport overhead */
+ mss_now -= tp->ext_header_len;
+
+ /* It we got too small (or even negative) value,
+ clamp it by 8 from below. Why 8 ?
+ Well, it could be 1 with the same success,
+ but if IP accepted segment of length 1,
+ it would love 8 even more 8) --ANK (980731)
+ */
+ if (mss_now < 8)
+ mss_now = 8;
+
+ /* And store cached results */
+ tp->pmtu_cookie = pmtu;
+ tp->mss_cache = mss_now;
+ return mss_now;
+}
+
+
+/* This routine writes packets to the network. It advances the
+ * send_head. This happens as incoming acks open up the remote
+ * window for us.
+ */
+void tcp_write_xmit(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ unsigned int mss_now;
+
+ /* Account for SACKS, we may need to fragment due to this.
+ * It is just like the real MSS changing on us midstream.
+ * We also handle things correctly when the user adds some
+ * IP options mid-stream. Silly to do, but cover it.
+ */
+ mss_now = tcp_current_mss(sk);
+
+ /* If we are zapped, the bytes will have to remain here.
+ * In time closedown will empty the write queue and all
+ * will be happy.
+ */
+ if(!sk->zapped) {
+ struct sk_buff *skb;
+ int sent_pkts = 0;
+
+ /* Anything on the transmit queue that fits the window can
+ * be added providing we are:
+ *
+ * a) following SWS avoidance [and Nagle algorithm]
+ * b) not exceeding our congestion window.
+ * c) not retransmitting [Nagle]
+ */
+ while((skb = tp->send_head) && tcp_snd_test(sk, skb)) {
+ if (skb->len > mss_now) {
+ if (tcp_fragment(sk, skb, mss_now))
+ break;
+ }
+
+ /* Advance the send_head. This one is going out. */
+ update_send_head(sk);
+ TCP_SKB_CB(skb)->when = tcp_time_stamp;
+ tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
+ tp->packets_out++;
+ tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC));
+ sent_pkts = 1;
+ }
+
+ /* If we sent anything, make sure the retransmit
+ * timer is active.
+ */
+ if (sent_pkts && !tcp_timer_is_set(sk, TIME_RETRANS))
+ tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+ }
+}
+
+/* This function returns the amount that we can raise the
+ * usable window based on the following constraints
+ *
+ * 1. The window can never be shrunk once it is offered (RFC 793)
+ * 2. We limit memory per socket
+ *
+ * RFC 1122:
+ * "the suggested [SWS] avoidance algorithm for the receiver is to keep
+ * RECV.NEXT + RCV.WIN fixed until:
+ * RCV.BUFF - RCV.USER - RCV.WINDOW >= min(1/2 RCV.BUFF, MSS)"
+ *
+ * i.e. don't raise the right edge of the window until you can raise
+ * it at least MSS bytes.
+ *
+ * Unfortunately, the recommended algorithm breaks header prediction,
+ * since header prediction assumes th->window stays fixed.
+ *
+ * Strictly speaking, keeping th->window fixed violates the receiver
+ * side SWS prevention criteria. The problem is that under this rule
+ * a stream of single byte packets will cause the right side of the
+ * window to always advance by a single byte.
+ *
+ * Of course, if the sender implements sender side SWS prevention
+ * then this will not be a problem.
+ *
+ * BSD seems to make the following compromise:
+ *
+ * If the free space is less than the 1/4 of the maximum
+ * space available and the free space is less than 1/2 mss,
+ * then set the window to 0.
+ * Otherwise, just prevent the window from shrinking
+ * and from being larger than the largest representable value.
+ *
+ * This prevents incremental opening of the window in the regime
+ * where TCP is limited by the speed of the reader side taking
+ * data out of the TCP receive queue. It does nothing about
+ * those cases where the window is constrained on the sender side
+ * because the pipeline is full.
+ *
+ * BSD also seems to "accidentally" limit itself to windows that are a
+ * multiple of MSS, at least until the free space gets quite small.
+ * This would appear to be a side effect of the mbuf implementation.
+ * Combining these two algorithms results in the observed behavior
+ * of having a fixed window size at almost all times.
+ *
+ * Below we obtain similar behavior by forcing the offered window to
+ * a multiple of the mss when it is feasible to do so.
+ *
+ * Note, we don't "adjust" for TIMESTAMP or SACK option bytes.
+ */
+u32 __tcp_select_window(struct sock *sk)
+{
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+ unsigned int mss = tp->mss_cache;
+ int free_space;
+ u32 window;
+
+ /* Sometimes free_space can be < 0. */
+ free_space = (sk->rcvbuf - atomic_read(&sk->rmem_alloc)) / 2;
+ if (tp->window_clamp) {
+ if (free_space > ((int) tp->window_clamp))
+ free_space = tp->window_clamp;
+ mss = min(tp->window_clamp, mss);
+ } else {
+ printk("tcp_select_window: tp->window_clamp == 0.\n");
+ }
+
+ if (mss < 1) {
+ mss = 1;
+ printk("tcp_select_window: sk->mss fell to 0.\n");
+ }
+
+ if ((free_space < (sk->rcvbuf/4)) && (free_space < ((int) (mss/2)))) {
+ window = 0;
+ tp->pred_flags = 0;
+ } else {
+ /* Get the largest window that is a nice multiple of mss.
+ * Window clamp already applied above.
+ * If our current window offering is within 1 mss of the
+ * free space we just keep it. This prevents the divide
+ * and multiply from happening most of the time.
+ * We also don't do any window rounding when the free space
+ * is too small.
+ */
+ window = tp->rcv_wnd;
+ if ((((int) window) <= (free_space - ((int) mss))) ||
+ (((int) window) > free_space))
+ window = (((unsigned int) free_space)/mss)*mss;
+ }
+ return window;
+}
+
+/* Attempt to collapse two adjacent SKB's during retransmission. */
+static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *skb, int mss_now)
+{
+ struct sk_buff *next_skb = skb->next;
+
+ /* The first test we must make is that neither of these two
+ * SKB's are still referenced by someone else.
+ */
+ if(!skb_cloned(skb) && !skb_cloned(next_skb)) {
+ int skb_size = skb->len, next_skb_size = next_skb->len;
+ u16 flags = TCP_SKB_CB(skb)->flags;
+
+ /* Punt if the first SKB has URG set. */
+ if(flags & TCPCB_FLAG_URG)
+ return;
+
+ /* Also punt if next skb has been SACK'd. */
+ if(TCP_SKB_CB(next_skb)->sacked & TCPCB_SACKED_ACKED)
+ return;
+
+ /* Punt if not enough space exists in the first SKB for
+ * the data in the second, or the total combined payload
+ * would exceed the MSS.
+ */
+ if ((next_skb_size > skb_tailroom(skb)) ||
+ ((skb_size + next_skb_size) > mss_now))
+ return;
+
+ /* Ok. We will be able to collapse the packet. */
+ __skb_unlink(next_skb, next_skb->list);
+
+ if(skb->len % 4) {
+ /* Must copy and rechecksum all data. */
+ memcpy(skb_put(skb, next_skb_size), next_skb->data, next_skb_size);
+ skb->csum = csum_partial(skb->data, skb->len, 0);
+ } else {
+ /* Optimize, actually we could also combine next_skb->csum
+ * to skb->csum using a single add w/carry operation too.
+ */
+ skb->csum = csum_partial_copy(next_skb->data,
+ skb_put(skb, next_skb_size),
+ next_skb_size, skb->csum);
+ }
+
+ /* Update sequence range on original skb. */
+ TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(next_skb)->end_seq;
+
+ /* Merge over control information. */
+ flags |= TCP_SKB_CB(next_skb)->flags; /* This moves PSH/FIN etc. over */
+ if(flags & TCPCB_FLAG_URG) {
+ u16 urgptr = TCP_SKB_CB(next_skb)->urg_ptr;
+ TCP_SKB_CB(skb)->urg_ptr = urgptr + skb_size;
+ }
+ TCP_SKB_CB(skb)->flags = flags;
+
+ /* All done, get rid of second SKB and account for it so
+ * packet counting does not break.
+ */
+ kfree_skb(next_skb);
+ sk->tp_pinfo.af_tcp.packets_out--;
+ }
+}
+
+/* Do a simple retransmit without using the backoff mechanisms in
+ * tcp_timer. This is used for path mtu discovery.
+ * The socket is already locked here.
+ */
+void tcp_simple_retransmit(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct sk_buff *skb, *old_next_skb;
+ unsigned int mss = tcp_current_mss(sk);
+
+ /* Don't muck with the congestion window here. */
+ tp->dup_acks = 0;
+ tp->high_seq = tp->snd_nxt;
+ tp->retrans_head = NULL;
+
+ /* Input control flow will see that this was retransmitted
+ * and not use it for RTT calculation in the absence of
+ * the timestamp option.
+ */
+ for (old_next_skb = skb = skb_peek(&sk->write_queue);
+ ((skb != tp->send_head) &&
+ (skb != (struct sk_buff *)&sk->write_queue));
+ skb = skb->next) {
+ int resend_skb = 0;
+
+ /* Our goal is to push out the packets which we
+ * sent already, but are being chopped up now to
+ * account for the PMTU information we have.
+ *
+ * As we resend the queue, packets are fragmented
+ * into two pieces, and when we try to send the
+ * second piece it may be collapsed together with
+ * a subsequent packet, and so on. -DaveM
+ */
+ if (old_next_skb != skb || skb->len > mss)
+ resend_skb = 1;
+ old_next_skb = skb->next;
+ if (resend_skb != 0)
+ tcp_retransmit_skb(sk, skb);
+ }
+}
+
+static __inline__ void update_retrans_head(struct sock *sk)
+{
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+
+ tp->retrans_head = tp->retrans_head->next;
+ if((tp->retrans_head == tp->send_head) ||
+ (tp->retrans_head == (struct sk_buff *) &sk->write_queue)) {
+ tp->retrans_head = NULL;
+ tp->rexmt_done = 1;
+ }
+}
+
+/* This retransmits one SKB. Policy decisions and retransmit queue
+ * state updates are done by the caller. Returns non-zero if an
+ * error occurred which prevented the send.
+ */
+int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ unsigned int cur_mss = tcp_current_mss(sk);
+
+ if(skb->len > cur_mss) {
+ if(tcp_fragment(sk, skb, cur_mss))
+ return 1; /* We'll try again later. */
+
+ /* New SKB created, account for it. */
+ tp->packets_out++;
+ }
+
+ /* Collapse two adjacent packets if worthwhile and we can. */
+ if(!(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_SYN) &&
+ (skb->len < (cur_mss >> 1)) &&
+ (skb->next != tp->send_head) &&
+ (skb->next != (struct sk_buff *)&sk->write_queue) &&
+ (sysctl_tcp_retrans_collapse != 0))
+ tcp_retrans_try_collapse(sk, skb, cur_mss);
+
+ if(tp->af_specific->rebuild_header(sk))
+ return 1; /* Routing failure or similar. */
+
+ /* Some Solaris stacks overoptimize and ignore the FIN on a
+ * retransmit when old data is attached. So strip it off
+ * since it is cheap to do so and saves bytes on the network.
+ */
+ if(skb->len > 0 &&
+ (TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN) &&
+ tp->snd_una == (TCP_SKB_CB(skb)->end_seq - 1)) {
+ TCP_SKB_CB(skb)->seq = TCP_SKB_CB(skb)->end_seq - 1;
+ skb_trim(skb, 0);
+ skb->csum = 0;
+ }
+
+ /* Ok, we're gonna send it out, update state. */
+ TCP_SKB_CB(skb)->sacked |= TCPCB_SACKED_RETRANS;
+ tp->retrans_out++;
+
+ /* Make a copy, if the first transmission SKB clone we made
+ * is still in somebody's hands, else make a clone.
+ */
+ TCP_SKB_CB(skb)->when = tcp_time_stamp;
+ if(skb_cloned(skb))
+ skb = skb_copy(skb, GFP_ATOMIC);
+ else
+ skb = skb_clone(skb, GFP_ATOMIC);
+
+ tcp_transmit_skb(sk, skb);
+
+ /* Update global TCP statistics and return success. */
+ sk->prot->retransmits++;
+ tcp_statistics.TcpRetransSegs++;
+
+ return 0;
+}
+
+/* This gets called after a retransmit timeout, and the initially
+ * retransmitted data is acknowledged. It tries to continue
+ * resending the rest of the retransmit queue, until either
+ * we've sent it all or the congestion window limit is reached.
+ * If doing SACK, the first ACK which comes back for a timeout
+ * based retransmit packet might feed us FACK information again.
+ * If so, we use it to avoid unnecessarily retransmissions.
+ */
+void tcp_xmit_retransmit_queue(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct sk_buff *skb;
+
+ if (tp->retrans_head == NULL &&
+ tp->rexmt_done == 0)
+ tp->retrans_head = skb_peek(&sk->write_queue);
+ if (tp->retrans_head == tp->send_head)
+ tp->retrans_head = NULL;
+
+ /* Each time, advance the retrans_head if we got
+ * a packet out or we skipped one because it was
+ * SACK'd. -DaveM
+ */
+ while ((skb = tp->retrans_head) != NULL) {
+ /* If it has been ack'd by a SACK block, we don't
+ * retransmit it.
+ */
+ if(!(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)) {
+ /* Send it out, punt if error occurred. */
+ if(tcp_retransmit_skb(sk, skb))
+ break;
+
+ update_retrans_head(sk);
+
+ /* Stop retransmitting if we've hit the congestion
+ * window limit.
+ */
+ if (tp->retrans_out >= tp->snd_cwnd)
+ break;
+ } else {
+ update_retrans_head(sk);
+ }
+ }
+}
+
+/* Using FACK information, retransmit all missing frames at the receiver
+ * up to the forward most SACK'd packet (tp->fackets_out) if the packet
+ * has not been retransmitted already.
+ */
+void tcp_fack_retransmit(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct sk_buff *skb = skb_peek(&sk->write_queue);
+ int packet_cnt = 0;
+
+ while((skb != NULL) &&
+ (skb != tp->send_head) &&
+ (skb != (struct sk_buff *)&sk->write_queue)) {
+ __u8 sacked = TCP_SKB_CB(skb)->sacked;
+
+ if(sacked & (TCPCB_SACKED_ACKED | TCPCB_SACKED_RETRANS))
+ goto next_packet;
+
+ /* Ok, retransmit it. */
+ if(tcp_retransmit_skb(sk, skb))
+ break;
+
+ if(tcp_packets_in_flight(tp) >= tp->snd_cwnd)
+ break;
+next_packet:
+ packet_cnt++;
+ if(packet_cnt >= tp->fackets_out)
+ break;
+ skb = skb->next;
+ }
+}
+
+/* Send a fin. The caller locks the socket for us. This cannot be
+ * allowed to fail queueing a FIN frame under any circumstances.
+ */
+void tcp_send_fin(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct sk_buff *skb = skb_peek_tail(&sk->write_queue);
+ unsigned int mss_now;
+
+ /* Optimization, tack on the FIN if we have a queue of
+ * unsent frames. But be careful about outgoing SACKS
+ * and IP options.
+ */
+ mss_now = tcp_current_mss(sk);
+
+ if((tp->send_head != NULL) && (skb->len < mss_now)) {
+ /* tcp_write_xmit() takes care of the rest. */
+ TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_FIN;
+ TCP_SKB_CB(skb)->end_seq++;
+ tp->write_seq++;
+
+ /* Special case to avoid Nagle bogosity. If this
+ * segment is the last segment, and it was queued
+ * due to Nagle/SWS-avoidance, send it out now.
+ */
+ if(tp->send_head == skb &&
+ !sk->nonagle &&
+ skb->len < (tp->mss_cache >> 1) &&
+ tp->packets_out &&
+ !(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_URG)) {
+ update_send_head(sk);
+ TCP_SKB_CB(skb)->when = tcp_time_stamp;
+ tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
+ tp->packets_out++;
+ tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC));
+ if(!tcp_timer_is_set(sk, TIME_RETRANS))
+ tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+ }
+ } else {
+ /* Socket is locked, keep trying until memory is available. */
+ do {
+ skb = sock_wmalloc(sk,
+ (MAX_HEADER +
+ sk->prot->max_header),
+ 1, GFP_KERNEL);
+ } while (skb == NULL);
+
+ /* Reserve space for headers and prepare control bits. */
+ skb_reserve(skb, MAX_HEADER + sk->prot->max_header);
+ skb->csum = 0;
+ TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | TCPCB_FLAG_FIN);
+ TCP_SKB_CB(skb)->sacked = 0;
+ TCP_SKB_CB(skb)->urg_ptr = 0;
+
+ /* FIN eats a sequence byte, write_seq advanced by tcp_send_skb(). */
+ TCP_SKB_CB(skb)->seq = tp->write_seq;
+ TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + 1;
+ tcp_send_skb(sk, skb, 0);
+ }
+}
+
+/* We get here when a process closes a file descriptor (either due to
+ * an explicit close() or as a byproduct of exit()'ing) and there
+ * was unread data in the receive queue. This behavior is recommended
+ * by draft-ietf-tcpimpl-prob-03.txt section 3.10. -DaveM
+ */
+void tcp_send_active_reset(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct sk_buff *skb;
+
+ /* NOTE: No TCP options attached and we never retransmit this. */
+ skb = alloc_skb(MAX_HEADER + sk->prot->max_header, GFP_KERNEL);
+ if (!skb)
+ return;
+
+ /* Reserve space for headers and prepare control bits. */
+ skb_reserve(skb, MAX_HEADER + sk->prot->max_header);
+ skb->csum = 0;
+ TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | TCPCB_FLAG_RST);
+ TCP_SKB_CB(skb)->sacked = 0;
+ TCP_SKB_CB(skb)->urg_ptr = 0;
+
+ /* Send it off. */
+ TCP_SKB_CB(skb)->seq = tp->write_seq;
+ TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq;
+ TCP_SKB_CB(skb)->when = tcp_time_stamp;
+ tcp_transmit_skb(sk, skb);
+}
+
+/* WARNING: This routine must only be called when we have already sent
+ * a SYN packet that crossed the incoming SYN that caused this routine
+ * to get called. If this assumption fails then the initial rcv_wnd
+ * and rcv_wscale values will not be correct.
+ */
+int tcp_send_synack(struct sock *sk)
+{
+ struct tcp_opt* tp = &(sk->tp_pinfo.af_tcp);
+ struct sk_buff* skb;
+
+ skb = sock_wmalloc(sk, (MAX_HEADER + sk->prot->max_header),
+ 1, GFP_ATOMIC);
+ if (skb == NULL)
+ return -ENOMEM;
+
+ /* Reserve space for headers and prepare control bits. */
+ skb_reserve(skb, MAX_HEADER + sk->prot->max_header);
+ skb->csum = 0;
+ TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | TCPCB_FLAG_SYN);
+ TCP_SKB_CB(skb)->sacked = 0;
+ TCP_SKB_CB(skb)->urg_ptr = 0;
+
+ /* SYN eats a sequence byte. */
+ TCP_SKB_CB(skb)->seq = tp->snd_una;
+ TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + 1;
+ __skb_queue_tail(&sk->write_queue, skb);
+ TCP_SKB_CB(skb)->when = tcp_time_stamp;
+ tp->packets_out++;
+ tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC));
+ return 0;
+}
+
+/*
+ * Prepare a SYN-ACK.
+ */
+struct sk_buff * tcp_make_synack(struct sock *sk, struct dst_entry *dst,
+ struct open_request *req, int mss)
+{
+ struct tcphdr *th;
+ int tcp_header_size;
+ struct sk_buff *skb;
+
+ skb = sock_wmalloc(sk, MAX_HEADER + sk->prot->max_header, 1, GFP_ATOMIC);
+ if (skb == NULL)
+ return NULL;
+
+ /* Reserve space for headers. */
+ skb_reserve(skb, MAX_HEADER + sk->prot->max_header);
+
+ skb->dst = dst_clone(dst);
+
+ /* Don't offer more than they did.
+ * This way we don't have to memorize who said what.
+ * FIXME: maybe this should be changed for better performance
+ * with syncookies.
+ */
+ req->mss = min(mss, req->mss);
+ if (req->mss < 8) {
+ printk(KERN_DEBUG "initial req->mss below 8\n");
+ req->mss = 8;
+ }
+
+ tcp_header_size = (sizeof(struct tcphdr) + TCPOLEN_MSS +
+ (req->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0) +
+ (req->wscale_ok ? TCPOLEN_WSCALE_ALIGNED : 0) +
+ /* SACK_PERM is in the place of NOP NOP of TS */
+ ((req->sack_ok && !req->tstamp_ok) ? TCPOLEN_SACKPERM_ALIGNED : 0));
+ skb->h.th = th = (struct tcphdr *) skb_push(skb, tcp_header_size);
+
+ memset(th, 0, sizeof(struct tcphdr));
+ th->syn = 1;
+ th->ack = 1;
+ th->source = sk->sport;
+ th->dest = req->rmt_port;
+ TCP_SKB_CB(skb)->seq = req->snt_isn;
+ TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + 1;
+ th->seq = htonl(TCP_SKB_CB(skb)->seq);
+ th->ack_seq = htonl(req->rcv_isn + 1);
+ if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */
+ __u8 rcv_wscale;
+ /* Set this up on the first call only */
+ req->window_clamp = skb->dst->window;
+ tcp_select_initial_window(sock_rspace(sk)/2,req->mss,
+ &req->rcv_wnd,
+ &req->window_clamp,
+ req->wscale_ok,
+ &rcv_wscale);
+ req->rcv_wscale = rcv_wscale;
+ }
+
+ /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */
+ th->window = htons(req->rcv_wnd);
+
+ TCP_SKB_CB(skb)->when = tcp_time_stamp;
+ tcp_syn_build_options((__u32 *)(th + 1), req->mss, req->tstamp_ok,
+ req->sack_ok, req->wscale_ok, req->rcv_wscale,
+ TCP_SKB_CB(skb)->when,
+ req->ts_recent);
+
+ skb->csum = 0;
+ th->doff = (tcp_header_size >> 2);
+ tcp_statistics.TcpOutSegs++;
+ return skb;
+}
+
+void tcp_connect(struct sock *sk, struct sk_buff *buff, int mtu)
+{
+ struct dst_entry *dst = sk->dst_cache;
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ /* Reserve space for headers. */
+ skb_reserve(buff, MAX_HEADER + sk->prot->max_header);
+
+ tp->snd_wnd = 0;
+ tp->snd_wl1 = 0;
+ tp->snd_wl2 = tp->write_seq;
+ tp->snd_una = tp->write_seq;
+ tp->rcv_nxt = 0;
+
+ sk->err = 0;
+
+ /* We'll fix this up when we get a response from the other end.
+ * See tcp_input.c:tcp_rcv_state_process case TCP_SYN_SENT.
+ */
+ tp->tcp_header_len = sizeof(struct tcphdr) +
+ (sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0);
+
+ /* If user gave his TCP_MAXSEG, record it to clamp */
+ if (tp->user_mss)
+ tp->mss_clamp = tp->user_mss;
+ tcp_sync_mss(sk, mtu);
+
+ /* Now unpleasant action: if initial pmtu is too low
+ set lower clamp. I am not sure that it is good.
+ To be more exact, I do not think that clamping at value, which
+ is apparently transient and may improve in future is good idea.
+ It would be better to wait until peer will returns its MSS
+ (probably 65535 too) and now advertise something sort of 65535
+ or at least first hop device mtu. Is it clear, what I mean?
+ We should tell peer what maximal mss we expect to RECEIVE,
+ it has nothing to do with pmtu.
+ I am afraid someone will be confused by such huge value.
+ --ANK (980731)
+ */
+ if (tp->mss_cache + tp->tcp_header_len - sizeof(struct tcphdr) < tp->mss_clamp )
+ tp->mss_clamp = tp->mss_cache + tp->tcp_header_len - sizeof(struct tcphdr);
+
+ TCP_SKB_CB(buff)->flags = TCPCB_FLAG_SYN;
+ TCP_SKB_CB(buff)->sacked = 0;
+ TCP_SKB_CB(buff)->urg_ptr = 0;
+ buff->csum = 0;
+ TCP_SKB_CB(buff)->seq = tp->write_seq++;
+ TCP_SKB_CB(buff)->end_seq = tp->write_seq;
+ tp->snd_nxt = TCP_SKB_CB(buff)->end_seq;
+
+ tp->window_clamp = dst->window;
+ tcp_select_initial_window(sock_rspace(sk)/2,tp->mss_clamp,
+ &tp->rcv_wnd,
+ &tp->window_clamp,
+ sysctl_tcp_window_scaling,
+ &tp->rcv_wscale);
+ /* Ok, now lock the socket before we make it visible to
+ * the incoming packet engine.
+ */
+ lock_sock(sk);
+
+ /* Socket identity change complete, no longer
+ * in TCP_CLOSE, so enter ourselves into the
+ * hash tables.
+ */
+ tcp_set_state(sk,TCP_SYN_SENT);
+ sk->prot->hash(sk);
+
+ tp->rto = dst->rtt;
+ tcp_init_xmit_timers(sk);
+ tp->retransmits = 0;
+ tp->fackets_out = 0;
+ tp->retrans_out = 0;
+
+ /* Send it off. */
+ __skb_queue_tail(&sk->write_queue, buff);
+ TCP_SKB_CB(buff)->when = tcp_time_stamp;
+ tp->packets_out++;
+ tcp_transmit_skb(sk, skb_clone(buff, GFP_KERNEL));
+ tcp_statistics.TcpActiveOpens++;
+
+ /* Timer for repeating the SYN until an answer. */
+ tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+
+ /* Now, it is safe to release the socket. */
+ release_sock(sk);
+}
+
+/* Send out a delayed ack, the caller does the policy checking
+ * to see if we should even be here. See tcp_input.c:tcp_ack_snd_check()
+ * for details.
+ */
+void tcp_send_delayed_ack(struct tcp_opt *tp, int max_timeout)
+{
+ unsigned long timeout;
+
+ /* Stay within the limit we were given */
+ timeout = tp->ato;
+ if (timeout > max_timeout)
+ timeout = max_timeout;
+ timeout += jiffies;
+
+ /* Use new timeout only if there wasn't a older one earlier. */
+ if (!tp->delack_timer.prev) {
+ tp->delack_timer.expires = timeout;
+ add_timer(&tp->delack_timer);
+ } else {
+ if (time_before(timeout, tp->delack_timer.expires))
+ mod_timer(&tp->delack_timer, timeout);
+ }
+}
+
+/* This routine sends an ack and also updates the window. */
+void tcp_send_ack(struct sock *sk)
+{
+ /* If we have been reset, we may not send again. */
+ if(!sk->zapped) {
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct sk_buff *buff;
+
+ /* We are not putting this on the write queue, so
+ * tcp_transmit_skb() will set the ownership to this
+ * sock.
+ */
+ buff = alloc_skb(MAX_HEADER + sk->prot->max_header, GFP_ATOMIC);
+ if (buff == NULL) {
+ /* Force it to send an ack. We don't have to do this
+ * (ACK is unreliable) but it's much better use of
+ * bandwidth on slow links to send a spare ack than
+ * resend packets.
+ *
+ * This is the one possible way that we can delay an
+ * ACK and have tp->ato indicate that we are in
+ * quick ack mode, so clear it.
+ */
+ if(tcp_in_quickack_mode(tp))
+ tcp_exit_quickack_mode(tp);
+ tcp_send_delayed_ack(tp, HZ/2);
+ return;
+ }
+
+ /* Reserve space for headers and prepare control bits. */
+ skb_reserve(buff, MAX_HEADER + sk->prot->max_header);
+ buff->csum = 0;
+ TCP_SKB_CB(buff)->flags = TCPCB_FLAG_ACK;
+ TCP_SKB_CB(buff)->sacked = 0;
+ TCP_SKB_CB(buff)->urg_ptr = 0;
+
+ /* Send it off, this clears delayed acks for us. */
+ TCP_SKB_CB(buff)->seq = TCP_SKB_CB(buff)->end_seq = tp->snd_nxt;
+ TCP_SKB_CB(buff)->when = tcp_time_stamp;
+ tcp_transmit_skb(sk, buff);
+ }
+}
+
+/* This routine sends a packet with an out of date sequence
+ * number. It assumes the other end will try to ack it.
+ */
+void tcp_write_wakeup(struct sock *sk)
+{
+ /* After a valid reset we can send no more. */
+ if (!sk->zapped) {
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct sk_buff *skb;
+
+ /* Write data can still be transmitted/retransmitted in the
+ * following states. If any other state is encountered, return.
+ * [listen/close will never occur here anyway]
+ */
+ if ((1 << sk->state) &
+ ~(TCPF_ESTABLISHED|TCPF_CLOSE_WAIT|TCPF_FIN_WAIT1|
+ TCPF_LAST_ACK|TCPF_CLOSING))
+ return;
+
+ if (before(tp->snd_nxt, tp->snd_una + tp->snd_wnd) &&
+ ((skb = tp->send_head) != NULL)) {
+ unsigned long win_size;
+
+ /* We are probing the opening of a window
+ * but the window size is != 0
+ * must have been a result SWS avoidance ( sender )
+ */
+ win_size = tp->snd_wnd - (tp->snd_nxt - tp->snd_una);
+ if (win_size < TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq) {
+ if (tcp_fragment(sk, skb, win_size))
+ return; /* Let a retransmit get it. */
+ }
+ update_send_head(sk);
+ TCP_SKB_CB(skb)->when = tcp_time_stamp;
+ tp->snd_nxt = TCP_SKB_CB(skb)->end_seq;
+ tp->packets_out++;
+ tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC));
+ if (!tcp_timer_is_set(sk, TIME_RETRANS))
+ tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+ } else {
+ /* We don't queue it, tcp_transmit_skb() sets ownership. */
+ skb = alloc_skb(MAX_HEADER + sk->prot->max_header,
+ GFP_ATOMIC);
+ if (skb == NULL)
+ return;
+
+ /* Reserve space for headers and set control bits. */
+ skb_reserve(skb, MAX_HEADER + sk->prot->max_header);
+ skb->csum = 0;
+ TCP_SKB_CB(skb)->flags = TCPCB_FLAG_ACK;
+ TCP_SKB_CB(skb)->sacked = 0;
+ TCP_SKB_CB(skb)->urg_ptr = 0;
+
+ /* Use a previous sequence. This should cause the other
+ * end to send an ack. Don't queue or clone SKB, just
+ * send it.
+ */
+ TCP_SKB_CB(skb)->seq = tp->snd_nxt - 1;
+ TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq;
+ TCP_SKB_CB(skb)->when = tcp_time_stamp;
+ tcp_transmit_skb(sk, skb);
+ }
+ }
+}
+
+/* A window probe timeout has occurred. If window is not closed send
+ * a partial packet else a zero probe.
+ */
+void tcp_send_probe0(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ tcp_write_wakeup(sk);
+ tp->pending = TIME_PROBE0;
+ tp->backoff++;
+ tp->probes_out++;
+ tcp_reset_xmit_timer (sk, TIME_PROBE0,
+ min(tp->rto << tp->backoff, 120*HZ));
+}
diff --git a/pfinet/linux-src/net/ipv4/tcp_timer.c b/pfinet/linux-src/net/ipv4/tcp_timer.c
new file mode 100644
index 00000000..7529a441
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/tcp_timer.c
@@ -0,0 +1,595 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Implementation of the Transmission Control Protocol(TCP).
+ *
+ * Version: $Id: tcp_timer.c,v 1.62.2.4 1999/09/23 19:21:39 davem Exp $
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ * Corey Minyard <wf-rch!minyard@relay.EU.net>
+ * Florian La Roche, <flla@stud.uni-sb.de>
+ * Charles Hedrick, <hedrick@klinzhai.rutgers.edu>
+ * Linus Torvalds, <torvalds@cs.helsinki.fi>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ * Matthew Dillon, <dillon@apollo.west.oic.com>
+ * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ * Jorge Cwik, <jorge@laser.satlink.net>
+ */
+
+#include <net/tcp.h>
+
+int sysctl_tcp_syn_retries = TCP_SYN_RETRIES;
+int sysctl_tcp_keepalive_time = TCP_KEEPALIVE_TIME;
+int sysctl_tcp_keepalive_probes = TCP_KEEPALIVE_PROBES;
+int sysctl_tcp_retries1 = TCP_RETR1;
+int sysctl_tcp_retries2 = TCP_RETR2;
+
+static void tcp_sltimer_handler(unsigned long);
+static void tcp_syn_recv_timer(unsigned long);
+static void tcp_keepalive(unsigned long data);
+static void tcp_twkill(unsigned long);
+
+struct timer_list tcp_slow_timer = {
+ NULL, NULL,
+ 0, 0,
+ tcp_sltimer_handler,
+};
+
+
+struct tcp_sl_timer tcp_slt_array[TCP_SLT_MAX] = {
+ {ATOMIC_INIT(0), TCP_SYNACK_PERIOD, 0, tcp_syn_recv_timer},/* SYNACK */
+ {ATOMIC_INIT(0), TCP_KEEPALIVE_PERIOD, 0, tcp_keepalive}, /* KEEPALIVE */
+ {ATOMIC_INIT(0), TCP_TWKILL_PERIOD, 0, tcp_twkill} /* TWKILL */
+};
+
+const char timer_bug_msg[] = KERN_DEBUG "tcpbug: unknown timer value\n";
+
+/*
+ * Using different timers for retransmit, delayed acks and probes
+ * We may wish use just one timer maintaining a list of expire jiffies
+ * to optimize.
+ */
+
+void tcp_init_xmit_timers(struct sock *sk)
+{
+ init_timer(&sk->tp_pinfo.af_tcp.retransmit_timer);
+ sk->tp_pinfo.af_tcp.retransmit_timer.function=&tcp_retransmit_timer;
+ sk->tp_pinfo.af_tcp.retransmit_timer.data = (unsigned long) sk;
+
+ init_timer(&sk->tp_pinfo.af_tcp.delack_timer);
+ sk->tp_pinfo.af_tcp.delack_timer.function=&tcp_delack_timer;
+ sk->tp_pinfo.af_tcp.delack_timer.data = (unsigned long) sk;
+
+ init_timer(&sk->tp_pinfo.af_tcp.probe_timer);
+ sk->tp_pinfo.af_tcp.probe_timer.function=&tcp_probe_timer;
+ sk->tp_pinfo.af_tcp.probe_timer.data = (unsigned long) sk;
+}
+
+/*
+ * Reset the retransmission timer
+ */
+
+void tcp_reset_xmit_timer(struct sock *sk, int what, unsigned long when)
+{
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+
+ switch (what) {
+ case TIME_RETRANS:
+ /* When seting the transmit timer the probe timer
+ * should not be set.
+ * The delayed ack timer can be set if we are changing the
+ * retransmit timer when removing acked frames.
+ */
+ if(tp->probe_timer.prev)
+ del_timer(&tp->probe_timer);
+ mod_timer(&tp->retransmit_timer, jiffies+when);
+ break;
+
+ case TIME_DACK:
+ mod_timer(&tp->delack_timer, jiffies+when);
+ break;
+
+ case TIME_PROBE0:
+ mod_timer(&tp->probe_timer, jiffies+when);
+ break;
+
+ case TIME_WRITE:
+ printk(KERN_DEBUG "bug: tcp_reset_xmit_timer TIME_WRITE\n");
+ break;
+
+ default:
+ printk(KERN_DEBUG "bug: unknown timer value\n");
+ };
+}
+
+void tcp_clear_xmit_timers(struct sock *sk)
+{
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+
+ if(tp->retransmit_timer.prev)
+ del_timer(&tp->retransmit_timer);
+ if(tp->delack_timer.prev)
+ del_timer(&tp->delack_timer);
+ if(tp->probe_timer.prev)
+ del_timer(&tp->probe_timer);
+}
+
+static int tcp_write_err(struct sock *sk, int force)
+{
+ sk->err = sk->err_soft ? sk->err_soft : ETIMEDOUT;
+ sk->error_report(sk);
+
+ tcp_clear_xmit_timers(sk);
+
+ /* Time wait the socket. */
+ if (!force && ((1<<sk->state) & (TCPF_FIN_WAIT1|TCPF_FIN_WAIT2|TCPF_CLOSING))) {
+ tcp_time_wait(sk);
+ } else {
+ /* Clean up time. */
+ tcp_set_state(sk, TCP_CLOSE);
+ return 0;
+ }
+ return 1;
+}
+
+/* A write timeout has occurred. Process the after effects. */
+static int tcp_write_timeout(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ /* Look for a 'soft' timeout. */
+ if ((sk->state == TCP_ESTABLISHED &&
+ tp->retransmits && (tp->retransmits % TCP_QUICK_TRIES) == 0) ||
+ (sk->state != TCP_ESTABLISHED && tp->retransmits > sysctl_tcp_retries1)) {
+ dst_negative_advice(&sk->dst_cache);
+ }
+
+ /* Have we tried to SYN too many times (repent repent 8)) */
+ if(tp->retransmits > sysctl_tcp_syn_retries && sk->state==TCP_SYN_SENT) {
+ tcp_write_err(sk, 1);
+ /* Don't FIN, we got nothing back */
+ return 0;
+ }
+
+ /* Has it gone just too far? */
+ if (tp->retransmits > sysctl_tcp_retries2)
+ return tcp_write_err(sk, 0);
+
+ return 1;
+}
+
+void tcp_delack_timer(unsigned long data)
+{
+ struct sock *sk = (struct sock*)data;
+
+ if(!sk->zapped &&
+ sk->tp_pinfo.af_tcp.delayed_acks &&
+ sk->state != TCP_CLOSE) {
+ /* If socket is currently locked, defer the ACK. */
+ if (!atomic_read(&sk->sock_readers))
+ tcp_send_ack(sk);
+ else
+ tcp_send_delayed_ack(&(sk->tp_pinfo.af_tcp), HZ/10);
+ }
+}
+
+void tcp_probe_timer(unsigned long data)
+{
+ struct sock *sk = (struct sock*)data;
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+
+ if(sk->zapped)
+ return;
+
+ if (atomic_read(&sk->sock_readers)) {
+ /* Try again later. */
+ tcp_reset_xmit_timer(sk, TIME_PROBE0, HZ/5);
+ return;
+ }
+
+ /* *WARNING* RFC 1122 forbids this
+ * It doesn't AFAIK, because we kill the retransmit timer -AK
+ * FIXME: We ought not to do it, Solaris 2.5 actually has fixing
+ * this behaviour in Solaris down as a bug fix. [AC]
+ */
+ if (tp->probes_out > sysctl_tcp_retries2) {
+ if(sk->err_soft)
+ sk->err = sk->err_soft;
+ else
+ sk->err = ETIMEDOUT;
+ sk->error_report(sk);
+
+ if ((1<<sk->state) & (TCPF_FIN_WAIT1|TCPF_FIN_WAIT2|TCPF_CLOSING)) {
+ /* Time wait the socket. */
+ tcp_time_wait(sk);
+ } else {
+ /* Clean up time. */
+ tcp_set_state(sk, TCP_CLOSE);
+ }
+ } else {
+ /* Only send another probe if we didn't close things up. */
+ tcp_send_probe0(sk);
+ }
+}
+
+static __inline__ int tcp_keepopen_proc(struct sock *sk)
+{
+ int res = 0;
+
+ if ((1<<sk->state) & (TCPF_ESTABLISHED|TCPF_CLOSE_WAIT|TCPF_FIN_WAIT2)) {
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+ __u32 elapsed = tcp_time_stamp - tp->rcv_tstamp;
+
+ if (elapsed >= sysctl_tcp_keepalive_time) {
+ if (tp->probes_out > sysctl_tcp_keepalive_probes) {
+ if(sk->err_soft)
+ sk->err = sk->err_soft;
+ else
+ sk->err = ETIMEDOUT;
+
+ tcp_set_state(sk, TCP_CLOSE);
+ sk->shutdown = SHUTDOWN_MASK;
+ if (!sk->dead)
+ sk->state_change(sk);
+ } else {
+ tp->probes_out++;
+ tp->pending = TIME_KEEPOPEN;
+ tcp_write_wakeup(sk);
+ res = 1;
+ }
+ }
+ }
+ return res;
+}
+
+/* Kill off TIME_WAIT sockets once their lifetime has expired. */
+int tcp_tw_death_row_slot = 0;
+static struct tcp_tw_bucket *tcp_tw_death_row[TCP_TWKILL_SLOTS] =
+ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+
+extern void tcp_timewait_kill(struct tcp_tw_bucket *tw);
+
+static void tcp_twkill(unsigned long data)
+{
+ struct tcp_tw_bucket *tw;
+ int killed = 0;
+
+ tw = tcp_tw_death_row[tcp_tw_death_row_slot];
+ tcp_tw_death_row[tcp_tw_death_row_slot] = NULL;
+ while(tw != NULL) {
+ struct tcp_tw_bucket *next = tw->next_death;
+
+ tcp_timewait_kill(tw);
+ killed++;
+ tw = next;
+ }
+ if(killed != 0) {
+ struct tcp_sl_timer *slt = (struct tcp_sl_timer *)data;
+ atomic_sub(killed, &slt->count);
+ }
+ tcp_tw_death_row_slot =
+ ((tcp_tw_death_row_slot + 1) & (TCP_TWKILL_SLOTS - 1));
+}
+
+/* These are always called from BH context. See callers in
+ * tcp_input.c to verify this.
+ */
+void tcp_tw_schedule(struct tcp_tw_bucket *tw)
+{
+ int slot = (tcp_tw_death_row_slot - 1) & (TCP_TWKILL_SLOTS - 1);
+ struct tcp_tw_bucket **tpp = &tcp_tw_death_row[slot];
+
+ if((tw->next_death = *tpp) != NULL)
+ (*tpp)->pprev_death = &tw->next_death;
+ *tpp = tw;
+ tw->pprev_death = tpp;
+
+ tw->death_slot = slot;
+
+ tcp_inc_slow_timer(TCP_SLT_TWKILL);
+}
+
+/* Happens rarely if at all, no care about scalability here. */
+void tcp_tw_reschedule(struct tcp_tw_bucket *tw)
+{
+ struct tcp_tw_bucket **tpp;
+ int slot;
+
+ if(tw->next_death)
+ tw->next_death->pprev_death = tw->pprev_death;
+ *tw->pprev_death = tw->next_death;
+ tw->pprev_death = NULL;
+
+ slot = (tcp_tw_death_row_slot - 1) & (TCP_TWKILL_SLOTS - 1);
+ tpp = &tcp_tw_death_row[slot];
+ if((tw->next_death = *tpp) != NULL)
+ (*tpp)->pprev_death = &tw->next_death;
+ *tpp = tw;
+ tw->pprev_death = tpp;
+
+ tw->death_slot = slot;
+ /* Timer was incremented when we first entered the table. */
+}
+
+/* This is for handling early-kills of TIME_WAIT sockets. */
+void tcp_tw_deschedule(struct tcp_tw_bucket *tw)
+{
+ if(tw->next_death)
+ tw->next_death->pprev_death = tw->pprev_death;
+ *tw->pprev_death = tw->next_death;
+ tw->pprev_death = NULL;
+ tcp_dec_slow_timer(TCP_SLT_TWKILL);
+}
+
+/*
+ * Check all sockets for keepalive timer
+ * Called every 75 seconds
+ * This timer is started by af_inet init routine and is constantly
+ * running.
+ *
+ * It might be better to maintain a count of sockets that need it using
+ * setsockopt/tcp_destroy_sk and only set the timer when needed.
+ */
+
+/*
+ * don't send over 5 keepopens at a time to avoid burstiness
+ * on big servers [AC]
+ */
+#define MAX_KA_PROBES 5
+
+int sysctl_tcp_max_ka_probes = MAX_KA_PROBES;
+
+/* Keepopen's are only valid for "established" TCP's, nicely our listener
+ * hash gets rid of most of the useless testing, so we run through a couple
+ * of the established hash chains each clock tick. -DaveM
+ *
+ * And now, even more magic... TIME_WAIT TCP's cannot have keepalive probes
+ * going off for them, so we only need check the first half of the established
+ * hash table, even less testing under heavy load.
+ *
+ * I _really_ would rather do this by adding a new timer_struct to struct sock,
+ * and this way only those who set the keepalive option will get the overhead.
+ * The idea is you set it for 2 hours when the sock is first connected, when it
+ * does fire off (if at all, most sockets die earlier) you check for the keepalive
+ * option and also if the sock has been idle long enough to start probing.
+ */
+static void tcp_keepalive(unsigned long data)
+{
+ static int chain_start = 0;
+ int count = 0;
+ int i;
+
+ for(i = chain_start; i < (chain_start + ((tcp_ehash_size/2) >> 2)); i++) {
+ struct sock *sk = tcp_ehash[i];
+ while(sk) {
+ if(!atomic_read(&sk->sock_readers) && sk->keepopen) {
+ count += tcp_keepopen_proc(sk);
+ if(count == sysctl_tcp_max_ka_probes)
+ goto out;
+ }
+ sk = sk->next;
+ }
+ }
+out:
+ chain_start = ((chain_start + ((tcp_ehash_size/2)>>2)) &
+ ((tcp_ehash_size/2) - 1));
+}
+
+/*
+ * The TCP retransmit timer. This lacks a few small details.
+ *
+ * 1. An initial rtt timeout on the probe0 should cause what we can
+ * of the first write queue buffer to be split and sent.
+ * 2. On a 'major timeout' as defined by RFC1122 we shouldn't report
+ * ETIMEDOUT if we know an additional 'soft' error caused this.
+ * tcp_err should save a 'soft error' for us.
+ * [Unless someone has broken it then it does, except for one 2.0
+ * broken case of a send when the route/device is directly unreachable,
+ * and we error but should retry! - FIXME] [AC]
+ */
+
+void tcp_retransmit_timer(unsigned long data)
+{
+ struct sock *sk = (struct sock*)data;
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+
+ /* We are reset. We will send no more retransmits. */
+ if(sk->zapped) {
+ tcp_clear_xmit_timer(sk, TIME_RETRANS);
+ return;
+ }
+
+ if (atomic_read(&sk->sock_readers)) {
+ /* Try again later */
+ tcp_reset_xmit_timer(sk, TIME_RETRANS, HZ/20);
+ return;
+ }
+
+ /* Clear delay ack timer. */
+ tcp_clear_xmit_timer(sk, TIME_DACK);
+
+ /* RFC 2018, clear all 'sacked' flags in retransmission queue,
+ * the sender may have dropped out of order frames and we must
+ * send them out should this timer fire on us.
+ */
+ if(tp->sack_ok) {
+ struct sk_buff *skb = skb_peek(&sk->write_queue);
+
+ while((skb != NULL) &&
+ (skb != tp->send_head) &&
+ (skb != (struct sk_buff *)&sk->write_queue)) {
+ TCP_SKB_CB(skb)->sacked &=
+ ~(TCPCB_SACKED_ACKED | TCPCB_SACKED_RETRANS);
+ skb = skb->next;
+ }
+ }
+
+ /* Retransmission. */
+ tp->retrans_head = NULL;
+ tp->rexmt_done = 0;
+ tp->fackets_out = 0;
+ tp->retrans_out = 0;
+ if (tp->retransmits == 0) {
+ /* Remember window where we lost:
+ * "one half of the current window but at least 2 segments"
+ *
+ * Here "current window" means the effective one, which
+ * means it must be an accurate representation of our current
+ * sending rate _and_ the snd_wnd.
+ */
+ tp->snd_ssthresh = tcp_recalc_ssthresh(tp);
+ tp->snd_cwnd_cnt = 0;
+ tp->snd_cwnd = 1;
+ }
+
+ tp->retransmits++;
+
+ tp->dup_acks = 0;
+ tp->high_seq = tp->snd_nxt;
+ tcp_retransmit_skb(sk, skb_peek(&sk->write_queue));
+
+ /* Increase the timeout each time we retransmit. Note that
+ * we do not increase the rtt estimate. rto is initialized
+ * from rtt, but increases here. Jacobson (SIGCOMM 88) suggests
+ * that doubling rto each time is the least we can get away with.
+ * In KA9Q, Karn uses this for the first few times, and then
+ * goes to quadratic. netBSD doubles, but only goes up to *64,
+ * and clamps at 1 to 64 sec afterwards. Note that 120 sec is
+ * defined in the protocol as the maximum possible RTT. I guess
+ * we'll have to use something other than TCP to talk to the
+ * University of Mars.
+ *
+ * PAWS allows us longer timeouts and large windows, so once
+ * implemented ftp to mars will work nicely. We will have to fix
+ * the 120 second clamps though!
+ */
+ tp->backoff++;
+ tp->rto = min(tp->rto << 1, 120*HZ);
+ tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+
+ tcp_write_timeout(sk);
+}
+
+/*
+ * Slow timer for SYN-RECV sockets
+ */
+
+/* This now scales very nicely. -DaveM */
+static void tcp_syn_recv_timer(unsigned long data)
+{
+ struct sock *sk;
+ unsigned long now = jiffies;
+ int i;
+
+ for(i = 0; i < TCP_LHTABLE_SIZE; i++) {
+ sk = tcp_listening_hash[i];
+
+ while(sk) {
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+
+ /* TCP_LISTEN is implied. */
+ if (!atomic_read(&sk->sock_readers) && tp->syn_wait_queue) {
+ struct open_request *prev = (struct open_request *)(&tp->syn_wait_queue);
+ struct open_request *req = tp->syn_wait_queue;
+ do {
+ struct open_request *conn;
+
+ conn = req;
+ req = req->dl_next;
+
+ if (conn->sk ||
+ ((long)(now - conn->expires)) <= 0) {
+ prev = conn;
+ continue;
+ }
+
+ tcp_synq_unlink(tp, conn, prev);
+ if (conn->retrans >= sysctl_tcp_retries1) {
+#ifdef TCP_DEBUG
+ printk(KERN_DEBUG "syn_recv: "
+ "too many retransmits\n");
+#endif
+ (*conn->class->destructor)(conn);
+ tcp_dec_slow_timer(TCP_SLT_SYNACK);
+ tp->syn_backlog--;
+ tcp_openreq_free(conn);
+
+ if (!tp->syn_wait_queue)
+ break;
+ } else {
+ unsigned long timeo;
+ struct open_request *op;
+
+ (*conn->class->rtx_syn_ack)(sk, conn);
+
+ conn->retrans++;
+#ifdef TCP_DEBUG
+ printk(KERN_DEBUG "syn_ack rtx %d\n",
+ conn->retrans);
+#endif
+ timeo = min((TCP_TIMEOUT_INIT
+ << conn->retrans),
+ 120*HZ);
+ conn->expires = now + timeo;
+ op = prev->dl_next;
+ tcp_synq_queue(tp, conn);
+ if (op != prev->dl_next)
+ prev = prev->dl_next;
+ }
+ /* old prev still valid here */
+ } while (req);
+ }
+ sk = sk->next;
+ }
+ }
+}
+
+void tcp_sltimer_handler(unsigned long data)
+{
+ struct tcp_sl_timer *slt = tcp_slt_array;
+ unsigned long next = ~0UL;
+ unsigned long now = jiffies;
+ int i;
+
+ for (i=0; i < TCP_SLT_MAX; i++, slt++) {
+ if (atomic_read(&slt->count)) {
+ long trigger;
+
+ trigger = slt->period - ((long)(now - slt->last));
+
+ if (trigger <= 0) {
+ (*slt->handler)((unsigned long) slt);
+ slt->last = now;
+ trigger = slt->period;
+ }
+
+ /* Only reschedule if some events remain. */
+ if (atomic_read(&slt->count))
+ next = min(next, trigger);
+ }
+ }
+ if (next != ~0UL)
+ mod_timer(&tcp_slow_timer, (now + next));
+}
+
+void __tcp_inc_slow_timer(struct tcp_sl_timer *slt)
+{
+ unsigned long now = jiffies;
+ unsigned long when;
+
+ slt->last = now;
+
+ when = now + slt->period;
+
+ if (tcp_slow_timer.prev) {
+ if ((long)(tcp_slow_timer.expires - when) >= 0)
+ mod_timer(&tcp_slow_timer, when);
+ } else {
+ tcp_slow_timer.expires = when;
+ add_timer(&tcp_slow_timer);
+ }
+}
diff --git a/pfinet/linux-src/net/ipv4/timer.c b/pfinet/linux-src/net/ipv4/timer.c
new file mode 100644
index 00000000..3687ec35
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/timer.c
@@ -0,0 +1,126 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * TIMER - implementation of software timers for IP.
+ *
+ * Version: $Id: timer.c,v 1.15 1999/02/22 13:54:29 davem Exp $
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Corey Minyard <wf-rch!minyard@relay.EU.net>
+ * Fred Baumgarten, <dc6iq@insu1.etec.uni-karlsruhe.de>
+ * Florian La Roche, <flla@stud.uni-sb.de>
+ *
+ * Fixes:
+ * Alan Cox : To avoid destroying a wait queue as we use it
+ * we defer destruction until the destroy timer goes
+ * off.
+ * Alan Cox : Destroy socket doesn't write a status value to the
+ * socket buffer _AFTER_ freeing it! Also sock ensures
+ * the socket will get removed BEFORE this is called
+ * otherwise if the timer TIME_DESTROY occurs inside
+ * of inet_bh() with this socket being handled it goes
+ * BOOM! Have to stop timer going off if net_bh is
+ * active or the destroy causes crashes.
+ * Alan Cox : Cleaned up unused code.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <linux/interrupt.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/arp.h>
+
+void net_delete_timer (struct sock *t)
+{
+ if(t->timer.prev)
+ del_timer (&t->timer);
+ t->timeout = 0;
+}
+
+void net_reset_timer (struct sock *t, int timeout, unsigned long len)
+{
+ t->timeout = timeout;
+ mod_timer(&t->timer, jiffies+len);
+}
+
+/* Now we will only be called whenever we need to do
+ * something, but we must be sure to process all of the
+ * sockets that need it.
+ */
+void net_timer (unsigned long data)
+{
+ struct sock *sk = (struct sock*)data;
+ int why = sk->timeout;
+
+ /* Only process if socket is not in use. */
+ if (atomic_read(&sk->sock_readers)) {
+ /* Try again later. */
+ mod_timer(&sk->timer, jiffies+HZ/20);
+ return;
+ }
+
+ /* Always see if we need to send an ack. */
+ if (sk->tp_pinfo.af_tcp.delayed_acks && !sk->zapped) {
+ sk->prot->read_wakeup (sk);
+ if (!sk->dead)
+ sk->data_ready(sk,0);
+ }
+
+ /* Now we need to figure out why the socket was on the timer. */
+ switch (why) {
+ case TIME_DONE:
+ /* If the socket hasn't been closed off, re-try a bit later. */
+ if (!sk->dead) {
+ net_reset_timer(sk, TIME_DONE, TCP_DONE_TIME);
+ break;
+ }
+
+ if (sk->state != TCP_CLOSE) {
+ printk (KERN_DEBUG "non CLOSE socket in time_done\n");
+ break;
+ }
+ destroy_sock (sk);
+ break;
+
+ case TIME_DESTROY:
+ /* We've waited for a while for all the memory associated with
+ * the socket to be freed.
+ */
+ destroy_sock(sk);
+ break;
+
+ case TIME_CLOSE:
+ /* We've waited long enough, close the socket. */
+ tcp_set_state(sk, TCP_CLOSE);
+ sk->shutdown = SHUTDOWN_MASK;
+ if (!sk->dead)
+ sk->state_change(sk);
+ net_reset_timer (sk, TIME_DONE, TCP_DONE_TIME);
+ break;
+
+ default:
+ /* I want to see these... */
+ printk ("net_timer: timer expired - reason %d is unknown\n", why);
+ break;
+ }
+}
diff --git a/pfinet/linux-src/net/ipv4/udp.c b/pfinet/linux-src/net/ipv4/udp.c
new file mode 100644
index 00000000..f9be2e04
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/udp.c
@@ -0,0 +1,1207 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * The User Datagram Protocol (UDP).
+ *
+ * Version: $Id: udp.c,v 1.66.2.3 1999/08/07 10:56:36 davem Exp $
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ * Alan Cox, <Alan.Cox@linux.org>
+ *
+ * Fixes:
+ * Alan Cox : verify_area() calls
+ * Alan Cox : stopped close while in use off icmp
+ * messages. Not a fix but a botch that
+ * for udp at least is 'valid'.
+ * Alan Cox : Fixed icmp handling properly
+ * Alan Cox : Correct error for oversized datagrams
+ * Alan Cox : Tidied select() semantics.
+ * Alan Cox : udp_err() fixed properly, also now
+ * select and read wake correctly on errors
+ * Alan Cox : udp_send verify_area moved to avoid mem leak
+ * Alan Cox : UDP can count its memory
+ * Alan Cox : send to an unknown connection causes
+ * an ECONNREFUSED off the icmp, but
+ * does NOT close.
+ * Alan Cox : Switched to new sk_buff handlers. No more backlog!
+ * Alan Cox : Using generic datagram code. Even smaller and the PEEK
+ * bug no longer crashes it.
+ * Fred Van Kempen : Net2e support for sk->broadcast.
+ * Alan Cox : Uses skb_free_datagram
+ * Alan Cox : Added get/set sockopt support.
+ * Alan Cox : Broadcasting without option set returns EACCES.
+ * Alan Cox : No wakeup calls. Instead we now use the callbacks.
+ * Alan Cox : Use ip_tos and ip_ttl
+ * Alan Cox : SNMP Mibs
+ * Alan Cox : MSG_DONTROUTE, and 0.0.0.0 support.
+ * Matt Dillon : UDP length checks.
+ * Alan Cox : Smarter af_inet used properly.
+ * Alan Cox : Use new kernel side addressing.
+ * Alan Cox : Incorrect return on truncated datagram receive.
+ * Arnt Gulbrandsen : New udp_send and stuff
+ * Alan Cox : Cache last socket
+ * Alan Cox : Route cache
+ * Jon Peatfield : Minor efficiency fix to sendto().
+ * Mike Shaver : RFC1122 checks.
+ * Alan Cox : Nonblocking error fix.
+ * Willy Konynenberg : Transparent proxying support.
+ * Mike McLagan : Routing by source
+ * David S. Miller : New socket lookup architecture.
+ * Last socket cache retained as it
+ * does have a high hit rate.
+ * Olaf Kirch : Don't linearise iovec on sendmsg.
+ * Andi Kleen : Some cleanups, cache destination entry
+ * for connect.
+ * Vitaly E. Lavrov : Transparent proxy revived after year coma.
+ * Melvin Smith : Check msg_name not msg_namelen in sendto(),
+ * return ENOTCONN for unconnected sockets (POSIX)
+ * Janos Farkas : don't deliver multi/broadcasts to a different
+ * bound-to-device socket
+ * YOSHIFUJI Hideaki @USAGI and: Support IPV6_V6ONLY socket option, which
+ * Alexey Kuznetsov: allow both IPv4 and IPv6 sockets to bind
+ * a single port at the same time.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/* RFC1122 Status:
+ 4.1.3.1 (Ports):
+ SHOULD send ICMP_PORT_UNREACHABLE in response to datagrams to
+ an un-listened port. (OK)
+ 4.1.3.2 (IP Options)
+ MUST pass IP options from IP -> application (OK)
+ MUST allow application to specify IP options (OK)
+ 4.1.3.3 (ICMP Messages)
+ MUST pass ICMP error messages to application (OK -- except when SO_BSDCOMPAT is set)
+ 4.1.3.4 (UDP Checksums)
+ MUST provide facility for checksumming (OK)
+ MAY allow application to control checksumming (OK)
+ MUST default to checksumming on (OK)
+ MUST discard silently datagrams with bad csums (OK, except during debugging)
+ 4.1.3.5 (UDP Multihoming)
+ MUST allow application to specify source address (OK)
+ SHOULD be able to communicate the chosen src addr up to application
+ when application doesn't choose (DOES - use recvmsg cmsgs)
+ 4.1.3.6 (Invalid Addresses)
+ MUST discard invalid source addresses (OK -- done in the new routing code)
+ MUST only send datagrams with one of our addresses (OK)
+*/
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/config.h>
+#include <linux/inet.h>
+#include <linux/ipv6.h>
+#include <linux/netdevice.h>
+#include <net/snmp.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/udp.h>
+#include <net/icmp.h>
+#include <net/route.h>
+#include <net/checksum.h>
+
+/*
+ * Snmp MIB for the UDP layer
+ */
+
+struct udp_mib udp_statistics;
+
+struct sock *udp_hash[UDP_HTABLE_SIZE];
+
+/* Shared by v4/v6 udp. */
+int udp_port_rover = 0;
+
+static int udp_v4_get_port(struct sock *sk, unsigned short snum)
+{
+ SOCKHASH_LOCK();
+ if (snum == 0) {
+ int best_size_so_far, best, result, i;
+
+ if (udp_port_rover > sysctl_local_port_range[1] ||
+ udp_port_rover < sysctl_local_port_range[0])
+ udp_port_rover = sysctl_local_port_range[0];
+ best_size_so_far = 32767;
+ best = result = udp_port_rover;
+ for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
+ struct sock *sk;
+ int size;
+
+ sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)];
+ if (!sk) {
+ if (result > sysctl_local_port_range[1])
+ result = sysctl_local_port_range[0] +
+ ((result - sysctl_local_port_range[0]) &
+ (UDP_HTABLE_SIZE - 1));
+ goto gotit;
+ }
+ size = 0;
+ do {
+ if (++size >= best_size_so_far)
+ goto next;
+ } while ((sk = sk->next) != NULL);
+ best_size_so_far = size;
+ best = result;
+ next:
+ ; /* Do nothing. */
+ }
+ result = best;
+ for(;; result += UDP_HTABLE_SIZE) {
+ if (result > sysctl_local_port_range[1])
+ result = sysctl_local_port_range[0]
+ + ((result - sysctl_local_port_range[0]) &
+ (UDP_HTABLE_SIZE - 1));
+ if (!udp_lport_inuse(result))
+ break;
+ }
+gotit:
+ udp_port_rover = snum = result;
+ } else {
+ struct sock *sk2;
+
+ for (sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
+ sk2 != NULL;
+ sk2 = sk2->next) {
+ if (sk2->num == snum &&
+ sk2 != sk &&
+ !ipv6_only_sock(sk2) &&
+ sk2->bound_dev_if == sk->bound_dev_if &&
+ (!sk2->rcv_saddr ||
+ !sk->rcv_saddr ||
+ sk2->rcv_saddr == sk->rcv_saddr) &&
+ (!sk2->reuse || !sk->reuse))
+ goto fail;
+ }
+ }
+ sk->num = snum;
+ SOCKHASH_UNLOCK();
+ return 0;
+
+fail:
+ SOCKHASH_UNLOCK();
+ return 1;
+}
+
+/* Last hit UDP socket cache, this is ipv4 specific so make it static. */
+static u32 uh_cache_saddr, uh_cache_daddr;
+static u16 uh_cache_dport, uh_cache_sport;
+static struct sock *uh_cache_sk = NULL;
+
+static void udp_v4_hash(struct sock *sk)
+{
+ struct sock **skp = &udp_hash[sk->num & (UDP_HTABLE_SIZE - 1)];
+
+ SOCKHASH_LOCK();
+ if ((sk->next = *skp) != NULL)
+ (*skp)->pprev = &sk->next;
+ *skp = sk;
+ sk->pprev = skp;
+ SOCKHASH_UNLOCK();
+}
+
+static void udp_v4_unhash(struct sock *sk)
+{
+ SOCKHASH_LOCK();
+ if (sk->pprev) {
+ if (sk->next)
+ sk->next->pprev = sk->pprev;
+ *sk->pprev = sk->next;
+ sk->pprev = NULL;
+ if(uh_cache_sk == sk)
+ uh_cache_sk = NULL;
+ }
+ SOCKHASH_UNLOCK();
+}
+
+/* UDP is nearly always wildcards out the wazoo, it makes no sense to try
+ * harder than this here plus the last hit cache. -DaveM
+ */
+struct sock *udp_v4_lookup_longway(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif)
+{
+ struct sock *sk, *result = NULL;
+ unsigned short hnum = ntohs(dport);
+ int badness = -1;
+
+ for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
+ if((sk->num == hnum) && !ipv6_only_sock(sk)
+ && !(sk->dead && (sk->state == TCP_CLOSE))) {
+ int score = (sk->family == PF_INET ? 1 : 0);
+ if(sk->rcv_saddr) {
+ if(sk->rcv_saddr != daddr)
+ continue;
+ score+=2;
+ }
+ if(sk->daddr) {
+ if(sk->daddr != saddr)
+ continue;
+ score+=2;
+ }
+ if(sk->dport) {
+ if(sk->dport != sport)
+ continue;
+ score+=2;
+ }
+ if(sk->bound_dev_if) {
+ if(sk->bound_dev_if != dif)
+ continue;
+ score+=2;
+ }
+ if(score == 9) {
+ result = sk;
+ break;
+ } else if(score > badness) {
+ result = sk;
+ badness = score;
+ }
+ }
+ }
+ return result;
+}
+
+__inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif)
+{
+ struct sock *sk;
+
+ if(!dif && uh_cache_sk &&
+ uh_cache_saddr == saddr &&
+ uh_cache_sport == sport &&
+ uh_cache_dport == dport &&
+ uh_cache_daddr == daddr)
+ return uh_cache_sk;
+
+ sk = udp_v4_lookup_longway(saddr, sport, daddr, dport, dif);
+ if(!dif) {
+ uh_cache_sk = sk;
+ uh_cache_saddr = saddr;
+ uh_cache_daddr = daddr;
+ uh_cache_sport = sport;
+ uh_cache_dport = dport;
+ }
+ return sk;
+}
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+#define secondlist(hpnum, sk, fpass) \
+({ struct sock *s1; if(!(sk) && (fpass)--) \
+ s1 = udp_hash[(hpnum) & (UDP_HTABLE_SIZE - 1)]; \
+ else \
+ s1 = (sk); \
+ s1; \
+})
+
+#define udp_v4_proxy_loop_init(hnum, hpnum, sk, fpass) \
+ secondlist((hpnum), udp_hash[(hnum)&(UDP_HTABLE_SIZE-1)],(fpass))
+
+#define udp_v4_proxy_loop_next(hnum, hpnum, sk, fpass) \
+ secondlist((hpnum),(sk)->next,(fpass))
+
+static struct sock *udp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
+ unsigned short rnum, unsigned long laddr,
+ struct device *dev, unsigned short pnum,
+ int dif)
+{
+ struct sock *s, *result = NULL;
+ int badness = -1;
+ u32 paddr = 0;
+ unsigned short hnum = ntohs(num);
+ unsigned short hpnum = ntohs(pnum);
+ int firstpass = 1;
+
+ if(dev && dev->ip_ptr) {
+ struct in_device *idev = dev->ip_ptr;
+
+ if(idev->ifa_list)
+ paddr = idev->ifa_list->ifa_local;
+ }
+
+ SOCKHASH_LOCK();
+ for(s = udp_v4_proxy_loop_init(hnum, hpnum, s, firstpass);
+ s != NULL;
+ s = udp_v4_proxy_loop_next(hnum, hpnum, s, firstpass)) {
+ if(s->num == hnum || s->num == hpnum) {
+ int score = 0;
+ if(s->dead && (s->state == TCP_CLOSE))
+ continue;
+ if(s->rcv_saddr) {
+ if((s->num != hpnum || s->rcv_saddr != paddr) &&
+ (s->num != hnum || s->rcv_saddr != laddr))
+ continue;
+ score++;
+ }
+ if(s->daddr) {
+ if(s->daddr != raddr)
+ continue;
+ score++;
+ }
+ if(s->dport) {
+ if(s->dport != rnum)
+ continue;
+ score++;
+ }
+ if(s->bound_dev_if) {
+ if(s->bound_dev_if != dif)
+ continue;
+ score++;
+ }
+ if(score == 4 && s->num == hnum) {
+ result = s;
+ break;
+ } else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
+ result = s;
+ badness = score;
+ }
+ }
+ }
+ SOCKHASH_UNLOCK();
+ return result;
+}
+
+#undef secondlist
+#undef udp_v4_proxy_loop_init
+#undef udp_v4_proxy_loop_next
+
+#endif
+
+static inline struct sock *udp_v4_mcast_next(struct sock *sk,
+ unsigned short num,
+ unsigned long raddr,
+ unsigned short rnum,
+ unsigned long laddr,
+ int dif)
+{
+ struct sock *s = sk;
+ unsigned short hnum = ntohs(num);
+ for(; s; s = s->next) {
+ if ((s->num != hnum) ||
+ (s->dead && (s->state == TCP_CLOSE)) ||
+ (s->daddr && s->daddr!=raddr) ||
+ (s->dport != rnum && s->dport != 0) ||
+ (s->rcv_saddr && s->rcv_saddr != laddr) ||
+ ipv6_only_sock(s) ||
+ (s->bound_dev_if && s->bound_dev_if != dif))
+ continue;
+ break;
+ }
+ return s;
+}
+
+/*
+ * This routine is called by the ICMP module when it gets some
+ * sort of error condition. If err < 0 then the socket should
+ * be closed and the error returned to the user. If err > 0
+ * it's just the icmp type << 8 | icmp code.
+ * Header points to the ip header of the error packet. We move
+ * on past this. Then (as it used to claim before adjustment)
+ * header points to the first 8 bytes of the udp header. We need
+ * to find the appropriate port.
+ */
+
+void udp_err(struct sk_buff *skb, unsigned char *dp, int len)
+{
+ struct iphdr *iph = (struct iphdr*)dp;
+ struct udphdr *uh = (struct udphdr*)(dp+(iph->ihl<<2));
+ int type = skb->h.icmph->type;
+ int code = skb->h.icmph->code;
+ struct sock *sk;
+ int harderr;
+ u32 info;
+ int err;
+
+ if (len < (iph->ihl<<2)+sizeof(struct udphdr)) {
+ icmp_statistics.IcmpInErrors++;
+ return;
+ }
+
+ sk = udp_v4_lookup(iph->daddr, uh->dest, iph->saddr, uh->source, skb->dev->ifindex);
+ if (sk == NULL) {
+ icmp_statistics.IcmpInErrors++;
+ return; /* No socket for error */
+ }
+
+ err = 0;
+ info = 0;
+ harderr = 0;
+
+ switch (type) {
+ default:
+ case ICMP_TIME_EXCEEDED:
+ err = EHOSTUNREACH;
+ break;
+ case ICMP_SOURCE_QUENCH:
+ return;
+ case ICMP_PARAMETERPROB:
+ err = EPROTO;
+ info = ntohl(skb->h.icmph->un.gateway)>>24;
+ harderr = 1;
+ break;
+ case ICMP_DEST_UNREACH:
+ if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */
+ if (sk->ip_pmtudisc != IP_PMTUDISC_DONT) {
+ err = EMSGSIZE;
+ info = ntohs(skb->h.icmph->un.frag.mtu);
+ harderr = 1;
+ break;
+ }
+ return;
+ }
+ err = EHOSTUNREACH;
+ if (code <= NR_ICMP_UNREACH) {
+ harderr = icmp_err_convert[code].fatal;
+ err = icmp_err_convert[code].errno;
+ }
+ break;
+ }
+
+ /*
+ * Various people wanted BSD UDP semantics. Well they've come
+ * back out because they slow down response to stuff like dead
+ * or unreachable name servers and they screw term users something
+ * chronic. Oh and it violates RFC1122. So basically fix your
+ * client code people.
+ */
+
+ /*
+ * RFC1122: OK. Passes ICMP errors back to application, as per
+ * 4.1.3.3. After the comment above, that should be no surprise.
+ */
+
+ if (!harderr && !sk->ip_recverr)
+ return;
+
+ /*
+ * 4.x BSD compatibility item. Break RFC1122 to
+ * get BSD socket semantics.
+ */
+ if(sk->bsdism && sk->state!=TCP_ESTABLISHED)
+ return;
+
+ if (sk->ip_recverr)
+ ip_icmp_error(sk, skb, err, uh->dest, info, (u8*)(uh+1));
+ sk->err = err;
+ sk->error_report(sk);
+}
+
+
+static unsigned short udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr, unsigned long base)
+{
+ return(csum_tcpudp_magic(saddr, daddr, len, IPPROTO_UDP, base));
+}
+
+struct udpfakehdr
+{
+ struct udphdr uh;
+ u32 saddr;
+ u32 daddr;
+ struct iovec *iov;
+ u32 wcheck;
+};
+
+/*
+ * Copy and checksum a UDP packet from user space into a buffer. We still have
+ * to do the planning to get ip_build_xmit to spot direct transfer to network
+ * card and provide an additional callback mode for direct user->board I/O
+ * transfers. That one will be fun.
+ */
+
+static int udp_getfrag(const void *p, char * to, unsigned int offset, unsigned int fraglen)
+{
+ struct udpfakehdr *ufh = (struct udpfakehdr *)p;
+ if (offset==0) {
+ if (csum_partial_copy_fromiovecend(to+sizeof(struct udphdr), ufh->iov, offset,
+ fraglen-sizeof(struct udphdr), &ufh->wcheck))
+ return -EFAULT;
+ ufh->wcheck = csum_partial((char *)ufh, sizeof(struct udphdr),
+ ufh->wcheck);
+ ufh->uh.check = csum_tcpudp_magic(ufh->saddr, ufh->daddr,
+ ntohs(ufh->uh.len),
+ IPPROTO_UDP, ufh->wcheck);
+ if (ufh->uh.check == 0)
+ ufh->uh.check = -1;
+ memcpy(to, ufh, sizeof(struct udphdr));
+ return 0;
+ }
+ if (csum_partial_copy_fromiovecend(to, ufh->iov, offset-sizeof(struct udphdr),
+ fraglen, &ufh->wcheck))
+ return -EFAULT;
+ return 0;
+}
+
+/*
+ * Unchecksummed UDP is sufficiently critical to stuff like ATM video conferencing
+ * that we use two routines for this for speed. Probably we ought to have a
+ * CONFIG_FAST_NET set for >10Mb/second boards to activate this sort of coding.
+ * Timing needed to verify if this is a valid decision.
+ */
+
+static int udp_getfrag_nosum(const void *p, char * to, unsigned int offset, unsigned int fraglen)
+{
+ struct udpfakehdr *ufh = (struct udpfakehdr *)p;
+
+ if (offset==0) {
+ memcpy(to, ufh, sizeof(struct udphdr));
+ return memcpy_fromiovecend(to+sizeof(struct udphdr), ufh->iov, offset,
+ fraglen-sizeof(struct udphdr));
+ }
+ return memcpy_fromiovecend(to, ufh->iov, offset-sizeof(struct udphdr),
+ fraglen);
+}
+
+int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len)
+{
+ int ulen = len + sizeof(struct udphdr);
+ struct ipcm_cookie ipc;
+ struct udpfakehdr ufh;
+ struct rtable *rt = NULL;
+ int free = 0;
+ int connected = 0;
+ u32 daddr;
+ u8 tos;
+ int err;
+
+ /* This check is ONLY to check for arithmetic overflow
+ on integer(!) len. Not more! Real check will be made
+ in ip_build_xmit --ANK
+
+ BTW socket.c -> af_*.c -> ... make multiple
+ invalid conversions size_t -> int. We MUST repair it f.e.
+ by replacing all of them with size_t and revise all
+ the places sort of len += sizeof(struct iphdr)
+ If len was ULONG_MAX-10 it would be cathastrophe --ANK
+ */
+
+ if (len < 0 || len > 0xFFFF)
+ return -EMSGSIZE;
+
+ /*
+ * Check the flags.
+ */
+
+ if (msg->msg_flags&MSG_OOB) /* Mirror BSD error message compatibility */
+ return -EOPNOTSUPP;
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ if (msg->msg_flags&~(MSG_DONTROUTE|MSG_DONTWAIT|MSG_PROXY|MSG_NOSIGNAL))
+ return -EINVAL;
+ if ((msg->msg_flags&MSG_PROXY) && !capable(CAP_NET_ADMIN))
+ return -EPERM;
+#else
+ if (msg->msg_flags&~(MSG_DONTROUTE|MSG_DONTWAIT|MSG_NOSIGNAL))
+ return -EINVAL;
+#endif
+
+ /*
+ * Get and verify the address.
+ */
+
+ if (msg->msg_name) {
+ struct sockaddr_in * usin = (struct sockaddr_in*)msg->msg_name;
+ if (msg->msg_namelen < sizeof(*usin))
+ return(-EINVAL);
+ if (usin->sin_family != AF_INET) {
+ static int complained;
+ if (!complained++)
+ printk(KERN_WARNING "%s forgot to set AF_INET in udp sendmsg. Fix it!\n", current->comm);
+ if (usin->sin_family)
+ return -EINVAL;
+ }
+ ufh.daddr = usin->sin_addr.s_addr;
+ ufh.uh.dest = usin->sin_port;
+ if (ufh.uh.dest == 0)
+ return -EINVAL;
+ } else {
+ if (sk->state != TCP_ESTABLISHED)
+ return -ENOTCONN;
+ ufh.daddr = sk->daddr;
+ ufh.uh.dest = sk->dport;
+ /* Open fast path for connected socket.
+ Route will not be used, if at least one option is set.
+ */
+ connected = 1;
+ }
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ if (msg->msg_flags&MSG_PROXY) {
+ /*
+ * We map the first 8 bytes of a second sockaddr_in
+ * into the last 8 (unused) bytes of a sockaddr_in.
+ */
+ struct sockaddr_in *from = (struct sockaddr_in *)msg->msg_name;
+ from = (struct sockaddr_in *)&from->sin_zero;
+ if (from->sin_family != AF_INET)
+ return -EINVAL;
+ ipc.addr = from->sin_addr.s_addr;
+ ufh.uh.source = from->sin_port;
+ if (ipc.addr == 0)
+ ipc.addr = sk->saddr;
+ connected = 0;
+ } else
+#endif
+ {
+ ipc.addr = sk->saddr;
+ ufh.uh.source = sk->sport;
+ }
+
+ ipc.opt = NULL;
+ ipc.oif = sk->bound_dev_if;
+ if (msg->msg_controllen) {
+ err = ip_cmsg_send(msg, &ipc);
+ if (err)
+ return err;
+ if (ipc.opt)
+ free = 1;
+ connected = 0;
+ }
+ if (!ipc.opt)
+ ipc.opt = sk->opt;
+
+ ufh.saddr = ipc.addr;
+ ipc.addr = daddr = ufh.daddr;
+
+ if (ipc.opt && ipc.opt->srr) {
+ if (!daddr)
+ return -EINVAL;
+ daddr = ipc.opt->faddr;
+ connected = 0;
+ }
+ tos = RT_TOS(sk->ip_tos);
+ if (sk->localroute || (msg->msg_flags&MSG_DONTROUTE) ||
+ (ipc.opt && ipc.opt->is_strictroute)) {
+ tos |= RTO_ONLINK;
+ connected = 0;
+ }
+
+ if (MULTICAST(daddr)) {
+ if (!ipc.oif)
+ ipc.oif = sk->ip_mc_index;
+ if (!ufh.saddr)
+ ufh.saddr = sk->ip_mc_addr;
+ connected = 0;
+ }
+
+ if (connected && sk->dst_cache) {
+ rt = (struct rtable*)sk->dst_cache;
+ if (rt->u.dst.obsolete) {
+ sk->dst_cache = NULL;
+ dst_release(&rt->u.dst);
+ rt = NULL;
+ } else
+ dst_clone(&rt->u.dst);
+ }
+
+ if (rt == NULL) {
+ err = ip_route_output(&rt, daddr, ufh.saddr,
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ (msg->msg_flags&MSG_PROXY ? RTO_TPROXY : 0) |
+#endif
+ tos, ipc.oif);
+ if (err)
+ goto out;
+
+ err = -EACCES;
+ if (rt->rt_flags&RTCF_BROADCAST && !sk->broadcast)
+ goto out;
+ if (connected && sk->dst_cache == NULL)
+ sk->dst_cache = dst_clone(&rt->u.dst);
+ }
+
+ ufh.saddr = rt->rt_src;
+ if (!ipc.addr)
+ ufh.daddr = ipc.addr = rt->rt_dst;
+ ufh.uh.len = htons(ulen);
+ ufh.uh.check = 0;
+ ufh.iov = msg->msg_iov;
+ ufh.wcheck = 0;
+
+ /* RFC1122: OK. Provides the checksumming facility (MUST) as per */
+ /* 4.1.3.4. It's configurable by the application via setsockopt() */
+ /* (MAY) and it defaults to on (MUST). */
+
+ err = ip_build_xmit(sk,sk->no_check ? udp_getfrag_nosum : udp_getfrag,
+ &ufh, ulen, &ipc, rt, msg->msg_flags);
+
+out:
+ ip_rt_put(rt);
+ if (free)
+ kfree(ipc.opt);
+ if (!err) {
+ udp_statistics.UdpOutDatagrams++;
+ return len;
+ }
+ return err;
+}
+
+#ifdef _HURD_
+
+#define udp_ioctl 0
+
+#else
+
+/*
+ * IOCTL requests applicable to the UDP protocol
+ */
+
+int udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ switch(cmd)
+ {
+ case TIOCOUTQ:
+ {
+ unsigned long amount;
+
+ amount = sock_wspace(sk);
+ return put_user(amount, (int *)arg);
+ }
+
+ case TIOCINQ:
+ {
+ struct sk_buff *skb;
+ unsigned long amount;
+
+ amount = 0;
+ /* N.B. Is this interrupt safe??
+ -> Yes. Interrupts do not remove skbs. --ANK (980725)
+ */
+ skb = skb_peek(&sk->receive_queue);
+ if (skb != NULL) {
+ /*
+ * We will only return the amount
+ * of this packet since that is all
+ * that will be read.
+ */
+ amount = skb->len - sizeof(struct udphdr);
+ }
+ return put_user(amount, (int *)arg);
+ }
+
+ default:
+ return(-ENOIOCTLCMD);
+ }
+ return(0);
+}
+
+#endif
+
+#ifndef HAVE_CSUM_COPY_USER
+#undef CONFIG_UDP_DELAY_CSUM
+#endif
+
+/*
+ * This should be easy, if there is something there we
+ * return it, otherwise we block.
+ */
+
+int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
+ int noblock, int flags, int *addr_len)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
+ struct sk_buff *skb;
+ int copied, err;
+
+ if (flags & MSG_ERRQUEUE)
+ return ip_recv_error(sk, msg, len);
+
+ /*
+ * From here the generic datagram does a lot of the work. Come
+ * the finished NET3, it will do _ALL_ the work!
+ */
+
+ skb = skb_recv_datagram(sk, flags, noblock, &err);
+ if (!skb)
+ goto out;
+
+ copied = skb->len - sizeof(struct udphdr);
+ if (copied > len) {
+ copied = len;
+ msg->msg_flags |= MSG_TRUNC;
+ }
+
+#ifndef CONFIG_UDP_DELAY_CSUM
+ err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
+ copied);
+#else
+ if (skb->ip_summed==CHECKSUM_UNNECESSARY) {
+ err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
+ copied);
+ } else if (copied > msg->msg_iov[0].iov_len || (msg->msg_flags&MSG_TRUNC)) {
+ if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum)))
+ goto csum_copy_err;
+ err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
+ copied);
+ } else {
+ unsigned int csum;
+
+ err = 0;
+ csum = csum_partial(skb->h.raw, sizeof(struct udphdr), skb->csum);
+ csum = csum_and_copy_to_user((char*)&skb->h.uh[1], msg->msg_iov[0].iov_base,
+ copied, csum, &err);
+ if (err)
+ goto out_free;
+ if ((unsigned short)csum_fold(csum))
+ goto csum_copy_err;
+ }
+#endif
+ if (err)
+ goto out_free;
+ sk->stamp=skb->stamp;
+
+ /* Copy the address. */
+ if (sin)
+ {
+ /*
+ * Check any passed addresses
+ */
+ if (addr_len)
+ *addr_len=sizeof(*sin);
+
+ sin->sin_family = AF_INET;
+ sin->sin_port = skb->h.uh->source;
+ sin->sin_addr.s_addr = skb->nh.iph->saddr;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ if (flags&MSG_PROXY)
+ {
+ /*
+ * We map the first 8 bytes of a second sockaddr_in
+ * into the last 8 (unused) bytes of a sockaddr_in.
+ * This _is_ ugly, but it's the only way to do it
+ * easily, without adding system calls.
+ */
+ struct sockaddr_in *sinto =
+ (struct sockaddr_in *) sin->sin_zero;
+
+ sinto->sin_family = AF_INET;
+ sinto->sin_port = skb->h.uh->dest;
+ sinto->sin_addr.s_addr = skb->nh.iph->daddr;
+ }
+#endif
+ }
+ if (sk->ip_cmsg_flags)
+ ip_cmsg_recv(msg, skb);
+ err = copied;
+
+out_free:
+ skb_free_datagram(sk, skb);
+out:
+ return err;
+
+#ifdef CONFIG_UDP_DELAY_CSUM
+csum_copy_err:
+ udp_statistics.UdpInErrors++;
+ skb_free_datagram(sk, skb);
+
+ /*
+ * Error for blocking case is chosen to masquerade
+ * as some normal condition.
+ */
+ return (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
+#endif
+}
+
+int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+{
+ struct sockaddr_in *usin = (struct sockaddr_in *) uaddr;
+ struct rtable *rt;
+ int err;
+
+
+ if (addr_len < sizeof(*usin))
+ return(-EINVAL);
+
+ /*
+ * 1003.1g - break association.
+ */
+
+ if (usin->sin_family==AF_UNSPEC)
+ {
+ sk->saddr=INADDR_ANY;
+ sk->rcv_saddr=INADDR_ANY;
+ sk->daddr=INADDR_ANY;
+ sk->state = TCP_CLOSE;
+ if(uh_cache_sk == sk)
+ uh_cache_sk = NULL;
+ return 0;
+ }
+
+ if (usin->sin_family && usin->sin_family != AF_INET)
+ return(-EAFNOSUPPORT);
+
+ dst_release(xchg(&sk->dst_cache, NULL));
+
+ err = ip_route_connect(&rt, usin->sin_addr.s_addr, sk->saddr,
+ sk->ip_tos|sk->localroute, sk->bound_dev_if);
+ if (err)
+ return err;
+ if ((rt->rt_flags&RTCF_BROADCAST) && !sk->broadcast) {
+ ip_rt_put(rt);
+ return -EACCES;
+ }
+ if(!sk->saddr)
+ sk->saddr = rt->rt_src; /* Update source address */
+ if(!sk->rcv_saddr)
+ sk->rcv_saddr = rt->rt_src;
+ sk->daddr = rt->rt_dst;
+ sk->dport = usin->sin_port;
+ sk->state = TCP_ESTABLISHED;
+
+ if(uh_cache_sk == sk)
+ uh_cache_sk = NULL;
+
+ sk->dst_cache = &rt->u.dst;
+ return(0);
+}
+
+
+static void udp_close(struct sock *sk, long timeout)
+{
+ /* See for explanation: raw_close in ipv4/raw.c */
+ sk->state = TCP_CLOSE;
+ udp_v4_unhash(sk);
+ sk->dead = 1;
+ destroy_sock(sk);
+}
+
+static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
+{
+ /*
+ * Charge it to the socket, dropping if the queue is full.
+ */
+
+#if defined(CONFIG_FILTER) && defined(CONFIG_UDP_DELAY_CSUM)
+ if (sk->filter && skb->ip_summed != CHECKSUM_UNNECESSARY) {
+ if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum))) {
+ udp_statistics.UdpInErrors++;
+ ip_statistics.IpInDiscards++;
+ ip_statistics.IpInDelivers--;
+ kfree_skb(skb);
+ return -1;
+ }
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ }
+#endif
+
+ if (sock_queue_rcv_skb(sk,skb)<0) {
+ udp_statistics.UdpInErrors++;
+ ip_statistics.IpInDiscards++;
+ ip_statistics.IpInDelivers--;
+ kfree_skb(skb);
+ return -1;
+ }
+ udp_statistics.UdpInDatagrams++;
+ return 0;
+}
+
+
+static inline void udp_deliver(struct sock *sk, struct sk_buff *skb)
+{
+ udp_queue_rcv_skb(sk, skb);
+}
+
+/*
+ * Multicasts and broadcasts go to each listener.
+ *
+ * Note: called only from the BH handler context,
+ * so we don't need to lock the hashes.
+ */
+static int udp_v4_mcast_deliver(struct sk_buff *skb, struct udphdr *uh,
+ u32 saddr, u32 daddr)
+{
+ struct sock *sk;
+ int dif;
+
+ sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
+ dif = skb->dev->ifindex;
+ sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr, dif);
+ if (sk) {
+ struct sock *sknext = NULL;
+
+ do {
+ struct sk_buff *skb1 = skb;
+
+ sknext = udp_v4_mcast_next(sk->next, uh->dest, saddr,
+ uh->source, daddr, dif);
+ if(sknext)
+ skb1 = skb_clone(skb, GFP_ATOMIC);
+
+ if(skb1)
+ udp_deliver(sk, skb1);
+ sk = sknext;
+ } while(sknext);
+ } else
+ kfree_skb(skb);
+ return 0;
+}
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+/*
+ * Check whether a received UDP packet might be for one of our
+ * sockets.
+ */
+
+int udp_chkaddr(struct sk_buff *skb)
+{
+ struct iphdr *iph = skb->nh.iph;
+ struct udphdr *uh = (struct udphdr *)(skb->nh.raw + iph->ihl*4);
+ struct sock *sk;
+
+ sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest, skb->dev->ifindex);
+ if (!sk)
+ return 0;
+
+ /* 0 means accept all LOCAL addresses here, not all the world... */
+ if (sk->rcv_saddr == 0)
+ return 0;
+
+ return 1;
+}
+#endif
+
+/*
+ * All we need to do is get the socket, and then do a checksum.
+ */
+
+int udp_rcv(struct sk_buff *skb, unsigned short len)
+{
+ struct sock *sk;
+ struct udphdr *uh;
+ unsigned short ulen;
+ struct rtable *rt = (struct rtable*)skb->dst;
+ u32 saddr = skb->nh.iph->saddr;
+ u32 daddr = skb->nh.iph->daddr;
+
+ /*
+ * First time through the loop.. Do all the setup stuff
+ * (including finding out the socket we go to etc)
+ */
+
+ /*
+ * Get the header.
+ */
+
+ uh = skb->h.uh;
+ __skb_pull(skb, skb->h.raw - skb->data);
+
+ ip_statistics.IpInDelivers++;
+
+ /*
+ * Validate the packet and the UDP length.
+ */
+
+ ulen = ntohs(uh->len);
+
+ if (ulen > len || ulen < sizeof(*uh)) {
+ NETDEBUG(printk(KERN_DEBUG "UDP: short packet: %d/%d\n", ulen, len));
+ udp_statistics.UdpInErrors++;
+ kfree_skb(skb);
+ return(0);
+ }
+ skb_trim(skb, ulen);
+
+#ifndef CONFIG_UDP_DELAY_CSUM
+ if (uh->check &&
+ (((skb->ip_summed==CHECKSUM_HW)&&udp_check(uh,ulen,saddr,daddr,skb->csum)) ||
+ ((skb->ip_summed==CHECKSUM_NONE) &&
+ (udp_check(uh,ulen,saddr,daddr, csum_partial((char*)uh, ulen, 0))))))
+ goto csum_error;
+#else
+ if (uh->check==0)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else if (skb->ip_summed==CHECKSUM_HW) {
+ if (udp_check(uh,ulen,saddr,daddr,skb->csum))
+ goto csum_error;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ } else if (skb->ip_summed != CHECKSUM_UNNECESSARY)
+ skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0);
+#endif
+
+ if(rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
+ return udp_v4_mcast_deliver(skb, uh, saddr, daddr);
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ if (IPCB(skb)->redirport)
+ sk = udp_v4_proxy_lookup(uh->dest, saddr, uh->source,
+ daddr, skb->dev, IPCB(skb)->redirport,
+ skb->dev->ifindex);
+ else
+#endif
+ sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, skb->dev->ifindex);
+
+ if (sk == NULL) {
+#ifdef CONFIG_UDP_DELAY_CSUM
+ if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
+ (unsigned short)csum_fold(csum_partial((char*)uh, ulen, skb->csum)))
+ goto csum_error;
+#endif
+ udp_statistics.UdpNoPorts++;
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+
+ /*
+ * Hmm. We got an UDP broadcast to a port to which we
+ * don't wanna listen. Ignore it.
+ */
+ kfree_skb(skb);
+ return(0);
+ }
+ udp_deliver(sk, skb);
+ return 0;
+
+csum_error:
+ /*
+ * RFC1122: OK. Discards the bad packet silently (as far as
+ * the network is concerned, anyway) as per 4.1.3.4 (MUST).
+ */
+ NETDEBUG(printk(KERN_DEBUG "UDP: bad checksum. From %d.%d.%d.%d:%d to %d.%d.%d.%d:%d ulen %d\n",
+ NIPQUAD(saddr),
+ ntohs(uh->source),
+ NIPQUAD(daddr),
+ ntohs(uh->dest),
+ ulen));
+ udp_statistics.UdpInErrors++;
+ kfree_skb(skb);
+ return(0);
+}
+
+struct proto udp_prot = {
+ (struct sock *)&udp_prot, /* sklist_next */
+ (struct sock *)&udp_prot, /* sklist_prev */
+ udp_close, /* close */
+ udp_connect, /* connect */
+ NULL, /* accept */
+ NULL, /* retransmit */
+ NULL, /* write_wakeup */
+ NULL, /* read_wakeup */
+ datagram_poll, /* poll */
+ udp_ioctl, /* ioctl */
+ NULL, /* init */
+ NULL, /* destroy */
+ NULL, /* shutdown */
+ ip_setsockopt, /* setsockopt */
+ ip_getsockopt, /* getsockopt */
+ udp_sendmsg, /* sendmsg */
+ udp_recvmsg, /* recvmsg */
+ NULL, /* bind */
+ udp_queue_rcv_skb, /* backlog_rcv */
+ udp_v4_hash, /* hash */
+ udp_v4_unhash, /* unhash */
+ udp_v4_get_port, /* good_socknum */
+ 128, /* max_header */
+ 0, /* retransmits */
+ "UDP", /* name */
+ 0, /* inuse */
+ 0 /* highestinuse */
+};
diff --git a/pfinet/linux-src/net/ipv4/utils.c b/pfinet/linux-src/net/ipv4/utils.c
new file mode 100644
index 00000000..fe10838b
--- /dev/null
+++ b/pfinet/linux-src/net/ipv4/utils.c
@@ -0,0 +1,90 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Various kernel-resident INET utility functions; mainly
+ * for format conversion and debugging output.
+ *
+ * Version: $Id: utils.c,v 1.6 1997/12/13 21:53:03 kuznet Exp $
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * Fixes:
+ * Alan Cox : verify_area check.
+ * Alan Cox : removed old debugging.
+ * Andi Kleen : add net_ratelimit()
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <stdarg.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+#include <linux/skbuff.h>
+
+
+/*
+ * Display an IP address in readable format.
+ */
+
+char *in_ntoa(__u32 in)
+{
+ static char buff[18];
+ char *p;
+
+ p = (char *) &in;
+ sprintf(buff, "%d.%d.%d.%d",
+ (p[0] & 255), (p[1] & 255), (p[2] & 255), (p[3] & 255));
+ return(buff);
+}
+
+
+/*
+ * Convert an ASCII string to binary IP.
+ */
+
+__u32 in_aton(const char *str)
+{
+ unsigned long l;
+ unsigned int val;
+ int i;
+
+ l = 0;
+ for (i = 0; i < 4; i++)
+ {
+ l <<= 8;
+ if (*str != '\0')
+ {
+ val = 0;
+ while (*str != '\0' && *str != '.')
+ {
+ val *= 10;
+ val += *str - '0';
+ str++;
+ }
+ l |= val;
+ if (*str != '\0')
+ str++;
+ }
+ }
+ return(htonl(l));
+}
diff --git a/pfinet/linux-src/net/ipv6/addrconf.c b/pfinet/linux-src/net/ipv6/addrconf.c
new file mode 100644
index 00000000..d392b3fd
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/addrconf.c
@@ -0,0 +1,1964 @@
+/*
+ * IPv6 Address [auto]configuration
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: addrconf.c,v 1.4 2009/02/24 01:21:14 sthibaul Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * Changes:
+ *
+ * Janos Farkas : delete timer on ifdown
+ * <chexum@bankinf.banki.hu>
+ * Andi Kleen : kill doube kfree on module
+ * unload.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/sched.h>
+#include <linux/net.h>
+#include <linux/in6.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/route.h>
+#include <linux/inetdevice.h>
+#include <linux/init.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
+#include <linux/delay.h>
+
+#include <linux/proc_fs.h>
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/ndisc.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+#include <net/ip.h>
+#include <linux/if_tunnel.h>
+#include <linux/rtnetlink.h>
+
+#include <asm/uaccess.h>
+
+/* Set to 3 to get tracing... */
+#define ACONF_DEBUG 2
+
+#if ACONF_DEBUG >= 3
+#define ADBG(x) printk x
+#else
+#define ADBG(x)
+#endif
+
+#ifdef CONFIG_SYSCTL
+static void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf *p);
+static void addrconf_sysctl_unregister(struct ipv6_devconf *p);
+#endif
+
+/*
+ * Configured unicast address list
+ */
+static struct inet6_ifaddr *inet6_addr_lst[IN6_ADDR_HSIZE];
+
+/*
+ * AF_INET6 device list
+ */
+static struct inet6_dev *inet6_dev_lst[IN6_ADDR_HSIZE];
+
+static atomic_t addr_list_lock = ATOMIC_INIT(0);
+
+void addrconf_verify(unsigned long);
+
+static struct timer_list addr_chk_timer = {
+ NULL, NULL,
+ 0, 0, addrconf_verify
+};
+
+/* These locks protect only against address deletions,
+ but not against address adds or status updates.
+ It is OK. The only race is when address is selected,
+ which becomes invalid immediately after selection.
+ It is harmless, because this address could be already invalid
+ several usecs ago.
+
+ Its important, that:
+
+ 1. The result of inet6_add_addr() is used only inside lock
+ or from bh_atomic context.
+
+ 2. inet6_get_lladdr() is used only from bh protected context.
+
+ 3. The result of ipv6_chk_addr() is not used outside of bh protected context.
+ */
+
+static __inline__ void addrconf_lock(void)
+{
+ atomic_inc(&addr_list_lock);
+ synchronize_bh();
+}
+
+static __inline__ void addrconf_unlock(void)
+{
+ atomic_dec(&addr_list_lock);
+}
+
+static int addrconf_ifdown(struct device *dev, int how);
+
+static void addrconf_dad_start(struct inet6_ifaddr *ifp);
+static void addrconf_dad_timer(unsigned long data);
+static void addrconf_dad_completed(struct inet6_ifaddr *ifp);
+static void addrconf_rs_timer(unsigned long data);
+static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
+
+struct ipv6_devconf ipv6_devconf =
+{
+ 0, /* forwarding */
+ IPV6_DEFAULT_HOPLIMIT, /* hop limit */
+ IPV6_MIN_MTU, /* mtu */
+ 1, /* accept RAs */
+ 1, /* accept redirects */
+ 1, /* autoconfiguration */
+ 1, /* dad transmits */
+ MAX_RTR_SOLICITATIONS, /* router solicits */
+ RTR_SOLICITATION_INTERVAL, /* rtr solicit interval */
+ MAX_RTR_SOLICITATION_DELAY, /* rtr solicit delay */
+};
+
+static struct ipv6_devconf ipv6_devconf_dflt =
+{
+ 0, /* forwarding */
+ IPV6_DEFAULT_HOPLIMIT, /* hop limit */
+ IPV6_MIN_MTU, /* mtu */
+ 1, /* accept RAs */
+ 1, /* accept redirects */
+ 1, /* autoconfiguration */
+ 1, /* dad transmits */
+ MAX_RTR_SOLICITATIONS, /* router solicits */
+ RTR_SOLICITATION_INTERVAL, /* rtr solicit interval */
+ MAX_RTR_SOLICITATION_DELAY, /* rtr solicit delay */
+};
+
+int ipv6_addr_type(struct in6_addr *addr)
+{
+ u32 st;
+
+ st = addr->s6_addr32[0];
+
+ /* Consider all addresses with the first three bits different of
+ 000 and 111 as unicasts.
+ */
+ if ((st & __constant_htonl(0xE0000000)) != __constant_htonl(0x00000000) &&
+ (st & __constant_htonl(0xE0000000)) != __constant_htonl(0xE0000000))
+ return IPV6_ADDR_UNICAST;
+
+ if ((st & __constant_htonl(0xFF000000)) == __constant_htonl(0xFF000000)) {
+ int type = IPV6_ADDR_MULTICAST;
+
+ switch((st & __constant_htonl(0x00FF0000))) {
+ case __constant_htonl(0x00010000):
+ type |= IPV6_ADDR_LOOPBACK;
+ break;
+
+ case __constant_htonl(0x00020000):
+ type |= IPV6_ADDR_LINKLOCAL;
+ break;
+
+ case __constant_htonl(0x00050000):
+ type |= IPV6_ADDR_SITELOCAL;
+ break;
+ };
+ return type;
+ }
+
+ if ((st & __constant_htonl(0xFFC00000)) == __constant_htonl(0xFE800000))
+ return (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST);
+
+ if ((st & __constant_htonl(0xFFC00000)) == __constant_htonl(0xFEC00000))
+ return (IPV6_ADDR_SITELOCAL | IPV6_ADDR_UNICAST);
+
+ if ((addr->s6_addr32[0] | addr->s6_addr32[1]) == 0) {
+ if (addr->s6_addr32[2] == 0) {
+ if (addr->__in6_u.__u6_addr32[3] == 0)
+ return IPV6_ADDR_ANY;
+
+ if (addr->s6_addr32[3] == __constant_htonl(0x00000001))
+ return (IPV6_ADDR_LOOPBACK | IPV6_ADDR_UNICAST);
+
+ return (IPV6_ADDR_COMPATv4 | IPV6_ADDR_UNICAST);
+ }
+
+ if (addr->s6_addr32[2] == __constant_htonl(0x0000ffff))
+ return IPV6_ADDR_MAPPED;
+ }
+
+ return IPV6_ADDR_RESERVED;
+}
+
+static struct inet6_dev * ipv6_add_dev(struct device *dev)
+{
+ struct inet6_dev *ndev, **bptr, *iter;
+ int hash;
+
+ if (dev->mtu < IPV6_MIN_MTU)
+ return NULL;
+
+ ndev = kmalloc(sizeof(struct inet6_dev), GFP_KERNEL);
+
+ if (ndev) {
+ memset(ndev, 0, sizeof(struct inet6_dev));
+
+ ndev->dev = dev;
+ memcpy(&ndev->cnf, &ipv6_devconf_dflt, sizeof(ndev->cnf));
+ ndev->cnf.mtu6 = dev->mtu;
+ ndev->cnf.sysctl = NULL;
+ ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
+ if (ndev->nd_parms == NULL) {
+ kfree(ndev);
+ return NULL;
+ }
+#ifdef CONFIG_SYSCTL
+ neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
+ addrconf_sysctl_register(ndev, &ndev->cnf);
+#endif
+ hash = ipv6_devindex_hash(dev->ifindex);
+ bptr = &inet6_dev_lst[hash];
+ iter = *bptr;
+
+ for (; iter; iter = iter->next)
+ bptr = &iter->next;
+
+ *bptr = ndev;
+
+ }
+ return ndev;
+}
+
+#ifndef _HURD_
+static
+#endif
+struct inet6_dev * ipv6_find_idev(struct device *dev)
+{
+ struct inet6_dev *idev;
+
+ if ((idev = ipv6_get_idev(dev)) == NULL) {
+ idev = ipv6_add_dev(dev);
+ if (idev == NULL)
+ return NULL;
+ if (dev->flags&IFF_UP)
+ ipv6_mc_up(idev);
+ }
+ return idev;
+}
+
+static void addrconf_forward_change(struct inet6_dev *idev)
+{
+ int i;
+
+ if (idev)
+ return;
+
+ for (i = 0; i < IN6_ADDR_HSIZE; i++) {
+ for (idev = inet6_dev_lst[i]; idev; idev = idev->next)
+ idev->cnf.forwarding = ipv6_devconf.forwarding;
+ }
+}
+
+struct inet6_dev * ipv6_get_idev(struct device *dev)
+{
+ struct inet6_dev *idev;
+ int hash;
+
+ hash = ipv6_devindex_hash(dev->ifindex);
+
+ for (idev = inet6_dev_lst[hash]; idev; idev = idev->next) {
+ if (idev->dev == dev)
+ return idev;
+ }
+ return NULL;
+}
+
+static struct inet6_ifaddr *
+ipv6_add_addr(struct inet6_dev *idev, struct in6_addr *addr, int scope)
+{
+ struct inet6_ifaddr *ifa;
+ int hash;
+
+ ifa = kmalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC);
+
+ if (ifa == NULL) {
+ ADBG(("ipv6_add_addr: malloc failed\n"));
+ return NULL;
+ }
+
+ memset(ifa, 0, sizeof(struct inet6_ifaddr));
+ memcpy(&ifa->addr, addr, sizeof(struct in6_addr));
+
+ init_timer(&ifa->timer);
+ ifa->timer.data = (unsigned long) ifa;
+ ifa->scope = scope;
+ ifa->idev = idev;
+
+ /* Add to list. */
+ hash = ipv6_addr_hash(addr);
+
+ ifa->lst_next = inet6_addr_lst[hash];
+ inet6_addr_lst[hash] = ifa;
+
+ /* Add to inet6_dev unicast addr list. */
+ ifa->if_next = idev->addr_list;
+ idev->addr_list = ifa;
+
+ return ifa;
+}
+
+static void ipv6_del_addr(struct inet6_ifaddr *ifp)
+{
+ struct inet6_ifaddr *iter, **back;
+ int hash;
+
+ if (atomic_read(&addr_list_lock)) {
+ ifp->flags |= ADDR_INVALID;
+ ipv6_ifa_notify(RTM_DELADDR, ifp);
+ return;
+ }
+
+ hash = ipv6_addr_hash(&ifp->addr);
+
+ iter = inet6_addr_lst[hash];
+ back = &inet6_addr_lst[hash];
+
+ for (; iter; iter = iter->lst_next) {
+ if (iter == ifp) {
+ *back = ifp->lst_next;
+ synchronize_bh();
+
+ ifp->lst_next = NULL;
+ break;
+ }
+ back = &(iter->lst_next);
+ }
+
+ iter = ifp->idev->addr_list;
+ back = &ifp->idev->addr_list;
+
+ for (; iter; iter = iter->if_next) {
+ if (iter == ifp) {
+ *back = ifp->if_next;
+ synchronize_bh();
+
+ ifp->if_next = NULL;
+ break;
+ }
+ back = &(iter->if_next);
+ }
+
+ ipv6_ifa_notify(RTM_DELADDR, ifp);
+ del_timer(&ifp->timer);
+
+ kfree(ifp);
+}
+
+/*
+ * Choose an appropriate source address
+ * should do:
+ * i) get an address with an appropriate scope
+ * ii) see if there is a specific route for the destination and use
+ * an address of the attached interface
+ * iii) don't use deprecated addresses
+ */
+int ipv6_get_saddr(struct dst_entry *dst,
+ struct in6_addr *daddr, struct in6_addr *saddr)
+{
+ int scope;
+ struct inet6_ifaddr *ifp = NULL;
+ struct inet6_ifaddr *match = NULL;
+ struct device *dev = NULL;
+ struct rt6_info *rt;
+ int err;
+ int i;
+
+ rt = (struct rt6_info *) dst;
+ if (rt)
+ dev = rt->rt6i_dev;
+
+ addrconf_lock();
+
+ scope = ipv6_addr_scope(daddr);
+ if (rt && (rt->rt6i_flags & RTF_ALLONLINK)) {
+ /*
+ * route for the "all destinations on link" rule
+ * when no routers are present
+ */
+ scope = IFA_LINK;
+ }
+
+ /*
+ * known dev
+ * search dev and walk through dev addresses
+ */
+
+ if (dev) {
+ struct inet6_dev *idev;
+ int hash;
+
+ if (dev->flags & IFF_LOOPBACK)
+ scope = IFA_HOST;
+
+ hash = ipv6_devindex_hash(dev->ifindex);
+ for (idev = inet6_dev_lst[hash]; idev; idev=idev->next) {
+ if (idev->dev == dev) {
+ for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
+ if (ifp->scope == scope) {
+ if (!(ifp->flags & (ADDR_STATUS|DAD_STATUS)))
+ goto out;
+
+ if (!(ifp->flags & (ADDR_INVALID|DAD_STATUS)))
+ match = ifp;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if (scope == IFA_LINK)
+ goto out;
+
+ /*
+ * dev == NULL or search failed for specified dev
+ */
+
+ for (i=0; i < IN6_ADDR_HSIZE; i++) {
+ for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) {
+ if (ifp->scope == scope) {
+ if (!(ifp->flags & (ADDR_STATUS|DAD_STATUS)))
+ goto out;
+
+ if (!(ifp->flags & (ADDR_INVALID|DAD_STATUS)))
+ match = ifp;
+ }
+ }
+ }
+
+out:
+ if (ifp == NULL)
+ ifp = match;
+
+ err = -ENETUNREACH;
+ if (ifp) {
+ memcpy(saddr, &ifp->addr, sizeof(struct in6_addr));
+ err = 0;
+ }
+ addrconf_unlock();
+ return err;
+}
+
+struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev)
+{
+ struct inet6_ifaddr *ifp = NULL;
+ struct inet6_dev *idev;
+
+ if ((idev = ipv6_get_idev(dev)) != NULL) {
+ addrconf_lock();
+ for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
+ if (ifp->scope == IFA_LINK)
+ break;
+ }
+ addrconf_unlock();
+ }
+ return ifp;
+}
+
+/*
+ * Retrieve the ifaddr struct from an v6 address
+ * Called from ipv6_rcv to check if the address belongs
+ * to the host.
+ */
+
+struct inet6_ifaddr * ipv6_chk_addr(struct in6_addr *addr, struct device *dev, int nd)
+{
+ struct inet6_ifaddr * ifp;
+ u8 hash;
+ unsigned flags = 0;
+
+ if (!nd)
+ flags |= DAD_STATUS|ADDR_INVALID;
+
+ addrconf_lock();
+
+ hash = ipv6_addr_hash(addr);
+ for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
+ if (ipv6_addr_cmp(&ifp->addr, addr) == 0 && !(ifp->flags&flags)) {
+ if (dev == NULL || ifp->idev->dev == dev ||
+ !(ifp->scope&(IFA_LINK|IFA_HOST)))
+ break;
+ }
+ }
+
+ addrconf_unlock();
+ return ifp;
+}
+
+void addrconf_dad_failure(struct inet6_ifaddr *ifp)
+{
+ printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name);
+ del_timer(&ifp->timer);
+ ipv6_del_addr(ifp);
+}
+
+
+/* Join to solicited addr multicast group. */
+
+static void addrconf_join_solict(struct device *dev, struct in6_addr *addr)
+{
+ struct in6_addr maddr;
+
+ if (dev->flags&(IFF_LOOPBACK|IFF_NOARP))
+ return;
+
+#ifndef CONFIG_IPV6_NO_PB
+ addrconf_addr_solict_mult_old(addr, &maddr);
+ ipv6_dev_mc_inc(dev, &maddr);
+#endif
+#ifdef CONFIG_IPV6_EUI64
+ addrconf_addr_solict_mult_new(addr, &maddr);
+ ipv6_dev_mc_inc(dev, &maddr);
+#endif
+}
+
+static void addrconf_leave_solict(struct device *dev, struct in6_addr *addr)
+{
+ struct in6_addr maddr;
+
+ if (dev->flags&(IFF_LOOPBACK|IFF_NOARP))
+ return;
+
+#ifndef CONFIG_IPV6_NO_PB
+ addrconf_addr_solict_mult_old(addr, &maddr);
+ ipv6_dev_mc_dec(dev, &maddr);
+#endif
+#ifdef CONFIG_IPV6_EUI64
+ addrconf_addr_solict_mult_new(addr, &maddr);
+ ipv6_dev_mc_dec(dev, &maddr);
+#endif
+}
+
+
+#ifdef CONFIG_IPV6_EUI64
+static int ipv6_generate_eui64(u8 *eui, struct device *dev)
+{
+ switch (dev->type) {
+ case ARPHRD_ETHER:
+ if (dev->addr_len != ETH_ALEN)
+ return -1;
+ memcpy(eui, dev->dev_addr, 3);
+ memcpy(eui + 5, dev->dev_addr+3, 3);
+ eui[3] = 0xFF;
+ eui[4] = 0xFE;
+ eui[0] ^= 2;
+ return 0;
+ }
+ return -1;
+}
+
+static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev)
+{
+ int err = -1;
+ struct inet6_ifaddr *ifp;
+
+ for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
+ if (ifp->scope == IFA_LINK && !(ifp->flags&(ADDR_STATUS|DAD_STATUS))) {
+ memcpy(eui, ifp->addr.s6_addr+8, 8);
+ err = 0;
+ break;
+ }
+ }
+ return err;
+}
+#endif
+
+/*
+ * Add prefix route.
+ */
+
+static void
+addrconf_prefix_route(struct in6_addr *pfx, int plen, struct device *dev,
+ unsigned long expires, unsigned flags)
+{
+ struct in6_rtmsg rtmsg;
+
+ memset(&rtmsg, 0, sizeof(rtmsg));
+ memcpy(&rtmsg.rtmsg_dst, pfx, sizeof(struct in6_addr));
+ rtmsg.rtmsg_dst_len = plen;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+ rtmsg.rtmsg_ifindex = dev->ifindex;
+ rtmsg.rtmsg_info = expires;
+ rtmsg.rtmsg_flags = RTF_UP|flags;
+ rtmsg.rtmsg_type = RTMSG_NEWROUTE;
+
+ /* Prevent useless cloning on PtP SIT.
+ This thing is done here expecting that the whole
+ class of non-broadcast devices need not cloning.
+ */
+ if (dev->type == ARPHRD_SIT && (dev->flags&IFF_POINTOPOINT))
+ rtmsg.rtmsg_flags |= RTF_NONEXTHOP;
+
+ ip6_route_add(&rtmsg);
+}
+
+/* Create "default" multicast route to the interface */
+
+static void addrconf_add_mroute(struct device *dev)
+{
+ struct in6_rtmsg rtmsg;
+
+ memset(&rtmsg, 0, sizeof(rtmsg));
+ ipv6_addr_set(&rtmsg.rtmsg_dst,
+ __constant_htonl(0xFF000000), 0, 0, 0);
+ rtmsg.rtmsg_dst_len = 8;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+ rtmsg.rtmsg_ifindex = dev->ifindex;
+ rtmsg.rtmsg_flags = RTF_UP|RTF_ADDRCONF;
+ rtmsg.rtmsg_type = RTMSG_NEWROUTE;
+ ip6_route_add(&rtmsg);
+}
+
+static void sit_route_add(struct device *dev)
+{
+ struct in6_rtmsg rtmsg;
+
+ memset(&rtmsg, 0, sizeof(rtmsg));
+
+ rtmsg.rtmsg_type = RTMSG_NEWROUTE;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+
+ /* prefix length - 96 bytes "::d.d.d.d" */
+ rtmsg.rtmsg_dst_len = 96;
+ rtmsg.rtmsg_flags = RTF_UP|RTF_NONEXTHOP;
+ rtmsg.rtmsg_ifindex = dev->ifindex;
+
+ ip6_route_add(&rtmsg);
+}
+
+static void addrconf_add_lroute(struct device *dev)
+{
+ struct in6_addr addr;
+
+ ipv6_addr_set(&addr, __constant_htonl(0xFE800000), 0, 0, 0);
+ addrconf_prefix_route(&addr, 10, dev, 0, RTF_ADDRCONF);
+}
+
+static struct inet6_dev *addrconf_add_dev(struct device *dev)
+{
+ struct inet6_dev *idev;
+
+ if ((idev = ipv6_find_idev(dev)) == NULL)
+ return NULL;
+
+ /* Add default multicast route */
+ addrconf_add_mroute(dev);
+
+ /* Add link local route */
+ addrconf_add_lroute(dev);
+ return idev;
+}
+
+void addrconf_prefix_rcv(struct device *dev, u8 *opt, int len)
+{
+ struct prefix_info *pinfo;
+ struct rt6_info *rt;
+ __u32 valid_lft;
+ __u32 prefered_lft;
+ int addr_type;
+ unsigned long rt_expires;
+ struct inet6_dev *in6_dev = ipv6_get_idev(dev);
+
+ if (in6_dev == NULL) {
+ printk(KERN_DEBUG "addrconf: device %s not configured\n", dev->name);
+ return;
+ }
+
+ pinfo = (struct prefix_info *) opt;
+
+ if (len < sizeof(struct prefix_info)) {
+ ADBG(("addrconf: prefix option too short\n"));
+ return;
+ }
+
+ /*
+ * Validation checks ([ADDRCONF], page 19)
+ */
+
+ addr_type = ipv6_addr_type(&pinfo->prefix);
+
+ if (addr_type & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL))
+ return;
+
+ valid_lft = ntohl(pinfo->valid);
+ prefered_lft = ntohl(pinfo->prefered);
+
+ if (prefered_lft > valid_lft) {
+ printk(KERN_WARNING "addrconf: prefix option has invalid lifetime\n");
+ return;
+ }
+
+ /*
+ * Two things going on here:
+ * 1) Add routes for on-link prefixes
+ * 2) Configure prefixes with the auto flag set
+ */
+
+ /* Avoid arithemtic overflow. Really, we could
+ save rt_expires in seconds, likely valid_lft,
+ but it would require division in fib gc, that it
+ not good.
+ */
+ if (valid_lft >= 0x7FFFFFFF/HZ)
+ rt_expires = 0;
+ else
+ rt_expires = jiffies + valid_lft * HZ;
+
+ rt = rt6_lookup(&pinfo->prefix, NULL, dev->ifindex, 1);
+
+ if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
+ if (rt->rt6i_flags&RTF_EXPIRES) {
+ if (pinfo->onlink == 0 || valid_lft == 0) {
+ ip6_del_rt(rt);
+ } else {
+ rt->rt6i_expires = rt_expires;
+ }
+ }
+ } else if (pinfo->onlink && valid_lft) {
+ addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
+ dev, rt_expires, RTF_ADDRCONF|RTF_EXPIRES);
+ }
+ if (rt)
+ dst_release(&rt->u.dst);
+
+ /* Try to figure out our local address for this prefix */
+
+ if (pinfo->autoconf && in6_dev->cnf.autoconf) {
+ struct inet6_ifaddr * ifp;
+ struct in6_addr addr;
+ int plen;
+
+ plen = pinfo->prefix_len >> 3;
+
+#ifdef CONFIG_IPV6_EUI64
+ if (pinfo->prefix_len == 64) {
+ memcpy(&addr, &pinfo->prefix, 8);
+ if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
+ ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev))
+ return;
+ goto ok;
+ }
+#endif
+#ifndef CONFIG_IPV6_NO_PB
+ if (pinfo->prefix_len == ((sizeof(struct in6_addr) - dev->addr_len)<<3)) {
+ memcpy(&addr, &pinfo->prefix, plen);
+ memcpy(addr.s6_addr + plen, dev->dev_addr,
+ dev->addr_len);
+ goto ok;
+ }
+#endif
+ printk(KERN_DEBUG "IPv6 addrconf: prefix with wrong length %d\n", pinfo->prefix_len);
+ return;
+
+ok:
+ ifp = ipv6_chk_addr(&addr, dev, 1);
+
+ if ((ifp == NULL || (ifp->flags&ADDR_INVALID)) && valid_lft) {
+
+ if (ifp == NULL)
+ ifp = ipv6_add_addr(in6_dev, &addr, addr_type & IPV6_ADDR_SCOPE_MASK);
+
+ if (ifp == NULL)
+ return;
+
+ ifp->prefix_len = pinfo->prefix_len;
+
+ addrconf_dad_start(ifp);
+ }
+
+ if (ifp && valid_lft == 0) {
+ ipv6_del_addr(ifp);
+ ifp = NULL;
+ }
+
+ if (ifp) {
+ int event = 0;
+ ifp->valid_lft = valid_lft;
+ ifp->prefered_lft = prefered_lft;
+ ifp->tstamp = jiffies;
+ if (ifp->flags & ADDR_INVALID)
+ event = RTM_NEWADDR;
+ ifp->flags &= ~(ADDR_DEPRECATED|ADDR_INVALID);
+ ipv6_ifa_notify(event, ifp);
+ }
+ }
+}
+
+#ifndef _HURD_
+/*
+ * Set destination address.
+ * Special case for SIT interfaces where we create a new "virtual"
+ * device.
+ */
+int addrconf_set_dstaddr(void *arg)
+{
+ struct in6_ifreq ireq;
+ struct device *dev;
+ int err = -EINVAL;
+
+ rtnl_lock();
+
+ err = -EFAULT;
+ if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+ goto err_exit;
+
+ dev = dev_get_by_index(ireq.ifr6_ifindex);
+
+ err = -ENODEV;
+ if (dev == NULL)
+ goto err_exit;
+
+ if (dev->type == ARPHRD_SIT) {
+ struct ifreq ifr;
+ mm_segment_t oldfs;
+ struct ip_tunnel_parm p;
+
+ err = -EADDRNOTAVAIL;
+ if (!(ipv6_addr_type(&ireq.ifr6_addr) & IPV6_ADDR_COMPATv4))
+ goto err_exit;
+
+ memset(&p, 0, sizeof(p));
+ p.iph.daddr = ireq.ifr6_addr.s6_addr32[3];
+ p.iph.saddr = 0;
+ p.iph.version = 4;
+ p.iph.ihl = 5;
+ p.iph.protocol = IPPROTO_IPV6;
+ p.iph.ttl = 64;
+ ifr.ifr_ifru.ifru_data = (void*)&p;
+
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ err = dev->do_ioctl(dev, &ifr, SIOCADDTUNNEL);
+ set_fs(oldfs);
+
+ if (err == 0) {
+ err = -ENOBUFS;
+ if ((dev = dev_get(p.name)) == NULL)
+ goto err_exit;
+ err = dev_open(dev);
+ }
+ }
+
+err_exit:
+ rtnl_unlock();
+ return err;
+}
+#endif /* not _HURD_ */
+
+/*
+ * Manual configuration of address on an interface
+ */
+#ifndef _HURD_
+static
+#endif
+int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen)
+{
+ struct inet6_ifaddr *ifp;
+ struct inet6_dev *idev;
+ struct device *dev;
+ int scope;
+
+ if ((dev = dev_get_by_index(ifindex)) == NULL)
+ return -ENODEV;
+
+ if (!(dev->flags&IFF_UP))
+ return -ENETDOWN;
+
+ if ((idev = addrconf_add_dev(dev)) == NULL)
+ return -ENOBUFS;
+
+ scope = ipv6_addr_scope(pfx);
+
+ addrconf_lock();
+ if ((ifp = ipv6_add_addr(idev, pfx, scope)) != NULL) {
+ ifp->prefix_len = plen;
+ ifp->flags |= ADDR_PERMANENT;
+ addrconf_dad_start(ifp);
+ addrconf_unlock();
+ return 0;
+ }
+ addrconf_unlock();
+
+ return -ENOBUFS;
+}
+
+#ifndef _HURD_
+static
+#endif
+int inet6_addr_del(int ifindex, struct in6_addr *pfx, int plen)
+{
+ struct inet6_ifaddr *ifp;
+ struct inet6_dev *idev;
+ struct device *dev;
+
+ if ((dev = dev_get_by_index(ifindex)) == NULL)
+ return -ENODEV;
+
+ if ((idev = ipv6_get_idev(dev)) == NULL)
+ return -ENXIO;
+
+ start_bh_atomic();
+ for (ifp = idev->addr_list; ifp; ifp=ifp->if_next) {
+ if (ifp->prefix_len == plen &&
+ (!memcmp(pfx, &ifp->addr, sizeof(struct in6_addr)))) {
+ ipv6_del_addr(ifp);
+ end_bh_atomic();
+
+ /* If the last address is deleted administratively,
+ disable IPv6 on this interface.
+ */
+ if (idev->addr_list == NULL)
+ addrconf_ifdown(idev->dev, 1);
+ return 0;
+ }
+ }
+ end_bh_atomic();
+ return -EADDRNOTAVAIL;
+}
+
+
+#ifndef _HURD_
+int addrconf_add_ifaddr(void *arg)
+{
+ struct in6_ifreq ireq;
+ int err;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+ return -EFAULT;
+
+ rtnl_lock();
+ err = inet6_addr_add(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen);
+ rtnl_unlock();
+ return err;
+}
+#endif /* not _HURD_ */
+
+#ifndef _HURD_
+int addrconf_del_ifaddr(void *arg)
+{
+ struct in6_ifreq ireq;
+ int err;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+ return -EFAULT;
+
+ rtnl_lock();
+ err = inet6_addr_del(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen);
+ rtnl_unlock();
+ return err;
+}
+#endif /* not _HURD_ */
+
+static void sit_add_v4_addrs(struct inet6_dev *idev)
+{
+ struct inet6_ifaddr * ifp;
+ struct in6_addr addr;
+ struct device *dev;
+ int scope;
+
+ memset(&addr, 0, sizeof(struct in6_addr));
+ memcpy(&addr.s6_addr32[3], idev->dev->dev_addr, 4);
+
+ if (idev->dev->flags&IFF_POINTOPOINT) {
+ addr.s6_addr32[0] = __constant_htonl(0xfe800000);
+ scope = IFA_LINK;
+ } else {
+ scope = IPV6_ADDR_COMPATv4;
+ }
+
+ if (addr.s6_addr32[3]) {
+ addrconf_lock();
+ ifp = ipv6_add_addr(idev, &addr, scope);
+ if (ifp) {
+ ifp->flags |= ADDR_PERMANENT;
+ ifp->prefix_len = 128;
+ ipv6_ifa_notify(RTM_NEWADDR, ifp);
+ }
+ addrconf_unlock();
+ return;
+ }
+
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
+ if (dev->ip_ptr && (dev->flags & IFF_UP)) {
+ struct in_device * in_dev = dev->ip_ptr;
+ struct in_ifaddr * ifa;
+
+ int flag = scope;
+
+ for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
+ addr.s6_addr32[3] = ifa->ifa_local;
+
+ if (ifa->ifa_scope == RT_SCOPE_LINK)
+ continue;
+ if (ifa->ifa_scope >= RT_SCOPE_HOST) {
+ if (idev->dev->flags&IFF_POINTOPOINT)
+ continue;
+ flag |= IFA_HOST;
+ }
+
+ addrconf_lock();
+ ifp = ipv6_add_addr(idev, &addr, flag);
+ if (ifp) {
+ if (idev->dev->flags&IFF_POINTOPOINT)
+ ifp->prefix_len = 10;
+ else
+ ifp->prefix_len = 96;
+ ifp->flags |= ADDR_PERMANENT;
+ ipv6_ifa_notify(RTM_NEWADDR, ifp);
+ }
+ addrconf_unlock();
+ }
+ }
+ }
+}
+
+static void init_loopback(struct device *dev)
+{
+ struct in6_addr addr;
+ struct inet6_dev *idev;
+ struct inet6_ifaddr * ifp;
+
+ /* ::1 */
+
+ memset(&addr, 0, sizeof(struct in6_addr));
+ addr.s6_addr[15] = 1;
+
+ if ((idev = ipv6_find_idev(dev)) == NULL) {
+ printk(KERN_DEBUG "init loopback: add_dev failed\n");
+ return;
+ }
+
+ addrconf_lock();
+ ifp = ipv6_add_addr(idev, &addr, IFA_HOST);
+
+ if (ifp) {
+ ifp->flags |= ADDR_PERMANENT;
+ ifp->prefix_len = 128;
+ ipv6_ifa_notify(RTM_NEWADDR, ifp);
+ }
+ addrconf_unlock();
+}
+
+static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
+{
+ struct inet6_ifaddr * ifp;
+
+ addrconf_lock();
+ ifp = ipv6_add_addr(idev, addr, IFA_LINK);
+ if (ifp) {
+ ifp->flags = ADDR_PERMANENT;
+ ifp->prefix_len = 10;
+ addrconf_dad_start(ifp);
+ }
+ addrconf_unlock();
+}
+
+static void addrconf_dev_config(struct device *dev)
+{
+ struct in6_addr addr;
+ struct inet6_dev * idev;
+
+ if (dev->type != ARPHRD_ETHER) {
+ /* Alas, we support only Ethernet autoconfiguration. */
+ return;
+ }
+
+ idev = addrconf_add_dev(dev);
+ if (idev == NULL)
+ return;
+
+#ifdef CONFIG_IPV6_EUI64
+ memset(&addr, 0, sizeof(struct in6_addr));
+
+ addr.s6_addr[0] = 0xFE;
+ addr.s6_addr[1] = 0x80;
+
+ if (ipv6_generate_eui64(addr.s6_addr + 8, dev) == 0)
+ addrconf_add_linklocal(idev, &addr);
+#endif
+
+#ifndef CONFIG_IPV6_NO_PB
+ memset(&addr, 0, sizeof(struct in6_addr));
+
+ addr.s6_addr[0] = 0xFE;
+ addr.s6_addr[1] = 0x80;
+
+ memcpy(addr.s6_addr + (sizeof(struct in6_addr) - dev->addr_len),
+ dev->dev_addr, dev->addr_len);
+ addrconf_add_linklocal(idev, &addr);
+#endif
+}
+
+static void addrconf_sit_config(struct device *dev)
+{
+ struct inet6_dev *idev;
+
+ /*
+ * Configure the tunnel with one of our IPv4
+ * addresses... we should configure all of
+ * our v4 addrs in the tunnel
+ */
+
+ if ((idev = ipv6_find_idev(dev)) == NULL) {
+ printk(KERN_DEBUG "init sit: add_dev failed\n");
+ return;
+ }
+
+ sit_add_v4_addrs(idev);
+
+ if (dev->flags&IFF_POINTOPOINT) {
+ addrconf_add_mroute(dev);
+ addrconf_add_lroute(dev);
+ } else
+ sit_route_add(dev);
+}
+
+
+int addrconf_notify(struct notifier_block *this, unsigned long event,
+ void * data)
+{
+ struct device *dev;
+
+ dev = (struct device *) data;
+
+ switch(event) {
+ case NETDEV_UP:
+ switch(dev->type) {
+ case ARPHRD_SIT:
+ addrconf_sit_config(dev);
+ break;
+
+ case ARPHRD_LOOPBACK:
+ init_loopback(dev);
+ break;
+
+ default:
+ addrconf_dev_config(dev);
+ break;
+ };
+
+#ifdef CONFIG_IPV6_NETLINK
+ rt6_sndmsg(RTMSG_NEWDEVICE, NULL, NULL, NULL, dev, 0, 0, 0, 0);
+#endif
+ break;
+
+ case NETDEV_CHANGEMTU:
+ if (dev->mtu >= IPV6_MIN_MTU) {
+ struct inet6_dev *idev;
+
+ if ((idev = ipv6_get_idev(dev)) == NULL)
+ break;
+ idev->cnf.mtu6 = dev->mtu;
+ rt6_mtu_change(dev, dev->mtu);
+ break;
+ }
+
+ /* MTU falled under IPV6_MIN_MTU. Stop IPv6 on this interface. */
+
+ case NETDEV_DOWN:
+ case NETDEV_UNREGISTER:
+ /*
+ * Remove all addresses from this interface.
+ */
+ if (addrconf_ifdown(dev, event != NETDEV_DOWN) == 0) {
+#ifdef CONFIG_IPV6_NETLINK
+ rt6_sndmsg(RTMSG_DELDEVICE, NULL, NULL, NULL, dev, 0, 0, 0, 0);
+#endif
+ }
+
+ break;
+ case NETDEV_CHANGE:
+ break;
+ };
+
+ return NOTIFY_OK;
+}
+
+static int addrconf_ifdown(struct device *dev, int how)
+{
+ struct inet6_dev *idev, **bidev;
+ struct inet6_ifaddr *ifa, **bifa;
+ int i, hash;
+
+ rt6_ifdown(dev);
+ neigh_ifdown(&nd_tbl, dev);
+
+ idev = ipv6_get_idev(dev);
+ if (idev == NULL)
+ return -ENODEV;
+
+ start_bh_atomic();
+
+ /* Discard address list */
+
+ idev->addr_list = NULL;
+
+ /*
+ * Clean addresses hash table
+ */
+
+ for (i=0; i<16; i++) {
+ bifa = &inet6_addr_lst[i];
+
+ while ((ifa = *bifa) != NULL) {
+ if (ifa->idev == idev) {
+ *bifa = ifa->lst_next;
+ del_timer(&ifa->timer);
+ ipv6_ifa_notify(RTM_DELADDR, ifa);
+ kfree(ifa);
+ continue;
+ }
+ bifa = &ifa->lst_next;
+ }
+ }
+
+ /* Discard multicast list */
+
+ if (how == 1)
+ ipv6_mc_destroy_dev(idev);
+ else
+ ipv6_mc_down(idev);
+
+ /* Delete device from device hash table (if unregistered) */
+
+ if (how == 1) {
+ hash = ipv6_devindex_hash(dev->ifindex);
+
+ for (bidev = &inet6_dev_lst[hash]; (idev=*bidev) != NULL; bidev = &idev->next) {
+ if (idev->dev == dev) {
+ *bidev = idev->next;
+ neigh_parms_release(&nd_tbl, idev->nd_parms);
+#ifdef CONFIG_SYSCTL
+ addrconf_sysctl_unregister(&idev->cnf);
+#endif
+ kfree(idev);
+ break;
+ }
+ }
+ }
+ end_bh_atomic();
+ return 0;
+}
+
+
+static void addrconf_rs_timer(unsigned long data)
+{
+ struct inet6_ifaddr *ifp;
+
+ ifp = (struct inet6_ifaddr *) data;
+
+ if (ifp->idev->cnf.forwarding)
+ return;
+
+ if (ifp->idev->if_flags & IF_RA_RCVD) {
+ /*
+ * Announcement received after solicitation
+ * was sent
+ */
+ return;
+ }
+
+ if (ifp->probes++ <= ifp->idev->cnf.rtr_solicits) {
+ struct in6_addr all_routers;
+
+ ipv6_addr_all_routers(&all_routers);
+
+ ndisc_send_rs(ifp->idev->dev, &ifp->addr, &all_routers);
+
+ ifp->timer.function = addrconf_rs_timer;
+ ifp->timer.expires = (jiffies +
+ ifp->idev->cnf.rtr_solicit_interval);
+ add_timer(&ifp->timer);
+ } else {
+ struct in6_rtmsg rtmsg;
+
+ printk(KERN_DEBUG "%s: no IPv6 routers present\n",
+ ifp->idev->dev->name);
+
+ memset(&rtmsg, 0, sizeof(struct in6_rtmsg));
+ rtmsg.rtmsg_type = RTMSG_NEWROUTE;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+ rtmsg.rtmsg_flags = (RTF_ALLONLINK | RTF_ADDRCONF |
+ RTF_DEFAULT | RTF_UP);
+
+ rtmsg.rtmsg_ifindex = ifp->idev->dev->ifindex;
+
+ ip6_route_add(&rtmsg);
+ }
+}
+
+/*
+ * Duplicate Address Detection
+ */
+static void addrconf_dad_start(struct inet6_ifaddr *ifp)
+{
+ struct device *dev;
+ unsigned long rand_num;
+
+ dev = ifp->idev->dev;
+
+ addrconf_join_solict(dev, &ifp->addr);
+
+ if (ifp->prefix_len != 128 && (ifp->flags&ADDR_PERMANENT))
+ addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0, RTF_ADDRCONF);
+
+ if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
+ start_bh_atomic();
+ ifp->flags &= ~DAD_INCOMPLETE;
+ addrconf_dad_completed(ifp);
+ end_bh_atomic();
+ return;
+ }
+
+ net_srandom(ifp->addr.s6_addr32[3]);
+
+ ifp->probes = ifp->idev->cnf.dad_transmits;
+ ifp->flags |= DAD_INCOMPLETE;
+
+ rand_num = net_random() % ifp->idev->cnf.rtr_solicit_delay;
+
+ ifp->timer.function = addrconf_dad_timer;
+ ifp->timer.expires = jiffies + rand_num;
+
+ add_timer(&ifp->timer);
+}
+
+static void addrconf_dad_timer(unsigned long data)
+{
+ struct inet6_ifaddr *ifp;
+ struct in6_addr unspec;
+ struct in6_addr mcaddr;
+
+ ifp = (struct inet6_ifaddr *) data;
+
+ if (ifp->probes == 0) {
+ /*
+ * DAD was successful
+ */
+
+ ifp->flags &= ~DAD_INCOMPLETE;
+ addrconf_dad_completed(ifp);
+ return;
+ }
+
+ ifp->probes--;
+
+ /* send a neighbour solicitation for our addr */
+ memset(&unspec, 0, sizeof(unspec));
+#ifdef CONFIG_IPV6_EUI64
+ addrconf_addr_solict_mult_new(&ifp->addr, &mcaddr);
+ ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
+#endif
+#ifndef CONFIG_IPV6_NO_PB
+ addrconf_addr_solict_mult_old(&ifp->addr, &mcaddr);
+ ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
+#endif
+
+ ifp->timer.expires = jiffies + ifp->idev->cnf.rtr_solicit_interval;
+ add_timer(&ifp->timer);
+}
+
+static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
+{
+ struct device * dev = ifp->idev->dev;
+
+ /*
+ * Configure the address for reception. Now it is valid.
+ */
+
+ ipv6_ifa_notify(RTM_NEWADDR, ifp);
+
+ /* If added prefix is link local and forwarding is off,
+ start sending router solicitations.
+ */
+
+ if (ifp->idev->cnf.forwarding == 0 &&
+ (dev->flags&IFF_LOOPBACK) == 0 &&
+ (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) {
+ struct in6_addr all_routers;
+
+ ipv6_addr_all_routers(&all_routers);
+
+ /*
+ * If a host as already performed a random delay
+ * [...] as part of DAD [...] there is no need
+ * to delay again before sending the first RS
+ */
+ ndisc_send_rs(ifp->idev->dev, &ifp->addr, &all_routers);
+
+ ifp->probes = 1;
+ ifp->timer.function = addrconf_rs_timer;
+ ifp->timer.expires = (jiffies +
+ ifp->idev->cnf.rtr_solicit_interval);
+ ifp->idev->if_flags |= IF_RS_SENT;
+ add_timer(&ifp->timer);
+ }
+}
+
+#ifdef CONFIG_PROC_FS
+static int iface_proc_info(char *buffer, char **start, off_t offset,
+ int length, int dummy)
+{
+ struct inet6_ifaddr *ifp;
+ int i;
+ int len = 0;
+ off_t pos=0;
+ off_t begin=0;
+
+ addrconf_lock();
+
+ for (i=0; i < IN6_ADDR_HSIZE; i++) {
+ for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) {
+ int j;
+
+ for (j=0; j<16; j++) {
+ sprintf(buffer + len, "%02x",
+ ifp->addr.s6_addr[j]);
+ len += 2;
+ }
+
+ len += sprintf(buffer + len,
+ " %02x %02x %02x %02x %8s\n",
+ ifp->idev->dev->ifindex,
+ ifp->prefix_len,
+ ifp->scope,
+ ifp->flags,
+ ifp->idev->dev->name);
+ pos=begin+len;
+ if(pos<offset) {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ goto done;
+ }
+ }
+
+done:
+ addrconf_unlock();
+
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ if(len<0)
+ len=0;
+ return len;
+}
+
+struct proc_dir_entry iface_proc_entry =
+{
+ 0, 8, "if_inet6",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, NULL,
+ &iface_proc_info
+};
+#endif /* CONFIG_PROC_FS */
+
+/*
+ * Periodic address status verification
+ */
+
+void addrconf_verify(unsigned long foo)
+{
+ struct inet6_ifaddr *ifp;
+ unsigned long now = jiffies;
+ int i;
+
+ if (atomic_read(&addr_list_lock)) {
+ addr_chk_timer.expires = jiffies + 1*HZ;
+ add_timer(&addr_chk_timer);
+ return;
+ }
+
+ for (i=0; i < IN6_ADDR_HSIZE; i++) {
+ for (ifp=inet6_addr_lst[i]; ifp;) {
+ if (ifp->flags & ADDR_INVALID) {
+ struct inet6_ifaddr *bp = ifp;
+ ifp= ifp->lst_next;
+ ipv6_del_addr(bp);
+ continue;
+ }
+ if (!(ifp->flags & ADDR_PERMANENT)) {
+ struct inet6_ifaddr *bp;
+ unsigned long age;
+
+ age = (now - ifp->tstamp) / HZ;
+
+ bp = ifp;
+ ifp= ifp->lst_next;
+
+ if (age > bp->valid_lft)
+ ipv6_del_addr(bp);
+ else if (age > bp->prefered_lft) {
+ bp->flags |= ADDR_DEPRECATED;
+ ipv6_ifa_notify(0, bp);
+ }
+
+ continue;
+ }
+ ifp = ifp->lst_next;
+ }
+ }
+
+ addr_chk_timer.expires = jiffies + ADDR_CHECK_FREQUENCY;
+ add_timer(&addr_chk_timer);
+}
+
+#ifdef CONFIG_RTNETLINK
+
+static int
+inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct rtattr **rta = arg;
+ struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
+ struct in6_addr *pfx;
+
+ pfx = NULL;
+ if (rta[IFA_ADDRESS-1]) {
+ if (RTA_PAYLOAD(rta[IFA_ADDRESS-1]) < sizeof(*pfx))
+ return -EINVAL;
+ pfx = RTA_DATA(rta[IFA_ADDRESS-1]);
+ }
+ if (rta[IFA_LOCAL-1]) {
+ if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx)))
+ return -EINVAL;
+ pfx = RTA_DATA(rta[IFA_LOCAL-1]);
+ }
+ if (pfx == NULL)
+ return -EINVAL;
+
+ return inet6_addr_del(ifm->ifa_index, pfx, ifm->ifa_prefixlen);
+}
+
+static int
+inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct rtattr **rta = arg;
+ struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
+ struct in6_addr *pfx;
+
+ pfx = NULL;
+ if (rta[IFA_ADDRESS-1]) {
+ if (RTA_PAYLOAD(rta[IFA_ADDRESS-1]) < sizeof(*pfx))
+ return -EINVAL;
+ pfx = RTA_DATA(rta[IFA_ADDRESS-1]);
+ }
+ if (rta[IFA_LOCAL-1]) {
+ if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx)))
+ return -EINVAL;
+ pfx = RTA_DATA(rta[IFA_LOCAL-1]);
+ }
+ if (pfx == NULL)
+ return -EINVAL;
+
+ return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen);
+}
+
+static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
+ u32 pid, u32 seq, int event)
+{
+ struct ifaddrmsg *ifm;
+ struct nlmsghdr *nlh;
+ struct ifa_cacheinfo ci;
+ unsigned char *b = skb->tail;
+
+ nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*ifm));
+ ifm = NLMSG_DATA(nlh);
+ ifm->ifa_family = AF_INET6;
+ ifm->ifa_prefixlen = ifa->prefix_len;
+ ifm->ifa_flags = ifa->flags & ~ADDR_INVALID;
+ ifm->ifa_scope = RT_SCOPE_UNIVERSE;
+ if (ifa->scope&IFA_HOST)
+ ifm->ifa_scope = RT_SCOPE_HOST;
+ else if (ifa->scope&IFA_LINK)
+ ifm->ifa_scope = RT_SCOPE_LINK;
+ else if (ifa->scope&IFA_SITE)
+ ifm->ifa_scope = RT_SCOPE_SITE;
+ ifm->ifa_index = ifa->idev->dev->ifindex;
+ RTA_PUT(skb, IFA_ADDRESS, 16, &ifa->addr);
+ if (!(ifa->flags&IFA_F_PERMANENT)) {
+ ci.ifa_prefered = ifa->prefered_lft;
+ ci.ifa_valid = ifa->valid_lft;
+ if (ci.ifa_prefered != 0xFFFFFFFF) {
+ long tval = (jiffies - ifa->tstamp)/HZ;
+ ci.ifa_prefered -= tval;
+ if (ci.ifa_valid != 0xFFFFFFFF)
+ ci.ifa_valid -= tval;
+ }
+ RTA_PUT(skb, IFA_CACHEINFO, sizeof(ci), &ci);
+ }
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+static int inet6_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int idx, ip_idx;
+ int s_idx, s_ip_idx;
+ struct inet6_ifaddr *ifa;
+
+ s_idx = cb->args[0];
+ s_ip_idx = ip_idx = cb->args[1];
+
+ for (idx=0; idx < IN6_ADDR_HSIZE; idx++) {
+ if (idx < s_idx)
+ continue;
+ if (idx > s_idx)
+ s_ip_idx = 0;
+ start_bh_atomic();
+ for (ifa=inet6_addr_lst[idx], ip_idx = 0; ifa;
+ ifa = ifa->lst_next, ip_idx++) {
+ if (ip_idx < s_ip_idx)
+ continue;
+ if (inet6_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, RTM_NEWADDR) <= 0) {
+ end_bh_atomic();
+ goto done;
+ }
+ }
+ end_bh_atomic();
+ }
+done:
+ cb->args[0] = idx;
+ cb->args[1] = ip_idx;
+
+ return skb->len;
+}
+
+static void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa)
+{
+ struct sk_buff *skb;
+ int size = NLMSG_SPACE(sizeof(struct ifaddrmsg)+128);
+
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (!skb) {
+ netlink_set_err(rtnl, 0, RTMGRP_IPV6_IFADDR, ENOBUFS);
+ return;
+ }
+ if (inet6_fill_ifaddr(skb, ifa, 0, 0, event) < 0) {
+ kfree_skb(skb);
+ netlink_set_err(rtnl, 0, RTMGRP_IPV6_IFADDR, EINVAL);
+ return;
+ }
+ NETLINK_CB(skb).dst_groups = RTMGRP_IPV6_IFADDR;
+ netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_IFADDR, GFP_ATOMIC);
+}
+
+static struct rtnetlink_link inet6_rtnetlink_table[RTM_MAX-RTM_BASE+1] =
+{
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+
+ { inet6_rtm_newaddr, NULL, },
+ { inet6_rtm_deladdr, NULL, },
+ { NULL, inet6_dump_ifaddr, },
+ { NULL, NULL, },
+
+ { inet6_rtm_newroute, NULL, },
+ { inet6_rtm_delroute, NULL, },
+ { inet6_rtm_getroute, inet6_dump_fib, },
+ { NULL, NULL, },
+};
+#endif
+
+static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
+{
+#ifdef CONFIG_RTNETLINK
+ inet6_ifa_notify(event ? : RTM_NEWADDR, ifp);
+#endif
+ switch (event) {
+ case RTM_NEWADDR:
+ ip6_rt_addr_add(&ifp->addr, ifp->idev->dev);
+ break;
+ case RTM_DELADDR:
+ start_bh_atomic();
+ addrconf_leave_solict(ifp->idev->dev, &ifp->addr);
+ if (ipv6_chk_addr(&ifp->addr, ifp->idev->dev, 0) == NULL)
+ ip6_rt_addr_del(&ifp->addr, ifp->idev->dev);
+ end_bh_atomic();
+ break;
+ }
+}
+
+#ifdef CONFIG_SYSCTL
+
+static
+int addrconf_sysctl_forward(ctl_table *ctl, int write, struct file * filp,
+ void *buffer, size_t *lenp)
+{
+ int *valp = ctl->data;
+ int val = *valp;
+ int ret;
+
+ ret = proc_dointvec(ctl, write, filp, buffer, lenp);
+
+ if (write && *valp != val && valp != &ipv6_devconf_dflt.forwarding) {
+ struct inet6_dev *idev = NULL;
+
+ if (valp != &ipv6_devconf.forwarding) {
+ struct device *dev = dev_get_by_index(ctl->ctl_name);
+ if (dev)
+ idev = ipv6_get_idev(dev);
+ if (idev == NULL)
+ return ret;
+ } else
+ ipv6_devconf_dflt.forwarding = ipv6_devconf.forwarding;
+
+ addrconf_forward_change(idev);
+
+ if (*valp) {
+ start_bh_atomic();
+ rt6_purge_dflt_routers(0);
+ end_bh_atomic();
+ }
+ }
+
+ return ret;
+}
+
+static struct addrconf_sysctl_table
+{
+ struct ctl_table_header *sysctl_header;
+ ctl_table addrconf_vars[11];
+ ctl_table addrconf_dev[2];
+ ctl_table addrconf_conf_dir[2];
+ ctl_table addrconf_proto_dir[2];
+ ctl_table addrconf_root_dir[2];
+} addrconf_sysctl = {
+ NULL,
+ {{NET_IPV6_FORWARDING, "forwarding",
+ &ipv6_devconf.forwarding, sizeof(int), 0644, NULL,
+ &addrconf_sysctl_forward},
+
+ {NET_IPV6_HOP_LIMIT, "hop_limit",
+ &ipv6_devconf.hop_limit, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_MTU, "mtu",
+ &ipv6_devconf.mtu6, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_ACCEPT_RA, "accept_ra",
+ &ipv6_devconf.accept_ra, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_ACCEPT_REDIRECTS, "accept_redirects",
+ &ipv6_devconf.accept_redirects, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_AUTOCONF, "autoconf",
+ &ipv6_devconf.autoconf, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_DAD_TRANSMITS, "dad_transmits",
+ &ipv6_devconf.dad_transmits, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_RTR_SOLICITS, "router_solicitations",
+ &ipv6_devconf.rtr_solicits, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_RTR_SOLICIT_INTERVAL, "router_solicitation_interval",
+ &ipv6_devconf.rtr_solicit_interval, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies},
+
+ {NET_IPV6_RTR_SOLICIT_DELAY, "router_solicitation_delay",
+ &ipv6_devconf.rtr_solicit_delay, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies},
+
+ {0}},
+
+ {{NET_PROTO_CONF_ALL, "all", NULL, 0, 0555, addrconf_sysctl.addrconf_vars},{0}},
+ {{NET_IPV6_CONF, "conf", NULL, 0, 0555, addrconf_sysctl.addrconf_dev},{0}},
+ {{NET_IPV6, "ipv6", NULL, 0, 0555, addrconf_sysctl.addrconf_conf_dir},{0}},
+ {{CTL_NET, "net", NULL, 0, 0555, addrconf_sysctl.addrconf_proto_dir},{0}}
+};
+
+static void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf *p)
+{
+ int i;
+ struct device *dev = idev ? idev->dev : NULL;
+ struct addrconf_sysctl_table *t;
+
+ t = kmalloc(sizeof(*t), GFP_KERNEL);
+ if (t == NULL)
+ return;
+ memcpy(t, &addrconf_sysctl, sizeof(*t));
+ for (i=0; i<sizeof(t->addrconf_vars)/sizeof(t->addrconf_vars[0])-1; i++) {
+ t->addrconf_vars[i].data += (char*)p - (char*)&ipv6_devconf;
+ t->addrconf_vars[i].de = NULL;
+ }
+ if (dev) {
+ t->addrconf_dev[0].procname = dev->name;
+ t->addrconf_dev[0].ctl_name = dev->ifindex;
+ } else {
+ t->addrconf_dev[0].procname = "default";
+ t->addrconf_dev[0].ctl_name = NET_PROTO_CONF_DEFAULT;
+ }
+ t->addrconf_dev[0].child = t->addrconf_vars;
+ t->addrconf_dev[0].de = NULL;
+ t->addrconf_conf_dir[0].child = t->addrconf_dev;
+ t->addrconf_conf_dir[0].de = NULL;
+ t->addrconf_proto_dir[0].child = t->addrconf_conf_dir;
+ t->addrconf_proto_dir[0].de = NULL;
+ t->addrconf_root_dir[0].child = t->addrconf_proto_dir;
+ t->addrconf_root_dir[0].de = NULL;
+
+ t->sysctl_header = register_sysctl_table(t->addrconf_root_dir, 0);
+ if (t->sysctl_header == NULL)
+ kfree(t);
+ else
+ p->sysctl = t;
+}
+
+static void addrconf_sysctl_unregister(struct ipv6_devconf *p)
+{
+ if (p->sysctl) {
+ struct addrconf_sysctl_table *t = p->sysctl;
+ p->sysctl = NULL;
+ unregister_sysctl_table(t->sysctl_header);
+ kfree(t);
+ }
+}
+
+
+#endif
+
+/*
+ * Init / cleanup code
+ */
+
+__initfunc(void addrconf_init(void))
+{
+#ifdef MODULE
+ struct device *dev;
+
+ /* This takes sense only during module load. */
+
+ for (dev = dev_base; dev; dev = dev->next) {
+ if (!(dev->flags&IFF_UP))
+ continue;
+
+ switch (dev->type) {
+ case ARPHRD_LOOPBACK:
+ init_loopback(dev);
+ break;
+ case ARPHRD_ETHER:
+ addrconf_dev_config(dev);
+ break;
+ default:
+ /* Ignore all other */
+ }
+ }
+#endif
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&iface_proc_entry);
+#endif
+
+ addr_chk_timer.expires = jiffies + ADDR_CHECK_FREQUENCY;
+ add_timer(&addr_chk_timer);
+#ifdef CONFIG_RTNETLINK
+ rtnetlink_links[PF_INET6] = inet6_rtnetlink_table;
+#endif
+#ifdef CONFIG_SYSCTL
+ addrconf_sysctl.sysctl_header =
+ register_sysctl_table(addrconf_sysctl.addrconf_root_dir, 0);
+ addrconf_sysctl_register(NULL, &ipv6_devconf_dflt);
+#endif
+}
+
+#ifdef MODULE
+void addrconf_cleanup(void)
+{
+ struct inet6_dev *idev;
+ struct inet6_ifaddr *ifa;
+ int i;
+
+#ifdef CONFIG_RTNETLINK
+ rtnetlink_links[PF_INET6] = NULL;
+#endif
+#ifdef CONFIG_SYSCTL
+ addrconf_sysctl_unregister(&ipv6_devconf_dflt);
+ addrconf_sysctl_unregister(&ipv6_devconf);
+#endif
+
+ del_timer(&addr_chk_timer);
+
+ /*
+ * clean dev list.
+ */
+
+ for (i=0; i < IN6_ADDR_HSIZE; i++) {
+ struct inet6_dev *next;
+ for (idev = inet6_dev_lst[i]; idev; idev = next) {
+ next = idev->next;
+ addrconf_ifdown(idev->dev, 1);
+ }
+ }
+
+ start_bh_atomic();
+ /*
+ * clean addr_list
+ */
+
+ for (i=0; i < IN6_ADDR_HSIZE; i++) {
+ for (ifa=inet6_addr_lst[i]; ifa; ) {
+ struct inet6_ifaddr *bifa;
+
+ bifa = ifa;
+ ifa = ifa->lst_next;
+ printk(KERN_DEBUG "bug: IPv6 address leakage detected: ifa=%p\n", bifa);
+ /* Do not free it; something is wrong.
+ Now we can investigate it with debugger.
+ */
+ }
+ }
+ end_bh_atomic();
+
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(iface_proc_entry.low_ino);
+#endif
+}
+#endif /* MODULE */
diff --git a/pfinet/linux-src/net/ipv6/af_inet6.c b/pfinet/linux-src/net/ipv6/af_inet6.c
new file mode 100644
index 00000000..f10ff8d7
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/af_inet6.c
@@ -0,0 +1,646 @@
+/*
+ * PF_INET6 socket protocol family
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * Adapted from linux/net/ipv4/af_inet.c
+ *
+ * $Id: af_inet6.c,v 1.3 2007/10/13 01:43:00 stesie Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/version.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/icmpv6.h>
+
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/udp.h>
+#include <net/tcp.h>
+#include <net/ipip.h>
+#include <net/protocol.h>
+#include <net/inet_common.h>
+#include <net/transp_v6.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#ifdef MODULE
+static int unloadable = 0; /* XX: Turn to one when all is ok within the
+ module for allowing unload */
+#endif
+
+#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115
+MODULE_AUTHOR("Cast of dozens");
+MODULE_DESCRIPTION("IPv6 protocol stack for Linux");
+MODULE_PARM(unloadable, "i");
+#endif
+
+extern struct proto_ops inet6_stream_ops;
+extern struct proto_ops inet6_dgram_ops;
+
+/* IPv6 procfs goodies... */
+
+#ifdef CONFIG_PROC_FS
+extern int raw6_get_info(char *, char **, off_t, int, int);
+extern int tcp6_get_info(char *, char **, off_t, int, int);
+extern int udp6_get_info(char *, char **, off_t, int, int);
+extern int afinet6_get_info(char *, char **, off_t, int, int);
+extern int afinet6_get_snmp(char *, char **, off_t, int, int);
+#endif
+
+#ifdef CONFIG_SYSCTL
+extern void ipv6_sysctl_register(void);
+extern void ipv6_sysctl_unregister(void);
+#endif
+
+int sysctl_ipv6_bindv6only;
+
+static int inet6_create(struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct proto *prot;
+
+ sk = sk_alloc(PF_INET6, GFP_KERNEL, 1);
+ if (sk == NULL)
+ goto do_oom;
+
+ if(sock->type == SOCK_STREAM || sock->type == SOCK_SEQPACKET) {
+ if (protocol && protocol != IPPROTO_TCP)
+ goto free_and_noproto;
+ protocol = IPPROTO_TCP;
+ prot = &tcpv6_prot;
+ sock->ops = &inet6_stream_ops;
+ } else if(sock->type == SOCK_DGRAM) {
+ if (protocol && protocol != IPPROTO_UDP)
+ goto free_and_noproto;
+ protocol = IPPROTO_UDP;
+ sk->no_check = UDP_NO_CHECK;
+ prot=&udpv6_prot;
+ sock->ops = &inet6_dgram_ops;
+ } else if(sock->type == SOCK_RAW) {
+ if (!capable(CAP_NET_RAW))
+ goto free_and_badperm;
+ if (!protocol)
+ goto free_and_noproto;
+ prot = &rawv6_prot;
+ sock->ops = &inet6_dgram_ops;
+ sk->reuse = 1;
+ sk->num = protocol;
+ } else {
+ goto free_and_badtype;
+ }
+
+ sock_init_data(sock, sk);
+
+ sk->destruct = NULL;
+ sk->zapped = 0;
+ sk->family = PF_INET6;
+ sk->protocol = protocol;
+
+ sk->prot = prot;
+ sk->backlog_rcv = prot->backlog_rcv;
+
+ sk->timer.data = (unsigned long)sk;
+ sk->timer.function = &net_timer;
+
+ sk->net_pinfo.af_inet6.hop_limit = -1;
+ sk->net_pinfo.af_inet6.mcast_hops = -1;
+ sk->net_pinfo.af_inet6.mc_loop = 1;
+ sk->net_pinfo.af_inet6.pmtudisc = IPV6_PMTUDISC_WANT;
+
+ sk->net_pinfo.af_inet6.ipv6only = sysctl_ipv6_bindv6only;
+
+ /* Init the ipv4 part of the socket since we can have sockets
+ * using v6 API for ipv4.
+ */
+ sk->ip_ttl = 64;
+
+ sk->ip_mc_loop = 1;
+ sk->ip_mc_ttl = 1;
+ sk->ip_mc_index = 0;
+ sk->ip_mc_list = NULL;
+
+ if (sk->type==SOCK_RAW && protocol==IPPROTO_RAW)
+ sk->ip_hdrincl=1;
+
+ if (sk->num) {
+ /* It assumes that any protocol which allows
+ * the user to assign a number at socket
+ * creation time automatically shares.
+ */
+ sk->sport = ntohs(sk->num);
+ sk->prot->hash(sk);
+ add_to_prot_sklist(sk);
+ }
+
+ if (sk->prot->init) {
+ int err = sk->prot->init(sk);
+ if (err != 0) {
+ destroy_sock(sk);
+ return(err);
+ }
+ }
+ MOD_INC_USE_COUNT;
+ return(0);
+
+free_and_badtype:
+ sk_free(sk);
+ return -ESOCKTNOSUPPORT;
+free_and_badperm:
+ sk_free(sk);
+ return -EPERM;
+free_and_noproto:
+ sk_free(sk);
+ return -EPROTONOSUPPORT;
+do_oom:
+ return -ENOBUFS;
+}
+
+
+/* bind for INET6 API */
+static int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ struct sockaddr_in6 *addr=(struct sockaddr_in6 *)uaddr;
+ struct sock *sk = sock->sk;
+ __u32 v4addr = 0;
+ unsigned short snum;
+ int addr_type = 0;
+
+ /* If the socket has its own bind function then use it. */
+ if(sk->prot->bind)
+ return sk->prot->bind(sk, uaddr, addr_len);
+
+ /* Check these errors (active socket, bad address length, double bind). */
+ if ((sk->state != TCP_CLOSE) ||
+ (addr_len < sizeof(struct sockaddr_in6)) ||
+ (sk->num != 0))
+ return -EINVAL;
+
+ addr_type = ipv6_addr_type(&addr->sin6_addr);
+ if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM)
+ return(-EINVAL);
+
+ /* Check if the address belongs to the host. */
+ if (addr_type == IPV6_ADDR_MAPPED) {
+ v4addr = addr->sin6_addr.s6_addr32[3];
+ if (inet_addr_type(v4addr) != RTN_LOCAL)
+ return(-EADDRNOTAVAIL);
+ } else {
+ if (addr_type != IPV6_ADDR_ANY) {
+ struct net_device *dev = NULL;
+
+ if (addr_type & IPV6_ADDR_LINKLOCAL) {
+ if (addr_len >= sizeof(struct sockaddr_in6) &&
+ addr->sin6_scope_id) {
+ /* Override any existing binding,
+ if another one is supplied
+ by user. */
+ sk->bound_dev_if = addr->sin6_scope_id;
+ }
+
+ /* Binding to link-local address requires
+ an interface */
+ if (!sk->bound_dev_if)
+ return(-EINVAL);
+ dev = dev_get_by_index(sk->bound_dev_if);
+ if (!dev)
+ return(-ENODEV);
+ }
+
+ /* ipv4 addr of the socket is invalid. Only the
+ * unpecified and mapped address have a v4 equivalent.
+ */
+ v4addr = LOOPBACK4_IPV6;
+ if (!(addr_type & IPV6_ADDR_MULTICAST)) {
+ if (ipv6_chk_addr(&addr->sin6_addr, NULL, 0) == NULL)
+ return(-EADDRNOTAVAIL);
+ }
+ }
+ }
+
+ sk->rcv_saddr = v4addr;
+ sk->saddr = v4addr;
+
+ memcpy(&sk->net_pinfo.af_inet6.rcv_saddr, &addr->sin6_addr,
+ sizeof(struct in6_addr));
+
+ if (!(addr_type & IPV6_ADDR_MULTICAST))
+ memcpy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr,
+ sizeof(struct in6_addr));
+
+ snum = ntohs(addr->sin6_port);
+ if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
+ return(-EACCES);
+
+ /* Make sure we are allowed to bind here. */
+ if(sk->prot->get_port(sk, snum) != 0)
+ return -EADDRINUSE;
+
+ sk->sport = ntohs(sk->num);
+ sk->dport = 0;
+ sk->daddr = 0;
+ sk->prot->hash(sk);
+ add_to_prot_sklist(sk);
+
+ return(0);
+}
+
+static int inet6_release(struct socket *sock, struct socket *peer)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk == NULL)
+ return -EINVAL;
+
+ /* Free mc lists */
+ ipv6_sock_mc_close(sk);
+
+ /* Huh! MOD_DEC_USE_COUNT was here :-(
+ It is impossible by two reasons: socket destroy
+ may be delayed and inet_release may sleep and
+ return to nowhere then. It should be moved to
+ inet6_destroy_sock(), but we have no explicit constructor :-(
+ --ANK (980802)
+ */
+ MOD_DEC_USE_COUNT;
+ return inet_release(sock, peer);
+}
+
+int inet6_destroy_sock(struct sock *sk)
+{
+ struct sk_buff *skb;
+ struct ipv6_txoptions *opt;
+
+ /*
+ * Release destination entry
+ */
+
+ dst_release(xchg(&sk->dst_cache,NULL));
+
+ /* Release rx options */
+
+ if ((skb = xchg(&sk->net_pinfo.af_inet6.pktoptions, NULL)) != NULL)
+ kfree_skb(skb);
+
+ /* Free flowlabels */
+ fl6_free_socklist(sk);
+
+ /* Free tx options */
+
+ if ((opt = xchg(&sk->net_pinfo.af_inet6.opt, NULL)) != NULL)
+ sock_kfree_s(sk, opt, opt->tot_len);
+
+ return 0;
+}
+
+/*
+ * This does both peername and sockname.
+ */
+
+static int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
+ int *uaddr_len, int peer)
+{
+ struct sockaddr_in6 *sin=(struct sockaddr_in6 *)uaddr;
+ struct sock *sk;
+
+ sin->sin6_family = AF_INET6;
+ sin->sin6_flowinfo = 0;
+ sin->sin6_scope_id = 0;
+
+ sk = sock->sk;
+ if (peer) {
+ if (!tcp_connected(sk->state))
+ return(-ENOTCONN);
+ sin->sin6_port = sk->dport;
+ memcpy(&sin->sin6_addr, &sk->net_pinfo.af_inet6.daddr,
+ sizeof(struct in6_addr));
+ if (sk->net_pinfo.af_inet6.sndflow)
+ sin->sin6_flowinfo = sk->net_pinfo.af_inet6.flow_label;
+ } else {
+ if (ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr) == IPV6_ADDR_ANY)
+ memcpy(&sin->sin6_addr,
+ &sk->net_pinfo.af_inet6.saddr,
+ sizeof(struct in6_addr));
+ else
+ memcpy(&sin->sin6_addr,
+ &sk->net_pinfo.af_inet6.rcv_saddr,
+ sizeof(struct in6_addr));
+
+ sin->sin6_port = sk->sport;
+ }
+ if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL)
+ sin->sin6_scope_id = sk->bound_dev_if;
+ *uaddr_len = sizeof(*sin);
+ return(0);
+}
+
+#ifdef _HURD_
+#define inet6_ioctl 0
+#else
+
+static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk = sock->sk;
+ int err = -EINVAL;
+ int pid;
+
+ switch(cmd)
+ {
+ case FIOSETOWN:
+ case SIOCSPGRP:
+ err = get_user(pid, (int *) arg);
+ if(err)
+ return err;
+
+ /* see sock_no_fcntl */
+ if (current->pid != pid && current->pgrp != -pid &&
+ !capable(CAP_NET_ADMIN))
+ return -EPERM;
+ sk->proc = pid;
+ return(0);
+ case FIOGETOWN:
+ case SIOCGPGRP:
+ err = put_user(sk->proc,(int *)arg);
+ if(err)
+ return err;
+ return(0);
+ case SIOCGSTAMP:
+ if(sk->stamp.tv_sec==0)
+ return -ENOENT;
+ err = copy_to_user((void *)arg, &sk->stamp,
+ sizeof(struct timeval));
+ if (err)
+ return -EFAULT;
+ return 0;
+
+ case SIOCADDRT:
+ case SIOCDELRT:
+
+ return(ipv6_route_ioctl(cmd,(void *)arg));
+
+ case SIOCSIFADDR:
+ return addrconf_add_ifaddr((void *) arg);
+ case SIOCDIFADDR:
+ return addrconf_del_ifaddr((void *) arg);
+ case SIOCSIFDSTADDR:
+ return addrconf_set_dstaddr((void *) arg);
+ default:
+ if ((cmd >= SIOCDEVPRIVATE) &&
+ (cmd <= (SIOCDEVPRIVATE + 15)))
+ return(dev_ioctl(cmd,(void *) arg));
+
+ if(sk->prot->ioctl==0 || (err=sk->prot->ioctl(sk, cmd, arg))==-ENOIOCTLCMD)
+ return(dev_ioctl(cmd,(void *) arg));
+ return err;
+ }
+ /*NOTREACHED*/
+ return(0);
+}
+
+#endif /* not _HURD_ */
+
+struct proto_ops inet6_stream_ops = {
+ PF_INET6,
+
+ sock_no_dup,
+ inet6_release,
+ inet6_bind,
+ inet_stream_connect, /* ok */
+ sock_no_socketpair, /* a do nothing */
+ inet_accept, /* ok */
+ inet6_getname,
+ inet_poll, /* ok */
+ inet6_ioctl, /* must change */
+ inet_listen, /* ok */
+ inet_shutdown, /* ok */
+ inet_setsockopt, /* ok */
+ inet_getsockopt, /* ok */
+ sock_no_fcntl, /* ok */
+ inet_sendmsg, /* ok */
+ inet_recvmsg /* ok */
+};
+
+struct proto_ops inet6_dgram_ops = {
+ PF_INET6,
+
+ sock_no_dup,
+ inet6_release,
+ inet6_bind,
+ inet_dgram_connect, /* ok */
+ sock_no_socketpair, /* a do nothing */
+ inet_accept, /* ok */
+ inet6_getname,
+ datagram_poll, /* ok */
+ inet6_ioctl, /* must change */
+ sock_no_listen, /* ok */
+ inet_shutdown, /* ok */
+ inet_setsockopt, /* ok */
+ inet_getsockopt, /* ok */
+ sock_no_fcntl, /* ok */
+ inet_sendmsg, /* ok */
+ inet_recvmsg /* ok */
+};
+
+struct net_proto_family inet6_family_ops = {
+ PF_INET6,
+ inet6_create
+};
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_raw6 = {
+ PROC_NET_RAW6, 4, "raw6",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ raw6_get_info
+};
+static struct proc_dir_entry proc_net_tcp6 = {
+ PROC_NET_TCP6, 4, "tcp6",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ tcp6_get_info
+};
+static struct proc_dir_entry proc_net_udp6 = {
+ PROC_NET_RAW6, 4, "udp6",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ udp6_get_info
+};
+static struct proc_dir_entry proc_net_sockstat6 = {
+ PROC_NET_SOCKSTAT6, 9, "sockstat6",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ afinet6_get_info
+};
+static struct proc_dir_entry proc_net_snmp6 = {
+ PROC_NET_SNMP6, 5, "snmp6",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ afinet6_get_snmp
+};
+#endif /* CONFIG_PROC_FS */
+
+#ifdef MODULE
+int ipv6_unload(void)
+{
+ if (!unloadable) return 1;
+ /* We keep internally 3 raw sockets */
+ return atomic_read(&(__this_module.uc.usecount)) - 3;
+}
+#endif
+
+#if defined(MODULE) && defined(CONFIG_SYSCTL)
+extern void ipv6_sysctl_register(void);
+extern void ipv6_sysctl_unregister(void);
+#endif
+
+#ifdef MODULE
+int init_module(void)
+#else
+__initfunc(void inet6_proto_init(struct net_proto *pro))
+#endif
+{
+ struct sk_buff *dummy_skb;
+ int err;
+
+#ifdef MODULE
+ if (!mod_member_present(&__this_module, can_unload))
+ return -EINVAL;
+
+ __this_module.can_unload = &ipv6_unload;
+#endif
+
+ printk(KERN_INFO "IPv6 v0.8 for NET4.0\n");
+
+ if (sizeof(struct inet6_skb_parm) > sizeof(dummy_skb->cb))
+ {
+ printk(KERN_CRIT "inet6_proto_init: size fault\n");
+#ifdef MODULE
+ return -EINVAL;
+#else
+ return;
+#endif
+ }
+
+ /*
+ * ipngwg API draft makes clear that the correct semantics
+ * for TCP and UDP is to consider one TCP and UDP instance
+ * in a host available by both INET and INET6 APIs and
+ * able to communicate via both network protocols.
+ */
+
+#if defined(MODULE) && defined(CONFIG_SYSCTL)
+ ipv6_sysctl_register();
+#endif
+ err = icmpv6_init(&inet6_family_ops);
+ if (err)
+ goto icmp_fail;
+ err = ndisc_init(&inet6_family_ops);
+ if (err)
+ goto ndisc_fail;
+ err = igmp6_init(&inet6_family_ops);
+ if (err)
+ goto igmp_fail;
+ ipv6_netdev_notif_init();
+ ipv6_packet_init();
+ ip6_route_init();
+ ip6_flowlabel_init();
+ addrconf_init();
+#ifndef _HURD_
+ sit_init();
+#endif
+
+ /* Init v6 transport protocols. */
+ udpv6_init();
+ tcpv6_init();
+
+ /* Create /proc/foo6 entries. */
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_net_raw6);
+ proc_net_register(&proc_net_tcp6);
+ proc_net_register(&proc_net_udp6);
+ proc_net_register(&proc_net_sockstat6);
+ proc_net_register(&proc_net_snmp6);
+#endif
+
+ /* Now the userspace is allowed to create INET6 sockets. */
+ (void) sock_register(&inet6_family_ops);
+
+#ifdef MODULE
+ return 0;
+#else
+ return;
+#endif
+
+igmp_fail:
+ ndisc_cleanup();
+ndisc_fail:
+ icmpv6_cleanup();
+icmp_fail:
+#if defined(MODULE) && defined(CONFIG_SYSCTL)
+ ipv6_sysctl_unregister();
+#endif
+#ifdef MODULE
+ return err;
+#else
+ return;
+#endif
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ /* First of all disallow new sockets creation. */
+ sock_unregister(PF_INET6);
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(proc_net_raw6.low_ino);
+ proc_net_unregister(proc_net_tcp6.low_ino);
+ proc_net_unregister(proc_net_udp6.low_ino);
+ proc_net_unregister(proc_net_sockstat6.low_ino);
+ proc_net_unregister(proc_net_snmp6.low_ino);
+#endif
+ /* Cleanup code parts. */
+ sit_cleanup();
+ ipv6_netdev_notif_cleanup();
+ ip6_flowlabel_cleanup();
+ addrconf_cleanup();
+ ip6_route_cleanup();
+ ipv6_packet_cleanup();
+ igmp6_cleanup();
+ ndisc_cleanup();
+ icmpv6_cleanup();
+#ifdef CONFIG_SYSCTL
+ ipv6_sysctl_unregister();
+#endif
+}
+#endif /* MODULE */
diff --git a/pfinet/linux-src/net/ipv6/datagram_ipv6.c b/pfinet/linux-src/net/ipv6/datagram_ipv6.c
new file mode 100644
index 00000000..1ed33de8
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/datagram_ipv6.c
@@ -0,0 +1,434 @@
+/*
+ * common UDP/RAW code
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: datagram_ipv6.c,v 1.2 2007/10/13 01:43:00 stesie Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in6.h>
+#include <linux/ipv6.h>
+#include <linux/route.h>
+
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+#include <net/addrconf.h>
+#include <net/transp_v6.h>
+
+#include <linux/errqueue.h>
+#include <asm/uaccess.h>
+
+void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
+ u16 port, u32 info, u8 *payload)
+{
+ struct icmp6hdr *icmph = (struct icmp6hdr *)skb->h.raw;
+ struct sock_exterr_skb *serr;
+
+ if (!sk->net_pinfo.af_inet6.recverr)
+ return;
+
+ skb = skb_clone(skb, GFP_ATOMIC);
+ if (!skb)
+ return;
+
+ serr = SKB_EXT_ERR(skb);
+ serr->ee.ee_errno = err;
+ serr->ee.ee_origin = SO_EE_ORIGIN_ICMP6;
+ serr->ee.ee_type = icmph->icmp6_type;
+ serr->ee.ee_code = icmph->icmp6_code;
+ serr->ee.ee_pad = 0;
+ serr->ee.ee_info = info;
+ serr->ee.ee_data = 0;
+ serr->addr_offset = (u8*)&(((struct ipv6hdr*)(icmph+1))->daddr) - skb->nh.raw;
+ serr->port = port;
+
+ skb->h.raw = payload;
+ skb_pull(skb, payload - skb->data);
+
+ if (sock_queue_err_skb(sk, skb))
+ kfree_skb(skb);
+}
+
+void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info)
+{
+ struct sock_exterr_skb *serr;
+ struct ipv6hdr *iph;
+ struct sk_buff *skb;
+
+ if (!sk->net_pinfo.af_inet6.recverr)
+ return;
+
+ skb = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC);
+ if (!skb)
+ return;
+
+ iph = (struct ipv6hdr*)skb_put(skb, sizeof(struct ipv6hdr));
+ skb->nh.ipv6h = iph;
+ memcpy(&iph->daddr, fl->fl6_dst, 16);
+
+ serr = SKB_EXT_ERR(skb);
+ serr->ee.ee_errno = err;
+ serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL;
+ serr->ee.ee_type = 0;
+ serr->ee.ee_code = 0;
+ serr->ee.ee_pad = 0;
+ serr->ee.ee_info = info;
+ serr->ee.ee_data = 0;
+ serr->addr_offset = (u8*)&iph->daddr - skb->nh.raw;
+ serr->port = fl->uli_u.ports.dport;
+
+ skb->h.raw = skb->tail;
+ skb_pull(skb, skb->tail - skb->data);
+
+ if (sock_queue_err_skb(sk, skb))
+ kfree_skb(skb);
+}
+
+/*
+ * Handle MSG_ERRQUEUE
+ */
+int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len)
+{
+ struct sock_exterr_skb *serr;
+ struct sk_buff *skb, *skb2;
+ struct sockaddr_in6 *sin;
+ struct {
+ struct sock_extended_err ee;
+ struct sockaddr_in6 offender;
+ } errhdr;
+ int err;
+ int copied;
+
+ err = -EAGAIN;
+ skb = skb_dequeue(&sk->error_queue);
+ if (skb == NULL)
+ goto out;
+
+ copied = skb->len;
+ if (copied > len) {
+ msg->msg_flags |= MSG_TRUNC;
+ copied = len;
+ }
+ err = memcpy_toiovec(msg->msg_iov, skb->data, copied);
+ if (err)
+ goto out_free_skb;
+
+ serr = SKB_EXT_ERR(skb);
+
+ sin = (struct sockaddr_in6 *)msg->msg_name;
+ if (sin) {
+ sin->sin6_family = AF_INET6;
+ sin->sin6_flowinfo = 0;
+ sin->sin6_port = serr->port;
+ sin->sin6_scope_id = 0;
+ if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) {
+ memcpy(&sin->sin6_addr, skb->nh.raw + serr->addr_offset, 16);
+ if (sk->net_pinfo.af_inet6.sndflow)
+ sin->sin6_flowinfo = *(u32*)(skb->nh.raw + serr->addr_offset - 24) & IPV6_FLOWINFO_MASK;
+ if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL)
+ sin->sin6_scope_id =
+ ((struct inet6_skb_parm *) skb->cb)->iif;
+ } else
+ ipv6_addr_set(&sin->sin6_addr, 0, 0,
+ __constant_htonl(0xffff),
+ *(u32*)(skb->nh.raw + serr->addr_offset));
+ }
+
+ memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
+ sin = &errhdr.offender;
+ sin->sin6_family = AF_UNSPEC;
+ if (serr->ee.ee_origin != SO_EE_ORIGIN_LOCAL) {
+ sin->sin6_family = AF_INET6;
+ sin->sin6_flowinfo = 0;
+ sin->sin6_scope_id = 0;
+ if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) {
+ memcpy(&sin->sin6_addr, &skb->nh.ipv6h->saddr, 16);
+ if (sk->net_pinfo.af_inet6.rxopt.all)
+ datagram_recv_ctl(sk, msg, skb);
+ if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL)
+ sin->sin6_scope_id =
+ ((struct inet6_skb_parm *) skb->cb)->iif;
+ } else {
+ ipv6_addr_set(&sin->sin6_addr, 0, 0,
+ __constant_htonl(0xffff),
+ skb->nh.iph->saddr);
+ if (sk->ip_cmsg_flags)
+ ip_cmsg_recv(msg, skb);
+ }
+ }
+
+ put_cmsg(msg, SOL_IPV6, IPV6_RECVERR, sizeof(errhdr), &errhdr);
+
+ /* Now we could try to dump offended packet options */
+
+ msg->msg_flags |= MSG_ERRQUEUE;
+ err = copied;
+
+ /* Reset and regenerate socket error */
+ sk->err = 0;
+ if ((skb2 = skb_peek(&sk->error_queue)) != NULL) {
+ sk->err = SKB_EXT_ERR(skb2)->ee.ee_errno;
+ sk->error_report(sk);
+ }
+
+out_free_skb:
+ kfree_skb(skb);
+out:
+ return err;
+}
+
+
+
+int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb;
+
+ if (np->rxopt.bits.rxinfo) {
+ struct in6_pktinfo src_info;
+
+ src_info.ipi6_ifindex = opt->iif;
+ ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr);
+ put_cmsg(msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
+ }
+
+ if (np->rxopt.bits.rxhlim) {
+ int hlim = skb->nh.ipv6h->hop_limit;
+ put_cmsg(msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
+ }
+
+ if (np->rxopt.bits.rxflow && (*(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK)) {
+ u32 flowinfo = *(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK;
+ put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
+ }
+ if (np->rxopt.bits.hopopts && opt->hop) {
+ u8 *ptr = skb->nh.raw + opt->hop;
+ put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr);
+ }
+ if (np->rxopt.bits.dstopts && opt->dst0) {
+ u8 *ptr = skb->nh.raw + opt->dst0;
+ put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr);
+ }
+ if (np->rxopt.bits.srcrt && opt->srcrt) {
+ struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt);
+ put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, (rthdr->hdrlen+1) << 3, rthdr);
+ }
+ if (np->rxopt.bits.authhdr && opt->auth) {
+ u8 *ptr = skb->nh.raw + opt->auth;
+ put_cmsg(msg, SOL_IPV6, IPV6_AUTHHDR, (ptr[1]+1)<<2, ptr);
+ }
+ if (np->rxopt.bits.dstopts && opt->dst1) {
+ u8 *ptr = skb->nh.raw + opt->dst1;
+ put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr);
+ }
+ return 0;
+}
+
+int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
+ struct ipv6_txoptions *opt,
+ int *hlimit)
+{
+ struct in6_pktinfo *src_info;
+ struct cmsghdr *cmsg;
+ struct ipv6_rt_hdr *rthdr;
+ struct ipv6_opt_hdr *hdr;
+ int len;
+ int err = 0;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+
+ if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
+ (unsigned long)(((char*)cmsg - (char*)msg->msg_control)
+ + cmsg->cmsg_len) > msg->msg_controllen) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+
+ if (cmsg->cmsg_level != SOL_IPV6)
+ continue;
+
+ switch (cmsg->cmsg_type) {
+ case IPV6_PKTINFO:
+ if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+
+ src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+
+ if (src_info->ipi6_ifindex) {
+ if (fl->oif && src_info->ipi6_ifindex != fl->oif)
+ return -EINVAL;
+ fl->oif = src_info->ipi6_ifindex;
+ }
+
+ if (!ipv6_addr_any(&src_info->ipi6_addr)) {
+ struct inet6_ifaddr *ifp;
+
+ ifp = ipv6_chk_addr(&src_info->ipi6_addr, NULL, 0);
+
+ if (ifp == NULL) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+
+ fl->fl6_src = &src_info->ipi6_addr;
+ }
+
+ break;
+
+ case IPV6_FLOWINFO:
+ if (cmsg->cmsg_len < CMSG_LEN(4)) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+
+ if (fl->fl6_flowlabel&IPV6_FLOWINFO_MASK) {
+ if ((fl->fl6_flowlabel^*(u32 *)CMSG_DATA(cmsg))&~IPV6_FLOWINFO_MASK) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+ }
+ fl->fl6_flowlabel = IPV6_FLOWINFO_MASK & *(u32 *)CMSG_DATA(cmsg);
+ break;
+
+ case IPV6_HOPOPTS:
+ if (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+
+ hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
+ len = ((hdr->hdrlen + 1) << 3);
+ if (cmsg->cmsg_len < CMSG_LEN(len)) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+ if (!capable(CAP_NET_RAW)) {
+ err = -EPERM;
+ goto exit_f;
+ }
+ opt->opt_nflen += len;
+ opt->hopopt = hdr;
+ break;
+
+ case IPV6_DSTOPTS:
+ if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+
+ hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
+ len = ((hdr->hdrlen + 1) << 3);
+ if (cmsg->cmsg_len < CMSG_LEN(len)) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+ if (!capable(CAP_NET_RAW)) {
+ err = -EPERM;
+ goto exit_f;
+ }
+ if (opt->dst1opt) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+ opt->opt_flen += len;
+ opt->dst1opt = hdr;
+ break;
+
+ case IPV6_AUTHHDR:
+ if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+
+ hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
+ len = ((hdr->hdrlen + 2) << 2);
+ if (cmsg->cmsg_len < CMSG_LEN(len)) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+ if (len & ~7) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+ opt->opt_flen += len;
+ opt->auth = hdr;
+ break;
+
+ case IPV6_RTHDR:
+ if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+
+ rthdr = (struct ipv6_rt_hdr *)CMSG_DATA(cmsg);
+
+ /*
+ * TYPE 0
+ */
+ if (rthdr->type) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+
+ len = ((rthdr->hdrlen + 1) << 3);
+
+ if (cmsg->cmsg_len < CMSG_LEN(len)) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+
+ /* segments left must also match */
+ if ((rthdr->hdrlen >> 1) != rthdr->segments_left) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+
+ opt->opt_nflen += len;
+ opt->srcrt = rthdr;
+
+ if (opt->dst1opt) {
+ int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3);
+
+ opt->opt_nflen += dsthdrlen;
+ opt->dst0opt = opt->dst1opt;
+ opt->dst1opt = NULL;
+ opt->opt_flen -= dsthdrlen;
+ }
+
+ break;
+
+ case IPV6_HOPLIMIT:
+ if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+
+ *hlimit = *(int *)CMSG_DATA(cmsg);
+ break;
+
+ default:
+ printk(KERN_DEBUG "invalid cmsg type: %d\n", cmsg->cmsg_type);
+ err = -EINVAL;
+ break;
+ };
+ }
+
+exit_f:
+ return err;
+}
diff --git a/pfinet/linux-src/net/ipv6/exthdrs.c b/pfinet/linux-src/net/ipv6/exthdrs.c
new file mode 100644
index 00000000..43ce8817
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/exthdrs.c
@@ -0,0 +1,770 @@
+/*
+ * Extension Header handling for IPv6
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ * Andi Kleen <ak@muc.de>
+ * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
+ *
+ * $Id: exthdrs.c,v 1.1 2007/10/08 21:12:30 stesie Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/sched.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+#include <linux/icmpv6.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/transp_v6.h>
+#include <net/rawv6.h>
+#include <net/ndisc.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+
+#include <asm/uaccess.h>
+
+/*
+ * Parsing inbound headers.
+ *
+ * Parsing function "func" returns pointer to the place,
+ * where next nexthdr value is stored or NULL, if parsing
+ * failed. It should also update skb->h.
+ */
+
+struct hdrtype_proc
+{
+ int type;
+ u8* (*func) (struct sk_buff **, u8 *ptr);
+};
+
+/*
+ * Parsing tlv encoded headers.
+ *
+ * Parsing function "func" returns 1, if parsing succeed
+ * and 0, if it failed.
+ * It MUST NOT touch skb->h.
+ */
+
+struct tlvtype_proc
+{
+ int type;
+ int (*func) (struct sk_buff *, __u8 *ptr);
+};
+
+/*********************
+ Generic functions
+ *********************/
+
+/* An unknown option is detected, decide what to do */
+
+int ip6_tlvopt_unknown(struct sk_buff *skb, u8 *opt)
+{
+ switch ((opt[0] & 0xC0) >> 6) {
+ case 0: /* ignore */
+ return 1;
+
+ case 1: /* drop packet */
+ break;
+
+ case 3: /* Send ICMP if not a multicast address and drop packet */
+ /* Actually, it is redundant check. icmp_send
+ will recheck in any case.
+ */
+ if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr))
+ break;
+ case 2: /* send ICMP PARM PROB regardless and drop packet */
+ icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, opt);
+ return 0;
+ };
+
+ kfree_skb(skb);
+ return 0;
+}
+
+/* Parse tlv encoded option header (hop-by-hop or destination) */
+
+static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb,
+ __u8 *nhptr)
+{
+ struct tlvtype_proc *curr;
+ u8 *ptr = skb->h.raw;
+ int len = ((ptr[1]+1)<<3) - 2;
+
+ ptr += 2;
+
+ if (skb->tail - (ptr + len) < 0) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ while (len > 0) {
+ int optlen = ptr[1]+2;
+
+ switch (ptr[0]) {
+ case IPV6_TLV_PAD0:
+ optlen = 1;
+ break;
+
+ case IPV6_TLV_PADN:
+ break;
+
+ default: /* Other TLV code so scan list */
+ for (curr=procs; curr->type >= 0; curr++) {
+ if (curr->type == ptr[0]) {
+ if (curr->func(skb, ptr) == 0)
+ return 0;
+ break;
+ }
+ }
+ if (curr->type < 0) {
+ if (ip6_tlvopt_unknown(skb, ptr) == 0)
+ return 0;
+ }
+ break;
+ }
+ ptr += optlen;
+ len -= optlen;
+ }
+ if (len == 0)
+ return 1;
+ kfree_skb(skb);
+ return 0;
+}
+
+/*****************************
+ Destination options header.
+ *****************************/
+
+struct tlvtype_proc tlvprocdestopt_lst[] = {
+ /* No destination options are defined now */
+ {-1, NULL}
+};
+
+static u8 *ipv6_dest_opt(struct sk_buff **skb_ptr, u8 *nhptr)
+{
+ struct sk_buff *skb=*skb_ptr;
+ struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
+ struct ipv6_destopt_hdr *hdr = (struct ipv6_destopt_hdr *) skb->h.raw;
+
+ opt->dst1 = (u8*)hdr - skb->nh.raw;
+
+ if (ip6_parse_tlv(tlvprocdestopt_lst, skb, nhptr)) {
+ skb->h.raw += ((hdr->hdrlen+1)<<3);
+ return &hdr->nexthdr;
+ }
+
+ return NULL;
+}
+
+/********************************
+ NONE header. No data in packet.
+ ********************************/
+
+static u8 *ipv6_nodata(struct sk_buff **skb_ptr, u8 *nhptr)
+{
+ kfree_skb(*skb_ptr);
+ return NULL;
+}
+
+/********************************
+ Routing header.
+ ********************************/
+
+static u8* ipv6_routing_header(struct sk_buff **skb_ptr, u8 *nhptr)
+{
+ struct sk_buff *skb = *skb_ptr;
+ struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
+ struct in6_addr *addr;
+ struct in6_addr daddr;
+ int addr_type;
+ int n, i;
+
+ struct ipv6_rt_hdr *hdr = (struct ipv6_rt_hdr *) skb->h.raw;
+ struct rt0_hdr *rthdr;
+
+ if (((hdr->hdrlen+1)<<3) > skb->tail - skb->h.raw) {
+ ipv6_statistics.Ip6InHdrErrors++;
+ kfree_skb(skb);
+ return NULL;
+ }
+
+looped_back:
+ if (hdr->segments_left == 0) {
+ opt->srcrt = (u8*)hdr - skb->nh.raw;
+ skb->h.raw += (hdr->hdrlen + 1) << 3;
+ opt->dst0 = opt->dst1;
+ opt->dst1 = 0;
+ return &hdr->nexthdr;
+ }
+
+ if (hdr->type != IPV6_SRCRT_TYPE_0 || hdr->hdrlen & 0x01) {
+ u8 *pos = (u8*) hdr;
+
+ if (hdr->type != IPV6_SRCRT_TYPE_0)
+ pos += 2;
+ else
+ pos += 1;
+
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, pos);
+ return NULL;
+ }
+
+ /*
+ * This is the routing header forwarding algorithm from
+ * RFC 1883, page 17.
+ */
+
+ n = hdr->hdrlen >> 1;
+
+ if (hdr->segments_left > n) {
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, &hdr->segments_left);
+ return NULL;
+ }
+
+ /* We are about to mangle packet header. Be careful!
+ Do not damage packets queued somewhere.
+ */
+ if (skb_cloned(skb)) {
+ struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
+ kfree_skb(skb);
+ if (skb2 == NULL)
+ return NULL;
+ *skb_ptr = skb = skb2;
+ opt = (struct inet6_skb_parm *)skb2->cb;
+ hdr = (struct ipv6_rt_hdr *) skb2->h.raw;
+ }
+
+ i = n - --hdr->segments_left;
+
+ rthdr = (struct rt0_hdr *) hdr;
+ addr = rthdr->addr;
+ addr += i - 1;
+
+ addr_type = ipv6_addr_type(addr);
+
+ if (addr_type == IPV6_ADDR_MULTICAST) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ ipv6_addr_copy(&daddr, addr);
+ ipv6_addr_copy(addr, &skb->nh.ipv6h->daddr);
+ ipv6_addr_copy(&skb->nh.ipv6h->daddr, &daddr);
+
+ dst_release(xchg(&skb->dst, NULL));
+ ip6_route_input(skb);
+ if (skb->dst->error) {
+ skb->dst->input(skb);
+ return NULL;
+ }
+ if (skb->dst->dev->flags&IFF_LOOPBACK) {
+ if (skb->nh.ipv6h->hop_limit <= 1) {
+ icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
+ 0, skb->dev);
+ kfree_skb(skb);
+ return NULL;
+ }
+ skb->nh.ipv6h->hop_limit--;
+ goto looped_back;
+ }
+
+ skb->dst->input(skb);
+ return NULL;
+}
+
+/*
+ This function inverts received rthdr.
+ NOTE: specs allow to make it automatically only if
+ packet authenticated.
+
+ I will not discuss it here (though, I am really pissed off at
+ this stupid requirement making rthdr idea useless)
+
+ Actually, it creates severe problems for us.
+ Embrionic requests has no associated sockets,
+ so that user have no control over it and
+ cannot not only to set reply options, but
+ even to know, that someone wants to connect
+ without success. :-(
+
+ For now we need to test the engine, so that I created
+ temporary (or permanent) backdoor.
+ If listening socket set IPV6_RTHDR to 2, then we invert header.
+ --ANK (980729)
+ */
+
+struct ipv6_txoptions *
+ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr)
+{
+ /* Received rthdr:
+
+ [ H1 -> H2 -> ... H_prev ] daddr=ME
+
+ Inverted result:
+ [ H_prev -> ... -> H1 ] daddr =sender
+
+ Note, that IP output engine will rewrire this rthdr
+ by rotating it left by one addr.
+ */
+
+ int n, i;
+ struct rt0_hdr *rthdr = (struct rt0_hdr*)hdr;
+ struct rt0_hdr *irthdr;
+ struct ipv6_txoptions *opt;
+ int hdrlen = ipv6_optlen(hdr);
+
+ if (hdr->segments_left ||
+ hdr->type != IPV6_SRCRT_TYPE_0 ||
+ hdr->hdrlen & 0x01)
+ return NULL;
+
+ n = hdr->hdrlen >> 1;
+ opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC);
+ if (opt == NULL)
+ return NULL;
+ memset(opt, 0, sizeof(*opt));
+ opt->tot_len = sizeof(*opt) + hdrlen;
+ opt->srcrt = (void*)(opt+1);
+ opt->opt_nflen = hdrlen;
+
+ memcpy(opt->srcrt, hdr, sizeof(*hdr));
+ irthdr = (struct rt0_hdr*)opt->srcrt;
+ /* Obsolete field, MBZ, when originated by us */
+ irthdr->bitmap = 0;
+ opt->srcrt->segments_left = n;
+ for (i=0; i<n; i++)
+ memcpy(irthdr->addr+i, rthdr->addr+(n-1-i), 16);
+ return opt;
+}
+
+/********************************
+ AUTH header.
+ ********************************/
+
+/*
+ rfc1826 said, that if a host does not implement AUTH header
+ it MAY ignore it. We use this hole 8)
+
+ Actually, now we can implement OSPFv6 without kernel IPsec.
+ Authentication for poors may be done in user space with the same success.
+
+ Yes, it means, that we allow application to send/receive
+ raw authentication header. Apparently, we suppose, that it knows
+ what it does and calculates authentication data correctly.
+ Certainly, it is possible only for udp and raw sockets, but not for tcp.
+
+ AUTH header has 4byte granular length, which kills all the idea
+ behind AUTOMATIC 64bit alignment of IPv6. Now we will loose
+ cpu ticks, checking that sender did not something stupid
+ and opt->hdrlen is even. Shit! --ANK (980730)
+ */
+
+static u8 *ipv6_auth_hdr(struct sk_buff **skb_ptr, u8 *nhptr)
+{
+ struct sk_buff *skb=*skb_ptr;
+ struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
+ struct ipv6_opt_hdr *hdr = (struct ipv6_opt_hdr *)skb->h.raw;
+ int len = (hdr->hdrlen+2)<<2;
+
+ if (len&7)
+ return NULL;
+ opt->auth = (u8*)hdr - skb->nh.raw;
+ if (skb->h.raw + len > skb->tail)
+ return NULL;
+ skb->h.raw += len;
+ return &hdr->nexthdr;
+}
+
+/* This list MUST NOT contain entry for NEXTHDR_HOP.
+ It is parsed immediately after packet received
+ and if it occurs somewhere in another place we must
+ generate error.
+ */
+
+struct hdrtype_proc hdrproc_lst[] = {
+ {NEXTHDR_FRAGMENT, ipv6_reassembly},
+ {NEXTHDR_ROUTING, ipv6_routing_header},
+ {NEXTHDR_DEST, ipv6_dest_opt},
+ {NEXTHDR_NONE, ipv6_nodata},
+ {NEXTHDR_AUTH, ipv6_auth_hdr},
+ /*
+ {NEXTHDR_ESP, ipv6_esp_hdr},
+ */
+ {-1, NULL}
+};
+
+u8 *ipv6_parse_exthdrs(struct sk_buff **skb_in, u8 *nhptr)
+{
+ struct hdrtype_proc *hdrt;
+ u8 nexthdr = *nhptr;
+
+restart:
+ for (hdrt=hdrproc_lst; hdrt->type >= 0; hdrt++) {
+ if (hdrt->type == nexthdr) {
+ if ((nhptr = hdrt->func(skb_in, nhptr)) != NULL) {
+ nexthdr = *nhptr;
+ goto restart;
+ }
+ return NULL;
+ }
+ }
+ return nhptr;
+}
+
+
+/**********************************
+ Hop-by-hop options.
+ **********************************/
+
+/* Router Alert as of draft-ietf-ipngwg-ipv6router-alert-04 */
+
+static int ipv6_hop_ra(struct sk_buff *skb, u8 *ptr)
+{
+ if (ptr[1] == 2) {
+ ((struct inet6_skb_parm*)skb->cb)->ra = ptr - skb->nh.raw;
+ return 1;
+ }
+ if (net_ratelimit())
+ printk(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n", ptr[1]);
+ kfree_skb(skb);
+ return 0;
+}
+
+/* Jumbo payload */
+
+static int ipv6_hop_jumbo(struct sk_buff *skb, u8 *ptr)
+{
+ u32 pkt_len;
+
+ if (ptr[1] != 4 || ((ptr-skb->nh.raw)&3) != 2) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", ptr[1]);
+ goto drop;
+ }
+
+ pkt_len = ntohl(*(u32*)(ptr+2));
+ if (pkt_len < 0x10000) {
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, ptr+2);
+ return 0;
+ }
+ if (skb->nh.ipv6h->payload_len) {
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, ptr);
+ return 0;
+ }
+
+ if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
+ ipv6_statistics.Ip6InTruncatedPkts++;
+ goto drop;
+ }
+ skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
+ return 1;
+
+drop:
+ kfree_skb(skb);
+ return 0;
+}
+
+struct tlvtype_proc tlvprochopopt_lst[] = {
+ {IPV6_TLV_ROUTERALERT, ipv6_hop_ra},
+ {IPV6_TLV_JUMBO, ipv6_hop_jumbo},
+ {-1, NULL}
+};
+
+u8 * ipv6_parse_hopopts(struct sk_buff *skb, u8 *nhptr)
+{
+ ((struct inet6_skb_parm*)skb->cb)->hop = sizeof(struct ipv6hdr);
+ if (ip6_parse_tlv(tlvprochopopt_lst, skb, nhptr))
+ return nhptr+((nhptr[1]+1)<<3);
+ return NULL;
+}
+
+/*
+ * Creating outbound headers.
+ *
+ * "build" functions work when skb is filled from head to tail (datagram)
+ * "push" functions work when headers are added from tail to head (tcp)
+ *
+ * In both cases we assume, that caller reserved enough room
+ * for headers.
+ */
+
+u8 *ipv6_build_rthdr(struct sk_buff *skb, u8 *prev_hdr,
+ struct ipv6_rt_hdr *opt, struct in6_addr *addr)
+{
+ struct rt0_hdr *phdr, *ihdr;
+ int hops;
+
+ ihdr = (struct rt0_hdr *) opt;
+
+ phdr = (struct rt0_hdr *) skb_put(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
+ memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
+
+ hops = ihdr->rt_hdr.hdrlen >> 1;
+
+ if (hops > 1)
+ memcpy(phdr->addr, ihdr->addr + 1,
+ (hops - 1) * sizeof(struct in6_addr));
+
+ ipv6_addr_copy(phdr->addr + (hops - 1), addr);
+
+ phdr->rt_hdr.nexthdr = *prev_hdr;
+ *prev_hdr = NEXTHDR_ROUTING;
+ return &phdr->rt_hdr.nexthdr;
+}
+
+static u8 *ipv6_build_exthdr(struct sk_buff *skb, u8 *prev_hdr, u8 type, struct ipv6_opt_hdr *opt)
+{
+ struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_put(skb, ipv6_optlen(opt));
+
+ memcpy(h, opt, ipv6_optlen(opt));
+ h->nexthdr = *prev_hdr;
+ *prev_hdr = type;
+ return &h->nexthdr;
+}
+
+static u8 *ipv6_build_authhdr(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_opt_hdr *opt)
+{
+ struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_put(skb, (opt->hdrlen+2)<<2);
+
+ memcpy(h, opt, (opt->hdrlen+2)<<2);
+ h->nexthdr = *prev_hdr;
+ *prev_hdr = NEXTHDR_AUTH;
+ return &h->nexthdr;
+}
+
+
+u8 *ipv6_build_nfrag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptions *opt,
+ struct in6_addr *daddr, u32 jumbolen)
+{
+ struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb->data;
+
+ if (opt && opt->hopopt)
+ prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_HOP, opt->hopopt);
+
+ if (jumbolen) {
+ u8 *jumboopt = (u8 *)skb_put(skb, 8);
+
+ if (opt && opt->hopopt) {
+ *jumboopt++ = IPV6_TLV_PADN;
+ *jumboopt++ = 0;
+ h->hdrlen++;
+ } else {
+ h = (struct ipv6_opt_hdr *)jumboopt;
+ h->nexthdr = *prev_hdr;
+ h->hdrlen = 0;
+ jumboopt += 2;
+ *prev_hdr = NEXTHDR_HOP;
+ prev_hdr = &h->nexthdr;
+ }
+ jumboopt[0] = IPV6_TLV_JUMBO;
+ jumboopt[1] = 4;
+ *(u32*)(jumboopt+2) = htonl(jumbolen);
+ }
+ if (opt) {
+ if (opt->dst0opt)
+ prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst0opt);
+ if (opt->srcrt)
+ prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, daddr);
+ }
+ return prev_hdr;
+}
+
+u8 *ipv6_build_frag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptions *opt)
+{
+ if (opt->auth)
+ prev_hdr = ipv6_build_authhdr(skb, prev_hdr, opt->auth);
+ if (opt->dst1opt)
+ prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst1opt);
+ return prev_hdr;
+}
+
+static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
+ struct ipv6_rt_hdr *opt,
+ struct in6_addr **addr_p)
+{
+ struct rt0_hdr *phdr, *ihdr;
+ int hops;
+
+ ihdr = (struct rt0_hdr *) opt;
+
+ phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
+ memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
+
+ hops = ihdr->rt_hdr.hdrlen >> 1;
+
+ if (hops > 1)
+ memcpy(phdr->addr, ihdr->addr + 1,
+ (hops - 1) * sizeof(struct in6_addr));
+
+ ipv6_addr_copy(phdr->addr + (hops - 1), *addr_p);
+ *addr_p = ihdr->addr;
+
+ phdr->rt_hdr.nexthdr = *proto;
+ *proto = NEXTHDR_ROUTING;
+}
+
+static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
+{
+ struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
+
+ memcpy(h, opt, ipv6_optlen(opt));
+ h->nexthdr = *proto;
+ *proto = type;
+}
+
+static void ipv6_push_authhdr(struct sk_buff *skb, u8 *proto, struct ipv6_opt_hdr *opt)
+{
+ struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, (opt->hdrlen+2)<<2);
+
+ memcpy(h, opt, (opt->hdrlen+2)<<2);
+ h->nexthdr = *proto;
+ *proto = NEXTHDR_AUTH;
+}
+
+void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
+ u8 *proto,
+ struct in6_addr **daddr)
+{
+ if (opt->srcrt)
+ ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
+ if (opt->dst0opt)
+ ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
+ if (opt->hopopt)
+ ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
+}
+
+void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
+{
+ if (opt->dst1opt)
+ ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
+ if (opt->auth)
+ ipv6_push_authhdr(skb, proto, opt->auth);
+}
+
+struct ipv6_txoptions *
+ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
+{
+ struct ipv6_txoptions *opt2;
+
+ opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
+ if (opt2) {
+ long dif = (char*)opt2 - (char*)opt;
+ memcpy(opt2, opt, opt->tot_len);
+ if (opt2->hopopt)
+ *((char**)&opt2->hopopt) += dif;
+ if (opt2->dst0opt)
+ *((char**)&opt2->dst0opt) += dif;
+ if (opt2->dst1opt)
+ *((char**)&opt2->dst1opt) += dif;
+ if (opt2->auth)
+ *((char**)&opt2->auth) += dif;
+ if (opt2->srcrt)
+ *((char**)&opt2->srcrt) += dif;
+ }
+ return opt2;
+}
+
+
+/*
+ * find out if nexthdr is a well-known extension header or a protocol
+ */
+
+static __inline__ int ipv6_ext_hdr(u8 nexthdr)
+{
+ /*
+ * find out if nexthdr is an extension header or a protocol
+ */
+ return ( (nexthdr == NEXTHDR_HOP) ||
+ (nexthdr == NEXTHDR_ROUTING) ||
+ (nexthdr == NEXTHDR_FRAGMENT) ||
+ (nexthdr == NEXTHDR_AUTH) ||
+ (nexthdr == NEXTHDR_NONE) ||
+ (nexthdr == NEXTHDR_DEST) );
+}
+
+/*
+ * Skip any extension headers. This is used by the ICMP module.
+ *
+ * Note that strictly speaking this conflicts with RFC1883 4.0:
+ * ...The contents and semantics of each extension header determine whether
+ * or not to proceed to the next header. Therefore, extension headers must
+ * be processed strictly in the order they appear in the packet; a
+ * receiver must not, for example, scan through a packet looking for a
+ * particular kind of extension header and process that header prior to
+ * processing all preceding ones.
+ *
+ * We do exactly this. This is a protocol bug. We can't decide after a
+ * seeing an unknown discard-with-error flavour TLV option if it's a
+ * ICMP error message or not (errors should never be send in reply to
+ * ICMP error messages).
+ *
+ * But I see no other way to do this. This might need to be reexamined
+ * when Linux implements ESP (and maybe AUTH) headers.
+ * --AK
+ *
+ * This function parses (probably truncated) exthdr set "hdr"
+ * of length "len". "nexthdrp" initially points to some place,
+ * where type of the first header can be found.
+ *
+ * It skips all well-known exthdrs, and returns pointer to the start
+ * of unparsable area i.e. the first header with unknown type.
+ * If it is not NULL *nexthdr is updated by type/protocol of this header.
+ *
+ * NOTES: - if packet terminated with NEXTHDR_NONE it returns NULL.
+ * - it may return pointer pointing beyond end of packet,
+ * if the last recognized header is truncated in the middle.
+ * - if packet is truncated, so that all parsed headers are skipped,
+ * it returns NULL.
+ * - First fragment header is skipped, not-first ones
+ * are considered as unparsable.
+ * - ESP is unparsable for now and considered like
+ * normal payload protocol.
+ * - Note also special handling of AUTH header. Thanks to IPsec wizards.
+ *
+ * --ANK (980726)
+ */
+
+u8 *ipv6_skip_exthdr(struct ipv6_opt_hdr *hdr, u8 *nexthdrp, int len)
+{
+ u8 nexthdr = *nexthdrp;
+
+ while (ipv6_ext_hdr(nexthdr)) {
+ int hdrlen;
+
+ if (len < sizeof(struct ipv6_opt_hdr))
+ return NULL;
+ if (nexthdr == NEXTHDR_NONE)
+ return NULL;
+ if (nexthdr == NEXTHDR_FRAGMENT) {
+ struct frag_hdr *fhdr = (struct frag_hdr *) hdr;
+ if (ntohs(fhdr->frag_off) & ~0x7)
+ break;
+ hdrlen = 8;
+ } else if (nexthdr == NEXTHDR_AUTH)
+ hdrlen = (hdr->hdrlen+2)<<2;
+ else
+ hdrlen = ipv6_optlen(hdr);
+
+ nexthdr = hdr->nexthdr;
+ hdr = (struct ipv6_opt_hdr *) ((u8*)hdr + hdrlen);
+ len -= hdrlen;
+ }
+
+ *nexthdrp = nexthdr;
+ return (u8*)hdr;
+}
diff --git a/pfinet/linux-src/net/ipv6/icmpv6.c b/pfinet/linux-src/net/ipv6/icmpv6.c
new file mode 100644
index 00000000..9d701764
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/icmpv6.c
@@ -0,0 +1,676 @@
+/*
+ * Internet Control Message Protocol (ICMPv6)
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: icmpv6.c,v 1.3 2009/02/24 01:21:16 sthibaul Exp $
+ *
+ * Based on net/ipv4/icmp.c
+ *
+ * RFC 1885
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * Changes:
+ *
+ * Andi Kleen : exception handling
+ * Andi Kleen add rate limits. never reply to a icmp.
+ * add more length checks and other fixes.
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/icmpv6.h>
+
+#include <net/ip.h>
+#include <net/sock.h>
+
+#include <net/ipv6.h>
+#include <net/checksum.h>
+#include <net/protocol.h>
+#include <net/raw.h>
+#include <net/rawv6.h>
+#include <net/transp_v6.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+#include <net/icmp.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+struct icmpv6_mib icmpv6_statistics;
+
+/*
+ * ICMP socket for flow control.
+ */
+
+struct socket *icmpv6_socket;
+
+int icmpv6_rcv(struct sk_buff *skb, unsigned long len);
+
+static struct inet6_protocol icmpv6_protocol =
+{
+ icmpv6_rcv, /* handler */
+ NULL, /* error control */
+ NULL, /* next */
+ IPPROTO_ICMPV6, /* protocol ID */
+ 0, /* copy */
+ NULL, /* data */
+ "ICMPv6" /* name */
+};
+
+struct icmpv6_msg {
+ struct icmp6hdr icmph;
+ __u8 *data;
+ struct in6_addr *daddr;
+ int len;
+ __u32 csum;
+};
+
+
+
+/*
+ * getfrag callback
+ */
+
+static int icmpv6_getfrag(const void *data, struct in6_addr *saddr,
+ char *buff, unsigned int offset, unsigned int len)
+{
+ struct icmpv6_msg *msg = (struct icmpv6_msg *) data;
+ struct icmp6hdr *icmph;
+ __u32 csum;
+
+ /*
+ * in theory offset must be 0 since we never send more
+ * than IPV6_MIN_MTU bytes on an error or more than the path mtu
+ * on an echo reply. (those are the rules on RFC 1883)
+ *
+ * Luckily, this statement is obsolete after
+ * draft-ietf-ipngwg-icmp-v2-00 --ANK (980730)
+ */
+
+ if (offset) {
+ csum = csum_partial_copy((void *) msg->data +
+ offset - sizeof(struct icmp6hdr),
+ buff, len, msg->csum);
+ msg->csum = csum;
+ return 0;
+ }
+
+ csum = csum_partial_copy((void *) &msg->icmph, buff,
+ sizeof(struct icmp6hdr), msg->csum);
+
+ csum = csum_partial_copy((void *) msg->data,
+ buff + sizeof(struct icmp6hdr),
+ len - sizeof(struct icmp6hdr), csum);
+
+ icmph = (struct icmp6hdr *) buff;
+
+ icmph->icmp6_cksum = csum_ipv6_magic(saddr, msg->daddr, msg->len,
+ IPPROTO_ICMPV6, csum);
+ return 0;
+}
+
+
+/*
+ * Slightly more convenient version of icmpv6_send.
+ */
+void icmpv6_param_prob(struct sk_buff *skb, int code, void *pos)
+{
+ int offset = (u8*)pos - (u8*)skb->nh.ipv6h;
+
+ icmpv6_send(skb, ICMPV6_PARAMPROB, code, offset, skb->dev);
+ kfree_skb(skb);
+}
+
+/*
+ * Figure out, may we reply to this packet with icmp error.
+ *
+ * We do not reply, if:
+ * - it was icmp error message.
+ * - it is truncated, so that it is known, that protocol is ICMPV6
+ * (i.e. in the middle of some exthdr)
+ * - it is not the first fragment. BTW IPv6 specs say nothing about
+ * this case, but it is clear, that our reply would be useless
+ * for sender.
+ *
+ * --ANK (980726)
+ */
+
+static int is_ineligible(struct ipv6hdr *hdr, int len)
+{
+ u8 *ptr;
+ __u8 nexthdr = hdr->nexthdr;
+
+ if (len < (int)sizeof(*hdr))
+ return 1;
+
+ ptr = ipv6_skip_exthdr((struct ipv6_opt_hdr *)(hdr+1), &nexthdr, len - sizeof(*hdr));
+ if (!ptr)
+ return 0;
+ if (nexthdr == IPPROTO_ICMPV6) {
+ struct icmp6hdr *ihdr = (struct icmp6hdr *)ptr;
+ return (ptr - (u8*)hdr) > len || !(ihdr->icmp6_type & 0x80);
+ }
+ return nexthdr == NEXTHDR_FRAGMENT;
+}
+
+int sysctl_icmpv6_time = 1*HZ;
+
+/*
+ * Check the ICMP output rate limit
+ */
+static inline int icmpv6_xrlim_allow(struct sock *sk, int type,
+ struct flowi *fl)
+{
+ struct dst_entry *dst;
+ int res = 0;
+
+ /* Informational messages are not limited. */
+ if (type & 0x80)
+ return 1;
+
+ /* Do not limit pmtu discovery, it would break it. */
+ if (type == ICMPV6_PKT_TOOBIG)
+ return 1;
+
+ /*
+ * Look up the output route.
+ * XXX: perhaps the expire for routing entries cloned by
+ * this lookup should be more aggressive (not longer than timeout).
+ */
+ dst = ip6_route_output(sk, fl);
+ if (dst->error) {
+ ipv6_statistics.Ip6OutNoRoutes++;
+ } else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) {
+ res = 1;
+ } else {
+ struct rt6_info *rt = (struct rt6_info *)dst;
+ int tmo = sysctl_icmpv6_time;
+
+ /* Give more bandwidth to wider prefixes. */
+ if (rt->rt6i_dst.plen < 128)
+ tmo >>= ((128 - rt->rt6i_dst.plen)>>5);
+
+ res = xrlim_allow(dst, tmo);
+ }
+ dst_release(dst);
+ return res;
+}
+
+/*
+ * an inline helper for the "simple" if statement below
+ * checks if parameter problem report is caused by an
+ * unrecognized IPv6 option that has the Option Type
+ * highest-order two bits set to 10
+ */
+
+static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset)
+{
+ u8 *buff = skb->nh.raw;
+
+ return ( ( *(buff + offset) & 0xC0 ) == 0x80 );
+}
+
+/*
+ * Send an ICMP message in response to a packet in error
+ */
+
+void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
+ struct device *dev)
+{
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ struct sock *sk = icmpv6_socket->sk;
+ struct in6_addr *saddr = NULL;
+ int iif = 0;
+ struct icmpv6_msg msg;
+ struct flowi fl;
+ int addr_type = 0;
+ int len;
+
+ /*
+ * sanity check pointer in case of parameter problem
+ */
+
+ if (type == ICMPV6_PARAMPROB &&
+ (info > (skb->tail - ((unsigned char *) hdr)))) {
+ printk(KERN_DEBUG "icmpv6_send: bug! pointer > skb\n");
+ return;
+ }
+
+ /*
+ * Make sure we respect the rules
+ * i.e. RFC 1885 2.4(e)
+ * Rule (e.1) is enforced by not using icmpv6_send
+ * in any code that processes icmp errors.
+ */
+
+ addr_type = ipv6_addr_type(&hdr->daddr);
+
+ if (ipv6_chk_addr(&hdr->daddr, skb->dev, 0))
+ saddr = &hdr->daddr;
+
+ /*
+ * Dest addr check
+ */
+
+ if ((addr_type & IPV6_ADDR_MULTICAST || skb->pkt_type != PACKET_HOST)) {
+ if (type != ICMPV6_PKT_TOOBIG &&
+ !(type == ICMPV6_PARAMPROB &&
+ code == ICMPV6_UNK_OPTION &&
+ (opt_unrec(skb, info))))
+ return;
+
+ saddr = NULL;
+ }
+
+ addr_type = ipv6_addr_type(&hdr->saddr);
+
+ /*
+ * Source addr check
+ */
+
+ if (addr_type & IPV6_ADDR_LINKLOCAL)
+ iif = skb->dev->ifindex;
+
+ /*
+ * Must not send if we know that source is Anycast also.
+ * for now we don't know that.
+ */
+ if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) {
+ printk(KERN_DEBUG "icmpv6_send: addr_any/mcast source\n");
+ return;
+ }
+
+ /*
+ * Never answer to a ICMP packet.
+ */
+ if (is_ineligible(hdr, (u8*)skb->tail - (u8*)hdr)) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "icmpv6_send: no reply to icmp error/fragment\n");
+ return;
+ }
+
+ fl.proto = IPPROTO_ICMPV6;
+ fl.nl_u.ip6_u.daddr = &hdr->saddr;
+ fl.nl_u.ip6_u.saddr = saddr;
+ fl.oif = iif;
+ fl.fl6_flowlabel = 0;
+ fl.uli_u.icmpt.type = type;
+ fl.uli_u.icmpt.code = code;
+
+ if (!icmpv6_xrlim_allow(sk, type, &fl))
+ return;
+
+ /*
+ * ok. kick it. checksum will be provided by the
+ * getfrag_t callback.
+ */
+
+ msg.icmph.icmp6_type = type;
+ msg.icmph.icmp6_code = code;
+ msg.icmph.icmp6_cksum = 0;
+ msg.icmph.icmp6_pointer = htonl(info);
+
+ msg.data = skb->nh.raw;
+ msg.csum = 0;
+ msg.daddr = &hdr->saddr;
+
+ len = min((skb->tail - ((unsigned char *) hdr)) + sizeof(struct icmp6hdr),
+ IPV6_MIN_MTU - sizeof(struct ipv6hdr));
+
+ if (len < 0) {
+ printk(KERN_DEBUG "icmp: len problem\n");
+ return;
+ }
+
+ msg.len = len;
+
+ ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
+ MSG_DONTWAIT);
+ if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
+ (&icmpv6_statistics.Icmp6OutDestUnreachs)[type-1]++;
+ icmpv6_statistics.Icmp6OutMsgs++;
+}
+
+static void icmpv6_echo_reply(struct sk_buff *skb)
+{
+ struct sock *sk = icmpv6_socket->sk;
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw;
+ struct in6_addr *saddr;
+ struct icmpv6_msg msg;
+ struct flowi fl;
+ unsigned char *data;
+ int len;
+
+ data = (char *) (icmph + 1);
+
+ saddr = &hdr->daddr;
+
+ if (ipv6_addr_type(saddr) & IPV6_ADDR_MULTICAST)
+ saddr = NULL;
+
+ len = skb->tail - data;
+ len += sizeof(struct icmp6hdr);
+
+ msg.icmph.icmp6_type = ICMPV6_ECHO_REPLY;
+ msg.icmph.icmp6_code = 0;
+ msg.icmph.icmp6_cksum = 0;
+ msg.icmph.icmp6_identifier = icmph->icmp6_identifier;
+ msg.icmph.icmp6_sequence = icmph->icmp6_sequence;
+
+ msg.data = data;
+ msg.csum = 0;
+ msg.len = len;
+ msg.daddr = &hdr->saddr;
+
+ fl.proto = IPPROTO_ICMPV6;
+ fl.nl_u.ip6_u.daddr = &hdr->saddr;
+ fl.nl_u.ip6_u.saddr = saddr;
+ fl.oif = skb->dev->ifindex;
+ fl.fl6_flowlabel = 0;
+ fl.uli_u.icmpt.type = ICMPV6_ECHO_REPLY;
+ fl.uli_u.icmpt.code = 0;
+
+ ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
+ MSG_DONTWAIT);
+ icmpv6_statistics.Icmp6OutEchoReplies++;
+ icmpv6_statistics.Icmp6OutMsgs++;
+}
+
+static void icmpv6_notify(struct sk_buff *skb,
+ int type, int code, u32 info, unsigned char *buff, int len)
+{
+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+ struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+ struct ipv6hdr *hdr = (struct ipv6hdr *) buff;
+ struct inet6_protocol *ipprot;
+ struct sock *sk;
+ u8 *pb;
+ int hash;
+ u8 nexthdr;
+
+ nexthdr = hdr->nexthdr;
+
+ len -= sizeof(struct ipv6hdr);
+ if (len < 0)
+ return;
+
+ /* now skip over extension headers */
+ pb = ipv6_skip_exthdr((struct ipv6_opt_hdr *) (hdr + 1), &nexthdr, len);
+ if (!pb)
+ return;
+
+ /* BUGGG_FUTURE: we should try to parse exthdrs in this packet.
+ Without this we will not able f.e. to make source routed
+ pmtu discovery.
+ Corresponding argument (opt) to notifiers is already added.
+ --ANK (980726)
+ */
+
+ hash = nexthdr & (MAX_INET_PROTOS - 1);
+
+ for (ipprot = (struct inet6_protocol *) inet6_protos[hash];
+ ipprot != NULL;
+ ipprot=(struct inet6_protocol *)ipprot->next) {
+ if (ipprot->protocol != nexthdr)
+ continue;
+
+ if (ipprot->err_handler)
+ ipprot->err_handler(skb, hdr, NULL, type, code, pb, info);
+ }
+
+ sk = raw_v6_htable[hash];
+
+ if (sk == NULL)
+ return;
+
+ while((sk = raw_v6_lookup(sk, nexthdr, daddr, saddr))) {
+ rawv6_err(sk, skb, hdr, NULL, type, code, pb, info);
+ sk = sk->next;
+ }
+}
+
+/*
+ * Handle icmp messages
+ */
+
+int icmpv6_rcv(struct sk_buff *skb, unsigned long len)
+{
+ struct device *dev = skb->dev;
+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+ struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+ struct ipv6hdr *orig_hdr;
+ struct icmp6hdr *hdr = (struct icmp6hdr *) skb->h.raw;
+ int ulen;
+ int type;
+
+ icmpv6_statistics.Icmp6InMsgs++;
+
+ if (len < sizeof(struct icmp6hdr))
+ goto discard_it;
+
+ /* Perform checksum. */
+ switch (skb->ip_summed) {
+ case CHECKSUM_NONE:
+ skb->csum = csum_partial((char *)hdr, len, 0);
+ case CHECKSUM_HW:
+ if (csum_ipv6_magic(saddr, daddr, len, IPPROTO_ICMPV6,
+ skb->csum)) {
+ printk(KERN_DEBUG "ICMPv6 checksum failed [%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x > %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n",
+ ntohs(saddr->__in6_u.__u6_addr16[0]),
+ ntohs(saddr->__in6_u.__u6_addr16[1]),
+ ntohs(saddr->__in6_u.__u6_addr16[2]),
+ ntohs(saddr->__in6_u.__u6_addr16[3]),
+ ntohs(saddr->__in6_u.__u6_addr16[4]),
+ ntohs(saddr->__in6_u.__u6_addr16[5]),
+ ntohs(saddr->__in6_u.__u6_addr16[6]),
+ ntohs(saddr->__in6_u.__u6_addr16[7]),
+ ntohs(daddr->__in6_u.__u6_addr16[0]),
+ ntohs(daddr->__in6_u.__u6_addr16[1]),
+ ntohs(daddr->__in6_u.__u6_addr16[2]),
+ ntohs(daddr->__in6_u.__u6_addr16[3]),
+ ntohs(daddr->__in6_u.__u6_addr16[4]),
+ ntohs(daddr->__in6_u.__u6_addr16[5]),
+ ntohs(daddr->__in6_u.__u6_addr16[6]),
+ ntohs(daddr->__in6_u.__u6_addr16[7]));
+ goto discard_it;
+ }
+ default:
+ /* CHECKSUM_UNNECESSARY */
+ break;
+ };
+
+ /*
+ * length of original packet carried in skb
+ */
+ ulen = skb->tail - (unsigned char *) (hdr + 1);
+
+ type = hdr->icmp6_type;
+
+ if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
+ (&icmpv6_statistics.Icmp6InDestUnreachs)[type-ICMPV6_DEST_UNREACH]++;
+ else if (type >= ICMPV6_ECHO_REQUEST && type <= NDISC_REDIRECT)
+ (&icmpv6_statistics.Icmp6InEchos)[type-ICMPV6_ECHO_REQUEST]++;
+
+ switch (type) {
+
+ case ICMPV6_ECHO_REQUEST:
+ icmpv6_echo_reply(skb);
+ break;
+
+ case ICMPV6_ECHO_REPLY:
+ /* we coulnd't care less */
+ break;
+
+ case ICMPV6_PKT_TOOBIG:
+ /* BUGGG_FUTURE: if packet contains rthdr, we cannot update
+ standard destination cache. Seems, only "advanced"
+ destination cache will allow to solve this problem
+ --ANK (980726)
+ */
+ orig_hdr = (struct ipv6hdr *) (hdr + 1);
+ if (ulen >= sizeof(struct ipv6hdr))
+ rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev,
+ ntohl(hdr->icmp6_mtu));
+
+ /*
+ * Drop through to notify
+ */
+
+ case ICMPV6_DEST_UNREACH:
+ case ICMPV6_TIME_EXCEED:
+ case ICMPV6_PARAMPROB:
+ icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu,
+ (char *) (hdr + 1), ulen);
+ break;
+
+ case NDISC_ROUTER_SOLICITATION:
+ case NDISC_ROUTER_ADVERTISEMENT:
+ case NDISC_NEIGHBOUR_SOLICITATION:
+ case NDISC_NEIGHBOUR_ADVERTISEMENT:
+ case NDISC_REDIRECT:
+ ndisc_rcv(skb, len);
+ break;
+
+ case ICMPV6_MGM_QUERY:
+ igmp6_event_query(skb, hdr, len);
+ break;
+
+ case ICMPV6_MGM_REPORT:
+ igmp6_event_report(skb, hdr, len);
+ break;
+
+ case ICMPV6_MGM_REDUCTION:
+ break;
+
+ default:
+ if (net_ratelimit())
+ printk(KERN_DEBUG "icmpv6: msg of unknown type\n");
+
+ /* informational */
+ if (type & 0x80)
+ break;
+
+ /*
+ * error of unknown type.
+ * must pass to upper level
+ */
+
+ icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu,
+ (char *) (hdr + 1), ulen);
+ };
+ kfree_skb(skb);
+ return 0;
+
+discard_it:
+ icmpv6_statistics.Icmp6InErrors++;
+ kfree_skb(skb);
+ return 0;
+}
+
+int __init icmpv6_init(struct net_proto_family *ops)
+{
+ struct sock *sk;
+ int err;
+
+ icmpv6_socket = sock_alloc();
+ if (icmpv6_socket == NULL) {
+ printk(KERN_ERR
+ "Failed to create the ICMP6 control socket.\n");
+ return -1;
+ }
+#ifndef _HURD_
+ icmpv6_socket->inode->i_uid = 0;
+ icmpv6_socket->inode->i_gid = 0;
+#endif
+ icmpv6_socket->type = SOCK_RAW;
+
+ if ((err = ops->create(icmpv6_socket, IPPROTO_ICMPV6)) < 0) {
+ printk(KERN_ERR
+ "Failed to initialize the ICMP6 control socket (err %d).\n",
+ err);
+ sock_release(icmpv6_socket);
+ icmpv6_socket = NULL; /* for safety */
+ return err;
+ }
+
+ sk = icmpv6_socket->sk;
+ sk->allocation = GFP_ATOMIC;
+ sk->num = 256; /* Don't receive any data */
+
+ inet6_add_protocol(&icmpv6_protocol);
+
+ return 0;
+}
+
+void icmpv6_cleanup(void)
+{
+ sock_release(icmpv6_socket);
+ icmpv6_socket = NULL; /* For safety. */
+ inet6_del_protocol(&icmpv6_protocol);
+}
+
+static struct icmp6_err {
+ int err;
+ int fatal;
+} tab_unreach[] = {
+ { ENETUNREACH, 0}, /* NOROUTE */
+ { EACCES, 1}, /* ADM_PROHIBITED */
+ { EHOSTUNREACH, 0}, /* Was NOT_NEIGHBOUR, now reserved */
+ { EHOSTUNREACH, 0}, /* ADDR_UNREACH */
+ { ECONNREFUSED, 1}, /* PORT_UNREACH */
+};
+
+int icmpv6_err_convert(int type, int code, int *err)
+{
+ int fatal = 0;
+
+ *err = EPROTO;
+
+ switch (type) {
+ case ICMPV6_DEST_UNREACH:
+ fatal = 1;
+ if (code <= ICMPV6_PORT_UNREACH) {
+ *err = tab_unreach[code].err;
+ fatal = tab_unreach[code].fatal;
+ }
+ break;
+
+ case ICMPV6_PKT_TOOBIG:
+ *err = EMSGSIZE;
+ break;
+
+ case ICMPV6_PARAMPROB:
+ *err = EPROTO;
+ fatal = 1;
+ break;
+
+ case ICMPV6_TIME_EXCEED:
+ *err = EHOSTUNREACH;
+ break;
+ };
+
+ return fatal;
+}
diff --git a/pfinet/linux-src/net/ipv6/ip6_fib.c b/pfinet/linux-src/net/ipv6/ip6_fib.c
new file mode 100644
index 00000000..670860b2
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/ip6_fib.c
@@ -0,0 +1,1203 @@
+/*
+ * Linux INET6 implementation
+ * Forwarding Information Database
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: ip6_fib.c,v 1.2 2007/10/08 21:59:10 stesie Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/net.h>
+#include <linux/route.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+#include <net/addrconf.h>
+
+#include <net/ip6_fib.h>
+#include <net/ip6_route.h>
+
+#define RT6_DEBUG 2
+#undef CONFIG_IPV6_SUBTREES
+
+#if RT6_DEBUG >= 1
+#define BUG_TRAP(x) ({ if (!(x)) { printk("Assertion (" #x ") failed at " __FILE__ "(%d):%s\n", __LINE__, __FUNCTION__); } })
+#else
+#define BUG_TRAP(x) do { ; } while (0)
+#endif
+
+#if RT6_DEBUG >= 3
+#define RT6_TRACE(x...) printk(KERN_DEBUG x)
+#else
+#define RT6_TRACE(x...) do { ; } while (0)
+#endif
+
+struct rt6_statistics rt6_stats;
+
+enum fib_walk_state_t
+{
+#ifdef CONFIG_IPV6_SUBTREES
+ FWS_S,
+#endif
+ FWS_L,
+ FWS_R,
+ FWS_C,
+ FWS_U
+};
+
+struct fib6_cleaner_t
+{
+ struct fib6_walker_t w;
+ int (*func)(struct rt6_info *, void *arg);
+ void *arg;
+};
+
+#ifdef CONFIG_IPV6_SUBTREES
+#define FWS_INIT FWS_S
+#define SUBTREE(fn) ((fn)->subtree)
+#else
+#define FWS_INIT FWS_L
+#define SUBTREE(fn) NULL
+#endif
+
+static void fib6_prune_clones(struct fib6_node *fn, struct rt6_info *rt);
+static void fib6_repair_tree(struct fib6_node *fn);
+
+/*
+ * A routing update causes an increase of the serial number on the
+ * afected subtree. This allows for cached routes to be asynchronously
+ * tested when modifications are made to the destination cache as a
+ * result of redirects, path MTU changes, etc.
+ */
+
+static __u32 rt_sernum = 0;
+
+static struct timer_list ip6_fib_timer = {
+ NULL, NULL,
+ 0,
+ ~0UL,
+ fib6_run_gc
+};
+
+struct fib6_walker_t fib6_walker_list = {
+ &fib6_walker_list, &fib6_walker_list,
+};
+
+#define FOR_WALKERS(w) for ((w)=fib6_walker_list.next; (w) != &fib6_walker_list; (w)=(w)->next)
+
+static __inline__ u32 fib6_new_sernum(void)
+{
+ u32 n = ++rt_sernum;
+ if ((__s32)n <= 0)
+ rt_sernum = n = 1;
+ return n;
+}
+
+/*
+ * Auxiliary address test functions for the radix tree.
+ *
+ * These assume a 32bit processor (although it will work on
+ * 64bit processors)
+ */
+
+/*
+ * compare "prefix length" bits of an address
+ */
+
+static __inline__ int addr_match(void *token1, void *token2, int prefixlen)
+{
+ __u32 *a1 = token1;
+ __u32 *a2 = token2;
+ int pdw;
+ int pbi;
+
+ pdw = prefixlen >> 5; /* num of whole __u32 in prefix */
+ pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */
+
+ if (pdw)
+ if (memcmp(a1, a2, pdw << 2))
+ return 0;
+
+ if (pbi) {
+ __u32 mask;
+
+ mask = htonl((0xffffffff) << (32 - pbi));
+
+ if ((a1[pdw] ^ a2[pdw]) & mask)
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * test bit
+ */
+
+static __inline__ int addr_bit_set(void *token, int fn_bit)
+{
+ __u32 *addr = token;
+
+ return htonl(1 << ((~fn_bit)&0x1F)) & addr[fn_bit>>5];
+}
+
+/*
+ * find the first different bit between two addresses
+ * length of address must be a multiple of 32bits
+ */
+
+static __inline__ int addr_diff(void *token1, void *token2, int addrlen)
+{
+ __u32 *a1 = token1;
+ __u32 *a2 = token2;
+ int i;
+
+ addrlen >>= 2;
+
+ for (i = 0; i < addrlen; i++) {
+ __u32 xb;
+
+ xb = a1[i] ^ a2[i];
+
+ if (xb) {
+ int j = 31;
+
+ xb = ntohl(xb);
+
+ while (test_bit(j, &xb) == 0)
+ j--;
+
+ return (i * 32 + 31 - j);
+ }
+ }
+
+ /*
+ * we should *never* get to this point since that
+ * would mean the addrs are equal
+ *
+ * However, we do get to it 8) And exacly, when
+ * addresses are equal 8)
+ *
+ * ip route add 1111::/128 via ...
+ * ip route add 1111::/64 via ...
+ * and we are here.
+ *
+ * Ideally, this function should stop comparison
+ * at prefix length. It does not, but it is still OK,
+ * if returned value is greater than prefix length.
+ * --ANK (980803)
+ */
+
+ return addrlen<<5;
+}
+
+static __inline__ struct fib6_node * node_alloc(void)
+{
+ struct fib6_node *fn;
+
+ if ((fn = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC)) != NULL) {
+ memset(fn, 0, sizeof(struct fib6_node));
+ rt6_stats.fib_nodes++;
+ }
+
+ return fn;
+}
+
+static __inline__ void node_free(struct fib6_node * fn)
+{
+ rt6_stats.fib_nodes--;
+ kfree(fn);
+}
+
+static __inline__ void rt6_release(struct rt6_info *rt)
+{
+ if (atomic_dec_and_test(&rt->rt6i_ref))
+ dst_free(&rt->u.dst);
+}
+
+
+/*
+ * Routing Table
+ *
+ * return the appropriate node for a routing tree "add" operation
+ * by either creating and inserting or by returning an existing
+ * node.
+ */
+
+static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr,
+ int addrlen, int plen,
+ int offset)
+{
+ struct fib6_node *fn, *in, *ln;
+ struct fib6_node *pn = NULL;
+ struct rt6key *key;
+ int bit;
+ int dir = 0;
+ __u32 sernum = fib6_new_sernum();
+
+ RT6_TRACE("fib6_add_1\n");
+
+ /* insert node in tree */
+
+ fn = root;
+
+ if (plen == 0)
+ return fn;
+
+ do {
+ key = (struct rt6key *)((u8 *)fn->leaf + offset);
+
+ /*
+ * Prefix match
+ */
+ if (plen < fn->fn_bit ||
+ !addr_match(&key->addr, addr, fn->fn_bit))
+ goto insert_above;
+
+ /*
+ * Exact match ?
+ */
+
+ if (plen == fn->fn_bit) {
+ /* clean up an intermediate node */
+ if ((fn->fn_flags & RTN_RTINFO) == 0) {
+ rt6_release(fn->leaf);
+ fn->leaf = NULL;
+ }
+
+ fn->fn_sernum = sernum;
+
+ return fn;
+ }
+
+ /*
+ * We have more bits to go
+ */
+
+ /* Try to walk down on tree. */
+ fn->fn_sernum = sernum;
+ dir = addr_bit_set(addr, fn->fn_bit);
+ pn = fn;
+ fn = dir ? fn->right: fn->left;
+ } while (fn);
+
+ /*
+ * We wlaked to the bottom of tree.
+ * Create new leaf node without children.
+ */
+
+ ln = node_alloc();
+
+ if (ln == NULL)
+ return NULL;
+ ln->fn_bit = plen;
+
+ ln->parent = pn;
+ ln->fn_sernum = sernum;
+
+ if (dir)
+ pn->right = ln;
+ else
+ pn->left = ln;
+
+ return ln;
+
+
+insert_above:
+ /*
+ * split since we don't have a common prefix anymore or
+ * we have a less significant route.
+ * we've to insert an intermediate node on the list
+ * this new node will point to the one we need to create
+ * and the current
+ */
+
+ pn = fn->parent;
+
+ /* find 1st bit in difference between the 2 addrs.
+
+ See comment in addr_diff: bit may be an invalid value,
+ but if it is >= plen, the value is ignored in any case.
+ */
+
+ bit = addr_diff(addr, &key->addr, addrlen);
+
+ /*
+ * (intermediate)[in]
+ * / \
+ * (new leaf node)[ln] (old node)[fn]
+ */
+ if (plen > bit) {
+ in = node_alloc();
+ ln = node_alloc();
+
+ if (in == NULL || ln == NULL) {
+ if (in)
+ node_free(in);
+ if (ln)
+ node_free(ln);
+ return NULL;
+ }
+
+ /*
+ * new intermediate node.
+ * RTN_RTINFO will
+ * be off since that an address that chooses one of
+ * the branches would not match less specific routes
+ * in the other branch
+ */
+
+ in->fn_bit = bit;
+
+ in->parent = pn;
+ in->leaf = fn->leaf;
+ atomic_inc(&in->leaf->rt6i_ref);
+
+ in->fn_sernum = sernum;
+
+ /* update parent pointer */
+ if (dir)
+ pn->right = in;
+ else
+ pn->left = in;
+
+ ln->fn_bit = plen;
+
+ ln->parent = in;
+ fn->parent = in;
+
+ ln->fn_sernum = sernum;
+
+ if (addr_bit_set(addr, bit)) {
+ in->right = ln;
+ in->left = fn;
+ } else {
+ in->left = ln;
+ in->right = fn;
+ }
+ } else { /* plen <= bit */
+
+ /*
+ * (new leaf node)[ln]
+ * / \
+ * (old node)[fn] NULL
+ */
+
+ ln = node_alloc();
+
+ if (ln == NULL)
+ return NULL;
+
+ ln->fn_bit = plen;
+
+ ln->parent = pn;
+
+ ln->fn_sernum = sernum;
+
+ if (dir)
+ pn->right = ln;
+ else
+ pn->left = ln;
+
+ if (addr_bit_set(&key->addr, plen))
+ ln->right = fn;
+ else
+ ln->left = fn;
+
+ fn->parent = ln;
+ }
+ return ln;
+}
+
+/*
+ * Insert routing information in a node.
+ */
+
+static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt)
+{
+ struct rt6_info *iter = NULL;
+ struct rt6_info **ins;
+
+ ins = &fn->leaf;
+
+ for (iter = fn->leaf; iter; iter=iter->u.next) {
+ /*
+ * Search for duplicates
+ */
+
+ if (iter->rt6i_metric == rt->rt6i_metric) {
+ /*
+ * Same priority level
+ */
+
+ if ((iter->rt6i_dev == rt->rt6i_dev) &&
+ (iter->rt6i_flowr == rt->rt6i_flowr) &&
+ (ipv6_addr_cmp(&iter->rt6i_gateway,
+ &rt->rt6i_gateway) == 0)) {
+ if (!(iter->rt6i_flags&RTF_EXPIRES))
+ return -EEXIST;
+ iter->rt6i_expires = rt->rt6i_expires;
+ if (!(rt->rt6i_flags&RTF_EXPIRES)) {
+ iter->rt6i_flags &= ~RTF_EXPIRES;
+ iter->rt6i_expires = 0;
+ }
+ return -EEXIST;
+ }
+ }
+
+ if (iter->rt6i_metric > rt->rt6i_metric)
+ break;
+
+ ins = &iter->u.next;
+ }
+
+ /*
+ * insert node
+ */
+
+ rt->u.next = iter;
+ *ins = rt;
+ rt->rt6i_node = fn;
+ atomic_inc(&rt->rt6i_ref);
+#ifdef CONFIG_RTNETLINK
+ inet6_rt_notify(RTM_NEWROUTE, rt);
+#endif
+ rt6_stats.fib_rt_entries++;
+
+ if ((fn->fn_flags & RTN_RTINFO) == 0) {
+ rt6_stats.fib_route_nodes++;
+ fn->fn_flags |= RTN_RTINFO;
+ }
+
+ return 0;
+}
+
+static __inline__ void fib6_start_gc(struct rt6_info *rt)
+{
+ if (ip6_fib_timer.expires == 0 &&
+ (rt->rt6i_flags & (RTF_EXPIRES|RTF_CACHE))) {
+ del_timer(&ip6_fib_timer);
+ ip6_fib_timer.expires = jiffies + ip6_rt_gc_interval;
+ add_timer(&ip6_fib_timer);
+ }
+}
+
+/*
+ * Add routing information to the routing tree.
+ * <destination addr>/<source addr>
+ * with source addr info in sub-trees
+ */
+
+int fib6_add(struct fib6_node *root, struct rt6_info *rt)
+{
+ struct fib6_node *fn;
+ int err = -ENOMEM;
+
+ fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr),
+ rt->rt6i_dst.plen, (u8*) &rt->rt6i_dst - (u8*) rt);
+
+ if (fn == NULL)
+ return -ENOMEM;
+
+#ifdef CONFIG_IPV6_SUBTREES
+ if (rt->rt6i_src.plen) {
+ struct fib6_node *sn;
+
+ if (fn->subtree == NULL) {
+ struct fib6_node *sfn;
+
+ /*
+ * Create subtree.
+ *
+ * fn[main tree]
+ * |
+ * sfn[subtree root]
+ * \
+ * sn[new leaf node]
+ */
+
+ /* Create subtree root node */
+ sfn = node_alloc();
+ if (sfn == NULL)
+ goto st_failure;
+
+ sfn->leaf = &ip6_null_entry;
+ atomic_inc(&ip6_null_entry.rt6i_ref);
+ sfn->fn_flags = RTN_ROOT;
+ sfn->fn_sernum = fib6_new_sernum();
+
+ /* Now add the first leaf node to new subtree */
+
+ sn = fib6_add_1(sfn, &rt->rt6i_src.addr,
+ sizeof(struct in6_addr), rt->rt6i_src.plen,
+ (u8*) &rt->rt6i_src - (u8*) rt);
+
+ if (sn == NULL) {
+ /* If it is failed, discard just allocated
+ root, and then (in st_failure) stale node
+ in main tree.
+ */
+ node_free(sfn);
+ goto st_failure;
+ }
+
+ /* Now link new subtree to main tree */
+ sfn->parent = fn;
+ fn->subtree = sfn;
+ if (fn->leaf == NULL) {
+ fn->leaf = rt;
+ atomic_inc(&rt->rt6i_ref);
+ }
+ } else {
+ sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr,
+ sizeof(struct in6_addr), rt->rt6i_src.plen,
+ (u8*) &rt->rt6i_src - (u8*) rt);
+
+ if (sn == NULL)
+ goto st_failure;
+ }
+
+ fn = sn;
+ }
+#endif
+
+ err = fib6_add_rt2node(fn, rt);
+
+ if (err == 0) {
+ fib6_start_gc(rt);
+ if (!(rt->rt6i_flags&RTF_CACHE))
+ fib6_prune_clones(fn, rt);
+ }
+
+ if (err)
+ dst_free(&rt->u.dst);
+ return err;
+
+#ifdef CONFIG_IPV6_SUBTREES
+ /* Subtree creation failed, probably main tree node
+ is orphan. If it is, shot it.
+ */
+st_failure:
+ if (fn && !(fn->fn_flags&RTN_RTINFO|RTN_ROOT))
+ fib_repair_tree(fn);
+ dst_free(&rt->u.dst);
+ return err;
+#endif
+}
+
+/*
+ * Routing tree lookup
+ *
+ */
+
+struct lookup_args {
+ int offset; /* key offset on rt6_info */
+ struct in6_addr *addr; /* search key */
+};
+
+static struct fib6_node * fib6_lookup_1(struct fib6_node *root,
+ struct lookup_args *args)
+{
+ struct fib6_node *fn;
+ int dir;
+
+ /*
+ * Descend on a tree
+ */
+
+ fn = root;
+
+ for (;;) {
+ struct fib6_node *next;
+
+ dir = addr_bit_set(args->addr, fn->fn_bit);
+
+ next = dir ? fn->right : fn->left;
+
+ if (next) {
+ fn = next;
+ continue;
+ }
+
+ break;
+ }
+
+ while ((fn->fn_flags & RTN_ROOT) == 0) {
+#ifdef CONFIG_IPV6_SUBTREES
+ if (fn->subtree) {
+ struct fib6_node *st;
+ struct lookup_args *narg;
+
+ narg = args + 1;
+
+ if (narg->addr) {
+ st = fib6_lookup_1(fn->subtree, narg);
+
+ if (!(st->fn_flags & RTN_ROOT))
+ {
+ return st;
+ }
+ }
+ }
+#endif
+
+ if (fn->fn_flags & RTN_RTINFO) {
+ struct rt6key *key;
+
+ key = (struct rt6key *) ((u8 *) fn->leaf +
+ args->offset);
+
+ if (addr_match(&key->addr, args->addr, key->plen))
+ return fn;
+ }
+
+ fn = fn->parent;
+ }
+
+ return NULL;
+}
+
+struct fib6_node * fib6_lookup(struct fib6_node *root, struct in6_addr *daddr,
+ struct in6_addr *saddr)
+{
+ struct lookup_args args[2];
+ struct rt6_info *rt = NULL;
+ struct fib6_node *fn;
+
+ args[0].offset = (u8*) &rt->rt6i_dst - (u8*) rt;
+ args[0].addr = daddr;
+
+#ifdef CONFIG_IPV6_SUBTREES
+ args[1].offset = (u8*) &rt->rt6i_src - (u8*) rt;
+ args[1].addr = saddr;
+#endif
+
+ fn = fib6_lookup_1(root, args);
+
+ if (fn == NULL)
+ fn = root;
+
+ return fn;
+}
+
+/*
+ * Get node with sepciafied destination prefix (and source prefix,
+ * if subtrees are used)
+ */
+
+
+static struct fib6_node * fib6_locate_1(struct fib6_node *root,
+ struct in6_addr *addr,
+ int plen, int offset)
+{
+ struct fib6_node *fn;
+
+ for (fn = root; fn ; ) {
+ struct rt6key *key = (struct rt6key *)((u8 *)fn->leaf + offset);
+
+ /*
+ * Prefix match
+ */
+ if (plen < fn->fn_bit ||
+ !addr_match(&key->addr, addr, fn->fn_bit))
+ return NULL;
+
+ if (plen == fn->fn_bit)
+ return fn;
+
+ /*
+ * We have more bits to go
+ */
+ if (addr_bit_set(addr, fn->fn_bit))
+ fn = fn->right;
+ else
+ fn = fn->left;
+ }
+ return NULL;
+}
+
+struct fib6_node * fib6_locate(struct fib6_node *root,
+ struct in6_addr *daddr, int dst_len,
+ struct in6_addr *saddr, int src_len)
+{
+ struct rt6_info *rt = NULL;
+ struct fib6_node *fn;
+
+ fn = fib6_locate_1(root, daddr, dst_len,
+ (u8*) &rt->rt6i_dst - (u8*) rt);
+
+#ifdef CONFIG_IPV6_SUBTREES
+ if (src_len) {
+ BUG_TRAP(saddr!=NULL);
+ if (fn != NULL)
+ fn = fn->subtree;
+ if (fn)
+ fn = fib6_locate_1(fn, saddr, src_len,
+ (u8*) &rt->rt6i_src - (u8*) rt);
+ }
+#endif
+
+ if (fn && fn->fn_flags&RTN_RTINFO)
+ return fn;
+
+ return NULL;
+}
+
+
+/*
+ * Deletion
+ *
+ */
+
+static struct rt6_info * fib6_find_prefix(struct fib6_node *fn)
+{
+ if (fn->fn_flags&RTN_ROOT)
+ return &ip6_null_entry;
+
+ while(fn) {
+ if(fn->left)
+ return fn->left->leaf;
+
+ if(fn->right)
+ return fn->right->leaf;
+
+ fn = SUBTREE(fn);
+ }
+ return NULL;
+}
+
+/*
+ * Called to trim the tree of intermediate nodes when possible. "fn"
+ * is the node we want to try and remove.
+ */
+
+static void fib6_repair_tree(struct fib6_node *fn)
+{
+ int children;
+ int nstate;
+ struct fib6_node *child, *pn;
+ struct fib6_walker_t *w;
+ int iter = 0;
+
+ for (;;) {
+ RT6_TRACE("fixing tree: plen=%d iter=%d\n", fn->fn_bit, iter);
+ iter++;
+
+ BUG_TRAP(!(fn->fn_flags&RTN_RTINFO));
+ BUG_TRAP(!(fn->fn_flags&RTN_TL_ROOT));
+ BUG_TRAP(fn->leaf==NULL);
+
+ children = 0;
+ child = NULL;
+ if (fn->right) child = fn->right, children |= 1;
+ if (fn->left) child = fn->left, children |= 2;
+
+ if (children == 3 || SUBTREE(fn)
+#ifdef CONFIG_IPV6_SUBTREES
+ /* Subtree root (i.e. fn) may have one child */
+ || (children && fn->fn_flags&RTN_ROOT)
+#endif
+ ) {
+ fn->leaf = fib6_find_prefix(fn);
+#if RT6_DEBUG >= 2
+ if (fn->leaf==NULL) {
+ BUG_TRAP(fn->leaf);
+ fn->leaf = &ip6_null_entry;
+ }
+#endif
+ atomic_inc(&fn->leaf->rt6i_ref);
+ return;
+ }
+
+ pn = fn->parent;
+#ifdef CONFIG_IPV6_SUBTREES
+ if (SUBTREE(pn) == fn) {
+ BUG_TRAP(fn->fn_flags&RTN_ROOT);
+ SUBTREE(pn) = NULL;
+ nstate = FWS_L;
+ } else {
+ BUG_TRAP(!(fn->fn_flags&RTN_ROOT));
+#endif
+ if (pn->right == fn) pn->right = child;
+ else if (pn->left == fn) pn->left = child;
+#if RT6_DEBUG >= 2
+ else BUG_TRAP(0);
+#endif
+ if (child)
+ child->parent = pn;
+ nstate = FWS_R;
+#ifdef CONFIG_IPV6_SUBTREES
+ }
+#endif
+
+ FOR_WALKERS(w) {
+ if (child == NULL) {
+ if (w->root == fn) {
+ w->root = w->node = NULL;
+ RT6_TRACE("W %p adjusted by delroot 1\n", w);
+ } else if (w->node == fn) {
+ RT6_TRACE("W %p adjusted by delnode 1, s=%d/%d\n", w, w->state, nstate);
+ w->node = pn;
+ w->state = nstate;
+ }
+ } else {
+ if (w->root == fn) {
+ w->root = child;
+ RT6_TRACE("W %p adjusted by delroot 2\n", w);
+ }
+ if (w->node == fn) {
+ w->node = child;
+ if (children&2) {
+ RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state);
+ w->state = w->state>=FWS_R ? FWS_U : FWS_INIT;
+ } else {
+ RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state);
+ w->state = w->state>=FWS_C ? FWS_U : FWS_INIT;
+ }
+ }
+ }
+ }
+
+ node_free(fn);
+ if (pn->fn_flags&RTN_RTINFO || SUBTREE(pn))
+ return;
+
+ rt6_release(pn->leaf);
+ pn->leaf = NULL;
+ fn = pn;
+ }
+}
+
+static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp)
+{
+ struct fib6_walker_t *w;
+ struct rt6_info *rt = *rtp;
+
+ RT6_TRACE("fib6_del_route\n");
+
+ /* Unlink it */
+ *rtp = rt->u.next;
+ rt->rt6i_node = NULL;
+ rt6_stats.fib_rt_entries--;
+
+ /* Adjust walkers */
+ FOR_WALKERS(w) {
+ if (w->state == FWS_C && w->leaf == rt) {
+ RT6_TRACE("walker %p adjusted by delroute\n", w);
+ w->leaf = rt->u.next;
+ if (w->leaf == NULL)
+ w->state = FWS_U;
+ }
+ }
+
+ rt->u.next = NULL;
+
+ /* If it was last route, expunge its radix tree node */
+ if (fn->leaf == NULL) {
+ fn->fn_flags &= ~RTN_RTINFO;
+ rt6_stats.fib_route_nodes--;
+ fib6_repair_tree(fn);
+ }
+
+#ifdef CONFIG_RTNETLINK
+ inet6_rt_notify(RTM_DELROUTE, rt);
+#endif
+ rt6_release(rt);
+}
+
+int fib6_del(struct rt6_info *rt)
+{
+ struct fib6_node *fn = rt->rt6i_node;
+ struct rt6_info **rtp;
+
+#if RT6_DEBUG >= 2
+ if (rt->u.dst.obsolete>0) {
+ BUG_TRAP(rt->u.dst.obsolete>0);
+ return -EFAULT;
+ }
+#endif
+ if (fn == NULL || rt == &ip6_null_entry)
+ return -ENOENT;
+
+ BUG_TRAP(fn->fn_flags&RTN_RTINFO);
+
+ if (!(rt->rt6i_flags&RTF_CACHE))
+ fib6_prune_clones(fn, rt);
+
+ /*
+ * Walk the leaf entries looking for ourself
+ */
+
+ for (rtp = &fn->leaf; *rtp; rtp = &(*rtp)->u.next) {
+ if (*rtp == rt) {
+ fib6_del_route(fn, rtp);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+/*
+ * Tree transversal function.
+ *
+ * Certainly, it is not interrupt safe.
+ * However, it is internally reenterable wrt itself and fib6_add/fib6_del.
+ * It means, that we can modify tree during walking
+ * and use this function for garbage collection, clone pruning,
+ * cleaning tree when a device goes down etc. etc.
+ *
+ * It guarantees that every node will be traversed,
+ * and that it will be traversed only once.
+ *
+ * Callback function w->func may return:
+ * 0 -> continue walking.
+ * positive value -> walking is suspended (used by tree dumps,
+ * and probably by gc, if it will be split to several slices)
+ * negative value -> terminate walking.
+ *
+ * The function itself returns:
+ * 0 -> walk is complete.
+ * >0 -> walk is incomplete (i.e. suspended)
+ * <0 -> walk is terminated by an error.
+ */
+
+int fib6_walk_continue(struct fib6_walker_t *w)
+{
+ struct fib6_node *fn, *pn;
+
+ for (;;) {
+ fn = w->node;
+ if (fn == NULL)
+ return 0;
+
+ if (w->prune && fn != w->root &&
+ fn->fn_flags&RTN_RTINFO && w->state < FWS_C) {
+ w->state = FWS_C;
+ w->leaf = fn->leaf;
+ }
+ switch (w->state) {
+#ifdef CONFIG_IPV6_SUBTREES
+ case FWS_S:
+ if (SUBTREE(fn)) {
+ w->node = SUBTREE(fn);
+ continue;
+ }
+ w->state = FWS_L;
+#endif
+ case FWS_L:
+ if (fn->left) {
+ w->node = fn->left;
+ w->state = FWS_INIT;
+ continue;
+ }
+ w->state = FWS_R;
+ case FWS_R:
+ if (fn->right) {
+ w->node = fn->right;
+ w->state = FWS_INIT;
+ continue;
+ }
+ w->state = FWS_C;
+ w->leaf = fn->leaf;
+ case FWS_C:
+ if (w->leaf && fn->fn_flags&RTN_RTINFO) {
+ int err = w->func(w);
+ if (err)
+ return err;
+ continue;
+ }
+ w->state = FWS_U;
+ case FWS_U:
+ if (fn == w->root)
+ return 0;
+ pn = fn->parent;
+ w->node = pn;
+#ifdef CONFIG_IPV6_SUBTREES
+ if (SUBTREE(pn) == fn) {
+ BUG_TRAP(fn->fn_flags&RTN_ROOT);
+ w->state = FWS_L;
+ continue;
+ }
+#endif
+ if (pn->left == fn) {
+ w->state = FWS_R;
+ continue;
+ }
+ if (pn->right == fn) {
+ w->state = FWS_C;
+ w->leaf = w->node->leaf;
+ continue;
+ }
+#if RT6_DEBUG >= 2
+ BUG_TRAP(0);
+#endif
+ }
+ }
+}
+
+int fib6_walk(struct fib6_walker_t *w)
+{
+ int res;
+
+ w->state = FWS_INIT;
+ w->node = w->root;
+
+ fib6_walker_link(w);
+ res = fib6_walk_continue(w);
+ if (res <= 0)
+ fib6_walker_unlink(w);
+ return res;
+}
+
+static int fib6_clean_node(struct fib6_walker_t *w)
+{
+ int res;
+ struct rt6_info *rt;
+ struct fib6_cleaner_t *c = (struct fib6_cleaner_t*)w;
+
+ for (rt = w->leaf; rt; rt = rt->u.next) {
+ res = c->func(rt, c->arg);
+ if (res < 0) {
+ w->leaf = rt;
+ res = fib6_del(rt);
+ if (res) {
+#if RT6_DEBUG >= 2
+ printk(KERN_DEBUG "fib6_clean_node: del failed: rt=%p@%p err=%d\n", rt, rt->rt6i_node, res);
+#endif
+ continue;
+ }
+ return 0;
+ }
+ BUG_TRAP(res==0);
+ }
+ w->leaf = rt;
+ return 0;
+}
+
+/*
+ * Convenient frontend to tree walker.
+ *
+ * func is called on each route.
+ * It may return -1 -> delete this route.
+ * 0 -> continue walking
+ *
+ * prune==1 -> only immediate children of node (certainly,
+ * ignoring pure split nodes) will be scanned.
+ */
+
+void fib6_clean_tree(struct fib6_node *root,
+ int (*func)(struct rt6_info *, void *arg),
+ int prune, void *arg)
+{
+ struct fib6_cleaner_t c;
+
+ c.w.root = root;
+ c.w.func = fib6_clean_node;
+ c.w.prune = prune;
+ c.func = func;
+ c.arg = arg;
+
+ start_bh_atomic();
+ fib6_walk(&c.w);
+ end_bh_atomic();
+}
+
+static int fib6_prune_clone(struct rt6_info *rt, void *arg)
+{
+ if (rt->rt6i_flags & RTF_CACHE) {
+ RT6_TRACE("pruning clone %p\n", rt);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void fib6_prune_clones(struct fib6_node *fn, struct rt6_info *rt)
+{
+ fib6_clean_tree(fn, fib6_prune_clone, 1, rt);
+}
+
+/*
+ * Garbage collection
+ */
+
+static struct fib6_gc_args
+{
+ int timeout;
+ int more;
+} gc_args;
+
+static int fib6_age(struct rt6_info *rt, void *arg)
+{
+ unsigned long now = jiffies;
+
+ /* Age clones. Note, that clones are aged out
+ only if they are not in use now.
+ */
+
+ if (rt->rt6i_flags & RTF_CACHE) {
+ if (atomic_read(&rt->u.dst.use) == 0 &&
+ (long)(now - rt->u.dst.lastuse) >= gc_args.timeout) {
+ RT6_TRACE("aging clone %p\n", rt);
+ return -1;
+ }
+ gc_args.more++;
+ }
+
+ /*
+ * check addrconf expiration here.
+ * They are expired even if they are in use.
+ */
+
+ if (rt->rt6i_flags&RTF_EXPIRES && rt->rt6i_expires) {
+ if ((long)(now - rt->rt6i_expires) > 0) {
+ RT6_TRACE("expiring %p\n", rt);
+ return -1;
+ }
+ gc_args.more++;
+ }
+
+ return 0;
+}
+
+void fib6_run_gc(unsigned long dummy)
+{
+ if (dummy != ~0UL)
+ gc_args.timeout = (int)dummy;
+ else
+ gc_args.timeout = ip6_rt_gc_interval;
+
+ gc_args.more = 0;
+
+ fib6_clean_tree(&ip6_routing_table, fib6_age, 0, NULL);
+
+ del_timer(&ip6_fib_timer);
+
+ ip6_fib_timer.expires = 0;
+ if (gc_args.more) {
+ ip6_fib_timer.expires = jiffies + ip6_rt_gc_interval;
+ add_timer(&ip6_fib_timer);
+ }
+}
+
+#ifdef MODULE
+void fib6_gc_cleanup(void)
+{
+ del_timer(&ip6_fib_timer);
+}
+#endif
diff --git a/pfinet/linux-src/net/ipv6/ip6_flowlabel.c b/pfinet/linux-src/net/ipv6/ip6_flowlabel.c
new file mode 100644
index 00000000..4a34b878
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/ip6_flowlabel.c
@@ -0,0 +1,627 @@
+/*
+ * ip6_flowlabel.c IPv6 flowlabel manager.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/route.h>
+#include <linux/proc_fs.h>
+
+#include <net/sock.h>
+
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+#include <net/protocol.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+#include <net/rawv6.h>
+#include <net/icmp.h>
+#include <net/transp_v6.h>
+
+#include <asm/uaccess.h>
+
+#define FL_MIN_LINGER 6 /* Minimal linger. It is set to 6sec specified
+ in old IPv6 RFC. Well, it was reasonable value.
+ */
+#define FL_MAX_LINGER 60 /* Maximal linger timeout */
+
+/* FL hash table */
+
+#define FL_MAX_PER_SOCK 32
+#define FL_MAX_SIZE 4096
+#define FL_HASH_MASK 255
+#define FL_HASH(l) (ntohl(l)&FL_HASH_MASK)
+
+static atomic_t fl_size = ATOMIC_INIT(0);
+static struct ip6_flowlabel *fl_ht[FL_HASH_MASK+1];
+
+static struct timer_list ip6_fl_gc_timer;
+
+/* FL hash table lock: it protects only of GC */
+
+static atomic_t ip6_fl_lock = ATOMIC_INIT(0);
+
+static __inline__ void fl_lock(void)
+{
+ atomic_inc(&ip6_fl_lock);
+ synchronize_bh();
+}
+
+static __inline__ void fl_unlock(void)
+{
+ atomic_dec(&ip6_fl_lock);
+}
+
+static struct ip6_flowlabel * fl_lookup(u32 label)
+{
+ struct ip6_flowlabel *fl;
+
+ fl_lock();
+ for (fl=fl_ht[FL_HASH(label)]; fl; fl = fl->next) {
+ if (fl->label == label) {
+ atomic_inc(&fl->users);
+ break;
+ }
+ }
+ fl_unlock();
+ return fl;
+}
+
+static void fl_free(struct ip6_flowlabel *fl)
+{
+ if (fl->opt)
+ kfree(fl->opt);
+ kfree(fl);
+}
+
+static void fl_release(struct ip6_flowlabel *fl)
+{
+ fl_lock();
+ fl->lastuse = jiffies;
+ if (atomic_dec_and_test(&fl->users)) {
+ unsigned long ttd = fl->lastuse + fl->linger;
+ if ((long)(ttd - fl->expires) > 0)
+ fl->expires = ttd;
+ ttd = fl->expires;
+ if (fl->opt && fl->share == IPV6_FL_S_EXCL) {
+ struct ipv6_txoptions *opt = fl->opt;
+ fl->opt = NULL;
+ kfree(opt);
+ }
+ if (!del_timer(&ip6_fl_gc_timer) ||
+ (long)(ip6_fl_gc_timer.expires - ttd) > 0)
+ ip6_fl_gc_timer.expires = ttd;
+ add_timer(&ip6_fl_gc_timer);
+ }
+ fl_unlock();
+}
+
+static void ip6_fl_gc(unsigned long dummy)
+{
+ int i;
+ unsigned long now = jiffies;
+ unsigned long sched = 0;
+
+ if (atomic_read(&ip6_fl_lock)) {
+ ip6_fl_gc_timer.expires = now + HZ/10;
+ add_timer(&ip6_fl_gc_timer);
+ return;
+ }
+
+ for (i=0; i<=FL_HASH_MASK; i++) {
+ struct ip6_flowlabel *fl, **flp;
+ flp = &fl_ht[i];
+ while ((fl=*flp) != NULL) {
+ if (atomic_read(&fl->users) == 0) {
+ unsigned long ttd = fl->lastuse + fl->linger;
+ if ((long)(ttd - fl->expires) > 0)
+ fl->expires = ttd;
+ ttd = fl->expires;
+ if ((long)(now - ttd) >= 0) {
+ *flp = fl->next;
+ fl_free(fl);
+ atomic_dec(&fl_size);
+ continue;
+ }
+ if (!sched || (long)(ttd - sched) < 0)
+ sched = ttd;
+ }
+ flp = &fl->next;
+ }
+ }
+ if (!sched && atomic_read(&fl_size))
+ sched = now + FL_MAX_LINGER;
+ if (sched) {
+ ip6_fl_gc_timer.expires = sched;
+ add_timer(&ip6_fl_gc_timer);
+ }
+}
+
+static int fl_intern(struct ip6_flowlabel *fl, __u32 label)
+{
+ fl->label = label & IPV6_FLOWLABEL_MASK;
+
+ fl_lock();
+ if (label == 0) {
+ for (;;) {
+ fl->label = htonl(net_random())&IPV6_FLOWLABEL_MASK;
+ if (fl->label) {
+ struct ip6_flowlabel *lfl;
+ lfl = fl_lookup(fl->label);
+ if (lfl == NULL)
+ break;
+ fl_release(lfl);
+ }
+ }
+ }
+
+ fl->lastuse = jiffies;
+ fl->next = fl_ht[FL_HASH(fl->label)];
+ fl_ht[FL_HASH(fl->label)] = fl;
+ atomic_inc(&fl_size);
+ fl_unlock();
+ return 0;
+}
+
+
+
+/* Socket flowlabel lists */
+
+struct ip6_flowlabel * fl6_sock_lookup(struct sock *sk, u32 label)
+{
+ struct ipv6_fl_socklist *sfl;
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+
+ label &= IPV6_FLOWLABEL_MASK;
+
+ for (sfl=np->ipv6_fl_list; sfl; sfl = sfl->next) {
+ struct ip6_flowlabel *fl = sfl->fl;
+ if (fl->label == label) {
+ fl->lastuse = jiffies;
+ atomic_inc(&fl->users);
+ return fl;
+ }
+ }
+ return NULL;
+}
+
+void fl6_free_socklist(struct sock *sk)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct ipv6_fl_socklist *sfl;
+
+ while ((sfl = np->ipv6_fl_list) != NULL) {
+ np->ipv6_fl_list = sfl->next;
+ fl_release(sfl->fl);
+ kfree(sfl);
+ }
+}
+
+/* Service routines */
+
+
+/*
+ It is the only difficult place. flowlabel enforces equal headers
+ before and including routing header, however user may supply options
+ following rthdr.
+ */
+
+struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space,
+ struct ip6_flowlabel * fl,
+ struct ipv6_txoptions * fopt)
+{
+ struct ipv6_txoptions * fl_opt = fl->opt;
+
+ if (fopt == NULL || fopt->opt_flen == 0)
+ return fl_opt;
+
+ if (fl_opt != NULL) {
+ opt_space->hopopt = fl_opt->hopopt;
+ opt_space->dst0opt = fl_opt->dst0opt;
+ opt_space->srcrt = fl_opt->srcrt;
+ opt_space->opt_nflen = fl_opt->opt_nflen;
+ } else {
+ if (fopt->opt_nflen == 0)
+ return fopt;
+ opt_space->hopopt = NULL;
+ opt_space->dst0opt = NULL;
+ opt_space->srcrt = NULL;
+ opt_space->opt_nflen = 0;
+ }
+ opt_space->dst1opt = fopt->dst1opt;
+ opt_space->auth = fopt->auth;
+ opt_space->opt_flen = fopt->opt_flen;
+ return opt_space;
+}
+
+static __u32 check_linger(__u16 ttl)
+{
+ if (ttl < FL_MIN_LINGER)
+ return FL_MIN_LINGER*HZ;
+ if (ttl > FL_MAX_LINGER && !capable(CAP_NET_ADMIN))
+ return 0;
+ return ttl*HZ;
+}
+
+static int fl6_renew(struct ip6_flowlabel *fl, unsigned linger, unsigned expires)
+{
+ linger = check_linger(linger);
+ if (!linger)
+ return -EPERM;
+ expires = check_linger(expires);
+ if (!expires)
+ return -EPERM;
+ fl->lastuse = jiffies;
+ if (fl->linger < linger)
+ fl->linger = linger;
+ if (expires < fl->linger)
+ expires = fl->linger;
+ if ((long)(fl->expires - (fl->lastuse+expires)) < 0)
+ fl->expires = fl->lastuse + expires;
+ return 0;
+}
+
+static struct ip6_flowlabel *
+fl_create(struct in6_flowlabel_req *freq, char *optval, int optlen, int *err_p)
+{
+ struct ip6_flowlabel *fl;
+ int olen;
+ int addr_type;
+ int err;
+
+ err = -ENOMEM;
+ fl = kmalloc(sizeof(*fl), GFP_KERNEL);
+ if (fl == NULL)
+ goto done;
+ memset(fl, 0, sizeof(*fl));
+
+ olen = optlen - CMSG_ALIGN(sizeof(*freq));
+ if (olen > 0) {
+ struct msghdr msg;
+ struct flowi flowi;
+ int junk;
+
+ err = -ENOMEM;
+ fl->opt = kmalloc(sizeof(*fl->opt) + olen, GFP_KERNEL);
+ if (fl->opt == NULL)
+ goto done;
+
+ memset(fl->opt, 0, sizeof(*fl->opt));
+ fl->opt->tot_len = sizeof(*fl->opt) + olen;
+ err = -EFAULT;
+ if (copy_from_user(fl->opt+1, optval+CMSG_ALIGN(sizeof(*freq)), olen))
+ goto done;
+
+ msg.msg_controllen = olen;
+ msg.msg_control = (void*)(fl->opt+1);
+ flowi.oif = 0;
+
+ err = datagram_send_ctl(&msg, &flowi, fl->opt, &junk);
+ if (err)
+ goto done;
+ err = -EINVAL;
+ if (fl->opt->opt_flen)
+ goto done;
+ if (fl->opt->opt_nflen == 0) {
+ kfree(fl->opt);
+ fl->opt = NULL;
+ }
+ }
+
+ fl->expires = jiffies;
+ err = fl6_renew(fl, freq->flr_linger, freq->flr_expires);
+ if (err)
+ goto done;
+ fl->share = freq->flr_share;
+ addr_type = ipv6_addr_type(&freq->flr_dst);
+ if ((addr_type&IPV6_ADDR_MAPPED)
+ || addr_type == IPV6_ADDR_ANY)
+ goto done;
+ ipv6_addr_copy(&fl->dst, &freq->flr_dst);
+ atomic_set(&fl->users, 1);
+ switch (fl->share) {
+ case IPV6_FL_S_EXCL:
+ case IPV6_FL_S_ANY:
+ break;
+ case IPV6_FL_S_PROCESS:
+ fl->owner = current->pid;
+ break;
+ case IPV6_FL_S_USER:
+#ifdef _HURD_
+ /* FIXME
+ * Which euid shall be assigned? Where to get it,
+ * `struct task_struct' doesn't have a `euid'.
+ */
+#else
+ fl->owner = current->euid;
+ break;
+#endif
+ default:
+ err = -EINVAL;
+ goto done;
+ }
+ return fl;
+
+done:
+ if (fl)
+ fl_free(fl);
+ *err_p = err;
+ return NULL;
+}
+
+static int mem_check(struct sock *sk)
+{
+ struct ipv6_fl_socklist *sfl;
+ int room = FL_MAX_SIZE - atomic_read(&fl_size);
+ int count = 0;
+
+ if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK)
+ return 0;
+
+ for (sfl = sk->net_pinfo.af_inet6.ipv6_fl_list; sfl; sfl = sfl->next)
+ count++;
+
+ if (room <= 0 ||
+ ((count >= FL_MAX_PER_SOCK ||
+ (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4)
+ && !capable(CAP_NET_ADMIN)))
+ return -ENOBUFS;
+
+ return 0;
+}
+
+static int ipv6_hdr_cmp(struct ipv6_opt_hdr *h1, struct ipv6_opt_hdr *h2)
+{
+ if (h1 == h2)
+ return 0;
+ if (h1 == NULL || h2 == NULL)
+ return 1;
+ if (h1->hdrlen != h2->hdrlen)
+ return 1;
+ return memcmp(h1+1, h2+1, ((h1->hdrlen+1)<<3) - sizeof(*h1));
+}
+
+static int ipv6_opt_cmp(struct ipv6_txoptions *o1, struct ipv6_txoptions *o2)
+{
+ if (o1 == o2)
+ return 0;
+ if (o1 == NULL || o2 == NULL)
+ return 1;
+ if (o1->opt_nflen != o2->opt_nflen)
+ return 1;
+ if (ipv6_hdr_cmp(o1->hopopt, o2->hopopt))
+ return 1;
+ if (ipv6_hdr_cmp(o1->dst0opt, o2->dst0opt))
+ return 1;
+ if (ipv6_hdr_cmp((struct ipv6_opt_hdr *)o1->srcrt, (struct ipv6_opt_hdr *)o2->srcrt))
+ return 1;
+ return 0;
+}
+
+int ipv6_flowlabel_opt(struct sock *sk, char *optval, int optlen)
+{
+ int err;
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct in6_flowlabel_req freq;
+ struct ipv6_fl_socklist *sfl1=NULL;
+ struct ipv6_fl_socklist *sfl, **sflp;
+ struct ip6_flowlabel *fl;
+
+ if (optlen < sizeof(freq))
+ return -EINVAL;
+
+ if (copy_from_user(&freq, optval, sizeof(freq)))
+ return -EFAULT;
+
+ switch (freq.flr_action) {
+ case IPV6_FL_A_PUT:
+ for (sflp = &np->ipv6_fl_list; (sfl=*sflp)!=NULL; sflp = &sfl->next) {
+ if (sfl->fl->label == freq.flr_label) {
+ if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK))
+ np->flow_label &= ~IPV6_FLOWLABEL_MASK;
+ *sflp = sfl->next;
+ synchronize_bh();
+ fl_release(sfl->fl);
+ kfree(sfl);
+ return 0;
+ }
+ }
+ return -ESRCH;
+
+ case IPV6_FL_A_RENEW:
+ for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) {
+ if (sfl->fl->label == freq.flr_label)
+ return fl6_renew(sfl->fl, freq.flr_linger, freq.flr_expires);
+ }
+ if (freq.flr_share == IPV6_FL_S_NONE && capable(CAP_NET_ADMIN)) {
+ fl = fl_lookup(freq.flr_label);
+ if (fl) {
+ err = fl6_renew(fl, freq.flr_linger, freq.flr_expires);
+ fl_release(fl);
+ return err;
+ }
+ }
+ return -ESRCH;
+
+ case IPV6_FL_A_GET:
+ if (freq.flr_label & ~IPV6_FLOWLABEL_MASK)
+ return -EINVAL;
+
+ fl = fl_create(&freq, optval, optlen, &err);
+ if (fl == NULL)
+ return err;
+ sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);
+
+ if (freq.flr_label) {
+ struct ip6_flowlabel *fl1 = NULL;
+
+ err = -EEXIST;
+ for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) {
+ if (sfl->fl->label == freq.flr_label) {
+ if (freq.flr_flags&IPV6_FL_F_EXCL)
+ goto done;
+ fl1 = sfl->fl;
+ atomic_inc(&fl->users);
+ break;
+ }
+ }
+
+ if (fl1 == NULL)
+ fl1 = fl_lookup(freq.flr_label);
+ if (fl1) {
+ err = -EEXIST;
+ if (freq.flr_flags&IPV6_FL_F_EXCL)
+ goto release;
+ err = -EPERM;
+ if (fl1->share == IPV6_FL_S_EXCL ||
+ fl1->share != fl->share ||
+ fl1->owner != fl->owner)
+ goto release;
+
+ err = -EINVAL;
+ if (ipv6_addr_cmp(&fl1->dst, &fl->dst) ||
+ ipv6_opt_cmp(fl1->opt, fl->opt))
+ goto release;
+
+ err = -ENOMEM;
+ if (sfl1 == NULL)
+ goto release;
+ if (fl->linger > fl1->linger)
+ fl1->linger = fl->linger;
+ if ((long)(fl->expires - fl1->expires) > 0)
+ fl1->expires = fl->expires;
+ sfl1->fl = fl1;
+ sfl1->next = np->ipv6_fl_list;
+ np->ipv6_fl_list = sfl1;
+ synchronize_bh();
+ fl_free(fl);
+ return 0;
+
+release:
+ fl_release(fl1);
+ goto done;
+ }
+ }
+ err = -ENOENT;
+ if (!(freq.flr_flags&IPV6_FL_F_CREATE))
+ goto done;
+
+ err = -ENOMEM;
+ if (sfl1 == NULL || (err = mem_check(sk)) != 0)
+ goto done;
+
+ err = fl_intern(fl, freq.flr_label);
+ if (err)
+ goto done;
+
+ /* Do not check for fault */
+ if (!freq.flr_label)
+ copy_to_user(optval + ((u8*)&freq.flr_label - (u8*)&freq), &fl->label, sizeof(fl->label));
+
+ sfl1->fl = fl;
+ sfl1->next = np->ipv6_fl_list;
+ np->ipv6_fl_list = sfl1;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+
+done:
+ if (fl)
+ fl_free(fl);
+ if (sfl1)
+ kfree(sfl1);
+ return err;
+}
+
+#ifdef CONFIG_PROC_FS
+
+
+static int ip6_fl_read_proc(char *buffer, char **start, off_t offset,
+ int length, int *eof, void *data)
+{
+ off_t pos=0;
+ off_t begin=0;
+ int len=0;
+ int i, k;
+ struct ip6_flowlabel *fl;
+
+ len+= sprintf(buffer,"Label S Owner Users Linger Expires "
+ "Dst Opt\n");
+
+ fl_lock();
+ for (i=0; i<=FL_HASH_MASK; i++) {
+ for (fl = fl_ht[i]; fl; fl = fl->next) {
+ len+=sprintf(buffer+len,"%05X %-1d %-6d %-6d %-6d %-8ld ",
+ (unsigned)ntohl(fl->label),
+ fl->share,
+ (unsigned)fl->owner,
+ atomic_read(&fl->users),
+ fl->linger/HZ,
+ (long)(fl->expires - jiffies)/HZ);
+
+ for (k=0; k<16; k++)
+ len+=sprintf(buffer+len, "%02x", fl->dst.s6_addr[k]);
+ buffer[len++]=' ';
+ len+=sprintf(buffer+len, "%-4d", fl->opt ? fl->opt->opt_nflen : 0);
+ buffer[len++]='\n';
+
+ pos=begin+len;
+ if(pos<offset) {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ goto done;
+ }
+ }
+ *eof = 1;
+
+done:
+ fl_unlock();
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ if(len<0)
+ len=0;
+ return len;
+}
+#endif
+
+
+void ip6_flowlabel_init()
+{
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *ent;
+#endif
+
+ init_timer(&ip6_fl_gc_timer);
+ ip6_fl_gc_timer.function = ip6_fl_gc;
+#ifdef CONFIG_PROC_FS
+ ent = create_proc_entry("net/ip6_flowlabel", 0, 0);
+ ent->read_proc = ip6_fl_read_proc;
+#endif
+}
+
+void ip6_flowlabel_cleanup()
+{
+ del_timer(&ip6_fl_gc_timer);
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("net/ip6_flowlabel", 0);
+#endif
+}
diff --git a/pfinet/linux-src/net/ipv6/ip6_input.c b/pfinet/linux-src/net/ipv6/ip6_input.c
new file mode 100644
index 00000000..c4a51831
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/ip6_input.c
@@ -0,0 +1,284 @@
+/*
+ * IPv6 input
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ * Ian P. Morris <I.P.Morris@soton.ac.uk>
+ *
+ * $Id: ip6_input.c,v 1.1 2007/10/08 21:12:30 stesie Exp $
+ *
+ * Based in linux/net/ipv4/ip_input.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/sched.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+#include <linux/icmpv6.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/transp_v6.h>
+#include <net/rawv6.h>
+#include <net/ndisc.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+
+
+int ipv6_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct ipv6hdr *hdr;
+ u32 pkt_len;
+
+ if (skb->pkt_type == PACKET_OTHERHOST)
+ goto drop;
+
+ ipv6_statistics.Ip6InReceives++;
+
+ /* Store incoming device index. When the packet will
+ be queued, we cannot refer to skb->dev anymore.
+ */
+ ((struct inet6_skb_parm *)skb->cb)->iif = dev->ifindex;
+
+ hdr = skb->nh.ipv6h;
+
+ if (skb->len < sizeof(struct ipv6hdr) || hdr->version != 6)
+ goto err;
+
+ pkt_len = ntohs(hdr->payload_len);
+
+ /* pkt_len may be zero if Jumbo payload option is present */
+ if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) {
+ if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
+ goto truncated;
+ skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
+ }
+
+ if (hdr->nexthdr == NEXTHDR_HOP) {
+ skb->h.raw = (u8*)(hdr+1);
+ if (!ipv6_parse_hopopts(skb, &hdr->nexthdr)) {
+ ipv6_statistics.Ip6InHdrErrors++;
+ return 0;
+ }
+ }
+
+ if (skb->dst == NULL)
+ ip6_route_input(skb);
+
+ return skb->dst->input(skb);
+
+truncated:
+ ipv6_statistics.Ip6InTruncatedPkts++;
+err:
+ ipv6_statistics.Ip6InHdrErrors++;
+drop:
+ kfree_skb(skb);
+ return 0;
+}
+
+/*
+ * 0 - deliver
+ * 1 - block
+ */
+static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb)
+{
+ struct icmp6hdr *icmph;
+ struct raw6_opt *opt;
+
+ opt = &sk->tp_pinfo.tp_raw;
+ icmph = (struct icmp6hdr *) (skb->nh.ipv6h + 1);
+ return test_bit(icmph->icmp6_type, &opt->filter);
+}
+
+/*
+ * demultiplex raw sockets.
+ * (should consider queueing the skb in the sock receive_queue
+ * without calling rawv6.c)
+ */
+static struct sock * ipv6_raw_deliver(struct sk_buff *skb,
+ int nexthdr, unsigned long len)
+{
+ struct in6_addr *saddr;
+ struct in6_addr *daddr;
+ struct sock *sk, *sk2;
+ __u8 hash;
+
+ saddr = &skb->nh.ipv6h->saddr;
+ daddr = saddr + 1;
+
+ hash = nexthdr & (MAX_INET_PROTOS - 1);
+
+ sk = raw_v6_htable[hash];
+
+ /*
+ * The first socket found will be delivered after
+ * delivery to transport protocols.
+ */
+
+ if (sk == NULL)
+ return NULL;
+
+ sk = raw_v6_lookup(sk, nexthdr, daddr, saddr);
+
+ if (sk) {
+ sk2 = sk;
+
+ while ((sk2 = raw_v6_lookup(sk2->next, nexthdr, daddr, saddr))) {
+ struct sk_buff *buff;
+
+ if (nexthdr == IPPROTO_ICMPV6 &&
+ icmpv6_filter(sk2, skb))
+ continue;
+
+ buff = skb_clone(skb, GFP_ATOMIC);
+ if (buff)
+ rawv6_rcv(sk2, buff, len);
+ }
+ }
+
+ if (sk && nexthdr == IPPROTO_ICMPV6 && icmpv6_filter(sk, skb))
+ sk = NULL;
+
+ return sk;
+}
+
+/*
+ * Deliver the packet to the host
+ */
+
+int ip6_input(struct sk_buff *skb)
+{
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ struct inet6_protocol *ipprot;
+ struct sock *raw_sk;
+ __u8 *nhptr;
+ int nexthdr;
+ int found = 0;
+ u8 hash;
+ int len;
+
+ skb->h.raw = skb->nh.raw + sizeof(struct ipv6hdr);
+
+ /*
+ * Parse extension headers
+ */
+
+ nexthdr = hdr->nexthdr;
+ nhptr = &hdr->nexthdr;
+
+ /* Skip hop-by-hop options, they are already parsed. */
+ if (nexthdr == NEXTHDR_HOP) {
+ nhptr = (u8*)(hdr+1);
+ nexthdr = *nhptr;
+ skb->h.raw += (nhptr[1]+1)<<3;
+ }
+
+ /* This check is sort of optimization.
+ It would be stupid to detect for optional headers,
+ which are missing with probability of 200%
+ */
+ if (nexthdr != IPPROTO_TCP && nexthdr != IPPROTO_UDP) {
+ nhptr = ipv6_parse_exthdrs(&skb, nhptr);
+ if (nhptr == NULL)
+ return 0;
+ nexthdr = *nhptr;
+ hdr = skb->nh.ipv6h;
+ }
+ len = skb->tail - skb->h.raw;
+
+ raw_sk = ipv6_raw_deliver(skb, nexthdr, len);
+
+ hash = nexthdr & (MAX_INET_PROTOS - 1);
+ for (ipprot = (struct inet6_protocol *) inet6_protos[hash];
+ ipprot != NULL;
+ ipprot = (struct inet6_protocol *) ipprot->next) {
+ struct sk_buff *buff = skb;
+
+ if (ipprot->protocol != nexthdr)
+ continue;
+
+ if (ipprot->copy || raw_sk)
+ buff = skb_clone(skb, GFP_ATOMIC);
+ /* buff == NULL ?????? */
+ ipprot->handler(buff, len);
+ found = 1;
+ }
+
+ if (raw_sk) {
+ rawv6_rcv(raw_sk, skb, len);
+ found = 1;
+ }
+
+ /*
+ * not found: send ICMP parameter problem back
+ */
+ if (!found) {
+ ipv6_statistics.Ip6InUnknownProtos++;
+ icmpv6_param_prob(skb, ICMPV6_UNK_NEXTHDR, nhptr);
+ }
+
+ return 0;
+}
+
+int ip6_mc_input(struct sk_buff *skb)
+{
+ struct ipv6hdr *hdr;
+ int deliver = 0;
+ int discard = 1;
+
+ ipv6_statistics.Ip6InMcastPkts++;
+
+ hdr = skb->nh.ipv6h;
+ if (ipv6_chk_mcast_addr(skb->dev, &hdr->daddr))
+ deliver = 1;
+
+ /*
+ * IPv6 multicast router mode isn't currently supported.
+ */
+#if 0
+ if (ipv6_config.multicast_route) {
+ int addr_type;
+
+ addr_type = ipv6_addr_type(&hdr->daddr);
+
+ if (!(addr_type & (IPV6_ADDR_LOOPBACK | IPV6_ADDR_LINKLOCAL))) {
+ struct sk_buff *skb2;
+ struct dst_entry *dst;
+
+ dst = skb->dst;
+
+ if (deliver) {
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ } else {
+ discard = 0;
+ skb2 = skb;
+ }
+
+ dst->output(skb2);
+ }
+ }
+#endif
+
+ if (deliver) {
+ discard = 0;
+ ip6_input(skb);
+ }
+
+ if (discard)
+ kfree_skb(skb);
+
+ return 0;
+}
diff --git a/pfinet/linux-src/net/ipv6/ip6_output.c b/pfinet/linux-src/net/ipv6/ip6_output.c
new file mode 100644
index 00000000..e06ad59a
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/ip6_output.c
@@ -0,0 +1,720 @@
+/*
+ * IPv6 output functions
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: ip6_output.c,v 1.1 2007/10/08 21:12:30 stesie Exp $
+ *
+ * Based on linux/net/ipv4/ip_output.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Changes:
+ * A.N.Kuznetsov : airthmetics in fragmentation.
+ * extension headers are implemented.
+ * route changes now work.
+ * ip6_forward does not confuse sniffers.
+ * etc.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/route.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+#include <net/protocol.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+#include <net/rawv6.h>
+#include <net/icmp.h>
+
+static u32 ipv6_fragmentation_id = 1;
+
+int ip6_output(struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb->dst;
+ struct device *dev = dst->dev;
+ struct hh_cache *hh = dst->hh;
+
+ skb->protocol = __constant_htons(ETH_P_IPV6);
+ skb->dev = dev;
+
+ if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr)) {
+ if (!(dev->flags&IFF_LOOPBACK) &&
+ (skb->sk == NULL || skb->sk->net_pinfo.af_inet6.mc_loop) &&
+ ipv6_chk_mcast_addr(dev, &skb->nh.ipv6h->daddr)) {
+ /* Do not check for IFF_ALLMULTI; multicast routing
+ is not supported in any case.
+ */
+ dev_loopback_xmit(skb);
+
+ if (skb->nh.ipv6h->hop_limit == 0) {
+ kfree_skb(skb);
+ return 0;
+ }
+ }
+
+ ipv6_statistics.Ip6OutMcastPkts++;
+ }
+
+ if (hh) {
+#ifdef __alpha__
+ /* Alpha has disguisting memcpy. Help it. */
+ u64 *aligned_hdr = (u64*)(skb->data - 16);
+ u64 *aligned_hdr0 = hh->hh_data;
+ read_lock_irq(&hh->hh_lock);
+ aligned_hdr[0] = aligned_hdr0[0];
+ aligned_hdr[1] = aligned_hdr0[1];
+#else
+ read_lock_irq(&hh->hh_lock);
+ memcpy(skb->data - 16, hh->hh_data, 16);
+#endif
+ read_unlock_irq(&hh->hh_lock);
+ skb_push(skb, dev->hard_header_len);
+ return hh->hh_output(skb);
+ } else if (dst->neighbour)
+ return dst->neighbour->output(skb);
+
+ kfree_skb(skb);
+ return -EINVAL;
+}
+
+/*
+ * xmit an sk_buff (used by TCP)
+ */
+
+int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
+ struct ipv6_txoptions *opt)
+{
+ struct ipv6_pinfo * np = sk ? &sk->net_pinfo.af_inet6 : NULL;
+ struct in6_addr *first_hop = fl->nl_u.ip6_u.daddr;
+ struct dst_entry *dst = skb->dst;
+ struct ipv6hdr *hdr;
+ u8 proto = fl->proto;
+ int seg_len = skb->len;
+ int hlimit;
+
+ if (opt) {
+ int head_room;
+
+ /* First: exthdrs may take lots of space (~8K for now)
+ MAX_HEADER is not enough.
+ */
+ head_room = opt->opt_nflen + opt->opt_flen;
+ seg_len += head_room;
+ head_room += sizeof(struct ipv6hdr) + ((dst->dev->hard_header_len + 15)&~15);
+
+ if (skb_headroom(skb) < head_room) {
+ struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room);
+ kfree(skb);
+ skb = skb2;
+ if (skb == NULL)
+ return -ENOBUFS;
+ if (sk)
+ skb_set_owner_w(skb, sk);
+ }
+ if (opt->opt_flen)
+ ipv6_push_frag_opts(skb, opt, &proto);
+ if (opt->opt_nflen)
+ ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop);
+ }
+
+ hdr = skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, sizeof(struct ipv6hdr));
+
+ /*
+ * Fill in the IPv6 header
+ */
+
+ *(u32*)hdr = __constant_htonl(0x60000000) | fl->fl6_flowlabel;
+ hlimit = -1;
+ if (np)
+ hlimit = np->hop_limit;
+ if (hlimit < 0)
+ hlimit = ((struct rt6_info*)dst)->rt6i_hoplimit;
+
+ hdr->payload_len = htons(seg_len);
+ hdr->nexthdr = proto;
+ hdr->hop_limit = hlimit;
+
+ ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
+ ipv6_addr_copy(&hdr->daddr, first_hop);
+
+ if (skb->len <= dst->pmtu) {
+ ipv6_statistics.Ip6OutRequests++;
+ dst->output(skb);
+ return 0;
+ }
+
+ printk(KERN_DEBUG "IPv6: sending pkt_too_big to self\n");
+ start_bh_atomic();
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst->pmtu, skb->dev);
+ end_bh_atomic();
+ kfree_skb(skb);
+ return -EMSGSIZE;
+}
+
+/*
+ * To avoid extra problems ND packets are send through this
+ * routine. It's code duplication but I really want to avoid
+ * extra checks since ipv6_build_header is used by TCP (which
+ * is for us performace critical)
+ */
+
+int ip6_nd_hdr(struct sock *sk, struct sk_buff *skb, struct device *dev,
+ struct in6_addr *saddr, struct in6_addr *daddr,
+ int proto, int len)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct ipv6hdr *hdr;
+ int totlen;
+
+ skb->protocol = __constant_htons(ETH_P_IPV6);
+ skb->dev = dev;
+
+ totlen = len + sizeof(struct ipv6hdr);
+
+ hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
+ skb->nh.ipv6h = hdr;
+
+ *(u32*)hdr = htonl(0x60000000);
+
+ hdr->payload_len = htons(len);
+ hdr->nexthdr = proto;
+ hdr->hop_limit = np->hop_limit;
+
+ ipv6_addr_copy(&hdr->saddr, saddr);
+ ipv6_addr_copy(&hdr->daddr, daddr);
+
+ return 0;
+}
+
+static struct ipv6hdr * ip6_bld_1(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
+ int hlimit, unsigned pktlength)
+{
+ struct ipv6hdr *hdr;
+
+ skb->nh.raw = skb_put(skb, sizeof(struct ipv6hdr));
+ hdr = skb->nh.ipv6h;
+
+ *(u32*)hdr = fl->fl6_flowlabel | htonl(0x60000000);
+
+ hdr->payload_len = htons(pktlength - sizeof(struct ipv6hdr));
+ hdr->hop_limit = hlimit;
+ hdr->nexthdr = fl->proto;
+
+ ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
+ ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);
+ return hdr;
+}
+
+static __inline__ u8 * ipv6_build_fraghdr(struct sk_buff *skb, u8* prev_hdr, unsigned offset)
+{
+ struct frag_hdr *fhdr;
+
+ fhdr = (struct frag_hdr *) skb_put(skb, sizeof(struct frag_hdr));
+
+ fhdr->nexthdr = *prev_hdr;
+ *prev_hdr = NEXTHDR_FRAGMENT;
+ prev_hdr = &fhdr->nexthdr;
+
+ fhdr->reserved = 0;
+ fhdr->frag_off = htons(offset);
+ fhdr->identification = ipv6_fragmentation_id++;
+ return &fhdr->nexthdr;
+}
+
+static int ip6_frag_xmit(struct sock *sk, inet_getfrag_t getfrag,
+ const void *data, struct dst_entry *dst,
+ struct flowi *fl, struct ipv6_txoptions *opt,
+ struct in6_addr *final_dst,
+ int hlimit, int flags, unsigned length, int mtu)
+{
+ struct ipv6hdr *hdr;
+ struct sk_buff *last_skb;
+ u8 *prev_hdr;
+ int unfrag_len;
+ int frag_len;
+ int last_len;
+ int nfrags;
+ int fhdr_dist;
+ int frag_off;
+ int data_off;
+ int err;
+
+ /*
+ * Fragmentation
+ *
+ * Extension header order:
+ * Hop-by-hop -> Dest0 -> Routing -> Fragment -> Auth -> Dest1 -> rest (...)
+ *
+ * We must build the non-fragmented part that
+ * will be in every packet... this also means
+ * that other extension headers (Dest, Auth, etc)
+ * must be considered in the data to be fragmented
+ */
+
+ unfrag_len = sizeof(struct ipv6hdr) + sizeof(struct frag_hdr);
+ last_len = length;
+
+ if (opt) {
+ unfrag_len += opt->opt_nflen;
+ last_len += opt->opt_flen;
+ }
+
+ /*
+ * Length of fragmented part on every packet but
+ * the last must be an:
+ * "integer multiple of 8 octects".
+ */
+
+ frag_len = (mtu - unfrag_len) & ~0x7;
+
+ /* Unfragmentable part exceeds mtu. */
+ if (frag_len <= 0) {
+ ipv6_local_error(sk, EMSGSIZE, fl, mtu);
+ return -EMSGSIZE;
+ }
+
+ nfrags = last_len / frag_len;
+
+ /*
+ * We must send from end to start because of
+ * UDP/ICMP checksums. We do a funny trick:
+ * fill the last skb first with the fixed
+ * header (and its data) and then use it
+ * to create the following segments and send it
+ * in the end. If the peer is checking the M_flag
+ * to trigger the reassembly code then this
+ * might be a good idea.
+ */
+
+ frag_off = nfrags * frag_len;
+ last_len -= frag_off;
+
+ if (last_len == 0) {
+ last_len = frag_len;
+ frag_off -= frag_len;
+ nfrags--;
+ }
+ data_off = frag_off;
+
+ /* And it is implementation problem: for now we assume, that
+ all the exthdrs will fit to the first fragment.
+ */
+ if (opt) {
+ if (frag_len < opt->opt_flen) {
+ ipv6_local_error(sk, EMSGSIZE, fl, mtu);
+ return -EMSGSIZE;
+ }
+ data_off = frag_off - opt->opt_flen;
+ }
+
+ last_skb = sock_alloc_send_skb(sk, unfrag_len + frag_len +
+ dst->dev->hard_header_len + 15,
+ 0, flags & MSG_DONTWAIT, &err);
+
+ if (last_skb == NULL)
+ return err;
+
+ last_skb->dst = dst_clone(dst);
+
+ skb_reserve(last_skb, (dst->dev->hard_header_len + 15) & ~15);
+
+ hdr = ip6_bld_1(sk, last_skb, fl, hlimit, frag_len+unfrag_len);
+ prev_hdr = &hdr->nexthdr;
+
+ if (opt && opt->opt_nflen)
+ prev_hdr = ipv6_build_nfrag_opts(last_skb, prev_hdr, opt, final_dst, 0);
+
+ prev_hdr = ipv6_build_fraghdr(last_skb, prev_hdr, frag_off);
+ fhdr_dist = prev_hdr - last_skb->data;
+
+ err = getfrag(data, &hdr->saddr, last_skb->tail, data_off, last_len);
+
+ if (!err) {
+ while (nfrags--) {
+ struct sk_buff *skb;
+
+ struct frag_hdr *fhdr2;
+
+ skb = skb_copy(last_skb, sk->allocation);
+
+ if (skb == NULL) {
+ ipv6_statistics.Ip6FragFails++;
+ kfree_skb(last_skb);
+ return -ENOMEM;
+ }
+
+ frag_off -= frag_len;
+ data_off -= frag_len;
+
+ fhdr2 = (struct frag_hdr *) (skb->data + fhdr_dist);
+
+ /* more flag on */
+ fhdr2->frag_off = htons(frag_off | 1);
+
+ /* Write fragmentable exthdrs to the first chunk */
+ if (nfrags == 0 && opt && opt->opt_flen) {
+ ipv6_build_frag_opts(skb, &fhdr2->nexthdr, opt);
+ frag_len -= opt->opt_flen;
+ data_off = 0;
+ }
+
+ err = getfrag(data, &hdr->saddr,skb_put(skb, frag_len),
+ data_off, frag_len);
+
+ if (err) {
+ kfree_skb(skb);
+ break;
+ }
+
+ ipv6_statistics.Ip6FragCreates++;
+ ipv6_statistics.Ip6OutRequests++;
+ dst->output(skb);
+ }
+ }
+
+ if (err) {
+ ipv6_statistics.Ip6FragFails++;
+ kfree_skb(last_skb);
+ return -EFAULT;
+ }
+
+ hdr->payload_len = htons(unfrag_len + last_len - sizeof(struct ipv6hdr));
+
+ /*
+ * update last_skb to reflect the getfrag we did
+ * on start.
+ */
+
+ skb_put(last_skb, last_len);
+
+ ipv6_statistics.Ip6FragCreates++;
+ ipv6_statistics.Ip6FragOKs++;
+ ipv6_statistics.Ip6OutRequests++;
+ dst->output(last_skb);
+
+ return 0;
+}
+
+int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
+ struct flowi *fl, unsigned length,
+ struct ipv6_txoptions *opt, int hlimit, int flags)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct in6_addr *final_dst = NULL;
+ struct dst_entry *dst;
+ int err = 0;
+ unsigned int pktlength, jumbolen, mtu;
+ struct in6_addr saddr;
+
+ if (opt && opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
+ final_dst = fl->fl6_dst;
+ fl->fl6_dst = rt0->addr;
+ }
+
+ if (!fl->oif && ipv6_addr_is_multicast(fl->nl_u.ip6_u.daddr))
+ fl->oif = np->mcast_oif;
+
+ dst = NULL;
+ if (sk->dst_cache) {
+ dst = dst_check(&sk->dst_cache, np->dst_cookie);
+ if (dst) {
+ struct rt6_info *rt = (struct rt6_info*)dst_clone(dst);
+
+ /* Yes, checking route validity in not connected
+ case is not very simple. Take into account,
+ that we do not support routing by source, TOS,
+ and MSG_DONTROUTE --ANK (980726)
+
+ 1. If route was host route, check that
+ cached destination is current.
+ If it is network route, we still may
+ check its validity using saved pointer
+ to the last used address: daddr_cache.
+ We do not want to save whole address now,
+ (because main consumer of this service
+ is tcp, which has not this problem),
+ so that the last trick works only on connected
+ sockets.
+ 2. oif also should be the same.
+ */
+ if (((rt->rt6i_dst.plen != 128 ||
+ ipv6_addr_cmp(fl->fl6_dst, &rt->rt6i_dst.addr))
+ && (np->daddr_cache == NULL ||
+ ipv6_addr_cmp(fl->fl6_dst, np->daddr_cache)))
+ || (fl->oif && fl->oif != dst->dev->ifindex)) {
+ dst_release(dst);
+ dst = NULL;
+ }
+ }
+ }
+
+ if (dst == NULL)
+ dst = ip6_route_output(sk, fl);
+
+ if (dst->error) {
+ ipv6_statistics.Ip6OutNoRoutes++;
+ dst_release(dst);
+ return -ENETUNREACH;
+ }
+
+ if (fl->fl6_src == NULL) {
+ err = ipv6_get_saddr(dst, fl->fl6_dst, &saddr);
+
+ if (err) {
+#if IP6_DEBUG >= 2
+ printk(KERN_DEBUG "ip6_build_xmit: "
+ "no available source address\n");
+#endif
+ goto out;
+ }
+ fl->fl6_src = &saddr;
+ }
+ pktlength = length;
+
+ if (hlimit < 0) {
+ if (ipv6_addr_is_multicast(fl->fl6_dst))
+ hlimit = np->mcast_hops;
+ else
+ hlimit = np->hop_limit;
+ if (hlimit < 0)
+ hlimit = ((struct rt6_info*)dst)->rt6i_hoplimit;
+ }
+
+ jumbolen = 0;
+
+ if (!sk->ip_hdrincl) {
+ pktlength += sizeof(struct ipv6hdr);
+ if (opt)
+ pktlength += opt->opt_flen + opt->opt_nflen;
+
+ if (pktlength > 0xFFFF + sizeof(struct ipv6hdr)) {
+ /* Jumbo datagram.
+ It is assumed, that in the case of sk->ip_hdrincl
+ jumbo option is supplied by user.
+ */
+ pktlength += 8;
+ jumbolen = pktlength - sizeof(struct ipv6hdr);
+ }
+ }
+
+ mtu = dst->pmtu;
+ if (np->frag_size < mtu) {
+ if (np->frag_size)
+ mtu = np->frag_size;
+ else if (np->pmtudisc == IPV6_PMTUDISC_DONT)
+ mtu = IPV6_MIN_MTU;
+ }
+
+ /* Critical arithmetic overflow check.
+ FIXME: may gcc optimize it out? --ANK (980726)
+ */
+ if (pktlength < length) {
+ ipv6_local_error(sk, EMSGSIZE, fl, mtu);
+ err = -EMSGSIZE;
+ goto out;
+ }
+
+ if (pktlength <= mtu) {
+ struct sk_buff *skb;
+ struct ipv6hdr *hdr;
+ struct device *dev = dst->dev;
+
+ skb = sock_alloc_send_skb(sk, pktlength + 15 +
+ dev->hard_header_len, 0,
+ flags & MSG_DONTWAIT, &err);
+
+ if (skb == NULL) {
+ ipv6_statistics.Ip6OutDiscards++;
+ goto out;
+ }
+
+ skb->dst = dst_clone(dst);
+
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+
+ hdr = (struct ipv6hdr *) skb->tail;
+ skb->nh.ipv6h = hdr;
+
+ if (!sk->ip_hdrincl) {
+ ip6_bld_1(sk, skb, fl, hlimit,
+ jumbolen ? sizeof(struct ipv6hdr) : pktlength);
+
+ if (opt || jumbolen) {
+ u8 *prev_hdr = &hdr->nexthdr;
+ prev_hdr = ipv6_build_nfrag_opts(skb, prev_hdr, opt, final_dst, jumbolen);
+ if (opt && opt->opt_flen)
+ ipv6_build_frag_opts(skb, prev_hdr, opt);
+ }
+ }
+
+ skb_put(skb, length);
+ err = getfrag(data, &hdr->saddr,
+ ((char *) hdr) + (pktlength - length),
+ 0, length);
+
+ if (!err) {
+ ipv6_statistics.Ip6OutRequests++;
+ dst->output(skb);
+ } else {
+ err = -EFAULT;
+ kfree_skb(skb);
+ }
+ } else {
+ if (sk->ip_hdrincl || jumbolen ||
+ np->pmtudisc == IPV6_PMTUDISC_DO) {
+ ipv6_local_error(sk, EMSGSIZE, fl, mtu);
+ err = -EMSGSIZE;
+ goto out;
+ }
+
+ err = ip6_frag_xmit(sk, getfrag, data, dst, fl, opt, final_dst, hlimit,
+ flags, length, mtu);
+ }
+
+ /*
+ * cleanup
+ */
+out:
+ ip6_dst_store(sk, dst, fl->nl_u.ip6_u.daddr == &np->daddr ? &np->daddr : NULL);
+ return err;
+}
+
+int ip6_call_ra_chain(struct sk_buff *skb, int sel)
+{
+ struct ip6_ra_chain *ra;
+ struct sock *last = NULL;
+
+ for (ra = ip6_ra_chain; ra; ra = ra->next) {
+ struct sock *sk = ra->sk;
+ if (sk && ra->sel == sel) {
+ if (last) {
+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2)
+ rawv6_rcv(last, skb2, skb2->len);
+ }
+ last = sk;
+ }
+ }
+
+ if (last) {
+ rawv6_rcv(last, skb, skb->len);
+ return 1;
+ }
+ return 0;
+}
+
+int ip6_forward(struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb->dst;
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ struct inet6_skb_parm *opt =(struct inet6_skb_parm*)skb->cb;
+
+ if (ipv6_devconf.forwarding == 0 && opt->srcrt == 0)
+ goto drop;
+
+ /*
+ * We DO NOT make any processing on
+ * RA packets, pushing them to user level AS IS
+ * without ane WARRANTY that application will be able
+ * to interpret them. The reason is that we
+ * cannot make anything clever here.
+ *
+ * We are not end-node, so that if packet contains
+ * AH/ESP, we cannot make anything.
+ * Defragmentation also would be mistake, RA packets
+ * cannot be fragmented, because there is no warranty
+ * that different fragments will go along one path. --ANK
+ */
+ if (opt->ra) {
+ u8 *ptr = skb->nh.raw + opt->ra;
+ if (ip6_call_ra_chain(skb, (ptr[2]<<8) + ptr[3]))
+ return 0;
+ }
+
+ /*
+ * check and decrement ttl
+ */
+ if (hdr->hop_limit <= 1) {
+ /* Force OUTPUT device used as source address */
+ skb->dev = dst->dev;
+ icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
+ 0, skb->dev);
+
+ kfree_skb(skb);
+ return -ETIMEDOUT;
+ }
+
+ /* IPv6 specs say nothing about it, but it is clear that we cannot
+ send redirects to source routed frames.
+ */
+ if (skb->dev == dst->dev && dst->neighbour && opt->srcrt == 0) {
+ struct in6_addr *target = NULL;
+ struct rt6_info *rt;
+ struct neighbour *n = dst->neighbour;
+
+ /*
+ * incoming and outgoing devices are the same
+ * send a redirect.
+ */
+
+ rt = (struct rt6_info *) dst;
+ if ((rt->rt6i_flags & RTF_GATEWAY))
+ target = (struct in6_addr*)&n->primary_key;
+ else
+ target = &hdr->daddr;
+
+ /* Limit redirects both by destination (here)
+ and by source (inside ndisc_send_redirect)
+ */
+ if (xrlim_allow(dst, 1*HZ))
+ ndisc_send_redirect(skb, n, target);
+ } else if (ipv6_addr_type(&hdr->saddr)&(IPV6_ADDR_MULTICAST|IPV6_ADDR_LOOPBACK
+ |IPV6_ADDR_LINKLOCAL)) {
+ /* This check is security critical. */
+ goto drop;
+ }
+
+ if (skb->len > dst->pmtu) {
+ /* Again, force OUTPUT device used as source address */
+ skb->dev = dst->dev;
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst->pmtu, skb->dev);
+ ipv6_statistics.Ip6InTooBigErrors++;
+ kfree_skb(skb);
+ return -EMSGSIZE;
+ }
+
+ if ((skb = skb_cow(skb, dst->dev->hard_header_len)) == NULL)
+ return 0;
+
+ hdr = skb->nh.ipv6h;
+
+ /* Mangling hops number delayed to point after skb COW */
+
+ hdr->hop_limit--;
+
+ ipv6_statistics.Ip6OutForwDatagrams++;
+ return dst->output(skb);
+
+drop:
+ ipv6_statistics.Ip6InAddrErrors++;
+ kfree_skb(skb);
+ return -EINVAL;
+}
diff --git a/pfinet/linux-src/net/ipv6/ipv6_sockglue.c b/pfinet/linux-src/net/ipv6/ipv6_sockglue.c
new file mode 100644
index 00000000..f1f67811
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/ipv6_sockglue.c
@@ -0,0 +1,452 @@
+/*
+ * IPv6 BSD socket options interface
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * Based on linux/net/ipv4/ip_sockglue.c
+ *
+ * $Id: ipv6_sockglue.c,v 1.1 2007/10/08 21:12:30 stesie Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * FIXME: Make the setsockopt code POSIX compliant: That is
+ *
+ * o Return -EINVAL for setsockopt of short lengths
+ * o Truncate getsockopt returns
+ * o Return an optlen of the truncated length if need be
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/sched.h>
+#include <linux/net.h>
+#include <linux/in6.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/sysctl.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+#include <net/protocol.h>
+#include <net/transp_v6.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+#include <net/inet_common.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+
+#include <asm/uaccess.h>
+
+struct ipv6_mib ipv6_statistics={0, };
+struct packet_type ipv6_packet_type =
+{
+ __constant_htons(ETH_P_IPV6),
+ NULL, /* All devices */
+ ipv6_rcv,
+ NULL,
+ NULL
+};
+
+/*
+ * addrconf module should be notifyed of a device going up
+ */
+static struct notifier_block ipv6_dev_notf = {
+ addrconf_notify,
+ NULL,
+ 0
+};
+
+struct ip6_ra_chain *ip6_ra_chain;
+
+int ip6_ra_control(struct sock *sk, int sel, void (*destructor)(struct sock *))
+{
+ struct ip6_ra_chain *ra, *new_ra, **rap;
+
+ /* RA packet may be delivered ONLY to IPPROTO_RAW socket */
+ if (sk->type != SOCK_RAW || sk->num != IPPROTO_RAW)
+ return -EINVAL;
+
+ new_ra = (sel>=0) ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
+
+ for (rap = &ip6_ra_chain; (ra=*rap) != NULL; rap = &ra->next) {
+ if (ra->sk == sk) {
+ if (sel>=0) {
+ if (new_ra)
+ kfree(new_ra);
+ return -EADDRINUSE;
+ }
+
+ *rap = ra->next;
+ synchronize_bh();
+
+ if (ra->destructor)
+ ra->destructor(sk);
+ kfree(ra);
+ return 0;
+ }
+ }
+ if (new_ra == NULL)
+ return -ENOBUFS;
+ new_ra->sk = sk;
+ new_ra->sel = sel;
+ new_ra->destructor = destructor;
+ start_bh_atomic();
+ new_ra->next = ra;
+ *rap = new_ra;
+ end_bh_atomic();
+ return 0;
+}
+
+
+int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
+ int optlen)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ int val, valbool;
+ int retv = -ENOPROTOOPT;
+
+ if(level==SOL_IP && sk->type != SOCK_RAW)
+ return udp_prot.setsockopt(sk, level, optname, optval, optlen);
+
+ if(level!=SOL_IPV6)
+ goto out;
+
+ if (optval == NULL)
+ val=0;
+ else if (get_user(val, (int *) optval))
+ return -EFAULT;
+
+ valbool = (val!=0);
+
+ switch (optname) {
+
+ case IPV6_ADDRFORM:
+ if (val == PF_INET) {
+ struct ipv6_txoptions *opt;
+ struct sk_buff *pktopt;
+
+ if (sk->protocol != IPPROTO_UDP &&
+ sk->protocol != IPPROTO_TCP)
+ goto out;
+
+ lock_sock(sk);
+ if (sk->state != TCP_ESTABLISHED) {
+ retv = ENOTCONN;
+ goto addrform_done;
+ }
+
+ if (ipv6_only_sock(sk) ||
+ !(ipv6_addr_type(&np->daddr) & IPV6_ADDR_MAPPED)) {
+ retv = -EADDRNOTAVAIL;
+ goto addrform_done;
+ }
+
+ fl6_free_socklist(sk);
+ ipv6_sock_mc_close(sk);
+
+ if (sk->protocol == IPPROTO_TCP) {
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ sk->prot = &tcp_prot;
+ tp->af_specific = &ipv4_specific;
+ sk->socket->ops = &inet_stream_ops;
+ sk->family = PF_INET;
+ tcp_sync_mss(sk, tp->pmtu_cookie);
+ } else {
+ sk->prot = &udp_prot;
+ sk->socket->ops = &inet_dgram_ops;
+ }
+ opt = xchg(&np->opt, NULL);
+ if (opt)
+ sock_kfree_s(sk, opt, opt->tot_len);
+ pktopt = xchg(&np->pktoptions, NULL);
+ if (pktopt)
+ kfree_skb(pktopt);
+ retv = 0;
+
+addrform_done:
+ release_sock(sk);
+ } else {
+ retv = -EINVAL;
+ }
+ break;
+
+ case IPV6_V6ONLY:
+ if (sk->num) {
+ retv = -EINVAL;
+ goto out;
+ }
+ np->ipv6only = valbool;
+ retv = 0;
+ break;
+
+ case IPV6_PKTINFO:
+ np->rxopt.bits.rxinfo = valbool;
+ retv = 0;
+ break;
+
+ case IPV6_HOPLIMIT:
+ np->rxopt.bits.rxhlim = valbool;
+ retv = 0;
+ break;
+
+ case IPV6_RTHDR:
+ retv = -EINVAL;
+ if (val >= 0 && val <= 2) {
+ np->rxopt.bits.srcrt = val;
+ retv = 0;
+ }
+ break;
+
+ case IPV6_HOPOPTS:
+ np->rxopt.bits.hopopts = valbool;
+ retv = 0;
+ break;
+
+ case IPV6_AUTHHDR:
+ np->rxopt.bits.authhdr = valbool;
+ retv = 0;
+ break;
+
+ case IPV6_DSTOPTS:
+ np->rxopt.bits.dstopts = valbool;
+ retv = 0;
+ break;
+
+ case IPV6_FLOWINFO:
+ np->rxopt.bits.rxflow = valbool;
+ return 0;
+
+ case IPV6_PKTOPTIONS:
+ {
+ struct ipv6_txoptions *opt = NULL;
+ struct msghdr msg;
+ struct flowi fl;
+ int junk;
+
+ fl.fl6_flowlabel = 0;
+ fl.oif = sk->bound_dev_if;
+
+ if (optlen == 0)
+ goto update;
+
+ opt = sock_kmalloc(sk, sizeof(*opt) + optlen, GFP_KERNEL);
+ retv = -ENOBUFS;
+ if (opt == NULL)
+ break;
+
+ memset(opt, 0, sizeof(*opt));
+ opt->tot_len = sizeof(*opt) + optlen;
+ retv = -EFAULT;
+ if (copy_from_user(opt+1, optval, optlen))
+ goto done;
+
+ msg.msg_controllen = optlen;
+ msg.msg_control = (void*)(opt+1);
+
+ retv = datagram_send_ctl(&msg, &fl, opt, &junk);
+ if (retv)
+ goto done;
+update:
+ retv = 0;
+ start_bh_atomic();
+ if (opt && sk->type == SOCK_STREAM) {
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+ if ((tcp_connected(sk->state) || sk->state == TCP_SYN_SENT)
+ && sk->daddr != LOOPBACK4_IPV6) {
+ tp->ext_header_len = opt->opt_flen + opt->opt_nflen;
+ tcp_sync_mss(sk, tp->pmtu_cookie);
+ }
+ }
+ opt = xchg(&np->opt, opt);
+ dst_release(xchg(&sk->dst_cache, NULL));
+ end_bh_atomic();
+
+done:
+ if (opt)
+ sock_kfree_s(sk, opt, opt->tot_len);
+ break;
+ }
+ case IPV6_UNICAST_HOPS:
+ if (val > 255 || val < -1)
+ retv = -EINVAL;
+ else {
+ np->hop_limit = val;
+ retv = 0;
+ }
+ break;
+
+ case IPV6_MULTICAST_HOPS:
+ if (val > 255 || val < -1)
+ retv = -EINVAL;
+ else {
+ np->mcast_hops = val;
+ retv = 0;
+ }
+ break;
+
+ case IPV6_MULTICAST_LOOP:
+ np->mc_loop = valbool;
+ retv = 0;
+ break;
+
+ case IPV6_MULTICAST_IF:
+ if (sk->bound_dev_if && sk->bound_dev_if != val) {
+ retv = -EINVAL;
+ break;
+ }
+ if (dev_get_by_index(val) == NULL) {
+ retv = -ENODEV;
+ break;
+ }
+ np->mcast_oif = val;
+ retv = 0;
+ break;
+ case IPV6_ADD_MEMBERSHIP:
+ case IPV6_DROP_MEMBERSHIP:
+ {
+ struct ipv6_mreq mreq;
+
+ if (copy_from_user(&mreq, optval, sizeof(struct ipv6_mreq)))
+ return -EFAULT;
+
+ if (optname == IPV6_ADD_MEMBERSHIP)
+ retv = ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
+ else
+ retv = ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
+ break;
+ }
+ case IPV6_ROUTER_ALERT:
+ retv = ip6_ra_control(sk, val, NULL);
+ break;
+ case IPV6_MTU_DISCOVER:
+ if (val<0 || val>2)
+ return -EINVAL;
+ np->pmtudisc = val;
+ return 0;
+ case IPV6_MTU:
+ if (val && val < IPV6_MIN_MTU)
+ return -EINVAL;
+ np->frag_size = val;
+ return 0;
+ case IPV6_RECVERR:
+ np->recverr = valbool;
+ if (!val)
+ skb_queue_purge(&sk->error_queue);
+ return 0;
+ case IPV6_FLOWINFO_SEND:
+ np->sndflow = valbool;
+ return 0;
+ case IPV6_FLOWLABEL_MGR:
+ return ipv6_flowlabel_opt(sk, optval, optlen);
+ };
+
+out:
+ return retv;
+}
+
+int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval,
+ int *optlen)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ int len;
+ int val;
+
+ if(level==SOL_IP && sk->type != SOCK_RAW)
+ return udp_prot.getsockopt(sk, level, optname, optval, optlen);
+ if(level!=SOL_IPV6)
+ return -ENOPROTOOPT;
+ if (get_user(len, optlen))
+ return -EFAULT;
+ switch (optname) {
+ case IPV6_PKTOPTIONS:
+ {
+ struct msghdr msg;
+ struct sk_buff *skb;
+
+ start_bh_atomic();
+ skb = np->pktoptions;
+ if (skb)
+ atomic_inc(&skb->users);
+ end_bh_atomic();
+
+ if (skb) {
+ int err;
+
+ msg.msg_control = optval;
+ msg.msg_controllen = len;
+ msg.msg_flags = 0;
+ err = datagram_recv_ctl(sk, &msg, skb);
+ kfree_skb(skb);
+ if (err)
+ return err;
+ len -= msg.msg_controllen;
+ } else
+ len = 0;
+ return put_user(len, optlen);
+ }
+ case IP_MTU:
+ val = 0;
+ lock_sock(sk);
+ if (sk->dst_cache)
+ val = sk->dst_cache->pmtu;
+ release_sock(sk);
+ if (!val)
+ return -ENOTCONN;
+ break;
+ case IPV6_V6ONLY:
+ val = np->ipv6only;
+ break;
+ default:
+ return -EINVAL;
+ }
+ len=min(sizeof(int),len);
+ if(put_user(len, optlen))
+ return -EFAULT;
+ if(copy_to_user(optval,&val,len))
+ return -EFAULT;
+ return 0;
+}
+
+#if defined(MODULE) && defined(CONFIG_SYSCTL)
+
+/*
+ * sysctl registration functions defined in sysctl_net_ipv6.c
+ */
+
+extern void ipv6_sysctl_register(void);
+extern void ipv6_sysctl_unregister(void);
+#endif
+
+__initfunc(void ipv6_packet_init(void))
+{
+ dev_add_pack(&ipv6_packet_type);
+}
+
+__initfunc(void ipv6_netdev_notif_init(void))
+{
+ register_netdevice_notifier(&ipv6_dev_notf);
+}
+
+#ifdef MODULE
+void ipv6_packet_cleanup(void)
+{
+ dev_remove_pack(&ipv6_packet_type);
+}
+
+void ipv6_netdev_notif_cleanup(void)
+{
+ unregister_netdevice_notifier(&ipv6_dev_notf);
+}
+#endif
diff --git a/pfinet/linux-src/net/ipv6/mcast.c b/pfinet/linux-src/net/ipv6/mcast.c
new file mode 100644
index 00000000..87e9e909
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/mcast.c
@@ -0,0 +1,711 @@
+/*
+ * Multicast support for IPv6
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: mcast.c,v 1.2 2007/10/08 21:59:10 stesie Exp $
+ *
+ * Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/sched.h>
+#include <linux/net.h>
+#include <linux/in6.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/route.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/if_inet6.h>
+#include <net/ndisc.h>
+#include <net/addrconf.h>
+#include <net/ip6_route.h>
+
+#include <net/checksum.h>
+
+/* Set to 3 to get tracing... */
+#define MCAST_DEBUG 2
+
+#if MCAST_DEBUG >= 3
+#define MDBG(x) printk x
+#else
+#define MDBG(x)
+#endif
+
+static struct socket *igmp6_socket;
+
+static void igmp6_join_group(struct ifmcaddr6 *ma);
+static void igmp6_leave_group(struct ifmcaddr6 *ma);
+void igmp6_timer_handler(unsigned long data);
+
+#define IGMP6_UNSOLICITED_IVAL (10*HZ)
+
+/*
+ * Hash list of configured multicast addresses
+ */
+static struct ifmcaddr6 *inet6_mcast_lst[IN6_ADDR_HSIZE];
+
+/*
+ * socket join on multicast group
+ */
+
+int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr)
+{
+ struct device *dev = NULL;
+ struct ipv6_mc_socklist *mc_lst;
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ int err;
+
+ if (!(ipv6_addr_type(addr) & IPV6_ADDR_MULTICAST))
+ return -EINVAL;
+
+ mc_lst = sock_kmalloc(sk, sizeof(struct ipv6_mc_socklist), GFP_KERNEL);
+
+ if (mc_lst == NULL)
+ return -ENOMEM;
+
+ mc_lst->next = NULL;
+ memcpy(&mc_lst->addr, addr, sizeof(struct in6_addr));
+ mc_lst->ifindex = ifindex;
+
+ if (ifindex == 0) {
+ struct rt6_info *rt;
+ rt = rt6_lookup(addr, NULL, 0, 0);
+ if (rt) {
+ dev = rt->rt6i_dev;
+ dst_release(&rt->u.dst);
+ }
+ } else
+ dev = dev_get_by_index(ifindex);
+
+ if (dev == NULL) {
+ sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
+ return -ENODEV;
+ }
+
+ /*
+ * now add/increase the group membership on the device
+ */
+
+ err = ipv6_dev_mc_inc(dev, addr);
+
+ if (err) {
+ sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
+ return err;
+ }
+
+ mc_lst->next = np->ipv6_mc_list;
+ np->ipv6_mc_list = mc_lst;
+
+ return 0;
+}
+
+/*
+ * socket leave on multicast group
+ */
+int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct ipv6_mc_socklist *mc_lst, **lnk;
+
+ for (lnk = &np->ipv6_mc_list; (mc_lst = *lnk) !=NULL ; lnk = &mc_lst->next) {
+ if (mc_lst->ifindex == ifindex &&
+ ipv6_addr_cmp(&mc_lst->addr, addr) == 0) {
+ struct device *dev;
+
+ *lnk = mc_lst->next;
+ synchronize_bh();
+
+ if ((dev = dev_get_by_index(ifindex)) != NULL)
+ ipv6_dev_mc_dec(dev, &mc_lst->addr);
+ sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+void ipv6_sock_mc_close(struct sock *sk)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct ipv6_mc_socklist *mc_lst;
+
+ while ((mc_lst = np->ipv6_mc_list) != NULL) {
+ struct device *dev = dev_get_by_index(mc_lst->ifindex);
+
+ if (dev)
+ ipv6_dev_mc_dec(dev, &mc_lst->addr);
+
+ np->ipv6_mc_list = mc_lst->next;
+ sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
+ }
+}
+
+static int igmp6_group_added(struct ifmcaddr6 *mc)
+{
+ char buf[MAX_ADDR_LEN];
+
+ if (!(mc->mca_flags&MAF_LOADED)) {
+ mc->mca_flags |= MAF_LOADED;
+ if (ndisc_mc_map(&mc->mca_addr, buf, mc->dev, 0) == 0)
+ dev_mc_add(mc->dev, buf, mc->dev->addr_len, 0);
+ }
+
+ if (mc->dev->flags&IFF_UP)
+ igmp6_join_group(mc);
+ return 0;
+}
+
+static int igmp6_group_dropped(struct ifmcaddr6 *mc)
+{
+ char buf[MAX_ADDR_LEN];
+
+ if (mc->mca_flags&MAF_LOADED) {
+ mc->mca_flags &= ~MAF_LOADED;
+ if (ndisc_mc_map(&mc->mca_addr, buf, mc->dev, 0) == 0)
+ dev_mc_delete(mc->dev, buf, mc->dev->addr_len, 0);
+ }
+
+ if (mc->dev->flags&IFF_UP)
+ igmp6_leave_group(mc);
+ return 0;
+}
+
+
+/*
+ * device multicast group inc (add if not found)
+ */
+int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr)
+{
+ struct ifmcaddr6 *mc;
+ struct inet6_dev *idev;
+ int hash;
+
+ idev = ipv6_get_idev(dev);
+
+ if (idev == NULL)
+ return -EINVAL;
+
+ hash = ipv6_addr_hash(addr);
+
+ for (mc = inet6_mcast_lst[hash]; mc; mc = mc->next) {
+ if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0 && mc->dev == dev) {
+ atomic_inc(&mc->mca_users);
+ return 0;
+ }
+ }
+
+ /*
+ * not found: create a new one.
+ */
+
+ mc = kmalloc(sizeof(struct ifmcaddr6), GFP_ATOMIC);
+
+ if (mc == NULL)
+ return -ENOMEM;
+
+ memset(mc, 0, sizeof(struct ifmcaddr6));
+ mc->mca_timer.function = igmp6_timer_handler;
+ mc->mca_timer.data = (unsigned long) mc;
+
+ memcpy(&mc->mca_addr, addr, sizeof(struct in6_addr));
+ mc->dev = dev;
+ atomic_set(&mc->mca_users, 1);
+
+ mc->next = inet6_mcast_lst[hash];
+ inet6_mcast_lst[hash] = mc;
+
+ mc->if_next = idev->mc_list;
+ idev->mc_list = mc;
+
+ igmp6_group_added(mc);
+
+ return 0;
+}
+
+static void ipv6_mca_remove(struct device *dev, struct ifmcaddr6 *ma)
+{
+ struct inet6_dev *idev;
+
+ idev = ipv6_get_idev(dev);
+
+ if (idev) {
+ struct ifmcaddr6 *iter, **lnk;
+
+ for (lnk = &idev->mc_list; (iter = *lnk) != NULL; lnk = &iter->if_next) {
+ if (iter == ma) {
+ *lnk = iter->if_next;
+ synchronize_bh();
+ return;
+ }
+ }
+ }
+}
+
+/*
+ * device multicast group del
+ */
+int ipv6_dev_mc_dec(struct device *dev, struct in6_addr *addr)
+{
+ struct ifmcaddr6 *ma, **lnk;
+ int hash;
+
+ hash = ipv6_addr_hash(addr);
+
+ for (lnk = &inet6_mcast_lst[hash]; (ma=*lnk) != NULL; lnk = &ma->next) {
+ if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0 && ma->dev == dev) {
+ if (atomic_dec_and_test(&ma->mca_users)) {
+ igmp6_group_dropped(ma);
+
+ *lnk = ma->next;
+ synchronize_bh();
+
+ ipv6_mca_remove(dev, ma);
+ kfree(ma);
+ }
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+/*
+ * check if the interface/address pair is valid
+ */
+int ipv6_chk_mcast_addr(struct device *dev, struct in6_addr *addr)
+{
+ struct ifmcaddr6 *mc;
+ int hash;
+
+ hash = ipv6_addr_hash(addr);
+
+ for (mc = inet6_mcast_lst[hash]; mc; mc=mc->next) {
+ if (mc->dev == dev && ipv6_addr_cmp(&mc->mca_addr, addr) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * IGMP handling (alias multicast ICMPv6 messages)
+ */
+
+static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
+{
+ unsigned long delay = resptime;
+
+ /* Do not start timer for addresses with link/host scope */
+ if (ipv6_addr_type(&ma->mca_addr)&(IPV6_ADDR_LINKLOCAL|IPV6_ADDR_LOOPBACK))
+ return;
+
+ if (del_timer(&ma->mca_timer))
+ delay = ma->mca_timer.expires - jiffies;
+
+ if (delay >= resptime) {
+ if (resptime)
+ delay = net_random() % resptime;
+ else
+ delay = 1;
+ }
+
+ ma->mca_flags |= MAF_TIMER_RUNNING;
+ ma->mca_timer.expires = jiffies + delay;
+ add_timer(&ma->mca_timer);
+}
+
+int igmp6_event_query(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
+{
+ struct ifmcaddr6 *ma;
+ struct in6_addr *addrp;
+ unsigned long resptime;
+
+ if (len < sizeof(struct icmp6hdr) + sizeof(struct in6_addr))
+ return -EINVAL;
+
+ /* Drop queries with not link local source */
+ if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr)&IPV6_ADDR_LINKLOCAL))
+ return -EINVAL;
+
+ resptime = ntohs(hdr->icmp6_maxdelay);
+ /* Translate milliseconds to jiffies */
+ resptime = (resptime<<10)/(1024000/HZ);
+
+ addrp = (struct in6_addr *) (hdr + 1);
+
+ if (ipv6_addr_any(addrp)) {
+ struct inet6_dev *idev;
+
+ idev = ipv6_get_idev(skb->dev);
+
+ if (idev == NULL)
+ return 0;
+
+ for (ma = idev->mc_list; ma; ma=ma->if_next)
+ igmp6_group_queried(ma, resptime);
+ } else {
+ int hash = ipv6_addr_hash(addrp);
+
+ for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) {
+ if (ma->dev == skb->dev &&
+ ipv6_addr_cmp(addrp, &ma->mca_addr) == 0) {
+ igmp6_group_queried(ma, resptime);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+int igmp6_event_report(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
+{
+ struct ifmcaddr6 *ma;
+ struct in6_addr *addrp;
+ struct device *dev;
+ int hash;
+
+ /* Our own report looped back. Ignore it. */
+ if (skb->pkt_type == PACKET_LOOPBACK)
+ return 0;
+
+ if (len < sizeof(struct icmp6hdr) + sizeof(struct in6_addr))
+ return -EINVAL;
+
+ /* Drop reports with not link local source */
+ if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr)&IPV6_ADDR_LINKLOCAL))
+ return -EINVAL;
+
+ addrp = (struct in6_addr *) (hdr + 1);
+
+ dev = skb->dev;
+
+ /*
+ * Cancel the timer for this group
+ */
+
+ hash = ipv6_addr_hash(addrp);
+
+ for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) {
+ if ((ma->dev == dev) && ipv6_addr_cmp(&ma->mca_addr, addrp) == 0) {
+ if (ma->mca_flags & MAF_TIMER_RUNNING) {
+ del_timer(&ma->mca_timer);
+ ma->mca_flags &= ~MAF_TIMER_RUNNING;
+ }
+
+ ma->mca_flags &= ~MAF_LAST_REPORTER;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void igmp6_send(struct in6_addr *addr, struct device *dev, int type)
+{
+ struct sock *sk = igmp6_socket->sk;
+ struct sk_buff *skb;
+ struct icmp6hdr *hdr;
+ struct inet6_ifaddr *ifp;
+ struct in6_addr *snd_addr;
+ struct in6_addr *addrp;
+ struct in6_addr all_routers;
+ int err, len, payload_len, full_len;
+ u8 ra[8] = { IPPROTO_ICMPV6, 0,
+ IPV6_TLV_ROUTERALERT, 0, 0, 0,
+ IPV6_TLV_PADN, 0 };
+
+ snd_addr = addr;
+ if (type == ICMPV6_MGM_REDUCTION) {
+ snd_addr = &all_routers;
+ ipv6_addr_all_routers(&all_routers);
+ }
+
+ len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+ payload_len = len + sizeof(ra);
+ full_len = sizeof(struct ipv6hdr) + payload_len;
+
+ skb = sock_alloc_send_skb(sk, dev->hard_header_len + full_len + 15, 0, 0, &err);
+
+ if (skb == NULL)
+ return;
+
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+ if (dev->hard_header) {
+ unsigned char ha[MAX_ADDR_LEN];
+ ndisc_mc_map(snd_addr, ha, dev, 1);
+ dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, full_len);
+ }
+
+ ifp = ipv6_get_lladdr(dev);
+
+ if (ifp == NULL) {
+#if MCAST_DEBUG >= 1
+ printk(KERN_DEBUG "igmp6: %s no linklocal address\n",
+ dev->name);
+#endif
+ return;
+ }
+
+ ip6_nd_hdr(sk, skb, dev, &ifp->addr, snd_addr, NEXTHDR_HOP, payload_len);
+
+ memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra));
+
+ hdr = (struct icmp6hdr *) skb_put(skb, sizeof(struct icmp6hdr));
+ memset(hdr, 0, sizeof(struct icmp6hdr));
+ hdr->icmp6_type = type;
+
+ addrp = (struct in6_addr *) skb_put(skb, sizeof(struct in6_addr));
+ ipv6_addr_copy(addrp, addr);
+
+ hdr->icmp6_cksum = csum_ipv6_magic(&ifp->addr, snd_addr, len,
+ IPPROTO_ICMPV6,
+ csum_partial((__u8 *) hdr, len, 0));
+
+ dev_queue_xmit(skb);
+ if (type == ICMPV6_MGM_REDUCTION)
+ icmpv6_statistics.Icmp6OutGroupMembReductions++;
+ else
+ icmpv6_statistics.Icmp6OutGroupMembResponses++;
+ icmpv6_statistics.Icmp6OutMsgs++;
+}
+
+static void igmp6_join_group(struct ifmcaddr6 *ma)
+{
+ unsigned long delay;
+ int addr_type;
+
+ addr_type = ipv6_addr_type(&ma->mca_addr);
+
+ if ((addr_type & (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_LOOPBACK)))
+ return;
+
+ start_bh_atomic();
+ igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
+
+ delay = net_random() % IGMP6_UNSOLICITED_IVAL;
+ if (del_timer(&ma->mca_timer))
+ delay = ma->mca_timer.expires - jiffies;
+
+ ma->mca_timer.expires = jiffies + delay;
+
+ add_timer(&ma->mca_timer);
+ ma->mca_flags |= MAF_TIMER_RUNNING | MAF_LAST_REPORTER;
+ end_bh_atomic();
+}
+
+static void igmp6_leave_group(struct ifmcaddr6 *ma)
+{
+ int addr_type;
+
+ addr_type = ipv6_addr_type(&ma->mca_addr);
+
+ if ((addr_type & IPV6_ADDR_LINKLOCAL))
+ return;
+
+ start_bh_atomic();
+ if (ma->mca_flags & MAF_LAST_REPORTER)
+ igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REDUCTION);
+
+ if (ma->mca_flags & MAF_TIMER_RUNNING)
+ del_timer(&ma->mca_timer);
+ end_bh_atomic();
+}
+
+void igmp6_timer_handler(unsigned long data)
+{
+ struct ifmcaddr6 *ma = (struct ifmcaddr6 *) data;
+
+ ma->mca_flags |= MAF_LAST_REPORTER;
+ igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
+ ma->mca_flags &= ~MAF_TIMER_RUNNING;
+}
+
+/* Device going down */
+
+void ipv6_mc_down(struct inet6_dev *idev)
+{
+ struct ifmcaddr6 *i;
+ struct in6_addr maddr;
+
+ /* Withdraw multicast list */
+
+ for (i = idev->mc_list; i; i=i->if_next)
+ igmp6_group_dropped(i);
+
+ /* Delete all-nodes address. */
+
+ ipv6_addr_all_nodes(&maddr);
+ ipv6_dev_mc_dec(idev->dev, &maddr);
+}
+
+/* Device going up */
+
+void ipv6_mc_up(struct inet6_dev *idev)
+{
+ struct ifmcaddr6 *i;
+ struct in6_addr maddr;
+
+ /* Add all-nodes address. */
+
+ ipv6_addr_all_nodes(&maddr);
+ ipv6_dev_mc_inc(idev->dev, &maddr);
+
+ /* Install multicast list, except for all-nodes (already installed) */
+
+ for (i = idev->mc_list; i; i=i->if_next)
+ igmp6_group_added(i);
+}
+
+/*
+ * Device is about to be destroyed: clean up.
+ */
+
+void ipv6_mc_destroy_dev(struct inet6_dev *idev)
+{
+ int hash;
+ struct ifmcaddr6 *i, **lnk;
+
+ while ((i = idev->mc_list) != NULL) {
+ idev->mc_list = i->if_next;
+
+ hash = ipv6_addr_hash(&i->mca_addr);
+
+ for (lnk = &inet6_mcast_lst[hash]; *lnk; lnk = &(*lnk)->next) {
+ if (*lnk == i) {
+ *lnk = i->next;
+ synchronize_bh();
+ break;
+ }
+ }
+ igmp6_group_dropped(i);
+ kfree(i);
+ }
+}
+
+#ifdef CONFIG_PROC_FS
+static int igmp6_read_proc(char *buffer, char **start, off_t offset,
+ int length, int *eof, void *data)
+{
+ off_t pos=0, begin=0;
+ struct ifmcaddr6 *im;
+ int len=0;
+ struct device *dev;
+
+ for (dev = dev_base; dev; dev = dev->next) {
+ struct inet6_dev *idev;
+
+ if ((idev = ipv6_get_idev(dev)) == NULL)
+ continue;
+
+ for (im = idev->mc_list; im; im = im->if_next) {
+ int i;
+
+ len += sprintf(buffer+len,"%-4d %-15s ", dev->ifindex, dev->name);
+
+ for (i=0; i<16; i++)
+ len += sprintf(buffer+len, "%02x", im->mca_addr.s6_addr[i]);
+
+ len+=sprintf(buffer+len,
+ " %5d %08X %ld\n",
+ atomic_read(&im->mca_users),
+ im->mca_flags,
+ (im->mca_flags&MAF_TIMER_RUNNING) ? im->mca_timer.expires-jiffies : 0);
+
+ pos=begin+len;
+ if (pos < offset) {
+ len=0;
+ begin=pos;
+ }
+ if (pos > offset+length)
+ goto done;
+ }
+ }
+ *eof = 1;
+
+done:
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ if (len<0)
+ len=0;
+ return len;
+}
+#endif
+
+__initfunc(int igmp6_init(struct net_proto_family *ops))
+{
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *ent;
+#endif
+ struct sock *sk;
+ int err;
+
+ igmp6_socket = sock_alloc();
+ if (igmp6_socket == NULL) {
+ printk(KERN_ERR
+ "Failed to create the IGMP6 control socket.\n");
+ return -1;
+ }
+#ifndef _HURD_
+ igmp6_socket->inode->i_uid = 0;
+ igmp6_socket->inode->i_gid = 0;
+#endif
+ igmp6_socket->type = SOCK_RAW;
+
+ if((err = ops->create(igmp6_socket, IPPROTO_ICMPV6)) < 0) {
+ printk(KERN_DEBUG
+ "Failed to initialize the IGMP6 control socket (err %d).\n",
+ err);
+ sock_release(igmp6_socket);
+ igmp6_socket = NULL; /* For safety. */
+ return err;
+ }
+
+ sk = igmp6_socket->sk;
+ sk->allocation = GFP_ATOMIC;
+ sk->num = 256; /* Don't receive any data */
+
+ sk->net_pinfo.af_inet6.hop_limit = 1;
+#ifdef CONFIG_PROC_FS
+ ent = create_proc_entry("net/igmp6", 0, 0);
+ ent->read_proc = igmp6_read_proc;
+#endif
+
+ return 0;
+}
+
+#ifdef MODULE
+void igmp6_cleanup(void)
+{
+ sock_release(igmp6_socket);
+ igmp6_socket = NULL; /* for safety */
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("net/igmp6", 0);
+#endif
+}
+#endif
diff --git a/pfinet/linux-src/net/ipv6/ndisc.c b/pfinet/linux-src/net/ipv6/ndisc.c
new file mode 100644
index 00000000..61f950d2
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/ndisc.c
@@ -0,0 +1,1217 @@
+/*
+ * Neighbour Discovery for IPv6
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ * Mike Shaver <shaver@ingenia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * Changes:
+ *
+ * Lars Fenneberg : fixed MTU setting on receipt
+ * of an RA.
+ *
+ * Janos Farkas : kmalloc failure checks
+ * Alexey Kuznetsov : state machine reworked
+ * and moved to net/core.
+ */
+
+/* Set to 3 to get tracing... */
+#define ND_DEBUG 1
+
+#define ND_PRINTK(x...) printk(KERN_DEBUG x)
+#define ND_NOPRINTK(x...) do { ; } while(0)
+#define ND_PRINTK0 ND_PRINTK
+#define ND_PRINTK1 ND_NOPRINTK
+#define ND_PRINTK2 ND_NOPRINTK
+#if ND_DEBUG >= 1
+#undef ND_PRINTK1
+#define ND_PRINTK1 ND_PRINTK
+#endif
+#if ND_DEBUG >= 2
+#undef ND_PRINTK2
+#define ND_PRINTK2 ND_PRINTK
+#endif
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/sched.h>
+#include <linux/net.h>
+#include <linux/in6.h>
+#include <linux/route.h>
+#include <linux/init.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
+
+#include <linux/if_arp.h>
+#include <linux/ipv6.h>
+#include <linux/icmpv6.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/ndisc.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+#include <net/icmp.h>
+
+#include <net/checksum.h>
+#include <linux/proc_fs.h>
+
+static struct socket *ndisc_socket;
+
+static int ndisc_constructor(struct neighbour *neigh);
+static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb);
+static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb);
+static int pndisc_constructor(struct pneigh_entry *n);
+static void pndisc_destructor(struct pneigh_entry *n);
+static void pndisc_redo(struct sk_buff *skb);
+
+static struct neigh_ops ndisc_generic_ops =
+{
+ AF_INET6,
+ NULL,
+ ndisc_solicit,
+ ndisc_error_report,
+ neigh_resolve_output,
+ neigh_connected_output,
+ dev_queue_xmit,
+ dev_queue_xmit
+};
+
+static struct neigh_ops ndisc_hh_ops =
+{
+ AF_INET6,
+ NULL,
+ ndisc_solicit,
+ ndisc_error_report,
+ neigh_resolve_output,
+ neigh_resolve_output,
+ dev_queue_xmit,
+ dev_queue_xmit
+};
+
+
+static struct neigh_ops ndisc_direct_ops =
+{
+ AF_INET6,
+ NULL,
+ NULL,
+ NULL,
+ dev_queue_xmit,
+ dev_queue_xmit,
+ dev_queue_xmit,
+ dev_queue_xmit
+};
+
+struct neigh_table nd_tbl =
+{
+ NULL,
+ AF_INET6,
+ sizeof(struct neighbour) + sizeof(struct in6_addr),
+ sizeof(struct in6_addr),
+ ndisc_constructor,
+ pndisc_constructor,
+ pndisc_destructor,
+ pndisc_redo,
+ { NULL, NULL, &nd_tbl, 0, NULL, NULL,
+ 30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 64, 0 },
+ 30*HZ, 128, 512, 1024,
+};
+
+#define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
+
+static u8 *ndisc_fill_option(u8 *opt, int type, void *data, int data_len)
+{
+ int space = NDISC_OPT_SPACE(data_len);
+
+ opt[0] = type;
+ opt[1] = space>>3;
+ memcpy(opt+2, data, data_len);
+ data_len += 2;
+ opt += data_len;
+ if ((space -= data_len) > 0)
+ memset(opt, 0, space);
+ return opt + space;
+}
+
+int ndisc_mc_map(struct in6_addr *addr, char *buf, struct device *dev, int dir)
+{
+ switch (dev->type) {
+ case ARPHRD_ETHER:
+ case ARPHRD_IEEE802: /* Not sure. Check it later. --ANK */
+ case ARPHRD_FDDI:
+ ipv6_eth_mc_map(addr, buf);
+ return 0;
+ default:
+ if (dir) {
+ memcpy(buf, dev->broadcast, dev->addr_len);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int ndisc_constructor(struct neighbour *neigh)
+{
+ struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;
+ struct device *dev = neigh->dev;
+ struct inet6_dev *in6_dev = ipv6_get_idev(dev);
+ int addr_type;
+
+ if (in6_dev == NULL)
+ return -EINVAL;
+
+ addr_type = ipv6_addr_type(addr);
+ if (in6_dev->nd_parms)
+ neigh->parms = in6_dev->nd_parms;
+
+ if (addr_type&IPV6_ADDR_MULTICAST)
+ neigh->type = RTN_MULTICAST;
+ else
+ neigh->type = RTN_UNICAST;
+ if (dev->hard_header == NULL) {
+ neigh->nud_state = NUD_NOARP;
+ neigh->ops = &ndisc_direct_ops;
+ neigh->output = neigh->ops->queue_xmit;
+ } else {
+ if (addr_type&IPV6_ADDR_MULTICAST) {
+ neigh->nud_state = NUD_NOARP;
+ ndisc_mc_map(addr, neigh->ha, dev, 1);
+ } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
+ neigh->nud_state = NUD_NOARP;
+ memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
+ if (dev->flags&IFF_LOOPBACK)
+ neigh->type = RTN_LOCAL;
+ } else if (dev->flags&IFF_POINTOPOINT) {
+ neigh->nud_state = NUD_NOARP;
+ memcpy(neigh->ha, dev->broadcast, dev->addr_len);
+ }
+ if (dev->hard_header_cache)
+ neigh->ops = &ndisc_hh_ops;
+ else
+ neigh->ops = &ndisc_generic_ops;
+ if (neigh->nud_state&NUD_VALID)
+ neigh->output = neigh->ops->connected_output;
+ else
+ neigh->output = neigh->ops->output;
+ }
+
+ return 0;
+}
+
+static int pndisc_constructor(struct pneigh_entry *n)
+{
+ struct in6_addr *addr = (struct in6_addr*)&n->key;
+ struct in6_addr maddr;
+ struct device *dev = n->dev;
+
+ if (dev == NULL || ipv6_get_idev(dev) == NULL)
+ return -EINVAL;
+#ifndef CONFIG_IPV6_NO_PB
+ addrconf_addr_solict_mult_old(addr, &maddr);
+ ipv6_dev_mc_inc(dev, &maddr);
+#endif
+#ifdef CONFIG_IPV6_EUI64
+ addrconf_addr_solict_mult_new(addr, &maddr);
+ ipv6_dev_mc_inc(dev, &maddr);
+#endif
+ return 0;
+}
+
+static void pndisc_destructor(struct pneigh_entry *n)
+{
+ struct in6_addr *addr = (struct in6_addr*)&n->key;
+ struct in6_addr maddr;
+ struct device *dev = n->dev;
+
+ if (dev == NULL || ipv6_get_idev(dev) == NULL)
+ return;
+#ifndef CONFIG_IPV6_NO_PB
+ addrconf_addr_solict_mult_old(addr, &maddr);
+ ipv6_dev_mc_dec(dev, &maddr);
+#endif
+#ifdef CONFIG_IPV6_EUI64
+ addrconf_addr_solict_mult_new(addr, &maddr);
+ ipv6_dev_mc_dec(dev, &maddr);
+#endif
+}
+
+
+
+static int
+ndisc_build_ll_hdr(struct sk_buff *skb, struct device *dev,
+ struct in6_addr *daddr, struct neighbour *neigh, int len)
+{
+ unsigned char ha[MAX_ADDR_LEN];
+ unsigned char *h_dest = NULL;
+
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+
+ if (dev->hard_header) {
+ if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST) {
+ ndisc_mc_map(daddr, ha, dev, 1);
+ h_dest = ha;
+ } else if (neigh) {
+ h_dest = neigh->ha;
+ } else {
+ neigh = neigh_lookup(&nd_tbl, daddr, dev);
+ if (neigh) {
+ if (neigh->nud_state&NUD_VALID) {
+ memcpy(ha, neigh->ha, dev->addr_len);
+ h_dest = ha;
+ }
+ neigh_release(neigh);
+ }
+ }
+
+ if (dev->hard_header(skb, dev, ETH_P_IPV6, h_dest, NULL, len) < 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/*
+ * Send a Neighbour Advertisement
+ */
+
+void ndisc_send_na(struct device *dev, struct neighbour *neigh,
+ struct in6_addr *daddr, struct in6_addr *solicited_addr,
+ int router, int solicited, int override, int inc_opt)
+{
+ struct sock *sk = ndisc_socket->sk;
+ struct nd_msg *msg;
+ int len;
+ struct sk_buff *skb;
+ int err;
+
+ len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+
+ if (inc_opt) {
+ if (dev->addr_len)
+ len += NDISC_OPT_SPACE(dev->addr_len);
+ else
+ inc_opt = 0;
+ }
+
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ 0, 0, &err);
+
+ if (skb == NULL) {
+ ND_PRINTK1("send_na: alloc skb failed\n");
+ return;
+ }
+
+ if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {
+ kfree_skb(skb);
+ return;
+ }
+
+ ip6_nd_hdr(sk, skb, dev, solicited_addr, daddr, IPPROTO_ICMPV6, len);
+
+ msg = (struct nd_msg *) skb_put(skb, len);
+
+ msg->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
+ msg->icmph.icmp6_code = 0;
+ msg->icmph.icmp6_cksum = 0;
+
+ msg->icmph.icmp6_unused = 0;
+ msg->icmph.icmp6_router = router;
+ msg->icmph.icmp6_solicited = solicited;
+ msg->icmph.icmp6_override = !!override;
+
+ /* Set the target address. */
+ ipv6_addr_copy(&msg->target, solicited_addr);
+
+ if (inc_opt)
+ ndisc_fill_option((void*)&msg->opt, ND_OPT_TARGET_LL_ADDR, dev->dev_addr, dev->addr_len);
+
+ /* checksum */
+ msg->icmph.icmp6_cksum = csum_ipv6_magic(solicited_addr, daddr, len,
+ IPPROTO_ICMPV6,
+ csum_partial((__u8 *) msg,
+ len, 0));
+
+ dev_queue_xmit(skb);
+
+ icmpv6_statistics.Icmp6OutNeighborAdvertisements++;
+ icmpv6_statistics.Icmp6OutMsgs++;
+}
+
+void ndisc_send_ns(struct device *dev, struct neighbour *neigh,
+ struct in6_addr *solicit,
+ struct in6_addr *daddr, struct in6_addr *saddr)
+{
+ struct sock *sk = ndisc_socket->sk;
+ struct sk_buff *skb;
+ struct nd_msg *msg;
+ int len;
+ int err;
+
+ len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+ if (dev->addr_len)
+ len += NDISC_OPT_SPACE(dev->addr_len);
+
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ 0, 0, &err);
+ if (skb == NULL) {
+ ND_PRINTK1("send_ns: alloc skb failed\n");
+ return;
+ }
+
+ if (saddr == NULL) {
+ struct inet6_ifaddr *ifa;
+
+ /* use link local address */
+ ifa = ipv6_get_lladdr(dev);
+
+ if (ifa)
+ saddr = &ifa->addr;
+ }
+
+ if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {
+ kfree_skb(skb);
+ return;
+ }
+
+ ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
+
+ msg = (struct nd_msg *)skb_put(skb, len);
+ msg->icmph.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION;
+ msg->icmph.icmp6_code = 0;
+ msg->icmph.icmp6_cksum = 0;
+ msg->icmph.icmp6_unused = 0;
+
+ /* Set the target address. */
+ ipv6_addr_copy(&msg->target, solicit);
+
+ if (dev->addr_len)
+ ndisc_fill_option((void*)&msg->opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr, dev->addr_len);
+
+ /* checksum */
+ msg->icmph.icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr,
+ daddr, len,
+ IPPROTO_ICMPV6,
+ csum_partial((__u8 *) msg,
+ len, 0));
+ /* send it! */
+ dev_queue_xmit(skb);
+
+ icmpv6_statistics.Icmp6OutNeighborSolicits++;
+ icmpv6_statistics.Icmp6OutMsgs++;
+}
+
+void ndisc_send_rs(struct device *dev, struct in6_addr *saddr,
+ struct in6_addr *daddr)
+{
+ struct sock *sk = ndisc_socket->sk;
+ struct sk_buff *skb;
+ struct icmp6hdr *hdr;
+ __u8 * opt;
+ int len;
+ int err;
+
+ len = sizeof(struct icmp6hdr);
+ if (dev->addr_len)
+ len += NDISC_OPT_SPACE(dev->addr_len);
+
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ 0, 0, &err);
+ if (skb == NULL) {
+ ND_PRINTK1("send_ns: alloc skb failed\n");
+ return;
+ }
+
+ if (ndisc_build_ll_hdr(skb, dev, daddr, NULL, len) == 0) {
+ kfree_skb(skb);
+ return;
+ }
+
+ ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
+
+ hdr = (struct icmp6hdr *) skb_put(skb, len);
+ hdr->icmp6_type = NDISC_ROUTER_SOLICITATION;
+ hdr->icmp6_code = 0;
+ hdr->icmp6_cksum = 0;
+ hdr->icmp6_unused = 0;
+
+ opt = (u8*) (hdr + 1);
+
+ if (dev->addr_len)
+ ndisc_fill_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr, dev->addr_len);
+
+ /* checksum */
+ hdr->icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, daddr, len,
+ IPPROTO_ICMPV6,
+ csum_partial((__u8 *) hdr, len, 0));
+
+ /* send it! */
+ dev_queue_xmit(skb);
+
+ icmpv6_statistics.Icmp6OutRouterSolicits++;
+ icmpv6_statistics.Icmp6OutMsgs++;
+}
+
+
+static u8 * ndisc_find_option(u8 *opt, int opt_len, int len, int option)
+{
+ while (opt_len <= len) {
+ int l = opt[1]<<3;
+
+ if (opt[0] == option && l >= opt_len)
+ return opt + 2;
+
+ if (l == 0) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "ndisc: option has 0 len\n");
+ return NULL;
+ }
+
+ opt += l;
+ len -= l;
+ }
+ return NULL;
+}
+
+
+static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
+{
+ /*
+ * "The sender MUST return an ICMP
+ * destination unreachable"
+ */
+ dst_link_failure(skb);
+ kfree_skb(skb);
+}
+
+static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
+{
+ struct in6_addr *saddr = NULL;
+ struct in6_addr mcaddr;
+ struct device *dev = neigh->dev;
+ struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
+ int probes = neigh->probes;
+
+ if (skb && ipv6_chk_addr(&skb->nh.ipv6h->saddr, dev, 0))
+ saddr = &skb->nh.ipv6h->saddr;
+
+ if ((probes -= neigh->parms->ucast_probes) < 0) {
+ if (!(neigh->nud_state&NUD_VALID))
+ ND_PRINTK1("trying to ucast probe in NUD_INVALID\n");
+ ndisc_send_ns(dev, neigh, target, target, saddr);
+ } else if ((probes -= neigh->parms->app_probes) < 0) {
+#ifdef CONFIG_ARPD
+ neigh_app_ns(neigh);
+#endif
+ } else {
+#ifdef CONFIG_IPV6_EUI64
+ addrconf_addr_solict_mult_new(target, &mcaddr);
+ ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
+#endif
+#ifndef CONFIG_IPV6_NO_PB
+ addrconf_addr_solict_mult_old(target, &mcaddr);
+ ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
+#endif
+ }
+}
+
+
+static void ndisc_update(struct neighbour *neigh, u8* opt, int len, int type)
+{
+ opt = ndisc_find_option(opt, neigh->dev->addr_len+2, len, type);
+ neigh_update(neigh, opt, NUD_STALE, 1, 1);
+}
+
+static void ndisc_router_discovery(struct sk_buff *skb)
+{
+ struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw;
+ struct neighbour *neigh;
+ struct inet6_dev *in6_dev;
+ struct rt6_info *rt;
+ int lifetime;
+ int optlen;
+
+ __u8 * opt = (__u8 *)(ra_msg + 1);
+
+ optlen = (skb->tail - skb->h.raw) - sizeof(struct ra_msg);
+
+ if (skb->nh.ipv6h->hop_limit != 255) {
+ printk(KERN_INFO
+ "NDISC: fake router advertisement received\n");
+ return;
+ }
+
+ /*
+ * set the RA_RECV flag in the interface
+ */
+
+ in6_dev = ipv6_get_idev(skb->dev);
+ if (in6_dev == NULL) {
+ ND_PRINTK1("RA: can't find in6 device\n");
+ return;
+ }
+ if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra)
+ return;
+
+ if (in6_dev->if_flags & IF_RS_SENT) {
+ /*
+ * flag that an RA was received after an RS was sent
+ * out on this interface.
+ */
+ in6_dev->if_flags |= IF_RA_RCVD;
+ }
+
+ lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
+
+ rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
+
+ if (rt && lifetime == 0) {
+ ip6_del_rt(rt);
+ dst_release(&rt->u.dst);
+ rt = NULL;
+ }
+
+ if (rt == NULL && lifetime) {
+ ND_PRINTK2("ndisc_rdisc: adding default router\n");
+
+ rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
+ if (rt == NULL) {
+ ND_PRINTK1("route_add failed\n");
+ return;
+ }
+
+ neigh = rt->rt6i_nexthop;
+ if (neigh == NULL) {
+ ND_PRINTK1("nd: add default router: null neighbour\n");
+ dst_release(&rt->u.dst);
+ return;
+ }
+ neigh->flags |= NTF_ROUTER;
+
+ /*
+ * If we where using an "all destinations on link" route
+ * delete it
+ */
+
+ rt6_purge_dflt_routers(RTF_ALLONLINK);
+ }
+
+ if (rt)
+ rt->rt6i_expires = jiffies + (HZ * lifetime);
+
+ if (ra_msg->icmph.icmp6_hop_limit)
+ in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
+
+ /*
+ * Update Reachable Time and Retrans Timer
+ */
+
+ if (in6_dev->nd_parms) {
+ if (ra_msg->retrans_timer)
+ in6_dev->nd_parms->retrans_time = (ntohl(ra_msg->retrans_timer)*HZ)/1000;
+
+ if (ra_msg->reachable_time) {
+ __u32 rtime = (ntohl(ra_msg->reachable_time)*HZ)/1000;
+
+ if (rtime != in6_dev->nd_parms->base_reachable_time) {
+ in6_dev->nd_parms->base_reachable_time = rtime;
+ in6_dev->nd_parms->gc_staletime = 3 * rtime;
+ in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
+ }
+ }
+ }
+
+ /*
+ * Process options.
+ */
+
+ while (optlen > 0) {
+ int len = (opt[1] << 3);
+
+ if (len == 0) {
+ ND_PRINTK0("RA: opt has 0 len\n");
+ break;
+ }
+
+ switch(*opt) {
+ case ND_OPT_SOURCE_LL_ADDR:
+
+ if (rt == NULL)
+ break;
+
+ if ((neigh = rt->rt6i_nexthop) != NULL &&
+ skb->dev->addr_len + 2 >= len)
+ neigh_update(neigh, opt+2, NUD_STALE, 1, 1);
+ break;
+
+ case ND_OPT_PREFIX_INFO:
+ addrconf_prefix_rcv(skb->dev, opt, len);
+ break;
+
+ case ND_OPT_MTU:
+ {
+ int mtu;
+
+ mtu = htonl(*(__u32 *)(opt+4));
+
+ if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
+ ND_PRINTK0("NDISC: router "
+ "announcement with mtu = %d\n",
+ mtu);
+ break;
+ }
+
+ if (in6_dev->cnf.mtu6 != mtu) {
+ in6_dev->cnf.mtu6 = mtu;
+
+ if (rt)
+ rt->u.dst.pmtu = mtu;
+
+ rt6_mtu_change(skb->dev, mtu);
+ }
+ }
+ break;
+
+ case ND_OPT_TARGET_LL_ADDR:
+ case ND_OPT_REDIRECT_HDR:
+ ND_PRINTK0("got illegal option with RA");
+ break;
+ default:
+ ND_PRINTK0("unknown option in RA\n");
+ };
+ optlen -= len;
+ opt += len;
+ }
+ if (rt)
+ dst_release(&rt->u.dst);
+}
+
+static void ndisc_redirect_rcv(struct sk_buff *skb)
+{
+ struct inet6_dev *in6_dev;
+ struct icmp6hdr *icmph;
+ struct in6_addr *dest;
+ struct in6_addr *target; /* new first hop to destination */
+ struct neighbour *neigh;
+ int on_link = 0;
+ int optlen;
+
+ if (skb->nh.ipv6h->hop_limit != 255) {
+ printk(KERN_WARNING "NDISC: fake ICMP redirect received\n");
+ return;
+ }
+
+ if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)) {
+ printk(KERN_WARNING "ICMP redirect: source address is not linklocal\n");
+ return;
+ }
+
+ optlen = skb->tail - skb->h.raw;
+ optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
+
+ if (optlen < 0) {
+ printk(KERN_WARNING "ICMP redirect: packet too small\n");
+ return;
+ }
+
+ icmph = (struct icmp6hdr *) skb->h.raw;
+ target = (struct in6_addr *) (icmph + 1);
+ dest = target + 1;
+
+ if (ipv6_addr_type(dest) & IPV6_ADDR_MULTICAST) {
+ printk(KERN_WARNING "ICMP redirect for multicast addr\n");
+ return;
+ }
+
+ if (ipv6_addr_cmp(dest, target) == 0) {
+ on_link = 1;
+ } else if (!(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL)) {
+ printk(KERN_WARNING "ICMP redirect: target address is not linklocal\n");
+ return;
+ }
+
+ in6_dev = ipv6_get_idev(skb->dev);
+ if (!in6_dev || in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
+ return;
+
+ /* passed validation tests */
+
+ /*
+ We install redirect only if nexthop state is valid.
+ */
+
+ neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
+ if (neigh) {
+ ndisc_update(neigh, (u8*)(dest + 1), optlen, ND_OPT_TARGET_LL_ADDR);
+ if (neigh->nud_state&NUD_VALID)
+ rt6_redirect(dest, &skb->nh.ipv6h->saddr, neigh, on_link);
+ else
+ __neigh_event_send(neigh, NULL);
+ neigh_release(neigh);
+ }
+}
+
+void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
+ struct in6_addr *target)
+{
+ struct sock *sk = ndisc_socket->sk;
+ int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
+ struct sk_buff *buff;
+ struct inet6_ifaddr *ifp;
+ struct icmp6hdr *icmph;
+ struct in6_addr *addrp;
+ struct device *dev;
+ struct rt6_info *rt;
+ u8 *opt;
+ int rd_len;
+ int err;
+ int hlen;
+
+ dev = skb->dev;
+ rt = rt6_lookup(&skb->nh.ipv6h->saddr, NULL, dev->ifindex, 1);
+
+ if (rt == NULL)
+ return;
+
+ if (rt->rt6i_flags & RTF_GATEWAY) {
+ ND_PRINTK1("ndisc_send_redirect: not a neighbour\n");
+ dst_release(&rt->u.dst);
+ return;
+ }
+ if (!xrlim_allow(&rt->u.dst, 1*HZ)) {
+ dst_release(&rt->u.dst);
+ return;
+ }
+ dst_release(&rt->u.dst);
+
+ if (dev->addr_len) {
+ if (neigh->nud_state&NUD_VALID) {
+ len += NDISC_OPT_SPACE(dev->addr_len);
+ } else {
+ /* If nexthop is not valid, do not redirect!
+ We will make it later, when will be sure,
+ that it is alive.
+ */
+ return;
+ }
+ }
+
+ rd_len = min(IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
+ rd_len &= ~0x7;
+ len += rd_len;
+
+ ifp = ipv6_get_lladdr(dev);
+
+ if (ifp == NULL) {
+ ND_PRINTK1("redirect: no link_local addr for dev\n");
+ return;
+ }
+
+ buff = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ 0, 0, &err);
+ if (buff == NULL) {
+ ND_PRINTK1("ndisc_send_redirect: alloc_skb failed\n");
+ return;
+ }
+
+ hlen = 0;
+
+ if (ndisc_build_ll_hdr(buff, dev, &skb->nh.ipv6h->saddr, NULL, len) == 0) {
+ kfree_skb(buff);
+ return;
+ }
+
+ ip6_nd_hdr(sk, buff, dev, &ifp->addr, &skb->nh.ipv6h->saddr,
+ IPPROTO_ICMPV6, len);
+
+ icmph = (struct icmp6hdr *) skb_put(buff, len);
+
+ memset(icmph, 0, sizeof(struct icmp6hdr));
+ icmph->icmp6_type = NDISC_REDIRECT;
+
+ /*
+ * copy target and destination addresses
+ */
+
+ addrp = (struct in6_addr *)(icmph + 1);
+ ipv6_addr_copy(addrp, target);
+ addrp++;
+ ipv6_addr_copy(addrp, &skb->nh.ipv6h->daddr);
+
+ opt = (u8*) (addrp + 1);
+
+ /*
+ * include target_address option
+ */
+
+ if (dev->addr_len)
+ opt = ndisc_fill_option(opt, ND_OPT_TARGET_LL_ADDR, neigh->ha, dev->addr_len);
+
+ /*
+ * build redirect option and copy skb over to the new packet.
+ */
+
+ memset(opt, 0, 8);
+ *(opt++) = ND_OPT_REDIRECT_HDR;
+ *(opt++) = (rd_len >> 3);
+ opt += 6;
+
+ memcpy(opt, skb->nh.ipv6h, rd_len - 8);
+
+ icmph->icmp6_cksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr,
+ len, IPPROTO_ICMPV6,
+ csum_partial((u8 *) icmph, len, 0));
+
+ dev_queue_xmit(buff);
+
+ icmpv6_statistics.Icmp6OutRedirects++;
+ icmpv6_statistics.Icmp6OutMsgs++;
+}
+
+static __inline__ struct neighbour *
+ndisc_recv_ns(struct in6_addr *saddr, struct sk_buff *skb)
+{
+ u8 *opt;
+
+ opt = skb->h.raw;
+ opt += sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+ opt = ndisc_find_option(opt, skb->dev->addr_len+2, skb->tail - opt, ND_OPT_SOURCE_LL_ADDR);
+
+ return neigh_event_ns(&nd_tbl, opt, saddr, skb->dev);
+}
+
+static __inline__ int ndisc_recv_na(struct neighbour *neigh, struct sk_buff *skb)
+{
+ struct nd_msg *msg = (struct nd_msg *) skb->h.raw;
+ u8 *opt;
+
+ opt = skb->h.raw;
+ opt += sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+ opt = ndisc_find_option(opt, skb->dev->addr_len+2, skb->tail - opt, ND_OPT_TARGET_LL_ADDR);
+
+ return neigh_update(neigh, opt,
+ msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
+ msg->icmph.icmp6_override, 1);
+}
+
+static void pndisc_redo(struct sk_buff *skb)
+{
+ ndisc_rcv(skb, skb->len);
+ kfree_skb(skb);
+}
+
+int ndisc_rcv(struct sk_buff *skb, unsigned long len)
+{
+ struct device *dev = skb->dev;
+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+ struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+ struct nd_msg *msg = (struct nd_msg *) skb->h.raw;
+ struct neighbour *neigh;
+ struct inet6_ifaddr *ifp;
+
+ switch (msg->icmph.icmp6_type) {
+ case NDISC_NEIGHBOUR_SOLICITATION:
+ if ((ifp = ipv6_chk_addr(&msg->target, dev, 1)) != NULL) {
+ int addr_type = ipv6_addr_type(saddr);
+
+ if (ifp->flags & ADDR_INVALID)
+ return 0;
+ if (ifp->flags & DAD_INCOMPLETE) {
+ /* Address is tentative. If the source
+ is unspecified address, it is someone
+ does DAD, otherwise we ignore solicitations
+ until DAD timer expires.
+ */
+ if (addr_type == IPV6_ADDR_ANY)
+ addrconf_dad_failure(ifp);
+ return 0;
+ }
+
+ if (addr_type == IPV6_ADDR_ANY) {
+ struct in6_addr maddr;
+
+ ipv6_addr_all_nodes(&maddr);
+ ndisc_send_na(dev, NULL, &maddr, &ifp->addr,
+ ifp->idev->cnf.forwarding, 0, 1, 1);
+ return 0;
+ }
+
+ if (addr_type & IPV6_ADDR_UNICAST) {
+ int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
+
+ if (inc)
+ nd_tbl.stats.rcv_probes_mcast++;
+ else
+ nd_tbl.stats.rcv_probes_ucast++;
+
+ /*
+ * update / create cache entry
+ * for the source adddress
+ */
+
+ neigh = ndisc_recv_ns(saddr, skb);
+
+ if (neigh) {
+ ndisc_send_na(dev, neigh, saddr, &ifp->addr,
+ ifp->idev->cnf.forwarding, 1, inc, inc);
+ neigh_release(neigh);
+ }
+ }
+ } else {
+ struct inet6_dev *in6_dev = ipv6_get_idev(dev);
+ int addr_type = ipv6_addr_type(saddr);
+
+ if (in6_dev && in6_dev->cnf.forwarding &&
+ (addr_type & IPV6_ADDR_UNICAST) &&
+ pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) {
+ int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
+
+ if (skb->stamp.tv_sec == 0 ||
+ skb->pkt_type == PACKET_HOST ||
+ inc == 0 ||
+ in6_dev->nd_parms->proxy_delay == 0) {
+ if (inc)
+ nd_tbl.stats.rcv_probes_mcast++;
+ else
+ nd_tbl.stats.rcv_probes_ucast++;
+
+ neigh = ndisc_recv_ns(saddr, skb);
+
+ if (neigh) {
+ ndisc_send_na(dev, neigh, saddr, &msg->target,
+ 0, 1, 0, inc);
+ neigh_release(neigh);
+ }
+ } else {
+ /* Hack. It will be freed upon exit from
+ ndisc_rcv
+ */
+ atomic_inc(&skb->users);
+ pneigh_enqueue(&nd_tbl, in6_dev->nd_parms, skb);
+ return 0;
+ }
+ }
+ }
+ return 0;
+
+ case NDISC_NEIGHBOUR_ADVERTISEMENT:
+ if ((ipv6_addr_type(saddr)&IPV6_ADDR_MULTICAST) &&
+ msg->icmph.icmp6_solicited) {
+ ND_PRINTK0("NDISC: solicited NA is multicasted\n");
+ return 0;
+ }
+ /* BUG! Target can be link-local on ANOTHER interface. Fixed. */
+ if ((ifp = ipv6_chk_addr(&msg->target, dev, 1))) {
+ if (ifp->flags & ADDR_INVALID)
+ return 0;
+ if (ifp->flags & DAD_INCOMPLETE) {
+ addrconf_dad_failure(ifp);
+ return 0;
+ }
+ /* What should we make now? The advertisement
+ is invalid, but ndisc specs say nothing
+ about it. It could be misconfiguration, or
+ an smart proxy agent tries to help us :-)
+ */
+ ND_PRINTK0("%s: someone avertise our address!\n",
+ ifp->idev->dev->name);
+ return 0;
+ }
+ neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 0);
+
+ if (neigh) {
+ if (neigh->flags & NTF_ROUTER) {
+ if (msg->icmph.icmp6_router == 0) {
+ /*
+ * Change: router to host
+ */
+ struct rt6_info *rt;
+ rt = rt6_get_dflt_router(saddr, skb->dev);
+ if (rt) {
+ /* It is safe only because
+ we aer in BH */
+ dst_release(&rt->u.dst);
+ ip6_del_rt(rt);
+ }
+ }
+ } else {
+ if (msg->icmph.icmp6_router)
+ neigh->flags |= NTF_ROUTER;
+ }
+
+ ndisc_recv_na(neigh, skb);
+ neigh_release(neigh);
+ }
+ break;
+
+ case NDISC_ROUTER_ADVERTISEMENT:
+ ndisc_router_discovery(skb);
+ break;
+
+ case NDISC_REDIRECT:
+ ndisc_redirect_rcv(skb);
+ break;
+ };
+
+ return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+#ifndef CONFIG_RTNETLINK
+int ndisc_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len=0;
+ off_t pos=0;
+ int size;
+ unsigned long now = jiffies;
+ int i;
+
+ neigh_table_lock(&nd_tbl);
+
+ for (i = 0; i <= NEIGH_HASHMASK; i++) {
+ struct neighbour *neigh;
+
+ for (neigh = nd_tbl.hash_buckets[i]; neigh; neigh = neigh->next) {
+ int j;
+
+ size = 0;
+ for (j=0; j<16; j++) {
+ sprintf(buffer+len+size, "%02x", neigh->primary_key[j]);
+ size += 2;
+ }
+
+ size += sprintf(buffer+len+size,
+ " %02x %02x %02x %02x %08lx %08lx %08x %04x %04x %04x %8s ", i,
+ 128,
+ neigh->type,
+ neigh->nud_state,
+ now - neigh->used,
+ now - neigh->confirmed,
+ neigh->parms->reachable_time,
+ neigh->parms->gc_staletime,
+ atomic_read(&neigh->refcnt),
+ neigh->flags | (!neigh->hh ? 0 : (neigh->hh->hh_output==dev_queue_xmit ? 4 : 2)),
+ neigh->dev->name);
+
+ if ((neigh->nud_state&NUD_VALID) && neigh->dev->addr_len) {
+ for (j=0; j < neigh->dev->addr_len; j++) {
+ sprintf(buffer+len+size, "%02x", neigh->ha[j]);
+ size += 2;
+ }
+ } else {
+ size += sprintf(buffer+len+size, "000000000000");
+ }
+ size += sprintf(buffer+len+size, "\n");
+ len += size;
+ pos += size;
+
+ if (pos <= offset)
+ len=0;
+ if (pos >= offset+length)
+ goto done;
+ }
+ }
+
+done:
+ neigh_table_unlock(&nd_tbl);
+
+ *start = buffer+len-(pos-offset); /* Start of wanted data */
+ len = pos-offset; /* Start slop */
+ if (len>length)
+ len = length; /* Ending slop */
+ if (len<0)
+ len = 0;
+ return len;
+}
+
+struct proc_dir_entry ndisc_proc_entry =
+{
+ PROC_NET_NDISC, 5, "ndisc",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, NULL,
+ &ndisc_get_info
+};
+#endif
+#endif /* CONFIG_PROC_FS */
+
+
+
+__initfunc(int ndisc_init(struct net_proto_family *ops))
+{
+ struct sock *sk;
+ int err;
+
+ ndisc_socket = sock_alloc();
+ if (ndisc_socket == NULL) {
+ printk(KERN_ERR
+ "Failed to create the NDISC control socket.\n");
+ return -1;
+ }
+#ifndef _HURD_
+ ndisc_socket->inode->i_uid = 0;
+ ndisc_socket->inode->i_gid = 0;
+#endif
+ ndisc_socket->type = SOCK_RAW;
+
+ if((err = ops->create(ndisc_socket, IPPROTO_ICMPV6)) < 0) {
+ printk(KERN_DEBUG
+ "Failed to initializee the NDISC control socket (err %d).\n",
+ err);
+ sock_release(ndisc_socket);
+ ndisc_socket = NULL; /* For safety. */
+ return err;
+ }
+
+ sk = ndisc_socket->sk;
+ sk->allocation = GFP_ATOMIC;
+ sk->net_pinfo.af_inet6.hop_limit = 255;
+ /* Do not loopback ndisc messages */
+ sk->net_pinfo.af_inet6.mc_loop = 0;
+ sk->num = 256;
+
+ /*
+ * Initialize the neighbour table
+ */
+
+ neigh_table_init(&nd_tbl);
+
+#ifdef CONFIG_PROC_FS
+#ifndef CONFIG_RTNETLINK
+ proc_net_register(&ndisc_proc_entry);
+#endif
+#endif
+#ifdef CONFIG_SYSCTL
+ neigh_sysctl_register(NULL, &nd_tbl.parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6");
+#endif
+
+ return 0;
+}
+
+void ndisc_cleanup(void)
+{
+#ifdef CONFIG_PROC_FS
+#ifndef CONFIG_RTNETLINK
+ proc_net_unregister(ndisc_proc_entry.low_ino);
+#endif
+#endif
+ neigh_table_clear(&nd_tbl);
+ sock_release(ndisc_socket);
+ ndisc_socket = NULL; /* For safety. */
+}
diff --git a/pfinet/linux-src/net/ipv6/protocol_ipv6.c b/pfinet/linux-src/net/ipv6/protocol_ipv6.c
new file mode 100644
index 00000000..ad871914
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/protocol_ipv6.c
@@ -0,0 +1,117 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * PF_INET6 protocol dispatch tables.
+ *
+ * Version: $Id: protocol_ipv6.c,v 1.1 2007/10/08 21:12:31 stesie Exp $
+ *
+ * Authors: Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/sched.h>
+#include <linux/net.h>
+#include <linux/in6.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/protocol.h>
+
+struct inet6_protocol *inet6_protocol_base = NULL;
+struct inet6_protocol *inet6_protos[MAX_INET_PROTOS] =
+{
+ NULL
+};
+
+
+struct inet6_protocol *inet6_get_protocol(unsigned char prot)
+{
+ unsigned char hash;
+ struct inet6_protocol *p;
+
+ hash = prot & (MAX_INET_PROTOS - 1);
+ for (p = inet6_protos[hash] ; p != NULL; p=p->next) {
+ if (p->protocol == prot)
+ return((struct inet6_protocol *) p);
+ }
+ return(NULL);
+}
+
+void inet6_add_protocol(struct inet6_protocol *prot)
+{
+ unsigned char hash;
+ struct inet6_protocol *p2;
+
+ hash = prot->protocol & (MAX_INET_PROTOS - 1);
+ prot->next = inet6_protos[hash];
+ inet6_protos[hash] = prot;
+ prot->copy = 0;
+
+ /*
+ * Set the copy bit if we need to.
+ */
+
+ p2 = (struct inet6_protocol *) prot->next;
+ while(p2 != NULL) {
+ if (p2->protocol == prot->protocol) {
+ prot->copy = 1;
+ break;
+ }
+ p2 = (struct inet6_protocol *) p2->next;
+ }
+}
+
+/*
+ * Remove a protocol from the hash tables.
+ */
+
+int inet6_del_protocol(struct inet6_protocol *prot)
+{
+ struct inet6_protocol *p;
+ struct inet6_protocol *lp = NULL;
+ unsigned char hash;
+
+ hash = prot->protocol & (MAX_INET_PROTOS - 1);
+ if (prot == inet6_protos[hash]) {
+ inet6_protos[hash] = (struct inet6_protocol *) inet6_protos[hash]->next;
+ return(0);
+ }
+
+ p = (struct inet6_protocol *) inet6_protos[hash];
+ while(p != NULL) {
+ /*
+ * We have to worry if the protocol being deleted is
+ * the last one on the list, then we may need to reset
+ * someone's copied bit.
+ */
+ if (p->next != NULL && p->next == prot) {
+ /*
+ * if we are the last one with this protocol and
+ * there is a previous one, reset its copy bit.
+ */
+ if (p->copy == 0 && lp != NULL)
+ lp->copy = 0;
+ p->next = prot->next;
+ return(0);
+ }
+ if (p->next != NULL && p->next->protocol == prot->protocol)
+ lp = p;
+
+ p = (struct inet6_protocol *) p->next;
+ }
+ return(-1);
+}
diff --git a/pfinet/linux-src/net/ipv6/raw_ipv6.c b/pfinet/linux-src/net/ipv6/raw_ipv6.c
new file mode 100644
index 00000000..64bc247c
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/raw_ipv6.c
@@ -0,0 +1,691 @@
+/*
+ * RAW sockets for IPv6
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * Adapted from linux/net/ipv4/raw.c
+ *
+ * $Id: raw_ipv6.c,v 1.2 2007/10/13 01:43:00 stesie Exp $
+ *
+ * Fixes:
+ * YOSHIFUJI,H.@USAGI : raw checksum (RFC2292(bis) compliance)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/sched.h>
+#include <linux/net.h>
+#include <linux/in6.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/icmpv6.h>
+#include <asm/uaccess.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+#include <net/protocol.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+#include <net/transp_v6.h>
+
+#include <net/rawv6.h>
+
+#include <asm/uaccess.h>
+
+struct sock *raw_v6_htable[RAWV6_HTABLE_SIZE];
+
+static void raw_v6_hash(struct sock *sk)
+{
+ struct sock **skp = &raw_v6_htable[sk->num & (RAWV6_HTABLE_SIZE - 1)];
+
+ SOCKHASH_LOCK();
+ if ((sk->next = *skp) != NULL)
+ (*skp)->pprev = &sk->next;
+ *skp = sk;
+ sk->pprev = skp;
+ SOCKHASH_UNLOCK();
+}
+
+static void raw_v6_unhash(struct sock *sk)
+{
+ SOCKHASH_LOCK();
+ if (sk->pprev) {
+ if (sk->next)
+ sk->next->pprev = sk->pprev;
+ *sk->pprev = sk->next;
+ sk->pprev = NULL;
+ }
+ SOCKHASH_UNLOCK();
+}
+
+static __inline__ int inet6_mc_check(struct sock *sk, struct in6_addr *addr)
+{
+ struct ipv6_mc_socklist *mc;
+
+ for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
+ if (ipv6_addr_cmp(&mc->addr, addr) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Grumble... icmp and ip_input want to get at this... */
+struct sock *raw_v6_lookup(struct sock *sk, unsigned short num,
+ struct in6_addr *loc_addr, struct in6_addr *rmt_addr)
+{
+ struct sock *s = sk;
+ int addr_type = ipv6_addr_type(loc_addr);
+
+ for(s = sk; s; s = s->next) {
+ if((s->num == num) &&
+ !(s->dead && (s->state == TCP_CLOSE))) {
+ struct ipv6_pinfo *np = &s->net_pinfo.af_inet6;
+
+ if (!ipv6_addr_any(&np->daddr) &&
+ ipv6_addr_cmp(&np->daddr, rmt_addr))
+ continue;
+
+ if (!ipv6_addr_any(&np->rcv_saddr)) {
+ if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
+ return(s);
+ if ((addr_type & IPV6_ADDR_MULTICAST) &&
+ inet6_mc_check(s, loc_addr))
+ return (s);
+ continue;
+ }
+ return(s);
+ }
+ }
+ return NULL;
+}
+
+/* This cleans up af_inet6 a bit. -DaveM */
+static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+{
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr;
+ __u32 v4addr = 0;
+ int addr_type;
+
+ /* Check these errors. */
+ if (sk->state != TCP_CLOSE || (addr_len < sizeof(struct sockaddr_in6)))
+ return -EINVAL;
+
+ addr_type = ipv6_addr_type(&addr->sin6_addr);
+
+ /* Check if the address belongs to the host. */
+ if (addr_type == IPV6_ADDR_MAPPED) {
+ /* Raw sockets are IPv6 only */
+ return(-EADDRNOTAVAIL);
+ } else {
+ if (addr_type != IPV6_ADDR_ANY) {
+ if (addr_type & IPV6_ADDR_LINKLOCAL) {
+ if (addr_len >= sizeof(struct sockaddr_in6) &&
+ addr->sin6_scope_id) {
+ /* Override any existing binding,
+ * if another one is supplied by user.
+ */
+ sk->bound_dev_if =
+ addr->sin6_scope_id;
+ }
+
+ /* Binding to link-local address requires
+ an interface */
+ if (!sk->bound_dev_if)
+ return(-EINVAL);
+
+ if (!dev_get_by_index(sk->bound_dev_if))
+ return(-ENODEV);
+ }
+
+ /* ipv4 addr of the socket is invalid. Only the
+ * unpecified and mapped address have a v4 equivalent.
+ */
+ v4addr = LOOPBACK4_IPV6;
+ if (!(addr_type & IPV6_ADDR_MULTICAST)) {
+ if (ipv6_chk_addr(&addr->sin6_addr, NULL, 0) == NULL)
+ return(-EADDRNOTAVAIL);
+ }
+ }
+ }
+
+ sk->rcv_saddr = v4addr;
+ sk->saddr = v4addr;
+ memcpy(&sk->net_pinfo.af_inet6.rcv_saddr, &addr->sin6_addr,
+ sizeof(struct in6_addr));
+ if (!(addr_type & IPV6_ADDR_MULTICAST))
+ memcpy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr,
+ sizeof(struct in6_addr));
+ return 0;
+}
+
+void rawv6_err(struct sock *sk, struct sk_buff *skb, struct ipv6hdr *hdr,
+ struct inet6_skb_parm *opt,
+ int type, int code, unsigned char *buff, u32 info)
+{
+ int err;
+ int harderr;
+
+ if (buff > skb->tail)
+ return;
+
+ /* Report error on raw socket, if:
+ 1. User requested recverr.
+ 2. Socket is connected (otherwise the error indication
+ is useless without recverr and error is hard.
+ */
+ if (!sk->net_pinfo.af_inet6.recverr && sk->state != TCP_ESTABLISHED)
+ return;
+
+ harderr = icmpv6_err_convert(type, code, &err);
+ if (type == ICMPV6_PKT_TOOBIG)
+ harderr = (sk->net_pinfo.af_inet6.pmtudisc == IPV6_PMTUDISC_DO);
+
+ if (sk->net_pinfo.af_inet6.recverr)
+ ipv6_icmp_error(sk, skb, err, 0, ntohl(info), buff);
+
+ if (sk->net_pinfo.af_inet6.recverr || harderr) {
+ sk->err = err;
+ sk->error_report(sk);
+ }
+}
+
+static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb)
+{
+ /* Charge it to the socket. */
+ if (sock_queue_rcv_skb(sk,skb)<0) {
+ ipv6_statistics.Ip6InDiscards++;
+ kfree_skb(skb);
+ return 0;
+ }
+
+ ipv6_statistics.Ip6InDelivers++;
+ return 0;
+}
+
+/*
+ * This is next to useless...
+ * if we demultiplex in network layer we don't need the extra call
+ * just to queue the skb...
+ * maybe we could have the network decide uppon a hint if it
+ * should call raw_rcv for demultiplexing
+ */
+int rawv6_rcv(struct sock *sk, struct sk_buff *skb, unsigned long len)
+{
+ if (sk->ip_hdrincl)
+ skb->h.raw = skb->nh.raw;
+
+ rawv6_rcv_skb(sk, skb);
+ return 0;
+}
+
+
+/*
+ * This should be easy, if there is something there
+ * we return it, otherwise we block.
+ */
+
+int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
+ int noblock, int flags, int *addr_len)
+{
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)msg->msg_name;
+ struct sk_buff *skb;
+ int copied, err;
+
+ if (flags & MSG_OOB)
+ return -EOPNOTSUPP;
+
+ if (addr_len)
+ *addr_len=sizeof(*sin6);
+
+ if (flags & MSG_ERRQUEUE)
+ return ipv6_recv_error(sk, msg, len);
+
+ skb = skb_recv_datagram(sk, flags, noblock, &err);
+ if (!skb)
+ goto out;
+
+ copied = skb->tail - skb->h.raw;
+ if (copied > len) {
+ copied = len;
+ msg->msg_flags |= MSG_TRUNC;
+ }
+
+ err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ sk->stamp=skb->stamp;
+ if (err)
+ goto out_free;
+
+ /* Copy the address. */
+ if (sin6) {
+ sin6->sin6_family = AF_INET6;
+ memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr,
+ sizeof(struct in6_addr));
+ sin6->sin6_flowinfo = 0;
+ sin6->sin6_scope_id = 0;
+ if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
+ sin6->sin6_scope_id =
+ ((struct inet6_skb_parm *) skb->cb)->iif;
+ }
+
+ if (sk->net_pinfo.af_inet6.rxopt.all)
+ datagram_recv_ctl(sk, msg, skb);
+ err = copied;
+
+out_free:
+ skb_free_datagram(sk, skb);
+out:
+ return err;
+}
+
+/*
+ * Sending...
+ */
+
+struct rawv6_fakehdr {
+ struct iovec *iov;
+ struct sock *sk;
+ __u32 len;
+ __u32 cksum;
+ __u32 proto;
+ struct in6_addr *daddr;
+};
+
+static int rawv6_getfrag(const void *data, struct in6_addr *saddr,
+ char *buff, unsigned int offset, unsigned int len)
+{
+ struct iovec *iov = (struct iovec *) data;
+
+ return memcpy_fromiovecend(buff, iov, offset, len);
+}
+
+static int rawv6_frag_cksum(const void *data, struct in6_addr *addr,
+ char *buff, unsigned int offset,
+ unsigned int len)
+{
+ struct rawv6_fakehdr *hdr = (struct rawv6_fakehdr *) data;
+
+ if (csum_partial_copy_fromiovecend(buff, hdr->iov, offset,
+ len, &hdr->cksum))
+ return -EFAULT;
+
+ if (offset == 0) {
+ struct sock *sk;
+ struct raw6_opt *opt;
+ struct in6_addr *daddr;
+
+ sk = hdr->sk;
+ opt = &sk->tp_pinfo.tp_raw;
+
+ if (hdr->daddr)
+ daddr = hdr->daddr;
+ else
+ daddr = addr + 1;
+
+ hdr->cksum = csum_ipv6_magic(addr, daddr, hdr->len,
+ hdr->proto, hdr->cksum);
+
+ if (opt->offset + 1 < len) {
+ __u16 *csum;
+
+ csum = (__u16 *) (buff + opt->offset);
+ if (*csum) {
+ /* in case cksum was not initialized */
+ __u32 sum = hdr->cksum;
+ sum += *csum;
+ *csum = hdr->cksum = (sum + (sum>>16));
+ } else {
+ *csum = hdr->cksum;
+ }
+ } else {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "icmp: cksum offset too big\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+
+static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
+{
+ struct ipv6_txoptions opt_space;
+ struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name;
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct ipv6_txoptions *opt = NULL;
+ struct ip6_flowlabel *flowlabel = NULL;
+ struct flowi fl;
+ int addr_len = msg->msg_namelen;
+ struct in6_addr *daddr;
+ struct raw6_opt *raw_opt;
+ int hlimit = -1;
+ u16 proto;
+ int err;
+
+ /* Rough check on arithmetic overflow,
+ better check is made in ip6_build_xmit
+ */
+ if (len < 0)
+ return -EMSGSIZE;
+
+ /* Mirror BSD error message compatibility */
+ if (msg->msg_flags & MSG_OOB)
+ return -EOPNOTSUPP;
+
+ if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
+ return(-EINVAL);
+ /*
+ * Get and verify the address.
+ */
+
+ fl.fl6_flowlabel = 0;
+
+ if (sin6) {
+ if (addr_len < sizeof(struct sockaddr_in6))
+ return(-EINVAL);
+
+ if (sin6->sin6_family && sin6->sin6_family != AF_INET6)
+ return(-EINVAL);
+
+ /* port is the proto value [0..255] carried in nexthdr */
+ proto = ntohs(sin6->sin6_port);
+
+ if (!proto)
+ proto = sk->num;
+
+ if (proto > 255)
+ return(-EINVAL);
+
+ daddr = &sin6->sin6_addr;
+ if (np->sndflow) {
+ fl.fl6_flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK;
+ if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
+ flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
+ if (flowlabel == NULL)
+ return -EINVAL;
+ daddr = &flowlabel->dst;
+ }
+ }
+
+
+ /* Otherwise it will be difficult to maintain sk->dst_cache. */
+ if (sk->state == TCP_ESTABLISHED &&
+ !ipv6_addr_cmp(daddr, &sk->net_pinfo.af_inet6.daddr))
+ daddr = &sk->net_pinfo.af_inet6.daddr;
+
+ if (addr_len >= sizeof(struct sockaddr_in6) &&
+ sin6->sin6_scope_id &&
+ ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL)
+ fl.oif = sin6->sin6_scope_id;
+ } else {
+ if (sk->state != TCP_ESTABLISHED)
+ return(-EINVAL);
+
+ proto = sk->num;
+ daddr = &(sk->net_pinfo.af_inet6.daddr);
+ fl.fl6_flowlabel = np->flow_label;
+ }
+
+ if (ipv6_addr_any(daddr)) {
+ /*
+ * unspecfied destination address
+ * treated as error... is this correct ?
+ */
+ return(-EINVAL);
+ }
+
+ fl.oif = sk->bound_dev_if;
+ fl.fl6_src = NULL;
+
+ if (msg->msg_controllen) {
+ opt = &opt_space;
+ memset(opt, 0, sizeof(struct ipv6_txoptions));
+
+ err = datagram_send_ctl(msg, &fl, opt, &hlimit);
+ if (err < 0) {
+ fl6_sock_release(flowlabel);
+ return err;
+ }
+ if ((fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) {
+ flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
+ if (flowlabel == NULL)
+ return -EINVAL;
+ }
+ if (!(opt->opt_nflen|opt->opt_flen))
+ opt = NULL;
+ }
+ if (opt == NULL)
+ opt = np->opt;
+ if (flowlabel)
+ opt = fl6_merge_options(&opt_space, flowlabel, opt);
+
+ raw_opt = &sk->tp_pinfo.tp_raw;
+
+ fl.proto = proto;
+ fl.fl6_dst = daddr;
+ fl.uli_u.icmpt.type = 0;
+ fl.uli_u.icmpt.code = 0;
+
+ if (raw_opt->checksum) {
+ struct rawv6_fakehdr hdr;
+
+ hdr.iov = msg->msg_iov;
+ hdr.sk = sk;
+ hdr.len = len;
+ hdr.cksum = 0;
+ hdr.proto = proto;
+
+ if (opt && opt->srcrt)
+ hdr.daddr = daddr;
+ else
+ hdr.daddr = NULL;
+
+ err = ip6_build_xmit(sk, rawv6_frag_cksum, &hdr, &fl, len,
+ opt, hlimit, msg->msg_flags);
+ } else {
+ err = ip6_build_xmit(sk, rawv6_getfrag, msg->msg_iov, &fl, len,
+ opt, hlimit, msg->msg_flags);
+ }
+
+ fl6_sock_release(flowlabel);
+
+ return err<0?err:len;
+}
+
+static int rawv6_seticmpfilter(struct sock *sk, int level, int optname,
+ char *optval, int optlen)
+{
+ switch (optname) {
+ case ICMPV6_FILTER:
+ if (optlen > sizeof(struct icmp6_filter))
+ optlen = sizeof(struct icmp6_filter);
+ if (copy_from_user(&sk->tp_pinfo.tp_raw.filter, optval, optlen))
+ return -EFAULT;
+ return 0;
+ default:
+ return -ENOPROTOOPT;
+ };
+
+ return 0;
+}
+
+static int rawv6_geticmpfilter(struct sock *sk, int level, int optname,
+ char *optval, int *optlen)
+{
+ int len;
+
+ switch (optname) {
+ case ICMPV6_FILTER:
+ if (get_user(len, optlen))
+ return -EFAULT;
+ if (len > sizeof(struct icmp6_filter))
+ len = sizeof(struct icmp6_filter);
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &sk->tp_pinfo.tp_raw.filter, len))
+ return -EFAULT;
+ return 0;
+ default:
+ return -ENOPROTOOPT;
+ };
+
+ return 0;
+}
+
+
+static int rawv6_setsockopt(struct sock *sk, int level, int optname,
+ char *optval, int optlen)
+{
+ struct raw6_opt *opt = &sk->tp_pinfo.tp_raw;
+ int val;
+
+ switch(level) {
+ case SOL_RAW:
+ break;
+
+ case SOL_ICMPV6:
+ if (sk->num != IPPROTO_ICMPV6)
+ return -EOPNOTSUPP;
+ return rawv6_seticmpfilter(sk, level, optname, optval,
+ optlen);
+ case SOL_IPV6:
+ if (optname == IPV6_CHECKSUM)
+ break;
+ default:
+ return ipv6_setsockopt(sk, level, optname, optval,
+ optlen);
+ };
+
+ if (get_user(val, (int *)optval))
+ return -EFAULT;
+
+ switch (optname) {
+ case IPV6_CHECKSUM:
+ /* You may get strange result with a positive odd offset;
+ RFC2292bis agrees with me. */
+ if (val > 0 && (val&1))
+ return(-EINVAL);
+ if (val < 0) {
+ opt->checksum = 0;
+ } else {
+ opt->checksum = 1;
+ opt->offset = val;
+ }
+
+ return 0;
+ break;
+
+ default:
+ return(-ENOPROTOOPT);
+ }
+}
+
+static int rawv6_getsockopt(struct sock *sk, int level, int optname,
+ char *optval, int *optlen)
+{
+ struct raw6_opt *opt = &sk->tp_pinfo.tp_raw;
+ int val, len;
+
+ switch(level) {
+ case SOL_RAW:
+ break;
+
+ case SOL_ICMPV6:
+ if (sk->num != IPPROTO_ICMPV6)
+ return -EOPNOTSUPP;
+ return rawv6_geticmpfilter(sk, level, optname, optval,
+ optlen);
+ case SOL_IPV6:
+ if (optname == IPV6_CHECKSUM)
+ break;
+ default:
+ return ipv6_getsockopt(sk, level, optname, optval,
+ optlen);
+ };
+
+ if (get_user(len,optlen))
+ return -EFAULT;
+
+ switch (optname) {
+ case IPV6_CHECKSUM:
+ if (opt->checksum == 0)
+ val = -1;
+ else
+ val = opt->offset;
+
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ len=min(sizeof(int),len);
+
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval,&val,len))
+ return -EFAULT;
+ return 0;
+}
+
+
+static void rawv6_close(struct sock *sk, long timeout)
+{
+ /* See for explanation: raw_close in ipv4/raw.c */
+ sk->state = TCP_CLOSE;
+ raw_v6_unhash(sk);
+ if (sk->num == IPPROTO_RAW)
+ ip6_ra_control(sk, -1, NULL);
+ sk->dead = 1;
+ destroy_sock(sk);
+}
+
+static int rawv6_init_sk(struct sock *sk)
+{
+ if (sk->num == IPPROTO_ICMPV6){
+ struct raw6_opt *opt = &sk->tp_pinfo.tp_raw;
+ opt->checksum = 1;
+ opt->offset = 2;
+ }
+ return(0);
+}
+
+struct proto rawv6_prot = {
+ (struct sock *)&rawv6_prot, /* sklist_next */
+ (struct sock *)&rawv6_prot, /* sklist_prev */
+ rawv6_close, /* close */
+ udpv6_connect, /* connect */
+ NULL, /* accept */
+ NULL, /* retransmit */
+ NULL, /* write_wakeup */
+ NULL, /* read_wakeup */
+ datagram_poll, /* poll */
+ NULL, /* ioctl */
+ rawv6_init_sk, /* init */
+ inet6_destroy_sock, /* destroy */
+ NULL, /* shutdown */
+ rawv6_setsockopt, /* setsockopt */
+ rawv6_getsockopt, /* getsockopt */
+ rawv6_sendmsg, /* sendmsg */
+ rawv6_recvmsg, /* recvmsg */
+ rawv6_bind, /* bind */
+ rawv6_rcv_skb, /* backlog_rcv */
+ raw_v6_hash, /* hash */
+ raw_v6_unhash, /* unhash */
+ NULL, /* get_port */
+ 128, /* max_header */
+ 0, /* retransmits */
+ "RAW", /* name */
+ 0, /* inuse */
+ 0 /* highestinuse */
+};
diff --git a/pfinet/linux-src/net/ipv6/reassembly.c b/pfinet/linux-src/net/ipv6/reassembly.c
new file mode 100644
index 00000000..3e1575dd
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/reassembly.c
@@ -0,0 +1,492 @@
+/*
+ * IPv6 fragment reassembly
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: reassembly.c,v 1.1 2007/10/08 21:12:31 stesie Exp $
+ *
+ * Based on: net/ipv4/ip_fragment.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * Fixes:
+ * Andi Kleen Make it work with multiple hosts.
+ * More RFC compliance.
+ */
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/sched.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+#include <linux/ipv6.h>
+#include <linux/icmpv6.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/transp_v6.h>
+#include <net/rawv6.h>
+#include <net/ndisc.h>
+#include <net/addrconf.h>
+
+int sysctl_ip6frag_high_thresh = 256*1024;
+int sysctl_ip6frag_low_thresh = 192*1024;
+int sysctl_ip6frag_time = IPV6_FRAG_TIMEOUT;
+
+atomic_t ip6_frag_mem = ATOMIC_INIT(0);
+
+struct ipv6_frag {
+ __u16 offset;
+ __u16 len;
+ struct sk_buff *skb;
+
+ struct frag_hdr *fhdr;
+
+ struct ipv6_frag *next;
+};
+
+/*
+ * Equivalent of ipv4 struct ipq
+ */
+
+struct frag_queue {
+
+ struct frag_queue *next;
+ struct frag_queue *prev;
+
+ __u32 id; /* fragment id */
+ struct in6_addr saddr;
+ struct in6_addr daddr;
+ struct timer_list timer; /* expire timer */
+ struct ipv6_frag *fragments;
+ struct device *dev;
+ int iif;
+ __u8 last_in; /* has first/last segment arrived? */
+#define FIRST_IN 2
+#define LAST_IN 1
+ __u8 nexthdr;
+ __u16 nhoffset;
+};
+
+static struct frag_queue ipv6_frag_queue = {
+ &ipv6_frag_queue, &ipv6_frag_queue,
+ 0, {{{0}}}, {{{0}}},
+ {0}, NULL, NULL,
+ 0, 0, 0, 0
+};
+
+/* Memory Tracking Functions. */
+extern __inline__ void frag_kfree_skb(struct sk_buff *skb)
+{
+ atomic_sub(skb->truesize, &ip6_frag_mem);
+ kfree_skb(skb);
+}
+
+extern __inline__ void frag_kfree_s(void *ptr, int len)
+{
+ atomic_sub(len, &ip6_frag_mem);
+ kfree(ptr);
+}
+
+extern __inline__ void *frag_kmalloc(int size, int pri)
+{
+ void *vp = kmalloc(size, pri);
+
+ if(!vp)
+ return NULL;
+ atomic_add(size, &ip6_frag_mem);
+ return vp;
+}
+
+
+static void create_frag_entry(struct sk_buff *skb,
+ __u8 *nhptr,
+ struct frag_hdr *fhdr);
+static u8 * reasm_frag(struct frag_queue *fq,
+ struct sk_buff **skb_in);
+
+static void reasm_queue(struct frag_queue *fq,
+ struct sk_buff *skb,
+ struct frag_hdr *fhdr,
+ u8 *nhptr);
+
+static void fq_free(struct frag_queue *fq);
+
+static void frag_prune(void)
+{
+ struct frag_queue *fq;
+
+ while ((fq = ipv6_frag_queue.next) != &ipv6_frag_queue) {
+ ipv6_statistics.Ip6ReasmFails++;
+ fq_free(fq);
+ if (atomic_read(&ip6_frag_mem) <= sysctl_ip6frag_low_thresh)
+ return;
+ }
+ if (atomic_read(&ip6_frag_mem))
+ printk(KERN_DEBUG "IPv6 frag_prune: memleak\n");
+ atomic_set(&ip6_frag_mem, 0);
+}
+
+
+u8* ipv6_reassembly(struct sk_buff **skbp, __u8 *nhptr)
+{
+ struct sk_buff *skb = *skbp;
+ struct frag_hdr *fhdr = (struct frag_hdr *) (skb->h.raw);
+ struct frag_queue *fq;
+ struct ipv6hdr *hdr;
+
+ hdr = skb->nh.ipv6h;
+
+ ipv6_statistics.Ip6ReasmReqds++;
+
+ /* Jumbo payload inhibits frag. header */
+ if (hdr->payload_len==0) {
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw);
+ return NULL;
+ }
+ if ((u8 *)(fhdr+1) > skb->tail) {
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw);
+ return NULL;
+ }
+ if (atomic_read(&ip6_frag_mem) > sysctl_ip6frag_high_thresh)
+ frag_prune();
+
+ for (fq = ipv6_frag_queue.next; fq != &ipv6_frag_queue; fq = fq->next) {
+ if (fq->id == fhdr->identification &&
+ !ipv6_addr_cmp(&hdr->saddr, &fq->saddr) &&
+ !ipv6_addr_cmp(&hdr->daddr, &fq->daddr)) {
+
+ reasm_queue(fq, skb, fhdr, nhptr);
+
+ if (fq->last_in == (FIRST_IN|LAST_IN))
+ return reasm_frag(fq, skbp);
+
+ return NULL;
+ }
+ }
+
+ create_frag_entry(skb, nhptr, fhdr);
+
+ return NULL;
+}
+
+
+static void fq_free(struct frag_queue *fq)
+{
+ struct ipv6_frag *fp, *back;
+
+ del_timer(&fq->timer);
+
+ for (fp = fq->fragments; fp; ) {
+ frag_kfree_skb(fp->skb);
+ back = fp;
+ fp=fp->next;
+ frag_kfree_s(back, sizeof(*back));
+ }
+
+ fq->prev->next = fq->next;
+ fq->next->prev = fq->prev;
+
+ fq->prev = fq->next = NULL;
+
+ frag_kfree_s(fq, sizeof(*fq));
+}
+
+static void frag_expire(unsigned long data)
+{
+ struct frag_queue *fq;
+ struct ipv6_frag *frag;
+
+ fq = (struct frag_queue *) data;
+
+ frag = fq->fragments;
+
+ ipv6_statistics.Ip6ReasmTimeout++;
+ ipv6_statistics.Ip6ReasmFails++;
+
+ if (frag == NULL) {
+ printk(KERN_DEBUG "invalid fragment queue\n");
+ return;
+ }
+
+ /* Send error only if the first segment arrived.
+ (fixed --ANK (980728))
+ */
+ if (fq->last_in&FIRST_IN) {
+ struct device *dev = dev_get_by_index(fq->iif);
+
+ /*
+ But use as source device on which LAST ARRIVED
+ segment was received. And do not use fq->dev
+ pointer directly, device might already disappeared.
+ */
+ if (dev) {
+ frag->skb->dev = dev;
+ icmpv6_send(frag->skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0,
+ dev);
+ }
+ }
+
+ fq_free(fq);
+}
+
+
+static void create_frag_entry(struct sk_buff *skb,
+ __u8 *nhptr,
+ struct frag_hdr *fhdr)
+{
+ struct frag_queue *fq;
+ struct ipv6hdr *hdr;
+
+ fq = (struct frag_queue *) frag_kmalloc(sizeof(struct frag_queue),
+ GFP_ATOMIC);
+
+ if (fq == NULL) {
+ ipv6_statistics.Ip6ReasmFails++;
+ kfree_skb(skb);
+ return;
+ }
+
+ memset(fq, 0, sizeof(struct frag_queue));
+
+ fq->id = fhdr->identification;
+
+ hdr = skb->nh.ipv6h;
+ ipv6_addr_copy(&fq->saddr, &hdr->saddr);
+ ipv6_addr_copy(&fq->daddr, &hdr->daddr);
+
+ /* init_timer has been done by the memset */
+ fq->timer.function = frag_expire;
+ fq->timer.data = (long) fq;
+ fq->timer.expires = jiffies + sysctl_ip6frag_time;
+
+ reasm_queue(fq, skb, fhdr, nhptr);
+
+ if (fq->fragments) {
+ fq->prev = ipv6_frag_queue.prev;
+ fq->next = &ipv6_frag_queue;
+ fq->prev->next = fq;
+ ipv6_frag_queue.prev = fq;
+
+ add_timer(&fq->timer);
+ } else
+ frag_kfree_s(fq, sizeof(*fq));
+}
+
+
+/*
+ * We queue the packet even if it's the last.
+ * It's a trade off. This allows the reassembly
+ * code to be simpler (=faster) and of the
+ * steps we do for queueing the only unnecessary
+ * one it's the kmalloc for a struct ipv6_frag.
+ * Feel free to try other alternatives...
+ */
+
+static void reasm_queue(struct frag_queue *fq, struct sk_buff *skb,
+ struct frag_hdr *fhdr, u8 *nhptr)
+{
+ struct ipv6_frag *nfp, *fp, **bptr;
+
+ nfp = (struct ipv6_frag *) frag_kmalloc(sizeof(struct ipv6_frag),
+ GFP_ATOMIC);
+
+ if (nfp == NULL) {
+ kfree_skb(skb);
+ return;
+ }
+
+ nfp->offset = ntohs(fhdr->frag_off) & ~0x7;
+ nfp->len = (ntohs(skb->nh.ipv6h->payload_len) -
+ ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
+
+ if ((u32)nfp->offset + (u32)nfp->len >= 65536) {
+ icmpv6_param_prob(skb,ICMPV6_HDR_FIELD, (u8*)&fhdr->frag_off);
+ goto err;
+ }
+ if (fhdr->frag_off & __constant_htons(0x0001)) {
+ /* Check if the fragment is rounded to 8 bytes.
+ * Required by the RFC.
+ * ... and would break our defragmentation algorithm 8)
+ */
+ if (nfp->len & 0x7) {
+ printk(KERN_DEBUG "fragment not rounded to 8bytes\n");
+
+ /*
+ It is not in specs, but I see no reasons
+ to send an error in this case. --ANK
+ */
+ if (nfp->offset == 0)
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
+ &skb->nh.ipv6h->payload_len);
+ goto err;
+ }
+ }
+
+ nfp->skb = skb;
+ nfp->fhdr = fhdr;
+ nfp->next = NULL;
+
+ bptr = &fq->fragments;
+
+ for (fp = fq->fragments; fp; fp=fp->next) {
+ if (nfp->offset <= fp->offset)
+ break;
+ bptr = &fp->next;
+ }
+ if (fp && fp->offset == nfp->offset) {
+ if (nfp->len != fp->len) {
+ printk(KERN_DEBUG "reasm_queue: dup with wrong len\n");
+ }
+
+ /* duplicate. discard it. */
+ goto err;
+ }
+
+ atomic_add(skb->truesize, &ip6_frag_mem);
+
+ /* All the checks are done, fragment is acepted.
+ Only now we are allowed to update reassembly data!
+ (fixed --ANK (980728))
+ */
+
+ /* iif always set to one of the last arrived segment */
+ fq->dev = skb->dev;
+ fq->iif = skb->dev->ifindex;
+
+ /* Last fragment */
+ if ((fhdr->frag_off & __constant_htons(0x0001)) == 0)
+ fq->last_in |= LAST_IN;
+
+ /* First fragment.
+ nexthdr and nhptr are get from the first fragment.
+ Moreover, nexthdr is UNDEFINED for all the fragments but the
+ first one.
+ (fixed --ANK (980728))
+ */
+ if (nfp->offset == 0) {
+ fq->nexthdr = fhdr->nexthdr;
+ fq->last_in |= FIRST_IN;
+ fq->nhoffset = nhptr - skb->nh.raw;
+ }
+
+ *bptr = nfp;
+ nfp->next = fp;
+ return;
+
+err:
+ frag_kfree_s(nfp, sizeof(*nfp));
+ kfree_skb(skb);
+}
+
+/*
+ * check if this fragment completes the packet
+ * returns true on success
+ */
+static u8* reasm_frag(struct frag_queue *fq, struct sk_buff **skb_in)
+{
+ struct ipv6_frag *fp;
+ struct ipv6_frag *head = fq->fragments;
+ struct ipv6_frag *tail = NULL;
+ struct sk_buff *skb;
+ __u32 offset = 0;
+ __u32 payload_len;
+ __u16 unfrag_len;
+ __u16 copy;
+ u8 *nhptr;
+
+ for(fp = head; fp; fp=fp->next) {
+ if (offset != fp->offset)
+ return NULL;
+
+ offset += fp->len;
+ tail = fp;
+ }
+
+ /*
+ * we know the m_flag arrived and we have a queue,
+ * starting from 0, without gaps.
+ * this means we have all fragments.
+ */
+
+ /* Unfragmented part is taken from the first segment.
+ (fixed --ANK (980728))
+ */
+ unfrag_len = (u8 *) (head->fhdr) - (u8 *) (head->skb->nh.ipv6h + 1);
+
+ payload_len = (unfrag_len + tail->offset +
+ (tail->skb->tail - (__u8 *) (tail->fhdr + 1)));
+
+ if (payload_len > 65535) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "reasm_frag: payload len = %d\n", payload_len);
+ ipv6_statistics.Ip6ReasmFails++;
+ fq_free(fq);
+ return NULL;
+ }
+
+ if ((skb = dev_alloc_skb(sizeof(struct ipv6hdr) + payload_len))==NULL) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "reasm_frag: no memory for reassembly\n");
+ ipv6_statistics.Ip6ReasmFails++;
+ fq_free(fq);
+ return NULL;
+ }
+
+ copy = unfrag_len + sizeof(struct ipv6hdr);
+
+ skb->nh.ipv6h = (struct ipv6hdr *) skb->data;
+ skb->dev = fq->dev;
+ skb->protocol = __constant_htons(ETH_P_IPV6);
+ skb->pkt_type = head->skb->pkt_type;
+ memcpy(skb->cb, head->skb->cb, sizeof(skb->cb));
+ skb->dst = dst_clone(head->skb->dst);
+
+ memcpy(skb_put(skb, copy), head->skb->nh.ipv6h, copy);
+ nhptr = skb->nh.raw + fq->nhoffset;
+ *nhptr = fq->nexthdr;
+
+ skb->h.raw = skb->tail;
+
+ skb->nh.ipv6h->payload_len = ntohs(payload_len);
+
+ *skb_in = skb;
+
+ /*
+ * FIXME: If we don't have a checksum we ought to be able
+ * to defragment and checksum in this pass. [AC]
+ * Note that we don't really know yet whether the protocol
+ * needs checksums at all. It might still be a good idea. -AK
+ */
+ for(fp = fq->fragments; fp; ) {
+ struct ipv6_frag *back;
+
+ memcpy(skb_put(skb, fp->len), (__u8*)(fp->fhdr + 1), fp->len);
+ frag_kfree_skb(fp->skb);
+ back = fp;
+ fp=fp->next;
+ frag_kfree_s(back, sizeof(*back));
+ }
+
+ del_timer(&fq->timer);
+ fq->prev->next = fq->next;
+ fq->next->prev = fq->prev;
+ fq->prev = fq->next = NULL;
+
+ frag_kfree_s(fq, sizeof(*fq));
+
+ ipv6_statistics.Ip6ReasmOKs++;
+ return nhptr;
+}
diff --git a/pfinet/linux-src/net/ipv6/route_ipv6.c b/pfinet/linux-src/net/ipv6/route_ipv6.c
new file mode 100644
index 00000000..5f79a226
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/route_ipv6.c
@@ -0,0 +1,1974 @@
+/*
+ * Linux INET6 implementation
+ * FIB front-end.
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: route_ipv6.c,v 1.2 2007/10/08 21:59:10 stesie Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/route.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+#include <linux/init.h>
+#include <linux/netlink.h>
+#include <linux/if_arp.h>
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+
+#include <net/snmp.h>
+#include <net/ipv6.h>
+#include <net/ip6_fib.h>
+#include <net/ip6_route.h>
+#include <net/ndisc.h>
+#include <net/addrconf.h>
+#include <net/tcp.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
+
+#undef CONFIG_RT6_POLICY
+
+/* Set to 3 to get tracing. */
+#define RT6_DEBUG 2
+
+#if RT6_DEBUG >= 3
+#define RDBG(x) printk x
+#define RT6_TRACE(x...) printk(KERN_DEBUG x)
+#else
+#define RDBG(x)
+#define RT6_TRACE(x...) do { ; } while (0)
+#endif
+
+#if RT6_DEBUG >= 1
+#define BUG_TRAP(x) ({ if (!(x)) { printk("Assertion (" #x ") failed at " __FILE__ "(%d):" __FUNCTION__ "\n", __LINE__); } })
+#else
+#define BUG_TRAP(x) do { ; } while (0)
+#endif
+
+
+int ip6_rt_max_size = 4096;
+int ip6_rt_gc_min_interval = 5*HZ;
+int ip6_rt_gc_timeout = 60*HZ;
+int ip6_rt_gc_interval = 30*HZ;
+int ip6_rt_gc_elasticity = 9;
+int ip6_rt_mtu_expires = 10*60*HZ;
+
+static struct rt6_info * ip6_rt_copy(struct rt6_info *ort);
+static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
+static struct dst_entry *ip6_dst_reroute(struct dst_entry *dst,
+ struct sk_buff *skb);
+static struct dst_entry *ip6_negative_advice(struct dst_entry *);
+static int ip6_dst_gc(void);
+
+static int ip6_pkt_discard(struct sk_buff *skb);
+static void ip6_link_failure(struct sk_buff *skb);
+
+struct dst_ops ip6_dst_ops = {
+ AF_INET6,
+ __constant_htons(ETH_P_IPV6),
+ 1024,
+
+ ip6_dst_gc,
+ ip6_dst_check,
+ ip6_dst_reroute,
+ NULL,
+ ip6_negative_advice,
+ ip6_link_failure,
+};
+
+struct rt6_info ip6_null_entry = {
+ {{NULL, ATOMIC_INIT(1), ATOMIC_INIT(1), &loopback_dev,
+ -1, 0, 0, 0, 0, 0, 0, 0, 0,
+ -ENETUNREACH, NULL, NULL,
+ ip6_pkt_discard, ip6_pkt_discard,
+#ifdef CONFIG_NET_CLS_ROUTE
+ 0,
+#endif
+ &ip6_dst_ops}},
+ NULL, {{{0}}}, RTF_REJECT|RTF_NONEXTHOP, ~0U,
+ 255, ATOMIC_INIT(1), {NULL}, {{{{0}}}, 0}, {{{{0}}}, 0}
+};
+
+struct fib6_node ip6_routing_table = {
+ NULL, NULL, NULL, NULL,
+ &ip6_null_entry,
+ 0, RTN_ROOT|RTN_TL_ROOT|RTN_RTINFO, 0
+};
+
+#ifdef CONFIG_RT6_POLICY
+int ip6_rt_policy = 0;
+
+struct pol_chain *rt6_pol_list = NULL;
+
+
+static int rt6_flow_match_in(struct rt6_info *rt, struct sk_buff *skb);
+static int rt6_flow_match_out(struct rt6_info *rt, struct sock *sk);
+
+static struct rt6_info *rt6_flow_lookup(struct rt6_info *rt,
+ struct in6_addr *daddr,
+ struct in6_addr *saddr,
+ struct fl_acc_args *args);
+
+#else
+#define ip6_rt_policy (0)
+#endif
+
+/*
+ * Route lookup
+ */
+
+static __inline__ struct rt6_info *rt6_device_match(struct rt6_info *rt,
+ int oif,
+ int strict)
+{
+ struct rt6_info *local = NULL;
+ struct rt6_info *sprt;
+
+ if (oif) {
+ for (sprt = rt; sprt; sprt = sprt->u.next) {
+ struct device *dev = sprt->rt6i_dev;
+ if (dev->ifindex == oif)
+ return sprt;
+ if (dev->flags&IFF_LOOPBACK)
+ local = sprt;
+ }
+
+ if (local)
+ return local;
+
+ if (strict)
+ return &ip6_null_entry;
+ }
+ return rt;
+}
+
+/*
+ * pointer to the last default router chosen
+ */
+static struct rt6_info *rt6_dflt_pointer = NULL;
+
+static struct rt6_info *rt6_best_dflt(struct rt6_info *rt, int oif)
+{
+ struct rt6_info *match = NULL;
+ struct rt6_info *sprt;
+ int mpri = 0;
+
+ for (sprt = rt; sprt; sprt = sprt->u.next) {
+ struct neighbour *neigh;
+
+ RDBG(("sprt(%p): ", sprt));
+ if ((neigh = sprt->rt6i_nexthop)) {
+ int m = -1;
+
+ RDBG(("nxthop(%p,%d) ", neigh, neigh->nud_state));
+ switch (neigh->nud_state) {
+ case NUD_REACHABLE:
+ RDBG(("NUD_REACHABLE "));
+ if (sprt != rt6_dflt_pointer) {
+ rt = sprt;
+ RDBG(("sprt!=dflt_ptr -> %p\n",
+ sprt));
+ goto out;
+ }
+ RDBG(("m=2, "));
+ m = 2;
+ break;
+
+ case NUD_DELAY:
+ RDBG(("NUD_DELAY, m=1, "));
+ m = 1;
+ break;
+
+ case NUD_STALE:
+ RDBG(("NUD_STALE, m=1, "));
+ m = 1;
+ break;
+ };
+
+ if (oif && sprt->rt6i_dev->ifindex == oif) {
+ m += 2;
+ }
+
+ if (m >= mpri) {
+ RDBG(("m>=mpri setmatch, "));
+ mpri = m;
+ match = sprt;
+ }
+ }
+ }
+
+ if (match) {
+ RDBG(("match, set rt, "));
+ rt = match;
+ } else {
+ /*
+ * No default routers are known to be reachable.
+ * SHOULD round robin
+ */
+ RDBG(("!match, trying rt6_dflt_pointer, "));
+ if (rt6_dflt_pointer) {
+ struct rt6_info *next;
+
+ if ((next = rt6_dflt_pointer->u.next) &&
+ next->u.dst.error == 0)
+ rt = next;
+ }
+ }
+
+out:
+ rt6_dflt_pointer = rt;
+ RDBG(("returning %p, dflt_ptr set\n", rt));
+ return rt;
+}
+
+struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr,
+ int oif, int strict)
+{
+ struct fib6_node *fn;
+ struct rt6_info *rt;
+
+ start_bh_atomic();
+ fn = fib6_lookup(&ip6_routing_table, daddr, saddr);
+ rt = rt6_device_match(fn->leaf, oif, strict);
+ atomic_inc(&rt->u.dst.use);
+ atomic_inc(&rt->u.dst.refcnt);
+ end_bh_atomic();
+
+ rt->u.dst.lastuse = jiffies;
+ if (rt->u.dst.error == 0)
+ return rt;
+ dst_release(&rt->u.dst);
+ return NULL;
+}
+
+static int rt6_ins(struct rt6_info *rt)
+{
+ int err;
+
+ start_bh_atomic();
+ err = fib6_add(&ip6_routing_table, rt);
+ end_bh_atomic();
+
+ return err;
+}
+
+static struct rt6_info *rt6_cow(struct rt6_info *ort, struct in6_addr *daddr,
+ struct in6_addr *saddr)
+{
+ int err;
+ struct rt6_info *rt;
+
+ /*
+ * Clone the route.
+ */
+
+ rt = ip6_rt_copy(ort);
+
+ if (rt) {
+ ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
+
+ if (!(rt->rt6i_flags&RTF_GATEWAY))
+ ipv6_addr_copy(&rt->rt6i_gateway, daddr);
+
+ rt->rt6i_dst.plen = 128;
+ rt->rt6i_flags |= RTF_CACHE;
+
+#ifdef CONFIG_IPV6_SUBTREES
+ if (rt->rt6i_src.plen && saddr) {
+ ipv6_addr_copy(&rt->rt6i_src.addr, saddr);
+ rt->rt6i_src.plen = 128;
+ }
+#endif
+
+ rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
+
+ dst_clone(&rt->u.dst);
+ err = rt6_ins(rt);
+ if (err == 0)
+ return rt;
+ rt->u.dst.error = err;
+ return rt;
+ }
+ dst_clone(&ip6_null_entry.u.dst);
+ return &ip6_null_entry;
+}
+
+#ifdef CONFIG_RT6_POLICY
+static __inline__ struct rt6_info *rt6_flow_lookup_in(struct rt6_info *rt,
+ struct sk_buff *skb)
+{
+ struct in6_addr *daddr, *saddr;
+ struct fl_acc_args arg;
+
+ arg.type = FL_ARG_FORWARD;
+ arg.fl_u.skb = skb;
+
+ saddr = &skb->nh.ipv6h->saddr;
+ daddr = &skb->nh.ipv6h->daddr;
+
+ return rt6_flow_lookup(rt, daddr, saddr, &arg);
+}
+
+static __inline__ struct rt6_info *rt6_flow_lookup_out(struct rt6_info *rt,
+ struct sock *sk,
+ struct flowi *fl)
+{
+ struct fl_acc_args arg;
+
+ arg.type = FL_ARG_ORIGIN;
+ arg.fl_u.fl_o.sk = sk;
+ arg.fl_u.fl_o.flow = fl;
+
+ return rt6_flow_lookup(rt, fl->nl_u.ip6_u.daddr, fl->nl_u.ip6_u.saddr,
+ &arg);
+}
+
+#endif
+
+#define BACKTRACK() \
+if (rt == &ip6_null_entry && strict) { \
+ while ((fn = fn->parent) != NULL) { \
+ if (fn->fn_flags & RTN_ROOT) { \
+ dst_clone(&rt->u.dst); \
+ goto out; \
+ } \
+ if (fn->fn_flags & RTN_RTINFO) \
+ goto restart; \
+ } \
+}
+
+
+void ip6_route_input(struct sk_buff *skb)
+{
+ struct fib6_node *fn;
+ struct rt6_info *rt;
+ int strict;
+
+ strict = ipv6_addr_type(&skb->nh.ipv6h->daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL);
+
+ fn = fib6_lookup(&ip6_routing_table, &skb->nh.ipv6h->daddr,
+ &skb->nh.ipv6h->saddr);
+
+restart:
+ rt = fn->leaf;
+
+ if ((rt->rt6i_flags & RTF_CACHE)) {
+ if (ip6_rt_policy == 0) {
+ rt = rt6_device_match(rt, skb->dev->ifindex, strict);
+ BACKTRACK();
+ dst_clone(&rt->u.dst);
+ goto out;
+ }
+
+#ifdef CONFIG_RT6_POLICY
+ if ((rt->rt6i_flags & RTF_FLOW)) {
+ struct rt6_info *sprt;
+
+ for (sprt = rt; sprt; sprt = sprt->u.next) {
+ if (rt6_flow_match_in(sprt, skb)) {
+ rt = sprt;
+ dst_clone(&rt->u.dst);
+ goto out;
+ }
+ }
+ }
+#endif
+ }
+
+ rt = rt6_device_match(rt, skb->dev->ifindex, 0);
+ BACKTRACK();
+
+ if (ip6_rt_policy == 0) {
+ if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) {
+ rt = rt6_cow(rt, &skb->nh.ipv6h->daddr,
+ &skb->nh.ipv6h->saddr);
+ goto out;
+ }
+ dst_clone(&rt->u.dst);
+ } else {
+#ifdef CONFIG_RT6_POLICY
+ rt = rt6_flow_lookup_in(rt, skb);
+#else
+ /* NEVER REACHED */
+#endif
+ }
+
+out:
+ rt->u.dst.lastuse = jiffies;
+ atomic_inc(&rt->u.dst.refcnt);
+ skb->dst = (struct dst_entry *) rt;
+}
+
+struct dst_entry * ip6_route_output(struct sock *sk, struct flowi *fl)
+{
+ struct fib6_node *fn;
+ struct rt6_info *rt;
+ int strict;
+
+ strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL);
+
+ start_bh_atomic();
+ fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr,
+ fl->nl_u.ip6_u.saddr);
+
+restart:
+ rt = fn->leaf;
+
+ if ((rt->rt6i_flags & RTF_CACHE)) {
+ if (ip6_rt_policy == 0) {
+ rt = rt6_device_match(rt, fl->oif, strict);
+ BACKTRACK();
+ dst_clone(&rt->u.dst);
+ goto out;
+ }
+
+#ifdef CONFIG_RT6_POLICY
+ if ((rt->rt6i_flags & RTF_FLOW)) {
+ struct rt6_info *sprt;
+
+ for (sprt = rt; sprt; sprt = sprt->u.next) {
+ if (rt6_flow_match_out(sprt, sk)) {
+ rt = sprt;
+ dst_clone(&rt->u.dst);
+ goto out;
+ }
+ }
+ }
+#endif
+ }
+ if (rt->rt6i_flags & RTF_DEFAULT) {
+ if (rt->rt6i_metric >= IP6_RT_PRIO_ADDRCONF)
+ rt = rt6_best_dflt(rt, fl->oif);
+ } else {
+ rt = rt6_device_match(rt, fl->oif, strict);
+ BACKTRACK();
+ }
+
+ if (ip6_rt_policy == 0) {
+ if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) {
+ rt = rt6_cow(rt, fl->nl_u.ip6_u.daddr,
+ fl->nl_u.ip6_u.saddr);
+ goto out;
+ }
+ dst_clone(&rt->u.dst);
+ } else {
+#ifdef CONFIG_RT6_POLICY
+ rt = rt6_flow_lookup_out(rt, sk, fl);
+#else
+ /* NEVER REACHED */
+#endif
+ }
+
+out:
+ rt->u.dst.lastuse = jiffies;
+ atomic_inc(&rt->u.dst.refcnt);
+ end_bh_atomic();
+ return &rt->u.dst;
+}
+
+
+/*
+ * Destination cache support functions
+ */
+
+static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
+{
+ struct rt6_info *rt;
+
+ rt = (struct rt6_info *) dst;
+
+ if (rt && rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
+ return dst;
+
+ dst_release(dst);
+ return NULL;
+}
+
+static struct dst_entry *ip6_dst_reroute(struct dst_entry *dst, struct sk_buff *skb)
+{
+ /*
+ * FIXME
+ */
+ RDBG(("ip6_dst_reroute(%p,%p)[%p] (AIEEE)\n", dst, skb,
+ __builtin_return_address(0)));
+ return NULL;
+}
+
+static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
+{
+ struct rt6_info *rt = (struct rt6_info *) dst;
+
+ if (rt) {
+ if (rt->rt6i_flags & RTF_CACHE)
+ ip6_del_rt(rt);
+ dst_release(dst);
+ }
+ return NULL;
+}
+
+static void ip6_link_failure(struct sk_buff *skb)
+{
+ struct rt6_info *rt;
+
+ icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev);
+
+ rt = (struct rt6_info *) skb->dst;
+ if (rt) {
+ if (rt->rt6i_flags&RTF_CACHE) {
+ dst_set_expires(&rt->u.dst, 0);
+ rt->rt6i_flags |= RTF_EXPIRES;
+ } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT))
+ rt->rt6i_node->fn_sernum = -1;
+ }
+}
+
+static int ip6_dst_gc()
+{
+ static unsigned expire = 30*HZ;
+ static unsigned long last_gc;
+ unsigned long now = jiffies;
+
+ start_bh_atomic();
+ if ((long)(now - last_gc) < ip6_rt_gc_min_interval)
+ goto out;
+
+ expire++;
+ fib6_run_gc(expire);
+ last_gc = now;
+ if (atomic_read(&ip6_dst_ops.entries) < ip6_dst_ops.gc_thresh)
+ expire = ip6_rt_gc_timeout>>1;
+
+out:
+ expire -= expire>>ip6_rt_gc_elasticity;
+ end_bh_atomic();
+ return (atomic_read(&ip6_dst_ops.entries) > ip6_rt_max_size);
+}
+
+/* Clean host part of a prefix. Not necessary in radix tree,
+ but results in cleaner routing tables.
+
+ Remove it only when all the things will work!
+ */
+
+static void ipv6_wash_prefix(struct in6_addr *pfx, int plen)
+{
+ int b = plen&0x7;
+ int o = (plen + 7)>>3;
+
+ if (o < 16)
+ memset(pfx->s6_addr + o, 0, 16 - o);
+ if (b != 0)
+ pfx->s6_addr[plen>>3] &= (0xFF<<(8-b));
+}
+
+static int ipv6_get_mtu(struct device *dev)
+{
+ struct inet6_dev *idev;
+
+ idev = ipv6_get_idev(dev);
+ if (idev)
+ return idev->cnf.mtu6;
+ else
+ return IPV6_MIN_MTU;
+}
+
+static int ipv6_get_hoplimit(struct device *dev)
+{
+ struct inet6_dev *idev;
+
+ idev = ipv6_get_idev(dev);
+ if (idev)
+ return idev->cnf.hop_limit;
+ else
+ return ipv6_devconf.hop_limit;
+}
+
+/*
+ *
+ */
+
+int ip6_route_add(struct in6_rtmsg *rtmsg)
+{
+ int err;
+ struct rt6_info *rt;
+ struct device *dev = NULL;
+ int addr_type;
+
+ if (rtmsg->rtmsg_dst_len > 128 || rtmsg->rtmsg_src_len > 128)
+ return -EINVAL;
+#ifndef CONFIG_IPV6_SUBTREES
+ if (rtmsg->rtmsg_src_len)
+ return -EINVAL;
+#endif
+ if (rtmsg->rtmsg_metric == 0)
+ rtmsg->rtmsg_metric = IP6_RT_PRIO_USER;
+
+ rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops);
+
+ if (rt == NULL)
+ return -ENOMEM;
+
+ rt->u.dst.obsolete = -1;
+ rt->rt6i_expires = rtmsg->rtmsg_info;
+
+ addr_type = ipv6_addr_type(&rtmsg->rtmsg_dst);
+
+ if (addr_type & IPV6_ADDR_MULTICAST)
+ rt->u.dst.input = ip6_mc_input;
+ else
+ rt->u.dst.input = ip6_forward;
+
+ rt->u.dst.output = ip6_output;
+
+ if (rtmsg->rtmsg_ifindex) {
+ dev = dev_get_by_index(rtmsg->rtmsg_ifindex);
+ err = -ENODEV;
+ if (dev == NULL)
+ goto out;
+ }
+
+ ipv6_addr_copy(&rt->rt6i_dst.addr, &rtmsg->rtmsg_dst);
+ rt->rt6i_dst.plen = rtmsg->rtmsg_dst_len;
+ ipv6_wash_prefix(&rt->rt6i_dst.addr, rt->rt6i_dst.plen);
+
+#ifdef CONFIG_IPV6_SUBTREES
+ ipv6_addr_copy(&rt->rt6i_src.addr, &rtmsg->rtmsg_src);
+ rt->rt6i_src.plen = rtmsg->rtmsg_src_len;
+ ipv6_wash_prefix(&rt->rt6i_src.addr, rt->rt6i_src.plen);
+#endif
+
+ rt->rt6i_metric = rtmsg->rtmsg_metric;
+
+ /* We cannot add true routes via loopback here,
+ they would result in kernel looping; promote them to reject routes
+ */
+ if ((rtmsg->rtmsg_flags&RTF_REJECT) ||
+ (dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK))) {
+ dev = &loopback_dev;
+ rt->u.dst.output = ip6_pkt_discard;
+ rt->u.dst.input = ip6_pkt_discard;
+ rt->u.dst.error = -ENETUNREACH;
+ rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
+ goto install_route;
+ }
+
+ if (rtmsg->rtmsg_flags & RTF_GATEWAY) {
+ struct in6_addr *gw_addr;
+ int gwa_type;
+
+ gw_addr = &rtmsg->rtmsg_gateway;
+ ipv6_addr_copy(&rt->rt6i_gateway, &rtmsg->rtmsg_gateway);
+ gwa_type = ipv6_addr_type(gw_addr);
+
+ if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
+ struct rt6_info *grt;
+
+ /* IPv6 strictly inhibits using not link-local
+ addresses as nexthop address.
+ Otherwise, router will not able to send redirects.
+ It is very good, but in some (rare!) curcumstances
+ (SIT, PtP, NBMA NOARP links) it is handy to allow
+ some exceptions. --ANK
+ */
+ err = -EINVAL;
+ if (!(gwa_type&IPV6_ADDR_UNICAST))
+ goto out;
+
+ grt = rt6_lookup(gw_addr, NULL, rtmsg->rtmsg_ifindex, 1);
+
+ err = -EHOSTUNREACH;
+ if (grt == NULL)
+ goto out;
+ if (!(grt->rt6i_flags&RTF_GATEWAY))
+ err = 0;
+ dev = grt->rt6i_dev;
+ dst_release(&grt->u.dst);
+
+ if (err)
+ goto out;
+ }
+ err = -EINVAL;
+ if (dev == NULL || (dev->flags&IFF_LOOPBACK))
+ goto out;
+ }
+
+ err = -ENODEV;
+ if (dev == NULL)
+ goto out;
+
+ if (rtmsg->rtmsg_flags & (RTF_GATEWAY|RTF_NONEXTHOP)) {
+ rt->rt6i_nexthop = ndisc_get_neigh(dev, &rt->rt6i_gateway);
+ err = -ENOMEM;
+ if (rt->rt6i_nexthop == NULL)
+ goto out;
+ }
+
+ if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr))
+ rt->rt6i_hoplimit = IPV6_DEFAULT_MCASTHOPS;
+ else
+ rt->rt6i_hoplimit = ipv6_get_hoplimit(dev);
+ rt->rt6i_flags = rtmsg->rtmsg_flags;
+
+install_route:
+ rt->u.dst.pmtu = ipv6_get_mtu(dev);
+ rt->u.dst.rtt = TCP_TIMEOUT_INIT;
+ rt->rt6i_dev = dev;
+ return rt6_ins(rt);
+
+out:
+ dst_free((struct dst_entry *) rt);
+ return err;
+}
+
+int ip6_del_rt(struct rt6_info *rt)
+{
+ int err;
+
+ start_bh_atomic();
+ rt6_dflt_pointer = NULL;
+ err = fib6_del(rt);
+ end_bh_atomic();
+
+ return err;
+}
+
+int ip6_route_del(struct in6_rtmsg *rtmsg)
+{
+ struct fib6_node *fn;
+ struct rt6_info *rt;
+ int err = -ESRCH;
+
+ start_bh_atomic();
+
+ fn = fib6_locate(&ip6_routing_table,
+ &rtmsg->rtmsg_dst, rtmsg->rtmsg_dst_len,
+ &rtmsg->rtmsg_src, rtmsg->rtmsg_src_len);
+
+ if (fn) {
+ for (rt = fn->leaf; rt; rt = rt->u.next) {
+ if (rtmsg->rtmsg_ifindex &&
+ (rt->rt6i_dev == NULL ||
+ rt->rt6i_dev->ifindex != rtmsg->rtmsg_ifindex))
+ continue;
+ if (rtmsg->rtmsg_flags&RTF_GATEWAY &&
+ ipv6_addr_cmp(&rtmsg->rtmsg_gateway, &rt->rt6i_gateway))
+ continue;
+ if (rtmsg->rtmsg_metric &&
+ rtmsg->rtmsg_metric != rt->rt6i_metric)
+ continue;
+ err = ip6_del_rt(rt);
+ break;
+ }
+ }
+ end_bh_atomic();
+
+ return err;
+}
+
+#ifdef CONFIG_IPV6_NETLINK
+/*
+ * NETLINK interface
+ * routing socket moral equivalent
+ */
+
+static int rt6_msgrcv(int unit, struct sk_buff *skb)
+{
+ int count = 0;
+ struct in6_rtmsg *rtmsg;
+ int err;
+
+ rtnl_lock();
+ while (skb->len) {
+ if (skb->len < sizeof(struct in6_rtmsg)) {
+ count = -EINVAL;
+ goto out;
+ }
+
+ rtmsg = (struct in6_rtmsg *) skb->data;
+ skb_pull(skb, sizeof(struct in6_rtmsg));
+ count += sizeof(struct in6_rtmsg);
+
+ switch (rtmsg->rtmsg_type) {
+ case RTMSG_NEWROUTE:
+ err = ip6_route_add(rtmsg);
+ break;
+ case RTMSG_DELROUTE:
+ err = ip6_route_del(rtmsg);
+ break;
+ default:
+ count = -EINVAL;
+ goto out;
+ };
+ }
+
+out:
+ rtnl_unlock();
+ kfree_skb(skb);
+ return count;
+}
+
+static void rt6_sndrtmsg(struct in6_rtmsg *rtmsg)
+{
+ struct sk_buff *skb;
+
+ skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
+ if (skb == NULL)
+ return;
+
+ memcpy(skb_put(skb, sizeof(struct in6_rtmsg)), &rtmsg,
+ sizeof(struct in6_rtmsg));
+
+ if (netlink_post(NETLINK_ROUTE6, skb))
+ kfree_skb(skb);
+}
+
+void rt6_sndmsg(int type, struct in6_addr *dst, struct in6_addr *src,
+ struct in6_addr *gw, struct device *dev,
+ int dstlen, int srclen, int metric, __u32 flags)
+{
+ struct sk_buff *skb;
+ struct in6_rtmsg *msg;
+
+ skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
+ if (skb == NULL)
+ return;
+
+ msg = (struct in6_rtmsg *) skb_put(skb, sizeof(struct in6_rtmsg));
+
+ memset(msg, 0, sizeof(struct in6_rtmsg));
+
+ msg->rtmsg_type = type;
+
+ if (dst)
+ ipv6_addr_copy(&msg->rtmsg_dst, dst);
+
+ if (src) {
+ ipv6_addr_copy(&msg->rtmsg_src, src);
+ msg->rtmsg_src_len = srclen;
+ }
+
+ if (gw)
+ ipv6_addr_copy(&msg->rtmsg_gateway, gw);
+
+ msg->rtmsg_dst_len = dstlen;
+ msg->rtmsg_metric = metric;
+
+ if (dev)
+ msg->rtmsg_ifindex = dev->ifindex;
+
+ msg->rtmsg_flags = flags;
+
+ if (netlink_post(NETLINK_ROUTE6, skb))
+ kfree_skb(skb);
+}
+#endif /* CONFIG_IPV6_NETLINK */
+
+/*
+ * Handle redirects
+ */
+void rt6_redirect(struct in6_addr *dest, struct in6_addr *saddr,
+ struct neighbour *neigh, int on_link)
+{
+ struct rt6_info *rt, *nrt;
+
+ /* Locate old route to this destination. */
+ rt = rt6_lookup(dest, NULL, neigh->dev->ifindex, 1);
+
+ if (rt == NULL)
+ return;
+
+ if (neigh->dev != rt->rt6i_dev)
+ goto out;
+
+ /* Redirect received -> path was valid.
+ Look, redirects are sent only in response to data packets,
+ so that this nexthop apparently is reachable. --ANK
+ */
+ dst_confirm(&rt->u.dst);
+
+ /* Duplicate redirect: silently ignore. */
+ if (neigh == rt->u.dst.neighbour)
+ goto out;
+
+ /* Current route is on-link; redirect is always invalid.
+
+ Seems, previous statement is not true. It could
+ be node, which looks for us as on-link (f.e. proxy ndisc)
+ But then router serving it might decide, that we should
+ know truth 8)8) --ANK (980726).
+ */
+ if (!(rt->rt6i_flags&RTF_GATEWAY))
+ goto out;
+
+#if !defined(CONFIG_IPV6_EUI64) || defined(CONFIG_IPV6_NO_PB)
+ /*
+ * During transition gateways have more than
+ * one link local address. Certainly, it is violation
+ * of basic principles, but it is temparary.
+ */
+ /*
+ * RFC 1970 specifies that redirects should only be
+ * accepted if they come from the nexthop to the target.
+ * Due to the way default routers are chosen, this notion
+ * is a bit fuzzy and one might need to check all default
+ * routers.
+ */
+
+ if (ipv6_addr_cmp(saddr, &rt->rt6i_gateway)) {
+ if (rt->rt6i_flags & RTF_DEFAULT) {
+ struct rt6_info *rt1;
+
+ for (rt1 = ip6_routing_table.leaf; rt1; rt1 = rt1->u.next) {
+ if (!ipv6_addr_cmp(saddr, &rt1->rt6i_gateway)) {
+ dst_clone(&rt1->u.dst);
+ dst_release(&rt->u.dst);
+ rt = rt1;
+ goto source_ok;
+ }
+ }
+ }
+ if (net_ratelimit())
+ printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
+ "for redirect target\n");
+ goto out;
+ }
+
+source_ok:
+#endif
+
+ /*
+ * We have finally decided to accept it.
+ */
+
+ nrt = ip6_rt_copy(rt);
+ if (nrt == NULL)
+ goto out;
+
+ nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
+ if (on_link)
+ nrt->rt6i_flags &= ~RTF_GATEWAY;
+
+ ipv6_addr_copy(&nrt->rt6i_dst.addr, dest);
+ nrt->rt6i_dst.plen = 128;
+
+ ipv6_addr_copy(&nrt->rt6i_gateway, (struct in6_addr*)neigh->primary_key);
+ nrt->rt6i_nexthop = neigh_clone(neigh);
+ /* Reset pmtu, it may be better */
+ nrt->u.dst.pmtu = ipv6_get_mtu(neigh->dev);
+ nrt->rt6i_hoplimit = ipv6_get_hoplimit(neigh->dev);
+
+ if (rt6_ins(nrt))
+ goto out;
+
+ /* Sic! rt6_redirect is called by bh, so that it is allowed */
+ dst_release(&rt->u.dst);
+ if (rt->rt6i_flags&RTF_CACHE)
+ ip6_del_rt(rt);
+ return;
+
+out:
+ dst_release(&rt->u.dst);
+ return;
+}
+
+/*
+ * Handle ICMP "packet too big" messages
+ * i.e. Path MTU discovery
+ */
+
+void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr,
+ struct device *dev, u32 pmtu)
+{
+ struct rt6_info *rt, *nrt;
+
+ if (pmtu < IPV6_MIN_MTU) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "rt6_pmtu_discovery: invalid MTU value %d\n",
+ pmtu);
+ return;
+ }
+
+ rt = rt6_lookup(daddr, saddr, dev->ifindex, 0);
+
+ if (rt == NULL)
+ return;
+
+ if (pmtu >= rt->u.dst.pmtu)
+ goto out;
+
+ /* New mtu received -> path was valid.
+ They are sent only in response to data packets,
+ so that this nexthop apparently is reachable. --ANK
+ */
+ dst_confirm(&rt->u.dst);
+
+ /* Host route. If it is static, it would be better
+ not to override it, but add new one, so that
+ when cache entry will expire old pmtu
+ would return automatically.
+ */
+ if (rt->rt6i_flags & RTF_CACHE) {
+ rt->u.dst.pmtu = pmtu;
+ dst_set_expires(&rt->u.dst, ip6_rt_mtu_expires);
+ rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES;
+ goto out;
+ }
+
+ /* Network route.
+ Two cases are possible:
+ 1. It is connected route. Action: COW
+ 2. It is gatewayed route or NONEXTHOP route. Action: clone it.
+ */
+ if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) {
+ nrt = rt6_cow(rt, daddr, saddr);
+ if (!nrt->u.dst.error) {
+ nrt->u.dst.pmtu = pmtu;
+ dst_set_expires(&rt->u.dst, ip6_rt_mtu_expires);
+ nrt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES;
+ dst_release(&nrt->u.dst);
+ }
+ } else {
+ nrt = ip6_rt_copy(rt);
+ if (nrt == NULL)
+ goto out;
+ ipv6_addr_copy(&nrt->rt6i_dst.addr, daddr);
+ nrt->rt6i_dst.plen = 128;
+ nrt->rt6i_nexthop = neigh_clone(rt->rt6i_nexthop);
+ dst_set_expires(&rt->u.dst, ip6_rt_mtu_expires);
+ nrt->rt6i_flags |= RTF_DYNAMIC|RTF_CACHE|RTF_EXPIRES;
+ nrt->u.dst.pmtu = pmtu;
+ rt6_ins(nrt);
+ }
+
+out:
+ dst_release(&rt->u.dst);
+}
+
+/*
+ * Misc support functions
+ */
+
+static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
+{
+ struct rt6_info *rt;
+
+ rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops);
+
+ if (rt) {
+ rt->u.dst.input = ort->u.dst.input;
+ rt->u.dst.output = ort->u.dst.output;
+
+ rt->u.dst.pmtu = ort->u.dst.pmtu;
+ rt->u.dst.rtt = ort->u.dst.rtt;
+ rt->u.dst.window = ort->u.dst.window;
+ rt->u.dst.mxlock = ort->u.dst.mxlock;
+ rt->u.dst.dev = ort->u.dst.dev;
+ rt->u.dst.lastuse = jiffies;
+ rt->rt6i_hoplimit = ort->rt6i_hoplimit;
+ rt->rt6i_expires = 0;
+
+ ipv6_addr_copy(&rt->rt6i_gateway, &ort->rt6i_gateway);
+ rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES;
+ rt->rt6i_metric = 0;
+
+ memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
+#ifdef CONFIG_IPV6_SUBTREES
+ memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
+#endif
+ }
+ return rt;
+}
+
+struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct device *dev)
+{
+ struct rt6_info *rt;
+ struct fib6_node *fn;
+
+ fn = &ip6_routing_table;
+
+ start_bh_atomic();
+ for (rt = fn->leaf; rt; rt=rt->u.next) {
+ if (dev == rt->rt6i_dev &&
+ ipv6_addr_cmp(&rt->rt6i_gateway, addr) == 0)
+ break;
+ }
+ if (rt)
+ dst_clone(&rt->u.dst);
+ end_bh_atomic();
+ return rt;
+}
+
+struct rt6_info *rt6_add_dflt_router(struct in6_addr *gwaddr,
+ struct device *dev)
+{
+ struct in6_rtmsg rtmsg;
+
+ memset(&rtmsg, 0, sizeof(struct in6_rtmsg));
+ rtmsg.rtmsg_type = RTMSG_NEWROUTE;
+ ipv6_addr_copy(&rtmsg.rtmsg_gateway, gwaddr);
+ rtmsg.rtmsg_metric = 1024;
+ rtmsg.rtmsg_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | RTF_UP;
+
+ rtmsg.rtmsg_ifindex = dev->ifindex;
+
+ ip6_route_add(&rtmsg);
+ return rt6_get_dflt_router(gwaddr, dev);
+}
+
+void rt6_purge_dflt_routers(int last_resort)
+{
+ struct rt6_info *rt;
+ u32 flags;
+
+ if (last_resort)
+ flags = RTF_ALLONLINK;
+ else
+ flags = RTF_DEFAULT | RTF_ADDRCONF;
+
+restart:
+ rt6_dflt_pointer = NULL;
+
+ for (rt = ip6_routing_table.leaf; rt; rt = rt->u.next) {
+ if (rt->rt6i_flags & flags) {
+ ip6_del_rt(rt);
+ goto restart;
+ }
+ }
+}
+
+#ifndef _HURD_
+int ipv6_route_ioctl(unsigned int cmd, void *arg)
+{
+ struct in6_rtmsg rtmsg;
+ int err;
+
+ RDBG(("ipv6_route_ioctl(%d,%p)\n", cmd, arg));
+ switch(cmd) {
+ case SIOCADDRT: /* Add a route */
+ case SIOCDELRT: /* Delete a route */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ err = copy_from_user(&rtmsg, arg,
+ sizeof(struct in6_rtmsg));
+ if (err)
+ return -EFAULT;
+
+ rtnl_lock();
+ switch (cmd) {
+ case SIOCADDRT:
+ err = ip6_route_add(&rtmsg);
+ break;
+ case SIOCDELRT:
+ err = ip6_route_del(&rtmsg);
+ break;
+ default:
+ err = -EINVAL;
+ };
+ rtnl_unlock();
+
+#ifdef CONFIG_IPV6_NETLINK
+ if (err == 0)
+ rt6_sndrtmsg(&rtmsg);
+#endif
+ return err;
+ };
+
+ return -EINVAL;
+}
+#endif /* not _HURD_ */
+
+/*
+ * Drop the packet on the floor
+ */
+
+int ip6_pkt_discard(struct sk_buff *skb)
+{
+ ipv6_statistics.Ip6OutNoRoutes++;
+ icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev);
+ kfree_skb(skb);
+ return 0;
+}
+
+/*
+ * Add address
+ */
+
+int ip6_rt_addr_add(struct in6_addr *addr, struct device *dev)
+{
+ struct rt6_info *rt;
+
+ rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops);
+ if (rt == NULL)
+ return -ENOMEM;
+
+ rt->u.dst.input = ip6_input;
+ rt->u.dst.output = ip6_output;
+ rt->rt6i_dev = dev_get("lo");
+ rt->u.dst.rtt = TCP_TIMEOUT_INIT;
+ rt->u.dst.pmtu = ipv6_get_mtu(rt->rt6i_dev);
+ rt->rt6i_hoplimit = ipv6_get_hoplimit(rt->rt6i_dev);
+ rt->u.dst.obsolete = -1;
+
+ rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
+ rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
+ if (rt->rt6i_nexthop == NULL) {
+ dst_free((struct dst_entry *) rt);
+ return -ENOMEM;
+ }
+
+ ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
+ rt->rt6i_dst.plen = 128;
+ rt6_ins(rt);
+
+ return 0;
+}
+
+/* Delete address. Warning: you should check that this address
+ disappeared before calling this function.
+ */
+
+int ip6_rt_addr_del(struct in6_addr *addr, struct device *dev)
+{
+ struct rt6_info *rt;
+ int err = -ENOENT;
+
+ rt = rt6_lookup(addr, NULL, loopback_dev.ifindex, 1);
+ if (rt) {
+ if (rt->rt6i_dst.plen == 128)
+ err= ip6_del_rt(rt);
+ dst_release(&rt->u.dst);
+ }
+
+ return err;
+}
+
+#ifdef CONFIG_RT6_POLICY
+
+static int rt6_flow_match_in(struct rt6_info *rt, struct sk_buff *skb)
+{
+ struct flow_filter *frule;
+ struct pkt_filter *filter;
+ int res = 1;
+
+ if ((frule = rt->rt6i_filter) == NULL)
+ goto out;
+
+ if (frule->type != FLR_INPUT) {
+ res = 0;
+ goto out;
+ }
+
+ for (filter = frule->u.filter; filter; filter = filter->next) {
+ __u32 *word;
+
+ word = (__u32 *) skb->h.raw;
+ word += filter->offset;
+
+ if ((*word ^ filter->value) & filter->mask) {
+ res = 0;
+ break;
+ }
+ }
+
+out:
+ return res;
+}
+
+static int rt6_flow_match_out(struct rt6_info *rt, struct sock *sk)
+{
+ struct flow_filter *frule;
+ int res = 1;
+
+ if ((frule = rt->rt6i_filter) == NULL)
+ goto out;
+
+ if (frule->type != FLR_INPUT) {
+ res = 0;
+ goto out;
+ }
+
+ if (frule->u.sk != sk)
+ res = 0;
+out:
+ return res;
+}
+
+static struct rt6_info *rt6_flow_lookup(struct rt6_info *rt,
+ struct in6_addr *daddr,
+ struct in6_addr *saddr,
+ struct fl_acc_args *args)
+{
+ struct flow_rule *frule;
+ struct rt6_info *nrt = NULL;
+ struct pol_chain *pol;
+
+ for (pol = rt6_pol_list; pol; pol = pol->next) {
+ struct fib6_node *fn;
+ struct rt6_info *sprt;
+
+ fn = fib6_lookup(pol->rules, daddr, saddr);
+
+ do {
+ for (sprt = fn->leaf; sprt; sprt=sprt->u.next) {
+ int res;
+
+ frule = sprt->rt6i_flowr;
+#if RT6_DEBUG >= 2
+ if (frule == NULL) {
+ printk(KERN_DEBUG "NULL flowr\n");
+ goto error;
+ }
+#endif
+ res = frule->ops->accept(rt, sprt, args, &nrt);
+
+ switch (res) {
+ case FLOWR_SELECT:
+ goto found;
+ case FLOWR_CLEAR:
+ goto next_policy;
+ case FLOWR_NODECISION:
+ break;
+ default:
+ goto error;
+ };
+ }
+
+ fn = fn->parent;
+
+ } while ((fn->fn_flags & RTN_TL_ROOT) == 0);
+
+ next_policy:
+ }
+
+error:
+ dst_clone(&ip6_null_entry.u.dst);
+ return &ip6_null_entry;
+
+found:
+ if (nrt == NULL)
+ goto error;
+
+ nrt->rt6i_flags |= RTF_CACHE;
+ dst_clone(&nrt->u.dst);
+ err = rt6_ins(nrt);
+ if (err)
+ nrt->u.dst.error = err;
+ return nrt;
+}
+#endif
+
+static int fib6_ifdown(struct rt6_info *rt, void *arg)
+{
+ if (((void*)rt->rt6i_dev == arg || arg == NULL) &&
+ rt != &ip6_null_entry) {
+ RT6_TRACE("deleted by ifdown %p\n", rt);
+ return -1;
+ }
+ return 0;
+}
+
+void rt6_ifdown(struct device *dev)
+{
+ fib6_clean_tree(&ip6_routing_table, fib6_ifdown, 0, dev);
+}
+
+struct rt6_mtu_change_arg
+{
+ struct device *dev;
+ unsigned mtu;
+};
+
+static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg)
+{
+ struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
+
+ /* In IPv6 pmtu discovery is not optional,
+ so that RTAX_MTU lock cannot dissable it.
+ We still use this lock to block changes
+ caused by addrconf/ndisc.
+ */
+ if (rt->rt6i_dev == arg->dev &&
+ !(rt->u.dst.mxlock&(1<<RTAX_MTU)))
+ rt->u.dst.pmtu = arg->mtu;
+ return 0;
+}
+
+void rt6_mtu_change(struct device *dev, unsigned mtu)
+{
+ struct rt6_mtu_change_arg arg;
+
+ arg.dev = dev;
+ arg.mtu = mtu;
+ fib6_clean_tree(&ip6_routing_table, rt6_mtu_change_route, 0, &arg);
+}
+
+#ifdef CONFIG_RTNETLINK
+
+static int inet6_rtm_to_rtmsg(struct rtmsg *r, struct rtattr **rta,
+ struct in6_rtmsg *rtmsg)
+{
+ memset(rtmsg, 0, sizeof(*rtmsg));
+
+ rtmsg->rtmsg_dst_len = r->rtm_dst_len;
+ rtmsg->rtmsg_src_len = r->rtm_src_len;
+ rtmsg->rtmsg_flags = RTF_UP;
+ if (r->rtm_type == RTN_UNREACHABLE)
+ rtmsg->rtmsg_flags |= RTF_REJECT;
+
+ if (rta[RTA_GATEWAY-1]) {
+ if (rta[RTA_GATEWAY-1]->rta_len != RTA_LENGTH(16))
+ return -EINVAL;
+ memcpy(&rtmsg->rtmsg_gateway, RTA_DATA(rta[RTA_GATEWAY-1]), 16);
+ rtmsg->rtmsg_flags |= RTF_GATEWAY;
+ }
+ if (rta[RTA_DST-1]) {
+ if (RTA_PAYLOAD(rta[RTA_DST-1]) < ((r->rtm_dst_len+7)>>3))
+ return -EINVAL;
+ memcpy(&rtmsg->rtmsg_dst, RTA_DATA(rta[RTA_DST-1]), ((r->rtm_dst_len+7)>>3));
+ }
+ if (rta[RTA_SRC-1]) {
+ if (RTA_PAYLOAD(rta[RTA_SRC-1]) < ((r->rtm_src_len+7)>>3))
+ return -EINVAL;
+ memcpy(&rtmsg->rtmsg_src, RTA_DATA(rta[RTA_SRC-1]), ((r->rtm_src_len+7)>>3));
+ }
+ if (rta[RTA_OIF-1]) {
+ if (rta[RTA_OIF-1]->rta_len != RTA_LENGTH(sizeof(int)))
+ return -EINVAL;
+ memcpy(&rtmsg->rtmsg_ifindex, RTA_DATA(rta[RTA_OIF-1]), sizeof(int));
+ }
+ if (rta[RTA_PRIORITY-1]) {
+ if (rta[RTA_PRIORITY-1]->rta_len != RTA_LENGTH(4))
+ return -EINVAL;
+ memcpy(&rtmsg->rtmsg_metric, RTA_DATA(rta[RTA_PRIORITY-1]), 4);
+ }
+ return 0;
+}
+
+int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+{
+ struct rtmsg *r = NLMSG_DATA(nlh);
+ struct in6_rtmsg rtmsg;
+
+ if (inet6_rtm_to_rtmsg(r, arg, &rtmsg))
+ return -EINVAL;
+ return ip6_route_del(&rtmsg);
+}
+
+int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+{
+ struct rtmsg *r = NLMSG_DATA(nlh);
+ struct in6_rtmsg rtmsg;
+
+ if (inet6_rtm_to_rtmsg(r, arg, &rtmsg))
+ return -EINVAL;
+ return ip6_route_add(&rtmsg);
+}
+
+struct rt6_rtnl_dump_arg
+{
+ struct sk_buff *skb;
+ struct netlink_callback *cb;
+};
+
+static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
+ struct in6_addr *dst,
+ struct in6_addr *src,
+ int iif,
+ int type, u32 pid, u32 seq)
+{
+ struct rtmsg *rtm;
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+ struct rtattr *mx;
+ struct rta_cacheinfo ci;
+
+ nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*rtm));
+ rtm = NLMSG_DATA(nlh);
+ rtm->rtm_family = AF_INET6;
+ rtm->rtm_dst_len = rt->rt6i_dst.plen;
+ rtm->rtm_src_len = rt->rt6i_src.plen;
+ rtm->rtm_tos = 0;
+ rtm->rtm_table = RT_TABLE_MAIN;
+ if (rt->rt6i_flags&RTF_REJECT)
+ rtm->rtm_type = RTN_UNREACHABLE;
+ else if (rt->rt6i_dev && (rt->rt6i_dev->flags&IFF_LOOPBACK))
+ rtm->rtm_type = RTN_LOCAL;
+ else
+ rtm->rtm_type = RTN_UNICAST;
+ rtm->rtm_flags = 0;
+ rtm->rtm_scope = RT_SCOPE_UNIVERSE;
+ rtm->rtm_protocol = RTPROT_BOOT;
+ if (rt->rt6i_flags&RTF_DYNAMIC)
+ rtm->rtm_protocol = RTPROT_REDIRECT;
+ else if (rt->rt6i_flags&(RTF_ADDRCONF|RTF_ALLONLINK))
+ rtm->rtm_protocol = RTPROT_KERNEL;
+ else if (rt->rt6i_flags&RTF_DEFAULT)
+ rtm->rtm_protocol = RTPROT_RA;
+
+ if (rt->rt6i_flags&RTF_CACHE)
+ rtm->rtm_flags |= RTM_F_CLONED;
+
+ if (dst) {
+ RTA_PUT(skb, RTA_DST, 16, dst);
+ rtm->rtm_dst_len = 128;
+ } else if (rtm->rtm_dst_len)
+ RTA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr);
+#ifdef CONFIG_IPV6_SUBTREES
+ if (src) {
+ RTA_PUT(skb, RTA_SRC, 16, src);
+ rtm->rtm_src_len = 128;
+ } else if (rtm->rtm_src_len)
+ RTA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);
+#endif
+ if (iif)
+ RTA_PUT(skb, RTA_IIF, 4, &iif);
+ else if (dst) {
+ struct in6_addr saddr_buf;
+ if (ipv6_get_saddr(&rt->u.dst, dst, &saddr_buf))
+ RTA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
+ }
+ mx = (struct rtattr*)skb->tail;
+ RTA_PUT(skb, RTA_METRICS, 0, NULL);
+ if (rt->u.dst.mxlock)
+ RTA_PUT(skb, RTAX_LOCK, sizeof(unsigned), &rt->u.dst.mxlock);
+ if (rt->u.dst.pmtu)
+ RTA_PUT(skb, RTAX_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
+ if (rt->u.dst.window)
+ RTA_PUT(skb, RTAX_WINDOW, sizeof(unsigned), &rt->u.dst.window);
+ if (rt->u.dst.rtt)
+ RTA_PUT(skb, RTAX_RTT, sizeof(unsigned), &rt->u.dst.rtt);
+ mx->rta_len = skb->tail - (u8*)mx;
+ if (mx->rta_len == RTA_LENGTH(0))
+ skb_trim(skb, (u8*)mx - skb->data);
+ if (rt->u.dst.neighbour)
+ RTA_PUT(skb, RTA_GATEWAY, 16, &rt->u.dst.neighbour->primary_key);
+ if (rt->u.dst.dev)
+ RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->rt6i_dev->ifindex);
+ RTA_PUT(skb, RTA_PRIORITY, 4, &rt->rt6i_metric);
+ ci.rta_lastuse = jiffies - rt->u.dst.lastuse;
+ if (rt->rt6i_expires)
+ ci.rta_expires = rt->rt6i_expires - jiffies;
+ else
+ ci.rta_expires = 0;
+ ci.rta_used = atomic_read(&rt->u.dst.refcnt);
+ ci.rta_clntref = atomic_read(&rt->u.dst.use);
+ ci.rta_error = rt->u.dst.error;
+ RTA_PUT(skb, RTA_CACHEINFO, sizeof(ci), &ci);
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+static int rt6_dump_route(struct rt6_info *rt, void *p_arg)
+{
+ struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
+
+ return rt6_fill_node(arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
+ NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq);
+}
+
+static int fib6_dump_node(struct fib6_walker_t *w)
+{
+ int res;
+ struct rt6_info *rt;
+
+ for (rt = w->leaf; rt; rt = rt->u.next) {
+ res = rt6_dump_route(rt, w->args);
+ if (res < 0) {
+ /* Frame is full, suspend walking */
+ w->leaf = rt;
+ return 1;
+ }
+ BUG_TRAP(res!=0);
+ }
+ w->leaf = NULL;
+ return 0;
+}
+
+static int fib6_dump_done(struct netlink_callback *cb)
+{
+ struct fib6_walker_t *w = (void*)cb->args[0];
+
+ if (w) {
+ cb->args[0] = 0;
+ start_bh_atomic();
+ fib6_walker_unlink(w);
+ end_bh_atomic();
+ kfree(w);
+ }
+ if (cb->args[1]) {
+ cb->done = (void*)cb->args[1];
+ cb->args[1] = 0;
+ }
+ return cb->done(cb);
+}
+
+int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct rt6_rtnl_dump_arg arg;
+ struct fib6_walker_t *w;
+ int res;
+
+ arg.skb = skb;
+ arg.cb = cb;
+
+ w = (void*)cb->args[0];
+ if (w == NULL) {
+ /* New dump:
+ *
+ * 1. hook callback destructor.
+ */
+ cb->args[1] = (long)cb->done;
+ cb->done = fib6_dump_done;
+
+ /*
+ * 2. allocate and initialize walker.
+ */
+ w = kmalloc(sizeof(*w), GFP_KERNEL);
+ if (w == NULL)
+ return -ENOMEM;
+ RT6_TRACE("dump<%p", w);
+ memset(w, 0, sizeof(*w));
+ w->root = &ip6_routing_table;
+ w->func = fib6_dump_node;
+ w->args = &arg;
+ cb->args[0] = (long)w;
+ start_bh_atomic();
+ res = fib6_walk(w);
+ end_bh_atomic();
+ } else {
+ w->args = &arg;
+ start_bh_atomic();
+ res = fib6_walk_continue(w);
+ end_bh_atomic();
+ }
+#if RT6_DEBUG >= 3
+ if (res <= 0 && skb->len == 0)
+ RT6_TRACE("%p>dump end\n", w);
+#endif
+ /* res < 0 is an error. (really, impossible)
+ res == 0 means that dump is complete, but skb still can contain data.
+ res > 0 dump is not complete, but frame is full.
+ */
+ return res < 0 ? res : skb->len;
+}
+
+int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
+{
+ struct rtattr **rta = arg;
+ int iif = 0;
+ int err;
+ struct sk_buff *skb;
+ struct flowi fl;
+ struct rt6_info *rt;
+
+ skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (skb == NULL)
+ return -ENOBUFS;
+
+ /* Reserve room for dummy headers, this skb can pass
+ through good chunk of routing engine.
+ */
+ skb->mac.raw = skb->data;
+ skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr));
+
+ fl.proto = 0;
+ fl.nl_u.ip6_u.daddr = NULL;
+ fl.nl_u.ip6_u.saddr = NULL;
+ fl.uli_u.icmpt.type = 0;
+ fl.uli_u.icmpt.code = 0;
+ if (rta[RTA_SRC-1])
+ fl.nl_u.ip6_u.saddr = (struct in6_addr*)RTA_DATA(rta[RTA_SRC-1]);
+ if (rta[RTA_DST-1])
+ fl.nl_u.ip6_u.daddr = (struct in6_addr*)RTA_DATA(rta[RTA_DST-1]);
+
+ if (rta[RTA_IIF-1])
+ memcpy(&iif, RTA_DATA(rta[RTA_IIF-1]), sizeof(int));
+
+ if (iif) {
+ struct device *dev;
+ dev = dev_get_by_index(iif);
+ if (!dev)
+ return -ENODEV;
+ }
+
+ fl.oif = 0;
+ if (rta[RTA_OIF-1])
+ memcpy(&fl.oif, RTA_DATA(rta[RTA_OIF-1]), sizeof(int));
+
+ rt = (struct rt6_info*)ip6_route_output(NULL, &fl);
+
+ skb->dst = &rt->u.dst;
+
+ NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid;
+ err = rt6_fill_node(skb, rt,
+ fl.nl_u.ip6_u.daddr,
+ fl.nl_u.ip6_u.saddr,
+ iif,
+ RTM_NEWROUTE, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq);
+ if (err < 0)
+ return -EMSGSIZE;
+
+ err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+void inet6_rt_notify(int event, struct rt6_info *rt)
+{
+ struct sk_buff *skb;
+ int size = NLMSG_SPACE(sizeof(struct rtmsg)+256);
+
+ skb = alloc_skb(size, gfp_any());
+ if (!skb) {
+ netlink_set_err(rtnl, 0, RTMGRP_IPV6_ROUTE, ENOBUFS);
+ return;
+ }
+ if (rt6_fill_node(skb, rt, NULL, NULL, 0, event, 0, 0) < 0) {
+ kfree_skb(skb);
+ netlink_set_err(rtnl, 0, RTMGRP_IPV6_ROUTE, EINVAL);
+ return;
+ }
+ NETLINK_CB(skb).dst_groups = RTMGRP_IPV6_ROUTE;
+ netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_ROUTE, gfp_any());
+}
+
+#endif
+
+/*
+ * /proc
+ */
+
+#ifdef CONFIG_PROC_FS
+
+#define RT6_INFO_LEN (32 + 4 + 32 + 4 + 32 + 40 + 5 + 1)
+
+struct rt6_proc_arg
+{
+ char *buffer;
+ int offset;
+ int length;
+ int skip;
+ int len;
+};
+
+static int rt6_info_route(struct rt6_info *rt, void *p_arg)
+{
+ struct rt6_proc_arg *arg = (struct rt6_proc_arg *) p_arg;
+ int i;
+
+ if (arg->skip < arg->offset / RT6_INFO_LEN) {
+ arg->skip++;
+ return 0;
+ }
+
+ if (arg->len >= arg->length)
+ return 0;
+
+ for (i=0; i<16; i++) {
+ sprintf(arg->buffer + arg->len, "%02x",
+ rt->rt6i_dst.addr.s6_addr[i]);
+ arg->len += 2;
+ }
+ arg->len += sprintf(arg->buffer + arg->len, " %02x ",
+ rt->rt6i_dst.plen);
+
+#ifdef CONFIG_IPV6_SUBTREES
+ for (i=0; i<16; i++) {
+ sprintf(arg->buffer + arg->len, "%02x",
+ rt->rt6i_src.addr.s6_addr[i]);
+ arg->len += 2;
+ }
+ arg->len += sprintf(arg->buffer + arg->len, " %02x ",
+ rt->rt6i_src.plen);
+#else
+ sprintf(arg->buffer + arg->len,
+ "00000000000000000000000000000000 00 ");
+ arg->len += 36;
+#endif
+
+ if (rt->rt6i_nexthop) {
+ for (i=0; i<16; i++) {
+ sprintf(arg->buffer + arg->len, "%02x",
+ rt->rt6i_nexthop->primary_key[i]);
+ arg->len += 2;
+ }
+ } else {
+ sprintf(arg->buffer + arg->len,
+ "00000000000000000000000000000000");
+ arg->len += 32;
+ }
+ arg->len += sprintf(arg->buffer + arg->len,
+ " %08x %08x %08x %08x %8s\n",
+ rt->rt6i_metric, atomic_read(&rt->u.dst.use),
+ atomic_read(&rt->u.dst.refcnt), rt->rt6i_flags,
+ rt->rt6i_dev ? rt->rt6i_dev->name : "");
+ return 0;
+}
+
+static int rt6_proc_info(char *buffer, char **start, off_t offset, int length,
+ int dummy)
+{
+ struct rt6_proc_arg arg;
+ arg.buffer = buffer;
+ arg.offset = offset;
+ arg.length = length;
+ arg.skip = 0;
+ arg.len = 0;
+
+ fib6_clean_tree(&ip6_routing_table, rt6_info_route, 0, &arg);
+
+ *start = buffer;
+ if (offset)
+ *start += offset % RT6_INFO_LEN;
+
+ arg.len -= offset % RT6_INFO_LEN;
+
+ if (arg.len > length)
+ arg.len = length;
+ if (arg.len < 0)
+ arg.len = 0;
+
+ return arg.len;
+}
+
+extern struct rt6_statistics rt6_stats;
+
+static int rt6_proc_stats(char *buffer, char **start, off_t offset, int length,
+ int dummy)
+{
+ int len;
+
+ len = sprintf(buffer, "%04x %04x %04x %04x %04x %04x\n",
+ rt6_stats.fib_nodes, rt6_stats.fib_route_nodes,
+ rt6_stats.fib_rt_alloc, rt6_stats.fib_rt_entries,
+ rt6_stats.fib_rt_cache,
+ atomic_read(&ip6_dst_ops.entries));
+
+ len -= offset;
+
+ if (len > length)
+ len = length;
+ if(len < 0)
+ len = 0;
+
+ *start = buffer + offset;
+
+ return len;
+}
+
+static struct proc_dir_entry proc_rt6_info = {
+ PROC_NET_RT6, 10, "ipv6_route",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rt6_proc_info
+};
+static struct proc_dir_entry proc_rt6_stats = {
+ PROC_NET_RT6_STATS, 9, "rt6_stats",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rt6_proc_stats
+};
+#endif /* CONFIG_PROC_FS */
+
+#ifdef CONFIG_SYSCTL
+
+static int flush_delay;
+
+static
+int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write, struct file * filp,
+ void *buffer, size_t *lenp)
+{
+ if (write) {
+ proc_dointvec(ctl, write, filp, buffer, lenp);
+ if (flush_delay < 0)
+ flush_delay = 0;
+ start_bh_atomic();
+ fib6_run_gc((unsigned long)flush_delay);
+ end_bh_atomic();
+ return 0;
+ } else
+ return -EINVAL;
+}
+
+ctl_table ipv6_route_table[] = {
+ {NET_IPV6_ROUTE_FLUSH, "flush",
+ &flush_delay, sizeof(int), 0644, NULL,
+ &ipv6_sysctl_rtcache_flush},
+ {NET_IPV6_ROUTE_GC_THRESH, "gc_thresh",
+ &ip6_dst_ops.gc_thresh, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV6_ROUTE_MAX_SIZE, "max_size",
+ &ip6_rt_max_size, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV6_ROUTE_GC_MIN_INTERVAL, "gc_min_interval",
+ &ip6_rt_gc_min_interval, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies},
+ {NET_IPV6_ROUTE_GC_TIMEOUT, "gc_timeout",
+ &ip6_rt_gc_timeout, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies},
+ {NET_IPV6_ROUTE_GC_INTERVAL, "gc_interval",
+ &ip6_rt_gc_interval, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies},
+ {NET_IPV6_ROUTE_GC_ELASTICITY, "gc_elasticity",
+ &ip6_rt_gc_elasticity, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies},
+ {NET_IPV6_ROUTE_MTU_EXPIRES, "mtu_expires",
+ &ip6_rt_mtu_expires, sizeof(int), 0644, NULL,
+ &proc_dointvec_jiffies},
+ {0}
+};
+
+#endif
+
+
+__initfunc(void ip6_route_init(void))
+{
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_rt6_info);
+ proc_net_register(&proc_rt6_stats);
+#endif
+#ifdef CONFIG_IPV6_NETLINK
+ netlink_attach(NETLINK_ROUTE6, rt6_msgrcv);
+#endif
+}
+
+#ifdef MODULE
+void ip6_route_cleanup(void)
+{
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(PROC_NET_RT6);
+ proc_net_unregister(PROC_NET_RT6_STATS);
+#endif
+#ifdef CONFIG_IPV6_NETLINK
+ netlink_detach(NETLINK_ROUTE6);
+#endif
+ rt6_ifdown(NULL);
+ fib6_gc_cleanup();
+}
+#endif /* MODULE */
diff --git a/pfinet/linux-src/net/ipv6/tcp_ipv6.c b/pfinet/linux-src/net/ipv6/tcp_ipv6.c
new file mode 100644
index 00000000..3fba9af6
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/tcp_ipv6.c
@@ -0,0 +1,1803 @@
+/*
+ * TCP over IPv6
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ * YOSHIFUJI Hideaki @USAGI and: Support IPV6_V6ONLY socket option, which
+ * Alexey Kuznetsov allow both IPv4 and IPv6 sockets to bind
+ * a single port at the same time.
+ *
+ * $Id: tcp_ipv6.c,v 1.3 2007/10/13 01:43:00 stesie Exp $
+ *
+ * Based on:
+ * linux/net/ipv4/tcp.c
+ * linux/net/ipv4/tcp_input.c
+ * linux/net/ipv4/tcp_output.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/netdevice.h>
+#include <linux/init.h>
+
+#include <linux/ipv6.h>
+#include <linux/icmpv6.h>
+#include <linux/random.h>
+
+#include <net/tcp.h>
+#include <net/ndisc.h>
+#include <net/ipv6.h>
+#include <net/transp_v6.h>
+#include <net/addrconf.h>
+#include <net/ip6_route.h>
+
+#include <asm/uaccess.h>
+
+extern int sysctl_max_syn_backlog;
+
+static void tcp_v6_send_reset(struct sk_buff *skb);
+static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len,
+ struct sk_buff *skb);
+
+static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);
+static void tcp_v6_xmit(struct sk_buff *skb);
+static struct open_request *tcp_v6_search_req(struct tcp_opt *tp,
+ struct ipv6hdr *ip6h,
+ struct tcphdr *th,
+ int iif,
+ struct open_request **prevp);
+
+static struct tcp_func ipv6_mapped;
+static struct tcp_func ipv6_specific;
+
+/* I have no idea if this is a good hash for v6 or not. -DaveM */
+static __inline__ int tcp_v6_hashfn(struct in6_addr *laddr, u16 lport,
+ struct in6_addr *faddr, u16 fport)
+{
+ int hashent = (lport ^ fport);
+
+ hashent ^= (laddr->s6_addr32[3] ^ faddr->s6_addr32[3]);
+ return (hashent & ((tcp_ehash_size/2) - 1));
+}
+
+static __inline__ int tcp_v6_sk_hashfn(struct sock *sk)
+{
+ struct in6_addr *laddr = &sk->net_pinfo.af_inet6.rcv_saddr;
+ struct in6_addr *faddr = &sk->net_pinfo.af_inet6.daddr;
+ __u16 lport = sk->num;
+ __u16 fport = sk->dport;
+ return tcp_v6_hashfn(laddr, lport, faddr, fport);
+}
+
+static inline int ipv6_rcv_saddr_equal(struct sock *sk, struct sock *sk2)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ int addr_type = ipv6_addr_type(&np->rcv_saddr);
+
+ if (!sk2->rcv_saddr && !ipv6_only_sock(sk))
+ return 1;
+
+ if (sk2->family == AF_INET6 &&
+ ipv6_addr_any(&sk2->rcv_saddr) &&
+ !(ipv6_only_sock(sk2) && addr_type == IPV6_ADDR_MAPPED))
+ return 1;
+
+ if (addr_type == IPV6_ADDR_ANY &&
+ (!ipv6_only_sock(sk) ||
+ !(sk2->family == AF_INET6 ?
+ ipv6_addr_type(&sk2->rcv_saddr) == IPV6_ADDR_MAPPED : 1)))
+ return 1;
+
+ if (sk2->family == AF_INET6 &&
+ !ipv6_addr_cmp(&np->rcv_saddr,
+ (sk2->state != TCP_TIME_WAIT ?
+ &sk2->rcv_saddr :
+ &((struct tcp_tw_bucket *)sk)->v6_rcv_saddr)))
+ return 1;
+
+ if (addr_type == IPV6_ADDR_MAPPED &&
+ !ipv6_only_sock(sk2) &&
+ (!sk2->rcv_saddr ||
+ !sk->rcv_saddr ||
+ sk->rcv_saddr == sk2->rcv_saddr))
+ return 1;
+
+ return 0;
+}
+
+
+/* Grrr, addr_type already calculated by caller, but I don't want
+ * to add some silly "cookie" argument to this method just for that.
+ * But it doesn't matter, the recalculation is in the rarest path
+ * this function ever takes.
+ */
+static int tcp_v6_get_port(struct sock *sk, unsigned short snum)
+{
+ struct tcp_bind_bucket *tb;
+
+ SOCKHASH_LOCK();
+ if (snum == 0) {
+ int rover = tcp_port_rover;
+ int low = sysctl_local_port_range[0];
+ int high = sysctl_local_port_range[1];
+ int remaining = (high - low) + 1;
+
+ do { rover++;
+ if ((rover < low) || (rover > high))
+ rover = low;
+ tb = tcp_bhash[tcp_bhashfn(rover)];
+ for ( ; tb; tb = tb->next)
+ if (tb->port == rover)
+ goto next;
+ break;
+
+ next:
+ (void) 0;
+
+ } while (--remaining > 0);
+ tcp_port_rover = rover;
+
+ /* Exhausted local port range during search? */
+ if (remaining <= 0)
+ goto fail;
+
+ /* OK, here is the one we will use. */
+ snum = rover;
+ tb = NULL;
+ } else {
+ for (tb = tcp_bhash[tcp_bhashfn(snum)];
+ tb != NULL;
+ tb = tb->next)
+ if (tb->port == snum)
+ break;
+ }
+ if (tb != NULL && tb->owners != NULL) {
+ if (tb->fastreuse != 0 && sk->reuse != 0) {
+ goto success;
+ } else {
+ struct sock *sk2 = tb->owners;
+ int sk_reuse = sk->reuse;
+ int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
+
+ for( ; sk2 != NULL; sk2 = sk2->bind_next) {
+ if (sk->bound_dev_if == sk2->bound_dev_if) {
+ if ((!sk_reuse ||
+ !sk2->reuse ||
+ sk2->state == TCP_LISTEN) &&
+ ipv6_rcv_saddr_equal(sk, sk2))
+ break;
+ }
+ }
+ /* If we found a conflict, fail. */
+ if (sk2 != NULL)
+ goto fail;
+ }
+ }
+ if (tb == NULL &&
+ (tb = tcp_bucket_create(snum)) == NULL)
+ goto fail;
+ if (tb->owners == NULL) {
+ if (sk->reuse && sk->state != TCP_LISTEN)
+ tb->fastreuse = 1;
+ else
+ tb->fastreuse = 0;
+ } else if (tb->fastreuse &&
+ ((sk->reuse == 0) || (sk->state == TCP_LISTEN)))
+ tb->fastreuse = 0;
+
+success:
+ sk->num = snum;
+ if ((sk->bind_next = tb->owners) != NULL)
+ tb->owners->bind_pprev = &sk->bind_next;
+ tb->owners = sk;
+ sk->bind_pprev = &tb->owners;
+ sk->prev = (struct sock *) tb;
+
+ SOCKHASH_UNLOCK();
+ return 0;
+
+fail:
+ SOCKHASH_UNLOCK();
+ return 1;
+}
+
+static void tcp_v6_hash(struct sock *sk)
+{
+ if(sk->state != TCP_CLOSE) {
+ struct sock **skp;
+
+ /* Well, I know that it is ugly...
+ * All this ->prot, ->af_specific etc. need LARGE cleanup --ANK
+ */
+ if (sk->tp_pinfo.af_tcp.af_specific == &ipv6_mapped) {
+ tcp_prot.hash(sk);
+ return;
+ }
+
+ if(sk->state == TCP_LISTEN)
+ skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
+ else
+ skp = &tcp_ehash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
+
+ SOCKHASH_LOCK();
+ if((sk->next = *skp) != NULL)
+ (*skp)->pprev = &sk->next;
+ *skp = sk;
+ sk->pprev = skp;
+ SOCKHASH_UNLOCK();
+ }
+}
+
+static void tcp_v6_unhash(struct sock *sk)
+{
+ SOCKHASH_LOCK();
+ if(sk->pprev) {
+ if(sk->next)
+ sk->next->pprev = sk->pprev;
+ *sk->pprev = sk->next;
+ sk->pprev = NULL;
+ tcp_reg_zap(sk);
+ __tcp_put_port(sk);
+ }
+ SOCKHASH_UNLOCK();
+}
+
+static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned short hnum, int dif)
+{
+ struct sock *sk;
+ struct sock *result = NULL;
+ int score, hiscore;
+
+ hiscore=0;
+ sk = tcp_listening_hash[tcp_lhashfn(hnum)];
+ for(; sk; sk = sk->next) {
+ if((sk->num == hnum) && (sk->family == PF_INET6)) {
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+
+ score = 1;
+ if(!ipv6_addr_any(&np->rcv_saddr)) {
+ if(ipv6_addr_cmp(&np->rcv_saddr, daddr))
+ continue;
+ score++;
+ }
+ if (sk->bound_dev_if) {
+ if (sk->bound_dev_if != dif)
+ continue;
+ score++;
+ }
+ if (score == 3)
+ return sk;
+ if (score > hiscore) {
+ hiscore = score;
+ result = sk;
+ }
+ }
+ }
+ return result;
+}
+
+/* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
+ * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
+ * It is assumed that this code only gets called from within NET_BH.
+ */
+static inline struct sock *__tcp_v6_lookup(struct tcphdr *th,
+ struct in6_addr *saddr, u16 sport,
+ struct in6_addr *daddr, u16 dport,
+ int dif)
+{
+ struct sock *sk;
+ __u16 hnum = ntohs(dport);
+ __u32 ports = TCP_COMBINED_PORTS(sport, hnum);
+ int hash;
+
+ /* Check TCP register quick cache first. */
+ sk = TCP_RHASH(sport);
+ if(sk && TCP_IPV6_MATCH(sk, saddr, daddr, ports, dif))
+ goto hit;
+
+ /* Optimize here for direct hit, only listening connections can
+ * have wildcards anyways.
+ */
+ hash = tcp_v6_hashfn(daddr, hnum, saddr, sport);
+ for(sk = tcp_ehash[hash]; sk; sk = sk->next) {
+ /* For IPV6 do the cheaper port and family tests first. */
+ if(TCP_IPV6_MATCH(sk, saddr, daddr, ports, dif)) {
+ if (sk->state == TCP_ESTABLISHED)
+ TCP_RHASH(sport) = sk;
+ goto hit; /* You sunk my battleship! */
+ }
+ }
+ /* Must check for a TIME_WAIT'er before going to listener hash. */
+ for(sk = tcp_ehash[hash+(tcp_ehash_size/2)]; sk; sk = sk->next) {
+ if(*((__u32 *)&(sk->dport)) == ports &&
+ sk->family == PF_INET6) {
+ struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk;
+ if(!ipv6_addr_cmp(&tw->v6_daddr, saddr) &&
+ !ipv6_addr_cmp(&tw->v6_rcv_saddr, daddr) &&
+ (!sk->bound_dev_if || sk->bound_dev_if == dif))
+ goto hit;
+ }
+ }
+ sk = tcp_v6_lookup_listener(daddr, hnum, dif);
+hit:
+ return sk;
+}
+
+#define tcp_v6_lookup(sa, sp, da, dp, dif) __tcp_v6_lookup((0),(sa),(sp),(da),(dp),(dif))
+
+static __inline__ u16 tcp_v6_check(struct tcphdr *th, int len,
+ struct in6_addr *saddr,
+ struct in6_addr *daddr,
+ unsigned long base)
+{
+ return csum_ipv6_magic(saddr, daddr, len, IPPROTO_TCP, base);
+}
+
+static __u32 tcp_v6_init_sequence(struct sock *sk, struct sk_buff *skb)
+{
+ __u32 si;
+ __u32 di;
+
+ if (skb->protocol == __constant_htons(ETH_P_IPV6)) {
+ si = skb->nh.ipv6h->saddr.s6_addr32[3];
+ di = skb->nh.ipv6h->daddr.s6_addr32[3];
+ } else {
+ si = skb->nh.iph->saddr;
+ di = skb->nh.iph->daddr;
+ }
+
+ return secure_tcp_sequence_number(di, si,
+ skb->h.th->dest,
+ skb->h.th->source);
+}
+
+static int tcp_v6_unique_address(struct sock *sk)
+{
+ struct tcp_bind_bucket *tb;
+ unsigned short snum = sk->num;
+ int retval = 1;
+
+ /* Freeze the hash while we snoop around. */
+ SOCKHASH_LOCK();
+ tb = tcp_bhash[tcp_bhashfn(snum)];
+ for(; tb; tb = tb->next) {
+ if(tb->port == snum && tb->owners != NULL) {
+ /* Almost certainly the re-use port case, search the real hashes
+ * so it actually scales. (we hope that all ipv6 ftp servers will
+ * use passive ftp, I just cover this case for completeness)
+ */
+ sk = __tcp_v6_lookup(NULL, &sk->net_pinfo.af_inet6.daddr,
+ sk->dport,
+ &sk->net_pinfo.af_inet6.rcv_saddr,
+ htons(snum),
+ sk->bound_dev_if);
+ if((sk != NULL) && (sk->state != TCP_LISTEN))
+ retval = 0;
+ break;
+ }
+ }
+ SOCKHASH_UNLOCK();
+ return retval;
+}
+
+static __inline__ int tcp_v6_iif(struct sk_buff *skb)
+{
+ struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb;
+ return opt->iif;
+}
+
+static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
+ int addr_len)
+{
+ struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+ struct in6_addr *saddr = NULL;
+ struct in6_addr saddr_buf;
+ struct flowi fl;
+ struct dst_entry *dst;
+ struct sk_buff *buff;
+ int addr_type;
+ int err;
+
+ if (sk->state != TCP_CLOSE)
+ return(-EISCONN);
+
+ /*
+ * Don't allow a double connect.
+ */
+
+ if(!ipv6_addr_any(&np->daddr))
+ return -EINVAL;
+
+ if (addr_len < sizeof(struct sockaddr_in6))
+ return(-EINVAL);
+
+ if (usin->sin6_family && usin->sin6_family != AF_INET6)
+ return(-EAFNOSUPPORT);
+
+ fl.fl6_flowlabel = 0;
+ if (np->sndflow) {
+ fl.fl6_flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK;
+ if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
+ struct ip6_flowlabel *flowlabel;
+ flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
+ if (flowlabel == NULL)
+ return -EINVAL;
+ ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst);
+ fl6_sock_release(flowlabel);
+ }
+ }
+
+ /*
+ * connect() to INADDR_ANY means loopback (BSD'ism).
+ */
+
+ if(ipv6_addr_any(&usin->sin6_addr))
+ usin->sin6_addr.s6_addr[15] = 0x1;
+
+ addr_type = ipv6_addr_type(&usin->sin6_addr);
+
+ if(addr_type & IPV6_ADDR_MULTICAST)
+ return -ENETUNREACH;
+
+ if (addr_type&IPV6_ADDR_LINKLOCAL) {
+ if (addr_len >= sizeof(struct sockaddr_in6) &&
+ usin->sin6_scope_id) {
+ /* If interface is set while binding, indices
+ * must coincide.
+ */
+ if (sk->bound_dev_if &&
+ sk->bound_dev_if != usin->sin6_scope_id)
+ return -EINVAL;
+
+ sk->bound_dev_if = usin->sin6_scope_id;
+ }
+
+ /* Connect to link-local address requires an interface */
+ if (!sk->bound_dev_if)
+ return -EINVAL;
+ }
+
+ /*
+ * connect to self not allowed
+ */
+
+ if (ipv6_addr_cmp(&usin->sin6_addr, &np->saddr) == 0 &&
+ usin->sin6_port == sk->sport)
+ return (-EINVAL);
+
+ memcpy(&np->daddr, &usin->sin6_addr, sizeof(struct in6_addr));
+ np->flow_label = fl.fl6_flowlabel;
+
+ /*
+ * TCP over IPv4
+ */
+
+ if (addr_type == IPV6_ADDR_MAPPED) {
+ u32 exthdrlen = tp->ext_header_len;
+ struct sockaddr_in sin;
+
+ SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
+
+ if (__ipv6_only_sock(sk))
+ return -ENETUNREACH;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = usin->sin6_port;
+ sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
+
+ sk->tp_pinfo.af_tcp.af_specific = &ipv6_mapped;
+ sk->backlog_rcv = tcp_v4_do_rcv;
+
+ err = tcp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
+
+ if (err) {
+ tp->ext_header_len = exthdrlen;
+ sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific;
+ sk->backlog_rcv = tcp_v6_do_rcv;
+ goto failure;
+ } else {
+ ipv6_addr_set(&np->saddr, 0, 0, __constant_htonl(0x0000FFFF),
+ sk->saddr);
+ ipv6_addr_set(&np->rcv_saddr, 0, 0, __constant_htonl(0x0000FFFF),
+ sk->rcv_saddr);
+ }
+
+ return err;
+ }
+
+ if (!ipv6_addr_any(&np->rcv_saddr))
+ saddr = &np->rcv_saddr;
+
+ fl.proto = IPPROTO_TCP;
+ fl.fl6_dst = &np->daddr;
+ fl.fl6_src = saddr;
+ fl.oif = sk->bound_dev_if;
+ fl.uli_u.ports.dport = usin->sin6_port;
+ fl.uli_u.ports.sport = sk->sport;
+
+ if (np->opt && np->opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+ fl.nl_u.ip6_u.daddr = rt0->addr;
+ }
+
+ dst = ip6_route_output(sk, &fl);
+
+ if ((err = dst->error) != 0) {
+ dst_release(dst);
+ goto failure;
+ }
+
+ if (fl.oif == 0 && addr_type&IPV6_ADDR_LINKLOCAL) {
+ /* Ough! This guy tries to connect to link local
+ * address and did not specify interface.
+ * Actually we should kick him out, but
+ * we will be patient :) --ANK
+ */
+ sk->bound_dev_if = dst->dev->ifindex;
+ }
+
+ ip6_dst_store(sk, dst, NULL);
+
+ if (saddr == NULL) {
+ err = ipv6_get_saddr(dst, &np->daddr, &saddr_buf);
+ if (err)
+ goto failure;
+
+ saddr = &saddr_buf;
+ }
+
+ /* set the source address */
+ ipv6_addr_copy(&np->rcv_saddr, saddr);
+ ipv6_addr_copy(&np->saddr, saddr);
+
+ tp->ext_header_len = 0;
+ if (np->opt)
+ tp->ext_header_len = np->opt->opt_flen+np->opt->opt_nflen;
+ /* Reset mss clamp */
+ tp->mss_clamp = ~0;
+
+ err = -ENOBUFS;
+ buff = sock_wmalloc(sk, (MAX_HEADER + sk->prot->max_header),
+ 0, GFP_KERNEL);
+
+ if (buff == NULL)
+ goto failure;
+
+ sk->dport = usin->sin6_port;
+
+ if (!tcp_v6_unique_address(sk)) {
+ kfree_skb(buff);
+ err = -EADDRNOTAVAIL;
+ goto failure;
+ }
+
+ /*
+ * Init variables
+ */
+
+ tp->write_seq = secure_tcp_sequence_number(np->saddr.s6_addr32[3],
+ np->daddr.s6_addr32[3],
+ sk->sport, sk->dport);
+
+ tcp_connect(sk, buff, dst->pmtu);
+
+ return 0;
+
+failure:
+ dst_release(xchg(&sk->dst_cache, NULL));
+ memset(&np->daddr, 0, sizeof(struct in6_addr));
+ sk->daddr = 0;
+ return err;
+}
+
+static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ int retval = -EINVAL;
+
+ /*
+ * Do sanity checking for sendmsg/sendto/send
+ */
+
+ if (msg->msg_flags & ~(MSG_OOB|MSG_DONTROUTE|MSG_DONTWAIT|MSG_NOSIGNAL))
+ goto out;
+ if (msg->msg_name) {
+ struct sockaddr_in6 *addr=(struct sockaddr_in6 *)msg->msg_name;
+
+ if (msg->msg_namelen < sizeof(*addr))
+ goto out;
+
+ if (addr->sin6_family && addr->sin6_family != AF_INET6)
+ goto out;
+ retval = -ENOTCONN;
+
+ if(sk->state == TCP_CLOSE)
+ goto out;
+ retval = -EISCONN;
+ if (addr->sin6_port != sk->dport)
+ goto out;
+ if (ipv6_addr_cmp(&addr->sin6_addr, &np->daddr))
+ goto out;
+ if (np->sndflow && np->flow_label != (addr->sin6_flowinfo&IPV6_FLOWINFO_MASK))
+ goto out;
+ }
+
+ retval = tcp_do_sendmsg(sk, msg);
+
+out:
+ return retval;
+}
+
+void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr,
+ struct inet6_skb_parm *opt,
+ int type, int code, unsigned char *header, __u32 info)
+{
+ struct in6_addr *saddr = &hdr->saddr;
+ struct in6_addr *daddr = &hdr->daddr;
+ struct tcphdr *th = (struct tcphdr *)header;
+ struct ipv6_pinfo *np;
+ struct sock *sk;
+ int err;
+ struct tcp_opt *tp;
+ __u32 seq;
+
+ if (header + 8 > skb->tail)
+ return;
+
+ sk = tcp_v6_lookup(daddr, th->dest, saddr, th->source, skb->dev->ifindex);
+
+ if (sk == NULL || sk->state == TCP_TIME_WAIT) {
+ /* XXX: Update ICMP error count */
+ return;
+ }
+
+ tp = &sk->tp_pinfo.af_tcp;
+ seq = ntohl(th->seq);
+ if (sk->state != TCP_LISTEN && !between(seq, tp->snd_una, tp->snd_nxt)) {
+ net_statistics.OutOfWindowIcmps++;
+ return;
+ }
+
+ np = &sk->net_pinfo.af_inet6;
+ if (type == ICMPV6_PKT_TOOBIG) {
+ struct dst_entry *dst = NULL;
+
+ if (atomic_read(&sk->sock_readers))
+ return;
+
+ if (sk->state == TCP_LISTEN)
+ return;
+
+ /* icmp should have updated the destination cache entry */
+ if (sk->dst_cache)
+ dst = dst_check(&sk->dst_cache, np->dst_cookie);
+
+ if (dst == NULL) {
+ struct flowi fl;
+
+ /* BUGGG_FUTURE: Again, it is not clear how
+ to handle rthdr case. Ignore this complexity
+ for now.
+ */
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &np->daddr;
+ fl.nl_u.ip6_u.saddr = &np->saddr;
+ fl.oif = sk->bound_dev_if;
+ fl.uli_u.ports.dport = sk->dport;
+ fl.uli_u.ports.sport = sk->sport;
+
+ dst = ip6_route_output(sk, &fl);
+ } else
+ dst = dst_clone(dst);
+
+ if (dst->error) {
+ sk->err_soft = -dst->error;
+ } else if (tp->pmtu_cookie > dst->pmtu) {
+ tcp_sync_mss(sk, dst->pmtu);
+ tcp_simple_retransmit(sk);
+ } /* else let the usual retransmit timer handle it */
+ dst_release(dst);
+ return;
+ }
+
+ icmpv6_err_convert(type, code, &err);
+
+ /* Might be for an open_request */
+ switch (sk->state) {
+ struct open_request *req, *prev;
+ struct ipv6hdr hd;
+ case TCP_LISTEN:
+ if (atomic_read(&sk->sock_readers)) {
+ net_statistics.LockDroppedIcmps++;
+ /* If too many ICMPs get dropped on busy
+ * servers this needs to be solved differently.
+ */
+ return;
+ }
+
+ /* Grrrr - fix this later. */
+ ipv6_addr_copy(&hd.saddr, saddr);
+ ipv6_addr_copy(&hd.daddr, daddr);
+ req = tcp_v6_search_req(tp, &hd, th, tcp_v6_iif(skb), &prev);
+ if (!req)
+ return;
+ if (seq != req->snt_isn) {
+ net_statistics.OutOfWindowIcmps++;
+ return;
+ }
+ if (req->sk) {
+ sk = req->sk; /* report error in accept */
+ } else {
+ tp->syn_backlog--;
+ tcp_synq_unlink(tp, req, prev);
+ req->class->destructor(req);
+ tcp_openreq_free(req);
+ }
+ /* FALL THROUGH */
+ case TCP_SYN_SENT:
+ case TCP_SYN_RECV: /* Cannot happen */
+ tcp_statistics.TcpAttemptFails++;
+ sk->err = err;
+ sk->zapped = 1;
+ mb();
+ sk->error_report(sk);
+ return;
+ }
+
+ if (np->recverr) {
+ /* This code isn't serialized with the socket code */
+ /* ANK (980927) ... which is harmless now,
+ sk->err's may be safely lost.
+ */
+ sk->err = err;
+ mb();
+ sk->error_report(sk);
+ } else {
+ sk->err_soft = err;
+ mb();
+ }
+}
+
+
+static void tcp_v6_send_synack(struct sock *sk, struct open_request *req)
+{
+ struct sk_buff * skb;
+ struct dst_entry *dst;
+ struct ipv6_txoptions *opt = NULL;
+ struct flowi fl;
+ int mss;
+
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &req->af.v6_req.rmt_addr;
+ fl.nl_u.ip6_u.saddr = &req->af.v6_req.loc_addr;
+ fl.fl6_flowlabel = 0;
+ fl.oif = req->af.v6_req.iif;
+ fl.uli_u.ports.dport = req->rmt_port;
+ fl.uli_u.ports.sport = sk->sport;
+
+ opt = sk->net_pinfo.af_inet6.opt;
+ if (opt == NULL &&
+ sk->net_pinfo.af_inet6.rxopt.bits.srcrt == 2 &&
+ req->af.v6_req.pktopts) {
+ struct sk_buff *pktopts = req->af.v6_req.pktopts;
+ struct inet6_skb_parm *rxopt = (struct inet6_skb_parm *)pktopts->cb;
+ if (rxopt->srcrt)
+ opt = ipv6_invert_rthdr(sk, (struct ipv6_rt_hdr*)(pktopts->nh.raw + rxopt->srcrt));
+ }
+
+ if (opt && opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
+ fl.nl_u.ip6_u.daddr = rt0->addr;
+ }
+
+ dst = ip6_route_output(sk, &fl);
+ if (dst->error)
+ goto done;
+
+ mss = dst->pmtu - sizeof(struct ipv6hdr) - sizeof(struct tcphdr);
+
+ skb = tcp_make_synack(sk, dst, req, mss);
+ if (skb) {
+ struct tcphdr *th = skb->h.th;
+
+ th->check = tcp_v6_check(th, skb->len,
+ &req->af.v6_req.loc_addr, &req->af.v6_req.rmt_addr,
+ csum_partial((char *)th, skb->len, skb->csum));
+
+ fl.nl_u.ip6_u.daddr = &req->af.v6_req.rmt_addr;
+ ip6_xmit(sk, skb, &fl, opt);
+ }
+
+done:
+ dst_release(dst);
+ if (opt && opt != sk->net_pinfo.af_inet6.opt)
+ sock_kfree_s(sk, opt, opt->tot_len);
+}
+
+static void tcp_v6_or_free(struct open_request *req)
+{
+ if (req->af.v6_req.pktopts) {
+ kfree_skb(req->af.v6_req.pktopts);
+ req->af.v6_req.pktopts = NULL;
+ }
+}
+
+static struct or_calltable or_ipv6 = {
+ tcp_v6_send_synack,
+ tcp_v6_or_free,
+ tcp_v6_send_reset
+};
+
+static int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb)
+{
+ struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
+
+ if (sk->net_pinfo.af_inet6.rxopt.all) {
+ if ((opt->hop && sk->net_pinfo.af_inet6.rxopt.bits.hopopts) ||
+ ((IPV6_FLOWINFO_MASK&*(u32*)skb->nh.raw) &&
+ sk->net_pinfo.af_inet6.rxopt.bits.rxflow) ||
+ (opt->srcrt && sk->net_pinfo.af_inet6.rxopt.bits.srcrt) ||
+ ((opt->dst1 || opt->dst0) && sk->net_pinfo.af_inet6.rxopt.bits.dstopts))
+ return 1;
+ }
+ return 0;
+}
+
+
+#define BACKLOG(sk) ((sk)->tp_pinfo.af_tcp.syn_backlog) /* lvalue! */
+#define BACKLOGMAX(sk) sysctl_max_syn_backlog
+
+/* FIXME: this is substantially similar to the ipv4 code.
+ * Can some kind of merge be done? -- erics
+ */
+static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, __u32 isn)
+{
+ struct tcp_opt tp;
+ struct open_request *req;
+
+ /* If the socket is dead, don't accept the connection. */
+ if (sk->dead) {
+ SOCK_DEBUG(sk, "Reset on %p: Connect on dead socket.\n", sk);
+ tcp_statistics.TcpAttemptFails++;
+ return -ENOTCONN;
+ }
+
+ if (skb->protocol == __constant_htons(ETH_P_IP))
+ return tcp_v4_conn_request(sk, skb, isn);
+
+ /* FIXME: do the same check for anycast */
+ if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr))
+ goto drop;
+
+ if (isn == 0)
+ isn = tcp_v6_init_sequence(sk,skb);
+
+ /*
+ * There are no SYN attacks on IPv6, yet...
+ */
+ if (BACKLOG(sk) >= BACKLOGMAX(sk)) {
+ (void)(net_ratelimit() &&
+ printk(KERN_INFO "droping syn ack:%d max:%d\n",
+ BACKLOG(sk), BACKLOGMAX(sk)));
+ goto drop;
+ }
+
+ req = tcp_openreq_alloc();
+ if (req == NULL) {
+ goto drop;
+ }
+
+ BACKLOG(sk)++;
+
+ req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */
+
+ req->rcv_isn = TCP_SKB_CB(skb)->seq;
+ req->snt_isn = isn;
+ tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0;
+ tp.mss_clamp = 65535;
+ tcp_parse_options(NULL, skb->h.th, &tp, 0);
+ if (tp.mss_clamp == 65535)
+ tp.mss_clamp = 576 - sizeof(struct ipv6hdr) - sizeof(struct iphdr);
+ if (sk->tp_pinfo.af_tcp.user_mss && sk->tp_pinfo.af_tcp.user_mss < tp.mss_clamp)
+ tp.mss_clamp = sk->tp_pinfo.af_tcp.user_mss;
+
+ req->mss = tp.mss_clamp;
+ if (tp.saw_tstamp)
+ req->ts_recent = tp.rcv_tsval;
+ req->tstamp_ok = tp.tstamp_ok;
+ req->sack_ok = tp.sack_ok;
+ req->snd_wscale = tp.snd_wscale;
+ req->wscale_ok = tp.wscale_ok;
+ req->rmt_port = skb->h.th->source;
+ ipv6_addr_copy(&req->af.v6_req.rmt_addr, &skb->nh.ipv6h->saddr);
+ ipv6_addr_copy(&req->af.v6_req.loc_addr, &skb->nh.ipv6h->daddr);
+ req->af.v6_req.pktopts = NULL;
+ if (ipv6_opt_accepted(sk, skb)) {
+ atomic_inc(&skb->users);
+ req->af.v6_req.pktopts = skb;
+ }
+ req->af.v6_req.iif = sk->bound_dev_if;
+
+ /* So that link locals have meaning */
+ if (!sk->bound_dev_if && ipv6_addr_type(&req->af.v6_req.rmt_addr)&IPV6_ADDR_LINKLOCAL)
+ req->af.v6_req.iif = tcp_v6_iif(skb);
+
+ req->class = &or_ipv6;
+ req->retrans = 0;
+ req->sk = NULL;
+
+ tcp_v6_send_synack(sk, req);
+
+ req->expires = jiffies + TCP_TIMEOUT_INIT;
+ tcp_inc_slow_timer(TCP_SLT_SYNACK);
+ tcp_synq_queue(&sk->tp_pinfo.af_tcp, req);
+
+ return 0;
+
+drop:
+ tcp_statistics.TcpAttemptFails++;
+ return 0; /* don't send reset */
+}
+
+static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len,
+ struct sk_buff *skb)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ th->check = 0;
+
+ th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,
+ csum_partial((char *)th, th->doff<<2,
+ skb->csum));
+}
+
+static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
+ struct open_request *req,
+ struct dst_entry *dst)
+{
+ struct ipv6_pinfo *np;
+ struct flowi fl;
+ struct tcp_opt *newtp;
+ struct sock *newsk;
+ struct ipv6_txoptions *opt;
+
+ if (skb->protocol == __constant_htons(ETH_P_IP)) {
+ /*
+ * v6 mapped
+ */
+
+ newsk = tcp_v4_syn_recv_sock(sk, skb, req, dst);
+
+ if (newsk == NULL)
+ return NULL;
+
+ np = &newsk->net_pinfo.af_inet6;
+
+ ipv6_addr_set(&np->daddr, 0, 0, __constant_htonl(0x0000FFFF),
+ newsk->daddr);
+
+ ipv6_addr_set(&np->saddr, 0, 0, __constant_htonl(0x0000FFFF),
+ newsk->saddr);
+
+ ipv6_addr_copy(&np->rcv_saddr, &np->saddr);
+
+ newsk->tp_pinfo.af_tcp.af_specific = &ipv6_mapped;
+ newsk->backlog_rcv = tcp_v4_do_rcv;
+ newsk->net_pinfo.af_inet6.pktoptions = NULL;
+ newsk->net_pinfo.af_inet6.opt = NULL;
+
+ /* It is tricky place. Until this moment IPv4 tcp
+ worked with IPv6 af_tcp.af_specific.
+ Sync it now.
+ */
+ tcp_sync_mss(newsk, newsk->tp_pinfo.af_tcp.pmtu_cookie);
+
+ return newsk;
+ }
+
+ opt = sk->net_pinfo.af_inet6.opt;
+
+ if (sk->ack_backlog > sk->max_ack_backlog)
+ goto out;
+
+ if (sk->net_pinfo.af_inet6.rxopt.bits.srcrt == 2 &&
+ opt == NULL && req->af.v6_req.pktopts) {
+ struct inet6_skb_parm *rxopt = (struct inet6_skb_parm *)req->af.v6_req.pktopts->cb;
+ if (rxopt->srcrt)
+ opt = ipv6_invert_rthdr(sk, (struct ipv6_rt_hdr*)(req->af.v6_req.pktopts->nh.raw+rxopt->srcrt));
+ }
+
+ if (dst == NULL) {
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &req->af.v6_req.rmt_addr;
+ if (opt && opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
+ fl.nl_u.ip6_u.daddr = rt0->addr;
+ }
+ fl.nl_u.ip6_u.saddr = &req->af.v6_req.loc_addr;
+ fl.fl6_flowlabel = 0;
+ fl.oif = sk->bound_dev_if;
+ fl.uli_u.ports.dport = req->rmt_port;
+ fl.uli_u.ports.sport = sk->sport;
+
+ dst = ip6_route_output(sk, &fl);
+ }
+
+ if (dst->error)
+ goto out;
+
+ sk->tp_pinfo.af_tcp.syn_backlog--;
+ sk->ack_backlog++;
+
+ newsk = tcp_create_openreq_child(sk, req, skb);
+ if (newsk == NULL)
+ goto out;
+
+ ip6_dst_store(newsk, dst, NULL);
+
+ newtp = &(newsk->tp_pinfo.af_tcp);
+
+ np = &newsk->net_pinfo.af_inet6;
+ ipv6_addr_copy(&np->daddr, &req->af.v6_req.rmt_addr);
+ ipv6_addr_copy(&np->saddr, &req->af.v6_req.loc_addr);
+ ipv6_addr_copy(&np->rcv_saddr, &req->af.v6_req.loc_addr);
+ newsk->bound_dev_if = req->af.v6_req.iif;
+
+ /* Now IPv6 options...
+
+ First: no IPv4 options.
+ */
+ newsk->opt = NULL;
+
+ /* Clone RX bits */
+ np->rxopt.all = sk->net_pinfo.af_inet6.rxopt.all;
+
+ /* Clone pktoptions received with SYN */
+ np->pktoptions = req->af.v6_req.pktopts;
+ if (np->pktoptions)
+ atomic_inc(&np->pktoptions->users);
+ np->opt = NULL;
+
+ /* Clone native IPv6 options from listening socket (if any)
+
+ Yes, keeping reference count would be much more clever,
+ but we make one more one thing there: reattach optmem
+ to newsk.
+ */
+ if (opt) {
+ np->opt = ipv6_dup_options(newsk, opt);
+ if (opt != sk->net_pinfo.af_inet6.opt)
+ sock_kfree_s(sk, opt, opt->tot_len);
+ }
+
+ newtp->ext_header_len = 0;
+ if (np->opt)
+ newtp->ext_header_len = np->opt->opt_nflen + np->opt->opt_flen;
+
+ tcp_sync_mss(newsk, dst->pmtu);
+ newtp->rcv_mss = newtp->mss_clamp;
+
+ newsk->daddr = LOOPBACK4_IPV6;
+ newsk->saddr = LOOPBACK4_IPV6;
+ newsk->rcv_saddr= LOOPBACK4_IPV6;
+
+ newsk->prot->hash(newsk);
+ tcp_inherit_port(sk, newsk);
+ add_to_prot_sklist(newsk);
+ sk->data_ready(sk, 0); /* Deliver SIGIO */
+
+ return newsk;
+
+out:
+ if (opt && opt != sk->net_pinfo.af_inet6.opt)
+ sock_kfree_s(sk, opt, opt->tot_len);
+ dst_release(dst);
+ return NULL;
+}
+
+static void tcp_v6_send_reset(struct sk_buff *skb)
+{
+ struct tcphdr *th = skb->h.th, *t1;
+ struct sk_buff *buff;
+ struct flowi fl;
+
+ if (th->rst)
+ return;
+
+ if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr))
+ return;
+
+ /*
+ * We need to grab some memory, and put together an RST,
+ * and then put it into the queue to be sent.
+ */
+
+ buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr), GFP_ATOMIC);
+ if (buff == NULL)
+ return;
+
+ skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr));
+
+ t1 = (struct tcphdr *) skb_push(buff,sizeof(struct tcphdr));
+
+ /* Swap the send and the receive. */
+ memset(t1, 0, sizeof(*t1));
+ t1->dest = th->source;
+ t1->source = th->dest;
+ t1->doff = sizeof(*t1)/4;
+ t1->rst = 1;
+
+ if(th->ack) {
+ t1->seq = th->ack_seq;
+ } else {
+ t1->ack = 1;
+ if(!th->syn)
+ t1->ack_seq = th->seq;
+ else
+ t1->ack_seq = htonl(ntohl(th->seq)+1);
+ }
+
+ buff->csum = csum_partial((char *)t1, sizeof(*t1), 0);
+
+ fl.nl_u.ip6_u.daddr = &skb->nh.ipv6h->saddr;
+ fl.nl_u.ip6_u.saddr = &skb->nh.ipv6h->daddr;
+ fl.fl6_flowlabel = 0;
+
+ t1->check = csum_ipv6_magic(fl.nl_u.ip6_u.saddr,
+ fl.nl_u.ip6_u.daddr,
+ sizeof(*t1), IPPROTO_TCP,
+ buff->csum);
+
+ fl.proto = IPPROTO_TCP;
+ fl.oif = tcp_v6_iif(skb);
+ fl.uli_u.ports.dport = t1->dest;
+ fl.uli_u.ports.sport = t1->source;
+
+ /* sk = NULL, but it is safe for now. RST socket required. */
+ buff->dst = ip6_route_output(NULL, &fl);
+
+ if (buff->dst->error == 0) {
+ ip6_xmit(NULL, buff, &fl, NULL);
+ tcp_statistics.TcpOutSegs++;
+ tcp_statistics.TcpOutRsts++;
+ return;
+ }
+
+ kfree_skb(buff);
+}
+
+static void tcp_v6_send_ack(struct sk_buff *skb, __u32 seq, __u32 ack, __u16 window)
+{
+ struct tcphdr *th = skb->h.th, *t1;
+ struct sk_buff *buff;
+ struct flowi fl;
+
+ buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr), GFP_ATOMIC);
+ if (buff == NULL)
+ return;
+
+ skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr));
+
+ t1 = (struct tcphdr *) skb_push(buff,sizeof(struct tcphdr));
+
+ /* Swap the send and the receive. */
+ memset(t1, 0, sizeof(*t1));
+ t1->dest = th->source;
+ t1->source = th->dest;
+ t1->doff = sizeof(*t1)/4;
+ t1->ack = 1;
+ t1->seq = seq;
+ t1->ack_seq = ack;
+
+ t1->window = htons(window);
+
+ buff->csum = csum_partial((char *)t1, sizeof(*t1), 0);
+
+ fl.nl_u.ip6_u.daddr = &skb->nh.ipv6h->saddr;
+ fl.nl_u.ip6_u.saddr = &skb->nh.ipv6h->daddr;
+ fl.fl6_flowlabel = 0;
+
+ t1->check = csum_ipv6_magic(fl.nl_u.ip6_u.saddr,
+ fl.nl_u.ip6_u.daddr,
+ sizeof(*t1), IPPROTO_TCP,
+ buff->csum);
+
+ fl.proto = IPPROTO_TCP;
+ fl.oif = tcp_v6_iif(skb);
+ fl.uli_u.ports.dport = t1->dest;
+ fl.uli_u.ports.sport = t1->source;
+
+ /* sk = NULL, but it is safe for now. static socket required. */
+ buff->dst = ip6_route_output(NULL, &fl);
+
+ if (buff->dst->error == 0) {
+ ip6_xmit(NULL, buff, &fl, NULL);
+ tcp_statistics.TcpOutSegs++;
+ return;
+ }
+
+ kfree_skb(buff);
+}
+
+static struct open_request *tcp_v6_search_req(struct tcp_opt *tp,
+ struct ipv6hdr *ip6h,
+ struct tcphdr *th,
+ int iif,
+ struct open_request **prevp)
+{
+ struct open_request *req, *prev;
+ __u16 rport = th->source;
+
+ /* assumption: the socket is not in use.
+ * as we checked the user count on tcp_rcv and we're
+ * running from a soft interrupt.
+ */
+ prev = (struct open_request *) (&tp->syn_wait_queue);
+ for (req = prev->dl_next; req; req = req->dl_next) {
+ if (!ipv6_addr_cmp(&req->af.v6_req.rmt_addr, &ip6h->saddr) &&
+ !ipv6_addr_cmp(&req->af.v6_req.loc_addr, &ip6h->daddr) &&
+ req->rmt_port == rport &&
+ (!req->af.v6_req.iif || req->af.v6_req.iif == iif)) {
+ *prevp = prev;
+ return req;
+ }
+ prev = req;
+ }
+ return NULL;
+}
+
+static void tcp_v6_rst_req(struct sock *sk, struct sk_buff *skb)
+{
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+ struct open_request *req, *prev;
+
+ req = tcp_v6_search_req(tp,skb->nh.ipv6h,skb->h.th,tcp_v6_iif(skb),&prev);
+ if (!req)
+ return;
+ /* Sequence number check required by RFC793 */
+ if (before(TCP_SKB_CB(skb)->seq, req->rcv_isn) ||
+ after(TCP_SKB_CB(skb)->seq, req->rcv_isn+1))
+ return;
+ if(req->sk)
+ sk->ack_backlog--;
+ else
+ tp->syn_backlog--;
+ tcp_synq_unlink(tp, req, prev);
+ req->class->destructor(req);
+ tcp_openreq_free(req);
+ net_statistics.EmbryonicRsts++;
+}
+
+static inline struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb)
+{
+ struct tcphdr *th = skb->h.th;
+ u32 flg = ((u32 *)th)[3];
+
+ /* Check for RST */
+ if (flg & __constant_htonl(0x00040000)) {
+ tcp_v6_rst_req(sk, skb);
+ return NULL;
+ }
+
+ /* Check SYN|ACK */
+ if (flg & __constant_htonl(0x00120000)) {
+ struct open_request *req, *dummy;
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ req = tcp_v6_search_req(tp, skb->nh.ipv6h, th, tcp_v6_iif(skb), &dummy);
+ if (req) {
+ sk = tcp_check_req(sk, skb, req);
+ }
+#if 0 /*def CONFIG_SYN_COOKIES */
+ else {
+ sk = cookie_v6_check(sk, skb);
+ }
+#endif
+ }
+ return sk;
+}
+
+static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
+{
+#ifdef CONFIG_FILTER
+ struct sk_filter *filter;
+#endif
+ int users = 0;
+
+ /* Imagine: socket is IPv6. IPv4 packet arrives,
+ goes to IPv4 receive handler and backlogged.
+ From backlog it always goes here. Kerboom...
+ Fortunately, tcp_rcv_established and rcv_established
+ handle them correctly, but it is not case with
+ tcp_v6_hnd_req and tcp_v6_send_reset(). --ANK
+ */
+
+ if (skb->protocol == __constant_htons(ETH_P_IP))
+ return tcp_v4_do_rcv(sk, skb);
+
+#ifdef CONFIG_FILTER
+ filter = sk->filter;
+ if (filter && sk_filter(skb, filter))
+ goto discard;
+#endif /* CONFIG_FILTER */
+
+ /*
+ * socket locking is here for SMP purposes as backlog rcv
+ * is currently called with bh processing disabled.
+ */
+
+ ipv6_statistics.Ip6InDelivers++;
+
+ /*
+ * This doesn't check if the socket has enough room for the packet.
+ * Either process the packet _without_ queueing it and then free it,
+ * or do the check later.
+ */
+ skb_set_owner_r(skb, sk);
+
+ /* Do Stevens' IPV6_PKTOPTIONS.
+
+ Yes, guys, it is the only place in our code, where we
+ may make it not affecting IPv4.
+ The rest of code is protocol independent,
+ and I do not like idea to uglify IPv4.
+
+ Actually, all the idea behind IPV6_PKTOPTIONS
+ looks not very well thought. For now we latch
+ options, received in the last packet, enqueued
+ by tcp. Feel free to propose better solution.
+ --ANK (980728)
+ */
+ if (sk->net_pinfo.af_inet6.rxopt.all) {
+ users = atomic_read(&skb->users);
+ atomic_inc(&skb->users);
+ }
+
+ if (sk->state == TCP_ESTABLISHED) { /* Fast path */
+ if (tcp_rcv_established(sk, skb, skb->h.th, skb->len))
+ goto reset;
+ if (users)
+ goto ipv6_pktoptions;
+ return 0;
+ }
+
+ if (sk->state == TCP_LISTEN) {
+ struct sock *nsk;
+
+ nsk = tcp_v6_hnd_req(sk, skb);
+ if (!nsk)
+ goto discard;
+
+ /*
+ * Queue it on the new socket if the new socket is active,
+ * otherwise we just shortcircuit this and continue with
+ * the new socket..
+ */
+ if (atomic_read(&nsk->sock_readers)) {
+ skb_orphan(skb);
+ __skb_queue_tail(&nsk->back_log, skb);
+ return 0;
+ }
+ sk = nsk;
+ }
+
+ if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len))
+ goto reset;
+ if (users)
+ goto ipv6_pktoptions;
+ return 0;
+
+reset:
+ tcp_v6_send_reset(skb);
+discard:
+ if (users)
+ kfree_skb(skb);
+ kfree_skb(skb);
+ return 0;
+
+ipv6_pktoptions:
+ /* Do you ask, what is it?
+
+ 1. skb was enqueued by tcp.
+ 2. skb is added to tail of read queue, rather than out of order.
+ 3. socket is not in passive state.
+ 4. Finally, it really contains options, which user wants to receive.
+ */
+ if (atomic_read(&skb->users) > users &&
+ TCP_SKB_CB(skb)->end_seq == sk->tp_pinfo.af_tcp.rcv_nxt &&
+ !((1<<sk->state)&(TCPF_CLOSE|TCPF_LISTEN))) {
+ if (ipv6_opt_accepted(sk, skb)) {
+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+ kfree_skb(skb);
+ skb = NULL;
+ if (skb2) {
+ skb_set_owner_r(skb2, sk);
+ skb = xchg(&sk->net_pinfo.af_inet6.pktoptions, skb2);
+ }
+ } else {
+ kfree_skb(skb);
+ skb = xchg(&sk->net_pinfo.af_inet6.pktoptions, NULL);
+ }
+ }
+
+ if (skb)
+ kfree_skb(skb);
+ return 0;
+}
+
+int tcp_v6_rcv(struct sk_buff *skb, unsigned long len)
+{
+ struct tcphdr *th;
+ struct sock *sk;
+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+ struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+
+ th = skb->h.th;
+
+ if (skb->pkt_type != PACKET_HOST)
+ goto discard_it;
+
+ /*
+ * Pull up the IP header.
+ */
+
+ __skb_pull(skb, skb->h.raw - skb->data);
+
+ /*
+ * Count it even if it's bad.
+ */
+
+ tcp_statistics.TcpInSegs++;
+
+ len = skb->len;
+ if (len < sizeof(struct tcphdr))
+ goto bad_packet;
+
+ /*
+ * Try to use the device checksum if provided.
+ */
+
+ switch (skb->ip_summed) {
+ case CHECKSUM_NONE:
+ skb->csum = csum_partial((char *)th, len, 0);
+ case CHECKSUM_HW:
+ if (tcp_v6_check(th,len,saddr,daddr,skb->csum)) {
+ printk(KERN_DEBUG "tcp csum failed\n");
+ bad_packet:
+ tcp_statistics.TcpInErrs++;
+ goto discard_it;
+ }
+ default:
+ /* CHECKSUM_UNNECESSARY */
+ break;
+ };
+
+ if((th->doff * 4) < sizeof(struct tcphdr) ||
+ len < (th->doff * 4))
+ goto bad_packet;
+
+ sk = __tcp_v6_lookup(th, saddr, th->source, daddr, th->dest, tcp_v6_iif(skb));
+
+ if (!sk)
+ goto no_tcp_socket;
+
+ TCP_SKB_CB(skb)->seq = ntohl(th->seq);
+ TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
+ len - th->doff*4);
+ TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
+ skb->used = 0;
+ if(sk->state == TCP_TIME_WAIT)
+ goto do_time_wait;
+
+ if (!atomic_read(&sk->sock_readers))
+ return tcp_v6_do_rcv(sk, skb);
+
+ __skb_queue_tail(&sk->back_log, skb);
+ return(0);
+
+no_tcp_socket:
+ tcp_v6_send_reset(skb);
+
+discard_it:
+
+ /*
+ * Discard frame
+ */
+
+ kfree_skb(skb);
+ return 0;
+
+do_time_wait:
+ switch (tcp_timewait_state_process((struct tcp_tw_bucket *)sk,
+ skb, th, skb->len)) {
+ case TCP_TW_ACK:
+ tcp_v6_send_ack(skb,
+ ((struct tcp_tw_bucket *)sk)->snd_nxt,
+ ((struct tcp_tw_bucket *)sk)->rcv_nxt,
+ ((struct tcp_tw_bucket *)sk)->window);
+ goto discard_it;
+ case TCP_TW_RST:
+ goto no_tcp_socket;
+ default:
+ goto discard_it;
+ }
+}
+
+static int tcp_v6_rebuild_header(struct sock *sk)
+{
+ struct dst_entry *dst = NULL;
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+
+ if (sk->dst_cache)
+ dst = dst_check(&sk->dst_cache, np->dst_cookie);
+
+ if (dst == NULL) {
+ struct flowi fl;
+
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &np->daddr;
+ fl.nl_u.ip6_u.saddr = &np->saddr;
+ fl.fl6_flowlabel = np->flow_label;
+ fl.oif = sk->bound_dev_if;
+ fl.uli_u.ports.dport = sk->dport;
+ fl.uli_u.ports.sport = sk->sport;
+
+ if (np->opt && np->opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+ fl.nl_u.ip6_u.daddr = rt0->addr;
+ }
+
+
+ dst = ip6_route_output(sk, &fl);
+
+ if (dst->error) {
+ dst_release(dst);
+ return dst->error;
+ }
+
+ ip6_dst_store(sk, dst, NULL);
+ }
+
+ return dst->error;
+}
+
+static struct sock * tcp_v6_get_sock(struct sk_buff *skb, struct tcphdr *th)
+{
+ struct in6_addr *saddr;
+ struct in6_addr *daddr;
+
+ if (skb->protocol == __constant_htons(ETH_P_IP))
+ return ipv4_specific.get_sock(skb, th);
+
+ saddr = &skb->nh.ipv6h->saddr;
+ daddr = &skb->nh.ipv6h->daddr;
+ return tcp_v6_lookup(saddr, th->source, daddr, th->dest, tcp_v6_iif(skb));
+}
+
+static void tcp_v6_xmit(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+ struct ipv6_pinfo * np = &sk->net_pinfo.af_inet6;
+ struct flowi fl;
+ struct dst_entry *dst = sk->dst_cache;
+
+ fl.proto = IPPROTO_TCP;
+ fl.fl6_dst = &np->daddr;
+ fl.fl6_src = &np->saddr;
+ fl.fl6_flowlabel = np->flow_label;
+ fl.oif = sk->bound_dev_if;
+ fl.uli_u.ports.sport = sk->sport;
+ fl.uli_u.ports.dport = sk->dport;
+
+ if (np->opt && np->opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+ fl.nl_u.ip6_u.daddr = rt0->addr;
+ }
+
+ if (sk->dst_cache)
+ dst = dst_check(&sk->dst_cache, np->dst_cookie);
+
+ if (dst == NULL) {
+ dst = ip6_route_output(sk, &fl);
+
+ if (dst->error) {
+ sk->err_soft = -dst->error;
+ dst_release(dst);
+ return;
+ }
+
+ ip6_dst_store(sk, dst, NULL);
+ }
+
+ skb->dst = dst_clone(dst);
+
+ /* Restore final destination back after routing done */
+ fl.nl_u.ip6_u.daddr = &np->daddr;
+
+ ip6_xmit(sk, skb, &fl, np->opt);
+}
+
+static void v6_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr)
+{
+ struct ipv6_pinfo * np = &sk->net_pinfo.af_inet6;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) uaddr;
+
+ sin6->sin6_family = AF_INET6;
+ memcpy(&sin6->sin6_addr, &np->daddr, sizeof(struct in6_addr));
+ sin6->sin6_port = sk->dport;
+ /* We do not store received flowlabel for TCP */
+ sin6->sin6_flowinfo = 0;
+ sin6->sin6_scope_id = 0;
+ if (sk->bound_dev_if &&
+ ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
+ sin6->sin6_scope_id = sk->bound_dev_if;
+}
+
+static struct tcp_func ipv6_specific = {
+ tcp_v6_xmit,
+ tcp_v6_send_check,
+ tcp_v6_rebuild_header,
+ tcp_v6_conn_request,
+ tcp_v6_syn_recv_sock,
+ tcp_v6_get_sock,
+ sizeof(struct ipv6hdr),
+
+ ipv6_setsockopt,
+ ipv6_getsockopt,
+ v6_addr2sockaddr,
+ sizeof(struct sockaddr_in6)
+};
+
+/*
+ * TCP over IPv4 via INET6 API
+ */
+
+static struct tcp_func ipv6_mapped = {
+ ip_queue_xmit,
+ tcp_v4_send_check,
+ tcp_v4_rebuild_header,
+ tcp_v6_conn_request,
+ tcp_v6_syn_recv_sock,
+ tcp_v6_get_sock,
+ sizeof(struct iphdr),
+
+ ipv6_setsockopt,
+ ipv6_getsockopt,
+ v6_addr2sockaddr,
+ sizeof(struct sockaddr_in6)
+};
+
+/* NOTE: A lot of things set to zero explicitly by call to
+ * sk_alloc() so need not be done here.
+ */
+static int tcp_v6_init_sock(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ skb_queue_head_init(&tp->out_of_order_queue);
+ tcp_init_xmit_timers(sk);
+
+ tp->rto = TCP_TIMEOUT_INIT; /*TCP_WRITE_TIME*/
+ tp->mdev = TCP_TIMEOUT_INIT;
+ tp->mss_clamp = ~0;
+
+ /* So many TCP implementations out there (incorrectly) count the
+ * initial SYN frame in their delayed-ACK and congestion control
+ * algorithms that we must have the following bandaid to talk
+ * efficiently to them. -DaveM
+ */
+ tp->snd_cwnd = 2;
+
+ /* See draft-stevens-tcpca-spec-01 for discussion of the
+ * initialization of these values.
+ */
+ tp->snd_cwnd_cnt = 0;
+ tp->snd_ssthresh = 0x7fffffff;
+
+ sk->state = TCP_CLOSE;
+ sk->max_ack_backlog = SOMAXCONN;
+ tp->rcv_mss = 536;
+
+ /* Init SYN queue. */
+ tcp_synq_init(tp);
+
+ sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific;
+
+ sk->write_space = tcp_write_space;
+
+ return 0;
+}
+
+static int tcp_v6_destroy_sock(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct sk_buff *skb;
+
+ tcp_clear_xmit_timers(sk);
+
+ if (sk->keepopen)
+ tcp_dec_slow_timer(TCP_SLT_KEEPALIVE);
+
+ /*
+ * Cleanup up the write buffer.
+ */
+
+ while((skb = __skb_dequeue(&sk->write_queue)) != NULL)
+ kfree_skb(skb);
+
+ /*
+ * Cleans up our, hopefuly empty, out_of_order_queue
+ */
+
+ while((skb = __skb_dequeue(&tp->out_of_order_queue)) != NULL)
+ kfree_skb(skb);
+
+ /* Clean up a locked TCP bind bucket, this only happens if a
+ * port is allocated for a socket, but it never fully connects.
+ */
+ if(sk->prev != NULL)
+ tcp_put_port(sk);
+
+ return inet6_destroy_sock(sk);
+}
+
+struct proto tcpv6_prot = {
+ (struct sock *)&tcpv6_prot, /* sklist_next */
+ (struct sock *)&tcpv6_prot, /* sklist_prev */
+ tcp_close, /* close */
+ tcp_v6_connect, /* connect */
+ tcp_accept, /* accept */
+ NULL, /* retransmit */
+ tcp_write_wakeup, /* write_wakeup */
+ tcp_read_wakeup, /* read_wakeup */
+ tcp_poll, /* poll */
+ tcp_ioctl, /* ioctl */
+ tcp_v6_init_sock, /* init */
+ tcp_v6_destroy_sock, /* destroy */
+ tcp_shutdown, /* shutdown */
+ tcp_setsockopt, /* setsockopt */
+ tcp_getsockopt, /* getsockopt */
+ tcp_v6_sendmsg, /* sendmsg */
+ tcp_recvmsg, /* recvmsg */
+ NULL, /* bind */
+ tcp_v6_do_rcv, /* backlog_rcv */
+ tcp_v6_hash, /* hash */
+ tcp_v6_unhash, /* unhash */
+ tcp_v6_get_port, /* get_port */
+ 128, /* max_header */
+ 0, /* retransmits */
+ "TCPv6", /* name */
+ 0, /* inuse */
+ 0 /* highestinuse */
+};
+
+static struct inet6_protocol tcpv6_protocol =
+{
+ tcp_v6_rcv, /* TCP handler */
+ tcp_v6_err, /* TCP error control */
+ NULL, /* next */
+ IPPROTO_TCP, /* protocol ID */
+ 0, /* copy */
+ NULL, /* data */
+ "TCPv6" /* name */
+};
+
+__initfunc(void tcpv6_init(void))
+{
+ /* register inet6 protocol */
+ inet6_add_protocol(&tcpv6_protocol);
+}
diff --git a/pfinet/linux-src/net/ipv6/udp_ipv6.c b/pfinet/linux-src/net/ipv6/udp_ipv6.c
new file mode 100644
index 00000000..bbc4f027
--- /dev/null
+++ b/pfinet/linux-src/net/ipv6/udp_ipv6.c
@@ -0,0 +1,1014 @@
+/*
+ * UDP over IPv6
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * YOSHIFUJI Hideaki @USAGI and: Support IPV6_V6ONLY socket option, which
+ * Alexey Kuznetsov allow both IPv4 and IPv6 sockets to bind
+ * a single port at the same time.
+ *
+ * Based on linux/ipv4/udp.c
+ *
+ * $Id: udp_ipv6.c,v 1.3 2007/10/13 01:43:00 stesie Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/sched.h>
+#include <linux/net.h>
+#include <linux/in6.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/ipv6.h>
+#include <linux/icmpv6.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+#include <net/protocol.h>
+#include <net/transp_v6.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+#include <net/ip.h>
+#include <net/udp.h>
+
+#include <net/checksum.h>
+
+struct udp_mib udp_stats_in6;
+
+static __inline__ int udv6_rcv_saddr_equal(struct sock *sk, struct sock *sk2)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ int addr_type = ipv6_addr_type(&np->rcv_saddr);
+
+ if (!sk2->rcv_saddr && !ipv6_only_sock(sk))
+ return 1;
+
+ if (sk2->family == AF_INET6 &&
+ ipv6_addr_any(&sk2->rcv_saddr) &&
+ !(ipv6_only_sock(sk2) && addr_type == IPV6_ADDR_MAPPED))
+ return 1;
+
+ if (addr_type == IPV6_ADDR_ANY &&
+ (!ipv6_only_sock(sk) ||
+ !(sk2->family == AF_INET6 ?
+ ipv6_addr_type(&sk2->rcv_saddr) == IPV6_ADDR_MAPPED : 1)))
+ return 1;
+
+ if (sk2->family == AF_INET6 &&
+ !ipv6_addr_cmp(&sk->rcv_saddr,
+ &sk2->rcv_saddr))
+ return 1;
+
+ if (addr_type == IPV6_ADDR_MAPPED &&
+ !ipv6_only_sock(sk2) &&
+ (!sk2->rcv_saddr ||
+ !sk->rcv_saddr ||
+ sk->rcv_saddr == sk2->rcv_saddr))
+ return 1;
+
+ return 0;
+}
+
+
+/* Grrr, addr_type already calculated by caller, but I don't want
+ * to add some silly "cookie" argument to this method just for that.
+ */
+static int udp_v6_get_port(struct sock *sk, unsigned short snum)
+{
+ SOCKHASH_LOCK();
+ if (snum == 0) {
+ int best_size_so_far, best, result, i;
+
+ if (udp_port_rover > sysctl_local_port_range[1] ||
+ udp_port_rover < sysctl_local_port_range[0])
+ udp_port_rover = sysctl_local_port_range[0];
+ best_size_so_far = 32767;
+ best = result = udp_port_rover;
+ for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
+ struct sock *sk;
+ int size;
+
+ sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)];
+ if (!sk) {
+ if (result > sysctl_local_port_range[1])
+ result = sysctl_local_port_range[0] +
+ ((result - sysctl_local_port_range[0]) &
+ (UDP_HTABLE_SIZE - 1));
+ goto gotit;
+ }
+ size = 0;
+ do {
+ if (++size >= best_size_so_far)
+ goto next;
+ } while ((sk = sk->next) != NULL);
+ best_size_so_far = size;
+ best = result;
+
+ next:
+ (void) 0;
+ }
+ result = best;
+ for(;; result += UDP_HTABLE_SIZE) {
+ if (result > sysctl_local_port_range[1])
+ result = sysctl_local_port_range[0]
+ + ((result - sysctl_local_port_range[0]) &
+ (UDP_HTABLE_SIZE - 1));
+ if (!udp_lport_inuse(result))
+ break;
+ }
+gotit:
+ udp_port_rover = snum = result;
+ } else {
+ struct sock *sk2;
+ int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
+
+ for (sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
+ sk2 != NULL;
+ sk2 = sk2->next) {
+ if (sk2->num == snum &&
+ sk2 != sk &&
+ sk2->bound_dev_if == sk->bound_dev_if &&
+ (!sk2->reuse || !sk->reuse) &&
+ udv6_rcv_saddr_equal(sk, sk2))
+ goto fail;
+ }
+ }
+
+ sk->num = snum;
+ SOCKHASH_UNLOCK();
+ return 0;
+
+fail:
+ SOCKHASH_UNLOCK();
+ return 1;
+}
+
+static void udp_v6_hash(struct sock *sk)
+{
+ struct sock **skp = &udp_hash[sk->num & (UDP_HTABLE_SIZE - 1)];
+
+ SOCKHASH_LOCK();
+ if ((sk->next = *skp) != NULL)
+ (*skp)->pprev = &sk->next;
+ *skp = sk;
+ sk->pprev = skp;
+ SOCKHASH_UNLOCK();
+}
+
+static void udp_v6_unhash(struct sock *sk)
+{
+ SOCKHASH_LOCK();
+ if (sk->pprev) {
+ if (sk->next)
+ sk->next->pprev = sk->pprev;
+ *sk->pprev = sk->next;
+ sk->pprev = NULL;
+ }
+ SOCKHASH_UNLOCK();
+}
+
+static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
+ struct in6_addr *daddr, u16 dport, int dif)
+{
+ struct sock *sk, *result = NULL;
+ unsigned short hnum = ntohs(dport);
+ int badness = -1;
+
+ for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
+ if((sk->num == hnum) &&
+ (sk->family == PF_INET6) &&
+ !(sk->dead && (sk->state == TCP_CLOSE))) {
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ int score = 0;
+ if(sk->dport) {
+ if(sk->dport != sport)
+ continue;
+ score++;
+ }
+ if(!ipv6_addr_any(&np->rcv_saddr)) {
+ if(ipv6_addr_cmp(&np->rcv_saddr, daddr))
+ continue;
+ score++;
+ }
+ if(!ipv6_addr_any(&np->daddr)) {
+ if(ipv6_addr_cmp(&np->daddr, saddr))
+ continue;
+ score++;
+ }
+ if(sk->bound_dev_if) {
+ if(sk->bound_dev_if != dif)
+ continue;
+ score++;
+ }
+ if(score == 4) {
+ result = sk;
+ break;
+ } else if(score > badness) {
+ result = sk;
+ badness = score;
+ }
+ }
+ }
+ return result;
+}
+
+/*
+ *
+ */
+
+int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+{
+ struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct in6_addr *daddr;
+ struct in6_addr saddr;
+ struct dst_entry *dst;
+ struct flowi fl;
+ struct ip6_flowlabel *flowlabel = NULL;
+ int addr_type;
+ int err;
+
+ if (usin->sin6_family == AF_INET) {
+ if (__ipv6_only_sock(sk))
+ return -EAFNOSUPPORT;
+ err = udp_connect(sk, uaddr, addr_len);
+ goto ipv4_connected;
+ }
+
+ if (usin->sin6_family == AF_UNSPEC) {
+ udp_connect(sk, uaddr, addr_len);
+ ipv6_addr_set(&np->daddr, 0, 0, 0, 0);
+ ipv6_addr_set(&np->saddr, 0, 0, 0, 0);
+ ipv6_addr_set(&np->rcv_saddr, 0, 0, 0, 0);
+ return 0;
+ }
+
+ if (addr_len < sizeof(*usin))
+ return(-EINVAL);
+
+ if (usin->sin6_family && usin->sin6_family != AF_INET6)
+ return(-EAFNOSUPPORT);
+
+ fl.fl6_flowlabel = 0;
+ if (np->sndflow) {
+ fl.fl6_flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK;
+ if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
+ flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
+ if (flowlabel == NULL)
+ return -EINVAL;
+ ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst);
+ }
+ }
+
+ addr_type = ipv6_addr_type(&usin->sin6_addr);
+
+ if (addr_type == IPV6_ADDR_ANY) {
+ /*
+ * connect to self
+ */
+ usin->sin6_addr.s6_addr[15] = 0x01;
+ }
+
+ daddr = &usin->sin6_addr;
+
+ if (addr_type == IPV6_ADDR_MAPPED) {
+ struct sockaddr_in sin;
+
+ if (__ipv6_only_sock(sk))
+ return -ENETUNREACH;
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = daddr->s6_addr32[3];
+ sin.sin_port = usin->sin6_port;
+
+ err = udp_connect(sk, (struct sockaddr*) &sin, sizeof(sin));
+
+ipv4_connected:
+ if (err < 0)
+ return err;
+
+ ipv6_addr_set(&np->daddr, 0, 0,
+ __constant_htonl(0x0000ffff),
+ sk->daddr);
+
+ if(ipv6_addr_any(&np->saddr)) {
+ ipv6_addr_set(&np->saddr, 0, 0,
+ __constant_htonl(0x0000ffff),
+ sk->saddr);
+
+ }
+
+ if(ipv6_addr_any(&np->rcv_saddr)) {
+ ipv6_addr_set(&np->rcv_saddr, 0, 0,
+ __constant_htonl(0x0000ffff),
+ sk->rcv_saddr);
+ }
+ return 0;
+ }
+
+ if (addr_type&IPV6_ADDR_LINKLOCAL) {
+ if (addr_len >= sizeof(struct sockaddr_in6) &&
+ usin->sin6_scope_id) {
+ if (sk->bound_dev_if &&
+ sk->bound_dev_if != usin->sin6_scope_id)
+ return(-EINVAL);
+
+ sk->bound_dev_if = usin->sin6_scope_id;
+ if (!sk->bound_dev_if &&
+ (addr_type & IPV6_ADDR_MULTICAST))
+ fl.oif = np->mcast_oif;
+ }
+
+ /* Connect to link-local address requires an interface */
+ if (!sk->bound_dev_if)
+ return(-EINVAL);
+ }
+
+ ipv6_addr_copy(&np->daddr, daddr);
+ np->flow_label = fl.fl6_flowlabel;
+
+ sk->dport = usin->sin6_port;
+
+ /*
+ * Check for a route to destination an obtain the
+ * destination cache for it.
+ */
+
+ fl.proto = IPPROTO_UDP;
+ fl.fl6_dst = &np->daddr;
+ fl.fl6_src = &saddr;
+ fl.oif = sk->bound_dev_if;
+ fl.uli_u.ports.dport = sk->dport;
+ fl.uli_u.ports.sport = sk->sport;
+
+ if (flowlabel) {
+ if (flowlabel->opt && flowlabel->opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) flowlabel->opt->srcrt;
+ fl.fl6_dst = rt0->addr;
+ }
+ } else if (np->opt && np->opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+ fl.fl6_dst = rt0->addr;
+ }
+
+ dst = ip6_route_output(sk, &fl);
+
+ if ((err = dst->error) != 0) {
+ dst_release(dst);
+ fl6_sock_release(flowlabel);
+ return err;
+ }
+
+ ip6_dst_store(sk, dst, fl.fl6_dst);
+
+ /* get the source adddress used in the appropriate device */
+
+ err = ipv6_get_saddr(dst, daddr, &saddr);
+
+ if (err == 0) {
+ if(ipv6_addr_any(&np->saddr))
+ ipv6_addr_copy(&np->saddr, &saddr);
+
+ if(ipv6_addr_any(&np->rcv_saddr)) {
+ ipv6_addr_copy(&np->rcv_saddr, &saddr);
+ sk->rcv_saddr = 0xffffffff;
+ }
+ sk->state = TCP_ESTABLISHED;
+ }
+ fl6_sock_release(flowlabel);
+
+ return err;
+}
+
+static void udpv6_close(struct sock *sk, long timeout)
+{
+ /* See for explanation: raw_close in ipv4/raw.c */
+ sk->state = TCP_CLOSE;
+ udp_v6_unhash(sk);
+ sk->dead = 1;
+ destroy_sock(sk);
+}
+
+#ifndef HAVE_CSUM_COPY_USER
+#undef CONFIG_UDP_DELAY_CSUM
+#endif
+
+/*
+ * This should be easy, if there is something there we
+ * return it, otherwise we block.
+ */
+
+int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
+ int noblock, int flags, int *addr_len)
+{
+ struct sk_buff *skb;
+ int copied, err;
+
+ if (addr_len)
+ *addr_len=sizeof(struct sockaddr_in6);
+
+ if (flags & MSG_ERRQUEUE)
+ return ipv6_recv_error(sk, msg, len);
+
+ skb = skb_recv_datagram(sk, flags, noblock, &err);
+ if (!skb)
+ goto out;
+
+ copied = skb->len - sizeof(struct udphdr);
+ if (copied > len) {
+ copied = len;
+ msg->msg_flags |= MSG_TRUNC;
+ }
+
+#ifndef CONFIG_UDP_DELAY_CSUM
+ err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr),
+ msg->msg_iov, copied);
+#else
+ if (skb->ip_summed==CHECKSUM_UNNECESSARY) {
+ err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
+ copied);
+ } else if (copied > msg->msg_iov[0].iov_len || (msg->msg_flags&MSG_TRUNC)) {
+ if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum))) {
+ /* Error for blocking case is chosen to masquerade
+ as some normal condition.
+ */
+ err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
+ udp_stats_in6.UdpInErrors++;
+ goto out_free;
+ }
+ err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
+ copied);
+ } else {
+ unsigned int csum = csum_partial(skb->h.raw, sizeof(struct udphdr), skb->csum);
+
+ err = 0;
+ csum = csum_and_copy_to_user((char*)&skb->h.uh[1], msg->msg_iov[0].iov_base, copied, csum, &err);
+ if (err)
+ goto out_free;
+ if ((unsigned short)csum_fold(csum)) {
+ /* Error for blocking case is chosen to masquerade
+ as some normal condition.
+ */
+ err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
+ udp_stats_in6.UdpInErrors++;
+ goto out_free;
+ }
+ }
+#endif
+ if (err)
+ goto out_free;
+
+ sk->stamp=skb->stamp;
+
+ /* Copy the address. */
+ if (msg->msg_name) {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *) msg->msg_name;
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = skb->h.uh->source;
+ sin6->sin6_flowinfo = 0;
+ sin6->sin6_scope_id = 0;
+
+ if (skb->protocol == __constant_htons(ETH_P_IP)) {
+ ipv6_addr_set(&sin6->sin6_addr, 0, 0,
+ __constant_htonl(0xffff), skb->nh.iph->saddr);
+ if (sk->ip_cmsg_flags)
+ ip_cmsg_recv(msg, skb);
+ } else {
+ memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr,
+ sizeof(struct in6_addr));
+ if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
+ sin6->sin6_scope_id =
+ ((struct inet6_skb_parm *) skb->cb)->iif;
+
+ if (sk->net_pinfo.af_inet6.rxopt.all)
+ datagram_recv_ctl(sk, msg, skb);
+ }
+ }
+ err = copied;
+
+out_free:
+ skb_free_datagram(sk, skb);
+out:
+ return err;
+}
+
+void udpv6_err(struct sk_buff *skb, struct ipv6hdr *hdr,
+ struct inet6_skb_parm *opt,
+ int type, int code, unsigned char *buff, __u32 info)
+{
+ struct device *dev = skb->dev;
+ struct in6_addr *saddr = &hdr->saddr;
+ struct in6_addr *daddr = &hdr->daddr;
+ struct sock *sk;
+ struct udphdr *uh;
+ int err;
+
+ if (buff + sizeof(struct udphdr) > skb->tail)
+ return;
+
+ uh = (struct udphdr *) buff;
+
+ sk = udp_v6_lookup(daddr, uh->dest, saddr, uh->source, dev->ifindex);
+
+ if (sk == NULL)
+ return;
+
+ if (!icmpv6_err_convert(type, code, &err) &&
+ !sk->net_pinfo.af_inet6.recverr)
+ return;
+
+ if (sk->bsdism && sk->state!=TCP_ESTABLISHED)
+ return;
+
+ if (sk->net_pinfo.af_inet6.recverr)
+ ipv6_icmp_error(sk, skb, err, uh->dest, ntohl(info), (u8 *)(uh+1));
+
+ sk->err = err;
+ sk->error_report(sk);
+}
+
+static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
+{
+#if defined(CONFIG_FILTER) && defined(CONFIG_UDP_DELAY_CSUM)
+ if (sk->filter && skb->ip_summed != CHECKSUM_UNNECESSARY) {
+ if ((unsigned short)csum_fold(csum_partial(skb->h.raw, skb->len, skb->csum))) {
+ udp_stats_in6.UdpInErrors++;
+ ipv6_statistics.Ip6InDiscards++;
+ kfree_skb(skb);
+ return 0;
+ }
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ }
+#endif
+ if (sock_queue_rcv_skb(sk,skb)<0) {
+ udp_stats_in6.UdpInErrors++;
+ ipv6_statistics.Ip6InDiscards++;
+ kfree_skb(skb);
+ return 0;
+ }
+ ipv6_statistics.Ip6InDelivers++;
+ udp_stats_in6.UdpInDatagrams++;
+ return 0;
+}
+
+static __inline__ int inet6_mc_check(struct sock *sk, struct in6_addr *addr)
+{
+ struct ipv6_mc_socklist *mc;
+
+ for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
+ if (ipv6_addr_cmp(&mc->addr, addr) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static struct sock *udp_v6_mcast_next(struct sock *sk,
+ u16 loc_port, struct in6_addr *loc_addr,
+ u16 rmt_port, struct in6_addr *rmt_addr,
+ int dif)
+{
+ struct sock *s = sk;
+ unsigned short num = ntohs(loc_port);
+ for(; s; s = s->next) {
+ if((s->num == num) &&
+ !(s->dead && (s->state == TCP_CLOSE))) {
+ struct ipv6_pinfo *np = &s->net_pinfo.af_inet6;
+ if(s->dport) {
+ if(s->dport != rmt_port)
+ continue;
+ }
+ if(!ipv6_addr_any(&np->daddr) &&
+ ipv6_addr_cmp(&np->daddr, rmt_addr))
+ continue;
+
+ if (s->bound_dev_if && s->bound_dev_if != dif)
+ continue;
+
+ if(!ipv6_addr_any(&np->rcv_saddr)) {
+ if(ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
+ return s;
+ }
+ if(!inet6_mc_check(s, loc_addr))
+ continue;
+ return s;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Note: called only from the BH handler context,
+ * so we don't need to lock the hashes.
+ */
+static void udpv6_mcast_deliver(struct udphdr *uh,
+ struct in6_addr *saddr, struct in6_addr *daddr,
+ struct sk_buff *skb)
+{
+ struct sock *sk, *sk2;
+ struct sk_buff *buff;
+ int dif;
+
+ sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
+ dif = skb->dev->ifindex;
+ sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif);
+ if (!sk)
+ goto free_skb;
+
+ buff = NULL;
+ sk2 = sk;
+ while((sk2 = udp_v6_mcast_next(sk2->next, uh->dest, saddr,
+ uh->source, daddr, dif))) {
+ if (!buff) {
+ buff = skb_clone(skb, GFP_ATOMIC);
+ if (!buff)
+ continue;
+ }
+ if (sock_queue_rcv_skb(sk2, buff) >= 0)
+ buff = NULL;
+ }
+ if (buff)
+ kfree_skb(buff);
+ if (sock_queue_rcv_skb(sk, skb) < 0) {
+free_skb:
+ kfree_skb(skb);
+ }
+}
+
+int udpv6_rcv(struct sk_buff *skb, unsigned long len)
+{
+ struct sock *sk;
+ struct udphdr *uh;
+ struct device *dev = skb->dev;
+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+ struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+ u32 ulen;
+
+ uh = skb->h.uh;
+ __skb_pull(skb, skb->h.raw - skb->data);
+
+ ulen = ntohs(uh->len);
+
+ /* Check for jumbo payload */
+ if (ulen == 0 && skb->nh.ipv6h->payload_len == 0)
+ ulen = len;
+
+ if (ulen > len || len < sizeof(*uh)) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "UDP: short packet: %d/%ld\n", ulen, len);
+ udp_stats_in6.UdpInErrors++;
+ kfree_skb(skb);
+ return(0);
+ }
+
+ if (uh->check == 0) {
+ /* IPv6 draft-v2 section 8.1 says that we SHOULD log
+ this error. Well, it is reasonable.
+ */
+ if (net_ratelimit())
+ printk(KERN_INFO "IPv6: udp checksum is 0\n");
+ goto discard;
+ }
+
+ skb_trim(skb, ulen);
+
+#ifndef CONFIG_UDP_DELAY_CSUM
+ switch (skb->ip_summed) {
+ case CHECKSUM_NONE:
+ skb->csum = csum_partial((char*)uh, ulen, 0);
+ case CHECKSUM_HW:
+ if (csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum)) {
+ printk(KERN_DEBUG "IPv6: udp checksum error\n");
+ goto discard;
+ }
+ };
+#else
+ if (skb->ip_summed==CHECKSUM_HW) {
+ if (csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum))
+ goto discard;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ } else if (skb->ip_summed != CHECKSUM_UNNECESSARY)
+ skb->csum = ~csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, 0);
+#endif
+
+ len = ulen;
+
+ /*
+ * Multicast receive code
+ */
+ if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST) {
+ udpv6_mcast_deliver(uh, saddr, daddr, skb);
+ return 0;
+ }
+
+ /* Unicast */
+
+ /*
+ * check socket cache ... must talk to Alan about his plans
+ * for sock caches... i'll skip this for now.
+ */
+
+ sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest, dev->ifindex);
+
+ if (sk == NULL) {
+#ifdef CONFIG_UDP_DELAY_CSUM
+ if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
+ (unsigned short)csum_fold(csum_partial((char*)uh, len, skb->csum)))
+ goto discard;
+#endif
+ udp_stats_in6.UdpNoPorts++;
+
+ icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev);
+
+ kfree_skb(skb);
+ return(0);
+ }
+
+ /* deliver */
+
+ udpv6_queue_rcv_skb(sk, skb);
+
+ return(0);
+
+discard:
+ udp_stats_in6.UdpInErrors++;
+ kfree_skb(skb);
+ return(0);
+}
+
+/*
+ * Sending
+ */
+
+struct udpv6fakehdr
+{
+ struct udphdr uh;
+ struct iovec *iov;
+ __u32 wcheck;
+ __u32 pl_len;
+ struct in6_addr *daddr;
+};
+
+/*
+ * with checksum
+ */
+
+static int udpv6_getfrag(const void *data, struct in6_addr *addr,
+ char *buff, unsigned int offset, unsigned int len)
+{
+ struct udpv6fakehdr *udh = (struct udpv6fakehdr *) data;
+ char *dst;
+ int final = 0;
+ int clen = len;
+
+ dst = buff;
+
+ if (offset) {
+ offset -= sizeof(struct udphdr);
+ } else {
+ dst += sizeof(struct udphdr);
+ final = 1;
+ clen -= sizeof(struct udphdr);
+ }
+
+ if (csum_partial_copy_fromiovecend(dst, udh->iov, offset,
+ clen, &udh->wcheck))
+ return -EFAULT;
+
+ if (final) {
+ struct in6_addr *daddr;
+
+ udh->wcheck = csum_partial((char *)udh, sizeof(struct udphdr),
+ udh->wcheck);
+
+ if (udh->daddr) {
+ daddr = udh->daddr;
+ } else {
+ /*
+ * use packet destination address
+ * this should improve cache locality
+ */
+ daddr = addr + 1;
+ }
+ udh->uh.check = csum_ipv6_magic(addr, daddr,
+ udh->pl_len, IPPROTO_UDP,
+ udh->wcheck);
+ if (udh->uh.check == 0)
+ udh->uh.check = -1;
+
+ memcpy(buff, udh, sizeof(struct udphdr));
+ }
+ return 0;
+}
+
+static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen)
+{
+ struct ipv6_txoptions opt_space;
+ struct udpv6fakehdr udh;
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) msg->msg_name;
+ struct ipv6_txoptions *opt = NULL;
+ struct ip6_flowlabel *flowlabel = NULL;
+ struct flowi fl;
+ int addr_len = msg->msg_namelen;
+ struct in6_addr *daddr;
+ int len = ulen + sizeof(struct udphdr);
+ int addr_type;
+ int hlimit = -1;
+
+ int err;
+
+ /* Rough check on arithmetic overflow,
+ better check is made in ip6_build_xmit
+ */
+ if (ulen < 0 || ulen > INT_MAX - sizeof(struct udphdr))
+ return -EMSGSIZE;
+
+ if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
+ return(-EINVAL);
+
+ fl.fl6_flowlabel = 0;
+
+ if (sin6) {
+ if (sin6->sin6_family == AF_INET) {
+ if (__ipv6_only_sock(sk))
+ return -ENETUNREACH;
+ return udp_sendmsg(sk, msg, ulen);
+ }
+
+ if (addr_len < sizeof(*sin6))
+ return(-EINVAL);
+
+ if (sin6->sin6_family && sin6->sin6_family != AF_INET6)
+ return(-EINVAL);
+
+ if (sin6->sin6_port == 0)
+ return(-EINVAL);
+
+ udh.uh.dest = sin6->sin6_port;
+ daddr = &sin6->sin6_addr;
+
+ if (np->sndflow) {
+ fl.fl6_flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK;
+ if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
+ flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
+ if (flowlabel == NULL)
+ return -EINVAL;
+ daddr = &flowlabel->dst;
+ }
+ }
+
+ /* Otherwise it will be difficult to maintain sk->dst_cache. */
+ if (sk->state == TCP_ESTABLISHED &&
+ !ipv6_addr_cmp(daddr, &sk->net_pinfo.af_inet6.daddr))
+ daddr = &sk->net_pinfo.af_inet6.daddr;
+
+ if (addr_len >= sizeof(struct sockaddr_in6) &&
+ sin6->sin6_scope_id &&
+ ipv6_addr_type(daddr)&IPV6_ADDR_LINKLOCAL)
+ fl.oif = sin6->sin6_scope_id;
+ } else {
+ if (sk->state != TCP_ESTABLISHED)
+ return(-ENOTCONN);
+
+ udh.uh.dest = sk->dport;
+ daddr = &sk->net_pinfo.af_inet6.daddr;
+ fl.fl6_flowlabel = np->flow_label;
+ }
+
+ addr_type = ipv6_addr_type(daddr);
+
+ if (addr_type == IPV6_ADDR_MAPPED) {
+ struct sockaddr_in sin;
+
+ if (__ipv6_only_sock(sk))
+ return -ENETUNREACH;
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = daddr->s6_addr32[3];
+ sin.sin_port = udh.uh.dest;
+ msg->msg_name = (struct sockaddr *)(&sin);
+ msg->msg_namelen = sizeof(sin);
+ fl6_sock_release(flowlabel);
+
+ return udp_sendmsg(sk, msg, ulen);
+ }
+
+ udh.daddr = NULL;
+ fl.oif = sk->bound_dev_if;
+ fl.fl6_src = NULL;
+
+ if (msg->msg_controllen) {
+ opt = &opt_space;
+ memset(opt, 0, sizeof(struct ipv6_txoptions));
+
+ err = datagram_send_ctl(msg, &fl, opt, &hlimit);
+ if (err < 0) {
+ fl6_sock_release(flowlabel);
+ return err;
+ }
+ if ((fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) {
+ flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
+ if (flowlabel == NULL)
+ return -EINVAL;
+ }
+ if (!(opt->opt_nflen|opt->opt_flen))
+ opt = NULL;
+ }
+ if (opt == NULL)
+ opt = np->opt;
+ if (flowlabel)
+ opt = fl6_merge_options(&opt_space, flowlabel, opt);
+ if (opt && opt->srcrt)
+ udh.daddr = daddr;
+
+ udh.uh.source = sk->sport;
+ udh.uh.len = len < 0x10000 ? htons(len) : 0;
+ udh.uh.check = 0;
+ udh.iov = msg->msg_iov;
+ udh.wcheck = 0;
+ udh.pl_len = len;
+
+ fl.proto = IPPROTO_UDP;
+ fl.fl6_dst = daddr;
+ fl.uli_u.ports.dport = udh.uh.dest;
+ fl.uli_u.ports.sport = udh.uh.source;
+
+ err = ip6_build_xmit(sk, udpv6_getfrag, &udh, &fl, len, opt, hlimit,
+ msg->msg_flags);
+
+ fl6_sock_release(flowlabel);
+
+ if (err < 0)
+ return err;
+
+ udp_stats_in6.UdpOutDatagrams++;
+ return ulen;
+}
+
+static struct inet6_protocol udpv6_protocol =
+{
+ udpv6_rcv, /* UDP handler */
+ udpv6_err, /* UDP error control */
+ NULL, /* next */
+ IPPROTO_UDP, /* protocol ID */
+ 0, /* copy */
+ NULL, /* data */
+ "UDPv6" /* name */
+};
+
+#ifdef _HURD_
+#define udp_ioctl 0
+#endif
+
+struct proto udpv6_prot = {
+ (struct sock *)&udpv6_prot, /* sklist_next */
+ (struct sock *)&udpv6_prot, /* sklist_prev */
+ udpv6_close, /* close */
+ udpv6_connect, /* connect */
+ NULL, /* accept */
+ NULL, /* retransmit */
+ NULL, /* write_wakeup */
+ NULL, /* read_wakeup */
+ datagram_poll, /* poll */
+ udp_ioctl, /* ioctl */
+ NULL, /* init */
+ inet6_destroy_sock, /* destroy */
+ NULL, /* shutdown */
+ ipv6_setsockopt, /* setsockopt */
+ ipv6_getsockopt, /* getsockopt */
+ udpv6_sendmsg, /* sendmsg */
+ udpv6_recvmsg, /* recvmsg */
+ NULL, /* bind */
+ udpv6_queue_rcv_skb, /* backlog_rcv */
+ udp_v6_hash, /* hash */
+ udp_v6_unhash, /* unhash */
+ udp_v6_get_port, /* get_port */
+ 128, /* max_header */
+ 0, /* retransmits */
+ "UDP", /* name */
+ 0, /* inuse */
+ 0 /* highestinuse */
+};
+
+void __init udpv6_init(void)
+{
+ inet6_add_protocol(&udpv6_protocol);
+}
diff --git a/pfinet/loopback.c b/pfinet/loopback.c
new file mode 100644
index 00000000..e15e4265
--- /dev/null
+++ b/pfinet/loopback.c
@@ -0,0 +1,137 @@
+/* Loopback "device" for pfinet
+ Copyright (C) 1996,98,2000 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "pfinet.h"
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <linux/if_ether.h> /* For the statistics structure. */
+#include <linux/if_arp.h> /* For ARPHRD_ETHER */
+
+#define LOOPBACK_MTU (vm_page_size - 172)
+
+/*
+ * The higher levels take care of making this non-reentrant (it's
+ * called with bh's disabled).
+ */
+static int loopback_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct net_device_stats *stats = (struct net_device_stats *)dev->priv;
+
+ /*
+ * Take this out if the debug says its ok
+ */
+
+ if (skb == NULL || dev == NULL)
+ printk(KERN_DEBUG "loopback fed NULL data - splat\n");
+
+ /*
+ * Optimise so buffers with skb->free=1 are not copied but
+ * instead are lobbed from tx queue to rx queue
+ */
+
+ if(atomic_read(&skb->users) != 1)
+ {
+ struct sk_buff *skb2=skb;
+ skb=skb_clone(skb, GFP_ATOMIC); /* Clone the buffer */
+ if(skb==NULL) {
+ kfree_skb(skb2);
+ return 0;
+ }
+ kfree_skb(skb2);
+ }
+ else
+ skb_orphan(skb);
+
+ skb->protocol=eth_type_trans(skb,dev);
+ skb->dev=dev;
+#ifndef LOOPBACK_MUST_CHECKSUM
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+#endif
+
+ /*
+ * Calling netif_rx() requires locking net_bh_lock, which
+ * has already been done since this function is called by
+ * the net_bh worker thread.
+ */
+
+ netif_rx(skb);
+
+ stats->rx_bytes+=skb->len;
+ stats->tx_bytes+=skb->len;
+ stats->rx_packets++;
+ stats->tx_packets++;
+
+ return(0);
+}
+
+static struct net_device_stats *get_stats(struct device *dev)
+{
+ return (struct net_device_stats *)dev->priv;
+}
+
+static int loopback_open(struct device *dev)
+{
+ dev->flags|=IFF_LOOPBACK;
+ return 0;
+}
+
+/* Initialize the rest of the LOOPBACK device. */
+static int loopback_init(struct device *dev)
+{
+ dev->mtu = LOOPBACK_MTU;
+ dev->tbusy = 0;
+ dev->hard_start_xmit = loopback_xmit;
+ dev->hard_header = eth_header;
+ dev->hard_header_cache = eth_header_cache;
+ dev->header_cache_update= eth_header_cache_update;
+ dev->hard_header_len = ETH_HLEN; /* 14 */
+ dev->addr_len = ETH_ALEN; /* 6 */
+ dev->tx_queue_len = 0;
+ dev->type = ARPHRD_LOOPBACK; /* 0x0001 */
+ dev->rebuild_header = eth_rebuild_header;
+ dev->open = loopback_open;
+ dev->flags = IFF_LOOPBACK;
+ dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct net_device_stats));
+ dev->get_stats = get_stats;
+
+ /*
+ * Fill in the generic fields of the device structure.
+ */
+
+ dev_init_buffers(dev);
+
+ return(0);
+}
+
+
+struct device loopback_dev = { name: "lo", init: &loopback_init, };
+
+/* It is important magic that this is the first thing on the list. */
+struct device *dev_base = &loopback_dev;
diff --git a/pfinet/main.c b/pfinet/main.c
new file mode 100644
index 00000000..5b0262fe
--- /dev/null
+++ b/pfinet/main.c
@@ -0,0 +1,495 @@
+/*
+ Copyright (C) 1995,96,97,99,2000,02,07 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "pfinet.h"
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <error.h>
+#include <argp.h>
+#include <hurd/startup.h>
+#include <string.h>
+#include <fcntl.h>
+#include <version.h>
+#include <pids.h>
+
+/* Include Hurd's errno.h file, but don't include glue-include/hurd/errno.h,
+ since it #undef's the errno macro. */
+#define _HACK_ERRNO_H
+#include <errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/inet.h>
+
+static void pfinet_activate_ipv6 (void);
+
+/* devinet.c */
+extern error_t configure_device (struct device *dev,
+ uint32_t addr, uint32_t netmask,
+ uint32_t peer, uint32_t broadcast);
+
+/* addrconf.c */
+extern int addrconf_notify(struct notifier_block *this, unsigned long event,
+ void * data);
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid;
+int trivfs_support_read = 1;
+int trivfs_support_write = 1;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = O_READ | O_WRITE;
+
+struct port_class *trivfs_protid_portclasses[2];
+int trivfs_protid_nportclasses = 2;
+
+struct port_class *trivfs_cntl_portclasses[2];
+int trivfs_cntl_nportclasses = 2;
+
+/* Which portclass to install on the bootstrap port, default to IPv4. */
+int pfinet_bootstrap_portclass = PORTCLASS_INET;
+
+struct port_class *shutdown_notify_class;
+
+const char *argp_program_version = STANDARD_HURD_VERSION (pfinet);
+
+/* Option parser. */
+extern struct argp pfinet_argp;
+
+#include "io_S.h"
+#include "socket_S.h"
+#include "pfinet_S.h"
+#include "iioctl_S.h"
+#include "startup_notify_S.h"
+
+int
+pfinet_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ struct port_info *pi;
+
+ /* We have several classes in one bucket, which need to be demuxed
+ differently. */
+ pi = ports_lookup_port(pfinet_bucket, inp->msgh_local_port, socketport_class);
+
+ if (pi)
+ {
+ ports_port_deref (pi);
+
+ mig_routine_t routine;
+ if ((routine = io_server_routine (inp)) ||
+ (routine = socket_server_routine (inp)) ||
+ (routine = pfinet_server_routine (inp)) ||
+ (routine = iioctl_server_routine (inp)) ||
+ (routine = NULL, trivfs_demuxer (inp, outp)) ||
+ (routine = startup_notify_server_routine (inp)))
+ {
+ if (routine)
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+ else
+ {
+ mig_routine_t routine;
+ if ((routine = socket_server_routine (inp)) ||
+ (routine = pfinet_server_routine (inp)) ||
+ (routine = iioctl_server_routine (inp)) ||
+ (routine = NULL, trivfs_demuxer (inp, outp)) ||
+ (routine = startup_notify_server_routine (inp)))
+ {
+ if (routine)
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+}
+
+/* The system is going down; destroy all the extant port rights. That
+ will cause net channels and such to close promptly. */
+error_t
+S_startup_dosync (mach_port_t handle)
+{
+ struct port_info *inpi = ports_lookup_port (pfinet_bucket, handle,
+ shutdown_notify_class);
+
+ if (!inpi)
+ return EOPNOTSUPP;
+
+ ports_class_iterate (socketport_class, ports_destroy_right);
+ return 0;
+}
+
+void
+sigterm_handler (int signo)
+{
+ ports_class_iterate (socketport_class, ports_destroy_right);
+ sleep (10);
+ signal (SIGTERM, SIG_DFL);
+ raise (SIGTERM);
+}
+
+void
+arrange_shutdown_notification ()
+{
+ error_t err;
+ mach_port_t initport, notify;
+ process_t procserver;
+ struct port_info *pi;
+
+ shutdown_notify_class = ports_create_class (0, 0);
+
+ signal (SIGTERM, sigterm_handler);
+
+ /* Arrange to get notified when the system goes down,
+ but if we fail for some reason, just silently give up. No big deal. */
+
+ err = ports_create_port (shutdown_notify_class, pfinet_bucket,
+ sizeof (struct port_info), &pi);
+ if (err)
+ return;
+
+ procserver = getproc ();
+ if (!procserver)
+ return;
+
+ err = proc_getmsgport (procserver, HURD_PID_STARTUP, &initport);
+ mach_port_deallocate (mach_task_self (), procserver);
+ if (err)
+ return;
+
+ notify = ports_get_send_right (pi);
+ ports_port_deref (pi);
+ startup_request_notification (initport, notify,
+ MACH_MSG_TYPE_MAKE_SEND,
+ program_invocation_short_name);
+ mach_port_deallocate (mach_task_self (), notify);
+ mach_port_deallocate (mach_task_self (), initport);
+}
+
+
+/* Return an open device called NAME. If NAME is 0, and there is a single
+ active device, it is returned, otherwise an error. */
+error_t
+find_device (char *name, struct device **device)
+{
+ struct device *dev = dev_base;
+ char *base_name;
+
+ /* Skip loopback interface. */
+ assert (dev);
+ dev = dev->next;
+
+ if (!name)
+ {
+ if (dev)
+ {
+ if (dev->next)
+ return EBUSY; /* XXXACK */
+ else
+ {
+ *device = dev;
+ return 0;
+ }
+ }
+ else
+ return ENXIO; /* XXX */
+ }
+
+ for (; dev; dev = dev->next)
+ if (strcmp (dev->name, name) == 0)
+ {
+ *device = dev;
+ return 0;
+ }
+
+ base_name = strrchr(name, '/');
+ if (base_name)
+ base_name++;
+ else
+ base_name = name;
+
+ if (strncmp(base_name, "tun", 3) == 0)
+ setup_tunnel_device (name, device);
+ else if (strncmp(base_name, "dummy", 5) == 0)
+ setup_dummy_device (name, device);
+ else
+ setup_ethernet_device (name, device);
+
+ /* Turn on device. */
+ dev_open (*device);
+
+ return 0;
+}
+
+/* Call FUN with each active device. If a call to FUN returns a
+ non-zero value, this function will return immediately. Otherwise 0 is
+ returned. */
+error_t
+enumerate_devices (error_t (*fun) (struct device *dev))
+{
+ error_t err;
+ struct device *dev = dev_base;
+
+ /* Skip loopback device. */
+ assert (dev);
+ dev = dev->next;
+
+ for (; dev; dev = dev->next)
+ {
+ err = (*fun) (dev);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+extern void sk_init (void), skb_init (void);
+extern int net_dev_init (void);
+extern void inet6_proto_init (struct net_proto *pro);
+
+int
+main (int argc,
+ char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct stat st;
+ pthread_t thread;
+
+ pfinet_bucket = ports_create_bucket ();
+ addrport_class = ports_create_class (clean_addrport, 0);
+ socketport_class = ports_create_class (clean_socketport, 0);
+ trivfs_fsid = getpid ();
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &fsys_identity);
+
+ /* Generic initialization */
+
+ init_time ();
+ ethernet_initialize ();
+ err = pthread_create (&thread, NULL, net_bh_worker, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+
+ pthread_mutex_lock (&global_lock);
+
+ prepare_current (1); /* Set up to call into Linux initialization. */
+
+ sk_init ();
+#ifdef SLAB_SKB
+ skb_init ();
+#endif
+ inet_proto_init (0);
+
+ /* This initializes the Linux network device layer, including
+ initializing each device on the `dev_base' list. For us,
+ that means just loopback_dev, which will get fully initialized now.
+ After this, we can use `register_netdevice' for new interfaces. */
+ net_dev_init ();
+
+ /* ifconfig lo up 127.0.0.1 netmask 0xff000000 */
+ configure_device (&loopback_dev,
+ htonl (INADDR_LOOPBACK), htonl (IN_CLASSA_NET),
+ htonl (INADDR_NONE), htonl (INADDR_NONE));
+
+ pthread_mutex_unlock (&global_lock);
+
+ /* Parse options. When successful, this configures the interfaces
+ before returning; to do so, it will acquire the global_lock.
+ (And when not successful, it never returns.) */
+ argp_parse (&pfinet_argp, argc, argv, 0,0,0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+
+ pfinet_owner = pfinet_group = 0;
+
+ if (bootstrap != MACH_PORT_NULL) {
+ /* Create portclass to install on the bootstrap port. */
+ if(trivfs_protid_portclasses[pfinet_bootstrap_portclass]
+ != MACH_PORT_NULL)
+ error(1, 0, "No portclass left to assign to bootstrap port");
+
+#ifdef CONFIG_IPV6
+ if (pfinet_bootstrap_portclass == PORTCLASS_INET6)
+ pfinet_activate_ipv6 ();
+#endif
+
+ trivfs_protid_portclasses[pfinet_bootstrap_portclass] =
+ ports_create_class (trivfs_clean_protid, 0);
+ trivfs_cntl_portclasses[pfinet_bootstrap_portclass] =
+ ports_create_class (trivfs_clean_cntl, 0);
+
+ /* Talk to parent and link us in. */
+ err = trivfs_startup (bootstrap, 0,
+ trivfs_cntl_portclasses[pfinet_bootstrap_portclass],
+ pfinet_bucket, trivfs_protid_portclasses
+ [pfinet_bootstrap_portclass], pfinet_bucket,
+ &pfinetctl);
+
+ if (err)
+ error (1, err, "contacting parent");
+
+ /* Initialize status from underlying node. */
+ err = io_stat (pfinetctl->underlying, &st);
+ if (! err)
+ {
+ pfinet_owner = st.st_uid;
+ pfinet_group = st.st_gid;
+ }
+ }
+ else { /* no bootstrap port. */
+ int i;
+ /* Check that at least one portclass has been bound,
+ error out otherwise. */
+ for (i = 0; i < trivfs_protid_nportclasses; i ++)
+ if (trivfs_protid_portclasses[i] != MACH_PORT_NULL)
+ break;
+
+ if (i == trivfs_protid_nportclasses)
+ error (1, 0, "should be started as a translator.\n");
+ }
+
+ /* Ask init to tell us when the system is going down,
+ so we can try to be friendly to our correspondents on the network. */
+ arrange_shutdown_notification ();
+
+ /* Launch */
+ ports_manage_port_operations_multithread (pfinet_bucket,
+ pfinet_demuxer,
+ 30 * 1000, 2 * 60 * 1000, 0);
+ return 0;
+}
+
+#ifdef CONFIG_IPV6
+static void
+pfinet_activate_ipv6 (void)
+{
+ inet6_proto_init (0);
+
+ /* Since we're registering the protocol after the devices have been
+ initialized, we need to care for the linking by ourselves. */
+ struct device *dev = dev_base;
+
+ if (dev)
+ do
+ {
+ if (!(dev->flags & IFF_UP))
+ continue;
+
+ addrconf_notify (NULL, NETDEV_REGISTER, dev);
+ addrconf_notify (NULL, NETDEV_UP, dev);
+ }
+ while ((dev = dev->next));
+}
+#endif /* CONFIG_IPV6 */
+
+void
+pfinet_bind (int portclass, const char *name)
+{
+ struct trivfs_control *cntl;
+ error_t err = 0;
+ mach_port_t right;
+ file_t file = file_name_lookup (name, O_CREAT|O_NOTRANS, 0666);
+
+ if (file == MACH_PORT_NULL)
+ err = errno;
+
+ if (! err) {
+ if (trivfs_protid_portclasses[portclass] != MACH_PORT_NULL)
+ error (1, 0, "Cannot bind one protocol to multiple nodes.\n");
+
+#ifdef CONFIG_IPV6
+ if (portclass == PORTCLASS_INET6)
+ pfinet_activate_ipv6 ();
+#endif
+
+ trivfs_protid_portclasses[portclass] =
+ ports_create_class (trivfs_clean_protid, 0);
+ trivfs_cntl_portclasses[portclass] =
+ ports_create_class (trivfs_clean_cntl, 0);
+
+ err = trivfs_create_control (file, trivfs_cntl_portclasses[portclass],
+ pfinet_bucket,
+ trivfs_protid_portclasses[portclass],
+ pfinet_bucket, &cntl);
+ }
+
+ if (! err)
+ {
+ right = ports_get_send_right (cntl);
+ err = file_set_translator (file, 0, FS_TRANS_EXCL | FS_TRANS_SET,
+ 0, 0, 0, right, MACH_MSG_TYPE_COPY_SEND);
+ mach_port_deallocate (mach_task_self (), right);
+ }
+
+ if (err)
+ error (1, err, "%s", name);
+
+ ports_port_deref (cntl);
+
+}
+
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred,
+ struct stat *st)
+{
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+ if (flags & FSYS_GOAWAY_FORCE)
+ exit (0);
+ else
+ {
+ /* Stop new requests. */
+ ports_inhibit_class_rpcs (trivfs_cntl_portclasses[0]);
+ ports_inhibit_class_rpcs (trivfs_protid_portclasses[0]);
+ ports_inhibit_class_rpcs (socketport_class);
+
+ if (ports_count_class (socketport_class) != 0)
+ {
+ /* We won't go away, so start things going again... */
+ ports_enable_class (socketport_class);
+ ports_resume_class_rpcs (trivfs_cntl_portclasses[0]);
+ ports_resume_class_rpcs (trivfs_protid_portclasses[0]);
+
+ return EBUSY;
+ }
+
+ /* There are no sockets, so we can die without breaking anybody
+ too badly. We don't let user ports on the /servers/socket/2
+ file keep us alive because those get cached in every process
+ that ever makes a PF_INET socket, libc copes with getting
+ MACH_SEND_INVALID_DEST and looking up the new translator. */
+ exit (0);
+ }
+}
diff --git a/pfinet/mapped-time.h b/pfinet/mapped-time.h
new file mode 100644
index 00000000..bcbfc6d4
--- /dev/null
+++ b/pfinet/mapped-time.h
@@ -0,0 +1,30 @@
+#ifndef _MAPPED_TIME_H_
+#define _MAPPED_TIME_H_
+
+#include <maptime.h>
+
+#define HZ 100
+
+extern volatile struct mapped_time_value *mapped_time;
+extern long long root_jiffies;
+
+extern inline int
+read_mapped_secs ()
+{
+ return mapped_time->seconds;
+}
+
+extern inline int
+fetch_jiffies ()
+{
+ struct timeval tv;
+ long long j;
+
+ maptime_read (mapped_time, &tv);
+
+ j = (long long) tv.tv_sec * HZ + ((long long) tv.tv_usec * HZ) / 1000000;
+ return j - root_jiffies;
+}
+
+
+#endif
diff --git a/pfinet/mig-decls.h b/pfinet/mig-decls.h
new file mode 100644
index 00000000..ec8fb232
--- /dev/null
+++ b/pfinet/mig-decls.h
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 1995,96,2000 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __PFINET_MIG_DECLS_H__
+#define __PFINET_MIG_DECLS_H__
+
+#include "pfinet.h"
+
+/* MiG bogosity */
+typedef struct sock_user *sock_user_t;
+typedef struct sock_addr *sock_addr_t;
+
+static inline struct sock_user * __attribute__ ((unused))
+begin_using_socket_port (mach_port_t port)
+{
+ return ports_lookup_port (pfinet_bucket, port, socketport_class);
+}
+
+static inline void __attribute__ ((unused))
+end_using_socket_port (struct sock_user *user)
+{
+ if (user)
+ ports_port_deref (user);
+}
+
+static inline struct sock_addr * __attribute__ ((unused))
+begin_using_sockaddr_port (mach_port_t port)
+{
+ return ports_lookup_port (pfinet_bucket, port, addrport_class);
+}
+
+static inline void __attribute__ ((unused))
+end_using_sockaddr_port (struct sock_addr *addr)
+{
+ if (addr)
+ ports_port_deref (addr);
+}
+
+#endif /* __PFINET_MIG_DECLS_H__ */
diff --git a/pfinet/mig-mutate.h b/pfinet/mig-mutate.h
new file mode 100644
index 00000000..0a1eeb84
--- /dev/null
+++ b/pfinet/mig-mutate.h
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* Only CPP macro definitions should go in this file. */
+
+#define IO_SELECT_REPLY_PORT
+
+#define IO_INTRAN sock_user_t begin_using_socket_port (io_t)
+#define IO_DESTRUCTOR end_using_socket_port (sock_user_t)
+#define IO_IMPORTS import "mig-decls.h";
+#define IIOCTL_IMPORTS import "mig-decls.h";
+
+#define SOCKET_INTRAN sock_user_t begin_using_socket_port (socket_t)
+#define SOCKET_DESTRUCTOR end_using_socket_port (sock_user_t)
+#define SOCKET_IMPORTS \
+ import "mig-decls.h"; \
+ import "../libtrivfs/mig-decls.h"; \
+
+#define ADDRPORT_INTRAN sock_addr_t begin_using_sockaddr_port (addr_port_t)
+#define ADDRPORT_DESTRUCTOR end_using_sockaddr_port (sock_addr_t)
+
+#define PF_INTRAN trivfs_protid_t trivfs_begin_using_protid (pf_t)
+#define PF_DESTRUCTOR trivfs_end_using_protid (trivfs_protid_t)
diff --git a/pfinet/misc.c b/pfinet/misc.c
new file mode 100644
index 00000000..d0987ebd
--- /dev/null
+++ b/pfinet/misc.c
@@ -0,0 +1,69 @@
+/*
+ Copyright (C) 1995,96,2000 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "pfinet.h"
+#include <string.h>
+#include <stddef.h>
+
+#include <linux/socket.h>
+#include <linux/net.h>
+
+
+/* Create a sockaddr port. Fill in *ADDR and *ADDRTYPE accordingly.
+ The address should come from SOCK; PEER is 0 if we want this socket's
+ name and 1 if we want the peer's name. */
+error_t
+make_sockaddr_port (struct socket *sock,
+ int peer,
+ mach_port_t *addr,
+ mach_msg_type_name_t *addrtype)
+{
+ union { struct sockaddr_storage storage; struct sockaddr sa; } buf;
+ int buflen = sizeof buf;
+ error_t err;
+ struct sock_addr *addrstruct;
+
+ err = (*sock->ops->getname) (sock, &buf.sa, &buflen, peer);
+ if (err)
+ return -err;
+
+ err = ports_create_port (addrport_class, pfinet_bucket,
+ (offsetof (struct sock_addr, address)
+ + buflen), &addrstruct);
+ if (!err)
+ {
+ addrstruct->address.sa_family = buf.sa.sa_family;
+ addrstruct->address.sa_len = buflen;
+ memcpy (addrstruct->address.sa_data, buf.sa.sa_data,
+ buflen - offsetof (struct sockaddr, sa_data));
+ *addr = ports_get_right (addrstruct);
+ *addrtype = MACH_MSG_TYPE_MAKE_SEND;
+ }
+
+ ports_port_deref (addrstruct);
+
+ return 0;
+}
+
+/* Nothing need be done here. */
+void
+clean_addrport (void *arg)
+{
+}
diff --git a/pfinet/options.c b/pfinet/options.c
new file mode 100644
index 00000000..e9b81a9c
--- /dev/null
+++ b/pfinet/options.c
@@ -0,0 +1,570 @@
+/* Pfinet option parsing
+
+ Copyright (C) 1996, 1997, 2000, 2001, 2006, 2007 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <hurd.h>
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "pfinet.h"
+
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/ip.h>
+#include <linux/route.h>
+#include <linux/rtnetlink.h>
+#include <net/route.h>
+#include <net/sock.h>
+#include <net/ip_fib.h>
+#include <net/ip6_fib.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+
+/* Our interface to the set of devices. */
+extern error_t find_device (char *name, struct device **device);
+extern error_t enumerate_devices (error_t (*fun) (struct device *dev));
+
+/* devinet.c */
+extern error_t configure_device (struct device *dev, uint32_t addr,
+ uint32_t netmask, uint32_t peer,
+ uint32_t broadcast);
+extern void inquire_device (struct device *dev, uint32_t *addr,
+ uint32_t *netmask, uint32_t *peer,
+ uint32_t *broadcast);
+
+/* addrconf.c */
+extern struct inet6_dev *ipv6_find_idev (struct device *dev);
+extern int inet6_addr_add (int ifindex, struct in6_addr *pfx, int plen);
+extern int inet6_addr_del (int ifindex, struct in6_addr *pfx, int plen);
+
+
+/* Pfinet options. Used for both startup and runtime. */
+static const struct argp_option options[] =
+{
+ {"interface", 'i', "DEVICE", 0, "Network interface to use", 1},
+ {0,0,0,0,"These apply to a given interface:", 2},
+ {"address", 'a', "ADDRESS", 0, "Set the network address"},
+ {"netmask", 'm', "MASK", 0, "Set the netmask"},
+ {"peer", 'p', "ADDRESS", 0, "Set the peer address"},
+ {"gateway", 'g', "ADDRESS", 0, "Set the default gateway"},
+ {"ipv4", '4', "NAME", 0, "Put active IPv4 translator on NAME"},
+#ifdef CONFIG_IPV6
+ {"ipv6", '6', "NAME", 0, "Put active IPv6 translator on NAME"},
+ {"address6", 'A', "ADDR/LEN",0, "Set the global IPv6 address"},
+ {"gateway6", 'G', "ADDRESS", 0, "Set the IPv6 default gateway"},
+#endif
+ {"shutdown", 's', 0, 0, "Shut it down"},
+ {0}
+};
+
+static const char doc[] = "Interface-specific options before the first \
+interface specification apply to the first following interface; otherwise \
+they apply to the previously specified interface.";
+
+/* Used to describe a particular interface during argument parsing. */
+struct parse_interface
+{
+ /* The network interface in question. */
+ struct device *device;
+
+ /* New values to apply to it. (IPv4) */
+ uint32_t address, netmask, peer, gateway;
+
+#ifdef CONFIG_IPV6
+ /* New IPv6 configuration to apply. */
+ struct inet6_ifaddr address6;
+ struct in6_addr gateway6;
+#endif
+};
+
+/* Used to hold data during argument parsing. */
+struct parse_hook
+{
+ /* A list of specified interfaces and their corresponding options. */
+ struct parse_interface *interfaces;
+ size_t num_interfaces;
+
+ /* Interface to which options apply. If the device field isn't filled in
+ then it should be by the next --interface option. */
+ struct parse_interface *curint;
+};
+
+/* Adds an empty interface slot to H, and sets H's current interface to it, or
+ returns an error. */
+static error_t
+parse_hook_add_interface (struct parse_hook *h)
+{
+ struct parse_interface *new =
+ realloc (h->interfaces,
+ (h->num_interfaces + 1) * sizeof (struct parse_interface));
+ if (! new)
+ return ENOMEM;
+ h->interfaces = new;
+ h->num_interfaces++;
+ h->curint = new + h->num_interfaces - 1;
+ h->curint->device = 0;
+ h->curint->address = INADDR_NONE;
+ h->curint->netmask = INADDR_NONE;
+ h->curint->peer = INADDR_NONE;
+ h->curint->gateway = INADDR_NONE;
+
+#ifdef CONFIG_IPV6
+ memset (&h->curint->address6, 0, sizeof (struct inet6_ifaddr));
+ memset (&h->curint->gateway6, 0, sizeof (struct in6_addr));
+#endif
+
+ return 0;
+}
+
+#ifdef CONFIG_IPV6
+static struct rt6_info *
+ipv6_get_dflt_router (void)
+{
+ struct in6_addr daddr = { 0 };
+
+ struct fib6_node *fib = fib6_lookup
+ (&ip6_routing_table, &daddr, NULL);
+ return fib->leaf;
+}
+#endif /* CONFIG_IPV6 */
+
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ error_t err = 0;
+ struct parse_hook *h = state->hook;
+
+ /* Return _ERR from this routine, and in the special case of OPT being
+ ARGP_KEY_SUCCESS, remember to free H first. */
+#define RETURN(_err) \
+ do { if (opt == ARGP_KEY_SUCCESS) \
+ { err = (_err); goto free_hook; } \
+ else \
+ return _err; } while (0)
+
+ /* Print a parsing error message and (if exiting is turned off) return the
+ error code ERR. */
+#define PERR(err, fmt, args...) \
+ do { argp_error (state, fmt , ##args); RETURN (err); } while (0)
+
+ /* Like PERR but for non-parsing errors. */
+#define FAIL(rerr, status, perr, fmt, args...) \
+ do{ argp_failure (state, status, perr, fmt , ##args); RETURN (rerr); } while(0)
+
+ /* Parse STR and return the corresponding internet address. If STR is not
+ a valid internet address, signal an error mentioned TYPE. */
+#undef ADDR
+#define ADDR(str, type) \
+ ({ unsigned long addr = inet_addr (str); \
+ if (addr == INADDR_NONE) PERR (EINVAL, "Malformed %s", type); \
+ addr; })
+
+ switch (opt)
+ {
+ struct parse_interface *in;
+ uint32_t gateway;
+#ifdef CONFIG_IPV6
+ struct parse_interface *gw6_in;
+ char *ptr;
+#endif
+
+ case 'i':
+ /* An interface. */
+ err = 0;
+ if (h->curint->device)
+ /* The current interface slot is not available. */
+ {
+ /* First see if a previously specified one is being re-specified. */
+ for (in = h->interfaces; in < h->interfaces + h->num_interfaces; in++)
+ if (strcmp (in->device->name, arg) == 0)
+ /* Re-use an old slot. */
+ {
+ h->curint = in;
+ return 0;
+ }
+
+ /* Add a new interface entry. */
+ err = parse_hook_add_interface (h);
+ }
+ in = h->curint;
+
+ if (! err)
+ err = find_device (arg, &in->device);
+ if (err)
+ FAIL (err, 10, err, "%s", arg);
+
+ break;
+
+ case 'a':
+ h->curint->address = ADDR (arg, "address");
+ if (!IN_CLASSA (ntohl (h->curint->address))
+ && !IN_CLASSB (ntohl (h->curint->address))
+ && !IN_CLASSC (ntohl (h->curint->address)))
+ {
+ if (IN_MULTICAST (ntohl (h->curint->address)))
+ FAIL (EINVAL, 1, 0,
+ "%s: Cannot set interface address to multicast address",
+ arg);
+ else
+ FAIL (EINVAL, 1, 0,
+ "%s: Illegal or undefined network address", arg);
+ }
+ break;
+ case 'm':
+ h->curint->netmask = ADDR (arg, "netmask"); break;
+ case 'p':
+ h->curint->peer = ADDR (arg, "peer"); break;
+ case 'g':
+ h->curint->gateway = ADDR (arg, "gateway"); break;
+
+ case '4':
+ pfinet_bind (PORTCLASS_INET, arg);
+
+ /* Install IPv6 port class on bootstrap port. */
+ pfinet_bootstrap_portclass = PORTCLASS_INET6;
+ break;
+
+#ifdef CONFIG_IPV6
+ case '6':
+ pfinet_bind (PORTCLASS_INET6, arg);
+ break;
+
+ case 'A':
+ if ((ptr = strchr (arg, '/')))
+ {
+ h->curint->address6.prefix_len = atoi (ptr + 1);
+ if (h->curint->address6.prefix_len > 128)
+ FAIL (EINVAL, 1, 0, "%s: The prefix-length is invalid", arg);
+
+ *ptr = 0;
+ }
+ else
+ {
+ h->curint->address6.prefix_len = 64;
+ fprintf (stderr, "No prefix-length given, defaulting to %s/64.\n",
+ arg);
+ }
+
+ if (inet_pton (AF_INET6, arg, &h->curint->address6.addr) <= 0)
+ PERR (EINVAL, "Malformed address");
+
+ if (IN6_IS_ADDR_MULTICAST (&h->curint->address6.addr))
+ FAIL (EINVAL, 1, 0, "%s: Cannot set interface address to "
+ "multicast address", arg);
+ break;
+
+ case 'G':
+ if (inet_pton (AF_INET6, arg, &h->curint->gateway6) <= 0)
+ PERR (EINVAL, "Malformed gateway");
+
+ if (IN6_IS_ADDR_MULTICAST (&h->curint->gateway6))
+ FAIL (EINVAL, 1, 0, "%s: Cannot set gateway to "
+ "multicast address", arg);
+ break;
+#endif /* CONFIG_IPV6 */
+
+ case ARGP_KEY_INIT:
+ /* Initialize our parsing state. */
+ h = malloc (sizeof (struct parse_hook));
+ if (! h)
+ FAIL (ENOMEM, 11, ENOMEM, "option parsing");
+
+ h->interfaces = 0;
+ h->num_interfaces = 0;
+ err = parse_hook_add_interface (h);
+ if (err)
+ FAIL (err, 12, err, "option parsing");
+
+ state->hook = h;
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ in = h->curint;
+ if (! in->device)
+ /* No specific interface specified; is that ok? */
+ if (in->address != INADDR_NONE || in->netmask != INADDR_NONE
+ || in->gateway != INADDR_NONE)
+ /* Some options were specified, so we need an interface. See if
+ there's a single extant interface to use as a default. */
+ {
+ err = find_device (0, &in->device);
+ if (err)
+ FAIL (err, 13, 0, "No default interface");
+ }
+#if 0 /* XXX what does this mean??? */
+ /* Check for bogus option combinations. */
+ for (in = h->interfaces; in < h->interfaces + h->num_interfaces; in++)
+ if (in->netmask != INADDR_NONE
+ && in->address == INADDR_NONE && in->device->pa_addr == 0)
+ /* Specifying a netmask for an address-less interface is a no-no. */
+ FAIL (EDESTADDRREQ, 14, 0, "Cannot set netmask");
+#endif
+
+ gateway = INADDR_NONE;
+#ifdef CONFIG_IPV6
+ gw6_in = NULL;
+#endif
+ for (in = h->interfaces; in < h->interfaces + h->num_interfaces; in++)
+ {
+ if (in->gateway != INADDR_NONE)
+ {
+ if (gateway != INADDR_NONE)
+ FAIL (err, 15, 0, "Cannot have multiple default gateways");
+ gateway = in->gateway;
+ in->gateway = INADDR_NONE;
+ }
+
+#ifdef CONFIG_IPV6
+ if (!IN6_IS_ADDR_UNSPECIFIED (&in->gateway6))
+ {
+ if (gw6_in != NULL)
+ FAIL (err, 15, 0, "Cannot have multiple IPv6 "
+ "default gateways");
+ gw6_in = in;
+ }
+#endif
+ }
+ /* Successfully finished parsing, return a result. */
+
+ pthread_mutex_lock (&global_lock);
+
+ for (in = h->interfaces; in < h->interfaces + h->num_interfaces; in++)
+ {
+#ifdef CONFIG_IPV6
+ struct inet6_dev *idev = NULL;
+ if (trivfs_protid_portclasses[PORTCLASS_INET6] != MACH_PORT_NULL
+ && in->device)
+ idev = ipv6_find_idev(in->device);
+#endif
+
+ if (in->address != INADDR_NONE || in->netmask != INADDR_NONE)
+ {
+ err = configure_device (in->device, in->address, in->netmask,
+ in->peer, INADDR_NONE);
+ if (err)
+ {
+ pthread_mutex_unlock (&global_lock);
+ FAIL (err, 16, 0, "cannot configure interface");
+ }
+ }
+
+#ifdef CONFIG_IPV6
+ if (!idev)
+ continue;
+
+ /* First let's remove all non-local addresses. */
+ struct inet6_ifaddr *ifa = idev->addr_list;
+
+ while (ifa)
+ {
+ struct inet6_ifaddr *c_ifa = ifa;
+ ifa = ifa->if_next;
+
+ if (IN6_ARE_ADDR_EQUAL (&c_ifa->addr, &in->address6.addr))
+ memset (&in->address6, 0, sizeof (struct inet6_ifaddr));
+
+ else if (!IN6_IS_ADDR_LINKLOCAL (&c_ifa->addr)
+ && !IN6_IS_ADDR_SITELOCAL (&c_ifa->addr))
+ inet6_addr_del (in->device->ifindex, &c_ifa->addr,
+ c_ifa->prefix_len);
+ }
+
+ if (!IN6_IS_ADDR_UNSPECIFIED (&in->address6.addr))
+ {
+ /* Now assign the new address */
+ inet6_addr_add (in->device->ifindex, &in->address6.addr,
+ in->address6.prefix_len);
+ }
+#endif /* CONFIG_IPV6 */
+ }
+
+ /* Set the default gateway. This code is cobbled together from what
+ the SIOCADDRT ioctl code does, and from the apparent functionality
+ of the "netlink" layer from perusing a little. */
+ {
+ struct kern_rta rta;
+ struct
+ {
+ struct nlmsghdr nlh;
+ struct rtmsg rtm;
+ } req;
+ struct fib_table *tb;
+
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = 0;
+ req.nlh.nlmsg_len = NLMSG_LENGTH (sizeof req.rtm);
+
+ bzero (&req.rtm, sizeof req.rtm);
+ bzero (&rta, sizeof rta);
+ req.rtm.rtm_scope = RT_SCOPE_UNIVERSE;
+ req.rtm.rtm_type = RTN_UNICAST;
+ req.rtm.rtm_protocol = RTPROT_STATIC;
+ rta.rta_gw = &gateway;
+
+ if (gateway == INADDR_NONE)
+ {
+ /* Delete any existing default route. */
+ req.nlh.nlmsg_type = RTM_DELROUTE;
+ req.nlh.nlmsg_flags = 0;
+ tb = fib_get_table (req.rtm.rtm_table);
+ if (tb)
+ {
+ err = - (*tb->tb_delete) (tb, &req.rtm, &rta, &req.nlh, 0);
+ if (err && err != ESRCH)
+ {
+ pthread_mutex_unlock (&global_lock);
+ FAIL (err, 17, 0, "cannot remove old default gateway");
+ }
+ err = 0;
+ }
+ }
+ else
+ {
+ /* Add a default route, replacing any existing one. */
+ req.nlh.nlmsg_type = RTM_NEWROUTE;
+ req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE;
+ tb = fib_new_table (req.rtm.rtm_table);
+ err = (!tb ? ENOBUFS
+ : - (*tb->tb_insert) (tb, &req.rtm, &rta, &req.nlh, 0));
+ if (err)
+ {
+ pthread_mutex_unlock (&global_lock);
+ FAIL (err, 17, 0, "cannot set default gateway");
+ }
+ }
+ }
+
+ /* Set IPv6 default router. */
+#ifdef CONFIG_IPV6
+ if (trivfs_protid_portclasses[PORTCLASS_INET6] != MACH_PORT_NULL)
+ {
+ struct rt6_info *rt6i = ipv6_get_dflt_router ();
+
+ if (!gw6_in || rt6i->rt6i_dev != gw6_in->device
+ || !IN6_ARE_ADDR_EQUAL (&rt6i->rt6i_gateway, &gw6_in->gateway6))
+ {
+ rt6_purge_dflt_routers (0);
+ if (gw6_in)
+ rt6_add_dflt_router (&gw6_in->gateway6, gw6_in->device);
+ }
+ }
+#endif
+
+ pthread_mutex_unlock (&global_lock);
+
+ /* Fall through to free hook. */
+
+ case ARGP_KEY_ERROR:
+ /* Parsing error occurred, free everything. */
+ free_hook:
+ free (h->interfaces);
+ free (h);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return err;
+}
+
+struct argp
+pfinet_argp = { options, parse_opt, 0, doc };
+
+struct argp *trivfs_runtime_argp = &pfinet_argp;
+
+error_t
+trivfs_append_args (struct trivfs_control *fsys, char **argz, size_t *argz_len)
+{
+ error_t add_dev_opts (struct device *dev)
+ {
+ error_t err = 0;
+ uint32_t addr, mask, peer, broad;
+ struct rt_key key = { 0 };
+ struct fib_result res;
+
+ inquire_device (dev, &addr, &mask, &peer, &broad);
+
+#define ADD_OPT(fmt, args...) \
+ do { char buf[100]; \
+ if (! err) { \
+ snprintf (buf, sizeof buf, fmt , ##args); \
+ err = argz_add (argz, argz_len, buf); } } while (0)
+#define ADD_ADDR_OPT(name, addr) \
+ do { struct in_addr i; \
+ i.s_addr = (addr); \
+ ADD_OPT ("--%s=%s", name, inet_ntoa (i)); } while (0)
+
+ ADD_OPT ("--interface=%s", dev->name);
+ if (addr != INADDR_NONE)
+ ADD_ADDR_OPT ("address", addr);
+ if (mask != INADDR_NONE)
+ ADD_ADDR_OPT ("netmask", mask);
+ if (peer != addr)
+ ADD_ADDR_OPT ("peer", peer);
+ key.iif = dev->ifindex;
+ if (! main_table->tb_lookup (main_table, &key, &res))
+ ADD_ADDR_OPT ("gateway", FIB_RES_GW (res));
+
+#undef ADD_ADDR_OPT
+
+#ifdef CONFIG_IPV6
+ struct inet6_dev *idev = NULL;
+
+ if (trivfs_protid_portclasses[PORTCLASS_INET6] != MACH_PORT_NULL)
+ idev = ipv6_find_idev(dev);
+
+ if (idev)
+ {
+ struct inet6_ifaddr *ifa;
+ static char addr_buf[INET6_ADDRSTRLEN];
+
+ /* Push all IPv6 addresses assigned to the interface. */
+ for (ifa = idev->addr_list; ifa; ifa = ifa->if_next)
+ {
+ inet_ntop (AF_INET6, &ifa->addr, addr_buf, INET6_ADDRSTRLEN);
+ ADD_OPT ("--address6=%s/%d", addr_buf, ifa->prefix_len);
+ }
+
+ /* Last not least push --gateway6 option. */
+ struct rt6_info *rt6i = ipv6_get_dflt_router ();
+ if(rt6i->rt6i_dev == dev)
+ {
+ inet_ntop (AF_INET6, &rt6i->rt6i_gateway, addr_buf,
+ INET6_ADDRSTRLEN);
+ ADD_OPT ("--gateway6=%s", addr_buf);
+ }
+ }
+#endif /* CONFIG_IPV6 */
+
+#undef ADD_OPT
+
+ return err;
+ }
+
+ return enumerate_devices (add_dev_opts);
+}
diff --git a/pfinet/pfinet-ops.c b/pfinet/pfinet-ops.c
new file mode 100644
index 00000000..6724575c
--- /dev/null
+++ b/pfinet/pfinet-ops.c
@@ -0,0 +1,93 @@
+/*
+ Copyright (C) 2000,02 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "pfinet.h"
+
+#include <linux/netdevice.h>
+#include <linux/notifier.h>
+
+#include "pfinet_S.h"
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <mach/notify.h>
+#include <sys/mman.h>
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+
+extern int dev_ifconf(char *arg);
+
+/* Return the list of devices in the format provided by SIOCGIFCONF
+ in IFR, but don't return more then AMOUNT bytes. If AMOUNT is
+ negative, there is no limit. */
+error_t
+S_pfinet_siocgifconf (io_t port,
+ vm_size_t amount,
+ char **ifr,
+ mach_msg_type_number_t *len)
+{
+ error_t err = 0;
+ struct ifconf ifc;
+
+ pthread_mutex_lock (&global_lock);
+ if (amount == (vm_size_t) -1)
+ {
+ /* Get the needed buffer length. */
+ ifc.ifc_buf = NULL;
+ ifc.ifc_len = 0;
+ err = dev_ifconf ((char *) &ifc);
+ if (err)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return -err;
+ }
+ amount = ifc.ifc_len;
+ }
+ else
+ ifc.ifc_len = amount;
+
+ if (amount > 0)
+ {
+ /* Possibly allocate a new buffer. */
+ if (*len < amount)
+ ifc.ifc_buf = (char *) mmap (0, amount, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ else
+ ifc.ifc_buf = *ifr;
+ err = dev_ifconf ((char *) &ifc);
+ }
+
+ if (err)
+ {
+ *len = 0;
+ if (ifc.ifc_buf != *ifr)
+ munmap (ifc.ifc_buf, amount);
+ }
+ else
+ {
+ *len = ifc.ifc_len;
+ *ifr = ifc.ifc_buf;
+ }
+
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
diff --git a/pfinet/pfinet.h b/pfinet/pfinet.h
new file mode 100644
index 00000000..46aa97bb
--- /dev/null
+++ b/pfinet/pfinet.h
@@ -0,0 +1,107 @@
+/*
+ Copyright (C) 1995, 1996, 1999, 2000, 2002, 2007
+ Free Software Foundation, Inc.
+
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef PFINET_H_
+#define PFINET_H_
+
+#include <device/device.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <pthread.h>
+
+extern pthread_mutex_t global_lock;
+extern pthread_mutex_t net_bh_lock;
+
+struct port_bucket *pfinet_bucket;
+struct port_class *addrport_class;
+struct port_class *socketport_class;
+
+mach_port_t fsys_identity;
+
+extern struct device *dev_base;
+extern struct device loopback_dev;
+
+/* A port on SOCK. Multiple sock_user's can point to the same socket. */
+struct sock_user
+{
+ struct port_info pi;
+ int isroot;
+ struct socket *sock;
+};
+
+/* Socket address ports. */
+struct sock_addr
+{
+ struct port_info pi;
+ struct sockaddr address;
+};
+
+/* Trivfs control structure for pfinet. */
+struct trivfs_control *pfinetctl;
+
+/* Owner of the underlying node. */
+uid_t pfinet_owner;
+
+/* Group of the underlying node. */
+uid_t pfinet_group;
+
+void ethernet_initialize (void);
+int ethernet_demuxer (mach_msg_header_t *, mach_msg_header_t *);
+void setup_ethernet_device (char *, struct device **);
+void setup_dummy_device (char *, struct device **);
+void setup_tunnel_device (char *, struct device **);
+struct sock_user *make_sock_user (struct socket *, int, int, int);
+error_t make_sockaddr_port (struct socket *, int,
+ mach_port_t *, mach_msg_type_name_t *);
+void init_devices (void);
+void *net_bh_worker (void *);
+void init_time (void);
+void ip_rt_add (short, u_long, u_long, u_long, struct device *,
+ u_short, u_long);
+void ip_rt_del (u_long, struct device *);
+struct sock;
+error_t tcp_tiocinq (struct sock *sk, mach_msg_type_number_t *amount);
+
+void clean_addrport (void *);
+void clean_socketport (void *);
+
+/* pfinet6 port classes. */
+enum {
+ PORTCLASS_INET,
+ PORTCLASS_INET6,
+};
+
+extern struct port_class *trivfs_protid_portclasses[];
+extern int trivfs_protid_nportclasses;
+
+extern struct port_class *trivfs_cntl_portclasses[2];
+extern int trivfs_cntl_nportclasses;
+
+/* Which portclass to install on the bootstrap port. */
+extern int pfinet_bootstrap_portclass;
+
+/* Install portclass on node NAME. */
+void pfinet_bind (int portclass, const char *name);
+
+#endif
diff --git a/pfinet/sched.c b/pfinet/sched.c
new file mode 100644
index 00000000..af03ab49
--- /dev/null
+++ b/pfinet/sched.c
@@ -0,0 +1,76 @@
+/*
+ Copyright (C) 1995,96,2000,02 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "pfinet.h"
+
+#include <asm/system.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t net_bh_lock = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t net_bh_wakeup = PTHREAD_COND_INITIALIZER;
+int net_bh_raised = 0;
+
+struct task_struct current_contents; /* zeros are right default values */
+
+
+/* Wake up the owner of the SOCK. If HOW is zero, then just
+ send SIGIO. If HOW is one, then send SIGIO only if the
+ SO_WAITDATA flag is off. If HOW is two, then send SIGIO
+ only if the SO_NOSPACE flag is on, and also clear it. */
+int
+sock_wake_async (struct socket *sock, int how)
+{
+ /* For now, do nothing. XXX */
+ return 0;
+}
+
+
+/* This function is the "net_bh worker thread".
+ The packet receiver thread calls net/core/dev.c::netif_rx with a packet;
+ netif_rx either drops the packet, or enqueues it and wakes us up
+ via mark_bh which is really condition_broadcast on net_bh_wakeup.
+ The packet receiver thread holds net_bh_lock while calling netif_rx.
+ We wake up and take global_lock, which locks out RPC service threads.
+ We then also take net_bh_lock running net_bh.
+ Thus, only this thread running net_bh locks out the packet receiver
+ thread (which takes only net_bh_lock while calling netif_rx), so packets
+ are quickly moved from the Mach port's message queue to the `backlog'
+ queue, or dropped, without synchronizing with RPC service threads.
+ (The RPC service threads lock out the running of net_bh, but not
+ the queuing/dropping of packets in netif_rx.) */
+void *
+net_bh_worker (void *arg)
+{
+ pthread_mutex_lock (&net_bh_lock);
+ while (1)
+ {
+ while (!net_bh_raised)
+ pthread_cond_wait (&net_bh_wakeup, &net_bh_lock);
+
+ net_bh_raised = 0;
+
+ pthread_mutex_lock (&global_lock);
+ net_bh ();
+ pthread_mutex_unlock (&global_lock);
+ }
+ /*NOTREACHED*/
+ return 0;
+}
diff --git a/pfinet/socket-ops.c b/pfinet/socket-ops.c
new file mode 100644
index 00000000..3f8b7fbc
--- /dev/null
+++ b/pfinet/socket-ops.c
@@ -0,0 +1,544 @@
+/* Interface functions for the socket.defs interface.
+ Copyright (C) 1995,96,97,99,2000,02,07 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <sys/stat.h>
+#include <hurd/trivfs.h>
+#include <string.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <hurd/fshelp.h>
+
+#include "pfinet.h"
+#include "socket_S.h"
+
+#include <linux/sched.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <net/sock.h>
+
+
+error_t
+S_socket_create (struct trivfs_protid *master,
+ int sock_type,
+ int protocol,
+ mach_port_t *port,
+ mach_msg_type_name_t *porttype)
+{
+ struct sock_user *user;
+ struct socket *sock;
+ error_t err;
+ int isroot;
+
+ if (!master)
+ return EOPNOTSUPP;
+
+ /* Don't allow bogus SOCK_PACKET here. */
+
+ if (sock_type != SOCK_STREAM
+ && sock_type != SOCK_DGRAM
+ && sock_type != SOCK_SEQPACKET
+ && sock_type != SOCK_RAW)
+ return EPROTOTYPE;
+
+ if (protocol < 0)
+ return EPROTONOSUPPORT;
+
+ pthread_mutex_lock (&global_lock);
+
+ become_task_protid (master);
+
+ sock = sock_alloc ();
+
+ sock->type = sock_type;
+
+ isroot = master->isroot;
+ if (!isroot)
+ {
+ struct stat st;
+
+ /* XXX */
+ st.st_uid = pfinet_owner;
+ st.st_gid = pfinet_group;
+
+ err = fshelp_isowner (&st, master->user);
+ if (! err)
+ isroot = 1;
+ }
+
+ if (master->pi.class == trivfs_protid_portclasses[PORTCLASS_INET])
+ err = - (*net_families[PF_INET]->create) (sock, protocol);
+ else
+ err = - (*net_families[PF_INET6]->create) (sock, protocol);
+
+ if (err)
+ sock_release (sock);
+ else
+ {
+ user = make_sock_user (sock, isroot, 0, 1);
+ *port = ports_get_right (user);
+ *porttype = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (user);
+ }
+
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+
+/* Listen on a socket. */
+error_t
+S_socket_listen (struct sock_user *user, int queue_limit)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ become_task (user);
+ err = - (*user->sock->ops->listen) (user->sock, queue_limit);
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+error_t
+S_socket_accept (struct sock_user *user,
+ mach_port_t *new_port,
+ mach_msg_type_name_t *new_port_type,
+ mach_port_t *addr_port,
+ mach_msg_type_name_t *addr_port_type)
+{
+ struct sock_user *newuser;
+ struct socket *sock, *newsock;
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ sock = user->sock;
+
+ pthread_mutex_lock (&global_lock);
+
+ become_task (user);
+
+ newsock = sock_alloc ();
+ if (!newsock)
+ err = ENOMEM;
+ else
+ {
+ newsock->type = sock->type;
+
+ err = - (*sock->ops->dup) (newsock, sock);
+ if (!err)
+ err = - (*sock->ops->accept) (sock, newsock, sock->flags);
+
+ if (!err)
+ /* In Linux there is a race here with the socket closing before the
+ ops->getname call we do in make_sockaddr_port. Since we still
+ have the world locked, this shouldn't be an issue for us. */
+ err = make_sockaddr_port (newsock, 1, addr_port, addr_port_type);
+
+ if (!err)
+ {
+ newuser = make_sock_user (newsock, user->isroot, 0, 1);
+ *new_port = ports_get_right (newuser);
+ *new_port_type = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newuser);
+ }
+
+ if (err)
+ sock_release (newsock);
+ }
+
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+error_t
+S_socket_connect (struct sock_user *user,
+ struct sock_addr *addr)
+{
+ struct socket *sock;
+ error_t err;
+
+ if (!user || !addr)
+ return EOPNOTSUPP;
+
+ sock = user->sock;
+
+ pthread_mutex_lock (&global_lock);
+
+ become_task (user);
+
+ err = - (*sock->ops->connect) (sock, &addr->address, addr->address.sa_len,
+ sock->flags);
+
+ pthread_mutex_unlock (&global_lock);
+
+ /* MiG should do this for us, but it doesn't. */
+ if (!err)
+ mach_port_deallocate (mach_task_self (), addr->pi.port_right);
+
+ return err;
+}
+
+error_t
+S_socket_bind (struct sock_user *user,
+ struct sock_addr *addr)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+ if (! addr)
+ return EADDRNOTAVAIL;
+
+ pthread_mutex_lock (&global_lock);
+ become_task (user);
+ err = - (*user->sock->ops->bind) (user->sock,
+ &addr->address, addr->address.sa_len);
+ pthread_mutex_unlock (&global_lock);
+
+ /* MiG should do this for us, but it doesn't. */
+ if (!err)
+ mach_port_deallocate (mach_task_self (), addr->pi.port_right);
+
+ return err;
+}
+
+error_t
+S_socket_name (struct sock_user *user,
+ mach_port_t *addr_port,
+ mach_msg_type_name_t *addr_port_name)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ become_task (user);
+ make_sockaddr_port (user->sock, 0, addr_port, addr_port_name);
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+S_socket_peername (struct sock_user *user,
+ mach_port_t *addr_port,
+ mach_msg_type_name_t *addr_port_name)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ become_task (user);
+ err = make_sockaddr_port (user->sock, 1, addr_port, addr_port_name);
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+error_t
+S_socket_connect2 (struct sock_user *user1,
+ struct sock_user *user2)
+{
+ error_t err;
+
+ if (!user1 || !user2)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ become_task (user1);
+
+ if (user1->sock->type != user2->sock->type)
+ err = EINVAL;
+ else if (user1->sock->state != SS_UNCONNECTED
+ && user2->sock->state != SS_UNCONNECTED)
+ err = EISCONN;
+ else
+ err = - (*user1->sock->ops->socketpair) (user1->sock, user2->sock);
+
+ pthread_mutex_unlock (&global_lock);
+
+ /* MiG should do this for us, but it doesn't. */
+ if (!err)
+ mach_port_deallocate (mach_task_self (), user2->pi.port_right);
+
+ return err;
+}
+
+error_t
+S_socket_create_address (mach_port_t server,
+ int sockaddr_type,
+ char *data,
+ mach_msg_type_number_t data_len,
+ mach_port_t *addr_port,
+ mach_msg_type_name_t *addr_port_type)
+{
+ error_t err;
+ struct sock_addr *addrstruct;
+ const struct sockaddr *const sa = (void *) data;
+
+ if (sockaddr_type != AF_INET && sockaddr_type != AF_INET6
+ && sockaddr_type != AF_UNSPEC)
+ return EAFNOSUPPORT;
+ if (sa->sa_family != sockaddr_type
+ || data_len < offsetof (struct sockaddr, sa_data))
+ return EINVAL;
+
+ err = ports_create_port (addrport_class, pfinet_bucket,
+ (offsetof (struct sock_addr, address)
+ + data_len), &addrstruct);
+ if (err)
+ return err;
+
+ memcpy (&addrstruct->address, data, data_len);
+
+ /* BSD does not require incoming sa_len to be set, so we don't either. */
+ addrstruct->address.sa_len = data_len;
+
+ *addr_port = ports_get_right (addrstruct);
+ *addr_port_type = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (addrstruct);
+ return 0;
+}
+
+error_t
+S_socket_fabricate_address (mach_port_t server,
+ int sockaddr_type,
+ mach_port_t *addr_port,
+ mach_msg_type_name_t *addr_port_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_socket_whatis_address (struct sock_addr *addr,
+ int *type,
+ char **data,
+ mach_msg_type_number_t *datalen)
+{
+ if (!addr)
+ return EOPNOTSUPP;
+
+ *type = addr->address.sa_family;
+ if (*datalen < addr->address.sa_len)
+ *data = mmap (0, addr->address.sa_len,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ *datalen = addr->address.sa_len;
+ memcpy (*data, &addr->address, addr->address.sa_len);
+
+ return 0;
+}
+
+error_t
+S_socket_shutdown (struct sock_user *user,
+ int direction)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ become_task (user);
+ err = - (*user->sock->ops->shutdown) (user->sock, direction);
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+error_t
+S_socket_getopt (struct sock_user *user,
+ int level,
+ int option,
+ char **data,
+ size_t *datalen)
+{
+ error_t err;
+
+ if (! user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ become_task (user);
+
+ int len = *datalen;
+ err = - (level == SOL_SOCKET ? sock_getsockopt
+ : *user->sock->ops->getsockopt)
+ (user->sock, level, option, *data, &len);
+ *datalen = len;
+
+ pthread_mutex_unlock (&global_lock);
+
+ /* XXX option data not properly typed, needs byte-swapping for netmsgserver.
+ Most options are ints, some like IP_OPTIONS are bytesex-neutral. */
+
+ return err;
+}
+
+error_t
+S_socket_setopt (struct sock_user *user,
+ int level,
+ int option,
+ char *data,
+ size_t datalen)
+{
+ error_t err;
+
+ if (! user)
+ return EOPNOTSUPP;
+
+ /* XXX option data not properly typed, needs byte-swapping for netmsgserver.
+ Most options are ints, some like IP_OPTIONS are bytesex-neutral. */
+
+ pthread_mutex_lock (&global_lock);
+ become_task (user);
+
+ err = - (level == SOL_SOCKET ? sock_setsockopt
+ : *user->sock->ops->setsockopt)
+ (user->sock, level, option, data, datalen);
+
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+error_t
+S_socket_send (struct sock_user *user,
+ struct sock_addr *addr,
+ int flags,
+ char *data,
+ size_t datalen,
+ mach_port_t *ports,
+ size_t nports,
+ char *control,
+ size_t controllen,
+ mach_msg_type_number_t *amount)
+{
+ int sent;
+ struct iovec iov = { data, datalen };
+ struct msghdr m = { msg_name: addr ? &addr->address : 0,
+ msg_namelen: addr ? addr->address.sa_len : 0,
+ msg_flags: flags,
+ msg_controllen: 0, msg_iov: &iov, msg_iovlen: 1 };
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ /* Don't do this yet, it's too bizarre to think about right now. */
+ if (nports != 0 || controllen != 0)
+ return EINVAL;
+
+ pthread_mutex_lock (&global_lock);
+ become_task (user);
+ if (user->sock->flags & O_NONBLOCK)
+ m.msg_flags |= MSG_DONTWAIT;
+ sent = (*user->sock->ops->sendmsg) (user->sock, &m, datalen, 0);
+ pthread_mutex_unlock (&global_lock);
+
+ /* MiG should do this for us, but it doesn't. */
+ if (addr && sent >= 0)
+ mach_port_deallocate (mach_task_self (), addr->pi.port_right);
+
+ if (sent >= 0)
+ {
+ *amount = sent;
+ return 0;
+ }
+ else
+ return (error_t)-sent;
+}
+
+error_t
+S_socket_recv (struct sock_user *user,
+ mach_port_t *addrport,
+ mach_msg_type_name_t *addrporttype,
+ int flags,
+ char **data,
+ size_t *datalen,
+ mach_port_t **ports,
+ mach_msg_type_name_t *portstype,
+ size_t *nports,
+ char **control,
+ size_t *controllen,
+ int *outflags,
+ mach_msg_type_number_t amount)
+{
+ error_t err;
+ union { struct sockaddr_storage storage; struct sockaddr sa; } addr;
+ int alloced = 0;
+ struct iovec iov;
+ struct msghdr m = { msg_name: &addr.sa, msg_namelen: sizeof addr,
+ msg_controllen: 0, msg_iov: &iov, msg_iovlen: 1 };
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ /* Instead of this, we should peek and the socket and only
+ allocate as much as necessary. */
+ if (amount > *datalen)
+ {
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ /* Should check whether errno is indeed ENOMEM --
+ but this can't be done in a straightforward way,
+ because the glue headers #undef errno. */
+ return ENOMEM;
+ alloced = 1;
+ }
+
+ iov.iov_base = *data;
+ iov.iov_len = amount;
+
+ pthread_mutex_lock (&global_lock);
+ become_task (user);
+ if (user->sock->flags & O_NONBLOCK)
+ flags |= MSG_DONTWAIT;
+ err = (*user->sock->ops->recvmsg) (user->sock, &m, amount, flags, 0);
+ pthread_mutex_unlock (&global_lock);
+
+ if (err < 0)
+ err = -err;
+ else
+ {
+ *datalen = err;
+ if (alloced && round_page (*datalen) < round_page (amount))
+ munmap (*data + round_page (*datalen),
+ round_page (amount) - round_page (*datalen));
+ err = S_socket_create_address (0, addr.sa.sa_family,
+ (void *) &addr.sa, m.msg_namelen,
+ addrport, addrporttype);
+ if (err && alloced)
+ munmap (*data, *datalen);
+
+ *outflags = m.msg_flags;
+ *nports = 0;
+ *portstype = MACH_MSG_TYPE_COPY_SEND;
+ *controllen = 0;
+ }
+
+ return err;
+}
diff --git a/pfinet/socket.c b/pfinet/socket.c
new file mode 100644
index 00000000..23a2dd91
--- /dev/null
+++ b/pfinet/socket.c
@@ -0,0 +1,126 @@
+/*
+ Copyright (C) 1995,2000,02 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <assert.h>
+#include "pfinet.h"
+
+#include <linux/socket.h>
+#include <linux/net.h>
+
+#ifndef NPROTO
+#define NPROTO (PF_INET + 1)
+#endif
+struct net_proto_family *net_families[NPROTO];
+
+/* Notice that a protocol family is live; this only works for inet here. */
+int
+sock_register (struct net_proto_family *fam)
+{
+ assert (fam->family < NPROTO);
+ net_families[fam->family] = fam;
+ return 0;
+}
+
+
+struct socket *
+sock_alloc (void)
+{
+ static ino_t nextino; /* locked by global_lock */
+ struct socket *sock;
+ pthread_cond_t *c;
+
+ sock = malloc (sizeof *sock + sizeof (pthread_cond_t));
+ if (!sock)
+ return 0;
+ c = (void *) &sock[1];
+ pthread_cond_init (c, NULL);
+ bzero (sock, sizeof *sock);
+ sock->state = SS_UNCONNECTED;
+ sock->identity = MACH_PORT_NULL;
+ sock->refcnt = 1;
+ sock->wait = (void *) c;
+
+ if (nextino == 0)
+ nextino = 2;
+ sock->st_ino = nextino++;
+
+ return sock;
+}
+
+/* Create a sock_user structure, initialized from SOCK and ISROOT.
+ If NOINSTALL is set, don't put it in the portset.
+ We increment SOCK->refcnt iff CONSUME is zero. */
+struct sock_user *
+make_sock_user (struct socket *sock, int isroot, int noinstall, int consume)
+{
+ error_t err;
+ struct sock_user *user;
+
+ assert (sock->refcnt != 0);
+
+ if (noinstall)
+ err = ports_create_port_noinstall (socketport_class, pfinet_bucket,
+ sizeof (struct sock_user), &user);
+ else
+ err = ports_create_port (socketport_class, pfinet_bucket,
+ sizeof (struct sock_user), &user);
+ if (err)
+ return 0;
+
+ /* We maintain a reference count in `struct socket' (a member not
+ in the original Linux structure), because there can be multiple
+ ports (struct sock_user, aka protids) pointing to the same socket.
+ The socket lives until all the ports die. */
+ if (! consume)
+ ++sock->refcnt;
+ user->isroot = isroot;
+ user->sock = sock;
+ return user;
+}
+
+/* This is called from the port cleanup function below, and on
+ a newly allocated socket when something went wrong in its creation. */
+void
+sock_release (struct socket *sock)
+{
+ if (--sock->refcnt != 0)
+ return;
+
+ if (sock->state != SS_UNCONNECTED)
+ sock->state = SS_DISCONNECTING;
+
+ if (sock->ops)
+ sock->ops->release(sock, NULL);
+
+ if (sock->identity != MACH_PORT_NULL)
+ mach_port_destroy (mach_task_self (), sock->identity);
+
+ free (sock);
+}
+
+/* Release the reference on the referenced socket. */
+void
+clean_socketport (void *arg)
+{
+ struct sock_user *const user = arg;
+
+ pthread_mutex_lock (&global_lock);
+ sock_release (user->sock);
+ pthread_mutex_unlock (&global_lock);
+}
diff --git a/pfinet/stubs.c b/pfinet/stubs.c
new file mode 100644
index 00000000..cdb8a6de
--- /dev/null
+++ b/pfinet/stubs.c
@@ -0,0 +1,73 @@
+/* Stub functions replacing things called from the Linux code
+ Copyright (C) 2000,02 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "pfinet.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <net/sock.h>
+#include <net/pkt_sched.h>
+
+int qdisc_restart(struct device *dev)
+{
+ return 0;
+}
+
+void qdisc_run_queues(void)
+{
+}
+
+struct Qdisc_head qdisc_head;
+struct Qdisc qdisc_stub;
+
+void
+dev_init_scheduler (struct device *dev)
+{
+ dev->qdisc = &qdisc_stub;
+}
+void dev_shutdown (struct device *)
+ __attribute__ ((alias ("dev_init_scheduler")));
+void dev_activate (struct device *)
+ __attribute__ ((alias ("dev_init_scheduler")));
+void dev_deactivate (struct device *)
+ __attribute__ ((alias ("dev_init_scheduler")));
+void tcp_ioctl () __attribute__ ((alias ("dev_init_scheduler")));
+
+/* This isn't quite a stub, but it's not quite right either. */
+__u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
+ __u16 sport, __u16 dport)
+{
+ static u32 tcp_iss;
+ static time_t last;
+ struct timeval now;
+
+ do_gettimeofday (&now);
+
+ if (now.tv_sec - last > 300)
+ {
+ last = now.tv_sec;
+ srandom (getpid () ^ now.tv_sec ^ now.tv_usec);
+ tcp_iss = random ();
+ }
+
+ return tcp_iss + (now.tv_sec * 1000000) + now.tv_usec;
+}
diff --git a/pfinet/time.c b/pfinet/time.c
new file mode 100644
index 00000000..13f53cb7
--- /dev/null
+++ b/pfinet/time.c
@@ -0,0 +1,27 @@
+/* Time management functions
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <sys/time.h>
+
+void
+do_gettimeofday (struct timeval *tp)
+{
+ gettimeofday (tp, 0);
+}
diff --git a/pfinet/timer-emul.c b/pfinet/timer-emul.c
new file mode 100644
index 00000000..f6760d7e
--- /dev/null
+++ b/pfinet/timer-emul.c
@@ -0,0 +1,182 @@
+/*
+ Copyright (C) 1995,96,2000,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* Do not include glue-include/linux/errno.h */
+#define _HACK_ERRNO_H
+
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <linux/sched.h>
+#include <error.h>
+#include <string.h>
+#include "pfinet.h"
+
+long long root_jiffies;
+volatile struct mapped_time_value *mapped_time;
+
+struct timer_list *timers;
+thread_t timer_thread = 0;
+
+static void *
+timer_function (void *this_is_a_pointless_variable_with_a_rather_long_name)
+{
+ mach_port_t recv;
+ int wait = 0;
+
+ recv = mach_reply_port ();
+
+ timer_thread = mach_thread_self ();
+
+ pthread_mutex_lock (&global_lock);
+ while (1)
+ {
+ int jiff = jiffies;
+
+ if (!timers)
+ wait = -1;
+ else if (timers->expires <= jiff)
+ wait = 0;
+ else
+ wait = ((timers->expires - jiff) * 1000) / HZ;
+
+ pthread_mutex_unlock (&global_lock);
+
+ mach_msg (NULL, (MACH_RCV_MSG | MACH_RCV_INTERRUPT
+ | (wait == -1 ? 0 : MACH_RCV_TIMEOUT)),
+ 0, 0, recv, wait, MACH_PORT_NULL);
+
+ pthread_mutex_lock (&global_lock);
+
+ while (timers->expires <= jiffies)
+ {
+ struct timer_list *tp;
+
+ tp = timers;
+
+ timers = timers->next;
+ if (timers)
+ timers->prev = &timers;
+
+ tp->next = 0;
+ tp->prev = 0;
+
+ (*tp->function) (tp->data);
+ }
+ }
+
+ return NULL;
+}
+
+
+void
+add_timer (struct timer_list *timer)
+{
+ struct timer_list **tp;
+
+ for (tp = &timers; *tp; tp = &(*tp)->next)
+ if ((*tp)->expires > timer->expires)
+ {
+ timer->next = *tp;
+ timer->next->prev = &timer->next;
+ timer->prev = tp;
+ *tp = timer;
+ break;
+ }
+ if (!*tp)
+ {
+ timer->next = 0;
+ timer->prev = tp;
+ *tp = timer;
+ }
+
+ if (timers == timer)
+ {
+ /* We have change the first one, so tweak the timer thread
+ to push things up. */
+ while (timer_thread == 0)
+ swtch_pri (0);
+
+ if (timer_thread != mach_thread_self ())
+ {
+ thread_suspend (timer_thread);
+ thread_abort (timer_thread);
+ thread_resume (timer_thread);
+ }
+ }
+}
+
+int
+del_timer (struct timer_list *timer)
+{
+ if (timer->prev)
+ {
+ *timer->prev = timer->next;
+ if (timer->next)
+ timer->next->prev = timer->prev;
+
+ timer->next = 0;
+ timer->prev = 0;
+ return 1;
+ }
+ else
+ return 0;
+}
+
+void
+mod_timer (struct timer_list *timer, unsigned long expires)
+{
+ /* Should optimize this. */
+ del_timer (timer);
+ timer->expires = expires;
+ add_timer (timer);
+}
+
+
+void
+init_timer (struct timer_list *timer)
+{
+ bzero (timer, sizeof (struct timer_list));
+}
+
+void
+init_time ()
+{
+ error_t err;
+ struct timeval tp;
+ pthread_t thread;
+
+ err = maptime_map (0, 0, &mapped_time);
+ if (err)
+ error (2, err, "cannot map time device");
+
+ maptime_read (mapped_time, &tp);
+
+ root_jiffies = (long long) tp.tv_sec * HZ
+ + ((long long) tp.tv_usec * HZ) / 1000000;
+
+ err = pthread_create (&thread, NULL, timer_function, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+}
diff --git a/pfinet/tunnel.c b/pfinet/tunnel.c
new file mode 100644
index 00000000..6a7f355d
--- /dev/null
+++ b/pfinet/tunnel.c
@@ -0,0 +1,683 @@
+/*
+ Copyright (C) 1995,96,98,99,2000,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "pfinet.h"
+
+#include <hurd.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <device/device.h>
+#include <device/net_status.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <error.h>
+#include <errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+
+#include "libtrivfs/trivfs_fs_S.h"
+#include "libtrivfs/trivfs_io_S.h"
+
+struct port_class *tunnel_cntlclass;
+struct port_class *tunnel_class;
+
+struct tunnel_device
+{
+ struct tunnel_device *next;
+ struct trivfs_control *cntl;
+ char *devname;
+ file_t underlying;
+ struct iouser *user;
+ struct sk_buff_head xq; /* Transmit queue. */
+ pthread_cond_t wait; /* For read and select. */
+ pthread_cond_t select_alert; /* For read and select. */
+ pthread_mutex_t lock; /* For read and select. */
+ int read_blocked; /* For read and select. */
+ struct device dev;
+ struct net_device_stats stats;
+};
+
+
+/* Linked list of all tunnel devices. */
+struct tunnel_device *tunnel_dev;
+
+
+struct net_device_stats *
+tunnel_get_stats (struct device *dev)
+{
+ struct tunnel_device *tdev = (struct tunnel_device *) dev->priv;
+
+ assert (tdev);
+
+ return &tdev->stats;
+}
+
+int
+tunnel_stop (struct device *dev)
+{
+ struct tunnel_device *tdev = (struct tunnel_device *) dev->priv;
+ struct sk_buff *skb;
+
+ assert (tdev);
+
+ while ((skb = skb_dequeue (&tdev->xq)) != 0)
+ dev_kfree_skb(skb);
+
+ /* Call those only if removing the device completely. */
+ /* free (tdev->devname); */
+ /* XXX??? mach_port_deallocate (mach_task_self, tdev->underlying) */
+ return 0;
+}
+
+void
+tunnel_set_multi (struct device *dev)
+{
+}
+
+void
+tunnel_initialize (void)
+{
+}
+
+int
+tunnel_open (struct device *dev)
+{
+ struct tunnel_device *tdev = (struct tunnel_device *) dev->priv;
+
+ assert (tdev);
+
+ skb_queue_head_init(&tdev->xq);
+
+ return 0;
+}
+
+/* Transmit an ethernet frame */
+int
+tunnel_xmit (struct sk_buff *skb, struct device *dev)
+{
+ struct tunnel_device *tdev = (struct tunnel_device *) dev->priv;
+
+ assert (tdev);
+
+ pthread_mutex_lock (&tdev->lock);
+
+ /* Avoid unlimited growth. */
+ if (skb_queue_len(&tdev->xq) > 128)
+ {
+ struct sk_buff *skb;
+
+ skb = skb_dequeue(&tdev->xq);
+ dev_kfree_skb(skb);
+ }
+
+ /* Queue it for processing. */
+ skb_queue_tail(&tdev->xq, skb);
+
+ if (tdev->read_blocked)
+ {
+ tdev->read_blocked = 0;
+ pthread_cond_broadcast (&tdev->wait);
+ pthread_cond_broadcast (&tdev->select_alert);
+ }
+
+ pthread_mutex_unlock (&tdev->lock);
+
+ return 0;
+}
+
+void
+setup_tunnel_device (char *name, struct device **device)
+{
+ error_t err;
+ struct tunnel_device *tdev;
+ struct device *dev;
+ char *base_name;
+
+ /* Do global initialization before setting up first tunnel device. */
+ if (!tunnel_dev)
+ {
+ trivfs_add_control_port_class (&tunnel_cntlclass);
+ trivfs_add_protid_port_class (&tunnel_class);
+ }
+
+ tdev = calloc (1, sizeof (struct tunnel_device));
+ if (!tdev)
+ error (2, ENOMEM, "%s", name);
+ tdev->next = tunnel_dev;
+ tunnel_dev = tdev;
+
+ *device = dev = &tdev->dev;
+
+ base_name = strrchr (name, '/');
+ if (base_name)
+ base_name++;
+ else
+ base_name = name;
+
+ dev->name = strdup (base_name);
+
+ dev->priv = tdev;
+ dev->get_stats = tunnel_get_stats;
+
+ /* Functions. These ones are the true "hardware layer" in Linux. */
+ dev->open = tunnel_open;
+ dev->stop = tunnel_stop;
+ dev->hard_start_xmit = tunnel_xmit;
+ dev->set_multicast_list = tunnel_set_multi;
+
+ /* These are the ones set by drivers/net/ppp_generic.c::ppp_net_init. */
+ dev->hard_header = 0;
+ dev->hard_header_len = 0;
+ dev->mtu = PPP_MTU;
+ dev->addr_len = 0;
+ dev->tx_queue_len = 3;
+ dev->type = ARPHRD_PPP;
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+
+ dev_init_buffers (dev);
+
+ if (base_name != name)
+ tdev->devname = strdup (name);
+ else
+ /* Setting up the translator at /dev/tunX. */
+ asprintf (&tdev->devname, "/dev/%s", tdev->dev.name);
+ tdev->underlying = file_name_lookup (tdev->devname, O_CREAT|O_NOTRANS, 0664);
+
+ if (tdev->underlying == MACH_PORT_NULL)
+ error (2, /* XXX */ 1, "%s", tdev->dev.name);
+
+ err = trivfs_create_control (tdev->underlying, tunnel_cntlclass,
+ pfinet_bucket, tunnel_class, pfinet_bucket,
+ &tdev->cntl);
+ tdev->cntl->hook = tdev;
+
+ if (! err)
+ {
+ mach_port_t right = ports_get_send_right (tdev->cntl);
+ err = file_set_translator (tdev->underlying, 0, FS_TRANS_EXCL
+ | FS_TRANS_SET, 0, 0, 0, right,
+ MACH_MSG_TYPE_COPY_SEND);
+ mach_port_deallocate (mach_task_self (), right);
+ }
+
+ if (err)
+ error (2, err, "%s", tdev->dev.name);
+
+ pthread_mutex_init (&tdev->lock, NULL);
+ pthread_cond_init (&tdev->wait, NULL);
+ pthread_cond_init (&tdev->select_alert, NULL);
+
+ /* This call adds the device to the `dev_base' chain,
+ initializes its `ifindex' member (which matters!),
+ and tells the protocol stacks about the device. */
+ err = - register_netdevice (dev);
+ assert_perror (err);
+}
+
+/* If a new open with read and/or write permissions is requested,
+ restrict to exclusive usage. */
+static error_t
+check_open_hook (struct trivfs_control *cntl,
+ struct iouser *user,
+ int flags)
+{
+ struct tunnel_device *tdev;
+
+ for (tdev = tunnel_dev; tdev; tdev = tdev->next)
+ if (tdev->cntl == cntl)
+ break;
+
+ if (tdev && flags != O_NORW)
+ {
+ if (tdev->user)
+ return EBUSY;
+ else
+ tdev->user = user;
+ }
+ return 0;
+}
+
+/* When a protid is destroyed, check if it is the current user.
+ If yes, release the interface for other users. */
+static void
+pi_destroy_hook (struct trivfs_protid *cred)
+{
+ struct tunnel_device *tdev;
+
+ if (cred->pi.class != tunnel_class)
+ return;
+
+ tdev = (struct tunnel_device *) cred->po->cntl->hook;
+
+ if (tdev->user == cred->user)
+ tdev->user = 0;
+}
+
+/* If this variable is set, it is called every time a new peropen
+ structure is created and initialized. */
+error_t (*trivfs_check_open_hook)(struct trivfs_control *,
+ struct iouser *, int)
+ = check_open_hook;
+
+/* If this variable is set, it is called every time a protid structure
+ is about to be destroyed. */
+void (*trivfs_protid_destroy_hook) (struct trivfs_protid *) = pi_destroy_hook;
+
+/* Read data from an IO object. If offset is -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMOUNT. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, mach_msg_type_number_t *data_len,
+ loff_t offs, size_t amount)
+{
+ struct tunnel_device *tdev;
+ struct sk_buff *skb;
+
+ /* Deny access if they have bad credentials. */
+ if (! cred)
+ return EOPNOTSUPP;
+ else if (! (cred->po->openmodes & O_READ))
+ return EBADF;
+
+ if (cred->pi.class != tunnel_class)
+ return EOPNOTSUPP;
+
+ tdev = (struct tunnel_device *) cred->po->cntl->hook;
+
+ pthread_mutex_lock (&tdev->lock);
+
+ while (skb_queue_len(&tdev->xq) == 0)
+ {
+ if (cred->po->openmodes & O_NONBLOCK)
+ {
+ pthread_mutex_unlock (&tdev->lock);
+ return EWOULDBLOCK;
+ }
+
+ tdev->read_blocked = 1;
+ if (pthread_hurd_cond_wait_np (&tdev->wait, &tdev->lock))
+ {
+ pthread_mutex_unlock (&tdev->lock);
+ return EINTR;
+ }
+ /* See term/users.c for possible race? */
+ }
+
+ skb = skb_dequeue (&tdev->xq);
+ assert(skb);
+
+ if (skb->len < amount)
+ amount = skb->len;
+ if (amount > 0)
+ {
+ /* Possibly allocate a new buffer. */
+ if (*data_len < amount)
+ {
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ {
+ dev_kfree_skb (skb);
+ pthread_mutex_unlock (&tdev->lock);
+ return ENOMEM;
+ }
+ }
+
+ /* Copy the constant data into the buffer. */
+ memcpy ((char *) *data, skb->data, amount);
+ }
+ *data_len = amount;
+ dev_kfree_skb (skb);
+
+ /* Set atime, see term/users.c */
+
+ pthread_mutex_unlock (&tdev->lock);
+
+ return 0;
+}
+
+/* Write data to an IO object. If offset is -1, write at the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount successfully written is returned in amount. A
+ given user should not have more than one outstanding io_write on an
+ object at a time; servers implement congestion control by delaying
+ responses to io_write. Servers may drop data (returning ENOBUFS)
+ if they receive more than one write when not prepared for it. */
+error_t
+trivfs_S_io_write (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ char *data,
+ mach_msg_type_number_t datalen,
+ off_t offset,
+ mach_msg_type_number_t *amount)
+{
+ struct tunnel_device *tdev;
+ struct sk_buff *skb;
+
+ /* Deny access if they have bad credentials. */
+ if (! cred)
+ return EOPNOTSUPP;
+ else if (! (cred->po->openmodes & O_WRITE))
+ return EBADF;
+
+ if (cred->pi.class != tunnel_class)
+ return EOPNOTSUPP;
+
+ tdev = (struct tunnel_device *) cred->po->cntl->hook;
+
+ pthread_mutex_lock (&tdev->lock);
+
+ pthread_mutex_lock (&net_bh_lock);
+ skb = alloc_skb (datalen, GFP_ATOMIC);
+ skb->len = datalen;
+ skb->dev = &tdev->dev;
+
+ bcopy (data, skb->data, datalen);
+
+ /* Drop it on the queue. */
+ skb->mac.raw = skb->data;
+ skb->protocol = htons (ETH_P_IP);
+ netif_rx (skb);
+ pthread_mutex_unlock (&net_bh_lock);
+
+ *amount = datalen;
+
+ pthread_mutex_unlock (&tdev->lock);
+ return 0;
+}
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+kern_return_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ mach_msg_type_number_t *amount)
+{
+ struct tunnel_device *tdev;
+ struct sk_buff *skb;
+
+ /* Deny access if they have bad credentials. */
+ if (! cred)
+ return EOPNOTSUPP;
+ else if (! (cred->po->openmodes & O_READ))
+ return EBADF;
+
+ if (cred->pi.class != tunnel_class)
+ return EOPNOTSUPP;
+
+ tdev = (struct tunnel_device *) cred->po->cntl->hook;
+
+ pthread_mutex_lock (&tdev->lock);
+
+ /* XXX: Now return the length of the next entry in the queue.
+ From the BSD manual:
+ The tunnel device, normally /dev/tunN, is exclusive-open (it cannot be
+ opened if it is already open) and is restricted to the super-user. A
+ read() call will return an error (EHOSTDOWN) if the interface is not
+ ``ready'' address has been set (which means that the control device is
+ open and the interface's). Once the interface is ready, read() will re-
+ turn a packet if one is available; if not, it will either block until one
+ is or return EWOULDBLOCK, depending on whether non-blocking I/O has been
+ enabled. If the packet is longer than is allowed for in the buffer
+ passed to read(), the extra data will be silently dropped.
+ */
+
+ skb = skb_dequeue(&tdev->xq);
+ if (skb)
+ {
+ *amount = skb->len;
+ skb_queue_head(&tdev->xq, skb);
+ }
+ else
+ *amount = 0;
+
+ pthread_mutex_unlock (&tdev->lock);
+
+ return 0;
+}
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. ID_TAG is returned as passed; it
+ is just for the convenience of the user in matching up reply messages with
+ specific requests sent. */
+static error_t
+io_select_common (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ struct timespec *tsp, int *type)
+{
+ struct tunnel_device *tdev;
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != tunnel_class)
+ return EOPNOTSUPP;
+
+ tdev = (struct tunnel_device *) cred->po->cntl->hook;
+
+ /* We only deal with SELECT_READ and SELECT_WRITE here. */
+ *type &= SELECT_READ | SELECT_WRITE;
+
+ if (*type == 0)
+ return 0;
+
+ pthread_mutex_lock (&tdev->lock);
+
+ if (*type & SELECT_WRITE)
+ {
+ /* We are always writable. */
+ if (skb_queue_len (&tdev->xq) == 0)
+ *type &= ~SELECT_READ;
+ pthread_mutex_unlock (&tdev->lock);
+ return 0;
+ }
+
+ while (1)
+ {
+ if (skb_queue_len (&tdev->xq) != 0)
+ {
+ *type = SELECT_READ;
+ pthread_mutex_unlock (&tdev->lock);
+ return 0;
+ }
+
+ ports_interrupt_self_on_port_death (cred, reply);
+ tdev->read_blocked = 1;
+ err = pthread_hurd_cond_timedwait_np (&tdev->select_alert, &tdev->lock,
+ tsp);
+ if (err)
+ {
+ *type = 0;
+ pthread_mutex_unlock (&tdev->lock);
+
+ if (err == ETIMEDOUT)
+ err = 0;
+
+ return err;
+ }
+ }
+}
+
+error_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int *type)
+{
+ return io_select_common (cred, reply, reply_type, NULL, type);
+}
+
+error_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ struct timespec ts,
+ int *type)
+{
+ return io_select_common (cred, reply, reply_type, &ts, type);
+}
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offs, int whence, off_t *new_offs)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != tunnel_class)
+ return EOPNOTSUPP;
+
+ return ESPIPE;
+}
+
+/* Change the size of the file. If the size increases, new blocks are
+ zero-filled. After successful return, it is safe to reference mapped
+ areas of the file up to NEW_SIZE. */
+error_t
+trivfs_S_file_set_size (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t size)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != tunnel_class)
+ return EOPNOTSUPP;
+
+ return size == 0 ? 0 : EINVAL;
+}
+
+/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and
+ O_NONBLOCK bits for the IO object. In addition, io_get_openmodes
+ will tell you which of O_READ, O_WRITE, and O_EXEC the object can
+ be used for. The O_ASYNC bit affects icky async I/O; good async
+ I/O is done through io_async which is orthogonal to these calls. */
+error_t
+trivfs_S_io_set_all_openmodes(struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int mode)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != tunnel_class)
+ return EOPNOTSUPP;
+
+ return 0;
+}
+
+error_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != tunnel_class)
+ return EOPNOTSUPP;
+
+ return 0;
+}
+
+error_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != tunnel_class)
+ return EOPNOTSUPP;
+
+ return 0;
+}
+
+error_t
+trivfs_S_io_get_owner (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ pid_t *owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != tunnel_class)
+ return EOPNOTSUPP;
+
+ *owner = 0;
+ return 0;
+}
+
+error_t
+trivfs_S_io_mod_owner (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ pid_t owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != tunnel_class)
+ return EOPNOTSUPP;
+
+ return EINVAL;
+}
+
+/* Return objects mapping the data underlying this memory object. If
+ the object can be read then memobjrd will be provided; if the
+ object can be written then memobjwr will be provided. For objects
+ where read data and write data are the same, these objects will be
+ equal, otherwise they will be disjoint. Servers are permitted to
+ implement io_map but not io_map_cntl. Some objects do not provide
+ mapping; they will set none of the ports and return an error. Such
+ objects can still be accessed by io_read and io_write. */
+error_t
+trivfs_S_io_map (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replyPoly,
+ memory_object_t *rdobj,
+ mach_msg_type_name_t *rdtype,
+ memory_object_t *wrobj,
+ mach_msg_type_name_t *wrtype)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != tunnel_class)
+ return EOPNOTSUPP;
+
+ return EINVAL;
+}
diff --git a/pflocal/Makefile b/pflocal/Makefile
new file mode 100644
index 00000000..bfc2f4e8
--- /dev/null
+++ b/pflocal/Makefile
@@ -0,0 +1,33 @@
+# Makefile for pflocal
+#
+# Copyright (C) 1995, 1996, 2000, 2012 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := pflocal
+makemode := server
+
+target = pflocal
+
+SRCS = connq.c io.c pflocal.c socket.c pf.c sock.c sserver.c
+
+MIGSTUBS = ioServer.o socketServer.o
+OBJS = $(SRCS:.c=.o) $(MIGSTUBS)
+HURDLIBS = pipe trivfs iohelp fshelp ports ihash shouldbeinlibc
+OTHERLIBS = -lpthread
+
+MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+
+include ../Makeconf
diff --git a/pflocal/connq.c b/pflocal/connq.c
new file mode 100644
index 00000000..d86f9a24
--- /dev/null
+++ b/pflocal/connq.c
@@ -0,0 +1,327 @@
+/* Listen queue functions
+
+ Copyright (C) 1995,96,2001,2012 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <pthread.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "connq.h"
+
+/* A queue for queueing incoming connections. */
+struct connq
+{
+ /* The connection request queue. */
+ struct connq_request *head;
+ struct connq_request **tail;
+ unsigned count;
+ unsigned max;
+
+ /* Threads that have done an accept on this queue wait on this condition. */
+ pthread_cond_t listeners;
+ unsigned num_listeners;
+
+ /* Threads that have done a connect on this queue wait on this condition. */
+ pthread_cond_t connectors;
+ unsigned num_connectors;
+
+ pthread_mutex_t lock;
+};
+
+/* ---------------------------------------------------------------- */
+
+/* A data block allocated by a thread waiting on a connq, which is used to
+ get information from and to the thread. */
+struct connq_request
+{
+ struct connq_request *next;
+
+ /* The socket that's waiting to connect. */
+ struct sock *sock;
+};
+
+static inline void
+connq_request_init (struct connq_request *req, struct sock *sock)
+{
+ req->sock = sock;
+}
+
+/* Enqueue connection request REQ onto CQ. CQ must be locked. */
+static void
+connq_request_enqueue (struct connq *cq, struct connq_request *req)
+{
+ assert (pthread_mutex_trylock (&cq->lock));
+
+ req->next = NULL;
+ *cq->tail = req;
+ cq->tail = &req->next;
+
+ cq->count ++;
+}
+
+/* Dequeue a pending request from CQ. CQ must be locked and must not
+ be empty. */
+static struct connq_request *
+connq_request_dequeue (struct connq *cq)
+{
+ struct connq_request *req;
+
+ assert (pthread_mutex_trylock (&cq->lock));
+ assert (cq->head);
+
+ req = cq->head;
+ cq->head = req->next;
+ if (! cq->head)
+ /* We just dequeued the last element. Fixup the tail pointer. */
+ cq->tail = &cq->head;
+
+ cq->count --;
+
+ return req;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Create a new listening queue, returning it in CQ. The resulting queue
+ will be of zero length, that is it won't allow connections unless someone
+ is already listening (change this with connq_set_length). */
+error_t
+connq_create (struct connq **cq)
+{
+ struct connq *new = malloc (sizeof (struct connq));
+
+ if (!new)
+ return ENOBUFS;
+
+ new->head = NULL;
+ new->tail = &new->head;
+ new->count = 0;
+ /* By default, don't queue requests. */
+ new->max = 0;
+
+ new->num_listeners = 0;
+ new->num_connectors = 0;
+
+ pthread_mutex_init (&new->lock, NULL);
+ pthread_cond_init (&new->listeners, NULL);
+ pthread_cond_init (&new->connectors, NULL);
+
+ *cq = new;
+ return 0;
+}
+
+/* Destroy a queue. */
+void
+connq_destroy (struct connq *cq)
+{
+ /* Everybody in the queue should hold a reference to the socket
+ containing the queue. */
+ assert (! cq->head);
+ assert (cq->count == 0);
+
+ free (cq);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Return a connection request on CQ. If SOCK is NULL, the request is
+ left in the queue. If TIMEOUT denotes a value of 0, EWOULDBLOCK is
+ returned when there are no immediate connections available.
+ Otherwise this value is used to limit the wait duration. If TIMEOUT
+ is NULL, the wait duration isn't bounded. */
+error_t
+connq_listen (struct connq *cq, struct timespec *tsp, struct sock **sock)
+{
+ error_t err = 0;
+
+ pthread_mutex_lock (&cq->lock);
+
+ if (tsp && tsp->tv_sec == 0 && tsp->tv_nsec == 0 && cq->count == 0
+ && cq->num_connectors == 0)
+ {
+ pthread_mutex_unlock (&cq->lock);
+ return EWOULDBLOCK;
+ }
+
+ if (! sock && (cq->count > 0 || cq->num_connectors > 0))
+ /* The caller just wants to know if a connection ready. */
+ {
+ pthread_mutex_unlock (&cq->lock);
+ return 0;
+ }
+
+ cq->num_listeners++;
+
+ if (cq->count == 0)
+ /* The request queue is empty. */
+ {
+ assert (! cq->head);
+
+ if (cq->num_connectors > 0)
+ /* Someone is waiting for an acceptor. Signal that we can
+ service their request. */
+ pthread_cond_signal (&cq->connectors);
+
+ do
+ {
+ err = pthread_hurd_cond_timedwait_np (&cq->listeners, &cq->lock, tsp);
+ if (err)
+ {
+ cq->num_listeners--;
+ goto out;
+ }
+ }
+ while (cq->count == 0);
+ }
+
+ assert (cq->head);
+
+ if (sock)
+ /* Dequeue the next request, if desired. */
+ {
+ struct connq_request *req = connq_request_dequeue (cq);
+ *sock = req->sock;
+ free (req);
+ }
+ else if (cq->num_listeners > 0)
+ /* The caller will not actually process this request but someone
+ else could. (This case is rare but possible: it would require
+ one thread to do a select on the socket and a second to do an
+ accept.) */
+ pthread_cond_signal (&cq->listeners);
+ else
+ /* There is no one else to process the request and the connection
+ has now been initiated. This is not actually a problem as even
+ if the current queue limit is 0, the connector will queue the
+ request and another listener (should) eventually come along.
+ (In fact it is very probably as the caller has likely done a
+ select and will now follow up with an accept.) */
+ { /* Do nothing. */ }
+
+ out:
+ pthread_mutex_unlock (&cq->lock);
+ return err;
+}
+
+/* Try to connect SOCK with the socket listening on CQ. If NOBLOCK is
+ true, then return EWOULDBLOCK if there are no connections
+ immediately available. On success, this call must be followed up
+ either connq_connect_complete or connq_connect_cancel. */
+error_t
+connq_connect (struct connq *cq, int noblock)
+{
+ pthread_mutex_lock (&cq->lock);
+
+ /* Check for listeners after we've locked CQ for good. */
+
+ if (noblock
+ && cq->count + cq->num_connectors >= cq->max + cq->num_listeners)
+ /* We are in non-blocking mode and would have to wait to secure an
+ entry in the listen queue. */
+ {
+ pthread_mutex_unlock (&cq->lock);
+ return EWOULDBLOCK;
+ }
+
+ cq->num_connectors ++;
+
+ while (cq->count + cq->num_connectors > cq->max + cq->num_listeners)
+ /* The queue is full and there is no immediate listener to service
+ us. Block until we can get a slot. */
+ if (pthread_hurd_cond_wait_np (&cq->connectors, &cq->lock))
+ {
+ cq->num_connectors --;
+ pthread_mutex_unlock (&cq->lock);
+ return EINTR;
+ }
+
+ pthread_mutex_unlock (&cq->lock);
+
+ return 0;
+}
+
+/* Follow up to connq_connect. Completes the connect, SOCK is the new
+ server socket. */
+void
+connq_connect_complete (struct connq *cq, struct sock *sock)
+{
+ struct connq_request *req;
+
+ req = malloc (sizeof (struct connq_request));
+ if (! req)
+ abort ();
+
+ connq_request_init (req, sock);
+
+ pthread_mutex_lock (&cq->lock);
+
+ assert (cq->num_connectors > 0);
+ cq->num_connectors --;
+
+ connq_request_enqueue (cq, req);
+
+ if (cq->num_listeners > 0)
+ /* Wake a listener up. We must consume the listener ref here as
+ someone else might call this function before the listener
+ thread dequeues this request. */
+ {
+ cq->num_listeners --;
+ pthread_cond_signal (&cq->listeners);
+ }
+
+ pthread_mutex_unlock (&cq->lock);
+}
+
+/* Follow up to connq_connect. Cancel the connect. */
+void
+connq_connect_cancel (struct connq *cq)
+{
+ pthread_mutex_lock (&cq->lock);
+
+ assert (cq->num_connectors > 0);
+ cq->num_connectors --;
+
+ if (cq->count + cq->num_connectors >= cq->max + cq->num_listeners)
+ /* A connector is blocked and could use the spot we reserved. */
+ pthread_cond_signal (&cq->connectors);
+
+ pthread_mutex_unlock (&cq->lock);
+}
+
+/* Set CQ's queue length to LENGTH. */
+error_t
+connq_set_length (struct connq *cq, int max)
+{
+ int omax;
+
+ pthread_mutex_lock (&cq->lock);
+ omax = cq->max;
+ cq->max = max;
+
+ if (max > omax && cq->count >= omax && cq->count < max
+ && cq->num_connectors >= cq->num_listeners)
+ /* This is an increase in the number of connection slots which has
+ made some slots available and there are waiting threads. Wake
+ them up. */
+ pthread_cond_broadcast (&cq->listeners);
+
+ pthread_mutex_unlock (&cq->lock);
+
+ return 0;
+}
diff --git a/pflocal/connq.h b/pflocal/connq.h
new file mode 100644
index 00000000..71583800
--- /dev/null
+++ b/pflocal/connq.h
@@ -0,0 +1,63 @@
+/* Connection queues
+
+ Copyright (C) 1995, 2012 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __CONNQ_H__
+#define __CONNQ_H__
+
+#include <errno.h>
+
+/* Forward. */
+struct connq;
+struct sock;
+
+/* Create a new listening queue, returning it in CQ. The resulting queue
+ will be of zero length, that is it won't allow connections unless someone
+ is already listening (change this with connq_set_length). */
+error_t connq_create (struct connq **cq);
+
+/* Destroy a queue. */
+void connq_destroy (struct connq *cq);
+
+/* Return a connection request on CQ. If SOCK is NULL, the request is
+ left in the queue. If TIMEOUT denotes a value of 0, EWOULDBLOCK is
+ returned when there are no immediate connections available.
+ Otherwise this value is used to limit the wait duration. If TIMEOUT
+ is NULL, the wait duration isn't bounded. */
+error_t connq_listen (struct connq *cq, struct timespec *tsp,
+ struct sock **sock);
+
+/* Try to connect SOCK with the socket listening on CQ. If NOBLOCK is
+ true, then return EWOULDBLOCK if there are no connections
+ immediately available. On success, this call must be followed up
+ either connq_connect_complete or connq_connect_cancel. */
+error_t connq_connect (struct connq *cq, int noblock);
+
+/* Follow up to connq_connect. Completes the connection, SOCK is the
+ new server socket. */
+void connq_connect_complete (struct connq *cq, struct sock *sock);
+
+/* Follow up to connq_connect. Cancel the connect. */
+void connq_connect_cancel (struct connq *cq);
+
+/* Set CQ's queue length to LENGTH. Any sockets already waiting for a
+ connections that are past the new length remain. */
+error_t connq_set_length (struct connq *cq, int length);
+
+#endif /* __CONNQ_H__ */
diff --git a/pflocal/io.c b/pflocal/io.c
new file mode 100644
index 00000000..00e859a4
--- /dev/null
+++ b/pflocal/io.c
@@ -0,0 +1,657 @@
+/* Socket I/O operations
+
+ Copyright (C) 1995, 1996, 1998, 1999, 2000, 2002, 2007, 2012
+ Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h> /* For bzero() */
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <hurd.h> /* for getauth() */
+#include <hurd/hurd_types.h>
+#include <hurd/auth.h>
+#include <hurd/pipe.h>
+#include <mach/notify.h>
+
+#include "sock.h"
+#include "connq.h"
+#include "sserver.h"
+
+#include "io_S.h"
+#include "interrupt_S.h"
+
+/* Read data from an IO object. If offset if -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in amount. */
+error_t
+S_io_read (struct sock_user *user,
+ char **data, mach_msg_type_number_t *data_len,
+ off_t offset, mach_msg_type_number_t amount)
+{
+ error_t err;
+ struct pipe *pipe;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ err = sock_acquire_read_pipe (user->sock, &pipe);
+ if (err == EPIPE)
+ /* EOF */
+ {
+ err = 0;
+ *data_len = 0;
+ }
+ else if (!err)
+ {
+ err =
+ pipe_read (pipe, user->sock->flags & PFLOCAL_SOCK_NONBLOCK, NULL,
+ data, data_len, amount);
+ pipe_release_reader (pipe);
+ }
+
+ return err;
+}
+
+/* Write data to an IO object. If offset is -1, write at the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount successfully written is returned in amount. A
+ given user should not have more than one outstanding io_write on an
+ object at a time; servers implement congestion control by delaying
+ responses to io_write. Servers may drop data (returning ENOBUFS)
+ if they recevie more than one write when not prepared for it. */
+error_t
+S_io_write (struct sock_user *user,
+ char *data, mach_msg_type_number_t data_len,
+ off_t offset, mach_msg_type_number_t *amount)
+{
+ error_t err;
+ struct pipe *pipe;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ err = sock_acquire_write_pipe (user->sock, &pipe);
+ if (!err)
+ {
+ struct addr *source_addr;
+
+ /* We could provide a source address for all writes, but we only do so
+ for connectionless sockets because that's the only place it's
+ required, and it's more efficient not to. */
+ if (pipe->class->flags & PIPE_CLASS_CONNECTIONLESS)
+ err = sock_get_addr (user->sock, &source_addr);
+ else
+ source_addr = NULL;
+
+ if (!err)
+ {
+ err = pipe_write (pipe, user->sock->flags & PFLOCAL_SOCK_NONBLOCK,
+ source_addr, data, data_len, amount);
+ if (err && source_addr)
+ ports_port_deref (source_addr);
+ }
+
+ pipe_release_writer (pipe);
+ }
+
+ return err;
+}
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+error_t
+S_io_readable (struct sock_user *user, mach_msg_type_number_t *amount)
+{
+ error_t err;
+ struct pipe *pipe;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ err = sock_acquire_read_pipe (user->sock, &pipe);
+ if (err == EPIPE)
+ /* EOF */
+ {
+ err = 0;
+ *amount = 0;
+ }
+ else if (!err)
+ {
+ *amount = pipe_readable (user->sock->read_pipe, 1);
+ pipe_release_reader (pipe);
+ }
+
+ return err;
+}
+
+/* Change current read/write offset */
+error_t
+S_io_seek (struct sock_user *user,
+ off_t offset, int whence, off_t *new_offset)
+{
+ return user ? ESPIPE : EOPNOTSUPP;
+}
+
+/* Return a new port with the same semantics as the existing port. */
+error_t
+S_io_duplicate (struct sock_user *user,
+ mach_port_t *new_port, mach_msg_type_name_t *new_port_type)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ err = sock_create_port (user->sock, new_port);
+ if (! err)
+ *new_port_type = MACH_MSG_TYPE_MAKE_SEND;
+
+ return err;
+}
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. */
+static error_t
+io_select_common (struct sock_user *user,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec *tsp, int *select_type)
+{
+ error_t err = 0;
+ struct sock *sock;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ *select_type &= SELECT_READ | SELECT_WRITE;
+
+ sock = user->sock;
+ pthread_mutex_lock (&sock->lock);
+
+ if (sock->listen_queue)
+ /* Sock is used for accepting connections, not I/O. For these, you can
+ only select for reading, which will block until a connection request
+ comes along. */
+ {
+ pthread_mutex_unlock (&sock->lock);
+
+ *select_type &= SELECT_READ;
+
+ if (*select_type & SELECT_READ)
+ {
+ struct timespec noblock = {0, 0};
+
+ /* Wait for a connect. Passing in NULL for SOCK means that
+ the request won't be dequeued. */
+ if (connq_listen (sock->listen_queue, &noblock, NULL) == 0)
+ /* We can satisfy this request immediately. */
+ return 0;
+ else
+ /* Gotta wait... */
+ {
+ ports_interrupt_self_on_port_death (user, reply);
+ err = connq_listen (sock->listen_queue, tsp, NULL);
+ if (err == ETIMEDOUT)
+ {
+ *select_type = 0;
+ err = 0;
+ }
+ return err;
+ }
+ }
+ }
+ else
+ /* Sock is a normal read/write socket. */
+ {
+ int valid;
+ int ready = 0;
+ struct pipe *read_pipe = sock->read_pipe;
+ struct pipe *write_pipe = sock->write_pipe;
+
+ if (! write_pipe)
+ ready |= SELECT_WRITE;
+ if (! read_pipe)
+ ready |= SELECT_READ;
+ ready &= *select_type; /* Only keep things requested. */
+ *select_type &= ~ready;
+
+ valid = *select_type;
+ if (valid & SELECT_READ)
+ {
+ pipe_acquire_reader (read_pipe);
+ err = pipe_wait_readable (read_pipe, 1, 1);
+ if (err == EWOULDBLOCK)
+ err = 0; /* Not readable, actually not an error. */
+ else
+ ready |= SELECT_READ; /* Data immediately readable (or error). */
+ pthread_mutex_unlock (&read_pipe->lock);
+ if (err)
+ /* Prevent write test from overwriting err. */
+ valid &= ~SELECT_WRITE;
+ }
+ if (valid & SELECT_WRITE)
+ {
+ pipe_acquire_writer (write_pipe);
+ err = pipe_wait_writable (write_pipe, 1);
+ if (err == EWOULDBLOCK)
+ err = 0; /* Not writable, actually not an error. */
+ else
+ ready |= SELECT_WRITE; /* Data immediately writable (or error). */
+ pthread_mutex_unlock (&write_pipe->lock);
+ }
+
+ pthread_mutex_unlock (&sock->lock);
+
+ if (ready)
+ /* No need to block, we've already got some results. */
+ *select_type = ready;
+ else
+ /* Wait for something to change. */
+ {
+ ports_interrupt_self_on_port_death (user, reply);
+ err = pipe_pair_select (read_pipe, write_pipe, tsp, select_type, 1);
+ }
+
+ if (valid & SELECT_READ)
+ pipe_remove_reader (read_pipe);
+ if (valid & SELECT_WRITE)
+ pipe_remove_writer (write_pipe);
+ }
+
+ return err;
+}
+
+error_t
+S_io_select (struct sock_user *user,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *select_type)
+{
+ return io_select_common (user, reply, reply_type, NULL, select_type);
+}
+
+error_t
+S_io_select_timeout (struct sock_user *user,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec ts,
+ int *select_type)
+{
+ return io_select_common (user, reply, reply_type, &ts, select_type);
+}
+
+/* Return the current status of the object. Not all the fields of the
+ io_statuf_t are meaningful for all objects; however, the access and
+ modify times, the optimal IO size, and the fs type are meaningful
+ for all objects. */
+error_t
+S_io_stat (struct sock_user *user, struct stat *st)
+{
+ struct sock *sock;
+ struct pipe *rpipe, *wpipe;
+
+ void copy_time (time_value_t *from, time_t *to_sec, long *to_nsec)
+ {
+ *to_sec = from->seconds;
+ *to_nsec = from->microseconds * 1000;
+ }
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ sock = user->sock;
+
+ bzero (st, sizeof (struct stat));
+
+ st->st_fstype = FSTYPE_SOCKET;
+ st->st_mode = sock->mode;
+ st->st_fsid = getpid ();
+ st->st_ino = sock->id;
+ /* As we try to be clever with large transfers, ask for them. */
+ st->st_blksize = vm_page_size * 16;
+
+ pthread_mutex_lock (&sock->lock); /* Make sure the pipes don't go away... */
+
+ rpipe = sock->read_pipe;
+ wpipe = sock->write_pipe;
+
+ if (rpipe)
+ {
+ pthread_mutex_lock (&rpipe->lock);
+ copy_time (&rpipe->read_time, &st->st_atim.tv_sec, &st->st_atim.tv_nsec);
+ /* This seems useful. */
+ st->st_size = pipe_readable (rpipe, 1);
+ pthread_mutex_unlock (&rpipe->lock);
+ }
+
+ if (wpipe)
+ {
+ pthread_mutex_lock (&wpipe->lock);
+ copy_time (&wpipe->write_time, &st->st_mtim.tv_sec, &st->st_mtim.tv_nsec);
+ pthread_mutex_unlock (&wpipe->lock);
+ }
+
+ copy_time (&sock->change_time, &st->st_ctim.tv_sec, &st->st_ctim.tv_nsec);
+
+ pthread_mutex_unlock (&sock->lock);
+
+ return 0;
+}
+
+error_t
+S_io_get_openmodes (struct sock_user *user, int *bits)
+{
+ unsigned flags;
+ if (!user)
+ return EOPNOTSUPP;
+ flags = user->sock->flags;
+ *bits =
+ O_APPEND /* pipes always append */
+ | (flags & PFLOCAL_SOCK_NONBLOCK ? O_NONBLOCK : 0)
+ | (flags & PFLOCAL_SOCK_SHUTDOWN_READ ? 0 : O_READ)
+ | (flags & PFLOCAL_SOCK_SHUTDOWN_WRITE ? 0 : O_WRITE);
+ return 0;
+}
+
+error_t
+S_io_set_all_openmodes (struct sock_user *user, int bits)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->sock->lock);
+ if (bits & O_NONBLOCK)
+ user->sock->flags |= PFLOCAL_SOCK_NONBLOCK;
+ else
+ user->sock->flags &= ~PFLOCAL_SOCK_NONBLOCK;
+ pthread_mutex_unlock (&user->sock->lock);
+
+ return 0;
+}
+
+error_t
+S_io_set_some_openmodes (struct sock_user *user, int bits)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->sock->lock);
+ if (bits & O_NONBLOCK)
+ user->sock->flags |= PFLOCAL_SOCK_NONBLOCK;
+ pthread_mutex_unlock (&user->sock->lock);
+
+ return 0;
+}
+
+error_t
+S_io_clear_some_openmodes (struct sock_user *user, int bits)
+{
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->sock->lock);
+ if (bits & O_NONBLOCK)
+ user->sock->flags &= ~PFLOCAL_SOCK_NONBLOCK;
+ pthread_mutex_unlock (&user->sock->lock);
+
+ return 0;
+}
+
+#define NIDS 10
+
+error_t
+S_io_reauthenticate (struct sock_user *user, mach_port_t rendezvous)
+{
+ error_t err;
+ mach_port_t auth_server;
+ mach_port_t new_user_port;
+ uid_t uids_buf[NIDS], aux_uids_buf[NIDS];
+ uid_t *uids = uids_buf, *aux_uids = aux_uids_buf;
+ gid_t gids_buf[NIDS], aux_gids_buf[NIDS];
+ gid_t *gids = gids_buf, *aux_gids = aux_gids_buf;
+ size_t num_uids = NIDS, num_aux_uids = NIDS;
+ size_t num_gids = NIDS, num_aux_gids = NIDS;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ do
+ err = sock_create_port (user->sock, &new_user_port);
+ while (err == EINTR);
+ if (err)
+ return err;
+
+ auth_server = getauth ();
+ err = mach_port_insert_right (mach_task_self (), new_user_port,
+ new_user_port, MACH_MSG_TYPE_MAKE_SEND);
+ assert_perror (err);
+ do
+ err =
+ auth_server_authenticate (auth_server,
+ rendezvous, MACH_MSG_TYPE_COPY_SEND,
+ new_user_port, MACH_MSG_TYPE_COPY_SEND,
+ &uids, &num_uids, &aux_uids, &num_aux_uids,
+ &gids, &num_gids, &aux_gids, &num_aux_gids);
+ while (err == EINTR);
+ mach_port_deallocate (mach_task_self (), rendezvous);
+ mach_port_deallocate (mach_task_self (), auth_server);
+ mach_port_deallocate (mach_task_self (), new_user_port);
+
+ /* Throw away the ids we went through all that trouble to get... */
+#define TRASH_IDS(ids, buf, num) \
+ if (buf != ids) \
+ munmap (ids, num * sizeof (uid_t));
+
+ TRASH_IDS (uids, uids_buf, num_uids);
+ TRASH_IDS (gids, gids_buf, num_gids);
+ TRASH_IDS (aux_uids, aux_uids_buf, num_aux_uids);
+ TRASH_IDS (aux_gids, aux_gids_buf, num_aux_gids);
+
+ return err;
+}
+
+error_t
+S_io_restrict_auth (struct sock_user *user,
+ mach_port_t *new_port,
+ mach_msg_type_name_t *new_port_type,
+ uid_t *uids, size_t num_uids,
+ uid_t *gids, size_t num_gids)
+{
+ if (!user)
+ return EOPNOTSUPP;
+ *new_port_type = MACH_MSG_TYPE_MAKE_SEND;
+ return sock_create_port (user->sock, new_port);
+}
+
+error_t
+S_io_pathconf (struct sock_user *user, int name, int *value)
+{
+ if (user == NULL)
+ return EOPNOTSUPP;
+ else if (name == _PC_PIPE_BUF)
+ {
+ pthread_mutex_lock (&user->sock->lock);
+ if (user->sock->write_pipe == NULL)
+ *value = 0;
+ else
+ *value = user->sock->write_pipe->write_atomic;
+ pthread_mutex_unlock (&user->sock->lock);
+ return 0;
+ }
+ else
+ return EINVAL;
+}
+
+error_t
+S_io_identity (struct sock_user *user,
+ mach_port_t *id, mach_msg_type_name_t *id_type,
+ mach_port_t *fsys_id, mach_msg_type_name_t *fsys_id_type,
+ ino_t *fileno)
+{
+ static mach_port_t server_id = MACH_PORT_NULL;
+ error_t err = 0;
+ struct sock *sock;
+
+ if (! user)
+ return EOPNOTSUPP;
+
+ if (server_id == MACH_PORT_NULL)
+ {
+ static pthread_mutex_t server_id_lock = PTHREAD_MUTEX_INITIALIZER;
+
+ pthread_mutex_lock (&server_id_lock);
+ if (server_id == MACH_PORT_NULL) /* Recheck with the lock held. */
+ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &server_id);
+ pthread_mutex_unlock (&server_id_lock);
+
+ if (err)
+ return err;
+ }
+
+ sock = user->sock;
+
+ pthread_mutex_lock (&sock->lock);
+ if (sock->id == MACH_PORT_NULL)
+ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &sock->id);
+ pthread_mutex_unlock (&sock->lock);
+
+ if (! err)
+ {
+ *id = sock->id;
+ *id_type = MACH_MSG_TYPE_MAKE_SEND;
+ *fsys_id = server_id;
+ *fsys_id_type = MACH_MSG_TYPE_MAKE_SEND;
+ *fileno = sock->id; /* Might as well */
+ }
+
+ return err;
+}
+
+
+/* Stubs for currently unsupported rpcs. */
+
+error_t
+S_io_revoke (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_async(struct sock_user *user,
+ mach_port_t notify_port,
+ mach_port_t *async_id_port,
+ mach_msg_type_name_t *async_id_port_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_mod_owner(struct sock_user *user, pid_t owner)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_get_owner(struct sock_user *user, pid_t *owner)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_get_icky_async_id (struct sock_user *user,
+ mach_port_t *icky_async_id_port,
+ mach_msg_type_name_t *icky_async_id_port_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_map (struct sock_user *user,
+ mach_port_t *memobj_rd, mach_msg_type_name_t *memobj_rd_type,
+ mach_port_t *memobj_wt, mach_msg_type_name_t *memobj_wt_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_map_cntl (struct sock_user *user,
+ mach_port_t *mem, mach_msg_type_name_t *mem_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_get_conch (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_release_conch (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_eofnotify (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_prenotify (struct sock_user *user, vm_offset_t start, vm_offset_t end)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_postnotify (struct sock_user *user, vm_offset_t start, vm_offset_t end)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_readsleep (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_readnotify (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+
+error_t
+S_io_sigio (struct sock_user *user)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_io_server_version (struct sock_user *user,
+ char *name, int *maj, int *min, int *edit)
+{
+ return EOPNOTSUPP;
+}
diff --git a/pflocal/mig-decls.h b/pflocal/mig-decls.h
new file mode 100644
index 00000000..983de9d1
--- /dev/null
+++ b/pflocal/mig-decls.h
@@ -0,0 +1,59 @@
+/* Type decls for mig-produced server stubs
+
+ Copyright (C) 1995,2001 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __MIG_DECLS_H__
+#define __MIG_DECLS_H__
+
+#include "sock.h"
+
+/* For mig */
+
+typedef struct sock_user *sock_user_t;
+typedef struct addr *addr_t;
+
+static inline sock_user_t __attribute__ ((unused))
+begin_using_sock_user_port(mach_port_t port)
+{
+ return (sock_user_t)ports_lookup_port (0, port, sock_user_port_class);
+}
+
+static inline void __attribute__ ((unused))
+end_using_sock_user_port (sock_user_t sock_user)
+{
+ if (sock_user != NULL)
+ ports_port_deref (sock_user);
+}
+
+static inline addr_t __attribute__ ((unused))
+begin_using_addr_port(mach_port_t port)
+{
+ return (addr_t)ports_lookup_port (0, port, addr_port_class);
+}
+
+static inline void __attribute__ ((unused))
+end_using_addr_port (addr_t addr)
+{
+ if (addr != NULL)
+ ports_port_deref (addr);
+}
+
+#endif /* __MIG_DECLS_H__ */
diff --git a/pflocal/mig-mutate.h b/pflocal/mig-mutate.h
new file mode 100644
index 00000000..b1494730
--- /dev/null
+++ b/pflocal/mig-mutate.h
@@ -0,0 +1,33 @@
+/* Automagic type transformation for our mig interfaces
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Only CPP macro definitions should go in this file. */
+
+#define IO_SELECT_REPLY_PORT
+
+#define IO_INTRAN sock_user_t begin_using_sock_user_port (io_t)
+#define IO_DESTRUCTOR end_using_sock_user_port (sock_user_t)
+
+#define IO_IMPORTS import "mig-decls.h";
+
+#define SOCKET_INTRAN sock_user_t begin_using_sock_user_port (socket_t)
+#define SOCKET_DESTRUCTOR end_using_sock_user_port (sock_user_t)
+#define ADDRPORT_INTRAN addr_t begin_using_addr_port (addr_port_t)
+#define ADDRPORT_DESTRUCTOR end_using_addr_port (addr_t)
+
+#define SOCKET_IMPORTS import "mig-decls.h";
diff --git a/pflocal/pf.c b/pflocal/pf.c
new file mode 100644
index 00000000..55824d41
--- /dev/null
+++ b/pflocal/pf.c
@@ -0,0 +1,140 @@
+/* Protocol family operations
+
+ Copyright (C) 1995, 1999, 2000, 2008 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stddef.h>
+#include <sys/socket.h>
+#include <hurd/pipe.h>
+
+#include "sock.h"
+
+#include "socket_S.h"
+
+/* Create a new socket. Sock type is, for example, SOCK_STREAM,
+ SOCK_DGRAM, or some such. */
+error_t
+S_socket_create (mach_port_t pf,
+ int sock_type, int protocol,
+ mach_port_t *port, mach_msg_type_name_t *port_type)
+{
+ error_t err;
+ struct sock *sock;
+ struct pipe_class *pipe_class;
+ mode_t mode;
+
+ /* We have a set of `magic' protocols that allow the user to choose
+ the file type of the socket. The primary application is to make
+ sockets that pretend to be a FIFO, for the implementations of
+ pipes. */
+ switch (protocol)
+ {
+ case 0:
+ mode = S_IFSOCK;
+ break;
+ case S_IFCHR:
+ case S_IFSOCK:
+ case S_IFIFO:
+ mode = protocol;
+ break;
+ default:
+ return EPROTONOSUPPORT;
+ }
+
+ switch (sock_type)
+ {
+ case SOCK_STREAM:
+ pipe_class = stream_pipe_class; break;
+ case SOCK_DGRAM:
+ pipe_class = dgram_pipe_class; break;
+ case SOCK_SEQPACKET:
+ pipe_class = seqpack_pipe_class; break;
+ default:
+ return EPROTOTYPE;
+ }
+
+ err = sock_create (pipe_class, mode, &sock);
+ if (!err)
+ {
+ err = sock_create_port (sock, port);
+ if (err)
+ sock_free (sock);
+ else
+ *port_type = MACH_MSG_TYPE_MAKE_SEND;
+ }
+
+ return err;
+}
+
+error_t
+S_socket_create_address (mach_port_t pf, int sockaddr_type,
+ char *data, size_t data_len,
+ mach_port_t *addr_port,
+ mach_msg_type_name_t *addr_port_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_socket_fabricate_address (mach_port_t pf,
+ int sockaddr_type,
+ mach_port_t *addr_port,
+ mach_msg_type_name_t *addr_port_type)
+{
+ error_t err;
+ struct addr *addr;
+
+ if (sockaddr_type != AF_LOCAL)
+ return EAFNOSUPPORT;
+
+ err = addr_create (&addr);
+ if (err)
+ return err;
+
+ *addr_port = ports_get_right (addr);
+ *addr_port_type = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (addr);
+
+ return 0;
+}
+
+/* Implement socket_whatis_address as described in <hurd/socket.defs>.
+ Since we cannot tell what our address is, return an empty string as
+ the file name. This is primarily for the implementation of accept
+ and recvfrom. The functions getsockname and getpeername remain
+ unsupported for the local namespace. */
+error_t
+S_socket_whatis_address (struct addr *addr,
+ int *sockaddr_type,
+ char **sockaddr, size_t *sockaddr_len)
+{
+ socklen_t addr_len = (offsetof (struct sockaddr, sa_data) + 1);
+
+ if (! addr)
+ return EOPNOTSUPP;
+
+ *sockaddr_type = AF_LOCAL;
+ if (*sockaddr_len < addr_len)
+ *sockaddr = mmap (0, addr_len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ ((struct sockaddr *) *sockaddr)->sa_len = addr_len;
+ ((struct sockaddr *) *sockaddr)->sa_family = *sockaddr_type;
+ ((struct sockaddr *) *sockaddr)->sa_data[0] = 0;
+ *sockaddr_len = addr_len;
+
+ return 0;
+}
diff --git a/pflocal/pflocal.c b/pflocal/pflocal.c
new file mode 100644
index 00000000..fcb62d1d
--- /dev/null
+++ b/pflocal/pflocal.c
@@ -0,0 +1,139 @@
+/* A server for local sockets, of type PF_LOCAL
+
+ Copyright (C) 1995, 1997, 1998 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <error.h>
+#include <sys/stat.h>
+
+#include <hurd/hurd_types.h>
+#include <hurd/trivfs.h>
+
+#include "sock.h"
+
+/* Where to put the file-system ports. */
+static struct port_bucket *pf_port_bucket;
+
+/* Trivfs hooks */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = 0;
+
+/* Trivfs noise. */
+struct port_class *trivfs_protid_portclasses[1];
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_protid_nportclasses = 1;
+int trivfs_cntl_nportclasses = 1;
+
+/* ---------------------------------------------------------------- */
+#include "socket_S.h"
+
+/* A demuxer to separate out pf-level operations on our node. */
+static int
+pf_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ mig_routine_t routine;
+ if ((routine = socket_server_routine (inp)) ||
+ (routine = NULL, trivfs_demuxer (inp, outp)))
+ {
+ if (routine)
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ error_t err;
+ mach_port_t bootstrap;
+
+ if (argc > 1)
+ {
+ fprintf(stderr, "Usage: %s\n", program_invocation_name);
+ exit (1);
+ }
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error(2, 0, "Must be started as a translator");
+
+ pf_port_bucket = ports_create_bucket ();
+
+ trivfs_cntl_portclasses[0] = ports_create_class (trivfs_clean_cntl, 0);
+ trivfs_protid_portclasses[0] = ports_create_class (trivfs_clean_protid, 0);
+
+ /* Prepare to create sockets. */
+ err = sock_global_init ();
+ if (err)
+ error(3, err, "Initializing");
+
+ /* Reply to our parent */
+ err =
+ trivfs_startup (bootstrap, 0,
+ trivfs_cntl_portclasses[0], pf_port_bucket,
+ trivfs_protid_portclasses[0], pf_port_bucket,
+ NULL);
+ if (err)
+ error(3, err, "Contacting parent");
+
+ /* Launch. */
+ do
+ ports_manage_port_operations_multithread (pf_port_bucket,
+ pf_demuxer,
+ 30*1000, 5*60*1000, 0);
+ while (sock_global_shutdown () != 0);
+
+ return 0;
+}
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ st->st_fstype = FSTYPE_MISC;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ int force = (flags & FSYS_GOAWAY_FORCE);
+ error_t err = ports_inhibit_bucket_rpcs (pf_port_bucket); /* Stop all I/O. */
+
+ if (err == 0 || (err != EINTR && force))
+ {
+ /* Now see if there are any old sockets lying around. */
+ err = sock_global_shutdown ();
+
+ /* Exit if not, or if we must. */
+ if (err == 0 || force)
+ exit (0);
+
+ /* We won't go away, so start things going again... */
+ ports_resume_bucket_rpcs (pf_port_bucket);
+ }
+
+ return err;
+}
diff --git a/pflocal/sock.c b/pflocal/sock.c
new file mode 100644
index 00000000..8076dd36
--- /dev/null
+++ b/pflocal/sock.c
@@ -0,0 +1,502 @@
+/* Sock functions
+
+ Copyright (C) 1995,96,2000,01,02, 2005 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h> /* For memset() */
+
+#include <pthread.h>
+
+#include <hurd/pipe.h>
+
+#include "sock.h"
+#include "sserver.h"
+#include "connq.h"
+
+/* ---------------------------------------------------------------- */
+
+/* Returns the pipe that SOCK is reading from in PIPE, locked and with an
+ additional reference, or an error saying why it's not possible. In the
+ case where the read should signal EOF, EPIPE is returned. SOCK mustn't be
+ locked. */
+error_t
+sock_acquire_read_pipe (struct sock *sock, struct pipe **pipe)
+{
+ error_t err = 0;
+
+ pthread_mutex_lock (&sock->lock);
+
+ *pipe = sock->read_pipe;
+ if (*pipe != NULL)
+ /* SOCK may have a read pipe even before it's connected, so make
+ sure it really is. */
+ if ( !(sock->pipe_class->flags & PIPE_CLASS_CONNECTIONLESS)
+ && !(sock->flags & PFLOCAL_SOCK_CONNECTED))
+ err = ENOTCONN;
+ else
+ pipe_acquire_reader (*pipe);
+ else if (sock->flags & PFLOCAL_SOCK_SHUTDOWN_READ)
+ /* Reading on a socket with the read-half shutdown always acts as if the
+ pipe were at eof, even if the socket isn't connected yet [at least in
+ netbsd]. */
+ err = EPIPE;
+ else
+ err = ENOTCONN;
+
+ pthread_mutex_unlock (&sock->lock);
+
+ return err;
+}
+
+/* Returns the pipe that SOCK is writing to in PIPE, locked and with an
+ additional reference, or an error saying why it's not possible. SOCK
+ mustn't be locked. */
+error_t
+sock_acquire_write_pipe (struct sock *sock, struct pipe **pipe)
+{
+ error_t err = 0;
+
+ pthread_mutex_lock (&sock->lock);
+ *pipe = sock->write_pipe;
+ if (*pipe != NULL)
+ pipe_acquire_writer (*pipe); /* Do this before unlocking the sock! */
+ else if (sock->flags & PFLOCAL_SOCK_SHUTDOWN_WRITE)
+ /* Writing on a socket with the write-half shutdown always acts as if the
+ pipe were broken, even if the socket isn't connected yet [at least in
+ netbsd]. */
+ err = EPIPE;
+ else if (sock->pipe_class->flags & PIPE_CLASS_CONNECTIONLESS)
+ /* Connectionless protocols give a different error when unconnected. */
+ err = EDESTADDRREQ;
+ else
+ err = ENOTCONN;
+
+ pthread_mutex_unlock (&sock->lock);
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Return a new socket with the given pipe class in SOCK. */
+error_t
+sock_create (struct pipe_class *pipe_class, mode_t mode, struct sock **sock)
+{
+ error_t err;
+ struct sock *new = malloc (sizeof (struct sock));
+
+ if (new == NULL)
+ return ENOMEM;
+
+ /* A socket always has a read pipe (this is just to avoid some annoyance in
+ sock_connect), so create it here. */
+ err = pipe_create (pipe_class, &new->read_pipe);
+ if (err)
+ {
+ free (new);
+ return err;
+ }
+
+ pipe_add_reader (new->read_pipe);
+
+ new->refs = 0;
+ new->flags = 0;
+ new->write_pipe = NULL;
+ new->mode = mode;
+ new->id = MACH_PORT_NULL;
+ new->listen_queue = NULL;
+ new->connect_queue = NULL;
+ new->pipe_class = pipe_class;
+ new->addr = NULL;
+ memset (&new->change_time, 0, sizeof (new->change_time));
+ pthread_mutex_init (&new->lock, NULL);
+
+ *sock = new;
+ return 0;
+}
+
+/* Free SOCK, assuming there are no more handle on it. */
+void
+sock_free (struct sock *sock)
+{
+ sock_shutdown (sock, PFLOCAL_SOCK_SHUTDOWN_READ | PFLOCAL_SOCK_SHUTDOWN_WRITE);
+ if (sock->id != MACH_PORT_NULL)
+ mach_port_destroy (mach_task_self (), sock->id);
+ if (sock->listen_queue)
+ connq_destroy (sock->listen_queue);
+ free (sock);
+}
+
+/* Free a sock derefed too far. */
+void
+_sock_norefs (struct sock *sock)
+{
+ /* A sock should never have an address when it has 0 refs, as the
+ address should hold a reference to the sock! */
+ assert (sock->addr == NULL);
+ pthread_mutex_unlock (&sock->lock); /* Unlock so sock_free can do stuff. */
+ sock_free (sock);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Return a new socket largely copied from TEMPLATE. */
+error_t
+sock_clone (struct sock *template, struct sock **sock)
+{
+ error_t err = sock_create (template->pipe_class, template->mode, sock);
+
+ if (err)
+ return err;
+
+ /* Copy some properties from TEMPLATE. */
+ (*sock)->flags = template->flags & ~PFLOCAL_SOCK_CONNECTED;
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+struct port_class *sock_user_port_class;
+
+/* Get rid of a user reference to a socket. */
+static void
+sock_user_clean (void *vuser)
+{
+ struct sock_user *user = vuser;
+ sock_deref (user->sock);
+}
+
+/* Return a new user port on SOCK in PORT. */
+error_t
+sock_create_port (struct sock *sock, mach_port_t *port)
+{
+ struct sock_user *user;
+ error_t err =
+ ports_create_port (sock_user_port_class, sock_port_bucket,
+ sizeof (struct sock_user), &user);
+
+ if (err)
+ return err;
+
+ ensure_sock_server ();
+
+ pthread_mutex_lock (&sock->lock);
+ sock->refs++;
+ pthread_mutex_unlock (&sock->lock);
+
+ user->sock = sock;
+
+ *port = ports_get_right (user);
+ ports_port_deref (user); /* We only want one ref, for the send right. */
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+/* Address manipulation. */
+
+struct addr
+{
+ struct port_info pi;
+ struct sock *sock;
+ pthread_mutex_t lock;
+};
+
+struct port_class *addr_port_class;
+
+/* Get rid of ADDR's socket's reference to it, in preparation for ADDR going
+ away. */
+static void
+addr_unbind (void *vaddr)
+{
+ struct sock *sock;
+ struct addr *addr = vaddr;
+
+ pthread_mutex_lock (&addr->lock);
+ sock = addr->sock;
+ if (sock)
+ {
+ pthread_mutex_lock (&sock->lock);
+ sock->addr = NULL;
+ addr->sock = NULL;
+ ports_port_deref_weak (addr);
+ pthread_mutex_unlock (&sock->lock);
+ sock_deref (sock);
+ }
+ pthread_mutex_unlock (&addr->lock);
+}
+
+/* Cleanup after the address ADDR, which is going away... */
+static void
+addr_clean (void *vaddr)
+{
+ struct addr *addr = vaddr;
+ /* ADDR should never have a socket bound to it at this point, as it should
+ have been removed by addr_unbind dropping the socket's weak reference
+ it. */
+ assert (addr->sock == NULL);
+}
+
+/* Return a new address, not connected to any socket yet, ADDR. */
+inline error_t
+addr_create (struct addr **addr)
+{
+ error_t err =
+ ports_create_port (addr_port_class, sock_port_bucket,
+ sizeof (struct addr), addr);
+
+ if (! err)
+ {
+ ensure_sock_server ();
+ (*addr)->sock = NULL;
+ pthread_mutex_init (&(*addr)->lock, NULL);
+ }
+
+ return err;
+}
+
+/* Bind SOCK to ADDR. */
+error_t
+sock_bind (struct sock *sock, struct addr *addr)
+{
+ error_t err = 0;
+ struct addr *old_addr;
+
+ pthread_mutex_lock (&addr->lock);
+ pthread_mutex_lock (&sock->lock);
+
+ old_addr = sock->addr;
+ if (addr && old_addr)
+ err = EINVAL; /* SOCK already bound. */
+ else if (addr && addr->sock)
+ err = EADDRINUSE; /* Something else already bound ADDR. */
+ else if (addr)
+ addr->sock = sock; /* First binding for SOCK. */
+ else
+ old_addr->sock = NULL; /* Unbinding SOCK. */
+
+ if (! err)
+ {
+ sock->addr = addr;
+ if (addr)
+ {
+ sock->refs++;
+ ports_port_ref_weak (addr);
+ }
+ if (old_addr)
+ {
+ /* Note that we don't have to worry about SOCK's ref count going to
+ zero because whoever's calling us should be holding a ref. */
+ sock->refs--;
+ ports_port_deref_weak (addr);
+ assert (sock->refs > 0); /* But make sure... */
+ }
+ }
+
+ pthread_mutex_unlock (&sock->lock);
+ pthread_mutex_unlock (&addr->lock);
+
+ return err;
+}
+
+/* Returns SOCK's addr, with an additional reference, fabricating one if
+ necessary. SOCK should be locked. */
+static inline error_t
+ensure_addr (struct sock *sock, struct addr **addr)
+{
+ error_t err = 0;
+
+ if (! sock->addr)
+ {
+ err = addr_create (&sock->addr);
+ if (!err)
+ {
+ sock->addr->sock = sock;
+ sock->refs++;
+ ports_port_ref_weak (sock->addr);
+ }
+ }
+ else
+ ports_port_ref (sock->addr);
+
+ if (!err)
+ *addr = sock->addr;
+
+ return err;
+}
+
+/* Returns the socket bound to ADDR in SOCK, or EADDRNOTAVAIL. The returned
+ sock will have one reference added to it. */
+error_t
+addr_get_sock (struct addr *addr, struct sock **sock)
+{
+ pthread_mutex_lock (&addr->lock);
+ *sock = addr->sock;
+ if (*sock)
+ (*sock)->refs++;
+ pthread_mutex_unlock (&addr->lock);
+ return *sock ? 0 : EADDRNOTAVAIL;
+}
+
+/* Returns SOCK's address in ADDR, with an additional reference added. If
+ SOCK doesn't currently have an address, one is fabricated first. */
+error_t
+sock_get_addr (struct sock *sock, struct addr **addr)
+{
+ error_t err;
+
+ pthread_mutex_lock (&sock->lock);
+ err = ensure_addr (sock, addr);
+ pthread_mutex_unlock (&sock->lock);
+
+ return err; /* XXX */
+}
+
+/* ---------------------------------------------------------------- */
+
+/* We hold this lock before we lock two sockets at once, to prevent someone
+ else trying to lock the same two sockets in the reverse order, resulting
+ in a deadlock. */
+static pthread_mutex_t socket_pair_lock;
+
+/* Connect SOCK1 and SOCK2. */
+error_t
+sock_connect (struct sock *sock1, struct sock *sock2)
+{
+ error_t err = 0;
+ /* In the case of a connectionless protocol, an already-connected socket may
+ be reconnected, so save the old destination for later disposal. */
+ struct pipe *old_sock1_write_pipe = NULL;
+ struct addr *old_sock1_write_addr = NULL;
+
+ void connect (struct sock *wr, struct sock *rd)
+ {
+ if (!( (wr->flags & PFLOCAL_SOCK_SHUTDOWN_WRITE)
+ || (rd->flags & PFLOCAL_SOCK_SHUTDOWN_READ)))
+ {
+ struct pipe *pipe = rd->read_pipe;
+ assert (pipe); /* Since PFLOCAL_SOCK_SHUTDOWN_READ isn't set. */
+ pipe_add_writer (pipe);
+ wr->write_pipe = pipe;
+ }
+ }
+
+ if (sock1->pipe_class != sock2->pipe_class)
+ /* Incompatible socket types. */
+ return EOPNOTSUPP; /* XXX?? */
+
+ pthread_mutex_lock (&socket_pair_lock);
+ pthread_mutex_lock (&sock1->lock);
+ if (sock1 != sock2)
+ /* If SOCK1 == SOCK2, then we get a fifo! */
+ pthread_mutex_lock (&sock2->lock);
+
+ if ((sock1->flags & PFLOCAL_SOCK_CONNECTED) || (sock2->flags & PFLOCAL_SOCK_CONNECTED))
+ /* An already-connected socket. */
+ err = EISCONN;
+ else
+ {
+ old_sock1_write_pipe = sock1->write_pipe;
+ old_sock1_write_addr = sock1->write_addr;
+
+ /* Always make the forward connection. */
+ connect (sock1, sock2);
+
+ /* Only make the reverse for connection-oriented protocols. */
+ if (! (sock1->pipe_class->flags & PIPE_CLASS_CONNECTIONLESS))
+ {
+ sock1->flags |= PFLOCAL_SOCK_CONNECTED;
+ if (sock1 != sock2)
+ {
+ connect (sock2, sock1);
+ sock2->flags |= PFLOCAL_SOCK_CONNECTED;
+ }
+ }
+ }
+
+ if (sock1 != sock2)
+ pthread_mutex_unlock (&sock2->lock);
+ pthread_mutex_unlock (&sock1->lock);
+ pthread_mutex_unlock (&socket_pair_lock);
+
+ if (old_sock1_write_pipe)
+ {
+ pipe_remove_writer (old_sock1_write_pipe);
+ ports_port_deref (old_sock1_write_addr);
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Shutdown either the read or write halves of SOCK, depending on whether the
+ PFLOCAL_SOCK_SHUTDOWN_READ or PFLOCAL_SOCK_SHUTDOWN_WRITE flags are set in FLAGS. */
+void
+sock_shutdown (struct sock *sock, unsigned flags)
+{
+ unsigned old_flags;
+ struct pipe *read_pipe = NULL;
+ struct pipe *write_pipe = NULL;
+
+ pthread_mutex_lock (&sock->lock);
+
+ old_flags = sock->flags;
+ sock->flags |= flags;
+
+ if (flags & PFLOCAL_SOCK_SHUTDOWN_READ && !(old_flags & PFLOCAL_SOCK_SHUTDOWN_READ))
+ {
+ /* Shutdown the read half. */
+ read_pipe = sock->read_pipe;
+ sock->read_pipe = NULL;
+ }
+ if (flags & PFLOCAL_SOCK_SHUTDOWN_WRITE && !(old_flags & PFLOCAL_SOCK_SHUTDOWN_WRITE))
+ {
+ /* Shutdown the write half. */
+ write_pipe = sock->write_pipe;
+ sock->write_pipe = NULL;
+ }
+
+ /* Unlock SOCK here, as we may subsequently wake up other threads. */
+ pthread_mutex_unlock (&sock->lock);
+
+ if (read_pipe)
+ pipe_remove_reader (read_pipe);
+ if (write_pipe)
+ pipe_remove_writer (write_pipe);
+}
+
+/* ---------------------------------------------------------------- */
+
+error_t
+sock_global_init ()
+{
+ sock_port_bucket = ports_create_bucket ();
+ sock_user_port_class = ports_create_class (sock_user_clean, NULL);
+ addr_port_class = ports_create_class (addr_clean, addr_unbind);
+ return 0;
+}
+
+/* Try to shutdown any active sockets, returning EBUSY if we can't. */
+error_t
+sock_global_shutdown ()
+{
+ int num_ports = ports_count_bucket (sock_port_bucket);
+ ports_enable_bucket (sock_port_bucket);
+ return (num_ports == 0 ? 0 : EBUSY);
+}
diff --git a/pflocal/sock.h b/pflocal/sock.h
new file mode 100644
index 00000000..5800420c
--- /dev/null
+++ b/pflocal/sock.h
@@ -0,0 +1,167 @@
+/* Internal sockets
+
+ Copyright (C) 1995,96,99,2000,01 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __SOCK_H__
+#define __SOCK_H__
+
+#include <assert.h>
+#include <pthread.h> /* For mutexes */
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <hurd/ports.h>
+
+struct pipe;
+struct pipe_class;
+
+/* A port on SOCK. Multiple sock_user's can point to the same socket. */
+struct sock_user
+{
+ struct port_info pi;
+ struct sock *sock;
+};
+
+/* An endpoint for a possible I/O stream. */
+struct sock
+{
+ int refs;
+ pthread_mutex_t lock;
+
+ /* What kind of socket this is. */
+ struct pipe_class *pipe_class;
+
+ /* Reads from this socket come from READ_PIPE, writes go to WRITE_PIPE.
+ A sock always has a read pipe, and a write pipe when it's connected to
+ another socket. */
+ struct pipe *read_pipe, *write_pipe;
+
+ /* FLAGS from SOCK_*, below. */
+ unsigned flags;
+
+ /* A receive right for this socket's id ports. */
+ mach_port_t id;
+
+ /* Last time the socket got frobbed. */
+ time_value_t change_time;
+
+ /* File mode as reported by stat. Usually this is S_ISOCK, but it
+ should be S_IFIFO for sockets (ab)used in a pipe. */
+ mode_t mode;
+
+ /* This socket's local address. Note that we don't hold any references on
+ ADDR, and depend on the addr zeroing our pointer if it goes away (which
+ is ok, as we can then just make up another address if necessary, and no
+ one could tell anyway). */
+ struct addr *addr;
+
+ /* If this sock has been connected to another sock, then WRITE_ADDR is the
+ addr of that sock. We *do* hold a reference to this addr. */
+ struct addr *write_addr;
+
+ /* A connection queue to listen for incoming connections on. Once a socket
+ has one of these, it always does, and can never again be used for
+ anything but accepting incoming connections. */
+ struct connq *listen_queue;
+ /* A connection queue we're attempting to connect through; a socket may
+ only be attempting one connection at a time. */
+ struct connq *connect_queue;
+};
+
+/* Socket flags */
+#define PFLOCAL_SOCK_CONNECTED 0x1 /* A connected connection-oriented sock. */
+#define PFLOCAL_SOCK_NONBLOCK 0x2 /* Don't block on I/O. */
+#define PFLOCAL_SOCK_SHUTDOWN_READ 0x4 /* The read-half has been shutdown. */
+#define PFLOCAL_SOCK_SHUTDOWN_WRITE 0x8 /* The write-half has been shutdown. */
+
+/* Returns the pipe that SOCK is reading from in PIPE, locked and with an
+ additional reference, or an error saying why it's not possible. NULL may
+ also be returned in PIPE with a 0 error, meaning that EOF should be
+ returned. SOCK mustn't be locked. */
+error_t sock_acquire_read_pipe (struct sock *sock, struct pipe **pipe);
+
+/* Returns the pipe that SOCK is writing to in PIPE, locked and with an
+ additional reference, or an error saying why it's not possible. SOCK
+ mustn't be locked. */
+error_t sock_acquire_write_pipe (struct sock *sock, struct pipe **pipe);
+
+/* Connect together the previously unconnected sockets SOCK1 and SOCK2. */
+error_t sock_connect (struct sock *sock1, struct sock *sock2);
+
+/* Return a new socket with the given pipe class in SOCK. */
+error_t sock_create (struct pipe_class *pipe_class, mode_t mode,
+ struct sock **sock);
+
+/* Free SOCK, assuming there are no more handle on it. */
+void sock_free (struct sock *sock);
+
+/* Free a sock derefed too far. */
+void _sock_norefs (struct sock *sock);
+
+/* Remove a reference from SOCK, possibly freeing it. */
+static inline void __attribute__ ((unused))
+sock_deref (struct sock *sock)
+{
+ pthread_mutex_lock (&sock->lock);
+ if (--sock->refs == 0)
+ _sock_norefs (sock);
+ else
+ pthread_mutex_unlock (&sock->lock);
+}
+
+/* Return a new socket just like TEMPLATE in SOCK. */
+error_t sock_clone (struct sock *template, struct sock **sock);
+
+/* Return a new user port on SOCK in PORT. */
+error_t sock_create_port (struct sock *sock, mach_port_t *port);
+
+/* Bind SOCK to ADDR. */
+error_t sock_bind (struct sock *sock, struct addr *addr);
+
+/* Returns SOCK's address in ADDR, with an additional reference added. If
+ SOCK doesn't currently have an address, one is fabricated first. */
+error_t sock_get_addr (struct sock *sock, struct addr **addr);
+
+/* If SOCK is a connected socket, returns a send right to SOCK's peer's
+ address in ADDR_PORT. */
+error_t sock_get_write_addr_port (struct sock *sock, mach_port_t *addr_port);
+
+/* Shutdown either the read or write halves of SOCK, depending on whether the
+ PFLOCAL_SOCK_SHUTDOWN_READ or PFLOCAL_SOCK_SHUTDOWN_WRITE flags are set in FLAGS. */
+void sock_shutdown (struct sock *sock, unsigned flags);
+
+/* Return a new address, not connected to any socket yet, ADDR. */
+error_t addr_create (struct addr **addr);
+
+/* Returns the socket bound to ADDR in SOCK, or EADDRNOTAVAIL. The returned
+ sock will have one reference added to it. */
+error_t addr_get_sock (struct addr *addr, struct sock **sock);
+
+/* Prepare for socket creation. */
+error_t sock_global_init ();
+
+/* Try to shutdown any active sockets, returning EBUSY if we can't. Assumes
+ non-socket RPCS's have been disabled. */
+error_t sock_global_shutdown ();
+
+/* Mostly here for use by mig-decls.h. */
+extern struct port_class *sock_user_port_class;
+extern struct port_class *addr_port_class;
+
+#endif /* __SOCK_H__ */
diff --git a/pflocal/socket.c b/pflocal/socket.c
new file mode 100644
index 00000000..792c6379
--- /dev/null
+++ b/pflocal/socket.c
@@ -0,0 +1,475 @@
+/* Socket-specific operations
+
+ Copyright (C) 1995, 2008, 2010, 2012 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/socket.h>
+
+#include <hurd/pipe.h>
+
+#include "sock.h"
+#include "connq.h"
+
+#include "socket_S.h"
+
+/* Connect two sockets */
+error_t
+S_socket_connect2 (struct sock_user *user1, struct sock_user *user2)
+{
+ error_t err;
+
+ if (!user1 || !user2)
+ return EOPNOTSUPP;
+
+ err = sock_connect (user1->sock, user2->sock);
+ if (!err && user1->sock->pipe_class->flags & PIPE_CLASS_CONNECTIONLESS)
+ err = sock_connect (user2->sock, user1->sock);
+
+ /* Since USER2 isn't in the receiver position in the rpc, we get a send
+ right for it (although we only use the receive right with the same
+ name); be sure it's deallocated! */
+ mach_port_deallocate (mach_task_self (), user2->pi.port_right);
+
+ return err;
+}
+
+/* Make sure we have a queue to listen on. */
+static error_t
+ensure_connq (struct sock *sock)
+{
+ error_t err = 0;
+ pthread_mutex_lock (&sock->lock);
+ if (!sock->listen_queue)
+ err = connq_create (&sock->listen_queue);
+ pthread_mutex_unlock (&sock->lock);
+ return err;
+}
+
+/* Prepare a socket of appropriate type for future accept operations. */
+error_t
+S_socket_listen (struct sock_user *user, int queue_limit)
+{
+ error_t err;
+ if (!user)
+ return EOPNOTSUPP;
+ if (queue_limit < 0)
+ return EINVAL;
+ err = ensure_connq (user->sock);
+ if (!err)
+ err = connq_set_length (user->sock->listen_queue, queue_limit);
+ return err;
+}
+
+error_t
+S_socket_connect (struct sock_user *user, struct addr *addr)
+{
+ error_t err;
+ struct sock *peer;
+
+ if (! addr)
+ return ECONNREFUSED;
+
+ /* Deallocate ADDR's send right, which we get as a side effect of the rpc. */
+ mach_port_deallocate (mach_task_self (),
+ ((struct port_info *)addr)->port_right);
+
+ if (! user)
+ return EOPNOTSUPP;
+
+ err = addr_get_sock (addr, &peer);
+ if (!err)
+ {
+ struct sock *sock = user->sock;
+ struct connq *cq = peer->listen_queue;
+
+ if (sock->pipe_class->flags & PIPE_CLASS_CONNECTIONLESS)
+ /* For connectionless protocols, connect() just sets where writes
+ will go, so the destination need not be doing an accept. */
+ err = sock_connect (sock, peer);
+ else if (cq)
+ /* For connection-oriented protocols, only connect with sockets that
+ are actually listening. */
+ {
+ pthread_mutex_lock (&sock->lock);
+ if (sock->connect_queue)
+ /* SOCK is already doing a connect. */
+ err = EALREADY;
+ else if (sock->flags & PFLOCAL_SOCK_CONNECTED)
+ /* PFLOCAL_SOCK_CONNECTED is only set for connection-oriented sockets,
+ which can only ever connect once. [If we didn't do this test
+ here, it would eventually fail when the listening socket
+ tried to accept our connection request.] */
+ err = EISCONN;
+ else
+ {
+ /* Assert that we're trying to connect, so anyone else trying
+ to do so will fail with EALREADY. */
+ sock->connect_queue = cq;
+ /* Unlock SOCK while waiting. */
+ pthread_mutex_unlock (&sock->lock);
+
+ err = connq_connect (peer->listen_queue,
+ sock->flags & PFLOCAL_SOCK_NONBLOCK);
+ if (!err)
+ {
+ struct sock *server;
+
+ err = sock_clone (peer, &server);
+ if (!err)
+ {
+ err = sock_connect (sock, server);
+ if (!err)
+ connq_connect_complete (peer->listen_queue, server);
+ else
+ sock_free (server);
+ }
+
+ if (err)
+ connq_connect_cancel (peer->listen_queue);
+ }
+
+ pthread_mutex_lock (&sock->lock);
+ /* We must set CONNECT_QUEUE to NULL, as no one else can
+ set it until we've done so. */
+ sock->connect_queue = NULL;
+ }
+
+ pthread_mutex_unlock (&sock->lock);
+ }
+ else
+ err = ECONNREFUSED;
+
+ sock_deref (peer);
+ }
+
+ return err;
+}
+
+/* Return a new connection from a socket previously listened. */
+error_t
+S_socket_accept (struct sock_user *user,
+ mach_port_t *port, mach_msg_type_name_t *port_type,
+ mach_port_t *peer_addr_port,
+ mach_msg_type_name_t *peer_addr_port_type)
+{
+ error_t err;
+ struct sock *sock;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ sock = user->sock;
+
+ err = ensure_connq (sock);
+ if (!err)
+ {
+ struct timespec noblock = {0, 0};
+ struct sock *peer_sock;
+
+ err = connq_listen (sock->listen_queue,
+ (sock->flags & PFLOCAL_SOCK_NONBLOCK) ? &noblock : NULL,
+ &peer_sock);
+ if (!err)
+ {
+ struct addr *peer_addr;
+ *port_type = MACH_MSG_TYPE_MAKE_SEND;
+ err = sock_create_port (peer_sock, port);
+ if (!err)
+ err = sock_get_addr (peer_sock, &peer_addr);
+ if (!err)
+ {
+ *peer_addr_port = ports_get_right (peer_addr);
+ *peer_addr_port_type = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (peer_addr);
+ }
+ else
+ {
+ /* TEAR DOWN THE CONNECTION XXX */
+ }
+ }
+ }
+
+ return err;
+}
+
+/* Bind a socket to an address. */
+error_t
+S_socket_bind (struct sock_user *user, struct addr *addr)
+{
+ if (! addr)
+ return EADDRNOTAVAIL;
+
+ /* Deallocate ADDR's send right, which we get as a side effect of the rpc. */
+ mach_port_deallocate (mach_task_self (),
+ ((struct port_info *)addr)->port_right);
+
+ if (! user)
+ return EOPNOTSUPP;
+
+ return sock_bind (user->sock, addr);
+}
+
+/* Shutdown a socket for reading or writing. */
+error_t
+S_socket_shutdown (struct sock_user *user, int what)
+{
+ if (! user)
+ return EOPNOTSUPP;
+ sock_shutdown (user->sock,
+ (what != 1 ? PFLOCAL_SOCK_SHUTDOWN_READ : 0)
+ | (what != 0 ? PFLOCAL_SOCK_SHUTDOWN_WRITE : 0));
+ return 0;
+}
+
+/* Find out the name of a socket. */
+error_t
+S_socket_name (struct sock_user *user,
+ mach_port_t *addr_port, mach_msg_type_name_t *addr_port_type)
+{
+ error_t err;
+ struct addr *addr;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ err = sock_get_addr (user->sock, &addr);
+ if (err)
+ return err;
+
+ *addr_port = ports_get_right (addr);
+ *addr_port_type = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (addr);
+
+ return 0;
+}
+
+/* Find out the name of the socket's peer. */
+error_t
+S_socket_peername (struct sock_user *user,
+ mach_port_t *addr_port,
+ mach_msg_type_name_t *addr_port_type)
+{
+ return EOPNOTSUPP; /* XXX */
+ if (!user)
+ return EOPNOTSUPP;
+ *addr_port_type = MACH_MSG_TYPE_MAKE_SEND;
+}
+
+/* Send data over a socket, possibly including Mach ports. */
+error_t
+S_socket_send (struct sock_user *user, struct addr *dest_addr, int flags,
+ char *data, size_t data_len,
+ mach_port_t *ports, size_t num_ports,
+ char *control, size_t control_len,
+ size_t *amount)
+{
+ error_t err = 0;
+ struct pipe *pipe;
+ struct sock *sock, *dest_sock;
+ struct addr *source_addr;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ sock = user->sock;
+
+ if (flags & MSG_OOB)
+ /* BSD local sockets don't support OOB data. */
+ return EOPNOTSUPP;
+
+ if (dest_addr)
+ {
+ err = addr_get_sock (dest_addr, &dest_sock);
+ if (err)
+ return err;
+ if (sock->pipe_class != dest_sock->pipe_class)
+ /* Sending to a different type of socket! */
+ err = EINVAL; /* ? XXX */
+ }
+ else
+ dest_sock = 0;
+
+ /* We could provide a source address for all writes, but we
+ only do so for connectionless sockets because that's the
+ only place it's required, and it's more efficient not to. */
+ if (!err && sock->pipe_class->flags & PIPE_CLASS_CONNECTIONLESS)
+ err = sock_get_addr (sock, &source_addr);
+ else
+ source_addr = NULL;
+
+ if (!err)
+ {
+ if (dest_sock)
+ /* Grab the destination socket's read pipe directly, and stuff data
+ into it. This is not quite the usage sock_acquire_read_pipe was
+ intended for, but it will work, as the only inappropriate errors
+ occur on a broken pipe, which shouldn't be possible with the sort of
+ sockets with which we can use socket_send... XXXX */
+ err = sock_acquire_read_pipe (dest_sock, &pipe);
+ else
+ /* No address, must be a connected socket... */
+ err = sock_acquire_write_pipe (sock, &pipe);
+
+ if (!err)
+ {
+ err = pipe_send (pipe, sock->flags & PFLOCAL_SOCK_NONBLOCK,
+ source_addr, data, data_len,
+ control, control_len, ports, num_ports,
+ amount);
+ if (dest_sock)
+ pipe_release_reader (pipe);
+ else
+ pipe_release_writer (pipe);
+ }
+
+ if (err)
+ /* The send failed, so free any resources it would have consumed
+ (mig gets rid of memory, but we have to do everything else). */
+ {
+ if (source_addr)
+ ports_port_deref (source_addr);
+ while (num_ports-- > 0)
+ mach_port_deallocate (mach_task_self (), *ports++);
+ }
+ }
+
+ if (dest_sock)
+ sock_deref (dest_sock);
+
+ return err;
+}
+
+/* Receive data from a socket, possibly including Mach ports. */
+error_t
+S_socket_recv (struct sock_user *user,
+ mach_port_t *addr, mach_msg_type_name_t *addr_type,
+ int in_flags,
+ char **data, size_t *data_len,
+ mach_port_t **ports, mach_msg_type_name_t *ports_type,
+ size_t *num_ports,
+ char **control, size_t *control_len,
+ int *out_flags, size_t amount)
+{
+ error_t err;
+ unsigned flags;
+ struct pipe *pipe;
+ void *source_addr = NULL;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ if (in_flags & MSG_OOB)
+ /* BSD local sockets don't support OOB data. */
+ return EINVAL; /* XXX */
+
+ /* Fill in the pipe FLAGS from any corresponding ones in IN_FLAGS. */
+ flags = in_flags & MSG_PEEK;
+
+ err = sock_acquire_read_pipe (user->sock, &pipe);
+ if (err == EPIPE)
+ /* EOF */
+ {
+ *data_len = 0;
+ if (num_ports)
+ *num_ports = 0;
+ if (control_len)
+ *control_len = 0;
+ }
+ else if (!err)
+ {
+ err =
+ pipe_recv (pipe, user->sock->flags & PFLOCAL_SOCK_NONBLOCK, &flags,
+ &source_addr, data, data_len, amount,
+ control, control_len, ports, num_ports);
+ pipe_release_reader (pipe);
+ }
+
+ if (!err)
+ /* Setup mach ports for return. */
+ {
+ *addr_type = MACH_MSG_TYPE_MAKE_SEND;
+ *ports_type = MACH_MSG_TYPE_MOVE_SEND;
+ if (source_addr)
+ {
+ *addr = ports_get_right (source_addr);
+ ports_port_deref (source_addr); /* since get_right has one too. */
+ }
+ else
+ *addr = MACH_PORT_NULL;
+ }
+
+ *out_flags = 0;
+
+ return err;
+}
+
+error_t
+S_socket_getopt (struct sock_user *user,
+ int level, int opt,
+ char **value, size_t *value_len)
+{
+ int ret = 0;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->sock->lock);
+ switch (level)
+ {
+ case SOL_SOCKET:
+ switch (opt)
+ {
+ case SO_TYPE:
+ assert (*value_len >= sizeof (int));
+ *(int *)*value = user->sock->pipe_class->sock_type;
+ *value_len = sizeof (int);
+ break;
+ default:
+ ret = ENOPROTOOPT;
+ break;
+ }
+ break;
+ default:
+ ret = ENOPROTOOPT;
+ break;
+ }
+ pthread_mutex_unlock (&user->sock->lock);
+
+ return ret;
+}
+
+error_t
+S_socket_setopt (struct sock_user *user,
+ int level, int opt, char *value, size_t value_len)
+{
+ int ret = 0;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->sock->lock);
+ switch (level)
+ {
+ default:
+ ret = ENOPROTOOPT;
+ break;
+ }
+ pthread_mutex_unlock (&user->sock->lock);
+
+ return ret;
+}
diff --git a/pflocal/sserver.c b/pflocal/sserver.c
new file mode 100644
index 00000000..7df69a41
--- /dev/null
+++ b/pflocal/sserver.c
@@ -0,0 +1,105 @@
+/* Server for socket ops
+
+ Copyright (C) 1995, 1997, 2013 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <pthread.h>
+#include <stdio.h>
+
+#include <hurd/ports.h>
+
+/* A port bucket to handle SOCK_USERs and ADDRs. */
+struct port_bucket *sock_port_bucket;
+
+/* ---------------------------------------------------------------- */
+
+/* True if there are threads servicing sock requests. */
+static int sock_server_active = 0;
+static pthread_spinlock_t sock_server_active_lock = PTHREAD_SPINLOCK_INITIALIZER;
+
+#include "io_S.h"
+#include "socket_S.h"
+#include "../libports/interrupt_S.h"
+#include "../libports/notify_S.h"
+
+/* A demuxer for socket operations. */
+static int
+sock_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ mig_routine_t routine;
+ if ((routine = io_server_routine (inp)) ||
+ (routine = socket_server_routine (inp)) ||
+ (routine = ports_interrupt_server_routine (inp)) ||
+ (routine = ports_notify_server_routine (inp)))
+ {
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/* Handle socket requests while there are sockets around. */
+static void *
+handle_sock_requests (void *unused)
+{
+ while (ports_count_bucket (sock_port_bucket) > 0)
+ {
+ ports_enable_bucket (sock_port_bucket);
+ ports_manage_port_operations_multithread (sock_port_bucket, sock_demuxer,
+ 30*1000, 2*60*1000, 0);
+ }
+
+ /* The last service thread is about to exist; make this known. */
+ pthread_spin_lock (&sock_server_active_lock);
+ sock_server_active = 0;
+ pthread_spin_unlock (&sock_server_active_lock);
+
+ /* Let the whole joke start once again. */
+ ports_enable_bucket (sock_port_bucket);
+
+ return NULL;
+}
+
+/* Makes sure there are some request threads for sock operations, and starts
+ a server if necessary. This routine should be called *after* creating the
+ port(s) which need server, as the server routine only operates while there
+ are any ports. */
+void
+ensure_sock_server ()
+{
+ pthread_t thread;
+ error_t err;
+
+ pthread_spin_lock (&sock_server_active_lock);
+ if (sock_server_active)
+ pthread_spin_unlock (&sock_server_active_lock);
+ else
+ {
+ sock_server_active = 1;
+ pthread_spin_unlock (&sock_server_active_lock);
+ err = pthread_create (&thread, NULL, handle_sock_requests, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+ }
+}
diff --git a/pflocal/sserver.h b/pflocal/sserver.h
new file mode 100644
index 00000000..a61ad916
--- /dev/null
+++ b/pflocal/sserver.h
@@ -0,0 +1,33 @@
+/* Server for socket ops
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __SSERVER_H__
+#define __SSERVER_H__
+
+/* Makes sure there are some request threads for sock operations, and starts
+ a server if necessary. This routine should be called *after* creating the
+ port(s) which need server, as the server routine only operates while there
+ are any ports. */
+void ensure_sock_server ();
+
+/* A port bucket to handle SOCK_USERs and ADDRs. */
+struct port_bucket *sock_port_bucket;
+
+#endif /* __SSERVER_H__ */
diff --git a/proc/=proc_excrepl.defs b/proc/=proc_excrepl.defs
new file mode 100644
index 00000000..ce78230b
--- /dev/null
+++ b/proc/=proc_excrepl.defs
@@ -0,0 +1,37 @@
+/* Reply side of proc-exc.defs. */
+
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+subsystem proc_excrepl 2500;
+
+#include <mach/std_types.defs>
+
+ServerPrefix S_;
+
+simpleroutine proc_exception_raise_reply (
+ reply: mach_port_send_t;
+ reply_code: int);
diff --git a/proc/Makefile b/proc/Makefile
new file mode 100644
index 00000000..aa31ffbf
--- /dev/null
+++ b/proc/Makefile
@@ -0,0 +1,38 @@
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 2012 Free Software Foundation,
+# Inc.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with the GNU Hurd; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := proc
+makemode := server
+
+target = proc
+SRCS = wait.c hash.c host.c info.c main.c mgt.c notify.c pgrp.c msg.c \
+ cpu-types.c stubs.c
+
+MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+
+MIGSTUBS = processServer.o notifyServer.o \
+ ourmsgUser.o proc_excUser.o proc_excServer.o
+OBJS = $(SRCS:.c=.o) $(MIGSTUBS)
+HURDLIBS = ihash ports shouldbeinlibc
+OTHERLIBS = -lpthread
+
+include ../Makeconf
+
+mutated_ourmsg_U.h: ourmsg_U.h
+ sed -e 's/_msg_user_/_ourmsg_user_/' < $< > $@
diff --git a/proc/cpu-types.c b/proc/cpu-types.c
new file mode 100644
index 00000000..7bcaa928
--- /dev/null
+++ b/proc/cpu-types.c
@@ -0,0 +1,163 @@
+/* Printable names for Mach CPU types and subtypes.
+ Information culled from <mach/machine.h>. */
+
+#include <mach/machine.h>
+
+const char *const mach_cpu_types[] =
+ {
+ [CPU_TYPE_VAX] = "vax",
+ [CPU_TYPE_ROMP] = "romp",
+ [CPU_TYPE_MC68020] = "mc68020",
+ [CPU_TYPE_NS32032] = "ns32032",
+ [CPU_TYPE_NS32332] = "ns32332",
+ [CPU_TYPE_NS32532] = "ns32532",
+ [CPU_TYPE_I386] = "i386",
+ [CPU_TYPE_MIPS] = "mips",
+ [CPU_TYPE_MC68030] = "mc68030",
+ [CPU_TYPE_MC68040] = "mc68040",
+ [CPU_TYPE_HPPA] = "hppa",
+ [CPU_TYPE_ARM] = "arm",
+ [CPU_TYPE_MC88000] = "mc88000",
+ [CPU_TYPE_SPARC] = "sparc",
+ [CPU_TYPE_I860] = "i860",
+ [CPU_TYPE_ALPHA] = "alpha",
+#ifdef CPU_TYPE_I486
+ [CPU_TYPE_I486] = "i486",
+#endif
+#ifdef CPU_TYPE_PENTIUM
+ [CPU_TYPE_PENTIUM] = "i586",
+#endif
+#ifdef CPU_TYPE_PENTIUMPRO
+ [CPU_TYPE_PENTIUMPRO] = "i686",
+#endif
+#ifdef CPU_TYPE_POWERPC
+ [CPU_TYPE_POWERPC] = "powerpc",
+#endif
+ };
+
+const char *const mach_cpu_subtypes[][32] =
+ {
+ [CPU_TYPE_VAX] =
+ {
+ [CPU_SUBTYPE_VAX780] = "VAX780",
+ [CPU_SUBTYPE_VAX785] = "VAX785",
+ [CPU_SUBTYPE_VAX750] = "VAX750",
+ [CPU_SUBTYPE_VAX730] = "VAX730",
+ [CPU_SUBTYPE_UVAXI] = "UVAXI",
+ [CPU_SUBTYPE_UVAXII] = "UVAXII",
+ [CPU_SUBTYPE_VAX8200] = "VAX8200",
+ [CPU_SUBTYPE_VAX8500] = "VAX8500",
+ [CPU_SUBTYPE_VAX8600] = "VAX8600",
+ [CPU_SUBTYPE_VAX8650] = "VAX8650",
+ [CPU_SUBTYPE_VAX8800] = "VAX8800",
+ [CPU_SUBTYPE_UVAXIII] = "UVAXIII",
+ },
+ [CPU_TYPE_ROMP] =
+ {
+ [CPU_SUBTYPE_RT_PC] = "RT_PC",
+ [CPU_SUBTYPE_RT_APC] = "RT_APC",
+ [CPU_SUBTYPE_RT_135] = "RT_135",
+ },
+ [CPU_TYPE_MC68020] =
+ {
+ [CPU_SUBTYPE_SUN3_50] = "SUN3_50",
+ [CPU_SUBTYPE_SUN3_160] = "SUN3_160",
+ [CPU_SUBTYPE_SUN3_260] = "SUN3_260",
+ [CPU_SUBTYPE_SUN3_110] = "SUN3_110",
+ [CPU_SUBTYPE_SUN3_60] = "SUN3_60",
+
+ [CPU_SUBTYPE_HP_320] = "HP_320",
+ [CPU_SUBTYPE_HP_330] = "HP_330",
+ [CPU_SUBTYPE_HP_350] = "HP_350",
+ },
+ [CPU_TYPE_NS32032] =
+ {
+ [CPU_SUBTYPE_MMAX_DPC] = "MMAX_DPC",
+ [CPU_SUBTYPE_SQT] = "SQT",
+ [CPU_SUBTYPE_MMAX_APC_FPU] = "MMAX_APC_FPU",
+ [CPU_SUBTYPE_MMAX_APC_FPA] = "MMAX_APC_FPA",
+ [CPU_SUBTYPE_MMAX_XPC] = "MMAX_XPC",
+ [CPU_SUBTYPE_PC532] = "PC532",
+ },
+#define Ix86_SUBTYPES \
+ { \
+ [CPU_SUBTYPE_AT386] = "AT386", \
+ [CPU_SUBTYPE_EXL] = "EXL", \
+ [CPU_SUBTYPE_iPSC386] = "iPSC386", \
+ [CPU_SUBTYPE_SYMMETRY] = "SYMMETRY", \
+ [CPU_SUBTYPE_PS2] = "PS2", \
+ }
+ [CPU_TYPE_I386] = Ix86_SUBTYPES,
+#ifdef CPU_TYPE_I486
+ [CPU_TYPE_I486] = Ix86_SUBTYPES,
+#endif
+#ifdef CPU_TYPE_PENTIUM
+ [CPU_TYPE_PENTIUM] = Ix86_SUBTYPES,
+#endif
+#ifdef CPU_TYPE_PENTIUMPRO
+ [CPU_TYPE_PENTIUMPRO] = Ix86_SUBTYPES,
+#endif
+ [CPU_TYPE_MIPS] =
+ {
+ [CPU_SUBTYPE_MIPS_R2300] = "R2300",
+ [CPU_SUBTYPE_MIPS_R2600] = "R2600",
+ [CPU_SUBTYPE_MIPS_R2800] = "R2800",
+ [CPU_SUBTYPE_MIPS_R2000a] = "R2000a",
+ [CPU_SUBTYPE_MIPS_R2000] = "R2000",
+ [CPU_SUBTYPE_MIPS_R3000a] = "R3000a",
+ [CPU_SUBTYPE_MIPS_R3000] = "R3000",
+ },
+ [CPU_TYPE_MC68030] =
+ {
+ [CPU_SUBTYPE_NeXT] = "NeXT",
+ [CPU_SUBTYPE_HP_340] = "HP_340",
+ [CPU_SUBTYPE_HP_360] = "HP_360",
+ [CPU_SUBTYPE_HP_370] = "HP_370",
+ },
+ [CPU_TYPE_HPPA] =
+ {
+ [CPU_SUBTYPE_HPPA_825] = "825",
+ [CPU_SUBTYPE_HPPA_835] = "835",
+ [CPU_SUBTYPE_HPPA_840] = "840",
+ [CPU_SUBTYPE_HPPA_850] = "850",
+ [CPU_SUBTYPE_HPPA_855] = "855",
+ },
+ [CPU_TYPE_ARM] =
+ {
+ [CPU_SUBTYPE_ARM_A500_ARCH] = "A500_ARCH",
+ [CPU_SUBTYPE_ARM_A500] = "A500",
+ [CPU_SUBTYPE_ARM_A440] = "A440",
+ [CPU_SUBTYPE_ARM_M4] = "M4",
+ [CPU_SUBTYPE_ARM_A680] = "A680",
+ },
+ [CPU_TYPE_MC88000] =
+ {
+ [CPU_SUBTYPE_MMAX_JPC] = "MMAX_JPC",
+ [CPU_SUBTYPE_LUNA88K] = "LUNA88K",
+ },
+ [CPU_TYPE_SPARC] =
+ {
+ [CPU_SUBTYPE_SUN4_260] = "SUN4_260",
+ [CPU_SUBTYPE_SUN4_110] = "SUN4_110",
+ [CPU_SUBTYPE_SUN4_330] = "SUN4_330",
+ [CPU_SUBTYPE_SUN4C_60] = "SUN4C_60",
+ [CPU_SUBTYPE_SUN4C_65] = "SUN4C_65",
+ [CPU_SUBTYPE_SUN4C_20] = "SUN4C_20",
+ [CPU_SUBTYPE_SUN4C_30] = "SUN4C_30",
+ [CPU_SUBTYPE_SUN4C_40] = "SUN4C_40",
+ [CPU_SUBTYPE_SUN4C_50] = "SUN4C_50",
+ [CPU_SUBTYPE_SUN4C_75] = "SUN4C_75",
+ },
+ [CPU_TYPE_I860] =
+ {
+ [CPU_SUBTYPE_iPSC860] = "iPSC860",
+ [CPU_SUBTYPE_OKI860] = "OKI860",
+ },
+ [CPU_TYPE_ALPHA] =
+ {
+ [CPU_SUBTYPE_ALPHA_EV3] = "ALPHA_EV3",
+ [CPU_SUBTYPE_ALPHA_EV4] = "ALPHA_EV4",
+ [CPU_SUBTYPE_ALPHA_ISP] = "ALPHA_ISP",
+ [CPU_SUBTYPE_ALPHA_21064] = "ALPHA_21064",
+ },
+ };
diff --git a/proc/hash.c b/proc/hash.c
new file mode 100644
index 00000000..e4dc5ffd
--- /dev/null
+++ b/proc/hash.c
@@ -0,0 +1,156 @@
+/* Hash table functions
+ Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include <mach.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <hurd/hurd_types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+
+#include "proc.h"
+#include <hurd/ihash.h>
+
+static struct hurd_ihash pghash
+ = HURD_IHASH_INITIALIZER (offsetof (struct pgrp, pg_hashloc));
+static struct hurd_ihash pidhash
+ = HURD_IHASH_INITIALIZER (offsetof (struct proc, p_pidhashloc));
+static struct hurd_ihash taskhash
+ = HURD_IHASH_INITIALIZER (offsetof (struct proc, p_taskhashloc));
+static struct hurd_ihash sidhash
+ = HURD_IHASH_INITIALIZER (offsetof (struct session, s_hashloc));
+
+
+/* Find the process corresponding to a given pid. */
+struct proc *
+pid_find (pid_t pid)
+{
+ struct proc *p;
+ p = hurd_ihash_find (&pidhash, pid);
+ return (!p || p->p_dead) ? 0 : p;
+}
+
+/* Find the process corresponding to a given pid. Return it even if
+ it's dead. */
+struct proc *
+pid_find_allow_zombie (pid_t pid)
+{
+ return hurd_ihash_find (&pidhash, pid);
+}
+
+/* Find the process corresponding to a given task. */
+struct proc *
+task_find (task_t task)
+{
+ struct proc *p;
+ p = hurd_ihash_find (&taskhash, task) ? : add_tasks (task);
+ return (!p || p->p_dead) ? 0 : p;
+}
+
+/* Find the process corresponding to a given task, but
+ if we don't already know about it, just return 0. */
+struct proc *
+task_find_nocreate (task_t task)
+{
+ struct proc *p;
+ p = hurd_ihash_find (&taskhash, task);
+ return (!p || p->p_dead) ? 0 : p;
+}
+
+/* Find the process group corresponding to a given pgid. */
+struct pgrp *
+pgrp_find (pid_t pgid)
+{
+ return hurd_ihash_find (&pghash, pgid);
+}
+
+/* Find the session corresponding to a given sid. */
+struct session *
+session_find (pid_t sid)
+{
+ return hurd_ihash_find (&sidhash, sid);
+}
+
+/* Add a new process to the various hash tables. */
+void
+add_proc_to_hash (struct proc *p)
+{
+ hurd_ihash_add (&pidhash, p->p_pid, p);
+ hurd_ihash_add (&taskhash, p->p_task, p);
+}
+
+/* Add a new process group to the various hash tables. */
+void
+add_pgrp_to_hash (struct pgrp *pg)
+{
+ hurd_ihash_add (&pghash, pg->pg_pgid, pg);
+}
+
+/* Add a new session to the various hash tables. */
+void
+add_session_to_hash (struct session *s)
+{
+ hurd_ihash_add (&sidhash, s->s_sid, s);
+}
+
+/* Remove a process group from the various hash tables. */
+void
+remove_pgrp_from_hash (struct pgrp *pg)
+{
+ hurd_ihash_locp_remove (&pghash, pg->pg_hashloc);
+}
+
+/* Remove a process from the various hash tables. */
+void
+remove_proc_from_hash (struct proc *p)
+{
+ hurd_ihash_locp_remove (&pidhash, p->p_pidhashloc);
+ hurd_ihash_locp_remove (&taskhash, p->p_taskhashloc);
+}
+
+/* Remove a session from the various hash tables. */
+void
+remove_session_from_hash (struct session *s)
+{
+ hurd_ihash_locp_remove (&sidhash, s->s_hashloc);
+}
+
+/* Call function FUN of two args for each process. FUN's first arg is
+ the process, its second arg is ARG. */
+void
+prociterate (void (*fun) (struct proc *, void *), void *arg)
+{
+ HURD_IHASH_ITERATE (&pidhash, value)
+ {
+ struct proc *p = value;
+ if (!p->p_dead)
+ (*fun)(p, arg);
+ }
+}
+
+/* Tell if a pid is available for use */
+int
+pidfree (pid_t pid)
+{
+ return (!pid_find_allow_zombie (pid)
+ && !pgrp_find (pid) && !session_find (pid));
+}
diff --git a/proc/host.c b/proc/host.c
new file mode 100644
index 00000000..6841273d
--- /dev/null
+++ b/proc/host.c
@@ -0,0 +1,481 @@
+/* Proc server host management calls
+
+ Copyright (C) 1992, 1993, 1994, 1996, 1997, 2001, 2002, 2013 Free Software
+ Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include <mach.h>
+#include <hurd.h>
+#include <sys/types.h>
+#include <hurd/hurd_types.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <mach/notify.h>
+#include <string.h>
+#include <stdio.h>
+#include <hurd/exec.h>
+#include <unistd.h>
+#include <assert.h>
+#include <version.h>
+#include <sys/mman.h>
+
+#include "proc.h"
+#include "process_S.h"
+
+static mach_port_t *std_port_array;
+static int *std_int_array;
+static int n_std_ports, n_std_ints;
+static struct utsname uname_info;
+
+struct server_version
+{
+ char *name;
+ char *version;
+} *server_versions;
+int nserver_versions, server_versions_nalloc;
+
+struct execdata_notify
+{
+ mach_port_t notify_port;
+ struct execdata_notify *next;
+} *execdata_notifys;
+
+
+/* Implement proc_getprivports as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getprivports (struct proc *p,
+ mach_port_t *hostpriv,
+ mach_port_t *devpriv)
+{
+ if (!p)
+ return EOPNOTSUPP;
+
+ if (! check_uid (p, 0))
+ return EPERM;
+
+ *hostpriv = _hurd_host_priv;
+ *devpriv = _hurd_device_master;
+ return 0;
+}
+
+
+/* Implement proc_setexecdata as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_setexecdata (struct proc *p,
+ mach_port_t *ports,
+ size_t nports,
+ int *ints,
+ size_t nints)
+{
+ int i;
+ struct execdata_notify *n;
+ mach_port_t *std_port_array_new;
+ int *std_int_array_new;
+
+ if (!p)
+ return EOPNOTSUPP;
+
+ if (!check_uid (p, 0))
+ return EPERM;
+
+ /* Allocate memory up front. */
+ std_port_array_new = malloc (sizeof (mach_port_t) * nports);
+ if (! std_port_array_new)
+ return ENOMEM;
+
+ std_int_array_new = malloc (sizeof (int) * nints);
+ if (! std_int_array_new)
+ {
+ free (std_port_array_new);
+ return ENOMEM;
+ }
+
+ if (std_port_array)
+ {
+ for (i = 0; i < n_std_ports; i++)
+ mach_port_deallocate (mach_task_self (), std_port_array[i]);
+ free (std_port_array);
+ }
+ if (std_int_array)
+ free (std_int_array);
+
+ std_port_array = std_port_array_new;
+ n_std_ports = nports;
+ memcpy (std_port_array, ports, sizeof (mach_port_t) * nports);
+
+ std_int_array = std_int_array_new;
+ n_std_ints = nints;
+ memcpy (std_int_array, ints, sizeof (int) * nints);
+
+ for (n = execdata_notifys; n; n = n->next)
+ exec_setexecdata (n->notify_port, std_port_array, MACH_MSG_TYPE_COPY_SEND,
+ n_std_ports, std_int_array, n_std_ints);
+
+ return 0;
+}
+
+/* Implement proc_getexecdata as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getexecdata (struct proc *p,
+ mach_port_t **ports,
+ mach_msg_type_name_t *portspoly,
+ size_t *nports,
+ int **ints,
+ size_t *nints)
+{
+ int i;
+ int ports_allocated = 0;
+ /* No need to check P here; we don't use it. */
+
+ if (!std_port_array)
+ return ENOENT;
+
+ if (*nports < n_std_ports)
+ {
+ *ports = mmap (0, round_page (n_std_ports * sizeof (mach_port_t)),
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*ports == MAP_FAILED)
+ return ENOMEM;
+ ports_allocated = 1;
+ }
+ memcpy (*ports, std_port_array, n_std_ports * sizeof (mach_port_t));
+ *nports = n_std_ports;
+
+ if (*nints < n_std_ints)
+ {
+ *ints = mmap (0, round_page (n_std_ints * sizeof (int)),
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*ints == MAP_FAILED)
+ {
+ if (ports_allocated)
+ munmap (*ports, round_page (n_std_ports * sizeof (mach_port_t)));
+ return ENOMEM;
+ }
+ }
+ memcpy (*ints, std_int_array, n_std_ints * sizeof (int));
+ *nints = n_std_ints;
+
+ for (i = 0; i < n_std_ports; i++)
+ mach_port_mod_refs (mach_task_self (), std_port_array[i], MACH_PORT_RIGHT_SEND, 1);
+ *portspoly = MACH_MSG_TYPE_MOVE_SEND;
+
+ return 0;
+}
+
+/* Implement proc_execdata_notify as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_execdata_notify (struct proc *p,
+ mach_port_t notify)
+{
+ struct execdata_notify *n;
+ mach_port_t foo;
+
+ /* No need to check P here; we don't use it. */
+
+ n = malloc (sizeof (struct execdata_notify));
+ if (! n)
+ return ENOMEM;
+
+ n->notify_port = notify;
+ n->next = execdata_notifys;
+ execdata_notifys = n;
+
+ mach_port_request_notification (mach_task_self (), notify,
+ MACH_NOTIFY_DEAD_NAME, 1,
+ generic_port, MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &foo);
+
+ if (foo)
+ mach_port_deallocate (mach_task_self (), foo);
+
+ if (std_port_array)
+ exec_setexecdata (n->notify_port, std_port_array, MACH_MSG_TYPE_COPY_SEND,
+ n_std_ports, std_int_array, n_std_ints);
+ return 0;
+}
+
+/* Check all the execdata notify ports and see if one of them is
+ PORT; if it is, then free it. */
+void
+check_dead_execdata_notify (mach_port_t port)
+{
+ struct execdata_notify *en, **prevp;
+
+ for (en = execdata_notifys, prevp = &execdata_notifys; en; en = *prevp)
+ {
+ if (en->notify_port == port)
+ {
+ mach_port_deallocate (mach_task_self (), port);
+ *prevp = en->next;
+ free (en);
+ }
+ else
+ prevp = &en->next;
+ }
+}
+
+/* Version information handling.
+
+ A server registers its name and version with proc_register_version.
+
+ The uname release is the most popular version number.
+
+ The uname version string is composed of all the server names and
+ versions, omitting special mention of those which match the uname
+ release, plus the kernel version string. */
+
+char *kernel_name, *kernel_version;
+
+
+/* Rebuild the uname version string. */
+static void
+rebuild_uname (void)
+{
+ unsigned int i, j;
+ char *p, *end;
+
+ /* Set up for addstr to write into STRING. */
+ inline void initstr (char *string)
+ {
+ p = string;
+ end = p + _UTSNAME_LENGTH;
+ }
+ /* If NAME is not null, write "name-version/", else "version/". */
+ inline void addstr (const char *name, const char *version)
+ {
+ size_t len;
+ if (name)
+ {
+ len = strlen (name);
+ if (p + len + 1 < end)
+ memcpy (p, name, len);
+ p += len;
+ if (p < end)
+ *p++ = '-';
+ }
+ len = strlen (version);
+ if (p + len + 1 < end)
+ memcpy (p, version, len);
+ p += len;
+ if (p < end)
+ *p++ = '/';
+ }
+
+ /* Collect all the differing version strings and count how many
+ servers use each. */
+ struct version
+ {
+ const char *version;
+ unsigned int count;
+ } versions[nserver_versions];
+ int compare_versions (const void *a, const void *b)
+ {
+ return (((const struct version *) b)->count -
+ ((const struct version *) a)->count);
+ }
+ unsigned int nversions = 0;
+
+ for (i = 0; i < nserver_versions; ++i)
+ {
+ for (j = 0; j < nversions; ++j)
+ if (! strcmp (versions[j].version, server_versions[i].version))
+ {
+ ++versions[j].count;
+ break;
+ }
+ if (j == nversions)
+ {
+ versions[nversions].version = server_versions[i].version;
+ versions[nversions].count = 1;
+ ++nversions;
+ }
+ }
+
+ /* Sort the versions in order of decreasing popularity. */
+ qsort (versions, nversions, sizeof (struct version), compare_versions);
+
+ /* Now build the uname strings. */
+
+ /* release is the most popular version */
+ strcpy (uname_info.release, versions[0].version);
+
+ initstr (uname_info.version);
+
+ addstr (kernel_name, kernel_version);
+
+ if (versions[0].count > 1)
+ addstr ("Hurd", versions[0].version);
+
+ /* Now, for any which differ (if there might be any), write it out
+ separately. */
+ if (versions[0].count != nserver_versions)
+ for (i = 0; i < nserver_versions; i++)
+ if (versions[0].count == 1
+ || strcmp (server_versions[i].version, versions[0].version))
+ addstr (server_versions[i].name, server_versions[i].version);
+
+ if (p > end)
+#ifdef notyet
+ syslog (LOG_EMERG,
+ "_UTSNAME_LENGTH %u too short; inform bug-glibc@prep.ai.mit.edu\n",
+ p - end)
+#endif
+ ;
+ else
+ p[-1] = '\0';
+ end[-1] = '\0';
+}
+
+void
+initialize_version_info (void)
+{
+ extern const char *const mach_cpu_types[];
+ extern const char *const mach_cpu_subtypes[][32];
+ kernel_version_t kv;
+ char *p;
+ struct host_basic_info info;
+ size_t n = sizeof info;
+ error_t err;
+
+ /* Fill in fixed slots sysname and machine. */
+ strcpy (uname_info.sysname, "GNU");
+
+ err = host_info (mach_host_self (), HOST_BASIC_INFO,
+ (host_info_t) &info, &n);
+ assert (! err);
+ snprintf (uname_info.machine, sizeof uname_info.machine, "%s-%s",
+ mach_cpu_types[info.cpu_type],
+ mach_cpu_subtypes[info.cpu_type][info.cpu_subtype]);
+
+ /* Notice Mach's and our own version and initialize server version
+ variables. */
+ server_versions = malloc (sizeof (struct server_version) * 10);
+ assert (server_versions);
+ server_versions_nalloc = 10;
+
+ err = host_kernel_version (mach_host_self (), kv);
+ assert (! err);
+ /* Make sure the result is null-terminated, as the kernel doesn't
+ guarantee it. */
+ kv[sizeof (kv) - 1] = '\0';
+ p = index (kv, ':');
+ if (p)
+ *p = '\0';
+ p = index (kv, ' ');
+ if (p)
+ *p = '\0';
+ kernel_name = strdup (p ? kv : "mach");
+ assert (kernel_name);
+ kernel_version = strdup (p ? p + 1 : kv);
+ assert (kernel_version);
+
+ server_versions[0].name = strdup ("proc");
+ assert (server_versions[0].name);
+ server_versions[0].version = strdup (HURD_VERSION);
+ assert (server_versions[0].version);
+
+ nserver_versions = 1;
+
+ rebuild_uname ();
+
+ uname_info.nodename[0] = '\0';
+}
+
+kern_return_t
+S_proc_uname (pstruct_t process,
+ struct utsname *uname)
+{
+ /* No need to check PROCESS here, we don't use it. */
+ *uname = uname_info;
+ return 0;
+}
+
+kern_return_t
+S_proc_register_version (pstruct_t server,
+ mach_port_t credential,
+ char *name,
+ char *release,
+ char *version)
+{
+ error_t err = 0;
+ int i;
+
+ /* No need to check SERVER here; we don't use it. */
+
+ if (credential != _hurd_host_priv)
+ /* Must be privileged to register for uname. */
+ return EPERM;
+
+ for (i = 0; i < nserver_versions; i++)
+ if (!strcmp (name, server_versions[i].name))
+ {
+ /* Change this entry. */
+ free (server_versions[i].version);
+ server_versions[i].version = malloc (strlen (version) + 1);
+ if (! server_versions[i].version)
+ {
+ err = ENOMEM;
+ goto out;
+ }
+ strcpy (server_versions[i].version, version);
+ break;
+ }
+ if (i == nserver_versions)
+ {
+ /* Didn't find it; extend. */
+ if (nserver_versions == server_versions_nalloc)
+ {
+ void *new = realloc (server_versions,
+ sizeof (struct server_version) *
+ server_versions_nalloc * 2);
+ if (! new)
+ {
+ err = ENOMEM;
+ goto out;
+ }
+
+ server_versions_nalloc *= 2;
+ server_versions = new;
+ }
+ server_versions[nserver_versions].name = malloc (strlen (name) + 1);
+ if (! server_versions[nserver_versions].name)
+ {
+ err = ENOMEM;
+ goto out;
+ }
+ server_versions[nserver_versions].version = malloc (strlen (version)
+ + 1);
+ if (! server_versions[nserver_versions].version)
+ {
+ free (server_versions[nserver_versions].name);
+ err = ENOMEM;
+ goto out;
+ }
+ strcpy (server_versions[nserver_versions].name, name);
+ strcpy (server_versions[nserver_versions].version, version);
+ nserver_versions++;
+ }
+
+ rebuild_uname ();
+out:
+ if (!err)
+ mach_port_deallocate (mach_task_self (), credential);
+ return err;
+}
diff --git a/proc/info.c b/proc/info.c
new file mode 100644
index 00000000..0d502c65
--- /dev/null
+++ b/proc/info.c
@@ -0,0 +1,801 @@
+/* Process information queries
+ Copyright (C) 1992,93,94,95,96,99,2000,01,02 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include <mach.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <hurd/hurd_types.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <assert.h>
+#include <hurd/msg.h>
+
+#include "proc.h"
+#include "process_S.h"
+
+
+/* Returns true if PROC1 has `owner' privileges over PROC2 (and can thus get
+ its task port &c). If PROC2 has an owner, then PROC1 must have that uid;
+ otherwise, both must be in the same login collection. */
+int
+check_owner (struct proc *proc1, struct proc *proc2)
+{
+ return
+ proc2->p_noowner
+ ? check_uid (proc1, 0) || proc1->p_login == proc2->p_login
+ : check_uid (proc1, proc2->p_owner);
+}
+
+
+/* Implement S_proc_pid2task as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_pid2task (struct proc *callerp,
+ pid_t pid,
+ task_t *t)
+{
+ struct proc *p;
+
+ if (!callerp)
+ return EOPNOTSUPP;
+
+ p = pid_find_allow_zombie (pid);
+ if (!p)
+ return ESRCH;
+
+ if (p->p_dead)
+ {
+ *t = MACH_PORT_NULL;
+ return 0;
+ }
+
+ if (! check_owner (callerp, p))
+ return EPERM;
+
+ assert (MACH_PORT_VALID (p->p_task));
+ *t = p->p_task;
+
+ return 0;
+}
+
+/* Implement proc_task2pid as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_task2pid (struct proc *callerp,
+ task_t t,
+ pid_t *pid)
+{
+ struct proc *p = task_find (t);
+
+ /* No need to check CALLERP here; we don't use it. */
+
+ if (!p)
+ return ESRCH;
+
+ *pid = p->p_pid;
+ mach_port_deallocate (mach_task_self (), t);
+ return 0;
+}
+
+/* Implement proc_task2proc as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_task2proc (struct proc *callerp,
+ task_t t,
+ mach_port_t *outproc)
+{
+ struct proc *p = task_find (t);
+
+ /* No need to check CALLERP here; we don't use it. */
+
+ if (!p)
+ return ESRCH;
+
+ *outproc = ports_get_right (p);
+ mach_port_deallocate (mach_task_self (), t);
+ return 0;
+}
+
+/* Implement proc_proc2task as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_proc2task (struct proc *p,
+ task_t *t)
+{
+ if (!p)
+ return EOPNOTSUPP;
+ *t = p->p_task;
+ return 0;
+}
+
+/* Implement proc_pid2proc as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_pid2proc (struct proc *callerp,
+ pid_t pid,
+ mach_port_t *outproc)
+{
+ struct proc *p;
+
+ if (!callerp)
+ return EOPNOTSUPP;
+
+ p = pid_find_allow_zombie (pid);
+ if (!p)
+ return ESRCH;
+
+ if (p->p_dead)
+ {
+ *outproc = MACH_PORT_NULL;
+ return 0;
+ }
+
+ if (! check_owner (callerp, p))
+ return EPERM;
+
+ *outproc = ports_get_right (p);
+ return 0;
+}
+
+
+/* Read a string starting at address ADDR in task T; set *STR to point at
+ newly malloced storage holding it, and *LEN to its length with null. */
+static error_t
+get_string (task_t t,
+ vm_address_t addr,
+ char **str, size_t *len)
+{
+ /* This version assumes that a string is never more than one
+ page in length. */
+
+ vm_address_t readaddr;
+ vm_address_t data;
+ size_t readlen;
+ error_t err;
+ char *c;
+
+ readaddr = trunc_page (addr);
+ err = vm_read (t, readaddr, vm_page_size * 2, &data, &readlen);
+ if (err == KERN_INVALID_ADDRESS)
+ err = vm_read (t, readaddr, vm_page_size, &data, &readlen);
+ if (err == MACH_SEND_INVALID_DEST)
+ err = ESRCH;
+ if (err)
+ return err;
+
+ /* Scan for a null. */
+ c = memchr ((char *) (data + (addr - readaddr)), '\0',
+ readlen - (addr - readaddr));
+ if (c == NULL)
+ err = KERN_INVALID_ADDRESS;
+ else
+ {
+ c++; /* Include the null. */
+ *len = c - (char *) (data + (addr - readaddr));
+ *str = malloc (*len);
+ if (*str == NULL)
+ err = ENOMEM;
+ else
+ memcpy (*str, (char *) data + (addr - readaddr), *len);
+ }
+
+ munmap ((caddr_t) data, readlen);
+ return err;
+}
+
+/* Read a vector of addresses (stored as are argv and envp) from task TASK
+ found at address ADDR. Set *VEC to point to newly malloced storage holding
+ the addresses. */
+static error_t
+get_vector (task_t task,
+ vm_address_t addr,
+ int **vec)
+{
+ vm_address_t readaddr;
+ vm_size_t readsize;
+ vm_address_t scanned;
+ error_t err;
+
+ *vec = NULL;
+ readaddr = trunc_page (addr);
+ readsize = 0;
+ scanned = addr;
+ do
+ {
+ vm_address_t data;
+ mach_msg_type_number_t readlen = 0;
+ vm_address_t *t;
+
+ readsize += vm_page_size;
+ err = vm_read (task, readaddr, readsize, &data, &readlen);
+ if (err == MACH_SEND_INVALID_DEST)
+ err = ESRCH;
+ if (err)
+ return err;
+
+ /* Scan for a null. */
+ for (t = (vm_address_t *) (data + (scanned - readaddr));
+ t < (vm_address_t *) (data + readlen);
+ ++t)
+ if (*t == 0)
+ {
+ ++t; /* Include the null. */
+ *vec = malloc ((char *)t - (char *)(data + (addr - readaddr)));
+ if (*vec == NULL)
+ err = ENOMEM;
+ else
+ memcpy (*vec, (char *)(data + (addr - readaddr)),
+ (char *)t - (char *)(data + (addr - readaddr)));
+ break;
+ }
+
+ /* If we didn't find the null terminator, then we will loop
+ to read an additional page. */
+ scanned = readaddr + readlen;
+ munmap ((caddr_t) data, readlen);
+ } while (!err && *vec == NULL);
+
+ return err;
+}
+
+/* Fetch an array of strings at address LOC in task T into
+ BUF of size BUFLEN. */
+static error_t
+get_string_array (task_t t,
+ vm_address_t loc,
+ vm_address_t *buf,
+ size_t *buflen)
+{
+ char *bp;
+ int *vector, *vp;
+ error_t err;
+ vm_address_t origbuf = *buf;
+
+ err = get_vector (t, loc, &vector);
+ if (err)
+ return err;
+
+ bp = (char *) *buf;
+ for (vp = vector; *vp; ++vp)
+ {
+ char *string;
+ size_t len;
+
+ err = get_string (t, *vp, &string, &len);
+ if (err)
+ {
+ free (vector);
+ if (*buf != origbuf)
+ munmap ((caddr_t) *buf, *buflen);
+ return err;
+ }
+
+ if (len > (char *) *buf + *buflen - bp)
+ {
+ char *newbuf;
+ vm_size_t prev_len = bp - (char *) *buf;
+ vm_size_t newsize = *buflen * 2;
+
+ if (newsize < prev_len + len)
+ /* Since we will mmap whole pages anyway,
+ notice how much space we really have. */
+ newsize = round_page (prev_len + len);
+
+ newbuf = mmap (0, newsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (newbuf == MAP_FAILED)
+ {
+ err = errno;
+ free (string);
+ free (vector);
+ if (*buf != origbuf)
+ munmap ((caddr_t) *buf, *buflen);
+ return err;
+ }
+
+ memcpy (newbuf, (char *) *buf, prev_len);
+ bp = newbuf + prev_len;
+ if (*buf != origbuf)
+ munmap ((caddr_t) *buf, *buflen);
+
+ *buf = (vm_address_t) newbuf;
+ *buflen = newsize;
+ }
+
+ memcpy (bp, string, len);
+ bp += len;
+ free (string);
+ }
+
+ free (vector);
+ *buflen = bp - (char *) *buf;
+ return 0;
+}
+
+
+/* Implement proc_getprocargs as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getprocargs (struct proc *callerp,
+ pid_t pid,
+ char **buf,
+ size_t *buflen)
+{
+ struct proc *p = pid_find (pid);
+
+ /* No need to check CALLERP here; we don't use it. */
+
+ if (!p)
+ return ESRCH;
+
+ return get_string_array (p->p_task, p->p_argv, (vm_address_t *) buf, buflen);
+}
+
+/* Implement proc_getprocenv as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getprocenv (struct proc *callerp,
+ pid_t pid,
+ char **buf,
+ size_t *buflen)
+{
+ struct proc *p = pid_find (pid);
+
+ /* No need to check CALLERP here; we don't use it. */
+
+ if (!p)
+ return ESRCH;
+
+ return get_string_array (p->p_task, p->p_envp, (vm_address_t *)buf, buflen);
+}
+
+/* Handy abbreviation for all the various thread details. */
+#define PI_FETCH_THREAD_DETAILS \
+ (PI_FETCH_THREAD_SCHED | PI_FETCH_THREAD_BASIC | PI_FETCH_THREAD_WAITS)
+
+/* Implement proc_getprocinfo as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getprocinfo (struct proc *callerp,
+ pid_t pid,
+ int *flags,
+ int **piarray,
+ size_t *piarraylen,
+ char **waits, mach_msg_type_number_t *waits_len)
+{
+ struct proc *p = pid_find (pid);
+ struct procinfo *pi;
+ size_t nthreads;
+ thread_t *thds;
+ error_t err = 0;
+ size_t structsize;
+ int i;
+ int pi_alloced = 0, waits_alloced = 0;
+ /* The amount of WAITS we've filled in so far. */
+ mach_msg_type_number_t waits_used = 0;
+ size_t tkcount, thcount;
+ struct proc *tp;
+ task_t task; /* P's task port. */
+ mach_port_t msgport; /* P's msgport, or MACH_PORT_NULL if none. */
+
+ /* No need to check CALLERP here; we don't use it. */
+
+ if (!p)
+ return ESRCH;
+
+ task = p->p_task;
+
+ check_msgport_death (p);
+ msgport = p->p_msgport;
+
+ if (*flags & PI_FETCH_THREAD_DETAILS)
+ *flags |= PI_FETCH_THREADS;
+
+ if (*flags & PI_FETCH_THREADS)
+ {
+ err = task_threads (p->p_task, &thds, &nthreads);
+ if (err == MACH_SEND_INVALID_DEST)
+ err = ESRCH;
+ if (err)
+ return err;
+ }
+ else
+ nthreads = 0;
+
+ structsize = sizeof (struct procinfo);
+ if (*flags & PI_FETCH_THREAD_DETAILS)
+ structsize += nthreads * sizeof (pi->threadinfos[0]);
+
+ if (structsize / sizeof (int) > *piarraylen)
+ {
+ *piarray = mmap (0, structsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*piarray == MAP_FAILED)
+ {
+ err = errno;
+ if (*flags & PI_FETCH_THREADS)
+ {
+ for (i = 0; i < nthreads; i++)
+ mach_port_deallocate (mach_task_self (), thds[i]);
+ munmap (thds, nthreads * sizeof (thread_t));
+ }
+ return err;
+ }
+ pi_alloced = 1;
+ }
+ *piarraylen = structsize / sizeof (int);
+ pi = (struct procinfo *) *piarray;
+
+ pi->state =
+ ((p->p_stopped ? PI_STOPPED : 0)
+ | (p->p_exec ? PI_EXECED : 0)
+ | (p->p_waiting ? PI_WAITING : 0)
+ | (!p->p_pgrp->pg_orphcnt ? PI_ORPHAN : 0)
+ | (p->p_msgport == MACH_PORT_NULL ? PI_NOMSG : 0)
+ | (p->p_pgrp->pg_session->s_sid == p->p_pid ? PI_SESSLD : 0)
+ | (p->p_noowner ? PI_NOTOWNED : 0)
+ | (!p->p_parentset ? PI_NOPARENT : 0)
+ | (p->p_traced ? PI_TRACED : 0)
+ | (p->p_msgportwait ? PI_GETMSG : 0)
+ | (p->p_loginleader ? PI_LOGINLD : 0));
+ pi->owner = p->p_owner;
+ pi->ppid = p->p_parent->p_pid;
+ pi->pgrp = p->p_pgrp->pg_pgid;
+ pi->session = p->p_pgrp->pg_session->s_sid;
+ for (tp = p; !tp->p_loginleader; tp = tp->p_parent)
+ assert (tp);
+ pi->logincollection = tp->p_pid;
+ if (p->p_dead || p->p_stopped)
+ {
+ pi->exitstatus = p->p_status;
+ pi->sigcode = p->p_sigcode;
+ }
+ else
+ pi->exitstatus = pi->sigcode = 0;
+
+ pi->nthreads = nthreads;
+
+ /* Release GLOBAL_LOCK around time consuming bits, and more importatantly,
+ potential calls to P's msgport, which can block. */
+ pthread_mutex_unlock (&global_lock);
+
+ if (*flags & PI_FETCH_TASKINFO)
+ {
+ tkcount = TASK_BASIC_INFO_COUNT;
+ err = task_info (task, TASK_BASIC_INFO,
+ (task_info_t) &pi->taskinfo, &tkcount);
+ if (err == MACH_SEND_INVALID_DEST)
+ err = ESRCH;
+#ifdef TASK_SCHED_TIMESHARE_INFO
+ if (!err)
+ {
+ tkcount = TASK_SCHED_TIMESHARE_INFO_COUNT;
+ err = task_info (task, TASK_SCHED_TIMESHARE_INFO,
+ (int *)&pi->timeshare_base_info, &tkcount);
+ if (err == KERN_INVALID_POLICY)
+ {
+ pi->timeshare_base_info.base_priority = -1;
+ err = 0;
+ }
+ }
+#endif
+ }
+ if (*flags & PI_FETCH_TASKEVENTS)
+ {
+ tkcount = TASK_EVENTS_INFO_COUNT;
+ err = task_info (task, TASK_EVENTS_INFO,
+ (task_info_t) &pi->taskevents, &tkcount);
+ if (err == MACH_SEND_INVALID_DEST)
+ err = ESRCH;
+ if (err)
+ {
+ /* Something screwy, give up on this bit of info. */
+ *flags &= ~PI_FETCH_TASKEVENTS;
+ err = 0;
+ }
+ }
+
+ for (i = 0; i < nthreads; i++)
+ {
+ if (*flags & PI_FETCH_THREAD_DETAILS)
+ pi->threadinfos[i].died = 0;
+ if (*flags & PI_FETCH_THREAD_BASIC)
+ {
+ thcount = THREAD_BASIC_INFO_COUNT;
+ err = thread_info (thds[i], THREAD_BASIC_INFO,
+ (thread_info_t) &pi->threadinfos[i].pis_bi,
+ &thcount);
+ if (err == MACH_SEND_INVALID_DEST)
+ {
+ pi->threadinfos[i].died = 1;
+ err = 0;
+ continue;
+ }
+ else if (err)
+ /* Something screwy, give up on this bit of info. */
+ {
+ *flags &= ~PI_FETCH_THREAD_BASIC;
+ err = 0;
+ }
+ }
+
+ if (*flags & PI_FETCH_THREAD_SCHED)
+ {
+ thcount = THREAD_SCHED_INFO_COUNT;
+ err = thread_info (thds[i], THREAD_SCHED_INFO,
+ (thread_info_t) &pi->threadinfos[i].pis_si,
+ &thcount);
+ if (err == MACH_SEND_INVALID_DEST)
+ {
+ pi->threadinfos[i].died = 1;
+ err = 0;
+ continue;
+ }
+ if (err)
+ /* Something screwy, give up on this bit of info. */
+ {
+ *flags &= ~PI_FETCH_THREAD_SCHED;
+ err = 0;
+ }
+ }
+
+ /* Note that there are thread wait entries only for those threads
+ not marked dead. */
+
+ if (*flags & PI_FETCH_THREAD_WAITS)
+ {
+ /* See what thread I is waiting on. */
+ if (msgport == MACH_PORT_NULL)
+ *flags &= ~PI_FETCH_THREAD_WAITS; /* Can't return much... */
+ else
+ {
+ string_t desc;
+ size_t desc_len;
+
+ if (msg_report_wait (msgport, thds[i],
+ desc, &pi->threadinfos[i].rpc_block))
+ desc[0] = '\0'; /* Don't know. */
+
+ /* See how long DESC is, being sure not to barf if it's
+ unterminated (string_t's are fixed length). */
+ desc_len = strnlen (desc, sizeof desc);
+
+ if (waits_used + desc_len + 1 > *waits_len)
+ /* Not enough room in WAITS, we must allocate more. */
+ {
+ char *new_waits = 0;
+ mach_msg_type_number_t new_len =
+ round_page (waits_used + desc_len + 1);
+
+ new_waits = mmap (0, new_len, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ err = (new_waits == MAP_FAILED) ? errno : 0;
+ if (err)
+ /* Just don't return any more waits information. */
+ *flags &= ~PI_FETCH_THREAD_WAITS;
+ else
+ {
+ if (waits_used > 0)
+ memcpy (new_waits, *waits, waits_used);
+ if (*waits_len > 0 && waits_alloced)
+ munmap (*waits, *waits_len);
+ *waits = new_waits;
+ *waits_len = new_len;
+ waits_alloced = 1;
+ }
+ }
+
+ if (waits_used + desc_len + 1 <= *waits_len)
+ /* Append DESC to WAITS. */
+ {
+ memcpy (*waits + waits_used, desc, desc_len);
+ waits_used += desc_len;
+ (*waits)[waits_used++] = '\0';
+ }
+ }
+ }
+
+ mach_port_deallocate (mach_task_self (), thds[i]);
+ }
+
+ if (*flags & PI_FETCH_THREADS)
+ munmap (thds, nthreads * sizeof (thread_t));
+ if (err && pi_alloced)
+ munmap (*piarray, structsize);
+ if (err && waits_alloced)
+ munmap (*waits, *waits_len);
+ else
+ *waits_len = waits_used;
+
+ /* Reacquire GLOBAL_LOCK to make the central locking code happy. */
+ pthread_mutex_lock (&global_lock);
+
+ return err;
+}
+
+/* Implement proc_make_login_coll as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_make_login_coll (struct proc *p)
+{
+ if (!p)
+ return EOPNOTSUPP;
+ p->p_loginleader = 1;
+ return 0;
+}
+
+/* Implement proc_getloginid as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getloginid (struct proc *callerp,
+ pid_t pid,
+ pid_t *leader)
+{
+ struct proc *proc = pid_find (pid);
+ struct proc *p;
+
+ /* No need to check CALLERP here; we don't use it. */
+
+ if (!proc)
+ return ESRCH;
+
+ for (p = proc; !p->p_loginleader; p = p->p_parent)
+ assert (p);
+
+ *leader = p->p_pid;
+ return 0;
+}
+
+/* Implement proc_getloginpids as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getloginpids (struct proc *callerp,
+ pid_t id,
+ pid_t **pids,
+ size_t *npids)
+{
+ error_t err = 0;
+ struct proc *l = pid_find (id);
+ struct proc *p;
+ struct proc **tail, **new, **parray;
+ int parraysize;
+ int i;
+
+ /* No need to check CALLERP here; we don't use it. */
+
+ if (!l || !l->p_loginleader)
+ return ESRCH;
+
+ /* Simple breadth first search of the children of L. */
+ parraysize = 50;
+ parray = malloc (sizeof (struct proc *) * parraysize);
+ if (! parray)
+ return ENOMEM;
+
+ parray[0] = l;
+ for (tail = parray, new = &parray[1]; tail != new; tail++)
+ {
+ for (p = (*tail)->p_ochild; p; p = p->p_sib)
+ if (!p->p_loginleader)
+ {
+ /* Add P to the list at NEW */
+ if (new - parray > parraysize)
+ {
+ struct proc **newparray;
+ newparray = realloc (parray, ((parraysize *= 2)
+ * sizeof (struct proc *)));
+ if (! newparray)
+ {
+ free (parray);
+ return ENOMEM;
+ }
+
+ tail = newparray + (tail - parray);
+ new = newparray + (new - parray);
+ parray = newparray;
+ }
+ *new++ = p;
+ }
+ }
+
+ if (*npids < new - parray)
+ {
+ *pids = mmap (0, (new - parray) * sizeof (pid_t), PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ if (*pids == MAP_FAILED)
+ err = errno;
+ }
+
+ if (! err)
+ {
+ *npids = new - parray;
+ for (i = 0; i < *npids; i++)
+ (*pids)[i] = parray[i]->p_pid;
+ }
+
+ free (parray);
+ return err;
+}
+
+/* Implement proc_setlogin as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_setlogin (struct proc *p,
+ char *login)
+{
+ struct login *l;
+
+ if (!p)
+ return EOPNOTSUPP;
+
+ if (!check_uid (p, 0))
+ return EPERM;
+
+ l = malloc (sizeof (struct login) + strlen (login) + 1);
+ if (! l)
+ return ENOMEM;
+
+ l->l_refcnt = 1;
+ strcpy (l->l_name, login);
+ if (!--p->p_login->l_refcnt)
+ free (p->p_login);
+ p->p_login = l;
+ return 0;
+}
+
+/* Implement proc_getlogin as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getlogin (struct proc *p,
+ char *login)
+{
+ if (!p)
+ return EOPNOTSUPP;
+ strcpy (login, p->p_login->l_name);
+ return 0;
+}
+
+/* Implement proc_get_tty as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_get_tty (struct proc *p, pid_t pid,
+ mach_port_t *tty, mach_msg_type_name_t *tty_type)
+{
+ return EOPNOTSUPP; /* XXX */
+}
+
+/* Implement proc_getnports as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getnports (struct proc *callerp,
+ pid_t pid,
+ mach_msg_type_number_t *nports)
+{
+ struct proc *p = pid_find (pid);
+ mach_port_array_t names;
+ mach_msg_type_number_t ncount;
+ mach_port_type_array_t types;
+ mach_msg_type_number_t tcount;
+ error_t err = 0;
+
+ /* No need to check CALLERP here; we don't use it. */
+
+ if (!p)
+ return ESRCH;
+
+ err = mach_port_names (p->p_task, &names, &ncount, &types, &tcount);
+ if (err == KERN_INVALID_TASK)
+ err = ESRCH;
+
+ if (!err) {
+ *nports = ncount;
+
+ munmap (names, ncount * sizeof (mach_port_t));
+ munmap (types, tcount * sizeof (mach_port_type_t));
+ }
+
+ return err;
+}
diff --git a/proc/main.c b/proc/main.c
new file mode 100644
index 00000000..73742edd
--- /dev/null
+++ b/proc/main.c
@@ -0,0 +1,146 @@
+/* Initialization of the proc server
+ Copyright (C) 1993,94,95,96,97,99,2000,01 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include <mach.h>
+#include <hurd/hurd_types.h>
+#include <hurd.h>
+#include <hurd/startup.h>
+#include <device/device.h>
+#include <assert.h>
+#include <argp.h>
+#include <error.h>
+#include <version.h>
+#include <pids.h>
+
+#include "proc.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (proc);
+
+#include "process_S.h"
+#include "notify_S.h"
+#include "../libports/interrupt_S.h"
+#include "proc_exc_S.h"
+
+int
+message_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ mig_routine_t routine;
+ if ((routine = process_server_routine (inp)) ||
+ (routine = notify_server_routine (inp)) ||
+ (routine = ports_interrupt_server_routine (inp)) ||
+ (routine = proc_exc_server_routine (inp)))
+ {
+ pthread_mutex_lock (&global_lock);
+ (*routine) (inp, outp);
+ pthread_mutex_unlock (&global_lock);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER;
+
+int
+main (int argc, char **argv, char **envp)
+{
+ mach_port_t boot;
+ error_t err;
+ mach_port_t pset, psetcntl;
+ void *genport;
+ process_t startup_port;
+ struct argp argp = { 0, 0, 0, "Hurd process server" };
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ initialize_version_info ();
+
+ err = task_get_bootstrap_port (mach_task_self (), &boot);
+ assert_perror (err);
+ if (boot == MACH_PORT_NULL)
+ error (2, 0, "proc server can only be run by init during boot");
+
+ proc_bucket = ports_create_bucket ();
+ proc_class = ports_create_class (0, 0);
+ generic_port_class = ports_create_class (0, 0);
+ exc_class = ports_create_class (exc_clean, 0);
+ ports_create_port (generic_port_class, proc_bucket,
+ sizeof (struct port_info), &genport);
+ generic_port = ports_get_right (genport);
+
+ /* Create the initial proc object for init (PID 1). */
+ startup_proc = create_startup_proc ();
+
+ /* Create our own proc object. */
+ self_proc = allocate_proc (mach_task_self ());
+ assert (self_proc);
+
+ complete_proc (self_proc, HURD_PID_PROC);
+
+ startup_port = ports_get_send_right (startup_proc);
+ err = startup_procinit (boot, startup_port, &startup_proc->p_task,
+ &authserver, &_hurd_host_priv, &_hurd_device_master);
+ assert_perror (err);
+ mach_port_deallocate (mach_task_self (), startup_port);
+
+ mach_port_mod_refs (mach_task_self (), authserver, MACH_PORT_RIGHT_SEND, 1);
+ _hurd_port_set (&_hurd_ports[INIT_PORT_AUTH], authserver);
+ mach_port_deallocate (mach_task_self (), boot);
+
+ proc_death_notify (startup_proc);
+ add_proc_to_hash (startup_proc); /* Now that we have the task port. */
+
+ /* Set our own argv and envp locations. */
+ self_proc->p_argv = (vm_address_t) argv;
+ self_proc->p_envp = (vm_address_t) envp;
+
+ /* Give ourselves good scheduling performance, because we are so
+ important. */
+ err = thread_get_assignment (mach_thread_self (), &pset);
+ assert_perror (err);
+ err = host_processor_set_priv (_hurd_host_priv, pset, &psetcntl);
+ assert_perror (err);
+ thread_max_priority (mach_thread_self (), psetcntl, 0);
+ assert_perror (err);
+ err = task_priority (mach_task_self (), 2, 1);
+ assert_perror (err);
+
+ mach_port_deallocate (mach_task_self (), pset);
+ mach_port_deallocate (mach_task_self (), psetcntl);
+
+ {
+ /* Get our stderr set up to print on the console, in case we have
+ to panic or something. */
+ mach_port_t cons;
+ error_t err;
+ err = device_open (_hurd_device_master, D_READ|D_WRITE, "console", &cons);
+ assert_perror (err);
+ stdin = mach_open_devstream (cons, "r");
+ stdout = stderr = mach_open_devstream (cons, "w");
+ mach_port_deallocate (mach_task_self (), cons);
+ }
+
+ while (1)
+ ports_manage_port_operations_multithread (proc_bucket,
+ message_demuxer,
+ 0, 0, 0);
+}
diff --git a/proc/mgt.c b/proc/mgt.c
new file mode 100644
index 00000000..b8aa0fc1
--- /dev/null
+++ b/proc/mgt.c
@@ -0,0 +1,947 @@
+/* Process management
+ Copyright (C) 1992,93,94,95,96,99,2000,01,02 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include <mach.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <hurd/hurd_types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mach/notify.h>
+#include <sys/wait.h>
+#include <mach/mig_errors.h>
+#include <sys/resource.h>
+#include <hurd/auth.h>
+#include <assert.h>
+#include <pids.h>
+
+#include "proc.h"
+#include "process_S.h"
+#include "mutated_ourmsg_U.h"
+#include "proc_exc_S.h"
+#include "proc_exc_U.h"
+#include <hurd/signal.h>
+
+/* Create a new id structure with the given genuine uids and gids. */
+static inline struct ids *
+make_ids (const uid_t *uids, size_t nuids)
+{
+ struct ids *i;
+
+ i = malloc (sizeof (struct ids) + sizeof (uid_t) * nuids);;
+ if (! i)
+ return NULL;
+
+ i->i_nuids = nuids;
+ i->i_refcnt = 1;
+
+ memcpy (&i->i_uids, uids, sizeof (uid_t) * nuids);
+ return i;
+}
+
+static inline void
+ids_ref (struct ids *i)
+{
+ i->i_refcnt ++;
+}
+
+/* Free an id structure. */
+static inline void
+ids_rele (struct ids *i)
+{
+ i->i_refcnt --;
+ if (i->i_refcnt == 0)
+ free (i);
+}
+
+/* Tell if process P has uid UID, or has root. */
+int
+check_uid (struct proc *p, uid_t uid)
+{
+ int i;
+ for (i = 0; i < p->p_id->i_nuids; i++)
+ if (p->p_id->i_uids[i] == uid || p->p_id->i_uids[i] == 0)
+ return 1;
+ return 0;
+}
+
+/* Implement proc_reathenticate as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_reauthenticate (struct proc *p, mach_port_t rendport)
+{
+ error_t err;
+ uid_t gubuf[50], aubuf[50], ggbuf[50], agbuf[50];
+ uid_t *gen_uids, *aux_uids, *gen_gids, *aux_gids;
+ size_t ngen_uids, naux_uids, ngen_gids, naux_gids;
+
+ if (!p)
+ return EOPNOTSUPP;
+
+ gen_uids = gubuf;
+ aux_uids = aubuf;
+ gen_gids = ggbuf;
+ aux_gids = agbuf;
+
+ ngen_uids = sizeof (gubuf) / sizeof (uid_t);
+ naux_uids = sizeof (aubuf) / sizeof (uid_t);
+ ngen_gids = sizeof (ggbuf) / sizeof (uid_t);
+ naux_gids = sizeof (agbuf) / sizeof (uid_t);
+
+ /* Release the global lock while blocking on the auth server and client. */
+ pthread_mutex_unlock (&global_lock);
+ err = auth_server_authenticate (authserver,
+ rendport, MACH_MSG_TYPE_COPY_SEND,
+ MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND,
+ &gen_uids, &ngen_uids,
+ &aux_uids, &naux_uids,
+ &gen_gids, &ngen_gids,
+ &aux_gids, &naux_gids);
+ pthread_mutex_lock (&global_lock);
+
+ if (err)
+ return err;
+
+ if (p->p_dead)
+ /* The process died while we had the lock released.
+ Its p_id field is no longer valid and we shouldn't touch it. */
+ err = EAGAIN;
+ else
+ {
+ ids_rele (p->p_id);
+ p->p_id = make_ids (gen_uids, ngen_uids);
+ if (! p->p_id)
+ err = ENOMEM;
+ }
+
+ if (gen_uids != gubuf)
+ munmap (gen_uids, ngen_uids * sizeof (uid_t));
+ if (aux_uids != aubuf)
+ munmap (aux_uids, naux_uids * sizeof (uid_t));
+ if (gen_gids != ggbuf)
+ munmap (gen_gids, ngen_gids * sizeof (uid_t));
+ if (aux_gids != agbuf)
+ munmap (aux_gids, naux_gids * sizeof (uid_t));
+
+ if (!err)
+ mach_port_deallocate (mach_task_self (), rendport);
+ return err;
+}
+
+/* Implement proc_child as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_child (struct proc *parentp,
+ task_t childt)
+{
+ struct proc *childp;
+
+ if (!parentp)
+ return EOPNOTSUPP;
+
+ childp = task_find (childt);
+ if (!childp)
+ return ESRCH;
+
+ if (childp->p_parentset)
+ return EBUSY;
+
+ mach_port_deallocate (mach_task_self (), childt);
+
+ /* Process identification.
+ Leave p_task and p_pid alone; all the rest comes from the
+ new parent. */
+
+ if (!--childp->p_login->l_refcnt)
+ free (childp->p_login);
+ childp->p_login = parentp->p_login;
+ childp->p_login->l_refcnt++;
+
+ childp->p_owner = parentp->p_owner;
+ childp->p_noowner = parentp->p_noowner;
+
+ ids_rele (childp->p_id);
+ ids_ref (parentp->p_id);
+ childp->p_id = parentp->p_id;
+
+ /* Process hierarchy. Remove from our current location
+ and place us under our new parent. Sanity check to make sure
+ parent is currently init. */
+ assert (childp->p_parent == startup_proc);
+ if (childp->p_sib)
+ childp->p_sib->p_prevsib = childp->p_prevsib;
+ *childp->p_prevsib = childp->p_sib;
+
+ childp->p_parent = parentp;
+ childp->p_sib = parentp->p_ochild;
+ childp->p_prevsib = &parentp->p_ochild;
+ if (parentp->p_ochild)
+ parentp->p_ochild->p_prevsib = &childp->p_sib;
+ parentp->p_ochild = childp;
+
+ /* Process group structure. */
+ if (childp->p_pgrp != parentp->p_pgrp)
+ {
+ leave_pgrp (childp);
+ childp->p_pgrp = parentp->p_pgrp;
+ join_pgrp (childp);
+ /* Not necessary to call newids ourself because join_pgrp does
+ it for us. */
+ }
+ else if (childp->p_msgport != MACH_PORT_NULL)
+ nowait_msg_proc_newids (childp->p_msgport, childp->p_task,
+ childp->p_parent->p_pid, childp->p_pgrp->pg_pgid,
+ !childp->p_pgrp->pg_orphcnt);
+ childp->p_parentset = 1;
+
+ /* If these are not set in the child, it was probably fork(2)ed. If
+ so, it inherits the values of its parent. */
+ if (! childp->start_code && ! childp->end_code)
+ {
+ childp->start_code = parentp->start_code;
+ childp->end_code = parentp->end_code;
+ }
+
+ return 0;
+}
+
+/* Implement proc_reassign as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_reassign (struct proc *p,
+ task_t newt)
+{
+ struct proc *stubp;
+
+ if (!p)
+ return EOPNOTSUPP;
+
+ stubp = task_find (newt);
+ if (!stubp)
+ return ESRCH;
+
+ if (stubp == p)
+ return EINVAL;
+
+ mach_port_deallocate (mach_task_self (), newt);
+
+ remove_proc_from_hash (p);
+
+ task_terminate (p->p_task);
+ mach_port_destroy (mach_task_self (), p->p_task);
+ p->p_task = stubp->p_task;
+
+ /* For security, we need to use the request port from STUBP */
+ ports_transfer_right (p, stubp);
+
+ /* Enqueued messages might refer to the old task port, so
+ destroy them. */
+ if (p->p_msgport != MACH_PORT_NULL)
+ {
+ mach_port_deallocate (mach_task_self (), p->p_msgport);
+ p->p_msgport = MACH_PORT_NULL;
+ p->p_deadmsg = 1;
+ }
+
+ /* These two are image dependent. */
+ p->p_argv = stubp->p_argv;
+ p->p_envp = stubp->p_envp;
+
+ /* Destroy stubp */
+ stubp->p_task = MACH_PORT_NULL;/* block deallocation */
+ process_has_exited (stubp);
+ stubp->p_waited = 1; /* fake out complete_exit */
+ complete_exit (stubp);
+
+ add_proc_to_hash (p);
+
+ return 0;
+}
+
+/* Implement proc_setowner as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_setowner (struct proc *p,
+ uid_t owner,
+ int clear)
+{
+ if (!p)
+ return EOPNOTSUPP;
+
+ if (clear)
+ p->p_noowner = 1;
+ else
+ {
+ if (! check_uid (p, owner))
+ return EPERM;
+
+ p->p_owner = owner;
+ p->p_noowner = 0;
+ }
+
+ return 0;
+}
+
+/* Implement proc_getpids as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getpids (struct proc *p,
+ pid_t *pid,
+ pid_t *ppid,
+ int *orphaned)
+{
+ if (!p)
+ return EOPNOTSUPP;
+ *pid = p->p_pid;
+ *ppid = p->p_parent->p_pid;
+ *orphaned = !p->p_pgrp->pg_orphcnt;
+ return 0;
+}
+
+/* Implement proc_set_arg_locations as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_set_arg_locations (struct proc *p,
+ vm_address_t argv,
+ vm_address_t envp)
+{
+ if (!p)
+ return EOPNOTSUPP;
+ p->p_argv = argv;
+ p->p_envp = envp;
+ return 0;
+}
+
+/* Implement proc_get_arg_locations as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_get_arg_locations (struct proc *p,
+ vm_address_t *argv,
+ vm_address_t *envp)
+{
+ *argv = p->p_argv;
+ *envp = p->p_envp;
+ return 0;
+}
+
+/* Implement proc_dostop as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_dostop (struct proc *p,
+ thread_t contthread)
+{
+ thread_t threadbuf[2], *threads = threadbuf;
+ size_t nthreads = sizeof (threadbuf) / sizeof (thread_t);
+ int i;
+ error_t err;
+
+ if (!p)
+ return EOPNOTSUPP;
+
+ err = task_suspend (p->p_task);
+ if (err)
+ return err;
+ err = task_threads (p->p_task, &threads, &nthreads);
+ if (err)
+ {
+ task_resume (p->p_task);
+ return err;
+ }
+ /* We can not compare the thread ports with CONTTHREAD, as CONTTHREAD
+ might be a proxy port (for example in rpctrace). For this reason
+ we suspend all threads and then resume CONTTHREAD. */
+ for (i = 0; i < nthreads; i++)
+ {
+ if (threads[i] != contthread)
+ thread_suspend (threads[i]);
+ mach_port_deallocate (mach_task_self (), threads[i]);
+ }
+ if (threads != threadbuf)
+ munmap (threads, nthreads * sizeof (thread_t));
+ err = task_resume (p->p_task);
+ if (err)
+ return err;
+
+ mach_port_deallocate (mach_task_self (), contthread);
+ return 0;
+}
+
+/* Clean state of E before it is deallocated */
+void
+exc_clean (void *arg)
+{
+ struct exc *e = arg;
+ mach_port_deallocate (mach_task_self (), e->forwardport);
+}
+
+/* Implement proc_handle_exceptions as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_handle_exceptions (struct proc *p,
+ mach_port_t msgport,
+ mach_port_t forwardport,
+ int flavor,
+ thread_state_t new_state,
+ mach_msg_type_number_t statecnt)
+{
+ struct exc *e;
+ error_t err;
+
+ /* No need to check P here; we don't use it. */
+
+ err = ports_import_port (exc_class, proc_bucket, msgport,
+ (sizeof (struct exc)
+ + (statecnt * sizeof (natural_t))), &e);
+ if (err)
+ return err;
+
+ e->forwardport = forwardport;
+ e->flavor = flavor;
+ e->statecnt = statecnt;
+ memcpy (e->thread_state, new_state, statecnt * sizeof (natural_t));
+ ports_port_deref (e);
+ return 0;
+}
+
+/* Called on exception ports provided to proc_handle_exceptions. Do
+ the thread_set_state requested by proc_handle_exceptions and then
+ send an exception_raise message as requested. */
+kern_return_t
+S_proc_exception_raise (struct exc *e,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mach_port_t thread,
+ mach_port_t task,
+ integer_t exception,
+ integer_t code,
+ integer_t subcode)
+{
+ error_t err;
+ struct proc *p;
+ if (!e || e->pi.bucket != proc_bucket || e->pi.class != exc_class)
+ return EOPNOTSUPP;
+
+ p = task_find (task);
+ if (! p)
+ {
+ /* Bogus RPC. */
+ return EINVAL;
+ }
+
+ /* Try to forward the message. */
+ err = proc_exception_raise (e->forwardport,
+ reply, reply_type, MACH_SEND_NOTIFY,
+ thread, task, exception, code, subcode);
+
+ switch (err)
+ {
+ struct hurd_signal_detail hsd;
+ int signo;
+
+ case 0:
+ /* We have successfully forwarded the exception message. Now reset
+ the faulting thread's state to run its recovery code, which should
+ dequeue that message. */
+ err = thread_set_state (thread, e->flavor, e->thread_state, e->statecnt);
+ mach_port_deallocate (mach_task_self (), thread);
+ mach_port_deallocate (mach_task_self (), task);
+ if (err)
+ return err;
+ return MIG_NO_REPLY;
+
+ default:
+ /* Some unexpected error in forwarding the message. */
+ /* FALLTHROUGH */
+
+ case MACH_SEND_NOTIFY_IN_PROGRESS:
+ /* The port's queue is full; this means the thread didn't receive
+ the exception message we forwarded last time it faulted.
+ Declare that signal thread hopeless and the task crashed. */
+
+ /* Translate the exception code into a signal number
+ and mark the process as having died that way. */
+ hsd.exc = exception;
+ hsd.exc_code = code;
+ hsd.exc_subcode = subcode;
+ _hurd_exception2signal (&hsd, &signo);
+ p->p_exiting = 1;
+ p->p_status = W_EXITCODE (0, signo);
+ p->p_sigcode = hsd.code;
+
+ /* Nuke the task; we will get a notification message and report that
+ it died with SIGNO. */
+ task_terminate (task);
+
+ /* In the MACH_SEND_NOTIFY_IN_PROGRESS case, the kernel did a
+ pseudo-receive of the RPC request message that may have added user
+ refs to these send rights. But we have lost track because the MiG
+ stub did not save the message buffer that was modified by the
+ pseudo-receive.
+
+ Fortunately, we can be sure that we don't need the THREAD send
+ right for anything since this task is now dead; there would be a
+ potential race here with another exception_raise message arriving
+ with the same thread, but we expect that this won't happen since
+ the thread will still be waiting for our reply. XXX We have no
+ secure knowledge that this is really from the kernel, so a
+ malicious user could confuse us and induce a race where we clobber
+ another port put on the THREAD name after the destroy; also, a
+ user just doing thread_set_state et al could arrange that we get a
+ second legitimate exception_raise for the same thread and have the
+ first race mentioned above!
+
+ There are all manner of race problems if we destroy the TASK
+ right. Fortunately, since we've terminated the task we know that
+ we will shortly be getting a dead-name notifiction and that will
+ call mach_port_destroy in TASK when it is safe to do so. */
+
+ mach_port_destroy (mach_task_self (), thread);
+
+ return MIG_NO_REPLY;
+ }
+
+}
+
+/* This function is used as callback in S_proc_getallpids. */
+static void
+count_up (struct proc *p, void *counter)
+{
+ ++*(int *)counter;
+}
+
+/* This function is used as callback in S_proc_getallpids. */
+static void
+store_pid (struct proc *p, void *loc)
+{
+ *(*(pid_t **)loc)++ = p->p_pid;
+}
+
+/* Implement proc_getallpids as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getallpids (struct proc *p,
+ pid_t **pids,
+ size_t *pidslen)
+{
+ int nprocs;
+ pid_t *loc;
+
+ /* No need to check P here; we don't use it. */
+
+ add_tasks (0);
+
+ nprocs = 0;
+ prociterate (count_up, &nprocs);
+
+ if (nprocs > *pidslen)
+ {
+ *pids = mmap (0, nprocs * sizeof (pid_t), PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ if (*pids == MAP_FAILED)
+ return ENOMEM;
+ }
+
+ loc = *pids;
+ prociterate (store_pid, &loc);
+
+ *pidslen = nprocs;
+ return 0;
+}
+
+/* Create a process for TASK, which is not otherwise known to us.
+ The PID/parentage/job-control fields are not yet filled in,
+ and the proc is not entered into any hash table. */
+struct proc *
+allocate_proc (task_t task)
+{
+ error_t err;
+ struct proc *p;
+
+ /* Pid 0 is us; pid 1 is init. We handle those here specially;
+ all other processes inherit from init here (though proc_child
+ will move them to their actual parent usually). */
+
+ err = ports_create_port (proc_class, proc_bucket, sizeof (struct proc), &p);
+ if (err)
+ return NULL;
+
+ memset (&p->p_pi + 1, 0, sizeof *p - sizeof p->p_pi);
+ p->p_task = task;
+ p->p_msgport = MACH_PORT_NULL;
+
+ pthread_cond_init (&p->p_wakeup, NULL);
+
+ return p;
+}
+
+/* Allocate and initialize the proc structure for init (PID 1),
+ the original parent of all other procs. */
+struct proc *
+create_startup_proc (void)
+{
+ static const uid_t zero;
+ struct proc *p;
+ const char *rootsname = "root";
+
+ p = allocate_proc (MACH_PORT_NULL);
+ assert (p);
+
+ p->p_pid = HURD_PID_STARTUP;
+
+ p->p_parent = p;
+ p->p_sib = 0;
+ p->p_prevsib = &p->p_ochild;
+ p->p_ochild = p;
+ p->p_parentset = 1;
+
+ p->p_deadmsg = 1; /* Force initial "re-"fetch of msgport. */
+
+ p->p_important = 1;
+
+ p->p_noowner = 0;
+ p->p_id = make_ids (&zero, 1);
+ assert (p->p_id);
+
+ p->p_loginleader = 1;
+ p->p_login = malloc (sizeof (struct login) + strlen (rootsname) + 1);
+ assert (p->p_login);
+
+ p->p_login->l_refcnt = 1;
+ strcpy (p->p_login->l_name, rootsname);
+
+ boot_setsid (p);
+
+ return p;
+}
+
+/* Request a dead-name notification for P's task port. */
+
+void
+proc_death_notify (struct proc *p)
+{
+ error_t err;
+ mach_port_t old;
+
+ err = mach_port_request_notification (mach_task_self (), p->p_task,
+ MACH_NOTIFY_DEAD_NAME, 1,
+ p->p_pi.port_right,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &old);
+ assert_perror (err);
+
+ if (old != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), old);
+}
+
+/* Complete a new process that has been allocated but not entirely initialized.
+ This gets called for every process except startup_proc (PID 1). */
+void
+complete_proc (struct proc *p, pid_t pid)
+{
+ /* Because these have a reference count of one before starting,
+ they can never be freed, so we're safe. */
+ static struct login *nulllogin;
+ static struct ids nullids = { i_refcnt : 1, i_nuids : 0};
+ const char nullsname [] = "<none>";
+
+ if (!nulllogin)
+ {
+ nulllogin = malloc (sizeof (struct login) + sizeof (nullsname) + 1);
+ nulllogin->l_refcnt = 1;
+ strcpy (nulllogin->l_name, nullsname);
+ }
+
+ p->p_pid = pid;
+
+ ids_ref (&nullids);
+ p->p_id = &nullids;
+
+ p->p_login = nulllogin;
+ p->p_login->l_refcnt++;
+
+ /* Our parent is init for now. */
+ p->p_parent = startup_proc;
+
+ p->p_sib = startup_proc->p_ochild;
+ p->p_prevsib = &startup_proc->p_ochild;
+ if (p->p_sib)
+ p->p_sib->p_prevsib = &p->p_sib;
+ startup_proc->p_ochild = p;
+ p->p_loginleader = 0;
+ p->p_ochild = 0;
+ p->p_parentset = 0;
+
+ p->p_noowner = 1;
+
+ p->p_pgrp = startup_proc->p_pgrp;
+
+ proc_death_notify (p);
+ add_proc_to_hash (p);
+ join_pgrp (p);
+}
+
+
+/* Create a process for TASK, which is not otherwise known to us
+ and initialize it in the usual ways. */
+static struct proc *
+new_proc (task_t task)
+{
+ struct proc *p;
+
+ p = allocate_proc (task);
+ if (p)
+ complete_proc (p, genpid ());
+ return p;
+}
+
+/* The task associated with process P has died. Drop most state,
+ and then record us as dead. Our parent will eventually complete the
+ deallocation. */
+void
+process_has_exited (struct proc *p)
+{
+ /* We have already died; this can happen since both proc_reassign
+ and dead-name notifications could result in two calls to this
+ routine for the same process. */
+ if (p->p_dead)
+ return;
+
+ p->p_waited = 0;
+ if (p->p_task != MACH_PORT_NULL)
+ alert_parent (p);
+
+ if (p->p_msgport)
+ mach_port_deallocate (mach_task_self (), p->p_msgport);
+ p->p_msgport = MACH_PORT_NULL;
+
+ prociterate ((void (*) (struct proc *, void *))check_message_dying, p);
+
+ /* Nuke external send rights and the (possible) associated reference. */
+ ports_destroy_right (p);
+
+ if (!--p->p_login->l_refcnt)
+ free (p->p_login);
+
+ ids_rele (p->p_id);
+
+ /* Reparent our children to init by attaching the head and tail
+ of our list onto init's. */
+ if (p->p_ochild)
+ {
+ struct proc *tp; /* will point to the last one. */
+ int isdead = 0;
+
+ /* first tell them their parent is changing */
+ for (tp = p->p_ochild; tp->p_sib; tp = tp->p_sib)
+ {
+ if (tp->p_msgport != MACH_PORT_NULL)
+ nowait_msg_proc_newids (tp->p_msgport, tp->p_task,
+ 1, tp->p_pgrp->pg_pgid,
+ !tp->p_pgrp->pg_orphcnt);
+ tp->p_parent = startup_proc;
+ if (tp->p_dead)
+ isdead = 1;
+ }
+ if (tp->p_msgport != MACH_PORT_NULL)
+ nowait_msg_proc_newids (tp->p_msgport, tp->p_task,
+ 1, tp->p_pgrp->pg_pgid,
+ !tp->p_pgrp->pg_orphcnt);
+ tp->p_parent = startup_proc;
+
+ /* And now append the lists. */
+ tp->p_sib = startup_proc->p_ochild;
+ if (tp->p_sib)
+ tp->p_sib->p_prevsib = &tp->p_sib;
+ startup_proc->p_ochild = p->p_ochild;
+ p->p_ochild->p_prevsib = &startup_proc->p_ochild;
+
+ if (isdead)
+ alert_parent (startup_proc);
+ }
+
+ /* If an operation is in progress for this process, cause it
+ to wakeup and return now. */
+ if (p->p_waiting || p->p_msgportwait)
+ pthread_cond_broadcast (&p->p_wakeup);
+
+ p->p_dead = 1;
+
+ /* Cancel any outstanding RPCs done on behalf of the dying process. */
+ ports_interrupt_rpcs (p);
+}
+
+void
+complete_exit (struct proc *p)
+{
+ assert (p->p_dead);
+ assert (p->p_waited);
+
+ remove_proc_from_hash (p);
+ if (p->p_task != MACH_PORT_NULL)
+ mach_port_destroy (mach_task_self (), p->p_task);
+
+ /* Remove us from our parent's list of children. */
+ if (p->p_sib)
+ p->p_sib->p_prevsib = p->p_prevsib;
+ *p->p_prevsib = p->p_sib;
+
+ leave_pgrp (p);
+
+ /* Drop the reference we created long ago in new_proc. The only
+ other references that ever show up are those for RPC args, which
+ will shortly vanish (because we are p_dead, those routines do
+ nothing). */
+ ports_port_deref (p);
+}
+
+/* Get the list of all tasks from the kernel and start adding them.
+ If we encounter TASK, then don't do any more and return its proc.
+ If TASK is null or we never find it, then return 0. */
+struct proc *
+add_tasks (task_t task)
+{
+ mach_port_t *psets;
+ size_t npsets;
+ int i;
+ struct proc *foundp = 0;
+
+ host_processor_sets (mach_host_self (), &psets, &npsets);
+ for (i = 0; i < npsets; i++)
+ {
+ mach_port_t psetpriv;
+ mach_port_t *tasks;
+ size_t ntasks;
+ int j;
+
+ if (!foundp)
+ {
+ host_processor_set_priv (_hurd_host_priv, psets[i], &psetpriv);
+ processor_set_tasks (psetpriv, &tasks, &ntasks);
+ for (j = 0; j < ntasks; j++)
+ {
+ int set = 0;
+
+ /* The kernel can deliver us an array with null slots in the
+ middle, e.g. if a task died during the call. */
+ if (! MACH_PORT_VALID (tasks[j]))
+ continue;
+
+ if (!foundp)
+ {
+ struct proc *p = task_find_nocreate (tasks[j]);
+ if (!p)
+ {
+ p = new_proc (tasks[j]);
+ set = 1;
+ }
+ if (!foundp && tasks[j] == task)
+ foundp = p;
+ }
+ if (!set)
+ mach_port_deallocate (mach_task_self (), tasks[j]);
+ }
+ munmap (tasks, ntasks * sizeof (task_t));
+ mach_port_deallocate (mach_task_self (), psetpriv);
+ }
+ mach_port_deallocate (mach_task_self (), psets[i]);
+ }
+ munmap (psets, npsets * sizeof (mach_port_t));
+ return foundp;
+}
+
+/* Allocate a new unused PID.
+ (Unused means it is neither the pid nor pgrp of any relevant data.) */
+int
+genpid ()
+{
+#define WRAP_AROUND 30000
+#define START_OVER 100
+ static int nextpid = 1;
+ static int wrap = WRAP_AROUND;
+
+ while (nextpid < wrap && !pidfree (nextpid))
+ ++nextpid;
+ if (nextpid >= wrap)
+ {
+ nextpid = START_OVER;
+ while (!pidfree (nextpid))
+ nextpid++;
+
+ while (nextpid > wrap)
+ wrap *= 2;
+ }
+
+ return nextpid++;
+}
+
+/* Implement proc_mark_important as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_mark_important (struct proc *p)
+{
+ if (!p)
+ return EOPNOTSUPP;
+
+ /* Only root may use this interface. Any children of startup_proc
+ exempt from this restriction, as startup_proc calls this on their
+ behalf. The kernel process is a notable example of an process
+ that needs this exemption. That is not an problem however, since
+ all children of /hurd/init are important and we mark them as such
+ anyway. */
+ if (! check_uid (p, 0) && ! check_owner (startup_proc, p))
+ return EPERM;
+
+ p->p_important = 1;
+ return 0;
+}
+
+/* Implement proc_is_important as described in <hurd/process.defs>. */
+error_t
+S_proc_is_important (struct proc *callerp,
+ boolean_t *essential)
+{
+ if (!callerp)
+ return EOPNOTSUPP;
+
+ *essential = callerp->p_important;
+
+ return 0;
+}
+
+/* Implement proc_set_code as described in <hurd/process.defs>. */
+error_t
+S_proc_set_code (struct proc *callerp,
+ vm_address_t start_code,
+ vm_address_t end_code)
+{
+ if (!callerp)
+ return EOPNOTSUPP;
+
+ callerp->start_code = start_code;
+ callerp->end_code = end_code;
+
+ return 0;
+}
+
+/* Implement proc_get_code as described in <hurd/process.defs>. */
+error_t
+S_proc_get_code (struct proc *callerp,
+ vm_address_t *start_code,
+ vm_address_t *end_code)
+{
+ if (!callerp)
+ return EOPNOTSUPP;
+
+ *start_code = callerp->start_code;
+ *end_code = callerp->end_code;
+
+ return 0;
+}
diff --git a/proc/mig-decls.h b/proc/mig-decls.h
new file mode 100644
index 00000000..7d36a870
--- /dev/null
+++ b/proc/mig-decls.h
@@ -0,0 +1,60 @@
+/* Translation functions for mig.
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __MIG_DECLS_H__
+#define __MIG_DECLS_H__
+
+#include "proc.h"
+
+/* Find the process corresponding to a given request port. */
+static inline struct proc * __attribute__ ((unused))
+begin_using_proc_port (mach_port_t port)
+{
+ struct proc *p;
+ p = ports_lookup_port (proc_bucket, port, proc_class);
+ if (p && p->p_dead)
+ ports_port_deref (p);
+ return (!p || p->p_dead) ? NULL : p;
+}
+
+static inline void __attribute__ ((unused))
+end_using_proc (struct proc *p)
+{
+ if (p)
+ ports_port_deref (p);
+}
+
+typedef struct exc* exc_t;
+
+static inline exc_t __attribute__ ((unused))
+begin_using_exc_port (mach_port_t port)
+{
+ return ports_lookup_port (NULL, port, exc_class);
+}
+
+static inline void __attribute__ ((unused))
+end_using_exc (exc_t exc)
+{
+ if (exc != NULL)
+ ports_port_deref (exc);
+}
+
+#endif
diff --git a/proc/mig-mutate.h b/proc/mig-mutate.h
new file mode 100644
index 00000000..ce9f88e6
--- /dev/null
+++ b/proc/mig-mutate.h
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Justus Winter.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+
+#define PROCESS_INTRAN \
+ pstruct_t begin_using_proc_port (process_t)
+#define PROCESS_DESTRUCTOR \
+ end_using_proc (pstruct_t)
+#define PROCESS_IMPORTS \
+ import "mig-decls.h";
+
+#define NOTIFY_INTRAN \
+ port_info_t begin_using_port_info_port (mach_port_t)
+#define NOTIFY_DESTRUCTOR \
+ end_using_port_info (port_info_t)
+#define NOTIFY_IMPORTS \
+ import "libports/mig-decls.h";
diff --git a/proc/msg.c b/proc/msg.c
new file mode 100644
index 00000000..796cae38
--- /dev/null
+++ b/proc/msg.c
@@ -0,0 +1,163 @@
+/* Message port manipulations
+ Copyright (C) 1994, 1995, 1996, 1999, 2001 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <mach.h>
+#include <hurd.h>
+#include "proc.h"
+#include <hurd/startup.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/* Check to see if process P is blocked trying to get the message
+ port of process AVAILP; if so, return its call. */
+void
+check_message_return (struct proc *p, void *availpaddr)
+{
+ if (p->p_msgportwait)
+ {
+ pthread_cond_broadcast (&p->p_wakeup);
+ p->p_msgportwait = 0;
+ }
+}
+
+/* Register ourselves with init. */
+static void *
+tickle_init (void *initport)
+{
+ startup_essential_task ((mach_port_t) initport, mach_task_self (),
+ MACH_PORT_NULL, "proc", _hurd_host_priv);
+ return NULL;
+}
+
+error_t
+S_proc_setmsgport (struct proc *p,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ mach_port_t msgport,
+ mach_port_t *oldmsgport,
+ mach_msg_type_name_t *oldmsgport_type)
+{
+ if (!p)
+ return EOPNOTSUPP;
+
+ *oldmsgport = p->p_msgport;
+ *oldmsgport_type = MACH_MSG_TYPE_MOVE_SEND;
+
+ p->p_msgport = msgport;
+ p->p_deadmsg = 0;
+ if (p->p_checkmsghangs)
+ prociterate (check_message_return, p);
+ p->p_checkmsghangs = 0;
+
+ if (p == startup_proc)
+ {
+ /* Init is single threaded, so we can't delay our reply for
+ the essential task RPC; spawn a thread to do it. */
+ pthread_t thread;
+ error_t err;
+ err = pthread_create (&thread, NULL, tickle_init,
+ (void*) (uintptr_t) msgport);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+ }
+
+ return 0;
+}
+
+/* Check to see if process P is blocked trying to get the message port of
+ process DYINGP; if so, wake it up. */
+void
+check_message_dying (struct proc *p, struct proc *dyingp)
+{
+ if (p->p_msgportwait)
+ {
+ pthread_cond_broadcast (&p->p_wakeup);
+ p->p_msgportwait = 0;
+ }
+}
+
+/* Check if the message port of process P has died. Return nonzero if
+ this has indeed happened. */
+int
+check_msgport_death (struct proc *p)
+{
+ /* Only check if the message port passed away, if we know that it
+ was ever alive. */
+ if (p->p_msgport != MACH_PORT_NULL)
+ {
+ mach_port_type_t type;
+ error_t err;
+
+ err = mach_port_type (mach_task_self (), p->p_msgport, &type);
+ if (err || (type & MACH_PORT_TYPE_DEAD_NAME))
+ {
+ /* The port appears to be dead; throw it away. */
+ mach_port_deallocate (mach_task_self (), p->p_msgport);
+ p->p_msgport = MACH_PORT_NULL;
+ p->p_deadmsg = 1;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+error_t
+S_proc_getmsgport (struct proc *callerp,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_port_type,
+ pid_t pid,
+ mach_port_t *msgport)
+{
+ int cancel;
+ struct proc *p;
+
+ if (!callerp)
+ return EOPNOTSUPP;
+
+ p = pid_find_allow_zombie (pid);
+
+restart:
+ while (p && p->p_deadmsg && !p->p_dead)
+ {
+ callerp->p_msgportwait = 1;
+ p->p_checkmsghangs = 1;
+ cancel = pthread_hurd_cond_wait_np (&callerp->p_wakeup, &global_lock);
+ if (callerp->p_dead)
+ return EOPNOTSUPP;
+ if (cancel)
+ return EINTR;
+
+ /* Refetch P in case it went away while we were waiting. */
+ p = pid_find_allow_zombie (pid);
+ }
+
+ if (!p)
+ return ESRCH;
+
+ if (check_msgport_death (p))
+ goto restart;
+
+ *msgport = p->p_msgport;
+
+ return 0;
+}
diff --git a/proc/notify.c b/proc/notify.c
new file mode 100644
index 00000000..b6731ae5
--- /dev/null
+++ b/proc/notify.c
@@ -0,0 +1,104 @@
+/* Handle notifications
+ Copyright (C) 1992, 1993, 1994, 1996, 1999 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include <mach.h>
+#include <sys/types.h>
+#include <hurd/hurd_types.h>
+#include <mach/notify.h>
+#include <stdio.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "proc.h"
+#include "notify_S.h"
+
+/* We ask for dead name notifications to detect when tasks and
+ message ports die. Both notifications get sent to the process
+ port. */
+kern_return_t
+do_mach_notify_dead_name (struct port_info *pi,
+ mach_port_t deadport)
+{
+ struct proc *p;
+
+ if (pi->port_right == generic_port)
+ {
+ check_dead_execdata_notify (deadport);
+ mach_port_deallocate (mach_task_self (), deadport);
+ return 0;
+ }
+
+ p = (struct proc *) pi;
+
+ if (!p
+ || p->p_pi.bucket != proc_bucket
+ || p->p_pi.class != proc_class)
+ return EOPNOTSUPP;
+
+ if (p->p_task == deadport)
+ {
+ process_has_exited (p);
+ mach_port_deallocate (mach_task_self (), deadport);
+ return 0;
+ }
+ else
+ {
+ return EINVAL;
+ }
+}
+
+/* We get no-senders notifications on exception ports that we
+ handle through proc_handle_exceptions. */
+kern_return_t
+do_mach_notify_no_senders (struct port_info *pi,
+ mach_port_mscount_t mscount)
+{
+ return ports_do_mach_notify_no_senders (pi, mscount);
+}
+
+kern_return_t
+do_mach_notify_port_deleted (struct port_info *pi,
+ mach_port_t name)
+{
+ return 0;
+}
+
+kern_return_t
+do_mach_notify_msg_accepted (struct port_info *pi,
+ mach_port_t name)
+{
+ return 0;
+}
+
+kern_return_t
+do_mach_notify_port_destroyed (struct port_info *pi,
+ mach_port_t name)
+{
+ return 0;
+}
+
+kern_return_t
+do_mach_notify_send_once (struct port_info *pi)
+{
+ return 0;
+}
diff --git a/proc/ourmsg.defs b/proc/ourmsg.defs
new file mode 100644
index 00000000..ca3fdcd6
--- /dev/null
+++ b/proc/ourmsg.defs
@@ -0,0 +1,12 @@
+/* Private specialized presentation of msg.defs for proc server. */
+
+#define routine simpleroutine
+
+/* The reason for this= is to prevent errors for get_init_port,
+ get_init_ports, get_init_int, get_init_ints, get_dtable, and get_fd.
+ We don't use those, so we're safe in breaking them. */
+#define out /* empty */
+
+#define USERPREFIX nowait_
+
+#include <hurd/msg.defs>
diff --git a/proc/pgrp.c b/proc/pgrp.c
new file mode 100644
index 00000000..a828e179
--- /dev/null
+++ b/proc/pgrp.c
@@ -0,0 +1,463 @@
+/* Session and process group manipulation
+ Copyright (C) 1992,93,94,95,96,99,2001,02,13 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#include <mach.h>
+#include <sys/types.h>
+#include <hurd/hurd_types.h>
+#include <sys/errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "proc.h"
+#include "process_S.h"
+#include "mutated_ourmsg_U.h"
+
+
+/* Create and return a new process group with pgid PGID in session SESS. */
+static inline struct pgrp *
+new_pgrp (pid_t pgid,
+ struct session *sess)
+{
+ struct pgrp *pg;
+
+ pg = malloc (sizeof (struct pgrp));
+ if (! pg)
+ return NULL;
+
+ pg->pg_plist = 0;
+ pg->pg_pgid = pgid;
+ pg->pg_orphcnt = 0;
+
+ pg->pg_session = sess;
+ pg->pg_next = sess->s_pgrps;
+ if (pg->pg_next)
+ pg->pg_next->pg_prevp = &pg->pg_next;
+ sess->s_pgrps = pg;
+ pg->pg_prevp = &sess->s_pgrps;
+
+ add_pgrp_to_hash (pg);
+ return pg;
+}
+
+/* Create and return a new session with session leader P. */
+static inline struct session *
+new_session (struct proc *p)
+{
+ struct session *sess;
+
+ sess = malloc (sizeof (struct session));
+ if (! sess)
+ return NULL;
+
+ sess->s_sid = p->p_pid;
+ sess->s_pgrps = 0;
+ sess->s_sessionid = MACH_PORT_NULL;
+
+ add_session_to_hash (sess);
+
+ return sess;
+}
+
+/* Free an empty session */
+static inline void
+free_session (struct session *s)
+{
+ if (s->s_sessionid)
+ mach_port_mod_refs (mach_task_self (), s->s_sessionid,
+ MACH_PORT_RIGHT_RECEIVE, -1);
+ remove_session_from_hash (s);
+ free (s);
+}
+
+/* Free an empty process group. */
+static inline void
+free_pgrp (struct pgrp *pg)
+{
+ *pg->pg_prevp = pg->pg_next;
+ if (pg->pg_next)
+ pg->pg_next->pg_prevp = pg->pg_prevp;
+ if (!pg->pg_session->s_pgrps)
+ free_session (pg->pg_session);
+ remove_pgrp_from_hash (pg);
+ free (pg);
+}
+
+/* Implement proc_setsid as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_setsid (struct proc *p)
+{
+ struct session *sess;
+
+ if (!p)
+ return EOPNOTSUPP;
+
+ if (p->p_pgrp->pg_pgid == p->p_pid || pgrp_find (p->p_pid))
+ return EPERM;
+
+ leave_pgrp (p);
+
+ sess = new_session (p);
+ p->p_pgrp= new_pgrp (p->p_pid, sess);
+ join_pgrp (p);
+
+ return 0;
+}
+
+/* Used in bootstrapping to set the pgrp of processes 0 and 1. */
+void
+boot_setsid (struct proc *p)
+{
+ struct session *sess;
+
+ sess = new_session (p);
+ p->p_pgrp = new_pgrp (p->p_pid, sess);
+ assert (p->p_pgrp);
+ join_pgrp (p);
+ return;
+}
+
+/* Implement proc_getsid as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getsid (struct proc *callerp,
+ pid_t pid,
+ pid_t *sid)
+{
+ struct proc *p = pid_find (pid);
+ if (!p)
+ return ESRCH;
+
+ /* No need to check CALLERP; we don't use it. */
+
+ *sid = p->p_pgrp->pg_session->s_sid;
+ return 0;
+}
+
+/* Implement proc_getsessionpids as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getsessionpids (struct proc *callerp,
+ pid_t sid,
+ pid_t **pids,
+ size_t *npidsp)
+{
+ int count;
+ struct pgrp *pg;
+ struct proc *p;
+ struct session *s;
+ pid_t *pp = *pids;
+ u_int npids = *npidsp;
+
+ /* No need to check CALLERP; we don't use it. */
+
+ s = session_find (sid);
+ if (!s)
+ return ESRCH;
+
+ count = 0;
+ for (pg = s->s_pgrps; pg; pg = pg->pg_next)
+ for (p = pg->pg_plist; p; p = p->p_gnext)
+ {
+ if (++count <= npids)
+ *pp++ = p->p_pid;
+ }
+
+ if (count > npids)
+ /* They didn't all fit */
+ {
+ *pids = mmap (0, count * sizeof (pid_t), PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ if (*pids == MAP_FAILED)
+ return errno;
+
+ pp = *pids;
+ for (pg = s->s_pgrps; pg; pg = pg->pg_next)
+ for (p = pg->pg_plist; p; p = p->p_gnext)
+ *pp++ = p->p_pid;
+ /* Set dealloc XXX */
+ }
+
+ *npidsp = count;
+ return 0;
+}
+
+/* Implement proc_getsessionpgids as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getsessionpgids (struct proc *callerp,
+ pid_t sid,
+ pid_t **pgids,
+ size_t *npgidsp)
+{
+ int count;
+ struct pgrp *pg;
+ struct session *s;
+ pid_t *pp = *pgids;
+ int npgids = *npgidsp;
+
+ /* No need to check CALLERP; we don't use it. */
+
+ s = session_find (sid);
+ if (!s)
+ return ESRCH;
+ count = 0;
+
+ for (pg = s->s_pgrps; pg; pg = pg->pg_next)
+ if (++count <= npgids)
+ *pp++ = pg->pg_pgid;
+
+ if (count > npgids)
+ /* They didn't all fit. */
+ {
+ *pgids = mmap (0, count * sizeof (pid_t), PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ if (*pgids == MAP_FAILED)
+ return errno;
+
+ pp = *pgids;
+ for (pg = s->s_pgrps; pg; pg = pg->pg_next)
+ *pp++ = pg->pg_pgid;
+ /* Dealloc ? XXX */
+ }
+ *npgidsp = count;
+ return 0;
+}
+
+/* Implement proc_getpgrppids as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getpgrppids (struct proc *callerp,
+ pid_t pgid,
+ pid_t **pids,
+ size_t *npidsp)
+{
+
+ struct proc *p;
+ struct pgrp *pg;
+ pid_t *pp = *pids;
+ unsigned int npids = *npidsp, count;
+
+ /* No need to check CALLERP; we don't use it. */
+
+ if (pgid == 0)
+ pg = callerp->p_pgrp;
+ else
+ {
+ pg = pgrp_find (pgid);
+ if (!pg)
+ return ESRCH;
+ }
+
+ count = 0;
+ for (p = pg->pg_plist; p; p = p->p_gnext)
+ if (!p->p_important && ++count <= npids)
+ *pp++ = p->p_pid;
+
+ if (count > npids)
+ /* They didn't all fit. */
+ {
+ *pids = mmap (0, count * sizeof (pid_t), PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ if (*pids == MAP_FAILED)
+ return errno;
+
+ pp = *pids;
+ for (p = pg->pg_plist; p; p = p->p_gnext)
+ if (!p->p_important)
+ *pp++ = p->p_pid;
+ /* Dealloc ? XXX */
+ }
+ *npidsp = count;
+ return 0;
+}
+
+/* Implement proc_getsidport as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getsidport (struct proc *p,
+ mach_port_t *sessport, mach_msg_type_name_t *sessport_type)
+{
+ error_t err = 0;
+
+ if (!p)
+ return EOPNOTSUPP;
+
+ if (!p->p_pgrp)
+ *sessport = MACH_PORT_NULL;
+ else
+ {
+ if (p->p_pgrp->pg_session->s_sessionid == MACH_PORT_NULL)
+ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &p->p_pgrp->pg_session->s_sessionid);
+ *sessport = p->p_pgrp->pg_session->s_sessionid;
+ }
+ *sessport_type = MACH_MSG_TYPE_MAKE_SEND;
+ return err;
+}
+
+/* Implement proc_setpgrp as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_setpgrp (struct proc *callerp,
+ pid_t pid,
+ pid_t pgid)
+{
+ struct proc *p;
+ struct pgrp *pg;
+
+ if (!callerp)
+ return EOPNOTSUPP;
+
+ p = pid ? pid_find (pid) : callerp;
+
+ if (!p || (p != callerp && p->p_parent != callerp))
+ return ESRCH;
+
+ if (p->p_parent == callerp && p->p_exec)
+ return EACCES;
+
+ if (!pgid)
+ pgid = p->p_pid;
+ pg = pgrp_find (pgid);
+
+ if (p->p_pgrp->pg_session->s_sid == p->p_pid
+ || p->p_pgrp->pg_session != callerp->p_pgrp->pg_session
+ || ((pgid != p->p_pid
+ && (!pg || pg->pg_session != callerp->p_pgrp->pg_session))))
+ return EPERM;
+
+ if (p->p_pgrp != pg)
+ {
+ /* If we have to create a new pgrp, we have to do this before
+ leaving the current one. p->p_pgrp is deallocated if p is
+ the last process in that group. Likewise, if p->p_pgrp was
+ the last group in p->p_pgrp->pg_session, the session is
+ deallocated. */
+ struct pgrp *new = pg ? pg : new_pgrp (pgid, p->p_pgrp->pg_session);
+ leave_pgrp (p);
+ p->p_pgrp = new;
+ join_pgrp (p);
+ }
+ else
+ nowait_msg_proc_newids (p->p_msgport, p->p_task, p->p_parent->p_pid,
+ pg->pg_pgid, !pg->pg_orphcnt);
+
+ return 0;
+}
+
+/* Implement proc_getpgrp as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_getpgrp (struct proc *callerp,
+ pid_t pid,
+ pid_t *pgid)
+{
+ struct proc *p = pid_find (pid);
+
+ /* No need to check CALLERP; we don't use it. */
+
+ if (!p)
+ return ESRCH;
+
+ if (p->p_pgrp)
+ *pgid = p->p_pgrp->pg_pgid;
+
+ return 0;
+}
+
+/* Implement proc_mark_exec as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_mark_exec (struct proc *p)
+{
+ if (!p)
+ return EOPNOTSUPP;
+ p->p_exec = 1;
+ return 0;
+}
+
+/* Make process P no longer a member of its process group.
+ Note that every process is always a member of some process group;
+ this must be followed by setting P->p_pgrp and then calling
+ join_pgrp. */
+void
+leave_pgrp (struct proc *p)
+{
+ struct pgrp *pg = p->p_pgrp;
+
+ *p->p_gprevp = p->p_gnext;
+ if (p->p_gnext)
+ p->p_gnext->p_gprevp = p->p_gprevp;
+
+ /* If we were the last member of our pgrp, free it */
+ if (!pg->pg_plist)
+ free_pgrp (pg);
+ else if (p->p_parent->p_pgrp != pg
+ && p->p_parent->p_pgrp->pg_session == pg->pg_session
+ && !--pg->pg_orphcnt)
+ {
+ /* We were the last process keeping this from being
+ an orphaned process group -- do the orphaning gook */
+ struct proc *ip;
+ int dosignal = 0;
+
+ for (ip = pg->pg_plist; ip; ip = ip->p_gnext)
+ {
+ if (ip->p_stopped)
+ dosignal = 1;
+ if (ip->p_msgport != MACH_PORT_NULL)
+ nowait_msg_proc_newids (ip->p_msgport, ip->p_task, ip->p_parent->p_pid,
+ ip->p_pid, 1);
+ }
+ if (dosignal)
+ for (ip = pg->pg_plist; ip; ip = ip->p_gnext)
+ {
+ send_signal (ip->p_msgport, SIGHUP, ip->p_task);
+ send_signal (ip->p_msgport, SIGCONT, ip->p_task);
+ }
+ }
+}
+
+/* Cause process P to join its process group. */
+void
+join_pgrp (struct proc *p)
+{
+ struct pgrp *pg = p->p_pgrp;
+ struct proc *tp;
+ int origorphcnt;
+
+ p->p_gnext = pg->pg_plist;
+ p->p_gprevp = &pg->pg_plist;
+ if (pg->pg_plist)
+ pg->pg_plist->p_gprevp = &p->p_gnext;
+ pg->pg_plist = p;
+
+ origorphcnt = !!pg->pg_orphcnt;
+ if (p->p_parent->p_pgrp != pg
+ && p->p_parent->p_pgrp->pg_session == pg->pg_session)
+ pg->pg_orphcnt++;
+ if (origorphcnt != !!pg->pg_orphcnt)
+ {
+ /* Tell all the processes that their status has changed */
+ for (tp = pg->pg_plist; tp; tp = tp->p_gnext)
+ if (tp->p_msgport != MACH_PORT_NULL)
+ nowait_msg_proc_newids (tp->p_msgport, tp->p_task,
+ tp->p_parent->p_pid, pg->pg_pgid,
+ !pg->pg_orphcnt);
+ }
+ else if (p->p_msgport != MACH_PORT_NULL)
+ /* Always notify process P, because its pgrp has changed. */
+ nowait_msg_proc_newids (p->p_msgport, p->p_task,
+ p->p_parent->p_pid, pg->pg_pgid, !pg->pg_orphcnt);
+}
diff --git a/proc/proc.h b/proc/proc.h
new file mode 100644
index 00000000..a2e3c537
--- /dev/null
+++ b/proc/proc.h
@@ -0,0 +1,205 @@
+/* Process server definitions
+ Copyright (C) 1992,93,94,95,96,99,2000,01 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Michael I. Bushnell. */
+
+#ifndef PROC_H_INCLUDED
+#define PROC_H_INCLUDED
+
+#include <sys/resource.h>
+#include <sys/mman.h>
+#include <hurd/ports.h>
+#include <hurd/ihash.h>
+#include <pthread.h>
+
+struct proc
+{
+ struct port_info p_pi;
+
+ /* List of members of a process group */
+ struct proc *p_gnext, **p_gprevp; /* process group */
+
+ /* Hash table pointers that point here */
+ hurd_ihash_locp_t p_pidhashloc; /* by pid */
+ hurd_ihash_locp_t p_taskhashloc; /* by task port */
+
+ /* Identification of this process */
+ task_t p_task;
+ pid_t p_pid;
+ struct login *p_login;
+ uid_t p_owner;
+ struct ids *p_id;
+
+ /* Process hierarchy */
+ /* Every process is in the process hierarchy except processes
+ 0 and 1. Processes which have not had proc_child called
+ on their behalf are parented by 1. */
+ struct proc *p_parent; /* parent process */
+ struct proc *p_ochild; /* youngest child */
+ struct proc *p_sib, **p_prevsib; /* next youngest sibling */
+
+ /* Process group structure */
+ struct pgrp *p_pgrp;
+
+ /* Communication */
+ mach_port_t p_msgport; /* send right */
+
+ pthread_cond_t p_wakeup;
+
+ /* Miscellaneous information */
+ vm_address_t p_argv, p_envp;
+ vm_address_t start_code; /* all executable segments are in this range */
+ vm_address_t end_code;
+ int p_status; /* to return via wait */
+ int p_sigcode;
+ struct rusage p_rusage; /* my usage if I'm dead, to return via wait */
+
+ struct rusage p_child_rusage; /* accumulates p_rusage of all dead children */
+
+ unsigned int p_exec:1; /* has called proc_mark_exec */
+ unsigned int p_stopped:1; /* has called proc_mark_stop */
+ unsigned int p_waited:1; /* stop has been reported to parent */
+ unsigned int p_exiting:1; /* has called proc_mark_exit */
+ unsigned int p_waiting:1; /* blocked in wait */
+ unsigned int p_traced:1; /* has called proc_mark_traced */
+ unsigned int p_nostopcld:1; /* has called proc_mark_nostopchild */
+ unsigned int p_parentset:1; /* has had a parent set with proc_child */
+ unsigned int p_deadmsg:1; /* hang on requests for a message port */
+ unsigned int p_checkmsghangs:1; /* someone is currently hanging on us */
+ unsigned int p_msgportwait:1; /* blocked in getmsgport */
+ unsigned int p_noowner:1; /* has no owner known */
+ unsigned int p_loginleader:1; /* leader of login collection */
+ unsigned int p_dead:1; /* process is dead */
+ unsigned int p_important:1; /* has called proc_mark_important */
+};
+
+typedef struct proc *pstruct_t;
+
+struct pgrp
+{
+ hurd_ihash_locp_t pg_hashloc;
+ struct proc *pg_plist; /* member processes */
+ struct pgrp *pg_next, **pg_prevp; /* list of pgrps in session */
+ pid_t pg_pgid;
+ struct session *pg_session;
+ int pg_orphcnt; /* number of non-orphaned processes */
+};
+
+struct session
+{
+ hurd_ihash_locp_t s_hashloc;
+ pid_t s_sid;
+ struct pgrp *s_pgrps; /* list of member pgrps */
+ mach_port_t s_sessionid; /* receive right */
+};
+
+struct login
+{
+ int l_refcnt;
+ char l_name[0];
+};
+
+struct ids
+{
+ int i_refcnt;
+ int i_nuids;
+ uid_t i_uids[0];
+};
+
+/* Structure for an exception port we are listening on. */
+struct exc
+{
+ struct port_info pi;
+ mach_port_t forwardport; /* Send right to forward msg to. */
+ int flavor; /* State to restore faulting thread to. */
+ mach_msg_type_number_t statecnt;
+ natural_t thread_state[0];
+};
+
+mach_port_t authserver;
+struct proc *self_proc; /* process HURD_PID_PROC (us) */
+struct proc *startup_proc; /* process 1 (init) */
+
+struct port_bucket *proc_bucket;
+struct port_class *proc_class;
+struct port_class *generic_port_class;
+struct port_class *exc_class;
+
+mach_port_t generic_port; /* messages not related to a specific proc */
+
+pthread_mutex_t global_lock;
+
+/* Forward declarations */
+void complete_wait (struct proc *, int);
+int check_uid (struct proc *, uid_t);
+int check_owner (struct proc *, struct proc *);
+void addalltasks (void);
+void prociterate (void (*)(struct proc *, void *), void *);
+void free_process (struct proc *);
+void panic (char *);
+int valid_task (task_t);
+int genpid (void);
+void abort_getmsgport (struct proc *);
+int zombie_check_pid (pid_t);
+void check_message_dying (struct proc *, struct proc *);
+int check_msgport_death (struct proc *);
+void check_dead_execdata_notify (mach_port_t);
+
+void add_proc_to_hash (struct proc *);
+void add_exc_to_hash (struct exc *);
+void remove_proc_from_hash (struct proc *);
+void add_pgrp_to_hash (struct pgrp *);
+void add_session_to_hash (struct session *);
+void remove_session_from_hash (struct session *);
+void remove_pgrp_from_hash (struct pgrp *);
+void remove_exc_from_hash (struct exc *);
+struct exc *exc_find (mach_port_t);
+struct proc *pid_find (int);
+struct proc *pid_find_allow_zombie (int);
+struct proc *task_find (task_t);
+struct proc *task_find_nocreate (task_t);
+struct pgrp *pgrp_find (int);
+struct proc *reqport_find (mach_port_t);
+struct session *session_find (pid_t);
+
+void exc_clean (void *);
+
+struct proc *add_tasks (task_t);
+int pidfree (pid_t);
+
+struct proc *create_startup_proc (void);
+struct proc *allocate_proc (task_t);
+void proc_death_notify (struct proc *);
+void complete_proc (struct proc *, pid_t);
+
+void leave_pgrp (struct proc *);
+void join_pgrp (struct proc *);
+void boot_setsid (struct proc *);
+
+void process_has_exited (struct proc *);
+void alert_parent (struct proc *);
+void reparent_zombies (struct proc *);
+void complete_exit (struct proc *);
+
+void initialize_version_info (void);
+
+void send_signal (mach_port_t, int, mach_port_t);
+
+
+#endif
diff --git a/proc/proc_exc.defs b/proc/proc_exc.defs
new file mode 100644
index 00000000..c9108246
--- /dev/null
+++ b/proc/proc_exc.defs
@@ -0,0 +1,51 @@
+/* This special version of exc.defs exists because the proc server
+ needs to do the call with special options.
+ Its RPC interface is identical to <mach/exc.defs>. */
+
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+subsystem proc_exc 2400;
+
+#include <mach/std_types.defs>
+
+ServerPrefix S_;
+import "mig-decls.h";
+
+type exception_t = mach_port_copy_send_t
+ cusertype: mach_port_t
+ intran: exc_t begin_using_exc_port (exception_t)
+ destructor: end_using_exc (exc_t);
+
+routine proc_exception_raise (
+ exception_port: exception_t;
+ replyport reply: mach_port_poly_t;
+ msgoption flags: integer_t;
+ thread: mach_port_t;
+ task: mach_port_t;
+ exception: integer_t;
+ code: integer_t;
+ subcode: integer_t);
diff --git a/proc/stubs.c b/proc/stubs.c
new file mode 100644
index 00000000..096e55ef
--- /dev/null
+++ b/proc/stubs.c
@@ -0,0 +1,178 @@
+/* By-hand stubs for some RPC calls
+ Copyright (C) 1994,96,99,2000 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <hurd/hurd_types.h>
+#include <mach/message.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "proc.h"
+
+/* From hurd/msg.defs: */
+#define RPCID_SIG_POST 23000
+
+
+struct msg_sig_post_request
+{
+ mach_msg_header_t head;
+ mach_msg_type_t signaltype;
+ int signal;
+ mach_msg_type_t sigcode_type;
+ natural_t sigcode;
+ mach_msg_type_t refporttype;
+ mach_port_t refport;
+};
+
+/* Send the Mach message indicated by msg_spec. call cthread_exit
+ when it has been delivered. */
+static void *
+blocking_message_send (void *arg)
+{
+ struct msg_sig_post_request *const req = arg;
+ error_t err;
+
+ err = mach_msg (&req->head, MACH_SEND_MSG, sizeof *req, 0,
+ MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+
+ switch (err)
+ {
+ /* These are the codes that mean a pseudo-receive modified
+ the message buffer and we might need to clean up the send rights.
+ None of them should be possible in our usage. */
+ case MACH_SEND_TIMED_OUT:
+ case MACH_SEND_INTERRUPTED:
+ case MACH_SEND_INVALID_NOTIFY:
+ case MACH_SEND_NO_NOTIFY:
+ case MACH_SEND_NOTIFY_IN_PROGRESS:
+ assert_perror (err);
+ break;
+
+ default: /* Other errors are safe to ignore. */
+ break;
+ }
+
+
+ return 0;
+}
+
+/* Send signal SIGNO to MSGPORT with REFPORT as reference. Don't
+ block in any fashion. */
+void
+send_signal (mach_port_t msgport,
+ int signal,
+ mach_port_t refport)
+{
+ error_t err;
+
+ /* This message buffer might be modified by mach_msg in some error cases,
+ so we cannot safely use a shared static buffer. */
+ struct msg_sig_post_request message =
+ {
+ {
+ /* Message header: */
+ (MACH_MSGH_BITS_COMPLEX
+ | MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE)), /* msgh_bits */
+ sizeof message, /* msgh_size */
+ msgport, /* msgh_remote_port */
+ MACH_PORT_NULL, /* msgh_local_port */
+ 0, /* msgh_seqno */
+ RPCID_SIG_POST, /* msgh_id */
+ },
+ {
+ /* Type descriptor for signo */
+ MACH_MSG_TYPE_INTEGER_32, /* msgt_name */
+ 32, /* msgt_size */
+ 1, /* msgt_number */
+ 1, /* msgt_inline */
+ 0, /* msgt_longform */
+ 0, /* msgt_deallocate */
+ 0, /* msgt_unused */
+ },
+ /* Signal number */
+ signal,
+ /* Type descriptor for sigcode */
+ {
+ MACH_MSG_TYPE_INTEGER_32, /* msgt_name */
+ 32, /* msgt_size */
+ 1, /* msgt_number */
+ 1, /* msgt_inline */
+ 0, /* msgt_longform */
+ 0, /* msgt_deallocate */
+ 0, /* msgt_unused */
+ },
+ /* Sigcode */
+ 0,
+ {
+ /* Type descriptor for refport */
+ MACH_MSG_TYPE_COPY_SEND, /* msgt_name */
+ 32, /* msgt_size */
+ 1, /* msgt_number */
+ 1, /* msgt_inline */
+ 0, /* msgt_longform */
+ 0, /* msgt_deallocate */
+ 0, /* msgt_unused */
+ },
+ refport
+ };
+
+ err = mach_msg ((mach_msg_header_t *)&message,
+ MACH_SEND_MSG|MACH_SEND_TIMEOUT, sizeof message, 0,
+ MACH_PORT_NULL, 0, MACH_PORT_NULL);
+ switch (err)
+ {
+ case MACH_SEND_TIMED_OUT:
+ /* The send could not complete immediately, and we do not want to
+ block. We'll fork off another thread to do the blocking send.
+ The message buffer was modified by a pseudo-receive operation, so
+ we need to copy its modified contents into a malloc'd buffer. */
+ {
+ struct msg_sig_post_request *copy = malloc (sizeof *copy);
+ if (copy)
+ {
+ pthread_t thread;
+ error_t err;
+ memcpy (copy, &message, sizeof message);
+ err = pthread_create (&thread, NULL, blocking_message_send, copy);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+ }
+ break;
+ }
+
+ /* These are the other codes that mean a pseudo-receive modified
+ the message buffer and we might need to clean up the send rights.
+ None of them should be possible in our usage. */
+ case MACH_SEND_INTERRUPTED:
+ case MACH_SEND_INVALID_NOTIFY:
+ case MACH_SEND_NO_NOTIFY:
+ case MACH_SEND_NOTIFY_IN_PROGRESS:
+ assert_perror (err);
+ break;
+
+ default: /* Other errors are safe to ignore. */
+ break;
+ }
+}
diff --git a/proc/wait.c b/proc/wait.c
new file mode 100644
index 00000000..824e6672
--- /dev/null
+++ b/proc/wait.c
@@ -0,0 +1,318 @@
+/* Implementation of wait
+ Copyright (C) 1994, 1995, 1996, 2001 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <mach.h>
+#include <sys/types.h>
+#include <hurd/hurd_types.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <mach/task_info.h>
+
+#include "proc.h"
+
+#include <signal.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "process_S.h"
+#include <mach/mig_errors.h>
+
+static inline void
+rusage_add (struct rusage *acc, const struct rusage *b)
+{
+ timeradd (&acc->ru_utime, &b->ru_utime, &acc->ru_utime);
+ timeradd (&acc->ru_stime, &b->ru_stime, &acc->ru_stime);
+
+ /* Check <bits/resource.h> definition of `struct rusage'
+ to make sure this gets all the fields. */
+ acc->ru_maxrss += b->ru_maxrss;
+ acc->ru_ixrss += b->ru_ixrss;
+ acc->ru_idrss += b->ru_idrss;
+ acc->ru_isrss += b->ru_isrss;
+ acc->ru_minflt += b->ru_minflt;
+ acc->ru_majflt += b->ru_majflt;
+ acc->ru_nswap += b->ru_nswap;
+ acc->ru_inblock += b->ru_inblock;
+ acc->ru_oublock += b->ru_oublock;
+ acc->ru_msgsnd += b->ru_msgsnd;
+ acc->ru_msgrcv += b->ru_msgrcv;
+ acc->ru_nsignals += b->ru_nsignals;
+ acc->ru_nvcsw += b->ru_nvcsw;
+ acc->ru_nivcsw += b->ru_nivcsw;
+}
+
+/* XXX This is real half-assed.
+
+ We want to collect usage statistics from dead processes to return to its
+ parent for its proc_wait calls and its aggregate child statistics.
+
+ The microkernel provides no access to this information once the task is
+ terminated. So the best we can do is take a sample at some time while
+ the task is still alive but not too long before it dies. Our results
+ are always inaccurate, because they don't account for the final part of
+ the task's lifetime. But perhaps it's better than nothing at all.
+
+ The obvious place to take this sample is in proc_mark_exit, which in
+ normal circumstances a task is calling immediately before terminating
+ itself. So in the best of cases, our data omits only the interval in
+ which our RPC returns to the task and it calls task_terminate. We could
+ take samples in other places just to have something rather than nothing
+ if the task dies unexpectedly (e.g. SIGKILL); but it may not be worthwhile
+ since the end result is never going to be accurate anyway.
+
+ The only way to get correct results is by adding some microkernel
+ feature to report the task statistics data post-mortem. */
+
+void
+sample_rusage (struct proc *p)
+{
+ struct task_basic_info bi;
+ struct task_events_info ei;
+ struct task_thread_times_info tti;
+ mach_msg_type_number_t count;
+ error_t err;
+
+ count = TASK_BASIC_INFO_COUNT;
+ err = task_info (p->p_task, TASK_BASIC_INFO,
+ (task_info_t) &bi, &count);
+ if (err)
+ memset (&bi, 0, sizeof bi);
+
+ count = TASK_EVENTS_INFO_COUNT;
+ err = task_info (p->p_task, TASK_EVENTS_INFO,
+ (task_info_t) &ei, &count);
+ if (err)
+ memset (&ei, 0, sizeof ei);
+
+ count = TASK_THREAD_TIMES_INFO_COUNT;
+ err = task_info (p->p_task, TASK_THREAD_TIMES_INFO,
+ (task_info_t) &tti, &count);
+ if (err)
+ memset (&tti, 0, sizeof tti);
+
+ time_value_add (&bi.user_time, &tti.user_time);
+ time_value_add (&bi.system_time, &tti.system_time);
+
+ memset (&p->p_rusage, 0, sizeof (struct rusage));
+
+ p->p_rusage.ru_utime.tv_sec = bi.user_time.seconds;
+ p->p_rusage.ru_utime.tv_usec = bi.user_time.microseconds;
+ p->p_rusage.ru_stime.tv_sec = bi.system_time.seconds;
+ p->p_rusage.ru_stime.tv_usec = bi.system_time.microseconds;
+
+ /* These statistics map only approximately. */
+ p->p_rusage.ru_majflt = ei.pageins;
+ p->p_rusage.ru_minflt = ei.faults - ei.pageins;
+ p->p_rusage.ru_msgsnd = ei.messages_sent; /* Mach IPC, not SysV IPC */
+ p->p_rusage.ru_msgrcv = ei.messages_received; /* ditto */
+}
+
+/* Return nonzero if a `waitpid' on WAIT_PID by a process
+ in MYPGRP cares about the death of PID/PGRP. */
+static inline int
+waiter_cares (pid_t wait_pid, pid_t mypgrp,
+ pid_t pid, pid_t pgrp)
+{
+ return (wait_pid == pid ||
+ wait_pid == -pgrp ||
+ wait_pid == WAIT_ANY ||
+ (wait_pid == WAIT_MYPGRP && pgrp == mypgrp));
+}
+
+/* A process is dying. Send SIGCHLD to the parent.
+ Wake the parent if it is waiting for us to exit. */
+void
+alert_parent (struct proc *p)
+{
+ /* We accumulate the aggregate usage stats of all our dead children. */
+ rusage_add (&p->p_parent->p_child_rusage, &p->p_rusage);
+
+ send_signal (p->p_parent->p_msgport, SIGCHLD, p->p_parent->p_task);
+
+ if (!p->p_exiting)
+ {
+ p->p_status = W_EXITCODE (0, SIGKILL);
+ p->p_sigcode = -1;
+ }
+
+ if (p->p_parent->p_waiting)
+ {
+ pthread_cond_broadcast (&p->p_parent->p_wakeup);
+ p->p_parent->p_waiting = 0;
+ }
+}
+
+kern_return_t
+S_proc_wait (struct proc *p,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_port_type,
+ pid_t pid,
+ int options,
+ int *status,
+ int *sigcode,
+ struct rusage *ru,
+ pid_t *pid_status)
+{
+ int cancel;
+
+ int reap (struct proc *child)
+ {
+ if (child->p_waited
+ || (!child->p_dead
+ && (!child->p_stopped
+ || !(child->p_traced || (options & WUNTRACED)))))
+ return 0;
+ child->p_waited = 1;
+ *status = child->p_status;
+ *sigcode = child->p_sigcode;
+ *ru = child->p_rusage; /* all zeros if !p_dead */
+ *pid_status = child->p_pid;
+ if (child->p_dead)
+ complete_exit (child);
+ return 1;
+ }
+
+ if (!p)
+ return EOPNOTSUPP;
+
+ start_over:
+ /* See if we can satisfy the request with a stopped
+ child; also check for invalid arguments here. */
+ if (!p->p_ochild)
+ return ECHILD;
+
+ if (pid > 0)
+ {
+ struct proc *child = pid_find_allow_zombie (pid);
+ if (!child || child->p_parent != p)
+ return ECHILD;
+ if (reap (child))
+ return 0;
+ }
+ else
+ {
+ struct proc *child;
+ int had_a_match = pid == 0;
+
+ for (child = p->p_ochild; child; child = child->p_sib)
+ if (waiter_cares (pid, p->p_pgrp->pg_pgid,
+ child->p_pid, child->p_pgrp->pg_pgid))
+ {
+ if (reap (child))
+ return 0;
+ had_a_match = 1;
+ }
+
+ if (!had_a_match)
+ return ECHILD;
+ }
+
+ if (options & WNOHANG)
+ return EWOULDBLOCK;
+
+ p->p_waiting = 1;
+ cancel = pthread_hurd_cond_wait_np (&p->p_wakeup, &global_lock);
+ if (p->p_dead)
+ return EOPNOTSUPP;
+ if (cancel)
+ return EINTR;
+ goto start_over;
+}
+
+/* Implement proc_mark_stop as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_mark_stop (struct proc *p,
+ int signo,
+ int sigcode)
+{
+ if (!p)
+ return EOPNOTSUPP;
+
+ p->p_stopped = 1;
+ p->p_status = W_STOPCODE (signo);
+ p->p_sigcode = sigcode;
+ p->p_waited = 0;
+
+ if (p->p_parent->p_waiting)
+ {
+ pthread_cond_broadcast (&p->p_parent->p_wakeup);
+ p->p_parent->p_waiting = 0;
+ }
+
+ if (!p->p_parent->p_nostopcld)
+ send_signal (p->p_parent->p_msgport, SIGCHLD, p->p_parent->p_task);
+
+ return 0;
+}
+
+/* Implement proc_mark_exit as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_mark_exit (struct proc *p,
+ int status,
+ int sigcode)
+{
+ if (!p)
+ return EOPNOTSUPP;
+
+ if (WIFSTOPPED (status))
+ return EINVAL;
+
+ sample_rusage (p); /* See comments above sample_rusage. */
+
+ if (p->p_exiting)
+ return EBUSY;
+
+ p->p_exiting = 1;
+ p->p_status = status;
+ p->p_sigcode = sigcode;
+ return 0;
+}
+
+/* Implement proc_mark_cont as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_mark_cont (struct proc *p)
+{
+ if (!p)
+ return EOPNOTSUPP;
+ p->p_stopped = 0;
+ return 0;
+}
+
+/* Implement proc_mark_traced as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_mark_traced (struct proc *p)
+{
+ if (!p)
+ return EOPNOTSUPP;
+ p->p_traced = 1;
+ return 0;
+}
+
+/* Implement proc_mark_nostopchild as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_mod_stopchild (struct proc *p,
+ int value)
+{
+ if (!p)
+ return EOPNOTSUPP;
+ /* VALUE is nonzero if we should send SIGCHLD. */
+ p->p_nostopcld = ! value;
+ return 0;
+}
diff --git a/release/=announce-0.0 b/release/=announce-0.0
new file mode 100644
index 00000000..7aa1fa9f
--- /dev/null
+++ b/release/=announce-0.0
@@ -0,0 +1,111 @@
+
+
+I am pleased to announce version 0.0 of the GNU Hurd, available via
+anonymous FTP from prep.ai.mit.edu [18.159.0.42] in the file
+/pub/gnu/hurd-0.0.tar.gz (about 1.2 MB compressed).
+
+This file contains complete source code for the following:
+
+Hurd servers:
+
+ auth, crash, devio, devport, exec, ext2fs, fifo, fwd, ifsock, init,
+ magic, new-fifo, nfs, null, pfinet, pflocal, proc, symlink, term,
+ ufs.
+
+Hurd libraries:
+
+ diskfs, fshelp, ihash, iohelp, netfs, pager, pipe, ports, ps,
+ shouldbeinlibc, store, threads, trivfs.
+
+Hurd utilities and other programs:
+
+ boot, shd, ps, settrans, showtrans, sync, su, mount, fsysopts,
+ storeinfo, login, w, uptime, hurdids, loginpr, sush, vmstat,
+ portinfo, devprobe, reboot, halt, fsck, fsck.ufs, mkfs.ufs, clri.ufs,
+ stati.ufs, getty, rc.
+
+
+------
+
+
+In addition, we have prepared a binary distribution of a complete
+version 0.0 GNU system corresponding to this Hurd release. This
+release runs only on PC-AT compatible systems with i[345]86
+processors.
+
+The GNU Hurd, plus Mach, is a kernel, not an operating system. The
+GNU operating system, like the Unix operating system, consists of many
+components, including kernel, libraries, compilers, assembler, shell,
+parser generators, utilities, window system, editors, text formatters,
+and so on. The GNU project set out a decade ago to develop this
+system, and we've been writing various components of it ever since.
+
+This release uses the `UK22' version of the Mach kernel, as
+distributed by the University of Utah. It is too difficult to prepare
+a detailed list of supported devices at this point. Common disk
+controllers and ethernet cards are generally supported.
+
+This release does not contain the X Window System.
+
+This release may be fetched by anonymous FTP from prep.ai.mit.edu
+[18.159.42] in the directory /pub/gnu/gnu-0.0/.
+
+In that directory, you should find the following files:
+
+ README
+ SOURCES
+ INSTALL-binary
+ grub-boot.image (about 1.4 MB, not compressed)
+ gnu-0.0.tar.gz (about 56.9 MB compressed)
+ gnu-0.0-stripped.tar.gz (about 26.2 MB compressed)
+
+SOURCES contains a complete list describing the sources for the
+binaries found in the image. INSTALL-binary contains complete
+installation instructions for this release.
+
+(The files README, SOURCES, and INSTALL-binary are also found in the
+root directory of the gnu-0.0 release.)
+
+gnu-0.0.tar.gz holds the image of the complete system. It unpacks
+into a directory that requires approximately 233 MB of disk space.
+
+gnu-0.0-stripped.tar.gz holds the same contents as gnu-0.0, except
+that executable programs have been stripped to save space, and the
+libraries have had debugging symbols stripped to save space and speed
+linking. It unpacks into a directory that requires about 85.5 MB of
+disk space.
+
+We recommend using the unstripped image, or you will be unable to
+debug anything. Surely there are bugs. So fetch the unstripped
+image, at least to have around.
+
+grub-boot.image is an image of a 3.5" floppy disk that you will need
+in order to complete part of the installation instructions.
+
+The following free software packages are found in this release:
+
+ autoconf, automake, bash, bc, binutils, bison, cpio, cvs, diffutils,
+ doschk, e2fsprogs, ed, emacs, fileutils, findutils, flex, from, gawk,
+ gcal, gcc, gdb, gdbm, gettext, glibc, gmp, gperf, grep, grub, gzip,
+ hello, hurd, indent, inetutils, less, mach, make, m4, miscfiles,
+ ncurses, nethack, nvi, patch, ptx, rcs, readline, recode, sed,
+ serverboot, sharutils, shellutils, tar, termcap, termutils, texinfo,
+ textutils, time, wdiff.
+
+
+------
+
+
+Here are md5sum checksums for the files mentioned in this message:
+
+b5f888bab3eb193fe97a00a141324c9d INSTALL-binary
+345dcd826747d7b11fc78f4db162d75b README
+1a5744bb4ed3448045fa6d24153d65fe SOURCES
+f7b1bc428bc4ee29977a5b28f5762092 gnu-0.0-stripped.tar.gz
+24554c58e5c89f295176e17d21dbae8e gnu-0.0.tar.gz
+8338c619d860b71bc4128c9c0fd39d63 grub-boot.image
+1fd18ccc4c81d051b83d28b13dc07ee2 hurd-0.0.tar.gz
+
+-----
+
+Br. Thomas Bushnell, n/BSG
diff --git a/release/=announce-0.1 b/release/=announce-0.1
new file mode 100644
index 00000000..b33af968
--- /dev/null
+++ b/release/=announce-0.1
@@ -0,0 +1,70 @@
+
+
+I am pleased to announce version 0.1 of the GNU Hurd, available via
+anonymous FTP from prep.ai.mit.edu [18.159.0.42] in the file
+/pub/gnu/hurd-0.1.tar.gz (about 1.2 MB compressed). There is also a
+patch file of diffs from the 0.0 release in
+/pub/gnu/hurd-0.0-0.1-diff.gz (about 75 KB compressed).
+
+(The GNU Hurd, plus Mach, is a kernel, not an operating system. The
+GNU operating system, like the Unix operating system, consists of many
+components, including kernel, libraries, compilers, assembler, shell,
+parser generators, utilities, window system, editors, text formatters,
+and so on. The GNU project set out a decade ago to develop this
+system, and we've been writing various components of it ever since.)
+
+This release contains many bug fixes from version 0.0. Many thanks to
+all the people who are helping find bugs!
+
+The best way you can help find bugs is to try and compile and use on
+the Hurd as many programs as you can find and find out where bugs
+still exist. There are also unimplemented features, and your reports
+will help us to prioritize which things we work on.
+
+Brief description of important news:
+
+ This release contains several new utilities:
+ * e2os
+ which modifies the "creator OS" field of an ext2fs filesystem
+ * nfsd
+ which implements an NFS server. It's still relatively untested,
+ so people are invited to use it for non-critical things and
+ report on successes and failures.
+ * vminfo
+ prints the virtual memory map of a process
+
+ This release contains one new shared library, `hurdbugaddr' which is
+ only used internally to various Hurd servers and utilities.
+
+ Problems in version 0.1 that prevented ext2fs from working correctly
+ have all been fixed, and the source files that were accidentally
+ missing from 0.0 have been added.
+
+For more detailed news, see the NEWS file in the distribution.
+
+There is no binary release corresponding to this source release. A
+new binary release will probably be made together with the next source
+release.
+
+This release contains complete source code for the following:
+
+Hurd servers:
+
+ auth, crash, devio, devport, exec, ext2fs, fifo, fwd, ifsock, init,
+ magic, new-fifo, nfs, null, pfinet, pflocal, proc, symlink, term,
+ ufs.
+
+Hurd libraries:
+
+ diskfs, fshelp, ihash, iohelp, netfs, pager, pipe, ports, ps,
+ shouldbeinlibc, store, threads, trivfs, hurdbugaddr.
+
+Hurd utilities and other programs:
+
+ boot, shd, ps, settrans, showtrans, sync, su, mount, fsysopts,
+ storeinfo, login, w, uptime, hurdids, loginpr, sush, vmstat,
+ portinfo, devprobe, reboot, halt, fsck, fsck.ufs, mkfs.ufs, clri.ufs,
+ stati.ufs, getty, rc, e2os, vminfo, nfsd.
+
+
+Thomas Bushnell, n/BSG
diff --git a/release/=announce-0.2 b/release/=announce-0.2
new file mode 100644
index 00000000..2d67687a
--- /dev/null
+++ b/release/=announce-0.2
@@ -0,0 +1,62 @@
+I am pleased to announce version 0.2 of the GNU Hurd, available via
+anonymous FTP from prep.ai.mit.edu [18.159.0.42] in the file
+/pub/gnu/hurd-0.2.tar.gz (about 1.37 MB compressed).
+
+(The GNU Hurd, plus Mach, is a kernel, not an operating system. The
+GNU operating system, like the Unix operating system, consists of many
+components, including kernel, libraries, compilers, assembler, shell,
+parser generators, utilities, window system, editors, text formatters,
+and so on. The GNU project set out a decade ago to develop this
+system, and we've been writing various components of it ever since.)
+
+This release contains many bug fixes from version 0.1. Many thanks to
+all the people who are helping find bugs!
+
+The best way you can help find bugs is to try and compile and use on
+the Hurd as many programs as you can find and find out where bugs
+still exist. There are also unimplemented features, and your reports
+will help us to prioritize which things we work on.
+
+The system is vastly more reliable than it has been in the past.
+
+One important addition:
+
+ New programs addauth, rmauth, unsu, su, and setauth modify the uid
+ sets of running programs. Using addauth you can add root to your
+ emacs, write a file, and then use rmauth to take the uid back. (Of
+ course, passwords are required when necessary.) New program `ids'
+ will tell you what all the user ids are that a program has. Note
+ that in the Hurd a program can have several user ids all at once,
+ just like Unix supports having several group ids. Now that you can
+ dynamically change the ids of running programs, system
+ administration (among other things) becomes much easier.
+
+For more detailed news, see the NEWS file in the distribution.
+
+This release contains complete source code for the following:
+
+Hurd servers:
+
+ auth, crash, devport, exec, ext2fs, fifo, fwd, ifsock, init,
+ magic, new-fifo, nfs, null, pfinet, pflocal, proc, symlink, term,
+ ufs, storeio, firmlink.
+
+Hurd libraries:
+
+ diskfs, fshelp, ihash, iohelp, netfs, pager, pipe, ports, ps,
+ shouldbeinlibc, store, threads, trivfs, hurdbugaddr, ftpconn
+
+Hurd utilities and other programs:
+
+ boot, shd, ps, settrans, showtrans, sync, su, mount, fsysopts,
+ storeinfo, login, w, uptime, ids, sush, vmstat, portinfo, devprobe,
+ reboot, halt, fsck, fsck.ufs, mkfs.ufs, clri.ufs, stati.ufs, getty,
+ rc, e2os, vminfo, nfsd, mail.local, serverboot, MAKEDEV, loginpr,
+ addauth, rmauth, unsu, setauth, ftpcp, ftpdir.
+
+We are also making a complete GNU 0.2 binary release, which will
+include Hurd 0.2, glibc 2.0.4, gnumach 1.1.2, and many other
+programs. This binary release is announced separately.
+
+
+Thomas Bushnell, n/BSG
diff --git a/release/COPYING.LIB b/release/COPYING.LIB
new file mode 100644
index 00000000..92b8903f
--- /dev/null
+++ b/release/COPYING.LIB
@@ -0,0 +1,481 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/release/INSTALL-binary b/release/INSTALL-binary
new file mode 100644
index 00000000..d13beb8c
--- /dev/null
+++ b/release/INSTALL-binary
@@ -0,0 +1,472 @@
+-*- Text -*-
+Instructions for bootstrapping the Hurd from binary images (i386).
+GNU Version 0.2
+
+
+This is the Hurd. Welcome.
+
+This directory contains i386 binaries for the Hurd and various
+programs built to run under it.
+
+See the file SOURCES to see where each package came from. Remember
+that you must adhere to the GNU General Public License and the GNU
+Library General Public when distributing this binary snapshot. See
+/share/misc/COPYING and /share/misc/COPYING.LIB for copies of these
+licenses.
+
+If you have noticed that these steps are rather long and a bit too
+complex, you are right. One of our upcoming tasks is to develop a
+convenient package mechanism and more streamlined installation
+procedure.
+
+Bug reports for programs in this distribution should be sent to the
+maintainer of the program. For a complete list of which mailing lists
+get bug reports for which programs, ask `gnu@prep.ai.mit.edu'.
+
+Bug reports for the GNU Hurd should be sent to the mailing list
+`bug-hurd@prep.ai.mit.edu'. Please do not send requests for
+assistance in installing or using the software to that address.
+Instead, send requests for assistance to the mailing list
+`help-hurd@prep.ai.mit.edu'. You can join these lists by sending a
+request to `bug-hurd-request@prep.ai.mit.edu' or
+`help-hurd-request@prep.ai.mit.edu' respectively.
+
+In addition, bug reports or requests for help in using the system as a
+connected whole (as opposed to for particular programs) should be sent
+to the hurd mailing lists.
+
+
+
+STEP I:
+
+Fetch the file `grub-boot.image' from the FTP site. It should be
+in the same directory as the Hurd binary image tar file. Copy this
+file onto a fresh floppy with the command:
+ dd if=grub-boot.image of=/dev/fd0 bs=512
+This is your `grub boot floppy', referred to below.
+
+Unpack the binary distribution onto a fresh disk partition, which
+needs to be BSD FFS format or Linux ext2fs format.
+
+Start with a system already running BSD:
+
+ You should ideally use a fresh disk, labelling it with the BSD
+ `disklabel' command.
+
+ Make the A partition large enough to hold the entire Hurd binary
+ image, and then some, and make a B partition for swap. Use newfs to
+ make a filesystem on the A partition, mount it, and extract the
+ entire Hurd image into it. Make sure you do this as root, and
+ remember to give tar the `p' option.
+
+Start with a system already running a GNU/Linux system:
+
+ Again, we suggest using a fresh disk. But you can also use an
+ existing partition. Make it big enough to hold the entire Hurd
+ binary image, and then some. Make sure you have a partition for
+ swap too. Extract the binary image onto the new partition using
+ tar; make sure you sue the `p' option to tar.
+
+ You need to set the "owning OS" field for this filesystem to "hurd";
+ otherwise you will not be able to use Hurd-related extensions on
+ it. (And using those extensions is required on the root
+ filesystem.) Run the command `e2os DEVICE hurd' after the
+ filesystem is unmounted in order to accomplish this.
+
+If you do not have a system running BSD or a GNU/Linux system, you can
+install using the NetBSD boot floppies. To do this, you do NOT need
+to install NetBSD; you will just be using their boot floppies
+briefly.
+
+ Unpack the entire Hurd image somewhere accessible by NFS. Then see
+ the instruction subroutine in this file labelled `Installing from
+ NetBSD boot floppies' and follow them.
+
+We plan to have our own boot floppies, which will make this all a fair
+bit easier.
+
+
+
+STEP II:
+
+You might need to change the device on which paging is done. This is
+done in a file in the Hurd partition called `boot/servers.boot', on
+the line which looks like:
+
+ /dev/sd0b $(add-paging-file) $(default-pager)
+
+`sd0b' is the paging device. Replace this with the correct device
+name (this is a mach partition name), or comment out the line if you
+don't want paging. Note that you don't have to do anything to
+initialize swap partitions, unlike Linux.
+
+If your filesystem is an ext2fs filesystem (if you are starting with a
+system already running a GNU/Linux system, for example), then you also
+should change the reference to /hurd/ufs.static into a reference to
+/hurd/ext2fs.static.
+
+(If you did STEP I using NetBSD boot floppies, then this file should
+be /mnt/boot/servers.boot.)
+
+
+
+STEP III:
+
+(If you used NetBSD boot floppies, shutdown NetBSD with the commands
+`umount /mnt' and `halt'.)
+
+Now boot the Hurd the following way. First, boot the grub boot
+floppy. When the menu comes up, select one of the single user boot
+commands, depending on whether you have a SCSI disk (sd0) or an IDE
+type disk (hd0). If you put the Hurd on something other than
+partition `a', then you will need to edit the boot commands
+appropriately before booting. (Editing the commands using grub only
+affects what you boot that time, it does not affect what the floppy
+does the next time you boot it.)
+
+Mach should load, and then the Hurd should start, saying something like:
+
+ Hurd server bootstrap: ufs exec init proc auth.
+ Single-user bootstrap: term sh.
+ bash#
+
+
+
+STEP IV:
+
+When GNU boots the first time, you might see some confusing messages,
+and you may notice that pipes don't work.
+
+In order to set up the translators for this to be a fully functional
+Hurd system, say
+
+ /bin/sh /SETUP
+
+This will set up some initial translators so that the system runs
+normally, and then offer to reboot the system. When you get to the
+GRUB menu, do step V.
+
+
+NOTE: Do NOT RUN BSD FSCK on a Hurd FFS!
+
+ (Hurd partitions, especially the root partition, may have filesystem
+ extensions that BSD does not know about. Most of these are ignored
+ by the BSD kernel and filesystem tools. One of them, `passive
+ translators' (a/k/a non-transient mounts), is not understood by the
+ BSD fsck. If you run the BSD fsck on a Hurd partition with such
+ translators, things might fail *massively*. The Hurd version of
+ fsck does not, of course, have any such problem.
+
+ (The Hurd root partition needs to have such translators installed in
+ order to work correctly. Other partitions don't; the filesystems
+ support compat options to prevent the use of Hurd filesystem
+ extensions.)
+
+
+
+STEP V:
+
+Now boot from the floppy again. This time, select the option to
+install grub from the floppy. (If your disk isn't known as hd0 to
+grub, then you will need to edit the commands accordingly.) Reset
+your PC and take the floppy disk out of the drive. The hard disk
+should now boot successfully.
+
+Select the single-user boot menu option, and proceed to STEP VI.
+
+The menu that grub provides is found in /boot/grub/menu.lst. You can
+edit that file to change what options the menu provides, as well as
+which option is selected by default. (You can change the one on the
+floppy by mounting the filesystem it contains and editing the file
+there.)
+
+
+
+STEP VI:
+
+Now you have a Hurd system. But in order to make it fully usable, do
+the following:
+
+
+*** make devices
+cd to /dev and make devices.
+
+Say `./MAKEDEV dev1 dev2 dev3 ...'.
+
+Supported devices are:
+ o any hard disk device you have; you must specify both unit number *and*
+ partition. Something like `sd0a' or `hd1f' is called for. Unit
+ number without any partition names the entire disk.
+ o floppy disk drives, give something like `fd0' or `fd1'.
+ o hardwired terminals, something like `com0' or `com1'.
+
+In general, the name of the device to use was printed by Mach when it
+booted.
+
+
+*** setup network
+
+If you want to use the network, set it up thus:
+
+ settrans /servers/socket/inet \
+ /hurd/pfinet --interface=eth0 --address=NN.NN.NN.NN \
+ --gateway=GG.GG.GG.GG --netmask=MM.MM.MM.MM
+
+where NN.NN.NN.NN is your IP address (not hostname, IP address). GG.GG.GG.GG
+is the address of an IP gateway, and MM.MM.MM.MM the netmask for the local
+subnet. If your host is isolated, then you can omit the gateway, and the
+netmask argument is optional if you don't use subnetting. The
+interface name to use was printed by Mach when it booted.
+
+Pfinet currently only supports a single active interface. Parameters may be
+changed while pfinet is running by using fsysopts, e.g.:
+
+ fsysopts /servers/socket/inet --netmask=MM.MM.MM.MM
+
+Make sure you edit /etc/resolv.conf and/or /etc/hosts for the
+nameserver to work properly.
+
+
+*** mount partitions
+
+You can mount a partition (say hd0a) by saying:
+
+ settrans /mnt /hurd/ufs /dev/hd0a
+
+The name `/dev/hd0a' must have been created using `./MAKEDEV hd0a' in
+the /dev directory.
+
+(This is equivalent to Unixy `mount /dev/hd0a /mnt'.)
+
+If it's a Linux ext2 format disk, just do
+
+ settrans /mnt /hurd/ext2fs /dev/hd0a
+
+You can make it readonly thus:
+
+ settrans /mnt /hurd/ufs -r /dev/hd0a
+
+For more information on settrans, see the end of this file.
+
+NFS mounts, not surprisingly, are done thus:
+
+ settrans /mnt /hurd/nfs /remote/file/system remote.host.org
+
+(You may also use the host:fs and fs@host notations if you prefer.)
+NFS supports many options; use `/hurd/nfs --help' to see them all.
+
+The mounts created this way are not transient--they will last across
+reboots, being recorded directly on disk. To get rid of one, say:
+`settrans /mnt' with no further args. The command `showtrans /mnt'
+will show you where it's at right now. However, note that to have them
+automatically fscked, you'll have to make entries in /etc/fstab (see
+`fscking', above).
+
+A temporary mount (which lasts only until the filesystem program is
+killed or the system is rebooted) can be accomplished by giving the -a
+option to settrans thus: `settrans -a /mnt /hurd/ufs /dev/sd0a'.
+(Note the placement of this option, as an arg to settrans, is
+different from the -r options you might give to the filesystem.)
+`showtrans' does not display these temporary mounts.
+
+
+
+
+*** edit configuration files
+
+Edit the password file (/etc/passwd) appropriately.
+
+Add any serial lines you have terminals on to /etc/ttys.
+
+Set your hostname with `echo foo.bar.baz > /etc/hostname'. This will
+then be permanent until you change the file.
+
+Create a link from /etc/localtime to the file describing your timezone
+in /share/zoneinfo. Something like the following command will do the
+trick: `ln /share/zoneinfo/US/Eastern /etc/localtime'. Look at the
+directory /share/zoneinfo to see all the various possibilities.
+
+Edit /etc/fstab according to the disk mounts you've installed; this
+will control which partitions `df' prints and which partitions get
+`fsck' run for them at boot time. It will not affect which partitions
+are mounted; use settrans for that as explained above.
+
+Edit /etc/hosts if you want to. The system works fine without it if a
+name server is available.
+
+Edit resolv.conf to provide for name service appropriate to your
+location.
+
+
+*** build a smaller kernel
+
+As an optional step, you can build a smaller kernel. The distributed
+kernel is quite large, because it includes a great many device
+drivers. If you fetch the gnumach distribution, you can build a
+kernel with only the device drivers you actually need. This will make
+bootstrapping faster, and also take less memory when the system is
+running, and result in a faster system in general.
+
+
+Once you've completed these steps, you can reboot the system multi
+user. Enjoy!
+
+
+
+
+MISCELLANEOUS NOTES:
+
+Fscking:
+
+/sbin/fsck is a wrapper that invokes filesystem-specific backend programs for
+each particular type of filesystem; these backends do the actual work (they
+can be found in the same directory, with names like /sbin/fsck.ufs and
+/sbin/fsck.ext2).
+
+/sbin/fsck will currently only work with filesystems that have entries in the
+file `/etc/fstab'; for those, it will try to be intelligent about making
+active filesystems readonly before fscking them, and telling them to
+incorporate any changes that result (the backend fsck programs do not know
+anything about active filesystems). However, it is up to the user to make
+sure that /etc/fstab accurately reflects reality.
+
+/etc/fstab is the same as in most unix systems -- any filesystems that are
+there and have a non-zero pass number will be automatically fscked during a
+multi-user boot.
+
+You'll certainly want to make an entry in /etc/fstab for the device that
+corresponds to your root filesystem (and make a device entry for it using
+MAKEDEV, as described above).
+
+
+GDB:
+
+The version of gdb included in this release has various features not used by
+most systems, in particular, the `thread' and `info thread' commands.
+
+The Hurd gdb can also debug running programs without halting them,
+which is useful to debug crucial system servers that can't be stopped
+without wedging the system. To do this, give the command `set
+noninvasive on'. Of course, gdb may get confused because data
+structures are changing underneath it, but this generally works pretty
+well. Doing a detach and then another attach usually causes gdb to
+clue in to changes it otherwise misses.
+
+The `portinfo' program is also useful for debugging problems related
+to Mach ports.
+
+
+SETTRANS:
+
+The syntax of settrans is:
+
+ settrans [settrans-option-args] file command-line
+
+All the options after the file are part of the command given to the
+filesystem. To see the args supported by ufs or ext2fs, say
+`/hurd/ufs --help' or `/hurd/ext2fs --help'.
+
+settrans itself supports several args. (Use settrans --help for a
+summary.) Once a filesystem is running, some options may be changed
+at runtime using the `fsysopts FSYS' command, where FSYS is the mount
+point (note that there is currently no easy way of finding out which
+ones).
+
+
+CRASH SERVER:
+
+When programs get fatal signals in the Hurd, they call the "crash
+server". Right now, the crash server suspends the program, and the
+rest of its process group.
+
+At that point, if you resume the program, it will exit. But you can
+also attach a debugger to it, and all it's current dynamic state will
+still be there.
+
+If you would rather have crashing programs just exit, and not suspend,
+then disable the crash server by saying `settrans /servers/crash'.
+You can always reenable it later, if you like, by typing the command
+`settrans /servers/crash /hurd/crash'.
+
+Core dumps are not yet supported.
+
+
+
+
+Subroutine: Installing from NetBSD boot floppies
+
+If you do not have a system running BSD, the NetBSD 2-floppy install
+set contains enough tools to make a new filesystem using newfs and
+copy to it from nfs. You can fetch these floppies from ftp.netbsd.org
+in the directory /pub/NetBSD/NetBSD-1.1/floppies. The NetBSD install
+script will start automatically when you boot from the floppies, and
+we suggest you use it in order to partition and set up your disk.
+
+Here are detailed instructions for this step, assuming you are using
+NetBSD boot floppies, and you have the Hurd binary snapshot unpacked
+somewhere accessible via nfs. It is assumed here that your machine's
+network address is MY-ADDR and that the nfs server's address is
+SERVER-ADDR. The nfs mountpoint on the server is presumed to be
+SERVER-DIR. (MY-ADDR and SERVER-ADDR should be IP addresses in dot
+notation, not hostnames.) Your server and your new machine need to be
+on the same network.
+
+If you installing this way, you probably want to do STEP II first,
+because your nfs server probably has more convenient editors than the
+NetBSD boot floppies. The boot floppies have only ed.
+
+A. Fetch the netbsd boot floppies from
+ ftp://ftp.netbsd.org/pub/NetBSD/NetBSD-1.1/i386/floppies. Put the
+ images onto floppy disks using the instructions found on the FTP
+ site.
+
+B. Boot the `kernel' floppy, and switch to the `filesystem' floppy
+ when instructed.
+
+C. Proceed through the NetBSD automated installation script.
+ When you are asked if you want to view the boot messages again,
+ say yes. Then answer the disk geometry questions correctly,
+ copying from what was printed at boot time.
+
+D. Tell the script that you want to use cylinders, not sectors,
+ in specifying sizes.
+
+E. Make the size of your `NetBSD' portion the total amount
+ that the script has listed, starting at cylinder 0.
+
+F. Then allocate however much disk you want to your root partition
+ and to your swap partition. The root partition must be big
+ enough to hold the entire Hurd binary snapshot; it is strongly
+ recommended that you make it a fair bit bigger than that. It is
+ quite satisfactory to use only one filesystem partition in the
+ Hurd.
+
+G. If you specify partitions beyond the root partition and swap,
+ the script will ask you for a `mount point'. Type anything you
+ like, it won't matter.
+
+H. Affirm to the over-eagerly questioning script that you really do
+ want to smash your disk. NetBSD will proceed to create
+ filesystems on all the partitions you specified in I.G-H.
+
+I. When the script says "populating ..." hit ^C. You are now done
+ with the script.
+
+J. cd to /mnt. For each mount point that was gratuitously created
+ in step I.H, say `umount NAME'. Then say `rm -rf * .*'. Make
+ sure you are really in /mnt. This will delete whatever NetBSD
+ has put on your new partition.
+
+K. Initialize the network with `ifconfig DEV MY-ADDR'. DEV was
+ printed by the kernel when it booted; type `more /kern/msgbuf'
+ if you want to see those messages again. (Sometimes ifconfig
+ says that something is "offline". Ignore it.)
+
+L. Mount the NFS server partition with the convenient command
+ `mount -o -P SERVER-ADDR:SERVER-DIR /mnt2'.
+
+M. Copy the Hurd onto your disk with the command
+ (cd /mnt2; tar cf - .) | (cd mnt; tar xfpv -)
+
+N. If you haven't done STEP II yet, then do it now; otherwise go on
+ to STEP III.
diff --git a/release/Makefile b/release/Makefile
new file mode 100644
index 00000000..a11a7df1
--- /dev/null
+++ b/release/Makefile
@@ -0,0 +1,73 @@
+# Makefile for Hurd release tools
+# Copyright (C) 1996, 1997, 2012 Free Software Foundation, Inc.
+# Written by Michael I. Bushnell, p/BSG.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+
+dir := release
+makemode := misc
+
+include ../Makeconf
+
+ifneq ($(dist-root),)
+install-dist: dist-links
+ cp $(srcdir)/INSTALL-binary $(dist-root)/
+ cp $(srcdir)/dist-README $(dist-root)/README
+ cp $(srcdir)/SETUP $(dist-root)/SETUP
+ cp $(srcdir)/SOURCES.0.2 $(dist-root)/SOURCES
+ cp $(srcdir)/servers.boot $(dist-root)/boot/servers.boot
+ cp $(srcdir)/menu.lst $(dist-root)/boot/grub/menu.lst
+ cp $(top_srcdir)/COPYING $(dist-root)/share/misc/COPYING
+ cp $(srcdir)/COPYING.LIB $(dist-root)/share/misc/COPYING.LIB
+
+dist-links:
+ mkdir -p $(dist-root)/boot/grub
+ mkdir -p $(dist-root)/servers/socket
+ mkdir -p $(dist-root)/share/misc
+ touch $(dist-root)/servers/socket/1
+ touch $(dist-root)/servers/socket/2
+ ln -f $(dist-root)/servers/socket/1 $(dist-root)/servers/socket/local
+ ln -f $(dist-root)/servers/socket/2 $(dist-root)/servers/socket/inet
+ touch $(dist-root)/servers/exec
+endif
+
+# Where to get files for the floppies from.
+bfloppy-src=$(firstword $(dist-root) /)
+
+# Boot floppies we generate
+boot-floppies = bfloppy1.ext2 bfloppy2.ext2 bfloppy.ext2 rfloppy.ext2.gz
+
+boot-floppies: $(boot-floppies)
+
+%.ext2.gz:
+ ./mkfsimage $@ --compress --owner=root.wheel \
+ $(bfloppy-src) --copy-rules=$(srcdir)/$*.copy \
+ $(srcdir) --copy-rules=$(srcdir)/$*-special.copy
+%.ext2:
+ ./mkfsimage $@ --owner=root.wheel \
+ $(bfloppy-src) --copy-rules=$(srcdir)/$*.copy \
+ $(srcdir) --copy-rules=$(srcdir)/$*-special.copy
+
+# floppy dependencies
+ifneq ($(no_deps),t)
+-include $(patsubst %,%.f_d, $(boot-floppies))
+
+%.ext2.f_d %.ext2.gz.f_d: mkfsimage
+ ./mkfsimage $(patsubst %.f_d,%,$@) --dependencies=$@ \
+ $(bfloppy-src) --copy-rules=$(srcdir)/$*.copy \
+ $(srcdir) --copy-rules=$(srcdir)/$*-special.copy
+endif
diff --git a/release/README b/release/README
new file mode 100644
index 00000000..016e0713
--- /dev/null
+++ b/release/README
@@ -0,0 +1,11 @@
+As if the editor cared that this was -*- Text -*-.
+
+This directory contains tools and scripts useful in setting up Hurd
+binary distributions. None of it is installed by default, but it's
+provided here for those who might find it helpful.
+
+Use `make install-dist dist-root=FOO' to install the files that go
+into the root of binary distributions.
+
+Use `make install-tools tool-root=FOO' to install the tools that are
+used to build binary distributions.
diff --git a/release/SETUP b/release/SETUP
new file mode 100644
index 00000000..9860f03a
--- /dev/null
+++ b/release/SETUP
@@ -0,0 +1,49 @@
+#!/bin/bash
+# Setup critical hurd translators
+
+PATH=/bin:/sbin
+
+# BOOT_DEV="$1"
+# if [ ! "$BOOT_DEV" ]; then
+# echo "No device to install a boot loader was specified."
+# echo "Here are some possible devices to use:"
+# /bin/devprobe sd0 hd0 sd1 hd1
+# echo -n "Boot device? [none] "
+# read BOOT_DEV
+# fi
+#
+# if [ "$BOOT_DEV" ]; then
+# if /bin/devprobe -s "$BOOT_DEV"; then true; else
+# echo 2>&1 "$0: $BOOT_DEV: No such device known"; exit 1
+# fi
+# fi
+
+set -v
+
+# Make sure the filesystem is writable
+fsysopts / --writable
+
+# Set up standard passive translators
+/bin/settrans -c /servers/socket/local /hurd/pflocal
+/bin/settrans -c /servers/crash /hurd/crash
+/bin/settrans -c /servers/password /hurd/password
+
+# Setup crucial devices
+cd /dev
+/bin/sh ./MAKEDEV std ptyp ptyq
+
+set +v
+
+# if test "$BOOT_DEV" && /bin/sh ./MAKEDEV "$BOOT_DEV"; then
+# echo -n "Install grub as main boot record on $BOOT_DEV? [y] "
+# read yn
+# case "$yn" in
+# "" | "[yY]*")
+# /bin/sh /INSTALL-GRUB-MBR /dev/$BOOT_DEV;;
+# esac
+# fi
+
+echo 'Hit ^C now for shell prompt or RET to reboot'
+read response
+sync
+reboot
diff --git a/release/SOURCES.0.0 b/release/SOURCES.0.0
new file mode 100644
index 00000000..673f8dd1
--- /dev/null
+++ b/release/SOURCES.0.0
@@ -0,0 +1,111 @@
+Sources for binaries in Hurd version 0.0.
+
+
+The following packages were built from the sources of the indicated
+version in ftp://prep.gnu.ai.edu/pub/gnu according to the provided
+instructions without modification:
+
+autoconf (2.10)
+automake (1.0)
+bc (1.03)
+binutils (2.7)
+bison (1.25)
+cpio (2.4.2)
+cvs (1.8.1, in cvs-1.8.tar.gz)
+diffutils (2.7)
+doschk (1.1)
+ed (0.2)
+emacs lisp manual (19-2.4)
+fileutils (3.13)
+flex (2.5.3)
+gawk (3.0.0)
+gcal (1.01)
+gcc (2.7.2)
+gdbm (1.7.3)
+gettext (0.10)
+gmp (2.0.2)
+grep (2.0)
+hello (1.3)
+hurd (0.0)
+indent (1.9.1)
+inetutils (1.1)
+less (320)
+m4 (1.4)
+miscfiles (1.0)
+nvi (1.71)
+ptx (0.4)
+readline (2.0)
+recode (3.4)
+sed (2.05)
+sharutils (4.2)
+termcap (1.3) [manual only]
+termutils (2.0)
+textutils (1.19)
+time (1.7)
+wdiff (0.5)
+
+
+
+The following packages were built from the sources of the indicated
+version in ftp://prep.gnu.ai.mit.edu/pub/gnu according to the provided
+instructions, after making the indicated minor modifications:
+
+emacs (19.31)
+ [comment out definition of tparam in src/terminfo.c.]
+ [add to src/s/gnu.h the following five lines]
+ #ifdef HAVE_LIBNCURSES
+ #define TERMINFO
+ #define LIBS_TERMCAP -lncurses
+ #endif
+ #define setpgrp(a,b) setpgrp()
+findutils (4.1)
+ [Comment out decl of basename in defs.h and defn in util.c
+ Add `#define ARG_MAX 20480' in xargs/xargs.c, right after the includes]
+gperf (cperf 2.1a)
+ [hacked src/Makefile and src/stderr.c slightly]
+gzip (1.2.4)
+ [commented out basename from gzip.h and util.c]
+ncurses (1.9.9e)
+ [In read_entry.c, change second arg of call to open from `0' to O_RDONLY]
+ [In lib_tparm.c:tparam, add the following before the call to tparam_internal]
+ if (!buffer)
+ buffer = malloc (256);
+patch (2.1)
+ [comment out basename in backupfile.c.]
+rcs (5.7)
+ [Put `#undef ED' before #define ED in conf.h.]
+ [Add `#define SYMLOOP_MAX 8' to conf.h.]
+tar (1.11.8)
+ [add `strerror' to AC_CHECK_FUNCS in configure.in]
+texinfo (3.7)
+ [info/terminal.c -- add `#define B9600 9600']
+nethack (3.2.1)
+ [3.2.0 + nethack-3.2.0-3.2.1.patch
+ define BSD & linux, frob paths in config.h & unixconf.h & root Makefile
+ comment out declaration of random in system.h
+ Use `-lncurses' for WINTTYLIB in src/Makefile
+ declare `int status' in files.c:decompress_file, and pass &status
+ as an arg to the call to `wait' in the same function. ]
+
+
+
+The following were compiled from the sources found in
+ftp://prep.gnu.ai.mit.edu/pub/gnu/gnu-0.0/hurd-special-src:
+
+bash (1.14.4 patchlevel 10, with some additional local bugfixes)
+e2fsprogs (e2fsprogs-0.5c-hurd1.tar.gz) [1.04 doesn't have hurd patches]
+serverboot (serverboot.tar.gz)
+ [ Unpack into the mach4 source directory, and rename it to be `bootstrap';
+ you won't need the existing mach4 bootstrap. Then build in the bootstrap
+ subdir of the build directory, and install as /boot/serverboot. ]
+from
+grub
+ [ Requires changes for ELF: avoid generating 16-bit relocations.
+ Requires other changes to deal with ELF; makefiles only work for
+ mach3-a.out object file format. ]
+sh-utils (1.12m from alpha.gnu.ai.mit.edu)
+ [ copy libc's time/strftime.c into lib/strftime.c to fix a date bug. ]
+make (3.74.5 from alpha.gnu.ai.mit.edu; unmodified)
+gdb (Modified from Cygnus snapshot of 960526)
+mach4 (UK22, slightly hacked) [already includes `serverboot' program.]
+libc (1.93, with modifications)
diff --git a/release/SOURCES.0.2 b/release/SOURCES.0.2
new file mode 100644
index 00000000..3fc68735
--- /dev/null
+++ b/release/SOURCES.0.2
@@ -0,0 +1,108 @@
+Sources for binaries in GNU version 0.2.
+
+Typical configure line is
+
+../../src/foo-NN/configure --prefix= --cache-file=../config.cache i486-gnu
+
+autoconf (2.12)
+automake (1.0) (useless w/out perl)
+bash (2.0)
+bc (1.04)
+binutils (2.8.1)
+bison (1.25)
+cpio (2.4.2)
+cvs (1.9)
+ [ inhibit use of libc getopt by defining _LIBC in lib/getopt.c
+ and lib/getopt1.c immediately before it is tested. ]
+diffutils (2.7)
+doschk (1.1)
+e2fsprogs (1.08)
+ed (0.2)
+emacs (19.34b)
+emacs lisp manual (19-2.4.2)
+fileutils (3.16)
+findutils (4.1)
+ [ Comment out basename in find/defs.h and find/util.c.
+ Define _GNU_SOURCE at front of find/pred.c.
+ Define ARG_MAX to be 20480 before its first use. ]
+flex (2.5.4)
+from (special source)
+g77 (0.5.19.1)
+gawk (3.0.3)
+gcal (2.10)
+gcc (2.7.2.2) (includes patches from g77 0.5.19.1)
+gdb (gdb-gnu-5 special!)
+ [ texinfo subdir fails, so finish make by doing `make all-gdb'
+ gdb installs as `i486-gnu-gdb' by default, so perhaps just copy
+ by hand ]
+gettext (0.10)
+glibc (2.0.4)
+gmp (2.0.2)
+gnuchess (4.0.pl77) [ in src/Makefile, use 'prefix=' ]
+gnumach (1.1.3)
+gnugo (1.2)
+grep (2.0)
+grub (0.4)
+gzip (1.2.4)
+ [ Comment out basename in gzip.h and util.c. ]
+hello (1.3)
+hurd (0.2)
+indent (1.9.1)
+inetutils (1.3a)
+less (332)
+libg++ (2.7.2)
+lynx (2.7)
+ [ Set defaults appropriately for GNU in userdefs.h. ]
+ [ In WWW/Library/Implementation/HTTCP.c add #ifndef __GNU__ around
+ the sys_errlist test and #ifdef __GNU__ around the strerror case
+ in HTInetStatus. ]
+
+m4 (1.4)
+make (3.75) [ use latest getloadavg ]
+miscfiles (1.1)
+ncurses (1.9.9g)
+nethack (3.2.2)
+ [ In include/config.h:
+ define COMPRESS as "/bin/gzip"
+ define COMPRESS_EXTENSION as ".gz"
+ define HACKDIR as "/games/lib/nethackdir"
+ define SCORE_ON_BOTL
+
+ In include/unixconf.h:
+ define POSIX_JOB_CONTROL and POSIX_TYPES.
+
+ In include/system.h:
+ comment out declaration of random.
+
+ In src/Makefile: Change -O to -O2 in CFLAGS and add -g.
+ Use -lncurses for WINTTYLIB
+
+ In util/Makefile: Change -O to -O2 in CFLAGS and add -g.
+ Use bison for YACC and flex for LEX.
+
+ In Makefile: Change GAMEGRP to games.
+ Change GAMEDIR to /games/lib/$(GAME)dir.
+ Change SHELLDIR to /games.
+ ]
+nvi (1.71)
+patch (2.2) [ Comment out basename in backupfile.c. ]
+perl (5.003)
+ptx (0.4)
+readline (2.0)
+rcs (5.7)
+recode (3.4)
+sed (2.05) [ Change sed.c to include <errno.h> and also use
+ strerror instead sys_errlist in read_file(). ]
+sendmail (8.8.5) [ with special! patch ]
+sh-utils (1.16) [ use latest getloadavg.c ]
+sharutils (4.2)
+tar (1.12)
+termutils (2.0)
+texinfo (3.9)
+ [ Omit OCRNL from the expression in which it occurs in
+ info/terminal.c.
+ In util/install-info.c, replace guts of my_strerror with a call to
+ strerror. ]
+textutils (1.22)
+time (1.7)
+wdiff (0.5)
diff --git a/release/bfloppy-special.copy b/release/bfloppy-special.copy
new file mode 100644
index 00000000..69ae66d7
--- /dev/null
+++ b/release/bfloppy-special.copy
@@ -0,0 +1 @@
+rename boot/servers.boot copy bfloppy.boot
diff --git a/release/bfloppy.boot b/release/bfloppy.boot
new file mode 100644
index 00000000..3766920b
--- /dev/null
+++ b/release/bfloppy.boot
@@ -0,0 +1,14 @@
+# Boot script file for booting GNU Hurd from a boot floppy. Each line
+# specifies a file to be loaded by the boot loader (the first word), and
+# actions to be done with it.
+
+# First, the bootstrap filesystem. It needs several ports as arguments,
+# as well as the user flags from the boot loader.
+/hurd/ufs.static.gz --bootflags=${boot-args} --host-priv-port=${host-port} --device-master-port=${device-port} --exec-server-task=${exec-task} -Tgunzip:device ${root-device} $(task-create) $(prompt-task-resume)
+
+# Now the exec server; to load the dynamically-linked exec server program,
+# we have the boot loader in fact load and run ld.so, which in turn
+# loads and runs /hurd/exec. This task is created, and its task port saved
+# in ${exec-task} to be passed to the fs above, but it is left suspended;
+# the fs will resume the exec task once it is ready.
+/lib/ld.so.1.gz /hurd/exec $(exec-task=task-create)
diff --git a/release/bfloppy.copy b/release/bfloppy.copy
new file mode 100644
index 00000000..eb2796fb
--- /dev/null
+++ b/release/bfloppy.copy
@@ -0,0 +1,11 @@
+mkdir boot
+mkdir hurd
+mkdir lib
+mkdir boot/grub
+gzip objcopy boot/gnumach
+gzip objcopy boot/serverboot
+gzip objcopy lib/ld.so.1
+gzip objcopy hurd/ext2fs.static
+copy boot/grub/menu.lst
+copy boot/grub/stage1
+copy boot/grub/stage2
diff --git a/release/bfloppy1-special.copy b/release/bfloppy1-special.copy
new file mode 100644
index 00000000..c92545e1
--- /dev/null
+++ b/release/bfloppy1-special.copy
@@ -0,0 +1 @@
+rename boot/grub/menu.lst copy bfloppy1.grub
diff --git a/release/bfloppy1.copy b/release/bfloppy1.copy
new file mode 100644
index 00000000..312fe70c
--- /dev/null
+++ b/release/bfloppy1.copy
@@ -0,0 +1,7 @@
+mkdir boot
+mkdir boot/grub
+gzip objcopy boot/gnumach
+gzip objcopy boot/serverboot
+copy boot/grub/stage1
+copy boot/grub/stage2
+copy boot/grub/ffs_stage1_5
diff --git a/release/bfloppy1.grub b/release/bfloppy1.grub
new file mode 100644
index 00000000..1c7cd56d
--- /dev/null
+++ b/release/bfloppy1.grub
@@ -0,0 +1,29 @@
+# This is the amount grub waits before booting the default entry
+timeout= 5
+
+# Tell which entry to boot by default. Note that this is origin zero
+# from the beginning of the file.
+default= 0
+
+# Note that to GRUB, all hard disks are `hd' and all floppy disks are `fd'.
+# To Mach, SCSI disks are `sd' and IDE type disks are `hd'. Use
+# GRUB names in the `root' command and prefixing filenames. Use a
+# Mach name as the `root' arg for the kernel, and whenever running the Hurd.
+
+
+# These two entries are for SCSI disks
+# Entry 0:
+title= fd0
+root= (fd0)
+kernel= /boot/gnumach.gz root=fd0
+module= /boot/serverboot.gz
+pause= Insert boot-floppy #2 and hit RETURN...
+
+# Installation steps for GRUB hard disk boot blocks
+# Entry 4:
+title= Install grub from floppy onto hard disk
+install= (fd0)+1 (hd0) (hd0,a)/boot/grub/stage2 0x8000 p
+
+# Entry 5:
+title= Reinstall grub from hard disk to itself
+install= (hd0)/boot/grub/stage1 (hd0) (hd0,a)/boot/grub/stage2 0x8000 p
diff --git a/release/bfloppy2-special.copy b/release/bfloppy2-special.copy
new file mode 100644
index 00000000..f5c3d761
--- /dev/null
+++ b/release/bfloppy2-special.copy
@@ -0,0 +1,2 @@
+mkdir boot
+rename boot/servers.boot copy bfloppy2.boot
diff --git a/release/bfloppy2.boot b/release/bfloppy2.boot
new file mode 100644
index 00000000..943d76d4
--- /dev/null
+++ b/release/bfloppy2.boot
@@ -0,0 +1,14 @@
+# Boot script file for booting GNU Hurd from a boot floppy. Each line
+# specifies a file to be loaded by the boot loader (the first word), and
+# actions to be done with it.
+
+# First, the bootstrap filesystem. It needs several ports as arguments,
+# as well as the user flags from the boot loader.
+/hurd/ext2fs.static --sync=2 --bootflags=${boot-args} --host-priv-port=${host-port} --device-master-port=${device-port} --exec-server-task=${exec-task} -Tgunzip:device ${root-device} $(task-create) $(prompt-task-resume)
+
+# Now the exec server; to load the dynamically-linked exec server program,
+# we have the boot loader in fact load and run ld.so, which in turn
+# loads and runs /hurd/exec. This task is created, and its task port saved
+# in ${exec-task} to be passed to the fs above, but it is left suspended;
+# the fs will resume the exec task once it is ready.
+/lib/ld.so.1 /hurd/exec $(exec-task=task-create)
diff --git a/release/bfloppy2.copy b/release/bfloppy2.copy
new file mode 100644
index 00000000..78c9f95b
--- /dev/null
+++ b/release/bfloppy2.copy
@@ -0,0 +1,4 @@
+mkdir hurd
+mkdir lib
+objcopy lib/ld.so.1
+objcopy hurd/ext2fs.static
diff --git a/release/checklist b/release/checklist
new file mode 100644
index 00000000..2e27a4b3
--- /dev/null
+++ b/release/checklist
@@ -0,0 +1,61 @@
+Order for building binary distribution:
+
+1) Make tools on build machine
+* Build/install libc and header files from mach and hurd.
+* Build/install gcc (just LANGUAGES=c)
+* Build/install binutils.
+
+2) Make everything for release; install both on build machine and dist dir.
+* Build/install binutils again (now using new as and ld).
+* Build/install gcc again. (only one stage necessary).
+* Build/install libc again.
+* Build/install everything else.
+
+See `release-steps' for Hurd source release steps.
+
+Checklist for binary image filesystems
+
+* Symlinks in /bin; esp. sh, awk, more, cc.
+* Symlink /lib/libtermcap.a -> /lib/libncurses.a, termcap_g.a,
+ curses_g.a, curses.a.
+* Symlink /lib/ld.so -> /lib/ld.so.1.
+* Symlink . -> /usr.
+* Make sure directories exist: /tmp, /var/run.
+* Touch /var/log/wtmp.
+* Check all symlinks to make sure they don't reference /gd4.
+* Don't use absolute symlinks--make them all relative.
+* Build some static fileutils: ls, ln, cp, mv, chmod.
+* Make damn sure that we are only distributing the md5 libcrypt.
+* Make sure /include/obstack.h is from libc and not binutils.
+* Make sure there is no /include/i386.
+* Make sure there is no /i486-gnu.
+* Make sure we are using Hurd versions of su and uptime, not sh-utils.
+* Make sure we are using Hurd fsck, not e2fs.
+* Make install-dist from release dir.
+* Make sure there is only a stubby resolv.conf in the distribution.
+* Make sure /etc/fstab has no active members
+* Make sure nethack is clean
+* Make sure localtime is Factory
+* Source code for Hurd and libc must be released.
+* Remove .stamp files from binary tree.
+* Chown everything root.wheel, mode 755/644.
+* /tmp is 1777.
+* Chown /games games.games.
+* Check permissions on set[gu]id files in binary tree.
+ (login, su, addauth, setauth, ids, ps, w, vmstat, vminfo, rsh, rlogin,
+ games/lib/nethack/nethack)
+* Check each directory for bogus cruft files.
+* Especially delete .bash_history, .gnunfs*, and .stamp files, *~, *.rej
+* Verify installation on bare machine.
+* Make sure everything listed in SOURCES is on prep.
+* Install release
+* Post announcement
+
+
+FSF Hurd machines need in addition to the INSTALL-binary installation:
+
+* our NFS translators
+* our kernel
+* sendmail.cf, /etc/aliases
+* /home/* symlinks
+* /etc/syslog.conf
diff --git a/release/dist-README b/release/dist-README
new file mode 100644
index 00000000..20f16275
--- /dev/null
+++ b/release/dist-README
@@ -0,0 +1,36 @@
+-*- Text -*-
+
+This is the Hurd. Welcome.
+
+This is the complete GNU system, version 0.2.
+
+This directory contains i386 binaries for the Hurd and various
+programs built to run under it.
+
+See the file SOURCES to see where each package came from. Remember
+that you must adhere to the GNU General Public License and the GNU
+Library General Public when distributing this binary snapshot. See
+/share/misc/COPYING and /share/misc/COPYING.LIB for copies of these
+licenses.
+
+For instructions on installing binary images and setting up a Hurd
+system, see the file INSTALL-binary.
+
+Bug reports for programs in this distribution should be sent to the
+maintainer of the program. For a complete list of which mailing lists
+get bug reports for which programs, ask `gnu@prep.ai.mit.edu'.
+
+Bug reports for the GNU Hurd should be sent to the mailing list
+`bug-hurd@prep.ai.mit.edu'. Please do not send requests for
+assistance in installing or using the software to that address.
+Instead, send requests for assistance to the mailing list
+`help-hurd@prep.ai.mit.edu'. You can join these lists by sending a
+request to `bug-hurd-request@prep.ai.mit.edu' or
+`help-hurd-request@prep.ai.mit.edu' respectively.
+
+In addition, bug reports or requests for help in using the system as a
+connected whole (as opposed to for particular programs) should be sent
+to the hurd mailing lists.
+
+The GNU system is free software. See the files /share/misc/COPYING
+and /share/misc/COPYING.LIB.
diff --git a/release/fstab-to-settrans b/release/fstab-to-settrans
new file mode 100755
index 00000000..e107cf10
--- /dev/null
+++ b/release/fstab-to-settrans
@@ -0,0 +1 @@
+sed -n 's;^\([^ ][^ ]*\)[ ][ ]*\([^ ][^ ]*\)[ ][ ]*nfs.*$;settrans -c \2 /hurd/nfs \1;p'
diff --git a/release/install-stripped b/release/install-stripped
new file mode 100755
index 00000000..8237c7f4
--- /dev/null
+++ b/release/install-stripped
@@ -0,0 +1,184 @@
+#! /usr/local/bin/bash
+
+# Shell script to copy files and directories, stripping things appropriately.
+# Executable programs are stripped completely, and library archives
+# have debugging symbols removed.
+
+usage()
+{
+ echo >&2 "Usage: $0 [-N NEWER-THAN-FILE] SOURCE-DIR TARGET-DIR"
+ exit 1
+}
+
+# If non-null, only copy files newer than this file when copying directories.
+newer_than=''
+
+while :; do
+ case "$1" in
+ -N) newer_than="$2"; shift 2
+ if test -r "$newer_than"; then
+ case "$newer_than" in [!/]*)
+ newer_than=`pwd`/"$newer_than"
+ esac
+ else
+ # There is no such file, so don't try to use it
+ newer_than=''
+ fi;;
+ -*) usage;;
+ *) break;;
+ esac
+done
+
+if [ $# -lt 2 ]; then
+ usage
+fi
+
+OBJCOPY=${OBJCOPY:-i386-gnu-objcopy}
+
+check_inode()
+{
+ inode=`ls -dli "$1" | awk '{ print $1 }'`
+ eval "old_inode=\${inode_${inode}}"
+ eval "inode_${inode}=$2"
+}
+
+# Prints the files in DIR, only using those newer than $newer_than if
+# it's non-null
+dir_files()
+{
+ case "$newer_than" in
+ "") test='';;
+ *) test="-newer $newer_than";;
+ esac
+
+ find $1 -maxdepth 1 \( ! -type d $test -print \) \
+ | sed "s@^$1/@@"
+}
+
+copy()
+{
+ local from=$1
+ local to=$2
+
+ copyattrs()
+ {
+ local ls="`ls -l $from`"
+ local owner="`echo "$ls" | awk '{print $3 "." $4}'`"
+ local mode="`echo "$ls" | sed -e 's/^.\(...\)\(...\)\(...\).*$/u=\1,g=\2,o=\3/' -e 's/-//g' -e 's/s/xs/g'`"
+ chown "$owner" $to
+ chmod "$mode" $to
+ }
+
+ plaincopy()
+ {
+ rm -f $to
+ echo "$from -> $to"
+ if ! ln -f $from $to 2>/dev/null; then
+ install -m 644 -c $from $to
+ copyattrs
+ fi
+ }
+
+ objcopy()
+ {
+ if $OBJCOPY --strip-debug $from $to; then
+ echo "strip-debug $from -> $to"
+ copyattrs
+ else
+ plaincopy
+ fi
+ }
+
+ copysymlink ()
+ {
+ linkto="`ls -ld $from | awk '{ print $NF }'`"
+ if test -L $to; then
+ oldlinkto="`ls -ld $to | awk '{ print $NF }'`"
+ else
+ oldlinkto=not-the-value-of-any-real-symlink
+ fi
+
+ if test $linkto != $oldlinkto; then
+ rm -f $to
+ echo "making symlink $to -> $linkto"
+ ln -s $linkto $to
+ fi
+ }
+
+ makelocalhardlink ()
+ {
+ if test -e $to; then
+ fromino=`ls -dli "$from" | awk '{ print $1 }'`
+ toino=`ls -dli "$to" | awk '{print $1 }'`
+ if test $fromino != $toino; then
+ echo "$to linked to $old_inode"
+ ln -f $old_inode $to
+ fi
+ else
+ echo "$to linked to $old_inode"
+ ln -f $old_inode $to
+ fi
+ }
+
+ check_inode $from $to
+
+# Not necessary; the other bits already do necessary deletion.
+# test -d $to || { test -e $to && rm -f $to }
+
+ if test -L $from; then
+ copysymlink
+ elif test "$old_inode"; then
+ makelocalhardlink
+ elif test -d $from; then
+ if test ! -d $to; then
+ echo making $to
+ mkdir -p $to
+ fi
+ for file in `dir_files $from` ..; do
+ test "$file" = .. && continue
+ copy $from/$file $to/$file
+ done
+ else
+ case $from in
+ *.a)
+ read p l o g size rest <<foo
+`ls -l $from`
+foo
+# I've reenabled installing large libraries; please don't change
+# this without syncing with mib first.
+# if test $size -gt 200000; then
+# echo "skipping large library $from ($size)"
+# else
+ objcopy
+# fi
+ ;;
+ *.o | *.so | *.so.*)
+ objcopy
+ ;;
+ *)
+ if test -x $from; then
+ if $OBJCOPY -S $from $to 2>/dev/null; then
+ echo "strip $from -> $to"
+ copyattrs
+ else
+ plaincopy
+ fi
+ else
+ plaincopy
+ fi
+ ;;
+ esac
+ fi
+}
+
+if [ $# -gt 2 ]; then
+ eval "todir=\${$#}"
+ test -d $todir || { echo >&2 With multiple args, last must be a directory.
+ exit 1; }
+ while [ $# -gt 1 ]; do
+ copy $1 $todir/`basename $1`
+ shift
+ done
+else
+ copy $1 $2
+fi
diff --git a/release/menu.lst b/release/menu.lst
new file mode 100644
index 00000000..cce718c2
--- /dev/null
+++ b/release/menu.lst
@@ -0,0 +1,49 @@
+# This is the amount grub waits before booting the default entry
+timeout= 30
+
+# Tell which entry to boot by default. Note that this is origin zero
+# from the beginning of the file.
+default= 0
+
+# Note that to GRUB, all hard disks are `hd' and all floppy disks are `fd'.
+# To Mach, SCSI disks are `sd' and IDE type disks are `hd'. Use
+# GRUB names in the `root' command and prefixing filenames. Use a
+# Mach name as the `root' arg for the kernel, and whenever running the Hurd.
+
+
+# These two entries are for SCSI disks
+# Entry 0:
+title= hurd (sd0a multi-user)
+root= (hd0,a)
+kernel= /boot/gnumach root=sd0a
+module= /boot/serverboot
+
+# Entry 1:
+title= hurd (sd0a single-user)
+root= (hd0,a)
+kernel= /boot/gnumach root=sd0a -s
+module= /boot/serverboot
+
+
+# These two entries are for RLL/IDE/ST-506/etc. disks
+# Entry 2:
+title= hurd (hd0a multi-user)
+root= (hd0,a)
+kernel= /boot/gnumach root=hd0a
+module= /boot/serverboot
+
+# Entry 3:
+title= hurd (hd0a single-user)
+root= (hd0,a)
+kernel= /boot/gnumach root=hd0a -s
+module= /boot/serverboot
+
+
+# Installation steps for GRUB hard disk boot blocks
+# Entry 4:
+title= Install grub from floppy onto hard disk
+install= (fd0)+1 (hd0) (hd0,a)/boot/grub/stage2 0x8000 p
+
+# Entry 5:
+title= Reinstall grub from hard disk to itself
+install= (hd0)/boot/grub/stage1 (hd0) (hd0,a)/boot/grub/stage2 0x8000 p
diff --git a/release/mkemptyso.sh b/release/mkemptyso.sh
new file mode 100755
index 00000000..0260a48e
--- /dev/null
+++ b/release/mkemptyso.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+# args: $1 -- destination .so file
+SO=$1
+GCC=${GCC-i386-gnu-gcc}
+$GCC -nostdlib -shared -fPIC -x c /dev/null -Wl,-soname=`basename $SO` -o $SO
diff --git a/release/mkfsimage.sh b/release/mkfsimage.sh
new file mode 100644
index 00000000..07b142f0
--- /dev/null
+++ b/release/mkfsimage.sh
@@ -0,0 +1,412 @@
+#!/bin/sh
+# Make a filesystem image
+#
+# Copyright (C) 1997 Free Software Foundation, Inc.
+# Written by Miles Bader <miles@gnu.ai.mit.edu>
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+#
+
+USAGE="\
+Usage: $0 [OPTION...] IMAGE-FILE SRC..."
+TRY="Try "\`"$0 --help' for more information"
+
+MAX_SIZE=1440 # size of floppy
+MIN_SIZE=500 # avoid lossage for compressed filesystems
+
+OWNER="`id -un`.`id -gn`"
+
+unset IMAGE SRCS COMPRESS SCRIPTS QUIET GEN_DEPS
+declare -a SRCS SCRIPTS
+
+NUM_SRCS=0
+NUM_SCRIPTS=0
+
+while :; do
+ case "$1" in
+ --compress) COMPRESS=yes; shift 1;;
+ --fstype=*) FSTYPE="`echo "$1" | sed 's/^--fstype=//'`"; shift 1;;
+ --fstype) FSTYPE="$2"; shift 2;;
+ --mkfs=*) MKFS="`echo "$1" | sed 's/^--mkfs=//'`"; shift 1;;
+ --mkfs) MKFS="$2"; shift 2;;
+ --fstrans=*) FSTRANS="`echo "$1" | sed 's/^--fstrans=//'`"; shift 1;;
+ --fstrans) FSTRANS="$2"; shift 2;;
+ --owner=*) OWNER="`echo "$1" | sed 's/^--owner=//'`"; shift 1;;
+ --owner) OWNER="$2"; shift 2;;
+ --max-size=*) MAX_SIZE="`echo "$1" | sed 's/^--max-size=//'`"; shift 1;;
+ --max-size) MAX_SIZE="$2"; shift 2;;
+ --quiet|-q|-s) QUIET=yes; shift 1;;
+ --copy=*|--copy-rules=*) SCRIPTS[NUM_SCRIPTS]="`echo "$1" | sed 's/^--[-a-z]*=//'`"; let NUM_SCRIPTS+=1; shift 1;;
+ --copy|--copy-rules) SCRIPTS[NUM_SCRIPTS]="$2"; let NUM_SCRIPTS+=1; shift 2;;
+ --dependencies=*) GEN_DEPS="`echo "$1" | sed 's/^--[-a-z]*=//'`"; shift 1;;
+ --dependencies) GEN_DEPS="$2"; shift 2;;
+
+ --version)
+ echo "STANDARD_HURD_VERSION_mkfsimage_"; exit 0;;
+ --help)
+ echo "$USAGE"
+ echo "Make a file-system image IMAGE-FILE from the files in SRC..."
+ echo ''
+ echo "\
+ --copy-rules=FILE Copy files in a manner described by FILE
+
+ --compress Compress the final image
+ --owner=USER[.GROUP] Make files owned by USER & GROUP (default "\`"$OWNER')
+ --max-size=KBYTES Maximum size of final image (default $MAX_SIZE)
+ --dependencies=DEPS Generate a make dependency rule into DEPS and exit
+
+ --fstype=TYPE Type of filesystem (TYPE may be "\`"ext2' or "\`"ufs')
+ --mkfs=PROGRAM Program to make an empty filesystem image
+ --fstrans=PROGRAM File system translator program
+
+ --help Display this help and exit
+ --version Output version information and exit
+
+If multiple SRCs are specified, then each occurrence of --files pertains only to
+the corresponding SRC.
+
+Each FILE named in a --copy-rules option contains lines of the form:
+
+ [gzip] [rename TARGET] COPY-OP NAME
+
+and says to copy NAME from the source tree to the destination, using the
+method specified by COPY-OP. A preceding "\`"rename TARGET"\'" says to give
+NAME a different name in the target tree, and a preceding "\`"gzip"\'" says
+to compress the result (appending .gz to the name). COPY-OP may be one of the
+following:
+
+ copy -- A plain copy, preserving symlinks
+ objcopy -- Copy using objcopy to strip any unneeded symbols
+ copytrans -- Copy a translator
+
+ touch -- Create an empty file in the destination, ignoring the source
+ mkdir -- Create an empty directory in the destination, ignoring the source
+ makedev -- Create the given device in the destination, ignoring the source
+ settrans -- Set a translator with the given arguments
+
+If both --mkfs and --fstrans are specified, no filesystem type need be given.
+If --fstype is not specified, an attempt is made to guess it based on the
+extension of IMAGE-FILE."
+ exit 0;;
+ -*)
+ echo 1>&2 "$0: $1: Unknown option"
+ echo 1>&2 "$TRY"
+ exit 64;;
+ '')
+ break;;
+ *)
+ case "${IMAGE+set}" in
+ set) SRCS[NUM_SRCS]="$1"; let 'NUM_SRCS += 1';;
+ *) IMAGE="$1";;
+ esac
+ shift
+ esac
+done
+
+case "${IMAGE+set}${SRCS[*]+set}" in
+ setset) ;;
+ *)
+ echo 1>&2 "$USAGE"
+ echo 1>&2 "$TRY"
+ exit 64;
+esac
+
+# Choose format
+if [ "${MKFS+set}" != set -o "${MKFS+set}" != set ]; then
+ if [ "${FSTYPE+set}" != set ]; then
+ case "$IMAGE" in
+ *.ext2|*.ext2.gz) FSTYPE=ext2;;
+ *.ufs|*.ext2.gz) FSTYPE=ufs;;
+ *) echo 1>&2 "$0: $IMAGE: Unknown filesystem type"; exit 1;;
+ esac
+ fi
+
+ case "$FSTYPE" in
+ ext2) MKFS="/sbin/mkfs.ext2 -ohurd"; MKFS_Q="-q"; FSTRANS=/hurd/ext2fs;;
+ ufs) MKFS="/sbin/mkfs.ufs --tracks=1 --sectors=80"; FSTRANS=/hurd/ufs;;
+ *) echo 1>&2 "$0: $IMAGE: Unknown filesystem type"; exit 1;;
+ esac
+fi
+
+case "$QUIET" in
+ yes) MKFS_Q="${MKFS_Q} >/dev/null"; ECHO=:;;
+ *) MKFS_Q=''; ECHO=echo;;
+esac
+export ECHO MKFS_Q
+
+case "$IMAGE" in *.gz) COMPRESS=yes;; esac
+
+IMAGE_TMP="${IMAGE}.new"
+IMAGE_GZIP_TMP="${IMAGE}.new.gz"
+MNT="/tmp/,mkfsimage-$$.mnt"
+ERROUT="/tmp/,mkfsimage-$$.errout"
+STAGE="/tmp/,mkfsimage-$$.stage"
+TRANS_LIST="/tmp/,mkfsimage-$$.trans"
+
+# Extra blocks that will be used by translators
+TRANS_BLOCKS=0
+
+if [ "$GEN_DEPS" ]; then
+ GEN_DEPS_TMP="$GEN_DEPS.new"
+ echo "$GEN_DEPS: ${SCRIPTS[*]}" >> "$GEN_DEPS_TMP"
+ echo "$IMAGE: \\" >> "$GEN_DEPS_TMP"
+fi
+
+trap "settrans 2>/dev/null -a $MNT; rm -rf $MNT $IMAGE_TMP $IMAGE_GZIP_TMP $ERROUT $STAGE $TRANS_LIST $GEN_DEPS_TMP" 0 1 2 3 15
+
+if [ ${#SRCS[@]} = 1 -a ${#SCRIPTS[@]} = 0 ]; then
+ # No staging directory
+ TREE="$1"
+else
+ # Multiple source trees, or selective copying -- copy using a staging directory.
+ # We record any translators in a file ($TRANS_LIST) for later, since we copy
+ # the staging dir to the final dir using tar, and it can't handle translators.
+
+ mkdir $STAGE || exit $?
+
+ SRC_NUM=0
+ while [ $SRC_NUM -lt ${#SRCS[@]} ]; do
+ SRC="${SRCS[SRC_NUM]}"
+ SCRIPT="${SCRIPTS[SRC_NUM]}"
+ let SRC_NUM+=1
+
+ if [ ! -d "$SRC" ]; then
+ echo 1>&2 "$0: $SRC: No such directory"
+ exit 24
+ fi
+ case "$SRC" in
+ /) PFX="/";;
+ *) PFX="$SRC/";;
+ esac
+
+ if [ ! "$GEN_DEPS" ]; then
+ eval $ECHO "'# Copying files from $SRC into staging directory $STAGE...'"
+ fi
+
+ if [ x"${SCRIPT}" != x ]; then
+ eval $ECHO "'# Using copy script $SCRIPT'"
+ (
+ if [ x"$SCRIPT" != x- ]; then
+ if [ ! -r "$SCRIPT" ]; then
+ echo 1>&2 "$0: $SCRIPT: No such file"
+ exit 25
+ fi
+ exec <"$SCRIPT"
+ fi
+
+ test "$GEN_DEPS" && echo " $SCRIPT \\" >> "$GEN_DEPS_TMP"
+
+ while read -a args; do
+ case $args in
+ gzip) gzip=yes; unset args[0]; args=(${args[@]});;
+ *) gzip=no;;
+ esac
+ case $args in
+ rename) dst="${args[1]}"; unset args[0] args[1]; args=(${args[@]});;
+ *) unset dst;;
+ esac
+
+ op="${args[0]}"
+ src="${args[1]}"
+
+ if echo "$src" | grep -q "[?*[]"; then
+ # For wildcards, use the most recent file matching that pattern
+ src="`(cd $SRC; ls -t1 $src | head -1)`"
+ fi
+
+ case ${dst+set} in
+ set)
+ # Destination patterns match files in the source tree (What
+ # else could we use? This may be useful for files that exist
+ # in the source, but which we want to use a different version
+ # of for this filesystem).
+ if echo "$dst" | grep -q "[?*[]"; then
+ dst="`(cd $SRC; ls -t1 $dst | head -1)`"
+ fi;;
+ *)
+ dst="$src";;
+ esac
+
+ # Pop op & src off of args
+ set -- "${args[@]}"; shift 2; unset args; args=("$@")
+
+ if [ "$GEN_DEPS" ]; then
+ case $op in
+ copy|objcopy)
+ echo " ${PFX}$src \\" >> "$GEN_DEPS_TMP";;
+ esac
+ continue
+ fi
+
+ case $op in
+ copy)
+ eval $ECHO "'cp -d ${PFX}$src $STAGE/$dst'"
+ cp -d ${PFX}$src $STAGE/$dst
+ ;;
+ objcopy)
+ eval $ECHO "'objcopy --strip-unneeded ${PFX}$src $STAGE/$dst'"
+ objcopy --strip-unneeded "${PFX}$src" "$STAGE/$dst"
+ ;;
+ symlink)
+ if echo "$args" | grep -q "[?*[]"; then
+ # symlink expansion is in the source tree, in the same
+ # directory as the file itself.
+ args="`(cd $PFX`dirname $dst`; ls -t1 $args | head -1)`"
+ fi
+ eval $ECHO "'ln -s $args $STAGE/$dst'"
+ ln -s $args $STAGE/$dst
+ ;;
+ mkdir)
+ eval $ECHO "'mkdir $STAGE/$dst'"
+ mkdir "$STAGE/$dst"
+ ;;
+ touch)
+ eval $ECHO "'touch $STAGE/$dst'"
+ touch "$STAGE/$dst"
+ ;;
+
+ makedev|settrans|copytrans)
+ # delay translators until later, as tar can't copy them.
+
+ case $op in
+ settrans|copytrans)
+ # We create the node on which translators will be put so
+ # that the owner gets set correctly; this isn't necessary for
+ # device because MAKEDEV does all the work needed, and doing so
+ # would cause problems with device names that are really
+ # categories.
+ touch "$STAGE/$dst";;
+ esac
+
+ # Accunt for space used by the translator block
+ TRANS_BLOCKS=$(($TRANS_BLOCKS + 1))
+
+ # Record the desired operation for a later pass
+ echo "$op $dst $src ${args[*]}" >> $TRANS_LIST
+ ;;
+
+ ''|'#')
+ ;;
+ *)
+ echo 1>&2 "$0: $op: Unknown operation"
+ ;;
+ esac
+
+ case $gzip in yes)
+ eval $ECHO "'gzip -v9 $STAGE/$dst'"
+ gzip -v9 "$STAGE/$dst";;
+ esac
+ done
+ ) || exit $?
+ else
+ eval $ECHO "'# Copying all files using tar'"
+ (cd $SRC; tar cf - .) | (cd $STAGE; tar -x --same-owner -p -f -)
+ fi
+ done
+ TREE="$STAGE"
+fi
+
+if [ "$GEN_DEPS" ]; then
+ echo "" >> "$GEN_DEPS_TMP" && mv "$GEN_DEPS_TMP" "$GEN_DEPS"
+ exit 0
+fi
+
+eval $ECHO "'# Changing file owners to $OWNER'"
+chown -R "$OWNER" $TREE
+
+# Size of source tree, plus 5% for overhead
+TREE_SIZE=$((($TRANS_BLOCKS + `du -s "$TREE" | sed 's/^\([0-9]*\).*/\1/'`) * 105 / 100))
+
+if [ "${COMPRESS-no}" = yes ]; then
+ # Add 10% to the filesystem size to leave some breathing room.
+ # Since unused filesystem space compresses very well, this shouldn't add
+ # much to the final size.
+ SIZE=$(($TREE_SIZE * 110 / 100))
+ test $SIZE -lt $MIN_SIZE && SIZE=$MIN_SIZE
+else
+ if [ $TREE_SIZE -gt $MAX_SIZE ]; then
+ echo 1>&2 "$0: $TREE: Too big (${TREE_SIZE}k) to fit in ${MAX_SIZE}k"
+ exit 10
+ fi
+ SIZE=$(($TREE_SIZE * 110 / 100))
+ test $SIZE -lt $MIN_SIZE && SIZE=$MIN_SIZE
+ test $SIZE -gt $MAX_SIZE && SIZE=$MAX_SIZE
+fi
+
+eval $ECHO "'# Zeroing disk image...'"
+rm -f $IMAGE_TMP
+if ! dd if=/dev/zero of=$IMAGE_TMP bs=${SIZE}k count=1 2>$ERROUT; then
+ sed -n "s@^dd:@$0@p" < $ERROUT 1>&2
+ exit 11
+fi
+
+eval $ECHO "'# Making filesystem...'"
+eval "$MKFS $MKFS_Q '$IMAGE_TMP'" || exit 12
+settrans -ac $MNT $FSTRANS $IMAGE_TMP || exit 13
+
+eval $ECHO "'# Copying $TREE into filesystem...'"
+(cd $TREE; tar cf - .) | (cd $MNT; tar -x --same-owner -p -f -)
+
+if [ -r "$TRANS_LIST" ]; then
+ # create any delayed translators
+ eval $ECHO "'# Creating translators...'"
+ cat "$TRANS_LIST" |
+ while read -a args; do
+ op="${args[0]}"
+ dst="${args[1]}"
+ src="${args[2]}"
+ set -- "${args[@]}"; shift 3; unset args; args=("$@")
+
+ case $op in
+ copytrans)
+ tr="`showtrans "${PFX}$src"`"
+ eval $ECHO "'settrans $MNT/$dst $tr'"
+ settrans "$MNT/$dst" $tr
+ ;;
+ settrans)
+ eval $ECHO "'settrans $MNT/$dst ${args[*]}'"
+ settrans "$MNT/$dst" "${args[@]}"
+ ;;
+ makedev)
+ dd="/`dirname $dst`"
+ eval $ECHO "'/sbin/MAKEDEV --devdir=$dd $MNT/$dst'"
+ /sbin/MAKEDEV --devdir=$dd "$MNT/$dst"
+ ;;
+ esac
+ done
+fi
+
+settrans -a $MNT
+
+case "$COMPRESS" in
+ yes)
+ case "$QUIET" in
+ yes)
+ gzip -9 $IMAGE_TMP
+ ;;
+ *)
+ eval $ECHO "'# Compressing disk image...'"
+ gzip -v9 $IMAGE_TMP 2>&1 | sed "s@$IMAGE_TMP\.gz@$IMAGE@g"
+ ;;
+ esac
+ mv $IMAGE_GZIP_TMP $IMAGE
+ ;;
+ *)
+ mv $IMAGE_TMP $IMAGE
+ ;;
+esac
+
+exit 0
diff --git a/release/mksmallso.sh b/release/mksmallso.sh
new file mode 100755
index 00000000..2f147bd0
--- /dev/null
+++ b/release/mksmallso.sh
@@ -0,0 +1,48 @@
+# Usage:
+# $1 : Destination merged, stripped, small shared library
+# $2 : lib*_pic.a files from which to produce the final small library
+# $3 : .so files that this library should depend on
+# ${4:$} : executables and shared libraries whos dependencies we care about
+
+while :; do
+ case "$1" in
+ -*) LDARGS="$1"; shift;;
+ *) break;;
+ esac
+done
+
+MERGED_SO="$1"; shift
+PIC_LIBS="$1"; shift
+DEPS="$1"; shift
+
+GCC=${GCC-gcc}
+LD=${LD-ld}
+OBJDUMP=${OBJDUMP-objdump}
+OBJCOPY=${OBJCOPY-objcopy}
+
+DEP_FLAGS_FILE=/tmp/,depflags.$$
+NEED_DSYMS_FILE=/tmp/,need.dyn.syms.$$
+HAVE_DSYMS_FILE=/tmp/,have.dyn.syms.$$
+MERGED_PIC_LIB=/tmp/,libmerged_pic.a.$$
+
+#trap "rm -f $DEP_FLAGS_FILE $MERGED_PIC_LIB $NEED_DSYMS_FILE $HAVE_DSYMS_FILE" 0
+
+
+$OBJDUMP --dynamic-syms "$@" 2>/dev/null \
+ | sed -n 's/^.*\*UND\*.* \([^ ]*\)$/\1/p' \
+ | sort -u > $NEED_DSYMS_FILE
+
+# 00000000 w F .text 00000000 syscall_device_write_request
+# 00000000 g F .text 0000056c __strtoq_internal
+$OBJDUMP --syms $PIC_LIBS 2>/dev/null \
+ | sed -n 's/^........ \(g \| w\) .. .* [0-9a-f]....... \([^ ]*\)$/\2/p' \
+ | sort -u > $HAVE_DSYMS_FILE
+
+# This had better be gnu diff...
+diff --unchanged-l='%L' --old-l= --new-l= $NEED_DSYMS_FILE $HAVE_DSYMS_FILE \
+ | sed 's/^/-u/' > $DEP_FLAGS_FILE
+
+$GCC $LDARGS -nostdlib -nostartfiles -shared -Wl,-soname=`basename $MERGED_SO` `cat $DEP_FLAGS_FILE` \
+ -o $MERGED_SO.uns $PIC_LIBS $DEPS \
+&& $OBJCOPY --strip-debug $MERGED_SO.uns $MERGED_SO \
+&& rm -f $MERGED_SO.uns
diff --git a/release/release-steps b/release/release-steps
new file mode 100644
index 00000000..5303017b
--- /dev/null
+++ b/release/release-steps
@@ -0,0 +1,8 @@
+
+Make tar file.
+Test compilation.
+Update README, including version number.
+Update INSTALL, including version number.
+Update NEWS.
+Write release announcement.
+README and release announcement should mention list of platforms.
diff --git a/release/rfloppy-special.copy b/release/rfloppy-special.copy
new file mode 100644
index 00000000..cf7ef22a
--- /dev/null
+++ b/release/rfloppy-special.copy
@@ -0,0 +1,3 @@
+rename etc/passwd copy rfloppy.passwd
+rename etc/group copy rfloppy.group
+rename etc/nsswitch.conf copy rfloppy.nss
diff --git a/release/rfloppy.copy b/release/rfloppy.copy
new file mode 100644
index 00000000..55beef92
--- /dev/null
+++ b/release/rfloppy.copy
@@ -0,0 +1,175 @@
+# Files on the hurd boot floppy root disk
+
+mkdir bin
+mkdir boot
+mkdir dev
+mkdir etc
+mkdir hurd
+mkdir lib
+mkdir sbin
+mkdir servers
+mkdir tmp
+mkdir mnt
+
+copy etc/protocols
+copy etc/services
+
+objcopy bin/bash
+objcopy bin/cat
+objcopy bin/chmod
+objcopy bin/chown
+objcopy bin/cp
+objcopy bin/dd
+objcopy bin/ed
+objcopy bin/devprobe
+objcopy bin/fsysopts
+objcopy bin/ftp
+objcopy bin/gzip
+objcopy bin/ln
+objcopy bin/ls
+objcopy bin/mv
+objcopy bin/rm
+objcopy bin/settrans
+objcopy bin/showtrans
+objcopy bin/sync
+copy bin/sh
+objcopy bin/tar
+objcopy bin/vmstat
+
+objcopy hurd/auth
+objcopy hurd/exec
+objcopy hurd/init
+objcopy hurd/nfs
+objcopy hurd/pfinet
+objcopy hurd/proc
+objcopy hurd/term
+objcopy hurd/ext2fs
+objcopy hurd/magic
+objcopy hurd/null
+objcopy hurd/pflocal
+objcopy hurd/storeio
+objcopy hurd/ufs
+
+copy sbin/MAKEDEV
+objcopy sbin/halt
+objcopy sbin/mkfs.ext2
+objcopy sbin/mkfs.ufs
+objcopy sbin/reboot
+objcopy sbin/fdisk
+objcopy sbin/swapon
+
+touch servers/exec
+mkdir servers/socket
+settrans servers/socket/1 /hurd/pflocal
+symlink servers/socket/local servers/socket/1
+touch servers/socket/2
+symlink servers/socket/inet servers/socket/2
+
+copy lib/ld.so
+objcopy lib/ld.so.1
+objcopy lib/libc-*.so
+copy lib/libc.so
+copy lib/libc.so.*
+copy lib/libcrypt.so
+copy lib/libcrypt.so.*
+objcopy lib/libcrypt-*.so
+copy lib/libdb.so
+copy lib/libdb.so.*
+objcopy lib/libdb-*.so
+copy lib/libdl.so
+copy lib/libdl.so.*
+objcopy lib/libdl-*.so
+objcopy lib/libdiskfs.so
+objcopy lib/libfshelp.so
+objcopy lib/libhurdbugaddr.so
+copy lib/libhurduser.so
+copy lib/libhurduser.so.*
+objcopy lib/libhurduser-*.so
+objcopy lib/libihash.so
+objcopy lib/libiohelp.so
+copy lib/libm.so
+copy lib/libm.so.*
+objcopy lib/libm-*.so
+copy lib/libmachuser.so
+copy lib/libmachuser.so.*
+objcopy lib/libmachuser-*.so
+objcopy lib/libnetfs.so
+copy lib/libnss_dns.so
+copy lib/libnss_dns.so.*
+objcopy lib/libnss_dns-*.so
+copy lib/libnss_files.so
+copy lib/libnss_files.so.*
+objcopy lib/libnss_files-*.so
+objcopy lib/libpager.so
+objcopy lib/libpipe.so
+objcopy lib/libports.so
+copy lib/libresolv.so
+copy lib/libresolv.so.*
+objcopy lib/libresolv-*.so
+objcopy lib/libshouldbeinlibc.so
+objcopy lib/libstore.so
+objcopy lib/libthreads.so
+objcopy lib/libtrivfs.so
+copy lib/libutil.so
+copy lib/libutil.so.*
+objcopy lib/libutil-*.so
+
+copy dev/MAKEDEV
+makedev dev/std
+makedev dev/fd0
+makedev dev/hd0
+makedev dev/hd0a
+makedev dev/hd0b
+makedev dev/hd0c
+makedev dev/hd0d
+makedev dev/hd0e
+makedev dev/hd0f
+makedev dev/hd0g
+makedev dev/hd0s1
+makedev dev/hd0s2
+makedev dev/hd0s3
+makedev dev/hd0s4
+makedev dev/hd0s5
+makedev dev/hd0s6
+makedev dev/hd1
+makedev dev/hd1a
+makedev dev/hd1b
+makedev dev/hd1c
+makedev dev/hd1d
+makedev dev/hd1e
+makedev dev/hd1f
+makedev dev/hd1g
+makedev dev/hd1s1
+makedev dev/hd1s2
+makedev dev/hd1s3
+makedev dev/hd1s4
+makedev dev/hd1s5
+makedev dev/hd1s6
+makedev dev/sd0
+makedev dev/sd0a
+makedev dev/sd0b
+makedev dev/sd0c
+makedev dev/sd0d
+makedev dev/sd0e
+makedev dev/sd0f
+makedev dev/sd0g
+makedev dev/sd0s1
+makedev dev/sd0s2
+makedev dev/sd0s3
+makedev dev/sd0s4
+makedev dev/sd0s5
+makedev dev/sd0s6
+makedev dev/sd1
+makedev dev/sd1a
+makedev dev/sd1b
+makedev dev/sd1c
+makedev dev/sd1d
+makedev dev/sd1e
+makedev dev/sd1f
+makedev dev/sd1g
+makedev dev/sd1s1
+makedev dev/sd1s2
+makedev dev/sd1s3
+makedev dev/sd1s4
+makedev dev/sd1s5
+makedev dev/sd1s6
diff --git a/release/rfloppy.group b/release/rfloppy.group
new file mode 100644
index 00000000..47b7278c
--- /dev/null
+++ b/release/rfloppy.group
@@ -0,0 +1 @@
+wheel::0:root
diff --git a/release/rfloppy.nss b/release/rfloppy.nss
new file mode 100644
index 00000000..31447c2a
--- /dev/null
+++ b/release/rfloppy.nss
@@ -0,0 +1,19 @@
+# /etc/nsswitch.conf
+#
+# Don't use name services that we can't provide (specifically, nis & db)
+#
+
+# defaults for hosts & networks are ok
+
+passwd: files
+group: files
+shadow: files
+aliases: files
+
+protocols: files
+services: files
+ethers: files
+rpc: files
+publickey: files
+
+netgroup: files
diff --git a/release/rfloppy.passwd b/release/rfloppy.passwd
new file mode 100644
index 00000000..a799e8f6
--- /dev/null
+++ b/release/rfloppy.passwd
@@ -0,0 +1 @@
+root::0:0:Lord of the Files:/:/bin/bash
diff --git a/release/servers.boot b/release/servers.boot
new file mode 100644
index 00000000..9ba7f6a4
--- /dev/null
+++ b/release/servers.boot
@@ -0,0 +1,14 @@
+# Boot script file for booting GNU Hurd. Each line specifies a file to be
+# loaded by the boot loader (the first word), and actions to be done with it.
+
+# First, the bootstrap filesystem. It needs several ports as arguments,
+# as well as the user flags from the boot loader.
+/hurd/ext2fs.static --multiboot-command-line=${kernel-command-line} --host-priv-port=${host-port} --device-master-port=${device-port} --exec-server-task=${exec-task} -T typed ${root} $(task-create) $(task-resume)
+
+
+# Now the exec server; to load the dynamically-linked exec server program,
+# we have the boot loader in fact load and run ld.so, which in turn
+# loads and runs /hurd/exec. This task is created, and its task port saved
+# in ${exec-task} to be passed to the fs above, but it is left suspended;
+# the fs will resume the exec task once it is ready.
+/lib/ld.so.1 /hurd/exec $(exec-task=task-create)
diff --git a/release/tool-Makefile b/release/tool-Makefile
new file mode 100644
index 00000000..6a23b95a
--- /dev/null
+++ b/release/tool-Makefile
@@ -0,0 +1,93 @@
+# Makefile for hurd image frobnication.
+
+export OBJCOPY=objcopy
+
+# Where we get programs from
+image-dir=/
+# Where we get libc
+libc-obj-dir = /gd4/hurd-native/build/glibc
+
+smallso-LDFLAGS = -Wl,-rpath-link=/lib
+
+BF=bfloppy
+RF=rfloppy
+
+bfloppy-files = $(bfloppy-bootfs:%=hurd/%) $(bfloppy-bootprogs:%=boot/%.gz) \
+ lib/ld.so boot/servers.boot
+bfloppy-bootfs = ufs
+bfloppy-bootprogs = gnumach serverboot
+
+$(BF)/boot/servers.boot: bfloppy.boot
+ @-rm -f $@
+ @test -d $(@D) || mkdir -p $(@D)
+ cp $< $@
+
+$(BF)/%/: $(image-dir)/%/
+ test -d $@ || mkdir -p $@
+$(addsuffix .gz, $(addprefix $(BF)/boot/, $(bfloppy-bootprogs))): \
+ $(BF)/boot/%.gz: $(image-dir)/boot/%
+ @-rm -f $@
+ @test -d $(@D) || mkdir -p $(@D)
+ $(OBJCOPY) --strip-unneeded $< | gzip -9 > $@
+$(BF)/%: $(image-dir)/%
+ @-rm -f $@
+ @test -d $(@D) || mkdir -p $(@D)
+ $(OBJCOPY) --strip-unneeded $< $@
+
+$(BF): $(bfloppy-files:%=$(BF)/%)
+$(BF).tar: $(bfloppy-files:%=$(BF)/%)
+ rm -f $@
+ cd $(BF); tar covf ../$@ $(^:$(BF)/%=%)
+
+rfloppy-files = $(rfloppy-hurd:%=hurd/%) $(rfloppy-progs:%=bin/%) \
+ $(rfloppy-sprogs:%=sbin/%) \
+ $(rfloppy-solib:%=lib/%.so) hurd/exec \
+ lib/libc.so lib/libhurduser.so lib/libmachuser.so \
+ servers/exec servers/socket/1 \
+ README tmp/ mnt/ dev/MAKEDEV
+rfloppy-hurd = auth storeio null init proc term pfinet nfs ufs ext2fs
+rfloppy-progs = bash ls cat settrans
+rfloppy-sprogs = mkfs.ufs mkfs.ext2
+rfloppy-solib = libtrivfs libthreads libshouldbeinlibc libports \
+ libpager libiohelp libstore libihash libfshelp libnetfs ld
+
+libc-satisfies = $(rfloppy-hurd:%=hurd/%) $(rfloppy-progs:%=bin/%) $(rfloppy-sprogs:%=sbin/%) $(rfloppy-solib:%=lib/%.so)
+
+$(RF)/lib/libc.so: $(image-dir)/lib/libc.so $(image-dir)/lib/libhurduser.so $(image-dir)/lib/libmachuser.so $(libc-satisfies:%=$(image-dir)/%)
+ mksmallso $(smallso-LDFLAGS) \
+ $@ $(libc-obj-dir)/libc_pic.a '-L$(image-dir)/lib -lhurduser -lmachuser' \
+ $(filter-out $(firstword $^),$^)
+
+$(RF)/lib/libhurduser.so: $(RF)/lib/libc.so $(libc-satisfies:%=$(image-dir)/%)
+ mksmallso $(smallso-LDFLAGS) $@ $(libc-obj-dir)/hurd/libhurduser_pic.a -L$(image-dir)/lib -lmachuser $(filter-out $(firstword $^),$^)
+
+$(RF)/lib/libmachuser.so: $(RF)/lib/libc.so $(RF)/lib/libhurduser.so $(libc-satisfies:%=$(image-dir)/%)
+ mksmallso $(smallso-LDFLAGS) $@ $(libc-obj-dir)/mach/libmachuser_pic.a '' $(filter-out $(firstword $^),$^)
+
+$(RF)/bin/%: $(image-dir)/bin/%
+ @-rm -f $@
+ @test -d $(@D) || mkdir -p $(@D)
+ $(OBJCOPY) --strip-unneeded $< $@
+$(RF)/sbin/%: $(image-dir)/sbin/%
+ @-rm -f $@
+ @test -d $(@D) || mkdir -p $(@D)
+ $(OBJCOPY) --strip-unneeded $< $@
+$(RF)/hurd/%: $(image-dir)/hurd/%
+ @-rm -f $@
+ @test -d $(@D) || mkdir -p $(@D)
+ $(OBJCOPY) --strip-unneeded $< $@
+$(RF)/servers/%:
+ @test -d $(@D) || mkdir -p $(@D)
+ touch $@
+$(RF)/%/: $(image-dir)/%/
+ test -d $@ || mkdir -p $@
+
+$(RF): $(rfloppy-files:%=$(RF)/%)
+$(RF).tar: $(rfloppy-files:%=$(RF)/%)
+ rm -f $@
+ cd $(RF); tar covf ../$@ $(^:$(RF)/%=%)
+
+bfloppy.%: $(bfloppy-files:%=$(BF)/%)
+ mkfsimage -q $@ $(BF)
+rfloppy.%.gz: $(rfloppy-files:%=$(RF)/%)
+ mkfsimage --compress -q $@ $(RF)
diff --git a/storeio/Makefile b/storeio/Makefile
new file mode 100644
index 00000000..c1317587
--- /dev/null
+++ b/storeio/Makefile
@@ -0,0 +1,29 @@
+# Makefile for storeio
+#
+# Copyright (C) 1995, 1996, 1997, 2000, 2012 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := storeio
+makemode := server
+
+target = storeio
+SRCS = dev.c storeio.c open.c pager.c io.c
+
+OBJS = $(SRCS:.c=.o)
+HURDLIBS = trivfs pager fshelp iohelp store ports ihash shouldbeinlibc
+OTHERLIBS = -lpthread
+
+include ../Makeconf
diff --git a/storeio/dev.c b/storeio/dev.c
new file mode 100644
index 00000000..8f520cd2
--- /dev/null
+++ b/storeio/dev.c
@@ -0,0 +1,473 @@
+/* store `device' I/O
+
+ Copyright (C) 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2008
+ Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <assert.h>
+#include <string.h>
+#include <hurd/pager.h>
+#include <hurd/store.h>
+#include <sys/mman.h>
+
+#include "dev.h"
+
+/* These functions deal with the buffer used for doing non-block-aligned I/O. */
+
+static inline int
+dev_buf_is_active (struct dev *dev)
+{
+ return dev->buf_offs >= 0;
+}
+
+/* Invalidate DEV's buffer, writing it to disk if necessary. */
+static error_t
+dev_buf_discard (struct dev *dev)
+{
+ if (dev_buf_is_active (dev))
+ {
+ if (dev->buf_dirty)
+ {
+ size_t amount;
+ struct store *store = dev->store;
+ error_t err =
+ store_write (store, dev->buf_offs >> store->log2_block_size,
+ dev->buf, store->block_size, &amount);
+ if (!err && amount < store->block_size)
+ err = EIO;
+ if (err)
+ return err;
+ dev->buf_dirty = 0;
+ }
+ dev->buf_offs = -1;
+ }
+ return 0;
+}
+
+/* Make DEV's buffer active, reading the block from DEV's store which
+ contains OFFS. */
+static error_t
+dev_buf_fill (struct dev *dev, off_t offs)
+{
+ error_t err;
+ unsigned block_mask = dev->block_mask;
+ void *buf = dev->buf;
+ struct store *store = dev->store;
+ size_t buf_len = store->block_size;
+
+ if (dev_buf_is_active (dev))
+ {
+ if ((dev->buf_offs & ~block_mask) == (offs & ~block_mask))
+ return 0; /* Correct block alredy in buffer. */
+ else
+ {
+ err = dev_buf_discard (dev);
+ if (err)
+ return err;
+ }
+ }
+
+ err = store_read (store, offs >> store->log2_block_size, store->block_size,
+ &buf, &buf_len);
+ if (err)
+ return err;
+
+ if (buf != dev->buf)
+ {
+ munmap (dev->buf, store->block_size);
+ dev->buf = buf;
+ }
+
+ dev->buf_offs = offs & ~block_mask;
+
+ return 0;
+}
+
+/* Do an in-buffer partial-block I/O operation. */
+static error_t
+dev_buf_rw (struct dev *dev, size_t buf_offs, size_t *io_offs, size_t *len,
+ error_t (*const buf_rw) (size_t buf_offs,
+ size_t io_offs, size_t len))
+{
+ size_t block_size = dev->store->block_size;
+
+ assert (dev_buf_is_active (dev));
+
+ if (buf_offs + *len >= block_size)
+ /* Only part of BUF lies within the buffer (or everything up
+ to the end of the block, in which case we want to flush
+ the buffer anyway). */
+ {
+ size_t buf_len = block_size - buf_offs;
+ error_t err = (*buf_rw) (buf_offs, *io_offs, buf_len);
+ if (err)
+ return err;
+ *io_offs += buf_len;
+ *len -= buf_len;
+ return dev_buf_discard (dev);
+ }
+ else
+ /* All I/O is within the block. */
+ {
+ error_t err = (*buf_rw) (buf_offs, *io_offs, *len);
+ if (err)
+ return err;
+ *io_offs += *len;
+ *len = 0;
+ return 0;
+ }
+}
+
+/* Called with DEV->lock held. Try to open the store underlying DEV. */
+error_t
+dev_open (struct dev *dev)
+{
+ error_t err;
+ const int flags = ((dev->readonly ? STORE_READONLY : 0)
+ | (dev->no_fileio ? STORE_NO_FILEIO : 0));
+
+ assert (dev->store == 0);
+
+ if (dev->store_name == 0)
+ {
+ /* This means we had no store arguments.
+ We are to operate on our underlying node. */
+ err = store_create (storeio_fsys->underlying, flags, 0, &dev->store);
+ }
+ else
+ /* Open based on the previously parsed store arguments. */
+ err = store_parsed_open (dev->store_name, flags, &dev->store);
+ if (err)
+ return err;
+
+ /* Inactivate the store, it will be activated at first access.
+ We ignore possible EINVAL here . XXX Pass STORE_INACTIVE to
+ store_create/store_parsed_open instead when libstore is fixed
+ to support this. */
+ store_set_flags (dev->store, STORE_INACTIVE);
+
+ dev->buf = mmap (0, dev->store->block_size, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ if (dev->buf == MAP_FAILED)
+ {
+ store_free (dev->store);
+ dev->store = 0;
+ return ENOMEM;
+ }
+
+ if (!dev->inhibit_cache)
+ {
+ dev->buf_offs = -1;
+ pthread_rwlock_init (&dev->io_lock, NULL);
+ dev->block_mask = (1 << dev->store->log2_block_size) - 1;
+ dev->pager = 0;
+ pthread_mutex_init (&dev->pager_lock, NULL);
+ }
+
+ return 0;
+}
+
+/* Shut down the store underlying DEV and free any resources it consumes.
+ DEV itself remains intact so that dev_open can be called again.
+ This should be called with DEV->lock held. */
+void
+dev_close (struct dev *dev)
+{
+ assert (dev->store);
+
+ if (!dev->inhibit_cache)
+ {
+ if (dev->pager != NULL)
+ pager_shutdown (dev->pager);
+
+ dev_buf_discard (dev);
+
+ munmap (dev->buf, dev->store->block_size);
+ }
+
+ store_free (dev->store);
+ dev->store = 0;
+}
+
+/* Try and write out any pending writes to DEV. If WAIT is true, will wait
+ for any paging activity to cease. */
+error_t
+dev_sync(struct dev *dev, int wait)
+{
+ error_t err;
+
+ if (dev->inhibit_cache)
+ return 0;
+
+ /* Sync any paged backing store. */
+ if (dev->pager != NULL)
+ pager_sync (dev->pager, wait);
+
+ pthread_rwlock_wrlock (&dev->io_lock);
+ err = dev_buf_discard (dev);
+ pthread_rwlock_unlock (&dev->io_lock);
+
+ return err;
+}
+
+/* Takes care of buffering I/O to/from DEV for a transfer at position OFFS,
+ length LEN; the amount of I/O successfully done is returned in AMOUNT.
+ BUF_RW is called to do I/O that's entirely inside DEV's internal buffer,
+ and RAW_RW to do I/O directly to DEV's store. */
+static inline error_t
+buffered_rw (struct dev *dev, off_t offs, size_t len, size_t *amount,
+ error_t (* const buf_rw) (size_t buf_offs,
+ size_t io_offs, size_t len),
+ error_t (* const raw_rw) (off_t offs,
+ size_t io_offs, size_t len,
+ size_t *amount))
+{
+ error_t err = 0;
+ unsigned block_mask = dev->block_mask;
+ unsigned block_size = dev->store->block_size;
+ size_t io_offs = 0; /* Offset within this I/O operation. */
+ unsigned block_offs = offs & block_mask; /* Offset within a block. */
+
+ pthread_rwlock_wrlock (&dev->io_lock);
+
+ if (block_offs != 0)
+ /* The start of the I/O isn't block aligned. */
+ {
+ err = dev_buf_fill (dev, offs);
+ if (! err)
+ err = dev_buf_rw (dev, block_offs, &io_offs, &len, buf_rw);
+ }
+
+ if (!err && len > 0)
+ /* Now the I/O should be block aligned. */
+ {
+ if (len >= block_size)
+ {
+ size_t amount;
+ err = dev_buf_discard (dev);
+ if (! err)
+ err =
+ (*raw_rw) (offs + io_offs, io_offs, len & ~block_mask, &amount);
+ if (! err)
+ {
+ io_offs += amount;
+ len -= amount;
+ }
+ }
+ if (len > 0 && len < block_size)
+ /* All full blocks were written successfully, so write
+ the tail end into the buffer. */
+ {
+ err = dev_buf_fill (dev, offs + io_offs);
+ if (! err)
+ err = dev_buf_rw (dev, 0, &io_offs, &len, buf_rw);
+ }
+ }
+
+ if (! err)
+ *amount = io_offs;
+
+ pthread_rwlock_unlock (&dev->io_lock);
+
+ return err;
+}
+
+/* Takes care of buffering I/O to/from DEV for a transfer at position OFFS,
+ length LEN, and direction DIR. BUF_RW is called to do I/O to/from data
+ buffered in DEV, and RAW_RW to do I/O directly to DEV's store. */
+static inline error_t
+dev_rw (struct dev *dev, off_t offs, size_t len, size_t *amount,
+ error_t (* const buf_rw) (size_t buf_offs,
+ size_t io_offs, size_t len),
+ error_t (* const raw_rw) (off_t offs,
+ size_t io_offs, size_t len,
+ size_t *amount))
+{
+ error_t err;
+ unsigned block_mask = dev->block_mask;
+
+ if (offs < 0 || offs > dev->store->size)
+ return EINVAL;
+ else if (offs + len > dev->store->size)
+ len = dev->store->size - offs;
+
+ pthread_rwlock_rdlock (&dev->io_lock);
+ if (dev_buf_is_active (dev)
+ || (offs & block_mask) != 0 || (len & block_mask) != 0)
+ /* Some non-aligned I/O has been done, or is needed, so we need to deal
+ with DEV's buffer, which means getting an exclusive lock. */
+ {
+ /* Acquire a writer lock instead of a reader lock. Note that other
+ writers may have acquired the lock by the time we get it. */
+ pthread_rwlock_unlock (&dev->io_lock);
+ err = buffered_rw (dev, offs, len, amount, buf_rw, raw_rw);
+ }
+ else
+ /* Only block-aligned I/O is being done, so things are easy. */
+ {
+ err = (*raw_rw) (offs, 0, len, amount);
+ pthread_rwlock_unlock (&dev->io_lock);
+ }
+
+ return err;
+}
+
+/* Write LEN bytes from BUF to DEV, returning the amount actually written in
+ AMOUNT. If successful, 0 is returned, otherwise an error code is
+ returned. */
+error_t
+dev_write (struct dev *dev, off_t offs, void *buf, size_t len,
+ size_t *amount)
+{
+ error_t buf_write (size_t buf_offs, size_t io_offs, size_t len)
+ {
+ bcopy (buf + io_offs, dev->buf + buf_offs, len);
+ dev->buf_dirty = 1;
+ return 0;
+ }
+ error_t raw_write (off_t offs, size_t io_offs, size_t len, size_t *amount)
+ {
+ struct store *store = dev->store;
+ return
+ store_write (store, offs >> store->log2_block_size,
+ buf + io_offs, len, amount);
+ }
+
+ if (dev->inhibit_cache)
+ {
+ /* Under --no-cache, we permit only whole-block writes.
+ Note that in this case we handle non-power-of-two block sizes. */
+
+ struct store *store = dev->store;
+
+ if (store->block_size == 0)
+ /* We don't know the block size, so let the device enforce it. */
+ return store_write (dev->store, offs, buf, len, amount);
+
+ if ((offs & (store->block_size - 1)) != 0
+ || (len & (store->block_size - 1)) != 0)
+ /* Not whole blocks. No can do. */
+ return EINVAL; /* EIO? */
+
+ /* Do a direct write to the store. */
+ return store_write (dev->store, offs << store->log2_block_size,
+ buf, len, amount);
+ }
+
+ return dev_rw (dev, offs, len, amount, buf_write, raw_write);
+}
+
+/* Read up to WHOLE_AMOUNT bytes from DEV, returned in BUF and LEN in the
+ with the usual mach memory result semantics. If successful, 0 is
+ returned, otherwise an error code is returned. */
+error_t
+dev_read (struct dev *dev, off_t offs, size_t whole_amount,
+ void **buf, size_t *len)
+{
+ error_t err;
+ int allocated_buf = 0;
+ error_t ensure_buf ()
+ {
+ if (*len < whole_amount)
+ {
+ void *new = mmap (0, whole_amount, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ if (new == (void *) -1)
+ return errno;
+ *buf = new;
+ allocated_buf = 1;
+ }
+ return 0;
+ }
+ error_t buf_read (size_t buf_offs, size_t io_offs, size_t len)
+ {
+ error_t err = ensure_buf ();
+ if (! err)
+ bcopy (dev->buf + buf_offs, *buf + io_offs, len);
+ return err;
+ }
+ error_t raw_read (off_t offs, size_t io_offs, size_t len, size_t *amount)
+ {
+ struct store *store = dev->store;
+ off_t addr = offs >> store->log2_block_size;
+ if (len == whole_amount)
+ /* Just return whatever the device does. */
+ return store_read (store, addr, len, buf, amount);
+ else
+ /* This read is returning less than the whole request, so we allocate
+ a buffer big enough to hold everything, in case we have to
+ coalesce multiple reads into a single return buffer. */
+ {
+ error_t err = ensure_buf ();
+ if (! err)
+ {
+ void *_req_buf = *buf + io_offs, *req_buf = _req_buf;
+ size_t req_len = len;
+ err = store_read (store, addr, len, &req_buf, &req_len);
+ if (! err)
+ {
+ if (req_buf != _req_buf)
+ /* Copy from wherever the read put it. */
+ {
+ bcopy (req_buf, _req_buf, req_len);
+ munmap (req_buf, req_len);
+ }
+ *amount = req_len;
+ }
+ }
+ return err;
+ }
+ }
+
+ if (dev->store->size > 0 && offs == dev->store->size)
+ {
+ /* Reading end of file. */
+ *len = 0;
+ return 0;
+ }
+
+ if (dev->inhibit_cache)
+ {
+ /* Under --no-cache, we permit only whole-block reads.
+ Note that in this case we handle non-power-of-two block sizes.
+ We could, that is, but libstore won't have it (see libstore/make.c).
+ If the device does not report a block size, we let any attempt
+ through on the assumption the device will enforce its own limits. */
+
+ struct store *store = dev->store;
+
+ if (store->block_size == 0)
+ /* We don't know the block size, so let the device enforce it. */
+ return store_read (dev->store, offs, whole_amount, buf, len);
+
+ if ((offs & (store->block_size - 1)) != 0
+ || (whole_amount & (store->block_size - 1)) != 0)
+ /* Not whole blocks. No can do. */
+ return EINVAL;
+
+ /* Do a direct read from the store. */
+ return store_read (dev->store, offs << store->log2_block_size,
+ whole_amount, buf, len);
+ }
+
+ err = dev_rw (dev, offs, whole_amount, len, buf_read, raw_read);
+ if (err && allocated_buf)
+ munmap (*buf, whole_amount);
+
+ return err;
+}
diff --git a/storeio/dev.h b/storeio/dev.h
new file mode 100644
index 00000000..139668a7
--- /dev/null
+++ b/storeio/dev.h
@@ -0,0 +1,127 @@
+/* store `device' I/O
+
+ Copyright (C) 1995,96,97,99,2000,2001 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __DEV_H__
+#define __DEV_H__
+
+#include <mach.h>
+#include <device/device.h>
+#include <pthread.h>
+#include <hurd/store.h>
+#include <hurd/trivfs.h>
+
+extern struct trivfs_control *storeio_fsys;
+
+/* Information about backend store, which we presumptively call a "device". */
+struct dev
+{
+ /* The argument specification that we use to open the store. */
+ struct store_parsed *store_name;
+
+ /* The device to which we're doing io. This is null when the
+ device is closed, in which case we will open from `store_name'. */
+ struct store *store;
+
+ int readonly; /* Nonzero if user gave --readonly flag. */
+ int enforced; /* Nonzero if user gave --enforced flag. */
+ int no_fileio; /* Nonzero if user gave --no-fileio flag. */
+ dev_t rdev; /* A unixy device number for st_rdev. */
+
+ /* The current owner of the open device. For terminals, this affects
+ controlling terminal behavior (see term_become_ctty). For all objects
+ this affects old-style async IO. Negative values represent pgrps. This
+ has nothing to do with the owner of a file (as returned by io_stat, and
+ as used for various permission checks by filesystems). An owner of 0
+ indicates that there is no owner. */
+ pid_t owner;
+
+ /* The number of active opens. */
+ int nperopens;
+
+ /* This lock protects `store', `owner' and `nperopens'. The other
+ members never change after creation, except for those locked by
+ io_lock (below). */
+ pthread_mutex_t lock;
+
+ /* Nonzero iff the --no-cache flag was given.
+ If this is set, the remaining members are not used at all
+ and don't need to be initialized or cleaned up. */
+ int inhibit_cache;
+
+ /* A bitmask corresponding to the part of an offset that lies within a
+ device block. */
+ unsigned block_mask;
+
+ /* Lock to arbitrate I/O through this device. Block I/O can occur in
+ parallel, and requires only a reader-lock.
+ Non-block I/O is always serialized, and requires a writer-lock. */
+ pthread_rwlock_t io_lock;
+
+ /* Non-block I/O is buffered through BUF. BUF_OFFS is the device offset
+ corresponding to the start of BUF (which holds one block); if it is -1,
+ then BUF is inactive. */
+ void *buf;
+ off_t buf_offs;
+ int buf_dirty;
+
+ struct pager *pager;
+ pthread_mutex_t pager_lock;
+};
+
+static inline int
+dev_is_readonly (const struct dev *dev)
+{
+ return dev->readonly || (dev->store && (dev->store->flags & STORE_READONLY));
+}
+
+/* Called with DEV->lock held. Try to open the store underlying DEV. */
+error_t dev_open (struct dev *dev);
+
+/* Shut down the store underlying DEV and free any resources it consumes.
+ DEV itself remains intact so that dev_open can be called again.
+ This should be called with DEV->lock held. */
+void dev_close (struct dev *dev);
+
+/* Returns in MEMOBJ the port for a memory object backed by the storage on
+ DEV. Returns 0 or the error code if an error occurred. */
+error_t dev_get_memory_object(struct dev *dev, vm_prot_t prot,
+ memory_object_t *memobj);
+
+/* Try to stop all paging activity on DEV, returning true if we were
+ successful. If NOSYNC is true, then we won't write back any (kernel)
+ cached pages to the device. */
+int dev_stop_paging (struct dev *dev, int nosync);
+
+/* Try and write out any pending writes to DEV. If WAIT is true, will wait
+ for any paging activity to cease. */
+error_t dev_sync (struct dev *dev, int wait);
+
+/* Write LEN bytes from BUF to DEV, returning the amount actually written in
+ AMOUNT. If successful, 0 is returned, otherwise an error code is
+ returned. */
+error_t dev_write (struct dev *dev, off_t offs, void *buf, size_t len,
+ size_t *amount);
+
+/* Read up to AMOUNT bytes from DEV, returned in BUF and LEN in the with the
+ usual mach memory result semantics. If successful, 0 is returned,
+ otherwise an error code is returned. */
+error_t dev_read (struct dev *dev, off_t offs, size_t amount,
+ void **buf, size_t *len);
+
+#endif /* !__DEV_H__ */
diff --git a/storeio/io.c b/storeio/io.c
new file mode 100644
index 00000000..2295615e
--- /dev/null
+++ b/storeio/io.c
@@ -0,0 +1,378 @@
+/* The hurd io interface to storeio
+
+ Copyright (C) 1995,96,97,99,2000,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd/trivfs.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include "open.h"
+#include "dev.h"
+#include "libtrivfs/trivfs_fs_S.h"
+#include "libtrivfs/trivfs_io_S.h"
+
+/* Return objects mapping the data underlying this memory object. If
+ the object can be read then memobjrd will be provided; if the
+ object can be written then memobjwr will be provided. For objects
+ where read data and write data are the same, these objects will be
+ equal, otherwise they will be disjoint. Servers are permitted to
+ implement io_map but not io_map_cntl. Some objects do not provide
+ mapping; they will set none of the ports and return an error. Such
+ objects can still be accessed by io_read and io_write. */
+error_t
+trivfs_S_io_map (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ memory_object_t *rd_obj, mach_msg_type_name_t *rd_type,
+ memory_object_t *wr_obj, mach_msg_type_name_t *wr_type)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else if (! (cred->po->openmodes & (O_READ|O_WRITE)))
+ return EBADF;
+ else
+ {
+ mach_port_t memobj;
+ int flags = cred->po->openmodes;
+ vm_prot_t prot =
+ ((flags & O_READ) ? VM_PROT_READ : 0)
+ | ((flags & O_WRITE) ? VM_PROT_WRITE : 0);
+ struct open *open = (struct open *)cred->po->hook;
+ error_t err = dev_get_memory_object (open->dev, prot, &memobj);
+
+ if (!err)
+ {
+ if (flags & O_READ)
+ *rd_obj = memobj;
+ else
+ *rd_obj = MACH_PORT_NULL;
+ if (flags & O_WRITE)
+ *wr_obj = memobj;
+ else
+ *wr_obj = MACH_PORT_NULL;
+
+ if ((flags & (O_READ|O_WRITE)) == (O_READ|O_WRITE)
+ && memobj != MACH_PORT_NULL)
+ mach_port_mod_refs (mach_task_self (), memobj,
+ MACH_PORT_RIGHT_SEND, 1);
+ }
+
+ *rd_type = *wr_type = MACH_MSG_TYPE_MOVE_SEND;
+
+ return err;
+ }
+}
+
+/* Read data from an IO object. If offset if -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMOUNT. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, mach_msg_type_number_t *data_len,
+ loff_t offs, mach_msg_type_number_t amount)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else if (! (cred->po->openmodes & O_READ))
+ return EBADF;
+ else
+ return open_read ((struct open *)cred->po->hook,
+ offs, amount, (void **)data, data_len);
+}
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+error_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mach_msg_type_number_t *amount)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else if (! (cred->po->openmodes & O_READ))
+ return EBADF;
+ else
+ {
+ struct open *open = (struct open *)cred->po->hook;
+ *amount = open->dev->store->size - open->offs;
+ return 0;
+ }
+}
+
+/* Write data to an IO object. If offset is -1, write at the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount successfully written is returned in amount. A
+ given user should not have more than one outstanding io_write on an
+ object at a time; servers implement congestion control by delaying
+ responses to io_write. Servers may drop data (returning ENOBUFS)
+ if they recevie more than one write when not prepared for it. */
+error_t
+trivfs_S_io_write (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *data, mach_msg_type_number_t data_len,
+ loff_t offs, mach_msg_type_number_t *amount)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else if (! (cred->po->openmodes & O_WRITE))
+ return EBADF;
+ else
+ return open_write ((struct open *)cred->po->hook,
+ offs, (void *)data, data_len, amount);
+}
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offs, int whence, off_t *new_offs)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else
+ return open_seek ((struct open *)cred->po->hook, offs, whence, new_offs);
+}
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. */
+error_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *type)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ *type &= ~SELECT_URG;
+ return 0;
+}
+
+error_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec ts,
+ int *type)
+{
+ return trivfs_S_io_select (cred, reply, reply_type, type);
+}
+
+/* Truncate file. */
+error_t
+trivfs_S_file_set_size (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t size)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else if (size < 0)
+ return EINVAL;
+ else
+ return 0;
+}
+
+/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and
+ O_NONBLOCK bits for the IO object. In addition, io_get_openmodes
+ will tell you which of O_READ, O_WRITE, and O_EXEC the object can
+ be used for. The O_ASYNC bit affects icky async I/O; good async
+ I/O is done through io_async which is orthogonal to these calls. */
+
+error_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *bits)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else
+ {
+ *bits = cred->po->openmodes;
+ return 0;
+ }
+}
+
+error_t
+trivfs_S_io_set_all_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int mode)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+/* Get/set the owner of the IO object. For terminals, this affects
+ controlling terminal behavior (see term_become_ctty). For all
+ objects this affects old-style async IO. Negative values represent
+ pgrps. This has nothing to do with the owner of a file (as
+ returned by io_stat, and as used for various permission checks by
+ filesystems). An owner of 0 indicates that there is no owner. */
+error_t
+trivfs_S_io_get_owner (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ pid_t *owner)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else
+ {
+ struct open *open = (struct open *)cred->po->hook;
+ *owner = open->dev->owner; /* atomic word fetch */
+ return 0;
+ }
+}
+
+error_t
+trivfs_S_io_mod_owner (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ pid_t owner)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else
+ {
+ struct open *open = (struct open *)cred->po->hook;
+ open->dev->owner = owner; /* atomic word store */
+ return 0;
+ }
+}
+
+/* File syncing operations; these all do the same thing, sync the underlying
+ device. */
+
+error_t
+trivfs_S_file_sync (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int wait, int omit_metadata)
+{
+ if (cred)
+ return dev_sync (((struct open *)cred->po->hook)->dev, wait);
+ else
+ return EOPNOTSUPP;
+}
+
+error_t
+trivfs_S_file_syncfs (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int wait, int dochildren)
+{
+ if (cred)
+ return dev_sync (((struct open *)cred->po->hook)->dev, wait);
+ else
+ return EOPNOTSUPP;
+}
+
+error_t
+trivfs_S_file_get_storage_info (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mach_port_t **ports,
+ mach_msg_type_name_t *ports_type,
+ mach_msg_type_number_t *num_ports,
+ int **ints, mach_msg_type_number_t *num_ints,
+ off_t **offsets,
+ mach_msg_type_number_t *num_offsets,
+ char **data, mach_msg_type_number_t *data_len)
+{
+ *ports_type = MACH_MSG_TYPE_COPY_SEND;
+
+ if (! cred || ! cred->po->hook)
+ return EOPNOTSUPP;
+ else
+ {
+ error_t err;
+ struct dev *dev = ((struct open *)cred->po->hook)->dev;
+ struct store *store = dev->store;
+
+ if (dev->enforced && !(store->flags & STORE_ENFORCED))
+ {
+ /* The --enforced switch tells us not to let anyone
+ get at the device, no matter how trustable they are. */
+ size_t name_len = (store->name ? strlen (store->name) + 1 : 0);
+ int i;
+ *num_ports = 0;
+ i = 0;
+ (*ints)[i++] = STORAGE_OTHER;
+ (*ints)[i++] = store->flags;
+ (*ints)[i++] = store->block_size;
+ (*ints)[i++] = 1; /* num_runs */
+ (*ints)[i++] = name_len;
+ (*ints)[i++] = 0; /* misc_len */
+ *num_ints = i;
+ i = 0;
+ (*offsets)[i++] = 0;
+ (*offsets)[i++] = store->size;
+ *num_offsets = i;
+ if (store->name)
+ memcpy (*data, store->name, name_len);
+ *data_len = name_len;
+ return 0;
+ }
+
+ if (!cred->isroot
+ && !store_is_securely_returnable (store, cred->po->openmodes))
+ {
+ struct store *clone;
+ err = store_clone (store, &clone);
+ if (! err)
+ {
+ err = store_set_flags (clone, STORE_INACTIVE);
+ if (err == EINVAL)
+ err = EACCES;
+ else
+ err = store_return (clone,
+ ports, num_ports, ints, num_ints,
+ offsets, num_offsets, data, data_len);
+ store_free (clone);
+ }
+ }
+ else
+ err = store_return (store,
+ ports, num_ports, ints, num_ints,
+ offsets, num_offsets, data, data_len);
+
+ return err;
+ }
+}
diff --git a/storeio/open.c b/storeio/open.c
new file mode 100644
index 00000000..f6a641d7
--- /dev/null
+++ b/storeio/open.c
@@ -0,0 +1,127 @@
+/* Per-open information for storeio
+
+ Copyright (C) 1995, 1996, 2006 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+
+#include "open.h"
+#include "dev.h"
+
+/* Returns a new per-open structure for the device DEV in OPEN. If an error
+ occurs, the error-code is returned, otherwise 0. */
+error_t
+open_create (struct dev *dev, struct open **open)
+{
+ *open = malloc (sizeof (struct open));
+ if (*open == NULL)
+ return ENOMEM;
+
+ (*open)->dev = dev;
+ (*open)->offs = 0;
+ pthread_mutex_init (&(*open)->lock, NULL);
+
+ return 0;
+}
+
+/* Free OPEN and any resources it holds. */
+void
+open_free (struct open *open)
+{
+ free (open);
+}
+
+/* Writes up to LEN bytes from BUF to OPEN's device at device offset OFFS
+ (which may be ignored if the device doesn't support random access),
+ and returns the number of bytes written in AMOUNT. If no error occurs,
+ zero is returned, otherwise the error code is returned. */
+error_t
+open_write (struct open *open, off_t offs, void *buf, size_t len,
+ vm_size_t *amount)
+{
+ error_t err;
+ if (offs < 0)
+ /* Use OPEN's offset. */
+ {
+ pthread_mutex_lock (&open->lock);
+ err = dev_write (open->dev, open->offs, buf, len, amount);
+ if (! err)
+ open->offs += *amount;
+ pthread_mutex_unlock (&open->lock);
+ }
+ else
+ err = dev_write (open->dev, offs, buf, len, amount);
+ return err;
+}
+
+/* Reads up to AMOUNT bytes from the device into BUF and LEN using the
+ standard mach out-array convention. If no error occurs, zero is returned,
+ otherwise the error code is returned. */
+error_t
+open_read (struct open *open, off_t offs, size_t amount,
+ void **buf, vm_size_t *len)
+{
+ error_t err;
+ if (offs < 0)
+ /* Use OPEN's offset. */
+ {
+ pthread_mutex_lock (&open->lock);
+ err = dev_read (open->dev, open->offs, amount, buf, len);
+ if (! err)
+ open->offs += *len;
+ pthread_mutex_unlock (&open->lock);
+ }
+ else
+ err = dev_read (open->dev, offs, amount, buf, len);
+ return err;
+}
+
+/* Set OPEN's location to OFFS, interpreted according to WHENCE as by seek.
+ The new absolute location is returned in NEW_OFFS (and may not be the same
+ as OFFS). If no error occurs, zero is returned, otherwise the error code
+ is returned. */
+error_t
+open_seek (struct open *open, off_t offs, int whence, off_t *new_offs)
+{
+ error_t err = 0;
+
+ pthread_mutex_lock (&open->lock);
+
+ switch (whence)
+ {
+ case SEEK_CUR:
+ offs += open->offs;
+ goto check;
+ case SEEK_END:
+ offs += open->dev->store->size;
+ case SEEK_SET:
+ check:
+ if (offs >= 0)
+ {
+ *new_offs = open->offs = offs;
+ break;
+ }
+ default:
+ err = EINVAL;
+ }
+
+ pthread_mutex_unlock (&open->lock);
+
+ return err;
+}
diff --git a/storeio/open.h b/storeio/open.h
new file mode 100644
index 00000000..78ad95ca
--- /dev/null
+++ b/storeio/open.h
@@ -0,0 +1,68 @@
+/* Per-open information for storeio
+
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __OPEN_H__
+#define __OPEN_H__
+
+#include "dev.h"
+
+/* ---------------------------------------------------------------- */
+
+/* A structure describing a particular i/o stream on this device. */
+struct open
+{
+ /* The device that this an open on. */
+ struct dev *dev;
+
+ /* The per-open offset used for I/O operations that don't specify an
+ explicit offset. */
+ off_t offs;
+
+ /* A lock used to control write access to OFFS. */
+ pthread_mutex_t lock;
+};
+
+/* Returns a new per-open structure for the device DEV in OPEN. If an error
+ occurs, the error-code is returned, otherwise 0. */
+error_t open_create (struct dev *dev, struct open **open);
+
+/* Free OPEN and any resources it holds. */
+void open_free (struct open *open);
+
+/* Writes up to LEN bytes from BUF to OPEN's device at device offset OFFS
+ (which may be ignored if the device doesn't support random access),
+ and returns the number of bytes written in AMOUNT. If no error occurs,
+ zero is returned, otherwise the error code is returned. */
+error_t open_write (struct open *open, off_t offs, void *buf, size_t len,
+ size_t *amount);
+
+/* Reads up to AMOUNT bytes from the device into BUF and BUF_LEN using the
+ standard mach out-array convention. If no error occurs, zero is returned,
+ otherwise the error code is returned. */
+error_t open_read (struct open *open, off_t offs, size_t amount,
+ void **buf, size_t *buf_len);
+
+/* Set OPEN's location to OFFS, interpreted according to WHENCE as by seek.
+ The new absolute location is returned in NEW_OFFS (and may not be the same
+ as OFFS). If no error occurs, zero is returned, otherwise the error code
+ is returned. */
+error_t open_seek (struct open *open, off_t offs, int whence, off_t *new_offs);
+
+#endif /* !__OPEN_H__ */
diff --git a/storeio/pager.c b/storeio/pager.c
new file mode 100644
index 00000000..7d787110
--- /dev/null
+++ b/storeio/pager.c
@@ -0,0 +1,286 @@
+/* Paging interface for storeio devices
+
+ Copyright (C) 1995,96,97,99,2002 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <hurd/pager.h>
+#include <assert.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <stdio.h>
+
+#include "dev.h"
+
+/* ---------------------------------------------------------------- */
+/* Pager library callbacks; see <hurd/pager.h> for more info. */
+
+/* For pager PAGER, read one page from offset PAGE. Set *BUF to be the
+ address of the page, and set *WRITE_LOCK if the page must be provided
+ read-only. The only permissible error returns are EIO, EDQUOT, and
+ ENOSPC. */
+error_t
+pager_read_page (struct user_pager_info *upi,
+ vm_offset_t page, vm_address_t *buf, int *writelock)
+{
+ error_t err;
+ size_t read = 0; /* bytes actually read */
+ int want = vm_page_size; /* bytes we want to read */
+ struct dev *dev = (struct dev *)upi;
+ struct store *store = dev->store;
+
+ if (page + want > store->size)
+ /* Read a partial page if necessary to avoid reading off the end. */
+ want = store->size - page;
+
+ err = dev_read (dev, page, want, (void **)buf, &read);
+
+ if (!err && want < vm_page_size)
+ /* Zero anything we didn't read. Allocation only happens in page-size
+ multiples, so we know we can write there. */
+ memset ((char *)*buf + want, '\0', vm_page_size - want);
+
+ *writelock = (store->flags & STORE_READONLY);
+
+ if (err || read < want)
+ return EIO;
+ else
+ return 0;
+}
+
+/* For pager PAGER, synchronously write one page from BUF to offset PAGE. In
+ addition, vm_deallocate (or equivalent) BUF. The only permissible error
+ returns are EIO, EDQUOT, and ENOSPC. */
+error_t
+pager_write_page (struct user_pager_info *upi,
+ vm_offset_t page, vm_address_t buf)
+{
+ struct dev *dev = (struct dev *)upi;
+ struct store *store = dev->store;
+
+ if (store->flags & STORE_READONLY)
+ return EROFS;
+ else
+ {
+ error_t err;
+ size_t written;
+ int want = vm_page_size;
+
+ if (page + want > store->size)
+ /* Write a partial page if necessary to avoid reading off the end. */
+ want = store->size - page;
+
+ err = dev_write (dev, page, (char *)buf, want, &written);
+
+ munmap ((caddr_t) buf, vm_page_size);
+
+ if (err || written < want)
+ return EIO;
+ else
+ return 0;
+ }
+}
+
+/* A page should be made writable. */
+error_t
+pager_unlock_page (struct user_pager_info *upi, vm_offset_t address)
+{
+ struct dev *dev = (struct dev *)upi;
+
+ if (dev->store->flags & STORE_READONLY)
+ return EROFS;
+ else
+ return 0;
+}
+
+void
+pager_notify_evict (struct user_pager_info *pager,
+ vm_offset_t page)
+{
+ assert (!"unrequested notification on eviction");
+}
+
+/* The user must define this function. It should report back (in
+ *OFFSET and *SIZE the minimum valid address the pager will accept
+ and the size of the object. */
+error_t
+pager_report_extent (struct user_pager_info *upi,
+ vm_address_t *offset, vm_size_t *size)
+{
+ *offset = 0;
+ *size = ((struct dev *)upi)->store->size;
+ return 0;
+}
+
+/* This is called when a pager is being deallocated after all extant send
+ rights have been destroyed. */
+void
+pager_clear_user_data (struct user_pager_info *upi)
+{
+ struct dev *dev = (struct dev *)upi;
+ pthread_mutex_lock (&dev->pager_lock);
+ dev->pager = 0;
+ pthread_mutex_unlock (&dev->pager_lock);
+}
+
+static struct port_bucket *pager_port_bucket = 0;
+
+/* A top-level function for the paging thread that just services paging
+ requests. */
+static void *
+service_paging_requests (void *arg)
+{
+ (void) arg;
+
+ for (;;)
+ ports_manage_port_operations_multithread (pager_port_bucket,
+ pager_demuxer,
+ 1000 * 30, 1000 * 60 * 5, 0);
+
+ return NULL;
+}
+
+/* Initialize paging for this device. */
+static void
+init_dev_paging ()
+{
+ if (! pager_port_bucket)
+ {
+ static pthread_mutex_t pager_global_lock = PTHREAD_MUTEX_INITIALIZER;
+
+ pthread_mutex_lock (&pager_global_lock);
+ if (pager_port_bucket == NULL)
+ {
+ pthread_t thread;
+ error_t err;
+
+ pager_port_bucket = ports_create_bucket ();
+
+ /* Make a thread to service paging requests. */
+ err = pthread_create (&thread, NULL, service_paging_requests, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+ }
+ pthread_mutex_unlock (&pager_global_lock);
+ }
+}
+
+void
+pager_dropweak (struct user_pager_info *upi __attribute__ ((unused)))
+{
+}
+
+/* Try to stop all paging activity on DEV, returning true if we were
+ successful. If NOSYNC is true, then we won't write back any (kernel)
+ cached pages to the device. */
+int
+dev_stop_paging (struct dev *dev, int nosync)
+{
+ size_t num_pagers = (pager_port_bucket ?
+ ports_count_bucket (pager_port_bucket) : 0);
+
+ if (num_pagers > 0 && !nosync)
+ {
+ error_t block_cache (void *arg)
+ {
+ struct pager *p = arg;
+ pager_change_attributes (p, 0, MEMORY_OBJECT_COPY_DELAY, 1);
+ return 0;
+ }
+ error_t enable_cache (void *arg)
+ {
+ struct pager *p = arg;
+ pager_change_attributes (p, 1, MEMORY_OBJECT_COPY_DELAY, 0);
+ return 0;
+ }
+
+ /* Loop through the pagers and turn off caching one by one,
+ synchronously. That should cause termination of each pager. */
+ ports_bucket_iterate (pager_port_bucket, block_cache);
+
+ /* Give it a second; the kernel doesn't actually shutdown
+ immediately. XXX */
+ sleep (1);
+
+ num_pagers = ports_count_bucket (pager_port_bucket);
+ if (num_pagers > 0)
+ /* Darn, there are actual honest users. Turn caching back on,
+ and return failure. */
+ ports_bucket_iterate (pager_port_bucket, enable_cache);
+ }
+
+ return num_pagers == 0;
+}
+
+/* Returns in MEMOBJ the port for a memory object backed by the storage on
+ DEV. Returns 0 or the error code if an error occurred. */
+error_t
+dev_get_memory_object (struct dev *dev, vm_prot_t prot, memory_object_t *memobj)
+{
+ error_t err = store_map (dev->store, prot, memobj);
+
+ if (err == EOPNOTSUPP && !dev->inhibit_cache)
+ {
+ int created = 0;
+
+ init_dev_paging ();
+
+ pthread_mutex_lock (&dev->pager_lock);
+
+ if (dev->pager == NULL)
+ {
+ dev->pager =
+ pager_create ((struct user_pager_info *)dev, pager_port_bucket,
+ 1, MEMORY_OBJECT_COPY_DELAY, 0);
+ if (dev->pager == NULL)
+ {
+ pthread_mutex_unlock (&dev->pager_lock);
+ return errno;
+ }
+ created = 1;
+ }
+
+ *memobj = pager_get_port (dev->pager);
+
+ if (*memobj == MACH_PORT_NULL)
+ /* Pager is currently being destroyed, try again. */
+ {
+ dev->pager = 0;
+ pthread_mutex_unlock (&dev->pager_lock);
+ return dev_get_memory_object (dev, prot, memobj);
+ }
+ else
+ err =
+ mach_port_insert_right (mach_task_self (),
+ *memobj, *memobj, MACH_MSG_TYPE_MAKE_SEND);
+
+ if (created)
+ ports_port_deref (dev->pager);
+
+ pthread_mutex_unlock (&dev->pager_lock);
+ }
+
+ return err;
+}
diff --git a/storeio/storeio.c b/storeio/storeio.c
new file mode 100644
index 00000000..eb383495
--- /dev/null
+++ b/storeio/storeio.c
@@ -0,0 +1,426 @@
+/* A translator for doing I/O to stores
+
+ Copyright (C) 1995,96,97,98,99,2000,01,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <error.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <argp.h>
+#include <argz.h>
+
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <version.h>
+
+#include "open.h"
+#include "dev.h"
+#include "libtrivfs/trivfs_fsys_S.h"
+
+static struct argp_option options[] =
+{
+ {"readonly", 'r', 0, 0,"Disallow writing"},
+ {"writable", 'w', 0, 0,"Allow writing"},
+ {"no-cache", 'c', 0, 0,"Never cache data--user io does direct device io"},
+ {"no-file-io", 'F', 0, 0,"Never perform io via plain file io RPCs"},
+ {"no-fileio", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"enforced", 'e', 0, 0,"Never reveal underlying devices, even to root"},
+ {"rdev", 'n', "ID", 0,
+ "The stat rdev number for this node; may be either a"
+ " single integer, or of the form MAJOR,MINOR"},
+ {0}
+};
+static const char doc[] = "Translator for devices and other stores";
+
+const char *argp_program_version = STANDARD_HURD_VERSION (storeio);
+
+/* Desired store parameters specified by the user. */
+struct storeio_argp_params
+{
+ struct store_argp_params store_params; /* Filled in by store_argp parser. */
+ struct dev *dev; /* We fill in its flag members. */
+};
+
+/* Parse a single option. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct storeio_argp_params *params = state->input;
+
+ switch (key)
+ {
+
+ case 'r': params->dev->readonly = 1; break;
+ case 'w': params->dev->readonly = 0; break;
+
+ case 'c': params->dev->inhibit_cache = 1; break;
+ case 'e': params->dev->enforced = 1; break;
+ case 'F': params->dev->no_fileio = 1; break;
+
+ case 'n':
+ {
+ char *start = arg, *end;
+ dev_t rdev;
+
+ rdev = strtoul (start, &end, 0);
+ if (*end == ',')
+ /* MAJOR,MINOR form */
+ {
+ start = end + 1;
+ rdev = makedev (rdev, strtoul (start, &end, 0));
+ }
+
+ if (end == start || *end != '\0')
+ {
+ argp_error (state, "%s: Invalid argument to --rdev", arg);
+ return EINVAL;
+ }
+
+ params->dev->rdev = rdev;
+ }
+ break;
+
+ case ARGP_KEY_INIT:
+ /* Now store_argp's parser will get to initialize its state.
+ The default_type member is our input parameter to it. */
+ bzero (&params->store_params, sizeof params->store_params);
+ params->store_params.default_type = "device";
+ params->store_params.store_optional = 1;
+ state->child_inputs[0] = &params->store_params;
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ params->dev->store_name = params->store_params.result;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static const struct argp_child argp_kids[] = { { &store_argp }, {0} };
+static const struct argp argp = { options, parse_opt, 0, doc, argp_kids };
+
+struct trivfs_control *storeio_fsys;
+
+int
+main (int argc, char *argv[])
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct dev device;
+ struct storeio_argp_params params;
+
+ bzero (&device, sizeof device);
+ pthread_mutex_init (&device.lock, NULL);
+
+ params.dev = &device;
+ argp_parse (&argp, argc, argv, 0, 0, &params);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (2, 0, "Must be started as a translator");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &storeio_fsys);
+ if (err)
+ error (3, err, "trivfs_startup");
+
+ storeio_fsys->hook = &device;
+
+ /* Launch. */
+ ports_manage_port_operations_multithread (storeio_fsys->pi.bucket,
+ trivfs_demuxer,
+ 30*1000, 5*60*1000, 0);
+
+ return 0;
+}
+
+error_t
+trivfs_append_args (struct trivfs_control *trivfs_control,
+ char **argz, size_t *argz_len)
+{
+ struct dev *const dev = trivfs_control->hook;
+ error_t err = 0;
+
+ if (dev->rdev != (dev_t) 0)
+ {
+ char buf[40];
+ snprintf (buf, sizeof buf, "--rdev=%d,%d",
+ major (dev->rdev), minor (dev->rdev));
+ err = argz_add (argz, argz_len, buf);
+ }
+
+ if (!err && dev->inhibit_cache)
+ err = argz_add (argz, argz_len, "--no-cache");
+
+ if (!err && dev->enforced)
+ err = argz_add (argz, argz_len, "--enforced");
+
+ if (!err && dev->no_fileio)
+ err = argz_add (argz, argz_len, "--no-file-io");
+
+ if (! err)
+ err = argz_add (argz, argz_len,
+ dev->readonly ? "--readonly" : "--writable");
+
+ if (! err)
+ err = store_parsed_append_args (dev->store_name, argz, argz_len);
+
+ return err;
+}
+
+/* Called whenever a new lookup is done of our node. The only reason we
+ set this hook is to duplicate the check done normally done against
+ trivfs_allow_open in trivfs_S_fsys_getroot, but looking at the
+ per-device state. This gets checked again in check_open_hook, but this
+ hook runs before a little but more overhead gets incurred. In the
+ success case, we just return EAGAIN to have trivfs_S_fsys_getroot
+ continue with its generic processing. */
+static error_t
+getroot_hook (struct trivfs_control *cntl,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_port_type,
+ mach_port_t dotdot,
+ uid_t *uids, u_int nuids, uid_t *gids, u_int ngids,
+ int flags,
+ retry_type *do_retry, char *retry_name,
+ mach_port_t *node, mach_msg_type_name_t *node_type)
+{
+ struct dev *const dev = cntl->hook;
+ return (dev_is_readonly (dev) && (flags & O_WRITE)) ? EROFS : EAGAIN;
+}
+
+/* Called whenever someone tries to open our node (even for a stat). We
+ delay opening the kernel device until this point, as we can usefully
+ return errors from here. */
+static error_t
+check_open_hook (struct trivfs_control *trivfs_control,
+ struct iouser *user,
+ int flags)
+{
+ struct dev *const dev = trivfs_control->hook;
+ error_t err = 0;
+
+ if (!err && dev_is_readonly (dev) && (flags & O_WRITE))
+ return EROFS;
+
+ pthread_mutex_lock (&dev->lock);
+ if (dev->store == NULL)
+ {
+ /* Try and open the store. */
+ err = dev_open (dev);
+ if (err && (flags & (O_READ|O_WRITE)) == 0)
+ /* If we're not opening for read or write, then just ignore the
+ error, as this allows stat to work correctly. XXX */
+ err = 0;
+ }
+ pthread_mutex_unlock (&dev->lock);
+
+ return err;
+}
+
+static error_t
+open_hook (struct trivfs_peropen *peropen)
+{
+ error_t err = 0;
+ struct dev *const dev = peropen->cntl->hook;
+
+ if (dev->store)
+ {
+ pthread_mutex_lock (&dev->lock);
+ if (dev->nperopens++ == 0)
+ err = store_clear_flags (dev->store, STORE_INACTIVE);
+ pthread_mutex_unlock (&dev->lock);
+ if (!err)
+ err = open_create (dev, (struct open **)&peropen->hook);
+ }
+ return err;
+}
+
+static void
+close_hook (struct trivfs_peropen *peropen)
+{
+ struct dev *const dev = peropen->cntl->hook;
+
+ if (peropen->hook)
+ {
+ pthread_mutex_lock (&dev->lock);
+ if (--dev->nperopens == 0)
+ store_set_flags (dev->store, STORE_INACTIVE);
+ pthread_mutex_unlock (&dev->lock);
+ open_free (peropen->hook);
+ }
+}
+
+/* ---------------------------------------------------------------- */
+/* Trivfs hooks */
+
+int trivfs_fstype = FSTYPE_DEV;
+int trivfs_fsid = 0;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 1;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = O_READ | O_WRITE;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ struct dev *const dev = cred->po->cntl->hook;
+ struct open *open = cred->po->hook;
+
+ st->st_mode &= ~S_IFMT;
+
+ if (open)
+ /* An open device. */
+ {
+ struct store *store = open->dev->store;
+ store_offset_t size = store->size;
+
+ if (store->block_size > 1)
+ st->st_blksize = store->block_size;
+
+ st->st_size = size;
+ st->st_mode |= ((dev->inhibit_cache || store->block_size == 1)
+ ? S_IFCHR : S_IFBLK);
+ }
+ else
+ /* Try and do things without an open device... */
+ {
+ st->st_blksize = 0;
+ st->st_size = 0;
+
+ st->st_mode |= dev->inhibit_cache ? S_IFCHR : S_IFBLK;
+ }
+
+ st->st_rdev = dev->rdev;
+ if (dev_is_readonly (dev))
+ st->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ struct dev *const device = fsys->hook;
+ error_t err;
+ int force = (flags & FSYS_GOAWAY_FORCE);
+ int nosync = (flags & FSYS_GOAWAY_NOSYNC);
+ struct port_class *root_port_class = fsys->protid_class;
+
+ pthread_mutex_lock (&device->lock);
+
+ if (device->store == NULL)
+ /* The device is not actually open.
+ XXX note that exitting here nukes non-io users, like someone
+ in the middle of a stat who will get SIGLOST or something. */
+ exit (0);
+
+ /* Wait until all pending rpcs are done. */
+ err = ports_inhibit_class_rpcs (root_port_class);
+ if (err == EINTR || (err && !force))
+ {
+ pthread_mutex_unlock (&device->lock);
+ return err;
+ }
+
+ if (force && nosync)
+ /* Exit with extreme prejudice. */
+ exit (0);
+
+ if (!force && ports_count_class (root_port_class) > 0)
+ /* Still users, so don't exit. */
+ goto busy;
+
+ if (! nosync)
+ /* Sync the device here, if necessary, so that closing it won't result in
+ any I/O (which could get hung up trying to use one of our pagers). */
+ dev_sync (device, 1);
+
+ /* devpager_shutdown may sync the pagers as side-effect (if NOSYNC is 0),
+ so we put that first in this test. */
+ if (dev_stop_paging (device, nosync) || force)
+ /* Bye-bye. */
+ {
+ if (! nosync)
+ /* If NOSYNC is true, we don't close DEV, as that could cause data to
+ be written back. */
+ dev_close (device);
+ exit (0);
+ }
+
+ busy:
+ /* Allow normal operations to proceed. */
+ ports_enable_class (root_port_class);
+ ports_resume_class_rpcs (root_port_class);
+ pthread_mutex_unlock (&device->lock);
+
+ /* Complain that there are still users. */
+ return EBUSY;
+}
+
+/* If this variable is set, it is called by trivfs_S_fsys_getroot before any
+ other processing takes place; if the return value is EAGAIN, normal trivfs
+ getroot processing continues, otherwise the rpc returns with that return
+ value. */
+error_t (*trivfs_getroot_hook) (struct trivfs_control *cntl,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_port_type,
+ mach_port_t dotdot,
+ uid_t *uids, u_int nuids, uid_t *gids, u_int ngids,
+ int flags,
+ retry_type *do_retry, char *retry_name,
+ mach_port_t *node, mach_msg_type_name_t *node_type)
+ = getroot_hook;
+
+/* If this variable is set, it is called every time an open happens.
+ USER and FLAGS are from the open; CNTL identifies the
+ node being opened. This call need not check permissions on the underlying
+ node. If the open call should block, then return EWOULDBLOCK. Other
+ errors are immediately reflected to the user. If O_NONBLOCK
+ is not set in FLAGS and EWOULDBLOCK is returned, then call
+ trivfs_complete_open when all pending open requests for this
+ file can complete. */
+error_t (*trivfs_check_open_hook)(struct trivfs_control *trivfs_control,
+ struct iouser *user,
+ int flags)
+ = check_open_hook;
+
+/* If this variable is set, it is called every time a new peropen
+ structure is created and initialized. */
+error_t (*trivfs_peropen_create_hook)(struct trivfs_peropen *) = open_hook;
+
+/* If this variable is set, it is called every time a peropen structure
+ is about to be destroyed. */
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
+
+/* Sync this filesystem. */
+kern_return_t
+trivfs_S_fsys_syncfs (struct trivfs_control *cntl,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int wait, int dochildren)
+{
+ struct dev *dev = cntl->hook;
+ if (dev)
+ return dev_sync (dev, wait);
+ else
+ return 0;
+}
diff --git a/sutils/MAKEDEV.sh b/sutils/MAKEDEV.sh
new file mode 100644
index 00000000..0a8f5144
--- /dev/null
+++ b/sutils/MAKEDEV.sh
@@ -0,0 +1,231 @@
+#!/bin/sh
+#
+# Make standard devices
+#
+
+PATH=/bin:/usr/bin
+
+ECHO=: # Change to "echo" to echo commands.
+EXEC="" # Change to ":" to suppress command execution.
+DEVDIR=`pwd` # Reset below by -D/--devdir command line option.
+STFLAGS="-g" # Set to -k if active translators are to be kept.
+KEEP= # Set to something if existing files are to be left alone.
+USE_PARTSTORE= # Whether to use the newer part: stores
+
+while :; do
+ case "$1" in
+ --help|"-?")
+ echo "\
+Usage: $0 [OPTION...] DEVNAME...
+Make filesystem nodes for accessing standard system devices
+
+ -D, --devdir=DIR Use DIR when a device node name must be
+ embedded in a translator; default is the cwd
+ -k, --keep-active Leave any existing active translator running
+ -K, --keep-all Don't overwrite existing files
+ -p, --parted Prefer user-space parted stores to kernel devices
+ for partition devices
+ -n, --dry-run Don't actually execute any commands
+ -v, --verbose Show what commands are executed to make the devices
+ -?, --help Give this help list
+ --usage Give a short usage message
+ -V, --version Print program version"
+ exit 0;;
+ --devdir) DEVDIR="$2"; shift 2;;
+ --devdir=*) DEVDIR="`echo "$1" | sed 's/^--devdir=//'`"; shift 1;;
+ -D) DEVDIR="$2"; shift 2;;
+ -D*) DEVDIR="`echo "$1" | sed 's/^-D//'`"; shift 1;;
+ --keep-active|-k) STFLAGS="-k"; shift;;
+ --keep-all|-K) KEEP=1; shift;;
+ --parted|-p) USE_PARTSTORE=1; shift;;
+ --verbose|-v) ECHO=echo; shift;;
+ --dry-run|-n) EXEC=:; shift;;
+ -nv|-vn) ECHO=echo; EXEC=:; shift;;
+ --usage)
+ echo "Usage: $0 [-V?] [-D DIR] [--help] [--usage] [--version] [--parted]"
+ echo " [--devdir=DIR] [--keep-active] [--keep-all] DEVNAME..."
+ exit 0;;
+ --version|-V)
+ echo "STANDARD_HURD_VERSION_MAKEDEV_"; exit 0;;
+ -*)
+ echo 1>&2 "$0: unrecognized option \`$1'"
+ echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information";
+ exit 1;;
+ *)
+ break;;
+ esac
+done
+
+case "$#" in 0)
+ echo 1>&2 "Usage: $0 [OPTION...] DEVNAME..."
+ echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information"
+ exit 1;;
+esac
+
+cmd() {
+ eval $ECHO "$@"
+ eval $EXEC "$@"
+}
+
+st() {
+ local NODE="$1"
+ local OWNER="$2"
+ local PERM="$3"
+ shift 3
+ if [ "$KEEP" ] && showtrans "$NODE" > /dev/null 2>&1 ; then
+ return;
+ fi
+ if cmd settrans $STFLAGS -c "$NODE"; then
+ cmd chown "$OWNER" "$NODE"
+ cmd chmod "$PERM" "$NODE"
+ cmd settrans $STFLAGS "$NODE" "$@"
+ fi
+}
+
+lose() {
+ local line
+ for line; do
+ echo 1>&2 "$0: $line"
+ done
+ exit 1
+}
+
+mkdev() {
+ local I
+ for I; do
+ case $I in
+ /* | */*)
+ lose "Device names cannot contain directories" \
+ "Change to target directory and run $0 from there."
+ ;;
+
+ std)
+ mkdev console tty null zero full fd time mem klog shm
+ ;;
+ console|com[0-9])
+ st $I root 600 /hurd/term ${DEVDIR}/$I device $I;;
+ vcs)
+ st $I root 600 /hurd/console;;
+ tty[1-9][0-9]|tty[1-9])
+ st $I root 600 /hurd/term ${DEVDIR}/$I hurdio \
+ ${DEVDIR}/vcs/`echo $I | sed -e s/tty//`/console;;
+ lpr[0-9])
+ st $I root 660 /hurd/streamio "$I";;
+ null)
+ st $I root 666 /hurd/null;;
+ full)
+ st $I root 666 /hurd/null --full;;
+ zero)
+ st $I root 666 /bin/nullauth -- /hurd/storeio -Tzero;;
+ tty)
+ st $I root 666 /hurd/magic tty;;
+ fd)
+ st $I root 666 /hurd/magic --directory fd
+ cmd ln -f -s fd/0 stdin
+ cmd ln -f -s fd/1 stdout
+ cmd ln -f -s fd/2 stderr
+ ;;
+ 'time')
+ st $I root 644 /hurd/storeio --no-cache time ;;
+ mem)
+ st $I root 660 /hurd/storeio --no-cache mem ;;
+ klog)
+ st $I root 660 /hurd/streamio kmsg;;
+ # ptys
+ [pt]ty[pqrstuvwxyzPQRS]?)
+ # Make one pty, both the master and slave halves.
+ local id="${I#???}"
+ st pty$id root 666 /hurd/term ${DEVDIR}/pty$id \
+ pty-master ${DEVDIR}/tty$id
+ st tty$id root 666 /hurd/term ${DEVDIR}/tty$id \
+ pty-slave ${DEVDIR}/pty$id
+ ;;
+ [pt]ty[pqrstuvwxyzPQRS])
+ # Make a bunch of ptys.
+ local n
+ for n in 0 1 2 3 4 5 6 7 8 9 \
+ a b c d e f g h i j k l m n o p q r s t u v; do
+ mkdev ${I}${n}
+ done
+ ;;
+
+ fd*|mt*)
+ st $I root 640 /hurd/storeio $I
+ ;;
+
+ [hrsc]d*)
+ local sliceno=
+ local n="${I#?d}"
+ local major="${n%%[!0-9]*}"
+ if [ -z "$major" ]; then
+ lose "$I: Invalid device name: must supply a device number"
+ fi
+ local minor="${n##$major}"
+ case "$minor" in
+ '') ;; # Whole disk
+ [a-z]) ;; # BSD partition syntax, no slice syntax
+ s[1-9]*) # Slice syntax.
+ local slicestuff="${minor#s}"
+ local slice="${slicestuff%%[!0-9]*}"
+ local rest="${slicestuff##$slice}"
+ case "$slice" in
+ [1-9] | [1-9][0-9]) ;;
+ *)
+ lose "$I: Invalid slice number \`$slice'"
+ ;;
+ esac
+ case "$rest" in
+ '') # Whole slice, can use parted stores
+ sliceno=$slice
+ ;;
+ [a-z]) ;; # BSD partition after slice
+ *)
+ lose "$I: Invalid partition \`$rest'"
+ ;;
+ esac
+ ;;
+ *)
+ lose "$I: Invalid slice or partition syntax"
+ ;;
+ esac
+
+ # The device name passed all syntax checks, so finally use it!
+ if [ "$USE_PARTSTORE" ] && [ -z "$rest" ] && [ "$sliceno" ]; then
+ local dev=${I%s[0-9]*}
+ st $I root 640 /hurd/storeio -T typed part:$sliceno:device:$dev
+ else
+ st $I root 640 /hurd/storeio $I
+ fi
+ ;;
+
+ netdde)
+ st $I root 660 /hurd/netdde;;
+ eth*)
+ st $I root 660 /hurd/devnode -M /dev/netdde $I;;
+
+ # /dev/shm is used by the POSIX.1 shm_open call in libc.
+ # We don't want the underlying node to be written by randoms,
+ # but the filesystem presented should be writable by anyone
+ # and have the sticky bit set so others' files can't be removed.
+ # tmpfs requires an arbitrary size limitation here. To be like
+ # Linux, we tell tmpfs to set the size to half the physical RAM
+ # in the machine.
+ shm)
+ st $I root 644 /hurd/tmpfs --mode=1777 50%
+ ;;
+
+ # Linux compatibility
+ loop*)
+ # In Linux an inactive "/dev/loopN" device acts like /dev/null.
+ # The `losetup' script changes the translator to "activate" the device.
+ st $I root 640 /hurd/null
+ ;;
+
+ *)
+ lose "$I: Unknown device name"
+ ;;
+ esac
+ done
+}
+
+mkdev "$@"
diff --git a/sutils/Makefile b/sutils/Makefile
new file mode 100644
index 00000000..773a7dab
--- /dev/null
+++ b/sutils/Makefile
@@ -0,0 +1,43 @@
+# Makefile for sutils
+#
+# Copyright (C) 1996,97,99,2000,2010,2012 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+dir := sutils
+makemode := utilities
+
+progs = reboot halt fsck swapon swapoff
+scripts = e2os MAKEDEV losetup
+targets = $(special-targets) $(progs)
+special-targets = $(scripts)
+installationdir = $(sbindir)
+SRCS = $(progs:=.c) clookup.c fstab.c update.c $(scripts:=.sh)
+
+OBJS = $(progs:=.c)
+HURDLIBS = store shouldbeinlibc
+
+include ../Makeconf
+
+fsck: fstab.o clookup.o
+swapon swapoff: ../libstore/libstore.a default_pagerUser.o
+$(progs): %: %.o ../libshouldbeinlibc/libshouldbeinlibc.a
+
+install: $(prefix)/dev $(prefix)/dev/MAKEDEV
+$(prefix)/dev/MAKEDEV:
+ ln -s ../sbin/MAKEDEV $@
+$(prefix)/dev:
+ @$(MKINSTALLDIRS) $@
diff --git a/sutils/clookup.c b/sutils/clookup.c
new file mode 100644
index 00000000..0232b634
--- /dev/null
+++ b/sutils/clookup.c
@@ -0,0 +1,155 @@
+/* Careful filename lookup
+
+ Copyright (C) 1996, 1998, 1999, 2000 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <hurd.h>
+#include <hurd/lookup.h>
+#include <hurd/id.h>
+#include <hurd/fsys.h>
+
+
+/* This function is like file_name_lookup, but tries hard to avoid starting
+ any passive translators. If a node with an unstarted passive translator
+ is encountered, ENXIO is returned in ERRNO; other errors are as for
+ file_name_lookup. Note that checking for an active translator currently
+ requires fetching the control port, which is a privileged operation. */
+file_t
+file_name_lookup_carefully (const char *name, int flags, mode_t mode)
+{
+ error_t err;
+ file_t node;
+ uid_t *uids; /* Authentication of the current process. */
+ gid_t *gids;
+ size_t num_uids, num_gids;
+
+ /* Do the actual directory lookup. We only do the first pathname element
+ of NAME, appending the rest to any RETRY_NAME returned. We then make
+ sure the result node doesn't have a passive translator with no active
+ translator started (but we make an exception for symlinks) -- if it
+ does, we just return ENXIO. */
+ error_t lookup (file_t dir, char *name, int flags, mode_t mode,
+ retry_type *retry, string_t retry_name,
+ mach_port_t *node)
+ {
+ error_t err;
+ char *head, *tail;
+ char *slash = index (name, '/');
+
+ if (slash)
+ {
+ *stpncpy (head = alloca (slash - name + 1), name, slash - name) = 0;
+ tail = slash + 1;
+ }
+ else
+ {
+ head = name;
+ tail = 0;
+ }
+
+ err = dir_lookup (dir, head, flags | O_NOTRANS, mode,
+ retry, retry_name, node);
+ if (err)
+ return err;
+
+ if (*node != MACH_PORT_NULL
+ && (!(flags & O_NOTRANS) || tail || *retry_name))
+ /* The dir_lookup has returned a node to use for the next stage of
+ the lookup. Unless it's the last element of the path and FLAGS
+ has O_NOTRANS set (in which case we just return what we got as
+ is), we have to simulate the above lookup being done without
+ O_NOTRANS. Do this being careful not to start any translators. */
+ {
+ /* See if there's an active translator. */
+ fsys_t fsys; /* Active translator control port. */
+
+ err = file_get_translator_cntl (*node, &fsys);
+ if (! err)
+ /* There is! Get its root node to use as the actual file. */
+ {
+ file_t unauth_dir; /* DIR unauthenticated. */
+ err = io_restrict_auth (dir, &unauth_dir, 0, 0, 0, 0);
+ if (! err)
+ {
+ file_t old_node = *node;
+ err = fsys_getroot (fsys,
+ unauth_dir, MACH_MSG_TYPE_COPY_SEND,
+ uids, num_uids, gids, num_gids,
+ flags & ~O_NOTRANS, retry,
+ retry_name, node);
+ mach_port_deallocate (mach_task_self (), unauth_dir);
+ if (! err)
+ mach_port_deallocate (mach_task_self (), old_node);
+ }
+ mach_port_deallocate (mach_task_self (), fsys);
+ }
+
+ if (!err && tail)
+ /* Append TAIL to RETRY_NAME. */
+ {
+ size_t rtn_len = strlen (retry_name);
+ if (rtn_len + 1 + strlen (tail) + 1 > sizeof (string_t))
+ err = ENAMETOOLONG; /* Argh. Lovely string_t. */
+ else
+ {
+ if (rtn_len > 0 && retry_name[rtn_len - 1] != '/')
+ retry_name[rtn_len++] = '/';
+ strcpy (retry_name + rtn_len, tail);
+ }
+ }
+
+ if (err)
+ mach_port_deallocate (mach_task_self (), *node);
+ }
+
+ return err;
+ }
+
+ /* Fetch uids for use with fsys_getroot. */
+ num_uids = geteuids (0, 0);
+ if (num_uids < 0)
+ return errno;
+ uids = alloca (num_uids * sizeof (uid_t));
+ num_uids = geteuids (num_uids, uids);
+ if (num_uids < 0)
+ return errno;
+
+ /* ... and gids. */
+ num_gids = getgroups (0, 0);
+ if (num_gids < 0)
+ return errno;
+ gids = alloca (num_gids * sizeof (gid_t));
+ num_gids = getgroups (num_gids, gids);
+ if (num_gids < 0)
+ return errno;
+
+ /* Look things up ... */
+ err = hurd_file_name_lookup (&_hurd_ports_use, &getdport, lookup,
+ name, flags, mode & ~getumask (),
+ &node);
+
+ return err ? (__hurd_fail (err), MACH_PORT_NULL) : node;
+}
diff --git a/sutils/e2os.sh b/sutils/e2os.sh
new file mode 100755
index 00000000..60c9e017
--- /dev/null
+++ b/sutils/e2os.sh
@@ -0,0 +1,153 @@
+#!/bin/sh
+# Set/get the `creator_os' field of an ext2fs partition
+#
+# Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+#
+# Written by Miles Bader <miles@gnu.ai.mit.edu>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+USAGE="Usage: $0 DEVICE [OS]"
+
+DD=${DD-/bin/dd}
+OD=${OD-/bin/od}
+SED=${SED-/bin/sed}
+AWK=${AWK-/bin/gawk}
+
+# Hack to allow this script to work well under linux too.
+test ! -x "$OD" -a -x /usr/bin/od && OD=/usr/bin/od
+test ! -x "$AWK" -a -x /usr/bin/gawk && AWK=/usr/bin/gawk
+
+while :; do
+ case "$1" in
+ --help|"-?")
+ echo "$USAGE"
+ echo "Get or set the creator_os parameter of an ext2fs partition."
+ echo ""
+ echo " -?, --help Give this help list"
+ echo " --usage Give a short usage message"
+ echo " -V, --version Print program version"
+ exit 0;;
+ --usage)
+ echo "Usage: $0 [-V?] [--help] [--usage] [--version] DEVICE [OS]"
+ exit 0;;
+ --version|-V)
+ echo "STANDARD_HURD_VERSION_e2os_"; exit 0;;
+ -*)
+ echo 1>&2 "$0: unrecognized option \`$1'"
+ echo 1>&2 "Try \`$0 --help' for more information";
+ exit 1;;
+ *)
+ break;;
+ esac
+done
+
+case "$#" in 1 | 2) ;; # ok
+ *) echo 1>&2 "$USAGE"
+ echo 1>&2 "Try \`--help' for more information";
+ exit 1;;
+esac
+
+DEVICE="$1"; shift
+OS="$1"
+
+# Superblock fields (format is "BYTE_OFFS SIZE")
+SB_MAGIC="56 2"
+SB_OS="72 4"
+# Ext2fs magic numbers
+MAGIC_EXT2=ef53
+MAGIC_EXT2_OLD=ef53
+# Symbolic names for os types we know about
+OS_LINUX=0
+OS_HURD=1
+OS_MASIX=2
+OS_FREEBSD=3
+OS_LITES=4
+
+# Superblock
+SB=/tmp/,e2os-sb.$$
+
+# We have to store error output in a file so that we can filter it (for all
+# unix's stressing of pipelines, /bin/sh sure works hard to prevent you using
+# them).
+ERRS=/tmp/,e2os-errs.$$
+
+trap "/bin/rm -f $SB $ERRS" 0
+
+# Read the superblock
+$DD 2>"$ERRS" if="$DEVICE" of="$SB" bs=1k skip=1 count=1 \
+|| { $SED 1>&2 "s;^$DD:;$0:;" "$ERRS"; exit 2; }
+
+# Extract a word of SZ bytes from byte offset POS in the superblock
+# Optional arg FMT is what format to use (x = hex, d = decimal)
+sbget ()
+{
+ local pos="$1" sz="$2" fmt="${3-d}"
+ pos=$(($pos / $sz))
+ $DD 2>/dev/null if="$SB" bs="$sz" skip="$pos" count=1 \
+ | $OD -An -t"$fmt$sz" \
+ | $SED 's;^[ 0]*\(.\);\1;'
+}
+
+# Set a word of SZ bytes at byte offset POS in the superblock to VAL
+sbset ()
+{
+ local pos="$1" sz="$2" val="$3"
+ pos=$(($pos / $sz))
+ echo "$val" \
+ | $AWK '{ n=$1+0; printf ("%c%c%c%c", n, n/256, n/(2^16), n/(2^24)); }' \
+ | $DD 2>/dev/null of="$SB" bs="$sz" seek="$pos" count=1 conv=notrunc
+}
+
+# Check the magic number
+magic="`sbget $SB_MAGIC x`"
+case "$magic" in
+ $MAGIC_EXT2) ;; # ok
+ $MAGIC_EXT2_OLD) echo "$0: $DEVICE: Old-format ext2 filesystem"; exit 3;;
+ *) echo "$0: $DEVICE: Not an ext2 filesystem (magic = 0x$magic)"; exit 4;;
+esac
+
+if test "$OS"; then
+ # Set the os field
+ case "$OS" in
+ linux) OS=$OS_LINUX;;
+ hurd) OS=$OS_HURD;;
+ masix) OS=$OS_MASIX;;
+ freebsd) OS=$OS_FREEBSD;;
+ lites) OS=$OS_LITES;;
+ "*[!0-9]*")
+ echo 1>&2 "$0: $OS: Unknown ext2 creator_os value"; exit 5;;
+ esac
+
+ # Frob the superlock
+ sbset $SB_OS "$OS"
+
+ # Write the superblock
+ $DD 2>"$ERRS" if="$SB" of="$DEVICE" bs=1k seek=1 count=1 conv=notrunc \
+ || { $SED 1>&2 "s;^$DD:;$0:;" "$ERRS"; exit 6; }
+else
+ # Print the os field.
+ OS="`sbget $SB_OS`"
+ case "$OS" in
+ "") exit 2;;
+ $OS_LINUX) OS=linux;;
+ $OS_HURD) OS=hurd;;
+ $OS_MASIX) OS=masix;;
+ $OS_FREEBSD) OS=freebsd;;
+ $OS_LITES) OS=lites;;
+ esac
+ echo "$OS"
+fi
diff --git a/sutils/fsck.c b/sutils/fsck.c
new file mode 100644
index 00000000..1ab9caa5
--- /dev/null
+++ b/sutils/fsck.c
@@ -0,0 +1,568 @@
+/* Hurd-aware fsck wrapper
+
+ Copyright (C) 1996, 97, 98, 99 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This wrapper runs other file-system specific fsck programs. They are
+ expected to accept at least the following options:
+
+ -p Terse automatic mode
+ -y Automatically answer yes to all questions
+ -n Automatically answer no to all questions
+ -f Check even if clean
+ -s Only print diagostic messages
+
+ They should also return exit-status codes as following:
+
+ 0 Filesystem was clean
+ 1,2 Filesystem fixed (and is now clean)
+ 4,8 Filesystem was broken, but couldn't be fixed
+ ... Anything else is assumed be some horrible error
+
+ The exit-status from this wrapper is the greatest status returned from any
+ individual fsck.
+
+ Although it knows something about the hurd, this fsck still uses
+ /etc/fstab, and is generally not very integrated. That will have to wait
+ until the appropriate mechanisms for doing so are decided. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <error.h>
+#include <argp.h>
+#include <argz.h>
+#include <assert.h>
+#include <version.h>
+
+#include "fstab.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (fsck);
+
+
+/* for debugging */
+static int _debug = 0;
+#define debug(fmt, args...) \
+ do { if (_debug) { \
+ fprintf (stderr, "[%s: ", __FUNCTION__); \
+ fprintf (stderr, fmt , ##args); \
+ fprintf (stderr, "]\n"); } } while (0)
+#define fs_debug(fs, fmt, args...) \
+ debug ("%s: " fmt, (fs)->mntent.mnt_dir , ##args)
+
+#define FSCK_SEARCH_FMTS "/sbin/fsck.%s"
+
+/* Exit codes we return. */
+#define FSCK_EX_OK 0 /* No errors */
+#define FSCK_EX_FIXED 1 /* File system errors corrected */
+#define FSCK_EX_BROKEN 4 /* File system errors left uncorrected */
+#define FSCK_EX_QUIT 12 /* Got SIGQUIT */
+#define FSCK_EX_SIGNAL 20 /* Signalled (not SIGQUIT) */
+#define FSCK_EX_ERROR 50
+#define FSCK_EX_EXEC 99 /* Exec failed */
+/* Everything else is some sort of fsck problem. */
+
+/* Things we know about what child fsck's might return. */
+#define FSCK_EX_IS_FIXED(st) ({ int _st = (st); _st >= 1 || _st <= 2; })
+#define FSCK_EX_IS_BROKEN(st) ({ int _st = (st); _st >= 4 || _st <= 8; })
+
+/* Common fsck flags. */
+#define FSCK_F_PREEN 0x1
+#define FSCK_F_YES 0x2
+#define FSCK_F_NO 0x4
+#define FSCK_F_FORCE 0x8
+#define FSCK_F_SILENT 0x10
+
+/* The following are only used internally. */
+#define FSCK_F_VERBOSE 0x100
+#define FSCK_F_WRITABLE 0x200 /* Make writable after fscking. */
+#define FSCK_F_AUTO 0x400 /* Do all filesystems in fstab. */
+#define FSCK_F_DRYRUN 0x800 /* Don't actually do anything. */
+
+static int got_sigquit = 0, got_sigint = 0;
+
+static void sigquit ()
+{
+ got_sigquit = 1;
+}
+
+static void sigint ()
+{
+ got_sigint = 1;
+}
+
+struct fsck
+{
+ struct fs *fs; /* Filesystem being fscked. */
+ int pid; /* Pid for process. */
+ int make_writable; /* Make writable after fscking if possible. */
+ struct fsck *next, **self;
+};
+
+struct fscks
+{
+ struct fsck *running; /* Fsck processes now running. */
+ int free_slots; /* Number of fsck processes we can start. */
+ int flags;
+};
+
+/* Starts FS's fsck program on FS's device, returning the pid of the process.
+ If an error is encountered, prints an error message and returns 0.
+ Filesystems that need not be fscked at all also return 0 (but don't print
+ an error message). */
+static pid_t
+fs_start_fsck (struct fs *fs, int flags)
+{
+ pid_t pid;
+ char flags_buf[10];
+ char *argv[4], **argp = argv;
+ struct fstype *type;
+ error_t err = fs_type (fs, &type);
+
+ assert_perror (err); /* Should already have been checked for. */
+ assert (type->program);
+
+ *argp++ = type->program;
+
+ if (flags & (FSCK_F_PREEN|FSCK_F_YES|FSCK_F_NO|FSCK_F_FORCE|FSCK_F_SILENT))
+ {
+ char *p = flags_buf;
+ *argp++ = flags_buf;
+ *p++ = '-';
+ if (flags & FSCK_F_PREEN) *p++ = 'p';
+ if (flags & FSCK_F_YES) *p++ = 'y';
+ if (flags & FSCK_F_NO) *p++ = 'n';
+ if (flags & FSCK_F_FORCE) *p++ = 'f';
+ if (flags & FSCK_F_SILENT) *p++ = 's';
+ *p = '\0';
+ }
+
+ *argp++ = fs->mntent.mnt_fsname;
+ *argp = 0;
+
+ if (flags & FSCK_F_DRYRUN)
+ {
+ char *argz;
+ size_t argz_len;
+ argz_create (argv, &argz, &argz_len);
+ argz_stringify (argz, argz_len, ' ');
+ puts (argz);
+ free (argz);
+ return 0;
+ }
+
+ pid = fork ();
+ if (pid < 0)
+ {
+ error (0, errno, "fork");
+ return 0;
+ }
+
+ if (pid == 0)
+ /* Child. */
+ {
+ execv (type->program, argv);
+ exit (FSCK_EX_EXEC); /* Exec failed. */
+ }
+
+ if ((flags & FSCK_F_VERBOSE) || _debug)
+ {
+ char *argz;
+ size_t argz_len;
+ argz_create (argv, &argz, &argz_len);
+ argz_stringify (argz, argz_len, ' ');
+ fs_debug (fs, "Spawned pid %d: %s", pid, argz);
+ if (flags & FSCK_F_VERBOSE)
+ puts (argz);
+ free (argz);
+ }
+
+ return pid;
+}
+
+/* Start a fsck process for FS running, and add an entry for it to FSCKS.
+ This also ensures that if FS is currently mounted, it will be made
+ readonly first. If the fsck is successfully started, 0 is returned,
+ otherwise FSCK_EX_ERROR. */
+static int
+fscks_start_fsck (struct fscks *fscks, struct fs *fs)
+{
+ error_t err;
+ int mounted, make_writable;
+ struct fsck *fsck;
+
+ if (got_sigint)
+ /* We got SIGINT, so we pretend that all fscks got a signal without even
+ attempting to run them. */
+ {
+ fs_debug (fs, "Forcing signal");
+ return FSCK_EX_SIGNAL;
+ }
+
+#define CK(err, fmt, args...) \
+ do { if (err) { error (0, err, fmt , ##args); return FSCK_EX_ERROR; } } while (0)
+
+ fs_debug (fs, "Checking mounted state");
+ err = fs_mounted (fs, &mounted);
+ CK (err, "%s: Cannot check mounted state", fs->mntent.mnt_dir);
+
+ if (mounted)
+ {
+ int readonly;
+
+ fs_debug (fs, "Checking readonly state");
+ err = fs_readonly (fs, &readonly);
+ CK (err, "%s: Cannot check readonly state", fs->mntent.mnt_dir);
+
+ if (fscks->flags & FSCK_F_DRYRUN)
+ {
+ if (! readonly)
+ {
+ printf ("%s: writable filesystem %s would be made read-only\n",
+ program_invocation_name, fs->mntent.mnt_dir);
+ readonly = 1;
+ }
+ }
+
+ if (! readonly)
+ {
+ fs_debug (fs, "Making readonly");
+ err = fs_set_readonly (fs, 1);
+ CK (err, "%s: Cannot make readonly", fs->mntent.mnt_dir);
+ }
+
+ make_writable = !readonly
+ || ((fscks->flags & FSCK_F_WRITABLE) && hasmntopt (&fs->mntent, "rw"));
+ if (make_writable)
+ {
+ fs_debug (fs, "Will make writable after fscking if possible");
+ make_writable = 1;
+ }
+ }
+ else
+ make_writable = 0;
+
+#undef CK
+
+ /* Ok, any mounted filesystem is safely readonly. */
+
+ fsck = malloc (sizeof (struct fsck));
+ if (! fsck)
+ {
+ error (0, ENOMEM, "malloc");
+ return FSCK_EX_ERROR;
+ }
+
+ fsck->fs = fs;
+ fsck->make_writable = make_writable;
+ fsck->next = fscks->running;
+ if (fsck->next)
+ fsck->next->self = &fsck->next;
+ fsck->self = &fscks->running;
+ fsck->pid = fs_start_fsck (fs, fscks->flags);
+ fscks->running = fsck;
+
+ if (fsck->pid)
+ fscks->free_slots--;
+
+ return 0;
+}
+
+/* Cleanup after fscking with FSCK. If REMOUNT is true, ask the filesystem
+ to remount itself (to incorporate changes made by the fsck program). If
+ MAKE_WRITABLE is true, then if the filesystem should be made writable, do
+ so (after remounting if applicable). */
+static void
+fsck_cleanup (struct fsck *fsck, int remount, int make_writable)
+{
+ error_t err = 0;
+ struct fs *fs = fsck->fs;
+
+ /* Remove from chain. */
+ *fsck->self = fsck->next;
+ if (fsck->next)
+ fsck->next->self = fsck->self;
+
+ fs_debug (fs, "Cleaning up after fsck (remount = %d, make_writable = %d)",
+ remount, make_writable);
+
+ if (fs->mounted > 0)
+ /* It's currently mounted; if the fsck modified the device, tell the
+ running filesystem to remount it. Also we may make it writable. */
+ {
+ if (remount)
+ {
+ fs_debug (fs, "Remounting");
+ err = fs_remount (fs);
+ if (err)
+ error (0, err, "%s: Cannot remount", fs->mntent.mnt_dir);
+ }
+ if (!err && make_writable && fsck->make_writable)
+ {
+ fs_debug (fs, "Making writable");
+ err = fs_set_readonly (fs, 0);
+ if (err)
+ error (0, err, "%s: Cannot make writable", fs->mntent.mnt_dir);
+ }
+ }
+
+ free (fsck);
+}
+
+/* Wait for some fsck process to exit, cleaning up after it, and return its
+ exit-status. */
+static int
+fscks_wait (struct fscks *fscks)
+{
+ pid_t pid;
+ int wstatus, status;
+ struct fsck *fsck, *next;
+
+ /* Cleanup fscks that didn't even start. */
+ for (fsck = fscks->running; fsck; fsck = next)
+ {
+ next = fsck->next;
+ if (fsck->pid == 0)
+ {
+ fs_debug (fsck->fs, "Pruning failed fsck");
+ fsck_cleanup (fsck, 0, 1);
+ }
+ }
+
+ debug ("Waiting...");
+
+ do
+ pid = wait (&wstatus);
+ while (pid < 0 && errno == EINTR);
+
+ if (pid > 0)
+ {
+ if (WIFEXITED (wstatus))
+ status = WEXITSTATUS (wstatus);
+ else if (WIFSIGNALED (wstatus))
+ status = FSCK_EX_SIGNAL;
+ else
+ status = FSCK_EX_ERROR;
+
+ for (fsck = fscks->running; fsck; fsck = fsck->next)
+ if (fsck->pid == pid)
+ {
+ int remount = (status != 0);
+ int make_writable = (status == 0 || FSCK_EX_IS_FIXED (status));
+ fs_debug (fsck->fs, "Fsck finished (status = %d)", status);
+ fsck_cleanup (fsck, remount, make_writable);
+ fscks->free_slots++;
+ break;
+ }
+ if (! fsck)
+ error (0, 0, "%d: Unknown process exited", pid);
+ }
+ else if (errno == ECHILD)
+ /* There are apparently no child processes left, and we weren't told of
+ their demise. This can't happen. */
+ {
+ while (fscks->running)
+ {
+ error (0, 0, "%s: Fsck process disappeared!",
+ fscks->running->fs->mntent.mnt_fsname);
+ /* Be pessimistic -- remount the filesystem, but leave it
+ readonly. */
+ fsck_cleanup (fscks->running, 1, 0);
+ fscks->free_slots++;
+ }
+ status = FSCK_EX_ERROR;
+ }
+ else
+ status = FSCK_EX_ERROR; /* What happened? */
+
+ return status;
+}
+
+/* Fsck all the filesystems in FSTAB, with the flags in FLAGS, doing at most
+ MAX_PARALLEL parallel fscks. The greatest exit code returned by any one
+ fsck is returned. */
+static int
+fsck (struct fstab *fstab, int flags, int max_parallel)
+{
+ int pass;
+ struct fs *fs;
+ int autom = (flags & FSCK_F_AUTO);
+ int summary_status = 0;
+ struct fscks fscks = { running: 0, flags: flags };
+
+ void merge_status (int status)
+ {
+ if (status > summary_status)
+ summary_status = status;
+ }
+
+ /* Do in pass order; pass 0 is never run, it is reserved for "off". */
+ for (pass = 1; pass > 0; pass = fstab_next_pass (fstab, pass))
+ /* Submit all filesystems in the given pass, up to MAX_PARALLEL at a
+ time. There should currently be no fscks running. */
+ {
+ debug ("Pass %d", pass);
+
+ fscks.free_slots = max_parallel;
+
+ /* Try and fsck every filesystem in this pass. */
+ for (fs = fstab->entries; fs; fs = fs->next)
+ if (fs->mntent.mnt_passno == pass)
+ /* FS is applicable for this pass. */
+ {
+ struct fstype *type;
+ error_t err = fs_type (fs, &type);
+
+ if (err)
+ {
+ error (0, err, "%s: Cannot find fsck program (type %s)",
+ fs->mntent.mnt_dir, fs->mntent.mnt_type);
+ merge_status (FSCK_EX_ERROR);
+ }
+ else if (type->program)
+ /* This is a fsckable filesystem. */
+ {
+ fs_debug (fs, "Fsckable; free_slots = %d", fscks.free_slots);
+ while (fscks.free_slots == 0)
+ /* No room; wait for another fsck to finish. */
+ merge_status (fscks_wait (&fscks));
+ merge_status (fscks_start_fsck (&fscks, fs));
+ }
+ else if (autom)
+ fs_debug (fs, "Not fsckable");
+ else
+ error (0, 0, "%s: %s: Not a fsckable filesystem type",
+ fs->mntent.mnt_dir, fs->mntent.mnt_type);
+ }
+
+ /* Now wait for them all to finish. */
+ while (fscks.running)
+ merge_status (fscks_wait (&fscks));
+ }
+
+ return summary_status;
+}
+
+static const struct argp_option options[] =
+{
+ {"preen", 'p', 0, 0, "Terse automatic mode", 1},
+ {"yes", 'y', 0, 0, "Automatically answer yes to all questions"},
+ {"no", 'n', 0, 0, "Automatically answer no to all questions"},
+ {"parallel", 'l', "NUM", 0, "Limit the number of parallel checks to NUM"},
+ {"verbose", 'v', 0, 0, "Print informational messages"},
+ {"writable", 'w', 0, 0,
+ "Make RW filesystems writable after fscking, if possible"},
+ {"debug", 'D', 0, OPTION_HIDDEN },
+ {"force", 'f', 0, 0, "Check even if clean"},
+
+ {"dry-run", 'N', 0, 0, "Don't check, just show what would be done"},
+ {0, 0, 0, 0, "In --preen mode, the following also apply:", 2},
+ {"silent", 's', 0, 0, "Print only diagnostic messages"},
+ {"quiet", 'q', 0, OPTION_ALIAS | OPTION_HIDDEN },
+ {0, 0}
+};
+static const char doc[] = "Filesystem consistency check and repair";
+static const char args_doc[] = "[ DEVICE|FSYS... ]";
+
+
+int
+main (int argc, char **argv)
+{
+ struct fstab *check;
+ int status; /* exit status */
+ int flags = 0;
+ int max_parallel = -1; /* -1 => use default */
+
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ struct fstab_argp_params *params = state->input;
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = params; /* pass down to fstab_argp parser */
+ break;
+ case 'p': flags |= FSCK_F_PREEN; break;
+ case 'y': flags |= FSCK_F_YES; break;
+ case 'n': flags |= FSCK_F_NO; break;
+ case 'f': flags |= FSCK_F_FORCE; break;
+ case 's': flags |= FSCK_F_SILENT; break;
+ case 'v': flags |= FSCK_F_VERBOSE; break;
+ case 'w': flags |= FSCK_F_WRITABLE; break;
+ case 'N': flags |= FSCK_F_DRYRUN; break;
+ case 'D': _debug = 1; break;
+ case 'l':
+ max_parallel = atoi (arg);
+ if (max_parallel < 1)
+ argp_error (state, "%s: Invalid value for --max-parallel", arg);
+ break;
+ case ARGP_KEY_NO_ARGS:
+ if (flags & FSCK_F_PREEN)
+ params->do_all = 1;
+ else if (!params->do_all)
+ {
+ argp_usage (state);
+ return EINVAL;
+ }
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ static const struct argp_child kids[] =
+ { { &fstab_argp, 0,
+ "Filesystem selection (default is all in " _PATH_MNTTAB "):", 2 },
+ { 0 } };
+ struct argp argp = { options, parse_opt, args_doc, doc, kids };
+ struct fstab_argp_params fstab_params;
+
+ argp_parse (&argp, argc, argv, 0, 0, &fstab_params);
+
+ check = fstab_argp_create (&fstab_params,
+ FSCK_SEARCH_FMTS, sizeof FSCK_SEARCH_FMTS);
+ if (fstab_params.do_all)
+ flags |= FSCK_F_AUTO;
+
+ if (max_parallel <= 0)
+ {
+ if (flags & FSCK_F_PREEN)
+ max_parallel = 100; /* In preen mode, do lots in parallel. */
+ else
+ max_parallel = 1; /* Do one at a time to keep output rational. */
+ }
+
+ /* If the user send a SIGQUIT (usually ^\), then do all checks, but
+ regardless of their outcome, return a status that will cause the
+ automatic reboot to stop after fscking is complete. */
+ signal (SIGQUIT, sigquit);
+
+ /* Let currently running fscks complete (each such program can handle
+ signals as it sees fit), and cause not-yet-run fscks to act as if they
+ got a signal. */
+ signal (SIGINT, sigint);
+
+ debug ("Fscking...");
+ status = fsck (check, flags, max_parallel);
+ if (got_sigquit && status < FSCK_EX_QUIT)
+ status = FSCK_EX_QUIT;
+
+ exit (status);
+}
diff --git a/sutils/fstab.c b/sutils/fstab.c
new file mode 100644
index 00000000..ed591519
--- /dev/null
+++ b/sutils/fstab.c
@@ -0,0 +1,943 @@
+/* Fstab filesystem frobbing
+
+ Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <error.h>
+#include <argz.h>
+#include <argp.h>
+#include <fnmatch.h>
+
+#include <hurd/fsys.h>
+
+#include "fstab.h"
+
+extern error_t fsys_set_readonly (fsys_t fsys, int readonly);
+extern error_t fsys_get_readonly (fsys_t fsys, int *readonly);
+extern error_t fsys_update (fsys_t fsys);
+
+extern file_t file_name_lookup_carefully (const char *file,
+ int flags, mode_t mode);
+
+/* Return a new fstab in FSTAB. */
+error_t
+fstab_create (struct fstypes *types, struct fstab **fstab)
+{
+ struct fstab *new = malloc (sizeof (struct fstab));
+ if (new)
+ {
+ new->entries = 0;
+ new->types = types;
+ *fstab = new;
+ return 0;
+ }
+ else
+ return ENOMEM;
+}
+
+/* Free FSTAB and all of its entries. */
+void
+fstab_free (struct fstab *fstab)
+{
+ while (fstab->entries)
+ fs_free (fstab->entries);
+ free (fstab);
+}
+
+/* Return a new fstypes structure in TYPES. SEARCH_FMTS is copied. */
+error_t
+fstypes_create (const char *search_fmts, size_t search_fmts_len,
+ struct fstypes **types)
+{
+ struct fstypes *new = malloc (sizeof (struct fstypes));
+ if (new)
+ {
+ new->entries = 0;
+ new->program_search_fmts = malloc (search_fmts_len);
+ new->program_search_fmts_len = search_fmts_len;
+ if (! new->program_search_fmts)
+ {
+ free (types);
+ return ENOMEM;
+ }
+ bcopy (search_fmts, new->program_search_fmts, search_fmts_len);
+ *types = new;
+ return 0;
+ }
+ else
+ return ENOMEM;
+}
+
+/* Return an fstype entry in TYPES called NAME, in FSTYPE. If there is no
+ existing entry, an attempt to find a fsck program with the given type,
+ using the alternatives in the FSCK_SEARCH_FMTS field in TYPES. If
+ one is found, it is added to TYPES, otherwise an new entry is created
+ with a NULL PROGRAM field. */
+error_t
+fstypes_get (struct fstypes *types, const char *name, struct fstype **fstype)
+{
+ char *fmts, *fmt;
+ size_t fmts_len;
+ struct fstype *type;
+ char *program = 0;
+
+ for (type = types->entries; type; type = type->next)
+ if (strcasecmp (type->name, name) == 0)
+ {
+ *fstype = type;
+ return 0;
+ }
+
+ /* No existing entry, make a new one. */
+
+ fmts = types->program_search_fmts;
+ fmts_len = types->program_search_fmts_len;
+
+ for (fmt = fmts; fmt; fmt = argz_next (fmts, fmts_len, fmt))
+ {
+ int fd;
+
+ asprintf (&program, fmt, name);
+ fd = open (program, O_EXEC);
+ if (fd < 0)
+ {
+ free (program); /* Failed. */
+ if (errno != ENOENT && errno != EACCES)
+ /* The program's there but something went wrong; fail. */
+ return errno;
+ }
+ else
+ /* We can open for exec, but check the stat info too (e.g. root can
+ open everything). */
+ {
+ struct stat stat;
+ int rv = fstat (fd, &stat);
+
+ close (fd);
+
+ if (rv < 0)
+ {
+ free (program);
+ return errno;
+ }
+
+ if (stat.st_mode & S_IXUSR)
+ /* Yup execute bit is set. This must be a program... */
+ break;
+
+ free (program);
+ }
+
+ program = 0;
+ }
+
+ type = malloc (sizeof (struct fstype));
+ if (! type)
+ {
+ free (program);
+ return ENOMEM;
+ }
+
+ type->name = strdup (name);
+ if (type->name == 0)
+ {
+ free (type);
+ return ENOMEM;
+ }
+ type->program = program;
+ type->next = types->entries;
+ types->entries = type;
+
+ *fstype = type;
+
+ return 0;
+}
+
+#if 0
+/* XXX nice idea, but not that useful since scanf's %s always eats all
+ non-ws, and it seems a bit overkill to convert it to a .+ regexp match */
+error_t
+fstypes_find_program (struct fstypes *types, const char *program,
+ struct fstype **fstype)
+{
+ char *fmts, *fmt;
+ size_t fmts_len;
+ struct fstype *type;
+ char *typename;
+
+ /* First see if a known type matches this program. */
+ for (type = types->entries; type; type = type->next)
+ if (type->program && !strcmp (type->program, program))
+ {
+ *fstype = type;
+ return 0;
+ }
+
+ /* No existing entry, see if we can make a new one. */
+
+ typename = alloca (strlen (program) + 1);
+
+ fmts = types->program_search_fmts;
+ fmts_len = types->program_search_fmts_len;
+ for (fmt = fmts; fmt; fmt = argz_next (fmts, fmts_len, fmt))
+ /* XXX this only works for trailing %s */
+ if (sscanf (program, fmt, typename) == 1)
+ {
+ /* This format matches the program and yields the type name.
+ Create a new entry for this type. */
+
+ type = malloc (sizeof (struct fstype));
+ if (! type)
+ return ENOMEM;
+ type->name = strdup (typename);
+ if (type->name == 0)
+ {
+ free (type);
+ return ENOMEM;
+ }
+ type->program = strdup (program);
+ if (type->program == 0)
+ {
+ free (type->name);
+ free (type);
+ return ENOMEM;
+ }
+ type->next = types->entries;
+ types->entries = type;
+
+ *fstype = type;
+ return 0;
+ }
+
+ /* We could find no program search format that could have yielded this
+ program name. */
+ *fstype = 0;
+ return 0;
+}
+#endif
+
+/* Copy MNTENT into FS, copying component strings as well. */
+error_t
+fs_set_mntent (struct fs *fs, const struct mntent *mntent)
+{
+ char *end;
+ size_t needed = 0;
+
+ if (fs->storage)
+ free (fs->storage);
+
+ /* Allocate space for all string mntent fields in FS. */
+#define COUNT(field) if (mntent->field) needed += strlen (mntent->field) + 1;
+ COUNT (mnt_fsname);
+ COUNT (mnt_dir);
+ COUNT (mnt_type);
+ COUNT (mnt_opts);
+#undef COUNT
+
+ fs->storage = malloc (needed);
+ if (! fs->storage)
+ return ENOMEM;
+
+ if (!fs->mntent.mnt_dir || !mntent->mnt_dir
+ || strcmp (fs->mntent.mnt_dir, mntent->mnt_dir) != 0)
+ {
+ fs->mounted = fs->readonly = -1;
+ if (fs->fsys != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), fs->fsys);
+ fs->fsys = MACH_PORT_NULL;
+ }
+
+ /* Copy MNTENT into FS; string-valued fields will be fixed up next. */
+ fs->mntent = *mntent;
+
+ /* Copy each mntent field from MNTENT into FS's version. */
+ end = fs->storage;
+#define STORE(field) \
+ if (mntent->field) \
+ { \
+ fs->mntent.field = end; \
+ end = stpcpy (end, mntent->field) + 1; \
+ } \
+ else \
+ fs->mntent.field = 0;
+ STORE (mnt_fsname);
+ STORE (mnt_dir);
+ STORE (mnt_type);
+ STORE (mnt_opts);
+#undef STORE
+
+ if (fs->type
+ && (!mntent->mnt_type
+ || strcasecmp (fs->type->name, mntent->mnt_type) != 0))
+ fs->type = 0; /* Type is different. */
+
+ return 0;
+}
+
+/* Returns an fstype for FS in TYPE, trying to fill in FS's type field if
+ necessary. */
+error_t
+fs_type (struct fs *fs, struct fstype **type)
+{
+ error_t err = 0;
+ if (! fs->type)
+ err = fstypes_get (fs->fstab->types, fs->mntent.mnt_type, &fs->type);
+ if (! err)
+ *type = fs->type;
+ return err;
+}
+
+/* Looks to see if FS is currently mounted, being very careful to avoid
+ mounting anything that's not already, and fills in the fsys & mounted
+ fields in FS. */
+static error_t
+_fs_check_mounted (struct fs *fs)
+{
+ error_t err = 0;
+
+ if (fs->mounted < 0)
+ /* The mounted field in FS is -1 if we're not sure. */
+ {
+ if (fs->fsys != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), fs->fsys);
+
+ if (strcmp (fs->mntent.mnt_dir, "/") == 0)
+ /* The root is always mounted. Get its control port. */
+ {
+ file_t root = getcrdir ();
+ if (root == MACH_PORT_NULL)
+ err = errno;
+ else
+ {
+ err = file_getcontrol (root, &fs->fsys);
+ mach_port_deallocate (mach_task_self (), root);
+ }
+ }
+ else
+ {
+ file_t mount_point =
+ file_name_lookup_carefully (fs->mntent.mnt_dir, O_NOTRANS, 0);
+
+ if (mount_point != MACH_PORT_NULL)
+ /* The node exists. Is it the root of an active translator?
+ [Note that it could be a different translator than the one in
+ the mntent, but oh well, nothing we can do about that.] */
+ {
+ err = file_get_translator_cntl (mount_point, &fs->fsys);
+ if (err == EINVAL || err == EOPNOTSUPP || err == ENXIO)
+ /* Either the mount point doesn't exist, or wasn't mounted. */
+ {
+ fs->fsys = MACH_PORT_NULL;
+ err = 0;
+ }
+ }
+ else if (errno == ENXIO)
+ /* Ran into an inactive passive translator. FS can't be mounted. */
+ {
+ fs->fsys = MACH_PORT_NULL;
+ err = 0;
+ }
+ }
+
+ if (! err)
+ fs->mounted = (fs->fsys != MACH_PORT_NULL);
+ }
+
+ return err;
+}
+
+/* Looks to see if FS is currently mounted, being very careful to avoid
+ mounting anything that's not already, and returns the control port for the
+ mounted filesystem (MACH_PORT_NULL if not mounted). */
+error_t
+fs_fsys (struct fs *fs, fsys_t *fsys)
+{
+ error_t err = _fs_check_mounted (fs);
+ if (!err && fsys)
+ *fsys = fs->fsys;
+ return err;
+}
+
+/* Looks to see if FS is currently mounted, being very careful to avoid
+ mounting anything that's not already, and returns the boolean MOUNTED. */
+error_t
+fs_mounted (struct fs *fs, int *mounted)
+{
+ error_t err = _fs_check_mounted (fs);
+ if (!err && mounted)
+ *mounted = fs->mounted;
+ return err;
+}
+
+/* Looks to see if FS is currently mounted readonly, being very careful to
+ avoid mounting anything that's not already, and returns the boolean
+ READONLY. If FS isn't mounted at all, READONLY is set to 1 (it's not
+ going to write anything!). */
+error_t
+fs_readonly (struct fs *fs, int *readonly)
+{
+ error_t err = 0;
+
+ if (fs->readonly < 0)
+ /* Unknown. */
+ {
+ fsys_t fsys;
+
+ err = fs_fsys (fs, &fsys);
+ if (! err)
+ {
+ if (fsys == MACH_PORT_NULL)
+ fs->readonly = 1;
+ else
+ err = fsys_get_readonly (fsys, &fs->readonly);
+ }
+ }
+
+ if (!err && readonly)
+ *readonly = fs->readonly;
+
+ return err;
+}
+
+/* If FS is currently mounted writable, try to make it readonly. XXX If FS
+ is not mounted at all, then nothing is done. */
+error_t
+fs_set_readonly (struct fs *fs, int readonly)
+{
+ int currently_readonly;
+ error_t err = fs_readonly (fs, &currently_readonly);
+
+ readonly = !!readonly;
+
+ if (!err && readonly != currently_readonly)
+ /* We have to try and change the readonly state. */
+ {
+ fsys_t fsys;
+ err = fs_fsys (fs, &fsys);
+ if (!err && fsys != MACH_PORT_NULL) /* XXX What to do if not mounted? */
+ err = fsys_set_readonly (fsys, readonly);
+ if (! err)
+ fs->readonly = readonly;
+ }
+
+ return err;
+}
+
+/* If FS is currently mounted tell it to remount the device. XXX If FS is
+ not mounted at all, then nothing is done. */
+error_t
+fs_remount (struct fs *fs)
+{
+ fsys_t fsys;
+ error_t err = fs_fsys (fs, &fsys);
+ if (!err && fsys != MACH_PORT_NULL) /* XXX What to do if not mounted? */
+ err = fsys_update (fsys);
+ return err;
+}
+
+/* Returns the FS entry in FSTAB with the device field NAME.
+
+ In general there can only be one such entry. This holds not true
+ for virtual file systems that use "none" as device name.
+
+ If name is "none", NULL is returned. This also makes it possible to
+ add more than one entry for the device "none". */
+inline struct fs *
+fstab_find_device (const struct fstab *fstab, const char *name)
+{
+ if (strcmp (name, "none") == 0)
+ return NULL;
+
+ struct fs *fs;
+ for (fs = fstab->entries; fs; fs = fs->next)
+ if (strcmp (fs->mntent.mnt_fsname, name) == 0)
+ return fs;
+ return 0;
+}
+
+/* Returns the FS entry in FSTAB with the mount point NAME (there can only
+ be one such entry). */
+inline struct fs *
+fstab_find_mount (const struct fstab *fstab, const char *name)
+{
+ struct fs *fs;
+
+ /* Don't count "none" or "-" as matching any other mount point.
+ It is canonical to use "none" for swap partitions, and multiple
+ such do not in fact conflict with each other. Likewise, the
+ special device name "ignore" is used for things that should not
+ be processed automatically. */
+ if (!strcmp (name, "-")
+ || !strcmp (name, "none")
+ || !strcmp (name, "ignore"))
+ return 0;
+
+ for (fs = fstab->entries; fs; fs = fs->next)
+ if (strcmp (fs->mntent.mnt_dir, name) == 0)
+ return fs;
+ return 0;
+}
+
+/* Returns the FS entry in FSTAB with the device or mount point NAME (there
+ can only be one such entry). */
+inline struct fs *
+fstab_find (const struct fstab *fstab, const char *name)
+{
+ struct fs *ret;
+ char *real_name;
+
+ ret = fstab_find_device (fstab, name);
+ if (ret)
+ return ret;
+
+ ret = fstab_find_mount (fstab, name);
+ if (ret)
+ return ret;
+
+ real_name = realpath (name, NULL);
+
+ ret = fstab_find_device (fstab, real_name);
+ if (ret) {
+ free (real_name);
+ return ret;
+ }
+
+ ret = fstab_find_mount (fstab, real_name);
+ free (real_name);
+
+ return ret;
+}
+
+/* Cons FS onto the beginning of FSTAB's entry list. */
+static void
+_fstab_add (struct fstab *fstab, struct fs *fs)
+{
+ fs->fstab = fstab;
+ fs->next = fstab->entries;
+ fs->self = &fstab->entries;
+ if (fstab->entries)
+ fstab->entries->self = &fs->next;
+ fstab->entries = fs;
+}
+
+/* Destroy FS, removing it from its containing FSTAB. */
+void
+fs_free (struct fs *fs)
+{
+ *fs->self = fs->next; /* unlink from chain */
+ if (fs->storage)
+ free (fs->storage);
+ if (fs->fsys != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), fs->fsys);
+ free (fs);
+}
+
+/* Add an entry for MNTENT to FSTAB, removing any existing entries that
+ conflict (in either the device or mount point). If RESULT is non-zero, the
+ new entry is returne in it. */
+error_t
+fstab_add_mntent (struct fstab *const fstab, const struct mntent *mntent,
+ struct fs **result)
+{
+ int new = 0; /* True if we didn't overwrite an old entry. */
+ error_t err = 0;
+ struct fs *fs = fstab_find_device (fstab, mntent->mnt_fsname);
+ struct fs *mounted_fs = fstab_find_mount (fstab, mntent->mnt_dir);
+
+ if (! fs)
+ /* No old entry with the same device; see if there's one with the same
+ mount point. */
+ {
+ fs = mounted_fs;
+ mounted_fs = 0;
+ }
+
+ if (! fs)
+ /* No old entry, make a new one. */
+ {
+ fs = malloc (sizeof (struct fs));
+ if (fs)
+ {
+ bzero (fs, sizeof (struct fs));
+ fs->mounted = fs->readonly = -1;
+ fs->fsys = MACH_PORT_NULL;
+ new = 1;
+ }
+ else
+ err = ENOMEM;
+ }
+
+ if (! err)
+ /* Try and fill in FS's mntent. */
+ err = fs_set_mntent (fs, mntent);
+
+ if (new)
+ {
+ if (! err)
+ _fstab_add (fstab, fs);
+ else if (fs)
+ free (fs);
+ }
+
+ if (!err && mounted_fs && mounted_fs != fs)
+ /* Get rid of the conflicting entry MOUNTED_FS. */
+ fs_free (mounted_fs);
+
+ if (!err && result)
+ *result = fs;
+
+ return err;
+}
+
+/* Copy the entry FS (which should belong to another fstab than DST) into
+ DST. If DST & SRC have different TYPES fields, EINVAL is returned. If
+ COPY is non-zero, the copy is returned in it. */
+error_t
+fstab_add_fs (struct fstab *dst, const struct fs *fs, struct fs **copy)
+{
+ error_t err;
+ struct fs *new;
+ struct fstab *src = fs->fstab;
+
+ if (dst->types != src->types)
+ return EINVAL;
+
+ err = fstab_add_mntent (dst, &fs->mntent, &new);
+ if (err)
+ return err;
+
+ new->type = fs->type;
+
+ if (copy)
+ *copy = new;
+
+ return 0;
+}
+
+/* Merge SRC into DST, as if by calling fstab_add_fs on DST with every
+ entry in SRC, and then deallocating SRC. If DST & SRC have different
+ TYPES fields, EINVAL is returned. */
+error_t
+fstab_merge (struct fstab *dst, struct fstab *src)
+{
+ struct fs *fs;
+
+ if (dst->types != src->types)
+ return EINVAL;
+
+ /* Remove entries in DST which conflict with those in SRC. */
+ for (fs = src->entries; fs; fs = fs->next)
+ {
+ struct fs *old_fs;
+
+ old_fs = fstab_find_device (dst, fs->mntent.mnt_fsname);
+ if (old_fs)
+ fs_free (old_fs);
+ old_fs = fstab_find_mount (dst, fs->mntent.mnt_dir);
+ if (old_fs)
+ fs_free (old_fs);
+ }
+
+ /* Now that we know there are no conflicts, steal all SRC's entries and
+ cons them onto DST. */
+ for (fs = src->entries; fs; fs = fs->next)
+ _fstab_add (dst, fs);
+
+ /* Now all entries from SRC should be in DST, so just deallocate SRC. */
+ free (src);
+
+ return 0;
+}
+
+/* Reads fstab-format entries into FSTAB from the file NAME. Any entries
+ duplicating one already in FS_LIST supersede the existing entry. */
+error_t
+fstab_read (struct fstab *fstab, const char *name)
+{
+ error_t err;
+ /* Used to hold entries from the file, before merging with FSTAB at the
+ end. */
+ struct fstab *contents;
+ FILE *stream = setmntent (name, "r");
+
+ if (! stream)
+ return errno;
+
+ err = fstab_create (fstab->types, &contents);
+ if (! err)
+ {
+ while (!err && !feof (stream))
+ {
+ struct mntent *mntent = getmntent (stream);
+
+ if (! mntent)
+ err = errno;
+ else if (fstab_find_device (fstab, mntent->mnt_fsname))
+ error (0, 0, "%s: Warning: duplicate entry for device %s (%s)",
+ name, mntent->mnt_fsname, mntent->mnt_dir);
+ else if (fstab_find_mount (fstab, mntent->mnt_dir))
+ error (0, 0, "%s: Warning: duplicate entry for mount point %s (%s)",
+ name, mntent->mnt_dir, mntent->mnt_fsname);
+ else
+ err = fstab_add_mntent (fstab, mntent, 0);
+ }
+
+ if (! err)
+ fstab_merge (fstab, contents);
+ else
+ fstab_free (contents);
+ }
+
+ endmntent (stream);
+
+ return err;
+}
+
+/* Return the next pass number that applies to any filesystem in FSTAB that
+ is greater than PASS, or -1 if there isn't any. */
+int fstab_next_pass (const struct fstab *fstab, int pass)
+{
+ int next_pass = -1;
+ struct fs *fs;
+ for (fs = fstab->entries; fs; fs = fs->next)
+ if (fs->mntent.mnt_passno > pass)
+ if (next_pass < 0 || fs->mntent.mnt_passno < next_pass)
+ {
+ next_pass = fs->mntent.mnt_passno;
+ if (next_pass == pass + 1)
+ break; /* Only possible answer. */
+ }
+ return next_pass;
+}
+
+
+static const struct argp_option options[] =
+{
+ {"all", 'a', 0, 0, "Do all filesystems in " _PATH_MNTTAB},
+ {0, 'A', 0, OPTION_ALIAS },
+ {"fstab", 'F', "FILE", 0, "File to use instead of " _PATH_MNTTAB},
+ {"fstype", 't', "TYPE", 0, "Do only filesystems of given type(s)"},
+ {"exclude-root",'R',0, 0,
+ "Exclude root (/) filesystem from " _PATH_MNTTAB " list"},
+ {"exclude", 'X', "PATTERN", 0, "Exclude directories matching PATTERN"},
+
+ {"search-fmts",'S', "FMTS", 0,
+ "`:' separated list of formats to use for finding"
+ " filesystem-specific programs"},
+
+ {0, 0}
+};
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ error_t err;
+ struct fstab_argp_params *params = state->input;
+
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ /* Initialize our parsing state. */
+ if (! params)
+ return EINVAL; /* Need at least a way to return a result. */
+ bzero (params, sizeof *params);
+ break;
+
+ case 'A':
+ case 'a':
+ params->do_all = 1;
+ break;
+
+ case 'F':
+ params->fstab_path = arg;
+ break;
+
+ case 'S':
+ argz_create_sep (arg, ':',
+ &params->program_search_fmts,
+ &params->program_search_fmts_len);
+ break;
+
+ case 'R':
+ arg = "/";
+ /* FALLTHROUGH */
+ case 'X':
+ err = argz_add (&params->exclude, &params->exclude_len, arg);
+ if (err)
+ argp_failure (state, 100, ENOMEM, "%s", arg);
+ break;
+ case 't':
+ err = argz_add_sep (&params->types, &params->types_len, arg, ',');
+ if (err)
+ argp_failure (state, 100, ENOMEM, "%s", arg);
+ break;
+
+ case ARGP_KEY_ARG:
+ err = argz_add (&params->names, &params->names_len, arg);
+ if (err)
+ argp_failure (state, 100, ENOMEM, "%s", arg);
+ break;
+
+ case ARGP_KEY_END:
+ /* Check for bogus combinations of arguments. */
+ if (params->names)
+ {
+ if (params->do_all)
+ argp_error (state, "filesystem arguments not allowed with --all");
+ if (params->exclude)
+ argp_error (state,
+ "--exclude not allowed with filesystem arguments");
+ if (params->types)
+ argp_error (state,
+ "--fstype not allowed with filesystem arguments");
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+const struct argp fstab_argp = {options, parse_opt, 0, 0};
+
+struct fstab *
+fstab_argp_create (struct fstab_argp_params *params,
+ const char *default_search_fmts,
+ size_t default_search_fmts_len)
+{
+ error_t err;
+ struct fstab *fstab, *check;
+ struct fstypes *types;
+
+ if (params->fstab_path == 0)
+ params->fstab_path = _PATH_MNTTAB;
+ if (params->program_search_fmts == 0)
+ {
+ params->program_search_fmts = (char *) default_search_fmts;
+ params->program_search_fmts_len = default_search_fmts_len;
+ }
+
+ err = fstypes_create (params->program_search_fmts,
+ params->program_search_fmts_len,
+ &types);
+ if (err)
+ error (102, err, "fstypes_create");
+
+ err = fstab_create (types, &fstab);
+ if (err)
+ error (101, err, "fstab_create");
+
+ err = fstab_read (fstab, params->fstab_path);
+ if (err)
+ error (103, err, "%s", params->fstab_path);
+
+ if (params->names)
+ {
+ /* Process specified filesystems; also look at /var/run/mtab. */
+ const char *name;
+
+ err = fstab_read (fstab, _PATH_MOUNTED);
+ if (err && err != ENOENT)
+ error (104, err, "%s", _PATH_MOUNTED);
+
+ err = fstab_create (types, &check);
+ if (err)
+ error (105, err, "fstab_create");
+
+ for (name = params->names; name; name = argz_next (params->names,
+ params->names_len,
+ name))
+ {
+ struct fs *fs = fstab_find (fstab, name);
+ if (! fs)
+ error (106, 0, "%s: Unknown device or filesystem", name);
+ fstab_add_fs (check, fs, 0);
+ }
+
+ /* fstab_free (fstab); XXX */
+ }
+ else
+ {
+ /* Process everything in /etc/fstab. */
+
+ if (params->exclude == 0 && params->types == 0)
+ check = fstab;
+ else
+ {
+ err = fstab_create (types, &check);
+ if (err)
+ error (105, err, "fstab_create");
+
+ int blacklist = strncasecmp (params->types, "no", 2) == 0;
+ if (blacklist)
+ params->types += 2; /* Skip no. */
+
+ struct fs *fs;
+ for (fs = fstab->entries; fs; fs = fs->next)
+ {
+ if (strcmp (fs->mntent.mnt_type, MNTTYPE_SWAP) == 0)
+ continue; /* Ignore swap entries. */
+
+ const char *tn;
+ int matched = 0;
+ for (tn = params->types; tn;
+ tn = argz_next (params->types, params->types_len, tn))
+ {
+ const char *type = fs->mntent.mnt_type;
+ if (strcmp (type, tn) == 0
+ /* Skip no for compatibility. */
+ || ((strncasecmp (type, "no", 2) == 0)
+ && strcmp (type, tn) == 0))
+ {
+ matched = 1;
+ break;
+ }
+ }
+
+ if (matched == blacklist)
+ continue; /* Either matched and types is a blacklist
+ or not matched and types is a whitelist */
+
+ const char *ptn;
+ for (ptn = params->exclude; ptn;
+ ptn = argz_next (params->exclude, params->exclude_len, ptn))
+ if (fnmatch (ptn, fs->mntent.mnt_dir, 0) == 0)
+ break;
+ if (ptn) /* An exclude pattern matched. */
+ continue;
+
+ err = fstab_add_fs (check, fs, 0);
+ if (err)
+ error (107, err, "fstab_add_fs");
+ }
+
+ /* fstab_free (fstab); XXX */
+ }
+ }
+
+ return check;
+}
diff --git a/sutils/fstab.h b/sutils/fstab.h
new file mode 100644
index 00000000..ddd1656c
--- /dev/null
+++ b/sutils/fstab.h
@@ -0,0 +1,178 @@
+/* Fstab filesystem frobbing
+
+ Copyright (C) 1996, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __FSTAB_H__
+#define __FSTAB_H__
+
+#include <mntent.h>
+#include <hurd.h>
+
+struct fs
+{
+ struct fstab *fstab; /* Containing fstab. */
+ struct mntent mntent; /* Mount entry from fstab file. */
+ char *storage; /* Storage for strings in MNTENT. */
+ struct fstype *type; /* Only set if fs_type called. */
+ int readonly, mounted; /* Only set if fs_{readonly,mounted} called.
+ 0 or 1 if known; -1 if unknown. */
+ fsys_t fsys; /* Only set if fs_fsys called. */
+ struct fs *next, **self;
+};
+
+struct fstab
+{
+ struct fs *entries;
+ struct fstypes *types;
+};
+
+struct fstype
+{
+ char *name; /* Malloced. */
+ char *program; /* Malloced. */
+ struct fstype *next;
+};
+
+struct fstypes
+{
+ struct fstype *entries;
+
+ /* How to search for programs. A '\0' separated list of strings. Each
+ should be a printf-style format string, which will be passed the
+ filesystem type. The first such expansion that results in an executable
+ file will be chosen as the fsck program for that type. */
+ char *program_search_fmts;
+ size_t program_search_fmts_len; /* Length of PROGRAM_SEARCH_FMTS. */
+};
+
+/* Return a new fstab in FSTAB. */
+error_t fstab_create (struct fstypes *types, struct fstab **fstab);
+
+/* Free FSTAB and all of its entries. */
+void fstab_free (struct fstab *fstab);
+
+/* Return a new fstypes structure in TYPES. SEARCH_FMTS is copied. */
+error_t fstypes_create (const char *search_fmts, size_t search_fmts_len,
+ struct fstypes **types);
+
+/* Return an fstype entry in TYPES called NAME, in FSTYPE. If there is no
+ existing entry, an attempt to find a program with the given type,
+ using the alternatives in the PROGRAM_SEARCH_FMTS field in TYPES. If
+ one is found, it is added to TYPES, otherwise a new entry is created
+ with a null PROGRAM field. */
+error_t fstypes_get (struct fstypes *types,
+ const char *name, struct fstype **fstype);
+
+/* Copy MNTENT into FS, copying component strings as well. */
+error_t fs_set_mntent (struct fs *fs, const struct mntent *mntent);
+
+/* Returns an fstype for FS in TYPE, trying to fill in FS's type field if
+ necessary. */
+error_t fs_type (struct fs *fs, struct fstype **type);
+
+/* Looks to see if FS is currently mounted, being very careful to avoid
+ mounting anything that's not already, and returns the control port for the
+ mounted filesystem (MACH_PORT_NULL if not mounted). */
+error_t fs_fsys (struct fs *fs, fsys_t *fsys);
+
+/* Looks to see if FS is currently mounted, being very careful to avoid
+ mounting anything that's not already, and returns the boolean MOUNTED. */
+error_t fs_mounted (struct fs *fs, int *mounted);
+
+/* Looks to see if FS is currently mounted readonly, being very careful to
+ avoid mounting anything that's not already, and returns the boolean
+ READONLY. If FS isn't mounted at all, READONLY is set to 1 (it's not
+ going to write anything!). */
+error_t fs_readonly (struct fs *fs, int *readonly);
+
+/* If FS is currently mounted writable, try to make it readonly. XXX If FS
+ is not mounted at all, then nothing is done. */
+error_t fs_set_readonly (struct fs *fs, int readonly);
+
+/* If FS is currently mounted tell it to remount the device. XXX If FS is
+ not mounted at all, then nothing is done. */
+error_t fs_remount (struct fs *fs);
+
+/* Destroy FS, removing it from its containing FSTAB. */
+void fs_free (struct fs *fs);
+
+/* Returns the FS entry in FSTAB with the device field NAME (there can only
+ be one such entry). */
+struct fs *fstab_find_device (const struct fstab *fstab, const char *name);
+
+/* Returns the FS entry in FSTAB with the mount point NAME (there can only
+ be one such entry). */
+struct fs *fstab_find_mount (const struct fstab *fstab, const char *name);
+
+/* Returns the FS entry in FSTAB with the device or mount point NAME (there
+ can only be one such entry). */
+struct fs *fstab_find (const struct fstab *fstab, const char *name);
+
+/* Add an entry for MNTENT to FSTAB, removing any existing entries that
+ conflict (in either the device or mount point). If RESULT is non-zero, the
+ new entry is returned in it. */
+error_t fstab_add_mntent (struct fstab *fstab, const struct mntent *mntent,
+ struct fs **result);
+
+/* Copy the entry FS (which should belong to another fstab than DST) into
+ DST. If DST & SRC have different TYPES fields, EINVAL is returned. If
+ COPY is non-zero, the copy is returned in it. */
+error_t fstab_add_fs (struct fstab *dst, const struct fs *fs,
+ struct fs **copy);
+
+/* Merge SRC into DST, as if by calling fstab_add_fs on DST with every
+ entry in SRC, and then deallocating SRC. If DST & SRC have different
+ TYPES fields, EINVAL is returned. */
+error_t fstab_merge (struct fstab *dst, struct fstab *src);
+
+/* Reads fstab-format entries into FSTAB from the file NAME. Any entries
+ duplicating one already in FS_LIST supersede the existing entry. */
+error_t fstab_read (struct fstab *fstab, const char *name);
+
+/* Return the next pass number that applies to any filesystem in FSTAB that
+ is greater than PASS, or -1 if there isn't any. */
+int fstab_next_pass (const struct fstab *fstab, int pass);
+
+
+struct argp;
+extern const struct argp fstab_argp;
+struct fstab_argp_params
+{
+ char *fstab_path;
+ char *program_search_fmts;
+ size_t program_search_fmts_len;
+
+ int do_all;
+ char *types;
+ size_t types_len;
+ char *exclude;
+ size_t exclude_len;
+
+ char *names;
+ size_t names_len;
+};
+
+struct fstab *fstab_argp_create (struct fstab_argp_params *params,
+ const char *default_search_fmts,
+ size_t default_search_fmts_len);
+
+
+#endif /* __FSTAB_H__ */
diff --git a/sutils/halt.c b/sutils/halt.c
new file mode 100644
index 00000000..08f754c4
--- /dev/null
+++ b/sutils/halt.c
@@ -0,0 +1,40 @@
+/* Halt the system
+ Copyright (C) 1994, 1996, 1997, 2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <sys/reboot.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <argp.h>
+#include <error.h>
+#include <hurd.h>
+#include <version.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (halt);
+
+int
+main (int argc, char *argv[])
+{
+ struct argp argp = {0, 0, 0, "Halt the system"};
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+ reboot (RB_HALT);
+ error (1, errno, "reboot");
+ return 1;
+}
diff --git a/sutils/losetup.sh b/sutils/losetup.sh
new file mode 100644
index 00000000..85734571
--- /dev/null
+++ b/sutils/losetup.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# This script is roughly compatible with the Linux `losetup' utility.
+# The Hurd's `storeio' translator provides the equivalent functionality
+# (and a whole lot more), and of course works on any old file you want
+# to translate, not just magical "/dev/loopN" device files.
+#
+
+PATH=/bin
+
+usage() {
+ echo >&2 ...
+ exit 1
+}
+
+offset=0
+while [ $# -gt 0 ]; do
+ case "$arg" in
+ -d)
+ [ $# -eq 2 ] || usage
+ exec settrans -g -- "$2" /hurd/null
+ ;;
+ -e)
+ echo >&2 "$0: encryption not supported"
+ exit 3
+ ;;
+ -o)
+ [ $# -gt 1 ] || usage
+ offset="$1"
+ shift
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ usage
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+[ $# -eq 2 ] || usage
+device="$1"
+file="$2"
+
+# If the device name is "/dev/loopN", then create it if necessary. (?)
+create=
+case "$device" in
+'/dev/loop[0-9]*') ;; # smarty pants
+/dev/loop[0-9]*) create=--create ;;
+esac
+
+type='-Tfile '
+if [ "$offset" != 0 ]; then
+ blksz=`storeinfo -B -- "$file"`
+ if [ $[ $offset % $blksz ] -ne 0 ]; then
+ echo >&2 "$0: offset $offset is not a multiple of device block size $blksz"
+ exit 1
+ fi
+ type="-Tremap $[ $offset / $blksz ]+:file:"
+fi
+
+exec settrans $create -gap -- "${device}" /hurd/storeio ${type}"${file}"
diff --git a/sutils/reboot.c b/sutils/reboot.c
new file mode 100644
index 00000000..54b9b99c
--- /dev/null
+++ b/sutils/reboot.c
@@ -0,0 +1,40 @@
+/* Reboot the system
+ Copyright (C) 1994, 1996, 1997, 2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <sys/reboot.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <argp.h>
+#include <error.h>
+#include <hurd.h>
+#include <version.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (reboot);
+
+int
+main (int argc, char *argv[])
+{
+ struct argp argp = {0, 0, 0, "Reboot the system"};
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+ reboot (0);
+ error (1, errno, "reboot");
+ return 1;
+}
diff --git a/sutils/swapoff.c b/sutils/swapoff.c
new file mode 100644
index 00000000..42521257
--- /dev/null
+++ b/sutils/swapoff.c
@@ -0,0 +1,2 @@
+#define SWAPOFF
+#include "swapon.c"
diff --git a/sutils/swapon.c b/sutils/swapon.c
new file mode 100644
index 00000000..2403f1c2
--- /dev/null
+++ b/sutils/swapon.c
@@ -0,0 +1,551 @@
+/* Add/remove paging devices
+
+ Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2007
+ Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <argp.h>
+#include <error.h>
+#include <assert.h>
+#include <sys/mman.h>
+#include <hurd/store.h>
+#include <hurd/paths.h>
+#include <version.h>
+#include <mntent.h>
+#include "default_pager_U.h"
+
+#ifdef SWAPOFF
+const char *argp_program_version = STANDARD_HURD_VERSION (swapoff);
+#else
+const char *argp_program_version = STANDARD_HURD_VERSION (swapon);
+#endif
+
+static int ignore_signature, require_signature, quiet, ifexists;
+
+static struct argp_option options[] =
+{
+ {"standard", 'a', 0, 0,
+ "Use all devices marked as `swap' in " _PATH_MNTTAB},
+ {"ifexists", 'e', 0, 0,
+ "Silently skip devices that do not exist"},
+ {"no-signature",'n', 0, 0,
+ "Do not check for a Linux swap signature page"},
+ {"require-signature", 's', 0, 0,
+ "Require a Linux swap signature page"},
+ {"silent", 'q', 0, 0, "Print only diagnostic messages"},
+ {"quiet", 'q', 0, OPTION_ALIAS | OPTION_HIDDEN },
+ {"verbose", 'v', 0, 0, "Be verbose"},
+ {0, 0}
+};
+static char *args_doc = "DEVICE...";
+
+static char *doc =
+#ifdef SWAPOFF
+ "Stop paging on DEVICE..."
+ "\vUnless overridden, a swap space signature is not considered when deciding"
+ " whether to remove a paging device or not."
+#else
+ "Start paging onto DEVICE..."
+ "\vUnless overridden, only devices with a valid (Linux) swap space signature"
+ " are considered when deciding whether to add a paging device or not."
+#endif
+;
+
+#define verbose(fmt, arg...) \
+ if (quiet_now) ((void)0); else error (0, 0, fmt ,##arg)
+#define inform_2_0(fmt, arg...) \
+ verbose ("%s: Linux 2.0 swap signature, " fmt, name ,##arg)
+#define inform_2_2(fmt, arg...) \
+ verbose ("%s: Linux 2.2 swap signature v1, %zuk swap-space" fmt, \
+ name, freepages * (LINUX_PAGE_SIZE / 1024) ,##arg)
+
+
+/* Examine the store in *STOREP to see if it has a Linux-compatible
+ swap signature page as created by the Linux `mkswap' utility. If
+ we find such a signature, it indicates some subset of the store
+ that should actually be used for paging; return zero after
+ consuming *STOREP and replacing it by using store_remap to get just
+ the indicated subset (unless NO_REMAP is nonzero). If we get an
+ error reading the store, or find a signature but have some problem
+ with it, return some error code. If the store has no signature at
+ all, and if --require-signature was given, then that is an error.
+ This function prints diagnostics for all those errors. Otherwise
+ (the store has no signature) we return EFTYPE and print nothing. */
+
+static error_t
+check_signature (const char *name, struct store **storep, int no_remap,
+ int quiet_now)
+{
+ struct store *const store = *storep;
+
+#define LINUX_PAGE_SIZE 4096 /* size of pages in Linux swap partitions */
+#define LINUX_PAGE_SHIFT 12
+
+ /* RUNS points to latest run (highest value of start). Each time we
+ remove a bad page from the set, we either adjust the latest run or add
+ a new one and point RUNS at it. */
+
+ struct run
+ {
+ struct run *next;
+ size_t start, limit; /* in units of LINUX_PAGE_SIZE */
+ };
+
+ size_t freepages = store->size / LINUX_PAGE_SIZE;
+ struct run first_run = { NULL, 0, freepages }, *runs = &first_run;
+ size_t nruns = 1;
+ /* This is always called with increasing page numbers. */
+#define BAD_PAGE(pageno) \
+ ({ \
+ size_t page = (pageno); \
+ if (page == runs->start) \
+ runs->start = page + 1; \
+ else \
+ { \
+ runs->next = alloca (sizeof *runs); \
+ runs->next->start = page + 1; \
+ runs->next->limit = runs->limit; \
+ runs->limit = page; \
+ ++nruns; \
+ } \
+ })
+
+ /* Read the first page, which contains the signature. */
+ void *buf = 0;
+ size_t len = 0;
+ error_t err = store_read (store, 0, LINUX_PAGE_SIZE, &buf, &len);
+ if (err)
+ {
+ error (0, err, "%s: cannot read Linux swap signature page", name);
+ return err;
+ }
+ if (len < LINUX_PAGE_SIZE)
+ {
+ error (0, 0, "%s: short read %zu reading Linux swap signature page",
+ name, len);
+ return EINVAL;
+ }
+
+ quiet_now |= quiet;
+
+ /* Check for Linux 2.0 format. */
+ if (!memcmp ("SWAP-SPACE", buf + LINUX_PAGE_SIZE-10, 10))
+ {
+ /* The partition's first page has a Linux swap signature.
+ This means the beginning of the page contains a bitmap
+ of good pages, and all others are bad. */
+ size_t i, bad, max;
+ int waste;
+
+ /* The first page, and the pages corresponding to the bits
+ occupied by the signature in the final 10 bytes of the page,
+ are always unavailable ("bad"). */
+ *(uint32_t *) buf &= ~(u_int32_t) 1;
+ memset (buf + LINUX_PAGE_SIZE-10, 0, 10);
+
+ max = LINUX_PAGE_SIZE / sizeof (uint32_t);
+ if (max > (store->size + 31) / 32)
+ max = (store->size + 31) / 32;
+
+ /* Search the page for zero bits, which indicate unusable pages. */
+ bad = 0;
+ for (i = 0; i < max; ++i)
+ {
+ size_t p = i*32;
+ uint32_t bm = ~((uint32_t *) buf)[i];
+ while (bm != 0) /* inverted so unusable pages are one bits */
+ {
+ /* Find the first bit set in this word. */
+ int bit = ffs (bm);
+ bm >>= bit; /* Next time look at the rest of the word. */
+ p += bit - 1; /* Corresponding page. */
+ if (p >= runs->limit)
+ break;
+ ++bad;
+ BAD_PAGE (p);
+ }
+ }
+ freepages -= bad;
+
+ --bad; /* Don't complain about first page. */
+ waste = (store->size >> LINUX_PAGE_SHIFT) - (8 * (LINUX_PAGE_SIZE-10));
+
+ if (waste > 0)
+ {
+ /* The wasted pages were already marked "bad". */
+ bad -= waste;
+ if (bad > 0)
+ inform_2_0 ("%zdk swap-space (%zdk bad, %dk wasted at end)",
+ freepages * (LINUX_PAGE_SIZE / 1024),
+ bad * (LINUX_PAGE_SIZE / 1024),
+ waste * (LINUX_PAGE_SIZE / 1024));
+ else
+ inform_2_0 ("%zdk swap-space (%dk wasted at end)",
+ freepages * (LINUX_PAGE_SIZE / 1024),
+ waste * (LINUX_PAGE_SIZE / 1024));
+ }
+ else if (bad > 0)
+ inform_2_0 ("%zdk swap-space (excludes %zdk marked bad)",
+ freepages * (LINUX_PAGE_SIZE / 1024),
+ bad * (LINUX_PAGE_SIZE / 1024));
+ else
+ inform_2_0 ("%zdk swap-space", freepages * (LINUX_PAGE_SIZE / 1024));
+ }
+ /* Check for Linux 2.2 format. */
+ else if (!memcmp ("SWAPSPACE2", buf + LINUX_PAGE_SIZE-10, 10))
+ {
+ struct
+ {
+ u_int8_t bootbits[1024];
+ u_int32_t version;
+ u_int32_t last_page;
+ u_int32_t nr_badpages;
+ u_int32_t padding[125];
+ u_int32_t badpages[1];
+ } *hdr = buf;
+
+ ++first_run.start; /* first page unusable */
+ --freepages;
+
+ switch (hdr->version)
+ {
+ default:
+ error (0, 0,
+ "%s: Linux 2.2 swap signature with unknown version %u",
+ name, hdr->version);
+ munmap (buf, len);
+ if (require_signature)
+ {
+ error (0, 0, "%s: will not use without valid signature page",
+ name);
+ return EINVAL;
+ }
+ error (0, 0, "WARNING: ignoring unrecognized signature page");
+ return EFTYPE;
+
+ case 1:
+ {
+ unsigned int waste, i;
+ if (hdr->last_page >= first_run.limit)
+ {
+ error (0, 0,
+ "%s: signature says %uk, partition has only %uk!",
+ name,
+ hdr->last_page * (LINUX_PAGE_SIZE / 1024),
+ (unsigned int) (store->size / 1024));
+ waste = 0;
+ }
+ else
+ {
+ waste = first_run.limit + 1 - hdr->last_page;
+ freepages = first_run.limit - first_run.start;
+ first_run.limit = hdr->last_page + 1;
+ }
+ for (i = 0; i < hdr->nr_badpages; ++i)
+ {
+ BAD_PAGE (hdr->badpages[i]);
+ --freepages;
+ }
+
+ {
+ size_t badk = hdr->nr_badpages * (LINUX_PAGE_SIZE / 1024);
+ size_t wastek = waste * (LINUX_PAGE_SIZE / 1024);
+ if (badk && wastek)
+ inform_2_2 ("\
+ (excludes %zuk marked bad and %zuk at end of partition)",
+ badk, wastek);
+ else if (badk)
+ inform_2_2 (" (excludes %zuk marked bad)", badk);
+ else if (wastek)
+ inform_2_2 (" (excludes %zuk at end of partition)", wastek);
+ else
+ inform_2_2 ("");
+ }
+ }
+ }
+ }
+ /* There does not appear to be any signature page here. */
+ else if (require_signature)
+ {
+ error (0, 0, "%s: will not use without Linux swap signature", name);
+ return EINVAL;
+ }
+ else
+ /* We use this error code to tell our caller that we found nothing. */
+ return EFTYPE;
+
+ /* Now that we have collected the runs of LINUX_PAGE_SIZE we will use,
+ convert those into store_run's in the store's block size. */
+ {
+ const int scale = LINUX_PAGE_SHIFT - store->log2_block_size;
+ struct store_run store_runs[nruns];
+ size_t i = 0;
+ struct run *r = &first_run;
+ do
+ {
+ struct store_run *sr = &store_runs[i++];
+ sr->start = (store_offset_t) r->start << scale;
+ sr->length = (r->limit - r->start) << scale;
+ do
+ r = r->next;
+ while (r != 0 && r->start == r->limit); /* skip empty runs */
+ } while (r != 0);
+
+ /* Give us a new store that uses only the good pages. */
+ return store_remap (store, store_runs, i, storep);
+ }
+}
+
+
+/* Process a single argument file. */
+
+static int
+swaponoff (const char *file, int add, int skipnotexisting)
+{
+ error_t err;
+ struct store *store;
+ static mach_port_t def_pager = MACH_PORT_NULL;
+ static mach_port_t dev_master = MACH_PORT_NULL;
+ static int old_protocol;
+ int quiet_now = 0;
+
+ try_again:
+ err = store_open (file, 0, 0, &store);
+ if (err)
+ {
+ /* If the device does not exist but we were told to ignore such error,
+ return cleanly. */
+ if (err == ENOENT && skipnotexisting)
+ return 0;
+ error (0, err, "%s", file);
+ return err;
+ }
+
+ /* Let's see what we've got. */
+ if (old_protocol)
+ {
+ /* The default pager only lets us give a whole partition, and
+ it will read the signature page (but not insist on it). */
+ if (! (store->flags & STORE_ENFORCED))
+ {
+ error (0, 0, "%s: Can only page to the entire device", file);
+ return EINVAL;
+ }
+ /* If we want to require the signature, we can check that it is
+ actually there even though we won't be the one interpreting it. */
+ if (require_signature
+ && check_signature (file, &store, 1, quiet_now) != 0)
+ return EINVAL;
+ }
+ else if (ignore_signature)
+ verbose ("%s: %uk swap space",
+ file, (unsigned int) (store->size / 1024));
+ else
+ {
+ /* Adjust the store according to the Linux signature. */
+ err = check_signature (file, &store, 0, 0);
+ if (err == EFTYPE)
+ verbose ("%s: %uk swap space (no Linux signature page)",
+ file, (unsigned int) (store->size / 1024));
+ else if (err)
+ {
+ store_free (store);
+ return err;
+ }
+ /* Otherwise check_signature printed something out. */
+ }
+
+ if (store->class != &store_device_class)
+ {
+ error (0, 0, "%s: Can't get underlying device", file);
+ store_free (store);
+ return EINVAL;
+ }
+
+ if (def_pager == MACH_PORT_NULL)
+ {
+ mach_port_t host;
+
+ err = get_privileged_ports (&host, &dev_master);
+ if (err == EPERM)
+ {
+ /* We are not root, so try opening the /servers node. */
+ def_pager = file_name_lookup (_SERVERS_DEFPAGER, O_WRITE, 0);
+ if (def_pager == MACH_PORT_NULL)
+ {
+ error (11, errno, _SERVERS_DEFPAGER);
+ return 0;
+ }
+ }
+ else
+ {
+ if (err)
+ error (12, err, "Cannot get privileged ports");
+
+ err = vm_set_default_memory_manager (host, &def_pager);
+ mach_port_deallocate (mach_task_self (), host);
+ if (err)
+ error (13, err, "Cannot get default pager port");
+ if (def_pager == MACH_PORT_NULL)
+ error (14, 0, "No default pager (memory manager) is running!");
+ }
+ }
+
+ if (old_protocol)
+ {
+ /* The default pager does not support the new protocol.
+ We tried it in a previous call (below) and got MIG_BAD_ID. */
+ char pname[sizeof "/dev/" + strlen (store->name) + 1];
+ strcpy (stpcpy (pname, "/dev/"), store->name);
+ err = default_pager_paging_file (def_pager, dev_master, pname, add);
+ }
+ else
+ {
+ /* Try the new protocol, which will take our list of runs. */
+ recnum_t runs[store->num_runs * 2];
+ size_t i, j;
+ for (i = j = 0; i < store->num_runs; ++i)
+ {
+ runs[j++] = store->runs[i].start;
+ runs[j++] = store->runs[i].length;
+ }
+ err = default_pager_paging_storage (def_pager, store->port,
+ runs, j, store->name, add);
+ if (err == MIG_BAD_ID)
+ {
+ /* The default pager does not support the new protocol.
+ We'll do the whole thing over again, since we have
+ different requirements now. */
+ old_protocol = 1;
+ store_free (store);
+ if (! ignore_signature)
+ error (0, 0, "\
+default pager uses old protocol, does its own signature checking");
+ quiet_now = 1;
+ goto try_again;
+ }
+ }
+
+ store_free (store);
+
+ if (err)
+ error (0, err, "%s", file);
+
+ return err;
+}
+
+#undef inform_2_0
+#undef inform_2_2
+#undef verbose
+
+static int do_all;
+
+int
+main (int argc, char *argv[])
+{
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'a':
+ do_all = 1;
+ break;
+
+ case 'e':
+ ifexists = 1;
+ break;
+
+ case 'n':
+ ignore_signature = 1;
+ break;
+
+ case 's':
+ require_signature = 1;
+ ignore_signature = 0;
+ break;
+
+ case 'q':
+ quiet = 1;
+ break;
+
+ case 'v':
+ quiet = 0;
+ break;
+
+ case ARGP_KEY_ARG:
+#ifdef SWAPOFF
+#define ONOFF 0
+#else
+#define ONOFF 1
+#endif
+ swaponoff (arg, ONOFF, 0);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ /* See the documentation string DOC. */
+#ifdef SWAPOFF
+ ignore_signature = 1;
+ require_signature = 0;
+#else
+ ignore_signature = 0;
+ require_signature = 1;
+#endif
+
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+ if (do_all)
+ {
+ struct mntent *me;
+ FILE *f;
+
+ f = setmntent (_PATH_MNTTAB, "r");
+ if (f == NULL)
+ error (1, errno, "Cannot read %s", _PATH_MNTTAB);
+ else
+ {
+ int done = 0, err = 0;
+ while ((me = getmntent (f)) != NULL)
+ if (!strcmp (me->mnt_type, MNTTYPE_SWAP))
+ {
+ done = 1;
+
+ err |= swaponoff (me->mnt_fsname, ONOFF, ifexists);
+ }
+ if (done == 0)
+ error (2, 0, "No swap partitions found in %s", _PATH_MNTTAB);
+ else if (err)
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/sutils/update.c b/sutils/update.c
new file mode 100644
index 00000000..c1f4e982
--- /dev/null
+++ b/sutils/update.c
@@ -0,0 +1,51 @@
+/* Periodically call sync.
+ Copyright (C) 1994, 2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (int argc, char **argv)
+{
+ int interval;
+
+ switch (argc)
+ {
+ case 1:
+ interval = 30;
+ break;
+ case 2:
+ interval = atoi (argv[1]);
+ break;
+ default:
+ fprintf (stderr, "Usage: %s [SECONDS]\n", argv[0]);
+ exit (1);
+ }
+
+ if (daemon (0, 0))
+ error (1, errno, "daemon");
+
+ for (;;)
+ {
+ sync ();
+ sleep (interval);
+ }
+}
diff --git a/tasks b/tasks
new file mode 100644
index 00000000..933a63ab
--- /dev/null
+++ b/tasks
@@ -0,0 +1,161 @@
+GNU Hurd Task List Version 1.23. Last updated 25 April 2006.
+
+If you would like to work on one of these, please contact
+bug-hurd@gnu.org. It's important that you let us know what's being
+worked on, because someone else might also be interested, and we should
+coordinate work.
+
+Items that we want done with more priority are marked with !!!.
+
+
+Some of these items might be outdated. If someone feels like triaging
+this list, please go ahead.
+
+These days, we tend to use the Savannah task tracker,
+<URL:http://savannah.gnu.org/task/?group=hurd>.
+
+
+General system work
+
+ * Compile all the free programs you can find to help us fix bugs in
+ the system, and to submit necessary ports to the maintainers of
+ those programs.
+
+ * Do whatever magic is necessary for Perl to take advantage of all
+ the nifty Hurd features that Unix doesn't have.
+
+
+GNU Mach Work
+
+Please discuss proposed microkernel work with thomas@gnu.org.
+
+ * Integrate stream device support so that the X server works
+ out-of-the-box.
+
+!!!
+ * Mach serial drivers support only a small subset of the
+ available possibilities in the Posix c_cflag termios field. Fix
+ this, by adding a new device_set_status call that sets the whole
+ thing and not just the old limited possibilities.
+
+!!!
+ * A replacement for MiG that understands C .h files.
+
+!!!
+ * Mach needs to provide support for task virtual timers similar
+ in functionality to the Unix ITIMER_PROF and ITIMER_VIRTUAL timers.
+
+!!!
+ * Mach needs to make switches from MEMORY_OBJECT_COPY_DELAY to
+ MEMORY_OBJECT_COPY_NONE have the effect of immediately completing any
+ delayed copies.
+
+ * Mach needs a facility to automatically send task and thread
+ status on task/thread exit to a port that can only be changed by
+ a privileged user; this would be used to implement process
+ accounting.
+
+ * Mach needs a facility to find out what task is the parent of
+ a given task.
+
+ * Mach needs a facility to find out which pages of a task's
+ address space are in core to implement Unix's mincore call.
+
+ * Mach needs a facility to do msync.
+
+ * GNU Mach needs the OSF Mach vm_remap call.
+
+ * Mach needs a replacement for MEMORY_OBJECT_COPY_CALL that
+ works at least for the cases needed in ordinary files. (Write
+ thomas if you want to know what the problem is and some ideas about
+ how to solve it.)
+
+!!!
+ * Mach needs proxy memory objects. (thomas can tell you what
+ these are and why they are important.)
+
+ * Mach needs a way to do per-task resource counters that are
+ accessible to servers called by the task.
+
+ * Mach needs facilities to implement resource limits of various sorts.
+
+ * Mach needs a way to have a thread's CPU time statistics
+ include time spent by servers on its behalf. [This has been done
+ for the migrating-threads version of Mach; talk to thomas before starting
+ work on it.]
+
+ * Of course, free ports are always necessary to machines that don't
+ already have free ports.
+
+ * Much work can be done doing research in how to improve Mach VM
+ performance and timesharing scheduling policy.
+
+ * Mach needs facilities to get a list of all the devices which
+ can be device_open'd, as well as to get the type of a device.
+
+ * A way to have the kernel send a message on some designated port
+ every time a new task is started.
+
+ * OSF has enhanced the exception_raise protocol to include thread_state
+ information. This code should be merged into the kernel; OSF people
+ have said their could could be released to the public (but it has not
+ appeared).
+
+ * Add an anonymous swap allocation statistic to the task info structure.
+
+
+Hurd work (these are brief descriptions; thomas can give more information):
+
+ * Make the thread timeout in libports/manage-multithread.c work.
+
+ * Make thread death in C threads really free resources.
+
+!!!
+ * An RPC trace program to aid debugging. Look at utils/rpctrace.c
+ for starters and ask roland for more info.
+
+ * Programs that use utmp need to be changed to use /utmp and utmp.defs.
+
+ * We need some standard translators for /utmp nodes; most importantly
+ one for ordinary terminals (set up by login) and one for X displays
+ (set up by xdm).
+
+!!!
+ * We need some existing shell programs changed to do Hurd things:
+ like ls, tar, cpio, etc. Some of this work has already been
+ done, but more is still needed.
+
+!!!
+ * Extend bash to call shell functions when various Hurdish async
+ events happen.
+
+ * Handy shell programs to send msgport msgs, and change default init
+ ports and ints.
+
+ * Shadow directory translators.
+
+ * A system for write, send, talkd and so forth to bleep users;
+ this should be integrated with the utmp replacement above.
+
+ * A filesystem for /tmp that uses virtual memory instead of disk.
+ (Roland has some ideas about this.)
+
+ * Filesystem implementations (using libdiskfs) for other popular
+ formats. Importantly, MSDOS FAT format.
+
+ * A fancy terminal driver that uses readline and supports
+ detach/attach, color, and virtual consoles.
+
+!!!
+ * A PPP interface. This will require some restructuring of pfinet.
+
+ * A notepad program to hold and keep track of ports for shell scripts.
+
+
+
+C library work. See roland@gnu.org if you are interested in working
+on anything in the C library.
+
+ * Useful response to SIGINFO.
+
+ * Get pthreads working and integrated.
diff --git a/term/Makefile b/term/Makefile
new file mode 100644
index 00000000..5006c0db
--- /dev/null
+++ b/term/Makefile
@@ -0,0 +1,37 @@
+# Copyright (C) 1995,96,97,99, 2000, 2002, 2012 Free Software Foundation,
+# Inc.
+#
+# Written by Michael I. Bushnell, p/BSG.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+
+dir := term
+makemode := server
+
+target = term
+SRCS = devio.c munge.c users.c main.c ptyio.c hurdio.c xinl.c
+
+HURDLIBS = trivfs fshelp iohelp ports ihash shouldbeinlibc
+OTHERLIBS = -lpthread
+OBJS = $(subst .c,.o,$(SRCS)) termServer.o device_replyServer.o tioctlServer.o ourmsgUser.o
+
+include ../Makeconf
+
+device_replyServer-CPPFLAGS = -DTypeCheck=0 -Wno-unused # XXX
+
+tioctl-MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+term-MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
diff --git a/term/devio.c b/term/devio.c
new file mode 100644
index 00000000..eedd7b87
--- /dev/null
+++ b/term/devio.c
@@ -0,0 +1,804 @@
+/*
+ Copyright (C) 1995,96,98,99,2000,01,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* Avoid defenition of the baud rates from <ternios.h> at a later time. */
+#include <termios.h>
+
+/* And undefine the baud rates to avoid warnings from
+ <device/tty_status.h>. */
+#undef B50
+#undef B75
+#undef B110
+#undef B134
+#undef B150
+#undef B200
+#undef B300
+#undef B600
+#undef B1200
+#undef B1800
+#undef B2400
+#undef B4800
+#undef B9600
+#undef B19200
+#undef B38400
+#undef B57600
+#undef B115200
+#undef EXTA
+#undef EXTB
+
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <string.h>
+
+#include <device/device.h>
+#include <device/device_request.h>
+#include <device/tty_status.h>
+#include <pthread.h>
+
+#include <hurd.h>
+#include <hurd/ports.h>
+
+#include "term.h"
+
+
+/* This flag is set if there is an outstanding device_write. */
+static int output_pending;
+
+/* This flag is set if there is an outstanding device_read. */
+static int input_pending;
+
+/* Tell the status of any pending open */
+static enum
+{
+ NOTPENDING, /* no open is pending */
+ INITIAL, /* initial open of device pending */
+ FAKE, /* open pending to block on dtr */
+} open_pending;
+
+static char pending_output[IO_INBAND_MAX];
+static int npending_output;
+
+static struct port_class *phys_reply_class;
+
+/* The Mach device_t representing the terminal. */
+static device_t phys_device = MACH_PORT_NULL;
+
+/* The ports we get replies on for device calls. */
+static mach_port_t phys_reply_writes = MACH_PORT_NULL;
+static mach_port_t phys_reply = MACH_PORT_NULL;
+
+/* The port-info structs. */
+static struct port_info *phys_reply_writes_pi;
+static struct port_info *phys_reply_pi;
+
+static device_t device_master;
+
+static int output_stopped;
+
+/* XXX Mask that omits high bits we are currently not supposed to pass
+ through. */
+static int char_size_mask_xxx = 0xff;
+
+/* Forward */
+static error_t devio_desert_dtr ();
+
+static error_t
+devio_init (void)
+{
+ mach_port_t host_priv;
+ error_t err;
+
+ err = get_privileged_ports (&host_priv, &device_master);
+ if (err)
+ return err;
+ mach_port_deallocate (mach_task_self (), host_priv);
+ if (!phys_reply_class)
+ phys_reply_class = ports_create_class (0, 0);
+ return 0;
+}
+
+static error_t
+devio_fini (void)
+{
+ if (phys_reply_pi)
+ {
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ phys_reply = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_pi);
+ phys_reply_pi = 0;
+ }
+ if (phys_reply_writes_pi)
+ {
+ mach_port_deallocate (mach_task_self (), phys_reply_writes);
+ phys_reply_writes = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_writes_pi);
+ phys_reply_writes_pi = 0;
+ }
+ mach_port_deallocate (mach_task_self (), phys_device);
+ mach_port_deallocate (mach_task_self (), device_master);
+ device_master = MACH_PORT_NULL;
+ return 0;
+}
+
+/* XXX Convert a real speed to a bogus Mach speed. Return
+ -1 if the real speed was bogus, else 0. */
+static int
+real_speed_to_bogus_speed (int rspeed, int *bspeed)
+{
+ switch (rspeed)
+ {
+ case 0:
+ *bspeed = B0;
+ break;
+ case 50:
+ *bspeed = B50;
+ break;
+ case 75:
+ *bspeed = B75;
+ break;
+ case 110:
+ *bspeed = B110;
+ break;
+ case 134:
+ *bspeed = B134;
+ break;
+ case 150:
+ *bspeed = B150;
+ break;
+ case 200:
+ *bspeed = B200;
+ break;
+ case 300:
+ *bspeed = B300;
+ break;
+ case 600:
+ *bspeed = B600;
+ break;
+ case 1200:
+ *bspeed = B1200;
+ break;
+ case 1800:
+ *bspeed = B1800;
+ break;
+ case 2400:
+ *bspeed = B2400;
+ break;
+ case 4800:
+ *bspeed = B4800;
+ break;
+ case 9600:
+ *bspeed = B9600;
+ break;
+ case 19200:
+ *bspeed = EXTA;
+ break;
+ case 38400:
+ *bspeed = EXTB;
+ break;
+#ifdef B57600
+ case 57600:
+ *bspeed = B57600;
+ break;
+#endif
+#ifdef B115200
+ case 115200:
+ *bspeed = B115200;
+ break;
+#endif
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* XXX Convert a bogus speed to a real speed. */
+static int
+bogus_speed_to_real_speed (int bspeed)
+{
+ switch (bspeed)
+ {
+ case B0:
+ default:
+ return 0;
+ case B50:
+ return 50;
+ case B75:
+ return 75;
+ case B110:
+ return 110;
+ case B134:
+ return 134;
+ case B150:
+ return 150;
+ case B200:
+ return 200;
+ case B300:
+ return 300;
+ case B600:
+ return 600;
+ case B1200:
+ return 1200;
+ case B1800:
+ return 1800;
+ case B2400:
+ return 2400;
+ case B4800:
+ return 4800;
+ case B9600:
+ return 9600;
+ case EXTA:
+ return 19200;
+ case EXTB:
+ return 38400;
+#ifdef B57600
+ case B57600:
+ return 57600;
+#endif
+#ifdef B115200
+ case B115200:
+ return 115200;
+#endif
+ }
+}
+
+/* If there are characters on the output queue and no
+ pending output requests, then send them. */
+static error_t
+devio_start_output ()
+{
+ char *cp;
+ int size;
+ error_t err;
+
+ size = qsize (outputq);
+
+ if (!size || output_pending || (termflags & USER_OUTPUT_SUSP))
+ return 0;
+
+ if (output_stopped)
+ {
+ device_set_status (phys_device, TTY_START, 0, 0);
+ output_stopped = 0;
+ }
+
+ /* Copy characters onto PENDING_OUTPUT, not bothering
+ those already there. */
+
+ if (size + npending_output > IO_INBAND_MAX)
+ size = IO_INBAND_MAX - npending_output;
+
+ cp = pending_output + npending_output;
+ npending_output += size;
+
+ while (size--)
+ *cp++ = dequeue (outputq);
+
+ /* Submit all the outstanding characters to the device. */
+ /* The D_NOWAIT flag does not, in fact, prevent blocks. Instead,
+ it merely causes D_WOULD_BLOCK errors when carrier is down...
+ whee.... */
+ err = device_write_request_inband (phys_device, phys_reply_writes, D_NOWAIT,
+ 0, pending_output, npending_output);
+
+ if (err == MACH_SEND_INVALID_DEST)
+ devio_desert_dtr ();
+ else if (!err)
+ output_pending = 1;
+ return 0;
+}
+
+error_t
+device_write_reply_inband (mach_port_t replypt,
+ error_t return_code,
+ int amount)
+{
+ if (replypt != phys_reply_writes)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ output_pending = 0;
+
+ if (return_code == 0)
+ {
+ if (amount >= npending_output)
+ {
+ npending_output = 0;
+ pthread_cond_broadcast (outputq->wait);
+ pthread_cond_broadcast (&select_alert);
+ }
+ else
+ {
+ /* Copy the characters that didn't get output
+ to the front of the array. */
+ npending_output -= amount;
+ memmove (pending_output, pending_output + amount, npending_output);
+ }
+ devio_start_output ();
+ }
+ else if (return_code == D_WOULD_BLOCK)
+ /* Carrier has dropped. */
+ devio_desert_dtr ();
+ else
+ devio_start_output ();
+
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+device_read_reply_inband (mach_port_t replypt,
+ error_t error_code,
+ char *data,
+ u_int datalen)
+{
+ int i, flush;
+ error_t err;
+
+ if (replypt != phys_reply)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ input_pending = 0;
+
+ if (!error_code && (termstate.c_cflag & CREAD))
+ for (i = 0; i < datalen; i++)
+ {
+ int c = data[i];
+
+ /* XXX Mach only supports 8-bit channels; this munges things
+ to account for the reality. */
+ c &= char_size_mask_xxx;
+
+ flush = input_character (c);
+ if (flush)
+ break;
+ }
+ else if (error_code == D_WOULD_BLOCK)
+ {
+ devio_desert_dtr ();
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ /* D_NOWAIT does not actually prevent blocks; it merely causes
+ D_WOULD_BLOCK errors when carrier drops. */
+ err = device_read_request_inband (phys_device, phys_reply, D_NOWAIT,
+ 0, vm_page_size);
+
+ if (err)
+ devio_desert_dtr ();
+ else
+ input_pending = 1;
+
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+static error_t
+devio_set_break ()
+{
+ device_set_status (phys_device, TTY_SET_BREAK, 0, 0);
+ return 0;
+}
+
+static error_t
+devio_clear_break ()
+{
+ device_set_status (phys_device, TTY_CLEAR_BREAK, 0, 0);
+ return 0;
+}
+
+static error_t
+devio_abandon_physical_output ()
+{
+ int val = D_WRITE;
+
+ /* If this variable is clear, then carrier is gone, so we
+ have nothing to do. */
+ if (!phys_reply_writes_pi)
+ return 0;
+
+ mach_port_deallocate (mach_task_self (), phys_reply_writes);
+ ports_reallocate_port (phys_reply_writes_pi);
+ phys_reply_writes = ports_get_send_right (phys_reply_writes_pi);
+
+ device_set_status (phys_device, TTY_FLUSH, &val, TTY_FLUSH_COUNT);
+ npending_output = 0;
+ output_pending = 0;
+ return 0;
+}
+
+static error_t
+devio_suspend_physical_output ()
+{
+ if (!output_stopped)
+ {
+ device_set_status (phys_device, TTY_STOP, 0, 0);
+ output_stopped = 1;
+ }
+ return 0;
+}
+
+static error_t
+devio_notice_input_flushed ()
+{
+ return 0;
+}
+
+static int
+devio_pending_output_size ()
+{
+ /* Unfortunately, there's no way to get the amount back from Mach
+ that has actually been written from this... */
+ return npending_output;
+}
+
+/* Do this the first time the device is to be opened */
+static error_t
+initial_open ()
+{
+ error_t err;
+
+ assert (open_pending != FAKE);
+
+ /* Nothing to do */
+ if (open_pending == INITIAL)
+ return 0;
+
+ assert (phys_device == MACH_PORT_NULL);
+ assert (phys_reply == MACH_PORT_NULL);
+ assert (phys_reply_pi == 0);
+
+ err = ports_create_port (phys_reply_class, term_bucket,
+ sizeof (struct port_info), &phys_reply_pi);
+ if (err)
+ return err;
+
+ phys_reply = ports_get_send_right (phys_reply_pi);
+
+ err = device_open_request (device_master, phys_reply,
+ D_READ|D_WRITE, tty_arg);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ phys_reply = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_pi);
+ phys_reply_pi = 0;
+ }
+ else
+ open_pending = INITIAL;
+
+ return err;
+}
+
+static error_t
+devio_desert_dtr ()
+{
+ int bits;
+
+ /* Turn off DTR. */
+ bits = TM_HUP;
+ device_set_status (phys_device, TTY_MODEM,
+ (dev_status_t) &bits, TTY_MODEM_COUNT);
+
+ report_carrier_off ();
+ return 0;
+}
+
+static error_t
+devio_assert_dtr ()
+{
+ error_t err;
+
+ /* The first time is special. */
+ if (phys_device == MACH_PORT_NULL)
+ return initial_open ();
+
+ /* Schedule a fake open to wait for DTR, unless one is already
+ happening. */
+ assert (open_pending != INITIAL);
+ if (open_pending == FAKE)
+ return 0;
+
+ err = device_open_request (device_master, phys_reply,
+ D_READ|D_WRITE, tty_arg);
+
+ if (err)
+ return err;
+
+ open_pending = FAKE;
+ return 0;
+}
+
+kern_return_t
+device_open_reply (mach_port_t replyport,
+ int returncode,
+ mach_port_t device)
+{
+ struct tty_status ttystat;
+ size_t count = TTY_STATUS_COUNT;
+ error_t err = 0;
+
+ if (replyport != phys_reply)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ assert (open_pending != NOTPENDING);
+
+ if (returncode != 0)
+ {
+ report_carrier_error (returncode);
+
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ phys_reply = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_pi);
+ phys_reply_pi = 0;
+
+ open_pending = NOTPENDING;
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ if (open_pending == INITIAL)
+ {
+ /* Special handling for the first open */
+
+ assert (phys_device == MACH_PORT_NULL);
+ assert (phys_reply_writes == MACH_PORT_NULL);
+ assert (phys_reply_writes_pi == 0);
+ phys_device = device;
+ err = ports_create_port (phys_reply_class, term_bucket,
+ sizeof (struct port_info),
+ &phys_reply_writes_pi);
+ if (err)
+ {
+ open_pending = NOTPENDING;
+ pthread_mutex_unlock (&global_lock);
+ return err;
+ }
+ phys_reply_writes = ports_get_send_right (phys_reply_writes_pi);
+
+ /* Schedule our first read */
+ err = device_read_request_inband (phys_device, phys_reply, D_NOWAIT,
+ 0, vm_page_size);
+
+ input_pending = 1;
+ }
+ else
+ {
+ /* This was a fake open, only for the sake of assert DTR. */
+ device_close (device);
+ mach_port_deallocate (mach_task_self (), device);
+ }
+
+ device_get_status (phys_device, TTY_STATUS,
+ (dev_status_t)&ttystat, &count);
+ ttystat.tt_breakc = 0;
+ ttystat.tt_flags = TF_ANYP | TF_LITOUT | TF_NOHANG | TF_HUPCLS;
+ device_set_status (phys_device, TTY_STATUS,
+ (dev_status_t)&ttystat, TTY_STATUS_COUNT);
+
+ report_carrier_on ();
+ if (err)
+ devio_desert_dtr ();
+
+ open_pending = NOTPENDING;
+ pthread_mutex_unlock (&global_lock);
+
+ return 0;
+}
+
+/* Adjust physical state on the basis of the terminal state.
+ Where it isn't possible, mutate terminal state to match
+ reality. */
+static error_t
+devio_set_bits (struct termios *state)
+{
+ if (!(state->c_cflag & CIGNORE) && phys_device != MACH_PORT_NULL)
+ {
+ struct tty_status ttystat;
+ size_t cnt = TTY_STATUS_COUNT;
+
+ /* Find the current state. */
+ device_get_status (phys_device, TTY_STATUS, (dev_status_t) &ttystat, &cnt);
+ if (state->__ispeed)
+ real_speed_to_bogus_speed (state->__ispeed, &ttystat.tt_ispeed);
+ if (state->__ospeed)
+ real_speed_to_bogus_speed (state->__ospeed, &ttystat.tt_ospeed);
+
+ /* Try and set it. */
+ device_set_status (phys_device, TTY_STATUS,
+ (dev_status_t) &ttystat, TTY_STATUS_COUNT);
+
+ /* And now make termstate match reality. */
+ cnt = TTY_STATUS_COUNT;
+ device_get_status (phys_device, TTY_STATUS, (dev_status_t) &ttystat, &cnt);
+ state->__ispeed = bogus_speed_to_real_speed (ttystat.tt_ispeed);
+ state->__ospeed = bogus_speed_to_real_speed (ttystat.tt_ospeed);
+
+ /* Mach forces us to use the normal stop bit convention:
+ two bits at 110 bps; 1 bit otherwise. */
+ if (state->__ispeed == 110)
+ state->c_cflag |= CSTOPB;
+ else
+ state->c_cflag &= ~CSTOPB;
+
+ /* Figure out how to munge input, since we are unable to actually
+ affect what the hardware does. */
+ switch (state->c_cflag & CSIZE)
+ {
+ case CS5:
+ char_size_mask_xxx = 0x1f;
+ break;
+
+ case CS6:
+ char_size_mask_xxx = 0x3f;
+ break;
+
+ case CS7:
+ char_size_mask_xxx = 0x7f;
+ break;
+
+ case CS8:
+ default:
+ char_size_mask_xxx = 0xff;
+ break;
+ }
+ if (state->c_cflag & PARENB)
+ char_size_mask_xxx |= 0x80;
+ }
+ return 0;
+}
+
+static error_t
+devio_mdmctl (int how, int bits)
+{
+ int oldbits, newbits;
+ size_t cnt;
+ if ((how == MDMCTL_BIS) || (how == MDMCTL_BIC))
+ {
+ cnt = TTY_MODEM_COUNT;
+ device_get_status (phys_device, TTY_MODEM,
+ (dev_status_t) &oldbits, &cnt);
+ if (cnt < TTY_MODEM_COUNT)
+ oldbits = 0; /* what else can we do? */
+ }
+
+ if (how == MDMCTL_BIS)
+ newbits = (oldbits | bits);
+ else if (how == MDMCTL_BIC)
+ newbits = (oldbits &= ~bits);
+ else
+ newbits = bits;
+
+ device_set_status (phys_device, TTY_MODEM,
+ (dev_status_t) &newbits, TTY_MODEM_COUNT);
+
+ return 0;
+}
+
+static error_t
+devio_mdmstate (int *state)
+{
+ int bits;
+ size_t cnt = TTY_MODEM_COUNT;
+ device_get_status (phys_device, TTY_MODEM, (dev_status_t) &bits, &cnt);
+ if (cnt == TTY_MODEM_COUNT)
+ *state = bits;
+ else
+ *state = 0;
+ return 0;
+}
+
+
+/* Unused stubs */
+kern_return_t
+device_read_reply (mach_port_t port,
+ kern_return_t retcode,
+ io_buf_ptr_t data,
+ mach_msg_type_number_t cnt)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+device_write_reply (mach_port_t replyport,
+ kern_return_t retcode,
+ int written)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+ports_do_mach_notify_send_once (struct port_info *pi)
+{
+ error_t err;
+
+ pthread_mutex_lock (&global_lock);
+
+ if (pi->port_right == phys_reply_writes)
+ {
+ err = 0;
+ devio_start_output ();
+ }
+ else if (pi->port_right == phys_reply)
+ {
+ if (input_pending)
+ {
+ /* xxx */
+ char msg[] = "Term input check happened\r\n";
+ int foo;
+ device_write_inband (phys_device, 0, 0, msg, sizeof msg, &foo);
+ /* end xxx */
+
+ input_pending = 0;
+
+ err = device_read_request_inband (phys_device, phys_reply,
+ D_NOWAIT, 0, vm_page_size);
+ if (err)
+ devio_desert_dtr ();
+ else
+ input_pending = 1;
+ }
+ else if (open_pending != NOTPENDING)
+ {
+ open_pending = NOTPENDING;
+
+ report_carrier_on ();
+ report_carrier_off ();
+
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ phys_reply = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_pi);
+ phys_reply_pi = 0;
+ }
+ err = 0;
+ }
+ else
+ err = EOPNOTSUPP;
+
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+
+const struct bottomhalf devio_bottom =
+{
+ TERM_ON_MACHDEV,
+ devio_init,
+ devio_fini,
+ NULL,
+ devio_start_output,
+ devio_set_break,
+ devio_clear_break,
+ devio_abandon_physical_output,
+ devio_suspend_physical_output,
+ devio_pending_output_size,
+ devio_notice_input_flushed,
+ devio_assert_dtr,
+ devio_desert_dtr,
+ devio_set_bits,
+ devio_mdmctl,
+ devio_mdmstate,
+};
diff --git a/term/hurdio.c b/term/hurdio.c
new file mode 100644
index 00000000..b34854cd
--- /dev/null
+++ b/term/hurdio.c
@@ -0,0 +1,652 @@
+/*
+ Copyright (C) 1995,96,98,99,2000,01,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG and Marcus Brinkmann.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* Handle carrier dropped (at least EIO errors in read, write) correctly. */
+
+#include <termios.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <pthread.h>
+
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/io.h>
+#include <hurd/tioctl.h>
+
+#include "term.h"
+
+
+/* The thread asserting the DTR and performing all reads. Only
+ different from MACH_PORT_NULL if thread is live and blocked. */
+thread_t reader_thread = MACH_PORT_NULL;
+
+/* The Hurd file_t representing the terminal. If this is not
+ MACH_PORT_NULL, it has the additional meaning that the DTR is
+ asserted. */
+static file_t ioport = MACH_PORT_NULL;
+
+/* Each bit represents a supported tioctl call in the underlying node.
+ If we detect that a tioctl is not supported, we clear the bit in
+ tioc_caps (which is initialized at every open). */
+#define TIOC_CAP_OUTQ 0x001
+#define TIOC_CAP_START 0x002
+#define TIOC_CAP_STOP 0x004
+#define TIOC_CAP_FLUSH 0x008
+#define TIOC_CAP_CBRK 0x010
+#define TIOC_CAP_SBRK 0x020
+#define TIOC_CAP_MODG 0x040
+#define TIOC_CAP_MODS 0x080
+#define TIOC_CAP_GETA 0x100
+#define TIOC_CAP_SETA 0x200
+#define TIOC_CAP_GWINSZ 0x400
+unsigned int tioc_caps;
+
+/* The thread performing all writes. Only different from
+ MACH_PORT_NULL if thread is live and blocked. */
+thread_t writer_thread = MACH_PORT_NULL;
+
+/* This flag is set if the output was suspended. */
+static int output_stopped;
+static pthread_cond_t hurdio_writer_condition;
+
+/* Hold the amount of bytes that are currently in the progress of
+ being written. May be set to zero while you hold the global lock
+ to drain the pending output buffer. */
+size_t npending_output;
+
+/* True if we should assert the dtr. */
+int assert_dtr;
+static pthread_cond_t hurdio_assert_dtr_condition;
+
+
+/* Forward */
+static error_t hurdio_desert_dtr ();
+static void *hurdio_reader_loop (void *arg);
+static void *hurdio_writer_loop (void *arg);
+static error_t hurdio_set_bits (struct termios *state);
+
+
+static error_t
+hurdio_init (void)
+{
+ pthread_t thread;
+ error_t err;
+
+ pthread_cond_init (&hurdio_writer_condition, NULL);
+ pthread_cond_init (&hurdio_assert_dtr_condition, NULL);
+
+ err = pthread_create (&thread, NULL, hurdio_reader_loop, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+ err = pthread_create (&thread, NULL, hurdio_writer_loop, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+ return 0;
+}
+
+static error_t
+hurdio_fini (void)
+{
+ hurdio_desert_dtr ();
+ writer_thread = MACH_PORT_NULL;
+ /* XXX destroy reader thread too */
+ return 0;
+}
+
+static error_t
+hurdio_gwinsz (struct winsize *size)
+{
+ if (tioc_caps & TIOC_CAP_GWINSZ)
+ {
+ error_t err = tioctl_tiocgwinsz (ioport, size);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ {
+ tioc_caps &= ~TIOC_CAP_GWINSZ;
+ err = EOPNOTSUPP;
+ }
+ return err;
+ }
+ return EOPNOTSUPP;
+}
+
+
+/* Assert the DTR if necessary. Must be called with global lock held. */
+static void
+wait_for_dtr (void)
+{
+ while (!assert_dtr)
+ pthread_hurd_cond_wait_np (&hurdio_assert_dtr_condition, &global_lock);
+ assert_dtr = 0;
+
+ if (tty_arg == 0)
+ ioport = termctl->underlying;
+ else
+ {
+ /* Open the file in blocking mode, so that the carrier is
+ established as well. */
+ ioport = file_name_lookup (tty_arg, O_READ|O_WRITE, 0);
+ if (ioport == MACH_PORT_NULL)
+ {
+ report_carrier_error (errno);
+ return;
+ }
+ }
+
+
+ error_t err;
+ struct termios state = termstate;
+
+ /* Assume that we have a full blown terminal initially. */
+ tioc_caps = ~0;
+
+ /* Set terminal in raw mode etc. */
+ err = hurdio_set_bits (&state);
+ if (err)
+ report_carrier_error (err);
+ else
+ {
+ termstate = state;
+
+ /* Signal that we have a carrier. */
+ report_carrier_on ();
+
+ /* Signal that the writer thread should resume its work. */
+ pthread_cond_broadcast (&hurdio_writer_condition);
+ }
+}
+
+
+/* Read and enqueue input characters. Is also responsible to assert
+ the DTR if necessary. */
+static void *
+hurdio_reader_loop (void *arg)
+{
+ /* XXX The input buffer has 256 bytes. */
+#define BUFFER_SIZE 256
+ char buffer[BUFFER_SIZE];
+ char *data;
+ size_t datalen;
+ error_t err;
+
+ pthread_mutex_lock (&global_lock);
+ reader_thread = mach_thread_self ();
+
+ while (1)
+ {
+ /* We can only start when the DTR has been asserted. */
+ while (ioport == MACH_PORT_NULL)
+ wait_for_dtr ();
+ pthread_mutex_unlock (&global_lock);
+
+ data = buffer;
+ datalen = BUFFER_SIZE;
+
+ err = io_read (ioport, &data, &datalen, -1, BUFFER_SIZE);
+
+ pthread_mutex_lock (&global_lock);
+ /* Error or EOF can mean the carrier has been dropped. */
+ if (err || !datalen)
+ hurdio_desert_dtr ();
+ else
+ {
+ if (termstate.c_cflag & CREAD)
+ {
+ int i;
+
+ for (i = 0; i < datalen; i++)
+ if (input_character (data[i]))
+ break;
+ }
+
+ if (data != buffer)
+ vm_deallocate (mach_task_self(), (vm_address_t) data, datalen);
+ }
+ }
+#undef BUFFER_SIZE
+
+ return 0;
+}
+
+
+/* Output characters. */
+static void *
+hurdio_writer_loop (void *arg)
+{
+ /* XXX The output buffer has 256 bytes. */
+#define BUFFER_SIZE 256
+ char *bufp;
+ char pending_output[BUFFER_SIZE];
+ size_t amount;
+ error_t err;
+ int size;
+ int npending_output_copy;
+ mach_port_t ioport_copy;
+
+ pthread_mutex_lock (&global_lock);
+ writer_thread = mach_thread_self ();
+
+ while (1)
+ {
+ while (writer_thread != MACH_PORT_NULL
+ && (ioport == MACH_PORT_NULL || !qsize (outputq)
+ || output_stopped))
+ pthread_hurd_cond_wait_np (&hurdio_writer_condition, &global_lock);
+ if (writer_thread == MACH_PORT_NULL) /* A sign to die. */
+ return 0;
+
+ /* Copy characters onto PENDING_OUTPUT, not bothering
+ those already there. */
+ size = qsize (outputq);
+
+ if (size + npending_output > BUFFER_SIZE)
+ size = BUFFER_SIZE - npending_output;
+
+ bufp = pending_output + npending_output;
+ npending_output += size;
+ /* We need to save these values, as otherwise there are races
+ with hurdio_abandon_physical_output or hurdio_desert_dtr,
+ which might overwrite the static variables. */
+ npending_output_copy = npending_output;
+ ioport_copy = ioport;
+ mach_port_mod_refs (mach_task_self (), ioport_copy,
+ MACH_PORT_RIGHT_SEND, 1);
+
+ while (size--)
+ *bufp++ = dequeue (outputq);
+
+ /* Submit all the outstanding characters to the I/O port. */
+ pthread_mutex_unlock (&global_lock);
+ err = io_write (ioport_copy, pending_output, npending_output_copy,
+ -1, &amount);
+ pthread_mutex_lock (&global_lock);
+
+ mach_port_mod_refs (mach_task_self (), ioport_copy,
+ MACH_PORT_RIGHT_SEND, -1);
+ if (err)
+ hurdio_desert_dtr ();
+ else
+ {
+ /* Note that npending_output might be set to null in the
+ meantime by hurdio_abandon_physical_output. */
+ if (amount >= npending_output)
+ {
+ npending_output = 0;
+ pthread_cond_broadcast (outputq->wait);
+ pthread_cond_broadcast (&select_alert);
+ }
+ else
+ {
+ /* Copy the characters that didn't get output
+ to the front of the array. */
+ npending_output -= amount;
+ memmove (pending_output, pending_output + amount,
+ npending_output);
+ }
+ }
+ }
+#undef BUFFER_SIZE
+
+ return 0;
+}
+
+
+/* If there are characters on the output queue, then send them. Is
+ called with global lock held. */
+static error_t
+hurdio_start_output ()
+{
+ /* If the output was suspended earlier and not anymore, we have to
+ tell the underlying port to resume it. */
+ if (output_stopped && !(termflags & USER_OUTPUT_SUSP))
+ {
+ if (tioc_caps & TIOC_CAP_START)
+ {
+ error_t err = tioctl_tiocstart (ioport);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_START;
+ }
+ output_stopped = 0;
+ }
+ pthread_cond_broadcast (&hurdio_writer_condition);
+ return 0;
+}
+
+
+/* Stop carrier on the line. Is called with global lock held. */
+static error_t
+hurdio_set_break ()
+{
+ if (tioc_caps & TIOC_CAP_SBRK)
+ {
+ error_t err = tioctl_tiocsbrk (ioport);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_SBRK;
+ else if (err)
+ return err;
+ }
+ return 0;
+}
+
+
+/* Reassert carrier on the line. Is called with global lock held. */
+static error_t
+hurdio_clear_break ()
+{
+ if (tioc_caps & TIOC_CAP_CBRK)
+ {
+ error_t err = tioctl_tioccbrk (ioport);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_CBRK;
+ else if (err)
+ return err;
+ }
+ return 0;
+}
+
+
+/* This is called when output queues are being flushed. But there may
+ be pending output which is sitting in a device buffer or other
+ place lower down than the terminal's output queue; so this is
+ called to flush whatever other such things may be going on. Is
+ called with global lock held. */
+static error_t
+hurdio_abandon_physical_output ()
+{
+ if (tioc_caps & TIOC_CAP_FLUSH)
+ {
+ error_t err = tioctl_tiocflush (ioport, O_WRITE);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_FLUSH;
+ else if (err)
+ return err;
+ }
+
+ /* Make sure that an incomplete write will not be finished.
+ hurdio_writer_loop must take care that meddling with
+ npending_output here does not introduce any races. */
+ npending_output = 0;
+ return 0;
+}
+
+
+/* Tell the underlying port to suspend all pending output, and stop
+ output in the bottom handler as well. Is called with the global
+ lock held. */
+static error_t
+hurdio_suspend_physical_output ()
+{
+ if (!output_stopped)
+ {
+ if (tioc_caps & TIOC_CAP_STOP)
+ {
+ error_t err = tioctl_tiocstop (ioport);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_STOP;
+ else if (err)
+ return err;
+ }
+ output_stopped = 1;
+ }
+ return 0;
+}
+
+/* This is called to notify the bottom half when an input flush has
+ occurred. It is necessary to support pty packet mode. */
+static error_t
+hurdio_notice_input_flushed ()
+{
+ if (tioc_caps & TIOC_CAP_FLUSH)
+ {
+ error_t err = tioctl_tiocflush (ioport, O_READ);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_FLUSH;
+ else if (err)
+ return err;
+ }
+ return 0;
+}
+
+
+/* Determine the number of bytes of output pending. */
+static int
+hurdio_pending_output_size ()
+{
+ int queue_size = 0;
+
+ if (tioc_caps & TIOC_CAP_OUTQ)
+ {
+ error_t err = tioctl_tiocoutq (ioport, &queue_size);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_OUTQ;
+ else if (err)
+ queue_size = 0;
+ }
+ /* We can not get the correct number, so let's try a guess. */
+ return queue_size + npending_output;
+}
+
+
+/* Desert the DTR. Is called with global lock held. */
+static error_t
+hurdio_desert_dtr ()
+{
+ if (writer_thread != MACH_PORT_NULL)
+ hurd_thread_cancel (writer_thread);
+ if (reader_thread != MACH_PORT_NULL)
+ hurd_thread_cancel (reader_thread);
+ if (ioport != MACH_PORT_NULL && tty_arg)
+ {
+ mach_port_deallocate (mach_task_self (), ioport);
+ ioport = MACH_PORT_NULL;
+ }
+ /* If we are called after hurdio_assert_dtr before the reader thread
+ had a chance to wake up and open the port, we can prevent it from
+ doing so by clearing this flag. */
+ assert_dtr = 0;
+ report_carrier_off ();
+ return 0;
+}
+
+
+static error_t
+hurdio_assert_dtr ()
+{
+ if (ioport == MACH_PORT_NULL)
+ {
+ assert_dtr = 1;
+ pthread_cond_signal (&hurdio_assert_dtr_condition);
+ }
+
+ return 0;
+}
+
+
+/* Adjust physical state on the basis of the terminal state.
+ Where it isn't possible, mutate terminal state to match
+ reality. */
+static error_t
+hurdio_set_bits (struct termios *state)
+{
+ error_t err;
+ struct termios ttystat;
+ /* This structure equals how the Hurd tioctl_tiocgeta/seta split up
+ a termios structure into RPC arguments. */
+ struct hurd_termios
+ {
+ modes_t modes;
+ ccs_t ccs;
+ speeds_t speeds;
+ } *hurd_ttystat = (struct hurd_termios *) &ttystat;
+
+ if (!(state->c_cflag & CIGNORE) && ioport != MACH_PORT_NULL)
+ {
+
+ /* If we can not get the terminal state, it doesn't make sense
+ to attempt to change it. Even if we could change it we
+ wouldn't know what changes took effect. */
+ if (!(tioc_caps & TIOC_CAP_GETA))
+ /* XXX Maybe return an error here, but then we must do the
+ right thing in users.c. */
+ return 0;
+
+ err = tioctl_tiocgeta (ioport, hurd_ttystat->modes,
+ hurd_ttystat->ccs, hurd_ttystat->speeds);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ {
+ tioc_caps &= ~TIOC_CAP_GETA;
+ /* XXX Maybe return an error here, but then we must do the
+ right thing in users.c. */
+ return 0;
+ }
+ else if (err)
+ return err;
+
+ /* If possible, change the state. Otherwise we will just make
+ termstate match reality below. */
+ if (tioc_caps & TIOC_CAP_SETA)
+ {
+ if (state->__ispeed)
+ hurd_ttystat->speeds[0] = state->__ispeed;
+ if (state->__ospeed)
+ hurd_ttystat->speeds[1] = state->__ospeed;
+ cfmakeraw (&ttystat);
+ ttystat.c_cflag = state->c_cflag &~ HUPCL;
+
+ err = tioctl_tiocseta (ioport, hurd_ttystat->modes,
+ hurd_ttystat->ccs, hurd_ttystat->speeds);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_SETA;
+ else if (err)
+ return err;
+
+ /* Refetch the terminal state. */
+ err = tioctl_tiocgeta (ioport, hurd_ttystat->modes,
+ hurd_ttystat->ccs, hurd_ttystat->speeds);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_GETA;
+ else if (err)
+ return err;
+ }
+
+ /* And now make termstate match reality. */
+ *state = ttystat;
+ }
+
+ return 0;
+}
+
+/* Diddle the modem control bits. If HOW is MDMCTL_BIC, the bits set
+ in BITS should be cleared. If HOW is MDMCTL_BIS, the bits in BITS
+ should be set. Otherwise, bits that are set in BITS should be set,
+ and the others cleared. */
+static error_t
+hurdio_mdmctl (int how, int bits)
+{
+ error_t err;
+ int oldbits, newbits;
+
+ if (tioc_caps & TIOC_CAP_MODS)
+ {
+ if ((how == MDMCTL_BIS) || (how == MDMCTL_BIC))
+ {
+ if (tioc_caps & TIOC_CAP_MODG)
+ {
+ error_t err = tioctl_tiocmodg (ioport, &oldbits);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ {
+ tioc_caps &= ~TIOC_CAP_MODG;
+ return EOPNOTSUPP;
+ }
+ else if (err)
+ return err;
+ }
+ else
+ return EOPNOTSUPP;
+ }
+
+ if (how == MDMCTL_BIS)
+ newbits = (oldbits | bits);
+ else if (how == MDMCTL_BIC)
+ newbits = (oldbits &= ~bits);
+ else
+ newbits = bits;
+
+ err = tioctl_tiocmods (ioport, newbits);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_MODS;
+ else if (err)
+ return err;
+ }
+ return 0;
+}
+
+
+static int
+hurdio_mdmstate ()
+{
+ int oldbits;
+
+ if (tioc_caps & TIOC_CAP_MODG)
+ {
+ error_t err = tioctl_tiocmodg (ioport, &oldbits);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_MODG;
+ else if (err)
+ return 0; /* XXX What else can we do? */
+ }
+ return 0;
+}
+
+
+
+const struct bottomhalf hurdio_bottom =
+{
+ TERM_ON_HURDIO,
+ hurdio_init,
+ hurdio_fini,
+ hurdio_gwinsz,
+ hurdio_start_output,
+ hurdio_set_break,
+ hurdio_clear_break,
+ hurdio_abandon_physical_output,
+ hurdio_suspend_physical_output,
+ hurdio_pending_output_size,
+ hurdio_notice_input_flushed,
+ hurdio_assert_dtr,
+ hurdio_desert_dtr,
+ hurdio_set_bits,
+ hurdio_mdmctl,
+ hurdio_mdmstate,
+};
diff --git a/term/main.c b/term/main.c
new file mode 100644
index 00000000..9cc32d4d
--- /dev/null
+++ b/term/main.c
@@ -0,0 +1,451 @@
+/* main.c - A translator that emulates a terminal.
+ Copyright (C) 1995,96,97,2000,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "term.h"
+#include <hurd.h>
+#include <fcntl.h>
+#include <hurd/trivfs.h>
+#include <stdio.h>
+#include <argp.h>
+#include <hurd/fsys.h>
+#include <string.h>
+#include <error.h>
+#include <inttypes.h>
+#include <argz.h>
+
+#include <version.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (term);
+
+int trivfs_fstype = FSTYPE_TERM;
+int trivfs_fsid = 0;
+int trivfs_support_read = 1;
+int trivfs_support_write = 1;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = O_READ|O_WRITE;
+
+enum tty_type { T_NONE = 0, T_DEVICE, T_HURDIO, T_PTYMASTER, T_PTYSLAVE };
+static const char *const tty_type_names[] =
+{
+ [T_DEVICE] = "device",
+ [T_HURDIO] = "hurdio",
+ [T_PTYMASTER] = "pty-master",
+ [T_PTYSLAVE] = "pty-slave",
+};
+
+
+/* The argument line options. */
+char *tty_name;
+enum tty_type tty_type;
+char *tty_arg;
+dev_t rdev;
+
+int
+demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ extern int term_server (mach_msg_header_t *, mach_msg_header_t *);
+ extern int tioctl_server (mach_msg_header_t *, mach_msg_header_t *);
+ extern int device_reply_server (mach_msg_header_t *, mach_msg_header_t *);
+
+ return (trivfs_demuxer (inp, outp)
+ || term_server (inp, outp)
+ || tioctl_server (inp, outp)
+ || device_reply_server (inp, outp));
+}
+
+static struct argp_option options[] =
+{
+ {"rdev", 'n', "ID", 0,
+ "The stat rdev number for this node; may be either a"
+ " single integer, or of the form MAJOR,MINOR"},
+ {"name", 'N', "NAME", 0,
+ "The name of this node, to be returned by term_get_nodename."},
+ {"type", 'T', "TYPE", 0,
+ "Backend type, see below. This determines the meaning of the argument."},
+ {0}
+};
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ struct
+ {
+ dev_t rdev;
+ int rdev_set;
+ enum tty_type type;
+ char *name;
+ char *arg;
+ } *const v = state->hook;
+
+ switch (opt)
+ {
+ default:
+ return ARGP_ERR_UNKNOWN;
+
+ case ARGP_KEY_INIT:
+ state->hook = calloc (1, sizeof *v);
+ break;
+ case ARGP_KEY_FINI:
+ free (v);
+ state->hook = 0;
+ break;
+
+ case 'n':
+ {
+ char *start = arg;
+ char *end;
+
+ v->rdev = strtoumax (start, &end, 0);
+ if (*end == ',')
+ {
+ /* MAJOR,MINOR form. */
+ start = end;
+ v->rdev = (rdev << 8) + strtoul (start, &end, 0);
+ }
+
+ if (end == start || *end != '\0')
+ {
+ argp_error (state, "%s: Invalid argument to --rdev", arg);
+ return EINVAL;
+ }
+
+ v->rdev_set = 1;
+ }
+ break;
+
+ case 'N':
+ v->name = arg;
+ break;
+
+ case ARGP_KEY_ARG:
+ if (!v->name && state->input == 0)
+ v->name = arg;
+ else if (!v->type && state->input == 0)
+ {
+ case 'T':
+ if (!strcmp (arg, "device"))
+ v->type = T_DEVICE;
+ else if (!strcmp (arg, "hurdio"))
+ v->type = T_HURDIO;
+ else if (!strcmp (arg, "pty-master"))
+ v->type = T_PTYMASTER;
+ else if (!strcmp (arg, "pty-slave"))
+ v->type = T_PTYSLAVE;
+ else
+ {
+ argp_error (state, "Invalid terminal type");
+ return EINVAL;
+ }
+ }
+ else if (!v->arg)
+ v->arg = arg;
+ else
+ {
+ argp_error (state, "Too many arguments");
+ return EINVAL;
+ }
+ break;
+
+ case ARGP_KEY_END:
+ if ((v->type && v->type != T_HURDIO && v->arg == 0)
+ || (state->input == 0 && v->name == 0))
+ {
+ argp_error (state, "Too few arguments");
+ return EINVAL;
+ }
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ /* Apply the values we've collected. */
+ if (v->rdev_set)
+ rdev = v->rdev;
+ if (v->name)
+ {
+ free (tty_name);
+ tty_name = strdup (v->name);
+ }
+ if (state->input == 0) /* This is startup time. */
+ {
+ tty_type = v->type ?: T_HURDIO;
+ tty_arg = v->arg ? strdup (v->arg) : 0;
+ }
+ else if (v->type || v->arg)
+ {
+ /* Dynamic backend switch. */
+ if (!v->type)
+ v->type = T_HURDIO;
+ switch (v->type)
+ {
+ case T_PTYMASTER:
+ case T_PTYSLAVE:
+ /* Cannot dynamically switch to pty flavors. */
+ return EINVAL;
+ default:
+ break;
+ }
+ switch (tty_type)
+ {
+ case T_PTYMASTER:
+ case T_PTYSLAVE:
+ /* Cannot dynamically switch from pty flavors either. */
+ return EINVAL;
+ default:
+ break;
+ }
+
+ pthread_mutex_lock (&global_lock);
+ (*bottom->fini) ();
+
+ tty_type = v->type;
+ switch (tty_type)
+ {
+ case T_DEVICE:
+ bottom = &devio_bottom;
+ break;
+ case T_HURDIO:
+ bottom = &hurdio_bottom;
+ break;
+ default:
+ assert (! "impossible type");
+ break;
+ }
+ free (tty_arg);
+ tty_arg = strdup (v->arg);
+ error_t err = (*bottom->init) ();
+ if (err == 0 && (termflags & TTY_OPEN))
+ err = (*bottom->assert_dtr) ();
+ pthread_mutex_unlock (&global_lock);
+ return err;
+ }
+ break;
+
+ case ARGP_KEY_ERROR:
+ break;
+ }
+ return 0;
+}
+
+static struct argp term_argp =
+ { options, parse_opt, "NAME TYPE ARG",
+ "A translator that implements POSIX termios discipline.\v"
+ "Possible values for TYPE:\n"
+ " device Use Mach device ARG for underlying i/o.\n"
+ " hurdio Use file ARG for i/o, underlying node if no ARG.\n"
+ " pty-master Master for slave at ARG.\n"\
+ " pty-slave Slave for master at ARG.\n"\
+ "\n"
+ "The default type is `hurdio', so no arguments uses the underlying node.\n"
+ "The filename of the node that the translator is attached to should be\n"
+ "supplied in NAME.\n"
+ };
+
+struct argp *trivfs_runtime_argp = &term_argp;
+
+error_t
+trivfs_append_args (struct trivfs_control *fsys,
+ char **argz, size_t *argz_len)
+{
+ error_t err = 0;
+
+ if (rdev)
+ {
+ char buf[64];
+ snprintf (buf, sizeof buf, "--rdev=%#jx", (uintmax_t)rdev);
+ err = argz_add (argz, argz_len, buf);
+ }
+
+ if (!err && tty_name)
+ err = argz_add (argz, argz_len, "--name")
+ ?: argz_add (argz, argz_len, tty_name);
+
+ if (!err && tty_type != T_HURDIO)
+ err = argz_add (argz, argz_len, "--type")
+ ?: argz_add (argz, argz_len, tty_type_names[tty_type]);
+
+ if (!err && tty_arg)
+ err = argz_add (argz, argz_len, tty_arg);
+
+ return err;
+}
+
+int
+main (int argc, char **argv)
+{
+ struct port_class *ourclass, *ourcntlclass;
+ struct port_class *peerclass, *peercntlclass;
+ struct trivfs_control **ourcntl, **peercntl;
+ mach_port_t bootstrap, right;
+ struct stat st;
+ error_t err;
+ int openmode;
+
+ term_bucket = ports_create_bucket ();
+
+ trivfs_add_control_port_class (&tty_cntl_class);
+ trivfs_add_control_port_class (&pty_cntl_class);
+ trivfs_add_protid_port_class (&tty_class);
+ trivfs_add_protid_port_class (&pty_class);
+
+ cttyid_class = ports_create_class (0, 0);
+
+ init_users ();
+
+ argp_parse (&term_argp, argc, argv, 0, 0, 0);
+
+ switch (tty_type)
+ {
+ case T_DEVICE:
+ bottom = &devio_bottom;
+ ourclass = tty_class;
+ ourcntlclass = tty_cntl_class;
+ ourcntl = &termctl;
+ peerclass = 0;
+ peercntlclass = 0;
+ peercntl = 0;
+ openmode = 0;
+ break;
+
+ case T_HURDIO:
+ bottom = &hurdio_bottom;
+ ourclass = tty_class;
+ ourcntlclass = tty_cntl_class;
+ ourcntl = &termctl;
+ peerclass = 0;
+ peercntlclass = 0;
+ peercntl = 0;
+
+ /* We don't want to have a writable peropen on the underlying node
+ when we'll never use it. Ideally, we shouldn't open one until we
+ do need it, in case it has an affect on the underlying node (like
+ keeping DTR high and such). */
+ openmode = O_RDWR;
+ break;
+
+ case T_PTYMASTER:
+ bottom = &ptyio_bottom;
+ ourclass = pty_class;
+ ourcntlclass = pty_cntl_class;
+ ourcntl = &ptyctl;
+ peerclass = tty_class;
+ peercntlclass = tty_cntl_class;
+ peercntl = &termctl;
+ openmode = 0;
+ break;
+
+ case T_PTYSLAVE:
+ bottom = &ptyio_bottom;
+ ourclass = tty_class;
+ ourcntlclass = tty_cntl_class;
+ ourcntl = &termctl;
+ peerclass = pty_class;
+ peercntlclass = pty_cntl_class;
+ peercntl = &ptyctl;
+ openmode = 0;
+ break;
+
+ default:
+ /* Should not happen. */
+ error (1, 0, "Unknown terminal type");
+ /*NOTREACHED*/
+ return 1;
+ }
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "Must be started as a translator");
+
+ /* Set our node. */
+ err = trivfs_startup (bootstrap, openmode,
+ ourcntlclass, term_bucket, ourclass, term_bucket,
+ ourcntl);
+ if (err)
+ error (1, err, "Starting translator");
+
+ /* For ptys, the nodename depends on which half is used. For now just use
+ the hook to store the nodename. */
+ (*ourcntl)->hook = tty_name;
+
+ /* Set peer. */
+ if (peerclass)
+ {
+ char *peer_name = tty_arg;
+ file_t file = file_name_lookup (peer_name, O_CREAT|O_NOTRANS, 0666);
+
+ if (file == MACH_PORT_NULL)
+ err = errno;
+
+ if (! err)
+ err = trivfs_create_control (file, peercntlclass, term_bucket,
+ peerclass, term_bucket, peercntl);
+ if (! err)
+ {
+ right = ports_get_send_right (*peercntl);
+ err = file_set_translator (file, 0, FS_TRANS_EXCL | FS_TRANS_SET,
+ 0, 0, 0, right, MACH_MSG_TYPE_COPY_SEND);
+ mach_port_deallocate (mach_task_self (), right);
+ }
+
+ if (err)
+ error (1, err, "%s", peer_name);
+
+ (*peercntl)->hook = peer_name;
+ ports_port_deref (*peercntl);
+ }
+
+ memset (&termstate, 0, sizeof (termstate));
+ termflags = NO_CARRIER | NO_OWNER;
+ pthread_mutex_init (&global_lock, NULL);
+
+ /* Initialize status from underlying node. */
+ err = io_stat ((*ourcntl)->underlying, &st);
+ if (err)
+ {
+ /* We cannot stat the underlying node. Fallback to the defaults. */
+ term_owner = term_group = 0;
+ term_mode = (bottom == &ptyio_bottom ? DEFFILEMODE : S_IRUSR | S_IWUSR);
+ }
+ else
+ {
+ term_owner = st.st_uid;
+ term_group = st.st_gid;
+ term_mode = (st.st_mode & ACCESSPERMS);
+ }
+ term_mode |= S_IFCHR | S_IROOT;
+
+ inputq = create_queue (256, QUEUE_LOWAT, QUEUE_HIWAT);
+
+ rawq = create_queue (256, QUEUE_LOWAT, QUEUE_HIWAT);
+
+ outputq = create_queue (256, QUEUE_LOWAT, QUEUE_HIWAT);
+
+ err = (*bottom->init) ();
+ if (err)
+ error (1, err, "Initializing bottom handler");
+
+ pthread_cond_init (&carrier_alert, NULL);
+ pthread_cond_init (&select_alert, NULL);
+
+ /* Launch. */
+ ports_manage_port_operations_multithread (term_bucket, demuxer, 0, 0, 0);
+
+ return 0;
+}
diff --git a/term/mig-mutate.h b/term/mig-mutate.h
new file mode 100644
index 00000000..a6b99fe6
--- /dev/null
+++ b/term/mig-mutate.h
@@ -0,0 +1,25 @@
+/*
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Justus Winter.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Only CPP macro definitions should go in this file. */
+
+#define IO_INTRAN trivfs_protid_t trivfs_begin_using_protid (io_t)
+#define IO_DESTRUCTOR trivfs_end_using_protid (trivfs_protid_t)
+#define TIOCTL_IMPORTS import "../libtrivfs/mig-decls.h";
+#define TERM_IMPORTS import "../libtrivfs/mig-decls.h";
diff --git a/term/munge.c b/term/munge.c
new file mode 100644
index 00000000..7e08e2d2
--- /dev/null
+++ b/term/munge.c
@@ -0,0 +1,770 @@
+/*
+ Copyright (C) 1995, 1996, 1999, 2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "term.h"
+#include <termios.h>
+#include <unistd.h>
+#include <signal.h>
+#include <ctype.h>
+#include <string.h>
+
+/* Number of characters in the rawq which have been
+ echoed continuously without intervening output. */
+int echo_qsize;
+
+/* Where the output_psize was when echo_qsize was last 0. */
+int echo_pstart;
+
+/* PHYSICAL position of the terminal cursor */
+int output_psize;
+
+/* Actually drop character onto output queue. This should be the
+ only place where we actually enqueue characters on the output queue;
+ it is responsible for keeping track of cursor positions. */
+inline void
+poutput (int c)
+{
+ if (termflags & FLUSH_OUTPUT)
+ return; /* never mind */
+
+ if ((c >= ' ') && (c < '\177'))
+ output_psize++;
+ else if (c == '\r')
+ output_psize = 0;
+ else if (c == '\t')
+ {
+ output_psize++;
+ while (output_psize % 8)
+ output_psize++;
+ }
+ else if (c == '\b')
+ output_psize--;
+
+ enqueue (&outputq, c);
+}
+
+/* Place C on output queue, doing normal output processing.
+ Only echo routines should directly call this function. Others
+ should call write_character below. */
+void
+output_character (int c)
+{
+ int oflag = termstate.c_oflag;
+
+ /* One might think we should turn of INHDERASE here, but, no
+ in U*x it is only turned off by echoed characters.
+ See echo_char in input.c. */
+ if (oflag & OPOST)
+ {
+ /* Characters we write specially */
+ if ((oflag & ONLCR) && c == '\n')
+ {
+ poutput ('\r');
+ poutput ('\n');
+ }
+ else if (!external_processing && (oflag & OXTABS) && c == '\t')
+ {
+ poutput (' ');
+ while (output_psize % 8)
+ poutput (' ');
+ }
+ else if ((oflag & ONOEOT) && c == CHAR_EOT)
+ ;
+ else if ((oflag & OTILDE) && c == '~')
+ {
+ poutput ('\\');
+ poutput ('`');
+ }
+ else if ((oflag & OLCASE) && isalpha (c))
+ {
+ if (isupper (c))
+ poutput ('\\');
+ else
+ c = toupper (c);
+ poutput (c);
+ }
+ else
+ poutput (c);
+ }
+ else
+ poutput (c);
+}
+
+/* Place C on output queue, doing normal processing. */
+void
+write_character (int c)
+{
+ output_character (c);
+ echo_qsize = 0;
+ echo_pstart = output_psize;
+}
+
+/* Report the width of character C as printed by output_character,
+ if output_psize were at LOC. . */
+int
+output_width (int c, int loc)
+{
+ int oflag = termstate.c_oflag;
+
+ if (oflag & OPOST)
+ {
+ if ((oflag & OTILDE) && c == '~')
+ return 2;
+ if ((oflag & OLCASE) && isalpha (c) && isupper (c))
+ return 2;
+ }
+ if (c == '\t')
+ {
+ int n = loc + 1;
+ while (n % 8)
+ n++;
+ return n - loc;
+ }
+ if ((c >= ' ') && (c < '\177'))
+ return 1;
+ return 0;
+}
+
+
+
+/* For ICANON mode, this holds the edited line. */
+struct queue *rawq;
+
+/* For each character in this table, if the element is set, then
+ the character is EVEN */
+char const char_parity[] =
+{
+ 1, 0, 0, 1, 0, 1, 1, 0, /* nul - bel */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* bs - si */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* dle - etb */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* can - us */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* sp - ' */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* ( - / */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* 0 - 7 */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* 8 - ? */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* @ - G */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* H - O */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* P - W */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* X - _ */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* ` - g */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* h - o */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* p - w */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* x - del */
+};
+#define checkevenpar(c) (((c)&0x80) \
+ ? !char_parity[(c)&0x7f] \
+ : char_parity[(c)&0x7f])
+#define checkoddpar(c) (((c)&0x80) \
+ ? char_parity[(c)&0x7f] \
+ : !char_parity[(c)&0x7f])
+
+
+
+/* These functions are used by both echo and erase code */
+
+/* Tell if we should echo a character at all */
+static inline int
+echo_p (char c, int quoted)
+{
+ if (external_processing)
+ return 0;
+ return ((termstate.c_lflag & ECHO)
+ || (c == '\n' && (termstate.c_lflag & ECHONL) && !quoted));
+}
+
+/* Tell if this character deserves double-width cntrl processing */
+static inline int
+echo_double (char c, int quoted)
+{
+ return (iscntrl (c) && (termstate.c_lflag & ECHOCTL)
+ && !((c == '\n' || c == '\t') && !quoted));
+}
+
+/* Do a single C-h SPC C-h sequence */
+static inline void
+write_erase_sequence ()
+{
+ poutput ('\b');
+ poutput (' ');
+ poutput ('\b');
+}
+
+/* Echo a single character to the output */
+/* If this is an echo of a character which is being hard-erased,
+ set hderase. If this is a newline or tab which should not receive
+ their normal special processing, set quoted. */
+static void
+echo_char (char c, int hderase, int quoted)
+{
+ echo_qsize++;
+
+ if (echo_p (c, quoted))
+ {
+ if (!hderase && (termflags & INSIDE_HDERASE))
+ {
+ write_character ('/');
+ termflags &= ~INSIDE_HDERASE;
+ }
+
+ if (hderase && !(termflags & INSIDE_HDERASE))
+ {
+ output_character ('\\');
+ termflags |= INSIDE_HDERASE;
+ }
+
+ /* Control characters that should use caret-letter */
+ if (echo_double (c, quoted))
+ {
+ output_character ('^');
+ output_character (c ^ CTRL_BIT);
+ }
+ else
+ output_character (c);
+ }
+}
+
+
+/* Re-echo the current rawq preceded by the VREPRINT char. */
+static inline void
+reprint_line ()
+{
+ short *cp;
+
+ if (termstate.c_cc[VREPRINT] != _POSIX_VDISABLE)
+ echo_char (termstate.c_cc[VREPRINT], 0, 0);
+ else
+ echo_char (CHAR_DC2, 0, 0);
+ echo_char ('\n', 0, 0);
+
+ echo_qsize = 0;
+ echo_pstart = output_psize;
+
+ for (cp = rawq->cs; cp != rawq->ce; cp++)
+ echo_char (unquote_char (*cp), 0, char_quoted_p (*cp));
+}
+
+/* Erase a single character off the end of the rawq, and delete
+ it from the screen appropriately. Set ERASE_CHAR if this
+ is being done by the VERASE character, to that character. */
+static void
+erase_1 (char erase_char)
+{
+ int quoted;
+ char c;
+ quoted_char cq;
+
+ if (qsize (rawq) == 0)
+ return;
+
+ cq = queue_erase (rawq);
+ c = unquote_char (cq);
+ quoted = char_quoted_p (cq);
+
+ if (!echo_p (c, quoted))
+ return;
+
+ /* The code for WERASE knows that we echo erase_char iff
+ !ECHOPRT && !ECHO. */
+
+ if (echo_qsize--)
+ {
+ if (termstate.c_lflag & ECHOPRT)
+ echo_char (c, 1, quoted);
+ else if (!(termstate.c_lflag & ECHOE) && erase_char)
+ echo_char (erase_char, 0, 0);
+ else
+ {
+ int nerase;
+
+ if (echo_double (c, quoted))
+ nerase = 2;
+ else if (c == '\t')
+ {
+ quoted_char *cp;
+ int loc = echo_pstart;
+
+ for (cp = rawq->ce - echo_qsize; cp != rawq->ce; cp++)
+ loc += (echo_double (unquote_char (*cp), char_quoted_p (*cp))
+ ? 2
+ : output_width (*cp, loc));
+ nerase = output_psize - loc;
+ }
+ else
+ nerase = output_width (c, output_psize);
+
+ while (nerase--)
+ write_erase_sequence ();
+ }
+ if (echo_qsize == 0)
+ assert (echo_pstart == output_psize);
+ }
+ else
+ reprint_line ();
+}
+
+
+/* Place newly input character C on the input queue. If all remaining
+ pending input characters should be dropped, return 1; else return
+ 0. */
+int
+input_character (int c)
+{
+ int lflag = termstate.c_lflag;
+ int iflag = termstate.c_iflag;
+ int cflag = termstate.c_cflag;
+ cc_t *cc = termstate.c_cc;
+ struct queue **qp = (lflag & ICANON) ? &rawq : &inputq;
+ int flush = 0;
+
+ /* Handle parity errors */
+ if ((iflag & INPCK)
+ && ((cflag & PARODD) ? checkoddpar (c) : checkevenpar (c)))
+ {
+ if (iflag & IGNPAR)
+ goto alldone;
+ else if (iflag & PARMRK)
+ {
+ enqueue_quote (qp, CHAR_USER_QUOTE);
+ enqueue_quote (qp, '\0');
+ enqueue_quote (qp, c);
+ goto alldone;
+ }
+ else
+ c = 0;
+ }
+
+ /* Check to see if we should send IXOFF */
+ if ((iflag & IXOFF)
+ && !qavail (*qp)
+ && (cc[VSTOP] != _POSIX_VDISABLE))
+ {
+ poutput (cc[VSTOP]);
+ termflags |= SENT_VSTOP;
+ }
+
+ /* Character mutations */
+ if (!(iflag & ISTRIP) && (iflag & PARMRK) && (c == CHAR_USER_QUOTE))
+ enqueue_quote (qp, CHAR_USER_QUOTE); /* cause doubling */
+
+ if (iflag & ISTRIP)
+ c &= 0x7f;
+
+ /* Handle LNEXT right away */
+ if (!external_processing && (termflags & LAST_LNEXT))
+ {
+ enqueue_quote (qp, c);
+ echo_char (c, 0, 1);
+ termflags &= ~LAST_LNEXT;
+ goto alldone;
+ }
+
+ /* Mutate ILCASE */
+ if (!external_processing && (iflag & ILCASE) && isalpha(c))
+ {
+ if (termflags & LAST_SLASH)
+ erase_1 (0); /* remove the slash from input */
+ else
+ c = isupper(c) ? tolower (c) : c;
+ }
+
+ /* IEXTEN control chars */
+ if (!external_processing && (lflag & IEXTEN))
+ {
+ if (CCEQ (cc[VLNEXT], c))
+ {
+ if (lflag & ECHO)
+ {
+ poutput ('^');
+ poutput ('\b');
+ }
+ termflags |= LAST_LNEXT;
+ goto alldone;
+ }
+
+ if (CCEQ (cc[VDISCARD], c))
+ {
+ if (termflags & FLUSH_OUTPUT)
+ termflags &= ~FLUSH_OUTPUT;
+ else
+ {
+ drop_output ();
+ poutput (cc[VDISCARD]);
+ termflags |= FLUSH_OUTPUT;
+ }
+ goto alldone;
+ }
+ }
+
+ /* Signals */
+ if (!external_processing && (lflag & ISIG))
+ {
+ if (CCEQ (cc[VINTR], c) || CCEQ (cc[VQUIT], c))
+ {
+ if (!(lflag & NOFLSH))
+ {
+ drop_output ();
+ clear_queue (inputq);
+ clear_queue (rawq);
+ flush = 1;
+ }
+ echo_char (c, 0, 0);
+ echo_qsize = 0;
+ echo_pstart = output_psize;
+ send_signal (CCEQ (cc[VINTR], c) ? SIGINT : SIGQUIT);
+ goto alldone;
+ }
+
+ if (CCEQ (cc[VSUSP], c))
+ {
+ if (!(lflag & NOFLSH))
+ {
+ flush = 1;
+ clear_queue (inputq);
+ clear_queue (rawq);
+ }
+ echo_char (c, 0, 0);
+ echo_qsize = 0;
+ echo_pstart = output_psize;
+ send_signal (SIGTSTP);
+ goto alldone;
+ }
+ }
+
+ /* IXON */
+ if (!external_processing && (iflag & IXON))
+ {
+ if (CCEQ (cc[VSTOP], c))
+ {
+ if (CCEQ(cc[VSTART], c) && (termflags & USER_OUTPUT_SUSP))
+ /* Toggle if VSTART == VSTOP. Alldone code always turns
+ off USER_OUTPUT_SUSP. */
+ goto alldone;
+
+ termflags |= USER_OUTPUT_SUSP;
+ (*bottom->suspend_physical_output) ();
+ return flush;
+ }
+ if (CCEQ (cc[VSTART], c))
+ goto alldone;
+ }
+
+ if (!external_processing)
+ {
+ /* Newline and carriage-return frobbing */
+ if (c == '\r')
+ {
+ if (iflag & ICRNL)
+ c = '\n';
+ else if (iflag & IGNCR)
+ goto alldone;
+ }
+ else if ((c == '\n') && (iflag & INLCR))
+ c = '\r';
+
+ }
+
+ /* Canonical mode processing */
+ if (!external_processing && (lflag & ICANON))
+ {
+ if (CCEQ (cc[VERASE], c))
+ {
+ if (qsize(rawq))
+ erase_1 (c);
+ if (!(termflags & LAST_SLASH)
+ || !(lflag & IEXTEN))
+ goto alldone;
+ }
+
+ if (CCEQ (cc[VKILL], c))
+ {
+ if (!(termflags & LAST_SLASH)
+ || !(lflag & IEXTEN))
+ {
+ if ((lflag & ECHOKE) && !(lflag & ECHOPRT)
+ && (echo_qsize == qsize (rawq)))
+ {
+ while (output_psize > echo_pstart)
+ write_erase_sequence ();
+ }
+ else
+ {
+ echo_char (c, 0, 0);
+ if ((lflag & ECHOK) || (lflag & ECHOKE))
+ echo_char ('\n', 0, 0);
+ }
+ clear_queue (rawq);
+ echo_qsize = 0;
+ echo_pstart = output_psize;
+ termflags &= ~(LAST_SLASH|LAST_LNEXT|INSIDE_HDERASE);
+ goto alldone;
+ }
+ else
+ erase_1 (0); /* remove \ */
+ }
+
+ if (CCEQ (cc[VWERASE], c))
+ {
+ /* If we are not going to echo the erase, then
+ echo a WERASE character right now. (If we
+ passed it to erase_1; it would echo it multiple
+ times.) */
+ if (!(lflag & (ECHOPRT|ECHOE)))
+ echo_char (cc[VWERASE], 0, 1);
+
+ /* Erase whitespace */
+ while (qsize (rawq) && isblank (unquote_char (rawq->ce[-1])))
+ erase_1 (0);
+
+ /* Erase word. */
+ if (lflag & ALTWERASE)
+ /* For ALTWERASE, we erase back to the first blank */
+ while (qsize (rawq) && !isblank (unquote_char (rawq->ce[-1])))
+ erase_1 (0);
+ else
+ /* For regular WERASE, we erase back to the first nonalpha/_ */
+ while (qsize (rawq) && !isblank (unquote_char (rawq->ce[-1]))
+ && (isalnum (unquote_char (rawq->ce[-1]))
+ || (unquote_char (rawq->ce[-1]) != '_')))
+ erase_1 (0);
+
+ goto alldone;
+ }
+
+ if (CCEQ (cc[VREPRINT], c) && (lflag & IEXTEN))
+ {
+ reprint_line ();
+ goto alldone;
+ }
+
+ if (CCEQ (cc[VSTATUS], c) && (lflag & ISIG) && (lflag & IEXTEN))
+ {
+ send_signal (SIGINFO);
+ goto alldone;
+ }
+ }
+
+ /* Now we have a character intended as input. See if it will fit. */
+ if (!qavail (*qp))
+ {
+ if (iflag & IMAXBEL)
+ poutput ('\a');
+ else
+ {
+ /* Drop everything */
+ drop_output ();
+ clear_queue (inputq);
+ clear_queue (rawq);
+ echo_pstart = 0;
+ echo_qsize = 0;
+ flush = 1;
+ }
+ goto alldone;
+ }
+
+ /* Echo */
+ echo_char (c, 0, 0);
+ if (CCEQ (cc[VEOF], c) && (lflag & ECHO))
+ {
+ /* Special bizarre echo processing for VEOF character. */
+ int n;
+ n = echo_double (c, 0) ? 2 : output_width (c, output_psize);
+ while (n--)
+ poutput ('\b');
+ }
+
+ /* Put character on input queue */
+ enqueue (qp, c);
+
+ /* Check for break characters in canonical input processing */
+ if (lflag & ICANON)
+ {
+ if (CCEQ (cc[VEOL], c)
+ || CCEQ (cc[VEOL2], c)
+ || CCEQ (cc[VEOF], c)
+ || c == '\n')
+ /* Make input available */
+ while (qsize (rawq))
+ enqueue (&inputq, dequeue (rawq));
+ }
+
+alldone:
+ /* Restart output */
+ if ((iflag & IXANY) || (CCEQ (cc[VSTART], c)))
+ termflags &= ~USER_OUTPUT_SUSP;
+ (*bottom->start_output) ();
+
+ return flush;
+}
+
+
+/* This is called by the lower half when a break is received. */
+void
+input_break ()
+{
+ struct queue **qp = termstate.c_lflag & ICANON ? &rawq : &inputq;
+
+ /* Don't do anything if IGNBRK is set. */
+ if (termstate.c_iflag & IGNBRK)
+ return;
+
+ /* If BRKINT is set, then flush queues and send SIGINT. */
+ if (termstate.c_iflag & BRKINT)
+ {
+ drop_output ();
+ /* XXX drop pending input How?? */
+ send_signal (SIGINT);
+ return;
+ }
+
+ /* A break is then read as a null byte; marked specially if PARMRK is set. */
+ if (termstate.c_iflag & PARMRK)
+ {
+ enqueue_quote (qp, CHAR_USER_QUOTE);
+ enqueue_quote (qp, '\0');
+ }
+ enqueue_quote (qp, '\0');
+}
+
+/* Called when a character is received with a framing error. */
+void
+input_framing_error (int c)
+{
+ struct queue **qp = termstate.c_lflag & ICANON ? &rawq : &inputq;
+
+ /* Ignore it if IGNPAR is set. */
+ if (termstate.c_iflag & IGNPAR)
+ return;
+
+ /* If PARMRK is set, pass it specially marked. */
+ if (termstate.c_iflag & PARMRK)
+ {
+ enqueue_quote (qp, CHAR_USER_QUOTE);
+ enqueue_quote (qp, '\0');
+ enqueue_quote (qp, c);
+ }
+ else
+ /* Otherwise, it looks like a null. */
+ enqueue_quote (qp, '\0');
+}
+
+/* Copy the characters in RAWQ to the end of INPUTQ and clear RAWQ. */
+void
+copy_rawq ()
+{
+ while (qsize (rawq))
+ enqueue (&inputq, dequeue (rawq));
+}
+
+/* Process all the characters in INPUTQ as if they had just been read. */
+void
+rescan_inputq ()
+{
+ short *buf;
+ int i, n;
+
+ n = qsize (inputq);
+ buf = alloca (n * sizeof (quoted_char));
+ memcpy (buf, inputq->cs, n * sizeof (quoted_char));
+ clear_queue (inputq);
+
+ for (i = 0; i < n; i++)
+ input_character (unquote_char (buf[i]));
+}
+
+
+error_t
+drop_output ()
+{
+ error_t err = (*bottom->abandon_physical_output) ();
+ if (!err)
+ clear_queue (outputq);
+ return err;
+}
+
+
+error_t
+drain_output ()
+{
+ int cancel = 0;
+
+ while ((qsize (outputq) || (*bottom->pending_output_size) ())
+ && (!(termflags & NO_CARRIER) || (termstate.c_cflag & CLOCAL))
+ && !cancel)
+ cancel = pthread_hurd_cond_wait_np (outputq->wait, &global_lock);
+
+ return cancel ? EINTR : 0;
+}
+
+/* Create and return a new queue. */
+struct queue *
+create_queue (int size, int lowat, int hiwat)
+{
+ struct queue *q;
+
+ q = malloc (sizeof (struct queue) + size * sizeof (quoted_char));
+ assert (q);
+
+ q->susp = 0;
+ q->lowat = lowat;
+ q->hiwat = hiwat;
+ q->cs = q->ce = q->array;
+ q->arraylen = size;
+ q->wait = malloc (sizeof (pthread_cond_t));
+ assert (q->wait);
+
+ pthread_cond_init (q->wait, NULL);
+ return q;
+}
+
+/* Make Q able to have more characters added to it. */
+struct queue *
+reallocate_queue (struct queue *q)
+{
+ int len;
+ struct queue *newq;
+
+ len = qsize (q);
+
+ if (len < q->arraylen / 2)
+ {
+ /* Shift the characters to the front of
+ the queue. */
+ memmove (q->array, q->cs, len * sizeof (quoted_char));
+ q->cs = q->array;
+ q->ce = q->cs + len;
+ }
+ else
+ {
+ /* Make the queue twice as large. */
+ newq = malloc (sizeof (struct queue)
+ + q->arraylen * 2 * sizeof (quoted_char));
+ newq->susp = q->susp;
+ newq->lowat = q->lowat;
+ newq->hiwat = q->hiwat;
+ newq->cs = newq->array;
+ newq->ce = newq->array + len;
+ newq->arraylen = q->arraylen * 2;
+ newq->wait = q->wait;
+ memmove (newq->array, q->cs, len * sizeof (quoted_char));
+ free (q);
+ q = newq;
+ }
+ return q;
+}
diff --git a/term/ourmsg.defs b/term/ourmsg.defs
new file mode 100644
index 00000000..ad55073b
--- /dev/null
+++ b/term/ourmsg.defs
@@ -0,0 +1,14 @@
+/* Private specialized presentation of msg.defs for term server. */
+
+#define routine simpleroutine
+
+/* The reason for this= is to prevent errors for get_init_port,
+ get_init_ports, get_init_int, get_init_ints, get_dtable, and get_fd.
+ We don't use those, so we're safe in breaking them. */
+#define out /* empty */
+
+#define USERPREFIX nowait_
+
+#define msg ourmsg /* Change subsystem name for headers et al. */
+
+#include <hurd/msg.defs>
diff --git a/term/ptyio.c b/term/ptyio.c
new file mode 100644
index 00000000..211e70a8
--- /dev/null
+++ b/term/ptyio.c
@@ -0,0 +1,643 @@
+/*
+ Copyright (C) 1995, 1996, 1999, 2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <sys/ioctl.h>
+#include <string.h>
+#include <hurd/ports.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "term.h"
+#include "tioctl_S.h"
+
+/* Set if we need a wakeup when tty output has been done */
+static int pty_read_blocked = 0;
+
+/* Wake this up when tty output occurs and pty_read_blocked is set */
+static pthread_cond_t pty_read_wakeup = PTHREAD_COND_INITIALIZER;
+
+static pthread_cond_t pty_select_wakeup = PTHREAD_COND_INITIALIZER;
+
+/* Set if "dtr" is on. */
+static int dtr_on = 0;
+
+/* Set if packet mode is on. */
+static int packet_mode = 0;
+
+/* Set if user ioctl mode is on. */
+static int user_ioctl_mode = 0;
+
+/* Byte to send to user in packet mode or user ioctl mode. */
+static char control_byte = 0;
+
+static int output_stopped = 0;
+
+static int pktnostop = 0;
+
+static int ptyopen = 0;
+
+static int nptyperopens = 0;
+
+
+static error_t
+ptyio_init (void)
+{
+ pty_select_alert = &pty_select_wakeup;
+ return 0;
+}
+
+error_t
+pty_open_hook (struct trivfs_control *cntl,
+ struct iouser *user,
+ int flags)
+{
+ if ((flags & (O_READ|O_WRITE)) == 0)
+ return 0;
+
+ pthread_mutex_lock (&global_lock);
+
+ if (ptyopen)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EBUSY;
+ }
+
+ ptyopen = 1;
+
+ /* Re-initialize pty state. */
+ external_processing = 0;
+ packet_mode = 0;
+ user_ioctl_mode = 0;
+ control_byte = 0;
+ pktnostop = 0;
+
+ pthread_mutex_unlock (&global_lock);
+
+ return 0;
+}
+
+error_t
+pty_po_create_hook (struct trivfs_peropen *po)
+{
+ pthread_mutex_lock (&global_lock);
+ if (po->openmodes & (O_READ | O_WRITE))
+ {
+ nptyperopens++;
+ report_carrier_on ();
+ }
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+pty_po_destroy_hook (struct trivfs_peropen *po)
+{
+ pthread_mutex_lock (&global_lock);
+ if ((po->openmodes & (O_READ | O_WRITE)) == 0)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+ }
+ nptyperopens--;
+ if (!nptyperopens)
+ {
+ ptyopen = 0;
+ report_carrier_off ();
+ }
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+static inline void
+wake_reader ()
+{
+ if (pty_read_blocked)
+ {
+ pty_read_blocked = 0;
+ pthread_cond_broadcast (&pty_read_wakeup);
+ pthread_cond_broadcast (&pty_select_wakeup);
+ }
+}
+
+
+/* Lower half for tty node */
+
+static error_t
+ptyio_start_output ()
+{
+ if (packet_mode && output_stopped && (!(termflags & USER_OUTPUT_SUSP)))
+ {
+ control_byte &= ~TIOCPKT_STOP;
+ control_byte |= TIOCPKT_START;
+ output_stopped = 0;
+ }
+ wake_reader ();
+ return 0;
+}
+
+static error_t
+ptyio_abandon_physical_output ()
+{
+ if (packet_mode)
+ {
+ control_byte |= TIOCPKT_FLUSHWRITE;
+ wake_reader ();
+ }
+ return 0;
+}
+
+static error_t
+ptyio_suspend_physical_output ()
+{
+ if (packet_mode)
+ {
+ control_byte &= ~TIOCPKT_START;
+ control_byte |= TIOCPKT_STOP;
+ output_stopped = 1;
+ wake_reader ();
+ }
+ return 0;
+}
+
+static int
+ptyio_pending_output_size ()
+{
+ /* We don't maintain any pending output buffer separate from the outputq. */
+ return 0;
+}
+
+static error_t
+ptyio_notice_input_flushed ()
+{
+ if (packet_mode)
+ {
+ control_byte |= TIOCPKT_FLUSHREAD;
+ wake_reader ();
+ }
+ return 0;
+}
+
+static error_t
+ptyio_assert_dtr ()
+{
+ dtr_on = 1;
+ return 0;
+}
+
+static error_t
+ptyio_desert_dtr ()
+{
+ dtr_on = 0;
+ wake_reader ();
+ return 0;
+}
+
+static error_t
+ptyio_set_bits (struct termios *state)
+{
+ if (packet_mode)
+ {
+ int wakeup = 0;
+ int stop = ((state->c_iflag & IXON)
+ && CCEQ (state->c_cc[VSTOP], CHAR_DC3)
+ && CCEQ (state->c_cc[VSTART], CHAR_DC1));
+
+ if (external_processing)
+ {
+ control_byte |= TIOCPKT_IOCTL;
+ wakeup = 1;
+ }
+
+ if (pktnostop && stop)
+ {
+ pktnostop = 0;
+ control_byte |= TIOCPKT_DOSTOP;
+ control_byte &= ~TIOCPKT_NOSTOP;
+ wakeup = 1;
+ }
+ else if (!pktnostop && !stop)
+ {
+ pktnostop = 1;
+ control_byte |= TIOCPKT_NOSTOP;
+ control_byte &= ~TIOCPKT_DOSTOP;
+ wakeup = 1;
+ }
+
+ if (wakeup)
+ wake_reader ();
+ }
+ return 0;
+}
+
+/* These do nothing. In BSD the associated ioctls get errors, but
+ I'd rather just ignore them. */
+static error_t
+ptyio_set_break ()
+{
+ return 0;
+}
+
+static error_t
+ptyio_clear_break ()
+{
+ return 0;
+}
+
+static error_t
+ptyio_mdmctl (int a, int b)
+{
+ return 0;
+}
+
+static error_t
+ptyio_mdmstate (int *state)
+{
+ *state = 0;
+ return 0;
+}
+
+const struct bottomhalf ptyio_bottom =
+{
+ TERM_ON_MASTERPTY,
+ ptyio_init,
+ NULL, /* fini */
+ NULL, /* gwinsz */
+ ptyio_start_output,
+ ptyio_set_break,
+ ptyio_clear_break,
+ ptyio_abandon_physical_output,
+ ptyio_suspend_physical_output,
+ ptyio_pending_output_size,
+ ptyio_notice_input_flushed,
+ ptyio_assert_dtr,
+ ptyio_desert_dtr,
+ ptyio_set_bits,
+ ptyio_mdmctl,
+ ptyio_mdmstate,
+};
+
+
+
+
+/* I/O interface for pty master nodes */
+
+/* Validation has already been done by trivfs_S_io_read. */
+error_t
+pty_io_read (struct trivfs_protid *cred,
+ char **data,
+ mach_msg_type_number_t *datalen,
+ mach_msg_type_number_t amount)
+{
+ int size;
+
+ pthread_mutex_lock (&global_lock);
+
+ if ((cred->po->openmodes & O_READ) == 0)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EBADF;
+ }
+
+ while (!control_byte
+ && (termflags & TTY_OPEN)
+ && (!qsize (outputq) || (termflags & USER_OUTPUT_SUSP)))
+ {
+ if (cred->po->openmodes & O_NONBLOCK)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EWOULDBLOCK;
+ }
+ pty_read_blocked = 1;
+ if (pthread_hurd_cond_wait_np (&pty_read_wakeup, &global_lock))
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EINTR;
+ }
+ }
+
+ if (!(termflags & TTY_OPEN))
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EIO;
+ }
+
+ if (control_byte)
+ {
+ size = 1;
+ if (packet_mode && (control_byte & TIOCPKT_IOCTL))
+ size += sizeof (struct termios);
+ }
+ else
+ {
+ size = qsize (outputq);
+ if (packet_mode || user_ioctl_mode)
+ size++;
+ }
+
+ if (size > amount)
+ size = amount;
+ if (size > *datalen)
+ *data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ *datalen = size;
+
+ if (control_byte)
+ {
+ **data = control_byte;
+ if (packet_mode && (control_byte & TIOCPKT_IOCTL))
+ memcpy (*data + 1, &termstate, size - 1);
+ control_byte = 0;
+ }
+ else
+ {
+ char *cp = *data;
+
+ if (packet_mode || user_ioctl_mode)
+ {
+ *cp++ = TIOCPKT_DATA;
+ --size;
+ }
+ while (size--)
+ *cp++ = dequeue (outputq);
+ }
+
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+
+/* Validation has already been done by trivfs_S_io_write. */
+error_t
+pty_io_write (struct trivfs_protid *cred,
+ char *data,
+ mach_msg_type_number_t datalen,
+ mach_msg_type_number_t *amount)
+{
+ int i, flush;
+ int cancel = 0;
+
+ pthread_mutex_lock (&global_lock);
+
+ if ((cred->po->openmodes & O_WRITE) == 0)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EBADF;
+ }
+
+ if (remote_input_mode)
+ {
+ /* Wait for the queue to be empty */
+ while (qsize (inputq) && !cancel)
+ {
+ if (cred->po->openmodes & O_NONBLOCK)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EWOULDBLOCK;
+ }
+ cancel = pthread_hurd_cond_wait_np (inputq->wait, &global_lock);
+ }
+ if (cancel)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EINTR;
+ }
+
+ for (i = 0; i < datalen; i++)
+ enqueue (&inputq, data[i]);
+
+ /* Extra garbage charater */
+ enqueue (&inputq, 0);
+ }
+ else if (termstate.c_cflag & CREAD)
+ for (i = 0; i < datalen; i++)
+ {
+ flush = input_character (data[i]);
+
+ if (flush)
+ {
+ if (packet_mode)
+ {
+ control_byte |= TIOCPKT_FLUSHREAD;
+ wake_reader ();
+ }
+ break;
+ }
+ }
+
+ pthread_mutex_unlock (&global_lock);
+
+ *amount = datalen;
+ return 0;
+}
+
+/* Validation has already been done by trivfs_S_io_readable */
+error_t
+pty_io_readable (size_t *amt)
+{
+ pthread_mutex_lock (&global_lock);
+ if (control_byte)
+ {
+ *amt = 1;
+ if (packet_mode && (control_byte & TIOCPKT_IOCTL))
+ *amt += sizeof (struct termios);
+ }
+ else
+ *amt = qsize (outputq);
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+/* Validation has already been done by trivfs_S_io_select. */
+error_t
+pty_io_select (struct trivfs_protid *cred, mach_port_t reply,
+ struct timespec *tsp, int *type)
+{
+ int avail = 0;
+ error_t err;
+
+ if (*type == 0)
+ return 0;
+
+ pthread_mutex_lock (&global_lock);
+
+ while (1)
+ {
+ if ((*type & SELECT_READ)
+ && (control_byte || qsize (outputq) || !(termflags & TTY_OPEN)))
+ avail |= SELECT_READ;
+
+ if ((*type & SELECT_URG) && control_byte)
+ avail |= SELECT_URG;
+
+ if ((*type & SELECT_WRITE) && (!remote_input_mode || !qsize (inputq)))
+ avail |= SELECT_WRITE;
+
+ if (avail)
+ {
+ *type = avail;
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ ports_interrupt_self_on_port_death (cred, reply);
+ pty_read_blocked = 1;
+ err = pthread_hurd_cond_timedwait_np (&pty_select_wakeup, &global_lock,
+ tsp);
+ if (err)
+ {
+ *type = 0;
+ pthread_mutex_unlock (&global_lock);
+
+ if (err == ETIMEDOUT)
+ err = 0;
+
+ return err;
+ }
+ }
+}
+
+error_t
+S_tioctl_tiocsig (struct trivfs_protid *cred,
+ int sig)
+{
+ if (!cred
+ || cred->pi.bucket != term_bucket
+ || cred->pi.class != pty_class)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ drop_output ();
+ clear_queue (inputq);
+ clear_queue (rawq);
+ ptyio_notice_input_flushed ();
+ send_signal (sig);
+
+ pthread_mutex_unlock (&global_lock);
+
+ return 0;
+}
+
+error_t
+S_tioctl_tiocpkt (struct trivfs_protid *cred,
+ int mode)
+{
+ error_t err;
+ if (!cred
+ || cred->pi.bucket != term_bucket
+ || cred->pi.class != pty_class)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ if (!!mode == !!packet_mode)
+ err = 0;
+ else if (mode && user_ioctl_mode)
+ err = EINVAL;
+ else
+ {
+ packet_mode = mode;
+ control_byte = 0;
+ err = 0;
+ }
+
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+error_t
+S_tioctl_tiocucntl (struct trivfs_protid *cred,
+ int mode)
+{
+ error_t err;
+ if (!cred
+ || cred->pi.bucket != term_bucket
+ || cred->pi.class != pty_class)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ if (!!mode == !!user_ioctl_mode)
+ err = 0;
+ else if (mode && packet_mode)
+ err = EINVAL;
+ else
+ {
+ user_ioctl_mode = mode;
+ control_byte = 0;
+ err = 0;
+ }
+
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+error_t
+S_tioctl_tiocremote (struct trivfs_protid *cred,
+ int how)
+{
+ if (!cred
+ || cred->pi.bucket != term_bucket
+ || cred->pi.class != pty_class)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ remote_input_mode = how;
+ drop_output ();
+ clear_queue (inputq);
+ clear_queue (rawq);
+ ptyio_notice_input_flushed ();
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+S_tioctl_tiocext (struct trivfs_protid *cred,
+ int mode)
+{
+ if (!cred
+ || cred->pi.bucket != term_bucket
+ || cred->pi.class != pty_class)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ if (mode && !external_processing)
+ {
+ if (packet_mode)
+ {
+ control_byte |= TIOCPKT_IOCTL;
+ wake_reader ();
+ }
+ external_processing = 1;
+ termstate.c_lflag |= EXTPROC;
+ }
+ else if (!mode && external_processing)
+ {
+ if (packet_mode)
+ {
+ control_byte |= TIOCPKT_IOCTL;
+ wake_reader ();
+ }
+ external_processing = 0;
+ termstate.c_lflag &= ~EXTPROC;
+ }
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
diff --git a/term/term.h b/term/term.h
new file mode 100644
index 00000000..df82b6c9
--- /dev/null
+++ b/term/term.h
@@ -0,0 +1,393 @@
+/*
+ Copyright (C) 1995,96,98,99, 2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <pthread.h>
+#include <assert.h>
+#include <errno.h>
+#include <hurd/trivfs.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <features.h>
+#include <hurd/hurd_types.h>
+
+#ifdef TERM_DEFINE_EI
+#define TERM_EI
+#else
+#define TERM_EI __extern_inline
+#endif
+
+#undef MDMBUF
+#undef ECHO
+#undef TOSTOP
+#undef FLUSHO
+#undef PENDIN
+#undef NOFLSH
+#include <termios.h>
+
+#define CHAR_EOT '\004' /* C-d */
+#define CHAR_DC1 '\021' /* C-q */
+#define CHAR_DC2 '\022' /* C-r */
+#define CHAR_DC3 '\023' /* C-s */
+#define CHAR_USER_QUOTE '\377' /* break quoting, etc. */
+
+/* This bit specifies control */
+#define CTRL_BIT 0x40
+
+/* XXX These belong in <termios.h> */
+#ifdef IUCLC
+#define ILCASE IUCLC
+#else
+#define ILCASE (1 << 14)
+#endif
+#ifdef OLCUC
+#define OLCASE OLCUC
+#else
+#define OLCASE (1 << 9)
+#endif
+#define OTILDE (1 << 10)
+
+/* used in mdmctl device call */
+#define MDMCTL_BIS 0
+#define MDMCTL_BIC 1
+#define MDMCTL_SET 2
+
+/* Directly user-visible state */
+struct termios termstate;
+
+/* Other state with the following bits: */
+long termflags;
+
+#define USER_OUTPUT_SUSP 0x00000001 /* user has suspended output */
+#define TTY_OPEN 0x00000002 /* someone has us open */
+#define LAST_SLASH 0x00000004 /* last input char was \ */
+#define LAST_LNEXT 0x00000008 /* last input char was VLNEXT */
+#define INSIDE_HDERASE 0x00000010 /* inside \.../ hardcopy erase pair */
+#define SENT_VSTOP 0x00000020 /* we've sent VSTOP to IXOFF peer */
+#define FLUSH_OUTPUT 0x00000040 /* user wants output flushed */
+#define NO_CARRIER 0x00000080 /* carrier is absent */
+#define EXCL_USE 0x00000100 /* user accessible exclusive use */
+#define NO_OWNER 0x00000200 /* there is no foreground_id */
+#define ICKY_ASYNC 0x00000400 /* some user has set O_ASYNC */
+
+/* Use a high watermark that allows about as much input as once as
+ other operating systems do. Using something just a bit smaller
+ than a power of 2 helps to make maximum use of the buffer and avoid
+ reallocation for just a few bytes. */
+#define QUEUE_LOWAT 200
+#define QUEUE_HIWAT 8100
+
+/* Global lock */
+pthread_mutex_t global_lock;
+
+/* Wakeup when NO_CARRIER turns off */
+pthread_cond_t carrier_alert;
+
+/* Wakeup for select */
+pthread_cond_t select_alert;
+
+/* Wakeup for pty select, if not null */
+pthread_cond_t *pty_select_alert;
+
+/* Bucket for all our ports. */
+struct port_bucket *term_bucket;
+
+/* Port class for tty control ports */
+struct port_class *tty_cntl_class;
+
+/* Port class for tty I/O ports */
+struct port_class *tty_class;
+
+/* Port class for ctty ID ports */
+struct port_class *cttyid_class;
+
+/* Port class for pty master ports */
+struct port_class *pty_class;
+
+/* Port class for pty control ports */
+struct port_class *pty_cntl_class;
+
+/* Trivfs control structure for the tty */
+struct trivfs_control *termctl;
+
+/* Trivfs control structure for the pty */
+struct trivfs_control *ptyctl;
+
+/* The queues we use */
+struct queue *inputq, *rawq, *outputq;
+
+/* Plain pass-through input */
+int remote_input_mode;
+
+/* External processing mode */
+int external_processing;
+
+/* Terminal owner */
+uid_t term_owner;
+
+/* Terminal group */
+uid_t term_group;
+
+/* Terminal mode */
+mode_t term_mode;
+
+
+/* XXX Including <sys/ioctl.h> or <hurd/ioctl_types.h> leads to "ECHO
+ undeclared" errors in munge.c or users.c. */
+struct winsize;
+
+/* Functions a bottom half defines */
+struct bottomhalf
+{
+ enum term_bottom_type type;
+ error_t (*init) (void);
+ error_t (*fini) (void);
+ error_t (*gwinsz) (struct winsize *size);
+ error_t (*start_output) (void);
+ error_t (*set_break) (void);
+ error_t (*clear_break) (void);
+ error_t (*abandon_physical_output) (void);
+ error_t (*suspend_physical_output) (void);
+ int (*pending_output_size) (void);
+ error_t (*notice_input_flushed) (void);
+ error_t (*assert_dtr) (void);
+ error_t (*desert_dtr) (void);
+ error_t (*set_bits) (struct termios *state);
+ error_t (*mdmctl) (int how, int bits);
+ error_t (*mdmstate) (int *state);
+};
+
+const struct bottomhalf *bottom;
+extern const struct bottomhalf devio_bottom, hurdio_bottom, ptyio_bottom;
+
+
+/* Character queues */
+#define QUEUE_QUOTE_MARK 0xf000
+typedef short quoted_char;
+struct queue
+{
+ int susp;
+ int lowat;
+ int hiwat;
+ short *cs, *ce;
+ int arraylen;
+ pthread_cond_t *wait;
+ quoted_char array[0];
+};
+
+struct queue *create_queue (int size, int lowat, int hiwat);
+
+extern int qsize (struct queue *q);
+extern int qavail (struct queue *q);
+extern void clear_queue (struct queue *q);
+extern quoted_char dequeue_quote (struct queue *q);
+extern char dequeue (struct queue *q);
+extern void enqueue_internal (struct queue **qp, quoted_char c);
+extern void enqueue (struct queue **qp, char c);
+extern void enqueue_quote (struct queue **qp, char c);
+extern char unquote_char (quoted_char c);
+extern int char_quoted_p (quoted_char c);
+extern short queue_erase (struct queue *q);
+
+#if defined(__USE_EXTERN_INLINES) || defined(TERM_DEFINE_EI)
+/* Return the number of characters in Q. */
+TERM_EI int
+qsize (struct queue *q)
+{
+ return q->ce - q->cs;
+}
+
+/* Return nonzero if characters can be added to Q. */
+TERM_EI int
+qavail (struct queue *q)
+{
+ return !q->susp;
+}
+
+/* Flush all the characters from Q. */
+TERM_EI void
+clear_queue (struct queue *q)
+{
+ q->susp = 0;
+ q->cs = q->ce = q->array;
+ pthread_cond_broadcast (q->wait);
+ pthread_cond_broadcast (&select_alert);
+ if (q == inputq && pty_select_alert != NULL)
+ pthread_cond_broadcast (pty_select_alert);
+}
+#endif /* Use extern inlines. */
+
+/* Should be below, but inlines need it. */
+void call_asyncs (int dir);
+
+#if defined(__USE_EXTERN_INLINES) || defined(TERM_DEFINE_EI)
+/* Return the next character off Q; leave the quoting bit on. */
+TERM_EI quoted_char
+dequeue_quote (struct queue *q)
+{
+ int beep = 0;
+
+ assert (qsize (q));
+ if (q->susp && (qsize (q) < q->lowat))
+ {
+ q->susp = 0;
+ beep = 1;
+ }
+ if (qsize (q) == 1)
+ beep = 1;
+ if (beep)
+ {
+ pthread_cond_broadcast (q->wait);
+ pthread_cond_broadcast (&select_alert);
+ if (q == inputq && pty_select_alert != NULL)
+ pthread_cond_broadcast (pty_select_alert);
+ else if (q == outputq)
+ call_asyncs (O_WRITE);
+ }
+ return *q->cs++;
+}
+
+/* Return the next character off Q. */
+TERM_EI char
+dequeue (struct queue *q)
+{
+ return dequeue_quote (q) & ~QUEUE_QUOTE_MARK;
+}
+#endif /* Use extern inlines. */
+
+struct queue *reallocate_queue (struct queue *);
+
+#if defined(__USE_EXTERN_INLINES) || defined(TERM_DEFINE_EI)
+/* Add C to *QP. */
+TERM_EI void
+enqueue_internal (struct queue **qp, quoted_char c)
+{
+ struct queue *q = *qp;
+
+ if (q->ce - q->array == q->arraylen)
+ q = *qp = reallocate_queue (q);
+
+ *q->ce++ = c;
+
+ if (qsize (q) == 1)
+ {
+ pthread_cond_broadcast (q->wait);
+ pthread_cond_broadcast (&select_alert);
+ if (q == inputq)
+ {
+ if (pty_select_alert != NULL)
+ pthread_cond_broadcast (pty_select_alert);
+ call_asyncs (O_READ);
+ }
+ }
+
+ if (!q->susp && (qsize (q) > q->hiwat))
+ q->susp = 1;
+}
+
+/* Add C to *QP. */
+TERM_EI void
+enqueue (struct queue **qp, char c)
+{
+ enqueue_internal (qp, c);
+}
+
+/* Add C to *QP, marking it with a quote. */
+TERM_EI void
+enqueue_quote (struct queue **qp, char c)
+{
+ enqueue_internal (qp, c | QUEUE_QUOTE_MARK);
+}
+
+/* Return the unquoted version of a quoted_char. */
+TERM_EI char
+unquote_char (quoted_char c)
+{
+ return c & ~QUEUE_QUOTE_MARK;
+}
+
+/* Tell if a quoted_char is actually quoted. */
+TERM_EI int
+char_quoted_p (quoted_char c)
+{
+ return c & QUEUE_QUOTE_MARK;
+}
+
+/* Remove the most recently enqueue character from Q; leaving
+ the quote mark on. */
+TERM_EI short
+queue_erase (struct queue *q)
+{
+ short answer;
+ int beep = 0;
+
+ assert (qsize (q));
+ answer = *--q->ce;
+ if (q->susp && (qsize (q) < q->lowat))
+ {
+ q->susp = 0;
+ beep = 1;
+ }
+ if (qsize (q) == 0)
+ beep = 1;
+ if (beep)
+ {
+ pthread_cond_broadcast (q->wait);
+ pthread_cond_broadcast (&select_alert);
+ if (q == inputq && pty_select_alert != NULL)
+ pthread_cond_broadcast (pty_select_alert);
+ }
+ return answer;
+}
+#endif /* Use extern inlines. */
+
+
+/* Functions devio is supposed to call */
+int input_character (int);
+void report_carrier_on (void);
+void report_carrier_off (void);
+void report_carrier_error (error_t);
+
+
+/* Other decls */
+error_t drop_output (void);
+void send_signal (int);
+error_t drain_output ();
+void output_character (int);
+void copy_rawq (void);
+void rescan_inputq (void);
+void write_character (int);
+void init_users (void);
+
+extern char *tty_arg;
+extern dev_t rdev;
+
+/* kludge--these are pty versions of trivfs_S_io_* functions called by
+ the real functions in users.c to do work for ptys. */
+error_t pty_io_write (struct trivfs_protid *, char *,
+ mach_msg_type_number_t, mach_msg_type_number_t *);
+error_t pty_io_read (struct trivfs_protid *, char **,
+ mach_msg_type_number_t *, mach_msg_type_number_t);
+error_t pty_io_readable (size_t *);
+error_t pty_io_select (struct trivfs_protid *, mach_port_t,
+ struct timespec *, int *);
+error_t pty_open_hook (struct trivfs_control *, struct iouser *, int);
+error_t pty_po_create_hook (struct trivfs_peropen *);
+error_t pty_po_destroy_hook (struct trivfs_peropen *);
diff --git a/term/users.c b/term/users.c
new file mode 100644
index 00000000..9bd51d05
--- /dev/null
+++ b/term/users.c
@@ -0,0 +1,2250 @@
+/*
+ Copyright (C) 1995,96,97,98,99,2000,01,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "term.h"
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <string.h>
+#include <fcntl.h>
+#include <hurd/trivfs.h>
+#include <pthread.h>
+#include <hurd.h>
+#include <stdio.h>
+#include <hurd/iohelp.h>
+#include <hurd/fshelp.h>
+#include <error.h>
+#include "ourmsg_U.h"
+
+
+#undef ECHO
+#undef MDMBUF
+#undef TOSTOP
+#undef FLUSHO
+#undef PENDIN
+#undef NOFLSH
+
+#include "term_S.h"
+#include "tioctl_S.h"
+#include "libtrivfs/trivfs_fs_S.h"
+#include "libtrivfs/trivfs_io_S.h"
+#include <sys/ioctl.h>
+
+#define TTYDEFCHARS
+#include <sys/ttydefaults.h>
+
+/* Count of active opens. */
+int nperopens;
+
+/* io_async requests. */
+struct async_req
+{
+ mach_port_t notify;
+ struct async_req *next;
+};
+struct async_req *async_requests;
+
+/* Number of peropens that have set the ICKY_ASYNC flags. */
+static int num_icky_async_peropens = 0;
+
+mach_port_t async_icky_id;
+mach_port_t async_id;
+struct port_info *cttyid;
+int foreground_id;
+
+struct winsize window_size;
+
+static int sigs_in_progress;
+static pthread_cond_t input_sig_wait = PTHREAD_COND_INITIALIZER;
+static int input_sig_wakeup;
+
+static error_t carrier_error;
+
+/* Attach this on the hook of any protid that is a ctty. */
+struct protid_hook
+{
+ int refcnt;
+ pid_t pid, pgrp, sid;
+};
+
+void
+init_users ()
+{
+ error_t err;
+
+ err = ports_create_port (cttyid_class, term_bucket,
+ sizeof (struct port_info), &cttyid);
+ if (err)
+ error (1, err, "Allocating cttyid");
+
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &async_icky_id);
+ /* Add a send right, since hurd_sig_post needs one. */
+ mach_port_insert_right (mach_task_self (),
+ async_icky_id, async_icky_id,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &async_id);
+ /* Add a send right, since hurd_sig_post needs one. */
+ mach_port_insert_right (mach_task_self (),
+ async_id, async_id, MACH_MSG_TYPE_MAKE_SEND);
+}
+
+
+static error_t
+check_access_hook (struct trivfs_control *cntl,
+ struct iouser *user,
+ mach_port_t realnode,
+ int *allowed)
+{
+ struct stat st;
+
+ pthread_mutex_lock (&global_lock);
+
+ st.st_uid = term_owner;
+ st.st_gid = term_group;
+ st.st_mode = term_mode;
+
+ *allowed = 0;
+ if (fshelp_access (&st, S_IREAD, user) == 0)
+ *allowed |= O_READ;
+ if (fshelp_access (&st, S_IWRITE, user) == 0)
+ *allowed |= O_WRITE;
+
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+error_t (*trivfs_check_access_hook) (struct trivfs_control *, struct iouser *,
+ mach_port_t, int *)
+ = check_access_hook;
+
+static error_t
+open_hook (struct trivfs_control *cntl,
+ struct iouser *user,
+ int flags)
+{
+ static int open_count = 0; /* XXX debugging */
+ int cancel = 0;
+ error_t err;
+
+ if (cntl == ptyctl)
+ return pty_open_hook (cntl, user, flags);
+
+ if ((flags & (O_READ|O_WRITE)) == 0)
+ return 0;
+
+ pthread_mutex_lock (&global_lock);
+
+ if (!(termflags & TTY_OPEN))
+ {
+ memset (&termstate, 0, sizeof termstate);
+
+ /* This is different from BSD: we don't turn on ISTRIP,
+ and we use CS8 rather than CS7|PARENB. */
+ termstate.c_iflag |= BRKINT | ICRNL | IMAXBEL | IXON | IXANY;
+ termstate.c_oflag |= OPOST | ONLCR | OXTABS;
+ termstate.c_lflag |= (ECHO | ICANON | ISIG | IEXTEN
+ | ECHOE|ECHOKE|ECHOCTL);
+ termstate.c_cflag |= CREAD | CS8 | HUPCL;
+
+ memcpy (termstate.c_cc, ttydefchars, NCCS);
+
+ memset (&window_size, 0, sizeof window_size);
+
+ termflags |= NO_OWNER;
+ }
+ else
+ {
+ assert (open_count > 0); /* XXX debugging */
+
+ if (termflags & EXCL_USE)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EBUSY;
+ }
+ }
+
+ open_count++; /* XXX debugging */
+
+ /* XXX debugging */
+ assert (! (termstate.c_oflag & OTILDE));
+
+ /* Assert DTR if necessary. */
+ if (termflags & NO_CARRIER)
+ {
+ err = (*bottom->assert_dtr) ();
+ if (err)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return err;
+ }
+ }
+
+ /* Wait for carrier to turn on. */
+ while (((termflags & NO_CARRIER) && !(termstate.c_cflag & CLOCAL))
+ && !(flags & O_NONBLOCK)
+ && !cancel)
+ cancel = pthread_hurd_cond_wait_np (&carrier_alert, &global_lock);
+
+ if (cancel)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EINTR;
+ }
+
+ err = carrier_error;
+ carrier_error = 0;
+
+ if (!err)
+ {
+ struct termios state = termstate;
+ err = (*bottom->set_bits) (&state);
+ if (!err)
+ {
+ termstate = state;
+ termflags |= TTY_OPEN;
+ }
+
+ if (bottom->gwinsz)
+ (*bottom->gwinsz) (&window_size);
+ }
+
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+error_t (*trivfs_check_open_hook) (struct trivfs_control *,
+ struct iouser *, int)
+ = open_hook;
+
+static error_t
+pi_create_hook (struct trivfs_protid *cred)
+{
+ if (cred->pi.class == pty_class)
+ return 0;
+
+ pthread_mutex_lock (&global_lock);
+ if (cred->hook)
+ ((struct protid_hook *)cred->hook)->refcnt++;
+ pthread_mutex_unlock (&global_lock);
+
+ return 0;
+}
+error_t (*trivfs_protid_create_hook) (struct trivfs_protid *) = pi_create_hook;
+
+static void
+pi_destroy_hook (struct trivfs_protid *cred)
+{
+ if (cred->pi.class == pty_class)
+ return;
+
+ pthread_mutex_lock (&global_lock);
+ if (cred->hook)
+ {
+ assert (((struct protid_hook *)cred->hook)->refcnt > 0);
+ if (--((struct protid_hook *)cred->hook)->refcnt == 0)
+ free (cred->hook);
+ }
+ pthread_mutex_unlock (&global_lock);
+}
+void (*trivfs_protid_destroy_hook) (struct trivfs_protid *) = pi_destroy_hook;
+
+static error_t
+po_create_hook (struct trivfs_peropen *po)
+{
+ if (po->cntl == ptyctl)
+ return pty_po_create_hook (po);
+
+ pthread_mutex_lock (&global_lock);
+ nperopens++;
+ if (po->openmodes & O_ASYNC)
+ {
+ termflags |= ICKY_ASYNC;
+ num_icky_async_peropens++;
+ call_asyncs (O_READ | O_WRITE);
+ }
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+error_t (*trivfs_peropen_create_hook) (struct trivfs_peropen *) =
+ po_create_hook;
+
+static void
+po_destroy_hook (struct trivfs_peropen *po)
+{
+ if (po->cntl == ptyctl)
+ {
+ pty_po_destroy_hook (po);
+ return;
+ }
+
+ pthread_mutex_lock (&global_lock);
+
+ if ((po->openmodes & O_ASYNC) && --num_icky_async_peropens == 0)
+ termflags &= ~ICKY_ASYNC;
+
+ nperopens--;
+ if (!nperopens && (termflags & TTY_OPEN))
+ {
+ /* Empty queues */
+ clear_queue (inputq);
+ clear_queue (rawq);
+ (*bottom->notice_input_flushed) ();
+
+ drain_output ();
+
+ /* Possibly drop carrier */
+ if ((termstate.c_cflag & HUPCL) || (termflags & NO_CARRIER))
+ (*bottom->desert_dtr) ();
+
+ termflags &= ~TTY_OPEN;
+ }
+
+ pthread_mutex_unlock (&global_lock);
+}
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *)
+ = po_destroy_hook;
+
+/* Tell if CRED can do foreground terminal operations. */
+static inline int
+fg_p (struct trivfs_protid *cred)
+{
+ struct protid_hook *hook = cred->hook;
+
+ if (!hook || (termflags & NO_OWNER))
+ return 1;
+
+ if (hook->pid == foreground_id
+ || hook->pgrp == -foreground_id)
+ return 1;
+
+ return 0;
+}
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ st->st_blksize = 512;
+ st->st_fstype = FSTYPE_TERM;
+ st->st_fsid = getpid ();
+ st->st_ino = 0;
+ st->st_rdev = rdev;
+ st->st_mode = term_mode;
+ st->st_uid = term_owner;
+ st->st_gid = term_group;
+}
+
+/* Implement term_getctty as described in <hurd/term.defs>. */
+kern_return_t
+S_term_getctty (struct trivfs_protid *cred,
+ mach_port_t *id,
+ mach_msg_type_name_t *idtype)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket
+ || cred->pi.class != tty_class)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ *id = ports_get_right (cttyid);
+ *idtype = MACH_MSG_TYPE_MAKE_SEND;
+ err = 0;
+ }
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* Implement termctty_open_terminal as described in <hurd/term.defs>. */
+kern_return_t
+S_termctty_open_terminal (mach_port_t arg,
+ int flags,
+ mach_port_t *result,
+ mach_msg_type_name_t *resulttype)
+{
+ error_t err;
+ mach_port_t new_realnode;
+ struct iouser *user;
+ struct trivfs_protid *newcred;
+ struct port_info *pi = ports_lookup_port (term_bucket, arg, cttyid_class);
+ if (!pi)
+ return EOPNOTSUPP;
+
+ assert (pi == cttyid);
+
+ err = io_restrict_auth (termctl->underlying, &new_realnode, 0, 0, 0, 0);
+
+ if (!err)
+ {
+ err = iohelp_create_empty_iouser (&user);
+ if (! err)
+ err = trivfs_open (termctl, user, flags, new_realnode, &newcred);
+ if (!err)
+ {
+ *result = ports_get_right (newcred);
+ *resulttype = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newcred);
+ }
+ }
+
+ ports_port_deref (pi);
+ return err;
+}
+
+/* Implement term_become_ctty as described in <hurd/term.defs>. */
+kern_return_t
+S_term_open_ctty (struct trivfs_protid *cred,
+ pid_t pid,
+ pid_t pgrp,
+ mach_port_t *newpt,
+ mach_msg_type_name_t *newpttype)
+{
+ error_t err;
+ struct trivfs_protid *newcred;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket
+ || cred->pi.class != tty_class)
+ return EOPNOTSUPP;
+
+ if (pid <= 0 || pgrp <= 0)
+ {
+ return EINVAL;
+ }
+
+ pthread_mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ {
+ pthread_mutex_unlock (&global_lock);
+ err = EBADF;
+ }
+ else
+ {
+ pthread_mutex_unlock (&global_lock);
+ err = trivfs_protid_dup (cred, &newcred);
+
+ if (!err)
+ {
+ struct protid_hook *hook = malloc (sizeof (struct protid_hook));
+
+ hook->pid = pid;
+ hook->pgrp = pgrp;
+ hook->sid = getsid (pid);
+ hook->refcnt = 1;
+
+ if (newcred->hook)
+ /* We inherited CRED's hook, get rid of our ref to it. */
+ pi_destroy_hook (newcred);
+ newcred->hook = hook;
+
+ *newpt = ports_get_right (newcred);
+ *newpttype = MACH_MSG_TYPE_MAKE_SEND;
+
+ ports_port_deref (newcred);
+ }
+ }
+
+ return err;
+}
+
+/* Implement chown locally; don't pass the value down to the
+ underlying node. */
+error_t
+trivfs_S_file_chown (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ uid_t uid,
+ gid_t gid)
+{
+ struct stat st;
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ /* XXX */
+ st.st_uid = term_owner;
+ st.st_gid = term_group;
+
+ if (!cred->isroot)
+ {
+ err = fshelp_isowner (&st, cred->user);
+ if (err)
+ goto out;
+
+ if ((uid != (uid_t) -1 && !idvec_contains (cred->user->uids, uid))
+ || (gid != (gid_t) -1 && !idvec_contains (cred->user->gids, gid)))
+ {
+ err = EPERM;
+ goto out;
+ }
+ }
+
+ /* Make the change */
+ if (uid != (uid_t) -1)
+ term_owner = uid;
+ if (gid != (gid_t) -1)
+ term_group = gid;
+ err = 0;
+
+out:
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* Implement chmod locally. */
+error_t
+trivfs_S_file_chmod (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mode_t mode)
+{
+ error_t err;
+ struct stat st;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ if (!cred->isroot)
+ {
+ /* XXX */
+ st.st_uid = term_owner;
+ st.st_gid = term_group;
+
+ err = fshelp_isowner (&st, cred->user);
+ if (err)
+ goto out;
+
+ mode &= ~S_ISVTX;
+
+ if (!idvec_contains (cred->user->uids, term_owner))
+ mode &= ~S_ISUID;
+
+ if (!idvec_contains (cred->user->gids, term_group))
+ mode &= ~S_ISUID;
+ }
+
+ term_mode = ((mode & ~S_IFMT & ~S_ITRANS & ~S_ISPARE) | S_IFCHR | S_IROOT);
+ err = 0;
+
+out:
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+
+/* Called for user writes to the terminal as described in
+ <hurd/io.defs>. */
+error_t
+trivfs_S_io_write (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ char *data,
+ size_t datalen,
+ loff_t offset,
+ size_t *amt)
+{
+ int i;
+ int cancel;
+ error_t err = 0;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class == pty_class)
+ return pty_io_write (cred, data, datalen, amt);
+
+ pthread_mutex_lock (&global_lock);
+
+ /* Check for errors first. */
+
+ if ((cred->po->openmodes & O_WRITE) == 0)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EBADF;
+ }
+
+ if ((termstate.c_lflag & TOSTOP) && !fg_p (cred))
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EBACKGROUND;
+ }
+
+ if ((termflags & NO_CARRIER) && !(termstate.c_cflag & CLOCAL))
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EIO;
+
+ }
+
+ cancel = 0;
+ for (i = 0; i < datalen; i++)
+ {
+ while (!qavail (outputq) && !cancel)
+ {
+ err = (*bottom->start_output) ();
+ if (err)
+ cancel = 1;
+ else
+ {
+ if (!qavail (outputq))
+ cancel = pthread_hurd_cond_wait_np (outputq->wait,
+ &global_lock);
+ }
+ }
+ if (cancel)
+ break;
+
+ write_character (data[i]);
+ }
+
+ *amt = i;
+
+ if (!err && datalen)
+ (*bottom->start_output) ();
+
+ trivfs_set_mtime (termctl);
+
+ call_asyncs (O_WRITE);
+
+ pthread_mutex_unlock (&global_lock);
+
+ return ((cancel && datalen && !*amt) ? (err ?: EINTR) : 0);
+}
+
+/* Called for user reads from the terminal. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ char **data,
+ size_t *datalen,
+ loff_t offset,
+ size_t amount)
+{
+ int cancel;
+ int i, max;
+ char *cp;
+ int avail;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class == pty_class)
+ return pty_io_read (cred, data, datalen, amount);
+
+ pthread_mutex_lock (&global_lock);
+
+ if ((cred->po->openmodes & O_READ) == 0)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EBADF;
+ }
+
+ if (!fg_p (cred))
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EBACKGROUND;
+ }
+
+ while (!qsize (inputq))
+ {
+ if (((termflags & NO_CARRIER) && !(termstate.c_cflag & CLOCAL)) || !amount)
+ {
+ /* Return EOF, Posix.1 7.1.1.10. */
+ pthread_mutex_unlock (&global_lock);
+ *datalen = 0;
+ return 0;
+ }
+
+ if (cred->po->openmodes & O_NONBLOCK)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EWOULDBLOCK;
+ }
+
+ if (pthread_hurd_cond_wait_np (inputq->wait, &global_lock))
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EINTR;
+ }
+
+ /* If a signal is being delivered, and we got woken up by
+ arriving input, then there's a possible race; we have to not
+ read from the queue as long as the signal is in progress.
+ Now, you might think that we should not read from the queue
+ when a signal is in progress even if we didn't block, but
+ that's not so. It's specifically that we have to get
+ *interrupted* by signals in progress (when the signallee is
+ this thread and wants to interrupt is) that is the race we
+ are avoiding. A reader who gets in after the signal begins,
+ while the signal is in progress, is harmless, because this
+ case is indiscernable from one where the reader gets in after
+ the signal has completed. */
+ if (sigs_in_progress)
+ {
+ input_sig_wakeup++;
+ if (pthread_hurd_cond_wait_np (&input_sig_wait, &global_lock))
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EINTR;
+ }
+ }
+ }
+
+ avail = qsize (inputq);
+ if (remote_input_mode)
+ avail--;
+
+ max = (amount < avail) ? amount : avail;
+
+ if (max > *datalen)
+ *data = mmap (0, max, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+
+ cancel = 0;
+ cp = *data;
+ for (i = 0; i < max; i++)
+ {
+ char c = dequeue (inputq);
+
+ if (remote_input_mode)
+ *cp++ = c;
+ else
+ {
+ /* Unless this is EOF, add it to the response. */
+ if (!(termstate.c_lflag & ICANON)
+ || !CCEQ (termstate.c_cc[VEOF], c))
+ *cp++ = c;
+
+ /* If this is a break character, then finish now. */
+ if ((termstate.c_lflag & ICANON)
+ && (c == '\n'
+ || CCEQ (termstate.c_cc[VEOF], c)
+ || CCEQ (termstate.c_cc[VEOL], c)
+ || CCEQ (termstate.c_cc[VEOL2], c)))
+ break;
+
+ /* If this is the delayed suspend character, then signal now. */
+ if ((termstate.c_lflag & ISIG)
+ && CCEQ (termstate.c_cc[VDSUSP], c))
+ {
+ /* The CANCEL flag is being used here to tell the return
+ below to make sure we don't signal EOF on a VDUSP that
+ happens at the front of a line. */
+ send_signal (SIGTSTP);
+ cancel = 1;
+ break;
+ }
+ }
+ }
+
+ if (remote_input_mode && qsize (inputq) == 1)
+ dequeue (inputq);
+
+ *datalen = cp - *data;
+
+ /* If we really read something, set atime. */
+ if (*datalen || !cancel)
+ trivfs_set_atime (termctl);
+
+ call_asyncs (O_READ);
+
+ pthread_mutex_unlock (&global_lock);
+
+ return !*datalen && cancel ? EINTR : 0;
+}
+
+error_t
+trivfs_S_io_pathconf (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int name,
+ int *val)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ switch (name)
+ {
+ case _PC_LINK_MAX:
+ case _PC_NAME_MAX:
+ case _PC_PATH_MAX:
+ case _PC_PIPE_BUF:
+ case _PC_NO_TRUNC:
+ default:
+ return io_pathconf (cred->realnode, name, val);
+
+ case _PC_MAX_CANON:
+ *val = rawq->hiwat;
+ return 0;
+
+ case _PC_MAX_INPUT:
+ *val = inputq->hiwat;
+ return 0;
+
+ case _PC_CHOWN_RESTRICTED:
+ /* We implement this locally, remember... */
+ *val = 1;
+ return 0;
+
+ case _PC_VDISABLE:
+ *val = _POSIX_VDISABLE;
+ return 0;
+ }
+}
+
+
+error_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ size_t *amt)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class == pty_class)
+ return pty_io_readable (amt);
+
+ pthread_mutex_lock (&global_lock);
+ if ((cred->po->openmodes & O_READ) == 0)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EBADF;
+ }
+ *amt = qsize (inputq);
+ if (remote_input_mode && *amt)
+ --*amt;
+ pthread_mutex_unlock (&global_lock);
+
+ return 0;
+}
+
+error_t
+trivfs_S_io_revoke (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype)
+{
+ struct stat st;
+
+ error_t iterator_function (void *port)
+ {
+ struct trivfs_protid *user = port;
+
+ if (user != cred)
+ ports_destroy_right (user);
+ return 0;
+ }
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ if (!cred->isroot)
+ {
+ error_t err;
+
+ /* XXX */
+ st.st_uid = term_owner;
+ st.st_gid = term_group;
+
+ err = fshelp_isowner (&st, cred->user);
+ if (err)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return err;
+ }
+ }
+
+ pthread_mutex_unlock (&global_lock);
+
+ ports_inhibit_bucket_rpcs (term_bucket);
+ ports_class_iterate (cred->pi.class, iterator_function);
+ ports_resume_bucket_rpcs (term_bucket);
+
+ return 0;
+}
+
+
+
+
+
+/* TIOCMODG ioctl -- Get modem state */
+kern_return_t
+S_tioctl_tiocmodg (struct trivfs_protid *cred,
+ int *state)
+{
+ error_t err = 0;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+ err = (*bottom->mdmstate) (state);
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+/* TIOCMODS ioctl -- Set modem state */
+kern_return_t
+S_tioctl_tiocmods (struct trivfs_protid *cred,
+ int state)
+{
+ error_t err;
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ err = (*bottom->mdmctl) (MDMCTL_SET, state);
+
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+/* TIOCEXCL ioctl -- Set exclusive use */
+kern_return_t
+S_tioctl_tiocexcl (struct trivfs_protid *cred)
+{
+ error_t err;
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ termflags |= EXCL_USE;
+ err = 0;
+ }
+
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* TIOCNXCL ioctl -- Clear exclusive use */
+kern_return_t
+S_tioctl_tiocnxcl (struct trivfs_protid *cred)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ termflags &= ~EXCL_USE;
+ err = 0;
+ }
+
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* TIOCFLUSH ioctl -- Flush input, output, or both */
+kern_return_t
+S_tioctl_tiocflush (struct trivfs_protid *cred,
+ int flags)
+{
+ error_t err = 0;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ if (flags == 0)
+ flags = O_READ|O_WRITE;
+
+ if (flags & O_READ)
+ {
+ (*bottom->notice_input_flushed) ();
+ clear_queue (inputq);
+ }
+
+ if (!err && (flags & O_WRITE))
+ err = drop_output ();
+ }
+
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* TIOCGETA ioctl -- Get termios state */
+kern_return_t
+S_tioctl_tiocgeta (struct trivfs_protid *cred,
+ tcflag_t *modes,
+ cc_t *ccs,
+ speed_t *speeds)
+{
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+ modes[0] = termstate.c_iflag;
+ modes[1] = termstate.c_oflag;
+ modes[2] = termstate.c_cflag;
+ modes[3] = termstate.c_lflag;
+ memcpy (ccs, termstate.c_cc, NCCS);
+ speeds[0] = termstate.__ispeed;
+ speeds[1] = termstate.__ospeed;
+ pthread_mutex_unlock (&global_lock);
+
+ return 0;
+}
+
+/* Common code for the varios TIOCSET* commands. */
+static error_t
+set_state (struct trivfs_protid *cred,
+ tcflag_t *modes,
+ cc_t *ccs,
+ speed_t *speeds,
+ int draino,
+ int flushi)
+{
+ error_t err;
+ struct termios state;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else if (!fg_p (cred))
+ err = EBACKGROUND;
+ else
+ {
+ if (cred->pi.class == pty_class)
+ {
+ err = (*bottom->abandon_physical_output) ();
+ if (err)
+ goto leave;
+ clear_queue (outputq);
+ }
+
+ if (draino)
+ {
+ err = drain_output ();
+ if (err)
+ goto leave;
+ }
+
+ if (flushi)
+ {
+ (*bottom->notice_input_flushed) ();
+ clear_queue (inputq);
+ }
+
+ state = termstate;
+ state.c_iflag = modes[0];
+ state.c_oflag = modes[1];
+ state.c_cflag = modes[2];
+ state.c_lflag = modes[3];
+ memcpy (state.c_cc, ccs, NCCS);
+ state.__ispeed = speeds[0];
+ state.__ospeed = speeds[1];
+
+ if (external_processing)
+ state.c_lflag |= EXTPROC;
+ else
+ state.c_lflag &= ~EXTPROC;
+
+ err = (*bottom->set_bits) (&state);
+ if (!err)
+ {
+ int oldlflag = termstate.c_lflag;
+
+ termstate = state;
+ if (oldlflag & ICANON)
+ {
+ if (!(termstate.c_lflag & ICANON))
+ copy_rawq ();
+ }
+ else
+ {
+ if (termstate.c_lflag & ICANON)
+ rescan_inputq ();
+ }
+ }
+ err = 0;
+ }
+
+ leave:
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+
+/* TIOCSETA -- Set termios state */
+kern_return_t
+S_tioctl_tiocseta (struct trivfs_protid *cred,
+ tcflag_t *modes,
+ cc_t *ccs,
+ speed_t *speeds)
+{
+ return set_state (cred, modes, ccs, speeds, 0, 0);
+}
+
+/* Drain output, then set term state. */
+kern_return_t
+S_tioctl_tiocsetaw (struct trivfs_protid *cred,
+ tcflag_t *modes,
+ cc_t *ccs,
+ speed_t *speeds)
+{
+ return set_state (cred, modes, ccs, speeds, 1, 0);
+}
+
+/* Flush input, drain output, then set term state. */
+kern_return_t
+S_tioctl_tiocsetaf (struct trivfs_protid *cred,
+ tcflag_t *modes,
+ cc_t *ccs,
+ speed_t *speeds)
+
+{
+ return set_state (cred, modes, ccs, speeds, 1, 1);
+}
+
+/* TIOCGETD -- Return line discipline */
+kern_return_t
+S_tioctl_tiocgetd (struct trivfs_protid *cred,
+ int *disc)
+{
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ *disc = 0;
+
+ return 0;
+}
+
+/* TIOCSETD -- Set line discipline */
+kern_return_t
+S_tioctl_tiocsetd (struct trivfs_protid *cred,
+ int disc)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ pthread_mutex_unlock (&global_lock);
+
+ if (disc != 0)
+ err = ENXIO;
+ else
+ err = 0;
+
+ return err;
+}
+
+/* TIOCDRAIN -- Wait for output to drain */
+kern_return_t
+S_tioctl_tiocdrain (struct trivfs_protid *cred)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & O_WRITE))
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EBADF;
+ }
+
+ err = drain_output ();
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* TIOCSWINSZ -- Set window size */
+kern_return_t
+S_tioctl_tiocswinsz (struct trivfs_protid *cred,
+ struct winsize size)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ err = 0;
+
+ if (! err
+ && (size.ws_row != window_size.ws_row
+ || size.ws_col != window_size.ws_col
+ || size.ws_xpixel != window_size.ws_xpixel
+ || size.ws_ypixel != window_size.ws_ypixel))
+ {
+ /* The size is actually changing. Record the new size and notify the
+ process group. */
+ window_size = size;
+ send_signal (SIGWINCH);
+ }
+
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* TIOCGWINSZ -- Fetch window size */
+kern_return_t
+S_tioctl_tiocgwinsz (struct trivfs_protid *cred,
+ struct winsize *size)
+{
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+ *size = window_size;
+ pthread_mutex_unlock (&global_lock);
+
+ return 0;
+}
+
+/* TIOCMGET -- Fetch all modem bits */
+kern_return_t
+S_tioctl_tiocmget (struct trivfs_protid *cred,
+ int *bits)
+{
+ error_t err = 0;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+ err = (*bottom->mdmstate) (bits);
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+/* TIOCMSET -- Set all modem bits */
+kern_return_t
+S_tioctl_tiocmset (struct trivfs_protid *cred,
+ int bits)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ err = (*bottom->mdmctl) (MDMCTL_SET, bits);
+
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* TIOCMBIC -- Clear some modem bits */
+kern_return_t
+S_tioctl_tiocmbic (struct trivfs_protid *cred,
+ int bits)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ err = (*bottom->mdmctl) (MDMCTL_BIC, bits);
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+/* TIOCMBIS -- Set some modem bits */
+kern_return_t
+S_tioctl_tiocmbis (struct trivfs_protid *cred,
+ int bits)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ err = (*bottom->mdmctl) (MDMCTL_BIS, bits);
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+/* TIOCSTART -- start output as if VSTART were typed */
+kern_return_t
+S_tioctl_tiocstart (struct trivfs_protid *cred)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ int old_termflags = termflags;
+
+ termflags &= ~USER_OUTPUT_SUSP;
+ err = (*bottom->start_output) ();
+ if (err)
+ termflags = old_termflags;
+ }
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+/* TIOCSTOP -- stop output as if VSTOP were typed */
+kern_return_t
+S_tioctl_tiocstop (struct trivfs_protid *cred)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+ pthread_mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ int old_termflags = termflags;
+ termflags |= USER_OUTPUT_SUSP;
+ err = (*bottom->suspend_physical_output) ();
+ if (err)
+ termflags = old_termflags;
+ }
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+/* TIOCSTI -- Simulate terminal input */
+kern_return_t
+S_tioctl_tiocsti (struct trivfs_protid *cred,
+ char c)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+
+ /* BSD returns EACCES if this is not our controlling terminal,
+ but we have no way to do that. (And I don't think it actually
+ provides any security there, either.) */
+
+ if (!(cred->po->openmodes & O_READ))
+ err = EPERM;
+ else
+ {
+ input_character (c);
+ err = 0;
+ }
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+/* TIOCOUTQ -- return output queue size */
+kern_return_t
+S_tioctl_tiocoutq (struct trivfs_protid *cred,
+ int *queue_size)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ *queue_size = qsize (outputq) + (*bottom->pending_output_size) ();
+ err = 0;
+ }
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+/* TIOCSPGRP -- set pgrp of terminal */
+kern_return_t
+S_tioctl_tiocspgrp (struct trivfs_protid *cred,
+ int pgrp)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ termflags &= ~NO_OWNER;
+ foreground_id = -pgrp;
+ err = 0;
+ }
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+/* TIOCGPGRP --- fetch pgrp of terminal */
+kern_return_t
+S_tioctl_tiocgpgrp (struct trivfs_protid *cred,
+ int *pgrp)
+{
+ error_t ret;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+ if (termflags & NO_OWNER)
+ ret = ENOTTY; /* that's what BSD says... */
+ else
+ {
+ *pgrp = - foreground_id;
+ ret = 0;
+ }
+ pthread_mutex_unlock (&global_lock);
+
+ return ret;
+}
+
+/* TIOCCDTR -- clear DTR */
+kern_return_t
+S_tioctl_tioccdtr (struct trivfs_protid *cred)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ err = (*bottom->mdmctl) (MDMCTL_BIC, TIOCM_DTR);
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+/* TIOCSDTR -- set DTR */
+kern_return_t
+S_tioctl_tiocsdtr (struct trivfs_protid *cred)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ err = (*bottom->mdmctl) (MDMCTL_BIS, TIOCM_DTR);
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+/* TIOCCBRK -- Clear break condition */
+kern_return_t
+S_tioctl_tioccbrk (struct trivfs_protid *cred)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ err = (*bottom->clear_break) ();
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+/* TIOCSBRK -- Set break condition */
+kern_return_t
+S_tioctl_tiocsbrk (struct trivfs_protid *cred)
+{
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ return EOPNOTSUPP;
+ }
+
+ pthread_mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ err = (*bottom->set_break) ();
+ pthread_mutex_unlock (&global_lock);
+
+ return err;
+}
+
+error_t
+trivfs_S_file_set_size (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t size)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (size < 0)
+ return EINVAL;
+ pthread_mutex_lock (&global_lock);
+ if ((cred->po->openmodes & O_WRITE) == 0)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EBADF;
+ }
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t off,
+ int whence,
+ off_t *newp)
+{
+ return ESPIPE;
+}
+
+error_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ *bits = cred->po->openmodes;
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+#define HONORED_STATE_MODES (O_APPEND|O_ASYNC|O_FSYNC|O_NONBLOCK|O_NOATIME)
+
+error_t
+trivfs_S_io_set_all_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ int obits;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ obits = cred->po->openmodes;
+ if ((obits & O_ASYNC) && --num_icky_async_peropens == 0)
+ termflags &= ~ICKY_ASYNC;
+
+ cred->po->openmodes &= ~HONORED_STATE_MODES;
+ cred->po->openmodes |= (bits & HONORED_STATE_MODES);
+
+ if ((bits & O_ASYNC) && !(obits & O_ASYNC))
+ {
+ termflags |= ICKY_ASYNC;
+ num_icky_async_peropens++;
+ call_asyncs (O_READ | O_WRITE);
+ }
+
+ pthread_mutex_unlock (&global_lock);
+
+ return 0;
+}
+
+error_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ int obits;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ obits = cred->po->openmodes;
+ cred->po->openmodes |= (bits & HONORED_STATE_MODES);
+ if ((bits & O_ASYNC) && !(obits & O_ASYNC))
+ {
+ termflags |= ICKY_ASYNC;
+ num_icky_async_peropens++;
+ call_asyncs (O_READ | O_WRITE);
+ }
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ if ((cred->po->openmodes & O_ASYNC) && --num_icky_async_peropens == 0)
+ termflags &= ~ICKY_ASYNC;
+ cred->po->openmodes &= ~(bits & HONORED_STATE_MODES);
+ pthread_mutex_unlock (&global_lock);
+
+ return 0;
+}
+
+error_t
+trivfs_S_io_mod_owner (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ pid_t owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ termflags &= ~NO_OWNER;
+ foreground_id = owner;
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+trivfs_S_io_get_owner (struct trivfs_protid *cred,
+ mach_port_t erply,
+ mach_msg_type_name_t reply_type,
+ pid_t *owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ if (termflags & NO_OWNER)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return ENOTTY;
+ }
+ *owner = foreground_id;
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+trivfs_S_io_get_icky_async_id (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mach_port_t *id, mach_msg_type_name_t *idtype)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EBADF;
+ }
+ *id = async_icky_id;
+ *idtype = MACH_MSG_TYPE_MAKE_SEND;
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+trivfs_S_io_async (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mach_port_t notify,
+ mach_port_t *id, mach_msg_type_name_t *idtype)
+{
+ struct async_req *ar;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EBADF;
+ }
+ ar = malloc (sizeof (struct async_req));
+ ar->notify = notify;
+ ar->next = async_requests;
+ async_requests = ar;
+ *id = async_id;
+ *idtype = MACH_MSG_TYPE_MAKE_SEND;
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+static error_t
+io_select_common (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ struct timespec *tsp, int *type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class == pty_class)
+ return pty_io_select (cred, reply, tsp, type);
+
+ if ((cred->po->openmodes & O_READ) == 0)
+ *type &= ~SELECT_READ;
+ if ((cred->po->openmodes & O_WRITE) == 0)
+ *type &= ~SELECT_WRITE;
+
+ pthread_mutex_lock (&global_lock);
+
+ while (1)
+ {
+ int available = 0;
+ error_t err = 0;
+
+ if ((*type & SELECT_READ) && qsize (inputq))
+ available |= SELECT_READ;
+ if ((*type & SELECT_WRITE) && qavail (outputq))
+ available |= SELECT_WRITE;
+
+ if (available == 0)
+ {
+ ports_interrupt_self_on_port_death (cred, reply);
+ err = pthread_hurd_cond_timedwait_np (&select_alert, &global_lock,
+ tsp);
+ if (!err)
+ continue;
+ }
+
+ *type = available;
+ pthread_mutex_unlock (&global_lock);
+
+ if (err == ETIMEDOUT)
+ err = 0;
+
+ return err;
+ }
+}
+
+error_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int *type)
+{
+ return io_select_common (cred, reply, reply_type, NULL, type);
+}
+
+error_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ struct timespec ts,
+ int *type)
+{
+ return io_select_common (cred, reply, reply_type, &ts, type);
+}
+
+kern_return_t
+trivfs_S_io_map (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replyPoly,
+ mach_port_t *rdobj,
+ mach_msg_type_name_t *rdtype,
+ mach_port_t *wrobj,
+ mach_msg_type_name_t *wrtype)
+{
+ return EOPNOTSUPP;
+}
+
+static void
+report_sig_start ()
+{
+ sigs_in_progress++;
+}
+
+static void
+report_sig_end ()
+{
+ sigs_in_progress--;
+ if ((sigs_in_progress == 0) && input_sig_wakeup)
+ {
+ input_sig_wakeup = 0;
+ pthread_cond_broadcast (&input_sig_wait);
+ }
+}
+
+/* Call all the scheduled async I/O handlers. DIR is a mask of O_READ &
+ O_WRITE; the asyncs will only be called if output is possible in one of
+ the directions given in DIR. */
+void
+call_asyncs (int dir)
+{
+ struct async_req *ar, *nxt, **prevp;
+ mach_port_t err;
+
+ /* If nobody wants async messages, don't bother further. */
+ if (!(termflags & ICKY_ASYNC) && !async_requests)
+ return;
+
+ if ((!(dir & O_READ) || qsize (inputq) == 0)
+ && (!(dir & O_WRITE) && qavail (outputq) == 0))
+ /* Output isn't possible in the desired directions. */
+ return;
+
+ if ((termflags & ICKY_ASYNC) && !(termflags & NO_OWNER))
+ {
+ report_sig_start ();
+ pthread_mutex_unlock (&global_lock);
+ hurd_sig_post (foreground_id, SIGIO, async_icky_id);
+ pthread_mutex_lock (&global_lock);
+ report_sig_end ();
+ }
+
+ for (ar = async_requests, prevp = &async_requests;
+ ar;
+ ar = nxt)
+ {
+ nxt = ar->next;
+ err = nowait_msg_sig_post (ar->notify, SIGIO, 0, async_id);
+ if (err == MACH_SEND_INVALID_DEST)
+ {
+ /* Receiver died; remove the notification request. */
+ *prevp = ar->next;
+ mach_port_deallocate (mach_task_self (), ar->notify);
+ free (ar);
+ }
+ else
+ prevp = &ar->next;
+ }
+}
+
+/* Send a signal to the current process (group) of the terminal. */
+void
+send_signal (int signo)
+{
+ mach_port_t right;
+
+ if (!(termflags & NO_OWNER))
+ {
+ right = ports_get_send_right (cttyid);
+ report_sig_start ();
+ pthread_mutex_unlock (&global_lock);
+ hurd_sig_post (foreground_id, signo, right);
+ pthread_mutex_lock (&global_lock);
+ report_sig_end ();
+ mach_port_deallocate (mach_task_self (), right);
+ }
+}
+
+void
+report_carrier_off ()
+{
+ clear_queue (inputq);
+ (*bottom->notice_input_flushed) ();
+ drop_output ();
+ termflags |= NO_CARRIER;
+ if (!(termstate.c_cflag & CLOCAL))
+ send_signal (SIGHUP);
+}
+
+void
+report_carrier_on ()
+{
+ termflags &= ~NO_CARRIER;
+ pthread_cond_broadcast (&carrier_alert);
+}
+
+void
+report_carrier_error (error_t err)
+{
+ carrier_error = err;
+ pthread_cond_broadcast (&carrier_alert);
+}
+
+kern_return_t
+S_term_get_nodename (struct trivfs_protid *cred,
+ char *name)
+{
+ if (!cred
+ || cred->pi.bucket != term_bucket
+ || cred->pi.class != tty_class)
+ return EOPNOTSUPP;
+
+ if (!cred->po->cntl->hook)
+ {
+ return ENOENT;
+ }
+
+ strcpy (name, (char *)cred->po->cntl->hook);
+
+ return 0;
+}
+
+kern_return_t
+S_term_set_nodename (struct trivfs_protid *cred,
+ char *name)
+{
+ error_t err = 0;
+ if (!cred
+ || cred->pi.bucket != term_bucket
+ || cred->pi.class != tty_class)
+ return EOPNOTSUPP;
+
+ if (strcmp (name, (char *)cred->po->cntl->hook) != 0)
+ err = EINVAL;
+
+ return err;
+}
+
+kern_return_t
+S_term_set_filenode (struct trivfs_protid *cred,
+ file_t filenode)
+{
+ if (!cred
+ || cred->pi.bucket != term_bucket
+ || cred->pi.class != tty_class)
+ return EOPNOTSUPP;
+
+ return EINVAL;
+}
+
+kern_return_t
+S_term_get_peername (struct trivfs_protid *cred,
+ char *name)
+{
+ struct trivfs_control *peer;
+
+ if (!cred
+ || cred->pi.bucket != term_bucket)
+ return EOPNOTSUPP;
+
+ if (!cred || (cred->pi.class != tty_class && cred->pi.class != pty_class))
+ {
+ if (cred)
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ peer = (cred->pi.class == tty_class) ? ptyctl : termctl;
+
+ if (bottom != &ptyio_bottom || !peer->hook)
+ {
+ ports_port_deref (cred);
+ return ENOENT;
+ }
+
+ strcpy (name, (char *) peer->hook);
+
+ return 0;
+}
+
+kern_return_t
+S_term_get_bottom_type (struct trivfs_protid *cred,
+ int *ttype)
+{
+ if (!cred
+ || cred->pi.bucket != term_bucket
+ || cred->pi.class != tty_class)
+ return EOPNOTSUPP;
+
+ *ttype = bottom->type;
+ return 0;
+}
+
+kern_return_t
+S_term_on_machdev (struct trivfs_protid *cred,
+ device_t machdev)
+{
+ if (!cred
+ || cred->pi.bucket != term_bucket
+ || cred->pi.class != tty_class)
+ return EOPNOTSUPP;
+
+ return EINVAL;
+}
+
+kern_return_t
+S_term_on_hurddev (struct trivfs_protid *cred,
+ struct trivfs_protid *hurddev)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_term_on_pty (struct trivfs_protid *cred,
+ struct trivfs_protid **master)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+ return EBUSY;
+}
diff --git a/term/xinl.c b/term/xinl.c
new file mode 100644
index 00000000..3695faa5
--- /dev/null
+++ b/term/xinl.c
@@ -0,0 +1,2 @@
+#define TERM_DEFINE_EI
+#include "term.h"
diff --git a/tmpfs/Makefile b/tmpfs/Makefile
new file mode 100644
index 00000000..fdcae349
--- /dev/null
+++ b/tmpfs/Makefile
@@ -0,0 +1,29 @@
+# Makefile for tmpfs
+#
+# Copyright (C) 2000,01,02,12 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := tmpfs
+makemode := server
+
+target = tmpfs
+SRCS = tmpfs.c node.c dir.c pager-stubs.c
+OBJS = $(SRCS:.c=.o) default_pagerUser.o
+# XXX The shared libdiskfs requires libstore even though we don't use it here.
+HURDLIBS = diskfs pager iohelp fshelp store ports ihash shouldbeinlibc
+OTHERLIBS = -lpthread
+
+include ../Makeconf
diff --git a/tmpfs/dir.c b/tmpfs/dir.c
new file mode 100644
index 00000000..45a07521
--- /dev/null
+++ b/tmpfs/dir.c
@@ -0,0 +1,315 @@
+/* Directories for tmpfs.
+ Copyright (C) 2000,01,02 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stddef.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "tmpfs.h"
+#include <stdlib.h>
+
+error_t
+diskfs_init_dir (struct node *dp, struct node *pdp, struct protid *cred)
+{
+ dp->dn->u.dir.dotdot = pdp->dn;
+ dp->dn->u.dir.entries = 0;
+
+ /* Increase hardlink count for parent directory */
+ pdp->dn_stat.st_nlink++;
+ /* Take '.' directory into account */
+ dp->dn_stat.st_nlink++;
+
+ return 0;
+}
+
+error_t
+diskfs_clear_directory (struct node *dp, struct node *pdp,
+ struct protid *cred)
+{
+ if (dp->dn->u.dir.entries != 0)
+ return ENOTEMPTY;
+ assert (dp->dn_stat.st_size == 0);
+ assert (dp->dn->u.dir.dotdot == pdp->dn);
+
+ /* Decrease hardlink count for parent directory */
+ pdp->dn_stat.st_nlink--;
+ /* Take '.' directory into account */
+ dp->dn_stat.st_nlink--;
+
+ return 0;
+}
+
+int
+diskfs_dirempty (struct node *dp, struct protid *cred)
+{
+ return dp->dn->u.dir.entries == 0;
+}
+
+error_t
+diskfs_get_directs (struct node *dp, int entry, int n,
+ char **data, size_t *datacnt,
+ vm_size_t bufsiz, int *amt)
+{
+ struct tmpfs_dirent *d;
+ struct dirent *entp;
+ int i;
+
+ if (bufsiz == 0)
+ bufsiz = dp->dn_stat.st_size
+ + 2 * ((offsetof (struct dirent, d_name[3]) + 7) & ~7);
+ if (bufsiz > *datacnt)
+ {
+ *data = mmap (0, bufsiz, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ return ENOMEM;
+ }
+
+ /* We always synthesize the first two entries (. and ..) on the fly. */
+ entp = (struct dirent *) *data;
+ i = 0;
+ if (i++ >= entry)
+ {
+ entp->d_fileno = dp->dn_stat.st_ino;
+ entp->d_type = DT_DIR;
+ entp->d_namlen = 1;
+ entp->d_name[0] = '.';
+ entp->d_name[1] = '\0';
+ entp->d_reclen = (&entp->d_name[2] - (char *) entp + 7) & ~7;
+ entp = (void *) entp + entp->d_reclen;
+ }
+ if (i++ >= entry)
+ {
+ if (dp->dn->u.dir.dotdot == 0)
+ {
+ assert (dp == diskfs_root_node);
+ /* Use something not zero and not an st_ino value for any node in
+ this filesystem. Since we use pointer values, 2 will never
+ be a valid number. */
+ entp->d_fileno = 2;
+ }
+ else
+ entp->d_fileno = (ino_t) (uintptr_t) dp->dn->u.dir.dotdot;
+ entp->d_type = DT_DIR;
+ entp->d_namlen = 2;
+ entp->d_name[0] = '.';
+ entp->d_name[1] = '.';
+ entp->d_name[2] = '\0';
+ entp->d_reclen = (&entp->d_name[3] - (char *) entp + 7) & ~7;
+ entp = (void *) entp + entp->d_reclen;
+ }
+
+ /* Skip ahead to the desired entry. */
+ for (d = dp->dn->u.dir.entries; i < entry && d != 0; d = d->next)
+ ++i;
+
+ if (i < entry)
+ {
+ assert (d == 0);
+ *datacnt = 0;
+ *amt = 0;
+ return 0;
+ }
+
+ /* Now fill in the buffer with real entries. */
+ for (; d != 0; d = d->next, i++)
+ {
+ size_t rlen = (offsetof (struct dirent, d_name[1]) + d->namelen + 7) & ~7;
+ if (rlen + (char *) entp - *data > bufsiz || (n >= 0 && i > n))
+ break;
+ entp->d_fileno = (ino_t) (uintptr_t) d->dn;
+ entp->d_type = DT_UNKNOWN;
+ entp->d_namlen = d->namelen;
+ memcpy (entp->d_name, d->name, d->namelen + 1);
+ entp->d_reclen = rlen;
+ entp = (void *) entp + rlen;
+ }
+
+ *datacnt = (char *) entp - *data;
+ *amt = i - entry;
+
+ return 0;
+}
+
+
+struct dirstat
+{
+ struct tmpfs_dirent **prevp;
+ int dotdot;
+};
+const size_t diskfs_dirstat_size = sizeof (struct dirstat);
+
+void
+diskfs_null_dirstat (struct dirstat *ds)
+{
+ ds->prevp = 0;
+}
+
+error_t
+diskfs_drop_dirstat (struct node *dp, struct dirstat *ds)
+{
+ /* No need to clear the pointers. */
+ return 0;
+}
+
+error_t
+diskfs_lookup_hard (struct node *dp,
+ const char *name, enum lookup_type type,
+ struct node **np, struct dirstat *ds,
+ struct protid *cred)
+{
+ const size_t namelen = strlen (name);
+ struct tmpfs_dirent *d, **prevp;
+
+ if (type == REMOVE || type == RENAME)
+ assert (np);
+
+ if (ds)
+ ds->dotdot = type & SPEC_DOTDOT;
+
+ if (namelen == 1 && name[0] == '.')
+ {
+ if (np != 0)
+ {
+ *np = dp;
+ diskfs_nref (dp);
+ }
+ return 0;
+ }
+ if (namelen == 2 && name[0] == '.' && name[1] == '.')
+ {
+ struct disknode *dddn = dp->dn->u.dir.dotdot;
+ error_t err;
+
+ assert (np != 0);
+ if (dddn == 0) /* root directory */
+ return EAGAIN;
+
+ if (type == (REMOVE|SPEC_DOTDOT) || type == (RENAME|SPEC_DOTDOT))
+ {
+ *np = *dddn->hprevp;
+ assert (*np);
+ assert ((*np)->dn == dddn);
+ assert (*dddn->hprevp == *np);
+ return 0;
+ }
+ else
+ {
+ pthread_mutex_unlock (&dp->lock);
+ err = diskfs_cached_lookup ((ino_t) (intptr_t) dddn, np);
+
+ if (type == (LOOKUP|SPEC_DOTDOT))
+ diskfs_nrele (dp);
+ else
+ pthread_mutex_lock (&dp->lock);
+
+ if (err)
+ *np = 0;
+
+ return err;
+ }
+ }
+
+ for (d = *(prevp = &dp->dn->u.dir.entries); d != 0;
+ d = *(prevp = &d->next))
+ if (d->namelen == namelen && !memcmp (d->name, name, namelen))
+ {
+ if (ds)
+ ds->prevp = prevp;
+
+ if (np)
+ return diskfs_cached_lookup ((ino_t) (uintptr_t) d->dn, np);
+ else
+ return 0;
+ }
+
+ if (ds)
+ ds->prevp = prevp;
+ if (np)
+ *np = 0;
+ return ENOENT;
+}
+
+
+error_t
+diskfs_direnter_hard (struct node *dp, const char *name,
+ struct node *np, struct dirstat *ds,
+ struct protid *cred)
+{
+ const size_t namelen = strlen (name);
+ const size_t entsize
+ = (offsetof (struct dirent, d_name[1]) + namelen + 7) & ~7;
+ struct tmpfs_dirent *new;
+
+ if (round_page (tmpfs_space_used + entsize) / vm_page_size
+ > tmpfs_page_limit)
+ return ENOSPC;
+
+ new = malloc (offsetof (struct tmpfs_dirent, name) + namelen + 1);
+ if (new == 0)
+ return ENOSPC;
+
+ new->next = 0;
+ new->dn = np->dn;
+ new->namelen = namelen;
+ memcpy (new->name, name, namelen + 1);
+ *ds->prevp = new;
+
+ dp->dn_stat.st_size += entsize;
+ adjust_used (entsize);
+
+ dp->dn_stat.st_blocks = ((sizeof *dp->dn + dp->dn->translen
+ + dp->dn_stat.st_size + 511)
+ / 512);
+ return 0;
+}
+
+error_t
+diskfs_dirrewrite_hard (struct node *dp, struct node *np,
+ struct dirstat *ds)
+{
+ if (ds->dotdot)
+ dp->dn->u.dir.dotdot = np->dn;
+ else
+ (*ds->prevp)->dn = np->dn;
+
+ return 0;
+}
+
+error_t
+diskfs_dirremove_hard (struct node *dp, struct dirstat *ds)
+{
+ struct tmpfs_dirent *d = *ds->prevp;
+ const size_t entsize
+ = (offsetof (struct dirent, d_name[1]) + d->namelen + 7) & ~7;
+
+ *ds->prevp = d->next;
+
+ if (dp->dirmod_reqs != 0)
+ diskfs_notice_dirchange (dp, DIR_CHANGED_UNLINK, d->name);
+
+ free (d);
+
+ adjust_used (-entsize);
+ dp->dn_stat.st_size -= entsize;
+ dp->dn_stat.st_blocks = ((sizeof *dp->dn + dp->dn->translen
+ + dp->dn_stat.st_size + 511)
+ / 512);
+
+ return 0;
+}
diff --git a/tmpfs/node.c b/tmpfs/node.c
new file mode 100644
index 00000000..acc029ae
--- /dev/null
+++ b/tmpfs/node.c
@@ -0,0 +1,594 @@
+/* Node state and file contents for tmpfs.
+ Copyright (C) 2000,01,02 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "tmpfs.h"
+#include <stddef.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <hurd/hurd_types.h>
+#include <hurd/store.h>
+#include "default_pager_U.h"
+#include "libdiskfs/fs_S.h"
+
+unsigned int num_files;
+static unsigned int gen;
+
+struct node *all_nodes;
+static size_t all_nodes_nr_items;
+
+error_t
+diskfs_alloc_node (struct node *dp, mode_t mode, struct node **npp)
+{
+ struct disknode *dn;
+
+ dn = calloc (1, sizeof *dn);
+ if (dn == 0)
+ return ENOSPC;
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ if (round_page (tmpfs_space_used + sizeof *dn) / vm_page_size
+ > tmpfs_page_limit)
+ {
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+ free (dn);
+ return ENOSPC;
+ }
+ dn->gen = gen++;
+ ++num_files;
+ tmpfs_space_used += sizeof *dn;
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+
+ dn->type = IFTODT (mode & S_IFMT);
+ return diskfs_cached_lookup ((ino_t) (uintptr_t) dn, npp);
+}
+
+void
+diskfs_free_node (struct node *np, mode_t mode)
+{
+ switch (np->dn->type)
+ {
+ case DT_REG:
+ if (np->dn->u.reg.memobj != MACH_PORT_NULL) {
+ vm_deallocate (mach_task_self (), np->dn->u.reg.memref, 4096);
+ mach_port_deallocate (mach_task_self (), np->dn->u.reg.memobj);
+ }
+ break;
+ case DT_DIR:
+ assert (np->dn->u.dir.entries == 0);
+ break;
+ case DT_LNK:
+ free (np->dn->u.lnk);
+ break;
+ }
+ *np->dn->hprevp = np->dn->hnext;
+ if (np->dn->hnext != 0)
+ np->dn->hnext->dn->hprevp = np->dn->hprevp;
+ all_nodes_nr_items -= 1;
+ free (np->dn);
+ np->dn = 0;
+
+ --num_files;
+ tmpfs_space_used -= sizeof *np->dn;
+}
+
+void
+diskfs_node_norefs (struct node *np)
+{
+ if (np->dn != 0)
+ {
+ /* We don't bother to do this in diskfs_write_disknode, since it only
+ ever matters here. The node state goes back into the `struct
+ disknode' while it has no associated diskfs node. */
+
+ np->dn->size = np->dn_stat.st_size;
+ np->dn->mode = np->dn_stat.st_mode;
+ np->dn->nlink = np->dn_stat.st_nlink;
+ np->dn->uid = np->dn_stat.st_uid;
+ np->dn->author = np->dn_stat.st_author;
+ np->dn->gid = np->dn_stat.st_gid;
+ np->dn->atime = np->dn_stat.st_atim;
+ np->dn->mtime = np->dn_stat.st_mtim;
+ np->dn->ctime = np->dn_stat.st_ctim;
+ np->dn->flags = np->dn_stat.st_flags;
+
+ switch (np->dn->type)
+ {
+ case DT_REG:
+ assert (np->allocsize % vm_page_size == 0);
+ np->dn->u.reg.allocpages = np->allocsize / vm_page_size;
+ break;
+ case DT_CHR:
+ case DT_BLK:
+ np->dn->u.chr = np->dn_stat.st_rdev;
+ break;
+ }
+
+ /* Remove this node from the cache list rooted at `all_nodes'. */
+ *np->dn->hprevp = np->dn->hnext;
+ if (np->dn->hnext != 0)
+ np->dn->hnext->dn->hprevp = np->dn->hprevp;
+ all_nodes_nr_items -= 1;
+ np->dn->hnext = 0;
+ np->dn->hprevp = 0;
+ }
+
+ free (np);
+}
+
+static void
+recompute_blocks (struct node *np)
+{
+ struct disknode *const dn = np->dn;
+ struct stat *const st = &np->dn_stat;
+
+ st->st_blocks = sizeof *dn + dn->translen;
+ switch (dn->type)
+ {
+ case DT_REG:
+ np->allocsize = dn->u.reg.allocpages * vm_page_size;
+ st->st_blocks += np->allocsize;
+ break;
+ case DT_LNK:
+ st->st_blocks += st->st_size + 1;
+ break;
+ case DT_CHR:
+ case DT_BLK:
+ st->st_rdev = dn->u.chr;
+ break;
+ case DT_DIR:
+ st->st_blocks += dn->size;
+ break;
+ }
+ st->st_blocks = (st->st_blocks + 511) / 512;
+}
+
+/* Fetch inode INUM, set *NPP to the node structure;
+ gain one user reference and lock the node. */
+error_t
+diskfs_cached_lookup (ino_t inum, struct node **npp)
+{
+ struct disknode *dn = (void *) (uintptr_t) inum;
+ struct node *np;
+
+ assert (npp);
+
+ if (dn->hprevp != 0) /* There is already a node. */
+ {
+ np = *dn->hprevp;
+ assert (np->dn == dn);
+ assert (*dn->hprevp == np);
+
+ diskfs_nref (np);
+ }
+ else
+ /* Create the new node. */
+ {
+ struct stat *st;
+
+ np = diskfs_make_node (dn);
+ np->cache_id = (ino_t) (uintptr_t) dn;
+
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ dn->hnext = all_nodes;
+ if (dn->hnext)
+ dn->hnext->dn->hprevp = &dn->hnext;
+ dn->hprevp = &all_nodes;
+ all_nodes = np;
+ all_nodes_nr_items += 1;
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+
+ st = &np->dn_stat;
+ memset (st, 0, sizeof *st);
+ st->st_fstype = FSTYPE_MEMFS;
+ st->st_fsid = getpid ();
+ st->st_blksize = vm_page_size;
+
+ st->st_ino = (ino_t) (uintptr_t) dn;
+ st->st_gen = dn->gen;
+
+ st->st_size = dn->size;
+ st->st_mode = dn->mode;
+ st->st_nlink = dn->nlink;
+ st->st_uid = dn->uid;
+ st->st_author = dn->author;
+ st->st_gid = dn->gid;
+ st->st_atim = dn->atime;
+ st->st_mtim = dn->mtime;
+ st->st_ctim = dn->ctime;
+ st->st_flags = dn->flags;
+
+ st->st_rdev = 0;
+ np->allocsize = 0;
+ recompute_blocks (np);
+ }
+
+ pthread_mutex_lock (&np->lock);
+ *npp = np;
+ return 0;
+}
+
+error_t
+diskfs_node_iterate (error_t (*fun) (struct node *))
+{
+ error_t err = 0;
+ size_t num_nodes;
+ struct node *node, **node_list, **p;
+
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+
+ /* We must copy everything from the hash table into another data structure
+ to avoid running into any problems with the hash-table being modified
+ during processing (normally we delegate access to hash-table with
+ diskfs_node_refcnt_lock, but we can't hold this while locking the
+ individual node locks). */
+
+ num_nodes = all_nodes_nr_items;
+
+ p = node_list = alloca (num_nodes * sizeof (struct node *));
+ for (node = all_nodes; node != 0; node = node->dn->hnext)
+ {
+ *p++ = node;
+ node->references++;
+ }
+
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+
+ p = node_list;
+ while (num_nodes-- > 0)
+ {
+ node = *p++;
+ if (!err)
+ {
+ pthread_mutex_lock (&node->lock);
+ err = (*fun) (node);
+ pthread_mutex_unlock (&node->lock);
+ }
+ diskfs_nrele (node);
+ }
+
+ return err;
+}
+
+/* The user must define this function. Node NP has some light
+ references, but has just lost its last hard references. Take steps
+ so that if any light references can be freed, they are. NP is locked
+ as is the pager refcount lock. This function will be called after
+ diskfs_lost_hardrefs. */
+void
+diskfs_try_dropping_softrefs (struct node *np)
+{
+}
+
+/* The user must define this funcction. Node NP has some light
+ references but has just lost its last hard reference. NP is locked. */
+void
+diskfs_lost_hardrefs (struct node *np)
+{
+}
+
+/* The user must define this function. Node NP has just acquired
+ a hard reference where it had none previously. It is thus now
+ OK again to have light references without real users. NP is
+ locked. */
+void
+diskfs_new_hardrefs (struct node *np)
+{
+}
+
+
+
+error_t
+diskfs_get_translator (struct node *np, char **namep, u_int *namelen)
+{
+ *namelen = np->dn->translen;
+ if (*namelen == 0)
+ return 0;
+ *namep = malloc (*namelen);
+ if (*namep == 0)
+ return ENOMEM;
+ memcpy (*namep, np->dn->trans, *namelen);
+ return 0;
+}
+
+error_t
+diskfs_set_translator (struct node *np,
+ const char *name, u_int namelen,
+ struct protid *cred)
+{
+ char *new;
+ if (namelen == 0)
+ {
+ free (np->dn->trans);
+ new = 0;
+ np->dn_stat.st_mode &= ~S_IPTRANS;
+ }
+ else
+ {
+ new = realloc (np->dn->trans, namelen);
+ if (new == 0)
+ return ENOSPC;
+ memcpy (new, name, namelen);
+ np->dn_stat.st_mode |= S_IPTRANS;
+ }
+ adjust_used (namelen - np->dn->translen);
+ np->dn->trans = new;
+ np->dn->translen = namelen;
+ recompute_blocks (np);
+ return 0;
+}
+
+static error_t
+create_symlink_hook (struct node *np, const char *target)
+{
+ assert (np->dn->u.lnk == 0);
+ np->dn_stat.st_size = strlen (target);
+ if (np->dn_stat.st_size > 0)
+ {
+ const size_t size = np->dn_stat.st_size + 1;
+ np->dn->u.lnk = malloc (size);
+ if (np->dn->u.lnk == 0)
+ return ENOSPC;
+ memcpy (np->dn->u.lnk, target, size);
+ np->dn->type = DT_LNK;
+ adjust_used (size);
+ recompute_blocks (np);
+ }
+ return 0;
+}
+error_t (*diskfs_create_symlink_hook)(struct node *np, const char *target)
+ = create_symlink_hook;
+
+static error_t
+read_symlink_hook (struct node *np, char *target)
+{
+ memcpy (target, np->dn->u.lnk, np->dn_stat.st_size + 1);
+ return 0;
+}
+error_t (*diskfs_read_symlink_hook)(struct node *np, char *target)
+ = read_symlink_hook;
+
+void
+diskfs_write_disknode (struct node *np, int wait)
+{
+}
+
+void
+diskfs_file_update (struct node *np, int wait)
+{
+ diskfs_node_update (np, wait);
+}
+
+error_t
+diskfs_node_reload (struct node *node)
+{
+ return 0;
+}
+
+
+/* The user must define this function. Truncate locked node NP to be SIZE
+ bytes long. (If NP is already less than or equal to SIZE bytes
+ long, do nothing.) If this is a symlink (and diskfs_shortcut_symlink
+ is set) then this should clear the symlink, even if
+ diskfs_create_symlink_hook stores the link target elsewhere. */
+error_t
+diskfs_truncate (struct node *np, off_t size)
+{
+ if (np->dn->type == DT_LNK)
+ {
+ free (np->dn->u.lnk);
+ adjust_used (size - np->dn_stat.st_size);
+ np->dn->u.lnk = 0;
+ np->dn_stat.st_size = size;
+ return 0;
+ }
+
+ if (np->allocsize <= size)
+ return 0;
+
+ assert (np->dn->type == DT_REG);
+
+ if (default_pager == MACH_PORT_NULL)
+ return EIO;
+
+ np->dn_stat.st_size = size;
+
+ off_t set_size = size;
+ size = round_page (size);
+
+ if (np->dn->u.reg.memobj != MACH_PORT_NULL)
+ {
+ error_t err = default_pager_object_set_size (np->dn->u.reg.memobj, set_size);
+ if (err == MIG_BAD_ID)
+ /* This is an old default pager. We have no way to truncate the
+ memory object. Note that the behavior here will be wrong in
+ two ways: user accesses past the end won't fault; and, more
+ importantly, later growing the file won't zero the contents
+ past the size we just supposedly truncated to. For proper
+ behavior, use a new default pager. */
+ return 0;
+ if (err)
+ return err;
+ }
+ /* Otherwise it never had any real contents. */
+
+ adjust_used (size - np->allocsize);
+ np->dn_stat.st_blocks += (size - np->allocsize) / 512;
+ np->allocsize = size;
+
+ return 0;
+}
+
+/* The user must define this function. Grow the disk allocated to locked node
+ NP to be at least SIZE bytes, and set NP->allocsize to the actual
+ allocated size. (If the allocated size is already SIZE bytes, do
+ nothing.) CRED identifies the user responsible for the call. */
+error_t
+diskfs_grow (struct node *np, off_t size, struct protid *cred)
+{
+ assert (np->dn->type == DT_REG);
+
+ if (np->allocsize >= size)
+ return 0;
+
+ off_t set_size = size;
+ size = round_page (size);
+ if (round_page (tmpfs_space_used + size - np->allocsize)
+ / vm_page_size > tmpfs_page_limit)
+ return ENOSPC;
+
+ if (default_pager == MACH_PORT_NULL)
+ return EIO;
+
+ if (np->dn->u.reg.memobj != MACH_PORT_NULL)
+ {
+ /* Increase the limit the memory object will allow to be accessed. */
+ error_t err = default_pager_object_set_size (np->dn->u.reg.memobj, set_size);
+ if (err == MIG_BAD_ID) /* Old default pager, never limited it. */
+ err = 0;
+ if (err)
+ return err;
+ }
+
+ adjust_used (size - np->allocsize);
+ np->dn_stat.st_blocks += (size - np->allocsize) / 512;
+ np->allocsize = size;
+ return 0;
+}
+
+mach_port_t
+diskfs_get_filemap (struct node *np, vm_prot_t prot)
+{
+ error_t err;
+
+ if (np->dn->type != DT_REG)
+ {
+ errno = EOPNOTSUPP; /* ? */
+ return MACH_PORT_NULL;
+ }
+
+ if (default_pager == MACH_PORT_NULL)
+ {
+ errno = EIO;
+ return MACH_PORT_NULL;
+ }
+
+ /* We don't bother to create the memory object until the first time we
+ need it (i.e. first mapping or i/o). This way we might have a clue
+ what size it's going to be beforehand, so we can tell the default
+ pager how big to make its bitmaps. This is just an optimization for
+ the default pager; the memory object can be expanded at any time just
+ by accessing more of it. (It also optimizes the case of empty files
+ so we might never make a memory object at all.) */
+ if (np->dn->u.reg.memobj == MACH_PORT_NULL)
+ {
+ error_t err = default_pager_object_create (default_pager,
+ &np->dn->u.reg.memobj,
+ np->allocsize);
+ if (err)
+ {
+ errno = err;
+ return MACH_PORT_NULL;
+ }
+ assert (np->dn->u.reg.memobj != MACH_PORT_NULL);
+
+ /* XXX we need to keep a reference to the object, or GNU Mach
+ will terminate it when we release the map. */
+ vm_map (mach_task_self (), &np->dn->u.reg.memref, 4096, 0, 1,
+ np->dn->u.reg.memobj, 0, 0, VM_PROT_NONE, VM_PROT_NONE,
+ VM_INHERIT_NONE);
+ assert_perror (err);
+ }
+
+ /* XXX always writable */
+
+ /* Add a reference for each call, the caller will deallocate it. */
+ err = mach_port_mod_refs (mach_task_self (), np->dn->u.reg.memobj,
+ MACH_PORT_RIGHT_SEND, +1);
+ assert_perror (err);
+
+ return np->dn->u.reg.memobj;
+}
+
+/* The user must define this function. Return a `struct pager *' suitable
+ for use as an argument to diskfs_register_memory_fault_area that
+ refers to the pager returned by diskfs_get_filemap for node NP.
+ NP is locked. */
+struct pager *
+diskfs_get_filemap_pager_struct (struct node *np)
+{
+ return 0;
+}
+
+/* We have no pager of our own, so there is no need to worry about
+ users of it, or to shut it down. */
+int
+diskfs_pager_users ()
+{
+ return 0;
+}
+void
+diskfs_shutdown_pager ()
+{
+}
+
+/* The purpose of this is to decide that it's ok to make the fs read-only.
+ Turning a temporary filesystem read-only seem pretty useless. */
+vm_prot_t
+diskfs_max_user_pager_prot ()
+{
+ return VM_PROT_READ; /* Probable lie that lets us go read-only. */
+}
+
+error_t
+diskfs_S_file_get_storage_info (struct protid *cred,
+ mach_port_t **ports,
+ mach_msg_type_name_t *ports_type,
+ mach_msg_type_number_t *num_ports,
+ int **ints, mach_msg_type_number_t *num_ints,
+ off_t **offsets,
+ mach_msg_type_number_t *num_offsets,
+ char **data, mach_msg_type_number_t *data_len)
+{
+ mach_port_t memobj = diskfs_get_filemap (cred->po->np, VM_PROT_ALL);
+ if (memobj == MACH_PORT_NULL)
+ return errno;
+
+ assert (*num_ports >= 1); /* mig always gives us some */
+ *num_ports = 1;
+ *ports_type = MACH_MSG_TYPE_MOVE_SEND;
+ (*ports)[0]
+ = (cred->po->openstat & O_RDWR) == O_RDWR ? memobj : MACH_PORT_NULL;
+
+ assert (*num_offsets >= 2); /* mig always gives us some */
+ *num_offsets = 2;
+ (*offsets)[0] = 0;
+ (*offsets)[1] = cred->po->np->dn_stat.st_size;
+
+ assert (*num_ints >= 6); /* mig always gives us some */
+ *num_ints = 6;
+ (*ints)[0] = STORAGE_MEMORY;
+ (*ints)[1] = (cred->po->openstat & O_WRITE) ? 0 : STORE_READONLY;
+ (*ints)[2] = 1; /* block size */
+ (*ints)[3] = 1; /* 1 run in offsets list */
+ (*ints)[4] = 0; /* name len */
+ (*ints)[5] = 0; /* misc len */
+
+ *data_len = 0;
+
+ return 0;
+}
diff --git a/tmpfs/pager-stubs.c b/tmpfs/pager-stubs.c
new file mode 100644
index 00000000..3cb264bb
--- /dev/null
+++ b/tmpfs/pager-stubs.c
@@ -0,0 +1,96 @@
+/* stupid stub functions never called, needed because libdiskfs uses libpager
+ Copyright (C) 2001 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <hurd/pager.h>
+#include <stdlib.h>
+
+/* The user must define this function. For pager PAGER, read one
+ page from offset PAGE. Set *BUF to be the address of the page,
+ and set *WRITE_LOCK if the page must be provided read-only.
+ The only permissible error returns are EIO, EDQUOT, and ENOSPC. */
+error_t
+pager_read_page (struct user_pager_info *pager,
+ vm_offset_t page,
+ vm_address_t *buf,
+ int *write_lock)
+{
+ abort();
+ return EIEIO;
+}
+
+/* The user must define this function. For pager PAGER, synchronously
+ write one page from BUF to offset PAGE. In addition, mfree
+ (or equivalent) BUF. The only permissible error returns are EIO,
+ EDQUOT, and ENOSPC. */
+error_t
+pager_write_page (struct user_pager_info *pager,
+ vm_offset_t page,
+ vm_address_t buf)
+{
+ abort();
+ return EIEIO;
+}
+
+/* The user must define this function. A page should be made writable. */
+error_t
+pager_unlock_page (struct user_pager_info *pager,
+ vm_offset_t address)
+{
+ abort();
+ return EIEIO;
+}
+
+void
+pager_notify_evict (struct user_pager_info *pager,
+ vm_offset_t page)
+{
+ abort();
+}
+
+
+/* The user must define this function. It should report back (in
+ *OFFSET and *SIZE the minimum valid address the pager will accept
+ and the size of the object. */
+error_t
+pager_report_extent (struct user_pager_info *pager,
+ vm_address_t *offset,
+ vm_size_t *size)
+{
+ abort();
+ return EIEIO;
+}
+
+/* The user must define this function. This is called when a pager is
+ being deallocated after all extant send rights have been destroyed. */
+void
+pager_clear_user_data (struct user_pager_info *pager)
+{
+ abort();
+}
+
+/* The use must define this function. This will be called when the ports
+ library wants to drop weak references. The pager library creates no
+ weak references itself. If the user doesn't either, then it's OK for
+ this function to do nothing. */
+void
+pager_dropweak (struct user_pager_info *p)
+{
+ abort();
+}
diff --git a/tmpfs/tmpfs.c b/tmpfs/tmpfs.c
new file mode 100644
index 00000000..718c6d84
--- /dev/null
+++ b/tmpfs/tmpfs.c
@@ -0,0 +1,447 @@
+/* Main program and global state for tmpfs.
+ Copyright (C) 2000,01,02 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <argp.h>
+#include <argz.h>
+#include <string.h>
+#include <inttypes.h>
+#include <error.h>
+
+#include "tmpfs.h"
+#include <limits.h>
+#include <version.h>
+#include <fcntl.h>
+#include <hurd.h>
+#include <hurd/paths.h>
+
+char *diskfs_server_name = "tmpfs";
+char *diskfs_server_version = HURD_VERSION;
+char *diskfs_extra_version = "GNU Hurd";
+char *diskfs_disk_name = "none";
+
+/* We ain't got to show you no stinkin' sync'ing. */
+int diskfs_default_sync_interval = 0;
+
+/* We must supply some claimed limits, though we don't impose any new ones. */
+int diskfs_link_max = (1ULL << (sizeof (nlink_t) * CHAR_BIT)) - 1;
+int diskfs_name_max = 255; /* dirent d_namlen limit */
+int diskfs_maxsymlinks = 8;
+
+/* Yeah, baby, we do it all! */
+int diskfs_shortcut_symlink = 1;
+int diskfs_shortcut_chrdev = 1;
+int diskfs_shortcut_blkdev = 1;
+int diskfs_shortcut_fifo = 1;
+int diskfs_shortcut_ifsock = 1;
+
+struct node *diskfs_root_node;
+mach_port_t default_pager;
+
+off_t tmpfs_page_limit, tmpfs_space_used;
+mode_t tmpfs_root_mode = -1;
+
+error_t
+diskfs_set_statfs (struct statfs *st)
+{
+ fsblkcnt_t pages;
+
+ st->f_type = FSTYPE_MEMFS;
+ st->f_fsid = getpid ();
+
+ st->f_bsize = vm_page_size;
+ st->f_blocks = tmpfs_page_limit;
+
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ st->f_files = num_files;
+ pages = round_page (tmpfs_space_used) / vm_page_size;
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+
+ st->f_bfree = pages < tmpfs_page_limit ? tmpfs_page_limit - pages : 0;
+ st->f_bavail = st->f_bfree;
+ st->f_ffree = st->f_bavail / sizeof (struct disknode); /* Well, sort of. */
+
+ return 0;
+}
+
+
+error_t
+diskfs_set_hypermetadata (int wait, int clean)
+{
+ /* All the state always just lives in core, so we have nothing to do. */
+ return 0;
+}
+
+void
+diskfs_sync_everything (int wait)
+{
+}
+
+error_t
+diskfs_reload_global_state ()
+{
+ return 0;
+}
+
+int diskfs_synchronous = 0;
+
+#define OPT_SIZE 600 /* --size */
+
+static const struct argp_option options[] =
+{
+ {"mode", 'm', "MODE", 0, "Permissions (octal) for root directory"},
+ {"size", OPT_SIZE, "MAX-BYTES", 0, "Maximum size"},
+ {NULL,}
+};
+
+struct option_values
+{
+ off_t size;
+ mode_t mode;
+};
+
+/* Parse the size string ARG, and set *NEWSIZE with the resulting size. */
+static error_t
+parse_opt_size (const char *arg, struct argp_state *state, off_t *newsize)
+{
+ char *end = NULL;
+ intmax_t size = strtoimax (arg, &end, 0);
+ if (end == NULL || end == arg)
+ {
+ argp_error (state, "argument must be a number");
+ return EINVAL;
+ }
+ if (size < 0)
+ {
+ argp_error (state, "negative size not meaningful");
+ return EINVAL;
+ }
+ switch (*end)
+ {
+ case 'g':
+ case 'G':
+ size <<= 10;
+ case 'm':
+ case 'M':
+ size <<= 10;
+ case 'k':
+ case 'K':
+ size <<= 10;
+ break;
+ case '%':
+ {
+ /* Set as a percentage of the machine's physical memory. */
+ struct vm_statistics vmstats;
+ error_t err = vm_statistics (mach_task_self (), &vmstats);
+ if (err)
+ {
+ argp_error (state, "cannot find total physical memory: %s",
+ strerror (err));
+ return err;
+ }
+ size = round_page ((((vmstats.free_count
+ + vmstats.active_count
+ + vmstats.inactive_count
+ + vmstats.wire_count)
+ * vm_page_size)
+ * size + 99) / 100);
+ break;
+ }
+ }
+ size = (off_t) size;
+ if (size < 0)
+ {
+ argp_error (state, "size too large");
+ return EINVAL;
+ }
+
+ *newsize = size;
+
+ return 0;
+}
+
+/* Parse a command line option. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ /* We save our parsed values in this structure, hung off STATE->hook.
+ Only after parsing all options successfully will we use these values. */
+ struct option_values *values = state->hook;
+
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = state->input;
+ values = malloc (sizeof *values);
+ if (values == 0)
+ return ENOMEM;
+ state->hook = values;
+ values->size = -1;
+ values->mode = -1;
+ break;
+ case ARGP_KEY_FINI:
+ free (values);
+ state->hook = 0;
+ break;
+
+ case 'm': /* --mode=OCTAL */
+ {
+ char *end = NULL;
+ mode_t mode = strtoul (arg, &end, 8);
+ if (end == NULL || end == arg)
+ {
+ argp_error (state, "argument must be an octal number");
+ return EINVAL;
+ }
+ if (mode & S_IFMT)
+ {
+ argp_error (state, "invalid bits in mode");
+ return EINVAL;
+ }
+ values->mode = mode;
+ }
+ break;
+
+ case OPT_SIZE: /* --size=MAX-BYTES */
+ {
+ error_t err = parse_opt_size (arg, state, &values->size);
+ if (err)
+ return err;
+ }
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ if (values->size < 0)
+ {
+ argp_error (state, "must supply maximum size");
+ return EINVAL;
+ }
+ break;
+
+ case ARGP_KEY_ARGS:
+ if (state->argv[state->next + 1] != 0)
+ {
+ argp_error (state, "too many arguments");
+ return EINVAL;
+ }
+ else if (values->size >= 0)
+ {
+ if (strcmp (state->argv[state->next], "tmpfs") != 0)
+ {
+ argp_error (state, "size specified with --size and argument is not \"tmpfs\"");
+ return EINVAL;
+ }
+ }
+ else
+ {
+ error_t err = parse_opt_size (state->argv[state->next], state,
+ &values->size);
+ if (err)
+ return err;
+ }
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ /* All options parse successfully, so implement ours if possible. */
+ tmpfs_page_limit = values->size / vm_page_size;
+ tmpfs_root_mode = values->mode;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Override the standard diskfs routine so we can add our own output. */
+error_t
+diskfs_append_args (char **argz, size_t *argz_len)
+{
+ error_t err;
+
+ /* Get the standard things. */
+ err = diskfs_append_std_options (argz, argz_len);
+
+ if (!err)
+ {
+ off_t lim = tmpfs_page_limit * vm_page_size;
+ char buf[100], sfx;
+#define S(n, c) if ((lim & ((1 << n) - 1)) == 0) sfx = c, lim >>= n
+ S (30, 'G'); else S (20, 'M'); else S (10, 'K'); else sfx = '\0';
+#undef S
+ snprintf (buf, sizeof buf, "%Ld%c", lim, sfx);
+ err = argz_add (argz, argz_len, buf);
+ }
+
+ return err;
+}
+
+/* Handling of operations for the ports in diskfs_port_bucket, calling
+ * demuxer for each incoming message */
+static void *
+diskfs_thread_function (void *demuxer)
+{
+ static int thread_timeout = 1000 * 60 * 2; /* two minutes */
+ error_t err;
+
+ do
+ {
+ ports_manage_port_operations_multithread (diskfs_port_bucket,
+ (ports_demuxer_type) demuxer,
+ thread_timeout,
+ 0,
+ 0);
+ err = diskfs_shutdown (0);
+ }
+ while (err);
+
+ exit (0);
+ /* NOTREACHED */
+ return NULL;
+}
+
+
+/* Add our startup arguments to the standard diskfs set. */
+static const struct argp_child startup_children[] =
+ {{&diskfs_startup_argp}, {0}};
+static struct argp startup_argp = {options, parse_opt, "MAX-BYTES", "\
+\v\
+MAX-BYTES may be followed by k or K for kilobytes,\n\
+m or M for megabytes, g or G for gigabytes.",
+ startup_children};
+
+/* Similarly at runtime. */
+static const struct argp_child runtime_children[] =
+ {{&diskfs_std_runtime_argp}, {0}};
+static struct argp runtime_argp = {options, parse_opt, 0, 0, runtime_children};
+
+struct argp *diskfs_runtime_argp = (struct argp *)&runtime_argp;
+
+
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap, realnode, host_priv;
+ pthread_t pthread_id;
+ struct stat st;
+
+ err = argp_parse (&startup_argp, argc, argv, ARGP_IN_ORDER, NULL, NULL);
+ assert_perror (err);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (2, 0, "Must be started as a translator");
+
+ /* Get our port to the default pager. Without that,
+ we have no place to put file contents. */
+ err = get_privileged_ports (&host_priv, NULL);
+ if (err == EPERM)
+ {
+ default_pager = file_name_lookup (_SERVERS_DEFPAGER, O_EXEC, 0);
+ if (default_pager == MACH_PORT_NULL)
+ error (0, errno, _SERVERS_DEFPAGER);
+ }
+ else if (err)
+ error (0, err, "Cannot get host privileged port");
+ else
+ {
+ err = vm_set_default_memory_manager (host_priv, &default_pager);
+ mach_port_deallocate (mach_task_self (), host_priv);
+ if (err)
+ error (0, err, "Cannot get default pager port");
+ }
+ if (default_pager == MACH_PORT_NULL)
+ error (0, 0, "files cannot have contents with no default pager port");
+
+ /* Initialize the diskfs library. Must come before any other diskfs call. */
+ err = diskfs_init_diskfs ();
+ if (err)
+ error (4, err, "init");
+
+ err = diskfs_alloc_node (0, S_IFDIR, &diskfs_root_node);
+ if (err)
+ error (4, err, "cannot create root directory");
+
+ /* Like diskfs_spawn_first_thread. But do it manually, without timeout */
+ err = pthread_create (&pthread_id, NULL, diskfs_thread_function,
+ diskfs_demuxer);
+ if (!err)
+ pthread_detach (pthread_id);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+
+ /* Now that we are all set up to handle requests, and diskfs_root_node is
+ set properly, it is safe to export our fsys control port to the
+ outside world. */
+ realnode = diskfs_startup_diskfs (bootstrap, 0);
+ diskfs_root_node->dn_stat.st_mode = S_IFDIR;
+
+ /* Propagate permissions, owner, etc. from underlying node to
+ the root directory of the new (empty) filesystem. */
+ err = io_stat (realnode, &st);
+ if (err)
+ {
+ error (0, err, "cannot stat underlying node");
+ if (tmpfs_root_mode == -1)
+ diskfs_root_node->dn_stat.st_mode |= 0777 | S_ISVTX;
+ else
+ diskfs_root_node->dn_stat.st_mode |= tmpfs_root_mode;
+ diskfs_root_node->dn_set_ctime = 1;
+ diskfs_root_node->dn_set_mtime = 1;
+ diskfs_root_node->dn_set_atime = 1;
+ }
+ else
+ {
+ if (tmpfs_root_mode == -1)
+ {
+ diskfs_root_node->dn_stat.st_mode |= st.st_mode &~ S_IFMT;
+ if (S_ISREG (st.st_mode) && (st.st_mode & 0111) == 0)
+ /* There are no execute bits set, as by default on a plain file.
+ For the virtual directory, set execute bits where read bits are
+ set on the underlying plain file. */
+ diskfs_root_node->dn_stat.st_mode |= (st.st_mode & 0444) >> 2;
+ }
+ else
+ diskfs_root_node->dn_stat.st_mode |= tmpfs_root_mode;
+ diskfs_root_node->dn_stat.st_uid = st.st_uid;
+ diskfs_root_node->dn_stat.st_author = st.st_author;
+ diskfs_root_node->dn_stat.st_gid = st.st_gid;
+ diskfs_root_node->dn_stat.st_atim = st.st_atim;
+ diskfs_root_node->dn_stat.st_mtim = st.st_mtim;
+ diskfs_root_node->dn_stat.st_ctim = st.st_ctim;
+ diskfs_root_node->dn_stat.st_flags = st.st_flags;
+ }
+ diskfs_root_node->dn_stat.st_mode &= ~S_ITRANS;
+ diskfs_root_node->dn_stat.st_mode |= S_IROOT;
+ diskfs_root_node->dn_stat.st_nlink = 2;
+
+ /* We must keep the REALNODE send right to remain the active
+ translator for the underlying node. */
+
+ pthread_mutex_unlock (&diskfs_root_node->lock);
+
+ /* and so we die, leaving others to do the real work. */
+ pthread_exit (NULL);
+ /* NOTREACHED */
+ return 0;
+}
diff --git a/tmpfs/tmpfs.h b/tmpfs/tmpfs.h
new file mode 100644
index 00000000..b3c636d0
--- /dev/null
+++ b/tmpfs/tmpfs.h
@@ -0,0 +1,85 @@
+/* Private data structures for tmpfs.
+ Copyright (C) 2000 Free Software Foundation, Inc.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _tmpfs_h
+#define _tmpfs_h 1
+
+#include <hurd/diskfs.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdint.h>
+
+struct disknode
+{
+ uint_fast8_t type; /* DT_REG et al */
+
+ unsigned int gen;
+ off_t size;
+ mode_t mode;
+ nlink_t nlink;
+ uid_t uid, author;
+ gid_t gid;
+ struct timespec atime, mtime, ctime;
+ unsigned int flags;
+
+ char *trans;
+ size_t translen;
+
+ union
+ {
+ char *lnk; /* malloc'd symlink target */
+ struct
+ {
+ mach_port_t memobj;
+ vm_address_t memref;
+ unsigned int allocpages; /* largest size while memobj was live */
+ } reg;
+ struct
+ {
+ struct tmpfs_dirent *entries;
+ struct disknode *dotdot;
+ } dir;
+ dev_t chr, blk;
+ } u;
+
+ struct node *hnext, **hprevp;
+};
+
+struct tmpfs_dirent
+{
+ struct tmpfs_dirent *next;
+ struct disknode *dn;
+ uint8_t namelen;
+ char name[0];
+};
+
+extern unsigned int num_files;
+extern off_t tmpfs_page_limit, tmpfs_space_used;
+
+extern mach_port_t default_pager;
+
+static inline void
+adjust_used (off_t change)
+{
+ pthread_spin_lock (&diskfs_node_refcnt_lock);
+ tmpfs_space_used += change;
+ pthread_spin_unlock (&diskfs_node_refcnt_lock);
+}
+
+#endif
diff --git a/trans/Makefile b/trans/Makefile
new file mode 100644
index 00000000..71e64248
--- /dev/null
+++ b/trans/Makefile
@@ -0,0 +1,67 @@
+#
+# Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2002, 2006, 2007,
+# 2008, 2013, 2014 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := trans
+makemode := servers
+
+targets = symlink firmlink ifsock magic null fifo new-fifo fwd crash \
+ password hello hello-mt streamio fakeroot proxy-defpager remap \
+ mtab
+SRCS = ifsock.c symlink.c magic.c null.c fifo.c new-fifo.c fwd.c \
+ crash.c firmlink.c password.c hello.c hello-mt.c streamio.c \
+ fakeroot.c proxy-defpager.c remap.c mtab.c
+OBJS = $(SRCS:.c=.o) fsysServer.o ifsockServer.o passwordServer.o \
+ crashServer.o crash_replyUser.o msgServer.o \
+ default_pagerServer.o default_pagerUser.o \
+ device_replyServer.o elfcore.o
+HURDLIBS = ports netfs trivfs iohelp fshelp pipe ihash shouldbeinlibc
+LDLIBS += -lpthread
+password-LDLIBS = -lcrypt
+password-MIGSFLAGS=\
+ "-DIO_INTRAN=trivfs_protid_t trivfs_begin_using_protid (io_t)" \
+ "-DIO_DESTRUCTOR=trivfs_end_using_protid (trivfs_protid_t)" \
+ "-DPASSWORD_IMPORTS=import \"../libtrivfs/mig-decls.h\";"
+
+ifsock-MIGSFLAGS=\
+ "-DFILE_INTRAN=trivfs_protid_t trivfs_begin_using_protid (io_t)" \
+ "-DFILE_DESTRUCTOR=trivfs_end_using_protid (trivfs_protid_t)" \
+ "-DIFSOCK_IMPORTS=import \"../libtrivfs/mig-decls.h\";"
+
+include ../Makeconf
+
+vpath elfcore.c $(top_srcdir)/exec
+
+symlink: fsysServer.o
+ifsock: ifsockServer.o
+crash: crashServer.o crash_replyUser.o msgServer.o elfcore.o
+password: passwordServer.o
+streamio: device_replyServer.o
+proxy-defpager: default_pagerServer.o default_pagerUser.o
+
+proxy-defpager crash password streamio: ../libports/libports.a ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a
+fifo new-fifo: ../libpipe/libpipe.a
+fwd: ../libfshelp/libfshelp.a ../libports/libports.a
+hello-mt magic null ifsock fifo new-fifo firmlink: ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a ../libports/libports.a ../libihash/libihash.a
+magic: ../libiohelp/libiohelp.a
+hello: ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a ../libports/libports.a ../libihash/libihash.a
+fakeroot: ../libnetfs/libnetfs.a ../libfshelp/libfshelp.a ../libiohelp/libiohelp.a ../libports/libports.a ../libihash/libihash.a
+remap: ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a ../libports/libports.a ../libihash/libihash.a
+mtab: ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a ../libports/libports.a ../libihash/libihash.a fsUser.o
+$(targets): ../libshouldbeinlibc/libshouldbeinlibc.a
+
+$(targets): %: %.o
diff --git a/trans/bogus-fifo.c b/trans/bogus-fifo.c
new file mode 100644
index 00000000..acad6e4b
--- /dev/null
+++ b/trans/bogus-fifo.c
@@ -0,0 +1,160 @@
+/* A translator for fifos
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <error.h>
+#include <sys/socket.h>
+#include <hurd/paths.h>
+#include <hurd/socket.h>
+#include <hurd/fsys.h>
+#include "fsys_S.h"
+
+/* ---------------------------------------------------------------- */
+
+extern int fsys_server (mach_msg_header_t *, mach_msg_header_t *);
+
+/* The actual fifo, which is just a socket connected to itself. */
+static socket_t fifo;
+
+void
+main (int argc, char **argv)
+{
+ error_t err;
+ char pflocal_name[1024];
+ mach_port_t pflocal;
+ mach_port_t bootstrap, fsys, realnode;
+
+ if (argc != 1)
+ {
+ fprintf(stderr, "Usage: %s", program_invocation_name);
+ exit(-1);
+ }
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error(3, 0, "Must be started as a translator");
+
+ /* Reply to our parent */
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &fsys);
+ err = fsys_startup (bootstrap, fsys, MACH_MSG_TYPE_MAKE_SEND, &realnode);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error(1, err, "starting translator");
+
+ /* Try and connect to the pflocal server */
+ sprintf (pflocal_name, "%s/%d", _SERVERS_SOCKET, PF_LOCAL);
+ pflocal = file_name_lookup (pflocal_name, 0, 0);
+ if (pflocal == MACH_PORT_NULL)
+ error (2, errno, "%s", pflocal_name);
+
+ /* Make a local domain socket to use for our data buffering. */
+ err = socket_create (pflocal, SOCK_STREAM, 0, &fifo);
+ if (err)
+ error (3, err, "%s: socket_create", pflocal_name);
+
+ /* Connect the socket to itself, yielding a fifo! */
+ err = socket_connect2 (fifo, fifo);
+ if (err)
+ error (3, err, "%s: socket_connect2", pflocal_name);
+
+ for (;;)
+ /* We don't ever time out. The problem is, you only want to exit when
+ (1) the pipe is empty (which we can check), and (2) there are no other
+ users (which we can't). If we just drop our ref to the pipe, there
+ still could be a writer holding a ref to it. */
+ mach_msg_server_timeout (fsys_server, 0, fsys, 0, 0);
+}
+
+/* ---------------------------------------------------------------- */
+
+error_t
+S_fsys_getroot (mach_port_t fsys, mach_port_t parent,
+ uid_t *uids, unsigned num_uids, gid_t *gids, unsigned num_gids,
+ int flags,
+ retry_type *do_retry, char *retry_name,
+ mach_port_t *result, mach_msg_type_name_t *result_type)
+{
+ /* Give back a handle on our fifo. */
+ *do_retry = FS_RETRY_NORMAL;
+ *retry_name = '\0';
+ *result = fifo;
+ *result_type = MACH_MSG_TYPE_COPY_SEND;
+ return 0;
+}
+
+error_t
+S_fsys_startup (mach_port_t bootstrap, mach_port_t fsys,
+ mach_port_t *real, mach_msg_type_name_t *real_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_goaway (mach_port_t fsys, int flags)
+{
+ /* If there are refs to the fifo left besides ours, it will
+ stay around after we're gone. */
+ exit (0);
+}
+
+error_t
+S_fsys_syncfs (mach_port_t fsys, int wait, int recurse)
+{
+ return 0;
+}
+
+error_t
+S_fsys_set_options (mach_port_t fsys,
+ char *data, mach_msg_type_number_t data_len, int recurse)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_getfile (mach_port_t fsys,
+ uid_t *uids, unsigned num_uids, gid_t *gids, unsigned num_gids,
+ char *handle, unsigned handle_len,
+ mach_port_t *port, mach_msg_type_name_t *port_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_getpriv (mach_port_t fsys,
+ mach_port_t *hostpriv, mach_port_t *devmaster, task_t *fstask)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_init (mach_port_t fsys,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mach_port_t proc, auth_t auth)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_forward (mach_port_t server, mach_port_t requestor,
+ char *argz, size_t argz_len)
+{
+ return EOPNOTSUPP;
+}
diff --git a/trans/crash.c b/trans/crash.c
new file mode 100644
index 00000000..4a59d450
--- /dev/null
+++ b/trans/crash.c
@@ -0,0 +1,800 @@
+/* GNU Hurd standard crash dump server.
+
+ Copyright (C) 1995, 1996, 1997, 1999, 2000, 2001, 2002, 2006, 2007
+ Free Software Foundation, Inc.
+
+ Written by Roland McGrath.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <fcntl.h>
+#include <hurd/trivfs.h>
+#include <sys/wait.h>
+#include <error.h>
+#include <argp.h>
+#include <argz.h>
+#include <sys/mman.h>
+
+#include <version.h>
+
+#include "crash_S.h"
+#include "crash_reply_U.h"
+#include "msg_S.h"
+
+
+const char *argp_program_version = STANDARD_HURD_VERSION (crash);
+
+process_t procserver; /* Our proc port, for easy access. */
+
+/* Port bucket we service requests on. */
+struct port_bucket *port_bucket;
+
+/* Trivfs hooks. */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = O_READ|O_WRITE|O_EXEC;
+
+struct port_class *trivfs_protid_portclasses[1];
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_protid_nportclasses = 1;
+int trivfs_cntl_nportclasses = 1;
+
+struct trivfs_control *fsys;
+
+enum crash_action
+{
+ crash_unspecified,
+ crash_suspend,
+ crash_kill,
+ crash_corefile
+};
+#define CRASH_DEFAULT crash_suspend
+#define CRASH_ORPHANS_DEFAULT crash_corefile
+
+static enum crash_action crash_how, crash_orphans_how;
+
+
+/* This is defined in ../exec/elfcore.c, or we could have
+ different implementations for other formats. */
+extern error_t dump_core (task_t task, file_t file, off_t corelimit,
+ int signo, long int sigcode, int sigerror);
+
+/* This data structure describes a crashing task which we have suspended.
+ This is attached to a receive right we have set as the process's message
+ port, so we can get a msg_sig_post RPC to continue or kill the process. */
+
+struct crasher
+ {
+ struct port_info pi;
+
+ /* How to reply to the crash_dump_task RPC. The RPC remains "in
+ progress" while the process is in crash suspension. If the process
+ is resumed with SIGCONT, we will dump a core file and then reply to
+ the RPC. */
+ mach_port_t reply_port;
+ mach_msg_type_name_t reply_type;
+
+ task_t task;
+ file_t core_file;
+ off_t core_limit;
+ int signo, sigcode, sigerror;
+
+ mach_port_t original_msgport; /* Restore on resume. */
+ mach_port_t sidport; /* Session ID port for SIGCONT auth. */
+ process_t proc; /* Proc server port. */
+ };
+
+struct port_class *crasher_portclass;
+
+/* If the process referred to by proc port USERPROC is not orphaned,
+ then send SIGTSTP to all the other members of its pgrp. */
+void
+stop_pgrp (process_t userproc, mach_port_t cttyid)
+{
+ pid_t pid, ppid, pgrp;
+ int orphaned;
+ error_t err;
+ size_t numpids = 20;
+ pid_t pids_[numpids], *pids = pids_;
+ int i;
+
+ err = proc_getpids (userproc, &pid, &ppid, &orphaned);
+ if (err || orphaned)
+ return;
+ err = proc_getpgrp (userproc, pid, &pgrp);
+ if (err)
+ return;
+
+ /* Use USERPROC so that if it's just died we get an error and don't do
+ anything. */
+ err = proc_getpgrppids (userproc, pgrp, &pids, &numpids);
+ if (err)
+ return;
+
+ for (i = 0; i < numpids; i++)
+ if (pids[i] != pid)
+ {
+ mach_port_t msgport;
+ if (proc_getmsgport (userproc, pids[i], &msgport))
+ continue;
+ msg_sig_post (msgport, SIGTSTP, 0, cttyid);
+ mach_port_deallocate (mach_task_self (), msgport);
+ }
+ if (pids != pids_)
+ munmap (pids, numpids);
+}
+
+
+kern_return_t
+S_crash_dump_task (mach_port_t port,
+ mach_port_t reply_port, mach_msg_type_name_t reply_type,
+ task_t task, file_t core_file,
+ int signo, integer_t sigcode, int sigerror,
+ natural_t exc, natural_t code, natural_t subcode,
+ mach_port_t ctty_id)
+{
+ error_t err;
+ struct trivfs_protid *cred;
+ mach_port_t user_proc = MACH_PORT_NULL;
+ enum crash_action how;
+
+ cred = ports_lookup_port (port_bucket, port, trivfs_protid_portclasses[0]);
+ if (! cred)
+ return EOPNOTSUPP;
+
+ how = crash_how;
+ if (crash_how != crash_orphans_how)
+ {
+ /* We must ascertain if this is an orphan before deciding what to do. */
+ err = proc_task2proc (procserver, task, &user_proc);
+ if (!err)
+ {
+ pid_t pid, ppid;
+ int orphan;
+ err = proc_getpids (user_proc, &pid, &ppid, &orphan);
+ if (!err && orphan)
+ how = crash_orphans_how;
+ }
+ }
+
+ switch (how)
+ {
+ default: /* NOTREACHED */
+ err = EGRATUITOUS;
+ break;
+
+ case crash_suspend:
+ /* Suspend the task first thing before being twiddling it. */
+ err = task_suspend (task);
+ if (err)
+ break;
+
+ if (user_proc != MACH_PORT_NULL)
+ err = proc_task2proc (procserver, task, &user_proc);
+ if (! err)
+ {
+ struct crasher *c;
+
+ err = ports_create_port (crasher_portclass, port_bucket,
+ sizeof *c, &c);
+ if (! err)
+ {
+ mach_port_t msgport;
+
+ stop_pgrp (user_proc, ctty_id);
+
+ /* Install our port as the crasher's msgport.
+ We will wait for signals to resume (crash) it. */
+ msgport = ports_get_send_right (c);
+ err = proc_setmsgport (user_proc, msgport, &c->original_msgport);
+ mach_port_deallocate (mach_task_self (), msgport);
+
+ c->reply_port = reply_port;
+ c->reply_type = reply_type;
+ if (proc_getsidport (user_proc, &c->sidport))
+ c->sidport = MACH_PORT_NULL;
+ c->proc = user_proc;
+
+ /* Tell the proc server the crasher stopped. */
+ proc_mark_stop (user_proc, signo, sigcode);
+
+ c->task = task;
+ c->core_file = core_file;
+ c->core_limit = (off_t) -1; /* XXX should core limit in RPC */
+ c->signo = signo;
+ c->sigcode = sigcode;
+ c->sigerror = sigerror;
+
+ err = MIG_NO_REPLY;
+ ports_port_deref (c);
+ }
+ }
+ if (err != MIG_NO_REPLY)
+ task_resume (task);
+ break;
+
+ case crash_corefile:
+ err = task_suspend (task);
+ if (!err)
+ {
+ err = dump_core (task, core_file,
+ (off_t) -1, /* XXX should get core limit in RPC */
+ signo, sigcode, sigerror);
+ task_resume (task);
+ }
+ break;
+
+ case crash_kill:
+ {
+ if (user_proc != MACH_PORT_NULL)
+ err = 0;
+ else
+ err = proc_task2proc (procserver, task, &user_proc);
+ if (!err)
+ err = proc_mark_exit (user_proc, W_EXITCODE (0, signo), sigcode);
+ err = task_terminate (task);
+ if (!err)
+ {
+ mach_port_deallocate (mach_task_self (), task);
+ mach_port_deallocate (mach_task_self (), core_file);
+ mach_port_deallocate (mach_task_self (), ctty_id);
+ }
+ }
+ }
+
+ if (user_proc != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), user_proc);
+
+ ports_port_deref (cred);
+ return err;
+}
+
+
+
+/* Handle an attempt to send a signal to crashing task C. */
+
+static error_t
+signal_crasher (struct crasher *c, int signo, int sigcode, mach_port_t refport)
+{
+ error_t err;
+
+ if (refport != c->task && (refport != c->sidport || signo != SIGCONT))
+ err = EPERM;
+ else
+ switch (signo)
+ {
+ case SIGTERM:
+ case SIGKILL:
+ /* Kill it as asked. */
+ proc_mark_exit (c->proc, W_EXITCODE (0, signo), sigcode);
+ err = task_terminate (c->task);
+ break;
+
+ case SIGCONT:
+ case SIGQUIT:
+ {
+ /* Resuming the process should make it dump core. */
+
+ mach_port_t old;
+
+ /* First, restore its msg port. */
+ err = proc_setmsgport (c->proc, c->original_msgport, &old);
+ mach_port_deallocate (mach_task_self (), old);
+
+ /* Tell the proc server it has resumed. */
+ proc_mark_cont (c->proc);
+
+ /* Reset the proc server port stored in C, and then destroy the
+ receive right. The null proc port tells dead_crasher to dump
+ a core file. */
+ mach_port_deallocate (mach_task_self (), c->proc);
+ c->proc = MACH_PORT_NULL;
+ ports_destroy_right (c);
+ }
+ break;
+
+ default:
+ err = EBUSY;
+ break;
+ }
+
+ ports_port_deref (c);
+ return err;
+}
+
+kern_return_t
+S_msg_sig_post (mach_port_t port,
+ mach_port_t reply_port, mach_msg_type_name_t reply_type,
+ int signo, natural_t sigcode, mach_port_t refport)
+{
+ struct crasher *c = ports_lookup_port (port_bucket, port, crasher_portclass);
+
+ if (! c)
+ return EOPNOTSUPP;
+
+ return signal_crasher (c, signo, sigcode, refport);
+}
+
+kern_return_t
+S_msg_sig_post_untraced (mach_port_t port,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ int signo, natural_t sigcode, mach_port_t refport)
+{
+ error_t err;
+ struct crasher *c = ports_lookup_port (port_bucket, port, crasher_portclass);
+
+ if (! c)
+ return EOPNOTSUPP;
+
+ if (signo != 0 && signo != c->signo)
+ return signal_crasher (c, signo, sigcode, refport);
+
+ if (refport != c->task)
+ err = EPERM;
+ else
+ {
+ /* Debugger attaching to the process and continuing it.
+ The debugger is welcome to this crasher, so let's
+ just restore his msgport and forget him. */
+
+ mach_port_t old;
+
+ /* First, restore its msg port. */
+ err = proc_setmsgport (c->proc, c->original_msgport, &old);
+ if (! err)
+ {
+ mach_port_deallocate (mach_task_self (), old);
+
+ /* Tell the proc server it has stopped (again)
+ with the original crash signal. */
+ proc_mark_stop (c->proc, c->signo, c->sigcode);
+
+ /* We don't need to listen on this msgport any more. */
+ ports_destroy_right (c);
+ }
+ }
+
+ ports_port_deref (c);
+ return err;
+}
+
+/* This gets called when the receive right for a crasher message port dies. */
+
+void
+dead_crasher (void *ptr)
+{
+ struct crasher *c = ptr;
+
+ if (c->proc != MACH_PORT_NULL)
+ {
+ /* This message port just died. Clean it up. */
+ mach_port_deallocate (mach_task_self (), c->proc);
+ if (c->reply_port != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), c->reply_port);
+ }
+ else
+ {
+ /* C->proc was cleared in S_msg_sig_post as a marker that
+ this crasher should get a core dump when we clean him up. */
+ error_t err = dump_core (c->task, c->core_file, c->core_limit,
+ c->signo, c->sigcode, c->sigerror);
+ /* Now reply to the crasher's original RPC which started this whole
+ party. He should now report his own death (with core dump iff ERR
+ reports successful dumping) to his proc server. */
+ crash_dump_task_reply (c->reply_port, c->reply_type, err);
+ /* Resume the task so it can receive our reply and die happily. */
+ task_resume (c->task);
+ }
+
+ /* Deallocate the other saved ports. */
+ mach_port_deallocate (mach_task_self (), c->original_msgport);
+ mach_port_deallocate (mach_task_self (), c->sidport);
+ mach_port_deallocate (mach_task_self (), c->task);
+ mach_port_deallocate (mach_task_self (), c->core_file);
+ mach_port_deallocate (mach_task_self (), c->sidport);
+
+ /* The port data structures are cleaned up when we return. */
+
+ /* See if we are going away and this was the last thing keeping us up. */
+ if (ports_count_class (trivfs_cntl_portclasses[0]) == 0)
+ {
+ /* We have no fsys control port, so we are detached from the
+ parent filesystem. Maybe we have no users left either. */
+ if (ports_count_class (trivfs_protid_portclasses[0]) == 0)
+ {
+ /* We have no user ports left. Maybe we have no crashers still
+ around either. */
+ if (ports_count_class (crasher_portclass) == 0)
+ /* Nobody talking. Time to die. */
+ exit (0);
+ ports_enable_class (crasher_portclass);
+ }
+ ports_enable_class (trivfs_protid_portclasses[0]);
+ }
+ ports_enable_class (trivfs_cntl_portclasses[0]);
+}
+
+
+static const struct argp_option options[] =
+{
+ {0,0,0,0,"These options specify the disposition of a crashing process:", 1},
+ {"action", 'a', "ACTION", 0, "Action taken on crashing processes", 1},
+ {"orphan-action", 'O', "ACTION", 0, "Action taken on crashing orphans", 1},
+
+ {0,0,0,0,"These options are synonyms for --action=OPTION:", 2},
+ {"suspend", 's', 0, 0, "Suspend the process", 2},
+ {"kill", 'k', 0, 0, "Kill the process", 2},
+ {"core-file", 'c', 0, 0, "Dump a core file", 2},
+ {"dump-core", 0, 0, OPTION_ALIAS },
+ {0}
+};
+static const char doc[] =
+"Server to handle crashing tasks and dump core files or equivalent.\v"
+"The ACTION values can be `suspend', `kill', or `core-file'.\n\n"
+"If `--orphan-action' is not specified, the `--action' value is used for "
+"orphans. The default is `--action=suspend --orphan-action=core-file'.";
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ error_t parse_action (enum crash_action *how)
+ {
+ if (!strcmp (arg, "suspend"))
+ *how = crash_suspend;
+ else if (!strcmp (arg, "kill"))
+ *how = crash_kill;
+ else if (!strcmp (arg, "core-file"))
+ *how = crash_corefile;
+ else
+ {
+ argp_error (state,
+ "action must be one of: suspend, kill, core-file");
+ return EINVAL;
+ }
+ return 0;
+ }
+
+ switch (opt)
+ {
+ default:
+ return ARGP_ERR_UNKNOWN;
+ case ARGP_KEY_INIT:
+ case ARGP_KEY_ERROR:
+ break;
+
+ case 'a':
+ return parse_action (&crash_how);
+ case 'O':
+ return parse_action (&crash_orphans_how);
+
+ case 's': crash_how = crash_suspend; break;
+ case 'k': crash_how = crash_kill; break;
+ case 'c': crash_how = crash_corefile; break;
+
+ case ARGP_KEY_SUCCESS:
+ if (crash_orphans_how == crash_unspecified)
+ crash_orphans_how = (crash_how == crash_unspecified
+ ? CRASH_ORPHANS_DEFAULT : crash_how);
+ if (crash_how == crash_unspecified)
+ crash_how = CRASH_DEFAULT;
+ break;
+ }
+ return 0;
+}
+
+error_t
+trivfs_append_args (struct trivfs_control *fsys,
+ char **argz, size_t *argz_len)
+{
+ error_t err;
+ const char *opt;
+
+ switch (crash_how)
+ {
+ case crash_suspend: opt = "--action=suspend"; break;
+ case crash_kill: opt = "--action=kill"; break;
+ case crash_corefile: opt = "--action=core-file"; break;
+ default:
+ return EGRATUITOUS;
+ }
+ err = argz_add (argz, argz_len, opt);
+
+ if (!err)
+ {
+ switch (crash_orphans_how)
+ {
+ case crash_suspend: opt = "--orphan-action=suspend"; break;
+ case crash_kill: opt = "--orphan-action=kill"; break;
+ case crash_corefile: opt = "--orphan-action=core-file"; break;
+ default:
+ return EGRATUITOUS;
+ }
+ err = argz_add (argz, argz_len, opt);
+ }
+
+ return err;
+}
+
+struct argp crash_argp = { options, parse_opt, 0, doc };
+struct argp *trivfs_runtime_argp = &crash_argp;
+
+
+static int
+crash_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ extern int crash_server (mach_msg_header_t *inp, mach_msg_header_t *outp);
+ extern int msg_server (mach_msg_header_t *inp, mach_msg_header_t *outp);
+ return (crash_server (inp, outp) ||
+ msg_server (inp, outp) ||
+ trivfs_demuxer (inp, outp));
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+
+ argp_parse (&crash_argp, argc, argv, 0,0,0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (2, 0, "Must be started as a translator");
+
+ /* Fetch our proc server port for easy use. */
+ procserver = getproc ();
+
+ port_bucket = ports_create_bucket ();
+ trivfs_cntl_portclasses[0] = ports_create_class (trivfs_clean_cntl, 0);
+ trivfs_protid_portclasses[0] = ports_create_class (trivfs_clean_protid, 0);
+ crasher_portclass = ports_create_class (dead_crasher, 0);
+
+ /* Reply to our parent. */
+ err = trivfs_startup (bootstrap, 0,
+ trivfs_cntl_portclasses[0], port_bucket,
+ trivfs_protid_portclasses[0], port_bucket,
+ &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (3, err, "Contacting parent");
+
+ /* Launch. */
+ do
+ ports_manage_port_operations_multithread (port_bucket, crash_demuxer,
+ 10 * 1000, /* idle thread */
+ 10 * 60 * 1000, /* idle server */
+ 0);
+ /* That returns when 10 minutes pass without an RPC. Try shutting down
+ as if sent fsys_goaway; if we have any users who need us to stay
+ around, this returns EBUSY and we loop to service more RPCs. */
+ while (trivfs_goaway (fsys, 0));
+
+ return 0;
+}
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ int count;
+
+ /* Stop new requests. */
+ ports_inhibit_class_rpcs (trivfs_cntl_portclasses[0]);
+ ports_inhibit_class_rpcs (trivfs_protid_portclasses[0]);
+
+ /* Are there any extant user ports for the /servers/crash file? */
+ count = ports_count_class (trivfs_protid_portclasses[0]);
+ if (count == 0 || (flags & FSYS_GOAWAY_FORCE))
+ {
+ /* No users. Disconnect from the filesystem. */
+ mach_port_deallocate (mach_task_self (), fsys->underlying);
+
+ /* Are there any crasher message ports we are listening on? */
+ count = ports_count_class (crasher_portclass);
+ if (count == 0)
+ /* Nope. We got no reason to live. */
+ exit (0);
+
+ /* Continue babysitting crashing tasks we previously suspended. */
+ ports_enable_class (crasher_portclass);
+
+ /* No more communication with the parent filesystem. */
+ ports_destroy_right (fsys);
+
+ return 0;
+ }
+ else
+ {
+ /* We won't go away, so start things going again... */
+ ports_enable_class (trivfs_protid_portclasses[0]);
+ ports_resume_class_rpcs (trivfs_cntl_portclasses[0]);
+ ports_resume_class_rpcs (trivfs_protid_portclasses[0]);
+
+ return EBUSY;
+ }
+}
+
+/* Stubs for unused msgport RPCs. */
+
+kern_return_t
+S_msg_proc_newids (mach_port_t process,
+ mach_port_t task,
+ pid_t ppid,
+ pid_t pgrp,
+ int orphaned)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_add_auth (mach_port_t process,
+ auth_t auth)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_del_auth (mach_port_t process,
+ mach_port_t task,
+ intarray_t uids,
+ mach_msg_type_number_t uidsCnt,
+ intarray_t gids,
+ mach_msg_type_number_t gidsCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_init_port (mach_port_t process,
+ mach_port_t refport,
+ int which,
+ mach_port_t *port,
+ mach_msg_type_name_t *portPoly)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_init_port (mach_port_t process,
+ mach_port_t refport,
+ int which,
+ mach_port_t port)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_init_ports (mach_port_t process,
+ mach_port_t refport,
+ portarray_t *ports,
+ mach_msg_type_name_t *portsPoly,
+ mach_msg_type_number_t *portsCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_init_ports (mach_port_t process,
+ mach_port_t refport,
+ portarray_t ports,
+ mach_msg_type_number_t portsCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_init_int (mach_port_t process,
+ mach_port_t refport,
+ int which,
+ int *value)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_init_int (mach_port_t process,
+ mach_port_t refport,
+ int which,
+ int value)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_init_ints (mach_port_t process,
+ mach_port_t refport,
+ intarray_t *values,
+ mach_msg_type_number_t *valuesCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_init_ints (mach_port_t process,
+ mach_port_t refport,
+ intarray_t values,
+ mach_msg_type_number_t valuesCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_dtable (mach_port_t process,
+ mach_port_t refport,
+ portarray_t *dtable,
+ mach_msg_type_name_t *dtablePoly,
+ mach_msg_type_number_t *dtableCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_dtable (mach_port_t process,
+ mach_port_t refport,
+ portarray_t dtable,
+ mach_msg_type_number_t dtableCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_fd (mach_port_t process,
+ mach_port_t refport,
+ int fd,
+ mach_port_t *port,
+ mach_msg_type_name_t *portPoly)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_fd (mach_port_t process,
+ mach_port_t refport,
+ int fd,
+ mach_port_t port)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_environment (mach_port_t process,
+ data_t *value,
+ mach_msg_type_number_t *valueCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_environment (mach_port_t process,
+ mach_port_t refport,
+ data_t value,
+ mach_msg_type_number_t valueCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_env_variable (mach_port_t process,
+ string_t variable,
+ data_t *value,
+ mach_msg_type_number_t *valueCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_env_variable (mach_port_t process,
+ mach_port_t refport,
+ string_t variable,
+ string_t value,
+ boolean_t replace)
+{ return EBUSY; }
+kern_return_t
+S_msg_get_exec_flags (mach_port_t process, mach_port_t refport, int *flags)
+{ return EBUSY; }
+kern_return_t
+S_msg_set_all_exec_flags (mach_port_t process, mach_port_t refport, int flags)
+{ return EBUSY; }
+kern_return_t
+S_msg_set_some_exec_flags (mach_port_t process, mach_port_t refport, int flags)
+{ return EBUSY; }
+kern_return_t
+S_msg_clear_some_exec_flags (mach_port_t process, mach_port_t refport,
+ int flags)
+{ return EBUSY; }
+error_t
+S_msg_report_wait (mach_port_t process, thread_t thread,
+ string_t desc, mach_msg_id_t *rpc)
+{ return EBUSY; }
+error_t
+S_msg_describe_ports (mach_port_t msgport, mach_port_t refport,
+ mach_port_t *ports, mach_msg_type_number_t nports,
+ char **desc, mach_msg_type_number_t *desclen)
+{ return EBUSY; }
diff --git a/trans/fakeroot.c b/trans/fakeroot.c
new file mode 100644
index 00000000..32a34ec4
--- /dev/null
+++ b/trans/fakeroot.c
@@ -0,0 +1,1041 @@
+/* fakeroot -- a translator for faking actions that aren't really permitted
+ Copyright (C) 2002, 2003, 2008, 2013 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <hurd/netfs.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <argp.h>
+#include <error.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <pthread.h>
+#include <hurd/ihash.h>
+#include <hurd/paths.h>
+
+#include <version.h>
+
+#include "libnetfs/fs_S.h"
+#include "libnetfs/io_S.h"
+#include "libnetfs/fsys_S.h"
+#include "libports/notify_S.h"
+#include "libports/interrupt_S.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (fakeroot);
+
+char *netfs_server_name = "fakeroot";
+char *netfs_server_version = HURD_VERSION;
+int netfs_maxsymlinks = 16; /* arbitrary */
+
+static auth_t fakeroot_auth_port;
+
+struct netnode
+{
+ hurd_ihash_locp_t idport_locp;/* easy removal pointer in idport ihash */
+ mach_port_t idport; /* port from io_identity */
+ int openmodes; /* O_READ | O_WRITE | O_EXEC */
+ file_t file; /* port on real file */
+
+ unsigned int faked;
+};
+
+#define FAKE_UID (1 << 0)
+#define FAKE_GID (1 << 1)
+#define FAKE_AUTHOR (1 << 2)
+#define FAKE_MODE (1 << 3)
+#define FAKE_DEFAULT (1 << 4)
+
+pthread_mutex_t idport_ihash_lock = PTHREAD_MUTEX_INITIALIZER;
+struct hurd_ihash idport_ihash
+= HURD_IHASH_INITIALIZER (sizeof (struct node)
+ + offsetof (struct netnode, idport_locp));
+
+
+/* Make a new virtual node. Always consumes the ports. If
+ successful, NP will be locked. */
+static error_t
+new_node (file_t file, mach_port_t idport, int locked, int openmodes,
+ struct node **np)
+{
+ error_t err;
+ struct netnode *nn;
+ *np = netfs_make_node_alloc (sizeof *nn);
+ if (*np == 0)
+ {
+ mach_port_deallocate (mach_task_self (), file);
+ if (idport != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), idport);
+ if (locked)
+ pthread_mutex_unlock (&idport_ihash_lock);
+ return ENOMEM;
+ }
+ nn = netfs_node_netnode (*np);
+ nn->file = file;
+ nn->openmodes = openmodes;
+ if (idport != MACH_PORT_NULL)
+ nn->idport = idport;
+ else
+ {
+ ino_t fileno;
+ mach_port_t fsidport;
+ assert (!locked);
+ err = io_identity (file, &nn->idport, &fsidport, &fileno);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), file);
+ free (*np);
+ return err;
+ }
+ }
+
+ if (!locked)
+ pthread_mutex_lock (&idport_ihash_lock);
+ err = hurd_ihash_add (&idport_ihash, nn->idport, *np);
+ if (err)
+ goto lose;
+
+ pthread_mutex_lock (&(*np)->lock);
+ pthread_mutex_unlock (&idport_ihash_lock);
+ return 0;
+
+ lose:
+ pthread_mutex_unlock (&idport_ihash_lock);
+ mach_port_deallocate (mach_task_self (), nn->idport);
+ mach_port_deallocate (mach_task_self (), file);
+ free (*np);
+ return err;
+}
+
+static void
+set_default_attributes (struct node *np)
+{
+ netfs_node_netnode (np)->faked = FAKE_UID | FAKE_GID | FAKE_DEFAULT;
+ np->nn_stat.st_uid = 0;
+ np->nn_stat.st_gid = 0;
+}
+
+static void
+set_faked_attribute (struct node *np, unsigned int faked)
+{
+ netfs_node_netnode (np)->faked |= faked;
+
+ if (netfs_node_netnode (np)->faked & FAKE_DEFAULT)
+ {
+ /* Now that the node has non-default faked attributes, they have to be
+ retained for future accesses. Account for the hash table reference.
+
+ XXX This means such nodes are currently leaked. Hopefully, there
+ won't be too many of them until the translator is shut down, and
+ the data structures should make implementing garbage collection
+ easy enough if it's ever needed, although scalability could be
+ improved. */
+ netfs_nref (np);
+ netfs_node_netnode (np)->faked &= ~FAKE_DEFAULT;
+ }
+}
+
+/* Node NP has no more references; free all its associated storage. */
+void
+netfs_node_norefs (struct node *np)
+{
+ pthread_mutex_unlock (&np->lock);
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+
+ pthread_mutex_lock (&idport_ihash_lock);
+ hurd_ihash_locp_remove (&idport_ihash, netfs_node_netnode (np)->idport_locp);
+ pthread_mutex_unlock (&idport_ihash_lock);
+
+ mach_port_deallocate (mach_task_self (), netfs_node_netnode (np)->file);
+ mach_port_deallocate (mach_task_self (), netfs_node_netnode (np)->idport);
+ free (np);
+
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+}
+
+/* This is the cleanup function we install in netfs_protid_class. If
+ the associated nodes reference count would also drop to zero, and
+ the node has no faked attributes, we destroy it as well. */
+static void
+fakeroot_netfs_release_protid (void *cookie)
+{
+ netfs_release_protid (cookie);
+
+ int cports = ports_count_class (netfs_control_class);
+ int nports = ports_count_class (netfs_protid_class);
+ ports_enable_class (netfs_control_class);
+ ports_enable_class (netfs_protid_class);
+ if (cports == 0 && nports == 0)
+ {
+ /* The last client is gone. Our job is done. */
+ error_t err = netfs_shutdown (0);
+ if (! err)
+ exit (EXIT_SUCCESS);
+
+ /* If netfs_shutdown returns EBUSY, we lost a race against
+ fsys_goaway. Hence we ignore this error. */
+ if (err != EBUSY)
+ error (1, err, "netfs_shutdown");
+ }
+}
+
+/* Given an existing node, make sure it has NEWMODES in its openmodes.
+ If not null, FILE is a port with those openmodes. */
+static error_t
+check_openmodes (struct netnode *nn, int newmodes, file_t file)
+{
+ error_t err = 0;
+
+ if (newmodes &~ nn->openmodes)
+ {
+ /* The user wants openmodes we haven't tried before. */
+
+ if (file != MACH_PORT_NULL && (nn->openmodes & ~newmodes))
+ {
+ /* Intersecting sets.
+ We need yet another new peropen on this node. */
+ mach_port_deallocate (mach_task_self (), file);
+ file = MACH_PORT_NULL;
+ }
+ if (file == MACH_PORT_NULL)
+ {
+ enum retry_type bad_retry;
+ char bad_retryname[1024]; /* XXX */
+ err = dir_lookup (nn->file, "", nn->openmodes | newmodes, 0,
+ &bad_retry, bad_retryname, &file);
+ if (!err && (bad_retry != FS_RETRY_NORMAL
+ || bad_retryname[0] != '\0'))
+ {
+ mach_port_deallocate (mach_task_self (), file);
+ err = EGRATUITOUS;
+ }
+ }
+ if (! err)
+ {
+ /* The new port has more openmodes than
+ the old one. We can just use it now. */
+ mach_port_deallocate (mach_task_self (), nn->file);
+ nn->file = file;
+ nn->openmodes = newmodes;
+ }
+ }
+ else if (file != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), file);
+
+ return err;
+}
+
+/* This is called by netfs_S_fsys_getroot. */
+error_t
+netfs_check_open_permissions (struct iouser *user, struct node *np,
+ int flags, int newnode)
+{
+ return check_openmodes (netfs_node_netnode (np),
+ flags & (O_RDWR|O_EXEC), MACH_PORT_NULL);
+}
+
+error_t
+netfs_S_dir_lookup (struct protid *diruser,
+ char *filename,
+ int flags,
+ mode_t mode,
+ retry_type *do_retry,
+ char *retry_name,
+ mach_port_t *retry_port,
+ mach_msg_type_name_t *retry_port_type)
+{
+ struct node *dnp, *np;
+ error_t err;
+ struct protid *newpi;
+ struct iouser *user;
+ mach_port_t file;
+ mach_port_t idport, fsidport;
+ ino_t fileno;
+
+ if (!diruser)
+ return EOPNOTSUPP;
+
+ dnp = diruser->po->np;
+
+ mach_port_t dir = netfs_node_netnode (dnp)->file;
+ redo_lookup:
+ err = dir_lookup (dir, filename,
+ flags & (O_NOLINK|O_RDWR|O_EXEC|O_CREAT|O_EXCL|O_NONBLOCK),
+ mode, do_retry, retry_name, &file);
+ if (dir != netfs_node_netnode (dnp)->file)
+ mach_port_deallocate (mach_task_self (), dir);
+ if (err)
+ return err;
+
+ switch (*do_retry)
+ {
+ case FS_RETRY_REAUTH:
+ {
+ mach_port_t ref = mach_reply_port ();
+ err = io_reauthenticate (file, ref, MACH_MSG_TYPE_MAKE_SEND);
+ if (! err)
+ {
+ mach_port_deallocate (mach_task_self (), file);
+ err = auth_user_authenticate (fakeroot_auth_port, ref,
+ MACH_MSG_TYPE_MAKE_SEND,
+ &dir);
+ }
+ mach_port_destroy (mach_task_self (), ref);
+ if (err)
+ return err;
+ }
+ filename = retry_name;
+ goto redo_lookup;
+
+ case FS_RETRY_NORMAL:
+ if (retry_name[0] != '\0')
+ {
+ dir = file;
+ filename = retry_name;
+ goto redo_lookup;
+ }
+ break;
+
+ case FS_RETRY_MAGICAL:
+ if (file == MACH_PORT_NULL)
+ {
+ *retry_port = MACH_PORT_NULL;
+ *retry_port_type = MACH_MSG_TYPE_COPY_SEND;
+ return 0;
+ }
+ /* Fallthrough. */
+
+ default:
+ /* Invalid response to our dir_lookup request. */
+ if (file != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), file);
+ *retry_port = MACH_PORT_NULL;
+ *retry_port_type = MACH_MSG_TYPE_COPY_SEND;
+ return EOPNOTSUPP;
+ }
+
+ /* We have a new port to an underlying node.
+ Find or make our corresponding virtual node. */
+
+ np = 0;
+ err = io_identity (file, &idport, &fsidport, &fileno);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), file);
+ return err;
+ }
+
+ mach_port_deallocate (mach_task_self (), fsidport);
+
+ redo_hash_lookup:
+ pthread_mutex_lock (&idport_ihash_lock);
+ pthread_mutex_lock (&dnp->lock);
+ /* The hashtable may not hold a true reference on the node. Acquire the
+ refcount lock so that, if a node is found, its reference counter cannot
+ drop to 0 before we get our own reference. */
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ np = hurd_ihash_find (&idport_ihash, idport);
+ if (np != NULL)
+ {
+ /* We already know about this node. */
+
+ if (np->references == 0)
+ {
+ /* But it might be in the process of being released. If so,
+ unlock the hash table to give the node a chance to actually
+ be removed and retry. */
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+ pthread_mutex_unlock (&dnp->lock);
+ pthread_mutex_unlock (&idport_ihash_lock);
+ goto redo_hash_lookup;
+ }
+
+ /* Otherwise, reference it right away. */
+ np->references++;
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+
+ mach_port_deallocate (mach_task_self (), idport);
+
+ if (np == dnp)
+ {
+ /* dnp is already locked. */
+ }
+ else
+ {
+ pthread_mutex_lock (&np->lock);
+ pthread_mutex_unlock (&dnp->lock);
+ }
+
+ err = check_openmodes (netfs_node_netnode (np),
+ (flags & (O_RDWR|O_EXEC)), file);
+ pthread_mutex_unlock (&idport_ihash_lock);
+ }
+ else
+ {
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+ err = new_node (file, idport, 1, flags, &np);
+ pthread_mutex_unlock (&dnp->lock);
+ if (!err)
+ {
+ set_default_attributes (np);
+ err = netfs_validate_stat (np, diruser->user);
+ }
+ }
+ if (err)
+ goto lose;
+
+ assert (retry_name[0] == '\0' && *do_retry == FS_RETRY_NORMAL);
+ flags &= ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS|O_NONBLOCK);
+
+ err = iohelp_dup_iouser (&user, diruser->user);
+ if (!err)
+ {
+ newpi = netfs_make_protid (netfs_make_peropen (np, flags, diruser->po),
+ user);
+ if (! newpi)
+ {
+ iohelp_free_iouser (user);
+ err = errno;
+ }
+ else
+ {
+ *retry_port = ports_get_right (newpi);
+ *retry_port_type = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newpi);
+ }
+ }
+
+ lose:
+ if (np != NULL)
+ netfs_nput (np);
+ return err;
+}
+
+/* These callbacks are used only by the standard netfs_S_dir_lookup,
+ which we do not use. But the shared library requires us to define them. */
+error_t
+netfs_attempt_lookup (struct iouser *user, struct node *dir,
+ char *name, struct node **np)
+{
+ assert (! "should not be here");
+ return EIEIO;
+}
+
+error_t
+netfs_attempt_create_file (struct iouser *user, struct node *dir,
+ char *name, mode_t mode, struct node **np)
+{
+ assert (! "should not be here");
+ return EIEIO;
+}
+
+/* Make sure that NP->nn_stat is filled with the most current information.
+ CRED identifies the user responsible for the operation. NP is locked. */
+error_t
+netfs_validate_stat (struct node *np, struct iouser *cred)
+{
+ struct stat st;
+ error_t err = io_stat (netfs_node_netnode (np)->file, &st);
+ if (err)
+ return err;
+
+ if (netfs_node_netnode (np)->faked & FAKE_UID)
+ st.st_uid = np->nn_stat.st_uid;
+ if (netfs_node_netnode (np)->faked & FAKE_GID)
+ st.st_gid = np->nn_stat.st_gid;
+ if (netfs_node_netnode (np)->faked & FAKE_AUTHOR)
+ st.st_author = np->nn_stat.st_author;
+ if (netfs_node_netnode (np)->faked & FAKE_MODE)
+ st.st_mode = np->nn_stat.st_mode;
+
+ np->nn_stat = st;
+ np->nn_translated = S_ISLNK (st.st_mode) ? S_IFLNK : 0;
+
+ return 0;
+}
+
+/* Various netfs functions will call fshelp_isowner to check whether
+ USER is allowed to do some operation. As fakeroot is not running
+ within the fakeauth'ed environment, USER contains the real
+ user. Hence, we override this check. */
+error_t
+fshelp_isowner (struct stat *st, struct iouser *user)
+{
+ return 0;
+}
+
+error_t
+netfs_attempt_chown (struct iouser *cred, struct node *np,
+ uid_t uid, uid_t gid)
+{
+ if (uid != ~0U)
+ {
+ set_faked_attribute (np, FAKE_UID);
+ np->nn_stat.st_uid = uid;
+ }
+ if (gid != ~0U)
+ {
+ set_faked_attribute (np, FAKE_GID);
+ np->nn_stat.st_gid = gid;
+ }
+ return 0;
+}
+
+error_t
+netfs_attempt_chauthor (struct iouser *cred, struct node *np, uid_t author)
+{
+ set_faked_attribute (np, FAKE_AUTHOR);
+ np->nn_stat.st_author = author;
+ return 0;
+}
+
+/* Return the mode that the real underlying file should have if the
+ fake mode is being set to MODE. We always give ourselves read and
+ write permission so that we can open the file as root would be able
+ to. We give ourselves execute permission iff any execute bit is
+ set in the fake mode. */
+static inline mode_t
+real_from_fake_mode (mode_t mode)
+{
+ return mode | S_IREAD | S_IWRITE | (((mode << 3) | (mode << 6)) & S_IEXEC);
+}
+
+/* This should attempt a chmod call for the user specified by CRED on
+ locked node NODE, to change the mode to MODE. Unlike the normal Unix
+ and Hurd meaning of chmod, this function is also used to attempt to
+ change files into other types. If such a transition is attempted which
+ is impossible, then return EOPNOTSUPP. */
+error_t
+netfs_attempt_chmod (struct iouser *cred, struct node *np, mode_t mode)
+{
+ if ((mode & S_IFMT) == 0)
+ mode |= np->nn_stat.st_mode & S_IFMT;
+ if ((mode & S_IFMT) != (np->nn_stat.st_mode & S_IFMT))
+ return EOPNOTSUPP;
+
+ /* We don't bother with error checking since the fake mode change should
+ always succeed--worst case a later open will get EACCES. */
+ (void) file_chmod (netfs_node_netnode (np)->file, mode);
+ set_faked_attribute (np, FAKE_MODE);
+ np->nn_stat.st_mode = mode;
+ return 0;
+}
+
+/* The user must define this function. Attempt to turn locked node NP
+ (user CRED) into a symlink with target NAME. */
+error_t
+netfs_attempt_mksymlink (struct iouser *cred, struct node *np, char *name)
+{
+ int namelen = strlen (name) + 1;
+ char trans[sizeof _HURD_SYMLINK + namelen];
+ memcpy (trans, _HURD_SYMLINK, sizeof _HURD_SYMLINK);
+ memcpy (&trans[sizeof _HURD_SYMLINK], name, namelen);
+ return file_set_translator (netfs_node_netnode (np)->file,
+ FS_TRANS_EXCL|FS_TRANS_SET,
+ FS_TRANS_EXCL|FS_TRANS_SET, 0,
+ trans, sizeof trans,
+ MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND);
+}
+
+error_t
+netfs_attempt_mkdev (struct iouser *cred, struct node *np,
+ mode_t type, dev_t indexes)
+{
+ char *trans = 0;
+ int translen = asprintf (&trans, "%s%c%d%c%d",
+ S_ISCHR (type) ? _HURD_CHRDEV : _HURD_BLKDEV,
+ '\0', major (indexes), '\0', minor (indexes));
+ if (trans == 0)
+ return ENOMEM;
+ else
+ {
+ error_t err = file_set_translator (netfs_node_netnode (np)->file,
+ FS_TRANS_EXCL|FS_TRANS_SET,
+ FS_TRANS_EXCL|FS_TRANS_SET, 0,
+ trans, translen + 1,
+ MACH_PORT_NULL,
+ MACH_MSG_TYPE_COPY_SEND);
+ free (trans);
+ return err;
+ }
+}
+
+error_t
+netfs_attempt_chflags (struct iouser *cred, struct node *np, int flags)
+{
+ return file_chflags (netfs_node_netnode (np)->file, flags);
+}
+
+error_t
+netfs_attempt_utimes (struct iouser *cred, struct node *np,
+ struct timespec *atime, struct timespec *mtime)
+{
+ union tv
+ {
+ struct timeval tv;
+ time_value_t tvt;
+ };
+ union tv a, m;
+ if (atime)
+ {
+ TIMESPEC_TO_TIMEVAL (&a.tv, atime);
+ }
+ else
+ a.tv.tv_sec = a.tv.tv_usec = -1;
+ if (mtime)
+ {
+ TIMESPEC_TO_TIMEVAL (&m.tv, mtime);
+ }
+ else
+ m.tv.tv_sec = m.tv.tv_usec = -1;
+
+ return file_utimes (netfs_node_netnode (np)->file, a.tvt, m.tvt);
+}
+
+error_t
+netfs_attempt_set_size (struct iouser *cred, struct node *np, off_t size)
+{
+ return file_set_size (netfs_node_netnode (np)->file, size);
+}
+
+error_t
+netfs_attempt_statfs (struct iouser *cred, struct node *np, struct statfs *st)
+{
+ return file_statfs (netfs_node_netnode (np)->file, st);
+}
+
+error_t
+netfs_attempt_sync (struct iouser *cred, struct node *np, int wait)
+{
+ return file_sync (netfs_node_netnode (np)->file, wait, 0);
+}
+
+error_t
+netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+ return 0;
+}
+
+error_t
+netfs_attempt_mkdir (struct iouser *user, struct node *dir,
+ char *name, mode_t mode)
+{
+ return dir_mkdir (netfs_node_netnode (dir)->file, name, mode | S_IRWXU);
+}
+
+
+/* XXX
+ Removing a node should mark the netnode so that it is GC'd when
+ it has no hard refs.
+ */
+
+error_t
+netfs_attempt_unlink (struct iouser *user, struct node *dir, char *name)
+{
+ return dir_unlink (netfs_node_netnode (dir)->file, name);
+}
+
+error_t
+netfs_attempt_rename (struct iouser *user, struct node *fromdir,
+ char *fromname, struct node *todir,
+ char *toname, int excl)
+{
+ return dir_rename (netfs_node_netnode (fromdir)->file, fromname,
+ netfs_node_netnode (todir)->file, toname, excl);
+}
+
+error_t
+netfs_attempt_rmdir (struct iouser *user,
+ struct node *dir, char *name)
+{
+ return dir_rmdir (netfs_node_netnode (dir)->file, name);
+}
+
+error_t
+netfs_attempt_link (struct iouser *user, struct node *dir,
+ struct node *file, char *name, int excl)
+{
+ return dir_link (netfs_node_netnode (dir)->file, netfs_node_netnode (file)->file, name, excl);
+}
+
+error_t
+netfs_attempt_mkfile (struct iouser *user, struct node *dir,
+ mode_t mode, struct node **np)
+{
+ file_t newfile;
+ error_t err = dir_mkfile (netfs_node_netnode (dir)->file, O_RDWR|O_EXEC,
+ real_from_fake_mode (mode), &newfile);
+ pthread_mutex_unlock (&dir->lock);
+ if (err == 0)
+ err = new_node (newfile, MACH_PORT_NULL, 0, O_RDWR|O_EXEC, np);
+ if (err == 0)
+ pthread_mutex_unlock (&(*np)->lock);
+ return err;
+}
+
+error_t
+netfs_attempt_readlink (struct iouser *user, struct node *np, char *buf)
+{
+ char transbuf[sizeof _HURD_SYMLINK + np->nn_stat.st_size + 1];
+ char *trans = transbuf;
+ size_t translen = sizeof transbuf;
+ error_t err = file_get_translator (netfs_node_netnode (np)->file,
+ &trans, &translen);
+ if (err == 0)
+ {
+ if (translen < sizeof _HURD_SYMLINK
+ || memcmp (trans, _HURD_SYMLINK, sizeof _HURD_SYMLINK) != 0)
+ err = EINVAL;
+ else
+ {
+ assert (translen <= sizeof _HURD_SYMLINK + np->nn_stat.st_size + 1);
+ memcpy (buf, &trans[sizeof _HURD_SYMLINK],
+ translen - sizeof _HURD_SYMLINK);
+ }
+ if (trans != transbuf)
+ munmap (trans, translen);
+ }
+ return err;
+}
+
+error_t
+netfs_attempt_read (struct iouser *cred, struct node *np,
+ off_t offset, size_t *len, void *data)
+{
+ char *buf = data;
+ error_t err = io_read (netfs_node_netnode (np)->file,
+ &buf, len, offset, *len);
+ if (err == 0 && buf != data)
+ {
+ memcpy (data, buf, *len);
+ munmap (buf, *len);
+ }
+ return err;
+}
+
+error_t
+netfs_attempt_write (struct iouser *cred, struct node *np,
+ off_t offset, size_t *len, void *data)
+{
+ return io_write (netfs_node_netnode (np)->file, data, *len, offset, len);
+}
+
+error_t
+netfs_report_access (struct iouser *cred, struct node *np, int *types)
+{
+ *types = O_RDWR|O_EXEC;
+ return 0;
+}
+
+error_t
+netfs_get_dirents (struct iouser *cred, struct node *dir,
+ int entry, int nentries, char **data,
+ mach_msg_type_number_t *datacnt,
+ vm_size_t bufsize, int *amt)
+{
+ return dir_readdir (netfs_node_netnode (dir)->file, data, datacnt,
+ entry, nentries, bufsize, amt);
+}
+
+error_t
+netfs_file_get_storage_info (struct iouser *cred,
+ struct node *np,
+ mach_port_t **ports,
+ mach_msg_type_name_t *ports_type,
+ mach_msg_type_number_t *num_ports,
+ int **ints,
+ mach_msg_type_number_t *num_ints,
+ off_t **offsets,
+ mach_msg_type_number_t *num_offsets,
+ char **data,
+ mach_msg_type_number_t *data_len)
+{
+ *ports_type = MACH_MSG_TYPE_MOVE_SEND;
+ return file_get_storage_info (netfs_node_netnode (np)->file,
+ ports, num_ports,
+ ints, num_ints,
+ offsets, num_offsets,
+ data, data_len);
+}
+
+kern_return_t
+netfs_S_file_exec (struct protid *user,
+ task_t task,
+ int flags,
+ char *argv,
+ size_t argvlen,
+ char *envp,
+ size_t envplen,
+ mach_port_t *fds,
+ size_t fdslen,
+ mach_port_t *portarray,
+ size_t portarraylen,
+ int *intarray,
+ size_t intarraylen,
+ mach_port_t *deallocnames,
+ size_t deallocnameslen,
+ mach_port_t *destroynames,
+ size_t destroynameslen)
+{
+ error_t err;
+ file_t file;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = check_openmodes (netfs_node_netnode (user->po->np),
+ O_EXEC, MACH_PORT_NULL);
+ file = netfs_node_netnode (user->po->np)->file;
+ if (!err)
+ err = mach_port_mod_refs (mach_task_self (),
+ file, MACH_PORT_RIGHT_SEND, 1);
+ pthread_mutex_unlock (&user->po->np->lock);
+
+ if (!err)
+ {
+ /* We cannot use MACH_MSG_TYPE_MOVE_SEND because we might need to
+ retry an interrupted call that would have consumed the rights. */
+ err = file_exec (netfs_node_netnode (user->po->np)->file,
+ task, flags, argv, argvlen,
+ envp, envplen, fds, MACH_MSG_TYPE_COPY_SEND, fdslen,
+ portarray, MACH_MSG_TYPE_COPY_SEND, portarraylen,
+ intarray, intarraylen, deallocnames, deallocnameslen,
+ destroynames, destroynameslen);
+ mach_port_deallocate (mach_task_self (), file);
+ }
+
+ if (err == 0)
+ {
+ size_t i;
+ mach_port_deallocate (mach_task_self (), task);
+ for (i = 0; i < fdslen; ++i)
+ mach_port_deallocate (mach_task_self (), fds[i]);
+ for (i = 0; i < portarraylen; ++i)
+ mach_port_deallocate (mach_task_self (), portarray[i]);
+ }
+ return err;
+}
+
+error_t
+netfs_S_io_map (struct protid *user,
+ mach_port_t *rdobj, mach_msg_type_name_t *rdobjtype,
+ mach_port_t *wrobj, mach_msg_type_name_t *wrobjtype)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+ *rdobjtype = *wrobjtype = MACH_MSG_TYPE_MOVE_SEND;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = io_map (netfs_node_netnode (user->po->np)->file, rdobj, wrobj);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
+
+error_t
+netfs_S_io_map_cntl (struct protid *user,
+ mach_port_t *obj,
+ mach_msg_type_name_t *objtype)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+ *objtype = MACH_MSG_TYPE_MOVE_SEND;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = io_map_cntl (netfs_node_netnode (user->po->np)->file, obj);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
+
+error_t
+netfs_S_io_identity (struct protid *user,
+ mach_port_t *id,
+ mach_msg_type_name_t *idtype,
+ mach_port_t *fsys,
+ mach_msg_type_name_t *fsystype,
+ ino_t *fileno)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ *idtype = *fsystype = MACH_MSG_TYPE_MOVE_SEND;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = io_identity (netfs_node_netnode (user->po->np)->file,
+ id, fsys, fileno);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
+
+#define NETFS_S_SIMPLE(name) \
+error_t \
+netfs_S_##name (struct protid *user) \
+{ \
+ error_t err; \
+ \
+ if (!user) \
+ return EOPNOTSUPP; \
+ \
+ pthread_mutex_lock (&user->po->np->lock); \
+ err = name (netfs_node_netnode (user->po->np)->file); \
+ pthread_mutex_unlock (&user->po->np->lock); \
+ return err; \
+}
+
+NETFS_S_SIMPLE (io_get_conch)
+NETFS_S_SIMPLE (io_release_conch)
+NETFS_S_SIMPLE (io_eofnotify)
+NETFS_S_SIMPLE (io_readnotify)
+NETFS_S_SIMPLE (io_readsleep)
+NETFS_S_SIMPLE (io_sigio)
+
+error_t
+netfs_S_io_prenotify (struct protid *user,
+ vm_offset_t start, vm_offset_t stop)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = io_prenotify (netfs_node_netnode (user->po->np)->file, start, stop);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
+
+error_t
+netfs_S_io_postnotify (struct protid *user,
+ vm_offset_t start, vm_offset_t stop)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = io_postnotify (netfs_node_netnode (user->po->np)->file, start, stop);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
+
+/* This overrides the library's definition. */
+int
+netfs_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ mig_routine_t routine;
+ if ((routine = netfs_io_server_routine (inp)) ||
+ (routine = netfs_fs_server_routine (inp)) ||
+ (routine = ports_notify_server_routine (inp)) ||
+ (routine = netfs_fsys_server_routine (inp)) ||
+ /* XXX we should intercept interrupt_operation and do
+ the ports_S_interrupt_operation work as well as
+ sending an interrupt_operation to the underlying file.
+ */
+ (routine = ports_interrupt_server_routine (inp)))
+ {
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+ else
+ {
+ /* We didn't recognize the message ID, so pass the message through
+ unchanged to the underlying file. */
+ struct protid *cred = ports_lookup_port (netfs_port_bucket,
+ inp->msgh_local_port,
+ netfs_protid_class);
+ if (cred == 0)
+ /* This must be an unknown message on our fsys control port. */
+ return 0;
+ else
+ {
+ error_t err;
+ assert (MACH_MSGH_BITS_LOCAL (inp->msgh_bits)
+ == MACH_MSG_TYPE_MOVE_SEND);
+ inp->msgh_bits = (inp->msgh_bits & MACH_MSGH_BITS_COMPLEX)
+ | MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND,
+ MACH_MSGH_BITS_REMOTE (inp->msgh_bits));
+ inp->msgh_local_port = inp->msgh_remote_port; /* reply port */
+ inp->msgh_remote_port = netfs_node_netnode (cred->po->np)->file;
+ err = mach_msg (inp, MACH_SEND_MSG, inp->msgh_size, 0,
+ MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ assert_perror (err); /* XXX should synthesize reply */
+ ports_port_deref (cred);
+ /* We already sent the message, so the server loop shouldn't do it again. */
+ ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
+ return 1;
+ }
+ }
+}
+
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+
+ struct argp argp = { .doc = "\
+A translator for faking privileged access to an underlying filesystem.\v\
+This translator appears to give transparent access to the underlying \
+directory node. However, all accesses are made using the credentials \
+of the translator regardless of the client and the translator fakes \
+success for chown and chmod operations that only root could actually do, \
+reporting the faked IDs and modes in later stat calls, and allows \
+any user to open nodes regardless of permissions as is done for root." };
+
+ /* Parse our command line arguments (all none of them). */
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+ fakeroot_auth_port = getauth ();
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ netfs_init ();
+
+ /* Install our own clean routine. */
+ netfs_protid_class->clean_routine = fakeroot_netfs_release_protid;
+
+ /* Get our underlying node (we presume it's a directory) and use
+ that to make the root node of the filesystem. */
+ err = new_node (netfs_startup (bootstrap, O_READ), MACH_PORT_NULL, 0, O_READ,
+ &netfs_root_node);
+ if (err)
+ error (5, err, "Cannot create root node");
+
+ err = netfs_validate_stat (netfs_root_node, 0);
+ if (err)
+ error (6, err, "Cannot stat underlying node");
+
+ netfs_root_node->nn_stat.st_mode &= ~(S_IPTRANS | S_IATRANS);
+ netfs_root_node->nn_stat.st_mode |= S_IROOT;
+ set_faked_attribute (netfs_root_node, FAKE_MODE);
+ pthread_mutex_unlock (&netfs_root_node->lock);
+
+ netfs_server_loop (); /* Never returns. */
+
+ /*NOTREACHED*/
+ return 0;
+}
diff --git a/trans/fifo.c b/trans/fifo.c
new file mode 100644
index 00000000..a9ad2dd2
--- /dev/null
+++ b/trans/fifo.c
@@ -0,0 +1,628 @@
+/* A translator for fifos
+
+ Copyright (C) 1995,96,97,98,2001,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <error.h>
+#include <string.h>
+#include <fcntl.h>
+#include <argp.h>
+
+#include <pthread.h>
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <hurd/fsys.h>
+#include <hurd/pipe.h>
+
+#include <version.h>
+
+#include "libtrivfs/trivfs_fs_S.h"
+#include "libtrivfs/trivfs_io_S.h"
+
+/* Global options. These defaults are the standard ones, I think... */
+int wait_for_reader = 1, wait_for_writer = 1;
+int one_reader = 1;
+
+/* What kinds of pipes we use. */
+struct pipe_class *fifo_pipe_class;
+
+/* The current fifo that new opens will see, or NULL if there is none. */
+struct pipe *active_fifo = NULL;
+
+/* Lock this when changing ACTIVE_FIFO. */
+pthread_mutex_t active_fifo_lock;
+/* Signal this when ACTIVE_FIFO may have changed. */
+pthread_cond_t active_fifo_changed;
+
+const char *argp_program_version = STANDARD_HURD_VERSION (fifo);
+
+static struct argp_option options[] =
+{
+ { "multiple-readers", 'm', 0, 0, "Allow multiple simultaneous readers" },
+ { "noblock", 'n', 0, 0, "Don't block on open" },
+ { "dgram", 'd', 0, 0, "Reads reflect write record boundaries" },
+ { 0 }
+};
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'm': one_reader = 0; break;
+ case 'n': wait_for_reader = wait_for_writer = 0; break;
+ case 'd': fifo_pipe_class = seqpack_pipe_class; break;
+ default: return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static const struct argp argp = {
+ options, parse_opt, 0, "Translator for fifos."
+};
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+
+ fifo_pipe_class = stream_pipe_class;
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "must be started as a translator");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (3, err, "Contacting parent");
+
+ /* Launch. */
+ do
+ {
+ ports_enable_class (fsys->protid_class);
+ ports_manage_port_operations_multithread (fsys->pi.bucket,
+ trivfs_demuxer,
+ 30*1000, 5*60*1000, 0);
+ }
+ while (ports_count_class (fsys->protid_class) > 0);
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+static error_t
+open_hook (struct trivfs_peropen *po)
+{
+ error_t err = 0;
+ int flags = po->openmodes;
+
+ if (flags & (O_READ | O_WRITE))
+ {
+ pthread_mutex_lock (&active_fifo_lock);
+
+/* Wait until the active fifo has changed so that CONDITION is true. */
+#define WAIT(condition, noblock_err) \
+ while (!err && !(condition)) \
+ { \
+ if (flags & O_NONBLOCK) \
+ { \
+ err = noblock_err; \
+ break; \
+ } \
+ else if (pthread_hurd_cond_wait_np (&active_fifo_changed, \
+ &active_fifo_lock)) \
+ err = EINTR; \
+ }
+
+ if (flags & O_READ)
+ /* When opening for read, what we do depends on what mode this server
+ is running in. The default (if ONE_READER is set) is to only
+ allow one reader at a time, with additional opens for read
+ blocking here until the old reader goes away; otherwise, we allow
+ multiple readers. If WAIT_FOR_WRITER is true, then once we've
+ created a fifo, we also block until someone opens it for writing;
+ otherwise, the first read will block until someone writes
+ something. */
+ {
+ if (one_reader)
+ /* Wait until there isn't any active fifo, so we can make one. */
+ WAIT (!active_fifo || !active_fifo->readers, EWOULDBLOCK);
+
+ if (!err && active_fifo == NULL)
+ /* No other readers, and indeed, no fifo; make one. */
+ {
+ err = pipe_create (fifo_pipe_class, &active_fifo);
+ if (! err)
+ active_fifo->flags &= ~PIPE_BROKEN; /* Avoid immediate EOF. */
+ }
+ if (!err)
+ {
+ pipe_add_reader (active_fifo);
+ pthread_cond_broadcast (&active_fifo_changed);
+ /* We'll unlock ACTIVE_FIFO_LOCK below; the writer code won't
+ make us block because we've ensured that there's a reader
+ for it. */
+
+ if (wait_for_writer)
+ /* Wait until there's a writer. */
+ {
+ WAIT (active_fifo->writers, 0);
+ if (err)
+ /* Back out the new pipe creation. */
+ {
+ pipe_remove_reader (active_fifo);
+ active_fifo = NULL;
+ pthread_cond_broadcast (&active_fifo_changed);
+ }
+ }
+ }
+ }
+
+ if (!err && (flags & O_WRITE))
+ /* Open the active_fifo for writing. If WAIT_FOR_READER is true,
+ then we block until there's someone to read what we wrote,
+ otherwise, if there's no fifo, we create one, which we just write
+ into and leave it for someone to read later. */
+ {
+ if (wait_for_reader)
+ /* Wait until there's a fifo to write to. */
+ WAIT (active_fifo && active_fifo->readers, ENXIO);
+ if (!err && active_fifo == NULL)
+ /* No other readers, and indeed, no fifo; make one. */
+ {
+ err = pipe_create (fifo_pipe_class, &active_fifo);
+ if (!err)
+ active_fifo->flags &= ~PIPE_BROKEN;
+ }
+ if (!err)
+ {
+ pipe_add_writer (active_fifo);
+ pthread_cond_broadcast (&active_fifo_changed);
+ }
+ }
+
+ po->hook = active_fifo;
+
+ pthread_mutex_unlock (&active_fifo_lock);
+ }
+
+ return err;
+}
+
+static void
+close_hook (struct trivfs_peropen *po)
+{
+ int was_active, detach = 0;
+ int flags = po->openmodes;
+ struct pipe *pipe = po->hook;
+
+ if (!pipe)
+ return;
+
+ pthread_mutex_lock (&active_fifo_lock);
+ was_active = (active_fifo == pipe);
+
+ if (was_active)
+ /* See if PIPE should cease to be the user-visible face of this fifo. */
+ detach =
+ ((flags & O_READ) && pipe->readers == 1)
+ || ((flags & O_WRITE) && pipe->writers == 1);
+ else
+ /* Let others have their fun. */
+ pthread_mutex_unlock (&active_fifo_lock);
+
+ if (flags & O_READ)
+ pipe_remove_reader (pipe);
+ if (flags & O_WRITE)
+ pipe_remove_writer (pipe);
+ /* At this point, PIPE may be gone, so we can't look at it again. */
+
+ if (was_active)
+ {
+ if (detach)
+ active_fifo = NULL;
+ pthread_cond_broadcast (&active_fifo_changed);
+ pthread_mutex_unlock (&active_fifo_lock);
+ }
+}
+
+/* Trivfs hooks */
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 1;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = O_READ | O_WRITE;
+
+error_t (*trivfs_peropen_create_hook) (struct trivfs_peropen *) = open_hook;
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ struct pipe *pipe = cred->po->hook;
+
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= S_IFIFO;
+
+ if (pipe)
+ {
+ pthread_mutex_lock (&pipe->lock);
+ st->st_size = pipe_readable (pipe, 1);
+ st->st_blocks = st->st_size >> 9;
+ pthread_mutex_unlock (&pipe->lock);
+ }
+ else
+ st->st_size = st->st_blocks = 0;
+
+ /* As we try to be clever with large transfers, ask for them. */
+ st->st_blksize = vm_page_size * 16;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+ error_t err;
+ int force = (flags & FSYS_GOAWAY_FORCE);
+ struct port_bucket *bucket = ((struct port_info *)cntl)->bucket;
+
+ err = ports_inhibit_bucket_rpcs (bucket);
+ if (err == EINTR || (err && !force))
+ return err;
+
+ if (ports_count_class (cntl->protid_class) > 0 && !force)
+ /* Still some opens, and we're not being forced to go away, so don't. */
+ {
+ ports_enable_class (cntl->protid_class);
+ ports_resume_bucket_rpcs (bucket);
+ return EBUSY;
+ }
+
+ exit (0);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Return objects mapping the data underlying this memory object. If
+ the object can be read then memobjrd will be provided; if the
+ object can be written then memobjwr will be provided. For objects
+ where read data and write data are the same, these objects will be
+ equal, otherwise they will be disjoint. Servers are permitted to
+ implement io_map but not io_map_cntl. Some objects do not provide
+ mapping; they will set none of the ports and return an error. Such
+ objects can still be accessed by io_read and io_write. */
+error_t
+trivfs_S_io_map (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ memory_object_t *rdobj,
+ mach_msg_type_name_t *rdtype,
+ memory_object_t *wrobj,
+ mach_msg_type_name_t *wrtype)
+{
+ return EOPNOTSUPP;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Read data from an IO object. If offset if -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMT. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, size_t *data_len,
+ off_t offs, size_t amount)
+{
+ error_t err;
+
+ if (!cred)
+ err = EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_READ))
+ err = EBADF;
+ else
+ {
+ struct pipe *pipe = cred->po->hook;
+ pthread_mutex_lock (&pipe->lock);
+ err = pipe_read (pipe, cred->po->openmodes & O_NONBLOCK, NULL,
+ data, data_len, amount);
+ pthread_mutex_unlock (&pipe->lock);
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+error_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ size_t *amount)
+{
+ error_t err;
+
+ if (!cred)
+ err = EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_READ))
+ err = EBADF;
+ else
+ {
+ struct pipe *pipe = cred->po->hook;
+ pthread_mutex_lock (&pipe->lock);
+ *amount = pipe_readable (pipe, 1);
+ pthread_mutex_unlock (&pipe->lock);
+ err = 0;
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offset, int whence, off_t *new_offset)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return ESPIPE;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. ID_TAG is returned as passed; it
+ is just for the convenience of the user in matching up reply messages with
+ specific requests sent. */
+static error_t
+io_select_common (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec *tsp, int *select_type)
+{
+ struct pipe *pipe;
+ error_t err = 0;
+ int ready = 0;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pipe = cred->po->hook;
+
+ if (*select_type & SELECT_READ)
+ {
+ if (cred->po->openmodes & O_READ)
+ {
+ pthread_mutex_lock (&pipe->lock);
+ err = pipe_wait_readable (pipe, 1, 1);
+ if (err == EWOULDBLOCK)
+ err = 0; /* Not readable, actually not an error. */
+ else
+ ready |= SELECT_READ; /* Data immediately readable (or error). */
+ pthread_mutex_unlock (&pipe->lock);
+ }
+ else
+ {
+ err = EBADF;
+ ready |= SELECT_READ; /* Error immediately available... */
+ }
+ if (err)
+ /* Prevent write test from overwriting err. */
+ *select_type &= ~SELECT_WRITE;
+ }
+
+ if (*select_type & SELECT_WRITE)
+ {
+ if (cred->po->openmodes & O_WRITE)
+ {
+ pthread_mutex_lock (&pipe->lock);
+ err = pipe_wait_writable (pipe, 1);
+ if (err == EWOULDBLOCK)
+ err = 0; /* Not writable, actually not an error. */
+ else
+ ready |= SELECT_WRITE; /* Data immediately writable (or error). */
+ pthread_mutex_unlock (&pipe->lock);
+ }
+ else
+ {
+ err = EBADF;
+ ready |= SELECT_WRITE; /* Error immediately available... */
+ }
+ }
+
+ if (ready)
+ *select_type = ready;
+ else
+ /* Wait for something to change. */
+ {
+ ports_interrupt_self_on_port_death (cred, reply);
+ err = pipe_pair_select (pipe, pipe, tsp, select_type, 1);
+ }
+
+ return err;
+}
+
+error_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *select_type)
+{
+ return io_select_common (cred, reply, reply_type, NULL, select_type);
+}
+
+error_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec ts,
+ int *select_type)
+{
+ return io_select_common (cred, reply, reply_type, &ts, select_type);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Write data to an IO object. If offset is -1, write at the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount successfully written is returned in amount. A
+ given user should not have more than one outstanding io_write on an
+ object at a time; servers implement congestion control by delaying
+ responses to io_write. Servers may drop data (returning ENOBUFS)
+ if they recevie more than one write when not prepared for it. */
+error_t
+trivfs_S_io_write (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *data, size_t data_len,
+ off_t offs, size_t *amount)
+{
+ error_t err;
+
+ if (!cred)
+ err = EOPNOTSUPP;
+ else
+ {
+ int flags = cred->po->openmodes;
+ struct pipe *pipe = cred->po->hook;
+
+ if (!(flags & O_WRITE))
+ err = EBADF;
+ else
+ {
+ pthread_mutex_lock (&pipe->lock);
+ err = pipe_write (pipe, flags & O_NONBLOCK, NULL,
+ data, data_len, amount);
+ pthread_mutex_unlock (&pipe->lock);
+ }
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+error_t
+trivfs_S_file_set_size (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t size)
+{
+ return size == 0 ? 0 : EINVAL;
+}
+
+/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and
+ O_NONBLOCK bits for the IO object. In addition, io_get_openmodes
+ will tell you which of O_READ, O_WRITE, and O_EXEC the object can
+ be used for. The O_ASYNC bit affects icky async I/O; good async
+ I/O is done through io_async which is orthogonal to these calls. */
+
+error_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+ *bits = cred->po->openmodes;
+ return 0;
+ }
+}
+
+error_t
+trivfs_S_io_set_all_openmodes(struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int mode)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+/* Get/set the owner of the IO object. For terminals, this affects
+ controlling terminal behavior (see term_become_ctty). For all
+ objects this affects old-style async IO. Negative values represent
+ pgrps. This has nothing to do with the owner of a file (as
+ returned by io_stat, and as used for various permission checks by
+ filesystems). An owner of 0 indicates that there is no owner. */
+
+error_t
+trivfs_S_io_get_owner (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ pid_t *owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ *owner = 0;
+ return 0;
+}
+
+error_t
+trivfs_S_io_mod_owner (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ pid_t owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return EINVAL;
+}
diff --git a/trans/firmlink.c b/trans/firmlink.c
new file mode 100644
index 00000000..69d4aaed
--- /dev/null
+++ b/trans/firmlink.c
@@ -0,0 +1,288 @@
+/* A translator for `firmlinks'
+
+ Copyright (C) 1997,98,99,2001,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <argp.h>
+#include <error.h>
+#include <sys/mman.h>
+
+#include <hurd/trivfs.h>
+
+#include <version.h>
+
+#include "libtrivfs/trivfs_io_S.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (firmlink);
+
+static const struct argp_option options[] =
+{
+ { 0 }
+};
+
+static const char args_doc[] = "TARGET";
+static const char doc[] = "A translator for firmlinks."
+"\vA firmlink is sort of half-way between a symbolic link and a hard link:"
+"\n"
+"\nLike a symbolic link, it is `by name', and contains no actual reference to"
+" the target. However, the lookup returns a node which will redirect parent"
+" lookups so that attempts to find the cwd that go through the link will"
+" reflect the link name, not the target name. The target referenced by the"
+" firmlink is looked up in the namespace of the translator, not the client.";
+
+/* Link parameters. */
+static char *target = 0; /* What we translate too. */
+
+/* Parse a single option/argument. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ if (key == ARGP_KEY_ARG && state->arg_num == 0)
+ target = arg;
+ else if (key == ARGP_KEY_ARG || key == ARGP_KEY_NO_ARGS)
+ argp_usage (state);
+ else
+ return ARGP_ERR_UNKNOWN;
+ return 0;
+}
+
+static struct argp argp = { options, parse_opt, args_doc, doc };
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+
+ /* Parse our options... */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "Must be started as a translator");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (2, err, "Contacting parent");
+
+ /* Launch. */
+ ports_manage_port_operations_multithread (fsys->pi.bucket, trivfs_demuxer,
+ 2 * 60 * 1000, 0, 0);
+
+ return 0;
+}
+
+/* Return in LINK the node that TARGET_NAME resolves to, with its parent
+ replaced by PARENT. FLAGS are the flags to open TARGET_NAME with. */
+static error_t
+firmlink (mach_port_t parent, const char *target_name, int flags,
+ mach_port_t *link)
+{
+ error_t err;
+ file_t authed_link;
+ file_t target = file_name_lookup (target_name, flags & ~O_CREAT, 0);
+
+ if (target == MACH_PORT_NULL)
+ return errno;
+
+ err = file_reparent (target, parent, &authed_link);
+ mach_port_deallocate (mach_task_self (), target);
+ mach_port_deallocate (mach_task_self (), parent);
+
+ if (! err)
+ {
+ err = io_restrict_auth (authed_link, link, 0, 0, 0, 0);
+ mach_port_deallocate (mach_task_self (), authed_link);
+ }
+
+ return err;
+}
+
+/* Trivfs hooks */
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = O_READ;
+
+/* Return the root node of our file system: A firmlink to TARGET, unless
+ TARGET doesn't exist, in which case we return a symlink-like node. */
+static error_t
+getroot (struct trivfs_control *cntl,
+ mach_port_t reply_port, mach_msg_type_name_t reply_port_type,
+ mach_port_t dotdot,
+ uid_t *uids, u_int nuids, uid_t *gids, u_int ngids,
+ int flags,
+ retry_type *do_retry, char *retry_name,
+ mach_port_t *node, mach_msg_type_name_t *node_type)
+{
+ error_t err = firmlink (dotdot, target, flags, node);
+
+ if (err == ENOENT)
+ /* No target? Act like a link. */
+ return EAGAIN;
+
+ if (! err)
+ {
+ *node_type = MACH_MSG_TYPE_MOVE_SEND;
+ *do_retry = FS_RETRY_REAUTH;
+ retry_name[0] = '\0';
+ }
+
+ return err;
+}
+
+/* Called by trivfs_S_fsys_getroot before any other processing takes place;
+ if the return value is EAGAIN, normal trivfs getroot processing continues,
+ otherwise the rpc returns with that return value. */
+error_t (*trivfs_getroot_hook) () = getroot;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ st->st_size = strlen (target);
+ st->st_blocks = 0;
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= S_IFLNK;
+}
+
+/* Shutdown the filesystem. */
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+ error_t err;
+ int force = (flags & FSYS_GOAWAY_FORCE);
+ struct port_bucket *bucket = ((struct port_info *)cntl)->bucket;
+
+ err = ports_inhibit_bucket_rpcs (bucket);
+ if (err == EINTR || (err && !force))
+ return err;
+
+ if (ports_count_class (cntl->protid_class) > 0 && !force)
+ /* Still some opens, and we're not being forced to go away, so don't. */
+ {
+ ports_enable_class (cntl->protid_class);
+ ports_resume_bucket_rpcs (bucket);
+ return EBUSY;
+ }
+
+ exit (0);
+}
+
+/* We store the file offset in po->hook (ick!) */
+
+/* Read data from an IO object. If offset if -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMT. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, mach_msg_type_number_t *data_len,
+ loff_t offs, mach_msg_type_number_t amount)
+{
+ error_t err = 0;
+
+ if (! cred)
+ err = EOPNOTSUPP;
+ else if (! (cred->po->openmodes & O_READ))
+ err = EBADF;
+ else
+ {
+ size_t max = strlen (target);
+ intptr_t start = offs >= 0 ? offs : (intptr_t)cred->po->hook;
+ if (start < 0)
+ return EINVAL;
+ if (start + amount > max)
+ amount = max - start;
+ if (amount > *data_len)
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ err = (*data == MAP_FAILED) ? errno : 0;
+ if (!err && amount > 0)
+ {
+ memcpy (*data, target + start, amount);
+ if (offs < 0)
+ cred->po->hook = (void *)(start + amount); /* Update PO offset. */
+ }
+ *data_len = amount;
+ }
+
+ return err;
+}
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+error_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mach_msg_type_number_t *amount)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else if (! (cred->po->openmodes & O_READ))
+ return EBADF;
+ else if ((intptr_t)cred->po->hook < 0)
+ return EINVAL;
+ else
+ *amount = strlen (target) - (intptr_t)cred->po->hook;
+ return 0;
+}
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offset, int whence, off_t *new_offset)
+{
+ return EOPNOTSUPP;
+}
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. ID_TAG is returned as passed; it
+ is just for the convenience of the user in matching up reply messages with
+ specific requests sent. */
+error_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec ts,
+ int *type)
+{
+ return trivfs_S_io_select (cred, reply, reply_type, type);
+}
diff --git a/trans/fwd.c b/trans/fwd.c
new file mode 100644
index 00000000..f30aad1a
--- /dev/null
+++ b/trans/fwd.c
@@ -0,0 +1,51 @@
+/* A translator to start up a central translation server
+
+ Note: most translators that use a central server will look for the server
+ and forward the request to the server if they find one, otherwise doing
+ the translation themselves.
+
+ Copyright (C) 1995, 1998, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <error.h>
+#include <stdio.h>
+#include <hurd/fshelp.h>
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+
+ if (argc < 2 || *argv[1] == '-')
+ {
+ fprintf (stderr, "Usage: %s SERVER [TRANS_NAME [TRANS_ARG...]]\n",
+ program_invocation_name);
+ return 1;
+ }
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (2, 0, "must be started as a translator");
+
+ err = fshelp_delegate_translation (argv[1], bootstrap, argv + 2);
+ if (err)
+ error (3, err, "%s", argv[1]);
+
+ return 0;
+}
diff --git a/trans/hello-mt.c b/trans/hello-mt.c
new file mode 100644
index 00000000..ba9329a7
--- /dev/null
+++ b/trans/hello-mt.c
@@ -0,0 +1,332 @@
+/* hello-mt.c - A trivial single-file translator, multithreaded version
+ Copyright (C) 1998,99,2001,02,2006 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#define _GNU_SOURCE 1
+
+#include <hurd/trivfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <pthread.h>
+
+#include <version.h>
+
+#include "libtrivfs/trivfs_io_S.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (hello-mt);
+
+/* The message we return when we are read. */
+static const char hello[] = "Hello, world!\n";
+static char *contents = (char *) hello;
+static size_t contents_len = sizeof hello - 1;
+
+/* This lock protects access to contents and contents_len. */
+static pthread_rwlock_t contents_lock;
+
+/* Trivfs hooks. */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+
+int trivfs_allow_open = O_READ;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+
+/* NOTE: This example is not robust: it is possible to trigger some
+ assertion failures because we don't implement the following:
+
+ $ cd /src/hurd/libtrivfs
+ $ grep -l 'assert.*!trivfs_support_read' *.c |
+ xargs grep '^trivfs_S_' | sed 's/^[^:]*:\([^ ]*\).*$/\1/'
+ trivfs_S_io_get_openmodes
+ trivfs_S_io_clear_some_openmodes
+ trivfs_S_io_set_some_openmodes
+ trivfs_S_io_set_all_openmodes
+ trivfs_S_io_readable
+ trivfs_S_io_select
+ $
+
+ For that reason, you should run this as an active translator
+ `settrans -ac testnode /path/to/thello' so that you can see the
+ error messages when they appear. */
+
+/* A hook for us to keep track of the file descriptor state. */
+struct open
+{
+ pthread_mutex_t lock;
+ off_t offs;
+};
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ /* Mark the node as a read-only plain file. */
+ st->st_mode &= ~(S_IFMT | ALLPERMS);
+ st->st_mode |= (S_IFREG | S_IRUSR | S_IRGRP | S_IROTH);
+ st->st_size = contents_len; /* No need to lock for reading one word. */
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+ exit (0);
+}
+
+
+static error_t
+open_hook (struct trivfs_peropen *peropen)
+{
+ struct open *op = malloc (sizeof (struct open));
+ if (op == NULL)
+ return ENOMEM;
+
+ /* Initialize the offset. */
+ op->offs = 0;
+ pthread_mutex_init (&op->lock, NULL);
+ peropen->hook = op;
+ return 0;
+}
+
+
+static void
+close_hook (struct trivfs_peropen *peropen)
+{
+ struct open *op = peropen->hook;
+
+ pthread_mutex_destroy (&op->lock);
+ free (op);
+}
+
+
+/* Read data from an IO object. If offset is -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMOUNT. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, mach_msg_type_number_t *data_len,
+ loff_t offs, mach_msg_type_number_t amount)
+{
+ struct open *op;
+
+ /* Deny access if they have bad credentials. */
+ if (! cred)
+ return EOPNOTSUPP;
+ else if (! (cred->po->openmodes & O_READ))
+ return EBADF;
+
+ op = cred->po->hook;
+
+ pthread_mutex_lock (&op->lock);
+
+ /* Get the offset. */
+ if (offs == -1)
+ offs = op->offs;
+
+ pthread_rwlock_rdlock (&contents_lock);
+
+ /* Prune the amount they want to read. */
+ if (offs > contents_len)
+ offs = contents_len;
+ if (offs + amount > contents_len)
+ amount = contents_len - offs;
+
+ if (amount > 0)
+ {
+ /* Possibly allocate a new buffer. */
+ if (*data_len < amount)
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ {
+ pthread_mutex_unlock (&op->lock);
+ pthread_rwlock_unlock (&contents_lock);
+ return ENOMEM;
+ }
+
+ /* Copy the constant data into the buffer. */
+ memcpy ((char *) *data, contents + offs, amount);
+
+ /* Update the saved offset. */
+ op->offs += amount;
+ }
+
+ pthread_mutex_unlock (&op->lock);
+
+ pthread_rwlock_unlock (&contents_lock);
+
+ *data_len = amount;
+ return 0;
+}
+
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offs, int whence, off_t *new_offs)
+{
+ struct open *op;
+ error_t err = 0;
+ if (! cred)
+ return EOPNOTSUPP;
+
+ op = cred->po->hook;
+
+ pthread_mutex_lock (&op->lock);
+
+ switch (whence)
+ {
+ case SEEK_CUR:
+ offs += op->offs;
+ goto check;
+ case SEEK_END:
+ offs += contents_len;
+ case SEEK_SET:
+ check:
+ if (offs >= 0)
+ {
+ *new_offs = op->offs = offs;
+ break;
+ }
+ default:
+ err = EINVAL;
+ }
+
+ pthread_mutex_unlock (&op->lock);
+
+ return err;
+}
+
+
+/* If this variable is set, it is called every time a new peropen
+ structure is created and initialized. */
+error_t (*trivfs_peropen_create_hook)(struct trivfs_peropen *) = open_hook;
+
+/* If this variable is set, it is called every time a peropen structure
+ is about to be destroyed. */
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
+
+
+/* Options processing. We accept the same options on the command line
+ and from fsys_set_options. */
+
+static const struct argp_option options[] =
+{
+ {"contents", 'c', "STRING", 0, "Specify the contents of the virtual file"},
+ {0}
+};
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ switch (opt)
+ {
+ default:
+ return ARGP_ERR_UNKNOWN;
+ case ARGP_KEY_INIT:
+ case ARGP_KEY_SUCCESS:
+ case ARGP_KEY_ERROR:
+ break;
+
+ case 'c':
+ {
+ char *new = strdup (arg);
+ if (new == NULL)
+ return ENOMEM;
+ pthread_rwlock_wrlock (&contents_lock);
+ if (contents != hello)
+ free (contents);
+ contents = new;
+ contents_len = strlen (new);
+ pthread_rwlock_unlock (&contents_lock);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* This will be called from libtrivfs to help construct the answer
+ to an fsys_get_options RPC. */
+error_t
+trivfs_append_args (struct trivfs_control *fsys,
+ char **argz, size_t *argz_len)
+{
+ error_t err;
+ char *opt;
+
+ pthread_rwlock_rdlock (&contents_lock);
+ err = asprintf (&opt, "--contents=%s", contents) < 0 ? ENOMEM : 0;
+ pthread_rwlock_unlock (&contents_lock);
+
+ if (!err)
+ {
+ err = argz_add (argz, argz_len, opt);
+ free (opt);
+ }
+
+ return err;
+}
+
+static struct argp hello_argp =
+{ options, parse_opt, 0,
+ "A multi-threaded translator providing a warm greeting." };
+
+/* Setting this variable makes libtrivfs use our argp to
+ parse options passed in an fsys_set_options RPC. */
+struct argp *trivfs_runtime_argp = &hello_argp;
+
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+
+ /* Initialize the lock that will protect CONTENTS and CONTENTS_LEN.
+ We must do this before argp_parse, because parse_opt (above) will
+ use the lock. */
+ pthread_rwlock_init (&contents_lock, NULL);
+
+ /* We use the same argp for options available at startup
+ as for options we'll accept in an fsys_set_options RPC. */
+ argp_parse (&hello_argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "Must be started as a translator");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (3, err, "trivfs_startup");
+
+ /* Launch. */
+ ports_manage_port_operations_multithread (fsys->pi.bucket, trivfs_demuxer,
+ 10 * 1000, /* idle thread */
+ 10 * 60 * 1000, /* idle server */
+ 0);
+
+ return 0;
+}
diff --git a/trans/hello.c b/trans/hello.c
new file mode 100644
index 00000000..4e88c609
--- /dev/null
+++ b/trans/hello.c
@@ -0,0 +1,293 @@
+/* hello.c - A trivial single-file translator
+ Copyright (C) 1998,1999,2001,02,2006 Free Software Foundation, Inc.
+ Gordon Matzigkeit <gord@fig.org>, 1999
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#define _GNU_SOURCE 1
+
+#include <hurd/trivfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include <version.h>
+
+#include "libtrivfs/trivfs_io_S.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (hello);
+
+/* The message we return when we are read. */
+static const char hello[] = "Hello, world!\n";
+static char *contents = (char *) hello;
+static size_t contents_len = sizeof hello - 1;
+
+/* Trivfs hooks. */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+
+int trivfs_allow_open = O_READ;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+
+/* NOTE: This example is not robust: it is possible to trigger some
+ assertion failures because we don't implement the following:
+
+ $ cd /src/hurd/libtrivfs
+ $ grep -l 'assert.*!trivfs_support_read' *.c |
+ xargs grep '^trivfs_S_' | sed 's/^[^:]*:\([^ ]*\).*$/\1/'
+ trivfs_S_io_get_openmodes
+ trivfs_S_io_clear_some_openmodes
+ trivfs_S_io_set_some_openmodes
+ trivfs_S_io_set_all_openmodes
+ trivfs_S_io_readable
+ trivfs_S_io_select
+ $
+
+ For that reason, you should run this as an active translator
+ `settrans -ac testnode /path/to/thello' so that you can see the
+ error messages when they appear. */
+
+/* A hook for us to keep track of the file descriptor state. */
+struct open
+{
+ off_t offs;
+};
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ /* Mark the node as a read-only plain file. */
+ st->st_mode &= ~(S_IFMT | ALLPERMS);
+ st->st_mode |= (S_IFREG | S_IRUSR | S_IRGRP | S_IROTH);
+ st->st_size = contents_len;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+ exit (0);
+}
+
+
+static error_t
+open_hook (struct trivfs_peropen *peropen)
+{
+ struct open *op = malloc (sizeof (struct open));
+ if (op == NULL)
+ return ENOMEM;
+
+ /* Initialize the offset. */
+ op->offs = 0;
+ peropen->hook = op;
+ return 0;
+}
+
+
+static void
+close_hook (struct trivfs_peropen *peropen)
+{
+ free (peropen->hook);
+}
+
+
+/* Read data from an IO object. If offset is -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMOUNT. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, mach_msg_type_number_t *data_len,
+ loff_t offs, mach_msg_type_number_t amount)
+{
+ struct open *op;
+
+ /* Deny access if they have bad credentials. */
+ if (! cred)
+ return EOPNOTSUPP;
+ else if (! (cred->po->openmodes & O_READ))
+ return EBADF;
+
+ /* Get the offset. */
+ op = cred->po->hook;
+ if (offs == -1)
+ offs = op->offs;
+
+ /* Prune the amount they want to read. */
+ if (offs > contents_len)
+ offs = contents_len;
+ if (offs + amount > contents_len)
+ amount = contents_len - offs;
+
+ if (amount > 0)
+ {
+ /* Possibly allocate a new buffer. */
+ if (*data_len < amount)
+ {
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ return ENOMEM;
+ }
+
+ /* Copy the constant data into the buffer. */
+ memcpy ((char *) *data, contents + offs, amount);
+
+ /* Update the saved offset. */
+ op->offs += amount;
+ }
+
+ *data_len = amount;
+ return 0;
+}
+
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offs, int whence, off_t *new_offs)
+{
+ struct open *op;
+ error_t err = 0;
+ if (! cred)
+ return EOPNOTSUPP;
+
+ op = cred->po->hook;
+ switch (whence)
+ {
+ case SEEK_CUR:
+ offs += op->offs;
+ goto check;
+ case SEEK_END:
+ offs += contents_len;
+ case SEEK_SET:
+ check:
+ if (offs >= 0)
+ {
+ *new_offs = op->offs = offs;
+ break;
+ }
+ default:
+ err = EINVAL;
+ }
+
+ return err;
+}
+
+
+/* If this variable is set, it is called every time a new peropen
+ structure is created and initialized. */
+error_t (*trivfs_peropen_create_hook)(struct trivfs_peropen *) = open_hook;
+
+/* If this variable is set, it is called every time a peropen structure
+ is about to be destroyed. */
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
+
+
+/* Options processing. We accept the same options on the command line
+ and from fsys_set_options. */
+
+static const struct argp_option options[] =
+{
+ {"contents", 'c', "STRING", 0, "Specify the contents of the virtual file"},
+ {0}
+};
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ switch (opt)
+ {
+ default:
+ return ARGP_ERR_UNKNOWN;
+ case ARGP_KEY_INIT:
+ case ARGP_KEY_SUCCESS:
+ case ARGP_KEY_ERROR:
+ break;
+
+ case 'c':
+ {
+ char *new = strdup (arg);
+ if (new == NULL)
+ return ENOMEM;
+ if (contents != hello)
+ free (contents);
+ contents = new;
+ contents_len = strlen (new);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* This will be called from libtrivfs to help construct the answer
+ to an fsys_get_options RPC. */
+error_t
+trivfs_append_args (struct trivfs_control *fsys,
+ char **argz, size_t *argz_len)
+{
+ error_t err;
+ char *opt;
+
+ if (asprintf (&opt, "--contents=%s", contents) < 0)
+ return ENOMEM;
+
+ err = argz_add (argz, argz_len, opt);
+
+ free (opt);
+
+ return err;
+}
+
+static struct argp hello_argp =
+{ options, parse_opt, 0, "A translator providing a warm greeting." };
+
+/* Setting this variable makes libtrivfs use our argp to
+ parse options passed in an fsys_set_options RPC. */
+struct argp *trivfs_runtime_argp = &hello_argp;
+
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+
+ /* We use the same argp for options available at startup
+ as for options we'll accept in an fsys_set_options RPC. */
+ argp_parse (&hello_argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "Must be started as a translator");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (3, err, "trivfs_startup");
+
+ /* Launch. */
+ ports_manage_port_operations_one_thread (fsys->pi.bucket, trivfs_demuxer, 0);
+
+ return 0;
+}
diff --git a/trans/ifsock.c b/trans/ifsock.c
new file mode 100644
index 00000000..4ed65898
--- /dev/null
+++ b/trans/ifsock.c
@@ -0,0 +1,152 @@
+/* Server for S_IFSOCK nodes
+ Copyright (C) 1994, 1995, 2001, 02, 2006 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <hurd/paths.h>
+#include <sys/socket.h>
+#include <hurd/socket.h>
+#include <hurd/fsys.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <error.h>
+#include <fcntl.h>
+#include <argp.h>
+
+#include <sys/cdefs.h>
+#ifndef __XSTRING /* Could / should (?) be provided by glibc. */
+#define __XSTRING(x) __STRING(x) /* Expand x, then stringify. */
+#endif
+
+#include <version.h>
+
+#include "ifsock_S.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (ifsock);
+
+static const char doc[] = "A translator to provide Unix domain sockets."
+"\vThis translator acts as a hook for Unix domain sockets."
+" The pflocal translator on " _SERVERS_SOCKET "/" __XSTRING(PF_LOCAL)
+" implements the sockets.";
+
+mach_port_t address_port;
+
+struct port_class *control_class;
+struct port_class *node_class;
+struct port_bucket *port_bucket;
+
+int trivfs_fstype = FSTYPE_IFSOCK;
+int trivfs_fsid = 0; /* ??? */
+
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = 0;
+
+struct port_class *trivfs_protid_portclasses[1];
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_protid_nportclasses = 1;
+int trivfs_cntl_nportclasses = 1;
+
+int
+demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ extern int ifsock_server (mach_msg_header_t *, mach_msg_header_t *);
+ return trivfs_demuxer (inp, outp) || ifsock_server (inp, outp);
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t pflocal;
+ mach_port_t bootstrap;
+ char buf[512];
+ const struct argp argp = { 0, 0, 0, doc };
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ control_class = ports_create_class (trivfs_clean_cntl, 0);
+ node_class = ports_create_class (trivfs_clean_protid, 0);
+ port_bucket = ports_create_bucket ();
+ trivfs_protid_portclasses[0] = node_class;
+ trivfs_cntl_portclasses[0] = control_class;
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error(1, 0, "Must be started as a translator");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0, control_class, port_bucket,
+ node_class, port_bucket, NULL);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error(2, err, "Contacting parent");
+
+ /* Try and connect to the pflocal server */
+ sprintf (buf, "%s/%d", _SERVERS_SOCKET, PF_LOCAL);
+ pflocal = file_name_lookup (buf, 0, 0);
+
+ if (pflocal == MACH_PORT_NULL)
+ address_port = MACH_PORT_NULL;
+ else
+ {
+ err = socket_fabricate_address (pflocal, AF_LOCAL, &address_port);
+ if (err)
+ address_port = MACH_PORT_NULL;
+ mach_port_deallocate (mach_task_self (), pflocal);
+ }
+
+ /* Launch. */
+ ports_manage_port_operations_one_thread (port_bucket, demuxer, 0);
+ return 0;
+}
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ st->st_mode = (st->st_mode & ~S_IFMT) | S_IFSOCK;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ exit (0);
+}
+
+error_t
+S_ifsock_getsockaddr (struct trivfs_protid *cred,
+ mach_port_t *address)
+{
+ int perms;
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != port_bucket
+ || cred->pi.class != node_class)
+ return EOPNOTSUPP;
+
+ err = file_check_access (cred->realnode, &perms);
+ if (!err && !(perms & O_READ))
+ err = EACCES;
+
+ if (!err)
+ *address = address_port;
+ return err;
+}
diff --git a/trans/magic.c b/trans/magic.c
new file mode 100644
index 00000000..58084838
--- /dev/null
+++ b/trans/magic.c
@@ -0,0 +1,567 @@
+/* A translator for returning FS_RETRY_MAGIC strings.
+
+ Copyright (C) 1999,2001,02, 03 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <hurd/fshelp.h>
+#include <hurd/fsys.h>
+#include <version.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <error.h>
+#include <string.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <argp.h>
+#include <argz.h>
+#include <assert.h>
+
+#include "fsys_S.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (magic);
+
+/* This structure has all the state about one filesystem.
+ It hangs off trivfs_control->hook. */
+struct magic
+{
+ /* We chain all filesystems together so we can tell easily when they are
+ all unused. */
+ struct trivfs_control *next;
+
+ /* The magic string we return for lookups. */
+ char *magic;
+
+ int directory; /* --directory flag */
+
+ /* Pre-fab contents of dummy directory for dir_readdir.
+ Set up only under --directory. */
+ void *dirbuf;
+ size_t dirbufsize;
+
+ unsigned int nusers; /* Count of users, only with --directory. */
+};
+
+static inline void
+free_magic (struct magic *m)
+{
+ free (m->magic);
+ if (m->dirbuf)
+ munmap (m->dirbuf, m->dirbufsize);
+ free (m);
+}
+
+static struct trivfs_control *all_fsys;
+
+/* Trivfs hooks */
+
+int trivfs_fstype = FSTYPE_DEV;
+int trivfs_fsid = 0;
+
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = O_READ;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ struct magic *const m = cred->po->cntl->hook;
+
+ st->st_size = m->dirbufsize;
+ st->st_blocks = getpagesize () / S_BLKSIZE;
+
+ st->st_mode = ((st->st_mode & ~S_IFMT & ~ALLPERMS)
+ | S_IFDIR | S_IXUSR|S_IXGRP|S_IXOTH
+ | (st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH)));
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ struct magic *const m = fsys->hook;
+
+ /* We are single-threaded, so no fancy stuff is needed here. */
+
+ if (m->nusers > 0 && !(flags & FSYS_GOAWAY_FORCE))
+ return EBUSY;
+
+ /* No more communication with the parent filesystem.
+ This running RPC should now be the only ref keeping FSYS alive. */
+ ports_destroy_right (fsys);
+ return 0;
+}
+
+
+/* Clean pointers in a struct trivfs_control when its last reference
+ vanishes before it's freed. This overrides the libtrivfs version
+ so we can clean up our hook data. */
+void
+trivfs_clean_cntl (void *arg)
+{
+ struct trivfs_control *cntl = arg;
+ struct magic *const m = cntl->hook;
+
+ /* Remove us from the list of all filesystems. */
+ struct trivfs_control **lastp = &all_fsys;
+ while (*lastp != cntl)
+ lastp = &((struct magic *) (*lastp)->hook)->next;
+ *lastp = m->next;
+
+ if (all_fsys == 0)
+ /* Nothing more to do in this life. */
+ exit (0);
+
+ mach_port_destroy (mach_task_self (), cntl->filesys_id);
+ mach_port_destroy (mach_task_self (), cntl->file_id);
+ mach_port_deallocate (mach_task_self (), cntl->underlying);
+
+ free_magic (m);
+}
+
+/* This hook is used when running without --directory;
+ it circumvents basically all the trivfs machinery. */
+
+static error_t
+magic_getroot (struct trivfs_control *cntl,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_port_type,
+ mach_port_t dotdot,
+ uid_t *uids, u_int nuids, uid_t *gids, u_int ngids,
+ int flags,
+ retry_type *do_retry, char *retry_name,
+ mach_port_t *node, mach_msg_type_name_t *node_type)
+{
+ error_t err;
+ struct magic *const m = cntl->hook;
+
+ if (m->directory)
+ return EAGAIN; /* Do normal trivfs getroot processing. */
+
+ strcpy (retry_name, m->magic);
+ *do_retry = FS_RETRY_MAGICAL;
+ *node = MACH_PORT_NULL;
+ *node_type = MACH_MSG_TYPE_COPY_SEND;
+
+ err = mach_port_deallocate (mach_task_self (), dotdot);
+ assert_perror (err);
+
+ return 0;
+}
+
+/* This hook is used when running with --directory, when
+ we do use all the normal trivfs machinery. We just use
+ the normal trivfs open, but then stash the DOTDOT port
+ in the trivfs_peropen. */
+
+static error_t
+magic_open (struct trivfs_control *cntl,
+ struct iouser *user,
+ mach_port_t dotdot,
+ int flags,
+ mach_port_t realnode,
+ struct trivfs_protid **cred)
+{
+ error_t err = trivfs_open (cntl, user, flags, realnode, cred);
+ if (!err)
+ {
+ /* We consume the reference for DOTDOT. */
+ (*cred)->po->hook = (void *) dotdot;
+ struct magic *const m = cntl->hook;
+ m->nusers++;
+ }
+ return err;
+}
+
+static void
+magic_peropen_destroy (struct trivfs_peropen *po)
+{
+ mach_port_deallocate (mach_task_self (), (mach_port_t) po->hook);
+}
+
+
+/* We have this hook only for simple tracking of the live user ports. */
+static void
+magic_protid_destroy (struct trivfs_protid *cred)
+{
+ struct magic *const m = cred->po->cntl->hook;
+ m->nusers--;
+}
+
+
+/* Do a directory lookup. */
+
+error_t
+trivfs_S_dir_lookup (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *name,
+ int flags,
+ mode_t mode,
+ retry_type *retry_type,
+ char *retry_name,
+ mach_port_t *retrypt,
+ mach_msg_type_name_t *retrypt_type)
+{
+ int perms;
+ error_t err;
+ struct trivfs_protid *newcred;
+ mach_port_t dotdot;
+ struct iouser *user;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (name[0] != '\0')
+ {
+ struct magic *const m = cred->po->cntl->hook;
+
+ if (!m->directory)
+ return ENOTDIR;
+
+ /* We have a real lookup in the dummy directory.
+ Handle `.' and `..' specially, and anything else
+ gets redirected to the magical retry. */
+
+ while (*name == '/')
+ ++name;
+ while (!strncmp (name, "./", 2))
+ {
+ name += 2;
+ while (*name == '/')
+ ++name;
+ }
+
+ if (!strcmp (name, "..") || !strncmp (name, "../", 3))
+ {
+ name += 2;
+ while (*name == '/')
+ ++name;
+ strcpy (retry_name, name);
+ *retry_type = FS_RETRY_REAUTH;
+ *retrypt = (mach_port_t) cred->po->hook;
+ *retrypt_type = MACH_MSG_TYPE_COPY_SEND;
+ return 0;
+ }
+ else if (name[0] != '\0' && strcmp (name, "."))
+ {
+ if (m->magic == 0)
+ strcpy (retry_name, name);
+ else
+ {
+ char *p = stpcpy (retry_name, m->magic);
+ *p++ = '/';
+ strcpy (p, name);
+ }
+ *retry_type = FS_RETRY_MAGICAL;
+ *retrypt = MACH_PORT_NULL;
+ *retrypt_type = MACH_MSG_TYPE_COPY_SEND;
+ return 0;
+ }
+ }
+
+ /* This is a null-pathname "reopen" call; do the right thing. */
+
+ /* Burn off flags we don't actually implement */
+ flags &= O_HURD;
+ flags &= ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS);
+
+ /* Validate permissions */
+ if (! trivfs_check_access_hook)
+ file_check_access (cred->realnode, &perms);
+ else
+ (*trivfs_check_access_hook) (cred->po->cntl, cred->user,
+ cred->realnode, &perms);
+ if ((flags & (O_READ|O_WRITE|O_EXEC) & perms)
+ != (flags & (O_READ|O_WRITE|O_EXEC)))
+ return EACCES;
+
+ /* Execute the open */
+
+ dotdot = (mach_port_t) cred->po->hook;
+ err = iohelp_dup_iouser (&user, cred->user);
+ if (err)
+ return err;
+ err = magic_open (cred->po->cntl, user, dotdot, flags,
+ cred->realnode, &newcred);
+ if (err)
+ {
+ iohelp_free_iouser (user);
+ return err;
+ }
+ err = mach_port_mod_refs (mach_task_self (), dotdot,
+ MACH_PORT_RIGHT_SEND, +1);
+ assert_perror (err);
+ err = mach_port_mod_refs (mach_task_self (), cred->realnode,
+ MACH_PORT_RIGHT_SEND, +1);
+ assert_perror (err);
+
+ *retry_type = FS_RETRY_NORMAL;
+ *retry_name = '\0';
+ *retrypt = ports_get_right (newcred);
+ *retrypt_type = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newcred);
+ return 0;
+}
+
+error_t
+trivfs_S_dir_readdir (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data,
+ size_t *datalen,
+ boolean_t *data_dealloc,
+ int entry,
+ int nentries,
+ vm_size_t bufsiz,
+ int *amount)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ struct magic *const m = cred->po->cntl->hook;
+
+ if (entry > 0)
+ {
+ void *p;
+ int i;
+ i = 0;
+ for (p = m->dirbuf; p < m->dirbuf + m->dirbufsize;
+ p += ((struct dirent *) p)->d_reclen)
+ if (i++ == entry)
+ break;
+ *data = p;
+ *datalen = m->dirbuf + m->dirbufsize - p;
+ *amount = 2 - entry;
+ }
+ else
+ {
+ *data = m->dirbuf;
+ *datalen = m->dirbufsize;
+ *amount = 2;
+ }
+
+ *data_dealloc = 0;
+ return 0;
+}
+
+
+#include <hurd/paths.h>
+#define _SERVERS_MAGIC _SERVERS "magic"
+
+/* To whom should we try to delegate on startup? */
+static const char *delegate = _SERVERS_MAGIC;
+
+static const struct argp_option options[] =
+{
+ {"directory", 'd', 0, 0, "Provide virtual (empty) directory node"},
+ {"use-server", 'U', "NAME", 0,
+ "Delegate to server NAME instead of " _SERVERS_MAGIC},
+ {0}
+};
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ struct magic *const m = state->input;
+
+ switch (opt)
+ {
+ case 'U':
+ /* This is only valid for the startup options, not delegates. */
+ if (all_fsys != 0)
+ return EINVAL;
+ delegate = arg;
+ return 0;
+
+ case 'd':
+ case ARGP_KEY_NO_ARGS:
+ m->directory = 1;
+ return 0;
+
+ case ARGP_KEY_ARG:
+ if (m->magic != 0)
+ {
+ argp_usage (state);
+ return EINVAL;
+ }
+ m->magic = strdup (arg);
+ return m->magic == 0 ? ENOMEM : 0;
+
+ case ARGP_KEY_SUCCESS:
+ if (m->directory)
+ {
+ inline struct dirent *add (struct dirent *d, const char *name)
+ {
+ d->d_fileno = 2; /* random */
+ d->d_type = DT_DIR;
+ d->d_namlen = strlen (name);
+ strcpy (d->d_name, name);
+ d->d_name[d->d_namlen] = '\0';
+ d->d_reclen = &d->d_name[d->d_namlen + 1] - (char *) d;
+ d->d_reclen = ((d->d_reclen + __alignof (struct dirent) - 1)
+ & ~(__alignof (struct dirent) - 1));
+ return (struct dirent *) ((char *) d + d->d_reclen);
+ }
+ struct dirent *d;
+ m->dirbuf = mmap (0, getpagesize (), PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ d = add (m->dirbuf, ".");
+ d = add (d, "..");
+ m->dirbufsize = (char *) d - (char *) m->dirbuf;
+ }
+ return 0;
+ }
+
+ return ARGP_ERR_UNKNOWN;
+}
+
+error_t
+trivfs_append_args (struct trivfs_control *fsys,
+ char **argz, size_t *argz_len)
+{
+ struct magic *const m = fsys->hook;
+ return ((m->directory ? argz_add (argz, argz_len, "--directory") : 0)
+ ?: (m->magic ? argz_add (argz, argz_len, m->magic) : 0));
+}
+
+static struct argp argp =
+ {
+ options, parse_opt, "MAGIC",
+ "A translator that returns the magic retry result MAGIC."
+ };
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+ struct magic *m = calloc (1, sizeof *m);
+
+ argp_parse (&argp, argc, argv, 0, 0, m);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "Must be started as a translator");
+
+ if (delegate != 0)
+ {
+ /* First, try to have the canonical server sitting on /servers/magic
+ take over for us. */
+ err = fshelp_delegate_translation (delegate, bootstrap, argv);
+ if (err == 0)
+ return 0;
+ }
+
+ /* Nope, we are doing it ourselves. */
+
+ trivfs_getroot_hook = &magic_getroot;
+ trivfs_open_hook = &magic_open;
+ trivfs_protid_destroy_hook = &magic_protid_destroy;
+ if (m->directory)
+ trivfs_peropen_destroy_hook = &magic_peropen_destroy;
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (3, err, "Contacting parent");
+ fsys->hook = m;
+ all_fsys = fsys;
+
+ /* Launch. */
+ while (1)
+ {
+ ports_manage_port_operations_one_thread (fsys->pi.bucket, trivfs_demuxer,
+ 10 * 60 * 1000);
+
+ /* That returns when 10 minutes pass without an RPC. Try shutting down
+ as if sent fsys_goaway; if we have any users who need us to stay
+ around, this returns EBUSY and we loop to service more RPCs. */
+
+ struct trivfs_control *fs = all_fsys;
+ do
+ {
+ struct magic *const m = fs->hook;
+ struct trivfs_control *const next = m->next;
+ trivfs_goaway (fs, 0);
+ fs = next;
+ } while (fs != 0);
+ }
+
+ return 0;
+}
+
+
+/* Handle delegated filesystems. */
+error_t
+trivfs_S_fsys_forward (mach_port_t server,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t requestor,
+ char *argz, size_t argz_len)
+{
+ struct trivfs_protid *cred
+ = ports_lookup_port (all_fsys->pi.bucket, server,
+ trivfs_protid_portclasses[0]);
+ if (!cred)
+ return EOPNOTSUPP;
+ ports_port_deref (cred);
+
+ /* Allocate a new structure for parameters, and parse the arguments
+ to fill it in. */
+ struct magic *m = calloc (1, sizeof *m);
+ if (!m)
+ return ENOMEM;
+
+ int argc = argz_count (argz, argz_len);
+ char *argv[argc + 1];
+ argz_extract (argz, argz_len, argv);
+ error_t err = argp_parse (&argp, argc, argv,
+ ARGP_NO_ERRS | ARGP_NO_HELP, 0, m);
+ if (err)
+ {
+ free_magic (m);
+ return err;
+ }
+
+ /* Now we are ready to start up the filesystem. Contact the parent. */
+ struct trivfs_control *fsys;
+ err = trivfs_startup (requestor, 0,
+ trivfs_cntl_portclasses[0], all_fsys->pi.bucket,
+ trivfs_protid_portclasses[0], all_fsys->pi.bucket,
+ &fsys);
+ if (err)
+ {
+ free_magic (m);
+ return err;
+ }
+ mach_port_deallocate (mach_task_self (), requestor);
+
+ /* The new filesystem is all hooked up.
+ Put it on the list of all filesystems we are serving. */
+ m->next = all_fsys;
+ fsys->hook = m;
+ all_fsys = fsys;
+
+ return 0;
+}
diff --git a/trans/mtab.c b/trans/mtab.c
new file mode 100644
index 00000000..df03b1d3
--- /dev/null
+++ b/trans/mtab.c
@@ -0,0 +1,882 @@
+/* This is an mtab translator.
+
+ Copyright (C) 2013,14 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <fcntl.h>
+#include <hurd.h>
+#include <hurd/trivfs.h>
+#include <inttypes.h>
+#include <mntent.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <version.h>
+
+#include "libtrivfs/trivfs_io_S.h"
+#include "fs_U.h"
+
+static char *target_path = NULL;
+static int insecure = 0;
+static int all_translators = 0;
+
+/* Our control port. */
+struct trivfs_control *control;
+
+/* These kind of objects are created and populated in the open_hook.
+ They keep track of the content and file position of the client. */
+struct mtab
+{
+ pthread_mutex_t lock;
+ char *contents;
+ size_t contents_len;
+ off_t offs;
+};
+
+const char *argp_program_version = STANDARD_HURD_VERSION (mtab);
+
+static const struct argp_option options[] =
+{
+ {"insecure", 'I', 0, 0,
+ "Follow translators not bound to nodes owned by you or root"},
+ {"all-translators", 'A', 0, 0,
+ "List all translators, even those that are probably not "
+ "filesystem translators"},
+ {}
+};
+
+/* Parse a command line option. */
+error_t parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'I':
+ insecure = 1;
+ break;
+
+ case 'A':
+ all_translators = 1;
+ break;
+
+ case ARGP_KEY_ARG:
+ target_path = realpath (arg, NULL);
+ if (! target_path)
+ argp_error (state, "Error while canonicalizing path");
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ return EINVAL;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static struct argp argp =
+ {
+ options,
+ parse_opt,
+ "TARGET\tFile name of a node with an active translator",
+ "A translator providing mtab compatible information about active "
+ "and passive translators below TARGET.",
+ };
+
+/* This will be called from libtrivfs to help construct the answer
+ to an fsys_get_options RPC. */
+error_t
+trivfs_append_args (struct trivfs_control *fsys,
+ char **argz, size_t *argz_len)
+{
+ error_t err;
+
+ if (insecure)
+ {
+ err = argz_add (argz, argz_len, target_path);
+ if (err)
+ return err;
+ }
+
+ err = argz_add (argz, argz_len, target_path);
+ return err;
+}
+
+/* Setting this variable makes libtrivfs use our argp to
+ parse options passed in an fsys_set_options RPC. */
+struct argp *trivfs_runtime_argp = &argp;
+
+/* Authentication of the current process. */
+uid_t *uids;
+gid_t *gids;
+size_t uids_len, gids_len;
+
+/* Initialize and populate the uids and gids vectors. */
+error_t
+get_credentials (void)
+{
+ /* Fetch uids... */
+ uids_len = geteuids (0, 0);
+ if (uids_len < 0)
+ return errno;
+
+ uids = malloc (uids_len * sizeof (uid_t));
+ if (! uids)
+ return ENOMEM;
+
+ uids_len = geteuids (uids_len, uids);
+ if (uids_len < 0)
+ return errno;
+
+ /* ... and gids. */
+ gids_len = getgroups (0, 0);
+ if (gids_len < 0)
+ return errno;
+
+ gids = malloc (gids_len * sizeof (gid_t));
+ if (! uids)
+ return ENOMEM;
+
+ gids_len = getgroups (gids_len, gids);
+ if (gids_len < 0)
+ return errno;
+
+ return 0;
+}
+
+/* Check if the given struct stat describes a node owned by the
+ current user. */
+int
+is_owner (io_statbuf_t *st)
+{
+ int found = 0;
+ for (size_t i = 0; i < uids_len; i++)
+ if (uids[i] == st->st_uid)
+ {
+ found = 1;
+ break;
+ }
+
+ if (! found)
+ return 0;
+
+ found = 0;
+ for (size_t i = 0; i < gids_len; i++)
+ if (gids[i] == st->st_gid)
+ {
+ found = 1;
+ break;
+ }
+
+ return found;
+}
+
+error_t
+mtab_populate (struct mtab *mtab, const char *path, int insecure);
+
+error_t
+argz_add_device (char **options, size_t *options_len, const char *device);
+
+error_t
+map_device_to_path (const char *device, char **path);
+
+int
+main (int argc, char *argv[])
+{
+ error_t err;
+
+ err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+ if (err)
+ error (1, err, "argument parsing");
+
+ err = get_credentials ();
+ if (err)
+ error (2, err, "getting credentials");
+
+ mach_port_t bootstrap;
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap != MACH_PORT_NULL)
+ {
+ /* Started as a translator. */
+
+ auth_t nullauth;
+ err = auth_makeauth (getauth (),
+ NULL, MACH_MSG_TYPE_COPY_SEND, 0,
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ &nullauth);
+ if (err)
+ error (3, err, "dropping credentials");
+
+ err = setauth (nullauth);
+ if (err)
+ error (3, err, "dropping credentials");
+
+ /* Reply to our parent. */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &control);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (4, err, "trivfs_startup");
+
+ /* Launch. */
+ ports_manage_port_operations_multithread (control->pi.bucket,
+ trivfs_demuxer,
+ /* idle thread timeout */
+ 30 * 1000,
+ /* idle server timeout */
+ 0,
+ NULL);
+ }
+ else
+ {
+ /* One-shot mode. */
+ struct mtab mtab = { .lock = PTHREAD_MUTEX_INITIALIZER };
+ err = mtab_populate (&mtab, target_path, insecure);
+ if (err)
+ error (5, err, "%s", target_path);
+
+ if (mtab.contents)
+ printf ("%s", mtab.contents);
+ }
+
+ return 0;
+}
+
+error_t
+mtab_add_entry (struct mtab *mtab, const char *entry, size_t length)
+{
+ char *p = realloc (mtab->contents, mtab->contents_len + length + 1);
+ if (! p)
+ return ENOMEM;
+
+ memcpy (&p[mtab->contents_len], entry, length);
+
+ mtab->contents = p;
+ mtab->contents_len += length;
+
+ /* Zero-terminate contents so that we can also interpret it as
+ string. */
+ mtab->contents[mtab->contents_len] = '\0';
+
+ return 0;
+}
+
+/* Check whether the given NODE is a directory on a filesystem
+ translator. */
+static boolean_t
+is_filesystem_translator (file_t node)
+{
+ error_t err;
+ char *data = NULL;
+ size_t datacnt = 0;
+ int amount;
+ err = dir_readdir (node, &data, &datacnt, 0, 1, 0, &amount);
+ if (data != NULL && datacnt > 0)
+ vm_deallocate (mach_task_self (), (vm_address_t) data, datacnt);
+
+ /* Filesystem translators return either no error, or, if NODE has
+ not been looked up with O_READ, EBADF to dir_readdir
+ requests. */
+ switch (err)
+ {
+ case 0:
+ case EBADF:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/* Populates the given MTAB object with the information for PATH. If
+ INSECURE is given, also follow translators bound to nodes not owned
+ by root or the current user. */
+/* XXX split up */
+error_t
+mtab_populate (struct mtab *mtab, const char *path, int insecure)
+{
+ error_t err = 0;
+
+ /* These resources are freed in the epilogue. */
+ file_t node = MACH_PORT_NULL;
+ char *argz = NULL;
+ size_t argz_len = 0;
+ char **argv = NULL;
+ char *type = NULL;
+ char *options = NULL;
+ size_t options_len = 0;
+ char *src = NULL;
+ char *entry = NULL;
+ size_t entry_len = 0;
+ char *children = NULL;
+ size_t children_len = 0;
+
+ if (! insecure)
+ {
+ /* Get the underlying node. */
+ node = file_name_lookup (path, O_NOTRANS, 0666);
+ if (node == MACH_PORT_NULL)
+ {
+ err = errno;
+ goto errout;
+ }
+
+ /* Check who owns the node the translator is bound to. */
+ io_statbuf_t st;
+ err = io_stat (node, &st);
+ if (err)
+ goto errout;
+
+ if (st.st_uid != 0 && st.st_gid != 0 && ! is_owner (&st))
+ {
+ err = EPERM;
+ goto errout;
+ }
+
+ mach_port_deallocate (mach_task_self (), node);
+ }
+
+ /* (Re-)do the lookup without O_NOTRANS to get the root node. */
+ node = file_name_lookup (path, 0, 0666);
+ if (node == MACH_PORT_NULL)
+ {
+ err = errno;
+ goto errout;
+ }
+
+ if (! (all_translators || is_filesystem_translator (node)))
+ {
+ err = 0;
+ goto errout;
+ }
+
+ /* Query its options. */
+ err = file_get_fs_options (node, &argz, &argz_len);
+ if (err)
+ {
+ if (err == EOPNOTSUPP)
+ err = 0; /* There's not much we could do then. */
+
+ goto errout;
+ }
+
+ size_t count = argz_count (argz, argz_len);
+ argv = malloc ((count + 1) * sizeof (char *));
+ if (! argv)
+ {
+ err = ENOMEM;
+ goto errout;
+ }
+
+ argz_extract (argz, argz_len, argv);
+
+ type = strdup (argv[0]);
+ if (! type)
+ {
+ err = ENOMEM;
+ goto errout;
+ }
+
+ for (int i = 1; i < count - 1; i++)
+ {
+ char *v = argv[i];
+
+ if (*v == '-')
+ v++;
+ if (*v == '-')
+ v++;
+
+ err = argz_add (&options, &options_len, v);
+ if (err)
+ goto errout;
+ }
+
+ err = argz_add_device (&options, &options_len, argv[count - 1]);
+ if (err)
+ goto errout;
+
+ argz_stringify (options, options_len, ',');
+
+ string_t source;
+ err = file_get_source (node, source);
+ if (err)
+ goto errout;
+
+ /* Guess based on the last argument. */
+ err = map_device_to_path (source, &src);
+ if (err)
+ goto errout;
+
+ entry_len = asprintf (&entry, "%s %s %s %s 0 0\n", src, path, type,
+ options? options: MNTOPT_DEFAULTS);
+ if (! entry)
+ {
+ err = ENOMEM;
+ goto errout;
+ }
+
+ err = mtab_add_entry (mtab, entry, entry_len);
+ if (err)
+ goto errout;
+
+ /* path has an active translator, query its children. */
+ err = file_get_children (node, &children, &children_len);
+ if (err == EOPNOTSUPP)
+ {
+ err = 0;
+ children_len = 0;
+ }
+
+ if (err)
+ goto errout;
+
+ if (children_len)
+ for (char *c = children; c; c = argz_next (children, children_len, c))
+ {
+ char *p = NULL;
+ asprintf (&p, "%s%s%s",
+ path,
+ path[strlen (path) - 1] == '/'? "": "/",
+ c);
+ if (! p)
+ {
+ err = ENOMEM;
+ goto errout;
+ }
+
+ err = mtab_populate (mtab, p, insecure);
+ if (err)
+ {
+ /* There is really not much we can do about errors here. */
+ error (0, err, "%s", p);
+ err = 0;
+ }
+
+ free (p);
+ }
+
+ errout:
+ if (node != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), node);
+
+ if (argz)
+ vm_deallocate (mach_task_self (), (vm_address_t) argz, argz_len);
+
+ free (argv);
+ free (type);
+ free (options);
+
+ if (src != source)
+ free (src);
+
+ free (entry);
+
+ if (children)
+ vm_deallocate (mach_task_self (), (vm_address_t) children, children_len);
+
+ return err;
+}
+
+/* Decodes the DEVICE string into appropriate OPTIONS. Currently only
+ tmpfs-style size declarations are supported. */
+error_t
+argz_add_device (char **options, size_t *options_len, const char *device)
+{
+ error_t err;
+ char *end = NULL;
+ intmax_t size = strtoimax (device, &end, 0);
+ if (end == NULL || end == device)
+ return 0;
+
+ if (size < 0)
+ return 0;
+
+ switch (*end)
+ {
+ case 'g':
+ case 'G':
+ case 'm':
+ case 'M':
+ case 'k':
+ case 'K':
+ break;
+ default:
+ return 0;
+ }
+
+ /* device specifies a size. */
+ char *arg = NULL;
+ asprintf (&arg, "size=%s", device);
+ if (! arg)
+ return ENOMEM;
+
+ err = argz_add (options, options_len, arg);
+
+ free (arg);
+ return err;
+}
+
+/* Matches [hs]d\ds\d\d?. */
+int
+looks_like_block_device (const char *s)
+{
+ size_t len = strlen (s);
+ if (len != 3 && len != 5 && len != 6)
+ return 0;
+
+ return ((s[0] == 'h' || s[0] == 's') && s[1] == 'd' && isdigit (s[2]) &&
+ (len == 3 || (s[3] == 's' && isdigit (s[4]) &&
+ (len == 5 || isdigit (s[5])))));
+}
+
+/* Map a device string to a file name referencing the appropriate
+ device file. */
+error_t
+map_device_to_path (const char *device, char **path)
+{
+ if (strncmp (device, "device:", 7) == 0)
+ asprintf (path, "/dev/%s", &device[7]);
+ else if (strncmp (device, "/dev/", 5) == 0)
+ *path = strdup (device);
+ else if (looks_like_block_device (device))
+ asprintf (path, "/dev/%s", device);
+ else
+ *path = strdup (device);
+
+ if (! *path)
+ return ENOMEM;
+
+ return 0;
+}
+
+/* Trivfs hooks. */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+
+int trivfs_allow_open = O_READ;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ /* Mark the node as a read-only plain file. */
+ st->st_mode &= ~(S_IFMT | ALLPERMS);
+ st->st_mode |= (S_IFREG | S_IRUSR | S_IRGRP | S_IROTH);
+ st->st_size = ((struct mtab *) cred->po->hook)->contents_len;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+ exit (EXIT_SUCCESS);
+}
+
+static error_t
+open_hook (struct trivfs_peropen *peropen)
+{
+ struct mtab *mtab = malloc (sizeof (struct mtab));
+ if (mtab == NULL)
+ return ENOMEM;
+
+ /* Hook! */
+ peropen->hook = mtab;
+
+ /* Initialize the fields. */
+ pthread_mutex_init (&mtab->lock, NULL);
+ mtab->offs = 0;
+ mtab->contents = NULL;
+ mtab->contents_len = 0;
+
+ /* The mtab object is initialized, but not yet populated. We delay
+ that until that data is really needed. This avoids the following
+ problems:
+
+ Suppose you have
+
+ settrans -ac /foo /hurd/mtab /
+
+ If you now access /foo, the mtab translator will walk the tree of
+ all active translators starting from /. If it visits /foo, it
+ will talk to itself. Previously the translator migitated this by
+ comparing the control port of the translator with its own. This
+ does not work if you got two mtab translators like this:
+
+ settrans -ac /foo /hurd/mtab /
+ settrans -ac /bar /hurd/mtab /
+
+ With a single-threaded mtab server this results in a dead-lock,
+ with a multi-threaded server this will create more and more
+ threads.
+
+ Delaying the data generation until it is really needed cleanly
+ avoids these kind of problems. */
+ return 0;
+}
+
+static void
+close_hook (struct trivfs_peropen *peropen)
+{
+ struct mtab *op = peropen->hook;
+ pthread_mutex_destroy (&op->lock);
+ free (op->contents);
+ free (op);
+}
+
+/* Read data from an IO object. If offset is -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMOUNT. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, mach_msg_type_number_t *data_len,
+ loff_t offs, mach_msg_type_number_t amount)
+{
+ error_t err = 0;
+ struct mtab *op;
+
+ /* Deny access if they have bad credentials. */
+ if (! cred)
+ return EOPNOTSUPP;
+
+ if (! (cred->po->openmodes & O_READ))
+ return EBADF;
+
+ /* Get the offset. */
+ op = cred->po->hook;
+ pthread_mutex_lock (&op->lock);
+
+ if (op->contents == NULL)
+ {
+ err = mtab_populate (op, target_path, insecure);
+ if (err)
+ goto out;
+ }
+
+ if (offs == -1)
+ offs = op->offs;
+
+ /* Prune the amount they want to read. */
+ if (offs > op->contents_len)
+ offs = op->contents_len;
+ if (offs + amount > op->contents_len)
+ amount = op->contents_len - offs;
+
+ if (amount > 0)
+ {
+ /* Possibly allocate a new buffer. */
+ if (*data_len < amount)
+ {
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ {
+ err = ENOMEM;
+ goto out;
+ }
+ }
+
+ /* Copy the constant data into the buffer. */
+ memcpy ((char *) *data, op->contents + offs, amount);
+
+ /* Update the saved offset. */
+ op->offs += amount;
+ }
+
+ *data_len = amount;
+ out:
+ pthread_mutex_unlock (&op->lock);
+ return err;
+}
+
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offs, int whence, off_t *new_offs)
+{
+ error_t err = 0;
+ if (! cred)
+ return EOPNOTSUPP;
+
+ struct mtab *op = cred->po->hook;
+ pthread_mutex_lock (&op->lock);
+
+ if (op->contents == NULL)
+ {
+ err = mtab_populate (op, target_path, insecure);
+ if (err)
+ goto out;
+ }
+
+ switch (whence)
+ {
+ case SEEK_CUR:
+ offs += op->offs;
+ goto check;
+ case SEEK_END:
+ offs += op->contents_len;
+ case SEEK_SET:
+ check:
+ if (offs >= 0)
+ {
+ *new_offs = op->offs = offs;
+ break;
+ }
+ default:
+ err = EINVAL;
+ }
+
+ out:
+ pthread_mutex_unlock (&op->lock);
+ return err;
+}
+
+/* If this variable is set, it is called every time a new peropen
+ structure is created and initialized. */
+error_t (*trivfs_peropen_create_hook)(struct trivfs_peropen *) = open_hook;
+
+/* If this variable is set, it is called every time a peropen structure
+ is about to be destroyed. */
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+kern_return_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ mach_msg_type_number_t *amount)
+{
+ error_t err = 0;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (!(cred->po->openmodes & O_READ))
+ return EINVAL;
+
+ struct mtab *op = cred->po->hook;
+ pthread_mutex_lock (&op->lock);
+
+ if (op->contents == NULL)
+ {
+ error_t err = mtab_populate (op, target_path, insecure);
+ if (err)
+ goto out;
+ }
+
+ *amount = op->contents_len - op->offs;
+ out:
+ pthread_mutex_unlock (&op->lock);
+ return err;
+}
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. ID_TAG is returned as passed; it
+ is just for the convenience of the user in matching up reply messages with
+ specific requests sent. */
+kern_return_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int *type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (((*type & SELECT_READ) && !(cred->po->openmodes & O_READ))
+ || ((*type & SELECT_WRITE) && !(cred->po->openmodes & O_WRITE)))
+ return EBADF;
+
+ *type &= ~SELECT_URG;
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ struct timespec ts,
+ int *type)
+{
+ return trivfs_S_io_select (cred, reply, replytype, type);
+}
+
+/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and
+ O_NONBLOCK bits for the IO object. In addition, io_get_openmodes
+ will tell you which of O_READ, O_WRITE, and O_EXEC the object can
+ be used for. The O_ASYNC bit affects icky async I/O; good async
+ I/O is done through io_async which is orthogonal to these calls. */
+
+kern_return_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ *bits = cred->po->openmodes;
+ return 0;
+}
+
+error_t
+trivfs_S_io_set_all_openmodes(struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int mode)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ return 0;
+}
diff --git a/trans/new-fifo.c b/trans/new-fifo.c
new file mode 100644
index 00000000..e71c95ca
--- /dev/null
+++ b/trans/new-fifo.c
@@ -0,0 +1,857 @@
+/* A translator for fifos
+
+ Copyright (C) 1995,96,97,98,2000,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <errno.h>
+#include <argp.h>
+#include <unistd.h>
+#include <error.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include <pthread.h>
+#include <hurd.h>
+#include <argz.h>
+#include <hurd/fshelp.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <hurd/fsys.h>
+#include <hurd/pipe.h>
+#include <hurd/paths.h>
+
+#include <version.h>
+
+#include "libtrivfs/trivfs_fs_S.h"
+#include "libtrivfs/trivfs_fsys_S.h"
+#include "libtrivfs/trivfs_io_S.h"
+
+#define DEFAULT_SERVER _SERVERS "fifo";
+
+const char *argp_program_version = STANDARD_HURD_VERSION (new-fifo);
+
+struct port_bucket *port_bucket;
+struct port_class *fifo_port_class, *server_port_class, *fsys_port_class;
+
+/* ---------------------------------------------------------------- */
+
+static const struct argp_option options[] =
+{
+ {"multiple-readers", 'r', 0, 0, "Allow multiple simultaneous readers"},
+ {"noblock", 'n', 0, 0, "Don't block on open"},
+ {"dgram", 'd', 0, 0, "Reflect write record boundaries"},
+ {"server", 's', 0, 0, "Operate in server mode"},
+ {"standalone", 'S', 0, 0, "Don't attempt to use a fifo server"},
+ {"use-server", 'U', "NAME",0, "Attempt use server NAME"},
+ {0,0}
+};
+
+/* Per translator variables. */
+struct fifo_trans
+{
+ /* True if this not a real translator, but instead a translator server,
+ which responds to requests to create translators. */
+ int server;
+
+ /* True if opens for writing should hang until there's a reader. */
+ int wait_for_reader;
+ /* True if opens for reading should hang until there's a writer. */
+ int wait_for_writer;
+ /* True if opens for read should hang until there are no other readers. */
+ int one_reader;
+
+ /* If non-null, the name of a fifo server to do the translation in our
+ stead. */
+ char *use_server;
+
+ /* The translator from which this was initialized. */
+ struct fifo_trans *parent;
+
+ /* What kinds of pipes we use. */
+ struct pipe_class *fifo_pipe_class;
+
+ /* The current fifo that new opens will see, or NULL if there is none. */
+ struct pipe *active_fifo;
+ /* Lock this when changing ACTIVE_FIFO. */
+ pthread_mutex_t active_fifo_lock;
+ /* Signal this when ACTIVE_FIFO may have changed. */
+ pthread_cond_t active_fifo_changed;
+};
+
+/* Return a new FIFO_TRANS in TRANS, initializing it from FROM if it's
+ non-null, where possible. */
+static void
+fifo_trans_create (struct fifo_trans *from, struct fifo_trans **trans)
+{
+ struct fifo_trans *new = malloc (sizeof (struct fifo_trans));
+
+ new->server = 0;
+ pthread_mutex_init (&new->active_fifo_lock, NULL);
+ pthread_cond_init (&new->active_fifo_changed, NULL);
+
+ new->parent = from;
+
+ if (from)
+ /* Inherit things that can be inherited. */
+ {
+ new->wait_for_reader = from->wait_for_reader;
+ new->wait_for_writer = from->wait_for_writer;
+ new->one_reader = from->one_reader;
+ new->use_server = from->use_server;
+ new->fifo_pipe_class = from->fifo_pipe_class;
+ }
+ else
+ /* Otherwise just use default values. */
+ {
+ new->wait_for_reader = 1;
+ new->wait_for_writer = 1;
+ new->one_reader = 1;
+ new->use_server = DEFAULT_SERVER;
+ new->fifo_pipe_class = stream_pipe_class;
+ }
+
+ *trans = new;
+}
+
+static void
+fifo_trans_free (struct fifo_trans *trans)
+{
+ free (trans);
+}
+
+static error_t
+fifo_trans_start (struct fifo_trans *trans, mach_port_t requestor)
+{
+ struct trivfs_control *control;
+ struct port_class *class =
+ (trans->server ? server_port_class : fifo_port_class);
+ error_t
+ err = trivfs_startup (requestor, 0,
+ fsys_port_class, port_bucket, class, port_bucket,
+ &control);
+ if (!err)
+ control->hook = trans;
+ return err;
+}
+
+/* Parse our options. SERVER is true if we are a fifo server providing
+ service for others (in which case we don't print error messages). The
+ results of the parse are put into TRANS. */
+static error_t
+fifo_trans_parse_args (struct fifo_trans *trans, int argc, char **argv,
+ int print_errs)
+{
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'r': trans->one_reader = 0; break;
+ case 'n': trans->wait_for_reader = trans->wait_for_writer = 0; break;
+ case 'd': trans->fifo_pipe_class = seqpack_pipe_class;
+ case 's': trans->server = 1; break;
+ case 'U': trans->use_server = arg; break;
+ case 'S': trans->use_server = 0; break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, 0, "A translator for fifos." };
+ return argp_parse (&argp, argc, argv, print_errs ? 0 : ARGP_SILENT, 0, 0);
+}
+
+/* ---------------------------------------------------------------- */
+
+struct port_class *trivfs_protid_portclasses[2];
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_protid_nportclasses = 2;
+int trivfs_cntl_nportclasses = 1;
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct fifo_trans *trans;
+ /* Clean up a fsys control node. */
+ void clean_fsys (void *vfsys)
+ {
+ struct trivfs_control *fsys = vfsys;
+ if (fsys->hook)
+ fifo_trans_free (fsys->hook);
+ trivfs_clean_cntl (fsys);
+ }
+
+ fifo_trans_create (0, &trans);
+
+ if (fifo_trans_parse_args (trans, argc, argv, 1) != 0)
+ exit (1);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error(1, 0, "must be started as a translator");
+
+ if (!trans->server && trans->use_server)
+ /* Attempt to contact a fifo server to do our work for us. */
+ {
+ err = fshelp_delegate_translation (trans->use_server, bootstrap, argv);
+ if (!err)
+ exit (0);
+ }
+
+ port_bucket = ports_create_bucket ();
+ fifo_port_class = ports_create_class (trivfs_clean_protid, 0);
+ server_port_class = ports_create_class (trivfs_clean_protid, 0);
+ fsys_port_class = ports_create_class (clean_fsys, 0);
+
+ trivfs_protid_portclasses[0] = fifo_port_class;
+ trivfs_protid_portclasses[1] = server_port_class;
+ trivfs_cntl_portclasses[0] = fsys_port_class;
+
+ /* Reply to our parent */
+ fifo_trans_start (trans, bootstrap);
+
+ /* Launch. */
+ do
+ {
+ ports_enable_class (fifo_port_class);
+ ports_manage_port_operations_multithread (port_bucket,
+ trivfs_demuxer,
+ 30*1000, 5*60*1000, 0);
+ }
+ while (ports_count_class (fifo_port_class) > 0);
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+static error_t
+fifo_trans_open (struct fifo_trans *trans, int flags, void **hook)
+{
+ error_t err = 0;
+
+ if (flags & (O_READ | O_WRITE))
+ {
+ pthread_mutex_lock (&trans->active_fifo_lock);
+
+/* Wait until the active fifo has changed so that CONDITION is true. */
+#define WAIT(condition, noblock_err) \
+ while (!err && !(condition)) \
+ { \
+ if (flags & O_NONBLOCK) \
+ { \
+ err = noblock_err; \
+ break; \
+ } \
+ else if (pthread_hurd_cond_wait_np (&trans->active_fifo_changed, \
+ &trans->active_fifo_lock)) \
+ err = EINTR; \
+ }
+
+ if (flags & O_READ)
+ /* When opening for read, what we do depends on what mode this server
+ is running in. The default (if ONE_READER is set) is to only
+ allow one reader at a time, with additional opens for read
+ blocking here until the old reader goes away; otherwise, we allow
+ multiple readers. If WAIT_FOR_WRITER is true, then once we've
+ created a fifo, we also block until someone opens it for writing;
+ otherwise, the first read will block until someone writes
+ something. */
+ {
+ if (trans->one_reader)
+ /* Wait until there isn't any active fifo, so we can make one. */
+ WAIT (!trans->active_fifo || !trans->active_fifo->readers,
+ EWOULDBLOCK);
+ if (!err && trans->active_fifo == NULL)
+ /* No other readers, and indeed, no fifo; make one. */
+ err = pipe_create (trans->fifo_pipe_class, &trans->active_fifo);
+ if (!err)
+ {
+ pipe_add_reader (trans->active_fifo);
+ pthread_cond_broadcast (&trans->active_fifo_changed);
+ /* We'll unlock ACTIVE_FIFO_LOCK below; the writer code won't
+ make us block because we've ensured that there's a reader
+ for it. */
+
+ if (trans->wait_for_writer)
+ /* Wait until there's a writer. */
+ {
+ WAIT (trans->active_fifo->writers, 0);
+ if (err)
+ /* Back out the new pipe creation. */
+ {
+ pipe_remove_reader (trans->active_fifo);
+ trans->active_fifo = NULL;
+ pthread_cond_broadcast (&trans->active_fifo_changed);
+ }
+ }
+ else
+ /* Otherwise prevent an immediate eof. */
+ trans->active_fifo->flags &= ~PIPE_BROKEN;
+ }
+ }
+
+ if (!err && (flags & O_WRITE))
+ /* Open the trans->active_fifo for writing. If WAIT_FOR_READER is
+ true, then we block until there's someone to read what we wrote,
+ otherwise, if there's no fifo, we create one, which we just write
+ into and leave it for someone to read later. */
+ {
+ if (trans->wait_for_reader)
+ /* Wait until there's a fifo to write to. */
+ WAIT (trans->active_fifo && trans->active_fifo->readers, 0);
+ if (!err && trans->active_fifo == NULL)
+ /* No other readers, and indeed, no fifo; make one. */
+ {
+ err = pipe_create (trans->fifo_pipe_class, &trans->active_fifo);
+ if (!err)
+ trans->active_fifo->flags &= ~PIPE_BROKEN;
+ }
+ if (!err)
+ {
+ pipe_add_writer (trans->active_fifo);
+ pthread_cond_broadcast (&trans->active_fifo_changed);
+ }
+ }
+
+ *hook = trans->active_fifo;
+ }
+
+ pthread_mutex_unlock (&trans->active_fifo_lock);
+
+ return err;
+}
+
+static void
+fifo_trans_close (struct fifo_trans *trans, struct trivfs_peropen *po)
+{
+ int was_active, going_away = 0;
+ int flags = po->openmodes;
+ struct pipe *pipe = po->hook;
+
+ if (!pipe)
+ return;
+
+ pthread_mutex_lock (&trans->active_fifo_lock);
+ was_active = (trans->active_fifo == pipe);
+
+ if (was_active)
+ /* We're the last reader; when we're gone there is no more joy. */
+ going_away = ((flags & O_READ) && pipe->readers == 1);
+ else
+ /* Let others have their fun. */
+ pthread_mutex_unlock (&trans->active_fifo_lock);
+
+ if (flags & O_READ)
+ pipe_remove_reader (pipe);
+ if (flags & O_WRITE)
+ pipe_remove_writer (pipe);
+ /* At this point, PIPE may be gone, so we can't look at it again. */
+
+ if (was_active)
+ {
+ if (going_away)
+ trans->active_fifo = NULL;
+ pthread_cond_broadcast (&trans->active_fifo_changed);
+ pthread_mutex_unlock (&trans->active_fifo_lock);
+ }
+}
+
+static error_t
+open_hook (struct trivfs_peropen *po)
+{
+ struct fifo_trans *trans = po->cntl->hook;
+
+ if (! trans->server)
+ /* We're opening a normal fifo node. */
+ return fifo_trans_open (trans, po->openmodes, &po->hook);
+ else if (po->openmodes & (O_READ|O_WRITE|O_APPEND))
+ return EPERM;
+ else
+ /* We're a fifo server serving a new fifo. */
+ return 0;
+}
+
+static void
+close_hook (struct trivfs_peropen *po)
+{
+ struct fifo_trans *trans = po->cntl->hook;
+
+ if (! trans->server)
+ /* We're closing a normal fifo node. */
+ fifo_trans_close (trans, po);
+}
+
+/* Trivfs hooks */
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 1;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = O_READ | O_WRITE;
+
+error_t (*trivfs_peropen_create_hook) (struct trivfs_peropen *) = open_hook;
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ struct fifo_trans *trans = cred->po->cntl->hook;
+ if (! trans->server)
+ /* A fifo node */
+ {
+ struct pipe *pipe = cred->po->hook;
+
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= S_IFIFO;
+
+ if (pipe)
+ {
+ pthread_mutex_lock (&pipe->lock);
+ st->st_size = pipe_readable (pipe, 1);
+ st->st_blocks = st->st_size >> 9;
+ pthread_mutex_unlock (&pipe->lock);
+ }
+ else
+ st->st_size = st->st_blocks = 0;
+
+ /* As we try to be clever with large transfers, ask for them. */
+ st->st_blksize = vm_page_size * 16;
+ }
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ error_t err;
+ int num_opens;
+ int force = flags & FSYS_GOAWAY_FORCE;
+ int unlink = flags & FSYS_GOAWAY_UNLINK;
+ struct fifo_trans *trans = fsys->hook;
+
+ err = ports_inhibit_port_rpcs (fsys);
+ if (err == EINTR || (err && !force))
+ return err;
+
+ num_opens = ports_count_class (fsys->protid_class);
+ if (num_opens > 0 && !force && !unlink)
+ /* Still some opens, and we're not being forced to go away, so don't.*/
+ {
+ ports_enable_class (fsys->protid_class);
+ ports_resume_port_rpcs (fsys);
+ return EBUSY;
+ }
+
+ /* Kill the control connection. */
+ mach_port_deallocate (mach_task_self (), fsys->underlying);
+ fsys->underlying = MACH_PORT_NULL;
+ ports_destroy_right (fsys);
+
+ if (force)
+ /* Kill opens. */
+ {
+ error_t maybe_trash_protid (void *vcred)
+ {
+ struct trivfs_protid *cred = vcred;
+ if (cred->po->cntl == fsys)
+ {
+ ports_destroy_right (cred);
+ ports_interrupt_rpcs (cred);
+ }
+ return 0;
+ }
+ ports_bucket_iterate (((struct port_info *)fsys)->bucket,
+ maybe_trash_protid);
+ }
+
+ if (! trans->parent)
+ /* The root translator, go away, bye bye. */
+ exit (0);
+
+ /* Let things continue; what should die will. */
+ ports_enable_class (fsys->protid_class);
+ ports_resume_port_rpcs (fsys);
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Return objects mapping the data underlying this memory object. If
+ the object can be read then memobjrd will be provided; if the
+ object can be written then memobjwr will be provided. For objects
+ where read data and write data are the same, these objects will be
+ equal, otherwise they will be disjoint. Servers are permitted to
+ implement io_map but not io_map_cntl. Some objects do not provide
+ mapping; they will set none of the ports and return an error. Such
+ objects can still be accessed by io_read and io_write. */
+error_t
+trivfs_S_io_map (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ memory_object_t *rdobj,
+ mach_msg_type_name_t *rdtype,
+ memory_object_t *wrobj,
+ mach_msg_type_name_t *wrtype)
+{
+ return EOPNOTSUPP;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Read data from an IO object. If offset if -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMT. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, size_t *data_len,
+ off_t offs, size_t amount)
+{
+ error_t err;
+
+ if (!cred)
+ err = EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_READ))
+ err = EBADF;
+ else
+ {
+ struct pipe *pipe = cred->po->hook;
+ assert (pipe);
+ pthread_mutex_lock (&pipe->lock);
+ err = pipe_read (pipe, cred->po->openmodes & O_NONBLOCK, NULL,
+ data, data_len, amount);
+ pthread_mutex_unlock (&pipe->lock);
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+error_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ size_t *amount)
+{
+ error_t err;
+
+ if (!cred)
+ err = EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_READ))
+ err = EBADF;
+ else
+ {
+ struct pipe *pipe = cred->po->hook;
+ assert (pipe);
+ pthread_mutex_lock (&pipe->lock);
+ *amount = pipe_readable (pipe, 1);
+ pthread_mutex_unlock (&pipe->lock);
+ err = 0;
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offset, int whence, off_t *new_offset)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return ESPIPE;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. ID_TAG is returned as passed; it
+ is just for the convenience of the user in matching up reply messages with
+ specific requests sent. */
+static error_t
+io_select_common (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec *tsp, int *select_type)
+{
+ struct pipe *pipe;
+ error_t err = 0;
+ int ready = 0;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pipe = cred->po->hook;
+
+ if (*select_type & SELECT_READ)
+ {
+ if (cred->po->openmodes & O_READ)
+ {
+ pthread_mutex_lock (&pipe->lock);
+ err = pipe_wait_readable (pipe, 1, 1);
+ if (err == EWOULDBLOCK)
+ err = 0; /* Not readable, actually not an error. */
+ else
+ ready |= SELECT_READ; /* Data immediately readable (or error). */
+ pthread_mutex_unlock (&pipe->lock);
+ }
+ else
+ {
+ err = EBADF;
+ ready |= SELECT_READ; /* Error immediately available... */
+ }
+ if (err)
+ /* Prevent write test from overwriting err. */
+ *select_type &= ~SELECT_WRITE;
+ }
+
+ if (*select_type & SELECT_WRITE)
+ {
+ if (cred->po->openmodes & O_WRITE)
+ {
+ pthread_mutex_lock (&pipe->lock);
+ err = pipe_wait_writable (pipe, 1);
+ if (err == EWOULDBLOCK)
+ err = 0; /* Not writable, actually not an error. */
+ else
+ ready |= SELECT_WRITE; /* Data immediately writable (or error). */
+ pthread_mutex_unlock (&pipe->lock);
+ }
+ else
+ {
+ err = EBADF;
+ ready |= SELECT_WRITE; /* Error immediately available... */
+ }
+ }
+
+ if (ready)
+ *select_type = ready;
+ else
+ /* Wait for something to change. */
+ {
+ ports_interrupt_self_on_port_death (cred, reply);
+ err = pipe_pair_select (pipe, pipe, tsp, select_type, 1);
+ }
+
+ return err;
+}
+
+error_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *select_type)
+{
+ return io_select_common (cred, reply, reply_type, NULL, select_type);
+}
+
+error_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec ts,
+ int *select_type)
+{
+ return io_select_common (cred, reply, reply_type, &ts, select_type);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Write data to an IO object. If offset is -1, write at the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount successfully written is returned in amount. A
+ given user should not have more than one outstanding io_write on an
+ object at a time; servers implement congestion control by delaying
+ responses to io_write. Servers may drop data (returning ENOBUFS)
+ if they recevie more than one write when not prepared for it. */
+error_t
+trivfs_S_io_write (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *data, size_t data_len,
+ off_t offs, size_t *amount)
+{
+ error_t err;
+
+ if (!cred)
+ err = EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_WRITE))
+ err = EBADF;
+ else
+ {
+ struct pipe *pipe = cred->po->hook;
+ pthread_mutex_lock (&pipe->lock);
+ err = pipe_write (pipe, cred->po->openmodes & O_NONBLOCK, NULL,
+ data, data_len, amount);
+ pthread_mutex_unlock (&pipe->lock);
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Truncate file. */
+error_t
+trivfs_S_file_set_size (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t size)
+{
+ return size == 0 ? 0 : EINVAL;
+}
+
+/* ---------------------------------------------------------------- */
+/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and
+ O_NONBLOCK bits for the IO object. In addition, io_get_openmodes
+ will tell you which of O_READ, O_WRITE, and O_EXEC the object can
+ be used for. The O_ASYNC bit affects icky async I/O; good async
+ I/O is done through io_async which is orthogonal to these calls. */
+
+error_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+ *bits = cred->po->openmodes;
+ return 0;
+ }
+}
+
+error_t
+trivfs_S_io_set_all_openmodes(struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int mode)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+/* Get/set the owner of the IO object. For terminals, this affects
+ controlling terminal behavior (see term_become_ctty). For all
+ objects this affects old-style async IO. Negative values represent
+ pgrps. This has nothing to do with the owner of a file (as
+ returned by io_stat, and as used for various permission checks by
+ filesystems). An owner of 0 indicates that there is no owner. */
+
+error_t
+trivfs_S_io_get_owner (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ pid_t *owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ *owner = 0;
+ return 0;
+}
+
+error_t
+trivfs_S_io_mod_owner (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ pid_t owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return EINVAL;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Ask SERVER to provide fsys translation service for us. REQUESTOR is
+ the bootstrap port supplied to the original translator, and ARGV are
+ the command line arguments. If the recipient accepts the request, he
+ (or some delegate) should send fsys_startup to REQUESTOR to start the
+ filesystem up. */
+error_t
+trivfs_S_fsys_forward (mach_port_t server,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t requestor,
+ char *argz, size_t argz_len)
+{
+ error_t err;
+ struct fifo_trans *server_trans, *trans;
+ int argc = argz_count (argz, argz_len);
+ char **argv = alloca (sizeof (char *) * (argc + 1));
+ /* SERVER should be our root node. */
+ struct trivfs_protid *cred =
+ ports_lookup_port (port_bucket, server, server_port_class);
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ server_trans = cred->po->cntl->hook;
+ assert (server_trans->server);
+
+ argz_extract (argz, argz_len, argv);
+
+ /* Make a new translator, inheriting from its server. */
+ fifo_trans_create (server_trans, &trans);
+
+ /* Parse the new arguments to change the defaults. */
+ err = fifo_trans_parse_args (trans, argc, argv, 0);
+
+ if (!err)
+ /* Set our new translator along it's merry way... */
+ fifo_trans_start (trans, requestor);
+
+ ports_port_deref (cred);
+
+ return err;
+}
diff --git a/trans/null.c b/trans/null.c
new file mode 100644
index 00000000..bd082dc8
--- /dev/null
+++ b/trans/null.c
@@ -0,0 +1,340 @@
+/* A translator for providing endless empty space and immediate eof.
+
+ Copyright (C) 1995,96,97,98,99,2001,02,03 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <hurd/fsys.h>
+#include <version.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <error.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <argp.h>
+#include <nullauth.h>
+
+#include "libtrivfs/trivfs_fs_S.h"
+#include "libtrivfs/trivfs_io_S.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (null);
+
+/* Error code to return for write attempts.
+ If zero, they succeed in writing to the bitbucket. */
+static error_t write_error_code;
+
+static const struct argp_option options[] =
+{
+ {"full", 'f', 0, 0, "Cause writes to fail as if to a full disk"},
+ {0}
+};
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ switch (opt)
+ {
+ case 'f':
+ write_error_code = ENOSPC;
+ return 0;
+ }
+ return ARGP_ERR_UNKNOWN;
+}
+
+static const struct argp argp =
+{ options, parse_opt, 0, "Endless sink and null source" };
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error(1, 0, "Must be started as a translator");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error(3, err, "Contacting parent");
+
+ err = setnullauth ();
+ if (err)
+ error(4, err, "Dropping privileges");
+
+ /* Launch. */
+ ports_manage_port_operations_multithread (fsys->pi.bucket, trivfs_demuxer,
+ 2 * 60 * 1000, 0, 0);
+
+ return 0;
+}
+
+/* Trivfs hooks */
+
+int trivfs_fstype = FSTYPE_DEV;
+int trivfs_fsid = 0;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 1;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = O_READ | O_WRITE;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ st->st_blksize = vm_page_size * 256; /* Make transfers LARRRRRGE */
+
+ st->st_size = 0;
+ st->st_blocks = 0;
+
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= S_IFCHR;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ exit (0);
+}
+
+/* Return objects mapping the data underlying this memory object. If
+ the object can be read then memobjrd will be provided; if the
+ object can be written then memobjwr will be provided. For objects
+ where read data and write data are the same, these objects will be
+ equal, otherwise they will be disjoint. Servers are permitted to
+ implement io_map but not io_map_cntl. Some objects do not provide
+ mapping; they will set none of the ports and return an error. Such
+ objects can still be accessed by io_read and io_write. */
+kern_return_t
+trivfs_S_io_map (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ memory_object_t *rdobj,
+ mach_msg_type_name_t *rdtype,
+ memory_object_t *wrobj,
+ mach_msg_type_name_t *wrtype)
+{
+ return EOPNOTSUPP; /* XXX should work! */
+}
+
+/* Read data from an IO object. If offset if -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMT. */
+kern_return_t
+trivfs_S_io_read(struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ char **data,
+ mach_msg_type_number_t *datalen,
+ loff_t offs,
+ mach_msg_type_number_t amt)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_READ))
+ return EBADF;
+ else
+ {
+ *datalen = 0;
+ return 0;
+ }
+}
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+kern_return_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ mach_msg_type_number_t *amount)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_READ))
+ return EINVAL;
+ else
+ *amount = 0;
+ return 0;
+}
+
+/* Change current read/write offset */
+kern_return_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ off_t offset, int whence, off_t *new_offset)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ *new_offset = 0;
+ return 0;
+}
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. ID_TAG is returned as passed; it
+ is just for the convenience of the user in matching up reply messages with
+ specific requests sent. */
+kern_return_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int *type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (((*type & SELECT_READ) && !(cred->po->openmodes & O_READ))
+ || ((*type & SELECT_WRITE) && !(cred->po->openmodes & O_WRITE)))
+ return EBADF;
+ else
+ *type &= ~SELECT_URG;
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ struct timespec ts,
+ int *type)
+{
+ return trivfs_S_io_select (cred, reply, replytype, type);
+}
+
+/* Write data to an IO object. If offset is -1, write at the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount successfully written is returned in amount. A
+ given user should not have more than one outstanding io_write on an
+ object at a time; servers implement congestion control by delaying
+ responses to io_write. Servers may drop data (returning ENOBUFS)
+ if they recevie more than one write when not prepared for it. */
+kern_return_t
+trivfs_S_io_write (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ char *data, mach_msg_type_number_t datalen,
+ loff_t offs, mach_msg_type_number_t *amt)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_WRITE))
+ return EBADF;
+ *amt = datalen;
+ return write_error_code;
+}
+
+/* Truncate file. */
+kern_return_t
+trivfs_S_file_set_size (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ loff_t size)
+{
+ if (size < 0)
+ return EINVAL;
+ return 0;
+}
+
+/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and
+ O_NONBLOCK bits for the IO object. In addition, io_get_openmodes
+ will tell you which of O_READ, O_WRITE, and O_EXEC the object can
+ be used for. The O_ASYNC bit affects icky async I/O; good async
+ I/O is done through io_async which is orthogonal to these calls. */
+
+kern_return_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+ *bits = cred->po->openmodes;
+ return 0;
+ }
+}
+
+error_t
+trivfs_S_io_set_all_openmodes(struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int mode)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+/* Get/set the owner of the IO object. For terminals, this affects
+ controlling terminal behavior (see term_become_ctty). For all
+ objects this affects old-style async IO. Negative values represent
+ pgrps. This has nothing to do with the owner of a file (as
+ returned by io_stat, and as used for various permission checks by
+ filesystems). An owner of 0 indicates that there is no owner. */
+
+kern_return_t
+trivfs_S_io_get_owner (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ pid_t *owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ *owner = 0;
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_mod_owner (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ pid_t owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return EINVAL;
+}
diff --git a/trans/password.c b/trans/password.c
new file mode 100644
index 00000000..344b78ba
--- /dev/null
+++ b/trans/password.c
@@ -0,0 +1,230 @@
+/* Hurd standard password server.
+ Copyright (C) 1999, 2013 Free Software Foundation
+ Written by Mark Kettenis.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <argp.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <hurd.h>
+#include <hurd/auth.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+
+#include <ugids.h>
+#include <version.h>
+
+#include "password_S.h"
+
+
+const char *argp_program_version = STANDARD_HURD_VERSION (password);
+
+/* Port bucket we service requests on. */
+struct port_bucket *port_bucket;
+
+/* Trivfs hooks. */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = 0;
+
+struct port_class *trivfs_protid_portclasses[1];
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_protid_nportclasses = 1;
+int trivfs_cntl_nportclasses = 1;
+
+
+static int
+password_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ extern int password_server (mach_msg_header_t *inp, mach_msg_header_t *outp);
+ return password_server (inp, outp) || trivfs_demuxer (inp, outp);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+ const struct argp argp = { 0, 0, 0, "Hurd standard password server." };
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "must be started as a translator");
+
+ port_bucket = ports_create_bucket ();
+ trivfs_cntl_portclasses[0] = ports_create_class (trivfs_clean_cntl, 0);
+ trivfs_protid_portclasses[0] = ports_create_class (trivfs_clean_protid, 0);
+
+ /* Reply to our parent. */
+ err = trivfs_startup (bootstrap, 0,
+ trivfs_cntl_portclasses[0], port_bucket,
+ trivfs_protid_portclasses[0], port_bucket,
+ &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (3, err, "Contacting parent");
+
+ /* Launch. */
+ do
+ ports_manage_port_operations_multithread (port_bucket, password_demuxer,
+ 2 * 60 * 1000,
+ 10 * 60 * 1000,
+ 0);
+ /* That returns when 10 minutes pass without an RPC. Try shutting down
+ as if sent fsys_goaway; if we have any users who need us to stay
+ around, this returns EBUSY and we loop to service more RPCs. */
+ while (trivfs_goaway (fsys, 0));
+
+ return 0;
+}
+
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ int count;
+
+ /* Stop new requests. */
+ ports_inhibit_class_rpcs (trivfs_cntl_portclasses[0]);
+ ports_inhibit_class_rpcs (trivfs_protid_portclasses[0]);
+
+ /* Are there any extant user ports for the /servers/password file? */
+ count = ports_count_class (trivfs_protid_portclasses[0]);
+ if (count > 0 && !(flags & FSYS_GOAWAY_FORCE))
+ {
+ /* We won't go away, so start things going again... */
+ ports_enable_class (trivfs_protid_portclasses[0]);
+ ports_resume_class_rpcs (trivfs_cntl_portclasses[0]);
+ ports_resume_class_rpcs (trivfs_protid_portclasses[0]);
+
+ return EBUSY;
+ }
+
+ exit (0);
+}
+
+
+/* Implement password_check_user as described in <hurd/password.defs>. */
+kern_return_t
+S_password_check_user (struct trivfs_protid *cred, uid_t user, char *pw,
+ mach_port_t *port, mach_msg_type_name_t *port_type)
+{
+ struct ugids ugids = UGIDS_INIT;
+ auth_t auth;
+ error_t err;
+
+ char *getpass (const char *prompt, uid_t id, int is_group,
+ void *pwd_or_group, void *hook)
+ {
+ assert (! is_group && id == user);
+ return strdup (pw);
+ }
+
+ if (! cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.bucket != port_bucket ||
+ cred->pi.class != trivfs_protid_portclasses[0])
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ /* Verify password. */
+ err = ugids_add_user (&ugids, user, 1);
+ if (!err)
+ err = ugids_verify (&ugids, 0, 0, getpass, 0, 0, 0);
+
+ if (!err)
+ {
+ auth = getauth ();
+ err = auth_makeauth (auth, 0, MACH_MSG_TYPE_COPY_SEND, 0,
+ ugids.eff_uids.ids, ugids.eff_uids.num,
+ ugids.avail_uids.ids, ugids.avail_uids.num,
+ ugids.eff_gids.ids, ugids.eff_gids.num,
+ ugids.avail_gids.ids, ugids.avail_gids.num,
+ port);
+ mach_port_deallocate (mach_task_self (), auth);
+ *port_type = MACH_MSG_TYPE_MOVE_SEND;
+ }
+
+ ugids_fini (&ugids);
+ return err;
+}
+
+/* Implement password_check_group as described in <hurd/password.defs>. */
+kern_return_t
+S_password_check_group (struct trivfs_protid *cred, uid_t group, char *pw,
+ mach_port_t *port, mach_msg_type_name_t *port_type)
+{
+ struct ugids ugids = UGIDS_INIT;
+ auth_t auth;
+ error_t err;
+
+ char *getpass (const char *prompt, uid_t id, int is_group,
+ void *pwd_or_group, void *hook)
+ {
+ assert (is_group && id == group);
+ return strdup (pw);
+ }
+
+ if (! cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.bucket != port_bucket ||
+ cred->pi.class != trivfs_protid_portclasses[0])
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ /* Verify password. */
+ err = ugids_add_gid (&ugids, group, 1);
+ if (!err)
+ err = ugids_verify (&ugids, 0, 0, getpass, 0, 0, 0);
+
+ if (!err)
+ {
+ auth = getauth ();
+ err = auth_makeauth (auth, 0, MACH_MSG_TYPE_COPY_SEND, 0,
+ ugids.eff_uids.ids, ugids.eff_uids.num,
+ ugids.avail_uids.ids, ugids.avail_uids.num,
+ ugids.eff_gids.ids, ugids.eff_gids.num,
+ ugids.avail_gids.ids, ugids.avail_gids.num,
+ port);
+ mach_port_deallocate (mach_task_self (), auth);
+ *port_type = MACH_MSG_TYPE_MOVE_SEND;
+ }
+
+ ugids_fini (&ugids);
+ return err;
+}
diff --git a/trans/proxy-defpager.c b/trans/proxy-defpager.c
new file mode 100644
index 00000000..98176577
--- /dev/null
+++ b/trans/proxy-defpager.c
@@ -0,0 +1,279 @@
+/* A translator for providing access to Mach default_pager.defs control calls
+
+ Copyright (C) 2002, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd/trivfs.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <argp.h>
+#include <error.h>
+#include <version.h>
+#include <hurd/paths.h>
+
+#include "libtrivfs/trivfs_io_S.h"
+#include "default_pager_S.h"
+#include "default_pager_U.h"
+
+static mach_port_t real_defpager, dev_master;
+
+static error_t
+allowed (mach_port_t port, int mode)
+{
+ struct trivfs_protid *cred = ports_lookup_port
+ (0, port, trivfs_protid_portclasses[0]);
+ if (!cred)
+ return MIG_BAD_ID;
+ error_t result = (cred->po->openmodes & mode) ? 0 : EACCES;
+ ports_port_deref (cred);
+ return result;
+}
+
+kern_return_t
+S_default_pager_object_create (mach_port_t default_pager,
+ memory_object_t *memory_object,
+ vm_size_t object_size)
+{
+ return allowed (default_pager, O_EXEC)
+ ?: default_pager_object_create (real_defpager, memory_object, object_size);
+}
+
+kern_return_t
+S_default_pager_info (mach_port_t default_pager, default_pager_info_t *info)
+{
+ return allowed (default_pager, O_READ)
+ ?: default_pager_info (real_defpager, info);
+}
+
+kern_return_t
+S_default_pager_objects (mach_port_t default_pager,
+ default_pager_object_array_t *objects,
+ mach_msg_type_number_t *objectsCnt,
+ mach_port_array_t *ports,
+ mach_msg_type_number_t *portsCnt)
+{
+ return allowed (default_pager, O_WRITE)
+ ?: default_pager_objects (real_defpager,
+ objects, objectsCnt, ports, portsCnt);
+}
+
+kern_return_t
+S_default_pager_object_pages (mach_port_t default_pager,
+ mach_port_t memory_object,
+ default_pager_page_array_t *pages,
+ mach_msg_type_number_t *pagesCnt)
+{
+ return allowed (default_pager, O_WRITE)
+ ?: default_pager_object_pages (real_defpager, memory_object,
+ pages, pagesCnt);
+}
+
+
+kern_return_t
+S_default_pager_paging_file (mach_port_t default_pager,
+ mach_port_t master_device_port,
+ default_pager_filename_t filename,
+ boolean_t add)
+{
+ return allowed (default_pager, O_WRITE)
+ ?: default_pager_paging_file (real_defpager, dev_master, filename, add)
+ ?: mach_port_deallocate (mach_task_self (), master_device_port);
+}
+
+kern_return_t
+S_default_pager_paging_storage (mach_port_t default_pager,
+ mach_port_t device,
+ recnum_t *runs, mach_msg_type_number_t nruns,
+ default_pager_filename_t name,
+ boolean_t add)
+{
+ return allowed (default_pager, O_WRITE)
+ ?: default_pager_paging_storage (real_defpager, dev_master,
+ runs, nruns, name, add)
+ ?: mach_port_deallocate (mach_task_self (), device);
+}
+
+kern_return_t
+S_default_pager_object_set_size (mach_port_t memory_object,
+ mach_port_seqno_t seqno,
+ vm_size_t object_size_limit)
+{
+ /* This is sent to an object, not the control port. */
+ return MIG_BAD_ID;
+}
+
+
+/* Trivfs hooks */
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 1;
+int trivfs_support_exec = 1;
+
+int trivfs_allow_open = O_READ | O_WRITE | O_EXEC;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ st->st_blksize = vm_page_size * 256; /* Make transfers LARRRRRGE */
+
+ st->st_size = 0;
+ st->st_blocks = 0;
+
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= S_IFCHR;
+}
+
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ exit (0);
+}
+
+kern_return_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ char **data,
+ mach_msg_type_number_t *datalen,
+ loff_t offs,
+ mach_msg_type_number_t amt)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return EIO;
+}
+
+kern_return_t
+trivfs_S_io_write (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ char *data, mach_msg_type_number_t datalen,
+ loff_t offs, mach_msg_type_number_t *amt)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return EIO;
+}
+
+kern_return_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+ *bits = cred->po->openmodes;
+ return 0;
+ }
+}
+
+error_t
+trivfs_S_io_set_all_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int mode)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+const char *argp_program_version = STANDARD_HURD_VERSION (proxy-defpager);
+
+static const struct argp argp =
+{doc: "\
+Access to control interfaces of Mach default pager.\n\
+This translator should normally be set on " _SERVERS_DEFPAGER "."};
+
+int
+proxy_defpager_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ extern int default_pager_server (mach_msg_header_t *, mach_msg_header_t *);
+
+ return default_pager_server (inp, outp)
+ || trivfs_demuxer (inp, outp);
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+ mach_port_t host_priv;
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ err = get_privileged_ports (&host_priv, &dev_master);
+ if (err)
+ error (2, err, "cannot get privileged ports");
+ real_defpager = MACH_PORT_NULL;
+ err = vm_set_default_memory_manager (host_priv, &real_defpager);
+ mach_port_deallocate (mach_task_self (), host_priv);
+ if (err)
+ error (3, err, "vm_set_default_memory_manager");
+ if (real_defpager == MACH_PORT_NULL)
+ error (1, 0, "no default memory manager set!");
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "Must be started as a translator");
+
+ trivfs_fsid = getpid ();
+
+ /* Reply to our parent. */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (4, err, "Contacting parent");
+
+ /* Launch. */
+ ports_manage_port_operations_multithread (fsys->pi.bucket,
+ proxy_defpager_demuxer,
+ 2 * 60 * 1000, 0, 0);
+
+ return 0;
+}
diff --git a/trans/remap.c b/trans/remap.c
new file mode 100644
index 00000000..5ee01891
--- /dev/null
+++ b/trans/remap.c
@@ -0,0 +1,152 @@
+/* remap -- a translator for changing paths
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <hurd/trivfs.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <argp.h>
+#include <error.h>
+#include <string.h>
+
+#include <version.h>
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = 0;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ /* Don't care */
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+ exit (0);
+}
+
+struct remap
+{
+ char *from;
+ char *to;
+ struct remap *next;
+};
+
+static struct remap *remaps;
+
+error_t
+trivfs_S_dir_lookup (struct trivfs_protid *diruser,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *filename,
+ int flags,
+ mode_t mode,
+ retry_type *do_retry,
+ char *retry_name,
+ mach_port_t *retry_port,
+ mach_msg_type_name_t *retry_port_type)
+{
+ struct remap *remap;
+
+ if (!diruser)
+ return EOPNOTSUPP;
+
+ for (remap = remaps; remap; remap = remap->next)
+ if (!strcmp (remap->from, filename))
+ {
+#ifdef DEBUG
+ fprintf (stderr,"replacing %s with %s\n", remap->from, remap->to);
+ fflush (stderr);
+#endif
+ filename = remap->to;
+ break;
+ }
+
+ *do_retry = FS_RETRY_REAUTH;
+ *retry_port = getcrdir ();
+ *retry_port_type = MACH_MSG_TYPE_COPY_SEND;
+ strcpy (retry_name, filename);
+
+ return 0;
+}
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ static char *remap_from;
+
+ switch (key)
+ {
+ case ARGP_KEY_ARG:
+
+ /* Skip heading slashes */
+ while (arg[0] == '/')
+ arg++;
+
+ if (!remap_from)
+ /* First of a pair */
+ remap_from = strdup (arg);
+ else
+ {
+ /* Second of a pair */
+ struct remap *remap = malloc (sizeof (*remap));
+ remap->from = remap_from;
+ remap->to = strdup (arg);
+ remap->next = remaps;
+#ifdef DEBUG
+ fprintf (stderr, "adding remap %s->%s\n", remap->from, remap->to);
+#endif
+ remaps = remap;
+ remap_from = NULL;
+ }
+
+ break;
+ }
+ return 0;
+}
+
+const char *argp_program_version = STANDARD_HURD_VERSION (fakeroot);
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+
+ struct argp argp = { NULL, parse_opt, "[ FROM1 TO1 [ FROM2 TO2 [ ... ] ] ]", "\
+A translator for remapping directories.\v\
+This translator is to be used as a chroot, within which paths point to the\
+same files as the original root, except a given set of paths, which are\
+remapped to given paths." };
+
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ struct trivfs_control *fsys;
+
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
+ if (err)
+ error (1, err, "trivfs_startup failed");
+ ports_manage_port_operations_one_thread (fsys->pi.bucket, trivfs_demuxer, 0);
+
+ /*NOTREACHED*/
+ return 0;
+}
diff --git a/trans/streamio.c b/trans/streamio.c
new file mode 100644
index 00000000..54627b73
--- /dev/null
+++ b/trans/streamio.c
@@ -0,0 +1,1189 @@
+/* A translator for handling stream devices.
+
+ Copyright (C) 2001,02 Free Software Foundation, Inc.
+
+ Written by OKUJI Yoshinori <okuji@kuicr.kyoto-u.ac.jp>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <argp.h>
+#include <error.h>
+
+#include <mach.h>
+#include <device/device.h>
+#include <device/device_request.h>
+
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <version.h>
+
+#include "libtrivfs/trivfs_fs_S.h"
+#include "libtrivfs/trivfs_io_S.h"
+
+/* The global lock */
+pthread_mutex_t global_lock;
+
+/* Wakeup when device_open is finished */
+pthread_cond_t open_alert;
+
+/* Wakeup for select */
+pthread_cond_t select_alert;
+
+/* Bucket for all out ports */
+struct port_bucket *streamdev_bucket;
+
+/* The buffers we use */
+struct buffer *input_buffer, *output_buffer;
+
+
+/* Information about a buffer. */
+struct buffer
+{
+ /* Point to the head of the buffer. */
+ char *head;
+ /* Point to the tail of the buffer. */
+ char *tail;
+ /* The buffer array size. */
+ size_t size;
+ /* Wakeup when the buffer is not empty or not full. */
+ pthread_cond_t *wait;
+ /* The buffer. */
+ char buf[0];
+};
+
+/* Create a new buffer structure with SIZE, returning the pointer. */
+static inline struct buffer *
+create_buffer (size_t size)
+{
+ struct buffer *new = malloc (sizeof (struct buffer) + size);
+ assert (new);
+ new->head = new->tail = new->buf;
+ new->size = size;
+ new->wait = malloc (sizeof (pthread_cond_t));
+ assert (new->wait);
+ pthread_cond_init (new->wait, NULL);
+ return new;
+}
+
+/* Return the size of B. */
+static inline size_t
+buffer_size (struct buffer *b)
+{
+ return b->tail - b->head;
+}
+
+/* Return how much characters can be read from B. */
+static inline size_t
+buffer_readable (struct buffer *b)
+{
+ return buffer_size (b);
+}
+
+/* Return how much characters can be written to B. */
+static inline size_t
+buffer_writable (struct buffer *b)
+{
+ return b->size - buffer_size (b);
+}
+
+/* Flush B. */
+static inline void
+clear_buffer (struct buffer *b)
+{
+ if (b == 0)
+ return;
+ b->head = b->tail = b->buf;
+ pthread_cond_broadcast (b->wait);
+ pthread_cond_broadcast (&select_alert);
+}
+
+/* Read up to LEN bytes from B to DATA, returning the amount actually read. */
+static inline size_t
+buffer_read (struct buffer *b, void *data, size_t len)
+{
+ size_t max = buffer_size (b);
+
+ if (len > max)
+ len = max;
+
+ memcpy (data, b->head, len);
+ b->head += len;
+
+ if (b->head > b->buf + b->size / 2)
+ {
+ size_t size = buffer_size (b);
+
+ memmove (b->buf, b->head, size);
+ b->head = b->buf;
+ b->tail = b->buf + size;
+ }
+
+ pthread_cond_broadcast (b->wait);
+ pthread_cond_broadcast (&select_alert);
+ return len;
+}
+
+/* Write LEN bytes from DATA to B, returning the amount actually written. */
+static inline size_t
+buffer_write (struct buffer *b, void *data, size_t len)
+{
+ size_t size = buffer_writable (b);
+
+ if (len > size)
+ len = size;
+
+ memcpy (b->tail, data, len);
+ b->tail += len;
+
+ pthread_cond_broadcast (b->wait);
+ pthread_cond_broadcast (&select_alert);
+ return len;
+}
+
+
+/* Open a new device structure for the device NAME with MODE. If an error
+ occurs, the error code is returned, otherwise 0. */
+error_t dev_open (const char *name, dev_mode_t mode);
+
+/* Check if the device is already opened. */
+int dev_already_opened (void);
+
+/* Close the device. */
+void dev_close (void);
+
+/* Read up to AMOUNT bytes, returned in BUF and LEN. If NOWAIT is non-zero
+ and the buffer is empty, then returns EWOULDBLOCK. If an error occurs,
+ the error code is returned, otherwise 0. */
+error_t dev_read (size_t amount, void **buf, size_t *len, int nowait);
+
+/* Return current readable size in AMOUNT. If an error occurs, the error
+ code is returned, otherwise 0. */
+error_t dev_readable (size_t *amount);
+
+/* Write LEN bytes from BUF, returning the amount actually written
+ in AMOUNT. If NOWAIT is non-zero and the buffer is full, then returns
+ EWOULDBLOCK. If an error occurs, the error code is returned,
+ otherwise 0. */
+error_t dev_write (void *buf, size_t len, size_t *amount, int nowait);
+
+/* Try and write out any pending writes to the device. If WAIT is non-zero,
+ will wait for any activity to cease. */
+error_t dev_sync (int wait);
+
+
+
+static struct argp_option options[] =
+{
+ {"rdev", 'n', "ID", 0,
+ "The stat rdev number for this node; may be either a"
+ " single integer, or of the form MAJOR,MINOR"},
+ {"readonly", 'r', 0, 0, "Disallow writing"},
+ {"rdonly", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"ro", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"writable", 'w', 0, 0, "Allow writing"},
+ {"rdwr", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"rw", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"writeonly", 'W',0, 0, "Disallow reading"},
+ {"wronly", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {0}
+};
+
+static const char args_doc[] = "DEVICE";
+static const char doc[] = "Translator for stream devices.";
+
+const char *argp_program_version = STANDARD_HURD_VERSION (streamio);
+
+
+static char *stream_name;
+static int rdev;
+static int nperopens;
+
+/* Parse a single option. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'r':
+ trivfs_allow_open = O_READ;
+ break;
+ case 'w':
+ trivfs_allow_open = O_RDWR;
+ break;
+ case 'W':
+ trivfs_allow_open = O_WRITE;
+ break;
+
+ case 'n':
+ {
+ char *start = arg;
+ char *end;
+
+ rdev = strtoul (start, &end, 0);
+ if (*end == ',')
+ /* MAJOR,MINOR form */
+ {
+ start = end + 1;
+ rdev = (rdev << 8) + strtoul (start, &end, 0);
+ }
+
+ if (end == start || *end != '\0')
+ {
+ argp_error (state, "%s: Invalid argument to --rdev", arg);
+ return EINVAL;
+ }
+ }
+ break;
+
+ case ARGP_KEY_ARG:
+ stream_name = arg;
+ break;
+
+ case ARGP_KEY_END:
+ if (stream_name == 0)
+ argp_usage (state);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static const struct argp argp = { options, parse_opt, args_doc, doc };
+
+
+int
+demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ extern int device_reply_server (mach_msg_header_t *, mach_msg_header_t *);
+
+ return (trivfs_demuxer (inp, outp)
+ || device_reply_server (inp, outp));
+}
+
+int
+main (int argc, char *argv[])
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (2, 0, "Must be started as a translator");
+
+ streamdev_bucket = ports_create_bucket ();
+
+ err = trivfs_startup (bootstrap, 0,
+ 0, streamdev_bucket, 0, streamdev_bucket,
+ &fsys);
+ if (err)
+ error (3, err, "trivfs_startup");
+
+ pthread_mutex_init (&global_lock, NULL);
+
+ pthread_cond_init (&open_alert, NULL);
+ pthread_cond_init (&select_alert, NULL);
+
+ if (trivfs_allow_open & O_READ)
+ input_buffer = create_buffer (256);
+ if (trivfs_allow_open & O_WRITE)
+ output_buffer = create_buffer (256);
+
+ /* Launch */
+ ports_manage_port_operations_multithread (streamdev_bucket, demuxer,
+ 0, 0, 0);
+
+ return 0;
+}
+
+
+int trivfs_fstype = FSTYPE_DEV;
+int trivfs_fsid = 0;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 1;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = O_READ | O_WRITE;
+
+static error_t
+open_hook (struct trivfs_control *cntl, struct iouser *user, int flags)
+{
+ error_t err;
+ dev_mode_t mode;
+
+ if (flags & O_WRITE & ~trivfs_allow_open)
+ return EROFS;
+ if (flags & O_READ & ~trivfs_allow_open)
+ return EIO;
+
+ if ((flags & (O_READ|O_WRITE)) == 0)
+ return 0;
+
+ /* XXX */
+ if (flags & O_ASYNC)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ mode = 0;
+ if (flags & O_READ)
+ mode |= D_READ;
+ if (flags & O_WRITE)
+ mode |= D_WRITE;
+
+ if (!dev_already_opened ())
+ {
+ err = dev_open (stream_name, mode);
+ if (err)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return err;
+ }
+
+ if (!(flags & O_NONBLOCK))
+ {
+ if (pthread_hurd_cond_wait_np (&open_alert, &global_lock))
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EINTR;
+ }
+
+ if (!dev_already_opened ())
+ {
+ pthread_mutex_unlock (&global_lock);
+ return ENODEV;
+ }
+ }
+ }
+
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t (*trivfs_check_open_hook) (struct trivfs_control *,
+ struct iouser *, int)
+ = open_hook;
+
+static error_t
+po_create_hook (struct trivfs_peropen *po)
+{
+ pthread_mutex_lock (&global_lock);
+ nperopens++;
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t (*trivfs_peropen_create_hook) (struct trivfs_peropen *) =
+ po_create_hook;
+
+static void
+po_destroy_hook (struct trivfs_peropen *po)
+{
+ pthread_mutex_lock (&global_lock);
+ nperopens--;
+ if (!nperopens)
+ {
+ if (dev_already_opened ())
+ {
+ clear_buffer (input_buffer);
+ dev_close ();
+ }
+ }
+ pthread_mutex_unlock (&global_lock);
+}
+
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *)
+ = po_destroy_hook;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ st->st_blksize = vm_page_size;
+ st->st_size = 0;
+
+ st->st_rdev = rdev;
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= S_IFCHR;
+ if ((trivfs_allow_open & O_READ) == 0)
+ st->st_mode &= ~(S_IRUSR | S_IRGRP | S_IROTH);
+ if ((trivfs_allow_open & O_WRITE) == 0)
+ st->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ error_t err;
+ int force = (flags & FSYS_GOAWAY_FORCE);
+ int nosync = (flags & FSYS_GOAWAY_NOSYNC);
+ struct port_class *root_port_class = fsys->protid_class;
+
+ pthread_mutex_lock (&global_lock);
+
+ if (!dev_already_opened ())
+ exit (0);
+
+ err = ports_inhibit_class_rpcs (root_port_class);
+ if (err == EINTR || (err && !force))
+ {
+ pthread_mutex_unlock (&global_lock);
+ return err;
+ }
+
+ if (force && nosync)
+ exit (0);
+
+ if (!force && ports_count_class (root_port_class) > 0)
+ goto busy;
+
+ if (!nosync)
+ dev_close ();
+ exit (0);
+
+ busy:
+ ports_enable_class (root_port_class);
+ ports_resume_class_rpcs (root_port_class);
+ pthread_mutex_unlock (&global_lock);
+
+ return EBUSY;
+}
+
+
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, mach_msg_type_number_t *data_len,
+ loff_t offs, mach_msg_type_number_t amount)
+{
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (!(cred->po->openmodes & O_READ))
+ return EBADF;
+
+ pthread_mutex_lock (&global_lock);
+ err = dev_read (amount, (void **)data, data_len, cred->po->openmodes & O_NONBLOCK);
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+error_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mach_msg_type_number_t *amount)
+{
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (!(cred->po->openmodes & O_READ))
+ return EBADF;
+
+ pthread_mutex_lock (&global_lock);
+ err = dev_readable (amount);
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+error_t
+trivfs_S_io_write (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *data, mach_msg_type_number_t data_len,
+ loff_t offs, mach_msg_type_number_t *amount)
+{
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (!(cred->po->openmodes & O_WRITE))
+ return EBADF;
+
+ pthread_mutex_lock (&global_lock);
+ err = dev_write ((void *)data, data_len, amount, cred->po->openmodes & O_NONBLOCK);
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offs, int whence, off_t *new_offs)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return ESPIPE;
+}
+
+static error_t
+io_select_common (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec *tsp,
+ int *type)
+{
+ int available;
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (!(cred->po->openmodes & O_WRITE) && (*type & SELECT_WRITE))
+ return EBADF;
+
+ *type &= SELECT_READ | SELECT_WRITE;
+
+ if (*type == 0)
+ return 0;
+
+ available = 0;
+
+ while (1)
+ {
+ pthread_mutex_lock (&global_lock);
+ if ((*type & SELECT_READ) && buffer_readable (input_buffer))
+ available |= SELECT_READ;
+ if (output_buffer)
+ {
+ if ((*type & SELECT_WRITE) && buffer_writable (output_buffer))
+ available |= SELECT_WRITE;
+ }
+
+ if (available)
+ {
+ *type = available;
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ if (cred->po->openmodes & O_NONBLOCK)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EWOULDBLOCK;
+ }
+
+ ports_interrupt_self_on_port_death (cred, reply);
+ err = pthread_hurd_cond_timedwait_np (&select_alert, &global_lock, tsp);
+ if (err)
+ {
+ *type = 0;
+ pthread_mutex_unlock (&global_lock);
+
+ if (err == ETIMEDOUT)
+ err = 0;
+
+ return err;
+ }
+ }
+}
+
+error_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *type)
+{
+ return io_select_common (cred, reply, reply_type, NULL, type);
+}
+
+error_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec ts,
+ int *type)
+{
+ return io_select_common (cred, reply, reply_type, &ts, type);
+}
+
+error_t
+trivfs_S_file_set_size (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t size)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_WRITE))
+ return EBADF;
+ else if (size < 0)
+ return EINVAL;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *bits)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else
+ {
+ *bits = cred->po->openmodes;
+ return 0;
+ }
+}
+
+error_t
+trivfs_S_io_set_all_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int mode)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_file_sync (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int wait, int omit_metadata)
+{
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ err = dev_sync (wait);
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+error_t
+trivfs_S_file_syncfs (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int wait, int dochildren)
+{
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ err = dev_sync (wait);
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+
+/* This flag is set if there is an outstanding device_write. */
+static int output_pending;
+
+/* This flag is set if there is an outstanding device_read. */
+static int input_pending;
+
+/* This flag is set if there is an outstanding device_open. */
+static int open_pending;
+
+static char pending_output[IO_INBAND_MAX];
+static int npending_output;
+
+/* This flag is set if EOF is returned. */
+static int eof;
+
+/* The error number. */
+static error_t err;
+
+static struct port_class *phys_reply_class;
+
+/* The Mach device_t representing the stream. */
+static device_t phys_device = MACH_PORT_NULL;
+
+/* The ports we get replies on for device calls. */
+static mach_port_t phys_reply_writes = MACH_PORT_NULL;
+static mach_port_t phys_reply = MACH_PORT_NULL;
+
+/* The port-info structures. */
+static struct port_info *phys_reply_writes_pi;
+static struct port_info *phys_reply_pi;
+
+static device_t device_master;
+
+/* The block size and whole size of the device. */
+static size_t dev_blksize;
+static size_t dev_size;
+
+
+/* Open a new device structure for the device NAME with MODE. If an error
+ occurs, the error code is returned, otherwise 0. */
+/* Be careful that the global lock is already locked. */
+error_t
+dev_open (const char *name, dev_mode_t mode)
+{
+ if (open_pending || (phys_device != MACH_PORT_NULL))
+ return 0;
+
+ err = get_privileged_ports (0, &device_master);
+ if (err)
+ return err;
+
+ phys_reply_class = ports_create_class (0, 0);
+ err = ports_create_port (phys_reply_class, streamdev_bucket,
+ sizeof (struct port_info), &phys_reply_pi);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), device_master);
+ return err;
+ }
+
+ phys_reply = ports_get_right (phys_reply_pi);
+ mach_port_insert_right (mach_task_self (), phys_reply, phys_reply,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ if (output_buffer)
+ {
+ err = ports_create_port (phys_reply_class, streamdev_bucket,
+ sizeof (struct port_info),
+ &phys_reply_writes_pi);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ phys_reply = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_pi);
+ phys_reply_pi = 0;
+ mach_port_deallocate (mach_task_self (), device_master);
+ return err;
+ }
+
+ phys_reply_writes = ports_get_right (phys_reply_writes_pi);
+ mach_port_insert_right (mach_task_self (), phys_reply_writes,
+ phys_reply_writes, MACH_MSG_TYPE_MAKE_SEND);
+ }
+
+ err = device_open_request (device_master, phys_reply, mode, name);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ phys_reply = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_pi);
+ phys_reply_pi = 0;
+ if (output_buffer)
+ {
+ mach_port_deallocate (mach_task_self (), phys_reply_writes);
+ phys_reply_writes = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_writes_pi);
+ phys_reply_writes_pi = 0;
+ }
+ mach_port_deallocate (mach_task_self (), device_master);
+ return err;
+ }
+
+ open_pending = 1;
+ return 0;
+}
+
+kern_return_t
+device_open_reply (mach_port_t reply, int returncode, mach_port_t device)
+{
+ int sizes[DEV_GET_SIZE_COUNT];
+ size_t sizes_len = DEV_GET_SIZE_COUNT;
+ int amount;
+
+ if (reply != phys_reply)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ open_pending = 0;
+ pthread_cond_broadcast (&open_alert);
+
+ if (returncode != 0)
+ {
+ dev_close ();
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ phys_device = device;
+ eof = 0;
+
+ /* Get the block size and the whole size. */
+ err = device_get_status (device, DEV_GET_SIZE, sizes, &sizes_len);
+ if (err == D_INVALID_OPERATION)
+ {
+ /* XXX Assume that the block size is 1 and the whole size is 0. */
+ dev_blksize = 1;
+ dev_size = 0;
+ err = 0;
+ }
+ else if (err == 0)
+ {
+ assert (sizes_len == DEV_GET_SIZE_COUNT);
+
+ dev_blksize = sizes[DEV_GET_SIZE_RECORD_SIZE];
+ dev_size = sizes[DEV_GET_SIZE_DEVICE_SIZE];
+
+ assert (dev_blksize && dev_blksize <= IO_INBAND_MAX);
+ }
+ else
+ {
+ dev_close ();
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ amount = vm_page_size;
+ if (dev_blksize != 1)
+ amount = amount / dev_blksize * dev_blksize;
+
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+/* Check if the device is already opened. */
+/* Be careful that the global lock is already locked. */
+int
+dev_already_opened (void)
+{
+ return (phys_device != MACH_PORT_NULL);
+}
+
+/* Close the device. */
+/* Be careful that the global lock is already locked. */
+void
+dev_close (void)
+{
+ /* Sync all pending writes. */
+ dev_sync (1);
+
+ device_close (phys_device);
+ mach_port_deallocate (mach_task_self (), phys_device);
+ phys_device = MACH_PORT_NULL;
+
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ phys_reply = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_pi);
+ phys_reply_pi = 0;
+ clear_buffer (input_buffer);
+ input_pending = 0;
+
+ if (output_buffer)
+ {
+ mach_port_deallocate (mach_task_self (), phys_reply_writes);
+ phys_reply_writes = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_writes_pi);
+ phys_reply_writes_pi = 0;
+ clear_buffer (output_buffer);
+ npending_output = 0;
+ output_pending = 0;
+ }
+}
+
+/* Be careful that the global lock is already locked. */
+static error_t
+start_input (int nowait)
+{
+ int size;
+ error_t err;
+ size_t amount;
+
+ size = buffer_writable (input_buffer);
+
+ if (size < dev_blksize || input_pending)
+ return 0;
+
+ amount = vm_page_size;
+ if (dev_blksize != 1)
+ amount = amount / dev_blksize * dev_blksize;
+
+ err = device_read_request_inband (phys_device, phys_reply,
+ nowait? D_NOWAIT : 0,
+ 0, amount);
+ if (err == D_WOULD_BLOCK)
+ err = 0;
+ if (err)
+ dev_close ();
+ else
+ input_pending = 1;
+
+ return err;
+}
+
+/* Read up to AMOUNT bytes, returned in BUF and LEN. If NOWAIT is non-zero
+ and the buffer is empty, then returns EWOULDBLOCK. If an error occurs,
+ the error code is returned, otherwise 0. */
+/* Be careful that the global lock is already locked. */
+error_t
+dev_read (size_t amount, void **buf, size_t *len, int nowait)
+{
+ size_t max, avail;
+
+ if (err)
+ return err;
+
+ while (!buffer_readable (input_buffer))
+ {
+ err = start_input (nowait);
+ if (err)
+ return err;
+
+ if (eof)
+ {
+ *len = 0;
+ return 0;
+ }
+
+ if (nowait)
+ return EWOULDBLOCK;
+
+ if (pthread_hurd_cond_wait_np (input_buffer->wait, &global_lock))
+ return EINTR;
+ }
+
+ avail = buffer_size (input_buffer);
+ max = (amount < avail) ? amount : avail;
+ if (max > *len)
+ vm_allocate (mach_task_self (), (vm_address_t *)buf, max, 1);
+
+ *len = buffer_read (input_buffer, *buf, max);
+ assert (*len == max);
+
+ err = start_input (nowait);
+ return err;
+}
+
+error_t
+device_read_reply_inband (mach_port_t reply, error_t errorcode,
+ char *data, u_int datalen)
+{
+ if (reply != phys_reply)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ input_pending = 0;
+ err = errorcode;
+ if (!err)
+ {
+ if (datalen == 0)
+ {
+ eof = 1;
+ dev_close ();
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ while (datalen)
+ {
+ size_t nwritten;
+
+ while (!buffer_writable (input_buffer))
+ pthread_cond_wait (input_buffer->wait, &global_lock);
+
+ nwritten = buffer_write (input_buffer, data, datalen);
+ data += nwritten;
+ datalen -= nwritten;
+ pthread_cond_broadcast (input_buffer->wait);
+ pthread_cond_broadcast (&select_alert);
+ }
+ }
+ else
+ {
+ dev_close ();
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+ }
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+/* Return current readable size in AMOUNT. If an error occurs, the error
+ code is returned, otherwise 0. */
+/* Be careful that the global lock is already locked. */
+error_t
+dev_readable (size_t *amount)
+{
+ *amount = buffer_size (input_buffer);
+ return 0;
+}
+
+/* Be careful that the global lock is already locked. */
+static error_t
+start_output (int nowait)
+{
+ int size;
+
+ assert (output_buffer);
+
+ size = buffer_size (output_buffer);
+
+ if (size < dev_blksize || output_pending)
+ return 0;
+
+ if (size + npending_output > IO_INBAND_MAX)
+ size = IO_INBAND_MAX - npending_output;
+
+ if (dev_blksize != 1)
+ size = size / dev_blksize * dev_blksize;
+
+ buffer_read (output_buffer, pending_output + npending_output, size);
+ npending_output += size;
+
+ err = device_write_request_inband (phys_device, phys_reply_writes,
+ nowait? D_NOWAIT : 0,
+ 0, pending_output, npending_output);
+ if (err == D_WOULD_BLOCK)
+ err = 0;
+ if (err)
+ dev_close ();
+ else
+ output_pending = 1;
+
+ return err;
+}
+
+/* Write LEN bytes from BUF, returning the amount actually written
+ in AMOUNT. If NOWAIT is non-zero and the buffer is full, then returns
+ EWOULDBLOCK. If an error occurs, the error code is returned,
+ otherwise 0. */
+/* Be careful that the global lock is already locked. */
+error_t
+dev_write (void *buf, size_t len, size_t *amount, int nowait)
+{
+ if (err)
+ return err;
+
+ while (!buffer_writable (output_buffer))
+ {
+ err = start_output (nowait);
+ if (err)
+ return err;
+
+ if (nowait)
+ return EWOULDBLOCK;
+
+ if (pthread_hurd_cond_wait_np (output_buffer->wait, &global_lock))
+ return EINTR;
+ }
+
+ *amount = buffer_write (output_buffer, buf, len);
+ err = start_output (nowait);
+
+ return err;
+}
+
+error_t
+device_write_reply_inband (mach_port_t reply, error_t returncode, int amount)
+{
+ if (reply != phys_reply_writes)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ output_pending = 0;
+
+ if (!returncode)
+ {
+ if (amount >= npending_output)
+ {
+ npending_output = 0;
+ pthread_cond_broadcast (output_buffer->wait);
+ pthread_cond_broadcast (&select_alert);
+ }
+ else
+ {
+ npending_output -= amount;
+ memmove (pending_output, pending_output + amount, npending_output);
+ }
+ }
+ else
+ dev_close ();
+
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+/* Try and write out any pending writes to the device. If WAIT is non-zero,
+ will wait for any activity to cease. */
+/* Be careful that the global lock is already locked. */
+error_t
+dev_sync (int wait)
+{
+ if (err)
+ return err;
+
+ if (!output_buffer || phys_device == MACH_PORT_NULL)
+ return 0;
+
+ while (buffer_readable (output_buffer) >= dev_blksize)
+ {
+ err = start_output (! wait);
+ if (err)
+ return err;
+
+ if (!wait)
+ return 0;
+
+ if (pthread_hurd_cond_wait_np (output_buffer->wait, &global_lock))
+ return EINTR;
+ }
+
+ /* XXX: When the size of output_buffer is non-zero and less than
+ DEV_BLKSIZE, the rest will be ignored or discarded. */
+ return 0;
+}
+
+/* Unused stubs. */
+kern_return_t
+device_read_reply (mach_port_t reply, kern_return_t returncode,
+ io_buf_ptr_t data, mach_msg_type_number_t amount)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+device_write_reply (mach_port_t reply, kern_return_t returncode, int amount)
+{
+ return EOPNOTSUPP;
+}
diff --git a/trans/symlink.c b/trans/symlink.c
new file mode 100644
index 00000000..845a1121
--- /dev/null
+++ b/trans/symlink.c
@@ -0,0 +1,236 @@
+/* Translator for S_IFLNK nodes
+ Copyright (C) 1994, 2000, 2001, 2002 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <argp.h>
+#include <hurd/fsys.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <error.h>
+#include <version.h>
+#include "fsys_S.h"
+
+mach_port_t realnode;
+
+/* We return this for O_NOLINK lookups */
+mach_port_t realnodenoauth;
+
+/* We return this for non O_NOLINK lookups */
+char *linktarget;
+
+extern int fsys_server (mach_msg_header_t *, mach_msg_header_t *);
+
+const char *argp_program_version = STANDARD_HURD_VERSION (symlink);
+
+static const struct argp_option options[] =
+ {
+ { 0 }
+ };
+
+static const char args_doc[] = "TARGET";
+static const char doc[] = "A translator for symlinks."
+"\vA symlink is an alias for another node in the filesystem."
+"\n"
+"\nA symbolic link refers to its target `by name', and contains no actual"
+" reference to the target. The target referenced by the symlink is"
+" looked up in the namespace of the client.";
+
+/* Parse a single option/argument. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ if (key == ARGP_KEY_ARG && state->arg_num == 0)
+ linktarget = arg;
+ else if (key == ARGP_KEY_ARG || key == ARGP_KEY_NO_ARGS)
+ argp_usage (state);
+ else
+ return ARGP_ERR_UNKNOWN;
+ return 0;
+}
+
+static struct argp argp = { options, parse_opt, args_doc, doc };
+
+
+int
+main (int argc, char **argv)
+{
+ mach_port_t bootstrap;
+ mach_port_t control;
+ error_t err;
+
+ /* Parse our options... */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "Must be started as a translator");
+
+ linktarget = argv[1];
+
+ /* Reply to our parent */
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &control);
+ mach_port_insert_right (mach_task_self (), control, control,
+ MACH_MSG_TYPE_MAKE_SEND);
+ err =
+ fsys_startup (bootstrap, 0, control, MACH_MSG_TYPE_COPY_SEND, &realnode);
+ mach_port_deallocate (mach_task_self (), control);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (1, err, "Starting up translator");
+
+ io_restrict_auth (realnode, &realnodenoauth, 0, 0, 0, 0);
+ mach_port_deallocate (mach_task_self (), realnode);
+
+ /* Mark us as important. */
+ mach_port_t proc = getproc ();
+ if (proc == MACH_PORT_NULL)
+ error (2, err, "cannot get a handle to our process");
+
+ err = proc_mark_important (proc);
+ /* This might fail due to permissions or because the old proc server
+ is still running, ignore any such errors. */
+ if (err && err != EPERM && err != EMIG_BAD_ID)
+ error (2, err, "Cannot mark us as important");
+
+ mach_port_deallocate (mach_task_self (), proc);
+
+ /* Launch */
+ while (1)
+ {
+ /* The timeout here is 10 minutes */
+ err = mach_msg_server_timeout (fsys_server, 0, control,
+ MACH_RCV_TIMEOUT, 1000 * 60 * 10);
+ if (err == MACH_RCV_TIMED_OUT)
+ exit (0);
+ }
+}
+
+error_t
+S_fsys_getroot (mach_port_t fsys_t,
+ mach_port_t dotdotnode,
+ uid_t *uids, size_t nuids,
+ uid_t *gids, size_t ngids,
+ int flags,
+ retry_type *do_retry,
+ char *retry_name,
+ mach_port_t *ret,
+ mach_msg_type_name_t *rettype)
+{
+ if (flags & O_NOLINK)
+ {
+ /* Return our underlying node. */
+ *ret = realnodenoauth;
+ *rettype = MACH_MSG_TYPE_COPY_SEND;
+ *do_retry = FS_RETRY_REAUTH;
+ retry_name[0] = '\0';
+ return 0;
+ }
+ else
+ {
+ /* Return telling the user to follow the link */
+ strcpy (retry_name, linktarget);
+ if (linktarget[0] == '/')
+ {
+ *do_retry = FS_RETRY_MAGICAL;
+ *ret = MACH_PORT_NULL;
+ *rettype = MACH_MSG_TYPE_COPY_SEND;
+ }
+ else
+ {
+ *do_retry = FS_RETRY_REAUTH;
+ *ret = dotdotnode;
+ *rettype = MACH_MSG_TYPE_MOVE_SEND;
+ }
+ }
+ return 0;
+}
+
+error_t
+S_fsys_startup (mach_port_t bootstrap, int flags, mach_port_t control,
+ mach_port_t *real, mach_msg_type_name_t *realtype)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_goaway (mach_port_t control, int flags)
+{
+ exit (0);
+}
+
+error_t
+S_fsys_syncfs (mach_port_t control,
+ int wait,
+ int recurse)
+{
+ return 0;
+}
+
+error_t
+S_fsys_set_options (mach_port_t control,
+ char *data, mach_msg_type_number_t len,
+ int do_children)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_get_options (mach_port_t control,
+ char **data, mach_msg_type_number_t *len)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_getfile (mach_port_t control,
+ uid_t *uids, size_t nuids,
+ uid_t *gids, size_t ngids,
+ char *handle, size_t handllen,
+ mach_port_t *pt,
+ mach_msg_type_name_t *pttype)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_getpriv (mach_port_t control,
+ mach_port_t *host_priv, mach_msg_type_name_t *host_priv_type,
+ mach_port_t *dev_master, mach_msg_type_name_t *dev_master_type,
+ task_t *fs_task, mach_msg_type_name_t *fs_task_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_init (mach_port_t control,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t proc,
+ auth_t auth)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_forward (mach_port_t server, mach_port_t requestor,
+ char *argz, size_t argz_len)
+{
+ return EOPNOTSUPP;
+}
diff --git a/usermux/Makefile b/usermux/Makefile
new file mode 100644
index 00000000..c30b5ab4
--- /dev/null
+++ b/usermux/Makefile
@@ -0,0 +1,30 @@
+# Makefile for usermux
+#
+# Copyright (C) 1997, 2000, 2012 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := usermux
+makemode := server
+
+target = usermux
+
+SRCS = usermux.c mux.c leaf.c node.c stubs.c
+
+OBJS = $(SRCS:.c=.o)
+HURDLIBS = netfs fshelp iohelp ports ihash shouldbeinlibc
+OTHERLIBS = -lpthread
+
+include ../Makeconf
diff --git a/usermux/leaf.c b/usermux/leaf.c
new file mode 100644
index 00000000..7f41b187
--- /dev/null
+++ b/usermux/leaf.c
@@ -0,0 +1,152 @@
+/* Usermux leaf node functions
+
+ Copyright (C) 1997,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdio.h>
+#include <string.h>
+#include <pwd.h>
+#include <argz.h>
+#include <hurd/paths.h>
+
+#include "usermux.h"
+
+/* Read the contents of NODE (a symlink), for USER, into BUF. */
+error_t
+netfs_attempt_readlink (struct iouser *user, struct node *node, char *buf)
+{
+ assert (node->nn->name);
+ /* For symlink nodes, the translator spec just contains the link target. */
+ memcpy (buf, node->nn->trans, node->nn->trans_len);
+ fshelp_touch (&node->nn_stat, TOUCH_ATIME, usermux_maptime);
+ return 0;
+}
+
+/* For locked node NODE with S_IPTRANS set in its mode, look up the name of
+ its translator. Store the name into newly malloced storage, and return it
+ in *ARGZ; set *ARGZ_LEN to the total length.
+
+ For usermux, this creates a new translator string by instantiating the
+ global translator template. */
+error_t
+netfs_get_translator (struct node *node, char **trans, size_t *trans_len)
+{
+ if (! node->nn->name)
+ return EINVAL;
+ else
+ {
+ fshelp_touch (&node->nn_stat, TOUCH_ATIME, usermux_maptime);
+ *trans = 0;
+ *trans_len = 0;
+ if (S_ISLNK (node->nn_stat.st_mode))
+ argz_add (trans, trans_len, _HURD_SYMLINK);
+ return
+ argz_append (trans, trans_len, node->nn->trans, node->nn->trans_len);
+ }
+}
+
+/* Create a new leaf node in MUX, with a name NAME, and return the new node
+ with a single reference in NODE. */
+error_t
+create_user_node (struct usermux *mux, struct usermux_name *name,
+ struct passwd *pw, struct node **node)
+{
+ error_t err;
+ struct node *new;
+ struct netnode *nn = malloc (sizeof (struct netnode));
+
+ if (! nn)
+ return ENOMEM;
+
+ nn->mux = mux;
+ nn->name = name;
+
+ new = netfs_make_node (nn);
+ if (! new)
+ {
+ free (nn);
+ return ENOMEM;
+ }
+
+ new->nn_stat = mux->stat_template;
+
+ new->nn_stat.st_ino = pw->pw_uid + USERMUX_FILENO_UID_OFFSET;
+
+ if (strcmp (mux->trans_template, _HURD_SYMLINK) == 0
+ && mux->trans_template_len == sizeof _HURD_SYMLINK)
+ {
+ err = argz_create_sep (pw->pw_dir, 0, &nn->trans, &nn->trans_len);
+ new->nn_stat.st_mode = (S_IFLNK | 0666);
+ new->nn_stat.st_size = nn->trans_len;
+ }
+ else
+ {
+ unsigned replace_count = 0;
+
+ nn->trans = 0; /* Initialize return value. */
+ nn->trans_len = 0;
+
+ err = argz_append (&nn->trans, &nn->trans_len,
+ mux->trans_template, mux->trans_template_len);
+
+ /* Perform any substitutions. */
+ if (!err && mux->user_pat && *mux->user_pat)
+ err = argz_replace (&nn->trans, &nn->trans_len,
+ mux->user_pat, pw->pw_name,
+ &replace_count);
+ if (!err && mux->home_pat && *mux->home_pat)
+ err = argz_replace (&nn->trans, &nn->trans_len,
+ mux->home_pat, pw->pw_dir,
+ &replace_count);
+ if (!err && mux->uid_pat && *mux->uid_pat)
+ {
+ char uid_buf[10];
+ snprintf (uid_buf, sizeof uid_buf, "%d", pw->pw_uid);
+ err = argz_replace (&nn->trans, &nn->trans_len,
+ mux->uid_pat, uid_buf,
+ &replace_count);
+ }
+
+ if (!err && replace_count == 0)
+ /* Default, if no instances of any pattern occur, is to append the
+ user home dir. */
+ err = argz_add (&nn->trans, &nn->trans_len, pw->pw_dir);
+
+ if (err && nn->trans_len > 0)
+ free (nn->trans);
+
+ new->nn_stat.st_mode = (S_IFREG | S_IPTRANS | 0666);
+ new->nn_stat.st_size = 0;
+ }
+ new->nn_translated = new->nn_stat.st_mode;
+
+ if (err)
+ {
+ free (nn);
+ free (new);
+ return err;
+ }
+
+ fshelp_touch (&new->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
+ usermux_maptime);
+
+ name->node = new;
+ *node = new;
+
+ return 0;
+}
diff --git a/usermux/mux.c b/usermux/mux.c
new file mode 100644
index 00000000..bfa95fd2
--- /dev/null
+++ b/usermux/mux.c
@@ -0,0 +1,502 @@
+/* Root usermux node
+
+ Copyright (C) 1997, 1998, 1999, 2000, 2002, 2008
+ Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stddef.h>
+#include <string.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <sys/mman.h>
+
+#include "usermux.h"
+
+/* The granularity with which we allocate space to return our result. */
+#define DIRENTS_CHUNK_SIZE (128*1024)/* Enough for perhaps 8000 names. */
+
+/* The number seconds we cache our directory return value, in seconds. */
+#define DIRENTS_CACHE_TIME 90
+
+/* Returned directory entries are aligned to blocks this many bytes long.
+ Must be a power of two. */
+#define DIRENT_ALIGN 4
+#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name)
+
+/* Length is structure before the name + the name + '\0', all
+ padded to a four-byte alignment. */
+#define DIRENT_LEN(name_len) \
+ ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \
+ & ~(DIRENT_ALIGN - 1))
+
+static error_t lookup_user (struct usermux *mux, const char *user,
+ struct node **node); /* fwd decl */
+
+/* [root] Directory operations. */
+
+/* Lookup NAME in DIR for USER; set *NODE to the found name upon return. If
+ the name was not found, then return ENOENT. On any error, clear *NODE.
+ (*NODE, if found, should be locked, this call should unlock DIR no matter
+ what.) */
+error_t
+netfs_attempt_lookup (struct iouser *user, struct node *dir,
+ char *name, struct node **node)
+{
+ error_t err;
+
+ if (dir->nn->name)
+ err = ENOTDIR;
+ else
+ err = lookup_user (dir->nn->mux, name, node);
+
+ fshelp_touch (&dir->nn_stat, TOUCH_ATIME, usermux_maptime);
+
+ pthread_mutex_unlock (&dir->lock);
+
+ if (! err)
+ pthread_mutex_lock (&(*node)->lock);
+
+ return err;
+}
+
+/* Fetch a directory of user entries, as for netfs_get_dirents (that function
+ is actually a wrapper that caches the results for a while). */
+static error_t
+get_dirents (struct node *dir,
+ int first_entry, int max_entries, char **data,
+ mach_msg_type_number_t *data_len,
+ vm_size_t max_data_len, int *data_entries)
+{
+ error_t err = 0;
+
+ if (dir->nn->name)
+ return ENOTDIR;
+
+ /* Start scanning. */
+ setpwent ();
+
+ /* Find the first entry. */
+ while (first_entry-- > 0)
+ if (! getpwent ())
+ {
+ max_entries = 0;
+ break;
+ }
+
+ if (max_entries != 0)
+ {
+ size_t size = (max_data_len == 0 ? DIRENTS_CHUNK_SIZE : max_data_len);
+
+ *data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ err = (data != (void *) -1) ? errno : 0;
+
+ if (! err)
+ {
+ struct passwd *pw;
+ char *p = *data;
+ int count = 0;
+ int entry_type =
+ (S_ISLNK (dir->nn->mux->stat_template.st_mode) ? DT_LNK : DT_REG);
+
+ /* See how much space we need for the result. */
+ while ((max_entries == -1 || count < max_entries)
+ && (pw = getpwent ()))
+ {
+ struct dirent hdr;
+ size_t name_len = strlen (pw->pw_name);
+ size_t sz = DIRENT_LEN (name_len);
+
+ if ((p - *data) + sz > size)
+ {
+ if (max_data_len > 0)
+ break;
+ else
+ /* Try to grow our return buffer. */
+ {
+ vm_address_t extension = (vm_address_t)(*data + size);
+ err = vm_allocate (mach_task_self (), &extension,
+ DIRENTS_CHUNK_SIZE, 0);
+ if (err)
+ break;
+ size += DIRENTS_CHUNK_SIZE;
+ }
+ }
+
+ hdr.d_namlen = name_len;
+ hdr.d_fileno = pw->pw_uid + USERMUX_FILENO_UID_OFFSET;
+ hdr.d_reclen = sz;
+ hdr.d_type = entry_type;
+
+ memcpy (p, &hdr, DIRENT_NAME_OFFS);
+ strcpy (p + DIRENT_NAME_OFFS, pw->pw_name);
+ p += sz;
+
+ count++;
+ }
+
+ if (err)
+ munmap (*data, size);
+ else
+ {
+ vm_address_t alloc_end = (vm_address_t)(*data + size);
+ vm_address_t real_end = round_page (p);
+ if (alloc_end > real_end)
+ munmap ((caddr_t) real_end, alloc_end - real_end);
+ *data_len = p - *data;
+ *data_entries = count;
+ }
+ }
+ }
+
+ endpwent ();
+
+ return err;
+}
+
+/* Implement the netfs_get_directs callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_get_dirents (struct iouser *cred, struct node *dir,
+ int first_entry, int max_entries, char **data,
+ mach_msg_type_number_t *data_len,
+ vm_size_t max_data_len, int *data_entries)
+{
+ error_t err;
+ static time_t cache_timestamp = 0;
+ static pthread_rwlock_t cache_lock = PTHREAD_RWLOCK_INITIALIZER;
+ static char *cached_data = 0;
+ static mach_msg_type_number_t cached_data_len = 0;
+ static int cached_data_entries = 0;
+ struct timeval tv;
+ char *first;
+ size_t bytes_left, entries_left;
+
+ maptime_read (usermux_maptime, &tv);
+ if (tv.tv_sec > cache_timestamp + DIRENTS_CACHE_TIME)
+ {
+ pthread_rwlock_wrlock (&cache_lock);
+
+ if (cached_data_len > 0)
+ /* Free the old cache. */
+ {
+ munmap (cached_data, cached_data_len);
+ cached_data = 0;
+ cached_data_len = 0;
+ }
+
+ err = get_dirents (dir, 0, -1, &cached_data, &cached_data_len, 0,
+ &cached_data_entries);
+
+ if (! err)
+ cache_timestamp = tv.tv_sec;
+
+ pthread_rwlock_unlock (&cache_lock);
+
+ if (err)
+ return err;
+ }
+
+ pthread_rwlock_rdlock (&cache_lock);
+
+ first = cached_data;
+ bytes_left = cached_data_len;
+ entries_left = cached_data_entries;
+
+ while (first_entry > 0)
+ {
+ struct dirent *e = (struct dirent *)first;
+
+ if (entries_left == 0)
+ {
+ pthread_rwlock_unlock (&cache_lock);
+ return EINVAL;
+ }
+
+ first += e->d_reclen;
+ bytes_left -= e->d_reclen;
+ entries_left--;
+ }
+
+ if ((max_data_len > 0 && max_data_len < bytes_left)
+ || (max_entries > 0 && max_entries < entries_left))
+ /* If there's some limit on the return value, we can't just use our
+ values representing the whole cache, so we have to explicitly count
+ how much we're going to return. */
+ {
+ char *lim = first;
+ int entries = 0;
+
+ while (entries_left > 0
+ && max_entries > 0
+ && max_data_len > ((struct dirent *)lim)->d_reclen)
+ {
+ size_t reclen = ((struct dirent *)lim)->d_reclen;
+ max_data_len -= reclen;
+ max_entries--;
+ entries++;
+ lim += reclen;
+ }
+
+ bytes_left = (lim - first);
+ entries_left = entries;
+ }
+
+ *data_len = bytes_left;
+ *data_entries = entries_left;
+
+ *data = mmap (0, bytes_left, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ err = (*data == (void *) -1) ? errno : 0;
+ if (! err)
+ bcopy (cached_data, *data, bytes_left);
+
+ pthread_rwlock_unlock (&cache_lock);
+
+ fshelp_touch (&dir->nn_stat, TOUCH_ATIME, usermux_maptime);
+
+ return err;
+}
+
+/* User lookup. */
+
+/* Free storage allocated consumed by the user mux name NM, but not the node
+ it points to. */
+static void
+free_name (struct usermux_name *nm)
+{
+ free ((char *)nm->name);
+ free (nm);
+}
+
+/* See if there's an existing entry for the name USER, and if so, return its
+ node in NODE with an additional references. True is returned iff the
+ lookup succeeds. If PURGE is true, then any nodes with a null node are
+ removed. */
+static int
+lookup_cached (struct usermux *mux, const char *user, int purge,
+ struct node **node)
+{
+ struct usermux_name *nm = mux->names, **prevl = &mux->names;
+
+ while (nm)
+ {
+ struct usermux_name *next = nm->next;
+
+ if (strcasecmp (user, nm->name) == 0)
+ {
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ if (nm->node)
+ nm->node->references++;
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+
+ if (nm->node)
+ {
+ *node = nm->node;
+ return 1;
+ }
+ }
+
+ if (purge && !nm->node)
+ {
+ *prevl = nm->next;
+ free_name (nm);
+ }
+ else
+ prevl = &nm->next;
+
+ nm = next;
+ }
+
+ return 0;
+}
+
+/* See if there's an existing entry for the name USER, and if so, return its
+ node in NODE, with an additional reference, otherwise, create a new node
+ for the user HE as referred to by USER, and return that instead, with a
+ single reference. The type of node created is either a translator node,
+ if USER refers to the official name of the user, or a symlink node to the
+ official name, if it doesn't. */
+static error_t
+lookup_pwent (struct usermux *mux, const char *user, struct passwd *pw,
+ struct node **node)
+{
+ error_t err;
+ struct usermux_name *nm = malloc (sizeof (struct usermux_name));
+
+ if (! nm)
+ return ENOMEM;
+
+ nm->name = strdup (user);
+ err = create_user_node (mux, nm, pw, node);
+ if (err)
+ {
+ free_name (nm);
+ return err;
+ }
+
+ pthread_rwlock_wrlock (&mux->names_lock);
+ if (lookup_cached (mux, user, 1, node))
+ /* An entry for USER has already been created between the time we last
+ looked and now (which is possible because we didn't lock MUX).
+ Just throw away our version and return the one already in the cache. */
+ {
+ pthread_rwlock_unlock (&mux->names_lock);
+ nm->node->nn->name = 0; /* Avoid touching the mux name list. */
+ netfs_nrele (nm->node); /* Free the tentative new node. */
+ free_name (nm); /* And the name it was under. */
+ }
+ else
+ /* Enter NM into MUX's list of names, and return the new node. */
+ {
+ nm->next = mux->names;
+ mux->names = nm;
+ pthread_rwlock_unlock (&mux->names_lock);
+ }
+
+ return 0;
+}
+
+/* Lookup the user USER in MUX, and return the resulting node in NODE, with
+ an additional reference, or an error. */
+static error_t
+lookup_user (struct usermux *mux, const char *user, struct node **node)
+{
+ int was_cached;
+ struct passwd _pw, *pw;
+ char pwent_data[2048]; /* XXX what size should this be???? */
+
+ pthread_rwlock_rdlock (&mux->names_lock);
+ was_cached = lookup_cached (mux, user, 0, node);
+ pthread_rwlock_unlock (&mux->names_lock);
+
+ if (was_cached)
+ return 0;
+ else
+ {
+ if (getpwnam_r (user, &_pw, pwent_data, sizeof pwent_data, &pw))
+ return ENOENT;
+ if (pw == NULL)
+ return ENOENT;
+ return lookup_pwent (mux, user, pw, node);
+ }
+}
+
+/* This should sync the entire remote filesystem. If WAIT is set, return
+ only after sync is completely finished. */
+error_t
+netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+ return 0;
+}
+
+/* This should attempt a chmod call for the user specified by CRED on node
+ NODE, to change the owner to UID and the group to GID. */
+error_t
+netfs_attempt_chown (struct iouser *cred, struct node *node, uid_t uid, uid_t gid)
+{
+ if (node->nn->name)
+ return EOPNOTSUPP;
+ else
+ {
+ struct usermux *mux = node->nn->mux;
+ error_t err = file_chown (mux->underlying, uid, gid);
+
+ if (! err)
+ {
+ struct usermux_name *nm;
+
+ /* Change NODE's owner. */
+ mux->stat_template.st_uid = uid;
+ mux->stat_template.st_gid = gid;
+ node->nn_stat.st_uid = uid;
+ node->nn_stat.st_gid = gid;
+
+ /* Change the owner of each leaf node. */
+ pthread_rwlock_rdlock (&mux->names_lock);
+ for (nm = mux->names; nm; nm = nm->next)
+ if (nm->node)
+ {
+ nm->node->nn_stat.st_uid = uid;
+ nm->node->nn_stat.st_gid = gid;
+ }
+ pthread_rwlock_unlock (&mux->names_lock);
+
+ fshelp_touch (&node->nn_stat, TOUCH_CTIME, usermux_maptime);
+ }
+
+ return err;
+ }
+}
+
+/* This should attempt a chauthor call for the user specified by CRED on node
+ NODE, to change the author to AUTHOR. */
+error_t
+netfs_attempt_chauthor (struct iouser *cred, struct node *node, uid_t author)
+{
+ if (node->nn->name)
+ return EOPNOTSUPP;
+ else
+ {
+ struct usermux *mux = node->nn->mux;
+ error_t err = file_chauthor (mux->underlying, author);
+
+ if (! err)
+ {
+ struct usermux_name *nm;
+
+ /* Change NODE's owner. */
+ mux->stat_template.st_author = author;
+ node->nn_stat.st_author = author;
+
+ /* Change the owner of each leaf node. */
+ pthread_rwlock_rdlock (&mux->names_lock);
+ for (nm = mux->names; nm; nm = nm->next)
+ if (nm->node)
+ nm->node->nn_stat.st_author = author;
+ pthread_rwlock_unlock (&mux->names_lock);
+
+ fshelp_touch (&node->nn_stat, TOUCH_CTIME, usermux_maptime);
+ }
+
+ return err;
+ }
+}
+
+/* This should attempt a chmod call for the user specified by CRED on node
+ NODE, to change the mode to MODE. Unlike the normal Unix and Hurd meaning
+ of chmod, this function is also used to attempt to change files into other
+ types. If such a transition is attempted which is impossible, then return
+ EOPNOTSUPP. */
+error_t
+netfs_attempt_chmod (struct iouser *cred, struct node *node, mode_t mode)
+{
+ mode &= ~S_ITRANS;
+ if ((mode & S_IFMT) == 0)
+ mode |= (node->nn_stat.st_mode & S_IFMT);
+ if (node->nn->name || ((mode & S_IFMT) != (node->nn_stat.st_mode & S_IFMT)))
+ return EOPNOTSUPP;
+ else
+ {
+ error_t err = file_chmod (node->nn->mux->underlying, mode & ~S_IFMT);
+ if (! err)
+ {
+ node->nn_stat.st_mode = mode;
+ fshelp_touch (&node->nn_stat, TOUCH_CTIME, usermux_maptime);
+ }
+ return err;
+ }
+}
diff --git a/usermux/node.c b/usermux/node.c
new file mode 100644
index 00000000..2341714c
--- /dev/null
+++ b/usermux/node.c
@@ -0,0 +1,129 @@
+/* General fs node functions
+
+ Copyright (C) 1997, 1999, 2007 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <fcntl.h>
+
+#include "usermux.h"
+
+/* Node maintenance. */
+
+/* Node NP is all done; free all its associated storage. */
+void
+netfs_node_norefs (struct node *node)
+{
+ if (node->nn->name)
+ /* Remove our name's pointer to us; the name itself will eventually be
+ freed by another party. */
+ node->nn->name->node = 0;
+ if (node->nn->trans_len > 0)
+ free (node->nn->trans);
+ free (node->nn);
+ free (node);
+}
+
+/* Attempt to create a file named NAME in DIR for USER with MODE. Set *NODE
+ to the new node upon return. On any error, clear *NODE. *NODE should be
+ locked on success; no matter what, unlock DIR before returning. */
+error_t
+netfs_attempt_create_file (struct iouser *user, struct node *dir,
+ char *name, mode_t mode, struct node **node)
+{
+ *node = 0;
+ pthread_mutex_unlock (&dir->lock);
+ return EOPNOTSUPP;
+}
+
+/* Node NODE is being opened by USER, with FLAGS. NEWNODE is nonzero if we
+ just created this node. Return an error if we should not permit the open
+ to complete because of a permission restriction. */
+error_t
+netfs_check_open_permissions (struct iouser *user, struct node *node,
+ int flags, int newnode)
+{
+ error_t err = 0;
+ if (flags & O_READ)
+ err = fshelp_access (&node->nn_stat, S_IREAD, user);
+ if (!err && (flags & O_WRITE))
+ err = fshelp_access (&node->nn_stat, S_IWRITE, user);
+ if (!err && (flags & O_EXEC))
+ err = fshelp_access (&node->nn_stat, S_IEXEC, user);
+ return err;
+}
+
+/* This should attempt a utimes call for the user specified by CRED on node
+ NODE, to change the atime to ATIME and the mtime to MTIME. */
+error_t
+netfs_attempt_utimes (struct iouser *cred, struct node *node,
+ struct timespec *atime, struct timespec *mtime)
+{
+ error_t err = fshelp_isowner (&node->nn_stat, cred);
+ int flags = TOUCH_CTIME;
+
+ if (! err)
+ {
+ if (mtime)
+ node->nn_stat.st_mtim = *mtime;
+ else
+ flags |= TOUCH_MTIME;
+
+ if (atime)
+ node->nn_stat.st_atim = *atime;
+ else
+ flags |= TOUCH_ATIME;
+
+ fshelp_touch (&node->nn_stat, flags, usermux_maptime);
+ }
+ return err;
+}
+
+/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and O_EXEC)
+ in *TYPES for file NODE and user CRED. */
+error_t
+netfs_report_access (struct iouser *cred, struct node *node, int *types)
+{
+ *types = 0;
+ if (fshelp_access (&node->nn_stat, S_IREAD, cred) == 0)
+ *types |= O_READ;
+ if (fshelp_access (&node->nn_stat, S_IWRITE, cred) == 0)
+ *types |= O_WRITE;
+ if (fshelp_access (&node->nn_stat, S_IEXEC, cred) == 0)
+ *types |= O_EXEC;
+ return 0;
+}
+
+/* Trivial definitions. */
+
+/* Make sure that NP->nn_stat is filled with current information. CRED
+ identifies the user responsible for the operation. */
+error_t
+netfs_validate_stat (struct node *node, struct iouser *cred)
+{
+ return 0;
+}
+
+/* This should sync the file NODE completely to disk, for the user CRED. If
+ WAIT is set, return only after sync is completely finished. */
+error_t
+netfs_attempt_sync (struct iouser *cred, struct node *node, int wait)
+{
+ return 0;
+}
diff --git a/usermux/stubs.c b/usermux/stubs.c
new file mode 100644
index 00000000..b1992b02
--- /dev/null
+++ b/usermux/stubs.c
@@ -0,0 +1,143 @@
+/* Stub routines for usermux
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <hurd/netfs.h>
+
+/* Attempt to turn NODE (user CRED) into a symlink with target NAME. */
+error_t
+netfs_attempt_mksymlink (struct iouser *cred, struct node *node, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to turn NODE (user CRED) into a device. TYPE is either S_IFBLK or
+ S_IFCHR. */
+error_t
+netfs_attempt_mkdev (struct iouser *cred, struct node *node,
+ mode_t type, dev_t indexes)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to set the passive translator record for FILE to ARGZ (of length
+ ARGZLEN) for user CRED. */
+error_t
+netfs_set_translator (struct iouser *cred, struct node *node,
+ char *argz, size_t argzlen)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should attempt a chflags call for the user specified by CRED on node
+ NODE, to change the flags to FLAGS. */
+error_t
+netfs_attempt_chflags (struct iouser *cred, struct node *node, int flags)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should attempt to set the size of the file NODE (for user CRED) to
+ SIZE bytes long. */
+error_t
+netfs_attempt_set_size (struct iouser *cred, struct node *node, off_t size)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should attempt to fetch filesystem status information for the remote
+ filesystem, for the user CRED. */
+error_t
+netfs_attempt_statfs (struct iouser *cred, struct node *node,
+ struct statfs *st)
+{
+ return EOPNOTSUPP;
+}
+
+/* Delete NAME in DIR for USER. */
+error_t
+netfs_attempt_unlink (struct iouser *user, struct node *dir, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+/* Note that in this one call, neither of the specific nodes are locked. */
+error_t
+netfs_attempt_rename (struct iouser *user, struct node *fromdir,
+ char *fromname, struct node *todir,
+ char *toname, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to create a new directory named NAME in DIR for USER with mode
+ MODE. */
+error_t
+netfs_attempt_mkdir (struct iouser *user, struct node *dir,
+ char *name, mode_t mode)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to remove directory named NAME in DIR for USER. */
+error_t
+netfs_attempt_rmdir (struct iouser *user,
+ struct node *dir, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+/* Create a link in DIR with name NAME to FILE for USER. Note that neither
+ DIR nor FILE are locked. If EXCL is set, do not delete the target, but
+ return EEXIST if NAME is already found in DIR. */
+error_t
+netfs_attempt_link (struct iouser *user, struct node *dir,
+ struct node *file, char *name, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to create an anonymous file related to DIR for USER with MODE.
+ Set *NODE to the returned file upon success. No matter what, unlock DIR. */
+error_t
+netfs_attempt_mkfile (struct iouser *user, struct node *dir,
+ mode_t mode, struct node **node)
+{
+ return EOPNOTSUPP;
+}
+
+/* Read from the file NODE for user CRED starting at OFFSET and continuing for
+ up to *LEN bytes. Put the data at DATA. Set *LEN to the amount
+ successfully read upon return. */
+error_t
+netfs_attempt_read (struct iouser *cred, struct node *node,
+ off_t offset, size_t *len, void *data)
+{
+ return EOPNOTSUPP;
+}
+
+/* Write to the file NODE for user CRED starting at OFSET and continuing for up
+ to *LEN bytes from DATA. Set *LEN to the amount seccessfully written upon
+ return. */
+error_t
+netfs_attempt_write (struct iouser *cred, struct node *node,
+ off_t offset, size_t *len, void *data)
+{
+ return EOPNOTSUPP;
+}
diff --git a/usermux/usermux-xinl.c b/usermux/usermux-xinl.c
new file mode 100644
index 00000000..fd7661c2
--- /dev/null
+++ b/usermux/usermux-xinl.c
@@ -0,0 +1,24 @@
+/* Real definitions for extern inline functions in usermux.h
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#define USERMUX_EI
+#undef __OPTIMIZE__
+#define __OPTIMIZE__ 1
+#include "usermux.h"
diff --git a/usermux/usermux.c b/usermux/usermux.c
new file mode 100644
index 00000000..fad923c9
--- /dev/null
+++ b/usermux/usermux.c
@@ -0,0 +1,156 @@
+/* Multiplexing filesystems by user
+
+ Copyright (C) 1997, 2000, 2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <unistd.h>
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <sys/time.h>
+#include <hurd/paths.h>
+
+#include <version.h>
+
+#include "usermux.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (usermux);
+
+char *netfs_server_name = "usermux";
+char *netfs_server_version = HURD_VERSION;
+int netfs_maxsymlinks = 25;
+
+volatile struct mapped_time_value *usermux_mapped_time;
+
+#define OPT_USER_PAT 1
+#define OPT_HOME_PAT 2
+#define OPT_UID_PAT 3
+
+/* Startup options. */
+static const struct argp_option options[] =
+{
+ { "user-pattern", OPT_USER_PAT, "PAT", 0,
+ "The string to replace in the translator specification with the user name"
+ " (default `${user}')" },
+ { "home-pattern", OPT_HOME_PAT, "PAT", 0,
+ "The string to replace in the translator specification with the user's"
+ " home directory (default `${home}')" },
+ { "uid-pattern", OPT_UID_PAT, "PAT", 0,
+ "The string to replace in the translator specification with the uid"
+ " (default `${uid}')" },
+ { "clear-patterns", 'C', 0, 0,
+ "Reset all patterns to empty; this option may then be followed by options"
+ " to set specific patterns" },
+ { 0 }
+};
+static const char args_doc[] = "[TRANSLATOR [ARG...]]";
+static const char doc[] =
+ "A translator for invoking user-specific translators."
+ "\vThis translator appears like a directory in which user names can be"
+ " looked up, and will start TRANSLATOR to service each resulting node."
+ " If no pattern occurs in the translator specification, the users's"
+ " home directory is appended to it instead; TRANSLATOR defaults to"
+ " " _HURD_SYMLINK ".";
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ struct stat ul_stat;
+ mach_port_t bootstrap;
+ struct usermux mux =
+ { user_pat: "${user}", home_pat: "${home}", uid_pat: "${uid}" };
+ struct netnode root_nn = { mux: &mux };
+
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case OPT_USER_PAT: mux.user_pat = arg; break;
+ case OPT_HOME_PAT: mux.home_pat = arg; break;
+ case OPT_UID_PAT: mux.uid_pat = arg; break;
+ case 'C': bzero (&mux, sizeof mux); break;
+
+ case ARGP_KEY_NO_ARGS:
+ bzero (&mux, sizeof mux); /* Default doesn't use them; be careful. */
+ return argz_create_sep (_HURD_SYMLINK, 0,
+ &mux.trans_template, &mux.trans_template_len);
+ case ARGP_KEY_ARGS:
+ /* Steal the entire tail of arg vector for our own use. */
+ return argz_create (state->argv + state->next,
+ &mux.trans_template, &mux.trans_template_len);
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = { options, parse_opt, args_doc, doc };
+
+ /* Parse our command line arguments. */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ netfs_init ();
+
+ /* Create the root node (some attributes initialized below). */
+ netfs_root_node = netfs_make_node (&root_nn);
+ if (! netfs_root_node)
+ error (5, ENOMEM, "Cannot create root node");
+
+ err = maptime_map (0, 0, &usermux_maptime);
+ if (err)
+ error (6, err, "Cannot map time");
+
+ /* Handshake with the party trying to start the translator. */
+ mux.underlying = netfs_startup (bootstrap, 0);
+
+ /* We inherit various attributes from the node underlying this translator. */
+ err = io_stat (mux.underlying, &ul_stat);
+ if (err)
+ error (7, err, "Cannot stat underlying node");
+
+ /* MUX.stat_template contains some fields that are inherited by all nodes
+ we create. */
+ mux.stat_template.st_uid = ul_stat.st_uid;
+ mux.stat_template.st_gid = ul_stat.st_gid;
+ mux.stat_template.st_author = ul_stat.st_author;
+ mux.stat_template.st_fsid = getpid ();
+ mux.stat_template.st_nlink = 1;
+ mux.stat_template.st_fstype = FSTYPE_MISC;
+
+ /* We implement symlinks directly, and everything else as a real
+ translator. */
+ if (strcmp (mux.trans_template, _HURD_SYMLINK) == 0)
+ mux.stat_template.st_mode = S_IFLNK | 0666;
+ else
+ mux.stat_template.st_mode = S_IFREG | S_IPTRANS | 0666;
+
+ /* Initialize the root node's stat information. */
+ netfs_root_node->nn_stat = mux.stat_template;
+ netfs_root_node->nn_stat.st_ino = 2;
+ netfs_root_node->nn_stat.st_mode =
+ S_IFDIR | (ul_stat.st_mode & ~S_IFMT & ~S_ITRANS);
+ netfs_root_node->nn_translated = 0;
+
+ fshelp_touch (&netfs_root_node->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
+ usermux_maptime);
+
+ for (;;) /* ?? */
+ netfs_server_loop ();
+}
diff --git a/usermux/usermux.h b/usermux/usermux.h
new file mode 100644
index 00000000..c9374aaa
--- /dev/null
+++ b/usermux/usermux.h
@@ -0,0 +1,99 @@
+/* Multiplexing filesystems by user
+
+ Copyright (C) 1997, 2000 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef __USERMUX_H__
+#define __USERMUX_H__
+
+#include <hurd/netfs.h>
+#include <pthread.h>
+#include <maptime.h>
+
+struct passwd;
+
+/* Filenos (aka inode numbers) for user nodes are the uid + this. */
+#define USERMUX_FILENO_UID_OFFSET 10
+
+/* Handy source of time. */
+volatile struct mapped_time_value *usermux_maptime;
+
+/* The state associated with a user multiplexer translator. */
+struct usermux
+{
+ /* The user nodes in this mux. */
+ struct usermux_name *names;
+ pthread_rwlock_t names_lock;
+
+ /* A template argz, which is used to start each user-specific translator
+ with the user name appropriately added. */
+ char *trans_template;
+ size_t trans_template_len;
+
+ /* What string to replace in TRANS_TEMPLATE with the name of the various
+ user params; if none occur in the template, the user's home dir is
+ appended as an additional argument. */
+ char *user_pat; /* User name */
+ char *home_pat; /* Home directory */
+ char *uid_pat; /* Numeric user id */
+
+ /* Constant fields for user stat entries. */
+ struct stat stat_template;
+
+ /* The file that this translator is sitting on top of; we inherit various
+ characteristics from it. */
+ file_t underlying;
+};
+
+/* The name of a recently looked up user entry. */
+struct usermux_name
+{
+ const char *name; /* Looked up name. */
+
+ /* A filesystem node associated with NAME. */
+ struct node *node;
+
+ struct usermux_name *next;
+};
+
+/* The fs specific storage that libnetfs associates with each filesystem
+ node. */
+struct netnode
+{
+ /* The mux this node belongs to (the node can either be the mux root, or
+ one of the users served by it). */
+ struct usermux *mux;
+
+ /* For mux nodes, 0, and for leaf nodes, the name under which the node was
+ looked up. */
+ struct usermux_name *name;
+
+ /* The translator associated with node, or if its a symlink, just the link
+ target. */
+ char *trans;
+ size_t trans_len;
+};
+
+error_t create_user_node (struct usermux *mux, struct usermux_name *name,
+ struct passwd *pw, struct node **node);
+
+#ifndef USERMUX_EI
+# define USERMUX_EI extern inline
+#endif
+
+#endif /* __USERMUX_H__ */
diff --git a/utils/Makefile b/utils/Makefile
new file mode 100644
index 00000000..241b0604
--- /dev/null
+++ b/utils/Makefile
@@ -0,0 +1,85 @@
+# Copyright (C) 1994,95,96,97,98,99,2000,01,02,12,14 Free Software Foundation,
+# Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := utils
+makemode := utilities
+
+targets = shd ps settrans showtrans syncfs fsysopts \
+ storeinfo login w uptime ids loginpr sush vmstat portinfo \
+ devprobe vminfo addauth rmauth unsu setauth ftpcp ftpdir storecat \
+ storeread msgport rpctrace mount gcore fakeauth fakeroot remap \
+ umount nullauth
+
+special-targets = loginpr sush uptime fakeroot remap
+SRCS = shd.c ps.c settrans.c syncfs.c showtrans.c addauth.c rmauth.c \
+ fsysopts.c storeinfo.c login.c loginpr.sh sush.sh w.c \
+ uptime.sh psout.c ids.c vmstat.c portinfo.c devprobe.c vminfo.c \
+ parse.c frobauth.c frobauth-mod.c setauth.c pids.c nonsugid.c \
+ unsu.c ftpcp.c ftpdir.c storeread.c storecat.c msgport.c \
+ rpctrace.c mount.c gcore.c fakeauth.c fakeroot.sh remap.sh \
+ nullauth.c match-options.c
+
+OBJS = $(filter-out %.sh,$(SRCS:.c=.o))
+HURDLIBS = ps ihash store fshelp ports ftpconn shouldbeinlibc
+LDLIBS += -lpthread
+login-LDLIBS = -lutil -lcrypt
+addauth-LDLIBS = -lcrypt
+setauth-LDLIBS = -lcrypt
+mount-LDLIBS = $(libblkid_LIBS)
+mount-CPPFLAGS = $(libblkid_CFLAGS)
+
+INSTALL-login-ops = -o root -m 4755
+INSTALL-ids-ops = -o root -m 4755
+INSTALL-ps-ops = -o root -m 4755
+INSTALL-w-ops = -o root -m 4755
+
+include ../Makeconf
+
+ps addauth rmauth setauth unsu msgport: parse.o pids.o
+login addauth setauth: nonsugid.o
+addauth rmauth setauth unsu: frobauth.o
+rmauth setauth unsu: frobauth-mod.o
+ps w: psout.o ../libps/libps.a ../libihash/libihash.a
+portinfo: ../libps/libps.a
+
+storeinfo storecat storeread: ../libstore/libstore.a
+ftpcp ftpdir: ../libftpconn/libftpconn.a
+
+settrans: ../libfshelp/libfshelp.a ../libports/libports.a
+ps w ids settrans syncfs showtrans fsysopts storeinfo login vmstat portinfo \
+ devprobe vminfo addauth rmauth setauth unsu ftpcp ftpdir storeread \
+ storecat msgport mount umount nullauth: \
+ ../libshouldbeinlibc/libshouldbeinlibc.a
+
+$(filter-out $(special-targets), $(targets)): %: %.o
+
+rpctrace: ../libports/libports.a ../libihash/libihash.a
+rpctrace-CPPFLAGS = -DDATADIR=\"${datadir}\"
+
+fakeauth: authServer.o auth_requestUser.o interruptServer.o \
+ ../libports/libports.a ../libihash/libihash.a \
+ ../libshouldbeinlibc/libshouldbeinlibc.a
+auth-MIGSFLAGS = -imacros $(srcdir)/../auth/mig-mutate.h
+fakeauth-CPPFLAGS = -I$(srcdir)/../auth
+authServer-CPPFLAGS = -I$(srcdir)/../auth
+auth_requestUser-CPPFLAGS = -I$(srcdir)/../auth
+
+mount umount: ../sutils/fstab.o ../sutils/clookup.o match-options.o \
+ $(foreach L,fshelp ports,../lib$L/lib$L.a)
+../sutils/fstab.o ../sutils/clookup.o: FORCE
+ $(MAKE) -C $(@D) $(@F)
+FORCE:
diff --git a/utils/addauth.c b/utils/addauth.c
new file mode 100644
index 00000000..0932a33f
--- /dev/null
+++ b/utils/addauth.c
@@ -0,0 +1,100 @@
+/* Add authentication to selected processes
+
+ Copyright (C) 1997, 1998 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <argp.h>
+#include <idvec.h>
+#include <ugids.h>
+#include <error.h>
+#include <hurd.h>
+#include <version.h>
+
+#include "frobauth.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (addauth);
+
+static struct argp_child child_argps[] = {{ &frobauth_ea_argp }, { 0 }};
+
+static char doc[] =
+ "Add new user/group ids to the authentication of selected processes";
+
+extern error_t
+get_nonsugid_ids (struct idvec *uids, struct idvec *gids);
+
+int
+main (int argc, char *argv[])
+{
+ int i;
+ error_t err;
+ auth_t auth;
+ char *ids_rep = 0;
+ process_t proc_server = getproc();
+ struct frobauth frobauth = FROBAUTH_INIT;
+ struct idvec have_uids = IDVEC_INIT, have_gids = IDVEC_INIT;
+ struct argp argp = { 0, 0, 0, doc, child_argps };
+
+ frobauth.require_ids = 1;
+
+ /* Parse our command line. This shouldn't ever return an error. */
+ argp_parse (&argp, argc, argv, 0, 0, &frobauth);
+
+ /* See what the invoking user is authorized to do. */
+ err = get_nonsugid_ids (&have_uids, &have_gids);
+ if (err)
+ error (52, err, "Cannot get invoking authentication");
+
+ /* Check passwords. */
+ err = ugids_verify_make_auth (&frobauth.ugids, &have_uids, &have_gids, 0, 0,
+ 0, 0, &auth);
+ if (err == EACCES)
+ error (15, 0, "Invalid password");
+ else if (err)
+ error (16, err, "Authentication failure");
+
+ if (frobauth.verbose)
+ /* A string showing which ids we will add. */
+ ids_rep = ugids_rep (&frobauth.ugids, 1, 1, 0, 0, 0);
+
+ /* Add the new authentication to each process. */
+ for (i = 0; i < frobauth.num_pids; i++)
+ {
+ mach_port_t msgport;
+ pid_t pid = frobauth.pids[i];
+ error_t err = proc_getmsgport (proc_server, pid, &msgport);
+
+ if (err)
+ error (0, err, "%d: Cannot get message port", pid);
+ else
+ {
+ if (! frobauth.dry_run)
+ err = msg_add_auth (msgport, auth);
+ if (err)
+ error (0, err, "%d: Cannot add authentication", pid);
+ else if (frobauth.verbose)
+ printf ("%d: Added %s\n", pid, ids_rep);
+ mach_port_deallocate (mach_task_self (), msgport);
+ }
+ }
+
+ return 0;
+}
diff --git a/utils/devprobe.c b/utils/devprobe.c
new file mode 100644
index 00000000..d7020322
--- /dev/null
+++ b/utils/devprobe.c
@@ -0,0 +1,112 @@
+/* Check the existence of mach devices
+
+ Copyright (C) 1996, 1997, 2002 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <argp.h>
+
+#include <hurd.h>
+#include <mach.h>
+#include <device/device.h>
+#include <version.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (devprobe);
+
+static const struct argp_option options[] = {
+ {"silent", 's', 0, 0, "Don't print devices found"},
+ {"quiet", 0, 0, OPTION_ALIAS},
+ {"first", 'f', 0, 0, "Stop after the first device found"},
+ {0}
+};
+static const char *args_doc = "DEVNAME...";
+static const char *doc = "Test for the existence of mach device DEVNAME..."
+ "\vThe exit status is 0 if any devices were found.";
+
+int
+main (int argc, char **argv)
+{
+ /* Print devices found? (otherwise only exit status matters) */
+ int print = 1;
+ /* If printing, print all devices on the command line that are found.
+ Otherwise, just the first one found is printed. */
+ int all = 1;
+ int found_one = 0;
+ mach_port_t device_master = MACH_PORT_NULL;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ error_t err;
+ device_t device;
+
+ case 's': case 'q':
+ /* Don't print any output. Since our exit status only reflects
+ whether any one of the devices exists, there's no point in
+ probing past the first one. */
+ print = all = 0; break;
+
+ case 'f':
+ all = 0; break;
+
+ case ARGP_KEY_ARG:
+ if (device_master == MACH_PORT_NULL)
+ {
+ err = get_privileged_ports (0, &device_master);
+ if (err)
+ argp_failure (state, 3, err, "Can't get device master port");
+ }
+
+ err = device_open (device_master, D_READ, arg, &device);
+ if (err == 0)
+ /* Got it. */
+ {
+ device_close (device);
+
+ /* Free the device port we got. */
+ mach_port_deallocate (mach_task_self (), device);
+
+ if (print)
+ puts (arg);
+
+ if (! all)
+ /* Only care about the first device found, so declare success
+ and... */
+ exit (0);
+
+ found_one = 1;
+ }
+ else if (err != ED_NO_SUCH_DEVICE)
+ /* Complain about unexpected errors. */
+ argp_failure (state, 0, err, "%s", arg);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ const struct argp argp = { options, parse_opt, args_doc, doc };
+
+ /* Parse our arguments. */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ exit (found_one ? 0 : 1);
+}
diff --git a/utils/fakeauth.c b/utils/fakeauth.c
new file mode 100644
index 00000000..590a421a
--- /dev/null
+++ b/utils/fakeauth.c
@@ -0,0 +1,443 @@
+/* fakeauth -- proxy auth server to lie to users about what their IDs are
+ Copyright (C) 2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <hurd.h>
+#include <hurd/auth.h>
+#include <hurd/interrupt.h>
+#include <hurd/ports.h>
+#include <idvec.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <argp.h>
+#include <error.h>
+#include "auth_S.h"
+#include "auth_request_U.h"
+#include "interrupt_S.h"
+
+#include "../auth/auth.h"
+
+/* Auth handles are server ports with sets of ids. */
+struct authhandle
+ {
+ struct port_info pi;
+ struct idvec euids, egids, auids, agids;
+ };
+
+struct port_bucket *auth_bucket;
+struct port_class *authhandle_portclass;
+
+/* Create a new auth port. */
+
+static error_t
+create_authhandle (struct authhandle **new)
+{
+ error_t err = ports_create_port (authhandle_portclass, auth_bucket,
+ sizeof **new, new);
+ if (! err)
+ bzero (&(*new)->euids, (void *) &(*new)[1] - (void *) &(*new)->euids);
+ return err;
+}
+
+/* Clean up a dead auth port. */
+
+static void
+destroy_authhandle (void *p)
+{
+ struct authhandle *h = p;
+ idvec_free_contents (&h->euids);
+ idvec_free_contents (&h->egids);
+ idvec_free_contents (&h->auids);
+ idvec_free_contents (&h->agids);
+}
+
+/* id management. */
+
+static inline void
+idvec_copyout (struct idvec *idvec, uid_t **ids, size_t *nids)
+{
+ if (idvec->num > *nids)
+ *ids = idvec->ids;
+ else
+ memcpy (*ids, idvec->ids, idvec->num * sizeof *ids);
+ *nids = idvec->num;
+}
+
+#define C(auth, ids) idvec_copyout (&auth->ids, ids, n##ids)
+#define OUTIDS(auth) (C (auth, euids), C (auth, egids), \
+ C (auth, auids), C (auth, agids))
+
+/* Implement auth_getids as described in <hurd/auth.defs>. */
+kern_return_t
+S_auth_getids (struct authhandle *auth,
+ uid_t **euids,
+ size_t *neuids,
+ uid_t **auids,
+ size_t *nauids,
+ uid_t **egids,
+ size_t *negids,
+ uid_t **agids,
+ size_t *nagids)
+{
+ if (! auth)
+ return EOPNOTSUPP;
+
+ OUTIDS (auth);
+
+ return 0;
+}
+
+/* Implement auth_makeauth as described in <hurd/auth.defs>. */
+kern_return_t
+S_auth_makeauth (struct authhandle *auth,
+ mach_port_t *authpts, size_t nauths,
+ uid_t *euids, size_t neuids,
+ uid_t *auids, size_t nauids,
+ uid_t *egids, size_t negids,
+ uid_t *agids, size_t nagids,
+ mach_port_t *newhandle)
+{
+ struct authhandle *newauth, *auths[1 + nauths];
+ int hasroot = 0;
+ error_t err;
+ size_t i, j;
+
+ if (!auth)
+ return EOPNOTSUPP;
+
+ auths[0] = auth;
+
+ /* Fetch the auth structures for all the ports passed in. */
+ for (i = 0; i < nauths; i++)
+ auths[i + 1] = auth_port_to_handle (authpts[i]);
+
+ ++nauths;
+
+ /* Verify that the union of the handles passed in either contains euid 0
+ (root), or contains all the requested ids. */
+
+#define isuid(uid, auth) \
+ (idvec_contains (&(auth)->euids, uid) \
+ || idvec_contains (&(auth)->auids, uid))
+#define groupmember(gid, auth) \
+ (idvec_contains (&(auth)->egids, gid) \
+ || idvec_contains (&(auth)->agids, gid))
+#define isroot(auth) isuid (0, auth)
+
+ for (i = 0; i < nauths; i++)
+ if (auths[i] && isroot (auths[i]))
+ {
+ hasroot = 1;
+ break;
+ }
+
+ if (!hasroot)
+ {
+ int has_it;
+
+ for (i = 0; i < neuids; i++)
+ {
+ has_it = 0;
+ for (j = 0; j < nauths; j++)
+ if (auths[j] && isuid (euids[i], auths[j]))
+ {
+ has_it = 1;
+ break;
+ }
+ if (!has_it)
+ goto eperm;
+ }
+
+ for (i = 0; i < nauids; i++)
+ {
+ has_it = 0;
+ for (j = 0; j < nauths; j++)
+ if (auths[j] && isuid (auids[i], auths[j]))
+ {
+ has_it = 1;
+ break;
+ }
+ if (!has_it)
+ goto eperm;
+ }
+
+ for (i = 0; i < negids; i++)
+ {
+ has_it = 0;
+ for (j = 0; j < nauths; j++)
+ if (auths[j] && groupmember (egids[i], auths[j]))
+ {
+ has_it = 1;
+ break;
+ }
+ if (!has_it)
+ goto eperm;
+ }
+
+ for (i = 0; i < nagids; i++)
+ {
+ has_it = 0;
+ for (j = 0; j < nauths; j++)
+ if (auths[j] && groupmember (agids[i], auths[j]))
+ {
+ has_it = 1;
+ break;
+ }
+ if (!has_it)
+ goto eperm;
+ }
+ }
+
+ err = create_authhandle (&newauth);
+
+ /* Create a new handle with the specified ids. */
+
+#define MERGE S (euids); S (egids); S (auids); S (agids);
+#define S(uids) if (!err) err = idvec_merge_ids (&newauth->uids, uids, n##uids)
+
+ MERGE;
+
+#undef S
+
+ if (! err)
+ {
+ for (j = 1; j < nauths; ++j)
+ mach_port_deallocate (mach_task_self (), authpts[j - 1]);
+ *newhandle = ports_get_right (newauth);
+ ports_port_deref (newauth);
+ }
+
+ for (j = 1; j < nauths; j++)
+ if (auths[j])
+ ports_port_deref (auths[j]);
+ return err;
+
+ eperm:
+ for (j = 1; j < nauths; j++)
+ if (auths[j])
+ ports_port_deref (auths[j]);
+ return EPERM;
+}
+
+
+/* This is our original auth port, where we will send all proxied
+ authentication requests. */
+static auth_t real_auth_port;
+
+kern_return_t
+S_auth_user_authenticate (struct authhandle *userauth,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mach_port_t rendezvous,
+ mach_port_t *newport,
+ mach_msg_type_name_t *newporttype)
+{
+ if (! userauth)
+ return EOPNOTSUPP;
+
+ if (rendezvous == MACH_PORT_DEAD) /* Port died in transit. */
+ return EINVAL;
+
+ return auth_user_authenticate_request (real_auth_port, reply, reply_type,
+ rendezvous, MACH_MSG_TYPE_MOVE_SEND)
+ ? : MIG_NO_REPLY;
+}
+
+kern_return_t
+S_auth_server_authenticate (struct authhandle *serverauth,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mach_port_t rendezvous,
+ mach_port_t newport,
+ mach_msg_type_name_t newport_type,
+ uid_t **euids,
+ size_t *neuids,
+ uid_t **auids,
+ size_t *nauids,
+ uid_t **egids,
+ size_t *negids,
+ uid_t **agids,
+ size_t *nagids)
+{
+ if (! serverauth)
+ return EOPNOTSUPP;
+
+ if (rendezvous == MACH_PORT_DEAD) /* Port died in transit. */
+ return EINVAL;
+
+ return auth_server_authenticate_request (real_auth_port,
+ reply, reply_type,
+ rendezvous, MACH_MSG_TYPE_MOVE_SEND,
+ newport, newport_type)
+ ? : MIG_NO_REPLY;
+}
+
+kern_return_t
+S_interrupt_operation (mach_port_t port, mach_port_seqno_t seqno)
+{
+ return interrupt_operation (real_auth_port, 0);
+}
+
+#include "../libports/notify_S.h"
+
+static int
+auth_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ mig_routine_t routine;
+ if ((routine = auth_server_routine (inp)) ||
+ (routine = interrupt_server_routine (inp)) ||
+ (routine = ports_notify_server_routine (inp)))
+ {
+ (*routine) (inp, outp);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static void *
+handle_auth_requests (void *ignored)
+{
+ while (1)
+ ports_manage_port_operations_multithread (auth_bucket, auth_demuxer,
+ 30 * 1000, 0, 0);
+ return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ struct authhandle *firstauth;
+ auth_t authport;
+ pid_t child;
+ pthread_t thread;
+ int status;
+ int argi;
+
+ /* Parse our options. */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ return EINVAL;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = { 0, parse_opt, "COMMAND...", "\
+Run COMMAND with a fake authentication handle that claims to be root or \
+any arbitrary identity derived from that handle, but in fact is always just \
+a proxy for your real authentication handle. This means that all processes \
+created by the COMMAND will have your privileges, even though it may \
+believe it has restricted them to different identities or no identity at all.\
+" };
+
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &argi, 0);
+
+ auth_bucket = ports_create_bucket ();
+ authhandle_portclass = ports_create_class (&destroy_authhandle, 0);
+
+ /* Create the initial root auth handle. */
+ err = create_authhandle (&firstauth);
+ assert_perror (err);
+ idvec_add (&firstauth->euids, 0);
+ idvec_add (&firstauth->auids, 0);
+ idvec_add (&firstauth->auids, 0);
+ idvec_merge (&firstauth->egids, &firstauth->euids);
+ idvec_merge (&firstauth->agids, &firstauth->auids);
+
+ /* Get a send right for it. */
+ authport = ports_get_right (firstauth);
+ err = mach_port_insert_right (mach_task_self (), authport, authport,
+ MACH_MSG_TYPE_MAKE_SEND);
+ assert_perror (err);
+ ports_port_deref (firstauth);
+
+ /* Stash our original auth port for later use. */
+ real_auth_port = getauth ();
+
+ /* Start handling auth requests on our fake handles. */
+ err = pthread_create (&thread, NULL, &handle_auth_requests, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+
+ /* Now we start faking ourselves out. This will immediately
+ reauthenticate all our descriptors through our proxy auth port.
+ The POSIXoid calls we make below will continue to use the fake
+ port and pass it on to the child. */
+ if (setauth (authport))
+ error (2, errno, "Cannot switch to fake auth handle");
+ mach_port_deallocate (mach_task_self (), authport);
+
+ /* We cannot use fork because it doesn't do the right thing with our send
+ rights that point to our own receive rights, i.e. the new auth port.
+ Since posix_spawn might be implemented with fork (prior to glibc 2.3),
+ we cannot use that simple interface either. We use _hurd_exec
+ directly to effect what posix_spawn does in the simple case. */
+ {
+ task_t newtask;
+ process_t proc;
+ file_t execfile = file_name_lookup (argv[argi], O_EXEC, 0);
+ if (execfile == MACH_PORT_NULL)
+ error (3, errno, "%s", argv[argi]);
+
+ err = task_create (mach_task_self (),
+#ifdef KERN_INVALID_LEDGER
+ NULL, 0, /* OSF Mach */
+#endif
+ 0, &newtask);
+ if (err)
+ error (3, err, "cannot create child task");
+ child = task2pid (newtask);
+ if (child < 0)
+ error (3, errno, "task2pid");
+ proc = getproc ();
+ err = proc_child (proc, newtask);
+ mach_port_deallocate (mach_task_self (), proc);
+ if (err)
+ error (3, err, "proc_child");
+
+ err = _hurd_exec (newtask, execfile, &argv[argi], environ);
+ mach_port_deallocate (mach_task_self (), newtask);
+ mach_port_deallocate (mach_task_self (), execfile);
+ if (err)
+ error (3, err, "cannot execute %s", argv[argi]);
+ }
+
+ if (waitpid (child, &status, 0) != child)
+ error (4, errno, "waitpid on %d", child);
+
+ if (WIFSIGNALED (status))
+ error (WTERMSIG (status) + 128, 0,
+ "%s for child %d", strsignal (WTERMSIG (status)), child);
+ if (WEXITSTATUS (status) != 0)
+ error (WEXITSTATUS (status), 0,
+ "Error %d for child %d", WEXITSTATUS (status), child);
+
+ return 0;
+}
diff --git a/utils/fakeroot.sh b/utils/fakeroot.sh
new file mode 100644
index 00000000..6993365d
--- /dev/null
+++ b/utils/fakeroot.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+# Execute a command in an environment where it appears to be root.
+#
+# Copyright (C) 2002, 2013 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+USAGE="Usage: $0 [OPTION...] [COMMAND...]"
+DOC="Execute COMMAND in an environment where it appears to be root."
+
+while :; do
+ case "$1" in
+ --help|"-?")
+ echo "$USAGE"
+ echo "$DOC"
+ echo ""
+ echo " -?, --help Give this help list"
+ echo " --usage Give a short usage message"
+ echo " -V, --version Print program version"
+ exit 0;;
+ --usage)
+ echo "Usage: $0 [-V?] [--help] [--usage] [--version]"
+ exit 0;;
+ --version|-V)
+ echo "STANDARD_HURD_VERSION_fakeroot_"; exit 0;;
+ --)
+ shift
+ break;;
+ -*)
+ echo 1>&2 "$0: unrecognized option \`$1'"
+ echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information";
+ exit 1;;
+ *)
+ break;;
+ esac
+done
+
+if [ $# -eq 0 ]; then
+ set -- ${SHELL:-/bin/sh}
+fi
+
+# We exec settrans, which execs the "fakeauth" command in the chroot context.
+# The `pwd` is evaluated here and now, and that result interpreted inside
+# the shell running under fakeauth to chdir there inside the chroot world.
+# That shell then execs our arguments as a command line.
+exec /bin/settrans --chroot \
+ /bin/fakeauth \
+ /bin/sh -c 'cd "$1" || exit ; shift ; exec "$@"' \
+ "$1" "$PWD" "$@" \
+ -- / /hurd/fakeroot
diff --git a/utils/frobauth-mod.c b/utils/frobauth-mod.c
new file mode 100644
index 00000000..4464c18c
--- /dev/null
+++ b/utils/frobauth-mod.c
@@ -0,0 +1,162 @@
+/* Modify the current authentication selected processes
+
+ Copyright (C) 1997, 1999, 2000 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <hurd.h>
+#include <error.h>
+
+#include "frobauth.h"
+
+/* For every pid in FROBAUTH, call MODIFY to change its argument UGIDS from
+ the current authentication to what it should be; CHANGE is whatever ids
+ the user specified. AUTHS, of length NUM_AUTHS, should be a vector of
+ auth ports giving whatever additional authentication is needed (besides
+ the process's current authentication). If the user specifies the
+ --verbose flags, PRINT_INFO is called after successfully installing the
+ new authentication in each process, to print a message about what
+ happened. True is returned if no errors occur, although most errors do
+ not cause termination, and error messages are printed for them. */
+error_t
+frobauth_modify (struct frobauth *frobauth,
+ const auth_t *auths, size_t num_auths,
+ error_t (*modify) (struct ugids *ugids,
+ const struct ugids *change,
+ pid_t pid, void *hook),
+ void (*print_info) (const struct ugids *new,
+ const struct ugids *old,
+ const struct ugids *change,
+ pid_t pid, void *hook),
+ void *hook)
+{
+ int i;
+ int ok = 1;
+ size_t num_all_auths = num_auths + 1;
+ auth_t all_auths[num_all_auths];
+ pid_t cur_pid = getpid ();
+ process_t proc_server = getproc ();
+
+ bcopy (auths, all_auths, num_auths * sizeof *auths);
+
+ /* Map MODIFY over all pids. */
+ for (i = 0; i < frobauth->num_pids; i++)
+ if (frobauth->pids[i] != cur_pid)
+ {
+ mach_port_t msgport;
+ pid_t pid = frobauth->pids[i];
+ error_t err = proc_getmsgport (proc_server, pid, &msgport);
+
+ if (err)
+ error (0, err, "%d: Cannot get message port", pid);
+ else
+ {
+ task_t task;
+
+ err = proc_pid2task (proc_server, pid, &task);
+ if (err)
+ error (0, err, "%d", pid);
+ else
+ {
+ auth_t old_auth;
+
+ err = msg_get_init_port (msgport, task, INIT_PORT_AUTH,
+ &old_auth);
+ if (err)
+ error (0, err, "%d: Cannot get auth port", pid);
+ else
+ {
+ struct ugids old = UGIDS_INIT;
+
+ err = ugids_merge_auth (&old, old_auth);
+
+ if (err)
+ error (0, err, "%d: Cannot get auth port ids", pid);
+ else
+ {
+ struct ugids new = UGIDS_INIT;
+
+ /* Assume all gids that can be implied by some uid are
+ only present for that reason. */
+ ugids_imply_all (&old);
+
+ err = ugids_set (&new, &old);
+
+ err = (*modify) (&new, &frobauth->ugids, pid, hook);
+ if (err)
+ error (99, err, "%d: Cannot modify ids", pid);
+ else if (! ugids_equal (&new, &old))
+ {
+ if (! frobauth->dry_run)
+ {
+ auth_t new_auth;
+
+ /* Add the PID's old authentication to that
+ supplied by the calller. */
+ all_auths[num_all_auths - 1] = old_auth;
+
+ err = ugids_make_auth (&new,
+ all_auths,
+ num_all_auths,
+ &new_auth);
+ if (err)
+ error (0, err,
+ "%d: Authentication failure", pid);
+ else
+ {
+ err =
+ msg_set_init_port (msgport, task,
+ INIT_PORT_AUTH,
+ new_auth,
+ MACH_MSG_TYPE_COPY_SEND);
+ mach_port_deallocate (mach_task_self (),
+ new_auth);
+ if (err)
+ error (0, err, "%d", pid);
+ }
+
+ }
+
+ if (frobauth->verbose && !err)
+ (*print_info) (&new, &old, &frobauth->ugids,
+ pid, hook);
+
+ }
+ else if (frobauth->verbose)
+ printf ("%d: Nothing changed\n", pid);
+
+ ugids_fini (&new);
+ }
+
+ ugids_fini (&old);
+ mach_port_deallocate (mach_task_self (), old_auth);
+ }
+ mach_port_deallocate (mach_task_self (), task);
+ }
+ mach_port_deallocate (mach_task_self (), msgport);
+ }
+
+ if (err)
+ ok = 0; /* Something didn't work. */
+ }
+
+ return ok;
+}
diff --git a/utils/frobauth.c b/utils/frobauth.c
new file mode 100644
index 00000000..44690d82
--- /dev/null
+++ b/utils/frobauth.c
@@ -0,0 +1,282 @@
+/* Common interface for auth frobbing utilities
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This file is rather a mess of intertwined argps; it shoud be redone as a
+ single level once argp can handle dynamic option frobbing. XXX */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <hurd.h>
+#include <hurd/process.h>
+
+#include "frobauth.h"
+#include "ugids.h"
+#include "pids.h"
+
+static struct argp common_argp;
+static struct argp fr_ugids_argp;
+
+static const struct argp_option common_options[] =
+{
+ {"verbose", 'v', 0, 0, "Print informational messages"},
+ {"dry-run", 'n', 0, 0, "Don't do anything, just print what would be done"},
+ { 0 }
+};
+static struct argp_child common_child_argps[] =
+{
+ { &pids_argp, 0, "Process selection:" },
+ { 0 }
+};
+
+static const char common_args_doc[] = "USER...";
+static const char common_doc[] =
+ "\vBy default, all processes in the current login collection are selected";
+
+static struct argp_child ugids_child_argps[] =
+{
+ { &ugids_argp, 0, "User/group ids:" },
+ { 0 }
+};
+
+/* An argp on top of the base frobauth argp that provides switchable
+ effective/available ids (XXX this junk should be moved into a single argp
+ [indeed, into ugids_argp] once argp can deal with the init routine
+ frobbing the argp source). */
+static const struct argp_option ea_options[] =
+{
+ {"available", 'a', 0, 0, "USER... specifies available ids"},
+ {"effective", 'e', 0, 0, "USER... specifies effective ids"},
+ { 0 }
+};
+
+static struct argp_child ea_posix_child_argps[] =
+{
+ { &common_argp },
+ { &fr_ugids_argp },
+ { 0 }
+};
+
+static struct argp_child no_ugids_child_argps[] =
+{
+ { &common_argp },
+ { 0 }
+};
+
+/* This holds state information that's only active during parsing. */
+struct frobauth_argp_state
+{
+ struct frobauth *frobauth;
+ struct pids_argp_params pids_argp_params;
+ struct ugids_argp_params ugids_argp_params;
+};
+
+static error_t
+common_parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct frobauth_argp_state *fs = state->input;
+ struct frobauth *frobauth = fs->frobauth;
+
+ switch (key)
+ {
+ case 'v':
+ frobauth->verbose = 1; break;
+ case 'n':
+ frobauth->dry_run = 1; break;
+
+ case ARGP_KEY_END:
+ if (frobauth->num_pids == 0)
+ /* No pids specified! By default, do the current login collection. */
+ {
+ pid_t lid;
+ error_t err = proc_getloginid (getproc (), getpid (), &lid);
+
+ if (err)
+ argp_failure (state, 2, err,
+ "Couldn't get current login collection");
+
+ err = add_fn_pids (&frobauth->pids, &frobauth->num_pids,
+ lid, proc_getloginpids);
+ if (err)
+ argp_failure (state, 3, err,
+ "%d: Couldn't get login collection pids", lid);
+
+ return err;
+ }
+ break;
+
+ case ARGP_KEY_INIT:
+ bzero (fs, sizeof *fs);
+ fs->frobauth = frobauth;
+ fs->pids_argp_params.pids = &frobauth->pids;
+ fs->pids_argp_params.num_pids = &frobauth->num_pids;
+ state->child_inputs[0] = &fs->pids_argp_params;
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ case ARGP_KEY_ERROR:
+ free (fs);
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static error_t
+ugids_parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct frobauth_argp_state *fs = state->input;
+ struct frobauth *frobauth = fs->frobauth;
+
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ fs->ugids_argp_params.ugids = &frobauth->ugids;
+ fs->ugids_argp_params.parse_user_args = 1;
+ fs->ugids_argp_params.default_user = frobauth->default_user;
+ fs->ugids_argp_params.require_ids = frobauth->require_ids;
+ fs->pids_argp_params.pids = &frobauth->pids;
+ fs->pids_argp_params.num_pids = &frobauth->num_pids;
+
+ state->child_inputs[0] = &fs->ugids_argp_params;
+
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static error_t
+ea_parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct frobauth_argp_state *fs = state->hook;
+
+ switch (key)
+ {
+ case 'a':
+ fs->ugids_argp_params.user_args_are_available = 1;
+ break;
+ case 'e':
+ fs->ugids_argp_params.user_args_are_effective = 1;
+ break;
+
+ case ARGP_KEY_ARG:
+ if (!fs->ugids_argp_params.user_args_are_effective
+ && !fs->ugids_argp_params.user_args_are_available)
+ /* Default to effective. */
+ fs->ugids_argp_params.user_args_are_effective = 1;
+
+ /* Let someone else parse the arg. */
+ return ARGP_ERR_UNKNOWN;
+
+ case ARGP_KEY_INIT:
+ /* Initialize inputs for child parsers. */
+ fs = state->hook = malloc (sizeof (struct frobauth_argp_state));
+ if (! fs)
+ return ENOMEM;
+
+ fs->frobauth = state->input;
+ state->child_inputs[0] = fs; /* Pass our state to the common parser. */
+ state->child_inputs[1] = fs; /* Pass our state to the common parser. */
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static error_t
+posix_parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct frobauth_argp_state *fs;
+
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ /* Initialize inputs for child parsers. */
+ fs = state->hook = malloc (sizeof (struct frobauth_argp_state));
+ if (! fs)
+ return ENOMEM;
+
+ fs->frobauth = state->input;
+ state->child_inputs[0] = fs; /* Pass our state to the common parser. */
+ state->child_inputs[1] = fs; /* Pass our state to the common parser. */
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static error_t
+no_ugids_parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct frobauth_argp_state *fs;
+
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ /* Initialize inputs for child parsers. */
+ fs = state->hook = malloc (sizeof (struct frobauth_argp_state));
+ if (! fs)
+ return ENOMEM;
+
+ fs->frobauth = state->input;
+ state->child_inputs[0] = fs; /* Pass our state to the common parser. */
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static struct argp common_argp =
+{
+ common_options, common_parse_opt, 0, common_doc, common_child_argps
+};
+static struct argp fr_ugids_argp =
+{
+ 0, ugids_parse_opt, 0, 0, ugids_child_argps
+};
+
+/* Parse frobauth args/options, where user args are added as single ids to
+ either the effective or available ids. */
+struct argp frobauth_ea_argp =
+{
+ ea_options, ea_parse_opt, 0, 0, ea_posix_child_argps
+};
+
+/* Parse frobauth args/options, where user args are added as posix user. */
+struct argp frobauth_posix_argp =
+{
+ 0, posix_parse_opt, 0, 0, ea_posix_child_argps
+};
+
+/* Parse frobauth args/options, where user args are added as posix user. */
+struct argp frobauth_no_ugids_argp =
+{
+ 0, no_ugids_parse_opt, 0, 0, no_ugids_child_argps
+};
diff --git a/utils/frobauth.doc b/utils/frobauth.doc
new file mode 100644
index 00000000..e4d1358f
--- /dev/null
+++ b/utils/frobauth.doc
@@ -0,0 +1,83 @@
+ -- Hurd process authentication frobbing commands --
+
+addauth -- Adds additional authority to selected processes, without changing
+ their identity (unless they previously had none)
+rmauth -- Removes authority
+setauth -- Changes the identity and authority of selected processes
+su -- Changes the identity and authority of selected processes, saving enough
+ authority to later undo the change
+unsu -- Attempts to undo the results of a previous su command
+
+Examples:
+
+As these commands effective existing processes rather than creating
+subshells, the following are all typed to the same shell.
+
+Starting with the ids I get from logging in as miles (the `ids' command shows
+all the ids in the process it was invoked from):
+
+ (utils) ids -tn
+ euids=miles egids=10 auids=miles,miles agids=10,10
+
+Note that first euid/egids is the traditional unix effective uid/gid, and,
+for instance, determines what identity files are created with; the 1st and
+2nd auids/agids are the posix `real' and `saved' ids. Now I add root
+authority:
+
+ (utils) addauth root
+ Password:
+ (utils) ids -tn
+ euids=miles,root egids=10,wheel auids=miles,miles agids=10,10
+
+The main id is still miles, but an effective root id is also present, meaning
+that the process has root privileges. The traditional `id' command hasn't
+yet been changed to print extended hurd ids, so it only knows about the
+additional group:
+
+ (utils) id
+ uid=9427(miles) gid=10 groups=10,0(wheel)
+
+Removing root puts us back where we started:
+
+ (utils) rmauth root
+ (utils) ids -tn
+ euids=miles egids=10 auids=miles,miles agids=10,10
+
+Now if we use su instead, it actually changes our process's identity (but
+note that the old ids are still around as available ids -- this means they
+the only privilege they grant is to become effective ids):
+
+ (utils) su
+ Password:
+ (utils) ids -tn
+ euids=root egids=wheel auids=root,root,miles,miles agids=wheel,wheel,10,10
+ (utils) id
+ uid=0(root) gid=0(wheel) groups=0(wheel)
+
+We can undo the su with unsu:
+
+ (utils) unsu
+ (utils) ids -tn
+ euids=miles egids=10 auids=miles,miles agids=10,10
+
+Now lets su again, to a different user:
+
+ (utils) su thomas
+ Password:
+ (utils) ids -tn
+ euids=thomas egids=11 auids=thomas,thomas,miles,miles agids=11,11,10,10
+
+If we now use another su command, instead of su, we can swap our identity;
+we don't need a password to do this, since the old ids are still there as
+available ids.
+
+ (utils) su miles
+ (utils) ids -tn
+ euids=miles egids=10 auids=miles,miles,thomas,thomas agids=10,10,11,11
+
+Now if we give unsu, we'll become thomas for good (this same effect may be
+had in one step with the `su --no-save' or `setauth' commands):
+
+ (utils) unsu
+ (utils) ids -tn
+ euids=thomas egids=11 auids=thomas,thomas agids=11,11
diff --git a/utils/frobauth.h b/utils/frobauth.h
new file mode 100644
index 00000000..2b9c78f1
--- /dev/null
+++ b/utils/frobauth.h
@@ -0,0 +1,71 @@
+/* Common interface for auth frobbing utilities
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __FROBAUTH_H__
+#define __FROBAUTH_H__
+
+#include <sys/types.h>
+#include <ugids.h>
+#include <argp.h>
+
+/* Structure describing which processes to frob, and how to frob them. */
+struct frobauth
+{
+ struct ugids ugids;
+ pid_t *pids;
+ size_t num_pids;
+ int verbose, dry_run; /* User options */
+ uid_t default_user; /* If none specified; -1 means none. */
+ int require_ids; /* If true, require some ids be specified. */
+};
+
+#define FROBAUTH_INIT { UGIDS_INIT, 0, 0, 0, 0, -1 }
+
+/* For every pid in FROBAUTH, call MODIFY to change its argument UGIDS from
+ the current authentication to what it should be; CHANGE is whatever ids
+ the user specified. AUTHS, of length NUM_AUTHS, should be a vector of
+ auth ports giving whatever additional authentication is needed (besides
+ the process's current authentication). If the user specifies the
+ --verbose flags, PRINT_INFO is called after successfully installing the
+ new authentication in each process, to print a message about what
+ happened. True is returned if no errors occur, although most errors do
+ not cause termination, and error messages are printed for them. */
+error_t frobauth_modify (struct frobauth *frobauth,
+ const auth_t *auths, size_t num_auths,
+ error_t (*modify) (struct ugids *ugids,
+ const struct ugids *change,
+ pid_t pid, void *hook),
+ void (*print_info) (const struct ugids *new,
+ const struct ugids *old,
+ const struct ugids *change,
+ pid_t pid, void *hook),
+ void *hook);
+
+/* Parse frobauth args/options, where user args are added as single ids to
+ either the effective or available ids. */
+extern struct argp frobauth_ea_argp;
+
+/* Parse frobauth args/options, where user args are added as posix user. */
+extern struct argp frobauth_posix_argp;
+
+/* Parse frobauth args/options, with no user specifications. */
+extern struct argp frobauth_no_ugids_argp;
+
+#endif /* __FROBAUTH_H__ */
diff --git a/utils/fsysopts.c b/utils/fsysopts.c
new file mode 100644
index 00000000..f3458a03
--- /dev/null
+++ b/utils/fsysopts.c
@@ -0,0 +1,128 @@
+/* Set options in a running filesystem
+
+ Copyright (C) 1995,96,97,98,2002,2004 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <argp.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <error.h>
+#include <argz.h>
+#include <version.h>
+
+#include <hurd/fsys.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (fsysopts);
+
+static struct argp_option options[] =
+{
+ {"dereference", 'L', 0, 0, "If FILESYS is a symbolic link, follow it"},
+ {"recursive", 'R', 0, 0, "Pass these options to any child translators"},
+ {0, 0, 0, 0}
+};
+static char *args_doc = "FILESYS [FS_OPTION...]";
+static char *doc = "Get or set command line options for running translator FILESYS."
+"\vThe legal values for FS_OPTION depends on FILESYS, but\
+ some common ones are: --readonly, --writable, --update, --sync[=INTERVAL],\
+ and --nosync.\n\nIf no options are supplied, FILESYS's existing options\
+ are printed";
+
+/* ---------------------------------------------------------------- */
+
+int
+main(int argc, char *argv[])
+{
+ error_t err;
+
+ /* The file we use as a handle to get FSYS. */
+ char *node_name = 0;
+ file_t node;
+
+ /* The filesystem options vector, in '\0' separated format. */
+ char *argz = 0;
+ size_t argz_len = 0;
+
+ int deref = 0, recursive = 0;
+
+ /* Parse a command line option. */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_ARG:
+ node_name = arg;
+ err = argz_create (state->argv + state->next, &argz, &argz_len);
+ if (err)
+ error(3, err, "Can't create options vector");
+ state->next = state->argc; /* stop parsing */
+ break;
+
+ case 'R': recursive = 1; break;
+ case 'L': deref = 1; break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ return EINVAL;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+ node = file_name_lookup (node_name, (deref ? 0 : O_NOLINK), 0666);
+ if (node == MACH_PORT_NULL)
+ error (1, errno, "%s", node_name);
+
+ if (argz_len)
+ {
+ /* The filesystem we're passing options to. */
+ fsys_t fsys;
+
+ /* Get the filesystem for NODE. */
+ err = file_getcontrol (node, &fsys);
+ if (err)
+ error (2, err, "%s", node_name);
+
+ err = fsys_set_options (fsys, argz, argz_len, recursive);
+ if (err)
+ {
+ argz_stringify (argz, argz_len, ' ');
+ error(5, err, "%s: %s", node_name, argz);
+ }
+ }
+ else
+ {
+ err = file_get_fs_options (node, &argz, &argz_len);
+ if (err)
+ error (5, err, "%s", node_name);
+ argz_stringify (argz, argz_len, ' ');
+ puts (argz);
+ }
+
+ return 0;
+}
diff --git a/utils/ftpcp.c b/utils/ftpcp.c
new file mode 100644
index 00000000..67ccb1a0
--- /dev/null
+++ b/utils/ftpcp.c
@@ -0,0 +1,397 @@
+/* Copy a file using the ftp protocol
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <error.h>
+#include <argp.h>
+#include <netdb.h>
+#include <fcntl.h>
+
+#include <version.h>
+
+#include <ftpconn.h>
+
+#define COPY_SZ 65536
+
+const char *argp_program_version = STANDARD_HURD_VERSION (ftpcp);
+
+#define OPT_SRC_U -3
+#define OPT_SRC_A -4
+#define OPT_SRC_P -5
+#define OPT_DST_U -6
+#define OPT_DST_A -7
+#define OPT_DST_P -8
+
+
+static struct argp_option options[] =
+{
+ {"user", 'u', "USER",0, "User to login as on both ftp servers"},
+ {"password", 'p', "PWD", 0, "USER's password"},
+ {"account", 'a', "ACCT",0, "Account to login as"},
+ {"src-user", OPT_SRC_U, "USER",0, "User to login as on the src ftp server"},
+ {"src-password",OPT_SRC_P, "PWD", 0, "The src USER's password"},
+ {"src-account", OPT_SRC_A, "ACCT",0, "Account to login as on the source server"},
+ {"dst-user", OPT_DST_U, "USER",0, "User to login as on the dst ftp server"},
+ {"dst-password",OPT_DST_P, "PWD", 0, "The dst USER's password"},
+ {"dst-account", OPT_DST_A, "ACCT",0, "Account to login as on the source server"},
+ {"debug", 'D', 0, 0, "Turn on debugging output for ftp connections"},
+ {0, 0}
+};
+static char *args_doc = "SRC [DST]";
+static char *doc = "Copy file SRC over ftp to DST."
+"\vBoth SRC and DST may have the form HOST:FILE, FILE, or -, where - is"
+" standard input for SRC or standard output for DST, and FILE is a local"
+" file. DST may be a directory, in which case the basename of SRC is"
+" appended to make the actual destination filename.";
+
+/* customization hooks. */
+static struct ftp_conn_hooks conn_hooks = { 0 };
+
+static void
+cntl_debug (struct ftp_conn *conn, int type, const char *txt)
+{
+ char *type_str;
+
+ switch (type)
+ {
+ case FTP_CONN_CNTL_DEBUG_CMD: type_str = ">"; break;
+ case FTP_CONN_CNTL_DEBUG_REPLY: type_str = "="; break;
+ default: type_str = "?"; break;
+ }
+
+ fprintf (stderr, "%s%s%s\n", (char *)conn->hook ?: "", type_str, txt);
+}
+
+/* Return an ftp connection for the host NAME using PARAMS. If an error
+ occurrs, a message is printed the program exits. If CNAME is non-zero,
+ the host's canonical name, in mallocated storage, is returned in it. */
+struct ftp_conn *
+get_host_conn (char *name, struct ftp_conn_params *params, char **cname)
+{
+ error_t err;
+ struct hostent *he;
+ struct ftp_conn *conn;
+
+ he = gethostbyname (name);
+ if (! he)
+ error (10, 0, "%s: %s", name, hstrerror (h_errno));
+
+ params->addr = malloc (he->h_length);
+ if (! params->addr)
+ error (11, ENOMEM, "%s", name);
+
+ bcopy (he->h_addr_list[0], params->addr, he->h_length);
+ params->addr_len = he->h_length;
+ params->addr_type = he->h_addrtype;
+
+ err = ftp_conn_create (params, &conn_hooks, &conn);
+ if (err)
+ error (12, err, "%s", he->h_name);
+
+ if (cname)
+ *cname = strdup (he->h_name);
+
+ return conn;
+}
+
+static void
+cp (int src, const char *src_name, int dst, const char *dst_name)
+{
+ ssize_t rd;
+ static void *copy_buf = 0;
+
+ if (! copy_buf)
+ {
+ copy_buf = valloc (COPY_SZ);
+ if (! copy_buf)
+ error (13, ENOMEM, "Cannot allocate copy buffer");
+ }
+
+ while ((rd = read (src, copy_buf, COPY_SZ)) > 0)
+ do
+ {
+ int wr = write (dst, copy_buf, rd);
+ if (wr < 0)
+ error (14, errno, "%s", dst_name);
+ rd -= wr;
+ }
+ while (rd > 0);
+
+ if (rd != 0)
+ error (15, errno, "%s", src_name);
+}
+
+struct epoint
+{
+ char *name; /* Name, of the form HOST:FILE, FILE, or -. */
+ char *file; /* If remote, the FILE portion, or 0. */
+ int fd; /* A file descriptor to use. */
+ struct ftp_conn *conn; /* An ftp connection to use. */
+ struct ftp_conn_params params;
+};
+
+static void
+econnect (struct epoint *e, struct ftp_conn_params *def_params, char *name)
+{
+ char *rmt;
+
+ if (! e->name)
+ e->name = "-";
+
+ rmt = strchr (e->name, ':');
+ if (rmt)
+ {
+ error_t err;
+
+ *rmt++ = 0;
+
+ if (! e->params.user)
+ e->params.user = def_params->user;
+ if (! e->params.pass)
+ e->params.pass = def_params->pass;
+ if (! e->params.acct)
+ e->params.acct = def_params->acct;
+
+ e->conn = get_host_conn (e->name, &e->params, &e->name);
+ e->name = realloc (e->name, strlen (e->name) + 1 + strlen (rmt) + 1);
+ if (! e->name)
+ error (22, ENOMEM, "Cannot allocate name storage");
+
+ e->conn->hook = name;
+
+ err = ftp_conn_set_type (e->conn, "I");
+ if (err)
+ error (23, err, "%s: Cannot set connection type to binary",
+ e->name);
+
+ strcat (e->name, ":");
+ strcat (e->name, rmt);
+
+ e->file = rmt;
+ }
+ else if (e->params.user || e->params.pass || e->params.acct)
+ error (20, 0,
+ "%s: Ftp login parameter specified for a local endpoint (%s,%s,%s)",
+ e->name, e->params.user, e->params.pass, e->params.acct);
+ else
+ e->file = strdup (e->name);
+}
+
+static error_t
+eopen_wr (struct epoint *e, int *fd)
+{
+ if (e->conn)
+ return ftp_conn_start_store (e->conn, e->file, fd);
+ else if (strcmp (e->name, "-") == 0)
+ *fd = 1;
+ else
+ {
+ *fd = open (e->name, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (*fd < 0)
+ return errno;
+ }
+ return 0;
+}
+
+static error_t
+eopen_rd (struct epoint *e, int *fd)
+{
+ if (e->conn)
+ return ftp_conn_start_retrieve (e->conn, e->file, fd);
+ else if (strcmp (e->name, "-") == 0)
+ *fd = 0;
+ else
+ {
+ *fd = open (e->name, O_RDONLY, 0666);
+ if (*fd < 0)
+ return errno;
+ }
+ return 0;
+}
+
+static void
+efinish (struct epoint *e)
+{
+ if (e->conn)
+ {
+ error_t err = ftp_conn_finish_transfer (e->conn);
+ if (err)
+ error (31, err, "%s", e->name);
+ }
+}
+
+/* Give a name which refers to a directory file, and a name in that
+ directory, this should return in COMPOSITE the composite name referring to
+ that name in that directory, in malloced storage. */
+error_t
+eappend (struct epoint *e,
+ const char *dir, const char *name,
+ char **composite)
+{
+ if (e->conn)
+ return ftp_conn_append_name (e->conn, dir, name, composite);
+ else
+ {
+ char *rval = malloc (strlen (dir) + 1 + strlen (name) + 1);
+
+ if (! rval)
+ return ENOMEM;
+
+ if (dir[0] == '/' && dir[1] == '\0')
+ stpcpy (stpcpy (rval, dir), name);
+ else
+ stpcpy (stpcpy (stpcpy (rval, dir), "/"), name);
+
+ *composite = rval;
+
+ return 0;
+ }
+}
+
+/* If the name of a file COMPOSITE is a composite name (containing both a
+ filename and a directory name), this function will return the name
+ component only in BASE, in malloced storage, otherwise it simply returns a
+ newly malloced copy of COMPOSITE in BASE. */
+error_t
+ebasename (struct epoint *e, const char *composite, char **base)
+{
+ if (e->conn)
+ return ftp_conn_basename (e->conn, composite, base);
+ else
+ {
+ *base = strdup (basename (composite));
+ return 0;
+ }
+}
+
+static void
+append_basename (struct epoint *dst, struct epoint *src)
+{
+ char *bname;
+ error_t err = ebasename (src, src->file, &bname);
+
+ if (err)
+ error (33, err, "%s: Cannot find basename", src->name);
+
+ err = eappend (dst, dst->file, bname, &dst->file);
+ if (err)
+ error (34, err, "%s: Cannot append name component", dst->name);
+
+ err = eappend (dst, dst->name, bname, &dst->name);
+ if (err)
+ error (35, err, "%s: Cannot append name component", dst->name);
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ struct epoint rd = { 0 }, wr = { 0 };
+ struct ftp_conn_params def_params = { 0 }; /* default params */
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_ARG:
+ switch (state->arg_num)
+ {
+ case 0: rd.name = arg; break;
+ case 1: wr.name = arg; break;
+ default: return ARGP_ERR_UNKNOWN;
+ }
+ break;
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+
+ case 'u': def_params.user = arg; break;
+ case 'p': def_params.pass = arg; break;
+ case 'a': def_params.acct = arg; break;
+
+ case OPT_SRC_U: rd.params.user = arg; break;
+ case OPT_SRC_P: rd.params.pass = arg; break;
+ case OPT_SRC_A: rd.params.acct = arg; break;
+
+ case OPT_DST_U: wr.params.user = arg; break;
+ case OPT_DST_P: wr.params.pass = arg; break;
+ case OPT_DST_A: wr.params.acct = arg; break;
+
+ case 'D': conn_hooks.cntl_debug = cntl_debug; break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ econnect (&rd, &def_params, "SRC");
+ econnect (&wr, &def_params, "DST");
+
+ if (rd.conn && wr.conn)
+ /* Both endpoints are remote; directly copy between ftp servers. */
+ {
+ err = ftp_conn_rmt_copy (rd.conn, rd.file, wr.conn, wr.file);
+ if (err == EISDIR)
+ /* The destination name is a directory; try again with the source
+ basename appended. */
+ {
+ append_basename (&wr, &rd);
+ err = ftp_conn_rmt_copy (rd.conn, rd.file, wr.conn, wr.file);
+ }
+ if (err)
+ error (30, err, "Remote copy");
+ }
+ else
+ /* One endpoint is local, so do the copying ourself. */
+ {
+ int rd_fd, wr_fd;
+
+ err = eopen_rd (&rd, &rd_fd);
+ if (err)
+ error (31, err, "%s", rd.name);
+
+ err = eopen_wr (&wr, &wr_fd);
+ if (err == EISDIR)
+ /* The destination name is a directory; try again with the source
+ basename appended. */
+ {
+ append_basename (&wr, &rd);
+ err = eopen_wr (&wr, &wr_fd);
+ }
+ if (err)
+ error (32, err, "%s", wr.name);
+
+ cp (rd_fd, rd.name, wr_fd, wr.name);
+
+ close (rd_fd);
+ close (wr_fd);
+
+ efinish (&rd);
+ efinish (&wr);
+ }
+
+ exit (0);
+}
diff --git a/utils/ftpdir.c b/utils/ftpdir.c
new file mode 100644
index 00000000..4ccb821d
--- /dev/null
+++ b/utils/ftpdir.c
@@ -0,0 +1,329 @@
+/* Get a directory listing using the ftp protocol
+
+ Copyright (C) 1997,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <unistd.h>
+#include <string.h>
+#include <error.h>
+#include <argp.h>
+#include <time.h>
+#include <netdb.h>
+
+#include <version.h>
+
+#include <ftpconn.h>
+
+#define COPY_SZ 65536
+
+const char *argp_program_version = STANDARD_HURD_VERSION (ftpdir);
+
+static struct argp_option options[] =
+{
+ {"user", 'u', "USER",0, "User to login as on ftp server"},
+ {"password", 'p', "PWD", 0, "USER's password"},
+ {"account", 'a', "ACCT",0, "Account to login as"},
+ {"separator",'S', "SEP", 0, "String to separate multiple listings"},
+ {"prefix", 'P', "PFX", 0, "String to proceed listings; the first and second"
+ " occurrences of %s are replace by HOST and DIR"},
+ {"host", 'h', "HOST",0, "Use HOST as a default host"},
+ {"debug", 'D', 0, 0, "Turn on debugging output for ftp connections"},
+ {"intepret", 'i', 0, 0, "Parse the directory output"},
+ {0, 0}
+};
+static char *args_doc = "[([HOST:]DIR | HOST:)...]";
+static char *doc = "Get a directory listing over ftp from HOST:DIR."
+"\vIf HOST is not supplied in an argument any default value set by --host is"
+" used; if DIR is not supplied, the default directory of HOST is used."
+"\nIf multiple DIRs are supplied on the command line, each listing is"
+" prefixed by a newline (or SEP) and a line containing HOST:DIR: (or PFX).";
+
+/* customization hooks. */
+static struct ftp_conn_hooks conn_hooks = { 0 };
+
+static void
+cntl_debug (struct ftp_conn *conn, int type, const char *txt)
+{
+ char *type_str;
+
+ switch (type)
+ {
+ case FTP_CONN_CNTL_DEBUG_CMD: type_str = "."; break;
+ case FTP_CONN_CNTL_DEBUG_REPLY: type_str = "="; break;
+ default: type_str = "?"; break;
+ }
+
+ fprintf (stderr, "%s%s\n", type_str, txt);
+}
+
+struct ftpdir_host
+{
+ char *name;
+ struct ftp_conn_params params;
+ struct ftp_conn *conn;
+ struct ftpdir_host *next;
+};
+
+/* Return an ftp connection for the host NAME using PARAMS, and add an entry
+ for it to *HOSTS. If a connection already exists in HOSTS, it is returned
+ instead of making a new one. If an error occurrs, a message is printed and
+ 0 is returned. */
+static struct ftpdir_host *
+get_host_conn (char *name, struct ftp_conn_params *params,
+ struct ftpdir_host **hosts)
+{
+ error_t err;
+ struct ftpdir_host *h;
+ struct hostent *he;
+
+ for (h = *hosts; h; h = h->next)
+ if (strcmp (h->name, name) == 0)
+ return h;
+
+ he = gethostbyname (name);
+ if (! he)
+ {
+ error (0, 0, "%s: %s", name, hstrerror (h_errno));
+ return 0;
+ }
+
+ for (h = *hosts; h; h = h->next)
+ if (he->h_addrtype == h->params.addr_type
+ && he->h_length == h->params.addr_len
+ && bcmp (he->h_addr_list[0], h->params.addr, he->h_length) == 0)
+ return h;
+
+ h = malloc (sizeof (struct ftpdir_host));
+ if (! h)
+ {
+ error (0, ENOMEM, "%s", name);
+ return 0;
+ }
+
+ h->params = *params;
+ h->params.addr = malloc (he->h_length);
+ h->name = strdup (he->h_name);
+
+ if (!h->name || !h->params.addr)
+ err = ENOMEM;
+ else
+ {
+ bcopy (he->h_addr_list[0], h->params.addr, he->h_length);
+ h->params.addr_len = he->h_length;
+ h->params.addr_type = he->h_addrtype;
+ err = ftp_conn_create (&h->params, &conn_hooks, &h->conn);
+ }
+
+ if (err)
+ {
+ error (0, err, "%s", he->h_name);
+ if (h->name)
+ free (h->name);
+ if (h->params.addr)
+ free (h->params.addr);
+ free (h);
+ return 0;
+ }
+
+ h->next = *hosts;
+ *hosts = h;
+
+ return h;
+}
+
+static int
+ftpdir (char *dir, struct ftpdir_host *host)
+{
+ int data;
+ int rd;
+ error_t err;
+ static void *copy_buf = 0;
+ struct ftp_conn *conn = host->conn;
+ char *host_name = host->name;
+
+ err = ftp_conn_start_dir (conn, dir, &data);
+ if (err)
+ {
+ error (0, err, "%s:%s", host_name, dir);
+ return err;
+ }
+
+ if (! copy_buf)
+ {
+ copy_buf = valloc (COPY_SZ);
+ if (! copy_buf)
+ error (12, ENOMEM, "Cannot allocate copy buffer");
+ }
+
+ while ((rd = read (data, copy_buf, COPY_SZ)) > 0)
+ do
+ {
+ int wr = write (1, copy_buf, rd);
+ if (wr < 0)
+ error (13, errno, "stdout");
+ rd -= wr;
+ }
+ while (rd > 0);
+ if (rd != 0)
+ {
+ error (0, errno, "%s:%s", host_name, dir);
+ return errno;
+ }
+
+ close (data);
+
+ err = ftp_conn_finish_transfer (conn);
+ if (err)
+ {
+ error (0, err, "%s:%s", host_name, dir);
+ return err;
+ }
+
+ return 0;
+}
+
+static error_t
+pdirent (const char *name, const struct stat *st, const char *symlink_target,
+ void *hook)
+{
+ char timebuf[20];
+ strftime (timebuf, sizeof timebuf, "%Y-%m-%d %H:%M", localtime (&st->st_mtime));
+ printf ("%6o %2d %5d %5d %6lld %s %s\n",
+ st->st_mode, st->st_nlink, st->st_uid, st->st_gid, st->st_size,
+ timebuf, name);
+ if (symlink_target)
+ printf (" -> %s\n",
+ symlink_target);
+ return 0;
+}
+
+static error_t
+ftpdir2 (char *dir, struct ftpdir_host *host)
+{
+ error_t err = ftp_conn_get_stats (host->conn, dir, 1, pdirent, 0);
+ if (err == ENOTDIR)
+ err = ftp_conn_get_stats (host->conn, dir, 0, pdirent, 0);
+ if (err)
+ error (0, err, "%s:%s", host->name, dir);
+ return err;
+}
+
+int
+main (int argc, char **argv)
+{
+ struct ftpdir_host *hosts = 0;
+ char *default_host = 0;
+ int interpret = 0;
+ struct ftp_conn_params params = { 0 };
+ char *sep = "\n";
+ char *pfx = "%s:%s:\n";
+ int use_pfx = 0;
+ int errs = 0;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_ARG:
+ {
+ char *host, *dir;
+
+ if (state->next < state->argc)
+ use_pfx = 1; /* Multiple arguments. */
+
+ dir = index (arg, ':');
+ if (dir)
+ {
+ host = arg;
+ *dir++ = '\0';
+ if (*host == '\0')
+ /* An argument of `:' */
+ host = default_host;
+ }
+ else
+ {
+ host = default_host;
+ dir = arg;
+ }
+
+ if (host)
+ {
+ struct ftpdir_host *h = get_host_conn (host, &params, &hosts);
+ if (h)
+ {
+ if (state->arg_num > 0)
+ fputs (sep, stdout);
+ if (use_pfx)
+ printf (pfx, h->name, dir);
+ if ((use_pfx && *pfx) || (state->arg_num > 0 && *sep))
+ fflush (stdout);
+ if (interpret)
+ errs |= ftpdir2 (dir, h);
+ else
+ errs |= ftpdir (dir, h);
+ }
+ errs = 1;
+ }
+ else
+ {
+ error (0, 0, "%s: No default host", arg);
+ errs = 1;
+ }
+ }
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ if (default_host)
+ {
+ struct ftpdir_host *h =
+ get_host_conn (default_host, &params, &hosts);
+ if (h)
+ errs |= ftpdir (0, h);
+ }
+ else
+ {
+ error (0, 0, "No default host");
+ errs = 1;
+ }
+ break;
+
+ return EINVAL;
+
+ case 'u': params.user = arg; break;
+ case 'p': params.pass = arg; break;
+ case 'a': params.acct = arg; break;
+ case 'h': default_host = arg; break;
+ case 'D': conn_hooks.cntl_debug = cntl_debug; break;
+ case 'P': pfx = arg; use_pfx = 1; break;
+ case 'S': sep = arg; break;
+ case 'i': interpret = 1; break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ if (errs)
+ exit (10);
+ else
+ exit (0);
+}
diff --git a/utils/gcore.c b/utils/gcore.c
new file mode 100644
index 00000000..3d72492c
--- /dev/null
+++ b/utils/gcore.c
@@ -0,0 +1,107 @@
+/* `gcore' program for GNU Hurd: write a core dump from a running process.
+ Copyright (C) 1992,2001,2002 Free Software Foundation, Inc.
+ Written by Roland McGrath.
+
+This file is part of the GNU Hurd.
+
+The GNU Hurd is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+The GNU Hurd is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <hurd.h>
+#include <hurd/crash.h>
+#include <hurd/paths.h>
+#include <fcntl.h>
+#include <argp.h>
+#include <error.h>
+#include <version.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (gcore);
+
+static const struct argp argp =
+{
+ NULL, NULL,
+ "PID...",
+ "Generate a core dump file from a running process"
+ "\vFor each PID, a core dump file \"core.PID\" is written."
+};
+
+int
+main (int argc, char **argv)
+{
+ int status = 0;
+ file_t crashserv;
+ int argi;
+
+ argp_parse (&argp, argc, argv, 0, &argi, 0);
+
+ crashserv = file_name_lookup (_SERVERS_CRASH, 0, 0);
+ if (crashserv == MACH_PORT_NULL)
+ error (1, errno, "cannot reach crash server: %s", _SERVERS_CRASH);
+
+ for (; argi < argc; ++argi)
+ {
+ char *end;
+ pid_t pid;
+ task_t task;
+
+ pid = strtol (argv[argi], &end, 10);
+ if (end == argv[argi] || *end != '\0')
+ {
+ error (0, 0, "cannot parse process ID: %s", argv[argi]);
+ status = 1;
+ continue;
+ }
+
+ task = pid2task ((pid_t) pid);
+ if (task == MACH_PORT_NULL)
+ {
+ error (0, errno, "pid2task: %d", pid);
+ status = 1;
+ }
+ else
+ {
+ file_t file;
+ char *name = 0;
+ asprintf (&name, "core.%d", pid);
+
+ file = file_name_lookup (name, O_WRONLY|O_CREAT,
+ 0600 &~ getumask ());
+ if (file == MACH_PORT_NULL)
+ {
+ error (0, errno, "cannot create %s", name);
+ status = 1;
+ }
+ else
+ {
+ error_t err = crash_dump_task (crashserv, task, file,
+ 0, 0, 0, 0, 0, 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TYPE_COPY_SEND);
+ mach_port_deallocate (mach_task_self (), file);
+ if (err)
+ {
+ (void) remove (name);
+ error (0, err, "crash_dump_task on %d to %s", pid, name);
+ status = 1;
+ }
+ free (name);
+ }
+ }
+ mach_port_deallocate (mach_task_self (), task);
+ }
+
+ return status;
+}
diff --git a/utils/ids.c b/utils/ids.c
new file mode 100644
index 00000000..f8ad22c5
--- /dev/null
+++ b/utils/ids.c
@@ -0,0 +1,196 @@
+/* Show all hurd ids
+
+ Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <argp.h>
+#include <unistd.h>
+#include <error.h>
+#include <ugids.h>
+#include <version.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (ids);
+
+static struct argp_option options[] =
+{
+ {"terse", 't', 0, 0, "Use a shorter one-line output format"},
+ {"effective", 'e', 0, 0, "Show effective ids"},
+ {"available", 'a', 0, 0, "Show available ids"},
+ {"uids", 'u', 0, 0, "Show user ids"},
+ {"gids", 'g', 0, 0, "Show group ids"},
+ {"names", 'n', 0, 0, "Show names of uids/gids"},
+ {"values", 'v', 0, 0, "Show numeric uids/gids"},
+ {0}
+};
+static char *args_doc = "[PID]";
+static char *doc = "Show hurd uids/gids."
+"\vIf PID is suppplied, show ids in that process.";
+
+/* ---------------------------------------------------------------- */
+
+int
+main(int argc, char *argv[])
+{
+ error_t err;
+ task_t task;
+ mach_port_t msgport;
+ int pid = -1;
+ auth_t auth = getauth ();
+ process_t proc = getproc ();
+ struct ugids ugids = UGIDS_INIT;
+ int show_eff = 0, show_avail = 0, show_uids = 0, show_gids = 0, terse = 0;
+ int show_names = 0, show_values = 0;
+
+ /* Print the given id vectors, using NAME for the prompt. */
+ void print_ids (struct idvec *uids, struct idvec *gids, char *name)
+ {
+ if (show_uids)
+ {
+ if (name && show_gids)
+ printf ("%s uids: ", name);
+ else if (show_gids)
+ printf ("uids: ");
+ else if (name)
+ printf ("%s: ", name);
+ printf ("%s\n",
+ idvec_uids_rep (uids, show_values, show_names, " "));
+ }
+ if (show_gids)
+ {
+ if (name && show_uids)
+ printf ("%s gids: ", name);
+ else if (show_uids)
+ printf ("gids: ");
+ else if (name)
+ printf ("%s: ", name);
+ printf ("%s\n", idvec_gids_rep (gids, show_values, show_names, " "));
+ }
+ }
+
+ /* Parse a command line option. */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'e': show_eff = 1; break;
+ case 'a': show_avail = 1; break;
+ case 'u': show_uids = 1; break;
+ case 'g': show_gids = 1; break;
+ case 'n': show_names = 1; break;
+ case 'v': show_values = 1; break;
+ case 't': terse = 1; break;
+ case ARGP_KEY_ARG:
+ if (state->arg_num == 0)
+ {
+ pid = atoi (arg);
+ break;
+ }
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ if (!show_eff && !show_avail)
+ show_eff = show_avail = 1;
+ if (!show_uids && !show_gids)
+ show_uids = show_gids = 1;
+ if (!show_names && !show_values)
+ show_names = show_values = 1;
+
+ if (pid < 0)
+ /* We get our parent's authentication instead of our own because this
+ program is usually installed setuid. This should work even if it's
+ not installed setuid, using the auth port as authentication to the
+ msg_get_init_port rpc. */
+ pid = getppid ();
+
+ /* Get a msgport for PID, to which we can send requests. */
+ err = proc_getmsgport (proc, pid, &msgport);
+ if (err)
+ error (5, err, "%d: Cannot get process msgport", pid);
+
+ /* Try to get the task port to use as authentication. */
+ err = proc_pid2task (proc, pid, &task);
+
+ /* Now fetch the auth port; if we couldn't get the task port to use for
+ authentication, we try the (old) auth port instead. */
+ if (err)
+ err = msg_get_init_port (msgport, auth, INIT_PORT_AUTH, &auth);
+ else
+ err = msg_get_init_port (msgport, task, INIT_PORT_AUTH, &auth);
+ if (err)
+ error (6, err, "%d: Cannot get process authentication", pid);
+
+ mach_port_deallocate (mach_task_self (), msgport);
+ mach_port_deallocate (mach_task_self (), task);
+
+ /* Get the ids that AUTH represents. */
+ err = ugids_merge_auth (&ugids, auth);
+ if (err)
+ error (10, err, "Cannot get authentication ids");
+
+ /* Print them. */
+ if (terse)
+ /* Short output format. */
+ {
+ /* Since we use ugids_rep to format the output, just clear any fields
+ we don't want to show. */
+ if (! show_eff)
+ {
+ idvec_clear (&ugids.eff_uids);
+ idvec_clear (&ugids.eff_gids);
+ }
+ if (! show_avail)
+ {
+ idvec_clear (&ugids.avail_uids);
+ idvec_clear (&ugids.avail_gids);
+ }
+ if (! show_uids)
+ {
+ idvec_clear (&ugids.eff_uids);
+ idvec_clear (&ugids.avail_uids);
+ }
+ if (! show_gids)
+ {
+ idvec_clear (&ugids.eff_gids);
+ idvec_clear (&ugids.avail_gids);
+ }
+ printf ("%s\n", ugids_rep (&ugids, show_values, show_names, 0, " ","="));
+ }
+ else
+ /* Long output format */
+ {
+ if (show_eff)
+ print_ids (&ugids.eff_uids, &ugids.eff_gids,
+ show_avail ? "effective" : 0);
+ if (show_avail)
+ print_ids (&ugids.avail_uids, &ugids.avail_gids,
+ show_eff ? "available" : 0);
+ }
+
+ return 0;
+}
diff --git a/utils/login.c b/utils/login.c
new file mode 100644
index 00000000..cad3b1ed
--- /dev/null
+++ b/utils/login.c
@@ -0,0 +1,895 @@
+/* Hurdish login
+
+ Copyright (C) 1995,96,97,98,99,2002 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <paths.h>
+#include <ctype.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <grp.h>
+#include <netdb.h>
+#include <time.h>
+#include <assert.h>
+#include <version.h>
+#include <sys/mman.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <sys/fcntl.h>
+
+#include <argp.h>
+#include <argz.h>
+#include <envz.h>
+#include <idvec.h>
+#include <error.h>
+#include <timefmt.h>
+#include <hurd/lookup.h>
+#include <ugids.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (login);
+
+extern error_t
+exec_reauth (auth_t auth, int secure, int must_reauth,
+ mach_port_t *ports, unsigned num_ports,
+ mach_port_t *fds, unsigned num_fds);
+extern error_t
+get_nonsugid_ids (struct idvec *uids, struct idvec *gids);
+
+/* Defaults for various login parameters. */
+char *default_args[] = {
+ "SHELL=/bin/bash",
+ /* A ':' separated list of what to try if can't exec user's shell. */
+ "BACKUP_SHELLS=/bin/bash:" _PATH_BSHELL,
+ "HOME=/etc/login", /* Initial WD. */
+ "USER=login",
+ "UMASK=0",
+ "NAME=Not logged in",
+ "HUSHLOGIN=.hushlogin", /* Looked up relative new home dir. */
+ "MOTD=/etc/motd",
+ "PATH=/bin",
+ "NOBODY=login",
+ "NOAUTH_TIMEOUT=300", /* seconds before unauthed sessions die. */
+ 0
+};
+/* Default values for the new environment. */
+char *default_env[] = {
+ "PATH=/bin",
+ 0
+};
+
+/* Which things are copied from the login parameters into the environment. */
+char *copied_args[] = {
+ "USER", "SHELL", "HOME", "NAME", "VIA", "VIA_ADDR", "PATH", 0
+};
+
+static struct argp_option options[] =
+{
+ {"arg0", '0', "ARG", 0, "Make ARG the shell's argv[0]"},
+ {"envvar", 'e', "ENTRY", 0, "Add ENTRY to the environment"},
+ {"envvar-default", 'E', "ENTRY", 0, "Use ENTRY as a default environment variable"},
+ {"no-args", 'x', 0, 0, "Don't put login args into the environment"},
+ {"arg", 'a', "ARG", 0, "Add login parameter ARG"},
+ {"arg-default", 'A', "ARG", 0, "Use ARG as a default login parameter"},
+ {"no-environment-args", 'X', 0, 0, "Don't add the parent environment as default login params"},
+ {"no-login", 'L', 0, 0, "Don't modify the shells argv[0] to look"
+ " like a login shell"},
+ {"preserve-environment", 'p', 0, 0, "Inherit the parent's environment"},
+ {"via", 'h', "HOST", 0, "This login is from HOST"},
+ {"no-passwd", 'f', 0, 0, "Don't ask for passwords"},
+ {"paranoid", 'P', 0, 0, "Don't admit that a user doesn't exist"},
+ {"save", 's', 0, 0, "Keep the old available ids, and save the old"
+ " effective ids as available ids"},
+ {"shell-from-args", 'S', 0, 0, "Use the first shell arg as the shell to invoke"},
+ {"retry", 'R', "ARG", OPTION_ARG_OPTIONAL,
+ "Re-exec login with no users after non-fatal errors; if ARG is supplied,"
+ "add it to the list of args passed to login when retrying"},
+ {0, 0}
+};
+static struct argp_child child_argps[] =
+{
+ { &ugids_argp, 0, "Adding individual user/group ids:" },
+ { 0 }
+};
+static char *args_doc = "[USER [ARG...]]";
+static char *doc =
+"Exec a program with uids and/or the environment changed appropriately.\v"
+"To give args to the shell without specifying a user, use - for USER.\n"
+"Current login parameters include HOME, SHELL, USER, NAME, and ROOT.";
+
+/* Outputs whatever can be read from the io_t NODE to standard output, and
+ then close it. If NODE is MACH_PORT_NULL, assumes an error happened, and
+ prints an error message using ERRNO and STR. */
+static void
+cat (mach_port_t node, char *str)
+{
+ error_t err;
+ if (node == MACH_PORT_NULL)
+ err = errno;
+ else
+ for (;;)
+ {
+ char buf[1024], *data = buf;
+ mach_msg_type_number_t data_len = sizeof (buf);
+
+ err = io_read (node, &data, &data_len, -1, 16384);
+ if (err || data_len == 0)
+ break;
+ else
+ {
+ write (0, data, data_len);
+ if (data != buf)
+ munmap (data, data_len);
+ }
+ }
+ if (err)
+ error (0, errno, "%s", str);
+}
+
+/* Add a utmp entry based on the parameters in ARGS & ARGS_LEN. If
+ INHERIT_HOST is true, the host parameters in ARGS aren't to be trusted, so
+ try to get the host from the existing utmp entry (this only works if
+ re-logging in during an existing session). */
+static void
+add_utmp_entry (char *args, unsigned args_len, int inherit_host)
+{
+ struct utmp utmp;
+ char const *host = 0;
+ long addr = 0;
+
+ bzero (&utmp, sizeof (utmp));
+
+ gettimeofday (&utmp.ut_tv, 0);
+ strncpy (utmp.ut_name, envz_get (args, args_len, "USER") ?: "",
+ sizeof (utmp.ut_name));
+
+ if (! inherit_host)
+ {
+ char *via_addr = envz_get (args, args_len, "VIA_ADDR");
+ host = envz_get (args, args_len, "VIA");
+ if (host && strlen (host) > sizeof (utmp.ut_host))
+ host = via_addr ?: host;
+ if (via_addr)
+ addr = inet_addr (via_addr);
+ }
+
+ if (!host || !addr)
+ /* Get the host from the `existing utmp entry'. This is a crock. */
+ {
+ int tty_fd = 0;
+ char *tty = 0;
+
+ /* Search for a file descriptor naming a tty. */
+ while (!tty && tty_fd < 3)
+ tty = ttyname (tty_fd++);
+ if (tty)
+ /* Find the old utmp entry for TTY, and grab its host parameters. */
+ {
+ struct utmp *old_utmp;
+ strncpy (utmp.ut_line, basename (tty), sizeof (utmp.ut_line));
+ setutent ();
+ old_utmp = getutline (&utmp);
+ endutent ();
+ if (old_utmp)
+ {
+ if (! host)
+ host = old_utmp->ut_host;
+ if (! addr)
+ addr = old_utmp->ut_addr;
+ }
+ }
+ }
+
+ strncpy (utmp.ut_host, host ?: "", sizeof (utmp.ut_host));
+ utmp.ut_addr = addr;
+
+ login (&utmp);
+}
+
+/* Lookup the host HOST, and add entries for VIA (the host name), and
+ VIA_ADDR (the dotted decimal address) to ARGS & ARGS_LEN. */
+static error_t
+add_canonical_host (char **args, size_t *args_len, char *host)
+{
+ struct hostent *he = gethostbyname (host);
+
+ if (he)
+ {
+ char *addr = 0;
+
+ /* Try and get an ascii version of the numeric host address. */
+ switch (he->h_addrtype)
+ {
+ case AF_INET:
+ addr = strdup (inet_ntoa (*(struct in_addr *)he->h_addr));
+ break;
+ }
+
+ if (addr && strcmp (he->h_name, addr) == 0)
+ /* gethostbyname() cheated! Lookup the host name via the address
+ this time to get the actual host name. */
+ he = gethostbyaddr (he->h_addr, he->h_length, he->h_addrtype);
+
+ if (he)
+ host = he->h_name;
+
+ if (addr)
+ {
+ envz_add (args, args_len, "VIA_ADDR", addr);
+ free (addr);
+ }
+ }
+
+ return envz_add (args, args_len, "VIA", host);
+}
+
+/* Add the `=' separated environment entry ENTRY to ENV & ENV_LEN, exiting
+ with an error message if we can't. */
+static void
+add_entry (char **env, size_t *env_len, char *entry)
+{
+ char *name = strsep (&entry, "=");
+ error_t err = envz_add (env, env_len, name, entry);
+ if (err)
+ error (8, err, "Adding %s", entry);
+}
+
+/* Return in OWNED whether PID has an owner, or an error. */
+static error_t
+check_owned (process_t proc_server, pid_t pid, int *owned)
+{
+ int flags = PI_FETCH_TASKINFO;
+ char *waits = 0;
+ mach_msg_type_number_t num_waits = 0;
+ struct procinfo _pi, *pi = &_pi;
+ mach_msg_type_number_t pi_size = sizeof pi;
+ error_t err =
+ proc_getprocinfo (proc_server, pid, &flags, (procinfo_t *)&pi, &pi_size,
+ &waits, &num_waits);
+
+ if (! err)
+ {
+ *owned = !(pi->state & PI_NOTOWNED);
+ if (pi != &_pi)
+ munmap (pi, pi_size);
+ }
+
+ return err;
+}
+
+/* Kills the login session PID with signal SIG. */
+static void
+kill_login (process_t proc_server, pid_t pid, int sig)
+{
+ error_t err;
+ size_t num_pids;
+ pid_t self = getpid ();
+
+ do
+ {
+ pid_t _pids[num_pids = 20], *pids = _pids;
+ err = proc_getloginpids (proc_server, pid, &pids, &num_pids);
+ if (! err)
+ {
+ size_t i;
+ for (i = 0; i < num_pids; i++)
+ if (pids[i] != self)
+ kill (pids[i], sig);
+ if (pids != _pids)
+ munmap (pids, num_pids);
+ }
+ }
+ while (!err && num_pids > 0);
+}
+
+/* Looks at the login collection LID. If the root process (PID == LID) is
+ owned by someone, then exit (0), otherwise, if it's exited, exit (42). */
+static void
+check_login (process_t proc_server, int lid)
+{
+ int owned;
+ error_t err = check_owned (proc_server, lid, &owned);
+
+ if (err == ESRCH)
+ exit (42); /* Nothing left to watch. */
+ else
+ assert_perror (err);
+
+ if (owned)
+ exit (0); /* Our task is done. */
+}
+
+/* Forks a process which will kill the login session headed by PID after
+ TIMEOUT seconds if PID still has no owner. */
+static void
+dog (time_t timeout, pid_t pid, char **argv)
+{
+ if (fork () == 0)
+ {
+ char buf[25]; /* Be gratuitously pretty. */
+ char *name = basename (argv[0]);
+ time_t left = timeout;
+ struct timeval tv = { 0, 0 };
+ process_t proc_server = getproc ();
+
+ while (left)
+ {
+ time_t interval = left < 5 ? left : 5;
+
+ tv.tv_sec = left;
+
+ /* Frob ARGV so that ps show something nice. */
+ fmt_named_interval (&tv, 0, buf, sizeof buf);
+ asprintf (&argv[0], "(watchdog for %s %d: %s remaining)",
+ name, pid, buf);
+ argv[1] = 0;
+
+ sleep (interval);
+ left -= interval;
+
+ check_login (proc_server, pid);
+ }
+
+ check_login (proc_server, pid);
+
+ /* Give you-forgot-to-login message. */
+ tv.tv_sec = timeout;
+ fmt_named_interval (&tv, 0, buf, sizeof buf);
+
+ putc ('\n', stderr); /* Make sure our message starts a line. */
+ error (0, 0, "Timed out after %s.", buf);
+
+ /* Kill login session, trying to be nice about it. */
+ kill_login (proc_server, pid, SIGHUP);
+ sleep (5);
+ kill_login (proc_server, pid, SIGKILL);
+ exit (0);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ io_t node;
+ char *arg;
+ char *path;
+ error_t err = 0;
+ char *args = 0; /* The login parameters */
+ size_t args_len = 0;
+ char *args_defs = 0; /* Defaults for login parameters. */
+ size_t args_defs_len = 0;
+ char *env = 0; /* The new environment. */
+ size_t env_len = 0;
+ char *env_defs = 0; /* Defaults for the environment. */
+ size_t env_defs_len = 0;
+ char *parent_env = 0; /* The environment we got from our parent */
+ size_t parent_env_len = 0;
+ int no_environ = 0; /* If false, use the env as default params. */
+ int no_args = 0; /* If false, put login params in the env. */
+ int inherit_environ = 0; /* True if we shouldn't clear our env. */
+ int no_passwd = 0; /* Don't bother verifying what we're doing. */
+ int no_login = 0; /* Don't prepend `-' to the shells argv[0]. */
+ int paranoid = 0; /* Admit no knowledge. */
+ int retry = 0; /* For some failures, exec a login shell. */
+ char *retry_args = 0; /* Args passed when retrying. */
+ size_t retry_args_len = 0;
+ char *shell = 0; /* The shell program to run. */
+ char *sh_arg0 = 0; /* The shell's argv[0]. */
+ char *sh_args = 0; /* The args to the shell. */
+ size_t sh_args_len = 0;
+ int shell_arg = 0; /* If there are shell args, use the first as
+ the shell name. */
+ struct ugids ugids = UGIDS_INIT; /* Authorization of the new shell. */
+ struct ugids_argp_params ugids_argp_params = { &ugids, 0, 0, 0, -1, 0 };
+ struct idvec parent_uids = IDVEC_INIT; /* Parent uids, -SETUID. */
+ struct idvec parent_gids = IDVEC_INIT; /* Parent gids, -SETGID. */
+ mach_port_t exec; /* The shell executable. */
+ mach_port_t root; /* The child's root directory. */
+ mach_port_t ports[INIT_PORT_MAX]; /* Init ports for the new process. */
+ int ints[INIT_INT_MAX]; /* Init ints for it. */
+ mach_port_t fds[3]; /* File descriptors passed. */
+ mach_port_t auth; /* The new shell's authentication. */
+ mach_port_t proc_server = getproc ();
+ pid_t pid = getpid (), sid;
+
+ /* These three functions are to do child-authenticated lookups. See
+ <hurd/lookup.h> for an explanation. */
+ error_t use_child_init_port (int which, error_t (*operate)(mach_port_t))
+ {
+ return (*operate)(ports[which]);
+ }
+ mach_port_t get_child_fd_port (int fd)
+ {
+ return fd < 0 || fd > 2 ? __hurd_fail (EBADF) : fds[fd];
+ }
+ mach_port_t child_lookup (char *name, char *path, int flags)
+ {
+ mach_port_t port = MACH_PORT_NULL;
+ errno =
+ hurd_file_name_path_lookup (use_child_init_port, get_child_fd_port, 0,
+ name, path, flags, 0, &port, 0);
+ return port;
+ }
+
+ /* Print an error message with FMT, STR and ERR. Then, if RETRY is on,
+ exec a default login shell, otherwise exit with CODE (must be non-0). */
+ void fail (int code, error_t err, char *fmt, const char *str)
+ {
+ int retry_argc;
+ char **retry_argv;
+ char *via = envz_get (args, args_len, "VIA");
+
+ if (fmt)
+ error (retry ? 0 : code, err, fmt, str); /* May exit... */
+ else if (! retry)
+ exit (code);
+
+ if (via)
+ envz_add (&retry_args, &retry_args_len, "--via", via);
+ argz_insert (&retry_args, &retry_args_len, retry_args, argv[0]);
+
+ retry_argc = argz_count (retry_args, retry_args_len);
+ retry_argv = alloca ((retry_argc + 1) * sizeof (char *));
+ argz_extract (retry_args, retry_args_len, retry_argv);
+
+ /* Reinvoke ourselves with no userids or anything; shouldn't return. */
+ main (retry_argc, retry_argv);
+ exit (code); /* But if it does... */
+ }
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'p': inherit_environ = 1; break;
+ case 'x': no_args = 1; break;
+ case 'X': no_environ = 1; break;
+ case 'e': add_entry (&env, &env_len, arg); break;
+ case 'E': add_entry (&env_defs, &env_defs_len, arg); break;
+ case 'a': add_entry (&args, &args_len, arg); break;
+ case 'A': add_entry (&args_defs, &args_defs_len, arg); break;
+ case '0': sh_arg0 = arg; break;
+ case 'L': no_login = 1; break;
+ case 'f': no_passwd = 1; break;
+ case 'P': paranoid = 1; break;
+ case 'S': shell_arg = 1; break;
+
+ case 'R':
+ retry = 1;
+ if (arg)
+ {
+ err = argz_add (&retry_args, &retry_args_len, arg);
+ if (err)
+ error (10, err, "Adding retry arg %s", arg);
+ }
+ break;
+
+ case 'h':
+ add_canonical_host (&args, &args_len, arg);
+ retry = 1;
+ break;
+
+ case 's':
+ idvec_merge (&ugids.avail_uids, &parent_uids);
+ idvec_merge (&ugids.avail_gids, &parent_gids);
+ break;
+
+ case ARGP_KEY_ARG:
+ if (state->arg_num > 0)
+ /* Program arguments. */
+ {
+ err = argz_create (state->argv + state->next - 1,
+ &sh_args, &sh_args_len);
+ state->next = state->argc; /* Consume all args */
+ if (err)
+ error (9, err, "Adding %s", arg);
+ break;
+ }
+
+ if (strcmp (arg, "-") == 0)
+ /* An explicit no-user-specified (so remaining args can be used
+ to set the program args). */
+ break;
+
+ if (isdigit (*arg))
+ err = ugids_set_posix_user (&ugids, atoi (arg));
+ else
+ {
+ struct passwd *pw = getpwnam (arg);
+ if (pw)
+ err = ugids_set_posix_user (&ugids, pw->pw_uid);
+ else if (paranoid)
+ /* Add a bogus uid so that password verification will
+ fail. */
+ idvec_add (&ugids.eff_uids, -1);
+ else
+ fail (10, 0, "%s: Unknown user", arg);
+ }
+
+ if (err)
+ fail (11, err, "%s: Can't set user!", arg);
+
+ break;
+
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = &ugids_argp_params;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = { options, parse_opt, args_doc, doc, child_argps };
+
+ /* Don't allow logins if the nologin file exists. */
+ node = file_name_lookup (_PATH_NOLOGIN, O_RDONLY, 0);
+ if (node != MACH_PORT_NULL)
+ {
+ cat (node, _PATH_NOLOGIN);
+ exit (40);
+ }
+
+ /* Put in certain last-ditch defaults. */
+ err = argz_create (default_args, &args_defs, &args_defs_len);
+ if (! err)
+ err = argz_create (default_env, &env_defs, &env_defs_len);
+ if (! err)
+ /* Set the default path using confstr() if possible. */
+ {
+ size_t path_len = confstr (_CS_PATH, 0, 0);
+ if (path_len > 0)
+ {
+ char path[path_len];
+ path_len = confstr (_CS_PATH, path, path_len);
+ if (path_len > 0)
+ err = envz_add (&env_defs, &env_defs_len, "PATH", path);
+ }
+ }
+ if (err)
+ error (23, err, "adding defaults");
+
+ err = argz_create (environ, &parent_env, &parent_env_len);
+
+ /* Get authentication of our parent, minus any setuid. */
+ get_nonsugid_ids (&parent_uids, &parent_gids);
+
+ /* Parse our options. */
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+ /* Check passwords where necessary. If no_passwd is set, then our parent
+ guarantees identity itself (where it is allowed), but otherwise
+ we want every UID fully checked. */
+ err = ugids_verify_make_auth (&ugids,
+ no_passwd ? &parent_uids : 0,
+ no_passwd ? &parent_gids : 0,
+ 0, 0, 0, 0, &auth);
+ if (err == EACCES)
+ fail (5, 0, "Invalid password", 0);
+ else if (err)
+ error (5, err, "Authentication failure");
+
+ /* Now that we've parsed the command line, put together all these
+ environments we've gotten from various places. There are two targets:
+ (1) the login parameters, and (2) the child environment.
+
+ The login parameters come from these sources (in priority order):
+ a) User specified (with the --arg option)
+ b) From the passwd file entry for the user being logged in as
+ c) From the parent environment, if --no-environ wasn't specified
+ d) From the user-specified defaults (--arg-default)
+ e) From last-ditch defaults given by the DEFAULT_* defines above
+
+ The child environment (constructed later) is from:
+ a) User specified (--environ)
+ b) From the login parameters (if --no-args wasn't specified)
+ c) From the parent environment, if --inherit-environ was specified
+ d) From the user-specified default env values (--environ-default)
+ e) From last-ditch defaults given by the DEFAULT_* defines above
+ */
+ {
+ struct passwd *pw;
+ char *passwd = 0; /* Login parameters from /etc/passwd */
+ size_t passwd_len = 0;
+
+ /* Decide which password entry to get parameters from. */
+ if (ugids.eff_uids.num > 0)
+ pw = getpwuid (ugids.eff_uids.ids[0]); /* Effective uid */
+ else if (ugids.avail_uids.num > 0)
+ pw = getpwuid (ugids.avail_uids.ids[0]); /* Auxiliary uid */
+ else
+ /* No user! Try to used the `not-logged-in' user to set various
+ parameters. */
+ pw = getpwnam (envz_get (args, args_len, "NOBODY")
+ ?: envz_get (args_defs, args_defs_len, "NOBODY")
+ ?: "login");
+
+ if (pw)
+ {
+ envz_add (&passwd, &passwd_len, "HOME", pw->pw_dir);
+ envz_add (&passwd, &passwd_len, "SHELL", pw->pw_shell);
+ envz_add (&passwd, &passwd_len, "NAME", pw->pw_gecos);
+ envz_add (&passwd, &passwd_len, "USER", pw->pw_name);
+ }
+
+ /* Merge the login parameters. */
+ err = envz_merge (&args, &args_len, passwd, passwd_len, 0);
+ if (! err && ! no_environ)
+ err = envz_merge (&args, &args_len, parent_env, parent_env_len, 0);
+ if (! err)
+ err = envz_merge (&args, &args_len, args_defs, args_defs_len, 0);
+ if (err)
+ error (24, err, "merging parameters");
+
+ free (passwd);
+ }
+
+ err = proc_getsid (proc_server, pid, &sid);
+ assert_perror (err); /* This should never fail. */
+
+ if (!no_login
+ && (parent_uids.num != 0
+ || ugids.eff_uids.num + ugids.avail_uids.num > 0))
+ /* Make a new login collection (but only for real users). */
+ {
+ char *user = envz_get (args, args_len, "USER");
+ if (user && *user)
+ setlogin (user);
+ proc_make_login_coll (proc_server);
+
+ if (ugids.eff_uids.num + ugids.avail_uids.num == 0)
+ /* We're transiting from having some uids to having none, which means
+ this is probably a new login session. Unless specified otherwise,
+ set a timer to kill this session if it hasn't acquired any ids by
+ then. Note that we fork off the timer process before clearing the
+ process owner: because we're interested in killing unowned
+ processes, proc's in-same-login-session rule should apply to us
+ (allowing us to kill them), and this way they can't kill the
+ watchdog (because it *does* have an owner). */
+ {
+ char *to = envz_get (args, args_len, "NOAUTH_TIMEOUT");
+ time_t timeout = to ? atoi (to) : 0;
+ if (timeout)
+ dog (timeout, pid, argv);
+ }
+ }
+
+ if (ugids.eff_uids.num > 0)
+ proc_setowner (proc_server, ugids.eff_uids.ids[0], 0);
+ else
+ proc_setowner (proc_server, 0, 1); /* Clear the owner. */
+
+ /* Now start constructing the exec arguments. */
+ bzero (ints, sizeof (*ints) * INIT_INT_MAX);
+ arg = envz_get (args, args_len, "UMASK");
+ ints[INIT_UMASK] = arg && *arg ? strtoul (arg, 0, 8) : umask (0);
+
+ for (i = 0; i < 3; i++)
+ fds[i] = getdport (i);
+
+ for (i = 0; i < INIT_PORT_MAX; i++)
+ ports[i] = MACH_PORT_NULL;
+ ports[INIT_PORT_PROC] = getproc ();
+ ports[INIT_PORT_CTTYID] = getcttyid ();
+ ports[INIT_PORT_CRDIR] = getcrdir (); /* May be replaced below. */
+ ports[INIT_PORT_CWDIR] = getcwdir (); /* " */
+
+ /* Now reauthenticate all of the ports we're passing to the child. */
+ err = exec_reauth (auth, 0, 1, ports, INIT_PORT_MAX, fds, 3);
+ if (err)
+ error (40, err, "Port reauth failure");
+
+ /* These are the default values for the child's root. We don't want to
+ modify PORTS just yet, because we use it to do child-authenticated
+ lookups. */
+ root = ports[INIT_PORT_CRDIR];
+
+ /* Find the shell executable (we copy the name, as ARGS may be changed). */
+ if (shell_arg && sh_args && *sh_args)
+ /* Special case for su mode: get the shell from the args if poss. */
+ {
+ shell = strdup (sh_args);
+ argz_delete (&sh_args, &sh_args_len, sh_args); /* Get rid of it. */
+ }
+ else
+ {
+ arg = envz_get (args, args_len, "SHELL");
+ if (arg && *arg)
+ shell = strdup (arg);
+ else
+ shell = 0;
+ }
+
+ path = envz_get (args, args_len, "PATH");
+ exec = shell ? child_lookup (shell, path, O_EXEC) : MACH_PORT_NULL;
+ if (exec == MACH_PORT_NULL)
+ {
+ char *backup = 0;
+ char *backups = envz_get (args, args_len, "BACKUP_SHELLS");
+ err = errno; /* Save original lookup errno. */
+
+ if (backups && *backups)
+ {
+ backups = strdupa (backups); /* Copy so we can trash it. */
+ while (exec == MACH_PORT_NULL && backups)
+ {
+ backup = strsep (&backups, ":, ");
+ if (*backup && (!shell || strcmp (shell, backup) != 0))
+ exec = child_lookup (backup, path, O_EXEC);
+ }
+ }
+
+ /* Give the error message, but only exit if we couldn't default. */
+ if (exec == MACH_PORT_NULL)
+ fail (1, err, "%s", shell);
+ else
+ error (0, err, "%s", shell);
+
+ /* If we get here, we looked up the default shell ok. */
+ shell = strdup (backup);
+ error (0, 0, "Using SHELL=%s", shell);
+ envz_add (&args, &args_len, "SHELL", shell);
+ err = 0; /* Don't emit random err msgs later! */
+ }
+
+ /* Now maybe change the cwd/root in the child. */
+
+ arg = envz_get (args, args_len, "HOME");
+ if (arg && *arg)
+ {
+ mach_port_t cwd = child_lookup (arg, 0, O_RDONLY);
+ if (cwd == MACH_PORT_NULL)
+ {
+ error (0, errno, "%s", arg);
+ error (0, 0, "Using HOME=/");
+ envz_add (&args, &args_len, "HOME", "/");
+ }
+ else
+ ports[INIT_PORT_CWDIR] = cwd;
+ }
+
+ arg = envz_get (args, args_len, "ROOT");
+ if (arg && *arg)
+ {
+ root = child_lookup (arg, 0, O_RDONLY);
+ if (root == MACH_PORT_NULL)
+ fail (40, errno, "%s", arg);
+ }
+
+ /* Build the child environment. */
+ if (! no_args)
+ /* We can't just merge ARGS, because it may contain the parent
+ environment, which we don't always want in the child environment, so
+ we pick out only those values of args which actually *are* args. */
+ {
+ char **name;
+ char *user = envz_get (args, args_len, "USER");
+
+ for (name = copied_args; *name && !err; name++)
+ if (! envz_get (env, env_len, *name))
+ {
+ char *val = envz_get (args, args_len, *name);
+ if (val && *val)
+ err = envz_add (&env, &env_len, *name, val);
+ }
+
+ if (user)
+ /* Copy the user arg into the environment as LOGNAME. */
+ err = envz_add (&env, &env_len, "LOGNAME", user);
+ }
+ if (! err && inherit_environ)
+ err = envz_merge (&env, &env_len, parent_env, parent_env_len, 0);
+ if (! err)
+ err = envz_merge (&env, &env_len, env_defs, env_defs_len, 0);
+ if (err)
+ error (24, err, "Can't build environment");
+
+ if (! sh_arg0)
+ /* The shells argv[0] defaults to the basename of the shell. */
+ {
+ char *shell_base = rindex (shell, '/');
+ if (shell_base)
+ shell_base++;
+ else
+ shell_base = shell;
+
+ if (no_login)
+ sh_arg0 = shell_base;
+ else if (ugids.eff_uids.num + ugids.avail_uids.num == 0)
+ /* Use a special format for the argv[0] of a login prompt shell,
+ so that `ps' shows something informative in the COMMAND field.
+ This string must begin with a `-', the convention to tell the
+ shell to be a login shell (i.e. run .profile and the like). */
+ err = (asprintf (&sh_arg0, "-login prompt (%s)", shell_base) == -1
+ ? ENOMEM : 0);
+ else
+ /* Prepend a `-' to the name, which is the ancient canonical
+ way to tell the shell that it's a login shell. */
+ err = asprintf (&sh_arg0, "-%s", shell_base) == -1 ? ENOMEM : 0;
+ }
+ if (! err)
+ err = argz_insert (&sh_args, &sh_args_len, sh_args, sh_arg0);
+ if (err)
+ error (21, err, "Error building shell args");
+
+ /* Maybe output the message of the day. Note that we use the child's
+ authentication to do it, so that this program can't be used to read
+ arbitrary files! */
+ arg = envz_get (args, args_len, "MOTD");
+ if (arg && *arg)
+ {
+ char *hush = envz_get (args, args_len, "HUSHLOGIN");
+ mach_port_t hush_node =
+ (hush && *hush) ? child_lookup (hush, 0, O_RDONLY) : MACH_PORT_NULL;
+ if (hush_node == MACH_PORT_NULL)
+ {
+ mach_port_t motd_node = child_lookup (arg, 0, O_RDONLY);
+ if (motd_node != MACH_PORT_NULL)
+ cat (motd_node, arg);
+ }
+ else
+ mach_port_deallocate (mach_task_self (), hush_node);
+ }
+
+ /* Now that we don't need to use PORTS for lookups anymore, put the correct
+ ROOT in. */
+ ports[INIT_PORT_CRDIR] = root;
+
+ /* Get rid of any accumulated null entries in env. */
+ envz_strip (&env, &env_len);
+
+ /* No more authentications to fail, so cross our fingers and add our utmp
+ entry. */
+
+ if (pid == sid)
+ /* Only add utmp entries for the session leader. */
+ add_utmp_entry (args, args_len, !idvec_contains (&parent_uids, 0));
+
+ if ((ugids.eff_uids.num | ugids.eff_gids.num) && !no_login)
+ {
+ char *tty = ttyname (0);
+ if (tty)
+ {
+ /* Change the terminal to be owned by the user. */
+ err = chown (tty,
+ ugids.eff_uids.num ? ugids.eff_uids.ids[0] : -1,
+ ugids.eff_gids.num ? ugids.eff_gids.ids[0] : -1);
+ if (err)
+ error (0, errno, "chown: %s", tty);
+ }
+ }
+
+ err = file_exec (exec, mach_task_self (), EXEC_DEFAULTS,
+ sh_args, sh_args_len, env, env_len,
+ fds, MACH_MSG_TYPE_COPY_SEND, 3,
+ ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
+ ints, INIT_INT_MAX,
+ 0, 0, 0, 0);
+ if (err)
+ error(5, err, "%s", shell);
+
+ return 0;
+}
diff --git a/utils/loginpr.sh b/utils/loginpr.sh
new file mode 100644
index 00000000..59924082
--- /dev/null
+++ b/utils/loginpr.sh
@@ -0,0 +1,20 @@
+#!/bin/bash -noprofile
+#
+# Traditional prompting login program
+#
+# This can be made the system default by making it the shell for the
+# pseudo-user `login'.
+#
+
+prompt='login: '
+user=''
+
+test "$_LOGIN_RETRY" && echo ''
+unset _LOGIN_RETRY
+
+until [ "$user" ]; do
+ echo -n "$prompt"
+ read user args || exit 0
+done
+
+exec login "$@" -p --paranoid -R-aSHELL="$0" -R-aMOTD -R-p -R-e_LOGIN_RETRY=yes "$user" $args
diff --git a/utils/match-options.c b/utils/match-options.c
new file mode 100644
index 00000000..11fc9dcb
--- /dev/null
+++ b/utils/match-options.c
@@ -0,0 +1,68 @@
+/* Common functionality for the --test-opts flag of mount and umount.
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <argz.h>
+#include <error.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "match-options.h"
+
+char *test_opts;
+size_t test_opts_len;
+
+int
+match_options (struct mntent *mntent)
+{
+ char *opts;
+ size_t opts_len;
+
+ error_t err = argz_create_sep (mntent->mnt_opts, ',', &opts, &opts_len);
+ if (err)
+ error (3, err, "parsing mount options failed");
+
+ for (char *test = test_opts;
+ test; test = argz_next (test_opts, test_opts_len, test))
+ {
+ char *needle = test;
+ int inverse = strncmp("no", needle, 2) == 0;
+ if (inverse)
+ needle += 2;
+
+ int match = 0;
+ for (char *opt = opts; opt; opt = argz_next (opts, opts_len, opt))
+ {
+ if (strcmp (opt, needle) == 0) {
+ if (inverse)
+ return 0; /* foo in opts, nofoo in test_opts. */
+
+ /* foo in opts, foo in test_opts, record match. */
+ match = 1;
+ }
+ }
+
+ if (! inverse && ! match)
+ return 0; /* No foo in opts, but foo in test_opts. */
+ }
+
+ /* If no conflicting test_opt was encountered, return success. */
+ return 1;
+}
diff --git a/utils/match-options.h b/utils/match-options.h
new file mode 100644
index 00000000..ea7ae70d
--- /dev/null
+++ b/utils/match-options.h
@@ -0,0 +1,33 @@
+/* Common functionality for the --test-opts flag of mount and umount.
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <mntent.h>
+
+extern char *test_opts;
+extern size_t test_opts_len;
+
+/* Check whether the given mount entry matches the given set of
+ options.
+
+ Returns 0 if foo is in the options vector but nofoo is in test_opts.
+ Returns 0 if foo is in test_opts but foo is not in the options vector. */
+int
+match_options (struct mntent *mntent);
diff --git a/utils/mount.c b/utils/mount.c
new file mode 100644
index 00000000..df77c66f
--- /dev/null
+++ b/utils/mount.c
@@ -0,0 +1,677 @@
+/* Roughly Unix/Linux-compatible `mount' frontend for Hurd translators.
+
+ Copyright (C) 1999, 2004, 2013 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "../sutils/fstab.h"
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <hurd/fsys.h>
+#include <hurd/fshelp.h>
+#include <hurd/paths.h>
+#ifdef HAVE_BLKID
+#include <blkid/blkid.h>
+#endif
+
+#include "match-options.h"
+
+#define SEARCH_FMTS _HURD "%sfs\0" _HURD "%s"
+#define DEFAULT_FSTYPE "auto"
+
+static char *fstype = DEFAULT_FSTYPE;
+static char *device, *mountpoint;
+static int verbose;
+static int fake;
+static char *options;
+static size_t options_len;
+static mach_msg_timeout_t timeout;
+
+static enum { mount, query } mode;
+static enum { qf_standard, qf_fstab, qf_translator } query_format;
+static struct fstab_argp_params fstab_params;
+
+
+static const struct argp_option argp_opts[] =
+{
+ {"timeout", 'T', "MILLISECONDS", 0, "Timeout for translator startup"},
+ {"format", 'p', "mount|fstab|translator", OPTION_ARG_OPTIONAL,
+ "Output format for query (no filesystem arguments)"},
+ {"options", 'o', "OPTIONS", 0, "A `,' separated list of options"},
+ {"readonly", 'r', 0, 0, "Never write to disk or allow opens for writing"},
+ {"writable", 'w', 0, 0, "Use normal read/write behavior"},
+ {"update", 'u', 0, 0, "Flush any meta-data cached in core"},
+ {"remount", 0, 0, OPTION_ALIAS},
+ {"verbose", 'v', 0, 0, "Give more detailed information"},
+ {"no-mtab", 'n', 0, 0, "Do not update /etc/mtab"},
+ {"test-opts", 'O', "OPTIONS", 0,
+ "Only mount fstab entries matching the given set of options"},
+ {"fake", 'f', 0, 0, "Do not actually mount, just pretend"},
+ {0, 0}
+};
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct fstab_argp_params *params = state->input;
+ error_t err;
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = params; /* pass down to fstab_argp parser */
+ break;
+
+#define ARGZ(call) \
+ err = argz_##call; \
+ if (err) \
+ argp_failure (state, 100, ENOMEM, "%s", arg); \
+ break
+ case 'r': ARGZ (add (&options, &options_len, "ro"));
+ case 'w': ARGZ (add (&options, &options_len, "rw"));
+ case 'u': ARGZ (add (&options, &options_len, "update"));
+ case 'o': ARGZ (add_sep (&options, &options_len, arg, ','));
+ case 'v': ++verbose; break;
+#undef ARGZ
+
+ case 'T':
+ {
+ char *end;
+ unsigned long int ms = strtoul (arg, &end, 10);
+ if (end && *end == '\0')
+ timeout = ms;
+ else
+ {
+ argp_error (state,
+ "--timeout needs a numeric argument (milliseconds)");
+ return EINVAL;
+ }
+ }
+ break;
+
+ case 'p':
+ if (arg == 0 || !strcasecmp (arg, "fstab"))
+ query_format = qf_fstab;
+ else if (!strcasecmp (arg, "mount"))
+ query_format = qf_standard;
+ else if (!strcasecmp (arg, "translator")
+ || !strcasecmp (arg, "showtrans"))
+ query_format = qf_translator;
+ else
+ {
+ argp_error (state, "invalid argument to --format");
+ return EINVAL;
+ }
+ break;
+
+ case 'n':
+ /* do nothing */
+ break;
+
+ case 'f':
+ fake = 1;
+ break;
+
+ case 'O':
+ err = argz_create_sep (arg, ',', &test_opts, &test_opts_len);
+ if (err)
+ argp_failure (state, 100, ENOMEM, "%s", arg);
+ break;
+
+ case ARGP_KEY_ARG:
+ if (mountpoint == 0) /* One arg: mountpoint */
+ mountpoint = arg;
+ else if (device == 0) /* Two args: device, mountpoint */
+ {
+ device = mountpoint;
+ mountpoint = arg;
+ }
+ else /* More than two args. */
+ {
+ argp_error (state, "too many arguments");
+ return EINVAL;
+ }
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ if (! params->do_all)
+ {
+ params->do_all = 1;
+ mode = query;
+ }
+ break;
+
+ case ARGP_KEY_END:
+ if (params->do_all && mountpoint)
+ {
+ argp_error (state, "filesystem argument not allowed with --all");
+ return EINVAL;
+ }
+ if (mode == query && options_len != 0)
+ {
+ argp_error (state,
+ "mount options not allowed without filesystem argument");
+ return EINVAL;
+ }
+ if (mountpoint)
+ switch (argz_count (params->types, params->types_len))
+ {
+ default:
+ argp_error (state,
+ "multiple types not allowed with filesystem argument");
+ return EINVAL;
+ case 1:
+ fstype = params->types;
+ params->types = 0;
+ params->types_len = 0;
+ break;
+ case 0:
+ break;
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static const char doc[] = "Start active filesystem translators";
+static const char args_doc[] = "\
+DEVICE\t(in " _PATH_MNTTAB ")\n\
+DIRECTORY\t(in " _PATH_MNTTAB ")\n\
+[-t TYPE] DEVICE DIRECTORY\n\
+-a";
+static const struct argp_child argp_kids[] =
+{ { &fstab_argp, 0,
+ "Filesystem selection (if no explicit filesystem arguments given):", 2 },
+ { 0 } };
+static struct argp argp = { argp_opts, parse_opt, args_doc, doc, argp_kids };
+
+/* Mount one filesystem. */
+static error_t
+do_mount (struct fs *fs, int remount)
+{
+ error_t err;
+ char *fsopts, *o;
+ size_t fsopts_len;
+ char *mntopts;
+ size_t mntopts_len;
+ fsys_t mounted;
+
+ inline void explain (const char *command)
+ {
+ if (verbose)
+ {
+ const char *o;
+ printf ("%s %s", command, fs->mntent.mnt_dir);
+ for (o = fsopts; o; o = argz_next (fsopts, fsopts_len, o))
+ printf (" %s", o);
+ putchar ('\n');
+ }
+ }
+
+ err = fs_fsys (fs, &mounted);
+ if (err)
+ {
+ error (0, err, "cannot determine if %s is already mounted",
+ fs->mntent.mnt_fsname);
+ return err;
+ }
+
+
+ /* Produce an argz of translator option arguments from the
+ given FS's options and the command-line options. */
+
+#define ARGZ(call) \
+ err = argz_##call; \
+ if (err) \
+ error (3, ENOMEM, "collecting mount options"); \
+
+ if (fs->mntent.mnt_opts)
+ {
+ /* Append the fstab options to any specified on the command line. */
+ ARGZ (create_sep (fs->mntent.mnt_opts, ',', &mntopts, &mntopts_len));
+
+ /* Remove the `noauto' option, since it's for us not the filesystem. */
+ for (o = mntopts; o; o = argz_next (mntopts, mntopts_len, o))
+ if (!strcmp (o, MNTOPT_NOAUTO))
+ break;
+ if (o)
+ argz_delete (&mntopts, &mntopts_len, o);
+
+ ARGZ (append (&mntopts, &mntopts_len, options, options_len));
+ }
+ else
+ {
+ mntopts = options;
+ mntopts_len = options_len;
+ }
+
+ /* Convert the list of options into a list of switch arguments. */
+ fsopts = 0;
+ fsopts_len = 0;
+ for (o = mntopts; o; o = argz_next (mntopts, mntopts_len, o))
+ if (*o == '-') /* Allow letter opts `-o -r,-E', BSD style. */
+ {
+ ARGZ (add (&fsopts, &fsopts_len, o));
+ }
+ else if (strcmp (o, "defaults") != 0)
+ {
+ /* Prepend `--' to the option to make a long option switch,
+ e.g. `--ro' or `--rsize=1024'. */
+ char arg[2 + strlen (o) + 1];
+ arg[0] = arg[1] = '-';
+ memcpy (&arg[2], o, sizeof arg - 2);
+ ARGZ (add (&fsopts, &fsopts_len, arg));
+ }
+
+ if (mntopts != options)
+ free (mntopts);
+#undef ARGZ
+
+ if (remount)
+ {
+ if (mounted == MACH_PORT_NULL)
+ {
+ error (0, 0, "%s not already mounted", fs->mntent.mnt_fsname);
+ return EBUSY;
+ }
+
+ /* Send an RPC to request the new options, including --update. */
+ explain ("fsysopts");
+ err = fsys_set_options (mounted, fsopts, fsopts_len, 0);
+ if (err)
+ error (0, err, "cannot remount %s", fs->mntent.mnt_fsname);
+ return err;
+ }
+ else
+ {
+ /* Error during file lookup; we use this to avoid duplicating error
+ messages. */
+ error_t open_err = 0;
+ /* The control port for any active translator we start up. */
+ fsys_t active_control;
+ file_t node; /* Port to the underlying node. */
+ struct fstype *type;
+
+ /* The callback to start_translator opens NODE as a side effect. */
+ error_t open_node (int flags,
+ mach_port_t *underlying,
+ mach_msg_type_name_t *underlying_type,
+ task_t task, void *cookie)
+ {
+ node = file_name_lookup (fs->mntent.mnt_dir,
+ flags | O_NOTRANS, 0666);
+ if (node == MACH_PORT_NULL)
+ {
+ open_err = errno;
+ return open_err;
+ }
+
+ *underlying = node;
+ *underlying_type = MACH_MSG_TYPE_COPY_SEND;
+
+ return 0;
+ }
+
+ /* Do not fail if there is an active translator if --fake is
+ given. This mimics Linux mount utility more closely which
+ just looks into the mtab file. */
+ if (mounted != MACH_PORT_NULL && !fake)
+ {
+ error (0, 0, "%s already mounted", fs->mntent.mnt_fsname);
+ return EBUSY;
+ }
+
+ if (strcmp (fs->mntent.mnt_type, "auto") == 0)
+ {
+#if HAVE_BLKID
+ char *type =
+ blkid_get_tag_value (NULL, "TYPE", fs->mntent.mnt_fsname);
+ if (! type)
+ {
+ error (0, 0, "failed to detect file system type");
+ return EFTYPE;
+ }
+ else
+ {
+ if (strcmp (type, "vfat") == 0)
+ fs->mntent.mnt_type = strdup ("fat");
+ else
+ fs->mntent.mnt_type = strdup (type);
+ if (! fs->mntent.mnt_type)
+ error (3, ENOMEM, "failed to allocate memory");
+ }
+#else
+ fs->mntent.mnt_type = strdup ("ext2");
+ if (! fs->mntent.mnt_type)
+ error (3, ENOMEM, "failed to allocate memory");
+#endif
+ }
+
+ err = fs_type (fs, &type);
+ if (err)
+ {
+ error (0, err, "%s: cannot determine filesystem type",
+ fs->mntent.mnt_fsname);
+ return err;
+ }
+ if (type->program == 0)
+ {
+ error (0, 0, "%s: filesystem type `%s' unknown",
+ fs->mntent.mnt_fsname, type->name);
+ return EFTYPE;
+ }
+
+ /* Stick the translator program name in front of the option switches. */
+ err = argz_insert (&fsopts, &fsopts_len, fsopts, type->program);
+ /* Now stick the device name on the end as the last argument. */
+ if (!err)
+ err = argz_add (&fsopts, &fsopts_len, fs->mntent.mnt_fsname);
+ if (err)
+ error (3, ENOMEM, "collecting mount options");
+
+ /* Now we have a translator command line argz in FSOPTS. */
+
+ if (fake) {
+ /* Fake the translator startup. */
+ mach_port_t underlying;
+ mach_msg_type_name_t underlying_type;
+ err = open_node (O_READ, &underlying, &underlying_type, 0, NULL);
+ if (err)
+ error (1, errno, "cannot mount on %s", fs->mntent.mnt_dir);
+
+ mach_port_deallocate (mach_task_self (), underlying);
+
+ /* See if the translator is at least executable. */
+ if (access(type->program, X_OK) == -1)
+ error (1, errno, "can not execute %s", type->program);
+
+ return 0;
+ }
+
+ explain ("settrans -a");
+ err = fshelp_start_translator (open_node, NULL, fsopts,
+ fsopts, fsopts_len, timeout,
+ &active_control);
+ /* If ERR is due to a problem opening the translated node, we print
+ that name, otherwise, the name of the translator. */
+ if (open_err)
+ error (0, open_err, "cannot mount on %s", fs->mntent.mnt_dir);
+ else if (err)
+ error (0, err, "cannot start translator %s", fsopts);
+ else
+ {
+ err = file_set_translator (node, 0, FS_TRANS_SET|FS_TRANS_EXCL, 0,
+ 0, 0,
+ active_control, MACH_MSG_TYPE_COPY_SEND);
+ if (err == EBUSY)
+ error (0, 0, "%s already mounted on", fs->mntent.mnt_dir);
+ else if (err)
+ error (0, err, "cannot set translator on %s", fs->mntent.mnt_dir);
+ if (err)
+ fsys_goaway (active_control, FSYS_GOAWAY_FORCE);
+ mach_port_deallocate (mach_task_self (), active_control);
+ }
+
+ return err;
+ }
+}
+
+/* Report the state of one filesystem to stdout. */
+static error_t
+do_query (struct fs *fs)
+{
+ error_t err;
+ fsys_t fsys;
+ char _opts[200], *opts = _opts;
+ size_t opts_len = sizeof opts;
+ size_t nopts;
+
+ err = fs_fsys (fs, &fsys);
+ if (err)
+ return err;
+
+ if (fsys == MACH_PORT_NULL)
+ /* This filesystem is not mounted. Nothing to report. */
+ return 0;
+
+ err = fsys_get_options (fsys, &opts, &opts_len);
+ if (err)
+ {
+ error (0, err, "%s: cannot get filesystem options",
+ fs->mntent.mnt_fsname);
+ return err;
+ }
+
+ nopts = argz_count (opts, opts_len);
+ if (nopts == 0)
+ {
+ error (0, 0, "%s: fsys_get_options returned empty string",
+ fs->mntent.mnt_dir);
+ return EGRATUITOUS;
+ }
+
+ if (query_format == qf_translator)
+ {
+ argz_stringify (opts, opts_len, ' ');
+ printf ("%s: %s\n", fs->mntent.mnt_dir, opts);
+ }
+ else
+ {
+ char *argv[nopts];
+ const char *prog, *device;
+
+ inline const char *fsopt_string (const char *opt) /* canonicalize */
+ {
+ if (opt[0] == '-' && opt[1] == '-')
+ {
+ opt += 2;
+ if (!strcmp (opt, "readonly"))
+ return "ro";
+ if (!strcmp (opt, "writable"))
+ return "rw";
+ }
+ else
+ {
+ if (!strcmp (opt, "-r"))
+ return "ro";
+ if (!strcmp (opt, "-w"))
+ return "rw";
+ }
+ return opt;
+ }
+ inline void print_prog (const char *sfx)
+ {
+ if (!strncmp (_HURD, prog, sizeof _HURD - 1))
+ {
+ const char *type = &prog[sizeof _HURD - 1];
+ size_t len = strlen (type);
+ if (!strcmp (&type[len - 2], "fs"))
+ printf ("%.*s%s", (int) (len - 2), type, sfx);
+ else
+ printf ("%s%s", type, sfx);
+ }
+ else
+ printf ("%s%s", prog, sfx);
+ }
+ inline int print_opts (char sep)
+ {
+ char *opt = argz_next (opts, opts_len, prog);
+ if (opt == 0)
+ return 0;
+ fputs (fsopt_string (opt), stdout);
+ while ((opt = argz_next (opts, opts_len, opt)) != 0)
+ printf ("%c%s", sep, fsopt_string (opt));
+ return 1;
+ }
+
+ argz_extract (opts, opts_len, argv);
+ prog = argv[0];
+
+ if (nopts < 2)
+ device = fs->mntent.mnt_fsname;
+ else
+ {
+ static const char type_opt[] = "--store-type=";
+ device = argv[--nopts];
+ opts_len -= strlen (device) + 1;
+ if (!strncmp (type_opt, argv[nopts - 1], sizeof type_opt - 1))
+ {
+ asprintf ((char **) &device, "%s:%s",
+ &argv[nopts - 1][sizeof type_opt - 1], device);
+ opts_len -= strlen (argv[nopts - 1]) + 1;
+ }
+ }
+
+ switch (query_format)
+ {
+ case qf_standard:
+ printf ("%s on %s type ", device, fs->mntent.mnt_dir);
+ print_prog (" (");
+ if (print_opts (','))
+ puts (")");
+ else
+ puts ("defaults)");
+ break;
+
+ case qf_fstab:
+ printf ("%s\t%s\t", device, fs->mntent.mnt_dir);
+ print_prog ("\t");
+ printf ("%s\t%d %d\n",
+ print_opts (',') ? "" : "defaults",
+ fs->mntent.mnt_freq, fs->mntent.mnt_passno);
+ break;
+
+ case qf_translator: /* impossible */
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+ unsigned int remount;
+ struct fstab *fstab;
+ struct fs *fs;
+ error_t err;
+
+ argp_parse (&argp, argc, argv, 0, 0, &fstab_params);
+
+ if (!mountpoint && (fstab_params.types == 0
+
+ || !strncasecmp (fstab_params.types, "no", 2)))
+ {
+ /* Always add the type "swap" to the list of types to exclude. */
+ err = argz_add (&fstab_params.types, &fstab_params.types_len,
+ "no"MNTTYPE_SWAP);
+ if (err)
+ error (3, ENOMEM, "parsing arguments");
+ }
+
+ fstab = fstab_argp_create (&fstab_params, SEARCH_FMTS, sizeof SEARCH_FMTS);
+
+ /* This is a convenient way of checking for any `remount' options. */
+ remount = 0;
+ err = argz_replace (&options, &options_len, "remount", "update", &remount);
+ if (err)
+ error (3, ENOMEM, "collecting mount options");
+
+ if (device) /* two-argument form */
+ {
+ struct mntent m =
+ {
+ mnt_fsname: device,
+ mnt_dir: mountpoint,
+ mnt_type: fstype,
+ mnt_opts: 0,
+ mnt_freq: 0, mnt_passno: 0
+ };
+
+ err = fstab_add_mntent (fstab, &m, &fs);
+ if (err)
+ error (2, err, "%s", mountpoint);
+ }
+ else if (mountpoint && remount) /* one-argument remount */
+ {
+ struct mntent m =
+ {
+ mnt_fsname: mountpoint, /* since we cannot know the device,
+ using mountpoint here leads to more
+ helpful error messages */
+ mnt_dir: mountpoint,
+ mnt_type: fstype,
+ mnt_opts: 0,
+ mnt_freq: 0, mnt_passno: 0
+ };
+
+ err = fstab_add_mntent (fstab, &m, &fs);
+ if (err)
+ error (2, err, "%s", mountpoint);
+ }
+ else if (mountpoint) /* one-argument form */
+ {
+ fs = fstab_find (fstab, mountpoint);
+ if (!fs)
+ error (2, 0, "%s: Unknown device or filesystem", mountpoint);
+ }
+ else
+ fs = 0;
+
+ if (fs != 0)
+ err = do_mount (fs, remount);
+ else
+ switch (mode)
+ {
+ case mount:
+ for (fs = fstab->entries; fs; fs = fs->next)
+ {
+ if (fstab_params.do_all) {
+ if (hasmntopt (&fs->mntent, MNTOPT_NOAUTO))
+ continue;
+
+ if (! match_options (&fs->mntent))
+ continue;
+
+ fsys_t mounted;
+ err = fs_fsys (fs, &mounted);
+ if (err)
+ error (0, err, "cannot determine if %s is already mounted",
+ fs->mntent.mnt_fsname);
+
+ if (mounted != MACH_PORT_NULL)
+ continue;
+ }
+ err |= do_mount (fs, remount);
+ }
+ break;
+
+ case query:
+ for (fs = fstab->entries; fs; fs = fs->next)
+ err |= do_query (fs);
+ break;
+ }
+
+ return err ? 1 : 0;
+}
diff --git a/utils/msgport.c b/utils/msgport.c
new file mode 100644
index 00000000..06b7dc37
--- /dev/null
+++ b/utils/msgport.c
@@ -0,0 +1,661 @@
+/* Send messages to selected processes
+
+ Copyright (C) 1998,99,2000,02 Free Software Foundation, Inc.
+ Written by Jose M. Moya <josem@gnu.org>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <argp.h>
+#include <error.h>
+#include <version.h>
+#include "pids.h"
+#include <sys/mman.h>
+
+/* From libc (not in hurd.h) */
+char *
+_hurd_canonicalize_directory_name_internal (file_t thisdir,
+ char *buf,
+ size_t size);
+
+const char *argp_program_version = STANDARD_HURD_VERSION (msgport);
+
+static const struct argp_option options[] =
+{
+ {0, 0}
+};
+
+static const char doc[] =
+"Send messages to selected processes";
+
+static const char args_doc[] =
+"";
+
+
+
+/* All command functions match this prototype. */
+typedef error_t (*cmd_func_t) (pid_t pid, mach_port_t msgport,
+ int argc, char *argv[]);
+
+/* One of these is created for each command given in the command line. */
+typedef struct cmd {
+ /* Function to execute for this command */
+ cmd_func_t f;
+
+ /* Array of arguments that will be passed to function F */
+ char **args;
+ size_t num_args;
+} cmd_t;
+
+
+/* Execute command CMD on process PID */
+error_t
+do_cmd (pid_t pid, cmd_t cmd)
+{
+ error_t err;
+ mach_port_t msgport;
+ process_t proc = getproc ();
+
+ /* Get a msgport for PID, to which we can send requests. */
+ err = proc_getmsgport (proc, pid, &msgport);
+ if (err)
+ error (1, err, "%d: Cannot get process msgport", pid);
+
+ err = (*cmd.f) (pid, msgport, cmd.num_args, cmd.args);
+ if (err)
+ error (2, err, "%d: Cannot execute command", pid);
+
+ mach_port_deallocate (mach_task_self (), msgport);
+ return 0;
+}
+
+
+/* All these functions, whose name start with cmd_, execute some
+ commands on the process PID, by sending messages (see msg.defs) to
+ its message port, which is MSGPORT. ARGC and ARGV are as in main.
+ They return zero iff successful. */
+
+/* Print the name and value of the environment variable ARGV[0].
+ Without arguments (ARGC==0), print the names and values of all
+ environment variables. */
+error_t
+cmd_getenv (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+
+ /* Memory will be vm_allocated by msg_get_* if the result does not
+ fit in buf. */
+ char buf[1024], *data = buf;
+ mach_msg_type_number_t len = sizeof (buf);
+
+ if (argc)
+ {
+ err = msg_get_env_variable (msgport, argv[0], &data, &len);
+ if (err)
+ return err;
+ printf ("%d: %s=%s\n", pid, argv[0], data);
+ }
+ else /* get the whole environment */
+ {
+ char *p;
+ err = msg_get_environment (msgport, &data, &len);
+ if (err)
+ return err;
+ for (p=data; p < data + len; p = strchr (p, '\0') + 1)
+ printf ("%d: %s\n", pid, p);
+ }
+ if (data != buf)
+ munmap (data, len);
+ return err;
+}
+
+/* Set environment variable ARGV[0] to the value ARGV[1]. */
+error_t
+cmd_setenv (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ task_t task;
+ process_t proc = getproc ();
+
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ return err;
+ err = msg_set_env_variable (msgport, task, argv[0], argv[1], 1);
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+}
+
+/* Clear environment. */
+error_t
+cmd_clearenv (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ task_t task;
+ process_t proc = getproc ();
+
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ return err;
+ err = msg_set_environment (msgport, task, 0, 0);
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+}
+
+/* Convert string STR in flags for file access modes. STR should be a
+ combination of `r', `w' and `x' (for read, write and execute modes
+ respectively). Other chars are ignored. */
+static inline int
+str2flags (const char *str)
+{
+ int flags = 0;
+ while (*str)
+ {
+ switch (*str)
+ {
+ case 'r': flags |= O_RDONLY; break;
+ case 'w': flags |= O_WRONLY|O_CREAT; break;
+ case 'x': flags |= O_EXEC; break;
+ case 'a': flags |= O_APPEND; break;
+ default:
+ /* ignore */
+ break;
+ }
+ ++str;
+ }
+ return flags;
+}
+
+/* Set port associated to file descriptor FD of process PID, whose
+ message port is MSGPORT, to FILE. Used by
+ cmd_{setfd,stdin,stdout,stderr}. */
+error_t
+do_setfd (pid_t pid, mach_port_t msgport, size_t fd, file_t file)
+{
+ error_t err;
+ task_t task;
+ process_t proc = getproc ();
+
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ return err;
+ err = msg_set_fd (msgport, task, fd, file, MACH_MSG_TYPE_COPY_SEND);
+ mach_port_deallocate (mach_task_self (), file);
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+}
+
+/* Set port associated to file descriptor ARGV[0] to the file ARGV[1].
+ File access mode is given by ARGV[2] (see str2flags). If no access
+ mode is given, the default is O_RDONLY. */
+error_t
+cmd_setfd (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ int flags = str2flags (argc > 2 ? argv[2] : "r");
+ file_t file = file_name_lookup (argv[1], flags, 0666);
+ if (file == MACH_PORT_NULL)
+ return errno;
+ err = do_setfd (pid, msgport, atoi (argv[0]), file);
+ if (err)
+ mach_port_deallocate (mach_task_self (), file);
+ return err;
+}
+
+/* Set standard input to ARGV[0]. Optionally, ARGV[1] may specify the
+ file access mode (see str2flags). The default is O_RDONLY */
+error_t
+cmd_stdin (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ int flags = str2flags (argc > 2 ? argv[2] : "r");
+ file_t file = file_name_lookup (argv[0], flags, 0666);
+ if (file == MACH_PORT_NULL)
+ return errno;
+ err = do_setfd (pid, msgport, STDIN_FILENO, file);
+ if (err)
+ mach_port_deallocate (mach_task_self (), file);
+ return err;
+}
+
+/* Set standard output to ARGV[0]. Optionally, ARGV[1] may specify the
+ file access mode (see str2flags). The default is O_WRONLY */
+error_t
+cmd_stdout (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ int flags = str2flags (argc > 2 ? argv[2] : "w");
+ file_t file = file_name_lookup (argv[0], flags, 0666);
+ if (file == MACH_PORT_NULL)
+ return errno;
+ err = do_setfd (pid, msgport, STDOUT_FILENO, file);
+ if (err)
+ mach_port_deallocate (mach_task_self (), file);
+ return err;
+}
+
+/* Set standard error to ARGV[0]. Optionally, ARGV[1] may specify the
+ file access mode (see str2flags). The default is O_RDONLY */
+error_t
+cmd_stderr (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ int flags = str2flags (argc > 2 ? argv[2] : "w");
+ file_t file = file_name_lookup (argv[0], flags, 0666);
+ if (file == MACH_PORT_NULL)
+ return errno;
+ err = do_setfd (pid, msgport, STDERR_FILENO, file);
+ if (err)
+ mach_port_deallocate (mach_task_self (), file);
+ return err;
+}
+
+/* Change current working directory to ARGV[0]. */
+error_t
+cmd_chcwdir (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ file_t dir;
+ task_t task;
+ process_t proc = getproc ();
+
+ dir = file_name_lookup (argv[0], 0, 0);
+ if (dir == MACH_PORT_NULL)
+ return errno;
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), dir);
+ return err;
+ }
+ err = msg_set_init_port (msgport, task, INIT_PORT_CWDIR, dir,
+ MACH_MSG_TYPE_COPY_SEND);
+ mach_port_deallocate (mach_task_self (), dir);
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+}
+
+/* Change current working directory to current root directory. */
+error_t
+cmd_cdroot (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ file_t dir;
+ task_t task;
+ process_t proc = getproc ();
+
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ return err;
+ err = msg_get_init_port (msgport, task, INIT_PORT_CRDIR, &dir);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+ }
+ err = msg_set_init_port (msgport, task, INIT_PORT_CWDIR, dir,
+ MACH_MSG_TYPE_COPY_SEND);
+ mach_port_deallocate (mach_task_self (), dir);
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+}
+
+/* Change current root directory to ARGV[0]. */
+error_t
+cmd_chcrdir (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ file_t dir;
+ task_t task;
+ process_t proc = getproc ();
+
+ dir = file_name_lookup (argv[0], 0, 0);
+ if (dir == MACH_PORT_NULL)
+ return errno;
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), dir);
+ return err;
+ }
+ err = msg_set_init_port (msgport, task, INIT_PORT_CRDIR, dir,
+ MACH_MSG_TYPE_COPY_SEND);
+ mach_port_deallocate (mach_task_self (), dir);
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+}
+
+/* Print current working directory. */
+error_t
+cmd_pwd (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ file_t dir;
+ task_t task;
+ process_t proc = getproc ();
+
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ return err;
+ err = msg_get_init_port (msgport, task, INIT_PORT_CWDIR, &dir);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+ }
+ printf ("%d: %s\n", pid,
+ _hurd_canonicalize_directory_name_internal(dir, NULL, 0));
+ mach_port_deallocate (mach_task_self (), dir);
+ mach_port_deallocate (mach_task_self (), task);
+ return 0;
+}
+
+/* Print current root directory */
+error_t
+cmd_getroot (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ file_t dir;
+ task_t task;
+ process_t proc = getproc ();
+
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ return err;
+ err = msg_get_init_port (msgport, task, INIT_PORT_CRDIR, &dir);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+ }
+ printf ("%d: %s\n", pid,
+ _hurd_canonicalize_directory_name_internal(dir, NULL, 0));
+ mach_port_deallocate (mach_task_self (), dir);
+ mach_port_deallocate (mach_task_self (), task);
+ return 0;
+}
+
+/* Change umask to ARGV[0] (octal value). Without arguments, print
+ the value of current umask. */
+error_t
+cmd_umask (pid_t pid, mach_port_t msgport, int argc, char *argv[])
+{
+ error_t err;
+ mode_t umask;
+ task_t task;
+ process_t proc = getproc ();
+
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ return err;
+ if (argc)
+ {
+ umask = strtol(argv[0], 0, 8);
+ err = msg_set_init_int (msgport, task, INIT_UMASK, umask);
+ }
+ else
+ {
+ err = msg_get_init_int (msgport, task, INIT_UMASK, &umask);
+ if (!err)
+ printf ("%d: %03o\n", pid, umask);
+ }
+ mach_port_deallocate (mach_task_self (), task);
+ return err;
+}
+
+
+#define OA OPTION_ARG_OPTIONAL
+
+#define CMD_GETENV 1000
+#define CMD_SETENV 1001
+#define CMD_CLRENV 1002
+#define CMD_CHCWDIR 1003
+#define CMD_CHCRDIR 1004
+#define CMD_CDROOT 1005
+#define CMD_UMASK 1006
+#define CMD_SETFD 1007
+#define CMD_STDIN 1008
+#define CMD_STDOUT 1009
+#define CMD_STDERR 1010
+#define CMD_PWD 1011
+#define CMD_GETROOT 1012
+
+/* Params to be passed as the input when parsing CMDS_ARGP. */
+struct cmds_argp_params
+{
+ /* Array to be extended with parsed cmds. */
+ cmd_t **cmds;
+ size_t *num_cmds;
+};
+
+static const struct argp_option cmd_options[] =
+{
+ {"getenv", CMD_GETENV, "VAR", OA, "Get environment variable"},
+ {"printenv", 0, 0, OPTION_ALIAS},
+ {"setenv", CMD_SETENV, "VAR VALUE", 0, "Set environment variable"},
+ {"clearenv", CMD_CLRENV, 0, 0, "Clear environment"},
+ {"pwd", CMD_PWD, 0, 0, "Print current working directory"},
+ {"getcwd", 0, 0, OPTION_ALIAS},
+ {"getroot", CMD_GETROOT,0, 0, "Print current root directory"},
+ {"setfd", CMD_SETFD, "FD FILE [rwxa]", 0, "Change file descriptor"},
+ {"stdin", CMD_STDIN, "FILE [rwxa]", 0, "Change standard input"},
+ {"stdout", CMD_STDOUT, "FILE [rwxa]", 0, "Change standard output"},
+ {"stderr", CMD_STDERR, "FILE [rwxa]", 0, "Change standard error"},
+ {"chdir", CMD_CHCWDIR,"DIR", 0, "Change current working directory"},
+ {"cd", 0, 0, OPTION_ALIAS},
+ {"chroot", CMD_CHCRDIR,"DIR", 0, "Change current root directory"},
+ {"cdroot", CMD_CDROOT, 0, 0, "Change cwd to root directory"},
+ {"umask", CMD_UMASK, "MASK", OA, "Change umask"},
+ {0, 0}
+};
+
+/* Add a new command to the array of commands already parsed
+ reallocating it in malloced memory. FUNC is the command function.
+ MINARGS and MAXARGS are the minimum and maximum number of arguments
+ the parser will accept for this command. Further checking of the
+ arguments should be done in FUNC. ARG is the next argument in the
+ command line (probably the first argument for this command). STATE
+ is the argp parser state as used in parse_cmd_opt. */
+static error_t
+add_cmd (cmd_func_t func, size_t minargs, size_t maxargs,
+ char *arg, struct argp_state *state)
+{
+ cmd_t *cmd;
+ size_t i = 0;
+
+ struct cmds_argp_params *params = state->input;
+ size_t num_cmds = *params->num_cmds + 1;
+ cmd_t *cmds = realloc (*params->cmds, num_cmds * sizeof(cmd_t));
+
+ *params->cmds = cmds;
+ *params->num_cmds = num_cmds;
+
+ cmd = &cmds[num_cmds-1];
+ cmd->f = func;
+ cmd->args = 0;
+ if (maxargs)
+ {
+ cmd->args = malloc (maxargs * sizeof (char *));
+ if (arg)
+ cmd->args[i++] = arg;
+ while (i < maxargs
+ && state->argv[state->next]
+ && state->argv[state->next][0] != '-')
+ cmd->args[i++] = state->argv[state->next++];
+ }
+ if (i < minargs || i > maxargs)
+ argp_usage(state);
+ cmd->num_args = i;
+ return 0;
+}
+
+/* Parse one option/arg for the argp parser cmds_argp (see argp.h). */
+static error_t
+parse_cmd_opt (int key, char *arg, struct argp_state *state)
+{
+ /* A buffer used for rewriting command line arguments without dashes
+ for the parser to understand them. It gets realloced for each
+ successive arg that needs it, on the assumption that args don't
+ get parsed multiple times. */
+ static char *arg_hack_buf = 0;
+ switch (key)
+ {
+ case ARGP_KEY_ARG: /* Non-option argument. */
+ if (!isdigit (*arg) && !state->quoted)
+ {
+ /* Make state->next point to the just parsed argument to
+ re-parse it with 2 dashes prepended. */
+ size_t len = strlen (arg) + 1;
+ arg_hack_buf = realloc (arg_hack_buf, 2 + len);
+ state->argv[--state->next] = arg_hack_buf;
+ state->argv[state->next][0] = '-';
+ state->argv[state->next][1] = '-';
+ memcpy (&state->argv[state->next][2], arg, len);
+ break;
+ }
+ else
+ return ARGP_ERR_UNKNOWN;
+ case CMD_CHCWDIR:
+ add_cmd (&cmd_chcwdir, 0, 1, arg, state);
+ break;
+ case CMD_CHCRDIR:
+ add_cmd (&cmd_chcrdir, 1, 1, arg, state);
+ break;
+ case CMD_CDROOT:
+ add_cmd (&cmd_cdroot, 0, 0, arg, state);
+ break;
+ case CMD_PWD:
+ add_cmd (&cmd_pwd, 0, 0, arg, state);
+ break;
+ case CMD_GETROOT:
+ add_cmd (&cmd_getroot, 0, 0, arg, state);
+ break;
+ case CMD_UMASK:
+ add_cmd (&cmd_umask, 0, 1, arg, state);
+ break;
+ case CMD_GETENV:
+ add_cmd (&cmd_getenv, 0, 1, arg, state);
+ break;
+ case CMD_SETENV:
+ add_cmd (&cmd_setenv, 2, 2, arg, state);
+ break;
+ case CMD_CLRENV:
+ add_cmd (&cmd_clearenv, 0, 0, arg, state);
+ break;
+ case CMD_SETFD:
+ add_cmd (&cmd_setfd, 2, 3, arg, state);
+ break;
+ case CMD_STDIN:
+ add_cmd (&cmd_stdin, 1, 2, arg, state);
+ break;
+ case CMD_STDOUT:
+ add_cmd (&cmd_stdout, 1, 2, arg, state);
+ break;
+ case CMD_STDERR:
+ add_cmd (&cmd_stderr, 1, 2, arg, state);
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Filtering of help output strings for cmds_argp parser. Return a
+ malloced replacement for TEXT as the arguments doc string. See
+ argp.h for details. */
+static char *
+help_filter (int key, const char *text, void *input)
+{
+ if (key == ARGP_KEY_HELP_ARGS_DOC)
+ return strdup ("CMD [ARG...]");
+
+ return (char *)text;
+}
+
+/* An argp parser for selecting a command (see argp.h). */
+struct argp cmds_argp = { cmd_options, parse_cmd_opt, 0, 0, 0, help_filter };
+
+
+
+int
+main(int argc, char *argv[])
+{
+ cmd_t *cmds = 0;
+ size_t num_cmds = 0;
+ struct cmds_argp_params cmds_argp_params = { &cmds, &num_cmds };
+ pid_t *pids = 0; /* User-specified pids. */
+ size_t num_pids = 0;
+ struct pids_argp_params pids_argp_params = { &pids, &num_pids, 0 };
+
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ /* Initialize inputs for child parsers. */
+ state->child_inputs[0] = &cmds_argp_params;
+ state->child_inputs[1] = &pids_argp_params;
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ if (!num_cmds || !num_pids)
+ argp_usage (state);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+
+ struct argp_child argp_kids[] =
+ { { &cmds_argp, 0,
+ "Commands:", 2},
+ { &pids_argp, 0,
+ "Process selection:", 3},
+ {0} };
+
+ struct argp argp = { options, parse_opt, args_doc, doc, argp_kids };
+
+ error_t err;
+ pid_t cur_pid = getpid ();
+ pid_t pid;
+ cmd_t cmd;
+ size_t i, j;
+
+ /* Parse our command line. This shouldn't ever return an error. */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ for (i = 0; i < num_pids; ++i)
+ {
+ pid = pids[i];
+ if (pid != cur_pid)
+ for (j = 0; j < num_cmds; ++j)
+ {
+ cmd = cmds[j];
+ if ((err = do_cmd (pid, cmd)))
+ error (2, err, "%d: Cannot execute command", pid);
+ }
+ }
+
+ exit (0);
+}
diff --git a/utils/nonsugid.c b/utils/nonsugid.c
new file mode 100644
index 00000000..71cd3d71
--- /dev/null
+++ b/utils/nonsugid.c
@@ -0,0 +1,63 @@
+/* Get our ids, minus any setuid result
+
+ Copyright (C) 1995,96,97,2000 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <errno.h>
+#include <idvec.h>
+#include <hurd.h>
+
+/* Make sure that the [UG]IDS are filled in. To make them useful for
+ su'ing, each is the avail ids with the saved set-ID removed, and all
+ effective ids but the first appended; this gets rid of the effect of
+ being suid, and is useful as a new process's avail id list (e.g., the
+ real id is right). */
+error_t
+get_nonsugid_ids (struct idvec *uids, struct idvec *gids)
+{
+ if (uids->num == 0 && gids->num == 0)
+ {
+ error_t err = 0;
+ static auth_t auth = MACH_PORT_NULL;
+ struct idvec *p_eff_uids = make_idvec ();
+ struct idvec *p_eff_gids = make_idvec ();
+
+ if (!p_eff_uids || !p_eff_gids)
+ err = ENOMEM;
+
+ if (auth == MACH_PORT_NULL)
+ auth = getauth ();
+
+ if (! err)
+ err = idvec_merge_auth (p_eff_uids, uids, p_eff_gids, gids, auth);
+ if (! err)
+ {
+ idvec_delete (p_eff_uids, 0); /* Remove effective ID from setuid. */
+ idvec_delete (p_eff_gids, 0);
+ idvec_delete (uids, 1); /* Remove saved set-ID from setuid. */
+ idvec_delete (gids, 1);
+ if (! err)
+ err = idvec_merge (uids, p_eff_uids);
+ if (! err)
+ err = idvec_merge (gids, p_eff_gids);
+ }
+
+ return err;
+ }
+ else
+ return 0;
+}
diff --git a/utils/nullauth.c b/utils/nullauth.c
new file mode 100644
index 00000000..a0d5d1b8
--- /dev/null
+++ b/utils/nullauth.c
@@ -0,0 +1,90 @@
+/* Utility to drop all authentication credentials.
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <argp.h>
+#include <error.h>
+#include <nullauth.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <version.h>
+
+static char **args;
+
+const char const *argp_program_version = STANDARD_HURD_VERSION (nullauth);
+
+static const struct argp_option const options[] =
+{
+ { 0 }
+};
+
+static const char const doc[] =
+ "Drop all authentication credentials and run the given program.";
+static const char const args_doc[] =
+ "PROGRAM [ARGUMENTS...]\tThe program to run";
+
+error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case ARGP_KEY_ARGS:
+ args = state->argv + state->next;
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_error (state, "expected program to run");
+ return EINVAL;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static struct argp argp = {
+ options,
+ parse_opt,
+ args_doc,
+ doc,
+ NULL,
+};
+
+int
+main (int argc, char *argv[])
+{
+ error_t err;
+
+ /* Parse our command line. This shouldn't ever return an error. */
+ argp_parse (&argp, argc, argv, 0, 0, NULL);
+
+ /* Drop all privileges. */
+ err = setnullauth();
+ if (err)
+ error (1, err, "Could not drop privileges");
+
+ execv (args[0], args);
+ error (1, errno, "execv");
+
+ /* Not reached. */
+ return EXIT_FAILURE;
+}
diff --git a/utils/parse.c b/utils/parse.c
new file mode 100644
index 00000000..5334dbda
--- /dev/null
+++ b/utils/parse.c
@@ -0,0 +1,185 @@
+/* Random helpful option parsing functions
+
+ Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <error.h>
+
+#include "parse.h"
+
+/* For each string in the comma-separated list in ARG, call ADD_FN; if ARG is
+ empty and DEFAULT_ADD_FN isn't NULL, then call DEFAULT_ADD_FN instead. */
+error_t
+_parse_strlist (char *arg,
+ error_t (*add_fn)(const char *str, struct argp_state *state),
+ error_t (*default_add_fn)(struct argp_state *state),
+ const char *type_name, struct argp_state *state)
+{
+ if (arg)
+ while (isspace(*arg))
+ arg++;
+
+ if (arg == NULL || *arg == '\0')
+ if (default_add_fn)
+ return (*default_add_fn)(state);
+ else
+ {
+ argp_error (state, "Empty %s list", type_name);
+ return EINVAL;
+ }
+ else
+ {
+ error_t err = 0;
+ char *end = arg;
+
+ void mark_end()
+ {
+ *end++ = '\0';
+ while (isspace(*end))
+ end++;
+ }
+ error_t parse_element()
+ {
+ char *cur = arg;
+ if (*cur == '\0')
+ {
+ argp_error (state, "Empty element in %s list", type_name);
+ return EINVAL;
+ }
+ arg = end;
+ return (*add_fn)(cur, state);
+ }
+
+ while (*end != '\0' && !err)
+ switch (*end)
+ {
+ case ' ': case '\t':
+ mark_end();
+ if (*end == ',')
+ mark_end();
+ err = parse_element();
+ break;
+ case ',':
+ mark_end();
+ err = parse_element();
+ break;
+ default:
+ end++;
+ }
+
+ if (! err)
+ err = parse_element();
+
+ return err;
+ }
+}
+
+/* For each string in the comma-separated list in ARG, call ADD_FN; if ARG is
+ empty and DEFAULT_FN isn't NULL, then call ADD_FN on the resutl of calling
+ DEFAULT_FN instead, otherwise signal an error. */
+error_t
+parse_strlist (char *arg,
+ error_t (*add_fn)(const char *str, struct argp_state *state),
+ const char *(*default_fn)(struct argp_state *state),
+ const char *type_name, struct argp_state *state)
+{
+ error_t default_str_add (struct argp_state *state)
+ { return (*add_fn)((*default_fn)(state), state); }
+ return _parse_strlist (arg, add_fn, default_str_add, type_name, state);
+}
+
+/* For each numeric string in the comma-separated list in ARG, call ADD_FN;
+ if ARG is empty and DEFAULT_FN isn't NULL, then call DEF_FN to get a number,
+ and call ADD_FN on that, otherwise signal an error. If any member of the
+ list isn't a number, and LOOKUP_FN isn't NULL, then it is called to return
+ an integer for the string. LOOKUP_FN should signal an error itself it
+ there's some problem parsing the string. */
+error_t
+parse_numlist (char *arg,
+ error_t (*add_fn)(unsigned num, struct argp_state *state),
+ int (*default_fn)(struct argp_state *state),
+ int (*lookup_fn)(const char *str, struct argp_state *state),
+ const char *type_name, struct argp_state *state)
+{
+ error_t default_num_add() { return (*add_fn)((*default_fn)(state), state); }
+ error_t add_num_str(const char *str, struct argp_state *state)
+ {
+ const char *p;
+ for (p = str; *p != '\0'; p++)
+ if (!isdigit(*p))
+ {
+ if (lookup_fn)
+ return (*add_fn)((*lookup_fn)(str, state), state);
+ else
+ {
+ argp_error (state, "%s: Invalid %s", p, type_name);
+ return EINVAL;
+ }
+ return 0;
+ }
+ return (*add_fn) (atoi (str), state);
+ }
+ return _parse_strlist(arg, add_num_str, default_fn ? default_num_add : 0,
+ type_name, state);
+}
+
+/* Return the index of which of a set of strings ARG matches, including
+ non-ambiguous partial matches. CHOICE_FN should be a function that
+ returns the name of the Nth option, or 0 if N is out of range, and KIND
+ should be a string that describes what's being matched, for use in error
+ messages. If ALLOW_MISMATCHES is true, then -1 is returned in the event
+ that ARG matches no entry , otherwise, an error message is printed and the
+ program exits in this event. If ARG is an ambiguous match, an error
+ message is printed and the program exits. */
+int
+parse_enum (const char *arg,
+ const char *(*choice_fn)(unsigned n),
+ const char *kind, int allow_mismatches,
+ struct argp_state *state)
+{
+ const char *choice;
+ int arglen = strlen (arg);
+ int n = 0;
+ int partial_match = -1;
+
+ while ((choice = (*choice_fn)(n)) != NULL)
+ if (strcasecmp (choice, arg) == 0)
+ return n;
+ else
+ {
+ if (strncasecmp (choice, arg, arglen) == 0)
+ {
+ if (partial_match >= 0)
+ {
+ argp_error (state, "%s: Ambiguous %s", arg, kind);
+ return -1;
+ }
+ else
+ partial_match = n;
+ }
+ n++;
+ }
+
+ if (partial_match < 0 && !allow_mismatches)
+ argp_error (state, "%s: Invalid %s", arg, kind);
+
+ return partial_match;
+}
diff --git a/utils/parse.h b/utils/parse.h
new file mode 100644
index 00000000..9b6f31f9
--- /dev/null
+++ b/utils/parse.h
@@ -0,0 +1,63 @@
+/* Random helpful option parsing functions
+
+ Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __PARSE_H__
+#define __PARSE_H__
+
+#include <errno.h>
+#include <argp.h>
+
+/* For each string in the comma-separated list in ARG, call ADD_FN; if ARG is
+ empty and DEFAULT_FN isn't NULL, then call ADD_FN on the resutl of calling
+ DEFAULT_FN instead, otherwise signal an error. */
+extern error_t
+parse_strlist (char *arg,
+ error_t (*add_fn)(const char *str, struct argp_state *state),
+ const char *(*default_fn)(struct argp_state *state),
+ const char *type_name, struct argp_state *state);
+
+/* For each numeric string in the comma-separated list in ARG, call ADD_FN;
+ if ARG is empty and DEFAULT_FN isn't NULL, then call DEF_FN to get a number,
+ and call ADD_FN on that, otherwise signal an error. If any member of the
+ list isn't a number, and LOOKUP_FN isn't NULL, then it is called to return
+ an integer for the string. LOOKUP_FN should signal an error itself it
+ there's some problem parsing the string. */
+extern error_t
+parse_numlist (char *arg,
+ error_t (*add_fn)(unsigned num, struct argp_state *state),
+ int (*default_fn)(struct argp_state *state),
+ int (*lookup_fn)(const char *str, struct argp_state *state),
+ const char *type_name, struct argp_state *state);
+
+/* Return the index of which of a set of strings ARG matches, including
+ non-ambiguous partial matches. CHOICE_FN should be a function that
+ returns the name of the Nth option, or 0 if N is out of range, and KIND
+ should be a string that describes what's being matched, for use in error
+ messages. If ALLOW_MISMATCHES is true, then -1 is returned in the event
+ that ARG matches no entry , otherwise, an error message is printed and the
+ program exits in this event. If ARG is an ambiguous match, an error
+ message is printed and the program exits. */
+extern int
+parse_enum (const char *arg,
+ const char *(*choice_fn)(unsigned n),
+ const char *kind, int allow_mismatches,
+ struct argp_state *state);
+
+#endif /* __PARSE_H__ */
diff --git a/utils/pids.c b/utils/pids.c
new file mode 100644
index 00000000..44cd0b44
--- /dev/null
+++ b/utils/pids.c
@@ -0,0 +1,231 @@
+/* Pid parsing/frobbing
+
+ Copyright (C) 1997,99,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <argp.h>
+#include <hurd.h>
+#include <hurd/process.h>
+#include <mach.h>
+#include <sys/mman.h>
+
+#include "parse.h"
+#include "pids.h"
+
+static process_t _proc_server = MACH_PORT_NULL;
+
+/* Return this process's proc server. */
+static inline process_t
+proc_server ()
+{
+ if (_proc_server == MACH_PORT_NULL)
+ _proc_server = getproc ();
+ return _proc_server;
+}
+
+/* Add the pids returned in vm_allocated memory by calling PIDS_FN with ID as
+ an argument to PIDS and NUM_PIDS, reallocating it in malloced memory. */
+error_t
+add_fn_pids (pid_t **pids, size_t *num_pids, unsigned id,
+ error_t (*pids_fn)(process_t proc, pid_t id,
+ pid_t **pids, size_t *num_pids))
+{
+ size_t num_new_pids = 25;
+ pid_t _new_pids[num_new_pids], *new_pids = _new_pids;
+ error_t err = (*pids_fn)(proc_server (), id, &new_pids, &num_new_pids);
+
+ if (! err)
+ {
+ size_t new_sz = *num_pids + num_new_pids;
+ pid_t *new = realloc (*pids, new_sz * sizeof (pid_t));
+ if (new)
+ {
+ bcopy (new_pids, new + (*num_pids * sizeof (pid_t)),
+ num_new_pids * sizeof (pid_t));
+ *pids = new;
+ *num_pids = new_sz;
+ }
+ else
+ err = ENOMEM;
+ if (new_pids != _new_pids)
+ munmap (new_pids, num_new_pids * sizeof (pid_t));
+ }
+
+ return err;
+}
+
+/* Add PID to PIDS and NUM_PIDS, reallocating it in malloced memory. */
+error_t
+add_pid (pid_t **pids, size_t *num_pids, pid_t pid)
+{
+ size_t new_sz = *num_pids + 1;
+ pid_t *new = realloc (*pids, new_sz * sizeof (pid_t));
+
+ if (new)
+ {
+ new[new_sz - 1] = pid;
+ *pids = new;
+ *num_pids = new_sz;
+ return 0;
+ }
+ else
+ return ENOMEM;
+}
+
+struct pids_parse_state
+{
+ struct pids_argp_params *params;
+ struct argp_state *state;
+};
+
+/* Returns our session id. */
+static pid_t
+current_sid (struct argp_state *state)
+{
+ pid_t sid = -1;
+ error_t err = proc_getsid (proc_server (), getpid (), &sid);
+ if (err)
+ argp_failure (state, 2, err, "Couldn't get current session id");
+ return sid;
+}
+
+/* Returns our login collection id. */
+static pid_t
+current_lid (struct argp_state *state)
+{
+ pid_t lid = -1;
+ error_t err = proc_getloginid (proc_server (), getpid (), &lid);
+ if (err)
+ argp_failure (state, 2, err, "Couldn't get current login collection");
+ return lid;
+}
+
+/* Add a specific process to be printed out. */
+static error_t
+parse_pid (unsigned pid, struct argp_state *state)
+{
+ struct pids_argp_params *params = state->input;
+ error_t err = add_pid (params->pids, params->num_pids, pid);
+ if (err)
+ argp_failure (state, 2, err, "%d: Cannot add process", pid);
+ return err;
+}
+
+/* Print out all process from the given session. */
+static error_t
+parse_sid (unsigned sid, struct argp_state *state)
+{
+ struct pids_argp_params *params = state->input;
+ error_t err =
+ add_fn_pids (params->pids, params->num_pids, sid, proc_getsessionpids);
+ if (err)
+ argp_failure (state, 2, err, "%d: Cannot add session", sid);
+ return err;
+}
+
+/* Print out all process from the given login collection. */
+static error_t
+parse_lid (unsigned lid, struct argp_state *state)
+{
+ struct pids_argp_params *params = state->input;
+ error_t err =
+ add_fn_pids (params->pids, params->num_pids, lid, proc_getloginpids);
+ if (err)
+ argp_failure (state, 2, err, "%d: Cannot add login collection", lid);
+ return err;
+}
+
+/* Print out all process from the given process group. */
+static error_t
+parse_pgrp (unsigned pgrp, struct argp_state *state)
+{
+ struct pids_argp_params *params = state->input;
+ error_t err =
+ add_fn_pids (params->pids, params->num_pids, pgrp, proc_getpgrppids);
+ if (err)
+ argp_failure (state, 2, err, "%d: Cannot add process group", pgrp);
+ return err;
+}
+
+#define OA OPTION_ARG_OPTIONAL
+
+/* Options for PIDS_ARGP. */
+static const struct argp_option options[] =
+{
+ {"login", 'L', "LID", OA, "Processes from the login"
+ " collection LID (which defaults that of"
+ " the current process)"},
+ {"lid", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"pid", 'p', "PID", 0, "The process PID"},
+ {"pgrp", 'P', "PGRP", 0, "Processes in process group PGRP"},
+ {"session", 'S', "SID", OA, "Processes from the session SID"
+ " (which defaults to that of the"
+ " current process)"},
+ {"sid", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {0, 0}
+};
+
+/* Parse one option/arg for PIDS_ARGP. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct pids_argp_params *params = state->input;
+
+ switch (key)
+ {
+ case 'p':
+ return
+ parse_numlist (arg, parse_pid, NULL, NULL, "process id", state);
+ case 'S':
+ return
+ parse_numlist (arg, parse_sid, current_sid, NULL, "session id", state);
+ case 'L':
+ return
+ parse_numlist (arg, parse_lid, current_lid, NULL, "login collection",
+ state);
+ case 'P':
+ return
+ parse_numlist (arg, parse_pgrp, NULL, NULL, "process group", state);
+
+ case ARGP_KEY_ARG:
+ if (params->parse_pid_args)
+ return parse_numlist (arg, parse_pid, NULL, NULL, "process id", state);
+ /* Else fall through */
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+}
+
+/* Filtering of help output strings for PIDS_ARGP. */
+static char *
+help_filter (int key, const char *text, void *input)
+{
+ struct pids_argp_params *params = input;
+
+ /* Describe the optional behavior of parsing normal args as pids. */
+ if (key == ARGP_KEY_HELP_ARGS_DOC && params->parse_pid_args)
+ return strdup ("[PID...]");
+
+ return (char *)text;
+}
+
+/* A parser for selecting a set of pids. */
+struct argp pids_argp = { options, parse_opt, 0, 0, 0, help_filter };
diff --git a/utils/pids.h b/utils/pids.h
new file mode 100644
index 00000000..dcf87e81
--- /dev/null
+++ b/utils/pids.h
@@ -0,0 +1,47 @@
+/* Pid parsing/frobbing
+
+ Copyright (C) 1997,2001 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __PIDS_H__
+#define __PIDS_H__
+
+/* Add the pids returned in vm_allocated memory by calling PIDS_FN with ID as
+ an argument to PIDS and NUM_PIDS, reallocating it in malloced memory. */
+extern error_t add_fn_pids (pid_t **pids, size_t *num_pids, unsigned id,
+ error_t (*pids_fn)(process_t proc, pid_t id,
+ pid_t **pids, size_t *num_pids));
+
+/* Add PID to PIDS and NUM_PIDS, reallocating it in malloced memory. */
+extern error_t add_pid (pid_t **pids, size_t *num_pids, pid_t pid);
+
+/* Params to be passed as the input when parsing PIDS_ARGP. */
+struct pids_argp_params
+{
+ /* Array to be extended with parsed pids. */
+ pid_t **pids;
+ size_t *num_pids;
+
+ /* If true, parse non-option arguments as pids. */
+ int parse_pid_args;
+};
+
+/* A parser for selecting a set of pids. */
+extern struct argp pids_argp;
+
+#endif /* __PIDS_H__ */
diff --git a/utils/portinfo.c b/utils/portinfo.c
new file mode 100644
index 00000000..4c403526
--- /dev/null
+++ b/utils/portinfo.c
@@ -0,0 +1,350 @@
+/* Print information about a task's ports
+
+ Copyright (C) 1996,97,98,99, 2000,13 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <argp.h>
+#include <error.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <version.h>
+
+#include <mach.h>
+
+#include <hurd.h>
+#include <hurd/process.h>
+#include <ps.h>
+#include <sys/mman.h>
+
+#include <portinfo.h>
+#include <portxlate.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (portinfo);
+
+static const struct argp_option options[] = {
+ {0,0,0,0,0, 1},
+ {"verbose", 'v', 0, 0, "Give more detailed information"},
+ {"members", 'm', 0, 0, "Show members of port-sets"},
+ {"hex-names", 'x', 0, 0, "Show port names in hexadecimal"},
+#if 0 /* XXX implement this */
+ {"query-process", 'q', 0, 0, "Query the process itself for the identity of"
+ " the ports in question -- requires the process be in a sane state"},
+#endif
+ {"hold", '*', 0, OPTION_HIDDEN},
+
+ {0,0,0,0, "Selecting which names to show:", 2},
+ {"receive", 'r', 0, 0, "Show ports with receive rights"},
+ {"send", 's', 0, 0, "Show ports with send rights"},
+ {"send-once", 'o', 0, 0, "Show ports with send once rights"},
+ {"dead-names",'d', 0, 0, "Show dead names"},
+ {"port-sets", 'p', 0, 0, "Show port sets"},
+
+ {0,0,0,0, "Translating port names between tasks:", 3},
+ {"translate", 't', "PID", 0, "Translate port names to process PID"},
+#if 0
+ {"show-targets", 'h', 0, 0,
+ "Print a header describing the target process" },
+#endif
+ {"no-translation-errors", 'E', 0, 0,
+ "Don't display an error if a specified port can't be translated" },
+ {"search", 'a', 0, 0, "Search all processes for the given ports"},
+#if 0
+ {"target-receive", 'R', 0, 0,
+ "Only show ports that translate into receive rights"},
+ {"target-send", 'S', 0, 0,
+ "Only show ports that translate into send rights"},
+ {"target-send-once",'O', 0, 0,
+ "Only show ports that translate into send-once rights"},
+ "Only show ports that translate into send once rights"},
+#endif
+
+ {0}
+};
+static const char *args_doc = "PID [NAME...]";
+static const char *doc =
+"Show information about mach ports NAME... (default all ports) in process PID."
+"\vIf no port NAMEs are given, all ports in process PID are reported (if"
+" translation is used, then only those common to both processes). NAMEs"
+" may be specified in hexadecimal or octal by using a 0x or 0 prefix.";
+
+/* Return the task corresponding to the user argument ARG, exiting with an
+ appriate error message if we can't. */
+static task_t
+parse_task (char *arg)
+{
+ error_t err;
+ task_t task;
+ char *arg_end;
+ pid_t pid = strtoul (arg, &arg_end, 10);
+ static process_t proc = MACH_PORT_NULL;
+
+ if (*arg == '\0' || *arg_end != '\0')
+ error (10, 0, "%s: Invalid process id", arg);
+
+ if (proc == MACH_PORT_NULL)
+ proc = getproc ();
+
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ error (11, err, "%s", arg);
+ else if (task == MACH_PORT_NULL)
+ error (11, 0, "%s: Process %d is dead and has no task", arg, (int) pid);
+
+ return task;
+}
+
+/* Functions searching for local ports in all processes. */
+
+/* Locates the port NAME from TASK in any other process and prints the
+ mappings. */
+error_t
+search_for_port (task_t task, mach_port_t name, unsigned show)
+{
+ error_t err;
+
+ /* These resources are freed in the function epilogue. */
+ struct ps_context *context = NULL;
+ struct proc_stat_list *procset = NULL;
+
+ /* Print infos about this port. */
+ err = print_port_info (name, 0, task, show, stdout);
+ if (err)
+ goto out;
+
+ static process_t proc = MACH_PORT_NULL;
+ if (proc == MACH_PORT_NULL)
+ proc = getproc ();
+
+ pid_t pid;
+ err = proc_task2pid (proc, task, &pid);
+ if (err)
+ goto out;
+
+ /* Get a list of all processes. */
+ err = ps_context_create (getproc (), &context);
+ if (err)
+ goto out;
+
+ err = proc_stat_list_create (context, &procset);
+ if (err)
+ goto out;
+
+ err = proc_stat_list_add_all (procset, 0, 0);
+ if (err)
+ goto out;
+
+ for (unsigned i = 0; i < procset->num_procs; i++)
+ {
+ /* Ignore the target process. */
+ if (procset->proc_stats[i]->pid == pid)
+ continue;
+
+ task_t xlate_task = MACH_PORT_NULL;
+ err = proc_pid2task (proc, procset->proc_stats[i]->pid, &xlate_task);
+ if (err || xlate_task == MACH_PORT_NULL)
+ continue;
+
+ struct port_name_xlator *xlator = NULL;
+ err = port_name_xlator_create (task, xlate_task, &xlator);
+ if (err)
+ goto loop_cleanup;
+
+ mach_port_t translated_port;
+ mach_msg_type_name_t translated_type;
+ err = port_name_xlator_xlate (xlator,
+ name, 0,
+ &translated_port, &translated_type);
+ if (err)
+ goto loop_cleanup;
+
+ /* The port translation was successful, print more infos. */
+ printf ("% 5i -> % 5i: ", pid, procset->proc_stats[i]->pid);
+
+ err = print_xlated_port_info (name, 0, xlator, show, stdout);
+ if (err)
+ goto loop_cleanup;
+
+ loop_cleanup:
+ if (xlate_task)
+ mach_port_deallocate (mach_task_self (), xlate_task);
+
+ if (xlator)
+ port_name_xlator_free (xlator);
+ }
+
+ err = 0;
+
+ out:
+ if (procset != NULL)
+ proc_stat_list_free (procset);
+
+ if (context != NULL)
+ ps_context_free (context);
+
+ return err;
+}
+
+/* Locates all ports from TASK in any other process and prints the
+ mappings. */
+error_t
+search_for_ports (task_t task, mach_port_type_t only, unsigned show)
+{
+ error_t err;
+
+ mach_port_t *names = NULL;
+ mach_port_type_t *types = NULL;
+ mach_msg_type_number_t names_len = 0;
+ mach_msg_type_number_t types_len = 0;
+ err = mach_port_names (task, &names, &names_len, &types, &types_len);
+ if (err)
+ return err;
+
+ for (mach_msg_type_number_t i = 0; i < names_len; i++)
+ if (types[i] & only)
+ search_for_port (task, names[i], show);
+
+ munmap ((caddr_t) names, names_len * sizeof *names);
+ munmap ((caddr_t) types, types_len * sizeof *types);
+
+ return 0;
+}
+
+static volatile int hold = 0;
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ task_t task;
+ int search = 0;
+ unsigned show = 0; /* what info we print */
+ mach_port_type_t only = 0, target_only = 0; /* Which names to show */
+ task_t xlate_task = MACH_PORT_NULL;
+ int no_translation_errors = 0; /* inhibit complaints about bad names */
+ struct port_name_xlator *xlator = 0;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'v': show |= PORTINFO_DETAILS; break;
+ case 'm': show |= PORTINFO_MEMBERS; break;
+ case 'x': show |= PORTINFO_HEX_NAMES; break;
+
+ case 'r': only |= MACH_PORT_TYPE_RECEIVE; break;
+ case 's': only |= MACH_PORT_TYPE_SEND; break;
+ case 'o': only |= MACH_PORT_TYPE_SEND_ONCE; break;
+ case 'd': only |= MACH_PORT_TYPE_DEAD_NAME; break;
+ case 'p': only |= MACH_PORT_TYPE_PORT_SET; break;
+
+ case 'R': target_only |= MACH_PORT_TYPE_RECEIVE; break;
+ case 'S': target_only |= MACH_PORT_TYPE_SEND; break;
+ case 'O': target_only |= MACH_PORT_TYPE_SEND_ONCE; break;
+
+ case 't': xlate_task = parse_task (arg); break;
+ case 'a': search = 1; break;
+ case 'E': no_translation_errors = 1; break;
+
+ case '*':
+ hold = 1;
+ while (hold)
+ sleep (1);
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ return EINVAL;
+
+ case ARGP_KEY_ARG:
+ if (state->arg_num == 0)
+ /* The task */
+ {
+ task = parse_task (arg);
+
+ if (only == 0)
+ only = ~0;
+ if (target_only == 0)
+ target_only = ~0;
+
+ if (xlate_task != MACH_PORT_NULL)
+ {
+ if (search)
+ argp_error (state,
+ "Both --search and --translate specified");
+ err = port_name_xlator_create (task, xlate_task, &xlator);
+ if (err)
+ error (13, err, "Cannot setup task translation");
+ }
+
+ if (state->next == state->argc)
+ /* No port names specified, print all of them. */
+ {
+ if (xlator)
+ err = print_xlated_task_ports_info (xlator, only,
+ show, stdout);
+ else if (search)
+ err = search_for_ports (task, only, show);
+ else
+ err = print_task_ports_info (task, only, show, stdout);
+ if (err)
+ error (12, err, "%s", arg);
+ }
+ break;
+ }
+
+ /* A port name */
+ {
+ char *end;
+ mach_port_t name = strtoul (arg, &end, 0);
+ if (name == 0)
+ error (0, 0, "%s: Invalid port name", arg);
+ else
+ {
+ if (xlator)
+ {
+ err = print_xlated_port_info (name, 0, xlator,
+ show, stdout);
+ if (err && no_translation_errors)
+ break;
+ }
+ else if (search)
+ err = search_for_port (task, name, show);
+ else
+ err = print_port_info (name, 0, task, show, stdout);
+ if (err)
+ error (0, err, "%s", arg);
+ }
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ const struct argp argp = { options, parse_opt, args_doc, doc };
+
+ /* Parse our arguments. */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ exit (0);
+}
diff --git a/utils/ps.c b/utils/ps.c
new file mode 100644
index 00000000..2abb92aa
--- /dev/null
+++ b/utils/ps.c
@@ -0,0 +1,448 @@
+/* Show process information.
+
+ Copyright (C) 1995,96,97,98,99,2002,2006 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <argp.h>
+#include <argz.h>
+#include <idvec.h>
+#include <ps.h>
+#include <error.h>
+#include <version.h>
+
+#include "psout.h"
+#include "parse.h"
+#include "pids.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (ps);
+
+#define OA OPTION_ARG_OPTIONAL
+
+static const struct argp_option options[] =
+{
+ {0,0,0,0, "Output format selection:", 1},
+ {"format", 'F', "FMT", 0, "Use the output-format FMT; FMT may be"
+ " `default', `user', `vmem', `long',"
+ " `jobc', `full', `hurd', `hurd-long',"
+ " or a custom format-string"},
+ {"posix-format",'o', "FMT", 0, "Use the posix-style output-format FMT"},
+ {0, 'f', 0, 0, "Use the `full' output-format"},
+ {0, 'j', 0, 0, "Use the `jobc' output-format"},
+ {0, 'l', 0, 0, "Use the `long' output-format"},
+ {0, 'u', 0, 0, "Use the `user' output-format"},
+ {0, 'v', 0, 0, "Use the `vmem' output-format"},
+
+ {0,0,0,0, "Process filtering (by default, other users'"
+ " processes, threads, and process-group leaders are not shown):", 2},
+ {"all-users", 'a', 0, 0, "List other users' processes"},
+ {0, 'd', 0, 0, "List all processes except process group"
+ " leaders"},
+ {"all", 'e', 0, 0, "List all processes"},
+ {0, 'A', 0, OPTION_ALIAS}, /* Posix option meaning -e */
+ {0, 'g', 0, 0, "Include session and login leaders"},
+ {"owner", 'U', "USER", 0, "Show only processes owned by USER"},
+ {"not-owner", 'O', "USER", 0, "Show only processes not owned by USER"},
+ {"no-parent", 'P', 0, 0, "Include processes without parents"},
+ {"threads", 'T', 0, 0, "Show the threads for each process"},
+ {"tty", 't', "TTY", OA, "Only show processes with controlling"
+ " terminal TTY"},
+ {0, 'x', 0, 0, "Include orphaned processes"},
+
+ {0,0,0,0, "Elision of output fields:", 4},
+ {"no-msg-port",'M', 0, 0, "Don't show info that uses a process's"
+ " msg port"},
+ {"nominal-fields",'n', 0, 0, "Don't elide fields containing"
+ " `uninteresting' data"},
+ {"all-fields", 'Q', 0, 0, "Don't elide unusable fields (normally"
+ " if there's some reason ps can't print"
+ " a field for any process, it's removed"
+ " from the output entirely)"},
+
+ {0,0,0,0, "Output attributes:"},
+ {"no-header", 'H', 0, 0, "Don't print a descriptive header line"},
+ {"reverse", 'r', 0, 0, "Reverse the order of any sort"},
+ {"sort", 's', "FIELD",0, "Sort the output with respect to FIELD,"
+ " backwards if FIELD is prefixed by `-'"},
+ {"top", 'h', "ENTRIES", OA, "Show the top ENTRIES processes"
+ " (default 10), or if ENTRIES is"
+ " negative, the bottom -ENTRIES"},
+ {"head", 0, 0, OPTION_ALIAS},
+ {"bottom", 'b', "ENTRIES", OA, "Show the bottom ENTRIES processes"
+ " (default 10)"},
+ {"tail", 0, 0, OPTION_ALIAS},
+ {"width", 'w', "WIDTH",OA, "If WIDTH is given, try to format the"
+ " output for WIDTH columns, otherwise,"
+ " remove the default limit"},
+ {0, 0}
+};
+
+static const char doc[] =
+"Show information about processes PID... (default all `interesting' processes)"
+"\vThe USER, LID, PID, PGRP, and SID arguments may also be comma separated"
+" lists. The System V options -u and -g may be accessed with -O and -G.";
+
+#define FILTER_OWNER 0x01
+#define FILTER_NOT_LEADER 0x02
+#define FILTER_CTTY 0x04
+#define FILTER_UNORPHANED 0x08
+#define FILTER_PARENTED 0x10
+
+/* A particular predefined output format. */
+struct output_fmt
+{
+ const char *name;
+ const char *sort_key; /* How this format should be sorted. */
+ const char *fmt; /* The format string. */
+};
+
+/* The predefined output formats. */
+struct output_fmt output_fmts[] =
+{
+ { "default", "pid",
+ "%^%?user %pid %th %tt %sc %stat %time %command" },
+ { "user", "-cpu",
+ "%^%user %pid %th %cpu %mem %sz %rss %tt %sc %stat %start %time %command" },
+ { "vmem", "-mem",
+ "%^%pid %th %stat %sl %pgins %pgflts %cowflts %zfills %sz %rss %cpu %mem %command"
+ },
+ { "long", "pid",
+ "%^%uid %pid %th %ppid %pri %ni %nth %msgi %msgo %sz %rss %sc %wait %stat %tt %time %command" },
+ { "jobc", "pid",
+ "%^%user %pid %th %ppid %pgrp %sess %lcoll %sc %stat %tt %time %command" },
+ { "full", "pid",
+ "%^%-user %pid %ppid %tty %time %command" },
+ { "hurd", "pid",
+ "%pid %th %uid %nth %{vsize:Vmem} %rss %{utime:User} %{stime:System} %args"
+ },
+ { "hurd-long", "pid",
+ "%pid %th %uid %ppid %pgrp %sess %nth %{vsize:Vmem} %rss %cpu %{utime:User} %{stime:System} %args"
+ }
+};
+
+/* Augment the standard specs with our own abbrevs. */
+static const struct ps_fmt_spec
+spec_abbrevs[] = {
+ {"TT=tty"}, {"SC=susp"}, {"Stat=state"}, {"Command=args"}, {"SL=sleep"},
+ {"NTH=nth", "TH"}, {"NI=bpri"}, {"SZ=vsize"}, {"RSS=rsize"},
+ {"MsgI=msgin"}, {"MsgO=msgout"},
+ {0}
+};
+static struct ps_fmt_specs ps_specs =
+ { spec_abbrevs, &ps_std_fmt_specs };
+
+/* Returns the UID for the user called NAME. */
+static int
+lookup_user (const char *name, struct argp_state *state)
+{
+ struct passwd *pw = getpwnam(name);
+ if (pw == NULL)
+ argp_failure (state, 2, 0, "%s: Unknown user", name);
+ return pw->pw_uid;
+}
+
+int
+main(int argc, char *argv[])
+{
+ error_t err;
+ /* A buffer used for rewriting old-style ps command line arguments that
+ need a dash prepended for the parser to understand them. It gets
+ realloced for each successive arg that needs it, on the assumption that
+ args don't get parsed multiple times. */
+ char *arg_hack_buf = 0;
+ struct idvec *only_uids = make_idvec (), *not_uids = make_idvec ();
+ char *tty_names = 0;
+ size_t num_tty_names = 0;
+ struct proc_stat_list *procset;
+ struct ps_context *context;
+ const char *fmt_string = "default", *sort_key_name = NULL;
+ unsigned filter_mask =
+ FILTER_OWNER | FILTER_NOT_LEADER | FILTER_UNORPHANED | FILTER_PARENTED;
+ int sort_reverse = FALSE, print_heading = TRUE;
+ int squash_bogus_fields = TRUE, squash_nominal_fields = TRUE;
+ int show_threads = FALSE, no_msg_port = FALSE;
+ int output_width = -1; /* Desired max output size. */
+ int show_non_hurd_procs = 1; /* Show non-hurd processes. */
+ int posix_fmt = 0; /* Use a posix_fmt-style format string. */
+ int top = 0; /* Number of entries to output. */
+ pid_t *pids = 0; /* User-specified pids. */
+ size_t num_pids = 0;
+ struct pids_argp_params pids_argp_params = { &pids, &num_pids, 1 };
+
+ /* Add a user who's processes should be printed out. */
+ error_t add_uid (uid_t uid, struct argp_state *state)
+ {
+ error_t err = idvec_add (only_uids, uid);
+ if (err)
+ argp_failure (state, 23, err, "Can't add uid");
+ return err;
+ }
+ /* Add a user who's processes should not be printed out. */
+ error_t add_not_uid (uid_t uid, struct argp_state *state)
+ {
+ error_t err = idvec_add (not_uids, uid);
+ if (err)
+ argp_failure (state, 23, err, "Can't add uid");
+ return err;
+ }
+ /* Returns TRUE if PS is owned by any of the users in ONLY_UIDS, and none
+ in NOT_UIDS. */
+ int proc_stat_owner_ok(struct proc_stat *ps)
+ {
+ int uid = proc_stat_owner_uid (ps);
+ if (only_uids->num > 0 && !idvec_contains (only_uids, uid))
+ return 0;
+ if (not_uids->num > 0 && idvec_contains (not_uids, uid))
+ return 0;
+ return 1;
+ }
+
+ /* Add TTY_NAME to the list for which processes with those controlling
+ terminals will be printed. */
+ error_t add_tty_name (const char *tty_name, struct argp_state *state)
+ {
+ error_t err = argz_add (&tty_names, &num_tty_names, tty_name);
+ if (err)
+ argp_failure (state, 8, err, "%s: Can't add tty", tty_name);
+ return err;
+ }
+ int proc_stat_has_ctty(struct proc_stat *ps)
+ {
+ if (proc_stat_has(ps, PSTAT_TTY))
+ /* Only match processes whose tty we can figure out. */
+ {
+ struct ps_tty *tty = proc_stat_tty (ps);
+ if (tty)
+ {
+ char *try = 0;
+ const char *name = ps_tty_name (tty);
+ const char *short_name = ps_tty_short_name(tty);
+
+ while ((try = argz_next (tty_names, num_tty_names, try)))
+ if ((name && strcmp (try, name) == 0)
+ || (short_name && strcmp (try, short_name) == 0))
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+
+ /* Returns the name of the current controlling terminal. */
+ const char *current_tty_name()
+ {
+ error_t err;
+ struct ps_tty *tty;
+ mach_port_t cttyid = getcttyid();
+
+ if (cttyid == MACH_PORT_NULL)
+ error(2, 0, "No controlling terminal");
+
+ err = ps_context_find_tty_by_cttyid (context, cttyid, &tty);
+ if (err)
+ error(2, err, "Can't get controlling terminal");
+
+ return ps_tty_name (tty);
+ }
+
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_ARG: /* Non-option argument. */
+ if (!isdigit (*arg) && !state->quoted)
+ /* Old-fashioned `ps' syntax takes options without the leading
+ dash. Prepend a dash and feed back to getopt. */
+ {
+ size_t len = strlen (arg) + 1;
+ arg_hack_buf = realloc (arg_hack_buf, 1 + len);
+ state->argv[--state->next] = arg_hack_buf;
+ state->argv[state->next][0] = '-';
+ memcpy (&state->argv[state->next][1], arg, len);
+ break;
+ }
+ else
+ /* Let PIDS_ARGP handle it. */
+ return ARGP_ERR_UNKNOWN;
+
+ case 'a': filter_mask &= ~FILTER_OWNER; break;
+ case 'd': filter_mask &= ~(FILTER_OWNER | FILTER_UNORPHANED); break;
+ case 'e': case 'A': filter_mask = 0; break;
+ case 'g': filter_mask &= ~FILTER_NOT_LEADER; break;
+ case 'x': filter_mask &= ~FILTER_UNORPHANED; break;
+ case 'P': filter_mask &= ~FILTER_PARENTED; break;
+ case 'f': fmt_string = "full"; break;
+ case 'u': fmt_string = "user"; break;
+ case 'v': fmt_string = "vmem"; break;
+ case 'j': fmt_string = "jobc"; break;
+ case 'l': fmt_string = "long"; break;
+ case 'M': no_msg_port = TRUE; break;
+ case 'H': print_heading = FALSE; break;
+ case 'Q': squash_bogus_fields = squash_nominal_fields = FALSE; break;
+ case 'n': squash_nominal_fields = FALSE; break;
+ case 'T': show_threads = TRUE; break;
+ case 's': sort_key_name = arg; break;
+ case 'r': sort_reverse = TRUE; break;
+ case 'h': top = arg ? atoi (arg) : 10; break;
+ case 'b': top = -(arg ? atoi (arg) : 10); break;
+ case 'F': fmt_string = arg; posix_fmt = 0; break;
+ case 'o': fmt_string = arg; posix_fmt = 1; break;
+
+ case 'w':
+ output_width = arg ? atoi (arg) : 0; /* 0 means `unlimited'. */
+ break;
+
+ case 't':
+ return parse_strlist (arg, add_tty_name, current_tty_name, "tty", state);
+ case 'U':
+ return parse_numlist (arg, add_uid, NULL, lookup_user, "user", state);
+ case 'O':
+ return parse_numlist (arg, add_not_uid, NULL, lookup_user, "user", state);
+
+ case ARGP_KEY_INIT:
+ /* Initialize inputs for child parsers. */
+ state->child_inputs[0] = &pids_argp_params;
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ /* Select an explicit format string if FMT_STRING is a format
+ name. This is done here because parse_enum needs STATE. */
+ {
+ if (posix_fmt)
+ break;
+ const char *fmt_name (unsigned n)
+ {
+ return
+ n >= (sizeof output_fmts / sizeof *output_fmts)
+ ? 0
+ : output_fmts[n].name;
+ }
+ int fmt_index = parse_enum (fmt_string, fmt_name,
+ "format type", 1, state);
+ if (fmt_index >= 0)
+ {
+ fmt_string = output_fmts[fmt_index].fmt;
+ if (sort_key_name == NULL)
+ sort_key_name = output_fmts[fmt_index].sort_key;
+ }
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+
+ struct argp_child argp_kids[] =
+ { { &pids_argp, 0,
+ "Process selection (before filtering; default is all processes):", 3},
+ {0} };
+ struct argp argp = { options, parse_opt, 0, doc, argp_kids };
+
+ err = ps_context_create (getproc (), &context);
+ if (err)
+ error(1, err, "ps_context_create");
+
+ /* Parse our command line. This shouldn't ever return an error. */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ err = proc_stat_list_create(context, &procset);
+ if (err)
+ error(1, err, "proc_stat_list_create");
+
+ if (num_pids == 0)
+ /* No explicit processes specified. */
+ {
+ err = proc_stat_list_add_all (procset, 0, 0);
+
+ /* Try to avoid showing non-hurd processes if this isn't a native-booted
+ hurd system (because there would be lots of them). Here we use a
+ simple heuristic: Is the 5th process a hurd process (1-4 are
+ typically: proc server, init, kernel, boot default pager (maybe); the
+ last two are know not to be hurd processes)? */
+ if (procset->num_procs > 4)
+ {
+ struct proc_stat *ps = procset->proc_stats[4];
+ if (proc_stat_set_flags (ps, PSTAT_STATE) == 0
+ && (ps->flags & PSTAT_STATE))
+ show_non_hurd_procs = !(ps->state & PSTAT_STATE_P_NOPARENT);
+ else
+ /* Something is fucked, we can't get the state bits for PS.
+ Default to showing everything. */
+ show_non_hurd_procs = 1;
+ }
+ }
+ else
+ /* User-specified processes. */
+ {
+ err = proc_stat_list_add_pids (procset, pids, num_pids, 0);
+ filter_mask = 0; /* Don't mess with them. */
+ }
+
+ if (err)
+ error(2, err, "Can't get process list");
+
+ if (no_msg_port)
+ proc_stat_list_set_flags(procset, PSTAT_NO_MSGPORT);
+
+ if (only_uids->num == 0 && (filter_mask & FILTER_OWNER))
+ /* Restrict the output to only our own processes. */
+ {
+ int uid = getuid ();
+ if (uid >= 0)
+ add_uid (uid, 0);
+ else
+ filter_mask &= ~FILTER_OWNER; /* Must be an anonymous process. */
+ }
+
+ /* Filter out any processes that we don't want to show. */
+ if (only_uids->num || not_uids->num)
+ proc_stat_list_filter1 (procset, proc_stat_owner_ok,
+ PSTAT_OWNER_UID, FALSE);
+ if (num_tty_names > 0)
+ {
+ /* We set the PSTAT_TTY flag separately so that our filter function
+ can look at any procs that fail to set it. */
+ proc_stat_list_set_flags(procset, PSTAT_TTY);
+ proc_stat_list_filter1(procset, proc_stat_has_ctty, 0, FALSE);
+ }
+ if (filter_mask & FILTER_NOT_LEADER)
+ proc_stat_list_filter (procset, &ps_not_leader_filter, FALSE);
+ if (filter_mask & FILTER_UNORPHANED)
+ proc_stat_list_filter (procset, &ps_unorphaned_filter, FALSE);
+ if (!show_non_hurd_procs && (filter_mask & FILTER_PARENTED))
+ proc_stat_list_filter (procset, &ps_parent_filter, FALSE);
+
+ if (show_threads)
+ proc_stat_list_add_threads(procset);
+
+ proc_stat_list_filter (procset, &ps_alive_filter, FALSE);
+
+ psout (procset, fmt_string, posix_fmt, &ps_specs,
+ sort_key_name, sort_reverse,
+ output_width, print_heading,
+ squash_bogus_fields, squash_nominal_fields, top);
+
+ return 0;
+}
diff --git a/utils/psout.c b/utils/psout.c
new file mode 100644
index 00000000..93667b28
--- /dev/null
+++ b/utils/psout.c
@@ -0,0 +1,144 @@
+/* Common output function for ps & w
+
+ Copyright (C) 1995, 1996, 1998 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <error.h>
+#include <ps.h>
+
+void
+psout (struct proc_stat_list *procs,
+ char *fmt_string, int posix_fmt, struct ps_fmt_specs *specs,
+ char *sort_key_name, int sort_reverse,
+ int output_width, int print_heading,
+ int squash_bogus_fields, int squash_nominal_fields,
+ int top)
+{
+ error_t err;
+ struct ps_stream *output;
+ struct ps_fmt *fmt;
+
+ err = ps_fmt_create (fmt_string, posix_fmt, specs, &fmt);
+ if (err)
+ {
+ char *problem;
+ ps_fmt_creation_error (fmt_string, posix_fmt, specs, &problem);
+ error (4, 0, "%s", problem);
+ }
+
+ if (squash_bogus_fields)
+ /* Remove any fields that we can't print anyway (because of system
+ bugs/protection violations?). */
+ {
+ ps_flags_t bogus_flags = ps_fmt_needs (fmt);
+
+ err = proc_stat_list_find_bogus_flags (procs, &bogus_flags);
+ if (err)
+ error (0, err, "Couldn't remove bogus fields");
+ else
+ ps_fmt_squash_flags (fmt, bogus_flags);
+ }
+
+ if (squash_nominal_fields)
+ /* Remove any fields that contain only `uninteresting' information. */
+ {
+ int nominal (struct ps_fmt_field *field)
+ {
+ return !(field->flags & PS_FMT_FIELD_KEEP)
+ && proc_stat_list_spec_nominal (procs, field->spec);
+ }
+ ps_fmt_squash (fmt, nominal);
+ }
+
+ if (sort_key_name)
+ /* Sort on the given field. */
+ {
+ const struct ps_fmt_spec *sort_key;
+
+ if (*sort_key_name == '-')
+ /* Sort in reverse. */
+ {
+ sort_reverse = 1;
+ sort_key_name++;
+ }
+
+ sort_key = ps_fmt_specs_find (specs, sort_key_name);
+ if (sort_key == NULL)
+ error (3, 0, "%s: bad sort key", sort_key_name);
+
+ err = proc_stat_list_sort (procs, sort_key, sort_reverse);
+ if (err)
+ /* Give an error message, but don't exit. */
+ error (0, err, "Couldn't sort processes");
+ }
+
+ err = ps_stream_create (stdout, &output);
+ if (err)
+ error (5, err, "Can't make output stream");
+
+ if (print_heading)
+ {
+ if (procs->num_procs > 0)
+ {
+ err = ps_fmt_write_titles (fmt, output);
+ if (err)
+ error (0, err, "Can't print titles");
+ ps_stream_newline (output);
+ }
+ else
+ error (1, 0, "No applicable processes");
+ }
+
+ if (output_width)
+ /* Try and restrict the number of output columns. */
+ {
+ int deduce_term_size (int fd, char *type, int *width, int *height);
+ if (output_width < 0)
+ /* Have to figure it out! */
+ if (! deduce_term_size (1, getenv ("TERM"), &output_width, 0))
+ output_width = 80; /* common default */
+ ps_fmt_set_output_width (fmt, output_width);
+ }
+
+ if (top)
+ /* Restrict output to the top TOP entries, if TOP is positive, or the
+ bottom -TOP entries, if it is negative. */
+ {
+ int filter (struct proc_stat *ps)
+ {
+ return --top >= 0;
+ }
+ if (top < 0)
+ {
+ top += procs->num_procs;
+ proc_stat_list_filter1 (procs, filter, 0, 1);
+ }
+ else
+ proc_stat_list_filter1 (procs, filter, 0, 0);
+ }
+
+ /* Finally, output all the processes! */
+ err = proc_stat_list_fmt (procs, fmt, output);
+ if (err)
+ error (5, err, "Couldn't output process status");
+}
diff --git a/utils/psout.h b/utils/psout.h
new file mode 100644
index 00000000..f4469ac4
--- /dev/null
+++ b/utils/psout.h
@@ -0,0 +1,34 @@
+/* Common output function for ps & w
+
+ Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __PSOUT_H__
+#define __PSOUT_H__
+
+#include <ps.h>
+
+void psout (const struct proc_stat_list *procs,
+ const char *fmt_string, int posix_fmt,
+ const struct ps_fmt_specs *specs,
+ const char *sort_key_name, int sort_reverse,
+ int output_width, int print_heading,
+ int squash_bogus_fields, int squash_nominal_fields,
+ int top);
+
+#endif /* __PSOUT_H__ */
diff --git a/utils/remap.sh b/utils/remap.sh
new file mode 100644
index 00000000..f24ed0e7
--- /dev/null
+++ b/utils/remap.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+# Execute a command in an environment where some paths are remapped
+#
+# Copyright (C) 2002, 2013 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+USAGE="Usage: $0 [OPTION...] [FROM1 TO1 [FROM2 TO2 [...]] -- [COMMAND...]"
+DOC="Execute COMMAND in an environment where some paths are remapped."
+
+REMAPPED=""
+
+while [ "$#" -gt 0 ]; do
+ case "$1" in
+ --help|"-?")
+ echo "$USAGE"
+ echo "$DOC"
+ echo ""
+ echo " -?, --help Give this help list"
+ echo " --usage Give a short usage message"
+ echo " -V, --version Print program version"
+ exit 0;;
+ --usage)
+ echo "Usage: $0 [-V?] [--help] [--usage] [--version]"
+ exit 0;;
+ --version|-V)
+ echo "STANDARD_HURD_VERSION_remap_"; exit 0;;
+ --)
+ shift
+ break;;
+ -*)
+ echo 1>&2 "$0: unrecognized option \`$1'"
+ echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information";
+ exit 1;;
+ *)
+ MAPPED="$MAPPED $1"
+ shift;;
+ esac
+done
+
+if [ $# -eq 0 ]; then
+ set -- ${SHELL:-/bin/sh}
+fi
+
+# We exec settrans, which execs the "fakeauth" command in the chroot context.
+# The `pwd` is evaluated here and now, and that result interpreted inside
+# the shell running under fakeauth to chdir there inside the chroot world.
+# That shell then execs our arguments as a command line.
+exec /bin/settrans --chroot \
+ /bin/sh -c 'cd "$1" || exit ; shift ; exec "$@"' \
+ "$1" "$PWD" "$@" \
+ -- / /hurd/remap $MAPPED
diff --git a/utils/rmauth.c b/utils/rmauth.c
new file mode 100644
index 00000000..4c68cd18
--- /dev/null
+++ b/utils/rmauth.c
@@ -0,0 +1,121 @@
+/* Remove authentication from selected processes
+
+ Copyright (C) 1997, 1998 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <hurd.h>
+#include <argp.h>
+#include <error.h>
+#include <version.h>
+
+#include "frobauth.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (rmauth);
+
+static const struct argp_option options[] =
+{
+#ifndef UNSU
+ {"save", 's', 0, 0, "Save removed effective ids as available ids"},
+#endif
+ { 0 }
+};
+
+#ifdef UNSU
+static struct argp_child child_argps[] = {{ &frobauth_posix_argp }, { 0 }};
+#else
+static struct argp_child child_argps[] = {{ &frobauth_ea_argp }, { 0 }};
+#endif
+
+static char doc[] =
+ "Remove user/group ids from the authentication of selected processes";
+
+int
+main (int argc, char *argv[])
+{
+ int save = 0; /* save effective ids */
+ struct frobauth frobauth = FROBAUTH_INIT;
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 's': save = 1; break;
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = state->input; break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ /* Modify UGIDS, to be what PID's new authentication should be, UGIDS is
+ what the user specified. */
+ error_t modify (struct ugids *ugids, const struct ugids *remove,
+ pid_t pid, void *hook)
+ {
+ error_t err = 0;
+ struct ugids saved = UGIDS_INIT;
+
+ if (save)
+ ugids_set (&saved, ugids);
+
+ err = ugids_subtract (ugids, remove);
+
+ if (save)
+ {
+ ugids_subtract (&saved, ugids);
+ ugids_save (&saved);
+ ugids_merge (ugids, &saved);
+ }
+
+ return err;
+ }
+ void print_info (const struct ugids *new,
+ const struct ugids *old,
+ const struct ugids *removed,
+ pid_t pid, void *hook)
+ {
+ char *delta_rep;
+ struct ugids delta = UGIDS_INIT;
+
+ ugids_set (&delta, old);
+ ugids_subtract (&delta, new);
+
+ delta_rep = ugids_rep (&delta, 1, 1, 0, 0, 0);
+ printf ("%d: Removed %s\n", pid, delta_rep);
+
+ free (delta_rep);
+ ugids_fini (&delta);
+ }
+ struct argp argp = { options, parse_opt, 0, doc, child_argps };
+
+#ifdef UNSU
+ frobauth.default_user = 0;
+#endif
+ frobauth.require_ids = 1;
+
+ /* Parse our command line. This shouldn't ever return an error. */
+ argp_parse (&argp, argc, argv, 0, 0, &frobauth);
+
+ if (frobauth_modify (&frobauth, 0, 0, modify, print_info, 0))
+ return 0;
+ else
+ return 1;
+}
diff --git a/utils/rpctrace.c b/utils/rpctrace.c
new file mode 100644
index 00000000..fc913e30
--- /dev/null
+++ b/utils/rpctrace.c
@@ -0,0 +1,1968 @@
+/* Trace RPCs sent to selected ports
+
+ Copyright (C) 1998, 1999, 2001, 2002, 2003, 2005, 2006, 2009, 2011,
+ 2013 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/ihash.h>
+#include <mach/message.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <argp.h>
+#include <error.h>
+#include <string.h>
+#include <version.h>
+#include <sys/wait.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <argz.h>
+#include <envz.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (rpctrace);
+
+#define STD_MSGIDS_DIR DATADIR "/msgids/"
+
+static unsigned strsize = 80;
+
+#define OPT_NOSTDINC -1
+static const struct argp_option options[] =
+{
+ {"output", 'o', "FILE", 0, "Send trace output to FILE instead of stderr."},
+ {"nostdinc", OPT_NOSTDINC, 0, 0,
+ "Do not search inside the standard system directory, `" STD_MSGIDS_DIR
+ "', for `.msgids' files."},
+ {"rpc-list", 'i', "FILE", 0,
+ "Read FILE for assocations of message ID numbers to names."},
+ {0, 'I', "DIR", 0,
+ "Add the directory DIR to the list of directories to be searched for files "
+ "containing message ID numbers."},
+ {0, 's', "SIZE", 0, "Specify the maximum string size to print (the default is 80)."},
+ {0, 'E', "var[=value]", 0,
+ "Set/change (var=value) or remove (var) an environment variable among the "
+ "ones inherited by the executed process."},
+ {0}
+};
+
+#define UNKNOWN_NAME MACH_PORT_NULL
+
+static const char args_doc[] = "COMMAND [ARG...]";
+static const char doc[] = "Trace Mach Remote Procedure Calls.";
+
+/* The msgid_ihash table maps msgh_id values to names. */
+
+struct msgid_info
+{
+ char *name;
+ char *subsystem;
+};
+
+static void
+msgid_ihash_cleanup (void *element, void *arg)
+{
+ struct msgid_info *info = element;
+ free (info->name);
+ free (info->subsystem);
+ free (info);
+}
+
+/* This structure stores the information of the traced task. */
+struct task_info
+{
+ task_t task;
+ boolean_t threads_wrapped; /* All threads of the task has been wrapped? */
+};
+
+static struct hurd_ihash msgid_ihash
+ = HURD_IHASH_INITIALIZER (HURD_IHASH_NO_LOCP);
+
+static struct hurd_ihash task_ihash
+ = HURD_IHASH_INITIALIZER (HURD_IHASH_NO_LOCP);
+
+task_t unknown_task;
+
+void
+add_task (task_t task)
+{
+ error_t err;
+ struct task_info *info = malloc (sizeof *info);
+
+ if (info == NULL)
+ error (1, 0, "Fail to allocate memory.");
+
+ info->task = task;
+ info->threads_wrapped = FALSE;
+
+ err = hurd_ihash_add (&task_ihash, task, info);
+ if (err)
+ error (1, err, "hurd_ihash_add");
+}
+
+void
+remove_task (task_t task)
+{
+ hurd_ihash_remove (&task_ihash, task);
+}
+
+/* Parse a file of RPC names and message IDs as output by mig's -list
+ option: "subsystem base-id routine n request-id reply-id". Put each
+ request-id value into `msgid_ihash' with the routine name as its value. */
+static void
+parse_msgid_list (const char *filename)
+{
+ FILE *fp;
+ char *buffer = NULL;
+ size_t bufsize = 0;
+ unsigned int lineno = 0;
+ char *name, *subsystem;
+ unsigned int msgid;
+ error_t err;
+
+ fp = fopen (filename, "r");
+ if (fp == 0)
+ {
+ error (2, errno, "%s", filename);
+ return;
+ }
+
+ while (getline (&buffer, &bufsize, fp) > 0)
+ {
+ ++lineno;
+ if (buffer[0] == '#' || buffer[0] == '\0')
+ continue;
+ if (sscanf (buffer, "%ms %*u %ms %*u %u %*u\n",
+ &subsystem, &name, &msgid) != 3)
+ error (0, 0, "%s:%u: invalid format in RPC list file",
+ filename, lineno);
+ else
+ {
+ struct msgid_info *info = malloc (sizeof *info);
+ if (info == 0)
+ error (1, errno, "malloc");
+ info->name = name;
+ info->subsystem = subsystem;
+ err = hurd_ihash_add (&msgid_ihash, msgid, info);
+ if (err)
+ error (1, err, "hurd_ihash_add");
+ }
+ }
+
+ free (buffer);
+ fclose (fp);
+}
+
+/* Look for a name describing MSGID. We check the table directly, and
+ also check if this looks like the ID of a reply message whose request
+ ID is already in the table. */
+static const struct msgid_info *
+msgid_info (mach_msg_id_t msgid)
+{
+ const struct msgid_info *info = hurd_ihash_find (&msgid_ihash, msgid);
+ if (info == 0 && (msgid / 100) % 2 == 1)
+ {
+ /* This message ID is not in the table, and its number makes it
+ what should be an RPC reply message ID. So look up the message
+ ID of the corresponding RPC request and synthesize a name from
+ that. Then stash that name in the table so the next time the
+ lookup will match directly. */
+ info = hurd_ihash_find (&msgid_ihash, msgid - 100);
+ if (info != 0)
+ {
+ struct msgid_info *reply_info = malloc (sizeof *info);
+ if (reply_info != 0)
+ {
+ reply_info->subsystem = strdup (info->subsystem);
+ reply_info->name = 0;
+ asprintf (&reply_info->name, "%s-reply", info->name);
+ hurd_ihash_add (&msgid_ihash, msgid, reply_info);
+ info = reply_info;
+ }
+ else
+ info = 0;
+ }
+ }
+ return info;
+}
+
+static const char *
+msgid_name (mach_msg_id_t msgid)
+{
+ const struct msgid_info *info = msgid_info (msgid);
+ return info ? info->name : 0;
+}
+
+/* Return true if this message's data should be printed out.
+ For a request message, that means the in parameters.
+ For a reply messages, that means the return code and out parameters. */
+static int
+msgid_display (const struct msgid_info *info)
+{
+ return 1;
+}
+
+/* Return true if we should interpose on this RPC's reply port. If this
+ returns false, we will pass the caller's original reply port through so
+ we never see the reply message at all. */
+static int
+msgid_trace_replies (const struct msgid_info *info)
+{
+ return 1;
+}
+
+
+/* A common structure between sender_info and send_once_info */
+struct traced_info
+{
+ struct port_info pi;
+ mach_msg_type_name_t type;
+ char *name; /* null or a string describing this */
+};
+
+/* Each traced port has one receiver info and multiple send wrappers.
+ * The receiver info records the information of the receive right to
+ * the traced port, while send wrappers are created for each task
+ * who has the send right to the traced port.
+ */
+
+struct receiver_info
+{
+ char *name; /* null or a string describing this */
+ hurd_ihash_locp_t locp; /* position in the traced_names hash table */
+ mach_port_t portname; /* The port name in the owner task. */
+ task_t task; /* The task who has the right. */
+ mach_port_t forward; /* real port. */
+ struct receiver_info *receive_right; /* Link with other receive rights. */
+ struct sender_info *next; /* The head of the send right list */
+};
+
+struct sender_info
+{
+ struct traced_info pi;
+ task_t task; /* The task who has the right. */
+
+ /* It is used to form the list of send rights for different tasks.
+ * The head is the receive right. */
+ struct sender_info *next;
+
+ struct receiver_info *receive_right; /* The corresponding receive right */
+};
+
+struct send_once_info
+{
+ struct traced_info pi;
+ mach_port_t forward; /* real port. */
+
+ struct send_once_info *nextfree; /* Link when on free list. */
+};
+
+#define INFO_SEND_ONCE(info) ((info)->type == MACH_MSG_TYPE_MOVE_SEND_ONCE)
+#define TRACED_INFO(info) ((struct traced_info *) info)
+#define SEND_INFO(info) ((struct sender_info *) info)
+#define SEND_ONCE_INFO(info) ((struct send_once_info *) info)
+
+/* This structure stores the information of the RPC requests. */
+struct req_info
+{
+ boolean_t is_req;
+ mach_msg_id_t req_id;
+ mach_port_t reply_port;
+ task_t from;
+ task_t to;
+ struct req_info *next;
+};
+
+static struct req_info *req_head = NULL;
+
+static struct req_info *
+add_request (mach_msg_id_t req_id, mach_port_t reply_port,
+ task_t from, task_t to)
+{
+ struct req_info *req = malloc (sizeof (*req));
+ if (!req)
+ error (1, 0, "cannot allocate memory");
+ req->req_id = req_id;
+ req->from = from;
+ req->to = to;
+ req->reply_port = reply_port;
+ req->is_req = TRUE;
+
+ req->next = req_head;
+ req_head = req;
+
+ return req;
+}
+
+static struct req_info *
+remove_request (mach_msg_id_t req_id, mach_port_t reply_port)
+{
+ struct req_info **prev;
+ struct req_info *req;
+
+ prev = &req_head;
+ while (*prev)
+ {
+ if ((*prev)->req_id == req_id && (*prev)->reply_port == reply_port)
+ break;
+ prev = &(*prev)->next;
+ }
+ if (*prev == NULL)
+ return NULL;
+
+ req = *prev;
+ *prev = req->next;
+ return req;
+}
+
+struct port_info *notify_pi;
+/* The list of receiver infos, but only the ones for the traced tasks. */
+struct receiver_info *receive_right_list;
+static struct traced_info dummy_wrapper;
+static struct send_once_info *freelist;
+
+struct hurd_ihash traced_names
+ = HURD_IHASH_INITIALIZER (offsetof (struct receiver_info, locp));
+struct port_class *traced_class;
+struct port_class *other_class;
+struct port_bucket *traced_bucket;
+FILE *ostream;
+
+/* These are the calls made from the tracing engine into
+ the output formatting code. */
+
+/* Called for a message that does not look like an RPC reply.
+ The header has already been swapped into the sender's view
+ with interposed ports. */
+static void print_request_header (struct sender_info *info,
+ mach_msg_header_t *header);
+
+/* Called for a message that looks like an RPC reply. */
+static void print_reply_header (struct send_once_info *info,
+ mig_reply_header_t *header,
+ struct req_info *req);
+
+/* Called for each data item (which might be an array).
+ Always called after one of the above two. */
+static void print_data (mach_msg_type_name_t type,
+ const void *data,
+ mach_msg_type_number_t nelt,
+ mach_msg_type_number_t eltsize);
+
+
+/*** Mechanics of tracing messages and interposing on ports ***/
+
+/* Create a new info for the receive right.
+ * It lives until the traced receive right dies. */
+static struct receiver_info *
+new_receiver_info (mach_port_t right, mach_port_t owner)
+{
+ error_t err;
+ struct receiver_info *info;
+ mach_port_t foo;
+
+ info = malloc (sizeof (*info));
+ if (!info)
+ error (1, 0, "cannot allocate memory");
+ info->forward = right;
+ info->task = owner;
+ info->portname = UNKNOWN_NAME;
+ info->receive_right = NULL;
+ info->next = NULL;
+ if (owner != unknown_task)
+ {
+ info->receive_right = receive_right_list;
+ receive_right_list = info;
+ }
+ info->name = 0;
+
+ /* Request the dead-name notification, so if the receive right is destroyed,
+ * we can destroy the wrapper. */
+ err = mach_port_request_notification (mach_task_self (), right,
+ MACH_NOTIFY_DEAD_NAME, 1,
+ notify_pi->port_right,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE, &foo);
+ if (err)
+ error (2, err, "mach_port_request_notification");
+ mach_port_deallocate (mach_task_self (), foo);
+
+ err = hurd_ihash_add (&traced_names, info->forward, info);
+ if (err)
+ error (2, err, "hurd_ihash_add");
+ return info;
+}
+
+static void
+destroy_receiver_info (struct receiver_info *info)
+{
+ struct sender_info *send_wrapper;
+ struct receiver_info **prev;
+
+ mach_port_deallocate (mach_task_self (), info->forward);
+ /* Remove it from the receive right list. */
+ prev = &receive_right_list;
+ while (*prev != info && *prev)
+ prev = &((*prev)->receive_right);
+ /* If we find the receiver info in the list. */
+ if (*prev)
+ *prev = info->receive_right;
+
+ send_wrapper = info->next;
+ while (send_wrapper)
+ {
+ struct sender_info *next = send_wrapper->next;
+ assert (TRACED_INFO (send_wrapper)->pi.refcnt == 1);
+ /* Reset the receive_right of the send wrapper in advance to avoid
+ * destroy_receiver_info is called when the port info is destroyed. */
+ send_wrapper->receive_right = NULL;
+ ports_destroy_right (send_wrapper);
+ send_wrapper = next;
+ }
+
+ hurd_ihash_locp_remove (&traced_names, info->locp);
+ free (info);
+}
+
+/* Create a new wrapper port and do `ports_get_right' on it.
+ *
+ * The wrapper lives until there is no send right to it,
+ * or the corresponding receiver info is destroyed.
+ */
+static struct sender_info *
+new_send_wrapper (struct receiver_info *receive, task_t task,
+ mach_port_t *wrapper_right)
+{
+ error_t err;
+ struct sender_info *info;
+
+ /* Create a new wrapper port that forwards to *RIGHT. */
+ err = ports_create_port (traced_class, traced_bucket,
+ sizeof *info, &info);
+ assert_perror (err);
+
+ TRACED_INFO (info)->name = 0;
+ asprintf (&TRACED_INFO (info)->name, " %d<--%d(pid%d)",
+ receive->forward, TRACED_INFO (info)->pi.port_right, task2pid (task));
+ TRACED_INFO (info)->type = MACH_MSG_TYPE_MOVE_SEND;
+ info->task = task;
+ info->receive_right = receive;
+ info->next = receive->next;
+ receive->next = info;
+
+ *wrapper_right = ports_get_right (info);
+ ports_port_deref (info);
+
+ return info;
+}
+
+/* Create a new wrapper port and do `ports_get_right' on it. */
+static struct send_once_info *
+new_send_once_wrapper (mach_port_t right, mach_port_t *wrapper_right)
+{
+ error_t err;
+ struct send_once_info *info;
+
+ /* Use a free send-once wrapper port if we have one. */
+ if (freelist)
+ {
+ info = freelist;
+ freelist = info->nextfree;
+ }
+ else
+ {
+ /* Create a new wrapper port that forwards to *RIGHT. */
+ err = ports_create_port (traced_class, traced_bucket,
+ sizeof *info, &info);
+ assert_perror (err);
+ TRACED_INFO (info)->name = 0;
+ }
+
+ info->forward = right;
+ TRACED_INFO (info)->type = MACH_MSG_TYPE_MOVE_SEND_ONCE;
+ info->nextfree = NULL;
+
+ /* Send-once rights never compare equal to any other right (even
+ another send-once right), so there is no point in putting them
+ in the reverse-lookup table.
+
+ Since we never make send rights to this port, we don't want to
+ use the normal libports mechanisms (ports_get_right) that are
+ designed for send rights and no-senders notifications.
+ Instead, we hold on to the initial hard ref to INFO until we
+ receive a message on it. The kernel automatically sends a
+ MACH_NOTIFY_SEND_ONCE message if the send-once right dies. */
+
+ *wrapper_right = TRACED_INFO (info)->pi.port_right;
+
+ return info;
+}
+
+/* Unlink the send wrapper from the list. */
+static void
+unlink_sender_info (void *pi)
+{
+ struct sender_info *info = pi;
+ struct sender_info **prev;
+
+ if (info->receive_right)
+ {
+ /* Remove it from the send right list. */
+ prev = &info->receive_right->next;
+ while (*prev != info && *prev)
+ prev = &((*prev)->next);
+ assert (*prev);
+ *prev = info->next;
+
+ info->next = NULL;
+ }
+}
+
+/* The function is called when the port_info is going to be destroyed.
+ * If it's the last send wrapper for the traced port, the receiver info
+ * will also be destroyed. */
+static void
+traced_clean (void *pi)
+{
+ struct sender_info *info = pi;
+
+ assert (TRACED_INFO (info)->type == MACH_MSG_TYPE_MOVE_SEND);
+ free (TRACED_INFO (info)->name);
+
+ if (info->receive_right)
+ {
+ unlink_sender_info (pi);
+
+ /* If this is the last send wrapper, it means that our traced port won't
+ * have any more send rights. We notify the owner of the receive right
+ * of that by deallocating the forward port. */
+ if (info->receive_right->next == NULL)
+ destroy_receiver_info (info->receive_right);
+
+ info->receive_right = NULL;
+ }
+}
+
+/* Check if the receive right has been seen. */
+boolean_t
+seen_receive_right (task_t task, mach_port_t name)
+{
+ struct receiver_info *info = receive_right_list;
+ while (info)
+ {
+ if (info->task == task && info->portname == name)
+ return TRUE;
+ info = info->receive_right;
+ }
+ return FALSE;
+}
+
+/* This function is to find the receive right for the send right 'send'
+ * among traced tasks. I assume that all receive rights are moved
+ * under the control of rpctrace.
+ *
+ * Note: 'send' shouldn't be the send right to the wrapper.
+ *
+ * Note: the receiver_info returned from the function
+ * might not be the receive right in the traced tasks.
+ * */
+struct receiver_info *
+discover_receive_right (mach_port_t send, task_t task)
+{
+ error_t err;
+ struct receiver_info *info = NULL;
+
+ info = hurd_ihash_find (&traced_names, send);
+ /* If we have seen the send right or send once right. */
+ if (info
+ /* If the receive right is in one of traced tasks,
+ * but we don't know its name
+ * (probably because the receive right has been moved),
+ * we need to find it out. */
+ && !(info->task != unknown_task
+ && info->portname == UNKNOWN_NAME))
+ return info;
+
+ {
+ int j;
+ mach_port_t *portnames = NULL;
+ mach_msg_type_number_t nportnames = 0;
+ mach_port_type_t *porttypes = NULL;
+ mach_msg_type_number_t nporttypes = 0;
+ struct receiver_info *receiver_info = NULL;
+
+ err = mach_port_names (task, &portnames, &nportnames,
+ &porttypes, &nporttypes);
+ if (err == MACH_SEND_INVALID_DEST)
+ {
+ remove_task (task);
+ return 0;
+ }
+ if (err)
+ error (2, err, "mach_port_names");
+
+ for (j = 0; j < nportnames; j++)
+ {
+ mach_port_status_t port_status;
+ mach_port_t send_right;
+ mach_msg_type_name_t type;
+
+ if (!(porttypes[j] & MACH_PORT_TYPE_RECEIVE) /* not a receive right */
+ || seen_receive_right (task, portnames[j]))
+ continue;
+
+ err = mach_port_get_receive_status (task, portnames[j],
+ &port_status);
+ if (err)
+ error (2, err, "mach_port_get_receive_status");
+ /* If the port doesn't have the send right, skip it. */
+ if (!port_status.mps_srights)
+ continue;
+
+ err = mach_port_extract_right (task, portnames[j],
+ MACH_MSG_TYPE_MAKE_SEND,
+ &send_right, &type);
+ if (err)
+ error (2, err, "mach_port_extract_right");
+
+ if (/* We have seen this send right before. */
+ hurd_ihash_find (&traced_names, send_right)
+ || send_right != send /* It's not the port we want. */)
+ {
+ mach_port_deallocate (mach_task_self (), send_right);
+ continue;
+ }
+
+ /* We have found the receive right we want. */
+ receiver_info = new_receiver_info (send_right, task);
+ receiver_info->portname = portnames[j];
+ break;
+ }
+ if (portnames)
+ vm_deallocate (mach_task_self (), (vm_address_t) portnames,
+ nportnames * sizeof (*portnames));
+ if (porttypes)
+ vm_deallocate (mach_task_self (), (vm_address_t) porttypes,
+ nporttypes * sizeof (*porttypes));
+
+ if (receiver_info)
+ return receiver_info;
+ }
+ return NULL;
+}
+
+/* get_send_wrapper searches for the send wrapper for the target task.
+ If it doesn't exist, create a new one. */
+struct sender_info *
+get_send_wrapper (struct receiver_info *receiver_info,
+ mach_port_t task, mach_port_t *right)
+{
+ struct sender_info *info = receiver_info->next;
+
+ while (info)
+ {
+ if (info->task == task)
+ {
+ *right = ports_get_right (info);
+ return info;
+ }
+ info = info->next;
+ }
+ /* No send wrapper is found. */
+ return new_send_wrapper (receiver_info, task, right);
+}
+
+/* Rewrite a port right in a message with an appropriate wrapper port. */
+static char *
+rewrite_right (mach_port_t *right, mach_msg_type_name_t *type,
+ struct req_info *req)
+{
+ error_t err;
+ struct receiver_info *receiver_info;
+ struct sender_info *send_wrapper;
+ task_t dest = unknown_task;
+ task_t source = unknown_task;
+
+ /* We can never do anything special with a null or dead port right. */
+ if (!MACH_PORT_VALID (*right))
+ return 0;
+
+ if (req)
+ {
+ if (req->is_req) /* It's a RPC request. */
+ {
+ source = req->from;
+ dest = req->to;
+ }
+ else
+ {
+ source = req->to;
+ dest = req->from;
+ }
+ }
+
+ switch (*type)
+ {
+ case MACH_MSG_TYPE_PORT_SEND:
+ /* The strategy for moving the send right is: if the destination task
+ * has the receive right, we move the send right of the traced port to
+ * the destination; otherwise, we move the one of the send wrapper.
+ */
+ /* See if this is already one of our own wrapper ports. */
+ send_wrapper = ports_lookup_port (traced_bucket, *right, 0);
+ if (send_wrapper)
+ {
+ /* This is a send right to one of our own wrapper ports. */
+ mach_port_deallocate (mach_task_self (), *right); /* eat msg ref */
+
+ /* If the send right is moved to the task with the receive right,
+ * copy the send right in 'forward' of receiver info to the destination.
+ * Otherwise, copy the send right to the send wrapper. */
+ assert (send_wrapper->receive_right);
+ if (dest == send_wrapper->receive_right->task)
+ {
+ *right = send_wrapper->receive_right->forward;
+ err = mach_port_mod_refs (mach_task_self (), *right,
+ MACH_PORT_RIGHT_SEND, +1);
+ if (err)
+ error (2, err, "mach_port_mod_refs");
+ ports_port_deref (send_wrapper);
+ }
+ else
+ {
+ struct sender_info *send_wrapper2
+ = get_send_wrapper (send_wrapper->receive_right, dest, right);
+ ports_port_deref (send_wrapper);
+ *type = MACH_MSG_TYPE_MAKE_SEND;
+ send_wrapper = send_wrapper2;
+ }
+ return TRACED_INFO (send_wrapper)->name;
+ }
+
+ if (req && req->req_id == 3216) /* mach_port_extract_right */
+ receiver_info = discover_receive_right (*right, dest);
+ else
+ receiver_info = discover_receive_right (*right, source);
+ if (receiver_info == NULL)
+ {
+ /* It's unusual to see an unknown send right from a traced task.
+ * We ignore it. */
+ if (source != unknown_task)
+ {
+ /* TODO: this happens on fork() when the new process does not
+ have the send right yet (it is about to get inserted). */
+ error (0, 0, "get an unknown send right from process %d",
+ task2pid (source));
+ return dummy_wrapper.name;
+ }
+ /* The receive right is owned by an unknown task. */
+ receiver_info = new_receiver_info (*right, unknown_task);
+ mach_port_mod_refs (mach_task_self (), *right,
+ MACH_PORT_RIGHT_SEND, 1);
+ }
+ /* If the send right is moved to the task with the receive right,
+ * don't do anything.
+ * Otherwise, we translate it into the one to the send wrapper. */
+ if (dest == receiver_info->task)
+ return receiver_info->name;
+ else
+ {
+ assert (*right == receiver_info->forward);
+ mach_port_deallocate (mach_task_self (), *right);
+ send_wrapper = get_send_wrapper (receiver_info, dest, right);
+ *type = MACH_MSG_TYPE_MAKE_SEND;
+ return TRACED_INFO (send_wrapper)->name;
+ }
+
+ case MACH_MSG_TYPE_PORT_SEND_ONCE:
+ /* There is no way to know if this send-once right is to the same
+ receive right as any other send-once or send right we have seen.
+ Fortunately, it doesn't matter, since the recipient of the
+ send-once right we pass along can't tell either. We always just
+ make a new send-once wrapper object, that will trace the one
+ message it receives, and then die. */
+ *type = MACH_MSG_TYPE_MAKE_SEND_ONCE;
+ return TRACED_INFO (new_send_once_wrapper (*right, right))->name;
+
+ case MACH_MSG_TYPE_PORT_RECEIVE:
+ /* We have got a receive right, call it A and the send wrapper for
+ * the destination task is denoted as B (if the destination task
+ * doesn't have the send wrapper, we create it before moving receive
+ * right).
+ * We wrap the receive right A in the send wrapper and move the receive
+ * right B to the destination task. */
+ {
+ assert (req);
+ receiver_info = hurd_ihash_find (&traced_names, *right);
+ if (receiver_info)
+ {
+ struct sender_info *send_wrapper2;
+ char *name;
+ mach_port_t rr;
+
+ /* The port A has at least one send right - the one in
+ * receiver_info->forward. If the source task doesn't have
+ * the send right, the port A will be destroyed after we
+ * deallocate the only send right. */
+
+ /* We have to deallocate the send right in
+ * receiver_info->forward before we import the port to port_info.
+ * So the reference count in the imported port info will be 1,
+ * if it doesn't have any other send rights. */
+ mach_port_deallocate (mach_task_self (), receiver_info->forward);
+ err = ports_import_port (traced_class, traced_bucket,
+ *right, sizeof *send_wrapper,
+ &send_wrapper);
+ if (err)
+ error (2, err, "ports_import_port");
+
+ TRACED_INFO (send_wrapper)->type = MACH_MSG_TYPE_MOVE_SEND;
+ send_wrapper->task = source;
+ TRACED_INFO (send_wrapper)->name = receiver_info->name;
+ /* Initialize them in case that the source task doesn't
+ * have the send right to the port, and the port will
+ * be destroyed immediately. */
+ send_wrapper->receive_right = NULL;
+ send_wrapper->next = NULL;
+ ports_port_deref (send_wrapper);
+
+ hurd_ihash_locp_remove (&traced_names, receiver_info->locp);
+
+ send_wrapper2 = get_send_wrapper (receiver_info, dest, &rr);
+ assert (TRACED_INFO (send_wrapper2)->pi.refcnt == 1);
+ name = TRACED_INFO (send_wrapper2)->name;
+ TRACED_INFO (send_wrapper2)->name = NULL;
+ /* send_wrapper2 isn't destroyed normally, so we need to unlink
+ * it from the send wrapper list before calling ports_claim_right */
+ unlink_sender_info (send_wrapper2);
+ send_wrapper2->receive_right = NULL;
+ rr = ports_claim_right (send_wrapper2);
+ /* Get us a send right that we will forward on. */
+ err = mach_port_insert_right (mach_task_self (), rr, rr,
+ MACH_MSG_TYPE_MAKE_SEND);
+ if (err)
+ error (2, err, "mach_port_insert_right");
+ receiver_info->forward = rr;
+ receiver_info->task = dest;
+ if (dest != unknown_task)
+ {
+ receiver_info->receive_right = receive_right_list;
+ receive_right_list = receiver_info;
+ }
+ /* The port name will be discovered
+ * when we search for this receive right. */
+ receiver_info->portname = UNKNOWN_NAME;
+ receiver_info->name = name;
+
+ send_wrapper->receive_right = receiver_info;
+ send_wrapper->next = receiver_info->next;
+ receiver_info->next = send_wrapper;
+
+ err = hurd_ihash_add (&traced_names, receiver_info->forward,
+ receiver_info);
+ if (err)
+ error (2, err, "hurd_ihash_add");
+ *right = rr;
+ }
+ else
+ {
+ /* Weird? no send right for the port. */
+ err = mach_port_insert_right (mach_task_self (), *right, *right,
+ MACH_MSG_TYPE_MAKE_SEND);
+ if (err)
+ error (2, err, "mach_port_insert_right");
+ receiver_info = new_receiver_info (*right, dest);
+ }
+
+ return receiver_info->name;
+ }
+
+ default:
+ assert (!"??? bogus port type from kernel!");
+ }
+ return 0;
+}
+
+static void
+print_contents (mach_msg_header_t *inp,
+ void *msg_buf_ptr, struct req_info *req)
+{
+ error_t err;
+
+ int first = 1;
+
+ /* Process the message data, wrapping ports and printing data. */
+ while (msg_buf_ptr < (void *) inp + inp->msgh_size)
+ {
+ mach_msg_type_t *const type = msg_buf_ptr;
+ mach_msg_type_long_t *const lt = (void *) type;
+ void *data;
+ mach_msg_type_number_t nelt; /* Number of data items. */
+ mach_msg_type_size_t eltsize; /* Bytes per item. */
+ mach_msg_type_name_t name; /* MACH_MSG_TYPE_* code */
+
+ if (!type->msgt_longform)
+ {
+ name = type->msgt_name;
+ nelt = type->msgt_number;
+ eltsize = type->msgt_size / 8;
+ data = msg_buf_ptr = type + 1;
+ }
+ else
+ {
+ name = lt->msgtl_name;
+ nelt = lt->msgtl_number;
+ eltsize = lt->msgtl_size / 8;
+ data = msg_buf_ptr = lt + 1;
+ }
+
+ if (!type->msgt_inline)
+ {
+ /* This datum is out-of-line, meaning the message actually
+ contains a pointer to a vm_allocate'd region of data. */
+ data = *(void **) data;
+ msg_buf_ptr += sizeof (void *);
+ }
+ else
+ msg_buf_ptr += ((nelt * eltsize + sizeof(natural_t) - 1)
+ & ~(sizeof(natural_t) - 1));
+
+ if (first)
+ first = 0;
+ else
+ putc (' ', ostream);
+
+ /* Note that MACH_MSG_TYPE_PORT_NAME does not indicate a port right.
+ It indicates a port name, i.e. just an integer--and we don't know
+ what task that port name is meaningful in. If it's meaningful in
+ a traced task, then it refers to our intercepting port rather than
+ the original port anyway. */
+ if (MACH_MSG_TYPE_PORT_ANY_RIGHT (name))
+ {
+ /* These are port rights. Translate them into wrappers. */
+ mach_port_t *const portnames = data;
+ mach_msg_type_number_t i;
+ mach_msg_type_name_t newtypes[nelt];
+ int poly;
+
+ assert (inp->msgh_bits & MACH_MSGH_BITS_COMPLEX);
+ assert (eltsize == sizeof (mach_port_t));
+
+ poly = 0;
+ for (i = 0; i < nelt; ++i)
+ {
+ char *str;
+
+ newtypes[i] = name;
+
+ str = rewrite_right (&portnames[i], &newtypes[i], req);
+
+ putc ((i == 0 && nelt > 1) ? '{' : ' ', ostream);
+
+ if (portnames[i] == MACH_PORT_NULL)
+ fprintf (ostream, "(null)");
+ else if (portnames[i] == MACH_PORT_DEAD)
+ fprintf (ostream, "(dead)");
+ else
+ {
+ if (str != 0)
+ fprintf (ostream, "%s", str);
+ else
+ fprintf (ostream, "%3u", (unsigned int) portnames[i]);
+ }
+ if (i > 0 && newtypes[i] != newtypes[0])
+ poly = 1;
+ }
+ if (nelt > 1)
+ putc ('}', ostream);
+
+ if (poly)
+ {
+ if (name == MACH_MSG_TYPE_MOVE_SEND_ONCE)
+ {
+ /* Some of the new rights are MAKE_SEND_ONCE.
+ Turn them all into MOVE_SEND_ONCE. */
+ for (i = 0; i < nelt; ++i)
+ if (newtypes[i] == MACH_MSG_TYPE_MAKE_SEND_ONCE)
+ {
+ err = mach_port_insert_right (mach_task_self (),
+ portnames[i],
+ portnames[i],
+ newtypes[i]);
+ assert_perror (err);
+ }
+ else
+ assert (newtypes[i] == MACH_MSG_TYPE_MOVE_SEND_ONCE);
+ }
+ else
+ {
+ for (i = 0; i < nelt; ++i)
+ switch (newtypes[i])
+ {
+ case MACH_MSG_TYPE_COPY_SEND:
+ err = mach_port_mod_refs (mach_task_self (),
+ portnames[i],
+ MACH_PORT_RIGHT_SEND, +1);
+ assert_perror (err);
+ break;
+ case MACH_MSG_TYPE_MAKE_SEND:
+ err = mach_port_insert_right (mach_task_self (),
+ portnames[i],
+ portnames[i],
+ newtypes[i]);
+ assert_perror (err);
+ break;
+ default:
+ assert (newtypes[i] == MACH_MSG_TYPE_MOVE_SEND);
+ break;
+ }
+
+ name = MACH_MSG_TYPE_MOVE_SEND;
+ }
+ if (type->msgt_longform)
+ lt->msgtl_name = name;
+ else
+ type->msgt_name = name;
+ }
+ else if (nelt > 0 && newtypes[0] != name)
+ {
+ if (type->msgt_longform)
+ lt->msgtl_name = newtypes[0];
+ else
+ type->msgt_name = newtypes[0];
+ }
+ }
+ else
+ print_data (name, data, nelt, eltsize);
+ }
+}
+
+/* Wrap all thread ports in the task */
+static void
+wrap_all_threads (task_t task)
+{
+ struct sender_info *thread_send_wrapper;
+ struct receiver_info *thread_receiver_info;
+ thread_t *threads;
+ size_t nthreads;
+ error_t err;
+
+ err = task_threads (task, &threads, &nthreads);
+ if (err)
+ error (2, err, "task_threads");
+
+ for (int i = 0; i < nthreads; ++i)
+ {
+ thread_receiver_info = hurd_ihash_find (&traced_names, threads[i]);
+ /* We haven't seen the port. */
+ if (thread_receiver_info == NULL)
+ {
+ mach_port_t new_thread_port;
+
+ thread_receiver_info = new_receiver_info (threads[i], unknown_task);
+ thread_send_wrapper = new_send_wrapper (thread_receiver_info,
+ task, &new_thread_port);
+ free (TRACED_INFO (thread_send_wrapper)->name);
+ asprintf (&TRACED_INFO (thread_send_wrapper)->name,
+ "thread%d(pid%d)", threads[i], task2pid (task));
+
+ err = mach_port_insert_right (mach_task_self (),
+ new_thread_port, new_thread_port,
+ MACH_MSG_TYPE_MAKE_SEND);
+ if (err)
+ error (2, err, "mach_port_insert_right");
+
+ err = thread_set_kernel_port (threads[i], new_thread_port);
+ if (err)
+ error (2, err, "thread_set_kernel_port");
+
+ mach_port_deallocate (mach_task_self (), new_thread_port);
+ }
+ }
+ vm_deallocate (mach_task_self (), threads, nthreads * sizeof (thread_t));
+}
+
+/* Wrap the new thread port that is in the message. */
+static void
+wrap_new_thread (mach_msg_header_t *inp, struct req_info *req)
+{
+ error_t err;
+ mach_port_t thread_port;
+ struct
+ {
+ mach_msg_header_t head;
+ mach_msg_type_t retcode_type;
+ kern_return_t retcode;
+ mach_msg_type_t child_thread_type;
+ mach_port_t child_thread;
+ } *reply = (void *) inp;
+ /* This function is called after rewrite_right,
+ * so the wrapper for the thread port has been created. */
+ struct sender_info *send_wrapper = ports_lookup_port (traced_bucket,
+ reply->child_thread, 0);
+
+ assert (send_wrapper);
+ assert (send_wrapper->receive_right);
+ thread_port = send_wrapper->receive_right->forward;
+
+ err = mach_port_insert_right (mach_task_self (), reply->child_thread,
+ reply->child_thread, MACH_MSG_TYPE_MAKE_SEND);
+ if (err)
+ error (2, err, "mach_port_insert_right");
+ err = thread_set_kernel_port (thread_port, reply->child_thread);
+ if (err)
+ error (2, err, "thread_set_kernel_port");
+ mach_port_deallocate (mach_task_self (), reply->child_thread);
+
+ free (TRACED_INFO (send_wrapper)->name);
+ asprintf (&TRACED_INFO (send_wrapper)->name, "thread%d(pid%d)",
+ thread_port, task2pid (req->from));
+ ports_port_deref (send_wrapper);
+}
+
+/* Wrap the new task port that is in the message. */
+static void
+wrap_new_task (mach_msg_header_t *inp, struct req_info *req)
+{
+ error_t err;
+ pid_t pid;
+ task_t pseudo_task_port;
+ task_t task_port;
+ struct
+ {
+ mach_msg_header_t head;
+ mach_msg_type_t retcode_type;
+ kern_return_t retcode;
+ mach_msg_type_t child_task_type;
+ mach_port_t child_task;
+ } *reply = (void *) inp;
+ /* The send wrapper of the new task for the father task */
+ struct sender_info *task_wrapper1 = ports_lookup_port (traced_bucket,
+ reply->child_task, 0);
+ /* The send wrapper for the new task itself. */
+ struct sender_info *task_wrapper2;
+
+ assert (task_wrapper1);
+ assert (task_wrapper1->receive_right);
+
+ task_port = task_wrapper1->receive_right->forward;
+ add_task (task_port);
+
+ task_wrapper2 = new_send_wrapper (task_wrapper1->receive_right,
+ task_port, &pseudo_task_port);
+ err = mach_port_insert_right (mach_task_self (),
+ pseudo_task_port, pseudo_task_port,
+ MACH_MSG_TYPE_MAKE_SEND);
+ if (err)
+ error (2, err, "mach_port_insert_right");
+ err = task_set_kernel_port (task_port, pseudo_task_port);
+ if (err)
+ error (2, err, "task_set_kernel_port");
+ mach_port_deallocate (mach_task_self (), pseudo_task_port);
+
+ pid = task2pid (task_port);
+ free (TRACED_INFO (task_wrapper1)->name);
+ asprintf (&TRACED_INFO (task_wrapper1)->name, "task%d(pid%d)",
+ task_port, task2pid (req->from));
+ free (TRACED_INFO (task_wrapper2)->name);
+ asprintf (&TRACED_INFO (task_wrapper2)->name, "task%d(pid%d)",
+ task_port, pid);
+ ports_port_deref (task_wrapper1);
+}
+
+int
+trace_and_forward (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ mach_port_t reply_port;
+
+ const mach_msg_type_t RetCodeType =
+ {
+ MACH_MSG_TYPE_INTEGER_32, /* msgt_name = */
+ 32, /* msgt_size = */
+ 1, /* msgt_number = */
+ TRUE, /* msgt_inline = */
+ FALSE, /* msgt_longform = */
+ FALSE, /* msgt_deallocate = */
+ 0 /* msgt_unused = */
+ };
+
+ error_t err;
+ const struct msgid_info *msgid;
+ struct traced_info *info;
+ mach_msg_bits_t complex;
+
+ /* Look up our record for the receiving port. There is no need to check
+ the class, because our port bucket only ever contains one class of
+ ports (traced_class). */
+ info = ports_lookup_port (traced_bucket, inp->msgh_local_port, 0);
+ assert (info);
+
+ /* A notification message from the kernel appears to have been sent
+ with a send-once right, even if there have never really been any. */
+ if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE)
+ {
+ if (inp->msgh_id == MACH_NOTIFY_DEAD_NAME && info == (void *) notify_pi)
+ {
+ struct receiver_info *receiver_info;
+ const mach_dead_name_notification_t *const n = (void *) inp;
+
+ /* Deallocate extra ref allocated by the notification. */
+ mach_port_deallocate (mach_task_self (), n->not_port);
+ receiver_info = hurd_ihash_find (&traced_names, n->not_port);
+ /* The receiver info might have been destroyed.
+ * If not, we destroy it here. */
+ if (receiver_info)
+ {
+ assert (n->not_port == receiver_info->forward);
+ destroy_receiver_info (receiver_info);
+ }
+
+ ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
+ ports_port_deref (info);
+
+ /* It might be a task port. Remove the dead task from the list. */
+ remove_task (n->not_port);
+
+ return 1;
+ }
+ else if (inp->msgh_id == MACH_NOTIFY_NO_SENDERS
+ && !INFO_SEND_ONCE (info))
+ {
+ /* No more senders for a send right we are tracing. Now INFO
+ will die, and we will release the tracee send right so it too
+ can see a no-senders notification. */
+ mach_no_senders_notification_t *n = (void *) inp;
+ ports_no_senders (info, n->not_count);
+ ports_port_deref (info);
+ ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
+ return 1;
+ }
+ /* Get some unexpected notification for rpctrace itself,
+ * TODO ignore them for now. */
+ else if (info == (void *) notify_pi)
+ {
+ ports_port_deref (info);
+ ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
+ return 1;
+ }
+ }
+
+ assert (info != (void *) notify_pi);
+ assert (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) == info->type);
+
+ complex = inp->msgh_bits & MACH_MSGH_BITS_COMPLEX;
+
+ msgid = msgid_info (inp->msgh_id);
+
+ /* Swap the header data like a crossover cable. */
+ {
+ mach_msg_type_name_t this_type = MACH_MSGH_BITS_LOCAL (inp->msgh_bits);
+ mach_msg_type_name_t reply_type = MACH_MSGH_BITS_REMOTE (inp->msgh_bits);
+
+ /* Save the original reply port in the RPC request. */
+ reply_port = inp->msgh_remote_port;
+
+ inp->msgh_local_port = inp->msgh_remote_port;
+ if (reply_type && msgid_trace_replies (msgid)
+ /* The reply port might be dead, e.g., the traced task has died. */
+ && MACH_PORT_VALID (inp->msgh_local_port))
+ {
+ switch (reply_type)
+ {
+ case MACH_MSG_TYPE_PORT_SEND:
+ rewrite_right (&inp->msgh_local_port, &reply_type, NULL);
+ break;
+
+ case MACH_MSG_TYPE_PORT_SEND_ONCE:;
+ struct send_once_info *info;
+ info = new_send_once_wrapper (inp->msgh_local_port,
+ &inp->msgh_local_port);
+ reply_type = MACH_MSG_TYPE_MAKE_SEND_ONCE;
+ assert (inp->msgh_local_port);
+
+ if (TRACED_INFO (info)->name == 0)
+ {
+ if (msgid == 0)
+ asprintf (&TRACED_INFO (info)->name, "reply(%u:%u)",
+ (unsigned int) TRACED_INFO (info)->pi.port_right,
+ (unsigned int) inp->msgh_id);
+ else
+ asprintf (&TRACED_INFO (info)->name, "reply(%u:%s)",
+ (unsigned int) TRACED_INFO (info)->pi.port_right,
+ msgid->name);
+ }
+ break;
+
+ default:
+ error (1, 0, "Reply type %i not handled", reply_type);
+ }
+ }
+
+ if (info->type == MACH_MSG_TYPE_MOVE_SEND_ONCE)
+ inp->msgh_remote_port = SEND_ONCE_INFO (info)->forward;
+ else
+ {
+ assert (SEND_INFO (info)->receive_right);
+ inp->msgh_remote_port = SEND_INFO (info)->receive_right->forward;
+ }
+ if (this_type == MACH_MSG_TYPE_MOVE_SEND_ONCE)
+ {
+ /* We have a message to forward for a send-once wrapper object.
+ Since each wrapper object only lives for a single message, this
+ one can be reclaimed now. We continue to hold a hard ref to the
+ ports object, but we know that nothing else refers to it now, and
+ we are consuming its `forward' right in the message we send. */
+ free (info->name);
+ info->name = 0;
+ SEND_ONCE_INFO (info)->forward = 0;
+ SEND_ONCE_INFO (info)->nextfree = freelist;
+ freelist = SEND_ONCE_INFO (info);
+ }
+ else
+ this_type = MACH_MSG_TYPE_COPY_SEND;
+
+ inp->msgh_bits = complex | MACH_MSGH_BITS (this_type, reply_type);
+ }
+
+ /* The message now appears as it would if we were the sender.
+ It is ready to be resent. */
+
+ if (msgid_display (msgid))
+ {
+ if (inp->msgh_local_port == MACH_PORT_NULL
+ && info->type == MACH_MSG_TYPE_MOVE_SEND_ONCE
+ && inp->msgh_size >= sizeof (mig_reply_header_t)
+ /* The notification message is considered as a request. */
+ && (inp->msgh_id > 72 || inp->msgh_id < 64)
+ && (*(int *) &((mig_reply_header_t *) inp)->RetCodeType
+ == *(int *)&RetCodeType))
+ {
+ struct req_info *req = remove_request (inp->msgh_id - 100,
+ inp->msgh_remote_port);
+ assert (req);
+ req->is_req = FALSE;
+ /* This sure looks like an RPC reply message. */
+ mig_reply_header_t *rh = (void *) inp;
+ print_reply_header ((struct send_once_info *) info, rh, req);
+ putc (' ', ostream);
+ fflush (ostream);
+ print_contents (&rh->Head, rh + 1, req);
+ putc ('\n', ostream);
+
+ if (inp->msgh_id == 2161)/* the reply message for thread_create */
+ wrap_new_thread (inp, req);
+ else if (inp->msgh_id == 2107) /* for task_create */
+ wrap_new_task (inp, req);
+
+ free (req);
+ }
+ else
+ {
+ struct task_info *task_info;
+ task_t to = 0;
+ struct req_info *req = NULL;
+
+ /* Print something about the message header. */
+ print_request_header ((struct sender_info *) info, inp);
+ /* It's a nofication message. */
+ if (inp->msgh_id <= 72 && inp->msgh_id >= 64)
+ {
+ assert (info->type == MACH_MSG_TYPE_MOVE_SEND_ONCE);
+ /* mach_notify_port_destroyed message has a port,
+ * TODO how do I handle it? */
+ assert (inp->msgh_id != 69);
+ }
+
+ /* If it's mach_port RPC,
+ * the port rights in the message will be moved to the target task. */
+ else if (inp->msgh_id >= 3200 && inp->msgh_id <= 3218)
+ to = SEND_INFO (info)->receive_right->forward;
+ else
+ to = SEND_INFO (info)->receive_right->task;
+ if (info->type == MACH_MSG_TYPE_MOVE_SEND)
+ req = add_request (inp->msgh_id, reply_port,
+ SEND_INFO (info)->task, to);
+
+ /* If it's the notification message, req is NULL.
+ * TODO again, it's difficult to handle mach_notify_port_destroyed */
+ print_contents (inp, inp + 1, req);
+ if (inp->msgh_local_port == MACH_PORT_NULL) /* simpleroutine */
+ {
+ /* If it's a simpleroutine,
+ * we don't need the request information any more. */
+ req = remove_request (inp->msgh_id, reply_port);
+ free (req);
+ fprintf (ostream, ");\n");
+ }
+ else
+ /* Leave a partial line that will be finished later. */
+ fprintf (ostream, ")");
+ fflush (ostream);
+
+ /* If it's the first request from the traced task,
+ * wrap the all threads in the task. */
+ task_info = hurd_ihash_find (&task_ihash, SEND_INFO (info)->task);
+ if (task_info && !task_info->threads_wrapped)
+ {
+ wrap_all_threads (SEND_INFO (info)->task);
+ task_info->threads_wrapped = TRUE;
+ }
+ }
+ }
+
+ /* Resend the message to the tracee. */
+ err = mach_msg (inp, MACH_SEND_MSG, inp->msgh_size, 0,
+ MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if (err == MACH_SEND_INVALID_DEST)
+ {
+ /* The tracee port died. No doubt we are about to receive the dead-name
+ notification. */
+ /* XXX MAKE_SEND, MAKE_SEND_ONCE rights in msg not handled */
+ mach_msg_destroy (inp);
+ }
+ else
+ assert_perror (err);
+
+ ports_port_deref (info);
+
+ /* We already sent the message, so the server loop shouldn't do it again. */
+ ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
+
+ return 1;
+}
+
+/* This function runs in the tracing thread and drives all the tracing. */
+static void *
+trace_thread_function (void *arg)
+{
+ struct port_bucket *const bucket = arg;
+ ports_manage_port_operations_one_thread (bucket, trace_and_forward, 0);
+ return 0;
+}
+
+/*** Output formatting ***/
+
+#if 0
+struct msg_type
+{
+ const char *name;
+ const char *letter;
+};
+
+static const char *const msg_types[] =
+{
+ [MACH_MSG_TYPE_BIT] = {"bool", "b"},
+ [MACH_MSG_TYPE_INTEGER_16] = {"int16", "h"},
+ [MACH_MSG_TYPE_INTEGER_32] = {"int32", "i"},
+ [MACH_MSG_TYPE_CHAR] = {"char", "c"},
+ [MACH_MSG_TYPE_INTEGER_8] = {"int8", "B"},
+ [MACH_MSG_TYPE_REAL] = {"float", "f"},
+ [MACH_MSG_TYPE_INTEGER_64] = {"int64", "q"},
+ [MACH_MSG_TYPE_STRING] = {"string", "s"},
+ [MACH_MSG_TYPE_MOVE_RECEIVE] = {"move-receive", "R"},
+ [MACH_MSG_TYPE_MOVE_SEND] = {"move-send", "S"},
+ [MACH_MSG_TYPE_MOVE_SEND_ONCE]= {"move-send-once", "O"},
+ [MACH_MSG_TYPE_COPY_SEND] = {"copy-send", "s"},
+ [MACH_MSG_TYPE_MAKE_SEND] = {"make-send", ""},
+ [MACH_MSG_TYPE_MAKE_SEND_ONCE]= {"make-send-once", ""},
+ [MACH_MSG_TYPE_PORT_NAME] = {"port-name", "n"},
+};
+#endif
+
+/* We keep track of the last reply port used in a request we print to
+ ostream. This way we can end incomplete requests with an ellipsis
+ and the name of the reply port. When the reply finally arrives, we
+ start a new line with that port name and an ellipsis, making it
+ easy to match it to the associated request. */
+static mach_port_t last_reply_port;
+
+/* Print an ellipsis if necessary. */
+static void
+print_ellipsis (void)
+{
+ if (MACH_PORT_VALID (last_reply_port))
+ fprintf (ostream, " ...%u\n", (unsigned int) last_reply_port);
+}
+
+static void
+print_request_header (struct sender_info *receiver, mach_msg_header_t *msg)
+{
+ const char *msgname = msgid_name (msg->msgh_id);
+ print_ellipsis ();
+ last_reply_port = msg->msgh_local_port;
+
+ if (TRACED_INFO (receiver)->name != 0)
+ fprintf (ostream, "%4s->", TRACED_INFO (receiver)->name);
+ else
+ fprintf (ostream, "%4u->",
+ (unsigned int) TRACED_INFO (receiver)->pi.port_right);
+
+ if (msgname != 0)
+ fprintf (ostream, "%5s (", msgname);
+ else
+ fprintf (ostream, "%5u (", (unsigned int) msg->msgh_id);
+}
+
+static void
+print_reply_header (struct send_once_info *info, mig_reply_header_t *reply,
+ struct req_info *req)
+{
+ if (last_reply_port != info->pi.pi.port_right)
+ {
+ print_ellipsis ();
+ fprintf (ostream, "%u...", (unsigned int) info->pi.pi.port_right);
+ }
+ last_reply_port = MACH_PORT_NULL;
+
+ /* We have printed a partial line for the request message,
+ and now we have the corresponding reply. */
+ if (reply->Head.msgh_id == req->req_id + 100)
+ fprintf (ostream, " = "); /* normal case */
+ else
+ /* This is not the proper reply message ID. */
+ fprintf (ostream, " =(%u != %u) ",
+ reply->Head.msgh_id, req->req_id + 100);
+
+ if (reply->RetCode == 0)
+ fprintf (ostream, "0");
+ else
+ {
+ const char *str = strerror (reply->RetCode);
+ if (str == 0)
+ fprintf (ostream, "%#x", reply->RetCode);
+ else
+ fprintf (ostream, "%#x (%s)", reply->RetCode, str);
+ }
+}
+
+static char escape_sequences[0x100] =
+ {
+ ['\0'] = '0',
+ ['\a'] = 'a',
+ ['\b'] = 'b',
+ ['\f'] = 'f',
+ ['\n'] = 'n',
+ ['\r'] = 'r',
+ ['\t'] = 't',
+ ['\v'] = 'v',
+ ['\\'] = '\\',
+ ['\''] = '\'',
+ ['"'] = '"',
+ };
+
+static void
+print_data (mach_msg_type_name_t type,
+ const void *data,
+ mach_msg_type_number_t nelt,
+ mach_msg_type_number_t eltsize)
+{
+ switch (type)
+ {
+ case MACH_MSG_TYPE_PORT_NAME:
+ assert (eltsize == sizeof (mach_port_t));
+ {
+ mach_msg_type_number_t i;
+ fprintf (ostream, "pn{");
+ for (i = 0; i < nelt; ++i)
+ {
+ fprintf (ostream, "%*u", (i > 0) ? 4 : 3,
+ (unsigned int) ((mach_port_t *) data)[i]);
+ }
+ fprintf (ostream, "}");
+ return;
+ }
+
+ case MACH_MSG_TYPE_STRING:
+ case MACH_MSG_TYPE_CHAR:
+ if (nelt > strsize)
+ nelt = strsize;
+ fprintf (ostream, "\"");
+ /* Scan data for non-printable characters. p always points to
+ the first character that has not yet been printed. */
+ const char *p, *q;
+ p = q = (const char *) data;
+ while (*q && q - (const char *) data < (int) (nelt * eltsize))
+ {
+ if (isgraph (*q) || *q == ' ')
+ {
+ q += 1;
+ continue;
+ }
+
+ /* We encountered a non-printable character. Print anything
+ that has not been printed so far. */
+ if (p < q)
+ fprintf (ostream, "%.*s", q - p, p);
+
+ char c = escape_sequences[*((const unsigned char *) q)];
+ if (c)
+ fprintf (ostream, "\\%c", c);
+ else
+ fprintf (ostream, "\\x%02x", *((const unsigned char *) q));
+
+ q += 1;
+ p = q;
+ }
+
+ /* Print anything that has not been printed so far. */
+ if (p < q)
+ fprintf (ostream, "%.*s", q - p, p);
+ fprintf (ostream, "\"");
+ return;
+
+#if 0
+ case MACH_MSG_TYPE_CHAR:
+ if (eltsize == 1)
+ FMT ("'%c'", unsigned char);
+ break;
+#endif
+
+#define FMT(fmt, ctype) do { \
+ mach_msg_type_number_t i; \
+ for (i = 0; i < nelt; ++i) \
+ { \
+ fprintf (ostream, "%s" fmt, \
+ (i == 0 && nelt > 1) ? "{" : i > 0 ? " " : "", \
+ *(const ctype *) data); \
+ data += eltsize; \
+ } \
+ if (nelt > 1) \
+ putc ('}', ostream); \
+ return; \
+ } while (0)
+
+ case MACH_MSG_TYPE_BIT:
+ case MACH_MSG_TYPE_INTEGER_8:
+ case MACH_MSG_TYPE_INTEGER_16:
+ case MACH_MSG_TYPE_INTEGER_32:
+ case MACH_MSG_TYPE_INTEGER_64:
+ switch (eltsize)
+ {
+ case 1: FMT ("%"PRId8, int8_t);
+ case 2: FMT ("%"PRId16, int16_t);
+ case 4: FMT ("%"PRId32, int32_t);
+ case 8: FMT ("%"PRId64, int64_t);
+ }
+ break;
+
+ case MACH_MSG_TYPE_REAL:
+ if (eltsize == sizeof (float))
+ FMT ("%g", float);
+ else if (eltsize == sizeof (double))
+ FMT ("%g", double);
+ else if (eltsize == sizeof (long double))
+ FMT ("%Lg", long double);
+ else
+ abort ();
+ break;
+ }
+
+ /* XXX */
+ fprintf (ostream, "\t%#x (type %d, %d*%d)\n", *(const int *)data, type,
+ nelt, eltsize);
+}
+
+
+/*** Main program and child startup ***/
+
+
+/* Run a child and have it do more or else `execvpe (argv, envp);'. */
+pid_t
+traced_spawn (char **argv, char **envp)
+{
+ error_t err;
+ pid_t pid;
+ mach_port_t task_wrapper;
+ task_t traced_task;
+ struct sender_info *ti;
+ struct receiver_info *receive_ti;
+ file_t file = file_name_path_lookup (argv[0], getenv ("PATH"),
+ O_EXEC, 0, 0);
+
+ if (file == MACH_PORT_NULL)
+ error (1, errno, "command not found: %s", argv[0]);
+
+ err = task_create (mach_task_self (),
+#ifdef KERN_INVALID_LEDGER
+ NULL, 0, /* OSF Mach */
+#endif
+ 0, &traced_task);
+ assert_perror (err);
+
+ add_task (traced_task);
+ /* Declare the new task to be our child. This is what a fork does. */
+ err = proc_child (getproc (), traced_task);
+ if (err)
+ error (2, err, "proc_child");
+ pid = task2pid (traced_task);
+ if (pid < 0)
+ error (2, errno, "task2pid");
+
+ receive_ti = new_receiver_info (traced_task, unknown_task);
+ /* Create a trace wrapper for the task port. */
+ ti = new_send_wrapper (receive_ti, traced_task, &task_wrapper);
+ ti->task = traced_task;
+ free (TRACED_INFO (ti)->name);
+ asprintf (&TRACED_INFO (ti)->name, "task%d(pid%d)", traced_task, pid);
+
+ /* Replace the task's kernel port with the wrapper. When this task calls
+ `mach_task_self ()', it will get our wrapper send right instead of its
+ own real task port. */
+ err = mach_port_insert_right (mach_task_self (), task_wrapper,
+ task_wrapper, MACH_MSG_TYPE_MAKE_SEND);
+ assert_perror (err);
+ err = task_set_special_port (traced_task, TASK_KERNEL_PORT, task_wrapper);
+ assert_perror (err);
+
+ /* Now actually run the command they told us to trace. We do the exec on
+ the actual task, so the RPCs to map in the program itself do not get
+ traced. Could have an option to use TASK_WRAPPER here instead. */
+ err = _hurd_exec (traced_task, file, argv, envp);
+ if (err)
+ error (2, err, "cannot exec `%s'", argv[0]);
+
+ /* We were keeping this send right alive so that the wrapper object
+ cannot die and hence our TRACED_TASK ref cannot have been released. */
+ mach_port_deallocate (mach_task_self (), task_wrapper);
+
+ return pid;
+}
+
+
+static void
+scan_msgids_dir (char **argz, size_t *argz_len, char *dir, bool append)
+{
+ struct dirent **eps;
+ int n;
+
+ int
+ msgids_file_p (const struct dirent *eps)
+ {
+ if (fnmatch ("*.msgids", eps->d_name, 0) != FNM_NOMATCH)
+ return 1;
+ return 0;
+ }
+
+ n = scandir (dir, &eps, msgids_file_p, NULL);
+ if (n >= 0)
+ {
+ for (int cnt = 0; cnt < n; ++cnt)
+ {
+ char *msgids_file;
+
+ if (asprintf (&msgids_file, "%s/%s", dir, eps[cnt]->d_name) < 0)
+ error (1, errno, "asprintf");
+
+ if (append == TRUE)
+ {
+ if (argz_add (argz, argz_len, msgids_file) != 0)
+ error (1, errno, "argz_add");
+ }
+ else
+ {
+ if (argz_insert (argz, argz_len, *argz, msgids_file) != 0)
+ error (1, errno, "argz_insert");
+ }
+ free (msgids_file);
+ }
+ }
+
+ /* If the directory couldn't be scanned for whatever reason, just ignore
+ it. */
+}
+
+int
+main (int argc, char **argv, char **envp)
+{
+ char *msgids_files_argz = NULL;
+ size_t msgids_files_argz_len = 0;
+ bool nostdinc = FALSE;
+ const char *outfile = 0;
+ char **cmd_argv = 0;
+ pthread_t thread;
+ error_t err;
+ char **cmd_envp = NULL;
+ char *envz = NULL;
+ size_t envz_len = 0;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'o':
+ outfile = arg;
+ break;
+
+ case OPT_NOSTDINC:
+ nostdinc = TRUE;
+ break;
+
+ case 'i':
+ if (argz_add (&msgids_files_argz, &msgids_files_argz_len,
+ arg) != 0)
+ error (1, errno, "argz_add");
+ break;
+
+ case 'I':
+ scan_msgids_dir (&msgids_files_argz, &msgids_files_argz_len,
+ arg, TRUE);
+ break;
+
+ case 's':
+ strsize = atoi (arg);
+ break;
+
+ case 'E':
+ if (envz == NULL)
+ {
+ if (argz_create (envp, &envz, &envz_len))
+ error (1, errno, "argz_create");
+ }
+ if (envz != NULL)
+ {
+ char *equal = strchr (arg, '=');
+ char *name;
+ char *newval;
+ if (equal != NULL)
+ {
+ name = strndupa (arg, equal - arg);
+ if (name == NULL)
+ error (1, errno, "strndupa");
+ newval = equal + 1;
+ }
+ else
+ {
+ name = arg;
+ newval = NULL;
+ }
+ if (envz_add (&envz, &envz_len, name, newval))
+ error (1, errno, "envz_add");
+ }
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ return EINVAL;
+
+ case ARGP_KEY_ARG:
+ cmd_argv = &state->argv[state->next - 1];
+ state->next = state->argc;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ const struct argp argp = { options, parse_opt, args_doc, doc };
+
+ /* Parse our arguments. */
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_DEAD_NAME,
+ &unknown_task);
+ assert_perror (err);
+
+ /* Insert the files from STD_MSGIDS_DIR at the beginning of the list, so that
+ their content can be overridden by subsequently parsed files. */
+ if (nostdinc == FALSE)
+ scan_msgids_dir (&msgids_files_argz, &msgids_files_argz_len,
+ STD_MSGIDS_DIR, FALSE);
+
+ if (msgids_files_argz != NULL)
+ {
+ char *msgids_file = NULL;
+
+ while ((msgids_file = argz_next (msgids_files_argz,
+ msgids_files_argz_len, msgids_file)))
+ parse_msgid_list (msgids_file);
+
+ free (msgids_files_argz);
+ }
+
+ if (outfile)
+ {
+ ostream = fopen (outfile, "w");
+ if (!ostream)
+ error (1, errno, "%s", outfile);
+ }
+ else
+ ostream = stderr;
+ setlinebuf (ostream);
+
+ traced_bucket = ports_create_bucket ();
+ traced_class = ports_create_class (&traced_clean, NULL);
+ other_class = ports_create_class (0, 0);
+ err = ports_create_port (other_class, traced_bucket,
+ sizeof (*notify_pi), &notify_pi);
+ assert_perror (err);
+
+ hurd_ihash_set_cleanup (&msgid_ihash, msgid_ihash_cleanup, 0);
+
+ /* Spawn a single thread that will receive intercepted messages, print
+ them, and interpose on the ports they carry. The access to the
+ `traced_info' and ihash data structures is all single-threaded,
+ happening only in this new thread. */
+ err = pthread_create (&thread, NULL, trace_thread_function, traced_bucket);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+
+ if (envz != NULL)
+ {
+ envz_strip (&envz, &envz_len);
+ cmd_envp = alloca ((argz_count (envz, envz_len) + 1) * sizeof (char *));
+ if (cmd_envp == NULL)
+ error (1, errno, "alloca");
+ else
+ argz_extract (envz, envz_len, cmd_envp);
+ }
+ if (cmd_envp == NULL)
+ cmd_envp = envp;
+
+ /* Run the program on the command line and wait for it to die.
+ The other thread does all the tracing and interposing. */
+ {
+ pid_t child, pid;
+ int status;
+ child = traced_spawn (cmd_argv, cmd_envp);
+ pid = waitpid (child, &status, 0);
+ sleep (1); /* XXX gives other thread time to print */
+ if (pid != child)
+ error (1, errno, "waitpid");
+ if (WIFEXITED (status))
+ fprintf (ostream, "Child %d exited with %d\n",
+ pid, WEXITSTATUS (status));
+ else
+ fprintf (ostream, "Child %d %s\n", pid, strsignal (WTERMSIG (status)));
+ }
+
+ ports_destroy_right (notify_pi);
+ free (envz);
+
+ return 0;
+}
diff --git a/utils/setauth.c b/utils/setauth.c
new file mode 100644
index 00000000..73f221a0
--- /dev/null
+++ b/utils/setauth.c
@@ -0,0 +1,134 @@
+/* Change the authentication of selected processes
+
+ Copyright (C) 1997, 1998 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <hurd.h>
+#include <argp.h>
+#include <error.h>
+#include <version.h>
+
+#include "frobauth.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (setauth);
+
+#define OPT_NO_SAVE 1
+
+static const struct argp_option options[] =
+{
+#ifdef SU
+ {"no-save", OPT_NO_SAVE, 0, 0, "Don't save removed effective ids as available ids"},
+#else
+ {"save", 's', 0, 0, "Save removed effective ids as available ids"},
+#endif
+ {"keep", 'k', 0, 0, "Keep old ids in addition to the new ones"},
+ { 0 }
+};
+
+static struct argp_child child_argps[] = {{ &frobauth_posix_argp }, { 0 }};
+
+static char doc[] =
+ "Change the authentication of selected processes";
+
+extern error_t
+get_nonsugid_ids (struct idvec *uids, struct idvec *gids);
+
+int
+main (int argc, char *argv[])
+{
+ error_t err;
+ auth_t auth; /* Authority to make changes. */
+ int save = 0, keep = 0;
+ struct idvec have_uids = IDVEC_INIT, have_gids = IDVEC_INIT;
+ struct frobauth frobauth = FROBAUTH_INIT;
+
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 's': save = 1; break;
+ case 'k': keep = 1; break;
+ case OPT_NO_SAVE: save = 0; break;
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = state->input; break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ /* Modify UGIDS, to be what PID's new authentication should be, UGIDS is
+ what the user specified. */
+ error_t modify (struct ugids *ugids, const struct ugids *new,
+ pid_t pid, void *hook)
+ {
+ struct ugids old = UGIDS_INIT;
+ ugids_set (&old, ugids);
+
+ ugids_set (ugids, new);
+
+ if (keep)
+ ugids_merge (ugids, &old);
+ if (save)
+ {
+ ugids_save (&old);
+ ugids_merge (ugids, &old);
+ }
+
+ return 0;
+ }
+ void print_info (const struct ugids *new,
+ const struct ugids *old,
+ const struct ugids *user,
+ pid_t pid, void *hook)
+ {
+ char *new_rep = ugids_rep (new, 1, 1, 0, 0, 0);
+ printf ("%d: Changed auth to %s\n", pid, new_rep);
+ free (new_rep);
+ }
+ struct argp argp = { options, parse_opt, 0, doc, child_argps };
+
+#ifdef SU
+ frobauth.default_user = 0;
+ save = 1; /* Default to saving ids */
+#endif
+
+ /* Parse our command line. This shouldn't ever return an error. */
+ argp_parse (&argp, argc, argv, 0, 0, &frobauth);
+
+ /* See what the invoking user is authorized to do. */
+ err = get_nonsugid_ids (&have_uids, &have_gids);
+ if (err)
+ error (52, err, "Cannot get invoking authentication");
+
+ /* Check passwords. */
+ err = ugids_verify_make_auth (&frobauth.ugids, &have_uids, &have_gids, 0, 0,
+ 0, 0, &auth);
+ if (err == EACCES)
+ error (15, 0, "Invalid password");
+ else if (err)
+ error (16, err, "Authentication failure");
+
+ if (frobauth_modify (&frobauth, &auth, 1, modify, print_info, 0))
+ return 0;
+ else
+ return 1;
+}
diff --git a/utils/settrans.c b/utils/settrans.c
new file mode 100644
index 00000000..ecc6d753
--- /dev/null
+++ b/utils/settrans.c
@@ -0,0 +1,331 @@
+/* Set a file's translator.
+
+ Copyright (C) 1995,96,97,98,2001,02,13 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <argp.h>
+#include <error.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <error.h>
+#include <argz.h>
+#include <hurd/fshelp.h>
+#include <hurd/process.h>
+#include <version.h>
+
+#include <hurd/lookup.h>
+#include <hurd/fsys.h>
+
+
+const char *argp_program_version = STANDARD_HURD_VERSION (settrans);
+
+#define DEFAULT_TIMEOUT 60
+
+#define _STRINGIFY(arg) #arg
+#define STRINGIFY(arg) _STRINGIFY (arg)
+
+static struct argp_option options[] =
+{
+ {"active", 'a', 0, 0, "Start TRANSLATOR and set it as NODE's active translator" },
+ {"passive", 'p', 0, 0, "Change NODE's passive translator record (default)" },
+ {"create", 'c', 0, 0, "Create NODE if it doesn't exist" },
+ {"dereference", 'L', 0, 0, "If a translator exists, put the new one on top"},
+ {"pid-file", 'F', "FILENAME", 0, "When starting an active translator,"
+ " write its pid to this file"},
+ {"pause", 'P', 0, 0, "When starting an active translator, prompt and"
+ " wait for a newline on stdin before completing the startup handshake"},
+ {"timeout", 't',"SEC",0, "Timeout for translator startup, in seconds"
+ " (default " STRINGIFY (DEFAULT_TIMEOUT) "); 0 means no timeout"},
+ {"exclusive", 'x', 0, 0, "Only set the translator if there is not one already"},
+ {"orphan", 'o', 0, 0, "Disconnect old translator from the filesystem "
+ "(do not ask it to go away)"},
+
+ {"chroot", 'C', 0, 0,
+ "Instead of setting the node's translator, take following arguments up to"
+ " `--' and run that command chroot'd to the translated node."},
+
+ {0,0,0,0, "When setting the passive translator, if there's an active translator:"},
+ {"goaway", 'g', 0, 0, "Ask the active translator to go away"},
+ {"keep-active", 'k', 0, 0, "Leave any existing active translator running"},
+
+ {0,0,0,0, "When an active translator is told to go away:"},
+ {"recursive", 'R', 0, 0, "Shutdown its children too"},
+ {"force", 'f', 0, 0, "Ask it to ignore current users and shutdown "
+ "anyway." },
+ {"nosync", 'S', 0, 0, "Don't sync it before killing it"},
+
+ {0, 0}
+};
+static char *args_doc = "NODE [TRANSLATOR ARG...]";
+static char *doc = "Set the passive/active translator on NODE."
+"\vBy default the passive translator is set.";
+
+/* ---------------------------------------------------------------- */
+
+int
+main(int argc, char *argv[])
+{
+ error_t err;
+
+ /* The filesystem node we're putting a translator on. */
+ char *node_name = 0;
+ file_t node;
+
+ /* The translator's arg vector, in '\0' separated format. */
+ char *argz = 0;
+ size_t argz_len = 0;
+
+ /* The control port for any active translator we start up. */
+ fsys_t active_control = MACH_PORT_NULL;
+
+ /* Flags to pass to file_set_translator. */
+ int active_flags = 0;
+ int passive_flags = 0;
+ int lookup_flags = O_NOTRANS;
+ int goaway_flags = 0;
+
+ /* Various option flags. */
+ int passive = 0, active = 0, keep_active = 0, pause = 0, kill_active = 0,
+ orphan = 0;
+ char *pid_file = NULL;
+ int excl = 0;
+ int timeout = DEFAULT_TIMEOUT * 1000; /* ms */
+ char **chroot_command = 0;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_ARG:
+ if (state->arg_num == 0)
+ node_name = arg;
+ else /* command */
+ {
+ error_t err =
+ argz_create (state->argv + state->next - 1, &argz, &argz_len);
+ if (err)
+ error(3, err, "Can't create options vector");
+ state->next = state->argc; /* stop parsing */
+ }
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ return EINVAL;
+
+ case 'a': active = 1; break;
+ case 'p': passive = 1; break;
+ case 'k': keep_active = 1; break;
+ case 'g': kill_active = 1; break;
+ case 'x': excl = 1; break;
+ case 'P': pause = 1; break;
+ case 'F':
+ pid_file = strdup (arg);
+ if (pid_file == NULL)
+ error(3, ENOMEM, "Failed to duplicate argument");
+ break;
+
+ case 'o': orphan = 1; break;
+
+ case 'C':
+ if (chroot_command)
+ {
+ argp_error (state, "--chroot given twice");
+ return EINVAL;
+ }
+ chroot_command = &state->argv[state->next];
+ while (state->next < state->argc)
+ {
+ if (!strcmp (state->argv[state->next], "--"))
+ {
+ state->argv[state->next++] = 0;
+ if (chroot_command[0] == 0)
+ {
+ argp_error (state,
+ "--chroot must be followed by a command");
+ return EINVAL;
+ }
+ return 0;
+ }
+ ++state->next;
+ }
+ argp_error (state, "--chroot command must be terminated with `--'");
+ return EINVAL;
+
+ case 'c': lookup_flags |= O_CREAT; break;
+ case 'L': lookup_flags &= ~O_NOTRANS; break;
+
+ case 'R': goaway_flags |= FSYS_GOAWAY_RECURSE; break;
+ case 'S': goaway_flags |= FSYS_GOAWAY_NOSYNC; break;
+ case 'f': goaway_flags |= FSYS_GOAWAY_FORCE; break;
+
+ /* Use atof so the user can specifiy fractional timeouts. */
+ case 't': timeout = atof (arg) * 1000.0; break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+ if (!active && !passive && !chroot_command)
+ passive = 1; /* By default, set the passive translator. */
+
+ if (passive)
+ passive_flags = FS_TRANS_SET | (excl ? FS_TRANS_EXCL : 0);
+ if (active)
+ active_flags = FS_TRANS_SET | (excl ? FS_TRANS_EXCL : 0)
+ | (orphan ? FS_TRANS_ORPHAN : 0);
+
+ if (passive && !active)
+ {
+ /* When setting just the passive, decide what to do with any active. */
+ if (kill_active)
+ /* Make it go away. */
+ active_flags = FS_TRANS_SET;
+ else if (! keep_active)
+ /* Ensure that there isn't one. */
+ active_flags = FS_TRANS_SET | FS_TRANS_EXCL;
+ }
+
+ if ((active || chroot_command) && argz_len > 0)
+ {
+ /* Error during file lookup; we use this to avoid duplicating error
+ messages. */
+ error_t open_err = 0;
+
+ /* The callback to start_translator opens NODE as a side effect. */
+ error_t open_node (int flags,
+ mach_port_t *underlying,
+ mach_msg_type_name_t *underlying_type,
+ task_t task, void *cookie)
+ {
+ if (pause)
+ {
+ fprintf (stderr, "Translator pid: %d\nPausing...",
+ task2pid (task));
+ getchar ();
+ }
+
+ if (pid_file != NULL)
+ {
+ FILE *h;
+ h = fopen (pid_file, "w");
+ if (h == NULL)
+ error (4, errno, "Failed to open pid file");
+
+ fprintf (h, "%i\n", task2pid (task));
+ fclose (h);
+ }
+
+ node = file_name_lookup (node_name, flags | lookup_flags, 0666);
+ if (node == MACH_PORT_NULL)
+ {
+ open_err = errno;
+ return open_err;
+ }
+
+ *underlying = node;
+ *underlying_type = MACH_MSG_TYPE_COPY_SEND;
+
+ return 0;
+ }
+ err = fshelp_start_translator (open_node, NULL, argz, argz, argz_len,
+ timeout, &active_control);
+ if (err)
+ /* If ERR is due to a problem opening the translated node, we print
+ that name, otherwise, the name of the translator. */
+ error(4, err, "%s", (err == open_err) ? node_name : argz);
+ }
+ else
+ {
+ node = file_name_lookup(node_name, lookup_flags, 0666);
+ if (node == MACH_PORT_NULL)
+ error(1, errno, "%s", node_name);
+ }
+
+ if (active || passive)
+ {
+ err = file_set_translator (node,
+ passive_flags, active_flags, goaway_flags,
+ argz, argz_len,
+ active_control, MACH_MSG_TYPE_COPY_SEND);
+ if (err)
+ error (5, err, "%s", node_name);
+ }
+
+ if (chroot_command)
+ {
+ pid_t pid;
+ switch ((pid = fork ()))
+ {
+ case -1:
+ error (6, errno, "fork");
+
+ case 0:; /* Child. */
+ /* We will act as the parent filesystem would for a lookup
+ of the active translator's root node, then use this port
+ as our root directory while we exec the command. */
+
+ char retry_name[1024]; /* XXX */
+ retry_type do_retry;
+ mach_port_t root;
+ err = fsys_getroot (active_control,
+ MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND,
+ NULL, 0, NULL, 0, 0,
+ &do_retry, retry_name, &root);
+ mach_port_deallocate (mach_task_self (), active_control);
+ if (err)
+ error (6, err, "fsys_getroot");
+ err = hurd_file_name_lookup_retry (&_hurd_ports_use, &getdport, 0,
+ do_retry, retry_name, 0, 0,
+ &root);
+ if (err)
+ error (6, err, "cannot resolve root port");
+
+ if (setcrdir (root))
+ error (7, errno, "cannot install root port");
+ mach_port_deallocate (mach_task_self (), root);
+ if (chdir ("/"))
+ error (8, errno, "cannot chdir to new root");
+
+ execvp (chroot_command[0], chroot_command);
+ error (8, errno, "cannot execute %s", chroot_command[0]);
+ break;
+
+ default: /* Parent. */
+ if (waitpid (pid, NULL, 0) == -1)
+ error (8, errno, "waitpid");
+
+ err = fsys_goaway (active_control, goaway_flags);
+ if (err && err != EBUSY)
+ error (9, err, "fsys_goaway");
+ }
+ }
+
+ return 0;
+}
diff --git a/utils/shd.c b/utils/shd.c
new file mode 100644
index 00000000..a1a4b26b
--- /dev/null
+++ b/utils/shd.c
@@ -0,0 +1,389 @@
+/*
+ Copyright (C) 1994,95,99,2002 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/types.h>
+#include <hurd.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <device/device.h>
+#include <unistd.h>
+#include <errno.h>
+#include <error.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <hurd/exec.h>
+
+mach_port_t proc;
+int pause_startup = 0;
+
+void
+reap (pid_t waitfor)
+{
+ pid_t pid;
+ int status;
+
+ while (1)
+ {
+ pid = waitpid (WAIT_ANY, &status, WUNTRACED | (waitfor ? 0 : WNOHANG));
+
+ if (pid == -1)
+ {
+ if (errno != ECHILD && errno != EWOULDBLOCK)
+ error (0, errno, "waitpid");
+ return;
+ }
+ else if (WIFEXITED (status))
+ printf ("PID %d exit status %d\n",
+ pid, WEXITSTATUS (status));
+ else if (WIFSIGNALED (status))
+ printf ("PID %d %s\n",
+ pid, strsignal (WTERMSIG (status)));
+ else if (WIFSTOPPED (status))
+ printf ("PID %d stopped: %s\n",
+ pid, strsignal (WSTOPSIG (status)));
+ else
+ printf ("PID %d bizarre status %#x\n", pid, status);
+
+ if (pid == waitfor)
+ waitfor = 0;
+ }
+}
+
+pid_t
+run (char **argv, int fd0, int fd1)
+{
+ file_t file;
+ char *program;
+ error_t err;
+
+ if (strchr (argv[0], '/') != NULL)
+ program = argv[0];
+ else
+ {
+ size_t len = strlen (argv[0]);
+ const char bin[] = "/bin/";
+ program = alloca (sizeof bin + len);
+ memcpy (program, bin, sizeof bin - 1);
+ memcpy (&program[sizeof bin - 1], argv[0], len + 1);
+ }
+
+ file = file_name_lookup (program, O_EXEC, 0);
+ if (file == MACH_PORT_NULL)
+ {
+ error (0, errno, "%s", program);
+ return -1;
+ }
+ else
+ {
+ task_t task;
+ pid_t pid;
+
+ err = task_create (mach_task_self (),
+#ifdef KERN_INVALID_LEDGER
+ NULL, 0, /* OSF Mach */
+#endif
+ 0, &task);
+ if (err)
+ {
+ error (0, err, "task_create");
+ pid = -1;
+ }
+ else
+ {
+ int save0, save1;
+
+ inline int movefd (int from, int to, int *save)
+ {
+ if (from == to)
+ return 0;
+ *save = dup (to);
+ if (*save < 0)
+ {
+ error (0, errno, "dup");
+ return -1;
+ }
+ if (dup2 (from, to) != to)
+ {
+ error (0, errno, "dup2");
+ return -1;
+ }
+ close (from);
+ return 0;
+ }
+ inline int restorefd (int from, int to, int *save)
+ {
+ if (from == to)
+ return 0;
+ if (dup2 (*save, to) != to)
+ {
+ error (0, errno, "dup2");
+ return -1;
+ }
+ close (*save);
+ return 0;
+ }
+
+ pid = task2pid (task);
+ if (pid == -1)
+ {
+ error (0, errno, "task2pid");
+ pid = 0;
+ }
+ err = proc_child (proc, task);
+ if (err)
+ error (0, err, "proc_child");
+ if (pause_startup)
+ {
+ printf ("Pausing (child PID %d)...", pid);
+ fflush (stdout);
+ getchar ();
+ }
+
+ if (movefd (fd0, 0, &save0) ||
+ movefd (fd1, 1, &save1))
+ return -1;
+
+ err = _hurd_exec (task, file, argv, environ);
+
+ if (restorefd (fd0, 0, &save0) ||
+ restorefd (fd1, 1, &save1))
+ return -1;
+
+ if (err)
+ {
+ error (0, err, "_hurd_exec");
+ err = task_terminate (task);
+ if (err)
+ error (0, err, "task_terminate");
+ }
+ mach_port_deallocate (mach_task_self (), task);
+
+ }
+ mach_port_deallocate (mach_task_self (), file);
+
+ errno = err;
+ return pid;
+ }
+}
+
+void
+command (int argc, char **argv)
+{
+ pid_t pid;
+ int bg;
+ int i, start;
+ int fds[2] = { 0, 1 };
+
+ bg = !strcmp (argv[argc - 1], "&");
+ if (bg)
+ argv[--argc] = NULL;
+
+ start = 0;
+ for (i = 1; i < argc; ++i)
+ if (! strcmp (argv[i], "|"))
+ {
+ int fd0 = fds[0];
+ argv[i] = NULL;
+ if (pipe (fds))
+ {
+ error (0, errno, "pipe");
+ return;
+ }
+ pid = run (argv + start, fd0, fds[1]);
+ if (pid < 0)
+ return;
+ start = i + 1;
+ }
+
+ pid = run (argv + start, fds[0], 1);
+
+ if (fds[0] != 0)
+ close (fds[0]);
+ if (fds[1] != 1)
+ close (fds[1]);
+
+ reap (bg ? 0 : pid);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ char *linebuf = NULL;
+ size_t linebufsize = 0;
+
+ proc = getproc ();
+ assert (proc);
+
+#if 0
+ {
+ error_t err;
+ mach_port_t outp;
+ mach_port_t hostp, masterd;
+ err = proc_getprivports (proc, &hostp, &masterd);
+ assert (!err);
+
+ err = device_open (masterd, D_WRITE|D_READ, "console", &outp);
+ assert (!err);
+
+ stdin = mach_open_devstream (outp, "r");
+ stdout = stderr = mach_open_devstream (outp, "w+");
+ }
+#endif
+
+ /* Kludge to give boot a port to the auth server. */
+ exec_init (getdport (0), getauth (),
+ MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND);
+
+ if ((fcntl (0, F_GETFL) & O_READ) == 0)
+ {
+ int ttyd = open ("/dev/tty", O_RDWR|O_IGNORE_CTTY);
+ if (ttyd >= 0)
+ {
+ fcntl (ttyd, F_SETFD, FD_CLOEXEC);
+ stdin = fdopen (ttyd, "r");
+ stdout = stderr = fdopen (ttyd, "w");
+ }
+ }
+
+ atexit ((void (*) (void)) &sync);
+
+ while (1)
+ {
+ ssize_t n;
+
+ sync ();
+ printf ("# ");
+ fflush (stdout);
+ n = getline (&linebuf, &linebufsize, stdin);
+ if (n == -1)
+ {
+ if (feof (stdin))
+ return 0;
+ error (0, errno, "getline");
+ continue;
+ }
+
+ if (linebuf[n - 1] == '\n')
+ linebuf[--n] = '\0';
+
+ if (n > 0)
+ {
+ char *argv[(n + 1) / 2 + 1];
+ int argc;
+ char *line, *p;
+
+ line = linebuf;
+ argc = 0;
+ while ((p = strsep (&line, " \t\n\f\v")) != NULL)
+ argv[argc++] = p;
+ argv[argc] = NULL;
+
+ if (!strcmp (argv[0], "exit"))
+ {
+ reap (0);
+ exit (0);
+ }
+ else if (!strcmp (argv[0], "pause"))
+ pause_startup = 1;
+ else if (!strcmp (argv[0], "nopause"))
+ pause_startup = 0;
+ else if (!strcmp (argv[0], "kill"))
+ {
+ if (argc == 1)
+ fprintf (stderr, "Usage: kill PID ...\n");
+ else
+ {
+ int pid;
+ task_t task;
+ int i;
+ for (i = 1; i < argc; i++)
+ {
+ pid = atoi (argv[i]);
+ printf ("Killing pid %d\n", pid);
+ if (pid)
+ {
+ proc_pid2task (proc, pid, &task);
+ task_terminate (task);
+ mach_port_deallocate (mach_task_self (), task);
+ }
+ }
+ }
+ }
+ else if (!strcmp (argv[0], "cd"))
+ {
+ if (argc != 2)
+ fprintf (stderr, "Usage: cd DIRECTORY\n");
+ else if (chdir (argv[1]))
+ error (0, errno, "chdir");
+ }
+ else if (!strcmp (argv[0], "exec"))
+ {
+ if (argc == 1)
+ fprintf (stderr, "Usage: exec PROGRAM [ARGS...]\n");
+ else
+ {
+ char *program;
+ if (strchr (argv[1], '/') != NULL)
+ program = argv[1];
+ else
+ {
+ size_t len = strlen (argv[1]);
+ const char bin[] = "/bin/";
+ program = alloca (sizeof bin + len);
+ memcpy (program, bin, sizeof bin - 1);
+ memcpy (&program[sizeof bin - 1], argv[1], len + 1);
+ }
+ if (execv (program, &argv[1]) == 0)
+ fprintf (stderr, "execv (%s) returned 0!\n", program);
+ else
+ error (0, errno, "execv");
+ }
+ }
+ else if (!strcmp (argv[0], "setenv"))
+ {
+ if (argc != 3)
+ fprintf (stderr, "Usage: setenv VAR VALUE\n");
+ else if (setenv (argv[1], argv[2], 1))
+ error (0, errno, "setenv");
+ }
+ else if (!strcmp (argv[0], "fork"))
+ {
+ pid_t pid = fork ();
+ switch (pid)
+ {
+ case -1:
+ error (0, errno, "fork");
+ break;
+ case 0:
+ printf ("I am the child, PID %d.\n", (int) getpid ());
+ break;
+ default:
+ printf ("I am the parent of child with PID %d.\n", pid);
+ reap (argc == 2 && !strcmp (argv[1], "&") ? 0 : pid);
+ break;
+ }
+ }
+ else
+ command (argc, argv);
+ }
+ reap (0);
+ fflush (stderr);
+ }
+}
diff --git a/utils/showtrans.c b/utils/showtrans.c
new file mode 100644
index 00000000..aa55f14d
--- /dev/null
+++ b/utils/showtrans.c
@@ -0,0 +1,144 @@
+/* Show files' passive translators.
+
+ Copyright (C) 1995,96,97,98,99,2001,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <argp.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <version.h>
+#include <sys/mman.h>
+
+#include <error.h>
+#include <argz.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (showtrans);
+
+static struct argp_option options[] =
+{
+ {"prefix", 'p', 0, 0, "Always display `FILENAME: ' before translators"},
+ {"no-prefix", 'P', 0, 0, "Never display `FILENAME: ' before translators"},
+ {"silent", 's', 0, 0, "No output; useful when checking error status"},
+ {"quiet", 'q', 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"translated",'t', 0, 0, "Only display files that have translators"},
+ {0, 0}
+};
+
+static char *args_doc = "FILE...";
+static char *doc = "Show the passive translator of FILE..."
+"\vA FILE argument of `-' prints the translator on the node"
+" attached to standard input.";
+
+/* ---------------------------------------------------------------- */
+
+int
+main (int argc, char *argv[])
+{
+ /* The default exit status -- changed to 0 if we find any translators. */
+ int status = 1;
+ /* Some option flags. -1 for PRINT_PREFIX means use the default. */
+ int print_prefix = -1, silent = 0, show_untrans = 1;
+
+ /* If NODE is MACH_PORT_NULL, prints an error message and exits, otherwise
+ prints the translator on NODE, possibly prefixed by `NAME:', and
+ deallocates NODE. */
+ void print_node_trans (file_t node, char *name)
+ {
+ if (node == MACH_PORT_NULL)
+ error (0, errno, "%s", name);
+ else
+ {
+ char buf[1024], *trans = buf;
+ size_t trans_len = sizeof (buf);
+ error_t err = file_get_translator (node, &trans, &trans_len);
+
+ switch (err)
+ {
+ case 0:
+ /* Make the '\0's in TRANS printable. */
+ argz_stringify (trans, trans_len, ' ');
+
+ if (!silent)
+ {
+ if (print_prefix)
+ printf ("%s: %.*s\n", name, (int) trans_len, trans);
+ else
+ printf ("%.*s\n", (int) trans_len, trans);
+ }
+
+ if (trans != buf)
+ munmap (trans, trans_len);
+
+ status = 0;
+
+ break;
+
+ case EINVAL:
+ /* NODE just doesn't have a translator. */
+ if (!silent && print_prefix && show_untrans)
+ puts (name);
+ break;
+
+ default:
+ error (0, err, "%s", name);
+ }
+
+ mach_port_deallocate (mach_task_self (), node);
+ }
+ }
+
+ /* Parse a command line option. */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_ARG: /* A FILE argument */
+ if (print_prefix < 0)
+ /* By default, only print a prefix if there are multiple files. */
+ print_prefix = state->next < state->argc;
+
+ if (strcmp (arg, "-") != 0)
+ print_node_trans (file_name_lookup (arg, O_NOTRANS, 0), arg);
+ else
+ print_node_trans (getdport (0), "-");
+ break;
+
+ /* Options. */
+ case 'p': print_prefix = 1; break;
+ case 'P': print_prefix = 0; break;
+ case 's': case 'q': silent = 1; break;
+ case 't': show_untrans = 0; break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state); /* exits */
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ return status;
+}
diff --git a/utils/storecat.c b/utils/storecat.c
new file mode 100644
index 00000000..7f9de491
--- /dev/null
+++ b/utils/storecat.c
@@ -0,0 +1,73 @@
+/* Write a store to stdout
+
+ Copyright (C) 1996, 1997, 2001, 2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <argp.h>
+#include <error.h>
+
+#include <hurd/store.h>
+#include <version.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (storecat);
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ struct store *s;
+ char *name;
+ store_offset_t addr;
+ store_offset_t left;
+ const struct argp_child kids[] = { { &store_argp }, { 0 }};
+ struct argp argp =
+ { 0, 0, 0, "Write the contents of a store to stdout", kids };
+ struct store_argp_params p = { 0 };
+
+ argp_parse (&argp, argc, argv, 0, 0, &p);
+ err = store_parsed_name (p.result, &name);
+ if (err)
+ error (2, err, "store_parsed_name");
+
+ err = store_parsed_open (p.result, STORE_READONLY, &s);
+ if (err)
+ error (4, err, "%s", name);
+
+ addr = 0;
+ left = s->size;
+ while (left > 0)
+ {
+ size_t read = left > 1024*1024 ? 1024*1024 : left;
+ char buf[4096];
+ void *data = buf;
+ size_t data_len = sizeof (buf);
+
+ err = store_read (s, addr, read, &data, &data_len);
+ if (err)
+ error (5, err, "%s", name);
+ if (write (1, data, data_len) < 0)
+ error (6, errno, "stdout");
+
+ addr += data_len >> s->log2_block_size;
+ left -= data_len;
+ }
+
+ exit (0);
+}
diff --git a/utils/storeinfo.c b/utils/storeinfo.c
new file mode 100644
index 00000000..8a9567e0
--- /dev/null
+++ b/utils/storeinfo.c
@@ -0,0 +1,267 @@
+/* Show where a file exists
+
+ Copyright (C) 1995,96,97,98,99,2001,02 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <argp.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <version.h>
+
+#include <error.h>
+
+#include <hurd/fs.h>
+#include <hurd/store.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (storeinfo);
+
+static struct argp_option options[] =
+{
+ {"type", 't', 0, 0, "Print the type of store behind FILE"},
+ {"flags", 'f', 0, 0, "Print the flags associated with FILE's store"},
+ {"name", 'n', 0, 0, "Print the name of the store behind FILE"},
+ {"blocks", 'b', 0, 0, "Print the number of blocks in FILE"},
+ {"block-size", 'B', 0, 0, "Print the block size of FILE's store"},
+ {"size", 's', 0, 0, "Print the size, in bytes, of FILE"},
+ {"block-list", 'l', 0, 0, "Print the blocks that are in FILE"},
+ {"children", 'c', 0, 0, "If the store has children, show them too"},
+ {"dereference", 'L', 0, 0, "If FILE is a symbolic link, follow it"},
+ {"prefix", 'p', 0, 0, "Always print `FILE: ' before info"},
+ {"no-prefix", 'P', 0, 0, "Never print `FILE: ' before info"},
+ {0, 0}
+};
+static char *args_doc = "FILE...";
+static char *doc = "Show information about storage used by FILE..."
+"\vWith no FILE arguments, the file attached to standard"
+" input is used. The fields to be printed are separated by colons, in this"
+" order: PREFIX: TYPE (FLAGS): NAME: BLOCK-SIZE: BLOCKS: SIZE: BLOCK-LIST."
+" If the store is a composite one and --children is specified, children"
+" are printed on lines following the main store, indented accordingly."
+" By default, all fields, and children, are printed.";
+
+/* ---------------------------------------------------------------- */
+
+/* Things we can print about a file's storage. */
+#define W_SOURCE 0x01
+#define W_TYPE 0x02
+#define W_NAME 0x04
+#define W_BLOCKS 0x08
+#define W_BLOCK_SIZE 0x10
+#define W_SIZE 0x20
+#define W_RUNS 0x40
+#define W_CHILDREN 0x80
+#define W_FLAGS 0x100
+
+#define W_ALL 0x1FF
+
+/* Print a line of information (exactly what is determinted by WHAT)
+ about store to stdout. LEVEL is the desired indentation level. */
+static void
+print_store (struct store *store, int level, unsigned what)
+{
+ int i;
+ int first = 1;
+
+ void psep ()
+ {
+ if (first)
+ first = 0;
+ else
+ {
+ putchar (':');
+ putchar (' ');
+ }
+ }
+ void pstr (const char *str, unsigned mask)
+ {
+ if ((what & mask) == mask)
+ {
+ psep ();
+ fputs (str ?: "-", stdout);
+ }
+ }
+ void psiz (size_t val, unsigned mask)
+ {
+ if ((what & mask) == mask)
+ {
+ psep ();
+ printf ("%zu", val);
+ }
+ }
+ void poff (store_offset_t val, unsigned mask)
+ {
+ if ((what & mask) == mask)
+ {
+ psep ();
+ printf ("%Ld", val);
+ }
+ }
+
+ /* Indent */
+ for (i = 0; i < level; i++)
+ {
+ putchar (' ');
+ putchar (' ');
+ }
+
+ pstr (store->class->name,W_TYPE);
+
+ if ((store->flags & ~STORE_INACTIVE) && (what & W_FLAGS))
+ {
+ int t = 0; /* flags tested */
+ int f = 1;
+ void pf (int mask, char *name)
+ {
+ if (store->flags & mask)
+ {
+ if (f)
+ f = 0;
+ else
+ putchar (',');
+ fputs (name, stdout);
+ }
+ t |= mask;
+ }
+
+ if (! first)
+ putchar (' ');
+ first = 0;
+ putchar ('(');
+
+ pf (STORE_READONLY, "ro");
+ pf (STORE_HARD_READONLY, "h_ro");
+ pf (STORE_ENFORCED, "enf");
+ pf (STORAGE_MUTATED, "mut");
+
+ if (store->flags & ~(t | STORE_INACTIVE))
+ /* Leftover flags. */
+ {
+ if (! f)
+ putchar (';');
+ printf ("0x%x", store->flags & ~(t | STORE_INACTIVE));
+ }
+ putchar (')');
+ }
+
+ pstr (store->name, W_NAME);
+ psiz (store->block_size, W_BLOCK_SIZE);
+ poff (store->blocks, W_BLOCKS);
+ poff (store->size, W_SIZE);
+
+ if (what & W_RUNS)
+ {
+ psep ();
+ for (i = 0; i < store->num_runs; i++)
+ {
+ if (i > 0)
+ putchar (',');
+ if (store->runs[i].start < 0)
+ /* A hole */
+ printf ("@+%Ld", store->runs[i].length);
+ else
+ printf ("%Ld+%Ld", store->runs[i].start, store->runs[i].length);
+ }
+ }
+
+ putchar ('\n');
+
+ if (what & W_CHILDREN)
+ /* Show info about stores that make up this one. */
+ for (i = 0; i < store->num_children; i++)
+ print_store (store->children[i], level + 1, what);
+}
+
+int
+main (int argc, char *argv[])
+{
+ int deref = 0, print_prefix = -1;
+ unsigned what = 0;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ void info (mach_port_t file, char *source, error_t err)
+ {
+ struct store *store;
+
+ if (file == MACH_PORT_NULL)
+ error (3, err, "%s", source);
+
+ if (print_prefix < 0)
+ /* By default, only print filename prefixes for multiple files. */
+ print_prefix = state->next < state->argc;
+
+ if (what == 0)
+ what = W_ALL;
+
+ /* The STORE_NO_FILEIO flag tells it to give us the special
+ "unknown" class instead of an error if it cannot parse the
+ file_get_storage_info results. That will allow us to display
+ what we can from them, i.e. the name that shows at least some
+ of what the unknown data looked like. */
+ err = store_create (file, STORE_INACTIVE|STORE_NO_FILEIO, 0, &store);
+ if (err)
+ error (4, err, "%s", source);
+
+ print_store (store, 0, what);
+ store_free (store);
+ }
+
+ switch (key)
+ {
+ case 'L': deref = 1; break;
+ case 'p': print_prefix = 1; break;
+ case 'P': print_prefix = 0; break;
+
+ case 't': what |= W_TYPE; break;
+ case 'f': what |= W_FLAGS; break;
+ case 'n': what |= W_NAME; break;
+ case 'b': what |= W_BLOCKS; break;
+ case 'B': what |= W_BLOCK_SIZE; break;
+ case 's': what |= W_SIZE; break;
+ case 'l': what |= W_RUNS; break;
+ case 'c': what |= W_CHILDREN; break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+
+ case ARGP_KEY_ARG:
+ if (strcmp (arg, "-") == 0)
+ info (getdport (0), "-", 0);
+ else
+ {
+ file_t file = file_name_lookup (arg, deref ? 0 : O_NOLINK, 0);
+ info (file, arg, errno);
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ return 0;
+}
diff --git a/utils/storeread.c b/utils/storeread.c
new file mode 100644
index 00000000..df381bec
--- /dev/null
+++ b/utils/storeread.c
@@ -0,0 +1,130 @@
+/* Write portions of a store to stdout
+
+ Copyright (C) 1996,97,99,2001,02,03,04 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <argp.h>
+#include <error.h>
+#include <unistd.h>
+#include <hurd.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+
+#include <hurd/store.h>
+#include <version.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (storeread);
+
+struct argp_option options[] = {
+ {"file", 'f', 0, 0, "Use file IO instead of the raw device"},
+ {"block-size", 'b', "BYTES", 0, "Set the file block size"},
+ {0, 0}
+};
+const char arg_doc[] = "FILE [ADDR [LENGTH]]...";
+const char doc[] = "Write portions of the contents of a store to stdout"
+"\vADDR is in blocks, and defaults to 0;"
+" LENGTH is in bytes, and defaults to the remainder of FILE.";
+
+int
+main (int argc, char **argv)
+{
+ struct store *store = 0;
+ store_offset_t addr = -1;
+ int dumped = 0, use_file_io = 0, block_size = 0;
+
+ void dump (store_offset_t addr, ssize_t len)
+ {
+ char buf[4096];
+ void *data = buf;
+ size_t data_len = sizeof (buf);
+
+ /* XXX: store->size can be too big for len. */
+ error_t err =
+ store_read (store, addr, len < 0 ? store->size : len,
+ &data, &data_len);
+ if (err)
+ error (5, err, store->name ? "%s" : "<store>", store->name);
+ if (write (1, data, data_len) < 0)
+ error (6, errno, "stdout");
+ if (data != buf)
+ munmap (data, data_len);
+ }
+
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'f': use_file_io = 1; break;
+ case 'b': block_size = atoi (arg); break;
+
+ case ARGP_KEY_ARG:
+ if (! store)
+ {
+ error_t err;
+ file_t source = file_name_lookup (arg, O_READ, 0);
+ if (errno)
+ error (2, errno, "%s", arg);
+ if (use_file_io)
+ if (block_size)
+ {
+ struct stat stat;
+ err = io_stat (source, &stat);
+ if (! err)
+ {
+ struct store_run run = {0, stat.st_size / block_size};
+ err = _store_file_create (source, 0, block_size, &run, 1,
+ &store);
+ }
+ }
+ else
+ err = store_file_create (source, 0, &store);
+ else
+ err = store_create (source, 0, 0, &store);
+ if (err)
+ error (3, err, "%s", arg);
+ }
+ else if (addr < 0)
+ addr = atoll (arg);
+ else
+ {
+ dump (addr, atoi (arg));
+ dumped = 1;
+ addr = -1;
+ }
+ break;
+
+ case ARGP_KEY_END:
+ if (!store)
+ argp_usage (state);
+
+ if (addr >= 0)
+ dump (addr, -1);
+ else if (! dumped)
+ dump (0, -1);
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, arg_doc, doc};
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+ exit (0);
+}
diff --git a/utils/sush.sh b/utils/sush.sh
new file mode 100644
index 00000000..4fccd75e
--- /dev/null
+++ b/utils/sush.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+# A unix-like su (one which invokes a sub-shell).
+#
+# Copyright (C) 1996 Free Software Foundation, Inc.
+#
+# Written by Miles Bader <miles@gnu.ai.mit.edu>
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+ARGS_DOC="[USER|- [COMMAND [ARG...]]]"
+USAGE="Usage: $0 $ARGS_DOC"
+DOC="Start a new shell, or COMMAND, as USER"
+
+LOGIN=${LOGIN-/bin/login}
+FMT=${FMT-/bin/fmt}
+
+needs_arg=""
+for I; do
+ case $needs_arg in
+ ?*) needs_arg="";;
+ "")
+ case "$I" in
+ -e|-E|-g|-G|-u|-U|--envar|--envva|--env|--en|--e|--envvar-default|--envvar-defaul|--envvar-defau|--envvar-defa|--envvar-def|--envvar-def|--envvar-de|--envvar-d|--envvar-|--group|--grou|--gro|--gr|--g|--avail-group|--avail-grou|--avail-gro|--avail-gr|--avail-g|--user|--use|--us|--u|--avail-user|--avail-use|--avail-us|--avail-u)
+ needs_arg="$I";;
+ -e*|-E*|-g*|-G*|-u*|-U*|--envar=*|--envva=*|--env=*|--en=*|--e=*|--envvar-default=*|--envvar-defaul=*|--envvar-defau=*|--envvar-defa=*|--envvar-def=*|--envvar-def=*|--envvar-de=*|--envvar-d=*|--envvar-=*|--group=*|--grou=*|--gro=*|--gr=*|--g=*|--avail-group=*|--avail-grou=*|--avail-gro=*|--avail-gr=*|--avail-g=*|--user=*|--use=*|--us=*|--u=*|--avail-user=*|--avail-use=*|--avail-us=*|--avail-u=*)
+ :;;
+ --avail-|--avail|--avai|--ava|--av|--a|--avail-=*|--avail=*|--avai=*|--ava=*|--av=*|--a=*)
+ echo 1>&2 "$0: option \`$1' is ambiguous"
+ echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information";
+ exit 1;;
+ --help|"-?" |--hel|--he|--h)
+ echo "$USAGE"
+ echo "$DOC"
+ echo ""
+ echo " -?, --help Give this help list"
+ echo " -e ENTRY, --envvar=ENTRY Add ENTRY to the environment"
+ echo " -E ENTRY, --envvar-default=ENTRY"
+ echo " Use ENTRY as a default environment variable"
+ echo " -g GROUP, --group=GROUP Add GROUP to the effective groups"
+ echo " -G GROUP, --avail-group=GROUP Add GROUP to the available groups"
+ echo " -u USER, --user=USER Add USER to the effective uids"
+ echo " -U USER, --avail-user=USER Add USER to the available uids"
+ echo " --usage Give a short usage message"
+ echo " -V, --version Print program version"
+ echo ""
+ echo "Unlike the traditional unix \`su' command, if USER is not specified,"
+ echo "then the result is *no* user-ids, not uid 0."
+ exit 0;;
+ --usage |--usag|--usa|--us|--u)
+ (echo "Usage: $0 [-V?]"
+ echo " [-e ENTRY] [-E ENTRY] [-g GROUP] [-G GROUP] [-u USER] [-U USER] [--envvar=ENTRY] [--envvar-default=ENTRY] [--group=GROUP] [--avail-group=GROUP][--group=GROUP] [--avail-group=GROUP] [--user=USER] [--avail-user=USER] [--help] [--usage] [--version] $ARGS_DOC") |$FMT -t
+ exit 0;;
+ --version|-V |--versio|--versi|--vers|--ver|--ve|--v)
+ echo "STANDARD_HURD_VERSION_sush_"; exit 0;;
+ -)
+ : ;;
+ --)
+ break;;
+ -*)
+ echo 1>&2 "$0: unrecognized option \`$1'"
+ echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information";
+ exit 1;;
+ *)
+ break;;
+ esac;;
+ esac
+done
+
+case "$needs_arg" in ?*)
+ echo 1>&2 "$0: option \`$1' requires an argument"
+ echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information";
+ exit 1;;
+esac
+
+exec $LOGIN --program-name="$0" -pxSLf -aHOME -aMOTD -aUMASK -aBACKUP_SHELLS "$@"
diff --git a/utils/syncfs.c b/utils/syncfs.c
new file mode 100644
index 00000000..3434f5c6
--- /dev/null
+++ b/utils/syncfs.c
@@ -0,0 +1,80 @@
+/* syncfs -- User interface to file_syncfs, synchronize filesystems.
+ Copyright (C) 1994, 95, 96, 97, 98, 99 Free Software Foundation, Inc.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <argp.h>
+#include <error.h>
+#include <version.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (sync);
+
+static int synchronous = 0, do_children = 1;
+
+static void
+sync_one (const char *name, file_t port)
+{
+ error_t err = (port == MACH_PORT_NULL ? errno
+ : file_syncfs (port, synchronous, do_children));
+ if (err)
+ error (1, err, "%s", name);
+}
+
+static error_t
+parser (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 's': synchronous = 1; break;
+ case 'c': do_children = 0; break;
+
+ case ARGP_KEY_NO_ARGS:
+ sync_one ("/", getcrdir ());
+ break;
+
+ case ARGP_KEY_ARG:
+ sync_one (arg, file_name_lookup (arg, 0, 0));
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ static struct argp_option options[] =
+ {
+ {"synchronous", 's', 0, 0, "Wait for completion of all disk writes"},
+ {"no-children", 'c', 0, 0, "Do not synchronize child filesystems"},
+ {0}
+ };
+ struct argp argp =
+ {options, parser,
+ "[FILE...]", "Force all pending disk writes to be done immediately"
+ "\vThe filesystem containing each FILE is synchronized, and its child"
+ " filesystems unless --no-children is specified. With no FILE argument"
+ " synchronizes the root filesystem."};
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ return 0;
+}
diff --git a/utils/umount.c b/utils/umount.c
new file mode 100644
index 00000000..7901da60
--- /dev/null
+++ b/utils/umount.c
@@ -0,0 +1,357 @@
+/* Roughly Unix/Linux-compatible `umount' frontend for Hurd translators.
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <fcntl.h>
+#include <hurd/fshelp.h>
+#include <hurd/fsys.h>
+#include <hurd/paths.h>
+#include <hurd/process.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "match-options.h"
+#include "../sutils/fstab.h"
+
+/* XXX fix libc */
+#undef _PATH_MOUNTED
+#define _PATH_MOUNTED "/etc/mtab"
+
+static char *targets;
+static size_t targets_len;
+static int readonly;
+static int verbose;
+static int passive_flags;
+static int active_flags = FS_TRANS_SET;
+static int goaway_flags;
+static int source_goaway;
+static int fake;
+
+static struct fstab_argp_params fstab_params;
+
+#define FAKE_KEY 0x80 /* !isascii (FAKE_KEY), so no short option. */
+
+static const struct argp_option argp_opts[] =
+{
+ {NULL, 'd', 0, 0, "Also ask the source translator to go away"},
+ {"fake", FAKE_KEY, 0, 0, "Do not actually umount, just pretend"},
+ {"force", 'f', 0, 0, "Force umount by killing the translator"},
+ {"no-mtab", 'n', 0, 0, "Do not update /etc/mtab"},
+ {"read-only", 'r', 0, 0, "If unmounting fails, try to remount read-only"},
+ {"nosync", 'S', 0, 0, "Don't sync a translator before killing it"},
+ {"test-opts", 'O', "OPTIONS", 0,
+ "Only mount fstab entries matching the given set of options"},
+ {"verbose", 'v', 0, 0, "Give more detailed information"},
+ {},
+};
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct fstab_argp_params *params = state->input;
+ error_t err;
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = params; /* pass down to fstab_argp parser */
+ break;
+
+ case 'd':
+ source_goaway = 1;
+ break;
+
+ case FAKE_KEY:
+ fake = 1;
+ break;
+
+ case 'f':
+ goaway_flags |= FSYS_GOAWAY_FORCE;
+ break;
+
+ case 'n':
+ /* do nothing */
+ break;
+
+ case 'r':
+ readonly = 1;
+ break;
+
+ case 'S':
+ goaway_flags |= FSYS_GOAWAY_NOSYNC;
+ break;
+
+ case 'O':
+ err = argz_create_sep (arg, ',', &test_opts, &test_opts_len);
+ if (err)
+ argp_failure (state, 100, ENOMEM, "%s", arg);
+ break;
+
+ case 'v':
+ verbose += 1;
+ break;
+
+ case ARGP_KEY_ARG:
+ err = argz_add (&targets, &targets_len, arg);
+ if (err)
+ argp_failure (state, 100, ENOMEM, "%s", arg);
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ if (! params->do_all)
+ {
+ argp_error (state,
+ "filesystem argument required if --all is not given");
+ return EINVAL;
+ }
+ break;
+
+ case ARGP_KEY_END:
+ if (params->do_all && targets)
+ {
+ argp_error (state, "filesystem argument not allowed with --all");
+ return EINVAL;
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static const char doc[] = "Stop active filesystem translators";
+static const char args_doc[] = "DEVICE|DIRECTORY [DEVICE|DIRECTORY ...]";
+
+static struct argp fstab_argp_mtab; /* Slightly modified version. */
+
+static const struct argp_child argp_kids[] =
+{
+ {&fstab_argp_mtab, 0,
+ "Filesystem selection (if no explicit filesystem arguments given):", 2},
+ {},
+};
+static struct argp argp =
+{
+ options: argp_opts,
+ parser: parse_opt,
+ args_doc: args_doc,
+ doc: doc,
+ children: argp_kids,
+};
+
+/* This is a trimmed and slightly modified version of
+ fstab_argp.options which uses _PATH_MOUNTED instead of _PATH_MNTTAB
+ in the doc strings. */
+static const struct argp_option fstab_argp_mtab_opts[] =
+{
+ {"all", 'a', 0, 0, "Do all filesystems in " _PATH_MOUNTED},
+ {0, 'A', 0, OPTION_ALIAS },
+ {"fstab", 'F', "FILE", 0, "File to use instead of " _PATH_MOUNTED},
+ {"fstype", 't', "TYPE", 0, "Do only filesystems of given type(s)"},
+ {"exclude-root",'R',0, 0,
+ "Exclude root (/) filesystem from " _PATH_MOUNTED " list"},
+ {"exclude", 'X', "PATTERN", 0, "Exclude directories matching PATTERN"},
+ {}
+};
+
+static error_t
+fstab_argp_mtab_parse_opt (int key, char *arg, struct argp_state *state)
+{
+ return fstab_argp.parser (key, arg, state);
+}
+
+static struct argp fstab_argp_mtab =
+{
+ options: fstab_argp_mtab_opts,
+ parser: fstab_argp_mtab_parse_opt,
+};
+
+/* Unmount one filesystem. */
+static error_t
+do_umount (struct fs *fs)
+{
+ error_t err = 0;
+
+ file_t node = file_name_lookup (fs->mntent.mnt_dir, O_NOTRANS, 0666);
+ if (node == MACH_PORT_NULL)
+ {
+ error (0, errno, "%s", fs->mntent.mnt_dir);
+ return errno;
+ }
+
+ if (verbose)
+ printf ("settrans -apg%s%s %s\n",
+ goaway_flags & FSYS_GOAWAY_NOSYNC? "S": "",
+ goaway_flags & FSYS_GOAWAY_FORCE? "f": "",
+ fs->mntent.mnt_dir);
+
+ if (! fake)
+ {
+ err = file_set_translator (node,
+ passive_flags, active_flags, goaway_flags,
+ NULL, 0,
+ MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND);
+ if (! err)
+ {
+ if (strcmp (fs->mntent.mnt_fsname, "") != 0 &&
+ strcmp (fs->mntent.mnt_fsname, "none") != 0)
+ {
+ if (verbose)
+ printf ("settrans -ag%s%s %s\n",
+ goaway_flags & FSYS_GOAWAY_NOSYNC? "S": "",
+ goaway_flags & FSYS_GOAWAY_FORCE? "f": "",
+ fs->mntent.mnt_fsname);
+
+ file_t source = file_name_lookup (fs->mntent.mnt_fsname,
+ O_NOTRANS,
+ 0666);
+ if (source == MACH_PORT_NULL)
+ {
+ error (0, errno, "%s", fs->mntent.mnt_fsname);
+ return errno;
+ }
+
+ err = file_set_translator (source,
+ 0, active_flags, goaway_flags,
+ NULL, 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TYPE_COPY_SEND);
+ if (err)
+ error (0, err, "%s", fs->mntent.mnt_fsname);
+
+ mach_port_deallocate (mach_task_self (), source);
+
+ }
+ }
+ else
+ {
+ error (0, err, "%s", fs->mntent.mnt_dir);
+
+ /* Try remounting readonly instead if requested. */
+ if (readonly)
+ {
+ if (verbose)
+ printf ("fsysopts %s --readonly\n", fs->mntent.mnt_dir);
+
+ error_t e = fs_set_readonly (fs, TRUE);
+ if (e)
+ error (0, e, "%s", fs->mntent.mnt_dir);
+ }
+ }
+ }
+
+ /* Deallocate the reference so that unmounting nested translators
+ works properly. */
+ mach_port_deallocate (mach_task_self (), node);
+ return err;
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+
+ err = argp_parse (&argp, argc, argv, 0, 0, &fstab_params);
+ if (err)
+ error (3, err, "parsing arguments");
+
+ /* Read the mtab file by default. */
+ if (! fstab_params.fstab_path)
+ fstab_params.fstab_path = _PATH_MOUNTED;
+
+ struct fstab *fstab = fstab_argp_create (&fstab_params, NULL, 0);
+ if (! fstab)
+ error (3, ENOMEM, "fstab creation");
+
+ if (targets)
+ for (char *t = targets; t; t = argz_next (targets, targets_len, t))
+ {
+ /* Figure out if t is the device or the mountpoint. */
+ struct fs *fs = fstab_find_mount (fstab, t);
+ if (! fs)
+ {
+ fs = fstab_find_device (fstab, t);
+ if (! fs)
+ {
+ error (0, 0, "could not find entry for: %s", t);
+
+ /* As last resort, just assume it is the mountpoint. */
+ struct mntent m =
+ {
+ mnt_fsname: "",
+ mnt_dir: t,
+ mnt_type: "",
+ mnt_opts: 0,
+ mnt_freq: 0,
+ mnt_passno: 0,
+ };
+
+ err = fstab_add_mntent (fstab, &m, &fs);
+ if (err)
+ error (2, err, "%s", t);
+ }
+ }
+
+ if (fs)
+ err |= do_umount (fs);
+ }
+ else
+ {
+ /* Sort entries in reverse lexicographical order so that the
+ longest mount points are unmounted first. This makes sure
+ that nested mounts are handled properly. */
+ size_t count = 0;
+ for (struct fs *fs = fstab->entries; fs; fs = fs->next)
+ count += 1;
+
+ char **entries = malloc (count * sizeof (char *));
+ if (! entries)
+ error (3, ENOMEM, "allocating entries array");
+
+ char **p = entries;
+ for (struct fs *fs = fstab->entries; fs; fs = fs->next)
+ *p++ = fs->mntent.mnt_dir;
+
+ /* Reverse lexicographical order. */
+ int compare_entries (const void *a, const void *b)
+ {
+ return -strcmp ((char *) a, (char *) b);
+ }
+
+ qsort (entries, count, sizeof (char *), compare_entries);
+
+ for (int i = 0; i < count; i++)
+ {
+ struct fs *fs = fstab_find_mount (fstab, entries[i]);
+ if (! fs)
+ error (4, 0, "could not find entry for: %s", entries[i]);
+
+ if (! match_options (&fs->mntent))
+ continue;
+
+ err |= do_umount (fs);
+ }
+ }
+
+ return err? EXIT_FAILURE: EXIT_SUCCESS;
+}
diff --git a/utils/unsu.c b/utils/unsu.c
new file mode 100644
index 00000000..467cf46f
--- /dev/null
+++ b/utils/unsu.c
@@ -0,0 +1,90 @@
+/* Attempt to undo a previous su
+
+ Copyright (C) 1997,98,2000 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <hurd.h>
+#include <argp.h>
+#include <error.h>
+#include <version.h>
+
+#include "frobauth.h"
+#include "pids.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (unsu);
+
+static struct argp_child child_argps[] = {{ &frobauth_no_ugids_argp }, { 0 }};
+
+static char doc[] =
+ "Attempt to undo a previous setauth --save"
+ "\vThis command is convenient, but will only correctly undo a limited"
+ " subset of possible setauth commands. It works by simply deleting all"
+ " current effective ids and the first two available ids, and then"
+ " making the first remaining available id the current effective id.";
+
+int
+main (int argc, char *argv[])
+{
+ struct frobauth frobauth = FROBAUTH_INIT;
+
+ /* Modify UGIDS, to be what PID's new authentication should be, NOISE is
+ ignored. */
+ error_t modify (struct ugids *ugids, const struct ugids *noise,
+ pid_t pid, void *hook)
+ {
+ error_t err = 0;
+
+ idvec_clear (&ugids->eff_uids);
+ idvec_clear (&ugids->eff_gids);
+ idvec_clear (&ugids->imp_eff_gids);
+
+ idvec_delete (&ugids->avail_uids, 0);
+ idvec_delete (&ugids->avail_uids, 0);
+
+ idvec_delete (&ugids->avail_gids, 0);
+ idvec_delete (&ugids->avail_gids, 0);
+ idvec_keep (&ugids->imp_avail_gids, &ugids->avail_gids);
+
+ if (ugids->avail_uids.num > 0)
+ err = ugids_set_posix_user (ugids, ugids->avail_uids.ids[0]);
+
+ return err;
+ }
+ void print_info (const struct ugids *new,
+ const struct ugids *old,
+ const struct ugids *removed,
+ pid_t pid, void *hook)
+ {
+ char *new_rep = ugids_rep (new, 1, 1, 0, 0, 0);
+ printf ("%d: Changed auth to %s\n", pid, new_rep);
+ free (new_rep);
+ }
+ struct argp argp = { 0, 0, 0, doc, child_argps };
+
+ /* Parse our command line. This shouldn't ever return an error. */
+ argp_parse (&argp, argc, argv, 0, 0, &frobauth);
+
+ if (frobauth_modify (&frobauth, 0, 0, modify, print_info, 0))
+ return 0;
+ else
+ return 1;
+}
diff --git a/utils/uptime.sh b/utils/uptime.sh
new file mode 100644
index 00000000..8e52c81b
--- /dev/null
+++ b/utils/uptime.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+# Show system uptime, number of users, and load
+#
+# Copyright (C) 1996 Free Software Foundation, Inc.
+#
+# Written by Miles Bader <miles@gnu.ai.mit.edu>
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+USAGE="Usage: $0 [OPTION...]"
+DOC="Show system uptime, number of users, and load"
+
+W=${W-/bin/w}
+
+while :; do
+ case "$1" in
+ --help|"-?")
+ echo "$USAGE"
+ echo "$DOC"
+ echo ""
+ echo " -?, --help Give this help list"
+ echo " --usage Give a short usage message"
+ echo " -V, --version Print program version"
+ exit 0;;
+ --usage)
+ echo "Usage: $0 [-V?] [--help] [--usage] [--version]"
+ exit 0;;
+ --version|-V)
+ echo "STANDARD_HURD_VERSION_uptime_"; exit 0;;
+ -*)
+ echo 1>&2 "$0: unrecognized option \`$1'"
+ echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information";
+ exit 1;;
+ *)
+ break;;
+ esac
+done
+
+case "$#" in
+ 0) :;;
+ *)
+ echo 1>&2 "$0: too many arguments"
+ echo 1>&2 "Try \`$0 --help' or \`$0 --usage' for more information";
+ exit 1;;
+esac
+
+$W -u
diff --git a/utils/vminfo.c b/utils/vminfo.c
new file mode 100644
index 00000000..6ead677e
--- /dev/null
+++ b/utils/vminfo.c
@@ -0,0 +1,241 @@
+/* Print task vm information
+
+ Copyright (C) 1996,97,98,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <argp.h>
+#include <error.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <version.h>
+
+#include <mach.h>
+#include <mach/vm_statistics.h>
+#include <mach/default_pager.h>
+#include <hurd.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (vminfo);
+
+static const struct argp_option options[] = {
+ {"verbose", 'v', 0, 0, "Give more detailed information"},
+ {"addresses", 'a', 0, 0, "Print region start addresses"},
+ {"sizes", 's', 0, 0, "Print region sizes"},
+ {"decimal", 'd', 0, 0, "Show number is decimal"},
+ {"holes", 'h', 0, 0, "Show holes between regions explicitly"},
+ {0}
+};
+static const char *args_doc = "PID [ADDR [SIZE]]]";
+static const char *doc = "Show virtual memory regions for process PID"
+"\vIf ADDR, and possibly SIZE, are given only regions enclosing the range"
+" ADDR to ADDR+SIZE are shown (SIZE defaults to 0)."
+"\nIf neither --addresses nor --sizes is specified, both are assumed.";
+
+/* Possible things to show about regions. */
+#define W_ADDRS 0x1
+#define W_SIZES 0x2
+#define W_DETAILS 0x4
+
+static char *
+prot_rep (vm_prot_t prot)
+{
+ if (prot == 0)
+ return "0";
+ else
+ {
+ static char buf[20];
+ char *p = buf;
+ if (prot & VM_PROT_READ)
+ *p++ = 'R';
+ if (prot & VM_PROT_WRITE)
+ *p++ = 'W';
+ if (prot & VM_PROT_EXECUTE)
+ *p++ = 'X';
+ if (prot & ~VM_PROT_ALL)
+ sprintf (p, "+%#x", (prot & ~VM_PROT_ALL));
+ else
+ *p = '\0';
+ return buf;
+ }
+}
+
+static char *
+inh_rep (vm_inherit_t inh)
+{
+ static char buf[20];
+ switch (inh)
+ {
+ case VM_INHERIT_SHARE: return "share";
+ case VM_INHERIT_COPY: return "copy";
+ case VM_INHERIT_NONE: return "none";
+ default:
+ sprintf (buf, "%d", inh);
+ return buf;
+ }
+}
+
+static unsigned
+parse_num (char *arg, unsigned base, struct argp_state *state, char *what)
+{
+ char *arg_end;
+ unsigned long num = strtoul (arg, &arg_end, base);
+ if (*arg == '\0' || *arg_end != '\0')
+ argp_error (state, "%s: Invalid %s", arg, what);
+ return num;
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ int what = 0, hex = 1, holes = 0;
+ vm_offset_t addr = 0, max_addr = ~addr;
+ task_t task;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ pid_t pid;
+ process_t proc;
+
+ case 'a': what |= W_ADDRS; break;
+ case 's': what |= W_SIZES; break;
+ case 'v': what |= W_DETAILS; break;
+ case 'd': hex = 0; break;
+ case 'h': holes = 1; break;
+
+ case ARGP_KEY_ARG:
+ switch (state->arg_num)
+ {
+ case 0: /* PID */
+ pid = parse_num (arg, 10, state, "PID");
+ proc = getproc ();
+ err = proc_pid2task (proc, pid, &task);
+ if (err)
+ argp_failure (state, 11, err, "%s", arg);
+ break;
+ case 1: /* ADDR */
+ addr = max_addr = parse_num (arg, 0, state, "address"); break;
+ case 2: /* SIZE */
+ max_addr = addr + parse_num (arg, 0, state, "size"); break;
+ default:
+ argp_usage (state);
+ }
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ const struct argp argp = { options, parse_opt, args_doc, doc };
+
+ /* Parse our arguments. */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ if ((what & ~W_DETAILS) == 0)
+ what = W_ADDRS | W_SIZES | W_DETAILS;
+
+ while (addr <= max_addr)
+ {
+ vm_size_t size;
+ vm_prot_t prot, max_prot;
+ mach_port_t obj;
+ vm_offset_t offs;
+ vm_inherit_t inh;
+ int shared;
+ vm_offset_t hole_addr = addr;
+
+ err =
+ vm_region (task, &addr, &size, &prot, &max_prot, &inh, &shared,
+ &obj, &offs);
+ if (err)
+ {
+ if (err != EKERN_NO_SPACE)
+ error (12, err, "vm_region");
+ break;
+ }
+
+ if (holes && hole_addr != addr)
+ {
+ if ((what & (W_ADDRS|W_SIZES)) == (W_ADDRS|W_SIZES))
+ {
+ if (hex)
+ printf (" [%#zx] (hole)\n", addr - hole_addr);
+ else
+ printf (" [%zd] (hole)\n", addr - hole_addr);
+ }
+ else if ((what & (W_ADDRS|W_SIZES)) == W_SIZES)
+ {
+ if (hex)
+ printf ("%#10zx (hole)\n", addr - hole_addr);
+ else
+ printf ("%10zu (hole)\n", addr - hole_addr);
+ }
+ }
+
+ if ((what & (W_ADDRS|W_SIZES)) == (W_ADDRS|W_SIZES))
+ if (hex)
+ printf ("%#10zx[%#zx]", addr, size);
+ else
+ printf ("%10zu[%zd]", addr, size);
+ else if ((what & (W_ADDRS|W_SIZES)) == W_ADDRS)
+ if (hex)
+ printf ("%#10zx", addr);
+ else
+ printf ("%10zu", addr);
+ else if ((what & (W_ADDRS|W_SIZES)) == W_SIZES)
+ {
+ if (hex)
+ printf ("%#10zx", size);
+ else
+ printf ("%10zu", size);
+ }
+ if (what & W_DETAILS)
+ {
+ printf (" (prot=%s", prot_rep (prot));
+ if (max_prot != prot)
+ printf (", max_prot=%s", prot_rep (max_prot));
+ if (inh != VM_INHERIT_DEFAULT)
+ printf (", inherit=%s", inh_rep (inh));
+ if (shared)
+ printf (", shared");
+ if (obj != MACH_PORT_NULL)
+ printf (", mem_obj=%d", obj);
+ if (offs != 0)
+ {
+ if (hex)
+ printf (", offs=%#zx", offs);
+ else
+ printf (", offs=%zd", offs);
+ }
+ putchar (')');
+ }
+ putchar ('\n');
+
+ addr += size;
+ }
+
+ exit (0);
+}
diff --git a/utils/vmstat.c b/utils/vmstat.c
new file mode 100644
index 00000000..e3944848
--- /dev/null
+++ b/utils/vmstat.c
@@ -0,0 +1,660 @@
+/* Print vm statistics
+
+ Copyright (C) 1996,97,99,2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <argp.h>
+#include <error.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <version.h>
+
+#include <mach.h>
+#include <mach/gnumach.h>
+#include <mach/vm_statistics.h>
+#include <mach/vm_cache_statistics.h>
+#include <mach/default_pager.h>
+#include <hurd.h>
+#include <hurd/paths.h>
+
+const char *argp_program_version = STANDARD_HURD_VERSION (vmstat);
+
+static const struct argp_option options[] = {
+ {"terse", 't', 0, 0, "Use short one-line output format"},
+ {"no-header", 'H', 0, 0, "Don't print a descriptive header line"},
+ {"prefix", 'p', 0, 0, "Always display a description before stats"},
+ {"no-prefix", 'P', 0, 0, "Never display a description before stats"},
+ {"pages", 'v', 0, 0, "Display sizes in pages"},
+ {"kilobytes", 'k', 0, 0, "Display sizes in 1024 byte blocks"},
+ {"bytes", 'b', 0, 0, "Display sizes in bytes"},
+ {0}
+};
+static const char *args_doc = "[PERIOD [COUNT [HEADER_INTERVAL]]]";
+static const char *doc = "Show system virtual memory statistics"
+"\vIf PERIOD is supplied, then terse mode is"
+" selected, and the output repeated every PERIOD seconds, with cumulative"
+" fields given the difference from the last output. If COUNT is given"
+" and non-zero, only that many lines are output. HEADER_INTERVAL"
+" defaults to 23, and if not zero, is the number of repeats after which a"
+" blank line and the header will be reprinted (as well as the totals for"
+" cumulative fields).";
+
+/* We use this one type to represent all values printed by this program. It
+ should be signed, and hopefully large enough (it may need to be larger
+ than what the system returns values in, as we represent some quantities as
+ bytes instead of pages)! */
+typedef long long val_t;
+#define BADVAL ((val_t) -1LL) /* a good generic value for "couldn't get" */
+
+/* What a given number describes. */
+enum val_type
+{
+ COUNT, /* As-is. */
+ SIZE, /* Use the most convenient unit, with suffix */
+ PAGESZ, /* Like SIZE, but never changed to PAGES. */
+ PCENT, /* Append `%'. */
+};
+
+/* Return the `nominal' width of a field of type TYPE, in units of SIZE_UNITS. */
+static size_t
+val_width (val_t val, enum val_type type, size_t size_units)
+{
+ size_t vwidth (val_t val)
+ {
+ size_t w = 1;
+ if (val < 0)
+ w++, val = -val;
+ while (val > 9)
+ w++, val /= 10;
+ return w;
+ }
+ if (type == PCENT)
+ return vwidth (val) + 1;
+ else if ((type == SIZE || type == PAGESZ) && size_units == 0)
+ return val > 1000 ? 5 : vwidth (val) + 1;
+ else
+ {
+ if ((type == SIZE || type == PAGESZ) && size_units > 0)
+ val /= size_units;
+ return vwidth (val);
+ }
+}
+
+/* Print a number of type TYPE. If SIZE_UNITS is non-zero, then values of
+ type SIZE are divided by that amount and printed without a suffix. FWIDTH
+ is the width of the field to print it in, right-justified. If SIGN is
+ true, the value is always printed with a sign, even if it's positive. */
+static void
+print_val (val_t val, enum val_type type,
+ size_t size_units, int fwidth, int sign)
+{
+ if (type == PCENT)
+ printf (sign ? "%+*lld%%" : "%*lld%%", fwidth - 1, val);
+ else if ((type == SIZE || type == PAGESZ) && size_units == 0)
+ {
+ float fval = val;
+ char *units = " KMGT", *u = units;
+
+ while (fval >= 10000)
+ {
+ fval /= 1024;
+ u++;
+ }
+
+ printf ((fval >= 1000
+ ? (sign ? "%+*.0f%c" : "%*.0f%c")
+ : (sign ? "%+*.3g%c" : "%*.3g%c")),
+ fwidth - 1, fval, *u);
+ }
+ else
+ {
+ if ((type == SIZE || type == PAGESZ) && size_units > 0)
+ val /= size_units;
+ printf (sign ? "%+*lld" : "%*lld", fwidth, val);
+ }
+}
+
+/* Special values for val_t ranges. */
+#define VAL_MAX_MEM -1 /* up to the system memory size */
+#define VAL_MAX_SWAP -2 /* up to the system swap size */
+
+/* How this field changes with time. */
+enum field_change_type
+{
+ VARY, /* Can go up or down. */
+ CONST, /* Always the same. */
+ CUMUL, /* Monotonic increasing. */
+};
+
+struct vm_state; /* fwd */
+
+struct field
+{
+ /* Name of the field. */
+ char *name;
+
+ /* Terse header used for the columnar style output. */
+ char *hdr;
+
+ /* A description of this field (for user help). */
+ char *doc;
+
+ /* Type of this field. */
+ enum field_change_type change_type;
+
+ /* How to display the number associated with this field.
+ If this is anything but `DIMLESS', then it can be overriden by the
+ user. */
+ enum val_type type;
+
+ /* The `maximum value' this field can have -- used for field widths. */
+ val_t max;
+
+ /* True if we display this field by default (user can always override). */
+ int standard :1;
+
+ /* Offset of the integer_t field in a vm_statistics structure. -1 if a
+ computed field (in which case the COMPUTE field should be filled in). */
+ int offs;
+
+ /* How to compute this field. If 0, get_vmstats_value is used. This
+ function should return a negative number if there's some problem getting
+ the field. */
+ val_t (*compute)(struct vm_state *state, const struct field *field);
+};
+
+/* State about system vm from which we compute the above defined fields. */
+struct vm_state
+{
+ /* General vm statistics. */
+ struct vm_statistics vmstats;
+
+ /* Page cache statistics. */
+ struct vm_cache_statistics cache_stats;
+
+ /* default pager port (must be privileged to fetch this). */
+ mach_port_t def_pager;
+ struct default_pager_info def_pager_info;
+};
+
+static error_t
+vm_state_refresh (struct vm_state *state)
+{
+ error_t err = vm_statistics (mach_task_self (), &state->vmstats);
+
+ if (err)
+ return err;
+
+ err = vm_cache_statistics (mach_task_self (), &state->cache_stats);
+
+ if (err)
+ return err;
+
+ /* Mark the info as invalid, but leave DEF_PAGER alone. */
+ memset (&state->def_pager_info, 0, sizeof state->def_pager_info);
+
+ return 0;
+}
+
+static val_t
+get_vmstats_field (struct vm_state *state, const struct field *field)
+{
+ val_t val =
+ (val_t)(*(integer_t *)((char *)&state->vmstats + field->offs));
+ if (field->type == SIZE)
+ val *= state->vmstats.pagesize;
+ return val;
+}
+
+static val_t
+get_size (struct vm_state *state, const struct field *field)
+{
+ return
+ (state->vmstats.free_count + state->vmstats.active_count
+ + state->vmstats.inactive_count + state->vmstats.wire_count)
+ * state->vmstats.pagesize;
+}
+
+static val_t
+vm_state_get_field (struct vm_state *state, const struct field *field)
+{
+ return (field->compute ?: get_vmstats_field) (state, field);
+}
+
+static val_t
+get_memobj_hit_ratio (struct vm_state *state, const struct field *field)
+{
+ return state->vmstats.hits * 100 / state->vmstats.lookups;
+}
+
+/* Makes sure STATE contains a default pager port and associated info, and
+ returns 0 if not (after printing an error). */
+static int
+ensure_def_pager_info (struct vm_state *state)
+{
+ error_t err;
+
+ if (state->def_pager == MACH_PORT_NULL)
+ {
+ mach_port_t host;
+
+ err = get_privileged_ports (&host, 0);
+ if (err == EPERM)
+ {
+ /* We are not root, so try opening the /servers file. */
+ state->def_pager = file_name_lookup (_SERVERS_DEFPAGER, O_READ, 0);
+ if (state->def_pager == MACH_PORT_NULL)
+ {
+ error (0, errno, _SERVERS_DEFPAGER);
+ return 0;
+ }
+ }
+ if (state->def_pager == MACH_PORT_NULL)
+ {
+ if (err)
+ {
+ error (0, err, "get_privileged_ports");
+ return 0;
+ }
+
+ err = vm_set_default_memory_manager (host, &state->def_pager);
+ mach_port_deallocate (mach_task_self (), host);
+
+ if (err)
+ {
+ error (0, err, "vm_set_default_memory_manager");
+ return 0;
+ }
+ }
+ }
+
+ if (!MACH_PORT_VALID (state->def_pager))
+ {
+ if (state->def_pager == MACH_PORT_NULL)
+ {
+ error (0, 0,
+ "No default pager running, so no swap information available");
+ state->def_pager = MACH_PORT_DEAD; /* so we don't try again */
+ }
+ return 0;
+ }
+
+ err = default_pager_info (state->def_pager, &state->def_pager_info);
+ if (err)
+ error (0, err, "default_pager_info");
+ return (err == 0);
+}
+
+#define SWAP_FIELD(getter, expr) \
+ static val_t getter (struct vm_state *state, const struct field *field) \
+ { return ensure_def_pager_info (state) ? (val_t) (expr) : BADVAL; }
+
+SWAP_FIELD (get_swap_size, state->def_pager_info.dpi_total_space)
+SWAP_FIELD (get_swap_free, state->def_pager_info.dpi_free_space)
+SWAP_FIELD (get_swap_page_size, state->def_pager_info.dpi_page_size)
+SWAP_FIELD (get_swap_active, (state->def_pager_info.dpi_total_space
+ - state->def_pager_info.dpi_free_space))
+
+/* Returns the byte offset of the field FIELD in a vm_state structure. */
+#define _F(field_name) offsetof (struct vm_state, field_name)
+
+#define K 1024
+#define M (1024*K)
+#define G (1024LL*M)
+
+/* vm_statistics fields we know about. */
+static const struct field fields[] =
+{
+ {"pagesize", "pgsz", "System pagesize",
+ CONST, PAGESZ, 16*K, 1, _F (vmstats.pagesize) },
+ {"size", "size", "Usable physical memory",
+ CONST, SIZE, VAL_MAX_MEM, 1, 0, get_size },
+ {"free", "free", "Unused physical memory",
+ VARY, SIZE, VAL_MAX_MEM, 1, _F (vmstats.free_count) },
+ {"active", "actv", "Physical memory in active use",
+ VARY, SIZE, VAL_MAX_MEM, 1, _F (vmstats.active_count) },
+ {"inactive", "inact", "Physical memory in the inactive queue",
+ VARY, SIZE, VAL_MAX_MEM, 1, _F (vmstats.inactive_count) },
+ {"wired", "wired", "Unpageable physical memory",
+ VARY, SIZE, VAL_MAX_MEM, 1, _F (vmstats.wire_count) },
+ {"zero filled", "zeroed","Cumulative zero-filled pages",
+ CUMUL, SIZE, 90*G, 1, _F (vmstats.zero_fill_count) },
+ {"reactivated", "react", "Cumulative reactivated inactive pages",
+ CUMUL, SIZE, 900*M, 1, _F (vmstats.reactivations) },
+ {"pageins", "pgins", "Cumulative pages paged in",
+ CUMUL, SIZE, 90*G, 1, _F (vmstats.pageins) },
+ {"pageouts", "pgouts","Cumulative pages paged out",
+ CUMUL, SIZE, 90*G, 1, _F (vmstats.pageouts) },
+ {"page faults", "pfaults","Cumulative page faults",
+ CUMUL, COUNT, 99999999, 1, _F (vmstats.faults) },
+ {"cow faults", "cowpfs", "Cumulative copy-on-write page faults",
+ CUMUL, COUNT, 9999999, 1, _F (vmstats.cow_faults) },
+ {"memobj lookups","lkups","Memory-object lookups",
+ CUMUL, COUNT, 999999, 0, _F (vmstats.lookups) },
+ {"memobj hits", "hits", "Memory-object lookups with active pagers",
+ CUMUL, COUNT, 999999, 0, _F (vmstats.hits) },
+ {"memobj hit ratio","hrat","Percentage of memory-object lookups with active pagers",
+ VARY, PCENT, 99, 1, -1, get_memobj_hit_ratio },
+ {"cached memobjs", "caobj", "Number of memory-objects retained in the page cache",
+ VARY, COUNT, 99999999, 1, _F (cache_stats.cache_object_count) },
+ {"cache", "cache", "Physical memory used by the page cache",
+ VARY, SIZE, VAL_MAX_MEM, 1, _F (cache_stats.cache_count) },
+ {"swap size", "swsize", "Size of the default-pager swap area",
+ CONST, SIZE, VAL_MAX_SWAP, 1, 0 ,get_swap_size },
+ {"swap active", "swactv", "Default-pager swap area in use",
+ VARY, SIZE, VAL_MAX_SWAP, 0, 0 ,get_swap_active },
+ {"swap free", "swfree", "Default-pager swap area available for swapping",
+ VARY, SIZE, VAL_MAX_SWAP, 1, 0 ,get_swap_free },
+ {"swap pagesize","swpgsz", "Units used for swapping to the default pager",
+ CONST, PAGESZ, 16*K, 0, 0 ,get_swap_page_size },
+ {0}
+};
+#undef _F
+
+/* Convert a field name to the corresponding user-option. */
+static char *name_to_option (const char *name)
+{
+ char *opt = strdup (name), *p;
+ if (opt)
+ for (p = opt; *p; p++)
+ if (*p == ' ')
+ *p = '-';
+ return opt;
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ const struct field *field;
+ struct vm_state state;
+ int num_fields = 0; /* Number of vm_fields known. */
+ unsigned long output_fields = 0; /* A bit per field, from 0. */
+ int count = 1; /* Number of repeats. */
+ unsigned period = 0; /* Seconds between terse mode repeats. */
+ unsigned hdr_interval = 22; /* */
+ ssize_t size_units = 0; /* -1 means `pages' */
+ int terse = 0, print_heading = 1, print_prefix = -1;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ if (key < 0)
+ /* A field option. */
+ output_fields |= (1 << (-1 - key));
+ else
+ switch (key)
+ {
+ case 't': terse = 1; break;
+ case 'p': print_prefix = 1; break;
+ case 'P': print_prefix = 0; break;
+ case 'H': print_heading = 0; break;
+ case 'b': size_units = 1; break;
+ case 'v': size_units = -1; break;
+ case 'k': size_units = K; break;
+
+ case ARGP_KEY_ARG:
+ terse = 1;
+ switch (state->arg_num)
+ {
+ case 0:
+ period = atoi (arg); count = 0; break;
+ case 1:
+ count = atoi (arg); break;
+ case 2:
+ hdr_interval = atoi (arg); break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp_option *field_opts;
+ int field_opts_size;
+ struct argp field_argp = { 0, parse_opt };
+ const struct argp_child children[] =
+ {{&field_argp, 0, "Selecting which statistics to show:"}, {0}};
+ const struct argp argp = { options, parse_opt, args_doc, doc, children };
+
+ /* See how many fields we know about. */
+ for (field = fields; field->name; field++)
+ num_fields++;
+
+ /* Construct an options vector for them. */
+ field_opts_size = ((num_fields + 1) * sizeof (struct argp_option));
+ field_opts = alloca (field_opts_size);
+ memset (field_opts, 0, field_opts_size);
+
+ for (field = fields; field->name; field++)
+ {
+ int which = field - fields;
+ struct argp_option *opt = &field_opts[which];
+
+ opt->name = name_to_option (field->name);
+ opt->key = -1 - which; /* options are numbered -1 ... -(N - 1). */
+ opt->doc = field->doc;
+ opt->group = 2;
+ }
+ /* No need to terminate FIELD_OPTS because the memset above has done so. */
+
+ field_argp.options = field_opts;
+
+ /* Parse our arguments. */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ if (output_fields == 0)
+ /* Show default fields. */
+ for (field = fields; field->name; field++)
+ if (field->standard)
+ output_fields |= (1 << (field - fields));
+
+ /* Returns an appropriate SIZE_UNITS for printing FIELD. */
+#define SIZE_UNITS(field) \
+ (size_units >= 0 \
+ ? size_units \
+ : ((field)->type == PAGESZ ? 0 : state.vmstats.pagesize))
+
+ /* Prints SEP if the variable FIRST is 0, otherwise, prints START (if
+ it's non-zero), and sets first to 0. */
+#define PSEP(sep, start) \
+ (first ? (first = 0, (start && fputs (start, stdout))) : fputs (sep, stdout))
+#define PVAL(val, field, width, sign) \
+ print_val (val, (field)->type, SIZE_UNITS (field), width, sign)
+ /* Intuit the likely maximum field width of FIELD. */
+#define FWIDTH(field) \
+ val_width ((field)->max == VAL_MAX_MEM ? get_size (&state, field) \
+ : (field)->max == VAL_MAX_SWAP ? get_swap_size (&state, field) \
+ : (field)->max, \
+ (field)->type, SIZE_UNITS (field))
+
+ /* Actually fetch the statistics. */
+ memset (&state, 0, sizeof (state)); /* Initialize STATE. */
+ err = vm_state_refresh (&state);
+ if (err)
+ error (2, err, "vm_state_refresh");
+
+ if (terse)
+ /* Terse (one-line) output mode. */
+ {
+ int first_hdr = 1, first, repeats;
+ struct vm_state prev_state;
+ int const_fields = 0;
+
+ if (count == 0)
+ count = -1;
+
+ /* We only show const fields once per page, so find out which ones
+ those are. */
+ for (field = fields; field->name; field++)
+ if ((output_fields & (1 << (field - fields)))
+ && field->change_type == CONST)
+ const_fields |= (1 << (field - fields));
+ output_fields &= ~const_fields;
+
+ if (const_fields)
+ hdr_interval--; /* Allow room for the constant fields. */
+
+ do
+ {
+ int num;
+ int fwidths[num_fields];
+
+ if (first_hdr)
+ first_hdr = 0;
+ else
+ putchar ('\n');
+
+ if (const_fields)
+ /* Output constant fields on a line preceding the header. */
+ {
+ for (field = fields, first = 1; field->name; field++)
+ if (const_fields & (1 << (field - fields)))
+ {
+ val_t val = vm_state_get_field (&state, field);
+ if (val < 0)
+ /* Couldn't fetch this field, don't try again. */
+ const_fields &= ~(1 << (field - fields));
+ else
+ {
+ PSEP (", ", "(");
+ printf ("%s: ", field->name);
+ PVAL (val, field, 0, 0);
+ }
+ }
+ if (! first)
+ puts (")");
+ }
+
+ /* Calculate field widths. */
+ for (field = fields, num = 0; field->name; field++, num++)
+ if (output_fields & (1 << (field - fields)))
+ {
+ fwidths[num] = FWIDTH (field);
+ if (count != 1 && size_units == 0
+ && field->change_type == CUMUL && field->type == SIZE)
+ /* We may be printing a `+' prefix for field changes, and
+ since this is using the mostly constant-width SIZE
+ notation, individual changes may be the same width as
+ appropriated for absolute values -- so reserver another
+ column for the `+' character. */
+ fwidths[num]++;
+ if (fwidths[num] < strlen (field->hdr))
+ fwidths[num] = strlen (field->hdr);
+ }
+
+ if (print_heading)
+ {
+ for (field = fields, num = 0, first = 1; field->name; field++, num++)
+ if (output_fields & (1 << (field - fields)))
+ {
+ PSEP (" ", 0);
+ fprintf (stdout, "%*s", fwidths[num], field->hdr);
+ }
+ putchar ('\n');
+ }
+
+ prev_state = state;
+
+ for (repeats = 0
+ ; count && repeats < hdr_interval
+ ; repeats++, count--)
+ {
+ /* Output the fields. */
+ for (field = fields, num = 0, first = 1; field->name; field++, num++)
+ if (output_fields & (1 << (field - fields)))
+ {
+ val_t val = vm_state_get_field (&state, field);
+
+ if (val < 0)
+ /* Couldn't fetch this field, don't try again. */
+ const_fields &= ~(1 << (field - fields));
+ else
+ {
+ int sign = 0;
+
+ if (repeats && field->change_type == CUMUL)
+ {
+ sign = 1;
+ val -= vm_state_get_field (&prev_state, field);
+ }
+
+ PSEP (" ", 0);
+ PVAL (val, field, fwidths[num], sign);
+ }
+ }
+ putchar ('\n');
+
+ prev_state = state;
+
+ if (period)
+ {
+ sleep (period);
+ err = vm_state_refresh (&state);
+ if (err)
+ error (2, err, "vm_state_refresh");
+ }
+ }
+ }
+ while (count);
+ }
+ else
+ /* Verbose output. */
+ {
+ int max_width = 0;
+
+ if (print_prefix < 0)
+ /* By default, only print a prefix if there are multiple fields. */
+ print_prefix = (output_fields & (output_fields - 1));
+
+ if (print_prefix)
+ /* Find the widest description string, so we can align the output. */
+ for (field = fields; field->name; field++)
+ if (output_fields & (1 << (field - fields)))
+ {
+ int width = strlen (field->name) + FWIDTH (field);
+ if (width > max_width)
+ max_width = width;
+ }
+
+ for (field = fields; field->name; field++)
+ if (output_fields & (1 << (field - fields)))
+ {
+ val_t val = vm_state_get_field (&state, field);
+ if (val >= 0)
+ {
+ int fwidth = 0;
+ if (print_prefix)
+ {
+ printf ("%s: ", field->name);
+ fwidth = max_width - strlen (field->name);
+ }
+ PVAL (val, field, fwidth, 0);
+ putchar ('\n');
+ }
+ }
+ }
+
+ exit (0);
+}
diff --git a/utils/w.c b/utils/w.c
new file mode 100644
index 00000000..37555088
--- /dev/null
+++ b/utils/w.c
@@ -0,0 +1,520 @@
+/* Hurdish w
+
+ Copyright (C) 1995,96,97,98,99,2001,02 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <paths.h>
+#include <ctype.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <grp.h>
+#include <netdb.h>
+#include <version.h>
+
+#include <sys/fcntl.h>
+
+#include <argp.h>
+#include <argz.h>
+#include <envz.h>
+#include <idvec.h>
+#include <ps.h>
+#include <timefmt.h>
+#include <error.h>
+
+#include "psout.h"
+
+#define DEFAULT_FMT_STRING "%^%user %tty %from %login %idle %pid %what"
+
+extern char *canon_host (char *host);
+extern char *shared_domain (char *host1, char *host2);
+extern char *localhost ();
+
+const char *argp_program_version = STANDARD_HURD_VERSION (w);
+
+#define OA OPTION_ARG_OPTIONAL
+
+static struct argp_option options[] =
+{
+ {"format", 'F', "FMT", 0, "Use the custom output-format FMT"},
+ {"no-header", 'H', 0, 0, "Don't print a descriptive header line"},
+ {0, 'h', 0, OPTION_ALIAS | OPTION_HIDDEN}, /* BSD compat */
+ {"reverse", 'r', 0, 0, "Reverse the order of any sort"},
+ {"sort", 's', "FIELD",0, "Sort the output with respect to FIELD,"
+ " backwards if FIELD is prefixed by `-'"},
+ {0, 'i', 0, 0, "Sort output by idle time"},
+ {"tty", 't', "TTY", OA, "Only show entries for terminal TTY"},
+ {"width", 'w', "WIDTH",OA, "If WIDTH is given, try to format the"
+ " output for WIDTH columns, otherwise,"
+ " remove the default limit"},
+ {"uptime", 'u', 0, 0, "Only show the uptime and load info"},
+ {"no-uptime", 'U', 0, 0, "Don't show the uptime and load info"},
+ {"raw-hosts", 'n', 0, 0, "Show network addresses as numbers"},
+ {0, 0}
+};
+static char *args_doc = "[USER...]";
+static char *doc = "Show logged in users and what they are doing";
+
+/* The current time of day. */
+static struct timeval now;
+
+/* True if we shouldn't attempt to show names for host addresses. */
+static int raw_hosts = 0;
+
+struct w_hook
+{
+ struct utmp utmp;
+ struct timeval idle;
+ struct ps_user *user;
+ char *host; /* A malloced host name. */
+};
+
+#define W_PSTAT_USER (PSTAT_USER_BASE << 0)
+#define W_PSTAT_HOST (PSTAT_USER_BASE << 1)
+#define W_PSTAT_IDLE (PSTAT_USER_BASE << 2)
+#define W_PSTAT_LOGIN (PSTAT_USER_BASE << 3)
+
+static ps_flags_t
+w_deps (ps_flags_t flags)
+{
+ if (flags & W_PSTAT_IDLE)
+ flags |= PSTAT_TTY;
+ return flags;
+}
+
+static ps_flags_t
+w_fetch (struct proc_stat *ps, ps_flags_t need, ps_flags_t have)
+{
+ struct w_hook *hook = ps->hook;
+
+ if (! hook)
+ return 0; /* Can't do anything without w specific info.*/
+
+ if (need & W_PSTAT_HOST)
+ {
+ struct utmp *utmp = &hook->utmp;
+ if (utmp->ut_host[0])
+ {
+ /* UTMP->ut_host might not be '\0' terminated; this copy is. */
+ char ut_host[sizeof utmp->ut_host + 1];
+
+ strncpy (ut_host, utmp->ut_host, sizeof utmp->ut_host);
+ ut_host[sizeof utmp->ut_host] = '\0';
+
+ if (raw_hosts)
+ hook->host = strdup (ut_host);
+ else
+ {
+ char *sd;
+ hook->host = strdup (canon_host (ut_host) ?: ut_host);
+ sd = shared_domain (hook->host, localhost ());
+ if (sd)
+ *sd = '\0';
+ }
+ }
+ have |= W_PSTAT_HOST;
+ }
+
+ if (need & W_PSTAT_IDLE)
+ {
+ if (have & PSTAT_TTY)
+ {
+ struct stat stat;
+ struct ps_tty *tty = ps->tty;
+
+ hook->idle.tv_usec = 0;
+ if (! tty)
+ {
+ hook->idle.tv_sec = 0;
+ have |= W_PSTAT_IDLE;
+ }
+ else
+ {
+ if (io_stat (tty->port, &stat) == 0)
+ {
+ hook->idle.tv_sec = now.tv_sec - stat.st_atime;
+ have |= W_PSTAT_IDLE;
+ }
+ }
+ }
+ else if (ps->inapp & PSTAT_TTY)
+ ps->inapp |= W_PSTAT_IDLE;
+ }
+
+ if (need & W_PSTAT_USER)
+ if (ps_user_uname_create (hook->utmp.ut_name, &hook->user) == 0)
+ have |= W_PSTAT_USER;
+
+ /* We can always return these. */
+ have |= (need & W_PSTAT_LOGIN);
+
+ return have;
+}
+
+static void
+w_cleanup (struct proc_stat *ps)
+{
+ if (ps->hook)
+ {
+ if (ps->flags & W_PSTAT_HOST)
+ free (((struct w_hook *)ps->hook)->host);
+ free (ps->hook);
+ }
+}
+
+static void
+w_get_idle (struct proc_stat *ps, struct timeval *tv)
+{
+ struct w_hook *hook = ps->hook;
+ *tv = hook->idle;
+}
+const struct ps_getter w_idle_getter =
+{"idle_time", W_PSTAT_IDLE, w_get_idle};
+
+static void
+w_get_login (struct proc_stat *ps, struct timeval *tv)
+{
+ struct w_hook *hook = ps->hook;
+ tv->tv_usec = 0;
+ tv->tv_sec = hook ? hook->utmp.ut_time : 0;
+}
+const struct ps_getter w_login_getter =
+{"login_time", 0, w_get_login};
+
+static void
+w_get_uname (struct proc_stat *ps, char **uname, unsigned *uname_len)
+{
+ struct w_hook *hook = ps->hook;
+ *uname = hook->utmp.ut_name;
+ *uname_len = ((char *)memchr (*uname, 0, UT_NAMESIZE) ?: *uname) - *uname;
+}
+const struct ps_getter w_uname_getter =
+{"uname", 0, w_get_uname};
+
+static struct ps_user *
+w_get_user (struct proc_stat *ps)
+{
+ struct w_hook *hook = ps->hook;
+ return hook->user;
+}
+const struct ps_getter w_user_getter =
+{"user", W_PSTAT_USER, (void (*)())w_get_user};
+
+static void
+w_get_host (struct proc_stat *ps, char **host, unsigned *host_len)
+{
+ struct w_hook *hook = ps->hook;
+ *host = hook->host;
+ *host_len = *host ? strlen (*host) : 0;
+}
+const struct ps_getter w_host_getter =
+{"host", W_PSTAT_HOST, w_get_host};
+
+extern error_t ps_emit_past_time (), ps_emit_string (), ps_emit_minutes ();
+extern error_t ps_emit_user_name ();
+extern int ps_cmp_times (), ps_cmp_strings (), ps_cmp_unames ();
+extern int ps_nominal_string ();
+const struct ps_fmt_spec _w_specs[] =
+{
+ {"User", 0, 8, -1,0, &w_uname_getter,ps_emit_string, ps_cmp_strings},
+ {"Name", 0, 16, -1,0, &w_user_getter, ps_emit_user_name,ps_cmp_unames,ps_nominal_string},
+ {"Login","Login@", -7, -1,0,&w_login_getter,ps_emit_past_time,ps_cmp_times},
+ {"From", 0, 14, -1,0, &w_host_getter, ps_emit_string, ps_cmp_strings, ps_nominal_string},
+ {"Idle", 0, -5, -1,PS_FMT_FIELD_COLON_MOD, &w_idle_getter, ps_emit_minutes,ps_cmp_times},
+ {"What=args"},
+ {0}
+};
+struct ps_fmt_specs w_specs = {_w_specs, &ps_std_fmt_specs};
+
+/* Add to PROCS any processes in the foreground process group corresponding
+ to U, attaching a struct w_hook to which ever process is deemed the most
+ noteworthy. */
+static void
+add_utmp_procs (struct proc_stat_list *procs, struct utmp *u)
+{
+ /* The tty name, with space for '\0' termination and an
+ appropriate prefix. */
+ char tty[sizeof _PATH_DEV + sizeof u->ut_line];
+ io_t tty_node;
+ error_t err;
+ pid_t pid;
+ int pos;
+ struct proc_stat *ps;
+
+ switch (u->ut_type)
+ {
+ case LOGIN_PROCESS:
+ case USER_PROCESS:
+ /* These are the types that indicate a user job that we might
+ find processes for. */
+ if (u->ut_name[0] != '\0' && u->ut_line[0] != '\0')
+ break;
+ default:
+ /* This entry is not for a user, skip it. */
+ return;
+ }
+
+ strncpy (tty, u->ut_line, sizeof u->ut_line);
+ tty[sizeof u->ut_line] = '\0'; /* Ensure it's '\0' terminated. */
+
+ if (*tty != '/')
+ /* Not an absolute path -- it must be in /dev, which insert. */
+ {
+ bcopy (tty, tty + sizeof _PATH_DEV - 1, strlen (tty) + 1);
+ bcopy (_PATH_DEV, tty, sizeof _PATH_DEV - 1);
+ }
+
+ /* Now find which process group is the in foreground for TTY. */
+ tty_node = file_name_lookup (tty, 0, 0);
+ if (tty_node == MACH_PORT_NULL)
+ {
+ error (0, errno, "%s", tty);
+ return;
+ }
+
+ err = io_get_owner (tty_node, &pid);
+ if (err)
+ {
+ error (0, err, "%s", tty);
+ return;
+ }
+
+ /* The new process will get added at the end, so look for it there. */
+ pos = proc_stat_list_num_procs (procs);
+ if (pid >= 0)
+ err = proc_stat_list_add_pid (procs, pid, &ps);
+ else
+ {
+ struct proc_stat **pgrp_procs;
+ size_t num_procs;
+
+ err = proc_stat_list_add_pgrp (procs, -pid, &pgrp_procs, &num_procs);
+ if (! err)
+ {
+ if (num_procs > 0)
+ ps = pgrp_procs[0]; /* Use the first one. */
+ else
+ ps = 0;
+ free (pgrp_procs);
+ }
+ }
+ if (err)
+ {
+ error (0, err, "%s (owner %s %d)",
+ tty, pid < 0 ? "pgrp" : "pid", pid < 0 ? -pid : pid);
+ return;
+ }
+
+ if (ps)
+ {
+ ps->hook = malloc (sizeof (struct w_hook));
+ if (ps->hook)
+ bcopy (u, &((struct w_hook *)ps->hook)->utmp, sizeof *u);
+ }
+ else
+ error (0, 0, "%s (owner %s %d): No processes",
+ tty, pid < 0 ? "pgrp" : "pid", pid < 0 ? -pid : pid);
+}
+
+/* Find the absolute timestamp of when the system was booted.
+ We define "system boot time" as the task creation time of PID 1 (init). */
+
+static error_t
+fetch_boot_time (struct timeval *when)
+{
+ struct ps_context *context;
+ struct proc_stat *ps;
+ error_t err;
+
+ err = ps_context_create (getproc (), &context);
+ if (err)
+ error (2, err, "ps_context_create");
+
+ err = ps_context_find_proc_stat (context, 1, &ps);
+ if (err)
+ error (3, err, "ps_context_find_proc_stat");
+
+ err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC);
+ if (!err && !(ps->flags & PSTAT_TASK_BASIC))
+ err = EGRATUITOUS;
+ if (err)
+ {
+ error (0, err, "cannot find boot time");
+ return err;
+ }
+ else
+ {
+ time_value_t *const tv = &proc_stat_task_basic_info (ps)->creation_time;
+ when->tv_sec = tv->seconds;
+ when->tv_usec = tv->microseconds;
+ }
+
+ ps_context_free (context);
+
+ return 0;
+}
+
+static void
+uptime (struct proc_stat_list *procs)
+{
+ error_t err;
+ struct timeval boot_time;
+ char uptime_rep[20], tod_rep[20];
+ struct host_load_info *load;
+ unsigned nusers = 0;
+ int maybe_add_user (struct proc_stat *ps)
+ { if (ps->hook) nusers++; return 0; }
+
+ proc_stat_list_for_each (procs, maybe_add_user);
+
+ if (fetch_boot_time (&boot_time))
+ strcpy (uptime_rep, "chuck");
+ else
+ {
+ struct timeval uptime;
+ timersub (&now, &boot_time, &uptime);
+ fmt_named_interval (&uptime, 0, uptime_rep, sizeof (uptime_rep));
+ }
+
+ strftime (tod_rep, sizeof (tod_rep), "%r",
+ localtime ((time_t *)&now.tv_sec));
+ if (tod_rep[0] == '0')
+ tod_rep[0] = ' '; /* Get rid of bletcherous leading 0. */
+
+ err = ps_host_load_info (&load);
+ if (err)
+ error (0, err, "ps_host_load_info");
+
+ printf ("%s up %s, %u user%s, load averages: %.2f, %.2f, %.2f\n",
+ tod_rep, uptime_rep, nusers, nusers == 1 ? "" : "s",
+ (double)load->avenrun[0] / (double)LOAD_SCALE,
+ (double)load->avenrun[1] / (double)LOAD_SCALE,
+ (double)load->avenrun[2] / (double)LOAD_SCALE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ error_t err;
+ struct utmp *ut;
+ struct ps_context *context;
+ int output_width = -1;
+ char *fmt_string = DEFAULT_FMT_STRING, *sort_key_name = NULL;
+ int sort_reverse = 0, print_heading = 1, show_entries = 1, show_uptime = 1;
+ int squash_bogus_fields = 1, squash_nominal_fields = 1;
+ struct proc_stat_list *procs;
+#if 0
+ char *tty_names = 0;
+ unsigned num_tty_names = 0;
+#endif
+ uid_t *users = 0;
+ size_t num_users = 0;
+ struct ps_user_hooks ps_hooks = { w_deps, w_fetch, w_cleanup };
+
+ int has_hook (struct proc_stat *ps) { return ps->hook != 0; }
+
+ int keep_users (struct proc_stat *ps)
+ {
+ int i;
+ struct w_hook *h = ps->hook;
+ for (i = 0; i < num_users; i++)
+ if (users[i] == h->user->uid)
+ return 1;
+ return 0;
+ }
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'H': case 'h': print_heading = 0; break;
+ case 'i': sort_key_name = "idle"; break;
+ case 's': sort_key_name = arg; break;
+ case 'F': fmt_string = arg; break;
+ case 'r': sort_reverse = 1; break;
+ case 'u': show_entries = 0; break;
+ case 'U': show_uptime = 0; break;
+ case 'n': raw_hosts = 1; break;
+ case 'w': output_width = arg ? atoi (arg) : 0; break;
+
+ case ARGP_KEY_ARG:
+ num_users++;
+ users = realloc (users, num_users * sizeof (*users));
+ if (! users)
+ argp_failure (state, 5, ENOMEM, "%s", arg);
+ else if (isdigit (*arg))
+ users[num_users - 1] = atoi (arg);
+ else
+ {
+ struct passwd *pw = getpwnam (arg);
+ if (! pw)
+ argp_failure (state, 6, 0, "%s: Unknown user", arg);
+ users[num_users - 1] = pw->pw_uid;
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ if (gettimeofday (&now, 0) < 0)
+ error (0, errno, "gettimeofday");
+
+ err = ps_context_create (getproc (), &context);
+ if (err)
+ error (2, err, "ps_context_create");
+
+ err = proc_stat_list_create (context, &procs);
+ if (err)
+ error (3, err, "proc_stat_list_create");
+ context->user_hooks = &ps_hooks;
+
+ /* Parse our options. */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ /* Read the utmp file. */
+ setutent ();
+ while ((ut = getutent ()) != NULL)
+ add_utmp_procs (procs, ut);
+ endutent ();
+
+ /* Keep only processes that have our hooks attached. */
+ proc_stat_list_filter1 (procs, has_hook, 0, 0);
+
+ if (num_users > 0)
+ proc_stat_list_filter1 (procs, keep_users, W_PSTAT_USER, 0);
+
+ if (show_uptime)
+ uptime (procs);
+
+ if (show_entries)
+ psout (procs, fmt_string, 0, &w_specs, sort_key_name, sort_reverse,
+ output_width, print_heading,
+ squash_bogus_fields, squash_nominal_fields, 0);
+
+ return 0;
+}
diff --git a/utils/x.c b/utils/x.c
new file mode 100644
index 00000000..7bbc8ac5
--- /dev/null
+++ b/utils/x.c
@@ -0,0 +1,247 @@
+/* Hurdish su
+
+ Copyright (C) 1996 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <paths.h>
+#include <ctype.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <grp.h>
+#include <netdb.h>
+#include <time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <sys/fcntl.h>
+
+#include <argp.h>
+#include <argz.h>
+#include <envz.h>
+#include <idvec.h>
+#include <error.h>
+#include <timefmt.h>
+#include <hurd/lookup.h>
+
+static struct argp_option options[] =
+{
+ {"add", 'a', "USER", 0, "Add following ids"},
+ {"remove", 'r', "USER", 0, "Remove following ids"},
+ {"user", 'u', "USER", 0, "Add USER to the effective uids"},
+ {"avail-user",'U', "USER", 0, "Add USER to the available uids"},
+ {"group", 'g', "GROUP", 0, "Add GROUP to the effective groups"},
+ {"avail-group",'G',"GROUP", 0, "Add GROUP to the available groups"},
+ {0, 0}
+};
+static char *args_doc = "[USER...]";
+static char *doc = "Modify authentication of existing processes"
+ "\vA USER specified as an argument adds (or removes) that user's groups as"
+ " well. When removing groups implied by such an argument, the groups to"
+ " which uids remaining in the process after any we remove are ignored."
+"\nUids and groups specified with options are used as-is.";
+
+/* Full set of desired authorization. XXX msg_del_auth doesn't allow such
+ fine control. */
+struct auth
+{
+ struct idvec euids, egids; /* Effective ids. */
+ struct idvec auids, agids; /* Available ids. */
+};
+
+/* Ids of our parent process, with the effect of this program being su'd
+ removed. */
+static struct idvec parent_uids = {0}, parent_gids = {0};
+
+/* Make sure that the parent_[ug]ids are filled in. To make them useful for
+ su'ing, each is the avail ids with all effective ids but the first
+ appended; this gets rid of the effect of login being suid, and is useful
+ as the new process's avail id list (e.g., the real id is right). */
+static void
+need_parent_ids ()
+{
+ if (parent_uids.num == 0 && parent_gids.num == 0)
+ {
+ struct idvec *p_eff_uids = make_idvec ();
+ struct idvec *p_eff_gids = make_idvec ();
+ if (!p_eff_uids || !p_eff_gids)
+ err = ENOMEM;
+ if (! err)
+ err = idvec_merge_auth (p_eff_uids, parent_uids,
+ p_eff_gids, parent_gids,
+ parent_auth);
+ if (! err)
+ {
+ idvec_delete (p_eff_uids, 0); /* Counteract setuid. */
+ idvec_delete (p_eff_gids, 0);
+ err = idvec_merge (parent_uids, p_eff_uids);
+ if (! err)
+ err = idvec_merge (parent_gids, p_eff_gids);
+ }
+ if (err)
+ error (39, err, "Can't get uids");
+ }
+}
+
+/* Returns true if the *caller* of this login program has UID. */
+static int
+parent_has_uid (uid_t uid)
+{
+ need_parent_ids ();
+ return idvec_contains (parent_uids, uid);
+}
+/* Returns true if the *caller* of this login program has GID. */
+static int
+parent_has_gid (gid_t gid)
+{
+ need_parent_ids ();
+ return idvec_contains (parent_gids, gid);
+}
+/* Returns the number of parent uids. */
+static int
+count_parent_uids ()
+{
+ need_parent_ids ();
+ return parent_uids.num;
+}
+/* Returns the number of parent gids. */
+static int
+count_parent_gids ()
+{
+ need_parent_ids ();
+ return parent_gids.num;
+}
+
+/* Make sure the user should be allowed to do this. */
+void verify_passwd (const char *name, const char *password,
+ uid_t id, int is_group, structh auth *auth)
+{
+ extern char *crypt (const char salt[2], const char *string);
+ char *prompt, *unencrypted, *encrypted;
+
+ if (!password || !*password
+ || idvec_contains (is_group ? auth->egids : auth->euids, id)
+ || idvec_contains (is_group ? auth->agids : auth->auids, id)
+ || (no_passwd
+ && (parent_has_uid (0)
+ || (is_group ? parent_has_uid (id) : parent_has_gid (id)))))
+ return; /* Already got this one. */
+
+ if (name)
+ asprintf (&prompt, "Password for %s%s:",
+ is_group ? "group " : "", name);
+ else
+ prompt = "Password:";
+
+ unencrypted = getpass (prompt);
+ encrypted = crypt (unencrypted, password);
+ /* Paranoia may destroya. */
+ memset (unencrypted, 0, strlen (unencrypted));
+
+ if (name)
+ free (prompt);
+
+ if (strcmp (encrypted, password) != 0)
+ error (50, 0, "Incorrect password", 0);
+}
+
+void
+main(int argc, char *argv[])
+{
+ error_t err = 0;
+ struct auth add, remove;
+
+ /* Parse our options... */
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case ARGP_KEY_NO_ARGS:
+ arg = "0"; /* root */
+ /* fall through. */
+
+ case 'u':
+ case 'U':
+ {
+ struct passwd *pw =
+ isdigit (*user) ? getpwuid (atoi (user)) : getpwnam (user);
+ /* True if this is the user arg and there were no user options. */
+
+ if (! pw)
+ error (10, 0, "%s: Unknown user", user);
+
+ verify_passwd (state->argv[state->next] ? pw->pw_name : 0,
+ pw->pw_passwd, pw->pw_uid, 0, &auth);
+
+ if (key == 'u')
+ idvec_add_new (&auth.euids, pw->pw_uid);
+ else if (key == 'U')
+ /* Add available ids instead of effective ones. */
+ idvec_add_new (&auth.auids, pw->pw_uid);
+ else
+ /* A plain argument. Add both the specified user and any
+ associated groups. */
+ {
+ /* Effective */
+ idvec_add_new (&auth.euids, 0, pw->pw_uid);
+ idvec_add_new (&auth.egids, 0, pw->pw_gid);
+ }
+ }
+ break;
+
+ case 'g':
+ case 'G':
+ {
+ struct group *gr =
+ isdigit (*arg) ? getgrgid (atoi (arg)) : getgrnam (arg);
+ if (! gr)
+ error (11, 0, "%s: Unknown group", arg);
+ verify_passwd (gr->gr_name, gr->gr_passwd, gr->gr_gid, 1, &auth);
+ idvec_add_new (key == 'g' ? &auth.egids : &auth.agids, gr->gr_gid);
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, args_doc, doc};
+
+ bzero (add, sizeof add);
+ bzero (remove, sizeof remove);
+
+
+ err =
+ auth_makeauth (getauth (), 0, MACH_MSG_TYPE_COPY_SEND, 0,
+ &auth.euids->ids, &auth.euids->num,
+ &auth.auids->ids, &auth.auids->num,
+ &auth.egids->ids, &auth.egids->num,
+ &auth.agids->ids, &auth.agids->num,
+ &auth);
+ if (err)
+ error (3, err, "Authentication failure", 0);
+
+
+ exit(0);
+}
diff --git a/version.h.in b/version.h.in
new file mode 100644
index 00000000..80e75b52
--- /dev/null
+++ b/version.h.in
@@ -0,0 +1,29 @@
+/* Hurd version
+ Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#ifndef HURD_VERSION
+#define HURD_VERSION MASTER_HURD_VERSION
+#endif
+
+/* The standard way to print versions for --version. */
+#define STANDARD_HURD_VERSION(s) \
+ #s " (GNU Hurd) " HURD_VERSION
+#define STANDARD_HURD_VERSION_EXTRA(s, extra) \
+ #s " (GNU Hurd; " extra ") " HURD_VERSION